1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Andrew Reynolds, Andres Noetzli, Mathias Preiner |
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 |
|
* Bounded integers module |
14 |
|
* |
15 |
|
* This class manages integer bounds for quantifiers. |
16 |
|
*/ |
17 |
|
|
18 |
|
#include "theory/quantifiers/fmf/bounded_integers.h" |
19 |
|
|
20 |
|
#include "expr/dtype_cons.h" |
21 |
|
#include "expr/node_algorithm.h" |
22 |
|
#include "expr/skolem_manager.h" |
23 |
|
#include "options/quantifiers_options.h" |
24 |
|
#include "theory/arith/arith_msum.h" |
25 |
|
#include "theory/datatypes/theory_datatypes_utils.h" |
26 |
|
#include "theory/decision_manager.h" |
27 |
|
#include "theory/quantifiers/first_order_model.h" |
28 |
|
#include "theory/quantifiers/fmf/model_engine.h" |
29 |
|
#include "theory/quantifiers/term_enumeration.h" |
30 |
|
#include "theory/quantifiers/term_util.h" |
31 |
|
#include "theory/rewriter.h" |
32 |
|
#include "util/rational.h" |
33 |
|
|
34 |
|
using namespace cvc5::kind; |
35 |
|
|
36 |
|
namespace cvc5 { |
37 |
|
namespace theory { |
38 |
|
namespace quantifiers { |
39 |
|
|
40 |
702 |
BoundedIntegers::IntRangeDecisionHeuristic::IntRangeDecisionHeuristic( |
41 |
702 |
Env& env, Node r, Valuation valuation, bool isProxy) |
42 |
|
: DecisionStrategyFmf(env, valuation), |
43 |
|
d_range(r), |
44 |
702 |
d_ranges_proxied(userContext()) |
45 |
|
{ |
46 |
702 |
if( options::fmfBoundLazy() ){ |
47 |
4 |
SkolemManager* sm = NodeManager::currentNM()->getSkolemManager(); |
48 |
4 |
d_proxy_range = isProxy ? r : sm->mkDummySkolem("pbir", r.getType()); |
49 |
|
}else{ |
50 |
698 |
d_proxy_range = r; |
51 |
|
} |
52 |
702 |
if( !isProxy ){ |
53 |
604 |
Trace("bound-int") << "Introduce proxy " << d_proxy_range << " for " << d_range << std::endl; |
54 |
|
} |
55 |
702 |
} |
56 |
2085 |
Node BoundedIntegers::IntRangeDecisionHeuristic::mkLiteral(unsigned n) |
57 |
|
{ |
58 |
2085 |
NodeManager* nm = NodeManager::currentNM(); |
59 |
4170 |
Node cn = nm->mkConst(Rational(n == 0 ? 0 : n - 1)); |
60 |
4170 |
return nm->mkNode(n == 0 ? LT : LEQ, d_proxy_range, cn); |
61 |
|
} |
62 |
|
|
63 |
2021 |
Node BoundedIntegers::IntRangeDecisionHeuristic::proxyCurrentRangeLemma() |
64 |
|
{ |
65 |
2021 |
if (d_range == d_proxy_range) |
66 |
|
{ |
67 |
2017 |
return Node::null(); |
68 |
|
} |
69 |
4 |
unsigned curr = 0; |
70 |
4 |
if (!getAssertedLiteralIndex(curr)) |
71 |
|
{ |
72 |
|
return Node::null(); |
73 |
|
} |
74 |
4 |
if (d_ranges_proxied.find(curr) != d_ranges_proxied.end()) |
75 |
|
{ |
76 |
|
return Node::null(); |
77 |
|
} |
78 |
4 |
d_ranges_proxied[curr] = true; |
79 |
4 |
NodeManager* nm = NodeManager::currentNM(); |
80 |
8 |
Node currLit = getLiteral(curr); |
81 |
|
Node lem = |
82 |
|
nm->mkNode(EQUAL, |
83 |
|
currLit, |
84 |
8 |
nm->mkNode(curr == 0 ? LT : LEQ, |
85 |
|
d_range, |
86 |
16 |
nm->mkConst(Rational(curr == 0 ? 0 : curr - 1)))); |
87 |
4 |
return lem; |
88 |
|
} |
89 |
|
|
90 |
1302 |
BoundedIntegers::BoundedIntegers(Env& env, |
91 |
|
QuantifiersState& qs, |
92 |
|
QuantifiersInferenceManager& qim, |
93 |
|
QuantifiersRegistry& qr, |
94 |
1302 |
TermRegistry& tr) |
95 |
1302 |
: QuantifiersModule(env, qs, qim, qr, tr) |
96 |
|
{ |
97 |
1302 |
} |
98 |
|
|
99 |
2604 |
BoundedIntegers::~BoundedIntegers() {} |
100 |
|
|
101 |
1447 |
void BoundedIntegers::presolve() { |
102 |
1447 |
d_bnd_it.clear(); |
103 |
1447 |
} |
104 |
|
|
105 |
11892 |
bool BoundedIntegers::hasNonBoundVar( Node f, Node b, std::map< Node, bool >& visited ) { |
106 |
11892 |
if( visited.find( b )==visited.end() ){ |
107 |
11110 |
visited[b] = true; |
108 |
11110 |
if( b.getKind()==BOUND_VARIABLE ){ |
109 |
1195 |
if( !isBound( f, b ) ){ |
110 |
624 |
return true; |
111 |
|
} |
112 |
|
}else{ |
113 |
17533 |
for( unsigned i=0; i<b.getNumChildren(); i++ ){ |
114 |
8470 |
if( hasNonBoundVar( f, b[i], visited ) ){ |
115 |
852 |
return true; |
116 |
|
} |
117 |
|
} |
118 |
|
} |
119 |
|
} |
120 |
10416 |
return false; |
121 |
|
} |
122 |
3422 |
bool BoundedIntegers::hasNonBoundVar( Node f, Node b ) { |
123 |
6844 |
std::map< Node, bool > visited; |
124 |
6844 |
return hasNonBoundVar( f, b, visited ); |
125 |
|
} |
126 |
|
|
127 |
787 |
bool BoundedIntegers::processEqDisjunct( Node q, Node n, Node& v, std::vector< Node >& v_cases ) { |
128 |
787 |
if( n.getKind()==EQUAL ){ |
129 |
2279 |
for( unsigned i=0; i<2; i++ ){ |
130 |
3038 |
Node t = n[i]; |
131 |
1546 |
if( !hasNonBoundVar( q, n[1-i] ) ){ |
132 |
1112 |
if( t==v ){ |
133 |
24 |
v_cases.push_back( n[1-i] ); |
134 |
24 |
return true; |
135 |
1088 |
}else if( v.isNull() && t.getKind()==BOUND_VARIABLE ){ |
136 |
30 |
v = t; |
137 |
30 |
v_cases.push_back( n[1-i] ); |
138 |
30 |
return true; |
139 |
|
} |
140 |
|
} |
141 |
|
} |
142 |
|
} |
143 |
733 |
return false; |
144 |
|
} |
145 |
|
|
146 |
116 |
void BoundedIntegers::processMatchBoundVars( Node q, Node n, std::vector< Node >& bvs, std::map< Node, bool >& visited ){ |
147 |
116 |
if( visited.find( n )==visited.end() ){ |
148 |
116 |
visited[n] = true; |
149 |
116 |
if( n.getKind()==BOUND_VARIABLE && !isBound( q, n ) ){ |
150 |
45 |
bvs.push_back( n ); |
151 |
|
//injective operators |
152 |
71 |
}else if( n.getKind()==kind::APPLY_CONSTRUCTOR ){ |
153 |
60 |
for( unsigned i=0; i<n.getNumChildren(); i++ ){ |
154 |
36 |
processMatchBoundVars( q, n[i], bvs, visited ); |
155 |
|
} |
156 |
|
} |
157 |
|
} |
158 |
116 |
} |
159 |
|
|
160 |
10554 |
void BoundedIntegers::process( Node q, Node n, bool pol, |
161 |
|
std::map< Node, unsigned >& bound_lit_type_map, |
162 |
|
std::map< int, std::map< Node, Node > >& bound_lit_map, |
163 |
|
std::map< int, std::map< Node, bool > >& bound_lit_pol_map, |
164 |
|
std::map< int, std::map< Node, Node > >& bound_int_range_term, |
165 |
|
std::map< Node, std::vector< Node > >& bound_fixed_set ){ |
166 |
10554 |
if( n.getKind()==OR || n.getKind()==AND ){ |
167 |
1918 |
if( (n.getKind()==OR)==pol ){ |
168 |
7279 |
for( unsigned i=0; i<n.getNumChildren(); i++) { |
169 |
5727 |
process( q, n[i], pol, bound_lit_type_map, bound_lit_map, bound_lit_pol_map, bound_int_range_term, bound_fixed_set ); |
170 |
|
} |
171 |
|
}else{ |
172 |
|
//if we are ( x != t1 ^ ...^ x != tn ), then x can be bound to { t1...tn } |
173 |
732 |
Node conj = n; |
174 |
366 |
if( !pol ){ |
175 |
|
conj = TermUtil::simpleNegate( conj ); |
176 |
|
} |
177 |
366 |
Trace("bound-int-debug") << "Process possible finite disequality conjunction : " << conj << std::endl; |
178 |
366 |
Assert(conj.getKind() == AND); |
179 |
732 |
Node v; |
180 |
732 |
std::vector< Node > v_cases; |
181 |
366 |
bool success = true; |
182 |
416 |
for( unsigned i=0; i<conj.getNumChildren(); i++ ){ |
183 |
392 |
if( conj[i].getKind()==NOT && processEqDisjunct( q, conj[i][0], v, v_cases ) ){ |
184 |
|
//continue |
185 |
|
}else{ |
186 |
342 |
Trace("bound-int-debug") << "...failed due to " << conj[i] << std::endl; |
187 |
342 |
success = false; |
188 |
342 |
break; |
189 |
|
} |
190 |
|
} |
191 |
366 |
if( success && !isBound( q, v ) ){ |
192 |
10 |
Trace("bound-int-debug") << "Success with variable " << v << std::endl; |
193 |
10 |
bound_lit_type_map[v] = BOUND_FIXED_SET; |
194 |
10 |
bound_lit_map[3][v] = n; |
195 |
10 |
bound_lit_pol_map[3][v] = pol; |
196 |
10 |
bound_fixed_set[v].clear(); |
197 |
10 |
bound_fixed_set[v].insert( bound_fixed_set[v].end(), v_cases.begin(), v_cases.end() ); |
198 |
|
} |
199 |
|
} |
200 |
8636 |
}else if( n.getKind()==EQUAL ){ |
201 |
1279 |
if( !pol ){ |
202 |
|
// non-applied DER on x != t, x can be bound to { t } |
203 |
1354 |
Node v; |
204 |
1354 |
std::vector< Node > v_cases; |
205 |
677 |
if( processEqDisjunct( q, n, v, v_cases ) ){ |
206 |
4 |
if( !isBound( q, v ) ){ |
207 |
2 |
bound_lit_type_map[v] = BOUND_FIXED_SET; |
208 |
2 |
bound_lit_map[3][v] = n; |
209 |
2 |
bound_lit_pol_map[3][v] = pol; |
210 |
2 |
Assert(v_cases.size() == 1); |
211 |
2 |
bound_fixed_set[v].clear(); |
212 |
2 |
bound_fixed_set[v].push_back( v_cases[0] ); |
213 |
|
} |
214 |
|
} |
215 |
|
} |
216 |
7357 |
}else if( n.getKind()==NOT ){ |
217 |
3109 |
process( q, n[0], !pol, bound_lit_type_map, bound_lit_map, bound_lit_pol_map, bound_int_range_term, bound_fixed_set ); |
218 |
4248 |
}else if( n.getKind()==GEQ ){ |
219 |
3522 |
if( n[0].getType().isInteger() ){ |
220 |
7044 |
std::map< Node, Node > msum; |
221 |
3522 |
if (ArithMSum::getMonomialSumLit(n, msum)) |
222 |
|
{ |
223 |
3522 |
Trace("bound-int-debug") << "literal (polarity = " << pol << ") " << n << " is monomial sum : " << std::endl; |
224 |
3522 |
ArithMSum::debugPrintMonomialSum(msum, "bound-int-debug"); |
225 |
10892 |
for( std::map< Node, Node >::iterator it = msum.begin(); it != msum.end(); ++it ){ |
226 |
7370 |
if ( !it->first.isNull() && it->first.getKind()==BOUND_VARIABLE && !isBound( q, it->first ) ){ |
227 |
|
//if not bound in another way |
228 |
1799 |
if( bound_lit_type_map.find( it->first )==bound_lit_type_map.end() || bound_lit_type_map[it->first] == BOUND_INT_RANGE ){ |
229 |
3584 |
Node veq; |
230 |
1792 |
if (ArithMSum::isolate(it->first, msum, veq, GEQ) != 0) |
231 |
|
{ |
232 |
3584 |
Node n1 = veq[0]; |
233 |
3584 |
Node n2 = veq[1]; |
234 |
1792 |
if(pol){ |
235 |
|
//flip |
236 |
877 |
n1 = veq[1]; |
237 |
877 |
n2 = veq[0]; |
238 |
877 |
if( n1.getKind()==BOUND_VARIABLE ){ |
239 |
2 |
n2 = ArithMSum::offset(n2, 1); |
240 |
|
}else{ |
241 |
875 |
n1 = ArithMSum::offset(n1, -1); |
242 |
|
} |
243 |
877 |
veq = NodeManager::currentNM()->mkNode( GEQ, n1, n2 ); |
244 |
|
} |
245 |
1792 |
Trace("bound-int-debug") << "Isolated for " << it->first << " : (" << n1 << " >= " << n2 << ")" << std::endl; |
246 |
3584 |
Node t = n1==it->first ? n2 : n1; |
247 |
1792 |
if( !hasNonBoundVar( q, t ) ) { |
248 |
1606 |
Trace("bound-int-debug") << "The bound is relevant." << std::endl; |
249 |
1606 |
int loru = n1==it->first ? 0 : 1; |
250 |
1606 |
bound_lit_type_map[it->first] = BOUND_INT_RANGE; |
251 |
1606 |
bound_int_range_term[loru][it->first] = t; |
252 |
1606 |
bound_lit_map[loru][it->first] = n; |
253 |
1606 |
bound_lit_pol_map[loru][it->first] = pol; |
254 |
|
}else{ |
255 |
186 |
Trace("bound-int-debug") << "The term " << t << " has non-bound variable." << std::endl; |
256 |
|
} |
257 |
|
} |
258 |
|
} |
259 |
|
} |
260 |
|
} |
261 |
|
} |
262 |
|
} |
263 |
726 |
}else if( n.getKind()==MEMBER ){ |
264 |
84 |
if( !pol && !hasNonBoundVar( q, n[1] ) ){ |
265 |
160 |
std::vector< Node > bound_vars; |
266 |
160 |
std::map< Node, bool > visited; |
267 |
80 |
processMatchBoundVars( q, n[0], bound_vars, visited ); |
268 |
125 |
for( unsigned i=0; i<bound_vars.size(); i++ ){ |
269 |
90 |
Node v = bound_vars[i]; |
270 |
45 |
Trace("bound-int-debug") << "literal (polarity = " << pol << ") " << n << " is membership." << std::endl; |
271 |
45 |
bound_lit_type_map[v] = BOUND_SET_MEMBER; |
272 |
45 |
bound_lit_map[2][v] = n; |
273 |
45 |
bound_lit_pol_map[2][v] = pol; |
274 |
|
} |
275 |
|
} |
276 |
|
}else{ |
277 |
642 |
Assert(n.getKind() != LEQ && n.getKind() != LT && n.getKind() != GT); |
278 |
|
} |
279 |
10554 |
} |
280 |
|
|
281 |
14809 |
bool BoundedIntegers::needsCheck( Theory::Effort e ) { |
282 |
14809 |
return e==Theory::EFFORT_LAST_CALL; |
283 |
|
} |
284 |
|
|
285 |
3472 |
void BoundedIntegers::check(Theory::Effort e, QEffort quant_e) |
286 |
|
{ |
287 |
3472 |
if (quant_e != QEFFORT_STANDARD) |
288 |
|
{ |
289 |
2306 |
return; |
290 |
|
} |
291 |
1166 |
Trace("bint-engine") << "---Bounded Integers---" << std::endl; |
292 |
1166 |
bool addedLemma = false; |
293 |
|
// make sure proxies are up-to-date with range |
294 |
3187 |
for (const Node& r : d_ranges) |
295 |
|
{ |
296 |
4042 |
Node prangeLem = d_rms[r]->proxyCurrentRangeLemma(); |
297 |
2021 |
if (!prangeLem.isNull()) |
298 |
|
{ |
299 |
8 |
Trace("bound-int-lemma") |
300 |
4 |
<< "*** bound int : proxy lemma : " << prangeLem << std::endl; |
301 |
4 |
d_qim.addPendingLemma(prangeLem, InferenceId::QUANTIFIERS_BINT_PROXY); |
302 |
4 |
addedLemma = true; |
303 |
|
} |
304 |
|
} |
305 |
1166 |
Trace("bint-engine") << " addedLemma = " << addedLemma << std::endl; |
306 |
|
} |
307 |
930 |
void BoundedIntegers::setBoundedVar(Node q, Node v, BoundVarType bound_type) |
308 |
|
{ |
309 |
930 |
d_bound_type[q][v] = bound_type; |
310 |
930 |
d_set_nums[q][v] = d_set[q].size(); |
311 |
930 |
d_set[q].push_back( v ); |
312 |
1860 |
Trace("bound-int-var") << "Bound variable #" << d_set_nums[q][v] << " : " << v |
313 |
930 |
<< std::endl; |
314 |
930 |
} |
315 |
|
|
316 |
942 |
void BoundedIntegers::checkOwnership(Node f) |
317 |
|
{ |
318 |
|
//this needs to be done at preregister since it affects e.g. QuantDSplit's preregister |
319 |
942 |
Trace("bound-int") << "check ownership quantifier " << f << std::endl; |
320 |
|
|
321 |
|
// determine if we should look at the quantified formula at all |
322 |
942 |
if (!options::fmfBound()) |
323 |
|
{ |
324 |
|
// only applying it to internal quantifiers |
325 |
729 |
QuantAttributes& qattr = d_qreg.getQuantAttributes(); |
326 |
729 |
if (!qattr.isQuantBounded(f)) |
327 |
|
{ |
328 |
138 |
Trace("bound-int") << "...not bounded, skip" << std::endl; |
329 |
138 |
return; |
330 |
|
} |
331 |
|
} |
332 |
|
|
333 |
804 |
NodeManager* nm = NodeManager::currentNM(); |
334 |
804 |
SkolemManager* sm = nm->getSkolemManager(); |
335 |
|
|
336 |
|
bool success; |
337 |
1718 |
do{ |
338 |
3436 |
std::map< Node, unsigned > bound_lit_type_map; |
339 |
3436 |
std::map< int, std::map< Node, Node > > bound_lit_map; |
340 |
3436 |
std::map< int, std::map< Node, bool > > bound_lit_pol_map; |
341 |
3436 |
std::map< int, std::map< Node, Node > > bound_int_range_term; |
342 |
3436 |
std::map< Node, std::vector< Node > > bound_fixed_set; |
343 |
1718 |
success = false; |
344 |
1718 |
process( f, f[1], true, bound_lit_type_map, bound_lit_map, bound_lit_pol_map, bound_int_range_term, bound_fixed_set ); |
345 |
|
//for( std::map< Node, Node >::iterator it = d_bounds[0][f].begin(); it != d_bounds[0][f].end(); ++it ){ |
346 |
2604 |
for( std::map< Node, unsigned >::iterator it = bound_lit_type_map.begin(); it != bound_lit_type_map.end(); ++it ){ |
347 |
1772 |
Node v = it->first; |
348 |
886 |
if( !isBound( f, v ) ){ |
349 |
886 |
bool setBoundVar = false; |
350 |
886 |
if( it->second==BOUND_INT_RANGE ){ |
351 |
|
//must have both |
352 |
837 |
std::map<Node, Node>& blm0 = bound_lit_map[0]; |
353 |
837 |
std::map<Node, Node>& blm1 = bound_lit_map[1]; |
354 |
837 |
if (blm0.find(v) != blm0.end() && blm1.find(v) != blm1.end()) |
355 |
|
{ |
356 |
739 |
setBoundedVar( f, v, BOUND_INT_RANGE ); |
357 |
739 |
setBoundVar = true; |
358 |
2217 |
for( unsigned b=0; b<2; b++ ){ |
359 |
|
//set the bounds |
360 |
1478 |
Assert(bound_int_range_term[b].find(v) |
361 |
|
!= bound_int_range_term[b].end()); |
362 |
1478 |
d_bounds[b][f][v] = bound_int_range_term[b][v]; |
363 |
|
} |
364 |
1478 |
Node r = nm->mkNode(MINUS, d_bounds[1][f][v], d_bounds[0][f][v]); |
365 |
739 |
d_range[f][v] = rewrite(r); |
366 |
739 |
Trace("bound-int") << "Variable " << v << " is bound because of int range literals " << bound_lit_map[0][v] << " and " << bound_lit_map[1][v] << std::endl; |
367 |
|
} |
368 |
49 |
}else if( it->second==BOUND_SET_MEMBER ){ |
369 |
|
// only handles infinite element types currently (cardinality is not |
370 |
|
// supported for finite element types #1123). Regardless, this is |
371 |
|
// typically not a limitation since this variable can be bound in a |
372 |
|
// standard way below since its type is finite. |
373 |
37 |
if (!d_env.isFiniteType(v.getType())) |
374 |
|
{ |
375 |
37 |
setBoundedVar(f, v, BOUND_SET_MEMBER); |
376 |
37 |
setBoundVar = true; |
377 |
37 |
d_setm_range[f][v] = bound_lit_map[2][v][1]; |
378 |
37 |
d_setm_range_lit[f][v] = bound_lit_map[2][v]; |
379 |
37 |
d_range[f][v] = nm->mkNode(CARD, d_setm_range[f][v]); |
380 |
74 |
Trace("bound-int") << "Variable " << v |
381 |
37 |
<< " is bound because of set membership literal " |
382 |
37 |
<< bound_lit_map[2][v] << std::endl; |
383 |
|
} |
384 |
12 |
}else if( it->second==BOUND_FIXED_SET ){ |
385 |
12 |
setBoundedVar(f, v, BOUND_FIXED_SET); |
386 |
12 |
setBoundVar = true; |
387 |
34 |
for (unsigned i = 0; i < bound_fixed_set[v].size(); i++) |
388 |
|
{ |
389 |
44 |
Node t = bound_fixed_set[v][i]; |
390 |
22 |
if (expr::hasBoundVar(t)) |
391 |
|
{ |
392 |
6 |
d_fixed_set_ngr_range[f][v].push_back(t); |
393 |
|
} |
394 |
|
else |
395 |
|
{ |
396 |
16 |
d_fixed_set_gr_range[f][v].push_back(t); |
397 |
|
} |
398 |
|
} |
399 |
24 |
Trace("bound-int") << "Variable " << v |
400 |
12 |
<< " is bound because of disequality conjunction " |
401 |
12 |
<< bound_lit_map[3][v] << std::endl; |
402 |
|
} |
403 |
886 |
if( setBoundVar ){ |
404 |
788 |
success = true; |
405 |
|
//set Attributes on literals |
406 |
2364 |
for( unsigned b=0; b<2; b++ ){ |
407 |
1576 |
std::map<Node, Node>& blm = bound_lit_map[b]; |
408 |
1576 |
if (blm.find(v) != blm.end()) |
409 |
|
{ |
410 |
1478 |
std::map<Node, bool>& blmp = bound_lit_pol_map[b]; |
411 |
|
// WARNING_CANDIDATE: |
412 |
|
// This assertion may fail. We intentionally do not enable this in |
413 |
|
// production as it is considered safe for this to fail. We fail |
414 |
|
// the assertion in debug mode to have this instance raised to |
415 |
|
// our attention. |
416 |
1478 |
Assert(blmp.find(v) != blmp.end()); |
417 |
|
BoundIntLitAttribute bila; |
418 |
1478 |
bound_lit_map[b][v].setAttribute(bila, blmp[v] ? 1 : 0); |
419 |
|
} |
420 |
|
else |
421 |
|
{ |
422 |
98 |
Assert(it->second != BOUND_INT_RANGE); |
423 |
|
} |
424 |
|
} |
425 |
|
} |
426 |
|
} |
427 |
|
} |
428 |
1718 |
if( !success ){ |
429 |
|
//resort to setting a finite bound on a variable |
430 |
1930 |
for( unsigned i=0; i<f[0].getNumChildren(); i++) { |
431 |
1126 |
if( d_bound_type[f].find( f[0][i] )==d_bound_type[f].end() ){ |
432 |
198 |
TypeNode tn = f[0][i].getType(); |
433 |
390 |
if ((tn.isSort() && d_env.isFiniteType(tn)) |
434 |
466 |
|| d_qreg.getQuantifiersBoundInference().mayComplete(tn)) |
435 |
|
{ |
436 |
142 |
success = true; |
437 |
142 |
setBoundedVar( f, f[0][i], BOUND_FINITE ); |
438 |
142 |
break; |
439 |
|
} |
440 |
|
} |
441 |
|
} |
442 |
|
} |
443 |
|
}while( success ); |
444 |
|
|
445 |
804 |
if( Trace.isOn("bound-int") ){ |
446 |
|
Trace("bound-int") << "Bounds are : " << std::endl; |
447 |
|
for( unsigned i=0; i<f[0].getNumChildren(); i++) { |
448 |
|
Node v = f[0][i]; |
449 |
|
if( std::find( d_set[f].begin(), d_set[f].end(), v )!=d_set[f].end() ){ |
450 |
|
Assert(d_bound_type[f].find(v) != d_bound_type[f].end()); |
451 |
|
if( d_bound_type[f][v]==BOUND_INT_RANGE ){ |
452 |
|
Trace("bound-int") << " " << d_bounds[0][f][v] << " <= " << v << " <= " << d_bounds[1][f][v] << " (range is " << d_range[f][v] << ")" << std::endl; |
453 |
|
}else if( d_bound_type[f][v]==BOUND_SET_MEMBER ){ |
454 |
|
if( d_setm_range_lit[f][v][0]==v ){ |
455 |
|
Trace("bound-int") << " " << v << " in " << d_setm_range[f][v] << std::endl; |
456 |
|
}else{ |
457 |
|
Trace("bound-int") << " " << v << " unifiable in " << d_setm_range_lit[f][v] << std::endl; |
458 |
|
} |
459 |
|
}else if( d_bound_type[f][v]==BOUND_FIXED_SET ){ |
460 |
|
Trace("bound-int") << " " << v << " in { "; |
461 |
|
for (TNode fnr : d_fixed_set_ngr_range[f][v]) |
462 |
|
{ |
463 |
|
Trace("bound-int") << fnr << " "; |
464 |
|
} |
465 |
|
for (TNode fgr : d_fixed_set_gr_range[f][v]) |
466 |
|
{ |
467 |
|
Trace("bound-int") << fgr << " "; |
468 |
|
} |
469 |
|
Trace("bound-int") << "}" << std::endl; |
470 |
|
}else if( d_bound_type[f][v]==BOUND_FINITE ){ |
471 |
|
Trace("bound-int") << " " << v << " has small finite type." << std::endl; |
472 |
|
}else{ |
473 |
|
Trace("bound-int") << " " << v << " has unknown bound." << std::endl; |
474 |
|
Assert(false); |
475 |
|
} |
476 |
|
}else{ |
477 |
|
Trace("bound-int") << " " << "*** " << v << " is unbounded." << std::endl; |
478 |
|
} |
479 |
|
} |
480 |
|
} |
481 |
|
|
482 |
804 |
bool bound_success = true; |
483 |
1732 |
for( unsigned i=0; i<f[0].getNumChildren(); i++) { |
484 |
950 |
if( d_bound_type[f].find( f[0][i] )==d_bound_type[f].end() ){ |
485 |
22 |
Trace("bound-int-warn") << "Warning : Bounded Integers : Due to quantification on " << f[0][i] << ", could not find bounds for " << f << std::endl; |
486 |
22 |
bound_success = false; |
487 |
22 |
break; |
488 |
|
} |
489 |
|
} |
490 |
|
|
491 |
804 |
if( bound_success ){ |
492 |
782 |
d_bound_quants.push_back( f ); |
493 |
782 |
DecisionManager* dm = d_qim.getDecisionManager(); |
494 |
1710 |
for( unsigned i=0; i<d_set[f].size(); i++) { |
495 |
1856 |
Node v = d_set[f][i]; |
496 |
928 |
std::map< Node, Node >::iterator itr = d_range[f].find( v ); |
497 |
928 |
if( itr != d_range[f].end() ){ |
498 |
1548 |
Node r = itr->second; |
499 |
774 |
Assert(!r.isNull()); |
500 |
774 |
bool isProxy = false; |
501 |
774 |
if (expr::hasBoundVar(r)) |
502 |
|
{ |
503 |
|
// introduce a new bound |
504 |
|
Node new_range = |
505 |
196 |
sm->mkDummySkolem("bir", r.getType(), "bound for term"); |
506 |
98 |
d_nground_range[f][v] = r; |
507 |
98 |
d_range[f][v] = new_range; |
508 |
98 |
r = new_range; |
509 |
98 |
isProxy = true; |
510 |
|
} |
511 |
774 |
if( !r.isConst() ){ |
512 |
730 |
if (d_rms.find(r) == d_rms.end()) |
513 |
|
{ |
514 |
702 |
Trace("bound-int") << "For " << v << ", bounded Integer Module will try to minimize : " << r << std::endl; |
515 |
702 |
d_ranges.push_back( r ); |
516 |
2106 |
d_rms[r].reset(new IntRangeDecisionHeuristic( |
517 |
1404 |
d_env, r, d_qstate.getValuation(), isProxy)); |
518 |
702 |
dm->registerStrategy(DecisionManager::STRAT_QUANT_BOUND_INT_SIZE, |
519 |
702 |
d_rms[r].get()); |
520 |
|
} |
521 |
|
} |
522 |
|
} |
523 |
|
} |
524 |
|
} |
525 |
|
} |
526 |
|
|
527 |
6857 |
bool BoundedIntegers::isBound(Node q, Node v) const |
528 |
|
{ |
529 |
6857 |
std::map<Node, std::vector<Node> >::const_iterator its = d_set.find(q); |
530 |
6857 |
if (its == d_set.end()) |
531 |
|
{ |
532 |
2919 |
return false; |
533 |
|
} |
534 |
7876 |
return std::find(its->second.begin(), its->second.end(), v) |
535 |
11814 |
!= its->second.end(); |
536 |
|
} |
537 |
|
|
538 |
6993 |
BoundVarType BoundedIntegers::getBoundVarType(Node q, Node v) const |
539 |
|
{ |
540 |
|
std::map<Node, std::map<Node, BoundVarType> >::const_iterator itb = |
541 |
6993 |
d_bound_type.find(q); |
542 |
6993 |
if (itb == d_bound_type.end()) |
543 |
|
{ |
544 |
|
return BOUND_NONE; |
545 |
|
} |
546 |
6993 |
std::map<Node, BoundVarType>::const_iterator it = itb->second.find(v); |
547 |
6993 |
if (it == itb->second.end()) |
548 |
|
{ |
549 |
22 |
return BOUND_NONE; |
550 |
|
} |
551 |
6971 |
return it->second; |
552 |
|
} |
553 |
|
|
554 |
4513 |
void BoundedIntegers::getBoundVarIndices(Node q, |
555 |
|
std::vector<unsigned>& indices) const |
556 |
|
{ |
557 |
4513 |
std::map<Node, std::vector<Node> >::const_iterator it = d_set.find(q); |
558 |
4513 |
if (it != d_set.end()) |
559 |
|
{ |
560 |
10301 |
for (const Node& v : it->second) |
561 |
|
{ |
562 |
5850 |
indices.push_back(TermUtil::getVariableNum(q, v)); |
563 |
|
} |
564 |
|
} |
565 |
4513 |
} |
566 |
|
|
567 |
4771 |
void BoundedIntegers::getBounds( Node f, Node v, RepSetIterator * rsi, Node & l, Node & u ) { |
568 |
4771 |
l = d_bounds[0][f][v]; |
569 |
4771 |
u = d_bounds[1][f][v]; |
570 |
4771 |
if( d_nground_range[f].find(v)!=d_nground_range[f].end() ){ |
571 |
|
//get the substitution |
572 |
2790 |
std::vector< Node > vars; |
573 |
2790 |
std::vector< Node > subs; |
574 |
1395 |
if( getRsiSubsitution( f, v, vars, subs, rsi ) ){ |
575 |
1320 |
u = u.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); |
576 |
1320 |
l = l.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); |
577 |
|
}else{ |
578 |
75 |
u = Node::null(); |
579 |
75 |
l = Node::null(); |
580 |
|
} |
581 |
|
} |
582 |
4771 |
} |
583 |
|
|
584 |
2423 |
void BoundedIntegers::getBoundValues( Node f, Node v, RepSetIterator * rsi, Node & l, Node & u ) { |
585 |
2423 |
getBounds( f, v, rsi, l, u ); |
586 |
2423 |
Trace("bound-int-rsi") << "Get value in model for..." << l << " and " << u << std::endl; |
587 |
2423 |
if( !l.isNull() ){ |
588 |
2348 |
l = d_treg.getModel()->getValue(l); |
589 |
|
} |
590 |
2423 |
if( !u.isNull() ){ |
591 |
2348 |
u = d_treg.getModel()->getValue(u); |
592 |
|
} |
593 |
2423 |
Trace("bound-int-rsi") << "Value is " << l << " ... " << u << std::endl; |
594 |
2423 |
return; |
595 |
|
} |
596 |
|
|
597 |
794 |
bool BoundedIntegers::isGroundRange(Node q, Node v) |
598 |
|
{ |
599 |
794 |
if (isBound(q, v)) |
600 |
|
{ |
601 |
794 |
if (d_bound_type[q][v] == BOUND_INT_RANGE) |
602 |
|
{ |
603 |
1950 |
return !expr::hasBoundVar(getLowerBound(q, v)) |
604 |
1950 |
&& !expr::hasBoundVar(getUpperBound(q, v)); |
605 |
|
} |
606 |
144 |
else if (d_bound_type[q][v] == BOUND_SET_MEMBER) |
607 |
|
{ |
608 |
12 |
return !expr::hasBoundVar(d_setm_range[q][v]); |
609 |
|
} |
610 |
132 |
else if (d_bound_type[q][v] == BOUND_FIXED_SET) |
611 |
|
{ |
612 |
132 |
return !d_fixed_set_ngr_range[q][v].empty(); |
613 |
|
} |
614 |
|
} |
615 |
|
return false; |
616 |
|
} |
617 |
|
|
618 |
78 |
Node BoundedIntegers::getSetRange( Node q, Node v, RepSetIterator * rsi ) { |
619 |
78 |
Node sr = d_setm_range[q][v]; |
620 |
78 |
if( d_nground_range[q].find(v)!=d_nground_range[q].end() ){ |
621 |
16 |
Trace("bound-int-rsi-debug") |
622 |
8 |
<< sr << " is non-ground, apply substitution..." << std::endl; |
623 |
|
//get the substitution |
624 |
16 |
std::vector< Node > vars; |
625 |
16 |
std::vector< Node > subs; |
626 |
8 |
if( getRsiSubsitution( q, v, vars, subs, rsi ) ){ |
627 |
8 |
Trace("bound-int-rsi-debug") |
628 |
4 |
<< " apply " << vars << " -> " << subs << std::endl; |
629 |
4 |
sr = sr.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); |
630 |
|
}else{ |
631 |
4 |
sr = Node::null(); |
632 |
|
} |
633 |
|
} |
634 |
78 |
return sr; |
635 |
|
} |
636 |
|
|
637 |
78 |
Node BoundedIntegers::getSetRangeValue( Node q, Node v, RepSetIterator * rsi ) { |
638 |
156 |
Node sr = getSetRange( q, v, rsi ); |
639 |
78 |
if (sr.isNull()) |
640 |
|
{ |
641 |
4 |
return sr; |
642 |
|
} |
643 |
74 |
Trace("bound-int-rsi") << "Get value in model for..." << sr << std::endl; |
644 |
74 |
Assert(!expr::hasFreeVar(sr)); |
645 |
148 |
Node sro = sr; |
646 |
74 |
sr = d_treg.getModel()->getValue(sr); |
647 |
|
// if non-constant, then sr does not occur in the model, we fail |
648 |
74 |
if (!sr.isConst()) |
649 |
|
{ |
650 |
|
return Node::null(); |
651 |
|
} |
652 |
74 |
Trace("bound-int-rsi") << "Value is " << sr << std::endl; |
653 |
74 |
if (sr.getKind() == EMPTYSET) |
654 |
|
{ |
655 |
4 |
return sr; |
656 |
|
} |
657 |
70 |
NodeManager* nm = NodeManager::currentNM(); |
658 |
140 |
Node nsr; |
659 |
140 |
TypeNode tne = sr.getType().getSetElementType(); |
660 |
|
|
661 |
|
// we can use choice functions for canonical symbolic instantiations |
662 |
70 |
unsigned srCard = 0; |
663 |
146 |
while (sr.getKind() == UNION) |
664 |
|
{ |
665 |
38 |
srCard++; |
666 |
38 |
sr = sr[0]; |
667 |
|
} |
668 |
70 |
Assert(sr.getKind() == SINGLETON); |
669 |
70 |
srCard++; |
670 |
|
// choices[i] stores the canonical symbolic representation of the (i+1)^th |
671 |
|
// element of sro |
672 |
140 |
std::vector<Node> choices; |
673 |
140 |
Node srCardN = nm->mkNode(CARD, sro); |
674 |
140 |
Node choice_i; |
675 |
178 |
for (unsigned i = 0; i < srCard; i++) |
676 |
|
{ |
677 |
108 |
if (i == d_setm_choice[sro].size()) |
678 |
|
{ |
679 |
38 |
choice_i = nm->mkBoundVar(tne); |
680 |
38 |
choices.push_back(choice_i); |
681 |
76 |
Node cBody = nm->mkNode(MEMBER, choice_i, sro); |
682 |
38 |
if (choices.size() > 1) |
683 |
|
{ |
684 |
14 |
cBody = nm->mkNode(AND, cBody, nm->mkNode(DISTINCT, choices)); |
685 |
|
} |
686 |
38 |
choices.pop_back(); |
687 |
76 |
Node bvl = nm->mkNode(BOUND_VAR_LIST, choice_i); |
688 |
76 |
Node cMinCard = nm->mkNode(LEQ, srCardN, nm->mkConst(Rational(i))); |
689 |
38 |
choice_i = nm->mkNode(WITNESS, bvl, nm->mkNode(OR, cMinCard, cBody)); |
690 |
38 |
d_setm_choice[sro].push_back(choice_i); |
691 |
|
} |
692 |
108 |
Assert(i < d_setm_choice[sro].size()); |
693 |
108 |
choice_i = d_setm_choice[sro][i]; |
694 |
108 |
choices.push_back(choice_i); |
695 |
216 |
Node sChoiceI = nm->mkSingleton(choice_i.getType(), choice_i); |
696 |
108 |
if (nsr.isNull()) |
697 |
|
{ |
698 |
70 |
nsr = sChoiceI; |
699 |
|
} |
700 |
|
else |
701 |
|
{ |
702 |
38 |
nsr = nm->mkNode(UNION, nsr, sChoiceI); |
703 |
|
} |
704 |
|
} |
705 |
|
// turns the concrete model value of sro into a canonical representation |
706 |
|
// e.g. |
707 |
|
// singleton(0) union singleton(1) |
708 |
|
// becomes |
709 |
|
// C1 union ( witness y. card(S)<=1 OR ( y in S AND distinct( y, C1 ) ) ) |
710 |
|
// where C1 = ( witness x. card(S)<=0 OR x in S ). |
711 |
70 |
Trace("bound-int-rsi") << "...reconstructed " << nsr << std::endl; |
712 |
70 |
return nsr; |
713 |
|
} |
714 |
|
|
715 |
1455 |
bool BoundedIntegers::getRsiSubsitution( Node q, Node v, std::vector< Node >& vars, std::vector< Node >& subs, RepSetIterator * rsi ) { |
716 |
|
|
717 |
1455 |
Trace("bound-int-rsi") << "Get bound value in model of variable " << v << std::endl; |
718 |
1455 |
Assert(d_set_nums[q].find(v) != d_set_nums[q].end()); |
719 |
1455 |
int vindex = d_set_nums[q][v]; |
720 |
1455 |
Assert(d_set_nums[q][v] == vindex); |
721 |
1455 |
Trace("bound-int-rsi-debug") << " index order is " << vindex << std::endl; |
722 |
|
//must take substitution for all variables that are iterating at higher level |
723 |
3190 |
for( int i=0; i<vindex; i++) { |
724 |
1735 |
Assert(d_set_nums[q][d_set[q][i]] == i); |
725 |
1735 |
Trace("bound-int-rsi") << "Look up the value for " << d_set[q][i] << " " << i << std::endl; |
726 |
1735 |
int vo = rsi->getVariableOrder(i); |
727 |
1735 |
Assert(q[0][vo] == d_set[q][i]); |
728 |
3470 |
TypeNode tn = d_set[q][i].getType(); |
729 |
|
// If the type of tn is not closed enumerable, we must map the value back |
730 |
|
// to a term that appears in the same equivalence class as the constant. |
731 |
|
// Notice that this is to ensure that unhandled values (e.g. uninterpreted |
732 |
|
// constants, datatype values) do not enter instantiations/lemmas, which |
733 |
|
// can lead to refutation unsoundness. However, it is important that we |
734 |
|
// conversely do *not* map terms to values in other cases. In particular, |
735 |
|
// replacing a constant c with a term t can lead to solution unsoundness |
736 |
|
// if we are instantiating a quantified formula that corresponds to a |
737 |
|
// reduction for t, since then the reduction is using circular reasoning: |
738 |
|
// the current value of t is being used to reason about the range of |
739 |
|
// its axiomatization. This is limited to reductions in the theory of |
740 |
|
// strings, which use quantification on integers only. Note this |
741 |
|
// impacts only quantified formulas with 2+ dimensions and dependencies |
742 |
|
// between dimensions, e.g. str.indexof_re reduction. |
743 |
3470 |
Node t = rsi->getCurrentTerm(vo, !tn.isClosedEnumerable()); |
744 |
1735 |
Trace("bound-int-rsi") << "term : " << t << std::endl; |
745 |
1735 |
vars.push_back( d_set[q][i] ); |
746 |
1735 |
subs.push_back( t ); |
747 |
|
} |
748 |
|
|
749 |
|
//check if it has been instantiated |
750 |
1455 |
if( !vars.empty() && !d_bnd_it[q][v].hasInstantiated(subs) ){ |
751 |
87 |
if( d_bound_type[q][v]==BOUND_INT_RANGE || d_bound_type[q][v]==BOUND_SET_MEMBER ){ |
752 |
|
//must add the lemma |
753 |
158 |
Node nn = d_nground_range[q][v]; |
754 |
79 |
nn = nn.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); |
755 |
158 |
Node lem = NodeManager::currentNM()->mkNode( LEQ, nn, d_range[q][v] ); |
756 |
79 |
Trace("bound-int-lemma") << "*** Add lemma to minimize instantiated non-ground term " << lem << std::endl; |
757 |
79 |
d_qim.lemma(lem, InferenceId::QUANTIFIERS_BINT_MIN_NG); |
758 |
|
} |
759 |
87 |
return false; |
760 |
|
}else{ |
761 |
1368 |
return true; |
762 |
|
} |
763 |
|
} |
764 |
|
|
765 |
80 |
Node BoundedIntegers::matchBoundVar( Node v, Node t, Node e ){ |
766 |
80 |
if( t==v ){ |
767 |
32 |
return e; |
768 |
48 |
}else if( t.getKind()==kind::APPLY_CONSTRUCTOR ){ |
769 |
32 |
if( e.getKind()==kind::APPLY_CONSTRUCTOR ){ |
770 |
|
if( t.getOperator() != e.getOperator() ) { |
771 |
|
return Node::null(); |
772 |
|
} |
773 |
|
} |
774 |
32 |
NodeManager* nm = NodeManager::currentNM(); |
775 |
32 |
const DType& dt = datatypes::utils::datatypeOf(t.getOperator()); |
776 |
32 |
unsigned index = datatypes::utils::indexOf(t.getOperator()); |
777 |
48 |
for( unsigned i=0; i<t.getNumChildren(); i++ ){ |
778 |
64 |
Node u; |
779 |
48 |
if( e.getKind()==kind::APPLY_CONSTRUCTOR ){ |
780 |
|
u = matchBoundVar( v, t[i], e[i] ); |
781 |
|
}else{ |
782 |
|
Node se = nm->mkNode(APPLY_SELECTOR_TOTAL, |
783 |
96 |
dt[index].getSelectorInternal(e.getType(), i), |
784 |
192 |
e); |
785 |
48 |
u = matchBoundVar( v, t[i], se ); |
786 |
|
} |
787 |
48 |
if( !u.isNull() ){ |
788 |
32 |
return u; |
789 |
|
} |
790 |
|
} |
791 |
|
} |
792 |
16 |
return Node::null(); |
793 |
|
} |
794 |
|
|
795 |
2865 |
bool BoundedIntegers::getBoundElements( RepSetIterator * rsi, bool initial, Node q, Node v, std::vector< Node >& elements ) { |
796 |
2865 |
if( initial || !isGroundRange( q, v ) ){ |
797 |
2595 |
elements.clear(); |
798 |
2595 |
BoundVarType bvt = getBoundVarType(q, v); |
799 |
2595 |
if( bvt==BOUND_INT_RANGE ){ |
800 |
4846 |
Node l, u; |
801 |
2423 |
getBoundValues( q, v, rsi, l, u ); |
802 |
2423 |
if( l.isNull() || u.isNull() ){ |
803 |
75 |
Trace("bound-int-warn") << "WARNING: Could not find integer bounds in model for " << v << " in " << q << std::endl; |
804 |
|
//failed, abort the iterator |
805 |
75 |
return false; |
806 |
|
}else{ |
807 |
2348 |
NodeManager* nm = NodeManager::currentNM(); |
808 |
2348 |
Trace("bound-int-rsi") << "Can limit bounds of " << v << " to " << l << "..." << u << std::endl; |
809 |
4696 |
Node range = rewrite(nm->mkNode(MINUS, u, l)); |
810 |
|
// 9999 is an arbitrary range past which we do not do exhaustive |
811 |
|
// bounded instantation, based on the check below. |
812 |
4696 |
Node ra = rewrite(nm->mkNode(LEQ, range, nm->mkConst(Rational(9999)))); |
813 |
4696 |
Node tl = l; |
814 |
4696 |
Node tu = u; |
815 |
2348 |
getBounds( q, v, rsi, tl, tu ); |
816 |
2348 |
Assert(!tl.isNull() && !tu.isNull()); |
817 |
2348 |
if (ra.isConst() && ra.getConst<bool>()) |
818 |
|
{ |
819 |
2348 |
long rr = range.getConst<Rational>().getNumerator().getLong()+1; |
820 |
2348 |
Trace("bound-int-rsi") << "Actual bound range is " << rr << std::endl; |
821 |
15348 |
for (long k = 0; k < rr; k++) |
822 |
|
{ |
823 |
26000 |
Node t = nm->mkNode(PLUS, tl, nm->mkConst(Rational(k))); |
824 |
13000 |
t = rewrite(t); |
825 |
13000 |
elements.push_back( t ); |
826 |
|
} |
827 |
2348 |
return true; |
828 |
|
}else{ |
829 |
|
Trace("fmf-incomplete") << "Incomplete because of integer quantification, bounds are too big for " << v << "." << std::endl; |
830 |
|
return false; |
831 |
|
} |
832 |
|
} |
833 |
172 |
}else if( bvt==BOUND_SET_MEMBER ){ |
834 |
156 |
Node srv = getSetRangeValue( q, v, rsi ); |
835 |
78 |
if( srv.isNull() ){ |
836 |
4 |
Trace("bound-int-warn") << "WARNING: Could not find set bound in model for " << v << " in " << q << std::endl; |
837 |
4 |
return false; |
838 |
|
}else{ |
839 |
74 |
Trace("bound-int-rsi") << "Bounded by set membership : " << srv << std::endl; |
840 |
74 |
if( srv.getKind()!=EMPTYSET ){ |
841 |
|
//collect the elements |
842 |
146 |
while( srv.getKind()==UNION ){ |
843 |
38 |
Assert(srv[1].getKind() == kind::SINGLETON); |
844 |
38 |
elements.push_back( srv[1][0] ); |
845 |
38 |
srv = srv[0]; |
846 |
|
} |
847 |
70 |
Assert(srv.getKind() == kind::SINGLETON); |
848 |
70 |
elements.push_back( srv[0] ); |
849 |
|
//check if we need to do matching, for literals like ( tuple( v ) in S ) |
850 |
140 |
Node t = d_setm_range_lit[q][v][0]; |
851 |
70 |
if( t!=v ){ |
852 |
32 |
std::vector< Node > elements_tmp; |
853 |
16 |
elements_tmp.insert( elements_tmp.end(), elements.begin(), elements.end() ); |
854 |
16 |
elements.clear(); |
855 |
48 |
for( unsigned i=0; i<elements_tmp.size(); i++ ){ |
856 |
|
//do matching to determine v -> u |
857 |
64 |
Node u = matchBoundVar( v, t, elements_tmp[i] ); |
858 |
32 |
Trace("bound-int-rsi-debug") << " unification : " << elements_tmp[i] << " = " << t << " yields " << v << " -> " << u << std::endl; |
859 |
32 |
if( !u.isNull() ){ |
860 |
32 |
elements.push_back( u ); |
861 |
|
} |
862 |
|
} |
863 |
|
} |
864 |
|
} |
865 |
74 |
return true; |
866 |
|
} |
867 |
94 |
}else if( bvt==BOUND_FIXED_SET ){ |
868 |
86 |
std::map< Node, std::vector< Node > >::iterator it = d_fixed_set_gr_range[q].find( v ); |
869 |
86 |
if( it!=d_fixed_set_gr_range[q].end() ){ |
870 |
174 |
for( unsigned i=0; i<it->second.size(); i++ ){ |
871 |
108 |
elements.push_back( it->second[i] ); |
872 |
|
} |
873 |
|
} |
874 |
86 |
it = d_fixed_set_ngr_range[q].find( v ); |
875 |
86 |
if( it!=d_fixed_set_ngr_range[q].end() ){ |
876 |
104 |
std::vector< Node > vars; |
877 |
104 |
std::vector< Node > subs; |
878 |
52 |
if( getRsiSubsitution( q, v, vars, subs, rsi ) ){ |
879 |
98 |
for( unsigned i=0; i<it->second.size(); i++ ){ |
880 |
108 |
Node t = it->second[i].substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); |
881 |
54 |
elements.push_back( t ); |
882 |
|
} |
883 |
44 |
return true; |
884 |
|
}else{ |
885 |
8 |
return false; |
886 |
|
} |
887 |
|
}else{ |
888 |
34 |
return true; |
889 |
|
} |
890 |
|
}else{ |
891 |
8 |
return false; |
892 |
|
} |
893 |
|
}else{ |
894 |
|
//no change required |
895 |
270 |
return true; |
896 |
|
} |
897 |
|
} |
898 |
|
|
899 |
|
/** |
900 |
|
* Attribute true for quantifiers that have been internally generated and |
901 |
|
* should be processed with the bounded integers module, e.g. quantified |
902 |
|
* formulas from reductions of string operators. |
903 |
|
* |
904 |
|
* Currently, this attribute is used for indicating that E-matching should |
905 |
|
* not be applied, as E-matching should not be applied to quantifiers |
906 |
|
* generated internally. |
907 |
|
* |
908 |
|
* This attribute can potentially be generalized to an identifier indicating |
909 |
|
* the internal source of the quantified formula (of which strings reduction |
910 |
|
* is one possibility). |
911 |
|
*/ |
912 |
|
struct BoundedQuantAttributeId |
913 |
|
{ |
914 |
|
}; |
915 |
|
typedef expr::Attribute<BoundedQuantAttributeId, bool> BoundedQuantAttribute; |
916 |
|
/** |
917 |
|
* Mapping to a dummy node for marking an attribute on internal quantified |
918 |
|
* formulas. This ensures that reductions are deterministic. |
919 |
|
*/ |
920 |
|
struct QInternalVarAttributeId |
921 |
|
{ |
922 |
|
}; |
923 |
|
typedef expr::Attribute<QInternalVarAttributeId, Node> QInternalVarAttribute; |
924 |
|
|
925 |
689 |
Node BoundedIntegers::mkBoundedForall(Node bvl, Node body) |
926 |
|
{ |
927 |
689 |
NodeManager* nm = NodeManager::currentNM(); |
928 |
|
QInternalVarAttribute qiva; |
929 |
1378 |
Node qvar; |
930 |
689 |
if (bvl.hasAttribute(qiva)) |
931 |
|
{ |
932 |
153 |
qvar = bvl.getAttribute(qiva); |
933 |
|
} |
934 |
|
else |
935 |
|
{ |
936 |
536 |
SkolemManager* sm = nm->getSkolemManager(); |
937 |
536 |
qvar = sm->mkDummySkolem("qinternal", nm->booleanType()); |
938 |
|
// this dummy variable marks that the quantified formula is internal |
939 |
536 |
qvar.setAttribute(BoundedQuantAttribute(), true); |
940 |
|
// remember the dummy variable |
941 |
536 |
bvl.setAttribute(qiva, qvar); |
942 |
|
} |
943 |
|
// make the internal attribute, and put it in a singleton list |
944 |
1378 |
Node ip = nm->mkNode(INST_ATTRIBUTE, qvar); |
945 |
1378 |
Node ipl = nm->mkNode(INST_PATTERN_LIST, ip); |
946 |
|
// make the overall formula |
947 |
1378 |
return nm->mkNode(FORALL, bvl, body, ipl); |
948 |
|
} |
949 |
|
|
950 |
12664 |
bool BoundedIntegers::isBoundedForallAttribute(Node var) |
951 |
|
{ |
952 |
12664 |
return var.getAttribute(BoundedQuantAttribute()); |
953 |
|
} |
954 |
|
|
955 |
|
} // namespace quantifiers |
956 |
|
} // namespace theory |
957 |
31137 |
} // namespace cvc5 |