7fd22fb7648596ae6ca2cd1ac1e14b2da91d7c30
1 /********************* */
4 ** Top contributors (to current version):
6 ** This file is part of the CVC4 project.
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.\endverbatim
12 ** \brief Implements the CDCAC approach.
14 ** Implements the CDCAC approach as described in
15 ** https://arxiv.org/pdf/2003.05633.pdf.
18 #include "theory/arith/nl/cad/cdcac.h"
22 #include "options/arith_options.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"
28 /** Generic streaming operator for std::vector. */
30 std::ostream
& operator<<(std::ostream
& os
, const std::vector
<T
>& v
)
32 CVC5::container_to_stream(os
, v
);
43 CDCAC::CDCAC(context::Context
* ctx
,
44 ProofNodeManager
* pnm
,
45 const std::vector
<poly::Variable
>& ordering
)
46 : d_variableOrdering(ordering
)
50 d_proof
.reset(new CADProofGenerator(ctx
, pnm
));
56 d_constraints
.reset();
60 void CDCAC::computeVariableOrdering()
62 // Actually compute the variable ordering
63 d_variableOrdering
= d_varOrder(d_constraints
.getConstraints(),
64 VariableOrderingStrategy::BROWN
);
65 Trace("cdcac") << "Variable ordering is now " << d_variableOrdering
68 // Write variable ordering back to libpoly.
69 lp_variable_order_t
* vo
= poly::Context::get_context().get_variable_order();
70 lp_variable_order_clear(vo
);
71 for (const auto& v
: d_variableOrdering
)
73 lp_variable_order_push(vo
, v
.get_internal());
77 void CDCAC::retrieveInitialAssignment(NlModel
& model
, const Node
& ran_variable
)
79 if (!options::nlCadUseInitial()) return;
80 d_initialAssignment
.clear();
81 Trace("cdcac") << "Retrieving initial assignment:" << std::endl
;
82 for (const auto& var
: d_variableOrdering
)
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
);
91 Constraints
& CDCAC::getConstraints() { return d_constraints
; }
92 const Constraints
& CDCAC::getConstraints() const { return d_constraints
; }
94 const poly::Assignment
& CDCAC::getModel() const { return d_assignment
; }
96 const std::vector
<poly::Variable
>& CDCAC::getVariableOrdering() const
98 return d_variableOrdering
;
101 std::vector
<CACInterval
> CDCAC::getUnsatIntervals(std::size_t cur_variable
)
103 std::vector
<CACInterval
> res
;
104 for (const auto& c
: d_constraints
.getConstraints())
106 const poly::Polynomial
& p
= std::get
<0>(c
);
107 poly::SignCondition sc
= std::get
<1>(c
);
108 const Node
& n
= std::get
<2>(c
);
110 if (main_variable(p
) != d_variableOrdering
[cur_variable
])
112 // Constraint is in another variable, ignore it.
116 Trace("cdcac") << "Infeasible intervals for " << p
<< " " << sc
117 << " 0 over " << d_assignment
<< std::endl
;
118 auto intervals
= infeasible_regions(p
, d_assignment
, sc
);
119 for (const auto& i
: intervals
)
121 Trace("cdcac") << "-> " << i
<< std::endl
;
122 PolyVector l
, u
, m
, d
;
124 m
.pushDownPolys(d
, d_variableOrdering
[cur_variable
]);
125 if (!is_minus_infinity(get_lower(i
))) l
= m
;
126 if (!is_plus_infinity(get_upper(i
))) u
= m
;
127 res
.emplace_back(CACInterval
{i
, l
, u
, m
, d
, {n
}});
128 if (isProofEnabled())
131 d_constraints
.varMapper()(d_variableOrdering
[cur_variable
]),
132 d_constraints
.varMapper(),
141 pruneRedundantIntervals(res
);
145 bool CDCAC::sampleOutsideWithInitial(const std::vector
<CACInterval
>& infeasible
,
147 std::size_t cur_variable
)
149 if (options::nlCadUseInitial() && cur_variable
< d_initialAssignment
.size())
151 const poly::Value
& suggested
= d_initialAssignment
[cur_variable
];
152 for (const auto& i
: infeasible
)
154 if (poly::contains(i
.d_interval
, suggested
))
156 d_initialAssignment
.clear();
157 return sampleOutside(infeasible
, sample
);
160 Trace("cdcac") << "Using suggested initial value" << std::endl
;
164 return sampleOutside(infeasible
, sample
);
167 PolyVector
CDCAC::requiredCoefficients(const poly::Polynomial
& p
) const
170 for (long deg
= degree(p
); deg
>= 0; --deg
)
172 auto coeff
= coefficient(p
, deg
);
173 if (lp_polynomial_is_constant(coeff
.get_internal())) break;
175 if (evaluate_constraint(coeff
, d_assignment
, poly::SignCondition::NE
))
183 PolyVector
CDCAC::constructCharacterization(std::vector
<CACInterval
>& intervals
)
185 Assert(!intervals
.empty()) << "A covering can not be empty";
186 Trace("cdcac") << "Constructing characterization now" << std::endl
;
189 for (std::size_t i
= 0, n
= intervals
.size(); i
< n
- 1; ++i
)
191 cad::makeFinestSquareFreeBasis(intervals
[i
], intervals
[i
+ 1]);
194 for (const auto& i
: intervals
)
196 Trace("cdcac") << "Considering " << i
.d_interval
<< std::endl
;
197 Trace("cdcac") << "-> " << i
.d_lowerPolys
<< " / " << i
.d_upperPolys
198 << " and " << i
.d_mainPolys
<< " / " << i
.d_downPolys
200 Trace("cdcac") << "-> " << i
.d_origins
<< std::endl
;
201 for (const auto& p
: i
.d_downPolys
)
203 // Add all polynomial from lower levels.
206 for (const auto& p
: i
.d_mainPolys
)
208 Trace("cdcac") << "Discriminant of " << p
<< " -> " << discriminant(p
)
210 // Add all discriminants
211 res
.add(discriminant(p
));
213 for (const auto& q
: requiredCoefficients(p
))
215 // Add all required coefficients
216 Trace("cdcac") << "Coeff of " << p
<< " -> " << q
<< std::endl
;
219 for (const auto& q
: i
.d_lowerPolys
)
221 if (p
== q
) continue;
222 // Check whether p(s \times a) = 0 for some a <= l
223 if (!hasRootBelow(q
, get_lower(i
.d_interval
))) continue;
224 Trace("cdcac") << "Resultant of " << p
<< " and " << q
<< " -> "
225 << resultant(p
, q
) << std::endl
;
226 res
.add(resultant(p
, q
));
228 for (const auto& q
: i
.d_upperPolys
)
230 if (p
== q
) continue;
231 // Check whether p(s \times a) = 0 for some a >= u
232 if (!hasRootAbove(q
, get_upper(i
.d_interval
))) continue;
233 Trace("cdcac") << "Resultant of " << p
<< " and " << q
<< " -> "
234 << resultant(p
, q
) << std::endl
;
235 res
.add(resultant(p
, q
));
240 for (std::size_t i
= 0, n
= intervals
.size(); i
< n
- 1; ++i
)
242 // Add resultants of consecutive intervals.
243 for (const auto& p
: intervals
[i
].d_upperPolys
)
245 for (const auto& q
: intervals
[i
+ 1].d_lowerPolys
)
247 Trace("cdcac") << "Resultant of " << p
<< " and " << q
<< " -> "
248 << resultant(p
, q
) << std::endl
;
249 res
.add(resultant(p
, q
));
255 res
.makeFinestSquareFreeBasis();
260 CACInterval
CDCAC::intervalFromCharacterization(
261 const PolyVector
& characterization
,
262 std::size_t cur_variable
,
263 const poly::Value
& sample
)
270 for (const auto& p
: characterization
)
272 // Add polynomials to main
275 // Push lower-dimensional polys to down
276 m
.pushDownPolys(d
, d_variableOrdering
[cur_variable
]);
278 // Collect -oo, all roots, oo
279 std::vector
<poly::Value
> roots
;
280 roots
.emplace_back(poly::Value::minus_infty());
281 for (const auto& p
: m
)
283 auto tmp
= isolate_real_roots(p
, d_assignment
);
284 roots
.insert(roots
.end(), tmp
.begin(), tmp
.end());
286 roots
.emplace_back(poly::Value::plus_infty());
287 std::sort(roots
.begin(), roots
.end());
289 // Now find the interval bounds
292 for (std::size_t i
= 0, n
= roots
.size(); i
< n
; ++i
)
294 if (sample
< roots
[i
])
296 lower
= roots
[i
- 1];
300 if (roots
[i
] == sample
)
307 Assert(!is_none(lower
) && !is_none(upper
));
309 if (!is_minus_infinity(lower
))
311 // Identify polynomials that have a root at the lower bound
312 d_assignment
.set(d_variableOrdering
[cur_variable
], lower
);
313 for (const auto& p
: m
)
315 if (evaluate_constraint(p
, d_assignment
, poly::SignCondition::EQ
))
320 d_assignment
.unset(d_variableOrdering
[cur_variable
]);
322 if (!is_plus_infinity(upper
))
324 // Identify polynomials that have a root at the upper bound
325 d_assignment
.set(d_variableOrdering
[cur_variable
], upper
);
326 for (const auto& p
: m
)
328 if (evaluate_constraint(p
, d_assignment
, poly::SignCondition::EQ
))
333 d_assignment
.unset(d_variableOrdering
[cur_variable
]);
338 // construct a point interval
340 poly::Interval(lower
, false, upper
, false), l
, u
, m
, d
, {}};
344 // construct an open interval
345 Assert(lower
< upper
);
347 poly::Interval(lower
, true, upper
, true), l
, u
, m
, d
, {}};
351 std::vector
<CACInterval
> CDCAC::getUnsatCover(std::size_t curVariable
,
352 bool returnFirstInterval
)
354 if (isProofEnabled())
356 d_proof
->startRecursive();
358 Trace("cdcac") << "Looking for unsat cover for "
359 << d_variableOrdering
[curVariable
] << std::endl
;
360 std::vector
<CACInterval
> intervals
= getUnsatIntervals(curVariable
);
362 if (Trace
.isOn("cdcac"))
364 Trace("cdcac") << "Unsat intervals for " << d_variableOrdering
[curVariable
]
366 for (const auto& i
: intervals
)
368 Trace("cdcac") << "-> " << i
.d_interval
<< " from " << i
.d_origins
374 while (sampleOutsideWithInitial(intervals
, sample
, curVariable
))
376 if (!checkIntegrality(curVariable
, sample
))
378 // the variable is integral, but the sample is not.
379 Trace("cdcac") << "Used " << sample
<< " for integer variable "
380 << d_variableOrdering
[curVariable
] << std::endl
;
381 auto newInterval
= buildIntegralityInterval(curVariable
, sample
);
382 Trace("cdcac") << "Adding integrality interval " << newInterval
.d_interval
384 intervals
.emplace_back(newInterval
);
385 pruneRedundantIntervals(intervals
);
388 d_assignment
.set(d_variableOrdering
[curVariable
], sample
);
389 Trace("cdcac") << "Sample: " << d_assignment
<< std::endl
;
390 if (curVariable
== d_variableOrdering
.size() - 1)
392 // We have a full assignment. SAT!
393 Trace("cdcac") << "Found full assignment: " << d_assignment
<< std::endl
;
396 if (isProofEnabled())
398 d_proof
->startScope();
400 // Recurse to next variable
401 auto cov
= getUnsatCover(curVariable
+ 1);
405 Trace("cdcac") << "SAT!" << std::endl
;
408 Trace("cdcac") << "Refuting Sample: " << d_assignment
<< std::endl
;
409 auto characterization
= constructCharacterization(cov
);
410 Trace("cdcac") << "Characterization: " << characterization
<< std::endl
;
412 d_assignment
.unset(d_variableOrdering
[curVariable
]);
415 intervalFromCharacterization(characterization
, curVariable
, sample
);
416 newInterval
.d_origins
= collectConstraints(cov
);
417 intervals
.emplace_back(newInterval
);
418 if (isProofEnabled())
420 auto cell
= d_proof
->constructCell(
421 d_constraints
.varMapper()(d_variableOrdering
[curVariable
]),
425 d_constraints
.varMapper());
426 d_proof
->endScope(cell
);
429 if (returnFirstInterval
)
434 Trace("cdcac") << "Added " << intervals
.back().d_interval
<< std::endl
;
435 Trace("cdcac") << "\tlower: " << intervals
.back().d_lowerPolys
437 Trace("cdcac") << "\tupper: " << intervals
.back().d_upperPolys
439 Trace("cdcac") << "\tmain: " << intervals
.back().d_mainPolys
441 Trace("cdcac") << "\tdown: " << intervals
.back().d_downPolys
443 Trace("cdcac") << "\torigins: " << intervals
.back().d_origins
<< std::endl
;
445 // Remove redundant intervals
446 pruneRedundantIntervals(intervals
);
449 if (Trace
.isOn("cdcac"))
451 Trace("cdcac") << "Returning intervals for "
452 << d_variableOrdering
[curVariable
] << ":" << std::endl
;
453 for (const auto& i
: intervals
)
455 Trace("cdcac") << "-> " << i
.d_interval
<< std::endl
;
458 if (isProofEnabled())
460 d_proof
->endRecursive();
465 void CDCAC::startNewProof()
467 if (isProofEnabled())
469 d_proof
->startNewProof();
473 ProofGenerator
* CDCAC::closeProof(const std::vector
<Node
>& assertions
)
475 if (isProofEnabled())
477 d_proof
->endScope(assertions
);
478 return d_proof
->getProofGenerator();
483 bool CDCAC::checkIntegrality(std::size_t cur_variable
, const poly::Value
& value
)
485 Node var
= d_constraints
.varMapper()(d_variableOrdering
[cur_variable
]);
486 if (var
.getType() != NodeManager::currentNM()->integerType())
488 // variable is not integral
491 return poly::represents_integer(value
);
494 CACInterval
CDCAC::buildIntegralityInterval(std::size_t cur_variable
,
495 const poly::Value
& value
)
497 poly::Variable var
= d_variableOrdering
[cur_variable
];
498 poly::Integer below
= poly::floor(value
);
499 poly::Integer above
= poly::ceil(value
);
500 // construct var \in (below, above)
501 return CACInterval
{poly::Interval(below
, above
),
504 {var
- below
, var
- above
},
509 bool CDCAC::hasRootAbove(const poly::Polynomial
& p
,
510 const poly::Value
& val
) const
512 auto roots
= poly::isolate_real_roots(p
, d_assignment
);
513 return std::any_of(roots
.begin(), roots
.end(), [&val
](const poly::Value
& r
) {
518 bool CDCAC::hasRootBelow(const poly::Polynomial
& p
,
519 const poly::Value
& val
) const
521 auto roots
= poly::isolate_real_roots(p
, d_assignment
);
522 return std::any_of(roots
.begin(), roots
.end(), [&val
](const poly::Value
& r
) {
527 void CDCAC::pruneRedundantIntervals(std::vector
<CACInterval
>& intervals
)
529 if (isProofEnabled())
531 std::vector
<CACInterval
> allIntervals
= intervals
;
532 cleanIntervals(intervals
);
533 d_proof
->pruneChildren([&allIntervals
, &intervals
](std::size_t i
) {
534 return std::find(intervals
.begin(), intervals
.end(), allIntervals
[i
])
540 cleanIntervals(intervals
);
547 } // namespace theory