Connect++ 0.6.0
A fast, readable connection prover for first-order logic.
Loading...
Searching...
No Matches
connect++.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 <iostream>
26#include <vector>
27#include <chrono>
28#include <filesystem>
29#include <ctime>
30#include <cstdlib>
31#include <csignal>
32
33#include <boost/program_options.hpp>
34
35#include "connect++-version.hpp"
36
37#include "StackProver.hpp"
38#include "Schedule.hpp"
39#include "ProofChecker.hpp"
40
41using std::cout;
42using std::endl;
43using std::get;
44
45namespace fs = std::filesystem;
46namespace chrono = std::chrono;
47namespace po = boost::program_options;
48
49using namespace verbose_print;
50using namespace boost::spirit;
51
52/*
53* Various signal handlers.
54*/
55void SIGINT_handler(int p) {
56 cout << endl << "% SZS status Error for "
57 << params::problem_name
58 << " : terminated by SIGINT." << endl;
59 exit(EXIT_FAILURE);
60}
61void SIGXCPU_handler(int p) {
62 cout << endl << "% SZS status Timeout for "
63 << params::problem_name
64 << " : terminated by SIGXCPU." << endl;
65 exit(EXIT_FAILURE);
66}
67void SIGALRM_handler(int p) {
68 cout << endl << "% SZS status Timeout for "
69 << params::problem_name
70 << " : terminated by SIGALRM." << endl;
71 exit(EXIT_FAILURE);
72}
73
74int main(int argc, char** argv) {
75 /*
76 * Start the timer...
77 */
78 chrono::steady_clock::time_point startTime;
79 startTime = chrono::steady_clock::now();
80 /*
81 * Set up handlers for signals.
82 */
83 std::signal(SIGINT, SIGINT_handler);
84 std::signal(SIGXCPU, SIGXCPU_handler);
85 std::signal(SIGALRM, SIGALRM_handler);
86 /*
87 * Pick up relevant environment variables for the
88 * TPTP library etc. Some of these may be overidden by a
89 * command line argument later.
90 */
91 char* tptp_env = getenv("TPTP");
92 if (tptp_env != nullptr) {
93 params::tptp_path = fs::path(tptp_env);
94 }
95 char* pwd_env = getenv("PWD");
96 if (pwd_env != nullptr) {
97 params::pwd_path = fs::path(pwd_env);
98 }
99 char* path_env = getenv("CONNECTPP_PATH");
100 if (path_env != nullptr) {
101 params::connectpp_path = fs::path(path_env);
102 }
103 else {
104 params::connectpp_path = params::pwd_path;
105 }
106 /*
107 * Initially, use the Boost Program Options library to decipher
108 * what's been added in the command line and deal with it.
109 */
110 po::options_description main_options("Basic options");
111 main_options.add_options()
112 ("help,h",
113 "Show this message.")
114 ("version",
115 "Show version number.")
116 ("input,i", po::value<string>(),
117 "Input filename.")
118 ("output,o", po::value<string>(),
119 "Write a short output summary (file, status, outcome, time etc) to the specified file. Using \"default\" places it in \"./output_summary.txt\"" )
120 ("no-equality",
121 "Don't add equality axioms. (For example, if they were added elsewhere.) NOTE: also inhibits adding of distinct object axioms.")
122 ("equality-last",
123 "Place equality axioms at the end of the matrix, not the beginning")
124 ("all-distinct-objects",
125 "By default Connect++ follows the TPTP convention of considering \"foobar\" to be a distinct object. This option makes *all* constants distinct.")
126 ("no-distinct-objects",
127 "By default Connect++ follows the TPTP convention of considering \"foobar\" to be a distinct object. This option disables that behaviour, so constants can be equal to others.")
128 ("timeout,t", po::value<int>(),
129 "Timeout in seconds. (Default UINT32_MAX).")
130 ("show-default-schedule",
131 "Display the build-in default schedule and halt.")
132 ("schedule,s", po::value<string>(),
133 "Instead of running with fixed parameters, use a schedule from the specified file. Use value \"default\" for the default schedule. If no --timeout is specified use 600s.")
134 ("random-seed", po::value<int>(),
135 "Specify the (non-negative, integer) seed to use for random number generation. Default 0.")
136 ("poly-unification",
137 "Use the alternative, polynomial-time unification algorithm.")
138 ("tptp", po::value<string>(),
139 "Specify a path for the TPTP. Alternative to using the TPTP environment variable.")
140 ("path", po::value<string>(),
141 "Specify the path for output files other than the one produced by --output. Alternative to using the CONNECTPP_PATH environment variable.");
142
143 po::options_description conversion_options("Clause conversion");
144 conversion_options.add_options()
145 ("no-definitional",
146 "Don't apply definitional transformation for any formula.")
147 ("all-definitional",
148 "Apply definitional transformation to all formulas.")
149 ("reorder,r", po::value<int>(),
150 "Reorder the clauses a specified number of times.")
151 ("random-reorder",
152 "Randomly reorder the clauses.")
153 ("random-reorder-literals",
154 "Randomly reorder the literals in each clause in the matrix.")
155 ("no-miniscope",
156 "Don't miniscope when converting to CNF.")
157 ("show-clauses",
158 "Show translation of first-order formulas to clauses and halt.");
159
160 po::options_description output_options("Output formatting");
161 output_options.add_options()
162 ("verbosity,v", po::value<int>(),
163 "Set verbosity level (0-3). Default is 2.")
164 ("no-colour",
165 "Don't use colour to highlight output.")
166 ("sub-output",
167 "Include substitutions when writing output.")
168 ("indent", po::value<int>(),
169 "Set indentation size. (Default 4.)")
170 ("out-int", po::value<int>(),
171 "Interval for updating output (default 50,000).");
172
173 po::options_description search_options("Search control");
174 search_options.add_options()
175 ("all-start",
176 "Use all start clauses. (Overides other start clause options.)")
177 ("pos-neg-start",
178 "Use all positive/negative start clauses, according to representation (currently always negative). (Interacts with other start clause options.)")
179 ("conjecture-start",
180 "Use all conjecture clauses as start clauses. (Interacts with other start clause options.)")
181 ("restrict-start",
182 "Only use one start clause. (Interacts with other start clause options.)")
183 ("no-regularity",
184 "Don't use the regularity test.")
185 ("all-reductions",
186 "Compute reductions for all literals in the clause, not just the first one.")
187 ("all-extensions",
188 "Compute extensions for all literals in the clause, not just the first one.")
189 ("all-lemmata",
190 "When using lemmata, consider all literals in the clause, not just the first one.")
191 ("no-lemmata",
192 "Do not use lemmata.");
193
194 po::options_description backtrack_options("Backtracking");
195 backtrack_options.add_options()
196 ("all-backtrack",
197 "Do not limit any backtracking.")
198 ("lemmata-backtrack",
199 "Do not limit backtracking for lemmata.")
200 ("reduction-backtrack",
201 "Do not limit backtracking for reductions.")
202 ("extension-backtrack",
203 "Do not limit backtracking for extensions.")
204 ("explore-left-trees",
205 "Apply backtracking within left extension trees.")
206 ("complete,c", po::value<int>(),
207 "Start again with a complete search on reaching the specified depth. (Default UINT32_MAX.)");
208
209 po::options_description depth_options("Depth limiting");
210 depth_options.add_options()
211 ("depth-limit,d", po::value<int>(),
212 "Maximum depth/path length. (Default UINT32_MAX.)")
213 ("start-depth", po::value<int>(),
214 "Initial depth/path length. (Default 1.)")
215 ("depth-increment", po::value<int>(),
216 "Increment for iterative deepening. (Default 1.)")
217 ("depth-limit-all",
218 "By default we do not depth limit on reductions or lemmata. Set this if you want to.")
219 ("limit-by-tree-depth",
220 "Use tree depth (not path length) for deepening.");
221
222 po::options_description proof_options("Proof generation");
223 proof_options.add_options()
224 ("tptp-proof",
225 "Generate a proof compatible with the TPTP. Note that what constitutes a TPTP-compatible proof in connection calculus is currently a moving target, so the format is subject to change. Output is to stdout.")
226 ("prolog-proof",
227 "Generate Prolog format files proof.pl and matrix.pl and store in the $CONNECTPP_PATH directory. You can check these using the check_proof utility.")
228 ("full-verify",
229 "Verify any proof found using the internal proof checker. Use this version to get a detailed description of the verification sent to stdout.")
230 ("verify",
231 "Verify any proof found using the internal proof checker. Use this version for a quiet verification that only adds \"Verified\" or \"FaliedVerification\" to the output string.");
232
233 po::options_description latex_options("LaTeX output");
234 latex_options.add_options()
235 ("latex", po::value<string>(),
236 "Make a LaTeX file containing a typeset proof. Use \"default\" for \"./latex_proof.tex\" or specify a destination.")
237 ("sub-latex",
238 "Include substitutions in the LaTeX proof.")
239 ("tiny-latex",
240 "Typeset the latex proof in a tiny font.")
241 ("latex-no-matrix",
242 "Don't include the matrix in the LaTeX proof.");
243
244 po::options_description all_options("Options");
245 all_options.add(main_options)
246 .add(conversion_options)
247 .add(output_options)
248 .add(search_options)
249 .add(backtrack_options)
250 .add(depth_options)
251 .add(proof_options)
252 .add(latex_options);
253
254 po::positional_options_description pos_opts;
255 pos_opts.add("input", -1);
256 po::variables_map vars_map;
257 try {
258 po::store(po::command_line_parser(argc, argv).
259 options(all_options).
260 positional(pos_opts).
261 run(), vars_map);
262 }
263 /*
264 * If the command line options are in error then we exit here.
265 */
266 catch (boost::program_options::error& err) {
267 cout << "% SZS status Error for "
268 << params::problem_name << " : " << err.what() << endl;
269 return EXIT_FAILURE;
270 }
271 po::notify(vars_map);
272 /*
273 * We had a correct parse of the command line, so now we deal with
274 * the options.
275 */
276 if (vars_map.count("help")) {
277 cout << all_options << endl;
278 return EXIT_SUCCESS;
279 }
280 if (vars_map.count("version")) {
281 cout << "Connect++ Version V" << VERSION << "." << endl;
282 cout << "Copyright © 2021-24 Sean Holden. All rights reserved." << endl;
283 return EXIT_SUCCESS;
284 }
285 if (vars_map.count("verbosity")) {
286 int v = vars_map["verbosity"].as<int>();
287 if (v >= 0 && v <= 3)
288 params::verbosity = v;
289 else
290 cerr << "Verbosity should be between 0 and 3. Using default (2)." << endl;
291 }
292 /*
293 * VPrint automates conditional output according to the verbosity setting.
294 */
295 VPrint show(params::verbosity);
296 /*
297 * Back to processing parameters.
298 */
299 if (vars_map.count("sub-output")) {
300 params::output_terms_with_substitution = true;
301 }
302 if (vars_map.count("indent")) {
303 int i = vars_map["indent"].as<int>();
304 if (i >= 0 && i <= UINT8_MAX)
305 params::indent_size = i;
306 else
307 cerr << "Indent size is negative or too large. Using default." << endl;
308 }
309 if (vars_map.count("out-int")) {
310 int i = vars_map["out-int"].as<int>();
311 if (i > 0 && i <= UINT32_MAX)
312 params::output_frequency = i;
313 else
314 cerr << "Interval is negative or too large. Using default." << endl;
315 }
316 if (vars_map.count("no-equality")) {
317 params::add_equality_axioms = false;
318 }
319 if (vars_map.count("equality-last")) {
320 params::equality_axioms_at_start = false;
321 }
322 if (vars_map.count("all-distinct-objects")) {
323 params::all_distinct_objects = true;
324 }
325 if (vars_map.count("no-distinct-objects")) {
326 if (params::all_distinct_objects) {
327 cerr << "Cancelled --all-distinct-objects." << endl;
328 params::all_distinct_objects = false;
329 }
330 params::no_distinct_objects = true;
331 }
332 if (vars_map.count("timeout")) {
333 params::timeout = true;
334 int t = vars_map["timeout"].as<int>();
335 if (t > 0) {
336 if (t >= 10)
337 params::timeout_value = t;
338 else {
339 cerr << "Timeout is too small. Using 10 seconds." << endl;
340 params::timeout_value = 10;
341 }
342 }
343 else
344 cerr << "Timeout should be positive. Using default." << endl;
345 }
346 if (vars_map.count("show-default-schedule")) {
347 params::set_default_schedule();
348 cout << params::default_schedule << endl;
349 exit(EXIT_SUCCESS);
350 }
351 if (vars_map.count("path")) {
352 params::connectpp_path = vars_map["path"].as<string>();
353 }
354 /*
355 * You should now have all the paths you need, so
356 * finalize them where possible.
357 */
358 params::LaTeX_proof_path
359 = params::pwd_path / params::LaTeX_proof_path;
360 params::Prolog_matrix_path
361 = params::connectpp_path / params::Prolog_matrix_path;
362 params::Prolog_proof_path
363 = params::connectpp_path / params::Prolog_proof_path;
364 params::output_summary_path
365 = params::pwd_path / params::output_summary_path;
366 /*
367 * Back to processing parameters.
368 */
369 if (vars_map.count("output")) {
370 params::write_output_summary = true;
371 string op = vars_map["output"].as<string>();
372 if (op != "default")
373 params::output_summary_path = op;
374 }
375 if (vars_map.count("all-definitional")) {
376 params::all_definitional = true;
377 }
378 if (vars_map.count("no-definitional")) {
379 params::no_definitional = true;
380 if (params::all_definitional) {
381 cerr << "Setting --no-definitional has cancelled --all-definitional." << endl;
382 params::all_definitional = false;
383 }
384 }
386 if (vars_map.count("schedule")) {
387 params::use_schedule = true;
388 if (!params::timeout) {
389 params::timeout = true;
390 params::timeout_value = 600;
391 }
392 params::schedule_path = vars_map["schedule"].as<string>();
393 try {
394 schedule.read_schedule_from_file(params::schedule_path);
395 }
396 catch (std::exception& err) {
397 cout << "% SZS status Error for "
398 << params::problem_name << " : " << err.what() << endl;
399 return EXIT_FAILURE;
400 }
401 }
402 /*
403 * Note: overides $TPTP environment variable, which is read
404 * earlier.
405 */
406 if (vars_map.count("tptp")) {
407 params::tptp_path = vars_map["tptp"].as<string>();
408 }
409 if (vars_map.count("reorder")) {
410 int n = vars_map["reorder"].as<int>();
411 if (n > 0) {
412 params::deterministic_reorder = true;
413 params::number_of_reorders = n;
414 }
415 else
416 cerr << "Number of re-orders should be positive. Skipping." << endl;
417 }
418 if (vars_map.count("random-seed")) {
419 int seed = vars_map["random-seed"].as<int>();
420 if (seed >= 0) {
421 params::random_seed = static_cast<unsigned>(seed);
422 params::boost_random_seed = static_cast<uint32_t>(seed);
423 }
424 else {
425 cerr << "Error: random seed must be a non-negative integer. Using default of 0." << endl;
426 }
427 }
428 if (vars_map.count("random-reorder")) {
429 params::random_reorder = true;
430 }
431 if (vars_map.count("random-reorder-literals")) {
432 params::random_reorder_literals = true;
433 }
434 if (vars_map.count("no-miniscope")) {
435 params::miniscope = false;
436 }
437 if (vars_map.count("show-clauses")) {
438 params::show_clauses = true;
439 }
440 if (vars_map.count("no-colour")) {
441 params::use_colours = false;
442 }
443 if (vars_map.count("poly-unification")) {
444 params::poly_unification = true;
445 }
446 /*
447 * Set up the path for the input file. A path to the TPTP may
448 * have been set by now either by picking up the $TPTP environment
449 * variable or using a command-line option.
450 *
451 * This looks really over-engineered, but the complexity that
452 * follows in dealing with TPTP "includes" probably makes that
453 * a good idea.
454 *
455 * The desired behaviour here is:
456 *
457 * - If it's an absolute path then we're done.
458 *
459 * - If it's a relative path then see if it exists.
460 *
461 * - If it doesn't exist then look for it relative to
462 * $TPTP.
463 */
464 fs::path initial_path;
465 bool found_file = false;
466 if (vars_map.count("input")) {
467 // Get what was on the command line.
468 initial_path = vars_map["input"].as<string>();
469
470 // Is it relative?
471 if (initial_path.is_relative()) {
472 // If it's relative, does it exist?
473 if (fs::exists(initial_path)) {
474 // It's an existing, relative path, so expand it. Belt
475 // and braces - make sure you catch things...
476 try {
477 initial_path = fs::canonical(initial_path);
478 }
479 catch (std::filesystem::filesystem_error& err) {
480 cout << "% SZS status Error for "
481 << params::problem_name << " : "
482 << err.what() << endl;
483 return EXIT_FAILURE;
484 }
485 found_file = true;
486 }
487 // It's relative, but doesn't exist...
488 else {
489 // So see if it's relative to $TPTP.
490 fs::path tptp_path(params::tptp_path);
491 initial_path = tptp_path / initial_path;
492 // Does it exist now?
493 if (fs::exists(initial_path)) {
494 // Yes! Expand it. Belt and braces again...
495 try {
496 initial_path = fs::canonical(initial_path);
497 }
498 catch (std::filesystem::filesystem_error& err) {
499 cout << "% SZS status Error for "
500 << params::problem_name << " : "
501 << err.what() << endl;
502 return EXIT_FAILURE;
503 }
504 found_file = true;
505 }
506 }
507 }
508 else {
509 // It's an absolute path.
510 if (initial_path.is_absolute() && fs::exists(initial_path)) {
511 // Found it! Tidy it up. (Yes, belt and braces etc...)
512 try {
513 initial_path = fs::canonical(initial_path);
514 }
515 catch (std::filesystem::filesystem_error& err) {
516 cout << "% SZS status Error for "
517 << params::problem_name << " : "
518 << err.what() << endl;
519 return EXIT_FAILURE;
520 }
521 found_file = true;
522 }
523 }
524 }
525 else {
526 cout << "% SZS status Error for "
527 << params::problem_name << " : no problem specified." << endl;
528 return EXIT_FAILURE;
529 }
530 // Have we found an existing file?
531 if (!found_file) {
532 cout << "% SZS status Error for "
533 << params::problem_name << " : the specified file does not exist." << endl;
534 return EXIT_FAILURE;
535 }
536 // At this point we have a nice, tidy absolute path.
537 params::full_problem_path = initial_path;
538 params::full_problem_path.remove_filename();
539 params::problem_name = initial_path.stem().string();
540 fs::path path = initial_path;
541 /*
542 * Finished sorting out the file/path. Back to the other
543 * options.
544 */
545 if (vars_map.count("depth-limit")) {
546 int l = vars_map["depth-limit"].as<int>();
547 if (l > 1)
548 params::depth_limit = l;
549 else
550 cerr << "Depth limit should be positive. Using default." << endl;
551 }
552 if (vars_map.count("start-depth")) {
553 int l = vars_map["start-depth"].as<int>();
554 if (l > 1 && l <= params::depth_limit)
555 params::start_depth = l;
556 else
557 cerr << "Start depth should be positive and bounded by the maximum depth. Using default." << endl;
558 }
559 if (vars_map.count("depth-increment")) {
560 int l = vars_map["depth-increment"].as<int>();
561 if (l > 0)
562 params::depth_increment = l;
563 else
564 cerr << "Depth increment should be positive. Using default." << endl;
565 }
566 if (vars_map.count("depth-limit-all")) {
567 params::depth_limit_all = true;
568 }
569 if (vars_map.count("limit-by-tree-depth")) {
570 params::limit_by_tree_depth = true;
571 }
584 if (vars_map.count("restrict-start")) {
585 params::restrict_start = true;
586 }
587 if (vars_map.count("pos-neg-start")) {
588 params::all_pos_neg_start = true;
589 }
590 if (vars_map.count("conjecture-start")) {
591 params::conjecture_start = true;
592 }
593 if (vars_map.count("all-start")) {
594 show(2, string("--all-start set. This cancels all other start options."), true);
596 }
597 /*
598 * Careful! It is possible that no start options were set. If that's
599 * the case just set a sensible one.
600 */
603 }
604 /*
605 * Back to processing parameters.
606 */
607 if (vars_map.count("no-regularity")) {
608 params::use_regularity_test = false;
609 }
610 if (vars_map.count("lemmata-backtrack")) {
611 params::limit_bt_lemmas = false;
612 }
613 if (vars_map.count("reduction-backtrack")) {
614 params::limit_bt_reductions = false;
615 }
616 if (vars_map.count("extension-backtrack")) {
617 params::limit_bt_extensions = false;
618 }
619 if (vars_map.count("explore-left-trees")) {
620 params::limit_bt_extensions_left_tree = false;
621 }
622 if (vars_map.count("all-backtrack")) {
623 show(2, string("--all-backtrack set. This prevails over all other backtracking options."), true);
625 }
626 if (vars_map.count("all-reductions")) {
627 params::limit_reductions = false;
628 }
629 if (vars_map.count("all-extensions")) {
630 params::limit_extensions = false;
631 }
632 if (vars_map.count("all-lemmata")) {
633 params::limit_lemmata = false;
634 }
635 if (vars_map.count("no-lemmata")) {
636 params::use_lemmata = false;
637 }
638 if (vars_map.count("complete")) {
639 int d = vars_map["complete"].as<int>();
640 if (d >= params::start_depth)
641 params::switch_to_complete = d;
642 else {
643 cerr << "Switch to complete search must happen after start depth. Using start depth." << endl;
644 params::switch_to_complete = params::start_depth;
645 }
646 }
647 if (vars_map.count("latex")) {
648 params::generate_LaTeX_proof = true;
649 params::build_proof = true;
650 string lp = vars_map["latex"].as<string>();
651 if (lp != "default")
652 params::LaTeX_proof_path = lp;
653 }
654 if (vars_map.count("sub-latex")) {
655 params::sub_LaTeX_proof = true;
656 }
657 if (vars_map.count("tiny-latex")) {
658 params::latex_tiny_proof = true;
659 }
660 if (vars_map.count("latex-no-matrix")) {
661 params::latex_include_matrix = false;
662 }
663 if (vars_map.count("tptp-proof")) {
664 params::generate_tptp_proof = true;
665 params::build_proof = true;
666 }
667 if (vars_map.count("prolog-proof")) {
668 params::generate_Prolog_proof = true;
669 params::build_proof = true;
670 }
671 if (vars_map.count("verify")) {
672 params::verify_proof = true;
673 params::build_proof = true;
674 }
675 if (vars_map.count("full-verify")) {
676 params::verify_proof = true;
677 params::verify_proof_verbose = true;
678 params::build_proof = true;
679 }
680 //------------------------------------------------------------------------
681 //------------------------------------------------------------------------
682 //------------------------------------------------------------------------
683 /*
684 * Now we're ready to start the party.
685 */
686 //------------------------------------------------------------------------
687 //------------------------------------------------------------------------
688 //------------------------------------------------------------------------
689 /*
690 * First, state the time we're starting at.
691 */
692 show(1, string("Connect++ V"));
693 show(1, string(VERSION));
694 show(1, string(" started at: "));
695 time_t date_and_time;
696 time(&date_and_time);
697 show(1, asctime(localtime(&date_and_time)));
698 /*
699 * You'll obviously be needing a prover.
700 *
701 * But it's not that simple! If we're running with
702 * no schedule then we'll just need one. However
703 * if we're running with a schedule then the matrix can be
704 * different for different sets of parameters, in particular
705 * because of --no-definitional and --all-definitional.
706 * We'll therefore use three different provers and
707 * switch between them.
708 */
709 StackProver prover_standard;
710 StackProver prover_all_definitional;
711 StackProver prover_no_definitional;
712 StackProver* prover = &prover_standard;
713 /*
714 * See if you can read the supplied input file.
715 *
716 * Make sure that if you're using a schedule, you
717 * make one for the default settings. If no schedule is
718 * being used then the command line settings will get used
719 * to make prover_standard.
720 */
721 if (params::use_schedule) {
722 params::no_definitional = false;
723 params::all_definitional = false;
724 }
725 show(1, string("Reading from: "));
726 show(1, path.c_str(), true);
727 bool found_conjecture = false;
728 size_t fof_size = 0;
729 try {
730 prover_standard.read_from_tptp_file(path.c_str(), found_conjecture, fof_size);
731 }
732 catch (std::exception& err) {
733 cout << "% SZS status Error for "
734 << params::problem_name << " : "
735 << err.what() << endl;
736 return EXIT_FAILURE;
737 }
738 /*
739 * If you're using a schedule, then set up provers for the
740 * extra cases.
741 */
742 if (params::use_schedule) {
743 params::all_definitional = true;
744 try {
745 prover_all_definitional.read_from_tptp_file(path.c_str(), found_conjecture, fof_size);
746 }
747 catch (std::exception& err) {
748 cout << "% SZS status Error for "
749 << params::problem_name << " : "
750 << err.what() << endl;
751 return EXIT_FAILURE;
752 }
753 params::all_definitional = false;
754 params::no_definitional = true;
755 try {
756 prover_no_definitional.read_from_tptp_file(path.c_str(), found_conjecture, fof_size);
757 }
758 catch (std::exception& err) {
759 cout << "% SZS status Error for "
760 << params::problem_name << " : "
761 << err.what() << endl;
762 return EXIT_FAILURE;
763 }
764 }
765 /*
766 * How long did all that take? Remember that reading from the file does all the
767 * necessary conversion of formulas to clauses.
768 */
769 chrono::milliseconds parse_time =
770 chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now() - startTime);
771 show(1, string("Total parse and conversion time: "));
772 show(1, std::to_string(parse_time.count()));
773 show(1, string(" milliseconds."), true);
774
775 prover_standard.set_problem_path(path);
776 prover_all_definitional.set_problem_path(path);
777 prover_no_definitional.set_problem_path(path);
778
779 show(2, string("Starting proof attempt for: "), false);
780 show(2, params::problem_name, true);
784 ProverOutcome prover_outcome;
785 ProverOutcome final_outcome;
786 final_outcome = ProverOutcome::TimeOut;
787 //------------------------------------------------------------------------
788 //------------------------------------------------------------------------
789 //------------------------------------------------------------------------
790 /*
791 * No schedule.
792 */
793 //------------------------------------------------------------------------
794 //------------------------------------------------------------------------
795 //------------------------------------------------------------------------
796 if (!params::use_schedule) {
797 if (params::verbosity > 2) {
798 prover_standard.show_matrix();
799 }
800 /*
801 * We're not using a schedule, so just run once using the
802 * command line options.
803 */
804 /*
805 * You want any timeout to include everything...
806 */
807 if (params::timeout) {
808 chrono::seconds timeout_duration(params::timeout_value);
809 prover_standard.set_timeout(startTime + timeout_duration);
810 }
811 /*
812 * Run the prover.
813 */
814 prover_outcome = prover_standard.prove();
815 final_outcome = prover_outcome;
816 }
817 //------------------------------------------------------------------------
818 //------------------------------------------------------------------------
819 //------------------------------------------------------------------------
820 /*
821 * A schedule is being used.
822 */
823 //------------------------------------------------------------------------
824 //------------------------------------------------------------------------
825 //------------------------------------------------------------------------
826 else {
827 /*
828 * Run using the specified or default schedule.
829 */
830 if (!params::timeout) {
831 params::timeout = true;
832 params::timeout_value = 600;
833 }
834 /*
835 * Convert to milliseconds and get a standard chunk of time.
836 */
837 chrono::milliseconds time_chunk((params::timeout_value * 1000) / 100);
838 chrono::steady_clock::time_point start_time = startTime;
839 chrono::milliseconds chunk_time;
840 /*
841 * If we're using a schedule, then it's already been read from the relevant
842 * file, so we can start it now.
843 */
844 size_t schedule_step_number = 1;
845 pair<bool, unsigned int> next_schedule = schedule.set_next_schedule();
846 if (params::verbosity > 0)
847 cout << endl;
848 /*
849 * Try each step of the schedule in turn.
850 */
851 while (next_schedule.first) {
852 show(1, string("Schedule step "));
853 show(1, std::to_string(schedule_step_number));
854 show(1, string(": "));
855 show(1, schedule.step_to_string(schedule_step_number - 1), true);
856 if (params::verbosity > 2) {
858 }
859 /*
860 * Set the appropriate prover to use.
861 */
862 if (params::all_definitional) {
863 prover = &prover_all_definitional;
864 }
865 else if (params::no_definitional) {
866 prover = &prover_no_definitional;
867 }
868 else {
869 prover = &prover_standard;
870 }
871 /*
872 * Set the timeouts.
873 */
874 if (next_schedule.second == 0) {
875 prover->set_timeout(startTime + chrono::seconds(params::timeout_value));
876 }
877 else if (schedule_step_number == 1) {
878 prover->set_timeout(startTime + (next_schedule.second * time_chunk));
879 }
880 else {
881 prover->set_timeout(start_time + (next_schedule.second * time_chunk));
882 }
883
884 prover_outcome = prover->prove();
885
886 if (prover_outcome == ProverOutcome::Valid ||
887 prover_outcome == ProverOutcome::Error ||
888 prover_outcome == ProverOutcome::False) {
889 final_outcome = prover_outcome;
890 break;
891 }
892
893 chunk_time = chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now() - start_time);
894
895 show.nl(1, 2);
896 show(1, string("Timeout after "));
897 show(1, std::to_string(chunk_time.count()));
898 show(1, string(" milliseconds"), true);
899
900 start_time = chrono::steady_clock::now();
901 schedule_step_number++;
902 next_schedule = schedule.set_next_schedule();
903 }
904 }
905 //------------------------------------------------------------------------
906 //------------------------------------------------------------------------
907 //------------------------------------------------------------------------
908 // We're done, so tidy up.
909 //------------------------------------------------------------------------
910 //------------------------------------------------------------------------
911 //------------------------------------------------------------------------
912 /*
913 * Stop the timer.
914 */
915 chrono::milliseconds runTime =
916 chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now() - startTime);
917 show(1, string("Total run time: "));
918 show(1, std::to_string(runTime.count()));
919 show(1, string(" milliseconds."), true);
920
921 if (params::verbosity > 0) {
922 if (params::show_full_stats) {
923 prover->show_full_statistics(runTime.count());
924 }
925 else {
926 prover->show_statistics();
927 }
928 }
929 /*
930 * Display the outcome.
931 *
932 * Note that there are a bunch of special cases here, depending on
933 * some possible outcomes of the original clause simplification:
934 *
935 * - You have fof (+cnf, +maybe equality) axioms, and the conjecture can be $false, or empty,
936 * or something.
937 *
938 * - You have no axioms (after simplification) and there's a conjecture
939 * which is not $true or $false but may be empty.
940 */
941 string status(prover->get_status());
942 string outcome;
943
944 bool cnf = prover->problem_is_cnf_only();
945 bool conj_true = prover->problem_has_true_conjecture();
946 bool conj_false = prover->problem_has_false_conjecture();
947 bool conj_missing = prover->problem_has_missing_conjecture();
948 bool neg_conj_removed = prover->problem_has_negated_conjecture_removed();
949 bool fof_has_axioms = prover->problem_has_fof_axioms();
950 bool simplified_has_axioms = prover->simplified_problem_has_fof_axioms();
951
952 cout << "% SZS status ";
953 switch (final_outcome) {
954 case ProverOutcome::Valid:
955 // It's a proved CNF-only problem.
956 if (cnf)
957 outcome = "Unsatisfiable";
958 // It's a proved FOF problem.
959 else {
960 // The problem has axioms.
961 if (simplified_has_axioms) {
962 // Axioms and $true conjecture.
963 if (conj_true) {
964 outcome = "ContradictoryAxioms";
965 }
966 // Axioms and $false conjecture.
967 else if (conj_false || neg_conj_removed) {
968 outcome = "ContradictoryAxioms";
969 }
970 // There was no conjecture. In this case we treat the
971 // axioms as a conjecture, according to the SZS specs.
972 else if (conj_missing) {
973 outcome = "Unsatisfiable";
974 }
975 // Axioms and negated conjecture.
976 else {
977 outcome = "Theorem";
978 }
979 }
980 // The problem doesn't have axioms. Uuurgh... we have to distinguish
981 // between problems that had none in the first place, and problems
982 // where they were simplified out.
983 //
984 // Luckily they turn out to be the same.
985 else {
986 // There's no conjecture because it was essentially $false.
987 if (neg_conj_removed) {
988 outcome = "Error";
989 }
990 // No axioms remaining, but there is a negated conjecture.
991 else
992 outcome = "Theorem";
993 }
994 }
995 break;
996 case ProverOutcome::False:
997 // It's a refuted CNF-only problem.
998 if (cnf)
999 outcome = "Satisfiable";
1000 // It's a refuted FOF problem.
1001 else {
1002 if (simplified_has_axioms) {
1003 if (conj_true) {
1004 outcome = "Error";
1005 }
1006 else if (conj_false || neg_conj_removed) {
1007 outcome = "CounterSatisfiable";
1008 }
1009 else if (conj_missing) {
1010 outcome = "Satisfiable";
1011 }
1012 else {
1013 outcome = "CounterSatisfiable";
1014 }
1015 }
1016 else {
1017 // There's no conjecture, and the axioms are $true.
1018 if (conj_missing)
1019 outcome = "Theorem";
1020 // There's no conjecture because it was essentially $false.
1021 else if (neg_conj_removed) {
1022 outcome = "Error";
1023 }
1024 // No axioms remaining, but there is a negated conjecture.
1025 else
1026 outcome = "CounterSatisfiable";
1027 }
1028 }
1029 break;
1030 case ProverOutcome::PathLenLimit:
1031 outcome = "GaveUp";
1032 break;
1033 case ProverOutcome::Error:
1034 outcome = "Error";
1035 break;
1036 case ProverOutcome::TimeOut:
1037 // This is a slightly odd case that can arise if, for example, a
1038 // depth limit is set that causes all options to run out without
1039 // actually reaching a timeout.
1040 if (chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now() - startTime).count() < (params::timeout_value * 1000))
1041 outcome = "GaveUp";
1042 else
1043 outcome = "Timeout";
1044 break;
1045 default:
1046 outcome = "Error";
1047 break;
1048 }
1049 cout << outcome << " for " << params::problem_name;
1050 /*
1051 * If needed, verify the proof.
1052 */
1053 bool verified = false;
1054 if (params::verify_proof && final_outcome == ProverOutcome::Valid) {
1055 std::tuple<VariableIndex*,
1056 FunctionIndex*,
1058 TermIndex*>
1059 inds = prover->get_indexes();
1060
1061 vector<pair<string, vector<size_t>>>
1062 internal_proof(prover->get_internal_proof());
1063
1064 ProofChecker checker(prover->get_matrix(),
1065 internal_proof,
1066 get<0>(inds), get<3>(inds));
1067
1068 bool verification_outcome;
1069 string outcome_string;
1070 if (params::verify_proof_verbose) {
1071 pair<bool, string> outcome = checker.check_proof_verbose();
1072 verification_outcome = outcome.first;
1073 outcome_string = outcome.second;
1074 }
1075 else {
1076 verification_outcome = checker.check_proof();
1077 }
1078
1079 if (verification_outcome) {
1080 verified = true;
1081 cout << " : Verified";
1082 }
1083 else {
1084 cout << " : FailedVerification";
1085 }
1086
1087 if (params::verify_proof_verbose) {
1088 cout << endl << "% SZS output start Proof for " << params::problem_name << endl << endl;
1089 cout << outcome_string << endl;
1090 cout << "% SZS output end Proof for " << params::problem_name << endl;
1091 }
1092 }
1093 cout << endl;
1094 /*
1095 * Leave a minimal file with some outcome information.
1096 */
1097 if (params::write_output_summary) {
1098 try {
1099 std::ofstream out_file(params::output_summary_path);
1100 out_file << "ProblemName: " << params::problem_name << endl;
1101 if (found_conjecture)
1102 out_file << "ConjectureFound" << endl;
1103 else
1104 out_file << "NoConjectureFound" << endl;
1105 if (status == "")
1106 status = "NoStatus";
1107 out_file << "ProblemStatus: " << status << endl;
1108 out_file << "ProverOutcome: " << outcome << endl;
1109 if (params::verify_proof && final_outcome == ProverOutcome::Valid) {
1110 if (verified)
1111 out_file << "Verified" << endl;
1112 else
1113 out_file << "FailedVerification" << endl;
1114 }
1115 else
1116 out_file << "NoVerify" << endl;
1117 out_file << "Time: " << runTime.count() << endl;
1118 out_file.close();
1119 }
1120 catch (std::exception& err) {
1121 cerr << "Error: can't write output summary." << endl;
1122 }
1123 }
1124 /*
1125 * If necessary, write a TPTP proof to stdout.
1126 */
1127 if (params::generate_tptp_proof && (final_outcome == ProverOutcome::Valid)) {
1128 cout << "% SZS output start Proof for " << params::problem_name << endl;
1129 cout << prover->get_tptp_conversion_string();
1130 prover->show_tptp_proof();
1131 cout << "% SZS output end Proof for " << params::problem_name << endl;
1132 }
1133 return EXIT_SUCCESS;
1134}
Mechanism for looking after functions.
Management of Predicate objects.
Prover using a pair of stacks to conduct the proof search.
void read_from_tptp_file(const string &, bool &, size_t &)
Obviously, reads a problem from a TPTP file.
void show_full_statistics(size_t) const
Display counts of number of extensions tried and so on, as well as numbers per second.
bool problem_has_true_conjecture() const
Find out whether the problem's conjecture is $true.
void show_tptp_proof()
Show a Prolog-formatted proof.
bool problem_is_cnf_only() const
Find out whether the problem is CNF only.
void set_problem_path(fs::path &p)
Set the path for the problem being solved.
void show_statistics() const
Display counts of number of extensions tried and so on.
void show_matrix()
Show a nicely formatted matrix.
bool problem_has_missing_conjecture() const
Find out whether the problem's conjecture is missing, in the sense that it didn't appear in the inp...
bool problem_has_negated_conjecture_removed() const
Find out whether the problem's negated conjecture was simplified out.
void set_timeout(chrono::steady_clock::time_point time)
Set a timeout.
bool simplified_problem_has_fof_axioms() const
Find out from the parser whether the problem had axioms after simplification.
string get_status() const
Straightforward get method.
bool problem_has_fof_axioms() const
Find out from the parser whether the problem had axioms before simplification.
std::tuple< VariableIndex *, FunctionIndex *, PredicateIndex *, TermIndex * > get_indexes()
Straightforward get method.
vector< pair< string, vector< size_t > > > get_internal_proof() const
Get an internal representation of the proof stack.
Matrix & get_matrix()
Get a reference to the matrix.
bool problem_has_false_conjecture() const
Find out whether the problem's conjecture is $false.
ProverOutcome prove()
Here is where the magic happens.
Look after terms, using hash consing to avoid storing copies of terms.
Definition TermIndex.hpp:54
Storage of named variables, and management of new, anonymous and unique variables.
Wrap up the parsing process and the operation of a schedule in a single class.
Definition Schedule.hpp:107
Hide all the global stuff in this namespace.
Definition Schedule.cpp:27
static void set_all_backtrack()
Self-explanatory.
static void set_all_start()
Self-explanatory.
static bool no_start_options()
Self-explanatory.
static void correct_missing_start_options()
Self-explanatory.
static void show_search_parameter_settings()
Give a detailed indication of what the parameters affecting the search are currently set to.