1 /******************************************************************************
2 * Top contributors (to current version):
3 * Tim King, Alex Ozdemir, Andrew Reynolds
5 * This file is part of the cvc5 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.
11 * ****************************************************************************
13 * [[ Add one-line brief description here ]]
15 * [[ Add lengthier description here ]]
16 * \todo document this file
19 #include "theory/arith/theory_arith_private.h"
25 #include "base/output.h"
26 #include "context/cdhashset.h"
27 #include "context/cdinsert_hashmap.h"
28 #include "context/cdlist.h"
29 #include "context/cdqueue.h"
30 #include "context/context.h"
31 #include "expr/kind.h"
32 #include "expr/metakind.h"
33 #include "expr/node.h"
34 #include "expr/node_algorithm.h"
35 #include "expr/node_builder.h"
36 #include "expr/proof_generator.h"
37 #include "expr/proof_node_manager.h"
38 #include "expr/proof_rule.h"
39 #include "expr/skolem_manager.h"
40 #include "options/arith_options.h"
41 #include "options/smt_options.h" // for incrementalSolving()
42 #include "preprocessing/util/ite_utilities.h"
43 #include "smt/logic_exception.h"
44 #include "smt/smt_statistics_registry.h"
45 #include "smt_util/boolean_simplification.h"
46 #include "theory/arith/approx_simplex.h"
47 #include "theory/arith/arith_ite_utils.h"
48 #include "theory/arith/arith_rewriter.h"
49 #include "theory/arith/arith_static_learner.h"
50 #include "theory/arith/arith_utilities.h"
51 #include "theory/arith/arithvar.h"
52 #include "theory/arith/congruence_manager.h"
53 #include "theory/arith/constraint.h"
54 #include "theory/arith/cut_log.h"
55 #include "theory/arith/delta_rational.h"
56 #include "theory/arith/dio_solver.h"
57 #include "theory/arith/linear_equality.h"
58 #include "theory/arith/matrix.h"
59 #include "theory/arith/nl/nonlinear_extension.h"
60 #include "theory/arith/normal_form.h"
61 #include "theory/arith/partial_model.h"
62 #include "theory/arith/simplex.h"
63 #include "theory/arith/theory_arith.h"
64 #include "theory/ext_theory.h"
65 #include "theory/quantifiers/fmf/bounded_integers.h"
66 #include "theory/rewriter.h"
67 #include "theory/theory_model.h"
68 #include "theory/trust_substitutions.h"
69 #include "theory/valuation.h"
70 #include "util/dense_map.h"
71 #include "util/integer.h"
72 #include "util/random.h"
73 #include "util/rational.h"
74 #include "util/result.h"
75 #include "util/statistics_stats.h"
78 using namespace cvc5::kind
;
84 static Node
toSumNode(const ArithVariables
& vars
, const DenseMap
<Rational
>& sum
);
85 static bool complexityBelow(const DenseMap
<Rational
>& row
, uint32_t cap
);
87 TheoryArithPrivate::TheoryArithPrivate(TheoryArith
& containing
,
89 context::UserContext
* u
,
92 const LogicInfo
& logicInfo
,
93 ProofNodeManager
* pnm
)
94 : d_containing(containing
),
99 d_pfGen(new EagerProofGenerator(d_pnm
, u
)),
100 d_constraintDatabase(c
,
104 RaiseConflict(*this),
107 d_qflraStatus(Result::SAT_UNKNOWN
),
109 d_hasDoneWorkSinceCut(false),
111 d_assertionsThatDoNotMatchTheirLiterals(c
),
112 d_nextIntegerCheckVar(0),
113 d_constantIntegerVariables(c
),
114 d_diseqQueue(c
, false),
115 d_currentPropagationList(),
117 d_preregisteredNodes(u
),
118 d_partialModel(c
, DeltaComputeCallback(*this)),
120 d_partialModel
, TableauSizes(&d_tableau
), BoundCountingLookup(*this)),
122 d_linEq(d_partialModel
,
125 BasicVarModelUpdateCallBack(*this)),
127 d_restartsCounter(0),
128 d_tableauSizeHasBeenModified(false),
129 d_tableauResetDensity(1.6),
130 d_tableauResetPeriod(10),
132 d_blackBoxConflict(c
, Node::null()),
133 d_blackBoxConflictPf(c
, std::shared_ptr
<ProofNode
>(nullptr)),
134 d_congruenceManager(c
,
136 d_constraintDatabase
,
137 SetupLiteralCallBack(*this),
139 RaiseEqualityEngineConflict(*this),
141 d_cmEnabled(c
, true),
144 d_linEq
, d_errorSet
, RaiseConflict(*this), TempVarMalloc(*this)),
146 d_linEq
, d_errorSet
, RaiseConflict(*this), TempVarMalloc(*this)),
148 d_linEq
, d_errorSet
, RaiseConflict(*this), TempVarMalloc(*this)),
150 d_linEq
, d_errorSet
, RaiseConflict(*this), TempVarMalloc(*this)),
153 d_lastContextIntegerAttempted(c
, -1),
157 d_fullCheckCounter(0),
160 d_likelyIntegerInfeasible(c
, false),
161 d_guessedCoeffSet(c
, false),
165 d_replayConstraints(),
168 d_attemptSolveIntTurnedOff(u
, 0),
169 d_dioSolveResources(0),
170 d_solveIntMaybeHelp(0u),
171 d_solveIntAttempts(0u),
173 d_previousStatus(Result::SAT_UNKNOWN
),
174 d_statistics("theory::arith::")
178 TheoryArithPrivate::~TheoryArithPrivate(){
179 if(d_treeLog
!= NULL
){ delete d_treeLog
; }
180 if(d_approxStats
!= NULL
) { delete d_approxStats
; }
183 bool TheoryArithPrivate::needsEqualityEngine(EeSetupInfo
& esi
)
185 return d_congruenceManager
.needsEqualityEngine(esi
);
187 void TheoryArithPrivate::finishInit()
189 eq::EqualityEngine
* ee
= d_containing
.getEqualityEngine();
190 eq::ProofEqEngine
* pfee
= d_containing
.getProofEqEngine();
191 Assert(ee
!= nullptr);
192 d_congruenceManager
.finishInit(ee
, pfee
);
195 static bool contains(const ConstraintCPVec
& v
, ConstraintP con
){
196 for(unsigned i
= 0, N
= v
.size(); i
< N
; ++i
){
203 static void drop( ConstraintCPVec
& v
, ConstraintP con
){
204 size_t readPos
, writePos
, N
;
205 for(readPos
= 0, writePos
= 0, N
= v
.size(); readPos
< N
; ++readPos
){
206 ConstraintCP curr
= v
[readPos
];
216 static void resolve(ConstraintCPVec
& buf
, ConstraintP c
, const ConstraintCPVec
& pos
, const ConstraintCPVec
& neg
){
217 unsigned posPos CVC5_UNUSED
= pos
.size();
218 for(unsigned i
= 0, N
= pos
.size(); i
< N
; ++i
){
222 buf
.push_back(pos
[i
]);
225 Assert(posPos
< pos
.size());
226 ConstraintP negc
= c
->getNegation();
227 unsigned negPos CVC5_UNUSED
= neg
.size();
228 for(unsigned i
= 0, N
= neg
.size(); i
< N
; ++i
){
232 buf
.push_back(neg
[i
]);
235 Assert(negPos
< neg
.size());
237 // Assert(dnconf.getKind() == kind::AND);
238 // Assert(upconf.getKind() == kind::AND);
239 // Assert(dnpos < dnconf.getNumChildren());
240 // Assert(uppos < upconf.getNumChildren());
241 // Assert(equalUpToNegation(dnconf[dnpos], upconf[uppos]));
243 // NodeBuilder nb(kind::AND);
244 // dropPosition(nb, dnconf, dnpos);
245 // dropPosition(nb, upconf, uppos);
246 // return safeConstructNary(nb);
249 TheoryArithPrivate::ModelException::ModelException(TNode n
, const char* msg
)
252 ss
<< "Cannot construct a model for " << n
<< " as " << endl
<< msg
;
253 setMessage(ss
.str());
255 TheoryArithPrivate::ModelException::~ModelException() {}
257 TheoryArithPrivate::Statistics::Statistics(const std::string
& name
)
258 : d_statAssertUpperConflicts(
259 smtStatisticsRegistry().registerInt(name
+ "AssertUpperConflicts")),
260 d_statAssertLowerConflicts(
261 smtStatisticsRegistry().registerInt(name
+ "AssertLowerConflicts")),
263 smtStatisticsRegistry().registerInt(name
+ "UserVariables")),
264 d_statAuxiliaryVariables(
265 smtStatisticsRegistry().registerInt(name
+ "AuxiliaryVariables")),
266 d_statDisequalitySplits(
267 smtStatisticsRegistry().registerInt(name
+ "DisequalitySplits")),
268 d_statDisequalityConflicts(
269 smtStatisticsRegistry().registerInt(name
+ "DisequalityConflicts")),
271 smtStatisticsRegistry().registerTimer(name
+ "simplifyTimer")),
272 d_staticLearningTimer(
273 smtStatisticsRegistry().registerTimer(name
+ "staticLearningTimer")),
275 smtStatisticsRegistry().registerTimer(name
+ "presolveTime")),
277 smtStatisticsRegistry().registerTimer(name
+ "newPropTimer")),
278 d_externalBranchAndBounds(smtStatisticsRegistry().registerInt(
279 name
+ "externalBranchAndBounds")),
280 d_initialTableauSize(
281 smtStatisticsRegistry().registerInt(name
+ "initialTableauSize")),
283 smtStatisticsRegistry().registerInt(name
+ "currSetToSmaller")),
285 smtStatisticsRegistry().registerInt(name
+ "smallerSetToCurr")),
287 smtStatisticsRegistry().registerTimer(name
+ "restartTimer")),
288 d_boundComputationTime(
289 smtStatisticsRegistry().registerTimer(name
+ "bound::time")),
290 d_boundComputations(smtStatisticsRegistry().registerInt(
291 name
+ "bound::boundComputations")),
292 d_boundPropagations(smtStatisticsRegistry().registerInt(
293 name
+ "bound::boundPropagations")),
295 smtStatisticsRegistry().registerInt(name
+ "status::unknowns")),
296 d_maxUnknownsInARow(smtStatisticsRegistry().registerInt(
297 name
+ "status::maxUnknownsInARow")),
298 d_avgUnknownsInARow(smtStatisticsRegistry().registerAverage(
299 name
+ "status::avgUnknownsInARow")),
300 d_revertsOnConflicts(smtStatisticsRegistry().registerInt(
301 name
+ "status::revertsOnConflicts")),
302 d_commitsOnConflicts(smtStatisticsRegistry().registerInt(
303 name
+ "status::commitsOnConflicts")),
304 d_nontrivialSatChecks(smtStatisticsRegistry().registerInt(
305 name
+ "status::nontrivialSatChecks")),
307 smtStatisticsRegistry().registerInt(name
+ "z::approx::replay::rec")),
308 d_replayLogRecConflictEscalation(smtStatisticsRegistry().registerInt(
309 name
+ "z::approx::replay::rec::escalation")),
310 d_replayLogRecEarlyExit(smtStatisticsRegistry().registerInt(
311 name
+ "z::approx::replay::rec::earlyexit")),
312 d_replayBranchCloseFailures(smtStatisticsRegistry().registerInt(
313 name
+ "z::approx::replay::rec::branch::closefailures")),
314 d_replayLeafCloseFailures(smtStatisticsRegistry().registerInt(
315 name
+ "z::approx::replay::rec::leaf::closefailures")),
316 d_replayBranchSkips(smtStatisticsRegistry().registerInt(
317 name
+ "z::approx::replay::rec::branch::skips")),
318 d_mirCutsAttempted(smtStatisticsRegistry().registerInt(
319 name
+ "z::approx::cuts::mir::attempted")),
320 d_gmiCutsAttempted(smtStatisticsRegistry().registerInt(
321 name
+ "z::approx::cuts::gmi::attempted")),
322 d_branchCutsAttempted(smtStatisticsRegistry().registerInt(
323 name
+ "z::approx::cuts::branch::attempted")),
324 d_cutsReconstructed(smtStatisticsRegistry().registerInt(
325 name
+ "z::approx::cuts::reconstructed")),
326 d_cutsReconstructionFailed(smtStatisticsRegistry().registerInt(
327 name
+ "z::approx::cuts::reconstructed::failed")),
328 d_cutsProven(smtStatisticsRegistry().registerInt(
329 name
+ "z::approx::cuts::proofs")),
330 d_cutsProofFailed(smtStatisticsRegistry().registerInt(
331 name
+ "z::approx::cuts::proofs::failed")),
332 d_mipReplayLemmaCalls(smtStatisticsRegistry().registerInt(
333 name
+ "z::approx::external::calls")),
334 d_mipExternalCuts(smtStatisticsRegistry().registerInt(
335 name
+ "z::approx::external::cuts")),
336 d_mipExternalBranch(smtStatisticsRegistry().registerInt(
337 name
+ "z::approx::external::branches")),
338 d_inSolveInteger(smtStatisticsRegistry().registerInt(
339 name
+ "z::approx::inSolverInteger")),
340 d_branchesExhausted(smtStatisticsRegistry().registerInt(
341 name
+ "z::approx::exhausted::branches")),
342 d_execExhausted(smtStatisticsRegistry().registerInt(
343 name
+ "z::approx::exhausted::exec")),
344 d_pivotsExhausted(smtStatisticsRegistry().registerInt(
345 name
+ "z::approx::exhausted::pivots")),
347 smtStatisticsRegistry().registerInt(name
+ "z::arith::paniclemmas")),
349 smtStatisticsRegistry().registerInt(name
+ "z::arith::relax::calls")),
350 d_relaxLinFeas(smtStatisticsRegistry().registerInt(
351 name
+ "z::arith::relax::feasible::res")),
352 d_relaxLinFeasFailures(smtStatisticsRegistry().registerInt(
353 name
+ "z::arith::relax::feasible::failures")),
354 d_relaxLinInfeas(smtStatisticsRegistry().registerInt(
355 name
+ "z::arith::relax::infeasible")),
356 d_relaxLinInfeasFailures(smtStatisticsRegistry().registerInt(
357 name
+ "z::arith::relax::infeasible::failures")),
358 d_relaxLinExhausted(smtStatisticsRegistry().registerInt(
359 name
+ "z::arith::relax::exhausted")),
361 smtStatisticsRegistry().registerInt(name
+ "z::arith::relax::other")),
362 d_applyRowsDeleted(smtStatisticsRegistry().registerInt(
363 name
+ "z::arith::cuts::applyRowsDeleted")),
364 d_replaySimplexTimer(smtStatisticsRegistry().registerTimer(
365 name
+ "z::approx::replay::simplex::timer")),
366 d_replayLogTimer(smtStatisticsRegistry().registerTimer(
367 name
+ "z::approx::replay::log::timer")),
369 smtStatisticsRegistry().registerTimer(name
+ "z::solveInt::timer")),
370 d_solveRealRelaxTimer(smtStatisticsRegistry().registerTimer(
371 name
+ "z::solveRealRelax::timer")),
373 smtStatisticsRegistry().registerInt(name
+ "z::solveInt::calls")),
374 d_solveStandardEffort(smtStatisticsRegistry().registerInt(
375 name
+ "z::solveInt::calls::standardEffort")),
377 smtStatisticsRegistry().registerInt(name
+ "z::approxDisabled")),
378 d_replayAttemptFailed(
379 smtStatisticsRegistry().registerInt(name
+ "z::replayAttemptFailed")),
380 d_cutsRejectedDuringReplay(smtStatisticsRegistry().registerInt(
381 name
+ "z::approx::replay::cuts::rejected")),
382 d_cutsRejectedDuringLemmas(smtStatisticsRegistry().registerInt(
383 name
+ "z::approx::external::cuts::rejected")),
384 d_satPivots(smtStatisticsRegistry().registerHistogram
<uint32_t>(
385 name
+ "pivots::sat")),
386 d_unsatPivots(smtStatisticsRegistry().registerHistogram
<uint32_t>(
387 name
+ "pivots::unsat")),
388 d_unknownPivots(smtStatisticsRegistry().registerHistogram
<uint32_t>(
389 name
+ "pivots::unknown")),
390 d_solveIntModelsAttempts(smtStatisticsRegistry().registerInt(
391 name
+ "z::solveInt::models::attempts")),
392 d_solveIntModelsSuccessful(smtStatisticsRegistry().registerInt(
393 name
+ "zzz::solveInt::models::successful")),
394 d_mipTimer(smtStatisticsRegistry().registerTimer(
395 name
+ "z::approx::mip::timer")),
397 smtStatisticsRegistry().registerTimer(name
+ "z::approx::lp::timer")),
398 d_mipProofsAttempted(smtStatisticsRegistry().registerInt(
399 name
+ "z::mip::proofs::attempted")),
400 d_mipProofsSuccessful(smtStatisticsRegistry().registerInt(
401 name
+ "z::mip::proofs::successful")),
402 d_numBranchesFailed(smtStatisticsRegistry().registerInt(
403 name
+ "z::mip::branch::proof::failed"))
407 bool complexityBelow(const DenseMap
<Rational
>& row
, uint32_t cap
){
408 DenseMap
<Rational
>::const_iterator riter
, rend
;
409 for(riter
=row
.begin(), rend
=row
.end(); riter
!= rend
; ++riter
){
411 const Rational
& q
= row
[v
];
412 if(q
.complexity() > cap
){
419 bool TheoryArithPrivate::isProofEnabled() const
421 return d_pnm
!= nullptr;
424 void TheoryArithPrivate::raiseConflict(ConstraintCP a
, InferenceId id
){
425 Assert(a
->inConflict());
426 d_conflicts
.push_back(std::make_pair(a
, id
));
429 void TheoryArithPrivate::raiseBlackBoxConflict(Node bb
,
430 std::shared_ptr
<ProofNode
> pf
)
432 Debug("arith::bb") << "raiseBlackBoxConflict: " << bb
<< std::endl
;
433 if (d_blackBoxConflict
.get().isNull())
435 if (isProofEnabled())
437 Debug("arith::bb") << " with proof " << pf
<< std::endl
;
438 d_blackBoxConflictPf
.set(pf
);
440 d_blackBoxConflict
= bb
;
444 bool TheoryArithPrivate::anyConflict() const
446 return !conflictQueueEmpty() || !d_blackBoxConflict
.get().isNull();
449 void TheoryArithPrivate::revertOutOfConflict(){
450 d_partialModel
.revertAssignmentChanges();
452 d_currentPropagationList
.clear();
455 void TheoryArithPrivate::clearUpdates(){
456 d_updatedBounds
.purge();
459 // void TheoryArithPrivate::raiseConflict(ConstraintCP a, ConstraintCP b){
460 // ConstraintCPVec v;
463 // d_conflicts.push_back(v);
466 // void TheoryArithPrivate::raiseConflict(ConstraintCP a, ConstraintCP b, ConstraintCP c){
467 // ConstraintCPVec v;
471 // d_conflicts.push_back(v);
474 void TheoryArithPrivate::zeroDifferenceDetected(ArithVar x
){
476 Assert(d_congruenceManager
.isWatchedVariable(x
));
477 Assert(d_partialModel
.upperBoundIsZero(x
));
478 Assert(d_partialModel
.lowerBoundIsZero(x
));
480 ConstraintP lb
= d_partialModel
.getLowerBoundConstraint(x
);
481 ConstraintP ub
= d_partialModel
.getUpperBoundConstraint(x
);
483 if(lb
->isEquality()){
484 d_congruenceManager
.watchedVariableIsZero(lb
);
485 }else if(ub
->isEquality()){
486 d_congruenceManager
.watchedVariableIsZero(ub
);
488 d_congruenceManager
.watchedVariableIsZero(lb
, ub
);
493 bool TheoryArithPrivate::getSolveIntegerResource(){
494 if(d_attemptSolveIntTurnedOff
> 0){
495 d_attemptSolveIntTurnedOff
= d_attemptSolveIntTurnedOff
- 1;
502 bool TheoryArithPrivate::getDioCuttingResource(){
503 if(d_dioSolveResources
> 0){
504 d_dioSolveResources
--;
505 if(d_dioSolveResources
== 0){
506 d_dioSolveResources
= -options::rrTurns();
510 d_dioSolveResources
++;
511 if(d_dioSolveResources
>= 0){
512 d_dioSolveResources
= options::dioSolverTurns();
518 /* procedure AssertLower( x_i >= c_i ) */
519 bool TheoryArithPrivate::AssertLower(ConstraintP constraint
){
520 Assert(constraint
!= NullConstraint
);
521 Assert(constraint
->isLowerBound());
522 Assert(constraint
->isTrue());
523 Assert(!constraint
->negationHasProof());
525 ArithVar x_i
= constraint
->getVariable();
526 const DeltaRational
& c_i
= constraint
->getValue();
528 Debug("arith") << "AssertLower(" << x_i
<< " " << c_i
<< ")"<< std::endl
;
530 Assert(!isInteger(x_i
) || c_i
.isIntegral());
532 //TODO Relax to less than?
533 if(d_partialModel
.lessThanLowerBound(x_i
, c_i
)){
537 int cmpToUB
= d_partialModel
.cmpToUpperBound(x_i
, c_i
);
538 if(cmpToUB
> 0){ // c_i < \lowerbound(x_i)
539 ConstraintP ubc
= d_partialModel
.getUpperBoundConstraint(x_i
);
540 ConstraintP negation
= constraint
->getNegation();
541 negation
->impliedByUnate(ubc
, true);
543 raiseConflict(constraint
, InferenceId::ARITH_CONF_LOWER
);
545 ++(d_statistics
.d_statAssertLowerConflicts
);
547 }else if(cmpToUB
== 0){
549 d_constantIntegerVariables
.push_back(x_i
);
550 Debug("dio::push") << "dio::push " << x_i
<< endl
;
552 ConstraintP ub
= d_partialModel
.getUpperBoundConstraint(x_i
);
555 if(!d_congruenceManager
.isWatchedVariable(x_i
) || c_i
.sgn() != 0){
556 // if it is not a watched variable report it
557 // if it is is a watched variable and c_i == 0,
558 // let zeroDifferenceDetected(x_i) catch this
559 d_congruenceManager
.equalsConstant(constraint
, ub
);
563 const ValueCollection
& vc
= constraint
->getValueCollection();
564 if(vc
.hasEquality()){
565 Assert(vc
.hasDisequality());
566 ConstraintP eq
= vc
.getEquality();
567 ConstraintP diseq
= vc
.getDisequality();
568 // x <= b, x >= b |= x = b
569 // (x > b or x < b or x = b)
570 Debug("arith::eq") << "lb == ub, propagate eq" << eq
<< endl
;
571 bool triConflict
= diseq
->isTrue();
574 eq
->impliedByTrichotomy(constraint
, ub
, triConflict
);
575 eq
->tryToPropagate();
578 ++(d_statistics
.d_statDisequalityConflicts
);
579 raiseConflict(eq
, InferenceId::ARITH_CONF_TRICHOTOMY
);
584 // l <= x <= u and l < u
586 const ValueCollection
& vc
= constraint
->getValueCollection();
588 if(vc
.hasDisequality()){
589 const ConstraintP diseq
= vc
.getDisequality();
591 const ConstraintP ub
= d_constraintDatabase
.ensureConstraint(const_cast<ValueCollection
&>(vc
), UpperBound
);
592 ConstraintP negUb
= ub
->getNegation();
594 // l <= x, l != x |= l < x
596 bool ubInConflict
= ub
->hasProof();
597 bool learnNegUb
= !(negUb
->hasProof());
599 negUb
->impliedByTrichotomy(constraint
, diseq
, ubInConflict
);
600 negUb
->tryToPropagate();
603 raiseConflict(ub
, InferenceId::ARITH_CONF_TRICHOTOMY
);
605 }else if(learnNegUb
){
606 d_learnedBounds
.push_back(negUb
);
612 d_currentPropagationList
.push_back(constraint
);
613 d_currentPropagationList
.push_back(d_partialModel
.getLowerBoundConstraint(x_i
));
615 d_partialModel
.setLowerBoundConstraint(constraint
);
618 if(d_congruenceManager
.isWatchedVariable(x_i
)){
621 d_congruenceManager
.watchedVariableCannotBeZero(constraint
);
622 }else if(sgn
== 0 && d_partialModel
.upperBoundIsZero(x_i
)){
623 zeroDifferenceDetected(x_i
);
628 d_updatedBounds
.softAdd(x_i
);
630 if(Debug
.isOn("model")) {
631 Debug("model") << "before" << endl
;
632 d_partialModel
.printModel(x_i
);
633 d_tableau
.debugPrintIsBasic(x_i
);
636 if(!d_tableau
.isBasic(x_i
)){
637 if(d_partialModel
.getAssignment(x_i
) < c_i
){
638 d_linEq
.update(x_i
, c_i
);
641 d_errorSet
.signalVariable(x_i
);
644 if(Debug
.isOn("model")) {
645 Debug("model") << "after" << endl
;
646 d_partialModel
.printModel(x_i
);
647 d_tableau
.debugPrintIsBasic(x_i
);
653 /* procedure AssertUpper( x_i <= c_i) */
654 bool TheoryArithPrivate::AssertUpper(ConstraintP constraint
){
655 Assert(constraint
!= NullConstraint
);
656 Assert(constraint
->isUpperBound());
657 Assert(constraint
->isTrue());
658 Assert(!constraint
->negationHasProof());
660 ArithVar x_i
= constraint
->getVariable();
661 const DeltaRational
& c_i
= constraint
->getValue();
663 Debug("arith") << "AssertUpper(" << x_i
<< " " << c_i
<< ")"<< std::endl
;
666 //Too strong because of rounding with integers
667 //Assert(!constraint->hasLiteral() || original == constraint->getLiteral());
668 Assert(!isInteger(x_i
) || c_i
.isIntegral());
670 Debug("arith") << "AssertUpper(" << x_i
<< " " << c_i
<< ")"<< std::endl
;
672 if(d_partialModel
.greaterThanUpperBound(x_i
, c_i
) ){ // \upperbound(x_i) <= c_i
676 // cmpToLb = \lowerbound(x_i).cmp(c_i)
677 int cmpToLB
= d_partialModel
.cmpToLowerBound(x_i
, c_i
);
678 if( cmpToLB
< 0 ){ // \upperbound(x_i) < \lowerbound(x_i)
679 // l_i <= x_i and c_i < l_i |= c_i < x_i
680 // or ... |= not (x_i <= c_i)
681 ConstraintP lbc
= d_partialModel
.getLowerBoundConstraint(x_i
);
682 ConstraintP negConstraint
= constraint
->getNegation();
683 negConstraint
->impliedByUnate(lbc
, true);
684 raiseConflict(constraint
, InferenceId::ARITH_CONF_UPPER
);
685 ++(d_statistics
.d_statAssertUpperConflicts
);
687 }else if(cmpToLB
== 0){ // \lowerBound(x_i) == \upperbound(x_i)
689 d_constantIntegerVariables
.push_back(x_i
);
690 Debug("dio::push") << "dio::push " << x_i
<< endl
;
693 const ValueCollection
& vc
= constraint
->getValueCollection();
694 ConstraintP lb
= d_partialModel
.getLowerBoundConstraint(x_i
);
696 if(!d_congruenceManager
.isWatchedVariable(x_i
) || c_i
.sgn() != 0){
697 // if it is not a watched variable report it
698 // if it is is a watched variable and c_i == 0,
699 // let zeroDifferenceDetected(x_i) catch this
700 d_congruenceManager
.equalsConstant(lb
, constraint
);
704 if(vc
.hasDisequality()){
705 Assert(vc
.hasDisequality());
706 ConstraintP eq
= vc
.getEquality();
707 ConstraintP diseq
= vc
.getDisequality();
708 // x <= b, x >= b |= x = b
709 // (x > b or x < b or x = b)
710 Debug("arith::eq") << "lb == ub, propagate eq" << eq
<< endl
;
711 bool triConflict
= diseq
->isTrue();
713 eq
->impliedByTrichotomy(constraint
, lb
, triConflict
);
714 eq
->tryToPropagate();
717 ++(d_statistics
.d_statDisequalityConflicts
);
718 raiseConflict(eq
, InferenceId::ARITH_CONF_TRICHOTOMY
);
722 }else if(cmpToLB
> 0){
723 // l <= x <= u and l < u
725 const ValueCollection
& vc
= constraint
->getValueCollection();
727 if(vc
.hasDisequality()){
728 const ConstraintP diseq
= vc
.getDisequality();
730 const ConstraintP lb
= d_constraintDatabase
.ensureConstraint(const_cast<ValueCollection
&>(vc
), LowerBound
);
731 ConstraintP negLb
= lb
->getNegation();
733 // x <= u, u != x |= u < x
735 bool lbInConflict
= lb
->hasProof();
736 bool learnNegLb
= !(negLb
->hasProof());
738 negLb
->impliedByTrichotomy(constraint
, diseq
, lbInConflict
);
739 negLb
->tryToPropagate();
742 raiseConflict(lb
, InferenceId::ARITH_CONF_TRICHOTOMY
);
744 }else if(learnNegLb
){
745 d_learnedBounds
.push_back(negLb
);
751 d_currentPropagationList
.push_back(constraint
);
752 d_currentPropagationList
.push_back(d_partialModel
.getUpperBoundConstraint(x_i
));
753 //It is fine if this is NullConstraint
755 d_partialModel
.setUpperBoundConstraint(constraint
);
758 if(d_congruenceManager
.isWatchedVariable(x_i
)){
761 d_congruenceManager
.watchedVariableCannotBeZero(constraint
);
762 }else if(sgn
== 0 && d_partialModel
.lowerBoundIsZero(x_i
)){
763 zeroDifferenceDetected(x_i
);
768 d_updatedBounds
.softAdd(x_i
);
770 if(Debug
.isOn("model")) {
771 Debug("model") << "before" << endl
;
772 d_partialModel
.printModel(x_i
);
773 d_tableau
.debugPrintIsBasic(x_i
);
776 if(!d_tableau
.isBasic(x_i
)){
777 if(d_partialModel
.getAssignment(x_i
) > c_i
){
778 d_linEq
.update(x_i
, c_i
);
781 d_errorSet
.signalVariable(x_i
);
784 if(Debug
.isOn("model")) {
785 Debug("model") << "after" << endl
;
786 d_partialModel
.printModel(x_i
);
787 d_tableau
.debugPrintIsBasic(x_i
);
794 /* procedure AssertEquality( x_i == c_i ) */
795 bool TheoryArithPrivate::AssertEquality(ConstraintP constraint
){
796 Assert(constraint
!= NullConstraint
);
797 Assert(constraint
->isEquality());
798 Assert(constraint
->isTrue());
799 Assert(!constraint
->negationHasProof());
801 ArithVar x_i
= constraint
->getVariable();
802 const DeltaRational
& c_i
= constraint
->getValue();
804 Debug("arith") << "AssertEquality(" << x_i
<< " " << c_i
<< ")"<< std::endl
;
806 //Should be fine in integers
807 Assert(!isInteger(x_i
) || c_i
.isIntegral());
809 int cmpToLB
= d_partialModel
.cmpToLowerBound(x_i
, c_i
);
810 int cmpToUB
= d_partialModel
.cmpToUpperBound(x_i
, c_i
);
813 // This can happen if both c_i <= x_i and x_i <= c_i are in the system.
814 if(cmpToUB
>= 0 && cmpToLB
<= 0){
818 if(cmpToUB
> 0 || cmpToLB
< 0){
819 ConstraintP cb
= (cmpToUB
> 0) ? d_partialModel
.getUpperBoundConstraint(x_i
) :
820 d_partialModel
.getLowerBoundConstraint(x_i
);
821 ConstraintP diseq
= constraint
->getNegation();
822 Assert(!diseq
->isTrue());
823 diseq
->impliedByUnate(cb
, true);
824 raiseConflict(constraint
, InferenceId::ARITH_CONF_EQ
);
828 Assert(cmpToUB
<= 0);
829 Assert(cmpToLB
>= 0);
830 Assert(cmpToUB
< 0 || cmpToLB
> 0);
833 d_constantIntegerVariables
.push_back(x_i
);
834 Debug("dio::push") << "dio::push " << x_i
<< endl
;
837 // Don't bother to check whether x_i != c_i is in d_diseq
838 // The a and (not a) should never be on the fact queue
839 d_currentPropagationList
.push_back(constraint
);
840 d_currentPropagationList
.push_back(d_partialModel
.getLowerBoundConstraint(x_i
));
841 d_currentPropagationList
.push_back(d_partialModel
.getUpperBoundConstraint(x_i
));
843 d_partialModel
.setUpperBoundConstraint(constraint
);
844 d_partialModel
.setLowerBoundConstraint(constraint
);
847 if(d_congruenceManager
.isWatchedVariable(x_i
)){
850 zeroDifferenceDetected(x_i
);
852 d_congruenceManager
.watchedVariableCannotBeZero(constraint
);
853 d_congruenceManager
.equalsConstant(constraint
);
856 d_congruenceManager
.equalsConstant(constraint
);
860 d_updatedBounds
.softAdd(x_i
);
862 if(Debug
.isOn("model")) {
863 Debug("model") << "before" << endl
;
864 d_partialModel
.printModel(x_i
);
865 d_tableau
.debugPrintIsBasic(x_i
);
868 if(!d_tableau
.isBasic(x_i
)){
869 if(!(d_partialModel
.getAssignment(x_i
) == c_i
)){
870 d_linEq
.update(x_i
, c_i
);
873 d_errorSet
.signalVariable(x_i
);
876 if(Debug
.isOn("model")) {
877 Debug("model") << "after" << endl
;
878 d_partialModel
.printModel(x_i
);
879 d_tableau
.debugPrintIsBasic(x_i
);
886 /* procedure AssertDisequality( x_i != c_i ) */
887 bool TheoryArithPrivate::AssertDisequality(ConstraintP constraint
){
888 Assert(constraint
!= NullConstraint
);
889 Assert(constraint
->isDisequality());
890 Assert(constraint
->isTrue());
891 Assert(!constraint
->negationHasProof());
893 ArithVar x_i
= constraint
->getVariable();
894 const DeltaRational
& c_i
= constraint
->getValue();
895 Debug("arith") << "AssertDisequality(" << x_i
<< " " << c_i
<< ")"<< std::endl
;
897 //Should be fine in integers
898 Assert(!isInteger(x_i
) || c_i
.isIntegral());
901 if(d_congruenceManager
.isWatchedVariable(x_i
)){
904 d_congruenceManager
.watchedVariableCannotBeZero(constraint
);
909 const ValueCollection
& vc
= constraint
->getValueCollection();
910 if(vc
.hasLowerBound() && vc
.hasUpperBound()){
911 const ConstraintP lb
= vc
.getLowerBound();
912 const ConstraintP ub
= vc
.getUpperBound();
913 if(lb
->isTrue() && ub
->isTrue()){
914 ConstraintP eq
= constraint
->getNegation();
915 eq
->impliedByTrichotomy(lb
, ub
, true);
916 raiseConflict(constraint
, InferenceId::ARITH_CONF_TRICHOTOMY
);
918 ++(d_statistics
.d_statDisequalityConflicts
);
922 if(vc
.hasLowerBound() ){
923 const ConstraintP lb
= vc
.getLowerBound();
925 const ConstraintP ub
= d_constraintDatabase
.ensureConstraint(const_cast<ValueCollection
&>(vc
), UpperBound
);
926 Assert(!ub
->isTrue());
927 Debug("arith::eq") << "propagate UpperBound " << constraint
<< lb
<< ub
<< endl
;
928 const ConstraintP negUb
= ub
->getNegation();
929 if(!negUb
->isTrue()){
930 negUb
->impliedByTrichotomy(constraint
, lb
, false);
931 negUb
->tryToPropagate();
932 d_learnedBounds
.push_back(negUb
);
936 if(vc
.hasUpperBound()){
937 const ConstraintP ub
= vc
.getUpperBound();
939 const ConstraintP lb
= d_constraintDatabase
.ensureConstraint(const_cast<ValueCollection
&>(vc
), LowerBound
);
940 Assert(!lb
->isTrue());
942 Debug("arith::eq") << "propagate LowerBound " << constraint
<< lb
<< ub
<< endl
;
943 const ConstraintP negLb
= lb
->getNegation();
944 if(!negLb
->isTrue()){
945 negLb
->impliedByTrichotomy(constraint
, ub
, false);
946 negLb
->tryToPropagate();
947 d_learnedBounds
.push_back(negLb
);
952 bool split
= constraint
->isSplit();
954 if(!split
&& c_i
== d_partialModel
.getAssignment(x_i
)){
955 Debug("arith::eq") << "lemma now! " << constraint
<< endl
;
956 outputTrustedLemma(constraint
->split(), InferenceId::ARITH_SPLIT_DEQ
);
958 }else if(d_partialModel
.strictlyLessThanLowerBound(x_i
, c_i
)){
959 Debug("arith::eq") << "can drop as less than lb" << constraint
<< endl
;
960 }else if(d_partialModel
.strictlyGreaterThanUpperBound(x_i
, c_i
)){
961 Debug("arith::eq") << "can drop as less than ub" << constraint
<< endl
;
963 Debug("arith::eq") << "push back" << constraint
<< endl
;
964 d_diseqQueue
.push(constraint
);
965 d_partialModel
.invalidateDelta();
967 Debug("arith::eq") << "skipping already split " << constraint
<< endl
;
972 void TheoryArithPrivate::notifySharedTerm(TNode n
)
974 Debug("arith::notifySharedTerm") << "notifySharedTerm: " << n
<< endl
;
976 d_partialModel
.invalidateDelta();
978 if(!n
.isConst() && !isSetup(n
)){
979 Polynomial poly
= Polynomial::parsePolynomial(n
);
980 Polynomial::iterator it
= poly
.begin();
981 Polynomial::iterator it_end
= poly
.end();
982 for (; it
!= it_end
; ++ it
) {
984 if (!m
.isConstant() && !isSetup(m
.getVarList().getNode())) {
985 setupVariableList(m
.getVarList());
991 Node
TheoryArithPrivate::getModelValue(TNode term
) {
993 const DeltaRational drv
= getDeltaValue(term
);
994 const Rational
& delta
= d_partialModel
.getDelta();
995 const Rational qmodel
= drv
.substituteDelta( delta
);
996 return mkRationalNode( qmodel
);
997 } catch (DeltaRationalException
& dr
) {
999 } catch (ModelException
& me
) {
1000 return Node::null();
1004 Theory::PPAssertStatus
TheoryArithPrivate::ppAssert(
1005 TrustNode tin
, TrustSubstitutionMap
& outSubstitutions
)
1007 TimerStat::CodeTimer
codeTimer(d_statistics
.d_simplifyTimer
);
1008 TNode in
= tin
.getNode();
1009 Debug("simplify") << "TheoryArithPrivate::solve(" << in
<< ")" << endl
;
1013 Rational minConstant
= 0;
1016 if (in
.getKind() == kind::EQUAL
&&
1017 Theory::theoryOf(in
[0].getType()) == THEORY_ARITH
) {
1018 Comparison cmp
= Comparison::parseNormalForm(in
);
1020 Polynomial left
= cmp
.getLeft();
1022 Monomial m
= left
.getHead();
1023 if (m
.getVarList().singleton()){
1024 VarList vl
= m
.getVarList();
1025 Node var
= vl
.getNode();
1028 // if vl.isIntegral then m.getConstant().isOne()
1029 if(!vl
.isIntegral() || m
.getConstant().isOne()){
1035 // Solve for variable
1036 if (!minVar
.isNull()) {
1037 Polynomial right
= cmp
.getRight();
1038 Node elim
= right
.getNode();
1039 // ax + p = c -> (ax + p) -ax - c = -ax
1040 // x = (p - ax - c) * -1/a
1041 // Add the substitution if not recursive
1042 Assert(elim
== Rewriter::rewrite(elim
));
1044 if (right
.size() > options::ppAssertMaxSubSize())
1047 << "TheoryArithPrivate::solve(): did not substitute due to the "
1048 "right hand side containing too many terms: "
1049 << minVar
<< ":" << elim
<< endl
;
1050 Debug("simplify") << right
.size() << endl
;
1052 else if (d_containing
.isLegalElimination(minVar
, elim
))
1054 // cannot eliminate integers here unless we know the resulting
1055 // substitution is integral
1056 Debug("simplify") << "TheoryArithPrivate::solve(): substitution "
1057 << minVar
<< " |-> " << elim
<< endl
;
1059 outSubstitutions
.addSubstitutionSolved(minVar
, elim
, tin
);
1060 return Theory::PP_ASSERT_STATUS_SOLVED
;
1064 Debug("simplify") << "TheoryArithPrivate::solve(): can't substitute "
1065 << minVar
<< ":" << minVar
.getType() << " |-> "
1066 << elim
<< ":" << elim
.getType() << endl
;
1071 // If a relation, remember the bound
1072 switch(in
.getKind()) {
1077 if (in
[0].isVar()) {
1078 d_learner
.addBound(in
);
1086 return Theory::PP_ASSERT_STATUS_UNSOLVED
;
1089 void TheoryArithPrivate::ppStaticLearn(TNode n
, NodeBuilder
& learned
)
1091 TimerStat::CodeTimer
codeTimer(d_statistics
.d_staticLearningTimer
);
1093 d_learner
.staticLearning(n
, learned
);
1096 ArithVar
TheoryArithPrivate::findShortestBasicRow(ArithVar variable
){
1097 ArithVar bestBasic
= ARITHVAR_SENTINEL
;
1098 uint64_t bestRowLength
= std::numeric_limits
<uint64_t>::max();
1100 Tableau::ColIterator basicIter
= d_tableau
.colIterator(variable
);
1101 for(; !basicIter
.atEnd(); ++basicIter
){
1102 const Tableau::Entry
& entry
= *basicIter
;
1103 Assert(entry
.getColVar() == variable
);
1104 RowIndex ridx
= entry
.getRowIndex();
1105 ArithVar basic
= d_tableau
.rowIndexToBasic(ridx
);
1106 uint32_t rowLength
= d_tableau
.getRowLength(ridx
);
1107 if((rowLength
< bestRowLength
) ||
1108 (rowLength
== bestRowLength
&& basic
< bestBasic
)){
1110 bestRowLength
= rowLength
;
1113 Assert(bestBasic
== ARITHVAR_SENTINEL
1114 || bestRowLength
< std::numeric_limits
<uint32_t>::max());
1118 void TheoryArithPrivate::setupVariable(const Variable
& x
){
1119 Node n
= x
.getNode();
1121 Assert(!isSetup(n
));
1123 ++(d_statistics
.d_statUserVariables
);
1124 requestArithVar(n
, false, false);
1125 //ArithVar varN = requestArithVar(n,false);
1126 //setupInitialValue(varN);
1131 void TheoryArithPrivate::setupVariableList(const VarList
& vl
){
1132 Assert(!vl
.empty());
1134 TNode vlNode
= vl
.getNode();
1135 Assert(!isSetup(vlNode
));
1136 Assert(!d_partialModel
.hasArithVar(vlNode
));
1138 for(VarList::iterator i
= vl
.begin(), end
= vl
.end(); i
!= end
; ++i
){
1141 if(!isSetup(var
.getNode())){
1146 if(!vl
.singleton()){
1147 // vl is the product of at least 2 variables
1148 // vl : (* v1 v2 ...)
1149 if(getLogicInfo().isLinear()){
1150 throw LogicException("A non-linear fact was asserted to arithmetic in a linear logic.");
1154 ++(d_statistics
.d_statUserVariables
);
1155 requestArithVar(vlNode
, false, false);
1156 //ArithVar av = requestArithVar(vlNode, false);
1157 //setupInitialValue(av);
1161 else if (vlNode
.getKind() == kind::EXPONENTIAL
1162 || vlNode
.getKind() == kind::SINE
|| vlNode
.getKind() == kind::COSINE
1163 || vlNode
.getKind() == kind::TANGENT
)
1169 * Only call markSetup if the VarList is not a singleton.
1170 * See the comment in setupPolynomail for more.
1174 void TheoryArithPrivate::cautiousSetupPolynomial(const Polynomial
& p
){
1175 if(p
.containsConstant()){
1176 if(!p
.isConstant()){
1177 Polynomial noConstant
= p
.getTail();
1178 if(!isSetup(noConstant
.getNode())){
1179 setupPolynomial(noConstant
);
1182 }else if(!isSetup(p
.getNode())){
1188 void TheoryArithPrivate::setupPolynomial(const Polynomial
& poly
) {
1189 Assert(!poly
.containsConstant());
1190 TNode polyNode
= poly
.getNode();
1191 Assert(!isSetup(polyNode
));
1192 Assert(!d_partialModel
.hasArithVar(polyNode
));
1194 for(Polynomial::iterator i
= poly
.begin(), end
= poly
.end(); i
!= end
; ++i
){
1196 const VarList
& vl
= mono
.getVarList();
1197 if(!isSetup(vl
.getNode())){
1198 setupVariableList(vl
);
1202 if(polyNode
.getKind() == PLUS
){
1203 d_tableauSizeHasBeenModified
= true;
1205 vector
<ArithVar
> variables
;
1206 vector
<Rational
> coefficients
;
1207 asVectors(poly
, coefficients
, variables
);
1209 ArithVar varSlack
= requestArithVar(polyNode
, true, false);
1210 d_tableau
.addRow(varSlack
, coefficients
, variables
);
1211 setupBasicValue(varSlack
);
1212 d_linEq
.trackRowIndex(d_tableau
.basicToRowIndex(varSlack
));
1214 //Add differences to the difference manager
1215 Polynomial::iterator i
= poly
.begin(), end
= poly
.end();
1217 Monomial first
= *i
;
1220 Monomial second
= *i
;
1223 if(first
.getConstant().isOne() && second
.getConstant().getValue() == -1){
1224 VarList vl0
= first
.getVarList();
1225 VarList vl1
= second
.getVarList();
1226 if(vl0
.singleton() && vl1
.singleton()){
1227 d_congruenceManager
.addWatchedPair(varSlack
, vl0
.getNode(), vl1
.getNode());
1234 ++(d_statistics
.d_statAuxiliaryVariables
);
1235 markSetup(polyNode
);
1239 * It is worth documenting that polyNode should only be marked as
1240 * being setup by this function if it has kind PLUS.
1241 * Other kinds will be marked as being setup by lower levels of setup
1242 * specifically setupVariableList.
1246 void TheoryArithPrivate::setupAtom(TNode atom
) {
1247 Assert(isRelationOperator(atom
.getKind())) << atom
;
1248 Assert(Comparison::isNormalAtom(atom
));
1249 Assert(!isSetup(atom
));
1250 Assert(!d_constraintDatabase
.hasLiteral(atom
));
1252 Comparison cmp
= Comparison::parseNormalForm(atom
);
1253 Polynomial nvp
= cmp
.normalizedVariablePart();
1254 Assert(!nvp
.isZero());
1256 if(!isSetup(nvp
.getNode())){
1257 setupPolynomial(nvp
);
1260 d_constraintDatabase
.addLiteral(atom
);
1265 void TheoryArithPrivate::preRegisterTerm(TNode n
) {
1266 Debug("arith::preregister") <<"begin arith::preRegisterTerm("<< n
<<")"<< endl
;
1268 d_preregisteredNodes
.insert(n
);
1271 if(isRelationOperator(n
.getKind())){
1275 ConstraintP c
= d_constraintDatabase
.lookup(n
);
1276 Assert(c
!= NullConstraint
);
1278 Debug("arith::preregister") << "setup constraint" << c
<< endl
;
1279 Assert(!c
->canBePropagated());
1280 c
->setPreregistered();
1282 } catch(LogicException
& le
) {
1283 std::stringstream ss
;
1284 ss
<< le
.getMessage() << endl
<< "The fact in question: " << n
<< endl
;
1285 throw LogicException(ss
.str());
1288 Debug("arith::preregister") << "end arith::preRegisterTerm("<< n
<<")" << endl
;
1291 void TheoryArithPrivate::releaseArithVar(ArithVar v
){
1292 //Assert(d_partialModel.hasNode(v));
1294 d_constraintDatabase
.removeVariable(v
);
1295 d_partialModel
.releaseArithVar(v
);
1298 ArithVar
TheoryArithPrivate::requestArithVar(TNode x
, bool aux
, bool internal
){
1299 //TODO : The VarList trick is good enough?
1300 Assert(isLeaf(x
) || VarList::isMember(x
) || x
.getKind() == PLUS
|| internal
);
1301 if(getLogicInfo().isLinear() && Variable::isDivMember(x
)){
1303 ss
<< "A non-linear fact (involving div/mod/divisibility) was asserted to "
1304 "arithmetic in a linear logic: "
1306 throw LogicException(ss
.str());
1308 Assert(!d_partialModel
.hasArithVar(x
));
1309 Assert(x
.getType().isReal()); // real or integer
1311 ArithVar max
= d_partialModel
.getNumberOfVariables();
1312 ArithVar varX
= d_partialModel
.allocate(x
, aux
);
1314 bool reclaim
= max
>= d_partialModel
.getNumberOfVariables();;
1317 d_dualSimplex
.increaseMax();
1319 d_tableau
.increaseSize();
1320 d_tableauSizeHasBeenModified
= true;
1322 d_constraintDatabase
.addVariable(varX
);
1324 Debug("arith::arithvar") << "@" << getSatContext()->getLevel()
1325 << " " << x
<< " |-> " << varX
1326 << "(relaiming " << reclaim
<< ")" << endl
;
1328 Assert(!d_partialModel
.hasUpperBound(varX
));
1329 Assert(!d_partialModel
.hasLowerBound(varX
));
1334 void TheoryArithPrivate::asVectors(const Polynomial
& p
, std::vector
<Rational
>& coeffs
, std::vector
<ArithVar
>& variables
) {
1335 for(Polynomial::iterator i
= p
.begin(), end
= p
.end(); i
!= end
; ++i
){
1336 const Monomial
& mono
= *i
;
1337 const Constant
& constant
= mono
.getConstant();
1338 const VarList
& variable
= mono
.getVarList();
1340 Node n
= variable
.getNode();
1342 Debug("arith::asVectors") << "should be var: " << n
<< endl
;
1344 // TODO: This VarList::isMember(n) can be stronger
1345 Assert(isLeaf(n
) || VarList::isMember(n
));
1346 Assert(theoryOf(n
) != THEORY_ARITH
|| d_partialModel
.hasArithVar(n
));
1348 Assert(d_partialModel
.hasArithVar(n
));
1349 ArithVar av
= d_partialModel
.asArithVar(n
);
1351 coeffs
.push_back(constant
.getValue());
1352 variables
.push_back(av
);
1357 * For basic variables the row must have been added to the tableau.
1359 void TheoryArithPrivate::setupBasicValue(ArithVar x
){
1360 Assert(d_tableau
.isBasic(x
));
1361 //If the variable is basic, assertions may have already happened and updates
1362 //may have occured before setting this variable up.
1364 //This can go away if the tableau creation is done at preregister
1365 //time instead of register
1366 DeltaRational safeAssignment
= d_linEq
.computeRowValue(x
, true);
1367 DeltaRational assignment
= d_linEq
.computeRowValue(x
, false);
1368 d_partialModel
.setAssignment(x
,safeAssignment
,assignment
);
1370 Debug("arith") << "setupVariable("<<x
<<")"<<std::endl
;
1373 ArithVar
TheoryArithPrivate::determineArithVar(const Polynomial
& p
) const{
1374 Assert(!p
.containsConstant());
1375 Assert(p
.getHead().constantIsPositive());
1376 TNode n
= p
.getNode();
1377 Debug("determineArithVar") << "determineArithVar(" << n
<< ")" << endl
;
1378 return d_partialModel
.asArithVar(n
);
1381 ArithVar
TheoryArithPrivate::determineArithVar(TNode assertion
) const{
1382 Debug("determineArithVar") << "determineArithVar " << assertion
<< endl
;
1383 Comparison cmp
= Comparison::parseNormalForm(assertion
);
1384 Polynomial variablePart
= cmp
.normalizedVariablePart();
1385 return determineArithVar(variablePart
);
1389 bool TheoryArithPrivate::canSafelyAvoidEqualitySetup(TNode equality
){
1390 Assert(equality
.getKind() == EQUAL
);
1391 return d_partialModel
.hasArithVar(equality
[0]);
1394 Comparison
TheoryArithPrivate::mkIntegerEqualityFromAssignment(ArithVar v
){
1395 const DeltaRational
& beta
= d_partialModel
.getAssignment(v
);
1397 Assert(beta
.isIntegral());
1398 Polynomial betaAsPolynomial
= Polynomial::mkPolynomial( Constant::mkConstant(beta
.floor()) );
1400 TNode var
= d_partialModel
.asNode(v
);
1401 Polynomial varAsPolynomial
= Polynomial::parsePolynomial(var
);
1402 return Comparison::mkComparison(EQUAL
, varAsPolynomial
, betaAsPolynomial
);
1405 TrustNode
TheoryArithPrivate::dioCutting()
1407 context::Context::ScopedPush
speculativePush(getSatContext());
1408 //DO NOT TOUCH THE OUTPUTSTREAM
1410 for(var_iterator vi
= var_begin(), vend
= var_end(); vi
!= vend
; ++vi
){
1413 if(d_partialModel
.cmpAssignmentUpperBound(v
) == 0 ||
1414 d_partialModel
.cmpAssignmentLowerBound(v
) == 0){
1415 if(!d_partialModel
.boundsAreEqual(v
)){
1416 // If the bounds are equal this is already in the dioSolver
1417 //Add v = dr as a speculation.
1418 Comparison eq
= mkIntegerEqualityFromAssignment(v
);
1419 Debug("dio::push") << "dio::push " << v
<< " " << eq
.getNode() << endl
;
1420 Assert(!eq
.isBoolean());
1421 d_diosolver
.pushInputConstraint(eq
, eq
.getNode());
1422 // It does not matter what the explanation of eq is.
1423 // It cannot be used in a conflict
1429 SumPair plane
= d_diosolver
.processEquationsForCut();
1431 return TrustNode::null();
1433 Polynomial p
= plane
.getPolynomial();
1434 Polynomial c
= Polynomial::mkPolynomial(plane
.getConstant() * Constant::mkConstant(-1));
1435 Integer gcd
= p
.gcd();
1436 Assert(p
.isIntegral());
1437 Assert(c
.isIntegral());
1439 Assert(!gcd
.divides(c
.asConstant().getNumerator()));
1440 Comparison leq
= Comparison::mkComparison(LEQ
, p
, c
);
1441 Comparison geq
= Comparison::mkComparison(GEQ
, p
, c
);
1442 Node lemma
= NodeManager::currentNM()->mkNode(OR
, leq
.getNode(), geq
.getNode());
1443 Node rewrittenLemma
= Rewriter::rewrite(lemma
);
1444 Debug("arith::dio::ex") << "dioCutting found the plane: " << plane
.getNode() << endl
;
1445 Debug("arith::dio::ex") << "resulting in the cut: " << lemma
<< endl
;
1446 Debug("arith::dio::ex") << "rewritten " << rewrittenLemma
<< endl
;
1447 Debug("arith::dio") << "dioCutting found the plane: " << plane
.getNode() << endl
;
1448 Debug("arith::dio") << "resulting in the cut: " << lemma
<< endl
;
1449 Debug("arith::dio") << "rewritten " << rewrittenLemma
<< endl
;
1450 if (proofsEnabled())
1452 NodeManager
* nm
= NodeManager::currentNM();
1453 Node gt
= nm
->mkNode(kind::GT
, p
.getNode(), c
.getNode());
1454 Node lt
= nm
->mkNode(kind::LT
, p
.getNode(), c
.getNode());
1456 Pf pfNotLeq
= d_pnm
->mkAssume(leq
.getNode().negate());
1458 d_pnm
->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM
, {pfNotLeq
}, {gt
});
1459 Pf pfNotGeq
= d_pnm
->mkAssume(geq
.getNode().negate());
1461 d_pnm
->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM
, {pfNotGeq
}, {lt
});
1463 d_pnm
->mkNode(PfRule::MACRO_ARITH_SCALE_SUM_UB
,
1465 {nm
->mkConst
<Rational
>(-1), nm
->mkConst
<Rational
>(1)});
1466 Pf pfBot
= d_pnm
->mkNode(
1467 PfRule::MACRO_SR_PRED_TRANSFORM
, {pfSum
}, {nm
->mkConst
<bool>(false)});
1468 std::vector
<Node
> assumptions
= {leq
.getNode().negate(),
1469 geq
.getNode().negate()};
1470 Pf pfNotAndNot
= d_pnm
->mkScope(pfBot
, assumptions
);
1471 Pf pfOr
= d_pnm
->mkNode(PfRule::NOT_AND
, {pfNotAndNot
}, {});
1472 Pf pfRewritten
= d_pnm
->mkNode(
1473 PfRule::MACRO_SR_PRED_TRANSFORM
, {pfOr
}, {rewrittenLemma
});
1474 return d_pfGen
->mkTrustNode(rewrittenLemma
, pfRewritten
);
1478 return TrustNode::mkTrustLemma(rewrittenLemma
, nullptr);
1483 Node
TheoryArithPrivate::callDioSolver(){
1484 while(!d_constantIntegerVariables
.empty()){
1485 ArithVar v
= d_constantIntegerVariables
.front();
1486 d_constantIntegerVariables
.pop();
1488 Debug("arith::dio") << "callDioSolver " << v
<< endl
;
1490 Assert(isInteger(v
));
1491 Assert(d_partialModel
.boundsAreEqual(v
));
1493 ConstraintP lb
= d_partialModel
.getLowerBoundConstraint(v
);
1494 ConstraintP ub
= d_partialModel
.getUpperBoundConstraint(v
);
1496 Node orig
= Node::null();
1497 if(lb
->isEquality()){
1498 orig
= Constraint::externalExplainByAssertions({lb
});
1499 }else if(ub
->isEquality()){
1500 orig
= Constraint::externalExplainByAssertions({ub
});
1502 orig
= Constraint::externalExplainByAssertions(ub
, lb
);
1505 Assert(d_partialModel
.assignmentIsConsistent(v
));
1507 Comparison eq
= mkIntegerEqualityFromAssignment(v
);
1510 //This can only be a conflict
1511 Assert(!eq
.getNode().getConst
<bool>());
1513 //This should be handled by the normal form earlier in the case of equality
1514 Assert(orig
.getKind() != EQUAL
);
1517 Debug("dio::push") << "dio::push " << v
<< " " << eq
.getNode() << " with reason " << orig
<< endl
;
1518 d_diosolver
.pushInputConstraint(eq
, orig
);
1522 return d_diosolver
.processEquationsForConflict();
1525 ConstraintP
TheoryArithPrivate::constraintFromFactQueue(TNode assertion
)
1527 Kind simpleKind
= Comparison::comparisonKind(assertion
);
1528 ConstraintP constraint
= d_constraintDatabase
.lookup(assertion
);
1529 if(constraint
== NullConstraint
){
1530 Assert(simpleKind
== EQUAL
|| simpleKind
== DISTINCT
);
1531 bool isDistinct
= simpleKind
== DISTINCT
;
1532 Node eq
= (simpleKind
== DISTINCT
) ? assertion
[0] : assertion
;
1533 Assert(!isSetup(eq
));
1534 Node reEq
= Rewriter::rewrite(eq
);
1535 Debug("arith::distinct::const") << "Assertion: " << assertion
<< std::endl
;
1536 Debug("arith::distinct::const") << "Eq : " << eq
<< std::endl
;
1537 Debug("arith::distinct::const") << "reEq : " << reEq
<< std::endl
;
1538 if(reEq
.getKind() == CONST_BOOLEAN
){
1539 if(reEq
.getConst
<bool>() == isDistinct
){
1540 // if is (not true), or false
1541 Assert((reEq
.getConst
<bool>() && isDistinct
)
1542 || (!reEq
.getConst
<bool>() && !isDistinct
));
1543 if (proofsEnabled())
1545 Pf assume
= d_pnm
->mkAssume(assertion
);
1546 std::vector
<Node
> assumptions
= {assertion
};
1547 Pf pf
= d_pnm
->mkScope(d_pnm
->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM
,
1548 {d_pnm
->mkAssume(assertion
)},
1551 raiseBlackBoxConflict(assertion
, pf
);
1555 raiseBlackBoxConflict(assertion
);
1558 return NullConstraint
;
1560 Assert(reEq
.getKind() != CONST_BOOLEAN
);
1564 Node reAssertion
= isDistinct
? reEq
.notNode() : reEq
;
1565 constraint
= d_constraintDatabase
.lookup(reAssertion
);
1567 if(assertion
!= reAssertion
){
1568 Debug("arith::nf") << "getting non-nf assertion " << assertion
<< " |-> " << reAssertion
<< endl
;
1569 Assert(constraint
!= NullConstraint
);
1570 d_assertionsThatDoNotMatchTheirLiterals
.insert(assertion
, constraint
);
1574 Assert(constraint
!= NullConstraint
);
1576 if(constraint
->assertedToTheTheory()){
1578 return NullConstraint
;
1580 Assert(!constraint
->assertedToTheTheory());
1581 bool inConflict
= constraint
->negationHasProof();
1582 constraint
->setAssertedToTheTheory(assertion
, inConflict
);
1584 if(!constraint
->hasProof()){
1585 Debug("arith::constraint") << "marking as constraint as self explaining " << endl
;
1586 constraint
->setAssumption(inConflict
);
1588 Debug("arith::constraint")
1589 << "already has proof: "
1590 << Constraint::externalExplainByAssertions({constraint
});
1593 if(Debug
.isOn("arith::negatedassumption") && inConflict
){
1594 ConstraintP negation
= constraint
->getNegation();
1595 if(Debug
.isOn("arith::negatedassumption") && negation
->isAssumption()){
1598 Debug("arith::eq") << "negation has proof" << endl
;
1599 Debug("arith::eq") << constraint
<< endl
;
1600 Debug("arith::eq") << negation
<< endl
;
1604 ConstraintP negation
= constraint
->getNegation();
1605 if(Debug
.isOn("arith::negatedassumption") && negation
->isAssumption()){
1608 Debug("arith::eq") << "negation has proof" << endl
;
1609 Debug("arith::eq") << constraint
<< endl
;
1610 Debug("arith::eq") << negation
<< endl
;
1611 raiseConflict(negation
, InferenceId::UNKNOWN
);
1612 return NullConstraint
;
1618 bool TheoryArithPrivate::assertionCases(ConstraintP constraint
){
1619 Assert(constraint
->hasProof());
1620 Assert(!constraint
->negationHasProof());
1622 ArithVar x_i
= constraint
->getVariable();
1624 switch(constraint
->getType()){
1626 if(isInteger(x_i
) && constraint
->isStrictUpperBound()){
1627 ConstraintP floorConstraint
= constraint
->getFloor();
1628 if(!floorConstraint
->isTrue()){
1629 bool inConflict
= floorConstraint
->negationHasProof();
1630 if (Debug
.isOn("arith::intbound")) {
1631 Debug("arith::intbound") << "literal, before: " << constraint
->getLiteral() << std::endl
;
1632 Debug("arith::intbound") << "constraint, after: " << floorConstraint
<< std::endl
;
1634 floorConstraint
->impliedByIntTighten(constraint
, inConflict
);
1635 floorConstraint
->tryToPropagate();
1637 raiseConflict(floorConstraint
, InferenceId::ARITH_TIGHTEN_FLOOR
);
1641 return AssertUpper(floorConstraint
);
1643 return AssertUpper(constraint
);
1646 if(isInteger(x_i
) && constraint
->isStrictLowerBound()){
1647 ConstraintP ceilingConstraint
= constraint
->getCeiling();
1648 if(!ceilingConstraint
->isTrue()){
1649 bool inConflict
= ceilingConstraint
->negationHasProof();
1650 if (Debug
.isOn("arith::intbound")) {
1651 Debug("arith::intbound") << "literal, before: " << constraint
->getLiteral() << std::endl
;
1652 Debug("arith::intbound") << "constraint, after: " << ceilingConstraint
<< std::endl
;
1654 ceilingConstraint
->impliedByIntTighten(constraint
, inConflict
);
1655 ceilingConstraint
->tryToPropagate();
1657 raiseConflict(ceilingConstraint
, InferenceId::ARITH_TIGHTEN_CEIL
);
1661 return AssertLower(ceilingConstraint
);
1663 return AssertLower(constraint
);
1666 return AssertEquality(constraint
);
1668 return AssertDisequality(constraint
);
1675 * Looks for through the variables starting at d_nextIntegerCheckVar
1676 * for the first integer variable that is between its upper and lower bounds
1677 * that has a non-integer assignment.
1679 * If assumeBounds is true, skip the check that the variable is in bounds.
1681 * If there is no such variable, returns ARITHVAR_SENTINEL;
1683 ArithVar
TheoryArithPrivate::nextIntegerViolation(bool assumeBounds
) const
1685 ArithVar numVars
= d_partialModel
.getNumberOfVariables();
1686 ArithVar v
= d_nextIntegerCheckVar
;
1689 const ArithVar rrEnd
= d_nextIntegerCheckVar
;
1692 if (isIntegerInput(v
))
1694 if (!d_partialModel
.integralAssignment(v
))
1696 if (assumeBounds
|| d_partialModel
.assignmentIsConsistent(v
))
1702 v
= (1 + v
== numVars
) ? 0 : (1 + v
);
1703 } while (v
!= rrEnd
);
1705 return ARITHVAR_SENTINEL
;
1709 * Checks the set of integer variables I to see if each variable
1710 * in I has an integer assignment.
1712 bool TheoryArithPrivate::hasIntegerModel()
1714 ArithVar next
= nextIntegerViolation(true);
1715 if (next
!= ARITHVAR_SENTINEL
)
1717 d_nextIntegerCheckVar
= next
;
1718 if (Debug
.isOn("arith::hasIntegerModel"))
1720 Debug("arith::hasIntegerModel") << "has int model? " << next
<< endl
;
1721 d_partialModel
.printModel(next
, Debug("arith::hasIntegerModel"));
1731 Node
flattenAndSort(Node n
){
1732 Kind k
= n
.getKind();
1743 std::vector
<Node
> out
;
1744 std::vector
<Node
> process
;
1745 process
.push_back(n
);
1746 while(!process
.empty()){
1747 Node b
= process
.back();
1749 if(b
.getKind() == k
){
1750 for(Node::iterator i
=b
.begin(), end
=b
.end(); i
!=end
; ++i
){
1751 process
.push_back(*i
);
1757 Assert(out
.size() >= 2);
1758 std::sort(out
.begin(), out
.end());
1759 return NodeManager::currentNM()->mkNode(k
, out
);
1764 /** Outputs conflicts to the output channel. */
1765 void TheoryArithPrivate::outputConflicts(){
1766 Debug("arith::conflict") << "outputting conflicts" << std::endl
;
1767 Assert(anyConflict());
1768 static unsigned int conflicts
= 0;
1770 if(!conflictQueueEmpty()){
1771 Assert(!d_conflicts
.empty());
1772 for(size_t i
= 0, i_end
= d_conflicts
.size(); i
< i_end
; ++i
){
1773 const std::pair
<ConstraintCP
, InferenceId
>& conf
= d_conflicts
[i
];
1774 const ConstraintCP
& confConstraint
= conf
.first
;
1775 bool hasProof
= confConstraint
->hasProof();
1776 Assert(confConstraint
->inConflict());
1777 const ConstraintRule
& pf
= confConstraint
->getConstraintRule();
1778 if (Debug
.isOn("arith::conflict"))
1780 pf
.print(std::cout
);
1781 std::cout
<< std::endl
;
1783 if (Debug
.isOn("arith::pf::tree"))
1785 Debug("arith::pf::tree") << "\n\nTree:\n";
1786 confConstraint
->printProofTree(Debug("arith::pf::tree"));
1787 confConstraint
->getNegation()->printProofTree(Debug("arith::pf::tree"));
1790 TrustNode trustedConflict
= confConstraint
->externalExplainConflict();
1791 Node conflict
= trustedConflict
.getNode();
1794 Debug("arith::conflict") << "d_conflicts[" << i
<< "] " << conflict
1795 << " has proof: " << hasProof
<< endl
;
1796 if(Debug
.isOn("arith::normalize::external")){
1797 conflict
= flattenAndSort(conflict
);
1798 Debug("arith::conflict") << "(normalized to) " << conflict
<< endl
;
1801 if (isProofEnabled())
1803 outputTrustedConflict(trustedConflict
, conf
.second
);
1807 outputConflict(conflict
, conf
.second
);
1811 if(!d_blackBoxConflict
.get().isNull()){
1812 Node bb
= d_blackBoxConflict
.get();
1814 Debug("arith::conflict") << "black box conflict" << bb
1815 //<< "("<<conflicts<<")"
1817 if(Debug
.isOn("arith::normalize::external")){
1818 bb
= flattenAndSort(bb
);
1819 Debug("arith::conflict") << "(normalized to) " << bb
<< endl
;
1821 if (isProofEnabled() && d_blackBoxConflictPf
.get())
1823 auto confPf
= d_blackBoxConflictPf
.get();
1824 outputTrustedConflict(d_pfGen
->mkTrustNode(bb
, confPf
, true), InferenceId::ARITH_BLACK_BOX
);
1828 outputConflict(bb
, InferenceId::ARITH_BLACK_BOX
);
1833 void TheoryArithPrivate::outputTrustedLemma(TrustNode lemma
, InferenceId id
)
1835 Debug("arith::channel") << "Arith trusted lemma: " << lemma
<< std::endl
;
1836 d_containing
.d_im
.trustedLemma(lemma
, id
);
1839 void TheoryArithPrivate::outputLemma(TNode lem
, InferenceId id
) {
1840 Debug("arith::channel") << "Arith lemma: " << lem
<< std::endl
;
1841 d_containing
.d_im
.lemma(lem
, id
);
1844 void TheoryArithPrivate::outputTrustedConflict(TrustNode conf
, InferenceId id
)
1846 Debug("arith::channel") << "Arith trusted conflict: " << conf
<< std::endl
;
1847 d_containing
.d_im
.trustedConflict(conf
, id
);
1850 void TheoryArithPrivate::outputConflict(TNode lit
, InferenceId id
) {
1851 Debug("arith::channel") << "Arith conflict: " << lit
<< std::endl
;
1852 d_containing
.d_im
.conflict(lit
, id
);
1855 void TheoryArithPrivate::outputPropagate(TNode lit
) {
1856 Debug("arith::channel") << "Arith propagation: " << lit
<< std::endl
;
1857 // call the propagate lit method of the
1858 d_containing
.d_im
.propagateLit(lit
);
1861 void TheoryArithPrivate::outputRestart() {
1862 Debug("arith::channel") << "Arith restart!" << std::endl
;
1863 (d_containing
.d_out
)->demandRestart();
1866 bool TheoryArithPrivate::attemptSolveInteger(Theory::Effort effortLevel
, bool emmmittedLemmaOrSplit
){
1867 int level
= getSatContext()->getLevel();
1869 << "attemptSolveInteger " << d_qflraStatus
1870 << " " << emmmittedLemmaOrSplit
1871 << " " << effortLevel
1872 << " " << d_lastContextIntegerAttempted
1874 << " " << hasIntegerModel()
1877 if(d_qflraStatus
== Result::UNSAT
){ return false; }
1878 if(emmmittedLemmaOrSplit
){ return false; }
1879 if(!options::useApprox()){ return false; }
1880 if(!ApproximateSimplex::enabled()){ return false; }
1882 if(Theory::fullEffort(effortLevel
)){
1883 if(hasIntegerModel()){
1886 return getSolveIntegerResource();
1890 if(d_lastContextIntegerAttempted
<= 0){
1891 if(hasIntegerModel()){
1892 d_lastContextIntegerAttempted
= getSatContext()->getLevel();
1895 return getSolveIntegerResource();
1900 if(!options::trySolveIntStandardEffort()){ return false; }
1902 if (d_lastContextIntegerAttempted
<= (level
>> 2))
1904 double d
= (double)(d_solveIntMaybeHelp
+ 1)
1905 / (d_solveIntAttempts
+ 1 + level
* level
);
1906 if (Random::getRandom().pickWithProb(d
))
1908 return getSolveIntegerResource();
1914 bool TheoryArithPrivate::replayLog(ApproximateSimplex
* approx
){
1915 TimerStat::CodeTimer
codeTimer(d_statistics
.d_replayLogTimer
);
1917 ++d_statistics
.d_mipProofsAttempted
;
1919 Assert(d_replayVariables
.empty());
1920 Assert(d_replayConstraints
.empty());
1922 size_t enteringPropN
= d_currentPropagationList
.size();
1923 Assert(conflictQueueEmpty());
1924 TreeLog
& tl
= getTreeLog();
1925 //tl.applySelected(); /* set row ids */
1927 d_replayedLemmas
= false;
1929 /* use the try block for the purpose of pushing the sat context */
1930 context::Context::ScopedPush
speculativePush(getSatContext());
1931 d_cmEnabled
= false;
1932 std::vector
<ConstraintCPVec
> res
=
1933 replayLogRec(approx
, tl
.getRootId(), NullConstraint
, 1);
1936 ++d_statistics
.d_replayAttemptFailed
;
1938 unsigned successes
= 0;
1939 for(size_t i
=0, N
= res
.size(); i
< N
; ++i
){
1940 ConstraintCPVec
& vec
= res
[i
];
1941 Assert(vec
.size() >= 2);
1942 for(size_t j
=0, M
= vec
.size(); j
< M
; ++j
){
1943 ConstraintCP at_j
= vec
[j
];
1944 Assert(at_j
->isTrue());
1945 if(!at_j
->negationHasProof()){
1947 vec
[j
] = vec
.back();
1949 ConstraintP neg_at_j
= at_j
->getNegation();
1951 Debug("approx::replayLog") << "Setting the proof for the replayLog conflict on:" << endl
1952 << " (" << neg_at_j
->isTrue() <<") " << neg_at_j
<< endl
1953 << " (" << at_j
->isTrue() <<") " << at_j
<< endl
;
1954 neg_at_j
->impliedByIntHole(vec
, true);
1955 raiseConflict(at_j
, InferenceId::UNKNOWN
);
1961 ++d_statistics
.d_mipProofsSuccessful
;
1965 if(d_currentPropagationList
.size() > enteringPropN
){
1966 d_currentPropagationList
.resize(enteringPropN
);
1969 /* It is not clear what the d_qflraStatus is at this point */
1970 d_qflraStatus
= Result::SAT_UNKNOWN
;
1972 Assert(d_replayVariables
.empty());
1973 Assert(d_replayConstraints
.empty());
1975 return !conflictQueueEmpty();
1978 std::pair
<ConstraintP
, ArithVar
> TheoryArithPrivate::replayGetConstraint(const DenseMap
<Rational
>& lhs
, Kind k
, const Rational
& rhs
, bool branch
)
1980 ArithVar added
= ARITHVAR_SENTINEL
;
1981 Node sum
= toSumNode(d_partialModel
, lhs
);
1982 if(sum
.isNull()){ return make_pair(NullConstraint
, added
); }
1984 Debug("approx::constraint") << "replayGetConstraint " << sum
1989 Assert(k
== kind::LEQ
|| k
== kind::GEQ
);
1991 Node comparison
= NodeManager::currentNM()->mkNode(k
, sum
, mkRationalNode(rhs
));
1992 Node rewritten
= Rewriter::rewrite(comparison
);
1993 if(!(Comparison::isNormalAtom(rewritten
))){
1994 return make_pair(NullConstraint
, added
);
1997 Comparison cmp
= Comparison::parseNormalForm(rewritten
);
1998 if(cmp
.isBoolean()){ return make_pair(NullConstraint
, added
); }
2000 Polynomial nvp
= cmp
.normalizedVariablePart();
2001 if(nvp
.isZero()){ return make_pair(NullConstraint
, added
); }
2003 Node norm
= nvp
.getNode();
2005 ConstraintType t
= Constraint::constraintTypeOfComparison(cmp
);
2006 DeltaRational dr
= cmp
.normalizedDeltaRational();
2008 Debug("approx::constraint") << "rewriting " << rewritten
<< endl
2009 << " |-> " << norm
<< " " << t
<< " " << dr
<< endl
;
2011 Assert(!branch
|| d_partialModel
.hasArithVar(norm
));
2012 ArithVar v
= ARITHVAR_SENTINEL
;
2013 if(d_partialModel
.hasArithVar(norm
)){
2015 v
= d_partialModel
.asArithVar(norm
);
2016 Debug("approx::constraint") << "replayGetConstraint found "
2017 << norm
<< " |-> " << v
<< " @ " << getSatContext()->getLevel() << endl
;
2018 Assert(!branch
|| d_partialModel
.isIntegerInput(v
));
2020 v
= requestArithVar(norm
, true, true);
2021 d_replayVariables
.push_back(v
);
2025 Debug("approx::constraint") << "replayGetConstraint adding "
2026 << norm
<< " |-> " << v
<< " @ " << getSatContext()->getLevel() << endl
;
2028 Polynomial poly
= Polynomial::parsePolynomial(norm
);
2029 vector
<ArithVar
> variables
;
2030 vector
<Rational
> coefficients
;
2031 asVectors(poly
, coefficients
, variables
);
2032 d_tableau
.addRow(v
, coefficients
, variables
);
2034 d_linEq
.trackRowIndex(d_tableau
.basicToRowIndex(v
));
2036 Assert(d_partialModel
.hasArithVar(norm
));
2037 Assert(d_partialModel
.asArithVar(norm
) == v
);
2038 Assert(d_constraintDatabase
.variableDatabaseIsSetup(v
));
2040 ConstraintP imp
= d_constraintDatabase
.getBestImpliedBound(v
, t
, dr
);
2041 if(imp
!= NullConstraint
){
2042 if(imp
->getValue() == dr
){
2043 Assert(added
== ARITHVAR_SENTINEL
);
2044 return make_pair(imp
, added
);
2048 ConstraintP newc
= d_constraintDatabase
.getConstraint(v
, t
, dr
);
2049 d_replayConstraints
.push_back(newc
);
2050 return make_pair(newc
, added
);
2053 std::pair
<ConstraintP
, ArithVar
> TheoryArithPrivate::replayGetConstraint(
2054 ApproximateSimplex
* approx
, const NodeLog
& nl
)
2056 Assert(nl
.isBranch());
2057 Assert(d_lhsTmp
.empty());
2059 ArithVar v
= approx
->getBranchVar(nl
);
2060 if(v
!= ARITHVAR_SENTINEL
&& d_partialModel
.isIntegerInput(v
)){
2061 if(d_partialModel
.hasNode(v
)){
2062 d_lhsTmp
.set(v
, Rational(1));
2063 double dval
= nl
.branchValue();
2064 Maybe
<Rational
> maybe_value
= ApproximateSimplex::estimateWithCFE(dval
);
2067 return make_pair(NullConstraint
, ARITHVAR_SENTINEL
);
2069 Rational
fl(maybe_value
.value().floor());
2070 pair
<ConstraintP
, ArithVar
> p
;
2071 p
= replayGetConstraint(d_lhsTmp
, kind::LEQ
, fl
, true);
2076 return make_pair(NullConstraint
, ARITHVAR_SENTINEL
);
2079 std::pair
<ConstraintP
, ArithVar
> TheoryArithPrivate::replayGetConstraint(const CutInfo
& ci
) {
2080 Assert(ci
.reconstructed());
2081 const DenseMap
<Rational
>& lhs
= ci
.getReconstruction().lhs
;
2082 const Rational
& rhs
= ci
.getReconstruction().rhs
;
2083 Kind k
= ci
.getKind();
2085 return replayGetConstraint(lhs
, k
, rhs
, ci
.getKlass() == BranchCutKlass
);
2088 // Node denseVectorToLiteral(const ArithVariables& vars, const DenseVector& dv, Kind k){
2089 // NodeManager* nm = NodeManager::currentNM();
2090 // Node sumLhs = toSumNode(vars, dv.lhs);
2091 // Node ineq = nm->mkNode(k, sumLhs, mkRationalNode(dv.rhs) );
2092 // Node lit = Rewriter::rewrite(ineq);
2096 Node
toSumNode(const ArithVariables
& vars
, const DenseMap
<Rational
>& sum
){
2097 Debug("arith::toSumNode") << "toSumNode() begin" << endl
;
2098 NodeBuilder
nb(kind::PLUS
);
2099 NodeManager
* nm
= NodeManager::currentNM();
2100 DenseMap
<Rational
>::const_iterator iter
, end
;
2101 iter
= sum
.begin(), end
= sum
.end();
2102 for(; iter
!= end
; ++iter
){
2104 if(!vars
.hasNode(x
)){ return Node::null(); }
2105 Node xNode
= vars
.asNode(x
);
2106 const Rational
& q
= sum
[x
];
2107 Node mult
= nm
->mkNode(kind::MULT
, mkRationalNode(q
), xNode
);
2108 Debug("arith::toSumNode") << "toSumNode() " << x
<< " " << mult
<< endl
;
2111 Debug("arith::toSumNode") << "toSumNode() end" << endl
;
2112 return safeConstructNary(nb
);
2115 ConstraintCP
TheoryArithPrivate::vectorToIntHoleConflict(const ConstraintCPVec
& conflict
){
2116 Assert(conflict
.size() >= 2);
2117 ConstraintCPVec
exp(conflict
.begin(), conflict
.end()-1);
2118 ConstraintCP back
= conflict
.back();
2119 Assert(back
->hasProof());
2120 ConstraintP negBack
= back
->getNegation();
2121 // This can select negBack multiple times so we need to test if negBack has a proof.
2122 if(negBack
->hasProof()){
2123 // back is in conflict already
2125 negBack
->impliedByIntHole(exp
, true);
2131 void TheoryArithPrivate::intHoleConflictToVector(ConstraintCP conflicting
, ConstraintCPVec
& conflict
){
2132 ConstraintCP negConflicting
= conflicting
->getNegation();
2133 Assert(conflicting
->hasProof());
2134 Assert(negConflicting
->hasProof());
2136 conflict
.push_back(conflicting
);
2137 conflict
.push_back(negConflicting
);
2139 Constraint::assertionFringe(conflict
);
2142 void TheoryArithPrivate::tryBranchCut(ApproximateSimplex
* approx
, int nid
, BranchCutInfo
& bci
){
2143 Assert(conflictQueueEmpty());
2144 std::vector
< ConstraintCPVec
> conflicts
;
2146 approx
->tryCut(nid
, bci
);
2147 Debug("approx::branch") << "tryBranchCut" << bci
<< endl
;
2148 Assert(bci
.reconstructed());
2149 Assert(!bci
.proven());
2150 pair
<ConstraintP
, ArithVar
> p
= replayGetConstraint(bci
);
2151 Assert(p
.second
== ARITHVAR_SENTINEL
);
2152 ConstraintP bc
= p
.first
;
2153 Assert(bc
!= NullConstraint
);
2158 ConstraintP bcneg
= bc
->getNegation();
2160 context::Context::ScopedPush
speculativePush(getSatContext());
2161 replayAssert(bcneg
);
2162 if(conflictQueueEmpty()){
2163 TimerStat::CodeTimer
codeTimer(d_statistics
.d_replaySimplexTimer
);
2165 //test for linear feasibility
2166 d_partialModel
.stopQueueingBoundCounts();
2167 UpdateTrackingCallback
utcb(&d_linEq
);
2168 d_partialModel
.processBoundsQueue(utcb
);
2169 d_linEq
.startTrackingBoundCounts();
2171 SimplexDecisionProcedure
& simplex
= selectSimplex(true);
2172 simplex
.findModel(false);
2173 // Can change d_qflraStatus
2175 d_linEq
.stopTrackingBoundCounts();
2176 d_partialModel
.startQueueingBoundCounts();
2178 for(size_t i
= 0, N
= d_conflicts
.size(); i
< N
; ++i
){
2180 conflicts
.push_back(ConstraintCPVec());
2181 intHoleConflictToVector(d_conflicts
[i
].first
, conflicts
.back());
2182 Constraint::assertionFringe(conflicts
.back());
2184 // ConstraintCP conflicting = d_conflicts[i];
2185 // ConstraintCP negConflicting = conflicting->getNegation();
2186 // Assert(conflicting->hasProof());
2187 // Assert(negConflicting->hasProof());
2189 // conflicts.push_back(ConstraintCPVec());
2190 // ConstraintCPVec& back = conflicts.back();
2191 // back.push_back(conflicting);
2192 // back.push_back(negConflicting);
2194 // // remove the floor/ceiling contraint implied by bcneg
2195 // Constraint::assertionFringe(back);
2198 if(Debug
.isOn("approx::branch")){
2199 if(d_conflicts
.empty()){
2200 entireStateIsConsistent("branchfailure");
2205 Debug("approx::branch") << "branch constraint " << bc
<< endl
;
2206 for(size_t i
= 0, N
= conflicts
.size(); i
< N
; ++i
){
2207 ConstraintCPVec
& conf
= conflicts
[i
];
2209 // make sure to be working on the assertion fringe!
2210 if(!contains(conf
, bcneg
)){
2211 Debug("approx::branch") << "reraise " << conf
<< endl
;
2212 ConstraintCP conflicting
= vectorToIntHoleConflict(conf
);
2213 raiseConflict(conflicting
, InferenceId::UNKNOWN
);
2214 }else if(!bci
.proven()){
2216 bci
.setExplanation(conf
);
2217 Debug("approx::branch") << "dropped " << bci
<< endl
;
2222 void TheoryArithPrivate::replayAssert(ConstraintP c
) {
2223 if(!c
->assertedToTheTheory()){
2224 bool inConflict
= c
->negationHasProof();
2226 c
->setInternalAssumption(inConflict
);
2227 Debug("approx::replayAssert") << "replayAssert " << c
<< " set internal" << endl
;
2229 Debug("approx::replayAssert") << "replayAssert " << c
<< " has explanation" << endl
;
2231 Debug("approx::replayAssert") << "replayAssertion " << c
<< endl
;
2233 raiseConflict(c
, InferenceId::UNKNOWN
);
2238 Debug("approx::replayAssert")
2239 << "replayAssert " << c
<< " already asserted" << endl
;
2244 void TheoryArithPrivate::resolveOutPropagated(std::vector
<ConstraintCPVec
>& confs
, const std::set
<ConstraintCP
>& propagated
) const {
2245 Debug("arith::resolveOutPropagated")
2246 << "starting resolveOutPropagated() " << confs
.size() << endl
;
2247 for(size_t i
=0, N
= confs
.size(); i
< N
; ++i
){
2248 ConstraintCPVec
& conf
= confs
[i
];
2249 size_t orig
= conf
.size();
2250 Constraint::assertionFringe(conf
);
2251 Debug("arith::resolveOutPropagated")
2252 << " conf["<<i
<<"] " << orig
<< " to " << conf
.size() << endl
;
2254 Debug("arith::resolveOutPropagated")
2255 << "ending resolveOutPropagated() " << confs
.size() << endl
;
2259 bool operator()(const ConstraintCPVec
& a
, const ConstraintCPVec
& b
) const{
2260 return a
.size() < b
.size();
2264 void TheoryArithPrivate::subsumption(
2265 std::vector
<ConstraintCPVec
> &confs
) const {
2266 int checks CVC5_UNUSED
= 0;
2267 int subsumed CVC5_UNUSED
= 0;
2269 for (size_t i
= 0, N
= confs
.size(); i
< N
; ++i
) {
2270 ConstraintCPVec
&conf
= confs
[i
];
2271 std::sort(conf
.begin(), conf
.end());
2274 std::sort(confs
.begin(), confs
.end(), SizeOrd());
2275 for (size_t i
= 0; i
< confs
.size(); i
++) {
2276 // i is not subsumed
2277 for (size_t j
= i
+ 1; j
< confs
.size();) {
2278 ConstraintCPVec
& a
= confs
[i
];
2279 ConstraintCPVec
& b
= confs
[j
];
2281 bool subsumes
= std::includes(a
.begin(), a
.end(), b
.begin(), b
.end());
2283 ConstraintCPVec
& back
= confs
.back();
2292 Debug("arith::subsumption") << "subsumed " << subsumed
<< "/" << checks
2296 std::vector
<ConstraintCPVec
> TheoryArithPrivate::replayLogRec(ApproximateSimplex
* approx
, int nid
, ConstraintP bc
, int depth
){
2297 ++(d_statistics
.d_replayLogRecCount
);
2298 Debug("approx::replayLogRec") << "replayLogRec()" << std::endl
;
2300 size_t rpvars_size
= d_replayVariables
.size();
2301 size_t rpcons_size
= d_replayConstraints
.size();
2302 std::vector
<ConstraintCPVec
> res
;
2304 { /* create a block for the purpose of pushing the sat context */
2305 context::Context::ScopedPush
speculativePush(getSatContext());
2306 Assert(!anyConflict());
2307 Assert(conflictQueueEmpty());
2308 set
<ConstraintCP
> propagated
;
2310 TreeLog
& tl
= getTreeLog();
2312 if(bc
!= NullConstraint
){
2316 const NodeLog
& nl
= tl
.getNode(nid
);
2317 NodeLog::const_iterator iter
= nl
.begin(), end
= nl
.end();
2318 for(; conflictQueueEmpty() && iter
!= end
; ++iter
){
2319 CutInfo
* ci
= *iter
;
2320 bool reject
= false;
2321 //cout << " trying " << *ci << endl;
2322 if(ci
->getKlass() == RowsDeletedKlass
){
2323 RowsDeleted
* rd
= dynamic_cast<RowsDeleted
*>(ci
);
2325 tl
.applyRowsDeleted(nid
, *rd
);
2326 // The previous line modifies nl
2328 ++d_statistics
.d_applyRowsDeleted
;
2329 }else if(ci
->getKlass() == BranchCutKlass
){
2330 BranchCutInfo
* bci
= dynamic_cast<BranchCutInfo
*>(ci
);
2331 Assert(bci
!= NULL
);
2332 tryBranchCut(approx
, nid
, *bci
);
2334 ++d_statistics
.d_branchCutsAttempted
;
2335 if(!(conflictQueueEmpty() || ci
->reconstructed())){
2336 ++d_statistics
.d_numBranchesFailed
;
2339 approx
->tryCut(nid
, *ci
);
2340 if(ci
->getKlass() == GmiCutKlass
){
2341 ++d_statistics
.d_gmiCutsAttempted
;
2342 }else if(ci
->getKlass() == MirCutKlass
){
2343 ++d_statistics
.d_mirCutsAttempted
;
2346 if(ci
->reconstructed() && ci
->proven()){
2347 const DenseMap
<Rational
>& row
= ci
->getReconstruction().lhs
;
2348 reject
= !complexityBelow(row
, options::replayRejectCutSize());
2351 if(conflictQueueEmpty()){
2353 ++d_statistics
.d_cutsRejectedDuringReplay
;
2354 }else if(ci
->reconstructed()){
2356 ++d_statistics
.d_cutsReconstructed
;
2358 pair
<ConstraintP
, ArithVar
> p
= replayGetConstraint(*ci
);
2359 if(p
.second
!= ARITHVAR_SENTINEL
){
2360 Assert(ci
->getRowId() >= 1);
2361 tl
.mapRowId(nl
.getNodeId(), ci
->getRowId(), p
.second
);
2363 ConstraintP con
= p
.first
;
2364 if(Debug
.isOn("approx::replayLogRec")){
2365 Debug("approx::replayLogRec") << "cut was remade " << con
<< " " << *ci
<< endl
;
2369 ++d_statistics
.d_cutsProven
;
2371 const ConstraintCPVec
& exp
= ci
->getExplanation();
2374 Debug("approx::replayLogRec") << "not asserted?" << endl
;
2375 }else if(!con
->negationHasProof()){
2376 con
->impliedByIntHole(exp
, false);
2378 Debug("approx::replayLogRec") << "cut prop" << endl
;
2380 con
->impliedByIntHole(exp
, true);
2381 Debug("approx::replayLogRec") << "cut into conflict " << con
<< endl
;
2382 raiseConflict(con
, InferenceId::UNKNOWN
);
2385 ++d_statistics
.d_cutsProofFailed
;
2386 Debug("approx::replayLogRec") << "failed to get proof " << *ci
<< endl
;
2388 }else if(ci
->getKlass() != RowsDeletedKlass
){
2389 ++d_statistics
.d_cutsReconstructionFailed
;
2394 /* check if the system is feasible under with the cuts */
2395 if(conflictQueueEmpty()){
2396 Assert(options::replayEarlyCloseDepths() >= 1);
2397 if(!nl
.isBranch() || depth
% options::replayEarlyCloseDepths() == 0 ){
2398 TimerStat::CodeTimer
codeTimer(d_statistics
.d_replaySimplexTimer
);
2399 //test for linear feasibility
2400 d_partialModel
.stopQueueingBoundCounts();
2401 UpdateTrackingCallback
utcb(&d_linEq
);
2402 d_partialModel
.processBoundsQueue(utcb
);
2403 d_linEq
.startTrackingBoundCounts();
2405 SimplexDecisionProcedure
& simplex
= selectSimplex(true);
2406 simplex
.findModel(false);
2407 // can change d_qflraStatus
2409 d_linEq
.stopTrackingBoundCounts();
2410 d_partialModel
.startQueueingBoundCounts();
2413 ++d_statistics
.d_replayLogRecConflictEscalation
;
2416 if(!conflictQueueEmpty()){
2417 /* if a conflict has been found stop */
2418 for(size_t i
= 0, N
= d_conflicts
.size(); i
< N
; ++i
){
2419 res
.push_back(ConstraintCPVec());
2420 intHoleConflictToVector(d_conflicts
[i
].first
, res
.back());
2422 ++d_statistics
.d_replayLogRecEarlyExit
;
2423 }else if(nl
.isBranch()){
2424 /* if it is a branch try the branch */
2425 pair
<ConstraintP
, ArithVar
> p
= replayGetConstraint(approx
, nl
);
2426 Assert(p
.second
== ARITHVAR_SENTINEL
);
2427 ConstraintP dnc
= p
.first
;
2428 if(dnc
!= NullConstraint
){
2429 ConstraintP upc
= dnc
->getNegation();
2431 int dnid
= nl
.getDownId();
2432 int upid
= nl
.getUpId();
2434 NodeLog
& dnlog
= tl
.getNode(dnid
);
2435 NodeLog
& uplog
= tl
.getNode(upid
);
2436 dnlog
.copyParentRowIds();
2437 uplog
.copyParentRowIds();
2439 std::vector
<ConstraintCPVec
> dnres
;
2440 std::vector
<ConstraintCPVec
> upres
;
2441 std::vector
<size_t> containsdn
;
2442 std::vector
<size_t> containsup
;
2444 dnres
= replayLogRec(approx
, dnid
, dnc
, depth
+1);
2445 for(size_t i
= 0, N
= dnres
.size(); i
< N
; ++i
){
2446 ConstraintCPVec
& conf
= dnres
[i
];
2447 if(contains(conf
, dnc
)){
2448 containsdn
.push_back(i
);
2450 res
.push_back(conf
);
2454 Debug("approx::replayLogRec") << "replayLogRec() skipping" << dnlog
<< std::endl
;
2455 ++d_statistics
.d_replayBranchSkips
;
2459 upres
= replayLogRec(approx
, upid
, upc
, depth
+1);
2461 for(size_t i
= 0, N
= upres
.size(); i
< N
; ++i
){
2462 ConstraintCPVec
& conf
= upres
[i
];
2463 if(contains(conf
, upc
)){
2464 containsup
.push_back(i
);
2466 res
.push_back(conf
);
2470 Debug("approx::replayLogRec") << "replayLogRec() skipping" << uplog
<< std::endl
;
2471 ++d_statistics
.d_replayBranchSkips
;
2475 for(size_t i
= 0, N
= containsdn
.size(); i
< N
; ++i
){
2476 ConstraintCPVec
& dnconf
= dnres
[containsdn
[i
]];
2477 for(size_t j
= 0, M
= containsup
.size(); j
< M
; ++j
){
2478 ConstraintCPVec
& upconf
= upres
[containsup
[j
]];
2480 res
.push_back(ConstraintCPVec());
2481 ConstraintCPVec
& back
= res
.back();
2482 resolve(back
, dnc
, dnconf
, upconf
);
2485 if(res
.size() >= 2u){
2488 if(res
.size() > 100u){
2493 Debug("approx::replayLogRec") << "replayLogRec() skipping resolving" << nl
<< std::endl
;
2495 Debug("approx::replayLogRec") << "found #"<<res
.size()<<" conflicts on branch " << nid
<< endl
;
2497 ++d_statistics
.d_replayBranchCloseFailures
;
2501 Debug("approx::replayLogRec") << "failed to make a branch " << nid
<< endl
;
2504 ++d_statistics
.d_replayLeafCloseFailures
;
2505 Debug("approx::replayLogRec") << "failed on node " << nid
<< endl
;
2506 Assert(res
.empty());
2508 resolveOutPropagated(res
, propagated
);
2509 Debug("approx::replayLogRec") << "replayLogRec() ending" << std::endl
;
2512 if(options::replayFailureLemma()){
2513 // must be done inside the sat context to get things
2514 // propagated at this level
2515 if(res
.empty() && nid
== getTreeLog().getRootId()){
2516 Assert(!d_replayedLemmas
);
2517 d_replayedLemmas
= replayLemmas(approx
);
2518 Assert(d_acTmp
.empty());
2519 while(!d_approxCuts
.empty()){
2520 TrustNode lem
= d_approxCuts
.front();
2522 d_acTmp
.push_back(lem
);
2526 } /* pop the sat context */
2528 /* move into the current context. */
2529 while(!d_acTmp
.empty()){
2530 TrustNode lem
= d_acTmp
.back();
2532 d_approxCuts
.push_back(lem
);
2534 Assert(d_acTmp
.empty());
2536 /* Garbage collect the constraints from this call */
2537 while(d_replayConstraints
.size() > rpcons_size
){
2538 ConstraintP c
= d_replayConstraints
.back();
2539 d_replayConstraints
.pop_back();
2540 d_constraintDatabase
.deleteConstraintAndNegation(c
);
2543 /* Garbage collect the ArithVars made by this call */
2544 if(d_replayVariables
.size() > rpvars_size
){
2545 d_partialModel
.stopQueueingBoundCounts();
2546 UpdateTrackingCallback
utcb(&d_linEq
);
2547 d_partialModel
.processBoundsQueue(utcb
);
2548 d_linEq
.startTrackingBoundCounts();
2549 while(d_replayVariables
.size() > rpvars_size
){
2550 ArithVar v
= d_replayVariables
.back();
2551 d_replayVariables
.pop_back();
2552 Assert(d_partialModel
.canBeReleased(v
));
2553 if(!d_tableau
.isBasic(v
)){
2554 /* if it is not basic make it basic. */
2555 ArithVar b
= ARITHVAR_SENTINEL
;
2556 for(Tableau::ColIterator ci
= d_tableau
.colIterator(v
); !ci
.atEnd(); ++ci
){
2557 const Tableau::Entry
& e
= *ci
;
2558 b
= d_tableau
.rowIndexToBasic(e
.getRowIndex());
2561 Assert(b
!= ARITHVAR_SENTINEL
);
2562 DeltaRational cp
= d_partialModel
.getAssignment(b
);
2563 if(d_partialModel
.cmpAssignmentLowerBound(b
) < 0){
2564 cp
= d_partialModel
.getLowerBound(b
);
2565 }else if(d_partialModel
.cmpAssignmentUpperBound(b
) > 0){
2566 cp
= d_partialModel
.getUpperBound(b
);
2568 d_linEq
.pivotAndUpdate(b
, v
, cp
);
2570 Assert(d_tableau
.isBasic(v
));
2571 d_linEq
.stopTrackingRowIndex(d_tableau
.basicToRowIndex(v
));
2572 d_tableau
.removeBasicRow(v
);
2575 Debug("approx::vars") << "releasing " << v
<< endl
;
2577 d_linEq
.stopTrackingBoundCounts();
2578 d_partialModel
.startQueueingBoundCounts();
2579 d_partialModel
.attemptToReclaimReleased();
2584 TreeLog
& TheoryArithPrivate::getTreeLog(){
2585 if(d_treeLog
== NULL
){
2586 d_treeLog
= new TreeLog();
2591 ApproximateStatistics
& TheoryArithPrivate::getApproxStats(){
2592 if(d_approxStats
== NULL
){
2593 d_approxStats
= new ApproximateStatistics();
2595 return *d_approxStats
;
2598 Node
TheoryArithPrivate::branchToNode(ApproximateSimplex
* approx
,
2599 const NodeLog
& bn
) const
2601 Assert(bn
.isBranch());
2602 ArithVar v
= approx
->getBranchVar(bn
);
2603 if(v
!= ARITHVAR_SENTINEL
&& d_partialModel
.isIntegerInput(v
)){
2604 if(d_partialModel
.hasNode(v
)){
2605 Node n
= d_partialModel
.asNode(v
);
2606 double dval
= bn
.branchValue();
2607 Maybe
<Rational
> maybe_value
= ApproximateSimplex::estimateWithCFE(dval
);
2610 return Node::null();
2612 Rational
fl(maybe_value
.value().floor());
2613 NodeManager
* nm
= NodeManager::currentNM();
2614 Node leq
= nm
->mkNode(kind::LEQ
, n
, mkRationalNode(fl
));
2615 Node norm
= Rewriter::rewrite(leq
);
2619 return Node::null();
2622 Node
TheoryArithPrivate::cutToLiteral(ApproximateSimplex
* approx
, const CutInfo
& ci
) const{
2623 Assert(ci
.reconstructed());
2625 const DenseMap
<Rational
>& lhs
= ci
.getReconstruction().lhs
;
2626 Node sum
= toSumNode(d_partialModel
, lhs
);
2628 Kind k
= ci
.getKind();
2629 Assert(k
== kind::LEQ
|| k
== kind::GEQ
);
2630 Node rhs
= mkRationalNode(ci
.getReconstruction().rhs
);
2632 NodeManager
* nm
= NodeManager::currentNM();
2633 Node ineq
= nm
->mkNode(k
, sum
, rhs
);
2634 return Rewriter::rewrite(ineq
);
2636 return Node::null();
2639 bool TheoryArithPrivate::replayLemmas(ApproximateSimplex
* approx
){
2640 ++(d_statistics
.d_mipReplayLemmaCalls
);
2641 bool anythingnew
= false;
2643 TreeLog
& tl
= getTreeLog();
2644 NodeLog
& root
= tl
.getRootNode();
2645 root
.applySelected(); /* set row ids */
2647 vector
<const CutInfo
*> cuts
= approx
->getValidCuts(root
);
2648 for(size_t i
=0, N
=cuts
.size(); i
< N
; ++i
){
2649 const CutInfo
* cut
= cuts
[i
];
2650 Assert(cut
->reconstructed());
2651 Assert(cut
->proven());
2653 const DenseMap
<Rational
>& row
= cut
->getReconstruction().lhs
;
2654 if(!complexityBelow(row
, options::lemmaRejectCutSize())){
2655 ++(d_statistics
.d_cutsRejectedDuringLemmas
);
2659 Node cutConstraint
= cutToLiteral(approx
, *cut
);
2660 if(!cutConstraint
.isNull()){
2661 const ConstraintCPVec
& exp
= cut
->getExplanation();
2662 Node asLemma
= Constraint::externalExplainByAssertions(exp
);
2664 Node implied
= Rewriter::rewrite(cutConstraint
);
2665 anythingnew
= anythingnew
|| !isSatLiteral(implied
);
2667 Node implication
= asLemma
.impNode(implied
);
2668 // DO NOT CALL OUTPUT LEMMA!
2669 // TODO (project #37): justify
2670 d_approxCuts
.push_back(TrustNode::mkTrustLemma(implication
, nullptr));
2671 Debug("approx::lemmas") << "cut["<<i
<<"] " << implication
<< endl
;
2672 ++(d_statistics
.d_mipExternalCuts
);
2675 if(root
.isBranch()){
2676 Node lit
= branchToNode(approx
, root
);
2678 anythingnew
= anythingnew
|| !isSatLiteral(lit
);
2679 Node branch
= lit
.orNode(lit
.notNode());
2680 if (proofsEnabled())
2682 d_pfGen
->mkTrustNode(branch
, PfRule::SPLIT
, {}, {lit
});
2686 d_approxCuts
.push_back(TrustNode::mkTrustLemma(branch
, nullptr));
2688 ++(d_statistics
.d_mipExternalBranch
);
2689 Debug("approx::lemmas") << "branching "<< root
<<" as " << branch
<< endl
;
2695 void TheoryArithPrivate::turnOffApproxFor(int32_t rounds
){
2696 d_attemptSolveIntTurnedOff
= d_attemptSolveIntTurnedOff
+ rounds
;
2697 ++(d_statistics
.d_approxDisabled
);
2700 bool TheoryArithPrivate::safeToCallApprox() const{
2701 unsigned numRows
= 0;
2702 unsigned numCols
= 0;
2703 var_iterator vi
= var_begin(), vi_end
= var_end();
2704 // Assign each variable to a row and column variable as it appears in the input
2705 for(; vi
!= vi_end
&& !(numRows
> 0 && numCols
> 0); ++vi
){
2708 if(d_partialModel
.isAuxiliary(v
)){
2714 return (numRows
> 0 && numCols
> 0);
2718 // res = solveRealRelaxation(effortLevel);
2726 void TheoryArithPrivate::solveInteger(Theory::Effort effortLevel
){
2727 if(!safeToCallApprox()) { return; }
2729 Assert(safeToCallApprox());
2730 TimerStat::CodeTimer
codeTimer0(d_statistics
.d_solveIntTimer
);
2732 ++(d_statistics
.d_solveIntCalls
);
2733 d_statistics
.d_inSolveInteger
= 1;
2735 if(!Theory::fullEffort(effortLevel
)){
2736 d_solveIntAttempts
++;
2737 ++(d_statistics
.d_solveStandardEffort
);
2740 // if integers are attempted,
2741 Assert(options::useApprox());
2742 Assert(ApproximateSimplex::enabled());
2744 int level
= getSatContext()->getLevel();
2745 d_lastContextIntegerAttempted
= level
;
2748 static const int32_t mipLimit
= 200000;
2750 TreeLog
& tl
= getTreeLog();
2751 ApproximateStatistics
& stats
= getApproxStats();
2752 ApproximateSimplex
* approx
=
2753 ApproximateSimplex::mkApproximateSimplexSolver(d_partialModel
, tl
, stats
);
2755 approx
->setPivotLimit(mipLimit
);
2756 if(!d_guessedCoeffSet
){
2757 d_guessedCoeffs
= approx
->heuristicOptCoeffs();
2758 d_guessedCoeffSet
= true;
2760 if(!d_guessedCoeffs
.empty()){
2761 approx
->setOptCoeffs(d_guessedCoeffs
);
2763 static const int32_t depthForLikelyInfeasible
= 10;
2764 int maxDepthPass1
= d_likelyIntegerInfeasible
?
2765 depthForLikelyInfeasible
: options::maxApproxDepth();
2766 approx
->setBranchingDepth(maxDepthPass1
);
2767 approx
->setBranchOnVariableLimit(100);
2768 LinResult relaxRes
= approx
->solveRelaxation();
2769 if( relaxRes
== LinFeasible
){
2770 MipResult mipRes
= MipUnknown
;
2772 TimerStat::CodeTimer
codeTimer1(d_statistics
.d_mipTimer
);
2773 mipRes
= approx
->solveMIP(false);
2776 Debug("arith::solveInteger") << "mipRes " << mipRes
<< endl
;
2779 // attempt the solution
2781 ++(d_statistics
.d_solveIntModelsAttempts
);
2783 d_partialModel
.stopQueueingBoundCounts();
2784 UpdateTrackingCallback
utcb(&d_linEq
);
2785 d_partialModel
.processBoundsQueue(utcb
);
2786 d_linEq
.startTrackingBoundCounts();
2788 ApproximateSimplex::Solution mipSolution
;
2789 mipSolution
= approx
->extractMIP();
2790 importSolution(mipSolution
);
2791 solveRelaxationOrPanic(effortLevel
);
2793 if (d_qflraStatus
== Result::SAT
)
2797 if (ARITHVAR_SENTINEL
== nextIntegerViolation(false))
2799 ++(d_statistics
.d_solveIntModelsSuccessful
);
2805 d_linEq
.stopTrackingBoundCounts();
2806 d_partialModel
.startQueueingBoundCounts();
2810 /* All integer branches closed */
2811 approx
->setPivotLimit(2*mipLimit
);
2813 TimerStat::CodeTimer
codeTimer2(d_statistics
.d_mipTimer
);
2814 mipRes
= approx
->solveMIP(true);
2817 if(mipRes
== MipClosed
){
2818 d_likelyIntegerInfeasible
= true;
2820 AlwaysAssert(anyConflict() || d_qflraStatus
!= Result::SAT
);
2824 solveRealRelaxation(effortLevel
);
2827 if(!(anyConflict() || !d_approxCuts
.empty())){
2828 turnOffApproxFor(options::replayNumericFailurePenalty());
2831 case BranchesExhausted
:
2833 case PivotsExhauasted
:
2834 if(mipRes
== BranchesExhausted
){
2835 ++d_statistics
.d_branchesExhausted
;
2836 }else if(mipRes
== ExecExhausted
){
2837 ++d_statistics
.d_execExhausted
;
2838 }else if(mipRes
== PivotsExhauasted
){
2839 ++d_statistics
.d_pivotsExhausted
;
2842 approx
->setPivotLimit(2*mipLimit
);
2843 approx
->setBranchingDepth(2);
2845 TimerStat::CodeTimer
codeTimer3(d_statistics
.d_mipTimer
);
2846 mipRes
= approx
->solveMIP(true);
2848 replayLemmas(approx
);
2856 if(!Theory::fullEffort(effortLevel
)){
2857 if(anyConflict() || !d_approxCuts
.empty()){
2858 d_solveIntMaybeHelp
++;
2862 d_statistics
.d_inSolveInteger
= 0;
2865 SimplexDecisionProcedure
& TheoryArithPrivate::selectSimplex(bool pass1
){
2867 if(d_pass1SDP
== NULL
){
2868 if(options::useFC()){
2869 d_pass1SDP
= (SimplexDecisionProcedure
*)(&d_fcSimplex
);
2870 }else if(options::useSOI()){
2871 d_pass1SDP
= (SimplexDecisionProcedure
*)(&d_soiSimplex
);
2873 d_pass1SDP
= (SimplexDecisionProcedure
*)(&d_dualSimplex
);
2876 Assert(d_pass1SDP
!= NULL
);
2879 if(d_otherSDP
== NULL
){
2880 if(options::useFC()){
2881 d_otherSDP
= (SimplexDecisionProcedure
*)(&d_fcSimplex
);
2882 }else if(options::useSOI()){
2883 d_otherSDP
= (SimplexDecisionProcedure
*)(&d_soiSimplex
);
2885 d_otherSDP
= (SimplexDecisionProcedure
*)(&d_soiSimplex
);
2888 Assert(d_otherSDP
!= NULL
);
2893 void TheoryArithPrivate::importSolution(const ApproximateSimplex::Solution
& solution
){
2894 if(Debug
.isOn("arith::importSolution")){
2895 Debug("arith::importSolution") << "importSolution before " << d_qflraStatus
<< endl
;
2896 d_partialModel
.printEntireModel(Debug("arith::importSolution"));
2899 d_qflraStatus
= d_attemptSolSimplex
.attempt(solution
);
2901 if(Debug
.isOn("arith::importSolution")){
2902 Debug("arith::importSolution") << "importSolution intermediate " << d_qflraStatus
<< endl
;
2903 d_partialModel
.printEntireModel(Debug("arith::importSolution"));
2906 if(d_qflraStatus
!= Result::UNSAT
){
2907 static const int32_t pass2Limit
= 20;
2908 int16_t oldCap
= options::arithStandardCheckVarOrderPivots();
2909 Options::current().set(options::arithStandardCheckVarOrderPivots
, pass2Limit
);
2910 SimplexDecisionProcedure
& simplex
= selectSimplex(false);
2911 d_qflraStatus
= simplex
.findModel(false);
2912 Options::current().set(options::arithStandardCheckVarOrderPivots
, oldCap
);
2915 if(Debug
.isOn("arith::importSolution")){
2916 Debug("arith::importSolution") << "importSolution after " << d_qflraStatus
<< endl
;
2917 d_partialModel
.printEntireModel(Debug("arith::importSolution"));
2921 bool TheoryArithPrivate::solveRelaxationOrPanic(Theory::Effort effortLevel
)
2923 // if at this point the linear relaxation is still unknown,
2924 // attempt to branch an integer variable as a last ditch effort on full check
2925 if (d_qflraStatus
== Result::SAT_UNKNOWN
)
2927 d_qflraStatus
= selectSimplex(true).findModel(false);
2930 if (Theory::fullEffort(effortLevel
) && d_qflraStatus
== Result::SAT_UNKNOWN
)
2932 ArithVar canBranch
= nextIntegerViolation(false);
2933 if (canBranch
!= ARITHVAR_SENTINEL
)
2935 ++d_statistics
.d_panicBranches
;
2936 TrustNode branch
= branchIntegerVariable(canBranch
);
2937 Assert(branch
.getNode().getKind() == kind::OR
);
2938 Node rwbranch
= Rewriter::rewrite(branch
.getNode()[0]);
2939 if (!isSatLiteral(rwbranch
))
2941 d_approxCuts
.push_back(branch
);
2945 d_qflraStatus
= selectSimplex(false).findModel(true);
2950 bool TheoryArithPrivate::solveRealRelaxation(Theory::Effort effortLevel
){
2951 TimerStat::CodeTimer
codeTimer0(d_statistics
.d_solveRealRelaxTimer
);
2952 Assert(d_qflraStatus
!= Result::SAT
);
2954 d_partialModel
.stopQueueingBoundCounts();
2955 UpdateTrackingCallback
utcb(&d_linEq
);
2956 d_partialModel
.processBoundsQueue(utcb
);
2957 d_linEq
.startTrackingBoundCounts();
2959 bool noPivotLimit
= Theory::fullEffort(effortLevel
) ||
2960 !options::restrictedPivots();
2962 SimplexDecisionProcedure
& simplex
= selectSimplex(true);
2964 bool useApprox
= options::useApprox() && ApproximateSimplex::enabled() && getSolveIntegerResource();
2966 Debug("TheoryArithPrivate::solveRealRelaxation")
2967 << "solveRealRelaxation() approx"
2968 << " " << options::useApprox()
2969 << " " << ApproximateSimplex::enabled()
2971 << " " << safeToCallApprox()
2974 bool noPivotLimitPass1
= noPivotLimit
&& !useApprox
;
2975 d_qflraStatus
= simplex
.findModel(noPivotLimitPass1
);
2977 Debug("TheoryArithPrivate::solveRealRelaxation")
2978 << "solveRealRelaxation()" << " pass1 " << d_qflraStatus
<< endl
;
2980 if(d_qflraStatus
== Result::SAT_UNKNOWN
&& useApprox
&& safeToCallApprox()){
2981 // pass2: fancy-final
2982 static const int32_t relaxationLimit
= 10000;
2983 Assert(ApproximateSimplex::enabled());
2985 TreeLog
& tl
= getTreeLog();
2986 ApproximateStatistics
& stats
= getApproxStats();
2987 ApproximateSimplex
* approxSolver
=
2988 ApproximateSimplex::mkApproximateSimplexSolver(d_partialModel
, tl
, stats
);
2990 approxSolver
->setPivotLimit(relaxationLimit
);
2992 if(!d_guessedCoeffSet
){
2993 d_guessedCoeffs
= approxSolver
->heuristicOptCoeffs();
2994 d_guessedCoeffSet
= true;
2996 if(!d_guessedCoeffs
.empty()){
2997 approxSolver
->setOptCoeffs(d_guessedCoeffs
);
3000 ++d_statistics
.d_relaxCalls
;
3002 ApproximateSimplex::Solution relaxSolution
;
3003 LinResult relaxRes
= LinUnknown
;
3005 TimerStat::CodeTimer
codeTimer1(d_statistics
.d_lpTimer
);
3006 relaxRes
= approxSolver
->solveRelaxation();
3008 Debug("solveRealRelaxation") << "solve relaxation? " << endl
;
3011 Debug("solveRealRelaxation") << "lin feasible? " << endl
;
3012 ++d_statistics
.d_relaxLinFeas
;
3013 relaxSolution
= approxSolver
->extractRelaxation();
3014 importSolution(relaxSolution
);
3015 if(d_qflraStatus
!= Result::SAT
){
3016 ++d_statistics
.d_relaxLinFeasFailures
;
3020 // todo attempt to recreate approximate conflict
3021 ++d_statistics
.d_relaxLinInfeas
;
3022 Debug("solveRealRelaxation") << "lin infeasible " << endl
;
3023 relaxSolution
= approxSolver
->extractRelaxation();
3024 importSolution(relaxSolution
);
3025 if(d_qflraStatus
!= Result::UNSAT
){
3026 ++d_statistics
.d_relaxLinInfeasFailures
;
3030 ++d_statistics
.d_relaxLinExhausted
;
3031 Debug("solveRealRelaxation") << "exhuasted " << endl
;
3035 ++d_statistics
.d_relaxOthers
;
3038 delete approxSolver
;
3042 bool emmittedConflictOrSplit
= solveRelaxationOrPanic(effortLevel
);
3044 // TODO Save zeroes with no conflicts
3045 d_linEq
.stopTrackingBoundCounts();
3046 d_partialModel
.startQueueingBoundCounts();
3048 return emmittedConflictOrSplit
;
3051 bool TheoryArithPrivate::hasFreshArithLiteral(Node n
) const{
3052 switch(n
.getKind()){
3057 return !isSatLiteral(n
);
3059 if(n
[0].getType().isReal()){
3060 return !isSatLiteral(n
);
3061 }else if(n
[0].getType().isBoolean()){
3062 return hasFreshArithLiteral(n
[0]) ||
3063 hasFreshArithLiteral(n
[1]);
3068 // try the rhs first
3069 return hasFreshArithLiteral(n
[1]) ||
3070 hasFreshArithLiteral(n
[0]);
3072 if(n
.getType().isBoolean()){
3073 for(Node::iterator ni
=n
.begin(), nend
=n
.end(); ni
!=nend
; ++ni
){
3075 if(hasFreshArithLiteral(child
)){
3084 bool TheoryArithPrivate::preCheck(Theory::Effort level
)
3086 Assert(d_currentPropagationList
.empty());
3087 if(Debug
.isOn("arith::consistency")){
3088 Assert(unenqueuedVariablesAreConsistent());
3091 d_newFacts
= !done();
3092 // If d_previousStatus == SAT, then reverts on conflicts are safe
3093 // Otherwise, they are not and must be committed.
3094 d_previousStatus
= d_qflraStatus
;
3097 d_qflraStatus
= Result::SAT_UNKNOWN
;
3098 d_hasDoneWorkSinceCut
= true;
3103 void TheoryArithPrivate::preNotifyFact(TNode atom
, bool pol
, TNode fact
)
3105 ConstraintP curr
= constraintFromFactQueue(fact
);
3106 if (curr
!= NullConstraint
)
3108 bool res CVC5_UNUSED
= assertionCases(curr
);
3109 Assert(!res
|| anyConflict());
3113 bool TheoryArithPrivate::postCheck(Theory::Effort effortLevel
)
3116 while(!d_learnedBounds
.empty()){
3117 // we may attempt some constraints twice. this is okay!
3118 ConstraintP curr
= d_learnedBounds
.front();
3119 d_learnedBounds
.pop();
3120 Debug("arith::learned") << curr
<< endl
;
3122 bool res CVC5_UNUSED
= assertionCases(curr
);
3123 Assert(!res
|| anyConflict());
3125 if(anyConflict()){ break; }
3130 d_qflraStatus
= Result::UNSAT
;
3131 if (options::revertArithModels() && d_previousStatus
== Result::SAT
)
3133 ++d_statistics
.d_revertsOnConflicts
;
3134 Debug("arith::bt") << "clearing here "
3135 << " " << d_newFacts
<< " " << d_previousStatus
<< " "
3136 << d_qflraStatus
<< endl
;
3137 revertOutOfConflict();
3140 ++d_statistics
.d_commitsOnConflicts
;
3141 Debug("arith::bt") << "committing here "
3142 << " " << d_newFacts
<< " " << d_previousStatus
<< " "
3143 << d_qflraStatus
<< endl
;
3144 d_partialModel
.commitAssignmentChanges();
3145 revertOutOfConflict();
3148 //cout << "unate conflict 1 " << effortLevel << std::endl;
3153 if(Debug
.isOn("arith::print_assertions")) {
3154 debugPrintAssertions(Debug("arith::print_assertions"));
3157 bool emmittedConflictOrSplit
= false;
3158 Assert(d_conflicts
.empty());
3160 bool useSimplex
= d_qflraStatus
!= Result::SAT
;
3161 Debug("arith::ems") << "ems: " << emmittedConflictOrSplit
3162 << "pre realRelax" << endl
;
3165 emmittedConflictOrSplit
= solveRealRelaxation(effortLevel
);
3167 Debug("arith::ems") << "ems: " << emmittedConflictOrSplit
3168 << "post realRelax" << endl
;
3171 Debug("arith::ems") << "ems: " << emmittedConflictOrSplit
3172 << "pre solveInteger" << endl
;
3174 if(attemptSolveInteger(effortLevel
, emmittedConflictOrSplit
)){
3175 solveInteger(effortLevel
);
3177 ++d_statistics
.d_commitsOnConflicts
;
3178 Debug("arith::bt") << "committing here "
3179 << " " << d_newFacts
<< " " << d_previousStatus
<< " "
3180 << d_qflraStatus
<< endl
;
3181 revertOutOfConflict();
3188 Debug("arith::ems") << "ems: " << emmittedConflictOrSplit
3189 << "post solveInteger" << endl
;
3191 switch(d_qflraStatus
){
3195 ++d_statistics
.d_nontrivialSatChecks
;
3198 Debug("arith::bt") << "committing sap inConflit"
3199 << " " << d_newFacts
<< " " << d_previousStatus
<< " "
3200 << d_qflraStatus
<< endl
;
3201 d_partialModel
.commitAssignmentChanges();
3202 d_unknownsInARow
= 0;
3203 if(Debug
.isOn("arith::consistency")){
3204 Assert(entireStateIsConsistent("sat comit"));
3206 if(useSimplex
&& options::collectPivots()){
3207 if(options::useFC()){
3208 d_statistics
.d_satPivots
<< d_fcSimplex
.getPivots();
3210 d_statistics
.d_satPivots
<< d_dualSimplex
.getPivots();
3214 case Result::SAT_UNKNOWN
:
3216 ++(d_statistics
.d_unknownChecks
);
3217 Assert(!Theory::fullEffort(effortLevel
));
3218 Debug("arith::bt") << "committing unknown"
3219 << " " << d_newFacts
<< " " << d_previousStatus
<< " "
3220 << d_qflraStatus
<< endl
;
3221 d_partialModel
.commitAssignmentChanges();
3222 d_statistics
.d_maxUnknownsInARow
.maxAssign(d_unknownsInARow
);
3224 if(useSimplex
&& options::collectPivots()){
3225 if(options::useFC()){
3226 d_statistics
.d_unknownPivots
<< d_fcSimplex
.getPivots();
3228 d_statistics
.d_unknownPivots
<< d_dualSimplex
.getPivots();
3233 d_unknownsInARow
= 0;
3235 ++d_statistics
.d_commitsOnConflicts
;
3237 Debug("arith::bt") << "committing on conflict"
3238 << " " << d_newFacts
<< " " << d_previousStatus
<< " "
3239 << d_qflraStatus
<< endl
;
3240 d_partialModel
.commitAssignmentChanges();
3241 revertOutOfConflict();
3243 if(Debug
.isOn("arith::consistency::comitonconflict")){
3244 entireStateIsConsistent("commit on conflict");
3247 emmittedConflictOrSplit
= true;
3248 Debug("arith::conflict") << "simplex conflict" << endl
;
3250 if(useSimplex
&& options::collectPivots()){
3251 if(options::useFC()){
3252 d_statistics
.d_unsatPivots
<< d_fcSimplex
.getPivots();
3254 d_statistics
.d_unsatPivots
<< d_dualSimplex
.getPivots();
3261 d_statistics
.d_avgUnknownsInARow
<< d_unknownsInARow
;
3264 options::useFC() ? d_fcSimplex
.getPivots() : d_dualSimplex
.getPivots();
3265 for (std::size_t i
= 0; i
< nPivots
; ++i
)
3267 d_containing
.d_out
->spendResource(
3268 Resource::ArithPivotStep
);
3271 Debug("arith::ems") << "ems: " << emmittedConflictOrSplit
3272 << "pre approx cuts" << endl
;
3273 if(!d_approxCuts
.empty()){
3274 bool anyFresh
= false;
3275 while(!d_approxCuts
.empty()){
3276 TrustNode lem
= d_approxCuts
.front();
3278 Debug("arith::approx::cuts") << "approximate cut:" << lem
<< endl
;
3279 anyFresh
= anyFresh
|| hasFreshArithLiteral(lem
.getNode());
3280 Debug("arith::lemma") << "approximate cut:" << lem
<< endl
;
3281 outputTrustedLemma(lem
, InferenceId::ARITH_APPROX_CUT
);
3284 emmittedConflictOrSplit
= true;
3288 Debug("arith::ems") << "ems: " << emmittedConflictOrSplit
3289 << "post approx cuts" << endl
;
3291 // This should be fine if sat or unknown
3292 if (!emmittedConflictOrSplit
3293 && (options::arithPropagationMode()
3294 == options::ArithPropagationMode::UNATE_PROP
3295 || options::arithPropagationMode()
3296 == options::ArithPropagationMode::BOTH_PROP
))
3298 TimerStat::CodeTimer
codeTimer0(d_statistics
.d_newPropTime
);
3299 Assert(d_qflraStatus
!= Result::UNSAT
);
3301 while(!d_currentPropagationList
.empty() && !anyConflict()){
3302 ConstraintP curr
= d_currentPropagationList
.front();
3303 d_currentPropagationList
.pop_front();
3305 ConstraintType t
= curr
->getType();
3306 Assert(t
!= Disequality
)
3307 << "Disequalities are not allowed in d_currentPropagation";
3312 ConstraintP prev
= d_currentPropagationList
.front();
3313 d_currentPropagationList
.pop_front();
3314 d_constraintDatabase
.unatePropLowerBound(curr
, prev
);
3319 ConstraintP prev
= d_currentPropagationList
.front();
3320 d_currentPropagationList
.pop_front();
3321 d_constraintDatabase
.unatePropUpperBound(curr
, prev
);
3326 ConstraintP prevLB
= d_currentPropagationList
.front();
3327 d_currentPropagationList
.pop_front();
3328 ConstraintP prevUB
= d_currentPropagationList
.front();
3329 d_currentPropagationList
.pop_front();
3330 d_constraintDatabase
.unatePropEquality(curr
, prevLB
, prevUB
);
3333 default: Unhandled() << curr
->getType();
3338 Debug("arith::unate") << "unate conflict" << endl
;
3339 revertOutOfConflict();
3340 d_qflraStatus
= Result::UNSAT
;
3342 emmittedConflictOrSplit
= true;
3343 //cout << "unate conflict " << endl;
3344 Debug("arith::bt") << "committing on unate conflict"
3345 << " " << d_newFacts
<< " " << d_previousStatus
<< " "
3346 << d_qflraStatus
<< endl
;
3348 Debug("arith::conflict") << "unate arith conflict" << endl
;
3353 TimerStat::CodeTimer
codeTimer1(d_statistics
.d_newPropTime
);
3354 d_currentPropagationList
.clear();
3356 Assert(d_currentPropagationList
.empty());
3358 Debug("arith::ems") << "ems: " << emmittedConflictOrSplit
3359 << "post unate" << endl
;
3361 if(!emmittedConflictOrSplit
&& Theory::fullEffort(effortLevel
)){
3362 ++d_fullCheckCounter
;
3364 if(!emmittedConflictOrSplit
&& Theory::fullEffort(effortLevel
)){
3365 emmittedConflictOrSplit
= splitDisequalities();
3367 Debug("arith::ems") << "ems: " << emmittedConflictOrSplit
3368 << "pos splitting" << endl
;
3371 Debug("arith") << "integer? "
3372 << " conf/split " << emmittedConflictOrSplit
3373 << " fulleffort " << Theory::fullEffort(effortLevel
)
3374 << " hasintmodel " << hasIntegerModel() << endl
;
3376 if(!emmittedConflictOrSplit
&& Theory::fullEffort(effortLevel
) && !hasIntegerModel()){
3377 Node possibleConflict
= Node::null();
3378 if(!emmittedConflictOrSplit
&& options::arithDioSolver()){
3379 possibleConflict
= callDioSolver();
3380 if(possibleConflict
!= Node::null()){
3381 revertOutOfConflict();
3382 Debug("arith::conflict") << "dio conflict " << possibleConflict
<< endl
;
3383 // TODO (project #37): justify (proofs in the DIO solver)
3384 raiseBlackBoxConflict(possibleConflict
);
3386 emmittedConflictOrSplit
= true;
3390 if(!emmittedConflictOrSplit
&& d_hasDoneWorkSinceCut
&& options::arithDioSolver()){
3391 if(getDioCuttingResource()){
3392 TrustNode possibleLemma
= dioCutting();
3393 if(!possibleLemma
.isNull()){
3394 emmittedConflictOrSplit
= true;
3395 d_hasDoneWorkSinceCut
= false;
3396 d_cutCount
= d_cutCount
+ 1;
3397 Debug("arith::lemma") << "dio cut " << possibleLemma
<< endl
;
3398 outputTrustedLemma(possibleLemma
, InferenceId::ARITH_DIO_CUT
);
3403 if(!emmittedConflictOrSplit
) {
3404 TrustNode possibleLemma
= roundRobinBranch();
3405 if (!possibleLemma
.getNode().isNull())
3407 ++(d_statistics
.d_externalBranchAndBounds
);
3408 d_cutCount
= d_cutCount
+ 1;
3409 emmittedConflictOrSplit
= true;
3410 Debug("arith::lemma") << "rrbranch lemma"
3411 << possibleLemma
<< endl
;
3412 outputTrustedLemma(possibleLemma
, InferenceId::ARITH_BB_LEMMA
);
3416 if(options::maxCutsInContext() <= d_cutCount
){
3417 if(d_diosolver
.hasMoreDecompositionLemmas()){
3418 while(d_diosolver
.hasMoreDecompositionLemmas()){
3419 Node decompositionLemma
= d_diosolver
.nextDecompositionLemma();
3420 Debug("arith::lemma") << "dio decomposition lemma "
3421 << decompositionLemma
<< endl
;
3422 outputLemma(decompositionLemma
, InferenceId::ARITH_DIO_DECOMPOSITION
);
3425 Debug("arith::restart") << "arith restart!" << endl
;
3429 }//if !emmittedConflictOrSplit && fullEffort(effortLevel) && !hasIntegerModel()
3431 if(Theory::fullEffort(effortLevel
)){
3432 if(Debug
.isOn("arith::consistency::final")){
3433 entireStateIsConsistent("arith::consistency::final");
3437 if(Debug
.isOn("paranoid:check_tableau")){ d_linEq
.debugCheckTableau(); }
3438 if(Debug
.isOn("arith::print_model")) {
3439 debugPrintModel(Debug("arith::print_model"));
3441 Debug("arith") << "TheoryArithPrivate::check end" << std::endl
;
3442 return emmittedConflictOrSplit
;
3445 bool TheoryArithPrivate::foundNonlinear() const { return d_foundNl
; }
3447 TrustNode
TheoryArithPrivate::branchIntegerVariable(ArithVar x
) const
3449 const DeltaRational
& d
= d_partialModel
.getAssignment(x
);
3450 Assert(!d
.isIntegral());
3451 const Rational
& r
= d
.getNoninfinitesimalPart();
3452 const Rational
& i
= d
.getInfinitesimalPart();
3453 Trace("integers") << "integers: assignment to [[" << d_partialModel
.asNode(x
) << "]] is " << r
<< "[" << i
<< "]" << endl
;
3455 Assert(!(r
.getDenominator() == 1 && i
.getNumerator() == 0));
3456 Assert(!d
.isIntegral());
3457 TNode var
= d_partialModel
.asNode(x
);
3458 Integer floor_d
= d
.floor();
3460 TrustNode lem
= TrustNode::null();
3461 NodeManager
* nm
= NodeManager::currentNM();
3462 if (options::brabTest())
3464 Trace("integers") << "branch-round-and-bound enabled" << endl
;
3465 Integer ceil_d
= d
.ceiling();
3466 Rational f
= r
- floor_d
;
3467 // Multiply by -1 to get abs value.
3468 Rational c
= (r
- ceil_d
) * (-1);
3469 Integer nearest
= (c
> f
) ? floor_d
: ceil_d
;
3471 // Prioritize trying a simple rounding of the real solution first,
3472 // it that fails, fall back on original branch and bound strategy.
3473 Node ub
= Rewriter::rewrite(
3474 nm
->mkNode(kind::LEQ
, var
, mkRationalNode(nearest
- 1)));
3475 Node lb
= Rewriter::rewrite(
3476 nm
->mkNode(kind::GEQ
, var
, mkRationalNode(nearest
+ 1)));
3477 Node right
= nm
->mkNode(kind::OR
, ub
, lb
);
3478 Node rawEq
= nm
->mkNode(kind::EQUAL
, var
, mkRationalNode(nearest
));
3479 Node eq
= Rewriter::rewrite(rawEq
);
3480 // Also preprocess it before we send it out. This is important since
3481 // arithmetic may prefer eliminating equalities.
3483 if (Theory::theoryOf(eq
) == THEORY_ARITH
)
3485 teq
= d_containing
.ppRewriteEq(eq
);
3486 eq
= teq
.isNull() ? eq
: teq
.getNode();
3488 Node literal
= d_containing
.getValuation().ensureLiteral(eq
);
3489 Trace("integers") << "eq: " << eq
<< "\nto: " << literal
<< endl
;
3490 d_containing
.getOutputChannel().requirePhase(literal
, true);
3491 Node l
= nm
->mkNode(kind::OR
, literal
, right
);
3492 Trace("integers") << "l: " << l
<< endl
;
3493 if (proofsEnabled())
3495 Node less
= nm
->mkNode(kind::LT
, var
, mkRationalNode(nearest
));
3496 Node greater
= nm
->mkNode(kind::GT
, var
, mkRationalNode(nearest
));
3497 // TODO (project #37): justify. Thread proofs through *ensureLiteral*.
3498 Debug("integers::pf") << "less: " << less
<< endl
;
3499 Debug("integers::pf") << "greater: " << greater
<< endl
;
3500 Debug("integers::pf") << "literal: " << literal
<< endl
;
3501 Debug("integers::pf") << "eq: " << eq
<< endl
;
3502 Debug("integers::pf") << "rawEq: " << rawEq
<< endl
;
3503 Pf pfNotLit
= d_pnm
->mkAssume(literal
.negate());
3504 // rewrite notLiteral to notRawEq, using teq.
3509 PfRule::MACRO_SR_PRED_TRANSFORM
,
3510 {pfNotLit
, teq
.getGenerator()->getProofFor(teq
.getProven())},
3513 d_pnm
->mkNode(PfRule::CONTRA
,
3514 {d_pnm
->mkNode(PfRule::ARITH_TRICHOTOMY
,
3515 {d_pnm
->mkAssume(less
.negate()), pfNotRawEq
},
3517 d_pnm
->mkAssume(greater
.negate())},
3519 std::vector
<Node
> assumptions
= {
3520 literal
.negate(), less
.negate(), greater
.negate()};
3521 // Proof of (not (and (not (= v i)) (not (< v i)) (not (> v i))))
3522 Pf pfNotAnd
= d_pnm
->mkScope(pfBot
, assumptions
);
3523 Pf pfL
= d_pnm
->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM
,
3524 {d_pnm
->mkNode(PfRule::NOT_AND
, {pfNotAnd
}, {})},
3526 lem
= d_pfGen
->mkTrustNode(l
, pfL
);
3530 lem
= TrustNode::mkTrustLemma(l
, nullptr);
3536 Rewriter::rewrite(nm
->mkNode(kind::LEQ
, var
, mkRationalNode(floor_d
)));
3537 Node lb
= ub
.notNode();
3538 if (proofsEnabled())
3540 lem
= d_pfGen
->mkTrustNode(
3541 nm
->mkNode(kind::OR
, ub
, lb
), PfRule::SPLIT
, {}, {ub
});
3545 lem
= TrustNode::mkTrustLemma(nm
->mkNode(kind::OR
, ub
, lb
), nullptr);
3549 Trace("integers") << "integers: branch & bound: " << lem
<< endl
;
3550 if (Debug
.isOn("integers"))
3552 Node l
= lem
.getNode();
3553 if (isSatLiteral(l
[0]))
3555 Debug("integers") << " " << l
[0] << " == " << getSatValue(l
[0])
3560 Debug("integers") << " " << l
[0] << " is not assigned a SAT literal"
3563 if (isSatLiteral(l
[1]))
3565 Debug("integers") << " " << l
[1] << " == " << getSatValue(l
[1])
3570 Debug("integers") << " " << l
[1] << " is not assigned a SAT literal"
3577 std::vector
<ArithVar
> TheoryArithPrivate::cutAllBounded() const{
3578 vector
<ArithVar
> lemmas
;
3579 ArithVar max
= d_partialModel
.getNumberOfVariables();
3581 if(options::doCutAllBounded() && max
> 0){
3582 for(ArithVar iter
= 0; iter
!= max
; ++iter
){
3583 //Do not include slack variables
3584 const DeltaRational
& d
= d_partialModel
.getAssignment(iter
);
3585 if(isIntegerInput(iter
) &&
3586 !d_cutInContext
.contains(iter
) &&
3587 d_partialModel
.hasUpperBound(iter
) &&
3588 d_partialModel
.hasLowerBound(iter
) &&
3590 lemmas
.push_back(iter
);
3597 /** Returns true if the roundRobinBranching() issues a lemma. */
3598 TrustNode
TheoryArithPrivate::roundRobinBranch()
3600 if(hasIntegerModel()){
3601 return TrustNode::null();
3603 ArithVar v
= d_nextIntegerCheckVar
;
3605 Assert(isInteger(v
));
3606 Assert(!isAuxiliaryVariable(v
));
3607 return branchIntegerVariable(v
);
3611 bool TheoryArithPrivate::splitDisequalities(){
3612 bool splitSomething
= false;
3614 vector
<ConstraintP
> save
;
3616 while(!d_diseqQueue
.empty()){
3617 ConstraintP front
= d_diseqQueue
.front();
3620 if(front
->isSplit()){
3621 Debug("arith::eq") << "split already" << endl
;
3623 Debug("arith::eq") << "not split already" << endl
;
3625 ArithVar lhsVar
= front
->getVariable();
3627 const DeltaRational
& lhsValue
= d_partialModel
.getAssignment(lhsVar
);
3628 const DeltaRational
& rhsValue
= front
->getValue();
3629 if(lhsValue
== rhsValue
){
3630 Debug("arith::lemma") << "Splitting on " << front
<< endl
;
3631 Debug("arith::lemma") << "LHS value = " << lhsValue
<< endl
;
3632 Debug("arith::lemma") << "RHS value = " << rhsValue
<< endl
;
3633 TrustNode lemma
= front
->split();
3634 ++(d_statistics
.d_statDisequalitySplits
);
3636 Debug("arith::lemma")
3637 << "Now " << Rewriter::rewrite(lemma
.getNode()) << endl
;
3638 outputTrustedLemma(lemma
, InferenceId::ARITH_SPLIT_DEQ
);
3639 //cout << "Now " << Rewriter::rewrite(lemma) << endl;
3640 splitSomething
= true;
3641 }else if(d_partialModel
.strictlyLessThanLowerBound(lhsVar
, rhsValue
)){
3642 Debug("arith::eq") << "can drop as less than lb" << front
<< endl
;
3643 }else if(d_partialModel
.strictlyGreaterThanUpperBound(lhsVar
, rhsValue
)){
3644 Debug("arith::eq") << "can drop as greater than ub" << front
<< endl
;
3646 Debug("arith::eq") << "save" << front
<< ": " <<lhsValue
<< " != " << rhsValue
<< endl
;
3647 save
.push_back(front
);
3651 vector
<ConstraintP
>::const_iterator i
=save
.begin(), i_end
= save
.end();
3652 for(; i
!= i_end
; ++i
){
3653 d_diseqQueue
.push(*i
);
3655 return splitSomething
;
3659 * Should be guarded by at least Debug.isOn("arith::print_assertions").
3660 * Prints to Debug("arith::print_assertions")
3662 void TheoryArithPrivate::debugPrintAssertions(std::ostream
& out
) const {
3663 out
<< "Assertions:" << endl
;
3664 for (var_iterator vi
= var_begin(), vend
= var_end(); vi
!= vend
; ++vi
){
3666 if (d_partialModel
.hasLowerBound(i
)) {
3667 ConstraintP lConstr
= d_partialModel
.getLowerBoundConstraint(i
);
3668 out
<< lConstr
<< endl
;
3671 if (d_partialModel
.hasUpperBound(i
)) {
3672 ConstraintP uConstr
= d_partialModel
.getUpperBoundConstraint(i
);
3673 out
<< uConstr
<< endl
;
3676 context::CDQueue
<ConstraintP
>::const_iterator it
= d_diseqQueue
.begin();
3677 context::CDQueue
<ConstraintP
>::const_iterator it_end
= d_diseqQueue
.end();
3678 for(; it
!= it_end
; ++ it
) {
3683 void TheoryArithPrivate::debugPrintModel(std::ostream
& out
) const{
3684 out
<< "Model:" << endl
;
3685 for (var_iterator vi
= var_begin(), vend
= var_end(); vi
!= vend
; ++vi
){
3687 if(d_partialModel
.hasNode(i
)){
3688 out
<< d_partialModel
.asNode(i
) << " : " <<
3689 d_partialModel
.getAssignment(i
);
3690 if(d_tableau
.isBasic(i
)){
3698 TrustNode
TheoryArithPrivate::explain(TNode n
)
3700 Debug("arith::explain") << "explain @" << getSatContext()->getLevel() << ": " << n
<< endl
;
3702 ConstraintP c
= d_constraintDatabase
.lookup(n
);
3704 if(c
!= NullConstraint
){
3705 Assert(!c
->isAssumption());
3706 exp
= c
->externalExplainForPropagation(n
);
3707 Debug("arith::explain") << "constraint explanation" << n
<< ":" << exp
<< endl
;
3708 }else if(d_assertionsThatDoNotMatchTheirLiterals
.find(n
) != d_assertionsThatDoNotMatchTheirLiterals
.end()){
3709 c
= d_assertionsThatDoNotMatchTheirLiterals
[n
];
3710 if(!c
->isAssumption()){
3711 exp
= c
->externalExplainForPropagation(n
);
3712 Debug("arith::explain") << "assertions explanation" << n
<< ":" << exp
<< endl
;
3714 Debug("arith::explain") << "this is a strange mismatch" << n
<< endl
;
3715 Assert(d_congruenceManager
.canExplain(n
));
3716 exp
= d_congruenceManager
.explain(n
);
3719 Assert(d_congruenceManager
.canExplain(n
));
3720 Debug("arith::explain") << "dm explanation" << n
<< endl
;
3721 exp
= d_congruenceManager
.explain(n
);
3726 void TheoryArithPrivate::propagate(Theory::Effort e
) {
3727 // This uses model values for safety. Disable for now.
3728 if (d_qflraStatus
== Result::SAT
3729 && (options::arithPropagationMode()
3730 == options::ArithPropagationMode::BOUND_INFERENCE_PROP
3731 || options::arithPropagationMode()
3732 == options::ArithPropagationMode::BOTH_PROP
)
3735 if(options::newProp()){
3736 propagateCandidatesNew();
3738 propagateCandidates();
3746 while(d_constraintDatabase
.hasMorePropagations()){
3747 ConstraintCP c
= d_constraintDatabase
.nextPropagation();
3748 Debug("arith::prop") << "next prop" << getSatContext()->getLevel() << ": " << c
<< endl
;
3750 if(c
->negationHasProof()){
3751 Debug("arith::prop") << "negation has proof " << c
->getNegation() << endl
;
3752 Debug("arith::prop") << c
->getNegation()->externalExplainByAssertions()
3755 Assert(!c
->negationHasProof())
3756 << "A constraint has been propagated on the constraint propagation "
3757 "queue, but the negation has been set to true. Contact Tim now!";
3759 if(!c
->assertedToTheTheory()){
3760 Node literal
= c
->getLiteral();
3761 Debug("arith::prop") << "propagating @" << getSatContext()->getLevel() << " " << literal
<< endl
;
3763 outputPropagate(literal
);
3765 Debug("arith::prop") << "already asserted to the theory " << c
->getLiteral() << endl
;
3769 while(d_congruenceManager
.hasMorePropagations()){
3770 TNode toProp
= d_congruenceManager
.getNextPropagation();
3772 //Currently if the flag is set this came from an equality detected by the
3773 //equality engine in the the difference manager.
3774 Node normalized
= Rewriter::rewrite(toProp
);
3776 ConstraintP constraint
= d_constraintDatabase
.lookup(normalized
);
3777 if(constraint
== NullConstraint
){
3778 Debug("arith::prop") << "propagating on non-constraint? " << toProp
<< endl
;
3780 outputPropagate(toProp
);
3781 }else if(constraint
->negationHasProof()){
3782 // The congruence manager can prove: antecedents => toProp,
3783 // ergo. antecedents ^ ~toProp is a conflict.
3784 TrustNode exp
= d_congruenceManager
.explain(toProp
);
3785 Node notNormalized
= normalized
.negate();
3786 std::vector
<Node
> ants(exp
.getNode().begin(), exp
.getNode().end());
3787 ants
.push_back(notNormalized
);
3788 Node lp
= safeConstructNary(kind::AND
, ants
);
3789 Debug("arith::prop") << "propagate conflict" << lp
<< endl
;
3790 if (proofsEnabled())
3792 // Assume all of antecedents and ~toProp (rewritten)
3793 std::vector
<Pf
> pfAntList
;
3794 for (size_t i
= 0; i
< ants
.size(); ++i
)
3796 pfAntList
.push_back(d_pnm
->mkAssume(ants
[i
]));
3798 Pf pfAnt
= pfAntList
.size() > 1
3799 ? d_pnm
->mkNode(PfRule::AND_INTRO
, pfAntList
, {})
3801 // Use modus ponens to get toProp (un rewritten)
3802 Pf pfConc
= d_pnm
->mkNode(
3803 PfRule::MODUS_PONENS
,
3804 {pfAnt
, exp
.getGenerator()->getProofFor(exp
.getProven())},
3806 // prove toProp (rewritten)
3807 Pf pfConcRewritten
= d_pnm
->mkNode(
3808 PfRule::MACRO_SR_PRED_TRANSFORM
, {pfConc
}, {normalized
});
3809 Pf pfNotNormalized
= d_pnm
->mkAssume(notNormalized
);
3810 // prove bottom from toProp and ~toProp
3812 if (normalized
.getKind() == kind::NOT
)
3814 pfBot
= d_pnm
->mkNode(
3815 PfRule::CONTRA
, {pfNotNormalized
, pfConcRewritten
}, {});
3819 pfBot
= d_pnm
->mkNode(
3820 PfRule::CONTRA
, {pfConcRewritten
, pfNotNormalized
}, {});
3823 Pf pfNotAnd
= d_pnm
->mkScope(pfBot
, ants
);
3824 raiseBlackBoxConflict(lp
, pfNotAnd
);
3828 raiseBlackBoxConflict(lp
);
3833 Debug("arith::prop") << "propagating still?" << toProp
<< endl
;
3834 outputPropagate(toProp
);
3839 DeltaRational
TheoryArithPrivate::getDeltaValue(TNode term
) const
3841 AlwaysAssert(d_qflraStatus
!= Result::SAT_UNKNOWN
);
3842 Debug("arith::value") << term
<< std::endl
;
3844 if (d_partialModel
.hasArithVar(term
)) {
3845 ArithVar var
= d_partialModel
.asArithVar(term
);
3846 return d_partialModel
.getAssignment(var
);
3849 switch (Kind kind
= term
.getKind()) {
3850 case kind::CONST_RATIONAL
:
3851 return term
.getConst
<Rational
>();
3853 case kind::PLUS
: { // 2+ args
3854 DeltaRational
value(0);
3855 for (TNode::iterator i
= term
.begin(), iend
= term
.end(); i
!= iend
;
3857 value
= value
+ getDeltaValue(*i
);
3862 case kind::NONLINEAR_MULT
:
3863 case kind::MULT
: { // 2+ args
3864 Assert(!isSetup(term
));
3865 DeltaRational
value(1);
3866 for (TNode::iterator i
= term
.begin(), iend
= term
.end(); i
!= iend
;
3868 value
= value
* getDeltaValue(*i
);
3872 case kind::MINUS
: { // 2 args
3873 return getDeltaValue(term
[0]) - getDeltaValue(term
[1]);
3875 case kind::UMINUS
: { // 1 arg
3876 return (-getDeltaValue(term
[0]));
3879 case kind::DIVISION
: { // 2 args
3880 Assert(!isSetup(term
));
3881 return getDeltaValue(term
[0]) / getDeltaValue(term
[1]);
3883 case kind::DIVISION_TOTAL
:
3884 case kind::INTS_DIVISION_TOTAL
:
3885 case kind::INTS_MODULUS_TOTAL
: { // 2 args
3886 Assert(!isSetup(term
));
3887 DeltaRational denominator
= getDeltaValue(term
[1]);
3888 if (denominator
.isZero()) {
3889 return DeltaRational(0, 0);
3891 DeltaRational numerator
= getDeltaValue(term
[0]);
3892 if (kind
== kind::DIVISION_TOTAL
) {
3893 return numerator
/ denominator
;
3894 } else if (kind
== kind::INTS_DIVISION_TOTAL
) {
3895 return Rational(numerator
.euclidianDivideQuotient(denominator
));
3897 Assert(kind
== kind::INTS_MODULUS_TOTAL
);
3898 return Rational(numerator
.euclidianDivideRemainder(denominator
));
3903 throw ModelException(term
, "No model assignment.");
3907 Rational
TheoryArithPrivate::deltaValueForTotalOrder() const{
3909 std::set
<DeltaRational
> relevantDeltaValues
;
3910 context::CDQueue
<ConstraintP
>::const_iterator qiter
= d_diseqQueue
.begin();
3911 context::CDQueue
<ConstraintP
>::const_iterator qiter_end
= d_diseqQueue
.end();
3913 for(; qiter
!= qiter_end
; ++qiter
){
3914 ConstraintP curr
= *qiter
;
3916 const DeltaRational
& rhsValue
= curr
->getValue();
3917 relevantDeltaValues
.insert(rhsValue
);
3920 Theory::shared_terms_iterator shared_iter
= d_containing
.shared_terms_begin();
3921 Theory::shared_terms_iterator shared_end
= d_containing
.shared_terms_end();
3922 for(; shared_iter
!= shared_end
; ++shared_iter
){
3923 Node sharedCurr
= *shared_iter
;
3925 // ModelException is fatal as this point. Don't catch!
3926 // DeltaRationalException is fatal as this point. Don't catch!
3927 DeltaRational val
= getDeltaValue(sharedCurr
);
3928 relevantDeltaValues
.insert(val
);
3931 for(var_iterator vi
= var_begin(), vend
= var_end(); vi
!= vend
; ++vi
){
3933 const DeltaRational
& value
= d_partialModel
.getAssignment(v
);
3934 relevantDeltaValues
.insert(value
);
3935 if( d_partialModel
.hasLowerBound(v
)){
3936 const DeltaRational
& lb
= d_partialModel
.getLowerBound(v
);
3937 relevantDeltaValues
.insert(lb
);
3939 if( d_partialModel
.hasUpperBound(v
)){
3940 const DeltaRational
& ub
= d_partialModel
.getUpperBound(v
);
3941 relevantDeltaValues
.insert(ub
);
3945 if(relevantDeltaValues
.size() >= 2){
3946 std::set
<DeltaRational
>::const_iterator iter
= relevantDeltaValues
.begin();
3947 std::set
<DeltaRational
>::const_iterator iter_end
= relevantDeltaValues
.end();
3948 DeltaRational prev
= *iter
;
3950 for(; iter
!= iter_end
; ++iter
){
3951 const DeltaRational
& curr
= *iter
;
3953 Assert(prev
< curr
);
3955 DeltaRational::seperatingDelta(min
, prev
, curr
);
3960 Assert(min
.sgn() > 0);
3961 Rational belowMin
= min
/Rational(2);
3965 void TheoryArithPrivate::collectModelValues(const std::set
<Node
>& termSet
,
3966 std::map
<Node
, Node
>& arithModel
)
3968 AlwaysAssert(d_qflraStatus
== Result::SAT
);
3970 if(Debug
.isOn("arith::collectModelInfo")){
3974 Debug("arith::collectModelInfo") << "collectModelInfo() begin " << endl
;
3976 // Delta lasts at least the duration of the function call
3977 const Rational
& delta
= d_partialModel
.getDelta();
3978 std::unordered_set
<TNode
> shared
= d_containing
.currentlySharedTerms();
3981 // This is not very good for user push/pop....
3982 // Revisit when implementing push/pop
3983 for(var_iterator vi
= var_begin(), vend
= var_end(); vi
!= vend
; ++vi
){
3986 if(!isAuxiliaryVariable(v
)){
3987 Node term
= d_partialModel
.asNode(v
);
3989 if((theoryOf(term
) == THEORY_ARITH
|| shared
.find(term
) != shared
.end())
3990 && termSet
.find(term
) != termSet
.end()){
3992 const DeltaRational
& mod
= d_partialModel
.getAssignment(v
);
3993 Rational qmodel
= mod
.substituteDelta(delta
);
3995 Node qNode
= mkRationalNode(qmodel
);
3996 Debug("arith::collectModelInfo") << "m->assertEquality(" << term
<< ", " << qmodel
<< ", true)" << endl
;
3998 arithModel
[term
] = qNode
;
4000 Debug("arith::collectModelInfo") << "Skipping m->assertEquality(" << term
<< ", true)" << endl
;
4006 // Iterate over equivalence classes in LinearEqualityModule
4007 // const eq::EqualityEngine& ee = d_congruenceManager.getEqualityEngine();
4008 // m->assertEqualityEngine(&ee);
4010 Debug("arith::collectModelInfo") << "collectModelInfo() end " << endl
;
4013 bool TheoryArithPrivate::safeToReset() const {
4014 Assert(!d_tableauSizeHasBeenModified
);
4015 Assert(d_errorSet
.noSignals());
4017 ErrorSet::error_iterator error_iter
= d_errorSet
.errorBegin();
4018 ErrorSet::error_iterator error_end
= d_errorSet
.errorEnd();
4019 for(; error_iter
!= error_end
; ++error_iter
){
4020 ArithVar basic
= *error_iter
;
4021 if(!d_smallTableauCopy
.isBasic(basic
)){
4029 void TheoryArithPrivate::notifyRestart(){
4030 TimerStat::CodeTimer
codeTimer(d_statistics
.d_restartTimer
);
4032 if(Debug
.isOn("paranoid:check_tableau")){ d_linEq
.debugCheckTableau(); }
4034 ++d_restartsCounter
;
4035 d_solveIntMaybeHelp
= 0;
4036 d_solveIntAttempts
= 0;
4039 bool TheoryArithPrivate::entireStateIsConsistent(const string
& s
){
4041 for(var_iterator vi
= var_begin(), vend
= var_end(); vi
!= vend
; ++vi
){
4043 //ArithVar var = d_partialModel.asArithVar(*i);
4044 if(!d_partialModel
.assignmentIsConsistent(var
)){
4045 d_partialModel
.printModel(var
);
4046 Warning() << s
<< ":" << "Assignment is not consistent for " << var
<< d_partialModel
.asNode(var
);
4047 if(d_tableau
.isBasic(var
)){
4048 Warning() << " (basic)";
4052 }else if(d_partialModel
.isInteger(var
) && !d_partialModel
.integralAssignment(var
)){
4053 d_partialModel
.printModel(var
);
4054 Warning() << s
<< ":" << "Assignment is not integer for integer variable " << var
<< d_partialModel
.asNode(var
);
4055 if(d_tableau
.isBasic(var
)){
4056 Warning() << " (basic)";
4065 bool TheoryArithPrivate::unenqueuedVariablesAreConsistent(){
4067 for(var_iterator vi
= var_begin(), vend
= var_end(); vi
!= vend
; ++vi
){
4069 if(!d_partialModel
.assignmentIsConsistent(var
)){
4070 if(!d_errorSet
.inError(var
)){
4072 d_partialModel
.printModel(var
);
4073 Warning() << "Unenqueued var is not consistent for " << var
<< d_partialModel
.asNode(var
);
4074 if(d_tableau
.isBasic(var
)){
4075 Warning() << " (basic)";
4079 } else if(Debug
.isOn("arith::consistency::initial")){
4080 d_partialModel
.printModel(var
);
4081 Warning() << "Initial var is not consistent for " << var
<< d_partialModel
.asNode(var
);
4082 if(d_tableau
.isBasic(var
)){
4083 Warning() << " (basic)";
4092 void TheoryArithPrivate::presolve(){
4093 TimerStat::CodeTimer
codeTimer(d_statistics
.d_presolveTime
);
4095 d_statistics
.d_initialTableauSize
= d_tableau
.size();
4097 if(Debug
.isOn("paranoid:check_tableau")){ d_linEq
.debugCheckTableau(); }
4099 static thread_local
unsigned callCount
= 0;
4100 if(Debug
.isOn("arith::presolve")) {
4101 Debug("arith::presolve") << "TheoryArithPrivate::presolve #" << callCount
<< endl
;
4102 callCount
= callCount
+ 1;
4105 vector
<TrustNode
> lemmas
;
4106 if(!options::incrementalSolving()) {
4107 switch(options::arithUnateLemmaMode()){
4108 case options::ArithUnateLemmaMode::NO
: break;
4109 case options::ArithUnateLemmaMode::INEQUALITY
:
4110 d_constraintDatabase
.outputUnateInequalityLemmas(lemmas
);
4112 case options::ArithUnateLemmaMode::EQUALITY
:
4113 d_constraintDatabase
.outputUnateEqualityLemmas(lemmas
);
4115 case options::ArithUnateLemmaMode::ALL
:
4116 d_constraintDatabase
.outputUnateInequalityLemmas(lemmas
);
4117 d_constraintDatabase
.outputUnateEqualityLemmas(lemmas
);
4119 default: Unhandled() << options::arithUnateLemmaMode();
4123 vector
<TrustNode
>::const_iterator i
= lemmas
.begin(), i_end
= lemmas
.end();
4124 for(; i
!= i_end
; ++i
){
4126 Debug("arith::oldprop") << " lemma lemma duck " <<lem
<< endl
;
4127 outputTrustedLemma(lem
, InferenceId::ARITH_UNATE
);
4131 EqualityStatus
TheoryArithPrivate::getEqualityStatus(TNode a
, TNode b
) {
4132 if(d_qflraStatus
== Result::SAT_UNKNOWN
){
4133 return EQUALITY_UNKNOWN
;
4136 if (getDeltaValue(a
) == getDeltaValue(b
)) {
4137 return EQUALITY_TRUE_IN_MODEL
;
4139 return EQUALITY_FALSE_IN_MODEL
;
4141 } catch (DeltaRationalException
& dr
) {
4142 return EQUALITY_UNKNOWN
;
4143 } catch (ModelException
& me
) {
4144 return EQUALITY_UNKNOWN
;
4149 bool TheoryArithPrivate::propagateCandidateBound(ArithVar basic
, bool upperBound
){
4150 ++d_statistics
.d_boundComputations
;
4152 RowIndex ridx
= d_tableau
.basicToRowIndex(basic
);
4153 DeltaRational bound
= d_linEq
.computeRowBound(ridx
, upperBound
, basic
);
4155 if((upperBound
&& d_partialModel
.strictlyLessThanUpperBound(basic
, bound
)) ||
4156 (!upperBound
&& d_partialModel
.strictlyGreaterThanLowerBound(basic
, bound
))){
4158 // TODO: "Policy point"
4159 //We are only going to recreate the functionality for now.
4160 //In the future this can be improved to generate a temporary constraint
4162 //Experiment with doing this every time or only when the new constraint
4163 //implies an unknown fact.
4165 ConstraintType t
= upperBound
? UpperBound
: LowerBound
;
4166 ConstraintP bestImplied
= d_constraintDatabase
.getBestImpliedBound(basic
, t
, bound
);
4168 // Node bestImplied = upperBound ?
4169 // d_apm.getBestImpliedUpperBound(basic, bound):
4170 // d_apm.getBestImpliedLowerBound(basic, bound);
4172 if(bestImplied
!= NullConstraint
){
4173 //This should be stronger
4174 Assert(!upperBound
|| bound
<= bestImplied
->getValue());
4177 || d_partialModel
.lessThanUpperBound(basic
, bestImplied
->getValue()));
4179 Assert(upperBound
|| bound
>= bestImplied
->getValue());
4181 || d_partialModel
.greaterThanLowerBound(basic
,
4182 bestImplied
->getValue()));
4185 // ConstraintP c = d_constraintDatabase.lookup(bestImplied);
4186 // Assert(c != NullConstraint);
4188 bool assertedToTheTheory
= bestImplied
->assertedToTheTheory();
4189 bool canBePropagated
= bestImplied
->canBePropagated();
4190 bool hasProof
= bestImplied
->hasProof();
4192 Debug("arith::prop") << "arith::prop" << basic
4193 << " " << assertedToTheTheory
4194 << " " << canBePropagated
4198 if(bestImplied
->negationHasProof()){
4199 Warning() << "the negation of " << bestImplied
<< " : " << endl
4200 << "has proof " << bestImplied
->getNegation() << endl
4201 << bestImplied
->getNegation()->externalExplainByAssertions()
4205 if(!assertedToTheTheory
&& canBePropagated
&& !hasProof
){
4206 d_linEq
.propagateBasicFromRow(bestImplied
);
4207 // I think this can be skipped if canBePropagated is true
4208 //d_learnedBounds.push(bestImplied);
4209 if(Debug
.isOn("arith::prop")){
4210 Debug("arith::prop") << "success " << bestImplied
<< endl
;
4211 d_partialModel
.printModel(basic
, Debug("arith::prop"));
4215 if(Debug
.isOn("arith::prop")){
4216 Debug("arith::prop") << "failed " << basic
4218 << " " << assertedToTheTheory
4219 << " " << canBePropagated
4220 << " " << hasProof
<< endl
;
4221 d_partialModel
.printModel(basic
, Debug("arith::prop"));
4224 }else if(Debug
.isOn("arith::prop")){
4225 Debug("arith::prop") << "false " << bound
<< " ";
4226 d_partialModel
.printModel(basic
, Debug("arith::prop"));
4231 void TheoryArithPrivate::propagateCandidate(ArithVar basic
){
4232 bool success
= false;
4233 RowIndex ridx
= d_tableau
.basicToRowIndex(basic
);
4235 bool tryLowerBound
=
4236 d_partialModel
.strictlyAboveLowerBound(basic
) &&
4237 d_linEq
.rowLacksBound(ridx
, false, basic
) == NULL
;
4239 bool tryUpperBound
=
4240 d_partialModel
.strictlyBelowUpperBound(basic
) &&
4241 d_linEq
.rowLacksBound(ridx
, true, basic
) == NULL
;
4244 success
|= propagateCandidateLowerBound(basic
);
4247 success
|= propagateCandidateUpperBound(basic
);
4250 ++d_statistics
.d_boundPropagations
;
4254 void TheoryArithPrivate::propagateCandidates(){
4255 TimerStat::CodeTimer
codeTimer(d_statistics
.d_boundComputationTime
);
4257 Debug("arith::prop") << "propagateCandidates begin" << endl
;
4259 Assert(d_candidateBasics
.empty());
4261 if(d_updatedBounds
.empty()){ return; }
4263 DenseSet::const_iterator i
= d_updatedBounds
.begin();
4264 DenseSet::const_iterator end
= d_updatedBounds
.end();
4265 for(; i
!= end
; ++i
){
4267 if(d_tableau
.isBasic(var
) &&
4268 d_tableau
.basicRowLength(var
) <= options::arithPropagateMaxLength()){
4269 d_candidateBasics
.softAdd(var
);
4271 Tableau::ColIterator basicIter
= d_tableau
.colIterator(var
);
4272 for(; !basicIter
.atEnd(); ++basicIter
){
4273 const Tableau::Entry
& entry
= *basicIter
;
4274 RowIndex ridx
= entry
.getRowIndex();
4275 ArithVar rowVar
= d_tableau
.rowIndexToBasic(ridx
);
4276 Assert(entry
.getColVar() == var
);
4277 Assert(d_tableau
.isBasic(rowVar
));
4278 if(d_tableau
.getRowLength(ridx
) <= options::arithPropagateMaxLength()){
4279 d_candidateBasics
.softAdd(rowVar
);
4284 d_updatedBounds
.purge();
4286 while(!d_candidateBasics
.empty()){
4287 ArithVar candidate
= d_candidateBasics
.back();
4288 d_candidateBasics
.pop_back();
4289 Assert(d_tableau
.isBasic(candidate
));
4290 propagateCandidate(candidate
);
4292 Debug("arith::prop") << "propagateCandidates end" << endl
<< endl
<< endl
;
4295 void TheoryArithPrivate::propagateCandidatesNew(){
4296 /* Four criteria must be met for progagation on a variable to happen using a row:
4297 * 0: A new bound has to have been added to the row.
4298 * 1: The hasBoundsCount for the row must be "full" or be full minus one variable
4299 * (This is O(1) to check, but requires book keeping.)
4300 * 2: The current assignment must be strictly smaller/greater than the current bound.
4301 * assign(x) < upper(x)
4302 * (This is O(1) to compute.)
4303 * 3: There is a bound that is strictly smaller/greater than the current assignment.
4304 * assign(x) < c for some x <= c literal
4305 * (This is O(log n) to compute.)
4306 * 4: The implied bound on x is strictly smaller/greater than the current bound.
4307 * (This is O(n) to compute.)
4310 TimerStat::CodeTimer
codeTimer(d_statistics
.d_boundComputationTime
);
4311 Debug("arith::prop") << "propagateCandidatesNew begin" << endl
;
4313 Assert(d_qflraStatus
== Result::SAT
);
4314 if(d_updatedBounds
.empty()){ return; }
4315 dumpUpdatedBoundsToRows();
4316 Assert(d_updatedBounds
.empty());
4318 if(!d_candidateRows
.empty()){
4319 UpdateTrackingCallback
utcb(&d_linEq
);
4320 d_partialModel
.processBoundsQueue(utcb
);
4323 while(!d_candidateRows
.empty()){
4324 RowIndex candidate
= d_candidateRows
.back();
4325 d_candidateRows
.pop_back();
4326 propagateCandidateRow(candidate
);
4328 Debug("arith::prop") << "propagateCandidatesNew end" << endl
<< endl
<< endl
;
4331 bool TheoryArithPrivate::propagateMightSucceed(ArithVar v
, bool ub
) const{
4332 int cmp
= ub
? d_partialModel
.cmpAssignmentUpperBound(v
)
4333 : d_partialModel
.cmpAssignmentLowerBound(v
);
4334 bool hasSlack
= ub
? cmp
< 0 : cmp
> 0;
4336 ConstraintType t
= ub
? UpperBound
: LowerBound
;
4337 const DeltaRational
& a
= d_partialModel
.getAssignment(v
);
4339 if(isInteger(v
) && !a
.isIntegral()){
4343 ConstraintP strongestPossible
= d_constraintDatabase
.getBestImpliedBound(v
, t
, a
);
4344 if(strongestPossible
== NullConstraint
){
4347 bool assertedToTheTheory
= strongestPossible
->assertedToTheTheory();
4348 bool canBePropagated
= strongestPossible
->canBePropagated();
4349 bool hasProof
= strongestPossible
->hasProof();
4351 return !assertedToTheTheory
&& canBePropagated
&& !hasProof
;
4358 bool TheoryArithPrivate::attemptSingleton(RowIndex ridx
, bool rowUp
){
4359 Debug("arith::prop") << " attemptSingleton" << ridx
;
4361 const Tableau::Entry
* ep
;
4362 ep
= d_linEq
.rowLacksBound(ridx
, rowUp
, ARITHVAR_SENTINEL
);
4365 ArithVar v
= ep
->getColVar();
4366 const Rational
& coeff
= ep
->getCoefficient();
4368 // 0 = c * v + \sum rest
4370 // - c * v = \sum rest \leq D
4371 // if c > 0, v \geq -D/c so !vUp
4372 // if c < 0, v \leq -D/c so vUp
4373 // Suppose not rowUp
4374 // - c * v = \sum rest \geq D
4375 // if c > 0, v \leq -D/c so vUp
4376 // if c < 0, v \geq -D/c so !vUp
4377 bool vUp
= (rowUp
== ( coeff
.sgn() < 0));
4379 Debug("arith::prop") << " " << rowUp
<< " " << v
<< " " << coeff
<< " " << vUp
<< endl
;
4380 Debug("arith::prop") << " " << propagateMightSucceed(v
, vUp
) << endl
;
4382 if(propagateMightSucceed(v
, vUp
)){
4383 DeltaRational dr
= d_linEq
.computeRowBound(ridx
, rowUp
, v
);
4384 DeltaRational bound
= dr
/ (- coeff
);
4385 return tryToPropagate(ridx
, rowUp
, v
, vUp
, bound
);
4390 bool TheoryArithPrivate::attemptFull(RowIndex ridx
, bool rowUp
){
4391 Debug("arith::prop") << " attemptFull" << ridx
<< endl
;
4393 vector
<const Tableau::Entry
*> candidates
;
4395 for(Tableau::RowIterator i
= d_tableau
.ridRowIterator(ridx
); !i
.atEnd(); ++i
){
4396 const Tableau::Entry
& e
=*i
;
4397 const Rational
& c
= e
.getCoefficient();
4398 ArithVar v
= e
.getColVar();
4399 bool vUp
= (rowUp
== (c
.sgn() < 0));
4400 if(propagateMightSucceed(v
, vUp
)){
4401 candidates
.push_back(&e
);
4404 if(candidates
.empty()){ return false; }
4406 const DeltaRational slack
=
4407 d_linEq
.computeRowBound(ridx
, rowUp
, ARITHVAR_SENTINEL
);
4409 vector
<const Tableau::Entry
*>::const_iterator i
, iend
;
4410 for(i
= candidates
.begin(), iend
= candidates
.end(); i
!= iend
; ++i
){
4411 const Tableau::Entry
* ep
= *i
;
4412 const Rational
& c
= ep
->getCoefficient();
4413 ArithVar v
= ep
->getColVar();
4415 // See the comment for attemptSingleton()
4416 bool activeUp
= (rowUp
== (c
.sgn() > 0));
4417 bool vUb
= (rowUp
== (c
.sgn() < 0));
4419 const DeltaRational
& activeBound
= activeUp
?
4420 d_partialModel
.getUpperBound(v
):
4421 d_partialModel
.getLowerBound(v
);
4423 DeltaRational contribution
= activeBound
* c
;
4424 DeltaRational impliedBound
= (slack
- contribution
)/(-c
);
4426 bool success
= tryToPropagate(ridx
, rowUp
, v
, vUb
, impliedBound
);
4432 bool TheoryArithPrivate::tryToPropagate(RowIndex ridx
, bool rowUp
, ArithVar v
, bool vUb
, const DeltaRational
& bound
){
4434 bool weaker
= vUb
? d_partialModel
.strictlyLessThanUpperBound(v
, bound
):
4435 d_partialModel
.strictlyGreaterThanLowerBound(v
, bound
);
4437 ConstraintType t
= vUb
? UpperBound
: LowerBound
;
4439 ConstraintP implied
= d_constraintDatabase
.getBestImpliedBound(v
, t
, bound
);
4440 if(implied
!= NullConstraint
){
4441 return rowImplicationCanBeApplied(ridx
, rowUp
, implied
);
4447 Node
flattenImplication(Node imp
){
4448 NodeBuilder
nb(kind::OR
);
4449 std::unordered_set
<Node
> included
;
4451 Node right
= imp
[1];
4453 if(left
.getKind() == kind::AND
){
4454 for(Node::iterator i
= left
.begin(), iend
= left
.end(); i
!= iend
; ++i
) {
4455 if (!included
.count((*i
).negate()))
4457 nb
<< (*i
).negate();
4458 included
.insert((*i
).negate());
4462 if (!included
.count(left
.negate()))
4464 nb
<< left
.negate();
4465 included
.insert(left
.negate());
4469 if(right
.getKind() == kind::OR
){
4470 for(Node::iterator i
= right
.begin(), iend
= right
.end(); i
!= iend
; ++i
) {
4471 if (!included
.count(*i
))
4474 included
.insert(*i
);
4478 if (!included
.count(right
))
4481 included
.insert(right
);
4488 bool TheoryArithPrivate::rowImplicationCanBeApplied(RowIndex ridx
, bool rowUp
, ConstraintP implied
){
4489 Assert(implied
!= NullConstraint
);
4490 ArithVar v
= implied
->getVariable();
4492 bool assertedToTheTheory
= implied
->assertedToTheTheory();
4493 bool canBePropagated
= implied
->canBePropagated();
4494 bool hasProof
= implied
->hasProof();
4496 Debug("arith::prop") << "arith::prop" << v
4497 << " " << assertedToTheTheory
4498 << " " << canBePropagated
4503 if( !assertedToTheTheory
&& canBePropagated
&& !hasProof
){
4504 ConstraintCPVec explain
;
4505 ARITH_PROOF(d_farkasBuffer
.clear());
4506 RationalVectorP coeffs
= ARITH_NULLPROOF(&d_farkasBuffer
);
4508 // After invoking `propegateRow`:
4509 // * coeffs[0] is for implied
4510 // * coeffs[i+1] is for explain[i]
4511 d_linEq
.propagateRow(explain
, ridx
, rowUp
, implied
, coeffs
);
4512 if(d_tableau
.getRowLength(ridx
) <= options::arithPropAsLemmaLength()){
4513 if (Debug
.isOn("arith::prop::pf")) {
4514 for (const auto & constraint
: explain
) {
4515 Assert(constraint
->hasProof());
4516 constraint
->printProofTree(Debug("arith::prop::pf"));
4519 Node implication
= implied
->externalImplication(explain
);
4520 Node clause
= flattenImplication(implication
);
4521 std::shared_ptr
<ProofNode
> clausePf
{nullptr};
4523 if (isProofEnabled())
4525 // We can prove this lemma from Farkas...
4526 std::vector
<std::shared_ptr
<ProofNode
>> conflictPfs
;
4527 // Assume the negated getLiteral version of the implied constaint
4528 // then rewrite it into proof normal form.
4529 conflictPfs
.push_back(
4530 d_pnm
->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM
,
4531 {d_pnm
->mkAssume(implied
->getLiteral().negate())},
4532 {implied
->getNegation()->getProofLiteral()}));
4533 // Add the explaination proofs.
4534 for (const auto constraint
: explain
)
4537 conflictPfs
.push_back(constraint
->externalExplainByAssertions(nb
));
4539 // Collect the farkas coefficients, as nodes.
4540 std::vector
<Node
> farkasCoefficients
;
4541 farkasCoefficients
.reserve(coeffs
->size());
4542 auto nm
= NodeManager::currentNM();
4546 std::back_inserter(farkasCoefficients
),
4547 [nm
](const Rational
& r
) { return nm
->mkConst
<Rational
>(r
); });
4550 auto sumPf
= d_pnm
->mkNode(
4551 PfRule::MACRO_ARITH_SCALE_SUM_UB
, conflictPfs
, farkasCoefficients
);
4552 auto botPf
= d_pnm
->mkNode(
4553 PfRule::MACRO_SR_PRED_TRANSFORM
, {sumPf
}, {nm
->mkConst(false)});
4555 // Prove the conflict
4556 std::vector
<Node
> assumptions
;
4557 assumptions
.reserve(clause
.getNumChildren());
4558 std::transform(clause
.begin(),
4560 std::back_inserter(assumptions
),
4561 [](TNode r
) { return r
.negate(); });
4562 auto notAndNotPf
= d_pnm
->mkScope(botPf
, assumptions
);
4564 // Convert it to a clause
4565 auto orNotNotPf
= d_pnm
->mkNode(PfRule::NOT_AND
, {notAndNotPf
}, {});
4566 clausePf
= d_pnm
->mkNode(
4567 PfRule::MACRO_SR_PRED_TRANSFORM
, {orNotNotPf
}, {clause
});
4570 TrustNode trustedClause
= d_pfGen
->mkTrustNode(clause
, clausePf
);
4571 outputTrustedLemma(trustedClause
, InferenceId::ARITH_ROW_IMPL
);
4575 outputLemma(clause
, InferenceId::ARITH_ROW_IMPL
);
4578 Assert(!implied
->negationHasProof());
4579 implied
->impliedByFarkas(explain
, coeffs
, false);
4580 implied
->tryToPropagate();
4585 if(Debug
.isOn("arith::prop")){
4586 Debug("arith::prop")
4587 << "failed " << v
<< " " << assertedToTheTheory
<< " "
4588 << canBePropagated
<< " " << hasProof
<< " " << implied
<< endl
;
4589 d_partialModel
.printModel(v
, Debug("arith::prop"));
4594 bool TheoryArithPrivate::propagateCandidateRow(RowIndex ridx
){
4595 BoundCounts hasCount
= d_linEq
.hasBoundCount(ridx
);
4596 uint32_t rowLength
= d_tableau
.getRowLength(ridx
);
4598 bool success
= false;
4599 static int instance
= 0;
4602 Debug("arith::prop")
4603 << "propagateCandidateRow " << instance
<< " attempt " << rowLength
<< " " << hasCount
<< endl
;
4605 if (rowLength
>= options::arithPropagateMaxLength()
4606 && Random::getRandom().pickWithProb(
4607 1.0 - double(options::arithPropagateMaxLength()) / rowLength
))
4612 if(hasCount
.lowerBoundCount() == rowLength
){
4613 success
|= attemptFull(ridx
, false);
4614 }else if(hasCount
.lowerBoundCount() + 1 == rowLength
){
4615 success
|= attemptSingleton(ridx
, false);
4618 if(hasCount
.upperBoundCount() == rowLength
){
4619 success
|= attemptFull(ridx
, true);
4620 }else if(hasCount
.upperBoundCount() + 1 == rowLength
){
4621 success
|= attemptSingleton(ridx
, true);
4626 void TheoryArithPrivate::dumpUpdatedBoundsToRows(){
4627 Assert(d_candidateRows
.empty());
4628 DenseSet::const_iterator i
= d_updatedBounds
.begin();
4629 DenseSet::const_iterator end
= d_updatedBounds
.end();
4630 for(; i
!= end
; ++i
){
4632 if(d_tableau
.isBasic(var
)){
4633 RowIndex ridx
= d_tableau
.basicToRowIndex(var
);
4634 d_candidateRows
.softAdd(ridx
);
4636 Tableau::ColIterator basicIter
= d_tableau
.colIterator(var
);
4637 for(; !basicIter
.atEnd(); ++basicIter
){
4638 const Tableau::Entry
& entry
= *basicIter
;
4639 RowIndex ridx
= entry
.getRowIndex();
4640 d_candidateRows
.softAdd(ridx
);
4644 d_updatedBounds
.purge();
4647 const BoundsInfo
& TheoryArithPrivate::boundsInfo(ArithVar basic
) const{
4648 RowIndex ridx
= d_tableau
.basicToRowIndex(basic
);
4649 return d_rowTracking
[ridx
];
4652 std::pair
<bool, Node
> TheoryArithPrivate::entailmentCheck(TNode lit
, const ArithEntailmentCheckParameters
& params
, ArithEntailmentCheckSideEffects
& out
){
4653 using namespace inferbounds
;
4656 // diff : (l - r) k 0
4657 Debug("arith::entailCheck") << "TheoryArithPrivate::entailmentCheck(" << lit
<< ")"<< endl
;
4660 Rational lm
, rm
, dm
;
4663 bool successful
= decomposeLiteral(lit
, k
, primDir
, lm
, lp
, rm
, rp
, dm
, dp
, sep
);
4664 if(!successful
) { return make_pair(false, Node::null()); }
4666 if(dp
.getKind() == CONST_RATIONAL
){
4667 Node eval
= Rewriter::rewrite(lit
);
4668 Assert(eval
.getKind() == kind::CONST_BOOLEAN
);
4669 // if true, true is an acceptable explaination
4670 // if false, the node is uninterpreted and eval can be forgotten
4671 return make_pair(eval
.getConst
<bool>(), eval
);
4673 Assert(dm
!= Rational(0));
4674 Assert(primDir
== 1 || primDir
== -1);
4676 int negPrim
= -primDir
;
4678 int secDir
= (k
== EQUAL
|| k
== DISTINCT
) ? negPrim
: 0;
4679 int negSecDir
= (k
== EQUAL
|| k
== DISTINCT
) ? primDir
: 0;
4681 // primDir*[lm*( lp )] k primDir*[ [rm*( rp )] + sep ]
4682 // primDir*[lm*( lp ) - rm*( rp ) ] k primDir*sep
4683 // primDir*[dm * dp] k primDir*sep
4685 std::pair
<Node
, DeltaRational
> bestPrimLeft
, bestNegPrimRight
, bestPrimDiff
, tmp
;
4686 std::pair
<Node
, DeltaRational
> bestSecLeft
, bestNegSecRight
, bestSecDiff
;
4687 bestPrimLeft
.first
= Node::null(); bestNegPrimRight
.first
= Node::null(); bestPrimDiff
.first
= Node::null();
4688 bestSecLeft
.first
= Node::null(); bestNegSecRight
.first
= Node::null(); bestSecDiff
.first
= Node::null();
4692 ArithEntailmentCheckParameters::const_iterator alg
, alg_end
;
4693 for( alg
= params
.begin(), alg_end
= params
.end(); alg
!= alg_end
; ++alg
){
4694 const inferbounds::InferBoundAlgorithm
& ibalg
= *alg
;
4696 Debug("arith::entailCheck") << "entailmentCheck trying " << (inferbounds::Algorithms
) ibalg
.getAlgorithm() << endl
;
4697 switch(ibalg
.getAlgorithm()){
4698 case inferbounds::None
:
4700 case inferbounds::Lookup
:
4701 case inferbounds::RowSum
:
4703 typedef void (TheoryArithPrivate::*EntailmentCheckFunc
)(std::pair
<Node
, DeltaRational
>&, int, TNode
) const;
4705 EntailmentCheckFunc ecfunc
=
4706 (ibalg
.getAlgorithm() == inferbounds::Lookup
)
4707 ? (&TheoryArithPrivate::entailmentCheckBoundLookup
)
4708 : (&TheoryArithPrivate::entailmentCheckRowSum
);
4710 (*this.*ecfunc
)(tmp
, primDir
* lm
.sgn(), lp
);
4711 setToMin(primDir
* lm
.sgn(), bestPrimLeft
, tmp
);
4713 (*this.*ecfunc
)(tmp
, negPrim
* rm
.sgn(), rp
);
4714 setToMin(negPrim
* rm
.sgn(), bestNegPrimRight
, tmp
);
4716 (*this.*ecfunc
)(tmp
, secDir
* lm
.sgn(), lp
);
4717 setToMin(secDir
* lm
.sgn(), bestSecLeft
, tmp
);
4719 (*this.*ecfunc
)(tmp
, negSecDir
* rm
.sgn(), rp
);
4720 setToMin(negSecDir
* rm
.sgn(), bestNegSecRight
, tmp
);
4722 (*this.*ecfunc
)(tmp
, primDir
* dm
.sgn(), dp
);
4723 setToMin(primDir
* dm
.sgn(), bestPrimDiff
, tmp
);
4725 (*this.*ecfunc
)(tmp
, secDir
* dm
.sgn(), dp
);
4726 setToMin(secDir
* dm
.sgn(), bestSecDiff
, tmp
);
4733 // turn bounds on prim * left and -prim * right into bounds on prim * diff
4734 if(!bestPrimLeft
.first
.isNull() && !bestNegPrimRight
.first
.isNull()){
4735 // primDir*lm* lp <= primDir*lm*L
4736 // -primDir*rm* rp <= -primDir*rm*R
4737 // primDir*lm* lp -primDir*rm* rp <= primDir*lm*L - primDir*rm*R
4738 // primDir [lm* lp -rm* rp] <= primDir[lm*L - *rm*R]
4739 // primDir [dm * dp] <= primDir[lm*L - *rm*R]
4740 // primDir [dm * dp] <= primDir * dm * ([lm*L - *rm*R]/dm)
4741 tmp
.second
= ((bestPrimLeft
.second
* lm
) - (bestNegPrimRight
.second
* rm
)) / dm
;
4742 tmp
.first
= (bestPrimLeft
.first
).andNode(bestNegPrimRight
.first
);
4743 setToMin(primDir
, bestPrimDiff
, tmp
);
4746 // turn bounds on sec * left and sec * right into bounds on sec * diff
4747 if(secDir
!= 0 && !bestSecLeft
.first
.isNull() && !bestNegSecRight
.first
.isNull()){
4748 // secDir*lm* lp <= secDir*lm*L
4749 // -secDir*rm* rp <= -secDir*rm*R
4750 // secDir*lm* lp -secDir*rm* rp <= secDir*lm*L - secDir*rm*R
4751 // secDir [lm* lp -rm* rp] <= secDir[lm*L - *rm*R]
4752 // secDir [dm * dp] <= secDir[lm*L - *rm*R]
4753 // secDir [dm * dp] <= secDir * dm * ([lm*L - *rm*R]/dm)
4754 tmp
.second
= ((bestSecLeft
.second
* lm
) - (bestNegSecRight
.second
* rm
)) / dm
;
4755 tmp
.first
= (bestSecLeft
.first
).andNode(bestNegSecRight
.first
);
4756 setToMin(secDir
, bestSecDiff
, tmp
);
4761 if(!bestPrimDiff
.first
.isNull()){
4762 DeltaRational d
= (bestPrimDiff
.second
* dm
);
4763 if((primDir
> 0 && d
<= sep
) || (primDir
< 0 && d
>= sep
) ){
4764 Debug("arith::entailCheck") << "entailmentCheck found "
4765 << primDir
<< "*" << dm
<< "*(" << dp
<<")"
4766 << " <= " << primDir
<< "*" << dm
<< "*" << bestPrimDiff
.second
4767 << " <= " << primDir
<< "*" << sep
<< endl
4768 << " by " << bestPrimDiff
.first
<< endl
;
4769 Assert(bestPrimDiff
.second
* (Rational(primDir
) * dm
)
4770 <= (sep
* Rational(primDir
)));
4771 return make_pair(true, bestPrimDiff
.first
);
4776 if(!bestPrimDiff
.first
.isNull() && !bestSecDiff
.first
.isNull()){
4777 // Is primDir [dm * dp] == primDir * sep entailed?
4778 // Iff [dm * dp] == sep entailed?
4779 // Iff dp == sep / dm entailed?
4780 // Iff dp <= sep / dm and dp >= sep / dm entailed?
4782 // primDir [dm * dp] <= primDir * dm * U
4783 // secDir [dm * dp] <= secDir * dm * L
4785 // Suppose primDir * dm > 0
4786 // then secDir * dm < 0
4787 // dp >= (secDir * L) / secDir * dm
4788 // dp >= (primDir * L) / primDir * dm
4792 // dp == sep / dm entailed iff U == L == sep
4793 // Suppose primDir * dm < 0
4794 // then secDir * dm > 0
4797 // dp == sep / dm entailed iff U == L == sep
4798 if(bestPrimDiff
.second
== bestSecDiff
.second
){
4799 if(bestPrimDiff
.second
== sep
){
4800 return make_pair(true, (bestPrimDiff
.first
).andNode(bestSecDiff
.first
));
4804 // intentionally fall through to DISTINCT case!
4805 // entailments of negations are eager exit cases for EQUAL
4808 if(!bestPrimDiff
.first
.isNull()){
4809 // primDir [dm * dp] <= primDir * dm * U < primDir * sep
4810 if((primDir
> 0 && (bestPrimDiff
.second
* dm
< sep
)) ||
4811 (primDir
< 0 && (bestPrimDiff
.second
* dm
> sep
))){
4812 // entailment of negation
4814 return make_pair(true, bestPrimDiff
.first
);
4817 return make_pair(false, Node::null());
4821 if(!bestSecDiff
.first
.isNull()){
4822 // If primDir [dm * dp] > primDir * sep, then this is not entailed.
4823 // If primDir [dm * dp] >= primDir * dm * L > primDir * sep
4824 // -primDir * dm * L < -primDir * sep
4825 // secDir * dm * L < secDir * sep
4826 if((secDir
> 0 && (bestSecDiff
.second
* dm
< sep
)) ||
4827 (secDir
< 0 && (bestSecDiff
.second
* dm
> sep
))){
4829 return make_pair(true, bestSecDiff
.first
);
4832 return make_pair(false, Node::null());
4843 return make_pair(false, Node::null());
4846 bool TheoryArithPrivate::decomposeTerm(Node term
, Rational
& m
, Node
& p
, Rational
& c
){
4847 Node t
= Rewriter::rewrite(term
);
4848 if(!Polynomial::isMember(t
)){
4853 preprocessing::util::ContainsTermITEVisitor ctv
;
4854 if(ctv
.containsTermITE(t
)){
4858 Polynomial poly
= Polynomial::parsePolynomial(t
);
4859 if(poly
.isConstant()){
4860 c
= poly
.getHead().getConstant().getValue();
4861 p
= mkRationalNode(Rational(0));
4864 }else if(poly
.containsConstant()){
4865 c
= poly
.getHead().getConstant().getValue();
4866 poly
= poly
.getTail();
4870 Assert(!poly
.isConstant());
4871 Assert(!poly
.containsConstant());
4873 const bool intVars
= poly
.allIntegralVariables();
4877 if(!poly
.isIntegral()){
4878 Integer denom
= poly
.denominatorLCM();
4880 poly
= poly
* denom
;
4882 Integer g
= poly
.gcd();
4884 poly
= poly
* Rational(1,g
);
4885 Assert(poly
.isIntegral());
4886 Assert(poly
.leadingCoefficientIsPositive());
4889 m
= poly
.getHead().getConstant().getValue();
4890 poly
= poly
* m
.inverse();
4891 Assert(poly
.leadingCoefficientIsAbsOne());
4897 void TheoryArithPrivate::setToMin(int sgn
, std::pair
<Node
, DeltaRational
>& min
, const std::pair
<Node
, DeltaRational
>& e
){
4899 if(min
.first
.isNull() && !e
.first
.isNull()){
4901 }else if(!min
.first
.isNull() && !e
.first
.isNull()){
4902 if(sgn
> 0 && min
.second
> e
.second
){
4904 }else if(sgn
< 0 && min
.second
< e
.second
){
4911 // std::pair<bool, Node> TheoryArithPrivate::entailmentUpperCheck(const Rational& lm, Node lp, const Rational& rm, Node rp, const DeltaRational& sep, const ArithEntailmentCheckParameters& params, ArithEntailmentCheckSideEffects& out){
4913 // Rational negRM = -rm;
4914 // Node diff = NodeManager::currentNM()->mkNode(MULT, mkRationalConstan(lm), lp) + (negRM * rp);
4918 // decompose(diff, diffm, diffNode);
4921 // std::pair<Node, DeltaRational> bestUbLeft, bestLbRight, bestUbDiff, tmp;
4922 // bestUbLeft = bestLbRight = bestUbDiff = make_pair(Node::Null(), DeltaRational());
4924 // return make_pair(false, Node::null());
4928 * Decomposes a literal into the form:
4929 * dir*[lm*( lp )] k dir*[ [rm*( rp )] + sep ]
4930 * dir*[dm* dp] k dir *sep
4931 * dir is either 1 or -1
4933 bool TheoryArithPrivate::decomposeLiteral(Node lit
, Kind
& k
, int& dir
, Rational
& lm
, Node
& lp
, Rational
& rm
, Node
& rp
, Rational
& dm
, Node
& dp
, DeltaRational
& sep
){
4934 bool negated
= (lit
.getKind() == kind::NOT
);
4935 TNode atom
= negated
? lit
[0] : lit
;
4937 TNode left
= atom
[0];
4938 TNode right
= atom
[1];
4940 // left : lm*( lp ) + lc
4941 // right: rm*( rp ) + rc
4943 bool success
= decomposeTerm(left
, lm
, lp
, lc
);
4944 if(!success
){ return false; }
4945 success
= decomposeTerm(right
, rm
, rp
, rc
);
4946 if(!success
){ return false; }
4948 Node diff
= Rewriter::rewrite(NodeManager::currentNM()->mkNode(kind::MINUS
, left
, right
));
4950 success
= decomposeTerm(diff
, dm
, dp
, dc
);
4953 // reduce the kind of the to not include literals
4958 Kind atomKind
= atom
.getKind();
4959 Kind normKind
= negated
? negateKind(atomKind
) : atomKind
;
4961 if(normKind
== GEQ
|| normKind
== GT
){
4963 normKind
= (normKind
== GEQ
) ? LEQ
: LT
;
4968 Debug("arith::decomp") << "arith::decomp "
4969 << lit
<< "(" << normKind
<< "*" << dir
<< ")"<< endl
4970 << " left:" << lc
<< " + " << lm
<< "*(" << lp
<< ") : " <<left
<< endl
4971 << " right:" << rc
<< " + " << rm
<< "*(" << rp
<< ") : " << right
<< endl
4972 << " diff: " << dc
<< " + " << dm
<< "*("<< dp
<<"): " << diff
<< endl
4973 << " sep: " << sep
<< endl
;
4976 // k in LT, LEQ, EQUAL, DISEQUAL
4977 // [dir*lm*( lp ) + dir*lc] k [dir*rm*( rp ) + dir*rc]
4978 Rational change
= rc
- lc
;
4979 Assert(change
== (-dc
));
4980 // [dir*lm*( lp )] k [dir*rm*( rp ) + dir*(rc - lc)]
4982 sep
= DeltaRational(change
, Rational(-1));
4985 sep
= DeltaRational(change
);
4988 // k in LEQ, EQUAL, DISEQUAL
4989 // dir*lm*( lp ) k [dir*rm*( rp )] + dir*(sep + d * delta)
4995 * tp is a polynomial not containing an ite.
4996 * either tp is constant or contains no constants.
4998 * if tmp.first is not null, then
4999 * sgn * tp <= sgn * tmp.second
5001 void TheoryArithPrivate::entailmentCheckBoundLookup(std::pair
<Node
, DeltaRational
>& tmp
, int sgn
, TNode tp
) const {
5002 tmp
.first
= Node::null();
5003 if(sgn
== 0){ return; }
5005 Assert(Polynomial::isMember(tp
));
5006 if(tp
.getKind() == CONST_RATIONAL
){
5007 tmp
.first
= mkBoolNode(true);
5008 tmp
.second
= DeltaRational(tp
.getConst
<Rational
>());
5009 }else if(d_partialModel
.hasArithVar(tp
)){
5010 Assert(tp
.getKind() != CONST_RATIONAL
);
5011 ArithVar v
= d_partialModel
.asArithVar(tp
);
5012 Assert(v
!= ARITHVAR_SENTINEL
);
5013 ConstraintP c
= (sgn
> 0)
5014 ? d_partialModel
.getUpperBoundConstraint(v
)
5015 : d_partialModel
.getLowerBoundConstraint(v
);
5016 if(c
!= NullConstraint
){
5017 tmp
.first
= Constraint::externalExplainByAssertions({c
});
5018 tmp
.second
= c
->getValue();
5023 void TheoryArithPrivate::entailmentCheckRowSum(std::pair
<Node
, DeltaRational
>& tmp
, int sgn
, TNode tp
) const {
5024 tmp
.first
= Node::null();
5025 if(sgn
== 0){ return; }
5026 if(tp
.getKind() != PLUS
){ return; }
5027 Assert(Polynomial::isMember(tp
));
5029 tmp
.second
= DeltaRational(0);
5030 NodeBuilder
nb(kind::AND
);
5032 Polynomial p
= Polynomial::parsePolynomial(tp
);
5033 for(Polynomial::iterator i
= p
.begin(), iend
= p
.end(); i
!= iend
; ++i
) {
5035 Node x
= m
.getVarList().getNode();
5036 if(d_partialModel
.hasArithVar(x
)){
5037 ArithVar v
= d_partialModel
.asArithVar(x
);
5038 const Rational
& coeff
= m
.getConstant().getValue();
5039 int dir
= sgn
* coeff
.sgn();
5040 ConstraintP c
= (dir
> 0)
5041 ? d_partialModel
.getUpperBoundConstraint(v
)
5042 : d_partialModel
.getLowerBoundConstraint(v
);
5043 if(c
!= NullConstraint
){
5044 tmp
.second
+= c
->getValue() * coeff
;
5045 c
->externalExplainByAssertions(nb
);
5059 ArithProofRuleChecker
* TheoryArithPrivate::getProofChecker()
5064 } // namespace arith
5065 } // namespace theory