summaryrefslogtreecommitdiff
path: root/src/theory/arith/simplex-converge.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/theory/arith/simplex-converge.cpp')
-rw-r--r--src/theory/arith/simplex-converge.cpp1674
1 files changed, 1674 insertions, 0 deletions
diff --git a/src/theory/arith/simplex-converge.cpp b/src/theory/arith/simplex-converge.cpp
new file mode 100644
index 000000000..decce3882
--- /dev/null
+++ b/src/theory/arith/simplex-converge.cpp
@@ -0,0 +1,1674 @@
+/********************* */
+/*! \file simplex.cpp
+ ** \verbatim
+ ** Original author: taking
+ ** Major contributors: none
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys)
+ ** Courant Institute of Mathematical Sciences
+ ** New York University
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief [[ Add one-line brief description here ]]
+ **
+ ** [[ Add lengthier description here ]]
+ ** \todo document this file
+ **/
+
+
+#include "theory/arith/simplex.h"
+#include "theory/arith/options.h"
+
+using namespace std;
+
+using namespace CVC4;
+using namespace CVC4::kind;
+
+using namespace CVC4::theory;
+using namespace CVC4::theory::arith;
+
+static const bool CHECK_AFTER_PIVOT = true;
+
+SimplexDecisionProcedure::SimplexDecisionProcedure(LinearEqualityModule& linEq, NodeCallBack& conflictChannel, ArithVarMalloc& malloc, ConstraintDatabase& cd) :
+ d_conflictVariable(ARITHVAR_SENTINEL),
+ d_linEq(linEq),
+ d_partialModel(d_linEq.getPartialModel()),
+ d_tableau(d_linEq.getTableau()),
+ d_queue(d_partialModel, d_tableau),
+ d_numVariables(0),
+ d_conflictChannel(conflictChannel),
+ d_pivotsInRound(),
+ d_DELTA_ZERO(0,0),
+ d_arithVarMalloc(malloc),
+ d_constraintDatabase(cd),
+ d_optRow(ARITHVAR_SENTINEL),
+ d_negOptConstant(d_DELTA_ZERO)
+{
+ switch(ArithHeuristicPivotRule rule = options::arithHeuristicPivotRule()) {
+ case MINIMUM:
+ d_queue.setPivotRule(ArithPriorityQueue::MINIMUM);
+ break;
+ case BREAK_TIES:
+ d_queue.setPivotRule(ArithPriorityQueue::BREAK_TIES);
+ break;
+ case MAXIMUM:
+ d_queue.setPivotRule(ArithPriorityQueue::MAXIMUM);
+ break;
+ default:
+ Unhandled(rule);
+ }
+
+ srand(62047);
+}
+
+SimplexDecisionProcedure::Statistics::Statistics():
+ d_statUpdateConflicts("theory::arith::UpdateConflicts", 0),
+ d_findConflictOnTheQueueTime("theory::arith::findConflictOnTheQueueTime"),
+ d_attemptBeforeDiffSearch("theory::arith::qi::BeforeDiffSearch::attempt",0),
+ d_successBeforeDiffSearch("theory::arith::qi::BeforeDiffSearch::success",0),
+ d_attemptAfterDiffSearch("theory::arith::qi::AfterDiffSearch::attempt",0),
+ d_successAfterDiffSearch("theory::arith::qi::AfterDiffSearch::success",0),
+ d_attemptDuringDiffSearch("theory::arith::qi::DuringDiffSearch::attempt",0),
+ d_successDuringDiffSearch("theory::arith::qi::DuringDiffSearch::success",0),
+ d_attemptDuringVarOrderSearch("theory::arith::qi::DuringVarOrderSearch::attempt",0),
+ d_successDuringVarOrderSearch("theory::arith::qi::DuringVarOrderSearch::success",0),
+ d_attemptAfterVarOrderSearch("theory::arith::qi::AfterVarOrderSearch::attempt",0),
+ d_successAfterVarOrderSearch("theory::arith::qi::AfterVarOrderSearch::success",0),
+ d_weakeningAttempts("theory::arith::weakening::attempts",0),
+ d_weakeningSuccesses("theory::arith::weakening::success",0),
+ d_weakenings("theory::arith::weakening::total",0),
+ d_weakenTime("theory::arith::weakening::time"),
+ d_simplexConflicts("theory::arith::simplexConflicts",0),
+ // primal
+ d_primalTimer("theory::arith::primal::overall::timer"),
+ d_internalTimer("theory::arith::primal::internal::timer"),
+ d_primalCalls("theory::arith::primal::calls",0),
+ d_primalSatCalls("theory::arith::primal::calls::sat",0),
+ d_primalUnsatCalls("theory::arith::primal::calls::unsat",0),
+ d_primalPivots("theory::arith::primal::pivots",0),
+ d_primalImprovingPivots("theory::arith::primal::pivots::improving",0),
+ d_primalThresholdReachedPivot("theory::arith::primal::thresholds",0),
+ d_primalThresholdReachedPivot_dropped("theory::arith::primal::thresholds::dropped",0),
+ d_primalReachedMaxPivots("theory::arith::primal::maxpivots",0),
+ d_primalReachedMaxPivots_contractMadeProgress("theory::arith::primal::maxpivots::contract",0),
+ d_primalReachedMaxPivots_checkForConflictWorked("theory::arith::primal::maxpivots::checkworked",0),
+ d_primalGlobalMinimum("theory::arith::primal::minimum",0),
+ d_primalGlobalMinimum_rowConflictWorked("theory::arith::primal::minimum::checkworked",0),
+ d_primalGlobalMinimum_firstHalfWasSat("theory::arith::primal::minimum::firsthalf::sat",0),
+ d_primalGlobalMinimum_firstHalfWasUnsat("theory::arith::primal::minimum::firsthalf::unsat",0),
+ d_primalGlobalMinimum_contractMadeProgress("theory::arith::primal::minimum::progress",0),
+ d_unboundedFound("theory::arith::primal::unbounded",0),
+ d_unboundedFound_drive("theory::arith::primal::unbounded::drive",0),
+ d_unboundedFound_dropped("theory::arith::primal::unbounded::dropped",0)
+{
+ StatisticsRegistry::registerStat(&d_statUpdateConflicts);
+
+ StatisticsRegistry::registerStat(&d_findConflictOnTheQueueTime);
+
+ StatisticsRegistry::registerStat(&d_attemptBeforeDiffSearch);
+ StatisticsRegistry::registerStat(&d_successBeforeDiffSearch);
+ StatisticsRegistry::registerStat(&d_attemptAfterDiffSearch);
+ StatisticsRegistry::registerStat(&d_successAfterDiffSearch);
+ StatisticsRegistry::registerStat(&d_attemptDuringDiffSearch);
+ StatisticsRegistry::registerStat(&d_successDuringDiffSearch);
+ StatisticsRegistry::registerStat(&d_attemptDuringVarOrderSearch);
+ StatisticsRegistry::registerStat(&d_successDuringVarOrderSearch);
+ StatisticsRegistry::registerStat(&d_attemptAfterVarOrderSearch);
+ StatisticsRegistry::registerStat(&d_successAfterVarOrderSearch);
+
+ StatisticsRegistry::registerStat(&d_weakeningAttempts);
+ StatisticsRegistry::registerStat(&d_weakeningSuccesses);
+ StatisticsRegistry::registerStat(&d_weakenings);
+ StatisticsRegistry::registerStat(&d_weakenTime);
+
+ StatisticsRegistry::registerStat(&d_simplexConflicts);
+
+ //primal
+ StatisticsRegistry::registerStat(&d_primalTimer);
+ StatisticsRegistry::registerStat(&d_internalTimer);
+
+ StatisticsRegistry::registerStat(&d_primalCalls);
+ StatisticsRegistry::registerStat(&d_primalSatCalls);
+ StatisticsRegistry::registerStat(&d_primalUnsatCalls);
+
+ StatisticsRegistry::registerStat(&d_primalPivots);
+ StatisticsRegistry::registerStat(&d_primalImprovingPivots);
+
+ StatisticsRegistry::registerStat(&d_primalThresholdReachedPivot);
+ StatisticsRegistry::registerStat(&d_primalThresholdReachedPivot_dropped);
+
+ StatisticsRegistry::registerStat(&d_primalReachedMaxPivots);
+ StatisticsRegistry::registerStat(&d_primalReachedMaxPivots_contractMadeProgress);
+ StatisticsRegistry::registerStat(&d_primalReachedMaxPivots_checkForConflictWorked);
+
+
+ StatisticsRegistry::registerStat(&d_primalGlobalMinimum);
+ StatisticsRegistry::registerStat(&d_primalGlobalMinimum_rowConflictWorked);
+ StatisticsRegistry::registerStat(&d_primalGlobalMinimum_firstHalfWasSat);
+ StatisticsRegistry::registerStat(&d_primalGlobalMinimum_firstHalfWasUnsat);
+ StatisticsRegistry::registerStat(&d_primalGlobalMinimum_contractMadeProgress);
+
+
+ StatisticsRegistry::registerStat(&d_unboundedFound);
+ StatisticsRegistry::registerStat(&d_unboundedFound_drive);
+ StatisticsRegistry::registerStat(&d_unboundedFound_dropped);
+
+}
+
+SimplexDecisionProcedure::Statistics::~Statistics(){
+ StatisticsRegistry::unregisterStat(&d_statUpdateConflicts);
+
+ StatisticsRegistry::unregisterStat(&d_findConflictOnTheQueueTime);
+
+ StatisticsRegistry::unregisterStat(&d_attemptBeforeDiffSearch);
+ StatisticsRegistry::unregisterStat(&d_successBeforeDiffSearch);
+ StatisticsRegistry::unregisterStat(&d_attemptAfterDiffSearch);
+ StatisticsRegistry::unregisterStat(&d_successAfterDiffSearch);
+ StatisticsRegistry::unregisterStat(&d_attemptDuringDiffSearch);
+ StatisticsRegistry::unregisterStat(&d_successDuringDiffSearch);
+ StatisticsRegistry::unregisterStat(&d_attemptDuringVarOrderSearch);
+ StatisticsRegistry::unregisterStat(&d_successDuringVarOrderSearch);
+ StatisticsRegistry::unregisterStat(&d_attemptAfterVarOrderSearch);
+ StatisticsRegistry::unregisterStat(&d_successAfterVarOrderSearch);
+
+ StatisticsRegistry::unregisterStat(&d_weakeningAttempts);
+ StatisticsRegistry::unregisterStat(&d_weakeningSuccesses);
+ StatisticsRegistry::unregisterStat(&d_weakenings);
+ StatisticsRegistry::unregisterStat(&d_weakenTime);
+
+ StatisticsRegistry::unregisterStat(&d_simplexConflicts);
+
+ //primal
+ StatisticsRegistry::unregisterStat(&d_primalTimer);
+ StatisticsRegistry::unregisterStat(&d_internalTimer);
+
+ StatisticsRegistry::unregisterStat(&d_primalCalls);
+ StatisticsRegistry::unregisterStat(&d_primalSatCalls);
+ StatisticsRegistry::unregisterStat(&d_primalUnsatCalls);
+
+ StatisticsRegistry::unregisterStat(&d_primalPivots);
+ StatisticsRegistry::unregisterStat(&d_primalImprovingPivots);
+
+ StatisticsRegistry::unregisterStat(&d_primalThresholdReachedPivot);
+ StatisticsRegistry::unregisterStat(&d_primalThresholdReachedPivot_dropped);
+
+ StatisticsRegistry::unregisterStat(&d_primalReachedMaxPivots);
+ StatisticsRegistry::unregisterStat(&d_primalReachedMaxPivots_contractMadeProgress);
+ StatisticsRegistry::unregisterStat(&d_primalReachedMaxPivots_checkForConflictWorked);
+
+
+ StatisticsRegistry::unregisterStat(&d_primalGlobalMinimum);
+ StatisticsRegistry::unregisterStat(&d_primalGlobalMinimum_rowConflictWorked);
+ StatisticsRegistry::unregisterStat(&d_primalGlobalMinimum_firstHalfWasSat);
+ StatisticsRegistry::unregisterStat(&d_primalGlobalMinimum_firstHalfWasUnsat);
+ StatisticsRegistry::unregisterStat(&d_primalGlobalMinimum_contractMadeProgress);
+
+ StatisticsRegistry::unregisterStat(&d_unboundedFound);
+ StatisticsRegistry::unregisterStat(&d_unboundedFound_drive);
+ StatisticsRegistry::unregisterStat(&d_unboundedFound_dropped);
+}
+
+
+
+
+ArithVar SimplexDecisionProcedure::minVarOrder(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y){
+ Assert(x != ARITHVAR_SENTINEL);
+ Assert(y != ARITHVAR_SENTINEL);
+ // Assert(!simp.d_tableau.isBasic(x));
+ // Assert(!simp.d_tableau.isBasic(y));
+ if(x <= y){
+ return x;
+ } else {
+ return y;
+ }
+}
+
+ArithVar SimplexDecisionProcedure::minColLength(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y){
+ Assert(x != ARITHVAR_SENTINEL);
+ Assert(y != ARITHVAR_SENTINEL);
+ Assert(!simp.d_tableau.isBasic(x));
+ Assert(!simp.d_tableau.isBasic(y));
+ uint32_t xLen = simp.d_tableau.getColLength(x);
+ uint32_t yLen = simp.d_tableau.getColLength(y);
+ if( xLen > yLen){
+ return y;
+ } else if( xLen== yLen ){
+ return minVarOrder(simp,x,y);
+ }else{
+ return x;
+ }
+}
+
+ArithVar SimplexDecisionProcedure::minRowLength(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y){
+ Assert(x != ARITHVAR_SENTINEL);
+ Assert(y != ARITHVAR_SENTINEL);
+ Assert(simp.d_tableau.isBasic(x));
+ Assert(simp.d_tableau.isBasic(y));
+ uint32_t xLen = simp.d_tableau.getRowLength(simp.d_tableau.basicToRowIndex(x));
+ uint32_t yLen = simp.d_tableau.getRowLength(simp.d_tableau.basicToRowIndex(y));
+ if( xLen > yLen){
+ return y;
+ } else if( xLen == yLen ){
+ return minVarOrder(simp,x,y);
+ }else{
+ return x;
+ }
+}
+ArithVar SimplexDecisionProcedure::minBoundAndRowCount(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y){
+ Assert(x != ARITHVAR_SENTINEL);
+ Assert(y != ARITHVAR_SENTINEL);
+ Assert(!simp.d_tableau.isBasic(x));
+ Assert(!simp.d_tableau.isBasic(y));
+ if(simp.d_partialModel.hasEitherBound(x) && !simp.d_partialModel.hasEitherBound(y)){
+ return y;
+ }else if(!simp.d_partialModel.hasEitherBound(x) && simp.d_partialModel.hasEitherBound(y)){
+ return x;
+ }else {
+ return minColLength(simp, x, y);
+ }
+}
+
+template <bool above>
+ArithVar SimplexDecisionProcedure::selectSlack(ArithVar x_i, SimplexDecisionProcedure::PreferenceFunction pref){
+ ArithVar slack = ARITHVAR_SENTINEL;
+
+ for(Tableau::RowIterator iter = d_tableau.basicRowIterator(x_i); !iter.atEnd(); ++iter){
+ const Tableau::Entry& entry = *iter;
+ ArithVar nonbasic = entry.getColVar();
+ if(nonbasic == x_i) continue;
+
+ const Rational& a_ij = entry.getCoefficient();
+ int sgn = a_ij.sgn();
+ if(isAcceptableSlack<above>(sgn, nonbasic)){
+ //If one of the above conditions is met, we have found an acceptable
+ //nonbasic variable to pivot x_i with. We can now choose which one we
+ //prefer the most.
+ slack = (slack == ARITHVAR_SENTINEL) ? nonbasic : pref(*this, slack, nonbasic);
+ }
+ }
+
+ return slack;
+}
+
+Node betterConflict(TNode x, TNode y){
+ if(x.isNull()) return y;
+ else if(y.isNull()) return x;
+ else if(x.getNumChildren() <= y.getNumChildren()) return x;
+ else return y;
+}
+
+bool SimplexDecisionProcedure::findConflictOnTheQueue(SearchPeriod type) {
+ TimerStat::CodeTimer codeTimer(d_statistics.d_findConflictOnTheQueueTime);
+ Assert(d_successes.empty());
+
+ switch(type){
+ case BeforeDiffSearch: ++(d_statistics.d_attemptBeforeDiffSearch); break;
+ case DuringDiffSearch: ++(d_statistics.d_attemptDuringDiffSearch); break;
+ case AfterDiffSearch: ++(d_statistics.d_attemptAfterDiffSearch); break;
+ case DuringVarOrderSearch: ++(d_statistics.d_attemptDuringVarOrderSearch); break;
+ case AfterVarOrderSearch: ++(d_statistics.d_attemptAfterVarOrderSearch); break;
+ }
+
+ ArithPriorityQueue::const_iterator i = d_queue.begin();
+ ArithPriorityQueue::const_iterator end = d_queue.end();
+ for(; i != end; ++i){
+ ArithVar x_i = *i;
+
+ if(x_i != d_conflictVariable && d_tableau.isBasic(x_i) && !d_successes.isMember(x_i)){
+ Node possibleConflict = checkBasicForConflict(x_i);
+ if(!possibleConflict.isNull()){
+ d_successes.add(x_i);
+ reportConflict(possibleConflict);
+ }
+ }
+ }
+ if(!d_successes.empty()){
+ switch(type){
+ case BeforeDiffSearch: ++(d_statistics.d_successBeforeDiffSearch); break;
+ case DuringDiffSearch: ++(d_statistics.d_successDuringDiffSearch); break;
+ case AfterDiffSearch: ++(d_statistics.d_successAfterDiffSearch); break;
+ case DuringVarOrderSearch: ++(d_statistics.d_successDuringVarOrderSearch); break;
+ case AfterVarOrderSearch: ++(d_statistics.d_successAfterVarOrderSearch); break;
+ }
+ d_successes.purge();
+ return true;
+ }else{
+ return false;
+ }
+}
+
+Result::Sat SimplexDecisionProcedure::dualFindModel(bool exactResult){
+ Assert(d_conflictVariable == ARITHVAR_SENTINEL);
+ Assert(d_queue.inCollectionMode());
+
+ if(d_queue.empty()){
+ return Result::SAT;
+ }
+ static CVC4_THREADLOCAL(unsigned int) instance = 0;
+ instance = instance + 1;
+ Debug("arith::findModel") << "begin findModel()" << instance << endl;
+
+ d_queue.transitionToDifferenceMode();
+
+ Result::Sat result = Result::SAT_UNKNOWN;
+
+ if(d_queue.empty()){
+ result = Result::SAT;
+ }else if(d_queue.size() > 1){
+ if(findConflictOnTheQueue(BeforeDiffSearch)){
+ result = Result::UNSAT;
+ }
+ }
+ static const bool verbose = false;
+ exactResult |= options::arithStandardCheckVarOrderPivots() < 0;
+ const uint32_t inexactResultsVarOrderPivots = exactResult ? 0 : options::arithStandardCheckVarOrderPivots();
+
+ uint32_t checkPeriod = options::arithSimplexCheckPeriod();
+ if(result == Result::SAT_UNKNOWN){
+ uint32_t numDifferencePivots = options::arithHeuristicPivots() < 0 ?
+ d_numVariables + 1 : options::arithHeuristicPivots();
+ // The signed to unsigned conversion is safe.
+ uint32_t pivotsRemaining = numDifferencePivots;
+ while(!d_queue.empty() &&
+ result != Result::UNSAT &&
+ pivotsRemaining > 0){
+ uint32_t pivotsToDo = min(checkPeriod, pivotsRemaining);
+ pivotsRemaining -= pivotsToDo;
+ if(searchForFeasibleSolution(pivotsToDo)){
+ result = Result::UNSAT;
+ }//Once every CHECK_PERIOD examine the entire queue for conflicts
+ if(result != Result::UNSAT){
+ if(findConflictOnTheQueue(DuringDiffSearch)) { result = Result::UNSAT; }
+ }else{
+ findConflictOnTheQueue(AfterDiffSearch); // already unsat
+ }
+ }
+
+ if(verbose && numDifferencePivots > 0){
+ if(result == Result::UNSAT){
+ Message() << "diff order found unsat" << endl;
+ }else if(d_queue.empty()){
+ Message() << "diff order found model" << endl;
+ }else{
+ Message() << "diff order missed" << endl;
+ }
+ }
+ }
+
+ if(!d_queue.empty() && result != Result::UNSAT){
+ if(exactResult){
+ d_queue.transitionToVariableOrderMode();
+
+ while(!d_queue.empty() && result != Result::UNSAT){
+ if(searchForFeasibleSolution(checkPeriod)){
+ result = Result::UNSAT;
+ }
+
+ //Once every CHECK_PERIOD examine the entire queue for conflicts
+ if(result != Result::UNSAT){
+ if(findConflictOnTheQueue(DuringVarOrderSearch)){
+ result = Result::UNSAT;
+ }
+ } else{
+ findConflictOnTheQueue(AfterVarOrderSearch);
+ }
+ }
+ if(verbose){
+ if(result == Result::UNSAT){
+ Message() << "bland found unsat" << endl;
+ }else if(d_queue.empty()){
+ Message() << "bland found model" << endl;
+ }else{
+ Message() << "bland order missed" << endl;
+ }
+ }
+ }else{
+ d_queue.transitionToVariableOrderMode();
+
+ if(searchForFeasibleSolution(inexactResultsVarOrderPivots)){
+ result = Result::UNSAT;
+ findConflictOnTheQueue(AfterVarOrderSearch); // already unsat
+ }else{
+ if(findConflictOnTheQueue(AfterVarOrderSearch)) { result = Result::UNSAT; }
+ }
+
+ if(verbose){
+ if(result == Result::UNSAT){
+ Message() << "restricted var order found unsat" << endl;
+ }else if(d_queue.empty()){
+ Message() << "restricted var order found model" << endl;
+ }else{
+ Message() << "restricted var order missed" << endl;
+ }
+ }
+ }
+ }
+
+ if(result == Result::SAT_UNKNOWN && d_queue.empty()){
+ result = Result::SAT;
+ }
+
+
+
+ d_pivotsInRound.purge();
+ // ensure that the conflict variable is still in the queue.
+ if(d_conflictVariable != ARITHVAR_SENTINEL){
+ d_queue.enqueueIfInconsistent(d_conflictVariable);
+ }
+ d_conflictVariable = ARITHVAR_SENTINEL;
+
+ d_queue.transitionToCollectionMode();
+ Assert(d_queue.inCollectionMode());
+ Debug("arith::findModel") << "end findModel() " << instance << " " << result << endl;
+ return result;
+
+
+ // Assert(foundConflict || d_queue.empty());
+
+ // // Curiously the invariant that we always do a full check
+ // // means that the assignment we can always empty these queues.
+ // d_queue.clear();
+ // d_pivotsInRound.purge();
+ // d_conflictVariable = ARITHVAR_SENTINEL;
+
+ // Assert(!d_queue.inCollectionMode());
+ // d_queue.transitionToCollectionMode();
+
+
+ // Assert(d_queue.inCollectionMode());
+
+ // Debug("arith::findModel") << "end findModel() " << instance << endl;
+
+ // return foundConflict;
+}
+
+
+
+Node SimplexDecisionProcedure::checkBasicForConflict(ArithVar basic){
+
+ Assert(d_tableau.isBasic(basic));
+ const DeltaRational& beta = d_partialModel.getAssignment(basic);
+
+ if(d_partialModel.strictlyLessThanLowerBound(basic, beta)){
+ ArithVar x_j = selectSlackUpperBound(basic);
+ if(x_j == ARITHVAR_SENTINEL ){
+ return generateConflictBelowLowerBound(basic);
+ }
+ }else if(d_partialModel.strictlyGreaterThanUpperBound(basic, beta)){
+ ArithVar x_j = selectSlackLowerBound(basic);
+ if(x_j == ARITHVAR_SENTINEL ){
+ return generateConflictAboveUpperBound(basic);
+ }
+ }
+ return Node::null();
+}
+
+//corresponds to Check() in dM06
+//template <SimplexDecisionProcedure::PreferenceFunction pf>
+bool SimplexDecisionProcedure::searchForFeasibleSolution(uint32_t remainingIterations){
+ Debug("arith") << "searchForFeasibleSolution" << endl;
+ Assert(remainingIterations > 0);
+
+ while(remainingIterations > 0){
+ if(Debug.isOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); }
+
+ ArithVar x_i = d_queue.dequeueInconsistentBasicVariable();
+ Debug("arith::update::select") << "selectSmallestInconsistentVar()=" << x_i << endl;
+ if(x_i == ARITHVAR_SENTINEL){
+ Debug("arith_update") << "No inconsistent variables" << endl;
+ return false; //sat
+ }
+
+ --remainingIterations;
+
+ bool useVarOrderPivot = d_pivotsInRound.count(x_i) >= options::arithPivotThreshold();
+ if(!useVarOrderPivot){
+ d_pivotsInRound.add(x_i);
+ }
+
+
+ Debug("playground") << "pivots in rounds: " << d_pivotsInRound.count(x_i)
+ << " use " << useVarOrderPivot
+ << " threshold " << options::arithPivotThreshold()
+ << endl;
+
+ PreferenceFunction pf = useVarOrderPivot ? minVarOrder : minBoundAndRowCount;
+
+ DeltaRational beta_i = d_partialModel.getAssignment(x_i);
+ ArithVar x_j = ARITHVAR_SENTINEL;
+
+ if(d_partialModel.strictlyLessThanLowerBound(x_i, beta_i)){
+ x_j = selectSlackUpperBound(x_i, pf);
+ if(x_j == ARITHVAR_SENTINEL ){
+ ++(d_statistics.d_statUpdateConflicts);
+ Node conflict = generateConflictBelowLowerBound(x_i); //unsat
+ d_conflictVariable = x_i;
+ reportConflict(conflict);
+ return true;
+ }
+ DeltaRational l_i = d_partialModel.getLowerBound(x_i);
+ d_linEq.pivotAndUpdate(x_i, x_j, l_i);
+
+ }else if(d_partialModel.strictlyGreaterThanUpperBound(x_i, beta_i)){
+ x_j = selectSlackLowerBound(x_i, pf);
+ if(x_j == ARITHVAR_SENTINEL ){
+ ++(d_statistics.d_statUpdateConflicts);
+ Node conflict = generateConflictAboveUpperBound(x_i); //unsat
+
+ d_conflictVariable = x_i;
+ reportConflict(conflict);
+ return true;
+ }
+ DeltaRational u_i = d_partialModel.getUpperBound(x_i);
+ d_linEq.pivotAndUpdate(x_i, x_j, u_i);
+ }
+ Assert(x_j != ARITHVAR_SENTINEL);
+
+ //Check to see if we already have a conflict with x_j to prevent wasteful work
+ if(CHECK_AFTER_PIVOT){
+ Node possibleConflict = checkBasicForConflict(x_j);
+ if(!possibleConflict.isNull()){
+ d_conflictVariable = x_j;
+ reportConflict(possibleConflict);
+ return true; // unsat
+ }
+ }
+ }
+ Assert(remainingIterations == 0);
+
+ return false;
+}
+
+
+
+Constraint SimplexDecisionProcedure::weakestExplanation(bool aboveUpper, DeltaRational& surplus, ArithVar v, const Rational& coeff, bool& anyWeakening, ArithVar basic){
+
+ int sgn = coeff.sgn();
+ bool ub = aboveUpper?(sgn < 0) : (sgn > 0);
+
+ Constraint c = ub ?
+ d_partialModel.getUpperBoundConstraint(v) :
+ d_partialModel.getLowerBoundConstraint(v);
+
+// #warning "revisit"
+// Node exp = ub ?
+// d_partialModel.explainUpperBound(v) :
+// d_partialModel.explainLowerBound(v);
+
+ bool weakened;
+ do{
+ const DeltaRational& bound = c->getValue();
+
+ weakened = false;
+
+ Constraint weaker = ub?
+ c->getStrictlyWeakerUpperBound(true, true):
+ c->getStrictlyWeakerLowerBound(true, true);
+
+ // Node weaker = ub?
+ // d_propManager.strictlyWeakerAssertedUpperBound(v, bound):
+ // d_propManager.strictlyWeakerAssertedLowerBound(v, bound);
+
+ if(weaker != NullConstraint){
+ //if(!weaker.isNull()){
+ const DeltaRational& weakerBound = weaker->getValue();
+ //DeltaRational weakerBound = asDeltaRational(weaker);
+
+ DeltaRational diff = aboveUpper ? bound - weakerBound : weakerBound - bound;
+ //if var == basic,
+ // if aboveUpper, weakerBound > bound, multiply by -1
+ // if !aboveUpper, weakerBound < bound, multiply by -1
+ diff = diff * coeff;
+ if(surplus > diff){
+ ++d_statistics.d_weakenings;
+ weakened = true;
+ anyWeakening = true;
+ surplus = surplus - diff;
+
+ Debug("weak") << "found:" << endl;
+ if(v == basic){
+ Debug("weak") << " basic: ";
+ }
+ Debug("weak") << " " << surplus << " "<< diff << endl
+ << " " << bound << c << endl
+ << " " << weakerBound << weaker << endl;
+
+ Assert(diff > d_DELTA_ZERO);
+ c = weaker;
+ }
+ }
+ }while(weakened);
+
+ return c;
+}
+
+Node SimplexDecisionProcedure::weakenConflict(bool aboveUpper, ArithVar basicVar){
+ TimerStat::CodeTimer codeTimer(d_statistics.d_weakenTime);
+
+ const DeltaRational& assignment = d_partialModel.getAssignment(basicVar);
+ DeltaRational surplus;
+ if(aboveUpper){
+ Assert(d_partialModel.hasUpperBound(basicVar));
+ Assert(assignment > d_partialModel.getUpperBound(basicVar));
+ surplus = assignment - d_partialModel.getUpperBound(basicVar);
+ }else{
+ Assert(d_partialModel.hasLowerBound(basicVar));
+ Assert(assignment < d_partialModel.getLowerBound(basicVar));
+ surplus = d_partialModel.getLowerBound(basicVar) - assignment;
+ }
+
+ NodeBuilder<> conflict(kind::AND);
+ bool anyWeakenings = false;
+ for(Tableau::RowIterator i = d_tableau.basicRowIterator(basicVar); !i.atEnd(); ++i){
+ const Tableau::Entry& entry = *i;
+ ArithVar v = entry.getColVar();
+ const Rational& coeff = entry.getCoefficient();
+ bool weakening = false;
+ Constraint c = weakestExplanation(aboveUpper, surplus, v, coeff, weakening, basicVar);
+ Debug("weak") << "weak : " << weakening << " "
+ << c->assertedToTheTheory() << " "
+ << d_partialModel.getAssignment(v) << " "
+ << c << endl
+ << c->explainForConflict() << endl;
+ anyWeakenings = anyWeakenings || weakening;
+
+ Debug("weak") << "weak : " << c->explainForConflict() << endl;
+ c->explainForConflict(conflict);
+ }
+ ++d_statistics.d_weakeningAttempts;
+ if(anyWeakenings){
+ ++d_statistics.d_weakeningSuccesses;
+ }
+ return conflict;
+}
+
+
+Node SimplexDecisionProcedure::generateConflictAboveUpperBound(ArithVar conflictVar){
+ return weakenConflict(true, conflictVar);
+}
+
+Node SimplexDecisionProcedure::generateConflictBelowLowerBound(ArithVar conflictVar){
+ return weakenConflict(false, conflictVar);
+}
+
+
+// responses
+// unbounded below(arithvar)
+// reached threshold
+// reached maxpivots
+// reached GlobalMinimumd
+//
+SimplexDecisionProcedure::PrimalResponse SimplexDecisionProcedure::primal(uint32_t maxIterations)
+{
+ Assert(d_optRow != ARITHVAR_SENTINEL);
+
+ for(uint32_t iteration = 0; iteration < maxIterations; iteration++){
+ if(belowThreshold()){
+ return ReachedThresholdValue;
+ }
+
+ PrimalResponse res = primalCheck();
+ switch(res){
+ case GlobalMinimum:
+ case FoundUnboundedVariable:
+ return res;
+ case NoProgressOnLeaving:
+ ++d_statistics.d_primalPivots;
+ ++d_pivotsSinceOptProgress;
+ ++d_pivotsSinceLastCheck;
+ ++d_pivotsSinceErrorProgress;
+
+ d_linEq.pivotAndUpdate(d_primalCarry.d_entering, d_primalCarry.d_leaving, d_partialModel.getAssignment(d_primalCarry.d_entering));
+
+ if(Debug.isOn("primal::tableau")){
+ d_linEq.debugCheckTableau();
+ }
+ if(Debug.isOn("primal::consistent")){ Assert(d_linEq.debugEntireLinEqIsConsistent("MakeProgressOnLeaving")); }
+
+ break;
+ case MakeProgressOnLeaving:
+ {
+ ++d_statistics.d_primalPivots;
+ ++d_statistics.d_primalImprovingPivots;
+
+ d_pivotsSinceOptProgress = 0;
+ ++d_pivotsSinceErrorProgress;
+ ++d_pivotsSinceLastCheck;
+
+ d_linEq.pivotAndUpdate(d_primalCarry.d_entering, d_primalCarry.d_leaving, d_primalCarry.d_nextEnteringValue);
+
+ static int helpful = 0;
+ static int hurtful = 0;
+ static int penguin = 0;
+ if(d_currentErrorVariables.isKey(d_primalCarry.d_entering)){
+ cout << "entering is error " << d_primalCarry.d_entering;
+ if(d_currentErrorVariables[d_primalCarry.d_entering].errorIsLeqZero(d_partialModel)){
+ cout << " now below threshold (" << (++helpful) << ") " << d_pivotsSinceErrorProgress << endl;
+ }else{
+ cout << "ouch (" << (++hurtful) << ")"<< d_pivotsSinceErrorProgress << endl;
+ }
+ }else if(d_currentErrorVariables.isKey(d_primalCarry.d_leaving)){
+ cout << "leaving is error " << d_primalCarry.d_leaving;
+ if(d_currentErrorVariables[d_primalCarry.d_leaving].errorIsLeqZero(d_partialModel)){
+ cout << " now below threshold(" << (++helpful) << ")" << d_pivotsSinceErrorProgress << endl;
+ }else{
+ cout << "ouch (" << (++hurtful) << ")" << d_pivotsSinceErrorProgress<< endl;
+ }
+ }else{
+ cout << " penguin (" << (++penguin) << ")" << d_pivotsSinceErrorProgress<< endl;
+ }
+
+ if(Debug.isOn("primal::tableau")){
+ d_linEq.debugCheckTableau();
+ }
+ if(Debug.isOn("primal::consistent")){ Assert(d_linEq.debugEntireLinEqIsConsistent("MakeProgressOnLeaving")); }
+ }
+ break;
+ default:
+ Unreachable();
+ }
+ }
+ return UsedMaxPivots;
+}
+
+
+/**
+ * Error set
+ * ErrorVariable |-> {ErrorVariable, InputVariable, InputConstraint}
+ */
+
+/**
+ * Returns SAT if it was able to satisfy all of the constraints in the error set
+ * Returns UNSAT if it was able to able to find an error
+ */
+Result::Sat SimplexDecisionProcedure::primalConverge(int depth){
+ d_pivotsSinceLastCheck = 0;
+
+ while(!d_currentErrorVariables.empty()){
+ PrimalResponse res = primal(MAX_ITERATIONS - d_pivotsSinceLastCheck);
+
+ switch(res){
+ case FoundUnboundedVariable:
+
+ // Drive the variable to at least 0
+ // TODO This variable should be driven to a value that makes all of the error functions including it 0
+ // It'll or another unbounded will be selected in the next round anyways so ignore for now.
+ ++d_statistics.d_unboundedFound;
+ if( !belowThreshold() ){
+ driveOptToZero(d_primalCarry.d_unbounded);
+ d_linEq.debugCheckTableau();
+ if(Debug.isOn("primal::consistent")){ Assert(d_linEq.debugEntireLinEqIsConsistent("primalConverge")); }
+
+ ++d_statistics.d_unboundedFound_drive;
+ }
+ Assert(belowThreshold());
+ {
+ uint32_t dropped = contractErrorVariables(true);
+ Debug("primal::converge") << "primalConverge -> FoundUnboundedVariable -> dropped " << dropped << " to " << d_currentErrorVariables.size() << endl;
+ d_statistics.d_unboundedFound_dropped += dropped;
+ }
+ break;
+ case ReachedThresholdValue:
+ ++d_statistics.d_primalThresholdReachedPivot;
+
+ Assert(belowThreshold());
+ {
+ uint32_t dropped = contractErrorVariables(true);
+ Debug("primal::converge") << "primalConverge -> ReachedThresholdValue -> dropped " << dropped << " to " << d_currentErrorVariables.size() << endl;
+ d_statistics.d_primalThresholdReachedPivot_dropped += dropped;
+ }
+ break;
+ case UsedMaxPivots:
+ {
+ d_pivotsSinceLastCheck = 0;
+ ++d_statistics.d_primalReachedMaxPivots;
+
+ // periodically attempt to do the following :
+ // contract the error variable
+ // check for a conflict on an error variable
+ uint32_t dropped = contractErrorVariables(false);
+
+ if( checkForRowConflicts() ){ // try to periodically look for a row
+ Debug("primal::converge") << "primalConverge -> UsedMaxPivots -> unsat " << endl;
+
+ ++d_statistics.d_primalReachedMaxPivots_checkForConflictWorked;
+ return Result::UNSAT; // row conflicts are minimal so stop.
+ }
+
+ if(dropped > 0){
+ Debug("primal::converge") << "primalConverge -> UsedMaxPivots -> dropped " << dropped << " to " << d_currentErrorVariables.size() << endl;
+ ++d_statistics.d_primalReachedMaxPivots_contractMadeProgress;
+ }else{
+ Debug("primal::converge") << "primalConverge -> UsedMaxPivots -> nothing " << endl;
+ }
+ }
+ break;
+ case GlobalMinimum:
+ ++d_statistics.d_primalGlobalMinimum;
+
+ // If the minimum is positive, this is unsat.
+ // However, the optimization row is not necessarily a minimal conflict
+ if(!belowThreshold()){
+
+ if(d_currentErrorVariables.size() == 1){
+ // The optimization function is exactly the same as the last remaining variable
+ // The conflict for the row is the same as the conflict for the optimization function.
+ bool foundConflict = checkForRowConflicts();
+ Assert(foundConflict);
+ Debug("primal::converge") << "primalConverge -> GlobalMinimum -> one variable" << endl;
+
+ return Result::UNSAT;
+ }else{
+ // There are at least 2 error variables.
+ // Look for a minimal conflict
+
+
+ if(checkForRowConflicts() ){
+ Debug("primal::converge") << "primalConverge -> GlobalMinimum -> postitive -> rowconflict " << endl;
+
+ ++d_statistics.d_primalGlobalMinimum_rowConflictWorked;
+ return Result::UNSAT;
+ }
+
+ uint32_t dropped = contractErrorVariables(false);
+
+ Debug("primal::converge") << "primalConverge -> GlobalMinimum -> postitive -> dropped " << dropped << " to " << d_currentErrorVariables.size() << endl;
+ if(dropped > 0){
+ ++d_statistics.d_primalGlobalMinimum_contractMadeProgress;
+ }
+
+ ErrorMap half;
+ d_currentErrorVariables.splitInto(half);
+
+ Debug("primal::converge") << "primalConverge -> GlobalMinimum -> recursion " << depth << endl;
+
+
+ reconstructOptimizationFunction();
+ Result::Sat resultOnRemaining = primalConverge(depth + 1);
+
+ if(resultOnRemaining == Result::UNSAT){
+ Debug("primal::converge") << "primalConverge -> GlobalMinimum -> recursion " << depth << " was unsat " << endl;
+ ++d_statistics.d_primalGlobalMinimum_firstHalfWasUnsat;
+ restoreErrorVariables(half);
+ return Result::UNSAT;
+ }else{
+ ++d_statistics.d_primalGlobalMinimum_firstHalfWasSat;
+ Debug("primal::converge") << "primalConverge -> GlobalMinimum -> recursion " << depth << " was sat " << endl;
+
+ Assert(resultOnRemaining == Result::SAT);
+ Assert(d_currentErrorVariables.empty());
+ d_currentErrorVariables.addAll(half);
+ reconstructOptimizationFunction();
+ return primalConverge(depth + 1);
+ }
+ }
+
+ }else{
+
+ // if the optimum is <= 0
+ // drop all of the satisfied variables and continue;
+ uint32_t dropped = contractErrorVariables(true);
+ Debug("primal::converge") << "primalConverge -> GlobalMinimum -> negative -> dropped "<< dropped << " to " << d_currentErrorVariables.size() << endl;
+
+ ++d_statistics.d_primalGlobalMinimum_contractMadeProgress;
+ }
+ break;
+ default:
+ Unreachable();
+ }
+ }
+
+ return Result::SAT;
+}
+
+
+Result::Sat SimplexDecisionProcedure::primalFindModel(){
+ Assert(d_primalCarry.isClear());
+
+ // Reduce the queue to only contain violations
+ reduceQueue();
+
+ if(d_queue.empty()){
+ return Result::SAT;
+ }
+ TimerStat::CodeTimer codeTimer(d_statistics.d_primalTimer);
+
+ ++d_statistics.d_primalCalls;
+
+ Debug("primalFindModel") << "primalFindModel() begin" << endl;
+
+ const int PAUSE_RATE = 100;
+ if(Debug.isOn("primal::pause") && d_statistics.d_primalCalls.getData() % PAUSE_RATE == 0){
+ Debug("primal::pause") << "waiting for input: ";
+ std::string dummy;
+ std::getline(std::cin, dummy);
+ }
+
+ // TODO restore the tableau by ejecting variables
+ // Tableau copy(d_tableau);
+
+ Result::Sat answer;
+ {
+ TimerStat::CodeTimer codeTimer(d_statistics.d_internalTimer);
+
+ // This is needed because of the fiddling with the partial model
+ //context::Context::ScopedPush speculativePush(satContext);
+
+ constructErrorVariables();
+ constructOptimizationFunction();
+ if(Debug.isOn("primal::tableau")){ d_linEq.debugCheckTableau(); }
+ if(Debug.isOn("primal::consistent")){ d_linEq.debugEntireLinEqIsConsistent("primalFindModel 1"); }
+ answer = primalConverge(0);
+ }
+ removeOptimizationFunction();
+
+
+ // exit
+ uint32_t nc = d_tableau.getNumColumns();
+ //d_tableau = copy;
+ while(d_tableau.getNumColumns() < nc){
+ d_tableau.increaseSize();
+ }
+ restoreErrorVariables(d_currentErrorVariables);
+
+ reduceQueue();
+
+ if(Debug.isOn("primal::tableau")){ d_linEq.debugCheckTableau(); }
+
+ if(Debug.isOn("primal::consistent")){ d_linEq.debugEntireLinEqIsConsistent("primalFindModel2"); }
+ Debug("primalFindModel") << "primalFindModel() end " << answer << endl;
+
+ // The set of variables in conflict with their bounds will still be a subset of the
+ // variables that are in conflict with their bounds in the beginning.
+ // The basic variables are the same because of the copy.
+ // Thus it is safe to not try to not recompute the queue of violating variables
+
+ if(answer == Result::UNSAT){
+ // This needs to be done in a different context level than the push
+ ++d_statistics.d_primalUnsatCalls;
+ }else{
+ ++d_statistics.d_primalSatCalls;
+ }
+
+ d_primalCarry.clear();
+
+ return answer;
+}
+
+/** Clears the ErrorMap and relase the resources associated with it.
+ * There are a couple of error maps around
+ */
+void SimplexDecisionProcedure::restoreErrorVariables(SimplexDecisionProcedure::ErrorMap& es){
+ while(!es.empty()){
+ ArithVar e = es.back();
+
+ reassertErrorConstraint(e, es, false, false);
+ es.pop_back();
+ }
+}
+
+void SimplexDecisionProcedure::constructErrorVariables(){
+ Assert(d_currentErrorVariables.empty());
+ Assert(!d_queue.empty());
+
+ for(ArithPriorityQueue::const_iterator iter = d_queue.begin(), end = d_queue.end(); iter != end; ++iter){
+ ArithVar input = *iter;
+
+ Assert(d_tableau.isBasic(input));
+ Assert(!d_partialModel.assignmentIsConsistent(input));
+
+ Assert(!d_currentErrorVariables.isKey(input));
+
+ bool ub = d_partialModel.strictlyGreaterThanUpperBound(input, d_partialModel.getAssignment(input));
+
+ Constraint original = ub ? d_partialModel.getUpperBoundConstraint(input)
+ : d_partialModel.getLowerBoundConstraint(input);
+
+ d_currentErrorVariables.set(input, ErrorInfo(input, ub, original));
+
+ if(ub){
+ d_partialModel.forceRelaxUpperBound(input);
+ }else{
+ d_partialModel.forceRelaxLowerBound(input);
+ }
+ Debug("primal") << "adding error variable (" << input << ", " << ", " << original <<") ";
+ Debug("primal") << "ub "<< ub << " " << d_partialModel.getAssignment(input) << " " << original->getValue() <<")" << endl;
+ d_currentErrorVariables.set(input, ErrorInfo(input, ub, original));
+
+ // Constraint boundIsValue = d_constraintDatabase.getConstraint(bound, Equality, original->getValue());
+ // boundIsValue->setPsuedoConstraint();
+
+ // d_partialModel.setAssignment(bound, boundIsValue->getValue());
+ // d_partialModel.setUpperBoundConstraint(boundIsValue);
+ // d_partialModel.setLowerBoundConstraint(boundIsValue);
+
+ // // if ub
+ // // then error = x - boundIsValue
+ // // else error = boundIsValue - x
+
+ // ArithVar error = requestVariable();
+
+ // DeltaRational diff = ub ?
+ // d_partialModel.getAssignment(input) - boundIsValue->getValue() :
+ // boundIsValue->getValue() - d_partialModel.getAssignment(input);
+
+ // d_partialModel.setAssignment(error, diff);
+
+ // vector<Rational> coeffs;
+ // vector<ArithVar> variables;
+ // variables.push_back(input);
+ // coeffs.push_back(ub ? Rational(1) : Rational(-1));
+ // variables.push_back(bound);
+ // coeffs.push_back(ub ? Rational(-1) : Rational(1));
+
+ // d_tableau.addRow(error, coeffs, variables);
+
+
+ }
+
+ if(Debug.isOn("primal::tableau")){ d_linEq.debugCheckTableau(); }
+ if(Debug.isOn("primal::consistent")){ d_linEq.debugEntireLinEqIsConsistent("constructErrorVariables");}
+ Assert(!d_currentErrorVariables.empty());
+}
+
+
+
+/** Returns true if it has found a row conflict for any of the error variables. */
+bool SimplexDecisionProcedure::checkForRowConflicts(){
+ vector<ArithVar> inConflict;
+ for(ErrorMap::const_iterator iter = d_currentErrorVariables.begin(), end = d_currentErrorVariables.end(); iter != end; ++iter){
+ ArithVar error = *iter;
+ const ErrorInfo& info = d_currentErrorVariables[error];
+ if(d_tableau.isBasic(error) && !info.errorIsLeqZero(d_partialModel)){
+
+ ArithVar x_j = info.isUpperbound() ?
+ selectSlackLowerBound(error) :
+ selectSlackUpperBound(error);
+
+ if(x_j == ARITHVAR_SENTINEL ){
+ inConflict.push_back(error);
+ }
+ }
+ }
+
+ if(!inConflict.empty()){
+ while(!inConflict.empty()){
+ ArithVar error = inConflict.back();
+ inConflict.pop_back();
+
+ reassertErrorConstraint(error, d_currentErrorVariables, false, true);
+
+ Node conflict = d_currentErrorVariables[error].isUpperbound() ?
+ generateConflictAboveUpperBound(error) :
+ generateConflictBelowLowerBound(error);
+ Assert(!conflict.isNull());
+
+ d_currentErrorVariables.remove(error);
+
+ reportConflict(conflict);
+ }
+ reconstructOptimizationFunction();
+ return true;
+ }else{
+ return false;
+ }
+}
+
+void SimplexDecisionProcedure::reassertErrorConstraint(ArithVar e, SimplexDecisionProcedure::ErrorMap& es, bool definitelyTrue, bool definitelyFalse){
+ Assert(es.isKey(e));
+ const ErrorInfo& info = es.get(e);
+ Constraint original = info.getConstraint();
+
+ if(info.isUpperbound()){
+ d_partialModel.setUpperBoundConstraint(original);
+ }else if(original->isLowerBound()){
+ d_partialModel.setLowerBoundConstraint(original);
+ }
+
+ Assert(!definitelyTrue || d_partialModel.assignmentIsConsistent(e));
+ Assert(!definitelyFalse || !d_partialModel.assignmentIsConsistent(e));
+}
+
+uint32_t SimplexDecisionProcedure::contractErrorVariables(bool guaranteedSuccess){
+ uint32_t entrySize = d_currentErrorVariables.size();
+ Debug("primal::contract") << "contractErrorVariables() begin : " << d_currentErrorVariables.size() << endl;
+
+ std::vector<ArithVar> toRemove;
+ for(ErrorMap::const_iterator iter = d_currentErrorVariables.begin(), end = d_currentErrorVariables.end(); iter != end; ++iter){
+ ArithVar e = *iter;
+ if(d_currentErrorVariables[e].errorIsLeqZero(d_partialModel)){
+ toRemove.push_back(e);
+ }
+ }
+
+ Assert(!guaranteedSuccess || !toRemove.empty());
+
+ if(!toRemove.empty()){
+ while(!toRemove.empty()){
+ ArithVar e = toRemove.back();
+ toRemove.pop_back();
+ reassertErrorConstraint(e, d_currentErrorVariables, true, false);
+ d_currentErrorVariables.remove(e);
+ }
+
+ reconstructOptimizationFunction();
+ }
+
+ Debug("primal::contract") << "contractErrorVariables() end : " << d_currentErrorVariables.size() << endl;
+
+ uint32_t exitSize = d_currentErrorVariables.size();
+
+ Assert(exitSize <= entrySize);
+ Assert(!guaranteedSuccess|| exitSize < entrySize);
+ return entrySize - exitSize;
+}
+
+void SimplexDecisionProcedure::removeOptimizationFunction(){
+ Assert(d_optRow != ARITHVAR_SENTINEL);
+ Assert(d_tableau.isBasic(d_optRow));
+
+ d_tableau.removeBasicRow(d_optRow);
+ releaseVariable(d_optRow);
+
+ d_optRow = ARITHVAR_SENTINEL;
+ d_negOptConstant = d_DELTA_ZERO;
+
+ Assert(d_optRow == ARITHVAR_SENTINEL);
+}
+
+void SimplexDecisionProcedure::constructOptimizationFunction(){
+ Assert(d_optRow == ARITHVAR_SENTINEL);
+ Assert(d_negOptConstant == d_DELTA_ZERO);
+
+ d_optRow = requestVariable();
+
+
+ std::vector<Rational> coeffs;
+ std::vector<ArithVar> variables;
+ for(ErrorMap::const_iterator iter = d_currentErrorVariables.begin(), end = d_currentErrorVariables.end(); iter != end; ++iter){
+ ArithVar e = *iter;
+
+ if(d_currentErrorVariables[e].isUpperbound()){
+ coeffs.push_back(Rational(1));
+ variables.push_back(e);
+ d_negOptConstant = d_negOptConstant + (d_currentErrorVariables[e].getConstraint()->getValue());
+ }else{
+ coeffs.push_back(Rational(-1));
+ variables.push_back(e);
+ d_negOptConstant = d_negOptConstant - (d_currentErrorVariables[e].getConstraint()->getValue());
+ }
+ }
+ d_tableau.addRow(d_optRow, coeffs, variables);
+
+ DeltaRational newAssignment = d_linEq.computeRowValue(d_optRow, false);
+ d_partialModel.setAssignment(d_optRow, newAssignment);
+
+ if(Debug.isOn("primal::tableau")){ d_linEq.debugCheckTableau(); }
+
+
+ if(Debug.isOn("primal::consistent")){
+ d_linEq.debugEntireLinEqIsConsistent("constructOptimizationFunction");
+ }
+
+ d_pivotsSinceOptProgress = 0;
+ d_pivotsSinceErrorProgress = 0;
+
+ Assert(d_optRow != ARITHVAR_SENTINEL);
+}
+
+void SimplexDecisionProcedure::reconstructOptimizationFunction(){
+ removeOptimizationFunction();
+ constructOptimizationFunction();
+}
+
+
+
+/* TODO:
+ * Very naive implementation. Recomputes everything every time.
+ * Currently looks for the variable that can decrease the optimization function the most.
+ *
+ */
+SimplexDecisionProcedure::PrimalResponse SimplexDecisionProcedure::primalCheck()
+{
+ Debug("primal") << "primalCheck() begin" << endl;
+
+ ArithVar leaving = ARITHVAR_SENTINEL;
+ ArithVar entering = ARITHVAR_SENTINEL;
+ DeltaRational leavingShift = d_DELTA_ZERO; // The amount the leaving variable can change by without making the tableau inconsistent
+ DeltaRational leavingDelta = d_DELTA_ZERO; // The amount the optimization function changes by selecting leaving
+
+ Assert(d_improvementCandidates.empty());
+
+ for( Tableau::RowIterator ri = d_tableau.basicRowIterator(d_optRow); !ri.atEnd(); ++ri){
+ const Tableau::Entry& e = *ri;
+
+ ArithVar curr = e.getColVar();
+ if(curr == d_optRow){ continue; }
+
+
+ int sgn = e.getCoefficient().sgn();
+ Assert(sgn != 0);
+ if( (sgn < 0 && d_partialModel.strictlyBelowUpperBound(curr)) ||
+ (sgn > 0 && d_partialModel.strictlyAboveLowerBound(curr)) ){
+
+ d_improvementCandidates.push_back(&e);
+ }
+ }
+
+ if(d_improvementCandidates.empty()){
+ Debug("primal") << "primalCheck() end : global" << endl;
+ return GlobalMinimum; // No variable in the optimization function can be improved
+ }
+
+ DeltaRational minimumShift;
+ DeltaRational currShift;
+ for(EntryVector::const_iterator ci = d_improvementCandidates.begin(), end_ci = d_improvementCandidates.end(); ci != end_ci; ++ci){
+ const Tableau::Entry& e = *(*ci);
+ ArithVar curr = e.getColVar();
+
+ ArithVar currEntering;
+ bool progress;
+
+ minimumShift = (leaving == ARITHVAR_SENTINEL ) ? leavingDelta/(e.getCoefficient().abs()) : d_DELTA_ZERO;
+ int sgn = e.getCoefficient().sgn();
+ computeShift(curr, sgn < 0, progress, currEntering, currShift, minimumShift);
+
+ if(currEntering == ARITHVAR_SENTINEL){
+ d_improvementCandidates.clear();
+
+ Debug("primal") << "primalCheck() end : unbounded" << endl;
+ d_primalCarry.d_unbounded = curr;
+ return FoundUnboundedVariable;
+ }else if(progress) {
+ leaving = curr;
+ leavingShift = currShift;
+ leavingDelta = currShift * e.getCoefficient();
+ entering = currEntering;
+
+ Assert(leavingDelta < d_DELTA_ZERO);
+
+ const int RECHECK_PERIOD = 10;
+ if(d_pivotsSinceErrorProgress % RECHECK_PERIOD != 0){
+ // we can make progress, stop
+ break;
+ }
+ }
+ }
+
+ if(leaving == ARITHVAR_SENTINEL){
+ cout << "Nothing can make progress " << endl;
+
+ const uint32_t THRESHOLD = 20;
+ if(d_pivotsSinceOptProgress <= THRESHOLD){
+
+ int index = rand() % d_improvementCandidates.size();
+ leaving = (*d_improvementCandidates[index]).getColVar();
+ entering = selectFirstValid(leaving, (*d_improvementCandidates[index]).getCoefficient().sgn() < 0);
+ }else{ // Bland's rule
+ bool increasing;
+ for(EntryVector::const_iterator ci = d_improvementCandidates.begin(), end_ci = d_improvementCandidates.end(); ci != end_ci; ++ci){
+ const Tableau::Entry& e = *(*ci);
+ ArithVar curr = e.getColVar();
+ leaving = (leaving == ARITHVAR_SENTINEL) ? curr : minVarOrder(*this, curr, leaving);
+ if(leaving == curr){
+ increasing = (e.getCoefficient().sgn() < 0);
+ }
+ }
+
+ entering = selectMinimumValid(leaving, increasing);
+ }
+ Assert(leaving != ARITHVAR_SENTINEL);
+ Assert(entering != ARITHVAR_SENTINEL);
+
+ d_primalCarry.d_leaving = leaving;
+ d_primalCarry.d_entering = entering;
+
+ d_primalCarry.d_nextEnteringValue = d_partialModel.getAssignment(entering);
+
+ Debug("primal") << "primalCheck() end : no progress made " << leaving << " to " << entering << " (" << d_pivotsSinceOptProgress << ")"<< endl;
+ d_improvementCandidates.clear();
+ return NoProgressOnLeaving;
+ }else{
+ const Tableau::Entry& enterLeavingEntry = d_tableau.findEntry(d_tableau.basicToRowIndex(entering), leaving);
+ Assert(!enterLeavingEntry.blank());
+
+ d_primalCarry.d_leaving = leaving;
+ d_primalCarry.d_entering = entering;
+ d_primalCarry.d_nextEnteringValue = d_partialModel.getAssignment(entering)
+ + leavingShift * enterLeavingEntry.getCoefficient();
+
+ Debug("primal") << "primalCheck() end : progress" << endl
+ << leaving << " to " << entering << " ~ "
+ << d_partialModel.getAssignment(leaving) << " ~ " << leavingShift
+ << " ~ " << enterLeavingEntry.getCoefficient()
+ << " ~ " << d_primalCarry.d_nextEnteringValue << endl;
+
+ d_improvementCandidates.clear();
+ return MakeProgressOnLeaving;
+ }
+
+ // anyProgress = true;
+
+ // DeltaRational currDelta = currShift * e.getCoefficient();
+
+ // int cmp = currDelta.cmp(leavingDelta);
+
+ // // Cases:
+ // // 1) No candidate yet,
+ // // 2) there is a candidate with a strictly better update, or
+ // // 3) there is a candidate with the same update value that has a smaller value in the variable ordering.
+ // //
+ // // Case 3 covers Bland's rule.
+ // if(entering == ARITHVAR_SENTINEL || cmp < 0){
+ // leaving = curr;
+ // }else if( cmp == 0 ){
+ // leaving = minVarOrder(*this, curr, leaving);
+ // }
+
+ // if(leaving == curr){
+ // leavingShift = currShift;
+ // leavingDelta = currDelta;
+ // entering = currEntering;
+ // }
+ // }
+ // }
+
+ // if(leaving == ARITHVAR_SENTINEL){
+ // Debug("primal") << "primalCheck() end : global" << endl;
+ // return GlobalMinimum; // No variable in the optimization function can be improved
+ // }else{
+ // const Tableau::Entry& enterLeavingEntry = d_tableau.findEntry(d_tableau.basicToRowIndex(entering), leaving);
+ // Assert(!enterLeavingEntry.blank());
+
+ // d_primalCarry.d_leaving = leaving;
+ // d_primalCarry.d_entering = entering;
+ // d_primalCarry.d_nextEnteringValue = d_partialModel.getAssignment(entering)
+ // + leavingShift * enterLeavingEntry.getCoefficient();
+
+ // Debug("primal") << "primalCheck() end : progress" << endl
+ // << leaving << " to " << entering << " ~ "
+ // << d_partialModel.getAssignment(leaving) << " ~ " << leavingShift
+ // << " ~ " << enterLeavingEntry.getCoefficient()
+ // << " ~ " << d_primalCarry.d_nextEnteringValue << endl;
+ // return MakeProgressOnLeaving;
+ // }
+}
+
+ArithVar SimplexDecisionProcedure::selectMinimumValid(ArithVar v, bool increasing){
+ ArithVar minimum = ARITHVAR_SENTINEL;
+ for(Tableau::ColIterator colIter = d_tableau.colIterator(v);!colIter.atEnd(); ++colIter){
+ const Tableau::Entry& e = *colIter;
+ ArithVar basic = d_tableau.rowIndexToBasic(e.getRowIndex());
+ if(basic == d_optRow) continue;
+
+
+ int esgn = e.getCoefficient().sgn();
+ bool basicInc = (increasing == (esgn > 0));
+
+ if(!(basicInc ? d_partialModel.strictlyBelowUpperBound(basic) :
+ d_partialModel.strictlyAboveLowerBound(basic))){
+ if(minimum == ARITHVAR_SENTINEL){
+ minimum = basic;
+ }else{
+ minimum = minVarOrder(*this, basic, minimum);
+ }
+ }
+ }
+ return minimum;
+}
+
+ArithVar SimplexDecisionProcedure::selectFirstValid(ArithVar v, bool increasing){
+ ArithVar minimum = ARITHVAR_SENTINEL;
+
+ for(Tableau::ColIterator colIter = d_tableau.colIterator(v);!colIter.atEnd(); ++colIter){
+ const Tableau::Entry& e = *colIter;
+ ArithVar basic = d_tableau.rowIndexToBasic(e.getRowIndex());
+ if(basic == d_optRow) continue;
+
+ int esgn = e.getCoefficient().sgn();
+ bool basicInc = (increasing == (esgn > 0));
+
+ if(!(basicInc ? d_partialModel.strictlyBelowUpperBound(basic) :
+ d_partialModel.strictlyAboveLowerBound(basic))){
+ if(minimum == ARITHVAR_SENTINEL){
+ minimum = basic;
+ }else{
+ minimum = minRowLength(*this, basic, minimum);
+ }
+ }
+ }
+ return minimum;
+}
+
+
+
+void SimplexDecisionProcedure::computeShift(ArithVar leaving, bool increasing, bool& progress, ArithVar& entering, DeltaRational& shift, const DeltaRational& minimumShift){
+ Assert(increasing ? (minimumShift >= d_DELTA_ZERO) : (minimumShift <= d_DELTA_ZERO) );
+
+ static int instance = 0;
+ Debug("primal") << "computeshift " << ++instance << " " << leaving << endl;
+
+ // The selection for the leaving variable
+ entering = ARITHVAR_SENTINEL;
+
+ // no progress is initially made
+ progress = false;
+
+ bool bounded = false;
+
+ if(increasing ? d_partialModel.hasUpperBound(leaving) : d_partialModel.hasLowerBound(leaving)){
+ const DeltaRational& assignment = d_partialModel.getAssignment(leaving);
+
+ bounded = true;
+
+ DeltaRational diff = increasing ? d_partialModel.getUpperBound(leaving) - assignment : d_partialModel.getLowerBound(leaving) - assignment;
+ Assert(increasing ? diff.sgn() >=0 : diff.sgn() <= 0);
+ if((increasing) ? (diff < minimumShift) : ( diff > minimumShift)){
+ Assert(!progress);
+ entering = leaving; // My my my, what an ugly hack
+ return; // no progress is possible stop
+ }
+ }
+
+ // shift has a meaningful value once entering has a meaningful value
+ // if increasing,
+ // then shift > minimumShift >= 0
+ // else shift < minimumShift <= 0
+ //
+ // Maintain the following invariant:
+ //
+ // if increasing,
+ // if e_ij > 0, diff >= shift > minimumShift >= 0
+ // if e_ij < 0, diff >= shift > minimumShift >= 0
+ // if !increasing,
+ // if e_ij > 0, diff <= shift < minimumShift <= 0
+ // if e_ij < 0, diff <= shift < minimumShift <= 0
+ // if increasing == (e_ij > 0), diff = (d_partialModel.getUpperBound(basic) - d_partialModel.getAssignment(basic)) / e.getCoefficient()
+ // if increasing != (e_ij > 0), diff = (d_partialModel.getLowerBound(basic) - d_partialModel.getAssignment(basic)) / e.getCoefficient()
+
+ for(Tableau::ColIterator colIter = d_tableau.colIterator(leaving);!colIter.atEnd(); ++colIter){
+ const Tableau::Entry& e = *colIter;
+
+ ArithVar basic = d_tableau.rowIndexToBasic(e.getRowIndex());
+ if(basic == d_optRow) continue;
+
+ int esgn = e.getCoefficient().sgn();
+ bool basicInc = (increasing == (esgn > 0));
+ // If both are true, increasing the variable entering increases the basic variable
+ // If both are false, the entering variable is decreasing, but the coefficient is negative and the basic variable is increasing
+ // If exactly one is false, the basic variable is decreasing.
+
+ Debug("primal::shift") << basic << " " << d_partialModel.hasUpperBound(basic) << " "
+ << d_partialModel.hasLowerBound(basic) << " "
+ << e.getCoefficient() << endl;
+
+ if( (basicInc && d_partialModel.hasUpperBound(basic))||
+ (!basicInc && d_partialModel.hasLowerBound(basic))){
+
+ if(!(basicInc ? d_partialModel.strictlyBelowUpperBound(basic) :
+ d_partialModel.strictlyAboveLowerBound(basic))){
+ // diff == 0, as diff > minimumShift >= 0 or diff < minimumShift <= 0
+ Assert(!progress);
+ entering = basic;
+ return;
+ }else{
+ DeltaRational diff = basicInc ?
+ (d_partialModel.getUpperBound(basic) - d_partialModel.getAssignment(basic)) / e.getCoefficient() :
+ (d_partialModel.getLowerBound(basic) - d_partialModel.getAssignment(basic)) / e.getCoefficient();
+
+ if( entering == ARITHVAR_SENTINEL ){
+ if(increasing ? (diff <= minimumShift) : (diff >= minimumShift)){
+ Assert(!progress);
+ entering = basic;
+ return;
+ }else{
+ Assert(increasing ? (diff > minimumShift) : (diff < minimumShift));
+ shift = diff;
+ entering = basic;
+ bounded = true;
+ }
+ }else{
+ if( increasing ? (diff < shift) : diff > shift){
+ // a new min for increasing
+ // a new max for decreasing
+
+ if(increasing ? (diff <= minimumShift) : (diff >= minimumShift)){
+ Assert(!progress);
+ entering = basic;
+ return;
+ }else{
+ Assert(increasing ? (diff > minimumShift) : (diff < minimumShift));
+ shift = diff;
+ entering = basic;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if(!bounded){
+ // A totally unbounded variable
+ Assert(entering == ARITHVAR_SENTINEL);
+ progress = true;
+ return;
+ }else if(entering == ARITHVAR_SENTINEL){
+ // We have a variable that is bounded only by its maximum
+ for(Tableau::ColIterator colIter = d_tableau.colIterator(leaving);!colIter.atEnd(); ++colIter){
+ const Tableau::Entry& e = *colIter;
+
+ ArithVar basic = d_tableau.rowIndexToBasic(e.getRowIndex());
+ if(basic == d_optRow){
+ continue;
+ } else{
+ entering = basic;
+ break;
+ }
+ }
+ Assert(entering != ARITHVAR_SENTINEL);
+
+ Assert(increasing ? d_partialModel.hasUpperBound(leaving) : d_partialModel.hasLowerBound(leaving));
+
+ const DeltaRational& assignment = d_partialModel.getAssignment(leaving);
+ DeltaRational diff = increasing ? d_partialModel.getUpperBound(leaving) - assignment : d_partialModel.getLowerBound(leaving) - assignment;
+
+ shift = diff;
+
+ Assert(increasing ? shift.sgn() >=0 : shift.sgn() <= 0);
+ Assert(increasing ? shift > minimumShift : shift < minimumShift);
+
+ progress = true;
+ return;
+ }else{
+ Assert(bounded);
+ progress = true;
+
+ if((increasing ? d_partialModel.hasUpperBound(leaving) : d_partialModel.hasLowerBound(leaving) )){
+ Assert(entering != ARITHVAR_SENTINEL);
+ const DeltaRational& assignment = d_partialModel.getAssignment(leaving);
+ DeltaRational diff = increasing ? d_partialModel.getUpperBound(leaving) - assignment : d_partialModel.getLowerBound(leaving) - assignment;
+ if((increasing) ? (diff < shift) : ( diff > shift)){
+ shift = diff;
+ }
+ }
+
+ Assert(increasing ? shift.sgn() >=0 : shift.sgn() <= 0);
+ Assert(increasing ? shift > minimumShift : shift < minimumShift);
+ return;
+ }
+
+
+ // if(! bounded ||
+ // (increasing && diff < shift) || // a new min for increasing
+ // (!increasing && diff > shift)){ // a new max for decreasing
+ // bounded = true;
+ // shift = diff;
+ // entering = basic;
+ // }
+ // }
+
+ // if(notAtTheBound && !blandMode){
+ // DeltaRational diff = basicInc ?
+ // (d_partialModel.getUpperBound(basic) - d_partialModel.getAssignment(basic)) / e.getCoefficient() :
+ // (d_partialModel.getLowerBound(basic) - d_partialModel.getAssignment(basic)) / e.getCoefficient();
+
+ // if(! bounded ||
+ // (increasing && diff < shift) || // a new min for increasing
+ // (!increasing && diff > shift)){ // a new max for decreasing
+ // bounded = true;
+ // shift = diff;
+ // entering = basic;
+ // }
+ // }else if (!notAtTheBound) { // basic is already exactly at the bound
+ // if(!blandMode){ // Enter into using Bland's rule
+ // blandMode = true;
+ // bounded = true;
+ // shift = d_DELTA_ZERO;
+ // entering = basic;
+ // }else{
+ // entering = minVarOrder(*this, entering, basic); // Bland's rule.
+ // }
+ // }
+ // else this basic variable cannot be violated by increasing/decreasing entering
+
+
+
+
+ // if(!blandMode && (increasing ? d_partialModel.hasUpperBound(leaving) : d_partialModel.hasLowerBound(leaving) )){
+ // Assert(entering != ARITHVAR_SENTINEL);
+ // bounded = true;
+ // DeltaRational diff = increasing ? d_partialModel.getUpperBound(leaving) - assignment : d_partialModel.getLowerBound(leaving) - assignment;
+ // if((increasing) ? (diff < shift) : ( diff > shift)){
+ // shift = diff;
+ // }
+ // }
+
+ // Assert(increasing ? shift.sgn() >=0 : shift.sgn() <= 0);
+
+ // return shift;
+}
+
+
+/**
+ * Given an variable on the optimization row that can be used to decrease the value of the optimization function
+ * arbitrarily and an optimization function that is strictly positive in the current model,
+ * driveOptToZero updates the value of unbounded s.t. the value of d_opt is exactly 0.
+ */
+void SimplexDecisionProcedure::driveOptToZero(ArithVar unbounded){
+ Assert(!belowThreshold());
+
+ const Tableau::Entry& e = d_tableau.findEntry(d_tableau.basicToRowIndex(d_optRow), unbounded);
+ Assert(!e.blank());
+
+ DeltaRational theta = (d_negOptConstant-d_partialModel.getAssignment(d_optRow))/ (e.getCoefficient());
+ Assert((e.getCoefficient().sgn() > 0) ? (theta.sgn() < 0) : (theta.sgn() > 0));
+
+ DeltaRational newAssignment = d_partialModel.getAssignment(unbounded) + theta;
+ d_linEq.update(unbounded, newAssignment);
+
+ if(Debug.isOn("primal::consistent")){ Assert(d_linEq.debugEntireLinEqIsConsistent("driveOptToZero")); }
+
+ Assert(belowThreshold());
+}
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback