1 /********************* */
2 /*! \file partial_model.cpp
4 ** Top contributors (to current version):
6 ** This file is part of the CVC4 project.
7 ** Copyright (c) 2009-2021 by the authors listed in the file AUTHORS
8 ** in the top-level source directory and their institutional affiliations.
9 ** All rights reserved. See the file COPYING in the top-level source
10 ** directory for licensing information.\endverbatim
12 ** \brief [[ Add one-line brief description here ]]
14 ** [[ Add lengthier description here ]]
15 ** \todo document this file
18 #include "base/output.h"
19 #include "theory/arith/constraint.h"
20 #include "theory/arith/normal_form.h"
21 #include "theory/arith/partial_model.h"
29 ArithVariables::ArithVariables(context::Context
* c
, DeltaComputeCallback deltaComputingFunc
)
32 d_numberOfVariables(0),
35 d_nodeToArithVarMap(),
37 d_enqueueingBoundCounts(true),
38 d_lbRevertHistory(c
, true, LowerBoundCleanUp(this)),
39 d_ubRevertHistory(c
, true, UpperBoundCleanUp(this)),
42 d_deltaComputingFunc(deltaComputingFunc
)
45 ArithVar
ArithVariables::getNumberOfVariables() const {
46 return d_numberOfVariables
;
50 bool ArithVariables::hasArithVar(TNode x
) const {
51 return d_nodeToArithVarMap
.find(x
) != d_nodeToArithVarMap
.end();
54 bool ArithVariables::hasNode(ArithVar a
) const {
55 return d_vars
.isKey(a
);
58 ArithVar
ArithVariables::asArithVar(TNode x
) const{
59 Assert(hasArithVar(x
));
60 Assert((d_nodeToArithVarMap
.find(x
))->second
<= ARITHVAR_SENTINEL
);
61 return (d_nodeToArithVarMap
.find(x
))->second
;
64 Node
ArithVariables::asNode(ArithVar a
) const{
66 return d_vars
[a
].d_node
;
69 ArithVariables::var_iterator::var_iterator()
74 ArithVariables::var_iterator::var_iterator(const VarInfoVec
* vars
, VarInfoVec::const_iterator ci
)
75 : d_vars(vars
), d_wrapped(ci
)
80 ArithVariables::var_iterator
& ArithVariables::var_iterator::operator++(){
85 bool ArithVariables::var_iterator::operator==(const ArithVariables::var_iterator
& other
) const{
86 return d_wrapped
== other
.d_wrapped
;
88 bool ArithVariables::var_iterator::operator!=(const ArithVariables::var_iterator
& other
) const{
89 return d_wrapped
!= other
.d_wrapped
;
91 ArithVar
ArithVariables::var_iterator::operator*() const{
95 void ArithVariables::var_iterator::nextInitialized(){
96 VarInfoVec::const_iterator end
= d_vars
->end();
97 while(d_wrapped
!= end
&&
98 !((*d_vars
)[*d_wrapped
].initialized())){
103 ArithVariables::var_iterator
ArithVariables::var_begin() const {
104 return var_iterator(&d_vars
, d_vars
.begin());
107 ArithVariables::var_iterator
ArithVariables::var_end() const {
108 return var_iterator(&d_vars
, d_vars
.end());
110 bool ArithVariables::isInteger(ArithVar x
) const {
111 return d_vars
[x
].d_type
>= ArithType::Integer
;
114 /** Is the assignment to x integral? */
115 bool ArithVariables::integralAssignment(ArithVar x
) const {
116 return getAssignment(x
).isIntegral();
118 bool ArithVariables::isAuxiliary(ArithVar x
) const {
119 return d_vars
[x
].d_auxiliary
;
122 bool ArithVariables::isIntegerInput(ArithVar x
) const {
123 return isInteger(x
) && !isAuxiliary(x
);
126 ArithVariables::VarInfo::VarInfo()
127 : d_var(ARITHVAR_SENTINEL
),
129 d_lb(NullConstraint
),
130 d_ub(NullConstraint
),
131 d_cmpAssignmentLB(1),
132 d_cmpAssignmentUB(-1),
134 d_type(ArithType::Unset
),
135 d_node(Node::null()),
136 d_auxiliary(false) {}
138 bool ArithVariables::VarInfo::initialized() const {
139 return d_var
!= ARITHVAR_SENTINEL
;
142 void ArithVariables::VarInfo::initialize(ArithVar v
, Node n
, bool aux
){
143 Assert(!initialized());
144 Assert(d_lb
== NullConstraint
);
145 Assert(d_ub
== NullConstraint
);
146 Assert(d_cmpAssignmentLB
> 0);
147 Assert(d_cmpAssignmentUB
< 0);
153 //The type computation is not quite accurate for Rationals that are
155 //We'll use the isIntegral check from the polynomial package instead.
156 Polynomial p
= Polynomial::parsePolynomial(n
);
157 d_type
= p
.isIntegral() ? ArithType::Integer
: ArithType::Real
;
159 d_type
= n
.getType().isInteger() ? ArithType::Integer
: ArithType::Real
;
162 Assert(initialized());
165 void ArithVariables::VarInfo::uninitialize(){
166 d_var
= ARITHVAR_SENTINEL
;
167 d_node
= Node::null();
170 bool ArithVariables::VarInfo::setAssignment(const DeltaRational
& a
, BoundsInfo
& prev
){
171 Assert(initialized());
173 int cmpUB
= (d_ub
== NullConstraint
) ? -1 :
174 d_assignment
.cmp(d_ub
->getValue());
176 int cmpLB
= (d_lb
== NullConstraint
) ? 1 :
177 d_assignment
.cmp(d_lb
->getValue());
179 bool lbChanged
= cmpLB
!= d_cmpAssignmentLB
&&
180 (cmpLB
== 0 || d_cmpAssignmentLB
== 0);
181 bool ubChanged
= cmpUB
!= d_cmpAssignmentUB
&&
182 (cmpUB
== 0 || d_cmpAssignmentUB
== 0);
184 if(lbChanged
|| ubChanged
){
188 d_cmpAssignmentUB
= cmpUB
;
189 d_cmpAssignmentLB
= cmpLB
;
190 return lbChanged
|| ubChanged
;
193 void ArithVariables::releaseArithVar(ArithVar v
){
194 VarInfo
& vi
= d_vars
.get(v
);
196 size_t removed CVC4_UNUSED
= d_nodeToArithVarMap
.erase(vi
.d_node
);
197 Assert(removed
== 1);
201 if(d_safeAssignment
.isKey(v
)){
202 d_safeAssignment
.remove(v
);
204 if(vi
.canBeReclaimed()){
207 d_released
.push_back(v
);
211 bool ArithVariables::VarInfo::setUpperBound(ConstraintP ub
, BoundsInfo
& prev
){
212 Assert(initialized());
213 bool wasNull
= d_ub
== NullConstraint
;
214 bool isNull
= ub
== NullConstraint
;
216 int cmpUB
= isNull
? -1 : d_assignment
.cmp(ub
->getValue());
217 bool ubChanged
= (wasNull
!= isNull
) ||
218 (cmpUB
!= d_cmpAssignmentUB
&& (cmpUB
== 0 || d_cmpAssignmentUB
== 0));
223 d_cmpAssignmentUB
= cmpUB
;
227 bool ArithVariables::VarInfo::setLowerBound(ConstraintP lb
, BoundsInfo
& prev
){
228 Assert(initialized());
229 bool wasNull
= d_lb
== NullConstraint
;
230 bool isNull
= lb
== NullConstraint
;
232 int cmpLB
= isNull
? 1 : d_assignment
.cmp(lb
->getValue());
234 bool lbChanged
= (wasNull
!= isNull
) ||
235 (cmpLB
!= d_cmpAssignmentLB
&& (cmpLB
== 0 || d_cmpAssignmentLB
== 0));
240 d_cmpAssignmentLB
= cmpLB
;
244 BoundCounts
ArithVariables::VarInfo::atBoundCounts() const {
245 uint32_t lbIndc
= (d_cmpAssignmentLB
== 0) ? 1 : 0;
246 uint32_t ubIndc
= (d_cmpAssignmentUB
== 0) ? 1 : 0;
247 return BoundCounts(lbIndc
, ubIndc
);
250 BoundCounts
ArithVariables::VarInfo::hasBoundCounts() const {
251 uint32_t lbIndc
= (d_lb
!= NullConstraint
) ? 1 : 0;
252 uint32_t ubIndc
= (d_ub
!= NullConstraint
) ? 1 : 0;
253 return BoundCounts(lbIndc
, ubIndc
);
256 BoundsInfo
ArithVariables::VarInfo::boundsInfo() const{
257 return BoundsInfo(atBoundCounts(), hasBoundCounts());
260 bool ArithVariables::VarInfo::canBeReclaimed() const{
261 return d_pushCount
== 0;
264 bool ArithVariables::canBeReleased(ArithVar v
) const{
265 return d_vars
[v
].canBeReclaimed();
268 void ArithVariables::attemptToReclaimReleased(){
269 size_t readPos
= 0, writePos
= 0, N
= d_released
.size();
270 for(; readPos
< N
; ++readPos
){
271 ArithVar v
= d_released
[readPos
];
272 if(canBeReleased(v
)){
275 d_released
[writePos
] = v
;
279 d_released
.resize(writePos
);
282 ArithVar
ArithVariables::allocateVariable(){
284 attemptToReclaimReleased();
286 bool reclaim
= !d_pool
.empty();
290 varX
= d_pool
.back();
293 varX
= d_numberOfVariables
;
294 ++d_numberOfVariables
;
296 d_vars
.set(varX
, VarInfo());
301 const Rational
& ArithVariables::getDelta(){
303 Rational nextDelta
= d_deltaComputingFunc();
306 Assert(d_deltaIsSafe
);
310 bool ArithVariables::boundsAreEqual(ArithVar x
) const{
311 if(hasLowerBound(x
) && hasUpperBound(x
)){
312 return getUpperBound(x
) == getLowerBound(x
);
319 std::pair
<ConstraintP
, ConstraintP
> ArithVariables::explainEqualBounds(ArithVar x
) const{
320 Assert(boundsAreEqual(x
));
322 ConstraintP lb
= getLowerBoundConstraint(x
);
323 ConstraintP ub
= getUpperBoundConstraint(x
);
324 if(lb
->isEquality()){
325 return make_pair(lb
, NullConstraint
);
326 }else if(ub
->isEquality()){
327 return make_pair(ub
, NullConstraint
);
329 return make_pair(lb
, ub
);
333 void ArithVariables::setAssignment(ArithVar x
, const DeltaRational
& r
){
334 Debug("partial_model") << "pm: updating the assignment to" << x
335 << " now " << r
<<endl
;
336 VarInfo
& vi
= d_vars
.get(x
);
337 if(!d_safeAssignment
.isKey(x
)){
338 d_safeAssignment
.set(x
, vi
.d_assignment
);
343 if(vi
.setAssignment(r
, prev
)){
344 addToBoundQueue(x
, prev
);
348 void ArithVariables::setAssignment(ArithVar x
, const DeltaRational
& safe
, const DeltaRational
& r
){
349 Debug("partial_model") << "pm: updating the assignment to" << x
350 << " now " << r
<<endl
;
352 if(d_safeAssignment
.isKey(x
)){
353 d_safeAssignment
.remove(x
);
356 d_safeAssignment
.set(x
, safe
);
360 VarInfo
& vi
= d_vars
.get(x
);
362 if(vi
.setAssignment(r
, prev
)){
363 addToBoundQueue(x
, prev
);
367 void ArithVariables::initialize(ArithVar x
, Node n
, bool aux
){
368 VarInfo
& vi
= d_vars
.get(x
);
369 vi
.initialize(x
, n
, aux
);
370 d_nodeToArithVarMap
[n
] = x
;
373 ArithVar
ArithVariables::allocate(Node n
, bool aux
){
374 ArithVar v
= allocateVariable();
375 initialize(v
, n
, aux
);
379 // void ArithVariables::initialize(ArithVar x, const DeltaRational& r){
380 // Assert(x == d_mapSize);
381 // Assert(equalSizes());
384 // // Is worth mentioning that this is not strictly necessary, but this maintains the internal invariant
385 // // that when d_assignment is set this gets set.
386 // invalidateDelta();
387 // d_assignment.push_back( r );
389 // d_boundRel.push_back(BetweenBounds);
391 // d_ubc.push_back(NullConstraint);
392 // d_lbc.push_back(NullConstraint);
395 /** Must know that the bound exists both calling this! */
396 const DeltaRational
& ArithVariables::getUpperBound(ArithVar x
) const {
398 Assert(hasUpperBound(x
));
400 return getUpperBoundConstraint(x
)->getValue();
403 const DeltaRational
& ArithVariables::getLowerBound(ArithVar x
) const {
405 Assert(hasLowerBound(x
));
407 return getLowerBoundConstraint(x
)->getValue();
410 const DeltaRational
& ArithVariables::getSafeAssignment(ArithVar x
) const{
412 if(d_safeAssignment
.isKey(x
)){
413 return d_safeAssignment
[x
];
415 return d_vars
[x
].d_assignment
;
419 const DeltaRational
& ArithVariables::getAssignment(ArithVar x
, bool safe
) const{
421 if(safe
&& d_safeAssignment
.isKey(x
)){
422 return d_safeAssignment
[x
];
424 return d_vars
[x
].d_assignment
;
428 const DeltaRational
& ArithVariables::getAssignment(ArithVar x
) const{
430 return d_vars
[x
].d_assignment
;
434 void ArithVariables::setLowerBoundConstraint(ConstraintP c
){
435 AssertArgument(c
!= NullConstraint
, "Cannot set a lower bound to NullConstraint.");
436 AssertArgument(c
->isEquality() || c
->isLowerBound(),
437 "Constraint type must be set to an equality or UpperBound.");
438 ArithVar x
= c
->getVariable();
439 Debug("partial_model") << "setLowerBoundConstraint(" << x
<< ":" << c
<< ")" << endl
;
441 Assert(greaterThanLowerBound(x
, c
->getValue()));
444 VarInfo
& vi
= d_vars
.get(x
);
447 if(vi
.setLowerBound(c
, prev
)){
448 addToBoundQueue(x
, prev
);
452 void ArithVariables::setUpperBoundConstraint(ConstraintP c
){
453 AssertArgument(c
!= NullConstraint
, "Cannot set a upper bound to NullConstraint.");
454 AssertArgument(c
->isEquality() || c
->isUpperBound(),
455 "Constraint type must be set to an equality or UpperBound.");
457 ArithVar x
= c
->getVariable();
458 Debug("partial_model") << "setUpperBoundConstraint(" << x
<< ":" << c
<< ")" << endl
;
460 Assert(lessThanUpperBound(x
, c
->getValue()));
463 VarInfo
& vi
= d_vars
.get(x
);
466 if(vi
.setUpperBound(c
, prev
)){
467 addToBoundQueue(x
, prev
);
471 int ArithVariables::cmpToLowerBound(ArithVar x
, const DeltaRational
& c
) const{
472 if(!hasLowerBound(x
)){
474 // ? c < -\infty |- _|_
477 return c
.cmp(getLowerBound(x
));
481 int ArithVariables::cmpToUpperBound(ArithVar x
, const DeltaRational
& c
) const{
482 if(!hasUpperBound(x
)){
484 // ? c > \infty |- _|_
487 return c
.cmp(getUpperBound(x
));
491 bool ArithVariables::equalsLowerBound(ArithVar x
, const DeltaRational
& c
){
492 if(!hasLowerBound(x
)){
495 return c
== getLowerBound(x
);
498 bool ArithVariables::equalsUpperBound(ArithVar x
, const DeltaRational
& c
){
499 if(!hasUpperBound(x
)){
502 return c
== getUpperBound(x
);
506 bool ArithVariables::hasEitherBound(ArithVar x
) const{
507 return hasLowerBound(x
) || hasUpperBound(x
);
510 bool ArithVariables::strictlyBelowUpperBound(ArithVar x
) const{
511 return d_vars
[x
].d_cmpAssignmentUB
< 0;
514 bool ArithVariables::strictlyAboveLowerBound(ArithVar x
) const{
515 return d_vars
[x
].d_cmpAssignmentLB
> 0;
518 bool ArithVariables::assignmentIsConsistent(ArithVar x
) const{
520 d_vars
[x
].d_cmpAssignmentLB
>= 0 &&
521 d_vars
[x
].d_cmpAssignmentUB
<= 0;
525 void ArithVariables::clearSafeAssignments(bool revert
){
527 if(revert
&& !d_safeAssignment
.empty()){
531 while(!d_safeAssignment
.empty()){
532 ArithVar atBack
= d_safeAssignment
.back();
534 VarInfo
& vi
= d_vars
.get(atBack
);
536 if(vi
.setAssignment(d_safeAssignment
[atBack
], prev
)){
537 addToBoundQueue(atBack
, prev
);
540 d_safeAssignment
.pop_back();
544 void ArithVariables::revertAssignmentChanges(){
545 clearSafeAssignments(true);
547 void ArithVariables::commitAssignmentChanges(){
548 clearSafeAssignments(false);
551 bool ArithVariables::lowerBoundIsZero(ArithVar x
){
552 return hasLowerBound(x
) && getLowerBound(x
).sgn() == 0;
555 bool ArithVariables::upperBoundIsZero(ArithVar x
){
556 return hasUpperBound(x
) && getUpperBound(x
).sgn() == 0;
559 void ArithVariables::printEntireModel(std::ostream
& out
) const{
560 out
<< "---Printing Model ---" << std::endl
;
561 for(var_iterator i
= var_begin(), iend
= var_end(); i
!= iend
; ++i
){
564 out
<< "---Done Model ---" << std::endl
;
567 void ArithVariables::printModel(ArithVar x
, std::ostream
& out
) const{
568 out
<< "model" << x
<< ": "
570 << getAssignment(x
) << " ";
571 if(!hasLowerBound(x
)){
574 out
<< getLowerBound(x
) << " ";
575 out
<< getLowerBoundConstraint(x
) << " ";
577 if(!hasUpperBound(x
)){
580 out
<< getUpperBound(x
) << " ";
581 out
<< getUpperBoundConstraint(x
) << " ";
584 if(isInteger(x
) && !integralAssignment(x
)){
585 out
<< "(not an integer)" << endl
;
590 void ArithVariables::printModel(ArithVar x
) const{
591 printModel(x
, Debug("model"));
594 void ArithVariables::pushUpperBound(VarInfo
& vi
){
596 d_ubRevertHistory
.push_back(make_pair(vi
.d_var
, vi
.d_ub
));
598 void ArithVariables::pushLowerBound(VarInfo
& vi
){
600 d_lbRevertHistory
.push_back(make_pair(vi
.d_var
, vi
.d_lb
));
603 void ArithVariables::popUpperBound(AVCPair
* c
){
604 ArithVar x
= c
->first
;
605 VarInfo
& vi
= d_vars
.get(x
);
607 if(vi
.setUpperBound(c
->second
, prev
)){
608 addToBoundQueue(x
, prev
);
613 void ArithVariables::popLowerBound(AVCPair
* c
){
614 ArithVar x
= c
->first
;
615 VarInfo
& vi
= d_vars
.get(x
);
617 if(vi
.setLowerBound(c
->second
, prev
)){
618 addToBoundQueue(x
, prev
);
623 void ArithVariables::addToBoundQueue(ArithVar v
, const BoundsInfo
& prev
){
624 if(d_enqueueingBoundCounts
&& !d_boundsQueue
.isKey(v
)){
625 d_boundsQueue
.set(v
, prev
);
629 BoundsInfo
ArithVariables::selectBoundsInfo(ArithVar v
, bool old
) const {
630 if(old
&& d_boundsQueue
.isKey(v
)){
631 return d_boundsQueue
[v
];
633 return boundsInfo(v
);
637 bool ArithVariables::boundsQueueEmpty() const {
638 return d_boundsQueue
.empty();
641 void ArithVariables::processBoundsQueue(BoundUpdateCallback
& changed
){
642 while(!boundsQueueEmpty()){
643 ArithVar v
= d_boundsQueue
.back();
644 BoundsInfo prev
= d_boundsQueue
[v
];
645 d_boundsQueue
.pop_back();
646 BoundsInfo curr
= boundsInfo(v
);
653 void ArithVariables::invalidateDelta() {
654 d_deltaIsSafe
= false;
657 void ArithVariables::setDelta(const Rational
& d
){
659 d_deltaIsSafe
= true;
662 void ArithVariables::startQueueingBoundCounts(){
663 d_enqueueingBoundCounts
= true;
665 void ArithVariables::stopQueueingBoundCounts(){
666 d_enqueueingBoundCounts
= false;
669 bool ArithVariables::inMaps(ArithVar x
) const{
670 return x
< getNumberOfVariables();
673 ArithVariables::LowerBoundCleanUp::LowerBoundCleanUp(ArithVariables
* pm
)
676 void ArithVariables::LowerBoundCleanUp::operator()(AVCPair
* p
){
677 d_pm
->popLowerBound(p
);
680 ArithVariables::UpperBoundCleanUp::UpperBoundCleanUp(ArithVariables
* pm
)
683 void ArithVariables::UpperBoundCleanUp::operator()(AVCPair
* p
){
684 d_pm
->popUpperBound(p
);
688 } // namespace theory