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