Connect++ 0.7.0
A fast, readable connection prover for first-order logic.
Loading...
Searching...
No Matches
connect++.cpp
1/*
2
3Copyright © 2023-26 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 built-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. (Currently not enabled.)")
198 ("all-extensions",
199 "Compute extensions for all literals in the clause, not just the first one. (Currently not enabled.)")
200 ("all-lemmas",
201 "When using lemmas, consider all literals in the clause, not just the first one. (Currently not enabled.)")
202 ("no-lemmas",
203 "Do not use lemmas.");
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 tptp_proof_output("TPTP proof output");
230 tptp_proof_output.add_options()
231 ("tptp-proof", po::value<string>()->implicit_value(string("default")),
232 "Generate a proof compatible with the TPTP tableau format. See tptp.org/Proposals/TableauxFormatProofs/index.shtml. Output is to stdout by default. Specify a filename if you want it in a file instead.")
233 ("tptp-proof-no-subs",
234 "Don't apply substitutions in the tableau when displaying clauses or literals.")
235 ("tptp-proof-show-paths",
236 "Show path(...) with full paths rather than parent(...).")
237 ("tptp-proof-show-all",
238 "By default only material actually used in the proof is shown. Setting this will give you everything.")
239 ("tptp-proof-show-subs",
240 "Add bind(...) annotations to the output.");
241
242 po::options_description sc_tptp_proof_output("SC-TPTP proof output");
243 sc_tptp_proof_output.add_options()
244 ("sc-tptp-proof",
245 "Generate a proof in the SC-TPTP format. See github.com/SC-TPTP/sc-tptp. Output is to stdout. (Not currently implemented.)");
246
247 po::options_description proof_verification("Proof verification");
248 proof_verification.add_options()
249 ("prolog-proof",
250 "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.")
251 ("full-verify",
252 "Verify any proof found using the internal proof checker. Use this version to get a detailed description of the verification sent to stdout.")
253 ("verify",
254 "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.");
255
256 po::options_description latex_options("LaTeX output");
257 latex_options.add_options()
258 ("latex", po::value<string>()->implicit_value(string("default")),
259 "Make a LaTeX file containing a typeset proof in the (clausal) connection calculus format. Default value: \"./latex_proof.tex\".")
260 ("latex-tableau", po::value<string>()->implicit_value(string("default")),
261 "Make a LaTeX file containing a typeset proof in the tableau format. Default value \"./latex_tableau_proof.tex\".")
262 ("latex-tableau-subs",
263 "Show lists of substitutions in the tableau nodes.")
264 ("sub-latex",
265 "Include substitutions in the LaTeX proof. That is, apply them in clauses and so on.")
266 ("latex-height", po::value<string>(),
267 "Page height for LaTeX output. Specify as a LaTex-compatible string. Default \"500mm\".")
268 ("latex-width", po::value<string>(),
269 "Page width for LaTeX output. Specify as a LaTex-compatible string. Default \"500mm\".")
270 ("latex-body-only",
271 "Only generate LaTeX for the diagram. Don't include \\documentclass... and so on.")
272 ("tiny-latex",
273 "Typeset the latex proof in a tiny font.")
274 ("latex-no-matrix",
275 "Don't include the matrix in the LaTeX proof.");
276
277 po::options_description graphviz_options("Graphviz output");
278 graphviz_options.add_options()
279 ("graphviz-tableau", po::value<string>()->implicit_value(string("default")),
280 "Produce a .dot file that can be displayed using Graphviz. Default value \"./graphviz_tableau_proof.dot\".")
281 ("graphviz-tableau-subs",
282 "Show lists of substitutions in the tableau nodes.")
283 ("sub-graphviz",
284 "Include substitutions in the proof. That is, apply them in clauses and so on.")
285 ("graphviz-height", po::value<string>(),
286 "Page height for Graphviz output. Specify in inches. Default 11.69 (A4 paper).")
287 ("graphviz-width", po::value<string>(),
288 "Page width for Graphviz output. Specify in inches. Default 8.27 (A4 paper).");
289
290
291 po::options_description all_options("Options");
292 all_options.add(main_options)
293 .add(conversion_options)
294 .add(output_options)
295 .add(search_options)
296 .add(backtrack_options)
297 .add(depth_options)
298 .add(tptp_proof_output)
299 .add(sc_tptp_proof_output)
300 .add(proof_verification)
301 .add(latex_options)
302 .add(graphviz_options);
303
304 po::positional_options_description pos_opts;
305 pos_opts.add("input", -1);
306 po::variables_map vars_map;
307 try {
308 po::store(po::command_line_parser(argc, argv).
309 options(all_options).
310 positional(pos_opts).
311 run(), vars_map);
312 }
313 /*
314 * If the command line options are in error then we exit here.
315 */
316 catch (boost::program_options::error& err) {
317 cout << "% SZS status Error for "
318 << params::problem_name << " : " << err.what() << endl;
319 return EXIT_FAILURE;
320 }
321 po::notify(vars_map);
322 /*
323 * We had a correct parse of the command line, so now we deal with
324 * the options.
325 */
326 if (vars_map.count("help")) {
327 cout << all_options << endl;
328 cout << "If you wish to report a bug, please supply:" << endl << endl;
329 cout << "1. A description of the claimed bug." << endl;
330 cout << "2. The input file used." << endl;
331 cout << "3. The exact command-line used, including all options." << endl;
332 cout << "4. The output from \"connect++ --version\"." << endl << endl;
333 cout << "The author can be contacted at: sbh11@cl.cam.ac.uk" << endl;
334 return EXIT_SUCCESS;
335 }
336 if (vars_map.count("version")) {
337 cout << "Connect++ Version V" << VERSION << "." << endl;
338 cout << "Copyright © 2021-26 Sean Holden. All rights reserved." << endl;
339 return EXIT_SUCCESS;
340 }
341 if (vars_map.count("verbosity")) {
342 int v = vars_map["verbosity"].as<int>();
343 if (v >= 0 && v <= 3)
344 params::verbosity = v;
345 else
346 cerr << "Verbosity should be between 0 and 3. Using default (2)." << endl;
347 }
348 /*
349 * VPrint automates conditional output according to the verbosity setting.
350 */
351 VPrint show(params::verbosity);
352 /*
353 * Back to processing parameters.
354 */
355 if (vars_map.count("sub-output")) {
356 params::output_terms_with_substitution = true;
357 }
358 if (vars_map.count("indent")) {
359 int i = vars_map["indent"].as<int>();
360 if (i >= 0 && i <= UINT8_MAX)
361 params::indent_size = i;
362 else
363 cerr << "Indent size is negative or too large. Using default." << endl;
364 }
365 if (vars_map.count("out-int")) {
366 int i = vars_map["out-int"].as<int>();
367 if (i > 0 && i <= UINT32_MAX)
368 params::output_frequency = i;
369 else
370 cerr << "Interval is negative or too large. Using default." << endl;
371 }
372 if (vars_map.count("no-equality")) {
373 params::add_equality_axioms = false;
374 }
375 if (vars_map.count("equality-last")) {
376 params::equality_axioms_at_start = false;
377 }
378 if (vars_map.count("all-distinct-objects")) {
379 params::all_distinct_objects = true;
380 }
381 if (vars_map.count("no-distinct-objects")) {
382 if (params::all_distinct_objects) {
383 cerr << "Cancelled --all-distinct-objects." << endl;
384 params::all_distinct_objects = false;
385 }
386 params::no_distinct_objects = true;
387 }
388 if (vars_map.count("timeout")) {
389 params::timeout = true;
390 int t = vars_map["timeout"].as<int>();
391 if (t > 0) {
392 if (t >= 10)
393 params::timeout_value = t;
394 else {
395 cerr << "Timeout is too small. Using 10 seconds." << endl;
396 params::timeout_value = 10;
397 }
398 }
399 else
400 cerr << "Timeout should be positive. Using default." << endl;
401
402 params::global_timeout += chrono::seconds(params::timeout_value);
403 }
404 if (vars_map.count("show-default-schedule")) {
405 params::set_default_schedule();
406 cout << params::default_schedule << endl;
407 exit(EXIT_SUCCESS);
408 }
409 if (vars_map.count("path")) {
410 params::connectpp_path = vars_map["path"].as<string>();
411 }
412 /*
413 * You should now have all the paths you need, so
414 * finalize them where possible.
415 */
416 params::LaTeX_proof_path
417 = params::pwd_path / params::LaTeX_proof_path;
418 params::LaTeX_tableau_proof_path
419 = params::pwd_path / params::LaTeX_tableau_proof_path;
420 params::Prolog_matrix_path
421 = params::connectpp_path / params::Prolog_matrix_path;
422 params::Prolog_proof_path
423 = params::connectpp_path / params::Prolog_proof_path;
424 params::output_summary_path
425 = params::pwd_path / params::output_summary_path;
426 /*
427 * Back to processing parameters.
428 */
429 if (vars_map.count("output")) {
430 params::write_output_summary = true;
431 string op = vars_map["output"].as<string>();
432 if (op != "default")
433 params::output_summary_path = op;
434 }
435 if (vars_map.count("all-definitional")) {
436 params::all_definitional = true;
437 }
438 if (vars_map.count("no-definitional")) {
439 params::no_definitional = true;
440 if (params::all_definitional) {
441 cerr << "Setting --no-definitional has cancelled --all-definitional." << endl;
442 params::all_definitional = false;
443 }
444 }
446 if (vars_map.count("schedule")) {
447 params::use_schedule = true;
448 if (!params::timeout) {
449 params::timeout = true;
450 params::timeout_value = 600;
451 }
452 params::schedule_path = vars_map["schedule"].as<string>();
453 try {
454 schedule.read_schedule_from_file(params::schedule_path);
455 }
456 catch (std::exception& err) {
457 cout << "% SZS status Error for "
458 << params::problem_name << " : " << err.what() << endl;
459 return EXIT_FAILURE;
460 }
461 }
462 /*
463 * Note: overides $TPTP environment variable, which is read
464 * earlier.
465 */
466 if (vars_map.count("tptp")) {
467 params::tptp_path = vars_map["tptp"].as<string>();
468 }
469 if (vars_map.count("reorder")) {
470 int n = vars_map["reorder"].as<int>();
471 if (n > 0) {
472 params::deterministic_reorder = true;
473 params::number_of_reorders = n;
474 }
475 else
476 cerr << "Number of re-orders should be positive. Skipping." << endl;
477 }
478 if (vars_map.count("random-seed")) {
479 int seed = vars_map["random-seed"].as<int>();
480 if (seed >= 0) {
481 params::random_seed = static_cast<unsigned>(seed);
482 params::boost_random_seed = static_cast<uint32_t>(seed);
483 }
484 else {
485 cerr << "Error: random seed must be a non-negative integer. Using default of 0." << endl;
486 }
487 }
488 if (vars_map.count("random-reorder")) {
489 params::random_reorder = true;
490 }
491 if (vars_map.count("random-reorder-literals")) {
492 params::random_reorder_literals = true;
493 }
494 if (vars_map.count("no-miniscope")) {
495 params::miniscope = false;
496 }
497 if (vars_map.count("show-clauses")) {
498 params::show_clauses = true;
499 }
500 if (vars_map.count("no-colour")) {
501 params::use_colours = false;
502 }
503 if (vars_map.count("poly-unification")) {
504 params::poly_unification = true;
505 }
506 /*
507 * Set up the path for the input file. A path to the TPTP may
508 * have been set by now either by picking up the $TPTP environment
509 * variable or using a command-line option.
510 *
511 * This looks really over-engineered, but the complexity that
512 * follows in dealing with TPTP "includes" probably makes that
513 * a good idea.
514 *
515 * The desired behaviour here is:
516 *
517 * - If it's an absolute path then we're done.
518 *
519 * - If it's a relative path then see if it exists.
520 *
521 * - If it doesn't exist then look for it relative to
522 * $TPTP.
523 */
524 fs::path initial_path;
525 bool found_file = false;
526 if (vars_map.count("input")) {
527 // Get what was on the command line.
528 initial_path = vars_map["input"].as<string>();
529
530 // Is it relative?
531 if (initial_path.is_relative()) {
532 // If it's relative, does it exist?
533 if (fs::exists(initial_path)) {
534 // It's an existing, relative path, so expand it. Belt
535 // and braces - make sure you catch things...
536 try {
537 initial_path = fs::canonical(initial_path);
538 }
539 catch (std::filesystem::filesystem_error& err) {
540 cout << "% SZS status Error for "
541 << params::problem_name << " : "
542 << err.what() << endl;
543 return EXIT_FAILURE;
544 }
545 found_file = true;
546 }
547 // It's relative, but doesn't exist...
548 else {
549 // So see if it's relative to $TPTP.
550 fs::path tptp_path(params::tptp_path);
551 initial_path = tptp_path / initial_path;
552 // Does it exist now?
553 if (fs::exists(initial_path)) {
554 // Yes! Expand it. Belt and braces again...
555 try {
556 initial_path = fs::canonical(initial_path);
557 }
558 catch (std::filesystem::filesystem_error& err) {
559 cout << "% SZS status Error for "
560 << params::problem_name << " : "
561 << err.what() << endl;
562 return EXIT_FAILURE;
563 }
564 found_file = true;
565 }
566 }
567 }
568 else {
569 // It's an absolute path.
570 if (initial_path.is_absolute() && fs::exists(initial_path)) {
571 // Found it! Tidy it up. (Yes, belt and braces etc...)
572 try {
573 initial_path = fs::canonical(initial_path);
574 }
575 catch (std::filesystem::filesystem_error& err) {
576 cout << "% SZS status Error for "
577 << params::problem_name << " : "
578 << err.what() << endl;
579 return EXIT_FAILURE;
580 }
581 found_file = true;
582 }
583 }
584 }
585 else {
586 cout << "% SZS status Error for "
587 << params::problem_name << " : no problem specified." << endl;
588 return EXIT_FAILURE;
589 }
590 // Have we found an existing file?
591 if (!found_file) {
592 cout << "% SZS status Error for "
593 << params::problem_name << " : the specified file does not exist." << endl;
594 return EXIT_FAILURE;
595 }
596 // At this point we have a nice, tidy absolute path.
597 params::full_problem_path = initial_path;
598 params::full_problem_path.remove_filename();
599 params::problem_name = initial_path.stem().string();
600 fs::path path = initial_path;
601 /*
602 * Finished sorting out the file/path. Back to the other
603 * options.
604 */
605 if (vars_map.count("depth-limit")) {
606 int l = vars_map["depth-limit"].as<int>();
607 if (l > 1)
608 params::depth_limit = l;
609 else
610 cerr << "Depth limit should be positive. Using default." << endl;
611 }
612 if (vars_map.count("start-depth")) {
613 int l = vars_map["start-depth"].as<int>();
614 if (l > 1 && l <= params::depth_limit)
615 params::start_depth = l;
616 else
617 cerr << "Start depth should be positive and bounded by the maximum depth. Using default." << endl;
618 }
619 if (vars_map.count("depth-increment")) {
620 int l = vars_map["depth-increment"].as<int>();
621 if (l > 0)
622 params::depth_increment = l;
623 else
624 cerr << "Depth increment should be positive. Using default." << endl;
625 }
638 if (vars_map.count("restrict-start")) {
639 params::restrict_start = true;
640 }
641 if (vars_map.count("pos-neg-start")) {
642 params::all_pos_neg_start = true;
643 }
644 if (vars_map.count("conjecture-start")) {
645 params::conjecture_start = true;
646 }
647 if (vars_map.count("all-start")) {
648 show(2, string("--all-start set. This cancels all other start options."), true);
650 }
651 /*
652 * Careful! It is possible that no start options were set. If that's
653 * the case just set a sensible one.
654 */
657 }
658 /*
659 * Back to processing parameters.
660 */
661 if (vars_map.count("no-regularity")) {
662 params::use_regularity_test = false;
663 }
664 if (vars_map.count("lemmata-backtrack")) {
665 params::limit_bt_lemmas = false;
666 }
667 if (vars_map.count("reduction-backtrack")) {
668 params::limit_bt_reductions = false;
669 }
670 if (vars_map.count("extension-backtrack")) {
671 params::limit_bt_extensions = false;
672 }
673 if (vars_map.count("explore-left-trees")) {
674 params::limit_bt_extensions_left_tree = false;
675 }
676 if (vars_map.count("all-backtrack")) {
677 show(2, string("--all-backtrack set. This prevails over all other backtracking options."), true);
679 }
680 if (vars_map.count("all-reductions")) {
681 params::limit_reductions = false;
682 }
683 if (vars_map.count("all-extensions")) {
684 params::limit_extensions = false;
685 }
686 if (vars_map.count("all-lemmas")) {
687 params::limit_lemmata = false;
688 }
689 if (vars_map.count("no-lemmas")) {
690 params::use_lemmata = false;
691 }
692 if (vars_map.count("complete")) {
693 int d = vars_map["complete"].as<int>();
694 if (d >= params::start_depth)
695 params::switch_to_complete = d;
696 else {
697 cerr << "Switch to complete search must happen after start depth. Using start depth." << endl;
698 params::switch_to_complete = params::start_depth;
699 }
700 }
701 if (vars_map.count("latex")) {
702 params::generate_LaTeX_proof = true;
703 params::build_proof = true;
704 string lp = vars_map["latex"].as<string>();
705 if (lp != "default")
706 params::LaTeX_proof_path = lp;
707 }
708 if (vars_map.count("latex-tableau")) {
709 params::generate_LaTeX_tableau_proof = true;
710 params::build_proof = true;
711 string lp = vars_map["latex-tableau"].as<string>();
712 if (lp != "default")
713 params::LaTeX_tableau_proof_path = lp;
714 }
715 if (vars_map.count("graphviz-tableau")) {
716 params::generate_graphviz_tableau_proof = true;
717 params::build_proof = true;
718 string lp = vars_map["graphviz-tableau"].as<string>();
719 if (lp != "default")
720 params::graphviz_tableau_proof_path = lp;
721 }
722 if (vars_map.count("latex-tableau-subs")) {
723 params::latex_tableau_subs = true;
724 }
725 if (vars_map.count("graphviz-tableau-subs")) {
726 params::graphviz_tableau_subs = true;
727 }
728 if (vars_map.count("sub-latex")) {
729 params::sub_LaTeX_proof = true;
730 }
731 if (vars_map.count("sub-graphviz")) {
732 params::sub_Graphviz_proof = true;
733 }
734 if (vars_map.count("latex-height")) {
735 string s = vars_map["latex-height"].as<string>();
736 params::latex_height = s;
737 }
738 if (vars_map.count("latex-width")) {
739 string s = vars_map["latex-width"].as<string>();
740 params::latex_width = s;
741 }
742 if (vars_map.count("graphviz-height")) {
743 float h = std::stof(vars_map["graphviz-height"].as<string>());
744 if (h > 0.0)
745 params::graphviz_height = h;
746 }
747 if (vars_map.count("graphviz-width")) {
748 float w = std::stof(vars_map["graphviz-width"].as<string>());
749 if (w > 0.0)
750 params::graphviz_width = w;
751 }
752 if (vars_map.count("latex-body-only")) {
753 params::latex_body_only = true;
754 }
755 if (vars_map.count("tiny-latex")) {
756 params::latex_tiny_proof = true;
757 }
758 if (vars_map.count("latex-no-matrix")) {
759 params::latex_include_matrix = false;
760 }
761 if (vars_map.count("tptp-proof")) {
762 params::generate_tptp_proof = true;
763 params::build_proof = true;
764 }
765 if (vars_map.count("tptp-proof")) {
766 params::generate_tptp_proof = true;
767 params::build_proof = true;
768 string tptp_p = vars_map["tptp-proof"].as<string>();
769 if (tptp_p != "default") {
770 params::tptp_proof_path = tptp_p;
771 params::tptp_proof_to_file = true;
772 }
773 }
774 if (vars_map.count("tptp-proof-no-subs")) {
775 params::tptp_proof_no_subs = true;
776 }
777 if (vars_map.count("tptp-proof-show-paths")) {
778 params::tptp_proof_show_paths = true;
779 }
780 if (vars_map.count("tptp-proof-show-all")) {
781 params::all_tptp_records = true;
782 }
783 if (vars_map.count("tptp-proof-show-subs")) {
784 params::tptp_proof_show_subs = true;
785 }
786 if (vars_map.count("sc-tptp-proof")) {
787 params::generate_sc_tptp_proof = true;
788 params::build_proof = true;
789 }
790 if (vars_map.count("prolog-proof")) {
791 params::generate_Prolog_proof = true;
792 params::build_proof = true;
793 }
794 if (vars_map.count("verify")) {
795 params::verify_proof = true;
796 params::build_proof = true;
797 }
798 if (vars_map.count("full-verify")) {
799 params::verify_proof = true;
800 params::verify_proof_verbose = true;
801 params::build_proof = true;
802 }
803 //------------------------------------------------------------------------
804 //------------------------------------------------------------------------
805 //------------------------------------------------------------------------
806 /*
807 * Now we're ready to start the party.
808 */
809 //------------------------------------------------------------------------
810 //------------------------------------------------------------------------
811 //------------------------------------------------------------------------
812 /*
813 * First, state the time we're starting at.
814 */
815 show(1, string("Connect++ V"));
816 show(1, string(VERSION));
817 show(1, string(" started at: "));
818 time_t date_and_time;
819 time(&date_and_time);
820 show(1, asctime(localtime(&date_and_time)));
821 /*
822 * You'll obviously be needing a prover.
823 *
824 * But it's not that simple! If we're running with
825 * no schedule then we'll just need one. However
826 * if we're running with a schedule then the matrix can be
827 * different for different sets of parameters, in particular
828 * because of --no-definitional and --all-definitional.
829 * We'll therefore use three different provers and
830 * switch between them.
831 */
832 StackProver prover_standard;
833 StackProver prover_all_definitional;
834 StackProver prover_no_definitional;
835 StackProver* prover = &prover_standard;
836 /*
837 * See if you can read the supplied input file.
838 *
839 * Make sure that if you're using a schedule, you
840 * make one for the default settings. If no schedule is
841 * being used then the command line settings will get used
842 * to make prover_standard.
843 */
844 if (params::use_schedule) {
845 params::no_definitional = false;
846 params::all_definitional = false;
847 }
848 show(1, string("Reading from: "));
849 show(1, path.c_str(), true);
850 bool found_conjecture = false;
851 size_t fof_size = 0;
852 try {
853 prover_standard.read_from_tptp_file(path.c_str(), found_conjecture, fof_size);
854 }
855 catch (std::exception& err) {
856 cout << "% SZS status Error for "
857 << params::problem_name << " : "
858 << err.what() << endl;
859 return EXIT_FAILURE;
860 }
861 /*
862 * If you're using a schedule, then set up provers for the
863 * extra cases.
864 */
865 if (params::use_schedule) {
866 params::all_definitional = true;
867 try {
868 prover_all_definitional.read_from_tptp_file(path.c_str(), found_conjecture, fof_size);
869 }
870 catch (std::exception& err) {
871 cout << "% SZS status Error for "
872 << params::problem_name << " : "
873 << err.what() << endl;
874 return EXIT_FAILURE;
875 }
876 params::all_definitional = false;
877 params::no_definitional = true;
878 try {
879 prover_no_definitional.read_from_tptp_file(path.c_str(), found_conjecture, fof_size);
880 }
881 catch (std::exception& err) {
882 cout << "% SZS status Error for "
883 << params::problem_name << " : "
884 << err.what() << endl;
885 return EXIT_FAILURE;
886 }
887 }
888 /*
889 * How long did all that take? Remember that reading from the file does all the
890 * necessary conversion of formulas to clauses.
891 */
892 chrono::milliseconds parse_time =
893 chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now() - startTime);
894 show(1, string("Total parse and conversion time: "));
895 show(1, std::to_string(parse_time.count()));
896 show(1, string(" milliseconds."), true);
897
898 prover_standard.set_problem_path(path);
899 prover_all_definitional.set_problem_path(path);
900 prover_no_definitional.set_problem_path(path);
901
902 show(2, string("Starting proof attempt for: "), false);
903 show(2, params::problem_name, true);
907 ProverOutcome prover_outcome;
908 ProverOutcome final_outcome;
909 final_outcome = ProverOutcome::TimeOut;
910 //------------------------------------------------------------------------
911 //------------------------------------------------------------------------
912 //------------------------------------------------------------------------
913 /*
914 * No schedule.
915 */
916 //------------------------------------------------------------------------
917 //------------------------------------------------------------------------
918 //------------------------------------------------------------------------
919 if (!params::use_schedule) {
920 if (params::verbosity > 2) {
921 prover_standard.show_matrix();
922 }
923 /*
924 * We're not using a schedule, so just run once using the
925 * command line options.
926 */
927 /*
928 * You want any timeout to include everything...
929 */
930 if (params::timeout) {
931 chrono::seconds timeout_duration(params::timeout_value);
932 prover_standard.set_timeout(startTime + timeout_duration);
933 }
934 /*
935 * Run the prover.
936 */
937 prover_outcome = prover_standard.prove();
938 final_outcome = prover_outcome;
939 }
940 //------------------------------------------------------------------------
941 //------------------------------------------------------------------------
942 //------------------------------------------------------------------------
943 /*
944 * A schedule is being used.
945 */
946 //------------------------------------------------------------------------
947 //------------------------------------------------------------------------
948 //------------------------------------------------------------------------
949 else {
950 /*
951 * Run using the specified or default schedule.
952 */
953 if (!params::timeout) {
954 params::timeout = true;
955 params::timeout_value = 600;
956 }
957 /*
958 * Convert to milliseconds and get a standard chunk of time.
959 */
960 chrono::milliseconds time_chunk((params::timeout_value * 1000) / 100);
961 chrono::steady_clock::time_point start_time = startTime;
962 chrono::milliseconds chunk_time;
963 /*
964 * If we're using a schedule, then it's already been read from the relevant
965 * file, so we can start it now.
966 */
967 size_t schedule_step_number = 1;
968 pair<bool, unsigned int> next_schedule = schedule.set_next_schedule();
969 if (params::verbosity > 0)
970 cout << endl;
971 /*
972 * Try each step of the schedule in turn.
973 */
974 while (next_schedule.first) {
975 show(1, string("Schedule step "));
976 show(1, std::to_string(schedule_step_number));
977 show(1, string(": "));
978 show(1, schedule.step_to_string(schedule_step_number - 1), true);
979 if (params::verbosity > 2) {
981 }
982 /*
983 * Set the appropriate prover to use.
984 */
985 if (params::all_definitional) {
986 prover = &prover_all_definitional;
987 }
988 else if (params::no_definitional) {
989 prover = &prover_no_definitional;
990 }
991 else {
992 prover = &prover_standard;
993 }
994 /*
995 * Set the timeouts.
996 */
997 if (next_schedule.second == 0) {
998 prover->set_timeout(startTime + chrono::seconds(params::timeout_value));
999 }
1000 else if (schedule_step_number == 1) {
1001 prover->set_timeout(startTime + (next_schedule.second * time_chunk));
1002 }
1003 else {
1004 prover->set_timeout(start_time + (next_schedule.second * time_chunk));
1005 }
1006
1007 prover_outcome = prover->prove();
1008
1009 if (prover_outcome == ProverOutcome::Valid ||
1010 prover_outcome == ProverOutcome::Error ||
1011 prover_outcome == ProverOutcome::False) {
1012 final_outcome = prover_outcome;
1013 break;
1014 }
1015
1016 chunk_time = chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now() - start_time);
1017
1018 show.nl(1, 2);
1019 show(1, string("Timeout after "));
1020 show(1, std::to_string(chunk_time.count()));
1021 show(1, string(" milliseconds"), true);
1022
1023 start_time = chrono::steady_clock::now();
1024 schedule_step_number++;
1025 next_schedule = schedule.set_next_schedule();
1026 }
1027 }
1028 //------------------------------------------------------------------------
1029 //------------------------------------------------------------------------
1030 //------------------------------------------------------------------------
1031 // We're done, so tidy up.
1032 //------------------------------------------------------------------------
1033 //------------------------------------------------------------------------
1034 //------------------------------------------------------------------------
1035 /*
1036 * Stop the timer.
1037 */
1038 chrono::milliseconds runTime =
1039 chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now() - startTime);
1040 show(1, string("Total run time: "));
1041 show(1, std::to_string(runTime.count()));
1042 show(1, string(" milliseconds."), true);
1043
1044 if (params::verbosity > 0) {
1045 if (params::show_full_stats) {
1046 prover->show_full_statistics(runTime.count());
1047 }
1048 else {
1049 prover->show_statistics();
1050 }
1051 }
1052 /*
1053 * Display the outcome.
1054 *
1055 * Note that there are a bunch of special cases here, depending on
1056 * some possible outcomes of the original clause simplification:
1057 *
1058 * - You have fof (+cnf, +maybe equality) axioms, and the conjecture can be $false, or empty,
1059 * or something.
1060 *
1061 * - You have no axioms (after simplification) and there's a conjecture
1062 * which is not $true or $false but may be empty.
1063 */
1064 string status(prover->get_status());
1065 string outcome;
1066
1067 bool cnf = prover->problem_is_cnf_only();
1068 bool conj_true = prover->problem_has_true_conjecture();
1069 bool conj_false = prover->problem_has_false_conjecture();
1070 bool conj_missing = prover->problem_has_missing_conjecture();
1071 bool neg_conj_removed = prover->problem_has_negated_conjecture_removed();
1072 bool fof_has_axioms = prover->problem_has_fof_axioms();
1073 bool simplified_has_axioms = prover->simplified_problem_has_fof_axioms();
1074
1075 cout << "% SZS status ";
1076 switch (final_outcome) {
1077 case ProverOutcome::Valid:
1078 // It's a proved CNF-only problem.
1079 if (cnf)
1080 outcome = "Unsatisfiable";
1081 // It's a proved FOF problem.
1082 else {
1083 // The problem has axioms.
1084 if (simplified_has_axioms) {
1085 // Axioms and $true conjecture.
1086 if (conj_true) {
1087 outcome = "ContradictoryAxioms";
1088 }
1089 // Axioms and $false conjecture.
1090 else if (conj_false || neg_conj_removed) {
1091 outcome = "ContradictoryAxioms";
1092 }
1093 // There was no conjecture. In this case we treat the
1094 // axioms as a conjecture, according to the SZS specs.
1095 else if (conj_missing) {
1096 outcome = "Unsatisfiable";
1097 }
1098 // Axioms and negated conjecture.
1099 else {
1100 outcome = "Theorem";
1101 }
1102 }
1103 // The problem doesn't have axioms. Uuurgh... we have to distinguish
1104 // between problems that had none in the first place, and problems
1105 // where they were simplified out.
1106 //
1107 // Luckily they turn out to be the same.
1108 else {
1109 // There's no conjecture because it was essentially $false.
1110 if (neg_conj_removed) {
1111 outcome = "Error";
1112 }
1113 // No axioms remaining, but there is a negated conjecture.
1114 else
1115 outcome = "Theorem";
1116 }
1117 }
1118 break;
1119 case ProverOutcome::False:
1120 // It's a refuted CNF-only problem.
1121 if (cnf)
1122 outcome = "Satisfiable";
1123 // It's a refuted FOF problem.
1124 else {
1125 if (simplified_has_axioms) {
1126 if (conj_true) {
1127 outcome = "Error";
1128 }
1129 else if (conj_false || neg_conj_removed) {
1130 outcome = "CounterSatisfiable";
1131 }
1132 else if (conj_missing) {
1133 outcome = "Satisfiable";
1134 }
1135 else {
1136 outcome = "CounterSatisfiable";
1137 }
1138 }
1139 else {
1140 // There's no conjecture, and the axioms are $true.
1141 if (conj_missing)
1142 outcome = "Theorem";
1143 // There's no conjecture because it was essentially $false.
1144 else if (neg_conj_removed) {
1145 outcome = "Error";
1146 }
1147 // No axioms remaining, but there is a negated conjecture.
1148 else
1149 outcome = "CounterSatisfiable";
1150 }
1151 }
1152 break;
1153 case ProverOutcome::PathLenLimit:
1154 outcome = "GaveUp";
1155 break;
1156 case ProverOutcome::Error:
1157 outcome = "Error";
1158 break;
1159 case ProverOutcome::TimeOut:
1160 // This is a slightly odd case that can arise if, for example, a
1161 // depth limit is set that causes all options to run out without
1162 // actually reaching a timeout.
1163 if (chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now() - startTime).count() < (params::timeout_value * 1000))
1164 outcome = "GaveUp";
1165 else
1166 outcome = "Timeout";
1167 break;
1168 default:
1169 outcome = "Error";
1170 break;
1171 }
1172 cout << outcome << " for " << params::problem_name;
1173 /*
1174 * If needed, verify the proof.
1175 */
1176 bool verified = false;
1177 if (params::verify_proof && final_outcome == ProverOutcome::Valid) {
1178 std::tuple<VariableIndex*,
1179 FunctionIndex*,
1181 TermIndex*>
1182 inds = prover->get_indexes();
1183
1184 vector<pair<string, vector<size_t>>>
1185 internal_proof(prover->get_internal_proof());
1186
1187 ProofChecker checker(prover->get_matrix(),
1188 internal_proof,
1189 get<0>(inds), get<3>(inds));
1190
1191 bool verification_outcome;
1192 string outcome_string;
1193 if (params::verify_proof_verbose) {
1194 pair<bool, string> outcome = checker.check_proof_verbose();
1195 verification_outcome = outcome.first;
1196 outcome_string = outcome.second;
1197 }
1198 else {
1199 verification_outcome = checker.check_proof();
1200 }
1201
1202 if (verification_outcome) {
1203 verified = true;
1204 cout << " : Verified";
1205 }
1206 else {
1207 cout << " : FailedVerification";
1208 }
1209
1210 if (params::verify_proof_verbose) {
1211 cout << endl << "% SZS output start Proof for " << params::problem_name << endl << endl;
1212 cout << outcome_string << endl;
1213 cout << "% SZS output end Proof for " << params::problem_name << endl;
1214 }
1215 }
1216 cout << endl;
1217 /*
1218 * Leave a minimal file with some outcome information.
1219 */
1220 if (params::write_output_summary) {
1221 try {
1222 std::ofstream out_file(params::output_summary_path);
1223 out_file << "ProblemName: " << params::problem_name << endl;
1224 if (found_conjecture)
1225 out_file << "ConjectureFound" << endl;
1226 else
1227 out_file << "NoConjectureFound" << endl;
1228 if (status == "")
1229 status = "NoStatus";
1230 out_file << "ProblemStatus: " << status << endl;
1231 out_file << "ProverOutcome: " << outcome << endl;
1232 if (params::verify_proof && final_outcome == ProverOutcome::Valid) {
1233 if (verified)
1234 out_file << "Verified" << endl;
1235 else
1236 out_file << "FailedVerification" << endl;
1237 }
1238 else
1239 out_file << "NoVerify" << endl;
1240 out_file << "Time: " << runTime.count() << endl;
1241 out_file.close();
1242 }
1243 catch (std::exception& err) {
1244 cerr << "Error: can't write output summary." << endl;
1245 }
1246 }
1247 /*
1248 * If necessary, write a TPTP proof to stdout.
1249 */
1250 if (params::generate_tptp_proof && (final_outcome == ProverOutcome::Valid)) {
1251 if (!params::tptp_proof_to_file) {
1252 cout << "% SZS output start Proof for " << params::problem_name << endl;
1253 cout << prover->proof_to_tptp();
1254 cout << "% SZS output end Proof for " << params::problem_name << endl;
1255 }
1256 else {
1257 try {
1258 std::ofstream out_file(params::tptp_proof_path);
1259 out_file << prover->proof_to_tptp();
1260 out_file.close();
1261 }
1262 catch (std::exception& err) {
1263 cerr << "Error: can't write TPTP proof to file." << endl;
1264 }
1265 }
1266 }
1267 return EXIT_SUCCESS;
1268}
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.
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.
string proof_to_tptp()
Show the TPTP-formatted conversion to CNF.
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()
Show a TPTP-formatted proof without the conversion info.
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.