xref: /haiku/src/libs/linprog/LayoutOptimizer.cpp (revision b46615c55ad2c8fe6de54412055a0713da3d610a)
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 
263 bool
264 solve(double** a, int n, double* b)
265 {
266 	// index array for row permutation
267 	// Note: We could eliminate it, if we would permutate the row pointers of a.
268 	int indices[n];
269 	for (int i = 0; i < n; i++)
270 		indices[i] = i;
271 
272 	// forward elimination
273 	for (int i = 0; i < n - 1; i++) {
274 		// find pivot
275 		int pivot = i;
276 		double pivotValue = fabs(a[indices[i]][i]);
277 		for (int j = i + 1; j < n; j++) {
278 			int index = indices[j];
279 			double value = fabs(a[index][i]);
280 			if (value > pivotValue) {
281 				pivot = j;
282 				pivotValue = value;
283 			}
284 		}
285 
286 		if (fuzzy_equals(pivotValue, 0)) {
287 			TRACE_ERROR("solve(): matrix is not regular\n");
288 			return false;
289 		}
290 
291 		if (pivot != i) {
292 			swap(indices[i], indices[pivot]);
293 			swap(b[i], b[pivot]);
294 		}
295 		pivot = indices[i];
296 
297 		// eliminate
298 		for (int j = i + 1; j < n; j++) {
299 			int index = indices[j];
300 			double q = -a[index][i] / a[pivot][i];
301 			a[index][i] = 0;
302 			for (int k = i + 1; k < n; k++)
303 				a[index][k] += a[pivot][k] * q;
304 			b[j] += b[i] * q;
305 		}
306 	}
307 
308 	// backwards substitution
309 	for (int i = n - 1; i >= 0; i--) {
310 		int index = indices[i];
311 		double sum = b[i];
312 		for (int j = i + 1; j < n; j++)
313 			sum -= a[index][j] * b[j];
314 
315 		b[i] = sum / a[index][i];
316 	}
317 
318 	return true;
319 }
320 
321 
322 int
323 compute_dependencies(double** a, int m, int n,
324 	bool* independent)
325 {
326 	// index array for row permutation
327 	// Note: We could eliminate it, if we would permutate the row pointers of a.
328 	int indices[m];
329 	for (int i = 0; i < m; i++)
330 		indices[i] = i;
331 
332 	// forward elimination
333 	int iterations = (m > n ? n : m);
334 	int i = 0;
335 	int column = 0;
336 	for (; i < iterations && column < n; i++) {
337 		// find next pivot
338 		int pivot = i;
339 		do {
340 			double pivotValue = fabs(a[indices[i]][column]);
341 			for (int j = i + 1; j < m; j++) {
342 				int index = indices[j];
343 				double value = fabs(a[index][column]);
344 				if (value > pivotValue) {
345 					pivot = j;
346 					pivotValue = value;
347 				}
348 			}
349 
350 			if (!fuzzy_equals(pivotValue, 0))
351 				break;
352 
353 			column++;
354 		} while (column < n);
355 
356 		if (column == n)
357 			break;
358 
359 		if (pivot != i)
360 			swap(indices[i], indices[pivot]);
361 		pivot = indices[i];
362 
363 		independent[pivot] = true;
364 
365 		// eliminate
366 		for (int j = i + 1; j < m; j++) {
367 			int index = indices[j];
368 			double q = -a[index][column] / a[pivot][column];
369 			a[index][column] = 0;
370 			for (int k = column + 1; k < n; k++)
371 				a[index][k] += a[pivot][k] * q;
372 		}
373 
374 		column++;
375 	}
376 
377 	for (int j = i; j < m; j++)
378 		independent[indices[j]] = false;
379 
380 	return i;
381 }
382 
383 
384 // remove_linearly_dependent_rows
385 int
386 remove_linearly_dependent_rows(double** A, double** temp,
387 	bool* independentRows, int m, int n)
388 {
389 	// copy to temp
390 	copy_matrix(A, temp, m, n);
391 
392 	int count = compute_dependencies(temp, m, n, independentRows);
393 	if (count == m)
394 		return count;
395 
396 	// remove the rows
397 	int index = 0;
398 	for (int i = 0; i < m; i++) {
399 		if (independentRows[i]) {
400 			if (index < i) {
401 				for (int k = 0; k < n; k++)
402 					A[index][k] = A[i][k];
403 			}
404 			index++;
405 		}
406 	}
407 
408 	return count;
409 }
410 
411 
412 /*!	QR decomposition using Householder transformations.
413 */
414 bool
415 qr_decomposition(double** a, int m, int n, double* d, double** q)
416 {
417 	if (m < n)
418 		return false;
419 
420 	for (int j = 0; j < n; j++) {
421 		// inner product of the first vector x of the (j,j) minor
422 		double innerProductU = 0;
423 		for (int i = j + 1; i < m; i++)
424 			innerProductU = innerProductU + a[i][j] * a[i][j];
425 		double innerProduct = innerProductU + a[j][j] * a[j][j];
426 		if (fuzzy_equals(innerProduct, 0)) {
427 			TRACE_ERROR("qr_decomposition(): 0 column %d\n", j);
428 			return false;
429 		}
430 
431 		// alpha (norm of x with opposite signedness of x_1) and thus r_{j,j}
432 		double alpha = (a[j][j] < 0 ? sqrt(innerProduct) : -sqrt(innerProduct));
433 		d[j] = alpha;
434 
435 		double beta = 1 / (alpha * a[j][j] - innerProduct);
436 
437 		// u = x - alpha * e_1
438 		// (u is a[j..n][j])
439 		a[j][j] -= alpha;
440 
441 		// left-multiply A_k with Q_k, thus obtaining a row of R and the A_{k+1}
442 		// for the next iteration
443 		for (int k = j + 1; k < n; k++) {
444 			double sum = 0;
445 			for (int i = j; i < m; i++)
446 				sum += a[i][j] * a[i][k];
447 			sum *= beta;
448 
449 			for (int i = j; i < m; i++)
450 				a[i][k] += a[i][j] * sum;
451 		}
452 
453 		// v = u/|u|
454 		innerProductU += a[j][j] * a[j][j];
455 		double beta2 = -2 / innerProductU;
456 
457 		// right-multiply Q with Q_k
458 		// Q_k = I - 2vv^T
459 		// Q * Q_k = Q - 2 * Q * vv^T
460 		if (j == 0) {
461 			for (int k = 0; k < m; k++) {
462 				for (int i = 0; i < m; i++)
463 					q[k][i] = beta2 * a[k][0] * a[i][0];
464 
465 				q[k][k] += 1;
466 			}
467 		} else {
468 			for (int k = 0; k < m; k++) {
469 				double sum = 0;
470 				for (int i = j; i < m; i++)
471 					sum += q[k][i] * a[i][j];
472 				sum *= beta2;
473 
474 				for (int i = j; i < m; i++)
475 					q[k][i] += sum * a[i][j];
476 			}
477 		}
478 	}
479 
480 	return true;
481 }
482 
483 
484 // MatrixDeleter
485 struct MatrixDelete {
486 	inline void operator()(double** matrix)
487 	{
488 		free_matrix(matrix);
489 	}
490 };
491 typedef BPrivate::AutoDeleter<double*, MatrixDelete> MatrixDeleter;
492 
493 
494 // #pragma mark - LayoutOptimizer
495 
496 
497 // constructor
498 LayoutOptimizer::LayoutOptimizer(const ConstraintList& list,
499 	int32 variableCount)
500 	:
501 	fVariableCount(0),
502 	fTemp1(NULL),
503 	fTemp2(NULL),
504 	fZtrans(NULL),
505 	fQ(NULL),
506 	fSoftConstraints(NULL),
507 	fG(NULL),
508 	fDesired(NULL)
509 {
510 	SetConstraints(list, variableCount);
511 }
512 
513 
514 // destructor
515 LayoutOptimizer::~LayoutOptimizer()
516 {
517 	_MakeEmpty();
518 }
519 
520 
521 bool
522 LayoutOptimizer::SetConstraints(const ConstraintList& list, int32 variableCount)
523 {
524 	fConstraints = (ConstraintList*)&list;
525 	int32 constraintCount = fConstraints->CountItems();
526 
527 	if (fVariableCount != variableCount) {
528 		_MakeEmpty();
529 		_Init(variableCount, constraintCount);
530 	}
531 
532 	zero_matrix(fSoftConstraints, constraintCount, fVariableCount);
533 	double rightSide[constraintCount];
534 	// set up soft constraint matrix
535 	for (int32 c = 0; c < fConstraints->CountItems(); c++) {
536 		Constraint* constraint = fConstraints->ItemAt(c);
537 		if (!constraint->IsSoft()) {
538 			rightSide[c] = 0;
539 			continue;
540 		}
541 		double weight = 0;
542 		double negPenalty = constraint->PenaltyNeg();
543 		if (negPenalty > 0)
544 			weight += negPenalty;
545 		double posPenalty = constraint->PenaltyPos();
546 		if (posPenalty > 0)
547 			weight += posPenalty;
548 		if (negPenalty > 0 && posPenalty > 0)
549 			weight /= 2;
550 
551 		rightSide[c] = _RightSide(constraint) * weight;
552 		SummandList* summands = constraint->LeftSide();
553 		for (int32 s = 0; s < summands->CountItems(); s++) {
554 			Summand* summand = summands->ItemAt(s);
555 			int32 variable = summand->Var()->Index();
556 			if (constraint->Op() == LinearProgramming::kLE)
557 				fSoftConstraints[c][variable] = -summand->Coeff();
558 			else
559 				fSoftConstraints[c][variable] = summand->Coeff();
560 			fSoftConstraints[c][variable] *= weight;
561 		}
562 	}
563 
564 	// create G
565 	transpose_matrix(fSoftConstraints, fTemp1, constraintCount, fVariableCount);
566 	multiply_matrices(fTemp1, fSoftConstraints, fG, fVariableCount,
567 		constraintCount, constraintCount);
568 
569 	// create d
570 	multiply_matrix_vector(fTemp1, rightSide, fVariableCount, constraintCount,
571 		fDesired);
572 	negate_vector(fDesired, fVariableCount);
573 
574 	return true;
575 }
576 
577 
578 // InitCheck
579 status_t
580 LayoutOptimizer::InitCheck() const
581 {
582 	if (!fTemp1 || !fTemp2 || !fZtrans || !fQ || !fSoftConstraints || !fG
583 		|| !fDesired)
584 		return B_NO_MEMORY;
585 	return B_OK;
586 }
587 
588 
589 double
590 LayoutOptimizer::_ActualValue(Constraint* constraint, double* values) const
591 {
592 	SummandList* summands = constraint->LeftSide();
593 	double value = 0;
594 	for (int32 s = 0; s < summands->CountItems(); s++) {
595 		Summand* summand = summands->ItemAt(s);
596 		int32 variable = summand->Var()->Index();
597 		value += values[variable] * summand->Coeff();
598 	}
599 	if (constraint->Op() == LinearProgramming::kLE)
600 		return -value;
601 	return value;
602 }
603 
604 
605 double
606 LayoutOptimizer::_RightSide(Constraint* constraint)
607 {
608 	if (constraint->Op() == LinearProgramming::kLE)
609 		return -constraint->RightSide();
610 	return constraint->RightSide();
611 }
612 
613 
614 void
615 LayoutOptimizer::_MakeEmpty()
616 {
617 	free_matrix(fTemp1);
618 	free_matrix(fTemp2);
619 	free_matrix(fZtrans);
620 	free_matrix(fSoftConstraints);
621 	free_matrix(fQ);
622 	free_matrix(fG);
623 
624 	delete[] fDesired;
625 }
626 
627 
628 void
629 LayoutOptimizer::_Init(int32 variableCount, int32 nConstraints)
630 {
631 	fVariableCount = variableCount;
632 
633 	fTemp1 = allocate_matrix(nConstraints, nConstraints);
634 	fTemp2 = allocate_matrix(nConstraints, nConstraints);
635 	fZtrans = allocate_matrix(nConstraints, fVariableCount);
636 	fSoftConstraints = allocate_matrix(nConstraints, fVariableCount);
637 	fQ = allocate_matrix(nConstraints, fVariableCount);
638 	fG = allocate_matrix(nConstraints, nConstraints);
639 
640 	fDesired = new(std::nothrow) double[fVariableCount];
641 }
642 
643 
644 // Solve
645 /*!	Solves the quadratic program (QP) given by the constraints added via
646 	AddConstraint(), the additional constraint \sum_{i=0}^{n-1} x_i = size,
647 	and the optimization criterion to minimize
648 	\sum_{i=0}^{n-1} (x_i - desired[i])^2.
649 	The \a values array must contain a feasible solution when called and will
650 	be overwritten with the optimial solution the method computes.
651 */
652 bool
653 LayoutOptimizer::Solve(double* values)
654 {
655 	if (values == NULL)
656 		return false;
657 
658 	int32 constraintCount = fConstraints->CountItems();
659 
660 	// allocate the active constraint matrix and its transposed matrix
661 	fActiveMatrix = allocate_matrix(constraintCount, fVariableCount);
662 	fActiveMatrixTemp = allocate_matrix(constraintCount, fVariableCount);
663 	MatrixDeleter _(fActiveMatrix);
664 	MatrixDeleter _2(fActiveMatrixTemp);
665 	if (!fActiveMatrix || !fActiveMatrixTemp)
666 		return false;
667 
668 	bool success = _Solve(values);
669 	return success;
670 }
671 
672 
673 // _Solve
674 bool
675 LayoutOptimizer::_Solve(double* values)
676 {
677 	int32 constraintCount = fConstraints->CountItems();
678 
679 TRACE_ONLY(
680 	TRACE("constraints:\n");
681 	for (int32 i = 0; i < constraintCount; i++) {
682 		TRACE(" %-2ld:  ", i);
683 		fConstraints->ItemAt(i)->PrintToStream();
684 	}
685 )
686 
687 	// our QP is supposed to be in this form:
688 	//   min_x 1/2x^TGx + x^Td
689 	//   s.t. a_i^Tx = b_i,  i \in E
690 	//        a_i^Tx >= b_i, i \in I
691 
692 	// init our initial x
693 	double x[fVariableCount];
694 	for (int i = 0; i < fVariableCount; i++)
695 		x[i] = values[i];
696 
697 	// init d
698 	// Note that the values of d and of G result from rewriting the
699 	// ||x - desired|| we actually want to minimize.
700 	double d[fVariableCount];
701 	for (int i = 0; i < fVariableCount; i++)
702 		d[i] = fDesired[i];
703 
704 	// init active set
705 	ConstraintList activeConstraints(constraintCount);
706 
707 	for (int32 i = 0; i < constraintCount; i++) {
708 		Constraint* constraint = fConstraints->ItemAt(i);
709 		if (constraint->IsSoft())
710 			continue;
711 		double actualValue = _ActualValue(constraint, x);
712 		TRACE("constraint %ld: actual: %f  constraint: %f\n", i, actualValue,
713 			_RightSide(constraint));
714 		if (fuzzy_equals(actualValue, _RightSide(constraint)))
715 			activeConstraints.AddItem(constraint);
716 	}
717 
718 	// The main loop: Each iteration we try to get closer to the optimum
719 	// solution. We compute a vector p that brings our x closer to the optimum.
720 	// We do that by computing the QP resulting from our active constraint set,
721 	// W^k. Afterward each iteration we adjust the active set.
722 TRACE_ONLY(int iteration = 0;)
723 	while (true) {
724 TRACE_ONLY(
725 		TRACE("\n[iteration %d]\n", iteration++);
726 		TRACE("active set:\n");
727 		for (int32 i = 0; i < activeConstraints.CountItems(); i++) {
728 			TRACE("  ");
729 			activeConstraints.ItemAt(i)->PrintToStream();
730 		}
731 )
732 
733 		// solve the QP:
734 		//   min_p 1/2p^TGp + g_k^Tp
735 		//   s.t. a_i^Tp = 0
736 		//   with a_i \in activeConstraints
737 		//        g_k = Gx_k + d
738 		//        p = x - x_k
739 
740 		int32 activeCount = activeConstraints.CountItems();
741 		if (activeCount == 0) {
742 			TRACE_ERROR("Solve(): Error: No more active constraints!\n");
743 			return false;
744 		}
745 
746 		// construct a matrix from the active constraints
747 		int am = activeCount;
748 		const int an = fVariableCount;
749 		bool independentRows[activeCount];
750 		zero_matrix(fActiveMatrix, am, an);
751 
752 		for (int32 i = 0; i < activeCount; i++) {
753 			Constraint* constraint = activeConstraints.ItemAt(i);
754 			if (constraint->IsSoft())
755 				continue;
756 			SummandList* summands = constraint->LeftSide();
757 			for (int32 s = 0; s < summands->CountItems(); s++) {
758 				Summand* summand = summands->ItemAt(s);
759 				int32 variable = summand->Var()->Index();
760 				if (constraint->Op() == LinearProgramming::kLE)
761 					fActiveMatrix[i][variable] = -summand->Coeff();
762 				else
763 					fActiveMatrix[i][variable] = summand->Coeff();
764 			}
765 		}
766 
767 // TODO: The fActiveMatrix is sparse (max 2 entries per row). There should be
768 // some room for optimization.
769 		am = remove_linearly_dependent_rows(fActiveMatrix, fActiveMatrixTemp,
770 			independentRows, am, an);
771 
772 		// gxd = G * x + d
773 		double gxd[fVariableCount];
774 		multiply_matrix_vector(fG, x, fVariableCount, fVariableCount, gxd);
775 		add_vectors(gxd, d, fVariableCount);
776 
777 		double p[fVariableCount];
778 		if (!_SolveSubProblem(gxd, am, p))
779 			return false;
780 
781 		if (is_zero(p, fVariableCount)) {
782 			// compute Lagrange multipliers lambda_i
783 			// if lambda_i >= 0 for all i \in W^k \union inequality constraints,
784 			// then we're done.
785 			// Otherwise remove the constraint with the smallest lambda_i
786 			// from the active set.
787 			// The derivation of the Lagrangian yields:
788 			//   \sum_{i \in W^k}(lambda_ia_i) = Gx_k + d
789 			// Which is an system we can solve:
790 			//   A^Tlambda = Gx_k + d
791 
792 			// A^T is over-determined, hence we need to reduce the number of
793 			// rows before we can solve it.
794 			bool independentColumns[an];
795 			double** aa = fTemp1;
796 			transpose_matrix(fActiveMatrix, aa, am, an);
797 			const int aam = remove_linearly_dependent_rows(aa, fTemp2,
798 				independentColumns, an, am);
799 			const int aan = am;
800 			if (aam != aan) {
801 				// This should not happen, since A has full row rank.
802 				TRACE_ERROR("Solve(): Transposed A has less linear independent "
803 					"rows than it has columns!\n");
804 				return false;
805 			}
806 
807 			// also reduce the number of rows on the right hand side
808 			double lambda[aam];
809 			int index = 0;
810 			for (int i = 0; i < an; i++) {
811 				if (independentColumns[i])
812 					lambda[index++] = gxd[i];
813 			}
814 
815 			bool success = solve(aa, aam, lambda);
816 			if (!success) {
817 				// Impossible, since we've removed all linearly dependent rows.
818 				TRACE_ERROR("Solve(): Failed to compute lambda!\n");
819 				return false;
820 			}
821 
822 			// find min lambda_i (only, if it's < 0, though)
823 			double minLambda = 0;
824 			int minIndex = -1;
825 			index = 0;
826 			for (int i = 0; i < activeCount; i++) {
827 				if (independentRows[i]) {
828 					Constraint* constraint = activeConstraints.ItemAt(i);
829 					if (constraint->Op() != LinearProgramming::kEQ) {
830 						if (lambda[index] < minLambda) {
831 							minLambda = lambda[index];
832 							minIndex = i;
833 						}
834 					}
835 
836 					index++;
837 				}
838 			}
839 
840 			// if the min lambda is >= 0, we're done
841 			if (minIndex < 0 || fuzzy_equals(minLambda, 0)) {
842 				_SetResult(x, values);
843 				return true;
844 			}
845 
846 			// remove i from the active set
847 			activeConstraints.RemoveItemAt(minIndex);
848 		} else {
849 			// compute alpha_k
850 			double alpha = 1;
851 			int barrier = -1;
852 			// if alpha_k < 1, add a barrier constraint to W^k
853 			for (int32 i = 0; i < constraintCount; i++) {
854 				Constraint* constraint = fConstraints->ItemAt(i);
855 				if (activeConstraints.HasItem(constraint))
856 					continue;
857 
858 				double divider = _ActualValue(constraint, p);
859 				if (divider > 0 || fuzzy_equals(divider, 0))
860 					continue;
861 
862 				// (b_i - a_i^Tx_k) / a_i^Tp_k
863 				double alphaI = _RightSide(constraint)
864 					- _ActualValue(constraint, x);
865 				alphaI /= divider;
866 				if (alphaI < alpha) {
867 					alpha = alphaI;
868 					barrier = i;
869 				}
870 			}
871 			TRACE("alpha: %f, barrier: %d\n", alpha, barrier);
872 
873 			if (alpha < 1)
874 				activeConstraints.AddItem(fConstraints->ItemAt(barrier));
875 
876 			// x += p * alpha;
877 			add_vectors_scaled(x, p, alpha, fVariableCount);
878 		}
879 	}
880 }
881 
882 
883 bool
884 LayoutOptimizer::_SolveSubProblem(const double* d, int am, double* p)
885 {
886 	// We have to solve the QP subproblem:
887 	//   min_p 1/2p^TGp + d^Tp
888 	//   s.t. a_i^Tp = 0
889 	//   with a_i \in activeConstraints
890 	//
891 	// We use the null space method, i.e. we find matrices Y and Z, such that
892 	// AZ = 0 and [Y Z] is regular. Then with
893 	//   p = Yp_Y + Zp_z
894 	// we get
895 	//   p_Y = 0
896 	// and
897 	//  (Z^TGZ)p_Z = -(Z^TYp_Y + Z^Tg) = -Z^Td
898 	// which is a linear equation system, which we can solve.
899 
900 	const int an = fVariableCount;
901 
902 	// we get Y and Z by QR decomposition of A^T
903 	double tempD[am];
904 	double** const Q = fQ;
905 	transpose_matrix(fActiveMatrix, fTemp1, am, an);
906 	bool success = qr_decomposition(fTemp1, an, am, tempD, Q);
907 	if (!success) {
908 		TRACE_ERROR("Solve(): QR decomposition failed!\n");
909 		return false;
910 	}
911 
912 	// Z is the (1, m + 1) minor of Q
913 	const int zm = an;
914 	const int zn = an - am;
915 
916 	double* Z[zm];
917 	for (int i = 0; i < zm; i++)
918 		Z[i] = Q[i] + am;
919 
920 	// solve (Z^TGZ)p_Z = -Z^Td
921 
922 	// Z^T
923 	transpose_matrix(Z, fZtrans, zm, zn);
924 	// rhs: -Z^T * d;
925 	double pz[zm];
926 	multiply_matrix_vector(fZtrans, d, zn, zm, pz);
927 	negate_vector(pz, zn);
928 
929 	// fTemp2 = Ztrans * G * Z
930 	multiply_matrices(fG, Z, fTemp1, zm, fVariableCount, zn);
931 	multiply_matrices(fZtrans, fTemp1, fTemp2, zn, zm, zn);
932 
933 	success = solve(fTemp2, zn, pz);
934 	if (!success) {
935 		TRACE_ERROR("Solve(): Failed to solve() system for p_Z\n");
936 		return false;
937 	}
938 
939 	// p = Z * pz;
940 	multiply_matrix_vector(Z, pz, zm, zn, p);
941 
942 	return true;
943 }
944 
945 
946 // _SetResult
947 void
948 LayoutOptimizer::_SetResult(const double* x, double* values)
949 {
950 	for (int i = 1; i < fVariableCount; i++)
951 		values[i] = x[i];
952 }
953