CBMC
Loading...
Searching...
No Matches
solver_factory.cpp
Go to the documentation of this file.
1/*******************************************************************\
2
3Module: Solver Factory
4
5Author: Daniel Kroening, Peter Schrammel
6
7\*******************************************************************/
8
11
12#include "solver_factory.h"
13
14#include <util/cmdline.h>
16#include <util/exit_codes.h>
17#include <util/message.h>
18#include <util/options.h>
19#include <util/unicode.h>
20#include <util/version.h>
21
24#include <solvers/prop/prop.h>
33
34#include <iostream>
35
37 const optionst &_options,
38 const namespacet &_ns,
41 : options(_options),
42 ns(_ns),
43 message_handler(_message_handler),
44 output_xml_in_refinement(_output_xml_in_refinement)
45{
46}
47
48solver_factoryt::solvert::solvert(std::unique_ptr<stack_decision_proceduret> p)
49 : decision_procedure_ptr(std::move(p))
50{
51}
52
54 std::unique_ptr<stack_decision_proceduret> p1,
55 std::unique_ptr<propt> p2)
56 : prop_ptr(std::move(p2)), decision_procedure_ptr(std::move(p1))
57{
58}
59
61 std::unique_ptr<stack_decision_proceduret> p1,
62 std::unique_ptr<std::ofstream> p2)
63 : ofstream_ptr(std::move(p2)), decision_procedure_ptr(std::move(p1))
64{
65}
66
68 std::unique_ptr<boolbvt> p1,
69 std::unique_ptr<propt> p2)
70 : prop_ptr(std::move(p2)), decision_procedure_is_boolbvt_ptr(std::move(p1))
71{
72}
73
75 std::unique_ptr<boolbvt> p1,
76 std::unique_ptr<propt> p2,
77 std::unique_ptr<std::ofstream> p3)
78 : ofstream_ptr(std::move(p3)),
79 prop_ptr(std::move(p2)),
80 decision_procedure_is_boolbvt_ptr(std::move(p1))
81{
82}
83
85{
87 (decision_procedure_ptr != nullptr) !=
88 (decision_procedure_is_boolbvt_ptr != nullptr));
89 if(decision_procedure_ptr)
90 return *decision_procedure_ptr;
91 else
92 return *decision_procedure_is_boolbvt_ptr;
93}
94
96{
97 PRECONDITION(decision_procedure_is_boolbvt_ptr != nullptr);
98 return *decision_procedure_is_boolbvt_ptr;
99}
100
102 solver_resource_limitst &decision_procedure)
103{
104 const int timeout_seconds =
105 options.get_signed_int_option("solver-time-limit");
106
107 if(timeout_seconds > 0)
108 decision_procedure.set_time_limit_seconds(timeout_seconds);
109}
110
111std::unique_ptr<solver_factoryt::solvert> solver_factoryt::get_solver()
112{
113 if(options.get_bool_option("dimacs"))
114 return get_dimacs();
115 if(options.is_set("external-sat-solver"))
116 return get_external_sat();
117 if(
118 options.get_bool_option("refine") &&
119 !options.get_bool_option("refine-strings"))
120 {
121 return get_bv_refinement();
122 }
123 else if(options.get_bool_option("refine-strings"))
124 return get_string_refinement();
125 const auto incremental_smt2_solver =
126 options.get_option("incremental-smt2-solver");
127 if(!incremental_smt2_solver.empty())
129 if(options.get_bool_option("smt2"))
131 return get_default();
132}
133
137{
138 // we shouldn't get here if this option isn't set
139 PRECONDITION(options.get_bool_option("smt2"));
140
142
143 if(options.get_bool_option("bitwuzla"))
145 else if(options.get_bool_option("boolector"))
147 else if(options.get_bool_option("cprover-smt2"))
149 else if(options.get_bool_option("mathsat"))
151 else if(options.get_bool_option("cvc3"))
153 else if(options.get_bool_option("cvc4"))
155 else if(options.get_bool_option("cvc5"))
157 else if(options.get_bool_option("yices"))
159 else if(options.get_bool_option("z3"))
161 else if(options.get_bool_option("generic"))
163
164 return s;
165}
166
170 const std::string &solver)
171{
173 log.warning() << "The specified solver, '" << solver
174 << "', is not available. "
175 << "The default solver will be used instead." << messaget::eom;
176}
177
178template <typename SatcheckT>
179static typename std::enable_if<
180 !std::is_base_of<hardness_collectort, SatcheckT>::value,
181 std::unique_ptr<SatcheckT>>::type
183{
184 auto satcheck = std::make_unique<SatcheckT>(message_handler);
185 if(options.is_set("write-solver-stats-to"))
186 {
188 log.warning()
189 << "Configured solver does not support --write-solver-stats-to. "
190 << "Solver stats will not be written." << messaget::eom;
191 }
192 return satcheck;
193}
194
195template <typename SatcheckT>
196static typename std::enable_if<
197 std::is_base_of<hardness_collectort, SatcheckT>::value,
198 std::unique_ptr<SatcheckT>>::type
200{
201 auto satcheck = std::make_unique<SatcheckT>(message_handler);
202 if(options.is_set("write-solver-stats-to"))
203 {
204 std::unique_ptr<solver_hardnesst> solver_hardness =
205 std::make_unique<solver_hardnesst>();
206 solver_hardness->set_outfile(options.get_option("write-solver-stats-to"));
207 satcheck->solver_hardness = std::move(solver_hardness);
208 }
209 return satcheck;
210}
211
212static std::unique_ptr<propt>
214{
215 const bool no_simplifier = options.get_bool_option("beautify") ||
216 !options.get_bool_option("sat-preprocessor") ||
217 options.get_bool_option("refine-strings");
218
219 if(options.is_set("sat-solver"))
220 {
221 const std::string &solver_option = options.get_option("sat-solver");
222 if(solver_option == "zchaff")
223 {
224#if defined SATCHECK_ZCHAFF
226#else
228#endif
229 }
230 else if(solver_option == "booleforce")
231 {
232#if defined SATCHECK_BOOLERFORCE
234#else
236#endif
237 }
238 else if(solver_option == "minisat1")
239 {
240#if defined SATCHECK_MINISAT1
242#else
244#endif
245 }
246 else if(solver_option == "minisat2")
247 {
248#if defined SATCHECK_MINISAT2
249 if(no_simplifier)
250 {
251 // simplifier won't work with beautification
254 }
255 else // with simplifier
256 {
259 }
260#else
262#endif
263 }
264 else if(solver_option == "ipasir")
265 {
266#if defined SATCHECK_IPASIR
268#else
270#endif
271 }
272 else if(solver_option == "picosat")
273 {
274#if defined SATCHECK_PICOSAT
276#else
278#endif
279 }
280 else if(solver_option == "lingeling")
281 {
282#if defined SATCHECK_LINGELING
284#else
286#endif
287 }
288 else if(solver_option == "glucose")
289 {
290#if defined SATCHECK_GLUCOSE
291 if(no_simplifier)
292 {
293 // simplifier won't work with beautification
296 }
297 else // with simplifier
298 {
301 }
302#else
304#endif
305 }
306 else if(solver_option == "cadical")
307 {
308#if defined SATCHECK_CADICAL
311#else
313#endif
314 }
315 else
316 {
318 log.error() << "unknown solver '" << solver_option << "'"
319 << messaget::eom;
321 }
322 }
323
324 // default solver
325 if(no_simplifier)
326 {
327 // simplifier won't work with beautification
330 }
331 else // with simplifier
332 {
334 }
335}
336
337static std::unique_ptr<std::ofstream> open_outfile_and_check(
338 const std::string &filename,
340 const std::string &arg_name)
341{
342 if(filename.empty())
343 return nullptr;
344
345 auto out = std::make_unique<std::ofstream>(widen_if_needed(filename));
346
347 if(!*out)
348 {
350 "failed to open file: " + filename, arg_name);
351 }
352
354 log.status() << "Outputting formula to file: " << filename << messaget::eom;
355 return out;
356}
357
358std::unique_ptr<solver_factoryt::solvert> solver_factoryt::get_default()
359{
361
362 bool get_array_constraints =
363 options.get_bool_option("show-array-constraints");
364 auto bv_pointers = std::make_unique<bv_pointerst>(
365 ns, *sat_solver, message_handler, get_array_constraints);
366
367 if(options.get_option("arrays-uf") == "never")
369 else if(options.get_option("arrays-uf") == "always")
371
373
374 std::unique_ptr<boolbvt> boolbv = std::move(bv_pointers);
375 return std::make_unique<solvert>(std::move(boolbv), std::move(sat_solver));
376}
377
378std::unique_ptr<solver_factoryt::solvert> solver_factoryt::get_dimacs()
379{
382
383 auto prop = std::make_unique<dimacs_cnft>(message_handler);
384
385 std::string filename = options.get_option("outfile");
386
387 if(filename.empty() || filename == "-")
388 {
389 std::unique_ptr<boolbvt> bv_dimacs =
390 std::make_unique<bv_dimacst>(ns, *prop, message_handler, std::cout);
391
392 return std::make_unique<solvert>(std::move(bv_dimacs), std::move(prop));
393 }
394
395 auto outfile = open_outfile_and_check(filename, message_handler, "--outfile");
396
397 std::unique_ptr<boolbvt> bv_dimacs =
398 std::make_unique<bv_dimacst>(ns, *prop, message_handler, *outfile);
399
400 return std::make_unique<solvert>(
401 std::move(bv_dimacs), std::move(prop), std::move(outfile));
402}
403
404std::unique_ptr<solver_factoryt::solvert> solver_factoryt::get_external_sat()
405{
408
409 std::string external_sat_solver = options.get_option("external-sat-solver");
410 auto prop =
411 std::make_unique<external_satt>(message_handler, external_sat_solver);
412
413 std::unique_ptr<boolbvt> bv_pointers =
414 std::make_unique<bv_pointerst>(ns, *prop, message_handler);
415
416 return std::make_unique<solvert>(std::move(bv_pointers), std::move(prop));
417}
418
419std::unique_ptr<solver_factoryt::solvert> solver_factoryt::get_bv_refinement()
420{
421 std::unique_ptr<propt> prop = get_sat_solver(message_handler, options);
422
424 info.ns = &ns;
425 info.prop = prop.get();
427
428 // we allow setting some parameters
429 if(options.get_bool_option("max-node-refinement"))
430 info.max_node_refinement =
431 options.get_unsigned_int_option("max-node-refinement");
432
433 info.refine_arrays = options.get_bool_option("refine-arrays");
434 info.refine_arithmetic = options.get_bool_option("refine-arithmetic");
436
437 std::unique_ptr<boolbvt> decision_procedure =
438 std::make_unique<bv_refinementt>(info);
439 set_decision_procedure_time_limit(*decision_procedure);
440 return std::make_unique<solvert>(
441 std::move(decision_procedure), std::move(prop));
442}
443
447std::unique_ptr<solver_factoryt::solvert>
449{
451 info.ns = &ns;
453 info.prop = prop.get();
454 info.refinement_bound = DEFAULT_MAX_NB_REFINEMENT;
456 if(options.get_bool_option("max-node-refinement"))
457 info.max_node_refinement =
458 options.get_unsigned_int_option("max-node-refinement");
459 info.refine_arrays = options.get_bool_option("refine-arrays");
460 info.refine_arithmetic = options.get_bool_option("refine-arithmetic");
462
463 std::unique_ptr<boolbvt> decision_procedure =
464 std::make_unique<string_refinementt>(info);
465 set_decision_procedure_time_limit(*decision_procedure);
466 return std::make_unique<solvert>(
467 std::move(decision_procedure), std::move(prop));
468}
469
470std::unique_ptr<solver_factoryt::solvert>
472{
474
475 const std::string outfile_arg = options.get_option("outfile");
476 const std::string dump_smt_formula = options.get_option("dump-smt-formula");
477
478 if(!outfile_arg.empty() && !dump_smt_formula.empty())
479 {
481 "Argument --outfile is incompatible with --dump-smt-formula. ",
482 "--outfile");
483 }
484
485 std::unique_ptr<smt_base_solver_processt> solver_process = nullptr;
486
487 if(!outfile_arg.empty())
488 {
489 bool on_std_out = outfile_arg == "-";
490 std::unique_ptr<std::ostream> outfile =
492 ? nullptr
494 solver_process = std::make_unique<smt_incremental_dry_run_solvert>(
495 message_handler, on_std_out ? std::cout : *outfile, std::move(outfile));
496 }
497 else
498 {
499 const auto out_filename = options.get_option("dump-smt-formula");
500
501 // If no out_filename is provided `open_outfile_and_check` will return
502 // `nullptr`, and the solver will work normally without any logging.
503 solver_process = std::make_unique<smt_piped_solver_processt>(
504 std::move(solver_command),
507 out_filename, message_handler, "--dump-smt-formula"));
508 }
509
510 return std::make_unique<solvert>(
511 std::make_unique<smt2_incremental_decision_proceduret>(
512 ns, std::move(solver_process), message_handler));
513}
514
515std::unique_ptr<solver_factoryt::solvert>
517{
519
520 const std::string &filename = options.get_option("outfile");
521
522 if(filename.empty())
523 {
525 {
527 "required filename not provided",
528 "--outfile",
529 "provide a filename with --outfile");
530 }
531
532 auto smt2_dec = std::make_unique<smt2_dect>(
533 ns,
534 "cbmc",
535 std::string("Generated by CBMC ") + CBMC_VERSION,
536 "QF_AUFBV",
537 solver,
538 options.get_option("external-smt2-solver"),
540
541 if(options.get_bool_option("fpa"))
542 smt2_dec->use_FPA_theory = true;
543
544 return std::make_unique<solvert>(std::move(smt2_dec));
545 }
546 else if(filename == "-")
547 {
548 auto smt2_conv = std::make_unique<smt2_convt>(
549 ns,
550 "cbmc",
551 std::string("Generated by CBMC ") + CBMC_VERSION,
552 "QF_AUFBV",
553 solver,
554 std::cout);
555
556 if(options.get_bool_option("fpa"))
557 smt2_conv->use_FPA_theory = true;
558
559 return std::make_unique<solvert>(std::move(smt2_conv));
560 }
561 else
562 {
563 auto out = open_outfile_and_check(filename, message_handler, "--outfile");
564
565 auto smt2_conv = std::make_unique<smt2_convt>(
566 ns,
567 "cbmc",
568 std::string("Generated by CBMC ") + CBMC_VERSION,
569 "QF_AUFBV",
570 solver,
571 *out);
572
573 if(options.get_bool_option("fpa"))
574 smt2_conv->use_FPA_theory = true;
575
576 return std::make_unique<solvert>(std::move(smt2_conv), std::move(out));
577 }
578}
579
581{
582 if(options.get_bool_option("beautify"))
583 {
585 "the chosen solver does not support beautification", "--beautify");
586 }
587}
588
590{
591 const bool all_properties = options.get_bool_option("all-properties");
592 const bool cover = options.is_set("cover");
593 const bool incremental_loop = options.is_set("incremental-loop");
594
596 {
598 "the chosen solver does not support incremental solving",
599 "--all_properties");
600 }
601 else if(cover)
602 {
604 "the chosen solver does not support incremental solving", "--cover");
605 }
606 else if(incremental_loop)
607 {
609 "the chosen solver does not support incremental solving",
610 "--incremental-loop");
611 }
612}
613
614static void parse_sat_options(const cmdlinet &cmdline, optionst &options)
615{
616 if(cmdline.isset("external-sat-solver"))
617 {
618 options.set_option(
619 "external-sat-solver", cmdline.get_value("external-sat-solver"));
620 }
621
622 options.set_option("sat-preprocessor", !cmdline.isset("no-sat-preprocessor"));
623
624 if(cmdline.isset("dimacs"))
625 options.set_option("dimacs", true);
626
627 if(cmdline.isset("sat-solver"))
628 options.set_option("sat-solver", cmdline.get_value("sat-solver"));
629}
630
631static void parse_smt2_options(const cmdlinet &cmdline, optionst &options)
632{
633 if(cmdline.isset("smt2"))
634 options.set_option("smt2", true);
635
636 if(cmdline.isset("fpa"))
637 options.set_option("fpa", true);
638
639 bool solver_set = false;
640
641 if(cmdline.isset("bitwuzla"))
642 {
643 options.set_option("bitwuzla", true), solver_set = true;
644 options.set_option("smt2", true);
645 }
646
647 if(cmdline.isset("boolector"))
648 {
649 options.set_option("boolector", true), solver_set = true;
650 options.set_option("smt2", true);
651 }
652
653 if(cmdline.isset("cprover-smt2"))
654 {
655 options.set_option("cprover-smt2", true), solver_set = true;
656 options.set_option("smt2", true);
657 }
658
659 if(cmdline.isset("mathsat"))
660 {
661 options.set_option("mathsat", true), solver_set = true;
662 options.set_option("smt2", true);
663 }
664
665 if(cmdline.isset("cvc4"))
666 {
667 options.set_option("cvc4", true), solver_set = true;
668 options.set_option("smt2", true);
669 }
670
671 if(cmdline.isset("cvc5"))
672 {
673 options.set_option("cvc5", true), solver_set = true;
674 options.set_option("smt2", true);
675 }
676
677 if(cmdline.isset("incremental-smt2-solver"))
678 {
679 options.set_option(
680 "incremental-smt2-solver", cmdline.get_value("incremental-smt2-solver")),
681 solver_set = true;
682 }
683
684 if(cmdline.isset("yices"))
685 {
686 options.set_option("yices", true), solver_set = true;
687 options.set_option("smt2", true);
688 }
689
690 if(cmdline.isset("z3"))
691 {
692 options.set_option("z3", true), solver_set = true;
693 options.set_option("smt2", true);
694 }
695
696 if(cmdline.isset("external-smt2-solver"))
697 {
698 options.set_option(
699 "external-smt2-solver", cmdline.get_value("external-smt2-solver"));
700 solver_set = true;
701 options.set_option("smt2", true);
702 }
703
704 if(cmdline.isset("smt2") && !solver_set)
705 {
706 if(cmdline.isset("outfile"))
707 {
708 // outfile and no solver should give standard compliant SMT-LIB
709 options.set_option("generic", true);
710 }
711 else
712 {
713 // the default smt2 solver
714 options.set_option("z3", true);
715 }
716 }
717}
718
720{
721 parse_sat_options(cmdline, options);
722 parse_smt2_options(cmdline, options);
723
724 if(cmdline.isset("outfile"))
725 options.set_option("outfile", cmdline.get_value("outfile"));
726
727 if(cmdline.isset("dump-smt-formula"))
728 options.set_option(
729 "dump-smt-formula", cmdline.get_value("dump-smt-formula"));
730
731 if(cmdline.isset("write-solver-stats-to"))
732 {
733 options.set_option(
734 "write-solver-stats-to", cmdline.get_value("write-solver-stats-to"));
735 }
736
737 if(cmdline.isset("beautify"))
738 options.set_option("beautify", true);
739
740 if(cmdline.isset("refine-arrays"))
741 {
742 options.set_option("refine", true);
743 options.set_option("refine-arrays", true);
744 }
745
746 if(cmdline.isset("refine-arithmetic"))
747 {
748 options.set_option("refine", true);
749 options.set_option("refine-arithmetic", true);
750 }
751
752 if(cmdline.isset("refine"))
753 {
754 options.set_option("refine", true);
755 options.set_option("refine-arrays", true);
756 options.set_option("refine-arithmetic", true);
757 }
758
759 if(cmdline.isset("max-node-refinement"))
760 {
761 options.set_option(
762 "max-node-refinement", cmdline.get_value("max-node-refinement"));
763 }
764}
Writing DIMACS Files.
Abstraction Refinement Loop.
message_handlert & message_handler
Definition ai.h:525
virtual xmlt output_xml(const namespacet &ns, const irep_idt &function_id, const goto_programt &goto_program) const
Output the abstract states for a single function as XML.
Definition ai.cpp:136
ait supplies three of the four components needed: an abstract interpreter (in this case handling func...
Definition ai.h:566
std::string get_value(char option) const
Definition cmdline.cpp:48
virtual bool isset(char option) const
Definition cmdline.cpp:30
Thrown when users pass incorrect command line arguments, for example passing no files to analysis or ...
Class that provides messages with a built-in verbosity 'level'.
Definition message.h:154
static eomt eom
Definition message.h:289
A namespacet is essentially one or two symbol tables bound together, to allow for symbol lookups in t...
Definition namespace.h:91
boolbvt & boolbv_decision_procedure() const
solvert(std::unique_ptr< stack_decision_proceduret > p)
stack_decision_proceduret & decision_procedure() const
message_handlert & message_handler
const optionst & options
const namespacet & ns
std::unique_ptr< solvert > get_external_sat()
std::unique_ptr< solvert > get_default()
solver_factoryt(const optionst &_options, const namespacet &_ns, message_handlert &_message_handler, bool _output_xml_in_refinement)
Note: The solver returned will hold a reference to the namespace ns.
void set_decision_procedure_time_limit(solver_resource_limitst &decision_procedure)
Sets the timeout of decision_procedure if the solver-time-limit option has a positive value (in secon...
std::unique_ptr< solvert > get_dimacs()
std::unique_ptr< solvert > get_string_refinement()
the string refinement adds to the bit vector refinement specifications for functions from the Java st...
std::unique_ptr< solvert > get_smt2(smt2_dect::solvert solver)
const bool output_xml_in_refinement
smt2_dect::solvert get_smt2_solver_type() const
Uses the options to pick an SMT 2.0 solver.
virtual std::unique_ptr< solvert > get_solver()
Returns a solvert object.
std::unique_ptr< solvert > get_incremental_smt2(std::string solver_command)
std::unique_ptr< solvert > get_bv_refinement()
virtual void set_time_limit_seconds(uint32_t)=0
Set the limit for the solver to time out in seconds.
Document and give macros for the exit codes of CPROVER binaries.
#define CPROVER_EXIT_USAGE_ERROR
A usage error is returned when the command line is invalid or conflicting.
Definition exit_codes.h:33
Allows calling an external SAT solver to allow faster integration of newer SAT solvers.
double log(double x)
Definition math.c:2416
STL namespace.
Options.
Decision procedure with incremental SMT2 solving.
void solver(std::vector< framet > &frames, const std::unordered_set< symbol_exprt, irep_hash > &address_taken, const solver_optionst &solver_options, const namespacet &ns, std::vector< propertyt > &properties, std::size_t property_index)
Definition solver.cpp:44
static std::enable_if<!std::is_base_of< hardness_collectort, SatcheckT >::value, std::unique_ptr< SatcheckT > >::type make_satcheck_prop(message_handlert &message_handler, const optionst &options)
static void parse_sat_options(const cmdlinet &cmdline, optionst &options)
static std::unique_ptr< std::ofstream > open_outfile_and_check(const std::string &filename, message_handlert &message_handler, const std::string &arg_name)
static void parse_smt2_options(const cmdlinet &cmdline, optionst &options)
static std::unique_ptr< propt > get_sat_solver(message_handlert &message_handler, const optionst &options)
static void emit_solver_warning(message_handlert &message_handler, const std::string &solver)
Emit a warning for non-existent solver solver via message_handler.
Solver Factory.
void parse_solver_options(const cmdlinet &cmdline, optionst &options)
Parse solver-related command-line parameters in cmdline and set corresponding values in options.
Solver capability to set resource limits.
#define PRECONDITION(CONDITION)
Definition invariant.h:463
void exit(int status)
Definition stdlib.c:102
String support via creating string constraints and progressively instantiating the universal constrai...
#define DEFAULT_MAX_NB_REFINEMENT
string_refinementt constructor arguments
#define widen_if_needed(s)
Definition unicode.h:28
const char * CBMC_VERSION