Restore the "GPL licensing not permitted" in GLUT license headers.
[haiku.git] / src / libs / linprog / LayoutOptimizer.cpp
blob203c622806ee32f3cdefbf7f3f9e3f25f739f0af
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 */
8 #include "LayoutOptimizer.h"
10 #include <algorithm>
11 #include <new>
12 #include <stdio.h>
13 #include <string.h>
15 #include <AutoDeleter.h>
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)
28 using std::nothrow;
29 using std::swap;
30 using std::max;
33 bool is_soft(Constraint* constraint)
35 if (constraint->Op() != LinearProgramming::kEQ)
36 return false;
37 if (constraint->PenaltyNeg() > 0. || constraint->PenaltyPos() > 0.)
38 return true;
39 return false;
43 /*! \class BPrivate::Layout::LayoutOptimizer
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.
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.
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.
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.
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.
77 // #pragma mark - vector and matrix operations
80 // is_zero
81 static inline bool
82 is_zero(double* x, int n)
84 for (int i = 0; i < n; i++) {
85 if (!fuzzy_equals(x[i], 0))
86 return false;
89 return true;
93 // add_vectors
94 static inline void
95 add_vectors(double* x, const double* y, int n)
97 for (int i = 0; i < n; i++)
98 x[i] += y[i];
102 // add_vectors_scaled
103 static inline void
104 add_vectors_scaled(double* x, const double* y, double scalar, int n)
106 for (int i = 0; i < n; i++)
107 x[i] += y[i] * scalar;
111 // negate_vector
112 static inline void
113 negate_vector(double* x, int n)
115 for (int i = 0; i < n; i++)
116 x[i] = -x[i];
120 // allocate_matrix
121 double**
122 allocate_matrix(int m, int n)
124 double** matrix = new(nothrow) double*[m];
125 if (!matrix)
126 return NULL;
128 double* values = new(nothrow) double[m * n];
129 if (!values) {
130 delete[] matrix;
131 return NULL;
134 double* row = values;
135 for (int i = 0; i < m; i++, row += n)
136 matrix[i] = row;
138 return matrix;
142 // free_matrix
143 void
144 free_matrix(double** matrix)
146 if (matrix) {
147 delete[] *matrix;
148 delete[] matrix;
153 // multiply_matrix_vector
154 /*! y = Ax
155 A: m x n matrix
157 static inline void
158 multiply_matrix_vector(const double* const* A, const double* x, int m, int n,
159 double* y)
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;
170 // multiply_matrices
171 /*! c = a*b
173 static void
174 multiply_matrices(const double* const* a, const double* const* b, double** c,
175 int m, int n, int l)
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;
188 // transpose_matrix
189 static inline void
190 transpose_matrix(const double* const* A, double** Atrans, int m, int n)
192 for (int i = 0; i < m; i++) {
193 for (int k = 0; k < n; k++)
194 Atrans[k][i] = A[i][k];
199 // zero_matrix
200 void
201 zero_matrix(double** A, int m, int n)
203 for (int i = 0; i < m; i++) {
204 for (int k = 0; k < n; k++)
205 A[i][k] = 0;
210 // copy_matrix
211 void
212 copy_matrix(const double* const* A, double** B, int m, int n)
214 for (int i = 0; i < m; i++) {
215 for (int k = 0; k < n; k++)
216 B[i][k] = A[i][k];
221 // #pragma mark - algorithms
223 #if 0
224 static void
225 print_system(double** a, int n, double* b)
227 for (int i = 0; i < n; i++) {
228 for (int j = 0; j < n; j++) {
229 printf("%.1f ", a[i][j]);
231 printf("= %f\n", b[i]);
234 #endif
237 static bool
238 solve(double** a, int n, double* b)
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;
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;
260 if (fuzzy_equals(pivotValue, 0)) {
261 TRACE_ERROR("solve(): matrix is not regular\n");
262 return false;
265 if (pivot != i) {
266 swap(indices[i], indices[pivot]);
267 swap(b[i], b[pivot]);
269 pivot = indices[i];
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;
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];
289 if (fuzzy_equals(a[index][i], 0))
290 return false;
291 b[i] = sum / a[index][i];
294 return true;
299 compute_dependencies(double** a, int m, int n,
300 bool* independent)
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;
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;
326 if (!fuzzy_equals(pivotValue, 0))
327 break;
329 column++;
330 } while (column < n);
332 if (column == n)
333 break;
335 if (pivot != i)
336 swap(indices[i], indices[pivot]);
337 pivot = indices[i];
339 independent[pivot] = true;
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;
350 column++;
353 for (int j = i; j < m; j++)
354 independent[indices[j]] = false;
356 return i;
360 // remove_linearly_dependent_rows
361 static int
362 remove_linearly_dependent_rows(double** A, double** temp,
363 bool* independentRows, int m, int n)
365 // copy to temp
366 copy_matrix(A, temp, m, n);
368 int count = compute_dependencies(temp, m, n, independentRows);
369 if (count == m)
370 return count;
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];
380 index++;
384 return count;
388 /*! QR decomposition using Householder transformations.
390 static bool
391 qr_decomposition(double** a, int m, int n, double* d, double** q)
393 if (m < n)
394 return false;
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;
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;
411 double beta = 1 / (alpha * a[j][j] - innerProduct);
413 // u = x - alpha * e_1
414 // (u is a[j..n][j])
415 a[j][j] -= alpha;
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;
425 for (int i = j; i < m; i++)
426 a[i][k] += a[i][j] * sum;
429 // v = u/|u|
430 innerProductU += a[j][j] * a[j][j];
431 double beta2 = -2 / innerProductU;
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];
441 q[k][k] += 1;
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;
450 for (int i = j; i < m; i++)
451 q[k][i] += sum * a[i][j];
456 return true;
460 // MatrixDeleter
461 struct MatrixDelete {
462 inline void operator()(double** matrix)
464 free_matrix(matrix);
467 typedef BPrivate::AutoDeleter<double*, MatrixDelete> MatrixDeleter;
470 // #pragma mark - LayoutOptimizer
473 // constructor
474 LayoutOptimizer::LayoutOptimizer(const ConstraintList& list,
475 int32 variableCount)
477 fVariableCount(0),
478 fTemp1(NULL),
479 fTemp2(NULL),
480 fZtrans(NULL),
481 fQ(NULL),
482 fSoftConstraints(NULL),
483 fG(NULL),
484 fDesired(NULL)
486 SetConstraints(list, variableCount);
490 // destructor
491 LayoutOptimizer::~LayoutOptimizer()
493 _MakeEmpty();
497 bool
498 LayoutOptimizer::SetConstraints(const ConstraintList& list, int32 variableCount)
500 fConstraints = (ConstraintList*)&list;
501 int32 constraintCount = fConstraints->CountItems();
503 if (fVariableCount != variableCount) {
504 _MakeEmpty();
505 _Init(variableCount, constraintCount);
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;
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;
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;
540 // create G
541 transpose_matrix(fSoftConstraints, fTemp1, constraintCount, fVariableCount);
542 multiply_matrices(fTemp1, fSoftConstraints, fG, fVariableCount,
543 constraintCount, fVariableCount);
545 // create d
546 multiply_matrix_vector(fTemp1, rightSide, fVariableCount, constraintCount,
547 fDesired);
548 negate_vector(fDesired, fVariableCount);
550 return true;
554 // InitCheck
555 status_t
556 LayoutOptimizer::InitCheck() const
558 if (!fTemp1 || !fTemp2 || !fZtrans || !fQ || !fSoftConstraints || !fG
559 || !fDesired)
560 return B_NO_MEMORY;
561 return B_OK;
565 double
566 LayoutOptimizer::_ActualValue(Constraint* constraint, double* values) const
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();
575 if (constraint->Op() == LinearProgramming::kLE)
576 return -value;
577 return value;
581 double
582 LayoutOptimizer::_RightSide(Constraint* constraint)
584 if (constraint->Op() == LinearProgramming::kLE)
585 return -constraint->RightSide();
586 return constraint->RightSide();
590 void
591 LayoutOptimizer::_MakeEmpty()
593 free_matrix(fTemp1);
594 free_matrix(fTemp2);
595 free_matrix(fZtrans);
596 free_matrix(fSoftConstraints);
597 free_matrix(fQ);
598 free_matrix(fG);
600 delete[] fDesired;
604 void
605 LayoutOptimizer::_Init(int32 variableCount, int32 nConstraints)
607 fVariableCount = variableCount;
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);
617 fDesired = new(std::nothrow) double[fVariableCount];
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.
629 bool
630 LayoutOptimizer::Solve(double* values)
632 if (values == NULL)
633 return false;
635 int32 constraintCount = fConstraints->CountItems();
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;
645 bool success = _Solve(values);
646 return success;
650 // _Solve
651 bool
652 LayoutOptimizer::_Solve(double* values)
654 int32 constraintCount = fConstraints->CountItems();
656 TRACE_ONLY(
657 TRACE("constraints:\n");
658 for (int32 i = 0; i < constraintCount; i++) {
659 TRACE(" %-2ld: ", i);
660 fConstraints->ItemAt(i)->PrintToStream();
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
669 // init our initial x
670 double x[fVariableCount];
671 for (int i = 0; i < fVariableCount; i++)
672 x[i] = values[i];
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];
681 // init active set
682 ConstraintList activeConstraints(constraintCount);
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);
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();
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
717 int32 activeCount = activeConstraints.CountItems();
718 if (activeCount == 0) {
719 TRACE_ERROR("Solve(): Error: No more active constraints!\n");
720 return false;
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);
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();
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);
749 // gxd = G * x + d
750 double gxd[fVariableCount];
751 multiply_matrix_vector(fG, x, fVariableCount, fVariableCount, gxd);
752 add_vectors(gxd, d, fVariableCount);
754 double p[fVariableCount];
755 if (!_SolveSubProblem(gxd, am, p))
756 return false;
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
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;
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];
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;
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;
813 index++;
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;
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;
835 double divider = _ActualValue(constraint, p);
836 if (divider > 0 || fuzzy_equals(divider, 0))
837 continue;
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;
848 TRACE("alpha: %f, barrier: %d\n", alpha, barrier);
850 if (alpha < 1)
851 activeConstraints.AddItem(fConstraints->ItemAt(barrier));
853 // x += p * alpha;
854 add_vectors_scaled(x, p, alpha, fVariableCount);
860 bool
861 LayoutOptimizer::_SolveSubProblem(const double* d, int am, double* p)
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
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.
877 const int an = fVariableCount;
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;
889 // Z is the (1, m + 1) minor of Q
890 const int zm = an;
891 const int zn = an - am;
893 double* Z[zm];
894 for (int i = 0; i < zm; i++)
895 Z[i] = Q[i] + am;
897 // solve (Z^TGZ)p_Z = -Z^Td
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);
906 // fTemp2 = Ztrans * G * Z
907 multiply_matrices(fG, Z, fTemp1, zm, fVariableCount, zn);
908 multiply_matrices(fZtrans, fTemp1, fTemp2, zn, zm, zn);
910 success = solve(fTemp2, zn, pz);
911 if (!success) {
912 TRACE_ERROR("Solve(): Failed to solve() system for p_Z\n");
913 return false;
916 // p = Z * pz;
917 multiply_matrix_vector(Z, pz, zm, zn, p);
919 return true;
923 // _SetResult
924 void
925 LayoutOptimizer::_SetResult(const double* x, double* values)
927 for (int i = 0; i < fVariableCount; i++)
928 values[i] = x[i];