Implemented a ComplexLayouter class which is going to replace the Layouter

implementation which used the qoca constraint solver. It does the min/max
computation itself -- thanks to Peter Moulder for hinting that we're actually
dealing with separation constraints and proposing an algorithm. The actual
layout is done with the help of an active set method based optimizer.
The test results look very good so far. The code needs some cleanup (debug
output, math comments, special handling for some cases) and is therefore not
yet enabled by default.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22288 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold 2007-09-24 00:03:57 +00:00
parent a0710babd7
commit 9c7408528e
4 changed files with 1974 additions and 0 deletions

View File

@ -0,0 +1,846 @@
/*
* Copyright 2007, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "ComplexLayouter.h"
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <new>
#include <OS.h>
#include <Size.h>
#include <AutoDeleter.h>
#include "LayoutOptimizer.h"
#include "SimpleLayouter.h"
using std::nothrow;
// MyLayoutInfo
class ComplexLayouter::MyLayoutInfo : public LayoutInfo {
public:
MyLayoutInfo(int32 elementCount, int32 spacing)
: fCount(elementCount),
fSpacing(spacing)
{
// We also store the location of the virtual elementCountth element.
// Thus fLocation[i + 1] - fLocation[i] is the size of the ith element
// (not considering spacing).
fLocations = new(nothrow) int32[elementCount + 1];
}
~MyLayoutInfo()
{
delete[] fLocations;
}
virtual float ElementLocation(int32 element)
{
if (element < 0 || element >= fCount)
return 0;
return fLocations[element];
}
virtual float ElementSize(int32 element)
{
if (element < 0 || element >= fCount)
return -1;
return fLocations[element + 1] - fLocations[element] - 1
- fSpacing;
}
virtual float ElementRangeSize(int32 position, int32 length)
{
if (position < 0 || length < 0 || position + length > fCount)
return -1;
return fLocations[position + length] - fLocations[position] - 1
- fSpacing;
}
void Dump()
{
printf("ComplexLayouter::MyLayoutInfo(): %ld elements:\n", fCount);
for (int32 i = 0; i < fCount + 1; i++)
printf(" %2ld: location: %4ld\n", i, fLocations[i]);
}
public:
int32 fCount;
int32 fSpacing;
int32* fLocations;
};
// Constraint
struct ComplexLayouter::Constraint {
Constraint(int32 start, int32 end, int32 min, int32 max)
: start(start),
end(end),
min(min),
max(max),
next(NULL)
{
if (min > max)
max = min;
effectiveMax = max;
}
void Restrict(int32 newMin, int32 newMax)
{
if (newMin > min)
min = newMin;
if (newMax < max)
max = newMax;
if (min > max)
max = min;
effectiveMax = max;
}
int32 start;
int32 end;
int32 min;
int32 max;
int32 effectiveMax;
Constraint* next;
};
// SumItem
struct ComplexLayouter::SumItem {
int32 min;
int32 max;
bool minDirty;
bool maxDirty;
};
// SumItemBackup
struct ComplexLayouter::SumItemBackup {
int32 min;
int32 max;
};
// #pragma mark - ComplexLayouter
// constructor
ComplexLayouter::ComplexLayouter(int32 elementCount, int32 spacing)
: fElementCount(elementCount),
fSpacing(spacing),
fConstraints(new(nothrow) Constraint*[elementCount]),
fWeights(new(nothrow) float[elementCount]),
fSums(new(nothrow) SumItem[elementCount + 1]),
fSumBackups(new(nothrow) SumItemBackup[elementCount + 1]),
fOptimizer(new(nothrow) LayoutOptimizer(elementCount)),
fLayoutValid(false)
{
// TODO: Check initialization!
if (fConstraints)
memset(fConstraints, 0, sizeof(Constraint*) * fElementCount);
if (fWeights) {
for (int32 i = 0; i < fElementCount; i++)
fWeights[i] = 1.0f;
}
}
// destructor
ComplexLayouter::~ComplexLayouter()
{
for (int32 i = 0; i < fElementCount; i++) {
Constraint* constraint = fConstraints[i];
fConstraints[i] = NULL;
while (constraint != NULL) {
Constraint* next = constraint->next;
delete constraint;
constraint = next;
}
}
delete[] fConstraints;
delete[] fWeights;
delete[] fSums;
delete[] fSumBackups;
delete fOptimizer;
}
// InitCheck
status_t
ComplexLayouter::InitCheck() const
{
if (!fConstraints || !fWeights || !fSums || !fSumBackups || !fOptimizer)
return B_NO_MEMORY;
return fOptimizer->InitCheck();
}
// AddConstraints
void
ComplexLayouter::AddConstraints(int32 element, int32 length,
float _min, float _max, float _preferred)
{
if (element < 0 || length <= 0 || element + length > fElementCount)
return;
//printf("%p->ComplexLayouter::AddConstraints(%ld, %ld, %ld, %ld, %ld)\n",
//this, element, length, (int32)_min, (int32)_max, (int32)_preferred);
int32 spacing = fSpacing * (length - 1);
int32 min = (int32)_min + 1 - spacing;
int32 max = (int32)_max + 1 - spacing;
if (min < 0)
min = 0;
if (max > B_SIZE_UNLIMITED)
max = B_SIZE_UNLIMITED;
int32 end = element + length - 1;
Constraint** slot = fConstraints + end;
while (*slot != NULL && (*slot)->start > element)
slot = &(*slot)->next;
if (*slot != NULL && (*slot)->start == element) {
// previous constraint exists -- use stricter values
(*slot)->Restrict(min, max);
} else {
// no previous constraint -- create new one
Constraint* constraint = new(nothrow) Constraint(element, end, min,
max);
if (!constraint)
return;
constraint->next = *slot;
*slot = constraint;
}
}
// SetWeight
void
ComplexLayouter::SetWeight(int32 element, float weight)
{
if (element < 0 || element >= fElementCount)
return;
fWeights[element] = max_c(weight, 0);
}
// MinSize
float
ComplexLayouter::MinSize()
{
_ValidateLayout();
return fMin;
}
// MaxSize
float
ComplexLayouter::MaxSize()
{
_ValidateLayout();
return fMax;
}
// PreferredSize
float
ComplexLayouter::PreferredSize()
{
return fMin;
}
// CreateLayoutInfo
LayoutInfo*
ComplexLayouter::CreateLayoutInfo()
{
MyLayoutInfo* layoutInfo = new(nothrow) MyLayoutInfo(fElementCount,
fSpacing);
if (layoutInfo && !layoutInfo->fLocations) {
delete layoutInfo;
return NULL;
}
return layoutInfo;
}
// Layout
void
ComplexLayouter::Layout(LayoutInfo* _layoutInfo, float _size)
{
//printf("%p->ComplexLayouter::Layout(%ld)\n", this, (int32)_size);
if (fElementCount == 0)
return;
_ValidateLayout();
MyLayoutInfo* layoutInfo = (MyLayoutInfo*)_layoutInfo;
int32 min = fSums[fElementCount].min;
int32 max = fSums[fElementCount].max;
int32 size = (int32)_size + 1 - (fElementCount - 1) * fSpacing;
if (size < min)
size = min;
if (size > max)
size = max;
SumItem sums[fElementCount + 1];
memcpy(sums, fSums, (fElementCount + 1) * sizeof(SumItem));
sums[fElementCount].min = size;
sums[fElementCount].max = size;
sums[fElementCount].minDirty = (size != min);
sums[fElementCount].maxDirty = (size != max);
// propagate the size
_PropagateChangesBack(sums, fElementCount - 1, NULL);
_PropagateChanges(sums, fElementCount - 1, NULL);
printf("Layout(%ld)\n", size);
for (int32 i = 0; i < fElementCount; i++) {
SumItem& sum = sums[i + 1];
printf("[%ld] minc = %4ld, maxc = %4ld\n", i + 1, sum.min, sum.max);
}
// TODO: Test whether the desired solution already satisfies all constraints.
// If so, we can skip the constraint solving part.
// TODO: We should probably already setup the constraints in _ValidateLayout().
// This way we might not be able to skip as many redundant constraints, but
// supposedly it doesn't matter all that much, since those constraints
// wouldn't make it into the active set anyway.
fOptimizer->RemoveAllConstraints();
// add constraints
for (int32 i = 0; i < fElementCount; i++) {
SumItem& sum = fSums[i + 1];
Constraint* constraint = fConstraints[i];
while (constraint != NULL) {
SumItem& base = fSums[constraint->start];
int32 sumMin = base.min + constraint->min;
int32 baseMax = sum.max - constraint->min;
bool minRedundant = (sumMin < sum.min && baseMax > base.max);
int32 sumMax = base.max + constraint->effectiveMax;
int32 baseMin = sum.min - constraint->effectiveMax;
bool maxRedundant = (sumMax > sum.max && baseMin < base.min);
if (!minRedundant || !maxRedundant) {
bool success = true;
if (constraint->min == constraint->effectiveMax) {
// min and max equal -- add an equality constraint
success = fOptimizer->AddConstraint(constraint->start - 1,
constraint->end, constraint->min, true);
} else {
// min and max not equal -- add them individually,
// unless redundant
if (!minRedundant) {
success |= fOptimizer->AddConstraint(
constraint->start - 1, constraint->end,
constraint->min, false);
}
if (!maxRedundant) {
success |= fOptimizer->AddConstraint(constraint->end,
constraint->start - 1,
-constraint->effectiveMax, false);
}
}
}
constraint = constraint->next;
}
}
// prepare a feasible solution (the minimum)
double values[fElementCount];
for (int32 i = 0; i < fElementCount; i++)
values[i] = sums[i + 1].min - sums[i].min;
// prepare the desired solution
int32 sizes[fElementCount];
SimpleLayouter::DistributeSize(size, fWeights, sizes, fElementCount);
double realSizes[fElementCount];
for (int32 i = 0; i < fElementCount; i++)
realSizes[i] = sizes[i];
printf("feasible solution vs. desired solution:\n");
for (int32 i = 0; i < fElementCount; i++)
printf("%8.4f %8.4f\n", values[i], realSizes[i]);
// solve
bigtime_t time = system_time();
if (!fOptimizer->Solve(realSizes, size, values))
return;
time = system_time() - time;
// compute integer solution
// The basic strategy is to floor() the sums. This guarantees that the
// difference between two rounded sums remains in the range of floor()
// and ceil() of their real value difference. Since the constraints have
// integer values, the integer solution will satisfy all constraints the
// real solution satisfied.
printf("computed solution in %lld us:\n", time);
double realSum = 0;
int32 spacing = 0;
layoutInfo->fLocations[0] = 0;
for (int32 i = 0; i < fElementCount; i++) {
realSum += values[i];
double roundedRealSum = floor(realSum);
if (fuzzy_equals(realSum, roundedRealSum + 1))
realSum = roundedRealSum + 1;
layoutInfo->fLocations[i + 1] = (int32)roundedRealSum + spacing;
spacing += fSpacing;
printf("x[%ld] = %8.4f %4ld\n", i, values[i], layoutInfo->fLocations[i + 1] - layoutInfo->fLocations[i]);
}
}
// CloneLayouter
Layouter*
ComplexLayouter::CloneLayouter()
{
ComplexLayouter* layouter
= new(nothrow) ComplexLayouter(fElementCount, fSpacing);
ObjectDeleter<ComplexLayouter> layouterDeleter(layouter);
if (!layouter || layouter->InitCheck() != B_OK
|| !layouter->fOptimizer->AddConstraintsFrom(fOptimizer)) {
return NULL;
}
// clone the constraints
for (int32 i = 0; i < fElementCount; i++) {
Constraint* constraint = fConstraints[i];
Constraint** end = layouter->fConstraints + i;
while (constraint) {
*end = new(nothrow) Constraint(constraint->start, constraint->end,
constraint->min, constraint->max);
if (!*end)
return NULL;
end = &(*end)->next;
constraint = constraint->next;
}
}
// copy the other stuff
memcpy(layouter->fWeights, fWeights, fElementCount * sizeof(float));
memcpy(layouter->fSums, fSums, (fElementCount + 1) * sizeof(SumItem));
memcpy(layouter->fSumBackups, fSumBackups,
(fElementCount + 1) * sizeof(SumItemBackup));
layouter->fMin = fMin;
layouter->fMax = fMax;
layouter->fLayoutValid = fLayoutValid;
return layouterDeleter.Detach();
}
// _ValidateLayout
void
ComplexLayouter::_ValidateLayout()
{
if (fLayoutValid)
return;
fSums[0].min = 0;
fSums[0].max = 0;
for (int32 i = 0; i < fElementCount; i++) {
SumItem& sum = fSums[i + 1];
sum.min = 0;
sum.max = B_SIZE_UNLIMITED;
sum.minDirty = false;
sum.maxDirty = false;
}
// apply min constraints forward:
// minc[k] >= minc[i-1] + min[i,j]
for (int32 i = 0; i < fElementCount; i++) {
SumItem& sum = fSums[i + 1];
Constraint* constraint = fConstraints[i];
while (constraint != NULL) {
int32 minSum = fSums[constraint->start].min + constraint->min;
if (minSum > sum.min)
sum.min = minSum;
else {
printf("min constraint is redundant: x%ld + ... + x%ld >= %ld\n",
constraint->start, constraint->end, constraint->min);
}
constraint = constraint->next;
}
}
// apply min constraints backwards:
// maxc[i-1] <= maxc[k] - min[i,j]
for (int32 i = fElementCount - 1; i >= 0; i--) {
SumItem& sum = fSums[i + 1];
Constraint* constraint = fConstraints[i];
while (constraint != NULL) {
SumItem& base = fSums[constraint->start];
int32 baseMax = sum.max - constraint->min;
if (baseMax < base.max)
base.max = baseMax;
constraint = constraint->next;
}
}
// apply max constraints
for (int32 i = 0; i < fElementCount; i++) {
Constraint* constraint = fConstraints[i];
while (constraint != NULL) {
_ApplyMaxConstraint(constraint, i);
constraint = constraint->next;
}
//printf("fSums[%ld] = {%ld, %ld}\n", i + 1, sum.min, sum.max);
}
for (int32 i = 0; i < fElementCount; i++) {
SumItem& sum = fSums[i + 1];
printf("[%ld] minc = %4ld, maxc = %4ld\n", i + 1, sum.min, sum.max);
}
if (fElementCount == 0) {
fMin = -1;
fMax = B_SIZE_UNLIMITED;
} else {
int32 spacing = (fElementCount - 1) * fSpacing;
fMin = fSums[fElementCount].min + spacing - 1;
fMax = fSums[fElementCount].max + spacing - 1;
}
fLayoutValid = true;
}
/*
x[i] + ... + x[i+j] >= min[i,j]
x[i] + ... + x[i+j] <= max[i,j]
with
1 <= i <= n
0 <= j <= n - i
0 <= min[i,j] <= max[i,j]
Let
c[0] = 0
c[k] = x[1] + ... + x[k] for 1 <= k <= n
it follows
x[i] + ... + x[i+j] = c[i+j] - c[i-1]
and thus the constraints can be rewritten as
c[i+j] - c[i-1] >= min[i,j]
c[i+j] - c[i-1] <= max[i,j]
or
c[i+j] >= c[i-1] + min[i,j]
c[i+j] <= c[i-1] + max[i,j]
We're looking for minimal minc[] and maximal maxc[] such that
minc[i+j] >= minc[i-1] + min[i,j]
maxc[i+j] <= maxc[i-1] + max[i,j]
minc[i+j] <= minc[i-1] + max[i,j]
maxc[i+j] >= maxc[i-1] + min[i,j]
holds for all i and j. The latter two kinds of constraints have to be
enforced backwards:
minc[i-1] >= minc[i+j] - max[i,j]
maxc[i-1] <= maxc[i+j] - min[i,j]
-----------------
// (1) maxc[k] <= maxc[i-1] + max[i,j]
// (2) minc[i-1] >= minc[k] - max[i,j]
Modifying maxc[k] according to (1) potentially invalidates constraints of
these forms:
(i) maxc[i'-1] <= maxc[k] - min[i',j']
(ii) maxc[k+1+j'] <= maxc[k] + max[k+1,j']
After propagating (i) constraints backwards, all of them will be hold,
though more (ii) constraints might have been invalidated, though.
Propagating (ii) constraints forward afterwards, will make them all hold.
Since the min[i,j] and max[i,j] constraints are separation constraints and
the CSP not including the newly added constraint was conflict-free,
propagating the (i) and (ii) constraints won't change the maxc[i], i < k by
more than what maxc[k] changed. If afterwards the constraint (1) doesn't
hold, it apparently conflicts with the other constraints.
*/
// _ApplyMaxConstraint
void
ComplexLayouter::_ApplyMaxConstraint(Constraint* currentConstraint, int32 index)
{
SumItem& sum = fSums[index + 1];
SumItem& base = fSums[currentConstraint->start];
// We want to apply:
// c[i+j] <= c[i-1] + max[i,j]
//
// This has the following direct consequences (let k = i + j):
// (1) maxc[k] <= maxc[i-1] + max[i,j]
// (2) minc[i-1] >= minc[k] - max[i,j]
//
// If maxc[k] or minc[i-i] changed, those changes have to be propagated
// back.
// apply (1) maxc[k] <= maxc[i-1] + max[i,j]
int32 max = currentConstraint->effectiveMax;
int32 sumMax = base.max + max;
// enforce maxc[i+j] >= minc[i+j]
if (sumMax < sum.min) {
sumMax = sum.min;
max = sumMax - base.max;
}
// apply (2) minc[i-1] >= minc[k] - max[i,j]
// and check minc[i-1] <= maxc[i-1]
int32 baseMin = sum.min - max;
if (baseMin > base.max) {
baseMin = base.max;
max = sum.min - baseMin;
sumMax = base.max + max;
}
// apply changes
if (currentConstraint->effectiveMax != max) {
printf("relaxing conflicting max constraint (1): x%ld + ... + x%ld <= %ld -> %ld\n",
currentConstraint->start, currentConstraint->end,
currentConstraint->effectiveMax, max);
}
currentConstraint->effectiveMax = max;
if (baseMin <= base.min && sumMax >= sum.max)
{
printf("max constraint is redundant: x%ld + ... + x%ld <= %ld\n",
currentConstraint->start, currentConstraint->end, currentConstraint->effectiveMax);
return;
}
// backup old values, in case we detect a conflict later
_BackupValues(index);
int32 diff;
do {
// apply the changes
int32 changedIndex = currentConstraint->start;
if (baseMin > base.min) {
base.min = baseMin;
base.minDirty = true;
}
if (sumMax < sum.max) {
changedIndex = index;
sum.max = sumMax;
sum.maxDirty = true;
}
// propagate the changes
_PropagateChangesBack(fSums, changedIndex, currentConstraint);
_PropagateChanges(fSums, index, currentConstraint);
// check the new constraint again -- if it doesn't hold, it
// conflicts with the other constraints
diff = 0;
// check (1) maxc[k] <= maxc[i-1] + max[i,j]
max = currentConstraint->effectiveMax;
sumMax = base.max + max;
if (sumMax < sum.max)
diff = sum.max - sumMax;
// check (2) minc[i-1] >= minc[k] - max[i,j]
baseMin = sum.min - max;
if (baseMin > base.min)
diff = max_c(diff, baseMin - base.min);
// clear the dirty flags
for (int32 i = 0; i <= changedIndex; i++) {
SumItem& sum = fSums[i + 1];
sum.minDirty = false;
sum.maxDirty = false;
}
// if we've got a conflict, we relax the constraint and try again
if (diff > 0) {
max += diff;
printf("relaxing conflicting max constraint (2): x%ld + ... + x%ld <= %ld -> %ld\n",
currentConstraint->start, currentConstraint->end,
currentConstraint->effectiveMax, max);
currentConstraint->effectiveMax = max;
_RestoreValues(index);
sumMax = base.max + max;
baseMin = sum.min - max;
if (baseMin <= base.min && sumMax >= sum.max)
return;
}
} while (diff > 0);
}
// _PropagateChanges
/*! Propagate changes forward using min and max constraints. Max constraints
Beyond \a toIndex or at \a to toIndex after (and including)
\a lastMaxConstraint will be ignored. To have all constraints be
considered pass \c fElementCount and \c NULL.
*/
void
ComplexLayouter::_PropagateChanges(SumItem* sums, int32 toIndex,
Constraint* lastMaxConstraint)
{
for (int32 i = 0; i < fElementCount; i++) {
SumItem& sum = sums[i + 1];
bool ignoreMaxConstraints = (i > toIndex);
Constraint* constraint = fConstraints[i];
while (constraint != NULL) {
SumItem& base = sums[constraint->start];
if (constraint == lastMaxConstraint)
ignoreMaxConstraints = true;
// minc[k] >= minc[i-1] + min[i,j]
if (base.minDirty) {
int32 sumMin = base.min + constraint->min;
if (sumMin > sum.min) {
sum.min = sumMin;
sum.minDirty = true;
}
}
// maxc[k] <= maxc[i-1] + max[i,j]
if (base.maxDirty && !ignoreMaxConstraints) {
int32 sumMax = base.max + constraint->effectiveMax;
if (sumMax < sum.max) {
sum.max = sumMax;
sum.maxDirty = true;
}
}
constraint = constraint->next;
}
if (sum.minDirty || sum.maxDirty) {
if (sum.min > sum.max) {
// TODO: Can this actually happen?
printf("adjusted max in propagation phase: index: %ld: %ld -> %ld\n", i, sum.max, sum.min);
sum.max = sum.min;
sum.maxDirty = true;
}
}
}
}
// _PropagateChangesBack
void
ComplexLayouter::_PropagateChangesBack(SumItem* sums, int32 changedIndex,
Constraint* lastMaxConstraint)
{
for (int32 i = changedIndex; i >= 0; i--) {
SumItem& sum = sums[i + 1];
bool ignoreMaxConstraints = false;
Constraint* constraint = fConstraints[i];
while (constraint != NULL) {
SumItem& base = sums[constraint->start];
if (constraint == lastMaxConstraint)
ignoreMaxConstraints = true;
// minc[i-1] >= minc[k] - max[i,j]
if (sum.minDirty && !ignoreMaxConstraints) {
int32 baseMin = sum.min - constraint->effectiveMax;
if (baseMin > base.min) {
if (baseMin > base.max) {
printf("min above max in back propagation phase: index: (%ld -> %ld), "
"min: %ld, max: %ld\n", i, constraint->start, baseMin, base.max);
}
base.min = baseMin;
base.minDirty = true;
}
}
// maxc[i-1] <= maxc[k] - min[i,j]
if (sum.maxDirty) {
int32 baseMax = sum.max - constraint->min;
if (baseMax < base.max) {
if (baseMax < base.min) {
printf("max below min in back propagation phase: index: (%ld -> %ld), "
"max: %ld, min: %ld\n", i, constraint->start, baseMax, base.min);
}
base.max = baseMax;
base.maxDirty = true;
}
}
constraint = constraint->next;
}
}
}
// _BackupValues
void
ComplexLayouter::_BackupValues(int32 maxIndex)
{
for (int32 i = 0; i <= maxIndex; i++) {
SumItem& sum = fSums[i + 1];
fSumBackups[i + 1].min = sum.min;
fSumBackups[i + 1].max = sum.max;
}
}
// _RestoreValues
void
ComplexLayouter::_RestoreValues(int32 maxIndex)
{
for (int32 i = 0; i <= maxIndex; i++) {
SumItem& sum = fSums[i + 1];
sum.min = fSumBackups[i + 1].min;
sum.max = fSumBackups[i + 1].max;
}
}

