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