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