ardour/libs/cassowary/ClLinearExpression.cc
Taybin Rutkin d09f6b3016 Initial revision
git-svn-id: svn://localhost/trunk/ardour2@4 d708f5d6-7413-0410-9779-e7cbd77b26cf
2005-05-13 20:47:18 +00:00

474 lines
13 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
//
// ClLinearExpression.cc
using namespace std;
#include <cassowary/ClLinearExpression.h>
#include <cassowary/ClSymbolicWeight.h> /// needed only to instantiate with T=ClSymbolicWeight
#include <cassowary/ClVariable.h>
#include <cassowary/ClTableau.h>
#include <cassowary/ClErrors.h>
#ifdef HAVE_CONFIG_H
#include <config.h>
#define CONFIG_H_INCLUDED
#endif
template <class T>
ClGenericLinearExpression<T>::ClGenericLinearExpression(T num) :
_constant(num)
{ }
// Convert from ClVariable to a ClLinearExpression
// this replaces ClVariable::asLinearExpression
template <class T>
ClGenericLinearExpression<T>::ClGenericLinearExpression(ClVariable clv, T value,
T Constant) :
_constant(Constant)
{
_terms[clv] = value;
}
template <class T>
ClGenericLinearExpression<T>::~ClGenericLinearExpression()
{ }
#ifndef CL_NO_IO
template <class T>
ostream &
ClGenericLinearExpression<T>::PrintOn(ostream &xo) const
{
typename ClVarToCoeffMap::const_iterator i = _terms.begin();
if (!ClApprox(_constant,0.0) || i == _terms.end())
{
xo << _constant;
}
else
{
if (i == _terms.end())
return xo;
xo << (*i).second << "*" << (*i).first;
++i;
}
for ( ; i != _terms.end(); ++i)
{
xo << " + " << (*i).second << "*" << (*i).first;
}
return xo;
}
#endif
// Destructively multiply self by x.
// (private memfn)
template <class T>
ClGenericLinearExpression<T> &
ClGenericLinearExpression<T>::MultiplyMe(T x)
{
_constant *= x;
typename ClVarToCoeffMap::const_iterator i = _terms.begin();
for ( ; i != _terms.end(); ++i)
{
_terms[(*i).first] = (*i).second * x;
}
return *this;
}
// Return a new linear expression formed by multiplying self by x.
// (Note that this result must be linear.)
template <class T>
ClGenericLinearExpression<T>
ClGenericLinearExpression<T>::Times(Number x) const
{
ClGenericLinearExpression<T> result = *this;
return result.MultiplyMe(x);
}
// Return a new linear expression formed by multiplying self by x.
// (Note that this result must be linear.)
// The above function optimizes the specific case of multiplying
// by a Constant, here is the more general case
template <class T>
ClGenericLinearExpression<T>
ClGenericLinearExpression<T>::Times(const ClGenericLinearExpression<T> &expr) const
{
if (IsConstant())
{
return expr.Times(_constant);
}
else if (!expr.IsConstant())
{
// neither are constants, so we'd introduce non-linearity
throw ExCLNonlinearExpression();
}
return Times(expr._constant);
}
// Return a new linear expression formed by adding x to self.
template <class T>
ClGenericLinearExpression<T>
ClGenericLinearExpression<T>::Plus(const ClGenericLinearExpression<T> &expr) const
{
ClGenericLinearExpression<T> result = *this;
result.AddExpression(expr,1.0);
return result;
}
// Return a new linear expression formed by subtracting x from self.
template <class T>
ClGenericLinearExpression<T>
ClGenericLinearExpression<T>::Minus(const ClGenericLinearExpression<T> &expr) const
{
ClGenericLinearExpression<T> result = *this;
result.AddExpression(expr,-1.0);
return result;
}
// Return a new linear expression formed by dividing self by x.
// (Note that this result must be linear.)
template <class T>
ClGenericLinearExpression<T>
ClGenericLinearExpression<T>::Divide(Number x) const
{
if (ClApprox(x,0.0))
{
throw ExCLNonlinearExpression();
}
return Times(1.0/x);
}
// Return a new linear expression formed by dividing self by x.
// (Note that this result must be linear.)
template <class T>
ClGenericLinearExpression<T>
ClGenericLinearExpression<T>::Divide(const ClGenericLinearExpression<T> &expr) const
{
if (!expr.IsConstant())
{
throw ExCLNonlinearExpression();
}
return Divide(expr._constant);
}
// Return a new linear expression (expr/this). Since the result
// must be linear, this is permissible only if 'this' is a Constant.
template <class T>
ClGenericLinearExpression<T>
ClGenericLinearExpression<T>::DivFrom(const ClGenericLinearExpression<T> &expr) const
{
if (!IsConstant() || ClApprox(_constant,0.0))
{
throw ExCLNonlinearExpression();
}
return expr.Divide(_constant);
}
// Add n*expr to this expression for another expression expr.
template <class T>
ClGenericLinearExpression<T> &
ClGenericLinearExpression<T>::AddExpression(const ClGenericLinearExpression<T> &expr, Number n)
{
IncrementConstant(expr.Constant()*n);
typename ClVarToCoeffMap::const_iterator i = expr._terms.begin();
for ( ; i != expr._terms.end(); ++i)
{
AddVariable((*i).first, (*i).second * n);
}
return *this;
}
// Add n*expr to this expression for another expression expr.
// Notify the solver if a variable is added or deleted from this
// expression.
template <class T>
ClGenericLinearExpression<T> &
ClGenericLinearExpression<T>::AddExpression(const ClGenericLinearExpression<T> &expr, Number n,
ClVariable subject,
ClTableau &solver)
{
IncrementConstant(expr.Constant() * n);
typename ClVarToCoeffMap::const_iterator i = expr._terms.begin();
for ( ; i != expr._terms.end(); ++i)
{
AddVariable((*i).first, (*i).second * n, subject, solver);
}
return *this;
}
// Add a term c*v to this expression. If the expression already
// contains a term involving v, Add c to the existing coefficient.
// If the new coefficient is approximately 0, delete v.
template <class T>
ClGenericLinearExpression<T> &
ClGenericLinearExpression<T>::AddVariable(ClVariable v, T c)
{ // body largely duplicated below
#ifdef CL_TRACE
Tracer TRACER(__FUNCTION__);
cerr << "(" << v << ", " << c << ")" << endl;
#endif
typename ClVarToCoeffMap::iterator i = _terms.find(v);
if (i != _terms.end())
{
// expression already contains that variable, so Add to it
T new_coefficient = 0;
new_coefficient = (*i).second + c;
if (ClApprox(new_coefficient,0.0))
{
// new coefficient is Zero, so erase it
_terms.erase(i);
}
else
{
(*i).second = new_coefficient;
}
}
else // expression did not contain that variable
{
if (!ClApprox(c,0.0))
{
_terms[v] = c;
}
}
return *this;
}
// Add a term c*v to this expression. If the expression already
// contains a term involving v, Add c to the existing coefficient.
// If the new coefficient is approximately 0, delete v. Notify the
// solver if v appears or disappears from this expression.
template <class T>
ClGenericLinearExpression<T> &
ClGenericLinearExpression<T>::AddVariable(ClVariable v, T c,
ClVariable subject,
ClTableau &solver)
{ // body largely duplicated above
#ifdef CL_TRACE
Tracer TRACER(__FUNCTION__);
cerr << "(" << v << ", " << c << ", " << subject << ", ...)" << endl;
#endif
typename ClVarToCoeffMap::iterator i = _terms.find(v);
if (i != _terms.end())
{
// expression already contains that variable, so Add to it
T new_coefficient = (*i).second + c;
if (ClApprox(new_coefficient,0.0))
{
// new coefficient is Zero, so erase it
solver.NoteRemovedVariable((*i).first,subject);
_terms.erase(i);
}
else
{
(*i).second = new_coefficient;
}
}
else // expression did not contain that variable
{
if (!ClApprox(c,0.0))
{
_terms[v] = c;
solver.NoteAddedVariable(v,subject);
}
}
#ifdef CL_TRACE
cerr << "Now *this == " << *this << endl;
#endif
return *this;
}
// Return a variable in this expression. (It is an error if this
// expression is Constant -- signal ExCLInternalError in that case).
template <class T>
ClVariable
ClGenericLinearExpression<T>::AnyPivotableVariable() const
{
if (IsConstant())
{
throw ExCLInternalError("(ExCLInternalError) No pivotable variables in Constant expression");
}
typename ClVarToCoeffMap::const_iterator i = _terms.begin();
for ( ; i != _terms.end(); ++i)
{
ClVariable v = (*i).first;
if (v.IsPivotable())
return v;
}
return clvNil;
}
// Replace var with a symbolic expression expr that is equal to it.
// If a variable has been added to this expression that wasn't there
// before, or if a variable has been dropped from this expression
// because it now has a coefficient of 0, inform the solver.
// PRECONDITIONS:
// var occurs with a non-Zero coefficient in this expression.
template <class T>
void
ClGenericLinearExpression<T>::SubstituteOut(ClVariable var,
const ClGenericLinearExpression<T> &expr,
ClVariable subject,
ClTableau &solver)
{
#ifdef CL_TRACE
cerr << "* ClGenericLinearExpression::";
Tracer TRACER(__FUNCTION__);
cerr << "(" << var << ", " << expr << ", " << subject << ", "
<< solver << ")" << endl;
cerr << "*this == " << *this << endl;
#endif
typename ClVarToCoeffMap::iterator pv = _terms.find(var);
#ifndef NDEBUG
if (pv == _terms.end())
{
#ifndef CL_NO_IO
cerr << "SubstituteOut: pv != _terms.end()" << endl;
cerr << "(" << var << ", " << expr << ", " << subject << ", "
<< ")" << endl;
cerr << "*this == " << *this << endl;
#endif
throw "SubstituteOut: pv != _terms.end()";
}
#endif
assert(pv != _terms.end());
// FIXGJB: this got thrown! assert(!ClApprox((*pv).second,0.0));
T multiplier = (*pv).second;
_terms.erase(pv);
IncrementConstant(multiplier * expr._constant);
typename ClVarToCoeffMap::const_iterator i = expr._terms.begin();
for ( ; i != expr._terms.end(); ++i)
{
ClVariable v = (*i).first;
T c = (*i).second;
typename ClVarToCoeffMap::iterator poc = _terms.find(v);
if (poc != _terms.end())
{ // if oldCoeff is not nil
#ifdef CL_TRACE
cerr << "Considering (*poc) == " << (*poc).second << "*" << (*poc).first << endl;
#endif
// found it, so new coefficient is old one Plus what is in *i
T newCoeff = (*poc).second + (multiplier*c);
if (ClApprox(newCoeff,0.0))
{
solver.NoteRemovedVariable((*poc).first,subject);
_terms.erase(poc);
}
else
{
(*poc).second = newCoeff;
}
}
else
{ // did not have that variable already (oldCoeff == nil)
#ifdef CL_TRACE
cerr << "Adding (*i) == " << (*i).second << "*" << (*i).first << endl;
#endif
_terms[v] = multiplier * c;
solver.NoteAddedVariable(v,subject);
}
}
#ifdef CL_TRACE
cerr << "Now (*this) is " << *this << endl;
#endif
}
// This linear expression currently represents the equation
// oldSubject=self. Destructively modify it so that it represents
// the equation NewSubject=self.
//
// Precondition: NewSubject currently has a nonzero coefficient in
// this expression.
//
// NOTES
// Suppose this expression is c + a*NewSubject + a1*v1 + ... + an*vn.
//
// Then the current equation is
// oldSubject = c + a*NewSubject + a1*v1 + ... + an*vn.
// The new equation will be
// NewSubject = -c/a + oldSubject/a - (a1/a)*v1 - ... - (an/a)*vn.
// Note that the term involving NewSubject has been dropped.
//
// Basically, we consider the expression to be an equation with oldSubject
// equal to the expression, then Resolve the equation for NewSubject,
// and destructively make the expression what NewSubject is then equal to
template <class T>
void
ClGenericLinearExpression<T>::ChangeSubject(ClVariable old_subject,
ClVariable new_subject)
{
_terms[old_subject] = NewSubject(new_subject);
}
inline double ReciprocalOf(double n)
{ return 1.0/n; }
// This linear expression currently represents the equation self=0. Destructively modify it so
// that subject=self represents an equivalent equation.
//
// Precondition: subject must be one of the variables in this expression.
// NOTES
// Suppose this expression is
// c + a*subject + a1*v1 + ... + an*vn
// representing
// c + a*subject + a1*v1 + ... + an*vn = 0
// The modified expression will be
// subject = -c/a - (a1/a)*v1 - ... - (an/a)*vn
// representing
// subject = -c/a - (a1/a)*v1 - ... - (an/a)*vn = 0
//
// Note that the term involving subject has been dropped.
//
// Returns the reciprocal, so that NewSubject can be used by ChangeSubject
template <class T>
T
ClGenericLinearExpression<T>::NewSubject(ClVariable subject)
{
#ifdef CL_TRACE
Tracer TRACER(__FUNCTION__);
cerr << "(" << subject << ")" << endl;
#endif
typename ClVarToCoeffMap::iterator pnewSubject = _terms.find(subject);
assert(pnewSubject != _terms.end());
// assert(!ClApprox((*pnewSubject).second,0.0));
T reciprocal = ReciprocalOf((*pnewSubject).second);
_terms.erase(pnewSubject);
MultiplyMe(-reciprocal);
return reciprocal;
}
template <class T>
T
ClGenericLinearExpression<T>::Evaluate() const
{
T answer = _constant;
typename ClVarToCoeffMap::const_iterator i = _terms.begin();
for ( ; i != _terms.end(); ++i)
{
ClVariable v = (*i).first;
answer += (*i).second * v.Value();
}
return answer;
}
template class ClGenericLinearExpression<Number>;
// template class ClGenericLinearExpression<ClSymbolicWeight>;