View File

@ -0,0 +1,78 @@
/*
* Copyright 2007, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef COMPLEX_LAYOUTER_H
#define COMPLEX_LAYOUTER_H
#include <List.h>
#include "Layouter.h"
namespace BPrivate {
namespace Layout {
class LayoutOptimizer;
class ComplexLayouter : public Layouter {
public:
ComplexLayouter(int32 elementCount,
int32 spacing);
virtual ~ComplexLayouter();
virtual status_t InitCheck() const;
virtual void AddConstraints(int32 element, int32 length,
float min, float max, float preferred);
virtual void SetWeight(int32 element, float weight);
virtual float MinSize();
virtual float MaxSize();
virtual float PreferredSize();
virtual LayoutInfo* CreateLayoutInfo();
virtual void Layout(LayoutInfo* layoutInfo, float size);
virtual Layouter* CloneLayouter();
private:
class MyLayoutInfo;
struct Constraint;
struct SumItem;
struct SumItemBackup;
void _ValidateLayout();
void _ApplyMaxConstraint(
Constraint* currentConstraint, int32 index);
void _PropagateChanges(SumItem* sums, int32 toIndex,
Constraint* lastMaxConstraint);
void _PropagateChangesBack(SumItem* sums,
int32 changedIndex,
Constraint* lastMaxConstraint);
void _BackupValues(int32 maxIndex);
void _RestoreValues(int32 maxIndex);
private:
int32 fElementCount;
int32 fSpacing;
Constraint** fConstraints;
float* fWeights;
SumItem* fSums;
SumItemBackup* fSumBackups;
LayoutOptimizer* fOptimizer;
float fMin;
float fMax;
bool fLayoutValid;
};
} // namespace Layout
} // namespace BPrivate
using BPrivate::Layout::ComplexLayouter;
#endif // COMPLEX_LAYOUTER_H

View File

@ -0,0 +1,982 @@
/*
* Copyright 2007, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "LayoutOptimizer.h"
#include <stdio.h>
#include <string.h>
#include <new>
#include <AutoDeleter.h>
using std::nothrow;
// #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
static double**
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
static void
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
static inline void
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
static inline void
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
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)) {
printf("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
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
static int
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)) {
printf("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)
{
free_matrix(matrix);
}
};
typedef BPrivate::AutoDeleter<double*, MatrixDelete> MatrixDeleter;
// Constraint
struct LayoutOptimizer::Constraint {
Constraint(int32 left, int32 right, double value, bool equality,
int32 index)
: left(left),
right(right),
value(value),
index(index),
equality(equality)
{
}
double ActualValue(double* values) const
{
double result = 0;
if (right >= 0)
result = values[right];
if (left >= 0)
result -= values[left];
return result;
}
void Print() const
{
printf("c[%2ld] - c[%2ld] %2s %4d\n", right, left,
(equality ? "=" : ">="), (int)value);
}
int32 left;
int32 right;
double value;
int32 index;
bool equality;
};
// #pragma mark - LayoutOptimizer
// constructor
LayoutOptimizer::LayoutOptimizer(int32 variableCount)
: fVariableCount(variableCount),
fConstraints(),
fVariables(new (nothrow) double[variableCount])
{
fTemp1 = allocate_matrix(fVariableCount, fVariableCount);
fTemp2 = allocate_matrix(fVariableCount, fVariableCount);
fZtrans = allocate_matrix(fVariableCount, fVariableCount);
fQ = allocate_matrix(fVariableCount, fVariableCount);
}
// destructor
LayoutOptimizer::~LayoutOptimizer()
{
free_matrix(fTemp1);
free_matrix(fTemp2);
free_matrix(fZtrans);
free_matrix(fQ);
delete[] fVariables;
for (int32 i = 0;
Constraint* constraint = (Constraint*)fConstraints.ItemAt(i);
i++) {
delete constraint;
}
}
// InitCheck
status_t
LayoutOptimizer::InitCheck() const
{
if (!fVariables || !fTemp1 || !fTemp2 || !fZtrans || !fQ)
return B_NO_MEMORY;
return B_OK;
}
// Clone
LayoutOptimizer*
LayoutOptimizer::Clone() const
{
LayoutOptimizer* clone = new(nothrow) LayoutOptimizer(fVariableCount);
ObjectDeleter<LayoutOptimizer> cloneDeleter(clone);
if (!clone || clone->InitCheck() != B_OK
|| !clone->AddConstraintsFrom(this)) {
return NULL;
}
return cloneDeleter.Detach();
}
// AddConstraint
/*! Adds a constraint of the form
\sum_{i=left+1}^{right} x_i >=/= value, if left < right
-\sum_{i=right+1}^{left} x_i >=/= value, if left > right
If \a equality is \c true, the constraint is an equality constraint.
*/
bool
LayoutOptimizer::AddConstraint(int32 left, int32 right, double value,
bool equality)
{
Constraint* constraint = new(nothrow) Constraint(left, right, value,
equality, fConstraints.CountItems());
if (constraint == NULL)
return false;
if (!fConstraints.AddItem(constraint)) {
delete constraint;
return false;
}
return true;
}
// AddConstraintsFrom
bool
LayoutOptimizer::AddConstraintsFrom(const LayoutOptimizer* other)
{
if (!other || other->fVariableCount != fVariableCount)
return false;
int32 count = fConstraints.CountItems();
for (int32 i = 0; i < count; i++) {
Constraint* constraint = (Constraint*)other->fConstraints.ItemAt(i);
if (!AddConstraint(constraint->left, constraint->right,
constraint->value, constraint->equality)) {
return false;
}
}
return true;
}
// RemoveAllConstraints
void
LayoutOptimizer::RemoveAllConstraints()
{
int32 count = fConstraints.CountItems();
for (int32 i = 0; i < count; i++) {
Constraint* constraint = (Constraint*)fConstraints.ItemAt(i);
delete constraint;
}
fConstraints.MakeEmpty();
}
// 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(const double* desired, double size, double* values)
{
if (fVariables == NULL || desired == NULL|| values == NULL)
return false;
int32 constraintCount = fConstraints.CountItems() + 1;
// 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;
// add sum constraint
if (!AddConstraint(-1, fVariableCount - 1, size, true))
return false;
bool success = _Solve(desired, values);
// remove sum constraint
Constraint* constraint = (Constraint*)fConstraints.RemoveItem(
constraintCount - 1);
delete constraint;
return success;
}
// _Solve
bool
LayoutOptimizer::_Solve(const double* desired, double* values)
{
int32 constraintCount = fConstraints.CountItems();
//printf("constraints:\n");
//for (int32 i = 0; i < constraintCount; i++) {
// printf(" %-2ld: ", i);
// ((Constraint*)fConstraints.ItemAt(i))->Print();
//}
// our QP is suppose 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 G and d
//
// The optimal solution minimizes the square of the distance to the desired
// solution:
// \sum_i=1^n (x_i - desired_i)^2
// Since we consider the sums c_i = \sum_k=1^i x_k, no the x_i directly,
// we get
// \sum_{i=1}^n (c_i - c_{i-1} - desired_i)^2
// Expanding and ignoring the constant part, we get
// \sum_{i=1}^n(c_i^2 - 2c_{i-1}c_i + c_{i-1}^2
// + 2desired_i(c_{i-1} - c_i))
// This results in a G of 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
// and d:
// d_i = 2(desired_{i+1} - desired_i)
// where desired_{n+1} = 0
//
// Note, that it is 1/2x^TGx, which is why we would have to multiply the
// G entries with 2. Instead we divide d by 2 though, which results in the
// equivalent optimization problem.
//
double x[fVariableCount];
x[0] = values[0];
for (int i = 1; i < fVariableCount; i++)
x[i] = values[i] + x[i - 1];
double d[fVariableCount];
for (int i = 0; i < fVariableCount - 1; i++)
d[i] = desired[i + 1] - desired[i];
d[fVariableCount - 1] = -desired[fVariableCount - 1];
//printf("d:\n");
//Matrix(fVariableCount, 1, d).Print();
// init active set
BList activeConstraints(constraintCount);
for (int32 i = 0; i < constraintCount; i++) {
Constraint* constraint = (Constraint*)fConstraints.ItemAt(i);
double actualValue = constraint->ActualValue(x);
//printf("constraint %ld: actual: %f constraint: %f\n", i, actualValue, constraint->value);
if (fuzzy_equals(actualValue, constraint->value))
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.
//int iteration = 0;
while (true) {
//printf("\n[iteration %d]\n", iteration++);
//printf("x:\n");
//Matrix(fVariableCount, 1, x).Print();
//printf("active set:\n");
//for (int32 i = 0; i < activeConstraints.CountItems(); i++) {
// printf(" ");
// ((Constraint*)activeConstraints.ItemAt(i))->Print();
//}
// 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) {
printf("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 = (Constraint*)activeConstraints.ItemAt(i);
if (constraint->right >= 0)
fActiveMatrix[i][constraint->right] = 1;
if (constraint->left >= 0)
fActiveMatrix[i][constraint->left] = -1;
}
// 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_optimization_matrix_vector(x, fVariableCount, gxd);
add_vectors(gxd, d, fVariableCount);
double p[fVariableCount];
if (!_SolveSubProblem(gxd, am, p))
return false;
//printf("p:\n");
//Matrix(fVariableCount, 1, p).Print();
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.
printf("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.
printf("Solve(): Failed to compute lambda!\n");
return false;
}
//printf("lambda:\n");
//Matrix(aam, 1, lambda).Print();
// 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->equality) {
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);
//printf("all lambda_i >= 0\n");
return true;
}
// remove i from the active set
activeConstraints.RemoveItem(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 = constraint->ActualValue(p);
if (divider > 0 || fuzzy_equals(divider, 0))
continue;
// (b_i - a_i^Tx_k) / a_i^Tp_k
double alphaI = constraint->value
- constraint->ActualValue(x);
alphaI /= divider;
if (alphaI < alpha) {
alpha = alphaI;
barrier = i;
}
}
//printf("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);
}
}
/*
min_x 1/2x^TGx + x^Td
s.t. Ax = b
x^* = x + p
c = Ax - b
g = d + Gx
-Gp - A^T lambda^* = g
-Ap = c
p = Yp_Y + Zp_Z
Y und Z aus QR-Faktorisierung von A^T
(AY)p_Y = -c -> p_Y
Z^TGZp_Z = -(Z^TGYp_Y + Z^Tg) -> p_Z
-----------
min_x x^TGx + x^Td
s.t. a_i^Tx = b_i i \in E
a_i^Tx >= b_i i \in I
p_k berechnen durch Loesen von
min_p 1/2p^TGp + g_k^Tp
s.t. a_i^Tp = 0, i \in W^k
*/
}
bool
LayoutOptimizer::_SolveSubProblem(const double* d, int am, double* p)
{
// x = p
// d = g_k
// b = 0
//
// x^* = x + p
// c = Ax - b
// g = d + Gx
//
// with x = 0 we get
// c = -b = 0
// g = d = g_k
//
// p = Yp_Y + Zp_Z
//
// (AY)p_Y = -c = 0
// => p_Y = 0
//
// (Z^TGZ)p_Z = -(Z^TYp_Y + Z^Tg) = -Z^Tg_k
// -> we have to solve (Z^TGZ)p_Z = -Z^Tg_k
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) {
printf("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^Tg_k
// 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(fZtrans, fTemp1, fTemp2, zn, zm, zn);
success = solve(fTemp2, zn, pz);
if (!success) {
printf("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)
{
values[0] = x[0];
for (int i = 1; i < fVariableCount; i++)
values[i] = x[i] - x[i - 1];
}

View File

@ -0,0 +1,68 @@
/*
* Copyright 2007, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef LAYOUT_OPTIMIZER_H
#define LAYOUT_OPTIMIZER_H
#include <List.h>
static const double kEqualsEpsilon = 0.000001;
namespace BPrivate {
namespace Layout {
class LayoutOptimizer {
public:
LayoutOptimizer(int32 variableCount);
~LayoutOptimizer();
status_t InitCheck() const;
LayoutOptimizer* Clone() const;
bool AddConstraint(int32 left, int32 right,
double value, bool equality);
bool AddConstraintsFrom(
const LayoutOptimizer* solver);
void RemoveAllConstraints();
bool Solve(const double* desired, double size,
double* values);
private:
bool _Solve(const double* desired, double* values);
bool _SolveSubProblem(const double* d, int am,
double* p);
void _SetResult(const double* x, double* values);
struct Constraint;
int32 fVariableCount;
BList fConstraints;
double* fVariables;
double** fTemp1;
double** fTemp2;
double** fZtrans;
double** fQ;
double** fActiveMatrix;
double** fActiveMatrixTemp;
};
} // 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