summaryrefslogtreecommitdiff
path: root/src/theory/arith/simplex-converge.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/theory/arith/simplex-converge.h')
-rw-r--r--src/theory/arith/simplex-converge.h531
1 files changed, 531 insertions, 0 deletions
diff --git a/src/theory/arith/simplex-converge.h b/src/theory/arith/simplex-converge.h
new file mode 100644
index 000000000..dac3a9b49
--- /dev/null
+++ b/src/theory/arith/simplex-converge.h
@@ -0,0 +1,531 @@
+/********************* */
+/*! \file simplex.h
+ ** \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 This is an implementation of the Simplex Module for the Simplex for DPLL(T) decision procedure.
+ **
+ ** This implements the Simplex module for the Simpelx for DPLL(T) decision procedure.
+ ** See the Simplex for DPLL(T) technical report for more background.(citation?)
+ ** This shares with the theory a Tableau, and a PartialModel that:
+ ** - satisfies the equalities in the Tableau, and
+ ** - the assignment for the non-basic variables satisfies their bounds.
+ ** This is required to either produce a conflict or satisifying PartialModel.
+ ** Further, we require being told when a basic variable updates its value.
+ **
+ ** During the Simplex search we maintain a queue of variables.
+ ** The queue is required to contain all of the basic variables that voilate their bounds.
+ ** As elimination from the queue is more efficient to be done lazily,
+ ** we do not maintain that the queue of variables needs to be only basic variables or only variables that satisfy their bounds.
+ **
+ ** The simplex procedure roughly follows Alberto's thesis. (citation?)
+ ** There is one round of selecting using a heuristic pivoting rule. (See PreferenceFunction Documentation for the available options.)
+ ** The non-basic variable is the one that appears in the fewest pivots. (Bruno says that Leonardo invented this first.)
+ ** After this, Bland's pivot rule is invoked.
+ **
+ ** During this proccess, we periodically inspect the queue of variables to
+ ** 1) remove now extraneous extries,
+ ** 2) detect conflicts that are "waiting" on the queue but may not be detected by the current queue heuristics, and
+ ** 3) detect multiple conflicts.
+ **
+ ** Conflicts are greedily slackened to use the weakest bounds that still produce the conflict.
+ **
+ ** Extra things tracked atm: (Subject to change at Tim's whims)
+ ** - A superset of all of the newly pivoted variables.
+ ** - A queue of additional conflicts that were discovered by Simplex.
+ ** These are theory valid and are currently turned into lemmas
+ **/
+
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__ARITH__SIMPLEX_H
+#define __CVC4__THEORY__ARITH__SIMPLEX_H
+
+#include "theory/arith/arithvar.h"
+#include "theory/arith/arith_priority_queue.h"
+#include "theory/arith/delta_rational.h"
+#include "theory/arith/matrix.h"
+#include "theory/arith/partial_model.h"
+#include "theory/arith/linear_equality.h"
+
+#include "context/cdlist.h"
+
+#include "util/dense_map.h"
+#include "options/options.h"
+#include "util/stats.h"
+#include "util/result.h"
+
+#include <queue>
+
+namespace CVC4 {
+namespace theory {
+namespace arith {
+
+class SimplexDecisionProcedure {
+private:
+ ArithVar d_conflictVariable;
+ DenseSet d_successes;
+
+ /** Linear equality module. */
+ LinearEqualityModule& d_linEq;
+
+ /**
+ * Manages information about the assignment and upper and lower bounds on
+ * variables.
+ * Partial model matches that in LinearEqualityModule.
+ */
+ ArithPartialModel& d_partialModel;
+
+ /**
+ * Stores the linear equalities used by Simplex.
+ * Tableau from the LinearEquality module.
+ */
+ Tableau& d_tableau;
+
+ /** Contains a superset of the basic variables in violation of their bounds. */
+ ArithPriorityQueue d_queue;
+
+ /** Number of variables in the system. This is used for tuning heuristics. */
+ ArithVar d_numVariables;
+
+ /** This is the call back channel for Simplex to report conflicts. */
+ NodeCallBack& d_conflictChannel;
+
+ /** Maps a variable to how many times they have been used as a pivot in the simplex search. */
+ DenseMultiset d_pivotsInRound;
+
+ /** Stores to the DeltaRational constant 0. */
+ DeltaRational d_DELTA_ZERO;
+
+ //TODO make an option
+ const static uint32_t MAX_ITERATIONS = 20;
+
+
+ /** Used for requesting d_opt, bound and error variables for primal.*/
+ ArithVarMalloc& d_arithVarMalloc;
+
+ /** Used for constructing temporary variables, bound and error variables for primal.*/
+ ConstraintDatabase& d_constraintDatabase;
+
+public:
+ SimplexDecisionProcedure(LinearEqualityModule& linEq,
+ NodeCallBack& conflictChannel,
+ ArithVarMalloc& variables,
+ ConstraintDatabase& constraintDatabase);
+
+ /**
+ * This must be called when the value of a basic variable may now voilate one
+ * of its bounds.
+ */
+ void updateBasic(ArithVar x){
+ d_queue.enqueueIfInconsistent(x);
+ }
+
+ /**
+ * Tries to update the assignments of variables such that all of the
+ * assignments are consistent with their bounds.
+ * This is done by a simplex search through the possible bases of the tableau.
+ *
+ * If all of the variables can be made consistent with their bounds
+ * SAT is returned. Otherwise UNSAT is returned, and at least 1 conflict
+ * was reported on the conflictCallback passed to the Module.
+ *
+ * Tableau pivoting is performed so variables may switch from being basic to
+ * nonbasic and vice versa.
+ *
+ * Corresponds to the "check()" procedure in [Cav06].
+ */
+ Result::Sat dualFindModel(bool exactResult);
+
+
+ /**
+ * Tries to update the assignments of the variables s.t. all of the assignments
+ * are consistent with their bounds.
+ *
+ * This is a REALLY heavy hammer consider calling dualFindModel(false) first.
+ *
+ * !!!!!!!!!!!!!IMPORTANT!!!!!!!!!!!!!!
+ * This procedure needs to temporarily relax contraints to contruct a satisfiable system.
+ * To do this, it is going to do a sat push.
+ */
+ Result::Sat primalFindModel();
+
+private:
+
+
+ /**
+ * A PreferenceFunction takes a const ref to the SimplexDecisionProcedure,
+ * and 2 ArithVar variables and returns one of the ArithVar variables potentially
+ * using the internals of the SimplexDecisionProcedure.
+ *
+ * Both ArithVar must be non-basic in d_tableau.
+ */
+ typedef ArithVar (*PreferenceFunction)(const SimplexDecisionProcedure&, ArithVar, ArithVar);
+
+ /**
+ * minVarOrder is a PreferenceFunction for selecting the smaller of the 2 ArithVars.
+ * This PreferenceFunction is used during the VarOrder stage of
+ * findModel.
+ */
+ static ArithVar minVarOrder(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y);
+
+ /**
+ * minRowCount is a PreferenceFunction for selecting the variable with the smaller
+ * row count in the tableau.
+ *
+ * This is a heuristic rule and should not be used
+ * during the VarOrder stage of findModel.
+ */
+ static ArithVar minColLength(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y);
+ static ArithVar minRowLength(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y);
+
+ /**
+ * minBoundAndRowCount is a PreferenceFunction for preferring a variable
+ * without an asserted bound over variables with an asserted bound.
+ * If both have bounds or both do not have bounds,
+ * the rule falls back to minRowCount(...).
+ *
+ * This is a heuristic rule and should not be used
+ * during the VarOrder stage of findModel.
+ */
+ static ArithVar minBoundAndRowCount(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y);
+
+
+ /**
+ * This is the main simplex for DPLL(T) loop.
+ * It runs for at most maxIterations.
+ *
+ * Returns true iff it has found a conflict.
+ * d_conflictVariable will be set and the conflict for this row is reported.
+ */
+ bool searchForFeasibleSolution(uint32_t maxIterations);
+
+ enum SearchPeriod {BeforeDiffSearch, DuringDiffSearch, AfterDiffSearch, DuringVarOrderSearch, AfterVarOrderSearch};
+
+ bool findConflictOnTheQueue(SearchPeriod period);
+
+
+ /**
+ * Given the basic variable x_i,
+ * this function finds the smallest nonbasic variable x_j in the row of x_i
+ * in the tableau that can "take up the slack" to let x_i satisfy its bounds.
+ * This returns ARITHVAR_SENTINEL if none exists.
+ *
+ * More formally one of the following conditions must be satisfied:
+ * - lowerBound && a_ij < 0 && assignment(x_j) < upperbound(x_j)
+ * - lowerBound && a_ij > 0 && assignment(x_j) > lowerbound(x_j)
+ * - !lowerBound && a_ij > 0 && assignment(x_j) < upperbound(x_j)
+ * - !lowerBound && a_ij < 0 && assignment(x_j) > lowerbound(x_j)
+ *
+ */
+ template <bool lowerBound> ArithVar selectSlack(ArithVar x_i, PreferenceFunction pf);
+ ArithVar selectSlackLowerBound(ArithVar x_i, PreferenceFunction pf = minVarOrder) {
+ return selectSlack<true>(x_i, pf);
+ }
+ ArithVar selectSlackUpperBound(ArithVar x_i, PreferenceFunction pf = minVarOrder) {
+ return selectSlack<false>(x_i, pf);
+ }
+ /**
+ * Returns the smallest basic variable whose assignment is not consistent
+ * with its upper and lower bounds.
+ */
+ ArithVar selectSmallestInconsistentVar();
+
+ /**
+ * Given a non-basic variable that is know to not be updatable
+ * to a consistent value, construct and return a conflict.
+ * Follows section 4.2 in the CAV06 paper.
+ */
+ Node generateConflictAboveUpperBound(ArithVar conflictVar);
+ Node generateConflictBelowLowerBound(ArithVar conflictVar);
+
+public:
+ void increaseMax() {d_numVariables++;}
+
+
+ void clearQueue() {
+ d_queue.clear();
+ }
+
+
+ bool debugIsInCollectionQueue(ArithVar var) const{
+ Assert(d_queue.inCollectionMode());
+ return d_queue.collectionModeContains(var);
+ }
+
+ void reduceQueue(){
+ d_queue.reduce();
+ }
+
+ ArithPriorityQueue::const_iterator queueBegin() const{
+ return d_queue.begin();
+ }
+
+ ArithPriorityQueue::const_iterator queueEnd() const{
+ return d_queue.end();
+ }
+
+private:
+
+ /** Reports a conflict to on the output channel. */
+ void reportConflict(Node conflict){
+ d_conflictChannel(conflict);
+ ++(d_statistics.d_simplexConflicts);
+ }
+
+ template <bool above>
+ inline bool isAcceptableSlack(int sgn, ArithVar nonbasic){
+ return
+ ( above && sgn < 0 && d_partialModel.strictlyBelowUpperBound(nonbasic)) ||
+ ( above && sgn > 0 && d_partialModel.strictlyAboveLowerBound(nonbasic)) ||
+ (!above && sgn > 0 && d_partialModel.strictlyBelowUpperBound(nonbasic)) ||
+ (!above && sgn < 0 && d_partialModel.strictlyAboveLowerBound(nonbasic));
+ }
+
+ /**
+ * Checks a basic variable, b, to see if it is in conflict.
+ * If a conflict is discovered a node summarizing the conflict is returned.
+ * Otherwise, Node::null() is returned.
+ */
+ Node checkBasicForConflict(ArithVar b);
+
+ Node weakenConflict(bool aboveUpper, ArithVar basicVar);
+ Constraint weakestExplanation(bool aboveUpper, DeltaRational& surplus, ArithVar v, const Rational& coeff, bool& anyWeakening, ArithVar basic);
+
+ /** Gets a fresh variable from TheoryArith. */
+ ArithVar requestVariable(){
+ return d_arithVarMalloc.request();
+ }
+
+ /** Releases a requested variable from TheoryArith.*/
+ void releaseVariable(ArithVar v){
+ d_arithVarMalloc.release(v);
+ }
+
+
+ /** An error info keeps the information associated with the construction of an ErrorVariable. */
+ class ErrorInfo {
+ private:
+ /** The variable for which the error variable was constructed.*/
+ ArithVar d_variable;
+
+ // If false -> lowerbound
+ bool d_upperbound;
+
+ /** The violated constraint this was constructed to try to satisfy.*/
+ Constraint d_violated;
+
+ public:
+ ErrorInfo(ArithVar error, bool ub, const Constraint original)
+ : d_variable(error), d_upperbound(ub), d_violated(original) {}
+
+ ErrorInfo() :
+ d_variable(ARITHVAR_SENTINEL), d_upperbound(false), d_violated(NullConstraint){}
+
+ inline ArithVar getVariable() const {
+ return d_variable;
+ }
+
+ inline bool isUpperbound() const {
+ return d_upperbound;
+ }
+
+ inline bool errorIsLeqZero(const ArithPartialModel& d_pm) const{
+ return isUpperbound() ?
+ (d_pm.getAssignment(d_variable) <= d_violated->getValue()) :
+ (d_pm.getAssignment(d_variable) >= d_violated->getValue());
+ }
+
+ inline Constraint getConstraint() const {
+ return d_violated;
+ }
+
+ inline DeltaRational getErrorAmount(const ArithPartialModel& d_pm) const {
+ return isUpperbound() ?
+ (d_pm.getAssignment(d_variable) - d_violated->getValue()) :
+ (d_violated->getValue() - d_pm.getAssignment(d_variable));
+ }
+ };
+
+ typedef DenseMap<ErrorInfo> ErrorMap;
+
+ /** A map from the error variables to the associated ErrorInfo.*/
+ ErrorMap d_currentErrorVariables;
+
+ /** The optimization function is implicitly defined as
+ * f_i = d_optRow - d_negOptConstant
+ *
+ * d_optRow is a basic variable in the tableau.
+ * The tableau maintains that it is equal to the sum of -1^{!ub} * the error variables in
+ * d_currentErrorVariables.
+ *
+ * d_negOptConstant is explicitly the negation of the sum of the bounds that were violated
+ *
+ * assignment(f_i) <= 0 iff assignment(d_optRow) <= d_negOptConstant
+ */
+ /** The variable for the variable part of the optimization function.*/
+ ArithVar d_optRow;
+
+ /** The constant part of the optimization function.*/
+ DeltaRational d_negOptConstant;
+
+ inline bool belowThreshold() const {
+ return d_partialModel.getAssignment(d_optRow) <= d_negOptConstant;
+ }
+
+ /**
+ * A PrimalResponse represents the state that the primal simplex solver is in.
+ */
+ enum PrimalResponse {
+ // The optimization can decrease arbitrarily on some variable in the function
+ FoundUnboundedVariable,
+
+ // The optimization function has reached a threshold value and is checking back in
+ ReachedThresholdValue,
+
+ // Simplex has used up its pivot bound and is checking back in with its caller
+ UsedMaxPivots,
+
+ //Simplex can make progress on the pair of entering and leaving variables
+ MakeProgressOnLeaving,
+
+ //Simplex is not at a minimum but no leaving variable can be changed to help
+ NoProgressOnLeaving,
+
+ // Simplex has reached a minimum for its optimization function
+ GlobalMinimum
+ };
+
+ /**
+ * These fields are for sticking information for passing information back with the PrimalRespones.
+ * These fields should be ignored as unsafe/unknown unless you have a PrimalResponse that states
+ * the field makes sense.
+ */
+ struct PrimalPassBack {
+ public:
+ /**
+ * A variable s.t. its value can be increased/decreased arbitrarily to change the optimization function
+ * arbitrarily low.
+ */
+ ArithVar d_unbounded;
+
+ /** The leaving variable selection for primal simplex. */
+ ArithVar d_leaving;
+
+ /** The entering variable selection for primal simplex. */
+ ArithVar d_entering;
+
+ /** The new value for the leaving variable value for primal simplex.*/
+ DeltaRational d_nextEnteringValue;
+
+ PrimalPassBack() { clear(); }
+ void clear(){
+ d_unbounded = (d_leaving = (d_entering = ARITHVAR_SENTINEL));
+ d_nextEnteringValue = DeltaRational();
+ }
+
+ bool isClear() const {
+ return d_unbounded == ARITHVAR_SENTINEL &&
+ d_leaving == ARITHVAR_SENTINEL &&
+ d_entering == ARITHVAR_SENTINEL &&
+ d_nextEnteringValue.sgn() == 0;
+ }
+ } d_primalCarry;
+
+ uint32_t d_pivotsSinceErrorProgress;
+ uint32_t d_pivotsSinceOptProgress;
+ uint32_t d_pivotsSinceLastCheck;
+
+ typedef std::vector< const Tableau::Entry* > EntryVector;
+ EntryVector d_improvementCandidates;
+
+ PrimalResponse primal(uint32_t maxIterations);
+ PrimalResponse primalCheck();
+ Result::Sat primalConverge(int depth);
+ void driveOptToZero(ArithVar unbounded);
+ uint32_t contractErrorVariables(bool guaranteedSuccess);
+ bool checkForRowConflicts();
+ void restoreErrorVariables(ErrorMap& es);
+ void constructErrorVariables();
+ void constructOptimizationFunction();
+ void removeOptimizationFunction();
+ void reconstructOptimizationFunction();
+ ArithVar selectMinimumValid(ArithVar v, bool increasing);
+ ArithVar selectFirstValid(ArithVar v, bool increasing);
+
+ void reassertErrorConstraint(ArithVar e, ErrorMap& es, bool definitelyTrue, bool definitelyFalse);
+ void computeShift(ArithVar leaving, bool increasing, bool& progress, ArithVar& entering, DeltaRational& shift, const DeltaRational& minimumShift);
+
+ /** These fields are designed to be accessible to TheoryArith methods. */
+ class Statistics {
+ public:
+ IntStat d_statUpdateConflicts;
+
+ TimerStat d_findConflictOnTheQueueTime;
+
+ IntStat d_attemptBeforeDiffSearch, d_successBeforeDiffSearch;
+ IntStat d_attemptAfterDiffSearch, d_successAfterDiffSearch;
+ IntStat d_attemptDuringDiffSearch, d_successDuringDiffSearch;
+ IntStat d_attemptDuringVarOrderSearch, d_successDuringVarOrderSearch;
+ IntStat d_attemptAfterVarOrderSearch, d_successAfterVarOrderSearch;
+
+ IntStat d_weakeningAttempts, d_weakeningSuccesses, d_weakenings;
+ TimerStat d_weakenTime;
+
+
+ IntStat d_simplexConflicts;
+
+ // Primal stuffs
+ TimerStat d_primalTimer;
+ TimerStat d_internalTimer;
+
+ IntStat d_primalCalls;
+ IntStat d_primalSatCalls;
+ IntStat d_primalUnsatCalls;
+
+ IntStat d_primalPivots;
+ IntStat d_primalImprovingPivots;
+
+ IntStat d_primalThresholdReachedPivot;
+ IntStat d_primalThresholdReachedPivot_dropped;
+
+ IntStat d_primalReachedMaxPivots;
+ IntStat d_primalReachedMaxPivots_contractMadeProgress;
+ IntStat d_primalReachedMaxPivots_checkForConflictWorked;
+
+
+ IntStat d_primalGlobalMinimum;
+ IntStat d_primalGlobalMinimum_rowConflictWorked;
+ IntStat d_primalGlobalMinimum_firstHalfWasSat;
+ IntStat d_primalGlobalMinimum_firstHalfWasUnsat;
+ IntStat d_primalGlobalMinimum_contractMadeProgress;
+
+
+ IntStat d_unboundedFound;
+ IntStat d_unboundedFound_drive;
+ IntStat d_unboundedFound_dropped;
+
+
+ Statistics();
+ ~Statistics();
+ };
+
+ Statistics d_statistics;
+
+};/* class SimplexDecisionProcedure */
+
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
+
+#endif /* __CVC4__THEORY__ARITH__SIMPLEX_H */
+
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback