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