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