1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Andrew Reynolds, Mathias Preiner, Aina Niemetz |
4 |
|
* |
5 |
|
* This file is part of the cvc5 project. |
6 |
|
* |
7 |
|
* Copyright (c) 2009-2021 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. |
11 |
|
* **************************************************************************** |
12 |
|
* |
13 |
|
* Implementation of relevant domain class. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "theory/quantifiers/relevant_domain.h" |
17 |
|
|
18 |
|
#include "expr/term_context.h" |
19 |
|
#include "expr/term_context_stack.h" |
20 |
|
#include "theory/arith/arith_msum.h" |
21 |
|
#include "theory/quantifiers/first_order_model.h" |
22 |
|
#include "theory/quantifiers/quantifiers_registry.h" |
23 |
|
#include "theory/quantifiers/quantifiers_state.h" |
24 |
|
#include "theory/quantifiers/term_database.h" |
25 |
|
#include "theory/quantifiers/term_registry.h" |
26 |
|
#include "theory/quantifiers/term_util.h" |
27 |
|
|
28 |
|
using namespace cvc5::kind; |
29 |
|
|
30 |
|
namespace cvc5 { |
31 |
|
namespace theory { |
32 |
|
namespace quantifiers { |
33 |
|
|
34 |
13348 |
void RelevantDomain::RDomain::merge( RDomain * r ) { |
35 |
13348 |
Assert(!d_parent); |
36 |
13348 |
Assert(!r->d_parent); |
37 |
13348 |
d_parent = r; |
38 |
13977 |
for( unsigned i=0; i<d_terms.size(); i++ ){ |
39 |
629 |
r->addTerm( d_terms[i] ); |
40 |
|
} |
41 |
13348 |
d_terms.clear(); |
42 |
13348 |
} |
43 |
|
|
44 |
12683 |
void RelevantDomain::RDomain::addTerm( Node t ) { |
45 |
12683 |
if( std::find( d_terms.begin(), d_terms.end(), t )==d_terms.end() ){ |
46 |
2020 |
d_terms.push_back( t ); |
47 |
|
} |
48 |
12683 |
} |
49 |
|
|
50 |
419911 |
RelevantDomain::RDomain * RelevantDomain::RDomain::getParent() { |
51 |
419911 |
if( !d_parent ){ |
52 |
208300 |
return this; |
53 |
|
}else{ |
54 |
211611 |
RDomain * p = d_parent->getParent(); |
55 |
211611 |
d_parent = p; |
56 |
211611 |
return p; |
57 |
|
} |
58 |
|
} |
59 |
|
|
60 |
764 |
void RelevantDomain::RDomain::removeRedundantTerms(QuantifiersState& qs) |
61 |
|
{ |
62 |
1528 |
std::map< Node, Node > rterms; |
63 |
2155 |
for( unsigned i=0; i<d_terms.size(); i++ ){ |
64 |
2782 |
Node r = d_terms[i]; |
65 |
1391 |
if( !TermUtil::hasInstConstAttr( d_terms[i] ) ){ |
66 |
1391 |
r = qs.getRepresentative(d_terms[i]); |
67 |
|
} |
68 |
1391 |
if( rterms.find( r )==rterms.end() ){ |
69 |
1274 |
rterms[r] = d_terms[i]; |
70 |
|
} |
71 |
|
} |
72 |
764 |
d_terms.clear(); |
73 |
2038 |
for( std::map< Node, Node >::iterator it = rterms.begin(); it != rterms.end(); ++it ){ |
74 |
1274 |
d_terms.push_back( it->second ); |
75 |
|
} |
76 |
764 |
} |
77 |
|
|
78 |
98 |
RelevantDomain::RelevantDomain(Env& env, |
79 |
|
QuantifiersState& qs, |
80 |
|
QuantifiersRegistry& qr, |
81 |
98 |
TermRegistry& tr) |
82 |
98 |
: QuantifiersUtil(env), d_qs(qs), d_qreg(qr), d_treg(tr) |
83 |
|
{ |
84 |
98 |
d_is_computed = false; |
85 |
98 |
} |
86 |
|
|
87 |
294 |
RelevantDomain::~RelevantDomain() { |
88 |
829 |
for( std::map< Node, std::map< int, RDomain * > >::iterator itr = d_rel_doms.begin(); itr != d_rel_doms.end(); ++itr ){ |
89 |
5892 |
for( std::map< int, RDomain * >::iterator itr2 = itr->second.begin(); itr2 != itr->second.end(); ++itr2 ){ |
90 |
5161 |
RDomain * current = (*itr2).second; |
91 |
5161 |
Assert(current != NULL); |
92 |
5161 |
delete current; |
93 |
|
} |
94 |
|
} |
95 |
196 |
} |
96 |
|
|
97 |
193961 |
RelevantDomain::RDomain * RelevantDomain::getRDomain( Node n, int i, bool getParent ) { |
98 |
193961 |
if( d_rel_doms.find( n )==d_rel_doms.end() || d_rel_doms[n].find( i )==d_rel_doms[n].end() ){ |
99 |
5161 |
d_rel_doms[n][i] = new RDomain; |
100 |
5161 |
d_rn_map[d_rel_doms[n][i]] = n; |
101 |
5161 |
d_ri_map[d_rel_doms[n][i]] = i; |
102 |
|
} |
103 |
193961 |
return getParent ? d_rel_doms[n][i]->getParent() : d_rel_doms[n][i]; |
104 |
|
} |
105 |
|
|
106 |
1185 |
bool RelevantDomain::reset( Theory::Effort e ) { |
107 |
1185 |
d_is_computed = false; |
108 |
1185 |
return true; |
109 |
|
} |
110 |
|
|
111 |
3732 |
void RelevantDomain::registerQuantifier(Node q) {} |
112 |
112 |
void RelevantDomain::compute(){ |
113 |
112 |
if( !d_is_computed ){ |
114 |
112 |
d_is_computed = true; |
115 |
1155 |
for( std::map< Node, std::map< int, RDomain * > >::iterator it = d_rel_doms.begin(); it != d_rel_doms.end(); ++it ){ |
116 |
10037 |
for( std::map< int, RDomain * >::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2 ){ |
117 |
8994 |
it2->second->reset(); |
118 |
|
} |
119 |
|
} |
120 |
112 |
FirstOrderModel* fm = d_treg.getModel(); |
121 |
1191 |
for( unsigned i=0; i<fm->getNumAssertedQuantifiers(); i++ ){ |
122 |
2158 |
Node q = fm->getAssertedQuantifier( i ); |
123 |
2158 |
Node icf = d_qreg.getInstConstantBody(q); |
124 |
1079 |
Trace("rel-dom-debug") << "compute relevant domain for " << icf << std::endl; |
125 |
1079 |
computeRelevantDomain(q); |
126 |
|
} |
127 |
|
|
128 |
112 |
Trace("rel-dom-debug") << "account for ground terms" << std::endl; |
129 |
112 |
TermDb* db = d_treg.getTermDatabase(); |
130 |
763 |
for (unsigned k = 0; k < db->getNumOperators(); k++) |
131 |
|
{ |
132 |
1302 |
Node op = db->getOperator(k); |
133 |
651 |
unsigned sz = db->getNumGroundTerms( op ); |
134 |
3297 |
for( unsigned i=0; i<sz; i++ ){ |
135 |
5292 |
Node n = db->getGroundTerm(op, i); |
136 |
|
//if it is a non-redundant term |
137 |
2646 |
if( db->isTermActive( n ) ){ |
138 |
13122 |
for( unsigned j=0; j<n.getNumChildren(); j++ ){ |
139 |
11137 |
RDomain * rf = getRDomain( op, j ); |
140 |
11137 |
rf->addTerm( n[j] ); |
141 |
11137 |
Trace("rel-dom-debug") << "...add ground term " << n[j] << " to rel dom " << op << "[" << j << "]" << std::endl; |
142 |
|
} |
143 |
|
} |
144 |
|
} |
145 |
|
} |
146 |
|
//print debug |
147 |
1867 |
for( std::map< Node, std::map< int, RDomain * > >::iterator it = d_rel_doms.begin(); it != d_rel_doms.end(); ++it ){ |
148 |
1755 |
Trace("rel-dom") << "Relevant domain for " << it->first << " : " << std::endl; |
149 |
15867 |
for( std::map< int, RDomain * >::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2 ){ |
150 |
14112 |
Trace("rel-dom") << " " << it2->first << " : "; |
151 |
14112 |
RDomain * r = it2->second; |
152 |
14112 |
RDomain * rp = r->getParent(); |
153 |
14112 |
if( r==rp ){ |
154 |
764 |
r->removeRedundantTerms(d_qs); |
155 |
2038 |
for( unsigned i=0; i<r->d_terms.size(); i++ ){ |
156 |
1274 |
Trace("rel-dom") << r->d_terms[i] << " "; |
157 |
|
} |
158 |
|
}else{ |
159 |
13348 |
Trace("rel-dom") << "Dom( " << d_rn_map[rp] << ", " << d_ri_map[rp] << " ) "; |
160 |
|
} |
161 |
14112 |
Trace("rel-dom") << std::endl; |
162 |
|
} |
163 |
|
} |
164 |
|
} |
165 |
112 |
} |
166 |
|
|
167 |
1079 |
void RelevantDomain::computeRelevantDomain(Node q) |
168 |
|
{ |
169 |
1079 |
Assert(q.getKind() == FORALL); |
170 |
2158 |
Node n = d_qreg.getInstConstantBody(q); |
171 |
|
// we care about polarity in the traversal, so we use a polarity term context |
172 |
2158 |
PolarityTermContext tc; |
173 |
2158 |
TCtxStack ctx(&tc); |
174 |
1079 |
ctx.pushInitial(n); |
175 |
|
std::unordered_set<std::pair<Node, uint32_t>, |
176 |
|
PairHashFunction<Node, uint32_t, std::hash<Node> > > |
177 |
2158 |
visited; |
178 |
2158 |
std::pair<Node, uint32_t> curr; |
179 |
2158 |
Node node; |
180 |
|
uint32_t nodeVal; |
181 |
|
std::unordered_set< |
182 |
|
std::pair<Node, uint32_t>, |
183 |
1079 |
PairHashFunction<Node, uint32_t, std::hash<Node> > >::const_iterator itc; |
184 |
|
bool hasPol, pol; |
185 |
202293 |
while (!ctx.empty()) |
186 |
|
{ |
187 |
100607 |
curr = ctx.getCurrent(); |
188 |
100607 |
itc = visited.find(curr); |
189 |
100607 |
ctx.pop(); |
190 |
100607 |
if (itc == visited.end()) |
191 |
|
{ |
192 |
31278 |
visited.insert(curr); |
193 |
31278 |
node = curr.first; |
194 |
|
// if not a quantified formula |
195 |
31278 |
if (!node.isClosure()) |
196 |
|
{ |
197 |
31179 |
nodeVal = curr.second; |
198 |
|
// get the polarity of the current term and process it |
199 |
31179 |
PolarityTermContext::getFlags(nodeVal, hasPol, pol); |
200 |
31179 |
computeRelevantDomainNode(q, node, hasPol, pol); |
201 |
|
// traverse the children |
202 |
31179 |
ctx.pushChildren(node, nodeVal); |
203 |
|
} |
204 |
|
} |
205 |
|
} |
206 |
1079 |
} |
207 |
|
|
208 |
31179 |
void RelevantDomain::computeRelevantDomainNode(Node q, |
209 |
|
Node n, |
210 |
|
bool hasPol, |
211 |
|
bool pol) |
212 |
|
{ |
213 |
31179 |
Trace("rel-dom-debug") << "Compute relevant domain " << n << "..." << std::endl; |
214 |
62358 |
Node op = d_treg.getTermDatabase()->getMatchOperator(n); |
215 |
31179 |
if (!op.isNull()) |
216 |
|
{ |
217 |
89077 |
for (size_t i = 0, nchild = n.getNumChildren(); i < nchild; i++) |
218 |
|
{ |
219 |
78683 |
RDomain * rf = getRDomain( op, i ); |
220 |
78683 |
if( n[i].getKind()==ITE ){ |
221 |
|
for( unsigned j=1; j<=2; j++ ){ |
222 |
|
computeRelevantDomainOpCh( rf, n[i][j] ); |
223 |
|
} |
224 |
|
}else{ |
225 |
78683 |
computeRelevantDomainOpCh( rf, n[i] ); |
226 |
|
} |
227 |
|
} |
228 |
|
} |
229 |
|
|
230 |
31179 |
if( ( ( n.getKind()==EQUAL && !n[0].getType().isBoolean() ) || n.getKind()==GEQ ) && TermUtil::hasInstConstAttr( n ) ){ |
231 |
|
//compute the information for what this literal does |
232 |
676 |
computeRelevantDomainLit( q, hasPol, pol, n ); |
233 |
676 |
if( d_rel_dom_lit[hasPol][pol][n].d_merge ){ |
234 |
112 |
Assert(d_rel_dom_lit[hasPol][pol][n].d_rd[0] != NULL |
235 |
|
&& d_rel_dom_lit[hasPol][pol][n].d_rd[1] != NULL); |
236 |
112 |
RDomain * rd1 = d_rel_dom_lit[hasPol][pol][n].d_rd[0]->getParent(); |
237 |
112 |
RDomain * rd2 = d_rel_dom_lit[hasPol][pol][n].d_rd[1]->getParent(); |
238 |
112 |
if( rd1!=rd2 ){ |
239 |
73 |
rd1->merge( rd2 ); |
240 |
|
} |
241 |
|
}else{ |
242 |
564 |
if( d_rel_dom_lit[hasPol][pol][n].d_rd[0]!=NULL ){ |
243 |
234 |
RDomain * rd = d_rel_dom_lit[hasPol][pol][n].d_rd[0]->getParent(); |
244 |
610 |
for( unsigned i=0; i<d_rel_dom_lit[hasPol][pol][n].d_val.size(); i++ ){ |
245 |
376 |
rd->addTerm( d_rel_dom_lit[hasPol][pol][n].d_val[i] ); |
246 |
|
} |
247 |
|
} |
248 |
|
} |
249 |
|
} |
250 |
31179 |
Trace("rel-dom-debug") << "...finished Compute relevant domain " << n << std::endl; |
251 |
31179 |
} |
252 |
|
|
253 |
78683 |
void RelevantDomain::computeRelevantDomainOpCh( RDomain * rf, Node n ) { |
254 |
78683 |
if( n.getKind()==INST_CONSTANT ){ |
255 |
155584 |
Node q = TermUtil::getInstConstAttr(n); |
256 |
|
//merge the RDomains |
257 |
77792 |
unsigned id = n.getAttribute(InstVarNumAttribute()); |
258 |
77792 |
Assert(q[0][id].getType() == n.getType()); |
259 |
77792 |
Trace("rel-dom-debug") << n << " is variable # " << id << " for " << q; |
260 |
155584 |
Trace("rel-dom-debug") << " with body : " << d_qreg.getInstConstantBody(q) |
261 |
77792 |
<< std::endl; |
262 |
77792 |
RDomain * rq = getRDomain( q, id ); |
263 |
77792 |
if( rf!=rq ){ |
264 |
13275 |
rq->merge( rf ); |
265 |
|
} |
266 |
891 |
}else if( !TermUtil::hasInstConstAttr( n ) ){ |
267 |
541 |
Trace("rel-dom-debug") << "...add ground term to rel dom " << n << std::endl; |
268 |
|
//term to add |
269 |
541 |
rf->addTerm( n ); |
270 |
|
} |
271 |
78683 |
} |
272 |
|
|
273 |
676 |
void RelevantDomain::computeRelevantDomainLit( Node q, bool hasPol, bool pol, Node n ) { |
274 |
676 |
if( d_rel_dom_lit[hasPol][pol].find( n )==d_rel_dom_lit[hasPol][pol].end() ){ |
275 |
359 |
d_rel_dom_lit[hasPol][pol][n].d_merge = false; |
276 |
359 |
int varCount = 0; |
277 |
359 |
int varCh = -1; |
278 |
1077 |
for( unsigned i=0; i<n.getNumChildren(); i++ ){ |
279 |
718 |
if( n[i].getKind()==INST_CONSTANT ){ |
280 |
|
// must get the quantified formula this belongs to, which may be |
281 |
|
// different from q |
282 |
420 |
Node qi = TermUtil::getInstConstAttr(n[i]); |
283 |
210 |
unsigned id = n[i].getAttribute(InstVarNumAttribute()); |
284 |
210 |
d_rel_dom_lit[hasPol][pol][n].d_rd[i] = getRDomain(qi, id, false); |
285 |
210 |
varCount++; |
286 |
210 |
varCh = i; |
287 |
|
}else{ |
288 |
508 |
d_rel_dom_lit[hasPol][pol][n].d_rd[i] = NULL; |
289 |
|
} |
290 |
|
} |
291 |
|
|
292 |
718 |
Node r_add; |
293 |
359 |
bool varLhs = true; |
294 |
359 |
if( varCount==2 ){ |
295 |
15 |
d_rel_dom_lit[hasPol][pol][n].d_merge = true; |
296 |
|
}else{ |
297 |
344 |
if( varCount==1 ){ |
298 |
180 |
r_add = n[1-varCh]; |
299 |
180 |
varLhs = (varCh==0); |
300 |
180 |
d_rel_dom_lit[hasPol][pol][n].d_rd[0] = d_rel_dom_lit[hasPol][pol][n].d_rd[varCh]; |
301 |
180 |
d_rel_dom_lit[hasPol][pol][n].d_rd[1] = NULL; |
302 |
|
}else{ |
303 |
|
//solve the inequality for one/two variables, if possible |
304 |
164 |
if( n[0].getType().isReal() ){ |
305 |
262 |
std::map< Node, Node > msum; |
306 |
131 |
if (ArithMSum::getMonomialSumLit(n, msum)) |
307 |
|
{ |
308 |
262 |
Node var; |
309 |
262 |
Node var2; |
310 |
131 |
bool hasNonVar = false; |
311 |
392 |
for( std::map< Node, Node >::iterator it = msum.begin(); it != msum.end(); ++it ){ |
312 |
762 |
if (!it->first.isNull() && it->first.getKind() == INST_CONSTANT |
313 |
577 |
&& TermUtil::getInstConstAttr(it->first) == q) |
314 |
|
{ |
315 |
55 |
if( var.isNull() ){ |
316 |
33 |
var = it->first; |
317 |
22 |
}else if( var2.isNull() ){ |
318 |
22 |
var2 = it->first; |
319 |
|
}else{ |
320 |
|
hasNonVar = true; |
321 |
|
} |
322 |
|
}else{ |
323 |
206 |
hasNonVar = true; |
324 |
|
} |
325 |
|
} |
326 |
131 |
if( !var.isNull() ){ |
327 |
33 |
if( var2.isNull() ){ |
328 |
|
//single variable solve |
329 |
22 |
Node veq_c; |
330 |
22 |
Node val; |
331 |
|
int ires = |
332 |
11 |
ArithMSum::isolate(var, msum, veq_c, val, n.getKind()); |
333 |
11 |
if( ires!=0 ){ |
334 |
11 |
if( veq_c.isNull() ){ |
335 |
3 |
r_add = val; |
336 |
3 |
varLhs = (ires==1); |
337 |
3 |
d_rel_dom_lit[hasPol][pol][n].d_rd[0] = getRDomain( q, var.getAttribute(InstVarNumAttribute()), false ); |
338 |
3 |
d_rel_dom_lit[hasPol][pol][n].d_rd[1] = NULL; |
339 |
|
} |
340 |
|
} |
341 |
22 |
}else if( !hasNonVar ){ |
342 |
|
//merge the domains |
343 |
9 |
d_rel_dom_lit[hasPol][pol][n].d_rd[0] = getRDomain( q, var.getAttribute(InstVarNumAttribute()), false ); |
344 |
9 |
d_rel_dom_lit[hasPol][pol][n].d_rd[1] = getRDomain( q, var2.getAttribute(InstVarNumAttribute()), false ); |
345 |
9 |
d_rel_dom_lit[hasPol][pol][n].d_merge = true; |
346 |
|
} |
347 |
|
} |
348 |
|
} |
349 |
|
} |
350 |
|
} |
351 |
|
} |
352 |
359 |
if( d_rel_dom_lit[hasPol][pol][n].d_merge ){ |
353 |
|
//do not merge if constant negative polarity |
354 |
24 |
if( hasPol && !pol ){ |
355 |
|
d_rel_dom_lit[hasPol][pol][n].d_merge = false; |
356 |
|
} |
357 |
335 |
}else if( !r_add.isNull() && !TermUtil::hasInstConstAttr( r_add ) ){ |
358 |
164 |
Trace("rel-dom-debug") << "...add term " << r_add << ", pol = " << pol << ", kind = " << n.getKind() << std::endl; |
359 |
|
//the negative occurrence adds the term to the domain |
360 |
164 |
if( !hasPol || !pol ){ |
361 |
164 |
d_rel_dom_lit[hasPol][pol][n].d_val.push_back( r_add ); |
362 |
|
} |
363 |
|
//the positive occurence adds other terms |
364 |
164 |
if( ( !hasPol || pol ) && n[0].getType().isInteger() ){ |
365 |
138 |
if( n.getKind()==EQUAL ){ |
366 |
|
for( unsigned i=0; i<2; i++ ){ |
367 |
|
d_rel_dom_lit[hasPol][pol][n].d_val.push_back( |
368 |
|
ArithMSum::offset(r_add, i == 0 ? 1 : -1)); |
369 |
|
} |
370 |
138 |
}else if( n.getKind()==GEQ ){ |
371 |
276 |
d_rel_dom_lit[hasPol][pol][n].d_val.push_back( |
372 |
276 |
ArithMSum::offset(r_add, varLhs ? 1 : -1)); |
373 |
|
} |
374 |
|
} |
375 |
|
}else{ |
376 |
171 |
d_rel_dom_lit[hasPol][pol][n].d_rd[0] = NULL; |
377 |
171 |
d_rel_dom_lit[hasPol][pol][n].d_rd[1] = NULL; |
378 |
|
} |
379 |
|
} |
380 |
676 |
} |
381 |
|
|
382 |
|
} // namespace quantifiers |
383 |
|
} // namespace theory |
384 |
29517 |
} // namespace cvc5 |