summaryrefslogtreecommitdiff
path: root/libs/cassowary/ClSimplexSolver.cc
diff options
context:
space:
mode:
Diffstat (limited to 'libs/cassowary/ClSimplexSolver.cc')
-rw-r--r--libs/cassowary/ClSimplexSolver.cc1633
1 files changed, 1633 insertions, 0 deletions
diff --git a/libs/cassowary/ClSimplexSolver.cc b/libs/cassowary/ClSimplexSolver.cc
new file mode 100644
index 0000000000..424a5d5aab
--- /dev/null
+++ b/libs/cassowary/ClSimplexSolver.cc
@@ -0,0 +1,1633 @@
+// $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.cc
+
+using namespace std;
+
+#include <cassowary/debug.h>
+#include <cassowary/ClSimplexSolver.h>
+#include <cassowary/ClErrors.h>
+#include <cassowary/ClVariable.h>
+#include <cassowary/ClPoint.h>
+#include <cassowary/ClSlackVariable.h>
+#include <cassowary/ClObjectiveVariable.h>
+#include <cassowary/ClDummyVariable.h>
+#include <cassowary/cl_auto_ptr.h>
+
+#include <algorithm>
+#include <float.h>
+#include <sstream>
+#include <queue>
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#define CONFIG_H_INCLUDED
+#endif
+
+const char *szCassowaryVersion = VERSION;
+
+// Need to delete all expressions
+// and all slack and dummy variables
+// See NewExpression -- all allocation is done in there
+ClSimplexSolver::~ClSimplexSolver()
+{
+#ifdef CL_SOLVER_STATS
+ cerr << "_slackCounter == " << _slackCounter
+ << "\n_artificialCounter == " << _artificialCounter
+ << "\n_dummyCounter == " << _dummyCounter << endl;
+ cerr << "stayMinusErrorVars " << _stayMinusErrorVars.size() << ", "
+ << "stayPlusErrorVars " << _stayPlusErrorVars.size() << ", "
+ << "errorVars " << _errorVars.size() << ", "
+ << "markerVars " << _markerVars.size() << endl;
+#endif
+ // Cannot print *this here, since local ClVariable-s may have been
+ // destructed already
+}
+
+// Add the constraint cn to the tableau
+ClSimplexSolver &
+ClSimplexSolver::AddConstraint(ClConstraint *const pcn)
+{
+#ifdef CL_TRACE
+ Tracer TRACER(__FUNCTION__);
+ cerr << "(" << *pcn << ")" << endl;
+#endif
+
+ if (!pcn->FIsOkayForSimplexSolver()) {
+ throw ExCLTooDifficultSpecial("SimplexSolver cannot handle this constraint object");
+ }
+
+ if (pcn->IsStrictInequality()) {
+ // cannot handle strict inequalities
+ throw ExCLStrictInequalityNotAllowed();
+ }
+
+ if (pcn->ReadOnlyVars().size() > 0) {
+ // cannot handle read-only vars
+ throw ExCLReadOnlyNotAllowed();
+ }
+
+ if (pcn->IsEditConstraint())
+ {
+ ClEditConstraint *pcnEdit = dynamic_cast<ClEditConstraint *>(pcn);
+ const ClVariable &v = pcnEdit->variable();
+ if (!v.IsExternal() ||
+ (!FIsBasicVar(v) && !ColumnsHasKey(v)))
+ {
+ // we could try to make this case work,
+ // but it'd be unnecessarily inefficient --
+ // and probably easier for the client application
+ // to deal with
+ throw ExCLEditMisuse("(ExCLEditMisuse) Edit constraint on variable not in tableau.");
+ }
+ ClEditInfo *pcei = PEditInfoFromClv(v);
+ if (pcei)
+ {
+ // we need to only add a partial _editInfoList entry for this
+ // edit constraint since the variable is already being edited.
+ // otherwise a more complete entry is added later in this function
+ _editInfoList.push_back(new ClEditInfo(v, NULL, clvNil, clvNil, 0));
+ return *this;
+ }
+ }
+
+ ClVariable clvEplus, clvEminus;
+ Number prevEConstant;
+ ClLinearExpression *pexpr = NewExpression(pcn, /* output to: */
+ clvEplus,clvEminus,
+ prevEConstant);
+ bool fAddedOkDirectly = false;
+
+ try
+ {
+ // If possible Add expr directly to the appropriate tableau by
+ // choosing a subject for expr (a variable to become basic) from
+ // among the current variables in expr. If this doesn't work use an
+ // artificial variable. After adding expr re-Optimize.
+ fAddedOkDirectly = TryAddingDirectly(*pexpr);
+ }
+ catch (ExCLRequiredFailure &error)
+ {
+#ifdef CL_TRACE
+ cerr << "could not Add directly -- caught ExCLRequiredFailure error" << endl;
+#endif
+ RemoveConstraintInternal(pcn);
+ throw;
+ }
+
+ if (!fAddedOkDirectly)
+ { // could not Add directly
+ ExCLRequiredFailureWithExplanation e;
+ if (!AddWithArtificialVariable(*pexpr, e))
+ {
+#ifdef CL_DEBUG_FAILURES
+ cerr << "Failed solve! Could not Add constraint.\n"
+ << *this << endl;
+#endif
+ RemoveConstraintInternal(pcn);
+ if (FIsExplaining())
+ throw e;
+ else
+ throw ExCLRequiredFailure();
+ }
+ }
+
+ _fNeedsSolving = true;
+
+ if (pcn->IsEditConstraint())
+ {
+ ClEditConstraint *pcnEdit = dynamic_cast<ClEditConstraint *>(pcn);
+ ClVariable clv = pcnEdit->variable();
+ _editInfoList.push_back(new ClEditInfo(clv, pcnEdit, clvEplus, clvEminus,
+ prevEConstant));
+ }
+
+ if (_fAutosolve)
+ {
+ Optimize(_objective);
+ SetExternalVariables();
+ }
+
+ pcn->addedTo(*this);
+ return *this;
+}
+
+// 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 &
+ClSimplexSolver::AddPointStays(const vector<const ClPoint *> &listOfPoints,
+ const ClStrength &strength)
+{
+#ifdef CL_TRACE
+ Tracer TRACER(__FUNCTION__);
+#endif
+
+ vector<const ClPoint *>::const_iterator it = listOfPoints.begin();
+ double weight = 1.0;
+ static const double multiplier = 2.0;
+ for ( ; it != listOfPoints.end(); ++it )
+ {
+ AddPointStay((*it)->X(),(*it)->Y(),strength,weight);
+ weight *= multiplier;
+ }
+ return *this;
+}
+
+ClSimplexSolver &
+ClSimplexSolver::AddPointStay(const ClPoint &clp, const ClStrength &strength, double weight)
+{
+ AddPointStay(clp.X(),clp.Y(),strength,weight);
+ return *this;
+}
+
+
+ClSimplexSolver &
+ClSimplexSolver::RemoveEditVarsTo(unsigned int n)
+{
+ queue<ClVariable> qclv;
+ ClVarSet sclvStillEditing; // Set of edit variables that we need to *not* remove
+#ifdef DEBUG_NESTED_EDITS
+ cerr << __FUNCTION__ << " " << n << endl;
+#endif
+ unsigned int i = 0;
+ for ( ClEditInfoList::const_iterator it = _editInfoList.begin();
+ (it != _editInfoList.end() && _editInfoList.size() != static_cast<unsigned int>(n));
+ ++it, ++i )
+ {
+ const ClEditInfo *pcei = (*it);
+ assert(pcei);
+#ifdef DEBUG_NESTED_EDITS
+ cerr << __FUNCTION__ << "Checking " << pcei->_clv
+ << ", index = " << i << endl;
+#endif
+ if (i >= n)
+ qclv.push(pcei->_clv);
+ else
+ sclvStillEditing.insert(pcei->_clv);
+ }
+ while (!qclv.empty())
+ {
+ ClVariable clv = qclv.front();
+ // only remove the variable if it's not in the set of variable
+ // from a previous nested outer edit
+ // e.g., if I do:
+ // Edit x,y
+ // Edit w,h,x,y
+ // EndEdit
+ // The end edit needs to only get rid of the edits on w,h
+ // not the ones on x,y
+ if (sclvStillEditing.find(clv) == sclvStillEditing.end())
+ {
+#ifdef DEBUG_NESTED_EDITS
+ cerr << __FUNCTION__ << ": Removing " << clv << endl;
+#endif
+ RemoveEditVar(clv);
+ }
+#ifdef DEBUG_NESTED_EDITS
+ else
+ {
+ cerr << __FUNCTION__ << ": Not removing " << clv << endl;
+ }
+#endif
+ qclv.pop();
+ }
+ while (_editInfoList.size() > n) {
+ _editInfoList.pop_back();
+ }
+
+ return *this;
+}
+
+
+/* A predicate used for remove_if */
+class VarInVarSet : public unary_function<ClVariable,bool> {
+public:
+ VarInVarSet(const ClVarSet &clvset) :
+ _set(clvset),
+ _setEnd(clvset.end())
+ { }
+
+ bool operator ()(ClVariable clv) const {
+ return (_set.find(clv) != _setEnd);
+ }
+
+private:
+ const ClVarSet &_set;
+ const ClVarSet::iterator _setEnd;
+};
+
+
+
+// Remove the constraint cn from the tableau
+// Also remove any error variable associated with cn
+ClSimplexSolver &
+ClSimplexSolver::RemoveConstraintInternal(const ClConstraint *const pcn)
+{
+#ifdef CL_TRACE
+ Tracer TRACER(__FUNCTION__);
+ cerr << "(" << *pcn << ")" << endl;
+#endif
+
+ // We are about to remove a constraint. There may be some stay
+ // constraints that were unsatisfied previously -- if we just
+ // removed the constraint these could come into play. Instead,
+ // Reset all of the stays so that things should stay where they are
+ // at the moment.
+ _fNeedsSolving = true;
+
+ ResetStayConstants();
+
+ // remove any error variables from the objective function
+ ClLinearExpression *pzRow = RowExpression(_objective);
+
+#ifdef CL_TRACE
+ cerr << _errorVars << endl << endl;
+#endif
+
+ ClConstraintToVarSetMap::iterator
+ it_eVars = _errorVars.find(pcn);
+ bool fFoundErrorVar = (it_eVars != _errorVars.end());
+
+ if (fFoundErrorVar)
+ {
+ ClVarSet &eVars = (*it_eVars).second;
+ ClVarSet::iterator it = eVars.begin();
+ for ( ; it != eVars.end(); ++it )
+ {
+ const ClLinearExpression *pexpr = RowExpression(*it);
+ if (pexpr == NULL )
+ {
+ pzRow->AddVariable(*it,-pcn->weight() * pcn->strength().symbolicWeight().AsDouble(),
+ _objective,*this);
+ }
+ else
+ { // the error variable was in the basis
+ pzRow->AddExpression(*pexpr,-pcn->weight() * pcn->strength().symbolicWeight().AsDouble(),
+ _objective,*this);
+ }
+ }
+ }
+
+ ClConstraintToVarMap::iterator
+ it_marker = _markerVars.find(pcn);
+ if (it_marker == _markerVars.end())
+ { // could not find the constraint
+ throw ExCLConstraintNotFound();
+ }
+ // try to make the marker variable basic if it isn't already
+ const ClVariable marker = (*it_marker).second;
+ _markerVars.erase(it_marker);
+ _constraintsMarked.erase(marker);
+#ifdef CL_TRACE
+ cerr << "Looking to remove var " << marker << endl;
+#endif
+ if (!FIsBasicVar(marker))
+ { // not in the basis, so need to do some work
+ // first choose which variable to move out of the basis
+ // only consider restricted basic variables
+ ClVarSet &col = _columns[marker];
+ ClVarSet::iterator it_col = col.begin();
+#ifdef CL_TRACE
+ cerr << "Must Pivot -- columns are " << col << endl;
+#endif
+
+ ClVariable exitVar = clvNil;
+ bool fExitVarSet = false;
+ double minRatio = 0.0;
+ for ( ; it_col != col.end(); ++it_col)
+ {
+ const ClVariable &v = *it_col;
+ if (v.IsRestricted() )
+ {
+ const ClLinearExpression *pexpr = RowExpression(v);
+ assert(pexpr != NULL );
+ Number coeff = pexpr->CoefficientFor(marker);
+#ifdef CL_TRACE
+ cerr << "Marker " << marker << "'s coefficient in " << *pexpr << " is "
+ << coeff << endl;
+#endif
+ // only consider negative coefficients
+ if (coeff < 0.0)
+ {
+ Number r = - pexpr->Constant() / coeff;
+ if (!fExitVarSet || r < minRatio)
+ {
+ minRatio = r;
+ exitVar = v;
+ fExitVarSet = true;
+ }
+ }
+ }
+ }
+ // if we didn't set exitvar above, then either the marker
+ // variable has a positive coefficient in all equations, or it
+ // only occurs in equations for unrestricted variables. If it
+ // does occur in an equation for a restricted variable, pick the
+ // equation that gives the smallest ratio. (The row with the
+ // marker variable will become infeasible, but all the other rows
+ // will still be feasible; and we will be dropping the row with
+ // the marker variable. In effect we are removing the
+ // non-negativity restriction on the marker variable.)
+ if (!fExitVarSet)
+ {
+#ifdef CL_TRACE
+ cerr << "exitVar did not get set" << endl;
+#endif
+ it_col = col.begin();
+ for ( ; it_col != col.end(); ++it_col)
+ {
+ ClVariable v = *it_col;
+ if (v.IsRestricted() )
+ {
+ const ClLinearExpression *pexpr = RowExpression(v);
+ assert(pexpr != NULL);
+ Number coeff = pexpr->CoefficientFor(marker);
+ Number r = pexpr->Constant() / coeff;
+ if (!fExitVarSet || r < minRatio)
+ {
+ minRatio = r;
+ exitVar = v;
+ fExitVarSet = true;
+ }
+ }
+ }
+ }
+
+ if (!fExitVarSet)
+ { // exitVar is still nil
+ // If col is empty, then exitVar doesn't occur in any equations,
+ // so just remove it. Otherwise pick an exit var from among the
+ // unrestricted variables whose equation involves the marker var
+ if (col.size() == 0)
+ {
+ RemoveColumn(marker);
+ }
+ else
+ {
+ // A. Beurive' Tue Sep 14 18:26:05 CEST 1999
+ // Don't pick the objective, or it will be removed!
+ it_col = col.begin();
+ for ( ; it_col != col.end(); ++it_col)
+ {
+ ClVariable v = *it_col;
+ if (v != _objective)
+ {
+ exitVar = v;
+ fExitVarSet = true;
+ break;
+ }
+ }
+ assert(fExitVarSet == true);
+ }
+ }
+
+ if (fExitVarSet)
+ {
+ Pivot(marker,exitVar);
+ }
+ }
+
+ if (FIsBasicVar(marker))
+ {
+ ClLinearExpression *pexpr = RemoveRow(marker);
+#ifdef CL_TRACE
+ cerr << "delete@ " << pexpr << endl;
+#endif
+ delete pexpr;
+ }
+
+ // Delete any error variables. If cn is an inequality, it also
+ // contains a slack variable; but we use that as the marker variable
+ // and so it has been deleted when we removed its row.
+ if (fFoundErrorVar)
+ {
+ ClVarSet &eVars = (*it_eVars).second;
+ ClVarSet::iterator it = eVars.begin();
+ for ( ; it != eVars.end(); ++it )
+ {
+ ClVariable v = (*it);
+ if (v != marker)
+ {
+ RemoveColumn(v);
+ }
+ }
+ }
+
+ if (pcn->isStayConstraint())
+ {
+ // iterate over the stay{Plus,Minus}ErrorVars and remove those
+ // variables v in those vectors that are also in set eVars
+ if (fFoundErrorVar)
+ {
+ ClVarSet &eVars = (*it_eVars).second;
+ _stayPlusErrorVars
+ .erase(remove_if(_stayPlusErrorVars.begin(),_stayPlusErrorVars.end(),
+ VarInVarSet(eVars)),
+ _stayPlusErrorVars.end());
+ _stayMinusErrorVars
+ .erase(remove_if(_stayMinusErrorVars.begin(),_stayMinusErrorVars.end(),
+ VarInVarSet(eVars)),
+ _stayMinusErrorVars.end());
+ }
+ }
+ else if (pcn->IsEditConstraint())
+ {
+ const ClEditConstraint *pcnEdit = dynamic_cast<const ClEditConstraint *>(pcn);
+ const ClVariable clv = pcnEdit->variable();
+ ClEditInfo *pcei = PEditInfoFromClv(clv);
+ assert(pcei);
+ ClVariable clvEditMinus = pcei->_clvEditMinus;
+ RemoveColumn(clvEditMinus); // clvEditPlus is a marker var and gets removed later
+ delete pcei;
+ _editInfoList.remove(pcei);
+ }
+
+ if (fFoundErrorVar)
+ {
+ // This code is not needed since the variables are deleted
+ // when they are removed from the row --
+ // leaving it in results in double deletions
+ // delete the constraint's error variables
+ // ClVarSet &evars_set = (*it_eVars).second;
+ // ClVarSet::const_iterator it_set = evars_set.begin();
+ // for ( ; it_set != evars_set.end(); ++it_set)
+ // {
+ // delete *it_set;
+ // }
+ _errorVars.erase((*it_eVars).first);
+ }
+
+ if (_fAutosolve)
+ {
+ Optimize(_objective);
+ SetExternalVariables();
+ }
+
+ return *this;
+}
+
+
+// Re-initialize this solver from the original constraints, thus
+// getting rid of any accumulated numerical problems. (Actually,
+// Alan hasn't observed any such problems yet, but here's the method
+// anyway.)
+void
+ClSimplexSolver::Reset()
+{
+#ifdef CL_TRACE
+ Tracer TRACER(__FUNCTION__);
+ cerr << "()" << endl;
+#endif
+ // FIXGJB -- can postpone writing this for a while
+ // gotta be careful, though, as it's a likely place for
+ // a memory leak to sneak in
+ assert(false);
+}
+
+
+// Re-solve the cuurent collection of constraints, given the new
+// values for the edit variables that have already been
+// suggested (see SuggestValue() method)
+void
+ClSimplexSolver::Resolve()
+{ // CODE DUPLICATED ABOVE
+#ifdef CL_TRACE
+ Tracer TRACER(__FUNCTION__);
+#endif
+ DualOptimize();
+ SetExternalVariables();
+ _infeasibleRows.clear();
+ if (_fResetStayConstantsAutomatically)
+ ResetStayConstants();
+}
+
+ClSimplexSolver &
+ClSimplexSolver::SuggestValue(ClVariable v, Number x)
+{
+#ifdef CL_TRACE
+ Tracer TRACER(__FUNCTION__);
+#endif
+ ClEditInfo *pcei = PEditInfoFromClv(v);
+ if (NULL == pcei)
+ {
+#ifndef CL_NO_IO
+ std::stringstream ss;
+ ss << "SuggestValue for variable " << v << ", but var is not an edit variable" << ends;
+ throw ExCLEditMisuse(ss.str().c_str());
+#else
+ throw ExCLEditMisuse(v.Name().c_str());
+#endif
+ }
+ ClVariable clvEditPlus = pcei->_clvEditPlus;
+ ClVariable clvEditMinus = pcei->_clvEditMinus;
+ Number delta = x - pcei->_prevEditConstant;
+ pcei->_prevEditConstant = x;
+ DeltaEditConstant(delta,clvEditPlus,clvEditMinus);
+ return *this;
+}
+
+// Re-solve the cuurent 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
+ClSimplexSolver::Resolve(const vector<Number> &newEditConstants)
+{
+ ClEditInfoList::iterator it = _editInfoList.begin();
+ unsigned int i = 0;
+ for (; i < newEditConstants.size() && it != _editInfoList.end(); ++it, ++i)
+ {
+ ClEditInfo *pcei = (*it);
+ SuggestValue(pcei->_clv,newEditConstants[i]);
+ }
+ Resolve();
+}
+
+
+//// protected
+
+// 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 -- and prepare explanation)
+bool
+ClSimplexSolver::AddWithArtificialVariable(ClLinearExpression &expr,
+ ExCLRequiredFailureWithExplanation &e)
+{
+#ifdef CL_TRACE
+ Tracer TRACER(__FUNCTION__);
+ cerr << "(" << expr << ")" << endl;
+#endif
+
+ // Allocate the objects on the heap because the objects
+ // will remain in the tableau if we throw an exception,
+ // and that will result in the destructor cleaning up
+ // after us
+ ClSlackVariable *pav = new ClSlackVariable(++_artificialCounter,"a");
+ ClObjectiveVariable *paz = new ClObjectiveVariable("az");
+ ClLinearExpression *pazRow = new ClLinearExpression(expr);
+ // the artificial objective is av, which we know is equal to expr
+ // (which contains only parametric variables)
+
+#ifdef CL_FIND_LEAK
+ cerr << "aC = " << _artificialCounter
+ << "\nDeletes = " << _cArtificialVarsDeleted << endl;
+#endif
+#ifdef CL_TRACE
+ cerr << __FUNCTION__ << " before addRow-s:\n"
+ << (*this) << endl;
+#endif
+
+ // the artificial objective is av, which we know is equal to expr
+ // (which contains only parametric variables)
+
+ // objective is treated as a row in the tableau,
+ // so do the substitution for its value (we are minimizing
+ // the artificial variable)
+ // this row will be removed from the tableau after optimizing
+ addRow(*paz,*pazRow);
+
+ // now Add the normal row to the tableau -- when artifical
+ // variable is minimized to 0 (if possible)
+ // this row remains in the tableau to maintain the constraint
+ // we are trying to Add
+ addRow(*pav,expr);
+
+#ifdef CL_TRACE
+ cerr << __FUNCTION__ << " after addRow-s:\n"
+ << (*this) << endl;
+#endif
+
+ // try to Optimize az to 0
+ // note we are *not* optimizing the real objective, but optimizing
+ // the artificial objective to see if the error in the constraint
+ // we are adding can be set to 0
+ Optimize(*paz);
+
+ // Careful, we want to get the Expression that is in
+ // the tableau, not the one we initialized it with!
+ ClLinearExpression *pazTableauRow = RowExpression(*paz);
+#ifdef CL_TRACE
+ cerr << "pazTableauRow->Constant() == " << pazTableauRow->Constant() << endl;
+#endif
+
+ // Check that we were able to make the objective value 0
+ // If not, the original constraint was not satisfiable
+ if (!ClApprox(pazTableauRow->Constant(),0.0))
+ {
+ BuildExplanation(e, paz, pazTableauRow);
+ // remove the artificial objective row that we just
+ // added temporarily
+ delete RemoveRow(*paz);
+ // and delete the artificial objective variable that we also added above
+ delete paz;
+ return false;
+ }
+
+ // see if av is a basic variable
+ const ClLinearExpression *pe = RowExpression(*pav);
+ if (pe != NULL)
+ {
+ // FIXGJB: do we ever even get here?
+ // Find another variable in this row and Pivot, so that av becomes parametric
+ // If there isn't another variable in the row then
+ // the tableau contains the equation av = 0 -- just delete av's row
+ if (pe->IsConstant())
+ {
+ // FIXGJB: do we ever get here?
+ assert(ClApprox(pe->Constant(),0.0));
+ delete RemoveRow(*pav);
+ // remove the temporary objective function
+ // FIXGJB may need this too: delete RemoveRow(*paz);
+ delete pav;
+#ifdef CL_FIND_LEAK
+ ++_cArtificialVarsDeleted;
+#endif
+ return true;
+ }
+ ClVariable entryVar = pe->AnyPivotableVariable();
+ if (entryVar.IsNil())
+ {
+ BuildExplanation(e, *pav, pe);
+ return false; /* required failure */
+ }
+ Pivot(entryVar, *pav);
+ }
+ // now av should be parametric
+ assert(RowExpression(*pav) == NULL);
+ RemoveColumn(*pav);
+ delete pav;
+#ifdef CL_FIND_LEAK
+ ++_cArtificialVarsDeleted;
+#endif
+ // remove the temporary objective function
+ delete RemoveRow(*paz);
+ delete paz;
+ return true;
+}
+
+
+// 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.
+void ClSimplexSolver::BuildExplanation(ExCLRequiredFailureWithExplanation &e,
+ ClVariable av,
+ const ClLinearExpression *pcle)
+{
+ ClVarToConstraintMap::iterator it_cn;
+ it_cn = _constraintsMarked.find(av);
+ if (it_cn != _constraintsMarked.end())
+ {
+ e.AddConstraint((*it_cn).second);
+ }
+
+ assert(pcle != NULL);
+
+ const ClVarToNumberMap & terms = pcle->Terms();
+ ClVarToNumberMap::const_iterator it_term;
+ for (it_term = terms.begin(); it_term != terms.end(); it_term++)
+ {
+ it_cn = _constraintsMarked.find((*it_term).first);
+ if (it_cn != _constraintsMarked.end())
+ {
+ e.AddConstraint((*it_cn).second);
+ }
+ }
+}
+
+
+
+// We are trying to Add the constraint expr=0 to the appropriate
+// tableau. Try to Add expr directly to the tableaus without
+// creating an artificial variable. Return true if successful and
+// false if not.
+bool
+ClSimplexSolver::TryAddingDirectly(ClLinearExpression &expr)
+{
+#ifdef CL_TRACE
+ Tracer TRACER(__FUNCTION__);
+ cerr << "(" << expr << ")" << endl;
+#endif
+ ClVariable subject = ChooseSubject(expr);
+ if (subject.get_pclv() == NULL )
+ {
+#ifdef CL_TRACE
+ cerr << "- returning false" << endl;
+#endif
+ return false;
+ }
+ expr.NewSubject(subject);
+ if (ColumnsHasKey(subject))
+ {
+ SubstituteOut(subject,expr);
+ }
+ addRow(subject,expr);
+#ifdef CL_TRACE
+ cerr << "- returning true" << endl;
+#endif
+ return true; // successfully added directly
+}
+
+
+// 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
+ClSimplexSolver::ChooseSubject(ClLinearExpression &expr)
+{
+#ifdef CL_TRACE
+ Tracer TRACER(__FUNCTION__);
+ cerr << "(" << expr << ")" << endl;
+#endif
+ ClVariable subject(clvNil); // the current best subject, if any
+
+ // true iff we have found a subject that is an unrestricted variable
+ bool foundUnrestricted = false;
+
+ // true iff we have found a restricted variable that is new to the
+ // solver (except for being in the obj. function) and that has a
+ // negative coefficient
+ bool foundNewRestricted = false;
+
+ const ClVarToNumberMap &terms = expr.Terms();
+ ClVarToNumberMap::const_iterator it = terms.begin();
+ for ( ; it != terms.end(); ++it )
+ {
+ ClVariable v = (*it).first;
+ Number c = (*it).second;
+
+ if (foundUnrestricted)
+ {
+ // We have already found an unrestricted variable. The only
+ // time we will want to use v instead of the current choice
+ // 'subject' is if v is unrestricted and new to the solver and
+ // 'subject' isn't new. If this is the case just pick v
+ // immediately and return.
+ if (!v.IsRestricted())
+ {
+ if (!ColumnsHasKey(v))
+ return v;
+ }
+ }
+ else
+ { // we haven't found an restricted variable yet
+ if (v.IsRestricted())
+ {
+ // v is restricted. If we have already found a suitable
+ // restricted variable just stick with that. Otherwise, if v
+ // is new to the solver and has a negative coefficient pick
+ // it. Regarding being new to the solver -- if the variable
+ // occurs only in the objective function we regard it as being
+ // new to the solver, since error variables are added to the
+ // objective function when we make the Expression. We also
+ // never pick a dummy variable here.
+ if (!foundNewRestricted && !v.IsDummy() && c < 0.0)
+ {
+ const ClTableauColumnsMap &col = Columns();
+ ClTableauColumnsMap::const_iterator it_col = col.find(v);
+ if (it_col == col.end() ||
+ ( col.size() == 1 && ColumnsHasKey(_objective) ) )
+ {
+ subject = v;
+ foundNewRestricted = true;
+ }
+ }
+ }
+ else
+ {
+ // v is unrestricted.
+ // If v is also new to the solver just pick it now
+ subject = v;
+ foundUnrestricted = true;
+ }
+ }
+ }
+ if (!subject.IsNil())
+ return subject;
+
+ // subject is nil.
+ // Make one last check -- if all of the variables in expr are dummy
+ // variables, then we can pick a dummy variable as the subject
+ Number coeff = 0;
+ it = terms.begin();
+ for ( ; it != terms.end(); ++it )
+ {
+ ClVariable v = (*it).first;
+ Number c = (*it).second;
+ if (!v.IsDummy())
+ return clvNil; // nope, no luck
+ // if v is new to the solver, tentatively make it the subject
+ if (!ColumnsHasKey(v))
+ {
+ subject = v;
+ coeff = c;
+ }
+ }
+
+ // If we get this far, all of the variables in the Expression should
+ // be dummy variables. If the Constant is nonzero we are trying to
+ // Add an unsatisfiable required constraint. (Remember that dummy
+ // variables must take on a value of 0.) Otherwise, if the Constant
+ // is Zero, multiply by -1 if necessary to make the coefficient for
+ // the subject negative."
+ if (!ClApprox(expr.Constant(),0.0))
+ {
+#ifdef CL_DEBUG_FAILURES
+ cerr << "required failure in choose subject:\n"
+ << *this << endl;
+#endif
+ if (FIsExplaining())
+ {
+ ExCLRequiredFailureWithExplanation e;
+ BuildExplanation(e, clvNil, &expr);
+ throw e;
+ }
+ else
+ throw ExCLRequiredFailure();
+ }
+ if (coeff > 0.0)
+ {
+ expr.MultiplyMe(-1);
+ }
+ return subject;
+}
+
+// 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
+ClSimplexSolver::DeltaEditConstant(Number delta,
+ ClVariable plusErrorVar,
+ ClVariable minusErrorVar)
+{
+#ifdef CL_TRACE
+ Tracer TRACER(__FUNCTION__);
+ cerr << "(" << delta << ", " << plusErrorVar << ", " << minusErrorVar << ")" << endl;
+#endif
+ // first check if the plusErrorVar is basic
+ ClLinearExpression *pexprPlus = RowExpression(plusErrorVar);
+ if (pexprPlus != NULL )
+ {
+ pexprPlus->IncrementConstant(delta);
+ // error variables are always restricted
+ // so the row is infeasible if the Constant is negative
+ if (pexprPlus->Constant() < 0.0)
+ {
+ _infeasibleRows.insert(plusErrorVar);
+ }
+ return;
+ }
+ // check if minusErrorVar is basic
+ ClLinearExpression *pexprMinus = RowExpression(minusErrorVar);
+ if (pexprMinus != NULL)
+ {
+ pexprMinus->IncrementConstant(-delta);
+ if (pexprMinus->Constant() < 0.0)
+ {
+ _infeasibleRows.insert(minusErrorVar);
+ }
+ return;
+ }
+ // Neither is basic. So they must both be nonbasic, and will both
+ // occur in exactly the same expressions. Find all the expressions
+ // in which they occur by finding the column for the minusErrorVar
+ // (it doesn't matter whether we look for that one or for
+ // plusErrorVar). Fix the constants in these expressions.
+ ClVarSet &columnVars = _columns[minusErrorVar];
+ ClVarSet::iterator it = columnVars.begin();
+ for (; it != columnVars.end(); ++it)
+ {
+ ClVariable basicVar = *it;
+ ClLinearExpression *pexpr = RowExpression(basicVar);
+ assert(pexpr != NULL );
+ double c = pexpr->CoefficientFor(minusErrorVar);
+ pexpr->IncrementConstant(c*delta);
+ if (basicVar.IsRestricted() && pexpr->Constant() < 0.0)
+ {
+ _infeasibleRows.insert(basicVar);
+ }
+ }
+}
+
+// We have set new values for the constants in the edit constraints.
+// Re-Optimize using the dual simplex algorithm.
+void
+ClSimplexSolver::DualOptimize()
+{
+#ifdef CL_TRACE
+ Tracer TRACER(__FUNCTION__);
+ cerr << "()" << endl;
+#endif
+ const ClLinearExpression *pzRow = RowExpression(_objective);
+ // need to handle infeasible rows
+ while (!_infeasibleRows.empty())
+ {
+ ClVarSet::iterator it_exitVar = _infeasibleRows.begin();
+ ClVariable exitVar = *it_exitVar;
+ _infeasibleRows.erase(it_exitVar);
+ ClVariable entryVar;
+ // exitVar might have become basic after some other pivoting
+ // so allow for the case of its not being there any longer
+ ClLinearExpression *pexpr = RowExpression(exitVar);
+ if (pexpr != NULL )
+ {
+ // make sure the row is still not feasible
+ if (pexpr->Constant() < 0.0)
+ {
+ double ratio = DBL_MAX;
+ double r;
+ ClVarToNumberMap &terms = pexpr->Terms();
+ ClVarToNumberMap::iterator it = terms.begin();
+ for ( ; it != terms.end(); ++it )
+ {
+ ClVariable v = (*it).first;
+ Number c = (*it).second;
+ if (c > 0.0 && v.IsPivotable())
+ {
+ Number zc = pzRow->CoefficientFor(v);
+ r = zc/c; // FIXGJB r:= zc/c or Zero, as ClSymbolicWeight-s
+ if (r < ratio)
+ {
+ entryVar = v;
+ ratio = r;
+ }
+ }
+ }
+ if (ratio == DBL_MAX)
+ {
+ stringstream ss;
+ ss << "ratio == nil (DBL_MAX)" << ends;
+ throw ExCLInternalError(ss.str().c_str());
+ }
+ Pivot(entryVar,exitVar);
+ }
+ }
+ }
+}
+
+// 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 *
+ClSimplexSolver::NewExpression(const ClConstraint *pcn,
+ /* output to */
+ ClVariable &clvEplus,
+ ClVariable &clvEminus,
+ Number &prevEConstant)
+{
+#ifdef CL_TRACE
+ Tracer TRACER(__FUNCTION__);
+ cerr << "(" << *pcn << ")" << endl;
+ cerr << "cn.IsInequality() == " << pcn->IsInequality() << endl;
+ cerr << "cn.IsRequired() == " << pcn->IsRequired() << endl;
+#endif
+ const ClLinearExpression &cnExpr = pcn->Expression();
+ cl_auto_ptr<ClLinearExpression> pexpr ( new ClLinearExpression(cnExpr.Constant()) );
+ cl_auto_ptr<ClSlackVariable> pslackVar;
+ cl_auto_ptr<ClDummyVariable> pdummyVar;
+ cl_auto_ptr<ClSlackVariable> peminus(0);
+ cl_auto_ptr<ClSlackVariable> peplus(0);
+ const ClVarToNumberMap &cnTerms = cnExpr.Terms();
+ ClVarToNumberMap::const_iterator it = cnTerms.begin();
+ for ( ; it != cnTerms.end(); ++it)
+ {
+ ClVariable v = (*it).first;
+ Number c = (*it).second;
+ const ClLinearExpression *pe = RowExpression(v);
+ if (pe == NULL)
+ {
+ pexpr->AddVariable(v,c);
+ }
+ else
+ {
+ pexpr->AddExpression(*pe,c);
+ }
+ }
+
+ // Add slack and error variables as needed
+ if (pcn->IsInequality())
+ {
+ // cn is an inequality, so Add a slack variable. The original
+ // constraint is expr>=0, so that the resulting equality is
+ // expr-slackVar=0. If cn is also non-required Add a negative
+ // error variable, giving
+ // expr-slackVar = -errorVar, in other words
+ // expr-slackVar+errorVar=0.
+ // Since both of these variables are newly created we can just Add
+ // them to the Expression (they can't be basic).
+ ++_slackCounter;
+ ReinitializeAutoPtr(pslackVar,new ClSlackVariable (_slackCounter, "s"));
+ pexpr->setVariable(*pslackVar,-1);
+ // index the constraint under its slack variable and vice-versa
+ _markerVars[pcn] = pslackVar.get();
+ _constraintsMarked[pslackVar.get()] = pcn;
+
+ if (!pcn->IsRequired())
+ {
+ ++_slackCounter;
+ ReinitializeAutoPtr(peminus,new ClSlackVariable (_slackCounter, "em"));
+ pexpr->setVariable(*peminus,1.0);
+ // Add emnius to the objective function with the appropriate weight
+ ClLinearExpression *pzRow = RowExpression(_objective);
+ // FIXGJB: pzRow->AddVariable(eminus,pcn->strength().symbolicWeight() * pcn->weight());
+ ClSymbolicWeight sw = pcn->strength().symbolicWeight().Times(pcn->weight());
+ pzRow->setVariable(*peminus,sw.AsDouble());
+ _errorVars[pcn].insert(peminus.get());
+ NoteAddedVariable(*peminus,_objective);
+ }
+ }
+ else
+ { // cn is an equality
+ if (pcn->IsRequired())
+ {
+ // Add a dummy variable to the Expression to serve as a marker
+ // for this constraint. The dummy variable is never allowed to
+ // enter the basis when pivoting.
+ ++_dummyCounter;
+ ReinitializeAutoPtr(pdummyVar,new ClDummyVariable (_dummyCounter, "d"));
+ pexpr->setVariable(*pdummyVar,1.0);
+ _markerVars[pcn] = pdummyVar.get();
+ _constraintsMarked[pdummyVar.get()] = pcn;
+#ifdef CL_TRACE
+ cerr << "Adding dummyVar == d" << _dummyCounter << endl;
+#endif
+ }
+ else
+ {
+ // cn is a non-required equality. Add a positive and a negative
+ // error variable, making the resulting constraint
+ // expr = eplus - eminus,
+ // in other words: expr-eplus+eminus=0
+ ++_slackCounter;
+ ReinitializeAutoPtr(peplus,new ClSlackVariable (_slackCounter, "ep"));
+ ReinitializeAutoPtr(peminus,new ClSlackVariable (_slackCounter, "em"));
+
+ pexpr->setVariable(*peplus,-1.0);
+ pexpr->setVariable(*peminus,1.0);
+ // index the constraint under one of the error variables
+ _markerVars[pcn] = peplus.get();
+ _constraintsMarked[peplus.get()] = pcn;
+
+ ClLinearExpression *pzRow = RowExpression(_objective);
+ // FIXGJB: pzRow->AddVariable(eplus,pcn->strength().symbolicWeight() * pcn->weight());
+ ClSymbolicWeight sw = pcn->strength().symbolicWeight().Times(pcn->weight());
+ double swCoeff = sw.AsDouble();
+#ifdef CL_TRACE
+ if (swCoeff == 0)
+ {
+ cerr << "sw == " << sw << endl
+ << "cn == " << *pcn << endl;
+ cerr << "adding " << *peplus << " and " << *peminus
+ << " with swCoeff == " << swCoeff << endl;
+ }
+#endif
+ pzRow->setVariable(*peplus,swCoeff);
+ NoteAddedVariable(*peplus,_objective);
+ // FIXGJB: pzRow->AddVariable(eminus,pcn->strength().symbolicWeight() * pcn->weight());
+ pzRow->setVariable(*peminus,swCoeff);
+ NoteAddedVariable(*peminus,_objective);
+ _errorVars[pcn].insert(peminus.get());
+ _errorVars[pcn].insert(peplus.get());
+ if (pcn->isStayConstraint())
+ {
+ _stayPlusErrorVars.push_back(peplus.get());
+ _stayMinusErrorVars.push_back(peminus.get());
+ }
+ else if (pcn->IsEditConstraint())
+ {
+ clvEplus = peplus.get();
+ clvEminus = peminus.get();
+ prevEConstant = cnExpr.Constant();
+ }
+ }
+ }
+
+ // the Constant in the Expression should be non-negative.
+ // If necessary normalize the Expression by multiplying by -1
+ if (pexpr->Constant() < 0)
+ {
+#ifdef CL_TRACE
+ cerr << "NewExpression's Constant is " << pexpr->Constant() << ", < 0, so flipping" << endl;
+#endif
+ pexpr->MultiplyMe(-1);
+ }
+#ifdef CL_TRACE
+ cerr << "- returning " << *pexpr << endl;
+#endif
+ // Terrible Name -- release() does *not* delete the object,
+ // only makes sure that the destructor won't delete the object
+ // (it releases the cl_auto_ptr from the responsibility of deleting the object)
+ pslackVar.release();
+ pdummyVar.release();
+ peminus.release();
+ peplus.release();
+ return pexpr.release();
+}
+
+// Minimize the value of the objective. (The tableau should already
+// be feasible.)
+void
+ClSimplexSolver::Optimize(ClVariable zVar)
+{
+#ifdef CL_TRACE
+ Tracer TRACER(__FUNCTION__);
+ cerr << "(" << zVar << ")\n"
+ << *this << endl;
+#endif
+ ClLinearExpression *pzRow = RowExpression(zVar);
+ assert(pzRow != NULL);
+ ClVariable entryVar = clvNil;
+ ClVariable exitVar = clvNil;
+ while (true)
+ {
+ Number objectiveCoeff = 0;
+ // Find the most negative coefficient in the objective function
+ // (ignoring the non-pivotable dummy variables). If all
+ // coefficients are positive we're done
+ ClVarToNumberMap &terms = pzRow->Terms();
+ ClVarToNumberMap::iterator it = terms.begin();
+ for (; it != terms.end(); ++it)
+ {
+ ClVariable v = (*it).first;
+ Number c = (*it).second;
+ if (v.IsPivotable() && c < objectiveCoeff)
+ {
+ objectiveCoeff = c;
+ entryVar = v;
+ // A. Beurive' Tue Jul 13 23:03:05 CEST 1999 Why the most
+ // negative? I encountered unending cycles of pivots!
+ break;
+ }
+ }
+ // if all coefficients were positive (or if the objective
+ // function has no pivotable variables)
+ // we are at an optimum
+ if (objectiveCoeff >= -_epsilon)
+ return;
+#ifdef CL_TRACE
+ cerr << "entryVar == " << entryVar << ", "
+ << "objectiveCoeff == " << objectiveCoeff
+ << endl;
+#endif
+
+ // choose which variable to move out of the basis
+ // Only consider pivotable basic variables
+ // (i.e. restricted, non-dummy variables)
+ double minRatio = DBL_MAX;
+ ClVarSet &columnVars = _columns[entryVar];
+ ClVarSet::iterator it_rowvars = columnVars.begin();
+ Number r = 0.0;
+ for (; it_rowvars != columnVars.end(); ++it_rowvars)
+ {
+ ClVariable v = *it_rowvars;
+#ifdef CL_TRACE
+ cerr << "Checking " << v << endl;
+#endif
+ if (v.IsPivotable())
+ {
+ const ClLinearExpression *pexpr = RowExpression(v);
+ Number coeff = pexpr->CoefficientFor(entryVar);
+ // only consider negative coefficients
+ if (coeff < 0.0)
+ {
+ r = - pexpr->Constant() / coeff;
+ if (r < minRatio)
+ {
+#ifdef CL_TRACE
+ cerr << "New minRatio == " << r << endl;
+#endif
+ minRatio = r;
+ exitVar = v;
+ }
+ }
+ }
+ }
+ // If minRatio is still nil at this point, it means that the
+ // objective function is unbounded, i.e. it can become
+ // arbitrarily negative. This should never happen in this
+ // application.
+ if (minRatio == DBL_MAX)
+ {
+ stringstream ss;
+ ss << "objective function is unbounded!" << ends;
+ throw ExCLInternalError(ss.str().c_str());
+ }
+ Pivot(entryVar, exitVar);
+#ifdef CL_TRACE
+ cerr << "After Optimize:\n"
+ << *this << endl;
+#endif
+ }
+}
+
+// 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
+ClSimplexSolver::Pivot(ClVariable entryVar, ClVariable exitVar)
+{
+#ifdef CL_TRACE
+ Tracer TRACER(__FUNCTION__);
+ cerr << "(" << entryVar << ", " << exitVar << ")" << endl;
+#endif
+
+ // the entryVar might be non-pivotable if we're doing a RemoveConstraint --
+ // otherwise it should be a pivotable variable -- enforced at call sites,
+ // hopefully
+
+ // expr is the Expression for the exit variable (about to leave the basis) --
+ // so that the old tableau includes the equation:
+ // exitVar = expr
+ ClLinearExpression *pexpr = RemoveRow(exitVar);
+
+ // Compute an Expression for the entry variable. Since expr has
+ // been deleted from the tableau we can destructively modify it to
+ // build this Expression.
+ pexpr->ChangeSubject(exitVar,entryVar);
+ SubstituteOut(entryVar,*pexpr);
+
+ if (entryVar.IsExternal())
+ {
+ // entry var is no longer a parametric variable since we're moving
+ // it into the basis
+ _externalParametricVars.erase(entryVar);
+ }
+ addRow(entryVar,*pexpr);
+}
+
+
+
+// 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
+ClSimplexSolver::ResetStayConstants()
+{
+#ifdef CL_TRACE
+ Tracer TRACER(__FUNCTION__);
+ cerr << "()" << endl;
+#endif
+ ClVarVector::const_iterator
+ itStayPlusErrorVars = _stayPlusErrorVars.begin();
+ ClVarVector::const_iterator
+ itStayMinusErrorVars = _stayMinusErrorVars.begin();
+
+ for ( ; itStayPlusErrorVars != _stayPlusErrorVars.end();
+ ++itStayPlusErrorVars, ++itStayMinusErrorVars )
+ {
+ ClLinearExpression *pexpr = RowExpression(*itStayPlusErrorVars);
+ if (pexpr == NULL )
+ {
+ pexpr = RowExpression(*itStayMinusErrorVars);
+ }
+ if (pexpr != NULL)
+ {
+ pexpr->Set_constant(0.0);
+ }
+ }
+}
+
+// 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
+ClSimplexSolver::SetExternalVariables()
+{
+#ifdef CL_TRACE
+ Tracer TRACER(__FUNCTION__);
+ cerr << "()\n"
+ << *this << endl;
+#endif
+
+ // FIXGJB -- oughta check some invariants here
+
+ // Set external parametric variables first
+ // in case I've screwed up
+ ClVarSet::iterator itParVars = _externalParametricVars.begin();
+ for ( ; itParVars != _externalParametricVars.end(); ++itParVars )
+ {
+ ClVariable v = *itParVars;
+#ifndef NDEBUG
+ // defensively skip it if it is basic -- ChangeValue is virtual
+ // so don't want to call it twice; this should never
+ // happen
+ if (FIsBasicVar(v))
+ {
+#ifndef CL_NO_IO
+ // WARNING
+ cerr << __FUNCTION__ << "Error: variable " << v
+ << " in _externalParametricVars is basic" << endl;
+ cerr << "Row is: " << *RowExpression(v) << endl;
+#endif
+ continue;
+ }
+#endif
+ ChangeClv(v,0.0);
+ }
+
+ // Only iterate over the rows w/ external variables
+ ClVarSet::iterator itRowVars = _externalRows.begin();
+ for ( ; itRowVars != _externalRows.end() ; ++itRowVars )
+ {
+ ClVariable v = *itRowVars;
+ ClLinearExpression *pexpr = RowExpression(v);
+ ChangeClv(v,pexpr->Constant());
+ }
+
+ _fNeedsSolving = false;
+ if (_pfnResolveCallback)
+ _pfnResolveCallback(this);
+}
+
+#ifndef CL_NO_IO
+ostream &
+PrintTo(ostream &xo, const ClVarVector &varlist)
+{
+ ClVarVector::const_iterator it = varlist.begin();
+ xo << varlist.size() << ":" << "[ ";
+ if (it != varlist.end())
+ {
+ xo << *it;
+ ++it;
+ }
+ for (; it != varlist.end(); ++it)
+ {
+ xo << ", " << *it;
+ }
+ xo << " ]";
+ return xo;
+}
+
+ostream &operator<<(ostream &xo, const ClVarVector &varlist)
+{ return PrintTo(xo,varlist); }
+
+
+ostream &
+PrintTo(ostream &xo, const ClConstraintToVarSetMap &mapCnToVarSet)
+{
+ ClConstraintToVarSetMap::const_iterator it = mapCnToVarSet.begin();
+ for ( ; it != mapCnToVarSet.end(); ++it) {
+ const ClConstraint *pcn = (*it).first;
+ const ClVarSet &set = (*it).second;
+ xo << "CN: " << pcn << *pcn << ":: " << set << endl;
+ }
+ return xo;
+}
+
+ostream &operator <<(ostream &xo, const ClConstraintToVarSetMap &mapCnToVarSet)
+{ return PrintTo(xo,mapCnToVarSet); }
+
+
+
+ostream &
+ClSimplexSolver::PrintOn(ostream &xo) const
+{
+ ClTableau::PrintOn(xo);
+
+ xo << "_stayPlusErrorVars: "
+ << _stayPlusErrorVars << endl;
+ xo << "_stayMinusErrorVars: "
+ << _stayMinusErrorVars << endl;
+ xo << "_editInfoList:\n"
+ << _editInfoList << endl;
+ return xo;
+}
+
+
+ostream &
+ClSimplexSolver::PrintInternalInfo(ostream &xo) const
+{
+ ClTableau::PrintInternalInfo(xo);
+ xo << "; edvars: " << _editInfoList.size();
+ xo << endl;
+ printExternalVariablesTo(xo);
+ return xo;
+}
+
+ostream &operator<<(ostream &xo, const ClSimplexSolver &clss)
+{
+ return clss.PrintOn(xo);
+}
+
+#endif
+
+bool
+ClSimplexSolver::FIsConstraintSatisfied(const ClConstraint *const pcn) const
+{
+ ClConstraintToVarMap::const_iterator it_marker = _markerVars.find(pcn);
+ if (it_marker == _markerVars.end())
+ { // could not find the constraint
+ throw ExCLConstraintNotFound();
+ }
+
+#ifndef CL_NO_IO
+ bool fCnsays = pcn->FIsSatisfied();
+#endif
+
+ ClConstraintToVarSetMap::const_iterator it_eVars = _errorVars.find(pcn);
+
+ if (it_eVars != _errorVars.end())
+ {
+ const ClVarSet &eVars = (*it_eVars).second;
+ ClVarSet::const_iterator it = eVars.begin();
+ for ( ; it != eVars.end(); ++it )
+ {
+ const ClLinearExpression *pexpr = RowExpression(*it);
+ if (pexpr != NULL && !ClApprox(pexpr->Constant(),0.0))
+ {
+#ifndef CL_NO_IO
+ if (fCnsays)
+ cerr << __FUNCTION__ << ": constraint says satisfiable, but solver does not" << endl;
+#endif
+ return false;
+ }
+ }
+ }
+
+#ifndef CL_NO_IO
+ if (!fCnsays)
+ cerr << __FUNCTION__ << ": solver says satisfiable, but constraint does not" << endl;
+#endif
+ return true;
+}
+
+
+
+#ifndef CL_NO_ID
+
+ostream &PrintTo(ostream &xo, const ClSimplexSolver::ClEditInfoList &listPEditInfo)
+{
+ ClSimplexSolver::ClEditInfoList::const_iterator it = listPEditInfo.begin();
+ for ( ; it != listPEditInfo.end(); ++it) {
+ const ClSimplexSolver::ClEditInfo *pcei = (*it);
+ xo << *pcei << endl;
+ }
+ return xo;
+}
+
+
+ostream &operator<<(ostream &xo, const ClSimplexSolver::ClEditInfoList &listPEditInfo)
+{ return PrintTo(xo,listPEditInfo); }
+
+#endif
+
+// A. Beurive' Tue Jul 6 17:03:32 CEST 1999
+void
+ClSimplexSolver::ChangeStrengthAndWeight(ClConstraint *pcn, const ClStrength &strength, double weight)
+{
+ ClConstraintToVarSetMap::iterator it_eVars = _errorVars.find(pcn);
+ // Only for constraints that already have error variables (i.e. non-required constraints)
+ assert(it_eVars != _errorVars.end());
+
+ ClLinearExpression *pzRow = RowExpression(_objective);
+
+ Number old_coeff = pcn->weight() * pcn->strength().symbolicWeight().AsDouble();
+ pcn->setStrength(strength);
+ pcn->setWeight(weight);
+ Number new_coeff = pcn->weight() * pcn->strength().symbolicWeight().AsDouble();
+
+ if (new_coeff != old_coeff)
+ {
+#ifdef CL_TRACE
+ cerr << "Changing strength and/or weight for constraint: " << endl << *pcn << endl;
+ cerr << "Updating objective row from:" << endl << *pzRow << endl;
+#endif
+ ClVarSet &eVars = (*it_eVars).second;
+ ClVarSet::iterator it = eVars.begin();
+ for ( ; it != eVars.end(); ++it )
+ {
+ const ClLinearExpression *pexpr = RowExpression(*it);
+ if (pexpr == NULL )
+ {
+ pzRow->AddVariable(*it,-old_coeff,_objective,*this);
+ pzRow->AddVariable(*it,new_coeff,_objective,*this);
+ }
+ else
+ {
+ pzRow->AddExpression(*pexpr,-old_coeff,_objective,*this);
+ pzRow->AddExpression(*pexpr,new_coeff,_objective,*this);
+ }
+ }
+#ifdef CL_TRACE
+ cerr << "to: " << endl << *pzRow << endl;
+#endif
+
+ if (_fAutosolve)
+ {
+ Optimize(_objective);
+ SetExternalVariables();
+ }
+ }
+}
+
+// A. Beurive' Tue Jul 6 17:03:42 CEST 1999
+void
+ClSimplexSolver::ChangeStrength(ClConstraint *pcn, const ClStrength &strength)
+{
+ ChangeStrengthAndWeight(pcn,strength,pcn->weight());
+}
+
+// A. Beurive' Tue Jul 6 17:03:42 CEST 1999
+void
+ClSimplexSolver::ChangeWeight(ClConstraint *pcn, double weight)
+{
+ ChangeStrengthAndWeight(pcn,pcn->strength(),weight);
+}