xref: /haiku/src/libs/linprog/LayoutOptimizer.cpp (revision 25a7b01d15612846f332751841da3579db313082)
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 
is_soft(Constraint * constraint)33 bool 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
81 static inline bool
is_zero(double * x,int n)82 is_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
94 static inline void
add_vectors(double * x,const double * y,int n)95 add_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
103 static inline void
add_vectors_scaled(double * x,const double * y,double scalar,int n)104 add_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
112 static inline void
negate_vector(double * x,int n)113 negate_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
121 double**
allocate_matrix(int m,int n)122 allocate_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
143 void
free_matrix(double ** matrix)144 free_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 */
157 static inline void
multiply_matrix_vector(const double * const * A,const double * x,int m,int n,double * y)158 multiply_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 */
173 static void
multiply_matrices(const double * const * a,const double * const * b,double ** c,int m,int n,int l)174 multiply_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
189 static inline void
transpose_matrix(const double * const * A,double ** Atrans,int m,int n)190 transpose_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
200 void
zero_matrix(double ** A,int m,int n)201 zero_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
211 void
copy_matrix(const double * const * A,double ** B,int m,int n)212 copy_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
224 static void
225 print_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 
237 static bool
solve(double ** a,int n,double * b)238 solve(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 
298 int
compute_dependencies(double ** a,int m,int n,bool * independent)299 compute_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
361 static int
remove_linearly_dependent_rows(double ** A,double ** temp,bool * independentRows,int m,int n)362 remove_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 */
390 static bool
qr_decomposition(double ** a,int m,int n,double * d,double ** q)391 qr_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
461 struct MatrixDelete {
operator ()MatrixDelete462 	inline void operator()(double** matrix)
463 	{
464 		free_matrix(matrix);
465 	}
466 };
467 typedef BPrivate::AutoDeleter<double*, MatrixDelete> MatrixDeleter;
468 
469 
470 // #pragma mark - LayoutOptimizer
471 
472 
473 // constructor
LayoutOptimizer(const ConstraintList & list,int32 variableCount)474 LayoutOptimizer::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
~LayoutOptimizer()491 LayoutOptimizer::~LayoutOptimizer()
492 {
493 	_MakeEmpty();
494 }
495 
496 
497 bool
SetConstraints(const ConstraintList & list,int32 variableCount)498 LayoutOptimizer::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
555 status_t
InitCheck() const556 LayoutOptimizer::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 
565 double
_ActualValue(Constraint * constraint,double * values) const566 LayoutOptimizer::_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 
581 double
_RightSide(Constraint * constraint)582 LayoutOptimizer::_RightSide(Constraint* constraint)
583 {
584 	if (constraint->Op() == LinearProgramming::kLE)
585 		return -constraint->RightSide();
586 	return constraint->RightSide();
587 }
588 
589 
590 void
_MakeEmpty()591 LayoutOptimizer::_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 
604 void
_Init(int32 variableCount,int32 nConstraints)605 LayoutOptimizer::_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 */
629 bool
Solve(double * values)630 LayoutOptimizer::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
651 bool
_Solve(double * values)652 LayoutOptimizer::_Solve(double* values)
653 {
654 	int32 constraintCount = fConstraints->CountItems();
655 
656 TRACE_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.
699 TRACE_ONLY(int iteration = 0;)
700 	while (true) {
701 TRACE_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 
860 bool
_SolveSubProblem(const double * d,int am,double * p)861 LayoutOptimizer::_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
924 void
_SetResult(const double * x,double * values)925 LayoutOptimizer::_SetResult(const double* x, double* values)
926 {
927 	for (int i = 0; i < fVariableCount; i++)
928 		values[i] = x[i];
929 }
930