Mistral is an SMT solver which decides satisfiability of formulas in the theory of linear arithmetic over integers and theory of equality with uninterpreted functions. Mistral is written in C++ and can be used both through a graphical user interface as well as a C++ library. In addition to deciding satisfiability, Mistral consists of the following modules:
You can obtain the source code of Mistral from http://www.cs.utexas.edu/~tdillig/mistral-1.1.tar.gz
Mistral has been tested to compile on Ubuntu 12.04. First, to compile Mistral, you need to have cmake installed on your system as well as a set of other required libraries. On a recent Ubuntu/Kubuntu system, the following command will install everything you need:
sudo apt-get install libc6-dev-i386 gettext gawk flex libmpfr-dev cmake \ kdelibs5-dev libgtkmm-2.4-dev libboost-thread-dev libboost-serialization-dev \ libglademm-2.4-dev graphviz doxygen g++ libgmp-dev build-essential flex bison \ binutils-gold
Once you have cmake installed, go to the mistral folder and type the following commands
mkdir build cd build cmake .. make
Once you type these commands, you can use Mistral either through a graphical user interface or as a library. For using Mistral from the GUI, type the following commands:
cd ui ../build/ui/mistral_ui
You can only start the Mistral GUI from the /mistral/ui folder
If you want to use Mistral as a library, see the Section Getting Started.
Logical formulas are represented in Mistral using the Constraint type. For example, you can construct boolean constants true and false in the following way:
Constraint t(true); Constraint f(false);
Here "t" represents the boolean constant "true" and "f" represents the boolean constant false. More complicated formulas are constructed using Term. Terms can be ConstantTerm (such as 0), VariableTerm (such as x), ArithmeticTerm (such as 3x + 2y), or FunctionTerm (such as f(g(x, 0), a)). Here is an example illustrating creation of various kinds of terms.
Term* t1 = VariableTerm::make("a"); Term* t2 = VariableTerm::make("b"); map<Term*, long int> elems; elems[t1] = 3; elems[t2] = 7; Term* t3 = ArithmeticTerm::make(elems, 2); vector<Term*> args; args.push_back(t1); Term* t4 = FunctionTerm::make("f", args);
Here, term t3 represents the arithmetic term 3a + 7b + 2, and term t4 represents the function term f(a).
Now, using these terms, we can create more interesting constraints. For example, the following code snippet shows how to create the constraint f(a) <= b & 3a + 7b + 2 = 4:
Constraint c1(t4, t2, ATOM_LEQ); Constraint c2(t3, ConstantTerm::make(4), ATOM_EQ); Constraint c3 = c1 & c2;
In this example, c1 corresponds to the formula f(a) <= b, c2 represents the formula 3a + 7b + 2 =4, and c3 represents the conjunction of c1 and c2. Mistral overloads the C++ operators &, |, ! for performing conjunction, disjunction, and negation of formulas respectively. For example, in the following code snippet:
Constraint c4 = !c1; Constraint c5 = c4 | c2;
c4 represents the formula f(a) > b and c5 represents the disjunction of f(a) > b and 3a + 7b + 2 = 4.
Now that we can construct constraints, we can use Mistral to decide their satisfiability and validity:
bool res1 = c5.sat_discard(); bool res2 = c3.valid_discard(); bool res3 = c5.equivalent(c3);
Here res1 is true if and only if the formula represented by c5 is satisfiable, and res2 is true if and only if the formula represented by c3 is valid. The equivalent method of Constraint is used to check equivalence. Therefore, res3 is true if and only if c2 and c5 are equivalent.
There is also another way to check satisfiability and validity in Mistral using the sat() and valid() methods rather than sat_discard() and valid_discard(). The difference between these is that sat() and valid() also simplify the formula, as described in this publication . Therefore, the methods sat and valid are more expensive than sat_discard and valid_discard and should only be used if you want the formula to get simplified after performing a satisfiability or validity query. The section Simplify describes this in more detail.
Given a satisfiable constraint, Mistral also provides a way for obtaining satisfying assignments as follows:
map<Term*, SatValue> assignment; bool res = c5.get_assignment(assignment); for(auto it = assignment.begin(); it!= assignment.end(); it++) { Term* t = it->first; SatValue sv = it->second; cout << " Term: " << t->to_string() << " satisfying assignment: " << sv.to_string() << endl; }
The code snippet above shows how to obtain and print the satisfying assignment for formula represented by c5. In this code snippet, res indicates whether c5 is satisfiable, and, if res is true, "assignment" is a full satisfying assignment from each term in the formula to a satisfying value.
In addition to checking satisfiability and validity, Mistral can be used for performing abductive inference, simplifying constraints, performing quantifier elimination, and computing minimum satisfying assignments. For tutorials on using these functionalities, please refer to the Explain, Simplify, Cooper, and MSAFinder pages.
Mistral is developed and maintained by:
Other people who have contributed to some of the ideas implemented in Mistral include:
The techniques described in the following publications are incorporated in Mistral:
We are grateful to the developers of the MiniSAT SAT solver, which forms the SAT solving engine of Mistral. We also thank the developers of the GNU MP Bignum Library .
Mistral is freely available for research purposes under the GPL license .