1 /********************************************************************
2 * AUTHORS: Vijay Ganesh
3 *
4 * BEGIN DATE: November, 2005
5 *
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
12
13 The above copyright notice and this permission notice shall be included in
14 all copies or substantial portions of the Software.
15
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 THE SOFTWARE.
23 ********************************************************************/
24 #include "stp/ToSat/ASTNode/ToSAT.h"
25 #include "stp/Printer/printers.h"
26 #include "stp/STPManager/UserDefinedFlags.h"
27 #include "stp/ToSat/ASTNode/ASTtoCNF.h"
28 #include "stp/ToSat/ASTNode/BBNodeManagerASTNode.h"
29 #include "stp/ToSat/ASTNode/ClauseList.h"
30 #include "stp/ToSat/BitBlaster.h"
31 #include <fstream>
32 #include <iostream>
33
34 namespace stp
35 {
36 using std::endl;
37
isTseitinVariable(const ASTNode & n)38 bool isTseitinVariable(const ASTNode& n)
39 {
40 if (n.GetKind() == SYMBOL && n.GetType() == BOOLEAN_TYPE)
41 {
42 const char* zz = n.GetName();
43 if (0 == strncmp("cnf", zz, 3))
44 {
45 return true;
46 }
47 }
48 return false;
49 }
50
51 /* FUNCTION: lookup or create a new MINISAT literal
52 * lookup or create new MINISAT Vars from the global MAP
53 * _ASTNode_to_SATVar.
54 */
55
LookupOrCreateSATVar(SATSolver & newSolver,const ASTNode & n)56 uint32_t ToSAT::LookupOrCreateSATVar(SATSolver& newSolver, const ASTNode& n)
57 {
58 // look for the symbol in the global map from ASTNodes to ints. if
59 // not found, create a S.newVar(), else use the existing one.
60 ASTtoSATMap::iterator it = _ASTNode_to_SATVar_Map.find(n);
61 if (it != _ASTNode_to_SATVar_Map.end())
62 return it->second;
63
64 //We have to create it.
65 const uint32_t v = newSolver.newVar();
66 _ASTNode_to_SATVar_Map[n] = v;
67
68 // WARNING ASSUMPTION: I am assuming that the newSolver.newVar() call increments v
69 // by 1 each time it is called, and the initial value of a
70 // uint32_t is 0.
71
72 // Copies the symbol into the map that is used to build the counter example.
73 // For boolean we create a vector of size 1.
74 if ((n.GetKind() == BOOLEXTRACT && n[0].GetKind() == SYMBOL) ||
75 (n.GetKind() == SYMBOL && !isTseitinVariable(n)))
76 {
77 const ASTNode& symbol = n.GetKind() == BOOLEXTRACT ? n[0] : n;
78 const unsigned index =
79 n.GetKind() == BOOLEXTRACT ? n[1].GetUnsignedConst() : 0;
80 const unsigned width =
81 n.GetKind() == BOOLEXTRACT ? symbol.GetValueWidth() : 1;
82
83 if (SATVar_to_SymbolIndex.find(symbol) == SATVar_to_SymbolIndex.end())
84 {
85 // In the SAT solver these are signed...
86 vector<unsigned> vec(width, ~((unsigned)0));
87 SATVar_to_SymbolIndex.insert(make_pair(symbol, vec));
88 }
89 assert(index < width);
90 assert(SATVar_to_SymbolIndex[symbol].size() > index);
91
92 SATVar_to_SymbolIndex[symbol][index] = v;
93 return v;
94 }
95
96 assert(false);
97 exit(-1);
98 }
99
100 /* Convert ASTClauses to CNF clauses and solve.
101 * Accepts ASTClauses and converts them to CNF clauses. Then
102 * adds the newly minted CNF clauses to the local SAT instance,
103 * and calls solve(). If solve returns unsat, then stop and return
104 * unsat. else continue.
105 */
toSATandSolve(SATSolver & newSolver,ClauseList & cll,bool final,ASTtoCNF * & cm)106 bool ToSAT::toSATandSolve(SATSolver& newSolver, ClauseList& cll, bool final,
107 ASTtoCNF*& cm)
108 {
109 CountersAndStats("SAT Solver", bm);
110 bm->GetRunTimes()->start(RunTimes::SendingToSAT);
111
112 if (cll.size() == 0)
113 {
114 FatalError("toSATandSolve: Nothing to Solve", ASTUndefined);
115 }
116
117 ClauseContainer& cc = *cll.asList();
118
119 if (bm->UserFlags.output_CNF_flag && true)
120 {
121 dump_to_cnf_file(newSolver, cll, &cc);
122 }
123
124 bool ret = fill_satsolver_with_clauses(cc, newSolver);
125 cll.deleteJustVectors();
126 if (!ret)
127 return false;
128
129 // Remove references to Tseitin variables.
130 // Delete the cnf generator.
131 if (final)
132 {
133 ASTVec toDelete;
134
135 ASTtoSATMap::const_iterator it = _ASTNode_to_SATVar_Map.begin();
136 for (; it != _ASTNode_to_SATVar_Map.end(); it++)
137 {
138 ASTNode n = it->first;
139 if (!n.IsNull() && isTseitinVariable(n))
140 toDelete.push_back(n);
141 }
142
143 for (ASTVec::iterator it = toDelete.begin(); it != toDelete.end(); it++)
144 _ASTNode_to_SATVar_Map.erase(*it);
145
146 delete cm;
147 cm = NULL;
148 }
149
150 bm->GetRunTimes()->stop(RunTimes::SendingToSAT);
151 bm->GetRunTimes()->start(RunTimes::Solving);
152 newSolver.solve(bm->soft_timeout_expired);
153 bm->GetRunTimes()->stop(RunTimes::Solving);
154 if (bm->UserFlags.stats_flag)
155 newSolver.printStats();
156 if (newSolver.okay())
157 return true;
158 else
159 return false;
160 }
161
fill_satsolver_with_clauses(ClauseContainer & cc,SATSolver & newSolver)162 bool ToSAT::fill_satsolver_with_clauses(ClauseContainer& cc,
163 SATSolver& newSolver)
164 {
165 // Clause for the SATSolver
166 SATSolver::vec_literals satSolverClause;
167
168 // iterate through the list (conjunction) of ASTclauses cll
169 for (ClauseContainer::const_iterator i = cc.begin(), iend = cc.end();
170 i != iend; i++)
171 {
172 satSolverClause.clear();
173 for (vector<const ASTNode*>::const_iterator j = (*i)->begin(),
174 jend = (*i)->end();
175 j != jend; j++)
176 {
177 ASTNode node = **j;
178 bool negate = (NOT == node.GetKind()) ? true : false;
179 ASTNode n = negate ? node[0] : node;
180 uint32_t v = LookupOrCreateSATVar(newSolver, n);
181 Minisat::Lit l = SATSolver::mkLit(v, negate);
182 satSolverClause.push(l);
183 }
184
185 newSolver.addClause(satSolverClause);
186
187 if (!newSolver.okay())
188 {
189 if (bm->UserFlags.stats_flag)
190 newSolver.printStats();
191 bm->GetRunTimes()->stop(RunTimes::SendingToSAT);
192
193 return false;
194 }
195 }
196
197 return true;
198 }
199
dump_to_cnf_file(const SATSolver & newSolver,const ClauseList & cll,const ClauseContainer * cc)200 void ToSAT::dump_to_cnf_file(const SATSolver& newSolver, const ClauseList& cll,
201 const ClauseContainer* cc)
202 {
203 // output a CNF
204
205 // Because we use the SAT solver incrementally, this may ouput little pieces
206 // of the CNF that need to be joined together. Nicer would be to read it out
207 // of the solver each time.
208 std::ofstream file;
209 std::stringstream fileName;
210 fileName << "output_" << CNFFileNameCounter++ << ".cnf";
211 file.open(fileName.str().c_str());
212
213 file << "p cnf " << newSolver.nVars() << " " << cll.size() << endl;
214 for (ClauseContainer::const_iterator i = cc->begin(), iend = cc->end();
215 i != iend; i++)
216 {
217 vector<const ASTNode*>::iterator j = (*i)->begin(), jend = (*i)->end();
218 for (; j != jend; j++)
219 {
220 const ASTNode& node = *(*j);
221 bool negate = (NOT == node.GetKind()) ? true : false;
222 ASTNode n = negate ? node[0] : node;
223
224 ASTtoSATMap::iterator it = _ASTNode_to_SATVar_Map.find(n);
225 assert(it != _ASTNode_to_SATVar_Map.end());
226
227 uint32_t v = it->second;
228
229 if (negate)
230 file << "-" << (v + 1) << " ";
231 else
232 file << (v + 1) << " ";
233 }
234 file << "0" << endl;
235 }
236 file.close();
237 }
238
239 // Bucketize clauses into buckets of size 1,2,...CLAUSAL_BUCKET_LIMIT
Sort_ClauseList_IntoBuckets(ClauseList * cl,int clause_bucket_size)240 ClauseBuckets* ToSAT::Sort_ClauseList_IntoBuckets(ClauseList* cl,
241 int clause_bucket_size)
242 {
243 ClauseBuckets* cb = new ClauseBuckets;
244 ClauseContainer* cc = cl->asList();
245
246 // Sort the clauses, and bucketize by the size of the clauses
247 for (ClauseContainer::iterator it = cc->begin(), itend = cc->end();
248 it != itend; it++)
249 {
250 ClausePtr cptr = *it;
251 int cl_size = cptr->size();
252 if (cl_size >= clause_bucket_size)
253 {
254 cl_size = clause_bucket_size;
255 }
256
257 // If no clauses of size cl_size have been seen, then create a
258 // bucket for that size
259 if (cb->find(cl_size) == cb->end())
260 {
261 ClauseList* cllist = new ClauseList();
262 cllist->push_back(cptr);
263 (*cb)[cl_size] = cllist;
264 }
265 else
266 {
267 ClauseList* cllist = (*cb)[cl_size];
268 cllist->push_back(cptr);
269 }
270 }
271
272 return cb;
273 }
274
CallSAT_On_ClauseBuckets(SATSolver & SatSolver,ClauseBuckets * cb,ASTtoCNF * & cm)275 bool ToSAT::CallSAT_On_ClauseBuckets(SATSolver& SatSolver, ClauseBuckets* cb,
276 ASTtoCNF*& cm)
277 {
278 ClauseBuckets::iterator it = cb->begin();
279 ClauseBuckets::iterator itend = cb->end();
280
281 bool sat = false;
282 for (size_t count = 1; it != itend; it++, count++)
283 {
284 ClauseList* cl = (*it).second;
285 sat = toSATandSolve(SatSolver, *cl, count == cb->size(), cm);
286
287 if (!sat)
288 {
289 return sat;
290 }
291 }
292 return sat;
293 }
294
295 // Call the SAT solver, and check the result before returning. This
296 // can return one of 3 values, SOLVER_VALID, SOLVER_INVALID or
297 // SOLVER_UNDECIDED
CallSAT(SATSolver & SatSolver,const ASTNode & input,bool)298 bool ToSAT::CallSAT(SATSolver& SatSolver, const ASTNode& input,
299 bool /*refinement*/)
300 {
301 bm->GetRunTimes()->start(RunTimes::BitBlasting);
302
303 ASTNode BBFormula;
304 {
305 BBNodeManagerASTNode nm(bm);
306 Simplifier simp(bm);
307 BitBlaster<ASTNode, BBNodeManagerASTNode> BB(
308 &nm, &simp, bm->defaultNodeFactory, &bm->UserFlags);
309 BBFormula = BB.BBForm(input);
310 }
311
312 bm->ASTNodeStats("after bitblasting: ", BBFormula);
313 bm->GetRunTimes()->stop(RunTimes::BitBlasting);
314
315 if (bm->UserFlags.output_bench_flag)
316 {
317 std::ofstream file;
318 std::stringstream fileName;
319 fileName << "output_" << benchFileNameCounter++ << ".bench";
320 file.open(fileName.str().c_str());
321 printer::Bench_Print(file, BBFormula);
322 file.close();
323 }
324
325 // The ASTtoCNF is deleted inside the CallSAT_On_ClauseBuckets,
326 // just before the final call to the SAT solver.
327
328 ASTtoCNF* to_cnf = new ASTtoCNF(bm);
329 ClauseList* cl = to_cnf->convertToCNF(BBFormula);
330
331 ClauseBuckets* cl_buckets = Sort_ClauseList_IntoBuckets(cl, 3);
332 cl->asList()->clear(); // clause buckets now point to the clauses.
333 delete cl;
334
335 const bool sat = CallSAT_On_ClauseBuckets(SatSolver, cl_buckets, to_cnf);
336
337 delete cl_buckets;
338 if (NULL != to_cnf)
339 delete to_cnf;
340
341 return sat;
342 }
343
344 /*******************************************************************
345 * Helper Functions
346 *******************************************************************/
347
348 #if 0
349
350 // Looks up truth value of ASTNode SYMBOL in MINISAT satisfying
351 // assignment.
352 ASTNode ToSAT::SymbolTruthValue(SATSolver &newSolver, ASTNode form)
353 {
354 uint32_t satvar = _ASTNode_to_SATVar_Map[form];
355 if (newSolver.model[satvar] == SATSolver::l_False)
356 {
357 return ASTFalse;
358 }
359 else
360 {
361 // True or undefined.
362 return ASTTrue;
363 }
364 }
365
366 // This function is for debugging problems with BitBlast and
367 // especially ToCNF. It evaluates the bit-blasted formula in the
368 // satisfying assignment. While doing that, it checks that every
369 // subformula has the same truth value as its representative
370 // literal, if it has one. If this condition is violated, it halts
371 // immediately (on the leftmost lowest term).
372 ASTNode ToSAT::CheckBBandCNF(SATSolver& newSolver, ASTNode form)
373 {
374 // Clear memo table (in case newSolver has changed).
375 CheckBBandCNFMemo.clear();
376 // Call recursive version that does the work.
377 return CheckBBandCNF_int(newSolver, form);
378 } //End of CheckBBandCNF()
379
380 // Recursive body CheckBBandCNF
381 ASTNode ToSAT::CheckBBandCNF_int(SATSolver& newSolver, ASTNode form)
382 {
383 // cout << "++++++++++++++++"
384 // << endl
385 // << "CheckBBandCNF_int form = "
386 // << form << endl;
387
388 ASTNodeMap::iterator memoit = CheckBBandCNFMemo.find(form);
389 if (memoit != CheckBBandCNFMemo.end())
390 {
391 // found it. Return memoized value.
392 return memoit->second;
393 }
394
395 ASTNode result; // return value, to memoize.
396
397 Kind k = form.GetKind();
398 switch (k)
399 {
400 case TRUE:
401 case FALSE:
402 {
403 return form;
404 break;
405 }
406 case SYMBOL:
407 case BOOLEXTRACT:
408 {
409 result = SymbolTruthValue(newSolver, form);
410
411 // cout << "================"
412 // << endl
413 // << "Checking BB formula:"
414 // << form << endl;
415 // cout << "----------------"
416 // << endl
417 // << "Result:" << result << endl;
418 break;
419 }
420 default:
421 {
422 // Evaluate the children recursively.
423 ASTVec eval_children;
424 ASTVec ch = form.GetChildren();
425 ASTVec::iterator itend = ch.end();
426 for (ASTVec::iterator it = ch.begin(); it < itend; it++)
427 {
428 eval_children.push_back(CheckBBandCNF_int(newSolver, *it));
429 }
430 result = bm->CreateNode(k, eval_children);
431
432 // cout << "================"
433 // << endl
434 // << "Checking BB formula:" << form << endl;
435 // cout << "----------------"
436 // << endl
437 // << "Result:" << result << endl;
438
439 ASTNode replit_eval;
440 // Compare with replit, if there is one.
441 ASTNodeMap::iterator replit_it = RepLitMap.find(form);
442 if (replit_it != RepLitMap.end())
443 {
444 ASTNode replit = RepLitMap[form];
445 // Replit is symbol or not symbol.
446 if (SYMBOL == replit.GetKind())
447 {
448 replit_eval = SymbolTruthValue(newSolver, replit);
449 }
450 else
451 {
452 // It's (NOT sym). Get value of sym and complement.
453 replit_eval =
454 bm->CreateNode(NOT,SymbolTruthValue(newSolver, replit[0]));
455 }
456
457 // cout << "----------------"
458 // << endl
459 // << "Rep lit: " << replit << endl;
460 // cout << "----------------"
461 // << endl
462 // << "Rep lit value: " << replit_eval << endl;
463
464 if (result != replit_eval)
465 {
466 // Hit the panic button.
467 FatalError("Truth value of BitBlasted formula disagrees with representative literal in CNF.");
468 }
469 }
470 else
471 {
472 //cout << "----------------" << endl << "No rep lit" << endl;
473 }
474
475 }
476 }
477
478 return (CheckBBandCNFMemo[form] = result);
479 }
480 #endif
481
482 } // end of namespace stp
483