1 /*
2 * Copyright 2007, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
5
6 #include "LayoutOptimizer.h"
7
8 #include <stdio.h>
9 #include <string.h>
10
11 #include <new>
12
13 #include <AutoDeleter.h>
14
15
16 //#define TRACE_LAYOUT_OPTIMIZER 1
17 #if TRACE_LAYOUT_OPTIMIZER
18 # define TRACE(format...) printf(format)
19 # define TRACE_ONLY(x) x
20 #else
21 # define TRACE(format...)
22 # define TRACE_ONLY(x)
23 #endif
24 #define TRACE_ERROR(format...) fprintf(stderr, format)
25
26 using std::nothrow;
27
28
29 /*! \class BPrivate::Layout::LayoutOptimizer
30
31 Given a set of layout constraints, a feasible solution, and a desired
32 (non-)solution this class finds an optimal solution. The optimization
33 criterion is to minimize the norm of the difference to the desired
34 (non-)solution.
35
36 It does so by implementing an active set method algorithm. The basic idea
37 is to start with the subset of the constraints that are barely satisfied by
38 the feasible solution, i.e. including all equality constraints and those
39 inequality constraints that are still satisfied, if restricted to equality
40 constraints. This set is called active set, the contained constraints active
41 constraints.
42
43 Considering all of the active constraints equality constraints a new
44 solution is computed, which still satisfies all those equality constraints
45 and is optimal with respect to the optimization criterion.
46
47 If the new solution equals the previous one, we find the inequality
48 constraint that, by keeping it in the active set, prevents us most from
49 further optimizing the solution. If none really does, we're done, having
50 found the globally optimal solution. Otherwise we remove the found
51 constraint from the active set and try again.
52
53 If the new solution does not equal the previous one, it might violate one
54 or more of the inactive constraints. If that is the case, we add the
55 most-violated constraint to the active set and adjust the new solution such
56 that barely satisfies that constraint. Otherwise, we don't adjust the
57 computed solution. With the adjusted respectively unadjusted solution
58 we enter the next iteration, i.e. by computing a new optimal solution with
59 respect to the active set.
60 */
61
62
63 // #pragma mark - vector and matrix operations
64
65
66 // is_zero
67 static inline bool
is_zero(double * x,int n)68 is_zero(double* x, int n)
69 {
70 for (int i = 0; i < n; i++) {
71 if (!fuzzy_equals(x[i], 0))
72 return false;
73 }
74
75 return true;
76 }
77
78
79 // add_vectors
80 static inline void
add_vectors(double * x,const double * y,int n)81 add_vectors(double* x, const double* y, int n)
82 {
83 for (int i = 0; i < n; i++)
84 x[i] += y[i];
85 }
86
87
88 // add_vectors_scaled
89 static inline void
add_vectors_scaled(double * x,const double * y,double scalar,int n)90 add_vectors_scaled(double* x, const double* y, double scalar, int n)
91 {
92 for (int i = 0; i < n; i++)
93 x[i] += y[i] * scalar;
94 }
95
96
97 // negate_vector
98 static inline void
negate_vector(double * x,int n)99 negate_vector(double* x, int n)
100 {
101 for (int i = 0; i < n; i++)
102 x[i] = -x[i];
103 }
104
105
106 // allocate_matrix
107 static double**
allocate_matrix(int m,int n)108 allocate_matrix(int m, int n)
109 {
110 double** matrix = new(nothrow) double*[m];
111 if (!matrix)
112 return NULL;
113
114 double* values = new(nothrow) double[m * n];
115 if (!values) {
116 delete[] matrix;
117 return NULL;
118 }
119
120 double* row = values;
121 for (int i = 0; i < m; i++, row += n)
122 matrix[i] = row;
123
124 return matrix;
125 }
126
127
128 // free_matrix
129 static void
free_matrix(double ** matrix)130 free_matrix(double** matrix)
131 {
132 if (matrix) {
133 delete[] *matrix;
134 delete[] matrix;
135 }
136 }
137
138
139 // multiply_matrix_vector
140 /*! y = Ax
141 A: m x n matrix
142 */
143 static inline void
multiply_matrix_vector(const double * const * A,const double * x,int m,int n,double * y)144 multiply_matrix_vector(const double* const* A, const double* x, int m, int n,
145 double* y)
146 {
147 for (int i = 0; i < m; i++) {
148 double sum = 0;
149 for (int k = 0; k < n; k++)
150 sum += A[i][k] * x[k];
151 y[i] = sum;
152 }
153 }
154
155
156 // multiply_matrices
157 /*! c = a*b
158 */
159 static void
multiply_matrices(const double * const * a,const double * const * b,double ** c,int m,int n,int l)160 multiply_matrices(const double* const* a, const double* const* b, double** c,
161 int m, int n, int l)
162 {
163 for (int i = 0; i < m; i++) {
164 for (int j = 0; j < l; j++) {
165 double sum = 0;
166 for (int k = 0; k < n; k++)
167 sum += a[i][k] * b[k][j];
168 c[i][j] = sum;
169 }
170 }
171 }
172
173
174 // transpose_matrix
175 static inline void
transpose_matrix(const double * const * A,double ** Atrans,int m,int n)176 transpose_matrix(const double* const* A, double** Atrans, int m, int n)
177 {
178 for (int i = 0; i < m; i++) {
179 for (int k = 0; k < n; k++)
180 Atrans[k][i] = A[i][k];
181 }
182 }
183
184
185 // zero_matrix
186 static inline void
zero_matrix(double ** A,int m,int n)187 zero_matrix(double** A, int m, int n)
188 {
189 for (int i = 0; i < m; i++) {
190 for (int k = 0; k < n; k++)
191 A[i][k] = 0;
192 }
193 }
194
195
196 // copy_matrix
197 static inline void
copy_matrix(const double * const * A,double ** B,int m,int n)198 copy_matrix(const double* const* A, double** B, int m, int n)
199 {
200 for (int i = 0; i < m; i++) {
201 for (int k = 0; k < n; k++)
202 B[i][k] = A[i][k];
203 }
204 }
205
206
207 static inline void
multiply_optimization_matrix_vector(const double * x,int n,double * y)208 multiply_optimization_matrix_vector(const double* x, int n, double* y)
209 {
210 // The matrix has the form:
211 // 2 -1 0 ... 0 0
212 // -1 2 -1 0 ... . .
213 // 0 -1 2 . .
214 // . 0 . . .
215 // . . 0 0
216 // . . -1 0
217 // 0 ... 0 -1 2 -1
218 // 0 ... -1 1
219 if (n == 1) {
220 y[0] = x[0];
221 return;
222 }
223
224 y[0] = 2 * x[0] - x[1];
225 for (int i = 1; i < n - 1; i++)
226 y[i] = 2 * x[i] - x[i - 1] - x[i + 1];
227 y[n - 1] = x[n - 1] - x[n - 2];
228 }
229
230
231 static inline void
multiply_optimization_matrix_matrix(const double * const * A,int m,int n,double ** B)232 multiply_optimization_matrix_matrix(const double* const* A, int m, int n,
233 double** B)
234 {
235 if (m == 1) {
236 memcpy(B[0], A[0], n * sizeof(double));
237 return;
238 }
239
240 for (int k = 0; k < n; k++) {
241 B[0][k] = 2 * A[0][k] - A[1][k];
242 for (int i = 1; i < m - 1; i++)
243 B[i][k] = 2 * A[i][k] - A[i - 1][k] - A[i + 1][k];
244 B[m - 1][k] = A[m - 1][k] - A[m - 2][k];
245 }
246 }
247
248
249 template<typename Type>
250 static inline void
swap(Type & a,Type & b)251 swap(Type& a, Type& b)
252 {
253 Type c = a;
254 a = b;
255 b = c;
256 }
257
258
259 // #pragma mark - algorithms
260
261
262 bool
solve(double ** a,int n,double * b)263 solve(double** a, int n, double* b)
264 {
265 // index array for row permutation
266 // Note: We could eliminate it, if we would permutate the row pointers of a.
267 int indices[n];
268 for (int i = 0; i < n; i++)
269 indices[i] = i;
270
271 // forward elimination
272 for (int i = 0; i < n - 1; i++) {
273 // find pivot
274 int pivot = i;
275 double pivotValue = fabs(a[indices[i]][i]);
276 for (int j = i + 1; j < n; j++) {
277 int index = indices[j];
278 double value = fabs(a[index][i]);
279 if (value > pivotValue) {
280 pivot = j;
281 pivotValue = value;
282 }
283 }
284
285 if (fuzzy_equals(pivotValue, 0)) {
286 TRACE_ERROR("solve(): matrix is not regular\n");
287 return false;
288 }
289
290 if (pivot != i) {
291 swap(indices[i], indices[pivot]);
292 swap(b[i], b[pivot]);
293 }
294 pivot = indices[i];
295
296 // eliminate
297 for (int j = i + 1; j < n; j++) {
298 int index = indices[j];
299 double q = -a[index][i] / a[pivot][i];
300 a[index][i] = 0;
301 for (int k = i + 1; k < n; k++)
302 a[index][k] += a[pivot][k] * q;
303 b[j] += b[i] * q;
304 }
305 }
306
307 // backwards substitution
308 for (int i = n - 1; i >= 0; i--) {
309 int index = indices[i];
310 double sum = b[i];
311 for (int j = i + 1; j < n; j++)
312 sum -= a[index][j] * b[j];
313
314 b[i] = sum / a[index][i];
315 }
316
317 return true;
318 }
319
320
321 int
compute_dependencies(double ** a,int m,int n,bool * independent)322 compute_dependencies(double** a, int m, int n, bool* independent)
323 {
324 // index array for row permutation
325 // Note: We could eliminate it, if we would permutate the row pointers of a.
326 int indices[m];
327 for (int i = 0; i < m; i++)
328 indices[i] = i;
329
330 // forward elimination
331 int iterations = (m > n ? n : m);
332 int i = 0;
333 int column = 0;
334 for (; i < iterations && column < n; i++) {
335 // find next pivot
336 int pivot = i;
337 do {
338 double pivotValue = fabs(a[indices[i]][column]);
339 for (int j = i + 1; j < m; j++) {
340 int index = indices[j];
341 double value = fabs(a[index][column]);
342 if (value > pivotValue) {
343 pivot = j;
344 pivotValue = value;
345 }
346 }
347
348 if (!fuzzy_equals(pivotValue, 0))
349 break;
350
351 column++;
352 } while (column < n);
353
354 if (column == n)
355 break;
356
357 if (pivot != i)
358 swap(indices[i], indices[pivot]);
359 pivot = indices[i];
360
361 independent[pivot] = true;
362
363 // eliminate
364 for (int j = i + 1; j < m; j++) {
365 int index = indices[j];
366 double q = -a[index][column] / a[pivot][column];
367 a[index][column] = 0;
368 for (int k = column + 1; k < n; k++)
369 a[index][k] += a[pivot][k] * q;
370 }
371
372 column++;
373 }
374
375 for (int j = i; j < m; j++)
376 independent[indices[j]] = false;
377
378 return i;
379 }
380
381
382 // remove_linearly_dependent_rows
383 static int
remove_linearly_dependent_rows(double ** A,double ** temp,bool * independentRows,int m,int n)384 remove_linearly_dependent_rows(double** A, double** temp, bool* independentRows,
385 int m, int n)
386 {
387 // copy to temp
388 copy_matrix(A, temp, m, n);
389
390 int count = compute_dependencies(temp, m, n, independentRows);
391 if (count == m)
392 return count;
393
394 // remove the rows
395 int index = 0;
396 for (int i = 0; i < m; i++) {
397 if (independentRows[i]) {
398 if (index < i) {
399 for (int k = 0; k < n; k++)
400 A[index][k] = A[i][k];
401 }
402 index++;
403 }
404 }
405
406 return count;
407 }
408
409
410 /*! QR decomposition using Householder transformations.
411 */
412 bool
qr_decomposition(double ** a,int m,int n,double * d,double ** q)413 qr_decomposition(double** a, int m, int n, double* d, double** q)
414 {
415 if (m < n)
416 return false;
417
418 for (int j = 0; j < n; j++) {
419 // inner product of the first vector x of the (j,j) minor
420 double innerProductU = 0;
421 for (int i = j + 1; i < m; i++)
422 innerProductU = innerProductU + a[i][j] * a[i][j];
423 double innerProduct = innerProductU + a[j][j] * a[j][j];
424 if (fuzzy_equals(innerProduct, 0)) {
425 TRACE_ERROR("qr_decomposition(): 0 column %d\n", j);
426 return false;
427 }
428
429 // alpha (norm of x with opposite signedness of x_1) and thus r_{j,j}
430 double alpha = (a[j][j] < 0 ? sqrt(innerProduct) : -sqrt(innerProduct));
431 d[j] = alpha;
432
433 double beta = 1 / (alpha * a[j][j] - innerProduct);
434
435 // u = x - alpha * e_1
436 // (u is a[j..n][j])
437 a[j][j] -= alpha;
438
439 // left-multiply A_k with Q_k, thus obtaining a row of R and the A_{k+1}
440 // for the next iteration
441 for (int k = j + 1; k < n; k++) {
442 double sum = 0;
443 for (int i = j; i < m; i++)
444 sum += a[i][j] * a[i][k];
445 sum *= beta;
446
447 for (int i = j; i < m; i++)
448 a[i][k] += a[i][j] * sum;
449 }
450
451 // v = u/|u|
452 innerProductU += a[j][j] * a[j][j];
453 double beta2 = -2 / innerProductU;
454
455 // right-multiply Q with Q_k
456 // Q_k = I - 2vv^T
457 // Q * Q_k = Q - 2 * Q * vv^T
458 if (j == 0) {
459 for (int k = 0; k < m; k++) {
460 for (int i = 0; i < m; i++)
461 q[k][i] = beta2 * a[k][0] * a[i][0];
462
463 q[k][k] += 1;
464 }
465 } else {
466 for (int k = 0; k < m; k++) {
467 double sum = 0;
468 for (int i = j; i < m; i++)
469 sum += q[k][i] * a[i][j];
470 sum *= beta2;
471
472 for (int i = j; i < m; i++)
473 q[k][i] += sum * a[i][j];
474 }
475 }
476 }
477
478 return true;
479 }
480
481
482 // MatrixDeleter
483 struct MatrixDelete {
operator ()MatrixDelete484 inline void operator()(double** matrix)
485 {
486 free_matrix(matrix);
487 }
488 };
489 typedef BPrivate::AutoDeleter<double*, MatrixDelete> MatrixDeleter;
490
491
492 // Constraint
493 struct LayoutOptimizer::Constraint {
ConstraintLayoutOptimizer::Constraint494 Constraint(int32 left, int32 right, double value, bool equality,
495 int32 index)
496 : left(left),
497 right(right),
498 value(value),
499 index(index),
500 equality(equality)
501 {
502 }
503
ActualValueLayoutOptimizer::Constraint504 double ActualValue(double* values) const
505 {
506 double result = 0;
507 if (right >= 0)
508 result = values[right];
509 if (left >= 0)
510 result -= values[left];
511 return result;
512 }
513
PrintLayoutOptimizer::Constraint514 void Print() const
515 {
516 TRACE("c[%2ld] - c[%2ld] %2s %4d\n", right, left,
517 (equality ? "=" : ">="), (int)value);
518 }
519
520 int32 left;
521 int32 right;
522 double value;
523 int32 index;
524 bool equality;
525 };
526
527
528 // #pragma mark - LayoutOptimizer
529
530
531 // constructor
LayoutOptimizer(int32 variableCount)532 LayoutOptimizer::LayoutOptimizer(int32 variableCount)
533 : fVariableCount(variableCount),
534 fConstraints(),
535 fVariables(new (nothrow) double[variableCount])
536 {
537 fTemp1 = allocate_matrix(fVariableCount, fVariableCount);
538 fTemp2 = allocate_matrix(fVariableCount, fVariableCount);
539 fZtrans = allocate_matrix(fVariableCount, fVariableCount);
540 fQ = allocate_matrix(fVariableCount, fVariableCount);
541 }
542
543
544 // destructor
~LayoutOptimizer()545 LayoutOptimizer::~LayoutOptimizer()
546 {
547 free_matrix(fTemp1);
548 free_matrix(fTemp2);
549 free_matrix(fZtrans);
550 free_matrix(fQ);
551
552 delete[] fVariables;
553
554 for (int32 i = 0;
555 Constraint* constraint = (Constraint*)fConstraints.ItemAt(i);
556 i++) {
557 delete constraint;
558 }
559 }
560
561
562 // InitCheck
563 status_t
InitCheck() const564 LayoutOptimizer::InitCheck() const
565 {
566 if (!fVariables || !fTemp1 || !fTemp2 || !fZtrans || !fQ)
567 return B_NO_MEMORY;
568 return B_OK;
569 }
570
571
572 // Clone
573 LayoutOptimizer*
Clone() const574 LayoutOptimizer::Clone() const
575 {
576 LayoutOptimizer* clone = new(nothrow) LayoutOptimizer(fVariableCount);
577 ObjectDeleter<LayoutOptimizer> cloneDeleter(clone);
578 if (!clone || clone->InitCheck() != B_OK
579 || !clone->AddConstraintsFrom(this)) {
580 return NULL;
581 }
582
583 return cloneDeleter.Detach();
584 }
585
586
587 // AddConstraint
588 /*! Adds a constraint of the form
589 \sum_{i=left+1}^{right} x_i >=/= value, if left < right
590 -\sum_{i=right+1}^{left} x_i >=/= value, if left > right
591 If \a equality is \c true, the constraint is an equality constraint.
592 */
593 bool
AddConstraint(int32 left,int32 right,double value,bool equality)594 LayoutOptimizer::AddConstraint(int32 left, int32 right, double value,
595 bool equality)
596 {
597 Constraint* constraint = new(nothrow) Constraint(left, right, value,
598 equality, fConstraints.CountItems());
599 if (constraint == NULL)
600 return false;
601
602 if (!fConstraints.AddItem(constraint)) {
603 delete constraint;
604 return false;
605 }
606
607 return true;
608 }
609
610
611 // AddConstraintsFrom
612 bool
AddConstraintsFrom(const LayoutOptimizer * other)613 LayoutOptimizer::AddConstraintsFrom(const LayoutOptimizer* other)
614 {
615 if (!other || other->fVariableCount != fVariableCount)
616 return false;
617
618 int32 count = fConstraints.CountItems();
619 for (int32 i = 0; i < count; i++) {
620 Constraint* constraint = (Constraint*)other->fConstraints.ItemAt(i);
621 if (!AddConstraint(constraint->left, constraint->right,
622 constraint->value, constraint->equality)) {
623 return false;
624 }
625 }
626
627 return true;
628 }
629
630
631 // RemoveAllConstraints
632 void
RemoveAllConstraints()633 LayoutOptimizer::RemoveAllConstraints()
634 {
635 int32 count = fConstraints.CountItems();
636 for (int32 i = 0; i < count; i++) {
637 Constraint* constraint = (Constraint*)fConstraints.ItemAt(i);
638 delete constraint;
639 }
640 fConstraints.MakeEmpty();
641 }
642
643
644 // Solve
645 /*! Solves the quadratic program (QP) given by the constraints added via
646 AddConstraint(), the additional constraint \sum_{i=0}^{n-1} x_i = size,
647 and the optimization criterion to minimize
648 \sum_{i=0}^{n-1} (x_i - desired[i])^2.
649 The \a values array must contain a feasible solution when called and will
650 be overwritten with the optimial solution the method computes.
651 */
652 bool
Solve(const double * desired,double size,double * values)653 LayoutOptimizer::Solve(const double* desired, double size, double* values)
654 {
655 if (fVariables == NULL || desired == NULL|| values == NULL)
656 return false;
657
658 int32 constraintCount = fConstraints.CountItems() + 1;
659
660 // allocate the active constraint matrix and its transposed matrix
661 fActiveMatrix = allocate_matrix(constraintCount, fVariableCount);
662 fActiveMatrixTemp = allocate_matrix(constraintCount, fVariableCount);
663 MatrixDeleter _(fActiveMatrix);
664 MatrixDeleter _2(fActiveMatrixTemp);
665 if (!fActiveMatrix || !fActiveMatrixTemp)
666 return false;
667
668 // add sum constraint
669 if (!AddConstraint(-1, fVariableCount - 1, size, true))
670 return false;
671
672 bool success = _Solve(desired, values);
673
674 // remove sum constraint
675 Constraint* constraint = (Constraint*)fConstraints.RemoveItem(
676 constraintCount - 1);
677 delete constraint;
678
679 return success;
680 }
681
682
683 // _Solve
684 bool
_Solve(const double * desired,double * values)685 LayoutOptimizer::_Solve(const double* desired, double* values)
686 {
687 int32 constraintCount = fConstraints.CountItems();
688
689 TRACE_ONLY(
690 TRACE("constraints:\n");
691 for (int32 i = 0; i < constraintCount; i++) {
692 TRACE(" %-2ld: ", i);
693 ((Constraint*)fConstraints.ItemAt(i))->Print();
694 }
695 )
696
697 // our QP is supposed to be in this form:
698 // min_x 1/2x^TGx + x^Td
699 // s.t. a_i^Tx = b_i, i \in E
700 // a_i^Tx >= b_i, i \in I
701
702 // init our initial x
703 double x[fVariableCount];
704 x[0] = values[0];
705 for (int i = 1; i < fVariableCount; i++)
706 x[i] = values[i] + x[i - 1];
707
708 // init d
709 // Note that the values of d and of G result from rewriting the
710 // ||x - desired|| we actually want to minimize.
711 double d[fVariableCount];
712 for (int i = 0; i < fVariableCount - 1; i++)
713 d[i] = desired[i + 1] - desired[i];
714 d[fVariableCount - 1] = -desired[fVariableCount - 1];
715
716 // init active set
717 BList activeConstraints(constraintCount);
718
719 for (int32 i = 0; i < constraintCount; i++) {
720 Constraint* constraint = (Constraint*)fConstraints.ItemAt(i);
721 double actualValue = constraint->ActualValue(x);
722 TRACE("constraint %ld: actual: %f constraint: %f\n", i, actualValue,
723 constraint->value);
724 if (fuzzy_equals(actualValue, constraint->value))
725 activeConstraints.AddItem(constraint);
726 }
727
728 // The main loop: Each iteration we try to get closer to the optimum
729 // solution. We compute a vector p that brings our x closer to the optimum.
730 // We do that by computing the QP resulting from our active constraint set,
731 // W^k. Afterward each iteration we adjust the active set.
732 TRACE_ONLY(int iteration = 0;)
733 while (true) {
734 TRACE_ONLY(
735 TRACE("\n[iteration %d]\n", iteration++);
736 TRACE("active set:\n");
737 for (int32 i = 0; i < activeConstraints.CountItems(); i++) {
738 TRACE(" ");
739 ((Constraint*)activeConstraints.ItemAt(i))->Print();
740 }
741 )
742
743 // solve the QP:
744 // min_p 1/2p^TGp + g_k^Tp
745 // s.t. a_i^Tp = 0
746 // with a_i \in activeConstraints
747 // g_k = Gx_k + d
748 // p = x - x_k
749
750 int32 activeCount = activeConstraints.CountItems();
751 if (activeCount == 0) {
752 TRACE_ERROR("Solve(): Error: No more active constraints!\n");
753 return false;
754 }
755
756 // construct a matrix from the active constraints
757 int am = activeCount;
758 const int an = fVariableCount;
759 bool independentRows[activeCount];
760 zero_matrix(fActiveMatrix, am, an);
761
762 for (int32 i = 0; i < activeCount; i++) {
763 Constraint* constraint = (Constraint*)activeConstraints.ItemAt(i);
764 if (constraint->right >= 0)
765 fActiveMatrix[i][constraint->right] = 1;
766 if (constraint->left >= 0)
767 fActiveMatrix[i][constraint->left] = -1;
768 }
769
770 // TODO: The fActiveMatrix is sparse (max 2 entries per row). There should be
771 // some room for optimization.
772 am = remove_linearly_dependent_rows(fActiveMatrix, fActiveMatrixTemp,
773 independentRows, am, an);
774
775 // gxd = G * x + d
776 double gxd[fVariableCount];
777 multiply_optimization_matrix_vector(x, fVariableCount, gxd);
778 add_vectors(gxd, d, fVariableCount);
779
780 double p[fVariableCount];
781 if (!_SolveSubProblem(gxd, am, p))
782 return false;
783
784 if (is_zero(p, fVariableCount)) {
785 // compute Lagrange multipliers lambda_i
786 // if lambda_i >= 0 for all i \in W^k \union inequality constraints,
787 // then we're done.
788 // Otherwise remove the constraint with the smallest lambda_i
789 // from the active set.
790 // The derivation of the Lagrangian yields:
791 // \sum_{i \in W^k}(lambda_ia_i) = Gx_k + d
792 // Which is an system we can solve:
793 // A^Tlambda = Gx_k + d
794
795 // A^T is over-determined, hence we need to reduce the number of
796 // rows before we can solve it.
797 bool independentColumns[an];
798 double** aa = fTemp1;
799 transpose_matrix(fActiveMatrix, aa, am, an);
800 const int aam = remove_linearly_dependent_rows(aa, fTemp2,
801 independentColumns, an, am);
802 const int aan = am;
803 if (aam != aan) {
804 // This should not happen, since A has full row rank.
805 TRACE_ERROR("Solve(): Transposed A has less linear independent "
806 "rows than it has columns!\n");
807 return false;
808 }
809
810 // also reduce the number of rows on the right hand side
811 double lambda[aam];
812 int index = 0;
813 for (int i = 0; i < an; i++) {
814 if (independentColumns[i])
815 lambda[index++] = gxd[i];
816 }
817
818 bool success = solve(aa, aam, lambda);
819 if (!success) {
820 // Impossible, since we've removed all linearly dependent rows.
821 TRACE_ERROR("Solve(): Failed to compute lambda!\n");
822 return false;
823 }
824
825 // find min lambda_i (only, if it's < 0, though)
826 double minLambda = 0;
827 int minIndex = -1;
828 index = 0;
829 for (int i = 0; i < activeCount; i++) {
830 if (independentRows[i]) {
831 Constraint* constraint
832 = (Constraint*)activeConstraints.ItemAt(i);
833 if (!constraint->equality) {
834 if (lambda[index] < minLambda) {
835 minLambda = lambda[index];
836 minIndex = i;
837 }
838 }
839
840 index++;
841 }
842 }
843
844 // if the min lambda is >= 0, we're done
845 if (minIndex < 0 || fuzzy_equals(minLambda, 0)) {
846 _SetResult(x, values);
847 return true;
848 }
849
850 // remove i from the active set
851 activeConstraints.RemoveItem(minIndex);
852
853 } else {
854 // compute alpha_k
855 double alpha = 1;
856 int barrier = -1;
857 // if alpha_k < 1, add a barrier constraint to W^k
858 for (int32 i = 0; i < constraintCount; i++) {
859 Constraint* constraint = (Constraint*)fConstraints.ItemAt(i);
860 if (activeConstraints.HasItem(constraint))
861 continue;
862
863 double divider = constraint->ActualValue(p);
864 if (divider > 0 || fuzzy_equals(divider, 0))
865 continue;
866
867 // (b_i - a_i^Tx_k) / a_i^Tp_k
868 double alphaI = constraint->value
869 - constraint->ActualValue(x);
870 alphaI /= divider;
871 if (alphaI < alpha) {
872 alpha = alphaI;
873 barrier = i;
874 }
875 }
876 TRACE("alpha: %f, barrier: %d\n", alpha, barrier);
877
878 if (alpha < 1)
879 activeConstraints.AddItem(fConstraints.ItemAt(barrier));
880
881 // x += p * alpha;
882 add_vectors_scaled(x, p, alpha, fVariableCount);
883 }
884 }
885 }
886
887
888 bool
_SolveSubProblem(const double * d,int am,double * p)889 LayoutOptimizer::_SolveSubProblem(const double* d, int am, double* p)
890 {
891 // We have to solve the QP subproblem:
892 // min_p 1/2p^TGp + d^Tp
893 // s.t. a_i^Tp = 0
894 // with a_i \in activeConstraints
895 //
896 // We use the null space method, i.e. we find matrices Y and Z, such that
897 // AZ = 0 and [Y Z] is regular. Then with
898 // p = Yp_Y + Zp_z
899 // we get
900 // p_Y = 0
901 // and
902 // (Z^TGZ)p_Z = -(Z^TYp_Y + Z^Tg) = -Z^Td
903 // which is a linear equation system, which we can solve.
904
905 const int an = fVariableCount;
906
907 // we get Y and Z by QR decomposition of A^T
908 double tempD[am];
909 double** const Q = fQ;
910 transpose_matrix(fActiveMatrix, fTemp1, am, an);
911 bool success = qr_decomposition(fTemp1, an, am, tempD, Q);
912 if (!success) {
913 TRACE_ERROR("Solve(): QR decomposition failed!\n");
914 return false;
915 }
916
917 // Z is the (1, m + 1) minor of Q
918 const int zm = an;
919 const int zn = an - am;
920 double* Z[zm];
921 for (int i = 0; i < zm; i++)
922 Z[i] = Q[i] + am;
923
924 // solve (Z^TGZ)p_Z = -Z^Td
925
926 // Z^T
927 transpose_matrix(Z, fZtrans, zm, zn);
928 // rhs: -Z^T * d;
929 double pz[zm];
930 multiply_matrix_vector(fZtrans, d, zn, zm, pz);
931 negate_vector(pz, zn);
932
933 // fTemp2 = Ztrans * G * Z
934 multiply_optimization_matrix_matrix(Z, an, zn, fTemp1);
935 multiply_matrices(fZtrans, fTemp1, fTemp2, zn, zm, zn);
936
937 success = solve(fTemp2, zn, pz);
938 if (!success) {
939 TRACE_ERROR("Solve(): Failed to solve() system for p_Z\n");
940 return false;
941 }
942
943 // p = Z * pz;
944 multiply_matrix_vector(Z, pz, zm, zn, p);
945
946 return true;
947 }
948
949
950 // _SetResult
951 void
_SetResult(const double * x,double * values)952 LayoutOptimizer::_SetResult(const double* x, double* values)
953 {
954 values[0] = x[0];
955 for (int i = 1; i < fVariableCount; i++)
956 values[i] = x[i] - x[i - 1];
957 }
958