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