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