conv-exprPow.cpp
Go to the documentation of this file.
1 /* $Id: conv-exprPow.cpp 1184 2015-08-17 12:55:35Z stefan $
2  *
3  * Name: conv-exprPow.cpp
4  * Author: Pietro Belotti
5  * Purpose: methods to convexify an expression x^k, k constant
6  *
7  * (C) Carnegie-Mellon University, 2006-11.
8  * This file is licensed under the Eclipse Public License (EPL)
9  */
10 
11 #include <cassert>
12 #include <math.h>
13 #ifndef M_E
14 # define M_E 2.7182818284590452354
15 #endif
16 
17 #include "CouenneCutGenerator.hpp"
18 
19 #include "CouenneTypes.hpp"
20 #include "CouenneRootQ.hpp"
21 #include "CouenneExprAux.hpp"
22 #include "CouenneExprPow.hpp"
23 #include "CouenneExprExp.hpp"
24 #include "CouenneExprConst.hpp"
25 #include "CouenneExprClone.hpp"
26 #include "CouenneExprMul.hpp"
27 #include "CouenneExprSum.hpp"
28 #include "CouenneExprLog.hpp"
29 #include "CouennePrecisions.hpp"
30 #include "CouenneProblem.hpp"
31 
32 using namespace Couenne;
33 
34 std::map <int, CouNumber> Qroot::Qmap;
35 
36 // Create standard formulation of this expression
37 
39 
40  expression *ret;
41 
42  if (arglist_ [0] -> Type () == CONST) { // expression is a^y, reduce
43  // to exp (x * log a)
45 
46  CouNumber base = arglist_ [0] -> Value ();
47 
48  if (fabs (base - M_E) < COUENNE_EPS_SIMPL) // is base e = 2.7182818...
49  ret = new exprExp (new exprClone (arglist_ [1]));
50  else // no? convert k^x to e^(x log (k))
51  ret = new exprExp (new exprClone
52  (p -> addAuxiliary (new exprMul (new exprClone (arglist_ [1]),
53  new exprConst (log (base))))));
54  } else if (arglist_ [1] -> Type () != CONST) { // x^y, convert to exp (y*log(x));
55 
57 
58  ret = new exprExp (new exprClone (p -> addAuxiliary
59  (new exprMul
60  (new exprClone (arglist_ [1]),
61  new exprClone
62  (p -> addAuxiliary
63  (new exprLog (new exprClone (arglist_ [0]))))))));
64 
65  } else { // expression is x^k, return as it is
66 
67  // TODO: check if it's of the form ||x||_k, as this admits a
68  // better (lower) convexification -- replace exprOp::standardize
70 
71  // if binary -- NO! Bounds not known yet
72  /*if (arglist_ [0] -> isInteger () && // integer
73  (fabs (p -> Lb (arglist_ [0] -> Index ())) < COUENNE_EPS) && // >= 0
74  (fabs (p -> Ub (arglist_ [0] -> Index ()) - 1.) < COUENNE_EPS)) // <= 1
75  {
76 
77  printf ("------------------ returning binary! [%g,%g] ",
78  p -> Lb (arglist_ [0] -> Index ()),
79  p -> Ub (arglist_ [0] -> Index ()));
80  print ();
81  printf ("\n");
82 
83  return (addAux ? (p -> addAuxiliary (arglist_ [0])) : // return same variable
84  new exprAux (arglist_ [0], p -> domain ()));
85  }
86  else*/ // otherwise return normal power
87  {
88 
89  /*printf ("------------------ returning normal [%g,%g] ",
90  arglist_ [0] -> ),
91  p -> Ub (arglist_ [0] -> Index ()));
92  print ();
93  printf ("\n");*/
94 
95  return (addAux ? (p -> addAuxiliary (this)) : new exprAux (this, p -> domain ()));
96  }
97  }
98 
99  return (addAux ? (p -> addAuxiliary (ret)) : new exprAux (ret, p -> domain ()));
100 }
101 
102 
103 // generate convexification cut for constraint w = x^k
104 
105 void exprPow::generateCuts (expression *aux, //const OsiSolverInterface &si,
106  OsiCuts &cs, const CouenneCutGenerator *cg,
107  t_chg_bounds *chg, int wind,
108  CouNumber lbw, CouNumber ubw) {
109 
110  // after standardization, all such expressions are of the form x^k,
111  // with k constant
112 
113  CouNumber k = arglist_ [1] -> Value ();
114 
115  // get bounds of base
116 
117  expression *xe = arglist_ [0];
118 
119  CouNumber l, u;
120  xe -> getBounds (l, u);
121 
122  int w_ind = aux -> Index ();
123  int x_ind = xe -> Index ();
124 
125  bool cL = !chg || (chg [x_ind].lower() != t_chg_bounds::UNCHANGED) || cg -> isFirst ();
126  bool cR = !chg || (chg [x_ind].upper() != t_chg_bounds::UNCHANGED) || cg -> isFirst ();
127 
128  CouNumber
129  w = (*aux) (),
130  x = (*xe) ();
131 
132  enum auxSign aSign = cg -> Problem () -> Var (w_ind) -> sign ();
133 
134  // if xl and xu are too close, approximate it as a line: sum the
135  // segment through the two extreme points (l,l^k) and (u,u^k), and
136  // the tangent at the midpoint ((l+u)/2, ((l+u)/2)^k)
137 
138  if (fabs (u-l) < COUENNE_EPS) {
139 
140  CouNumber avg = 0.5 * (l+u),
141  avg_k_1 = safe_pow (avg, k-1, issignpower_),
142  lk = safe_pow (l, k, issignpower_),
143  uk = safe_pow (u, k, issignpower_);
144 
145  if (cL || cR)
146  cg -> createCut (cs, u*lk - l*uk + avg * avg_k_1 * (1-k), aSign,
147  w_ind, u - l + 1, x_ind, lk-uk - k * avg_k_1);
148  return;
149  }
150 
151  // classify power
152 
153  int intk = 0;
154 
155  if (k < - COUENNE_INFINITY) { // w=x^{-inf} means w=0
156  if (cL || cR) cg -> createCut (cs, 0., aSign, w_ind, 1.);
157  return;
158  }
159 
160  if (k > COUENNE_INFINITY) // w=x^{inf} means not much...
161  return;
162 
163  if (fabs (k) < COUENNE_EPS) { // w = x^0 means w=1
164  if (cL || cR) cg -> createCut (cs, 1., aSign, w_ind, 1.);
165  return;
166  }
167 
168  bool isInt = fabs (k - (double) (intk = COUENNE_round (k))) < COUENNE_EPS,
169  isInvInt = !isInt && (fabs (1./k - (double) (intk = COUENNE_round (1./k))) < COUENNE_EPS);
170 
171  // FIXME currently, convexification of signpower(x,k) is only implemented for k odd or k=2,4,6,8,10
172  // for other cases, Qroot needs to be extended
173  assert(!issignpower_ || isInt);
174 
175  // two macro-cases:
176 
177  if ( (isInt || isInvInt)
178  && (intk % 2 || issignpower_)
179  && (k > COUENNE_EPS)
180  // && (l < - COUENNE_EPS)
181  // && (u > COUENNE_EPS)
182  ) {
183 
184  // 1) k (or its inverse) is positive, integer, and odd, or signed power, and 0 is
185  // an internal point of the interval [l,u].
186 
187  // this case is somewhat simpler than the second, although if the
188  // bound interval includes the origin we have to resort to
189  // numerical procedures to find the (unique) root of a polynomial
190  // Q(x) (see Liberti and Pantelides, 2003).
191 
192  CouNumber q = 0.;
193 
194  if ((l<0.) && (u>0.)) {
195 
196  Qroot qmap;
197  q = qmap (intk);
198  }
199 
200  int sign;
201 
202  if (isInvInt) {
203  if (cg -> isFirst ()) {
204  w = (l>0.) ? 1. : (u<0.) ? -1. : 0.;
205  x = 0;
206  }
207  q = safe_pow (q, k, issignpower_);
208  sign = -1;
209  }
210  else {
211  if (cg -> isFirst ()) {
212  x = (l>0) ? l : (u<0) ? u : 0;
213  w = 0;
214  }
215  sign = 1;
216  }
217 
218  // don't want big coefficients -- check only when k>1
219  CouNumber powThres = (k<=1) ? COUENNE_INFINITY : pow (COU_MAX_COEFF, 1./k);
220 
221  // lower envelope
222  if ((aSign != expression::AUX_LEQ) && (l > -powThres)) {
223  if (l>=0.) addPowEnvelope (cg, cs, w_ind, x_ind, x, w, k, l, u, sign, issignpower_); // 0<l<u, tangents only
224  else if (u > q * l) { // upper x is after "turning point", add lower envelope
225  addPowEnvelope (cg, cs, w_ind, x_ind, x, w, k, q*l, u, sign, issignpower_);
226  cg -> addSegment (cs, w_ind, x_ind, l, safe_pow (l,k, issignpower_), q*l, safe_pow (q*l,k, issignpower_), sign);
227  } else cg -> addSegment (cs, w_ind, x_ind, l, safe_pow (l,k, issignpower_), u, safe_pow (u, k, issignpower_), sign);
228  }
229 
230  // upper envelope
231  if ((aSign != expression::AUX_GEQ) && (u < powThres)) {
232  if (u<=0.) addPowEnvelope (cg, cs, w_ind, x_ind, x, w, k, l, u, -sign, issignpower_); // l<u<0, tangents only
233  else if (l < q * u) { // lower x is before "turning point", add upper envelope
234  addPowEnvelope (cg, cs, w_ind, x_ind, x, w, k, l, q*u, -sign, issignpower_);
235  cg -> addSegment (cs, w_ind, x_ind, q*u, safe_pow (q*u,k, issignpower_), u, safe_pow (u,k, issignpower_), -sign);
236  } else {
237  cg -> addSegment (cs, w_ind, x_ind, l, safe_pow (l,k, issignpower_), u, safe_pow (u,k, issignpower_), -sign);
238  }
239  }
240  }
241  else {
242 
243  // 2) all other cases.
244 
245  // if k is real or inv(k) is even, then lift l to max (0,l) but if
246  // u is also negative, there is no convexification -- this
247  // function is only defined on non-negative numbers
248 
249  if (!isInt
250  && (!isInvInt || !(intk % 2 || issignpower_))
251  && (l < - COUENNE_EPS)
252  && (u < (l=0))) // CAUTION! l updated if negative (k real)
253  return;
254 
255  // if k is negative and 0 is an internal point of [l,u], no
256  // convexification is possible -- just add a segment between two
257  // tails of the asymptotes.
258 
259  if ((k < 0) &&
260  (l < - COUENNE_EPS) &&
261  (u > COUENNE_EPS) &&
262  aSign != expression::AUX_LEQ) {
263 
264  if (!(intk % 2))
265  cg -> addSegment (cs, w_ind, arglist_ [0] -> Index (),
266  l, safe_pow (l,k, issignpower_), u, safe_pow (u,k, issignpower_), +1);
267 
268  // TODO: if a<=w<=b, c<=x<=d, there is a diamond enclosing the
269  // whole convexification
270 
271  return;
272  }
273 
274  // Between l and u we have a convex/concave function that needs to
275  // be enveloped. Standard segment and tangent cuts can be applied.
276 
277  // create upper envelope (segment)
278 
279  int sign = 1; // sign based on k
280 
281  // invert sign if
282  if ( ((l < - COUENNE_EPS) && (intk % 2) && (k < -COUENNE_EPS)) // k<0 odd, l<0
283  || ((u <= 0.) && (intk % 2 || issignpower_) && (k > COUENNE_EPS)) // k>0 odd or signed power, u<0
284  || (fabs (k-0.5) < 0.5 - COUENNE_EPS)) // k in [0,1]
285  sign = -1;
286 
287  CouNumber powThres = CoinMin (COUENNE_INFINITY,
288  pow (COU_MAX_COEFF, 1./k)), // don't want big coefficients
289  powStep = 1;
290 
291  // lower envelope for k negative even
292  if ((k < COUENNE_EPS) &&
293  isInt && !(intk % 2 || issignpower_) &&
294  (l < -COUENNE_EPS) && // bounds do not contain 0
295  (u > COUENNE_EPS) &&
296  (l > - powThres) && // and are finite
297  (u < powThres) &&
298  aSign != expression::AUX_LEQ)
299 
300  cg -> addSegment (cs, w_ind, x_ind, l, safe_pow (l, k, issignpower_), u, safe_pow (u, k, issignpower_), 1);
301 
302  // upper envelope
303  if (( (k > COUENNE_EPS) // when k negative, add only if
304  || (l > COUENNE_EPS) // bounds do not contain 0
305  || (u < - COUENNE_EPS)) &&
306  (l > - powThres) && // and are finite
307  (u < powThres) &&
308  (fabs (l+u) > COUENNE_EPS) &&
309  (aSign != expression::AUX_GEQ)) // bounds are not opposite (otherwise it's a variable bound)
310 
311  cg -> addSegment (cs, w_ind, x_ind, l, safe_pow (l, k, issignpower_), u, safe_pow (u, k, issignpower_), -sign);
312 
313  // similarly, pay attention not to add infinite slopes
314 
315  if (cg -> isFirst()) {
316  x = (k<0) ? ((u<0) ? u : (l>0) ? l : 0) : 0;
317  w = 0;
318  }
319 
320  if (k > COUENNE_EPS) {
321 
322  if (u > powThres) u = CoinMax (x,l) + powStep;
323  if (l < - powThres) l = CoinMin (x,u) - powStep;
324  }
325  else {
326 
327  if (fabs (l) < COUENNE_EPS) l = 1. / powThres; // l --> 0+
328  if (fabs (u) < COUENNE_EPS) u = -1. / powThres; // u --> 0-
329  }
330 
331  if ((sign == 1 && aSign != expression::AUX_LEQ) ||
332  (sign == -1 && aSign != expression::AUX_GEQ))
333  addPowEnvelope (cg, cs, w_ind, x_ind, x, w, k, l, u, sign, issignpower_);
334  }
335 }
Cut Generator for linear convexifications.
static Bigint *ULong * xe
Definition: OSdtoa.cpp:1707
#define COUENNE_round(x)
CouExpr & log(CouExpr &e)
status of lower/upper bound of a variable, to be checked/modified in bound tightening ...
const char & lower() const
virtual exprAux * standardize(CouenneProblem *, bool addAux=true)
generate auxiliary variable
Definition: exprOp.cpp:142
const char & upper() const
virtual exprAux * standardize(CouenneProblem *p, bool addAux=true)
reduce expression in standard form, creating additional aux variables (and constraints) ...
void addPowEnvelope(const CouenneCutGenerator *, OsiCuts &, int, int, CouNumber, CouNumber, CouNumber, CouNumber, CouNumber, int, bool=false)
add upper/lower envelope to power in convex/concave areas
constant-type operator
#define COUENNE_EPS_SIMPL
virtual enum nodeType Type() const
Node type.
virtual int Index() const
Return index of variable (only valid for exprVar and exprAux)
auxSign
&quot;sign&quot; of the constraint defining an auxiliary.
virtual void getBounds(expression *&, expression *&)
Get lower and upper bound of an expression (if any)
Class for MINLP problems with symbolic information.
#define COU_MAX_COEFF
void fint fint * k
expression clone (points to another expression)
#define COUENNE_EPS
bool issignpower_
do we mean a signed power function: sign(arg0) * |arg0|^arg1 (assumes that arg1 is constant) ...
static std::map< int, CouNumber > Qmap
Maps an integer k with the root of .
expression ** arglist_
argument list is an array of pointers to other expressions
double CouNumber
main number type in Couenne
class that stores result of previous calls to rootQ into a map for faster access
#define COUENNE_INFINITY
class logarithm,
Auxiliary variable.
#define M_E
virtual void generateCuts(expression *w, OsiCuts &cs, const CouenneCutGenerator *cg, t_chg_bounds *=NULL, int=-1, CouNumber=-COUENNE_INFINITY, CouNumber=COUENNE_INFINITY)
generate equality between *this and *w
CouNumber safe_pow(CouNumber base, CouNumber exponent, bool signpower=false)
compute power and check for integer-and-odd inverse exponent
Expression base class.
class for the exponential,
void fint fint fint real fint real real real real real real real real * w
virtual CouNumber Value() const
value (empty)
void fint fint fint real fint real * x
class for multiplications,