1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Gereon Kremer, 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 |
|
* Implements the CDCAC approach as described in |
14 |
|
* https://arxiv.org/pdf/2003.05633.pdf. |
15 |
|
*/ |
16 |
|
|
17 |
|
#include "theory/arith/nl/cad/cdcac.h" |
18 |
|
|
19 |
|
#ifdef CVC5_POLY_IMP |
20 |
|
|
21 |
|
#include "options/arith_options.h" |
22 |
|
#include "theory/arith/nl/cad/lazard_evaluation.h" |
23 |
|
#include "theory/arith/nl/cad/projections.h" |
24 |
|
#include "theory/arith/nl/cad/variable_ordering.h" |
25 |
|
#include "theory/arith/nl/nl_model.h" |
26 |
|
#include "theory/rewriter.h" |
27 |
|
|
28 |
|
namespace std { |
29 |
|
/** Generic streaming operator for std::vector. */ |
30 |
|
template <typename T> |
31 |
|
std::ostream& operator<<(std::ostream& os, const std::vector<T>& v) |
32 |
|
{ |
33 |
|
cvc5::container_to_stream(os, v); |
34 |
|
return os; |
35 |
|
} |
36 |
|
} // namespace std |
37 |
|
|
38 |
|
namespace cvc5 { |
39 |
|
namespace theory { |
40 |
|
namespace arith { |
41 |
|
namespace nl { |
42 |
|
namespace cad { |
43 |
|
|
44 |
5207 |
CDCAC::CDCAC(Env& env, const std::vector<poly::Variable>& ordering) |
45 |
5207 |
: EnvObj(env), d_variableOrdering(ordering) |
46 |
|
{ |
47 |
5207 |
if (d_env.isTheoryProofProducing()) |
48 |
|
{ |
49 |
1186 |
d_proof.reset( |
50 |
593 |
new CADProofGenerator(d_env.getContext(), d_env.getProofNodeManager())); |
51 |
|
} |
52 |
5207 |
} |
53 |
|
|
54 |
60 |
void CDCAC::reset() |
55 |
|
{ |
56 |
60 |
d_constraints.reset(); |
57 |
60 |
d_assignment.clear(); |
58 |
60 |
} |
59 |
|
|
60 |
68 |
void CDCAC::computeVariableOrdering() |
61 |
|
{ |
62 |
|
// Actually compute the variable ordering |
63 |
136 |
d_variableOrdering = d_varOrder(d_constraints.getConstraints(), |
64 |
68 |
VariableOrderingStrategy::BROWN); |
65 |
136 |
Trace("cdcac") << "Variable ordering is now " << d_variableOrdering |
66 |
68 |
<< std::endl; |
67 |
|
|
68 |
|
// Write variable ordering back to libpoly. |
69 |
68 |
lp_variable_order_t* vo = poly::Context::get_context().get_variable_order(); |
70 |
68 |
lp_variable_order_clear(vo); |
71 |
223 |
for (const auto& v : d_variableOrdering) |
72 |
|
{ |
73 |
155 |
lp_variable_order_push(vo, v.get_internal()); |
74 |
|
} |
75 |
68 |
} |
76 |
|
|
77 |
56 |
void CDCAC::retrieveInitialAssignment(NlModel& model, const Node& ran_variable) |
78 |
|
{ |
79 |
56 |
if (!d_env.getOptions().arith.nlCadUseInitial) return; |
80 |
|
d_initialAssignment.clear(); |
81 |
|
Trace("cdcac") << "Retrieving initial assignment:" << std::endl; |
82 |
|
for (const auto& var : d_variableOrdering) |
83 |
|
{ |
84 |
|
Node v = getConstraints().varMapper()(var); |
85 |
|
Node val = model.computeConcreteModelValue(v); |
86 |
|
poly::Value value = node_to_value(val, ran_variable); |
87 |
|
Trace("cdcac") << "\t" << var << " = " << value << std::endl; |
88 |
|
d_initialAssignment.emplace_back(value); |
89 |
|
} |
90 |
|
} |
91 |
1127 |
Constraints& CDCAC::getConstraints() { return d_constraints; } |
92 |
|
const Constraints& CDCAC::getConstraints() const { return d_constraints; } |
93 |
|
|
94 |
73 |
const poly::Assignment& CDCAC::getModel() const { return d_assignment; } |
95 |
|
|
96 |
16 |
const std::vector<poly::Variable>& CDCAC::getVariableOrdering() const |
97 |
|
{ |
98 |
16 |
return d_variableOrdering; |
99 |
|
} |
100 |
|
|
101 |
223 |
std::vector<CACInterval> CDCAC::getUnsatIntervals(std::size_t cur_variable) |
102 |
|
{ |
103 |
223 |
std::vector<CACInterval> res; |
104 |
446 |
LazardEvaluation le; |
105 |
446 |
if (d_env.getOptions().arith.nlCadLifting |
106 |
223 |
== options::NlCadLiftingMode::LAZARD) |
107 |
|
{ |
108 |
|
for (size_t vid = 0; vid < cur_variable; ++vid) |
109 |
|
{ |
110 |
|
const auto& val = d_assignment.get(d_variableOrdering[vid]); |
111 |
|
le.add(d_variableOrdering[vid], val); |
112 |
|
} |
113 |
|
le.addFreeVariable(d_variableOrdering[cur_variable]); |
114 |
|
} |
115 |
2747 |
for (const auto& c : d_constraints.getConstraints()) |
116 |
|
{ |
117 |
2524 |
const poly::Polynomial& p = std::get<0>(c); |
118 |
2524 |
poly::SignCondition sc = std::get<1>(c); |
119 |
2524 |
const Node& n = std::get<2>(c); |
120 |
|
|
121 |
2524 |
if (main_variable(p) != d_variableOrdering[cur_variable]) |
122 |
|
{ |
123 |
|
// Constraint is in another variable, ignore it. |
124 |
1368 |
continue; |
125 |
|
} |
126 |
|
|
127 |
2312 |
Trace("cdcac") << "Infeasible intervals for " << p << " " << sc |
128 |
1156 |
<< " 0 over " << d_assignment << std::endl; |
129 |
2312 |
std::vector<poly::Interval> intervals; |
130 |
2312 |
if (d_env.getOptions().arith.nlCadLifting |
131 |
1156 |
== options::NlCadLiftingMode::LAZARD) |
132 |
|
{ |
133 |
|
intervals = le.infeasibleRegions(p, sc); |
134 |
|
if (Trace.isOn("cdcac")) |
135 |
|
{ |
136 |
|
auto reference = poly::infeasible_regions(p, d_assignment, sc); |
137 |
|
Trace("cdcac") << "Lazard: " << intervals << std::endl; |
138 |
|
Trace("cdcac") << "Regular: " << reference << std::endl; |
139 |
|
} |
140 |
|
} |
141 |
|
else |
142 |
|
{ |
143 |
1156 |
intervals = poly::infeasible_regions(p, d_assignment, sc); |
144 |
|
} |
145 |
2694 |
for (const auto& i : intervals) |
146 |
|
{ |
147 |
1538 |
Trace("cdcac") << "-> " << i << std::endl; |
148 |
3076 |
PolyVector l, u, m, d; |
149 |
1538 |
m.add(p); |
150 |
1538 |
m.pushDownPolys(d, d_variableOrdering[cur_variable]); |
151 |
1538 |
if (!is_minus_infinity(get_lower(i))) l = m; |
152 |
1538 |
if (!is_plus_infinity(get_upper(i))) u = m; |
153 |
1538 |
res.emplace_back(CACInterval{i, l, u, m, d, {n}}); |
154 |
1538 |
if (isProofEnabled()) |
155 |
|
{ |
156 |
12 |
d_proof->addDirect( |
157 |
8 |
d_constraints.varMapper()(d_variableOrdering[cur_variable]), |
158 |
|
d_constraints.varMapper(), |
159 |
|
p, |
160 |
|
d_assignment, |
161 |
|
sc, |
162 |
|
i, |
163 |
|
n); |
164 |
|
} |
165 |
|
} |
166 |
|
} |
167 |
223 |
pruneRedundantIntervals(res); |
168 |
446 |
return res; |
169 |
|
} |
170 |
|
|
171 |
321 |
bool CDCAC::sampleOutsideWithInitial(const std::vector<CACInterval>& infeasible, |
172 |
|
poly::Value& sample, |
173 |
|
std::size_t cur_variable) |
174 |
|
{ |
175 |
642 |
if (d_env.getOptions().arith.nlCadUseInitial |
176 |
321 |
&& cur_variable < d_initialAssignment.size()) |
177 |
|
{ |
178 |
|
const poly::Value& suggested = d_initialAssignment[cur_variable]; |
179 |
|
for (const auto& i : infeasible) |
180 |
|
{ |
181 |
|
if (poly::contains(i.d_interval, suggested)) |
182 |
|
{ |
183 |
|
d_initialAssignment.clear(); |
184 |
|
return sampleOutside(infeasible, sample); |
185 |
|
} |
186 |
|
} |
187 |
|
Trace("cdcac") << "Using suggested initial value" << std::endl; |
188 |
|
sample = suggested; |
189 |
|
return true; |
190 |
|
} |
191 |
321 |
return sampleOutside(infeasible, sample); |
192 |
|
} |
193 |
|
|
194 |
|
namespace { |
195 |
|
|
196 |
|
/** |
197 |
|
* This method follows the projection operator as detailed in algorithm 6 of |
198 |
|
* 10.1016/j.jlamp.2020.100633, which mostly follows the projection operator due |
199 |
|
* to McCallum. It uses all coefficients until one is either constant or does |
200 |
|
* not vanish over the current assignment. |
201 |
|
*/ |
202 |
184 |
PolyVector requiredCoefficientsOriginal(const poly::Polynomial& p, |
203 |
|
const poly::Assignment& assignment) |
204 |
|
{ |
205 |
184 |
PolyVector res; |
206 |
184 |
for (long deg = degree(p); deg >= 0; --deg) |
207 |
|
{ |
208 |
184 |
auto coeff = coefficient(p, deg); |
209 |
184 |
Assert(poly::is_constant(coeff) |
210 |
|
== lp_polynomial_is_constant(coeff.get_internal())); |
211 |
184 |
if (poly::is_constant(coeff)) break; |
212 |
26 |
res.add(coeff); |
213 |
26 |
if (evaluate_constraint(coeff, assignment, poly::SignCondition::NE)) |
214 |
|
{ |
215 |
26 |
break; |
216 |
|
} |
217 |
|
} |
218 |
184 |
return res; |
219 |
|
} |
220 |
|
|
221 |
|
/** |
222 |
|
* This method follows the original projection operator due to Lazard from |
223 |
|
* section 3 of 10.1007/978-1-4612-2628-4_29. It uses the leading and trailing |
224 |
|
* coefficient, and makes some limited efforts to avoid them in certain cases: |
225 |
|
* We avoid the leading coefficient if it is constant. We avoid the trailing |
226 |
|
* coefficient if the leading coefficient does not vanish over the current |
227 |
|
* assignment and the trailing coefficient is not constant. |
228 |
|
*/ |
229 |
|
PolyVector requiredCoefficientsLazard(const poly::Polynomial& p, |
230 |
|
const poly::Assignment& assignment) |
231 |
|
{ |
232 |
|
PolyVector res; |
233 |
|
auto lc = poly::leading_coefficient(p); |
234 |
|
if (poly::is_constant(lc)) return res; |
235 |
|
res.add(lc); |
236 |
|
if (evaluate_constraint(lc, assignment, poly::SignCondition::NE)) return res; |
237 |
|
auto tc = poly::coefficient(p, 0); |
238 |
|
if (poly::is_constant(tc)) return res; |
239 |
|
res.add(tc); |
240 |
|
return res; |
241 |
|
} |
242 |
|
|
243 |
|
/** |
244 |
|
* This method follows the enhancements from 10.1007/978-3-030-60026-6_8 for the |
245 |
|
* projection operator due to Lazard, more specifically Section 3.3 and |
246 |
|
* Definition 4. In essence, we can skip the trailing coefficient if we can show |
247 |
|
* that p is not nullified by any n-1 dimensional point. The statement in the |
248 |
|
* paper is slightly more general, but there is no simple way to have such a |
249 |
|
* procedure T here. We simply try to show that T(f) = {} by using the extended |
250 |
|
* rewriter to simplify (and (= f_i 0)) (f_i being the coefficients of f) to |
251 |
|
* false. |
252 |
|
*/ |
253 |
|
PolyVector requiredCoefficientsLazardModified( |
254 |
|
const poly::Polynomial& p, |
255 |
|
const poly::Assignment& assignment, |
256 |
|
VariableMapper& vm) |
257 |
|
{ |
258 |
|
PolyVector res; |
259 |
|
auto lc = poly::leading_coefficient(p); |
260 |
|
// if leading coefficient is constant |
261 |
|
if (poly::is_constant(lc)) return res; |
262 |
|
// add leading coefficient |
263 |
|
res.add(lc); |
264 |
|
auto tc = poly::coefficient(p, 0); |
265 |
|
// if trailing coefficient is constant |
266 |
|
if (poly::is_constant(tc)) return res; |
267 |
|
// if leading coefficient does not vanish over the current assignment |
268 |
|
if (evaluate_constraint(lc, assignment, poly::SignCondition::NE)) return res; |
269 |
|
|
270 |
|
// construct phi := (and (= p_i 0)) with p_i the coefficients of p |
271 |
|
std::vector<Node> conditions; |
272 |
|
auto zero = NodeManager::currentNM()->mkConst(Rational(0)); |
273 |
|
for (const auto& coeff : poly::coefficients(p)) |
274 |
|
{ |
275 |
|
conditions.emplace_back(NodeManager::currentNM()->mkNode( |
276 |
|
Kind::EQUAL, nl::as_cvc_polynomial(coeff, vm), zero)); |
277 |
|
} |
278 |
|
// if phi is false (i.e. p can not vanish) |
279 |
|
Node rewritten = Rewriter::callExtendedRewrite( |
280 |
|
NodeManager::currentNM()->mkAnd(conditions)); |
281 |
|
if (rewritten.isConst()) |
282 |
|
{ |
283 |
|
Assert(rewritten.getKind() == Kind::CONST_BOOLEAN); |
284 |
|
Assert(!rewritten.getConst<bool>()); |
285 |
|
return res; |
286 |
|
} |
287 |
|
// otherwise add trailing coefficient as well |
288 |
|
res.add(tc); |
289 |
|
return res; |
290 |
|
} |
291 |
|
|
292 |
|
} // namespace |
293 |
|
|
294 |
184 |
PolyVector CDCAC::requiredCoefficients(const poly::Polynomial& p) |
295 |
|
{ |
296 |
184 |
if (Trace.isOn("cdcac")) |
297 |
|
{ |
298 |
|
Trace("cdcac") << "Poly: " << p << " over " << d_assignment << std::endl; |
299 |
|
Trace("cdcac") << "Lazard: " |
300 |
|
<< requiredCoefficientsLazard(p, d_assignment) << std::endl; |
301 |
|
Trace("cdcac") << "LMod: " |
302 |
|
<< requiredCoefficientsLazardModified( |
303 |
|
p, d_assignment, d_constraints.varMapper()) |
304 |
|
<< std::endl; |
305 |
|
Trace("cdcac") << "Original: " |
306 |
|
<< requiredCoefficientsOriginal(p, d_assignment) |
307 |
|
<< std::endl; |
308 |
|
} |
309 |
184 |
switch (d_env.getOptions().arith.nlCadProjection) |
310 |
|
{ |
311 |
184 |
case options::NlCadProjectionMode::MCCALLUM: |
312 |
184 |
return requiredCoefficientsOriginal(p, d_assignment); |
313 |
|
case options::NlCadProjectionMode::LAZARD: |
314 |
|
return requiredCoefficientsLazard(p, d_assignment); |
315 |
|
case options::NlCadProjectionMode::LAZARDMOD: |
316 |
|
return requiredCoefficientsLazardModified( |
317 |
|
p, d_assignment, d_constraints.varMapper()); |
318 |
|
default: |
319 |
|
Assert(false); |
320 |
|
return requiredCoefficientsOriginal(p, d_assignment); |
321 |
|
} |
322 |
|
} |
323 |
|
|
324 |
98 |
PolyVector CDCAC::constructCharacterization(std::vector<CACInterval>& intervals) |
325 |
|
{ |
326 |
98 |
Assert(!intervals.empty()) << "A covering can not be empty"; |
327 |
98 |
Trace("cdcac") << "Constructing characterization now" << std::endl; |
328 |
98 |
PolyVector res; |
329 |
|
|
330 |
180 |
for (std::size_t i = 0, n = intervals.size(); i < n - 1; ++i) |
331 |
|
{ |
332 |
82 |
cad::makeFinestSquareFreeBasis(intervals[i], intervals[i + 1]); |
333 |
|
} |
334 |
|
|
335 |
278 |
for (const auto& i : intervals) |
336 |
|
{ |
337 |
180 |
Trace("cdcac") << "Considering " << i.d_interval << std::endl; |
338 |
360 |
Trace("cdcac") << "-> " << i.d_lowerPolys << " / " << i.d_upperPolys |
339 |
180 |
<< " and " << i.d_mainPolys << " / " << i.d_downPolys |
340 |
180 |
<< std::endl; |
341 |
180 |
Trace("cdcac") << "-> " << i.d_origins << std::endl; |
342 |
218 |
for (const auto& p : i.d_downPolys) |
343 |
|
{ |
344 |
|
// Add all polynomial from lower levels. |
345 |
38 |
res.add(p); |
346 |
|
} |
347 |
364 |
for (const auto& p : i.d_mainPolys) |
348 |
|
{ |
349 |
368 |
Trace("cdcac") << "Discriminant of " << p << " -> " << discriminant(p) |
350 |
184 |
<< std::endl; |
351 |
|
// Add all discriminants |
352 |
184 |
res.add(discriminant(p)); |
353 |
|
|
354 |
210 |
for (const auto& q : requiredCoefficients(p)) |
355 |
|
{ |
356 |
|
// Add all required coefficients |
357 |
26 |
Trace("cdcac") << "Coeff of " << p << " -> " << q << std::endl; |
358 |
26 |
res.add(q); |
359 |
|
} |
360 |
268 |
for (const auto& q : i.d_lowerPolys) |
361 |
|
{ |
362 |
84 |
if (p == q) continue; |
363 |
|
// Check whether p(s \times a) = 0 for some a <= l |
364 |
2 |
if (!hasRootBelow(q, get_lower(i.d_interval))) continue; |
365 |
4 |
Trace("cdcac") << "Resultant of " << p << " and " << q << " -> " |
366 |
2 |
<< resultant(p, q) << std::endl; |
367 |
2 |
res.add(resultant(p, q)); |
368 |
|
} |
369 |
268 |
for (const auto& q : i.d_upperPolys) |
370 |
|
{ |
371 |
84 |
if (p == q) continue; |
372 |
|
// Check whether p(s \times a) = 0 for some a >= u |
373 |
2 |
if (!hasRootAbove(q, get_upper(i.d_interval))) continue; |
374 |
4 |
Trace("cdcac") << "Resultant of " << p << " and " << q << " -> " |
375 |
2 |
<< resultant(p, q) << std::endl; |
376 |
2 |
res.add(resultant(p, q)); |
377 |
|
} |
378 |
|
} |
379 |
|
} |
380 |
|
|
381 |
180 |
for (std::size_t i = 0, n = intervals.size(); i < n - 1; ++i) |
382 |
|
{ |
383 |
|
// Add resultants of consecutive intervals. |
384 |
164 |
for (const auto& p : intervals[i].d_upperPolys) |
385 |
|
{ |
386 |
164 |
for (const auto& q : intervals[i + 1].d_lowerPolys) |
387 |
|
{ |
388 |
164 |
Trace("cdcac") << "Resultant of " << p << " and " << q << " -> " |
389 |
82 |
<< resultant(p, q) << std::endl; |
390 |
82 |
res.add(resultant(p, q)); |
391 |
|
} |
392 |
|
} |
393 |
|
} |
394 |
|
|
395 |
98 |
res.reduce(); |
396 |
98 |
res.makeFinestSquareFreeBasis(); |
397 |
|
|
398 |
98 |
return res; |
399 |
|
} |
400 |
|
|
401 |
98 |
CACInterval CDCAC::intervalFromCharacterization( |
402 |
|
const PolyVector& characterization, |
403 |
|
std::size_t cur_variable, |
404 |
|
const poly::Value& sample) |
405 |
|
{ |
406 |
196 |
PolyVector l; |
407 |
196 |
PolyVector u; |
408 |
196 |
PolyVector m; |
409 |
196 |
PolyVector d; |
410 |
|
|
411 |
244 |
for (const auto& p : characterization) |
412 |
|
{ |
413 |
|
// Add polynomials to main |
414 |
146 |
m.add(p); |
415 |
|
} |
416 |
|
// Push lower-dimensional polys to down |
417 |
98 |
m.pushDownPolys(d, d_variableOrdering[cur_variable]); |
418 |
|
|
419 |
|
// Collect -oo, all roots, oo |
420 |
196 |
std::vector<poly::Value> roots; |
421 |
98 |
roots.emplace_back(poly::Value::minus_infty()); |
422 |
222 |
for (const auto& p : m) |
423 |
|
{ |
424 |
248 |
auto tmp = isolate_real_roots(p, d_assignment); |
425 |
124 |
roots.insert(roots.end(), tmp.begin(), tmp.end()); |
426 |
|
} |
427 |
98 |
roots.emplace_back(poly::Value::plus_infty()); |
428 |
98 |
std::sort(roots.begin(), roots.end()); |
429 |
|
|
430 |
|
// Now find the interval bounds |
431 |
196 |
poly::Value lower; |
432 |
196 |
poly::Value upper; |
433 |
260 |
for (std::size_t i = 0, n = roots.size(); i < n; ++i) |
434 |
|
{ |
435 |
260 |
if (sample < roots[i]) |
436 |
|
{ |
437 |
84 |
lower = roots[i - 1]; |
438 |
84 |
upper = roots[i]; |
439 |
84 |
break; |
440 |
|
} |
441 |
176 |
if (roots[i] == sample) |
442 |
|
{ |
443 |
14 |
lower = sample; |
444 |
14 |
upper = sample; |
445 |
14 |
break; |
446 |
|
} |
447 |
|
} |
448 |
98 |
Assert(!is_none(lower) && !is_none(upper)); |
449 |
|
|
450 |
98 |
if (!is_minus_infinity(lower)) |
451 |
|
{ |
452 |
|
// Identify polynomials that have a root at the lower bound |
453 |
60 |
d_assignment.set(d_variableOrdering[cur_variable], lower); |
454 |
142 |
for (const auto& p : m) |
455 |
|
{ |
456 |
82 |
if (evaluate_constraint(p, d_assignment, poly::SignCondition::EQ)) |
457 |
|
{ |
458 |
60 |
l.add(p, true); |
459 |
|
} |
460 |
|
} |
461 |
60 |
d_assignment.unset(d_variableOrdering[cur_variable]); |
462 |
|
} |
463 |
98 |
if (!is_plus_infinity(upper)) |
464 |
|
{ |
465 |
|
// Identify polynomials that have a root at the upper bound |
466 |
62 |
d_assignment.set(d_variableOrdering[cur_variable], upper); |
467 |
140 |
for (const auto& p : m) |
468 |
|
{ |
469 |
78 |
if (evaluate_constraint(p, d_assignment, poly::SignCondition::EQ)) |
470 |
|
{ |
471 |
62 |
u.add(p, true); |
472 |
|
} |
473 |
|
} |
474 |
62 |
d_assignment.unset(d_variableOrdering[cur_variable]); |
475 |
|
} |
476 |
|
|
477 |
98 |
if (lower == upper) |
478 |
|
{ |
479 |
|
// construct a point interval |
480 |
|
return CACInterval{ |
481 |
14 |
poly::Interval(lower, false, upper, false), l, u, m, d, {}}; |
482 |
|
} |
483 |
|
else |
484 |
|
{ |
485 |
|
// construct an open interval |
486 |
84 |
Assert(lower < upper); |
487 |
|
return CACInterval{ |
488 |
84 |
poly::Interval(lower, true, upper, true), l, u, m, d, {}}; |
489 |
|
} |
490 |
|
} |
491 |
|
|
492 |
223 |
std::vector<CACInterval> CDCAC::getUnsatCover(std::size_t curVariable, |
493 |
|
bool returnFirstInterval) |
494 |
|
{ |
495 |
223 |
if (isProofEnabled()) |
496 |
|
{ |
497 |
2 |
d_proof->startRecursive(); |
498 |
|
} |
499 |
446 |
Trace("cdcac") << "Looking for unsat cover for " |
500 |
223 |
<< d_variableOrdering[curVariable] << std::endl; |
501 |
446 |
std::vector<CACInterval> intervals = getUnsatIntervals(curVariable); |
502 |
|
|
503 |
223 |
if (Trace.isOn("cdcac")) |
504 |
|
{ |
505 |
|
Trace("cdcac") << "Unsat intervals for " << d_variableOrdering[curVariable] |
506 |
|
<< ":" << std::endl; |
507 |
|
for (const auto& i : intervals) |
508 |
|
{ |
509 |
|
Trace("cdcac") << "-> " << i.d_interval << " from " << i.d_origins |
510 |
|
<< std::endl; |
511 |
|
} |
512 |
|
} |
513 |
446 |
poly::Value sample; |
514 |
|
|
515 |
419 |
while (sampleOutsideWithInitial(intervals, sample, curVariable)) |
516 |
|
{ |
517 |
197 |
if (!checkIntegrality(curVariable, sample)) |
518 |
|
{ |
519 |
|
// the variable is integral, but the sample is not. |
520 |
|
Trace("cdcac") << "Used " << sample << " for integer variable " |
521 |
|
<< d_variableOrdering[curVariable] << std::endl; |
522 |
|
auto newInterval = buildIntegralityInterval(curVariable, sample); |
523 |
|
Trace("cdcac") << "Adding integrality interval " << newInterval.d_interval |
524 |
|
<< std::endl; |
525 |
|
intervals.emplace_back(newInterval); |
526 |
|
pruneRedundantIntervals(intervals); |
527 |
|
continue; |
528 |
|
} |
529 |
197 |
d_assignment.set(d_variableOrdering[curVariable], sample); |
530 |
197 |
Trace("cdcac") << "Sample: " << d_assignment << std::endl; |
531 |
197 |
if (curVariable == d_variableOrdering.size() - 1) |
532 |
|
{ |
533 |
|
// We have a full assignment. SAT! |
534 |
42 |
Trace("cdcac") << "Found full assignment: " << d_assignment << std::endl; |
535 |
141 |
return {}; |
536 |
|
} |
537 |
155 |
if (isProofEnabled()) |
538 |
|
{ |
539 |
1 |
d_proof->startScope(); |
540 |
|
} |
541 |
|
// Recurse to next variable |
542 |
253 |
auto cov = getUnsatCover(curVariable + 1); |
543 |
155 |
if (cov.empty()) |
544 |
|
{ |
545 |
|
// Found SAT! |
546 |
57 |
Trace("cdcac") << "SAT!" << std::endl; |
547 |
57 |
return {}; |
548 |
|
} |
549 |
98 |
Trace("cdcac") << "Refuting Sample: " << d_assignment << std::endl; |
550 |
196 |
auto characterization = constructCharacterization(cov); |
551 |
98 |
Trace("cdcac") << "Characterization: " << characterization << std::endl; |
552 |
|
|
553 |
98 |
d_assignment.unset(d_variableOrdering[curVariable]); |
554 |
|
|
555 |
|
auto newInterval = |
556 |
196 |
intervalFromCharacterization(characterization, curVariable, sample); |
557 |
98 |
newInterval.d_origins = collectConstraints(cov); |
558 |
98 |
intervals.emplace_back(newInterval); |
559 |
98 |
if (isProofEnabled()) |
560 |
|
{ |
561 |
|
auto cell = d_proof->constructCell( |
562 |
2 |
d_constraints.varMapper()(d_variableOrdering[curVariable]), |
563 |
|
newInterval, |
564 |
|
d_assignment, |
565 |
|
sample, |
566 |
3 |
d_constraints.varMapper()); |
567 |
1 |
d_proof->endScope(cell); |
568 |
|
} |
569 |
|
|
570 |
98 |
if (returnFirstInterval) |
571 |
|
{ |
572 |
|
return intervals; |
573 |
|
} |
574 |
|
|
575 |
98 |
Trace("cdcac") << "Added " << intervals.back().d_interval << std::endl; |
576 |
196 |
Trace("cdcac") << "\tlower: " << intervals.back().d_lowerPolys |
577 |
98 |
<< std::endl; |
578 |
196 |
Trace("cdcac") << "\tupper: " << intervals.back().d_upperPolys |
579 |
98 |
<< std::endl; |
580 |
196 |
Trace("cdcac") << "\tmain: " << intervals.back().d_mainPolys |
581 |
98 |
<< std::endl; |
582 |
196 |
Trace("cdcac") << "\tdown: " << intervals.back().d_downPolys |
583 |
98 |
<< std::endl; |
584 |
98 |
Trace("cdcac") << "\torigins: " << intervals.back().d_origins << std::endl; |
585 |
|
|
586 |
|
// Remove redundant intervals |
587 |
98 |
pruneRedundantIntervals(intervals); |
588 |
|
} |
589 |
|
|
590 |
124 |
if (Trace.isOn("cdcac")) |
591 |
|
{ |
592 |
|
Trace("cdcac") << "Returning intervals for " |
593 |
|
<< d_variableOrdering[curVariable] << ":" << std::endl; |
594 |
|
for (const auto& i : intervals) |
595 |
|
{ |
596 |
|
Trace("cdcac") << "-> " << i.d_interval << std::endl; |
597 |
|
} |
598 |
|
} |
599 |
124 |
if (isProofEnabled()) |
600 |
|
{ |
601 |
2 |
d_proof->endRecursive(); |
602 |
|
} |
603 |
124 |
return intervals; |
604 |
|
} |
605 |
|
|
606 |
56 |
void CDCAC::startNewProof() |
607 |
|
{ |
608 |
56 |
if (isProofEnabled()) |
609 |
|
{ |
610 |
1 |
d_proof->startNewProof(); |
611 |
|
} |
612 |
56 |
} |
613 |
|
|
614 |
24 |
ProofGenerator* CDCAC::closeProof(const std::vector<Node>& assertions) |
615 |
|
{ |
616 |
24 |
if (isProofEnabled()) |
617 |
|
{ |
618 |
1 |
d_proof->endScope(assertions); |
619 |
1 |
return d_proof->getProofGenerator(); |
620 |
|
} |
621 |
23 |
return nullptr; |
622 |
|
} |
623 |
|
|
624 |
197 |
bool CDCAC::checkIntegrality(std::size_t cur_variable, const poly::Value& value) |
625 |
|
{ |
626 |
394 |
Node var = d_constraints.varMapper()(d_variableOrdering[cur_variable]); |
627 |
197 |
if (var.getType() != NodeManager::currentNM()->integerType()) |
628 |
|
{ |
629 |
|
// variable is not integral |
630 |
183 |
return true; |
631 |
|
} |
632 |
14 |
return poly::represents_integer(value); |
633 |
|
} |
634 |
|
|
635 |
|
CACInterval CDCAC::buildIntegralityInterval(std::size_t cur_variable, |
636 |
|
const poly::Value& value) |
637 |
|
{ |
638 |
|
poly::Variable var = d_variableOrdering[cur_variable]; |
639 |
|
poly::Integer below = poly::floor(value); |
640 |
|
poly::Integer above = poly::ceil(value); |
641 |
|
// construct var \in (below, above) |
642 |
|
return CACInterval{poly::Interval(below, above), |
643 |
|
{var - below}, |
644 |
|
{var - above}, |
645 |
|
{var - below, var - above}, |
646 |
|
{}, |
647 |
|
{}}; |
648 |
|
} |
649 |
|
|
650 |
2 |
bool CDCAC::hasRootAbove(const poly::Polynomial& p, |
651 |
|
const poly::Value& val) const |
652 |
|
{ |
653 |
4 |
auto roots = poly::isolate_real_roots(p, d_assignment); |
654 |
4 |
return std::any_of(roots.begin(), roots.end(), [&val](const poly::Value& r) { |
655 |
|
return r >= val; |
656 |
6 |
}); |
657 |
|
} |
658 |
|
|
659 |
2 |
bool CDCAC::hasRootBelow(const poly::Polynomial& p, |
660 |
|
const poly::Value& val) const |
661 |
|
{ |
662 |
4 |
auto roots = poly::isolate_real_roots(p, d_assignment); |
663 |
4 |
return std::any_of(roots.begin(), roots.end(), [&val](const poly::Value& r) { |
664 |
|
return r <= val; |
665 |
6 |
}); |
666 |
|
} |
667 |
|
|
668 |
321 |
void CDCAC::pruneRedundantIntervals(std::vector<CACInterval>& intervals) |
669 |
|
{ |
670 |
321 |
if (isProofEnabled()) |
671 |
|
{ |
672 |
6 |
std::vector<CACInterval> allIntervals = intervals; |
673 |
3 |
cleanIntervals(intervals); |
674 |
27 |
d_proof->pruneChildren([&allIntervals, &intervals](std::size_t i) { |
675 |
24 |
return std::find(intervals.begin(), intervals.end(), allIntervals[i]) |
676 |
12 |
!= intervals.end(); |
677 |
12 |
}); |
678 |
|
} |
679 |
|
else |
680 |
|
{ |
681 |
318 |
cleanIntervals(intervals); |
682 |
|
} |
683 |
321 |
} |
684 |
|
|
685 |
|
} // namespace cad |
686 |
|
} // namespace nl |
687 |
|
} // namespace arith |
688 |
|
} // namespace theory |
689 |
29511 |
} // namespace cvc5 |
690 |
|
|
691 |
|
#endif |