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 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 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 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 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 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** 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 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 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 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 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 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 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 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 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 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 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 { 462 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 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 491 LayoutOptimizer::~LayoutOptimizer() 492 { 493 _MakeEmpty(); 494 } 495 496 497 bool 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 556 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 566 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 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 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 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 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 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 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 925 LayoutOptimizer::_SetResult(const double* x, double* values) 926 { 927 for (int i = 0; i < fVariableCount; i++) 928 values[i] = x[i]; 929 } 930