* Cleanup in ComplexLayouter and LayoutOptimizer: Removed debug output or

made it conditional, added/modified comments documenting the maths and
  algorithms.
* Refactored quite a bit in ComplexLayouter and added special handling for
  the case that the desired solution is already feasible.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22334 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2007-09-27 22:44:21 +00:00
parent 2745621c91
commit c29edec13e
4 changed files with 361 additions and 317 deletions
+257 -200
View File
@@ -20,6 +20,15 @@
#include "SimpleLayouter.h"
//#define TRACE_COMPLEX_LAYOUTER 1
#if TRACE_COMPLEX_LAYOUTER
# define TRACE(format...) printf(format);
# define TRACE_ONLY(x) x
#else
# define TRACE(format...)
# define TRACE_ONLY(x)
#endif
using std::nothrow;
@@ -41,6 +50,13 @@ public:
delete[] fLocations;
}
void InitFromSizes(int32* sizes)
{
fLocations[0] = 0;
for (int32 i = 0; i < fCount; i++)
fLocations[i + 1] = fLocations[i] + sizes[i] + fSpacing;
}
virtual float ElementLocation(int32 element)
{
if (element < 0 || element >= fCount)
@@ -106,6 +122,12 @@ struct ComplexLayouter::Constraint {
effectiveMax = max;
}
bool IsSatisfied(int32* sumValues) const
{
int32 value = sumValues[end] - sumValues[start - 1];
return (value >= min && value <= max);
}
int32 start;
int32 end;
int32 min;
@@ -143,9 +165,9 @@ ComplexLayouter::ComplexLayouter(int32 elementCount, int32 spacing)
fSums(new(nothrow) SumItem[elementCount + 1]),
fSumBackups(new(nothrow) SumItemBackup[elementCount + 1]),
fOptimizer(new(nothrow) LayoutOptimizer(elementCount)),
fLayoutValid(false)
fLayoutValid(false),
fOptimizerConstraintsAdded(false)
{
// TODO: Check initialization!
if (fConstraints)
memset(fConstraints, 0, sizeof(Constraint*) * fElementCount);
@@ -195,8 +217,8 @@ ComplexLayouter::AddConstraints(int32 element, int32 length,
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);
TRACE("%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;
@@ -283,7 +305,8 @@ ComplexLayouter::CreateLayoutInfo()
void
ComplexLayouter::Layout(LayoutInfo* _layoutInfo, float _size)
{
//printf("%p->ComplexLayouter::Layout(%ld)\n", this, (int32)_size);
TRACE("%p->ComplexLayouter::Layout(%ld)\n", this, (int32)_size);
if (fElementCount == 0)
return;
@@ -312,105 +335,19 @@ ComplexLayouter::Layout(LayoutInfo* _layoutInfo, float _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
#if TRACE_COMPLEX_LAYOUTER
TRACE("Layout(%ld)\n", size);
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;
}
SumItem& sum = sums[i + 1];
TRACE("[%ld] minc = %4ld, maxc = %4ld\n", i + 1, sum.min, sum.max);
}
#endif
// 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]);
if (!_Layout(size, sums, sizes)) {
}
layoutInfo->InitFromSizes(sizes);
}
@@ -454,10 +391,191 @@ ComplexLayouter::CloneLayouter()
}
// _Layout
bool
ComplexLayouter::_Layout(int32 size, SumItem* sums, int32* sizes)
{
// prepare the desired solution
SimpleLayouter::DistributeSize(size, fWeights, sizes, fElementCount);
if (_SatisfiesConstraints(sizes)) {
// The desired solution already satisfies all constraints.
return true;
}
double realSizes[fElementCount];
for (int32 i = 0; i < fElementCount; i++)
realSizes[i] = sizes[i];
if (!_AddOptimizerConstraints())
return false;
// 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;
#if TRACE_COMPLEX_LAYOUTER
TRACE("feasible solution vs. desired solution:\n");
for (int32 i = 0; i < fElementCount; i++)
TRACE("%8.4f %8.4f\n", values[i], realSizes[i]);
#endif
// solve
TRACE_ONLY(bigtime_t time = system_time();)
if (!fOptimizer->Solve(realSizes, size, values))
return false;
TRACE_ONLY(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 therefore satisfy all
// constraints the real solution satisfied.
TRACE("computed solution in %lld us:\n", time);
double realSum = 0;
double previousSum = 0;
for (int32 i = 0; i < fElementCount; i++) {
realSum += values[i];
double roundedRealSum = floor(realSum);
if (fuzzy_equals(realSum, roundedRealSum + 1))
realSum = roundedRealSum + 1;
sizes[i] = int32(roundedRealSum - previousSum);
previousSum = roundedRealSum;
TRACE("x[%ld] = %8.4f %4ld\n", i, values[i], sizes[i]);
}
return _SatisfiesConstraints(sizes);
}
// _AddOptimizerConstraints
bool
ComplexLayouter::_AddOptimizerConstraints()
{
if (fOptimizerConstraintsAdded)
return true;
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);
}
}
if (!success)
return false;
}
constraint = constraint->next;
}
}
fOptimizerConstraintsAdded = true;
return true;
}
// _SatisfiesConstraints
bool
ComplexLayouter::_SatisfiesConstraints(int32* sizes) const
{
int32 sumValues[fElementCount + 1];
sumValues[0] = 0;
for (int32 i = 0; i < fElementCount; i++)
sumValues[i + 1] = sumValues[i] + sizes[i];
return _SatisfiesConstraintsSums(sumValues);
}
// _SatisfiesConstraintsSums
bool
ComplexLayouter::_SatisfiesConstraintsSums(int32* sumValues) const
{
for (int32 i = 0; i < fElementCount; i++) {
Constraint* constraint = fConstraints[i];
while (constraint) {
if (!constraint->IsSatisfied(sumValues))
return false;
constraint = constraint->next;
}
}
return true;
}
// _ValidateLayout
void
ComplexLayouter::_ValidateLayout()
{
// The general idea for computing the min and max for the given constraints
// is that we rewrite the problem a little. Instead of considering the
// x_1, ... x_n (n = fElementCount) and the constraints of the form
// x_i + ... + x_{i+j} >= min[i,j] and
// x_i + ... + x_{i+j} >= max[i,j], with i >= 1, j >= 0, i + j <= n
// and min[i,j], max[i,j] >= 0
// we define
// c[0] = 0
// c[i] = \sum_{k=1}^i x_k, for all i, 1 <= i <= n
// and thus the constraints read:
// c[i+j] - c[i-1] >= min[i,j]
// c[i+j] - c[i-1] <= max[i,j]
//
// Let minc[i] and maxc[i] the limits imposed by the given constraints, i.e.
// minc[i] <= c[i] <= maxc[i] for any tuple of (c[i])_i satisfying the
// constraints (minc[i] and maxc[i] are unique), then we gain:
// minc[i+j] >= c[i-1] + min[i,j]
// maxc[i+j] <= c[i-1] + min[i,j]
// minc[i-1] >= minc[i+j] - max[i,j]
// maxc[i-1] >= maxc[i+j] - min[i,j]
// We can compute the minc[i] and maxc[i] in an iterative process,
// propagating the first to kinds of constraints forward and the other two
// backwards. First we start considering all min constraints only. They
// can't contradict each other and are usually to be enforced over max
// constraints. Afterwards we add the max constraints one by one. For each
// one of them we propagate resulting changes back and forth. In case of
// a conflict, we relax the max constraint as much as necessary to yield
// a consistent set of constraints. After all constraints have been
// incorporated, the resulting minc[n] and maxc[n] are the min and max
// limits we wanted to compute.
if (fLayoutValid)
return;
@@ -473,26 +591,26 @@ ComplexLayouter::_ValidateLayout()
}
// apply min constraints forward:
// minc[k] >= minc[i-1] + min[i,j]
// minc[i+j] >= 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)
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);
}
} else {
TRACE("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]
// maxc[i-1] <= maxc[i+j] - min[i,j]
for (int32 i = fElementCount - 1; i >= 0; i--) {
SumItem& sum = fSums[i + 1];
@@ -515,13 +633,14 @@ constraint->start, constraint->end, constraint->min);
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 TRACE_COMPLEX_LAYOUTER
for (int32 i = 0; i < fElementCount; i++) {
SumItem& sum = fSums[i + 1];
TRACE("[%ld] minc = %4ld, maxc = %4ld\n", i + 1, sum.min, sum.max);
}
#endif
if (fElementCount == 0) {
fMin = -1;
@@ -532,73 +651,10 @@ printf("[%ld] minc = %4ld, maxc = %4ld\n", i + 1, sum.min, sum.max);
fMax = fSums[fElementCount].max + spacing - 1;
}
fOptimizerConstraintsAdded = false;
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
@@ -636,21 +692,19 @@ ComplexLayouter::_ApplyMaxConstraint(Constraint* currentConstraint, int32 index)
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);
}
if (currentConstraint->effectiveMax != max) {
TRACE("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);
if (baseMin <= base.min && sumMax >= sum.max) {
TRACE("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);
@@ -700,9 +754,9 @@ currentConstraint->start, currentConstraint->end, currentConstraint->effectiveMa
// 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);
TRACE("relaxing conflicting max constraint (2): "
"x%ld + ... + x%ld <= %ld -> %ld\n", currentConstraint->start,
currentConstraint->end, currentConstraint->effectiveMax, max);
currentConstraint->effectiveMax = max;
_RestoreValues(index);
@@ -762,8 +816,9 @@ ComplexLayouter::_PropagateChanges(SumItem* sums, int32 toIndex,
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);
// TODO: Can this actually happen?
TRACE("adjusted max in propagation phase: index: "
"%ld: %ld -> %ld\n", i, sum.max, sum.min);
sum.max = sum.min;
sum.maxDirty = true;
}
@@ -793,10 +848,11 @@ ComplexLayouter::_PropagateChangesBack(SumItem* sums, int32 changedIndex,
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);
}
if (baseMin > base.max) {
TRACE("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;
}
@@ -806,10 +862,11 @@ printf("min above max in back propagation phase: index: (%ld -> %ld), "
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);
}
if (baseMax < base.min) {
TRACE("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;
}
@@ -1,6 +1,8 @@
/*
* Copyright 2007, Ingo Weinhold <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*
* Layouter implementation that can handle complex constraints.
*/
#ifndef COMPLEX_LAYOUTER_H
#define COMPLEX_LAYOUTER_H
@@ -43,6 +45,11 @@ private:
struct SumItem;
struct SumItemBackup;
bool _Layout(int32 size, SumItem* sums,
int32* sizes);
bool _AddOptimizerConstraints();
bool _SatisfiesConstraints(int32* sizes) const;
bool _SatisfiesConstraintsSums(int32* sums) const;
void _ValidateLayout();
void _ApplyMaxConstraint(
@@ -67,6 +74,7 @@ private:
float fMin;
float fMax;
bool fLayoutValid;
bool fOptimizerConstraintsAdded;
};
} // namespace Layout
+90 -115
View File
@@ -13,9 +13,53 @@
#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
@@ -239,7 +283,7 @@ solve(double** a, int n, double* b)
}
if (fuzzy_equals(pivotValue, 0)) {
printf("solve(): matrix is not regular\n");
TRACE_ERROR("solve(): matrix is not regular\n");
return false;
}
@@ -378,7 +422,7 @@ qr_decomposition(double** a, int m, int n, double* d, double** q)
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);
TRACE_ERROR("qr_decomposition(): 0 column %d\n", j);
return false;
}
@@ -469,7 +513,7 @@ struct LayoutOptimizer::Constraint {
void Print() const
{
printf("c[%2ld] - c[%2ld] %2s %4d\n", right, left,
TRACE("c[%2ld] - c[%2ld] %2s %4d\n", right, left,
(equality ? "=" : ">="), (int)value);
}
@@ -642,65 +686,41 @@ 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();
//}
TRACE_ONLY(
TRACE("constraints:\n");
for (int32 i = 0; i < constraintCount; i++) {
TRACE(" %-2ld: ", i);
((Constraint*)fConstraints.ItemAt(i))->Print();
}
)
// our QP is suppose to be in this form:
// 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 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.
//
// init our initial x
double x[fVariableCount];
x[0] = values[0];
for (int i = 1; i < fVariableCount; i++)
x[i] = values[i] + x[i - 1];
// 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 - 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);
TRACE("constraint %ld: actual: %f constraint: %f\n", i, actualValue,
constraint->value);
if (fuzzy_equals(actualValue, constraint->value))
activeConstraints.AddItem(constraint);
}
@@ -709,16 +729,16 @@ LayoutOptimizer::_Solve(const double* desired, double* values)
// 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;
TRACE_ONLY(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();
//}
TRACE_ONLY(
TRACE("\n[iteration %d]\n", iteration++);
TRACE("active set:\n");
for (int32 i = 0; i < activeConstraints.CountItems(); i++) {
TRACE(" ");
((Constraint*)activeConstraints.ItemAt(i))->Print();
}
)
// solve the QP:
// min_p 1/2p^TGp + g_k^Tp
@@ -729,7 +749,7 @@ LayoutOptimizer::_Solve(const double* desired, double* values)
int32 activeCount = activeConstraints.CountItems();
if (activeCount == 0) {
printf("Solve(): Error: No more active constraints!\n");
TRACE_ERROR("Solve(): Error: No more active constraints!\n");
return false;
}
@@ -761,9 +781,6 @@ LayoutOptimizer::_Solve(const double* desired, double* values)
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,
@@ -785,8 +802,8 @@ LayoutOptimizer::_Solve(const double* desired, double* values)
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");
TRACE_ERROR("Solve(): Transposed A has less linear independent "
"rows than it has columns!\n");
return false;
}
@@ -801,11 +818,9 @@ LayoutOptimizer::_Solve(const double* desired, double* values)
bool success = solve(aa, aam, lambda);
if (!success) {
// Impossible, since we've removed all linearly dependent rows.
printf("Solve(): Failed to compute lambda!\n");
TRACE_ERROR("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;
@@ -829,7 +844,6 @@ LayoutOptimizer::_Solve(const double* desired, double* values)
// 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;
}
@@ -859,7 +873,7 @@ LayoutOptimizer::_Solve(const double* desired, double* values)
barrier = i;
}
}
//printf("alpha: %f, barrier: %d\n", alpha, barrier);
TRACE("alpha: %f, barrier: %d\n", alpha, barrier);
if (alpha < 1)
activeConstraints.AddItem(fConstraints.ItemAt(barrier));
@@ -868,64 +882,25 @@ LayoutOptimizer::_Solve(const double* desired, double* values)
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
// 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
//
// 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
// 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;
@@ -935,7 +910,7 @@ LayoutOptimizer::_SolveSubProblem(const double* d, int am, double* p)
transpose_matrix(fActiveMatrix, fTemp1, am, an);
bool success = qr_decomposition(fTemp1, an, am, tempD, Q);
if (!success) {
printf("Solve(): QR decomposition failed!\n");
TRACE_ERROR("Solve(): QR decomposition failed!\n");
return false;
}
@@ -946,7 +921,7 @@ LayoutOptimizer::_SolveSubProblem(const double* d, int am, double* p)
for (int i = 0; i < zm; i++)
Z[i] = Q[i] + am;
// solve (Z^TGZ)p_Z = -Z^Tg_k
// solve (Z^TGZ)p_Z = -Z^Td
// Z^T
transpose_matrix(Z, fZtrans, zm, zn);
@@ -961,7 +936,7 @@ LayoutOptimizer::_SolveSubProblem(const double* d, int am, double* p)
success = solve(fTemp2, zn, pz);
if (!success) {
printf("Solve(): Failed to solve() system for p_Z\n");
TRACE_ERROR("Solve(): Failed to solve() system for p_Z\n");
return false;
}
+6 -2
View File
@@ -1,12 +1,16 @@
/*
* Copyright 2006, Haiku Inc.
* Distributed under the terms of the MIT License.
* Copyright 2006-2007, Ingo Weinhold <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*
* Layouter implementation that can handle simple layout constraints
* (restricting one element) only. It is
*/
#ifndef SIMPLE_LAYOUTER_H
#define SIMPLE_LAYOUTER_H
#include "Layouter.h"
class BList;
namespace BPrivate {