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 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 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 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 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 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 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 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 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 fVariableCount(0), 502 fTemp1(NULL), 503 fTemp2(NULL), 504 fZtrans(NULL), 505 fQ(NULL), 506 fSoftConstraints(NULL), 507 fG(NULL), 508 fDesired(NULL) 509 { 510 SetConstraints(list, variableCount); 511 } 512 513 514 // destructor 515 LayoutOptimizer::~LayoutOptimizer() 516 { 517 _MakeEmpty(); 518 } 519 520 521 bool 522 LayoutOptimizer::SetConstraints(const ConstraintList& list, int32 variableCount) 523 { 524 fConstraints = (ConstraintList*)&list; 525 int32 constraintCount = fConstraints->CountItems(); 526 527 if (fVariableCount != variableCount) { 528 _MakeEmpty(); 529 _Init(variableCount, constraintCount); 530 } 531 532 zero_matrix(fSoftConstraints, constraintCount, fVariableCount); 533 double rightSide[constraintCount]; 534 // set up soft constraint matrix 535 for (int32 c = 0; c < fConstraints->CountItems(); c++) { 536 Constraint* constraint = fConstraints->ItemAt(c); 537 if (!constraint->IsSoft()) { 538 rightSide[c] = 0; 539 continue; 540 } 541 double weight = 0; 542 double negPenalty = constraint->PenaltyNeg(); 543 if (negPenalty > 0) 544 weight += negPenalty; 545 double posPenalty = constraint->PenaltyPos(); 546 if (posPenalty > 0) 547 weight += posPenalty; 548 if (negPenalty > 0 && posPenalty > 0) 549 weight /= 2; 550 551 rightSide[c] = _RightSide(constraint) * weight; 552 SummandList* summands = constraint->LeftSide(); 553 for (int32 s = 0; s < summands->CountItems(); s++) { 554 Summand* summand = summands->ItemAt(s); 555 int32 variable = summand->Var()->Index(); 556 if (constraint->Op() == LinearProgramming::kLE) 557 fSoftConstraints[c][variable] = -summand->Coeff(); 558 else 559 fSoftConstraints[c][variable] = summand->Coeff(); 560 fSoftConstraints[c][variable] *= weight; 561 } 562 } 563 564 // create G 565 transpose_matrix(fSoftConstraints, fTemp1, constraintCount, fVariableCount); 566 multiply_matrices(fTemp1, fSoftConstraints, fG, fVariableCount, 567 constraintCount, constraintCount); 568 569 // create d 570 multiply_matrix_vector(fTemp1, rightSide, fVariableCount, constraintCount, 571 fDesired); 572 negate_vector(fDesired, fVariableCount); 573 574 return true; 575 } 576 577 578 // InitCheck 579 status_t 580 LayoutOptimizer::InitCheck() const 581 { 582 if (!fTemp1 || !fTemp2 || !fZtrans || !fQ || !fSoftConstraints || !fG 583 || !fDesired) 584 return B_NO_MEMORY; 585 return B_OK; 586 } 587 588 589 double 590 LayoutOptimizer::_ActualValue(Constraint* constraint, double* values) const 591 { 592 SummandList* summands = constraint->LeftSide(); 593 double value = 0; 594 for (int32 s = 0; s < summands->CountItems(); s++) { 595 Summand* summand = summands->ItemAt(s); 596 int32 variable = summand->Var()->Index(); 597 value += values[variable] * summand->Coeff(); 598 } 599 if (constraint->Op() == LinearProgramming::kLE) 600 return -value; 601 return value; 602 } 603 604 605 double 606 LayoutOptimizer::_RightSide(Constraint* constraint) 607 { 608 if (constraint->Op() == LinearProgramming::kLE) 609 return -constraint->RightSide(); 610 return constraint->RightSide(); 611 } 612 613 614 void 615 LayoutOptimizer::_MakeEmpty() 616 { 617 free_matrix(fTemp1); 618 free_matrix(fTemp2); 619 free_matrix(fZtrans); 620 free_matrix(fSoftConstraints); 621 free_matrix(fQ); 622 free_matrix(fG); 623 624 delete[] fDesired; 625 } 626 627 628 void 629 LayoutOptimizer::_Init(int32 variableCount, int32 nConstraints) 630 { 631 fVariableCount = variableCount; 632 633 fTemp1 = allocate_matrix(nConstraints, nConstraints); 634 fTemp2 = allocate_matrix(nConstraints, nConstraints); 635 fZtrans = allocate_matrix(nConstraints, fVariableCount); 636 fSoftConstraints = allocate_matrix(nConstraints, fVariableCount); 637 fQ = allocate_matrix(nConstraints, fVariableCount); 638 fG = allocate_matrix(nConstraints, nConstraints); 639 640 fDesired = new(std::nothrow) double[fVariableCount]; 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(double* values) 654 { 655 if (values == NULL) 656 return false; 657 658 int32 constraintCount = fConstraints->CountItems(); 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 bool success = _Solve(values); 669 return success; 670 } 671 672 673 // _Solve 674 bool 675 LayoutOptimizer::_Solve(double* values) 676 { 677 int32 constraintCount = fConstraints->CountItems(); 678 679 TRACE_ONLY( 680 TRACE("constraints:\n"); 681 for (int32 i = 0; i < constraintCount; i++) { 682 TRACE(" %-2ld: ", i); 683 fConstraints->ItemAt(i)->PrintToStream(); 684 } 685 ) 686 687 // our QP is supposed to be in this form: 688 // min_x 1/2x^TGx + x^Td 689 // s.t. a_i^Tx = b_i, i \in E 690 // a_i^Tx >= b_i, i \in I 691 692 // init our initial x 693 double x[fVariableCount]; 694 for (int i = 0; i < fVariableCount; i++) 695 x[i] = values[i]; 696 697 // init d 698 // Note that the values of d and of G result from rewriting the 699 // ||x - desired|| we actually want to minimize. 700 double d[fVariableCount]; 701 for (int i = 0; i < fVariableCount; i++) 702 d[i] = fDesired[i]; 703 704 // init active set 705 ConstraintList activeConstraints(constraintCount); 706 707 for (int32 i = 0; i < constraintCount; i++) { 708 Constraint* constraint = fConstraints->ItemAt(i); 709 if (constraint->IsSoft()) 710 continue; 711 double actualValue = _ActualValue(constraint, x); 712 TRACE("constraint %ld: actual: %f constraint: %f\n", i, actualValue, 713 _RightSide(constraint)); 714 if (fuzzy_equals(actualValue, _RightSide(constraint))) 715 activeConstraints.AddItem(constraint); 716 } 717 718 // The main loop: Each iteration we try to get closer to the optimum 719 // solution. We compute a vector p that brings our x closer to the optimum. 720 // We do that by computing the QP resulting from our active constraint set, 721 // W^k. Afterward each iteration we adjust the active set. 722 TRACE_ONLY(int iteration = 0;) 723 while (true) { 724 TRACE_ONLY( 725 TRACE("\n[iteration %d]\n", iteration++); 726 TRACE("active set:\n"); 727 for (int32 i = 0; i < activeConstraints.CountItems(); i++) { 728 TRACE(" "); 729 activeConstraints.ItemAt(i)->PrintToStream(); 730 } 731 ) 732 733 // solve the QP: 734 // min_p 1/2p^TGp + g_k^Tp 735 // s.t. a_i^Tp = 0 736 // with a_i \in activeConstraints 737 // g_k = Gx_k + d 738 // p = x - x_k 739 740 int32 activeCount = activeConstraints.CountItems(); 741 if (activeCount == 0) { 742 TRACE_ERROR("Solve(): Error: No more active constraints!\n"); 743 return false; 744 } 745 746 // construct a matrix from the active constraints 747 int am = activeCount; 748 const int an = fVariableCount; 749 bool independentRows[activeCount]; 750 zero_matrix(fActiveMatrix, am, an); 751 752 for (int32 i = 0; i < activeCount; i++) { 753 Constraint* constraint = activeConstraints.ItemAt(i); 754 if (constraint->IsSoft()) 755 continue; 756 SummandList* summands = constraint->LeftSide(); 757 for (int32 s = 0; s < summands->CountItems(); s++) { 758 Summand* summand = summands->ItemAt(s); 759 int32 variable = summand->Var()->Index(); 760 if (constraint->Op() == LinearProgramming::kLE) 761 fActiveMatrix[i][variable] = -summand->Coeff(); 762 else 763 fActiveMatrix[i][variable] = summand->Coeff(); 764 } 765 } 766 767 // TODO: The fActiveMatrix is sparse (max 2 entries per row). There should be 768 // some room for optimization. 769 am = remove_linearly_dependent_rows(fActiveMatrix, fActiveMatrixTemp, 770 independentRows, am, an); 771 772 // gxd = G * x + d 773 double gxd[fVariableCount]; 774 multiply_matrix_vector(fG, x, fVariableCount, fVariableCount, gxd); 775 add_vectors(gxd, d, fVariableCount); 776 777 double p[fVariableCount]; 778 if (!_SolveSubProblem(gxd, am, p)) 779 return false; 780 781 if (is_zero(p, fVariableCount)) { 782 // compute Lagrange multipliers lambda_i 783 // if lambda_i >= 0 for all i \in W^k \union inequality constraints, 784 // then we're done. 785 // Otherwise remove the constraint with the smallest lambda_i 786 // from the active set. 787 // The derivation of the Lagrangian yields: 788 // \sum_{i \in W^k}(lambda_ia_i) = Gx_k + d 789 // Which is an system we can solve: 790 // A^Tlambda = Gx_k + d 791 792 // A^T is over-determined, hence we need to reduce the number of 793 // rows before we can solve it. 794 bool independentColumns[an]; 795 double** aa = fTemp1; 796 transpose_matrix(fActiveMatrix, aa, am, an); 797 const int aam = remove_linearly_dependent_rows(aa, fTemp2, 798 independentColumns, an, am); 799 const int aan = am; 800 if (aam != aan) { 801 // This should not happen, since A has full row rank. 802 TRACE_ERROR("Solve(): Transposed A has less linear independent " 803 "rows than it has columns!\n"); 804 return false; 805 } 806 807 // also reduce the number of rows on the right hand side 808 double lambda[aam]; 809 int index = 0; 810 for (int i = 0; i < an; i++) { 811 if (independentColumns[i]) 812 lambda[index++] = gxd[i]; 813 } 814 815 bool success = solve(aa, aam, lambda); 816 if (!success) { 817 // Impossible, since we've removed all linearly dependent rows. 818 TRACE_ERROR("Solve(): Failed to compute lambda!\n"); 819 return false; 820 } 821 822 // find min lambda_i (only, if it's < 0, though) 823 double minLambda = 0; 824 int minIndex = -1; 825 index = 0; 826 for (int i = 0; i < activeCount; i++) { 827 if (independentRows[i]) { 828 Constraint* constraint = activeConstraints.ItemAt(i); 829 if (constraint->Op() != LinearProgramming::kEQ) { 830 if (lambda[index] < minLambda) { 831 minLambda = lambda[index]; 832 minIndex = i; 833 } 834 } 835 836 index++; 837 } 838 } 839 840 // if the min lambda is >= 0, we're done 841 if (minIndex < 0 || fuzzy_equals(minLambda, 0)) { 842 _SetResult(x, values); 843 return true; 844 } 845 846 // remove i from the active set 847 activeConstraints.RemoveItemAt(minIndex); 848 } else { 849 // compute alpha_k 850 double alpha = 1; 851 int barrier = -1; 852 // if alpha_k < 1, add a barrier constraint to W^k 853 for (int32 i = 0; i < constraintCount; i++) { 854 Constraint* constraint = fConstraints->ItemAt(i); 855 if (activeConstraints.HasItem(constraint)) 856 continue; 857 858 double divider = _ActualValue(constraint, p); 859 if (divider > 0 || fuzzy_equals(divider, 0)) 860 continue; 861 862 // (b_i - a_i^Tx_k) / a_i^Tp_k 863 double alphaI = _RightSide(constraint) 864 - _ActualValue(constraint, x); 865 alphaI /= divider; 866 if (alphaI < alpha) { 867 alpha = alphaI; 868 barrier = i; 869 } 870 } 871 TRACE("alpha: %f, barrier: %d\n", alpha, barrier); 872 873 if (alpha < 1) 874 activeConstraints.AddItem(fConstraints->ItemAt(barrier)); 875 876 // x += p * alpha; 877 add_vectors_scaled(x, p, alpha, fVariableCount); 878 } 879 } 880 } 881 882 883 bool 884 LayoutOptimizer::_SolveSubProblem(const double* d, int am, double* p) 885 { 886 // We have to solve the QP subproblem: 887 // min_p 1/2p^TGp + d^Tp 888 // s.t. a_i^Tp = 0 889 // with a_i \in activeConstraints 890 // 891 // We use the null space method, i.e. we find matrices Y and Z, such that 892 // AZ = 0 and [Y Z] is regular. Then with 893 // p = Yp_Y + Zp_z 894 // we get 895 // p_Y = 0 896 // and 897 // (Z^TGZ)p_Z = -(Z^TYp_Y + Z^Tg) = -Z^Td 898 // which is a linear equation system, which we can solve. 899 900 const int an = fVariableCount; 901 902 // we get Y and Z by QR decomposition of A^T 903 double tempD[am]; 904 double** const Q = fQ; 905 transpose_matrix(fActiveMatrix, fTemp1, am, an); 906 bool success = qr_decomposition(fTemp1, an, am, tempD, Q); 907 if (!success) { 908 TRACE_ERROR("Solve(): QR decomposition failed!\n"); 909 return false; 910 } 911 912 // Z is the (1, m + 1) minor of Q 913 const int zm = an; 914 const int zn = an - am; 915 916 double* Z[zm]; 917 for (int i = 0; i < zm; i++) 918 Z[i] = Q[i] + am; 919 920 // solve (Z^TGZ)p_Z = -Z^Td 921 922 // Z^T 923 transpose_matrix(Z, fZtrans, zm, zn); 924 // rhs: -Z^T * d; 925 double pz[zm]; 926 multiply_matrix_vector(fZtrans, d, zn, zm, pz); 927 negate_vector(pz, zn); 928 929 // fTemp2 = Ztrans * G * Z 930 multiply_matrices(fG, Z, fTemp1, zm, fVariableCount, zn); 931 multiply_matrices(fZtrans, fTemp1, fTemp2, zn, zm, zn); 932 933 success = solve(fTemp2, zn, pz); 934 if (!success) { 935 TRACE_ERROR("Solve(): Failed to solve() system for p_Z\n"); 936 return false; 937 } 938 939 // p = Z * pz; 940 multiply_matrix_vector(Z, pz, zm, zn, p); 941 942 return true; 943 } 944 945 946 // _SetResult 947 void 948 LayoutOptimizer::_SetResult(const double* x, double* values) 949 { 950 for (int i = 1; i < fVariableCount; i++) 951 values[i] = x[i]; 952 } 953