HAL
smt.cpp
Go to the documentation of this file.
2 
3 namespace hal
4 {
6  {
7  auto py_smt = m.def_submodule("SMT", R"(
8  SMT solver functions.
9  )");
10 
11  py::enum_<SMT::SolverType> py_smt_solver_type(py_smt, "SolverType", R"(
12  Identifier for the SMT solver type.
13  )");
14 
15  py_smt_solver_type.value("Z3", SMT::SolverType::Z3, R"(Z3 SMT solver.)")
16  .value("Boolector", SMT::SolverType::Boolector, R"(Boolector SMT solver.)")
17  .value("Unknown", SMT::SolverType::Unknown, R"(Unknown (unsupported) SMT solver.)")
18  .export_values();
19 
20  py::class_<SMT::QueryConfig> py_smt_query_config(py_smt, "QueryConfig", R"(
21  Represents the data structure to configure an SMT query.
22  )");
23 
24  py_smt_query_config.def(py::init<>(), R"(
25  Constructs a new query configuration.
26  )");
27 
28  py_smt_query_config.def_readwrite("solver", &SMT::QueryConfig::solver, R"(
29  The SMT solver identifier.
30 
31  :type: hal_py.SMT.SolverType
32  )");
33 
34  py_smt_query_config.def_readwrite("local", &SMT::QueryConfig::local, R"(
35  Controls whether the SMT query is performed on a local or a remote machine.
36 
37  :type: bool
38  )");
39 
40  py_smt_query_config.def_readwrite("generate_model", &SMT::QueryConfig::generate_model, R"(
41  Controls whether the SMT solver should generate a model in case formula is satisfiable.
42 
43  :type: bool
44  )");
45 
46  py_smt_query_config.def_readwrite("timeout_in_seconds", &SMT::QueryConfig::timeout_in_seconds, R"(
47  The timeout after which the SMT solver is killed in seconds.
48 
49  :type: int
50  )");
51 
52  py_smt_query_config.def("with_solver", &SMT::QueryConfig::with_solver, py::arg("solver"), R"(
53  Sets the solver type to the desired SMT solver.
54 
55  :param hal_py.SMT.SolverType solver: The solver type identifier.
56  :returns: The updated SMT query configuration.
57  :rtype: hal_py.SMT.QueryConfig
58  )");
59 
60  py_smt_query_config.def("with_call", &SMT::QueryConfig::with_call, py::arg("call"), R"(
61  Sets the call type to the desired target.
62 
63  :param hal_py.SMT.CallTyepe call: The solver type identifier.
64  :returns: The updated SMT query configuration.
65  :rtype: hal_py.SMT.QueryConfig
66  )");
67 
68  py_smt_query_config.def("with_local_solver", &SMT::QueryConfig::with_local_solver, R"(
69  Activates local SMT solver execution.
70 
71  :returns: The updated SMT query configuration.
72  :rtype: hal_py.SMT.QueryConfig
73  )");
74 
75  py_smt_query_config.def("with_remote_solver", &SMT::QueryConfig::with_remote_solver, R"(
76  Activates remote SMT solver execution.
77 
78  :returns: The updated SMT query configuration.
79  :rtype: hal_py.SMT.QueryConfig
80  )");
81 
82  py_smt_query_config.def("with_model_generation", &SMT::QueryConfig::with_model_generation, R"(
83  Indicates that the SMT solver should generate a model in case the formula is satisfiable.
84 
85  :returns: The updated SMT query configuration.
86  :rtype: hal_py.SMT.QueryConfig
87  )");
88 
89  py_smt_query_config.def("without_model_generation", &SMT::QueryConfig::without_model_generation, R"(
90  Indicates that the SMT solver should not generate a model.
91 
92  :returns: The updated SMT query configuration.
93  :rtype: hal_py.SMT.QueryConfig
94  )");
95 
96  py_smt_query_config.def("with_timeout", &SMT::QueryConfig::with_timeout, py::arg("seconds"), R"(
97  Sets a timeout in seconds that terminates an SMT query after the specified time has passed.
98 
99  :param int seconds: The timeout in seconds.
100  :returns: The updated SMT query configuration.
101  :rtype: hal_py.SMT.QueryConfig
102  )");
103 
104  py::class_<SMT::Constraint> py_smt_constraint(py_smt, "Constraint", R"(
105  Represents a constraint to the SMT query.
106  A constraint is either an assignment of two Boolean functions or a single Boolean function, e.g., an equality check or similar.
107  )");
108 
109  py_smt_constraint.def_readwrite("constraint", &SMT::Constraint::constraint, R"(
110  A constraint that is either an assignment of two Boolean functions or a single Boolean function, e.g., an equality check or similar.
111 
112  :type: hal_py.BooleanFunction or tuple(hal_py.BooleanFunction, hal_py.BooleanFunction)
113  )");
114 
115  py_smt_constraint.def(py::init([](BooleanFunction constraint) { return new SMT::Constraint(std::move(constraint)); }), py::arg("constraint"), R"(
116  Constructs a new constraint from one Boolean function that evaluates to a single bit.
117 
118  :param hal_py.BooleanFunction constraint: The constraint function.
119  )");
120 
121  py_smt_constraint.def(py::init([](BooleanFunction lhs, BooleanFunction rhs) { return new SMT::Constraint(std::move(lhs), std::move(rhs)); }), py::arg("lhs"), py::arg("rhs"), R"(
122  Constructs a new equality constraint from two Boolean functions.
123 
124  :param hal_py.BooleanFunction lhs: The left-hand side of the equality constraint.
125  :param hal_py.BooleanFunction rhs: The right-hand side of the equality constraint.
126  )");
127 
128  py_smt_constraint.def("is_assignment", &SMT::Constraint::is_assignment, R"(
129  Checks whether the constraint is an assignment constraint.
130 
131  :returns: True of the constraint is an assignment, False otherwise.
132  :rtype: bool
133  )");
134 
135  py_smt_constraint.def(
136  "get_assignment",
137  [](const SMT::Constraint& self) -> std::optional<std::pair<BooleanFunction, BooleanFunction>> {
138  auto res = self.get_assignment();
139  if (res.is_ok())
140  {
141  return *res.get();
142  }
143  else
144  {
145  log_error("python_context", "{}", res.get_error().get());
146  return std::nullopt;
147  }
148  },
149  R"(
150  Returns the assignment constraint as a pair of Boolean functions.
151 
152  :returns: The assignment constraint on success, None otherwise.
153  :rtype: tuple(hal_py.BooleanFunction,hal_py.BooleanFunction) or None
154  )");
155 
156  py_smt_constraint.def(
157  "get_function",
158  [](const SMT::Constraint& self) -> std::optional<const BooleanFunction*> {
159  auto res = self.get_function();
160  if (res.is_ok())
161  {
162  return res.get();
163  }
164  else
165  {
166  log_error("python_context", "{}", res.get_error().get());
167  return std::nullopt;
168  }
169  },
170  R"(
171  Returns the function constraint as a Boolean function.
172 
173  :returns: The function constraint on success, None otherwise.
174  :rtype: hal_py.BooleanFunction or None
175  )");
176 
177  py::enum_<SMT::SolverResultType> py_smt_result_type(py_smt, "SolverResultType", R"(
178  Result type of an SMT solver query.
179  )");
180 
181  py_smt_result_type.value("Sat", SMT::SolverResultType::Sat, R"(The list of constraints is satisfiable.)")
182  .value("UnSat", SMT::SolverResultType::UnSat, R"(The list of constraints is not satisfiable.)")
183  .value("Unknown", SMT::SolverResultType::Unknown, R"(A result could not be obtained, e.g., due to a time-out.)")
184  .export_values();
185 
186  py::class_<SMT::Model> py_smt_model(py_smt, "Model", R"(
187  Represents a list of assignments for variable nodes that yield a satisfiable assignment for a given list of constraints.
188  )");
189 
190  py_smt_model.def(py::init<const std::map<std::string, std::tuple<u64, u16>>&>(), py::arg("model") = std::map<std::string, std::tuple<u64, u16>>(), R"(
191  Constructs a new model from a map of variable names to value and bit-size.
192 
193  :param dict[str,tuple(int,int)] model: A dict from variable name to value and bit-size.
194  )");
195 
196  py_smt_model.def(py::self == py::self, R"(
197  Checks whether two SMT models are equal.
198 
199  :returns: True if both models are equal, False otherwise.
200  :rtype: bool
201  )");
202 
203  py_smt_model.def(py::self != py::self, R"(
204  Checks whether two SMT models are unequal.
205 
206  :returns: True if both models are unequal, False otherwise.
207  :rtype: bool
208  )");
209 
210  py_smt_model.def_readwrite("model", &SMT::Model::model, R"(
211  A dict from variable identifiers to a (1) value and (2) its bit-size.
212 
213  :type: dict(str,tuple(int,int))
214  )");
215 
216  py_smt_model.def_static(
217  "parse",
218  [](const std::string& model_str, const SMT::SolverType& solver) -> std::optional<SMT::Model> {
219  auto res = SMT::Model::parse(model_str, solver);
220  if (res.is_ok())
221  {
222  return res.get();
223  }
224  else
225  {
226  log_error("python_context", "{}", res.get_error().get());
227  return std::nullopt;
228  }
229  },
230  py::arg("model_str"),
231  py::arg("solver"),
232  R"(
233  Parses an SMT-Lib model from a string output by a solver of the given type.
234 
235  :param str model_str: The SMT-Lib model string.
236  :param hal_py.SMT.SolverType solver: The solver that computed the model.
237  :returns: The model on success, None otherwise.
238  :rtype: hal_py.SMT.Model or None
239  )");
240 
241  py_smt_model.def(
242  "evaluate",
243  [](const SMT::Model& self, const BooleanFunction& bf) -> std::optional<BooleanFunction> {
244  auto res = self.evaluate(bf);
245  if (res.is_ok())
246  {
247  return res.get();
248  }
249  else
250  {
251  log_error("python_context", "{}", res.get_error().get());
252  return std::nullopt;
253  }
254  },
255  py::arg("bf"),
256  R"(
257  Evaluates the given Boolean function by replacing all variables contained in the model with their corresponding value and simplifying the result.
258 
259  :param hal_py.BooleanFunction bf: The Boolean function to evaluate.
260  :returns: The evaluated function on success, None otherwise.
261  :rtype: hal_py.BooleanFunction or None
262  )");
263 
264  py::class_<SMT::SolverResult> py_smt_result(py_smt, "SolverResult", R"(
265  Represents the result of an SMT query.
266  )");
267 
268  py_smt_result.def_readwrite("type", &SMT::SolverResult::type, R"(
269  Result type of the SMT query.
270 
271  :type: hal_py.SMT.ResultType
272  )");
273 
274  py_smt_result.def_readwrite("model", &SMT::SolverResult::model, R"(
275  The (optional) model that is only available if type == SMT.ResultType.Sat and model generation is enabled.
276 
277  :type: hal_py.SMT.Model
278  )");
279 
280  py_smt_result.def_static("Sat", &SMT::SolverResult::Sat, py::arg("model") = std::optional<SMT::Model>(), R"(
281  Creates a satisfiable result with an optional model.
282 
283  :param hal_py.SMT.Model model: Optional model for satisfiable formula.
284  :returns: The result.
285  :rtype: hal_py.SMT.SolverResult
286  )");
287 
288  py_smt_result.def_static("UnSat", &SMT::SolverResult::UnSat, R"(
289  Creates an unsatisfiable result.
290 
291  :returns: The result.
292  :rtype: hal_py.SMT.SolverResult
293  )");
294 
295  py_smt_result.def_static("Unknown", &SMT::SolverResult::Unknown, R"(
296  Creates an unknown result.
297 
298  :returns: The result.
299  :rtype: hal_py.SMT.SolverResult
300  )");
301 
302  py_smt_result.def("is", &SMT::SolverResult::is, py::arg("type"), R"(
303  Checks whether the result is of a specific type.
304 
305  :param hal_py.SMT.ResultType type: The type to check.
306  :returns: True in case result matches the given type, False otherwise.
307  :rtype: bool
308  )");
309 
310  py_smt_result.def("is_sat", &SMT::SolverResult::is_sat, R"(
311  Checks whether the result is satisfiable.
312 
313  :returns: True in case result is satisfiable, False otherwise.
314  :rtype: bool
315  )");
316 
317  py_smt_result.def("is_unsat", &SMT::SolverResult::is_unsat, R"(
318  Checks whether the result is unsatisfiable.
319 
320  :returns: True in case result is unsatisfiable, False otherwise.
321  :rtype: bool
322  )");
323 
324  py_smt_result.def("is_unknown", &SMT::SolverResult::is_unknown, R"(
325  Checks whether the result is unknown.
326 
327  :returns: True in case result is unknown, False otherwise.
328  :rtype: bool
329  )");
330 
331  py::class_<SMT::Solver> py_smt_solver(py_smt, "Solver", R"(
332  Provides an interface to query SMT solvers for a list of constraints, i.e. statements that have to be equal. To this end, we translate constraints to a SMT-LIB v2 string representation and query solvers with a defined configuration, i.e., chosen solver, model generation etc.
333  )");
334 
335  py_smt_solver.def(py::init<std::vector<SMT::Constraint>>(), py::arg("constraints") = std::vector<SMT::Constraint>(), R"(
336  Constructs an solver with an optional list of constraints.
337 
338  :param list[hal_py.SMT.Constraint] constraints: The (optional) list of constraints.
339  )");
340 
341  py_smt_solver.def_property_readonly("constraints", &SMT::Solver::get_constraints, R"(
342  The list of constraints.
343 
344  :type: list[hal_py.SMT.Constraint]
345  )");
346 
347  py_smt_solver.def("get_constraints", &SMT::Solver::get_constraints, R"(
348  Returns the list of constraints.
349 
350  :returns: The list of constraints.
351  :rtype: list[hal_py.SMT.Constraint]
352  )");
353 
354  py_smt_solver.def("with_constraint", &SMT::Solver::with_constraint, py::arg("constraint"), R"(
355  Adds a constraint to the SMT solver.
356 
357  :param hal_py.SMT.Constraint constraint: The constraint.
358  :returns: The updated SMT solver.
359  :rtype: hal_py.SMT.Solver
360  )");
361 
362  py_smt_solver.def("with_constraints", &SMT::Solver::with_constraints, py::arg("constraints"), R"(
363  Adds a list of constraints to the SMT solver.
364 
365  :param list[hal_py.SMT.Constraint] constraints: The constraints.
366  :returns: The updated SMT solver.
367  :rtype: hal_py.SMT.Solver
368  )");
369 
370  py_smt_solver.def_static("has_local_solver_for", &SMT::Solver::has_local_solver_for, py::arg("type"), py::arg("call"), R"(
371  Checks whether a SMT solver of the given type is available on the local machine.
372 
373  :param hal_py.SMT.SolverType type: The SMT solver type.
374  :param hal_py.SMT.SolverCall call: The call to the SMT solver.
375  :returns: True if an SMT solver of the requested type is available, False otherwise.
376  :rtype: bool
377  )");
378 
379  py_smt_solver.def(
380  "query",
381  [](const SMT::Solver& self, const SMT::QueryConfig& config = SMT::QueryConfig()) -> std::optional<SMT::SolverResult> {
382  auto res = self.query(config);
383  if (res.is_ok())
384  {
385  return res.get();
386  }
387  else
388  {
389  log_error("python_context", "{}", res.get_error().get());
390  return std::nullopt;
391  }
392  },
393  py::arg("config") = SMT::QueryConfig(),
394  R"(
395  Queries an SMT solver with the specified query configuration.
396 
397  :param hal_py.SMT.QueryConfig config: The SMT solver query configuration.
398  :returns: The result on success, a string error message otherwise.
399  :rtype: hal_py.SMT.Result or str
400  )");
401 
402  py_smt_solver.def(
403  "query_local",
404  [](const SMT::Solver& self, const SMT::QueryConfig& config) -> std::optional<SMT::SolverResult> {
405  auto res = self.query_local(config);
406  if (res.is_ok())
407  {
408  return res.get();
409  }
410  else
411  {
412  log_error("python_context", "{}", res.get_error().get());
413  return std::nullopt;
414  }
415  },
416  py::arg("config"),
417  R"(
418  Queries a local SMT solver with the specified query configuration.
419 
420  :param hal_py.SMT.QueryConfig config: The SMT solver query configuration.
421  :returns: The result on success, a string error message otherwise.
422  :rtype: hal_py.SMT.Result or str
423  )");
424 
425  py_smt_solver.def_static(
426  "query_local_with_smt2",
427  [](const SMT::QueryConfig& config, const std::string& smt2) -> std::optional<SMT::SolverResult> {
428  auto res = SMT::Solver::query_local_with_smt2(config, smt2);
429  if (res.is_ok())
430  {
431  return res.get();
432  }
433  else
434  {
435  log_error("python_context", "{}", res.get_error().get());
436  return std::nullopt;
437  }
438  },
439  py::arg("config"),
440  py::arg("smt2"),
441  R"(
442  Queries a local SMT solver with the specified query configuration and the smt2 formatted query.
443 
444  :param hal_py.SMT.QueryConfig config: The SMT solver query configuration.
445  :param string smt2: A solver query formatted in smt2 style.
446  :returns: The result on success, a string error message otherwise.
447  :rtype: hal_py.SMT.Result or str
448  )");
449 
450  py_smt_solver.def("query_remote", &SMT::Solver::query_remote, py::arg("config"), R"(
451  Queries a remote SMT solver with the specified query configuration.
452 
453  WARNING: This function is not yet implemented.
454 
455  :param hal_py.SMT.QueryConfig config: The SMT solver query configuration.
456  :returns: The result on success, a string error message otherwise.
457  :rtype: hal_py.SMT.Result or str
458  )");
459 
460  py::class_<SMT::SymbolicState> py_smt_symbolic_state(py_smt, "SymbolicState", R"(
461  Represents the data structure that keeps track of symbolic variable values (e.g., required for symbolic simplification).
462  )");
463 
464  py_smt_symbolic_state.def(py::init<const std::vector<BooleanFunction>&>(), py::arg("variables") = std::vector<BooleanFunction>(), R"(
465  Constructs a symbolic state and (optionally) initializes the variables.
466 
467  :param list[hal_py.BooleanFunction] variables: The (optional) list of variables.
468  )");
469 
470  py_smt_symbolic_state.def("get", &SMT::SymbolicState::get, py::arg("key"), R"(
471  Looks up the Boolean function equivalent in the symbolic state.
472 
473  :param hal_py.BooleanFunction key: The Boolean function to look up.
474  :returns: The Boolean function equivalent from the symbolic state or the key itself if it is not contained in the symbolic state.
475  :rtype: hal_py.BooleanFunction
476  )");
477 
478  py_smt_symbolic_state.def("set", &SMT::SymbolicState::set, py::arg("key"), py::arg("value"), R"(
479  Sets a Boolean function equivalent in the symbolic state.
480 
481  :param hal_py.BooleanFunction key: The Boolean function.
482  :param hal_py.BooleanFunction value: The equivalent Boolean function.
483  )");
484 
485  py::class_<SMT::SymbolicExecution> py_smt_symbolic_execution(py_smt, "SymbolicExecution", R"(
486  Represents the symbolic execution engine that handles the evaluation and simplification of Boolean function abstract syntax trees.
487  )");
488 
489  py_smt_symbolic_execution.def_readwrite("state", &SMT::SymbolicExecution::state, R"(
490  The current symbolic state.
491 
492  :type: hal_py.SMT.SymbolicState
493  )");
494 
495  py_smt_symbolic_execution.def(py::init<const std::vector<BooleanFunction>&>(), py::arg("variables") = std::vector<BooleanFunction>(), R"(
496  Creates a symbolic execution engine and (optionally) initializes the variables.
497 
498  :param list[hal_py.BooleanFunction] variables: The (optional) list of variables.
499  )");
500 
501  py_smt_symbolic_execution.def("evaluate", py::overload_cast<const BooleanFunction&>(&SMT::SymbolicExecution::evaluate, py::const_), py::arg("function"), R"(
502  Evaluates a Boolean function within the symbolic state of the symbolic execution.
503 
504  :param hal_py.BooleanFunction function: The Boolean function to evaluate.
505  :returns: The evaluated Boolean function on success, a string error message otherwise.
506  :rtype: hal_py.BooleanFunction or str
507  )");
508 
509  py_smt_symbolic_execution.def("evaluate", py::overload_cast<const SMT::Constraint&>(&SMT::SymbolicExecution::evaluate), py::arg("constraint"), R"(
510  Evaluates an equality constraint and applies it to the symbolic state of the symbolic execution.
511 
512  :param hal_py.SMT.Constraint constraint: The equality constraint to evaluate.
513  :returns: None on success, a string error message otherwise.
514  :rtype: None or str
515  )");
516  }
517 } // namespace hal
static bool has_local_solver_for(SolverType type, SolverCall call)
Definition: solver.cpp:376
Solver & with_constraints(const std::vector< Constraint > &constraints)
Definition: solver.cpp:362
const std::vector< Constraint > & get_constraints() const
Definition: solver.cpp:371
static Result< SolverResult > query_local_with_smt2(const QueryConfig &config, const std::string &smt2)
Definition: solver.cpp:440
Result< SolverResult > query_remote(const QueryConfig &config) const
Definition: solver.cpp:451
Solver & with_constraint(const Constraint &constraint)
Definition: solver.cpp:356
Result< BooleanFunction > evaluate(const BooleanFunction &function) const
SymbolicState state
The current symbolic state.
const BooleanFunction & get(const BooleanFunction &key) const
void set(const BooleanFunction &key, const BooleanFunction &value)
void smt_init(py::module &m)
Definition: smt.cpp:5
#define log_error(channel,...)
Definition: log.h:78
SolverType
Definition: types.h:44
const Module * module(const Gate *g, const NodeBoxes &boxes)
bool is_assignment() const
Definition: types.cpp:173
std::variant< BooleanFunction, std::pair< BooleanFunction, BooleanFunction > > constraint
A constraint that is either an assignment of two Boolean functions or a single Boolean function,...
Definition: types.h:154
static Result< Model > parse(const std::string &model_str, const SolverType &solver)
Definition: types.cpp:220
std::map< std::string, std::tuple< u64, u16 > > model
maps variable identifiers to a (1) value and (2) its bit-size.
Definition: types.h:233
bool generate_model
Controls whether the SMT solver should generate a model in case formula is satisfiable.
Definition: types.h:73
QueryConfig & with_remote_solver()
Definition: types.cpp:107
SolverType solver
The SMT solver identifier.
Definition: types.h:67
u64 timeout_in_seconds
The timeout after which the SMT solver is killed in seconds.
Definition: types.h:75
QueryConfig & without_model_generation()
Definition: types.cpp:119
QueryConfig & with_model_generation()
Definition: types.cpp:113
QueryConfig & with_local_solver()
Definition: types.cpp:101
QueryConfig & with_timeout(u64 seconds)
Definition: types.cpp:125
QueryConfig & with_solver(SolverType solver)
Definition: types.cpp:89
QueryConfig & with_call(SolverCall call)
Definition: types.cpp:95
static SolverResult Unknown()
Definition: types.cpp:309
std::optional< Model > model
The (optional) model that is only available if type == SMT::ResultType::Sat and model generation is e...
Definition: types.h:305
static SolverResult Sat(const std::optional< Model > &model={})
Definition: types.cpp:299
bool is_sat() const
Definition: types.cpp:319
bool is(const SolverResultType &type) const
Definition: types.cpp:314
bool is_unsat() const
Definition: types.cpp:324
static SolverResult UnSat()
Definition: types.cpp:304
SolverResultType type
Result type of the SMT query.
Definition: types.h:303
bool is_unknown() const
Definition: types.cpp:329