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