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