1 
2 /*
3  * File InterpolantMinimizer.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 InterpolantMinimizer.cpp
21  * Implements class InterpolantMinimizer.
22  */
23 
24 #include <fstream>
25 #include <sstream>
26 
27 #include "Lib/Environment.hpp"
28 
29 #include "Kernel/Clause.hpp"
30 #include "Kernel/Formula.hpp"
31 #include "Kernel/FormulaUnit.hpp"
32 #include "Kernel/Inference.hpp"
33 #include "Kernel/Renaming.hpp"
34 #include "Kernel/Term.hpp"
35 #include "Kernel/Problem.hpp"
36 #include "Kernel/Signature.hpp"
37 #include "Kernel/Connective.hpp"
38 #include "Kernel/MainLoop.hpp"
39 
40 #include "Indexing/ClauseVariantIndex.hpp"
41 
42 #include "Interpolants.hpp"
43 #include "Options.hpp"
44 
45 #include "Saturation/Splitter.hpp"
46 
47 #include "InterpolantMinimizer.hpp"
48 
49 
50 using namespace Shell;
51 using namespace Indexing;
52 
53 #define TRACE(x)
54 
55 /**
56  * Return minimized interpolant of @c refutation
57  * it traverses the proof and collects grey formulas.
58  * For each grey formula, it adds digest and fringe properties (with case distinction between leaves and non-leaves).
59  * Using these properties, the interpolant is minimized wrt its cost
60  */
getInterpolant(Unit * refutation)61 Formula* InterpolantMinimizer::getInterpolant(Unit* refutation)
62 {
63   CALL("InterpolantMinimizer::getInterpolant");
64 
65   traverse(refutation);
66   addAllFormulas();
67 
68   SMTSolverResult res;
69   YicesSolver solver;
70   YicesSolver::MinimizationResult mres = solver.minimize(_resBenchmark, costFunction(), res);
71 
72 
73   DHSet<Unit*> slicedOff;
74 
75   if(mres==SMTSolver::FAIL) {
76     cerr << "Minimization timed failed to find a satisfiable assignment, generating basic interpolant" << endl;
77     goto just_generate_interpolant;
78   }
79   if(mres==SMTSolver::APPROXIMATE) {
80     cerr << "Minimization gave approximate result" << endl;
81   }
82 
83   if(_showStats) {
84     env.beginOutput();
85     env.out() << _statsPrefix << " cost: " <<res.assignment.get(costFunction()) << endl;
86     env.endOutput();
87   }
88 
89   collectSlicedOffNodes(res, slicedOff);
90 
91 just_generate_interpolant:
92   Formula* interpolant = Interpolants(&slicedOff).getInterpolant(refutation);
93 
94   return interpolant;
95 }
96 
97 /*print formulas in SMT*/
98 
prettyPrint(Formula * formula,ostream & out)99 void InterpolantMinimizer::prettyPrint(Formula* formula, ostream& out)
100 {
101   CALL("SMTLibPrinter::prettyPrint");
102 
103   Signature *sig = env.signature;
104   unsigned symbNum;
105   Symbol* symb;
106   TermList* args;
107   FormulaList* fs;
108   switch (formula->connective()) {
109     case LITERAL:
110         symbNum = formula->literal()->functor();
111         symb = sig->getPredicate(symbNum);
112 
113         if(formula->literal()->args()->isNonEmpty())
114              out << "(";
115 
116         prettyPrint(symb, out);
117 
118         args = formula->literal()->args();
119         while(args->isNonEmpty()) {
120                 out << " ";
121                 if(args->isVar())
122                     out << "x" << args->var();
123                 else
124                     prettyPrint(args->term(), out);
125                 args = args->next();
126             }
127 
128         if(formula->literal()->args()->isNonEmpty())
129                 out << ")";
130 
131         return;
132     case AND:
133     case OR:
134         if(formula->connective() == AND)
135             out << "(and ";
136         else
137             out << "(or ";
138 
139         for(fs = formula->args(); FormulaList::isNonEmpty(fs); fs = fs->tail()) {
140             prettyPrint(fs->head(), out);
141             out << " ";
142         }
143 
144         out << ")";
145         return;
146     case IMP:
147     case IFF:
148     case XOR:
149         if(formula->connective() == IMP)
150                 out << "(implies ";
151         else if(formula->connective() == IFF)
152             out << "(= ";
153         else
154             ASS(false);
155 
156         prettyPrint(formula->left(), out);
157         out << " ";
158         prettyPrint(formula->right(), out);
159         out << ")";
160         return;
161     case NOT:
162         out << "(not ";
163         prettyPrint(formula->uarg(), out);
164         out << ")";
165         return;
166     case FORALL:
167         return;
168     case EXISTS:
169         return;
170     case BOOL_TERM:
171         out << formula->getBooleanTerm().toString();
172         return;
173     case TRUE:
174         out << "true";
175         return;
176     case FALSE:
177         out << "false";
178         return;
179     default:
180         out << "WARNING!!! This is not a literal\n";
181         ASS(false);
182         return;
183     }
184 }
185 
186 
187 
188 /*print terms in SMT*/
prettyPrint(Term * term,ostream & out)189 void InterpolantMinimizer::prettyPrint(Term* term, ostream& out)
190 {
191     Signature *sig = env.signature;
192     unsigned int symbNum = term->functor();
193     Symbol* symb = sig->getFunction(symbNum);
194 
195     if(term->args()->isNonEmpty())
196         out << "(";
197 
198     prettyPrint(symb, out);
199 
200     TermList* args = term->args();
201     while(args->isNonEmpty()) {
202         out << " ";
203         if(args->isVar())
204             out << "x" << args->var();
205         else
206             prettyPrint(args->term(), out);
207         args = args->next();
208     }
209 
210     if(term->args()->isNonEmpty())
211             out << ")";
212 }
213 
214 /*print symbols in SMT*/
215 //TODO: use builtin enumerator for builtin operators instead of this trick...
216 //unfortunately I only discovered about those later..
prettyPrint(Symbol * symb,ostream & out)217 void InterpolantMinimizer::prettyPrint(Symbol* symb, ostream& out)
218 {
219     vstring name = symb->name();
220     if(symb->interpreted()) {
221         if(name == "$less")
222         {out << "<";}
223         else if(name == "$lesseq")
224         {out << "<=";}
225         else if(name == "$greater")
226         {out << ">";}
227         else if(name == "$greatereq")
228         {out << ">=";}
229         else if(name == "=")
230         {out << "=";}
231         else if(name == "$plus")
232         {out << "+";}
233         else if(name == "$sum")
234         {out << "+";}
235         else if(name == "$times")
236         {out << "*";}
237         else if(name == "$product")
238         {out << "*";}
239         else if(name == "$uminus")
240         {out << "-";}
241         else
242         {out << name;} //TODO: handle negative number and print them as (- N)
243     }
244     else{out<<name;}
245 }
246 
247 
248 
249 
250 
251 /**
252  * Into @c acc add all units that are sliced off in the model given
253  * by SMT solver in @c solverResult.
254  */
collectSlicedOffNodes(SMTSolverResult & solverResult,DHSet<Unit * > & acc)255 void InterpolantMinimizer::collectSlicedOffNodes(SMTSolverResult& solverResult, DHSet<Unit*>& acc)
256 {
257   CALL("InterpolantMinimizer::collectSlicedOffNodes");
258 
259 
260   InfoMap::Iterator uit(_infos);
261   while(uit.hasNext()) {
262     Unit* unit;
263     UnitInfo info;
264     uit.next(unit, info);
265 
266     if(info.color!=COLOR_TRANSPARENT || !info.leadsToColor) {
267       continue;
268     }
269 
270     vstring uid = getUnitId(unit);
271 
272     SMTConstant sU = pred(S, uid);
273     vstring val = solverResult.assignment.get(sU);
274     if(val=="false") {
275       continue;
276     }
277     ASS_EQ(val,"true");
278     acc.insert(unit);
279   }
280 
281   TRACE(
282   WeightMap::Iterator wit(_atomWeights);
283   while(wit.hasNext()) {
284     vstring atom;
285     unsigned weight;
286     wit.next(atom, weight);
287 
288     Unit* unit = _unitsById.get(atom);
289 
290     SMTConstant vU = pred(V, atom);
291 
292     cout << "because " << solverResult.assignment.get(vU) << " for " << unit->toString() << endl;
293   })
294 }
295 
296 /**
297  * Add into @c _resBenchmark all formulas needed for interpolant minimization
298  */
addAllFormulas()299 void InterpolantMinimizer::addAllFormulas()
300 {
301   CALL("InterpolantMinimizer::addAllFormulas");
302 
303   InfoMap::Iterator uit(_infos);
304   while(uit.hasNext()) {
305     Unit* unit;
306     UnitInfo info;
307     uit.next(unit, info);
308 
309     if(info.color==COLOR_TRANSPARENT && info.leadsToColor) {
310       addNodeFormulas(unit);
311     }
312   }
313 
314   addCostFormula();
315 }
316 
317 /**
318  * Add into @c _resBenchmark formulas related to @c u and to it's relation to
319  * its parents.
320  */
addNodeFormulas(Unit * u)321 void InterpolantMinimizer::addNodeFormulas(Unit* u)
322 {
323   CALL("InterpolantMinimizer::addNodeFormulas");
324 
325   static ParentSummary psum;
326   psum.reset();
327 
328   UnitIterator pit = InferenceStore::instance()->getParents(u);
329   while(pit.hasNext()) {
330     Unit* par = pit.next();
331     UnitInfo& info = _infos.get(par);
332     if(!info.leadsToColor) {
333       continue;
334     }
335     vstring parId = getUnitId(par);
336     switch(info.color) {
337     case COLOR_LEFT: psum.rParents.push(parId); break;
338     case COLOR_RIGHT: psum.bParents.push(parId); break;
339     case COLOR_TRANSPARENT: psum.gParents.push(parId); break;
340     default: ASSERTION_VIOLATION;
341     }
342   }
343 
344   UnitInfo& uinfo = _infos.get(u);
345   ASS_EQ(uinfo.color, COLOR_TRANSPARENT);
346 
347   vstring uId = getUnitId(u);
348 
349   if(uinfo.inputInheritedColor!=COLOR_TRANSPARENT) {
350     //if unit has an inherited color, it must be input unit and therefore
351     //cannot have any parents
352     ASS(psum.rParents.isEmpty());
353     ASS(psum.bParents.isEmpty());
354     ASS(psum.gParents.isEmpty());
355     addLeafNodePropertiesFormula(uId);
356   }
357   else {
358     addNodeFormulas(uId, psum);
359     addFringeFormulas(u);
360   }
361 
362 
363 
364   if(_noSlicing || uinfo.isRefutation) {
365     vstring comment;
366     if(uinfo.isRefutation) {
367       comment += "refutation";
368     }
369     _resBenchmark.addFormula(!pred(S,uId), comment);
370   }
371 
372   //if formula is a parent of colored formula, we do not allow to have
373   //opposite color in the trace.
374   if(uinfo.isParentOfLeft) {
375     _resBenchmark.addFormula(!pred(B,uId), "parent_of_left");
376   }
377   if(uinfo.isParentOfRight) {
378     _resBenchmark.addFormula(!pred(R,uId), "parent_of_right");
379   }
380 
381   addAtomImplicationFormula(u);
382 }
383 
384 /**
385  * Add formulas related tot he fringe of the node and to the digest
386  *
387  * These formulas aren't generated for leaves
388  */
addFringeFormulas(Unit * u)389 void InterpolantMinimizer::addFringeFormulas(Unit* u)
390 {
391   CALL("InterpolantMinimizer::addFringeFormulas");
392 
393   vstring n = getUnitId(u);
394 
395   SMTFormula rcN = pred(RC, n);
396   SMTFormula bcN = pred(BC, n);
397   SMTFormula rfN = pred(RF, n);
398   SMTFormula bfN = pred(BF, n);
399   SMTFormula dN = pred(D, n);
400 
401   _resBenchmark.addFormula(dN -=- ((rcN & !rfN) | (bcN & !bfN)));
402 
403   UnitInfo& uinfo = _infos.get(u);
404 
405   if(uinfo.isRefutation) {
406     _resBenchmark.addFormula(!rfN);
407     _resBenchmark.addFormula(bfN);
408     return;
409   }
410 
411   SMTFormula rfRhs = SMTFormula::getTrue();
412   SMTFormula bfRhs = SMTFormula::getTrue();
413   UList::Iterator gsit(uinfo.transparentSuccessors);
414   while(gsit.hasNext()) {
415     Unit* succ = gsit.next();
416     vstring succId = getUnitId(succ);
417 
418     SMTFormula rcS = pred(RC, succId);
419     SMTFormula bcS = pred(BC, succId);
420     SMTFormula rfS = pred(RF, succId);
421     SMTFormula bfS = pred(BF, succId);
422 
423     rfRhs = rfRhs & ( rfS | rcS ) & !bcS;
424     bfRhs = bfRhs & ( bfS | bcS ) & !rcS;
425   }
426 
427 
428   if(uinfo.rightSuccessors) {
429     _resBenchmark.addFormula(!rfN);
430   }
431   else {
432     _resBenchmark.addFormula(rfN -=- rfRhs);
433   }
434 
435   if(uinfo.leftSuccessors) {
436     _resBenchmark.addFormula(!bfN);
437   }
438   else {
439     _resBenchmark.addFormula(bfN -=- bfRhs);
440   }
441 
442 }
443 
444 /////////////////////////////////////////////////////////
445 // Generating the weight-minimizing part of the problem
446 //
447 
448 /**
449  * Class that splits a clause into components, facilitating also
450  * sharing of the components
451  */
452 class InterpolantMinimizer::ClauseSplitter {
453 public:
454   CLASS_NAME(InterpolantMinimizer::ClauseSplitter);
455   USE_ALLOCATOR(InterpolantMinimizer::ClauseSplitter);
456 
ClauseSplitter()457   ClauseSplitter() : _index(new SubstitutionTreeClauseVariantIndex()), _acc(0) {}
458 
459   /**
460    * Into @c acc push clauses that correspond to components of @c cl.
461    * The components are shared among calls to the function, so for
462    * components that are variants of each other, the same result is
463    * returned.
464    */
getComponents(Clause * cl,ClauseStack & acc)465   void getComponents(Clause* cl, ClauseStack& acc)
466   {
467     CALL("InterpolantMinimizer::ClauseSplitter::getComponents");
468     ASS(!_acc);
469 
470     _acc = &acc;
471 
472     if(cl->length()==0) {
473       handleNoSplit(cl);
474     }
475     else {
476       doSplitting(cl);
477     }
478 
479     _acc = 0;
480   }
481 protected:
482 
doSplitting(Clause * cl)483   void doSplitting(Clause* cl) {
484 
485     static Stack<LiteralStack> comps;
486     comps.reset();
487     // fills comps with components, returning if not splittable
488     if(!Saturation::Splitter::getComponents(cl, comps)) {
489       handleNoSplit(cl);
490     } else {
491       buildAndInsertComponents(comps.begin(),comps.size());
492     }
493   }
494 
buildAndInsertComponents(const LiteralStack * comps,unsigned compCnt)495   void buildAndInsertComponents(const LiteralStack* comps, unsigned compCnt)
496   {
497     CALL("InterpolantMinimizer::ClauseSplitter::buildAndInsertComponents");
498 
499     for(unsigned i=0; i<compCnt; i++) {
500       Clause* compCl = getComponent(comps[i].begin(), comps[i].size());
501       _acc->push(compCl);
502     }
503   }
504 
handleNoSplit(Clause * cl)505   void handleNoSplit(Clause* cl)
506   {
507     CALL("InterpolantMinimizer::ClauseSplitter::handleNoSplit");
508 
509     _acc->push(getComponent(cl));
510   }
511 
512 private:
513 
getComponent(Literal * const * lits,unsigned len)514   Clause* getComponent(Literal* const * lits, unsigned len)
515   {
516     CALL("InterpolantMinimizer::ClauseSplitter::getComponent/2");
517 
518     if(len==1) {
519       return getAtomComponent(lits[0], 0);
520     }
521     ClauseIterator cit = _index->retrieveVariants(lits, len);
522     if(cit.hasNext()) {
523       Clause* res = cit.next();
524       ASS(!cit.hasNext());
525       return res;
526     }
527     //here the input type and inference are just arbitrary, they'll never be used
528     Clause* res = Clause::fromIterator(getArrayishObjectIterator(lits, len),NonspecificInference0(UnitInputType::AXIOM,InferenceRule::INPUT));
529     res->incRefCnt();
530     _index->insert(res);
531     return res;
532   }
533 
getComponent(Clause * cl)534   Clause* getComponent(Clause* cl)
535   {
536     CALL("InterpolantMinimizer::ClauseSplitter::getComponent/1");
537 
538     if(cl->length()==1) {
539       return getAtomComponent((*cl)[0], cl);
540     }
541 
542     ClauseIterator cit = _index->retrieveVariants(cl);
543     if(cit.hasNext()) {
544       Clause* res = cit.next();
545       ASS(!cit.hasNext());
546       return res;
547     }
548     _index->insert(cl);
549     return cl;
550   }
551 
552   /** cl can be 0 */
getAtomComponent(Literal * lit,Clause * cl)553   Clause* getAtomComponent(Literal* lit, Clause* cl)
554   {
555     CALL("InterpolantMinimizer::ClauseSplitter::getAtomComponent");
556 
557     Literal* norm = Literal::positiveLiteral(lit);
558     norm = Renaming::normalize(norm);
559 
560     Clause* res;
561     if(_atomIndex.find(norm, res)) {
562       return res;
563     }
564     res = cl;
565     if(!res) {
566       res = Clause::fromIterator(getSingletonIterator(norm),
567           NonspecificInference0(UnitInputType::AXIOM,InferenceRule::INPUT));
568     }
569     ALWAYS(_atomIndex.insert(norm, res));
570     return res;
571   }
572 
573   ScopedPtr<ClauseVariantIndex> _index;
574   DHMap<Literal*,Clause*> _atomIndex;
575 
576   ClauseStack* _acc;
577 };
578 
579 /**
580  * Into @c atoms add IDs of components that appear in FormulaUnit @c u
581  *
582  * Currently we consider formulas to be one big component.
583  */
collectAtoms(FormulaUnit * f,Stack<vstring> & atoms)584 void InterpolantMinimizer::collectAtoms(FormulaUnit* f, Stack<vstring>& atoms)
585 {
586   CALL("InterpolantMinimizer::collectAtoms(FormulaUnit*...)");
587 
588   vstring key = f->formula()->toString();
589 
590   vstring id;
591   if(!_formulaAtomIds.find(key, id)) {
592     id = "f" + Int::toString(_formulaAtomIds.size());
593     _formulaAtomIds.insert(key, id);
594     unsigned weight = f->formula()->weight();
595     _atomWeights.insert(id, weight);
596     _unitsById.insert(id, f);
597   }
598 
599   atoms.push(id);
600 }
601 
602 /**
603  * Get ID of component @c cl
604  */
getComponentId(Clause * cl)605 vstring InterpolantMinimizer::getComponentId(Clause* cl)
606 {
607   CALL("InterpolantMinimizer::getComponentId");
608   vstring id;
609   if(!_atomIds.find(cl, id)) {
610     id = "c" + Int::toString(_atomIds.size());
611     _atomIds.insert(cl, id);
612     unsigned weight = cl->weight();
613     _atomWeights.insert(id, weight);
614     _unitsById.insert(id, cl);
615   }
616   return id;
617 }
618 
619 /**
620  * Into @c atoms add IDs of components that appear in @c u
621  * If the formula is not a clause (i.e. conjunction of formulas), then the sub-formulas need to be traversed
622  * (though currently, conjunctions of clauses is treated one formula in interpolation)
623  */
collectAtoms(Unit * u,Stack<vstring> & atoms)624 void InterpolantMinimizer::collectAtoms(Unit* u, Stack<vstring>& atoms)
625 {
626   CALL("InterpolantMinimizer::collectAtoms(Unit*...)");
627 
628   if(!u->isClause()) {
629     collectAtoms(static_cast<FormulaUnit*>(u), atoms);
630     return;
631   }
632 
633   Clause* cl = u->asClause();
634 
635   static ClauseStack components;
636   components.reset();
637   _splitter->getComponents(cl, components);
638   ASS(components.isNonEmpty());
639   ClauseStack::Iterator cit(components);
640 
641   TRACE(cout << "collecting for " << u->toString() << endl);
642 
643   while(cit.hasNext()) {
644     Clause* comp = cit.next();
645 
646     TRACE(cout << "comp " << comp->toString() << endl);
647 
648     atoms.push(getComponentId(comp));
649   }
650 }
651 
652 /**
653  * Add formula implying the presence of components of @c u if
654  * it appears in the digest into @c _resBenchmark
655  */
addAtomImplicationFormula(Unit * u)656 void InterpolantMinimizer::addAtomImplicationFormula(Unit* u)
657 {
658   CALL("InterpolantMinimizer::getAtomImplicationFormula");
659 
660 
661   static Stack<vstring> atoms;
662   atoms.reset();
663 
664   collectAtoms(u, atoms);
665 
666   vstring uId = getUnitId(u);
667 
668   SMTFormula cConj = SMTFormula::getTrue();
669   Stack<vstring>::Iterator ait(atoms);
670   while(ait.hasNext()) {
671 
672     vstring atom = ait.next();
673     cConj = cConj & pred(V, atom);
674   }
675 
676   vstring comment = "atom implications for " + u->toString();
677   _resBenchmark.addFormula(pred(D, uId) --> cConj, comment);
678 }
679 
680 /**
681  * Add formula defining the cost function into @c _resBenchmark
682  */
addCostFormula()683 void InterpolantMinimizer::addCostFormula()
684 {
685   CALL("InterpolantMinimizer::getCostFormula");
686 
687   TRACE(cout << "adding cost fla" << endl);
688 
689   SMTFormula costSum = SMTFormula::unsignedValue(0);
690 
691   WeightMap::Iterator wit(_atomWeights);
692   while(wit.hasNext()) {
693     vstring atom;
694     unsigned weight;
695     wit.next(atom, weight);
696 
697     Unit* unit = _unitsById.get(atom);
698     unsigned varCnt = unit->varCnt();
699 
700     if(_optTarget==OT_COUNT && weight>0) {
701       weight = 1;
702     }
703     //**minimize the interpolant wrt the number of quantifiers
704     if(_optTarget==OT_QUANTIFIERS){
705       weight = varCnt;
706     }
707 
708     TRACE(cout << "cost " << weight << " for unit " << unit->toString() << endl);
709 
710     SMTFormula atomExpr = SMTFormula::condNumber(pred(V, atom), weight);
711     costSum = SMTFormula::add(costSum, atomExpr);
712   }
713   _resBenchmark.addFormula(SMTFormula::equals(costFunction(), costSum));
714 }
715 
716 ///////////////////////////////////////////
717 // Generating the SAT part of the problem
718 //
719 
pred(PredType t,vstring node)720 SMTConstant InterpolantMinimizer::pred(PredType t, vstring node)
721 {
722   CALL("InterpolantMinimizer::pred");
723   //Fake node is fictitious parent of gray nodes marked as colored in the TPTP.
724   //We should never create predicates for these.
725   ASS_NEQ(node, "fake_node");
726 
727   vstring n1;
728   switch(t) {
729   case R: n1 = "r"; break;
730   case B: n1 = "b"; break;
731   case G: n1 = "g"; break;
732   case S: n1 = "s"; break;
733   case RC: n1 = "rc"; break;
734   case BC: n1 = "bc"; break;
735   case RF: n1 = "rf"; break;
736   case BF: n1 = "bf"; break;
737   case D: n1 = "d"; break;
738   case V: n1 = "v"; break;
739   default: ASSERTION_VIOLATION;
740   }
741   SMTConstant res = SMTFormula::name(n1, node);
742   _resBenchmark.declarePropositionalConstant(res);
743   return res;
744 }
745 
costFunction()746 SMTConstant InterpolantMinimizer::costFunction()
747 {
748   SMTConstant res = SMTFormula::name("cost");
749   _resBenchmark.declareRealConstant(res);
750   return res;
751 }
752 
getUnitId(Unit * u)753 vstring InterpolantMinimizer::getUnitId(Unit* u)
754 {
755   CALL("InterpolantMinimizer::getUnitId");
756 
757   vstring id = InferenceStore::instance()->getUnitIdStr(u);
758 //  _idToFormulas.insert(is, u); //the item might be already there from previous call to getUnitId
759   return id;
760 }
761 
762 /**
763  * Add into @c _resBenchmark formulas stating uniqueness of trace colours
764  * of node @c n.
765  */
addDistinctColorsFormula(vstring n)766 void InterpolantMinimizer::addDistinctColorsFormula(vstring n)
767 {
768   CALL("InterpolantMinimizer::distinctColorsFormula");
769 
770   SMTFormula rN = pred(R, n);
771   SMTFormula bN = pred(B, n);
772   SMTFormula gN = pred(G, n);
773 
774   SMTFormula res = bN | rN | gN;
775   res = res & ( rN --> ((!bN) & (!gN)) );
776   res = res & ( bN --> ((!rN) & (!gN)) );
777   res = res & ( gN --> ((!rN) & (!bN)) );
778 
779   _resBenchmark.addFormula(res);
780 }
781 
782 /**
783  * Add into @c _resBenchmark formulas related to digest and trace of node @c n
784  * that are specific to a node which is parent of only gray formulas.
785  */
addGreyNodePropertiesFormula(vstring n,ParentSummary & parents)786 void InterpolantMinimizer::addGreyNodePropertiesFormula(vstring n, ParentSummary& parents)
787 {
788   CALL("InterpolantMinimizer::gNodePropertiesFormula");
789   ASS(parents.rParents.isEmpty());
790   ASS(parents.bParents.isEmpty());
791 
792   SMTFormula rParDisj = SMTFormula::getFalse();
793   SMTFormula bParDisj = SMTFormula::getFalse();
794   SMTFormula gParConj = SMTFormula::getTrue();
795 
796   Stack<vstring>::Iterator pit(parents.gParents);
797   while(pit.hasNext()) {
798     vstring par = pit.next();
799     rParDisj = rParDisj | pred(R, par);
800     bParDisj = bParDisj | pred(B, par);
801     gParConj = gParConj & pred(G, par);
802   }
803 
804   SMTFormula rN = pred(R, n);
805   SMTFormula bN = pred(B, n);
806   SMTFormula gN = pred(G, n);
807   SMTFormula sN = pred(S, n);
808   SMTFormula rcN = pred(RC, n);
809   SMTFormula bcN = pred(BC, n);
810 //  SMTFormula dN = pred(D, n);
811 
812 
813   _resBenchmark.addFormula(rcN -=- ((!sN) & rParDisj));
814   _resBenchmark.addFormula(bcN -=- ((!sN) & bParDisj));
815 
816   _resBenchmark.addFormula(rParDisj-->!bParDisj);
817   _resBenchmark.addFormula(bParDisj-->!rParDisj);
818   _resBenchmark.addFormula((sN & rParDisj)-->rN);
819   _resBenchmark.addFormula((sN & bParDisj)-->bN);
820   _resBenchmark.addFormula((sN & gParConj)-->gN);
821   _resBenchmark.addFormula( (!sN)-->gN );
822 //  _resBenchmark.addFormula( dN -=- ( (!sN) & !gParConj ) );
823 }
824 
825 /**
826  * Add properties for a leaf node which was marked as colored in the TPTP problem,
827  * but doesn't contain any colored symbols
828  */
addLeafNodePropertiesFormula(vstring n)829 void InterpolantMinimizer::addLeafNodePropertiesFormula(vstring n)
830 {
831   CALL("InterpolantMinimizer::addLeafNodePropertiesFormula");
832 
833 
834   SMTFormula gN = pred(G, n);
835   SMTFormula sN = pred(S, n);
836   SMTFormula dN = pred(D, n);
837 
838   _resBenchmark.addFormula(!sN);
839   _resBenchmark.addFormula(gN);
840   _resBenchmark.addFormula(dN);
841 }
842 
843 /**
844  * Add into @c _resBenchmark formulas related to digest and trace of node @c n
845  * that are specific to a node which is parent of a colored formula.
846  */
addColoredParentPropertiesFormulas(vstring n,ParentSummary & parents)847 void InterpolantMinimizer::addColoredParentPropertiesFormulas(vstring n, ParentSummary& parents)
848 {
849   CALL("InterpolantMinimizer::coloredParentPropertiesFormula");
850   ASS_NEQ(parents.rParents.isNonEmpty(),parents.bParents.isNonEmpty());
851 
852   PredType parentType = parents.rParents.isNonEmpty() ? R : B;
853   PredType oppositeType = (parentType==R) ? B : R;
854 
855   Stack<vstring>::Iterator gParIt(parents.gParents);
856 
857   SMTFormula gParNegConj = SMTFormula::getTrue();
858   while(gParIt.hasNext()) {
859     vstring par = gParIt.next();
860     gParNegConj = gParNegConj & !pred(oppositeType, par);
861   }
862 
863   SMTFormula parN = pred(parentType, n);
864   SMTFormula gN = pred(G, n);
865   SMTFormula sN = pred(S, n);
866   SMTFormula rcN = pred(RC, n);
867   SMTFormula bcN = pred(BC, n);
868 //  SMTFormula dN = pred(D, n);
869 
870   if(parentType==R) {
871     _resBenchmark.addFormula(rcN -=- !sN);
872     _resBenchmark.addFormula(!bcN);
873   }
874   else {
875     ASS_EQ(parentType,B);
876     _resBenchmark.addFormula(bcN -=- !sN);
877     _resBenchmark.addFormula(!rcN);
878   }
879 
880   _resBenchmark.addFormula(gParNegConj);
881   _resBenchmark.addFormula(sN --> parN);
882   _resBenchmark.addFormula((!sN) --> gN);
883 //  _resBenchmark.addFormula(dN -=- !sN);
884 }
885 
886 /**
887  * Add into @c _resBenchmark formulas related to digest and trace of node @c n, provided
888  * @c n is not a leaf node.
889  *
890  * Formulas related to the cost function are added elsewhere.
891  */
addNodeFormulas(vstring n,ParentSummary & parents)892 void InterpolantMinimizer::addNodeFormulas(vstring n, ParentSummary& parents)
893 {
894   CALL("InterpolantMinimizer::propertiesFormula");
895 
896   addDistinctColorsFormula(n);
897 
898   if(parents.rParents.isEmpty() && parents.bParents.isEmpty()) {
899     addGreyNodePropertiesFormula(n, parents);
900   }
901   else {
902     addColoredParentPropertiesFormulas(n, parents);
903   }
904 }
905 
906 /////////////////////////
907 // Proof tree traversal
908 //
909 
910 struct InterpolantMinimizer::TraverseStackEntry
911 {
TraverseStackEntryInterpolantMinimizer::TraverseStackEntry912   TraverseStackEntry(InterpolantMinimizer& im, Unit* u) : unit(u), _im(im)
913   {
914     CALL("InterpolantMinimizer::TraverseStackEntry::TraverseStackEntry");
915 
916     parentIterator = InferenceStore::instance()->getParents(u);
917 
918     //we don't create stack entries for already visited units,
919     //so we must always be able to insert
920     ALWAYS(im._infos.insert(unit, UnitInfo()));
921     UnitInfo& info = getInfo();
922 
923     info.color = u->getColor();
924     info.inputInheritedColor = u->inheritedColor();
925     if(info.inputInheritedColor==COLOR_INVALID) {
926       if(!parentIterator.hasNext()) {
927 	//this covers introduced name definitions
928 	info.inputInheritedColor = info.color;
929       }
930       else {
931 	info.inputInheritedColor = COLOR_TRANSPARENT;
932       }
933     }
934 
935     info.leadsToColor = info.color!=COLOR_TRANSPARENT || info.inputInheritedColor!=COLOR_TRANSPARENT;
936   }
937 
938   /**
939    * Extract the needed information on the relation between the current unit
940    * and its premise @c parent
941    */
processParentInterpolantMinimizer::TraverseStackEntry942   void processParent(Unit* parent)
943   {
944     CALL("InterpolantMinimizer::TraverseStackEntry::processParent");
945 
946     UnitInfo& info = getInfo();
947 
948     UnitInfo& parInfo = _im._infos.get(parent);
949 //    Color pcol = parent.unit()->getColor();
950     Color pcol = parInfo.color;
951     if(pcol==COLOR_LEFT) {
952       ASS_NEQ(info.state, HAS_RIGHT_PARENT);
953       info.state = HAS_LEFT_PARENT;
954     }
955     if(pcol==COLOR_RIGHT) {
956       ASS_REP2(info.state!=HAS_LEFT_PARENT, unit->toString(), parent->toString());
957       info.state = HAS_RIGHT_PARENT;
958     }
959 
960     info.leadsToColor |= parInfo.leadsToColor;
961 
962     if(info.color==COLOR_LEFT) {
963       parInfo.isParentOfLeft = true;
964       UList::push(unit, parInfo.leftSuccessors);
965     }
966     else if(info.color==COLOR_RIGHT) {
967       parInfo.isParentOfRight = true;
968       UList::push(unit, parInfo.rightSuccessors);
969     }
970     else {
971       ASS_EQ(info.color, COLOR_TRANSPARENT);
972       UList::push(unit, parInfo.transparentSuccessors);
973     }
974   }
975 
976   /**
977    * The returned reference is valid only before updating
978    * InterpolantMinimizer::_infos
979    */
getInfoInterpolantMinimizer::TraverseStackEntry980   UnitInfo& getInfo()
981   {
982     return _im._infos.get(unit);
983   }
984 
985   Unit* unit;
986   /** Premises that are yet to be traversed */
987   UnitIterator parentIterator;
988 
989   InterpolantMinimizer& _im;
990 };
991 
992 /**
993  * Traverse through the proof graph of @c refutationClause and
994  * record everything that is necessary for generating the
995  * minimization problem
996  */
traverse(Unit * refutationUnit)997 void InterpolantMinimizer::traverse(Unit* refutationUnit)
998 {
999   CALL("InterpolantMinimizer::traverse");
1000 
1001   Unit* refutation = refutationUnit;
1002 
1003   static Stack<TraverseStackEntry> stack;
1004   stack.reset();
1005 
1006   stack.push(TraverseStackEntry(*this, refutation));
1007   stack.top().getInfo().isRefutation = true;
1008   while(stack.isNonEmpty()) {
1009     TraverseStackEntry& top = stack.top();
1010     if(top.parentIterator.hasNext()) {
1011       Unit* parent = top.parentIterator.next();
1012 
1013       if(!_infos.find(parent)) {
1014 	stack.push(TraverseStackEntry(*this, parent));
1015       }
1016       else {
1017 	top.processParent(parent);
1018       }
1019     }
1020     else {
1021       if(stack.size()>1){
1022 	TraverseStackEntry& child = stack[stack.size()-2];
1023 	child.processParent(stack.top().unit);
1024       }
1025       stack.pop();
1026     }
1027   }
1028 
1029 }
1030 
1031 /**
1032  * Create InterpolantMinimizer object
1033  *
1034  * @param target optimiziation target, determines what should be optimised.
1035  *
1036  * @param noSlicing If true, we forbid all slicing of proof nodes. This simulates
1037  * the original algorithm which didn't use minimization.
1038  *
1039  * @param showStats Value of the cost function is output
1040  *
1041  * @param statsPrefix The value of the cost function is output on line starting
1042  * with statsPrefix + " cost: "
1043  */
InterpolantMinimizer(OptimizationTarget target,bool noSlicing,bool showStats,vstring statsPrefix)1044 InterpolantMinimizer::InterpolantMinimizer(OptimizationTarget target, bool noSlicing,
1045 					   bool showStats, vstring statsPrefix)
1046   : _optTarget(target),
1047     _noSlicing(noSlicing),
1048     _showStats(showStats),
1049     _statsPrefix(statsPrefix)
1050 {
1051   CALL("InterpolantMinimizer::InterpolantMinimizer");
1052 
1053   _splitter = new ClauseSplitter();
1054 }
1055 
~InterpolantMinimizer()1056 InterpolantMinimizer::~InterpolantMinimizer()
1057 {
1058   CALL("InterpolantMinimizer::~InterpolantMinimizer");
1059 
1060   delete _splitter;
1061 }
1062