1 
2 /*
3  * File SMTFormula.cpp.
4  *
5  * This file is part of the source code of the software program
6  * Vampire. It is protected by applicable
7  * copyright laws.
8  *
9  * This source code is distributed under the licence found here
10  * https://vprover.github.io/license.html
11  * and in the source directory
12  *
13  * In summary, you are allowed to use Vampire for non-commercial
14  * purposes but not allowed to distribute, modify, copy, create derivatives,
15  * or use in competitions.
16  * For other uses of Vampire please contact developers for a different
17  * licence, which we will make an effort to provide.
18  */
19 /**
20  * @file SMTFormula.cpp
21  * Implements class SMTFormula.
22  */
23 
24 #include <ostream>
25 #include <sstream>
26 
27 #include "Lib/Int.hpp"
28 #include "Lib/Metaiterators.hpp"
29 #include "Lib/Sort.hpp"
30 #include "Lib/System.hpp"
31 
32 #include "SMTFormula.hpp"
33 
34 namespace Shell
35 {
36 
unsignedValue(unsigned val)37 SMTConstant SMTFormula::unsignedValue(unsigned val)
38 {
39   CALL("SMTFormula::unsignedValue");
40 
41   return SMTConstant(Int::toString(val)+".0");
42 }
43 
name(vstring name)44 SMTConstant SMTFormula::name(vstring name)
45 {
46   CALL("SMTFormula::name/1");
47 
48   return SMTConstant(name);
49 }
50 
name(vstring n1,vstring n2)51 SMTConstant SMTFormula::name(vstring n1, vstring n2)
52 {
53   CALL("SMTFormula::name/2");
54 
55   return SMTConstant(n1 + "_" + n2);
56 }
57 
58 
condNumber(SMTFormula condition,unsigned value)59 SMTFormula SMTFormula::condNumber(SMTFormula condition, unsigned value)
60 {
61   CALL("SMTFormula::condNumber");
62 
63   return SMTFormula("(ite " + condition._val + " " + unsignedValue(value)._val + " 0.0)");
64 }
65 
66 
conjunction(const SMTFormula & f1,const SMTFormula & f2)67 SMTFormula SMTFormula::conjunction(const SMTFormula& f1, const SMTFormula& f2)
68 {
69   CALL("SMTFormula::conjunction");
70 
71   if(f1.isTrue()) { return f2; }
72   if(f2.isTrue()) { return f1; }
73   if(f1.isFalse() || f2.isFalse()) { return getFalse(); }
74   return SMTFormula("(and " + f1._val + " " + f2._val + ")");
75 }
76 
disjunction(const SMTFormula & f1,const SMTFormula & f2)77 SMTFormula SMTFormula::disjunction(const SMTFormula& f1, const SMTFormula& f2)
78 {
79   CALL("SMTFormula::disjunction");
80 
81   if(f1.isFalse()) { return f2; }
82   if(f2.isFalse()) { return f1; }
83   if(f1.isTrue() || f2.isTrue()) { getTrue(); }
84   return SMTFormula("(or " + f1._val + " " + f2._val + ")");
85 }
86 
87 
88 
89 //helper operator implementation for pseudo-operators --> and -=-
90 
operator --(int) const91 SMTFormula__HalfImpl SMTFormula::operator--(int) const { return SMTFormula__HalfImpl(*this); }
92 
operator -() const93 SMTFormula__HalfEquiv SMTFormula::operator-() const { return SMTFormula__HalfEquiv(*this); }
operator -=(const SMTFormula__HalfEquiv & r) const94 SMTFormula SMTFormula::operator-=(const SMTFormula__HalfEquiv& r) const
95 { return SMTFormula("(= " + _val + " " + r.pf._val + ")"); }
96 
97 
98 
99 
100 ///////////////////////
101 // SMTBenchmark
102 //
103 
addFormula(const SMTFormula & f,vstring comment)104 void SMTBenchmark::addFormula(const SMTFormula& f, vstring comment)
105 {
106   CALL("SMTBenchmark::addFormula");
107   ASS_EQ(_formulas.size(), _formulaComments.size());
108 
109   _formulas.push(f);
110   _formulaComments.push(comment);
111 }
112 
113 /**
114  * Remove the most recently added formula
115  */
popFormula()116 void SMTBenchmark::popFormula()
117 {
118   CALL("SMTBenchmark::popFormula");
119   ASS_EQ(_formulas.size(), _formulaComments.size());
120 
121   _formulas.pop();
122   _formulaComments.pop();
123 }
124 
declarePropositionalConstant(SMTConstant pred)125 void SMTBenchmark::declarePropositionalConstant(SMTConstant pred)
126 {
127   CALL("SMTBenchmark::declarePropositionalConstant");
128   ASS(!_funDecls.find(pred.toString()));
129 
130   _predDecls.insert(pred.toString());
131 }
132 
declareRealConstant(SMTConstant pred)133 void SMTBenchmark::declareRealConstant(SMTConstant pred)
134 {
135   CALL("SMTBenchmark::declareRealConstant");
136   ASS(!_predDecls.find(pred.toString()));
137 
138   _funDecls.insert(pred.toString(), "Real");
139 }
140 
output(ostream & out)141 void SMTBenchmark::output(ostream& out)
142 {
143   CALL("SMTBenchmark::output");
144 
145   out << "(benchmark VampireGeneratedBenchmark" << endl;
146 
147   StringMap::Iterator fdeclIt(_funDecls);
148   while(fdeclIt.hasNext()) {
149     vstring func, fType;
150     fdeclIt.next(func, fType);
151     out << ":extrafuns ((" << func << " " << fType << "))" <<endl;
152   }
153 
154   static Stack<vstring> predDeclStack;
155   predDeclStack.reset();
156   predDeclStack.loadFromIterator(_predDecls.iterator());
157   sort<DefaultComparator>(predDeclStack.begin(), predDeclStack.end());
158 
159   Stack<vstring>::BottomFirstIterator pdeclIt(predDeclStack);
160   while(pdeclIt.hasNext()) {
161     vstring pred = pdeclIt.next();
162     out << ":extrapreds ((" << pred << "))" <<endl;
163   }
164 
165   out << ":formula ( (and " << endl;
166 
167   Stack<SMTFormula>::BottomFirstIterator fit(_formulas);
168   Stack<vstring>::BottomFirstIterator fCommentIt(_formulaComments);
169 
170   if(!fit.hasNext()) { out << "  true" << endl; }
171 
172   while(fit.hasNext()) {
173     ALWAYS(fCommentIt.hasNext());
174 
175     SMTFormula form = fit.next();
176     vstring comment = fCommentIt.next();
177     out << "  " << form.toString();
178     if(comment!="") {
179       out << " ; " << comment;
180     }
181     out << endl;
182   }
183 
184   out << ") )" << endl;
185   out << ")" << endl;
186 }
187 
188 ///////////////////////
189 // SMTSolver
190 //
191 
minimize(SMTBenchmark & problem,SMTConstant costFn,SMTSolverResult & res)192 SMTSolver::MinimizationResult SMTSolver::minimize(SMTBenchmark& problem, SMTConstant costFn, SMTSolverResult& res)
193 {
194   CALL("SMTSolver::minimize");
195 
196   bool approx = false;
197 
198   unsigned left = 1;
199   unsigned guess = left;
200   while(tryUpperBound(problem, costFn, guess, res)!=SMTSolverResult::SAT) {
201     approx |= res.status==SMTSolverResult::UNKNOWN;
202     unsigned newGuess = guess*2;
203     if(newGuess<=guess) {
204       //we had an overflow
205       cerr << "cost overflow durint SMT minimization" << endl;
206       return FAIL;
207     }
208     left = guess;
209     guess = newGuess;
210   }
211   unsigned right = guess;
212 
213   while(left!=right) {
214     unsigned middle = left + (right-left)/2;
215     if(tryUpperBound(problem, costFn, middle, res)==SMTSolverResult::SAT) {
216       right = middle;
217     }
218     else {
219       approx |= res.status==SMTSolverResult::UNKNOWN;
220       left = middle+1;
221     }
222   }
223   tryUpperBound(problem, costFn, left, res);
224   if(res.status==SMTSolverResult::UNKNOWN) {
225     //we don't even have a satisfying assignment
226     return FAIL;
227   }
228   return approx ? APPROXIMATE : EXACT;
229 }
230 
tryUpperBound(SMTBenchmark & problem,SMTConstant costFn,unsigned val,SMTSolverResult & res)231 SMTSolverResult::Status SMTSolver::tryUpperBound(SMTBenchmark& problem, SMTConstant costFn, unsigned val, SMTSolverResult& res)
232 {
233   CALL("SMTSolver::tryUpperBound");
234 
235   SMTFormula valFormula = SMTFormula::unsignedValue(val);
236   SMTFormula bound = SMTFormula::less(costFn, valFormula);
237 
238   problem.addFormula(bound);
239 
240   run(problem, res, 2);
241 
242   problem.popFormula();
243 
244   if(res.status==SMTSolverResult::UNKNOWN) {
245     cerr << "SMT solver gave \"unknown\" for cost value "<< val << "(possibly due to time limit)" << endl;
246   }
247 
248   return res.status;
249 }
250 
251 ///////////////////////
252 // YicesSolver
253 //
254 
run(SMTBenchmark & problem,SMTSolverResult & res,unsigned timeout)255 void YicesSolver::run(SMTBenchmark& problem, SMTSolverResult& res, unsigned timeout)
256 {
257   CALL("YicesSolver::run");
258 
259   vostringstream problemStm;
260   problem.output(problemStm);
261 
262   static Stack<vstring> proverOut;
263   proverOut.reset();
264 
265   vstring execName = System::guessExecutableDirectory()+"/yices";
266   if(!System::fileExists(execName)) {
267     USER_ERROR("Executable "+execName+" does not exist");
268   }
269 
270   vstring cmdLine = execName+" -smt -e"; // this works for yices1.0.xx
271   if(timeout!=0) {
272     cmdLine += " --timeout="+Int::toString(timeout);
273   }
274 
275   System::executeCommand(cmdLine, problemStm.str(), proverOut);
276 
277   res.reset();
278 
279   Stack<vstring>::Iterator oit(proverOut);
280   while(oit.hasNext()) {
281     vstring line = oit.next();
282     if(line=="") { continue; }
283     if(line=="sat") { res.status = SMTSolverResult::SAT; continue; }
284     if(line=="unsat") { res.status = SMTSolverResult::UNSAT; continue; }
285     if(line=="unknown") { res.status = SMTSolverResult::UNKNOWN; continue; }
286 
287     if(line.substr(0,3)!="(= " || line.substr(line.size()-1,1)!=")") {
288       continue;
289     }
290     vstring lineCore = line.substr(3, line.size()-4);
291     size_t sep = lineCore.find(' ');
292     vstring element = lineCore.substr(0, sep);
293     vstring value = lineCore.substr(sep+1);
294 
295     res.assignment.insert(element, value);
296   }
297 }
298 
299 
300 }
301