Merge branch 'gh/maint-clean' into ht/-include
[shapes.git] / source / solve_qp_dist_gen.cc
blobbe886fb48a0d5aed9e0840f00d2e09ac13f59a0b
1 /* This file is part of Shapes.
3 * Shapes is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * any later version.
8 * Shapes is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with Shapes. If not, see <http://www.gnu.org/licenses/>.
16 * Copyright 2009 Henrik Tidefelt
19 #include "quadratic_programs.h"
21 #include <gsl/gsl_linalg.h>
22 #include <gsl/gsl_blas.h>
23 #include <gsl/gsl_cblas.h>
25 using namespace Shapes;
27 #include <vector>
28 #include <cstdlib>
29 #include <cmath>
30 #include <limits>
32 //#define DISPLAY
33 //#define TRACEFIGS // generate output on stdout that gives a graphical picture of the operation
35 #ifdef DISPLAY
36 #include "mathematicarepresentation.h"
37 size_t polytope_distance_generator_form_CallCount = 0;
38 #endif
39 #ifdef TRACEFIGS
40 #include <iomanip>
41 #endif
44 namespace Shapes
46 namespace Computation
48 /* Since an active constraint implies that the corresponding variable is zero, the
49 * subproblems are not concerned with these variables at all.
51 void polytope_distance_generator_form_sub( const size_t dim,
52 const double ** g1Begin, const double ** g1End,
53 const double ** g2Begin, const double ** g2End,
54 const double * yDiff,
55 double sigmaTol,
56 double * d1, double * d2,
57 double * work ); /* allocated memory of size max( dim * ( n1 + n2 - 2 ) + ( n1 + n2 - 2 )^2 + 2 * dim + n1 + n2 )*/
59 void polytope_distance_generator_form_sub_Gilbert( const size_t dim,
60 const double ** g1Begin, const double ** g1End,
61 const double ** g2Begin, const double ** g2End,
62 const double * yDiff,
63 double * d1, double * d2 );
65 void polytope_distance_generator_form_diff( const size_t dim,
66 const double ** g1Begin, const double ** g1End,
67 const double ** g2Begin, const double ** g2End,
68 const double * x1, const double * x2,
69 double * yDiff );
71 void polytope_distance_generator_form_costs( const size_t dim,
72 const double ** g1Begin, const double ** g1End,
73 const double ** g2Begin, const double ** g2End,
74 const double * yDiff,
75 double * primalCost,
76 double * dualCostAccum, /* This value is updated as an accumulated maximum. */
77 double * work ); /* Just needs to be of size (dim) */
79 /* This function will compute orthogonal vectors spanning the nullspaces of vectors of ones.
80 * Memorey will be reused, but never returned.
81 * Given argument n, it returns a pointer to an array of ( n - 1 ) pointers, each pointing to an
82 * array of doubles of length n.
84 const double ** polytope_distance_generator_form_kernel( size_t n );
88 template<class T>
89 class DeleteOnExit
91 T * mem_;
92 public:
93 DeleteOnExit( T * mem ) : mem_( mem ) { }
94 ~DeleteOnExit( ){ delete mem_; }
99 void
100 Computation::polytope_distance_generator_form( const size_t dim,
101 const size_t n1, const double * g1,
102 const size_t n2, const double * g2,
103 double * costLower, double * costUpper,
104 double stopLower, double stopUpper,
105 double gapTolAbs, double gapTolRel,
106 double lmTol,
107 double xTol,
108 double sigmaTol,
109 double * y1, double * y2,
110 QPSolverStatus * status )
112 /* In case we for some reason would return without setting status properly... */
113 status->code = QP_ERROR;
114 status->error = QP_ERROR_UNDEFINED;
116 if( stopLower < 0 )
118 stopLower = std::numeric_limits< double >::max( );
120 /* stopUpper < 0 gives a test that will never be true. */
122 if( lmTol <= 0 )
124 lmTol = 1e-14;
126 if( xTol <= 0 )
128 xTol = 1e-8;
130 if( sigmaTol <= 0 )
132 sigmaTol = 1e-3;
135 if( costLower != 0 )
137 /* Since the dual cost is not strictly increasing, we must compute the accumulative max of all dual costs.
138 * Since the cost cannot be negative, it is safe to initialize with 0.
140 *costLower = 0;
143 bool checkCost =
144 ( costLower != 0 && stopLower < std::numeric_limits< double >::max( ) )
145 || ( costUpper != 0 && stopUpper > 0 )
146 || ( costLower != 0 && costUpper != 0 && ( gapTolAbs >= 0 || gapTolRel >= 0 ) );
148 /*-------------------------------------------------------------------------*/
149 /* Allocate memory: this section should be removed later and free memory
150 should be passed as a pointer */
151 /*-------------------------------------------------------------------------*/
152 const double ** gMem = new const double * [ n1 + n2 ];
153 DeleteOnExit< const double * > gMemDeleter( gMem );
154 const double ** g1InactiveBegin = gMem;
155 const double ** g1InactiveEnd = g1InactiveBegin + n1;
156 const double ** g1ActiveEnd = g1InactiveEnd;
157 const double ** g2InactiveBegin = gMem + n1;
158 const double ** g2InactiveEnd = g2InactiveBegin + n2;
159 const double ** g2ActiveEnd = g2InactiveEnd;
160 double * xMem = new double[ 5 * ( n1 + n2 ) ];
161 DeleteOnExit< double > xMemDeleter( xMem );
162 /* The x1 and x2 variables correspond to the generators in the order they appear in the sets defined above.
163 * The elements that corresponds to generators with active constraints are zero by definition, and
164 * do not have to be zero in memory.
166 double * x1 = xMem;
167 double * x2 = x1 + n1;
168 /* Similarly, only the last part of the Lagrange multiplier vectors are actually used.
170 double * lm1 = x2 + n2;
171 double * lm2 = lm1 + n1;
172 /* ... and only the first part of the subproblem step vectors.
174 double * d1 = lm2 + n2;
175 double * d2 = d1 + n1;
176 double * x1short = d2 + n2;
177 double * x2short = x1short + n1;
178 double * x1long = x2short + n2;
179 double * x2long = x1long + n1;
181 const double xInit = 1. / n1;
182 double * xDst = x1;
183 const double * src = g1;
184 for( const double ** dst = g1InactiveBegin; dst != g1InactiveEnd; ++dst, src += dim, ++xDst )
186 *dst = src;
187 *xDst = xInit;
191 const double xInit = 1. / n2;
192 double * xDst = x2;
193 const double * src = g2;
194 for( const double ** dst = g2InactiveBegin; dst != g2InactiveEnd; ++dst, src += dim, ++xDst )
196 *dst = src;
197 *xDst = xInit;
200 double * yDiff = new double[ dim ];
201 DeleteOnExit< double > xDiffDeleter( yDiff );
202 double * yDiffEnd = yDiff + dim;
204 double * sub_work = new double[ ( dim + n1 + n2 - 2 ) * ( n1 + n2 - 2 ) + 2 * dim + n1 + n2 ];
205 DeleteOnExit< double > sub_workDeleter( sub_work );
207 #ifdef DISPLAY
208 ++polytope_distance_generator_form_CallCount;
209 std::cerr << "Polytope distance call count: " << polytope_distance_generator_form_CallCount << std::endl ;
210 if( polytope_distance_generator_form_CallCount <= 10 )
212 std::cerr << "=== Problem number " << polytope_distance_generator_form_CallCount << " ===" << std::endl ;
213 Computation::polytope_distance_generator_form_write_data( std::cerr,
214 dim,
215 n1, g1,
216 n2, g2 );
217 std::cerr << "========================" << std::endl ;
219 #endif
220 #ifdef TRACEFIGS
221 if( dim != 2 )
223 std::cout << "##echo Invalid program, dimension of metric space is not two!" << std::endl ;
225 bool tracefigPageOpen = false;
226 std::cout << "#catalog.[setbboxgroup 'a]" << std::endl ;
228 std::cout << std::fixed << "g1: ";
229 const double * begin = g1;
230 const double * end = begin + dim * n1;
231 for( const double * gSrc = begin; gSrc != end; gSrc += dim )
233 if( gSrc != begin )
235 std::cout << "--" ;
237 std::cout << "(" ;
238 for( const double * src = gSrc; src != gSrc + dim; ++src )
240 if( src != gSrc )
242 std::cout << "," ;
244 std::cout << "(" << *src << "bp)" ;
246 std::cout << ")" ;
248 std::cout << std::endl ;
251 std::cout << "g2: ";
252 const double * begin = g2;
253 const double * end = begin + dim * n2;
254 for( const double * gSrc = begin; gSrc != end; gSrc += dim )
256 if( gSrc != begin )
258 std::cout << "--" ;
260 std::cout << "(" ;
261 for( const double * src = gSrc; src != gSrc + dim; ++src )
263 if( src != gSrc )
265 std::cout << "," ;
267 std::cout << "(" << *src << "bp)" ;
269 std::cout << ")" ;
271 std::cout << std::endl ;
273 #endif
275 bool checkNextCost = true;
276 /* The number of iterations is theoretically bounded by the number of possible combinations of activated constraints. Here, we know that
277 * one can never activate all constraints in a polytope, leading to the following bound.
279 const size_t maxIter = ( (2<<(n1-1)) - 1 ) * ( (2<<(n2-1)) - 1 );
280 for( size_t iterCount = 0; ; ++iterCount )
282 if( iterCount > maxIter )
284 status->code = QP_FAIL;
285 status->failure = QP_FAIL_ITER;
286 #ifdef DISPLAY
287 std::cerr << "Early return: Maximum number of iterations exceeded." << std::endl ;
288 #endif
289 #ifdef TRACEFIGS
290 if( tracefigPageOpen )
292 std::cout << ")" << std::endl ;
293 tracefigPageOpen = false;
295 #endif
296 return;
298 #ifdef TRACEFIGS
299 if( tracefigPageOpen )
301 std::cout << ")" << std::endl ;
302 tracefigPageOpen = false;
304 std::cout << "#catalog << @text_size:5bp | ( newGroup" << std::endl ;
305 tracefigPageOpen = true;
306 std::cout << " << @stroking:RGB_RED&@width:2bp | [[range '0 [duration g1]].foldl \\ p e → p & [spot [g1 1*e].p] null]" << std::endl
307 << " << @stroking:RGB_GREEN&@width:2bp | [[range '0 [duration g2]].foldl \\ p e → p & [spot [g2 1*e].p] null]" << std::endl
308 << " << @dash:[dashpattern 2bp 2bp]&@width:0.3bp | [stroke [controlling_hull g1] & [controlling_hull g2]]" << std::endl ;
309 for( const double ** gSrc = g1InactiveEnd; gSrc != g2ActiveEnd; ++gSrc )
311 if( gSrc == g1ActiveEnd )
313 gSrc = g2InactiveEnd - 1;
314 continue;
316 std::cout << " << [stroke [circle 2bp] >> [shift (" ;
317 for( const double * src = *gSrc; src != *gSrc + dim; ++src )
319 if( src != *gSrc )
321 std::cout << "," ;
323 std::cout << "(" << *src << "bp)" ;
325 std::cout << ")]]" << std::endl ;
327 std::cout << " << @stroking:RGB_BLUE | [stroke (" ;
328 polytope_distance_generator_form_diff( dim,
329 g1InactiveBegin, g1InactiveEnd,
330 g2InactiveBegin, g2InactiveBegin,
331 x1, x2,
332 yDiff );
333 for( const double * src = yDiff; src != yDiff + dim; ++src )
335 if( src != yDiff )
337 std::cout << "," ;
339 std::cout << "(" << -*src << "bp)" ;
341 std::cout << ")--(" ;
342 polytope_distance_generator_form_diff( dim,
343 g1InactiveBegin, g1InactiveBegin,
344 g2InactiveBegin, g2InactiveEnd,
345 x1, x2,
346 yDiff );
347 for( const double * src = yDiff; src != yDiff + dim; ++src )
349 if( src != yDiff )
351 std::cout << "," ;
353 std::cout << "(" << *src << "bp)" ;
355 std::cout << ")]" << std::endl ;
356 #endif
357 size_t nInactive1 = g1InactiveEnd - g1InactiveBegin;
358 size_t nInactive2 = g2InactiveEnd - g2InactiveBegin;
359 polytope_distance_generator_form_diff( dim,
360 g1InactiveBegin, g1InactiveEnd,
361 g2InactiveBegin, g2InactiveEnd,
362 x1, x2,
363 yDiff );
364 if( checkCost && checkNextCost )
366 /* All generators are used here, not only those of inactive constraints. */
367 polytope_distance_generator_form_costs( dim,
368 g1InactiveBegin, g1ActiveEnd,
369 g2InactiveBegin, g2ActiveEnd,
370 yDiff,
371 costUpper,
372 costLower,
373 sub_work );
374 #ifdef DISPLAY
375 std::cerr << "Interation: " << iterCount + 1 << std::endl ;
376 std::cerr << "Cost: " << *costLower << " -- " << *costUpper << std::endl ;
377 #endif
379 if( costUpper != 0 && costLower != 0 )
381 if( *costUpper - *costLower < gapTolAbs )
383 status->code = QP_OK;
384 status->termination = QP_OK_GAP_ABS;
385 status->iterations = iterCount;
386 break;
388 if( *costUpper - *costLower < gapTolRel * *costUpper )
390 status->code = QP_OK;
391 status->termination = QP_OK_GAP_REL;
392 status->iterations = iterCount;
393 break;
396 if( costUpper != 0 && *costUpper < stopUpper )
398 status->code = QP_OK;
399 status->termination = QP_OK_UPPER;
400 status->iterations = iterCount;
401 break;
403 if( costLower != 0 && *costLower > stopLower )
405 status->code = QP_OK;
406 status->termination = QP_OK_LOWER;
407 status->iterations = iterCount;
408 break;
411 checkNextCost = true;
412 polytope_distance_generator_form_sub( dim,
413 g1InactiveBegin, g1InactiveEnd,
414 g2InactiveBegin, g2InactiveEnd,
415 yDiff,
416 sigmaTol,
417 d1, d2,
418 sub_work );
419 #ifdef DISPLAY
420 std::cerr << "Delta: " << Helpers::mathematicaFormat( nInactive1, 1, d1 )
421 << " " << Helpers::mathematicaFormat( nInactive2, 1, d2 ) << std::endl ;
422 #endif
424 /* Compute squared norm of subproblem step. */
425 double stepNorm2 = 0;
427 double * src = d1;
428 double * end = d1 + nInactive1;
429 for( ; src != end; ++src )
431 stepNorm2 += (*src) * (*src);
435 double * src = d2;
436 double * end = d2 + nInactive2;
437 for( ; src != end; ++src )
439 stepNorm2 += (*src) * (*src);
442 if( stepNorm2 < xTol )
444 /* No step taken in sub-problem. Are there any constraints to drop? */
445 /* Compute lagrange multipliers.
446 * Note that the lagrange multipliers of the inactive constraints are only used for temporary storage.
447 * Normalizing yDiff will only scale the Langrange multipliers, and by normalizing yDiff we give
448 * the Lagrange multiplier tolerance (lmTol) a geometric interpretation; lmTol becomes a gap tolerance which
449 * is linear in the distance (not distance squared).
451 polytope_distance_generator_form_diff( dim,
452 g1InactiveBegin, g1InactiveEnd,
453 g2InactiveBegin, g2InactiveEnd,
454 x1short, x2short,
455 yDiff );
457 /* Normalize yDiff. */
458 double lengthSquared = 0;
459 for( const double * src = yDiff; src != yDiffEnd; ++src )
461 lengthSquared += (*src) * (*src);
463 const double yDiffScaling = 1. / sqrt( lengthSquared );
464 for( double * dst = yDiff; dst != yDiffEnd; ++dst )
466 *dst *= yDiffScaling;
470 /* Initialize with negative gradient. */
471 double * lmDst = lm1;
472 for( const double ** gSrc = g1InactiveBegin; gSrc != g1ActiveEnd; ++gSrc, ++lmDst )
474 double tmp = 0;
475 const double * ySrc = yDiff;
476 const double * src = *gSrc;
477 for( ; ySrc != yDiffEnd; ++ySrc, ++src )
479 tmp -= (*ySrc) * (*src);
481 *lmDst = tmp;
483 /* Let nu be the dual variable of the given equality constraint (all variables sum to 1). */
484 double nuMax = -1;
485 double nu = 0; /* Initialize to avoid compiler warnings. */
486 double * lmInactiveEnd = lm1 + nInactive1;
487 double * lmPtr = lm1;
488 for( ; lmPtr != lmInactiveEnd; ++lmPtr )
490 double lmAbs = fabs( *lmPtr );
491 if( lmAbs > nuMax )
493 nuMax = lmAbs;
494 nu = *lmPtr;
497 /* Then the remaining Lagrange multipliers follow. */
498 double * lmActiveEnd = lm1 + n1;
499 for( ; lmPtr != lmActiveEnd; ++lmPtr )
501 *lmPtr -= nu;
506 /* Compute Lagrange multipliers for the second polytope in the same way... up to sign changes. */
507 double * lmDst = lm2;
508 for( const double ** gSrc = g2InactiveBegin; gSrc != g2ActiveEnd; ++gSrc, ++lmDst )
510 double tmp = 0;
511 const double * ySrc = yDiff;
512 const double * src = *gSrc;
513 for( ; ySrc != yDiffEnd; ++ySrc, ++src )
515 tmp += (*ySrc) * (*src); /* Note sign! */
517 *lmDst = tmp;
519 double nuMax = -1;
520 double nu = 0; /* Initialize to avoid compiler warnings. */
521 double * lmInactiveEnd = lm2 + nInactive2;
522 double * lmPtr = lm2;
523 for( ; lmPtr != lmInactiveEnd; ++lmPtr )
525 double lmAbs = fabs( *lmPtr );
526 if( lmAbs > nuMax )
528 nuMax = lmAbs;
529 nu = *lmPtr;
532 double * lmActiveEnd = lm2 + n2;
533 for( ; lmPtr != lmActiveEnd; ++lmPtr )
535 *lmPtr -= nu;
539 #ifdef DISPLAY
540 std::cerr << "lambda: " << Helpers::mathematicaFormat( n1 - nInactive1, 1, lm1 + nInactive1 )
541 << " " << Helpers::mathematicaFormat( n2 - nInactive2, 1, lm2 + nInactive2 ) << std::endl ;
542 #endif
543 #ifdef TRACEFIGS
545 const double * lambdaSrc = lm1 + nInactive1;
546 for( const double ** gSrc = g1InactiveEnd; gSrc != g2ActiveEnd; ++gSrc, ++lambdaSrc )
548 if( gSrc == g1ActiveEnd )
550 gSrc = g2InactiveEnd - 1;
551 lambdaSrc = lm2 + nInactive2 - 1;
552 continue;
554 std::cout << " << @nonstroking:[gray 0.5] | (newText << `" << std::scientific << std::setprecision(1) << *lambdaSrc << std::fixed << "´) >> [shift (0,3bp)+(" ;
555 for( const double * src = *gSrc; src != *gSrc + dim; ++src )
557 if( src != *gSrc )
559 std::cout << "," ;
561 std::cout << "(" << *src << "bp)" ;
563 std::cout << ")]" << std::endl ;
566 #endif
568 bool dropped = false;
570 const double * lmSrc = lm1 + nInactive1;
571 const double * lmEnd = lm1 + n1;
572 const double ** gSrc = g1InactiveEnd;
573 double * xDst = x1 + nInactive1;
574 for( ; lmSrc != lmEnd; ++lmSrc, ++gSrc )
576 if( *lmSrc < -lmTol )
578 /* Drop constraint.
579 * Swap the generator out of the active set, so that it appears at the end
580 * of the inactive set.
582 const double * gTmp = *g1InactiveEnd;
583 *g1InactiveEnd = *gSrc;
584 *gSrc = gTmp;
585 ++g1InactiveEnd;
587 /* Since the constraint has been active, the corresponding variable has been zero.
589 *xDst = 0;
590 ++xDst;
592 dropped = true;
597 const double * lmSrc = lm2 + nInactive2;
598 const double * lmEnd = lm2 + n2;
599 const double ** gSrc = g2InactiveEnd;
600 double * xDst = x2 + nInactive2;
601 for( ; lmSrc != lmEnd; ++lmSrc, ++gSrc )
603 if( *lmSrc < -lmTol )
605 /* Drop constraint.
606 * Swap the generator out of the active set, so that it appears at the end
607 * of the inactive set.
609 const double * gTmp = *g2InactiveEnd;
610 *g2InactiveEnd = *gSrc;
611 *gSrc = gTmp;
612 ++g2InactiveEnd;
614 /* Since the constraint has been active, the corresponding variable has been zero.
616 *xDst = 0;
617 ++xDst;
619 dropped = true;
623 if( ! dropped )
625 /* We reached optimum!
627 status->code = QP_OK;
628 status->termination = QP_OK_LM;
629 status->iterations = iterCount;
630 break;
632 /* If we dropped some constraint, we just return to the top of the loop.
633 * No need to re-normalize variables since added variables are exactly zero.
635 checkNextCost = false; /* No need to check the cost in the next iteration since no step was made. */
637 else
639 /* Find inactive constraints that limit the step length in each polytope. */
640 double s1 = 1;
641 const double ** gStop1 = 0;
643 const double ** gSrc = g1InactiveBegin;
644 const double ** gEnd = g1InactiveEnd;
645 const double * dSrc = d1;
646 const double * xSrc = x1;
647 for( ; gSrc != gEnd; ++gSrc, ++dSrc, ++xSrc )
649 double denom = -*dSrc;
650 if( denom > 0 )
652 double sTmp = *xSrc / denom;
653 if( sTmp < s1 )
655 s1 = sTmp;
656 gStop1 = gSrc;
661 double s2 = 1;
662 const double ** gStop2 = 0;
664 const double ** gSrc = g2InactiveBegin;
665 const double ** gEnd = g2InactiveEnd;
666 const double * dSrc = d2;
667 const double * xSrc = x2;
668 for( ; gSrc != gEnd; ++gSrc, ++dSrc, ++xSrc )
670 double denom = -*dSrc;
671 if( denom > 0 )
673 double sTmp = *xSrc / denom;
674 if( sTmp < s2 )
676 s2 = sTmp;
677 gStop2 = gSrc;
683 /* In a basic active set algorithm, one uses only the smaller of s1 and s2, but here
684 * we actually use both, if that gives further decrease in the objective function.
686 double sShort = ( s1 < s2 ) ? s1 : s2;
688 const double * xSrc = x1;
689 const double * xEnd = x1 + nInactive1;
690 const double * dSrc = d1;
691 double * dst = x1short;
692 for( ; xSrc != xEnd; ++xSrc, ++dSrc, ++dst )
694 *dst = *xSrc + sShort * (*dSrc);
698 const double * xSrc = x2;
699 const double * xEnd = x2 + nInactive2;
700 const double * dSrc = d2;
701 double * dst = x2short;
702 for( ; xSrc != xEnd; ++xSrc, ++dSrc, ++dst )
704 *dst = *xSrc + sShort * (*dSrc);
707 double shortCost = 0;
708 polytope_distance_generator_form_diff( dim,
709 g1InactiveBegin, g1InactiveEnd,
710 g2InactiveBegin, g2InactiveEnd,
711 x1short, x2short,
712 yDiff );
713 for( const double * src = yDiff; src != yDiffEnd; ++src )
715 shortCost += (*src) * (*src);
718 if( s1 > s2 )
720 const double * xSrc = x1;
721 const double * xEnd = x1 + nInactive1;
722 const double * dSrc = d1;
723 double * dst = x1long;
724 for( ; xSrc != xEnd; ++xSrc, ++dSrc, ++dst )
726 *dst = *xSrc + s1 * (*dSrc);
728 double longCost = 0;
729 polytope_distance_generator_form_diff( dim,
730 g1InactiveBegin, g1InactiveEnd,
731 g2InactiveBegin, g2InactiveEnd,
732 x1long, x2short,
733 yDiff );
734 for( const double * src = yDiff; src != yDiffEnd; ++src )
736 longCost += (*src) * (*src);
739 if( longCost > shortCost )
741 /* Dont make long step. */
742 gStop1 = 0;
743 s1 = s2;
746 else if( s2 > s1 )
748 const double * xSrc = x2;
749 const double * xEnd = x2 + nInactive2;
750 const double * dSrc = d2;
751 double * dst = x2long;
752 for( ; xSrc != xEnd; ++xSrc, ++dSrc, ++dst )
754 *dst = *xSrc + s2 * (*dSrc);
756 double longCost = 0;
757 polytope_distance_generator_form_diff( dim,
758 g1InactiveBegin, g1InactiveEnd,
759 g2InactiveBegin, g2InactiveEnd,
760 x1short, x2long,
761 yDiff );
762 for( const double * src = yDiff; src != yDiffEnd; ++src )
764 longCost += (*src) * (*src);
767 if( longCost > shortCost )
769 /* Dont make long step. */
770 gStop2 = 0;
771 s2 = s1;
775 /* Update variables and active constraints.
776 * Important: variables first, or we will get a mess!
779 const double * xSrc = x1;
780 const double * xEnd = x1 + nInactive1;
781 const double * dSrc = d1;
782 double * dst = x1;
783 for( ; xSrc != xEnd; ++xSrc, ++dSrc, ++dst )
785 *dst = *xSrc + s1 * (*dSrc);
788 if( gStop1 != 0 )
790 /* Swap constraints, and swap variables in the same way. New variable is zero by construction. */
791 --g1InactiveEnd;
792 const double * gTmp = *gStop1;
793 *gStop1 = *g1InactiveEnd;
794 *g1InactiveEnd = gTmp;
795 *( x1 + ( gStop1 - g1InactiveBegin ) ) = *( x1 + ( g1InactiveEnd - g1InactiveBegin ) );
798 const double * xSrc = x2;
799 const double * xEnd = x2 + nInactive2;
800 const double * dSrc = d2;
801 double * dst = x2;
802 for( ; xSrc != xEnd; ++xSrc, ++dSrc, ++dst )
804 *dst = *xSrc + s2 * (*dSrc);
807 if( gStop2 != 0 )
809 /* Swap constraints, and swap variables in the same way. New variable is zero by construction. */
810 --g2InactiveEnd;
811 const double * gTmp = *gStop2;
812 *gStop2 = *g2InactiveEnd;
813 *g2InactiveEnd = gTmp;
814 *( x2 + ( gStop2 - g2InactiveBegin ) ) = *( x2 + ( g2InactiveEnd - g2InactiveBegin ) );
819 #ifdef TRACEFIGS
820 if( tracefigPageOpen )
822 std::cout << ")" << std::endl ;
823 tracefigPageOpen = false;
825 #endif
827 /* Compute final cost. */
829 polytope_distance_generator_form_diff( dim,
830 g1InactiveBegin, g1InactiveEnd,
831 g2InactiveBegin, g2InactiveEnd,
832 x1, x2,
833 yDiff );
834 polytope_distance_generator_form_costs( dim,
835 g1InactiveBegin, g1ActiveEnd,
836 g2InactiveBegin, g2ActiveEnd,
837 yDiff,
838 costUpper,
839 costLower,
840 sub_work );
841 #ifdef DISPLAY
842 std::cerr << "Terminating with final cost: " << *costLower << " -- " << *costUpper << std::endl ;
843 #endif
846 /* Compute final point in each polytope. */
847 if( y1 != 0 )
849 double * y = y1;
850 double * yEnd = y + dim;
851 for( double * dst = y; dst != yEnd; ++dst )
853 *dst = 0;
855 const double * xSrc = x1;
856 for( const double ** gSrc = g1InactiveBegin; gSrc != g1InactiveEnd; ++gSrc, ++xSrc )
858 double xVal = *xSrc;
859 const double * src = *gSrc;
860 for( double * dst = y; dst != yEnd; ++dst, ++src )
862 *dst += xVal * (*src);
866 if( y2 != 0 )
868 double * y = y2;
869 double * yEnd = y + dim;
870 for( double * dst = y; dst != yEnd; ++dst )
872 *dst = 0;
874 const double * xSrc = x2;
875 for( const double ** gSrc = g2InactiveBegin; gSrc != g2InactiveEnd; ++gSrc, ++xSrc )
877 double xVal = *xSrc;
878 const double * src = *gSrc;
879 for( double * dst = y; dst != yEnd; ++dst, ++src )
881 *dst += xVal * (*src);
887 void
888 Computation::polytope_distance_generator_form_diff( const size_t dim,
889 const double ** g1Begin, const double ** g1End,
890 const double ** g2Begin, const double ** g2End,
891 const double * x1, const double * x2,
892 double * yDiff )
894 double * yDiffEnd = yDiff + dim;
895 for( double * dst = yDiff; dst != yDiffEnd; ++dst )
897 *dst = 0;
900 const double * xSrc = x2;
901 for( const double ** gSrc = g2Begin; gSrc != g2End; ++gSrc, ++xSrc )
903 double xVal = *xSrc;
904 const double * src = *gSrc;
905 for( double * dst = yDiff; dst != yDiffEnd; ++dst, ++src )
907 *dst += xVal * (*src);
912 const double * xSrc = x1;
913 for( const double ** gSrc = g1Begin; gSrc != g1End; ++gSrc, ++xSrc )
915 double xVal = *xSrc;
916 const double * src = *gSrc;
917 for( double * dst = yDiff; dst != yDiffEnd; ++dst, ++src )
919 *dst -= xVal * (*src);
925 void
926 Computation::polytope_distance_generator_form_costs( const size_t dim,
927 const double ** g1Begin, const double ** g1End,
928 const double ** g2Begin, const double ** g2End,
929 const double * yDiff,
930 double * primalCost,
931 double * dualCostAccum,
932 double * work )
934 const double * yDiffEnd = yDiff + dim;
936 double pCost = 0;
937 for( const double * src = yDiff; src != yDiffEnd; ++src )
939 pCost += (*src) * (*src);
942 if( primalCost != 0 )
944 *primalCost = pCost;
947 if( dualCostAccum == 0 )
949 return;
952 double * n = work; /* Normalaized normal to planes separating the two polytopes. */
953 double * nEnd = work + dim;
955 /* Normalize yDiff and store in n. */
956 double yDiffScale = 1. / sqrt( pCost );
957 double * dst = n;
958 for( const double * src = yDiff; src != yDiffEnd; ++src, ++dst )
960 *dst = yDiffScale * (*src);
964 /* Find dual cost by projecting generators onto n. */
965 double min2 = std::numeric_limits< double >::max( );
967 for( const double ** gSrc = g2Begin; gSrc != g2End; ++gSrc )
969 double tmp = 0;
970 const double * src = *gSrc;
971 for( const double * nSrc = n; nSrc != nEnd; ++nSrc, ++src )
973 tmp += (*nSrc) * (*src);
975 min2 = std::min( min2, tmp );
979 double max1 = -std::numeric_limits< double >::max( );
981 for( const double ** gSrc = g1Begin; gSrc != g1End; ++gSrc )
983 double tmp = 0;
984 const double * src = *gSrc;
985 for( const double * nSrc = n; nSrc != nEnd; ++nSrc, ++src )
987 tmp += (*nSrc) * (*src);
989 max1 = std::max( max1, tmp );
993 double diff = min2 - max1;
994 if( diff > 0 )
996 *dualCostAccum = std::max( *dualCostAccum, diff * diff );
1001 void
1002 Computation::polytope_distance_generator_form_sub( const size_t dim,
1003 const double ** g1Begin, const double ** g1End,
1004 const double ** g2Begin, const double ** g2End,
1005 const double * yDiff,
1006 double sigmaTol,
1007 double * d1, double * d2,
1008 double * work )
1010 size_t n1 = g1End - g1Begin;
1011 size_t n2 = g2End - g2Begin;
1012 double * d1End = d1 + n1;
1013 double * d2End = d2 + n2;
1014 for( double * dst = d1; dst != d1End; ++dst )
1016 *dst = 0;
1018 for( double * dst = d2; dst != d2End; ++dst )
1020 *dst = 0;
1022 if( n1 == 1 && n2 == 1 ) /* They must never be less than 1! */
1024 /* If there is only one inactive constraint in a polytope, the corresponding variable is in fact locked to 1.
1025 * Henece, we cannot move in that polytope. If we can't move in either polytope, there is nothing to optimize.
1026 * We have already set the result to 0.
1028 return;
1030 const double ** N1 = Computation::polytope_distance_generator_form_kernel( n1 );
1031 const double ** N2 = Computation::polytope_distance_generator_form_kernel( n2 );
1033 gsl_vector y_gsl;
1034 y_gsl.data = const_cast< double * >( yDiff );
1035 y_gsl.size = dim;
1036 y_gsl.stride = 1;
1038 gsl_vector d_gsl;
1039 d_gsl.data = work;
1040 work += ( d_gsl.size = n1 + n2 - 2 );
1041 d_gsl.stride = 1;
1043 if( dim >= n1 + n2 - 2 )
1045 /* Over-determined case. SVD of A can be computed as usual. */
1046 gsl_matrix A;
1047 A.data = work;
1048 work += ( A.size1 = dim ) * ( A.size2 = A.tda = n1 + n2 - 2 );
1049 gsl_matrix V;
1050 V.data = work;
1051 work += ( V.size1 = n1 + n2 - 2 ) * ( V.size2 = V.tda = n1 + n2 - 2 );
1052 gsl_vector S;
1053 S.data = work;
1054 work += ( S.size = n1 + n2 - 2 );
1055 S.stride = 1;
1056 gsl_vector SV_work;
1057 SV_work.data = work;
1058 work += ( SV_work.size = n1 + n2 - 2 );
1059 SV_work.stride = 1;
1061 /* Populate A with data.
1062 * The data access here can be improved. */
1064 /* Polytope 1 */
1065 double * ARowStart = A.data;
1066 for( size_t row = 0; row < dim; ++row, ARowStart += A.tda )
1068 const double ** NcolStart = N1;
1069 const double ** NcolEnd = N1 + n1 - 1;
1070 double * Adst = ARowStart;
1071 for( ; NcolStart != NcolEnd; ++NcolStart, ++Adst )
1073 double tmp = 0;
1074 const double ** gcolStart = g1Begin;
1075 const double ** gcolEnd = g1End;
1076 const double * Nsrc = *NcolStart;
1077 for( ; gcolStart != gcolEnd; ++gcolStart, ++Nsrc )
1079 tmp += (*((*gcolStart) + row)) * (*Nsrc);
1081 *Adst = tmp;
1086 /* Polytope 2 */
1087 double * ARowStart = A.data + ( n1 - 1 );
1088 for( size_t row = 0; row < dim; ++row, ARowStart += A.tda )
1090 const double ** NcolStart = N2;
1091 const double ** NcolEnd = N2 + n2 - 1;
1092 double * Adst = ARowStart;
1093 for( ; NcolStart != NcolEnd; ++NcolStart, ++Adst )
1095 double tmp = 0;
1096 const double ** gcolStart = g2Begin;
1097 const double ** gcolEnd = g2End;
1098 const double * Nsrc = *NcolStart;
1099 for( ; gcolStart != gcolEnd; ++gcolStart, ++Nsrc )
1101 tmp -= (*((*gcolStart) + row)) * (*Nsrc); /* Note the sign! */
1103 *Adst = tmp;
1108 gsl_linalg_SV_decomp( & A, & V, & S, & SV_work );
1110 /* Truncate small singular values. */
1111 double * dst = S.data;
1112 double * end = S.data + S.size;
1113 const double sigmaCut = sigmaTol * (*dst);
1114 ++dst;
1115 for( ; dst != end; ++dst )
1117 if( *dst < sigmaCut )
1119 break;
1122 for( ; dst != end; ++dst )
1124 *dst = 0;
1127 gsl_linalg_SV_solve( & A, & V, & S, & y_gsl, & d_gsl );
1129 else
1131 /* Under-determined case. Need to transpose A to compute SVD. */
1132 gsl_matrix A;
1133 A.data = work;
1134 work += ( A.size1 = n1 + n2 - 2 ) * ( A.size2 = A.tda = dim );
1135 gsl_matrix V;
1136 V.data = work;
1137 work += ( V.size1 = dim ) * ( V.size2 = V.tda = dim );
1138 gsl_vector S;
1139 S.data = work;
1140 work += ( S.size = dim );
1141 S.stride = 1;
1142 gsl_vector SV_work;
1143 SV_work.data = work;
1144 work += ( SV_work.size = dim );
1145 SV_work.stride = 1;
1147 /* Populate A with data.
1148 * The data access here can be improved. */
1150 /* Polytope 1 */
1151 double * AColStart = A.data;
1152 for( size_t row = 0; row < dim; ++row, ++AColStart )
1154 const double ** NcolStart = N1;
1155 const double ** NcolEnd = N1 + n1 - 1;
1156 double * Adst = AColStart;
1157 for( ; NcolStart != NcolEnd; ++NcolStart, Adst += A.tda )
1159 double tmp = 0;
1160 const double ** gcolStart = g1Begin;
1161 const double ** gcolEnd = g1End;
1162 const double * Nsrc = *NcolStart;
1163 for( ; gcolStart != gcolEnd; ++gcolStart, ++Nsrc )
1165 tmp += (*((*gcolStart) + row)) * (*Nsrc);
1167 *Adst = tmp;
1172 /* Polytope 2 */
1173 double * AColStart = A.data + ( n1 - 1 ) * A.tda;
1174 for( size_t row = 0; row < dim; ++row, ++AColStart )
1176 const double ** NcolStart = N2;
1177 const double ** NcolEnd = N2 + n2 - 1;
1178 double * Adst = AColStart;
1179 for( ; NcolStart != NcolEnd; ++NcolStart, Adst += A.tda )
1181 double tmp = 0;
1182 const double ** gcolStart = g2Begin;
1183 const double ** gcolEnd = g2End;
1184 const double * Nsrc = *NcolStart;
1185 for( ; gcolStart != gcolEnd; ++gcolStart, ++Nsrc )
1187 tmp -= (*((*gcolStart) + row)) * (*Nsrc); /* Note the sign! */
1189 *Adst = tmp;
1194 gsl_linalg_SV_decomp( & A, & V, & S, & SV_work );
1196 /* Truncate small singular values. */
1197 double * dst = S.data;
1198 double * end = S.data + S.size;
1199 const double sigmaCut = sigmaTol * (*dst);
1200 ++dst;
1201 for( ; dst != end; ++dst )
1203 if( *dst < sigmaCut )
1205 break;
1208 for( ; dst != end; ++dst )
1210 *dst = 0;
1213 /* Now, instead of using
1214 * gsl_linalg_SV_solve( & A, & V, & S, & y_gsl, & d_gsl );
1215 * we must solve the equation ourselves since the decomposition is transposed.
1217 gsl_blas_dgemv( CblasTrans, 1, & V, & y_gsl, 0, & SV_work );
1219 const double * src = S.data;
1220 const double * srcEnd = S.data + S.size;
1221 double * dst = SV_work.data;
1222 for( ; src != srcEnd; ++src, ++dst )
1224 if( *src > 0 )
1226 *dst /= *src;
1228 else
1230 *dst = 0;
1234 gsl_blas_dgemv( CblasNoTrans, 1, & A, & SV_work, 0, & d_gsl );
1238 /* Compute output variables. */
1239 /* First clear. */
1241 double * dst = d1;
1242 double * dstEnd = d1 + n1;
1243 for( ; dst != dstEnd; ++dst )
1245 *dst = 0;
1249 double * dst = d2;
1250 double * dstEnd = d2 + n2;
1251 for( ; dst != dstEnd; ++dst )
1253 *dst = 0;
1257 /* Then add linear combination of basis vectors. */
1258 const double * dSrc = d_gsl.data;
1260 const double ** NSrc = N1;
1261 const double ** NSrcEnd = N1 + ( n1 - 1 );
1262 for( ; NSrc != NSrcEnd; ++NSrc, ++dSrc )
1264 double w = *dSrc;
1265 double * dst = d1;
1266 double * dstEnd = d1 + n1;
1267 const double * src = *NSrc;
1268 for( ; dst != dstEnd; ++dst, ++src )
1270 *dst += w * *src;
1275 const double ** NSrc = N2;
1276 const double ** NSrcEnd = N2 + ( n2 - 1 );
1277 for( ; NSrc != NSrcEnd; ++NSrc, ++dSrc )
1279 double w = *dSrc;
1280 double * dst = d2;
1281 double * dstEnd = d2 + n2;
1282 const double * src = *NSrc;
1283 for( ; dst != dstEnd; ++dst, ++src )
1285 *dst += w * *src;
1292 void
1293 Computation::polytope_distance_generator_form_sub_Gilbert( const size_t dim,
1294 const double ** g1Begin, const double ** g1End,
1295 const double ** g2Begin, const double ** g2End,
1296 const double * yDiff,
1297 double * d1, double * d2 )
1302 const double **
1303 Computation::polytope_distance_generator_form_kernel( size_t n )
1305 static std::vector< const double ** > mem( 0 );
1306 if( n < mem.size( ) )
1308 return mem[ n ];
1310 mem.reserve( n + 1 );
1311 while( mem.size( ) <= n )
1313 size_t m = mem.size( );
1314 if( m == 0 )
1316 /* Invalid dimension, just skip. */
1317 mem.push_back( 0 );
1318 continue;
1320 if( m == 1 )
1322 /* Trivial case; kernel has dimension 0 */
1323 mem.push_back( 0 );
1324 continue;
1326 const double ** res = new const double * [ m - 1 ];
1327 gsl_matrix * qr = gsl_matrix_alloc( m, m );
1328 gsl_vector * tau = gsl_vector_alloc( m );
1329 gsl_matrix_set_zero( qr );
1330 for( size_t i = 0; i < m; ++i )
1332 gsl_matrix_set( qr, i, 0, 1. );
1334 gsl_matrix * q = gsl_matrix_alloc( m, m );
1335 gsl_matrix * r = gsl_matrix_alloc( m, m );
1337 gsl_linalg_QR_decomp( qr, tau );
1338 gsl_linalg_QR_unpack( qr, tau, q, r );
1340 const double ** dst = res;
1341 const double ** dstEnd = res + ( m - 1 );
1342 double * srcQStart = q->data + 1; /* Skip first column. */
1343 for( ; dst != dstEnd; ++dst, ++srcQStart )
1345 double * v = new double[ m ];
1346 *dst = v;
1347 double * vEnd = v + m;
1348 double * srcQ = srcQStart;
1349 for( ; v != vEnd; ++v, srcQ += q->tda )
1351 *v = *srcQ;
1356 mem.push_back( res );
1358 gsl_matrix_free( qr );
1359 gsl_vector_free( tau );
1360 gsl_matrix_free( q );
1361 gsl_matrix_free( r );
1363 return mem[ n ];