41ab4dfbce
git-svn-id: svn://localhost/trunk/ardour2@9 d708f5d6-7413-0410-9779-e7cbd77b26cf
1632 lines
49 KiB
C++
1632 lines
49 KiB
C++
// $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);
|
|
}
|