exprMul-upperHull.cpp
Go to the documentation of this file.
1 /* $Id: exprMul-upperHull.cpp 786 2011-11-17 04:10:29Z pbelotti $
2  *
3  * Name: exprMul-upperHull.cpp
4  * Author: Pietro Belotti
5  * Purpose: generates upper envelope of a product
6  *
7  * (C) Pietro Belotti, 2010.
8  * This file is licensed under the Eclipse Public License (EPL)
9  */
10 
11 #include "CouenneCutGenerator.hpp"
12 
13 #include "CouenneExprMul.hpp"
14 #include "CouennePrecisions.hpp"
15 
16 namespace Couenne {
17 
18  //#define DEBUG
19 
22  CouNumber *wl, CouNumber *wu,
23  CouNumber *xA, CouNumber *yA,
24  CouNumber *xB, CouNumber *yB);
25 
27  CouNumber x2, CouNumber y2,
28  char whichUse,
29  CouNumber &cX, CouNumber &cY, CouNumber &cW);
30 
31 
32 // invert interval bounds and current point
33 inline void invertInterval (register double &l, register double &u, register double x) {
34 
35  register double tmp = l;
36  l = -u;
37  u = -tmp;
38 
39  x = -x;
40 }
41 
42 void upperEnvHull (const CouenneCutGenerator *cg, OsiCuts &cs,
43  int xi, CouNumber x0, CouNumber xl, CouNumber xu,
44  int yi, CouNumber y0, CouNumber yl, CouNumber yu,
45  int wi, CouNumber w0, CouNumber wl, CouNumber wu) {
46 
47  //
48  // See forthcoming paper for explanation ;-)
49  //
50  // Summarized version available at
51  //
52  // http://www.neos-guide.org/NEOS/images/a/aa/ViewsAndNews-22%281%29.pdf
53 
54 #ifdef DEBUG
55  printf ("entering points: ===================================================\n");
56  printf ("x [%d]: %9g\t[%9g\t%9g]\n", xi, x0, xl, xu);
57  printf ("y [%d]: %9g\t[%9g\t%9g]\n", yi, y0, yl, yu);
58  printf ("w [%d]: %9g\t[%9g\t%9g]\n", wi, w0, wl, wu);
59 #endif
60 
61  // Preprocess to reduce everything to a first-orthant problem
62 
63  if ((wl < 0 && wu > 0)) // nothing to tighten
64  return;
65 
66  // project back into bounding box
67  if (x0 < xl) x0 = xl; if (x0 > xu) x0 = xu;
68  if (y0 < yl) y0 = yl; if (y0 > yu) y0 = yu;
69 
70  // preliminary bound tightening
71  if (wl >= 0) {
72  if ((xl >= 0) || (yl >= 0) || (xl * yl < wl - COUENNE_EPS)) {
73  if (xl < 0) xl = 0;
74  if (yl < 0) yl = 0;
75  } else if ((xu <= 0) || (yu <= 0) || (xu * yu < wl - COUENNE_EPS)) {
76  if (xu > 0) xu = 0;
77  if (yu > 0) yu = 0;
78  }
79  } else {
80  if ((xl >= 0) || (yu <= 0) || (xl * yu > wu + COUENNE_EPS)) {
81  if (xl < 0) xl = 0;
82  if (yu > 0) yu = 0;
83  } else if ((xu <= 0) || (yl >= 0) || (xu * yl > wu + COUENNE_EPS)) {
84  if (xu > 0) xu = 0;
85  if (yl < 0) yl = 0;
86  }
87  }
88 
89  // Reduce
90 
91  bool
92  flipX = xl < 0,
93  flipY = yl < 0,
94  flipW = false;
95 
96  if (flipX && flipY) { // swap bounds on x and y
97 
98  invertInterval (xl,xu,x0);
99  invertInterval (yl,yu,y0);
100 
101  } else if (flipX) { // swap bounds on x and w only
102 
103  invertInterval (xl,xu,x0);
104  invertInterval (wl,wu,w0);
105 
106  flipW = true;
107 
108  } else if (flipY) { // swap bounds on y and w only
109 
110  invertInterval (yl,yu,y0);
111  invertInterval (wl,wu,w0);
112 
113  flipW = true;
114  }
115 
116 #ifdef DEBUG
117  printf ("reduced points:\n");
118  printf ("x: %9g\t[%9g\t%9g]\n", x0, xl, xu);
119  printf ("y: %9g\t[%9g\t%9g]\n", y0, yl, yu);
120  printf ("w: %9g\t[%9g\t%9g]\n", w0, wl, wu);
121 #endif
122 
123  // Check whether lower and upper curve both contain bounding box of
124  // x,y. If so, there is nothing to separate
125 
126  if (((xl*yl >= wl) && // b.box totally contained between two curves
127  (xu*yu <= wu)) || //
128  (x0*y0 >= w0)) // or current point below curve
129  return;
130 
131  // Find intersections of halfline from origin
132 
133  CouNumber xLow, yLow, xUpp, yUpp;
134  if (findIntersection (0., 0., x0, y0, &wl, &wu, &xLow, &yLow, &xUpp, &yUpp))
135  return; // couldn't find proper point
136 
137 #ifdef DEBUG
138  printf ("intersections:\n");
139  printf ("lower: %9g\t%9g\tprod %9g\n", xLow, yLow, xLow*yLow);
140  printf ("upper: %9g\t%9g\tprod %9g\n", xUpp, yUpp, xUpp*yUpp);
141 #endif
142 
143  // Case 1: both are outside of bounding box, either NW or SE. McCormick's suffice.
144 
145  if ((xLow <= xl && yUpp >= yu) ||
146  (yLow <= yl && xUpp >= xu))
147  return;
148 
149  // There will be at least one cut. Define coefficients and rhs ---
150  // will have to change them back if (flipX || flipY)
151 
152  CouNumber
153  cX, cY, cW, c0, c0X, c0Y, c0W;
154 
155  if (xLow >= xl && xUpp <= xu &&
156  yLow >= yl && yUpp <= yu) {
157 
158 #ifdef DEBUG
159  printf ("easy lifting:\n");
160 #endif
161 
162  // Case 2: both are inside. Easy lifting...
163  if (genMulCoeff (xLow, yLow, xUpp, yUpp, 0, cX, cY, cW))
164  return;
165 
166  c0X = cX * xLow;
167  c0Y = cY * yLow;
168  c0W = cW * wl;
169 
170  } else if (xLow >= xl && yLow >= yl &&
171  (xUpp > xu || yUpp > yu)) {
172 
173 #ifdef DEBUG
174  printf ("through lower, not upper:\n");
175 #endif
176 
177  // Case 3a and 3b: through lower curve, but not upper.
178 
179  if (yUpp > yu) { // upper intersect is North; place it within box
180  yUpp = yu;
181  xUpp = CoinMin (xu, wu / yu);
182  } else { // East
183  xUpp = xu;
184  yUpp = CoinMin (yu, wu / xu);
185  }
186 
187  // find intersection on low curve on half line through new point and (x0,y0)
188  if ((findIntersection (xUpp, yUpp, x0, y0, &wl, NULL, &xLow, &yLow, NULL, NULL)) ||
189  (xLow < xl || yLow < yl) || // McCormick's suffice
190  (genMulCoeff (xLow, yLow, xUpp, yUpp, 0, cX, cY, cW))) // Otherwise, lift inequality on lower point
191  return;
192 
193  c0X = cX * xLow;
194  c0Y = cY * yLow;
195  c0W = cW * wl;
196 
197  } else if (xUpp <= xu && yUpp <= yu &&
198  (xLow < xl || yLow < yl)) {
199 
200 #ifdef DEBUG
201  printf ("through upper, not lower:\n");
202 #endif
203 
204  // Case 4a and 4b: viceversa (lift for validity)
205 
206  if (yLow < yl) { // upper intersect is South; place it within box
207  yLow = yl;
208  xLow = CoinMax (xl, wl / yl);
209  } else { // West
210  xLow = xl;
211  yLow = CoinMax (yl, wl / xl);
212  }
213 
214  // find intersection on low curve on half line through new point and (x0,y0)
215  if ((findIntersection (xLow, yLow, x0, y0, NULL, &wu, NULL, NULL, &xUpp, &yUpp)) ||
216  (xUpp > xu || yUpp > yu) || // McCormick's suffice
217  (genMulCoeff (xLow, yLow, xUpp, yUpp, 1, cX, cY, cW))) // Otherwise, lift inequality on UPPER point
218  return;
219 
220  c0X = cX * xUpp;
221  c0Y = cY * yUpp;
222  c0W = cW * wu;
223 
224  } else if ((xLow < xl && xUpp > xu) ||
225  (yLow < yl && yUpp > yu)) {
226 
227 #ifdef DEBUG
228  printf ("between lower and upper:\n");
229 #endif
230 
231  // Case 5: both outside of bounding box, N and S or W and
232  // E. Separate both from lower and from upper
233 
234  if (yLow < yl) { // upper intersect is South; place it within box
235  yLow = yl; xLow = CoinMax (xl, wl / yl);
236  yUpp = yu; xUpp = CoinMin (xu, wu / yu);
237  } else { // West
238  xLow = xl; yLow = CoinMax (yl, wl / xl);
239  xUpp = xu; yUpp = CoinMax (yu, wu / xu);
240  }
241 
242 #ifdef DEBUG
243  printf ("New intersections:\n");
244  printf ("lower: %9g\t%9g\tprod %9g\n", xLow, yLow, xLow*yLow);
245  printf ("upper: %9g\t%9g\tprod %9g\n", xUpp, yUpp, xUpp*yUpp);
246 #endif
247 
248  // Nothing to find. Just separate two inequalities at the same
249  // point, just using different support
250  //
251  // if (genMulCoeff (xLow, yLow, xUpp, yUpp, 0, cX, cY, cW) ||
252  // genMulCoeff (xLow, yLow, xUpp, yUpp, 1, cXp, cYp, cWp))
253  // return;
254 
255  // A more clever way (courtesy of Andrew J. Miller): find the
256  // intersect on the lower (upper) curve on the line through xLP
257  // and the upper (lower) point
258 
259  CouNumber xLow2 = xLow, yLow2 = yLow, xUpp2, yUpp2;
260 
261  if ((findIntersection (xLow, yLow, x0, y0, NULL, &wu, NULL, NULL, &xUpp2, &yUpp2) || genMulCoeff (xLow, yLow, xUpp2, yUpp2, 0, cX, cY, cW)) &&
262  (findIntersection (xUpp, yUpp, x0, y0, &wl, NULL, &xLow2, &yLow2, NULL, NULL) || genMulCoeff (xLow2, yLow2, xUpp, yUpp, 1, cX, cY, cW)))
263  return;
264 
265 #ifdef DEBUG
266  printf ("coeffs: (%g,%g,%g)\n", // [(%g,%g,%g)]\n",
267  cX,cY,cW);
268 #endif
269 
270  c0X = cX * xLow2; // c0Xp = cXp * xUpp;
271  c0Y = cY * yLow2; // c0Yp = cYp * yUpp;
272  c0W = cW * wl; // c0Wp = cWp * wu;
273 
274 // twoIneqs = true;
275 
276  } else {
277 
278 #ifdef DEBUG
279  printf ("points are in a weird position:\n");
280  printf ("lower: %9g\t%9g\tprod %9g\n", xLow, yLow, xLow*yLow);
281  printf ("upper: %9g\t%9g\tprod %9g\n", xUpp, yUpp, xUpp*yUpp);
282 #endif
283 
284  return;
285  }
286 
287  // Re-transform back into original variables
288 
289  if (flipX) {cX = -cX; c0X = -c0X;}
290  if (flipY) {cY = -cY; c0Y = -c0Y;}
291  if (flipW) {cW = -cW; c0W = -c0W;}
292 
293  c0 = c0X + c0Y + c0W;
294 
295 #ifdef DEBUG
296  printf ("there are cuts\n");
297 #endif
298 
299  //cg -> createCut (cs, alpha*wb + 2*wb/xt, sign, wi, alpha, yi, 1., xi, wb/(xt*xt));
300  cg -> createCut (cs, c0, 1, wi, cW, yi, cY, xi, cX);
301 }
302 
303 
304 // finds intersections of a parametric line (x,y) = (x0,y0) + t [(x1,y1) - (x0,y0)]
305 // on curves xy = wl and xy = yu
306 
308  CouNumber x1, CouNumber y1,
309  CouNumber *wl, CouNumber *wu,
310  CouNumber *xA, CouNumber *yA,
311  CouNumber *xB, CouNumber *yB) {
312 
313  // The parametric line is of the form
314  //
315  // x = x0 + t (x1-x0)
316  // y = y0 + t (y1-y0)
317  //
318  // and for that to satisfy xy = wl and xy = wu we must have
319  //
320  // x0 y0 + t [x0 (y1-y0) + y0 (x1-x0)] + t^2 (x1-x0) (y1-y0) = wl
321  // = wu
322  //
323  // or a t^2 + b t + c - wl = 0 for proper values of a,b,c.
324  // a t^2 + b t + c - wu = 0
325  //
326  // Because of the way this procedure will be used, of the two
327  // solutions found we must always use the minimum nonnegative one
328 
329  CouNumber
330  a = (x1-x0) * (y1-y0),
331  c = x0 * y0,
332  b = x0*y1 + y0*x1 - 2*c; // x0 * (y1-y0) + y0 * (x1-x0)
333 
334  if (fabs (a) < COUENNE_EPS)
335  return 1;
336 
337  // These are the solution to the above equation.
338 
339  CouNumber tL1, tL2, tU1, tU2, tL=0., tU=0.;
340 
341  if (wl) {
342  tL1 = (- b - sqrt (b*b - 4*a*(c-*wl))) / (2*a);
343  tL2 = (- b + sqrt (b*b - 4*a*(c-*wl))) / (2*a);
344  //printf ("solutions L: %g %g (b2-4ac=%g)\n", tL1, tL2, b*b - 4*a*(c-*wl));
345  tL = (tL1 < 0) ? tL2 : tL1;
346  }
347 
348  if (wu) {
349  tU1 = (- b - sqrt (b*b - 4*a*(c-*wu))) / (2*a);
350  tU2 = (- b + sqrt (b*b - 4*a*(c-*wu))) / (2*a);
351  //printf ("solutions U: %g %g (b2-4ac=%g)\n", tU1, tU2, b*b - 4*a*(c-*wu));
352  tU = (tU1 < 0) ? tU2 : tU1;
353  }
354 
355  if (xA) *xA = x0 + tL * (x1-x0); if (yA) *yA = y0 + tL * (y1-y0);
356  if (xB) *xB = x0 + tU * (x1-x0); if (yB) *yB = y0 + tU * (y1-y0);
357 
358  return 0;
359 }
360 
361 
362 // generate coefficients for a plane through points (x1, y1, x1 y1)
363 // and (x2, y2, x2 y2) such that intersecting it with one of them (the
364 // first if whichUse==0, the second otherwise) gives a tangent to the
365 // curve xy = k.
366 
368  CouNumber x2, CouNumber y2,
369  char whichUse,
370  CouNumber &cX, CouNumber &cY, CouNumber &cW) {
371 
372  // the x-y slope of this constraint must be tangent to a curve xy=k
373  // at (xD,yD). Easy:
374 
375  CouNumber xD, yD, xO, yO;
376 
377  if (!whichUse) {
378  xD = x1; xO = x2;
379  yD = y1; yO = y2;
380  } else {
381  xD = x2; xO = x1;
382  yD = y2; yO = y1;
383  }
384 
385  cX = yD;
386  cY = xD;
387  //c0 = 2*xD*yD;
388 
389 #ifdef DEBUG
390  printf ("points: (%g,%g) (%g,%g), cW = (%g - %g) / %g = %g\n",
391  xD,yD, xO,yO, 2*xD*yD, (cX*xO + cY*yO), (xO*yO - xD*yD),
392  (2*xD*yD - (cX*xO + cY*yO)) / (xO*yO - xD*yD));
393 #endif
394 
395  // Now the hard part: lift it so that it touches the other curve
396 
397  if (fabs (xO*yO - xD*yD) < COUENNE_EPS)
398  return 1; // no cut if the two points are on the same curve
399 
400  // should ALWAYS be negative
401  cW = (2*xD*yD - (cX*xO + cY*yO)) / (xO*yO - xD*yD);
402 
403  return 0;
404 }
405 
406 }
Cut Generator for linear convexifications.
int genMulCoeff(CouNumber x1, CouNumber y1, CouNumber x2, CouNumber y2, char whichUse, CouNumber &cX, CouNumber &cY, CouNumber &cW)
ULong x2
Definition: OSdtoa.cpp:1776
void fint fint fint real * a
void invertInterval(register double &l, register double &u, register double x)
ULong * x0
Definition: OSdtoa.cpp:1776
ULong x1
Definition: OSdtoa.cpp:1776
void upperEnvHull(const CouenneCutGenerator *cg, OsiCuts &cs, int xi, CouNumber x0, CouNumber xl, CouNumber xu, int yi, CouNumber y0, CouNumber yl, CouNumber yu, int wi, CouNumber w0, CouNumber wl, CouNumber wu)
better cuts than those from unifiedProdCuts
#define COUENNE_EPS
int findIntersection(CouNumber x0, CouNumber y0, CouNumber x1, CouNumber y1, CouNumber *wl, CouNumber *wu, CouNumber *xA, CouNumber *yA, CouNumber *xB, CouNumber *yB)
double CouNumber
main number type in Couenne
return b
Definition: OSdtoa.cpp:1719
real c
void fint fint fint real fint real * x