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