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