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 #if 0 263 static void 264 print_system(double** a, int n, double* b) 265 { 266 for (int i = 0; i < n; i++) { 267 for (int j = 0; j < n; j++) { 268 printf("%.1f ", a[i][j]); 269 } 270 printf("= %f\n", b[i]); 271 } 272 } 273 #endif 274 275 276 static bool 277 solve(double** a, int n, double* b) 278 { 279 // index array for row permutation 280 // Note: We could eliminate it, if we would permutate the row pointers of a. 281 int indices[n]; 282 for (int i = 0; i < n; i++) 283 indices[i] = i; 284 285 // forward elimination 286 for (int i = 0; i < n - 1; i++) { 287 // find pivot 288 int pivot = i; 289 double pivotValue = fabs(a[indices[i]][i]); 290 for (int j = i + 1; j < n; j++) { 291 int index = indices[j]; 292 double value = fabs(a[index][i]); 293 if (value > pivotValue) { 294 pivot = j; 295 pivotValue = value; 296 } 297 } 298 299 if (fuzzy_equals(pivotValue, 0)) { 300 TRACE_ERROR("solve(): matrix is not regular\n"); 301 return false; 302 } 303 304 if (pivot != i) { 305 swap(indices[i], indices[pivot]); 306 swap(b[i], b[pivot]); 307 } 308 pivot = indices[i]; 309 310 // eliminate 311 for (int j = i + 1; j < n; j++) { 312 int index = indices[j]; 313 double q = -a[index][i] / a[pivot][i]; 314 a[index][i] = 0; 315 for (int k = i + 1; k < n; k++) 316 a[index][k] += a[pivot][k] * q; 317 b[j] += b[i] * q; 318 } 319 } 320 321 // backwards substitution 322 for (int i = n - 1; i >= 0; i--) { 323 int index = indices[i]; 324 double sum = b[i]; 325 for (int j = i + 1; j < n; j++) 326 sum -= a[index][j] * b[j]; 327 328 if (fuzzy_equals(a[index][i], 0)) 329 return false; 330 b[i] = sum / a[index][i]; 331 } 332 333 return true; 334 } 335 336 337 int 338 compute_dependencies(double** a, int m, int n, 339 bool* independent) 340 { 341 // index array for row permutation 342 // Note: We could eliminate it, if we would permutate the row pointers of a. 343 int indices[m]; 344 for (int i = 0; i < m; i++) 345 indices[i] = i; 346 347 // forward elimination 348 int iterations = (m > n ? n : m); 349 int i = 0; 350 int column = 0; 351 for (; i < iterations && column < n; i++) { 352 // find next pivot 353 int pivot = i; 354 do { 355 double pivotValue = fabs(a[indices[i]][column]); 356 for (int j = i + 1; j < m; j++) { 357 int index = indices[j]; 358 double value = fabs(a[index][column]); 359 if (value > pivotValue) { 360 pivot = j; 361 pivotValue = value; 362 } 363 } 364 365 if (!fuzzy_equals(pivotValue, 0)) 366 break; 367 368 column++; 369 } while (column < n); 370 371 if (column == n) 372 break; 373 374 if (pivot != i) 375 swap(indices[i], indices[pivot]); 376 pivot = indices[i]; 377 378 independent[pivot] = true; 379 380 // eliminate 381 for (int j = i + 1; j < m; j++) { 382 int index = indices[j]; 383 double q = -a[index][column] / a[pivot][column]; 384 a[index][column] = 0; 385 for (int k = column + 1; k < n; k++) 386 a[index][k] += a[pivot][k] * q; 387 } 388 389 column++; 390 } 391 392 for (int j = i; j < m; j++) 393 independent[indices[j]] = false; 394 395 return i; 396 } 397 398 399 // remove_linearly_dependent_rows 400 static int 401 remove_linearly_dependent_rows(double** A, double** temp, 402 bool* independentRows, int m, int n) 403 { 404 // copy to temp 405 copy_matrix(A, temp, m, n); 406 407 int count = compute_dependencies(temp, m, n, independentRows); 408 if (count == m) 409 return count; 410 411 // remove the rows 412 int index = 0; 413 for (int i = 0; i < m; i++) { 414 if (independentRows[i]) { 415 if (index < i) { 416 for (int k = 0; k < n; k++) 417 A[index][k] = A[i][k]; 418 } 419 index++; 420 } 421 } 422 423 return count; 424 } 425 426 427 /*! QR decomposition using Householder transformations. 428 */ 429 static bool 430 qr_decomposition(double** a, int m, int n, double* d, double** q) 431 { 432 if (m < n) 433 return false; 434 435 for (int j = 0; j < n; j++) { 436 // inner product of the first vector x of the (j,j) minor 437 double innerProductU = 0; 438 for (int i = j + 1; i < m; i++) 439 innerProductU = innerProductU + a[i][j] * a[i][j]; 440 double innerProduct = innerProductU + a[j][j] * a[j][j]; 441 if (fuzzy_equals(innerProduct, 0)) { 442 TRACE_ERROR("qr_decomposition(): 0 column %d\n", j); 443 return false; 444 } 445 446 // alpha (norm of x with opposite signedness of x_1) and thus r_{j,j} 447 double alpha = (a[j][j] < 0 ? sqrt(innerProduct) : -sqrt(innerProduct)); 448 d[j] = alpha; 449 450 double beta = 1 / (alpha * a[j][j] - innerProduct); 451 452 // u = x - alpha * e_1 453 // (u is a[j..n][j]) 454 a[j][j] -= alpha; 455 456 // left-multiply A_k with Q_k, thus obtaining a row of R and the A_{k+1} 457 // for the next iteration 458 for (int k = j + 1; k < n; k++) { 459 double sum = 0; 460 for (int i = j; i < m; i++) 461 sum += a[i][j] * a[i][k]; 462 sum *= beta; 463 464 for (int i = j; i < m; i++) 465 a[i][k] += a[i][j] * sum; 466 } 467 468 // v = u/|u| 469 innerProductU += a[j][j] * a[j][j]; 470 double beta2 = -2 / innerProductU; 471 472 // right-multiply Q with Q_k 473 // Q_k = I - 2vv^T 474 // Q * Q_k = Q - 2 * Q * vv^T 475 if (j == 0) { 476 for (int k = 0; k < m; k++) { 477 for (int i = 0; i < m; i++) 478 q[k][i] = beta2 * a[k][0] * a[i][0]; 479 480 q[k][k] += 1; 481 } 482 } else { 483 for (int k = 0; k < m; k++) { 484 double sum = 0; 485 for (int i = j; i < m; i++) 486 sum += q[k][i] * a[i][j]; 487 sum *= beta2; 488 489 for (int i = j; i < m; i++) 490 q[k][i] += sum * a[i][j]; 491 } 492 } 493 } 494 495 return true; 496 } 497 498 499 // MatrixDeleter 500 struct MatrixDelete { 501 inline void operator()(double** matrix) 502 { 503 free_matrix(matrix); 504 } 505 }; 506 typedef BPrivate::AutoDeleter<double*, MatrixDelete> MatrixDeleter; 507 508 509 // #pragma mark - LayoutOptimizer 510 511 512 // constructor 513 LayoutOptimizer::LayoutOptimizer(const ConstraintList& list, 514 int32 variableCount) 515 : 516 fVariableCount(0), 517 fTemp1(NULL), 518 fTemp2(NULL), 519 fZtrans(NULL), 520 fQ(NULL), 521 fSoftConstraints(NULL), 522 fG(NULL), 523 fDesired(NULL) 524 { 525 SetConstraints(list, variableCount); 526 } 527 528 529 // destructor 530 LayoutOptimizer::~LayoutOptimizer() 531 { 532 _MakeEmpty(); 533 } 534 535 536 bool 537 LayoutOptimizer::SetConstraints(const ConstraintList& list, int32 variableCount) 538 { 539 fConstraints = (ConstraintList*)&list; 540 int32 constraintCount = fConstraints->CountItems(); 541 542 if (fVariableCount != variableCount) { 543 _MakeEmpty(); 544 _Init(variableCount, constraintCount); 545 } 546 547 zero_matrix(fSoftConstraints, constraintCount, fVariableCount); 548 double rightSide[constraintCount]; 549 // set up soft constraint matrix 550 for (int32 c = 0; c < fConstraints->CountItems(); c++) { 551 Constraint* constraint = fConstraints->ItemAt(c); 552 if (!constraint->IsSoft()) { 553 rightSide[c] = 0; 554 continue; 555 } 556 double weight = 0; 557 double negPenalty = constraint->PenaltyNeg(); 558 if (negPenalty > 0) 559 weight += negPenalty; 560 double posPenalty = constraint->PenaltyPos(); 561 if (posPenalty > 0) 562 weight += posPenalty; 563 if (negPenalty > 0 && posPenalty > 0) 564 weight /= 2; 565 566 rightSide[c] = _RightSide(constraint) * weight; 567 SummandList* summands = constraint->LeftSide(); 568 for (int32 s = 0; s < summands->CountItems(); s++) { 569 Summand* summand = summands->ItemAt(s); 570 int32 variable = summand->Var()->Index(); 571 if (constraint->Op() == LinearProgramming::kLE) 572 fSoftConstraints[c][variable] = -summand->Coeff(); 573 else 574 fSoftConstraints[c][variable] = summand->Coeff(); 575 fSoftConstraints[c][variable] *= weight; 576 } 577 } 578 579 // create G 580 transpose_matrix(fSoftConstraints, fTemp1, constraintCount, fVariableCount); 581 multiply_matrices(fTemp1, fSoftConstraints, fG, fVariableCount, 582 constraintCount, constraintCount); 583 584 // create d 585 multiply_matrix_vector(fTemp1, rightSide, fVariableCount, constraintCount, 586 fDesired); 587 negate_vector(fDesired, fVariableCount); 588 589 return true; 590 } 591 592 593 // InitCheck 594 status_t 595 LayoutOptimizer::InitCheck() const 596 { 597 if (!fTemp1 || !fTemp2 || !fZtrans || !fQ || !fSoftConstraints || !fG 598 || !fDesired) 599 return B_NO_MEMORY; 600 return B_OK; 601 } 602 603 604 double 605 LayoutOptimizer::_ActualValue(Constraint* constraint, double* values) const 606 { 607 SummandList* summands = constraint->LeftSide(); 608 double value = 0; 609 for (int32 s = 0; s < summands->CountItems(); s++) { 610 Summand* summand = summands->ItemAt(s); 611 int32 variable = summand->Var()->Index(); 612 value += values[variable] * summand->Coeff(); 613 } 614 if (constraint->Op() == LinearProgramming::kLE) 615 return -value; 616 return value; 617 } 618 619 620 double 621 LayoutOptimizer::_RightSide(Constraint* constraint) 622 { 623 if (constraint->Op() == LinearProgramming::kLE) 624 return -constraint->RightSide(); 625 return constraint->RightSide(); 626 } 627 628 629 void 630 LayoutOptimizer::_MakeEmpty() 631 { 632 free_matrix(fTemp1); 633 free_matrix(fTemp2); 634 free_matrix(fZtrans); 635 free_matrix(fSoftConstraints); 636 free_matrix(fQ); 637 free_matrix(fG); 638 639 delete[] fDesired; 640 } 641 642 643 void 644 LayoutOptimizer::_Init(int32 variableCount, int32 nConstraints) 645 { 646 fVariableCount = variableCount; 647 648 fTemp1 = allocate_matrix(nConstraints, nConstraints); 649 fTemp2 = allocate_matrix(nConstraints, nConstraints); 650 fZtrans = allocate_matrix(nConstraints, fVariableCount); 651 fSoftConstraints = allocate_matrix(nConstraints, fVariableCount); 652 fQ = allocate_matrix(nConstraints, fVariableCount); 653 fG = allocate_matrix(nConstraints, nConstraints); 654 655 fDesired = new(std::nothrow) double[fVariableCount]; 656 } 657 658 659 // Solve 660 /*! Solves the quadratic program (QP) given by the constraints added via 661 AddConstraint(), the additional constraint \sum_{i=0}^{n-1} x_i = size, 662 and the optimization criterion to minimize 663 \sum_{i=0}^{n-1} (x_i - desired[i])^2. 664 The \a values array must contain a feasible solution when called and will 665 be overwritten with the optimial solution the method computes. 666 */ 667 bool 668 LayoutOptimizer::Solve(double* values) 669 { 670 if (values == NULL) 671 return false; 672 673 int32 constraintCount = fConstraints->CountItems(); 674 675 // allocate the active constraint matrix and its transposed matrix 676 fActiveMatrix = allocate_matrix(constraintCount, fVariableCount); 677 fActiveMatrixTemp = allocate_matrix(constraintCount, fVariableCount); 678 MatrixDeleter _(fActiveMatrix); 679 MatrixDeleter _2(fActiveMatrixTemp); 680 if (!fActiveMatrix || !fActiveMatrixTemp) 681 return false; 682 683 bool success = _Solve(values); 684 return success; 685 } 686 687 688 // _Solve 689 bool 690 LayoutOptimizer::_Solve(double* values) 691 { 692 int32 constraintCount = fConstraints->CountItems(); 693 694 TRACE_ONLY( 695 TRACE("constraints:\n"); 696 for (int32 i = 0; i < constraintCount; i++) { 697 TRACE(" %-2ld: ", i); 698 fConstraints->ItemAt(i)->PrintToStream(); 699 } 700 ) 701 702 // our QP is supposed to be in this form: 703 // min_x 1/2x^TGx + x^Td 704 // s.t. a_i^Tx = b_i, i \in E 705 // a_i^Tx >= b_i, i \in I 706 707 // init our initial x 708 double x[fVariableCount]; 709 for (int i = 0; i < fVariableCount; i++) 710 x[i] = values[i]; 711 712 // init d 713 // Note that the values of d and of G result from rewriting the 714 // ||x - desired|| we actually want to minimize. 715 double d[fVariableCount]; 716 for (int i = 0; i < fVariableCount; i++) 717 d[i] = fDesired[i]; 718 719 // init active set 720 ConstraintList activeConstraints(constraintCount); 721 722 for (int32 i = 0; i < constraintCount; i++) { 723 Constraint* constraint = fConstraints->ItemAt(i); 724 if (constraint->IsSoft()) 725 continue; 726 double actualValue = _ActualValue(constraint, x); 727 TRACE("constraint %ld: actual: %f constraint: %f\n", i, actualValue, 728 _RightSide(constraint)); 729 if (fuzzy_equals(actualValue, _RightSide(constraint))) 730 activeConstraints.AddItem(constraint); 731 } 732 733 // The main loop: Each iteration we try to get closer to the optimum 734 // solution. We compute a vector p that brings our x closer to the optimum. 735 // We do that by computing the QP resulting from our active constraint set, 736 // W^k. Afterward each iteration we adjust the active set. 737 TRACE_ONLY(int iteration = 0;) 738 while (true) { 739 TRACE_ONLY( 740 TRACE("\n[iteration %d]\n", iteration++); 741 TRACE("active set:\n"); 742 for (int32 i = 0; i < activeConstraints.CountItems(); i++) { 743 TRACE(" "); 744 activeConstraints.ItemAt(i)->PrintToStream(); 745 } 746 ) 747 748 // solve the QP: 749 // min_p 1/2p^TGp + g_k^Tp 750 // s.t. a_i^Tp = 0 751 // with a_i \in activeConstraints 752 // g_k = Gx_k + d 753 // p = x - x_k 754 755 int32 activeCount = activeConstraints.CountItems(); 756 if (activeCount == 0) { 757 TRACE_ERROR("Solve(): Error: No more active constraints!\n"); 758 return false; 759 } 760 761 // construct a matrix from the active constraints 762 int am = activeCount; 763 const int an = fVariableCount; 764 bool independentRows[activeCount]; 765 zero_matrix(fActiveMatrix, am, an); 766 767 for (int32 i = 0; i < activeCount; i++) { 768 Constraint* constraint = activeConstraints.ItemAt(i); 769 if (constraint->IsSoft()) 770 continue; 771 SummandList* summands = constraint->LeftSide(); 772 for (int32 s = 0; s < summands->CountItems(); s++) { 773 Summand* summand = summands->ItemAt(s); 774 int32 variable = summand->Var()->Index(); 775 if (constraint->Op() == LinearProgramming::kLE) 776 fActiveMatrix[i][variable] = -summand->Coeff(); 777 else 778 fActiveMatrix[i][variable] = summand->Coeff(); 779 } 780 } 781 782 // TODO: The fActiveMatrix is sparse (max 2 entries per row). There should be 783 // some room for optimization. 784 am = remove_linearly_dependent_rows(fActiveMatrix, fActiveMatrixTemp, 785 independentRows, am, an); 786 787 // gxd = G * x + d 788 double gxd[fVariableCount]; 789 multiply_matrix_vector(fG, x, fVariableCount, fVariableCount, gxd); 790 add_vectors(gxd, d, fVariableCount); 791 792 double p[fVariableCount]; 793 if (!_SolveSubProblem(gxd, am, p)) 794 return false; 795 796 if (is_zero(p, fVariableCount)) { 797 // compute Lagrange multipliers lambda_i 798 // if lambda_i >= 0 for all i \in W^k \union inequality constraints, 799 // then we're done. 800 // Otherwise remove the constraint with the smallest lambda_i 801 // from the active set. 802 // The derivation of the Lagrangian yields: 803 // \sum_{i \in W^k}(lambda_ia_i) = Gx_k + d 804 // Which is an system we can solve: 805 // A^Tlambda = Gx_k + d 806 807 // A^T is over-determined, hence we need to reduce the number of 808 // rows before we can solve it. 809 bool independentColumns[an]; 810 double** aa = fTemp1; 811 transpose_matrix(fActiveMatrix, aa, am, an); 812 const int aam = remove_linearly_dependent_rows(aa, fTemp2, 813 independentColumns, an, am); 814 const int aan = am; 815 if (aam != aan) { 816 // This should not happen, since A has full row rank. 817 TRACE_ERROR("Solve(): Transposed A has less linear independent " 818 "rows than it has columns!\n"); 819 return false; 820 } 821 822 // also reduce the number of rows on the right hand side 823 double lambda[aam]; 824 int index = 0; 825 for (int i = 0; i < an; i++) { 826 if (independentColumns[i]) 827 lambda[index++] = gxd[i]; 828 } 829 830 bool success = solve(aa, aam, lambda); 831 if (!success) { 832 // Impossible, since we've removed all linearly dependent rows. 833 TRACE_ERROR("Solve(): Failed to compute lambda!\n"); 834 return false; 835 } 836 837 // find min lambda_i (only, if it's < 0, though) 838 double minLambda = 0; 839 int minIndex = -1; 840 index = 0; 841 for (int i = 0; i < activeCount; i++) { 842 if (independentRows[i]) { 843 Constraint* constraint = activeConstraints.ItemAt(i); 844 if (constraint->Op() != LinearProgramming::kEQ) { 845 if (lambda[index] < minLambda) { 846 minLambda = lambda[index]; 847 minIndex = i; 848 } 849 } 850 851 index++; 852 } 853 } 854 855 // if the min lambda is >= 0, we're done 856 if (minIndex < 0 || fuzzy_equals(minLambda, 0)) { 857 _SetResult(x, values); 858 return true; 859 } 860 861 // remove i from the active set 862 activeConstraints.RemoveItemAt(minIndex); 863 } else { 864 // compute alpha_k 865 double alpha = 1; 866 int barrier = -1; 867 // if alpha_k < 1, add a barrier constraint to W^k 868 for (int32 i = 0; i < constraintCount; i++) { 869 Constraint* constraint = fConstraints->ItemAt(i); 870 if (activeConstraints.HasItem(constraint)) 871 continue; 872 873 double divider = _ActualValue(constraint, p); 874 if (divider > 0 || fuzzy_equals(divider, 0)) 875 continue; 876 877 // (b_i - a_i^Tx_k) / a_i^Tp_k 878 double alphaI = _RightSide(constraint) 879 - _ActualValue(constraint, x); 880 alphaI /= divider; 881 if (alphaI < alpha) { 882 alpha = alphaI; 883 barrier = i; 884 } 885 } 886 TRACE("alpha: %f, barrier: %d\n", alpha, barrier); 887 888 if (alpha < 1) 889 activeConstraints.AddItem(fConstraints->ItemAt(barrier)); 890 891 // x += p * alpha; 892 add_vectors_scaled(x, p, alpha, fVariableCount); 893 } 894 } 895 } 896 897 898 bool 899 LayoutOptimizer::_SolveSubProblem(const double* d, int am, double* p) 900 { 901 // We have to solve the QP subproblem: 902 // min_p 1/2p^TGp + d^Tp 903 // s.t. a_i^Tp = 0 904 // with a_i \in activeConstraints 905 // 906 // We use the null space method, i.e. we find matrices Y and Z, such that 907 // AZ = 0 and [Y Z] is regular. Then with 908 // p = Yp_Y + Zp_z 909 // we get 910 // p_Y = 0 911 // and 912 // (Z^TGZ)p_Z = -(Z^TYp_Y + Z^Tg) = -Z^Td 913 // which is a linear equation system, which we can solve. 914 915 const int an = fVariableCount; 916 917 // we get Y and Z by QR decomposition of A^T 918 double tempD[am]; 919 double** const Q = fQ; 920 transpose_matrix(fActiveMatrix, fTemp1, am, an); 921 bool success = qr_decomposition(fTemp1, an, am, tempD, Q); 922 if (!success) { 923 TRACE_ERROR("Solve(): QR decomposition failed!\n"); 924 return false; 925 } 926 927 // Z is the (1, m + 1) minor of Q 928 const int zm = an; 929 const int zn = an - am; 930 931 double* Z[zm]; 932 for (int i = 0; i < zm; i++) 933 Z[i] = Q[i] + am; 934 935 // solve (Z^TGZ)p_Z = -Z^Td 936 937 // Z^T 938 transpose_matrix(Z, fZtrans, zm, zn); 939 // rhs: -Z^T * d; 940 double pz[zm]; 941 multiply_matrix_vector(fZtrans, d, zn, zm, pz); 942 negate_vector(pz, zn); 943 944 // fTemp2 = Ztrans * G * Z 945 multiply_matrices(fG, Z, fTemp1, zm, fVariableCount, zn); 946 multiply_matrices(fZtrans, fTemp1, fTemp2, zn, zm, zn); 947 948 success = solve(fTemp2, zn, pz); 949 if (!success) { 950 TRACE_ERROR("Solve(): Failed to solve() system for p_Z\n"); 951 return false; 952 } 953 954 // p = Z * pz; 955 multiply_matrix_vector(Z, pz, zm, zn, p); 956 957 return true; 958 } 959 960 961 // _SetResult 962 void 963 LayoutOptimizer::_SetResult(const double* x, double* values) 964 { 965 for (int i = 0; i < fVariableCount; i++) 966 values[i] = x[i]; 967 } 968