/home/coin/SVN-release/OS-2.4.2/Couenne/src/convex/operators/exprMul-upperHull.cpp

Go to the documentation of this file.
00001 /* $Id: exprMul-upperHull.cpp 490 2011-01-14 16:07:12Z pbelotti $ 
00002  *
00003  * Name:    exprMul-upperHull.cpp
00004  * Author:  Pietro Belotti
00005  * Purpose: generates upper envelope of a product
00006  *
00007  * (C) Pietro Belotti, 2010.
00008  * This file is licensed under the Eclipse Public License (EPL)
00009  */
00010 
00011 #include "CouenneCutGenerator.hpp"
00012 
00013 #include "CouenneExprMul.hpp"
00014 #include "CouennePrecisions.hpp"
00015 
00016 namespace Couenne {
00017 
00018 //#define DEBUG
00019 
00020 int findIntersection (CouNumber  x0, CouNumber  y0,
00021                       CouNumber  x1, CouNumber  y1,
00022                       CouNumber *wl, CouNumber *wu,
00023                       CouNumber *xA, CouNumber *yA,
00024                       CouNumber *xB, CouNumber *yB);
00025 
00026 int genMulCoeff (CouNumber x1, CouNumber y1, 
00027                   CouNumber x2, CouNumber y2, 
00028                   char whichUse,
00029                   CouNumber &cX, CouNumber &cY, CouNumber &cW);
00030 
00031 
00032 // invert interval bounds and current point
00033 inline void invertInterval (register double &l, register double &u, register double x) {
00034 
00035   register double tmp = l; 
00036   l = -u; 
00037   u = -tmp;
00038 
00039   x = -x;
00040 }
00041 
00042 void upperEnvHull (const CouenneCutGenerator *cg, OsiCuts &cs, 
00043                    int xi, CouNumber x0, CouNumber xl, CouNumber xu,
00044                    int yi, CouNumber y0, CouNumber yl, CouNumber yu,
00045                    int wi, CouNumber w0, CouNumber wl, CouNumber wu) {
00046 
00047   //
00048   // See forthcoming paper for explanation ;-)
00049   //
00050 
00051 #ifdef DEBUG
00052   printf ("entering points: ===================================================\n");
00053   printf ("x [%d]: %9g\t[%9g\t%9g]\n", xi, x0, xl, xu);
00054   printf ("y [%d]: %9g\t[%9g\t%9g]\n", yi, y0, yl, yu);
00055   printf ("w [%d]: %9g\t[%9g\t%9g]\n", wi, w0, wl, wu);
00056 #endif
00057 
00058   // Preprocess to reduce everything to a first-orthant problem
00059 
00060   if ((wl < 0 && wu > 0)) // nothing to tighten
00061     return;
00062 
00063   // project back into bounding box
00064   if (x0 < xl) x0 = xl;  if (x0 > xu) x0 = xu;
00065   if (y0 < yl) y0 = yl;  if (y0 > yu) y0 = yu;
00066 
00067   // preliminary bound tightening
00068   if (wl >= 0) {
00069     if        ((xl >= 0) || (yl >= 0) || (xl * yl < wl - COUENNE_EPS)) {
00070       if (xl < 0) xl = 0;
00071       if (yl < 0) yl = 0;
00072     } else if ((xu <= 0) || (yu <= 0) || (xu * yu < wl - COUENNE_EPS)) {
00073       if (xu > 0) xu = 0;
00074       if (yu > 0) yu = 0;
00075     }
00076   } else {
00077     if        ((xl >= 0) || (yu <= 0) || (xl * yu > wu + COUENNE_EPS)) {
00078       if (xl < 0) xl = 0;
00079       if (yu > 0) yu = 0;
00080     } else if ((xu <= 0) || (yl >= 0) || (xu * yl > wu + COUENNE_EPS)) {
00081       if (xu > 0) xu = 0;
00082       if (yl < 0) yl = 0;
00083     }
00084   }
00085 
00086   // Reduce
00087 
00088   bool 
00089     flipX = xl < 0,
00090     flipY = yl < 0,
00091     flipW = false;
00092 
00093   if (flipX && flipY) { // swap bounds on x and y
00094 
00095     invertInterval (xl,xu,x0);
00096     invertInterval (yl,yu,y0);
00097 
00098   } else if (flipX) { // swap bounds on x and w only
00099 
00100     invertInterval (xl,xu,x0);
00101     invertInterval (wl,wu,w0);
00102 
00103     flipW = true;
00104 
00105   } else if (flipY) { // swap bounds on y and w only
00106 
00107     invertInterval (yl,yu,y0);
00108     invertInterval (wl,wu,w0);
00109 
00110     flipW = true;
00111   }
00112 
00113 #ifdef DEBUG
00114   printf ("reduced points:\n");
00115   printf ("x: %9g\t[%9g\t%9g]\n", x0, xl, xu);
00116   printf ("y: %9g\t[%9g\t%9g]\n", y0, yl, yu);
00117   printf ("w: %9g\t[%9g\t%9g]\n", w0, wl, wu);
00118 #endif
00119 
00120   // Check whether lower and upper curve both contain bounding box of
00121   // x,y. If so, there is nothing to separate
00122 
00123   if (((xl*yl >= wl) &&  // b.box totally contained between two curves
00124        (xu*yu <= wu)) || //
00125       (x0*y0 >= w0))     // or current point below curve
00126     return;
00127 
00128   // Find intersections of halfline from origin
00129 
00130   CouNumber xLow, yLow, xUpp, yUpp;
00131   if (findIntersection (0., 0., x0, y0, &wl, &wu, &xLow, &yLow, &xUpp, &yUpp))
00132     return; // couldn't find proper point
00133 
00134 #ifdef DEBUG
00135   printf ("intersections:\n");
00136   printf ("lower: %9g\t%9g\tprod %9g\n", xLow, yLow, xLow*yLow);
00137   printf ("upper: %9g\t%9g\tprod %9g\n", xUpp, yUpp, xUpp*yUpp);
00138 #endif
00139 
00140   // Case 1: both are outside of bounding box, either NW or SE. McCormick's suffice.
00141 
00142   if ((xLow <= xl && yUpp >= yu) ||
00143       (yLow <= yl && xUpp >= xu))
00144     return;
00145 
00146   // There will be at least one cut. Define coefficients and rhs ---
00147   // will have to change them back if (flipX || flipY)
00148 
00149   CouNumber
00150     cX,  cY,  cW,  c0,  c0X,  c0Y,  c0W;
00151 
00152   if (xLow >= xl && xUpp <= xu &&
00153       yLow >= yl && yUpp <= yu) {
00154 
00155 #ifdef DEBUG
00156     printf ("easy lifting:\n");
00157 #endif
00158 
00159     // Case 2: both are inside. Easy lifting...
00160     if (genMulCoeff (xLow, yLow, xUpp, yUpp, 0, cX, cY, cW))
00161       return;
00162 
00163     c0X = cX * xLow;
00164     c0Y = cY * yLow;
00165     c0W = cW * wl;
00166 
00167   } else if (xLow >= xl && yLow >= yl && 
00168              (xUpp > xu || yUpp > yu)) {
00169 
00170 #ifdef DEBUG
00171     printf ("through lower, not upper:\n");
00172 #endif
00173 
00174     // Case 3a and 3b: through lower curve, but not upper. 
00175 
00176     if (yUpp > yu) { // upper intersect is North; place it within box
00177       yUpp = yu;
00178       xUpp = wu / yu;
00179     } else {         //                    East
00180       xUpp = xu;
00181       yUpp = wu / xu;
00182     }
00183 
00184     // find intersection on low curve on half line through new point and (x0,y0)
00185     if ((findIntersection (xUpp, yUpp, x0, y0, &wl, NULL, &xLow, &yLow, NULL, NULL)) ||
00186         (xLow < xl || yLow < yl) ||                            // McCormick's suffice
00187         (genMulCoeff (xLow, yLow, xUpp, yUpp, 0, cX, cY, cW))) // Otherwise, lift inequality on lower point
00188       return;
00189 
00190     c0X = cX * xLow;
00191     c0Y = cY * yLow;
00192     c0W = cW * wl;
00193 
00194   } else if (xUpp <= xu && yUpp <= yu && 
00195              (xLow < xl || yLow < yl)) {
00196 
00197 #ifdef DEBUG
00198     printf ("through upper, not lower:\n");
00199 #endif
00200 
00201     // Case 4a and 4b: viceversa (lift for validity)
00202 
00203     if (yLow < yl) { // upper intersect is South; place it within box
00204       yLow = yl;
00205       xLow = wl / yl;
00206     } else {         //                    West
00207       xLow = xl;
00208       yLow = wl / xl;
00209     }
00210 
00211     // find intersection on low curve on half line through new point and (x0,y0)
00212     if (findIntersection (xLow, yLow, x0, y0, NULL, &wu, NULL, NULL, &xUpp, &yUpp))
00213       return;
00214 
00215     if (xUpp > xu || yUpp > yu) // McCormick's suffice
00216       return;
00217 
00218     // Otherwise, lift inequality on UPPER point
00219     if (genMulCoeff (xLow, yLow, xUpp, yUpp, 1, cX, cY, cW))
00220       return;
00221 
00222     c0X = cX * xUpp;
00223     c0Y = cY * yUpp;
00224     c0W = cW * wu;
00225 
00226   } else if ((xLow < xl && xUpp > xu) ||
00227              (yLow < yl && yUpp > yu)) {
00228 
00229 #ifdef DEBUG
00230     printf ("between lower and upper:\n");
00231 #endif
00232 
00233     // Case 5: both outside of bounding box, N and S or W and
00234     //         E. Separate both from lower and from upper
00235 
00236     if (yLow < yl) { // upper intersect is South; place it within box
00237       yLow = yl;      xLow = wl / yl;
00238       yUpp = yu;      xUpp = wu / yu;
00239     } else {         //                    West
00240       xLow = xl;      yLow = wl / xl;
00241       xUpp = xu;      yUpp = wu / xu;
00242     }
00243 
00244 #ifdef DEBUG
00245     printf ("New intersections:\n");
00246     printf ("lower: %9g\t%9g\tprod %9g\n", xLow, yLow, xLow*yLow);
00247     printf ("upper: %9g\t%9g\tprod %9g\n", xUpp, yUpp, xUpp*yUpp);
00248 #endif
00249 
00250     // Nothing to find. Just separate two inequalities at the same
00251     // point, just using different support
00252     //
00253     // if (genMulCoeff (xLow, yLow, xUpp, yUpp, 0, cX,  cY,  cW) ||
00254     //     genMulCoeff (xLow, yLow, xUpp, yUpp, 1, cXp, cYp, cWp))
00255     //   return;
00256 
00257     // A more clever way (courtesy of Andrew J. Miller): find the
00258     // intersect on the lower (upper) curve on the line through xLP
00259     // and the upper (lower) point
00260 
00261     CouNumber xLow2, yLow2, xUpp2, yUpp2;
00262 
00263     if ((findIntersection (xLow, yLow, x0, y0, NULL, &wu, NULL,   NULL,   &xUpp2, &yUpp2) || genMulCoeff (xLow, yLow, xUpp, yUpp, 0, cX, cY, cW)) &&
00264         (findIntersection (xUpp, yUpp, x0, y0, &wl, NULL, &xLow2, &yLow2, NULL,   NULL)   || genMulCoeff (xLow, yLow, xUpp, yUpp, 1, cX, cY, cW)))
00265       return;
00266 
00267 #ifdef DEBUG
00268     printf ("coeffs: (%g,%g,%g)\n", // [(%g,%g,%g)]\n", 
00269             cX,cY,cW);
00270 #endif
00271 
00272     c0X = cX * xLow; //   c0Xp = cXp * xUpp;
00273     c0Y = cY * yLow; //   c0Yp = cYp * yUpp;
00274     c0W = cW * wl;   //   c0Wp = cWp * wu;  
00275 
00276 //     twoIneqs = true;
00277 
00278   } else {
00279 
00280 #ifdef DEBUG
00281     printf ("points are in a weird position:\n");
00282     printf ("lower: %9g\t%9g\tprod %9g\n", xLow, yLow, xLow*yLow);
00283     printf ("upper: %9g\t%9g\tprod %9g\n", xUpp, yUpp, xUpp*yUpp);
00284 #endif
00285 
00286     return;
00287   }
00288 
00289   // Re-transform back into original variables
00290 
00291   if (flipX) {cX = -cX; c0X = -c0X;}
00292   if (flipY) {cY = -cY; c0Y = -c0Y;}
00293   if (flipW) {cW = -cW; c0W = -c0W;}
00294 
00295   c0 = c0X + c0Y + c0W;
00296 
00297 #ifdef DEBUG
00298   printf ("there are cuts\n");
00299 #endif
00300 
00301   //cg -> createCut (cs, alpha*wb + 2*wb/xt, sign, wi, alpha, yi, 1., xi, wb/(xt*xt));
00302   cg   -> createCut (cs, c0,  1, wi, cW,  yi, cY,  xi, cX);
00303 }
00304 
00305 
00306 // finds intersections of a parametric line (x,y) = (x0,y0) + t [(x1,y1) - (x0,y0)] 
00307 // on curves xy = wl and xy = yu
00308 
00309 int findIntersection (CouNumber  x0, CouNumber  y0,
00310                       CouNumber  x1, CouNumber  y1,
00311                       CouNumber *wl, CouNumber *wu,
00312                       CouNumber *xA, CouNumber *yA,
00313                       CouNumber *xB, CouNumber *yB) {
00314 
00315   // The parametric line is of the form
00316   //
00317   //  x = x0 + t (x1-x0)
00318   //  y = y0 + t (y1-y0)
00319   // 
00320   // and for that to satisfy xy = wl and xy = wu we must have
00321   //
00322   // x0 y0 + t [x0 (y1-y0) + y0 (x1-x0)] + t^2 (x1-x0) (y1-y0) = wl
00323   //                                                           = wu
00324   //
00325   // or a t^2 + b t + c - wl = 0 for proper values of a,b,c.
00326   //    a t^2 + b t + c - wu = 0
00327   //
00328   // Because of the way this procedure will be used, of the two
00329   // solutions found we must always use the minimum nonnegative one
00330 
00331   CouNumber
00332     a = (x1-x0) * (y1-y0),
00333     c = x0 * y0,
00334     b = x0*y1 + y0*x1 - 2*c; // x0 * (y1-y0) + y0 * (x1-x0)
00335 
00336   if (fabs (a) < COUENNE_EPS)
00337     return 1;
00338 
00339   // These are the solution to the above equation.
00340 
00341   CouNumber tL1, tL2, tU1, tU2, tL=0., tU=0.;
00342 
00343   if (wl) {
00344     tL1 = (- b - sqrt (b*b - 4*a*(c-*wl))) / (2*a);
00345     tL2 = (- b + sqrt (b*b - 4*a*(c-*wl))) / (2*a);
00346     //printf ("solutions L: %g %g (b2-4ac=%g)\n", tL1, tL2, b*b - 4*a*(c-*wl));
00347     tL = (tL1 < 0) ? tL2 : tL1;
00348   }
00349 
00350   if (wu) {
00351     tU1 = (- b - sqrt (b*b - 4*a*(c-*wu))) / (2*a);
00352     tU2 = (- b + sqrt (b*b - 4*a*(c-*wu))) / (2*a);
00353     //printf ("solutions U: %g %g (b2-4ac=%g)\n", tU1, tU2, b*b - 4*a*(c-*wu));
00354     tU = (tU1 < 0) ? tU2 : tU1;
00355   }
00356 
00357   if (xA) *xA = x0 + tL * (x1-x0);   if (yA) *yA = y0 + tL * (y1-y0);
00358   if (xB) *xB = x0 + tU * (x1-x0);   if (yB) *yB = y0 + tU * (y1-y0);
00359 
00360   return 0;
00361 }
00362 
00363 
00364 // generate coefficients for a plane through points (x1, y1, x1 y1)
00365 // and (x2, y2, x2 y2) such that intersecting it with one of them (the
00366 // first if whichUse==0, the second otherwise) gives a tangent to the
00367 // curve xy = k.
00368 
00369 int genMulCoeff (CouNumber x1, CouNumber y1, 
00370                   CouNumber x2, CouNumber y2, 
00371                   char whichUse,
00372                   CouNumber &cX, CouNumber &cY, CouNumber &cW) {
00373 
00374   // the x-y slope of this constraint must be tangent to a curve xy=k
00375   // at (xD,yD). Easy:
00376 
00377   CouNumber xD, yD, xO, yO;
00378 
00379   if (!whichUse) {
00380     xD = x1; xO = x2;
00381     yD = y1; yO = y2;
00382   } else {
00383     xD = x2; xO = x1;
00384     yD = y2; yO = y1;
00385   }
00386 
00387   cX = yD;
00388   cY = xD;
00389   //c0 = 2*xD*yD;
00390 
00391 #ifdef DEBUG
00392     printf ("points: (%g,%g) (%g,%g), cW = (%g - %g) / %g = %g\n", 
00393             xD,yD, xO,yO, 2*xD*yD, (cX*xO + cY*yO), (xO*yO - xD*yD),
00394             (2*xD*yD - (cX*xO + cY*yO)) / (xO*yO - xD*yD));
00395 #endif
00396 
00397   // Now the hard part: lift it so that it touches the other curve
00398 
00399   if (fabs (xO*yO - xD*xD) < COUENNE_EPS) 
00400     return 1; // no cut if the two points are on the same curve
00401 
00402   // should ALWAYS be negative
00403   cW = (2*xD*yD - (cX*xO + cY*yO)) / (xO*yO - xD*yD);
00404 
00405   return 0;
00406 }
00407 
00408 }

Generated on Wed Nov 30 03:03:59 2011 by  doxygen 1.4.7