Connect++ 0.5.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//----------------------------------------------------------------------
128 /*
129 * Equality axioms as described in Handbook of Automated
130 * Reasoning, Volume 1, page 615.
131 */
132 Arity max_fun_arity = fun_index.find_maximum_arity();
133 Arity max_pred_arity = pred_index.find_maximum_arity();
134 /*
135 * You need at least three variables to describe these, and
136 * twice as many as the arity of the biggest predicate or
137 * function.
138 */
139 uint32_t max_arity = max_fun_arity;
140 if (max_pred_arity > max_arity)
141 max_arity = max_pred_arity;
142 if (max_arity < 3)
143 max_arity = 3;
144 vector<Term*> xs;
145 vector<Term*> ys;
146 string xvar("__eqx_");
147 string yvar("__eqy_");
148 for (size_t i = 0; i < max_arity; i++) {
149 Variable* xvarp = var_index.add_named_var(xvar + std::to_string(i));
150 Variable* yvarp = var_index.add_named_var(yvar + std::to_string(i));
151 xs.push_back(term_index.add_variable_term(xvarp));
152 ys.push_back(term_index.add_variable_term(yvarp));
153 }
154 /*
155 * How you construct these depends on which representation you're using.
156 * It's easy enough to show that the difference is only a case
157 * of swapping negations. See for example "Equality Preprocessing in
158 * Connection Calculi", Oliver and Otten.
159 */
160 bool pol = !params::positive_representation;
161 uint32_t n_added = 0;
162 /*
163 * Axiom for reflexivity.
164 */
165 vector<Term*> ref;
166 ref.push_back(xs[0]);
167 ref.push_back(xs[0]);
168 Literal reflexive(equals_predicate, ref, 2, pol);
169 Clause ref_c;
170 ref_c.add_lit(reflexive);
171 matrix.add_clause(ref_c, "equality");
172 n_added++;
173 /*
174 * Axiom for symmetry.
175 */
176 vector<Term*> xy;
177 xy.push_back(xs[0]);
178 xy.push_back(xs[1]);
179 vector<Term*> yx;
180 yx.push_back(xs[1]);
181 yx.push_back(xs[0]);
182 Literal sym1(equals_predicate, xy, 2, !pol);
183 Literal sym2(equals_predicate, yx, 2, pol);
184 Clause sym_c;
185 sym_c.add_lit(sym1);
186 sym_c.add_lit(sym2);
187 matrix.add_clause(sym_c, "equality");
188 n_added++;
189 /*
190 * Axiom for transitivity.
191 */
192 vector<Term*> yz;
193 yz.push_back(xs[1]);
194 yz.push_back(xs[2]);
195 vector<Term*> xz;
196 xz.push_back(xs[0]);
197 xz.push_back(xs[2]);
198 Literal tr1(equals_predicate, xy, 2, !pol);
199 Literal tr2(equals_predicate, yz, 2, !pol);
200 Literal tr3(equals_predicate, xz, 2, pol);
201 Clause tr_c;
202 tr_c.add_lit(tr1);
203 tr_c.add_lit(tr2);
204 tr_c.add_lit(tr3);
205 matrix.add_clause(tr_c, "equality");
206 n_added++;
207 /*
208 * Function substitution.
209 */
210 for (size_t j = 0; j < fun_index.get_size(); j++) {
211 Function* p = fun_index[j];
212 Arity ar = p->get_arity();
213 if (ar > 0) {
214 Clause c;
215 vector<Term*> x1xn;
216 vector<Term*> y1yn;
217 for (size_t i = 0; i < ar; i++) {
218 x1xn.push_back(xs[i]);
219 y1yn.push_back(ys[i]);
220 vector<Term*> xiyi;
221 xiyi.push_back(xs[i]);
222 xiyi.push_back(ys[i]);
223 Literal eq_lit(equals_predicate, xiyi, 2, !pol);
224 c.add_lit(eq_lit);
225 }
226 vector<Term*> t;
227 t.push_back(term_index.add_function_term(p, x1xn));
228 t.push_back(term_index.add_function_term(p, y1yn));
229 Literal f_lit(equals_predicate, t, 2, pol);
230 c.add_lit(f_lit);
231 matrix.add_clause(c, "equality");
232 n_added++;
233 }
234 }
235 /*
236 * Predicate substitution.
237 */
238 for (size_t j = 0; j < pred_index.get_num_preds(); j++) {
239 Predicate* p = pred_index[j];
240 Arity ar = p->get_arity();
241 if (ar > 0 && p != equals_predicate) {
242 Clause c;
243 vector<Term*> x1xn;
244 vector<Term*> y1yn;
245 for (size_t i = 0; i < ar; i++) {
246 x1xn.push_back(xs[i]);
247 y1yn.push_back(ys[i]);
248 vector<Term*> xiyi;
249 xiyi.push_back(xs[i]);
250 xiyi.push_back(ys[i]);
251 Literal eq_lit(equals_predicate, xiyi, 2, !pol);
252 c.add_lit(eq_lit);
253 }
254 Literal p_lit1(p, x1xn, ar, !pol);
255 Literal p_lit2(p, y1yn, ar, pol);
256 c.add_lit(p_lit1);
257 c.add_lit(p_lit2);
258 matrix.add_clause(c, "equality");
259 n_added++;
260 }
261 }
262 /*
263 * Distinct objects
264 */
265 Arity min_arity = fun_index.find_minimum_arity();
266 if (!params::no_distinct_objects && min_arity == 0) {
267 vector<Term*> all_distinct_constants;
268 vector<Term*> empty_args;
269 for (size_t i = 0; i < fun_index.get_size(); i++) {
270 Function* p = fun_index[i];
271 Arity ar = p->get_arity();
272 // Remember, you don't want to do this for Skolem constants.
273 string name = p->get_name();
274 string prefix = name.string::substr(0,params::unique_skolem_prefix.length());
275 bool is_skolem = (params::unique_skolem_prefix.string::compare(0, string::npos, prefix) == 0) &&
276 (params::unique_skolem_prefix.length() < name.length());
277 bool is_quoted = (name[0] == '\"' && name[name.size() - 1] == '\"');
278 if (ar == 0 &&
279 !is_skolem &&
280 (params::all_distinct_objects || is_quoted)) {
281 Term* t = term_index.add_function_term(p, empty_args);
282 all_distinct_constants.push_back(t);
283 }
284 }
285 size_t s = all_distinct_constants.size();
286 if (s > 1) {
287 for (size_t i = s - 1; i > 0; i--) {
288 for (size_t j = 0; j < i; j++) {
289 Clause c;
290 vector<Term*> args;
291 args.push_back(all_distinct_constants[i]);
292 args.push_back(all_distinct_constants[j]);
293 Literal eq_lit(equals_predicate, args, 2, !pol);
294 c.add_lit(eq_lit);
295 matrix.add_clause(c, "distinct_objects");
296 n_added++;
297 }
298 }
299 }
300 }
301 matrix.set_num_equals(n_added);
302}
303//----------------------------------------------------------------------
305 /*
306 * Don't waste your time if the regularity test applies.
307 */
308 if (params::use_regularity_test && !path.test_for_regularity(new_C))
309 return;
310 /*
311 * Don't try to populate axioms.
312 */
313 if (new_C.size() == 0) {
314 return;
315 }
316 /*
317 * NOTE: As these are being stacked, lemmata are actually tried
318 * first.
319 */
320 /*
321 * Extensions
322 */
323 if (params::limit_extensions)
325 else
327 /*
328 * Reductions
329 */
330 if (params::limit_reductions)
331 path.find_limited_reductions(u, si->actions, new_C);
332 else
333 path.find_all_reductions(u, si->actions, new_C);
334 /*
335 * Lemmata
336 */
337 if (params::use_lemmata) {
338 if (params::limit_lemmata)
340 else
342 }
343}
344//----------------------------------------------------------------------
346 bool result = ((params::limit_by_tree_depth && (current_depth >= current_depth_limit))
347 ||
348 (!params::limit_by_tree_depth && (si->p.length() >= current_depth_limit)));
349 if (result)
350 depth_limit_reached = true;
351 return result;
352}
353//----------------------------------------------------------------------
354bool StackProver::axiom() const {
355 return si->c.empty();
356}
357//----------------------------------------------------------------------
359 /*
360 * Add a new StackItem using the next action from the list stored
361 * in the StackItem currently in play. If necessary, also
362 * add something to right_branch_stack. Populate the new list of
363 * actions and update si.
364 */
365 action = si->actions.back();
366 si->actions.pop_back();
368 /*
369 * Why are the scope rules for switch so odd???
370 */
371 Clause old_C;
372 Lemmata old_Lem;
373 Literal neg_lit;
374 UnificationOutcome outcome;
375 Substitution sig;
376 switch (action.T) {
377 //----------------------------------------------------------------------
378 //----------------------------------------------------------------------
379 //----------------------------------------------------------------------
380 // Lemmata.
381 //----------------------------------------------------------------------
382 //----------------------------------------------------------------------
383 //----------------------------------------------------------------------
384 case InferenceItemType::Lemma:
386 /*
387 * If you are restricting backtracking for lemmata then
388 * at this point you can remove all alternatives.
389 */
390 if (params::limit_bt_lemmas)
392 /*
393 * Work out the new state.
394 */
395 new_C = si->c;
397 path = si->p;
398 lemmata = si->l;
399 /*
400 * Extend the stack.
401 */
402 stack.push_back(StackItem(StackItemType::Lemmata, new_C, path,
404 stack.back().set_this_action(action);
405 break;
406 //----------------------------------------------------------------------
407 //----------------------------------------------------------------------
408 //----------------------------------------------------------------------
409 // Reduction.
410 //----------------------------------------------------------------------
411 //----------------------------------------------------------------------
412 //----------------------------------------------------------------------
413 case InferenceItemType::Reduction:
415 /*
416 * If you are restricting backtracking for reductions then
417 * at this point you can remove all alternatives.
418 */
419 if (params::limit_bt_reductions)
421 /*
422 * Reductions have a substitution, so apply it and remember
423 * in case you need to undo it later.
424 */
427 /*
428 * Work out the new state.
429 */
430 new_C = si->c;
432 path = si->p;
433 lemmata = si->l;
435 /*
436 * Extend the stack.
437 */
438 stack.push_back(StackItem(StackItemType::Reduction, new_C, path,
440 stack.back().set_this_action(action);
441 break;
442 //----------------------------------------------------------------------
443 //----------------------------------------------------------------------
444 //----------------------------------------------------------------------
445 // Extension.
446 //----------------------------------------------------------------------
447 //----------------------------------------------------------------------
448 //----------------------------------------------------------------------
449 case InferenceItemType::Extension:
451 /*
452 * You're going to generate new variables, so remember where to
453 * backtrack to.
454 */
456 /*
457 * This is an Extension, so you're going to add something to
458 * right_branch_stack.
459 */
460 path = si->p;
461 old_C = si->c;
463 old_Lem = si->l;
464 old_Lem.push_back(action.L);
465 /*
466 * DON'T do populate_stack_item here! That can wait until you actually
467 * use the right branch. In fact it *has* to wait because we might
468 * apply substitutions that affect it.
469 */
470 right_branch_stack.push_back(StackItem(StackItemType::RightBranch, old_C,
471 path, old_Lem, current_depth));
472 /*
473 * The right branch needs to know where to restrict backtracking.
474 */
475 right_branch_stack.back().set_bt_restriction_index(stack.size() - 1);
476 /*
477 * Now you can deal with the left branch.
478 */
479 new_C = matrix[action.C_2].make_copy_with_new_vars(var_index, term_index);
480 /*
481 * Extensions have a substitution, so apply it and remember
482 * in case you need to undo it later.
483 */
484 neg_lit = action.L;
485 neg_lit.invert();
486 outcome = u(neg_lit, new_C[action.Lprime]);
487 sig = u.get_substitution();
488 u.backtrack();
489 sig.apply();
490 sub_stack.push_all(sig);
491 /*
492 * Work out the new state.
493 */
495 path.push(action.L);
496 lemmata = si->l;
497 /*
498 * Extend the stack.
499 */
500 stack.push_back(StackItem(StackItemType::LeftBranch, new_C, path,
501 lemmata, sig, current_depth));
502 stack.back().set_this_action(action);
503 break;
504 default:
505 cerr << "PANIC!!! You should only have a lemmata, reduction or an extension here!"
506 << endl;
507 break;
508 }
509 /*
510 * Finally, move si on and work out the next bunch of possible actions.
511 */
512 si = &stack.back();
514}
515//----------------------------------------------------------------------
517 /*
518 * When you're moving forward in the search and you hit an axiom,
519 * you need to see whether there are right branches still needing
520 * to be dealt with.
521 *
522 * Note that an empty right_branch_stack - meaning that you've
523 * found a proof - is dealt with by go().
524 *
525 * this_action does not get populated for the new StackItem in
526 * this case.
527 */
529 /*
530 * Move the next right branch to the stack.
531 */
532 stack.push_back(right_branch_stack.back());
533 right_branch_stack.pop_back();
534 /*
535 * Reset si.
536 */
537 si = &stack.back();
538 /*
539 * Set up the new state.
540 */
541 new_C = si->c;
542 path = si->p;
543 lemmata = si->l;
545 /*
546 * We deliberately delayed doing this, so do it now. (See
547 * documentation for StackProver::extend_with_action.)
548 */
550 /*
551 * At this point you are starting a right branch, so
552 * if you are restricting backtracking you remove all
553 * alternatives from the relevant point in the stack.
554 */
555 if (params::limit_bt_extensions) {
556 stack[si->bt_restriction_index].restrict_backtrack();
557 }
558}
559//----------------------------------------------------------------------
561 backtrack = true;
562 stack.pop_back();
563 si = &stack.back();
565}
566//----------------------------------------------------------------------
571//----------------------------------------------------------------------
575//----------------------------------------------------------------------
577 /*
578 * You're backtracking through a left branch, so you
579 * need to remember to get rid of the corresponding
580 * right branch as well.
581 */
582 right_branch_stack.pop_back();
586}
587//----------------------------------------------------------------------
608 /*
609 * If you're not limiting backtracking for extensions, or
610 * you *are*, but you're still exploring left trees, then this
611 * is straightforward: just put the item back on right_branch_stack
612 * so that it gets explored again later.
613 */
614 if (!params::limit_bt_extensions ||
615 ((params::limit_bt_extensions || params::limit_bt_all) &&
616 !params::limit_bt_extensions_left_tree)) {
617 /*
618 * Why is this necessary? After we backtrack we may make different
619 * substitutions, so in revisiting the right branch different
620 * possibilties may apply, so we re-compute them later.
621 */
622 stack.back().clear();
623 right_branch_stack.push_back(stack.back());
625 return;
626 }
627 /*
628 * We know we are limiting backtracking for extensions, and we
629 * are not exploring the left tree.
630 *
631 * Care is needed if you're not backtracking within the left
632 * part of the tree. You need to move back down the stack,
633 * deleting everything while also making sure that sub_stack
634 * and var_index are correctly maintained. Also, you don't
635 * want to return anything to right_branch_stack.
636 *
637 * This goes back to where the relevant literal was selected.
638 * Thus if you are not limiting the possibilities to only those
639 * for the first literal, it's open to the backtracking
640 * restriction to leave other possibilites to be tried, and
641 * they get picked up from this point.
642 */
643 if (params::limit_bt_extensions_left_tree) {
644 size_t target_index = si->bt_restriction_index;
645 size_t current_index = stack.size() - 1;
646 while (current_index > target_index) {
647 switch (si->item_type) {
648 case StackItemType::Lemmata:
649 break;
650 case StackItemType::Reduction:
652 break;
653 case StackItemType::LeftBranch:
656 break;
657 case StackItemType::RightBranch:
658 break;
659 default:
660 cerr << "Something is VERY WRONG!" << endl;
661 break;
662 }
664 current_index--;
665 }
666 }
667}
668//----------------------------------------------------------------------
669ProverResult StackProver::go() {
670 /*
671 * Having set up a single entry on the stack, containing a start
672 * state, search for a proof.
673 *
674 * Either you return by ending at the start state with nothing left
675 * to try, by finding a proof, by depth limiting or by timing out.
676 *
677 * The backtrack variable is important here - when true you are
678 * (surprise surprise) backtracking. So mostly each case in the
679 * following switch is divided according to whether you're going
680 * forward or backtracking.
681 */
682 while(true) {
683 /*
684 * Test for and deal with a timeout.
685 */
686 if (use_timeout && chrono::steady_clock::now() > end_time)
687 return ProverResult::TimeOut;
688 /*
689 * Say what's going on.
690 */
691 if (output_interval.tick() && params::verbosity >= 2) {
692 cout << cursor_symbols::Cursor::to_column(1);
694 cout << "Reductions: " << reductions_tried << " Extensions: " << extensions_tried;
695 cout << " Lemmata: " << lemmata_tried << " Right branches: " << right_branches_started;
696 cout << " Stack size: " << stack.size();
697 cout.flush();
698 }
699 /*
700 * si must point to the back of the stack at this point.
701 *
702 * Remember that extend_with_action will deal with this for you.
703 */
704 switch (si->item_type) {
705 //----------------------------------------------------------------
706 //----------------------------------------------------------------
707 //----------------------------------------------------------------
708 // Deal with the start state. Essentially straightforward. Just
709 // deal with a completed search, otherwise work out the
710 // possibly actions and get on with it.
711 //----------------------------------------------------------------
712 //----------------------------------------------------------------
713 //----------------------------------------------------------------
714 case StackItemType::Start:
715 backtrack = false;
716 if (si->actions.empty())
717 return ProverResult::OptionsExhausted;
718 else
720 break;
721 //----------------------------------------------------------------
722 //----------------------------------------------------------------
723 //----------------------------------------------------------------
724 // Lemmata. Again, mostly straightforward.
725 //----------------------------------------------------------------
726 //----------------------------------------------------------------
727 //----------------------------------------------------------------
728 case StackItemType::Lemmata:
729 /*
730 * Operation is essentially similar to the reduction case.
731 *
732 * First deal with moving forward.
733 */
734 if (!backtrack) {
735 if (axiom()) {
736 /*
737 * Either you've found a proof or you try a right branch.
738 */
739 if (right_branch_stack.empty())
740 return ProverResult::Valid;
741 else
743 }
744 /*
745 * Backtrack because of depth limiting.
746 */
747 else if (depth_limited() && params::depth_limit_all)
749 /*
750 * Backtrack because there's nothing left to try.
751 */
752 else if (si->actions.empty())
754 /*
755 * There must be something left to try, so try it.
756 */
757 else
759 }
760 /*
761 * We are moving down the stack.
762 */
763 else {
764 /*
765 * If you're backtracking then you need to jump over axioms.
766 */
767 if (axiom())
769 /*
770 * If you're not at an axiom then you can start going forward
771 * again.
772 */
773 else
774 backtrack = false;
775 }
776 break;
777 //----------------------------------------------------------------
778 //----------------------------------------------------------------
779 //----------------------------------------------------------------
780 // Reduction. Almost identical to Lemmata, but note the
781 // slightly different backtracking requirement to take account
782 // of undoing the substitution.
783 //----------------------------------------------------------------
784 //----------------------------------------------------------------
785 //----------------------------------------------------------------
786 case StackItemType::Reduction:
787 /*
788 * We are moving up the stack.
789 */
790 if (!backtrack) {
791 if (axiom()) {
792 /*
793 * Either you've found a proof or you try a right branch.
794 */
795 if (right_branch_stack.empty())
796 return ProverResult::Valid;
797 else
799 }
800 /*
801 * Backtrack because of depth limiting.
802 */
803 else if (depth_limited() && params::depth_limit_all)
805 /*
806 * Backtrack because there's nothing left to try.
807 */
808 else if (si->actions.empty())
810 /*
811 * There must be something left to try, so try it.
812 */
813 else
815 }
816 /*
817 * We are moving down the stack.
818 */
819 else {
820 /*
821 * If you're backtracking then you need to jump over axioms.
822 */
823 if (axiom())
825 /*
826 * If you're not at an axiom then you can start going forward
827 * again.
828 */
829 else
830 backtrack = false;
831 }
832 break;
833 //----------------------------------------------------------------
834 //----------------------------------------------------------------
835 //----------------------------------------------------------------
836 // Left branch of Extension. Mostly similar to the Reduction
837 // and Lemmata cases, but the backtrack is again different to
838 // take care of the new variables, the substitution, and the
839 // right_branch_stack.
840 //----------------------------------------------------------------
841 //----------------------------------------------------------------
842 //----------------------------------------------------------------
843 case StackItemType::LeftBranch:
844 /*
845 * Operation is essentially similar to the Reduction and
846 * Lemmata cases. See those for corresponding comments.
847 */
848 if (!backtrack) {
849 if (axiom())
851 else if (depth_limited())
853 else if (si->actions.empty())
855 else
857 }
858 /*
859 * We are moving down the stack.
860 */
861 else {
862 if (axiom())
864 else
865 backtrack = false;
866 }
867 break;
868 //----------------------------------------------------------------
869 //----------------------------------------------------------------
870 //----------------------------------------------------------------
871 // Right branch of Extension. Mostly similar to the Reduction
872 // and Lemmata cases, but the backtrack is now much more
873 // delicate. See the documentation for right_extension_backtrack.
874 //----------------------------------------------------------------
875 //----------------------------------------------------------------
876 //----------------------------------------------------------------
877 case StackItemType::RightBranch:
878 /*
879 * Operation is essentially similar to the reduction case.
880 */
881 if (!backtrack) {
882 if (axiom()) {
883 if (right_branch_stack.empty())
884 return ProverResult::Valid;
885 else
887 }
888 else if (depth_limited())
890 else if (si->actions.empty())
892 else
894 }
895 /*
896 * We are moving down the stack.
897 */
898 else {
899 if (axiom())
901 else
902 backtrack = false;
903 }
904 break;
905 //----------------------------------------------------------------
906 default:
907 cerr << "Something is VERY WRONG!" << endl;
908 break;
909 }
910 }
911 return ProverResult::Error;
912}
913//----------------------------------------------------------------------
914// Set start clauses according to current options.
915//
916// Note that restrict_start with !all_pos_neg_start and !conjecture_start
917// should never be available as it would make no sense.
918//
919// all_pos_neg_start along with conjecture_start are on a best-effort
920// basis - you may end up with nothing.
921//----------------------------------------------------------------------
923 results.clear();
924 size_t m_size = matrix.get_num_clauses();
925 /*
926 * Make sure noone has messed up and not set any start
927 * clause optionss.
928 */
931 /*
932 * The allstart option overides everything else so this is easy.
933 */
934 if (params::all_start) {
935 for (size_t i = 0; i < m_size; i++) {
936 results.push_back(StartClauseStatus::Start);
937 }
938 return;
939 }
940 bool first_clause_included = false;
941 /*
942 * params::all_pos_neg_start indicates use of positive
943 * or negative start clauses according to the representation.
944 * When you don't also have conjecture_start, either include
945 * all, or just the first possibility found.
946 */
947 if (params::all_pos_neg_start && !params::conjecture_start) {
948 for (size_t i = 0; i < m_size; i++) {
949 if (
950 (
951 (params::positive_representation && matrix.is_positive(i))
952 ||
953 (!params::positive_representation && matrix.is_negative(i))
954 )
955 &&
956 (!(params::restrict_start && first_clause_included))
957 ) {
958 results.push_back(StartClauseStatus::Start);
959 first_clause_included = true;
960 }
961 else {
962 results.push_back(StartClauseStatus::NoStart);
963 }
964 }
965 }
966 /*
967 * Similar case if you have conjecture_start but not all_pos_neg_start.
968 */
969 else if (!params::all_pos_neg_start && params::conjecture_start) {
970 for (size_t i = 0; i < m_size; i++) {
971 if (matrix.is_conjecture(i)
972 &&
973 (!(params::restrict_start && first_clause_included))) {
974 results.push_back(StartClauseStatus::Start);
975 first_clause_included = true;
976 }
977 else {
978 results.push_back(StartClauseStatus::NoStart);
979 }
980 }
981 }
982 /*
983 * The tricky case is when you want to combine pos/neg clauses,
984 * conjecture clauses, and restriction in some other way.
985 *
986 * Assume here that you have all_pos_neg_start and conjecture_start.
987 */
988 else {
989 for (size_t i = 0; i < m_size; i++) {
990 if (matrix.is_conjecture(i)
991 &&
992 (
993 (params::positive_representation && matrix.is_positive(i))
994 ||
995 (!params::positive_representation && matrix.is_negative(i))
996 )
997 &&
998 !(params::restrict_start && first_clause_included)) {
999 results.push_back(StartClauseStatus::Start);
1000 first_clause_included = true;
1001 }
1002 else {
1003 results.push_back(StartClauseStatus::NoStart);
1004 }
1005 }
1006 }
1007 /*
1008 * There's a rare possibility that---because either there was no
1009 * (negated) conjecture clause in the problem, or they were
1010 * simplified out---at this point no start clause has been
1011 * selected. If that's the case, either use all positive/negative
1012 * clauses or just the first, according to the parameters set.
1013 *
1014 * Note: this must choose at least one start clause because problems
1015 * without a positive and negative clause have already been solved.
1016 */
1017 if (!first_clause_included) {
1018 if (params::verbosity > 2) {
1019 cout << "Note: you're asking for a conjecture to start, but there are none!" << endl;
1020 cout << " depending on the other parameter settings, we will use one or " << endl;
1021 cout << " all of the negative clauses." << endl;
1022 }
1023 // Don't forget this! If you get here you have a whole bunch of
1024 // NoStart in results!
1025 results.clear();
1026 for (size_t i = 0; i < m_size; i++) {
1027 if ((
1028 (params::positive_representation && matrix.is_positive(i))
1029 ||
1030 (!params::positive_representation && matrix.is_negative(i))
1031 ) &&
1032 !(params::restrict_start && first_clause_included)) {
1033 results.push_back(StartClauseStatus::Start);
1034 first_clause_included = true;
1035 }
1036 else {
1037 results.push_back(StartClauseStatus::NoStart);
1038 }
1039 }
1040 }
1041}
1042//----------------------------------------------------------------------
1043//----------------------------------------------------------------------
1044//----------------------------------------------------------------------
1045// Find a single proof using iterative deepening search.
1046//----------------------------------------------------------------------
1047//----------------------------------------------------------------------
1048//----------------------------------------------------------------------
1049ProverOutcome StackProver::prove() {
1053 if (params::deterministic_reorder) {
1054 deterministic_reorder(params::number_of_reorders);
1055 }
1056 if (params::random_reorder) {
1058 }
1059 if (params::random_reorder_literals) {
1061 }
1062 pair<bool, size_t> start_clause = matrix.find_start();
1063 /*
1064 * If the initial clauses don't have a positive and a negative
1065 * clause then the problem is trivial.
1066 */
1067 if (!start_clause.first) {
1068 return ProverOutcome::False;
1069 }
1070 /*
1071 * Deal with the possible ways to set up start clause(s) according to
1072 * the options. Keep track of which start clauses are in use, and if
1073 * necessary what outcomes for them have been seen so far.
1074 */
1076 /*
1077 * Main loop for iterative deepening search.
1078 */
1079 bool switched_to_complete = false;
1080 for (current_depth_limit = params::start_depth;
1081 current_depth_limit <= params::depth_limit;
1082 current_depth_limit += params::depth_increment) {
1083 /*
1084 * See if the parameters dictate that it's time to convert to
1085 * a complete search.
1086 */
1087 if (current_depth_limit >= params::switch_to_complete
1088 && !switched_to_complete) {
1090 /*
1091 * You may have changed some parameters, so make sure all relevant
1092 * start clauses now get tried.
1093 */
1095 current_depth_limit = params::start_depth;
1096 switched_to_complete = true;
1097 colour_string::ColourString cs(params::use_colours);
1098 show.nl(1);
1099 show(1, cs("Switching to complete search.").orange(), true);
1100 }
1101 show.nl(1);
1102 show(1, string("SEARCH TO DEPTH: "));
1103 show(1, std::to_string(current_depth_limit), true);
1104 /*
1105 * Generate each possible start move, and try to prove from
1106 * it.
1107 */
1108 size_t start_clause_index = 0;
1109 for (const Clause& C : matrix) {
1110 /*
1111 * Find the next start clause.
1112 */
1113 if (results[start_clause_index] == StartClauseStatus::NoStart
1114 || results[start_clause_index] == StartClauseStatus::False) {
1115 start_clause_index++;
1116 continue;
1117 }
1118 /*
1119 * Reset everything to use the current start clause.
1120 *
1121 * TODO: this is slightly messy at present because
1122 * the var_index doesn't necessarily get reset in the
1123 * most efficient way possible if a previous schedule
1124 * attempt timed out. (You'd need to go back down
1125 * the stack and backtrack it as necessary.) This is
1126 * of course irrelevant
1127 * because it just means you might not get full re-use of
1128 * new variable names, but all the same it would be nice
1129 * to fix.
1130 */
1134 /*
1135 * Say what's going on.
1136 */
1137 show(1, string("START from clause "));
1138 show(1, std::to_string(start_clause_index + 1));
1139 show(1, string(" of "));
1140 show(1, std::to_string(matrix.get_num_clauses()));
1141 show(2, string(": "));
1142 show(2, new_C.to_string(), true);
1143 cout.flush();
1144 /*
1145 * Set up the initial stack item containing the start clause, and
1146 * populate it.
1147 */
1148 StackItem start_item(StackItemType::Start, new_C, path, lemmata, 1);
1149 start_item.set_this_action(InferenceItem(InferenceItemType::Start, start_clause_index));
1150 stack.push_back(start_item);
1151 si = &stack.back();
1153 /*
1154 * Start with depth 1, as this makes sense when reading output if you're
1155 * using depth of recursion or path length.
1156 */
1157 current_depth = 1;
1158 /*
1159 * Liftoff!!!
1160 */
1161 ProverResult result = go();
1162 /*
1163 * Dealing with the outcome takes some care and depends on
1164 * the parameters being used.
1165 */
1166 switch (result) {
1167 case ProverResult::Valid:
1168 proof_count++;
1169 if (params::build_proof) {
1170 if (params::generate_LaTeX_proof) {
1171 proof_printer.make_LaTeX(params::LaTeX_proof_path,
1173 matrix.make_LaTeX());
1174 }
1175 if (params::generate_Prolog_proof) {
1176 fs::path prolog_path = params::Prolog_proof_path;
1177 proof_printer.make_Prolog(prolog_path);
1178 matrix.write_to_prolog_file(params::Prolog_matrix_path);
1179 }
1180 }
1181 show(1, string(": Found proof number "));
1182 show(1, std::to_string(proof_count), true);
1183 return ProverOutcome::Valid;
1184 break;
1185 case ProverResult::Error:
1186 return ProverOutcome::Error;
1187 break;
1188 case ProverResult::TimeOut:
1189 return ProverOutcome::TimeOut;
1190 break;
1191 case ProverResult::OptionsExhausted:
1192 /*
1193 * If you ran out of options because you reached the depth
1194 * limit then you still need to continue.
1195 */
1196 if (depth_limit_reached) {
1197 show(1, string(": Depth limited"), true);
1198 }
1199 /*
1200 * If you ran out of options without reaching the depth limit, then
1201 * what you do depends on whether or not the search is complete.
1202 */
1203 else {
1205 results[start_clause_index] = StartClauseStatus::False;
1206 show(1, string(": False"), true);
1207 }
1208 }
1209 start_clause_index++;
1210 break;
1211 default:
1212 return ProverOutcome::Error;
1213 break;
1214 }
1215 /*
1216 * This is necessary. Yes, I've checked. Think about it: you need
1217 * one extra backtrack to undo the new variables generated when you
1218 * make a start clause.
1219 */
1221 }
1222 /*
1223 * Loop for start moves ends here.
1224 *
1225 * If everything was False then the theorem is False, otherwise
1226 * at least one attempt was depth-limited.
1227 */
1228 bool all_false = true;
1229 for (StartClauseStatus& outcome : results) {
1230 if (outcome == StartClauseStatus::Start) {
1231 all_false = false;
1232 break;
1233 }
1234 }
1235 if (all_false)
1236 return ProverOutcome::False;
1237 }
1238 /*
1239 * Iterative deepening loop ends here.
1240 */
1241 return ProverOutcome::PathLenLimit;
1242}
1243//----------------------------------------------------------------------
1244vector<pair<string, vector<size_t>>> StackProver::get_internal_proof() const {
1246}
1247//----------------------------------------------------------------------
1248void StackProver::show_stack() {
1249 cout << "--------------------------------------------------------" << endl;
1250 cout << "Stack:" << endl;
1251 cout << "--------------------------------------------------------" << endl;
1252 for (auto s : stack) {
1253 cout << s << endl;
1254 }
1255 cout << "--------------------------------------------------------" << endl;
1256}
1257//----------------------------------------------------------------------
1258void StackProver::show_right_stack() {
1259 cout << "--------------------------------------------------------" << endl;
1260 cout << "Right Stack:" << endl;
1261 cout << "--------------------------------------------------------" << endl;
1262 for (auto s : right_branch_stack) {
1263 cout << s << endl;
1264 }
1265 cout << "--------------------------------------------------------" << endl;
1266}
1267//----------------------------------------------------------------------
1269 verbose_print::VPrint show(params::verbosity);
1270 show(1, string("Reductions: "));
1271 show(1, std::to_string(reductions_tried));
1272 show(1, string(" Extensions: "));
1273 show(1, std::to_string(extensions_tried));
1274 show(1, string(" Lemmata: "));
1275 show(1, std::to_string(lemmata_tried));
1276 show(1, string(" Right branches: "));
1277 show(1, std::to_string(right_branches_started), true);
1278}
1279//----------------------------------------------------------------------
1280ostream& operator<<(ostream& out, const StackProver& p) {
1281 out << "Current state of the RecursiveProver object" << endl;
1282 out << "-------------------------------------------" << endl << endl;
1283 out << p.var_index << endl;
1284 out << p.fun_index << endl;
1285 out << p.term_index << endl;
1286 out << p.path << endl;
1287 out << p.matrix << endl;
1288 return out;
1289}
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:52
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:270
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:225
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:235
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:283
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?
General representation of a substitution.
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
void backtrack()
Apply the backtracking process to the substitution that has been constructed.
Definition Unifier.cpp:134
Substitution get_substitution() const
Trivial get methods for the result.
Definition Unifier.hpp:116
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. This may or may not be reusable,...
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.