Connect++ 0.4.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." << endl;
289 }
290 VPrint show(params::verbosity);
291 if (vars_map.count("sub-output")) {
292 params::output_terms_with_substitution = true;
293 }
294 if (vars_map.count("indent")) {
295 int i = vars_map["indent"].as<int>();
296 if (i >= 0 && i <= UINT8_MAX)
297 params::indent_size = i;
298 else
299 cerr << "Indent size is negative or too large. Using default." << endl;
300 }
301 if (vars_map.count("out-int")) {
302 int i = vars_map["out-int"].as<int>();
303 if (i > 0 && i <= UINT32_MAX)
304 params::output_frequency = i;
305 else
306 cerr << "Interval is negative or too large. Using default." << endl;
307 }
308 if (vars_map.count("no-equality")) {
309 params::add_equality_axioms = false;
310 }
311 if (vars_map.count("equality-last")) {
312 params::equality_axioms_at_start = false;
313 }
314 if (vars_map.count("all-distinct-objects")) {
315 params::all_distinct_objects = true;
316 }
317 if (vars_map.count("no-distinct-objects")) {
318 if (params::all_distinct_objects) {
319 cerr << "Cancelled --all-distinct-objects." << endl;
320 params::all_distinct_objects = false;
321 }
322 params::no_distinct_objects = true;
323 }
324 if (vars_map.count("timeout")) {
325 params::timeout = true;
326 int t = vars_map["timeout"].as<int>();
327 if (t > 0) {
328 if (t >= 10)
329 params::timeout_value = t;
330 else {
331 cerr << "Timeout is too small. Using 10 seconds." << endl;
332 params::timeout_value = 10;
333 }
334 }
335 else
336 cerr << "Timeout should be positive. Using default." << endl;
337 }
338 if (vars_map.count("show-default-schedule")) {
339 params::set_default_schedule();
340 cout << params::default_schedule << endl;
341 exit(EXIT_SUCCESS);
342 }
343 if (vars_map.count("path")) {
344 params::connectpp_path = vars_map["path"].as<string>();
345 }
346 // You should now have all the paths you need, so
347 // finalize them where possible.
348 params::LaTeX_proof_path
349 = params::pwd_path / params::LaTeX_proof_path;
350 params::Prolog_matrix_path
351 = params::connectpp_path / params::Prolog_matrix_path;
352 params::Prolog_proof_path
353 = params::connectpp_path / params::Prolog_proof_path;
354 params::output_summary_path
355 = params::pwd_path / params::output_summary_path;
356
357 if (vars_map.count("output")) {
358 params::write_output_summary = true;
359 string op = vars_map["output"].as<string>();
360 if (op != "default")
361 params::output_summary_path = op;
362 }
363 if (vars_map.count("all-definitional")) {
364 params::all_definitional = true;
365 }
366 if (vars_map.count("no-definitional")) {
367 params::no_definitional = true;
368 if (params::all_definitional) {
369 cerr << "Setting --no-definitional has cancelled --all-definitional." << endl;
370 params::all_definitional = false;
371 }
372 }
374 if (vars_map.count("schedule")) {
375 params::use_schedule = true;
376 if (!params::timeout) {
377 params::timeout = true;
378 params::timeout_value = 600;
379 }
380 params::schedule_path = vars_map["schedule"].as<string>();
381 try {
382 schedule.read_schedule_from_file(params::schedule_path);
383 }
384 catch (std::exception& err) {
385 cout << "% SZS status Error for "
386 << params::problem_name << " : " << err.what() << endl;
387 return EXIT_FAILURE;
388 }
389 }
390 /*
391 * Note: overides $TPTP environment variable, which is read
392 * earlier.
393 */
394 if (vars_map.count("tptp")) {
395 params::tptp_path = vars_map["tptp"].as<string>();
396 }
397 if (vars_map.count("reorder")) {
398 int n = vars_map["reorder"].as<int>();
399 if (n > 0) {
400 params::deterministic_reorder = true;
401 params::number_of_reorders = n;
402 }
403 else
404 cerr << "Number of re-orders should be positive. Skipping." << endl;
405 }
406 if (vars_map.count("random-seed")) {
407 int seed = vars_map["random-seed"].as<int>();
408 if (seed >= 0) {
409 params::random_seed = static_cast<unsigned>(seed);
410 params::boost_random_seed = static_cast<uint32_t>(seed);
411 }
412 else{
413 cerr << "Error: random seed must be a non-negative integer. Using default of 0." << endl;
414 }
415 }
416 if (vars_map.count("random-reorder")) {
417 params::random_reorder = true;
418 }
419 if (vars_map.count("random-reorder-literals")) {
420 params::random_reorder_literals = true;
421 }
422 if (vars_map.count("no-miniscope")) {
423 params::miniscope = false;
424 }
425 if (vars_map.count("show-clauses")) {
426 params::show_clauses = true;
427 }
428 if (vars_map.count("no-colour")) {
429 params::use_colours = false;
430 }
431 /*
432 * Set up the path for the input file. A path to the TPTP may
433 * have been set by now either by picking up the $TPTP environment
434 * variable or using a command-line option.
435 *
436 * This looks really over-engineered, but the complexity that
437 * follows in dealing with TPTP includes probably makes that
438 * a good idea.
439 *
440 * The desired behaviour here is:
441 *
442 * - If it's an absolute path then we're done.
443 *
444 * - If it's a relative path then see if it exists.
445 *
446 * - If it doesn't exist then look for it relative
447 * $TPTP.
448 */
449 fs::path initial_path;
450 bool found_file = false;
451 if (vars_map.count("input")) {
452 // Get what was on the command line.
453 initial_path = vars_map["input"].as<string>();
454
455 // Is it relative?
456 if (initial_path.is_relative()) {
457 // If it's relative, does it exist?
458 if (fs::exists(initial_path)) {
459 // It's an existing, relative path, so expand it. Belt
460 // and braces - make sure you catch things...
461 try {
462 initial_path = fs::canonical(initial_path);
463 }
464 catch (std::filesystem::filesystem_error& err) {
465 cout << "% SZS status Error for "
466 << params::problem_name << " : "
467 << err.what() << endl;
468 return EXIT_FAILURE;
469 }
470 found_file = true;
471 }
472 // It's relative, but doesn't exist...
473 else {
474 // So see if it's relative to $TPTP.
475 fs::path tptp_path(params::tptp_path);
476 initial_path = tptp_path / initial_path;
477 // Does it exist now?
478 if (fs::exists(initial_path)) {
479 // Yes! Expand it. Belt and braces again...
480 try {
481 initial_path = fs::canonical(initial_path);
482 }
483 catch (std::filesystem::filesystem_error& err) {
484 cout << "% SZS status Error for "
485 << params::problem_name << " : "
486 << err.what() << endl;
487 return EXIT_FAILURE;
488 }
489 found_file = true;
490 }
491 }
492 }
493 else {
494 // It's an absolute path.
495 if (initial_path.is_absolute() && fs::exists(initial_path)) {
496 // Found it! Tidy it up. (Yes, belt and braces etc...)
497 try {
498 initial_path = fs::canonical(initial_path);
499 }
500 catch (std::filesystem::filesystem_error& err) {
501 cout << "% SZS status Error for "
502 << params::problem_name << " : "
503 << err.what() << endl;
504 return EXIT_FAILURE;
505 }
506 found_file = true;
507 }
508 }
509 }
510 else {
511 cout << "% SZS status Error for "
512 << params::problem_name << " : no problem specified." << endl;
513 return EXIT_FAILURE;
514 }
515 // Have we found an existibng file?
516 if (!found_file) {
517 cout << "% SZS status Error for "
518 << params::problem_name << " : the specified file does not exist." << endl;
519 return EXIT_FAILURE;
520 }
521 // At this point we have a nice, tidy absolute path.
522 params::full_problem_path = initial_path;
523 params::full_problem_path.remove_filename();
524 params::problem_name = initial_path.stem().string();
525 fs::path path = initial_path;
526
527 /*
528 * Finished sorting out the file/path. Back to the other
529 * options.
530 */
531 if (vars_map.count("depth-limit")) {
532 int l = vars_map["depth-limit"].as<int>();
533 if (l > 1)
534 params::depth_limit = l;
535 else
536 cerr << "Depth limit should be positive. Using default." << endl;
537 }
538 if (vars_map.count("start-depth")) {
539 int l = vars_map["start-depth"].as<int>();
540 if (l > 1 && l <= params::depth_limit)
541 params::start_depth = l;
542 else
543 cerr << "Start depth should be positive and bounded by the maximum depth. Using default." << endl;
544 }
545 if (vars_map.count("depth-increment")) {
546 int l = vars_map["depth-increment"].as<int>();
547 if (l > 0)
548 params::depth_increment = l;
549 else
550 cerr << "Depth increment should be positive. Using default." << endl;
551 }
552 if (vars_map.count("depth-limit-all")) {
553 params::depth_limit_all = true;
554 }
555 if (vars_map.count("limit-by-tree-depth")) {
556 params::limit_by_tree_depth = true;
557 }
570 if (vars_map.count("restrict-start")) {
571 params::restrict_start = true;
572 }
573 if (vars_map.count("pos-neg-start")) {
574 params::all_pos_neg_start = true;
575 }
576 if (vars_map.count("conjecture-start")) {
577 params::conjecture_start = true;
578 }
579 if (vars_map.count("all-start")) {
580 show(2, string("--all-start set. This cancels all other start options."), true);
582 }
583 /*
584 * Careful! It is possible that no start options were set. If that's
585 * the case just set a sensible one.
586 */
589 }
590 if (vars_map.count("no-regularity")) {
591 params::use_regularity_test = false;
592 }
593 if (vars_map.count("lemmata-backtrack")) {
594 params::limit_bt_lemmas = false;
595 }
596 if (vars_map.count("reduction-backtrack")) {
597 params::limit_bt_reductions = false;
598 }
599 if (vars_map.count("extension-backtrack")) {
600 params::limit_bt_extensions = false;
601 }
602 if (vars_map.count("explore-left-trees")) {
603 params::limit_bt_extensions_left_tree = false;
604 }
605 if (vars_map.count("all-backtrack")) {
606 show(2, string("--all-backtrack set. This prevails over all other backtracking options."), true);
608 }
609 if (vars_map.count("all-reductions")) {
610 params::limit_reductions = false;
611 }
612 if (vars_map.count("all-extensions")) {
613 params::limit_extensions = false;
614 }
615 if (vars_map.count("all-lemmata")) {
616 params::limit_lemmata = false;
617 }
618 if (vars_map.count("no-lemmata")) {
619 params::use_lemmata = false;
620 }
621 if (vars_map.count("complete")) {
622 int d = vars_map["complete"].as<int>();
623 if (d >= params::start_depth)
624 params::switch_to_complete = d;
625 else {
626 cerr << "Switch to complete search must happen after start depth. Using start depth." << endl;
627 params::switch_to_complete = params::start_depth;
628 }
629 }
630 if (vars_map.count("latex")) {
631 params::generate_LaTeX_proof = true;
632 params::build_proof = true;
633 string lp = vars_map["latex"].as<string>();
634 if (lp != "default")
635 params::LaTeX_proof_path = lp;
636 }
637 if (vars_map.count("sub-latex")) {
638 params::sub_LaTeX_proof = true;
639 }
640 if (vars_map.count("tiny-latex")) {
641 params::latex_tiny_proof = true;
642 }
643 if (vars_map.count("latex-no-matrix")) {
644 params::latex_include_matrix = false;
645 }
646 if (vars_map.count("tptp-proof")) {
647 params::generate_tptp_proof = true;
648 params::build_proof = true;
649 }
650 if (vars_map.count("prolog-proof")) {
651 params::generate_Prolog_proof = true;
652 params::build_proof = true;
653 }
654 if (vars_map.count("verify")) {
655 params::verify_proof = true;
656 params::build_proof = true;
657 }
658 if (vars_map.count("full-verify")) {
659 params::verify_proof = true;
660 params::verify_proof_verbose = true;
661 params::build_proof = true;
662 }
663 //------------------------------------------------------------------------
664 //------------------------------------------------------------------------
665 //------------------------------------------------------------------------
666 /*
667 * Now we're ready to start the party.
668 */
669 //------------------------------------------------------------------------
670 //------------------------------------------------------------------------
671 //------------------------------------------------------------------------
672 /*
673 * First, state the time we're starting at.
674 */
675 show(1, string("Connect++ V"));
676 show(1, string(VERSION));
677 show(1, string(" started at: "));
678 time_t date_and_time;
679 time(&date_and_time);
680 show(1, asctime(localtime(&date_and_time)));
692 StackProver prover_standard;
693 StackProver prover_all_definitional;
694 StackProver prover_no_definitional;
695 StackProver* prover = &prover_standard;
702 if (params::use_schedule) {
703 params::no_definitional = false;
704 params::all_definitional = false;
705 }
706 show(1, string("Reading from: "));
707 show(1, path.c_str(), true);
708 bool found_conjecture = false;
709 size_t fof_size = 0;
710 try {
711 prover_standard.read_from_tptp_file(path.c_str(), found_conjecture, fof_size);
712 }
713 catch (std::exception& err) {
714 cout << "% SZS status Error for "
715 << params::problem_name << " : "
716 << err.what() << endl;
717 return EXIT_FAILURE;
718 }
723 if (params::use_schedule) {
724 params::all_definitional = true;
725 try {
726 prover_all_definitional.read_from_tptp_file(path.c_str(), found_conjecture, fof_size);
727 }
728 catch (std::exception& err) {
729 cout << "% SZS status Error for "
730 << params::problem_name << " : "
731 << err.what() << endl;
732 return EXIT_FAILURE;
733 }
734 params::all_definitional = false;
735 params::no_definitional = true;
736 try {
737 prover_no_definitional.read_from_tptp_file(path.c_str(), found_conjecture, fof_size);
738 }
739 catch (std::exception& err) {
740 cout << "% SZS status Error for "
741 << params::problem_name << " : "
742 << err.what() << endl;
743 return EXIT_FAILURE;
744 }
745 }
749 chrono::milliseconds parse_time =
750 chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now() - startTime);
751 show(1, string("Total parse and conversion time: "));
752 show(1, std::to_string(parse_time.count()));
753 show(1, string(" milliseconds."), true);
754
755 prover_standard.set_problem_path(path);
756 prover_all_definitional.set_problem_path(path);
757 prover_no_definitional.set_problem_path(path);
758
759 show(2, string("Starting proof attempt for: "), false);
760 show(2, params::problem_name, true);
764 ProverOutcome prover_outcome;
765 ProverOutcome final_outcome;
766 final_outcome = ProverOutcome::TimeOut;
767 //------------------------------------------------------------------------
768 //------------------------------------------------------------------------
769 //------------------------------------------------------------------------
770 /*
771 * No schedule.
772 */
773 //------------------------------------------------------------------------
774 //------------------------------------------------------------------------
775 //------------------------------------------------------------------------
776 if (!params::use_schedule) {
784 if (params::timeout) {
785 chrono::seconds timeout_duration(params::timeout_value);
786 prover_standard.set_timeout(startTime + timeout_duration);
787 }
788 /*
789 * Run the prover.
790 */
791 prover_outcome = prover_standard.prove();
792 final_outcome = prover_outcome;
793 }
794 //------------------------------------------------------------------------
795 //------------------------------------------------------------------------
796 //------------------------------------------------------------------------
797 /*
798 * A schedule is being used.
799 */
800 //------------------------------------------------------------------------
801 //------------------------------------------------------------------------
802 //------------------------------------------------------------------------
803 else {
807 if (!params::timeout) {
808 params::timeout = true;
809 params::timeout_value = 600;
810 }
811 /*
812 * Convert to milliseconds and get a standard chunk of time.
813 */
814 chrono::milliseconds time_chunk((params::timeout_value * 1000) / 100);
815 chrono::steady_clock::time_point start_time = startTime;
816 chrono::milliseconds chunk_time;
817 /*
818 * If we're using a schedule, then it's already been read from the relevant
819 * file, so we can start it now.
820 */
821 size_t schedule_step_number = 1;
822 pair<bool, unsigned int> next_schedule =
823 schedule.set_next_schedule();
824 if (params::verbosity > 0)
825 cout << endl;
826 /*
827 * Try each step of the schedule in turn.
828 */
829 while (next_schedule.first) {
830 show(1, string("Schedule step "));
831 show(1, std::to_string(schedule_step_number));
832 show(1, string(": "));
833 show(1, schedule.step_to_string(schedule_step_number - 1), true);
837 if (params::all_definitional) {
838 prover = &prover_all_definitional;
839 }
840 else if (params::no_definitional) {
841 prover = &prover_no_definitional;
842 }
843 else {
844 prover = &prover_standard;
845 }
849 if (next_schedule.second == 0) {
850 prover->set_timeout(startTime + chrono::seconds(params::timeout_value));
851 }
852 else if (schedule_step_number == 1) {
853 prover->set_timeout(startTime + (next_schedule.second * time_chunk));
854 }
855 else {
856 prover->set_timeout(start_time + (next_schedule.second * time_chunk));
857 }
858
859 prover_outcome = prover->prove();
860
861 if (prover_outcome == ProverOutcome::Valid ||
862 prover_outcome == ProverOutcome::Error ||
863 prover_outcome == ProverOutcome::False) {
864 final_outcome = prover_outcome;
865 break;
866 }
867
868 chunk_time = chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now() - start_time);
869
870 show.nl(1, 2);
871 show(1, string("Timeout after "));
872 show(1, std::to_string(chunk_time.count()));
873 show(1, string(" milliseconds"), true);
874
875 start_time = chrono::steady_clock::now();
876 schedule_step_number++;
877 next_schedule = schedule.set_next_schedule();
878 }
879 }
880 //------------------------------------------------------------------------
881 //------------------------------------------------------------------------
882 //------------------------------------------------------------------------
883 // We're done, so tidy up.
884 //------------------------------------------------------------------------
885 //------------------------------------------------------------------------
886 //------------------------------------------------------------------------
887 if (params::verbosity > 0) {
888 cout << endl;
889 prover->show_statistics();
890 }
894 chrono::milliseconds runTime =
895 chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now() - startTime);
896 show(1, string("Total run time: "));
897 show(1, std::to_string(runTime.count()));
898 show(1, string(" milliseconds."), true);
899 /*
900 * Display the outcome.
901 *
902 * Note that there are a bunch of special cases here, depending on
903 * some possible outcomes of the original clause simplification:
904 *
905 * - You have fof (+cnf, +maybe equality) axioms, and the conjecture can be $false, or empty,
906 * or something.
907 *
908 * - You have no axioms (after simplification) and there's a conjecture
909 * which is not $true or $false but may be empty.
910 */
911 string status(prover->get_status());
912 string outcome;
913
914 bool cnf = prover->problem_is_cnf_only();
915 bool conj_true = prover->problem_has_true_conjecture();
916 bool conj_false = prover->problem_has_false_conjecture();
917 bool conj_missing = prover->problem_has_missing_conjecture();
918 bool neg_conj_removed = prover->problem_has_negated_conjecture_removed();
919 bool fof_has_axioms = prover->problem_has_fof_axioms();
920 bool simplified_has_axioms = prover->simplified_problem_has_fof_axioms();
921
922 cout << "% SZS status ";
923 switch (final_outcome) {
924 case ProverOutcome::Valid:
925 // It's a proved CNF-only problem.
926 if (cnf)
927 outcome = "Unsatisfiable";
928 // It's a proved FOF problem.
929 else {
930 // The problem has axioms.
931 if (simplified_has_axioms) {
932 // Axioms and $true conjecture.
933 if (conj_true) {
934 outcome = "ContradictoryAxioms";
935 }
936 // Axioms and $false conjecture.
937 else if (conj_false || neg_conj_removed) {
938 outcome = "ContradictoryAxioms";
939 }
940 // There was no conjecture. In this case we treat the
941 // axioms as a conjecture, according to the SZS specs.
942 else if (conj_missing) {
943 outcome = "Unsatisfiable";
944 }
945 // Axioms and negated conjecture.
946 else {
947 outcome = "Theorem";
948 }
949 }
950 // The problem doesn't have axioms. Uuurgh... we have to distinguish
951 // between problems that had none in the first place, and problems
952 // where they were simplified out.
953 //
954 // Luckily they turn out to be the same.
955 else {
956 // There's no conjecture because it was essentially $false.
957 if (neg_conj_removed) {
958 outcome = "Error";
959 }
960 // No axioms remaining, but there is a negated conjecture.
961 else
962 outcome = "Theorem";
963 }
964 }
965 break;
966 case ProverOutcome::False:
967 // It's a refuted CNF-only problem.
968 if (cnf)
969 outcome = "Satisfiable";
970 // It's a refuted FOF problem.
971 else {
972 if (simplified_has_axioms) {
973 if (conj_true) {
974 outcome = "Error";
975 }
976 else if (conj_false || neg_conj_removed) {
977 outcome = "CounterSatisfiable";
978 }
979 else if (conj_missing) {
980 outcome = "Satisfiable";
981 }
982 else {
983 outcome = "CounterSatisfiable";
984 }
985 }
986 else {
987 // There's no conjecture, and the axioms are $true.
988 if (conj_missing)
989 outcome = "Theorem";
990 // There's no conjecture because it was essentially $false.
991 else if (neg_conj_removed) {
992 outcome = "Error";
993 }
994 // No axioms remaining, but there is a negated conjecture.
995 else
996 outcome = "CounterSatisfiable";
997 }
998 }
999 break;
1000 case ProverOutcome::PathLenLimit:
1001 outcome = "GaveUp";
1002 break;
1003 case ProverOutcome::Error:
1004 outcome = "Error";
1005 break;
1006 case ProverOutcome::TimeOut:
1007 // This is a slightly odd case that can arise if, for example, a
1008 // depth limit is set that causes all options to run out without
1009 // actually reaching a timeout.
1010 if (chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now() - startTime).count() < (params::timeout_value * 1000))
1011 outcome = "GaveUp";
1012 else
1013 outcome = "Timeout";
1014 break;
1015 default:
1016 outcome = "Error";
1017 break;
1018 }
1019 cout << outcome << " for " << params::problem_name;
1020 /*
1021 * If needed, verify the proof.
1022 */
1023 bool verified = false;
1024 if (params::verify_proof && final_outcome == ProverOutcome::Valid) {
1025 std::tuple<VariableIndex*,
1026 FunctionIndex*,
1028 TermIndex*>
1029 inds = prover->get_indexes();
1030
1031 vector<pair<string, vector<size_t>>>
1032 internal_proof(prover->get_internal_proof());
1033
1034 ProofChecker checker(prover->get_matrix(),
1035 internal_proof,
1036 get<0>(inds), get<3>(inds));
1037
1038 bool verification_outcome;
1039 string outcome_string;
1040 if (params::verify_proof_verbose) {
1041 pair<bool, string> outcome = checker.check_proof_verbose();
1042 verification_outcome = outcome.first;
1043 outcome_string = outcome.second;
1044 }
1045 else {
1046 verification_outcome = checker.check_proof();
1047 }
1048
1049 if (verification_outcome) {
1050 verified = true;
1051 cout << " : Verified";
1052 }
1053 else {
1054 cout << " : FailedVerification";
1055 }
1056
1057 if (params::verify_proof_verbose) {
1058 cout << endl << "% SZS output start Proof for " << params::problem_name << endl << endl;
1059 cout << outcome_string << endl;
1060 cout << "% SZS output end Proof for " << params::problem_name << endl;
1061 }
1062 }
1063 cout << endl;
1064 /*
1065 * Leave a minimal file with some outcome information.
1066 */
1067 if (params::write_output_summary) {
1068 try {
1069 std::ofstream out_file(params::output_summary_path);
1070 out_file << "ProblemName: " << params::problem_name << endl;
1071 if (found_conjecture)
1072 out_file << "ConjectureFound" << endl;
1073 else
1074 out_file << "NoConjectureFound" << endl;
1075 if (status == "")
1076 status = "NoStatus";
1077 out_file << "ProblemStatus: " << status << endl;
1078 out_file << "ProverOutcome: " << outcome << endl;
1079 if (params::verify_proof && final_outcome == ProverOutcome::Valid) {
1080 if (verified)
1081 out_file << "Verified" << endl;
1082 else
1083 out_file << "FailedVerification" << endl;
1084 }
1085 else
1086 out_file << "NoVerify" << endl;
1087 out_file << "Time: " << runTime.count() << endl;
1088 out_file.close();
1089 }
1090 catch (std::exception& err) {
1091 cerr << "Error: can't write output summary." << endl;
1092 }
1093 }
1094 /*
1095 * If necessary, write a TPTP proof to stdout.
1096 */
1097 if (params::generate_tptp_proof && (final_outcome == ProverOutcome::Valid)) {
1098 cout << "% SZS output start Proof for " << params::problem_name << endl;
1099 cout << prover->get_tptp_conversion_string();
1100 prover->show_tptp_proof();
1101 cout << "% SZS output end Proof for " << params::problem_name << endl;
1102 }
1103 return EXIT_SUCCESS;
1104}
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 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.