* 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:
@@ -20,6 +20,15 @@
|
|||||||
#include "SimpleLayouter.h"
|
#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;
|
using std::nothrow;
|
||||||
|
|
||||||
|
|
||||||
@@ -41,6 +50,13 @@ public:
|
|||||||
delete[] fLocations;
|
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)
|
virtual float ElementLocation(int32 element)
|
||||||
{
|
{
|
||||||
if (element < 0 || element >= fCount)
|
if (element < 0 || element >= fCount)
|
||||||
@@ -106,6 +122,12 @@ struct ComplexLayouter::Constraint {
|
|||||||
effectiveMax = max;
|
effectiveMax = max;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsSatisfied(int32* sumValues) const
|
||||||
|
{
|
||||||
|
int32 value = sumValues[end] - sumValues[start - 1];
|
||||||
|
return (value >= min && value <= max);
|
||||||
|
}
|
||||||
|
|
||||||
int32 start;
|
int32 start;
|
||||||
int32 end;
|
int32 end;
|
||||||
int32 min;
|
int32 min;
|
||||||
@@ -143,9 +165,9 @@ ComplexLayouter::ComplexLayouter(int32 elementCount, int32 spacing)
|
|||||||
fSums(new(nothrow) SumItem[elementCount + 1]),
|
fSums(new(nothrow) SumItem[elementCount + 1]),
|
||||||
fSumBackups(new(nothrow) SumItemBackup[elementCount + 1]),
|
fSumBackups(new(nothrow) SumItemBackup[elementCount + 1]),
|
||||||
fOptimizer(new(nothrow) LayoutOptimizer(elementCount)),
|
fOptimizer(new(nothrow) LayoutOptimizer(elementCount)),
|
||||||
fLayoutValid(false)
|
fLayoutValid(false),
|
||||||
|
fOptimizerConstraintsAdded(false)
|
||||||
{
|
{
|
||||||
// TODO: Check initialization!
|
|
||||||
if (fConstraints)
|
if (fConstraints)
|
||||||
memset(fConstraints, 0, sizeof(Constraint*) * fElementCount);
|
memset(fConstraints, 0, sizeof(Constraint*) * fElementCount);
|
||||||
|
|
||||||
@@ -195,8 +217,8 @@ ComplexLayouter::AddConstraints(int32 element, int32 length,
|
|||||||
if (element < 0 || length <= 0 || element + length > fElementCount)
|
if (element < 0 || length <= 0 || element + length > fElementCount)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
//printf("%p->ComplexLayouter::AddConstraints(%ld, %ld, %ld, %ld, %ld)\n",
|
TRACE("%p->ComplexLayouter::AddConstraints(%ld, %ld, %ld, %ld, %ld)\n",
|
||||||
//this, element, length, (int32)_min, (int32)_max, (int32)_preferred);
|
this, element, length, (int32)_min, (int32)_max, (int32)_preferred);
|
||||||
|
|
||||||
int32 spacing = fSpacing * (length - 1);
|
int32 spacing = fSpacing * (length - 1);
|
||||||
int32 min = (int32)_min + 1 - spacing;
|
int32 min = (int32)_min + 1 - spacing;
|
||||||
@@ -283,7 +305,8 @@ ComplexLayouter::CreateLayoutInfo()
|
|||||||
void
|
void
|
||||||
ComplexLayouter::Layout(LayoutInfo* _layoutInfo, float _size)
|
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)
|
if (fElementCount == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -312,105 +335,19 @@ ComplexLayouter::Layout(LayoutInfo* _layoutInfo, float _size)
|
|||||||
_PropagateChangesBack(sums, fElementCount - 1, NULL);
|
_PropagateChangesBack(sums, fElementCount - 1, NULL);
|
||||||
_PropagateChanges(sums, fElementCount - 1, NULL);
|
_PropagateChanges(sums, fElementCount - 1, NULL);
|
||||||
|
|
||||||
printf("Layout(%ld)\n", size);
|
#if TRACE_COMPLEX_LAYOUTER
|
||||||
for (int32 i = 0; i < fElementCount; i++) {
|
TRACE("Layout(%ld)\n", size);
|
||||||
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++) {
|
for (int32 i = 0; i < fElementCount; i++) {
|
||||||
SumItem& sum = fSums[i + 1];
|
SumItem& sum = sums[i + 1];
|
||||||
|
TRACE("[%ld] minc = %4ld, maxc = %4ld\n", i + 1, sum.min, sum.max);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
#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];
|
int32 sizes[fElementCount];
|
||||||
SimpleLayouter::DistributeSize(size, fWeights, sizes, fElementCount);
|
if (!_Layout(size, sums, sizes)) {
|
||||||
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]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
// _ValidateLayout
|
||||||
void
|
void
|
||||||
ComplexLayouter::_ValidateLayout()
|
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)
|
if (fLayoutValid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -473,26 +591,26 @@ ComplexLayouter::_ValidateLayout()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// apply min constraints forward:
|
// 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++) {
|
for (int32 i = 0; i < fElementCount; i++) {
|
||||||
SumItem& sum = fSums[i + 1];
|
SumItem& sum = fSums[i + 1];
|
||||||
|
|
||||||
Constraint* constraint = fConstraints[i];
|
Constraint* constraint = fConstraints[i];
|
||||||
while (constraint != NULL) {
|
while (constraint != NULL) {
|
||||||
int32 minSum = fSums[constraint->start].min + constraint->min;
|
int32 minSum = fSums[constraint->start].min + constraint->min;
|
||||||
if (minSum > sum.min)
|
if (minSum > sum.min) {
|
||||||
sum.min = minSum;
|
sum.min = minSum;
|
||||||
else {
|
} else {
|
||||||
printf("min constraint is redundant: x%ld + ... + x%ld >= %ld\n",
|
TRACE("min constraint is redundant: x%ld + ... + x%ld >= %ld\n",
|
||||||
constraint->start, constraint->end, constraint->min);
|
constraint->start, constraint->end, constraint->min);
|
||||||
}
|
}
|
||||||
|
|
||||||
constraint = constraint->next;
|
constraint = constraint->next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply min constraints backwards:
|
// 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--) {
|
for (int32 i = fElementCount - 1; i >= 0; i--) {
|
||||||
SumItem& sum = fSums[i + 1];
|
SumItem& sum = fSums[i + 1];
|
||||||
|
|
||||||
@@ -515,13 +633,14 @@ constraint->start, constraint->end, constraint->min);
|
|||||||
|
|
||||||
constraint = constraint->next;
|
constraint = constraint->next;
|
||||||
}
|
}
|
||||||
//printf("fSums[%ld] = {%ld, %ld}\n", i + 1, sum.min, sum.max);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int32 i = 0; i < fElementCount; i++) {
|
#if TRACE_COMPLEX_LAYOUTER
|
||||||
SumItem& sum = fSums[i + 1];
|
for (int32 i = 0; i < fElementCount; i++) {
|
||||||
printf("[%ld] minc = %4ld, maxc = %4ld\n", i + 1, sum.min, sum.max);
|
SumItem& sum = fSums[i + 1];
|
||||||
}
|
TRACE("[%ld] minc = %4ld, maxc = %4ld\n", i + 1, sum.min, sum.max);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (fElementCount == 0) {
|
if (fElementCount == 0) {
|
||||||
fMin = -1;
|
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;
|
fMax = fSums[fElementCount].max + spacing - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fOptimizerConstraintsAdded = false;
|
||||||
fLayoutValid = true;
|
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
|
// _ApplyMaxConstraint
|
||||||
void
|
void
|
||||||
@@ -636,21 +692,19 @@ ComplexLayouter::_ApplyMaxConstraint(Constraint* currentConstraint, int32 index)
|
|||||||
sumMax = base.max + max;
|
sumMax = base.max + max;
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply changes
|
if (currentConstraint->effectiveMax != max) {
|
||||||
|
TRACE("relaxing conflicting max constraint (1): "
|
||||||
if (currentConstraint->effectiveMax != max) {
|
"x%ld + ... + x%ld <= %ld -> %ld\n", currentConstraint->start,
|
||||||
printf("relaxing conflicting max constraint (1): x%ld + ... + x%ld <= %ld -> %ld\n",
|
currentConstraint->end, currentConstraint->effectiveMax, max);
|
||||||
currentConstraint->start, currentConstraint->end,
|
}
|
||||||
currentConstraint->effectiveMax, max);
|
|
||||||
}
|
|
||||||
currentConstraint->effectiveMax = max;
|
currentConstraint->effectiveMax = max;
|
||||||
|
|
||||||
if (baseMin <= base.min && sumMax >= sum.max)
|
if (baseMin <= base.min && sumMax >= sum.max) {
|
||||||
{
|
TRACE("max constraint is redundant: x%ld + ... + x%ld <= %ld\n",
|
||||||
printf("max constraint is redundant: x%ld + ... + x%ld <= %ld\n",
|
currentConstraint->start, currentConstraint->end,
|
||||||
currentConstraint->start, currentConstraint->end, currentConstraint->effectiveMax);
|
currentConstraint->effectiveMax);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// backup old values, in case we detect a conflict later
|
// backup old values, in case we detect a conflict later
|
||||||
_BackupValues(index);
|
_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 we've got a conflict, we relax the constraint and try again
|
||||||
if (diff > 0) {
|
if (diff > 0) {
|
||||||
max += diff;
|
max += diff;
|
||||||
printf("relaxing conflicting max constraint (2): x%ld + ... + x%ld <= %ld -> %ld\n",
|
TRACE("relaxing conflicting max constraint (2): "
|
||||||
currentConstraint->start, currentConstraint->end,
|
"x%ld + ... + x%ld <= %ld -> %ld\n", currentConstraint->start,
|
||||||
currentConstraint->effectiveMax, max);
|
currentConstraint->end, currentConstraint->effectiveMax, max);
|
||||||
currentConstraint->effectiveMax = max;
|
currentConstraint->effectiveMax = max;
|
||||||
|
|
||||||
_RestoreValues(index);
|
_RestoreValues(index);
|
||||||
@@ -762,8 +816,9 @@ ComplexLayouter::_PropagateChanges(SumItem* sums, int32 toIndex,
|
|||||||
|
|
||||||
if (sum.minDirty || sum.maxDirty) {
|
if (sum.minDirty || sum.maxDirty) {
|
||||||
if (sum.min > sum.max) {
|
if (sum.min > sum.max) {
|
||||||
// TODO: Can this actually happen?
|
// TODO: Can this actually happen?
|
||||||
printf("adjusted max in propagation phase: index: %ld: %ld -> %ld\n", i, sum.max, sum.min);
|
TRACE("adjusted max in propagation phase: index: "
|
||||||
|
"%ld: %ld -> %ld\n", i, sum.max, sum.min);
|
||||||
sum.max = sum.min;
|
sum.max = sum.min;
|
||||||
sum.maxDirty = true;
|
sum.maxDirty = true;
|
||||||
}
|
}
|
||||||
@@ -793,10 +848,11 @@ ComplexLayouter::_PropagateChangesBack(SumItem* sums, int32 changedIndex,
|
|||||||
if (sum.minDirty && !ignoreMaxConstraints) {
|
if (sum.minDirty && !ignoreMaxConstraints) {
|
||||||
int32 baseMin = sum.min - constraint->effectiveMax;
|
int32 baseMin = sum.min - constraint->effectiveMax;
|
||||||
if (baseMin > base.min) {
|
if (baseMin > base.min) {
|
||||||
if (baseMin > base.max) {
|
if (baseMin > base.max) {
|
||||||
printf("min above max in back propagation phase: index: (%ld -> %ld), "
|
TRACE("min above max in back propagation phase: index: "
|
||||||
"min: %ld, max: %ld\n", i, constraint->start, baseMin, base.max);
|
"(%ld -> %ld), min: %ld, max: %ld\n", i,
|
||||||
}
|
constraint->start, baseMin, base.max);
|
||||||
|
}
|
||||||
base.min = baseMin;
|
base.min = baseMin;
|
||||||
base.minDirty = true;
|
base.minDirty = true;
|
||||||
}
|
}
|
||||||
@@ -806,10 +862,11 @@ printf("min above max in back propagation phase: index: (%ld -> %ld), "
|
|||||||
if (sum.maxDirty) {
|
if (sum.maxDirty) {
|
||||||
int32 baseMax = sum.max - constraint->min;
|
int32 baseMax = sum.max - constraint->min;
|
||||||
if (baseMax < base.max) {
|
if (baseMax < base.max) {
|
||||||
if (baseMax < base.min) {
|
if (baseMax < base.min) {
|
||||||
printf("max below min in back propagation phase: index: (%ld -> %ld), "
|
TRACE("max below min in back propagation phase: index: "
|
||||||
"max: %ld, min: %ld\n", i, constraint->start, baseMax, base.min);
|
"(%ld -> %ld), max: %ld, min: %ld\n", i,
|
||||||
}
|
constraint->start, baseMax, base.min);
|
||||||
|
}
|
||||||
base.max = baseMax;
|
base.max = baseMax;
|
||||||
base.maxDirty = true;
|
base.maxDirty = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2007, Ingo Weinhold <[email protected]>.
|
* Copyright 2007, Ingo Weinhold <[email protected]>.
|
||||||
* All rights reserved. Distributed under the terms of the MIT License.
|
* All rights reserved. Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Layouter implementation that can handle complex constraints.
|
||||||
*/
|
*/
|
||||||
#ifndef COMPLEX_LAYOUTER_H
|
#ifndef COMPLEX_LAYOUTER_H
|
||||||
#define COMPLEX_LAYOUTER_H
|
#define COMPLEX_LAYOUTER_H
|
||||||
@@ -43,6 +45,11 @@ private:
|
|||||||
struct SumItem;
|
struct SumItem;
|
||||||
struct SumItemBackup;
|
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 _ValidateLayout();
|
||||||
void _ApplyMaxConstraint(
|
void _ApplyMaxConstraint(
|
||||||
@@ -67,6 +74,7 @@ private:
|
|||||||
float fMin;
|
float fMin;
|
||||||
float fMax;
|
float fMax;
|
||||||
bool fLayoutValid;
|
bool fLayoutValid;
|
||||||
|
bool fOptimizerConstraintsAdded;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Layout
|
} // namespace Layout
|
||||||
|
|||||||
@@ -13,9 +13,53 @@
|
|||||||
#include <AutoDeleter.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;
|
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
|
// #pragma mark - vector and matrix operations
|
||||||
|
|
||||||
|
|
||||||
@@ -239,7 +283,7 @@ solve(double** a, int n, double* b)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (fuzzy_equals(pivotValue, 0)) {
|
if (fuzzy_equals(pivotValue, 0)) {
|
||||||
printf("solve(): matrix is not regular\n");
|
TRACE_ERROR("solve(): matrix is not regular\n");
|
||||||
return false;
|
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];
|
innerProductU = innerProductU + a[i][j] * a[i][j];
|
||||||
double innerProduct = innerProductU + a[j][j] * a[j][j];
|
double innerProduct = innerProductU + a[j][j] * a[j][j];
|
||||||
if (fuzzy_equals(innerProduct, 0)) {
|
if (fuzzy_equals(innerProduct, 0)) {
|
||||||
printf("qr_decomposition(): 0 column %d\n", j);
|
TRACE_ERROR("qr_decomposition(): 0 column %d\n", j);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -469,7 +513,7 @@ struct LayoutOptimizer::Constraint {
|
|||||||
|
|
||||||
void Print() const
|
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);
|
(equality ? "=" : ">="), (int)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -642,65 +686,41 @@ LayoutOptimizer::_Solve(const double* desired, double* values)
|
|||||||
{
|
{
|
||||||
int32 constraintCount = fConstraints.CountItems();
|
int32 constraintCount = fConstraints.CountItems();
|
||||||
|
|
||||||
//printf("constraints:\n");
|
TRACE_ONLY(
|
||||||
//for (int32 i = 0; i < constraintCount; i++) {
|
TRACE("constraints:\n");
|
||||||
// printf(" %-2ld: ", i);
|
for (int32 i = 0; i < constraintCount; i++) {
|
||||||
// ((Constraint*)fConstraints.ItemAt(i))->Print();
|
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
|
// min_x 1/2x^TGx + x^Td
|
||||||
// s.t. a_i^Tx = b_i, i \in E
|
// s.t. a_i^Tx = b_i, i \in E
|
||||||
// a_i^Tx >= b_i, i \in I
|
// a_i^Tx >= b_i, i \in I
|
||||||
|
|
||||||
// init G and d
|
// init our initial x
|
||||||
//
|
|
||||||
// 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];
|
double x[fVariableCount];
|
||||||
x[0] = values[0];
|
x[0] = values[0];
|
||||||
for (int i = 1; i < fVariableCount; i++)
|
for (int i = 1; i < fVariableCount; i++)
|
||||||
x[i] = values[i] + x[i - 1];
|
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];
|
double d[fVariableCount];
|
||||||
for (int i = 0; i < fVariableCount - 1; i++)
|
for (int i = 0; i < fVariableCount - 1; i++)
|
||||||
d[i] = desired[i + 1] - desired[i];
|
d[i] = desired[i + 1] - desired[i];
|
||||||
d[fVariableCount - 1] = -desired[fVariableCount - 1];
|
d[fVariableCount - 1] = -desired[fVariableCount - 1];
|
||||||
|
|
||||||
//printf("d:\n");
|
|
||||||
//Matrix(fVariableCount, 1, d).Print();
|
|
||||||
|
|
||||||
// init active set
|
// init active set
|
||||||
BList activeConstraints(constraintCount);
|
BList activeConstraints(constraintCount);
|
||||||
|
|
||||||
for (int32 i = 0; i < constraintCount; i++) {
|
for (int32 i = 0; i < constraintCount; i++) {
|
||||||
Constraint* constraint = (Constraint*)fConstraints.ItemAt(i);
|
Constraint* constraint = (Constraint*)fConstraints.ItemAt(i);
|
||||||
double actualValue = constraint->ActualValue(x);
|
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))
|
if (fuzzy_equals(actualValue, constraint->value))
|
||||||
activeConstraints.AddItem(constraint);
|
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.
|
// 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,
|
// We do that by computing the QP resulting from our active constraint set,
|
||||||
// W^k. Afterward each iteration we adjust the active set.
|
// W^k. Afterward each iteration we adjust the active set.
|
||||||
//int iteration = 0;
|
TRACE_ONLY(int iteration = 0;)
|
||||||
while (true) {
|
while (true) {
|
||||||
//printf("\n[iteration %d]\n", iteration++);
|
TRACE_ONLY(
|
||||||
//printf("x:\n");
|
TRACE("\n[iteration %d]\n", iteration++);
|
||||||
//Matrix(fVariableCount, 1, x).Print();
|
TRACE("active set:\n");
|
||||||
//printf("active set:\n");
|
for (int32 i = 0; i < activeConstraints.CountItems(); i++) {
|
||||||
//for (int32 i = 0; i < activeConstraints.CountItems(); i++) {
|
TRACE(" ");
|
||||||
// printf(" ");
|
((Constraint*)activeConstraints.ItemAt(i))->Print();
|
||||||
// ((Constraint*)activeConstraints.ItemAt(i))->Print();
|
}
|
||||||
//}
|
)
|
||||||
|
|
||||||
// solve the QP:
|
// solve the QP:
|
||||||
// min_p 1/2p^TGp + g_k^Tp
|
// min_p 1/2p^TGp + g_k^Tp
|
||||||
@@ -729,7 +749,7 @@ LayoutOptimizer::_Solve(const double* desired, double* values)
|
|||||||
|
|
||||||
int32 activeCount = activeConstraints.CountItems();
|
int32 activeCount = activeConstraints.CountItems();
|
||||||
if (activeCount == 0) {
|
if (activeCount == 0) {
|
||||||
printf("Solve(): Error: No more active constraints!\n");
|
TRACE_ERROR("Solve(): Error: No more active constraints!\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -761,9 +781,6 @@ LayoutOptimizer::_Solve(const double* desired, double* values)
|
|||||||
if (!_SolveSubProblem(gxd, am, p))
|
if (!_SolveSubProblem(gxd, am, p))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
//printf("p:\n");
|
|
||||||
//Matrix(fVariableCount, 1, p).Print();
|
|
||||||
|
|
||||||
if (is_zero(p, fVariableCount)) {
|
if (is_zero(p, fVariableCount)) {
|
||||||
// compute Lagrange multipliers lambda_i
|
// compute Lagrange multipliers lambda_i
|
||||||
// if lambda_i >= 0 for all i \in W^k \union inequality constraints,
|
// 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;
|
const int aan = am;
|
||||||
if (aam != aan) {
|
if (aam != aan) {
|
||||||
// This should not happen, since A has full row rank.
|
// This should not happen, since A has full row rank.
|
||||||
printf("Solve(): Transposed A has less linear independent rows "
|
TRACE_ERROR("Solve(): Transposed A has less linear independent "
|
||||||
"than it has columns!\n");
|
"rows than it has columns!\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -801,11 +818,9 @@ LayoutOptimizer::_Solve(const double* desired, double* values)
|
|||||||
bool success = solve(aa, aam, lambda);
|
bool success = solve(aa, aam, lambda);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
// Impossible, since we've removed all linearly dependent rows.
|
// 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;
|
return false;
|
||||||
}
|
}
|
||||||
//printf("lambda:\n");
|
|
||||||
//Matrix(aam, 1, lambda).Print();
|
|
||||||
|
|
||||||
// find min lambda_i (only, if it's < 0, though)
|
// find min lambda_i (only, if it's < 0, though)
|
||||||
double minLambda = 0;
|
double minLambda = 0;
|
||||||
@@ -829,7 +844,6 @@ LayoutOptimizer::_Solve(const double* desired, double* values)
|
|||||||
// if the min lambda is >= 0, we're done
|
// if the min lambda is >= 0, we're done
|
||||||
if (minIndex < 0 || fuzzy_equals(minLambda, 0)) {
|
if (minIndex < 0 || fuzzy_equals(minLambda, 0)) {
|
||||||
_SetResult(x, values);
|
_SetResult(x, values);
|
||||||
//printf("all lambda_i >= 0\n");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -859,7 +873,7 @@ LayoutOptimizer::_Solve(const double* desired, double* values)
|
|||||||
barrier = i;
|
barrier = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//printf("alpha: %f, barrier: %d\n", alpha, barrier);
|
TRACE("alpha: %f, barrier: %d\n", alpha, barrier);
|
||||||
|
|
||||||
if (alpha < 1)
|
if (alpha < 1)
|
||||||
activeConstraints.AddItem(fConstraints.ItemAt(barrier));
|
activeConstraints.AddItem(fConstraints.ItemAt(barrier));
|
||||||
@@ -868,64 +882,25 @@ LayoutOptimizer::_Solve(const double* desired, double* values)
|
|||||||
add_vectors_scaled(x, p, alpha, fVariableCount);
|
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
|
bool
|
||||||
LayoutOptimizer::_SolveSubProblem(const double* d, int am, double* p)
|
LayoutOptimizer::_SolveSubProblem(const double* d, int am, double* p)
|
||||||
{
|
{
|
||||||
// x = p
|
// We have to solve the QP subproblem:
|
||||||
// d = g_k
|
// min_p 1/2p^TGp + d^Tp
|
||||||
// b = 0
|
// s.t. a_i^Tp = 0
|
||||||
|
// with a_i \in activeConstraints
|
||||||
//
|
//
|
||||||
// x^* = x + p
|
// We use the null space method, i.e. we find matrices Y and Z, such that
|
||||||
// c = Ax - b
|
// AZ = 0 and [Y Z] is regular. Then with
|
||||||
// g = d + Gx
|
// p = Yp_Y + Zp_z
|
||||||
//
|
// we get
|
||||||
// with x = 0 we get
|
// p_Y = 0
|
||||||
// c = -b = 0
|
// and
|
||||||
// g = d = g_k
|
// (Z^TGZ)p_Z = -(Z^TYp_Y + Z^Tg) = -Z^Td
|
||||||
//
|
// which is a linear equation system, which we can solve.
|
||||||
// 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;
|
const int an = fVariableCount;
|
||||||
|
|
||||||
@@ -935,7 +910,7 @@ LayoutOptimizer::_SolveSubProblem(const double* d, int am, double* p)
|
|||||||
transpose_matrix(fActiveMatrix, fTemp1, am, an);
|
transpose_matrix(fActiveMatrix, fTemp1, am, an);
|
||||||
bool success = qr_decomposition(fTemp1, an, am, tempD, Q);
|
bool success = qr_decomposition(fTemp1, an, am, tempD, Q);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
printf("Solve(): QR decomposition failed!\n");
|
TRACE_ERROR("Solve(): QR decomposition failed!\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -946,7 +921,7 @@ LayoutOptimizer::_SolveSubProblem(const double* d, int am, double* p)
|
|||||||
for (int i = 0; i < zm; i++)
|
for (int i = 0; i < zm; i++)
|
||||||
Z[i] = Q[i] + am;
|
Z[i] = Q[i] + am;
|
||||||
|
|
||||||
// solve (Z^TGZ)p_Z = -Z^Tg_k
|
// solve (Z^TGZ)p_Z = -Z^Td
|
||||||
|
|
||||||
// Z^T
|
// Z^T
|
||||||
transpose_matrix(Z, fZtrans, zm, zn);
|
transpose_matrix(Z, fZtrans, zm, zn);
|
||||||
@@ -961,7 +936,7 @@ LayoutOptimizer::_SolveSubProblem(const double* d, int am, double* p)
|
|||||||
|
|
||||||
success = solve(fTemp2, zn, pz);
|
success = solve(fTemp2, zn, pz);
|
||||||
if (!success) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2006, Haiku Inc.
|
* Copyright 2006-2007, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
|
||||||
* Distributed under the terms of the MIT License.
|
* 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
|
#ifndef SIMPLE_LAYOUTER_H
|
||||||
#define SIMPLE_LAYOUTER_H
|
#define SIMPLE_LAYOUTER_H
|
||||||
|
|
||||||
#include "Layouter.h"
|
#include "Layouter.h"
|
||||||
|
|
||||||
|
|
||||||
class BList;
|
class BList;
|
||||||
|
|
||||||
namespace BPrivate {
|
namespace BPrivate {
|
||||||
|
|||||||
Reference in New Issue
Block a user