diff options
Diffstat (limited to 'libs/cassowary/ClSimplexSolver.cc')
-rw-r--r-- | libs/cassowary/ClSimplexSolver.cc | 1631 |
1 files changed, 0 insertions, 1631 deletions
diff --git a/libs/cassowary/ClSimplexSolver.cc b/libs/cassowary/ClSimplexSolver.cc deleted file mode 100644 index 32ea59df74..0000000000 --- a/libs/cassowary/ClSimplexSolver.cc +++ /dev/null @@ -1,1631 +0,0 @@ -// $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 - -// 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); -} |