Scippy

SCIP

Solving Constraint Integer Programs

cons_bivariate.c
Go to the documentation of this file.
1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the program and library */
4 /* SCIP --- Solving Constraint Integer Programs */
5 /* */
6 /* Copyright (C) 2002-2014 Konrad-Zuse-Zentrum */
7 /* fuer Informationstechnik Berlin */
8 /* */
9 /* SCIP is distributed under the terms of the ZIB Academic License. */
10 /* */
11 /* You should have received a copy of the ZIB Academic License */
12 /* along with SCIP; see the file COPYING. If not email to scip@zib.de. */
13 /* */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15 
16 /**@file cons_bivariate.c
17  * @brief constraint handler for bivariate nonlinear constraints \f$\textrm{lhs} \leq f(x,y) + c z \leq \textrm{rhs}\f$
18  * @author Martin Ballerstein
19  * @author Dennis Michaels
20  * @author Stefan Vigerske
21  */
22 
23 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
24 
25 #include <assert.h>
26 #include <math.h>
27 
28 #include "scip/cons_bivariate.h"
29 #include "scip/cons_linear.h"
30 #include "scip/cons_quadratic.h"
31 #include "scip/cons_nonlinear.h"
32 #include "scip/heur_subnlp.h"
33 #include "scip/heur_trysol.h"
34 #include "scip/debug.h"
35 #include "nlpi/nlpi.h"
36 #include "nlpi/exprinterpret.h"
37 
38 /* constraint handler properties */
39 #define CONSHDLR_NAME "bivariate"
40 #define CONSHDLR_DESC "constraint handler for constraints of the form lhs <= f(x,y) + c*z <= rhs where f(x,y) is a bivariate function"
41 #define CONSHDLR_SEPAPRIORITY 5 /**< priority of the constraint handler for separation */
42 #define CONSHDLR_ENFOPRIORITY -55 /**< priority of the constraint handler for constraint enforcing */
43 #define CONSHDLR_CHECKPRIORITY -3600000 /**< priority of the constraint handler for checking feasibility */
44 #define CONSHDLR_SEPAFREQ 1 /**< frequency for separating cuts; zero means to separate only in the root node */
45 #define CONSHDLR_PROPFREQ 1 /**< frequency for propagating domains; zero means only preprocessing propagation */
46 #define CONSHDLR_EAGERFREQ 100 /**< frequency for using all instead of only the useful constraints in separation,
47  * propagation and enforcement, -1 for no eager evaluations, 0 for first only */
48 #define CONSHDLR_MAXPREROUNDS -1 /**< maximal number of presolving rounds the constraint handler participates in (-1: no limit) */
49 #define CONSHDLR_DELAYSEPA FALSE /**< should separation method be delayed, if other separators found cuts? */
50 #define CONSHDLR_DELAYPROP FALSE /**< should propagation method be delayed, if other propagators found reductions? */
51 #define CONSHDLR_DELAYPRESOL FALSE /**< should presolving method be delayed, if other presolvers found reductions? */
52 #define CONSHDLR_NEEDSCONS TRUE /**< should the constraint handler be skipped, if no constraints are available? */
53 #define CONSHDLR_PROP_TIMING SCIP_PROPTIMING_BEFORELP
54 
55 #define INTERVALINFTY 1E+43 /**< value for infinity in interval operations */
56 #define NEWTONMAXITER 1000 /**< maximal number of iterations in newton method */
57 #define INITLPMAXVARVAL 1000.0 /**< maximal absolute value of variable for still generating a linearization cut at that point in initlp */
58 
59 #define QUADCONSUPGD_PRIORITY 5000 /**< priority of the constraint handler for upgrading of quadratic constraints */
60 #define NONLINCONSUPGD_PRIORITY 10000 /**< priority of the constraint handler for upgrading of nonlinear constraints */
61 
62 /* activate the following define to get output on number of bivariate constraints for each convexity-type during INITSOL */
63 /* #define TYPESTATISTICS */
64 
65 /*
66  * Data structures
67  */
68 
69 /** data structure to cache data used for separation of convex-concave constraints */
70 struct SepaData_ConvexConcave
71 {
72  SCIP_Bool linearinx; /**< whether the function is linear in x */
73  SCIP_Bool lineariny; /**< whether the function is linear in y */
74  SCIP_EXPRTREE* f_yfixed; /**< expression tree for f(x,yfixed) */
75  SCIP_EXPRTREE* f_neg_swapped; /**< expression tree for -f(y,x) */
76  SCIP_EXPRTREE* f_neg_swapped_yfixed;/**< expression tree for -f(y,xfixed) */
77  SCIP_EXPRTREE* vred; /**< expression tree for vred to underestimate f(x,y) */
78  SCIP_EXPRTREE* vred_neg_swapped; /**< expression tree for vred to underestimate -f(y,x) */
79 };
80 /** data structure to cache data used for separation of convex-concave constraints */
81 typedef struct SepaData_ConvexConcave SEPADATA_CONVEXCONCAVE;
82 
83 /** constraint data for bivariate constraints */
84 struct SCIP_ConsData
85 {
86  SCIP_EXPRTREE* f; /**< expression tree of bivariate function f(x,y) */
87  SCIP_BIVAR_CONVEXITY convextype; /**< kind of convexity of f(x,y) */
88  SCIP_VAR* z; /**< linear variable */
89  SCIP_Real zcoef; /**< coefficient of linear variable */
90  SCIP_Real lhs; /**< left hand side */
91  SCIP_Real rhs; /**< right hand side */
92 
93  SCIP_Real activity; /**< activity of bivariate function w.r.t. current solution */
94  SCIP_Real lhsviol; /**< violation of left hand side in current solution */
95  SCIP_Real rhsviol; /**< violation of left hand side in current solution */
96 
97  unsigned int mayincreasez:1; /**< whether z can be increased without harming other constraints */
98  unsigned int maydecreasez:1; /**< whether z can be decreased without harming other constraints */
99  SCIP_Bool ispropagated; /**< whether bound tightenings on z have been propagated */
100  int eventfilterpos; /**< position of z var events in SCIP event filter */
101 
102  SCIP_EXPRGRAPHNODE* exprgraphnode; /**< node in expression graph corresponding to bivariate function */
103 
104  SEPADATA_CONVEXCONCAVE sepaconvexconcave; /**< separation data for convex-concave constraints */
105 };
106 
107 /** constraint handler data */
108 struct SCIP_ConshdlrData
109 {
110  SCIP_EXPRINT* exprinterpreter; /**< expression interpreter (computer gradients and hessians) */
111 
112  SCIP_Real mincutefficacysepa; /**< minimal efficacy of a cut in order to add it to relaxation during separation */
113  SCIP_Real mincutefficacyenfo; /**< minimal target efficacy of a cut in order to add it to relaxation during enforcement (may be ignored) */
114  SCIP_Real cutmaxrange; /**< maximal range (maximal coef / minimal coef) of a cut in order to be added to LP */
115  SCIP_Bool linfeasshift; /**< whether to make solutions in check feasible if possible */
116  int maxproprounds; /**< limit on number of propagation rounds for a single constraint within one round of SCIP propagation */
117  int ninitlprefpoints; /**< number of reference points in each direction where to compute linear support for envelope in LP initialization */
118  SCIP_Bool enfocutsremovable; /**< are cuts added during enforcement removable from the LP in the same node? */
119  char scaling; /**< scaling method of constraints in feasibility check */
120 
121  SCIP_EVENTHDLR* linvareventhdlr; /**< handler for linear variable bound change events */
122  SCIP_EVENTHDLR* nonlinvareventhdlr; /**< handler for nonlinear variable bound change events */
123  SCIP_HEUR* subnlpheur; /**< a pointer to the subNLP heuristic */
124  SCIP_HEUR* trysolheur; /**< a pointer to the TRYSOL heuristic, if available */
125  int newsoleventfilterpos;/**< filter position of new solution event handler, if catched */
126 
127  SCIP_EXPRGRAPH* exprgraph; /**< expression graph */
128  SCIP_Bool isremovedfixings; /**< whether variable fixations have been removed from the expression graph */
129  SCIP_Bool ispropagated; /**< whether the bounds on the variables in the expression graph have been propagated */
130  SCIP* scip; /**< SCIP data structure, needed in expression graph callbacks */
131 
132  SCIP_NODE* lastenfolpnode; /**< the node for which enforcement was called the last time (and some constraint was violated) */
133  int nenfolprounds; /**< counter on number of enforcement rounds for the current node */
134 };
135 
136 
137 /*
138  * Local methods
139  */
140 
141 /** translate from one value of infinity to another
142  *
143  * if val is >= infty1, then give infty2, else give val
144  */
145 #define infty2infty(infty1, infty2, val) ((val) >= (infty1) ? (infty2) : (val))
146 
147 /** processes bound tightening event */
148 static
149 SCIP_DECL_EVENTEXEC(processLinearVarEvent)
150 {
151  assert(scip != NULL);
152  assert(event != NULL);
153  assert(eventdata != NULL);
154  assert(eventhdlr != NULL);
156 
157  *((SCIP_Bool*)eventdata) = FALSE;
158 
159  return SCIP_OKAY;
160 }
161 
162 /** catches variable bound change events on the linear variable in a bivariate constraint */
163 static
165  SCIP* scip, /**< SCIP data structure */
166  SCIP_CONS* cons /**< constraint for which to catch bound change events */
167  )
168 {
169  SCIP_CONSHDLRDATA* conshdlrdata;
170  SCIP_CONSDATA* consdata;
171  SCIP_EVENTTYPE eventtype;
172 
173  assert(scip != NULL);
174  assert(cons != NULL);
175  assert(SCIPconsIsEnabled(cons));
176  assert(SCIPconsIsTransformed(cons));
177 
178  assert(SCIPconsGetHdlr(cons) != NULL);
179  conshdlrdata = SCIPconshdlrGetData(SCIPconsGetHdlr(cons));
180  assert(conshdlrdata != NULL);
181  assert(conshdlrdata->linvareventhdlr != NULL);
182 
183  consdata = SCIPconsGetData(cons);
184  assert(consdata != NULL);
185 
186  if( consdata->z == NULL )
187  return SCIP_OKAY;
188  assert(consdata->eventfilterpos == -1);
189 
190  eventtype = SCIP_EVENTTYPE_DISABLED;
191  if( !SCIPisInfinity(scip, consdata->rhs) )
192  {
193  /* if right hand side is finite, then a tightening in the lower bound of coef*linvar is of interest */
194  if( consdata->zcoef > 0.0 )
195  eventtype |= SCIP_EVENTTYPE_LBTIGHTENED;
196  else
197  eventtype |= SCIP_EVENTTYPE_UBTIGHTENED;
198  }
199  if( !SCIPisInfinity(scip, -consdata->lhs) )
200  {
201  /* if left hand side is finite, then a tightening in the upper bound of coef*linvar is of interest */
202  if( consdata->zcoef > 0.0 )
203  eventtype |= SCIP_EVENTTYPE_UBTIGHTENED;
204  else
205  eventtype |= SCIP_EVENTTYPE_LBTIGHTENED;
206  }
207 
208  SCIP_CALL( SCIPcatchVarEvent(scip, consdata->z, eventtype, conshdlrdata->linvareventhdlr, (SCIP_EVENTDATA*)&consdata->ispropagated, &consdata->eventfilterpos) );
209 
210  consdata->ispropagated = FALSE;
211 
212  return SCIP_OKAY;
213 }
214 
215 /** drops variable bound change events on the linear variable in a bivariate constraint */
216 static
218  SCIP* scip, /**< SCIP data structure */
219  SCIP_CONS* cons /**< constraint for which to catch bound change events */
220  )
221 {
222  SCIP_CONSHDLRDATA* conshdlrdata;
223  SCIP_CONSDATA* consdata;
224  SCIP_EVENTTYPE eventtype;
225 
226  assert(scip != NULL);
227  assert(cons != NULL);
228  assert(SCIPconsIsTransformed(cons));
229 
230  assert(SCIPconsGetHdlr(cons) != NULL);
231  conshdlrdata = SCIPconshdlrGetData(SCIPconsGetHdlr(cons));
232  assert(conshdlrdata != NULL);
233  assert(conshdlrdata->linvareventhdlr != NULL);
234 
235  consdata = SCIPconsGetData(cons);
236  assert(consdata != NULL);
237 
238  if( consdata->z == NULL )
239  return SCIP_OKAY;
240  assert(consdata->eventfilterpos >= 0);
241 
242  eventtype = SCIP_EVENTTYPE_DISABLED;
243  if( !SCIPisInfinity(scip, consdata->rhs) )
244  {
245  /* if right hand side is finite, then a tightening in the lower bound of coef*linvar is of interest */
246  if( consdata->zcoef > 0.0 )
247  eventtype |= SCIP_EVENTTYPE_LBTIGHTENED;
248  else
249  eventtype |= SCIP_EVENTTYPE_UBTIGHTENED;
250  }
251  if( !SCIPisInfinity(scip, -consdata->lhs) )
252  {
253  /* if left hand side is finite, then a tightening in the upper bound of coef*linvar is of interest */
254  if( consdata->zcoef > 0.0 )
255  eventtype |= SCIP_EVENTTYPE_UBTIGHTENED;
256  else
257  eventtype |= SCIP_EVENTTYPE_LBTIGHTENED;
258  }
259 
260  SCIP_CALL( SCIPdropVarEvent(scip, consdata->z, eventtype, conshdlrdata->linvareventhdlr, (SCIP_EVENTDATA*)&consdata->ispropagated, consdata->eventfilterpos) );
261  consdata->eventfilterpos = -1;
262 
263  return SCIP_OKAY;
264 }
265 
266 
267 /** processes bound change events for variables in expression graph */
268 static
269 SCIP_DECL_EVENTEXEC(processNonlinearVarEvent)
270 {
271  SCIP_CONSHDLRDATA* conshdlrdata;
272  SCIP_EVENTTYPE eventtype;
273 
274  assert(scip != NULL);
275  assert(event != NULL);
276  assert(eventdata != NULL);
277  assert(eventhdlr != NULL);
278 
279  conshdlrdata = (SCIP_CONSHDLRDATA*)SCIPeventhdlrGetData(eventhdlr);
280  assert(conshdlrdata != NULL);
281  assert(conshdlrdata->exprgraph != NULL);
282 
283  eventtype = SCIPeventGetType(event);
284  assert( eventtype & (SCIP_EVENTTYPE_BOUNDCHANGED | SCIP_EVENTTYPE_VARFIXED) );
285 
286  if( eventtype & SCIP_EVENTTYPE_BOUNDCHANGED )
287  {
288  SCIPdebugMessage("changed %s bound on expression graph variable <%s> from %g to %g\n",
289  eventtype & SCIP_EVENTTYPE_LBCHANGED ? "lower" : "upper",
291 
292  if( eventtype & SCIP_EVENTTYPE_BOUNDTIGHTENED )
293  conshdlrdata->ispropagated = FALSE;
294 
295  /* update variable bound in expression graph
296  * @todo should we add epsilon to variable range?
297  */
298  if( eventtype & SCIP_EVENTTYPE_LBCHANGED )
299  SCIPexprgraphSetVarNodeLb(conshdlrdata->exprgraph, (SCIP_EXPRGRAPHNODE*)eventdata,
300  -infty2infty(SCIPinfinity(scip), INTERVALINFTY, -SCIPeventGetNewbound(event))); /*lint !e666*/
301  else
302  SCIPexprgraphSetVarNodeUb(conshdlrdata->exprgraph, (SCIP_EXPRGRAPHNODE*)eventdata,
303  +infty2infty(SCIPinfinity(scip), INTERVALINFTY, SCIPeventGetNewbound(event))); /*lint !e666*/
304  }
305  else
306  {
307  assert(eventtype & SCIP_EVENTTYPE_VARFIXED);
308  conshdlrdata->isremovedfixings = FALSE;
309  }
310 
311  return SCIP_OKAY;
312 }
313 
314 /** callback method for variable addition in expression graph */
315 static
316 SCIP_DECL_EXPRGRAPHVARADDED( exprgraphVarAdded )
317 {
318  SCIP_CONSHDLRDATA* conshdlrdata;
319  SCIP_INTERVAL varbounds;
320  SCIP_VAR* var_;
321 
322  assert(exprgraph != NULL);
323  assert(var != NULL);
324  assert(varnode != NULL);
325 
326  var_ = (SCIP_VAR*)var;
327 
328  conshdlrdata = (SCIP_CONSHDLRDATA*)userdata;
329  assert(conshdlrdata != NULL);
330  assert(conshdlrdata->exprgraph == exprgraph);
331 
332  /* catch variable bound change events */
333  SCIP_CALL( SCIPcatchVarEvent(conshdlrdata->scip, (SCIP_VAR*)var, SCIP_EVENTTYPE_BOUNDCHANGED | SCIP_EVENTTYPE_VARFIXED, conshdlrdata->nonlinvareventhdlr, (SCIP_EVENTDATA*)varnode, NULL) );
334  SCIPdebugMessage("catch boundchange events on new expression graph variable <%s>\n", SCIPvarGetName(var_));
335 
336  /* set current bounds in expression graph */
337  SCIPintervalSetBounds(&varbounds,
338  -infty2infty(SCIPinfinity(conshdlrdata->scip), INTERVALINFTY, -MIN(SCIPvarGetLbLocal(var_), SCIPvarGetUbLocal(var_))), /*lint !e666*/
339  +infty2infty(SCIPinfinity(conshdlrdata->scip), INTERVALINFTY, MAX(SCIPvarGetLbLocal(var_), SCIPvarGetUbLocal(var_))) /*lint !e666*/
340  );
341  SCIPexprgraphSetVarNodeBounds(exprgraph, varnode, varbounds);
342 
343  SCIP_CALL( SCIPaddVarLocks(conshdlrdata->scip, var_, 1, 1) );
344  SCIPdebugMessage("increased up- and downlocks of variable <%s>\n", SCIPvarGetName(var_));
345 
346  conshdlrdata->isremovedfixings &= SCIPvarIsActive(var_);
347  conshdlrdata->ispropagated = FALSE;
348 
349  return SCIP_OKAY;
350 }
351 
352 /** callback method for variable removal in expression graph */
353 static
354 SCIP_DECL_EXPRGRAPHVARREMOVE( exprgraphVarRemove )
355 {
356  SCIP_CONSHDLRDATA* conshdlrdata;
357  SCIP_VAR* var_;
358 
359  assert(exprgraph != NULL);
360  assert(var != NULL);
361  assert(varnode != NULL);
362 
363  var_ = (SCIP_VAR*)var;
364 
365  conshdlrdata = (SCIP_CONSHDLRDATA*)userdata;
366  assert(conshdlrdata != NULL);
367  assert(conshdlrdata->exprgraph == exprgraph);
368 
369  SCIP_CALL( SCIPdropVarEvent(conshdlrdata->scip, var_, SCIP_EVENTTYPE_BOUNDCHANGED | SCIP_EVENTTYPE_VARFIXED, conshdlrdata->nonlinvareventhdlr, (SCIP_EVENTDATA*)varnode, -1) );
370  SCIPdebugMessage("drop boundchange events on expression graph variable <%s>\n", SCIPvarGetName(var_));
371 
372  SCIP_CALL( SCIPaddVarLocks(conshdlrdata->scip, var_, -1, -1) );
373  SCIPdebugMessage("decreased up- and downlocks of variable <%s>\n", SCIPvarGetName(var_));
374 
375  return SCIP_OKAY;
376 }
377 
378 /** locks linear variable in a constraint */
379 static
381  SCIP* scip, /**< SCIP data structure */
382  SCIP_CONS* cons, /**< constraint where to lock a variable */
383  SCIP_VAR* var, /**< variable to lock */
384  SCIP_Real coef /**< coefficient of variable in constraint */
385  )
386 {
387  SCIP_CONSDATA* consdata;
388 
389  assert(scip != NULL);
390  assert(cons != NULL);
391  assert(var != NULL);
392  assert(coef != 0.0);
393 
394  consdata = SCIPconsGetData(cons);
395  assert(consdata != NULL);
396 
397  if( coef > 0.0 )
398  {
399  SCIP_CALL( SCIPlockVarCons(scip, var, cons, !SCIPisInfinity(scip, -consdata->lhs), !SCIPisInfinity(scip, consdata->rhs)) );
400  }
401  else
402  {
403  SCIP_CALL( SCIPlockVarCons(scip, var, cons, !SCIPisInfinity(scip, consdata->rhs), !SCIPisInfinity(scip, -consdata->lhs)) );
404  }
405 
406  return SCIP_OKAY;
407 }
408 
409 /** unlocks linear variable in a constraint */
410 static
412  SCIP* scip, /**< SCIP data structure */
413  SCIP_CONS* cons, /**< constraint where to unlock a variable */
414  SCIP_VAR* var, /**< variable to unlock */
415  SCIP_Real coef /**< coefficient of variable in constraint */
416  )
417 {
418  SCIP_CONSDATA* consdata;
419 
420  assert(scip != NULL);
421  assert(cons != NULL);
422  assert(var != NULL);
423  assert(coef != 0.0);
424 
425  consdata = SCIPconsGetData(cons);
426  assert(consdata != NULL);
427 
428  if( coef > 0.0 )
429  {
430  SCIP_CALL( SCIPunlockVarCons(scip, var, cons, !SCIPisInfinity(scip, -consdata->lhs), !SCIPisInfinity(scip, consdata->rhs)) );
431  }
432  else
433  {
434  SCIP_CALL( SCIPunlockVarCons(scip, var, cons, !SCIPisInfinity(scip, consdata->rhs), !SCIPisInfinity(scip, -consdata->lhs)) );
435  }
436 
437  return SCIP_OKAY;
438 }
439 
440 /** resolves variable fixations and aggregations in a constraint */
441 static
443  SCIP* scip, /**< SCIP data structure */
444  SCIP_CONSHDLR* conshdlr, /**< constraint handler */
445  SCIP_CONS* cons, /**< constraint where to remove fixed variables */
446  SCIP_Bool* ischanged, /**< buffer to store whether something was changed in the constraint */
447  SCIP_Bool* isupgraded /**< buffer to store whether the constraint has been upgraded (and deleted) */
448  )
449 {
450 #ifndef NDEBUG
451  SCIP_CONSHDLRDATA* conshdlrdata;
452 #endif
453  SCIP_CONSDATA* consdata;
454  SCIP_EXPR* substexpr[2];
455  SCIP_VAR* var;
456  SCIP_VAR* vars[2];
457  SCIP_Real coef;
458  SCIP_Real constant;
459  int i;
460 
461  assert(conshdlr != NULL);
462  assert(scip != NULL);
463  assert(cons != NULL);
464  assert(ischanged != NULL);
465  assert(isupgraded != NULL);
466 
467 #ifndef NDEBUG
468  conshdlrdata = SCIPconshdlrGetData(conshdlr);
469  assert(conshdlrdata != NULL);
470 #endif
471 
472  consdata = SCIPconsGetData(cons);
473  assert(consdata != NULL);
474  assert(consdata->f != NULL);
475 
476  *ischanged = FALSE;
477  *isupgraded = FALSE;
478 
479  if( consdata->z != NULL && !SCIPvarIsActive(consdata->z) && SCIPvarGetStatus(consdata->z) != SCIP_VARSTATUS_MULTAGGR )
480  {
481  /* replace z by active or multaggr. variable */
482 
483  /* drop events on z, unlock and release variable */
484  SCIP_CALL( dropLinearVarEvents(scip, cons) );
485  SCIP_CALL( unlockLinearVariable(scip, cons, consdata->z, consdata->zcoef) );
486 
487  /* replace by new variable, or NULL */
488  constant = 0.0;
489  SCIP_CALL( SCIPgetProbvarSum(scip, &consdata->z, &consdata->zcoef, &constant) );
490  if( consdata->zcoef == 0.0 )
491  consdata->z = NULL;
492  if( constant != 0.0 && !SCIPisInfinity(scip, -consdata->lhs) )
493  consdata->lhs -= constant;
494  if( constant != 0.0 && !SCIPisInfinity(scip, consdata->rhs) )
495  consdata->rhs -= constant;
496 
497  if( consdata->z != NULL )
498  {
499  /* catch events on new z, lock and capture variable, mark as not to multaggr */
500  SCIP_CALL( catchLinearVarEvents(scip, cons) );
501  SCIP_CALL( lockLinearVariable(scip, cons, consdata->z, consdata->zcoef) );
502  if( SCIPvarIsActive(consdata->z) )
503  {
504  SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, consdata->z) );
505  }
506  }
507 
508  *ischanged = TRUE;
509  }
510 
511  assert(SCIPexprtreeGetNVars(consdata->f) == 2);
512  vars[0] = SCIPexprtreeGetVars(consdata->f)[0];
513  vars[1] = SCIPexprtreeGetVars(consdata->f)[1];
514 
517  SCIPvarGetProbvar(vars[0]) == SCIPvarGetProbvar(vars[1]) )
518  {
519  /* if number of variable reduces, then upgrade to nonlinear constraint
520  * except if we are in the exit-presolving stage, where upgrading is not allowed
521  * in the latter case, we just do nothing, which may not be most efficient, but should still work
522  */
523  SCIP_EXPRTREE* tree;
524  SCIP_CONS* nlcons;
525 
527  return SCIP_OKAY;
528 
529  SCIP_CALL( SCIPexprtreeCopy(SCIPblkmem(scip), &tree, consdata->f) );
530 
531  for( i = 0; i < 2; ++i )
532  {
533  substexpr[i] = NULL;
534 
535  var = vars[i];
537  continue;
538 
539  coef = 1.0;
540  constant = 0.0;
541  SCIP_CALL( SCIPgetProbvarSum(scip, &var, &coef, &constant) );
542 
543  if( coef == 0.0 )
544  {
545  /* replace var_i by constant in expression tree */
546  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &substexpr[i], SCIP_EXPR_CONST, constant) );
547  vars[0] = NULL;
548  }
549  else if( coef == 1.0 && constant == 0.0 )
550  {
551  /* do not need to change expression tree, just store new variable in tree */
552  substexpr[i] = NULL;
553  vars[i] = var;
554  }
555  else
556  {
557  /* replace var_i by coef * var_i + constant in expression tree */
558  SCIP_EXPR* child;
559 
560  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &child, SCIP_EXPR_VARIDX, i) );
561  SCIP_CALL( SCIPexprCreateLinear(SCIPblkmem(scip), &substexpr[i], 1, &child, &coef, constant) );
562  vars[i] = var;
563  }
564  }
565 
566  assert(substexpr[0] != NULL || substexpr[1] != NULL);
567 
568  SCIP_CALL( SCIPexprtreeSubstituteVars(tree, substexpr) );
569  if( substexpr[0] != NULL )
570  SCIPexprFreeDeep(SCIPblkmem(scip), &substexpr[0]);
571  if( substexpr[1] != NULL )
572  SCIPexprFreeDeep(SCIPblkmem(scip), &substexpr[1]);
573 
574  /* if variable 0 has been remove or is the same as variable 1, reindex 1 to 0 */
575  if( (vars[0] == NULL || vars[0] == vars[1]) && vars[1] != NULL )
576  {
577  int reindex[2];
578 
579  reindex[0] = 0;
580  reindex[1] = 0;
582  vars[0] = vars[1];
583  vars[1] = NULL;
584  }
585 
586  /* update variables array in tree */
587  assert(vars[1] == NULL || vars[0] != NULL);
588  SCIP_CALL( SCIPexprtreeSetVars(tree, vars[0] == NULL ? 0 : (vars[1] == NULL ? 1 : 2), vars) );
589 
590  SCIP_CALL( SCIPcreateConsNonlinear(scip, &nlcons, SCIPconsGetName(cons),
591  consdata->z != NULL ? 1 : 0, consdata->z != NULL ? &consdata->z : NULL, &consdata->zcoef,
592  1, &tree, NULL, consdata->lhs, consdata->rhs,
596  SCIPconsIsStickingAtNode(cons)) ); /*lint !e826*/
597  SCIP_CALL( SCIPaddCons(scip, nlcons) );
598  SCIP_CALL( SCIPreleaseCons(scip, &nlcons) );
599 
600  *isupgraded = TRUE;
601 
602  SCIP_CALL( SCIPexprtreeFree(&tree) );
603 
604  return SCIP_OKAY;
605  }
606 
607  for( i = 0; i < 2; ++i )
608  {
609  substexpr[i] = NULL;
610 
611  var = vars[i];
613  continue;
614 
615  coef = 1.0;
616  constant = 0.0;
617  SCIP_CALL( SCIPgetProbvarSum(scip, &var, &coef, &constant) );
618  assert(coef != 0.0); /* fixed vars should have been handled above */
619 
620  if( coef == 1.0 && constant == 0.0 )
621  {
622  /* do not need to change expression tree, just store new variable in tree */
623  substexpr[i] = NULL;
624  vars[i] = var;
625  }
626  else
627  {
628  /* replace var_i by coef * var_i + constant in expression tree */
629  SCIP_EXPR* child;
630 
631  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &child, SCIP_EXPR_VARIDX, i) );
632  SCIP_CALL( SCIPexprCreateLinear(SCIPblkmem(scip), &substexpr[i], 1, &child, &coef, constant) );
633  vars[i] = var;
634  }
635 
636  /* update variables array in tree for next operation */
637  SCIP_CALL( SCIPexprtreeSetVars(consdata->f, 2, vars) );
638 
639  /* mark that variables in constraint should not be multiaggregated (bad for bound tightening and branching) */
640  if( SCIPvarIsActive(vars[0]) )
641  {
642  SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, vars[0]) );
643  }
644  if( SCIPvarIsActive(vars[1]) )
645  {
646  SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, vars[1]) );
647  }
648 
649  *ischanged = TRUE;
650  }
651 
652  /* update expression tree, if necessary */
653  if( substexpr[0] != NULL || substexpr[1] != NULL )
654  {
655  SCIP_CALL( SCIPexprtreeSubstituteVars(consdata->f, substexpr) );
656  if( substexpr[0] != NULL )
657  SCIPexprFreeDeep(SCIPblkmem(scip), &substexpr[0]);
658  if( substexpr[1] != NULL )
659  SCIPexprFreeDeep(SCIPblkmem(scip), &substexpr[1]);
660  }
661 
662  return SCIP_OKAY;
663 }
664 
665 /** removes fixed variables from expression graph */
666 static
668  SCIP* scip, /**< SCIP data structure */
669  SCIP_CONSHDLR* conshdlr /**< constraint handler */
670  )
671 {
672  SCIP_CONSHDLRDATA* conshdlrdata;
673  SCIP_VAR* var;
674  SCIP_VAR** vars;
675  SCIP_Real* coefs;
676  int nvars;
677  int varssize;
678  SCIP_Real constant;
679  int i;
680  int requsize;
681  SCIPdebug( int j );
682 
683  conshdlrdata = SCIPconshdlrGetData(conshdlr);
684  assert(conshdlrdata != NULL);
685  assert(conshdlrdata->exprgraph != NULL);
686 
687  if( conshdlrdata->isremovedfixings )
688  return SCIP_OKAY;
689 
690  varssize = 5;
691  SCIP_CALL( SCIPallocBufferArray(scip, &vars, varssize) );
692  SCIP_CALL( SCIPallocBufferArray(scip, &coefs, varssize) );
693 
694  i = 0;
695  while( i < SCIPexprgraphGetNVars(conshdlrdata->exprgraph) )
696  {
697  var = (SCIP_VAR*) SCIPexprgraphGetVars(conshdlrdata->exprgraph)[i];
698  if( SCIPvarIsActive(var) )
699  {
700  ++i;
701  continue;
702  }
703 
704  do
705  {
706  vars[0] = var;
707  coefs[0] = 1.0;
708  constant = 0.0;
709  nvars = 1;
710  SCIP_CALL( SCIPgetProbvarLinearSum(scip, vars, coefs, &nvars, varssize, &constant, &requsize, TRUE) );
711 
712  if( requsize > varssize )
713  {
714  SCIP_CALL( SCIPreallocBufferArray(scip, &vars, requsize) );
715  SCIP_CALL( SCIPreallocBufferArray(scip, &coefs, requsize) );
716  varssize = requsize;
717  continue;
718  }
719 
720  }
721  while( FALSE );
722 
723 #ifdef SCIP_DEBUG
724  SCIPdebugMessage("replace fixed variable <%s> by %g", SCIPvarGetName(var), constant);
725  for( j = 0; j < nvars; ++j )
726  {
727  SCIPdebugPrintf(" %+g <%s>", coefs[j], SCIPvarGetName(vars[j]));
728  }
729  SCIPdebugPrintf("\n");
730 #endif
731 
732  SCIP_CALL( SCIPexprgraphReplaceVarByLinearSum(conshdlrdata->exprgraph, var, nvars, coefs, (void**)vars, constant) );
733 
734  i = 0;
735  }
736 
737  SCIPfreeBufferArray(scip, &vars);
738  SCIPfreeBufferArray(scip, &coefs);
739 
740  conshdlrdata->isremovedfixings = TRUE;
741 
742  return SCIP_OKAY;
743 }
744 
745 /** computes violation of a constraint */
746 static
748  SCIP* scip, /**< SCIP data structure */
749  SCIP_CONSHDLR* conshdlr, /**< constraint handler */
750  SCIP_CONS* cons, /**< constraint */
751  SCIP_SOL* sol /**< solution or NULL if LP solution should be used */
752  )
753 { /*lint --e{666}*/
754  SCIP_CONSHDLRDATA* conshdlrdata;
755  SCIP_CONSDATA* consdata;
756  SCIP_Real xyvals[2];
757  SCIP_Real zval;
758  SCIP_Real xlb;
759  SCIP_Real xub;
760  SCIP_Real ylb;
761  SCIP_Real yub;
762  SCIP_VAR* x;
763  SCIP_VAR* y;
764 
765  assert(scip != NULL);
766  assert(conshdlr != NULL);
767  assert(cons != NULL);
768 
769  conshdlrdata = SCIPconshdlrGetData(conshdlr);
770  assert(conshdlrdata != NULL);
771  assert(conshdlrdata->exprinterpreter != NULL);
772 
773  consdata = SCIPconsGetData(cons);
774  assert(consdata != NULL);
775 
776  if( SCIPexprtreeGetInterpreterData(consdata->f) == NULL )
777  {
778  SCIP_CALL( SCIPexprintCompile(conshdlrdata->exprinterpreter, consdata->f) );
779  }
780 
781  x = SCIPexprtreeGetVars(consdata->f)[0];
782  y = SCIPexprtreeGetVars(consdata->f)[1];
783 
784  xyvals[0] = SCIPgetSolVal(scip, sol, x);
785  xyvals[1] = SCIPgetSolVal(scip, sol, y);
786  zval = SCIPgetSolVal(scip, sol, consdata->z);
787 
788  /* @todo proper handling of variables at infinity
789  * for now, just say infeasible and keep fingers crossed
790  */
791  if( SCIPisInfinity(scip, REALABS(xyvals[0])) )
792  {
793  consdata->lhsviol = consdata->rhsviol = SCIPinfinity(scip);
794  return SCIP_OKAY;
795  }
796 
797  if( SCIPisInfinity(scip, REALABS(xyvals[1])) )
798  {
799  consdata->lhsviol = consdata->rhsviol = SCIPinfinity(scip);
800  return SCIP_OKAY;
801  }
802 
803  /* project point onto box if from LP or very close to bounds to avoid eval error when function is not defined slightly outside bounds */
804  xlb = SCIPvarGetLbGlobal(x);
805  xub = SCIPvarGetUbGlobal(x);
806  ylb = SCIPvarGetLbGlobal(y);
807  yub = SCIPvarGetUbGlobal(y);
808  if( sol == NULL )
809  {
810  assert(SCIPisFeasGE(scip, xyvals[0], xlb));
811  assert(SCIPisFeasLE(scip, xyvals[0], xub));
812  xyvals[0] = MAX(xlb, MIN(xub, xyvals[0]));
813 
814  assert(SCIPisFeasGE(scip, xyvals[1], ylb));
815  assert(SCIPisFeasLE(scip, xyvals[1], yub));
816  xyvals[1] = MAX(ylb, MIN(yub, xyvals[1]));
817 
818  assert(SCIPisFeasGE(scip, zval, SCIPvarGetLbLocal(consdata->z)));
819  assert(SCIPisFeasLE(scip, zval, SCIPvarGetUbLocal(consdata->z)));
820  zval = MAX(SCIPvarGetLbLocal(consdata->z), MIN(SCIPvarGetUbLocal(consdata->z), zval));
821  }
822  else
823  {
824  if( SCIPisEQ(scip, xyvals[0], xlb) || SCIPisEQ(scip, xyvals[0], xub) )
825  xyvals[0] = MAX(xlb, MIN(xub, xyvals[0]));
826  if( SCIPisEQ(scip, xyvals[1], ylb) || SCIPisEQ(scip, xyvals[1], yub) )
827  xyvals[1] = MAX(ylb, MIN(yub, xyvals[1]));
828  }
829 
830  /* compute activity of constraint */
831  SCIP_CALL( SCIPexprintEval(conshdlrdata->exprinterpreter, consdata->f, xyvals, &consdata->activity) );
832 
833  /* point is outside the domain of f */
834  if( !SCIPisFinite(consdata->activity) )
835  {
836  consdata->lhsviol = consdata->rhsviol = SCIPinfinity(scip);
837  return SCIP_OKAY;
838  }
839 
840  if( consdata->z != NULL )
841  consdata->activity += consdata->zcoef * zval;
842 
843  /* compute violation of constraint sides */
844  if( consdata->activity < consdata->lhs && !SCIPisInfinity(scip, -consdata->lhs) )
845  consdata->lhsviol = consdata->lhs - consdata->activity;
846  else
847  consdata->lhsviol = 0.0;
848 
849  if( consdata->activity > consdata->rhs && !SCIPisInfinity(scip, consdata->rhs) )
850  consdata->rhsviol = consdata->activity - consdata->rhs;
851  else
852  consdata->rhsviol = 0.0;
853 
854  switch( conshdlrdata->scaling )
855  {
856  case 'o' :
857  /* no scaling */
858  break;
859 
860  case 'g' :
861  /* scale by sup-norm of gradient in current point */
862  if( consdata->lhsviol > 0.0 || consdata->rhsviol > 0.0 )
863  {
864  SCIP_Real grad[2];
865  SCIP_Real norm;
866  SCIP_Real val;
867 
868  /* compute gradient of f in (x,y) */
869  SCIP_CALL( SCIPexprintGrad(conshdlrdata->exprinterpreter, consdata->f, xyvals, TRUE, &val, grad) );
870 
871  if( SCIPisFinite(grad[0]) && SCIPisFinite(grad[1]) )
872  {
873  /* compute maximal absolute element of gradient, to use for scaling if > 1.0 */
874  norm = MAX(REALABS(grad[0]), REALABS(grad[1]));
875  if( consdata->z != NULL )
876  norm = MAX(norm, REALABS(consdata->zcoef));
877 
878  if( norm > 1.0 )
879  {
880  consdata->lhsviol /= norm;
881  consdata->rhsviol /= norm;
882  }
883  }
884  }
885  break;
886 
887  case 's' :
888  /* scale by left/right hand side of constraint */
889  if( consdata->lhsviol > 0.0 )
890  consdata->lhsviol /= MAX(1.0, REALABS(consdata->lhs));
891 
892  if( consdata->rhsviol > 0.0 )
893  consdata->rhsviol /= MAX(1.0, REALABS(consdata->rhs));
894 
895  break;
896 
897  default :
898  SCIPerrorMessage("Unknown scaling method '%c'.", conshdlrdata->scaling);
899  SCIPABORT();
900  return SCIP_INVALIDDATA; /*lint !e527*/
901  }
902 
903  return SCIP_OKAY;
904 }
905 
906 /** computes violation of a set of constraints */
907 static
909  SCIP* scip, /**< SCIP data structure */
910  SCIP_CONSHDLR* conshdlr, /**< constraint handler */
911  SCIP_CONS** conss, /**< constraints */
912  int nconss, /**< number of constraints */
913  SCIP_SOL* sol, /**< solution or NULL if LP solution should be used */
914  SCIP_CONS** maxviolcon /**< buffer to store constraint with largest violation, or NULL if solution is feasible */
915  )
916 {
917  SCIP_CONSDATA* consdata;
918  SCIP_Real viol;
919  SCIP_Real maxviol;
920  int c;
921 
922  assert(scip != NULL);
923  assert(conshdlr != NULL);
924  assert(conss != NULL || nconss == 0);
925  assert(maxviolcon != NULL);
926 
927  *maxviolcon = NULL;
928 
929  maxviol = 0.0;
930 
931  for( c = 0; c < nconss; ++c )
932  {
933  assert(conss != NULL);
934  assert(conss[c] != NULL);
935 
936  SCIP_CALL( computeViolation(scip, conshdlr, conss[c], sol) );
937 
938  consdata = SCIPconsGetData(conss[c]);
939  assert(consdata != NULL);
940 
941  viol = MAX(consdata->lhsviol, consdata->rhsviol);
942  if( viol > maxviol && SCIPisGT(scip, viol, SCIPfeastol(scip)) )
943  {
944  maxviol = viol;
945  *maxviolcon = conss[c];
946  }
947  }
948 
949  return SCIP_OKAY;
950 }
951 
952 /** setup vred(s;x0,y0,ylb,yub) for a given f(x,y) for computing a convex-concave underestimator
953  * vred(s;x0,y0,ylb,yub) = (yub-y0)/(yub-ylb) f((yub-ylb)/(yub-y0)x0 - (y0-ylb)/(yub-y0)*s, ylb) + (y0-ylb)/(yub-ylb) f(s,yub)
954  */
955 static
957  SCIP* scip, /**< SCIP data structure */
958  SCIP_EXPRTREE** vred, /**< buffer where to store exprtree for vred */
959  SCIP_EXPRTREE* f /**< function f(x,y) for which vred should be setup */
960  )
961 {
962  SCIP_EXPR* subst[2];
963  SCIP_Real minusone;
964  SCIP_EXPR* e1;
965  SCIP_EXPR* e2;
966  SCIP_EXPR* e3;
967  SCIP_EXPR* e4;
968  SCIP_EXPR* e5;
969  SCIP_EXPR* e6;
970  SCIP_EXPR* arg1;
971  SCIP_EXPR* arg2;
972  SCIP_EXPR* vredexpr;
973 
974  assert(scip != NULL);
975  assert(vred != NULL);
976  assert(f != NULL);
977  assert(SCIPexprGetOperator(SCIPexprtreeGetRoot(f)) != SCIP_EXPR_VARIDX); /* substitute cannot substitute the root node, but f should not be a single variable anyway */
978 
979  /* setup vred(s;x0,y0,ylb,yub) for computing a convex-concave underestimator in the case where y is not at one of its bounds
980  * vred(s;x0,y0,ylb,yub) = (yub-y0)/(yub-ylb) f((yub-ylb)/(yub-y0)x0 - (y0-ylb)/(yub-y0)*s, ylb) + (y0-ylb)/(yub-ylb) f(s,yub)
981  */
982  /* create expression for x0(yub-ylb)/(yub-y0) */
983  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &e1, SCIP_EXPR_PARAM, 2) ); /* ylb */
984  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &e2, SCIP_EXPR_PARAM, 3) ); /* yub */
985  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &e3, SCIP_EXPR_MINUS, e2, e1) ); /* yub-ylb */
986 
987  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &e1, SCIP_EXPR_PARAM, 0) ); /* x0 */
988  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &e3, SCIP_EXPR_MUL, e1, e3) ); /* x0(yub-ylb) */
989 
990  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &e1, SCIP_EXPR_PARAM, 1) ); /* y0 */
991  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &e2, SCIP_EXPR_PARAM, 3) ); /* yub */
992  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &e4, SCIP_EXPR_MINUS, e2, e1) ); /* yub-y0 */
993 
994  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &e5, SCIP_EXPR_DIV, e3, e4) ); /* x0(yub-ylb)/(yub-y0) */
995 
996  /* create expression for s(y0-ylb)/(yub-y0) */
997  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &e1, SCIP_EXPR_PARAM, 1) ); /* y0 */
998  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &e2, SCIP_EXPR_PARAM, 2) ); /* ylb */
999  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &e3, SCIP_EXPR_MINUS, e1, e2) ); /* y0-ylb */
1000 
1001  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &e1, SCIP_EXPR_VARIDX, 0) ); /* s */
1002  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &e3, SCIP_EXPR_MUL, e1, e3) ); /* s(y0-ylb) */
1003 
1004  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &e1, SCIP_EXPR_PARAM, 1) ); /* y0 */
1005  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &e2, SCIP_EXPR_PARAM, 3) ); /* yub */
1006  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &e4, SCIP_EXPR_MINUS, e2, e1) ); /* yub-y0 */
1007 
1008  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &e6, SCIP_EXPR_DIV, e3, e4) ); /* s(y0-ylb)/(yub-y0) */
1009 
1010  /* create expression for (yub-ylb)/(yub-y0)x0 - (y0-ylb)/(yub-y0)*s */
1011  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &subst[0], SCIP_EXPR_MINUS, e5, e6) );
1012 
1013  /* create expression for ylb */
1014  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &subst[1], SCIP_EXPR_PARAM, 2) );
1015 
1016  /* create expression for f((yub-ylb)/(yub-y0)x0 - (y0-ylb)/(yub-y0)*s, ylb) */
1018  SCIP_CALL( SCIPexprSubstituteVars(SCIPblkmem(scip), arg1, subst) );
1019  SCIPexprFreeDeep(SCIPblkmem(scip), &subst[0]);
1020  SCIPexprFreeDeep(SCIPblkmem(scip), &subst[1]);
1021 
1022  /* create expression for f(s,yub) */
1024  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &subst[1], SCIP_EXPR_PARAM, 3) );
1025  SCIP_CALL( SCIPexprSubstituteVars(SCIPblkmem(scip), arg2, subst) );
1026  SCIPexprFreeDeep(SCIPblkmem(scip), &subst[1]);
1027 
1028  /* create expression for (yub-y0)/(yub-ylb) */
1029  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &e1, SCIP_EXPR_PARAM, 1) ); /* y0 */
1030  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &e2, SCIP_EXPR_PARAM, 3) ); /* yub */
1031  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &e3, SCIP_EXPR_MINUS, e2, e1) ); /* yub-y0 */
1032 
1033  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &e1, SCIP_EXPR_PARAM, 2) ); /* ylb */
1034  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &e2, SCIP_EXPR_PARAM, 3) ); /* yub */
1035  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &e4, SCIP_EXPR_MINUS, e2, e1) ); /* yub-ylb */
1036 
1037  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &e5, SCIP_EXPR_DIV, e3, e4) ); /* (yub-y0)/(yub-ylb) */
1038 
1039  /* create expression for 1 - (yub-y0)/(yub-ylb) */
1040  minusone = -1.0;
1041  SCIP_CALL( SCIPexprCopyDeep(SCIPblkmem(scip), &e1, e5) ); /* (yub-y0)/(yub-ylb) */
1042  SCIP_CALL( SCIPexprCreateLinear(SCIPblkmem(scip), &e6, 1, &e1, &minusone, 1.0) ); /* 1 - (yub-y0)/(yub-ylb) */
1043 
1044  /* create expression for vred = e5*arg1 + e6*arg2 */
1045  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &e1, SCIP_EXPR_MUL, e5, arg1) );
1046  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &e2, SCIP_EXPR_MUL, e6, arg2) );
1047  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &vredexpr, SCIP_EXPR_PLUS, e1, e2) );
1048 
1049  SCIP_CALL( SCIPexprtreeCreate(SCIPblkmem(scip), vred, vredexpr, 1, 4, NULL) );
1050 
1051  return SCIP_OKAY;
1052 }
1053 
1054 /** initializes separation data */
1055 static
1057  SCIP* scip, /**< SCIP data structure */
1058  SCIP_EXPRINT* exprinterpreter, /**< expressions interpreter */
1059  SCIP_CONS* cons /**< constraint */
1060  )
1061 {
1062  SCIP_CONSDATA* consdata;
1063 
1064  assert(scip != NULL);
1065  assert(exprinterpreter != NULL);
1066  assert(cons != NULL);
1067 
1068  consdata = SCIPconsGetData(cons);
1069  assert(consdata != NULL);
1070  assert(consdata->f != NULL);
1071 
1072  switch( consdata->convextype )
1073  {
1075  {
1076  SCIP_VAR** xy;
1077  SCIP_Real ref[2];
1078  SCIP_Bool sparsity[4];
1079 
1080  if( SCIPexprtreeGetInterpreterData(consdata->f) == NULL )
1081  {
1082  SCIP_CALL( SCIPexprintCompile(exprinterpreter, consdata->f) );
1083  }
1084 
1085  xy = SCIPexprtreeGetVars(consdata->f);
1086  assert(xy != NULL);
1087 
1088  /* check if the function is linear in x or y */
1089  ref[0] = MIN(MAX(SCIPvarGetLbLocal(xy[0]), 0.0), SCIPvarGetUbLocal(xy[0])); /*lint !e666*/
1090  ref[1] = MIN(MAX(SCIPvarGetLbLocal(xy[1]), 0.0), SCIPvarGetUbLocal(xy[1])); /*lint !e666*/
1091 
1092  SCIP_CALL( SCIPexprintHessianSparsityDense(exprinterpreter, consdata->f, ref, sparsity) );
1093 
1094  consdata->sepaconvexconcave.linearinx = !sparsity[0];
1095  consdata->sepaconvexconcave.lineariny = !sparsity[3];
1096 
1097  if( !consdata->sepaconvexconcave.linearinx && !SCIPisInfinity(scip, consdata->rhs) )
1098  {
1099  SCIP_EXPR* subst[2];
1100  SCIP_Real one;
1101 
1102  /* setup f(x,yfixed) for computing a convex-concave underestimator in the case where y is at one of its bounds */
1103  SCIP_CALL( SCIPexprtreeCopy(SCIPblkmem(scip), &consdata->sepaconvexconcave.f_yfixed, consdata->f) );
1104 
1105  /* x stays x, nothing to substitute
1106  * y is substituted by SCIP_EXPR_PARAM
1107  */
1108  subst[0] = NULL;
1109  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &subst[1], SCIP_EXPR_PARAM, 0) );
1110 
1111  /* make y a parameter */
1112  SCIP_CALL( SCIPexprtreeSubstituteVars(consdata->sepaconvexconcave.f_yfixed, subst) );
1113 
1114  /* reset variables array to {x} and parameters array to {y} */
1115  one = 1.0;
1116  SCIP_CALL( SCIPexprtreeSetVars(consdata->sepaconvexconcave.f_yfixed, 1, &xy[0]) );
1117  SCIP_CALL( SCIPexprtreeSetParams(consdata->sepaconvexconcave.f_yfixed, 1, &one) );
1118 
1119  /* free subst[1] */
1120  SCIPexprFreeDeep(SCIPblkmem(scip), &subst[1]);
1121 
1122  SCIP_CALL( SCIPexprintCompile(exprinterpreter, consdata->sepaconvexconcave.f_yfixed) );
1123 
1124  /* setup vred(s;x0,y0,ylb,yub) for computing a convex-concave underestimator in the case where y is not at one of its bounds
1125  * vred(s;x0,y0,ylb,yub) = (yub-y0)/(yub-ylb) f((yub-ylb)/(yub-y0)x0 - (y0-ylb)/(yub-y0)*s, ylb) + (y0-ylb)/(yub-ylb) f(s,yub)
1126  */
1127  SCIP_CALL( initSepaDataCreateVred(scip, &consdata->sepaconvexconcave.vred, consdata->f) );
1128  SCIP_CALL( SCIPexprintCompile(exprinterpreter, consdata->sepaconvexconcave.vred) );
1129  }
1130  else
1131  {
1132  consdata->sepaconvexconcave.f_yfixed = NULL;
1133  consdata->sepaconvexconcave.vred = NULL;
1134  }
1135 
1136  if( !consdata->sepaconvexconcave.lineariny && !SCIPisInfinity(scip, -consdata->lhs) )
1137  {
1138  /* if we have a left hand side and are not linear y in, then we may need to call
1139  * generateConvexConcaveUnderestimator for -f with swapped variables
1140  */
1141  SCIP_EXPR* minusf;
1142  SCIP_EXPR* fcopy;
1143  SCIP_VAR* vars[2];
1144  int reindex[2];
1145  SCIP_Real minusone;
1146  SCIP_Real one;
1147  SCIP_EXPR* subst[2];
1148 
1149  /* create expression for -f */
1150  minusone = -1.0;
1151  SCIP_CALL( SCIPexprCopyDeep(SCIPblkmem(scip), &fcopy, SCIPexprtreeGetRoot(consdata->f)) );
1152  SCIP_CALL( SCIPexprCreateLinear(SCIPblkmem(scip), &minusf, 1, &fcopy, &minusone, 0.0) );
1153 
1154  /* reindex/swap variables */
1155  reindex[0] = 1;
1156  reindex[1] = 0;
1157  SCIPexprReindexVars(minusf, reindex);
1158 
1159  /* create expression tree for -f(y,x) */
1160  SCIP_CALL( SCIPexprtreeCreate(SCIPblkmem(scip), &consdata->sepaconvexconcave.f_neg_swapped, minusf, 2, 0, NULL) );
1161 
1162  vars[0] = xy[1];
1163  vars[1] = xy[0];
1164  SCIP_CALL( SCIPexprtreeSetVars(consdata->sepaconvexconcave.f_neg_swapped, 2, vars) );
1165 
1166  SCIP_CALL( SCIPexprintCompile(exprinterpreter, consdata->sepaconvexconcave.f_neg_swapped) );
1167 
1168  /* setup -f(y, xfixed) for computing a convex-concave overestimator in the case where x is at on of it's bounds */
1169  SCIP_CALL( SCIPexprtreeCopy(SCIPblkmem(scip), &consdata->sepaconvexconcave.f_neg_swapped_yfixed, consdata->sepaconvexconcave.f_neg_swapped) );
1170 
1171  /* y stays y, nothing to substitute
1172  * x is substituted by SCIP_EXPR_PARAM
1173  */
1174  subst[0] = NULL;
1175  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &subst[1], SCIP_EXPR_PARAM, 0) );
1176 
1177  /* make x a parameter */
1178  SCIP_CALL( SCIPexprtreeSubstituteVars(consdata->sepaconvexconcave.f_neg_swapped_yfixed, subst) );
1179 
1180  /* reset variables array to {y} and parameters array to {x} */
1181  one = 1.0;
1182  SCIP_CALL( SCIPexprtreeSetVars(consdata->sepaconvexconcave.f_neg_swapped_yfixed, 1, &xy[1]) );
1183  SCIP_CALL( SCIPexprtreeSetParams(consdata->sepaconvexconcave.f_neg_swapped_yfixed, 1, &one) );
1184 
1185  /* free subst[1] */
1186  SCIPexprFreeDeep(SCIPblkmem(scip), &subst[1]);
1187 
1188  SCIP_CALL( SCIPexprintCompile(exprinterpreter, consdata->sepaconvexconcave.f_neg_swapped_yfixed) );
1189 
1190  /* setup vred(s;y0,x0,xlb,xub) for computing a convex-concave underestimator in the case where x is not at one of its bounds */
1191  SCIP_CALL( initSepaDataCreateVred(scip, &consdata->sepaconvexconcave.vred_neg_swapped, consdata->sepaconvexconcave.f_neg_swapped) );
1192  SCIP_CALL( SCIPexprintCompile(exprinterpreter, consdata->sepaconvexconcave.vred_neg_swapped) );
1193  }
1194  else
1195  {
1196  consdata->sepaconvexconcave.f_neg_swapped = NULL;
1197  consdata->sepaconvexconcave.f_neg_swapped_yfixed = NULL;
1198  consdata->sepaconvexconcave.vred_neg_swapped = NULL;
1199  }
1200 
1201  break;
1202  }
1203 
1204  default: ;
1205  } /*lint !e788*/
1206 
1207  return SCIP_OKAY;
1208 }
1209 
1210 /** frees separation data */
1211 static
1213  SCIP* scip, /**< SCIP data structure */
1214  SCIP_CONS* cons /**< constraint */
1215  )
1216 {
1217  SCIP_CONSDATA* consdata;
1218 
1219  assert(scip != NULL);
1220  assert(cons != NULL);
1221 
1222  consdata = SCIPconsGetData(cons);
1223  assert(consdata != NULL);
1224  assert(consdata->f != NULL);
1225 
1226  switch( consdata->convextype )
1227  {
1229  {
1230  if( consdata->sepaconvexconcave.f_yfixed != NULL )
1231  {
1232  SCIP_CALL( SCIPexprtreeFree(&consdata->sepaconvexconcave.f_yfixed) );
1233  }
1234  if( consdata->sepaconvexconcave.f_neg_swapped != NULL )
1235  {
1236  SCIP_CALL( SCIPexprtreeFree(&consdata->sepaconvexconcave.f_neg_swapped) );
1237  }
1238  if( consdata->sepaconvexconcave.f_neg_swapped_yfixed != NULL )
1239  {
1240  SCIP_CALL( SCIPexprtreeFree(&consdata->sepaconvexconcave.f_neg_swapped_yfixed) );
1241  }
1242  if( consdata->sepaconvexconcave.vred != NULL )
1243  {
1244  SCIP_CALL( SCIPexprtreeFree(&consdata->sepaconvexconcave.vred) );
1245  }
1246  if( consdata->sepaconvexconcave.vred_neg_swapped != NULL )
1247  {
1248  SCIP_CALL( SCIPexprtreeFree(&consdata->sepaconvexconcave.vred_neg_swapped) );
1249  }
1250  break;
1251  }
1252 
1253  default: ;
1254  } /*lint !e788*/
1255 
1256  return SCIP_OKAY;
1257 }
1258 
1259 /** perturbs a value w.r.t. bounds */
1260 static
1261 void perturb(
1262  SCIP_Real* val, /**< value to perturb on input; perturbed value on output */
1263  SCIP_Real lb, /**< lower bound */
1264  SCIP_Real ub, /**< upper bound */
1265  SCIP_Real amount /**< relative amount of perturbation */
1266  )
1267 {
1268  SCIP_Real range;
1269  SCIP_Real mid;
1270 
1271  assert(val != NULL);
1272 
1273  range = ub - lb;
1274  mid = 0.5 * (lb + ub);
1275 
1276  if( *val < mid )
1277  *val += MIN(1.0, amount * range);
1278  else
1279  *val -= MIN(1.0, amount * range);
1280 }
1281 
1282 /** solves an equation f'(s) = constant for a univariate convex or concave function f with respect to bounds on s
1283  * if there is no s between the bounds such that f'(s) = constant, then it returns the closest bound (and still claims success)
1284  */
1285 static
1287  SCIP* scip, /**< SCIP data structure */
1288  SCIP_EXPRINT* exprinterpreter, /**< expressions interpreter */
1289  SCIP_EXPRTREE* f, /**< expression tree for f(s) */
1290  SCIP_Real targetvalue, /**< target value for derivative */
1291  SCIP_Real lb, /**< lower bound on variable */
1292  SCIP_Real ub, /**< upper bound on variable */
1293  SCIP_Real* val, /**< buffer to store solution value */
1294  SCIP_Bool* success /**< buffer to indicate whether a solution has been found */
1295  )
1296 {
1297  SCIP_Real fval;
1298  SCIP_Real grad;
1299  SCIP_Real hess;
1300  SCIP_Real s;
1301  SCIP_Real nexts;
1302  SCIP_Real step;
1303  int iter;
1304 
1305  assert(scip != NULL);
1306  assert(exprinterpreter != NULL);
1307  assert(f != NULL);
1308  assert(SCIPexprtreeGetInterpreterData(f) != NULL);
1309  assert(SCIPexprtreeGetNVars(f) == 1);
1310  assert(val != NULL);
1311  assert(success != NULL);
1312 
1313  if( SCIPisEQ(scip, lb, ub) )
1314  {
1315  *val = lb;
1316  *success = TRUE;
1317  return SCIP_OKAY;
1318  }
1319 
1320  *success = FALSE;
1321 
1322  iter = 0;
1323 
1324  /* start at 0.0, projected onto interior of interval
1325  * we don't want to start at a bound, because we would not recognize if hessian is 0.0 then
1326  */
1327  s = MIN(MAX(0.0, lb), ub);
1328  perturb(&s, lb, ub, 0.1);
1329 
1330  while( ++iter < NEWTONMAXITER )
1331  {
1332  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, &s, TRUE, &fval, &grad) );
1333 
1334  /* SCIPdebugMessage("s = %.20g [%g,%g] f(s) = %g grad = %g\n", s, lb, ub, fval, grad); */
1335 
1336  if( !SCIPisFinite(grad) )
1337  {
1338  /* if f cannot be differentiated at s, perturb s to some other point close by
1339  * for that, we perturb by 0.1 * 2^{-iter}, if iter <= 65, otherwise by 1e-20
1340  * if that amount is too small to get a change in s, we increase by a factor of 2
1341  */
1342  SCIP_Real amount;
1343  SCIP_Real sold;
1344 
1345  sold = s;
1346  amount = iter <= 65 ? 0.1 / (1u<<iter) : 1e-20; /*lint !e790*/
1347  do
1348  {
1349  perturb(&s, lb, ub, amount);
1350  amount *= 2.0;
1351  } while( s == sold ); /*lint !e777*/
1352 
1353  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, &s, TRUE, &fval, &grad) );
1354 
1355  /* SCIPdebugMessage("s = %.20g [%g,%g] f(s) = %g grad = %g (perturbed by %g)\n", s, lb, ub, fval, grad, iter <= 65 ? 0.1 / (1<<iter) : 1e-20); */
1356 
1357  assert(SCIPisFinite(grad));
1358  }
1359 
1360  if( SCIPisRelEQ(scip, grad, targetvalue) )
1361  {
1362  /* if grad is targetvalue (w.r.t. epsilon), then we are done */
1363  *val = s;
1364  *success = TRUE;
1365  break;
1366  }
1367 
1368  SCIP_CALL( SCIPexprintHessianDense(exprinterpreter, f, &s, FALSE, &fval, &hess) );
1369 
1370  /* SCIPdebugMessage("s = %.20g [%g,%g] f(s) = %g hess = %g\n", s, lb, ub, fval, hess); */
1371 
1372  if( !SCIPisFinite(hess) )
1373  {
1374  SCIP_Real smod;
1375  SCIP_Real smodval;
1376 
1377  /* if f cannot be two times differentiated at s, take the Hessian from another point close by */
1378  smod = s;
1379  perturb(&smod, lb, ub, 0.01);
1380  SCIP_CALL( SCIPexprintHessianDense(exprinterpreter, f, &smod, TRUE, &smodval, &hess) );
1381 
1382  assert(SCIPisFinite(hess));
1383  }
1384 
1385  /* next iterate would be s - (grad - targetvalue) / hess */
1386 
1387  if( SCIPisEQ(scip, s, lb) && (grad - targetvalue) * hess >= 0 )
1388  {
1389  /* if we are on the left boundary and would go left (or stay), then stop
1390  * (multiply instead of divide by hess for the case that hess is zero and since only the sign matters
1391  */
1392  *val = lb;
1393  *success = TRUE;
1394  break;
1395  }
1396 
1397  if( SCIPisEQ(scip, s, ub) && (grad - targetvalue) * hess <= 0 )
1398  {
1399  /* similar, if we are on the right boundary and would go right (or stay), then stop */
1400  *val = ub;
1401  *success = TRUE;
1402  break;
1403  }
1404 
1405  if( SCIPisZero(scip, hess) )
1406  {
1407  /* hmm, stationary point, don't know how to continue; thus, give up */
1408  break;
1409  }
1410 
1411  if( SCIPisZero(scip, (grad - targetvalue) / hess) && SCIPisFeasEQ(scip, grad, targetvalue) )
1412  {
1413  /* if grad is targetvalue (w.r.t. feastol) and step length would be almost 0, then we are also done */
1414  *val = s;
1415  *success = TRUE;
1416  break;
1417  }
1418 
1419  /* @todo we could also implement a damped Newton method if the step is too large */
1420  step = (grad - targetvalue) / hess;
1421  assert(step != 0.0);
1422 
1423  nexts = s - step;
1424  while( s == nexts ) /*lint !e777*/
1425  {
1426  /* if steplength is so tiny that there is no change in s, go by 1e-9 into given direction */
1427  step *= 2.0;
1428  nexts = s - step;
1429  }
1430  assert(nexts != s); /*lint !e777*/
1431  s = nexts;
1432 
1433  if( s < lb )
1434  s = lb;
1435  else if( s > ub )
1436  s = ub;
1437  }
1438 
1439  return SCIP_OKAY;
1440 }
1441 
1442 /** generates a cut for f(x,y) + c*z <= rhs with f(x,y) being convex or 1-convex with x or y fixed or convex-concave with y fixed
1443  * f(x0, y0) + <grad, (x,y)-(x0,y0)> + c*z <= rhs, where grad is gradient of f in (x0, y0)
1444  */
1445 static
1447  SCIP* scip, /**< SCIP data structure */
1448  SCIP_EXPRINT* exprinterpreter, /**< expressions interpreter */
1449  SCIP_CONS* cons, /**< constraint */
1450  SCIP_Real* x0y0, /**< value of x and y variables where to generate cut */
1451  SCIP_Bool newxy, /**< whether the last evaluation of f(x,y) with the expression interpreter was at (x0, y0) */
1452  SCIP_ROW** row /**< storage for cut */
1453  )
1454 {
1455  SCIP_VAR* x;
1456  SCIP_VAR* y;
1457  SCIP_CONSDATA* consdata;
1458  char rowname[SCIP_MAXSTRLEN];
1459  SCIP_Real fval;
1460  SCIP_Real fgrad[2];
1461  SCIP_Real rhs;
1462 
1463  assert(scip != NULL);
1464  assert(cons != NULL);
1465  assert(row != NULL);
1466 
1467  consdata = SCIPconsGetData(cons);
1468  assert(consdata != NULL);
1469  assert(!SCIPisInfinity(scip, consdata->rhs));
1470  assert(newxy || SCIPexprtreeGetInterpreterData(consdata->f) != NULL);
1471 
1472  /* compile expression if evaluated the first time; can only happen if newxy is FALSE */
1473  if( newxy && SCIPexprtreeGetInterpreterData(consdata->f) == NULL )
1474  {
1475  SCIP_CALL( SCIPexprintCompile(exprinterpreter, consdata->f) );
1476  }
1477 
1478  x = SCIPexprtreeGetVars(consdata->f)[0];
1479  y = SCIPexprtreeGetVars(consdata->f)[1];
1480 
1481  assert(consdata->convextype == SCIP_BIVAR_ALLCONVEX ||
1482  (consdata->convextype == SCIP_BIVAR_1CONVEX_INDEFINITE && (SCIPisEQ(scip, SCIPvarGetLbLocal(x), SCIPvarGetUbLocal(x)) || SCIPisEQ(scip, SCIPvarGetLbLocal(y), SCIPvarGetUbLocal(y)))) ||
1483  (consdata->convextype == SCIP_BIVAR_CONVEX_CONCAVE && SCIPisEQ(scip, SCIPvarGetLbLocal(y), SCIPvarGetUbLocal(y))) );
1484 
1485  /* compute f(x,y) and gradient of f in (x, y) */
1486  SCIP_CALL( SCIPexprintGrad(exprinterpreter, consdata->f, x0y0, newxy, &fval, fgrad) );
1487 
1488  if( !SCIPisFinite(fval) || !SCIPisFinite(fgrad[0]) || !SCIPisFinite(fgrad[1]) )
1489  {
1490  perturb(&x0y0[0], SCIPvarGetLbLocal(x), SCIPvarGetUbLocal(x), 0.001);
1491  perturb(&x0y0[1], SCIPvarGetLbLocal(y), SCIPvarGetUbLocal(y), 0.001);
1492 
1493  SCIP_CALL( SCIPexprintGrad(exprinterpreter, consdata->f, x0y0, TRUE, &fval, fgrad) );
1494 
1495  if( !SCIPisFinite(fval) || !SCIPisFinite(fgrad[0]) || !SCIPisFinite(fgrad[1]) )
1496  {
1497  SCIPdebugMessage("could not evaluate f at given reference point and perturbed one");
1498  *row = NULL;
1499  return SCIP_OKAY;
1500  }
1501  }
1502 
1503  rhs = consdata->rhs - fval + fgrad[0] * x0y0[0] + fgrad[1] * x0y0[1];
1504 
1505  /* setup SCIP row */
1506  (void) SCIPsnprintf(rowname, SCIP_MAXSTRLEN, "%s_linearization_%d", SCIPconsGetName(cons), SCIPgetNLPs(scip));
1507 
1508  SCIP_CALL( SCIPcreateEmptyRowCons(scip, row, SCIPconsGetHdlr(cons), rowname, -SCIPinfinity(scip), rhs, FALSE, FALSE /* modifiable */, TRUE /* removable */) );
1509 
1510  SCIP_CALL( SCIPaddVarsToRow(scip, *row, 2, SCIPexprtreeGetVars(consdata->f), fgrad) );
1511 
1512  if( consdata->z != NULL )
1513  SCIP_CALL( SCIPaddVarToRow(scip, *row, consdata->z, consdata->zcoef) );
1514 
1515  return SCIP_OKAY;
1516 }
1517 
1518 /** Given three points, constructs coefficient of equation for hyperplane generated by these three points.
1519  * Three points a, b, and c are given.
1520  * Given coefficients alpha, beta, gamma, and delta, such that a, b, and c, satisfy
1521  * alpha * x1 + beta * x2 + gamma * x3 = delta and gamma >= 0.0.
1522  */
1523 static
1525  SCIP_Real a1, /* first coordinate of a */
1526  SCIP_Real a2, /* second coordinate of a */
1527  SCIP_Real a3, /* third coordinate of a */
1528  SCIP_Real b1, /* first coordinate of b */
1529  SCIP_Real b2, /* second coordinate of b */
1530  SCIP_Real b3, /* third coordinate of b */
1531  SCIP_Real c1, /* first coordinate of c */
1532  SCIP_Real c2, /* second coordinate of c */
1533  SCIP_Real c3, /* third coordinate of c */
1534  SCIP_Real* alpha, /* coefficient of first coordinate */
1535  SCIP_Real* beta, /* coefficient of second coordinate */
1536  SCIP_Real* gamma_, /* coefficient of third coordinate */
1537  SCIP_Real* delta /* constant right-hand side */
1538  )
1539 {
1540  assert(alpha != NULL);
1541  assert(beta != NULL);
1542  assert(gamma_ != NULL);
1543  assert(delta != NULL);
1544 
1545  *alpha = -b3*c2 + a3*(-b2+c2) + a2*(b3-c3) + b2*c3;
1546  *beta = -(-b3*c1 + a3*(-b1+c1) + a1*(b3-c3) + b1*c3);
1547  *gamma_ = -a2*b1 + a1*b2 + a2*c1 - b2*c1 - a1*c2 + b1*c2;
1548  *delta = -a3*b2*c1 + a2*b3*c1 + a3*b1*c2 - a1*b3*c2 - a2*b1*c3 + a1*b2*c3;
1549 
1550  if( *gamma_ < 0.0 )
1551  {
1552  *alpha = -*alpha;
1553  *beta = -*beta;
1554  *gamma_ = -*gamma_;
1555  *delta = -*delta;
1556  }
1557 }
1558 
1559 /** given a convex (concave, resp.) bivariate function, computes an over- (under-, resp.) estimating hyperplane
1560  * does not succeed if some variable is unbounded or both variables are fixed
1561  */
1562 static
1564  SCIP* scip, /**< SCIP data structure */
1565  SCIP_EXPRINT* exprinterpreter, /**< expression interpreter */
1566  SCIP_EXPRTREE* f, /**< bivariate function to compute under or overestimator for */
1567  SCIP_Bool doover, /**< whether to compute an overestimator (TRUE) or an underestimator (FALSE) */
1568  SCIP_Real* x0y0, /**< reference values for nonlinear variables */
1569  SCIP_Real* coefx, /**< coefficient of x in estimator */
1570  SCIP_Real* coefy, /**< coefficient of y in estimator */
1571  SCIP_Real* constant, /**< constant part of estimator */
1572  SCIP_Bool* success /**< pointer to indicate whether coefficients where successfully computed */
1573  )
1574 {
1575  SCIP_VAR* x;
1576  SCIP_VAR* y;
1577  SCIP_Real xlb;
1578  SCIP_Real xub;
1579  SCIP_Real ylb;
1580  SCIP_Real yub;
1581 
1582  SCIP_Real p1[2];
1583  SCIP_Real p2[2];
1584  SCIP_Real p3[2];
1585  SCIP_Real p4[2];
1586  SCIP_Real p1val;
1587  SCIP_Real p2val;
1588  SCIP_Real p3val;
1589  SCIP_Real p4val;
1590 
1591  SCIP_Real alpha;
1592  SCIP_Real beta;
1593  SCIP_Real gamma_;
1594  SCIP_Real delta;
1595 
1596  SCIP_Bool tryother;
1597 
1598  assert(scip != NULL);
1599  assert(exprinterpreter != NULL);
1600  assert(f != NULL);
1601  assert(x0y0 != NULL);
1602  assert(coefx != NULL);
1603  assert(coefy != NULL);
1604  assert(constant != NULL);
1605  assert(success != NULL);
1606 
1607  *success = FALSE;
1608 
1609  x = SCIPexprtreeGetVars(f)[0];
1610  y = SCIPexprtreeGetVars(f)[1];
1611 
1612  xlb = SCIPvarGetLbLocal(x);
1613  xub = SCIPvarGetUbLocal(x);
1614  ylb = SCIPvarGetLbLocal(y);
1615  yub = SCIPvarGetUbLocal(y);
1616 
1617  /* reference point should not be outside of bounds */
1618  assert(SCIPisLE(scip, xlb, x0y0[0]));
1619  assert(SCIPisGE(scip, xub, x0y0[0]));
1620  assert(SCIPisLE(scip, ylb, x0y0[1]));
1621  assert(SCIPisGE(scip, yub, x0y0[1]));
1622 
1623  if( SCIPisInfinity(scip, -xlb) || SCIPisInfinity(scip, xub) || SCIPisInfinity(scip, -ylb) || SCIPisInfinity(scip, yub) )
1624  {
1625  SCIPdebugMessage("skip estimating hyperplane since <%s> or <%s> is unbounded\n", SCIPvarGetName(x), SCIPvarGetName(y));
1626  return SCIP_OKAY;
1627  }
1628 
1629  if( SCIPisEQ(scip, xlb, xub) && SCIPisEQ(scip, ylb, yub) )
1630  {
1631  SCIPdebugMessage("skip estimating hyperplane since both <%s> and <%s> are fixed\n", SCIPvarGetName(x), SCIPvarGetName(y));
1632  return SCIP_OKAY;
1633  }
1634 
1635  /* unten links */
1636  p1[0] = xlb;
1637  p1[1] = ylb;
1638 
1639  /* unten rechts */
1640  p2[0] = xub;
1641  p2[1] = ylb;
1642 
1643  /* oben rechts */
1644  p3[0] = xub;
1645  p3[1] = yub;
1646 
1647  /* oben links */
1648  p4[0] = xlb;
1649  p4[1] = yub;
1650 
1651  if( SCIPisEQ(scip, xlb, xub) )
1652  {
1653  /* secant between p1 and p4: p1val + [(p4val - p1val) / (yub - ylb)] * (y - ylb) */
1654  assert(!SCIPisEQ(scip, ylb, yub));
1655 
1656  SCIP_CALL( SCIPexprintEval(exprinterpreter, f, p1, &p1val) );
1657  SCIP_CALL( SCIPexprintEval(exprinterpreter, f, p4, &p4val) );
1658 
1659  if( !SCIPisFinite(p1val) || SCIPisInfinity(scip, REALABS(p1val)) || !SCIPisFinite(p4val) || SCIPisInfinity(scip, REALABS(p4val)) )
1660  {
1661  SCIPdebugMessage("skip hyperplane since function cannot be evaluated\n");
1662  return SCIP_OKAY;
1663  }
1664 
1665  *coefx = 0.0;
1666  *coefy = (p4val - p1val) / (yub - ylb);
1667  *constant = p1val - *coefy * ylb;
1668 
1669  *success = TRUE;
1670 
1671  return SCIP_OKAY;
1672  }
1673 
1674  if( SCIPisEQ(scip, ylb, yub) )
1675  {
1676  /* secant between p1 and p2: p1val + [(p2val - p1val) / (xub - xlb)] * (x - xlb) */
1677  assert(!SCIPisEQ(scip, xlb, xub));
1678 
1679  SCIP_CALL( SCIPexprintEval(exprinterpreter, f, p1, &p1val) );
1680  SCIP_CALL( SCIPexprintEval(exprinterpreter, f, p2, &p2val) );
1681 
1682  if( !SCIPisFinite(p1val) || SCIPisInfinity(scip, REALABS(p1val)) || !SCIPisFinite(p2val) || SCIPisInfinity(scip, REALABS(p2val)) )
1683  {
1684  SCIPdebugMessage("skip hyperplane since function cannot be evaluated\n");
1685  return SCIP_OKAY;
1686  }
1687 
1688  *coefx = (p2val - p1val) / (xub - xlb);
1689  *coefy = 0.0;
1690  *constant = p1val - *coefx * xlb;
1691 
1692  *success = TRUE;
1693 
1694  return SCIP_OKAY;
1695  }
1696 
1697  SCIP_CALL( SCIPexprintEval(exprinterpreter, f, p1, &p1val) );
1698  SCIP_CALL( SCIPexprintEval(exprinterpreter, f, p2, &p2val) );
1699  SCIP_CALL( SCIPexprintEval(exprinterpreter, f, p3, &p3val) );
1700  SCIP_CALL( SCIPexprintEval(exprinterpreter, f, p4, &p4val) );
1701 
1702  /* if we want an underestimator, flip f(x,y), i.e., do as if we compute an overestimator for -f(x,y) */
1703  if( !doover )
1704  {
1705  p1val = -p1val;
1706  p2val = -p2val;
1707  p3val = -p3val;
1708  p4val = -p4val;
1709  }
1710 
1711  SCIPdebugMessage("p1 = (%g, %g), f(p1) = %g\n", p1[0], p1[1], p1val);
1712  SCIPdebugMessage("p2 = (%g, %g), f(p2) = %g\n", p2[0], p2[1], p2val);
1713  SCIPdebugMessage("p3 = (%g, %g), f(p3) = %g\n", p3[0], p3[1], p3val);
1714  SCIPdebugMessage("p4 = (%g, %g), f(p4) = %g\n", p4[0], p4[1], p4val);
1715 
1716  if( !SCIPisFinite(p1val) || SCIPisInfinity(scip, REALABS(p1val)) || !SCIPisFinite(p2val) || SCIPisInfinity(scip, REALABS(p2val)) ||
1717  ! SCIPisFinite(p3val) || SCIPisInfinity(scip, REALABS(p3val)) || !SCIPisFinite(p4val) || SCIPisInfinity(scip, REALABS(p4val)) )
1718  {
1719  SCIPdebugMessage("skip hyperplane since function cannot be evaluated\n");
1720  return SCIP_OKAY;
1721  }
1722 
1723  /* compute coefficients alpha, beta, gamma (>0), delta such that
1724  * alpha*x + beta*y + gamma*z = delta
1725  * is satisfied by at least three of the corner points (p1,f(p1)), ..., (p4,f(p4)) and
1726  * the fourth corner point lies below this hyperplane.
1727  * Since we assume that f is convex, we then know that all points (x,y,f(x,y)) are below this hyperplane, i.e.,
1728  * alpha*x + beta*y - delta <= -gamma * f(x,y),
1729  * or, equivalently,
1730  * -alpha/gamma*x - beta/gamma*y + delta/gamma >= f(x,y).
1731  */
1732 
1733  tryother = FALSE;
1734  if( x0y0[1] <= ylb + (yub - ylb)/(xub - xlb) * (x0y0[0] - xlb) )
1735  {
1736  getAlphaBetaGammaDelta(p1[0], p1[1], p1val, p2[0], p2[1], p2val, p3[0], p3[1], p3val, &alpha, &beta, &gamma_, &delta);
1737  assert(SCIPisInfinity(scip, delta) || SCIPisFeasEQ(scip, alpha * p1[0] + beta * p1[1] + gamma_ * p1val, delta));
1738  assert(SCIPisInfinity(scip, delta) || SCIPisFeasEQ(scip, alpha * p2[0] + beta * p2[1] + gamma_ * p2val, delta));
1739  assert(SCIPisInfinity(scip, delta) || SCIPisFeasEQ(scip, alpha * p3[0] + beta * p3[1] + gamma_ * p3val, delta));
1740 
1741  /* if hyperplane through p1,p2,p3 does not overestimate f(p4), then it must be the other variant */
1742  if( SCIPisInfinity(scip, delta) || alpha * p4[0] + beta * p4[1] + gamma_ * p4val > delta )
1743  tryother = TRUE;
1744  }
1745  else
1746  {
1747  getAlphaBetaGammaDelta(p1[0], p1[1], p1val, p3[0], p3[1], p3val, p4[0], p4[1], p4val, &alpha, &beta, &gamma_, &delta);
1748  assert(SCIPisInfinity(scip, delta) || SCIPisFeasEQ(scip, alpha * p1[0] + beta * p1[1] + gamma_ * p1val, delta));
1749  assert(SCIPisInfinity(scip, delta) || SCIPisFeasEQ(scip, alpha * p3[0] + beta * p3[1] + gamma_ * p3val, delta));
1750  assert(SCIPisInfinity(scip, delta) || SCIPisFeasEQ(scip, alpha * p4[0] + beta * p4[1] + gamma_ * p4val, delta));
1751 
1752  /* if hyperplane through p1,p3,p4 does not overestimate f(p2), then it must be the other variant */
1753  if( SCIPisInfinity(scip, delta) || alpha * p2[0] + beta * p2[1] + gamma_ * p2val > delta )
1754  tryother = TRUE;
1755  }
1756 
1757  if( tryother )
1758  {
1759  if( x0y0[1] <= yub + (ylb - yub)/(xub - xlb) * (x0y0[0] - xlb) )
1760  {
1761  getAlphaBetaGammaDelta(p1[0], p1[1], p1val, p2[0], p2[1], p2val, p4[0], p4[1], p4val, &alpha, &beta, &gamma_, &delta);
1762 
1763  /* hyperplane should be above (p3,f(p3)) and other points should lie on hyperplane */
1764  assert(SCIPisInfinity(scip, delta) || SCIPisFeasEQ(scip, alpha * p1[0] + beta * p1[1] + gamma_ * p1val, delta));
1765  assert(SCIPisInfinity(scip, delta) || SCIPisFeasEQ(scip, alpha * p2[0] + beta * p2[1] + gamma_ * p2val, delta));
1766  assert(SCIPisInfinity(scip, delta) || SCIPisFeasLE(scip, alpha * p3[0] + beta * p3[1] + gamma_ * p3val, delta));
1767  assert(SCIPisInfinity(scip, delta) || SCIPisFeasEQ(scip, alpha * p4[0] + beta * p4[1] + gamma_ * p4val, delta));
1768  }
1769  else
1770  {
1771  getAlphaBetaGammaDelta(p2[0], p2[1], p2val, p3[0], p3[1], p3val, p4[0], p4[1], p4val, &alpha, &beta, &gamma_, &delta);
1772 
1773  /* hyperplane should be above (p1,f(p1)) and other points should lie on hyperplane */
1774  assert(SCIPisInfinity(scip, delta) || SCIPisFeasLE(scip, alpha * p1[0] + beta * p1[1] + gamma_ * p1val, delta));
1775  assert(SCIPisInfinity(scip, delta) || SCIPisFeasEQ(scip, alpha * p2[0] + beta * p2[1] + gamma_ * p2val, delta));
1776  assert(SCIPisInfinity(scip, delta) || SCIPisFeasEQ(scip, alpha * p3[0] + beta * p3[1] + gamma_ * p3val, delta));
1777  assert(SCIPisInfinity(scip, delta) || SCIPisFeasEQ(scip, alpha * p4[0] + beta * p4[1] + gamma_ * p4val, delta));
1778  }
1779  }
1780 
1781  SCIPdebugMessage("alpha = %g, beta = %g, gamma = %g, delta = %g\n", alpha, beta, gamma_, delta);
1782 
1783  /* check if bad luck: should not happen if xlb != xub and ylb != yub and numerics are fine */
1784  if( SCIPisInfinity(scip, delta) || SCIPisZero(scip, gamma_) )
1785  return SCIP_OKAY;
1786  assert(!SCIPisNegative(scip, gamma_));
1787 
1788  /* flip hyperplane */
1789  if( !doover )
1790  gamma_ = -gamma_;
1791 
1792  *coefx = -alpha / gamma_;
1793  *coefy = -beta / gamma_;
1794  *constant = delta / gamma_;
1795 
1796  *success = TRUE;
1797 
1798  return SCIP_OKAY;
1799 }
1800 
1801 /** generates a cut for lhs <= f(x,y) + c*z with f(x,y) being convex */
1802 static
1804  SCIP* scip, /**< SCIP data structure */
1805  SCIP_EXPRINT* exprinterpreter, /**< expressions interpreter */
1806  SCIP_CONS* cons, /**< constraint */
1807  SCIP_Real* x0y0, /**< reference values for nonlinear variables */
1808  SCIP_ROW** row /**< storage for cut */
1809  )
1810 {
1811  SCIP_CONSDATA* consdata;
1812  SCIP_Real coefs[2];
1813  SCIP_Real constant = SCIP_INVALID;
1814  SCIP_Bool success;
1815 
1816  assert(scip != NULL);
1817  assert(cons != NULL);
1818  assert(row != NULL);
1819 
1820  *row = NULL;
1821 
1822  consdata = SCIPconsGetData(cons);
1823  assert(consdata != NULL);
1824 
1825  SCIP_CALL( generateEstimatingHyperplane(scip, exprinterpreter, consdata->f, TRUE, x0y0, &coefs[0], &coefs[1], &constant, &success) );
1826 
1827  if( success )
1828  {
1829  assert(!SCIPisInfinity(scip, -consdata->lhs));
1830  assert(SCIPisFinite(coefs[0]));
1831  assert(SCIPisFinite(coefs[1]));
1832  assert(SCIPisFinite(constant));
1833 
1834  SCIP_CALL( SCIPcreateRowCons(scip, row, SCIPconsGetHdlr(cons), "bivaroveresthyperplanecut", 0, NULL, NULL, consdata->lhs - constant, SCIPinfinity(scip), TRUE, FALSE, TRUE) );
1835 
1836  SCIP_CALL( SCIPaddVarsToRow(scip, *row, 2, SCIPexprtreeGetVars(consdata->f), coefs) );
1837  if( consdata->z != NULL )
1838  SCIP_CALL( SCIPaddVarToRow(scip, *row, consdata->z, consdata->zcoef) );
1839  }
1840  else
1841  {
1842  SCIPdebugMessage("failed to compute overestimator for all-convex constraint <%s>\n", SCIPconsGetName(cons));
1843  }
1844 
1845  return SCIP_OKAY;
1846 }
1847 
1848 /** generates a linear underestimator for f(x,y)
1849  * when the generators of the underestimating segment
1850  * are contained in y=ylb and y=yub.
1851  * Generate coefficients cutcoeff = (alpha, beta, gamma, delta), such that
1852  * alpha * x + beta * y - delta <= gamma * f(x,y)
1853  */
1854 static
1856  SCIP* scip, /**< SCIP data structure */
1857  SCIP_EXPRINT* exprinterpreter, /**< expressions interpreter */
1858  SCIP_EXPRTREE* f, /**< function f(x,y) */
1859  SCIP_Real* xyref, /**< reference values for x and y */
1860  SCIP_Real cutcoeff[4], /**< cut coefficients alpha, beta, gamma, delta */
1861  SCIP_Real* convenvvalue, /**< function value of the convex envelope */
1862  SCIP_Bool* success /**< buffer to store whether coefficients were successfully computed */
1863  )
1864 {
1865  SCIP_VAR* x;
1866  SCIP_VAR* y;
1867  SCIP_Real xval;
1868  SCIP_Real xlb;
1869  SCIP_Real xub;
1870  SCIP_Real yval;
1871  SCIP_Real ylb;
1872  SCIP_Real yub;
1873 
1874  SCIP_Real t;
1875  SCIP_EXPR* vred;
1876  SCIP_EXPRTREE* vredtree;
1877  SCIP_EXPR* e1;
1878  SCIP_EXPR* e2;
1879  SCIP_EXPR* tmp;
1880  SCIP_EXPR* tmp2;
1881  SCIP_EXPR* subst[2];
1882 
1883  SCIP_Real sval;
1884  SCIP_Real slb;
1885  SCIP_Real sub;
1886  SCIP_Real rval;
1887 
1888  SCIP_Real frval;
1889  SCIP_Real fsval;
1890  SCIP_Real x0y0[2];
1891  SCIP_Real grad[2];
1892 
1893  assert(scip != NULL);
1894  assert(exprinterpreter != NULL);
1895  assert(f != NULL);
1896  assert(xyref != NULL);
1897  assert(success != NULL);
1898 
1899  x = SCIPexprtreeGetVars(f)[0];
1900  y = SCIPexprtreeGetVars(f)[1];
1901 
1902  xlb = SCIPvarGetLbLocal(x);
1903  xub = SCIPvarGetUbLocal(x);
1904 
1905  ylb = SCIPvarGetLbLocal(y);
1906  yub = SCIPvarGetUbLocal(y);
1907 
1908  xval = xyref[0];
1909  yval = xyref[1];
1910 
1911  *success = FALSE;
1912 
1913  /* check that variables are not unbounded or fixed and reference point is in interior */
1914  assert(!SCIPisInfinity(scip, -xlb));
1915  assert(!SCIPisInfinity(scip, xub));
1916  assert(!SCIPisInfinity(scip, -ylb));
1917  assert(!SCIPisInfinity(scip, yub));
1918  assert(!SCIPisEQ(scip,xlb,xub));
1919  assert(!SCIPisEQ(scip,ylb,yub));
1920  assert(!SCIPisEQ(scip,xlb,xval));
1921  assert(!SCIPisEQ(scip,xub,xval));
1922  assert(!SCIPisEQ(scip,ylb,yval));
1923  assert(!SCIPisEQ(scip,yub,yval));
1924 
1925  SCIPdebugMessage("f(%s, %s) = ", SCIPvarGetName(x), SCIPvarGetName(y));
1927  SCIPdebugPrintf("\n");
1928 
1929  t = (yub - yval) / (yub - ylb);
1930 
1931  /* construct v_red(s) := t f(1/t xval + (1-1/t) s, ylb) + (1-t) f(s, yub) */
1932 
1933  /* construct e1 := f(1/t xval + (1-1/t) s, ylb) */
1934  SCIP_CALL( SCIPexprCopyDeep(SCIPblkmem(scip), &e1, SCIPexprtreeGetRoot(f)) ); /* e1 = f(x,y) */
1935 
1936  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp, SCIP_EXPR_VARIDX, 0) ); /* tmp = s */
1937  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp2, SCIP_EXPR_CONST, 1.0 - 1.0 / t) ); /* tmp2 = 1-1/t */
1938  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp, SCIP_EXPR_MUL, tmp, tmp2) ); /* tmp = (1-1/t)*s */
1939  if( xval != 0.0 )
1940  {
1941  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp2, SCIP_EXPR_CONST, 1/t*xval) ); /* tmp2 = 1/t*xval */
1942  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp, SCIP_EXPR_PLUS, tmp, tmp2) ); /* tmp = 1/t*xval + (1-1/t)*s */
1943  }
1944  subst[0] = tmp;
1945 
1946  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &subst[1], SCIP_EXPR_CONST, ylb) ); /* tmp = ylb */
1947 
1948  assert(SCIPexprGetOperator(e1) != SCIP_EXPR_VARIDX); /* substitute cannot substitute the root node, but f should not be a single variable anyway */
1949  SCIP_CALL( SCIPexprSubstituteVars(SCIPblkmem(scip), e1, subst) ); /* e1 = f(1/t*xval + (1-1/t)*s, ylb) */
1950 
1951  SCIPexprFreeDeep(SCIPblkmem(scip), &subst[0]);
1952  SCIPexprFreeDeep(SCIPblkmem(scip), &subst[1]);
1953 
1954  /* construct e2 := f(s, yub) */
1955  SCIP_CALL( SCIPexprCopyDeep(SCIPblkmem(scip), &e2, SCIPexprtreeGetRoot(f)) ); /* e2 = f(x,y) */
1956 
1957  subst[0] = NULL;
1958 
1959  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &subst[1], SCIP_EXPR_CONST, yub) );
1960 
1961  assert(SCIPexprGetOperator(e2) != SCIP_EXPR_VARIDX); /* substitute cannot substitute the root node, but f should not be a single variable anyway */
1962  SCIP_CALL( SCIPexprSubstituteVars(SCIPblkmem(scip), e2, subst) ); /* e2 = f(s,yub) */
1963 
1964  SCIPexprFreeDeep(SCIPblkmem(scip), &subst[1]);
1965 
1966  /* construct vred := t * e1 + (1-t) * e2 */
1967  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp, SCIP_EXPR_CONST, t) ); /* tmp = t */
1968  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &e1, SCIP_EXPR_MUL, e1, tmp) ); /* e1 = t * f(1/t*xval+(1-1/t)*s,ylb) */
1969 
1970  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp, SCIP_EXPR_CONST, 1.0 - t) ); /* tmp = 1 - t */
1971  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &e2, SCIP_EXPR_MUL, e2, tmp) ); /* e2 = (1-t) * f(s, yub) */
1972 
1973  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &vred, SCIP_EXPR_PLUS, e1, e2) );
1974  SCIP_CALL( SCIPexprtreeCreate(SCIPblkmem(scip), &vredtree, vred, 1, 0, NULL) );
1975 
1976  SCIP_CALL( SCIPexprintCompile(exprinterpreter, vredtree) );
1977 
1978  /* compute bounds on s */
1979  slb = (yval - yub) / (ylb - yval) * (xval / t - xub);
1980  sub = (yval - yub) / (ylb - yval) * (xval / t - xlb);
1981  if( slb < xlb )
1982  slb = xlb;
1983  if( sub > xub )
1984  sub = xub;
1985 
1986  /* find s in [slb, sub] such that vred'(s) = 0 */
1987  SCIP_CALL( solveDerivativeEquation(scip, exprinterpreter, vredtree, 0.0, slb, sub, &sval, success) );
1988 
1989  SCIP_CALL( SCIPexprtreeFree(&vredtree) );
1990 
1991  if( *success == FALSE )
1992  {
1993  /* something went wrong when computing s */
1994  return SCIP_OKAY;
1995  }
1996 
1997  /* compute r from s */
1998  rval = 1.0 / t * xval + (1.0 - 1.0 / t) * sval;
1999  assert(SCIPisFeasGE(scip, rval, xlb));
2000  assert(SCIPisFeasLE(scip, rval, xub));
2001  rval = MAX(xlb, MIN(rval, xub));
2002 
2003  /* compute f(sval, yub) */
2004  x0y0[0] = sval;
2005  x0y0[1] = yub;
2006  SCIP_CALL( SCIPexprtreeEval(f, x0y0, &fsval) );
2007 
2008  /* compute f(rval, ylb) */
2009  x0y0[0] = rval;
2010  x0y0[1] = ylb;
2011  SCIP_CALL( SCIPexprtreeEval(f, x0y0, &frval) );
2012 
2013  if( !SCIPisEQ(scip, sval, xlb) && !SCIPisEQ(scip, sval, xub) )
2014  {
2015  x0y0[0] = sval;
2016  x0y0[1] = yub;
2017 
2018  /* compute f'(xbar, ybar) */
2019  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, x0y0, TRUE, &fsval, grad) );
2020  }
2021  else if( !SCIPisEQ(scip, rval, xlb) && !SCIPisEQ(scip, rval, xub) )
2022  {
2023  x0y0[0] = rval;
2024  x0y0[1] = ylb;
2025 
2026  /* compute f'(xbar, ybar) */
2027  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, x0y0, TRUE, &frval, grad) );
2028  }
2029  else
2030  {
2031  /* rare case
2032  * both points (sval, yub) and (rval, ylb) should yield valid inequality
2033  * for now, just take the first one, if differentiable, otherwise second one */
2034  x0y0[0] = sval;
2035  x0y0[1] = yub;
2036 
2037  /* compute f'(xbar, ybar) */
2038  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, x0y0, TRUE, &fsval, grad) );
2039 
2040  if( !SCIPisFinite(grad[0]) )
2041  {
2042  x0y0[0] = rval;
2043  x0y0[1] = ylb;
2044 
2045  /* compute f'(xbar, ybar) */
2046  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, x0y0, TRUE, &frval, grad) );
2047  }
2048  }
2049 
2050  /* compute vred(s) = t * f(rval, ylb) + (1-t) * f(s, yub) */
2051  /* SCIP_CALL( SCIPexprtreeEval(vredtree, &sval, &vredval) ); */
2052  *convenvvalue = t * frval + (1.0 - t) * fsval;
2053 
2054  SCIPdebugMessage("Parallel: Cut of (xval,yval)=(%g,%g)\n",xval,yval);
2055  SCIPdebugMessage("Parallel: r=%g in [%g,%g], s=%g in [%g,%g], f(r,ylb)=%g, f(xlb,s)=%g\n",rval,xlb,xub,sval,ylb,yub,frval,fsval);
2056  SCIPdebugMessage("(r,ylb)=(%g,%g), (s,yub)=(%g,%g), vredval=%g\n",rval,ylb,sval,yub,*convenvvalue);
2057 
2058  if( !SCIPisFinite(grad[0]) || SCIPisInfinity(scip, REALABS(grad[0])) )
2059  {
2060  SCIPdebugMessage("f not differentiable in (x0,y0) w.r.t. x\n");
2061  return SCIP_OKAY;
2062  }
2063 
2064  /* compute cut coefficients */
2065  cutcoeff[0] = (yub - ylb) * grad[0];
2066  cutcoeff[1] = fsval - frval - (sval - rval) * grad[0];
2067  cutcoeff[2] = yub - ylb;
2068  cutcoeff[3] = cutcoeff[0] * xval + cutcoeff[1] * yval - cutcoeff[2] * *convenvvalue;
2069 
2070  SCIPdebugMessage("Parallel: cutcoeff[0]=%g, cutcoeff[1]=%g, cutcoeff[2]=%g, cutcoeff[3]=%g\n",cutcoeff[0]/cutcoeff[2],cutcoeff[1]/cutcoeff[2],cutcoeff[2]/cutcoeff[2],cutcoeff[3]/cutcoeff[2]);
2071 
2072  *success = TRUE;
2073 
2074  return SCIP_OKAY;
2075 }
2076 
2077 
2078 /** generates a linear underestimator for f(x,y)
2079  * with f(x,y) being convex in x and convex in y.
2080  * The segmenent connects orthogonal facets: Either (x=l_x,y=l_y)
2081  * or (x=u_x,y=u_y).
2082  * generate coefficients cutcoeff = (alpha, beta, gamma, delta), such that
2083  * alpha * x + beta * y - delta <= gamma * f(x,y)
2084  */
2085 static
2087  SCIP* scip, /**< SCIP data structure */
2088  SCIP_EXPRINT* exprinterpreter, /**< expressions interpreter */
2089  SCIP_EXPRTREE* f, /**< function f(x,y) */
2090  SCIP_Real* xyref, /**< reference values for x and y */
2091  SCIP_Real cutcoeff[4], /**< cut coefficients alpha, beta, gamma, delta */
2092  SCIP_Real* convenvvalue, /**< function value of the convex envelope */
2093  SCIP_Bool* success /**< buffer to store whether coefficients were successfully computed */
2094  )
2095 {
2096  SCIP_VAR* x;
2097  SCIP_VAR* y;
2098  SCIP_Real xval;
2099  SCIP_Real xlb;
2100  SCIP_Real xub;
2101  SCIP_Real yval;
2102  SCIP_Real ylb;
2103  SCIP_Real yub;
2104 
2105  SCIP_Real x0y0[2];
2106 
2107  SCIP_EXPR* vred;
2108  SCIP_EXPRTREE* vredtree;
2109  SCIP_EXPR* e1;
2110  SCIP_EXPR* e2;
2111  SCIP_EXPR* tmp;
2112  SCIP_EXPR* expr;
2113  SCIP_EXPR* expr1;
2114  SCIP_EXPR* expr2;
2115  SCIP_EXPR* subst[2];
2116 
2117  SCIP_Real tval, tlb, tub;
2118  SCIP_Real sval;
2119  SCIP_Real rval;
2120 
2121  SCIP_Real frval,fsval;
2122  SCIP_Real grad_rval[2];
2123  SCIP_Real grad_sval[2];
2124 
2125  assert(scip != NULL);
2126  assert(exprinterpreter != NULL);
2127  assert(f != NULL);
2128  assert(convenvvalue != NULL);
2129  assert(success != NULL);
2130 
2131  x = SCIPexprtreeGetVars(f)[0];
2132  y = SCIPexprtreeGetVars(f)[1];
2133 
2134  xlb = SCIPvarGetLbLocal(x);
2135  xub = SCIPvarGetUbLocal(x);
2136 
2137  ylb = SCIPvarGetLbLocal(y);
2138  yub = SCIPvarGetUbLocal(y);
2139 
2140  xval = xyref[0];
2141  yval = xyref[1];
2142 
2143  /* check that variables are not unbounded or fixed and reference point is in interior */
2144  assert(!SCIPisInfinity(scip, -xlb));
2145  assert(!SCIPisInfinity(scip, xub));
2146  assert(!SCIPisInfinity(scip, -ylb));
2147  assert(!SCIPisInfinity(scip, yub));
2148  assert(!SCIPisEQ(scip,xlb,xub));
2149  assert(!SCIPisEQ(scip,ylb,yub));
2150  assert(!SCIPisEQ(scip,xlb,xval));
2151  assert(!SCIPisEQ(scip,xub,xval));
2152  assert(!SCIPisEQ(scip,ylb,yval));
2153  assert(!SCIPisEQ(scip,yub,yval));
2154 
2155  *success = FALSE;
2156 
2157  SCIPdebugMessage("f(%s, %s) = ", SCIPvarGetName(x), SCIPvarGetName(y));
2159  SCIPdebugPrintf("\n");
2160  SCIPdebugMessage("%s[%g,%g] = %g %s[%g,%g] = %g\n", SCIPvarGetName(x), xlb, xub, xval, SCIPvarGetName(y), ylb, yub, yval);
2161 
2162  /* check in which triangle the point (xval,yval) lies */
2163  if( yval <= (ylb-yub) / (xub-xlb) * (xval-xlb) + yub )
2164  {
2165  /* (xval,yval) lies in lower left triangle, i.e. region A_1 */
2166  /* construct v_red(t) := t f( xlb, (yval-(1-t)ylb)/t ) + (1-t)*f( (xval-xlb*t)/(1-t), ylb ) */
2167 
2168  /* construct e1 := f(xlb, ylb + (yval-ylb)/t) */
2169  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_VARIDX, 0) ); /* expr = t */
2170  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp, SCIP_EXPR_CONST, yval-ylb) ); /* tmp = yval-ylb */
2171  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_DIV, tmp, expr) ); /* expr = (yval-ylb) / t */
2172  if( ylb != 0.0 )
2173  {
2174  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp, SCIP_EXPR_CONST, ylb) ); /* tmp = ylb */
2175  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_PLUS, expr, tmp) ); /* expr = ylb + (yval-ylb) / t */
2176  }
2177  subst[1] = expr;
2178 
2179  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &subst[0], SCIP_EXPR_CONST, xlb) ); /* subst[0] = xlb */
2180 
2181  SCIP_CALL( SCIPexprCopyDeep(SCIPblkmem(scip), &e1, SCIPexprtreeGetRoot(f)) ); /* e1 = f(x,y) */
2182  assert(SCIPexprGetOperator(e1) != SCIP_EXPR_VARIDX); /* expr substitute vars cannot substitute the root node, but f should not be a single variable anyway */
2183  SCIP_CALL( SCIPexprSubstituteVars(SCIPblkmem(scip), e1, subst) ); /* e1 = f(xlb, ylb + (yval-ylb)/t) */
2184 
2185  SCIPexprFreeDeep(SCIPblkmem(scip), &subst[0]);
2186  SCIPexprFreeDeep(SCIPblkmem(scip), &subst[1]);
2187 
2188 
2189  /* construct e2 := f((xval-xlb*t)/(1-t), ylb) */
2190  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr1, SCIP_EXPR_VARIDX, 0) ); /* expr1 = t */
2191  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp, SCIP_EXPR_CONST, 1.0) ); /* tmp = 1.0 */
2192  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr1, SCIP_EXPR_MINUS, tmp, expr1) ); /* expr1 = 1-t */
2193 
2194  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr2, SCIP_EXPR_VARIDX, 0) ); /* expr2 = t */
2195  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp, SCIP_EXPR_CONST, xlb) ); /* tmp = xlb */
2196  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr2, SCIP_EXPR_MUL, expr2, tmp) ); /* expr2 = xlb * t */
2197  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp, SCIP_EXPR_CONST, xval) ); /* tmp = xval */
2198  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr2, SCIP_EXPR_MINUS, tmp, expr2) ); /* expr2 = xval - xlb * t */
2199 
2200  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_DIV, expr2, expr1) ); /* expr = (xval-t*xlb)/(1-t) */
2201  subst[0] = expr;
2202 
2203  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &subst[1], SCIP_EXPR_CONST, ylb) ); /* subst[0] = ylb */
2204 
2205  SCIP_CALL( SCIPexprCopyDeep(SCIPblkmem(scip), &e2, SCIPexprtreeGetRoot(f)) ); /* e2 = f(x,y) */
2206  assert(SCIPexprGetOperator(e2) != SCIP_EXPR_VARIDX); /* expr substitute vars cannot substitute the root node, but f should not be a single variable anyway */
2207  SCIP_CALL( SCIPexprSubstituteVars(SCIPblkmem(scip), e2, subst) ); /* e2 = f((xval-xlb*t)/(1-t), ylb) */
2208 
2209  SCIPexprFreeDeep(SCIPblkmem(scip), &subst[0]);
2210  SCIPexprFreeDeep(SCIPblkmem(scip), &subst[1]);
2211 
2212 
2213  /* construct vred := t * e1 + (1-t) * e2 */
2214  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_VARIDX, 0) ); /* expr = t */
2215  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr1, SCIP_EXPR_MUL, expr, e1) ); /* expr1 = t * e1*/
2216 
2217  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_VARIDX, 0) ); /* expr = t */
2218  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp, SCIP_EXPR_CONST, 1.0) ); /* tmp = 1.0 */
2219  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_MINUS, tmp, expr) ); /* expr = 1 - t */
2220  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr2, SCIP_EXPR_MUL, expr, e2) ); /* expr2 = (1-t) * e2 */
2221 
2222  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &vred, SCIP_EXPR_PLUS, expr1, expr2) );
2223  SCIP_CALL( SCIPexprtreeCreate(SCIPblkmem(scip), &vredtree, vred, 1, 0, NULL) );
2224  SCIP_CALL( SCIPexprintCompile(exprinterpreter, vredtree) );
2225 
2226  /* compute bounds on t */
2227  tlb = (yval-ylb)/(yub-ylb);
2228  tub = (xub-xval)/(xub-xlb);
2229 
2230  /* find t in [lambalb, tub] such that vred'(t) = 0 */
2231  SCIP_CALL( solveDerivativeEquation(scip, exprinterpreter, vredtree, 0.0, tlb, tub, &tval, success) );
2232 
2233  /* computing the cut coefficients */
2234  if( *success == FALSE )
2235  {
2236  /* something went wrong when computing s */
2237  SCIP_CALL( SCIPexprtreeFree(&vredtree) );
2238  return SCIP_OKAY;
2239  }
2240 
2241  /* compute r and s from tval */
2242  rval = (yval-(1-tval)*ylb)/tval;
2243  rval = MAX(ylb, MIN(yub, rval));
2244  sval = (xval-xlb*tval)/(1-tval);
2245  sval = MAX(xlb, MIN(xub, sval));
2246 
2247  SCIPdebugMessage("LowerLeft: t[%g,%g] = %g -> r = %g, s = %g\n",tlb,tub,tval,rval,sval);
2248 
2249  /* compute vred(tval) */
2250  SCIP_CALL( SCIPexprtreeEval(vredtree, &tval, convenvvalue) );
2251 
2252  SCIP_CALL( SCIPexprtreeFree(&vredtree) );
2253 
2254  /* compute f(s, ylb) and f'(s, ylb) */
2255  x0y0[0] = sval;
2256  x0y0[1] = ylb;
2257  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, x0y0, TRUE, &fsval, grad_sval) );
2258 
2259  /* compute f(xlb, r) and f'(xlb,r) */
2260  x0y0[0] = xlb;
2261  x0y0[1] = rval;
2262  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, x0y0, TRUE, &frval, grad_rval) );
2263 
2264  /* generate coefficients cutcoeff = (alpha, beta, gamma, delta), such that
2265  * alpha * x + beta * y - delta <= gamma * f(x,y)
2266  * cf. Section 2.5.2 Aux.prob. 2 case (ii)
2267  */
2268  if( !SCIPisEQ(scip, sval, xub) )
2269  {
2270  /* use the x-axis to determine the second direction */
2271  if( !SCIPisFinite(grad_sval[0]) || SCIPisInfinity(scip, REALABS(grad_sval[0])) )
2272  {
2273  *success = FALSE;
2274  return SCIP_OKAY;
2275  }
2276  cutcoeff[0] = (rval-ylb) * grad_sval[0];
2277  cutcoeff[1] = (sval-xlb) * grad_sval[0] + frval - fsval;
2278  cutcoeff[2] = rval-ylb;
2279  cutcoeff[3] = cutcoeff[0]*xlb+cutcoeff[1]*rval-cutcoeff[2]*frval;
2280  }
2281  else if( !SCIPisEQ(scip,rval,yub) )
2282  {
2283  /* use the y-axis to determine the second direction */
2284  if( !SCIPisFinite(grad_rval[1]) || SCIPisInfinity(scip, REALABS(grad_rval[1])) )
2285  {
2286  *success = FALSE;
2287  return SCIP_OKAY;
2288  }
2289  cutcoeff[0] = (rval-ylb)*grad_rval[1]+fsval-frval;
2290  cutcoeff[1] = (sval-xlb)*grad_rval[1];
2291  cutcoeff[2] = sval-xlb;
2292  cutcoeff[3] = cutcoeff[0]*xlb+cutcoeff[1]*rval-cutcoeff[2]*frval;
2293  }
2294  else
2295  {
2296  /* the point lies on the segment between (xlb,yub) and (xub,ylb) */
2297  if( !SCIPisFinite(grad_sval[0]) || !SCIPisFinite(grad_rval[0]) || SCIPisInfinity(scip, REALABS(MIN(grad_sval[0],grad_rval[0]))) )
2298  {
2299  /* FIXME maybe it is sufficient to have one of them finite, using that one for the MIN below? */
2300  *success = FALSE;
2301  return SCIP_OKAY;
2302  }
2303  cutcoeff[0] = (rval-ylb)* MIN(grad_sval[0],grad_rval[0]);
2304  cutcoeff[1] = (sval-xlb)* MIN(grad_sval[0],grad_rval[0])+frval-fsval;
2305  cutcoeff[2] = (rval-ylb);
2306  cutcoeff[3] = cutcoeff[0]*xlb+cutcoeff[1]*rval-cutcoeff[2]*frval;
2307  }
2308 
2309  SCIPdebugMessage("LowerLeft: Cut of (xval,yval)=(%g,%g)\n",xval,yval);
2310  SCIPdebugMessage("LowerLeft: r=%g in [%g,%g], s=%g in [%g,%g], f(s,ylb)=%g, f(xlb,r)=%g\n",rval,xlb,xub,sval,ylb,yub,fsval,frval);
2311  SCIPdebugMessage("(s,ylb)=(%g,%g) (xlb,r)=(%g,%g) t=%g, vredval=%g\n",sval,ylb,xlb,rval,tval,*convenvvalue);
2312  SCIPdebugMessage("LowerLeft: cutcoeff[0]=%g, cutcoeff[1]=%g,cutcoeff[2]=%g,cutcoeff[3]=%g\n",cutcoeff[0],cutcoeff[1],cutcoeff[2],cutcoeff[3]);
2313  }
2314  else
2315  {
2316  /* (xval,yval) lies in the upper right triangle, i.e region A_2 */
2317  /* construct v_red(t) := t f( xub, yub + (yval-yub)/t ) + (1-t)*f((xval-xub*t)/(1-t), yub) */
2318 
2319  /* construct e1 := f(xub, yub+(yval-yub)/t) */
2320  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_VARIDX, 0) ); /* expr = t*/
2321  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp, SCIP_EXPR_CONST, yval-yub) ); /* tmp = yval-yub*/
2322  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_DIV, tmp, expr) ); /* expr = (yval-yub) / t */
2323  if( yub != 0.0 )
2324  {
2325  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp, SCIP_EXPR_CONST, yub) ); /* tmp = yub */
2326  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_PLUS, expr, tmp) ); /* expr = yub + (yval-yub)/t */
2327  }
2328  subst[1] = expr;
2329 
2330  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &subst[0], SCIP_EXPR_CONST, xub) ); /* tmp = xub */
2331 
2332  SCIP_CALL( SCIPexprCopyDeep(SCIPblkmem(scip), &e1, SCIPexprtreeGetRoot(f)) ); /* e1 = f(x,y) */
2333  assert(SCIPexprGetOperator(e1) != SCIP_EXPR_VARIDX); /* cannot substitute root */
2334  SCIP_CALL( SCIPexprSubstituteVars(SCIPblkmem(scip), e1, subst) ); /* e1 = f(xub, yub + (yval-yub)/t) */
2335 
2336  SCIPexprFreeDeep(SCIPblkmem(scip), &subst[0]);
2337  SCIPexprFreeDeep(SCIPblkmem(scip), &subst[1]);
2338 
2339  /* construct e2 := f((xval-t*xub)/(1-t), yub) */
2340  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr1, SCIP_EXPR_VARIDX, 0) ); /* expr1 = t */
2341  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp, SCIP_EXPR_CONST, 1.0) ); /* tmp = 1.0 */
2342  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr1, SCIP_EXPR_MINUS, tmp, expr1) ); /* expr1 = 1-t */
2343 
2344  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr2, SCIP_EXPR_VARIDX, 0) ); /* expr2 = t */
2345  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp, SCIP_EXPR_CONST, xub) ); /* tmp = xub */
2346  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr2, SCIP_EXPR_MUL, expr2, tmp) ); /* expr2 = xub * t */
2347  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp, SCIP_EXPR_CONST, xval) ); /* tmp = xval */
2348  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr2, SCIP_EXPR_MINUS, tmp, expr2) ); /* expr2 = xval - xub * t */
2349 
2350  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_DIV, expr2, expr1) ); /* expr = (xval-t*xub)/(1-t) */
2351  subst[0] = expr;
2352 
2353  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &subst[1], SCIP_EXPR_CONST, yub) ); /* tmp = yub */
2354 
2355  SCIP_CALL( SCIPexprCopyDeep(SCIPblkmem(scip), &e2, SCIPexprtreeGetRoot(f)) ); /* e2 = f(x,y) */
2356  assert(SCIPexprGetOperator(e2) != SCIP_EXPR_VARIDX); /* cannot substitute root */
2357  SCIP_CALL( SCIPexprSubstituteVars(SCIPblkmem(scip), e2, subst) ); /* e2 = f((xval-t*xub)/(1-t), yub) */
2358 
2359  SCIPexprFreeDeep(SCIPblkmem(scip), &subst[0]);
2360  SCIPexprFreeDeep(SCIPblkmem(scip), &subst[1]);
2361 
2362  /* construct vred := t * e1 + (1-t) * e2 */
2363  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_VARIDX, 0) ); /* expr = t */
2364  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr1, SCIP_EXPR_MUL, e1, expr) ); /* expr1 = t * e1*/
2365 
2366  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_VARIDX, 0) ); /* expr = t */
2367  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp, SCIP_EXPR_CONST, 1.0) ); /* tmp = 1.0 */
2368  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_MINUS, tmp, expr) ); /* expr = 1-t */
2369  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr2, SCIP_EXPR_MUL, e2, expr) ); /* expr2 = (1-t) * e2*/
2370 
2371 
2372  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &vred, SCIP_EXPR_PLUS, expr1, expr2) );
2373  SCIP_CALL( SCIPexprtreeCreate(SCIPblkmem(scip), &vredtree, vred, 1, 0, NULL) );
2374  SCIP_CALL( SCIPexprintCompile(exprinterpreter, vredtree) );
2375 
2376  /* compute bounds on t */
2377  tlb = (yub-yval)/(yub-ylb);
2378  tub = (xval-xlb)/(xub-xlb);
2379 
2380  /* find t in [tlb, tub] such that vred'(t) = 0 */
2381  SCIP_CALL( solveDerivativeEquation(scip, exprinterpreter, vredtree, 0.0, tlb, tub, &tval, success) );
2382 
2383  SCIP_CALL( SCIPexprtreeFree(&vredtree) );
2384 
2385  if( *success == FALSE )
2386  {
2387  /* something went wrong when computing s */
2388  return SCIP_OKAY;
2389  }
2390 
2391  /* computing the cut coefficients */
2392 
2393  /* compute r and s from tval */
2394  rval = (yval-(1.0-tval)*yub)/tval;
2395  assert(SCIPisFeasGE(scip, rval, ylb));
2396  assert(SCIPisFeasLE(scip, rval, yub));
2397  rval = MAX(ylb, MIN(yub, rval));
2398 
2399  sval = (xval-xub*tval)/(1.0-tval);
2400  assert(SCIPisFeasGE(scip, sval, xlb));
2401  assert(SCIPisFeasLE(scip, sval, xub));
2402  sval = MAX(xlb, MIN(xub, sval));
2403 
2404  /* compute f(xub,r) and f'(xub,r) */
2405  x0y0[0] = xub;
2406  x0y0[1] = rval;
2407  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, x0y0, TRUE, &frval, grad_rval) );
2408 
2409  /* compute f(s,yub) and f'(s,yub) */
2410  x0y0[0] = sval;
2411  x0y0[1] = yub;
2412  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, x0y0, TRUE, &fsval, grad_sval) );
2413 
2414  /* compute vred(tval) */
2415  *convenvvalue = tval * frval + (1.0-tval) * fsval;
2416 
2417  /* generate coefficients cutcoeff = (alpha, beta, gamma, delta), such that
2418  * alpha * x + beta * y - delta <= gamma * f(x,y) */
2419 
2420  if( !SCIPisEQ(scip, sval, xlb) )
2421  {
2422  /* use the x-axis to determine the second direction */
2423  if( !SCIPisFinite(grad_sval[0]) || SCIPisInfinity(scip, REALABS(grad_sval[0])) )
2424  {
2425  *success = FALSE;
2426  return SCIP_OKAY;
2427  }
2428 
2429  cutcoeff[0] = (yub-rval)*grad_sval[0];
2430  cutcoeff[1] = (xub-sval)*grad_sval[0]+fsval-frval;
2431  cutcoeff[2] = yub-rval;
2432  cutcoeff[3] = cutcoeff[0]*sval+cutcoeff[1]*yub-cutcoeff[2]*fsval;
2433  }
2434  else if( !SCIPisEQ(scip,rval,ylb) )
2435  {
2436  /* use the y-axis to determine the second direction */
2437  if( !SCIPisFinite(grad_rval[1]) || SCIPisInfinity(scip, REALABS(grad_rval[1])) )
2438  {
2439  *success = FALSE;
2440  return SCIP_OKAY;
2441  }
2442  cutcoeff[0] = (yub-rval)*grad_rval[1]+frval-fsval;
2443  cutcoeff[1] = (xub-sval)*grad_rval[1];
2444  cutcoeff[2] = xub-sval;
2445  cutcoeff[3] = cutcoeff[0]*sval+cutcoeff[1]*yub-cutcoeff[2]*fsval;
2446  }
2447  else
2448  {
2449  /* the point lies on the segment between (xlb,yub) and (xub,ylb)
2450  * due to numerics, we get into this case here instead in the LowerLeft
2451  */
2452  assert(SCIPisFeasLE(scip, yval, (ylb-yub) / (xub-xlb) * (xval-xlb) + yub));
2453  if( !SCIPisFinite(grad_sval[0]) || !SCIPisFinite(grad_rval[0]) || SCIPisInfinity(scip, REALABS(MIN(grad_sval[0],grad_rval[0]))) )
2454  {
2455  /* FIXME maybe it is sufficient to have one of them finite, using that one for the MIN below? */
2456  *success = FALSE;
2457  return SCIP_OKAY;
2458  }
2459 
2460  cutcoeff[0] = (yub-rval)*MIN(grad_sval[0],grad_rval[0]);
2461  cutcoeff[1] = (xub-sval)*MIN(grad_sval[0],grad_rval[0])+fsval-frval;
2462  cutcoeff[2] = xub-sval;
2463  cutcoeff[3] = cutcoeff[0]*sval+cutcoeff[1]*yub-cutcoeff[2]*fsval;
2464  }
2465 
2466  SCIPdebugMessage("UpperRight: Cut of (xval,yval)=(%g,%g)\n",xval,yval);
2467  SCIPdebugMessage("UpperRight: r=%g in [%g,%g], s=%g in [%g,%g], f(r,yub)=%g, f(xub,s)=%g\n",rval,xlb,xub,sval,ylb,yub,frval,fsval);
2468  SCIPdebugMessage("(s,yub)=(%g,%g) (xub,r)=(%g,%g) t=%g, vredval=%g\n",sval,yub,xub,rval,tval,*convenvvalue);
2469  SCIPdebugMessage("UpperRight: cutcoeff[0]=%g, cutcoeff[1]=%g, cutcoeff[2]=%g, cutcoeff[3]=%g\n",cutcoeff[0],cutcoeff[1],cutcoeff[2],cutcoeff[3]);
2470  }
2471 
2472  return SCIP_OKAY;
2473 }
2474 
2475 /** generates a linear underestimator for f(x,y) with f(x,y) being convex in x and convex in y
2476  * generate coefficients cutcoeff = (alpha, beta, gamma, delta), such that
2477  * alpha * x + beta * y - delta <= gamma * f(x,y)
2478  */
2479 static
2481  SCIP* scip, /**< SCIP data structure */
2482  SCIP_EXPRINT* exprinterpreter, /**< expressions interpreter */
2483  SCIP_EXPRTREE* f, /**< function f(x,y) */
2484  SCIP_Real* xyref, /**< reference values for x and y */
2485  SCIP_Real cutcoeff[4], /**< cut coefficients alpha, beta, gamma, delta */
2486  SCIP_Real* convenvvalue, /**< function value of the convex envelope */
2487  SCIP_Bool* success /**< buffer to store whether coefficients were successfully computed */
2488  )
2489 {
2490  SCIP_VAR* x;
2491  SCIP_VAR* y;
2492  SCIP_Real xval;
2493  SCIP_Real xlb;
2494  SCIP_Real xub;
2495  SCIP_Real yval;
2496  SCIP_Real ylb;
2497  SCIP_Real yub;
2498  SCIP_Real x0y0[2];
2499 
2500  SCIP_EXPR* vred;
2501  SCIP_EXPRTREE* vredtree;
2502  SCIP_EXPR* e1;
2503  SCIP_EXPR* e2;
2504  SCIP_EXPR* tmp;
2505  SCIP_EXPR* expr;
2506  SCIP_EXPR* expr1;
2507  SCIP_EXPR* expr2;
2508  SCIP_EXPR* subst[2];
2509 
2510  SCIP_Real tval;
2511  SCIP_Real tlb;
2512  SCIP_Real tub;
2513  SCIP_Real sval;
2514  SCIP_Real rval;
2515 
2516  SCIP_Real frval;
2517  SCIP_Real fsval;
2518  SCIP_Real grad_rval[2];
2519  SCIP_Real grad_sval[2];
2520 
2521  assert(scip != NULL);
2522  assert(exprinterpreter != NULL);
2523  assert(f != NULL);
2524  assert(convenvvalue != NULL);
2525  assert(success != NULL);
2526 
2527  x = SCIPexprtreeGetVars(f)[0];
2528  y = SCIPexprtreeGetVars(f)[1];
2529 
2530  xlb = SCIPvarGetLbLocal(x);
2531  xub = SCIPvarGetUbLocal(x);
2532 
2533  ylb = SCIPvarGetLbLocal(y);
2534  yub = SCIPvarGetUbLocal(y);
2535 
2536  xval = xyref[0];
2537  yval = xyref[1];
2538 
2539  /* check that variables are not unbounded or fixed and reference point is in interior */
2540  assert(!SCIPisInfinity(scip, -xlb));
2541  assert(!SCIPisInfinity(scip, xub));
2542  assert(!SCIPisInfinity(scip, -ylb));
2543  assert(!SCIPisInfinity(scip, yub));
2544  assert(!SCIPisEQ(scip,xlb,xub));
2545  assert(!SCIPisEQ(scip,ylb,yub));
2546  assert(!SCIPisEQ(scip,xlb,xval));
2547  assert(!SCIPisEQ(scip,xub,xval));
2548  assert(!SCIPisEQ(scip,ylb,yval));
2549  assert(!SCIPisEQ(scip,yub,yval));
2550 
2551  *success = FALSE;
2552 
2553  SCIPdebugMessage("f(%s, %s) = ", SCIPvarGetName(x), SCIPvarGetName(y));
2555  SCIPdebugPrintf("\n");
2556 
2557  /* check in which triangle the point (xval,yval) lies */
2558  if( yval <= (yub-ylb)/(xub-xlb)*(xval-xlb)+ylb )
2559  {
2560  /* lower right triangle, i.e. region A_2 */
2561  /* construct v_red(t) := t f( xub+(xval-xub)/t, ylb ) + (1-t)*f( xub, (yval-ylb*t)/(1-t)) */
2562 
2563  /* construct e1:= f(xub+(xval-xub)/t, ylb) */
2564  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_VARIDX, 0) ); /* expr = t */
2565  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp, SCIP_EXPR_CONST, xval-xub) ); /* tmp = xval-xub */
2566  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_DIV, tmp, expr) ); /* expr = (xval-xub)/t */
2567  if( xub != 0.0 )
2568  {
2569  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp, SCIP_EXPR_CONST, xub) ); /* tmp = xub */
2570  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_PLUS, expr, tmp) ); /* expr = xub + (xval-xub)/t */
2571  }
2572  subst[0] = expr;
2573 
2574  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &subst[1], SCIP_EXPR_CONST, ylb) ); /* subst[1] = ylb */
2575 
2576  SCIP_CALL( SCIPexprCopyDeep(SCIPblkmem(scip), &e1, SCIPexprtreeGetRoot(f)) ); /* e1 = f(x,y) */
2577  assert(SCIPexprGetOperator(e1) != SCIP_EXPR_VARIDX);
2578  SCIP_CALL( SCIPexprSubstituteVars(SCIPblkmem(scip), e1, subst) ); /* e1 = f(xub + (xval-xub)/t, ylb) */
2579 
2580  SCIPexprFreeDeep(SCIPblkmem(scip), &subst[0]);
2581  SCIPexprFreeDeep(SCIPblkmem(scip), &subst[1]);
2582 
2583 
2584  /* construct e2 := f(xub, (yval-t*ylb)/(1-t)) */
2585  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr1, SCIP_EXPR_VARIDX, 0) ); /* expr1 = t */
2586  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp, SCIP_EXPR_CONST, 1.0) ); /* tmp = 1.0 */
2587  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr1, SCIP_EXPR_MINUS, tmp, expr1) ); /* expr1 = 1-t */
2588 
2589  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr2, SCIP_EXPR_VARIDX, 0) ); /* expr2 = t */
2590  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp, SCIP_EXPR_CONST, ylb) ); /* tmp = ylb */
2591  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr2, SCIP_EXPR_MUL, expr2, tmp) ); /* expr2 = ylb * t */
2592  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp, SCIP_EXPR_CONST, yval) ); /* tmp = yval */
2593  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr2, SCIP_EXPR_MINUS, tmp, expr2) ); /* expr2 = yval - ylb * t */
2594 
2595  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_DIV, expr2, expr1) ); /* expr = (yval-t*ylb)/(1-t) */
2596  subst[1] = expr;
2597 
2598  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &subst[0], SCIP_EXPR_CONST, xub) ); /* subst[0] = xub */
2599 
2600  SCIP_CALL( SCIPexprCopyDeep(SCIPblkmem(scip), &e2, SCIPexprtreeGetRoot(f)) ); /* e2 = f(x,y) */
2601  assert(SCIPexprGetOperator(e2) != SCIP_EXPR_VARIDX);
2602  SCIP_CALL( SCIPexprSubstituteVars(SCIPblkmem(scip), e2, subst) ); /* e2 = f(xub, (yval-t*ylb)/(1-t)) */
2603 
2604  SCIPexprFreeDeep(SCIPblkmem(scip), &subst[0]);
2605  SCIPexprFreeDeep(SCIPblkmem(scip), &subst[1]);
2606 
2607 
2608  /* construct vred := t * e1 + (1-t) * e2 */
2609  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_VARIDX, 0) ); /* expr = t */
2610  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr1, SCIP_EXPR_MUL, e1, expr) ); /* expr1 = t * e1*/
2611 
2612 
2613  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_VARIDX, 0) ); /* expr = t */
2614  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp, SCIP_EXPR_CONST, 1.0) ); /* tmp = 1.0 */
2615  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_MINUS, tmp, expr) ); /* expr = 1-t */
2616  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr2, SCIP_EXPR_MUL, e2, expr) ); /* expr2 = (1-t) * e2*/
2617 
2618 
2619  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &vred, SCIP_EXPR_PLUS, expr1, expr2) );
2620  SCIP_CALL( SCIPexprtreeCreate(SCIPblkmem(scip), &vredtree, vred, 1, 0, NULL) );
2621  SCIP_CALL( SCIPexprintCompile(exprinterpreter, vredtree) );
2622 
2623 
2624  /* compute bounds on t */
2625  tlb = (xub-xval)/(xub-xlb);
2626  tub = (yub-yval)/(yub-ylb);
2627 
2628  /* find t in [tlb, tub] such that vred'(t) = 0 */
2629  SCIP_CALL( solveDerivativeEquation(scip, exprinterpreter, vredtree, 0.0, tlb, tub, &tval, success) );
2630 
2631  if( *success == FALSE )
2632  {
2633  /* something went wrong when computing t */
2634  SCIP_CALL( SCIPexprtreeFree(&vredtree) );
2635  return SCIP_OKAY;
2636  }
2637 
2638  /* computing the cut coefficients */
2639 
2640  /* compute r and s from tval */
2641  rval = xub+(xval-xub)/tval;
2642  rval = MAX(xlb, MIN(xub, rval));
2643  sval = (yval-tval*ylb)/(1-tval);
2644  sval = MAX(ylb, MIN(yub, sval));
2645 
2646  /* compute vred(tval) */
2647  SCIP_CALL( SCIPexprtreeEval(vredtree, &tval, convenvvalue) );
2648 
2649  SCIP_CALL( SCIPexprtreeFree(&vredtree) );
2650 
2651  /* compute f(r, ylb) and f'(r, ylb) */
2652  x0y0[0] = rval;
2653  x0y0[1] = ylb;
2654  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, x0y0, TRUE, &frval, grad_rval) );
2655 
2656  /* compute f(xub, s) and f'(xub,s) */
2657  x0y0[0] = xub;
2658  x0y0[1] = sval;
2659  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, x0y0, TRUE, &fsval, grad_sval) );
2660 
2661  /* generate coefficients cutcoeff = (alpha, beta, gamma, delta), such that
2662  * alpha * x + beta * y - delta <= gamma * f(x,y) */
2663  if( !(SCIPisEQ(scip,rval,xlb)) )
2664  {
2665  /* take the slope along the x-axis and the slope between the points */
2666  if( !SCIPisFinite(grad_rval[0]) || SCIPisInfinity(scip, REALABS(grad_rval[0])) )
2667  {
2668  *success = FALSE;
2669  return SCIP_OKAY;
2670  }
2671  cutcoeff[0] = (sval-ylb)*grad_rval[0];
2672  cutcoeff[1] = (rval-xub)*grad_rval[0]-frval+fsval;
2673  cutcoeff[2] = sval-ylb;
2674  cutcoeff[3] = cutcoeff[0]*xub+cutcoeff[1]*sval-cutcoeff[2]*fsval;
2675  }
2676  else if( !(SCIPisEQ(scip,sval,yub)) )
2677  {
2678  /* take the slope along the y-axis and the slope between the points */
2679  if( !SCIPisFinite(grad_sval[1]) || SCIPisInfinity(scip, REALABS(grad_sval[1])) )
2680  {
2681  *success = FALSE;
2682  return SCIP_OKAY;
2683  }
2684  cutcoeff[0] = (ylb-sval)*grad_sval[1]-frval+fsval;
2685  cutcoeff[1] = (xub-rval)*grad_sval[1];
2686  cutcoeff[2] = xub-rval;
2687  cutcoeff[3] = cutcoeff[0]*xub+cutcoeff[1]*sval-cutcoeff[2]*fsval;
2688  }
2689  else
2690  {
2691  /* the point lies on the segment between (xlb,yub) and (xub,ylb) */
2692  if( !SCIPisFinite(grad_sval[0]) || !SCIPisFinite(grad_rval[0]) || SCIPisInfinity(scip, REALABS(MIN(grad_sval[0],grad_rval[0]))) )
2693  {
2694  /* FIXME maybe it is sufficient to have one of them finite, using that one for the MIN below? */
2695  *success = FALSE;
2696  return SCIP_OKAY;
2697  }
2698  cutcoeff[0] = (sval-ylb)*MIN(grad_sval[0],grad_rval[0]);
2699  cutcoeff[1] = (rval-xub)*MIN(grad_sval[0],grad_rval[0])+fsval-frval;
2700  cutcoeff[2] = sval-ylb;
2701  cutcoeff[3] = cutcoeff[0]*xub+cutcoeff[1]*sval-cutcoeff[2]*fsval;
2702  }
2703 
2704 
2705  SCIPdebugMessage("LowerRight: Cut of (xval,yval)=(%g,%g)\n",xval,yval);
2706  SCIPdebugMessage("LowerRight: t=%g in [%g,%g], r=%g in [%g,%g], s=%g in [%g,%g]\n",tval,tlb,tub,rval,xlb,xub,sval,ylb,yub);
2707  SCIPdebugMessage("LowerRight: (r,ylb)=(%g,%g) (xub,sval)=(%g,%g) vredval=%g\n",rval,ylb,xub,sval,*convenvvalue);
2708  SCIPdebugMessage("LowerRight: cutcoeff[0]=%g, cutcoeff[1]=%g,cutcoeff[2]=%g,cutcoeff[3]=%g\n",cutcoeff[0]/cutcoeff[2],cutcoeff[1]/cutcoeff[2],cutcoeff[2]/cutcoeff[2],cutcoeff[3]/cutcoeff[2]);
2709 
2710  }
2711  else
2712  {
2713  /* (xval,yval) lie in the upper left triangle, i.e. region A_1 */
2714  /* construct v_red(t) := t f( xlb+(xval-xlb)/t, yub ) + (1-t)*f( xlb, (yval-yub*t)/(1-t) ) */
2715 
2716  /* construct e1:= f(xlb+(xval-xlb)/t, yub) */
2717  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_VARIDX, 0) ); /* expr = t */
2718  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp, SCIP_EXPR_CONST, xval-xlb) ); /* tmp = xval-xlb */
2719  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_DIV, tmp, expr) ); /* expr = (xval-xlb)/lambda */
2720  if( xlb != 0.0 )
2721  {
2722  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp, SCIP_EXPR_CONST, xlb) ); /* tmp = xlb */
2723  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_PLUS, expr, tmp) ); /* expr = xlb + (xval-xlb)/t */
2724  }
2725  subst[0] = expr;
2726 
2727  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &subst[1], SCIP_EXPR_CONST, yub) ); /* subst[1] = yub */
2728 
2729  SCIP_CALL( SCIPexprCopyDeep(SCIPblkmem(scip), &e1, SCIPexprtreeGetRoot(f)) ); /* e1 = f(x,y) */
2730  assert(SCIPexprGetOperator(e1) != SCIP_EXPR_VARIDX);
2731  SCIP_CALL( SCIPexprSubstituteVars(SCIPblkmem(scip), e1, subst) ); /* e1 = f(xlb + (xval-xlb)/t, yub) */
2732 
2733  SCIPexprFreeDeep(SCIPblkmem(scip), &subst[0]);
2734  SCIPexprFreeDeep(SCIPblkmem(scip), &subst[1]);
2735 
2736 
2737  /* construct e2 := f(xlb, (yval-t*yub)/(1-t) ) */
2738  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr1, SCIP_EXPR_VARIDX, 0) ); /* expr1 = t */
2739  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp, SCIP_EXPR_CONST, 1.0) ); /* tmp = 1.0 */
2740  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr1, SCIP_EXPR_MINUS, tmp, expr1) ); /* expr1 = 1-t */
2741 
2742  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr2, SCIP_EXPR_VARIDX, 0) ); /* expr2 = t */
2743  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp, SCIP_EXPR_CONST, yub) ); /* tmp = yub */
2744  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr2, SCIP_EXPR_MUL, expr2, tmp) ); /* expr2 = yub * t */
2745  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp, SCIP_EXPR_CONST, yval) ); /* tmp = yval */
2746  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr2, SCIP_EXPR_MINUS, tmp, expr2) ); /* expr2 = yval - yub * t */
2747 
2748  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_DIV, expr2, expr1) ); /* expr = (yval-t*yub)/(1-t) */
2749  subst[1] = expr;
2750 
2751  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &subst[0], SCIP_EXPR_CONST, xlb) ); /* subst[0] = xlb */
2752 
2753  SCIP_CALL( SCIPexprCopyDeep(SCIPblkmem(scip), &e2, SCIPexprtreeGetRoot(f)) ); /* e2 = f(x,y) */
2754  SCIP_CALL( SCIPexprSubstituteVars(SCIPblkmem(scip), e2, subst) ); /* e2 = f( xlb , (yval-t*yub)/(1-t) ) */
2755 
2756  SCIPexprFreeDeep(SCIPblkmem(scip), &subst[0]);
2757  SCIPexprFreeDeep(SCIPblkmem(scip), &subst[1]);
2758 
2759 
2760  /* construct vred := t * e1 + (1-t) * e2 */
2761  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_VARIDX, 0) ); /* expr = t */
2762  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr1, SCIP_EXPR_MUL, e1, expr) ); /* expr1 = t * e1*/
2763 
2764 
2765  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_VARIDX, 0) ); /* expr = t */
2766  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &tmp, SCIP_EXPR_CONST, 1.0) ); /* tmp = 1.0 */
2767  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_MINUS, tmp, expr) ); /* expr = 1-t */
2768  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr2, SCIP_EXPR_MUL, e2, expr) ); /* expr2 = (1-t) * e2*/
2769 
2770 
2771  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &vred, SCIP_EXPR_PLUS, expr1, expr2) );
2772  SCIP_CALL( SCIPexprtreeCreate(SCIPblkmem(scip), &vredtree, vred, 1, 0, NULL) );
2773  SCIP_CALL( SCIPexprintCompile(exprinterpreter, vredtree) );
2774 
2775 
2776  /* compute bounds on lambda */
2777  tlb = (xval-xlb)/(xub-xlb);
2778  tub = (yval-ylb)/(yub-ylb);
2779 
2780  /* find t in [tlb, tub] such that vred'(t) = 0 */
2781  SCIP_CALL( solveDerivativeEquation(scip, exprinterpreter, vredtree, 0.0, tlb, tub, &tval, success) );
2782 
2783  if( *success == FALSE )
2784  {
2785  /* something went wrong when computing s */
2786  SCIP_CALL( SCIPexprtreeFree(&vredtree) );
2787  return SCIP_OKAY;
2788  }
2789 
2790  /* computing the cut coefficients */
2791 
2792  /* compute r and s from tval */
2793  rval = xlb+(xval-xlb)/tval;
2794  rval = MAX(xlb, MIN(xub, rval));
2795  sval = (yval-tval*yub)/(1-tval);
2796  sval = MAX(ylb, MIN(yub, sval));
2797 
2798  /* compute vred(tval) */
2799  SCIP_CALL( SCIPexprtreeEval(vredtree, &tval, convenvvalue) );
2800 
2801  SCIP_CALL( SCIPexprtreeFree(&vredtree) );
2802 
2803  /* compute f(r, yub) and f'(r, yub) */
2804  x0y0[0] = rval;
2805  x0y0[1] = yub;
2806  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, x0y0, TRUE, &frval, grad_rval) );
2807 
2808  /* compute f(xlb, s) and f'(xlb, s) */
2809  x0y0[0] = xlb;
2810  x0y0[1] = sval;
2811  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, x0y0, TRUE, &fsval, grad_sval) );
2812 
2813  /* generate coefficients cutcoeff = (alpha, beta, gamma, delta), such that
2814  * alpha * x + beta * y - delta <= gamma * f(x,y) */
2815  if( !SCIPisEQ(scip,rval,xub) )
2816  {
2817  /* take the slope along the x-axis and the slope between the points */
2818  if( !SCIPisFinite(grad_rval[0]) || SCIPisInfinity(scip, REALABS(grad_rval[0])) )
2819  {
2820  *success = FALSE;
2821  return SCIP_OKAY;
2822  }
2823  cutcoeff[0] = (yub-sval)*grad_rval[0];
2824  cutcoeff[1] = (xlb-rval)*grad_rval[0]-fsval+frval;
2825  cutcoeff[2] = yub-sval;
2826  cutcoeff[3] = cutcoeff[0]*xlb+cutcoeff[1]*sval-cutcoeff[2]*fsval;
2827  }
2828  else if( !SCIPisEQ(scip,sval,ylb) )
2829  {
2830  /* take the slope along the y-axis and the slope between the points */
2831  if( !SCIPisFinite(grad_sval[1]) || SCIPisInfinity(scip, REALABS(grad_sval[1])) )
2832  {
2833  *success = FALSE;
2834  return SCIP_OKAY;
2835  }
2836  cutcoeff[0] = (sval-yub)*grad_sval[1]-fsval+frval;
2837  cutcoeff[1] = (rval-xlb)*grad_sval[1];
2838  cutcoeff[2] = rval-xlb;
2839  cutcoeff[3] = cutcoeff[0]*xlb+cutcoeff[1]*sval-cutcoeff[2]*fsval;
2840  }
2841  else
2842  {
2843  /* the point lies on the segment between (xlb,yub) and (xub,ylb) */
2844  if( !SCIPisFinite(grad_sval[0]) || !SCIPisFinite(grad_rval[0]) || SCIPisInfinity(scip, REALABS(MIN(grad_rval[0],grad_sval[0]))) )
2845  {
2846  /* FIXME maybe it is sufficient to have one of them finite, using that one for the MIN below? */
2847  *success = FALSE;
2848  return SCIP_OKAY;
2849  }
2850  cutcoeff[0] = (yub-sval)*MIN(grad_rval[0],grad_sval[0]);
2851  cutcoeff[1] = (xlb-rval)*MIN(grad_rval[0],grad_sval[0])-fsval+frval;
2852  cutcoeff[2] = yub-sval;
2853  cutcoeff[3] = cutcoeff[0]*xlb+cutcoeff[1]*sval-cutcoeff[2]*fsval;
2854  }
2855 
2856  SCIPdebugMessage("UpperLeft: Cut of (xval,yval)=(%g,%g)\n",xval,yval);
2857  SCIPdebugMessage("UpperLeft: r=%g in [%g,%g], s=%g in [%g,%g], f(r,yub)=%g, f(xlb,s)=%g\n",rval,xlb,xub,sval,ylb,yub,frval,fsval);
2858  SCIPdebugMessage("t=%g in [%g,%g], (r,yub)=(%g,%g) (xlb,sval)=(%g,%g) vredval=%g\n",tval,tlb,tub,rval,yub,xlb,sval,*convenvvalue);
2859  SCIPdebugMessage("UpperLeft: cutcoeff[0]=%g, cutcoeff[1]=%g,cutcoeff[2]=%g,cutcoeff[3]=%g\n",cutcoeff[0]/cutcoeff[2],cutcoeff[1]/cutcoeff[2],cutcoeff[2]/cutcoeff[2],cutcoeff[3]/cutcoeff[2]);
2860  }
2861 
2862  return SCIP_OKAY;
2863 }
2864 
2865 
2866 /** generates a linear underestimator for f(x,y) with f(x,y) being STRICTLY convex in x and concave in y
2867  * generate coefficients cutcoeff = (alpha, beta, gamma, delta), such that alpha * x + beta * y - delta <= gamma * f(x,y)
2868  */
2869 static
2871  SCIP* scip, /**< SCIP data structure */
2872  SCIP_EXPRINT* exprinterpreter, /**< expressions interpreter */
2873  SCIP_EXPRTREE* f, /**< function f(x,y) */
2874  SCIP_EXPRTREE* f_yfixed, /**< function f(x;y) with x variable and y parameter */
2875  SCIP_EXPRTREE* vred, /**< function vred(s;x0,y0,ylb,yub) */
2876  SCIP_Real xyref[2], /**< reference values for (x,y) */
2877  SCIP_Real cutcoeff[4], /**< cut coefficients alpha, beta, gamma, delta */
2878  SCIP_Real* convenvvalue, /**< function value of the convex envelope */
2879  SCIP_Bool* success /**< buffer to store whether coefficients were successfully computed */
2880  )
2881 {
2882  SCIP_VAR* x;
2883  SCIP_VAR* y;
2884  SCIP_Real xval;
2885  SCIP_Real xlb;
2886  SCIP_Real xub;
2887  SCIP_Real yval;
2888  SCIP_Real ylb;
2889  SCIP_Real yub;
2890 
2891  assert(scip != NULL);
2892  assert(exprinterpreter != NULL);
2893  assert(f != NULL);
2894  assert(success != NULL);
2895  assert(xyref != NULL);
2896 
2897  x = SCIPexprtreeGetVars(f)[0];
2898  y = SCIPexprtreeGetVars(f)[1];
2899 
2900  xlb = SCIPvarGetLbLocal(x);
2901  xub = SCIPvarGetUbLocal(x);
2902 
2903  ylb = SCIPvarGetLbLocal(y);
2904  yub = SCIPvarGetUbLocal(y);
2905 
2906  xval = xyref[0];
2907  yval = xyref[1];
2908 
2909  /* reference point should not be outside of bounds */
2910  assert(SCIPisLE(scip, xlb, xval));
2911  assert(SCIPisGE(scip, xub, xval));
2912  assert(SCIPisLE(scip, ylb, yval));
2913  assert(SCIPisGE(scip, yub, yval));
2914 
2915  *success = FALSE;
2916 
2917  if( SCIPisInfinity(scip, -ylb) || SCIPisInfinity(scip, yub) )
2918  {
2919  SCIPdebugMessage("skip convex-concave underestimator, since y is unbounded\n");
2920  return SCIP_OKAY;
2921  }
2922 
2923  SCIPdebugMessage("f(%s, %s) = ", SCIPvarGetName(x), SCIPvarGetName(y));
2925  SCIPdebugPrintf("\n");
2926 
2927  if( SCIPisEQ(scip, xlb, xub) )
2928  {
2929  /* x is fixed, so function is now concave -> generate secant between (x, ylb) and (x, yub) */
2930  SCIP_Real xy[2];
2931  SCIP_Real f_ylb;
2932  SCIP_Real f_yub;
2933  SCIP_Real slope;
2934 
2935  if( SCIPisEQ(scip, ylb, yub) )
2936  {
2937  SCIPdebugMessage("skip convex-concave underestimator, since both x and y are fixed\n");
2938  return SCIP_OKAY;
2939  }
2940 
2941  /* get f(x, ylb) */
2942  xy[0] = xlb;
2943  xy[1] = ylb;
2944  SCIP_CALL( SCIPexprintEval(exprinterpreter, f, xy, &f_ylb) );
2945 
2946  if( !SCIPisFinite(f_ylb) )
2947  {
2948  SCIPdebugMessage("cannot evaluate function at (xlb, ylb)\n");
2949  return SCIP_OKAY;
2950  }
2951 
2952  /* get f(x, yub) */
2953  xy[1] = yub;
2954  SCIP_CALL( SCIPexprintEval(exprinterpreter, f, xy, &f_yub) );
2955 
2956  if( !SCIPisFinite(f_yub) )
2957  {
2958  SCIPdebugMessage("cannot evaluate function at (xlb, yub)\n");
2959  return SCIP_OKAY;
2960  }
2961 
2962  slope = (f_yub - f_ylb) / (yub - ylb);
2963 
2964  /* secant is f(x,ylb) + slope * (y - ylb) <= f(x,y)*/
2965 
2966  cutcoeff[0] = 0.0; /* coefficient of x == 0 */
2967  cutcoeff[1] = slope; /* coefficient of y == slope */
2968  cutcoeff[2] = 1.0; /* coefficient of f(x,y) == 1.0 */
2969  cutcoeff[3] = -(f_ylb - slope * ylb); /* constant == -(f(x,ylb) - slope * ylb) */
2970  *convenvvalue = f_ylb+slope*(yval-ylb);
2971 
2972  *success = TRUE;
2973  return SCIP_OKAY;
2974  }
2975 
2976  if( SCIPisEQ(scip, ylb, yub) )
2977  {
2978  /* y is fixed, so function is now convex -> linearize in (xval, ylb) */
2979  SCIP_Real xy[2];
2980  SCIP_Real grad[2];
2981  SCIP_Real fval;
2982 
2983  xy[0] = xval;
2984  xy[1] = ylb;
2985  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, xy, TRUE, &fval, grad) );
2986 
2987  if( !SCIPisFinite(fval) || !SCIPisFinite(grad[0]) || SCIPisInfinity(scip, REALABS(grad[0])) )
2988  {
2989  perturb(&xval, xlb, xub, 0.001);
2990  xy[0] = xval;
2991 
2992  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, xy, TRUE, &fval, grad) );
2993 
2994  if( !SCIPisFinite(fval) || !SCIPisFinite(grad[0]) || SCIPisInfinity(scip, REALABS(grad[0])) )
2995  {
2996  SCIPdebugMessage("cannot evaluate function or derivative in (xval,ylb), also after perturbation\n");
2997  return SCIP_OKAY;
2998  }
2999  }
3000 
3001  /* linearization is f(xval,ylb) + df/dx(xval,ylb) * (x - xval) <= f(x,y) */
3002 
3003  cutcoeff[0] = grad[0]; /* coefficient of x == gradient in x */
3004  cutcoeff[1] = 0.0; /* coefficient of y == 0 */
3005  cutcoeff[2] = 1.0; /* coefficient of f(x,y) == 1.0 */
3006  cutcoeff[3] = -(fval - grad[0] * xval); /* constant == -(f(xval,ylb) - grad * xval) */
3007  *convenvvalue = fval;
3008 
3009  *success = TRUE;
3010  return SCIP_OKAY;
3011  }
3012 
3013  /* compute coefficients of a valid underestimating hyperplane */
3014 
3015  if( SCIPisFeasEQ(scip, xlb, xval) || SCIPisFeasEQ(scip, xub, xval) )
3016  {
3017  /* x is at it's lower or upper bound */
3018  SCIP_Real x0y0[2];
3019  SCIP_Real gradylb[2];
3020  SCIP_Real gradyub[2];
3021  SCIP_Real fvalylb;
3022  SCIP_Real fvalyub;
3023 
3024  xval = SCIPisFeasEQ(scip, xlb, xval) ? xlb : xub;
3025 
3026  /* compute f'(xval, ylb) and f'(xval, yub) */
3027  x0y0[0] = xval;
3028  x0y0[1] = ylb;
3029  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, x0y0, TRUE, &fvalylb, gradylb) );
3030 
3031  x0y0[1] = yub;
3032  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, x0y0, TRUE, &fvalyub, gradyub) );
3033 
3034  if( !SCIPisFinite(gradylb[0]) || !SCIPisFinite(gradyub[0]) || !SCIPisFinite(fvalylb) || !SCIPisFinite(fvalyub) ||
3035  SCIPisInfinity(scip, REALABS(gradylb[0])) || SCIPisInfinity(scip, REALABS(gradyub[0])) )
3036  {
3037  /* move xval inside domain and continue below, hope this will work better */
3038  perturb(&xval, xlb, xub, 0.001);
3039  }
3040  else
3041  {
3042  /* setup cut coefficients */
3043  if( xval == xlb ) /*lint !e777*/
3044  cutcoeff[0] = (yub - ylb) * MIN(gradylb[0], gradyub[0]);/* coefficient of x */
3045  else
3046  cutcoeff[0] = (yub - ylb) * MAX(gradylb[0], gradyub[0]);/* coefficient of x */
3047  cutcoeff[1] = fvalyub - fvalylb; /* coefficient of y */
3048  cutcoeff[2] = yub - ylb; /* coefficient of f(x,y) */
3049  cutcoeff[3] = cutcoeff[0] * xval + cutcoeff[1] * ylb - cutcoeff[2] * fvalylb; /* constant */
3050  *convenvvalue = fvalylb;
3051 
3052  SCIPdebugMessage("alpha: %g, beta: %g, gamma: %g, delta: %g\n",
3053  cutcoeff[0]/cutcoeff[2], cutcoeff[1]/cutcoeff[2], cutcoeff[2]/cutcoeff[2], cutcoeff[3]/cutcoeff[2]);
3054 
3055  *success = TRUE;
3056  return SCIP_OKAY;
3057  }
3058  }
3059 
3060  if( SCIPisFeasEQ(scip, ylb, yval) )
3061  {
3062  /* y is at it's lower bound */
3063  SCIP_Real x0y0[2];
3064  SCIP_Real grad[2];
3065  SCIP_Real xtilde;
3066  SCIP_Real fval, ftilde;
3067 
3068  /* these two cases should have been handled above */
3069  assert(!SCIPisEQ(scip, xlb, xval));
3070  assert(!SCIPisEQ(scip, xub, xval));
3071 
3072  assert(f_yfixed != NULL);
3073 
3074  /* compute f(xval, ylb) and f'(xval, ylb) */
3075  x0y0[0] = xval;
3076  x0y0[1] = ylb;
3077  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, x0y0, TRUE, &fval, grad) );
3078 
3079  if( !SCIPisFinite(fval) || !SCIPisFinite(grad[0]) || SCIPisInfinity(scip, REALABS(grad[0])) )
3080  {
3081  /* move yval inside domain and continue below, hope this will work better */
3082  perturb(&yval, ylb, yub, 0.001);
3083  }
3084  else
3085  {
3086  /* setup f(x,yub) */
3087  SCIPexprtreeSetParamVal(f_yfixed, 0, yub);
3088  SCIP_CALL( SCIPexprintNewParametrization(exprinterpreter, f_yfixed) );
3089 
3090  SCIPdebugMessage("f(x,yub) = ");
3092  SCIPdebugPrintf("\n");
3093 
3094  /* find xtilde in [xlb, xub] such that f'(xtilde,yub) = f'(xval,ylb) */
3095  SCIP_CALL( solveDerivativeEquation(scip, exprinterpreter, f_yfixed, grad[0], xlb, xub, &xtilde, success) );
3096 
3097  if( !*success )
3098  {
3099  SCIP_Real fxlb;
3100  SCIP_Real fxub;
3101 
3102  /* if we could not find an xtilde such that f'(xtilde,yub) = f'(xval,ylb), then probably because f'(x,yub) is constant
3103  * in this case, choose xtilde from {xlb, xub} such that it maximizes f'(xtilde, yub) - grad[0]*xtilde
3104  */
3105  SCIP_CALL( SCIPexprintEval(exprinterpreter, f_yfixed, &xlb, &fxlb) );
3106  SCIP_CALL( SCIPexprintEval(exprinterpreter, f_yfixed, &xub, &fxub) );
3107 
3108  SCIPdebugMessage("couldn't solve deriv equ, compare f(%g,%g) - %g*%g = %g and f(%g,%g) - %g*%g = %g\n",
3109  xlb, ylb, grad[0], xlb, fxlb - grad[0] * xlb,
3110  xub, ylb, grad[0], xub, fxub - grad[0] * xub);
3111 
3112  if( SCIPisFinite(fxlb) && SCIPisFinite(fxub) )
3113  {
3114  if( fxlb - grad[0] * xlb > fxub - grad[0] * xub )
3115  xtilde = xlb;
3116  else
3117  xtilde = xub;
3118  *success = TRUE;
3119  }
3120  else
3121  {
3122  /* move yval inside domain and continue below, hope this will work better */
3123  perturb(&yval, ylb, yub, 0.001);
3124  }
3125  }
3126 
3127  if( *success )
3128  {
3129  /* compute f(xtilde, yub) */
3130  SCIP_CALL( SCIPexprintEval(exprinterpreter, f_yfixed, &xtilde, &ftilde) );
3131 
3132  SCIPdebugMessage("xtilde = %g, f(%g,%g) = %g\n", xtilde, xtilde, yub, ftilde);
3133 
3134  /* setup cut coefficients */
3135  cutcoeff[0] = (yub - ylb) * grad[0]; /* coefficient of x */
3136  cutcoeff[1] = ftilde - fval - grad[0] * (xtilde - xval); /* coefficient of y */
3137  cutcoeff[2] = yub - ylb; /* coefficient of f(x,y) */
3138  cutcoeff[3] = cutcoeff[0] * xval + cutcoeff[1] * ylb - cutcoeff[2] * fval; /* constant */
3139  *convenvvalue = fval;
3140 
3141  SCIPdebugMessage("alpha: %g, beta: %g, gamma: %g, delta: %g\n", cutcoeff[0], cutcoeff[1], cutcoeff[2], cutcoeff[3]);
3142 
3143  return SCIP_OKAY;
3144  }
3145  }
3146  }
3147 
3148  if( SCIPisFeasEQ(scip, yval, yub) )
3149  {
3150  /* y is at it's upper bound */
3151  SCIP_Real x0y0[2];
3152  SCIP_Real grad[2];
3153  SCIP_Real fval;
3154  SCIP_Real xtilde;
3155  SCIP_Real ftilde;
3156 
3157  assert(f_yfixed != NULL);
3158 
3159  /* compute f(xval, yub) and f'(xval, yub) */
3160  x0y0[0] = xval;
3161  x0y0[1] = yub;
3162  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, x0y0, TRUE, &fval, grad) );
3163 
3164  if( !SCIPisFinite(fval) || !SCIPisFinite(grad[0]) || SCIPisInfinity(scip, REALABS(grad[0])) )
3165  {
3166  /* move yval inside domain and continue below, hope this will work better */
3167  perturb(&yval, ylb, yub, 0.001);
3168  }
3169  else
3170  {
3171  /* setup f(x,ylb) */
3172  SCIPexprtreeSetParamVal(f_yfixed, 0, ylb);
3173  SCIP_CALL( SCIPexprintNewParametrization(exprinterpreter, f_yfixed) );
3174 
3175  /* find xtilde in [xlb, xub] such that f'(x,ylb) = f'(xval,yub) */
3176  SCIP_CALL( solveDerivativeEquation(scip, exprinterpreter, f_yfixed, grad[0], xlb, xub, &xtilde, success) );
3177 
3178  if( !*success )
3179  {
3180  SCIP_Real fxlb;
3181  SCIP_Real fxub;
3182 
3183  /* if we could not find an xtilde such that f'(xtilde,ylb) = f'(xval,yub), then probably because f'(x,ylb) is constant
3184  * in this case, choose xtilde from {xlb, xub} such that it maximizes f'(xtilde, yub) - grad[0]*xtilde
3185  */
3186  SCIP_CALL( SCIPexprintEval(exprinterpreter, f_yfixed, &xlb, &fxlb) );
3187  SCIP_CALL( SCIPexprintEval(exprinterpreter, f_yfixed, &xub, &fxub) );
3188 
3189  SCIPdebugMessage("couldn't solve deriv equ, compare f(%g,%g) - %g*%g = %g and f(%g,%g) - %g*%g = %g\n",
3190  xlb, yub, grad[0], xlb, fxlb - grad[0] * xlb,
3191  xub, yub, grad[0], xub, fxub - grad[0] * xub);
3192 
3193  if( SCIPisFinite(fxlb) && SCIPisFinite(fxub) )
3194  {
3195  if( fxlb - grad[0] * xlb < fxub - grad[0] * xub )
3196  xtilde = xlb;
3197  else
3198  xtilde = xub;
3199  *success = TRUE;
3200  }
3201  else
3202  {
3203  /* move yval inside domain and continue below, hope this will work better */
3204  perturb(&yval, ylb, yub, 0.001);
3205  }
3206  }
3207 
3208  if( *success )
3209  {
3210  /* compute f(xtilde, yub) */
3211  SCIP_CALL( SCIPexprintEval(exprinterpreter, f_yfixed, &xtilde, &ftilde) );
3212 
3213  SCIPdebugMessage("xtilde = %g, f(%g,%g) = %g\n", xtilde, xtilde, ylb, ftilde);
3214 
3215  /* set up cut coefficients */
3216  cutcoeff[0] = (yub - ylb) * grad[0];
3217  cutcoeff[1] = grad[0] * (xtilde - xval) - ftilde + fval;
3218  cutcoeff[2] = yub - ylb;
3219  cutcoeff[3] = cutcoeff[0] * xval + cutcoeff[1] * yub - cutcoeff[2] * fval;
3220  *convenvvalue = fval;
3221 
3222  SCIPdebugMessage("alpha: %g, beta: %g, gamma: %g, delta: %g\n", cutcoeff[0], cutcoeff[1], cutcoeff[2], cutcoeff[3]);
3223 
3224  return SCIP_OKAY;
3225  }
3226  }
3227  }
3228 
3229  {
3230  /* x and y are somewhere between the bounds,
3231  * -> envelope is generated from f(x,y) in y=ylb and in y=yub
3232  */
3233  SCIP_Real paramvals[4];
3234 #ifdef SCIP_DEBUG
3235  const char* paramnames[4] = {"x0", "y0", "ylb", "yub"};
3236 #endif
3237  SCIP_Real t;
3238  SCIP_Real slb;
3239  SCIP_Real sub;
3240  SCIP_Real sval;
3241  SCIP_Real rval;
3242  SCIP_Real fsval;
3243  SCIP_Real frval;
3244  SCIP_Real grad[2];
3245  SCIP_Real x0y0[2];
3246 
3247  assert(vred != NULL);
3248 
3249  /* check that variables are not unbounded or fixed and reference point is in interior
3250  * @todo it should also work if x is unbounded, or? */
3251  /* assert(!SCIPisInfinity(scip, -xlb));
3252  assert(!SCIPisInfinity(scip, xub)); */
3253  assert(!SCIPisInfinity(scip, -ylb));
3254  assert(!SCIPisInfinity(scip, yub));
3255 
3256  /* update parameter values in vred */
3257  paramvals[0] = xval;
3258  paramvals[1] = yval;
3259  paramvals[2] = ylb;
3260  paramvals[3] = yub;
3261  SCIP_CALL( SCIPexprtreeSetParams(vred, 4, paramvals) );
3262  SCIP_CALL( SCIPexprintNewParametrization(exprinterpreter, vred) );
3263 
3264  SCIPdebugMessage("vred(s;x0,y0,ylb,yub) = ");
3265  SCIPdebug( SCIPexprtreePrint(vred, SCIPgetMessagehdlr(scip), NULL, NULL, paramnames) );
3266  SCIPdebugPrintf("\n");
3267 
3268  /* compute bounds on s */
3269  t = (yub - yval) / (yub - ylb);
3270  if( !SCIPisInfinity(scip, xub) )
3271  slb = (yval - yub) / (ylb - yval) * (xval / t - xub);
3272  else
3273  slb = -SCIPinfinity(scip);
3274  if( !SCIPisInfinity(scip, xlb) )
3275  sub = (yval - yub) / (ylb - yval) * (xval / t - xlb);
3276  else
3277  sub = SCIPinfinity(scip);
3278  if( slb < xlb )
3279  slb = xlb;
3280  if( sub > xub )
3281  sub = xub;
3282 
3283  /* find s in [slb, sub] such that vred'(s) = 0 */
3284  SCIP_CALL( solveDerivativeEquation(scip, exprinterpreter, vred, 0.0, slb, sub, &sval, success) );
3285  assert(!*success || !SCIPisInfinity(scip, REALABS(sval)));
3286 
3287  if( *success )
3288  {
3289  /* compute r from s */
3290  rval = xval / t + (1.0 - 1.0 / t) * sval;
3291  assert(SCIPisFeasGE(scip, rval, xlb));
3292  assert(SCIPisFeasLE(scip, rval, xub));
3293  rval = MAX(xlb, MIN(rval, xub));
3294 
3295  /* compute f(sval, yub) */
3296  x0y0[0] = sval;
3297  x0y0[1] = yub;
3298  SCIP_CALL( SCIPexprtreeEval(f, x0y0, &fsval) );
3299 
3300  /* compute f(rval, ylb) */
3301  x0y0[0] = rval;
3302  x0y0[1] = ylb;
3303  SCIP_CALL( SCIPexprtreeEval(f, x0y0, &frval) );
3304 
3305  if( !SCIPisEQ(scip, sval, xlb) && !SCIPisEQ(scip, sval, xub) )
3306  {
3307  x0y0[0] = sval;
3308  x0y0[1] = yub;
3309 
3310  /* compute f'(xbar, ybar) */
3311  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, x0y0, TRUE, &fsval, grad) );
3312  }
3313  else if( !SCIPisEQ(scip, rval, xlb) && !SCIPisEQ(scip, rval, xub) )
3314  {
3315  x0y0[0] = rval;
3316  x0y0[1] = ylb;
3317 
3318  /* compute f'(xbar, ybar) */
3319  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, x0y0, TRUE, &frval, grad) );
3320  }
3321  else
3322  {
3323  /* rare case
3324  * both points (sval, yub) and (rval, ylb) should yield valid inequality
3325  * for now, just take the first one, if differentiable, otherwise second one
3326  */
3327  x0y0[0] = sval;
3328  x0y0[1] = yub;
3329 
3330  /* compute f'(xbar, ybar) */
3331  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, x0y0, TRUE, &fsval, grad) );
3332 
3333  if( !SCIPisFinite(grad[0]) )
3334  {
3335  x0y0[0] = rval;
3336  x0y0[1] = ylb;
3337 
3338  /* compute new f'(xbar, ybar) */
3339  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, x0y0, TRUE, &frval, grad) );
3340  }
3341  }
3342 
3343  /* compute vred(s) = t * f(rval, ylb) + (1-t) * f(sval, yub) */
3344  *convenvvalue = t * frval + (1.0 - t) * fsval;
3345 
3346  SCIPdebugMessage("Parallel: Cut of (xval,yval)=(%g,%g)\n",xval,yval);
3347  SCIPdebugMessage("Parallel: r=%g s=%g in [%g,%g], y in [%g,%g], f(r,ylb)=%g, f(xlb,s)=%g\n",rval,sval,xlb,xub,ylb,yub,frval,fsval);
3348  SCIPdebugMessage("(r,ylb)=(%g,%g), (s,yub)=(%g,%g), vredval=%g\n",rval,ylb,sval,yub,*convenvvalue);
3349 
3350  if( !SCIPisFinite(grad[0]) || SCIPisInfinity(scip, REALABS(grad[0])) )
3351  {
3352  SCIPdebugMessage("f not differentiable at (x0,y0) w.r.t. x\n");
3353  *success = FALSE;
3354  return SCIP_OKAY;
3355  }
3356 
3357  /* compute cut coefficients */
3358  cutcoeff[0] = (yub - ylb) * grad[0];
3359  cutcoeff[1] = fsval - frval - (sval - rval) * grad[0];
3360  cutcoeff[2] = yub - ylb;
3361  cutcoeff[3] = cutcoeff[0] * xval + cutcoeff[1] * yval - cutcoeff[2] * *convenvvalue;
3362 
3363  SCIPdebugMessage("Parallel: cutcoeff[0]=%g, cutcoeff[1]=%g,cutcoeff[2]=%g,cutcoeff[3]=%g\n",cutcoeff[0]/cutcoeff[2],cutcoeff[1]/cutcoeff[2],cutcoeff[2]/cutcoeff[2],cutcoeff[3]/cutcoeff[2]);
3364  }
3365  }
3366 
3367  return SCIP_OKAY;
3368 }
3369 
3370 
3371 /** generates a cut for one side of lhs <= f(x,y) + c*z <= rhs with f(x,y) being convex in x and concave in y */
3372 static
3374  SCIP* scip, /**< SCIP data structure */
3375  SCIP_EXPRINT* exprinterpreter, /**< expressions interpreter */
3376  SCIP_CONS* cons, /**< constraint */
3377  SCIP_Real xyref[2], /**< reference values for nonlinear variables */
3378  SCIP_SIDETYPE violside, /**< for which side of constraint to find a cut */
3379  SCIP_ROW** row /**< storage for cut */
3380  )
3381 {
3382  SCIP_CONSDATA* consdata;
3383  SCIP_Real cutcoeff[4];
3384  SCIP_Real dummy;
3385  SCIP_Bool success;
3386  SCIP_Real coefs[2];
3387  char cutname[SCIP_MAXSTRLEN];
3388 
3389  assert(scip != NULL);
3390  assert(SCIPgetStage(scip) == SCIP_STAGE_SOLVING);
3391  assert(cons != NULL);
3392  assert(row != NULL);
3393 
3394  consdata = SCIPconsGetData(cons);
3395  assert(consdata != NULL);
3396  assert(consdata->f != NULL);
3397  assert(consdata->convextype == SCIP_BIVAR_CONVEX_CONCAVE);
3398 
3399  *row = NULL;
3400 
3401  SCIPdebugMessage("generate %sestimator for convex-concave constraint <%s>\n",
3402  (violside == SCIP_SIDETYPE_LEFT ? "over" : "under"), SCIPconsGetName(cons));
3403  SCIPdebugPrintCons(scip, cons, NULL);
3404 
3405  if( violside == SCIP_SIDETYPE_LEFT )
3406  {
3407  /* need overestimator */
3408  assert(!SCIPisInfinity(scip, -consdata->lhs));
3409 
3410  if( consdata->sepaconvexconcave.lineariny )
3411  {
3412  /* f is strictly convex in x and linear in y -> overestimator is polyhedral */
3413  SCIP_Real constant;
3414 
3415  SCIP_CALL( generateEstimatingHyperplane(scip, exprinterpreter, consdata->f, TRUE, xyref, &coefs[0], &coefs[1], &constant, &success) );
3416 
3417  if( success )
3418  {
3419  assert(SCIPisFinite(coefs[0]));
3420  assert(SCIPisFinite(coefs[1]));
3421  assert(SCIPisFinite(constant));
3422 
3423  (void) SCIPsnprintf(cutname, SCIP_MAXSTRLEN, "%s_overesthyperplanecut_%d", SCIPconsGetName(cons), SCIPgetNLPs(scip));
3424  SCIP_CALL( SCIPcreateRowCons(scip, row, SCIPconsGetHdlr(cons), cutname, 0, NULL, NULL, consdata->lhs - constant, SCIPinfinity(scip), TRUE, FALSE, TRUE) );
3425 
3426  SCIP_CALL( SCIPaddVarsToRow(scip, *row, 2, SCIPexprtreeGetVars(consdata->f), coefs) );
3427  if( consdata->z != NULL )
3428  {
3429  SCIP_CALL( SCIPaddVarToRow(scip, *row, consdata->z, consdata->zcoef) );
3430  }
3431  }
3432  }
3433  else
3434  {
3435  SCIP_Real xyref_[2];
3436 
3437  /* f is strictly concave in y -> can compute overestimator by applying generateConvexConcaveUnderstimator on -f(y,x) */
3438  assert(consdata->sepaconvexconcave.f_neg_swapped != NULL);
3439 
3440  xyref_[0] = xyref[1];
3441  xyref_[1] = xyref[0];
3442  SCIP_CALL( generateConvexConcaveUnderestimator(scip, exprinterpreter, consdata->sepaconvexconcave.f_neg_swapped, consdata->sepaconvexconcave.f_neg_swapped_yfixed, consdata->sepaconvexconcave.vred_neg_swapped, xyref_, cutcoeff, &dummy, &success) );
3443 
3444  if( success )
3445  {
3446  assert(SCIPisFinite(cutcoeff[0]));
3447  assert(SCIPisFinite(cutcoeff[1]));
3448  assert(SCIPisFinite(cutcoeff[2]));
3449  assert(SCIPisFinite(cutcoeff[3]));
3450  assert(SCIPisPositive(scip, cutcoeff[2])); /* assert gamma > 0 */
3451 
3452  /* construct row from cut coefficients (alpha, beta, gamma, delta)
3453  * coefficients are such that alpha * y + beta * x - gamma * (-f(x,y)) <= delta,
3454  * i.e., gamma * f(x,y) <= delta - alpha * y - beta * x
3455  * -> lhs <= f(x,y) + c*z <= delta/gamma - alpha/gamma * y - beta/gamma * x + c*z
3456  */
3457  coefs[0] = -cutcoeff[1] / cutcoeff[2];
3458  coefs[1] = -cutcoeff[0] / cutcoeff[2];
3459  (void) SCIPsnprintf(cutname, SCIP_MAXSTRLEN, "%s_convexconcaveoverest_%d", SCIPconsGetName(cons), SCIPgetNLPs(scip));
3460  SCIP_CALL( SCIPcreateEmptyRowCons(scip, row, SCIPconsGetHdlr(cons), cutname, consdata->lhs - cutcoeff[3]/cutcoeff[2], SCIPinfinity(scip),
3461  TRUE, FALSE /* modifiable */, TRUE /* removable */) );
3462  SCIP_CALL( SCIPaddVarsToRow(scip, *row, 2, SCIPexprtreeGetVars(consdata->f), coefs) );
3463  if( consdata->z != NULL )
3464  {
3465  SCIP_CALL( SCIPaddVarToRow(scip, *row, consdata->z, consdata->zcoef) );
3466  }
3467  }
3468  }
3469  }
3470  else
3471  {
3472  /* need underestimator */
3473  assert(violside == SCIP_SIDETYPE_RIGHT);
3474  assert(!SCIPisInfinity(scip, consdata->rhs));
3475 
3476  if( consdata->sepaconvexconcave.linearinx )
3477  {
3478  /* f is linear in x and strictly concave in y -> underestimator is polyhedral */
3479  SCIP_Real constant;
3480 
3481  SCIP_CALL( generateEstimatingHyperplane(scip, exprinterpreter, consdata->f, FALSE, xyref, &coefs[0], &coefs[1], &constant, &success) );
3482 
3483  if( success )
3484  {
3485  assert(SCIPisFinite(coefs[0]));
3486  assert(SCIPisFinite(coefs[1]));
3487  assert(SCIPisFinite(constant));
3488 
3489  (void) SCIPsnprintf(cutname, SCIP_MAXSTRLEN, "%s_underesthyperplanecut_%d", SCIPconsGetName(cons), SCIPgetNLPs(scip));
3490  SCIP_CALL( SCIPcreateRowCons(scip, row, SCIPconsGetHdlr(cons), cutname, 0, NULL, NULL, -SCIPinfinity(scip), consdata->rhs - constant, TRUE, FALSE, TRUE) );
3491 
3492  SCIP_CALL( SCIPaddVarsToRow(scip, *row, 2, SCIPexprtreeGetVars(consdata->f), coefs) );
3493  if( consdata->z != NULL )
3494  {
3495  SCIP_CALL( SCIPaddVarToRow(scip, *row, consdata->z, consdata->zcoef) );
3496  }
3497  }
3498  }
3499  else
3500  {
3501  /* f is strictly convex in x -> can compute underestimator by applying generateConvexConcaveUnderstimator */
3502  assert(!consdata->sepaconvexconcave.linearinx); /* generateConvexConcaveUnderestimator assumes that if f is strictly convex in x */
3503 
3504  SCIP_CALL( generateConvexConcaveUnderestimator(scip, exprinterpreter, consdata->f, consdata->sepaconvexconcave.f_yfixed, consdata->sepaconvexconcave.vred, xyref, cutcoeff, &dummy, &success) );
3505 
3506  if( success )
3507  {
3508  assert(SCIPisFinite(cutcoeff[0]));
3509  assert(SCIPisFinite(cutcoeff[1]));
3510  assert(SCIPisFinite(cutcoeff[2]));
3511  assert(SCIPisFinite(cutcoeff[3]));
3512  assert(SCIPisPositive(scip, cutcoeff[2])); /* assert gamma > 0 */
3513 
3514  /* construct row from cut coefficients (alpha, beta, gamma, delta)
3515  * coefficients are such that alpha * x + beta * y - gamma * f(x,y) <= delta,
3516  * i.e., alpha/gamma * x + beta/gamma * y - delta/gamma <= f(x,y)
3517  * -> alpha/gamma * x + beta/gamma * y - delta/gamma + c*z <= f(x,y) + c*z <= rhs
3518  */
3519 
3520  coefs[0] = cutcoeff[0] / cutcoeff[2];
3521  coefs[1] = cutcoeff[1] / cutcoeff[2];
3522  (void) SCIPsnprintf(cutname, SCIP_MAXSTRLEN, "%s_convexconcaveunderest_%d", SCIPconsGetName(cons), SCIPgetNLPs(scip));
3523  SCIP_CALL( SCIPcreateEmptyRowCons(scip, row, SCIPconsGetHdlr(cons), cutname, -SCIPinfinity(scip), consdata->rhs + cutcoeff[3]/cutcoeff[2],
3524  TRUE, FALSE /* modifiable */, TRUE /* removable */) );
3525  SCIP_CALL( SCIPaddVarsToRow(scip, *row, 2, SCIPexprtreeGetVars(consdata->f), coefs) );
3526  if( consdata->z != NULL )
3527  {
3528  SCIP_CALL( SCIPaddVarToRow(scip, *row, consdata->z, consdata->zcoef) );
3529  }
3530  }
3531  }
3532  }
3533 
3534  return SCIP_OKAY;
3535 }
3536 
3537 
3538 /** computes an underestimating hyperplane for functions that are convex in x and y if the point to cut off lies on the boundary */
3539 static
3541  SCIP* scip, /**< SCIP data structure */
3542  SCIP_EXPRINT* exprinterpreter, /**< expressions interpreter */
3543  SCIP_EXPRTREE* f, /**< function f(x,y) */
3544  SCIP_Real xval, /**< current x value */
3545  SCIP_Real yval, /**< current y value */
3546  SCIP_Real xlb, /**< lower bound x */
3547  SCIP_Real xub, /**< upper bound x */
3548  SCIP_Real ylb, /**< lower bound y */
3549  SCIP_Real yub, /**< upper bound y */
3550  int min_max, /**< min=-1 max=1 */
3551  SCIP_Real cutcoeff[4], /**< returns the lifting coefficient*/
3552  SCIP_Real* convenvvalue, /**< value of the convex envelope at (xval,yval) */
3553  SCIP_Bool* success /**< buffer to indicate whether lifting was successful */
3554  )
3555 {
3556  int idx; /* indicates which variable is at the boundary */
3557 
3558  SCIP_Real mu;
3559  SCIP_Real fval;
3560  SCIP_Real grad[2];
3561 
3562  SCIP_Real x0y0[2];
3563  SCIP_Real f_lb;
3564  SCIP_Real f_ub;
3565  SCIP_Real grad_lb[2];
3566  SCIP_Real grad_ub[2];
3567 
3568  assert(SCIPisEQ(scip,xlb,xub) || SCIPisEQ(scip,ylb,yub));
3569  assert(success != NULL);
3570 
3571  *success = FALSE;
3572  idx = SCIPisEQ(scip, xlb, xub) ? 0 : 1;
3573 
3574  /* determine mu
3575  * if f is bivariate quadratic then f_x(xlb,yval) is linear in yval
3576  * thus the minimum is attained at the lower or the upper bound
3577  */
3578  x0y0[0] = xlb;
3579  x0y0[1] = ylb;
3580  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, x0y0, TRUE, &f_lb, grad_lb) );
3581  if( !SCIPisFinite(grad_lb[idx]) )
3582  return SCIP_OKAY;
3583 
3584  x0y0[0] = xub;
3585  x0y0[1] = yub;
3586  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, x0y0, TRUE, &f_ub, grad_ub) );
3587  if( !SCIPisFinite(grad_ub[idx]) )
3588  return SCIP_OKAY;
3589 
3590  /* if min_max=-1 choose min( grad_lb[idx], grad_ub[idx] )
3591  * if min_max= 1 choose max( grad_lb[idx], grad_ub[idx] )
3592  */
3593  if( min_max * (grad_lb[idx] - grad_ub[idx]) >= 0 )
3594  mu = grad_lb[idx];
3595  else
3596  mu = grad_ub[idx];
3597 
3598  /* determine coefficients for the hyperplane */
3599  x0y0[0] = xval;
3600  x0y0[1] = yval;
3601  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, x0y0, TRUE, &fval, grad) );
3602 
3603  if( idx == 0 )
3604  {
3605  if( !SCIPisFinite(grad[1]) || SCIPisInfinity(scip, REALABS(grad[1])) )
3606  return SCIP_OKAY;
3607  cutcoeff[0] = mu;
3608  cutcoeff[1] = grad[1];
3609  }
3610  else
3611  {
3612  assert(idx == 1);
3613  if( !SCIPisFinite(grad[0]) || SCIPisInfinity(scip, REALABS(grad[0])) )
3614  return SCIP_OKAY;
3615  cutcoeff[0] = grad[0];
3616  cutcoeff[1] = mu;
3617  }
3618  cutcoeff[2] = 1;
3619  cutcoeff[3] = -(fval-cutcoeff[0]*xval-cutcoeff[1]*yval);
3620  *convenvvalue = fval;
3621  *success = TRUE;
3622 
3623  return SCIP_OKAY;
3624 }
3625 
3626 /** generate a linear underestimator for f(x,y) with f(x,y) being convex in x and convex in y and the point to cut off lies on the boundary
3627  * generate coefficients cutcoeff = (alpha, beta, gamma, delta), such that alpha * x + beta * y - delta <= gamma * f(x,y)
3628  */
3629 static
3631  SCIP* scip, /**< SCIP data structure */
3632  SCIP_EXPRINT* exprinterpreter, /**< expressions interpreter */
3633  SCIP_EXPRTREE* f, /**< function f(x,y) */
3634  SCIP_Real xyref[2], /**< reference values for x and y */
3635  SCIP_Real cutcoeff[4], /**< cut coefficients alpha, beta, gamma, delta */
3636  SCIP_Real* convenvvalue, /**< function value of the convex envelope */
3637  SCIP_Bool* success /**< buffer to store whether coefficients were successfully computed */
3638  )
3639 {
3640  SCIP_VAR* x;
3641  SCIP_VAR* y;
3642  SCIP_Real xval;
3643  SCIP_Real xlb;
3644  SCIP_Real xub;
3645  SCIP_Real yval;
3646  SCIP_Real ylb;
3647  SCIP_Real yub;
3648 
3649  assert(scip != NULL);
3650  assert(exprinterpreter != NULL);
3651  assert(f != NULL);
3652  assert(convenvvalue != NULL);
3653  assert(success != NULL);
3654 
3655  x = SCIPexprtreeGetVars(f)[0];
3656  y = SCIPexprtreeGetVars(f)[1];
3657 
3658  xlb = SCIPvarGetLbLocal(x);
3659  xub = SCIPvarGetUbLocal(x);
3660 
3661  ylb = SCIPvarGetLbLocal(y);
3662  yub = SCIPvarGetUbLocal(y);
3663 
3664  *success = FALSE;
3665 
3666  SCIPdebugMessage("f(%s, %s) = ", SCIPvarGetName(x), SCIPvarGetName(y));
3668  SCIPdebugPrintf("\n");
3669 
3670  xval = xyref[0];
3671  yval = xyref[1];
3672 
3673  SCIPdebugMessage("xval=%g in [%g,%g], yval=%g in [%g,%g]\n",xval,xlb,xub,yval,ylb,yub);
3674 
3675  if( SCIPisEQ(scip, ylb, yub) )
3676  {
3677  /* y is fixed, so function is now convex -> linearize in (xval, ylb) */
3678  SCIP_Real xy[2];
3679  SCIP_Real grad[2];
3680  SCIP_Real fval;
3681 
3682  xy[0] = xval;
3683  xy[1] = ylb;
3684  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, xy, TRUE, &fval, grad) );
3685  if( !SCIPisFinite(grad[0]) || SCIPisInfinity(scip, REALABS(grad[0])) )
3686  return SCIP_OKAY;
3687 
3688  /* linearization is f(xval,ylb) + df/dx(xval,ylb) * (x - xval) <= f(x,y) */
3689 
3690  cutcoeff[0] = grad[0]; /* coefficient of x == gradient in x */
3691  cutcoeff[1] = 0.0; /* coefficient of y == 0 */
3692  cutcoeff[2] = 1.0; /* coefficient of f(x,y) == 1.0 */
3693  cutcoeff[3] = -(fval - grad[0] * xval); /* constant == -(f(xval,ylb) - grad * xval) */
3694 
3695  *success = TRUE;
3696  return SCIP_OKAY;
3697  }
3698 
3699  if( SCIPisEQ(scip, xlb, xub) )
3700  {
3701  /* x is fixed, so function is now convex -> linearize in (xlb, yval) */
3702  SCIP_Real xy[2];
3703  SCIP_Real grad[2];
3704  SCIP_Real fval;
3705 
3706  xy[0] = xlb;
3707  xy[1] = yval;
3708  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, xy, TRUE, &fval, grad) );
3709  if( !SCIPisFinite(grad[1]) || SCIPisInfinity(scip, REALABS(grad[1])) )
3710  return SCIP_OKAY;
3711 
3712  /* linearization is f(xlb,yval) + df/dy(xlb,yval) * (y - yval) <= f(x,y) */
3713 
3714  cutcoeff[0] = 0.0; /* coefficient of x == 0.0 */
3715  cutcoeff[1] = grad[1]; /* coefficient of y == gradient in y */
3716  cutcoeff[2] = 1.0; /* coefficient of f(x,y) == 1.0 */
3717  cutcoeff[3] = -(fval - grad[1] * yval); /* constant == -(f(xlb,yval) - grad * yval) */
3718 
3719  *success = TRUE;
3720  return SCIP_OKAY;
3721  }
3722 
3723  /* check if the points lie on a boundary */
3724  if( SCIPisFeasEQ(scip, xlb, xval) )
3725  {
3726  /* apply a lifting and exploit that the function is convex in x and y
3727  * Idea: f(xlb,y) + mu (x-xlb) <= f(x,y)
3728  * determine mu with mu <= min_{x,y} ( f(x,y)-f(xlb,y) ) / (x-xlb)
3729  * f is convex in x: mu<= min_{y} f_x(xlb,y)
3730  *
3731  * mu (x-lb) + f_y(xlb,yval) * y <= f(x,y)
3732  */
3733  xval = xlb;
3734 
3735  SCIP_CALL( lifting(scip,exprinterpreter,f,xval,yval,xlb,xlb,ylb,yub,-1,cutcoeff,convenvvalue,success) );
3736 
3737  if( !*success )
3738  return SCIP_OKAY;
3739 
3740  SCIPdebugMessage("Boundary x=lb: Cut of (xval,yval)=(%g,%g)\n",xval,yval);
3741  SCIPdebugMessage("convenvvalue = %g\n",*convenvvalue);
3742  SCIPdebugMessage("cutcoeff[0]=%g, cutcoeff[1]=%g,cutcoeff[2]=%g,cutcoeff[3]=%g\n",
3743  cutcoeff[0],cutcoeff[1],cutcoeff[2],cutcoeff[3]);
3744 
3745  return SCIP_OKAY;
3746  }
3747 
3748  if( SCIPisFeasEQ(scip, ylb, yval) )
3749  {
3750  yval = ylb;
3751 
3752  SCIP_CALL( lifting(scip,exprinterpreter,f,xval,yval,xlb,xub,ylb,ylb,-1,cutcoeff,convenvvalue,success) );
3753 
3754  if( !*success )
3755  return SCIP_OKAY;
3756 
3757  SCIPdebugMessage("Boundary y=lb: Cut of (xval,yval)=(%g,%g)\n",xval,yval);
3758  SCIPdebugMessage("convenvvalue = %g\n",*convenvvalue);
3759  SCIPdebugMessage("cutcoeff[0]=%g, cutcoeff[1]=%g,cutcoeff[2]=%g,cutcoeff[3]=%g\n",
3760  cutcoeff[0],cutcoeff[1],cutcoeff[2],cutcoeff[3]);
3761 
3762  return SCIP_OKAY;
3763  }
3764 
3765  if( SCIPisFeasEQ(scip, xub, xval) )
3766  {
3767  /* apply a lifting and exploit that the function is convex in x and y
3768  * Idea: f(xlb,y) + mu (xub-x) <= f(x,y)
3769  * determine mu with mu <= min_{x,y} ( f(x,y)-f(xub,y) ) / (xub-x)
3770  * f is convex in x: -1 * mu >= min_{y} f_x(xub,y)
3771  *
3772  * mu (xub-x) + f_y(xub,yval) * y <= f(x,y)
3773  * -mu*x -mu*xub + f_y(xub,yval) * y <= f(x,y)
3774  */
3775  xval = xub;
3776 
3777  SCIP_CALL( lifting(scip,exprinterpreter,f,xval,yval,xub,xub,ylb,yub,1,cutcoeff,convenvvalue,success) );
3778 
3779  if( !*success )
3780  return SCIP_OKAY;
3781 
3782  SCIPdebugMessage("Boundary x=ub: Cut of (xval,yval)=(%g,%g)\n",xval,yval);
3783  SCIPdebugMessage("convenvvalue = %g\n",*convenvvalue);
3784  SCIPdebugMessage("cutcoeff[0]=%g, cutcoeff[1]=%g,cutcoeff[2]=%g,cutcoeff[3]=%g\n",
3785  cutcoeff[0],cutcoeff[1],cutcoeff[2],cutcoeff[3]);
3786 
3787  return SCIP_OKAY;
3788  }
3789 
3790  if( SCIPisFeasEQ(scip, yub, yval) )
3791  {
3792  yval = yub;
3793 
3794  SCIP_CALL( lifting(scip,exprinterpreter,f,xval,yval,xlb,xub,yub,yub,1,cutcoeff,convenvvalue,success) );
3795 
3796  if( !*success )
3797  return SCIP_OKAY;
3798 
3799  SCIPdebugMessage("Boundary y=ub: Cut of (xval,yval)=(%g,%g)\n",xval,yval);
3800  SCIPdebugMessage("convenvvalue = %g\n",*convenvvalue);
3801  SCIPdebugMessage("cutcoeff[0]=%g, cutcoeff[1]=%g,cutcoeff[2]=%g,cutcoeff[3]=%g\n",
3802  cutcoeff[0],cutcoeff[1],cutcoeff[2],cutcoeff[3]);
3803 
3804  return SCIP_OKAY;
3805  }
3806 
3807  /* (xval,yval) lies in the interior */
3808  SCIPerrorMessage("Tries to compute underestimator for a point at the boundary. But point is not on the boundary!\n");
3809  return SCIP_ERROR;
3810 }
3811 
3812 /** generates a linear underestimator for f(x,y) with f(x,y) being convex in x and convex in y but indefinite
3813  * This is for the case where the cone of the concave directions is (R_+ x R_-) union (R_\- x R_+).
3814  * We consider two cases:
3815  * a) the underestimating segmenent connects parallel facets
3816  * b) the underestimating segmenent connects orthogonal facets where
3817  * x=l_x, y=l_y and x=u_x, y=u_y
3818  * We ensure that the parallel facets are the horizontal with y=l_y and y=u_y
3819  * We compute the objective value of the two problems.
3820  * The smaller objective value corresponds to the convex envelope.
3821  * The supporting hyperplane is then constructed at the this point.
3822  */
3823 static
3825  SCIP* scip, /**< SCIP data structure */
3826  SCIP_EXPRINT* exprinterpreter, /**< expressions interpreter */
3827  SCIP_EXPRTREE* f, /**< function f(x,y) */
3828  SCIP_Real xyref[2], /**< reference values for x and y */
3829  SCIP_Real cutcoeff[4], /**< cut coefficients alpha, beta, gamma, delta */
3830  SCIP_Real* convenvvalue, /**< function value of the convex envelope */
3831  SCIP_Bool* success /**< buffer to store whether coefficients were successfully computed */
3832  )
3833 {
3834  SCIP_VAR* x;
3835  SCIP_VAR* y;
3836  SCIP_Real xval;
3837  SCIP_Real xlb;
3838  SCIP_Real xub;
3839  SCIP_Real yval;
3840  SCIP_Real ylb;
3841  SCIP_Real yub;
3842  SCIP_Real xub_ylb[2];
3843  SCIP_Real xlb_yub[2];
3844  SCIP_Real grad_xub_ylb[2];
3845  SCIP_Real grad_xlb_yub[2];
3846  SCIP_Real fval_xub_ylb;
3847  SCIP_Real fval_xlb_yub;
3848 
3849  SCIP_Real all_cutcoeff[2][4];
3850  SCIP_Real all_convenvvalue[2];
3851  SCIP_Bool all_success[2];
3852 
3853  SCIP_Real lowest;
3854  int lowestidx;
3855  int i;
3856 
3857  SCIP_EXPRTREE* fswapped;
3858  SCIP_VAR* vars[2];
3859  SCIP_Bool swapped;
3860  SCIP_Real swap_buffer;
3861  SCIP_EXPR* subst[2];
3862 
3863  assert(scip != NULL);
3864  assert(exprinterpreter != NULL);
3865  assert(f != NULL);
3866  assert(convenvvalue != NULL);
3867  assert(success != NULL);
3868 
3869  x = SCIPexprtreeGetVars(f)[0];
3870  y = SCIPexprtreeGetVars(f)[1];
3871 
3872  xlb = SCIPvarGetLbLocal(x);
3873  xub = SCIPvarGetUbLocal(x);
3874 
3875  ylb = SCIPvarGetLbLocal(y);
3876  yub = SCIPvarGetUbLocal(y);
3877 
3878  *success = FALSE;
3879 
3880  xval = xyref[0];
3881  yval = xyref[1];
3882 
3883  xub_ylb[0] = xub;
3884  xub_ylb[1] = ylb;
3885  xlb_yub[0] = xlb;
3886  xlb_yub[1] = yub;
3887 
3888  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, xub_ylb, TRUE, &fval_xub_ylb, grad_xub_ylb) );
3889  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, xlb_yub, TRUE, &fval_xlb_yub, grad_xlb_yub) );
3890 
3891  if( !SCIPisFinite(fval_xub_ylb) || SCIPisInfinity(scip, REALABS(fval_xub_ylb)) || !SCIPisFinite(fval_xlb_yub) || SCIPisInfinity(scip, REALABS(fval_xlb_yub)) )
3892  {
3893  SCIPdebugMessage("skip 1-convex underestimator since function cannot be evaluated\n");
3894  return SCIP_OKAY;
3895  }
3896 
3897  if( !SCIPisFinite(grad_xub_ylb[0]) || !SCIPisFinite(grad_xlb_yub[1]) )
3898  {
3899  SCIPdebugMessage("skip 1-convex underestimator since function cannot be differentiated\n");
3900  return SCIP_OKAY;
3901  }
3902 
3903  SCIPdebugMessage("f(%s, %s) = ", SCIPvarGetName(x), SCIPvarGetName(y));
3905  SCIPdebugPrintf("\n");
3906 
3907  SCIPdebugMessage("xval=%g in [%g,%g], yval=%g in [%g,%g]\n", xval, xlb, xub, yval, ylb, yub);
3908 
3909  /* assure (xub-xlb)*f_x(xub,ylb) - (yub-ylb)*f_y(xlb,yub) >= f(xub,ylb) - f(xlb,yub) */
3910  /* f_y(xlb,yub)*(ylb-yub)* + f(xlb,yub) >= f_x(xub,ylb)*(xub-xlb) + f(xub,ylb) */
3911  if( fval_xub_ylb-fval_xlb_yub <= (xub-xlb)*grad_xub_ylb[0]-(yub-ylb)*grad_xlb_yub[1] )
3912  {
3913  swapped = 0;
3914  }
3915  else
3916  {
3917  /* swap the variables */
3918  swapped = 1;
3919 
3920  vars[0] = SCIPexprtreeGetVars(f)[1];
3921  vars[1] = SCIPexprtreeGetVars(f)[0];
3922 
3923  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &subst[0], SCIP_EXPR_VARIDX, 1) );
3924  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &subst[1], SCIP_EXPR_VARIDX, 0) );
3925 
3926  SCIP_CALL( SCIPexprtreeCopy(SCIPblkmem(scip), &fswapped, f) );
3927  SCIP_CALL( SCIPexprtreeSubstituteVars(fswapped, subst) );
3928  SCIP_CALL( SCIPexprtreeSetVars(fswapped, 2, vars) );
3929  SCIP_CALL( SCIPexprintCompile(exprinterpreter, fswapped) );
3930 
3931  SCIPexprFreeDeep(SCIPblkmem(scip), &subst[0]);
3932  SCIPexprFreeDeep(SCIPblkmem(scip), &subst[1]);
3933  }
3934 
3935  if( swapped == 0 )
3936  {
3937  /* assume (xval,yval) lie in A1 (lower left triangle) or A2 (upper right triangle) */
3938  SCIP_CALL( generateOrthogonal_lx_ly_Underestimator(scip, exprinterpreter, f, xyref, all_cutcoeff[0], &all_convenvvalue[0], &all_success[0]) );
3939  /* assume (xval,yval) lie in A3 */
3940  SCIP_CALL( generateUnderestimatorParallelYFacets(scip, exprinterpreter, f, xyref, all_cutcoeff[1], &all_convenvvalue[1], &all_success[1]) );
3941  }
3942  else
3943  {
3944  SCIP_Real xyref_[2];
3945 
3946  assert(swapped == 1);
3947 
3948  xyref_[0] = xyref[1];
3949  xyref_[1] = xyref[0];
3950 
3951  /* assume (xval,yval) lie in A1 (lower left triangle) or A2 (upper right triangle) */
3952  SCIP_CALL( generateOrthogonal_lx_ly_Underestimator(scip, exprinterpreter, fswapped, xyref_, all_cutcoeff[0], &all_convenvvalue[0], &all_success[0]) ); /*lint !e644*/
3953  /* assume (xval,yval) lie in A3 */
3954  SCIP_CALL( generateUnderestimatorParallelYFacets(scip, exprinterpreter, fswapped, xyref_, all_cutcoeff[1], &all_convenvvalue[1], &all_success[1]) );
3955 
3956  /* swap back */
3957  swap_buffer = all_cutcoeff[0][0];
3958  all_cutcoeff[0][0] = all_cutcoeff[0][1];
3959  all_cutcoeff[0][1] = swap_buffer;
3960 
3961  swap_buffer = all_cutcoeff[1][0];
3962  all_cutcoeff[1][0] = all_cutcoeff[1][1];
3963  all_cutcoeff[1][1] = swap_buffer;
3964 
3965  SCIP_CALL( SCIPexprtreeFree(&fswapped) );
3966  }
3967 
3968  /* Select the underestimator with the lowest convex envelope */
3969  SCIPdebugMessage("\n");
3970  SCIPdebugMessage("Triangulation: convenvvalue=%g\n", all_convenvvalue[0]);
3971  SCIPdebugMessage("Parallel Y: convenvvalue=%g\n", all_convenvvalue[1]);
3972 
3973  lowest = SCIPinfinity(scip);
3974  lowestidx = -1;
3975 
3976  if( all_success[0] && all_success[1] )
3977  {
3978  *success = TRUE;
3979  for( i = 0; i < 2; ++i )
3980  {
3981  assert(SCIPisFinite(all_cutcoeff[i][0]));
3982  assert(SCIPisFinite(all_cutcoeff[i][1]));
3983  assert(SCIPisFinite(all_cutcoeff[i][2]));
3984  assert(SCIPisFinite(all_cutcoeff[i][3]));
3985 
3986  if( all_convenvvalue[i] < lowest )
3987  {
3988  /* if all_convenvvalue[0] == all_convenvalue[1], take all_convenvvalue[0] */
3989  lowest = all_convenvvalue[i];
3990  lowestidx = i;
3991  }
3992  }
3993  assert(lowestidx >= 0);
3994 
3995  *convenvvalue = all_convenvvalue[lowestidx];
3996  cutcoeff[0] = all_cutcoeff[lowestidx][0];
3997  cutcoeff[1] = all_cutcoeff[lowestidx][1];
3998  cutcoeff[2] = all_cutcoeff[lowestidx][2];
3999  cutcoeff[3] = all_cutcoeff[lowestidx][3];
4000  assert(SCIPisPositive(scip, cutcoeff[2])); /* assert gamma > 0 */
4001  }
4002  else
4003  {
4004  *success = FALSE;
4005  }
4006 
4007  return SCIP_OKAY;
4008 }
4009 
4010 
4011 /** generates a linear underestimator for f(x,y) with f(x,y) being convex in x and convex in y but indefinite
4012  * This is for the case where the cone of the concave directions is (R_+ x R_+) union (R_- x R_-).
4013  * We consider two cases:
4014  * a) the underestimating segmenent connects parallel facets
4015  * b) the underestimating segmenent connects orthogonal facets where
4016  * x=l_x, y=u_y and x=u_x, y=l_y
4017  * We ensure that the parallel facets are the horizontal with y=l_y and y=u_y
4018  * We compute the objective value of the two problems.
4019  * The smaller objective value corresponds to the convex envelope.
4020  * The supporting hyperplane is then constructed at the this point.
4021  * Generates coefficients cutcoeff = (alpha, beta, gamma, delta), such that alpha * x + beta * y - delta <= gamma * f(x,y)
4022  */
4023 static
4025  SCIP* scip, /**< SCIP data structure */
4026  SCIP_EXPRINT* exprinterpreter, /**< expressions interpreter */
4027  SCIP_EXPRTREE* f, /**< function f(x,y) */
4028  SCIP_Real xyref[2], /**< reference values for x and y */
4029  SCIP_Real cutcoeff[4], /**< cut coefficients alpha, beta, gamma, delta */
4030  SCIP_Real* convenvvalue, /**< function value of the convex envelope */
4031  SCIP_Bool* success /**< buffer to store whether coefficients were successfully computed */
4032  )
4033 {
4034  SCIP_VAR* x;
4035  SCIP_VAR* y;
4036  SCIP_Real xval;
4037  SCIP_Real xlb;
4038  SCIP_Real xub;
4039  SCIP_Real yval;
4040  SCIP_Real ylb;
4041  SCIP_Real yub;
4042  SCIP_Real xlb_ylb[2];
4043  SCIP_Real xub_yub[2];
4044  SCIP_Real grad_xlb_ylb[2];
4045  SCIP_Real grad_xub_yub[2];
4046  SCIP_Real fval_xlb_ylb;
4047  SCIP_Real fval_xub_yub;
4048 
4049  SCIP_Real all_cutcoeff[2][4];
4050  SCIP_Real all_convenvvalue[2];
4051  SCIP_Bool all_success[2];
4052 
4053  SCIP_Real lowest;
4054  int lowestidx;
4055  int i;
4056 
4057  SCIP_EXPRTREE* fswapped;
4058  SCIP_VAR* vars[2];
4059  SCIP_Bool swapped;
4060  SCIP_Real swap_buffer;
4061  SCIP_EXPR* subst[2];
4062 
4063  assert(scip != NULL);
4064  assert(exprinterpreter != NULL);
4065  assert(f != NULL);
4066  assert(convenvvalue != NULL);
4067  assert(success != NULL);
4068 
4069  x = SCIPexprtreeGetVars(f)[0];
4070  y = SCIPexprtreeGetVars(f)[1];
4071 
4072  xlb = SCIPvarGetLbLocal(x);
4073  xub = SCIPvarGetUbLocal(x);
4074 
4075  ylb = SCIPvarGetLbLocal(y);
4076  yub = SCIPvarGetUbLocal(y);
4077 
4078  *success = FALSE;
4079 
4080  SCIPdebugMessage("f(%s, %s) = ", SCIPvarGetName(x), SCIPvarGetName(y));
4082  SCIPdebugPrintf("\n");
4083 
4084  xval = xyref[0];
4085  yval = xyref[1];
4086 
4087  xlb_ylb[0] = xlb;
4088  xlb_ylb[1] = ylb;
4089  xub_yub[0] = xub;
4090  xub_yub[1] = yub;
4091 
4092  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, xlb_ylb, TRUE, &fval_xlb_ylb, grad_xlb_ylb) );
4093  SCIP_CALL( SCIPexprintGrad(exprinterpreter, f, xub_yub, TRUE, &fval_xub_yub, grad_xub_yub) );
4094 
4095  if( !SCIPisFinite(fval_xlb_ylb) || SCIPisInfinity(scip, REALABS(fval_xlb_ylb)) || !SCIPisFinite(fval_xub_yub) || SCIPisInfinity(scip, REALABS(fval_xub_yub)) )
4096  {
4097  SCIPdebugMessage("skip 1-convex underestimator since function cannot be evaluated\n");
4098  return SCIP_OKAY;
4099  }
4100 
4101  if( !SCIPisFinite(grad_xlb_ylb[1]) || !SCIPisFinite(grad_xub_yub[0]) )
4102  {
4103  SCIPdebugMessage("skip 1-convex underestimator since function cannot be differentiated\n");
4104  return SCIP_OKAY;
4105  }
4106 
4107  SCIPdebugMessage("xval=%g in [%g,%g], yval=%g in [%g,%g]\n",xval,xlb,xub,yval,ylb,yub);
4108 
4109  /* assure f_y(xlb,ylb)*(yub-ylb)* + f(xlb,ylb) >= f_x(xub,yub)*(xlb-xub) + f(xub,yub) */
4110  if( SCIPisGE( scip, fval_xlb_ylb+(yub-ylb)*grad_xlb_ylb[1], fval_xub_yub+(xlb-xub)*grad_xub_yub[0] ) )
4111  {
4112  swapped = 0;
4113  }
4114  else
4115  {
4116  /* swap the variables */
4117  swapped = 1;
4118 
4119  vars[0] = SCIPexprtreeGetVars(f)[1];
4120  vars[1] = SCIPexprtreeGetVars(f)[0];
4121 
4122  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &subst[0], SCIP_EXPR_VARIDX, 1) );
4123  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &subst[1], SCIP_EXPR_VARIDX, 0) );
4124 
4125  SCIP_CALL( SCIPexprtreeCopy(SCIPblkmem(scip), &fswapped, f) );
4126  SCIP_CALL( SCIPexprtreeSubstituteVars(fswapped, subst) );
4127  SCIP_CALL( SCIPexprtreeSetVars(fswapped, 2, vars) );
4128  SCIP_CALL( SCIPexprintCompile(exprinterpreter, fswapped) );
4129 
4130  SCIPexprFreeDeep(SCIPblkmem(scip), &subst[0]);
4131  SCIPexprFreeDeep(SCIPblkmem(scip), &subst[1]);
4132  }
4133 
4134  if( swapped == 0 )
4135  {
4136  /* assume (xval,yval) lie in A1 (lower left triangle) or A2 (upper right triangle) */
4137  SCIP_CALL( generateOrthogonal_lx_uy_Underestimator(scip, exprinterpreter, f, xyref, all_cutcoeff[0], &all_convenvvalue[0], &all_success[0]) );
4138  /* assume (xval,yval) lie in A3*/
4139  SCIP_CALL( generateUnderestimatorParallelYFacets(scip, exprinterpreter, f, xyref, all_cutcoeff[1], &all_convenvvalue[1], &all_success[1]) );
4140  }
4141  else
4142  {
4143  SCIP_Real xyref_[2];
4144 
4145  assert(swapped == 1);
4146 
4147  xyref_[0] = xyref[1];
4148  xyref_[1] = xyref[0];
4149  /* assume (xval,yval) lie in A1 (upper left triangle) or A2 (lower left triangle) */
4150  SCIP_CALL( generateOrthogonal_lx_uy_Underestimator(scip, exprinterpreter, fswapped, xyref_, all_cutcoeff[0], &all_convenvvalue[0], &all_success[0]) ); /*lint !e644*/
4151  /* assume (xval,yval) lie in A3 */
4152  SCIP_CALL( generateUnderestimatorParallelYFacets(scip, exprinterpreter, fswapped, xyref_, all_cutcoeff[1], &all_convenvvalue[1], &all_success[1]) );
4153 
4154  /* swap back */
4155  swap_buffer = all_cutcoeff[0][0];
4156  all_cutcoeff[0][0] = all_cutcoeff[0][1];
4157  all_cutcoeff[0][1] = swap_buffer;
4158 
4159  swap_buffer = all_cutcoeff[1][0];
4160  all_cutcoeff[1][0] = all_cutcoeff[1][1];
4161  all_cutcoeff[1][1] = swap_buffer;
4162 
4163  SCIP_CALL( SCIPexprtreeFree(&fswapped) );
4164  }
4165 
4166  /* select the underestimator with the lowest convex envelope */
4167  SCIPdebugMessage("\n");
4168  SCIPdebugMessage("Triangulation: convenvvalue=%g\n", all_convenvvalue[0]);
4169  SCIPdebugMessage("Parallel Y: convenvvalue=%g\n", all_convenvvalue[1]);
4170 
4171  lowest = SCIPinfinity(scip);
4172  lowestidx = -1;
4173 
4174  if( all_success[0] && all_success[1] )
4175  {
4176  *success = TRUE;
4177  for( i = 0; i < 2; ++i )
4178  {
4179  assert(SCIPisFinite(all_cutcoeff[i][0]));
4180  assert(SCIPisFinite(all_cutcoeff[i][1]));
4181  assert(SCIPisFinite(all_cutcoeff[i][2]));
4182  assert(SCIPisFinite(all_cutcoeff[i][3]));
4183 
4184  /* if all_convenvvalue[0]==all_convenvalue[1], take all_convenvvalue[0] */
4185  if( all_convenvvalue[i] < lowest )
4186  {
4187  lowest = all_convenvvalue[i];
4188  lowestidx = i;
4189  }
4190  }
4191  assert(lowestidx >= 0);
4192 
4193  *convenvvalue = all_convenvvalue[lowestidx];
4194  cutcoeff[0] = all_cutcoeff[lowestidx][0];
4195  cutcoeff[1] = all_cutcoeff[lowestidx][1];
4196  cutcoeff[2] = all_cutcoeff[lowestidx][2];
4197  cutcoeff[3] = all_cutcoeff[lowestidx][3];
4198  assert(SCIPisPositive(scip, cutcoeff[2])); /* assert gamma > 0 */
4199  }
4200  else
4201  {
4202  *success = FALSE;
4203  }
4204 
4205  return SCIP_OKAY;
4206 }
4207 
4208 
4209 /** generates a linear underestimator for f(x,y) with f(x,y) being convex in x and convex in y but indefinite
4210  * generate coefficients cutcoeff = (alpha, beta, gamma, delta), such that alpha * x + beta * y - delta <= gamma * f(x,y)
4211  * 1. If the point lies on the boundary we apply the lifting technique.
4212  * 2. If the point lies in the interior we check the pattern of
4213  * the concave directions and compute the corresponding underestimators.
4214  */
4215 static
4217  SCIP* scip, /**< SCIP data structure */
4218  SCIP_EXPRINT* exprinterpreter, /**< expressions interpreter */
4219  SCIP_CONS* cons, /**< constraint */
4220  SCIP_Real* xyref, /**< reference values for x and y */
4221  SCIP_ROW** row /**< storage for cut */
4222  )
4223 {
4224  SCIP_CONSDATA* consdata;
4225  SCIP_EXPRTREE* f;
4226  SCIP_Real cutcoeff[4];
4227  SCIP_Bool success;
4228  SCIP_Real rhs;
4229  SCIP_Real convenvvalue;
4230 
4231  SCIP_VAR* x;
4232  SCIP_VAR* y;
4233  SCIP_Real xlb;
4234  SCIP_Real xub;
4235  SCIP_Real ylb;
4236  SCIP_Real yub;
4237  SCIP_Real xy_mid[2];
4238  SCIP_Real fval_mid;
4239  SCIP_Real hess[4];
4240 
4241  assert(scip != NULL);
4242  assert(cons != NULL);
4243  assert(row != NULL);
4244 
4245  consdata = SCIPconsGetData(cons);
4246  assert(consdata != NULL);
4247 
4248  assert(consdata->convextype == SCIP_BIVAR_1CONVEX_INDEFINITE);
4249 
4250  assert(!SCIPisInfinity(scip, consdata->rhs));
4251 
4252  f = consdata->f;
4253 
4254  x = SCIPexprtreeGetVars(f)[0];
4255  y = SCIPexprtreeGetVars(f)[1];
4256 
4257  xlb = SCIPvarGetLbLocal(x);
4258  xub = SCIPvarGetUbLocal(x);
4259 
4260  ylb = SCIPvarGetLbLocal(y);
4261  yub = SCIPvarGetUbLocal(y);
4262 
4263  xy_mid[0] = 0.5 * (xlb+xub);
4264  xy_mid[1] = 0.5 * (ylb+yub);
4265 
4266  /* assert that the bounds are finite */
4267  if( SCIPisInfinity(scip, -xlb) || SCIPisInfinity(scip, xub) || SCIPisInfinity(scip, -ylb) || SCIPisInfinity(scip, yub) )
4268  {
4269  SCIPdebugMessage("skip underestimate for 1-convex indefinite constraint <%s> since <%s> or <%s> is unbounded\n", SCIPconsGetName(cons), SCIPvarGetName(x), SCIPvarGetName(y));
4270  return SCIP_OKAY;
4271  }
4272 
4273  success = FALSE;
4274  cutcoeff[0] = SCIP_INVALID;
4275  cutcoeff[1] = SCIP_INVALID;
4276  cutcoeff[2] = SCIP_INVALID;
4277  cutcoeff[3] = SCIP_INVALID;
4278 
4279  /* (xval,yval) lie on a boundary */
4280  if( SCIPisFeasEQ(scip,xyref[0],xlb) || SCIPisFeasEQ(scip,xyref[0],xub) || SCIPisFeasEQ(scip,xyref[1],ylb) || SCIPisFeasEQ(scip,xyref[1],yub) )
4281  {
4282  SCIP_CALL( generate1ConvexIndefiniteUnderestimatorAtBoundary(scip, exprinterpreter, f, xyref, cutcoeff, &convenvvalue, &success) );
4283 
4284  if( !success )
4285  {
4286  /* maybe f is not differentiable on boundary, so move reference point into interior
4287  * we do this here w.r.t. both coordinates
4288  */
4289  perturb(&xyref[0], xlb, xub, 0.001);
4290  perturb(&xyref[1], ylb, yub, 0.001);
4291  }
4292  }
4293 
4294  if( !success )
4295  {
4296  /* xyref lies in the interior */
4297  /* check the pattern of the concave directions */
4298  SCIP_CALL( SCIPexprintHessianDense(exprinterpreter, f, xy_mid, TRUE, &fval_mid, hess) );
4299  assert(SCIPisFinite(hess[1]));
4300 
4301  if( hess[1] > 0.0 )
4302  {
4303  /* Pattern A: (R>=0 x R<=0) union (R<=0 x R>=0)*/
4304  SCIPdebugMessage("Pattern A\n");
4305  SCIP_CALL( generate1ConvexIndefiniteUnderestimatorInTheInteriorPatternA(scip, exprinterpreter, f, xyref, cutcoeff, &convenvvalue, &success) );
4306  }
4307  else
4308  {
4309  /* Pattern B: (R>=0 x R>=0) union (R<=0 x R <=0)*/
4310  SCIPdebugMessage("Pattern B\n");
4311  SCIP_CALL( generate1ConvexIndefiniteUnderestimatorInTheInteriorPatternB(scip, exprinterpreter, f, xyref, cutcoeff, &convenvvalue, &success) );
4312  }
4313  }
4314 
4315  if( !success )
4316  {
4317  /* bad luck */
4318  *row = NULL;
4319  return SCIP_OKAY;
4320  }
4321 
4322 
4323  /* construct row from cut coefficients (alpha, beta, gamma, delta)
4324  * coefficients are such that alpha * x + beta * y - gamma * f(x,y) <= delta,
4325  * i.e., alpha/gamma * x + beta/gamma * y - delta/gamma <= f(x,y)
4326  * -> alpha/gamma * x + beta/gamma * y - delta/gamma + c*z <= f(x,y) + c*z <= rhs
4327  */
4328 
4329  assert(cutcoeff[0] != SCIP_INVALID); /*lint !e777*/
4330  assert(cutcoeff[1] != SCIP_INVALID); /*lint !e777*/
4331  assert(cutcoeff[2] != SCIP_INVALID); /*lint !e777*/
4332  assert(cutcoeff[3] != SCIP_INVALID); /*lint !e777*/
4333  assert(SCIPisFinite(cutcoeff[0]));
4334  assert(SCIPisFinite(cutcoeff[1]));
4335  assert(SCIPisFinite(cutcoeff[2]));
4336  assert(SCIPisFinite(cutcoeff[3]));
4337  assert(SCIPisPositive(scip, cutcoeff[2])); /* assert gamma > 0 */
4338 
4339  if( SCIPisInfinity(scip, REALABS(cutcoeff[0]/cutcoeff[2])) ||
4340  SCIPisInfinity( scip, REALABS(cutcoeff[1]/cutcoeff[2])) ||
4341  SCIPisInfinity( scip, REALABS(cutcoeff[3]/cutcoeff[2])) )
4342  {
4343  *row = NULL;
4344  return SCIP_OKAY;
4345  }
4346 
4347  rhs = consdata->rhs + cutcoeff[3]/cutcoeff[2];
4348  SCIP_CALL( SCIPcreateEmptyRowCons(scip, row, SCIPconsGetHdlr(cons), "1ConvexUnderest", -SCIPinfinity(scip), rhs,
4349  TRUE, FALSE /* modifiable */, TRUE /* removable */) );
4350  SCIP_CALL( SCIPaddVarToRow(scip, *row, SCIPexprtreeGetVars(consdata->f)[0], cutcoeff[0] / cutcoeff[2]) );
4351  SCIP_CALL( SCIPaddVarToRow(scip, *row, SCIPexprtreeGetVars(consdata->f)[1], cutcoeff[1] / cutcoeff[2]) );
4352  if( consdata->z != NULL )
4353  {
4354  SCIP_CALL( SCIPaddVarToRow(scip, *row, consdata->z, consdata->zcoef) );
4355  }
4356 
4357  return SCIP_OKAY;
4358 }
4359 
4360 /** generates a cut */
4361 static
4363  SCIP* scip, /**< SCIP data structure */
4364  SCIP_EXPRINT* exprinterpreter, /**< expressions interpreter */
4365  SCIP_CONS* cons, /**< constraint */
4366  SCIP_SOL* sol, /**< solution to separate, or NULL if LP solution should be used */
4367  SCIP_SIDETYPE violside, /**< for which side of constraint we want to generate a cut */
4368  SCIP_Real cutmaxrange, /**< bound on cut coef range */
4369  SCIP_ROW** row /**< storage for cut */
4370  )
4371 {
4372  SCIP_CONSDATA* consdata;
4373  SCIP_VAR* x;
4374  SCIP_VAR* y;
4375  SCIP_Real x0y0[2];
4376 
4377  assert(scip != NULL);
4378  assert(cons != NULL);
4379  assert(row != NULL);
4380 
4381  consdata = SCIPconsGetData(cons);
4382  assert(consdata != NULL);
4383 
4384  *row = NULL;
4385 
4386  x = SCIPexprtreeGetVars(consdata->f)[0];
4387  y = SCIPexprtreeGetVars(consdata->f)[1];
4388 
4389  x0y0[0] = SCIPgetSolVal(scip, sol, x);
4390  x0y0[1] = SCIPgetSolVal(scip, sol, y);
4391 
4392  assert(SCIPisFeasLE(scip, SCIPvarGetLbLocal(x), x0y0[0]));
4393  assert(SCIPisFeasGE(scip, SCIPvarGetUbLocal(x), x0y0[0]));
4394  assert(SCIPisFeasLE(scip, SCIPvarGetLbLocal(y), x0y0[1]));
4395  assert(SCIPisFeasGE(scip, SCIPvarGetUbLocal(y), x0y0[1]));
4396 
4397  /* project into box */
4398  x0y0[0] = MIN(MAX(SCIPvarGetLbLocal(x),x0y0[0]),SCIPvarGetUbLocal(x)); /*lint !e666*/
4399  x0y0[1] = MIN(MAX(SCIPvarGetLbLocal(y),x0y0[1]),SCIPvarGetUbLocal(y)); /*lint !e666*/
4400 
4401  SCIPdebugPrintf("\n");
4402  SCIPdebugMessage("generate cut for constraint <%s> with %s hand side violated by %g\n", SCIPconsGetName(cons), violside == SCIP_SIDETYPE_LEFT ? "left" : "right", violside == SCIP_SIDETYPE_LEFT ? consdata->lhsviol : consdata->rhsviol);
4403  SCIPdebugMessage("convextype = %d\n",consdata->convextype);
4404  SCIPdebugMessage("%s = %g with bounds [%g, %g], %s = %g with bounds [%g, %g]",
4407  if( consdata->z != NULL )
4408  SCIPdebugPrintf(", %s = %g with bounds [%g, %g]", SCIPvarGetName(consdata->z), SCIPgetSolVal(scip, sol, consdata->z), SCIPvarGetLbLocal(consdata->z), SCIPvarGetUbLocal(consdata->z));
4409  SCIPdebugPrintf("\n");
4410  SCIPdebugPrintCons(scip, cons, NULL);
4411  SCIPdebugPrintf("\n");
4412 
4413  switch( consdata->convextype )
4414  {
4415  case SCIP_BIVAR_ALLCONVEX:
4416  {
4417  if( violside == SCIP_SIDETYPE_RIGHT )
4418  {
4419  /* rhs is violated */
4420  SCIP_CALL( generateLinearizationCut(scip, exprinterpreter, cons, x0y0, FALSE, row) );
4421  }
4422  else
4423  {
4424  /* lhs is violated */
4425  SCIP_CALL( generateOverestimatingHyperplaneCut(scip, exprinterpreter, cons, x0y0, row) );
4426  }
4427 
4428  break;
4429  }
4430 
4432  {
4433  SCIP_CALL( generateConvexConcaveEstimator(scip, exprinterpreter, cons, x0y0, violside, row) );
4434  break;
4435  }
4436 
4438  {
4439  if( violside == SCIP_SIDETYPE_RIGHT )
4440  {
4441  /* rhs is violated */
4442  SCIP_CALL( generate1ConvexIndefiniteUnderestimator(scip, exprinterpreter, cons, x0y0, row) );
4443  }
4444  else
4445  {
4446  /* lhs is violated */
4447  SCIP_CALL( generateOverestimatingHyperplaneCut(scip, exprinterpreter, cons, x0y0, row) );
4448  }
4449  break;
4450  }
4451  default:
4452  {
4453  SCIPdebugMessage("cut generation for convexity type not implemented\n");
4454  }
4455  } /*lint !e788*/
4456 
4457  if( *row == NULL )
4458  return SCIP_OKAY;
4459 
4460  SCIPdebug( SCIP_CALL( SCIPprintRow(scip, *row, NULL) ) );
4461 
4462  /* check numerics */
4463  {
4464  SCIP_Real mincoef;
4465  SCIP_Real maxcoef;
4466 
4467  mincoef = SCIPgetRowMinCoef(scip, *row);
4468  maxcoef = SCIPgetRowMaxCoef(scip, *row);
4469 
4470  while( maxcoef / mincoef > cutmaxrange )
4471  {
4472  SCIP_VAR* var;
4473  SCIP_Real coef;
4474  SCIP_Real constant;
4475  int j;
4476 
4477  /* if range of coefficients is bad, find very small coefficients and make them zero */
4478  SCIPdebugMessage("cut coefficients for constraint <%s> have very large range: mincoef = %g maxcoef = %g\n", SCIPconsGetName(cons), mincoef, maxcoef);
4479 
4480  /* if minimal coefficient is given by z, then give up (probably the maximal coefficient is the problem) */
4481  if( mincoef == consdata->zcoef ) /*lint !e777*/
4482  {
4483  SCIPdebugMessage("could not eliminate small coefficient, since it comes from linear part\n");
4484  break;
4485  }
4486 
4487  constant = 0.0;
4488  for( j = 0; j < SCIProwGetNNonz(*row); ++j )
4489  {
4490  coef = SCIProwGetVals(*row)[j];
4491  if( !SCIPisEQ(scip, REALABS(coef), mincoef) )
4492  continue;
4493 
4494  var = SCIPcolGetVar(SCIProwGetCols(*row)[j]);
4495  assert(var != NULL);
4496 
4497  /* try to eliminate coefficient with minimal absolute value by weakening cut and try again */
4498  if( ((coef > 0.0 && violside == SCIP_SIDETYPE_RIGHT) || (coef < 0.0 && violside == SCIP_SIDETYPE_LEFT)) && !SCIPisInfinity(scip, -SCIPvarGetLbLocal(var)) )
4499  {
4500  SCIPdebugMessage("eliminate coefficient %g for <%s> = %g [%g, %g]\n", coef, SCIPvarGetName(var), SCIPgetSolVal(scip, sol, var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var));
4501 
4502  constant += coef * (SCIProwIsLocal(*row) ? SCIPvarGetLbLocal(var) : SCIPvarGetLbGlobal(var));
4503  SCIP_CALL( SCIPaddVarToRow(scip, *row, var, -coef) );
4504  continue;
4505  }
4506 
4507  if( ((coef < 0.0 && violside == SCIP_SIDETYPE_RIGHT) || (coef > 0.0 && violside == SCIP_SIDETYPE_LEFT)) && !SCIPisInfinity(scip, SCIPvarGetUbLocal(var)) )
4508  {
4509  SCIPdebugMessage("eliminate coefficient %g for <%s> = %g [%g, %g]\n", coef, SCIPvarGetName(var), SCIPgetSolVal(scip, sol, var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var));
4510 
4511  constant += coef * (SCIProwIsLocal(*row) ? SCIPvarGetUbLocal(var) : SCIPvarGetUbGlobal(var));
4512  SCIP_CALL( SCIPaddVarToRow(scip, *row, var, -coef) );
4513  continue;
4514  }
4515 
4516  break;
4517  }
4518 
4519  if( j < SCIProwGetNNonz(*row) )
4520  {
4521  SCIPdebugMessage("could not eliminate small coefficient\n");
4522  SCIP_CALL( SCIPreleaseRow(scip, row) );
4523  break;
4524  }
4525 
4526  if( violside == SCIP_SIDETYPE_LEFT )
4527  {
4528  SCIP_CALL( SCIPchgRowLhs(scip, *row, SCIProwGetLhs(*row) - constant) );
4529  }
4530  else
4531  {
4532  SCIP_CALL( SCIPchgRowRhs(scip, *row, SCIProwGetRhs(*row) - constant) );
4533  }
4534 
4535  /* update min/max coefficient */
4536  mincoef = SCIPgetRowMinCoef(scip, *row);
4537  maxcoef = SCIPgetRowMaxCoef(scip, *row);
4538  };
4539 
4540  /* avoid numerically very bad cuts */
4541  if( maxcoef / mincoef > cutmaxrange )
4542  {
4543  SCIPdebugMessage("drop row for constraint <%s> because range of coefficients is too large: mincoef = %g, maxcoef = %g -> range = %g\n",
4544  SCIPconsGetName(cons), mincoef, maxcoef, maxcoef / mincoef);
4545  }
4546 
4547  if( *row != NULL &&
4548  ( (violside == SCIP_SIDETYPE_LEFT && SCIPisInfinity(scip, -SCIProwGetLhs(*row))) ||
4549  (violside == SCIP_SIDETYPE_RIGHT && SCIPisInfinity(scip, SCIProwGetRhs(*row)))) )
4550  {
4551  SCIPdebugMessage("drop row for constraint <%s> because of very large side: %g\n", SCIPconsGetName(cons), violside == SCIP_SIDETYPE_LEFT ? -SCIProwGetLhs(*row) : SCIProwGetRhs(*row));
4552  SCIP_CALL( SCIPreleaseRow(scip, row) );
4553  }
4554  }
4555 
4556  return SCIP_OKAY;
4557 }
4558 
4559 /** returns whether one side of a constraint function is convex w.r.t. local bounds
4560  * i.e., if side == RIGHT, then returns whether constraint function is convex w.r.t. local bounds
4561  * and if side == LEFT, then returns whether constraint function is concave w.r.t. local bounds
4562  */
4563 static
4565  SCIP* scip, /**< SCIP data structure */
4566  SCIP_CONS* cons, /**< constraint */
4567  SCIP_SIDETYPE side /**< constraint side to consider */
4568  )
4569 {
4570  SCIP_CONSDATA* consdata;
4571  SCIP_VAR** xy;
4572 
4573  consdata = SCIPconsGetData(cons);
4574  assert(consdata != NULL);
4575  assert(consdata->f != NULL);
4576 
4577  switch( consdata->convextype )
4578  {
4579  case SCIP_BIVAR_ALLCONVEX:
4580  /* always convex w.r.t. right hand side and concave w.r.t. left hand side */
4581  return side == SCIP_SIDETYPE_RIGHT;
4582 
4584  {
4585  /* always not convex w.r.t. left hand side */
4586  if( side == SCIP_SIDETYPE_LEFT )
4587  return FALSE;
4588 
4589  xy = SCIPexprtreeGetVars(consdata->f);
4590  assert(xy != NULL);
4591 
4592  /* convex w.r.t. right hand side if one of the variables is fixed */
4593  return SCIPisEQ(scip, SCIPvarGetLbLocal(xy[0]), SCIPvarGetUbLocal(xy[0])) ||
4594  SCIPisEQ(scip, SCIPvarGetLbLocal(xy[1]), SCIPvarGetUbLocal(xy[1]));
4595  }
4596 
4598  {
4599  xy = SCIPexprtreeGetVars(consdata->f);
4600  assert(xy != NULL);
4601 
4602  /* convex w.r.t. right hand side if y is fixed and
4603  * convex w.r.t. left hand side if x is fixed */
4604  return (side == SCIP_SIDETYPE_RIGHT && SCIPisEQ(scip, SCIPvarGetLbLocal(xy[1]), SCIPvarGetUbLocal(xy[1]))) ||
4605  (side == SCIP_SIDETYPE_LEFT && SCIPisEQ(scip, SCIPvarGetLbLocal(xy[0]), SCIPvarGetUbLocal(xy[0])));
4606  }
4607 
4608  default:
4609  return FALSE;
4610  } /*lint !e788*/
4611 }
4612 
4613 #ifdef SCIP_DEBUG
4614 static
4615 void printEstimator(
4616  SCIP* scip, /**< SCIP data structure */
4617  SCIP_SOL* sol, /**< solution to separate, or NULL if LP solution should be used */
4618  SCIP_CONS* cons, /**< constraint */
4619  SCIP_SIDETYPE side, /**< violated side of constraint */
4620  SCIP_ROW* row /**< row */
4621  )
4622 {
4623  SCIP_CONSDATA* consdata;
4624  const char* varnames[2] = {"x", "y"};
4625  SCIP_VAR* x;
4626  SCIP_VAR* y;
4627  int i;
4628 
4629  assert(scip != NULL);
4630  assert(cons != NULL);
4631  assert(row != NULL);
4632 
4633  consdata = SCIPconsGetData(cons);
4634  assert(consdata != NULL);
4635  x = SCIPexprtreeGetVars(consdata->f)[0];
4636  y = SCIPexprtreeGetVars(consdata->f)[1];
4637 
4638  SCIPinfoMessage(scip, NULL, "splot [%g:%g] [%g:%g] ", SCIPvarGetLbLocal(x), SCIPvarGetUbLocal(x), SCIPvarGetLbLocal(y), SCIPvarGetUbLocal(y));
4639  SCIPexprtreePrint(consdata->f, SCIPgetMessagehdlr(scip), NULL, varnames, NULL);
4640  SCIPinfoMessage(scip, NULL, "%+g", side == SCIP_SIDETYPE_LEFT ? consdata->lhs : consdata->rhs);
4641 
4642  SCIPinfoMessage(scip, NULL, ", %g", SCIPisInfinity(scip, SCIProwGetRhs(row)) ? -SCIProwGetLhs(row) : -SCIProwGetRhs(row));
4643  for( i = 0; i < SCIProwGetNNonz(row); ++i )
4644  {
4645  SCIP_VAR* var;
4646 
4647  var = SCIPcolGetVar(SCIProwGetCols(row)[i]);
4648  if( var != x && var != y )
4649  continue;
4650 
4651  SCIPinfoMessage(scip, NULL, "%+g * %s", SCIProwGetVals(row)[i], var == x ? "x" : "y");
4652  }
4653 
4654  SCIPinfoMessage(scip, NULL, ", \"< echo '%g %g %g'\" with circles", SCIPgetSolVal(scip, sol, x), SCIPgetSolVal(scip, sol, y), consdata->activity);
4655 
4656  SCIPinfoMessage(scip, NULL, "\n");
4657 }
4658 #endif
4659 
4660 /** tries to separate solution or LP solution by a linear cut
4661  *
4662  * assumes that constraint violations have been computed
4663  */
4664 static
4666  SCIP* scip, /**< SCIP data structure */
4667  SCIP_CONSHDLR* conshdlr, /**< quadratic constraints handler */
4668  SCIP_CONS** conss, /**< constraints */
4669  int nconss, /**< number of constraints */
4670  int nusefulconss, /**< number of constraints that seem to be useful */
4671  SCIP_SOL* sol, /**< solution to separate, or NULL if LP solution should be used */
4672  SCIP_Real minefficacy, /**< minimal efficacy of a cut if it should be added to the LP */
4673  SCIP_Bool inenforcement, /**< whether we are in constraint enforcement */
4674  SCIP_RESULT* result, /**< result of separation */
4675  SCIP_Real* bestefficacy /**< buffer to store best efficacy of a cut that was added to the LP, if found; or NULL if not of interest */
4676  )
4677 {
4678  SCIP_CONSHDLRDATA* conshdlrdata;
4679  SCIP_CONSDATA* consdata;
4680  SCIP_SIDETYPE violside;
4681  SCIP_Real feasibility;
4682  SCIP_Real efficacy;
4683  SCIP_Real norm;
4684  int c;
4685  SCIP_ROW* row;
4686 
4687  assert(scip != NULL);
4688  assert(conshdlr != NULL);
4689  assert(conss != NULL || nconss == 0);
4690  assert(nusefulconss <= nconss);
4691  assert(result != NULL);
4692 
4693  *result = SCIP_FEASIBLE;
4694 
4695  if( bestefficacy != NULL )
4696  *bestefficacy = 0.0;
4697 
4698  conshdlrdata = SCIPconshdlrGetData(conshdlr);
4699  assert(conshdlrdata != NULL);
4700 
4701  for( c = 0; c < nconss; ++c )
4702  {
4703  assert(conss != NULL);
4704  consdata = SCIPconsGetData(conss[c]);
4705  assert(consdata != NULL);
4706 
4707  if( SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) || SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
4708  {
4709  /* we are not feasible anymore */
4710  if( *result == SCIP_FEASIBLE )
4711  *result = SCIP_DIDNOTFIND;
4712 
4713  violside = SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) ? SCIP_SIDETYPE_LEFT : SCIP_SIDETYPE_RIGHT;
4714 
4715  /* generate cut */
4716  SCIP_CALL( generateCut(scip, conshdlrdata->exprinterpreter, conss[c], sol, violside, conshdlrdata->cutmaxrange, &row) );
4717  if( row == NULL ) /* failed to generate cut */
4718  continue;
4719 
4720  if( sol == NULL )
4721  feasibility = SCIPgetRowLPFeasibility(scip, row);
4722  else
4723  feasibility = SCIPgetRowSolFeasibility(scip, row, sol);
4724 
4725  switch( conshdlrdata->scaling )
4726  {
4727  case 'o' :
4728  efficacy = -feasibility;
4729  break;
4730 
4731  case 'g' :
4732  /* in difference to SCIPgetCutEfficacy, we scale by norm only if the norm is > 1.0 this avoid finding cuts
4733  * efficient which are only very slightly violated CPLEX does not seem to scale row coefficients up too
4734  * also we use infinity norm, since that seem to be the usual scaling strategy in LP solvers (equilibrium
4735  * scaling) */
4736  norm = SCIPgetRowMaxCoef(scip, row);
4737  efficacy = -feasibility / MAX(1.0, norm);
4738  break;
4739 
4740  case 's' :
4741  {
4742  SCIP_Real abslhs = REALABS(SCIProwGetLhs(row));
4743  SCIP_Real absrhs = REALABS(SCIProwGetRhs(row));
4744  SCIP_Real minval = MIN(abslhs, absrhs);
4745 
4746  efficacy = -feasibility / MAX(1.0, minval);
4747  break;
4748  }
4749 
4750  default:
4751  SCIPerrorMessage("Unknown scaling method '%c'.", conshdlrdata->scaling);
4752  SCIPABORT();
4753  return SCIP_INVALIDDATA; /*lint !e527*/
4754  }
4755 
4756  SCIPdebug( printEstimator(scip, sol, conss[c], violside, row) );
4757 
4758  /* if cut is strong enough or it's weak but we separate on a convex function and accept weak cuts there, add cut to SCIP */
4759  if( (SCIPisGT(scip, efficacy, minefficacy) ||
4760  (inenforcement && SCIPisGT(scip, efficacy, SCIPgetRelaxFeastolFactor(scip) > 0.0 ? SCIPepsilon(scip) : SCIPfeastol(scip)) && isConvexLocal(scip, conss[c], violside))) &&
4761  SCIPisCutApplicable(scip, row) )
4762  {
4763  SCIP_Bool infeasible;
4764 
4765  /* cut cuts off solution sufficiently */
4766  SCIP_CALL( SCIPaddCut(scip, sol, row, FALSE, &infeasible) );
4767  if( infeasible )
4768  {
4769  SCIPdebugMessage("cut for constraint <%s> is infeasible -> cutoff.\n", SCIPconsGetName(conss[c]));
4770  *result = SCIP_CUTOFF;
4771  }
4772  else
4773  {
4774  SCIPdebugMessage("added cut with efficacy %g for constraint <%s> violated by %g\n", efficacy, SCIPconsGetName(conss[c]), MAX(consdata->lhsviol, consdata->rhsviol));
4775  *result = SCIP_SEPARATED;
4776  }
4777  if( bestefficacy != NULL && efficacy > *bestefficacy )
4778  *bestefficacy = efficacy;
4779 
4780  /* mark row as not removable from LP for current node, if in enforcement */
4781  if( inenforcement && !conshdlrdata->enfocutsremovable )
4782  SCIPmarkRowNotRemovableLocal(scip, row);
4783  }
4784  else
4785  {
4786  SCIPdebugMessage("abandon cut since efficacy %g is too small\n", efficacy);
4787  }
4788 
4789  SCIP_CALL( SCIPreleaseRow(scip, &row) );
4790  }
4791 
4792  if( *result == SCIP_CUTOFF )
4793  break;
4794 
4795  /* enforce only useful constraints
4796  * others are only checked and enforced if we are still feasible or have not found a separating cut yet
4797  */
4798  if( c >= nusefulconss && *result == SCIP_FEASIBLE )
4799  break;
4800  }
4801 
4802  return SCIP_OKAY;
4803 }
4804 
4805 /** processes the event that a new primal solution has been found adds linearizations of all-convex constraints to the cutpool */
4806 static
4807 SCIP_DECL_EVENTEXEC(processNewSolutionEvent)
4808 {
4809  SCIP_CONSHDLR* conshdlr;
4810  SCIP_CONSHDLRDATA* conshdlrdata;
4811  SCIP_CONS** conss;
4812  int nconss;
4813  SCIP_CONSDATA* consdata;
4814  int c;
4815  SCIP_SOL* sol;
4816  SCIP_ROW* row;
4817  SCIP_Real x0y0[2];
4818 
4819  assert(scip != NULL);
4820  assert(event != NULL);
4821  assert(eventdata != NULL);
4822  assert(eventhdlr != NULL);
4823 
4824  assert((SCIPeventGetType(event) & SCIP_EVENTTYPE_SOLFOUND) != 0);
4825 
4826  conshdlr = (SCIP_CONSHDLR*)eventdata;
4827 
4828  nconss = SCIPconshdlrGetNConss(conshdlr);
4829 
4830  if( nconss == 0 )
4831  return SCIP_OKAY;
4832 
4833  conshdlrdata = SCIPconshdlrGetData(conshdlr);
4834  assert(conshdlrdata != NULL);
4835 
4836  sol = SCIPeventGetSol(event);
4837  assert(sol != NULL);
4838 
4839  /* we are only interested in solution coming from some heuristic other than trysol, but not from the tree
4840  * the reason for ignoring trysol solutions is that they may come from an NLP solve in sepalp, where we already added linearizations,
4841  * or are from the tree, but postprocessed via proposeFeasibleSolution
4842  */
4843  if( SCIPsolGetHeur(sol) == NULL || SCIPsolGetHeur(sol) == conshdlrdata->trysolheur )
4844  return SCIP_OKAY;
4845 
4846  conss = SCIPconshdlrGetConss(conshdlr);
4847  assert(conss != NULL);
4848 
4849  SCIPdebugMessage("catched new sol event %x from heur <%s>; have %d conss\n", SCIPeventGetType(event), SCIPheurGetName(SCIPsolGetHeur(sol)), nconss);
4850 
4851  row = NULL;
4852 
4853  for( c = 0; c < nconss; ++c )
4854  {
4855  if( SCIPconsIsLocal(conss[c]) )
4856  continue;
4857 
4858  consdata = SCIPconsGetData(conss[c]);
4859  assert(consdata != NULL);
4860 
4861  if( consdata->convextype == SCIP_BIVAR_ALLCONVEX && !SCIPisInfinity(scip, consdata->rhs) )
4862  {
4863  SCIP_CALL( SCIPgetSolVals(scip, sol, 2, SCIPexprtreeGetVars(consdata->f), x0y0) );
4864  SCIP_CALL( generateLinearizationCut(scip, conshdlrdata->exprinterpreter, conss[c], x0y0, TRUE, &row) );
4865  }
4866  else
4867  continue;
4868 
4869  if( row == NULL )
4870  continue;
4871 
4872  assert(!SCIProwIsLocal(row));
4873 
4874  SCIP_CALL( SCIPaddPoolCut(scip, row) );
4875  SCIP_CALL( SCIPreleaseRow(scip, &row) );
4876  }
4877 
4878  return SCIP_OKAY;
4879 }
4880 
4881 /** registers unfixed variables in nonlinear terms of violated constraints as external branching candidates
4882  * We score the variables by their gap between the convex envelope and the bivariate function in the current (x,y).
4883  * This value is given by the constraint violation, since we assume that cuts have been generated which support
4884  * the convex envelope in the LP.
4885  */
4886 static
4888  SCIP* scip, /**< SCIP data structure */
4889  SCIP_CONS** conss, /**< constraints to check */
4890  int nconss, /**< number of constraints to check */
4891  int* nnotify /**< counter for number of notifications performed */
4892  )
4893 {
4894  SCIP_CONSDATA* consdata;
4895  SCIP_VAR** xy;
4896  int c;
4897 
4898  assert(scip != NULL);
4899  assert(conss != NULL || nconss == 0);
4900 
4901  *nnotify = 0;
4902 
4903  for( c = 0; c < nconss; ++c )
4904  {
4905  assert(conss != NULL);
4906  consdata = SCIPconsGetData(conss[c]);
4907  assert(consdata != NULL);
4908  SCIPdebugMessage("cons <%s> violation: %g %g\n", SCIPconsGetName(conss[c]), consdata->lhsviol, consdata->rhsviol);
4909 
4910  xy = SCIPexprtreeGetVars(consdata->f);
4911  assert(xy != NULL);
4912 
4913  /* @todo prefer binary before continuous, prefer unbounded before bounded */
4914 
4915  switch( consdata->convextype )
4916  {
4918  {
4919  /* need to branch on the variable in which function is concave (or linear) */
4920  if( !SCIPisFeasZero(scip, consdata->lhsviol) )
4921  {
4922  /* regarding left hand side, we are concave in x and convex in y, so branch on x, if not fixed */
4923  if( !SCIPisEQ(scip, SCIPvarGetLbLocal(xy[0]), SCIPvarGetUbLocal(xy[0])) )
4924  {
4925  SCIPdebugMessage("register variable x = <%s>[%g,%g] in convex-concave <%s> with violation %g %g\n", SCIPvarGetName(xy[0]), SCIPvarGetLbLocal(xy[0]), SCIPvarGetUbLocal(xy[0]), SCIPconsGetName(conss[c]), consdata->lhsviol, consdata->rhsviol);
4926  SCIP_CALL( SCIPaddExternBranchCand(scip, xy[0], consdata->lhsviol, SCIP_INVALID) );
4927  ++*nnotify;
4928  }
4929  }
4930  if( !SCIPisFeasZero(scip, consdata->rhsviol) )
4931  {
4932  /* regarding right hand side, we are convex in x and concave in y, so branch on y, if not fixed */
4933  if( !SCIPisEQ(scip, SCIPvarGetLbLocal(xy[1]), SCIPvarGetUbLocal(xy[1])) )
4934  {
4935  SCIPdebugMessage("register variable y = <%s>[%g,%g] in convex-concave <%s> with violation %g %g\n", SCIPvarGetName(xy[1]), SCIPvarGetLbLocal(xy[1]), SCIPvarGetUbLocal(xy[1]), SCIPconsGetName(conss[c]), consdata->lhsviol, consdata->rhsviol);
4936  SCIP_CALL( SCIPaddExternBranchCand(scip, xy[1], consdata->lhsviol, SCIP_INVALID) );
4937  ++*nnotify;
4938  }
4939  }
4940  break;
4941  }
4942 
4944  {
4945  if( !SCIPisFeasZero(scip, consdata->rhsviol) )
4946  if( SCIPisEQ(scip, SCIPvarGetLbLocal(xy[0]), SCIPvarGetUbLocal(xy[0])) || SCIPisEQ(scip, SCIPvarGetLbLocal(xy[1]), SCIPvarGetUbLocal(xy[1])) )
4947  break;
4948 
4949  /* register both variables, if not fixed */
4950  if( !SCIPisEQ(scip, SCIPvarGetLbLocal(xy[0]), SCIPvarGetUbLocal(xy[0])) )
4951  {
4952  SCIPdebugMessage("register variable x = <%s>[%g,%g] in 1-convex <%s> with violation %g %g\n", SCIPvarGetName(xy[0]), SCIPvarGetLbLocal(xy[0]), SCIPvarGetUbLocal(xy[0]), SCIPconsGetName(conss[c]), consdata->lhsviol, consdata->rhsviol);
4953  SCIP_CALL( SCIPaddExternBranchCand(scip, xy[0], consdata->lhsviol, SCIP_INVALID) );
4954  ++*nnotify;
4955  }
4956 
4957  if( !SCIPisEQ(scip, SCIPvarGetLbLocal(xy[1]), SCIPvarGetUbLocal(xy[1])) )
4958  {
4959  SCIPdebugMessage("register variable y = <%s>[%g,%g] in 1-convex <%s> with violation %g %g\n", SCIPvarGetName(xy[1]), SCIPvarGetLbLocal(xy[1]), SCIPvarGetUbLocal(xy[1]), SCIPconsGetName(conss[c]), consdata->lhsviol, consdata->rhsviol);
4960  SCIP_CALL( SCIPaddExternBranchCand(scip, xy[1], consdata->lhsviol, SCIP_INVALID) );
4961  ++*nnotify;
4962  }
4963 
4964  break;
4965  }
4966 
4967  case SCIP_BIVAR_ALLCONVEX:
4968  {
4969  if( SCIPisFeasZero(scip, consdata->lhsviol) )
4970  continue;
4971  } /*lint -fallthrough*/
4972 
4973  default:
4974  {
4975  /* register both variables, if not fixed */
4976  if( !SCIPisEQ(scip, SCIPvarGetLbLocal(xy[0]), SCIPvarGetUbLocal(xy[0])) )
4977  {
4978  SCIPdebugMessage("register variable x = <%s>[%g,%g] in allconvex <%s> with violation %g %g\n", SCIPvarGetName(xy[0]), SCIPvarGetLbLocal(xy[0]), SCIPvarGetUbLocal(xy[0]), SCIPconsGetName(conss[c]), consdata->lhsviol, consdata->rhsviol);
4979  SCIP_CALL( SCIPaddExternBranchCand(scip, xy[0], consdata->lhsviol, SCIP_INVALID) );
4980  ++*nnotify;
4981  }
4982 
4983  if( !SCIPisEQ(scip, SCIPvarGetLbLocal(xy[1]), SCIPvarGetUbLocal(xy[1])) )
4984  {
4985  SCIPdebugMessage("register variable y = <%s>[%g,%g] in allconvex <%s> with violation %g %g\n", SCIPvarGetName(xy[1]), SCIPvarGetLbLocal(xy[1]), SCIPvarGetUbLocal(xy[1]), SCIPconsGetName(conss[c]), consdata->lhsviol, consdata->rhsviol);
4986  SCIP_CALL( SCIPaddExternBranchCand(scip, xy[1], consdata->lhsviol, SCIP_INVALID) );
4987  ++*nnotify;
4988  }
4989  }
4990  } /*lint !e788*/
4991  }
4992 
4993  return SCIP_OKAY;
4994 }
4995 
4996 /** registers a nonlinear variable from a violated constraint as branching candidate that has a large absolute value in the LP relaxation */
4997 static
4999  SCIP* scip, /**< SCIP data structure */
5000  SCIP_CONS** conss, /**< constraints */
5001  int nconss, /**< number of constraints */
5002  SCIP_VAR** brvar /**< buffer to store branching variable */
5003  )
5004 {
5005  SCIP_CONSDATA* consdata;
5006  SCIP_VAR* var;
5007  SCIP_Real val;
5008  SCIP_Real brvarval;
5009  int i;
5010  int c;
5011 
5012  assert(scip != NULL);
5013  assert(conss != NULL || nconss == 0);
5014 
5015  *brvar = NULL;
5016  brvarval = -1.0;
5017 
5018  for( c = 0; c < nconss; ++c )
5019  {
5020  assert(conss != NULL);
5021  consdata = SCIPconsGetData(conss[c]);
5022  assert(consdata != NULL);
5023  assert(consdata->f != NULL);
5024 
5025  if( !SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) && !SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
5026  continue;
5027 
5028  for( i = 0; i < 2; ++i )
5029  {
5030  var = SCIPexprtreeGetVars(consdata->f)[i];
5031  /* do not propose fixed variables */
5032  if( SCIPisEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) )
5033  continue;
5034  val = SCIPgetSolVal(scip, NULL, var);
5035  if( REALABS(val) > brvarval )
5036  {
5037  brvarval = REALABS(val);
5038  *brvar = var;
5039  }
5040  }
5041  }
5042 
5043  if( *brvar != NULL )
5044  {
5045  SCIP_CALL( SCIPaddExternBranchCand(scip, *brvar, brvarval, SCIP_INVALID) );
5046  }
5047 
5048  return SCIP_OKAY;
5049 }
5050 
5051 /** enforces violated bivariate constraints where both nonlinear variables can be assumed to be fixed
5052  * apply a bound change to the remaining linear variable, or recognizing infeasibility
5053  */
5054 static
5056  SCIP* scip, /**< SCIP data structure */
5057  SCIP_CONS** conss, /**< constraints */
5058  int nconss, /**< number of constraints */
5059  SCIP_Bool* reduceddom, /**< whether a domain has been reduced */
5060  SCIP_Bool* infeasible /**< whether we detected infeasibility */
5061  )
5062 {
5063  SCIP_CONSDATA* consdata;
5064  SCIP_INTERVAL nonlinact;
5065  SCIP_Real lhs;
5066  SCIP_Real rhs;
5067  int c;
5068 
5069  assert(scip != NULL);
5070  assert(conss != NULL || nconss == 0);
5071  assert(reduceddom != NULL);
5072  assert(infeasible != NULL);
5073 
5074  *reduceddom = FALSE;
5075  *infeasible = FALSE;
5076 
5077  for( c = 0; c < nconss; ++c )
5078  {
5079  assert(conss != NULL);
5080  consdata = SCIPconsGetData(conss[c]);
5081  assert(consdata != NULL);
5082 
5083  if( !SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) && !SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
5084  continue;
5085 
5086  /* get activity for f(x,y) */
5087  SCIP_CALL( SCIPevalExprtreeLocalBounds(scip, consdata->f, SCIPinfinity(scip), &nonlinact) );
5088  assert(!SCIPintervalIsEmpty(nonlinact));
5089 
5090  /* if all variables are fixed (at least up to epsilson), then the activity of the nonlinear part should be bounded */
5091  assert(!SCIPisInfinity(scip, -SCIPintervalGetInf(nonlinact)));
5092  assert(!SCIPisInfinity(scip, SCIPintervalGetSup(nonlinact)));
5093 
5094  if( !SCIPisInfinity(scip, -consdata->lhs) )
5095  lhs = consdata->lhs - SCIPintervalGetSup(nonlinact);
5096  else
5097  lhs = -SCIPinfinity(scip);
5098 
5099  if( !SCIPisInfinity(scip, consdata->rhs) )
5100  rhs = consdata->rhs - SCIPintervalGetInf(nonlinact);
5101  else
5102  rhs = SCIPinfinity(scip);
5103 
5104  if( consdata->z != NULL )
5105  {
5106  SCIP_Bool tightened;
5107  SCIP_Real coef;
5108 
5109  coef = consdata->zcoef;
5110  assert(!SCIPisZero(scip, coef));
5111 
5112  SCIPdebugMessage("Linear constraint with one variable: %g <= %g <%s> <= %g\n", lhs, coef, SCIPvarGetName(consdata->z), rhs);
5113 
5114  /* possibly correct lhs/rhs */
5115  if( coef >= 0.0 )
5116  {
5117  if( !SCIPisInfinity(scip, -lhs) )
5118  lhs /= coef;
5119  if( !SCIPisInfinity(scip, rhs) )
5120  rhs /= coef;
5121  }
5122  else
5123  {
5124  SCIP_Real h;
5125  h = rhs;
5126  if( !SCIPisInfinity(scip, -lhs) )
5127  rhs = lhs/coef;
5128  else
5129  rhs = SCIPinfinity(scip);
5130 
5131  if( !SCIPisInfinity(scip, h) )
5132  lhs = h/coef;
5133  else
5134  lhs = -SCIPinfinity(scip);
5135  }
5136  SCIPdebugMessage("Linear constraint is a bound: %g <= <%s> <= %g\n", lhs, SCIPvarGetName(consdata->z), rhs);
5137 
5138  if( !SCIPisInfinity(scip, -lhs) )
5139  {
5140  SCIP_CALL( SCIPtightenVarLb(scip, consdata->z, lhs, TRUE, infeasible, &tightened) );
5141  if( *infeasible )
5142  {
5143  SCIPdebugMessage("Lower bound leads to infeasibility.\n");
5144  return SCIP_OKAY;
5145  }
5146  if( tightened )
5147  {
5148  SCIPdebugMessage("Lower bound changed.\n");
5149  *reduceddom = TRUE;
5150  return SCIP_OKAY;
5151  }
5152  }
5153 
5154  if( !SCIPisInfinity(scip, rhs) )
5155  {
5156  SCIP_CALL( SCIPtightenVarUb(scip, consdata->z, rhs, TRUE, infeasible, &tightened) );
5157  if( *infeasible )
5158  {
5159  SCIPdebugMessage("Upper bound leads to infeasibility.\n");
5160  return SCIP_OKAY;
5161  }
5162  if( tightened )
5163  {
5164  SCIPdebugMessage("Upper bound changed.\n");
5165  *reduceddom = TRUE;
5166  return SCIP_OKAY;
5167  }
5168  }
5169  }
5170  else
5171  {
5172  /* no variable, thus check feasibility of lhs <= 0.0 <= rhs */
5173  *infeasible = SCIPisFeasGT(scip, lhs, 0.0) || SCIPisFeasLT(scip, rhs, 0.0);
5174  }
5175  }
5176 
5177  return SCIP_OKAY;
5178 }
5179 
5180 /** tightens bounds on a variable to given interval */
5181 static
5183  SCIP* scip, /**< SCIP data structure */
5184  SCIP_VAR* var, /**< variable which bounds to tighten */
5185  SCIP_INTERVAL bounds, /**< new bounds */
5186  SCIP_CONS* cons, /**< constraint that is propagated */
5187  SCIP_RESULT* result, /**< pointer where to update the result of the propagation call */
5188  int* nchgbds /**< buffer where to add the the number of changed bounds */
5189  )
5190 {
5191  SCIP_Bool infeas;
5192  SCIP_Bool tightened;
5193  SCIP_Real bnd;
5194 
5195  assert(scip != NULL);
5196  assert(var != NULL);
5197  assert(result != NULL);
5198  assert(*result == SCIP_DIDNOTFIND || *result == SCIP_REDUCEDDOM);
5199  assert(nchgbds != NULL);
5200 
5201  if( SCIPintervalIsPositiveInfinity(SCIPinfinity(scip), bounds) ||
5203  SCIPintervalIsEmpty(bounds) )
5204  {
5205  /* domain outside [-infty, +infty] or empty -> declare node infeasible */
5206  SCIPdebugMessage("found <%s> infeasible due to domain propagation for variable <%s>\n", cons != NULL ? SCIPconsGetName(cons) : "???", SCIPvarGetName(var)); /*lint !e585*/
5207  *result = SCIP_CUTOFF;
5208  return SCIP_OKAY;
5209  }
5210 
5212  {
5213  bnd = SCIPadjustedVarLb(scip, var, SCIPintervalGetInf(bounds));
5214  SCIP_CALL( SCIPtightenVarLb(scip, var, bnd, FALSE, &infeas, &tightened) );
5215  if( infeas )
5216  {
5217  SCIPdebugMessage("found <%s> infeasible due to domain propagation for variable <%s>\n", cons != NULL ? SCIPconsGetName(cons) : "???", SCIPvarGetName(var)); /*lint !e585*/
5218  *result = SCIP_CUTOFF;
5219  return SCIP_OKAY;
5220  }
5221  if( tightened )
5222  {
5223  SCIPdebugMessage("tightened lower bound of variable <%s> in constraint <%s> to %g\n", SCIPvarGetName(var), cons != NULL ? SCIPconsGetName(cons) : "???", SCIPvarGetLbLocal(var)); /*lint !e585*/
5224  ++*nchgbds;
5225  *result = SCIP_REDUCEDDOM;
5226  }
5227  }
5228 
5230  {
5231  bnd = SCIPadjustedVarLb(scip, var, SCIPintervalGetSup(bounds));
5232  SCIP_CALL( SCIPtightenVarUb(scip, var, bnd, FALSE, &infeas, &tightened) );
5233  if( infeas )
5234  {
5235  SCIPdebugMessage("found <%s> infeasible due to domain propagation for variable <%s>\n", cons != NULL ? SCIPconsGetName(cons) : "???", SCIPvarGetName(var)); /*lint !e585*/
5236  *result = SCIP_CUTOFF;
5237  return SCIP_OKAY;
5238  }
5239  if( tightened )
5240  {
5241  SCIPdebugMessage("tightened upper bound of variable <%s> in constraint <%s> to %g\n", SCIPvarGetName(var), cons != NULL ? SCIPconsGetName(cons) : "???", SCIPvarGetUbLocal(var)); /*lint !e585*/
5242  ++*nchgbds;
5243  *result = SCIP_REDUCEDDOM;
5244  }
5245  }
5246 
5247  return SCIP_OKAY;
5248 }
5249 
5250 /** tightens bounds of z in a single bivariate constraint
5251  * checks for redundancy and infeasibility
5252  */
5253 static
5255  SCIP* scip, /**< SCIP data structure */
5256  SCIP_CONSHDLR* conshdlr, /**< constraint handler */
5257  SCIP_CONS* cons, /**< constraint to process */
5258  SCIP_RESULT* result, /**< pointer to store the result of the propagation call */
5259  int* nchgbds, /**< buffer where to add the the number of changed bounds */
5260  SCIP_Bool* redundant /**< buffer where to store whether constraint has been found to be redundant */
5261  )
5262 {
5263  SCIP_CONSHDLRDATA* conshdlrdata;
5264  SCIP_CONSDATA* consdata;
5265  SCIP_INTERVAL consbounds; /* left and right side of constraint */
5266  SCIP_INTERVAL ftermactivity; /* activity of f(x,y) */
5267  SCIP_INTERVAL ztermactivity; /* activity of c*z */
5268  SCIP_INTERVAL consactivity; /* activity of f(x,y) + c*z */
5269  SCIP_INTERVAL tmp;
5270  SCIP_Bool cutoff;
5271 
5272  assert(scip != NULL);
5273  assert(cons != NULL);
5274  assert(result != NULL);
5275  assert(nchgbds != NULL);
5276 
5277  conshdlrdata = SCIPconshdlrGetData(conshdlr);
5278  assert(conshdlrdata != NULL);
5279  assert(conshdlrdata->exprgraph != NULL);
5280 
5281  consdata = SCIPconsGetData(cons);
5282  assert(consdata != NULL);
5283  assert(consdata->exprgraphnode != NULL);
5284 
5285  *result = SCIP_DIDNOTRUN;
5286  *redundant = FALSE;
5287 
5288  /* extend interval by epsilon to avoid cutoff in forward propagation if constraint is only almost feasible */
5289  SCIPintervalSetBounds(&consbounds,
5290  -infty2infty(SCIPinfinity(scip), INTERVALINFTY, -consdata->lhs+SCIPepsilon(scip)), /*lint !e666*/
5291  +infty2infty(SCIPinfinity(scip), INTERVALINFTY, consdata->rhs+SCIPepsilon(scip)) ); /*lint !e666*/
5292 
5293  /* get activity for f(x,y) */
5294  ftermactivity = SCIPexprgraphGetNodeBounds(consdata->exprgraphnode);
5295  assert(!SCIPintervalIsEmpty(ftermactivity) );
5296 
5297  /* get activity for c*z */
5298  if( consdata->z != NULL )
5299  {
5300  SCIPintervalSetBounds(&ztermactivity,
5301  -infty2infty(SCIPinfinity(scip), INTERVALINFTY, -MIN(SCIPvarGetLbLocal(consdata->z), SCIPvarGetUbLocal(consdata->z))), /*lint !e666*/
5302  +infty2infty(SCIPinfinity(scip), INTERVALINFTY, MAX(SCIPvarGetLbLocal(consdata->z), SCIPvarGetUbLocal(consdata->z)))); /*lint !e666*/
5303  SCIPintervalMulScalar(INTERVALINFTY, &ztermactivity, ztermactivity, consdata->zcoef);
5304  }
5305  else
5306  {
5307  SCIPintervalSet(&ztermactivity, 0.0);
5308  }
5309 
5310  /* get activity for f(x,y)+c*z */
5311  SCIPintervalAdd(INTERVALINFTY, &consactivity, ftermactivity, ztermactivity);
5312 
5313  /* check redundancy */
5314  if( SCIPintervalIsSubsetEQ(INTERVALINFTY, consactivity, consbounds) )
5315  {
5316  SCIPdebugMessage("found constraint <%s> to be redundant: sides: [%g, %g], activity: [%g, %g]\n",
5317  SCIPconsGetName(cons), consdata->lhs, consdata->rhs, SCIPintervalGetInf(consactivity), SCIPintervalGetSup(consactivity));
5318  *redundant = TRUE;
5319  return SCIP_OKAY;
5320  }
5321 
5322  /* check infeasibility */
5323  if( SCIPintervalAreDisjoint(consbounds, consactivity) )
5324  {
5325  SCIPdebugMessage("found constraint <%s> to be infeasible; sides: [%g, %g], activity: [%g, %g], infeas: %g\n",
5326  SCIPconsGetName(cons), consdata->lhs, consdata->rhs, SCIPintervalGetInf(consactivity), SCIPintervalGetSup(consactivity),
5327  MAX(consdata->lhs - SCIPintervalGetSup(consactivity), SCIPintervalGetInf(consactivity) - consdata->rhs)); /*lint !e666*/
5328  *result = SCIP_CUTOFF;
5329  return SCIP_OKAY;
5330  }
5331 
5332  /* try to tighten bounds on z */
5333  if( consdata->z != NULL )
5334  {
5335  *result = SCIP_DIDNOTFIND;
5336 
5337  /* compute ([lhs, rhs] - f([xlb,xub], [ylb,yub])) / zcoef */
5338  SCIPintervalSub(INTERVALINFTY, &tmp, consbounds, ftermactivity);
5339  SCIPintervalDivScalar(INTERVALINFTY, &tmp, tmp, consdata->zcoef);
5340 
5341  SCIP_CALL( propagateBoundsTightenVar(scip, consdata->z, tmp, cons, result, nchgbds) );
5342 
5343  if( *result == SCIP_CUTOFF )
5344  return SCIP_OKAY;
5345 
5346  if( *result == SCIP_SUCCESS )
5347  {
5348  SCIPintervalSetBounds(&ztermactivity,
5349  -infty2infty(SCIPinfinity(scip), INTERVALINFTY, -MIN(SCIPvarGetLbLocal(consdata->z), SCIPvarGetUbLocal(consdata->z))), /*lint !e666*/
5350  +infty2infty(SCIPinfinity(scip), INTERVALINFTY, MAX(SCIPvarGetLbLocal(consdata->z), SCIPvarGetUbLocal(consdata->z)))); /*lint !e666*/
5351  SCIPintervalMulScalar(INTERVALINFTY, &ztermactivity, ztermactivity, consdata->zcoef);
5352  }
5353  }
5354 
5355  /* set bounds for exprgraphnode = [lhs,rhs] - c*z */
5356  SCIPintervalSub(INTERVALINFTY, &tmp, consbounds, ztermactivity);
5357  SCIPexprgraphTightenNodeBounds(conshdlrdata->exprgraph, consdata->exprgraphnode, tmp, 0.05, &cutoff);
5358  if( cutoff )
5359  {
5360  SCIPdebugMessage("found constraint <%s> infeasible%s\n", SCIPconsGetName(cons), SCIPinProbing(scip) ? " in probing" : "");
5361  *result = SCIP_CUTOFF;
5362  return SCIP_OKAY;
5363  }
5364 
5365  return SCIP_OKAY;
5366 }
5367 
5368 /** calls domain propagation for a set of constraints */
5369 static
5371  SCIP* scip, /**< SCIP data structure */
5372  SCIP_CONSHDLR* conshdlr, /**< constraint handler */
5373  SCIP_CONS** conss, /**< constraints to process */
5374  int nconss, /**< number of constraints */
5375  SCIP_RESULT* result, /**< pointer to store the result of the propagation calls */
5376  int* nchgbds, /**< buffer where to add the the number of changed bounds */
5377  int* ndelconss /**< buffer where to increase if a constraint was deleted (locally) due to redundancy */
5378  )
5379 {
5380  SCIP_CONSHDLRDATA* conshdlrdata;
5381  SCIP_CONSDATA* consdata;
5382  SCIP_RESULT propresult;
5383  SCIP_Bool redundant;
5384  SCIP_Bool domainerror;
5385  int roundnr;
5386  SCIP_Bool success;
5387  int nvars;
5388  SCIP_VAR** vars;
5389  SCIP_EXPRGRAPHNODE** varnodes;
5390  SCIP_Bool cutoff;
5391  int c;
5392  int i;
5393 
5394  assert(scip != NULL);
5395  assert(conshdlr != NULL);
5396  assert(conss != NULL || nconss == 0);
5397  assert(result != NULL);
5398  assert(nchgbds != NULL);
5399  assert(ndelconss != NULL);
5400 
5401  conshdlrdata = SCIPconshdlrGetData(conshdlr);
5402  assert(conshdlrdata != NULL);
5403  assert(conshdlrdata->exprgraph != NULL);
5404 
5405  *result = SCIP_DIDNOTRUN;
5406 
5407  if( nconss == 0 )
5408  return SCIP_OKAY;
5409 
5410  if( conshdlrdata->ispropagated )
5411  {
5412  /* check whether there was also no tightening in the bounds of the linear variables
5413  * @todo put this in processLinearVarEvent
5414  */
5415  for( c = 0; c < nconss; ++c )
5416  {
5417  assert(conss[c] != NULL); /*lint !e613*/
5418 
5419  consdata = SCIPconsGetData(conss[c]); /*lint !e613*/
5420  assert(consdata != NULL);
5421  if( !consdata->ispropagated )
5422  break;
5423  }
5424  if( c == nconss )
5425  return SCIP_OKAY;
5426  }
5427 
5428  *result = SCIP_DIDNOTFIND;
5429 
5430  roundnr = 0;
5431  do
5432  {
5433  success = FALSE;
5434 
5435  SCIPdebugMessage("starting domain propagation round %d for %d constraints\n", roundnr, nconss);
5436 
5437  conshdlrdata->ispropagated = TRUE;
5438 
5439  /* propagate variable bounds through expression graph
5440  * roundnr == 0 clears remainings from a previous backward propagation
5441  * @todo could give FALSE if no linear variable in the constraints had been relaxed since last time
5442  */
5443  SCIP_CALL( SCIPexprgraphPropagateVarBounds(conshdlrdata->exprgraph, INTERVALINFTY, roundnr == 0, &domainerror) );
5444 
5445  if( domainerror )
5446  {
5447  SCIPdebugMessage("current bounds out of domain for some expression, do cutoff\n");
5448  *result = SCIP_CUTOFF;
5449  break;
5450  }
5451 
5452  /* check for redundancy and infeasibility of constraints
5453  * tighten bounds on linear variables
5454  * setup bounds for expression graph nodes */
5455  for( c = 0; c < nconss && *result != SCIP_CUTOFF; ++c )
5456  {
5457  assert(conss != NULL);
5458  if( !SCIPconsIsEnabled(conss[c]) || SCIPconsIsDeleted(conss[c]) )
5459  continue;
5460 
5461  consdata = SCIPconsGetData(conss[c]);
5462  assert(consdata != NULL);
5463 
5464  SCIP_CALL( propagateBoundsCons(scip, conshdlr, conss[c], &propresult, nchgbds, &redundant) );
5465  if( propresult != SCIP_DIDNOTFIND && propresult != SCIP_DIDNOTRUN )
5466  {
5467  *result = propresult;
5468  success = TRUE;
5469  }
5470  if( redundant )
5471  {
5472  SCIPdebugMessage("delete redundant constraint <%s> locally\n", SCIPconsGetName(conss[c]));
5473  SCIP_CALL( SCIPdelConsLocal(scip, conss[c]) );
5474  ++*ndelconss;
5475  }
5476 
5477  consdata->ispropagated = TRUE;
5478  }
5479  if( *result == SCIP_CUTOFF )
5480  break;
5481 
5482  /* propagate backward through expression graph */
5483  SCIPdebugMessage("start backward propagation in expression graph\n");
5484 
5485  /* compute bound tightenings for nonlinear variables */
5486  SCIPexprgraphPropagateNodeBounds(conshdlrdata->exprgraph, INTERVALINFTY, 0.05, &cutoff);
5487 
5488  if( cutoff )
5489  {
5490  SCIPdebugMessage("backward propagation found problem infeasible%s\n", SCIPinProbing(scip) ? " in probing" : "");
5491  *result = SCIP_CUTOFF;
5492  break;
5493  }
5494 
5495  /* put back new bounds into SCIP variables */
5496  nvars = SCIPexprgraphGetNVars(conshdlrdata->exprgraph);
5497  vars = (SCIP_VAR**)SCIPexprgraphGetVars(conshdlrdata->exprgraph);
5498  varnodes = SCIPexprgraphGetVarNodes(conshdlrdata->exprgraph);
5499  propresult = SCIP_DIDNOTFIND;
5500  for( i = 0; i < nvars && propresult != SCIP_CUTOFF; ++i )
5501  {
5502  SCIP_CALL( propagateBoundsTightenVar(scip, vars[i], SCIPexprgraphGetNodeBounds(varnodes[i]), NULL, &propresult, nchgbds) );
5503  }
5504  if( propresult != SCIP_DIDNOTFIND )
5505  {
5506  *result = propresult;
5507  success = TRUE;
5508  }
5509  }
5510  while( success && *result != SCIP_CUTOFF && ++roundnr < conshdlrdata->maxproprounds );
5511 
5512  return SCIP_OKAY;
5513 }
5514 
5515 
5516 /** Given a solution where every bivariate constraint is either feasible or can be made feasible by
5517  * moving the linear variable, construct the corresponding feasible solution and pass it to the trysol heuristic.
5518  * The method assumes that this is always possible and that not all constraints are feasible already.
5519  */
5520 static
5522  SCIP* scip, /**< SCIP data structure */
5523  SCIP_CONSHDLR* conshdlr, /**< constraint handler */
5524  SCIP_CONS** conss, /**< constraints to process */
5525  int nconss, /**< number of constraints */
5526  SCIP_SOL* sol, /**< solution to process */
5527  SCIP_Bool* success /**< buffer to store whether we succeeded to construct a solution that satisfies all provided constraints */
5528  )
5529 {
5530  SCIP_CONSHDLRDATA* conshdlrdata;
5531  SCIP_CONSDATA* consdata;
5532  SCIP_SOL* newsol;
5533  SCIP_VAR* var;
5534  int c;
5535  SCIP_Real viol;
5536  SCIP_Real delta;
5537  SCIP_Real gap;
5538 
5539  assert(scip != NULL);
5540  assert(conshdlr != NULL);
5541  assert(conss != NULL || nconss == 0);
5542  assert(success != NULL);
5543 
5544  conshdlrdata = SCIPconshdlrGetData(conshdlr);
5545  assert(conshdlrdata != NULL);
5546  assert(conshdlrdata->trysolheur != NULL);
5547 
5548  *success = FALSE;
5549 
5550  if( sol != NULL )
5551  {
5552  SCIP_CALL( SCIPcreateSolCopy(scip, &newsol, sol) );
5553  }
5554  else
5555  {
5556  SCIP_CALL( SCIPcreateLPSol(scip, &newsol, NULL) );
5557  }
5558  SCIP_CALL( SCIPunlinkSol(scip, newsol) );
5559 
5560  for( c = 0; c < nconss; ++c )
5561  {
5562  consdata = SCIPconsGetData(conss[c]); /*lint !e613*/
5563  assert(consdata != NULL);
5564 
5565  /* recompute violation of solution in case solution has changed
5566  * get absolution violation and sign */
5567  if( SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) )
5568  {
5569  SCIP_CALL( computeViolation(scip, conshdlr, conss[c], newsol) ); /*lint !e613*/
5570  viol = consdata->lhs - consdata->activity;
5571  }
5572  else if( SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
5573  {
5574  SCIP_CALL( computeViolation(scip, conshdlr, conss[c], newsol) ); /*lint !e613*/
5575  viol = consdata->rhs - consdata->activity;
5576  }
5577  else
5578  continue; /* constraint is satisfied */
5579 
5580  assert(viol != 0.0);
5581  if( consdata->mayincreasez &&
5582  ((viol > 0.0 && consdata->zcoef > 0.0) || (viol < 0.0 && consdata->zcoef < 0.0)) )
5583  {
5584  /* have variable where increasing makes the constraint less violated */
5585  var = consdata->z;
5586  /* compute how much we would like to increase var */
5587  delta = viol / consdata->zcoef;
5588  assert(delta > 0.0);
5589  /* if var has an upper bound, may need to reduce delta */
5590  if( !SCIPisInfinity(scip, SCIPvarGetUbGlobal(var)) )
5591  {
5592  gap = SCIPvarGetUbGlobal(var) - SCIPgetSolVal(scip, newsol, var);
5593  delta = MIN(MAX(0.0, gap), delta);
5594  }
5595  if( SCIPisPositive(scip, delta) )
5596  {
5597  /* if variable is integral, round delta up so that it will still have an integer value */
5598  if( SCIPvarIsIntegral(var) )
5599  delta = SCIPceil(scip, delta);
5600 
5601  SCIP_CALL( SCIPincSolVal(scip, newsol, var, delta) );
5602  SCIPdebugMessage("increase <%s> by %g to %g\n", SCIPvarGetName(var), delta, SCIPgetSolVal(scip, newsol, var));
5603 
5604  /* adjust constraint violation, if satisfied go on to next constraint */
5605  viol -= consdata->zcoef * delta;
5606  if( SCIPisZero(scip, viol) )
5607  continue;
5608  }
5609  }
5610 
5611  assert(viol != 0.0);
5612  if( consdata->maydecreasez &&
5613  ((viol > 0.0 && consdata->zcoef < 0.0) || (viol < 0.0 && consdata->zcoef > 0.0)) )
5614  {
5615  /* have variable where decreasing makes constraint less violated */
5616  var = consdata->z;
5617  /* compute how much we would like to decrease var */
5618  delta = viol / consdata->zcoef;
5619  assert(delta < 0.0);
5620  /* if var has a lower bound, may need to reduce delta */
5621  if( !SCIPisInfinity(scip, -SCIPvarGetLbGlobal(var)) )
5622  {
5623  gap = SCIPgetSolVal(scip, newsol, var) - SCIPvarGetLbGlobal(var);
5624  delta = MAX(MIN(0.0, gap), delta);
5625  }
5626  if( SCIPisNegative(scip, delta) )
5627  {
5628  /* if variable is integral, round delta down so that it will still have an integer value */
5629  if( SCIPvarIsIntegral(var) )
5630  delta = SCIPfloor(scip, delta);
5631  SCIP_CALL( SCIPincSolVal(scip, newsol, var, delta) );
5632  SCIPdebugMessage("increase <%s> by %g to %g\n", SCIPvarGetName(var), delta, SCIPgetSolVal(scip, newsol, var));
5633 
5634  /* adjust constraint violation, if satisfied go on to next constraint */
5635  viol -= consdata->zcoef * delta;
5636  if( SCIPisZero(scip, viol) )
5637  continue;
5638  }
5639  }
5640 
5641  /* still here... so maybe we could not make constraint feasible due to variable bounds
5642  * check if we are feasible w.r.t. (relative) feasibility tolerance */
5643  SCIP_CALL( computeViolation(scip, conshdlr, conss[c], newsol) ); /*lint !e613*/
5644  /* if still violated, we give up */
5645  if( SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) || SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
5646  break;
5647 
5648  /* if objective value is not better than current upper bound, we give up */
5649  if( !SCIPisInfinity(scip, SCIPgetUpperbound(scip)) && !SCIPisSumLT(scip, SCIPgetSolTransObj(scip, newsol), SCIPgetUpperbound(scip)) )
5650  break;
5651  }
5652 
5653  /* if we have a solution that should satisfy all nonlinear constraints and has a better objective than the current upper bound,
5654  * then pass it to the trysol heuristic */
5655  if( c == nconss )
5656  {
5657  SCIPdebugMessage("pass solution with objective value %g to trysol heuristic\n", SCIPgetSolTransObj(scip, newsol));
5658 
5659  SCIP_CALL( SCIPheurPassSolTrySol(scip, conshdlrdata->trysolheur, newsol) );
5660  *success = TRUE;
5661  }
5662 
5663  SCIP_CALL( SCIPfreeSol(scip, &newsol) );
5664 
5665  return SCIP_OKAY;
5666 }
5667 
5668 /** creates bivariate constraint from quadratic constraint data of the form
5669  * lhs <= xsqrcoef * x^2 + xlincoef * x + ysqrcoef * y^2 + ylincoef * y + bilincoef * x*y + zcoef * z <= rhs
5670  */
5671 static
5673  SCIP* scip, /**< SCIP data structure */
5674  SCIP_CONS* srccons, /**< source constraint to take attributes from */
5675  SCIP_CONS** cons, /**< pointer to store new constraint */
5676  const char* name, /**< name of new constraint */
5677  SCIP_VAR* x, /**< first nonlinear variable */
5678  SCIP_VAR* y, /**< second nonlinear variable */
5679  SCIP_VAR* z, /**< linear variable, can be NULL */
5680  SCIP_Real coefxx, /**< coefficient of x^2 */
5681  SCIP_Real coefx, /**< coefficient of x */
5682  SCIP_Real coefyy, /**< coefficient of y^2 */
5683  SCIP_Real coefy, /**< coefficient of y */
5684  SCIP_Real coefxy, /**< coefficient of x*y */
5685  SCIP_Real coefz, /**< coefficient of z */
5686  SCIP_Real lhs, /**< left-hand-side */
5687  SCIP_Real rhs /**< right-hand-side */
5688  )
5689 {
5690  SCIP_Real mult;
5691  SCIP_VAR* xy[2];
5692  SCIP_BIVAR_CONVEXITY convextype;
5693  SCIP_EXPR* e;
5694  SCIP_EXPRTREE* exprtree;
5695 
5696  SCIP_EXPR* children[2];
5697  SCIP_Real lincoefs[2];
5698  SCIP_QUADELEM quadelems[3];
5699  int nquadelems;
5700 
5701  assert(scip != NULL);
5702  assert(srccons != NULL);
5703  assert(cons != NULL);
5704  assert(name != NULL);
5705 
5706  assert(x != NULL);
5707  assert(y != NULL);
5708  assert(SCIPisLE(scip, lhs, rhs));
5709 
5710  if( coefxx >= 0 && coefyy >= 0 && 4 * coefxx * coefyy >= coefxy * coefxy )
5711  {
5712  /* quadratic term is convex in both variables (jointly) */
5713  mult = 1.0;
5714  convextype = SCIP_BIVAR_ALLCONVEX;
5715  }
5716  else if( coefxx <= 0 && coefyy <= 0 && 4 * coefxx * coefyy >= coefxy * coefxy )
5717  {
5718  /* quadratic term is concave in both variables (jointly) */
5719  mult = -1.0;
5720  convextype = SCIP_BIVAR_ALLCONVEX;
5721  }
5722  else if( coefxx > 0 && coefyy > 0 )
5723  {
5724  /* indefinite but 1-convex */
5725  assert(4 * coefxx * coefyy < coefxy * coefxy); /* assert indefiniteness */
5726  mult = 1.0;
5727  convextype = SCIP_BIVAR_1CONVEX_INDEFINITE;
5728  }
5729  else if( coefxx < 0 && coefyy < 0 )
5730  {
5731  /* indefinite but 1-convex */
5732  assert(4 * coefxx * coefyy < coefxy * coefxy); /* assert indefiniteness */
5733  mult = -1.0;
5734  convextype = SCIP_BIVAR_1CONVEX_INDEFINITE;
5735  }
5736  else
5737  {
5738  /* convex in one variable and concave in other variable */
5739  assert(coefxx * coefyy <= 0);
5740  convextype = SCIP_BIVAR_CONVEX_CONCAVE;
5741  if( coefxx != 0.0 )
5742  {
5743  /* if coefxx < 0 (and thus coefyy >= 0) f(x,y) is concave in x and convex in y
5744  * but we need convex in x and concave in y, thus we multiply by -1
5745  */
5746  if( coefxx < 0.0 )
5747  mult = -1.0;
5748  else
5749  mult = 1.0;
5750  }
5751  else if( coefyy != 0.0 )
5752  {
5753  /* coefxx == 0.0 */
5754  /* if coefyy < 0 (and coefxx == 0) f(x,y) is concave in y and convex in x
5755  * otherwise we convert to convex in y and concave in x by multiplying by -1
5756  */
5757  if( coefyy < 0.0 )
5758  mult = 1.0;
5759  else
5760  mult = -1.0;
5761  }
5762  else
5763  {
5764  /* coefxx == 0.0 && coefyy == 0.0 && coefxy != 0.0 */
5765  assert(coefxy != 0.0);
5766  mult = 1.0;
5767  }
5768  }
5769 
5770  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &children[0], SCIP_EXPR_VARIDX, 0) );
5771  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &children[1], SCIP_EXPR_VARIDX, 1) );
5772 
5773  lincoefs[0] = coefx * mult;
5774  lincoefs[1] = coefy * mult;
5775 
5776  nquadelems = 0;
5777  if( coefxx != 0.0 )
5778  {
5779  quadelems[nquadelems].idx1 = 0;
5780  quadelems[nquadelems].idx2 = 0;
5781  quadelems[nquadelems].coef = coefxx * mult;
5782  ++nquadelems;
5783  }
5784  if( coefyy != 0.0 )
5785  {
5786  quadelems[nquadelems].idx1 = 1;
5787  quadelems[nquadelems].idx2 = 1;
5788  quadelems[nquadelems].coef = coefyy * mult;
5789  ++nquadelems;
5790  }
5791  if( coefxy != 0.0 )
5792  {
5793  quadelems[nquadelems].idx1 = 0;
5794  quadelems[nquadelems].idx2 = 1;
5795  quadelems[nquadelems].coef = coefxy * mult;
5796  ++nquadelems;
5797  }
5798 
5799  SCIP_CALL( SCIPexprCreateQuadratic(SCIPblkmem(scip), &e, 2, children, 0.0, (coefx != 0.0 || coefy != 0.0) ? lincoefs : NULL, nquadelems, quadelems) ); /*lint !e826*/
5800  assert(e != NULL);
5801 
5802  xy[0] = x;
5803  xy[1] = y;
5804 
5805  SCIP_CALL( SCIPexprtreeCreate(SCIPblkmem(scip), &exprtree, e, 2, 0, NULL) );
5806  SCIP_CALL( SCIPexprtreeSetVars(exprtree, 2, xy) );
5807 
5808  if( mult == -1.0 )
5809  {
5810  SCIP_Real tmp;
5811  tmp = lhs;
5812  lhs = -rhs;
5813  rhs = -tmp;
5814  coefz = -coefz;
5815  }
5816  else
5817  {
5818  assert(mult == 1.0);
5819  }
5820 
5821  SCIPdebugMessage("upgrading constraint <%s> to bivariate constraint <%s> with convexity type %d\n", SCIPconsGetName(srccons), name, convextype);
5822 
5823  SCIP_CALL( SCIPcreateConsBivariate(scip, cons, name,
5824  exprtree, convextype, z, coefz, lhs, rhs,
5825  SCIPconsIsInitial(srccons), SCIPconsIsSeparated(srccons), SCIPconsIsEnforced(srccons),
5826  SCIPconsIsChecked(srccons), SCIPconsIsPropagated(srccons), SCIPconsIsLocal(srccons),
5827  SCIPconsIsModifiable(srccons), SCIPconsIsDynamic(srccons), SCIPconsIsRemovable(srccons),
5828  SCIPconsIsStickingAtNode(srccons)) );
5829  SCIPdebugPrintCons(scip, *cons, NULL);
5830 
5831  SCIP_CALL( SCIPexprtreeFree(&exprtree) );
5832 
5833  return SCIP_OKAY;
5834 }
5835 
5836 /** creates expression tree for monomial of the form coef * x^p * y^q with x >= 0 and y >= 0 and checks its convexity type */
5837 static
5839  SCIP* scip, /**< SCIP data structure */
5840  SCIP_VAR* x, /**< first variable */
5841  SCIP_VAR* y, /**< second variable */
5842  SCIP_Real coef, /**< monomial coefficient */
5843  SCIP_Real p, /**< exponent of x */
5844  SCIP_Real q, /**< exponent of y */
5845  SCIP_EXPRTREE** exprtree, /**< buffer to store pointer to expression tree */
5846  SCIP_Real* mult, /**< buffer to store multiplicator for generated expression tree */
5847  SCIP_BIVAR_CONVEXITY* convextype /**< buffer to store convexity type of expression tree */
5848  )
5849 {
5850  SCIP_Bool swapvars;
5851  SCIP_EXPR* children[2];
5852  int childidxs[2];
5853  SCIP_Real exponents[2];
5854  SCIP_VAR* vars[2];
5855  SCIP_EXPR* e;
5856  SCIP_EXPRDATA_MONOMIAL* monomial;
5857 
5858  assert(scip != NULL);
5859  assert(x != NULL);
5860  assert(y != NULL);
5861  assert(!SCIPisZero(scip, coef));
5862  assert(!SCIPisZero(scip, p));
5863  assert(!SCIPisZero(scip, q));
5864  assert(exprtree != NULL);
5865  assert(mult != NULL);
5866  assert(convextype != NULL);
5867 
5868  /* determine convexity type, and whether to negate monomial or swap variables */
5869  *mult = coef < 0.0 ? -1.0 : 1.0; /* for the check, assume that monomial has positive coefficient */
5870  swapvars = FALSE;
5871  *convextype = SCIP_BIVAR_UNKNOWN;
5872  if( (p + q >= 1.0 && ((p > 1.0 && q < 0.0) || (p < 0.0 && q > 1.0))) ||
5873  (p < 0.0 && q < 0.0) )
5874  {
5875  *convextype = SCIP_BIVAR_ALLCONVEX;
5876  }
5877  else if( (p > 1.0 && q > 1.0) || (p + q < 1.0 && ((p > 1.0 && q < 0.0) || (p < 0.0 && q > 1.0))) )
5878  {
5879  *convextype = SCIP_BIVAR_1CONVEX_INDEFINITE;
5880  }
5881  else if( (p < 0.0 || p > 1.0) && q > 0.0 && q < 1.0 )
5882  {
5883  *convextype = SCIP_BIVAR_CONVEX_CONCAVE;
5884  }
5885  else if( (p < 0.0 || p > 1.0) && q == 1.0 )
5886  {
5887  *mult *= -1.0;
5888  swapvars = TRUE;
5889  *convextype = SCIP_BIVAR_CONVEX_CONCAVE;
5890  }
5891  else if( (q < 0.0 || q > 1.0) && p > 0.0 && p <= 1.0 )
5892  {
5893  swapvars = TRUE;
5894  *convextype = SCIP_BIVAR_CONVEX_CONCAVE;
5895  }
5896  else if( p > 0.0 && p < 1.0 && q > 0.0 && q < 1.0 && p + q > 1.0 )
5897  {
5898  *mult *= -1.0;
5899  *convextype = SCIP_BIVAR_1CONVEX_INDEFINITE;
5900  }
5901  else if( p == 1.0 && q > 0.0 && q < 1.0 )
5902  {
5903  *convextype = SCIP_BIVAR_CONVEX_CONCAVE;
5904  }
5905  else if( q == 1.0 && p > 0.0 && p < 1.0 )
5906  {
5907  swapvars = TRUE;
5908  *convextype = SCIP_BIVAR_CONVEX_CONCAVE;
5909  }
5910  else if( p == 1.0 && q == 1.0 )
5911  {
5912  *convextype = SCIP_BIVAR_CONVEX_CONCAVE;
5913  }
5914  else if( p > 0.0 && p < 1.0 && q > 0.0 && q < 1.0 && p + q <= 1.0 )
5915  {
5916  *mult *= -1.0;
5917  *convextype = SCIP_BIVAR_ALLCONVEX;
5918  }
5919  assert(*convextype != SCIP_BIVAR_UNKNOWN); /* there should be no case where this can still happen */
5920 
5921  /* setup expression tree */
5922  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &children[0], SCIP_EXPR_VARIDX, 0) );
5923  SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &children[1], SCIP_EXPR_VARIDX, 1) );
5924  childidxs[0] = 0;
5925  childidxs[1] = 1;
5926  if( !swapvars )
5927  {
5928  exponents[0] = p;
5929  exponents[1] = q;
5930  vars[0] = x;
5931  vars[1] = y;
5932  }
5933  else
5934  {
5935  exponents[0] = q;
5936  exponents[1] = p;
5937  vars[0] = y;
5938  vars[1] = x;
5939  }
5940  SCIP_CALL( SCIPexprCreateMonomial(SCIPblkmem(scip), &monomial, *mult*coef, 2, childidxs, exponents) );
5941 
5942  SCIP_CALL( SCIPexprCreatePolynomial(SCIPblkmem(scip), &e, 2, children, 1, &monomial, 0.0, FALSE) );
5943  assert( e != NULL );
5944 
5945  SCIP_CALL( SCIPexprtreeCreate(SCIPblkmem(scip), exprtree, e, 2, 0, NULL) );
5946  SCIP_CALL( SCIPexprtreeSetVars(*exprtree, 2, vars) );
5947 
5948  return SCIP_OKAY;
5949 }
5950 
5951 /** creates bivariate constraint from monomial of the form coef * x^p * y^q with x >= 0 and y >= 0
5952  * lhs <= coef * x^p * y^q + zcoef * z <= rhs
5953  */
5954 static
5956  SCIP* scip, /**< SCIP data structure */
5957  SCIP_CONS* srccons, /**< source constraint to take attributes from, or NULL */
5958  SCIP_CONS** cons, /**< pointer to store new constraint */
5959  const char* name, /**< name of new constraint */
5960  SCIP_VAR* x, /**< first nonlinear variable */
5961  SCIP_VAR* y, /**< second nonlinear variable */
5962  SCIP_VAR* z, /**< linear variable, can be NULL */
5963  SCIP_Real coef, /**< monomial coefficient */
5964  SCIP_Real p, /**< exponent of x */
5965  SCIP_Real q, /**< exponent of y */
5966  SCIP_Real zcoef, /**< coefficient of z */
5967  SCIP_Real lhs, /**< left-hand-side */
5968  SCIP_Real rhs /**< right-hand-side */
5969  )
5970 {
5971  SCIP_Real mult;
5972  SCIP_BIVAR_CONVEXITY convextype;
5973  SCIP_EXPRTREE* exprtree;
5974 
5975  assert(scip != NULL);
5976  assert(cons != NULL);
5977  assert(name != NULL);
5978 
5979  assert(x != NULL);
5980  assert(y != NULL);
5981  assert(!SCIPisZero(scip, coef));
5982  assert(!SCIPisZero(scip, p));
5983  assert(!SCIPisZero(scip, q));
5984  assert(SCIPisLE(scip, lhs, rhs));
5985 
5986  SCIP_CALL( createExprtreeFromMonomial(scip, x, y, coef, p, q, &exprtree, &mult, &convextype) );
5987 
5988  if( mult == -1.0 )
5989  {
5990  SCIP_Real tmp;
5991  tmp = lhs;
5992  lhs = -rhs;
5993  rhs = -tmp;
5994  zcoef = -zcoef;
5995  }
5996  else
5997  {
5998  assert(mult == 1.0);
5999  }
6000 
6001  SCIPdebugMessage("upgrading monomial %g<%s>^%g<%s>^%g from constraint <%s> to bivariate constraint with convexity type %d\n", /*lint !e585*/
6002  coef, SCIPvarGetName(x), p, SCIPvarGetName(y), q, srccons != NULL ? SCIPconsGetName(srccons) : "??", convextype); /*lint !e585*/
6003 
6004  if( srccons != NULL )
6005  {
6006  SCIP_CALL( SCIPcreateConsBivariate(scip, cons, name,
6007  exprtree, convextype, z, zcoef, lhs, rhs,
6008  SCIPconsIsInitial(srccons), SCIPconsIsSeparated(srccons), SCIPconsIsEnforced(srccons),
6009  SCIPconsIsChecked(srccons), SCIPconsIsPropagated(srccons), SCIPconsIsLocal(srccons),
6010  SCIPconsIsModifiable(srccons), SCIPconsIsDynamic(srccons), SCIPconsIsRemovable(srccons),
6011  SCIPconsIsStickingAtNode(srccons)) );
6012  }
6013  else
6014  {
6015  SCIP_CALL( SCIPcreateConsBivariate(scip, cons, name,
6016  exprtree, convextype, z, zcoef, lhs, rhs,
6017  TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
6018  }
6019  SCIPdebugPrintCons(scip, *cons, NULL);
6020 
6021  SCIP_CALL( SCIPexprtreeFree(&exprtree) );
6022 
6023  return SCIP_OKAY;
6024 }
6025 
6026 /*
6027  * Callback methods of constraint handler
6028  */
6029 
6030 /** copy method for constraint handler plugins (called when SCIP copies plugins) */
6031 static
6032 SCIP_DECL_CONSHDLRCOPY(conshdlrCopyBivariate)
6033 { /*lint --e{715}*/
6034  assert(scip != NULL);
6035  assert(conshdlr != NULL);
6036  /* assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0); */
6037 
6038  /* call inclusion method of constraint handler */
6040 
6041  *valid = TRUE;
6042 
6043  return SCIP_OKAY;
6044 }
6045 
6046 /** destructor of constraint handler to free constraint handler data (called when SCIP is exiting) */
6047 static
6048 SCIP_DECL_CONSFREE(consFreeBivariate)
6049 { /*lint --e{715}*/
6050  SCIP_CONSHDLRDATA* conshdlrdata;
6051 
6052  assert(scip != NULL);
6053  assert(conshdlr != NULL);
6054 
6055  conshdlrdata = SCIPconshdlrGetData(conshdlr);
6056  assert(conshdlrdata != NULL);
6057  assert(conshdlrdata->exprinterpreter != NULL);
6058  assert(conshdlrdata->exprgraph != NULL);
6059  assert(SCIPexprgraphGetNVars(conshdlrdata->exprgraph) == 0);
6060 
6061  /* free expression graph */
6062  SCIP_CALL( SCIPexprgraphFree(&conshdlrdata->exprgraph) );
6063 
6064  if( conshdlrdata->exprinterpreter != NULL )
6065  {
6066  SCIP_CALL( SCIPexprintFree(&conshdlrdata->exprinterpreter) );
6067  }
6068 
6069  SCIPfreeMemory(scip, &conshdlrdata);
6070 
6071  return SCIP_OKAY;
6072 }
6073 
6074 /** initialization method of constraint handler (called after problem was transformed) */
6075 static
6076 SCIP_DECL_CONSINIT(consInitBivariate)
6077 { /*lint --e{715}*/
6078  SCIP_CONSHDLRDATA* conshdlrdata;
6079 
6080  assert(scip != NULL);
6081  assert(conshdlr != NULL);
6082 
6083  conshdlrdata = SCIPconshdlrGetData(conshdlr);
6084  assert(conshdlrdata != NULL);
6085 
6086  conshdlrdata->subnlpheur = SCIPfindHeur(scip, "subnlp");
6087  conshdlrdata->trysolheur = SCIPfindHeur(scip, "trysol");
6088 
6089  return SCIP_OKAY;
6090 }
6091 
6092 /** deinitialization method of constraint handler (called before transformed problem is freed) */
6093 static
6094 SCIP_DECL_CONSEXIT(consExitBivariate)
6095 { /*lint --e{715}*/
6096  SCIP_CONSHDLRDATA* conshdlrdata;
6097 
6098  assert(scip != NULL);
6099  assert(conshdlr != NULL);
6100 
6101  conshdlrdata = SCIPconshdlrGetData(conshdlr);
6102  assert(conshdlrdata != NULL);
6103 
6104  conshdlrdata->subnlpheur = NULL;
6105  conshdlrdata->trysolheur = NULL;
6106 
6107  return SCIP_OKAY;
6108 }
6109 
6110 /** presolving initialization method of constraint handler (called when presolving is about to begin) */
6111 static
6112 SCIP_DECL_CONSINITPRE(consInitpreBivariate)
6113 { /*lint --e{715}*/
6114  SCIP_CONSDATA* consdata;
6115  int c;
6116 
6117  assert(scip != NULL);
6118  assert(conss != NULL || nconss == 0);
6119 
6120  for( c = 0; c < nconss; ++c )
6121  {
6122  consdata = SCIPconsGetData(conss[c]); /*lint !e613*/
6123  assert(consdata != NULL);
6124 
6125  /* reset may{in,de}creasez to FALSE in case some values are still set from a previous solve round */
6126  consdata->mayincreasez = FALSE;
6127  consdata->maydecreasez = FALSE;
6128  }
6129 
6130  return SCIP_OKAY;
6131 }
6132 
6133 /** presolving deinitialization method of constraint handler (called after presolving has been finished) */
6134 static
6135 SCIP_DECL_CONSEXITPRE(consExitpreBivariate)
6136 { /*lint --e{715}*/
6137  SCIP_CONSHDLRDATA* conshdlrdata;
6138  int c;
6139  SCIP_Bool changed;
6140  SCIP_Bool upgraded;
6141 #ifndef NDEBUG
6142  SCIP_CONSDATA* consdata;
6143 #endif
6144 
6145  assert(scip != NULL);
6146  assert(conss != NULL || nconss == 0);
6147 
6148  conshdlrdata = SCIPconshdlrGetData(conshdlr);
6149  assert(conshdlrdata != NULL);
6150 
6151  if( !conshdlrdata->isremovedfixings )
6152  {
6153  SCIP_CALL( removeFixedNonlinearVariables(scip, conshdlr) );
6154  assert(conshdlrdata->isremovedfixings);
6155  /* @todo call expression graph simplifier? */
6156  }
6157 
6158  for( c = 0; c < nconss; ++c )
6159  {
6160  assert(conss != NULL); /* for flexelint */
6161  assert(conss[c] != NULL);
6162 
6163  /* make sure variable fixations have been resolved */
6164  SCIP_CALL( removeFixedVariables(scip, conshdlr, conss[c], &changed, &upgraded) );
6165  assert(!upgraded);
6166 
6167 #ifndef NDEBUG
6168  consdata = SCIPconsGetData(conss[c]);
6169  assert(consdata != NULL);
6170 
6171  assert(consdata->f != NULL);
6172  assert(SCIPexprtreeGetNVars(consdata->f) == 2);
6173  assert(consdata->z == NULL || SCIPvarIsActive(consdata->z) || SCIPvarGetStatus(consdata->z) == SCIP_VARSTATUS_MULTAGGR);
6174 #endif
6175 
6176  /* tell SCIP that we have something nonlinear */
6177  if( SCIPconsIsAdded(conss[c]) )
6178  SCIPenableNLP(scip);
6179  }
6180 
6181  return SCIP_OKAY;
6182 }
6183 
6184 /** solving process initialization method of constraint handler (called when branch and bound process is about to begin) */
6185 static
6186 SCIP_DECL_CONSINITSOL(consInitsolBivariate)
6187 { /*lint --e{715}*/
6188  SCIP_CONSHDLRDATA* conshdlrdata;
6189  SCIP_CONSDATA* consdata;
6190  int c;
6191 #ifdef TYPESTATISTICS
6192  int nconvextypeslhs[(int)SCIP_BIVAR_UNKNOWN+1];
6193  int nconvextypesrhs[(int)SCIP_BIVAR_UNKNOWN+1];
6194 #endif
6195 
6196  assert(scip != NULL);
6197  assert(conss != NULL || nconss == 0);
6198 
6199  conshdlrdata = SCIPconshdlrGetData(conshdlr);
6200  assert(conshdlrdata != NULL);
6201 
6202 #ifdef TYPESTATISTICS
6203  BMSclearMemoryArray(nconvextypeslhs, (int)SCIP_BIVAR_UNKNOWN+1);
6204  BMSclearMemoryArray(nconvextypesrhs, (int)SCIP_BIVAR_UNKNOWN+1);
6205 #endif
6206 
6207  for( c = 0; c < nconss; ++c )
6208  {
6209  assert(conss[c] != NULL); /*lint !e613*/
6210 
6211  consdata = SCIPconsGetData(conss[c]); /*lint !e613*/
6212  assert(consdata != NULL);
6213 
6214  /* check if linear variable can be rounded up or down without harming other constraints */
6215  if( consdata->z != NULL )
6216  {
6217  int poslock;
6218  int neglock;
6219 
6220  if( consdata->zcoef > 0.0 )
6221  {
6222  poslock = !SCIPisInfinity(scip, -consdata->lhs) ? 1 : 0;
6223  neglock = !SCIPisInfinity(scip, consdata->rhs) ? 1 : 0;
6224  }
6225  else
6226  {
6227  poslock = !SCIPisInfinity(scip, consdata->rhs) ? 1 : 0;
6228  neglock = !SCIPisInfinity(scip, -consdata->lhs) ? 1 : 0;
6229  }
6230 
6231  if( SCIPvarGetNLocksDown(consdata->z) - neglock == 0 )
6232  {
6233  /* for c*z + f(x,y) \in [lhs, rhs], we can decrease z without harming other constraints */
6234  consdata->maydecreasez = TRUE;
6235  SCIPdebugMessage("may decrease <%s> to become feasible\n", SCIPvarGetName(consdata->z));
6236  }
6237 
6238  if( SCIPvarGetNLocksDown(consdata->z) - poslock == 0 )
6239  {
6240  /* for c*x + f(x,y) \in [lhs, rhs], we can increase x without harming other constraints */
6241  consdata->mayincreasez = TRUE;
6242  SCIPdebugMessage("may increase <%s> to become feasible\n", SCIPvarGetName(consdata->z));
6243  }
6244  }
6245 
6246  /* add nlrow respresentation to NLP, if NLP had been constructed */
6247  if( SCIPisNLPConstructed(scip) && SCIPconsIsEnabled(conss[c]) ) /*lint !e613*/
6248  {
6249  SCIP_NLROW* nlrow;
6250 
6251  SCIP_CALL( SCIPcreateNlRow(scip, &nlrow, SCIPconsGetName(conss[c]), 0.0,
6252  consdata->z != NULL ? 1 : 0, consdata->z != NULL ? &consdata->z : NULL, &consdata->zcoef,
6253  0, NULL, 0, NULL,
6254  consdata->f, consdata->lhs, consdata->rhs) ); /*lint !e826 !e613*/
6255 
6256  SCIP_CALL( SCIPaddNlRow(scip, nlrow) );
6257  SCIP_CALL( SCIPreleaseNlRow(scip, &nlrow) );
6258  }
6259 
6260  /* initialize data for cut generation */
6261  SCIP_CALL( initSepaData(scip, conshdlrdata->exprinterpreter, conss[c]) ); /*lint !e613*/
6262 
6263 #ifdef TYPESTATISTICS
6264  if( !SCIPisInfinity(scip, -consdata->lhs) )
6265  ++nconvextypeslhs[consdata->convextype];
6266  if( !SCIPisInfinity(scip, consdata->rhs) )
6267  ++nconvextypesrhs[consdata->convextype];
6268 #endif
6269  }
6270 
6271  conshdlrdata->newsoleventfilterpos = -1;
6272  if( nconss != 0 )
6273  {
6274  SCIP_EVENTHDLR* eventhdlr;
6275 
6276  eventhdlr = SCIPfindEventhdlr(scip, CONSHDLR_NAME"_newsolution");
6277  assert(eventhdlr != NULL);
6278 
6279  SCIP_CALL( SCIPcatchEvent(scip, SCIP_EVENTTYPE_SOLFOUND, eventhdlr, (SCIP_EVENTDATA*)conshdlr, &conshdlrdata->newsoleventfilterpos) );
6280 
6281 #ifdef TYPESTATISTICS
6282  for( c = 0; c <= (int)SCIP_BIVAR_UNKNOWN; ++c )
6283  {
6284  const char* typename;
6285  switch( c )
6286  {
6287  case SCIP_BIVAR_ALLCONVEX:
6288  typename = "allconvex";
6289  break;
6291  typename = "1-convex";
6292  break;
6294  typename = "convex-concave";
6295  break;
6296  case SCIP_BIVAR_UNKNOWN:
6297  default:
6298  typename = "unknown";
6299  break;
6300  }
6301  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "%4d left and %4d right bivariate constraints of type [%s]\n", nconvextypeslhs[c], nconvextypesrhs[c], typename);
6302  }
6303 #endif
6304 
6305  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "NOTE: the constraint handler for bivariate constraints is still experimental!\n");
6306  }
6307 
6308  /* reset counter */
6309  conshdlrdata->lastenfolpnode = NULL;
6310  conshdlrdata->nenfolprounds = 0;
6311 
6312  return SCIP_OKAY;
6313 }
6314 
6315 /** solving process deinitialization method of constraint handler (called before branch and bound process data is freed) */
6316 static
6317 SCIP_DECL_CONSEXITSOL(consExitsolBivariate)
6318 { /*lint --e{715}*/
6319  SCIP_CONSHDLRDATA* conshdlrdata;
6320  int c;
6321 
6322  assert(scip != NULL);
6323  assert(conss != NULL || nconss == 0);
6324 
6325  conshdlrdata = SCIPconshdlrGetData(conshdlr);
6326  assert(conshdlrdata != NULL);
6327 
6328  if( conshdlrdata->newsoleventfilterpos >= 0 )
6329  {
6330  SCIP_EVENTHDLR* eventhdlr;
6331 
6332  eventhdlr = SCIPfindEventhdlr(scip, CONSHDLR_NAME"_newsolution");
6333  assert(eventhdlr != NULL);
6334 
6335  SCIP_CALL( SCIPdropEvent(scip, SCIP_EVENTTYPE_SOLFOUND, eventhdlr, (SCIP_EVENTDATA*)conshdlr, conshdlrdata->newsoleventfilterpos) );
6336  conshdlrdata->newsoleventfilterpos = -1;
6337  }
6338 
6339  for( c = 0; c < nconss; ++c )
6340  {
6341  /* free data for cut generation */
6342  assert(conss[c] != NULL); /*lint !e613*/
6343 
6344  SCIP_CALL( freeSepaData(scip, conss[c]) ); /*lint !e613*/
6345  }
6346 
6347  return SCIP_OKAY;
6348 }
6349 
6350 /** frees specific constraint data */
6351 static
6352 SCIP_DECL_CONSDELETE(consDeleteBivariate)
6353 { /*lint --e{715}*/
6354 #ifndef NDEBUG
6355  SCIP_CONSHDLRDATA* conshdlrdata;
6356 #endif
6357 
6358  assert(scip != NULL);
6359  assert(cons != NULL);
6360  assert(consdata != NULL);
6361 
6362 #ifndef NDEBUG
6363  conshdlrdata = SCIPconshdlrGetData(conshdlr);
6364  assert(conshdlrdata != NULL);
6365 #endif
6366 
6367  /* expression should have been removed from expression graph when constraint was deactivated */
6368  assert((*consdata)->exprgraphnode == NULL);
6369 
6370  if( (*consdata)->f != NULL )
6371  {
6372  SCIP_CALL( SCIPexprtreeFree(&(*consdata)->f) );
6373  }
6374 
6375  SCIPfreeMemory(scip, consdata);
6376  *consdata = NULL;
6377 
6378  return SCIP_OKAY;
6379 }
6380 
6381 /** transforms constraint data into data belonging to the transformed problem */
6382 static
6383 SCIP_DECL_CONSTRANS(consTransBivariate)
6384 { /*lint --e{715}*/
6385  SCIP_CONSDATA* sourcedata;
6386  SCIP_CONSDATA* targetdata;
6387 
6388  SCIP_VAR* targetvars[2];
6389 
6390  sourcedata = SCIPconsGetData(sourcecons);
6391  assert(sourcedata != NULL);
6392 
6393  SCIP_CALL( SCIPduplicateMemory(scip, &targetdata, sourcedata) );
6394  assert(targetdata->eventfilterpos == -1);
6395 
6396  assert(sourcedata->f != NULL);
6397  SCIP_CALL( SCIPexprtreeCopy(SCIPblkmem(scip), &targetdata->f, sourcedata->f) );
6398  SCIP_CALL( SCIPgetTransformedVars(scip, 2, SCIPexprtreeGetVars(sourcedata->f), targetvars) );
6399  SCIP_CALL( SCIPexprtreeSetVars(targetdata->f, 2, targetvars) );
6400 
6401  if( sourcedata->z != NULL )
6402  {
6403  SCIP_CALL( SCIPgetTransformedVar(scip, sourcedata->z, &targetdata->z) );
6404  }
6405 
6406  SCIP_CALL( SCIPcreateCons(scip, targetcons, SCIPconsGetName(sourcecons), conshdlr, targetdata,
6407  SCIPconsIsInitial(sourcecons), SCIPconsIsSeparated(sourcecons), SCIPconsIsEnforced(sourcecons),
6408  SCIPconsIsChecked(sourcecons), SCIPconsIsPropagated(sourcecons), SCIPconsIsLocal(sourcecons),
6409  SCIPconsIsModifiable(sourcecons), SCIPconsIsDynamic(sourcecons), SCIPconsIsRemovable(sourcecons),
6410  SCIPconsIsStickingAtNode(sourcecons)) );
6411 
6412  return SCIP_OKAY;
6413 }
6414 
6415 /** LP initialization method of constraint handler (called before the initial LP relaxation at a node is solved) */
6416 static
6417 SCIP_DECL_CONSINITLP(consInitlpBivariate)
6418 { /*lint --e{715}*/
6419  SCIP_CONSHDLRDATA* conshdlrdata;
6420  SCIP_CONSDATA* consdata;
6421  SCIP_ROW* row1;
6422  SCIP_ROW* row2;
6423  SCIP_Real xy[2];
6424  int c;
6425  int i;
6426  int ix;
6427  int iy;
6428  int nref;
6429  SCIP_Real lb[2];
6430  SCIP_Real ub[2];
6431  SCIP_Bool unbounded[2];
6432 
6433  assert(scip != NULL);
6434  assert(conshdlr != NULL);
6435  assert(conss != NULL || nconss == 0);
6436 
6437  conshdlrdata = SCIPconshdlrGetData(conshdlr);
6438  assert(conshdlrdata != NULL);
6439 
6440  nref = conshdlrdata->ninitlprefpoints;
6441 
6442  if( nref == 0 )
6443  {
6444  SCIPdebugMessage("skip LP initialization since ninitlprefpoints is 0\n");
6445  return SCIP_OKAY;
6446  }
6447 
6448  row1 = NULL;
6449  row2 = NULL;
6450 
6451  for( c = 0; c < nconss; ++c )
6452  {
6453  assert(conss[c] != NULL); /*lint !e613*/
6454 
6455  consdata = SCIPconsGetData(conss[c]); /*lint !e613*/
6456  assert(consdata != NULL);
6457  assert(consdata->f != NULL);
6458 
6459  if( SCIPexprtreeGetInterpreterData(consdata->f) == NULL )
6460  {
6461  SCIP_CALL( SCIPexprintCompile(conshdlrdata->exprinterpreter, consdata->f) );
6462  }
6463 
6464  /* create a bounded rectangle in which we take reference points for initial cut generation
6465  * For a missing bound, we either reflect the other bound at 0.0 if finite and on the right side,
6466  * or double the other bound if on the same side but not 0.0, or set it to +/-1000.0.
6467  */
6468  for( i = 0; i < 2; ++i )
6469  {
6470  lb[i] = SCIPvarGetLbGlobal(SCIPexprtreeGetVars(consdata->f)[i]);
6471  ub[i] = SCIPvarGetUbGlobal(SCIPexprtreeGetVars(consdata->f)[i]);
6472 
6473  unbounded[i] = FALSE;
6474  if( SCIPisInfinity(scip, -lb[i]) )
6475  {
6476  unbounded[i] = TRUE;
6477  ub[i] = MIN(INITLPMAXVARVAL, ub[i]);
6478  if( SCIPisPositive(scip, ub[i]) )
6479  lb[i] = -ub[i];
6480  else if( SCIPisZero(scip, ub[i]) )
6481  lb[i] = -INITLPMAXVARVAL;
6482  else
6483  lb[i] = 2.0 * ub[i];
6484  }
6485  else if( SCIPisInfinity(scip, ub[i]) )
6486  {
6487  unbounded[i] = TRUE;
6488  assert(!SCIPisInfinity(scip, -lb[i]));
6489  lb[i] = MAX(-INITLPMAXVARVAL, lb[i]);
6490  if( SCIPisNegative(scip, lb[i]) )
6491  ub[i] = -lb[i];
6492  else if( SCIPisZero(scip, lb[i]) )
6493  ub[i] = INITLPMAXVARVAL;
6494  else
6495  ub[i] = 2.0 * lb[i];
6496  }
6497  }
6498 
6499  for( ix = 0; ix < nref; ++ix )
6500  {
6501  if( nref > 1 )
6502  xy[0] = lb[0] + ix * (ub[0] - lb[0]) / (nref - 1.0);
6503  else
6504  xy[0] = (lb[0] + ub[0]) / 2.0;
6505 
6506  for( iy = 0; iy < nref; ++iy )
6507  {
6508  if( nref > 1 )
6509  xy[1] = lb[1] + iy * (ub[1] - lb[1]) / (nref - 1.0);
6510  else
6511  xy[1] = (lb[1] + ub[1]) / 2.0;
6512 
6513  SCIPdebugMessage("cons <%s>: generate cuts for <%s> = %g [%g,%g], <%s> = %g [%g,%g]\n",
6514  SCIPconsGetName(conss[c]), /*lint !e613*/
6515  SCIPvarGetName(SCIPexprtreeGetVars(consdata->f)[0]), xy[0],
6517  SCIPvarGetName(SCIPexprtreeGetVars(consdata->f)[1]), xy[1],
6519  );
6520 
6521  /* try to generate one cut for each side */
6522  switch( consdata->convextype )
6523  {
6524  case SCIP_BIVAR_ALLCONVEX:
6525  {
6526  if( !SCIPisInfinity(scip, -consdata->lhs) && !unbounded[0] && !unbounded[1] && (ix == 0 || ix == nref-1) && (iy == 0 || iy == nref-1) )
6527  {
6528  /* lhs is finite and both variables are bounded, so can do overest. hyperplane
6529  * do this only for corner points, since we can get at most two cuts out of it
6530  * @todo generate only two cuts instead of four
6531  */
6532  SCIP_CALL( generateOverestimatingHyperplaneCut(scip, conshdlrdata->exprinterpreter, conss[c], xy, &row1) ); /*lint !e613*/
6533  }
6534  if( !SCIPisInfinity(scip, consdata->rhs) )
6535  {
6536  /* rhs is finite */
6537  SCIP_CALL( generateLinearizationCut(scip, conshdlrdata->exprinterpreter, conss[c], xy, TRUE, &row2) ); /*lint !e613*/
6538  }
6539  break;
6540  }
6541 
6543  {
6544  if( !SCIPisInfinity(scip, -consdata->lhs) && !unbounded[0])
6545  {
6546  /* lhs is finite and x is bounded */
6547  SCIP_CALL( generateConvexConcaveEstimator(scip, conshdlrdata->exprinterpreter, conss[c], xy, SCIP_SIDETYPE_LEFT, &row1) ); /*lint !e613*/
6548  }
6549  if( !SCIPisInfinity(scip, consdata->rhs) && !unbounded[1])
6550  {
6551  /* rhs is finite and y is bounded */
6552  SCIP_CALL( generateConvexConcaveEstimator(scip, conshdlrdata->exprinterpreter, conss[c], xy, SCIP_SIDETYPE_RIGHT, &row2) ); /*lint !e613*/
6553  }
6554  break;
6555  }
6556 
6558  {
6559  if( !SCIPisInfinity(scip, -consdata->lhs) && !unbounded[0] && !unbounded[1] && (ix == 0 || ix == nref-1) && (iy == 0 || iy == nref-1) )
6560  {
6561  /* lhs is finite and both variables are bounded
6562  * do this only for corner points, since we can get at most two cuts out of it
6563  * @todo generate only two cuts instead of four
6564  */
6565  SCIP_CALL( generateOverestimatingHyperplaneCut(scip, conshdlrdata->exprinterpreter, conss[c], xy, &row1) ); /*lint !e613*/
6566  }
6567  if( !SCIPisInfinity(scip, consdata->rhs) && !unbounded[0] && !unbounded[1] )
6568  { /* rhs is finite and both variables are bounded */
6569  SCIP_CALL( generate1ConvexIndefiniteUnderestimator(scip, conshdlrdata->exprinterpreter, conss[c], xy, &row2) ); /*lint !e613*/
6570  }
6571  break;
6572  }
6573 
6574  default:
6575  {
6576  SCIPwarningMessage(scip, "initlp for convexity type %d not implemented\n", consdata->convextype);
6577  }
6578  } /*lint !e788*/
6579 
6580  /* check numerics */
6581  if( row1 != NULL )
6582  {
6583  if( SCIPgetRowMaxCoef(scip, row1) / SCIPgetRowMinCoef(scip, row1) > conshdlrdata->cutmaxrange )
6584  {
6585  SCIPdebugMessage("drop row1 for constraint <%s> because range of coefficients is too large: mincoef = %g, maxcoef = %g -> range = %g\n",
6586  SCIPconsGetName(conss[c]), SCIPgetRowMinCoef(scip, row1), SCIPgetRowMaxCoef(scip, row1), SCIPgetRowMaxCoef(scip, row1) / SCIPgetRowMinCoef(scip, row1)); /*lint !e613*/
6587  SCIP_CALL( SCIPreleaseRow(scip, &row1) );
6588  }
6589  else if( SCIPisInfinity(scip, -SCIProwGetLhs(row1)) )
6590  {
6591  /* row1 should be a cut with finite lhs, but infinite rhs */
6592  assert(SCIPisInfinity(scip, SCIProwGetRhs(row1)));
6593  SCIPdebugMessage("drop row1 for constraint <%s> because of very large lhs: %g\n", SCIPconsGetName(conss[c]), SCIProwGetLhs(row1)); /*lint !e613*/
6594  SCIP_CALL( SCIPreleaseRow(scip, &row1) );
6595  }
6596  }
6597 
6598  if( row2 != NULL )
6599  {
6600  if( SCIPgetRowMaxCoef(scip, row2) / SCIPgetRowMinCoef(scip, row2) > conshdlrdata->cutmaxrange )
6601  {
6602  SCIPdebugMessage("drop row2 for constraint <%s> because range of coefficients is too large: mincoef = %g, maxcoef = %g -> range = %g\n",
6603  SCIPconsGetName(conss[c]), SCIPgetRowMinCoef(scip, row2), SCIPgetRowMaxCoef(scip, row2), SCIPgetRowMaxCoef(scip, row2) / SCIPgetRowMinCoef(scip, row2)); /*lint !e613*/
6604  SCIP_CALL( SCIPreleaseRow(scip, &row2) );
6605  }
6606  else if( SCIPisInfinity(scip, SCIProwGetRhs(row2)) )
6607  {
6608  /* row2 should be a cut with finite rhs, but infinite lhs */
6609  assert(SCIPisInfinity(scip, SCIProwGetRhs(row2)));
6610  SCIPdebugMessage("drop row2 for constraint <%s> because of very large rhs: %g\n", SCIPconsGetName(conss[c]), SCIProwGetLhs(row2)); /*lint !e613*/
6611  SCIP_CALL( SCIPreleaseRow(scip, &row2) );
6612  }
6613  }
6614 
6615  /* add to LP */
6616  if( row1 != NULL )
6617  {
6618  SCIP_Bool infeasible;
6619  SCIP_CALL( SCIPaddCut(scip, NULL, row1, FALSE /* forcecut */, &infeasible) );
6620  assert( ! infeasible );
6621  SCIPdebug( SCIP_CALL( SCIPprintRow(scip, row1, NULL) ) );
6622  SCIP_CALL( SCIPreleaseRow(scip, &row1) );
6623  }
6624  if( row2 != NULL )
6625  {
6626  SCIP_Bool infeasible;
6627  SCIP_CALL( SCIPaddCut(scip, NULL, row2, FALSE /* forcecut */, &infeasible) );
6628  assert( ! infeasible );
6629  SCIPdebug( SCIP_CALL( SCIPprintRow(scip, row2, NULL) ) );
6630  SCIP_CALL( SCIPreleaseRow(scip, &row2) );
6631  }
6632  }
6633  }
6634  }
6635 
6636  return SCIP_OKAY;
6637 }
6638 
6639 /** separation method of constraint handler for LP solutions */
6640 static
6641 SCIP_DECL_CONSSEPALP(consSepalpBivariate)
6642 { /*lint --e{715}*/
6643  SCIP_CONSHDLRDATA* conshdlrdata;
6644  SCIP_CONS* maxviolcon;
6645 
6646  assert(scip != NULL);
6647  assert(conshdlr != NULL);
6648  assert(conss != NULL || nconss == 0);
6649  assert(result != NULL);
6650 
6651  *result = SCIP_DIDNOTFIND;
6652 
6653  conshdlrdata = SCIPconshdlrGetData(conshdlr);
6654  assert(conshdlrdata != NULL);
6655 
6656  SCIP_CALL( computeViolations(scip, conshdlr, conss, nconss, NULL, &maxviolcon) );
6657  if( maxviolcon == NULL )
6658  return SCIP_OKAY;
6659 
6660  /* @todo add separation of convex (only?) constraints in nlp relaxation solution */
6661 
6662  SCIP_CALL( separatePoint(scip, conshdlr, conss, nconss, nusefulconss, NULL, conshdlrdata->mincutefficacysepa, FALSE, result, NULL) );
6663 
6664  return SCIP_OKAY;
6665 }
6666 
6667 /** separation method of constraint handler for arbitrary primal solutions */
6668 static
6669 SCIP_DECL_CONSSEPASOL(consSepasolBivariate)
6670 { /*lint --e{715}*/
6671  SCIP_CONSHDLRDATA* conshdlrdata;
6672  SCIP_CONS* maxviolcon;
6673 
6674  assert(scip != NULL);
6675  assert(conshdlr != NULL);
6676  assert(conss != NULL || nconss == 0);
6677  assert(sol != NULL);
6678  assert(result != NULL);
6679 
6680  conshdlrdata = SCIPconshdlrGetData(conshdlr);
6681  assert(conshdlrdata != NULL);
6682 
6683  *result = SCIP_DIDNOTFIND;
6684 
6685  SCIP_CALL( computeViolations(scip, conshdlr, conss, nconss, sol, &maxviolcon) );
6686  if( maxviolcon == NULL )
6687  return SCIP_OKAY;
6688 
6689  SCIP_CALL( separatePoint(scip, conshdlr, conss, nconss, nusefulconss, sol, conshdlrdata->mincutefficacysepa, FALSE, result, NULL) );
6690 
6691  return SCIP_OKAY;
6692 }
6693 
6694 /** constraint enforcing method of constraint handler for LP solutions */
6695 static
6696 SCIP_DECL_CONSENFOLP(consEnfolpBivariate)
6697 { /*lint --e{715}*/
6698  SCIP_CONSHDLRDATA* conshdlrdata;
6699  SCIP_CONSDATA* consdata;
6700  SCIP_CONS* maxviolcons;
6701  SCIP_Real maxviol;
6702  SCIP_RESULT propresult;
6703  SCIP_RESULT separateresult;
6704  int dummy;
6705  int nnotify;
6706  SCIP_Real sepaefficacy;
6707  SCIP_Real minefficacy;
6708  SCIP_Real leastpossibleefficacy;
6709 
6710  assert(scip != NULL);
6711  assert(conshdlr != NULL);
6712  assert(conss != NULL || nconss == 0);
6713  assert(result != NULL);
6714 
6715  conshdlrdata = SCIPconshdlrGetData(conshdlr);
6716  assert(conshdlrdata != NULL);
6717 
6718  SCIP_CALL( computeViolations(scip, conshdlr, conss, nconss, NULL, &maxviolcons) );
6719  if( maxviolcons == NULL )
6720  {
6721  *result = SCIP_FEASIBLE;
6722  return SCIP_OKAY;
6723  }
6724 
6725  *result = SCIP_INFEASIBLE;
6726 
6727  /* if we are above the 100'th enforcement round for this node, something is strange
6728  * (maybe the LP does not think that the cuts we add are violated, or we do ECP on a high-dimensional convex function)
6729  * in this case, check if some limit is hit or SCIP should stop for some other reason and terminate enforcement by creating a dummy node
6730  * (in optimized more, returning SCIP_INFEASIBLE in *result would be sufficient, but in debug mode this would give an assert in scip.c)
6731  * the reason to wait for 100 rounds is to avoid calls to SCIPisStopped in normal runs, which may be expensive
6732  * we only increment nenfolprounds until 101 to avoid an overflow
6733  */
6734  if( conshdlrdata->lastenfolpnode == SCIPgetCurrentNode(scip) )
6735  {
6736  if( conshdlrdata->nenfolprounds > 100 )
6737  {
6738  if( SCIPisStopped(scip) )
6739  {
6740  SCIP_NODE* child;
6741 
6742  SCIP_CALL( SCIPcreateChild(scip, &child, 1.0, SCIPnodeGetEstimate(SCIPgetCurrentNode(scip))) );
6743  *result = SCIP_BRANCHED;
6744 
6745  return SCIP_OKAY;
6746  }
6747  }
6748  else
6749  ++conshdlrdata->nenfolprounds;
6750  }
6751  else
6752  {
6753  conshdlrdata->lastenfolpnode = SCIPgetCurrentNode(scip);
6754  conshdlrdata->nenfolprounds = 0;
6755  }
6756 
6757  consdata = SCIPconsGetData(maxviolcons);
6758  assert(consdata != NULL);
6759  maxviol = consdata->lhsviol + consdata->rhsviol;
6760  assert(SCIPisGT(scip, maxviol, SCIPfeastol(scip)));
6761 
6762  SCIPdebugMessage("enfolp with max violation %g in cons <%s>\n", maxviol, SCIPconsGetName(maxviolcons));
6763 
6764  /* run domain propagation */
6765  dummy = 0;
6766  SCIP_CALL( propagateBounds(scip, conshdlr, conss, nconss, &propresult, &dummy, &dummy) );
6767  if( propresult == SCIP_CUTOFF || propresult == SCIP_REDUCEDDOM )
6768  {
6769  *result = propresult;
6770  return SCIP_OKAY;
6771  }
6772 
6773  /* we would like a cut that is efficient enough that it is not redundant in the LP (>feastol)
6774  * however, if the maximal violation is very small, also the best cut efficacy cannot be large
6775  * thus, in the latter case, we are also happy if the efficacy is at least, say, 75% of the maximal violation
6776  * but in any case we need an efficacy that is at least feastol
6777  */
6778  minefficacy = MIN(0.75*maxviol, conshdlrdata->mincutefficacyenfo); /*lint !e666*/
6779  minefficacy = MAX(minefficacy, SCIPfeastol(scip)); /*lint !e666*/
6780  SCIP_CALL( separatePoint(scip, conshdlr, conss, nconss, nusefulconss, NULL, minefficacy, TRUE, &separateresult, &sepaefficacy) );
6781  if( separateresult == SCIP_SEPARATED || separateresult == SCIP_CUTOFF )
6782  {
6783  SCIPdebugMessage("separation succeeded (bestefficacy = %g, minefficacy = %g, cutoff = %d)\n", sepaefficacy, minefficacy, separateresult == SCIP_CUTOFF);
6784  *result = separateresult;
6785  return SCIP_OKAY;
6786  }
6787 
6788  /* we are not feasible, the whole node is not infeasible, and we cannot find a good cut
6789  * -> collect variables for branching
6790  */
6791 
6792  SCIPdebugMessage("separation failed (bestefficacy = %g < %g = minefficacy ); max viol: %g\n", sepaefficacy, minefficacy, maxviol);
6793 
6794  /* find branching candidates */
6795  SCIP_CALL( registerBranchingVariables(scip, conss, nconss, &nnotify) );
6796 
6797  /* if sepastore can decrease LP feasibility tolerance, we can add cuts with efficacy in [eps, feastol] */
6798  leastpossibleefficacy = SCIPgetRelaxFeastolFactor(scip) > 0.0 ? SCIPepsilon(scip) : SCIPfeastol(scip);
6799  if( nnotify == 0 && !solinfeasible && minefficacy > leastpossibleefficacy )
6800  {
6801  /* fallback 1: we also have no branching candidates, so try to find a weak cut */
6802  SCIP_CALL( separatePoint(scip, conshdlr, conss, nconss, nusefulconss, NULL, leastpossibleefficacy, TRUE, &separateresult, &sepaefficacy) );
6803  if( separateresult == SCIP_SEPARATED || separateresult == SCIP_CUTOFF )
6804  {
6805  *result = separateresult;
6806  return SCIP_OKAY;
6807  }
6808  }
6809 
6810  if( nnotify == 0 && !solinfeasible )
6811  {
6812  /* fallback 2: separation probably failed because of numerical difficulties with a convex constraint;
6813  * if noone declared solution infeasible yet and we had not even found a weak cut, try to resolve by branching
6814  */
6815  SCIP_VAR* brvar = NULL;
6816  SCIP_CALL( registerLargeLPValueVariableForBranching(scip, conss, nconss, &brvar) );
6817  if( brvar == NULL )
6818  {
6819  /* fallback 3: all nonlinear variables in all violated constraints seem to be fixed -> treat as linear constraint in one variable */
6820  SCIP_Bool reduceddom;
6821  SCIP_Bool infeasible;
6822 
6823  SCIP_CALL( enforceViolatedFixedNonlinear(scip, conss, nconss, &reduceddom, &infeasible) );
6824  /* if the linear constraints are actually feasible, then adding them and returning SCIP_CONSADDED confuses SCIP when it enforces the new constraints again and nothing resolves the infeasiblity that we declare here
6825  * thus, we only add them if considered violated, and otherwise claim the solution is feasible (but print a warning)
6826  */
6827  if ( infeasible )
6828  *result = SCIP_CUTOFF;
6829  else if ( reduceddom )
6830  *result = SCIP_REDUCEDDOM;
6831  else
6832  {
6833  *result = SCIP_FEASIBLE;
6834  SCIPwarningMessage(scip, "could not enforce feasibility by separating or branching; declaring solution with viol %g as feasible\n", maxviol);
6835  }
6836  return SCIP_OKAY;
6837  }
6838  else
6839  {
6840  SCIPdebugMessage("Could not find any usual branching variable candidate. Proposed variable <%s> with LP value %g for branching.\n", SCIPvarGetName(brvar), SCIPgetSolVal(scip, NULL, brvar));
6841  nnotify = 1;
6842  }
6843  }
6844 
6845  assert(*result == SCIP_INFEASIBLE && (solinfeasible || nnotify > 0));
6846  return SCIP_OKAY;
6847 }
6848 
6849 
6850 /** constraint enforcing method of constraint handler for pseudo solutions */
6851 static
6852 SCIP_DECL_CONSENFOPS(consEnfopsBivariate)
6853 { /*lint --e{715}*/
6854  SCIP_CONS* maxviolcons;
6855  SCIP_CONSDATA* consdata;
6856  SCIP_RESULT propresult;
6857  SCIP_VAR* var;
6858  int nnotify;
6859  int dummy;
6860  int c;
6861  int i;
6862 
6863  assert(scip != NULL);
6864  assert(conss != NULL || nconss == 0);
6865 
6866  SCIP_CALL( computeViolations(scip, conshdlr, conss, nconss, NULL, &maxviolcons) );
6867  if( maxviolcons == NULL )
6868  {
6869  *result = SCIP_FEASIBLE;
6870  return SCIP_OKAY;
6871  }
6872 
6873  *result = SCIP_INFEASIBLE;
6874 
6875  SCIPdebugMessage("enfops with max violation in cons <%s>\n", SCIPconsGetName(maxviolcons));
6876 
6877  /* run domain propagation */
6878  dummy = 0;
6879  SCIP_CALL( propagateBounds(scip, conshdlr, conss, nconss, &propresult, &dummy, &dummy) );
6880  if( propresult == SCIP_CUTOFF || propresult == SCIP_REDUCEDDOM )
6881  {
6882  *result = propresult;
6883  return SCIP_OKAY;
6884  }
6885 
6886  /* we are not feasible and we cannot proof that the whole node is infeasible
6887  * -> collect all variables in violated constraints for branching
6888  */
6889 
6890  nnotify = 0;
6891  for( c = 0; c < nconss; ++c )
6892  {
6893  assert(conss != NULL);
6894  consdata = SCIPconsGetData(conss[c]);
6895  assert(consdata != NULL);
6896  assert(consdata->f != NULL);
6897 
6898  if( !SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) && !SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
6899  continue;
6900 
6901  /* if nonlinear variables are fixed, z should be propagated such that the constraint becomes feasible,
6902  * so there should be no branching on z necessary
6903  */
6904  if( consdata->z != NULL && !SCIPisRelEQ(scip, SCIPvarGetLbLocal(consdata->z), SCIPvarGetUbLocal(consdata->z)) )
6905  {
6906  SCIP_CALL( SCIPaddExternBranchCand(scip, consdata->z, MAX(consdata->lhsviol, consdata->rhsviol), SCIP_INVALID) );
6907  ++nnotify;
6908  }
6909 
6910  for( i = 0; i < 2; ++i )
6911  {
6912  var = SCIPexprtreeGetVars(consdata->f)[i];
6913  if( !SCIPisRelEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) )
6914  {
6915  SCIP_CALL( SCIPaddExternBranchCand(scip, var, MAX(consdata->lhsviol, consdata->rhsviol), SCIP_INVALID) );
6916  ++nnotify;
6917  }
6918  }
6919  }
6920 
6921  if( nnotify == 0 )
6922  {
6923  SCIPdebugMessage("All variables in violated constraints fixed (up to epsilon). Cannot find branching candidate. Forcing solution of LP.\n");
6924  *result = SCIP_SOLVELP;
6925  }
6926 
6927  assert(*result == SCIP_SOLVELP || (*result == SCIP_INFEASIBLE && nnotify > 0));
6928  return SCIP_OKAY;
6929 }
6930 
6931 /** feasibility check method of constraint handler for integral solutions */
6932 static
6933 SCIP_DECL_CONSCHECK(consCheckBivariate)
6934 { /*lint --e{715}*/
6935  SCIP_CONSHDLRDATA* conshdlrdata;
6936  SCIP_CONSDATA* consdata;
6937  SCIP_Real maxviol;
6938  int c;
6939  SCIP_Bool maypropfeasible; /* whether we may be able to propose a feasible solution */
6940 
6941  assert(scip != NULL);
6942  assert(conss != NULL || nconss == 0);
6943  assert(sol != NULL);
6944  assert(result != NULL);
6945 
6946  conshdlrdata = SCIPconshdlrGetData(conshdlr);
6947  assert(conshdlrdata != NULL);
6948 
6949  *result = SCIP_FEASIBLE;
6950 
6951  maxviol = 0.0;
6952  maypropfeasible = conshdlrdata->linfeasshift && (conshdlrdata->trysolheur != NULL);
6953  for( c = 0; c < nconss; ++c )
6954  {
6955  assert(conss != NULL);
6956  SCIP_CALL( computeViolation(scip, conshdlr, conss[c], sol) );
6957 
6958  consdata = SCIPconsGetData(conss[c]);
6959  assert(consdata != NULL);
6960 
6961  if( SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) || SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
6962  {
6963  *result = SCIP_INFEASIBLE;
6964  if( printreason )
6965  {
6966  SCIP_CALL( SCIPprintCons(scip, conss[c], NULL) );
6967  SCIPinfoMessage(scip, NULL, ";\n");
6968  {
6969  SCIPinfoMessage(scip, NULL, "violation: left hand side is violated by %.15g (scaled: %.15g)\n", consdata->lhs - consdata->activity, consdata->lhsviol);
6970  }
6971  if( SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
6972  {
6973  SCIPinfoMessage(scip, NULL, "violation: right hand side is violated by %.15g (scaled: %.15g)\n", consdata->activity - consdata->rhs, consdata->rhsviol);
6974  }
6975  }
6976 
6977  if( (conshdlrdata->subnlpheur == NULL || sol == NULL) && !maypropfeasible )
6978  return SCIP_OKAY;
6979 
6980  if( consdata->lhsviol > maxviol || consdata->rhsviol > maxviol )
6981  maxviol = consdata->lhsviol + consdata->rhsviol;
6982 
6983  /* do not try to shift linear variables if activity is at infinity (leads to setting variable to infinity in solution, which is not allowed) */
6984  if( maypropfeasible && SCIPisInfinity(scip, REALABS(consdata->activity)) )
6985  maypropfeasible = FALSE;
6986 
6987  if( maypropfeasible )
6988  {
6989  if( SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) )
6990  {
6991  /* check if the linear variable may help to get the left hand side satisfied
6992  * if not, then we cannot get feasible */
6993  if( !(consdata->mayincreasez && consdata->zcoef > 0.0) && !(consdata->maydecreasez && consdata->zcoef < 0.0) )
6994  maypropfeasible = FALSE;
6995  }
6996  else
6997  {
6998  assert(SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)));
6999  /* check if the linear variable may help to get the right hand side satisfied
7000  * if not, then we cannot get feasible */
7001  if( !(consdata->mayincreasez && consdata->zcoef < 0.0) && !(consdata->maydecreasez && consdata->zcoef > 0.0) )
7002  maypropfeasible = FALSE;
7003  }
7004  }
7005  }
7006  }
7007 
7008  if( *result == SCIP_INFEASIBLE && maypropfeasible )
7009  {
7010  SCIP_Bool success;
7011 
7012  SCIP_CALL( proposeFeasibleSolution(scip, conshdlr, conss, nconss, sol, &success) );
7013 
7014  /* do not pass solution to NLP heuristic if we made it feasible this way */
7015  if( success )
7016  return SCIP_OKAY;
7017  }
7018 
7019  if( *result == SCIP_INFEASIBLE && conshdlrdata->subnlpheur != NULL && sol != NULL )
7020  {
7021  SCIP_CALL( SCIPupdateStartpointHeurSubNlp(scip, conshdlrdata->subnlpheur, sol, maxviol) );
7022  }
7023 
7024  return SCIP_OKAY;
7025 }
7026 
7027 /** domain propagation method of constraint handler */
7028 static
7029 SCIP_DECL_CONSPROP(consPropBivariate)
7030 { /*lint --e{715}*/
7031  int dummy;
7032 
7033  assert(scip != NULL);
7034  assert(conshdlr != NULL);
7035  assert(conss != NULL || nconss == 0);
7036  assert(result != NULL);
7037 
7038  dummy = 0;
7039  SCIP_CALL( propagateBounds(scip, conshdlr, conss, nconss, result, &dummy, &dummy) );
7040 
7041  return SCIP_OKAY;
7042 }
7043 
7044 /** presolving method of constraint handler */
7045 static
7046 SCIP_DECL_CONSPRESOL(consPresolBivariate)
7047 { /*lint --e{715}*/
7048 #ifndef NDEBUG
7049  SCIP_CONSDATA* consdata;
7050 #endif
7051  SCIP_CONSHDLRDATA* conshdlrdata;
7052  SCIP_RESULT propresult;
7053  SCIP_Bool havechange;
7054  SCIP_Bool upgraded;
7055  int c;
7056 
7057  assert(scip != NULL);
7058  assert(conshdlr != NULL);
7059  assert(conss != NULL || nconss == 0);
7060  assert(result != NULL);
7061 
7062  *result = SCIP_DIDNOTFIND;
7063 
7064  conshdlrdata = SCIPconshdlrGetData(conshdlr);
7065  assert(conshdlrdata != NULL);
7066  assert(conshdlrdata->exprgraph != NULL);
7067 
7068  if( !conshdlrdata->isremovedfixings )
7069  {
7070  SCIP_CALL( removeFixedNonlinearVariables(scip, conshdlr) );
7071  assert(conshdlrdata->isremovedfixings);
7072  }
7073  /* @todo call expression graph simplifier, if not done yet? */
7074 
7075  for( c = 0; c < nconss; ++c )
7076  {
7077  assert(conss != NULL);
7078 
7079 #ifndef NDEBUG
7080  consdata = SCIPconsGetData(conss[c]);
7081  assert(consdata != NULL);
7082 #endif
7083 
7084  SCIPdebugMessage("process constraint <%s>\n", SCIPconsGetName(conss[c]));
7085  SCIPdebugPrintCons(scip, conss[c], NULL);
7086 
7087  havechange = FALSE;
7088 
7089  SCIP_CALL( removeFixedVariables(scip, conshdlr, conss[c], &havechange, &upgraded) );
7090  if( upgraded )
7091  {
7092  SCIP_CALL( SCIPdelCons(scip, conss[c]) );
7093  ++*nupgdconss;
7094  continue;
7095  }
7096  if( havechange )
7097  {
7098  SCIPdebugMessage("removed fixed variables -> ");
7099  SCIPdebugPrintCons(scip, conss[c], NULL);
7100  }
7101  }
7102 
7103  /* run domain propagation */
7104  SCIP_CALL( propagateBounds(scip, conshdlr, conss, nconss, &propresult, nchgbds, ndelconss) );
7105  switch( propresult )
7106  {
7107  case SCIP_REDUCEDDOM:
7108  *result = SCIP_SUCCESS;
7109  break;
7110  case SCIP_CUTOFF:
7111  SCIPdebugMessage("propagation says problem is infeasible in presolve\n");
7112  *result = SCIP_CUTOFF;
7113  return SCIP_OKAY;
7114  default:
7115  assert(propresult == SCIP_DIDNOTFIND || propresult == SCIP_DIDNOTRUN);
7116  } /*lint !e788*/
7117 
7118  /* ensure we are called again if we are about to finish, since another presolver may still fix some variable and we cannot remove these fixations in exitpre anymore */
7120  *result = SCIP_DELAYED;
7121 
7122  return SCIP_OKAY;
7123 }
7124 
7125 /** variable rounding lock method of constraint handler */
7126 static
7127 SCIP_DECL_CONSLOCK(consLockBivariate)
7128 { /*lint --e{715}*/
7129  SCIP_CONSDATA* consdata;
7130 
7131  assert(scip != NULL);
7132  assert(cons != NULL);
7133 
7134  consdata = SCIPconsGetData(cons);
7135  assert(consdata != NULL);
7136 
7137  if( consdata->z != NULL )
7138  {
7139  if( consdata->zcoef > 0 )
7140  {
7141  if( !SCIPisInfinity(scip, -consdata->lhs) )
7142  {
7143  SCIP_CALL( SCIPaddVarLocks(scip, consdata->z, nlockspos, nlocksneg) );
7144  }
7145  if( !SCIPisInfinity(scip, consdata->rhs) )
7146  {
7147  SCIP_CALL( SCIPaddVarLocks(scip, consdata->z, nlocksneg, nlockspos) );
7148  }
7149  }
7150  else
7151  {
7152  if( !SCIPisInfinity(scip, -consdata->lhs) )
7153  {
7154  SCIP_CALL( SCIPaddVarLocks(scip, consdata->z, nlocksneg, nlockspos) );
7155  }
7156  if( !SCIPisInfinity(scip, consdata->rhs) )
7157  {
7158  SCIP_CALL( SCIPaddVarLocks(scip, consdata->z, nlockspos, nlocksneg) );
7159  }
7160  }
7161  }
7162 
7163  return SCIP_OKAY;
7164 }
7165 
7166 
7167 /** constraint activation notification method of constraint handler */
7168 static
7169 SCIP_DECL_CONSACTIVE(consActiveBivariate)
7170 { /*lint --e{715}*/
7171  SCIP_CONSHDLRDATA* conshdlrdata;
7172  SCIP_CONSDATA* consdata;
7173  SCIP_Bool exprtreeisnew;
7174 
7175  assert(scip != NULL);
7176  assert(conshdlr != NULL);
7177  assert(cons != NULL);
7178  assert(SCIPconsIsTransformed(cons));
7179 
7180  conshdlrdata = SCIPconshdlrGetData(conshdlr);
7181  assert(conshdlrdata != NULL);
7182  assert(conshdlrdata->exprgraph != NULL);
7183 
7184  consdata = SCIPconsGetData(cons);
7185  assert(consdata != NULL);
7186  assert(consdata->exprgraphnode == NULL);
7187 
7188  SCIPdebugMessage("activate %scons <%s>\n", SCIPconsIsTransformed(cons) ? "transformed " : "", SCIPconsGetName(cons));
7189 
7190  /* add exprtree to expression graph */
7191  SCIP_CALL( SCIPexprgraphAddExprtreeSum(conshdlrdata->exprgraph, 1, &consdata->f, NULL, &consdata->exprgraphnode, &exprtreeisnew) );
7192  assert(consdata->exprgraphnode != NULL);
7193 
7194  /* mark that variables in constraint should not be multiaggregated (bad for bound tightening and branching) */
7195  if( SCIPvarIsActive(SCIPexprtreeGetVars(consdata->f)[0]) )
7196  {
7197  SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, SCIPexprtreeGetVars(consdata->f)[0]) );
7198  }
7199  if( SCIPvarIsActive(SCIPexprtreeGetVars(consdata->f)[1]) )
7200  {
7201  SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, SCIPexprtreeGetVars(consdata->f)[1]) );
7202  }
7203  if( consdata->z != NULL && SCIPvarIsActive(consdata->z) )
7204  {
7205  SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, consdata->z) );
7206  }
7207 
7208  return SCIP_OKAY;
7209 }
7210 
7211 /** constraint deactivation notification method of constraint handler */
7212 static
7213 SCIP_DECL_CONSDEACTIVE(consDeactiveBivariate)
7214 { /*lint --e{715}*/
7215  SCIP_CONSHDLRDATA* conshdlrdata;
7216  SCIP_CONSDATA* consdata;
7217 
7218  assert(scip != NULL);
7219  assert(conshdlr != NULL);
7220  assert(cons != NULL);
7221  assert(SCIPconsIsTransformed(cons));
7222 
7223  conshdlrdata = SCIPconshdlrGetData(conshdlr);
7224  assert(conshdlrdata != NULL);
7225  assert(conshdlrdata->exprgraph != NULL);
7226 
7227  consdata = SCIPconsGetData(cons);
7228  assert(consdata != NULL);
7229  assert(consdata->exprgraphnode != NULL);
7230 
7231  SCIPdebugMessage("deactivate %scons <%s>\n", SCIPconsIsTransformed(cons) ? "transformed " : "", SCIPconsGetName(cons));
7232 
7233  SCIP_CALL( SCIPexprgraphReleaseNode(conshdlrdata->exprgraph, &consdata->exprgraphnode) );
7234 
7235  return SCIP_OKAY;
7236 }
7237 
7238 /** constraint enabling notification method of constraint handler */
7239 static
7240 SCIP_DECL_CONSENABLE(consEnableBivariate)
7241 { /*lint --e{715}*/
7242  SCIP_CONSHDLRDATA* conshdlrdata;
7243  SCIP_CONSDATA* consdata;
7244 
7245  assert(scip != NULL);
7246  assert(conshdlr != NULL);
7247  assert(cons != NULL);
7248  assert(SCIPconsIsTransformed(cons));
7249  assert(SCIPconsIsActive(cons));
7250 
7251  conshdlrdata = SCIPconshdlrGetData(conshdlr);
7252  assert(conshdlrdata != NULL);
7253  assert(conshdlrdata->exprgraph != NULL);
7254 
7255  consdata = SCIPconsGetData(cons);
7256  assert(consdata != NULL);
7257  assert(consdata->exprgraphnode != NULL);
7258 
7259  SCIPdebugMessage("enable %scons <%s>\n", SCIPconsIsTransformed(cons) ? "transformed " : "", SCIPconsGetName(cons));
7260 
7261  /* enable node of expression in expression graph */
7262  SCIPexprgraphEnableNode(conshdlrdata->exprgraph, consdata->exprgraphnode);
7263 
7264  /* enable event catching for linear variables */
7265  SCIP_CALL( catchLinearVarEvents(scip, cons) );
7266 
7267  return SCIP_OKAY;
7268 }
7269 
7270 /** constraint disabling notification method of constraint handler */
7271 static
7272 SCIP_DECL_CONSDISABLE(consDisableBivariate)
7273 { /*lint --e{715}*/
7274  SCIP_CONSHDLRDATA* conshdlrdata;
7275  SCIP_CONSDATA* consdata;
7276 
7277  assert(scip != NULL);
7278  assert(conshdlr != NULL);
7279  assert(cons != NULL);
7280  assert(SCIPconsIsTransformed(cons));
7281 
7282  conshdlrdata = SCIPconshdlrGetData(conshdlr);
7283  assert(conshdlrdata != NULL);
7284  assert(conshdlrdata->exprgraph != NULL);
7285 
7286  consdata = SCIPconsGetData(cons);
7287  assert(consdata != NULL);
7288  assert(consdata->exprgraphnode != NULL);
7289 
7290  SCIPdebugMessage("disable %scons <%s>\n", SCIPconsIsTransformed(cons) ? "transformed " : "", SCIPconsGetName(cons));
7291 
7292  /* disable node of expression in expression graph */
7293  SCIPexprgraphDisableNode(conshdlrdata->exprgraph, consdata->exprgraphnode);
7294 
7295  SCIP_CALL( dropLinearVarEvents(scip, cons) );
7296 
7297  return SCIP_OKAY;
7298 }
7299 
7300 /** constraint display method of constraint handler */
7301 static
7302 SCIP_DECL_CONSPRINT(consPrintBivariate)
7303 { /*lint --e{715}*/
7304  SCIP_CONSDATA* consdata;
7305 
7306  assert(scip != NULL);
7307  assert(cons != NULL);
7308 
7309  consdata = SCIPconsGetData(cons);
7310  assert(consdata != NULL);
7311 
7312  /* print left hand side for ranged rows */
7313  if( !SCIPisInfinity(scip, -consdata->lhs)
7314  && !SCIPisInfinity(scip, consdata->rhs)
7315  && !SCIPisEQ(scip, consdata->lhs, consdata->rhs) )
7316  SCIPinfoMessage(scip, file, "%.15g <= ", consdata->lhs);
7317 
7318  /* print coefficients and variables */
7319  SCIP_CALL( SCIPexprtreePrintWithNames(consdata->f, SCIPgetMessagehdlr(scip), file) );
7320 
7321  if( consdata->z != NULL )
7322  {
7323  SCIPinfoMessage(scip, file, "%+.15g", consdata->zcoef);
7324  SCIP_CALL( SCIPwriteVarName(scip, file, consdata->z, TRUE) );
7325  }
7326 
7327  /* print right hand side */
7328  if( SCIPisEQ(scip, consdata->lhs, consdata->rhs) )
7329  {
7330  SCIPinfoMessage(scip, file, " == %.15g", consdata->rhs);
7331  }
7332  else if( !SCIPisInfinity(scip, consdata->rhs) )
7333  {
7334  SCIPinfoMessage(scip, file, " <= %.15g", consdata->rhs);
7335  }
7336  else if( !SCIPisInfinity(scip, -consdata->lhs) )
7337  {
7338  SCIPinfoMessage(scip, file, " >= %.15g", consdata->lhs);
7339  }
7340  else
7341  {
7342  SCIPinfoMessage(scip, file, " [free]");
7343  }
7344 
7345  /* print convexity type, if known */
7346  switch( consdata->convextype )
7347  {
7348  case SCIP_BIVAR_ALLCONVEX:
7349  SCIPinfoMessage(scip, file, " [allconvex]");
7350  break;
7352  SCIPinfoMessage(scip, file, " [1-convex]");
7353  break;
7355  SCIPinfoMessage(scip, file, " [convex-concave]");
7356  break;
7357  default: ;
7358  } /*lint !e788*/
7359 
7360  return SCIP_OKAY;
7361 }
7362 
7363 /** constraint copying method of constraint handler */
7364 static
7365 SCIP_DECL_CONSCOPY(consCopyBivariate)
7366 { /*lint --e{715}*/
7367  SCIP_CONSDATA* consdata;
7368  SCIP_EXPRTREE* f;
7369  SCIP_VAR* xy[2];
7370  SCIP_VAR* z;
7371 
7372  assert(scip != NULL);
7373  assert(cons != NULL);
7374  assert(sourcescip != NULL);
7375  assert(sourceconshdlr != NULL);
7376  assert(sourcecons != NULL);
7377  assert(varmap != NULL);
7378  assert(valid != NULL);
7379 
7380  consdata = SCIPconsGetData(sourcecons);
7381  assert(consdata != NULL);
7382  assert(consdata->f != NULL);
7383 
7384  *valid = TRUE;
7385 
7386  if( consdata->z != NULL )
7387  {
7388  SCIP_CALL( SCIPgetVarCopy(sourcescip, scip, consdata->z, &z, varmap, consmap, global, valid) );
7389  assert(!*valid || z != NULL);
7390  }
7391  else
7392  z = NULL;
7393 
7394  if( *valid )
7395  {
7396  SCIP_CALL( SCIPgetVarCopy(sourcescip, scip, SCIPexprtreeGetVars(consdata->f)[0], &xy[0], varmap, consmap, global, valid) );
7397  assert(!*valid || xy[0] != NULL);
7398  }
7399 
7400  if( *valid )
7401  {
7402  SCIP_CALL( SCIPgetVarCopy(sourcescip, scip, SCIPexprtreeGetVars(consdata->f)[1], &xy[1], varmap, consmap, global, valid) );
7403  assert(!*valid || xy[1] != NULL);
7404  }
7405 
7406  if( *valid )
7407  {
7408  SCIP_CALL( SCIPexprtreeCopy(SCIPblkmem(scip), &f, consdata->f) );
7409  SCIP_CALL( SCIPexprtreeSetVars(f, 2, xy) );
7410  }
7411  else
7412  f = NULL;
7413 
7414  if( *valid )
7415  {
7416  SCIP_CALL( SCIPcreateConsBivariate(scip, cons, name ? name : SCIPconsGetName(sourcecons),
7417  f, consdata->convextype, z, consdata->zcoef, consdata->lhs, consdata->rhs,
7418  initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, stickingatnode) );
7419  }
7420 
7421  if( f != NULL )
7422  {
7423  SCIP_CALL( SCIPexprtreeFree(&f) );
7424  }
7425 
7426  return SCIP_OKAY;
7427 }
7428 
7429 /** constraint method of constraint handler which returns the variables (if possible) */
7430 static
7431 SCIP_DECL_CONSGETVARS(consGetVarsBivariate)
7432 { /*lint --e{715}*/
7433 
7434  if( varssize < 3 )
7435  (*success) = FALSE;
7436  else
7437  {
7438  SCIP_CONSDATA* consdata;
7439 
7440  assert(cons != NULL);
7441  assert(vars != NULL);
7442 
7443  consdata = SCIPconsGetData(cons);
7444  assert(consdata != NULL);
7445 
7446  vars[0] = SCIPexprtreeGetVars(consdata->f)[0];
7447  vars[1] = SCIPexprtreeGetVars(consdata->f)[1];
7448  vars[2] = consdata->z;
7449  (*success) = TRUE;
7450  }
7451 
7452  return SCIP_OKAY;
7453 }
7454 
7455 /** constraint method of constraint handler which returns the number of variables (if possible) */
7456 static
7457 SCIP_DECL_CONSGETNVARS(consGetNVarsBivariate)
7458 { /*lint --e{715}*/
7459 
7460  (*nvars) = 3;
7461  (*success) = TRUE;
7462 
7463  return SCIP_OKAY;
7464 }
7465 
7466 /*
7467  * Quadratic constraint upgrading
7468  */
7469 
7470 /** tries to upgrade a quadratic constraint into a bivariate constraint */
7471 static
7472 SCIP_DECL_QUADCONSUPGD(quadconsUpgdBivariate)
7473 { /*lint --e{715}*/
7474  SCIP_QUADVARTERM* quadvarterms;
7475  SCIP_BILINTERM* bilinterms;
7476  int nquadvarterms;
7477  int nbilinterms;
7478 
7479  SCIP_VAR* x;
7480  SCIP_VAR* y;
7481 
7482  SCIP_Real coefxx;
7483  SCIP_Real coefxy;
7484  SCIP_Real coefyy;
7485  SCIP_Real coefx;
7486  SCIP_Real coefy;
7487 
7488  SCIP_Real zcoef;
7489  SCIP_VAR* z;
7490 
7491  assert(nupgdconss != NULL);
7492  assert(upgdconss != NULL);
7493 
7494  *nupgdconss = 0;
7495 
7496  /* not interested in univariate case */
7497  if( nbinquad + nintquad + ncontquad < 2 )
7498  return SCIP_OKAY;
7499 
7500  if( SCIPgetNBilinTermsQuadratic(scip, cons) == 0 )
7501  return SCIP_OKAY;
7502 
7503  quadvarterms = SCIPgetQuadVarTermsQuadratic(scip, cons);
7504  nquadvarterms = SCIPgetNQuadVarTermsQuadratic(scip, cons);
7505  bilinterms = SCIPgetBilinTermsQuadratic(scip, cons);
7506  nbilinterms = SCIPgetNBilinTermsQuadratic(scip, cons);
7507 
7508  if( nquadvarterms == 2 && SCIPgetNLinearVarsQuadratic(scip, cons) <= 1 )
7509  {
7510  x = quadvarterms[0].var;
7511  y = quadvarterms[1].var;
7512 
7513  coefxx = quadvarterms[0].sqrcoef;
7514  coefyy = quadvarterms[1].sqrcoef;
7515 
7516  /* only one bilinear term -> not interesting for us */
7517  if( coefxx == 0.0 && coefyy == 0.0 )
7518  return SCIP_OKAY;
7519 
7520  /* two square terms without bilinear term -> also not interesting for us */
7521  if( nbilinterms == 0 )
7522  return SCIP_OKAY;
7523  assert(nbilinterms == 1);
7524 
7525  assert(bilinterms[0].var1 == x || bilinterms[0].var1 == y);
7526  assert(bilinterms[0].var2 == x || bilinterms[0].var2 == y);
7527 
7528  coefxy = bilinterms[0].coef;
7529 
7530  coefx = quadvarterms[0].lincoef;
7531  coefy = quadvarterms[1].lincoef;
7532 
7533  if( SCIPgetNLinearVarsQuadratic(scip, cons) )
7534  {
7535  assert(SCIPgetNLinearVarsQuadratic(scip, cons) == 1);
7536  zcoef = SCIPgetCoefsLinearVarsQuadratic(scip, cons)[0];
7537  z = SCIPgetLinearVarsQuadratic(scip, cons)[0];
7538  }
7539  else
7540  {
7541  z = NULL;
7542  zcoef = 0.0;
7543  }
7544 
7545  if( upgdconsssize < 1 )
7546  {
7547  *nupgdconss = -1;
7548  return SCIP_OKAY;
7549  }
7550 
7551  SCIP_CALL( createConsFromQuadTerm(scip, cons, &upgdconss[0], SCIPconsGetName(cons),
7552  x, y, z, coefxx, coefx, coefyy, coefy, coefxy, zcoef, SCIPgetLhsQuadratic(scip, cons), SCIPgetRhsQuadratic(scip, cons)) );
7553  *nupgdconss = 1;
7554  }
7555  else
7556  {
7557  SCIP_CONS* quadcons;
7558  SCIP_Bool upgdlhs;
7559  SCIP_Bool upgdrhs;
7560  SCIP_Bool keeporig;
7561  SCIP_Bool* marked;
7562  char name[SCIP_MAXSTRLEN];
7563  SCIP_VAR* auxvar;
7564  int xpos;
7565  int ypos;
7566  int pos;
7567  int i;
7568 
7569  /* check if we find at least one bilinear term for which we would create a bivariate constraint
7570  * thus, we search for a variable that has a square term and is involved in at least one bivariate term */
7571  for( i = 0; i < nquadvarterms; ++i )
7572  if( quadvarterms[i].sqrcoef != 0.0 && quadvarterms[i].nadjbilin > 0 )
7573  break;
7574 
7575  /* if nothing found, then don't try upgrade and return */
7576  if( i == nquadvarterms )
7577  return SCIP_OKAY;
7578 
7579  /* check which constraint side we want to upgrade and whether to keep some
7580  * we want to upgrade those that are nonconvex */
7581  SCIP_CALL( SCIPcheckCurvatureQuadratic(scip, cons) );
7582  upgdlhs = FALSE;
7583  upgdrhs = FALSE;
7584  keeporig = FALSE;
7585  if( !SCIPisInfinity(scip, -SCIPgetLhsQuadratic(scip, cons)) )
7586  {
7587  if( SCIPisConcaveQuadratic(scip, cons) )
7588  keeporig = TRUE;
7589  else
7590  upgdlhs = TRUE;
7591  }
7592  if( !SCIPisInfinity(scip, SCIPgetRhsQuadratic(scip, cons)) )
7593  {
7594  if( SCIPisConvexQuadratic(scip, cons) )
7595  keeporig = TRUE;
7596  else
7597  upgdrhs = TRUE;
7598  }
7599 
7600  /* if nothing to upgrade, then return */
7601  if( !upgdlhs && !upgdrhs )
7602  return SCIP_OKAY;
7603 
7604  /* require enough space here already, so we do not create and add aux vars that we cannot get rid of easily later */
7605  if( upgdconsssize < nbilinterms + 1 + (keeporig ? 1 : 0) )
7606  {
7607  *nupgdconss = -(nbilinterms + 1 + (keeporig ? 1 : 0));
7608  return SCIP_OKAY;
7609  }
7610 
7611  /* initial remaining quadratic constraint: take linear part and constraint sides from original constraint */
7612  SCIP_CALL( SCIPcreateConsQuadratic(scip, &quadcons, SCIPconsGetName(cons),
7614  0, NULL, NULL, NULL,
7615  upgdlhs ? SCIPgetLhsQuadratic(scip, cons) : -SCIPinfinity(scip),
7616  upgdrhs ? SCIPgetRhsQuadratic(scip, cons) : SCIPinfinity(scip),
7620 
7621  /* remember for each quadratic variable whether its linear and square part has been moved into a bivariate constraint */
7622  SCIP_CALL( SCIPallocBufferArray(scip, &marked, nquadvarterms) );
7624 
7625  /* @todo what is a good partition of a number of quadratic terms into bivariate terms? */
7626 
7627  /* check for each bilinear term, whether we want to create a bivariate constraint for it and associated square terms */
7628  for( i = 0; i < nbilinterms; ++i )
7629  {
7630  assert(bilinterms[i].coef != 0.0);
7631 
7632  x = bilinterms[i].var1;
7633  y = bilinterms[i].var2;
7634 
7635  SCIP_CALL( SCIPfindQuadVarTermQuadratic(scip, cons, x, &xpos) );
7636  assert(xpos >= 0);
7637  assert(xpos < nquadvarterms);
7638  assert(quadvarterms[xpos].var == x);
7639 
7640  SCIP_CALL( SCIPfindQuadVarTermQuadratic(scip, cons, y, &ypos) );
7641  assert(ypos >= 0);
7642  assert(ypos < nquadvarterms);
7643  assert(quadvarterms[ypos].var == y);
7644 
7645  coefxx = marked[xpos] ? 0.0 : quadvarterms[xpos].sqrcoef;
7646  coefyy = marked[ypos] ? 0.0 : quadvarterms[ypos].sqrcoef;
7647 
7648  /* if there are no square terms, then do not upgrade bilinear term to bivariate constraint
7649  * thus, add bivariate term to quadcons and continue
7650  */
7651  if( coefxx == 0.0 && coefyy == 0.0 )
7652  {
7653  /* check if x and y already are in quadcons and add if not there yet */
7654  SCIP_CALL( SCIPfindQuadVarTermQuadratic(scip, quadcons, x, &pos) );
7655  if( pos == -1 )
7656  {
7657  SCIP_CALL( SCIPaddQuadVarQuadratic(scip, quadcons, x, 0.0, 0.0) );
7658  }
7659  SCIP_CALL( SCIPfindQuadVarTermQuadratic(scip, quadcons, y, &pos) );
7660  if( pos == -1 )
7661  {
7662  SCIP_CALL( SCIPaddQuadVarQuadratic(scip, quadcons, y, 0.0, 0.0) );
7663  }
7664 
7665  SCIP_CALL( SCIPaddBilinTermQuadratic(scip, quadcons, x, y, bilinterms[i].coef) );
7666 
7667  continue;
7668  }
7669 
7670  coefx = marked[xpos] ? 0.0 : quadvarterms[xpos].lincoef;
7671  coefy = marked[ypos] ? 0.0 : quadvarterms[ypos].lincoef;
7672  coefxy = bilinterms[i].coef;
7673 
7674  /* create new auxiliary variable for bilinear quad. term in x and y */
7675  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s_auxvar%d", SCIPconsGetName(cons), *nupgdconss);
7676  SCIP_CALL( SCIPcreateVar(scip, &auxvar, name, -SCIPinfinity(scip), SCIPinfinity(scip), 0.0,
7678  SCIP_CALL( SCIPaddVar(scip, auxvar) );
7679 
7680  /* add 1*auxvar to quadcons */
7681  SCIP_CALL( SCIPaddLinearVarQuadratic(scip, quadcons, auxvar, 1.0) );
7682 
7683  /* create new bivariate constraint */
7684  assert(*nupgdconss < upgdconsssize);
7685  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s_auxcons%d", SCIPconsGetName(cons), *nupgdconss);
7686  SCIP_CALL( createConsFromQuadTerm(scip, cons, &upgdconss[*nupgdconss], name,
7687  x, y, auxvar, coefxx, coefx, coefyy, coefy, coefxy, -1.0,
7688  SCIPisInfinity(scip, -SCIPgetLhsQuadratic(scip, cons)) ? -SCIPinfinity(scip) : 0.0,
7689  SCIPisInfinity(scip, SCIPgetRhsQuadratic(scip, cons)) ? SCIPinfinity(scip) : 0.0) );
7690  ++*nupgdconss;
7691 
7692  /* compute value of auxvar in debug solution */
7693 #ifdef SCIP_DEBUG_SOLUTION
7694  if( SCIPdebugIsMainscip(scip) )
7695  {
7696  SCIP_Real xval;
7697  SCIP_Real yval;
7698  SCIP_CALL( SCIPdebugGetSolVal(scip, x, &xval) );
7699  SCIP_CALL( SCIPdebugGetSolVal(scip, y, &yval) );
7700  SCIP_CALL( SCIPdebugAddSolVal(scip, auxvar, coefxx * xval * xval + coefyy * yval * yval + coefxy * xval * yval + coefx * xval + coefy * yval) );
7701  }
7702 #endif
7703 
7704  SCIP_CALL( SCIPreleaseVar(scip, &auxvar) );
7705 
7706  marked[xpos] = TRUE;
7707  marked[ypos] = TRUE;
7708  }
7709 
7710  if( *nupgdconss == 0 )
7711  {
7712  /* if no constraints created, then forget also quadcons and do no upgrade */
7713  SCIP_CALL( SCIPreleaseCons(scip, &quadcons) );
7714  }
7715  else
7716  {
7717  /* complete quadcons: check for unmarked quadvarterms and add their linear and square coefficients to quadcons */
7718  for( i = 0; i < nquadvarterms; ++i )
7719  {
7720  if( marked[i] )
7721  continue;
7722 
7723  x = quadvarterms[i].var;
7724 
7725  /* check if variable is already in quadcons
7726  * if the variable appears in a bilinear term, then this term should have been added to quadcons above, so the variable is there
7727  */
7728  pos = -1;
7729  if( quadvarterms[i].nadjbilin > 0 )
7730  {
7731  SCIP_CALL( SCIPfindQuadVarTermQuadratic(scip, quadcons, x, &pos) );
7732  }
7733 
7734  /* create new quad var or add existing quad var */
7735  if( quadvarterms[i].sqrcoef != 0.0 )
7736  {
7737  if( pos == -1 )
7738  {
7739  SCIP_CALL( SCIPaddQuadVarQuadratic(scip, quadcons, x, quadvarterms[i].lincoef, quadvarterms[i].sqrcoef) );
7740  }
7741  else
7742  {
7743  SCIP_CALL( SCIPaddSquareCoefQuadratic(scip, quadcons, x, quadvarterms[i].sqrcoef) );
7744  SCIP_CALL( SCIPaddQuadVarLinearCoefQuadratic(scip, quadcons, x, quadvarterms[i].lincoef) );
7745  }
7746  }
7747  else if( quadvarterms[i].lincoef != 0.0 )
7748  {
7749  /* if no square term and no quadratic variable term, then add to linear part */
7750  SCIP_CALL( SCIPaddLinearVarQuadratic(scip, quadcons, x, quadvarterms[i].lincoef) );
7751  }
7752  }
7753 
7754  /* add quadcons to set of upgrade constraints */
7755  assert(*nupgdconss < upgdconsssize);
7756  upgdconss[*nupgdconss] = quadcons;
7757  ++*nupgdconss;
7758 
7759  SCIPdebugPrintCons(scip, quadcons, NULL);
7760 
7761  if( keeporig )
7762  {
7763  assert(*nupgdconss < upgdconsssize);
7764  /* copy of original quadratic constraint with one of the sides relaxed */
7765  SCIP_CALL( SCIPcreateConsQuadratic2(scip, &upgdconss[*nupgdconss], SCIPconsGetName(cons),
7769  upgdlhs ? -SCIPinfinity(scip) : SCIPgetLhsQuadratic(scip, cons),
7770  upgdrhs ? SCIPinfinity(scip) : SCIPgetRhsQuadratic(scip, cons),
7774  ++*nupgdconss;
7775  }
7776  }
7777 
7778  SCIPfreeBufferArray(scip, &marked);
7779  }
7780 
7781  return SCIP_OKAY;
7782 }
7783 
7784 
7785 /*
7786  * Nonlinear constraint upgrading
7787  */
7788 
7789 /** tries to reformulate a expression graph node that is a monomial in two variables */
7790 static
7791 SCIP_DECL_EXPRGRAPHNODEREFORM(exprgraphnodeReformBivariate)
7792 {
7793  SCIP_EXPRDATA_MONOMIAL* monomial;
7794  SCIP_CONS* cons;
7795  SCIP_VAR* auxvar;
7796  char name[SCIP_MAXSTRLEN];
7797  SCIP_VAR* x;
7798  SCIP_VAR* y;
7799  SCIP_Real expx;
7800  SCIP_Real expy;
7801 
7802  assert(scip != NULL);
7803  assert(exprgraph != NULL);
7804  assert(node != NULL);
7805  assert(naddcons != NULL);
7806  assert(reformnode != NULL);
7807 
7808  *reformnode = NULL;
7809 
7810  /* could also upgrade bivariate quadratic, but if we don't then node will appear in cons_quadratic later, from which we also upgrade...
7811  * @todo could also upgrade x/y from EXPR_DIV */
7813  return SCIP_OKAY;
7814 
7815  /* sums of monomials are split up by reformulation, so wait that this happened */
7817  return SCIP_OKAY;
7818 
7819  /* we are only interested in monomials that are not convex or concave, since cons_nonlinear can handle these the same was as we do */
7821  return SCIP_OKAY;
7822 
7823  monomial = SCIPexprgraphGetNodePolynomialMonomials(node)[0];
7824  assert(monomial != NULL);
7825 
7826  /* @todo we could also do some more complex reformulation for n-variate monomials, something better than what reformMonomial in cons_nonlinear is doing */
7827  if( SCIPexprGetMonomialNFactors(monomial) != 2 )
7828  return SCIP_OKAY;
7829  assert(SCIPexprgraphGetNodeNChildren(node) == 2);
7830 
7831  expx = SCIPexprGetMonomialExponents(monomial)[0];
7832  expy = SCIPexprGetMonomialExponents(monomial)[1];
7833 
7834  /* no interest in upgrading x*y -> let cons_quadratic do this */
7835  if( SCIPisEQ(scip, expx, 1.0) && SCIPisEQ(scip, expy, 1.0) )
7836  return SCIP_OKAY;
7837 
7838  /* so far only support variables as arguments @todo could allow more here, e.g., f(x)^pg(y)^q */
7841  return SCIP_OKAY;
7842 
7845  assert(x != y);
7846 
7847  /* so far only allow positive x and y @todo could also allow x<0 or y<0 */
7849  return SCIP_OKAY;
7850 
7851  SCIPdebugMessage("reformulate bivariate monomial in node %p\n", (void*)node);
7852 
7853  /* create auxiliary variable */
7854  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "nlreform%dbv", *naddcons);
7855  SCIP_CALL( SCIPcreateVar(scip, &auxvar, name, SCIPexprgraphGetNodeBounds(node).inf, SCIPexprgraphGetNodeBounds(node).sup,
7857  SCIP_CALL( SCIPaddVar(scip, auxvar) );
7858 
7859  /* create bivariate constraint */
7860  SCIP_CALL( createConsFromMonomial(scip, NULL, &cons, name, x, y, auxvar,
7862  SCIP_CALL( SCIPaddCons(scip, cons) );
7863  SCIPdebugPrintCons(scip, cons, NULL);
7864  SCIP_CALL( SCIPreleaseCons(scip, &cons) );
7865  ++*naddcons;
7866 
7867  /* add auxvar to exprgraph and return it in reformnode */
7868  SCIP_CALL( SCIPexprgraphAddVars(exprgraph, 1, (void**)&auxvar, reformnode) );
7869 
7870  /* set value of auxvar and reformnode in debug solution */
7871 #ifdef SCIP_DEBUG_SOLUTION
7872  if( SCIPdebugIsMainscip(scip) )
7873  {
7874  SCIPdebugAddSolVal(scip, auxvar, SCIPexprgraphGetNodeVal(node));
7876  }
7877 #endif
7878 
7879  SCIP_CALL( SCIPreleaseVar(scip, &auxvar) );
7880 
7881  return SCIP_OKAY;
7882 }
7883 
7884 /*
7885  * constraint specific interface methods
7886  */
7887 
7888 /** creates the handler for bivariate constraints and includes it in SCIP */
7890  SCIP* scip /**< SCIP data structure */
7891  )
7892 {
7893  SCIP_CONSHDLRDATA* conshdlrdata;
7894  SCIP_CONSHDLR* conshdlr;
7895 
7896  /* create bivariate constraint handler data */
7897  SCIP_CALL( SCIPallocMemory(scip, &conshdlrdata) );
7898  BMSclearMemory(conshdlrdata);
7899 
7900  /* include constraint handler */
7903  consEnfolpBivariate, consEnfopsBivariate, consCheckBivariate, consLockBivariate,
7904  conshdlrdata) );
7905 
7906  assert(conshdlr != NULL);
7907 
7908  /* set non-fundamental callbacks via specific setter functions */
7909  SCIP_CALL( SCIPsetConshdlrActive(scip, conshdlr, consActiveBivariate) );
7910  SCIP_CALL( SCIPsetConshdlrCopy(scip, conshdlr, conshdlrCopyBivariate, consCopyBivariate) );
7911  SCIP_CALL( SCIPsetConshdlrDeactive(scip, conshdlr, consDeactiveBivariate) );
7912  SCIP_CALL( SCIPsetConshdlrDelete(scip, conshdlr, consDeleteBivariate) );
7913  SCIP_CALL( SCIPsetConshdlrDisable(scip, conshdlr, consDisableBivariate) );
7914  SCIP_CALL( SCIPsetConshdlrEnable(scip, conshdlr, consEnableBivariate) );
7915  SCIP_CALL( SCIPsetConshdlrExit(scip, conshdlr, consExitBivariate) );
7916  SCIP_CALL( SCIPsetConshdlrExitpre(scip, conshdlr, consExitpreBivariate) );
7917  SCIP_CALL( SCIPsetConshdlrExitsol(scip, conshdlr, consExitsolBivariate) );
7918  SCIP_CALL( SCIPsetConshdlrFree(scip, conshdlr, consFreeBivariate) );
7919  SCIP_CALL( SCIPsetConshdlrGetVars(scip, conshdlr, consGetVarsBivariate) );
7920  SCIP_CALL( SCIPsetConshdlrGetNVars(scip, conshdlr, consGetNVarsBivariate) );
7921  SCIP_CALL( SCIPsetConshdlrInit(scip, conshdlr, consInitBivariate) );
7922  SCIP_CALL( SCIPsetConshdlrInitpre(scip, conshdlr, consInitpreBivariate) );
7923  SCIP_CALL( SCIPsetConshdlrInitsol(scip, conshdlr, consInitsolBivariate) );
7924  SCIP_CALL( SCIPsetConshdlrInitlp(scip, conshdlr, consInitlpBivariate) );
7925  SCIP_CALL( SCIPsetConshdlrPresol(scip, conshdlr, consPresolBivariate, CONSHDLR_MAXPREROUNDS, CONSHDLR_DELAYPRESOL) );
7926  SCIP_CALL( SCIPsetConshdlrPrint(scip, conshdlr, consPrintBivariate) );
7927  SCIP_CALL( SCIPsetConshdlrProp(scip, conshdlr, consPropBivariate, CONSHDLR_PROPFREQ, CONSHDLR_DELAYPROP,
7929  SCIP_CALL( SCIPsetConshdlrSepa(scip, conshdlr, consSepalpBivariate, consSepasolBivariate, CONSHDLR_SEPAFREQ,
7931  SCIP_CALL( SCIPsetConshdlrTrans(scip, conshdlr, consTransBivariate) );
7932 
7933  /* include the quadratic constraint upgrade in the quadratic constraint handler */
7935 
7936  /* include the quadratic constraint upgrade in the quadratic constraint handler */
7937  SCIP_CALL( SCIPincludeNonlinconsUpgrade(scip, NULL, exprgraphnodeReformBivariate, NONLINCONSUPGD_PRIORITY, FALSE, CONSHDLR_NAME) );
7938 
7939  /* add bivariate constraint handler parameters */
7940  SCIP_CALL( SCIPaddRealParam(scip, "constraints/"CONSHDLR_NAME"/minefficacysepa",
7941  "minimal efficacy for a cut to be added to the LP during separation; overwrites separating/efficacy",
7942  &conshdlrdata->mincutefficacysepa, FALSE, 0.0001, 0.0, SCIPinfinity(scip), NULL, NULL) );
7943 
7944  SCIP_CALL( SCIPaddRealParam(scip, "constraints/"CONSHDLR_NAME"/minefficacyenfo",
7945  "minimal target efficacy of a cut in order to add it to relaxation during enforcement (may be ignored)",
7946  &conshdlrdata->mincutefficacyenfo, FALSE, 2.0*SCIPfeastol(scip), 0.0, SCIPinfinity(scip), NULL, NULL) );
7947 
7948  SCIP_CALL( SCIPaddRealParam(scip, "constraints/"CONSHDLR_NAME"/cutmaxrange",
7949  "maximal coef range of a cut (maximal coefficient divided by minimal coefficient) in order to be added to LP relaxation",
7950  &conshdlrdata->cutmaxrange, TRUE, 1e+7, 0.0, SCIPinfinity(scip), NULL, NULL) );
7951 
7952  SCIP_CALL( SCIPaddBoolParam(scip, "constraints/"CONSHDLR_NAME"/linfeasshift",
7953  "whether to try to make solutions in check function feasible by shifting a linear variable (esp. useful if constraint was actually objective function)",
7954  &conshdlrdata->linfeasshift, FALSE, TRUE, NULL, NULL) );
7955 
7956  SCIP_CALL( SCIPaddIntParam(scip, "constraints/"CONSHDLR_NAME"/maxproprounds",
7957  "limit on number of propagation rounds for a single constraint within one round of SCIP propagation",
7958  &conshdlrdata->maxproprounds, FALSE, 1, 0, INT_MAX, NULL, NULL) );
7959 
7960  SCIP_CALL( SCIPaddIntParam(scip, "constraints/"CONSHDLR_NAME"/ninitlprefpoints",
7961  "number of reference points in each direction where to compute linear support for envelope in LP initialization",
7962  &conshdlrdata->ninitlprefpoints, FALSE, 3, 0, INT_MAX, NULL, NULL) );
7963 
7964  SCIP_CALL( SCIPaddBoolParam(scip, "constraints/"CONSHDLR_NAME"/enfocutsremovable",
7965  "are cuts added during enforcement removable from the LP in the same node?",
7966  &conshdlrdata->enfocutsremovable, TRUE, FALSE, NULL, NULL) );
7967 
7968  SCIP_CALL( SCIPaddCharParam(scip, "constraints/"CONSHDLR_NAME"/scaling",
7969  "whether scaling of infeasibility is 'o'ff, by sup-norm of function 'g'radient, or by left/right hand 's'ide",
7970  &conshdlrdata->scaling, TRUE, 'o', "ogs", NULL, NULL) );
7971 
7972  conshdlrdata->linvareventhdlr = NULL;
7973  SCIP_CALL( SCIPincludeEventhdlrBasic(scip, &(conshdlrdata->linvareventhdlr), CONSHDLR_NAME"_boundchange", "signals a bound tightening in a linear variable to a bivariate constraint",
7974  processLinearVarEvent, NULL) );
7975  assert(conshdlrdata->linvareventhdlr != NULL);
7976 
7977  conshdlrdata->nonlinvareventhdlr = NULL;
7978  SCIP_CALL( SCIPincludeEventhdlrBasic(scip, &(conshdlrdata->nonlinvareventhdlr), CONSHDLR_NAME"_boundchange2", "signals a bound change in a nonlinear variable to the bivariate constraint handler",
7979  processNonlinearVarEvent, (SCIP_EVENTHDLRDATA*)conshdlrdata) );
7980  assert(conshdlrdata->nonlinvareventhdlr != NULL);
7981 
7982  SCIP_CALL( SCIPincludeEventhdlrBasic(scip, NULL, CONSHDLR_NAME"_newsolution", "handles the event that a new primal solution has been found",
7983  processNewSolutionEvent, NULL) );
7984 
7985  /* create expression interpreter */
7986  SCIP_CALL( SCIPexprintCreate(SCIPblkmem(scip), &conshdlrdata->exprinterpreter) );
7987 
7988  /* create expression graph */
7989  SCIP_CALL( SCIPexprgraphCreate(SCIPblkmem(scip), &conshdlrdata->exprgraph, -1, -1,
7990  exprgraphVarAdded, exprgraphVarRemove, NULL, (void*)conshdlrdata) );
7991  conshdlrdata->isremovedfixings = TRUE;
7992  conshdlrdata->ispropagated = TRUE;
7993 
7994  conshdlrdata->scip = scip;
7995 
7996  return SCIP_OKAY;
7997 }
7998 
7999 /** creates and captures a bivariate constraint
8000  *
8001  * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
8002  */
8004  SCIP* scip, /**< SCIP data structure */
8005  SCIP_CONS** cons, /**< pointer to hold the created constraint */
8006  const char* name, /**< name of constraint */
8007  SCIP_EXPRTREE* f, /**< expression tree specifying bivariate function f(x,y) */
8008  SCIP_BIVAR_CONVEXITY convextype, /**< kind of convexity of f(x,y) */
8009  SCIP_VAR* z, /**< linear variable in constraint */
8010  SCIP_Real zcoef, /**< coefficient of linear variable */
8011  SCIP_Real lhs, /**< left hand side of constraint */
8012  SCIP_Real rhs, /**< right hand side of constraint */
8013  SCIP_Bool initial, /**< should the LP relaxation of constraint be in the initial LP?
8014  * Usually set to TRUE. Set to FALSE for 'lazy constraints'. */
8015  SCIP_Bool separate, /**< should the constraint be separated during LP processing?
8016  * Usually set to TRUE. */
8017  SCIP_Bool enforce, /**< should the constraint be enforced during node processing?
8018  * TRUE for model constraints, FALSE for additional, redundant constraints. */
8019  SCIP_Bool check, /**< should the constraint be checked for feasibility?
8020  * TRUE for model constraints, FALSE for additional, redundant constraints. */
8021  SCIP_Bool propagate, /**< should the constraint be propagated during node processing?
8022  * Usually set to TRUE. */
8023  SCIP_Bool local, /**< is constraint only valid locally?
8024  * Usually set to FALSE. Has to be set to TRUE, e.g., for branching constraints. */
8025  SCIP_Bool modifiable, /**< is constraint modifiable (subject to column generation)?
8026  * Usually set to FALSE. In column generation applications, set to TRUE if pricing
8027  * adds coefficients to this constraint. */
8028  SCIP_Bool dynamic, /**< is constraint subject to aging?
8029  * Usually set to FALSE. Set to TRUE for own cuts which
8030  * are seperated as constraints. */
8031  SCIP_Bool removable, /**< should the relaxation be removed from the LP due to aging or cleanup?
8032  * Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */
8033  SCIP_Bool stickingatnode /**< should the constraint always be kept at the node where it was added, even
8034  * if it may be moved to a more global node?
8035  * Usually set to FALSE. Set to TRUE to for constraints that represent node data. */
8036  )
8037 {
8038  SCIP_CONSHDLR* conshdlr;
8039  SCIP_CONSDATA* consdata;
8040 
8041  assert(f != NULL);
8042  assert(!SCIPisInfinity(scip, REALABS(zcoef)));
8043  assert(modifiable == FALSE); /* we do not support column generation */
8044 
8045  /* find the bivariate constraint handler */
8046  conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
8047  if( conshdlr == NULL )
8048  {
8049  SCIPerrorMessage("bivariate constraint handler not found\n");
8050  return SCIP_PLUGINNOTFOUND;
8051  }
8052 
8053  /* create constraint data */
8054  SCIP_CALL( SCIPallocMemory(scip, &consdata) );
8055  BMSclearMemory(consdata);
8056 
8057  SCIP_CALL( SCIPexprtreeCopy(SCIPblkmem(scip), &consdata->f, f) );
8058  consdata->convextype = convextype;
8059  consdata->z = z;
8060  consdata->zcoef = zcoef;
8061  consdata->lhs = lhs;
8062  consdata->rhs = rhs;
8063 
8064  assert(SCIPexprtreeGetNVars(consdata->f) == 2);
8065  assert(SCIPexprtreeGetVars(consdata->f) != NULL);
8066  assert(SCIPexprtreeGetVars(consdata->f)[0] != NULL);
8067  assert(SCIPexprtreeGetVars(consdata->f)[1] != NULL);
8068 
8069  /* mark that variable events are not catched so far */
8070  consdata->eventfilterpos = -1;
8071 
8072  /* create constraint */
8073  SCIP_CALL( SCIPcreateCons(scip, cons, name, conshdlr, consdata, initial, separate, enforce, check, propagate,
8074  local, modifiable, dynamic, removable, stickingatnode) );
8075 
8076  return SCIP_OKAY;
8077 }
8078 
8079 /** creates and captures an absolute power constraint
8080  * in its most basic version, i. e., all constraint flags are set to their basic value as explained for the
8081  * method SCIPcreateConsBivariate(); all flags can be set via SCIPsetConsFLAGNAME-methods in scip.h
8082  *
8083  * @see SCIPcreateConsBivariate() for information about the basic constraint flag configuration
8084  *
8085  * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
8086  */
8088  SCIP* scip, /**< SCIP data structure */
8089  SCIP_CONS** cons, /**< pointer to hold the created constraint */
8090  const char* name, /**< name of constraint */
8091  SCIP_EXPRTREE* f, /**< expression tree specifying bivariate function f(x,y) */
8092  SCIP_BIVAR_CONVEXITY convextype, /**< kind of convexity of f(x,y) */
8093  SCIP_VAR* z, /**< linear variable in constraint */
8094  SCIP_Real zcoef, /**< coefficient of linear variable */
8095  SCIP_Real lhs, /**< left hand side of constraint */
8096  SCIP_Real rhs /**< right hand side of constraint */
8097  )
8098 {
8099  assert(scip != NULL);
8100 
8101  SCIP_CALL( SCIPcreateConsBivariate(scip, cons, name, f, convextype, z, zcoef, lhs, rhs,
8102  TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
8103 
8104  return SCIP_OKAY;
8105 }
8106 
8107 /** gets the linear variable of a bivariate constraint, or NULL if no such variable */
8109  SCIP* scip, /**< SCIP data structure */
8110  SCIP_CONS* cons /**< constraint */
8111  )
8112 {
8113  assert(cons != NULL);
8114  assert(SCIPconsGetData(cons) != NULL);
8115 
8116  return SCIPconsGetData(cons)->z;
8117 }
8118 
8119 /** gets the coefficients of the linear variable of a bivariate constraint */
8121  SCIP* scip, /**< SCIP data structure */
8122  SCIP_CONS* cons /**< constraint */
8123  )
8124 {
8125  assert(cons != NULL);
8126  assert(SCIPconsGetData(cons) != NULL);
8127 
8128  return SCIPconsGetData(cons)->zcoef;
8129 }
8130 
8131 /** gets the expression tree of a bivariate constraint */
8133  SCIP* scip, /**< SCIP data structure */
8134  SCIP_CONS* cons /**< constraint */
8135  )
8136 {
8137  assert(cons != NULL);
8138  assert(SCIPconsGetData(cons) != NULL);
8139 
8140  return SCIPconsGetData(cons)->f;
8141 }
8142 
8143 /** gets the left hand side of a bivariate constraint */
8145  SCIP* scip, /**< SCIP data structure */
8146  SCIP_CONS* cons /**< constraint */
8147  )
8148 {
8149  assert(cons != NULL);
8150  assert(SCIPconsGetData(cons) != NULL);
8151 
8152  return SCIPconsGetData(cons)->lhs;
8153 }
8154 
8155 /** gets the right hand side of a bivariate constraint */
8157  SCIP* scip, /**< SCIP data structure */
8158  SCIP_CONS* cons /**< constraint */
8159  )
8160 {
8161  assert(cons != NULL);
8162  assert(SCIPconsGetData(cons) != NULL);
8163 
8164  return SCIPconsGetData(cons)->rhs;
8165 }
8166