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