1 
2 /*
3  * File Interpolants.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 Interpolants.cpp
21  * Implements class Interpolants.
22  */
23 
24 #include "Lib/DHMap.hpp"
25 #include "Lib/Stack.hpp"
26 #include "Lib/SharedSet.hpp"
27 
28 #include "Kernel/Clause.hpp"
29 #include "Kernel/ColorHelper.hpp"
30 #include "Kernel/Formula.hpp"
31 #include "Kernel/Inference.hpp"
32 #include "Kernel/InferenceStore.hpp"
33 #include "Kernel/SubstHelper.hpp"
34 #include "Kernel/Term.hpp"
35 #include "Kernel/Unit.hpp"
36 #include "Kernel/FormulaUnit.hpp"
37 
38 #include "Flattening.hpp"
39 #include "SimplifyFalseTrue.hpp"
40 #include "NNF.hpp"
41 
42 #include "Interpolants.hpp"
43 
44 #define TRACE(x)
45 
46 /** surprising colors occur when a clause which is a consequence of transparent clauses, is colored */
47 #define ALLOW_SURPRISING_COLORS 1
48 
49 namespace Shell
50 {
51 
52 using namespace Lib;
53 using namespace Kernel;
54 
55 typedef pair<Formula*, Formula*> UIPair; //pair of unit U and the U-interpolant
56 typedef List<UIPair> UIPairList;
57 
getParents(Unit * u)58 VirtualIterator<Unit*> Interpolants::getParents(Unit* u)
59 {
60   CALL("Interpolants::getParents");
61 
62   if(!_slicedOff) {
63     return InferenceStore::instance()->getParents(u);
64   }
65 
66   static Stack<Unit*> toDo;
67   static Stack<Unit*> parents;
68 
69   toDo.reset();
70   parents.reset();
71 
72   for(;;) {
73     UnitIterator pit = InferenceStore::instance()->getParents(u);
74     while(pit.hasNext()) {
75       Unit* par = pit.next();
76       if(_slicedOff->find(par)) {
77 	toDo.push(par);
78       }
79       else {
80 	parents.push(par);
81       }
82     }
83     if(toDo.isEmpty()) {
84       break;
85     }
86     u = toDo.pop();
87   }
88 
89   return getPersistentIterator(Stack<Unit*>::BottomFirstIterator(parents));
90 }
91 
92 struct Interpolants::ItemState
93 {
ItemStateShell::Interpolants::ItemState94   ItemState() {}
95 
ItemStateShell::Interpolants::ItemState96   ItemState(Unit* us) : parCnt(0), inheritedColor(COLOR_TRANSPARENT), interpolant(0),
97       leftInts(0), rightInts(0), processed(false), _us(us)
98   {
99     CALL("ItemState::ItemState");
100     _usColor = us->getColor();
101   }
102 
destroyShell::Interpolants::ItemState103   void destroy()
104   {
105     CALL("ItemState::destroy");
106 
107     List<UIPair>::destroy(leftInts);
108     List<UIPair>::destroy(rightInts);
109   }
110 
usShell::Interpolants::ItemState111   Unit* us() const { return _us; }
usColorShell::Interpolants::ItemState112   Color usColor() const { return _usColor; }
113   /** Parents that remain to be traversed
114    *
115    * Parents in the sense of inferencing, but children in the sense of DFS traversal */
116   VirtualIterator<Unit*> pars;
117   /** number of parents */
118   int parCnt;
119   /** Color of premise formulas, or the declared color for input formulas */
120   Color inheritedColor;
121   /** If non-zero, interpolant of the current formula. */
122   Formula* interpolant;
123   /** Left interpolants of parent formulas */
124   List<UIPair>* leftInts;
125   /** Right interpolants of parent formulas */
126   List<UIPair>* rightInts;
127   /** This state was processed, and if it should have its invarient generated,
128    * it was generated */
129   bool processed;
130 private:
131   /** The current formula */
132   Unit* _us;
133   Color _usColor;
134 };
135 
compareUIP(const UIPair & a,const UIPair & b)136 Comparison compareUIP(const UIPair& a, const UIPair& b)
137 {
138   CALL("compareUIP");
139 
140   Comparison res = Int::compare(a.first, b.first);
141   if(res==EQUAL) {
142     res = Int::compare(a.second, b.second);
143   }
144   return res;
145 }
146 
147 /**
148  * Assuming arguments are lists ordered by the @c compareUIP() function,
149  * add non-destructively new elements from @c src into @c tgt.
150  */
mergeCopy(UIPairList * & tgt,UIPairList * src)151 void mergeCopy(UIPairList*& tgt, UIPairList* src)
152 {
153   CALL("mergeCopy");
154   if(!tgt) {
155     tgt = UIPairList::copy(src);
156     return;
157   }
158 
159   UIPairList** curr = &tgt;
160   UIPairList::Iterator sit(src);
161   while(sit.hasNext()) {
162     UIPair spl = sit.next();
163   retry_curr:
164     if(*curr) {
165       Comparison cmp = compareUIP((*curr)->head(), spl);
166       if(cmp!=GREATER) {
167 	curr = (*curr)->tailPtr();
168 	if(cmp==EQUAL) {
169 	  continue;
170 	}
171 	else {
172 	  goto retry_curr;
173 	}
174       }
175     }
176     *curr = new UIPairList(spl, *curr);
177     curr = (*curr)->tailPtr();
178   }
179 }
180 
181 /**
182  * Any pre-processing of the refutation before interpolation is considered.
183  *
184  * We remove the leafs corresponding to the conjecture
185  * and leave the negated_conjecture child of this unit as the leaf instead.
186  * (Inference::NEGATED_CONJECTURE is not sound).
187  */
removeConjectureNodesFromRefutation(Unit * refutation)188 void Interpolants::removeConjectureNodesFromRefutation(Unit* refutation)
189 {
190   CALL("Interpolants::removeConjectureNodesFromRefutation");
191 
192   Stack<Unit*> todo;
193   DHSet<Unit*> seen;
194 
195   todo.push(refutation);
196   while (todo.isNonEmpty()) {
197     Unit* cur = todo.pop();
198     if (!seen.insert(cur)) {
199       continue;
200     }
201 
202     if (cur->inference().rule() == InferenceRule::NEGATED_CONJECTURE) {
203       VirtualIterator<Unit*> pars = InferenceStore::instance()->getParents(cur);
204 
205       // negating the conjecture is not a sound inference,
206       // we want to consider the proof only from the point where it has been done already
207 
208       ASS(pars.hasNext()); // negating a conjecture should have exactly one parent
209       Unit* par = pars.next();
210 
211       // so we steal parent's inherited color
212       cur->setInheritedColor(par->inheritedColor());
213 
214       // and pretend there is no parent
215 
216       ASS(!pars.hasNext()); // negating a conjecture should have exactly one parent
217 
218       cur->inference().destroy();
219       cur->inference() = Inference(NonspecificInference0(UnitInputType::NEGATED_CONJECTURE,InferenceRule::NEGATED_CONJECTURE)); // negated conjecture without a parent (non-standard, but nobody will see it)
220     }
221 
222     todo.loadFromIterator(InferenceStore::instance()->getParents(cur));
223   }
224 }
225 
226 /**
227 * For any input unit marked as properly colored but which is, in fact, transparent,
228 * we add an artificial parent which is forced to pretend it was truly colored that way,
229 * so that InterpolantMinimizer can consider this input unit as a result of a symbol eliminating inference.
230 * (Without this, InterpolantMinimizer does not work properly in such cases.)
231  */
fakeNodesFromRightButGrayInputsRefutation(Unit * refutation)232 void Interpolants::fakeNodesFromRightButGrayInputsRefutation(Unit* refutation)
233 {
234   CALL("Interpolants::fakeNodesFromRightButGrayInputsRefutation");
235 
236   Stack<Unit*> todo;
237   DHSet<Unit*> seen;
238 
239   todo.push(refutation);
240   while (todo.isNonEmpty()) {
241     Unit* cur = todo.pop();
242     if (!seen.insert(cur)) {
243       continue;
244     }
245 
246     {
247       VirtualIterator<Unit*> pars = InferenceStore::instance()->getParents(cur);
248 
249       if (!pars.hasNext() && // input-like, because no parents
250           cur->inheritedColor() != COLOR_INVALID && cur->inheritedColor() != COLOR_TRANSPARENT && // proper inherited color
251           cur->getColor() == COLOR_TRANSPARENT) {  // but in fact transparent
252 
253           Clause* fakeParent = Clause::fromIterator(LiteralIterator::getEmpty(), NonspecificInference0(cur->inputType(),InferenceRule::INPUT));
254           fakeParent->setInheritedColor(cur->inheritedColor());
255           fakeParent->updateColor(cur->inheritedColor());
256 
257           cur->inference().destroy();
258           cur->inference() = Inference(NonspecificInference1(InferenceRule::INPUT,fakeParent)); // input inference with a parent (non-standard, but nobody will see it)
259           cur->invalidateInheritedColor();
260       }
261     }
262 
263     todo.loadFromIterator(InferenceStore::instance()->getParents(cur));
264   }
265 }
266 
267 
268 /**
269  * Turn all Units in a refutation into FormulaUnits (casting Clauses to Formulas and wrapping these as Units).
270  *
271  * Keep the old refutation (= non-destructive). Possible sharing of the formula part of the original refutation.
272  *
273  * Assume that once we have formula on a parent path we can't go back to a clause.
274  *
275  */
formulifyRefutation(Unit * refutation)276 Unit* Interpolants::formulifyRefutation(Unit* refutation)
277 {
278   CALL("Interpolants::formulifyRefutation");
279 
280   Stack<Unit*> todo;
281   DHMap<Unit*,Unit*> translate; // for caching results (we deal with a DAG in general), but also to distinguish the first call from the next
282 
283   todo.push(refutation);
284   while (todo.isNonEmpty()) {
285     Unit* cur = todo.top();
286 
287     if (translate.find(cur)) {  // the DAG hit case
288       todo.pop();
289 
290       continue;
291     }
292 
293     if (!cur->isClause()) {     // the formula case
294       todo.pop();
295 
296       translate.insert(cur,cur);
297       continue;
298     }
299 
300     // are all children done?
301     bool allDone = true;
302     Inference& inf = cur->inference();
303     Inference::Iterator iit = inf.iterator();
304     while (inf.hasNext(iit)) {
305       Unit* premUnit=inf.next(iit);
306       if (!translate.find(premUnit)) {
307         allDone = false;
308         break;
309       }
310     }
311 
312     if (allDone) { // ready to return
313       todo.pop();
314 
315       List<Unit*>* prems = 0;
316 
317       Inference::Iterator iit = inf.iterator();
318       while (inf.hasNext(iit)) {
319         Unit* premUnit=inf.next(iit);
320 
321         List<Unit*>::push(translate.get(premUnit), prems);
322       }
323 
324       InferenceRule rule=inf.rule();
325       prems = List<Unit*>::reverse(prems);  //we want items in the same order
326 
327       Formula* f = Formula::fromClause(cur->asClause());
328       FormulaUnit* fu = new FormulaUnit(f,NonspecificInferenceMany(rule,prems));
329 
330       if (cur->inheritedColor() != COLOR_INVALID) {
331         fu->setInheritedColor(cur->inheritedColor());
332       }
333 
334       translate.insert(cur,fu);
335     } else { // need "recursive" calls first
336 
337       Inference::Iterator iit = inf.iterator();
338       while (inf.hasNext(iit)) {
339         Unit* premUnit=inf.next(iit);
340         todo.push(premUnit);
341       }
342     }
343   }
344 
345   return translate.get(refutation);
346 }
347 
getInterpolant(Unit * unit)348 Formula* Interpolants::getInterpolant(Unit* unit)
349 {
350   CALL("Interpolants::getInterpolant");
351 
352   typedef DHMap<Unit*,ItemState> ProcMap;
353   ProcMap processed;
354 
355   TRACE(cout << "===== getInterpolant for " << unit->toString() << endl);
356 
357   Stack<ItemState> sts;
358 
359   Unit* curr= unit;
360 
361   Formula* resultInterpolant = 0;
362 
363   int ctr=0;
364 
365   for(;;) {
366     ItemState st;
367 
368     if(processed.find(curr)) {
369       st = processed.get(curr);
370       ASS(st.us()==curr);
371       ASS(st.processed);
372       ASS(!st.pars.hasNext());
373     }
374     else {
375       st = ItemState(curr);
376       st.pars = getParents(curr);
377     }
378 
379     TRACE(cout << "curr  " << curr->toString() << endl);
380     TRACE(cout << "cu_ic " << curr->inheritedColor() << endl);
381     TRACE(cout << "st_co " << st.usColor() << endl);
382 
383     if(curr->inheritedColor()!=COLOR_INVALID) {
384       //set premise-color information for input clauses
385       st.inheritedColor=ColorHelper::combine(curr->inheritedColor(), st.usColor());
386       ASS_NEQ(st.inheritedColor, COLOR_INVALID);
387     }
388 #if ALLOW_SURPRISING_COLORS
389     else {
390       //we set inherited color to the color of the unit.
391       //in the case of clause being conclusion of
392       //transparent parents, the assigned color should be
393       //transparent as well, but there are some corner cases
394       //coming from proof transformations which can yield us
395       //a colored clause in such case (when the colored premise
396       //was removed by the transformation).
397       st.inheritedColor=st.usColor();
398     }
399 #else
400     else if(!st.processed && !st.pars.hasNext()) {
401       //we have unit without any parents. This case is reserved for
402       //units introduced by some naming. In this case we need to set
403       //the inherited color to the color of the unit.
404       st.inheritedColor=st.usColor();
405     }
406 #endif
407 
408     TRACE(cout << "st_ic " << st.inheritedColor << endl);
409 
410     if(sts.isNonEmpty()) {
411       //update premise color information in the level above
412       ItemState& pst=sts.top(); //parent state
413       pst.parCnt++;
414       if(pst.inheritedColor==COLOR_TRANSPARENT) {
415         pst.inheritedColor=st.usColor();
416 
417         TRACE(cout << "updated parent's inh col to " << pst.inheritedColor << endl);
418         TRACE(cout << "parent " << pst.us()->toString() << endl);
419 
420       }
421 #if VDEBUG
422       else {
423         Color clr=st.usColor();
424         ASS_REP2(pst.inheritedColor==clr || clr==COLOR_TRANSPARENT, pst.us()->toString(), curr->toString());
425       }
426       ASS_EQ(curr->getColor(),st.usColor());
427 #endif
428     }
429 
430     // keep initializing splitting components
431     if (curr->isClause()) {
432       Clause* cl = curr->asClause();
433 
434       if (cl->isComponent()) {
435         SplitSet* splits = cl->splits();
436         ASS_EQ(splits->size(),1);
437         _splittingComponents.insert(splits->sval(),cl);
438       }
439     }
440 
441     sts.push(st);
442 
443     for(;;) {
444       if(sts.top().pars.hasNext()) {
445         curr=sts.top().pars.next();
446         break;
447       }
448       //we're done with all children, now we can process what we've gathered
449       st=sts.pop();
450       ctr++;
451       Color color = st.usColor();
452       if(!st.processed) {
453 	if(st.inheritedColor!=color || sts.isEmpty()) {
454 	  //we either have a transparent clause justified by A or B, or the refutation
455 //	  ASS_EQ(color,COLOR_TRANSPARENT);
456 	    TRACE(cout<<"generate interpolant of transparent clause: "<<st.us()->toString()<<"\n");
457 	  ASS_REP2(color==COLOR_TRANSPARENT, st.us()->toString(), st.inheritedColor);
458 	  generateInterpolant(st);
459 	}
460 	st.processed = true;
461 	processed.insert(st.us(), st);
462       }
463 
464       if(sts.isNonEmpty()) {
465 	//pass interpolants to the level above
466         if(color!=COLOR_LEFT) {
467           mergeCopy(sts.top().leftInts, st.leftInts);
468         }
469         if(color!=COLOR_RIGHT) {
470           mergeCopy(sts.top().rightInts, st.rightInts);
471         }
472       }
473       else {
474 	//empty sts (so refutation) with clause st justified by A or B (st is false).
475 	//interpolant was already generated in st.interpolant
476 	//we have now the interpolant for refutation
477         resultInterpolant = st.interpolant;
478         goto fin;
479       }
480     }
481   }
482 
483 fin:
484 
485   //clean-up
486   ProcMap::Iterator destrIt(processed);
487   while(destrIt.hasNext()) {
488     destrIt.next().destroy();
489   }
490 
491   TRACE(cout << "result interpolant (before false/true - simplification) " << resultInterpolant->toString() << endl);
492 
493   cout << "Before simplification: " << resultInterpolant->toString() << endl;
494   cout << "Weight before simplification: " << resultInterpolant->weight() << endl;
495 
496   //simplify the interpolant and exit
497   return Flattening::flatten(NNF::ennf(Flattening::flatten(SimplifyFalseTrue::simplify(resultInterpolant)),true));
498 //  return resultInterpolant;
499 }
500 
generateInterpolant(ItemState & st)501 void Interpolants::generateInterpolant(ItemState& st)
502 {
503   CALL("Interpolants::generateInterpolant");
504 
505   Unit* u=st.us();
506 
507   TRACE(cout << "GenerateInterpolant for " << u->toString() << endl);
508 
509   Color color=st.usColor();
510   ASS_EQ(color, COLOR_TRANSPARENT);
511 
512   Formula* interpolant;
513   Formula* unitFormula=u->getFormula();//st.us().prop());
514 
515   // add assertions from splitting
516   if (u->isClause()) {
517     Clause* cl = u->asClause();
518 
519     if (cl->splits()) {
520       FormulaList* disjuncts = FormulaList::empty();
521       SplitSet::Iterator it(*cl->splits());
522       while(it.hasNext()) {
523         SplitLevel lv=it.next();
524         Clause* ass = _splittingComponents.get(lv);
525         FormulaList::push(new NegatedFormula(Formula::fromClause(ass)),disjuncts);
526       }
527       if (FormulaList::isNonEmpty(disjuncts)) {
528         FormulaList::push(unitFormula,disjuncts);
529 
530         unitFormula = JunctionFormula::generalJunction(OR, disjuncts);
531       }
532     }
533   }
534 
535   TRACE(cout	<<"unitFormula: "<<unitFormula->toString()<<endl);
536 
537   if(st.parCnt) {
538     //interpolants from refutation proof with at least one inference (there are premises, i.e. parents)
539     FormulaList* conj=0;
540     List<UIPair>* src= (st.inheritedColor==COLOR_LEFT) //source of relevant parent interpolants
541         ? st.rightInts
542         : st.leftInts;
543     //construct the common part of the interpolant
544     List<UIPair>::Iterator sit(src);
545     while(sit.hasNext()) {
546       UIPair uip=sit.next();
547       FormulaList* disj=0;
548       FormulaList::push(uip.first, disj);
549       FormulaList::push(uip.second, disj);
550       FormulaList::push(JunctionFormula::generalJunction(OR, disj), conj);
551     }
552 
553     if(st.inheritedColor==COLOR_LEFT) {
554       //u is justified by A
555       FormulaList* innerConj=0;
556       List<UIPair>::Iterator sit2(src);
557       while(sit2.hasNext()) {
558         UIPair uip=sit2.next();
559         FormulaList::push(uip.first, innerConj);
560       }
561       FormulaList::push(new NegatedFormula(JunctionFormula::generalJunction(AND, innerConj)), conj);
562     }
563     else {
564       //u is justified by B or a refutation
565     }
566     interpolant=JunctionFormula::generalJunction(AND, conj);
567   }
568   else {
569     //trivial interpolants (when there are no premises)
570     if(st.inheritedColor==COLOR_RIGHT) {
571       interpolant=new NegatedFormula(unitFormula); //this is for TRUE interpolant if the unitFormula is False
572     }
573     else {
574       //a formula coming from left or a refutation
575       interpolant=unitFormula; //this is for FALSE interpolant if the unitFormula is False
576     }
577   }
578   st.interpolant=interpolant;
579   TRACE(cout<<"Unit "<<u->toString()
580 	<<"\nto Formula "<<unitFormula->toString()
581 	<<"\ninterpolant "<<interpolant->toString()<<endl<<endl);
582   UIPair uip=make_pair(unitFormula, interpolant);
583   if(st.inheritedColor==COLOR_LEFT) {
584     List<UIPair>::destroy(st.leftInts);
585     st.leftInts=0;
586     List<UIPair>::push(uip,st.leftInts);
587   }
588   else if(st.inheritedColor==COLOR_RIGHT) {
589     List<UIPair>::destroy(st.rightInts);
590     st.rightInts=0;
591     List<UIPair>::push(uip,st.rightInts);
592   }
593 }
594 
595 }
596