1 /*********************                                                        */
2 /*! \file inst_propagator.cpp
3  ** \verbatim
4  ** Top contributors (to current version):
5  **   Andrew Reynolds, Tim King, Morgan Deters
6  ** This file is part of the CVC4 project.
7  ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS
8  ** in the top-level source directory) and their institutional affiliations.
9  ** All rights reserved.  See the file COPYING in the top-level source
10  ** directory for licensing information.\endverbatim
11  **
12  ** Propagate mechanism for instantiations
13  **/
14 
15 #include <vector>
16 
17 #include "theory/quantifiers/inst_propagator.h"
18 #include "theory/quantifiers/instantiate.h"
19 #include "theory/quantifiers/term_database.h"
20 #include "theory/rewriter.h"
21 
22 using namespace CVC4;
23 using namespace std;
24 using namespace CVC4::theory;
25 using namespace CVC4::theory::quantifiers;
26 using namespace CVC4::kind;
27 
28 
EqualityQueryInstProp(QuantifiersEngine * qe)29 EqualityQueryInstProp::EqualityQueryInstProp( QuantifiersEngine* qe ) : d_qe( qe ){
30   d_true = NodeManager::currentNM()->mkConst( true );
31   d_false = NodeManager::currentNM()->mkConst( false );
32 }
33 
reset(Theory::Effort e)34 bool EqualityQueryInstProp::reset( Theory::Effort e ) {
35   d_uf.clear();
36   d_uf_exp.clear();
37   d_diseq_list.clear();
38   d_uf_func_map_trie.clear();
39   return true;
40 }
41 
42 /** contains term */
hasTerm(Node a)43 bool EqualityQueryInstProp::hasTerm( Node a ) {
44   if( getEngine()->hasTerm( a ) ){
45     return true;
46   }else{
47     std::vector< Node > exp;
48     Node ar = getUfRepresentative( a, exp );
49     return !ar.isNull() && getEngine()->hasTerm( ar );
50   }
51 }
52 
53 /** get the representative of the equivalence class of a */
getRepresentative(Node a)54 Node EqualityQueryInstProp::getRepresentative( Node a ) {
55   if( getEngine()->hasTerm( a ) ){
56     a = getEngine()->getRepresentative( a );
57   }
58   std::vector< Node > exp;
59   Node ar = getUfRepresentative( a, exp );
60   return ar.isNull() ? a : ar;
61 }
62 
63 /** returns true if a and b are equal in the current context */
areEqual(Node a,Node b)64 bool EqualityQueryInstProp::areEqual( Node a, Node b ) {
65   if( a==b ){
66     return true;
67   }else{
68     eq::EqualityEngine* ee = getEngine();
69     if( ee->hasTerm( a ) && ee->hasTerm( b ) ){
70       if( ee->areEqual( a, b ) ){
71         return true;
72       }
73     }
74     return false;
75   }
76 }
77 
78 /** returns true is a and b are disequal in the current context */
areDisequal(Node a,Node b)79 bool EqualityQueryInstProp::areDisequal( Node a, Node b ) {
80   if( a==b ){
81     return false;
82   }else{
83     eq::EqualityEngine* ee = getEngine();
84     if( ee->hasTerm( a ) && ee->hasTerm( b ) ){
85       if( ee->areDisequal( a, b, false ) ){
86         return true;
87       }
88     }
89     return false;
90   }
91 }
92 
93 /** get the equality engine associated with this query */
getEngine()94 eq::EqualityEngine* EqualityQueryInstProp::getEngine() {
95   return d_qe->getActiveEqualityEngine();
96 }
97 
98 /** get the equivalence class of a */
getEquivalenceClass(Node a,std::vector<Node> & eqc)99 void EqualityQueryInstProp::getEquivalenceClass( Node a, std::vector< Node >& eqc ) {
100   //TODO?
101 }
102 
getCongruentTerm(Node f,std::vector<TNode> & args)103 TNode EqualityQueryInstProp::getCongruentTerm( Node f, std::vector< TNode >& args ) {
104   TNode t = d_qe->getTermDatabase()->getCongruentTerm( f, args );
105   if( !t.isNull() ){
106     return t;
107   }else{
108     return d_uf_func_map_trie[f].existsTerm( args );
109   }
110 }
111 
getRepresentativeExp(Node a,std::vector<Node> & exp)112 Node EqualityQueryInstProp::getRepresentativeExp( Node a, std::vector< Node >& exp ) {
113   bool engine_has_a = getEngine()->hasTerm( a );
114   if( engine_has_a ){
115     a = getEngine()->getRepresentative( a );
116   }
117   //get union find representative, if this occurs in the equality engine, return it
118   unsigned prev_size = exp.size();
119   Node ar = getUfRepresentative( a, exp );
120   if( !ar.isNull() ){
121     if( engine_has_a || getEngine()->hasTerm( ar ) ){
122       Trace("qip-eq") << "getRepresentativeExp " << a << " returns " << ar << std::endl;
123       Assert( getEngine()->hasTerm( ar ) );
124       Assert( getEngine()->getRepresentative( ar )==ar );
125       return ar;
126     }
127   }else{
128     if( engine_has_a ){
129       return a;
130     }
131   }
132   //retract explanation
133   while( exp.size()>prev_size ){
134     exp.pop_back();
135   }
136   return Node::null();
137 }
138 
areEqualExp(Node a,Node b,std::vector<Node> & exp)139 bool EqualityQueryInstProp::areEqualExp( Node a, Node b, std::vector< Node >& exp ) {
140   if( areEqual( a, b ) ){
141     return true;
142   }else{
143     std::vector< Node > exp_a;
144     Node ar = getUfRepresentative( a, exp_a );
145     if( !ar.isNull() ){
146       std::vector< Node > exp_b;
147       if( ar==getUfRepresentative( b, exp_b ) ){
148         merge_exp( exp, exp_a );
149         merge_exp( exp, exp_b );
150         return true;
151       }
152     }
153     return false;
154   }
155 }
156 
areDisequalExp(Node a,Node b,std::vector<Node> & exp)157 bool EqualityQueryInstProp::areDisequalExp( Node a, Node b, std::vector< Node >& exp ) {
158   if( areDisequal( a, b ) ){
159     return true;
160   }else{
161     //Assert( getRepresentative( a )==a );
162     //Assert( getRepresentative( b )==b );
163     std::map< Node, std::vector< Node > >::iterator itd = d_diseq_list[a].find( b );
164     if( itd!=d_diseq_list[a].end() ){
165       exp.insert( exp.end(), itd->second.begin(), itd->second.end() );
166       return true;
167     }else{
168       return false;
169     }
170   }
171 }
172 
getCongruentTermExp(Node f,std::vector<TNode> & args,std::vector<Node> & exp)173 TNode EqualityQueryInstProp::getCongruentTermExp( Node f, std::vector< TNode >& args, std::vector< Node >& exp ) {
174   TNode t = d_qe->getTermDatabase()->getCongruentTerm( f, args );
175   if( !t.isNull() ){
176     return t;
177   }else{
178     TNode tt = d_uf_func_map_trie[f].existsTerm( args );
179     if( !tt.isNull() ){
180       //TODO?
181       return tt;
182     }else{
183       return tt;
184     }
185   }
186 }
187 
getUfRepresentative(Node a,std::vector<Node> & exp)188 Node EqualityQueryInstProp::getUfRepresentative( Node a, std::vector< Node >& exp ) {
189   Assert( exp.empty() );
190   std::map< Node, Node >::iterator it = d_uf.find( a );
191   if( it!=d_uf.end() ){
192     if( it->second==a ){
193       Assert( d_uf_exp[ a ].empty() );
194       return it->second;
195     }else{
196       Node m = getUfRepresentative( it->second, exp );
197       Assert( !m.isNull() );
198       if( m!=it->second ){
199         //update union find
200         d_uf[ a ] = m;
201         //update explanation : merge the explanation of the parent
202         merge_exp( d_uf_exp[ a ], exp );
203         Trace("qip-eq") << "EqualityQueryInstProp::getUfRepresentative : merge " << a << " -> " << m << ", exp size=" << d_uf_exp[ a ].size() << std::endl;
204       }
205       //add current explanation to exp: note that exp is a subset of d_uf_exp[ a ], reset
206       exp.clear();
207       exp.insert( exp.end(), d_uf_exp[ a ].begin(), d_uf_exp[ a ].end() );
208       return m;
209     }
210   }else{
211     return Node::null();
212   }
213 }
214 
215 // set a == b with reason, return status, modify a and b to representatives pre-merge
setEqual(Node & a,Node & b,bool pol,std::vector<Node> & reason)216 int EqualityQueryInstProp::setEqual( Node& a, Node& b, bool pol, std::vector< Node >& reason ) {
217   if( a==b ){
218     return pol ? STATUS_NONE : STATUS_CONFLICT;
219   }
220   int status = pol ? STATUS_MERGED_UNKNOWN : STATUS_NONE;
221   Trace("qip-eq") << "EqualityQueryInstProp::setEqual " << a << ", " << b << ", pol = " << pol << ", reason size = " << reason.size() << std::endl;
222   //get the representative for a
223   std::vector< Node > exp_a;
224   Node ar = getUfRepresentative( a, exp_a );
225   if( ar.isNull() ){
226     Assert( exp_a.empty() );
227     ar = a;
228   }
229   if( ar==b ){
230     Trace("qip-eq") << "EqualityQueryInstProp::setEqual : already equal" << std::endl;
231     if( pol ){
232       return STATUS_NONE;
233     }else{
234       merge_exp( reason, exp_a );
235       return STATUS_CONFLICT;
236     }
237   }
238   bool swap = false;
239   //get the representative for b
240   std::vector< Node > exp_b;
241   Node br = getUfRepresentative( b, exp_b );
242   if( br.isNull() ){
243     Assert( exp_b.empty() );
244     br = b;
245     if( !getEngine()->hasTerm( br ) ){
246       if( ar!=a || getEngine()->hasTerm( ar ) ){
247         swap = true;
248       }
249     }else{
250       if( getEngine()->hasTerm( ar ) ){
251         status = STATUS_MERGED_KNOWN;
252       }
253     }
254   }else{
255     if( ar==br ){
256       Trace("qip-eq") << "EqualityQueryInstProp::setEqual : already equal" << std::endl;
257       if( pol ){
258         return STATUS_NONE;
259       }else{
260         merge_exp( reason, exp_a );
261         merge_exp( reason, exp_b );
262         return STATUS_CONFLICT;
263       }
264     }else if( getEngine()->hasTerm( ar ) ){
265       if( getEngine()->hasTerm( br ) ){
266         status = STATUS_MERGED_KNOWN;
267       }else{
268         swap = true;
269       }
270     }
271   }
272 
273   if( swap ){
274     //swap
275     Node temp_r = ar;
276     ar = br;
277     br = temp_r;
278   }
279 
280   Assert( !getEngine()->hasTerm( ar ) || getEngine()->hasTerm( br ) );
281   Assert( ar!=br );
282 
283   std::vector< Node > exp_d;
284   if( areDisequalExp( ar, br, exp_d ) ){
285     if( pol ){
286       merge_exp( reason, exp_b );
287       merge_exp( reason, exp_b );
288       merge_exp( reason, exp_d );
289       return STATUS_CONFLICT;
290     }else{
291       return STATUS_NONE;
292     }
293   }else{
294     if( pol ){
295       //update the union find
296       Assert( d_uf_exp[ar].empty() );
297       Assert( d_uf_exp[br].empty() );
298 
299       //registerUfTerm( ar );
300       d_uf[ar] = br;
301       merge_exp( d_uf_exp[ar], exp_a );
302       merge_exp( d_uf_exp[ar], exp_b );
303       merge_exp( d_uf_exp[ar], reason );
304 
305       //registerUfTerm( br );
306       d_uf[br] = br;
307       d_uf_exp[br].clear();
308 
309       Trace("qip-eq") << "EqualityQueryInstProp::setEqual : merge " << ar << " -> " << br << ", exp size = " << d_uf_exp[ar].size() << ", status = " << status << std::endl;
310       a = ar;
311       b = br;
312 
313       //carry disequality list
314       std::map< Node, std::map< Node, std::vector< Node > > >::iterator itd = d_diseq_list.find( ar );
315       if( itd!=d_diseq_list.end() ){
316         for( std::map< Node, std::vector< Node > >::iterator itdd = itd->second.begin(); itdd != itd->second.end(); ++itdd ){
317           Node d = itdd->first;
318           if( d_diseq_list[br].find( d )==d_diseq_list[br].end() ){
319             merge_exp( d_diseq_list[br][d], itdd->second );
320             merge_exp( d_diseq_list[br][d], d_uf_exp[ar] );
321           }
322         }
323       }
324 
325       return status;
326     }else{
327       Trace("qip-eq") << "EqualityQueryInstProp::setEqual : disequal " << ar << " <> " << br << std::endl;
328       Assert( d_diseq_list[ar].find( br )==d_diseq_list[ar].end() );
329       Assert( d_diseq_list[br].find( ar )==d_diseq_list[br].end() );
330 
331       merge_exp( d_diseq_list[ar][br], reason );
332       merge_exp( d_diseq_list[br][ar], reason );
333       return STATUS_NONE;
334     }
335   }
336 }
337 
registerUfTerm(TNode n)338 void EqualityQueryInstProp::registerUfTerm( TNode n ) {
339   if( d_uf.find( n )==d_uf.end() ){
340     if( !getEngine()->hasTerm( n ) ){
341       TNode f = d_qe->getTermDatabase()->getMatchOperator( n );
342       if( !f.isNull() ){
343         std::vector< TNode > args;
344         for( unsigned i=0; i<n.getNumChildren(); i++ ){
345           if( !getEngine()->hasTerm( n[i] ) ){
346             return;
347           }else{
348             args.push_back( n[i] );
349           }
350         }
351         d_uf_func_map_trie[f].addTerm( n, args );
352       }
353     }
354   }
355 }
356 
357 //void EqualityQueryInstProp::addArgument( std::vector< Node >& args, std::vector< Node >& props, Node n, bool is_prop, bool pol ) {
addArgument(Node n,std::vector<Node> & args,std::vector<Node> & watch,bool is_watch)358 void EqualityQueryInstProp::addArgument( Node n, std::vector< Node >& args, std::vector< Node >& watch, bool is_watch ) {
359   if( is_watch ){
360     watch.push_back( n );
361   }
362   args.push_back( n );
363 }
364 
isPropagateLiteral(Node n)365 bool EqualityQueryInstProp::isPropagateLiteral( Node n ) {
366   if( n==d_true || n==d_false ){
367     return false;
368   }else{
369     Kind ak = n.getKind()==NOT ? n[0].getKind() : n.getKind();
370     if( ak==EQUAL ){
371       Node atom = n.getKind()==NOT ? n[0] : n;
372       return !atom[0].getType().isBoolean();
373     }else{
374       Assert( ak!=NOT );
375       return ak!=AND && ak!=OR && ak!=ITE;
376     }
377   }
378 }
379 
setWatchList(Node n,std::vector<Node> & watch,std::map<Node,std::vector<Node>> & watch_list_out)380 void EqualityQueryInstProp::setWatchList( Node n, std::vector< Node >& watch, std::map< Node, std::vector< Node > >& watch_list_out ) {
381   if( watch.empty() ){
382     watch.push_back( n );
383   }
384   for( unsigned j=0; j<watch.size(); j++ ){
385     Trace("qip-eval") << "Watch : " << n << " -> " << watch[j] << std::endl;
386     watch_list_out[n].push_back( watch[j] );
387   }
388 }
389 
collectWatchList(Node n,std::map<Node,std::vector<Node>> & watch_list_out,std::vector<Node> & watch_list)390 void EqualityQueryInstProp::collectWatchList( Node n, std::map< Node, std::vector< Node > >& watch_list_out, std::vector< Node >& watch_list ) {
391   std::map< Node, std::vector< Node > >::iterator it = watch_list_out.find( n );
392   if( it!=watch_list_out.end() && std::find( watch_list.begin(), watch_list.end(), n )==watch_list.end() ){
393     watch_list.push_back( n );
394     for( unsigned j=0; j<it->second.size(); j++ ){
395       collectWatchList( it->second[j], watch_list_out, watch_list );
396     }
397   }
398 }
399 
400 //this is similar to TermDb::evaluateTerm2, but tracks more information
evaluateTermExp(Node n,std::vector<Node> & exp,std::map<int,std::map<Node,Node>> & visited,bool hasPol,bool pol,std::map<Node,std::vector<Node>> & watch_list_out,std::vector<Node> & props)401 Node EqualityQueryInstProp::evaluateTermExp( Node n, std::vector< Node >& exp, std::map< int, std::map< Node, Node > >& visited,
402                                              bool hasPol, bool pol, std::map< Node, std::vector< Node > >& watch_list_out, std::vector< Node >& props ) {
403   int polIndex = hasPol ? ( pol ? 1 : -1 ) : 0;
404   std::map< Node, Node >::iterator itv = visited[polIndex].find( n );
405   if( itv!=visited[polIndex].end() ){
406     return itv->second;
407   }else{
408     visited[polIndex][n] = n;
409     Node ret;
410     //check if it should be propagated in this context
411     if( hasPol && isPropagateLiteral( n ) ){
412       Assert( n.getType().isBoolean() );
413       //must be Boolean
414       ret = evaluateTermExp( n, exp, visited, false, pol, watch_list_out, props );
415       if( isPropagateLiteral( ret ) ){
416         Trace("qip-eval") << "-----> propagate : " << ret << std::endl;
417         props.push_back( pol ? ret : ret.negate() );
418         ret = pol ? d_true : d_false;
419       }
420     }else{
421       Trace("qip-eval") << "evaluate term : " << n << " [" << polIndex << "]" << std::endl;
422       std::vector< Node > exp_n;
423       ret = getRepresentativeExp( n, exp_n );
424       if( ret.isNull() ){
425         //term is not known to be equal to a representative in equality engine, evaluate it
426         Kind k = n.getKind();
427         if( k!=FORALL ){
428           TNode f = d_qe->getTermDatabase()->getMatchOperator( n );
429           std::vector< Node > args;
430           bool ret_set = false;
431           bool childChanged = false;
432           int abort_i = -1;
433           //get the child entailed polarity
434           Assert( n.getKind()!=IMPLIES );
435           bool newHasPol, newPol;
436           QuantPhaseReq::getEntailPolarity( n, 0, hasPol, pol, newHasPol, newPol );
437           std::vector< Node > watch;
438           //for each child
439           for( unsigned i=0; i<n.getNumChildren(); i++ ){
440             Node c = evaluateTermExp( n[i], exp, visited, newHasPol, newPol, watch_list_out, props );
441             if( c.isNull() ){
442               ret = Node::null();
443               ret_set = true;
444               break;
445             }else if( c==d_true || c==d_false ){
446               //short-circuiting
447               if( k==kind::AND || k==kind::OR ){
448                 if( (k==kind::AND)==(c==d_false) ){
449                   ret = c;
450                   ret_set = true;
451                   break;
452                 }else{
453                   //redundant
454                   c = Node::null();
455                   childChanged = true;
456                 }
457               }else if( k==kind::ITE && i==0 ){
458                 ret = evaluateTermExp( n[ c==d_true ? 1 : 2], exp, visited, hasPol, pol, watch_list_out, props );
459                 ret_set = true;
460                 break;
461               }else if( k==kind::NOT ){
462                 ret = c==d_true ? d_false : d_true;
463                 ret_set = true;
464                 break;
465               }
466             }
467             if( !c.isNull() ){
468               childChanged = childChanged || n[i]!=c;
469               bool is_watch = watch_list_out.find( c )!=watch_list_out.end();
470               if( !f.isNull() && is_watch ){
471                 // we are done if this is an UF application and an argument is unevaluated
472                 addArgument( c, args, watch, is_watch );
473                 abort_i = i;
474                 break;
475               }else if( k==kind::AND || k==kind::OR || k==kind::ITE || ( k==EQUAL && n[0].getType().isBoolean() ) ){
476                 Trace("qip-eval-debug") << "Adding argument " << c << " to " << k << ", isProp = " << newHasPol << std::endl;
477                 if( ( k==kind::AND || k==kind::OR  ) && c.getKind()==k ){
478                   //flatten
479                   for( unsigned j=0; j<c.getNumChildren(); j++ ){
480                     addArgument( c[j], args, watch, is_watch );
481                   }
482                 }else{
483                   addArgument( c, args, watch, is_watch );
484                 }
485                 Trace("qip-eval-debug") << "props/args = " << props.size() << "/" << args.size() << std::endl;
486                 //if we are in a branching position
487                 if( hasPol && !newHasPol && args.size()>=2 ){
488                   //we are done if at least two args are unevaluated
489                   abort_i = i;
490                   break;
491                 }
492               }else{
493                 addArgument( c, args, watch, is_watch );
494               }
495             }
496           }
497           //add remaining children if we aborted
498           if( abort_i!=-1 ){
499             Trace("qip-eval-debug") << "..." << n << " aborted at " << abort_i << std::endl;
500             for( int i=(abort_i+1); i<(int)n.getNumChildren(); i++ ){
501               args.push_back( n[i] );
502             }
503           }
504           //if we have not short-circuited evaluation
505           if( !ret_set ){
506             //if it is an indexed term, return the congruent term
507             if( !f.isNull() && watch.empty() ){
508               std::vector< TNode > t_args;
509               for( unsigned i=0; i<args.size(); i++ ) {
510                 Trace("qip-eval") << "arg " << i << " : " << args[i] << std::endl;
511                 t_args.push_back( args[i] );
512               }
513               Assert( args.size()==n.getNumChildren() );
514               //args contains terms known by the equality engine
515               TNode nn = getCongruentTerm( f, t_args );
516               Trace("qip-eval") << "  got congruent term " << nn << " for " << n << std::endl;
517               if( !nn.isNull() ){
518                 //successfully constructed representative in EE
519                 Assert( exp_n.empty() );
520                 ret = getRepresentativeExp( nn, exp_n );
521                 Trace("qip-eval") << "return rep, exp size = " << exp_n.size() << std::endl;
522                 merge_exp( exp, exp_n );
523                 ret_set = true;
524                 Assert( !ret.isNull() );
525                 Assert( ret!=n );
526                 // we have that n == ret, check if the union find should be updated TODO?
527               }else{
528                 watch.push_back( ret );
529               }
530             }
531             if( !ret_set ){
532               if( childChanged || args.size()!=n.getNumChildren() ){
533                 Trace("qip-eval") << "return rewrite" << std::endl;
534                 if( k==kind::AND || k==kind::OR ){
535                   if( args.empty() ){
536                     ret = k==kind::AND ? d_true : d_false;
537                     ret_set = true;
538                   }else if( args.size()==1 ){
539                     //need to re-evaluate (may be new propagations)
540                     ret = evaluateTermExp( args[0], exp, visited, hasPol, pol, watch_list_out, props );
541                     ret_set = true;
542                   }
543                 }else{
544                   Assert( args.size()==n.getNumChildren() );
545                 }
546                 if( !ret_set ){
547                   if( n.getMetaKind() == kind::metakind::PARAMETERIZED ){
548                     args.insert( args.begin(), n.getOperator() );
549                   }
550                   ret = NodeManager::currentNM()->mkNode( k, args );
551                   setWatchList( ret, watch, watch_list_out );
552                   ret = Rewriter::rewrite( ret );
553                   //need to re-evaluate
554                   ret = evaluateTermExp( ret, exp, visited, hasPol, pol, watch_list_out, props );
555                 }
556               }else{
557                 ret = n;
558                 setWatchList( ret, watch, watch_list_out );
559               }
560             }
561           }
562         }
563       }else{
564         Trace("qip-eval") << "...exists in ee, return rep, exp size = " << exp_n.size() << std::endl;
565         merge_exp( exp, exp_n );
566       }
567     }
568 
569     Trace("qip-eval") << "evaluated term : " << n << " [" << polIndex << "], got : " << ret << ", exp size = " << exp.size() << ", watch list size = " << watch_list_out.size() << std::endl;
570     visited[polIndex][n] = ret;
571     return ret;
572   }
573 }
574 
merge_exp(std::vector<Node> & v,std::vector<Node> & v_to_merge,int up_to_size)575 void EqualityQueryInstProp::merge_exp( std::vector< Node >& v, std::vector< Node >& v_to_merge, int up_to_size ) {
576   //TODO : optimize
577   if( v.empty() ){
578     Assert( up_to_size==-1 || up_to_size==(int)v_to_merge.size() );
579     v.insert( v.end(), v_to_merge.begin(), v_to_merge.end() );
580   }else{
581     //std::vector< Node >::iterator v_end = v.end();
582     up_to_size = up_to_size==-1 ? (int)v_to_merge.size() : up_to_size;
583     for( int j=0; j<up_to_size; j++ ){
584       if( std::find( v.begin(), v.end(), v_to_merge[j] )==v.end() ){
585         v.push_back( v_to_merge[j] );
586       }
587     }
588   }
589 }
590 
591 
init(Node q,Node lem,std::vector<Node> & terms,Node body)592 void InstPropagator::InstInfo::init( Node q, Node lem, std::vector< Node >& terms, Node body ) {
593   d_active = true;
594   //information about the instance
595   d_q = q;
596   d_lem = lem;
597   Assert( d_terms.empty() );
598   d_terms.insert( d_terms.end(), terms.begin(), terms.end() );
599   //the current lemma
600   d_curr = body;
601   d_curr_exp.push_back( body );
602 }
603 
InstPropagator(QuantifiersEngine * qe)604 InstPropagator::InstPropagator(QuantifiersEngine* qe)
605     : d_qe(qe),
606       d_notify(*this),
607       d_qy(qe),
608       d_icount(1),
609       d_conflict(false),
610       d_has_relevant_inst(false) {}
611 
reset(Theory::Effort e)612 bool InstPropagator::reset( Theory::Effort e ) {
613   d_icount = 1;
614   d_ii.clear();
615   for( unsigned i=0; i<2; i++ ){
616     d_conc_to_id[i].clear();
617     d_conc_to_id[i][d_qy.d_true] = 0;
618   }
619   d_conflict = false;
620   d_watch_list.clear();
621   d_update_list.clear();
622   d_relevant_inst.clear();
623   d_has_relevant_inst = false;
624   return d_qy.reset( e );
625 }
626 
notifyInstantiation(QuantifiersModule::QEffort quant_e,Node q,Node lem,std::vector<Node> & terms,Node body)627 bool InstPropagator::notifyInstantiation(QuantifiersModule::QEffort quant_e,
628                                          Node q,
629                                          Node lem,
630                                          std::vector<Node>& terms,
631                                          Node body)
632 {
633   if( !d_conflict ){
634     if( Trace.isOn("qip-prop") ){
635       Trace("qip-prop") << "InstPropagator:: Notify instantiation " << q << " : " << std::endl;
636       for( unsigned i=0; i<terms.size(); i++ ){
637         Trace("qip-prop") << "  " << terms[i] << std::endl;
638       }
639     }
640     unsigned id = allocateInstantiation( q, lem, terms, body );
641     //initialize the information
642     if( cacheConclusion( id, body ) ){
643       Assert( d_update_list.empty() );
644       d_update_list.push_back( id );
645       bool firstTime = true;
646       //update infos in the update list until empty
647       do {
648         unsigned uid = d_update_list.back();
649         d_update_list.pop_back();
650         if( d_ii[uid].d_active ){
651           update( uid, d_ii[uid], firstTime );
652         }
653         firstTime = false;
654       }while( !d_conflict && !d_update_list.empty() );
655     }else{
656       d_ii[id].d_active = false;
657       Trace("qip-prop") << "...duplicate." << std::endl;
658     }
659     Trace("qip-prop") << "...finished notify instantiation." << std::endl;
660     return !d_conflict;
661   }else{
662     Assert( false );
663     return false;
664   }
665 }
666 
filterInstantiations()667 void InstPropagator::filterInstantiations() {
668   if( d_has_relevant_inst ){
669     //now, inform quantifiers engine which instances should be retracted
670     Trace("qip-prop-debug") << "...remove instantiation ids : ";
671     for( std::map< unsigned, InstInfo >::iterator it = d_ii.begin(); it != d_ii.end(); ++it ){
672       if( !it->second.d_q.isNull() ){
673         if( d_relevant_inst.find( it->first )==d_relevant_inst.end() ){
674           if (!d_qe->getInstantiate()->removeInstantiation(
675                   it->second.d_q, it->second.d_lem, it->second.d_terms))
676           {
677             Trace("qip-warn") << "WARNING : did not remove instantiation id " << it->first << std::endl;
678             Assert( false );
679           }else{
680             Trace("qip-prop-debug") << it->first << " ";
681           }
682         }else{
683           //mark the quantified formula as relevant
684           d_qe->markRelevant( it->second.d_q );
685         }
686       }
687     }
688     Trace("qip-prop-debug") << std::endl;
689     Trace("quant-engine-conflict") << "-----> InstPropagator::" << ( d_conflict ? "conflict" : "propagate" ) << " with " << d_relevant_inst.size() << " instances." << std::endl;
690   }
691 }
692 
allocateInstantiation(Node q,Node lem,std::vector<Node> & terms,Node body)693 unsigned InstPropagator::allocateInstantiation( Node q, Node lem, std::vector< Node >& terms, Node body ) {
694   unsigned id = d_icount;
695   d_icount++;
696   Trace("qip-prop") << "...assign id=" << id << std::endl;
697   d_ii[id].init( q, lem, terms, body );
698   return id;
699 }
700 
update(unsigned id,InstInfo & ii,bool firstTime)701 bool InstPropagator::update( unsigned id, InstInfo& ii, bool firstTime ) {
702   Assert( !d_conflict );
703   Assert( ii.d_active );
704   Trace("qip-prop-debug") << "Update info [" << id << "]..." << std::endl;
705   //update the evaluation of the current lemma
706   std::map< Node, std::vector< Node > > watch_list_out;
707   std::map< int, std::map< Node, Node > > visited;
708   std::vector< Node > exp;
709   std::vector< Node > props;
710   Node eval = d_qy.evaluateTermExp( ii.d_curr, exp, visited, true, true, watch_list_out, props );
711   EqualityQueryInstProp::merge_exp( ii.d_curr_exp, exp );
712   if( eval.isNull() ){
713     ii.d_active = false;
714   }else if( firstTime || eval!=ii.d_curr ){
715     std::vector< Node > watch_list;
716     d_qy.collectWatchList( eval, watch_list_out, watch_list );
717     if( Trace.isOn("qip-prop") ){
718       Trace("qip-prop") << "Update info [" << id << "]..." << std::endl;
719       Trace("qip-prop") << "...updated lemma " << ii.d_curr << " -> " << eval << std::endl;
720       Trace("qip-prop") << "...explanation = ";
721       debugPrintExplanation( ii.d_curr_exp, "qip-prop" );
722       Trace("qip-prop") << std::endl;
723       Trace("qip-prop") << "...watch list: " << std::endl;
724       for( unsigned i=0; i<watch_list.size(); i++ ){
725         Trace("qip-prop") << "  " << watch_list[i] << std::endl;
726       }
727       Trace("qip-prop") << "...new propagations: " << std::endl;
728       for( unsigned i=0; i<props.size(); i++ ){
729         Trace("qip-prop") << "  " << props[i] << std::endl;
730       }
731       Trace("qip-prop") << std::endl;
732     }
733     //determine the status of eval
734     if( eval==d_qy.d_false ){
735       Assert( props.empty() );
736       //we have inferred a conflict
737       conflict( ii.d_curr_exp );
738       return false;
739     }else{
740       for( unsigned i=0; i<props.size(); i++ ){
741         Trace("qip-prop-debug2")  << "Process propagation " << props[i] << std::endl;
742         Assert( d_qy.isPropagateLiteral( props[i] ) );
743         //if we haven't propagated this literal yet
744         if( cacheConclusion( id, props[i], 1 ) ){
745           //watch list for propagated literal: may not yet be purely EE representatives
746           std::vector< Node > prop_watch_list;
747           d_qy.collectWatchList( props[i], watch_list_out, prop_watch_list );
748 
749           Node lit = props[i].getKind()==NOT ? props[i][0] : props[i];
750           bool pol = props[i].getKind()!=NOT;
751           if( lit.getKind()==EQUAL ){
752             propagate( lit[0], lit[1], pol, ii.d_curr_exp );
753           }else{
754             propagate( lit, pol ? d_qy.d_true : d_qy.d_false, true, ii.d_curr_exp );
755           }
756           if( d_conflict ){
757             return false;
758           }
759         }
760         Trace("qip-prop-debug2")  << "Done process propagation " << props[i] << std::endl;
761       }
762       //if we have not inferred this conclusion yet
763       if( cacheConclusion( id, eval ) ){
764         ii.d_curr = eval;
765         //update the watch list
766         Trace("qip-prop-debug") << "...updating watch list for [" << id << "], curr is " << ii.d_curr << std::endl;
767         //Here, we need to be notified of enough terms such that if we are not notified, then update( id, ii ) will return no propagations.
768         //  Similar to two-watched literals, but since we are taking into account UF, we need to watch all terms on a complete path of two terms.
769         for( unsigned i=0; i<watch_list.size(); i++ ){
770           d_watch_list[ watch_list[i] ][ id ] = true;
771         }
772       }else{
773         Trace("qip-prop-debug") << "...conclusion " << eval << " is duplicate." << std::endl;
774         ii.d_active = false;
775       }
776     }
777   }else{
778     Trace("qip-prop-debug") << "...did not update." << std::endl;
779   }
780   Assert( !d_conflict );
781   return true;
782 }
783 
propagate(Node a,Node b,bool pol,std::vector<Node> & exp)784 void InstPropagator::propagate( Node a, Node b, bool pol, std::vector< Node >& exp ) {
785   if( Trace.isOn("qip-propagate") ){
786     Trace("qip-propagate") << "* Propagate " << a << ( pol ? " == " : " != " ) << b << ", exp = ";
787     debugPrintExplanation( exp, "qip-propagate" );
788     Trace("qip-propagate") << "..." << std::endl;
789   }
790   //set equal
791   int status = d_qy.setEqual( a, b, pol, exp );
792   if( status==EqualityQueryInstProp::STATUS_NONE ){
793     Trace("qip-prop-debug") << "...already equal/no conflict." << std::endl;
794     return;
795   }else if( status==EqualityQueryInstProp::STATUS_CONFLICT ){
796     Trace("qip-prop-debug") << "...conflict." << std::endl;
797     conflict( exp );
798     return;
799   }
800   if( pol ){
801     if( status==EqualityQueryInstProp::STATUS_MERGED_KNOWN ){
802       Trace("qip-rlv-propagate")
803           << "Relevant propagation : " << a << " == " << b << std::endl;
804       Assert( d_qy.getEngine()->hasTerm( a ) );
805       Assert( d_qy.getEngine()->hasTerm( b ) );
806       Trace("qip-prop-debug") << "...equality between known terms." << std::endl;
807       addRelevantInstances( exp, "qip-propagate" );
808       //d_has_relevant_inst = true;
809     }
810     Trace("qip-prop-debug") << "...merged representatives " << a << " and " << b << std::endl;
811     for( unsigned i=0; i<2; i++ ){
812       //update terms from watched lists
813       Node c = i==0 ? a : b;
814       std::map< Node, std::map< unsigned, bool > >::iterator it = d_watch_list.find( c );
815       if( it!=d_watch_list.end() ){
816         Trace("qip-prop-debug") << "...update ids from watch list of " << c << ", size=" << it->second.size() << "..." << std::endl;
817         for( std::map< unsigned, bool >::iterator itw = it->second.begin(); itw != it->second.end(); ++itw ){
818           unsigned idw = itw->first;
819           if( std::find( d_update_list.begin(), d_update_list.end(), idw )==d_update_list.end() ){
820             Trace("qip-prop-debug") << "...will update " << idw << std::endl;
821             d_update_list.push_back( idw );
822           }
823         }
824         d_watch_list.erase( c );
825       }
826     }
827   }
828 }
829 
conflict(std::vector<Node> & exp)830 void InstPropagator::conflict( std::vector< Node >& exp ) {
831   Trace("qip-propagate") << "Conflict, exp size =" << exp.size() << std::endl;
832   d_conflict = true;
833   d_relevant_inst.clear();
834   addRelevantInstances( exp, "qip-propagate" );
835   d_has_relevant_inst = true;
836 }
837 
cacheConclusion(unsigned id,Node body,int prop_index)838 bool InstPropagator::cacheConclusion( unsigned id, Node body, int prop_index ) {
839   Assert( prop_index==0 || prop_index==1 );
840   //check if the conclusion is non-redundant
841   if( d_conc_to_id[prop_index].find( body )==d_conc_to_id[prop_index].end() ){
842     d_conc_to_id[prop_index][body] = id;
843     return true;
844   }else{
845     return false;
846   }
847 }
848 
addRelevantInstances(std::vector<Node> & exp,const char * c)849 void InstPropagator::addRelevantInstances( std::vector< Node >& exp, const char * c ) {
850   for( unsigned i=0; i<exp.size(); i++ ){
851     Assert( d_conc_to_id[0].find( exp[i] )!=d_conc_to_id[0].end() );
852     Trace(c) << "  relevant instance id : " << d_conc_to_id[0][ exp[i] ] << std::endl;
853     d_relevant_inst[ d_conc_to_id[0][ exp[i] ] ] = true;
854   }
855 }
856 
debugPrintExplanation(std::vector<Node> & exp,const char * c)857 void InstPropagator::debugPrintExplanation( std::vector< Node >& exp, const char * c ) {
858   for( unsigned i=0; i<exp.size(); i++ ){
859     Assert( d_conc_to_id[0].find( exp[i] )!=d_conc_to_id[0].end() );
860     Trace(c) << d_conc_to_id[0][ exp[i] ] << " ";
861   }
862 }
863 
864