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