Institut de Robòtica i Informàtica Industrial
KRD Group

The CuikSuite Project

simplex.c

Go to the documentation of this file.
00001 #include "simplex.h"
00002 
00003 #include "defines.h"
00004 #include "random.h"
00005 
00006 
00019 void SimplexExpandBounds(unsigned int eq_type,Tinterval *b)
00020 {
00021   double v1,v2;
00022 
00023   v1=LowerLimit(b);
00024   v2=UpperLimit(b);
00025 
00026   if (eq_type==LEQ)
00027     v1=-INF;
00028   else
00029     {
00030       if (eq_type==GEQ)
00031         v2=INF;
00032     }
00033 
00034   NewInterval(v1,v2,b);
00035 }
00036 
00037 /*
00038   Sets new ranges for the variables in the simplex
00039 */
00040 void SetSimplexBounds(Tbox *b,TSimplex *lp)
00041 {
00042   unsigned int i;
00043   Tinterval *is;
00044   unsigned int nv;
00045 
00046   #if (_DEBUG>4)
00047     printf("  Setting bound for columns (variable):\n");
00048   #endif
00049 
00050   nv=GetBoxNIntervals(b);
00051   is=GetBoxIntervals(b);
00052   for(i=0;i<nv;i++)
00053     {
00054       SimplexSetColBounds(i,&(is[i]),lp);
00055 
00056       #if (_DEBUG>4)
00057       printf("    %u: ",i);
00058         PrintInterval(stdout,&(is[i]));
00059         if (IntervalSize(&(is[i]))<INF)
00060           printf(" -> %g\n",IntervalSize(&(is[i])));
00061         else
00062           printf(" -> +inf\n");
00063       #endif
00064     }
00065 }
00066 
00067 boolean SimplexAddNewConstraint(double epsilon,
00068                                 TLinearConstraint *lc,
00069                                 unsigned int eq_type,
00070                                 Tinterval *is,
00071                                 TSimplex *s)
00072 { 
00073   boolean full;
00074   TLinearConstraint lcInt;
00075   unsigned int n;
00076   Tinterval bounds;
00077   double es;
00078 
00079   full=TRUE;
00080 
00081   /*We avoid any modification in the given linear constraint. Modification
00082     can produce undesired side-effects out of this method*/
00083   CopyLinearConstraint(&lcInt,lc);
00084 
00085   #if (_DEBUG>4)
00086     printf("       Adding new constraint:\n         ");
00087     PrintLinearConstraint(stdout,TRUE,NULL,&lcInt);
00088   #endif
00089 
00090   /* For GLPK it is compulsatory to use this. For the other simplex engines
00091      a smaller threshold could be used.  */
00092   CleanLinearConstraint((_SIMPLEX_ENGINE==_GLPK?epsilon:ZERO),is,&lcInt);
00093 
00094   #if (_DEBUG>4)
00095     printf("         Constraint after cleaning:\n           ");
00096     PrintLinearConstraint(stdout,TRUE,NULL,&lcInt);
00097   #endif
00098 
00099   n=GetNumTermsInLinearConstraint(&lcInt);
00100   /* If the input linear constraint involves at least one variable... */
00101   if (n>0)
00102     {     
00103       /* linear constraint with very narrow range are converted to inequalities
00104          (randomly selecting LEQ or GEQ). This is a conservative way to avoid
00105          numerical issues.  */
00106       GetLinearConstraintError(&bounds,&lcInt);
00107       SimplexExpandBounds(eq_type,&bounds);
00108       SetLinearConstraintError(&bounds,&lcInt);
00109 
00110       #if (_DEBUG>4)
00111         printf("         Constraint after expanding bounds:\n           ");
00112         PrintLinearConstraint(stdout,TRUE,NULL,&lcInt);
00113       #endif
00114 
00115       if (!SimplifyLinearConstraint(&full,is,&lcInt))
00116         {
00117           /*******************************************************************/
00118           /* Extremely small ranges for equalities are not correctly processed
00119              by the simplex engines (they convert the tiny ranges to a point and
00120              then solutions might be lost) */
00121           es=IntervalSize(&bounds);
00122           if ((eq_type==EQU)&&(es>0.0)&&(es<epsilon)&&(epsilon>=1e-6))
00123             SimplexExpandBounds((randomDouble()<0.5?LEQ:GEQ),&bounds);
00124           SetLinearConstraintError(&bounds,&lcInt);
00125           /*******************************************************************/
00126 
00127           SimplexAddNewConstraintRaw(&lcInt,s);
00128         }
00129       #if (_DEBUG>4)
00130       else
00131         printf("           Empty simplified equation\n");
00132       #endif
00133     }
00134   #if (_DEBUG>4)
00135   else
00136     printf("       Empty input equation\n");
00137   #endif
00138 
00139   DeleteLinearConstraint(&lcInt);
00140 
00141   return(full);
00142 }
00143 
00144 double SimplexGetOptimalValue(unsigned int safe_simplex,double m,Tbox *x,TSimplex *s)
00145 {
00146   double o_safe;
00147  
00148   if (safe_simplex==0)
00149     o_safe=SimplexGetOptimalValueRaw(s);
00150   else
00151     {
00152       /*
00153         LP safe bounds taken from
00154         "Safe bounds in linear and mixed-integer linear programming"
00155         Arnold Neumaier and Oleg Shcherbina, 2003
00156       */
00157 
00158       double *lambda,*obj_c;
00159       double o,rl,ru;
00160       unsigned int n,m;
00161       unsigned int i,j,k;;
00162       Tinterval r,obj,a;
00163       TLinearConstraint lc;
00164       Tinterval bounds;
00165       double ct_l,ct_u;
00166       TLinearConstraint s_obj;
00167 
00168       m=SimplexNRows(s);
00169       n=SimplexNColumns(s);
00170 
00171       NEW(lambda,m,double);
00172       for(j=0;j<m;j++)
00173         lambda[j]=SimplexGetRowDual(j,s);
00174 
00175       NEW(obj_c,n,double);
00176       for(i=0;i<n;i++)
00177         obj_c[i]=0.0;
00178       SimplexGetOptimizationFunction(&s_obj,s);
00179       k=GetNumTermsInLinearConstraint(&s_obj);
00180       for(i=0;i<k;i++)
00181         obj_c[GetLinearConstraintVariable(i,&s_obj)]=GetLinearConstraintCoefficient(i,&s_obj);
00182       DeleteLinearConstraint(&s_obj);
00183 
00184       /* o_safe = inf{  (\sum_j^m lambda[j]*b[j]) - (sum_i^n r[i]*x[i])}
00185          with 
00186          r[i] = - c[i] + sum_j^m A(j,i)*lambda[j]
00187          Note that only the lower limit is necessary, thus
00188          o_safe = inf{\sum_j^m lambda[j]*b[j]} -sup{sum_i^n r[i]*x[i]}
00189       */
00190 
00191       o_safe=0.0; /*= inf{\sum_j^m lambda[j]*b[j]} */
00192       ROUNDDOWN;
00193       for(j=0;j<m;j++)
00194         {
00195           SimplexGetRowBounds(j,&bounds,s);
00196 
00197           ct_l=LowerLimit(&bounds);
00198           ct_u=UpperLimit(&bounds);
00199 
00200           if ((ct_u==INF)||(lambda[j]>0))
00201             o_safe+=(lambda[j]*ct_l);
00202           else
00203             o_safe+=(lambda[j]*ct_u);
00204         }
00205       ROUNDNEAR;
00206 
00207       NewInterval(0,0,&obj); /*obj=sum_i^n r[i]*x[i]*/
00208 
00209       for(i=0;i<n;i++)
00210         {
00211           /*Compute r[i] = -c[i] + sum_j^m A(j,i)*lambda[j] */
00212           SimplexGetColConstraint(i,&lc,s);
00213           k=GetNumTermsInLinearConstraint(&lc);
00214 
00215           ROUNDDOWN;
00216           rl=-obj_c[i];
00217           for(j=0;j<k;j++)
00218             rl+=(GetLinearConstraintCoefficient(j,&lc)*lambda[GetLinearConstraintVariable(j,&lc)]);
00219             
00220           ROUNDUP;
00221           ru=-obj_c[i];
00222           for(j=0;j<k;j++)
00223             ru+=(GetLinearConstraintCoefficient(j,&lc)*lambda[GetLinearConstraintVariable(j,&lc)]);
00224       
00225           ROUNDNEAR;
00226           NewInterval(rl,ru,&r);
00227 
00228           /*Compute r[i]*x[i]*/
00229           IntervalProduct(&r,GetBoxInterval(i,x),&a);
00230 
00231           /*Add to obj*/
00232           IntervalAdd(&obj,&a,&obj);
00233         
00234           DeleteLinearConstraint(&lc);
00235         }
00236 
00237       free(lambda);
00238       free(obj_c);
00239 
00240       ROUNDDOWN;
00241       o_safe=o_safe-UpperLimit(&obj);
00242       ROUNDNEAR;
00243 
00244       o=SimplexGetOptimalValueRaw(s);
00245 
00246       o_safe=(o_safe<o?o_safe:o);
00247     }
00248 
00249   return(o_safe<m?m:o_safe);
00250 }
00251 
00252 
00253 /*
00254   Reduces a box along a given range. The reduction is based on minimizing
00255   the simplex problem with a objective function that only affects the given
00256   varible. 
00257 */
00258 unsigned int ReduceRange(double epsilon,unsigned int safe_simplex,
00259                          unsigned int nv,Tbox *b,TSimplex *lp)
00260 {
00261   unsigned int j;                        
00262   unsigned int r;    /*Simplex result*/
00263   boolean empty;     
00264   boolean err;       /*Simplex error*/
00265   Tinterval new_range;
00266   Tinterval *i_in;
00267   double s_in;
00268   double o;
00269   TLinearConstraint obj;
00270   
00271   empty=FALSE;
00272   err=FALSE;
00273 
00274   /* Add the most up to date bounds for the system/dummy variables to the simplex */
00275   SetSimplexBounds(b,lp);
00276   
00277   #if (_SIMPLEX_ENGINE!=_CLP) /* in CLP Reset Simplex has no effect */
00278     if (safe_simplex>1)
00279       {
00280         /* This improves the numerical stability but slows down the process */
00281         ResetSimplex(lp);
00282       }
00283   #endif
00284 
00285   i_in=GetBoxInterval(nv,b);  /* Range to be reduced */
00286 
00287   s_in=IntervalSize(i_in);
00288 
00289   #if (_DEBUG>4)
00290     printf("  Reducing interval %u via simplex\n",nv);
00291     printf("    Original Interval ");
00292     PrintInterval(stdout,i_in);
00293     if (s_in>=INF) printf(" s: inf\n");
00294     else printf(" s:%g\n",s_in);
00295   #endif
00296 
00297   CopyInterval(&new_range,i_in); /* Default initialization used in case 
00298                                     of ERROR_IN_PROCESS*/
00299   if (s_in>=epsilon)
00300     {
00301       InitLinearConstraint(&obj);
00302   
00303       for(j=0;((!empty)&&(!err)&&(j<2));j++) /*j=0 minimize j=1 maximize*/
00304         {
00305           ResetLinearConstraint(&obj);
00306           if (j==0)
00307             {
00308               AddTerm2LinearConstraint(nv,+1.0,&obj);
00309 
00310               #if (_DEBUG>4)
00311                 printf("Determining lower bound for variable %u -> \n",nv);
00312               #endif
00313             }
00314           else
00315             {
00316               AddTerm2LinearConstraint(nv,-1.0,&obj);
00317 
00318               #if (_DEBUG>4)
00319                 printf("Determining upper bound for variable %u -> \n",nv);
00320               #endif
00321             }
00322 
00323           SimplexSetOptimizationFunction(&obj,lp);
00324 
00325           #if (_SIMPLEX_ENGINE!=_CLP) /* in CLP Reset Simplex has no effect */
00326             if ((safe_simplex>2)&&(j==1))
00327               {
00328                 /* This improves (even more) the numerical stability but slows down (even more)
00329                    the process (See the use of ResetSimplex in ReduceBox) */
00330                 ResetSimplex(lp);
00331               }
00332           #endif
00333 
00334           /*Solve the simplex problem*/
00335           #if (_DEBUG>4)
00336             printf("==================================================================================\n");
00337           #endif
00338           r=SimplexSolve(lp);
00339 
00340           if (r==REDUCED_BOX)
00341             {
00342               if (IntervalSize(i_in)<INF)
00343                 o=SimplexGetOptimalValue(safe_simplex,(j==0?LowerLimit(i_in):-UpperLimit(i_in)),b,lp);
00344               else
00345                 o=SimplexGetOptimalValueRaw(lp);
00346             }
00347 
00348           #if (_SIMPLEX_ENGINE!=_CLP) /* in CLP Reset Simplex has no effect */
00349             if ((r==EMPTY_BOX)||
00350                 (r==ERROR_IN_PROCESS)||
00351                 ((j==1)&&(r==REDUCED_BOX)&&(-o<LowerLimit(&new_range))))
00352               {
00353                 /* A second chance but with clean internal
00354                    structures. This minimizes que change of wrongly
00355                    classifying a box as empty */
00356                 ResetSimplex(lp);
00357                 r=SimplexSolve(lp);
00358               }
00359           #endif
00360 
00361           #if (_DEBUG>4)
00362             printf("==================================================================================\n");
00363           #endif
00364 
00365           switch(r)
00366             {
00367             case REDUCED_BOX:
00368               if (j==0) /*min*/
00369                 {
00370                   SetLowerLimit(o,&new_range); /*a new lower*/
00371               
00372                   #if (_DEBUG>4)
00373                     printf("   The new lower limit is %g\n",LowerLimit(&new_range));
00374                   #endif
00375                 }
00376               else /*max*/
00377                 {
00378                   SetUpperLimit(-o,&new_range); /*a new upper*/ 
00379 
00380                   #if (_DEBUG>4)
00381                     printf("   The new upper limit is %g\n",UpperLimit(&new_range));
00382                     fflush(stdout);
00383                   #endif
00384                 }
00385               break;
00386 
00387             #if (_DEBUG>4)
00388             case UNBOUNDED_BOX:
00389               printf("   The range can not be bounded\n");
00390               break;
00391             #endif
00392 
00393             case EMPTY_BOX:
00394               empty=TRUE;
00395               break;
00396 
00397             case ERROR_IN_PROCESS:
00398               #if (_SIMPLEX_ENGINE!=_CLP)  /* in CLP Reset Simplex has no effect */
00399                 ResetSimplex(lp);
00400               #endif 
00401               err=TRUE;
00402               break;
00403 
00404             default:
00405               Error("Unknow Error in ReduceRange");
00406             }
00407         } /*min/max*/
00408 
00409       DeleteLinearConstraint(&obj);
00410       
00411 
00412       #if (_DEBUG>4)
00413         if (!empty)
00414           printf("   Interval out of simplex [%g %g] s:%g\n",
00415                  LowerLimit(&new_range),
00416                  UpperLimit(&new_range),
00417                  IntervalSize(&new_range));
00418         else
00419           printf("   Empty Interval out of simplex\n");
00420       #endif
00421 
00422     
00423       if ((!empty)&&(!err))
00424         {
00425           /*
00426             In some cases the simplex return empty intervals. This occurs due to
00427             numerical inestabilities in the simplex tableau monomialization process. 
00428             We treat this as an error -> the box is bisected and a brand new
00429             simplex-based reduction is triggered
00430           */
00431           if (EmptyInterval(&new_range))
00432             err=TRUE;
00433           else
00434             CopyInterval(i_in,&new_range);
00435         }
00436     }
00437   #if (_DEBUG>4)
00438   else
00439     printf("  Input interval is too small to be reduced any more\n");
00440   #endif
00441 
00442   if (empty)
00443     return(EMPTY_BOX);
00444   else
00445     {
00446       if (err)
00447         return(ERROR_IN_PROCESS);
00448       else
00449         return(REDUCED_BOX);
00450     }
00451 }