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 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 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 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 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** 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 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 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 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 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 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 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 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 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 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 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 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 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 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 { 484 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 { 494 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 504 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 514 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 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 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 564 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* 574 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 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 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 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 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 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 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 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