Connect++ 0.4.0
A fast, readable connection prover for first-order logic.
Loading...
Searching...
No Matches
StackProver.cpp
1/*
2
3Copyright © 2023-24 Sean Holden. All rights reserved.
4
5*/
6/*
7
8This file is part of Connect++.
9
10Connect++ is free software: you can redistribute it and/or modify it
11under the terms of the GNU General Public License as published by the
12Free Software Foundation, either version 3 of the License, or (at your
13option) any later version.
14
15Connect++ is distributed in the hope that it will be useful, but WITHOUT
16ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
18more details.
19
20You should have received a copy of the GNU General Public License along
21with Connect++. If not, see <https://www.gnu.org/licenses/>.
22
23*/
24
25#include "StackProver.hpp"
26
31
32//----------------------------------------------------------------------
34: num_preds(0)
35, var_index()
36, fun_index()
37, term_index()
38, pred_index()
39, sub_stack()
40, results()
41, matrix()
42, path()
43, new_C()
44, lemmata()
45, u()
46, action(InferenceItemType::Start)
47, si(nullptr)
48, current_depth_limit(0)
49, current_depth(1)
50, depth_limit_reached(false)
51, status()
52, tptp_conversion_string()
53, stack()
54, right_branch_stack()
55, backtrack(false)
56, proof_printer(&stack)
57, problem_path()
58, output_interval(params::output_frequency)
59, proof_count(0)
60, use_timeout(false)
61, end_time()
62, show(params::verbosity)
63, cnf_only(false)
64, conjecture_true(false)
65, conjecture_false(false)
66, conjecture_missing(false)
67, negated_conjecture_removed(false)
68, fof_has_axioms(false)
69, simplified_fof_has_axioms(false)
70{}
71//----------------------------------------------------------------------
73 num_preds = n;
75 path.set_num_preds(n);
76}
77//----------------------------------------------------------------------
78void StackProver::read_from_tptp_file(const string& file_name,
79 bool& found_conjecture,
80 size_t& fof_size) {
82 parser.parse_tptp_from_file(file_name);
83 status = parser.get_problem_status();
84 bool equality = parser.equality_used();
85 found_conjecture = parser.conjecture_present();
86 fof_size = parser.number_of_fof_formulas();
87 Predicate* equals_predicate = parser.get_equals_predicate();
96 parser.clear();
98 /*
99 * num_preds for Matrix is set by parser.
100 */
101 path.set_num_preds(num_preds);
102
103 if (params::show_clauses) {
104 std::exit(EXIT_SUCCESS);
105 }
106
107 if (status != string("") && params::first_parse) {
108 show(1, string("Problem status found: "));
109 show(1, status, true);
110 }
111 if (equality && params::add_equality_axioms) {
112 if (params::first_parse) {
113 show(1, string("Problem involves equality: adding axioms for =."), true);
114 params::first_parse = false;
115 }
116 add_equality_axioms(equals_predicate);
117 if (params::equality_axioms_at_start) {
119 }
120 }
121 /*
122 * Any further variables will be anonymous.
123 */
125
126 if (params::generate_Prolog_proof && params::build_proof) {
127 matrix.write_to_prolog_file(params::Prolog_matrix_path);
128 }
129 }
130//----------------------------------------------------------------------
132 /*
133 * Equality axioms as described in Handbook of Automated
134 * Reasoning, Volume 1, page 615.
135 */
136 Arity max_fun_arity = fun_index.find_maximum_arity();
137 Arity max_pred_arity = pred_index.find_maximum_arity();
138 /*
139 * You need at least three variables to describe these, and
140 * twice as many as the arity of the biggest predicate or
141 * function.
142 */
143 uint32_t max_arity = max_fun_arity;
144 if (max_pred_arity > max_arity)
145 max_arity = max_pred_arity;
146 if (max_arity < 3)
147 max_arity = 3;
148 vector<Term*> xs;
149 vector<Term*> ys;
150 string xvar("__eqx_");
151 string yvar("__eqy_");
152 for (size_t i = 0; i < max_arity; i++) {
153 Variable* xvarp = var_index.add_named_var(xvar + std::to_string(i));
154 Variable* yvarp = var_index.add_named_var(yvar + std::to_string(i));
155 xs.push_back(term_index.add_variable_term(xvarp));
156 ys.push_back(term_index.add_variable_term(yvarp));
157 }
158 /*
159 * How you construct these depends on which representation you're using.
160 * It's easy enough to show that the difference is only a case
161 * of swapping negations. See for example "Equality Preprocessing in
162 * Connection Calculi", Oliver and Otten.
163 */
164 bool pol = !params::positive_representation;
165 uint32_t n_added = 0;
166 /*
167 * Axiom for reflexivity.
168 */
169 vector<Term*> ref;
170 ref.push_back(xs[0]);
171 ref.push_back(xs[0]);
172 Literal reflexive(equals_predicate, ref, 2, pol);
173 Clause ref_c;
174 ref_c.add_lit(reflexive);
175 matrix.add_clause(ref_c, "equality");
176 n_added++;
177 /*
178 * Axiom for symmetry.
179 */
180 vector<Term*> xy;
181 xy.push_back(xs[0]);
182 xy.push_back(xs[1]);
183 vector<Term*> yx;
184 yx.push_back(xs[1]);
185 yx.push_back(xs[0]);
186 Literal sym1(equals_predicate, xy, 2, !pol);
187 Literal sym2(equals_predicate, yx, 2, pol);
188 Clause sym_c;
189 sym_c.add_lit(sym1);
190 sym_c.add_lit(sym2);
191 matrix.add_clause(sym_c, "equality");
192 n_added++;
193 /*
194 * Axiom for transitivity.
195 */
196 vector<Term*> yz;
197 yz.push_back(xs[1]);
198 yz.push_back(xs[2]);
199 vector<Term*> xz;
200 xz.push_back(xs[0]);
201 xz.push_back(xs[2]);
202 Literal tr1(equals_predicate, xy, 2, !pol);
203 Literal tr2(equals_predicate, yz, 2, !pol);
204 Literal tr3(equals_predicate, xz, 2, pol);
205 Clause tr_c;
206 tr_c.add_lit(tr1);
207 tr_c.add_lit(tr2);
208 tr_c.add_lit(tr3);
209 matrix.add_clause(tr_c, "equality");
210 n_added++;
211 /*
212 * Function substitution.
213 */
214 for (size_t j = 0; j < fun_index.get_size(); j++) {
215 Function* p = fun_index[j];
216 Arity ar = p->get_arity();
217 if (ar > 0) {
218 Clause c;
219 vector<Term*> x1xn;
220 vector<Term*> y1yn;
221 for (size_t i = 0; i < ar; i++) {
222 x1xn.push_back(xs[i]);
223 y1yn.push_back(ys[i]);
224 vector<Term*> xiyi;
225 xiyi.push_back(xs[i]);
226 xiyi.push_back(ys[i]);
227 Literal eq_lit(equals_predicate, xiyi, 2, !pol);
228 c.add_lit(eq_lit);
229 }
230 vector<Term*> t;
231 t.push_back(term_index.add_function_term(p, x1xn));
232 t.push_back(term_index.add_function_term(p, y1yn));
233 Literal f_lit(equals_predicate, t, 2, pol);
234 c.add_lit(f_lit);
235 matrix.add_clause(c, "equality");
236 n_added++;
237 }
238 }
239 /*
240 * Predicate substitution.
241 */
242 for (size_t j = 0; j < pred_index.get_num_preds(); j++) {
243 Predicate* p = pred_index[j];
244 Arity ar = p->get_arity();
245 if (ar > 0 && p != equals_predicate) {
246 Clause c;
247 vector<Term*> x1xn;
248 vector<Term*> y1yn;
249 for (size_t i = 0; i < ar; i++) {
250 x1xn.push_back(xs[i]);
251 y1yn.push_back(ys[i]);
252 vector<Term*> xiyi;
253 xiyi.push_back(xs[i]);
254 xiyi.push_back(ys[i]);
255 Literal eq_lit(equals_predicate, xiyi, 2, !pol);
256 c.add_lit(eq_lit);
257 }
258 Literal p_lit1(p, x1xn, ar, !pol);
259 Literal p_lit2(p, y1yn, ar, pol);
260 c.add_lit(p_lit1);
261 c.add_lit(p_lit2);
262 matrix.add_clause(c, "equality");
263 n_added++;
264 }
265 }
266 /*
267 * Distinct objects
268 */
269 Arity min_arity = fun_index.find_minimum_arity();
270 if (!params::no_distinct_objects && min_arity == 0) {
271 vector<Term*> all_distinct_constants;
272 vector<Term*> empty_args;
273 for (size_t i = 0; i < fun_index.get_size(); i++) {
274 Function* p = fun_index[i];
275 Arity ar = p->get_arity();
276 // Remember, you don't want to do this for Skolem constants.
277 string name = p->get_name();
278 string prefix = name.string::substr(0,params::unique_skolem_prefix.length());
279 bool is_skolem = (params::unique_skolem_prefix.string::compare(0, string::npos, prefix) == 0) &&
280 (params::unique_skolem_prefix.length() < name.length());
281 bool is_quoted = (name[0] == '\"' && name[name.size() - 1] == '\"');
282 if (ar == 0 &&
283 !is_skolem &&
284 (params::all_distinct_objects || is_quoted)) {
285 Term* t = term_index.add_function_term(p, empty_args);
286 all_distinct_constants.push_back(t);
287 }
288 }
289 size_t s = all_distinct_constants.size();
290 if (s > 1) {
291 for (size_t i = s - 1; i > 0; i--) {
292 for (size_t j = 0; j < i; j++) {
293 Clause c;
294 vector<Term*> args;
295 args.push_back(all_distinct_constants[i]);
296 args.push_back(all_distinct_constants[j]);
297 Literal eq_lit(equals_predicate, args, 2, !pol);
298 c.add_lit(eq_lit);
299 matrix.add_clause(c, "distinct_objects");
300 n_added++;
301 }
302 }
303 }
304 }
305 matrix.set_num_equals(n_added);
306}
307//----------------------------------------------------------------------
309 /*
310 * Don't waste your time if the regularity test applies.
311 */
312 if (params::use_regularity_test && !path.test_for_regularity(new_C))
313 return;
314 /*
315 * Don't try to populate axioms.
316 */
317 if (new_C.size() == 0) {
318 return;
319 }
320 /*
321 * NOTE: As these are being stacked, lemmata are actually tried
322 * first.
323 */
324 /*
325 * Extensions
326 */
327 if (params::limit_extensions)
329 else
331 /*
332 * Reductions
333 */
334 if (params::limit_reductions)
335 path.find_limited_reductions(u, si->actions, new_C);
336 else
337 path.find_all_reductions(u, si->actions, new_C);
338 /*
339 * Lemmata
340 */
341 if (params::use_lemmata) {
342 if (params::limit_lemmata)
344 else
346 }
347}
348//----------------------------------------------------------------------
350 bool result = ((params::limit_by_tree_depth && (current_depth >= current_depth_limit))
351 ||
352 (!params::limit_by_tree_depth && (si->p.length() >= current_depth_limit)));
353 if (result)
354 depth_limit_reached = true;
355 return result;
356}
357//----------------------------------------------------------------------
358bool StackProver::axiom() const {
359 return si->c.empty();
360}
361//----------------------------------------------------------------------
363 /*
364 * Add a new StackItem using the next action from the list stored
365 * in the StackItem currently in play. If necessary, also
366 * add something to right_branch_stack. Populate the new list of
367 * actions and update si.
368 */
369 action = si->actions.back();
370 si->actions.pop_back();
372 /*
373 * Why are the scope rules for switch so odd???
374 */
375 Clause old_C;
376 Lemmata old_Lem;
377 switch (action.T) {
378 //----------------------------------------------------------------------
379 //----------------------------------------------------------------------
380 //----------------------------------------------------------------------
381 // Lemmata.
382 //----------------------------------------------------------------------
383 //----------------------------------------------------------------------
384 //----------------------------------------------------------------------
385 case InferenceItemType::Lemma:
387 /*
388 * If you are restricting backtracking for lemmata then
389 * at this point you can remove all alternatives.
390 */
391 if (params::limit_bt_lemmas)
393 /*
394 * Work out the new state.
395 */
396 new_C = si->c;
398 path = si->p;
399 lemmata = si->l;
400 /*
401 * Extend the stack.
402 */
403 stack.push_back(StackItem(StackItemType::Lemmata, new_C, path,
405 stack.back().set_this_action(action);
406 break;
407 //----------------------------------------------------------------------
408 //----------------------------------------------------------------------
409 //----------------------------------------------------------------------
410 // Reduction.
411 //----------------------------------------------------------------------
412 //----------------------------------------------------------------------
413 //----------------------------------------------------------------------
414 case InferenceItemType::Reduction:
416 /*
417 * If you are restricting backtracking for reductions then
418 * at this point you can remove all alternatives.
419 */
420 if (params::limit_bt_reductions)
422 /*
423 * Reductions have a substitution, so apply it and remember
424 * in case you need to undo it later.
425 */
428 /*
429 * Work out the new state.
430 */
431 new_C = si->c;
433 path = si->p;
434 lemmata = si->l;
436 /*
437 * Extend the stack.
438 */
439 stack.push_back(StackItem(StackItemType::Reduction, new_C, path,
441 stack.back().set_this_action(action);
442 break;
443 //----------------------------------------------------------------------
444 //----------------------------------------------------------------------
445 //----------------------------------------------------------------------
446 // Extension.
447 //----------------------------------------------------------------------
448 //----------------------------------------------------------------------
449 //----------------------------------------------------------------------
450 case InferenceItemType::Extension:
452 /*
453 * You're going to generate new variables, so remember where to
454 * backtrack to.
455 */
457 /*
458 * This is an Extension, so you're going to add something to
459 * right_branch_stack.
460 */
461 path = si->p;
462 old_C = si->c;
464 old_Lem = si->l;
465 old_Lem.push_back(action.L);
466 /*
467 * DON'T do populate_stack_item here! That can wait until you actually
468 * use the right branch. In fact it *has* to wait because we might
469 * apply substitutions that affect it.
470 */
471 right_branch_stack.push_back(StackItem(StackItemType::RightBranch, old_C,
472 path, old_Lem, current_depth));
473 /*
474 * The right branch needs to know where to restrict backtracking.
475 */
476 right_branch_stack.back().set_bt_restriction_index(stack.size() - 1);
477 /*
478 * Now you can deal with the left branch.
479 */
480 new_C = matrix[action.C_2].make_copy_with_new_vars(var_index, term_index);
481 /*
482 * Extensions have a substitution, so apply it and remember
483 * in case you need to undo it later.
484 */
487 /*
488 * Work out the new state.
489 */
491 path.push(action.L);
492 lemmata = si->l;
493 /*
494 * Extend the stack.
495 */
496 stack.push_back(StackItem(StackItemType::LeftBranch, new_C, path,
498 stack.back().set_this_action(action);
499 break;
500 default:
501 cerr << "PANIC!!! You should only have a lemmata, reduction or an extension here!"
502 << endl;
503 break;
504 }
505 /*
506 * Finally, move si on and work out the next bunch of possible actions.
507 */
508 si = &stack.back();
510}
511//----------------------------------------------------------------------
513 /*
514 * When you're moving forward in the search and you hit an axiom,
515 * you need to see whether there are right branches still needing
516 * to be dealt with.
517 *
518 * Note that an empty right_branch_stack - meaning that you've
519 * found a proof - is dealt with by go().
520 *
521 * this_action does not get populated for the new StackItem in
522 * this case.
523 */
525 /*
526 * Move the next right branch to the stack.
527 */
528 stack.push_back(right_branch_stack.back());
529 right_branch_stack.pop_back();
530 /*
531 * Reset si.
532 */
533 si = &stack.back();
534 /*
535 * Set up the new state.
536 */
537 new_C = si->c;
538 path = si->p;
539 lemmata = si->l;
541 /*
542 * We deliberately delayed doing this, so do it now. (See
543 * documentation for StackProver::extend_with_action.)
544 */
546 /*
547 * At this point you are starting a right branch, so
548 * if you are restricting backtracking you remove all
549 * alternatives from the relevant point in the stack.
550 */
551 if (params::limit_bt_extensions) {
552 stack[si->bt_restriction_index].restrict_backtrack();
553 }
554}
555//----------------------------------------------------------------------
557 backtrack = true;
558 stack.pop_back();
559 si = &stack.back();
561}
562//----------------------------------------------------------------------
567//----------------------------------------------------------------------
571//----------------------------------------------------------------------
573 /*
574 * You're backtracking through a left branch, so you
575 * need to remember to get rid of the corresponding
576 * right branch as well.
577 */
578 right_branch_stack.pop_back();
582}
583//----------------------------------------------------------------------
604 /*
605 * If you're not limiting backtracking for extensions, or
606 * you *are*, but you're still exploring left trees, then this
607 * is straightforward: just put the item back on right_branch_stack
608 * so that it gets explored again later.
609 */
610 if (!params::limit_bt_extensions ||
611 ((params::limit_bt_extensions || params::limit_bt_all) &&
612 !params::limit_bt_extensions_left_tree)) {
613 /*
614 * Why is this necessary? After we backtrack we may make different
615 * substitutions, so in revisiting the right branch different
616 * possibilties may apply, so we re-compute them later.
617 */
618 stack.back().clear();
619 right_branch_stack.push_back(stack.back());
621 return;
622 }
623 /*
624 * We know we are limiting backtracking for extensions, and we
625 * are not exploring the left tree.
626 *
627 * Care is needed if you're not backtracking within the left
628 * part of the tree. You need to move back down the stack,
629 * deleting everything while also making sure that sub_stack
630 * and var_index are correctly maintained. Also, you don't
631 * want to return anything to right_branch_stack.
632 *
633 * This goes back to where the relevant literal was selected.
634 * Thus if you are not limiting the possibilities to only those
635 * for the first literal, it's open to the backtracking
636 * restriction to leave other possibilites to be tried, and
637 * they get picked up from this point.
638 */
639 if (params::limit_bt_extensions_left_tree) {
640 size_t target_index = si->bt_restriction_index;
641 size_t current_index = stack.size() - 1;
642 while (current_index > target_index) {
643 switch (si->item_type) {
644 case StackItemType::Lemmata:
645 break;
646 case StackItemType::Reduction:
648 break;
649 case StackItemType::LeftBranch:
652 break;
653 case StackItemType::RightBranch:
654 break;
655 default:
656 cerr << "Something is VERY WRONG!" << endl;
657 break;
658 }
660 current_index--;
661 }
662 }
663}
664//----------------------------------------------------------------------
665ProverResult StackProver::go() {
666 /*
667 * Having set up a single entry on the stack, containing a start
668 * state, search for a proof.
669 *
670 * Either you return by ending at the start state with nothing left
671 * to try, by finding a proof, by depth limiting or by timing out.
672 *
673 * The backtrack variable is important here - when true you are
674 * (surprise surprise) backtracking. So mostly each case in the
675 * following switch is divided according to whether you're going
676 * forward or backtracking.
677 */
678 while(true) {
679 /*
680 * Test for and deal with a timeout.
681 */
682 if (use_timeout && chrono::steady_clock::now() > end_time)
683 return ProverResult::TimeOut;
684 /*
685 * Say what's going on.
686 */
687 if (output_interval.tick() && params::verbosity >= 2) {
688 cout << cursor_symbols::Cursor::to_column(1);
690 cout << "Reductions: " << reductions_tried << " Extensions: " << extensions_tried;
691 cout << " Lemmata: " << lemmata_tried << " Right branches: " << right_branches_started;
692 cout << " Stack size: " << stack.size();
693 cout.flush();
694 }
695 /*
696 * si must point to the back of the stack at this point.
697 *
698 * Remember that extend_with_action will deal with this for you.
699 */
700 switch (si->item_type) {
701 //----------------------------------------------------------------
702 //----------------------------------------------------------------
703 //----------------------------------------------------------------
704 // Deal with the start state. Essentially straightforward. Just
705 // deal with a completed search, otherwise work out the
706 // possibly actions and get on with it.
707 //----------------------------------------------------------------
708 //----------------------------------------------------------------
709 //----------------------------------------------------------------
710 case StackItemType::Start:
711 backtrack = false;
712 if (si->actions.empty())
713 return ProverResult::OptionsExhausted;
714 else
716 break;
717 //----------------------------------------------------------------
718 //----------------------------------------------------------------
719 //----------------------------------------------------------------
720 // Lemmata. Again, mostly straightforward.
721 //----------------------------------------------------------------
722 //----------------------------------------------------------------
723 //----------------------------------------------------------------
724 case StackItemType::Lemmata:
725 /*
726 * Operation is essentially similar to the reduction case.
727 *
728 * First deal with moving forward.
729 */
730 if (!backtrack) {
731 if (axiom()) {
732 /*
733 * Either you've found a proof or you try a right branch.
734 */
735 if (right_branch_stack.empty())
736 return ProverResult::Valid;
737 else
739 }
740 /*
741 * Backtrack because of depth limiting.
742 */
743 else if (depth_limited() && params::depth_limit_all)
745 /*
746 * Backtrack because there's nothing left to try.
747 */
748 else if (si->actions.empty())
750 /*
751 * There must be something left to try, so try it.
752 */
753 else
755 }
756 /*
757 * We are moving down the stack.
758 */
759 else {
760 /*
761 * If you're backtracking then you need to jump over axioms.
762 */
763 if (axiom())
765 /*
766 * If you're not at an axiom then you can start going forward
767 * again.
768 */
769 else
770 backtrack = false;
771 }
772 break;
773 //----------------------------------------------------------------
774 //----------------------------------------------------------------
775 //----------------------------------------------------------------
776 // Reduction. Almost identical to Lemmata, but note the
777 // slightly different backtracking requirement to take account
778 // of undoing the substitution.
779 //----------------------------------------------------------------
780 //----------------------------------------------------------------
781 //----------------------------------------------------------------
782 case StackItemType::Reduction:
783 /*
784 * We are moving up the stack.
785 */
786 if (!backtrack) {
787 if (axiom()) {
788 /*
789 * Either you've found a proof or you try a right branch.
790 */
791 if (right_branch_stack.empty())
792 return ProverResult::Valid;
793 else
795 }
796 /*
797 * Backtrack because of depth limiting.
798 */
799 else if (depth_limited() && params::depth_limit_all)
801 /*
802 * Backtrack because there's nothing left to try.
803 */
804 else if (si->actions.empty())
806 /*
807 * There must be something left to try, so try it.
808 */
809 else
811 }
812 /*
813 * We are moving down the stack.
814 */
815 else {
816 /*
817 * If you're backtracking then you need to jump over axioms.
818 */
819 if (axiom())
821 /*
822 * If you're not at an axiom then you can start going forward
823 * again.
824 */
825 else
826 backtrack = false;
827 }
828 break;
829 //----------------------------------------------------------------
830 //----------------------------------------------------------------
831 //----------------------------------------------------------------
832 // Left branch of Extension. Mostly similar to the Reduction
833 // and Lemmata cases, but the backtrack is again different to
834 // take care of the new variables, the substitution, and the
835 // right_branch_stack.
836 //----------------------------------------------------------------
837 //----------------------------------------------------------------
838 //----------------------------------------------------------------
839 case StackItemType::LeftBranch:
840 /*
841 * Operation is essentially similar to the Reduction and
842 * Lemmata cases. See those for corresponding comments.
843 */
844 if (!backtrack) {
845 if (axiom())
847 else if (depth_limited())
849 else if (si->actions.empty())
851 else
853 }
854 /*
855 * We are moving down the stack.
856 */
857 else {
858 if (axiom())
860 else
861 backtrack = false;
862 }
863 break;
864 //----------------------------------------------------------------
865 //----------------------------------------------------------------
866 //----------------------------------------------------------------
867 // Right branch of Extension. Mostly similar to the Reduction
868 // and Lemmata cases, but the backtrack is now much more
869 // delicate. See the documentation for right_extension_backtrack.
870 //----------------------------------------------------------------
871 //----------------------------------------------------------------
872 //----------------------------------------------------------------
873 case StackItemType::RightBranch:
874 /*
875 * Operation is essentially similar to the reduction case.
876 */
877 if (!backtrack) {
878 if (axiom()) {
879 if (right_branch_stack.empty())
880 return ProverResult::Valid;
881 else
883 }
884 else if (depth_limited())
886 else if (si->actions.empty())
888 else
890 }
891 /*
892 * We are moving down the stack.
893 */
894 else {
895 if (axiom())
897 else
898 backtrack = false;
899 }
900 break;
901 //----------------------------------------------------------------
902 default:
903 cerr << "Something is VERY WRONG!" << endl;
904 break;
905 }
906 }
907 return ProverResult::Error;
908}
909//----------------------------------------------------------------------
910// Set start clauses according to current options.
911//
912// Note that restrict_start with !all_pos_neg_start and !conjecture_start
913// should never be available as it would make no sense.
914//
915// all_pos_neg_start along with conjecture_start are on a best-effort
916// basis - you may end up with nothing.
917//----------------------------------------------------------------------
919 results.clear();
920 size_t m_size = matrix.get_num_clauses();
921 /*
922 * Make sure noone has messed up and not set any start
923 * clause optionss.
924 */
927 /*
928 * The allstart option overides everything else so this is easy.
929 */
930 if (params::all_start) {
931 for (size_t i = 0; i < m_size; i++) {
932 results.push_back(StartClauseStatus::Start);
933 }
934 return;
935 }
936 bool first_clause_included = false;
937 /*
938 * params::all_pos_neg_start indicates use of positive
939 * or negative start clauses according to the representation.
940 * When you don't also have conjecture_start, either include
941 * all, or just the first possibility found.
942 */
943 if (params::all_pos_neg_start && !params::conjecture_start) {
944 for (size_t i = 0; i < m_size; i++) {
945 if (
946 (
947 (params::positive_representation && matrix.is_positive(i))
948 ||
949 (!params::positive_representation && matrix.is_negative(i))
950 )
951 &&
952 (!(params::restrict_start && first_clause_included))
953 ) {
954 results.push_back(StartClauseStatus::Start);
955 first_clause_included = true;
956 }
957 else {
958 results.push_back(StartClauseStatus::NoStart);
959 }
960 }
961 }
962 /*
963 * Similar case if you have conjecture_start but not all_pos_neg_start.
964 */
965 else if (!params::all_pos_neg_start && params::conjecture_start) {
966 for (size_t i = 0; i < m_size; i++) {
967 if (matrix.is_conjecture(i)
968 &&
969 (!(params::restrict_start && first_clause_included))) {
970 results.push_back(StartClauseStatus::Start);
971 first_clause_included = true;
972 }
973 else {
974 results.push_back(StartClauseStatus::NoStart);
975 }
976 }
977 }
978 /*
979 * The tricky case is when you want to combine pos/neg clauses,
980 * conjecture clauses, and restriction in some other way.
981 *
982 * Assume here that you have all_pos_neg_start and conjecture_start.
983 */
984 else {
985 for (size_t i = 0; i < m_size; i++) {
986 if (matrix.is_conjecture(i)
987 &&
988 (
989 (params::positive_representation && matrix.is_positive(i))
990 ||
991 (!params::positive_representation && matrix.is_negative(i))
992 )
993 &&
994 !(params::restrict_start && first_clause_included)) {
995 results.push_back(StartClauseStatus::Start);
996 first_clause_included = true;
997 }
998 else {
999 results.push_back(StartClauseStatus::NoStart);
1000 }
1001 }
1002 }
1003 /*
1004 * There's a rare possibility that---because either there was no
1005 * (negated) conjecture clause in the problem, or they were
1006 * simplified out---at this point no start clause has been
1007 * selected. If that's the case, either use all positive/negative
1008 * clauses or just the first, according to the parameters set.
1009 *
1010 * Note: this must choose at least one start clause because problems
1011 * without a positive and negative clause have already been solved.
1012 */
1013 if (!first_clause_included) {
1014 if (params::verbosity > 2) {
1015 cout << "Note: you're asking for a conjecture to start, but there are none!" << endl;
1016 cout << " depending on the other parameter settings, we will use one or " << endl;
1017 cout << " all of the negative clauses." << endl;
1018 }
1019 // Don't forget this! If you get here you have a whole bunch of
1020 // NoStart in results!
1021 results.clear();
1022 for (size_t i = 0; i < m_size; i++) {
1023 if ((
1024 (params::positive_representation && matrix.is_positive(i))
1025 ||
1026 (!params::positive_representation && matrix.is_negative(i))
1027 ) &&
1028 !(params::restrict_start && first_clause_included)) {
1029 results.push_back(StartClauseStatus::Start);
1030 first_clause_included = true;
1031 }
1032 else {
1033 results.push_back(StartClauseStatus::NoStart);
1034 }
1035 }
1036 }
1037}
1038//----------------------------------------------------------------------
1039//----------------------------------------------------------------------
1040//----------------------------------------------------------------------
1041// Find a single proof using iterative deepening search.
1042//----------------------------------------------------------------------
1043//----------------------------------------------------------------------
1044//----------------------------------------------------------------------
1045ProverOutcome StackProver::prove() {
1049 if (params::deterministic_reorder) {
1050 deterministic_reorder(params::number_of_reorders);
1051 }
1052 if (params::random_reorder) {
1054 }
1055 if (params::random_reorder_literals) {
1057 }
1058 pair<bool, size_t> start_clause = matrix.find_start();
1059 /*
1060 * If the initial clauses don't have a positive and a negative
1061 * clause then the problem is trivial.
1062 */
1063 if (!start_clause.first) {
1064 return ProverOutcome::False;
1065 }
1066 /*
1067 * Deal with the possible ways to set up start clause(s) according to
1068 * the options. Keep track of which start clauses are in use, and if
1069 * necessary what outcomes for them have been seen so far.
1070 */
1072 /*
1073 * Main loop for iterative deepening search.
1074 */
1075 bool switched_to_complete = false;
1076 for (current_depth_limit = params::start_depth;
1077 current_depth_limit <= params::depth_limit;
1078 current_depth_limit += params::depth_increment) {
1079 /*
1080 * See if the parameters dictate that it's time to convert to
1081 * a complete search.
1082 */
1083 if (current_depth_limit >= params::switch_to_complete
1084 && !switched_to_complete) {
1086 /*
1087 * You may have changed some parameters, so make sure all relevant
1088 * start clauses now get tried.
1089 */
1091 current_depth_limit = params::start_depth;
1092 switched_to_complete = true;
1093 colour_string::ColourString cs(params::use_colours);
1094 show.nl(1);
1095 show(1, cs("Switching to complete search.").orange(), true);
1096 }
1097 show.nl(1);
1098 show(1, string("SEARCH TO DEPTH: "));
1099 show(1, std::to_string(current_depth_limit), true);
1100 /*
1101 * Generate each possible start move, and try to prove from
1102 * it.
1103 */
1104 size_t start_clause_index = 0;
1105 for (const Clause& C : matrix) {
1106 /*
1107 * Find the next start clause.
1108 */
1109 if (results[start_clause_index] == StartClauseStatus::NoStart
1110 || results[start_clause_index] == StartClauseStatus::False) {
1111 start_clause_index++;
1112 continue;
1113 }
1114 /*
1115 * Reset everything to use the current start clause.
1116 *
1117 * TODO: this is slightly messy at present because
1118 * the var_index doesn't necessarily get reset in the
1119 * most efficient way possible if a previous schedule
1120 * attempt timed out. (You'd need to go back down
1121 * the stack and backtrack it as necessary.) This is
1122 * of course irrelevant
1123 * because it just means you might not get full re-use of
1124 * new variable names, but all the same it would be nice
1125 * to fix.
1126 */
1130 /*
1131 * Say what's going on.
1132 */
1133 show(1, string("START from clause "));
1134 show(1, std::to_string(start_clause_index + 1));
1135 show(1, string(" of "));
1136 show(1, std::to_string(matrix.get_num_clauses()));
1137 show(2, string(": "));
1138 show(2, new_C.to_string(), true);
1139 cout.flush();
1140 /*
1141 * Set up the initial stack item containing the start clause, and
1142 * populate it.
1143 */
1144 StackItem start_item(StackItemType::Start, new_C, path, lemmata, 1);
1145 start_item.set_this_action(InferenceItem(InferenceItemType::Start, start_clause_index));
1146 stack.push_back(start_item);
1147 si = &stack.back();
1149 /*
1150 * Start with depth 1, as this makes sense when reading output if you're
1151 * using depth of recursion or path length.
1152 */
1153 current_depth = 1;
1154 /*
1155 * Liftoff!!!
1156 */
1157 ProverResult result = go();
1158 /*
1159 * Dealing with the outcome takes some care and depends on
1160 * the parameters being used.
1161 */
1162 switch (result) {
1163 case ProverResult::Valid:
1164 proof_count++;
1165 if (params::build_proof) {
1166 if (params::generate_LaTeX_proof) {
1167 proof_printer.make_LaTeX(params::LaTeX_proof_path,
1169 matrix.make_LaTeX());
1170 }
1171 if (params::generate_Prolog_proof) {
1172 fs::path prolog_path = params::Prolog_proof_path;
1173 proof_printer.make_Prolog(prolog_path);
1174 }
1175 }
1176 show(1, string(": Found proof number "));
1177 show(1, std::to_string(proof_count), true);
1178 return ProverOutcome::Valid;
1179 break;
1180 case ProverResult::Error:
1181 return ProverOutcome::Error;
1182 break;
1183 case ProverResult::TimeOut:
1184 return ProverOutcome::TimeOut;
1185 break;
1186 case ProverResult::OptionsExhausted:
1187 /*
1188 * If you ran out of options because you reached the depth
1189 * limit then you still need to continue.
1190 */
1191 if (depth_limit_reached) {
1192 show(1, string(": Depth limited"), true);
1193 }
1194 /*
1195 * If you ran out of options without reaching the depth limit, then
1196 * what you do depends on whether or not the search is complete.
1197 */
1198 else {
1200 results[start_clause_index] = StartClauseStatus::False;
1201 show(1, string(": False"), true);
1202 }
1203 }
1204 start_clause_index++;
1205 break;
1206 default:
1207 return ProverOutcome::Error;
1208 break;
1209 }
1210 /*
1211 * This is necessary. Yes, I've checked. Think about it: you need
1212 * one extra backtrack to undo the new variables generated when you
1213 * make a start clause.
1214 */
1216 }
1217 /*
1218 * Loop for start moves ends here.
1219 *
1220 * If everything was False then the theorem is False, otherwise
1221 * at least one attempt was depth-limited.
1222 */
1223 bool all_false = true;
1224 for (StartClauseStatus& outcome : results) {
1225 if (outcome == StartClauseStatus::Start) {
1226 all_false = false;
1227 break;
1228 }
1229 }
1230 if (all_false)
1231 return ProverOutcome::False;
1232 }
1233 /*
1234 * Iterative deepening loop ends here.
1235 */
1236 return ProverOutcome::PathLenLimit;
1237}
1238//----------------------------------------------------------------------
1239vector<pair<string, vector<size_t>>> StackProver::get_internal_proof() const {
1241}
1242//----------------------------------------------------------------------
1243void StackProver::show_stack() {
1244 cout << "--------------------------------------------------------" << endl;
1245 cout << "Stack:" << endl;
1246 cout << "--------------------------------------------------------" << endl;
1247 for (auto s : stack) {
1248 cout << s << endl;
1249 }
1250 cout << "--------------------------------------------------------" << endl;
1251}
1252//----------------------------------------------------------------------
1253void StackProver::show_right_stack() {
1254 cout << "--------------------------------------------------------" << endl;
1255 cout << "Right Stack:" << endl;
1256 cout << "--------------------------------------------------------" << endl;
1257 for (auto s : right_branch_stack) {
1258 cout << s << endl;
1259 }
1260 cout << "--------------------------------------------------------" << endl;
1261}
1262//----------------------------------------------------------------------
1264 verbose_print::VPrint show(params::verbosity);
1265 show(1, string("Reductions: "));
1266 show(1, std::to_string(reductions_tried));
1267 show(1, string(" Extensions: "));
1268 show(1, std::to_string(extensions_tried));
1269 show(1, string(" Lemmata: "));
1270 show(1, std::to_string(lemmata_tried));
1271 show(1, string(" Right branches: "));
1272 show(1, std::to_string(right_branches_started), true);
1273}
1274//----------------------------------------------------------------------
1275ostream& operator<<(ostream& out, const StackProver& p) {
1276 out << "Current state of the RecursiveProver object" << endl;
1277 out << "-------------------------------------------" << endl << endl;
1278 out << p.var_index << endl;
1279 out << p.fun_index << endl;
1280 out << p.term_index << endl;
1281 out << p.path << endl;
1282 out << p.matrix << endl;
1283 return out;
1284}
Representation of clauses.
Definition Clause.hpp:52
bool empty() const
Straightforward get method.
Definition Clause.hpp:82
Clause make_copy_with_new_vars(VariableIndex &, TermIndex &) const
Make a copy of an entire clause, introducing new variables.
Definition Clause.cpp:98
void add_lit(const Literal &)
Add a literal, making sure you don't duplicate.
Definition Clause.cpp:87
size_t size() const
Straightforward get method.
Definition Clause.hpp:78
string to_string(bool=false) const
Convert to a string.
Definition Clause.cpp:202
void drop_literal(LitNum)
Get rid of the specified Literal.
Definition Clause.cpp:135
Basic representation of functions.
Definition Function.hpp:54
Arity get_arity() const
Most basic access function.
Definition Function.hpp:89
string get_name() const
Most basic access function.
Definition Function.hpp:85
Arity find_minimum_arity() const
Find the smallest arity appearing for any Function in the index.
size_t get_size() const
Self-explanatory.
Arity find_maximum_arity() const
Find the largest arity appearing for any Function in the index.
bool tick()
Definition Interval.hpp:54
Representation of the lemma list.
Definition Lemmata.hpp:49
void find_initial_lemmata(vector< InferenceItem > &, Clause &)
Find all lemmata that are applicable, but only for the initial Literal in a Clause.
Definition Lemmata.cpp:33
void find_all_lemmata(vector< InferenceItem > &, Clause &)
Find all lemmata that are applicable, given a Clause.
Definition Lemmata.cpp:56
void push_back(const Literal &)
Self-explanatory.
Definition Lemmata.cpp:28
Basic representation of literals, bundling together (pointers to) a Predicate, a collection of argume...
Definition Literal.hpp:50
void move_equals_to_start()
Self-explanatory.
Definition Matrix.cpp:183
ClauseNum get_num_clauses() const
Straightforward get method.
Definition Matrix.hpp:227
string make_LaTeX(bool=false) const
Make a usable LaTeX representation.
Definition Matrix.cpp:273
void add_clause(Clause &, string="")
Add a Clause to the Matrix and update the index accordingly.
Definition Matrix.cpp:91
bool is_negative(size_t i) const
Is a particular Clause negative?.
Definition Matrix.hpp:247
void find_limited_extensions(Unifier &, vector< InferenceItem > &, Clause &, VariableIndex &, TermIndex &)
Find all possible extensions given a Clause, but only consider the first Literal in the Clause.
Definition Matrix.cpp:228
void find_all_extensions(Unifier &, vector< InferenceItem > &, Clause &, VariableIndex &, TermIndex &)
Find all possible extensions given a Clause, considering all Literals in the Clause.
Definition Matrix.cpp:238
bool is_conjecture(size_t i) const
Is a particular Clause a conjecture?
Definition Matrix.cpp:46
void write_to_prolog_file(const path &) const
Write to a file that can be read by Prolog.
Definition Matrix.cpp:286
void set_num_equals(uint32_t n)
Straightforward set method.
Definition Matrix.hpp:259
bool is_positive(size_t i) const
Is a particular Clause positive?.
Definition Matrix.hpp:241
void set_num_preds(size_t)
Make an empty index.
Definition Matrix.cpp:42
pair< bool, size_t > find_start() const
Use a simple heuristic to find a good start clause.
Definition Matrix.cpp:51
Basic representation of predicates: here just names, ids and arities.
Definition Predicate.hpp:51
Arity get_arity() const
Basic get method.
Definition Predicate.hpp:90
size_t get_num_preds() const
Basic get method.
Arity find_maximum_arity() const
Find the largest arity appearing in the index.
void make_Prolog(const path &)
Convert to a form suitable for use by the Prolog proof checker and write to a file.
void make_LaTeX(const path &, const path &, const string &)
Convert to LaTeX and store in the specified file.
vector< pair< string, vector< size_t > > > make_internal() const
Make a simple data structure representing the proof stack.
uint32_t length() const
Straightforward get method.
Prover using a pair of stacks to conduct the proof search.
void set_num_preds(size_t)
Set the number of predicates.
bool depth_limit_reached
Self-explanatary.
void process_axiom_forward()
Start a right branch to continue from an axiom.
string status
Problem status, if found in input file.
void read_from_tptp_file(const string &, bool &, size_t &)
Obviously, reads a problem from a TPTP file.
vector< StartClauseStatus > results
This is populated by the StackProver::set_up_start_clauses method.
bool negated_conjecture_removed
Keep track of whether the parser simplified the conjecture away.
static uint32_t lemmata_tried
We'll be keeping some simple statistics about the search process.
void lemmata_backtrack()
One of several different kinds of backtracking.
void reset_for_start()
Reset everything so that you can start from a specified start clause.
void random_reorder()
Randomly reorder the matrix.
uint32_t current_depth_limit
Self-explanatary.
InferenceItem action
Stores the next action from the current StackItem.
void set_up_start_clauses()
The start clauses to use depend on the settings, and the settings can change.
size_t num_preds
How many prdicates does the problem of interest have?
bool fof_has_axioms
Keep track of whether the parser found that it's an FOF problem with axioms before simplification.
vector< StackItem > stack
Main stack: this is constructed by the search process and, if completed, represents a proof.
ProverResult go()
This runs the proof search from a given Start Move.
Matrix matrix
A copy of the matrix you're working with.
void deterministic_reorder(uint32_t n)
Deterministically reorder the matrix n times.
PredicateIndex pred_index
This class needs one of each kind of index to keep track of Variables, Terms etc.
vector< StackItem > right_branch_stack
We build the proof by trying the left branches of extensions first: this stack keeps track of the rig...
ProofPrinter proof_printer
You need one of these to print LaTeX output or any kind of proof certificate.
void extend_with_action()
Take a single inference (action) and update the stacks accordingly.
bool cnf_only
Keep track of whether there were any FOF formulas in the problem file.
SimplePath path
At any point in the search process this is a copy of the path for the current node in the proof being...
FunctionIndex fun_index
This class needs one of each kind of index to keep track of Variables, Terms etc.
void populate_stack_item()
Fill the vector of possible actions with everything available.
Lemmata lemmata
At any point in the search process this is a copy of the list of lemmata for the current node in the ...
void show_statistics() const
Display counts of number of extensions tried and so on.
static uint32_t extensions_tried
We'll be keeping some simple statistics about the search process.
uint32_t current_depth
Self-explanatary.
string tptp_conversion_string
TPTP-friendly description of the clause conversion.
void add_equality_axioms(Predicate *)
After reading a problem in which = and/or != appears, add the axioms for equality.
bool simplified_fof_has_axioms
Keep track of whether the parser found that it's an FOF problem with axioms after simplification.
bool conjecture_missing
Keep track of whether the parser found a conjecture in the problem file.
TermIndex term_index
This class needs one of each kind of index to keep track of Variables, Terms etc.
uint32_t proof_count
If we're searching for multiple proofs, keep count of which one this is.
bool conjecture_false
Keep track of whether the parser found the conjecture to be false.
VariableIndex var_index
This class needs one of each kind of index to keep track of Variables, Terms etc.
void random_reorder_literals()
Randomly reorder the literals in each clause in the matrix.
void backtrack_once()
Basic, single step backtrack on the stack.
bool depth_limited()
Test for the depth limit.
bool conjecture_true
Keep track of whether the parser found the conjecture to be true.
void left_extension_backtrack()
One of several different kinds of backtracking.
static uint32_t right_branches_started
We'll be keeping some simple statistics about the search process.
bool backtrack
Are we moving up or down the stack?
chrono::steady_clock::time_point end_time
When do we stop because of a timeout?
vector< pair< string, vector< size_t > > > get_internal_proof() const
Get an internal representation of the proof stack.
StackItem * si
Pointer to the current StackItem.
Clause new_C
At any point in the search process this is a copy of the clause for the current node in the proof bei...
void right_extension_backtrack()
One of several different kinds of backtracking.
fs::path problem_path
Path for the problem of interest.
StackProver()
You only need a basic constructor.
bool use_timeout
Are we using a timeout?
void reduction_backtrack()
One of several different kinds of backtracking.
static uint32_t reductions_tried
We'll be keeping some simple statistics about the search process.
SubstitutionStack sub_stack
There is a separate stack to make application and removal of substitutions straightforward.
verbose_print::VPrint show
Set up printing according to verbosity.
Unifier u
We need a single Unifier to use throught the process.
bool axiom() const
Test to see if you're at an axiom.
ProverOutcome prove()
Here is where the magic happens.
Interval output_interval
How often do you output updates about progress?
void apply() const
Apply a substitution everywhere.
void backtrack()
Remove variables from the stack, and remove substitutions as you go, as far back as the most recent b...
void push_all(Substitution &)
Take all the substitutions provided and add the corresponding variables to the stack.
Wrap up everything the TPTP parser needs to do inside a single class.
bool simplified_fof_has_axioms() const
Where there any axioms for an FOF problem after simplification?
string get_problem_status()
The parse tries to identify the problem status.
void clear()
Clear everything associated with the TPTP parser.
bool parse_tptp_from_file(const string &)
Main method to parse from a TPTP file to the data structures needed by the prover.
bool fof_conjecture_is_missing() const
Was there a conjecture clause in the problem file?
bool equality_used()
Did equality turn up anywhere in the parse?
bool conjecture_present() const
Did a conjecture turn up anywhere in the parse?
bool fof_has_axioms() const
Where there any axioms for an FOF problem before simplification?
string get_tptp_conversion_string() const
Self-explanatory.
bool fof_conjecture_is_false() const
Self-explanatory.
bool fof_conjecture_is_true() const
Self-explanatory.
Predicate * get_equals_predicate() const
If equality turned up anywhere in the parse it will have been turned into an actual Predicate,...
bool problem_is_cnf_only() const
Self-explanatory.
size_t number_of_fof_formulas() const
How many first-order formulas turned up in the parse?
bool fof_negated_conjecture_removed() const
Was there a conjecture clause that got simplified away?
General representation of terms.
Definition Term.hpp:62
Term * add_function_term(Function *, const vector< Term * > &)
Self-explanatory: add a Term containing a function to the index.
Definition TermIndex.cpp:73
Term * add_variable_term(Variable *)
Self-explanatory: add a Term containing a variable to the index.
Definition TermIndex.cpp:56
Basic representation of variables.
Definition Variable.hpp:58
Variable * add_named_var(const string &)
Add a variable with the specified name to the index.
void set_all_names_added()
Call this to indicate that only anonymous variables can now be created.
void backtrack()
Backtrack to the last marker.
void add_backtrack_point()
Add a backtrack point.
Simple addition of colour to strings and ostreams.
static string erase_line(uint8_t n)
Definition cursor.hpp:157
void nl(uint8_t, uint8_t=1)
Full representation of inferences, beyond just the name.
LitNum Lindex
The index of the literal within the clause being used.
LitNum Lprime
The index of the literal in C_2 being used.
Substitution sigma
A copy of the substitution that makes the rule applicable.
Literal L
The Literal that is used to make the inference.
ClauseNum C_2
For extensions, the number of the clause for which a fresh copy is being made.
InferenceItemType T
What kind of inference is this?
Stack items: each contains its own stack of possible next inferences.
Definition StackItem.hpp:51
void restrict_backtrack()
Adjust the collection of actions to limit backtracking.
Definition StackItem.cpp:51
SimplePath p
Each node in the proof tree is a tuple of clause, matrix, path, lemmata: this is the path.
Definition StackItem.hpp:65
vector< InferenceItem > actions
Actions available to try.
Definition StackItem.hpp:84
uint32_t depth
How deep in the proof tree are we?
Definition StackItem.hpp:88
size_t bt_restriction_index
Pointer that allows a right branch to know where to delete alternatives for restricted backtracking.
void set_this_action(const InferenceItem &inf_i)
Basic set method.
Lemmata l
Each node in the proof tree is a tuple of clause, matrix, path, lemmata: this is the lemmata.
Definition StackItem.hpp:70
Clause c
Each node in the proof tree is a tuple of clause, matrix, path, lemmata: this is the clause.
Definition StackItem.hpp:60
StackItemType item_type
What type of StackItem is this?
Definition StackItem.hpp:55
Structure containing all the command line and other options.
static void set_complete_parameters()
Change the parameters to make the search complete.
static bool no_start_options()
Self-explanatory.
static void correct_missing_start_options()
Self-explanatory.
static bool search_is_complete()
Self-explanatory.