Add an alternative solver to lp_solve. The solver based on Ingo's active set solver but is able to handle arbitrary hard and soft constraints. The advantage to lp_solve is that the active set solver can optimize variable in respect to a quadratic objective function. This makes it possible to minimise the quadratic derivation to a desired value e.g. \Sum_i(x_i - x_{i,pref})^2 -> min.

The solver part has been refactored in this way that both solver can be used with the same layout specifications. The active set solver is default now; the performance is not as good as lp_solve, though.



git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@40285 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Clemens Zeidler
2011-01-25 04:59:40 +00:00
parent 6537cf9750
commit 7583db5a1e
19 changed files with 2207 additions and 640 deletions
+2 -2
View File
@@ -124,8 +124,6 @@ private:
/*! Add a view without initialize the Area. */ /*! Add a view without initialize the Area. */
BLayoutItem* _CreateLayoutItem(BView* view); BLayoutItem* _CreateLayoutItem(BView* view);
void _SolveLayout();
void _UpdateAreaConstraints(); void _UpdateAreaConstraints();
BSize _CalculateMinSize(); BSize _CalculateMinSize();
@@ -152,8 +150,10 @@ private:
Area* fCurrentArea; Area* fCurrentArea;
#if USE_SCALE_VARIABLE
Variable* fScaleWidth; Variable* fScaleWidth;
Variable* fScaleHeight; Variable* fScaleHeight;
#endif
}; };
} // namespace BALM } // namespace BALM
+11 -1
View File
@@ -20,6 +20,9 @@
#include "Tab.h" #include "Tab.h"
#define USE_SCALE_VARIABLE 1
class Constraint; class Constraint;
@@ -106,6 +109,7 @@ public:
private: private:
Area(BLayoutItem* item); Area(BLayoutItem* item);
#if USE_SCALE_VARIABLE
void _Init(LinearSpec* ls, XTab* left, YTab* top, void _Init(LinearSpec* ls, XTab* left, YTab* top,
XTab* right, YTab* bottom, XTab* right, YTab* bottom,
Variable* scaleWidth, Variable* scaleWidth,
@@ -113,6 +117,11 @@ private:
void _Init(LinearSpec* ls, Row* row, Column* column, void _Init(LinearSpec* ls, Row* row, Column* column,
Variable* scaleWidth, Variable* scaleWidth,
Variable* scaleHeight); Variable* scaleHeight);
#else
void _Init(LinearSpec* ls, XTab* left, YTab* top,
XTab* right, YTab* bottom);
void _Init(LinearSpec* ls, Row* row, Column* column);
#endif
void _DoLayout(); void _DoLayout();
@@ -152,9 +161,10 @@ private:
double fContentAspectRatio; double fContentAspectRatio;
Constraint* fContentAspectRatioC; Constraint* fContentAspectRatioC;
#if USE_SCALE_VARIABLE
Variable* fScaleWidth; Variable* fScaleWidth;
Variable* fScaleHeight; Variable* fScaleHeight;
#endif
public: public:
friend class BALMLayout; friend class BALMLayout;
+7 -5
View File
@@ -27,7 +27,6 @@ class LinearSpec;
* May render a specification infeasible. * May render a specification infeasible.
*/ */
class Constraint { class Constraint {
public: public:
int32 Index() const; int32 Index() const;
@@ -57,24 +56,26 @@ public:
const char* Label(); const char* Label();
void SetLabel(const char* label); void SetLabel(const char* label);
void WriteXML(BFile* file);
Variable* DNeg() const; Variable* DNeg() const;
Variable* DPos() const; Variable* DPos() const;
bool IsSoft() const;
bool IsValid(); bool IsValid();
void Invalidate(); void Invalidate();
operator BString() const; operator BString() const;
void GetString(BString& string) const; void GetString(BString& string) const;
void PrintToStream();
~Constraint(); ~Constraint();
protected: protected:
Constraint(LinearSpec* ls, Constraint(LinearSpec* ls,
SummandList* summands, OperatorType op, SummandList* summands, OperatorType op,
double rightSide, double penaltyNeg, double rightSide,
double penaltyPos); double penaltyNeg = -1,
double penaltyPos = -1);
private: private:
LinearSpec* fLS; LinearSpec* fLS;
@@ -92,6 +93,7 @@ private:
public: public:
friend class LinearSpec; friend class LinearSpec;
friend class LPSolveInterface;
}; };
+31 -40
View File
@@ -11,47 +11,51 @@
#include <List.h> #include <List.h>
#include <OS.h> #include <OS.h>
#include <Size.h>
#include <String.h> #include <String.h>
#include <SupportDefs.h> #include <SupportDefs.h>
#include "Constraint.h" #include "Constraint.h"
#include "LinearProgrammingTypes.h" #include "LinearProgrammingTypes.h"
#include "PenaltyFunction.h"
#include "Summand.h" #include "Summand.h"
#include "Variable.h" #include "Variable.h"
namespace LinearProgramming { namespace LinearProgramming {
class LinearSpec;
const BSize kMinSize(0, 0);
const BSize kMaxSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
class SolverInterface { class SolverInterface {
public: public:
SolverInterface(LinearSpec* linSpec);
virtual ~SolverInterface() {} virtual ~SolverInterface() {}
virtual ResultType Solve(VariableList& variables) = 0; virtual ResultType Solve() = 0;
virtual double GetObjectiveValue() = 0;
virtual bool AddVariable() = 0; virtual bool VariableAdded(Variable* variable) = 0;
virtual bool RemoveVariable(int variable) = 0; virtual bool VariableRemoved(Variable* variable) = 0;
virtual bool SetVariableRange(int variable, double min, virtual bool VariableRangeChanged(Variable* variable) = 0;
double max) = 0;
virtual bool AddConstraint(int nElements, virtual bool ConstraintAdded(Constraint* constraint) = 0;
double* coefficients, int* variableIndices, virtual bool ConstraintRemoved(Constraint* constraint) = 0;
OperatorType op, double rightSide) = 0; virtual bool LeftSideChanged(Constraint* constraint) = 0;
virtual bool RemoveConstraint(int constraint) = 0; virtual bool RightSideChanged(Constraint* constraint) = 0;
virtual bool SetLeftSide(int constraint, int nElements, virtual bool OperatorChanged(Constraint* constraint) = 0;
double* coefficients,
int* variableIndices) = 0;
virtual bool SetRightSide(int constraint, double value) = 0;
virtual bool SetOperator(int constraint,
OperatorType op) = 0;
virtual bool SetObjectiveFunction(int nElements,
double* coefficients,
int* variableIndices) = 0;
virtual bool SetOptimization(OptimizationType value) = 0;
virtual bool SaveModel(const char* fileName) = 0; virtual bool SaveModel(const char* fileName) = 0;
virtual BSize MinSize(Variable* width, Variable* height) = 0;
virtual BSize MaxSize(Variable* width, Variable* height) = 0;
protected:
LinearSpec* fLinearSpec;
}; };
@@ -113,31 +117,22 @@ public:
OperatorType op, double rightSide, OperatorType op, double rightSide,
double penaltyNeg, double penaltyPos); double penaltyNeg, double penaltyPos);
PenaltyFunction* AddPenaltyFunction(Variable* var, BList* xs, BSize MinSize(Variable* width, Variable* height);
BList* gs); BSize MaxSize(Variable* width, Variable* height);
SummandList* ObjectiveFunction();
//! Caller takes ownership of the Summand's and the SummandList.
SummandList* SwapObjectiveFunction(
SummandList* objFunction);
void SetObjectiveFunction(SummandList* objFunction);
void UpdateObjectiveFunction();
ResultType Solve(); ResultType Solve();
bool Save(const char* fileName); bool Save(const char* fileName);
int32 CountColumns() const; int32 CountColumns() const;
OptimizationType Optimization() const;
void SetOptimization(OptimizationType value);
ResultType Result() const; ResultType Result() const;
double ObjectiveValue() const; bigtime_t SolvingTime() const;
double SolvingTime() const;
operator BString() const; operator BString() const;
void GetString(BString& string) const; void GetString(BString& string) const;
const ConstraintList& Constraints() const; const ConstraintList& Constraints() const;
const VariableList& Variables() const;
protected: protected:
friend class Constraint; friend class Constraint;
@@ -152,14 +147,10 @@ private:
OperatorType op, double rightSide, OperatorType op, double rightSide,
double penaltyNeg, double penaltyPos); double penaltyNeg, double penaltyPos);
OptimizationType fOptimization;
SummandList* fObjFunction;
VariableList fVariables; VariableList fVariables;
ConstraintList fConstraints; ConstraintList fConstraints;
ResultType fResult; ResultType fResult;
double fObjectiveValue; bigtime_t fSolvingTime;
double fSolvingTime;
SolverInterface* fSolver; SolverInterface* fSolver;
}; };
-50
View File
@@ -1,50 +0,0 @@
/*
* Copyright 2007-2008, Christof Lutteroth, [email protected]
* Copyright 2007-2008, James Kim, [email protected]
* Distributed under the terms of the MIT License.
*/
#ifndef PENALTY_FUNCTION_H
#define PENALTY_FUNCTION_H
#include <List.h>
namespace LinearProgramming {
class LinearSpec;
class Variable;
/**
* Penalty function.
*/
class PenaltyFunction {
protected:
PenaltyFunction(LinearSpec* ls, Variable* var, BList* xs, BList* gs);
public:
~PenaltyFunction();
const Variable* Var() const;
const BList* Xs() const;
const BList* Gs() const;
private:
LinearSpec* fLS;
Variable* fVar;
BList* fXs; // double
BList* fGs; // double
BList* fConstraints;
BList* fObjFunctionSummands;
public:
friend class LinearSpec;
};
} // namespace LinearProgramming
using LinearProgramming::PenaltyFunction;
#endif // PENALTY_FUNCTION_H
+1
View File
@@ -27,6 +27,7 @@ public:
Variable* Var(); Variable* Var();
void SetVar(Variable* var); void SetVar(Variable* var);
int32 VariableIndex();
private: private:
double fCoeff; double fCoeff;
Variable* fVar; Variable* fVar;
+18 -71
View File
@@ -19,8 +19,6 @@ using namespace LinearProgramming;
const BSize kUnsetSize(B_SIZE_UNSET, B_SIZE_UNSET); const BSize kUnsetSize(B_SIZE_UNSET, B_SIZE_UNSET);
const BSize kMinSize(0, 0);
const BSize kMaxSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
/*! /*!
@@ -42,7 +40,8 @@ BALMLayout::BALMLayout(float spacing, BALMLayout* friendLayout)
fTop = AddYTab(); fTop = AddYTab();
fBottom = AddYTab(); fBottom = AddYTab();
// the Left tab is always at x-position 0, and the Top tab is always at y-position 0 // the Left tab is always at x-position 0, and the Top tab is always at
// y-position 0
fLeft->SetRange(0, 0); fLeft->SetRange(0, 0);
fTop->SetRange(0, 0); fTop->SetRange(0, 0);
@@ -54,15 +53,19 @@ BALMLayout::BALMLayout(float spacing, BALMLayout* friendLayout)
fPerformancePath = NULL; fPerformancePath = NULL;
#if USE_SCALE_VARIABLE
fScaleWidth = fSolver->AddVariable(); fScaleWidth = fSolver->AddVariable();
fScaleHeight = fSolver->AddVariable(); fScaleHeight = fSolver->AddVariable();
#endif
} }
BALMLayout::~BALMLayout() BALMLayout::~BALMLayout()
{ {
#if USE_SCALE_VARIABLE
delete fScaleWidth; delete fScaleWidth;
delete fScaleHeight; delete fScaleHeight;
#endif
} }
@@ -484,7 +487,11 @@ BALMLayout::AddItem(BLayoutItem* item, XTab* left, YTab* top, XTab* right,
return NULL; return NULL;
fCurrentArea = area; fCurrentArea = area;
#if USE_SCALE_VARIABLE
area->_Init(fSolver, left, top, right, bottom, fScaleWidth, fScaleHeight); area->_Init(fSolver, left, top, right, bottom, fScaleWidth, fScaleHeight);
#else
area->_Init(fSolver, left, top, right, bottom);
#endif
return area; return area;
} }
@@ -499,7 +506,11 @@ BALMLayout::AddItem(BLayoutItem* item, Row* row, Column* column)
return NULL; return NULL;
fCurrentArea = area; fCurrentArea = area;
#if USE_SCALE_VARIABLE
area->_Init(fSolver, row, column, fScaleWidth, fScaleHeight); area->_Init(fSolver, row, column, fScaleWidth, fScaleHeight);
#else
area->_Init(fSolver, row, column);
#endif
return area; return area;
} }
@@ -701,7 +712,7 @@ BALMLayout::DerivedLayoutItems()
Right()->SetRange(area.right, area.right); Right()->SetRange(area.right, area.right);
Bottom()->SetRange(area.bottom, area.bottom); Bottom()->SetRange(area.bottom, area.bottom);
_SolveLayout(); fSolver->Solve();
// if new layout is infeasible, use previous layout // if new layout is infeasible, use previous layout
if (fSolver->Result() == kInfeasible) if (fSolver->Result() == kInfeasible)
@@ -786,36 +797,6 @@ BALMLayout::_CreateLayoutItem(BView* view)
} }
void
BALMLayout::_SolveLayout()
{
// Try to solve the layout until the result is kOptimal or kInfeasible,
// maximally 15 tries sometimes the solving algorithm encounters numerical
// problems (NUMFAILURE), and repeating the solving often helps to overcome
// them.
BFile* file = NULL;
if (fPerformancePath != NULL) {
file = new(std::nothrow) BFile(fPerformancePath,
B_READ_WRITE | B_CREATE_FILE | B_OPEN_AT_END);
}
ResultType result;
for (int32 tries = 0; tries < 15; tries++) {
result = fSolver->Solve();
if (fPerformancePath != NULL) {
/*char buffer [100];
file->Write(buffer, sprintf(buffer, "%d\t%fms\t#vars=%ld\t"
"#constraints=%ld\n", result, fSolver->SolvingTime(),
fSolver->Variables()->CountItems(),
fSolver->Constraints()->CountItems()));*/
}
if (result == kOptimal || result == kInfeasible)
break;
}
delete file;
}
/** /**
* Caculates the miminum size. * Caculates the miminum size.
*/ */
@@ -824,24 +805,7 @@ BALMLayout::_CalculateMinSize()
{ {
_UpdateAreaConstraints(); _UpdateAreaConstraints();
SummandList* newObjFunction = new(std::nothrow) SummandList(2); return fSolver->MinSize(Right(), Bottom());
newObjFunction->AddItem(new(std::nothrow) Summand(1.0, fRight));
newObjFunction->AddItem(new(std::nothrow) Summand(1.0, fBottom));
SummandList* oldObjFunction = fSolver->SwapObjectiveFunction(
newObjFunction);
_SolveLayout();
fSolver->SetObjectiveFunction(oldObjFunction);
if (fSolver->Result() == kUnbounded)
return kMinSize;
if (fSolver->Result() != kOptimal) {
fSolver->Save("failed-layout.txt");
printf("Could not solve the layout specification (%d). "
"Saved specification in file failed-layout.txt", fSolver->Result());
}
return BSize(Right()->Value() - Left()->Value(),
Bottom()->Value() - Top()->Value());
} }
@@ -853,24 +817,7 @@ BALMLayout::_CalculateMaxSize()
{ {
_UpdateAreaConstraints(); _UpdateAreaConstraints();
SummandList* newObjFunction = new(std::nothrow) SummandList(2); return fSolver->MaxSize(Right(), Bottom());
newObjFunction->AddItem(new(std::nothrow) Summand(-1.0, fRight));
newObjFunction->AddItem(new(std::nothrow) Summand(-1.0, fBottom));
SummandList* oldObjFunction = fSolver->SwapObjectiveFunction(
newObjFunction);
_SolveLayout();
fSolver->SetObjectiveFunction(oldObjFunction);
if (fSolver->Result() == kUnbounded)
return kMaxSize;
if (fSolver->Result() != kOptimal) {
fSolver->Save("failed-layout.txt");
printf("Could not solve the layout specification (%d). "
"Saved specification in file failed-layout.txt", fSolver->Result());
}
return BSize(Right()->Value() - Left()->Value(),
Bottom()->Value() - Top()->Value());
} }
@@ -882,7 +829,7 @@ BALMLayout::_CalculatePreferredSize()
{ {
_UpdateAreaConstraints(); _UpdateAreaConstraints();
_SolveLayout(); fSolver->Solve();
if (fSolver->Result() != kOptimal) { if (fSolver->Result() != kOptimal) {
fSolver->Save("failed-layout.txt"); fSolver->Save("failed-layout.txt");
printf("Could not solve the layout specification (%d). " printf("Could not solve the layout specification (%d). "
+35 -7
View File
@@ -599,19 +599,24 @@ Area::Area(BLayoutItem* item)
/** /**
* Initialize variables. * Initialize variables.
*/ */
#if USE_SCALE_VARIABLE
void void
Area::_Init(LinearSpec* ls, XTab* left, YTab* top, XTab* right, YTab* bottom, Area::_Init(LinearSpec* ls, XTab* left, YTab* top, XTab* right, YTab* bottom,
Variable* scaleWidth, Variable* scaleHeight) Variable* scaleWidth, Variable* scaleHeight)
{ {
fScaleWidth = scaleWidth;
fScaleHeight = scaleHeight;
#else
void
Area::_Init(LinearSpec* ls, XTab* left, YTab* top, XTab* right, YTab* bottom)
{
#endif
fLS = ls; fLS = ls;
fLeft = left; fLeft = left;
fRight = right; fRight = right;
fTop = top; fTop = top;
fBottom = bottom; fBottom = bottom;
fScaleWidth = scaleWidth;
fScaleHeight = scaleHeight;
// adds the two essential constraints of the area that make sure that the // adds the two essential constraints of the area that make sure that the
// left x-tab is really to the left of the right x-tab, and the top y-tab // left x-tab is really to the left of the right x-tab, and the top y-tab
// really above the bottom y-tab // really above the bottom y-tab
@@ -621,6 +626,7 @@ Area::_Init(LinearSpec* ls, XTab* left, YTab* top, XTab* right, YTab* bottom,
fConstraints.AddItem(fMinContentWidth); fConstraints.AddItem(fMinContentWidth);
fConstraints.AddItem(fMinContentHeight); fConstraints.AddItem(fMinContentHeight);
#if USE_SCALE_VARIABLE
fPreferredContentWidth = fLS->AddConstraint(-1.0, fLeft, 1.0, fRight, -1.0, fPreferredContentWidth = fLS->AddConstraint(-1.0, fLeft, 1.0, fRight, -1.0,
fScaleWidth, kEQ, 0, fShrinkPenalties.Width(), fScaleWidth, kEQ, 0, fShrinkPenalties.Width(),
fGrowPenalties.Width()); fGrowPenalties.Width());
@@ -628,18 +634,34 @@ Area::_Init(LinearSpec* ls, XTab* left, YTab* top, XTab* right, YTab* bottom,
fPreferredContentHeight = fLS->AddConstraint(-1.0, fTop, 1.0, fBottom, -1.0, fPreferredContentHeight = fLS->AddConstraint(-1.0, fTop, 1.0, fBottom, -1.0,
fScaleHeight, kEQ, 0, fShrinkPenalties.Height(), fScaleHeight, kEQ, 0, fShrinkPenalties.Height(),
fGrowPenalties.Height()); fGrowPenalties.Height());
#else
BSize preferredSize = fLayoutItem->PreferredSize();
fPreferredContentWidth = fLS->AddConstraint(-1.0, fLeft, 1.0, fRight, kEQ,
0, fShrinkPenalties.Width(), fGrowPenalties.Width());
_UpdatePreferredWidthConstraint(preferredSize);
fPreferredContentHeight = fLS->AddConstraint(-1.0, fTop, 1.0, fBottom, kEQ,
0, fShrinkPenalties.Height(), fGrowPenalties.Height());
_UpdatePreferredHeightConstraint(preferredSize);
#endif
fConstraints.AddItem(fPreferredContentWidth); fConstraints.AddItem(fPreferredContentWidth);
fConstraints.AddItem(fPreferredContentHeight); fConstraints.AddItem(fPreferredContentHeight);
} }
#if USE_SCALE_VARIABLE
void void
Area::_Init(LinearSpec* ls, Row* row, Column* column, Variable* scaleWidth, Area::_Init(LinearSpec* ls, Row* row, Column* column, Variable* scaleWidth,
Variable* scaleHeight) Variable* scaleHeight)
{ {
_Init(ls, column->Left(), row->Top(), column->Right(), row->Bottom(), _Init(ls, column->Left(), row->Top(), column->Right(), row->Bottom(),
scaleWidth, scaleHeight); scaleWidth, scaleHeight);
#else
void
Area::_Init(LinearSpec* ls, Row* row, Column* column)
{
_Init(ls, column->Left(), row->Top(), column->Right(), row->Bottom());
#endif
fRow = row; fRow = row;
fColumn = column; fColumn = column;
} }
@@ -723,22 +745,28 @@ Area::_UpdateMaxSizeConstraint(BSize max)
void void
Area::_UpdatePreferredWidthConstraint(BSize& preferred) Area::_UpdatePreferredWidthConstraint(BSize& preferred)
{ {
float width = 32000; float width = 0;
if (preferred.width > 0) if (preferred.width > 0)
width = preferred.Width() + LeftInset() + RightInset(); width = preferred.Width() + LeftInset() + RightInset();
#if USE_SCALE_VARIABLE
fPreferredContentWidth->SetLeftSide(-1.0, fLeft, 1.0, fRight, -width, fPreferredContentWidth->SetLeftSide(-1.0, fLeft, 1.0, fRight, -width,
fScaleWidth); fScaleWidth);
#else
fPreferredContentWidth->SetRightSide(width);
#endif
} }
void void
Area::_UpdatePreferredHeightConstraint(BSize& preferred) Area::_UpdatePreferredHeightConstraint(BSize& preferred)
{ {
float height = 32000; float height = 0;
if (preferred.height > 0) if (preferred.height > 0)
height = preferred.Height() + TopInset() + BottomInset(); height = preferred.Height() + TopInset() + BottomInset();
#if USE_SCALE_VARIABLE
fPreferredContentHeight->SetLeftSide(-1.0, fTop, 1.0, fBottom, -height, fPreferredContentHeight->SetLeftSide(-1.0, fTop, 1.0, fBottom, -height,
fScaleHeight); fScaleHeight);
#else
fPreferredContentHeight->SetRightSide(height);
#endif
} }
+542
View File
@@ -0,0 +1,542 @@
#include "ActiveSetSolver.h"
#include <stdio.h>
#include "LayoutOptimizer.h"
//#define DEBUG_ACTIVE_SOLVER
#ifdef DEBUG_ACTIVE_SOLVER
#include <stdio.h>
#define TRACE(x...) printf(x)
#else
#define TRACE(x...) /* nothing */
#endif
using namespace LinearProgramming;
using namespace BPrivate::Layout;
template<typename Type>
static inline void
swap(Type& a, Type& b)
{
Type c = a;
a = b;
b = c;
}
EquationSystem::EquationSystem(int32 rows, int32 columns)
:
fRows(rows),
fColumns(columns)
{
fMatrix = allocate_matrix(fRows, fColumns);
fB = new double[fColumns];
// better init all values to prevent side cases where not all variables
// needed to solve the problem, coping theses values to the results could
// cause problems
for (int i = 0; i < fColumns; i++)
fB[i] = 0;
zero_matrix(fMatrix, fRows, fColumns);
fRowIndices = new int32[fRows];
fColumnIndices = new int32[fColumns];
for (int i = 0; i < fRows; i++)
fRowIndices[i] = i;
for (int i = 0; i < fColumns; i++)
fColumnIndices[i] = i;
}
EquationSystem::~EquationSystem()
{
free_matrix(fMatrix);
delete[] fB;
delete[] fRowIndices;
delete[] fColumnIndices;
}
void
EquationSystem::SetRows(int32 rows)
{
fRows = rows;
}
int32
EquationSystem::Rows()
{
return fRows;
}
int32
EquationSystem::Columns()
{
return fColumns;
}
double&
EquationSystem::A(int32 row, int32 column)
{
return fMatrix[fRowIndices[row]][fColumnIndices[column]];
}
double&
EquationSystem::B(int32 row)
{
return fB[row];
}
void
EquationSystem::Results(double* results, int32 size)
{
for (int i = 0; i < size; i++)
results[i] = 0;
for (int i = 0; i < fColumns; i++) {
int32 index = fColumnIndices[i];
if (index < fRows)
results[index] = fB[i];
}
}
void
EquationSystem::SwapColumn(int32 i, int32 j)
{
swap(fColumnIndices[i], fColumnIndices[j]);
}
void
EquationSystem::SwapRow(int32 i, int32 j)
{
swap(fRowIndices[i], fRowIndices[j]);
swap(fB[i], fB[j]);
}
bool
EquationSystem::GaussJordan()
{
// basic solve
for (int i = 0; i < fRows; i++) {
// find none zero element
int swapRow = -1;
for (int r = i; r < fRows; r++) {
double& value = fMatrix[fRowIndices[r]][fColumnIndices[i]];
if (fuzzy_equals(value, 0))
continue;
swapRow = r;
break;
}
if (swapRow == -1) {
int swapColumn = -1;
for (int c = i + 1; c < fColumns; c++) {
double& value = fMatrix[fRowIndices[i]][fColumnIndices[c]];
if (fuzzy_equals(value, 0))
continue;
swapRow = i;
swapColumn = c;
break;
}
if (swapColumn == -1) {
printf("can't solve column %i\n", i);
return false;
}
SwapColumn(i, swapColumn);
}
if (i != swapRow)
SwapRow(i, swapRow);
// normalize
GaussJordan(i);
}
return true;
}
void
EquationSystem::GaussJordan(int32 i)
{
double value = fMatrix[fRowIndices[i]][fColumnIndices[i]];
for (int j = 0; j < fColumns; j++)
fMatrix[fRowIndices[i]][fColumnIndices[j]] /= value;
fB[i] /= value;
for (int r = 0; r < fRows; r++) {
if (r == i)
continue;
double q = -fMatrix[fRowIndices[r]][fColumnIndices[i]];
// don't need to do nothing, since matrix is typically sparse this
// should save some work
if (fuzzy_equals(q, 0))
continue;
for (int c = 0; c < fColumns; c++)
fMatrix[fRowIndices[r]][fColumnIndices[c]]
+= fMatrix[fRowIndices[i]][fColumnIndices[c]] * q;
fB[r] += fB[i] * q;
}
}
void
EquationSystem::RemoveLinearlyDependentRows()
{
double oldB[fRows];
for (int r = 0; r < fRows; r++)
oldB[r] = fB[r];
double** temp = allocate_matrix(fRows, fColumns);
bool independentRows[fRows];
// copy to temp
copy_matrix(fMatrix, temp, fRows, fColumns);
int nIndependent = compute_dependencies(temp, fRows, fColumns,
independentRows);
if (nIndependent == fRows)
return;
// remove the rows
for (int i = 0; i < fRows; i++) {
if (!independentRows[i]) {
int lastDepRow = -1;
for (int d = fRows - 1; d > i; d--) {
if (independentRows[d]) {
lastDepRow = d;
break;
}
}
if (lastDepRow < 0)
break;
SwapRow(i, lastDepRow);
fRows--;
}
}
fRows = nIndependent;
free_matrix(temp);
}
void
EquationSystem::RemoveUnusedVariables()
{
for (int c = 0; c < fColumns; c++) {
bool used = false;
for (int r = 0; r < fRows; r++) {
if (!fuzzy_equals(fMatrix[r][fColumnIndices[c]], 0)) {
used = true;
break;
}
}
if (used)
continue;
//MoveColumnRight(c, fColumns - 1);
SwapColumn(c, fColumns - 1);
fColumns--;
c--;
}
}
void
EquationSystem::MoveColumnRight(int32 i, int32 target)
{
int32 index = fColumnIndices[i];
for (int c = i; c < target; c++)
fColumnIndices[c] = fColumnIndices[c + 1];
fColumnIndices[target] = index;
}
void
EquationSystem::Print()
{
for (int m = 0; m < fRows; m++) {
for (int n = 0; n < fColumns; n++)
printf("%.1f ", fMatrix[fRowIndices[m]][fColumnIndices[n]]);
printf("= %.1f\n", fB[m]);
}
}
ActiveSetSolver::ActiveSetSolver(LinearSpec* linearSpec)
:
SolverInterface(linearSpec),
fVariables(linearSpec->Variables()),
fConstraints(linearSpec->Constraints())
{
}
ActiveSetSolver::~ActiveSetSolver()
{
}
/* Using algorithm found in:
Solving Inequalities and Proving Farkas's Lemma Made Easy
David Avis and Bohdan Kaluzny
The American Mathematical Monthly
Vol. 111, No. 2 (Feb., 2004), pp. 152-157 */
bool
solve(EquationSystem& system)
{
// basic solve
if (!system.GaussJordan())
return false;
bool done = false;
while (!done) {
double smallestB = HUGE_VALF;
int smallestBRow = -1;
for (int row = 0; row < system.Rows(); row++) {
if (system.B(row) > 0 || fuzzy_equals(system.B(row), 0))
continue;
double bValue = fabs(system.B(row));
if (bValue < smallestB) {
smallestB = bValue;
smallestBRow = row;
}
}
if (smallestBRow == -1) {
done = true;
break;
}
int negValueCol = -1;
for (int col = system.Rows(); col < system.Columns(); col++) {
double value = system.A(smallestBRow, col);
if (value > 0 || fuzzy_equals(value, 0))
continue;
negValueCol = col;
break;
}
if (negValueCol == -1) {
printf("can't solve\n");
return false;
}
system.SwapColumn(smallestBRow, negValueCol);
// eliminate
system.GaussJordan(smallestBRow);
}
return true;
}
ResultType
ActiveSetSolver::Solve()
{
int32 nConstraints = fConstraints.CountItems();
int32 nVariables = fVariables.CountItems();
if (nVariables > nConstraints) {
printf("More variables then constraints! vars: %i, constraints: %i\n",
(int)nVariables, (int)nConstraints);
return kInfeasible;
}
/* First find an initial solution and then optimize it using the active set
method. */
EquationSystem system(nConstraints, nVariables + nConstraints);
int32 slackIndex = nVariables;
// setup constraint matrix and add slack variables if necessary
int32 rowIndex = 0;
for (int32 c = 0; c < nConstraints; c++) {
Constraint* constraint = fConstraints.ItemAt(c);
if (constraint->IsSoft())
continue;
SummandList* leftSide = constraint->LeftSide();
system.B(rowIndex) = constraint->RightSide();
for (int32 sIndex = 0; sIndex < leftSide->CountItems(); sIndex++ ) {
Summand* summand = leftSide->ItemAt(sIndex);
int32 coefficient = summand->Coeff();
system.A(rowIndex, summand->VariableIndex()) = coefficient;
}
if (constraint->Op() == kLE) {
system.A(rowIndex, slackIndex) = 1;
slackIndex++;
} else if (constraint->Op() == kGE) {
system.A(rowIndex, slackIndex) = -1;
slackIndex++;
}
rowIndex++;
}
system.SetRows(rowIndex);
system.RemoveLinearlyDependentRows();
system.RemoveUnusedVariables();
if (!solve(system))
return kInfeasible;
double results[nVariables + nConstraints];
system.Results(results, nVariables + nConstraints);
printf("base system solved\n");
LayoutOptimizer optimizer(fConstraints, nVariables);
optimizer.Solve(results);
// back to the variables
for (int32 i = 0; i < nVariables; i++)
fVariables.ItemAt(i)->SetValue(results[i]);
for (int32 i = 0; i < nVariables; i++)
TRACE("var %f\n", results[i]);
return kOptimal;
}
bool
ActiveSetSolver::VariableAdded(Variable* variable)
{
// TODO: error checks
fVariableGEConstraints.AddItem(NULL);
fVariableLEConstraints.AddItem(NULL);
return true;
}
bool
ActiveSetSolver::VariableRemoved(Variable* variable)
{
fVariableGEConstraints.RemoveItemAt(variable->Index());
fVariableLEConstraints.RemoveItemAt(variable->Index());
return true;
}
bool
ActiveSetSolver::VariableRangeChanged(Variable* variable)
{
double min = variable->Min();
double max = variable->Max();
int32 variableIndex = variable->Index();
Constraint* constraintGE = fVariableGEConstraints.ItemAt(variableIndex);
Constraint* constraintLE = fVariableLEConstraints.ItemAt(variableIndex);
if (constraintGE == NULL && min > -20000) {
constraintGE = fLinearSpec->AddConstraint(1, variable, kGE, 0);
if (constraintGE == NULL)
return false;
fVariableGEConstraints.RemoveItemAt(variableIndex);
fVariableGEConstraints.AddItem(constraintGE, variableIndex);
}
if (constraintLE == NULL && max < 20000) {
constraintLE = fLinearSpec->AddConstraint(1, variable, kLE, 20000);
if (constraintLE == NULL)
return false;
fVariableLEConstraints.RemoveItemAt(variableIndex);
fVariableLEConstraints.AddItem(constraintLE, variableIndex);
}
if (constraintGE)
constraintGE->SetRightSide(min);
if (constraintLE)
constraintLE->SetRightSide(max);
return true;
}
bool
ActiveSetSolver::ConstraintAdded(Constraint* constraint)
{
return true;
}
bool
ActiveSetSolver::ConstraintRemoved(Constraint* constraint)
{
return true;
}
bool
ActiveSetSolver::LeftSideChanged(Constraint* constraint)
{
return true;
}
bool
ActiveSetSolver::RightSideChanged(Constraint* constraint)
{
return true;
}
bool
ActiveSetSolver::OperatorChanged(Constraint* constraint)
{
return true;
}
bool
ActiveSetSolver::SaveModel(const char* fileName)
{
return false;
}
BSize
ActiveSetSolver::MinSize(Variable* width, Variable* height)
{
Constraint* heightConstraint = fLinearSpec->AddConstraint(1, height,
kEQ, 0, 5, 5);
Constraint* widthConstraint = fLinearSpec->AddConstraint(1, width,
kEQ, 0, 5, 5);
ResultType result = Solve();
fLinearSpec->RemoveConstraint(heightConstraint);
fLinearSpec->RemoveConstraint(widthConstraint);
if (result == kUnbounded)
return kMinSize;
if (result != kOptimal)
printf("Could not solve the layout specification (%d). ", result);
return BSize(width->Value(), height->Value());
}
BSize
ActiveSetSolver::MaxSize(Variable* width, Variable* height)
{
const double kHugeValue = 32000;
Constraint* heightConstraint = fLinearSpec->AddConstraint(1, height,
kEQ, kHugeValue, 5, 5);
Constraint* widthConstraint = fLinearSpec->AddConstraint(1, width,
kEQ, kHugeValue, 5, 5);
ResultType result = Solve();
fLinearSpec->RemoveConstraint(heightConstraint);
fLinearSpec->RemoveConstraint(widthConstraint);
if (result == kUnbounded)
return kMinSize;
if (result != kOptimal)
printf("Could not solve the layout specification (%d). ", result);
return BSize(width->Value(), height->Value());
}
+82
View File
@@ -0,0 +1,82 @@
/*
* Copyright 2010, Clemens Zeidler <[email protected]>
* Distributed under the terms of the MIT License.
*/
#ifndef ACTICE_SET_SOLVER_H
#define ACTICE_SET_SOLVER_H
#include "LinearSpec.h"
class EquationSystem {
public:
EquationSystem(int32 rows, int32 columns);
~EquationSystem();
void SetRows(int32 rows);
int32 Rows();
int32 Columns();
inline double& A(int32 row, int32 column);
inline double& B(int32 row);
/*! Copy the solved variables into results, keeping the original
variable order. */
inline void Results(double* results, int32 size);
inline void SwapColumn(int32 i, int32 j);
inline void SwapRow(int32 i, int32 j);
bool GaussJordan();
/*! Gauss Jordan elimination just for one column, the diagonal
element must be none zero. */
void GaussJordan(int32 column);
void RemoveLinearlyDependentRows();
void RemoveUnusedVariables();
void MoveColumnRight(int32 i, int32 target);
void Print();
private:
int32* fRowIndices;
int32* fColumnIndices;
double** fMatrix;
double* fB;
int32 fRows;
int32 fColumns;
};
class ActiveSetSolver : public LinearProgramming::SolverInterface {
public:
ActiveSetSolver(LinearSpec* linearSpec);
~ActiveSetSolver();
ResultType Solve();
bool VariableAdded(Variable* variable);
bool VariableRemoved(Variable* variable);
bool VariableRangeChanged(Variable* variable);
bool ConstraintAdded(Constraint* constraint);
bool ConstraintRemoved(Constraint* constraint);
bool LeftSideChanged(Constraint* constraint);
bool RightSideChanged(Constraint* constraint);
bool OperatorChanged(Constraint* constraint);
bool SaveModel(const char* fileName);
BSize MinSize(Variable* width, Variable* height);
BSize MaxSize(Variable* width, Variable* height);
public:
const VariableList& fVariables;
const ConstraintList& fConstraints;
ConstraintList fVariableGEConstraints;
ConstraintList fVariableLEConstraints;
};
#endif // ACTICE_SET_SOLVER_H
+28 -78
View File
@@ -34,11 +34,10 @@ int32
Constraint::Index() const Constraint::Index() const
{ {
int32 i = fLS->Constraints().IndexOf(this); int32 i = fLS->Constraints().IndexOf(this);
if (i == -1) { if (i == -1)
STRACE(("Constraint not part of fLS->Constraints().")); STRACE(("Constraint not part of fLS->Constraints()."));
return -1;
} return i;
return i + 1;
} }
@@ -222,22 +221,7 @@ Constraint::SetPenaltyNeg(double value)
{ {
fPenaltyNeg = value; fPenaltyNeg = value;
if (!fIsValid) fLS->UpdateLeftSide(this);
return;
if (fDNegObjSummand == NULL) {
fDNegObjSummand = new(std::nothrow) Summand(value, fLS->AddVariable());
fLS->ObjectiveFunction()->AddItem(fDNegObjSummand);
fLS->UpdateLeftSide(this);
fLS->UpdateObjectiveFunction();
return;
}
if (value == fDNegObjSummand->Coeff())
return;
fDNegObjSummand->SetCoeff(value);
fLS->UpdateObjectiveFunction();
} }
@@ -263,22 +247,7 @@ Constraint::SetPenaltyPos(double value)
{ {
fPenaltyPos = value; fPenaltyPos = value;
if (!fIsValid) fLS->UpdateLeftSide(this);
return;
if (fDPosObjSummand == NULL) {
fDPosObjSummand = new(std::nothrow) Summand(value, fLS->AddVariable());
fLS->ObjectiveFunction()->AddItem(fDPosObjSummand);
fLS->UpdateLeftSide(this);
fLS->UpdateObjectiveFunction();
return;
}
if (value == fDPosObjSummand->Coeff())
return;
fDPosObjSummand->SetCoeff(value);
fLS->UpdateObjectiveFunction();
} }
@@ -296,47 +265,6 @@ Constraint::SetLabel(const char* label)
} }
void
Constraint::WriteXML(BFile* file)
{
if (!file->IsWritable())
return;
char buffer[200];
file->Write(buffer, sprintf(buffer, "\t<constraint>\n"));
file->Write(buffer, sprintf(buffer, "\t\t<leftside>\n"));
Summand* summand;
for (int32 i = 0; i < fLeftSide->CountItems(); i++) {
summand = (Summand*)fLeftSide->ItemAt(i);
file->Write(buffer, sprintf(buffer, "\t\t\t<summand>\n"));
file->Write(buffer, sprintf(buffer, "\t\t\t\t<coeff>%f</coeff>\n",
summand->Coeff()));
BString varStr = *(summand->Var());
file->Write(buffer, sprintf(buffer, "\t\t\t\t<var>%s</var>\n",
varStr.String()));
file->Write(buffer, sprintf(buffer, "\t\t\t</summand>\n"));
}
file->Write(buffer, sprintf(buffer, "\t\t</leftside>\n"));
const char* op = "??";
if (fOp == kEQ)
op = "EQ";
else if (fOp == kLE)
op = "LE";
else if (fOp == kGE)
op = "GE";
file->Write(buffer, sprintf(buffer, "\t\t<op>%s</op>\n", op));
file->Write(buffer, sprintf(buffer, "\t\t<rightside>%f</rightside>\n", fRightSide));
//~ file->Write(buffer, sprintf(buffer, "\t\t<penaltyneg>%s</penaltyneg>\n", PenaltyNeg()));
//~ file->Write(buffer, sprintf(buffer, "\t\t<penaltypos>%s</penaltypos>\n", PenaltyPos()));
file->Write(buffer, sprintf(buffer, "\t</constraint>\n"));
}
/** /**
* Gets the slack variable for the negative variations. * Gets the slack variable for the negative variations.
* *
@@ -365,6 +293,18 @@ Constraint::DPos() const
} }
bool
Constraint::IsSoft() const
{
if (fPenaltyNeg > 0. && fOp != kLE)
return true;
if (fPenaltyPos > 0. && fOp != kGE)
return true;
return false;
}
bool bool
Constraint::IsValid() Constraint::IsValid()
{ {
@@ -404,7 +344,8 @@ Constraint::GetString(BString& string) const
for (int i = 0; i < fLeftSide->CountItems(); i++) { for (int i = 0; i < fLeftSide->CountItems(); i++) {
Summand* s = static_cast<Summand*>(fLeftSide->ItemAt(i)); Summand* s = static_cast<Summand*>(fLeftSide->ItemAt(i));
string << (float)s->Coeff() << "*"; string << (float)s->Coeff() << "*";
s->Var()->GetString(string); string << "x";
string << s->Var()->Index() - 1;
string << " "; string << " ";
} }
string << ((fOp == kEQ) ? "== " string << ((fOp == kEQ) ? "== "
@@ -419,6 +360,15 @@ Constraint::GetString(BString& string) const
} }
void
Constraint::PrintToStream()
{
BString string;
GetString(string);
printf("%s\n", string.String());
}
/** /**
* Constructor. * Constructor.
*/ */
+3 -1
View File
@@ -5,12 +5,14 @@ SetSubDirSupportedPlatformsBeOSCompatible ;
UseLibraryHeaders lp_solve linprog ; UseLibraryHeaders lp_solve linprog ;
UsePrivateHeaders shared ; UsePrivateHeaders shared ;
StaticLibrary liblinprog.a : StaticLibrary liblinprog.a :
Constraint.cpp Constraint.cpp
LinearSpec.cpp LinearSpec.cpp
LPSolveInterface.cpp LPSolveInterface.cpp
Summand.cpp Summand.cpp
PenaltyFunction.cpp
Variable.cpp Variable.cpp
LayoutOptimizer.cpp
ActiveSetSolver.cpp
; ;
+302 -55
View File
@@ -8,14 +8,20 @@
#include "LPSolveInterface.h" #include "LPSolveInterface.h"
#include <new>
using namespace LinearProgramming; using namespace LinearProgramming;
LPSolveInterface::LPSolveInterface() LPSolveInterface::LPSolveInterface(LinearSpec* linearSpec)
: :
SolverInterface(linearSpec),
fLpPresolved(NULL), fLpPresolved(NULL),
fLP(NULL) fLP(NULL),
fOptimization(kMinimize),
fObjFunction(new(std::nothrow) SummandList())
{ {
fLP = make_lp(0, 0); fLP = make_lp(0, 0);
if (fLP == NULL) if (fLP == NULL)
@@ -33,41 +39,47 @@ LPSolveInterface::~LPSolveInterface()
{ {
_RemovePresolved(); _RemovePresolved();
delete_lp(fLP); delete_lp(fLP);
for (int32 i = 0; i < fObjFunction->CountItems(); i++)
delete (Summand*)fObjFunction->ItemAt(i);
delete fObjFunction;
} }
ResultType ResultType
LPSolveInterface::Solve(VariableList& variables) LPSolveInterface::Solve()
{ {
const VariableList& variables = fLinearSpec->Variables();
if (fLpPresolved != NULL) if (fLpPresolved != NULL)
return _Presolve(variables); return _Presolve(variables);
ResultType result = (ResultType)solve(fLP); // Try to solve the layout until the result is kOptimal or kInfeasible,
// maximally 15 tries sometimes the solving algorithm encounters numerical
// problems (NUMFAILURE), and repeating the solving often helps to overcome
// them.
ResultType result = kInfeasible;
for (int32 tries = 0; tries < 15; tries++) {
result = (ResultType)solve(fLP);
if (result == OPTIMAL) { if (result == OPTIMAL) {
int32 size = variables.CountItems(); int32 size = variables.CountItems();
double x[size]; double x[size];
if (!get_variables(fLP, &x[0])) if (!get_variables(fLP, &x[0]))
printf("Error in get_variables.\n"); printf("Error in get_variables.\n");
for (int32 i = 0; i < size; i++) for (int32 i = 0; i < size; i++)
variables.ItemAt(i)->SetValue(x[i]); variables.ItemAt(i)->SetValue(x[i]);
break;
} else if (result == kInfeasible)
break;
} }
return result; return result;
} }
double
LPSolveInterface::GetObjectiveValue()
{
if (fLpPresolved)
return get_objective(fLpPresolved);
return get_objective(fLP);
}
bool bool
LPSolveInterface::AddVariable() LPSolveInterface::VariableAdded(Variable* variable)
{ {
double d = 0; double d = 0;
int i = 0; int i = 0;
@@ -79,9 +91,9 @@ LPSolveInterface::AddVariable()
bool bool
LPSolveInterface::RemoveVariable(int variable) LPSolveInterface::VariableRemoved(Variable* variable)
{ {
if (!del_column(fLP, variable)) if (!del_column(fLP, variable->Index() + 1))
return false; return false;
_RemovePresolved(); _RemovePresolved();
return true; return true;
@@ -89,9 +101,11 @@ LPSolveInterface::RemoveVariable(int variable)
bool bool
LPSolveInterface::SetVariableRange(int variable, double min, double max) LPSolveInterface::VariableRangeChanged(Variable* variable)
{ {
if (!set_bounds(fLP, variable, min, max)) double min = variable->Min();
double max = variable->Max();
if (!set_bounds(fLP, variable->Index() + 1, min, max))
return false; return false;
_RemovePresolved(); _RemovePresolved();
return true; return true;
@@ -99,22 +113,144 @@ LPSolveInterface::SetVariableRange(int variable, double min, double max)
bool bool
LPSolveInterface::AddConstraint(int nElements, double* coefficients, LPSolveInterface::ConstraintAdded(Constraint* constraint)
int* variableIndices, OperatorType op, double rightSide)
{ {
if (!add_constraintex(fLP, nElements, coefficients, variableIndices, OperatorType op = constraint->Op();
SummandList* summands = constraint->LeftSide();
double coeffs[summands->CountItems() + 2];
int variableIndices[summands->CountItems() + 2];
int32 nCoefficient = 0;
for (; nCoefficient < summands->CountItems(); nCoefficient++) {
Summand* s = summands->ItemAt(nCoefficient);
coeffs[nCoefficient] = s->Coeff();
variableIndices[nCoefficient] = s->Var()->Index() + 1;
}
double penaltyNeg = constraint->PenaltyNeg();
if (penaltyNeg > 0. && op != kLE) {
constraint->fDNegObjSummand = new(std::nothrow) Summand(
constraint->PenaltyNeg(), fLinearSpec->AddVariable());
fObjFunction->AddItem(constraint->fDNegObjSummand);
variableIndices[nCoefficient]
= constraint->fDNegObjSummand->Var()->Index() + 1;
coeffs[nCoefficient] = 1.0;
nCoefficient++;
}
double penaltyPos = constraint->PenaltyPos();
if (penaltyPos > 0. && op != kGE) {
constraint->fDPosObjSummand = new(std::nothrow) Summand(
constraint->PenaltyPos(), fLinearSpec->AddVariable());
fObjFunction->AddItem(constraint->fDPosObjSummand);
variableIndices[nCoefficient]
= constraint->fDPosObjSummand->Var()->Index() + 1;
coeffs[nCoefficient] = -1.0;
nCoefficient++;
}
double rightSide = constraint->RightSide();
if (!add_constraintex(fLP, nCoefficient, coeffs, variableIndices,
(op == kEQ ? EQ : (op == kGE) ? GE : LE), rightSide)) { (op == kEQ ? EQ : (op == kGE) ? GE : LE), rightSide)) {
return false; return false;
} }
_UpdateObjectiveFunction();
_RemovePresolved(); _RemovePresolved();
return true; return true;
} }
bool bool
LPSolveInterface::RemoveConstraint(int constraint) LPSolveInterface::ConstraintRemoved(Constraint* constraint)
{ {
if (!del_constraint(fLP, constraint)) if (constraint->fDNegObjSummand) {
fObjFunction->RemoveItem(constraint->fDNegObjSummand);
delete constraint->fDNegObjSummand->Var();
delete constraint->fDNegObjSummand;
constraint->fDNegObjSummand = NULL;
}
if (constraint->fDPosObjSummand) {
fObjFunction->RemoveItem(constraint->fDPosObjSummand);
delete constraint->fDPosObjSummand->Var();
delete constraint->fDPosObjSummand;
constraint->fDPosObjSummand = NULL;
}
if (!del_constraint(fLP, constraint->Index() + 1))
return false;
_UpdateObjectiveFunction();
_RemovePresolved();
return true;
}
bool
LPSolveInterface::LeftSideChanged(Constraint* constraint)
{
if (!constraint->IsValid())
return false;
int32 index = constraint->Index() + 1;
if (index <= 0)
return false;
SummandList* leftSide = constraint->LeftSide();
OperatorType op = constraint->Op();
double coeffs[leftSide->CountItems() + 2];
int variableIndices[leftSide->CountItems() + 2];
int32 i;
for (i = 0; i < leftSide->CountItems(); i++) {
Summand* s = leftSide->ItemAt(i);
coeffs[i] = s->Coeff();
variableIndices[i] = s->Var()->Index() + 1;
}
double penaltyNeg = constraint->PenaltyNeg();
if (penaltyNeg > 0. && op != kLE) {
if (!constraint->fDNegObjSummand) {
constraint->fDNegObjSummand = new(std::nothrow) Summand(
constraint->PenaltyNeg(), fLinearSpec->AddVariable());
fObjFunction->AddItem(constraint->fDNegObjSummand);
}
variableIndices[i] = constraint->fDNegObjSummand->Var()->Index() + 1;
coeffs[i] = 1.0;
i++;
} else {
fObjFunction->RemoveItem(constraint->fDNegObjSummand);
delete constraint->fDNegObjSummand;
constraint->fDNegObjSummand = NULL;
}
double penaltyPos = constraint->PenaltyPos();
if (penaltyPos > 0. && op != kGE) {
if (constraint->fDPosObjSummand == NULL) {
constraint->fDPosObjSummand = new(std::nothrow) Summand(penaltyPos,
fLinearSpec->AddVariable());
fObjFunction->AddItem(constraint->fDPosObjSummand);
}
variableIndices[i] = constraint->fDPosObjSummand->Var()->Index() + 1;
coeffs[i] = -1.0;
i++;
} else {
fObjFunction->RemoveItem(constraint->fDPosObjSummand);
delete constraint->fDPosObjSummand;
constraint->fDPosObjSummand = NULL;
}
if (!set_rowex(fLP, index, i, coeffs, variableIndices))
return false;
_UpdateObjectiveFunction();
_RemovePresolved();
return true;
}
bool
LPSolveInterface::RightSideChanged(Constraint* constraint)
{
if (!set_rh(fLP, constraint->Index() + 1, constraint->RightSide()))
return false; return false;
_RemovePresolved(); _RemovePresolved();
return true; return true;
@@ -122,31 +258,14 @@ LPSolveInterface::RemoveConstraint(int constraint)
bool bool
LPSolveInterface::SetLeftSide(int constraint, int nElements, LPSolveInterface::OperatorChanged(Constraint* constraint)
double* coefficients, int* variableIndices)
{ {
if (!set_rowex(fLP, constraint, nElements, coefficients, variableIndices)) int32 index = constraint->Index() + 1;
if (index <= 0)
return false; return false;
_RemovePresolved();
return true;
}
OperatorType op = constraint->Op();
bool if (!set_constr_type(fLP, index, op == kEQ) ? EQ : (op == kGE) ? GE : LE) {
LPSolveInterface::SetRightSide(int constraint, double value)
{
if (!set_rh(fLP, constraint, value))
return false;
_RemovePresolved();
return true;
}
bool
LPSolveInterface::SetOperator(int constraint, OperatorType op)
{
if (!set_constr_type(fLP, constraint, op == kEQ) ? EQ : (op == kGE) ? GE
: LE) {
return false; return false;
} }
_RemovePresolved(); _RemovePresolved();
@@ -168,7 +287,8 @@ LPSolveInterface::SetObjectiveFunction(int nElements, double* coefficients,
bool bool
LPSolveInterface::SetOptimization(OptimizationType value) LPSolveInterface::SetOptimization(OptimizationType value)
{ {
if (value == kMinimize) fOptimization = value;
if (fOptimization == kMinimize)
set_minim(fLP); set_minim(fLP);
else else
set_maxim(fLP); set_maxim(fLP);
@@ -176,6 +296,19 @@ LPSolveInterface::SetOptimization(OptimizationType value)
} }
/**
* Gets the current optimization.
* The default is minimization.
*
* @return the current optimization
*/
OptimizationType
LPSolveInterface::Optimization() const
{
return fOptimization;
}
bool bool
LPSolveInterface::SaveModel(const char* fileName) LPSolveInterface::SaveModel(const char* fileName)
{ {
@@ -186,6 +319,120 @@ LPSolveInterface::SaveModel(const char* fileName)
} }
BSize
LPSolveInterface::MinSize(Variable* width, Variable* height)
{
SummandList* newObjFunction = new(std::nothrow) SummandList(2);
newObjFunction->AddItem(new(std::nothrow) Summand(1.0, width));
newObjFunction->AddItem(new(std::nothrow) Summand(1.0, height));
SummandList* oldObjFunction = SwapObjectiveFunction(newObjFunction);
ResultType result = Solve();
SetObjectiveFunction(oldObjFunction);
if (result == kUnbounded)
return kMinSize;
if (result != kOptimal)
printf("Could not solve the layout specification (%d). ", result);
return BSize(width->Value(), height->Value());
}
BSize
LPSolveInterface::MaxSize(Variable* width, Variable* height)
{
SummandList* newObjFunction = new(std::nothrow) SummandList(2);
newObjFunction->AddItem(new(std::nothrow) Summand(-1.0, width));
newObjFunction->AddItem(new(std::nothrow) Summand(-1.0, height));
SummandList* oldObjFunction = SwapObjectiveFunction(
newObjFunction);
ResultType result = Solve();
SetObjectiveFunction(oldObjFunction);
if (result == kUnbounded)
return kMinSize;
if (result != kOptimal)
printf("Could not solve the layout specification (%d). ", result);
return BSize(width->Value(), height->Value());
}
SummandList*
LPSolveInterface::SwapObjectiveFunction(SummandList* objFunction)
{
SummandList* list = fObjFunction;
fObjFunction = objFunction;
_UpdateObjectiveFunction();
return list;
}
/**
* Sets a new objective function.
*
* @param summands SummandList containing the objective function's summands
*/
void
LPSolveInterface::SetObjectiveFunction(SummandList* objFunction)
{
for (int32 i = 0; i < fObjFunction->CountItems(); i++)
delete (Summand*)fObjFunction->ItemAt(i);
delete fObjFunction;
fObjFunction = objFunction;
_UpdateObjectiveFunction();
}
/**
* Gets the objective function.
*
* @return SummandList containing the objective function's summands
*/
SummandList*
LPSolveInterface::ObjectiveFunction()
{
return fObjFunction;
}
double
LPSolveInterface::GetObjectiveValue()
{
if (fLpPresolved)
return get_objective(fLpPresolved);
return get_objective(fLP);
}
/**
* Updates the internal representation of the objective function.
* Must be called whenever the summands of the objective function are changed.
*/
void
LPSolveInterface::_UpdateObjectiveFunction()
{
int32 size = fObjFunction->CountItems();
double coeffs[size];
int varIndexes[size];
Summand* current;
for (int32 i = 0; i < size; i++) {
current = (Summand*)fObjFunction->ItemAt(i);
coeffs[i] = current->Coeff();
varIndexes[i] = current->Var()->Index() + 1;
}
if (!SetObjectiveFunction(size, &coeffs[0], &varIndexes[0]))
printf("Error in set_obj_fnex.\n");
}
/** /**
* Remove a cached presolved model, if existent. * Remove a cached presolved model, if existent.
* This is automatically done each time after the model has been changed, * This is automatically done each time after the model has been changed,
@@ -210,7 +457,7 @@ LPSolveInterface::_RemovePresolved()
* @return the result of the solving attempt * @return the result of the solving attempt
*/ */
ResultType ResultType
LPSolveInterface::_Presolve(VariableList& variables) LPSolveInterface::_Presolve(const VariableList& variables)
{ {
if (fLpPresolved == NULL) { if (fLpPresolved == NULL) {
fLpPresolved = copy_lp(fLP); fLpPresolved = copy_lp(fLP);
@@ -225,7 +472,7 @@ LPSolveInterface::_Presolve(VariableList& variables)
for (int32 i = 0; i < size; i++) { for (int32 i = 0; i < size; i++) {
Variable* current = variables.ItemAt(i); Variable* current = variables.ItemAt(i);
current->SetValue(get_var_primalresult(fLpPresolved, current->SetValue(get_var_primalresult(fLpPresolved,
get_Norig_rows(fLpPresolved) + current->Index())); get_Norig_rows(fLpPresolved) + current->Index() + 1));
} }
} }
+37 -23
View File
@@ -11,43 +11,57 @@
#include "lp_lib.h" #include "lp_lib.h"
class LPSolveInterface : public LinearProgramming::SolverInterface { namespace LinearProgramming {
class LPSolveInterface : public SolverInterface {
public: public:
LPSolveInterface(); LPSolveInterface(LinearSpec* linearSpec);
~LPSolveInterface(); ~LPSolveInterface();
ResultType Solve(VariableList& variables); ResultType Solve();
double GetObjectiveValue();
bool AddVariable(); bool VariableAdded(Variable* variable);
bool RemoveVariable(int variable); bool VariableRemoved(Variable* variable);
bool SetVariableRange(int variable, double min, bool VariableRangeChanged(Variable* variable);
double max);
bool AddConstraint(int nElements, bool ConstraintAdded(Constraint* constraint);
double* coefficients, int* variableIndices, bool ConstraintRemoved(Constraint* constraint);
OperatorType op, double rightSide); bool LeftSideChanged(Constraint* constraint);
bool RemoveConstraint(int constraint); bool RightSideChanged(Constraint* constraint);
bool SetLeftSide(int constraint, int nElements, bool OperatorChanged(Constraint* constraint);
double* coefficients, int* variableIndices);
bool SetRightSide(int constraint, double value);
bool SetOperator(int constraint,
OperatorType op);
bool SetObjectiveFunction(int nElements,
double* coefficients,
int* variableIndices);
bool SetOptimization(OptimizationType value);
bool SaveModel(const char* fileName); bool SaveModel(const char* fileName);
BSize MinSize(Variable* width, Variable* height);
BSize MaxSize(Variable* width, Variable* height);
bool SetOptimization(OptimizationType value);
OptimizationType Optimization() const;
bool SetObjectiveFunction(int nElements,
double* coefficients,
int* variableIndices);
SummandList* ObjectiveFunction();
double GetObjectiveValue();
//! Caller takes ownership of the Summand's and the SummandList.
SummandList* SwapObjectiveFunction(
SummandList* objFunction);
void SetObjectiveFunction(SummandList* objFunction);
private: private:
ResultType _Presolve(VariableList& variables); void _UpdateObjectiveFunction();
ResultType _Presolve(const VariableList& variables);
void _RemovePresolved(); void _RemovePresolved();
lprec* fLpPresolved; lprec* fLpPresolved;
lprec* fLP; lprec* fLP;
OptimizationType fOptimization;
SummandList* fObjFunction;
}; };
} // namespace LinearProgramming
#endif // LP_SOLVE_INTERFACE_H #endif // LP_SOLVE_INTERFACE_H
+940
View File
@@ -0,0 +1,940 @@
/*
* Copyright 2007, Ingo Weinhold <[email protected]>.
* Copyright 2010, Clemens Zeidler <[email protected]>
* Distributed under the terms of the MIT License.
*/
#include "LayoutOptimizer.h"
#include <new>
#include <stdio.h>
#include <string.h>
#include <AutoDeleter.h>
//#define TRACE_LAYOUT_OPTIMIZER 1
#if TRACE_LAYOUT_OPTIMIZER
# define TRACE(format...) printf(format)
# define TRACE_ONLY(x) x
#else
# define TRACE(format...)
# define TRACE_ONLY(x)
#endif
#define TRACE_ERROR(format...) fprintf(stderr, format)
using std::nothrow;
/*! \class BPrivate::Layout::LayoutOptimizer
Given a set of layout constraints, a feasible solution, and a desired
(non-)solution this class finds an optimal solution. The optimization
criterion is to minimize the norm of the difference to the desired
(non-)solution.
It does so by implementing an active set method algorithm. The basic idea
is to start with the subset of the constraints that are barely satisfied by
the feasible solution, i.e. including all equality constraints and those
inequality constraints that are still satisfied, if restricted to equality
constraints. This set is called active set, the contained constraints active
constraints.
Considering all of the active constraints equality constraints a new
solution is computed, which still satisfies all those equality constraints
and is optimal with respect to the optimization criterion.
If the new solution equals the previous one, we find the inequality
constraint that, by keeping it in the active set, prevents us most from
further optimizing the solution. If none really does, we're done, having
found the globally optimal solution. Otherwise we remove the found
constraint from the active set and try again.
If the new solution does not equal the previous one, it might violate one
or more of the inactive constraints. If that is the case, we add the
most-violated constraint to the active set and adjust the new solution such
that barely satisfies that constraint. Otherwise, we don't adjust the
computed solution. With the adjusted respectively unadjusted solution
we enter the next iteration, i.e. by computing a new optimal solution with
respect to the active set.
*/
// #pragma mark - vector and matrix operations
// is_zero
static inline bool
is_zero(double* x, int n)
{
for (int i = 0; i < n; i++) {
if (!fuzzy_equals(x[i], 0))
return false;
}
return true;
}
// add_vectors
static inline void
add_vectors(double* x, const double* y, int n)
{
for (int i = 0; i < n; i++)
x[i] += y[i];
}
// add_vectors_scaled
static inline void
add_vectors_scaled(double* x, const double* y, double scalar, int n)
{
for (int i = 0; i < n; i++)
x[i] += y[i] * scalar;
}
// negate_vector
static inline void
negate_vector(double* x, int n)
{
for (int i = 0; i < n; i++)
x[i] = -x[i];
}
// allocate_matrix
double**
BPrivate::Layout::allocate_matrix(int m, int n)
{
double** matrix = new(nothrow) double*[m];
if (!matrix)
return NULL;
double* values = new(nothrow) double[m * n];
if (!values) {
delete[] matrix;
return NULL;
}
double* row = values;
for (int i = 0; i < m; i++, row += n)
matrix[i] = row;
return matrix;
}
// free_matrix
void
BPrivate::Layout::free_matrix(double** matrix)
{
if (matrix) {
delete[] *matrix;
delete[] matrix;
}
}
// multiply_matrix_vector
/*! y = Ax
A: m x n matrix
*/
static inline void
multiply_matrix_vector(const double* const* A, const double* x, int m, int n,
double* y)
{
for (int i = 0; i < m; i++) {
double sum = 0;
for (int k = 0; k < n; k++)
sum += A[i][k] * x[k];
y[i] = sum;
}
}
// multiply_matrices
/*! c = a*b
*/
static void
multiply_matrices(const double* const* a, const double* const* b, double** c,
int m, int n, int l)
{
for (int i = 0; i < m; i++) {
for (int j = 0; j < l; j++) {
double sum = 0;
for (int k = 0; k < n; k++)
sum += a[i][k] * b[k][j];
c[i][j] = sum;
}
}
}
// transpose_matrix
static inline void
transpose_matrix(const double* const* A, double** Atrans, int m, int n)
{
for (int i = 0; i < m; i++) {
for (int k = 0; k < n; k++)
Atrans[k][i] = A[i][k];
}
}
// zero_matrix
void
BPrivate::Layout::zero_matrix(double** A, int m, int n)
{
for (int i = 0; i < m; i++) {
for (int k = 0; k < n; k++)
A[i][k] = 0;
}
}
// copy_matrix
void
BPrivate::Layout::copy_matrix(const double* const* A, double** B, int m, int n)
{
for (int i = 0; i < m; i++) {
for (int k = 0; k < n; k++)
B[i][k] = A[i][k];
}
}
static inline void
multiply_optimization_matrix_vector(const double* x, int n, double* y)
{
// The matrix has the form:
// 2 -1 0 ... 0 0
// -1 2 -1 0 ... . .
// 0 -1 2 . .
// . 0 . . .
// . . 0 0
// . . -1 0
// 0 ... 0 -1 2 -1
// 0 ... -1 1
if (n == 1) {
y[0] = x[0];
return;
}
y[0] = 2 * x[0] - x[1];
for (int i = 1; i < n - 1; i++)
y[i] = 2 * x[i] - x[i - 1] - x[i + 1];
y[n - 1] = x[n - 1] - x[n - 2];
}
static inline void
multiply_optimization_matrix_matrix(const double* const* A, int m, int n,
double** B)
{
if (m == 1) {
memcpy(B[0], A[0], n * sizeof(double));
return;
}
for (int k = 0; k < n; k++) {
B[0][k] = 2 * A[0][k] - A[1][k];
for (int i = 1; i < m - 1; i++)
B[i][k] = 2 * A[i][k] - A[i - 1][k] - A[i + 1][k];
B[m - 1][k] = A[m - 1][k] - A[m - 2][k];
}
}
template<typename Type>
static inline void
swap(Type& a, Type& b)
{
Type c = a;
a = b;
b = c;
}
// #pragma mark - algorithms
bool
BPrivate::Layout::solve(double** a, int n, double* b)
{
// index array for row permutation
// Note: We could eliminate it, if we would permutate the row pointers of a.
int indices[n];
for (int i = 0; i < n; i++)
indices[i] = i;
// forward elimination
for (int i = 0; i < n - 1; i++) {
// find pivot
int pivot = i;
double pivotValue = fabs(a[indices[i]][i]);
for (int j = i + 1; j < n; j++) {
int index = indices[j];
double value = fabs(a[index][i]);
if (value > pivotValue) {
pivot = j;
pivotValue = value;
}
}
if (fuzzy_equals(pivotValue, 0)) {
TRACE_ERROR("solve(): matrix is not regular\n");
return false;
}
if (pivot != i) {
swap(indices[i], indices[pivot]);
swap(b[i], b[pivot]);
}
pivot = indices[i];
// eliminate
for (int j = i + 1; j < n; j++) {
int index = indices[j];
double q = -a[index][i] / a[pivot][i];
a[index][i] = 0;
for (int k = i + 1; k < n; k++)
a[index][k] += a[pivot][k] * q;
b[j] += b[i] * q;
}
}
// backwards substitution
for (int i = n - 1; i >= 0; i--) {
int index = indices[i];
double sum = b[i];
for (int j = i + 1; j < n; j++)
sum -= a[index][j] * b[j];
b[i] = sum / a[index][i];
}
return true;
}
int
BPrivate::Layout::compute_dependencies(double** a, int m, int n,
bool* independent)
{
// index array for row permutation
// Note: We could eliminate it, if we would permutate the row pointers of a.
int indices[m];
for (int i = 0; i < m; i++)
indices[i] = i;
// forward elimination
int iterations = (m > n ? n : m);
int i = 0;
int column = 0;
for (; i < iterations && column < n; i++) {
// find next pivot
int pivot = i;
do {
double pivotValue = fabs(a[indices[i]][column]);
for (int j = i + 1; j < m; j++) {
int index = indices[j];
double value = fabs(a[index][column]);
if (value > pivotValue) {
pivot = j;
pivotValue = value;
}
}
if (!fuzzy_equals(pivotValue, 0))
break;
column++;
} while (column < n);
if (column == n)
break;
if (pivot != i)
swap(indices[i], indices[pivot]);
pivot = indices[i];
independent[pivot] = true;
// eliminate
for (int j = i + 1; j < m; j++) {
int index = indices[j];
double q = -a[index][column] / a[pivot][column];
a[index][column] = 0;
for (int k = column + 1; k < n; k++)
a[index][k] += a[pivot][k] * q;
}
column++;
}
for (int j = i; j < m; j++)
independent[indices[j]] = false;
return i;
}
// remove_linearly_dependent_rows
int
BPrivate::Layout::remove_linearly_dependent_rows(double** A, double** temp,
bool* independentRows, int m, int n)
{
// copy to temp
copy_matrix(A, temp, m, n);
int count = compute_dependencies(temp, m, n, independentRows);
if (count == m)
return count;
// remove the rows
int index = 0;
for (int i = 0; i < m; i++) {
if (independentRows[i]) {
if (index < i) {
for (int k = 0; k < n; k++)
A[index][k] = A[i][k];
}
index++;
}
}
return count;
}
/*! QR decomposition using Householder transformations.
*/
bool
qr_decomposition(double** a, int m, int n, double* d, double** q)
{
if (m < n)
return false;
for (int j = 0; j < n; j++) {
// inner product of the first vector x of the (j,j) minor
double innerProductU = 0;
for (int i = j + 1; i < m; i++)
innerProductU = innerProductU + a[i][j] * a[i][j];
double innerProduct = innerProductU + a[j][j] * a[j][j];
if (fuzzy_equals(innerProduct, 0)) {
TRACE_ERROR("qr_decomposition(): 0 column %d\n", j);
return false;
}
// alpha (norm of x with opposite signedness of x_1) and thus r_{j,j}
double alpha = (a[j][j] < 0 ? sqrt(innerProduct) : -sqrt(innerProduct));
d[j] = alpha;
double beta = 1 / (alpha * a[j][j] - innerProduct);
// u = x - alpha * e_1
// (u is a[j..n][j])
a[j][j] -= alpha;
// left-multiply A_k with Q_k, thus obtaining a row of R and the A_{k+1}
// for the next iteration
for (int k = j + 1; k < n; k++) {
double sum = 0;
for (int i = j; i < m; i++)
sum += a[i][j] * a[i][k];
sum *= beta;
for (int i = j; i < m; i++)
a[i][k] += a[i][j] * sum;
}
// v = u/|u|
innerProductU += a[j][j] * a[j][j];
double beta2 = -2 / innerProductU;
// right-multiply Q with Q_k
// Q_k = I - 2vv^T
// Q * Q_k = Q - 2 * Q * vv^T
if (j == 0) {
for (int k = 0; k < m; k++) {
for (int i = 0; i < m; i++)
q[k][i] = beta2 * a[k][0] * a[i][0];
q[k][k] += 1;
}
} else {
for (int k = 0; k < m; k++) {
double sum = 0;
for (int i = j; i < m; i++)
sum += q[k][i] * a[i][j];
sum *= beta2;
for (int i = j; i < m; i++)
q[k][i] += sum * a[i][j];
}
}
}
return true;
}
// MatrixDeleter
struct MatrixDelete {
inline void operator()(double** matrix)
{
BPrivate::Layout::free_matrix(matrix);
}
};
typedef BPrivate::AutoDeleter<double*, MatrixDelete> MatrixDeleter;
// #pragma mark - LayoutOptimizer
// constructor
LayoutOptimizer::LayoutOptimizer(const ConstraintList& list,
int32 variableCount)
:
fTemp1(NULL),
fTemp2(NULL),
fZtrans(NULL),
fQ(NULL),
fSoftConstraints(NULL),
fG(NULL),
fDesired(NULL)
{
SetConstraints(list, variableCount);
}
// destructor
LayoutOptimizer::~LayoutOptimizer()
{
_MakeEmpty();
}
bool
LayoutOptimizer::SetConstraints(const ConstraintList& list, int32 variableCount)
{
fConstraints = list;
int32 constraintCount = fConstraints.CountItems();
if (fVariableCount != variableCount) {
_MakeEmpty();
_Init(variableCount, constraintCount);
}
zero_matrix(fSoftConstraints, constraintCount, fVariableCount);
double rightSide[constraintCount];
// set up soft constraint matrix
for (int32 c = 0; c < fConstraints.CountItems(); c++) {
Constraint* constraint = fConstraints.ItemAt(c);
if (!constraint->IsSoft()) {
rightSide[c] = 0;
continue;
}
rightSide[c] = _RightSide(constraint);
SummandList* summands = constraint->LeftSide();
for (int32 s = 0; s < summands->CountItems(); s++) {
Summand* summand = summands->ItemAt(s);
int32 variable = summand->Var()->Index();
if (constraint->Op() == LinearProgramming::kLE)
fSoftConstraints[c][variable] = -summand->Coeff();
else
fSoftConstraints[c][variable] = summand->Coeff();
}
}
// create G
transpose_matrix(fSoftConstraints, fTemp1, constraintCount, fVariableCount);
multiply_matrices(fTemp1, fSoftConstraints, fG, fVariableCount,
constraintCount, constraintCount);
// create d
multiply_matrix_vector(fTemp1, rightSide, fVariableCount, constraintCount,
fDesired);
negate_vector(fDesired, fVariableCount);
return true;
}
// InitCheck
status_t
LayoutOptimizer::InitCheck() const
{
if (!fTemp1 || !fTemp2 || !fZtrans || !fQ || !fSoftConstraints || !fG
|| !fDesired)
return B_NO_MEMORY;
return B_OK;
}
double
LayoutOptimizer::_ActualValue(Constraint* constraint, double* values) const
{
SummandList* summands = constraint->LeftSide();
double value = 0;
for (int32 s = 0; s < summands->CountItems(); s++) {
Summand* summand = summands->ItemAt(s);
int32 variable = summand->Var()->Index();
value += values[variable] * summand->Coeff();
}
if (constraint->Op() == LinearProgramming::kLE)
return -value;
return value;
}
double
LayoutOptimizer::_RightSide(Constraint* constraint)
{
if (constraint->Op() == LinearProgramming::kLE)
return -constraint->RightSide();
return constraint->RightSide();
}
void
LayoutOptimizer::_MakeEmpty()
{
free_matrix(fTemp1);
free_matrix(fTemp2);
free_matrix(fZtrans);
free_matrix(fSoftConstraints);
free_matrix(fQ);
free_matrix(fG);
delete[] fDesired;
}
void
LayoutOptimizer::_Init(int32 variableCount, int32 nConstraints)
{
fVariableCount = variableCount;
fTemp1 = allocate_matrix(nConstraints, nConstraints);
fTemp2 = allocate_matrix(nConstraints, nConstraints);
fZtrans = allocate_matrix(nConstraints, fVariableCount);
fSoftConstraints = allocate_matrix(nConstraints, fVariableCount);
fQ = allocate_matrix(nConstraints, fVariableCount);
fG = allocate_matrix(nConstraints, nConstraints);
fDesired = new(std::nothrow) double[fVariableCount];
}
// Solve
/*! Solves the quadratic program (QP) given by the constraints added via
AddConstraint(), the additional constraint \sum_{i=0}^{n-1} x_i = size,
and the optimization criterion to minimize
\sum_{i=0}^{n-1} (x_i - desired[i])^2.
The \a values array must contain a feasible solution when called and will
be overwritten with the optimial solution the method computes.
*/
bool
LayoutOptimizer::Solve(double* values)
{
if (values == NULL)
return false;
int32 constraintCount = fConstraints.CountItems();
// allocate the active constraint matrix and its transposed matrix
fActiveMatrix = allocate_matrix(constraintCount, fVariableCount);
fActiveMatrixTemp = allocate_matrix(constraintCount, fVariableCount);
MatrixDeleter _(fActiveMatrix);
MatrixDeleter _2(fActiveMatrixTemp);
if (!fActiveMatrix || !fActiveMatrixTemp)
return false;
bool success = _Solve(values);
return success;
}
// _Solve
bool
LayoutOptimizer::_Solve(double* values)
{
int32 constraintCount = fConstraints.CountItems();
TRACE_ONLY(
TRACE("constraints:\n");
for (int32 i = 0; i < constraintCount; i++) {
TRACE(" %-2ld: ", i);
fConstraints.ItemAt(i)->PrintToStream();
}
)
// our QP is supposed to be in this form:
// min_x 1/2x^TGx + x^Td
// s.t. a_i^Tx = b_i, i \in E
// a_i^Tx >= b_i, i \in I
// init our initial x
double x[fVariableCount];
for (int i = 0; i < fVariableCount; i++)
x[i] = values[i];
// init d
// Note that the values of d and of G result from rewriting the
// ||x - desired|| we actually want to minimize.
double d[fVariableCount];
for (int i = 0; i < fVariableCount; i++)
d[i] = fDesired[i];
// init active set
ConstraintList activeConstraints(constraintCount);
for (int32 i = 0; i < constraintCount; i++) {
Constraint* constraint = (Constraint*)fConstraints.ItemAt(i);
if (constraint->IsSoft())
continue;
double actualValue = _ActualValue(constraint, x);
TRACE("constraint %ld: actual: %f constraint: %f\n", i, actualValue,
_RightSide(constraint));
if (fuzzy_equals(actualValue, _RightSide(constraint)))
activeConstraints.AddItem(constraint);
}
// The main loop: Each iteration we try to get closer to the optimum
// solution. We compute a vector p that brings our x closer to the optimum.
// We do that by computing the QP resulting from our active constraint set,
// W^k. Afterward each iteration we adjust the active set.
TRACE_ONLY(int iteration = 0;)
while (true) {
TRACE_ONLY(
TRACE("\n[iteration %d]\n", iteration++);
TRACE("active set:\n");
for (int32 i = 0; i < activeConstraints.CountItems(); i++) {
TRACE(" ");
activeConstraints.ItemAt(i)->PrintToStream();
}
)
// solve the QP:
// min_p 1/2p^TGp + g_k^Tp
// s.t. a_i^Tp = 0
// with a_i \in activeConstraints
// g_k = Gx_k + d
// p = x - x_k
int32 activeCount = activeConstraints.CountItems();
if (activeCount == 0) {
TRACE_ERROR("Solve(): Error: No more active constraints!\n");
return false;
}
// construct a matrix from the active constraints
int am = activeCount;
const int an = fVariableCount;
bool independentRows[activeCount];
zero_matrix(fActiveMatrix, am, an);
for (int32 i = 0; i < activeCount; i++) {
Constraint* constraint = activeConstraints.ItemAt(i);
SummandList* summands = constraint->LeftSide();
for (int32 s = 0; s < summands->CountItems(); s++) {
Summand* summand = summands->ItemAt(s);
int32 variable = summand->Var()->Index();
if (constraint->Op() == LinearProgramming::kLE)
fActiveMatrix[i][variable] = -summand->Coeff();
else
fActiveMatrix[i][variable] = summand->Coeff();
}
}
// TODO: The fActiveMatrix is sparse (max 2 entries per row). There should be
// some room for optimization.
am = remove_linearly_dependent_rows(fActiveMatrix, fActiveMatrixTemp,
independentRows, am, an);
// gxd = G * x + d
double gxd[fVariableCount];
multiply_matrix_vector(fG, x, fVariableCount, fVariableCount, gxd);
add_vectors(gxd, d, fVariableCount);
double p[fVariableCount];
if (!_SolveSubProblem(gxd, am, p))
return false;
if (is_zero(p, fVariableCount)) {
// compute Lagrange multipliers lambda_i
// if lambda_i >= 0 for all i \in W^k \union inequality constraints,
// then we're done.
// Otherwise remove the constraint with the smallest lambda_i
// from the active set.
// The derivation of the Lagrangian yields:
// \sum_{i \in W^k}(lambda_ia_i) = Gx_k + d
// Which is an system we can solve:
// A^Tlambda = Gx_k + d
// A^T is over-determined, hence we need to reduce the number of
// rows before we can solve it.
bool independentColumns[an];
double** aa = fTemp1;
transpose_matrix(fActiveMatrix, aa, am, an);
const int aam = remove_linearly_dependent_rows(aa, fTemp2,
independentColumns, an, am);
const int aan = am;
if (aam != aan) {
// This should not happen, since A has full row rank.
TRACE_ERROR("Solve(): Transposed A has less linear independent "
"rows than it has columns!\n");
return false;
}
// also reduce the number of rows on the right hand side
double lambda[aam];
int index = 0;
for (int i = 0; i < an; i++) {
if (independentColumns[i])
lambda[index++] = gxd[i];
}
bool success = solve(aa, aam, lambda);
if (!success) {
// Impossible, since we've removed all linearly dependent rows.
TRACE_ERROR("Solve(): Failed to compute lambda!\n");
return false;
}
// find min lambda_i (only, if it's < 0, though)
double minLambda = 0;
int minIndex = -1;
index = 0;
for (int i = 0; i < activeCount; i++) {
if (independentRows[i]) {
Constraint* constraint
= (Constraint*)activeConstraints.ItemAt(i);
if (constraint->Op() != LinearProgramming::kEQ) {
if (lambda[index] < minLambda) {
minLambda = lambda[index];
minIndex = i;
}
}
index++;
}
}
// if the min lambda is >= 0, we're done
if (minIndex < 0 || fuzzy_equals(minLambda, 0)) {
_SetResult(x, values);
return true;
}
// remove i from the active set
activeConstraints.RemoveItemAt(minIndex);
} else {
// compute alpha_k
double alpha = 1;
int barrier = -1;
// if alpha_k < 1, add a barrier constraint to W^k
for (int32 i = 0; i < constraintCount; i++) {
Constraint* constraint = (Constraint*)fConstraints.ItemAt(i);
if (activeConstraints.HasItem(constraint))
continue;
double divider = _ActualValue(constraint, p);
if (divider > 0 || fuzzy_equals(divider, 0))
continue;
// (b_i - a_i^Tx_k) / a_i^Tp_k
double alphaI = _RightSide(constraint)
- _ActualValue(constraint, x);
alphaI /= divider;
if (alphaI < alpha) {
alpha = alphaI;
barrier = i;
}
}
TRACE("alpha: %f, barrier: %d\n", alpha, barrier);
if (alpha < 1)
activeConstraints.AddItem(fConstraints.ItemAt(barrier));
// x += p * alpha;
add_vectors_scaled(x, p, alpha, fVariableCount);
}
}
}
bool
LayoutOptimizer::_SolveSubProblem(const double* d, int am, double* p)
{
// We have to solve the QP subproblem:
// min_p 1/2p^TGp + d^Tp
// s.t. a_i^Tp = 0
// with a_i \in activeConstraints
//
// We use the null space method, i.e. we find matrices Y and Z, such that
// AZ = 0 and [Y Z] is regular. Then with
// p = Yp_Y + Zp_z
// we get
// p_Y = 0
// and
// (Z^TGZ)p_Z = -(Z^TYp_Y + Z^Tg) = -Z^Td
// which is a linear equation system, which we can solve.
const int an = fVariableCount;
// we get Y and Z by QR decomposition of A^T
double tempD[am];
double** const Q = fQ;
transpose_matrix(fActiveMatrix, fTemp1, am, an);
bool success = qr_decomposition(fTemp1, an, am, tempD, Q);
if (!success) {
TRACE_ERROR("Solve(): QR decomposition failed!\n");
return false;
}
// Z is the (1, m + 1) minor of Q
const int zm = an;
const int zn = an - am;
double* Z[zm];
for (int i = 0; i < zm; i++)
Z[i] = Q[i] + am;
// solve (Z^TGZ)p_Z = -Z^Td
// Z^T
transpose_matrix(Z, fZtrans, zm, zn);
// rhs: -Z^T * d;
double pz[zm];
multiply_matrix_vector(fZtrans, d, zn, zm, pz);
negate_vector(pz, zn);
// fTemp2 = Ztrans * G * Z
//multiply_optimization_matrix_matrix(Z, an, zn, fTemp1);
multiply_matrices(fG, Z, fTemp1, zm, fVariableCount, zn);
multiply_matrices(fZtrans, fTemp1, fTemp2, zn, zm, zn);
success = solve(fTemp2, zn, pz);
if (!success) {
TRACE_ERROR("Solve(): Failed to solve() system for p_Z\n");
return false;
}
// p = Z * pz;
multiply_matrix_vector(Z, pz, zm, zn, p);
return true;
}
// _SetResult
void
LayoutOptimizer::_SetResult(const double* x, double* values)
{
for (int i = 1; i < fVariableCount; i++)
values[i] = x[i];
}
+85
View File
@@ -0,0 +1,85 @@
/*
* Copyright 2007, Ingo Weinhold <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef LAYOUT_OPTIMIZER_H
#define LAYOUT_OPTIMIZER_H
#include <List.h>
#include <math.h>
#include "LinearSpec.h"
static const double kEqualsEpsilon = 0.000001;
namespace BPrivate {
namespace Layout {
double** allocate_matrix(int m, int n);
void free_matrix(double** matrix);
void copy_matrix(const double* const* A, double** B, int m, int n);
void zero_matrix(double** A, int m, int n);
int compute_dependencies(double** a, int m, int n, bool* independent);
int remove_linearly_dependent_rows(double** A, double** temp,
bool* independentRows, int m, int n);
bool solve(double** a, int n, double* b);
class LayoutOptimizer {
public:
LayoutOptimizer(const ConstraintList& list,
int32 variableCount);
~LayoutOptimizer();
bool SetConstraints(const ConstraintList& list,
int32 variableCount);
status_t InitCheck() const;
bool Solve(double* initialSolution);
private:
double _ActualValue(Constraint* constraint,
double* values) const;
double _RightSide(Constraint* constraint);
void _MakeEmpty();
void _Init(int32 variableCount, int32 nConstraints);
bool _Solve(double* values);
bool _SolveSubProblem(const double* d, int am,
double* p);
void _SetResult(const double* x, double* values);
int32 fVariableCount;
ConstraintList fConstraints;
double** fTemp1;
double** fTemp2;
double** fZtrans;
double** fQ;
double** fActiveMatrix;
double** fActiveMatrixTemp;
double** fSoftConstraints;
double** fG;
double* fDesired;
};
} // namespace Layout
} // namespace BPrivate
using BPrivate::Layout::LayoutOptimizer;
inline bool
fuzzy_equals(double a, double b)
{
return fabs(a - b) < kEqualsEpsilon;
}
#endif // LAYOUT_OPTIMIZER_H
+76 -237
View File
@@ -12,6 +12,28 @@
#include <stdio.h> #include <stdio.h>
#include "LPSolveInterface.h" #include "LPSolveInterface.h"
#include "ActiveSetSolver.h"
using namespace LinearProgramming;
#define DEBUG_LINEAR_SPECIFICATIONS
#ifdef DEBUG_LINEAR_SPECIFICATIONS
#include <stdio.h>
#define TRACE(x...) printf(x)
#else
#define TRACE(x...) /* nothing */
#endif
SolverInterface::SolverInterface(LinearSpec* linSpec)
:
fLinearSpec(linSpec)
{
}
/** /**
@@ -20,13 +42,11 @@
*/ */
LinearSpec::LinearSpec() LinearSpec::LinearSpec()
: :
fOptimization(kMinimize),
fObjFunction(new(std::nothrow) SummandList()),
fResult(kError), fResult(kError),
fObjectiveValue(NAN), fSolvingTime(0)
fSolvingTime(NAN)
{ {
fSolver = new LPSolveInterface; //fSolver = new LPSolveInterface(this);
fSolver = new ActiveSetSolver(this);
} }
@@ -39,12 +59,9 @@ LinearSpec::~LinearSpec()
{ {
for (int32 i = 0; i < fConstraints.CountItems(); i++) for (int32 i = 0; i < fConstraints.CountItems(); i++)
delete (Constraint*)fConstraints.ItemAt(i); delete (Constraint*)fConstraints.ItemAt(i);
for (int32 i = 0; i < fObjFunction->CountItems(); i++)
delete (Summand*)fObjFunction->ItemAt(i);
while (fVariables.CountItems() > 0) while (fVariables.CountItems() > 0)
RemoveVariable(fVariables.ItemAt(0)); RemoveVariable(fVariables.ItemAt(0));
delete fObjFunction;
delete fSolver; delete fSolver;
} }
@@ -77,7 +94,7 @@ LinearSpec::AddVariable(Variable* variable)
if (!fVariables.AddItem(variable)) if (!fVariables.AddItem(variable))
return false; return false;
if (!fSolver->AddVariable()) { if (!fSolver->VariableAdded(variable)) {
fVariables.RemoveItem(variable); fVariables.RemoveItem(variable);
return false; return false;
} }
@@ -95,13 +112,10 @@ LinearSpec::AddVariable(Variable* variable)
bool bool
LinearSpec::RemoveVariable(Variable* variable, bool deleteVariable) LinearSpec::RemoveVariable(Variable* variable, bool deleteVariable)
{ {
int32 index = IndexOf(variable); // must be called first otherwise the index is invalid
if (index < 0) if (!fSolver->VariableRemoved(variable))
return false; return false;
fVariables.RemoveItem(variable);
if (!fSolver->RemoveVariable(index))
return false;
fVariables.RemoveItemAt(index - 1);
variable->fIsValid = false; variable->fIsValid = false;
// invalidate all constraints that use this variable // invalidate all constraints that use this variable
@@ -135,20 +149,14 @@ LinearSpec::RemoveVariable(Variable* variable, bool deleteVariable)
int32 int32
LinearSpec::IndexOf(const Variable* variable) const LinearSpec::IndexOf(const Variable* variable) const
{ {
int32 i = fVariables.IndexOf(variable); return fVariables.IndexOf(variable);
if (i == -1) {
printf("Variable 0x%p not part of fLS->Variables().\n", variable);
return -1;
}
return i + 1;
} }
bool bool
LinearSpec::UpdateRange(Variable* variable) LinearSpec::UpdateRange(Variable* variable)
{ {
if (!fSolver->SetVariableRange(IndexOf(variable), variable->Min(), if (!fSolver->VariableRangeChanged(variable))
variable->Max()))
return false; return false;
return true; return true;
} }
@@ -157,49 +165,13 @@ LinearSpec::UpdateRange(Variable* variable)
bool bool
LinearSpec::AddConstraint(Constraint* constraint) LinearSpec::AddConstraint(Constraint* constraint)
{ {
SummandList* summands = constraint->LeftSide(); if (!fConstraints.AddItem(constraint))
OperatorType op = constraint->Op(); return false;
double rightSide = constraint->RightSide();
double penaltyNeg = constraint->PenaltyNeg();
double penaltyPos = constraint->PenaltyPos();
double coeffs[summands->CountItems() + 2]; if (!fSolver->ConstraintAdded(constraint)) {
int varIndexes[summands->CountItems() + 2]; fConstraints.RemoveItem(constraint);
int32 nCoefficient = 0;
for (; nCoefficient < summands->CountItems(); nCoefficient++) {
Summand* s = summands->ItemAt(nCoefficient);
coeffs[nCoefficient] = s->Coeff();
varIndexes[nCoefficient] = s->Var()->Index();
}
if (penaltyNeg != INFINITY && penaltyNeg != 0. && op != LE) {
constraint->fDNegObjSummand
= new(std::nothrow) Summand(constraint->PenaltyNeg(),
AddVariable());
fObjFunction->AddItem(constraint->fDNegObjSummand);
varIndexes[nCoefficient] = constraint->fDNegObjSummand->Var()->Index();
coeffs[nCoefficient] = 1.0;
nCoefficient++;
}
if (penaltyPos != INFINITY && penaltyPos != 0. && op != GE) {
constraint->fDPosObjSummand
= new(std::nothrow) Summand(constraint->PenaltyPos(),
AddVariable());
fObjFunction->AddItem(constraint->fDPosObjSummand);
varIndexes[nCoefficient] = constraint->fDPosObjSummand->Var()->Index();
coeffs[nCoefficient] = -1.0;
nCoefficient++;
}
if (!fSolver->AddConstraint(nCoefficient, &coeffs[0], &varIndexes[0], op,
rightSide)) {
return false; return false;
} }
UpdateObjectiveFunction();
fConstraints.AddItem(constraint);
return true; return true;
} }
@@ -207,20 +179,7 @@ LinearSpec::AddConstraint(Constraint* constraint)
bool bool
LinearSpec::RemoveConstraint(Constraint* constraint, bool deleteConstraint) LinearSpec::RemoveConstraint(Constraint* constraint, bool deleteConstraint)
{ {
if (constraint->fDNegObjSummand) { fSolver->ConstraintRemoved(constraint);
fObjFunction->RemoveItem(constraint->fDNegObjSummand);
delete constraint->fDNegObjSummand->Var();
delete constraint->fDNegObjSummand;
constraint->fDNegObjSummand = NULL;
}
if (constraint->fDPosObjSummand) {
fObjFunction->RemoveItem(constraint->fDPosObjSummand);
delete constraint->fDPosObjSummand->Var();
delete constraint->fDPosObjSummand;
constraint->fDPosObjSummand = NULL;
}
fSolver->RemoveConstraint(constraint->Index());
fConstraints.RemoveItem(constraint); fConstraints.RemoveItem(constraint);
constraint->fIsValid = false; constraint->fIsValid = false;
@@ -233,38 +192,8 @@ LinearSpec::RemoveConstraint(Constraint* constraint, bool deleteConstraint)
bool bool
LinearSpec::UpdateLeftSide(Constraint* constraint) LinearSpec::UpdateLeftSide(Constraint* constraint)
{ {
if (!constraint->IsValid()) if (!fSolver->LeftSideChanged(constraint))
return false; return false;
SummandList* leftSide = constraint->LeftSide();
OperatorType op = constraint->Op();
double coeffs[leftSide->CountItems() + 2];
int varIndexes[leftSide->CountItems() + 2];
int32 i;
for (i = 0; i < leftSide->CountItems(); i++) {
Summand* s = leftSide->ItemAt(i);
coeffs[i] = s->Coeff();
varIndexes[i] = s->Var()->Index();
}
if (constraint->fDNegObjSummand != NULL && op != OperatorType(LE)) {
varIndexes[i] = constraint->fDNegObjSummand->Var()->Index();
coeffs[i] = 1.0;
i++;
}
if (constraint->fDPosObjSummand != NULL && op != OperatorType(GE)) {
varIndexes[i] = constraint->fDPosObjSummand->Var()->Index();
coeffs[i] = -1.0;
i++;
}
if (!fSolver->SetLeftSide(constraint->Index(), i, &coeffs[0],
&varIndexes[0]))
return false;
UpdateObjectiveFunction();
return true; return true;
} }
@@ -272,7 +201,7 @@ LinearSpec::UpdateLeftSide(Constraint* constraint)
bool bool
LinearSpec::UpdateRightSide(Constraint* constraint) LinearSpec::UpdateRightSide(Constraint* constraint)
{ {
if (!fSolver->SetRightSide(constraint->Index(), constraint->RightSide())) if (!fSolver->RightSideChanged(constraint))
return false; return false;
return true; return true;
} }
@@ -281,7 +210,7 @@ LinearSpec::UpdateRightSide(Constraint* constraint)
bool bool
LinearSpec::UpdateOperator(Constraint* constraint) LinearSpec::UpdateOperator(Constraint* constraint)
{ {
if (!fSolver->SetOperator(constraint->Index(), constraint->Op())) if (!fSolver->OperatorChanged(constraint))
return false; return false;
return true; return true;
} }
@@ -300,7 +229,7 @@ Constraint*
LinearSpec::AddConstraint(SummandList* summands, OperatorType op, LinearSpec::AddConstraint(SummandList* summands, OperatorType op,
double rightSide) double rightSide)
{ {
return AddConstraint(summands, op, rightSide, INFINITY, INFINITY); return AddConstraint(summands, op, rightSide, -1, -1);
} }
@@ -317,7 +246,7 @@ Constraint*
LinearSpec::AddConstraint(double coeff1, Variable* var1, LinearSpec::AddConstraint(double coeff1, Variable* var1,
OperatorType op, double rightSide) OperatorType op, double rightSide)
{ {
return AddConstraint(coeff1, var1, op, rightSide, INFINITY, INFINITY); return AddConstraint(coeff1, var1, op, rightSide, -1, -1);
} }
@@ -336,8 +265,8 @@ Constraint*
LinearSpec::AddConstraint(double coeff1, Variable* var1, LinearSpec::AddConstraint(double coeff1, Variable* var1,
double coeff2, Variable* var2, OperatorType op, double rightSide) double coeff2, Variable* var2, OperatorType op, double rightSide)
{ {
return AddConstraint(coeff1, var1, coeff2, var2, op, rightSide, INFINITY, return AddConstraint(coeff1, var1, coeff2, var2, op, rightSide, -1,
INFINITY); -1);
} }
@@ -360,7 +289,7 @@ LinearSpec::AddConstraint(double coeff1, Variable* var1,
OperatorType op, double rightSide) OperatorType op, double rightSide)
{ {
return AddConstraint(coeff1, var1, coeff2, var2, coeff3, var3, op, return AddConstraint(coeff1, var1, coeff2, var2, coeff3, var3, op,
rightSide, INFINITY, INFINITY); rightSide, -1, -1);
} }
@@ -385,7 +314,7 @@ LinearSpec::AddConstraint(double coeff1, Variable* var1,
double coeff4, Variable* var4, OperatorType op, double rightSide) double coeff4, Variable* var4, OperatorType op, double rightSide)
{ {
return AddConstraint(coeff1, var1, coeff2, var2, coeff3, var3, coeff4, var4, return AddConstraint(coeff1, var1, coeff2, var2, coeff3, var3, coeff4, var4,
op, rightSide, INFINITY, INFINITY); op, rightSide, -1, -1);
} }
@@ -526,79 +455,17 @@ LinearSpec::AddConstraint(double coeff1, Variable* var1,
} }
/** BSize
* Adds a new penalty function to the specification. LinearSpec::MinSize(Variable* width, Variable* height)
*
* @param var the penalty function's variable
* @param xs the penalty function's sampling points
* @param gs the penalty function's gradients
* @return the new penalty function
*/
PenaltyFunction*
LinearSpec::AddPenaltyFunction(Variable* var, BList* xs, BList* gs)
{ {
return new(std::nothrow) PenaltyFunction(this, var, xs, gs); return fSolver->MinSize(width, height);
} }
/** BSize
* Gets the objective function. LinearSpec::MaxSize(Variable* width, Variable* height)
*
* @return SummandList containing the objective function's summands
*/
SummandList*
LinearSpec::ObjectiveFunction()
{ {
return fObjFunction; return fSolver->MaxSize(width, height);
}
SummandList*
LinearSpec::SwapObjectiveFunction(SummandList* objFunction)
{
SummandList* list = fObjFunction;
fObjFunction = objFunction;
UpdateObjectiveFunction();
return list;
}
/**
* Sets a new objective function.
*
* @param summands SummandList containing the objective function's summands
*/
void
LinearSpec::SetObjectiveFunction(SummandList* objFunction)
{
for (int32 i = 0; i < fObjFunction->CountItems(); i++)
delete (Summand*)fObjFunction->ItemAt(i);
delete fObjFunction;
fObjFunction = objFunction;
UpdateObjectiveFunction();
}
/**
* Updates the internal representation of the objective function.
* Must be called whenever the summands of the objective function are changed.
*/
void
LinearSpec::UpdateObjectiveFunction()
{
int32 size = fObjFunction->CountItems();
double coeffs[size];
int varIndexes[size];
Summand* current;
for (int32 i = 0; i < size; i++) {
current = (Summand*)fObjFunction->ItemAt(i);
coeffs[i] = current->Coeff();
varIndexes[i] = current->Var()->Index();
}
if (!fSolver->SetObjectiveFunction(size, &coeffs[0], &varIndexes[0]))
printf("Error in set_obj_fnex.\n");
} }
@@ -638,23 +505,27 @@ LinearSpec::_AddConstraint(SummandList* leftSide, OperatorType op,
} }
/** #ifdef DEBUG_LINEAR_SPECIFICATIONS
* Tries to solve the linear programming problem. static bigtime_t sAverageSolvingTime = 0;
* If a cached simplified version of the problem exists, it is used instead. static int32 sSolvedCount = 0;
* #endif
* @return the result of the solving attempt
*/
ResultType ResultType
LinearSpec::Solve() LinearSpec::Solve()
{ {
bigtime_t start, end; bigtime_t startTime = system_time();
start = system_time();
fResult = fSolver->Solve(fVariables); fResult = fSolver->Solve();
fObjectiveValue = fSolver->GetObjectiveValue();
end = system_time(); fSolvingTime = system_time() - startTime;
fSolvingTime = (end - start) / 1000.0;
#ifdef DEBUG_LINEAR_SPECIFICATIONS
sAverageSolvingTime += fSolvingTime;
sSolvedCount++;
TRACE("Solving time %i average %i [micro s]\n", (int)fSolvingTime,
int(sAverageSolvingTime / sSolvedCount));
#endif
return fResult; return fResult;
} }
@@ -673,33 +544,6 @@ LinearSpec::Save(const char* fileName)
} }
/**
* Gets the current optimization.
* The default is minimization.
*
* @return the current optimization
*/
OptimizationType
LinearSpec::Optimization() const
{
return fOptimization;
}
/**
* Sets whether the solver should minimize or maximize the objective function.
* The default is minimization.
*
* @param optimization the optimization type
*/
void
LinearSpec::SetOptimization(OptimizationType value)
{
fOptimization = value;
fSolver->SetOptimization(value);
}
/** /**
* Gets the constraints. * Gets the constraints.
* *
@@ -712,6 +556,13 @@ LinearSpec::Constraints() const
} }
const VariableList&
LinearSpec::Variables() const
{
return fVariables;
}
/** /**
* Gets the result type. * Gets the result type.
* *
@@ -724,24 +575,12 @@ LinearSpec::Result() const
} }
/**
* Gets the objective value.
*
* @return the objective value
*/
double
LinearSpec::ObjectiveValue() const
{
return fObjectiveValue;
}
/** /**
* Gets the solving time. * Gets the solving time.
* *
* @return the solving time * @return the solving time
*/ */
double bigtime_t
LinearSpec::SolvingTime() const LinearSpec::SolvingTime() const
{ {
return fSolvingTime; return fSolvingTime;
@@ -789,6 +628,6 @@ LinearSpec::GetString(BString& string) const
string << "kNumFailure"; string << "kNumFailure";
else else
string << fResult; string << fResult;
string << " SolvingTime=" << (float)fSolvingTime << "ms"; string << " SolvingTime=" << fSolvingTime << "micro s";
} }
-70
View File
@@ -1,70 +0,0 @@
/*
* Copyright 2007-2008, Christof Lutteroth, [email protected]
* Copyright 2007-2008, James Kim, [email protected]
* Distributed under the terms of the MIT License.
*/
#include "PenaltyFunction.h"
#include <stdio.h>
#include "Constraint.h"
#include "LinearSpec.h"
#include "Summand.h"
#include "Variable.h"
/**
* Constructor.
*/
PenaltyFunction::PenaltyFunction(LinearSpec* ls, Variable* var, BList* xs, BList* gs)
{
int32 sizeXs = xs->CountItems();
int32 sizeGs = gs->CountItems();
if (var->LS() != ls)
printf("The variable must belong to the same linear specification as the penalty function.");
if (sizeXs + 1 != sizeGs)
printf("The number of sampling points must be exactly one less than the number of gradients.");
for (int32 i = 1; i < sizeGs; i++) {
if (*(double*)(gs->ItemAt(i - 1)) > *(double*)(gs->ItemAt(i)))
printf("Penalty function must be concave.");
}
fVar = var;
fXs = xs;
fGs = gs;
fConstraints = new BList(sizeGs + 1);
fObjFunctionSummands = new BList(sizeGs);
fConstraints->AddItem(ls->AddConstraint(1.0, var, kEQ,
*(double*)(xs->ItemAt(0)), -*(double*)(gs->ItemAt(0)),
*(double*)(gs->ItemAt(1))));
for (int32 i = 1; i < sizeGs; i++) {
Variable* dPos = ls->AddVariable();
fConstraints->AddItem(ls->AddConstraint(1.0, var, -1.0, dPos, kLE,
*(double*)(xs->ItemAt(i))));
Summand* objSummand = new Summand(*(double*)(gs->ItemAt(i + 1)) - *(double*)(gs->ItemAt(i)), dPos);
ls->ObjectiveFunction()->AddItem(objSummand);
fObjFunctionSummands->AddItem(objSummand);
}
ls->UpdateObjectiveFunction();
}
/**
* Destructor.
* Removes all constraints and summands from the penalty function.
*/
PenaltyFunction::~PenaltyFunction()
{
for (int32 i = 0; i < fConstraints->CountItems(); i++)
delete (Constraint*)fConstraints->ItemAt(i);
for (int32 i = 0; i < fObjFunctionSummands->CountItems(); i++)
delete (Summand*)fObjFunctionSummands->ItemAt(i);
}
+7
View File
@@ -57,6 +57,13 @@ Summand::SetVar(Variable* var)
} }
int32
Summand::VariableIndex()
{
return fVar->Index();
}
/** /**
* Destructor. * Destructor.
*/ */