rsmt2_zz/lib.rs
1//! A wrapper around SMT Lib 2(.5)-compliant SMT solvers.
2//!
3//! See [`CHANGES.md`][changes] for the list of changes.
4//!
5//! If you use this library consider contacting us on the [repository][rsmt2] so that we can add
6//! your project to the readme.
7//!
8//!
9//!
10//!
11//! # Description
12//!
13//!
14//! In rsmt2, solvers run in a separate process and communication is achieved *via* system pipes.
15//! For the moment, only [z3][z3] is officially supported. If you would like `rsmt2` to support
16//! other solvers, please open an issue on the [repository][rsmt2].
17//!
18//! **NB**: most of the tests and documentation examples in this crate will not work unless you have
19//! [z3][z3] in your path under the name `z3`.
20//!
21//! This library does **not** have a structure for S-expressions. It must be provided by the user,
22//! as well as the relevant printing and parsing functions. Printing-related traits are discussed in
23//! the [`print`][print_mod] module, and parsing-related traits are in the [`parse`][parse_mod]
24//! module.
25//!
26//!
27//! # Supported Backend Solvers
28//!
29//! This crate supports the following solvers:
30//!
31//! - [z3][z3]: full support
32//! - [cvc4][cvc4]: full support in theory, but only partially tested. Note that `get-value` is
33//! known to crash some versions of CVC4.
34//! - [yices 2][yices 2]: full support in theory, but only partially tested. Command `get-model`
35//! will only work on Yices 2 > `2.6.1`, and needs to be activated in [SmtConf][SmtConf] with
36//! [`conf.models()`](struct.SmtConf.html#method.models). To understand why, see
37//! <https://github.com/SRI-CSL/yices2/issues/162>.
38//!
39//!
40//!
41//! # Very basic example
42//!
43//! String types already implement `rsmt2`'s SMT-printing traits. It's not a scalable approach, but
44//! it's useful for testing and explaining things. Let's create a solver first.
45//!
46//! ```rust
47//! fn do_smt_stuff() -> ::rsmt2::SmtRes<()> {
48//! let parser = () ;
49//! use rsmt2::SmtConf ;
50//!
51//! let solver = SmtConf::z3().spawn(parser) ? ;
52//!
53//! // Alternatively
54//! use rsmt2::Solver ;
55//! let _solver = Solver::new(SmtConf::z3(), parser) ? ;
56//!
57//! // Another alternative, using the default configuration
58//! let _solver = Solver::default(parser) ? ;
59//! Ok(())
60//! }
61//! do_smt_stuff().unwrap()
62//! ```
63//!
64//! Notice that all three functions spawning a solver take a parser used to parse identifiers,
65//! values and/or expressions. `rsmt2` parses everything else (keywords and such), and lets users
66//! handle the important parts. See the [`parse`][parse_mod] module documentation for more details.
67//!
68//! Our current parser `()` is enough for this example. We can even perform `check-sat`s since,
69//! unlike `get-model` for instance, it does not require any user-data-structure-specific parsing.
70//! Let's declare a few symbols and perform a check-sat.
71//!
72//! ```rust
73//! # fn do_smt_stuff() -> ::rsmt2::SmtRes<()> {
74//! use rsmt2::Solver ;
75//! let mut solver = Solver::default(()) ? ;
76//!
77//! solver.declare_const("n", "Int") ? ;
78//! // ^^^^^^^^^^^^^~~~ same as `declare-fun` for a nullary symbol
79//! solver.declare_const("m", "Int") ? ;
80//! solver.assert("(= (+ (* n n) (* m m)) 7)") ? ;
81//!
82//! let is_sat = solver.check_sat() ? ;
83//! assert! { ! is_sat }
84//! # Ok(())
85//! # }
86//! # do_smt_stuff().unwrap()
87//! ```
88//!
89//! We already knew there's no pair of integers the sum of the squares of which is equal to `7`, but
90//! now we **proved** it.
91//!
92//!
93//!
94//!
95//! # Parsing things
96//!
97//! If we want to be able to retrieve models, we need a parser that can parse two things:
98//! identifiers, types and values. That is, we need a parser that implements
99//! [`IdentParser`][ident_parser] (identifiers and types) and [`ModelParser`][model_parser]
100//! (values). The previous parser `()` doesn't, so `solver.get_model()` won't even compile.
101//!
102//! There's different ways to implement these traits, discussed in the [`parse`][parse_mod] module
103//! documentation. Let us be lazy and just have rsmt2 do the work for us. Note the (unnecessary) use
104//! of `define_fun`.
105//!
106//! ```rust
107//! use rsmt2::{ Solver, SmtRes } ;
108//! use rsmt2::parse::{ IdentParser, ModelParser } ;
109//!
110//! #[derive(Clone, Copy)]
111//! struct Parser ;
112//!
113//! // Types ~~~~~~~~~~~~vvvvvv
114//! impl<'a> IdentParser<String, String, & 'a str> for Parser {
115//! // Idents ~~~~~~~~~^^^^^^ ^^^^^^^^~~~~ Input
116//! fn parse_ident(self, input: & 'a str) -> SmtRes<String> {
117//! Ok(input.into())
118//! }
119//! fn parse_type(self, input: & 'a str) -> SmtRes<String> {
120//! Ok(input.into())
121//! }
122//! }
123//!
124//! // Types ~~~~~~~~~~~~~~~~~~vvvvvv vvvvvv~~~~~~~~~~~~~~ Values
125//! impl<'a> ModelParser<String, String, String, & 'a str> for Parser {
126//! // Idents ~~~~~~~~~^^^^^^ ^^^^^^^^~~~~ Input
127//! fn parse_value(
128//! self, input: & 'a str,
129//! ident: & String, params: & [ (String, String) ], typ: & String,
130//! ) -> SmtRes<String> {
131//! Ok(input.into())
132//! }
133//! }
134//!
135//! # fn do_smt_stuff() -> ::rsmt2::SmtRes<()> {
136//! let mut solver = Solver::default(Parser) ? ;
137//!
138//! solver.define_fun(
139//! "sq", & [ ("n", "Int") ], "Int", "(* n n)"
140//! // fn sq (n: Int) -> Int { n * n }
141//! ) ? ;
142//! solver.declare_const("n", "Int") ? ;
143//! solver.declare_const("m", "Int") ? ;
144//!
145//! solver.assert("(= (+ (sq n) (sq m)) 29)") ? ;
146//! solver.assert("(and (< n 5) (> n 0) (> m 0))") ? ;
147//!
148//! let is_sat = solver.check_sat() ? ;
149//! assert! { is_sat }
150//! let mut model = solver.get_model() ? ;
151//! model.sort() ; // Order might vary, sorting for assert below.
152//! assert_eq! {
153//! model,
154//! vec![
155//! ("m".into(), vec![], "Int".into(), "5".into()),
156//! ("n".into(), vec![], "Int".into(), "2".into()),
157//! ]
158//! }
159//! # Ok(())
160//! # }
161//! # do_smt_stuff().unwrap()
162//! ```
163//!
164//!
165//!
166//!
167//!
168//! # Asynchronous check-sats.
169//!
170//! The check-sat command above is blocking, in that the caller cannot do anything until the backend
171//! solver answers. Using the `print_check_sat...` and `parse_check_sat...` functions, users can
172//! issue the check-sat command, work on something else, and get the result later on.
173//!
174//! The `print_check_sat...` functions return a [`FutureCheckSat`][future] required by the
175//! `parse_check_sat...` functions to guarantee statically that the parse request makes sense.
176//! `FutureCheckSat` is equivalent to unit and exists only at compile time.
177//!
178//! Rewriting the previous example in an asynchronous fashion yields (omitting most of the
179//! unmodified code):
180//!
181//! ```rust
182//! # use rsmt2::{ Solver, SmtRes } ;
183//! # use rsmt2::parse::{ IdentParser, ValueParser } ;
184//! # #[derive(Clone, Copy)]
185//! # struct Parser ;
186//! # impl<'a> IdentParser<String, String, & 'a str> for Parser {
187//! # fn parse_ident(self, input: & 'a str) -> SmtRes<String> {
188//! # Ok(input.into())
189//! # }
190//! # fn parse_type(self, input: & 'a str) -> SmtRes<String> {
191//! # Ok(input.into())
192//! # }
193//! # }
194//! # impl<'a> ValueParser<String, & 'a str> for Parser {
195//! # fn parse_value(self, input: & 'a str) -> SmtRes<String> {
196//! # Ok(input.into())
197//! # }
198//! # }
199//! # fn do_smt_stuff() -> ::rsmt2::SmtRes<()> {
200//! # let mut solver = Solver::default(Parser) ? ;
201//! solver.define_fun(
202//! "sq", & [ ("n", "Int") ], "Int", "(* n n)"
203//! ) ? ;
204//! solver.declare_const("n", "Int") ? ;
205//! solver.declare_const("m", "Int") ? ;
206//!
207//! solver.assert("(= (+ (sq n) (sq m)) 29)") ? ;
208//! solver.assert("(and (< n 5) (> n 0) (> m 0))") ? ;
209//!
210//! let my_check_sat = solver.print_check_sat() ? ;
211//! // Solver is working, we can do other things.
212//! let is_sat = solver.parse_check_sat(my_check_sat) ? ;
213//! assert! { is_sat }
214//! # Ok(())
215//! # }
216//! # do_smt_stuff().unwrap()
217//! ```
218//!
219//!
220//!
221//! # Other SMT-LIB 2 commands
222//!
223//! Refer to [`Solver`][solver]'s documentation for the complete list of SMT-LIB 2 commands.
224//!
225//!
226//!
227//!
228//!
229//!
230//! # Activation literals
231//!
232//! Module [`actlit`][actlit_mod] discusses rsmt2's API for *activation literals*, a alternative to
233//! [`push`][push]/[`pop`][pop] that's more limited but more efficient.
234//!
235//!
236//!
237//!
238//!
239//!
240//!
241//!
242//!
243//!
244//! # Custom data structures
245//!
246//! Module [`example::simple`][simple_mod]'s documentation discusses in detail how to use rsmt2 with
247//! a custom data structure. This includes implementing the [print traits][print_mod] and writing a
248//! more evolved parser.
249//!
250//!
251//!
252//!
253//!
254//!
255//!
256//!
257//! # Print-time information
258//!
259//! Module [`example::print_time`][print_time_mod]'s documentation showcases print-time information.
260//! Proper documentation is somewhat lacking as it is a rather advanced topic, and no one asked for
261//! more details about it.
262//!
263//! Print-time information is the reason for
264//!
265//! - the `Info` type parameter in the [`...2Smt`][print_mod] traits,
266//! - all the `..._with` solver functions, such as `assert_with`.
267//!
268//! Users can call these functions to pass information down to their own printers as commands are
269//! written on the solver's input. The typical use-case is *print-time unrolling* when working with
270//! transition systems. Given a transition relation `T` over a current and next state `s[0]` and
271//! `s[1]`, *unrolling* consists in creating a sequence of states `s[0]`, `s[1]`, `s[2]`, ... such
272//! that `T(s[0], s[1]) and T(s[1], s[2]) and ...`. Such a sequence is called a *trace*.
273//!
274//! Say the state `s` is some variables `(x, y)` and the transition relation is `T(s[0], s[1]) =
275//! (x[1] == x[0] + 1) && (y[1] == 2 * y[0])`. Then in SMT-LIB, unrolling `T` twice looks like
276//!
277//! ```lisp
278//! (define-fun trans ( (x_0 Int) (y_0 Int) (x_1 Int) (y_1 Int) ) Bool
279//! (and (= x_1 (+ x_0 1)) (= y_1 (* 2 y_0)) )
280//! )
281//!
282//! (declare-fun x_0 () Int)
283//! (declare-fun y_0 () Int)
284//!
285//! (declare-fun x_1 () Int)
286//! (declare-fun y_1 () Int)
287//!
288//! (assert (trans x_0 y_0 x_1 y_1))
289//!
290//! (declare-fun x_2 () Int)
291//! (declare-fun y_2 () Int)
292//!
293//! (assert (trans x_1 y_1 x_2 y_2))
294//! ```
295//!
296//! In a model-checker, at each point of the unrolling one wants to (conditionally) assert terms
297//! about a state, or a pair of succeeding states, but never more. Also, the "same" term will
298//! typically be asserted for many different states / pair of states.
299//!
300//! Notice that if we want to assert `P(s) = x > 0` for `s[0]` and `s[1]`, then in theory we have to
301//! create two terms `x_0 > 0` and `x_1 > 0`. By extension, these are called *unrollings* of `P`.
302//! Now, these terms can end up being pretty big, and memory can become a problem, even with
303//! [hashconsing][hashconsing].
304//!
305//! Creating a different term for each unrolling of `P`, asserting it, and then (usually) discarding
306//! them right away is not practical time- and memory-wise. It is better if the term structure has a
307//! notion of "current `x`" (`x_0`) and "next `x`" (`x_1`), and to decide how to print them *at
308//! print-time* by passing an *offset* that's essentially an integer. It represents the offset for
309//! the "current" state.
310//!
311//! So, from the term `x_1 > x_0` for instance, passing an offset of `3` to the printer would cause
312//! `x_0` to be printed as `x_3` and `x_1` as `x_4`. Without creating anything, just from the
313//! original term.
314//!
315//! This is the workflow showcased (but only partially explained) by
316//! [`example::print_time`][print_time_mod].
317//!
318//!
319//!
320//!
321//!
322//!
323//! [rsmt2]: https://github.com/kino-mc/rsmt2 (rsmt2 github repository)
324//! [z3]: https://github.com/Z3Prover/z3 (z3 github repository)
325//! [cvc4]: https://cvc4.github.io/ (cvc4 github pages)
326//! [yices 2]: https://yices.csl.sri.com/ (yices 2 official page)
327//! [SmtConf]: struct.SmtConf.html (SmtConf type)
328//! [changes]: https://github.com/kino-mc/rsmt2/blob/master/CHANGES.md (List of changes on github)
329//! [solver]: struct.Solver.html (Solver type)
330//! [push]: struct.Solver.html#method.push (Solver's push function)
331//! [pop]: struct.Solver.html#method.pop (Solver's pop function)
332//! [ident_parser]: parse/trait.IdentParser.html (IdentParser trait)
333//! [model_parser]: parse/trait.ModelParser.html (ValueParser trait)
334//! [parse_mod]: parse/index.html (parse module)
335//! [print_mod]: print/index.html (print module)
336//! [simple_mod]: example/simple/index.html (rsmt2 simple example)
337//! [print_time_mod]: example/print_time/index.html (rsmt2 complex example)
338//! [actlit_mod]: actlit/index.html (rsmt2 complex example)
339//! [hashconsing]: https://crates.io/crates/hashconsing (hashconsing crate on crates.io)
340//! [future]: future/struct.FutureCheckSat.html (FutureCheckSat struct)
341
342#[macro_use]
343extern crate error_chain;
344
345/// Errors.
346///
347/// Aggregates I/O errors and `rsmt2` specific errors.
348pub mod errors {
349 error_chain! {
350 types {
351 Error, ErrorKind, ResExt, SmtRes ;
352 }
353
354 foreign_links {
355 Io(::std::io::Error) #[doc = "IO error."] ;
356 }
357
358 errors {
359 #[doc = "The solver reported `unknown`."]
360 Unknown {
361 description("smt solver reported `unknown`")
362 }
363 #[doc = "The solver reported `timeout`."]
364 Timeout {
365 description("smt solver reported `timeout`")
366 }
367 #[doc = "The solver reported `unsupported`."]
368 Unsupported {
369 description("unsupported command")
370 }
371
372 #[doc = "IO error."]
373 IoError(s: String) {
374 description("input/output error")
375 display("IO error: \"{}\"", s)
376 }
377
378 #[doc = "The solver reported an error."]
379 SolverError(s: String) {
380 description("solver error")
381 display("solver error: \"{}\"", s)
382 }
383
384 #[doc =
385 "Parse error, contains the s-expression on which the error happened"
386 ]
387 ParseError(msg: String, sexpr: String) {
388 description("parse error")
389 display("parse error: {} on `{}`", msg, sexpr)
390 }
391 }
392 }
393
394 impl ErrorKind {
395 /// True if the error is `Unknown`.
396 pub fn is_unknown(&self) -> bool {
397 if let ErrorKind::Unknown = *self {
398 true
399 } else {
400 false
401 }
402 }
403
404 /// True if the error is `Timeout`.
405 pub fn is_timeout(&self) -> bool {
406 if let ErrorKind::Timeout = *self {
407 true
408 } else {
409 false
410 }
411 }
412 }
413
414 impl Error {
415 /// Multi-line, pretty representation of a chain of errors.
416 pub fn to_ml_string(&self) -> String {
417 let mut s = String::new();
418 let mut pref = "";
419 for e in self.iter() {
420 let e_str = e.to_string();
421 let lines = e_str.lines();
422 let mut sub_pref = "- ";
423 for line in lines {
424 s.push_str(pref);
425 s.push_str(sub_pref);
426 s.push_str(line);
427 pref = "\n";
428 sub_pref = " ";
429 }
430 }
431 s
432 }
433 }
434}
435
436#[macro_use]
437mod common;
438pub mod actlit;
439mod conf;
440pub mod parse;
441mod solver;
442
443pub use crate::common::Logic;
444pub use crate::conf::{SmtConf, SmtStyle};
445pub use crate::errors::SmtRes;
446pub use crate::solver::Solver;
447
448/// Promises for future results on ongoing computations.
449pub mod future {
450 pub use crate::solver::FutureCheckSat;
451}
452
453pub mod example;
454
455/// Traits your types must implement so that `rsmt2` can use them.
456pub mod print {
457 pub use crate::common::{Expr2Smt, Sort2Smt, Sym2Smt};
458}