diff options
Diffstat (limited to 'libs/cassowary/cassowary/ClSimplexSolver.h')
-rw-r--r-- | libs/cassowary/cassowary/ClSimplexSolver.h | 588 |
1 files changed, 588 insertions, 0 deletions
diff --git a/libs/cassowary/cassowary/ClSimplexSolver.h b/libs/cassowary/cassowary/ClSimplexSolver.h new file mode 100644 index 0000000000..c187992728 --- /dev/null +++ b/libs/cassowary/cassowary/ClSimplexSolver.h @@ -0,0 +1,588 @@ +// $Id$ +// +// Cassowary Incremental Constraint Solver +// Original Smalltalk Implementation by Alan Borning +// This C++ Implementation by Greg J. Badros, <gjb@cs.washington.edu> +// http://www.cs.washington.edu/homes/gjb +// (C) 1998, 1999 Greg J. Badros and Alan Borning +// See ../LICENSE for legal details regarding this software +// +// ClSimplexSolver.h + +#ifndef ClSimplexSolver_H +#define ClSimplexSolver_H + +#if defined(HAVE_CONFIG_H) && !defined(CONFIG_H_INCLUDED) && !defined(CONFIG_INLINE_H_INCLUDED) +#include <cassowary/config-inline.h> +#define CONFIG_INLINE_H_INCLUDED +#endif + +#include <stack> +#include <list> +#include <iostream> +#include "Cassowary.h" +#include "ClSolver.h" +#include "ClTableau.h" +#include "ClLinearInequality.h" +#include "ClStrength.h" +#include "ClStayConstraint.h" +#include "ClEditConstraint.h" +#include "ClSlackVariable.h" +#include "ClObjectiveVariable.h" +#include "ClErrors.h" +#include "ClTypedefs.h" + +using std::list; +using std::stack; +using std::ostream; + +class ClVariable; +class ClPoint; +class ExCLRequiredFailureWithExplanation; + + +// ClSimplexSolver encapsulates the solving behaviour +// of the cassowary algorithm +class ClSimplexSolver : public ClSolver, public ClTableau { + protected: + class ClEditInfo; + typedef list<ClEditInfo *> ClEditInfoList; + + ClEditInfo *PEditInfoFromClv(ClVariable clv) { + ClEditInfoList::iterator it = _editInfoList.begin(); + for (; it != _editInfoList.end(); ++it) { + ClEditInfo *pei = (*it); + if (pei->_clv == clv) + return pei; + } + return 0; + } + + public: + + // Constructor + ClSimplexSolver() : + ClSolver(), + _objective(ClVariable(new ClObjectiveVariable("Z"))), + _slackCounter(0), + _artificialCounter(0), +#ifdef CL_FIND_LEAK + _cArtificialVarsDeleted(0), +#endif + _dummyCounter(0), + _epsilon(1e-8), + _fResetStayConstantsAutomatically(true), + _fNeedsSolving(false), + _fExplainFailure(false), + _pfnResolveCallback(0), + _pfnCnSatCallback(0) + { + _rows[_objective] = new ClLinearExpression(); + // start out with no edit variables + _stkCedcns.push(0); +#ifdef CL_TRACE + std::cerr << "objective row new@ " << _rows[_objective] << std::endl; +#endif + } + + virtual ~ClSimplexSolver(); + + // Add constraints so that lower<=var<=upper. (nil means no bound.) + ClSimplexSolver &AddLowerBound(ClVariable v, Number lower) + { + ClLinearInequality *pcn = new ClLinearInequality(ClLinearExpression(v - lower)); + return AddConstraint(pcn); + } + ClSimplexSolver &AddUpperBound(ClVariable v, Number upper) + { + ClLinearInequality *pcn = new ClLinearInequality(ClLinearExpression(upper - v)); + return AddConstraint(pcn); + } + ClSimplexSolver &AddBounds(ClVariable v, Number lower, Number upper) + { AddLowerBound(v,lower); AddUpperBound(v,upper); return *this; } + + // Add the constraint cn to the tableau + ClSimplexSolver &AddConstraint(ClConstraint *const pcn); + +#ifndef CL_NO_DEPRECATED + // Deprecated! --02/19/99 gjb + ClSimplexSolver &AddConstraint(ClConstraint &cn) + { return AddConstraint(&cn); } +#endif + + // Add an edit constraint for "v" with given strength + ClSimplexSolver &AddEditVar(const ClVariable v, const ClStrength &strength = ClsStrong(), + double weight = 1.0 ) + { + ClEditConstraint *pedit = new ClEditConstraint(v, strength, weight); + return AddConstraint(pedit); + } + + ClSimplexSolver &RemoveEditVar(ClVariable v) + { + ClEditInfo *pcei = PEditInfoFromClv(v); + if (!pcei) { + throw ExCLEditMisuse("Removing edit variable that was not found"); + } + ClConstraint *pcnEdit = pcei->_pconstraint; + RemoveConstraint(pcnEdit); + delete pcnEdit; + return *this; + } + + // BeginEdit() should be called before sending + // Resolve() messages, after adding the appropriate edit variables + ClSimplexSolver &BeginEdit() + { + if (_editInfoList.size() == 0) { + throw ExCLEditMisuse("BeginEdit called, but no edit variable"); + } + // may later want to do more in here + _infeasibleRows.clear(); + ResetStayConstants(); + _stkCedcns.push(_editInfoList.size()); + return *this; + } + + // EndEdit should be called after editing has finished + // for now, it just removes edit variables added from before the last BeginEdit + ClSimplexSolver &EndEdit() + { + if (_editInfoList.size() == 0) + throw ExCLEditMisuse("EndEdit called but no edit variables"); + Resolve(); + _stkCedcns.pop(); + RemoveEditVarsTo(_stkCedcns.top()); + // may later want to do more in here + return *this; + } + + // RemoveAllEditVars() just eliminates all the edit constraints + // that were added + ClSimplexSolver &RemoveAllEditVars() { RemoveEditVarsTo(0); return *this; } + + // remove the last added edit vars to leave only n edit vars left + ClSimplexSolver &RemoveEditVarsTo(unsigned int n); + + int numEditVars() const + { return _editInfoList.size(); } + + // Add weak stays to the x and y parts of each point. These have + // increasing weights so that the solver will try to satisfy the x + // and y stays on the same point, rather than the x stay on one and + // the y stay on another. + ClSimplexSolver &AddPointStays(const vector<const ClPoint *> &listOfPoints, + const ClStrength &strength = ClsWeak()); + + ClSimplexSolver &AddPointStay(const ClVariable vx, const ClVariable vy, + const ClStrength &strength = ClsWeak(), + double weight = 1.0) + { AddStay(vx,strength,weight); AddStay(vy,strength,weight); return *this; } + + ClSimplexSolver &AddPointStay(const ClPoint &clp, + const ClStrength &strength = ClsWeak(), + double weight = 1.0); + + + // Add a stay of the given strength (default to weak) of v to the tableau + ClSimplexSolver &AddStay(const ClVariable v, + const ClStrength &strength = ClsWeak(), double weight = 1.0 ) + { + ClStayConstraint *pcn = new ClStayConstraint(v,strength,weight); + return AddConstraint(pcn); + } + + // Remove the constraint cn from the tableau + // Also remove any error variable associated with cn + ClSimplexSolver &RemoveConstraint(ClConstraint *const pcn) + { RemoveConstraintInternal(pcn); pcn->removedFrom(*this); return *this; } + +#ifndef CL_NO_DEPRECATED + // Deprecated! --02/19/99 gjb + ClSimplexSolver &RemoveConstraint(ClConstraint &cn) + { return RemoveConstraint(&cn); } +#endif + + + // Re-initialize this solver from the original constraints, thus + // getting rid of any accumulated numerical problems. (Actually, we + // haven't definitely observed any such problems yet) + void Reset(); + + // Re-solve the current collection of constraints, given the new + // values for the edit variables that have already been + // suggested (see SuggestValue() method) + // This is not guaranteed to work if you remove an edit constraint + // from the middle of the edit constraints you added + // (e.g., edit A, edit B, edit C, remove B -> this will fail!) + // DEPRECATED + void Resolve(); + + // Re-solve the current collection of constraints for new values for + // the constants of the edit variables. + // This is implemented in terms of SuggestValue-s, and is + // less efficient than that more natural interface + void Resolve(const vector<Number> &newEditConstants); + + // Convenience function for Resolve-s of two variables + void Resolve(Number x, Number y) + { + vector<Number> vals; + vals.push_back(x); + vals.push_back(y); + Resolve(vals); + } + + // Suggest a new value for an edit variable + // the variable needs to be added as an edit variable + // and BeginEdit() needs to be called before this is called. + // The tableau will not be solved completely until + // after Resolve() has been called + ClSimplexSolver &SuggestValue(ClVariable v, Number x); + + // Set and check whether or not the solver will attempt to compile + // an explanation of failure when a required constraint conflicts + // with another required constraint + ClSimplexSolver &SetExplaining(bool f) + { _fExplainFailure = f; return *this; } + + bool FIsExplaining() const + { return _fExplainFailure; } + + // If autosolving has been turned off, client code needs + // to explicitly call solve() before accessing variables + // values + ClSimplexSolver &Solve() + { +#ifdef CL_SOLVER_CHECK_INTEGRITY + AssertValid(); +#endif + if (_fNeedsSolving) + { + Optimize(_objective); + SetExternalVariables(); +#ifdef CL_TRACE_VERBOSE + std::cerr << "Manual solve actually solving." << std::endl; +#endif + } + return *this; + } + + ClSimplexSolver &SetEditedValue(ClVariable v, double n) + { + if (!FContainsVariable(v)) + { + ChangeClv(v,n); + return *this; + } + + if (!ClApprox(n, v.Value())) + { + AddEditVar(v); + BeginEdit(); + SuggestValue(v,n); + EndEdit(); + } + return *this; + } + + // Solver contains the variable if it's in either the columns + // list or the rows list + bool FContainsVariable(const ClVariable v) + { return ColumnsHasKey(v) || RowExpression(v); } + + ClSimplexSolver &AddVar(const ClVariable v) + { if (!FContainsVariable(v)) + { + AddStay(v); +#ifdef CL_TRACE + std::cerr << "added initial stay on " << v << std::endl; +#endif + } + return *this; } + + typedef void (*PfnResolveCallback)(ClSimplexSolver *psolver); + + void SetResolveCallback(PfnResolveCallback pfn) + { _pfnResolveCallback = pfn; } + + typedef void (*PfnCnSatCallback)(ClSimplexSolver *psolver, + ClConstraint *pcn, bool fSatisfied); + + void SetCnSatCallback(PfnCnSatCallback pfn) + { _pfnCnSatCallback = pfn; } + +#ifndef CL_NO_IO + friend ostream &operator<<(ostream &xo, const ClSimplexSolver &tableau); + + ostream &PrintOn(ostream &xo) const; + + ostream &PrintInternalInfo(ostream &xo) const; + + ostream &PrintOnVerbose(ostream &xo) const + { PrintOn(xo); PrintInternalInfo(xo); xo << std::endl; return xo; } + +#endif + + const ClConstraintToVarMap &ConstraintMap() const + { return _markerVars; } + + const ClVarToConstraintMap &MarkerMap() const + { return _constraintsMarked; } + + bool FIsConstraintSatisfied(const ClConstraint *const pcn) const; + + // DEPRECATED + bool FIsConstraintSatisfied(const ClConstraint &pcn) const + { return FIsConstraintSatisfied(&pcn); } + + // re-set all the external variables to their current values + // most importantly, this re-calls all the ChangeClv callbacks + // (which might be used to copy the ClVariable's value to another + // variable) + void UpdateExternalVariables() + { SetExternalVariables(); } + + // A. Beurive' Tue Jul 6 17:05:39 CEST 1999 + void ChangeStrengthAndWeight(ClConstraint *pcn, const ClStrength &strength, double weight); + void ChangeStrength(ClConstraint *pcn, const ClStrength &strength); + void ChangeWeight(ClConstraint *pcn, double weight); + // void DisplayObjective(); + + // Each of the non-required stays will be represented by an equation + // of the form + // v = c + eplus - eminus + // where v is the variable with the stay, c is the previous value of + // v, and eplus and eminus are slack variables that hold the error + // in satisfying the stay constraint. We are about to change + // something, and we want to fix the constants in the equations + // representing the stays. If both eplus and eminus are nonbasic + // they have value 0 in the current solution, meaning the previous + // stay was exactly satisfied. In this case nothing needs to be + // changed. Otherwise one of them is basic, and the other must + // occur only in the Expression for that basic error variable. + // Reset the Constant in this Expression to 0. + void ResetStayConstants(); + + ClSimplexSolver &SetAutoResetStayConstants(bool f) + { _fResetStayConstantsAutomatically = f; if (f) ResetStayConstants(); return *this; } + + bool FIsAutoResetStayConstants() const + { return _fResetStayConstantsAutomatically; } + + protected: + + // ClEditInfo is a privately-used class + // that just wraps a constraint, its positive and negative + // error variables, and its prior edit Constant. + // It is used as values in _editInfoList, and replaces + // the parallel vectors of error variables and previous edit + // constants from the smalltalk version of the code. + class ClEditInfo { + friend class ClSimplexSolver; + public: + + // These instances own none of the pointers; + // the tableau row (the Expression) owns the peplus, peminus, + // and AddEditVar/RemoveEditVar pair or the client code owns + // the constraint object + ClEditInfo(ClVariable clv, + ClEditConstraint *pconstraint, + ClVariable eplus, ClVariable eminus, + Number prevEditConstant) + :_clv(clv), + _pconstraint(pconstraint), + _clvEditPlus(eplus), _clvEditMinus(eminus), + _prevEditConstant(prevEditConstant) + { } + + ~ClEditInfo() + { } + + ostream &PrintOn(ostream &xo) const + { xo << _clv << " -> [" << _clvEditPlus << ", " << _clvEditMinus << "](" + << _prevEditConstant << ")@" << " -- " + << *_pconstraint; + return xo; } + + friend ostream &operator<<(ostream &xo, const ClEditInfo &cei) + { return cei.PrintOn(xo); } + + private: + ClVariable _clv; + ClConstraint *_pconstraint; + ClVariable _clvEditPlus; + ClVariable _clvEditMinus; + Number _prevEditConstant; + }; + + // Add the constraint expr=0 to the inequality tableau using an + // artificial variable. To do this, create an artificial variable + // av and Add av=expr to the inequality tableau, then make av be 0. + // (Raise an exception if we can't attain av=0.) + // (Raise an exception if we can't attain av=0.) If the Add fails, + // prepare an explanation in e that describes why it failed (note + // that an empty explanation is considered to mean the explanation + // encompasses all active constraints. + bool AddWithArtificialVariable(ClLinearExpression &pexpr, + ExCLRequiredFailureWithExplanation &e); + + // Using the given equation (av = cle) build an explanation which + // implicates all constraints used to construct the equation. That + // is, everything for which the variables in the equation are markers. + // Thanks to Steve Wolfman for the implementation of the explanation feature + void BuildExplanation(ExCLRequiredFailureWithExplanation & e, + ClVariable av, + const ClLinearExpression * pcle); + + // We are trying to Add the constraint expr=0 to the appropriate + // tableau. Try to Add expr directly to the tableax without + // creating an artificial variable. Return true if successful and + // false if not. + bool TryAddingDirectly(ClLinearExpression &pexpr); + + // We are trying to Add the constraint expr=0 to the tableaux. Try + // to choose a subject (a variable to become basic) from among the + // current variables in expr. If expr contains any unrestricted + // variables, then we must choose an unrestricted variable as the + // subject. Also, if the subject is new to the solver we won't have + // to do any substitutions, so we prefer new variables to ones that + // are currently noted as parametric. If expr contains only + // restricted variables, if there is a restricted variable with a + // negative coefficient that is new to the solver we can make that + // the subject. Otherwise we can't find a subject, so return nil. + // (In this last case we have to Add an artificial variable and use + // that variable as the subject -- this is done outside this method + // though.) + // + // Note: in checking for variables that are new to the solver, we + // ignore whether a variable occurs in the objective function, since + // new slack variables are added to the objective function by + // 'NewExpression:', which is called before this method. + ClVariable ChooseSubject(ClLinearExpression &pexpr); + + // Each of the non-required edits will be represented by an equation + // of the form + // v = c + eplus - eminus + // where v is the variable with the edit, c is the previous edit + // value, and eplus and eminus are slack variables that hold the + // error in satisfying the edit constraint. We are about to change + // something, and we want to fix the constants in the equations + // representing the edit constraints. If one of eplus and eminus is + // basic, the other must occur only in the Expression for that basic + // error variable. (They can't both be basic.) Fix the Constant in + // this Expression. Otherwise they are both nonbasic. Find all of + // the expressions in which they occur, and fix the constants in + // those. See the UIST paper for details. + // (This comment was for resetEditConstants(), but that is now + // gone since it was part of the screwey vector-based interface + // to resolveing. --02/15/99 gjb) + void DeltaEditConstant(Number delta, ClVariable pv1, ClVariable pv2); + + // We have set new values for the constants in the edit constraints. + // Re-Optimize using the dual simplex algorithm. + void DualOptimize(); + + // Make a new linear Expression representing the constraint cn, + // replacing any basic variables with their defining expressions. + // Normalize if necessary so that the Constant is non-negative. If + // the constraint is non-required give its error variables an + // appropriate weight in the objective function. + ClLinearExpression *NewExpression(const ClConstraint *pcn, + /* output to */ + ClVariable &clvEplus, + ClVariable &clvEminus, + Number &prevEConstant); + + // Minimize the value of the objective. (The tableau should already + // be feasible.) + void Optimize(ClVariable zVar); + + // Do a Pivot. Move entryVar into the basis (i.e. make it a basic variable), + // and move exitVar out of the basis (i.e., make it a parametric variable) + void Pivot(ClVariable entryVar, ClVariable exitVar); + + // Set the external variables known to this solver to their appropriate values. + // Set each external basic variable to its value, and set each + // external parametric variable to 0. (It isn't clear that we will + // ever have external parametric variables -- every external + // variable should either have a stay on it, or have an equation + // that defines it in terms of other external variables that do have + // stays. For the moment I'll put this in though.) Variables that + // are internal to the solver don't actually store values -- their + // values are just implicit in the tableu -- so we don't need to set + // them. + void SetExternalVariables(); + + // this gets called by RemoveConstraint and by AddConstraint when the + // contraint we're trying to Add is inconsistent + ClSimplexSolver &RemoveConstraintInternal(const ClConstraint *const pcn); + + void ChangeClv(ClVariable clv, Number n) { + clv.ChangeValue(n); + if (_pfnChangeClvCallback) + _pfnChangeClvCallback(&clv,this); + } + + /// instance variables + + // the arrays of positive and negative error vars for the stay constraints + // (need both positive and negative since they have only non-negative values) + ClVarVector _stayMinusErrorVars; + ClVarVector _stayPlusErrorVars; + + // give error variables for a non required constraint, + // maps to ClSlackVariable-s + ClConstraintToVarSetMap _errorVars; + + // Return a lookup table giving the marker variable for each + // constraint (used when deleting a constraint). + ClConstraintToVarMap _markerVars; + + // Reverse of the above-- a lookup table giving the constraint + // for each marker variable (used when building failure explanations) + ClVarToConstraintMap _constraintsMarked; + + ClVariable _objective; + + // Map edit variables to their constraints, errors, and prior + // values + ClEditInfoList _editInfoList; + + int _slackCounter; + int _artificialCounter; +#ifdef CL_FIND_LEAK + int _cArtificialVarsDeleted; +#endif + int _dummyCounter; + const double _epsilon; + + bool _fResetStayConstantsAutomatically; + bool _fNeedsSolving; + bool _fExplainFailure; + + PfnResolveCallback _pfnResolveCallback; + PfnCnSatCallback _pfnCnSatCallback; + + // C-style extension mechanism so I + // don't have to wrap ScwmClSolver separately + void *_pv; + + // a stack of the number of edit constraints + // that existed at the prior BeginEdit. + // an EndEdit needs to pop off the top value, + // then remove constraints to get down + // to the # of constraints as in _stkCedcns.top() + stack<int> _stkCedcns; + + +#ifndef CL_NO_IO + +friend ostream &PrintTo(ostream &xo, const ClSimplexSolver::ClEditInfoList &listPEditInfo); +friend ostream &operator<<(ostream &xo, const ClSimplexSolver::ClEditInfoList &listPEditInfo); + +#endif + +}; + +#endif // ClSimplexSolver_H |