1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /*                                                                           */
3 /*                  This file is part of the program and library             */
4 /*         SCIP --- Solving Constraint Integer Programs                      */
5 /*                                                                           */
6 /*    Copyright (C) 2002-2021 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 visit scipopt.org.         */
13 /*                                                                           */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15 
16 /**@file   cons_nonlinear.c
17  * @ingroup DEFPLUGINS_CONS
18  * @brief  constraint handler for nonlinear constraints \f$\textrm{lhs} \leq \sum_{i=1}^n a_ix_i + \sum_{j=1}^m c_jf_j(x) \leq \textrm{rhs}\f$
19  * @author Stefan Vigerske
20  * @author Ingmar Vierhaus (consparse)
21  */
22 
23 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
24 
25 #include "blockmemshell/memory.h"
26 #include <ctype.h>
27 #include "lpi/lpi.h"
28 #include "lpi/type_lpi.h"
29 #include "nlpi/exprinterpret.h"
30 #include "nlpi/nlpi_ipopt.h"
31 #include "nlpi/pub_expr.h"
32 #include "nlpi/type_exprinterpret.h"
33 #include "nlpi/type_nlpi.h"
34 #include "scip/cons_linear.h"
35 #include "scip/cons_nonlinear.h"
36 #define SCIP_PRIVATE_ROWPREP
37 #include "scip/cons_quadratic.h"  /* for SCIP_ROWPREP */
38 #include "scip/debug.h"
39 #include "scip/heur_subnlp.h"
40 #include "scip/heur_trysol.h"
41 #include "scip/intervalarith.h"
42 #include "scip/pub_cons.h"
43 #include "scip/pub_event.h"
44 #include "scip/pub_heur.h"
45 #include "scip/pub_lp.h"
46 #include "scip/pub_message.h"
47 #include "scip/pub_misc.h"
48 #include "scip/pub_misc_sort.h"
49 #include "scip/pub_nlp.h"
50 #include "scip/pub_sol.h"
51 #include "scip/pub_tree.h"
52 #include "scip/pub_var.h"
53 #include "scip/scip_branch.h"
54 #include "scip/scip_cons.h"
55 #include "scip/scip_copy.h"
56 #include "scip/scip_cut.h"
57 #include "scip/scip_event.h"
58 #include "scip/scip_expr.h"
59 #include "scip/scip_general.h"
60 #include "scip/scip_heur.h"
61 #include "scip/scip_lp.h"
62 #include "scip/scip_mem.h"
63 #include "scip/scip_message.h"
64 #include "scip/scip_nlp.h"
65 #include "scip/scip_numerics.h"
66 #include "scip/scip_param.h"
67 #include "scip/scip_prob.h"
68 #include "scip/scip_probing.h"
69 #include "scip/scip_sepa.h"
70 #include "scip/scip_sol.h"
71 #include "scip/scip_solvingstats.h"
72 #include "scip/scip_tree.h"
73 #include "scip/scip_var.h"
74 #include <string.h>
75 
76 /* Inform compiler that this code accesses the floating-point environment, so that
77  * certain optimizations should be omitted (http://www.cplusplus.com/reference/cfenv/FENV_ACCESS/).
78  * Not supported by Clang (gives warning) and GCC (silently), at the moment.
79  */
80 #if defined(__INTEL_COMPILER) || defined(_MSC_VER)
81 #pragma fenv_access (on)
82 #elif defined __GNUC__
83 #pragma STDC FENV_ACCESS ON
84 #endif
85 
86 /* constraint handler properties */
87 #define CONSHDLR_NAME          "nonlinear"
88 #define CONSHDLR_DESC          "constraint handler for nonlinear constraints"
89 #define CONSHDLR_SEPAPRIORITY        10 /**< priority of the constraint handler for separation */
90 #define CONSHDLR_ENFOPRIORITY       -60 /**< priority of the constraint handler for constraint enforcing */
91 #define CONSHDLR_CHECKPRIORITY -4000010 /**< priority of the constraint handler for checking feasibility */
92 #define CONSHDLR_SEPAFREQ             1 /**< frequency for separating cuts; zero means to separate only in the root node */
93 #define CONSHDLR_PROPFREQ             1 /**< frequency for propagating domains; zero means only preprocessing propagation */
94 #define CONSHDLR_EAGERFREQ          100 /**< frequency for using all instead of only the useful constraints in separation,
95                                          *   propagation and enforcement, -1 for no eager evaluations, 0 for first only */
96 #define CONSHDLR_MAXPREROUNDS        -1 /**< maximal number of presolving rounds the constraint handler participates in (-1: no limit) */
97 #define CONSHDLR_DELAYSEPA        FALSE /**< should separation method be delayed, if other separators found cuts? */
98 #define CONSHDLR_DELAYPROP        FALSE /**< should propagation method be delayed, if other propagators found reductions? */
99 #define CONSHDLR_NEEDSCONS         TRUE /**< should the constraint handler be skipped, if no constraints are available? */
100 
101 #define CONSHDLR_PROP_TIMING             SCIP_PROPTIMING_BEFORELP /**< propagation timing mask of the constraint handler */
102 #define CONSHDLR_PRESOLTIMING            SCIP_PRESOLTIMING_ALWAYS /**< presolving timing of the constraint handler (fast, medium, or exhaustive) */
103 
104 #define INTERVALINFTY             1E+43 /**< value for infinity in interval operations */
105 #define BOUNDTIGHTENING_MINSTRENGTH 0.05/**< minimal required bound tightening strength in expression graph domain tightening for propagating bound change */
106 #define INITLPMAXVARVAL          1000.0 /**< maximal absolute value of variable for still generating a linearization cut at that point in initlp */
107 
108 /*
109  * Data structures
110  */
111 
112 /** event data for linear variable bound change events */
113 struct LinVarEventData
114 {
115    SCIP_CONSHDLRDATA*    conshdlrdata;       /**< the constraint handler data */
116    SCIP_CONS*            cons;               /**< the constraint */
117    int                   varidx;             /**< the index of the linear variable which bound change is catched */
118    int                   filterpos;          /**< position of eventdata in SCIP's event filter */
119 };
120 typedef struct LinVarEventData LINVAREVENTDATA;
121 
122 /** constraint data for nonlinear constraints */
123 struct SCIP_ConsData
124 {
125    SCIP_Real             lhs;                /**< left hand side of constraint */
126    SCIP_Real             rhs;                /**< right hand side of constraint */
127 
128    int                   nlinvars;           /**< number of linear variables */
129    int                   linvarssize;        /**< length of linear variable arrays */
130    SCIP_VAR**            linvars;            /**< linear variables */
131    SCIP_Real*            lincoefs;           /**< coefficients of linear variables */
132    LINVAREVENTDATA**     lineventdata;       /**< eventdata for bound change of linear variable */
133 
134    int                   nexprtrees;         /**< number of expression trees */
135    SCIP_Real*            nonlincoefs;        /**< coefficients of expression trees */
136    SCIP_EXPRTREE**       exprtrees;          /**< nonlinear part of constraint */
137    SCIP_EXPRCURV*        curvatures;         /**< curvature of each expression tree (taking nonlincoefs into account) */
138    SCIP_EXPRGRAPHNODE*   exprgraphnode;      /**< node in expression graph corresponding to expression tree of this constraint */
139    SCIP_EXPRCURV         curvature;          /**< curvature of complete nonlinear part, if checked */
140 
141    SCIP_NLROW*           nlrow;              /**< a nonlinear row representation of this constraint */
142 
143    unsigned int          linvarssorted:1;    /**< are the linear variables already sorted? */
144    unsigned int          linvarsmerged:1;    /**< are equal linear variables already merged? */
145 
146    unsigned int          iscurvchecked:1;    /**< is nonlinear function checked on convexity or concavity ? */
147    unsigned int          isremovedfixingslin:1; /**< did we removed fixed/aggr/multiaggr variables in linear part? */
148    unsigned int          ispresolved:1;      /**< did we checked for possibilities of upgrading or implicit integer variables? */
149    unsigned int          forcebackprop:1;    /**< should we force to run the backward propagation on our subgraph in the exprgraph? */
150 
151    SCIP_Real             minlinactivity;     /**< sum of minimal activities of all linear terms with finite minimal activity */
152    SCIP_Real             maxlinactivity;     /**< sum of maximal activities of all linear terms with finite maximal activity */
153    int                   minlinactivityinf;  /**< number of linear terms with infinite minimal activity */
154    int                   maxlinactivityinf;  /**< number of linear terms with infinity maximal activity */
155    SCIP_Real             activity;           /**< activity of constraint function w.r.t. current solution */
156    SCIP_Real             lhsviol;            /**< violation of lower bound by current solution (used temporarily inside constraint handler) */
157    SCIP_Real             rhsviol;            /**< violation of lower bound by current solution (used temporarily inside constraint handler) */
158 
159    int                   linvar_maydecrease; /**< index of a variable in linvars that may be decreased without making any other constraint infeasible, or -1 if none */
160    int                   linvar_mayincrease; /**< index of a variable in linvars that may be increased without making any other constraint infeasible, or -1 if none */
161 
162    SCIP_Real             lincoefsmin;        /**< maximal absolute value of coefficients in linear part, only available in solving stage */
163    SCIP_Real             lincoefsmax;        /**< minimal absolute value of coefficients in linear part, only available in solving stage */
164    unsigned int          ncuts;              /**< number of cuts created for this constraint so far */
165 };
166 
167 /** nonlinear constraint update method */
168 struct SCIP_NlConsUpgrade
169 {
170    SCIP_DECL_NONLINCONSUPGD((*nlconsupgd));  /**< method to call for upgrading nonlinear constraint */
171    SCIP_DECL_EXPRGRAPHNODEREFORM((*nodereform));/**< method to call for reformulating an expression graph node */
172    int                   priority;           /**< priority of upgrading method */
173    SCIP_Bool             active;             /**< is upgrading enabled */
174 };
175 typedef struct SCIP_NlConsUpgrade SCIP_NLCONSUPGRADE;
176 
177 /** constraint handler data */
178 struct SCIP_ConshdlrData
179 {
180    SCIP_EXPRINT*         exprinterpreter;    /**< expression interpreter to compute gradients */
181 
182    SCIP_Real             cutmaxrange;        /**< maximal range (maximal coef / minimal coef) of a cut in order to be added to LP */
183    SCIP_Bool             linfeasshift;       /**< whether to make solutions in check feasible if possible */
184    SCIP_Bool             assumeconvex;       /**< whether functions in inequalities should be assumed to be convex */
185    int                   maxproprounds;      /**< limit on number of propagation rounds for a single constraint within one round of SCIP propagation */
186    SCIP_Bool             reformulate;        /**< whether to reformulate expression graph */
187    int                   maxexpansionexponent;/**< maximal exponent where still expanding non-monomial polynomials in expression simplification */
188    SCIP_Real             sepanlpmincont;     /**< minimal required fraction of continuous variables in problem to use solution of NLP relaxation in root for separation */
189    SCIP_Bool             enfocutsremovable;  /**< are cuts added during enforcement removable from the LP in the same node? */
190 
191    SCIP_HEUR*            subnlpheur;         /**< a pointer to the subNLP heuristic, if available */
192    SCIP_HEUR*            trysolheur;         /**< a pointer to the TRYSOL heuristic, if available */
193    SCIP_EVENTHDLR*       linvareventhdlr;    /**< our handler for linear variable bound change events */
194    SCIP_EVENTHDLR*       nonlinvareventhdlr; /**< our handler for nonlinear variable bound change events */
195    int                   newsoleventfilterpos;/**< filter position of new solution event handler, if catched */
196 
197    SCIP_NLCONSUPGRADE**  nlconsupgrades;     /**< nonlinear constraint upgrade methods for specializing nonlinear constraints */
198    int                   nlconsupgradessize; /**< size of nlconsupgrade array */
199    int                   nnlconsupgrades;    /**< number of nonlinear constraint upgrade methods */
200 
201    SCIP_EXPRGRAPH*       exprgraph;          /**< expression graph */
202    SCIP*                 scip;               /**< SCIP pointer for use in expression graph callbacks */
203    unsigned int          isremovedfixings:1; /**< have fixed variables been removed in the expression graph? */
204    unsigned int          ispropagated:1;     /**< have current bounds of linear variables in constraints and variables in expression graph been propagated? */
205    unsigned int          isreformulated:1;   /**< has expression graph been reformulated? */
206    unsigned int          sepanlp:1;          /**< has a linearization in the NLP relaxation been added? */
207    int                   naddedreformconss;  /**< number of constraints added via reformulation */
208    SCIP_NODE*            lastenfonode;       /**< the node for which enforcement was called the last time (and some constraint was violated) */
209    int                   nenforounds;        /**< counter on number of enforcement rounds for the current node */
210 };
211 
212 /*
213  * Local methods
214  */
215 
216 /** translate from one value of infinity to another
217  *
218  *  if val is >= infty1, then give infty2, else give val
219  */
220 #define infty2infty(infty1, infty2, val) ((val) >= (infty1) ? (infty2) : (val))
221 
222 /* catches variable bound change events on a linear variable in a nonlinear constraint */
223 static
catchLinearVarEvents(SCIP * scip,SCIP_CONS * cons,int linvarpos)224 SCIP_RETCODE catchLinearVarEvents(
225    SCIP*                 scip,               /**< SCIP data structure */
226    SCIP_CONS*            cons,               /**< constraint for which to catch bound change events */
227    int                   linvarpos           /**< position of variable in linear variables array */
228    )
229 {
230    SCIP_CONSHDLRDATA* conshdlrdata;
231    SCIP_CONSDATA* consdata;
232    LINVAREVENTDATA* eventdata;
233    SCIP_EVENTTYPE eventtype;
234 
235    assert(scip != NULL);
236    assert(cons != NULL);
237    assert(SCIPconsIsEnabled(cons));
238    assert(SCIPconsIsTransformed(cons));
239 
240    assert(SCIPconsGetHdlr(cons) != NULL);
241    conshdlrdata = SCIPconshdlrGetData(SCIPconsGetHdlr(cons));
242    assert(conshdlrdata != NULL);
243    assert(conshdlrdata->linvareventhdlr != NULL);
244 
245    consdata = SCIPconsGetData(cons);
246    assert(consdata != NULL);
247 
248    assert(linvarpos >= 0);
249    assert(linvarpos < consdata->nlinvars);
250 
251    SCIP_CALL( SCIPallocBlockMemory(scip, &eventdata) );
252 
253    eventtype = SCIP_EVENTTYPE_VARFIXED;
254    if( !SCIPisInfinity(scip, consdata->rhs) )
255    {
256       /* if right hand side is finite, then a tightening in the lower bound of coef*linvar is of interest */
257       if( consdata->lincoefs[linvarpos] > 0.0 )
258          eventtype |= SCIP_EVENTTYPE_LBCHANGED;
259       else
260          eventtype |= SCIP_EVENTTYPE_UBCHANGED;
261    }
262    if( !SCIPisInfinity(scip, -consdata->lhs) )
263    {
264       /* if left hand side is finite, then a tightening in the upper bound of coef*linvar is of interest */
265       if( consdata->lincoefs[linvarpos] > 0.0 )
266          eventtype |= SCIP_EVENTTYPE_UBCHANGED;
267       else
268          eventtype |= SCIP_EVENTTYPE_LBCHANGED;
269    }
270 
271    eventdata->conshdlrdata = conshdlrdata;
272    eventdata->cons = cons;
273    eventdata->varidx = linvarpos;
274    SCIP_CALL( SCIPcatchVarEvent(scip, consdata->linvars[linvarpos], eventtype, conshdlrdata->linvareventhdlr, (SCIP_EVENTDATA*)eventdata, &eventdata->filterpos) );
275 
276    /* ensure lineventdata array is existing */
277    if( consdata->lineventdata == NULL )
278    {
279       SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consdata->lineventdata, consdata->linvarssize) );
280    }
281 
282    consdata->lineventdata[linvarpos] = eventdata;
283 
284    /* since bound changes were not catched before, a possibly stored linear activity may have become outdated, so set to invalid */
285    consdata->minlinactivity = SCIP_INVALID;
286    consdata->maxlinactivity = SCIP_INVALID;
287 
288    /* mark constraint for propagation */
289    SCIP_CALL( SCIPmarkConsPropagate(scip, cons) );
290 
291    return SCIP_OKAY;
292 }
293 
294 /* drops variable bound change events on a linear variable in a nonlinear constraint */
295 static
dropLinearVarEvents(SCIP * scip,SCIP_CONS * cons,int linvarpos)296 SCIP_RETCODE dropLinearVarEvents(
297    SCIP*                 scip,               /**< SCIP data structure */
298    SCIP_CONS*            cons,               /**< constraint for which to catch bound change events */
299    int                   linvarpos           /**< position of variable in linear variables array */
300    )
301 {
302    SCIP_CONSHDLRDATA* conshdlrdata;
303    SCIP_CONSDATA* consdata;
304    SCIP_EVENTTYPE eventtype;
305 
306    assert(scip != NULL);
307    assert(cons != NULL);
308    assert(SCIPconsIsTransformed(cons));
309 
310    assert(SCIPconsGetHdlr(cons) != NULL);
311    conshdlrdata = SCIPconshdlrGetData(SCIPconsGetHdlr(cons));
312    assert(conshdlrdata != NULL);
313    assert(conshdlrdata->linvareventhdlr != NULL);
314 
315    consdata = SCIPconsGetData(cons);
316    assert(consdata != NULL);
317 
318    assert(linvarpos >= 0);
319    assert(linvarpos < consdata->nlinvars);
320    assert(consdata->lineventdata != NULL);
321    assert(consdata->lineventdata[linvarpos] != NULL);
322    assert(consdata->lineventdata[linvarpos]->cons == cons);
323    assert(consdata->lineventdata[linvarpos]->varidx == linvarpos);
324    assert(consdata->lineventdata[linvarpos]->filterpos >= 0);
325 
326    eventtype = SCIP_EVENTTYPE_VARFIXED;
327    if( !SCIPisInfinity(scip, consdata->rhs) )
328    {
329       /* if right hand side is finite, then a tightening in the lower bound of coef*linvar is of interest */
330       if( consdata->lincoefs[linvarpos] > 0.0 )
331          eventtype |= SCIP_EVENTTYPE_LBCHANGED;
332       else
333          eventtype |= SCIP_EVENTTYPE_UBCHANGED;
334    }
335    if( !SCIPisInfinity(scip, -consdata->lhs) )
336    {
337       /* if left hand side is finite, then a tightening in the upper bound of coef*linvar is of interest */
338       if( consdata->lincoefs[linvarpos] > 0.0 )
339          eventtype |= SCIP_EVENTTYPE_UBCHANGED;
340       else
341          eventtype |= SCIP_EVENTTYPE_LBCHANGED;
342    }
343 
344    SCIP_CALL( SCIPdropVarEvent(scip, consdata->linvars[linvarpos], eventtype, conshdlrdata->linvareventhdlr, (SCIP_EVENTDATA*)consdata->lineventdata[linvarpos], consdata->lineventdata[linvarpos]->filterpos) );
345 
346    SCIPfreeBlockMemory(scip, &consdata->lineventdata[linvarpos]);  /*lint !e866*/
347 
348    return SCIP_OKAY;
349 }
350 
351 /** locks a linear variable in a constraint */
352 static
lockLinearVariable(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * var,SCIP_Real coef)353 SCIP_RETCODE lockLinearVariable(
354    SCIP*                 scip,               /**< SCIP data structure */
355    SCIP_CONS*            cons,               /**< constraint where to lock a variable */
356    SCIP_VAR*             var,                /**< variable to lock */
357    SCIP_Real             coef                /**< coefficient of variable in constraint */
358    )
359 {
360    SCIP_CONSDATA* consdata;
361 
362    assert(scip != NULL);
363    assert(cons != NULL);
364    assert(var != NULL);
365    assert(coef != 0.0);
366 
367    consdata = SCIPconsGetData(cons);
368    assert(consdata != NULL);
369 
370    if( coef > 0.0 )
371    {
372       SCIP_CALL( SCIPlockVarCons(scip, var, cons, !SCIPisInfinity(scip, -consdata->lhs), !SCIPisInfinity(scip,  consdata->rhs)) );
373    }
374    else
375    {
376       SCIP_CALL( SCIPlockVarCons(scip, var, cons, !SCIPisInfinity(scip,  consdata->rhs), !SCIPisInfinity(scip, -consdata->lhs)) );
377    }
378 
379    return SCIP_OKAY;
380 }
381 
382 /** unlocks a linear variable in a constraint */
383 static
unlockLinearVariable(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * var,SCIP_Real coef)384 SCIP_RETCODE unlockLinearVariable(
385    SCIP*                 scip,               /**< SCIP data structure */
386    SCIP_CONS*            cons,               /**< constraint where to unlock a variable */
387    SCIP_VAR*             var,                /**< variable to unlock */
388    SCIP_Real             coef                /**< coefficient of variable in constraint */
389    )
390 {
391    SCIP_CONSDATA* consdata;
392 
393    assert(scip != NULL);
394    assert(cons != NULL);
395    assert(!SCIPconsIsLockedType(cons, SCIP_LOCKTYPE_CONFLICT));
396    assert(var != NULL);
397    assert(coef != 0.0);
398 
399    consdata = SCIPconsGetData(cons);
400    assert(consdata != NULL);
401 
402    if( coef > 0.0 )
403    {
404       SCIP_CALL( SCIPunlockVarCons(scip, var, cons, !SCIPisInfinity(scip, -consdata->lhs), !SCIPisInfinity(scip,  consdata->rhs)) );
405    }
406    else
407    {
408       SCIP_CALL( SCIPunlockVarCons(scip, var, cons, !SCIPisInfinity(scip,  consdata->rhs), !SCIPisInfinity(scip, -consdata->lhs)) );
409    }
410 
411    return SCIP_OKAY;
412 }
413 
414 /** computes the minimal and maximal activity for the linear part in a constraint data
415  *  only sums up terms that contribute finite values
416  *  gives the number of terms that contribute infinite values
417  *  only computes those activities where the corresponding side of the constraint is finite
418  */
419 static
consdataUpdateLinearActivity(SCIP * scip,SCIP_CONSDATA * consdata)420 void consdataUpdateLinearActivity(
421    SCIP*                 scip,               /**< SCIP data structure */
422    SCIP_CONSDATA*        consdata            /**< constraint data */
423    )
424 {  /*lint --e{666}*/
425    SCIP_ROUNDMODE prevroundmode;
426    int       i;
427    SCIP_Real bnd;
428 
429    assert(scip != NULL);
430    assert(consdata != NULL);
431 
432    if( consdata->minlinactivity != SCIP_INVALID && consdata->maxlinactivity != SCIP_INVALID )  /*lint !e777*/
433    {
434       /* activities should be uptodate */
435       assert(consdata->minlinactivityinf >= 0);
436       assert(consdata->maxlinactivityinf >= 0);
437       return;
438    }
439 
440    consdata->minlinactivityinf = 0;
441    consdata->maxlinactivityinf = 0;
442 
443    /* if lhs is -infinite, then we do not compute a maximal activity, so we set it to  infinity
444     * if rhs is  infinite, then we do not compute a minimal activity, so we set it to -infinity
445     */
446    consdata->minlinactivity = SCIPisInfinity(scip,  consdata->rhs) ? -INTERVALINFTY : 0.0;
447    consdata->maxlinactivity = SCIPisInfinity(scip, -consdata->lhs) ?  INTERVALINFTY : 0.0;
448 
449    if( consdata->nlinvars == 0 )
450       return;
451 
452    /* if the activities computed here should be still uptodate after boundchanges,
453     * variable events need to be catched */
454    assert(consdata->lineventdata != NULL);
455 
456    prevroundmode = SCIPintervalGetRoundingMode();
457 
458    if( !SCIPisInfinity(scip,  consdata->rhs) )
459    {
460       /* compute minimal activity only if there is a finite right hand side */
461       SCIPintervalSetRoundingModeDownwards();
462 
463       for( i = 0; i < consdata->nlinvars; ++i )
464       {
465          assert(SCIPvarGetLbLocal(consdata->linvars[i]) <= SCIPvarGetUbLocal(consdata->linvars[i]));
466          assert(consdata->lineventdata[i] != NULL);
467          if( consdata->lincoefs[i] >= 0.0 )
468          {
469             bnd = SCIPvarGetLbLocal(consdata->linvars[i]);
470             if( SCIPisInfinity(scip, -bnd) )
471             {
472                ++consdata->minlinactivityinf;
473                continue;
474             }
475             assert(!SCIPisInfinity(scip, bnd)); /* do not like variables that are fixed at +infinity */
476          }
477          else
478          {
479             bnd = SCIPvarGetUbLocal(consdata->linvars[i]);
480             if( SCIPisInfinity(scip,  bnd) )
481             {
482                ++consdata->minlinactivityinf;
483                continue;
484             }
485             assert(!SCIPisInfinity(scip, -bnd)); /* do not like variables that are fixed at -infinity */
486          }
487          consdata->minlinactivity += consdata->lincoefs[i] * bnd;
488       }
489    }
490 
491    if( !SCIPisInfinity(scip, -consdata->lhs) )
492    {
493       /* compute maximal activity only if there is a finite left hand side */
494       SCIPintervalSetRoundingModeUpwards();
495 
496       for( i = 0; i < consdata->nlinvars; ++i )
497       {
498          assert(consdata->lineventdata[i] != NULL);
499          assert(SCIPvarGetLbLocal(consdata->linvars[i]) <= SCIPvarGetUbLocal(consdata->linvars[i]));
500          if( consdata->lincoefs[i] >= 0.0 )
501          {
502             bnd = SCIPvarGetUbLocal(consdata->linvars[i]);
503             if( SCIPisInfinity(scip,  bnd) )
504             {
505                ++consdata->maxlinactivityinf;
506                continue;
507             }
508             assert(!SCIPisInfinity(scip, -bnd)); /* do not like variables that are fixed at -infinity */
509          }
510          else
511          {
512             bnd = SCIPvarGetLbLocal(consdata->linvars[i]);
513             if( SCIPisInfinity(scip, -bnd) )
514             {
515                ++consdata->maxlinactivityinf;
516                continue;
517             }
518             assert(!SCIPisInfinity(scip, bnd)); /* do not like variables that are fixed at +infinity */
519          }
520          consdata->maxlinactivity += consdata->lincoefs[i] * bnd;
521       }
522    }
523    assert(consdata->minlinactivity <= consdata->maxlinactivity || consdata->minlinactivityinf > 0 || consdata->maxlinactivityinf > 0);
524 
525    SCIPintervalSetRoundingMode(prevroundmode);
526 }
527 
528 /** update the linear activities after a change in the lower bound of a variable */
529 static
consdataUpdateLinearActivityLbChange(SCIP * scip,SCIP_CONSDATA * consdata,SCIP_Real coef,SCIP_Real oldbnd,SCIP_Real newbnd)530 void consdataUpdateLinearActivityLbChange(
531    SCIP*                 scip,               /**< SCIP data structure */
532    SCIP_CONSDATA*        consdata,           /**< constraint data */
533    SCIP_Real             coef,               /**< coefficient of variable in constraint */
534    SCIP_Real             oldbnd,             /**< previous lower bound of variable */
535    SCIP_Real             newbnd              /**< new lower bound of variable */
536    )
537 {
538    SCIP_ROUNDMODE prevroundmode;
539 
540    assert(scip != NULL);
541    assert(consdata != NULL);
542    /* we can't deal with lower bounds at infinity */
543    assert(!SCIPisInfinity(scip, oldbnd));
544    assert(!SCIPisInfinity(scip, newbnd));
545 
546    /* assume lhs <= a*x + y <= rhs, then the following boundchanges can be deduced:
547     * a > 0:  y <= rhs - a*lb(x),  y >= lhs - a*ub(x)
548     * a < 0:  y <= rhs - a*ub(x),  y >= lhs - a*lb(x)
549     */
550 
551    if( coef > 0.0 )
552    {
553       /* we should only be called if rhs is finite */
554       assert(!SCIPisInfinity(scip, consdata->rhs));
555 
556       /* we have no min activities computed so far, so cannot update */
557       if( consdata->minlinactivity == SCIP_INVALID )  /*lint !e777*/
558          return;
559 
560       assert(consdata->minlinactivity > -INTERVALINFTY);
561 
562       prevroundmode = SCIPintervalGetRoundingMode();
563       SCIPintervalSetRoundingModeDownwards();
564 
565       /* update min activity */
566       if( SCIPisInfinity(scip, -oldbnd) )
567       {
568          --consdata->minlinactivityinf;
569          assert(consdata->minlinactivityinf >= 0);
570       }
571       else
572       {
573          consdata->minlinactivity += SCIPintervalNegateReal(coef) * oldbnd;
574       }
575 
576       if( SCIPisInfinity(scip, -newbnd) )
577       {
578          ++consdata->minlinactivityinf;
579       }
580       else
581       {
582          consdata->minlinactivity += coef * newbnd;
583       }
584 
585       SCIPintervalSetRoundingMode(prevroundmode);
586    }
587    else
588    {
589       /* we should only be called if lhs is finite */
590       assert(!SCIPisInfinity(scip, -consdata->lhs));
591 
592       /* we have no max activities computed so far, so cannot update */
593       if( consdata->maxlinactivity == SCIP_INVALID )  /*lint !e777*/
594          return;
595 
596       assert(consdata->maxlinactivity < INTERVALINFTY);
597 
598       prevroundmode = SCIPintervalGetRoundingMode();
599       SCIPintervalSetRoundingModeUpwards();
600 
601       /* update max activity */
602       if( SCIPisInfinity(scip, -oldbnd) )
603       {
604          --consdata->maxlinactivityinf;
605          assert(consdata->maxlinactivityinf >= 0);
606       }
607       else
608       {
609          consdata->maxlinactivity += SCIPintervalNegateReal(coef) * oldbnd;
610       }
611 
612       if( SCIPisInfinity(scip, -newbnd) )
613       {
614          ++consdata->maxlinactivityinf;
615       }
616       else
617       {
618          consdata->maxlinactivity += coef * newbnd;
619       }
620 
621       SCIPintervalSetRoundingMode(prevroundmode);
622    }
623 }
624 
625 /** update the linear activities after a change in the upper bound of a variable */
626 static
consdataUpdateLinearActivityUbChange(SCIP * scip,SCIP_CONSDATA * consdata,SCIP_Real coef,SCIP_Real oldbnd,SCIP_Real newbnd)627 void consdataUpdateLinearActivityUbChange(
628    SCIP*                 scip,               /**< SCIP data structure */
629    SCIP_CONSDATA*        consdata,           /**< constraint data */
630    SCIP_Real             coef,               /**< coefficient of variable in constraint */
631    SCIP_Real             oldbnd,             /**< previous lower bound of variable */
632    SCIP_Real             newbnd              /**< new lower bound of variable */
633    )
634 {
635    SCIP_ROUNDMODE prevroundmode;
636 
637    assert(scip != NULL);
638    assert(consdata != NULL);
639    /* we can't deal with upper bounds at -infinity */
640    assert(!SCIPisInfinity(scip, -oldbnd));
641    assert(!SCIPisInfinity(scip, -newbnd));
642 
643    /* assume lhs <= a*x + y <= rhs, then the following boundchanges can be deduced:
644     * a > 0:  y <= rhs - a*lb(x),  y >= lhs - a*ub(x)
645     * a < 0:  y <= rhs - a*ub(x),  y >= lhs - a*lb(x)
646     */
647    if( coef > 0.0 )
648    {
649       /* we should only be called if lhs is finite */
650       assert(!SCIPisInfinity(scip, -consdata->lhs));
651 
652       /* we have no max activities computed so far, so cannot update */
653       if( consdata->maxlinactivity == SCIP_INVALID )  /*lint !e777*/
654          return;
655 
656       assert(consdata->maxlinactivity < INTERVALINFTY);
657 
658       prevroundmode = SCIPintervalGetRoundingMode();
659       SCIPintervalSetRoundingModeUpwards();
660 
661       /* update max activity */
662       if( SCIPisInfinity(scip, oldbnd) )
663       {
664          --consdata->maxlinactivityinf;
665          assert(consdata->maxlinactivityinf >= 0);
666       }
667       else
668       {
669          consdata->maxlinactivity += SCIPintervalNegateReal(coef) * oldbnd;
670       }
671 
672       if( SCIPisInfinity(scip, newbnd) )
673       {
674          ++consdata->maxlinactivityinf;
675       }
676       else
677       {
678          consdata->maxlinactivity += coef * newbnd;
679       }
680 
681       SCIPintervalSetRoundingMode(prevroundmode);
682    }
683    else
684    {
685       /* we should only be called if rhs is finite */
686       assert(!SCIPisInfinity(scip, consdata->rhs));
687 
688       /* we have no min activities computed so far, so cannot update */
689       if( consdata->minlinactivity == SCIP_INVALID )  /*lint !e777*/
690          return;
691 
692       assert(consdata->minlinactivity > -INTERVALINFTY);
693 
694       prevroundmode = SCIPintervalGetRoundingMode();
695       SCIPintervalSetRoundingModeDownwards();
696 
697       /* update min activity */
698       if( SCIPisInfinity(scip, oldbnd) )
699       {
700          --consdata->minlinactivityinf;
701          assert(consdata->minlinactivityinf >= 0);
702       }
703       else
704       {
705          consdata->minlinactivity += SCIPintervalNegateReal(coef) * oldbnd;
706       }
707 
708       if( SCIPisInfinity(scip, newbnd) )
709       {
710          ++consdata->minlinactivityinf;
711       }
712       else
713       {
714          consdata->minlinactivity += coef * newbnd;
715       }
716 
717       SCIPintervalSetRoundingMode(prevroundmode);
718    }
719 }
720 
721 /** processes variable fixing or bound change event */
722 static
SCIP_DECL_EVENTEXEC(processLinearVarEvent)723 SCIP_DECL_EVENTEXEC(processLinearVarEvent)
724 {
725    SCIP_CONS* cons;
726    SCIP_CONSDATA* consdata;
727    SCIP_EVENTTYPE eventtype;
728    int varidx;
729 
730    assert(scip != NULL);
731    assert(event != NULL);
732    assert(eventdata != NULL);
733    assert(eventhdlr != NULL);
734 
735    cons = ((LINVAREVENTDATA*)eventdata)->cons;
736    assert(cons != NULL);
737 
738    consdata = SCIPconsGetData(cons);
739    assert(consdata != NULL);
740 
741    varidx = ((LINVAREVENTDATA*)eventdata)->varidx;
742    assert(varidx >= 0);
743    assert(varidx < consdata->nlinvars);
744 
745    eventtype = SCIPeventGetType(event);
746 
747    if( eventtype & SCIP_EVENTTYPE_VARFIXED )
748    {
749       consdata->isremovedfixingslin = FALSE;
750    }
751 
752    if( eventtype & SCIP_EVENTTYPE_BOUNDCHANGED )
753    {
754       /* update activity bounds for linear terms */
755       if( eventtype & SCIP_EVENTTYPE_LBCHANGED )
756          consdataUpdateLinearActivityLbChange(scip, consdata, consdata->lincoefs[varidx], SCIPeventGetOldbound(event), SCIPeventGetNewbound(event));
757       else
758          consdataUpdateLinearActivityUbChange(scip, consdata, consdata->lincoefs[varidx], SCIPeventGetOldbound(event), SCIPeventGetNewbound(event));
759 
760       if( eventtype & SCIP_EVENTTYPE_BOUNDTIGHTENED )
761       {
762          assert(((LINVAREVENTDATA*)eventdata)->conshdlrdata != NULL);
763          ((LINVAREVENTDATA*)eventdata)->conshdlrdata->ispropagated = FALSE;
764 
765          /* mark constraint for propagation */
766          SCIP_CALL( SCIPmarkConsPropagate(scip, cons) );
767       }
768    }
769 
770    return SCIP_OKAY;
771 }
772 
773 /** processes bound change events for variables in expression graph */
774 static
SCIP_DECL_EVENTEXEC(processNonlinearVarEvent)775 SCIP_DECL_EVENTEXEC(processNonlinearVarEvent)
776 {
777    SCIP_CONSHDLRDATA* conshdlrdata;
778    SCIP_EVENTTYPE eventtype;
779 
780    assert(scip != NULL);
781    assert(event != NULL);
782    assert(eventdata != NULL);
783    assert(eventhdlr != NULL);
784 
785    conshdlrdata = (SCIP_CONSHDLRDATA*)SCIPeventhdlrGetData(eventhdlr);
786    assert(conshdlrdata != NULL);
787    assert(conshdlrdata->exprgraph != NULL);
788 
789    eventtype = SCIPeventGetType(event);
790    assert( eventtype & (SCIP_EVENTTYPE_BOUNDCHANGED | SCIP_EVENTTYPE_VARFIXED) );
791 
792    if( eventtype & SCIP_EVENTTYPE_BOUNDCHANGED )
793    {
794       SCIP_Real newbd;
795 
796       SCIPdebugMsg(scip, "changed %s bound on expression graph variable <%s> from %g to %g\n",
797          (eventtype & SCIP_EVENTTYPE_LBCHANGED) ? "lower" : "upper",
798          SCIPvarGetName(SCIPeventGetVar(event)), SCIPeventGetOldbound(event), SCIPeventGetNewbound(event));
799 
800       if( eventtype & SCIP_EVENTTYPE_BOUNDTIGHTENED )
801          conshdlrdata->ispropagated = FALSE;
802       /* @todo a global bound tightening may yield in convex/concave curvatures, maybe the iscurvcheck flag should be reset? */
803 
804       /* update variable bound in expression graph, with epsilon added */
805       if( eventtype & SCIP_EVENTTYPE_LBCHANGED )
806       {
807          newbd = -infty2infty(SCIPinfinity(scip), INTERVALINFTY, -SCIPeventGetNewbound(event));  /*lint !e666*/
808          /* if newbd in [0,eps], then relax to 0.0, otherwise relax by -epsilon
809           * this is to ensure that an original positive variable does not get negative by this, which may have an adverse effect on convexity recoginition, for example */
810          if( newbd >= 0.0 && newbd <= SCIPepsilon(scip) )
811             newbd = 0.0;
812          else
813             newbd -= SCIPepsilon(scip);
814          SCIPexprgraphSetVarNodeLb(conshdlrdata->exprgraph, (SCIP_EXPRGRAPHNODE*)eventdata, newbd);
815       }
816       else
817       {
818          newbd = +infty2infty(SCIPinfinity(scip), INTERVALINFTY,  SCIPeventGetNewbound(event));  /*lint !e666*/
819          /* if newbd in [-eps,0], then relax to 0.0, otherwise relax by +epsilon */
820          if( newbd >= -SCIPepsilon(scip) && newbd <= 0.0 )
821             newbd = 0.0;
822          else
823             newbd += SCIPepsilon(scip);
824          SCIPexprgraphSetVarNodeUb(conshdlrdata->exprgraph, (SCIP_EXPRGRAPHNODE*)eventdata, newbd);
825       }
826    }
827    else
828    {
829       assert(eventtype & SCIP_EVENTTYPE_VARFIXED);
830       conshdlrdata->isremovedfixings = FALSE;
831    }
832 
833    return SCIP_OKAY;
834 }
835 
836 /** callback method for variable addition in expression graph */
837 static
SCIP_DECL_EXPRGRAPHVARADDED(exprgraphVarAdded)838 SCIP_DECL_EXPRGRAPHVARADDED( exprgraphVarAdded )
839 {
840    SCIP_CONSHDLRDATA* conshdlrdata;
841    SCIP_INTERVAL varbounds;
842    SCIP_VAR* var_;
843 
844    assert(exprgraph != NULL);
845    assert(var != NULL);
846    assert(varnode != NULL);
847 
848    var_ = (SCIP_VAR*)var;
849 
850    conshdlrdata = (SCIP_CONSHDLRDATA*)userdata;
851    assert(conshdlrdata != NULL);
852    assert(conshdlrdata->exprgraph == exprgraph);
853 
854    /* catch variable bound change events */
855    SCIP_CALL( SCIPcatchVarEvent(conshdlrdata->scip, (SCIP_VAR*)var, SCIP_EVENTTYPE_BOUNDCHANGED | SCIP_EVENTTYPE_VARFIXED, conshdlrdata->nonlinvareventhdlr, (SCIP_EVENTDATA*)varnode, NULL) );
856    SCIPdebugMessage("catch boundchange events on new expression graph variable <%s>\n", SCIPvarGetName(var_));
857 
858    /* set current bounds in expression graph */
859    SCIPintervalSetBounds(&varbounds,
860       -infty2infty(SCIPinfinity(conshdlrdata->scip), INTERVALINFTY, -MIN(SCIPvarGetLbLocal(var_), SCIPvarGetUbLocal(var_))),  /*lint !e666*/
861       +infty2infty(SCIPinfinity(conshdlrdata->scip), INTERVALINFTY,  MAX(SCIPvarGetLbLocal(var_), SCIPvarGetUbLocal(var_)))   /*lint !e666*/
862       );
863    SCIPexprgraphSetVarNodeBounds(exprgraph, varnode, varbounds);
864 
865    SCIP_CALL( SCIPaddVarLocksType(conshdlrdata->scip, var_, SCIP_LOCKTYPE_MODEL, 1, 1) );
866    SCIPdebugMessage("increased up- and downlocks of variable <%s>\n", SCIPvarGetName(var_));
867 
868    SCIP_CALL( SCIPcaptureVar(conshdlrdata->scip, var_) );
869    SCIPdebugMessage("capture variable <%s>\n", SCIPvarGetName(var_));
870 
871    conshdlrdata->isremovedfixings &= SCIPvarIsActive(var_);
872    conshdlrdata->ispropagated = FALSE;
873 
874    return SCIP_OKAY;
875 }
876 
877 /** callback method for variable removal in expression graph */
878 static
SCIP_DECL_EXPRGRAPHVARREMOVE(exprgraphVarRemove)879 SCIP_DECL_EXPRGRAPHVARREMOVE( exprgraphVarRemove )
880 {
881    SCIP_CONSHDLRDATA* conshdlrdata;
882    SCIP_VAR* var_;
883 
884    assert(exprgraph != NULL);
885    assert(var != NULL);
886    assert(varnode != NULL);
887 
888    var_ = (SCIP_VAR*)var;
889 
890    conshdlrdata = (SCIP_CONSHDLRDATA*)userdata;
891    assert(conshdlrdata != NULL);
892    assert(conshdlrdata->exprgraph == exprgraph);
893 
894    SCIP_CALL( SCIPdropVarEvent(conshdlrdata->scip, var_, SCIP_EVENTTYPE_BOUNDCHANGED | SCIP_EVENTTYPE_VARFIXED, conshdlrdata->nonlinvareventhdlr, (SCIP_EVENTDATA*)varnode, -1) );
895    SCIPdebugMessage("drop boundchange events on expression graph variable <%s>\n", SCIPvarGetName(var_));
896 
897    SCIP_CALL( SCIPaddVarLocksType(conshdlrdata->scip, var_, SCIP_LOCKTYPE_MODEL, -1, -1) );
898    SCIPdebugMessage("decreased up- and downlocks of variable <%s>\n", SCIPvarGetName(var_));
899 
900    SCIPdebugMessage("release variable <%s>\n", SCIPvarGetName(var_));
901    SCIP_CALL( SCIPreleaseVar(conshdlrdata->scip, &var_) );
902 
903    return SCIP_OKAY;
904 }
905 
906 /* adds expression trees to constraint */
907 static
consdataAddExprtrees(SCIP * scip,SCIP_CONSDATA * consdata,int nexprtrees,SCIP_EXPRTREE ** exprtrees,SCIP_Real * coefs,SCIP_Bool copytrees)908 SCIP_RETCODE consdataAddExprtrees(
909    SCIP*                 scip,               /**< SCIP data structure */
910    SCIP_CONSDATA*        consdata,           /**< nonlinear constraint data */
911    int                   nexprtrees,         /**< number of expression trees */
912    SCIP_EXPRTREE**       exprtrees,          /**< expression trees */
913    SCIP_Real*            coefs,              /**< coefficients of expression trees, or NULL if all 1.0 */
914    SCIP_Bool             copytrees           /**< whether trees should be copied or ownership should be assumed */
915    )
916 {
917    int i;
918 
919    assert(scip != NULL);
920    assert(consdata != NULL);
921    assert(consdata->exprtrees != NULL || consdata->nexprtrees == 0);
922 
923    if( nexprtrees == 0 )
924       return SCIP_OKAY;
925 
926    /* invalidate activity information */
927    consdata->activity = SCIP_INVALID;
928 
929    /* invalidate nonlinear row */
930    if( consdata->nlrow != NULL )
931    {
932       SCIP_CALL( SCIPreleaseNlRow(scip, &consdata->nlrow) );
933    }
934 
935    consdata->ispresolved  = FALSE;
936    consdata->curvature = SCIP_EXPRCURV_UNKNOWN;
937    consdata->iscurvchecked = FALSE;
938 
939    if( consdata->nexprtrees == 0 )
940    {
941       assert(consdata->exprtrees   == NULL);
942       assert(consdata->nonlincoefs == NULL);
943       SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consdata->exprtrees,   nexprtrees) );
944       SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consdata->nonlincoefs, nexprtrees) );
945       SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consdata->curvatures,  nexprtrees) );
946    }
947    else
948    {
949       assert(consdata->exprtrees   != NULL);
950       assert(consdata->nonlincoefs != NULL);
951       SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->exprtrees,   consdata->nexprtrees, consdata->nexprtrees + nexprtrees) );
952       SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->nonlincoefs, consdata->nexprtrees, consdata->nexprtrees + nexprtrees) );
953       SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->curvatures,  consdata->nexprtrees, consdata->nexprtrees + nexprtrees) );
954    }
955 
956    for( i = 0; i < nexprtrees; ++i )
957    {
958       assert(exprtrees[i] != NULL);
959       /* the expression tree need to have SCIP_VAR*'s stored */
960       assert(SCIPexprtreeGetNVars(exprtrees[i]) == 0 || SCIPexprtreeGetVars(exprtrees[i]) != NULL);
961 
962       if( copytrees )
963       {
964          SCIP_CALL( SCIPexprtreeCopy(SCIPblkmem(scip), &consdata->exprtrees[consdata->nexprtrees + i], exprtrees[i]) );
965       }
966       else
967       {
968          consdata->exprtrees[consdata->nexprtrees + i] = exprtrees[i];
969       }
970 
971       consdata->nonlincoefs[consdata->nexprtrees + i] = (coefs != NULL ? coefs[i] : 1.0);
972       consdata->curvatures[consdata->nexprtrees + i] = SCIP_EXPRCURV_UNKNOWN;
973    }
974    consdata->nexprtrees += nexprtrees;
975 
976    return SCIP_OKAY;
977 }
978 
979 /* sets expression trees of constraints, clears previously ones */
980 static
consdataSetExprtrees(SCIP * scip,SCIP_CONSDATA * consdata,int nexprtrees,SCIP_EXPRTREE ** exprtrees,SCIP_Real * coefs,SCIP_Bool copytrees)981 SCIP_RETCODE consdataSetExprtrees(
982    SCIP*                 scip,               /**< SCIP data structure */
983    SCIP_CONSDATA*        consdata,           /**< nonlinear constraint data */
984    int                   nexprtrees,         /**< number of expression trees */
985    SCIP_EXPRTREE**       exprtrees,          /**< expression trees */
986    SCIP_Real*            coefs,              /**< coefficients of expression trees, or NULL if all 1.0 */
987    SCIP_Bool             copytrees           /**< whether trees should be copied or ownership should be assumed */
988    )
989 {
990    int i;
991 
992    assert(scip != NULL);
993    assert(consdata != NULL);
994    assert(consdata->exprtrees != NULL || consdata->nexprtrees == 0);
995 
996    /* clear existing expression trees */
997    if( consdata->nexprtrees > 0 )
998    {
999       for( i = 0; i < consdata->nexprtrees; ++i )
1000       {
1001          assert(consdata->exprtrees[i] != NULL);
1002          SCIP_CALL( SCIPexprtreeFree(&consdata->exprtrees[i]) );
1003       }
1004 
1005       /* invalidate activity information */
1006       consdata->activity = SCIP_INVALID;
1007 
1008       /* invalidate nonlinear row */
1009       if( consdata->nlrow != NULL )
1010       {
1011          SCIP_CALL( SCIPreleaseNlRow(scip, &consdata->nlrow) );
1012       }
1013 
1014       consdata->ispresolved  = FALSE;
1015       consdata->curvature = SCIP_EXPRCURV_LINEAR;
1016       consdata->iscurvchecked = TRUE;
1017 
1018       SCIPfreeBlockMemoryArray(scip, &consdata->exprtrees,   consdata->nexprtrees);
1019       SCIPfreeBlockMemoryArray(scip, &consdata->nonlincoefs, consdata->nexprtrees);
1020       SCIPfreeBlockMemoryArray(scip, &consdata->curvatures,  consdata->nexprtrees);
1021       consdata->nexprtrees = 0;
1022    }
1023 
1024    SCIP_CALL( consdataAddExprtrees(scip, consdata, nexprtrees, exprtrees, coefs, copytrees) );
1025 
1026    return SCIP_OKAY;
1027 }
1028 
1029 /** ensures, that linear vars and coefs arrays can store at least num entries */
1030 static
consdataEnsureLinearVarsSize(SCIP * scip,SCIP_CONSDATA * consdata,int num)1031 SCIP_RETCODE consdataEnsureLinearVarsSize(
1032    SCIP*                 scip,               /**< SCIP data structure */
1033    SCIP_CONSDATA*        consdata,           /**< nonlinear constraint data */
1034    int                   num                 /**< minimum number of entries to store */
1035    )
1036 {
1037    assert(scip != NULL);
1038    assert(consdata != NULL);
1039    assert(consdata->nlinvars <= consdata->linvarssize);
1040 
1041    if( num > consdata->linvarssize )
1042    {
1043       int newsize;
1044 
1045       newsize = SCIPcalcMemGrowSize(scip, num);
1046       SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->linvars,  consdata->linvarssize, newsize) );
1047       SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->lincoefs, consdata->linvarssize, newsize) );
1048       if( consdata->lineventdata != NULL )
1049       {
1050          SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->lineventdata, consdata->linvarssize, newsize) );
1051       }
1052       consdata->linvarssize = newsize;
1053    }
1054    assert(num <= consdata->linvarssize);
1055 
1056    return SCIP_OKAY;
1057 }
1058 
1059 /** creates constraint data structure */
1060 static
consdataCreate(SCIP * scip,SCIP_CONSDATA ** consdata,SCIP_Real lhs,SCIP_Real rhs,int nlinvars,SCIP_VAR ** linvars,SCIP_Real * lincoefs,int nexprtrees,SCIP_EXPRTREE ** exprtrees,SCIP_Real * nonlincoefs,SCIP_Bool capturevars)1061 SCIP_RETCODE consdataCreate(
1062    SCIP*                 scip,               /**< SCIP data structure */
1063    SCIP_CONSDATA**       consdata,           /**< a buffer to store pointer to new constraint data */
1064    SCIP_Real             lhs,                /**< left hand side of constraint */
1065    SCIP_Real             rhs,                /**< right hand side of constraint */
1066    int                   nlinvars,           /**< number of linear variables */
1067    SCIP_VAR**            linvars,            /**< array of linear variables */
1068    SCIP_Real*            lincoefs,           /**< array of coefficients of linear variables */
1069    int                   nexprtrees,         /**< number of expression trees */
1070    SCIP_EXPRTREE**       exprtrees,          /**< expression trees */
1071    SCIP_Real*            nonlincoefs,        /**< coefficients of expression trees */
1072    SCIP_Bool             capturevars         /**< whether we should capture variables */
1073    )
1074 {
1075    int i;
1076 
1077    assert(scip != NULL);
1078    assert(consdata != NULL);
1079 
1080    assert(nlinvars == 0 || linvars  != NULL);
1081    assert(nlinvars == 0 || lincoefs != NULL);
1082    assert(nexprtrees == 0 || exprtrees != NULL);
1083    assert(nexprtrees == 0 || nonlincoefs != NULL);
1084 
1085    SCIP_CALL( SCIPallocBlockMemory(scip, consdata) );
1086    BMSclearMemory(*consdata);
1087 
1088    (*consdata)->minlinactivity = SCIP_INVALID;
1089    (*consdata)->maxlinactivity = SCIP_INVALID;
1090    (*consdata)->minlinactivityinf = -1;
1091    (*consdata)->maxlinactivityinf = -1;
1092 
1093    (*consdata)->lhs = lhs;
1094    (*consdata)->rhs = rhs;
1095 
1096    if( nlinvars > 0 )
1097    {
1098       SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &(*consdata)->linvars,  linvars,  nlinvars) );
1099       SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &(*consdata)->lincoefs, lincoefs, nlinvars) );
1100       (*consdata)->nlinvars = nlinvars;
1101       (*consdata)->linvarssize = nlinvars;
1102 
1103       if( capturevars )
1104          for( i = 0; i < nlinvars; ++i )
1105          {
1106             SCIP_CALL( SCIPcaptureVar(scip, linvars[i]) );
1107          }
1108    }
1109    else
1110    {
1111       (*consdata)->linvarssorted = TRUE;
1112       (*consdata)->linvarsmerged = TRUE;
1113    }
1114 
1115    SCIP_CALL( consdataSetExprtrees(scip, *consdata, nexprtrees, exprtrees, nonlincoefs, TRUE) );
1116 
1117    (*consdata)->linvar_maydecrease = -1;
1118    (*consdata)->linvar_mayincrease = -1;
1119 
1120    (*consdata)->activity = SCIP_INVALID;
1121    (*consdata)->lhsviol  = SCIPisInfinity(scip, -lhs) ? 0.0 : SCIP_INVALID;
1122    (*consdata)->rhsviol  = SCIPisInfinity(scip,  rhs) ? 0.0 : SCIP_INVALID;
1123 
1124    return SCIP_OKAY;
1125 }
1126 
1127 /** creates empty constraint data structure */
1128 static
consdataCreateEmpty(SCIP * scip,SCIP_CONSDATA ** consdata)1129 SCIP_RETCODE consdataCreateEmpty(
1130    SCIP*                 scip,               /**< SCIP data structure */
1131    SCIP_CONSDATA**       consdata            /**< a buffer to store pointer to new constraint data */
1132    )
1133 {
1134    assert(scip != NULL);
1135    assert(consdata != NULL);
1136 
1137    SCIP_CALL( SCIPallocBlockMemory(scip, consdata) );
1138    BMSclearMemory(*consdata);
1139 
1140    (*consdata)->lhs = -SCIPinfinity(scip);
1141    (*consdata)->rhs =  SCIPinfinity(scip);
1142 
1143    (*consdata)->linvarssorted    = TRUE;
1144    (*consdata)->linvarsmerged    = TRUE;
1145 
1146    (*consdata)->isremovedfixingslin = TRUE;
1147 
1148    (*consdata)->linvar_maydecrease = -1;
1149    (*consdata)->linvar_mayincrease = -1;
1150 
1151    (*consdata)->minlinactivity = SCIP_INVALID;
1152    (*consdata)->maxlinactivity = SCIP_INVALID;
1153    (*consdata)->minlinactivityinf = -1;
1154    (*consdata)->maxlinactivityinf = -1;
1155 
1156    (*consdata)->curvature = SCIP_EXPRCURV_LINEAR;
1157    (*consdata)->iscurvchecked = TRUE;
1158 
1159    (*consdata)->ncuts = 0;
1160 
1161    return SCIP_OKAY;
1162 }
1163 
1164 /** frees constraint data structure */
1165 static
consdataFree(SCIP * scip,SCIP_CONSDATA ** consdata)1166 SCIP_RETCODE consdataFree(
1167    SCIP*                 scip,               /**< SCIP data structure */
1168    SCIP_CONSDATA**       consdata            /**< pointer to constraint data to free */
1169    )
1170 {
1171    int i;
1172 
1173    assert(scip != NULL);
1174    assert(consdata != NULL);
1175    assert(*consdata != NULL);
1176 
1177    /* release linear variables and free linear part */
1178    if( (*consdata)->linvarssize > 0 )
1179    {
1180       for( i = 0; i < (*consdata)->nlinvars; ++i )
1181       {
1182          assert((*consdata)->lineventdata == NULL || (*consdata)->lineventdata[i] == NULL); /* variable events should have been dropped earlier */
1183          SCIP_CALL( SCIPreleaseVar(scip, &(*consdata)->linvars[i]) );
1184       }
1185       SCIPfreeBlockMemoryArray(scip, &(*consdata)->linvars,  (*consdata)->linvarssize);
1186       SCIPfreeBlockMemoryArray(scip, &(*consdata)->lincoefs, (*consdata)->linvarssize);
1187       SCIPfreeBlockMemoryArrayNull(scip, &(*consdata)->lineventdata, (*consdata)->linvarssize);
1188    }
1189    assert((*consdata)->linvars == NULL);
1190    assert((*consdata)->lincoefs == NULL);
1191    assert((*consdata)->lineventdata == NULL);
1192 
1193    if( (*consdata)->nexprtrees > 0 )
1194    {
1195       assert((*consdata)->exprtrees   != NULL);
1196       assert((*consdata)->nonlincoefs != NULL);
1197       assert((*consdata)->curvatures  != NULL);
1198       for( i = 0; i < (*consdata)->nexprtrees; ++i )
1199       {
1200          assert((*consdata)->exprtrees[i] != NULL);
1201          SCIP_CALL( SCIPexprtreeFree(&(*consdata)->exprtrees[i]) );
1202          assert((*consdata)->exprtrees[i] == NULL);
1203       }
1204       SCIPfreeBlockMemoryArray(scip, &(*consdata)->exprtrees,   (*consdata)->nexprtrees);
1205       SCIPfreeBlockMemoryArray(scip, &(*consdata)->nonlincoefs, (*consdata)->nexprtrees);
1206       SCIPfreeBlockMemoryArray(scip, &(*consdata)->curvatures,  (*consdata)->nexprtrees);
1207    }
1208    assert((*consdata)->exprtrees   == NULL);
1209    assert((*consdata)->nonlincoefs == NULL);
1210    assert((*consdata)->curvatures  == NULL);
1211 
1212    /* free nonlinear row representation */
1213    if( (*consdata)->nlrow != NULL )
1214    {
1215       SCIP_CALL( SCIPreleaseNlRow(scip, &(*consdata)->nlrow) );
1216    }
1217 
1218    SCIPfreeBlockMemory(scip, consdata);
1219    *consdata = NULL;
1220 
1221    return SCIP_OKAY;
1222 }
1223 
1224 /** sorts linear part of constraint data */
1225 static
consdataSortLinearVars(SCIP_CONSDATA * consdata)1226 void consdataSortLinearVars(
1227    SCIP_CONSDATA*        consdata            /**< nonlinear constraint data */
1228    )
1229 {
1230    assert(consdata != NULL);
1231 
1232    if( consdata->linvarssorted )
1233       return;
1234 
1235    if( consdata->nlinvars <= 1 )
1236    {
1237       consdata->linvarssorted = TRUE;
1238       return;
1239    }
1240 
1241    if( consdata->lineventdata == NULL )
1242    {
1243       SCIPsortPtrReal((void**)consdata->linvars, consdata->lincoefs, SCIPvarComp, consdata->nlinvars);
1244    }
1245    else
1246    {
1247       int i;
1248 
1249       SCIPsortPtrPtrReal((void**)consdata->linvars, (void**)consdata->lineventdata, consdata->lincoefs, SCIPvarComp, consdata->nlinvars);
1250 
1251       /* update variable indices in event data */
1252       for( i = 0; i < consdata->nlinvars; ++i )
1253          if( consdata->lineventdata[i] != NULL )
1254             consdata->lineventdata[i]->varidx = i;
1255    }
1256 
1257    consdata->linvarssorted = TRUE;
1258 }
1259 
1260 /* this function is currently not needed, but also to nice to be deleted, so it is only deactivated */
1261 #ifdef SCIP_DISABLED_CODE
1262 /** returns the position of variable in the linear coefficients array of a constraint, or -1 if not found */
1263 static
consdataFindLinearVar(SCIP_CONSDATA * consdata,SCIP_VAR * var)1264 int consdataFindLinearVar(
1265    SCIP_CONSDATA*        consdata,           /**< nonlinear constraint data */
1266    SCIP_VAR*             var                 /**< variable to search for */
1267    )
1268 {
1269    int pos;
1270 
1271    assert(consdata != NULL);
1272    assert(var != NULL);
1273 
1274    if( consdata->nlinvars == 0 )
1275       return -1;
1276 
1277    consdataSortLinearVars(consdata);
1278 
1279    if( !SCIPsortedvecFindPtr((void**)consdata->linvars, SCIPvarComp, (void*)var, consdata->nlinvars, &pos) )
1280       pos = -1;
1281 
1282    return pos;
1283 }
1284 #endif
1285 
1286 /** moves a linear variable from one position to another */
1287 static
consdataMoveLinearVar(SCIP_CONSDATA * consdata,int oldpos,int newpos)1288 void consdataMoveLinearVar(
1289    SCIP_CONSDATA*        consdata,           /**< constraint data */
1290    int                   oldpos,             /**< position of variable that shall be moved */
1291    int                   newpos              /**< new position of variable */
1292    )
1293 {
1294    assert(consdata != NULL);
1295    assert(oldpos >= 0);
1296    assert(oldpos < consdata->nlinvars);
1297    assert(newpos >= 0);
1298    assert(newpos < consdata->linvarssize);
1299 
1300    if( newpos == oldpos )
1301       return;
1302 
1303    consdata->linvars [newpos] = consdata->linvars [oldpos];
1304    consdata->lincoefs[newpos] = consdata->lincoefs[oldpos];
1305 
1306    if( consdata->lineventdata != NULL )
1307    {
1308       assert(newpos >= consdata->nlinvars || consdata->lineventdata[newpos] == NULL);
1309 
1310       consdata->lineventdata[newpos] = consdata->lineventdata[oldpos];
1311       consdata->lineventdata[newpos]->varidx = newpos;
1312 
1313       consdata->lineventdata[oldpos] = NULL;
1314    }
1315 
1316    consdata->linvarssorted = FALSE;
1317 }
1318 
1319 /** adds linear coefficient in nonlinear constraint */
1320 static
addLinearCoef(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * var,SCIP_Real coef)1321 SCIP_RETCODE addLinearCoef(
1322    SCIP*                 scip,               /**< SCIP data structure */
1323    SCIP_CONS*            cons,               /**< nonlinear constraint */
1324    SCIP_VAR*             var,                /**< variable of constraint entry */
1325    SCIP_Real             coef                /**< coefficient of constraint entry */
1326    )
1327 {
1328    SCIP_CONSDATA* consdata;
1329    SCIP_Bool transformed;
1330 
1331    assert(scip != NULL);
1332    assert(cons != NULL);
1333    assert(var  != NULL);
1334 
1335    /* ignore coefficient if it is nearly zero */
1336    if( SCIPisZero(scip, coef) )
1337       return SCIP_OKAY;
1338 
1339    consdata = SCIPconsGetData(cons);
1340    assert(consdata != NULL);
1341 
1342    /* are we in the transformed problem? */
1343    transformed = SCIPconsIsTransformed(cons);
1344 
1345    /* always use transformed variables in transformed constraints */
1346    if( transformed )
1347    {
1348       SCIP_CALL( SCIPgetTransformedVar(scip, var, &var) );
1349    }
1350    assert(var != NULL);
1351    assert(transformed == SCIPvarIsTransformed(var));
1352 
1353    SCIP_CALL( consdataEnsureLinearVarsSize(scip, consdata, consdata->nlinvars+1) );
1354    consdata->linvars [consdata->nlinvars] = var;
1355    consdata->lincoefs[consdata->nlinvars] = coef;
1356 
1357    ++consdata->nlinvars;
1358 
1359    /* catch variable events */
1360    if( SCIPconsIsEnabled(cons) )
1361    {
1362       /* catch bound change events of variable */
1363       SCIP_CALL( catchLinearVarEvents(scip, cons, consdata->nlinvars-1) );
1364    }
1365 
1366    /* invalidate activity information */
1367    consdata->activity = SCIP_INVALID;
1368    consdata->minlinactivity = SCIP_INVALID;
1369    consdata->maxlinactivity = SCIP_INVALID;
1370    consdata->minlinactivityinf = -1;
1371    consdata->maxlinactivityinf = -1;
1372 
1373    /* invalidate nonlinear row */
1374    if( consdata->nlrow != NULL )
1375    {
1376       SCIP_CALL( SCIPreleaseNlRow(scip, &consdata->nlrow) );
1377    }
1378 
1379    /* install rounding locks for new variable */
1380    SCIP_CALL( lockLinearVariable(scip, cons, var, coef) );
1381 
1382    /* capture new variable */
1383    SCIP_CALL( SCIPcaptureVar(scip, var) );
1384 
1385    consdata->ispresolved = FALSE;
1386    consdata->isremovedfixingslin = consdata->isremovedfixingslin && SCIPvarIsActive(var);
1387    if( consdata->nlinvars == 1 )
1388       consdata->linvarssorted = TRUE;
1389    else
1390       consdata->linvarssorted = consdata->linvarssorted &&
1391          (SCIPvarCompare(consdata->linvars[consdata->nlinvars-2], consdata->linvars[consdata->nlinvars-1]) == -1);
1392    /* always set too FALSE since the new linear variable should be checked if already existing as quad var term */
1393    consdata->linvarsmerged = FALSE;
1394 
1395    return SCIP_OKAY;
1396 }
1397 
1398 /** deletes linear coefficient at given position from nonlinear constraint data */
1399 static
delLinearCoefPos(SCIP * scip,SCIP_CONS * cons,int pos)1400 SCIP_RETCODE delLinearCoefPos(
1401    SCIP*                 scip,               /**< SCIP data structure */
1402    SCIP_CONS*            cons,               /**< nonlinear constraint */
1403    int                   pos                 /**< position of coefficient to delete */
1404    )
1405 {
1406    SCIP_CONSDATA* consdata;
1407    SCIP_VAR* var;
1408    SCIP_Real coef;
1409 
1410    assert(scip != NULL);
1411    assert(cons != NULL);
1412 
1413    consdata = SCIPconsGetData(cons);
1414    assert(consdata != NULL);
1415    assert(0 <= pos && pos < consdata->nlinvars);
1416 
1417    var  = consdata->linvars[pos];
1418    coef = consdata->lincoefs[pos];
1419    assert(var != NULL);
1420 
1421    /* remove rounding locks for deleted variable */
1422    SCIP_CALL( unlockLinearVariable(scip, cons, var, coef) );
1423 
1424    /* if constraint is enabled, drop the events on the variable */
1425    if( SCIPconsIsEnabled(cons) )
1426    {
1427       /* drop bound change events of variable */
1428       SCIP_CALL( dropLinearVarEvents(scip, cons, pos) );
1429    }
1430 
1431    /* release variable */
1432    SCIP_CALL( SCIPreleaseVar(scip, &consdata->linvars[pos]) );
1433 
1434    /* move the last variable to the free slot */
1435    consdataMoveLinearVar(consdata, consdata->nlinvars-1, pos);
1436 
1437    --consdata->nlinvars;
1438 
1439    /* invalidate activity */
1440    consdata->activity = SCIP_INVALID;
1441    consdata->minlinactivity = SCIP_INVALID;
1442    consdata->maxlinactivity = SCIP_INVALID;
1443    consdata->minlinactivityinf = -1;
1444    consdata->maxlinactivityinf = -1;
1445 
1446    /* invalidate nonlinear row */
1447    if( consdata->nlrow != NULL )
1448    {
1449       SCIP_CALL( SCIPreleaseNlRow(scip, &consdata->nlrow) );
1450    }
1451 
1452    consdata->ispresolved  = FALSE;
1453 
1454    return SCIP_OKAY;
1455 }
1456 
1457 /** changes linear coefficient value at given position of nonlinear constraint */
1458 static
chgLinearCoefPos(SCIP * scip,SCIP_CONS * cons,int pos,SCIP_Real newcoef)1459 SCIP_RETCODE chgLinearCoefPos(
1460    SCIP*                 scip,               /**< SCIP data structure */
1461    SCIP_CONS*            cons,               /**< nonlinear constraint */
1462    int                   pos,                /**< position of linear coefficient to change */
1463    SCIP_Real             newcoef             /**< new value of linear coefficient */
1464    )
1465 {
1466    SCIP_CONSDATA* consdata;
1467    SCIP_VAR* var;
1468    SCIP_Real coef;
1469    SCIP_Bool locked;
1470    int i;
1471 
1472    assert(scip != NULL);
1473    assert(cons != NULL);
1474    assert(!SCIPisZero(scip, newcoef));
1475 
1476    consdata = SCIPconsGetData(cons);
1477    assert(consdata != NULL);
1478    assert(0 <= pos);
1479    assert(pos < consdata->nlinvars);
1480    assert(!SCIPisZero(scip, newcoef));
1481 
1482    var = consdata->linvars[pos];
1483    coef = consdata->lincoefs[pos];
1484    assert(var != NULL);
1485    assert(SCIPconsIsTransformed(cons) == SCIPvarIsTransformed(var));
1486 
1487    /* invalidate activity */
1488    consdata->activity = SCIP_INVALID;
1489    consdata->minlinactivity = SCIP_INVALID;
1490    consdata->maxlinactivity = SCIP_INVALID;
1491    consdata->minlinactivityinf = -1;
1492    consdata->maxlinactivityinf = -1;
1493 
1494    /* invalidate nonlinear row */
1495    if( consdata->nlrow != NULL )
1496    {
1497       SCIP_CALL( SCIPreleaseNlRow(scip, &consdata->nlrow) );
1498    }
1499 
1500    locked = FALSE;
1501    for( i = 0; i < NLOCKTYPES && !locked; i++ )
1502       locked = SCIPconsIsLockedType(cons, (SCIP_LOCKTYPE) i);
1503 
1504    /* if necessary, remove the rounding locks and event catching of the variable */
1505    if( newcoef * coef < 0.0 )
1506    {
1507       if( locked )
1508       {
1509          assert(SCIPconsIsTransformed(cons));
1510 
1511          /* remove rounding locks for variable with old coefficient */
1512          SCIP_CALL( unlockLinearVariable(scip, cons, var, coef) );
1513       }
1514 
1515       if( SCIPconsIsEnabled(cons) )
1516       {
1517          /* drop bound change events of variable */
1518          SCIP_CALL( dropLinearVarEvents(scip, cons, pos) );
1519       }
1520    }
1521 
1522    /* change the coefficient */
1523    consdata->lincoefs[pos] = newcoef;
1524 
1525    /* if necessary, install the rounding locks and event catching of the variable again */
1526    if( newcoef * coef < 0.0 )
1527    {
1528       if( locked )
1529       {
1530          /* install rounding locks for variable with new coefficient */
1531          SCIP_CALL( lockLinearVariable(scip, cons, var, newcoef) );
1532       }
1533 
1534       if( SCIPconsIsEnabled(cons) )
1535       {
1536          /* catch bound change events of variable */
1537          SCIP_CALL( catchLinearVarEvents(scip, cons, pos) );
1538       }
1539    }
1540 
1541    consdata->ispresolved = FALSE;
1542 
1543    return SCIP_OKAY;
1544 }
1545 
1546 /** changes side of constraint and allow to change between finite and infinite
1547  *
1548  * takes care of updating events and locks of linear variables
1549  */
1550 static
chgSideNonlinear(SCIP * scip,SCIP_CONS * cons,SCIP_SIDETYPE side,SCIP_Real sideval)1551 SCIP_RETCODE chgSideNonlinear(
1552    SCIP*                 scip,               /**< SCIP data structure */
1553    SCIP_CONS*            cons,               /**< constraint */
1554    SCIP_SIDETYPE         side,               /**< which side to change */
1555    SCIP_Real             sideval             /**< new value for side */
1556    )
1557 {
1558    SCIP_CONSDATA* consdata;
1559    int i;
1560 
1561    assert(scip != NULL);
1562    assert(cons != NULL);
1563    assert(!SCIPisInfinity(scip, side == SCIP_SIDETYPE_LEFT ? sideval : -sideval));
1564 
1565    consdata = SCIPconsGetData(cons);
1566    assert(consdata != NULL);
1567 
1568    /* if remaining finite or remaining infinite, then can just update the value */
1569    if( side == SCIP_SIDETYPE_LEFT )
1570    {
1571       if( SCIPisInfinity(scip, -consdata->lhs) == SCIPisInfinity(scip, -sideval) )
1572       {
1573          consdata->lhs = sideval;
1574          return SCIP_OKAY;
1575       }
1576    }
1577    else
1578    {
1579       if( SCIPisInfinity(scip, consdata->rhs) == SCIPisInfinity(scip, sideval) )
1580       {
1581          consdata->rhs = sideval;
1582          return SCIP_OKAY;
1583       }
1584    }
1585 
1586    /* catched boundchange events and locks for linear variables depends on whether side is finite, so first drop all */
1587    for( i = 0; i < consdata->nlinvars; ++i )
1588    {
1589       int j;
1590       if( SCIPconsIsEnabled(cons) )
1591       {
1592          SCIP_CALL( dropLinearVarEvents(scip, cons, i) );
1593       }
1594 
1595       for( j = 0; j < NLOCKTYPES; j++ )
1596       {
1597          if( SCIPconsIsLockedType(cons, (SCIP_LOCKTYPE) j) )
1598          {
1599             assert(SCIPconsIsTransformed(cons));
1600             SCIP_CALL( unlockLinearVariable(scip, cons, consdata->linvars[i], consdata->lincoefs[i]) );
1601             break;
1602          }
1603       }
1604    }
1605 
1606    if( side == SCIP_SIDETYPE_LEFT )
1607       consdata->lhs = sideval;
1608    else
1609       consdata->rhs = sideval;
1610 
1611    /* catch boundchange events and locks on variables again */
1612    for( i = 0; i < consdata->nlinvars; ++i )
1613    {
1614       int j;
1615       if( SCIPconsIsEnabled(cons) )
1616       {
1617          SCIP_CALL( catchLinearVarEvents(scip, cons, i) );
1618       }
1619 
1620       for( j = 0; j < NLOCKTYPES; j++ )
1621       {
1622          if( SCIPconsIsLockedType(cons, (SCIP_LOCKTYPE) j) )
1623          {
1624             SCIP_CALL( lockLinearVariable(scip, cons, consdata->linvars[i], consdata->lincoefs[i]) );
1625             break;
1626          }
1627       }
1628    }
1629 
1630    return SCIP_OKAY;
1631 }
1632 
1633 /* merges entries with same linear variable into one entry and cleans up entries with coefficient 0.0 */
1634 static
mergeAndCleanLinearVars(SCIP * scip,SCIP_CONS * cons)1635 SCIP_RETCODE mergeAndCleanLinearVars(
1636    SCIP*                 scip,               /**< SCIP data structure */
1637    SCIP_CONS*            cons                /**< nonlinear constraint */
1638    )
1639 {
1640    SCIP_CONSDATA* consdata;
1641    SCIP_Real newcoef;
1642    int i;
1643    int j;
1644 
1645    assert(scip != NULL);
1646    assert(cons != NULL);
1647 
1648    consdata = SCIPconsGetData(cons);
1649 
1650    if( consdata->linvarsmerged )
1651       return SCIP_OKAY;
1652 
1653    if( consdata->nlinvars == 0 )
1654    {
1655       consdata->linvarsmerged = TRUE;
1656       return SCIP_OKAY;
1657    }
1658 
1659    i = 0;
1660    while( i < consdata->nlinvars )
1661    {
1662       /* make sure linear variables are sorted (do this in every round, since we may move variables around) */
1663       consdataSortLinearVars(consdata);
1664 
1665       /* sum up coefficients that correspond to variable i */
1666       newcoef = consdata->lincoefs[i];
1667       for( j = i+1; j < consdata->nlinvars && consdata->linvars[i] == consdata->linvars[j]; ++j )
1668          newcoef += consdata->lincoefs[j];
1669       /* delete the additional variables in backward order */
1670       for( j = j-1; j > i; --j )
1671       {
1672          SCIP_CALL( delLinearCoefPos(scip, cons, j) );
1673       }
1674 
1675       /* delete also entry at position i, if it became zero (or was zero before) */
1676       if( SCIPisZero(scip, newcoef) )
1677       {
1678          SCIP_CALL( delLinearCoefPos(scip, cons, i) );
1679       }
1680       else
1681       {
1682          SCIP_CALL( chgLinearCoefPos(scip, cons, i, newcoef) );
1683          ++i;
1684       }
1685    }
1686 
1687    consdata->linvarsmerged = TRUE;
1688 
1689    return SCIP_OKAY;
1690 }
1691 
1692 /** removes fixes (or aggregated) linear variables from a nonlinear constraint */
1693 static
removeFixedLinearVariables(SCIP * scip,SCIP_CONS * cons)1694 SCIP_RETCODE removeFixedLinearVariables(
1695    SCIP*                 scip,               /**< SCIP data structure */
1696    SCIP_CONS*            cons                /**< nonlinearconstraint */
1697    )
1698 {
1699    SCIP_CONSDATA* consdata;
1700    SCIP_Real coef;
1701    SCIP_Real offset;
1702    SCIP_VAR* var;
1703    int i;
1704    int j;
1705 
1706    assert(scip != NULL);
1707    assert(cons != NULL);
1708 
1709    consdata = SCIPconsGetData(cons);
1710 
1711    if( !consdata->isremovedfixingslin )
1712    {
1713       i = 0;
1714       while( i < consdata->nlinvars )
1715       {
1716          var = consdata->linvars[i];
1717 
1718          if( SCIPvarIsActive(var) )
1719          {
1720             ++i;
1721             continue;
1722          }
1723 
1724          coef = consdata->lincoefs[i];
1725          offset = 0.0;
1726 
1727          SCIP_CALL( SCIPgetProbvarSum(scip, &var, &coef, &offset) );
1728 
1729          SCIPdebugMsg(scip, "  linear term %g*<%s> is replaced by %g * <%s> + %g\n", consdata->lincoefs[i], SCIPvarGetName(consdata->linvars[i]), coef, SCIPvarGetName(var), offset);
1730 
1731          /* delete previous variable (this will move another variable to position i) */
1732          SCIP_CALL( delLinearCoefPos(scip, cons, i) );
1733 
1734          /* put constant part into bounds */
1735          if( offset != 0.0 )
1736          {
1737             if( !SCIPisInfinity(scip, -consdata->lhs) )
1738             {
1739                SCIP_CALL( chgSideNonlinear(scip, cons, SCIP_SIDETYPE_LEFT, consdata->lhs - offset) );
1740             }
1741             if( !SCIPisInfinity(scip,  consdata->rhs) )
1742             {
1743                SCIP_CALL( chgSideNonlinear(scip, cons, SCIP_SIDETYPE_RIGHT, consdata->rhs - offset) );
1744             }
1745          }
1746 
1747          /* nothing left to do if variable had been fixed */
1748          if( coef == 0.0 )
1749             continue;
1750 
1751          /* if GetProbvar gave a linear variable, just add it
1752           * if it's a multilinear variable, add it's disaggregated variables */
1753          if( SCIPvarIsActive(var) )
1754          {
1755             SCIP_CALL( addLinearCoef(scip, cons, var, coef) );
1756          }
1757          else
1758          {
1759             int        naggrs;
1760             SCIP_VAR** aggrvars;
1761             SCIP_Real* aggrscalars;
1762             SCIP_Real  aggrconstant;
1763 
1764             assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR);
1765 
1766             naggrs = SCIPvarGetMultaggrNVars(var);
1767             aggrvars = SCIPvarGetMultaggrVars(var);
1768             aggrscalars = SCIPvarGetMultaggrScalars(var);
1769             aggrconstant = SCIPvarGetMultaggrConstant(var);
1770 
1771             SCIP_CALL( consdataEnsureLinearVarsSize(scip, consdata, consdata->nlinvars + naggrs) );
1772 
1773             for( j = 0; j < naggrs; ++j )
1774             {
1775                SCIP_CALL( addLinearCoef(scip, cons, aggrvars[j], coef * aggrscalars[j]) );
1776             }
1777 
1778             if( aggrconstant != 0.0 )
1779             {
1780                if( !SCIPisInfinity(scip, -consdata->lhs) )
1781                {
1782                   consdata->lhs -= coef * aggrconstant;
1783                   assert(!SCIPisInfinity(scip, REALABS(consdata->lhs)));
1784                }
1785                if( !SCIPisInfinity(scip,  consdata->rhs) )
1786                {
1787                   consdata->rhs -= coef * aggrconstant;
1788                   assert(!SCIPisInfinity(scip, REALABS(consdata->rhs)));
1789                }
1790             }
1791          }
1792       }
1793 
1794       SCIP_CALL( mergeAndCleanLinearVars(scip, cons) );
1795 
1796       consdata->isremovedfixingslin = TRUE;
1797    }
1798 
1799    SCIPdebugMsg(scip, "removed fixations of linear variables from <%s>\n  -> ", SCIPconsGetName(cons));
1800    SCIPdebugPrintCons(scip, cons, NULL);
1801 
1802 #ifndef NDEBUG
1803    for( i = 0; i < consdata->nlinvars; ++i )
1804       assert(SCIPvarIsActive(consdata->linvars[i]));
1805 #endif
1806 
1807    return SCIP_OKAY;
1808 }
1809 
1810 /** removes fixed variables from expression graph */
1811 static
removeFixedNonlinearVariables(SCIP * scip,SCIP_CONSHDLR * conshdlr)1812 SCIP_RETCODE removeFixedNonlinearVariables(
1813    SCIP*                 scip,               /**< SCIP data structure */
1814    SCIP_CONSHDLR*        conshdlr            /**< constraint handler */
1815    )
1816 {
1817    SCIP_CONSHDLRDATA* conshdlrdata;
1818    SCIP_VAR* var;
1819    SCIP_VAR** vars;
1820    SCIP_Real* coefs;
1821    int nvars;
1822    int varssize;
1823    SCIP_Real constant;
1824    int i;
1825    int requsize;
1826    SCIPdebug( int j );
1827 
1828    conshdlrdata = SCIPconshdlrGetData(conshdlr);
1829    assert(conshdlrdata != NULL);
1830    assert(conshdlrdata->exprgraph != NULL);
1831 
1832    if( conshdlrdata->isremovedfixings )
1833       return SCIP_OKAY;
1834 
1835    varssize = 5;
1836    SCIP_CALL( SCIPallocBufferArray(scip, &vars,  varssize) );
1837    SCIP_CALL( SCIPallocBufferArray(scip, &coefs, varssize) );
1838 
1839    i = 0;
1840    while( i < SCIPexprgraphGetNVars(conshdlrdata->exprgraph) )
1841    {
1842       var = (SCIP_VAR*)SCIPexprgraphGetVars(conshdlrdata->exprgraph)[i];
1843       if( SCIPvarIsActive(var) )
1844       {
1845          ++i;
1846          continue;
1847       }
1848 
1849       vars[0]  = var;
1850       coefs[0] = 1.0;
1851       constant = 0.0;
1852       nvars = 1;
1853       SCIP_CALL( SCIPgetProbvarLinearSum(scip, vars, coefs, &nvars, varssize, &constant, &requsize, TRUE) );
1854 
1855       if( requsize > varssize )
1856       {
1857          SCIP_CALL( SCIPreallocBufferArray(scip, &vars,  requsize) );
1858          SCIP_CALL( SCIPreallocBufferArray(scip, &coefs, requsize) );
1859          varssize = requsize;
1860 
1861          SCIP_CALL( SCIPgetProbvarLinearSum(scip, vars, coefs, &nvars, varssize, &constant, &requsize, TRUE) );
1862       }
1863 
1864 #ifdef SCIP_DEBUG
1865       SCIPdebugMsg(scip, "replace fixed variable <%s> by %g", SCIPvarGetName(var), constant);
1866       for( j = 0; j < nvars; ++j )
1867       {
1868          SCIPdebugMsgPrint(scip, " %+g <%s>", coefs[j], SCIPvarGetName(vars[j]));
1869       }
1870       SCIPdebugMsgPrint(scip, "\n");
1871 #endif
1872 
1873       SCIP_CALL( SCIPexprgraphReplaceVarByLinearSum(conshdlrdata->exprgraph, var, nvars, coefs, (void**)vars, constant) );
1874 
1875       i = 0;
1876    }
1877 
1878    SCIPfreeBufferArray(scip, &coefs);
1879    SCIPfreeBufferArray(scip, &vars);
1880 
1881    conshdlrdata->isremovedfixings = TRUE;
1882 
1883    return SCIP_OKAY;
1884 }
1885 
1886 /** moves constant and linear part from expression graph node into constraint sides and linear part
1887  * frees expression graph node if expression is constant or linear */
1888 static
splitOffLinearPart(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS * cons,SCIP_Bool * infeasible)1889 SCIP_RETCODE splitOffLinearPart(
1890    SCIP*                 scip,               /**< SCIP data structure */
1891    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
1892    SCIP_CONS*            cons,               /**< nonlinear constraint */
1893    SCIP_Bool*            infeasible          /**< pointer to store whether the problem is infeasible or not */
1894    )
1895 {
1896    SCIP_CONSHDLRDATA* conshdlrdata;
1897    SCIP_CONSDATA* consdata;
1898    SCIP_VAR** linvars;
1899    SCIP_Real* lincoefs;
1900    SCIP_Real constant;
1901    int linvarssize;
1902    int nlinvars;
1903    int i;
1904 
1905    assert(scip != NULL);
1906    assert(conshdlr != NULL);
1907    assert(cons != NULL);
1908 
1909    consdata = SCIPconsGetData(cons);
1910    assert(consdata != NULL);
1911 
1912    *infeasible = FALSE;
1913 
1914    if( consdata->exprgraphnode == NULL )
1915       return SCIP_OKAY;
1916 
1917    conshdlrdata = SCIPconshdlrGetData(conshdlr);
1918    assert(conshdlrdata != NULL);
1919    assert(conshdlrdata->exprgraph != NULL);
1920 
1921    /* number of children of expression graph node is a good upper estimate on number of linear variables */
1922    linvarssize = MAX(SCIPexprgraphGetNodeNChildren(consdata->exprgraphnode), 1);  /*lint !e666*/
1923    SCIP_CALL( SCIPallocBufferArray(scip, &linvars,  linvarssize) );
1924    SCIP_CALL( SCIPallocBufferArray(scip, &lincoefs, linvarssize) );
1925 
1926    /* get linear and constant part from expression graph node
1927     * releases expression graph node if not uses otherwise */
1928    SCIP_CALL( SCIPexprgraphNodeSplitOffLinear(conshdlrdata->exprgraph, &consdata->exprgraphnode, linvarssize, &nlinvars, (void**)linvars, lincoefs, &constant) );
1929 
1930    if( SCIPisInfinity(scip, constant) )
1931    {
1932       if( !SCIPisInfinity(scip, -consdata->lhs) )
1933       {
1934          /* setting constraint lhs to -infinity; this may change linear variable locks and events */
1935          for( i = 0; i < consdata->nlinvars; ++i )
1936          {
1937             SCIP_Bool locked = FALSE;
1938             int j;
1939 
1940             for( j = 0; j < NLOCKTYPES && !locked; j++ )
1941                locked = SCIPconsIsLockedType(cons, (SCIP_LOCKTYPE) j);
1942 
1943             if( locked )
1944             {
1945                SCIP_CALL( unlockLinearVariable(scip, cons, consdata->linvars[i], consdata->lincoefs[i]) );
1946             }
1947             if( SCIPconsIsEnabled(cons) )
1948             {
1949                SCIP_CALL( dropLinearVarEvents(scip, cons, i) );
1950             }
1951          }
1952 
1953          consdata->lhs = -SCIPinfinity(scip);
1954 
1955          for( i = 0; i < consdata->nlinvars; ++i )
1956          {
1957             SCIP_Bool locked = FALSE;
1958             int j;
1959 
1960             for( j = 0; j < NLOCKTYPES && !locked; j++ )
1961                locked = SCIPconsIsLockedType(cons, (SCIP_LOCKTYPE) j);
1962 
1963             if( SCIPconsIsEnabled(cons) )
1964             {
1965                SCIP_CALL( catchLinearVarEvents(scip, cons, i) );
1966             }
1967             if( locked )
1968             {
1969                SCIP_CALL( lockLinearVariable(scip, cons, consdata->linvars[i], consdata->lincoefs[i]) );
1970             }
1971          }
1972       }
1973 
1974       if( !SCIPisInfinity(scip, consdata->rhs) )
1975       {
1976          *infeasible = TRUE;
1977          goto TERMINATE;
1978       }
1979    }
1980    else if( SCIPisInfinity(scip, -constant) )
1981    {
1982       if( !SCIPisInfinity(scip, consdata->rhs) )
1983       {
1984          /* setting constraint rhs to infinity; this may change linear variable locks and events */
1985          for( i = 0; i < consdata->nlinvars; ++i )
1986          {
1987             SCIP_Bool locked = FALSE;
1988             int j;
1989 
1990             for( j = 0; j < NLOCKTYPES && !locked; j++ )
1991                locked = SCIPconsIsLockedType(cons, (SCIP_LOCKTYPE) j);
1992 
1993             if( locked )
1994             {
1995                SCIP_CALL( unlockLinearVariable(scip, cons, consdata->linvars[i], consdata->lincoefs[i]) );
1996             }
1997             if( SCIPconsIsEnabled(cons) )
1998             {
1999                SCIP_CALL( dropLinearVarEvents(scip, cons, i) );
2000             }
2001          }
2002 
2003          consdata->rhs = SCIPinfinity(scip);
2004 
2005          for( i = 0; i < consdata->nlinvars; ++i )
2006          {
2007             SCIP_Bool locked = FALSE;
2008             int j;
2009 
2010             for( j = 0; j < NLOCKTYPES && !locked; j++ )
2011                locked = SCIPconsIsLockedType(cons, (SCIP_LOCKTYPE) j);
2012 
2013             if( SCIPconsIsEnabled(cons) )
2014             {
2015                SCIP_CALL( catchLinearVarEvents(scip, cons, i) );
2016             }
2017             if( locked )
2018             {
2019                SCIP_CALL( lockLinearVariable(scip, cons, consdata->linvars[i], consdata->lincoefs[i]) );
2020             }
2021          }
2022       }
2023       if( !SCIPisInfinity(scip, -consdata->lhs) )
2024       {
2025          *infeasible = TRUE;
2026          goto TERMINATE;
2027       }
2028    }
2029    else if( constant != 0.0 )
2030    {
2031       if( !SCIPisInfinity(scip, -consdata->lhs) )
2032       {
2033          consdata->lhs -= constant;
2034          assert(!SCIPisInfinity(scip, REALABS(consdata->lhs)));
2035       }
2036       if( !SCIPisInfinity(scip,  consdata->rhs) )
2037       {
2038          consdata->rhs -= constant;
2039          assert(!SCIPisInfinity(scip, REALABS(consdata->rhs)));
2040       }
2041    }
2042 
2043 TERMINATE:
2044    for( i = 0; i < nlinvars; ++i )
2045    {
2046       SCIP_CALL( addLinearCoef(scip, cons, linvars[i], lincoefs[i]) );
2047    }
2048 
2049    SCIPfreeBufferArray(scip, &lincoefs);
2050    SCIPfreeBufferArray(scip, &linvars);
2051 
2052    /* @todo linear variables that are also children of exprgraphnode could be moved into the expression graph for certain nonlinear operators (quadratic, polynomial), since that may allow better bound tightening */
2053 
2054    return SCIP_OKAY;
2055 }
2056 
2057 /** create a nonlinear row representation of the constraint and stores them in consdata */
2058 static
createNlRow(SCIP * scip,SCIP_CONS * cons)2059 SCIP_RETCODE createNlRow(
2060    SCIP*                 scip,               /**< SCIP data structure */
2061    SCIP_CONS*            cons                /**< nonlinear constraint */
2062    )
2063 {
2064    SCIP_CONSDATA* consdata;
2065 
2066    assert(scip != NULL);
2067    assert(cons != NULL);
2068 
2069    consdata = SCIPconsGetData(cons);
2070    assert(consdata != NULL);
2071 
2072    if( consdata->nlrow != NULL )
2073    {
2074       SCIP_CALL( SCIPreleaseNlRow(scip, &consdata->nlrow) );
2075    }
2076 
2077    if( consdata->nexprtrees == 0 )
2078    {
2079       SCIP_CALL( SCIPcreateNlRow(scip, &consdata->nlrow, SCIPconsGetName(cons), 0.0,
2080             consdata->nlinvars, consdata->linvars, consdata->lincoefs,
2081             0, NULL, 0, NULL,
2082             NULL, consdata->lhs, consdata->rhs,
2083             consdata->curvature) );
2084    }
2085    else if( consdata->nexprtrees == 1 && consdata->nonlincoefs[0] == 1.0 )
2086    {
2087       assert(consdata->exprtrees[0] != NULL);
2088       SCIP_CALL( SCIPcreateNlRow(scip, &consdata->nlrow, SCIPconsGetName(cons), 0.0,
2089             consdata->nlinvars, consdata->linvars, consdata->lincoefs,
2090             0, NULL, 0, NULL,
2091             consdata->exprtrees[0], consdata->lhs, consdata->rhs,
2092             consdata->curvature) );
2093    }
2094    else
2095    {
2096       /* since expression trees may share variable, we cannot easily sum them up,
2097        * but we can request a single expression tree from the expression graph
2098        */
2099       SCIP_CONSHDLRDATA* conshdlrdata;
2100       SCIP_EXPRTREE* exprtree;
2101 
2102       assert(consdata->exprgraphnode != NULL); /* since nexprtrees > 0 */
2103       conshdlrdata = SCIPconshdlrGetData(SCIPconsGetHdlr(cons));
2104       assert(conshdlrdata != NULL);
2105 
2106       SCIP_CALL( SCIPexprgraphGetTree(conshdlrdata->exprgraph, consdata->exprgraphnode, &exprtree) );
2107       SCIP_CALL( SCIPcreateNlRow(scip, &consdata->nlrow, SCIPconsGetName(cons), 0.0,
2108             consdata->nlinvars, consdata->linvars, consdata->lincoefs,
2109             0, NULL, 0, NULL,
2110             exprtree, consdata->lhs, consdata->rhs,
2111             consdata->curvature) );
2112       SCIP_CALL( SCIPexprtreeFree(&exprtree) );
2113    }
2114 
2115    return SCIP_OKAY;
2116 }
2117 
2118 /** tries to automatically convert a nonlinear constraint (or a part of it) into a more specific and more specialized constraint */
2119 static
presolveUpgrade(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS * cons,SCIP_Bool * upgraded,int * nupgdconss,int * naddconss)2120 SCIP_RETCODE presolveUpgrade(
2121    SCIP*                 scip,               /**< SCIP data structure */
2122    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler data structure */
2123    SCIP_CONS*            cons,               /**< source constraint to try to convert */
2124    SCIP_Bool*            upgraded,           /**< buffer to store whether constraint was upgraded */
2125    int*                  nupgdconss,         /**< buffer to increase if constraint was upgraded */
2126    int*                  naddconss           /**< buffer to increase with number of additional constraints created during upgrade */
2127    )
2128 {
2129    SCIP_CONSHDLRDATA* conshdlrdata;
2130    SCIP_CONS** upgdconss;
2131    int upgdconsssize;
2132    int nupgdconss_;
2133    int i;
2134 
2135    assert(scip != NULL);
2136    assert(conshdlr != NULL);
2137    assert(cons != NULL);
2138    assert(!SCIPconsIsModifiable(cons));
2139    assert(upgraded   != NULL);
2140    assert(nupgdconss != NULL);
2141    assert(naddconss  != NULL);
2142 
2143    *upgraded = FALSE;
2144 
2145    nupgdconss_ = 0;
2146 
2147    conshdlrdata = SCIPconshdlrGetData(conshdlr);
2148    assert(conshdlrdata != NULL);
2149 
2150    /* if there are no upgrade methods, we can stop */
2151    if( conshdlrdata->nnlconsupgrades == 0 )
2152       return SCIP_OKAY;
2153 
2154    /* set debug solution in expression graph and evaluate nodes, so reformulation methods can compute debug solution values for new auxiliary variables */
2155 #ifdef WITH_DEBUG_SOLUTION
2156    if( SCIPdebugIsMainscip(scip) )
2157    {
2158       SCIP_Real* varvals;
2159 
2160       SCIP_CALL( SCIPallocBufferArray(scip, &varvals, SCIPexprgraphGetNVars(conshdlrdata->exprgraph)) );
2161 
2162       for( i = 0; i < SCIPexprgraphGetNVars(conshdlrdata->exprgraph); ++i )
2163          SCIP_CALL( SCIPdebugGetSolVal(scip, (SCIP_VAR*)SCIPexprgraphGetVars(conshdlrdata->exprgraph)[i], &varvals[i]) );
2164 
2165       SCIP_CALL( SCIPexprgraphEval(conshdlrdata->exprgraph, varvals) );
2166 
2167       SCIPfreeBufferArray(scip, &varvals);
2168    }
2169 #endif
2170 
2171    upgdconsssize = 2;
2172    SCIP_CALL( SCIPallocBufferArray(scip, &upgdconss, upgdconsssize) );
2173 
2174    /* call the upgrading methods */
2175 
2176    SCIPdebugMsg(scip, "upgrading nonlinear constraint <%s> (up to %d upgrade methods):\n",
2177       SCIPconsGetName(cons), conshdlrdata->nnlconsupgrades);
2178    SCIPdebugPrintCons(scip, cons, NULL);
2179 
2180    /* try all upgrading methods in priority order in case the upgrading step is enable  */
2181    for( i = 0; i < conshdlrdata->nnlconsupgrades; ++i )
2182    {
2183       if( !conshdlrdata->nlconsupgrades[i]->active )
2184          continue;
2185       if( conshdlrdata->nlconsupgrades[i]->nlconsupgd == NULL )
2186          continue;
2187 
2188       SCIP_CALL( conshdlrdata->nlconsupgrades[i]->nlconsupgd(scip, cons, &nupgdconss_, upgdconss, upgdconsssize) );
2189 
2190       while( nupgdconss_ < 0 )
2191       {
2192          /* upgrade function requires more memory: resize upgdconss and call again */
2193          assert(-nupgdconss_ > upgdconsssize);
2194          upgdconsssize = -nupgdconss_;
2195          SCIP_CALL( SCIPreallocBufferArray(scip, &upgdconss, -nupgdconss_) );
2196 
2197          SCIP_CALL( conshdlrdata->nlconsupgrades[i]->nlconsupgd(scip, cons, &nupgdconss_, upgdconss, upgdconsssize) );
2198 
2199          assert(nupgdconss_ != 0);
2200       }
2201 
2202       if( nupgdconss_ > 0 )
2203       {
2204          /* got upgrade */
2205          SCIP_CONSDATA* consdata;
2206          int j;
2207 
2208          SCIPdebugMsg(scip, " -> upgraded to %d constraints:\n", nupgdconss_);
2209 
2210          /* add the upgraded constraints to the problem and forget them */
2211          for( j = 0; j < nupgdconss_; ++j )
2212          {
2213             SCIPdebugMsgPrint(scip, "\t");
2214             SCIPdebugPrintCons(scip, upgdconss[j], NULL);
2215 
2216             SCIP_CALL( SCIPaddCons(scip, upgdconss[j]) );      /*lint !e613*/
2217             SCIP_CALL( SCIPreleaseCons(scip, &upgdconss[j]) ); /*lint !e613*/
2218          }
2219 
2220          /* count the first upgrade constraint as constraint upgrade and the remaining ones as added constraints */
2221          *nupgdconss += 1;
2222          *naddconss += nupgdconss_ - 1;
2223          *upgraded = TRUE;
2224 
2225          /* delete upgraded constraint */
2226          SCIPdebugMsg(scip, "delete constraint <%s> after upgrade\n", SCIPconsGetName(cons));
2227          SCIP_CALL( SCIPdelCons(scip, cons) );
2228 
2229          /* make sure node is disabled now, so that reformulation within this presolve round does not work on it */
2230          consdata = SCIPconsGetData(cons);
2231          assert(consdata != NULL);
2232          if( consdata->exprgraphnode != NULL )
2233             SCIPexprgraphDisableNode(conshdlrdata->exprgraph, consdata->exprgraphnode);
2234 
2235          break;
2236       }
2237    }
2238 
2239    SCIPfreeBufferArray(scip, &upgdconss);
2240 
2241    return SCIP_OKAY;
2242 }
2243 
2244 /** checks a nonlinear constraint for convexity and/or concavity */
2245 static
checkCurvature(SCIP * scip,SCIP_CONS * cons,SCIP_Bool assumeconvex)2246 SCIP_RETCODE checkCurvature(
2247    SCIP*                 scip,               /**< SCIP data structure */
2248    SCIP_CONS*            cons,               /**< nonlinear constraint */
2249    SCIP_Bool             assumeconvex        /**< whether to assume convexity in inequalities */
2250    )
2251 {
2252    SCIP_CONSDATA* consdata;
2253    SCIP_INTERVAL* varbounds;
2254    int varboundssize;
2255    SCIP_VAR* var;
2256    int i;
2257    int j;
2258 
2259    assert(scip != NULL);
2260    assert(cons != NULL);
2261 
2262    consdata = SCIPconsGetData(cons);
2263    assert(consdata != NULL);
2264 
2265    if( consdata->iscurvchecked )
2266       return SCIP_OKAY;
2267 
2268    SCIPdebugMsg(scip, "Checking curvature of constraint <%s>\n", SCIPconsGetName(cons));
2269 
2270    consdata->curvature = SCIP_EXPRCURV_LINEAR;
2271    consdata->iscurvchecked = TRUE;
2272 
2273    varbounds = NULL;
2274    varboundssize = 0;
2275 
2276    for( i = 0; i < consdata->nexprtrees; ++i )
2277    {
2278       assert(consdata->exprtrees[i] != NULL);
2279       assert(SCIPexprtreeGetNVars(consdata->exprtrees[i]) > 0 );
2280 
2281       if( assumeconvex )
2282       {
2283          /* for constraints a*f(x) <= rhs, we assume that it is convex */
2284          if( SCIPisInfinity(scip, -consdata->lhs) )
2285             consdata->curvatures[i] = SCIP_EXPRCURV_CONVEX;
2286 
2287          /* for constraints lhs <= a*f(x), we assume that it is concave */
2288          if( SCIPisInfinity(scip,  consdata->rhs) )
2289             consdata->curvatures[i] = SCIP_EXPRCURV_CONCAVE;
2290       }
2291       else
2292       {
2293          if( varboundssize == 0 )
2294          {
2295             SCIP_CALL( SCIPallocBufferArray(scip, &varbounds, SCIPexprtreeGetNVars(consdata->exprtrees[i])) );
2296             varboundssize = SCIPexprtreeGetNVars(consdata->exprtrees[i]);
2297          }
2298          else if( varboundssize < SCIPexprtreeGetNVars(consdata->exprtrees[i]) )
2299          {
2300             SCIP_CALL( SCIPreallocBufferArray(scip, &varbounds, SCIPexprtreeGetNVars(consdata->exprtrees[i])) );
2301             varboundssize = SCIPexprtreeGetNVars(consdata->exprtrees[i]);
2302          }
2303          assert(varbounds != NULL);
2304 
2305          for( j = 0; j < SCIPexprtreeGetNVars(consdata->exprtrees[i]); ++j )
2306          {
2307             var = SCIPexprtreeGetVars(consdata->exprtrees[i])[j];
2308             SCIPintervalSetBounds(&varbounds[j],
2309                -infty2infty(SCIPinfinity(scip), INTERVALINFTY, -MIN(SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var))),    /*lint !e666*/
2310                +infty2infty(SCIPinfinity(scip), INTERVALINFTY,  MAX(SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var))) );  /*lint !e666*/
2311          }
2312 
2313          SCIP_CALL( SCIPexprtreeCheckCurvature(consdata->exprtrees[i], INTERVALINFTY, varbounds, &consdata->curvatures[i], NULL) );
2314          consdata->curvatures[i] = SCIPexprcurvMultiply(consdata->nonlincoefs[i], consdata->curvatures[i]);
2315 
2316          if( consdata->curvatures[i] == SCIP_EXPRCURV_UNKNOWN && SCIPconshdlrGetData(SCIPconsGetHdlr(cons))->isreformulated && SCIPexprGetOperator(SCIPexprtreeGetRoot(consdata->exprtrees[i])) != SCIP_EXPR_USER )
2317          {
2318             SCIPverbMessage(scip, SCIP_VERBLEVEL_NORMAL, NULL, "indefinite expression tree in constraint <%s>\n", SCIPconsGetName(cons));
2319             SCIPdebug( SCIP_CALL( SCIPexprtreePrintWithNames(consdata->exprtrees[i], SCIPgetMessagehdlr(scip), NULL) ) );
2320             SCIPdebugMsgPrint(scip, "\n");
2321          }
2322       }
2323 
2324       /* @todo implement some more expensive checks */
2325 
2326       consdata->curvature = SCIPexprcurvAdd(consdata->curvature, consdata->curvatures[i]);
2327 
2328       SCIPdebugMsg(scip, "-> tree %d with coef %g is %s -> nonlinear part is %s\n", i, consdata->nonlincoefs[i], SCIPexprcurvGetName(consdata->curvatures[i]), SCIPexprcurvGetName(consdata->curvature));
2329    }
2330 
2331    SCIPfreeBufferArrayNull(scip, &varbounds);
2332 
2333    return SCIP_OKAY;
2334 }  /*lint !e715*/
2335 
2336 /* replaces a node by another node in expression graph
2337  * moves all parents of node to replacement
2338  * replaces all exprgraphnode's in constraints that use node by replacement, except for the last "update constraints" many, because they may use node on purpose
2339  * node may be freed, if captured only by given constraints
2340  */
2341 static
reformReplaceNode(SCIP_EXPRGRAPH * exprgraph,SCIP_EXPRGRAPHNODE ** node,SCIP_EXPRGRAPHNODE * replacement,SCIP_CONSHDLR * conshdlr,int nskipupdatecons)2342 SCIP_RETCODE reformReplaceNode(
2343    SCIP_EXPRGRAPH*       exprgraph,          /**< expression graph */
2344    SCIP_EXPRGRAPHNODE**  node,               /**< pointer to node to be replaced in expression graph */
2345    SCIP_EXPRGRAPHNODE*   replacement,        /**< node which takes node's place */
2346    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
2347    int                   nskipupdatecons     /**< number of last update constraints to be skipped in check */
2348    )
2349 {
2350    SCIP_CONSDATA* consdata;
2351    int c;
2352 
2353    assert(exprgraph != NULL);
2354    assert(node != NULL);
2355    assert(*node != NULL);
2356    assert(replacement != NULL);
2357    assert(conshdlr != NULL);
2358    assert(nskipupdatecons <= SCIPconshdlrGetNUpdateConss(conshdlr));
2359 
2360    SCIP_CALL( SCIPexprgraphMoveNodeParents(exprgraph, node, replacement) );
2361 
2362    /* node was not captured by any constraint */
2363    if( *node == NULL )
2364       return SCIP_OKAY;
2365 
2366    /* if node still exists, then because it is captured by some constraint (it should not have parents anymore)
2367     * thus, look into all active constraints and also those that have been just added during reformulation (delayed activation)
2368     * and replace their exprgraphnode by replacement
2369     * @todo may be expensive when this is done more often
2370     */
2371    assert(*node == NULL || SCIPexprgraphGetNodeNParents(*node) == 0);
2372    for( c = 0; c < SCIPconshdlrGetNActiveConss(conshdlr) + SCIPconshdlrGetNUpdateConss(conshdlr) - nskipupdatecons; ++c )
2373    {
2374       SCIP_CONS* cons;
2375 
2376       if( c < SCIPconshdlrGetNActiveConss(conshdlr) )
2377          cons = SCIPconshdlrGetConss(conshdlr)[c];
2378       else
2379          cons = SCIPconshdlrGetUpdateConss(conshdlr)[c-SCIPconshdlrGetNActiveConss(conshdlr)];
2380 
2381       assert(cons != NULL);  /*lint !e613*/
2382 
2383       consdata = SCIPconsGetData(cons);  /*lint !e613*/
2384       assert(consdata != NULL);
2385 
2386       if( consdata->exprgraphnode == *node )
2387       {
2388          SCIP_CALL( SCIPexprgraphReleaseNode(exprgraph, &consdata->exprgraphnode) );
2389          consdata->exprgraphnode = replacement;
2390          SCIPexprgraphCaptureNode(replacement);
2391 
2392          /* since we change the node, also the constraint changes, so ensure that it is presolved again */
2393          consdata->ispresolved = FALSE;
2394       }
2395    }
2396    *node = NULL;
2397 
2398    return SCIP_OKAY;
2399 }
2400 
2401 /** creates a new auxiliary variable and a new auxiliary nonlinear constraint connecting the var and a given node
2402  * node is replaced by new new auxiliary variables node in all parents of node in expression graph and in all constraints that use node
2403  */
2404 static
reformNode2Var(SCIP * scip,SCIP_EXPRGRAPH * exprgraph,SCIP_EXPRGRAPHNODE * node,SCIP_CONSHDLR * conshdlr,int * naddcons,SCIP_Bool donotmultaggr)2405 SCIP_RETCODE reformNode2Var(
2406    SCIP*                 scip,               /**< SCIP data structure */
2407    SCIP_EXPRGRAPH*       exprgraph,          /**< expression graph */
2408    SCIP_EXPRGRAPHNODE*   node,               /**< expression graph node */
2409    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
2410    int*                  naddcons,           /**< counter to increase when constraint is added */
2411    SCIP_Bool             donotmultaggr       /**< whether to mark auxiliary variable as not to multiaggregate */
2412    )
2413 {
2414    char name[SCIP_MAXSTRLEN];
2415    SCIP_VAR* auxvar;
2416    SCIP_CONS* auxcons;
2417    SCIP_EXPRGRAPHNODE* auxvarnode;
2418    SCIP_INTERVAL bounds;
2419    SCIP_Real minusone;
2420    SCIP_Bool cutoff;
2421 
2422    assert(scip != NULL);
2423    assert(exprgraph != NULL);
2424    assert(node != NULL);
2425    assert(naddcons != NULL);
2426    assert(SCIPexprgraphGetNodeDepth(node) >= 1); /* do not turn vars or consts into new vars */
2427 
2428    bounds = SCIPexprgraphGetNodeBounds(node);
2429    (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "nlreform%d", *naddcons);
2430 
2431    SCIPdebugMsg(scip, "add auxiliary variable and constraint %s for node %p(%d,%d)\n", name, (void*)node, SCIPexprgraphGetNodeDepth(node), SCIPexprgraphGetNodePosition(node));
2432 
2433    SCIP_CALL( SCIPcreateVar(scip, &auxvar, name, SCIPintervalGetInf(bounds), SCIPintervalGetSup(bounds), 0.0,
2434          SCIP_VARTYPE_CONTINUOUS, TRUE, TRUE, NULL, NULL, NULL, NULL, NULL) );
2435    SCIP_CALL( SCIPaddVar(scip, auxvar) );
2436    SCIP_CALL( SCIPexprgraphAddVars(exprgraph, 1, (void**)&auxvar, &auxvarnode) );
2437 #ifdef WITH_DEBUG_SOLUTION
2438    if( SCIPdebugIsMainscip(scip) )
2439    {
2440       /* store debug sol value of node as value for auxvar in debug solution and as value for auxvarnode */
2441       SCIPexprgraphSetVarNodeValue(auxvarnode, SCIPexprgraphGetNodeVal(node));
2442       SCIP_CALL( SCIPdebugAddSolVal(scip, auxvar, SCIPexprgraphGetNodeVal(node)) );
2443    }
2444 #endif
2445 
2446    if( donotmultaggr )
2447    {
2448       SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, auxvar) );
2449    }
2450 
2451    /* set also bounds of auxvarnode to bounds, so it is available for new parent nodes (currently node->parents)
2452     * when updating their curvature information; avoid having to run domain propagation through exprgraph
2453     */
2454    SCIPexprgraphTightenNodeBounds(exprgraph, auxvarnode, bounds, BOUNDTIGHTENING_MINSTRENGTH, INTERVALINFTY, &cutoff);
2455    assert(!cutoff); /* we tightenend bounds from [-inf,+inf] to bounds, this should not be infeasible */
2456 
2457    /* add new constraint auxvar == node */
2458    minusone = -1.0;
2459    SCIP_CALL( SCIPcreateConsNonlinear2(scip, &auxcons, name, 1, &auxvar, &minusone, node, 0.0, 0.0, TRUE, TRUE, TRUE, TRUE, TRUE,
2460          FALSE, FALSE, FALSE, FALSE, FALSE) );
2461    SCIP_CALL( SCIPaddCons(scip, auxcons) );
2462 
2463    /* move parents of node in expression graph to auxvarnode
2464     * replace node by auxvarnode in constraints that use node */
2465    SCIP_CALL( reformReplaceNode(exprgraph, &node, auxvarnode, conshdlr, 1) );
2466 
2467    SCIP_CALL( SCIPreleaseCons(scip, &auxcons) );
2468    SCIP_CALL( SCIPreleaseVar(scip, &auxvar) );
2469 
2470    ++*naddcons;
2471 
2472    return SCIP_OKAY;
2473 }
2474 
2475 /** ensures that all children of a node have at least a given curvature by adding auxiliary variables */
2476 static
reformEnsureChildrenMinCurvature(SCIP * scip,SCIP_EXPRGRAPH * exprgraph,SCIP_EXPRGRAPHNODE * node,SCIP_EXPRCURV mincurv,SCIP_CONSHDLR * conshdlr,int * naddcons)2477 SCIP_RETCODE reformEnsureChildrenMinCurvature(
2478    SCIP*                 scip,               /**< SCIP data structure */
2479    SCIP_EXPRGRAPH*       exprgraph,          /**< expression graph */
2480    SCIP_EXPRGRAPHNODE*   node,               /**< expression graph node */
2481    SCIP_EXPRCURV         mincurv,            /**< minimal desired curvature */
2482    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
2483    int*                  naddcons            /**< counter to increase when constraint is added */
2484    )
2485 {
2486    SCIP_EXPRGRAPHNODE* child;
2487    SCIP_Bool needupdate;
2488 
2489    int i;
2490    assert(scip != NULL);
2491    assert(exprgraph != NULL);
2492    assert(node != NULL);
2493    assert(naddcons != NULL);
2494    assert(SCIPexprgraphGetNodeDepth(node) >= 1); /* do not turn vars or consts into new vars */
2495    assert(mincurv != SCIP_EXPRCURV_UNKNOWN); /* this is trivial and makes no sense */
2496 
2497    needupdate = FALSE; /* whether we need to update curvature of node */
2498 
2499    for( i = 0; i < SCIPexprgraphGetNodeNChildren(node); ++i )
2500    {
2501       child = SCIPexprgraphGetNodeChildren(node)[i];
2502       assert(child != NULL);
2503 
2504       if( (SCIPexprgraphGetNodeCurvature(child) & mincurv) != mincurv )
2505       {
2506          SCIPdebugMsg(scip, "add auxiliary variable for child %p(%d,%d) with curvature %s\n",
2507             (void*)child, SCIPexprgraphGetNodeDepth(child), SCIPexprgraphGetNodePosition(child), SCIPexprcurvGetName(SCIPexprgraphGetNodeCurvature(child)) );
2508 
2509          SCIP_CALL( reformNode2Var(scip, exprgraph, child, conshdlr, naddcons, FALSE) );
2510          needupdate = TRUE;
2511 
2512          /* i'th child of node should now be a variable */
2513          assert(SCIPexprgraphGetNodeChildren(node)[i] != child);
2514          assert(SCIPexprgraphGetNodeOperator(SCIPexprgraphGetNodeChildren(node)[i]) == SCIP_EXPR_VARIDX);
2515       }
2516 
2517       assert(SCIPexprgraphGetNodeCurvature(SCIPexprgraphGetNodeChildren(node)[i]) & mincurv);
2518    }
2519 
2520    if( needupdate )
2521    {
2522       SCIP_CALL( SCIPexprgraphUpdateNodeBoundsCurvature(node, INTERVALINFTY, BOUNDTIGHTENING_MINSTRENGTH, TRUE) );
2523       assert(!SCIPintervalIsEmpty(INTERVALINFTY, SCIPexprgraphGetNodeBounds(node)));
2524    }
2525 
2526    return SCIP_OKAY;
2527 }
2528 
2529 /** reformulates a monomial by adding auxiliary variables and constraints for bilinear terms */
2530 static
reformMonomial(SCIP * scip,SCIP_EXPRGRAPH * exprgraph,int nfactors,SCIP_EXPRGRAPHNODE ** factors,SCIP_Real * exponents,SCIP_EXPRGRAPHNODE ** resultnode,SCIP_Bool createauxcons,int mindepth,int * naddcons)2531 SCIP_RETCODE reformMonomial(
2532    SCIP*                 scip,               /**< SCIP data structure */
2533    SCIP_EXPRGRAPH*       exprgraph,          /**< expression graph */
2534    int                   nfactors,           /**< number of factors */
2535    SCIP_EXPRGRAPHNODE**  factors,            /**< factors */
2536    SCIP_Real*            exponents,          /**< exponents, or NULL if all 1.0 */
2537    SCIP_EXPRGRAPHNODE**  resultnode,         /**< buffer to store node which represents the reformulated monomial */
2538    SCIP_Bool             createauxcons,      /**< whether to create auxiliary var/cons */
2539    int                   mindepth,           /**< minimal depth of new nodes in expression graph, or -1 */
2540    int*                  naddcons            /**< buffer to increase by number of added cons */
2541    )
2542 {
2543    char name[SCIP_MAXSTRLEN];
2544    SCIP_VAR* auxvar;
2545    SCIP_CONS* auxcons;
2546    SCIP_Real minusone;
2547 
2548    assert(scip != NULL);
2549    assert(exprgraph != NULL);
2550    assert(nfactors > 0);
2551    assert(factors != NULL);
2552    assert(resultnode != NULL);
2553    assert(naddcons != NULL);
2554 
2555    /* factors are just one node */
2556    if( nfactors == 1 && (exponents == NULL || exponents[0] == 1.0) )
2557    {
2558       *resultnode = factors[0];
2559       return SCIP_OKAY;
2560    }
2561 
2562    /* only one factor, but with exponent < 0.0 and factor has mixed sign, e.g., x^(-3)
2563     * reformulate as auxvar * factor^(-exponent) = 1 and return the node for auxvar in resultnode
2564     */
2565    if( nfactors == 1 && exponents[0] < 0.0 && SCIPexprgraphGetNodeBounds(factors[0]).inf < 0.0 && SCIPexprgraphGetNodeBounds(factors[0]).sup > 0.0 )  /*lint !e613*/
2566    {
2567       SCIP_EXPRGRAPHNODE* auxnode;
2568       SCIP_EXPRGRAPHNODE* reformfactors[2];
2569       SCIP_Real reformexp[2];
2570 
2571       (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "nlreform%d", *naddcons);
2572       SCIPdebugMsg(scip, "add auxiliary variable and constraint %s\n", name);
2573 
2574       SCIP_CALL( SCIPcreateVar(scip, &auxvar, name, -SCIPinfinity(scip), SCIPinfinity(scip), 0.0,
2575             SCIP_VARTYPE_CONTINUOUS, TRUE, TRUE, NULL, NULL, NULL, NULL, NULL) );
2576       SCIP_CALL( SCIPaddVar(scip, auxvar) );
2577       SCIP_CALL( SCIPexprgraphAddVars(exprgraph, 1, (void**)&auxvar, resultnode) );
2578 
2579 #ifdef WITH_DEBUG_SOLUTION
2580       /* store debug sol value of node as value for auxvar in debug solution and as value for resultnode */
2581       if( SCIPdebugIsMainscip(scip) )
2582       {
2583          SCIP_Real debugval;
2584          debugval = pow(SCIPexprgraphGetNodeVal(factors[0]), exponents[0]);
2585          SCIPexprgraphSetVarNodeValue(*resultnode, debugval);
2586          SCIP_CALL( SCIPdebugAddSolVal(scip, auxvar, debugval) );
2587       }
2588 #endif
2589 
2590       /* increase naddcons before next call to reformMonomial, to avoid duplicate variable names, which is bad for debugging */
2591       ++*naddcons;
2592 
2593       /* add reformulation for resultnode(=auxvar) * factor^(-exponent) = 1.0
2594        * if exponent != -1.0, then factor^(-exponent) should be moved into extra variable
2595        * finally one should get an EXPR_MUL node */
2596       reformfactors[0] = *resultnode;
2597       reformfactors[1] = factors[0];
2598       reformexp[0] = 1.0;
2599       reformexp[1] = -exponents[0];  /*lint !e613*/
2600       SCIP_CALL( reformMonomial(scip, exprgraph, 2, reformfactors, reformexp, &auxnode, FALSE, mindepth, naddcons) );
2601 
2602       SCIP_CALL( SCIPcreateConsNonlinear2(scip, &auxcons, name, 0, NULL, NULL, auxnode, 1.0, 1.0,
2603             TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
2604       SCIP_CALL( SCIPaddCons(scip, auxcons) );
2605 
2606       SCIP_CALL( SCIPreleaseCons(scip, &auxcons) );
2607       SCIP_CALL( SCIPreleaseVar(scip, &auxvar) );
2608 
2609       return SCIP_OKAY;
2610    }
2611 
2612    /* only one factor, but with exponent != 1.0 */
2613    if( nfactors == 1 )
2614    {
2615       /* create some power expression node, if not existing already */
2616       SCIP_EXPRGRAPHNODE* expnode;
2617       SCIP_EXPRGRAPHNODE* parent;
2618       int p;
2619 
2620       assert(exponents != NULL);
2621 
2622       /* check if there is already a node for factors[0]^exponents[0] */
2623       expnode = NULL;
2624       for( p = 0; p < SCIPexprgraphGetNodeNParents(factors[0]); ++p)
2625       {
2626          parent = SCIPexprgraphGetNodeParents(factors[0])[p];
2627          if( SCIPisIntegral(scip, exponents[0]) &&
2628             SCIPexprgraphGetNodeOperator(parent) == SCIP_EXPR_INTPOWER &&
2629             SCIPexprgraphGetNodeIntPowerExponent(parent) == (int)SCIPround(scip, exponents[0]) )
2630          {
2631             expnode = parent;
2632             break;
2633          }
2634          if( SCIPexprgraphGetNodeOperator(parent) == SCIP_EXPR_REALPOWER &&
2635             SCIPisEQ(scip, SCIPexprgraphGetNodeRealPowerExponent(parent), exponents[0]) )
2636          {
2637             expnode = parent;
2638          }
2639       }
2640       if( expnode == NULL )
2641       {
2642          if( SCIPisIntegral(scip, exponents[0]) )
2643             SCIP_CALL( SCIPexprgraphCreateNode(SCIPblkmem(scip), &expnode, SCIP_EXPR_INTPOWER, (int)SCIPround(scip, exponents[0])) );
2644          else
2645             SCIP_CALL( SCIPexprgraphCreateNode(SCIPblkmem(scip), &expnode, SCIP_EXPR_REALPOWER, exponents[0]) );
2646 
2647          SCIP_CALL( SCIPexprgraphAddNode(exprgraph, expnode, mindepth, 1, &factors[0]) );
2648          SCIP_CALL( SCIPexprgraphUpdateNodeBoundsCurvature(expnode, INTERVALINFTY, BOUNDTIGHTENING_MINSTRENGTH, TRUE) );
2649          assert(!SCIPintervalIsEmpty(INTERVALINFTY, SCIPexprgraphGetNodeBounds(expnode)));
2650       }
2651 
2652       if( createauxcons )
2653       {
2654          /* @todo if there was already a node for factors[0]^exponents[0], then there may have been also been already an auxiliary variable and constraint (-> ex7_3_4) */
2655          (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "nlreform%d", *naddcons);
2656          SCIPdebugMsg(scip, "add auxiliary variable and constraint %s\n", name);
2657 
2658          SCIP_CALL( SCIPcreateVar(scip, &auxvar, name, -SCIPinfinity(scip), SCIPinfinity(scip), 0.0,
2659                SCIP_VARTYPE_CONTINUOUS, TRUE, TRUE, NULL, NULL, NULL, NULL, NULL) );
2660          SCIP_CALL( SCIPaddVar(scip, auxvar) );
2661          SCIP_CALL( SCIPexprgraphAddVars(exprgraph, 1, (void**)&auxvar, resultnode) );
2662 
2663 #ifdef WITH_DEBUG_SOLUTION
2664          if( SCIPdebugIsMainscip(scip) )
2665          {
2666             SCIPexprgraphSetVarNodeValue(*resultnode, SCIPexprgraphGetNodeVal(expnode));
2667             SCIP_CALL( SCIPdebugAddSolVal(scip, auxvar, SCIPexprgraphGetNodeVal(expnode)) );
2668          }
2669 #endif
2670 
2671          /* add new constraint resultnode(=auxvar) = expnode */
2672          minusone = -1.0;
2673          SCIP_CALL( SCIPcreateConsNonlinear2(scip, &auxcons, name, 1, &auxvar, &minusone, expnode, 0.0, 0.0, TRUE, TRUE, TRUE, TRUE, TRUE,
2674                FALSE, FALSE, FALSE, FALSE, FALSE) );
2675          SCIP_CALL( SCIPaddCons(scip, auxcons) );
2676 
2677          SCIP_CALL( SCIPreleaseCons(scip, &auxcons) );
2678          SCIP_CALL( SCIPreleaseVar(scip, &auxvar) );
2679 
2680          ++*naddcons;
2681       }
2682       else
2683       {
2684          *resultnode = expnode;
2685       }
2686 
2687       return SCIP_OKAY;
2688    }
2689 
2690    if( nfactors == 2 && exponents != NULL && exponents[0] != 1.0 && exponents[0] == exponents[1] )  /*lint !e777*/
2691    {
2692       /* factor0^exponent * factor1^exponent with exponent != 1.0, reform as (factor0*factor1)^exponent */
2693       SCIP_EXPRGRAPHNODE* productnode;
2694 
2695       /* create node for factor0*factor1 */
2696       SCIP_CALL( reformMonomial(scip, exprgraph, 2, factors, NULL, &productnode, TRUE, mindepth, naddcons) );
2697 
2698       /* create node for productnode^exponents[0] by just calling this method again */
2699       SCIP_CALL( reformMonomial(scip, exprgraph, 1, &productnode, &exponents[0], resultnode, createauxcons, mindepth, naddcons) );
2700 
2701       return SCIP_OKAY;
2702    }
2703 
2704    if( nfactors == 2 && exponents != NULL && exponents[0] == -exponents[1] )  /*lint !e777*/
2705    {
2706       /* factor0^exponent * factor1^(-exponent), reform as (factor0/factor1)^exponent or (factor1/factor0)^(-exponent) */
2707       SCIP_EXPRGRAPHNODE* auxvarnode;
2708       SCIP_EXPRGRAPHNODE* auxconsnode;
2709       SCIP_EXPRGRAPHNODE* leftright[2];
2710       SCIP_Real absexp;
2711 
2712       /* create variable and constraint for factor0 = auxvar * factor1 (if exponent > 0) or factor1 = auxvar * factor0 (if exponent < 0) */
2713 
2714       (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "nlreform%d", *naddcons);
2715       SCIPdebugMsg(scip, "add auxiliary variable and constraint %s\n", name);
2716 
2717       SCIP_CALL( SCIPcreateVar(scip, &auxvar, name, -SCIPinfinity(scip), SCIPinfinity(scip), 0.0,
2718             SCIP_VARTYPE_CONTINUOUS, TRUE, TRUE, NULL, NULL, NULL, NULL, NULL) );
2719       SCIP_CALL( SCIPaddVar(scip, auxvar) );
2720       SCIP_CALL( SCIPexprgraphAddVars(exprgraph, 1, (void**)&auxvar, &auxvarnode) );
2721 
2722 #ifdef WITH_DEBUG_SOLUTION
2723       /* store debug sol value of node as value for auxvar in debug solution and as value for resultnode */
2724       if( SCIPdebugIsMainscip(scip) )
2725       {
2726          SCIP_Real debugval;
2727          if( exponents[0] > 0.0 )
2728             debugval = SCIPexprgraphGetNodeVal(factors[0]) / SCIPexprgraphGetNodeVal(factors[1]);
2729          else
2730             debugval = SCIPexprgraphGetNodeVal(factors[1]) / SCIPexprgraphGetNodeVal(factors[0]);
2731          SCIPexprgraphSetVarNodeValue(auxvarnode, debugval);
2732          SCIP_CALL( SCIPdebugAddSolVal(scip, auxvar, debugval) );
2733       }
2734 #endif
2735 
2736       /* add new constraint resultnode(= auxvar) * factor1 - factor0 == 0 (exponent > 0) or auxvar * factor0 - factor1 == 0 (exponent < 0) */
2737       leftright[0] = auxvarnode;
2738       leftright[1] = exponents[0] > 0.0 ? factors[1] : factors[0];
2739 
2740       SCIP_CALL( SCIPexprgraphCreateNode(SCIPblkmem(scip), &auxconsnode, SCIP_EXPR_MUL, NULL) );
2741       SCIP_CALL( SCIPexprgraphAddNode(exprgraph, auxconsnode, -1, 2, leftright) );
2742 
2743       leftright[0] = auxconsnode;
2744       leftright[1] = exponents[0] > 0.0 ? factors[0] : factors[1];
2745 
2746       SCIP_CALL( SCIPexprgraphCreateNode(SCIPblkmem(scip), &auxconsnode, SCIP_EXPR_MINUS, NULL) );
2747       SCIP_CALL( SCIPexprgraphAddNode(exprgraph, auxconsnode, -1, 2, leftright) );
2748 
2749       SCIP_CALL( SCIPcreateConsNonlinear2(scip, &auxcons, name, 0, NULL, NULL, auxconsnode, 0.0, 0.0,
2750          TRUE, TRUE, TRUE, TRUE, TRUE,
2751          FALSE, FALSE, FALSE, FALSE, FALSE) );
2752       SCIP_CALL( SCIPaddCons(scip, auxcons) );
2753 
2754       SCIP_CALL( SCIPreleaseCons(scip, &auxcons) );
2755       SCIP_CALL( SCIPreleaseVar(scip, &auxvar) );
2756 
2757       ++*naddcons;
2758 
2759       /* create node for auxvarnode^abs(exponents[0]) by just calling this method again */
2760       absexp = fabs(exponents[0]);
2761       SCIP_CALL( reformMonomial(scip, exprgraph, 1, &auxvarnode, &absexp, resultnode, createauxcons, mindepth, naddcons) );
2762 
2763       return SCIP_OKAY;
2764    }
2765 
2766    /* @todo if nfactors > 2, assemble groups of factors with same exponent and replace these by a single variable first */
2767 
2768    {
2769       /* at least two factors */
2770       /* create auxvar for left half (recursively) and auxvar for right half (recursively) and maybe new auxvar for product */
2771       /* @todo it may be enough to replace single factors in a monomial to get it convex or concave, see Westerlund et.al. */
2772 
2773       SCIP_EXPRGRAPHNODE* productnode;
2774       SCIP_EXPRGRAPHNODE* leftright[2]; /* {left, right} */
2775       SCIP_EXPRGRAPHNODE* parent;
2776       int half;
2777       int p;
2778 
2779       half = nfactors / 2;
2780       assert(half > 0);
2781       assert(half < nfactors);
2782 
2783       SCIP_CALL( reformMonomial(scip, exprgraph, half, factors, exponents, &leftright[0], TRUE, mindepth, naddcons) );
2784       SCIP_CALL( reformMonomial(scip, exprgraph, nfactors-half, &factors[half], exponents != NULL ? &exponents[half] : NULL, &leftright[1], TRUE, mindepth, naddcons) );  /*lint !e826*/
2785 
2786       /* check if there is already a node for left * right */
2787       productnode = NULL;
2788       for( p = 0; p < SCIPexprgraphGetNodeNParents(leftright[0]); ++p)
2789       {
2790          parent = SCIPexprgraphGetNodeParents(leftright[0])[p];
2791          if( SCIPexprgraphGetNodeOperator(parent) != SCIP_EXPR_MUL )
2792             continue;
2793 
2794          assert(SCIPexprgraphGetNodeNChildren(parent) == 2);
2795          if( (SCIPexprgraphGetNodeChildren(parent)[0] == leftright[0] && SCIPexprgraphGetNodeChildren(parent)[1] == leftright[1]) ||
2796             ( SCIPexprgraphGetNodeChildren(parent)[0] == leftright[1] && SCIPexprgraphGetNodeChildren(parent)[1] == leftright[0]) )
2797          {
2798             productnode = parent;
2799             break;
2800          }
2801       }
2802       if( productnode == NULL )
2803       {
2804          /* create node for left * right */
2805          SCIP_CALL( SCIPexprgraphCreateNode(SCIPblkmem(scip), &productnode, SCIP_EXPR_MUL, NULL) );
2806          SCIP_CALL( SCIPexprgraphAddNode(exprgraph, productnode, mindepth, 2, leftright) );
2807          SCIP_CALL( SCIPexprgraphUpdateNodeBoundsCurvature(productnode, INTERVALINFTY, BOUNDTIGHTENING_MINSTRENGTH, TRUE) );
2808          assert(!SCIPintervalIsEmpty(INTERVALINFTY, SCIPexprgraphGetNodeBounds(productnode)));
2809       }
2810 
2811       if( createauxcons )
2812       {
2813          /* @todo if there was already a node for factors[0]^exponents[0], then there may have been also been already an auxiliary variable and constraint (-> ex7_3_4) */
2814          (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "nlreform%d", *naddcons);
2815          SCIPdebugMsg(scip, "add auxiliary variable and constraint %s\n", name);
2816 
2817          SCIP_CALL( SCIPcreateVar(scip, &auxvar, name, -SCIPinfinity(scip), SCIPinfinity(scip), 0.0,
2818                SCIP_VARTYPE_CONTINUOUS, TRUE, TRUE, NULL, NULL, NULL, NULL, NULL) );
2819          SCIP_CALL( SCIPaddVar(scip, auxvar) );
2820          SCIP_CALL( SCIPexprgraphAddVars(exprgraph, 1, (void**)&auxvar, resultnode) );
2821 
2822 #ifdef WITH_DEBUG_SOLUTION
2823          if( SCIPdebugIsMainscip(scip) )
2824          {
2825             SCIPexprgraphSetVarNodeValue(*resultnode, SCIPexprgraphGetNodeVal(productnode));
2826             SCIP_CALL( SCIPdebugAddSolVal(scip, auxvar, SCIPexprgraphGetNodeVal(productnode)) );
2827          }
2828 #endif
2829 
2830          /* add new constraint resultnode(= auxvar) == left * right */
2831          minusone = -1.0;
2832          SCIP_CALL( SCIPcreateConsNonlinear2(scip, &auxcons, name, 1, &auxvar, &minusone, productnode, 0.0, 0.0, TRUE, TRUE, TRUE, TRUE, TRUE,
2833                FALSE, FALSE, FALSE, FALSE, FALSE) );
2834          SCIP_CALL( SCIPaddCons(scip, auxcons) );
2835 
2836          SCIP_CALL( SCIPreleaseCons(scip, &auxcons) );
2837          SCIP_CALL( SCIPreleaseVar(scip, &auxvar) );
2838 
2839          ++*naddcons;
2840       }
2841       else
2842       {
2843          *resultnode = productnode;
2844       }
2845    }
2846 
2847    return SCIP_OKAY;
2848 }
2849 
2850 /** reformulates expression graph into a form so that for each node under- and overestimators could be computed
2851  * similar to factorable reformulation in other global solvers, but sometimes does not split up complex operands (like quadratic)
2852  */
2853 static
reformulate(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS ** conss,int nconss,int * naddcons)2854 SCIP_RETCODE reformulate(
2855    SCIP*                 scip,               /**< SCIP data structure */
2856    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
2857    SCIP_CONS**           conss,              /**< constraints */
2858    int                   nconss,             /**< number of constraints */
2859    int*                  naddcons            /**< buffer to increase by the number of added constraints */
2860    )
2861 {
2862    SCIP_CONSHDLRDATA* conshdlrdata;
2863    SCIP_CONSDATA* consdata;
2864    SCIP_EXPRGRAPH* exprgraph;
2865    SCIP_EXPRGRAPHNODE* node;
2866    SCIP_EXPRGRAPHNODE** children;
2867    SCIP_EXPRGRAPHNODE* reformnode;
2868    SCIP_Bool havenonlinparent;
2869    SCIP_Bool domainerror;
2870    int nchildren;
2871    int c;
2872    int d;
2873    int i;
2874    int u;
2875 #ifndef NDEBUG
2876    int j;
2877 #endif
2878 
2879    assert(scip != NULL);
2880    assert(conshdlr != NULL);
2881    assert(conss != NULL || nconss == 0);
2882    assert(naddcons != NULL);
2883    assert(SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING);
2884    assert(!SCIPinProbing(scip));
2885 
2886    conshdlrdata = SCIPconshdlrGetData(conshdlr);
2887    assert(conshdlrdata != NULL);
2888 
2889    if( conshdlrdata->isreformulated )
2890    {
2891       SCIPdebugMsg(scip, "skip reformulation, already done\n");
2892       return SCIP_OKAY;
2893    }
2894 
2895    exprgraph = conshdlrdata->exprgraph;
2896 
2897    /* make sure current variable bounds are variable nodes of exprgraph */
2898    SCIP_CALL( SCIPexprgraphPropagateVarBounds(exprgraph, INTERVALINFTY, FALSE, &domainerror) );
2899    assert(!domainerror); /* should have been found by domain propagation */
2900 
2901    /* set debug solution in expression graph and evaluate nodes, so we can compute debug solution values for auxiliary variables */
2902 #ifdef WITH_DEBUG_SOLUTION
2903    if( SCIPdebugIsMainscip(scip) )
2904    {
2905       SCIP_Real* varvals;
2906 
2907       SCIP_CALL( SCIPallocBufferArray(scip, &varvals, SCIPexprgraphGetNVars(exprgraph)) );
2908 
2909       for( i = 0; i < SCIPexprgraphGetNVars(exprgraph); ++i )
2910          SCIP_CALL( SCIPdebugGetSolVal(scip, (SCIP_VAR*)SCIPexprgraphGetVars(exprgraph)[i], &varvals[i]) );
2911 
2912       SCIP_CALL( SCIPexprgraphEval(exprgraph, varvals) );
2913 
2914       SCIPfreeBufferArray(scip, &varvals);
2915    }
2916 #endif
2917 
2918    for( d = 1; d < SCIPexprgraphGetDepth(exprgraph); ++d )
2919    {
2920       i = 0;
2921       while( i < SCIPexprgraphGetNNodes(exprgraph)[d] )
2922       {
2923          node = SCIPexprgraphGetNodes(exprgraph)[d][i];
2924          assert(node != NULL);
2925 
2926          /* skip disabled nodes, they should be removed soon */
2927          if( !SCIPexprgraphIsNodeEnabled(node) )
2928          {
2929             ++i;
2930             continue;
2931          }
2932 
2933          /* make sure bounds and curvature of node are uptodate */
2934          SCIP_CALL( SCIPexprgraphUpdateNodeBoundsCurvature(node, INTERVALINFTY, BOUNDTIGHTENING_MINSTRENGTH, TRUE) );
2935          assert(!SCIPintervalIsEmpty(INTERVALINFTY, SCIPexprgraphGetNodeBounds(node)));
2936 
2937          /* try external reformulation methods */
2938          for( u = 0; u < conshdlrdata->nnlconsupgrades; ++u )
2939          {
2940             if( conshdlrdata->nlconsupgrades[u]->nodereform != NULL && conshdlrdata->nlconsupgrades[u]->active )
2941             {
2942                int nupdateconsbefore = SCIPconshdlrGetNUpdateConss(conshdlr);
2943                SCIP_CALL( conshdlrdata->nlconsupgrades[u]->nodereform(scip, exprgraph, node, naddcons, &reformnode) );
2944                if( reformnode == NULL )
2945                   continue;
2946 
2947                SCIPdebugMsg(scip, "external nodereform reformulated node %p(%d,%d), replace by %p\n",
2948                   (void*)node, SCIPexprgraphGetNodeDepth(node), SCIPexprgraphGetNodePosition(node), (void*)reformnode);
2949 
2950                /* do not replace node in nonlinear update constraints that may have been added by reformulation */
2951                SCIP_CALL( reformReplaceNode(exprgraph, &node, reformnode, conshdlr, SCIPconshdlrGetNUpdateConss(conshdlr) - nupdateconsbefore) );
2952                SCIP_CALL( SCIPexprgraphUpdateNodeBoundsCurvature(reformnode, INTERVALINFTY, BOUNDTIGHTENING_MINSTRENGTH, TRUE) );
2953                assert(!SCIPintervalIsEmpty(INTERVALINFTY, SCIPexprgraphGetNodeBounds(reformnode)));
2954 
2955                break;
2956             }
2957          }
2958          /* if node has been reformulated, continue with next node without increasing i */
2959          if( u < conshdlrdata->nnlconsupgrades )
2960             continue;
2961 
2962          /* leave nodes that are known to be convex/concave/linear as they are */
2963          if( SCIPexprgraphGetNodeCurvature(node) != SCIP_EXPRCURV_UNKNOWN )
2964          {
2965             SCIPdebugMsg(scip, "skip reformulating node %p(%d,%d) = ", (void*)node, SCIPexprgraphGetNodeDepth(node), SCIPexprgraphGetNodePosition(node));
2966             SCIPdebug( SCIPexprgraphPrintNode(node, SCIPgetMessagehdlr(scip), NULL) );
2967             SCIPdebugMsgPrint(scip, ", curv = %s\n", SCIPexprcurvGetName(SCIPexprgraphGetNodeCurvature(node)));
2968             ++i;
2969             continue;
2970          }
2971 
2972          /* get flag whether node has a nonlinear parent
2973           * we want to know whether the current node will be at the top of the tree after the next simplification run
2974           * due to the tricky reformulation of polynomials below, this may not be the case yet
2975           */
2976          havenonlinparent = SCIPexprgraphHasNodeNonlinearAncestor(node);
2977 
2978          /* take action */
2979          assert(SCIPexprgraphGetNodeCurvature(node) == SCIP_EXPRCURV_UNKNOWN);
2980          SCIPdebugMsg(scip, "think about reformulating %s node %p(%d,%d) = ", SCIPexpropGetName(SCIPexprgraphGetNodeOperator(node)), (void*)node, SCIPexprgraphGetNodeDepth(node), SCIPexprgraphGetNodePosition(node));
2981          SCIPdebug( SCIPexprgraphPrintNode(node, SCIPgetMessagehdlr(scip), NULL) );
2982          SCIPdebugMsgPrint(scip, "\n");
2983 
2984          children  = SCIPexprgraphGetNodeChildren(node);
2985          nchildren = SCIPexprgraphGetNodeNChildren(node);
2986          assert(children != NULL || nchildren == 0);
2987 
2988 #ifndef NDEBUG
2989          /* at this place, all children nodes should have a known curvature, except if they only appear only linearly in constraints
2990           * the latter for cases where constraints with unknown curvature are upgraded to other constraint handler that can handle these (quadratic, signpower,...)
2991           */
2992          for( j = 0; j < nchildren; ++j )
2993          {
2994             assert(children[j] != NULL);  /*lint !e613*/
2995             if( havenonlinparent ||
2996                (  SCIPexprgraphGetNodeOperator(node) != SCIP_EXPR_PLUS  &&
2997                   SCIPexprgraphGetNodeOperator(node) != SCIP_EXPR_MINUS &&
2998                   SCIPexprgraphGetNodeOperator(node) != SCIP_EXPR_SUM   &&
2999                   SCIPexprgraphGetNodeOperator(node) != SCIP_EXPR_LINEAR) )
3000                assert(SCIPexprgraphGetNodeCurvature(children[j]) != SCIP_EXPRCURV_UNKNOWN || SCIPexprgraphGetNodeOperator(children[j]) == SCIP_EXPR_USER);  /*lint !e613*/
3001          }
3002 #endif
3003 
3004          switch( SCIPexprgraphGetNodeOperator(node) )
3005          {
3006          case SCIP_EXPR_VARIDX:
3007          case SCIP_EXPR_PARAM:
3008          case SCIP_EXPR_CONST:
3009             SCIPerrorMessage("node with operator %d cannot have unknown curvature\n", SCIPexprgraphGetNodeOperator(node));
3010             SCIPABORT();
3011             break;  /*lint !e527*/
3012 
3013             /* linear operands */
3014          case SCIP_EXPR_PLUS:
3015          case SCIP_EXPR_MINUS:
3016          case SCIP_EXPR_SUM:
3017          case SCIP_EXPR_LINEAR:
3018             /* children have conflicting curvature, we can handle such sums in cons_nonlinear
3019              * thus, turn node into variable, if it has nonlinear parents */
3020             if( havenonlinparent )
3021             {
3022                SCIP_CALL( reformNode2Var(scip, exprgraph, node, conshdlr, naddcons, FALSE) );
3023                assert(node != NULL);
3024                assert(SCIPexprgraphGetNodeNParents(node) == 0); /* node should now be at top of graph */
3025             }
3026             ++i;
3027             break;
3028 
3029          /* quadratic operands */
3030          case SCIP_EXPR_MUL:
3031          case SCIP_EXPR_QUADRATIC:
3032          {
3033             SCIP_EXPRGRAPHNODE* child;
3034             SCIP_Bool needupdate;
3035 
3036             /* ensure all children are linear, so next simplifier run makes sure all children will be variables (by distributing the product)
3037              * however, that will not work for user-expressions, so we should also ensure that they are none (@todo as they are linear, they could actually be replaced by a regular linear expression)
3038              */
3039             SCIPdebugMessage("ensure children are linear\n");
3040             SCIP_CALL( reformEnsureChildrenMinCurvature(scip, exprgraph, node, SCIP_EXPRCURV_LINEAR, conshdlr, naddcons) );
3041 
3042             needupdate = FALSE;  /* whether we need to update curvature of node */
3043             for( c = 0; c < SCIPexprgraphGetNodeNChildren(node); ++c )
3044             {
3045                child = SCIPexprgraphGetNodeChildren(node)[c];
3046                assert(child != NULL);
3047 
3048                if( SCIPexprgraphGetNodeCurvature(child) != SCIP_EXPRCURV_LINEAR || SCIPexprgraphGetNodeOperator(child) == SCIP_EXPR_USER || SCIPexprgraphGetNodeOperator(child) == SCIP_EXPR_ABS )
3049                {
3050                   SCIPdebugMessage("add auxiliary variable for child %p(%d,%d) with curvature %s operator %s\n",
3051                      (void*)child, SCIPexprgraphGetNodeDepth(child), SCIPexprgraphGetNodePosition(child), SCIPexprcurvGetName(SCIPexprgraphGetNodeCurvature(child)), SCIPexpropGetName(SCIPexprgraphGetNodeOperator(child)) );
3052 
3053                   SCIP_CALL( reformNode2Var(scip, exprgraph, child, conshdlr, naddcons, FALSE) );
3054                   needupdate = TRUE;
3055 
3056                   /* c'th child of node should now be a variable */
3057                   assert(SCIPexprgraphGetNodeChildren(node)[c] != child);
3058                   assert(SCIPexprgraphGetNodeOperator(SCIPexprgraphGetNodeChildren(node)[c]) == SCIP_EXPR_VARIDX);
3059                }
3060             }
3061             if( needupdate )
3062             {
3063                SCIP_CALL( SCIPexprgraphUpdateNodeBoundsCurvature(node, INTERVALINFTY, BOUNDTIGHTENING_MINSTRENGTH, TRUE) );
3064                assert(!SCIPintervalIsEmpty(INTERVALINFTY, SCIPexprgraphGetNodeBounds(node)));
3065             }
3066 
3067             if( SCIPexprgraphGetNodeCurvature(node) != SCIP_EXPRCURV_UNKNOWN )
3068             {
3069                /* if curvature is now known then we are done */
3070                ++i;
3071                break;
3072             }
3073 
3074             /* if we have nonlinear parents or a sibling, then add auxiliary variable for this node, so an upgrade to cons_quadratic should take place
3075              * we assume that siblings are non-linear and non-quadratic, which should be the case if simplifier was run, and also if this node was created during reformulating a polynomial
3076              * @todo we could also add auxvars for the sibling nodes, e.g., if there is only one
3077              * @todo if sibling nodes are quadratic (or even linear) due to reformulation, then we do not need to reform here... (-> nvs16)
3078              *       maybe this step should not be done here at all if havenonlinparent is FALSE? e.g., move into upgrade from quadratic?
3079              */
3080             if( havenonlinparent || SCIPexprgraphHasNodeSibling(node) )
3081             {
3082                SCIP_CALL( reformNode2Var(scip, exprgraph, node, conshdlr, naddcons, FALSE) );
3083                assert(node != NULL);
3084                assert(SCIPexprgraphGetNodeNParents(node) == 0); /* node should now be at top of graph, so it can be upgraded by cons_quadratic */
3085                break;
3086             }
3087 
3088             ++i;
3089             break;
3090          }
3091 
3092          case SCIP_EXPR_DIV:
3093          {
3094             /* reformulate as bilinear term
3095              * note that in the reformulation, a zero in the denominator forces the nominator to be zero too, but the auxiliary can be arbitrary
3096              */
3097             SCIP_EXPRGRAPHNODE* auxvarnode;
3098             SCIP_EXPRGRAPHNODE* auxnode;
3099             SCIP_EXPRGRAPHNODE* auxchildren[3];
3100             SCIP_Real           lincoefs[3];
3101             SCIP_QUADELEM       quadelem;
3102             SCIP_VAR*           auxvar;
3103             SCIP_CONS*          auxcons;
3104             char                name[SCIP_MAXSTRLEN];
3105             SCIP_INTERVAL       bounds;
3106 
3107             assert(children != NULL);
3108             assert(nchildren == 2);
3109 
3110             bounds = SCIPexprgraphGetNodeBounds(node);
3111             (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "nlreform%d", *naddcons);
3112 
3113             SCIPdebugMsg(scip, "add auxiliary variable %s for division in node %p(%d,%d)\n", name, (void*)node, SCIPexprgraphGetNodeDepth(node), SCIPexprgraphGetNodePosition(node));
3114 
3115             SCIP_CALL( SCIPcreateVar(scip, &auxvar, name, SCIPintervalGetInf(bounds), SCIPintervalGetSup(bounds), 0.0,
3116                   SCIP_VARTYPE_CONTINUOUS, TRUE, TRUE, NULL, NULL, NULL, NULL, NULL) );
3117             SCIP_CALL( SCIPaddVar(scip, auxvar) );
3118             SCIP_CALL( SCIPexprgraphAddVars(exprgraph, 1, (void**)&auxvar, &auxvarnode) );
3119 
3120 #ifdef WITH_DEBUG_SOLUTION
3121             if( SCIPdebugIsMainscip(scip) )
3122             {
3123                SCIP_Real debugval;
3124                debugval = SCIPexprgraphGetNodeVal(children[0]) / SCIPexprgraphGetNodeVal(children[1]);
3125                SCIPexprgraphSetVarNodeValue(auxvarnode, debugval);
3126                SCIP_CALL( SCIPdebugAddSolVal(scip, auxvar, debugval) );
3127             }
3128 #endif
3129 
3130             /* add new constraint auxvar * child[1] - child[0] == 0 */
3131             auxchildren[0] = children[0];  /*lint !e613*/
3132             auxchildren[1] = children[1];  /*lint !e613*/
3133             auxchildren[2] = auxvarnode;
3134 
3135             lincoefs[0] = -1.0;
3136             lincoefs[1] =  0.0;
3137             lincoefs[2] =  0.0;
3138 
3139             quadelem.idx1 = 1;
3140             quadelem.idx2 = 2;
3141             quadelem.coef = 1.0;
3142 
3143             SCIP_CALL( SCIPexprgraphCreateNodeQuadratic(SCIPblkmem(scip), &auxnode, 3, lincoefs, 1, &quadelem, 0.0) );
3144             SCIP_CALL( SCIPexprgraphAddNode(exprgraph, auxnode, -1, 3, auxchildren) );
3145 
3146             SCIP_CALL( SCIPcreateConsNonlinear2(scip, &auxcons, name, 0, NULL, NULL, auxnode, 0.0, 0.0,
3147                   TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
3148             SCIP_CALL( SCIPaddCons(scip, auxcons) );
3149 
3150             /* replace node by auxvarnode in graph and constraints that use it */
3151             SCIP_CALL( reformReplaceNode(exprgraph, &node, auxvarnode, conshdlr, 1) );
3152 
3153             SCIP_CALL( SCIPreleaseCons(scip, &auxcons) );
3154             SCIP_CALL( SCIPreleaseVar(scip, &auxvar) );
3155 
3156             ++*naddcons;
3157 
3158             /* do not increase i, since node was removed and not necessarily replaced here */
3159             break;
3160          }
3161 
3162          case SCIP_EXPR_MIN:
3163          {
3164             /* make sure that both children are concave, because min of concave functions is concave */
3165             SCIP_CALL( reformEnsureChildrenMinCurvature(scip, exprgraph, node, SCIP_EXPRCURV_CONCAVE, conshdlr, naddcons) );
3166             assert(SCIPexprgraphGetNodeCurvature(node) & SCIP_EXPRCURV_CONCAVE);
3167             ++i;
3168             break;
3169          }
3170 
3171          case SCIP_EXPR_MAX:
3172          {
3173             /* make sure that both children are convex, because max of convex functions is convex */
3174             SCIP_CALL( reformEnsureChildrenMinCurvature(scip, exprgraph, node, SCIP_EXPRCURV_CONVEX, conshdlr, naddcons) );
3175             assert(SCIPexprgraphGetNodeCurvature(node) & SCIP_EXPRCURV_CONVEX);
3176             ++i;
3177             break;
3178          }
3179 
3180          case SCIP_EXPR_INTPOWER:
3181          {
3182             assert(nchildren == 1);
3183 
3184             /* for an intpower with mixed sign in the base and negative exponent, we reformulate similar as for EXPR_DIV */
3185             if( SCIPexprgraphGetNodeIntPowerExponent(node) < 0 && SCIPintervalGetInf(SCIPexprgraphGetNodeBounds(children[0])) < 0.0 && SCIPintervalGetSup(SCIPexprgraphGetNodeBounds(children[0])) > 0.0 )  /*lint !e613*/
3186             {
3187                SCIP_EXPRGRAPHNODE* auxvarnode;
3188                SCIP_Real exponent;
3189                int naddconsbefore = *naddcons;
3190 
3191                /* if we have something like x^(-3) with mixed sign for x, then add auxvar and reform as auxvar*x^3 = 1 via reformMonomial */
3192                exponent = (SCIP_Real)SCIPexprgraphGetNodeIntPowerExponent(node);
3193                SCIP_CALL( reformMonomial(scip, exprgraph, 1, children, &exponent, &auxvarnode, TRUE, SCIPexprgraphGetNodeDepth(node), naddcons) );
3194 
3195                /* replace node by auxvarnode */
3196                SCIP_CALL( reformReplaceNode(exprgraph, &node, auxvarnode, conshdlr, *naddcons - naddconsbefore) );
3197                break;
3198             }
3199 
3200             /* otherwise, we continue as for other univariate operands */
3201          }   /*lint -fallthrough*/
3202 
3203          /* univariate operands where the child does not have bounds and curvature from which we can deduce curvature of this node,
3204           * but where we can do more if the child is linear
3205           * thus, turn child into auxiliary variable
3206           */
3207          case SCIP_EXPR_SQUARE:
3208          case SCIP_EXPR_SQRT:
3209          case SCIP_EXPR_EXP:
3210          case SCIP_EXPR_LOG:
3211          case SCIP_EXPR_ABS:
3212          case SCIP_EXPR_REALPOWER:
3213          case SCIP_EXPR_SIGNPOWER:
3214          {
3215             assert(nchildren == 1);
3216 
3217             SCIP_CALL( reformEnsureChildrenMinCurvature(scip, exprgraph, node, SCIP_EXPRCURV_LINEAR, conshdlr, naddcons) );
3218 
3219             if( SCIPexprgraphGetNodeCurvature(node) == SCIP_EXPRCURV_UNKNOWN )
3220             {
3221                /* the only case where f(x) for a linear term x is indefinite here is if f is intpower or signpower and x has mixed sign */
3222                assert(SCIPexprgraphGetNodeOperator(node) == SCIP_EXPR_INTPOWER || SCIPexprgraphGetNodeOperator(node) == SCIP_EXPR_SIGNPOWER);
3223                assert(SCIPintervalGetInf(SCIPexprgraphGetNodeBounds(children[0])) < 0.0);  /*lint !e613*/
3224                assert(SCIPintervalGetSup(SCIPexprgraphGetNodeBounds(children[0])) > 0.0);  /*lint !e613*/
3225             }
3226 
3227             /* update curvature of node */
3228             SCIP_CALL( SCIPexprgraphUpdateNodeBoundsCurvature(node, INTERVALINFTY, BOUNDTIGHTENING_MINSTRENGTH, TRUE) );
3229             assert(!SCIPintervalIsEmpty(INTERVALINFTY, SCIPexprgraphGetNodeBounds(node)));
3230 
3231             if( SCIPexprgraphGetNodeCurvature(node) == SCIP_EXPRCURV_UNKNOWN )
3232             {
3233                /* if intpower and signpower with positive exponent and a mixed sign in the child bounds still does not give a curvature,
3234                 * we can do more if we make this node the root of a nonlinear constraints expression node, so it can be upgraded by cons_signpower
3235                 * of course, this is only required if the node is still intermediate
3236                 *
3237                 * an intpower with negative exponent should have been handled above
3238                 * for signpower, we assume the exponent is > 1.0
3239                 */
3240                assert(SCIPexprgraphGetNodeOperator(node) == SCIP_EXPR_INTPOWER || SCIPexprgraphGetNodeOperator(node) == SCIP_EXPR_SIGNPOWER);
3241                assert(SCIPexprgraphGetNodeOperator(node) != SCIP_EXPR_INTPOWER  || SCIPexprgraphGetNodeIntPowerExponent(node) > 1);
3242                assert(SCIPexprgraphGetNodeOperator(node) != SCIP_EXPR_SIGNPOWER || SCIPexprgraphGetNodeSignPowerExponent(node) > 1.0);
3243                if( havenonlinparent )
3244                {
3245                   SCIP_CALL( reformNode2Var(scip, exprgraph, node, conshdlr, naddcons, FALSE) );
3246                   assert(node != NULL); /* it should be used by some auxiliary constraint now */
3247                   assert(SCIPexprgraphGetNodeNParents(node) == 0); /* node should now be at top of graph (and used by new auxiliary constraint) */
3248                }
3249             }
3250             ++i;
3251 
3252             break;
3253          }
3254 
3255          case SCIP_EXPR_SIN:
3256          case SCIP_EXPR_COS:
3257          case SCIP_EXPR_TAN:
3258          case SCIP_EXPR_SIGN:
3259             /* case SCIP_EXPR_ERF   : */
3260             /* case SCIP_EXPR_ERFI  : */
3261          {
3262             SCIPerrorMessage("no support for trigonometric or sign operands yet\n");
3263             return SCIP_ERROR;
3264          }
3265 
3266          case SCIP_EXPR_PRODUCT:
3267          {
3268             int naddconsbefore;
3269             /* ensure all children are linear */
3270             SCIP_CALL( reformEnsureChildrenMinCurvature(scip, exprgraph, node, SCIP_EXPRCURV_LINEAR, conshdlr, naddcons) );
3271             if( SCIPexprgraphGetNodeCurvature(node) != SCIP_EXPRCURV_UNKNOWN )
3272             {
3273                ++i;
3274                break;
3275             }
3276 
3277             /* if curvature is still unknown (quite likely), then turn into a cascade of bilinear terms
3278              * if node has parents, then ensure that it has a known curvature, otherwise we are also fine with a node that is a product of two (aux)variables */
3279             naddconsbefore = *naddcons;
3280             SCIP_CALL( reformMonomial(scip, exprgraph, nchildren, children, NULL, &reformnode, havenonlinparent, SCIPexprgraphGetNodeDepth(node), naddcons) );
3281 
3282             /* replace node by reformnode in graph and in all constraints that use it */
3283             SCIP_CALL( reformReplaceNode(exprgraph, &node, reformnode, conshdlr, *naddcons - naddconsbefore) );
3284 
3285             /* do not increase i, since node was removed and not necessarily replaced here */
3286             break;
3287          }
3288 
3289          case SCIP_EXPR_POLYNOMIAL:
3290          {
3291             /* if polynomial has several monomials, replace by a sum of nodes each having a single monomial and one that has all linear and quadratic monomials
3292              * if polynomial has only a single monomial, then reformulate that one
3293              */
3294             SCIP_EXPRDATA_MONOMIAL** monomials;
3295             SCIP_EXPRDATA_MONOMIAL* monomial;
3296             int nmonomials;
3297             SCIP_Real* exponents;
3298             SCIP_Real coef;
3299             int* childidxs;
3300             int nfactors;
3301             int f;
3302             SCIP_INTERVAL childbounds;
3303             SCIP_EXPRCURV childcurv;
3304             SCIP_Bool modified;
3305 
3306             monomials  = SCIPexprgraphGetNodePolynomialMonomials(node);
3307             nmonomials = SCIPexprgraphGetNodePolynomialNMonomials(node);
3308             assert(nmonomials >= 1); /* constant polynomials should have been simplified away */
3309 
3310             if( nmonomials > 1 )
3311             {
3312                SCIP_EXPRGRAPHNODE* sumnode;
3313                SCIP_Real constant;
3314                int nquadelems;
3315                SCIP_QUADELEM* quadelems;
3316                SCIP_Real* lincoefs;
3317                int nmonomialnodes;
3318                SCIP_EXPRGRAPHNODE** childrennew;
3319                SCIP_EXPRGRAPHNODE** monomialnodes;
3320                SCIP_Bool foundlincoefs;
3321                int m;
3322 
3323                /* @todo if a monomial is a factor of another monomial, then we could (and should?) replace it there by the node we create for it here -> ex7_2_1
3324                 * @todo factorizing the polynomial could be beneficial
3325                 */
3326 
3327                /* constant part of polynomials, to add to first monomialnode, if any, or quadratic or linear part */
3328                constant = SCIPexprgraphGetNodePolynomialConstant(node);
3329 
3330                /* coefficients from linear monomials */
3331                foundlincoefs = FALSE;
3332 
3333                /* quadratic elements */
3334                nquadelems = 0;
3335 
3336                /* expression graph nodes representing single higher-degree monomials, and single node with linear and/or quadratic monomials */
3337                nmonomialnodes = 0;
3338                SCIP_CALL( SCIPallocBufferArray(scip, &monomialnodes, nmonomials) );
3339 
3340                /* allocate memory */
3341                SCIP_CALL( SCIPallocClearBufferArray(scip, &lincoefs, nchildren) );
3342                SCIP_CALL( SCIPallocBufferArray(scip, &quadelems, nmonomials) );
3343                SCIP_CALL( SCIPallocBufferArray(scip, &childrennew, nchildren) );
3344 
3345                for( m = 0; m < nmonomials; ++m )
3346                {
3347                   monomial = monomials[m];
3348                   assert(monomial != NULL);
3349 
3350                   coef = SCIPexprGetMonomialCoef(monomial);
3351                   exponents = SCIPexprGetMonomialExponents(monomial);
3352                   childidxs = SCIPexprGetMonomialChildIndices(monomial);
3353                   nfactors = SCIPexprGetMonomialNFactors(monomial);
3354                   assert(nfactors >= 1); /* constant monomials should have been simplified away */
3355                   assert(coef != 0.0);  /* zero-monomials should have been simplified away */
3356 
3357                   if( nfactors == 1 && exponents[0] == 1.0 )
3358                   {
3359                      /* linear monomial */
3360                      foundlincoefs = TRUE;
3361                      assert(0 <= childidxs[0] && childidxs[0] < nchildren);
3362                      assert(lincoefs[childidxs[0]] == 0.0); /* monomials should have been merged */
3363                      lincoefs[childidxs[0]] = coef;
3364                   }
3365                   else if( nfactors == 1 && exponents[0] == 2.0 )
3366                   {
3367                      /* square monomial */
3368                      quadelems[nquadelems].idx1 = childidxs[0];
3369                      quadelems[nquadelems].idx2 = childidxs[0];
3370                      quadelems[nquadelems].coef = coef;
3371                      ++nquadelems;
3372                   }
3373                   else if( nfactors == 2 && exponents[0] == 1.0 && exponents[1] == 1.0 )
3374                   {
3375                      /* bilinear monomial */
3376                      if( childidxs[0] < childidxs[1] )
3377                      {
3378                         quadelems[nquadelems].idx1 = childidxs[0];
3379                         quadelems[nquadelems].idx2 = childidxs[1];
3380                      }
3381                      else
3382                      {
3383                         quadelems[nquadelems].idx1 = childidxs[1];
3384                         quadelems[nquadelems].idx2 = childidxs[0];
3385                      }
3386                      quadelems[nquadelems].coef = coef;
3387                      ++nquadelems;
3388                   }
3389                   else
3390                   {
3391                      /* general monomial -> pass into separate expression graph node */
3392                      SCIP_EXPRDATA_MONOMIAL* monomialnew;
3393 
3394                      /* create new node for this monomial, children will be those associated with factors */
3395                      SCIP_CALL( SCIPexprCreateMonomial(SCIPblkmem(scip), &monomialnew, coef, nfactors, NULL, exponents) );
3396                      SCIP_CALL( SCIPexprgraphCreateNodePolynomial(SCIPblkmem(scip), &monomialnodes[nmonomialnodes], 1, &monomialnew, constant, FALSE) );
3397                      constant = 0.0;
3398 
3399                      assert(nfactors <= nchildren);
3400                      for( f = 0; f < nfactors; ++f )
3401                         childrennew[f] = children[childidxs[f]];  /*lint !e613*/
3402 
3403                      /* add new node to same depth as this node, so we will reformulate it during this run
3404                       * no need to refresh bounds/curvature here, since that will be done when we reach this node next */
3405                      SCIP_CALL( SCIPexprgraphAddNode(exprgraph, monomialnodes[nmonomialnodes], SCIPexprgraphGetNodeDepth(node), nfactors, childrennew) );
3406 
3407                      ++nmonomialnodes;
3408                   }
3409                }
3410                /* should have had at least one linear, quadratic, or general monomial */
3411                assert(foundlincoefs || nquadelems > 0 || nmonomialnodes > 0);
3412 
3413                if( nquadelems > 0 )
3414                {
3415                   /* create and add additional node for quadratic and linear part, simplifier should take care of removing unused children later */
3416                   SCIP_CALL( SCIPexprgraphCreateNodeQuadratic(SCIPblkmem(scip), &monomialnodes[nmonomialnodes], nchildren, foundlincoefs ? lincoefs : NULL, nquadelems, quadelems, constant) );
3417                   constant = 0.0;
3418                   SCIP_CALL( SCIPexprgraphAddNode(exprgraph, monomialnodes[nmonomialnodes], SCIPexprgraphGetNodeDepth(node), nchildren, children) );
3419                   ++nmonomialnodes;
3420                }
3421                else if( foundlincoefs )
3422                {
3423                   /* create additional node for linear part, simplifier should take care of removing unused children later */
3424                   SCIP_CALL( SCIPexprgraphCreateNodeLinear(SCIPblkmem(scip), &monomialnodes[nmonomialnodes], nchildren, lincoefs, constant) );
3425                   constant = 0.0;
3426                   SCIP_CALL( SCIPexprgraphAddNode(exprgraph, monomialnodes[nmonomialnodes], SCIPexprgraphGetNodeDepth(node), nchildren, children) );
3427                   ++nmonomialnodes;
3428                }
3429                assert(constant == 0.0); /* the constant should have been used somewhere */
3430 
3431                /* release memory */
3432                SCIPfreeBufferArray(scip, &childrennew);
3433                SCIPfreeBufferArray(scip, &quadelems);
3434                SCIPfreeBufferArray(scip, &lincoefs);
3435 
3436                assert(nmonomialnodes > 0);
3437                if( nmonomialnodes > 1 )
3438                {
3439                   /* add node for sum of monomials to expression graph */
3440                   SCIP_CALL( SCIPexprgraphCreateNode(SCIPblkmem(scip), &sumnode, nmonomialnodes == 2 ? SCIP_EXPR_PLUS : SCIP_EXPR_SUM) );
3441                   SCIP_CALL( SCIPexprgraphAddNode(exprgraph, sumnode, -1, nmonomialnodes, monomialnodes) );
3442                }
3443                else
3444                {
3445                   /* if only one monomial, then because polynomial was linear or quadratic... */
3446                   assert(SCIPexprgraphGetNodeOperator(monomialnodes[0]) == SCIP_EXPR_LINEAR || SCIPexprgraphGetNodeOperator(monomialnodes[0]) == SCIP_EXPR_QUADRATIC);
3447                   sumnode = monomialnodes[0];
3448                }
3449                SCIPfreeBufferArray(scip, &monomialnodes);
3450 
3451                /* replace node by sumnode, and we are done */
3452                SCIP_CALL( reformReplaceNode(exprgraph, &node, sumnode, conshdlr, 0) );
3453 
3454                SCIPdebugMsg(scip, "splitup polynomial into sum of %d nodes\n", nmonomialnodes);
3455 
3456                break;
3457             }
3458 
3459             /* reformulate a monomial such that it becomes convex or concave, if necessary */
3460 
3461             monomial = monomials[0];
3462             assert(monomial != NULL);
3463 
3464             coef = SCIPexprGetMonomialCoef(monomial);
3465             exponents = SCIPexprGetMonomialExponents(monomial);
3466             childidxs = SCIPexprGetMonomialChildIndices(monomial);
3467             nfactors = SCIPexprGetMonomialNFactors(monomial);
3468             assert(nfactors >= 1); /* constant monomials should have been simplified away */
3469             assert(coef != 0.0);  /* zero-monomials should have been simplified away */
3470             assert(children != NULL);
3471 
3472             /* check if we make monomial convex or concave by making a child linear */
3473             modified = FALSE;
3474             if( nfactors == 1 )
3475             {
3476                /* ensure that the child of an univariate monomial is linear if its current (bounds,curvature) yields an unknown curvature for the monomial
3477                 * and with linear child it had a known curvature (rules out x^a, a negative, x not linear) */
3478                childcurv = SCIPexprgraphGetNodeCurvature(children[childidxs[0]]);  /*lint !e613*/
3479                childbounds = SCIPexprgraphGetNodeBounds(children[childidxs[0]]);  /*lint !e613*/
3480                assert(SCIPexprcurvPower(childbounds, childcurv, exponents[0]) == SCIP_EXPRCURV_UNKNOWN); /* this is exactly the curvature of the node, which is unknown */
3481 
3482                /* if monomial were convex or concave if child were linear, then make child linear */
3483                if( SCIPexprcurvPower(childbounds, SCIP_EXPRCURV_LINEAR, exponents[0]) != SCIP_EXPRCURV_UNKNOWN )
3484                {
3485                   assert(childcurv != SCIP_EXPRCURV_LINEAR);
3486                   SCIPdebugMsg(scip, "reform child %d (univar. monomial) with curv %s into var\n", childidxs[0], SCIPexprcurvGetName(childcurv));
3487                   SCIP_CALL( reformNode2Var(scip, exprgraph, children[childidxs[0]], conshdlr, naddcons, FALSE) );  /*lint !e613*/
3488                   modified = TRUE;
3489                }
3490             }
3491             else
3492             {
3493                /* check if the conditions on the exponents allow for a convex or concave monomial, assuming that the children are linear
3494                 * if one of these conditions is fulfilled but a children curvature does not fit, then make these children linear
3495                 */
3496                int nnegative;
3497                int npositive;
3498                SCIP_Real sum;
3499                SCIP_Bool expcurvpos;
3500                SCIP_Bool expcurvneg;
3501                SCIP_EXPRCURV desiredcurv;
3502 
3503                nnegative = 0; /* number of negative exponents */
3504                npositive = 0; /* number of positive exponents */
3505                sum = 0.0;     /* sum of exponents */
3506                expcurvpos = TRUE; /* whether exp_j * f_j''(x) >= 0 for all factors (assuming f_j >= 0) */
3507                expcurvneg = TRUE; /* whether exp_j * f_j''(x) <= 0 for all factors (assuming f_j >= 0) */
3508 
3509                /* ensure that none of the children have unknown curvature */
3510                for( c = 0; c < SCIPexprgraphGetNodeNChildren(node); ++c )
3511                {
3512                   childcurv = SCIPexprgraphGetNodeCurvature(children[c]);  /*lint !e613*/
3513                   if( childcurv == SCIP_EXPRCURV_UNKNOWN )
3514                   {
3515                      SCIPdebugMessage("reform child %d with unknown curvature into var\n", c);
3516                      SCIP_CALL( reformNode2Var(scip, exprgraph, children[c], conshdlr, naddcons, FALSE) );  /*lint !e613*/
3517                      modified = TRUE;
3518                   }
3519                }
3520                if( modified )
3521                {
3522                   /* refresh curvature information in node, since we changed children */
3523                   SCIP_CALL( SCIPexprgraphUpdateNodeBoundsCurvature(node, INTERVALINFTY, BOUNDTIGHTENING_MINSTRENGTH, TRUE) );
3524                   assert(!SCIPintervalIsEmpty(INTERVALINFTY, SCIPexprgraphGetNodeBounds(node)));
3525 
3526                   modified = FALSE;
3527                }
3528 
3529                for( f = 0; f < nfactors; ++f )
3530                {
3531                   childcurv = SCIPexprgraphGetNodeCurvature(children[childidxs[f]]);  /*lint !e613*/
3532                   assert(childcurv != SCIP_EXPRCURV_UNKNOWN);
3533                   childbounds = SCIPexprgraphGetNodeBounds(children[childidxs[f]]);  /*lint !e613*/
3534                   if( childbounds.inf < 0.0 && childbounds.sup > 0.0 )
3535                      break;
3536 
3537                   if( exponents[f] < 0.0 )
3538                      ++nnegative;
3539                   else
3540                      ++npositive;
3541                   sum += exponents[f];
3542 
3543                   /* negate curvature if factor is negative */
3544                   if( childbounds.inf < 0.0 )
3545                      childcurv = SCIPexprcurvNegate(childcurv);
3546 
3547                   /* check if exp_j * checkcurv is convex (>= 0) and/or concave */
3548                   childcurv = SCIPexprcurvMultiply(exponents[f], childcurv);
3549                   if( !(childcurv & SCIP_EXPRCURV_CONVEX) )
3550                      expcurvpos = FALSE;
3551                   if( !(childcurv & SCIP_EXPRCURV_CONCAVE) )
3552                      expcurvneg = FALSE;
3553                }
3554 
3555                /* if some child can be both positive and negative, then nothing we can do here to get the monomial convex or concave
3556                 * otherwise (i.e., f == nfactors), look further */
3557                desiredcurv = SCIP_EXPRCURV_UNKNOWN;
3558                if( f == nfactors )
3559                {
3560                   /* if all factors are linear, then a product f_j^exp_j with f_j >= 0 is convex if
3561                    * - all exponents are negative, or
3562                    * - all except one exponent j* are negative and exp_j* >= 1 - sum_{j!=j*}exp_j, but the latter is equivalent to sum_j exp_j >= 1
3563                    * further, the product is concave if
3564                    * - all exponents are positive and the sum of exponents is <= 1.0
3565                    *
3566                    * if factors are nonlinear, then we require additionally, that for convexity
3567                    * - each factor is convex if exp_j >= 0, or concave if exp_j <= 0, i.e., exp_j*f_j'' >= 0
3568                    * and for concavity, we require that
3569                    * - all factors are concave, i.e., exp_j*f_j'' <= 0
3570                    */
3571 
3572                   if( nnegative == nfactors || (nnegative == nfactors-1 && SCIPisGE(scip, sum, 1.0)) )
3573                   {
3574                      /* if exponents are such that we can be convex, but children curvature does not fit, make some children linear */
3575                      SCIPdebugMsg(scip, "%d-variate monomial is convex (modulo sign), child curv fits = %u\n", nfactors, expcurvpos);
3576                      /* since current node curvature is set to unknown, there must be such a child, since otherwise the node curvature had to be convex */
3577                      assert(!expcurvpos);
3578                      desiredcurv = SCIP_EXPRCURV_CONVEX;
3579                   }
3580                   else if( npositive == nfactors && SCIPisLE(scip, sum, 1.0) )
3581                   {
3582                      /* if exponents are such that we can be concave, but children curvature does not fit, make some children linear */
3583                      SCIPdebugMsg(scip, "%d-variate monomial is concave (modulo sign), child curv fits = %u\n", nfactors, expcurvneg);
3584                      /* since current node curvature is set to unknown, there must be such a child, since otherwise the node curvature had to be concave */
3585                      assert(!expcurvneg);
3586                      desiredcurv = SCIP_EXPRCURV_CONCAVE;
3587                   }
3588                   else
3589                   {
3590                      /* exponents are such that monomial is neither convex nor concave even if children were linear
3591                       * thus, reformulate monomial below
3592                       */
3593                   }
3594                }
3595 
3596                if( desiredcurv != SCIP_EXPRCURV_UNKNOWN )
3597                {
3598                   for( f = 0; f < nfactors; ++f )
3599                   {
3600                      childcurv = SCIPexprgraphGetNodeCurvature(children[childidxs[f]]);  /*lint !e613*/
3601                      assert(childcurv != SCIP_EXPRCURV_UNKNOWN);
3602                      childbounds = SCIPexprgraphGetNodeBounds(children[childidxs[f]]);  /*lint !e613*/
3603                      assert(childbounds.inf >= 0.0 || childbounds.sup <= 0.0);
3604 
3605                      /* negate curvature if factor is negative */
3606                      if( childbounds.inf < 0.0 )
3607                         childcurv = SCIPexprcurvNegate(childcurv);
3608 
3609                      /* check if exp_j * checkcurv is convex (>= 0) and/or concave */
3610                      childcurv = SCIPexprcurvMultiply(SCIPexprGetMonomialExponents(monomial)[f], childcurv);
3611                      if( (desiredcurv == SCIP_EXPRCURV_CONVEX  && !(childcurv & SCIP_EXPRCURV_CONVEX )) ||
3612                         (desiredcurv == SCIP_EXPRCURV_CONCAVE && !(childcurv & SCIP_EXPRCURV_CONCAVE)) )
3613                      {
3614                         SCIPdebugMsg(scip, "reform child %d (factor %d) with curv %s into var\n",
3615                            childidxs[f], f, SCIPexprcurvGetName(SCIPexprgraphGetNodeCurvature(children[childidxs[f]])));  /*lint !e613*/
3616                         SCIP_CALL( reformNode2Var(scip, exprgraph, children[childidxs[f]], conshdlr, naddcons, FALSE) );  /*lint !e613*/
3617                         modified = TRUE;
3618                      }
3619                   }
3620                }
3621             }
3622 
3623             if( modified )
3624             {
3625                /* refresh curvature information in node, since we changed children, it should be convex or concave now */
3626                SCIP_CALL( SCIPexprgraphUpdateNodeBoundsCurvature(node, INTERVALINFTY, BOUNDTIGHTENING_MINSTRENGTH, TRUE) );
3627                assert(!SCIPintervalIsEmpty(INTERVALINFTY, SCIPexprgraphGetNodeBounds(node)));
3628                assert(SCIPexprgraphGetNodeCurvature(node) != SCIP_EXPRCURV_UNKNOWN);
3629 
3630                /* we are done and can proceed with the next node */
3631                ++i;
3632                break;
3633             }
3634 
3635             /* monomial can only have unknown curvature here, if it has several factors
3636              * or is of form x^a with x both negative and positive and a an odd or negative integer (-> INTPOWER expression)
3637              */
3638             assert(nfactors > 1 ||
3639                (SCIPexprgraphGetNodeBounds(children[childidxs[0]]).inf < 0.0 && SCIPexprgraphGetNodeBounds(children[childidxs[0]]).sup > 0.0 &&
3640                   SCIPisIntegral(scip, exponents[0]) && (exponents[0] < 0.0 || ((int)SCIPround(scip, exponents[0]) % 2 != 0)))
3641                );  /*lint !e613*/
3642 
3643             /* bilinear monomials should not come up here, since simplifier should have turned them into quadratic expression nodes */
3644             assert(!(nfactors == 2 && exponents[0] == 1.0 && exponents[1] == 1.0));
3645 
3646             /* reform monomial if it is a product, or we need it to be on the top of the graph, or if it of the form x^a with a < 0.0 (and thus x having mixed sign, see assert above)
3647              * thus, in the case x^a with a an odd positive integer we assume that cons_signpower will do something */
3648             if( nfactors > 1 || havenonlinparent || exponents[0] < 0.0 )
3649             {
3650                SCIP_EXPRGRAPHNODE* auxnode;
3651                SCIP_EXPRGRAPHNODE** factors;
3652                int naddconsbefore = *naddcons;
3653 
3654                if( nfactors > 1 )
3655                {
3656                   SCIP_CALL( SCIPallocBufferArray(scip, &factors, nfactors) );
3657                   for( f = 0; f < nfactors; ++f )
3658                      factors[f] = children[childidxs[f]];  /*lint !e613*/
3659                }
3660                else
3661                   factors = &children[childidxs[0]];  /*lint !e613*/
3662 
3663                SCIPdebugMsg(scip, "reform monomial node, create auxvar = %u\n", havenonlinparent);
3664                /* get new auxnode for monomial
3665                 * if node has parents and monomial is of indefinite form x^a, then also create auxvar for it, since otherwise we create a auxnode with unknown curvature
3666                 * note, that the case x^a with positive and odd a will still give an indefinite node (without parents), where we assume that signpower will pick it up at some point
3667                 */
3668                SCIP_CALL( reformMonomial(scip, exprgraph, nfactors, factors, exponents, &auxnode, havenonlinparent, SCIPexprgraphGetNodeDepth(node), naddcons) );
3669 
3670                if( nfactors > 1 )
3671                {
3672                   SCIPfreeBufferArray(scip, &factors);
3673                }
3674 
3675                /* create node for monomialcoef * auxnode + monomialconstant, if not identical to auxnode */
3676                if( SCIPexprgraphGetNodePolynomialConstant(node) != 0.0 || coef != 1.0 )
3677                {
3678                   SCIP_EXPRGRAPHNODE* replnode;
3679 
3680                   SCIP_CALL( SCIPexprgraphCreateNodeLinear(SCIPblkmem(scip), &replnode, 1, &coef, SCIPexprgraphGetNodePolynomialConstant(node)) );
3681                   SCIP_CALL( SCIPexprgraphAddNode(exprgraph, replnode, -1, 1, &auxnode) );
3682                   auxnode = replnode;
3683                }
3684 
3685                /* replace node by auxnode and refresh its curvature */
3686                SCIP_CALL( reformReplaceNode(exprgraph, &node, auxnode, conshdlr, *naddcons - naddconsbefore) );
3687                SCIP_CALL( SCIPexprgraphUpdateNodeBoundsCurvature(auxnode, INTERVALINFTY, BOUNDTIGHTENING_MINSTRENGTH, TRUE) );
3688                assert(!SCIPintervalIsEmpty(INTERVALINFTY, SCIPexprgraphGetNodeBounds(auxnode)));
3689 
3690                break;
3691             }
3692             else
3693             {
3694                SCIPdebugMsg(scip, "no reformulation of monomial node, assume signpower will take care of it\n");
3695             }
3696 
3697             ++i;
3698             break;
3699          }
3700 
3701          case SCIP_EXPR_USER:
3702          {
3703             /* ensure all children are linear */
3704             SCIP_CALL( reformEnsureChildrenMinCurvature( scip, exprgraph, node, SCIP_EXPRCURV_LINEAR, conshdlr, naddcons ) );
3705 
3706             /* unknown curvature can be handled by user estimator callback or interval gradient */
3707             /*
3708             if( SCIPexprgraphGetNodeCurvature( node ) == SCIP_EXPRCURV_UNKNOWN )
3709             {
3710                SCIPerrorMessage("user expression with unknown curvature not supported\n");
3711                return SCIP_ERROR;
3712             }
3713             */
3714 
3715             ++i;
3716             break;
3717          }
3718 
3719          case SCIP_EXPR_LAST:
3720             SCIPABORT();
3721             break;
3722          }
3723       }
3724    }
3725 
3726    /* for constraints with concave f(g(x)) with linear g:R^n -> R, n>1, reformulate to get a univariate concave function, since this is easier to underestimate
3727     * @todo this does not work yet for sums of functions other than polynomials
3728     */
3729    for( c = 0; c < nconss; ++c )
3730    {
3731       SCIP_EXPRGRAPHNODE* multivarnode;
3732       SCIP_EXPRCURV curv;
3733 
3734       assert(conss[c] != NULL);  /*lint !e613*/
3735 
3736       /* skip constraints that are to be deleted */
3737       if( SCIPconsIsDeleted(conss[c]) )  /*lint !e613*/
3738          continue;
3739 
3740       consdata = SCIPconsGetData(conss[c]);  /*lint !e613*/
3741       assert(consdata != NULL);
3742 
3743       if( consdata->exprgraphnode == NULL )
3744          continue;
3745 
3746       /* after reformulation, force a round of backpropagation in expression graph for all constraints,
3747        * since new variables (nlreform*) may now be used in existing constraints and we want domain restrictions
3748        * of operators propagated for these variables
3749        */
3750       consdata->forcebackprop = TRUE;
3751 
3752       if( SCIPexprgraphGetNodeOperator(consdata->exprgraphnode) == SCIP_EXPR_POLYNOMIAL )
3753       {
3754          SCIP_EXPRDATA_MONOMIAL* monomial;
3755          int m;
3756          int f;
3757 
3758          for( m = 0; m < SCIPexprgraphGetNodePolynomialNMonomials(consdata->exprgraphnode); ++m )
3759          {
3760             SCIP_CALL( SCIPexprgraphGetNodePolynomialMonomialCurvature(consdata->exprgraphnode, m, INTERVALINFTY, &curv) );
3761 
3762             monomial = SCIPexprgraphGetNodePolynomialMonomials(consdata->exprgraphnode)[m];
3763             assert(monomial != NULL);
3764 
3765             /* if nothing concave, then continue */
3766             if( (SCIPisInfinity(scip,  consdata->rhs) || curv != SCIP_EXPRCURV_CONCAVE) &&
3767                ( SCIPisInfinity(scip, -consdata->lhs) || curv != SCIP_EXPRCURV_CONVEX) )
3768                continue;
3769 
3770             for( f = 0; f < SCIPexprGetMonomialNFactors(monomial); ++f )
3771             {
3772                multivarnode = SCIPexprgraphGetNodeChildren(consdata->exprgraphnode)[SCIPexprGetMonomialChildIndices(monomial)[f]];
3773 
3774                /* search for a descendant of node that has > 1 children
3775                 * after simplifier run, there should be no constant expressions left
3776                 */
3777                while( SCIPexprgraphGetNodeNChildren(multivarnode) == 1 )
3778                   multivarnode = SCIPexprgraphGetNodeChildren(multivarnode)[0];
3779 
3780                /* if node expression is obviously univariate, then continue */
3781                if( SCIPexprgraphGetNodeNChildren(multivarnode) == 0 )
3782                {
3783                   assert(SCIPexprgraphGetNodeOperator(multivarnode) == SCIP_EXPR_CONST || SCIPexprgraphGetNodeOperator(multivarnode) == SCIP_EXPR_VARIDX);
3784                   continue;
3785                }
3786 
3787                /* if multivarnode is a linear expression, then replace this by an auxiliary variable/node
3788                 * mark auxiliary variable as not to multiaggregate, so SCIP cannot undo what we just did
3789                 */
3790                if( SCIPexprgraphGetNodeCurvature(multivarnode) == SCIP_EXPRCURV_LINEAR )
3791                {
3792                   SCIPdebugMsg(scip, "replace linear multivariate node %p(%d,%d) in expression of cons <%s> by auxvar\n",
3793                      (void*)multivarnode, SCIPexprgraphGetNodeDepth(multivarnode), SCIPexprgraphGetNodePosition(multivarnode), SCIPconsGetName(conss[c]));  /*lint !e613*/
3794                   SCIPdebugPrintCons(scip, conss[c], NULL);  /*lint !e613*/
3795                   SCIP_CALL( reformNode2Var(scip, exprgraph, multivarnode, conshdlr, naddcons, TRUE) );
3796                }
3797             }
3798          }
3799       }
3800       else
3801       {
3802          curv = SCIPexprgraphGetNodeCurvature(consdata->exprgraphnode);
3803 
3804          /* if nothing concave, then continue */
3805          if( (SCIPisInfinity(scip,  consdata->rhs) || curv != SCIP_EXPRCURV_CONCAVE) &&
3806             ( SCIPisInfinity(scip, -consdata->lhs) || curv != SCIP_EXPRCURV_CONVEX) )
3807             continue;
3808 
3809          /* search for a descendant of node that has > 1 children
3810           * after simplifier run, there should be no constant expressions left
3811           */
3812          multivarnode = consdata->exprgraphnode;
3813          while( SCIPexprgraphGetNodeNChildren(multivarnode) == 1 )
3814             multivarnode = SCIPexprgraphGetNodeChildren(multivarnode)[0];
3815 
3816          /* if node expression is obviously univariate, then continue */
3817          if( SCIPexprgraphGetNodeNChildren(multivarnode) == 0 )
3818          {
3819             assert(SCIPexprgraphGetNodeOperator(multivarnode) == SCIP_EXPR_CONST || SCIPexprgraphGetNodeOperator(multivarnode) == SCIP_EXPR_VARIDX);
3820             continue;
3821          }
3822 
3823          /* if node itself is multivariate, then continue */
3824          if( multivarnode == consdata->exprgraphnode )
3825             continue;
3826 
3827          /* if multivarnode is a linear expression, then replace this by an auxiliary variable/node
3828           * mark auxiliary variable as not to multiaggregate, so SCIP cannot undo what we just did
3829           */
3830          if( SCIPexprgraphGetNodeCurvature(multivarnode) == SCIP_EXPRCURV_LINEAR )
3831          {
3832             SCIPdebugMsg(scip, "replace linear multivariate node %p(%d,%d) in expression of cons <%s> by auxvar\n",
3833                (void*)multivarnode, SCIPexprgraphGetNodeDepth(multivarnode), SCIPexprgraphGetNodePosition(multivarnode), SCIPconsGetName(conss[c]));  /*lint !e613*/
3834             SCIPdebugPrintCons(scip, conss[c], NULL);  /*lint !e613*/
3835             SCIP_CALL( reformNode2Var(scip, exprgraph, multivarnode, conshdlr, naddcons, TRUE) );
3836          }
3837       }
3838    }
3839 
3840    conshdlrdata->isreformulated = TRUE;
3841 
3842    return SCIP_OKAY;
3843 }
3844 
3845 /** computes activity and violation of a constraint
3846  *
3847  * During presolving and if the constraint is active, it is assumes that SCIPexprgraphEval has been called for sol before.
3848  *
3849  * If a solution is found to violate the variable bounds, then violation calculation is stopped and solviolbounds is set to TRUE.
3850  */
3851 static
computeViolation(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS * cons,SCIP_SOL * sol,SCIP_Bool * solviolbounds)3852 SCIP_RETCODE computeViolation(
3853    SCIP*                 scip,               /**< SCIP data structure */
3854    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
3855    SCIP_CONS*            cons,               /**< nonlinear constraint */
3856    SCIP_SOL*             sol,                /**< solution or NULL if LP solution should be used */
3857    SCIP_Bool*            solviolbounds       /**< buffer to indicate whether solution is found to violate variable bounds by more than feastol */
3858    )
3859 {  /*lint --e{666}*/
3860    SCIP_CONSHDLRDATA* conshdlrdata;
3861    SCIP_CONSDATA* consdata;
3862    SCIP_VAR* var;
3863    SCIP_Real varval;
3864    int i;
3865 
3866    assert(scip != NULL);
3867    assert(conshdlr != NULL);
3868    assert(cons != NULL);
3869    assert(solviolbounds != NULL);
3870 
3871    conshdlrdata = SCIPconshdlrGetData(conshdlr);
3872    assert(conshdlrdata != NULL);
3873    assert(conshdlrdata->exprinterpreter != NULL);
3874 
3875    consdata = SCIPconsGetData(cons);
3876    assert(consdata != NULL);
3877 
3878    consdata->activity = 0.0;
3879    consdata->lhsviol = 0.0;
3880    consdata->rhsviol = 0.0;
3881    varval = 0.0;
3882    *solviolbounds = FALSE;
3883 
3884    for( i = 0; i < consdata->nlinvars; ++i )
3885    {
3886       SCIP_Real activity;
3887 
3888       var = consdata->linvars[i];
3889       varval = SCIPgetSolVal(scip, sol, var);
3890 
3891       /* project onto local box, in case the LP solution is slightly outside the bounds (which is not our job to enforce) */
3892       if( sol == NULL )
3893       {
3894          /* with non-initial columns, this might fail because variables can shortly be a column variable before entering the LP and have value 0.0 in this case */
3895          if( (!SCIPisInfinity(scip, -SCIPvarGetLbLocal(var)) && !SCIPisFeasGE(scip, varval, SCIPvarGetLbLocal(var))) ||
3896              (!SCIPisInfinity(scip,  SCIPvarGetUbLocal(var)) && !SCIPisFeasLE(scip, varval, SCIPvarGetUbLocal(var))) )
3897          {
3898             *solviolbounds = TRUE;
3899             return SCIP_OKAY;
3900          }
3901          varval = MAX(SCIPvarGetLbLocal(var), MIN(SCIPvarGetUbLocal(var), varval));
3902       }
3903       activity = consdata->lincoefs[i] * varval;
3904 
3905       /* the contribution of a variable with |varval| = +inf is +inf when activity > 0.0, -inf when activity < 0.0, and
3906        * 0.0 otherwise
3907        */
3908       if( SCIPisInfinity(scip, REALABS(varval)) )
3909       {
3910          if( activity > 0.0 && !SCIPisInfinity(scip, consdata->rhs) )
3911          {
3912             consdata->activity = SCIPinfinity(scip);
3913             consdata->rhsviol = SCIPinfinity(scip);
3914             return SCIP_OKAY;
3915          }
3916 
3917          if( activity < 0.0 && !SCIPisInfinity(scip, -consdata->lhs) )
3918          {
3919             consdata->activity = -SCIPinfinity(scip);
3920             consdata->lhsviol = SCIPinfinity(scip);
3921             return SCIP_OKAY;
3922          }
3923       }
3924 
3925       consdata->activity += activity;
3926    }
3927 
3928    for( i = 0; i < consdata->nexprtrees; ++i )
3929    {
3930       SCIP_Real activity;
3931       SCIP_Real val;
3932       int nvars;
3933 
3934       /* compile expression tree, if not done before */
3935       if( SCIPexprtreeGetInterpreterData(consdata->exprtrees[i]) == NULL )
3936       {
3937          SCIP_CALL( SCIPexprintCompile(conshdlrdata->exprinterpreter, consdata->exprtrees[i]) );
3938       }
3939 
3940       nvars = SCIPexprtreeGetNVars(consdata->exprtrees[i]);
3941 
3942       if( nvars == 1 )
3943       {
3944          /* in the not so unusual case that an expression has only one variable, we do not need to extra allocate memory */
3945          var = SCIPexprtreeGetVars(consdata->exprtrees[i])[0];
3946          varval = SCIPgetSolVal(scip, sol, var);
3947 
3948          /* project onto local box, in case the LP solution is slightly outside the bounds (and then cannot be evaluated) */
3949          if( sol == NULL )
3950          {
3951             /* with non-initial columns, this might fail because variables can shortly be a column variable before entering the LP and have value 0.0 in this case */
3952             if( (!SCIPisInfinity(scip, -SCIPvarGetLbLocal(var)) && !SCIPisFeasGE(scip, varval, SCIPvarGetLbLocal(var))) ||
3953                 (!SCIPisInfinity(scip,  SCIPvarGetUbLocal(var)) && !SCIPisFeasLE(scip, varval, SCIPvarGetUbLocal(var))) )
3954             {
3955                *solviolbounds = TRUE;
3956                return SCIP_OKAY;
3957             }
3958             varval = MAX(SCIPvarGetLbLocal(var), MIN(SCIPvarGetUbLocal(var), varval));
3959          }
3960 
3961          /* coverity[callee_ptr_arith] */
3962          SCIP_CALL( SCIPexprintEval(conshdlrdata->exprinterpreter, consdata->exprtrees[i], &varval, &val) );
3963       }
3964       else
3965       {
3966          SCIP_Real* x;
3967          int j;
3968 
3969          SCIP_CALL( SCIPallocBufferArray(scip, &x, nvars) );
3970 
3971          for( j = 0; j < nvars; ++j )
3972          {
3973             var = SCIPexprtreeGetVars(consdata->exprtrees[i])[j];
3974             varval = SCIPgetSolVal(scip, sol, var);
3975 
3976             /* project onto local box, in case the LP solution is slightly outside the bounds (and then cannot be evaluated) */
3977             if( sol == NULL )
3978             {
3979                /* with non-initial columns, this might fail because variables can shortly be a column variable before entering the LP and have value 0.0 in this case */
3980                if( (!SCIPisInfinity(scip, -SCIPvarGetLbLocal(var)) && !SCIPisFeasGE(scip, varval, SCIPvarGetLbLocal(var))) ||
3981                    (!SCIPisInfinity(scip,  SCIPvarGetUbLocal(var)) && !SCIPisFeasLE(scip, varval, SCIPvarGetUbLocal(var))) )
3982                {
3983                   *solviolbounds = TRUE;
3984                   SCIPfreeBufferArray(scip, &x);
3985                   return SCIP_OKAY;
3986                }
3987                varval = MAX(SCIPvarGetLbLocal(var), MIN(SCIPvarGetUbLocal(var), varval));
3988             }
3989 
3990             x[j] = varval;
3991          }
3992 
3993          SCIP_CALL( SCIPexprintEval(conshdlrdata->exprinterpreter, consdata->exprtrees[i], x, &val) );
3994 
3995          SCIPfreeBufferArray(scip, &x);
3996       }
3997 
3998       /* set the activity to infinity if a function evaluation was not valid (e.g., sqrt(-1) ) */
3999       if( !SCIPisFinite(val) )
4000       {
4001          consdata->activity = SCIPinfinity(scip);
4002          if( !SCIPisInfinity(scip, -consdata->lhs) )
4003             consdata->lhsviol = SCIPinfinity(scip);
4004          if( !SCIPisInfinity(scip,  consdata->rhs) )
4005             consdata->rhsviol = SCIPinfinity(scip);
4006          return SCIP_OKAY;
4007       }
4008 
4009       /* the contribution of an expression with |val| = +inf is +inf when its coefficient is > 0.0, -inf when its coefficient is < 0.0, and
4010        * 0.0 otherwise
4011        */
4012       activity = consdata->nonlincoefs[i] * val;
4013       if( SCIPisInfinity(scip, REALABS(val)) )
4014       {
4015          if( activity > 0.0 && !SCIPisInfinity(scip, consdata->rhs) )
4016          {
4017             consdata->activity = SCIPinfinity(scip);
4018             consdata->rhsviol = SCIPinfinity(scip);
4019             return SCIP_OKAY;
4020          }
4021 
4022          if( activity < 0.0  && !SCIPisInfinity(scip, -consdata->lhs) )
4023          {
4024             consdata->activity = -SCIPinfinity(scip);
4025             consdata->lhsviol = SCIPinfinity(scip);
4026             return SCIP_OKAY;
4027          }
4028       }
4029 
4030       consdata->activity += activity;
4031    }
4032 
4033    if( consdata->nexprtrees == 0 && consdata->exprgraphnode != NULL )
4034    {
4035       SCIP_Real val;
4036 
4037       assert(SCIPgetStage(scip) >= SCIP_STAGE_INITPRESOLVE && SCIPgetStage(scip) <= SCIP_STAGE_EXITPRESOLVE);
4038 
4039       val = SCIPexprgraphGetNodeVal(consdata->exprgraphnode);
4040       assert(val != SCIP_INVALID);  /*lint !e777*/
4041 
4042       /* set the activity to infinity if a function evaluation was not valid (e.g., sqrt(-1) ) */
4043       if( !SCIPisFinite(val) )
4044       {
4045          consdata->activity = SCIPinfinity(scip);
4046          if( !SCIPisInfinity(scip, -consdata->lhs) )
4047             consdata->lhsviol = SCIPinfinity(scip);
4048          if( !SCIPisInfinity(scip,  consdata->rhs) )
4049             consdata->rhsviol = SCIPinfinity(scip);
4050          return SCIP_OKAY;
4051       }
4052 
4053       if( SCIPisInfinity(scip, val) && !SCIPisInfinity(scip, consdata->rhs) )
4054       {
4055          consdata->activity = SCIPinfinity(scip);
4056          consdata->rhsviol = SCIPinfinity(scip);
4057          return SCIP_OKAY;
4058       }
4059       else if( SCIPisInfinity(scip, -val) && !SCIPisInfinity(scip, -consdata->lhs) )
4060       {
4061          consdata->activity = -SCIPinfinity(scip);
4062          consdata->lhsviol = SCIPinfinity(scip);
4063          return SCIP_OKAY;
4064       }
4065 
4066       consdata->activity += val;
4067    }
4068 
4069    if( !SCIPisInfinity(scip, -consdata->lhs) && SCIPisGT(scip, consdata->lhs - consdata->activity, SCIPfeastol(scip)) )
4070       consdata->lhsviol = consdata->lhs - consdata->activity;
4071    else
4072       consdata->lhsviol = 0.0;
4073 
4074    if( !SCIPisInfinity(scip,  consdata->rhs) && SCIPisGT(scip, consdata->activity - consdata->rhs, SCIPfeastol(scip)) )
4075       consdata->rhsviol = consdata->activity - consdata->rhs;
4076    else
4077       consdata->rhsviol = 0.0;
4078 
4079    /* update absolute and relative violation of the solution */
4080    if( sol != NULL )
4081    {
4082       SCIP_Real absviol;
4083       SCIP_Real relviol;
4084       SCIP_Real lhsrelviol;
4085       SCIP_Real rhsrelviol;
4086 
4087       absviol = MAX(consdata->lhsviol, consdata->rhsviol);
4088 
4089       lhsrelviol = SCIPrelDiff(consdata->lhs, consdata->activity);
4090       rhsrelviol = SCIPrelDiff(consdata->activity, consdata->rhs);
4091       relviol = MAX(lhsrelviol, rhsrelviol);
4092 
4093       SCIPupdateSolConsViolation(scip, sol, absviol, relviol);
4094    }
4095 
4096    return SCIP_OKAY;
4097 }
4098 
4099 /** computes violation of a set of constraints
4100  *
4101  * If the solution is found to violate bounds of some variable in some constraint, then violation computation is stopped and solviolbounds is set to TRUE.
4102  */
4103 static
computeViolations(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS ** conss,int nconss,SCIP_SOL * sol,SCIP_Bool * solviolbounds,SCIP_CONS ** maxviolcon)4104 SCIP_RETCODE computeViolations(
4105    SCIP*                 scip,               /**< SCIP data structure */
4106    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
4107    SCIP_CONS**           conss,              /**< constraints */
4108    int                   nconss,             /**< number of constraints */
4109    SCIP_SOL*             sol,                /**< solution or NULL if LP solution should be used */
4110    SCIP_Bool*            solviolbounds,      /**< buffer to indicate whether solution violates bounds of some variable by more than feastol */
4111    SCIP_CONS**           maxviolcon          /**< buffer to store constraint with largest violation, or NULL if solution is feasible */
4112    )
4113 {
4114    SCIP_CONSDATA* consdata;
4115    SCIP_Real      viol;
4116    SCIP_Real      maxviol;
4117    int            c;
4118 
4119    assert(scip != NULL);
4120    assert(conshdlr != NULL);
4121    assert(conss != NULL || nconss == 0);
4122    assert(maxviolcon != NULL);
4123    assert(solviolbounds != NULL);
4124 
4125    if( SCIPgetStage(scip) >= SCIP_STAGE_INITPRESOLVE && SCIPgetStage(scip) <= SCIP_STAGE_EXITPRESOLVE )
4126    {
4127       SCIP_CONSHDLRDATA* conshdlrdata;
4128       SCIP_Real* varvals;
4129 
4130       conshdlrdata = SCIPconshdlrGetData(conshdlr);
4131       assert(conshdlrdata != NULL);
4132       assert(conshdlrdata->exprgraph != NULL);
4133 
4134       SCIP_CALL( SCIPallocBufferArray(scip, &varvals, SCIPexprgraphGetNVars(conshdlrdata->exprgraph)) );
4135       SCIP_CALL( SCIPgetSolVals(scip, sol, SCIPexprgraphGetNVars(conshdlrdata->exprgraph), (SCIP_VAR**)SCIPexprgraphGetVars(conshdlrdata->exprgraph), varvals) );
4136 
4137       SCIP_CALL( SCIPexprgraphEval(conshdlrdata->exprgraph, varvals) );
4138 
4139       SCIPfreeBufferArray(scip, &varvals);
4140    }
4141 
4142    *maxviolcon = NULL;
4143 
4144    maxviol = 0.0;
4145 
4146    for( c = 0; c < nconss; ++c )
4147    {
4148       assert(conss != NULL);
4149       assert(conss[c] != NULL);
4150 
4151       SCIP_CALL( computeViolation(scip, conshdlr, conss[c], sol, solviolbounds) );
4152 
4153       /* stop if solution violates bounds */
4154       if( *solviolbounds )
4155          break;
4156 
4157       consdata = SCIPconsGetData(conss[c]);
4158       assert(consdata != NULL);
4159 
4160       viol = MAX(consdata->lhsviol, consdata->rhsviol);
4161       if( viol > maxviol && SCIPisGT(scip, viol, SCIPfeastol(scip)) )
4162       {
4163          maxviol = viol;
4164          *maxviolcon = conss[c];
4165       }
4166 
4167       /* SCIPdebugMsg(scip, "constraint <%s> violated by (%g, %g), activity = %g\n", SCIPconsGetName(conss[c]), consdata->lhsviol, consdata->rhsviol, consdata->activity); */
4168    }
4169 
4170    return SCIP_OKAY;
4171 }
4172 
4173 /** adds linearization of a constraints expression tree in reference point to a row */
4174 static
addLinearization(SCIP * scip,SCIP_EXPRINT * exprint,SCIP_CONS * cons,int exprtreeidx,SCIP_Real * x,SCIP_Bool newx,SCIP_ROWPREP * rowprep,SCIP_Bool * success)4175 SCIP_RETCODE addLinearization(
4176    SCIP*                 scip,               /**< SCIP data structure */
4177    SCIP_EXPRINT*         exprint,            /**< expression interpreter */
4178    SCIP_CONS*            cons,               /**< constraint */
4179    int                   exprtreeidx,        /**< for which tree a linearization should be added */
4180    SCIP_Real*            x,                  /**< value of expression tree variables where to generate cut */
4181    SCIP_Bool             newx,               /**< whether the last evaluation of the expression with the expression interpreter was not at x */
4182    SCIP_ROWPREP*         rowprep,            /**< rowprep where to add linearization */
4183    SCIP_Bool*            success             /**< buffer to store whether a linearization was succefully added to the row */
4184    )
4185 {
4186    SCIP_CONSDATA* consdata;
4187    SCIP_EXPRTREE* exprtree;
4188    SCIP_Real treecoef;
4189    SCIP_Real val;
4190    SCIP_Real* grad;
4191    SCIP_Real constant = 0.0;
4192    SCIP_Bool perturbedx;
4193    int nvars;
4194    int i;
4195 
4196    assert(scip != NULL);
4197    assert(cons != NULL);
4198    assert(x    != NULL);
4199    assert(rowprep  != NULL);
4200    assert(success != NULL);
4201 
4202    consdata = SCIPconsGetData(cons);
4203    assert(consdata != NULL);
4204    assert(exprtreeidx >= 0);
4205    assert(exprtreeidx < consdata->nexprtrees);
4206    assert(consdata->exprtrees != NULL);
4207 
4208    exprtree = consdata->exprtrees[exprtreeidx];
4209    assert(exprtree != NULL);
4210    assert(newx || SCIPexprtreeGetInterpreterData(exprtree) != NULL);
4211 
4212    treecoef = consdata->nonlincoefs[exprtreeidx];
4213 
4214    *success = FALSE;
4215 
4216    /* compile expression if evaluated the first time; can only happen if newx is FALSE */
4217    if( newx && SCIPexprtreeGetInterpreterData(exprtree) == NULL )
4218    {
4219       SCIP_CALL( SCIPexprintCompile(exprint, exprtree) );
4220    }
4221 
4222    nvars = SCIPexprtreeGetNVars(exprtree);
4223    SCIP_CALL( SCIPallocBufferArray(scip, &grad, nvars) );
4224 
4225    perturbedx = FALSE;
4226    do
4227    {
4228       /* get value and gradient */
4229       SCIP_CALL( SCIPexprintGrad(exprint, exprtree, x, newx, &val, grad) );
4230       if( SCIPisFinite(val) && !SCIPisInfinity(scip, REALABS(val)) )
4231       {
4232          val *= treecoef;
4233          /* check gradient entries and compute constant f(refx) - grad * refx */
4234          constant = val;
4235          for( i = 0; i < nvars; ++i )
4236          {
4237             if( !SCIPisFinite(grad[i]) || SCIPisInfinity(scip, grad[i]) || SCIPisInfinity(scip, -grad[i]) )
4238                break;
4239 
4240             grad[i] *= treecoef;
4241             constant -= grad[i] * x[i];
4242 
4243             /* try to perturb x if the constant is too large */
4244             if( SCIPisInfinity(scip, REALABS(constant)) )
4245                break;
4246 
4247             /* coefficients smaller than epsilon are rounded to 0.0 when added to row, this can be wrong if variable value is very large (bad numerics)
4248              * in this case, set gradient to 0.0 here, but modify constant so that cut is still valid (if possible)
4249              * i.e., estimate grad[i]*x >= grad[i] * bound(x) or grad[i]*x <= grad[i] * bound(x), depending on whether we compute an underestimator (convex) or an overestimator (concave)
4250              * if required bound of x is not finite, then give up
4251              */
4252             if( grad[i] != 0.0 && SCIPisZero(scip, grad[i]) )
4253             {
4254                SCIP_VAR* var;
4255                SCIP_Real xbnd;
4256 
4257                var = SCIPexprtreeGetVars(exprtree)[i];
4258                if( consdata->curvatures[exprtreeidx] & SCIP_EXPRCURV_CONVEX )
4259                {
4260                   xbnd = grad[i] > 0.0 ? SCIPvarGetLbGlobal(var) : SCIPvarGetUbGlobal(var);
4261                }
4262                else
4263                {
4264                   assert(consdata->curvatures[exprtreeidx] & SCIP_EXPRCURV_CONCAVE);
4265                   xbnd = grad[i] > 0.0 ? SCIPvarGetUbGlobal(var) : SCIPvarGetLbGlobal(var);
4266                }
4267                if( !SCIPisInfinity(scip, REALABS(xbnd)) )
4268                {
4269                   SCIPdebugMsg(scip, "var <%s> [%g,%g] has tiny gradient %g, replace coefficient by constant %g\n",
4270                      SCIPvarGetName(var), SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var), grad[i], grad[i] * xbnd);
4271                   constant += grad[i] * xbnd;
4272                   grad[i] = 0.0;
4273                }
4274                else
4275                {
4276                   *success = FALSE;
4277                   SCIPdebugMsg(scip, "skipping linearization, var <%s> [%g,%g] has tiny gradient %g but no finite bound in this direction\n",
4278                      SCIPvarGetName(var), SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var), grad[i]);
4279                   SCIPfreeBufferArray(scip, &grad);
4280                   return SCIP_OKAY;
4281                }
4282             }
4283          }
4284 
4285          if( i == nvars )
4286             break;
4287       }
4288 
4289       SCIPdebugMsg(scip, "got nonfinite value in evaluation or gradient of <%s>: ", SCIPconsGetName(cons));
4290       if( !perturbedx )
4291       {
4292          SCIP_Real lb;
4293          SCIP_Real ub;
4294 
4295          SCIPdebugMsgPrint(scip, "perturbing reference point and trying again\n");
4296          for( i = 0; i < nvars; ++i )
4297          {
4298             lb = SCIPvarGetLbGlobal(SCIPexprtreeGetVars(exprtree)[i]);
4299             ub = SCIPvarGetUbGlobal(SCIPexprtreeGetVars(exprtree)[i]);
4300             if( SCIPisEQ(scip, x[i], lb) )
4301                x[i] += MIN(0.9*(ub-lb), i*SCIPfeastol(scip));  /*lint !e666*/
4302             else if( SCIPisEQ(scip, x[i], ub) )
4303                x[i] -= MIN(0.9*(ub-lb), i*SCIPfeastol(scip));  /*lint !e666*/
4304             else
4305                x[i] += MIN3(0.9*(ub-x[i]), 0.9*(x[i]-lb), i*SCIPfeastol(scip)) * (i%2 != 0 ? -1.0 : 1.0);  /*lint !e666*/
4306          }
4307          newx = TRUE;
4308          perturbedx = TRUE;
4309       }
4310       else
4311       {
4312          SCIPdebugMsgPrint(scip, "skipping linearization\n");
4313          SCIPfreeBufferArray(scip, &grad);
4314          return SCIP_OKAY;
4315       }
4316    }
4317    while( TRUE );  /*lint !e506*/
4318 
4319    /* add linearization to SCIP row */
4320    SCIPaddRowprepConstant(rowprep, constant);
4321    SCIP_CALL( SCIPaddRowprepTerms(scip, rowprep, nvars, SCIPexprtreeGetVars(exprtree), grad) );
4322 
4323    *success = TRUE;
4324 
4325    SCIPfreeBufferArray(scip, &grad);
4326 
4327    SCIPdebugMsg(scip, "added linearization for tree %d of constraint <%s>\n", exprtreeidx, SCIPconsGetName(cons));
4328    SCIPdebug( SCIPprintRowprep(scip, rowprep, NULL) );
4329 
4330    return SCIP_OKAY;
4331 }
4332 
4333 /** adds secant of a constraints univariate expression tree in reference point to a row */
4334 static
addConcaveEstimatorUnivariate(SCIP * scip,SCIP_CONS * cons,int exprtreeidx,SCIP_ROWPREP * rowprep,SCIP_Bool * success)4335 SCIP_RETCODE addConcaveEstimatorUnivariate(
4336    SCIP*                 scip,               /**< SCIP data structure */
4337    SCIP_CONS*            cons,               /**< constraint */
4338    int                   exprtreeidx,        /**< for which tree a secant should be added */
4339    SCIP_ROWPREP*         rowprep,            /**< rowprep where to add estimator */
4340    SCIP_Bool*            success             /**< buffer to store whether a secant was succefully added to the row */
4341    )
4342 {
4343    SCIP_CONSDATA* consdata;
4344    SCIP_EXPRTREE* exprtree;
4345    SCIP_Real      treecoef;
4346    SCIP_VAR*      var;
4347    SCIP_Real      xlb;
4348    SCIP_Real      xub;
4349    SCIP_Real      vallb;
4350    SCIP_Real      valub;
4351    SCIP_Real      slope;
4352    SCIP_Real      constant;
4353 
4354    assert(scip != NULL);
4355    assert(cons != NULL);
4356    assert(rowprep  != NULL);
4357    assert(success != NULL);
4358 
4359    consdata = SCIPconsGetData(cons);
4360    assert(consdata != NULL);
4361    assert(exprtreeidx >= 0);
4362    assert(exprtreeidx < consdata->nexprtrees);
4363    assert(consdata->exprtrees != NULL);
4364 
4365    exprtree = consdata->exprtrees[exprtreeidx];
4366    assert(exprtree != NULL);
4367    assert(SCIPexprtreeGetNVars(exprtree) == 1);
4368 
4369    treecoef = consdata->nonlincoefs[exprtreeidx];
4370 
4371    *success = FALSE;
4372 
4373    var = SCIPexprtreeGetVars(exprtree)[0];
4374    xlb = SCIPvarGetLbLocal(var);
4375    xub = SCIPvarGetUbLocal(var);
4376 
4377    /* if variable is unbounded, then cannot really compute secant */
4378    if( SCIPisInfinity(scip, -xlb) || SCIPisInfinity(scip, xub) )
4379    {
4380       SCIPdebugMsg(scip, "skip secant for tree %d of constraint <%s> since variable is unbounded\n", exprtreeidx, SCIPconsGetName(cons));
4381       return SCIP_OKAY;
4382    }
4383    assert(SCIPisLE(scip, xlb, xub));
4384 
4385    /* coverity[callee_ptr_arith] */
4386    SCIP_CALL( SCIPexprtreeEval(exprtree, &xlb, &vallb) );
4387    if( !SCIPisFinite(vallb) || SCIPisInfinity(scip, REALABS(vallb)) )
4388    {
4389       SCIPdebugMsg(scip, "skip secant for tree %d of constraint <%s> since function cannot be evaluated in lower bound\n", exprtreeidx, SCIPconsGetName(cons));
4390       return SCIP_OKAY;
4391    }
4392    vallb *= treecoef;
4393 
4394    /* coverity[callee_ptr_arith] */
4395    SCIP_CALL( SCIPexprtreeEval(exprtree, &xub, &valub) );
4396    if( !SCIPisFinite(valub) || SCIPisInfinity(scip, REALABS(valub)) )
4397    {
4398       SCIPdebugMsg(scip, "skip secant for tree %d of constraint <%s> since function cannot be evaluated in upper bound\n", exprtreeidx, SCIPconsGetName(cons));
4399       return SCIP_OKAY;
4400    }
4401    valub *= treecoef;
4402 
4403    if( SCIPisEQ(scip, xlb, xub) )
4404    {
4405       slope = 0.0;
4406       /* choose most conservative value for the cut */
4407       if( rowprep->sidetype == SCIP_SIDETYPE_LEFT )
4408          constant = MAX(vallb, valub);
4409       else
4410          constant = MIN(vallb, valub);
4411    }
4412    else
4413    {
4414       slope = (valub - vallb) / (xub - xlb);
4415       constant = vallb - slope * xlb;
4416    }
4417 
4418    /* add secant to SCIP row */
4419    SCIPaddRowprepConstant(rowprep, constant);
4420    SCIP_CALL( SCIPaddRowprepTerm(scip, rowprep, var, slope) );
4421 
4422    *success = TRUE;
4423 
4424    SCIPdebugMsg(scip, "added secant for tree %d of constraint <%s>, slope = %g\n", exprtreeidx, SCIPconsGetName(cons), slope);
4425    SCIPdebug( SCIPprintRowprep(scip, rowprep, NULL) );
4426 
4427    return SCIP_OKAY;
4428 }
4429 
4430 /** adds estimator of a constraints bivariate expression tree to a row
4431  * a reference point is given to decide which hyperplane to choose
4432  */
4433 static
addConcaveEstimatorBivariate(SCIP * scip,SCIP_CONS * cons,int exprtreeidx,SCIP_Real * ref,SCIP_ROWPREP * rowprep,SCIP_Bool * success)4434 SCIP_RETCODE addConcaveEstimatorBivariate(
4435    SCIP*                 scip,               /**< SCIP data structure */
4436    SCIP_CONS*            cons,               /**< constraint */
4437    int                   exprtreeidx,        /**< for which tree a secant should be added */
4438    SCIP_Real*            ref,                /**< reference values of expression tree variables where to generate cut */
4439    SCIP_ROWPREP*         rowprep,            /**< rowprep where to add estimator */
4440    SCIP_Bool*            success             /**< buffer to store whether a secant was succefully added to the row */
4441    )
4442 {
4443    SCIP_CONSDATA* consdata;
4444    SCIP_EXPRTREE* exprtree;
4445    SCIP_Real      treecoef;
4446    SCIP_VAR*      x;
4447    SCIP_VAR*      y;
4448    SCIP_Real      xlb;
4449    SCIP_Real      xub;
4450    SCIP_Real      ylb;
4451    SCIP_Real      yub;
4452 
4453    SCIP_Real      coefx;
4454    SCIP_Real      coefy;
4455    SCIP_Real      constant;
4456 
4457    SCIP_Real      p1[2];
4458    SCIP_Real      p2[2];
4459    SCIP_Real      p3[2];
4460    SCIP_Real      p4[2];
4461    SCIP_Real      p1val, p2val, p3val, p4val;
4462 
4463    assert(scip != NULL);
4464    assert(cons != NULL);
4465    assert(ref  != NULL);
4466    assert(rowprep != NULL);
4467    assert(success != NULL);
4468 
4469    consdata = SCIPconsGetData(cons);
4470    assert(consdata != NULL);
4471    assert(exprtreeidx >= 0);
4472    assert(exprtreeidx < consdata->nexprtrees);
4473    assert(consdata->exprtrees != NULL);
4474 
4475    exprtree = consdata->exprtrees[exprtreeidx];
4476    assert(exprtree != NULL);
4477    assert(SCIPexprtreeGetNVars(exprtree) == 2);
4478 
4479    treecoef = consdata->nonlincoefs[exprtreeidx];
4480 
4481    *success = FALSE;
4482 
4483    x = SCIPexprtreeGetVars(exprtree)[0];
4484    y = SCIPexprtreeGetVars(exprtree)[1];
4485    xlb = SCIPvarGetLbLocal(x);
4486    xub = SCIPvarGetUbLocal(x);
4487    ylb = SCIPvarGetLbLocal(y);
4488    yub = SCIPvarGetUbLocal(y);
4489 
4490    if( SCIPisInfinity(scip, -xlb) || SCIPisInfinity(scip, xub) || SCIPisInfinity(scip, -ylb) || SCIPisInfinity(scip, yub) )
4491    {
4492       SCIPdebugMsg(scip, "skip bivariate secant since <%s> or <%s> is unbounded\n", SCIPvarGetName(x), SCIPvarGetName(y));
4493       return SCIP_OKAY;
4494    }
4495 
4496    /* reference point should not be outside of bounds */
4497    assert(SCIPisFeasLE(scip, xlb, ref[0]));
4498    assert(SCIPisFeasGE(scip, xub, ref[0]));
4499    ref[0] = MIN(xub, MAX(xlb, ref[0]));
4500    assert(SCIPisFeasLE(scip, ylb, ref[1]));
4501    assert(SCIPisFeasGE(scip, yub, ref[1]));
4502    ref[1] = MIN(yub, MAX(ylb, ref[1]));
4503 
4504    /* lower left */
4505    p1[0] = xlb;
4506    p1[1] = ylb;
4507 
4508    /* lower right */
4509    p2[0] = xub;
4510    p2[1] = ylb;
4511 
4512    /* upper right */
4513    p3[0] = xub;
4514    p3[1] = yub;
4515 
4516    /* upper left */
4517    p4[0] = xlb;
4518    p4[1] = yub;
4519 
4520    if( SCIPisEQ(scip, xlb, xub) && SCIPisEQ(scip, ylb, yub) )
4521    {
4522       SCIP_CALL( SCIPexprtreeEval(exprtree, p1, &p1val) );
4523 
4524       if( !SCIPisFinite(p1val) || SCIPisInfinity(scip, REALABS(p1val)) )
4525       {
4526          SCIPdebugMsg(scip, "skip secant for tree %d of constraint <%s> since function cannot be evaluated\n", exprtreeidx, SCIPconsGetName(cons));
4527          return SCIP_OKAY;
4528       }
4529 
4530       p1val *= treecoef;
4531 
4532       coefx = 0.0;
4533       coefy = 0.0;
4534       constant = p1val;
4535    }
4536    else if( SCIPisEQ(scip, xlb, xub) )
4537    {
4538       /* secant between p1 and p4: p1val + [(p4val - p1val) / (yub - ylb)] * (y - ylb) */
4539       assert(!SCIPisEQ(scip, ylb, yub));
4540 
4541       SCIP_CALL( SCIPexprtreeEval(exprtree, p1, &p1val) );
4542       SCIP_CALL( SCIPexprtreeEval(exprtree, p4, &p4val) );
4543       if( !SCIPisFinite(p1val) || SCIPisInfinity(scip, REALABS(p1val)) || !SCIPisFinite(p4val) || SCIPisInfinity(scip, REALABS(p4val)) )
4544       {
4545          SCIPdebugMsg(scip, "skip secant for tree %d of constraint <%s> since function cannot be evaluated\n", exprtreeidx, SCIPconsGetName(cons));
4546          return SCIP_OKAY;
4547       }
4548       p1val *= treecoef;
4549       p4val *= treecoef;
4550 
4551       coefx = 0.0;
4552       coefy = (p4val - p1val) / (yub - ylb);
4553       constant = p1val - coefy * ylb;
4554    }
4555    else if( SCIPisEQ(scip, ylb, yub) )
4556    {
4557       /* secant between p1 and p2: p1val + [(p2val - p1val) / (xub - xlb)] * (x - xlb) */
4558       assert(!SCIPisEQ(scip, xlb, xub));
4559 
4560       SCIP_CALL( SCIPexprtreeEval(exprtree, p1, &p1val) );
4561       SCIP_CALL( SCIPexprtreeEval(exprtree, p2, &p2val) );
4562       if( !SCIPisFinite(p1val) || SCIPisInfinity(scip, REALABS(p1val)) || !SCIPisFinite(p2val) || SCIPisInfinity(scip, REALABS(p2val)) )
4563       {
4564          SCIPdebugMsg(scip, "skip secant for tree %d of constraint <%s> since function cannot be evaluated\n", exprtreeidx, SCIPconsGetName(cons));
4565          return SCIP_OKAY;
4566       }
4567 
4568       p1val *= treecoef;
4569       p2val *= treecoef;
4570 
4571       coefx = (p2val - p1val) / (xub - xlb);
4572       coefy = 0.0;
4573       constant = p1val - coefx * xlb;
4574    }
4575    else
4576    {
4577       SCIP_Real alpha, beta, gamma_, delta;
4578       SCIP_Bool tryother;
4579       SCIP_Bool doover;
4580 
4581       /* if function is convex, then we want an overestimator, otherwise we want an underestimator */
4582       assert(consdata->curvatures[exprtreeidx] == SCIP_EXPRCURV_CONVEX || consdata->curvatures[exprtreeidx] == SCIP_EXPRCURV_CONCAVE);
4583       doover = (consdata->curvatures[exprtreeidx] & SCIP_EXPRCURV_CONVEX);  /*lint !e641*/
4584 
4585       SCIP_CALL( SCIPexprtreeEval(exprtree, p1, &p1val) );
4586       SCIP_CALL( SCIPexprtreeEval(exprtree, p2, &p2val) );
4587       SCIP_CALL( SCIPexprtreeEval(exprtree, p3, &p3val) );
4588       SCIP_CALL( SCIPexprtreeEval(exprtree, p4, &p4val) );
4589       if( !SCIPisFinite(p1val) || SCIPisInfinity(scip, REALABS(p1val)) || !SCIPisFinite(p2val) || SCIPisInfinity(scip, REALABS(p2val)) ||
4590          ! SCIPisFinite(p3val) || SCIPisInfinity(scip, REALABS(p3val)) || !SCIPisFinite(p4val) || SCIPisInfinity(scip, REALABS(p4val)) )
4591       {
4592          SCIPdebugMsg(scip, "skip secant for tree %d of constraint <%s> since function cannot be evaluated\n", exprtreeidx, SCIPconsGetName(cons));
4593          return SCIP_OKAY;
4594       }
4595       p1val *= treecoef;
4596       p2val *= treecoef;
4597       p3val *= treecoef;
4598       p4val *= treecoef;
4599 
4600       /* if we want an underestimator, flip f(x,y), i.e., do as if we compute an overestimator for -f(x,y) */
4601       if( !doover )
4602       {
4603          p1val = -p1val;
4604          p2val = -p2val;
4605          p3val = -p3val;
4606          p4val = -p4val;
4607       }
4608 
4609       SCIPdebugMsg(scip, "p1 = (%g, %g), f(p1) = %g\n", p1[0], p1[1], p1val);
4610       SCIPdebugMsg(scip, "p2 = (%g, %g), f(p2) = %g\n", p2[0], p2[1], p2val);
4611       SCIPdebugMsg(scip, "p3 = (%g, %g), f(p3) = %g\n", p3[0], p3[1], p3val);
4612       SCIPdebugMsg(scip, "p4 = (%g, %g), f(p4) = %g\n", p4[0], p4[1], p4val);
4613 
4614       /* Compute coefficients alpha, beta, gamma (>0), delta such that
4615        *   alpha*x + beta*y + gamma*z = delta
4616        * is satisfied by at least three of the corner points (p1,f(p1)), ..., (p4,f(p4)) and
4617        * the fourth corner point lies below this hyperplane.
4618        * Since we assume that f is convex, we then know that all points (x,y,f(x,y)) are below this hyperplane, i.e.,
4619        *    alpha*x + beta*y - delta <= -gamma * f(x,y),
4620        * or, equivalently,
4621        *   -alpha/gamma*x - beta/gamma*y + delta/gamma >= f(x,y).
4622        */
4623 
4624       tryother = FALSE;
4625       if( ref[1] <= ylb + (yub - ylb)/(xub - xlb) * (ref[0] - xlb) )
4626       {
4627          SCIP_CALL( SCIPcomputeHyperplaneThreePoints(scip, p1[0], p1[1], p1val, p2[0], p2[1], p2val, p3[0], p3[1], p3val,
4628                &alpha, &beta, &gamma_, &delta) );
4629 
4630          assert(SCIPisRelEQ(scip, alpha * p1[0] + beta * p1[1] - delta, -gamma_ * p1val));
4631          assert(SCIPisRelEQ(scip, alpha * p2[0] + beta * p2[1] - delta, -gamma_ * p2val));
4632          assert(SCIPisRelEQ(scip, alpha * p3[0] + beta * p3[1] - delta, -gamma_ * p3val));
4633 
4634          /* if hyperplane through p1,p2,p3 does not overestimate f(p4), then it must be the other variant */
4635          if( alpha * p4[0] + beta * p4[1] + gamma_ * p4val > delta )
4636             tryother = TRUE;
4637          else if( (!SCIPisZero(scip, alpha) && SCIPisZero(scip, alpha/gamma_)) ||
4638             (      !SCIPisZero(scip, beta)  && SCIPisZero(scip, beta /gamma_)) )
4639          {
4640             /* if numerically bad, take alternative hyperplane */
4641             SCIP_CALL( SCIPcomputeHyperplaneThreePoints(scip, p1[0], p1[1], p1val, p3[0], p3[1], p3val, p4[0], p4[1],
4642                   p4val, &alpha, &beta, &gamma_, &delta) );
4643 
4644             assert(SCIPisRelEQ(scip, alpha * p1[0] + beta * p1[1] - delta, -gamma_ * p1val));
4645             assert(SCIPisRelEQ(scip, alpha * p3[0] + beta * p3[1] - delta, -gamma_ * p3val));
4646             assert(SCIPisRelEQ(scip, alpha * p4[0] + beta * p4[1] - delta, -gamma_ * p4val));
4647 
4648             /* if hyperplane through p1,p3,p4 does not overestimate f(p2), then it must be the other variant */
4649             if( alpha * p2[0] + beta * p2[1] + gamma_ * p2val > delta )
4650                tryother = TRUE;
4651          }
4652       }
4653       else
4654       {
4655          SCIP_CALL( SCIPcomputeHyperplaneThreePoints(scip, p1[0], p1[1], p1val, p3[0], p3[1], p3val, p4[0], p4[1], p4val,
4656                &alpha, &beta, &gamma_, &delta) );
4657 
4658          assert(SCIPisRelEQ(scip, alpha * p1[0] + beta * p1[1] - delta, -gamma_ * p1val));
4659          assert(SCIPisRelEQ(scip, alpha * p3[0] + beta * p3[1] - delta, -gamma_ * p3val));
4660          assert(SCIPisRelEQ(scip, alpha * p4[0] + beta * p4[1] - delta, -gamma_ * p4val));
4661 
4662          /* if hyperplane through p1,p3,p4 does not overestimate f(p2), then it must be the other variant */
4663          if( alpha * p2[0] + beta * p2[1] + gamma_ * p2val > delta )
4664             tryother = TRUE;
4665          else if( (!SCIPisZero(scip, alpha) && SCIPisZero(scip, alpha/gamma_)) ||
4666             (      !SCIPisZero(scip, beta)  && SCIPisZero(scip, beta /gamma_)) )
4667          {
4668             /* if numerically bad, take alternative */
4669             SCIP_CALL( SCIPcomputeHyperplaneThreePoints(scip, p1[0], p1[1], p1val, p2[0], p2[1], p2val, p3[0], p3[1],
4670                   p3val, &alpha, &beta, &gamma_, &delta) );
4671 
4672             assert(SCIPisRelEQ(scip, alpha * p1[0] + beta * p1[1] - delta, -gamma_ * p1val));
4673             assert(SCIPisRelEQ(scip, alpha * p2[0] + beta * p2[1] - delta, -gamma_ * p2val));
4674             assert(SCIPisRelEQ(scip, alpha * p3[0] + beta * p3[1] - delta, -gamma_ * p3val));
4675 
4676             /* if hyperplane through p1,p2,p3 does not overestimate f(p4), then it must be the other variant */
4677             if( alpha * p4[0] + beta * p4[1] + gamma_ * p4val > delta )
4678                tryother = TRUE;
4679          }
4680       }
4681 
4682       if( tryother )
4683       {
4684          if( ref[1] <= yub + (ylb - yub)/(xub - xlb) * (ref[0] - xlb) )
4685          {
4686             SCIP_CALL( SCIPcomputeHyperplaneThreePoints(scip, p1[0], p1[1], p1val, p2[0], p2[1], p2val, p4[0], p4[1],
4687                   p4val, &alpha, &beta, &gamma_, &delta) );
4688 
4689             /* hyperplane should be above (p3,f(p3)) and other points should lie on hyperplane */
4690             assert(SCIPisRelEQ(scip, alpha * p1[0] + beta * p1[1] - delta, -gamma_ * p1val));
4691             assert(SCIPisRelEQ(scip, alpha * p2[0] + beta * p2[1] - delta, -gamma_ * p2val));
4692             assert(SCIPisRelLE(scip, alpha * p3[0] + beta * p3[1] - delta, -gamma_ * p3val));
4693             assert(SCIPisRelEQ(scip, alpha * p4[0] + beta * p4[1] - delta, -gamma_ * p4val));
4694 
4695             if( (!SCIPisZero(scip, alpha) && SCIPisZero(scip, alpha/gamma_)) ||
4696                ( !SCIPisZero(scip, beta)  && SCIPisZero(scip, beta /gamma_)) )
4697             {
4698                /* if numerically bad, take alternative */
4699                SCIP_CALL( SCIPcomputeHyperplaneThreePoints(scip, p2[0], p2[1], p2val, p3[0], p3[1], p3val, p4[0], p4[1],
4700                      p4val, &alpha, &beta, &gamma_, &delta) );
4701 
4702                /* hyperplane should be above (p1,f(p1)) and other points should lie on hyperplane */
4703                assert(SCIPisRelLE(scip, alpha * p1[0] + beta * p1[1] - delta, -gamma_ * p1val));
4704                assert(SCIPisRelEQ(scip, alpha * p2[0] + beta * p2[1] - delta, -gamma_ * p2val));
4705                assert(SCIPisRelEQ(scip, alpha * p3[0] + beta * p3[1] - delta, -gamma_ * p3val));
4706                assert(SCIPisRelEQ(scip, alpha * p4[0] + beta * p4[1] - delta, -gamma_ * p4val));
4707             }
4708          }
4709          else
4710          {
4711             SCIP_CALL( SCIPcomputeHyperplaneThreePoints(scip, p2[0], p2[1], p2val, p3[0], p3[1], p3val, p4[0], p4[1],
4712                   p4val, &alpha, &beta, &gamma_, &delta) );
4713 
4714             /* hyperplane should be above (p1,f(p1)) and other points should lie on hyperplane */
4715             assert(SCIPisRelLE(scip, alpha * p1[0] + beta * p1[1] - delta, -gamma_ * p1val));
4716             assert(SCIPisRelEQ(scip, alpha * p2[0] + beta * p2[1] - delta, -gamma_ * p2val));
4717             assert(SCIPisRelEQ(scip, alpha * p3[0] + beta * p3[1] - delta, -gamma_ * p3val));
4718             assert(SCIPisRelEQ(scip, alpha * p4[0] + beta * p4[1] - delta, -gamma_ * p4val));
4719 
4720             if( (!SCIPisZero(scip, alpha) && SCIPisZero(scip, alpha/gamma_)) ||
4721                ( !SCIPisZero(scip, beta)  && SCIPisZero(scip, beta /gamma_)) )
4722             {
4723                /* if numerically bad, take alternative */
4724                SCIP_CALL( SCIPcomputeHyperplaneThreePoints(scip, p1[0], p1[1], p1val, p2[0], p2[1], p2val, p4[0], p4[1],
4725                      p4val, &alpha, &beta, &gamma_, &delta) );
4726 
4727                /* hyperplane should be above (p3,f(p3)) and other points should lie on hyperplane */
4728                assert(SCIPisRelEQ(scip, alpha * p1[0] + beta * p1[1] - delta, -gamma_ * p1val));
4729                assert(SCIPisRelEQ(scip, alpha * p2[0] + beta * p2[1] - delta, -gamma_ * p2val));
4730                assert(SCIPisRelLE(scip, alpha * p3[0] + beta * p3[1] - delta, -gamma_ * p3val));
4731                assert(SCIPisRelEQ(scip, alpha * p4[0] + beta * p4[1] - delta, -gamma_ * p4val));
4732             }
4733          }
4734       }
4735 
4736       SCIPdebugMsg(scip, "alpha = %g, beta = %g, gamma = %g, delta = %g\n", alpha, beta, gamma_, delta);
4737 
4738       /* check if bad luck: should not happen if xlb != xub and ylb != yub and numerics are fine */
4739       if( SCIPisZero(scip, gamma_) )
4740          return SCIP_OKAY;
4741       assert(!SCIPisNegative(scip, gamma_));
4742 
4743       /* flip hyperplane */
4744       if( !doover )
4745          gamma_ = -gamma_;
4746 
4747       coefx    = -alpha / gamma_;
4748       coefy    = -beta  / gamma_;
4749       constant =  delta / gamma_;
4750 
4751       /* if we loose coefficients because division by gamma makes them < SCIPepsilon(scip), then better not generate a cut here */
4752       if( (!SCIPisZero(scip, alpha) && SCIPisZero(scip, coefx)) ||
4753          ( !SCIPisZero(scip, beta)  && SCIPisZero(scip, coefy)) )
4754       {
4755          SCIPdebugMsg(scip, "skip bivar secant for <%s> tree %d due to bad numerics\n", SCIPconsGetName(cons), exprtreeidx);
4756          return SCIP_OKAY;
4757       }
4758    }
4759 
4760    /* add hyperplane coefs to SCIP row */
4761    SCIPaddRowprepConstant(rowprep, constant);
4762    SCIP_CALL( SCIPaddRowprepTerm(scip, rowprep, x, coefx) );
4763    SCIP_CALL( SCIPaddRowprepTerm(scip, rowprep, y, coefy) );
4764 
4765    *success = TRUE;
4766 
4767    SCIPdebugMsg(scip, "added bivariate secant for tree %d of constraint <%s>\n", exprtreeidx, SCIPconsGetName(cons));
4768    SCIPdebug( SCIPprintRowprep(scip, rowprep, NULL) );
4769 
4770    return SCIP_OKAY;
4771 }
4772 
4773 /** internal method using an auxiliary LPI, see addConcaveEstimatorMultivariate() */
4774 static
_addConcaveEstimatorMultivariate(SCIP * scip,SCIP_LPI * lpi,SCIP_CONS * cons,int exprtreeidx,SCIP_Real * ref,SCIP_ROWPREP * rowprep,SCIP_VAR ** vars,SCIP_EXPRTREE * exprtree,int nvars,SCIP_Bool doupper,SCIP_Bool * success)4775 SCIP_RETCODE _addConcaveEstimatorMultivariate(
4776    SCIP*                 scip,               /**< SCIP data structure */
4777    SCIP_LPI*             lpi,                /**< auxiliary LPI */
4778    SCIP_CONS*            cons,               /**< constraint */
4779    int                   exprtreeidx,        /**< for which tree a secant should be added */
4780    SCIP_Real*            ref,                /**< reference values of expression tree variables where to generate cut */
4781    SCIP_ROWPREP*         rowprep,            /**< rowprep where to add estimator */
4782    SCIP_VAR**            vars,               /**< variables of the constraint */
4783    SCIP_EXPRTREE*        exprtree,           /**< expression tree of constraint */
4784    int                   nvars,              /**< number of variables */
4785    SCIP_Bool             doupper,            /**< should an upper estimator be computed */
4786    SCIP_Bool*            success             /**< buffer to store whether a secant was succefully added to the row */
4787    )
4788 {
4789    SCIP_CONSDATA* consdata;
4790    SCIP_Real* val;
4791    SCIP_Real* obj;
4792    SCIP_Real* lb;
4793    SCIP_Real* ub;
4794    SCIP_Real* corner;
4795    SCIP_Real* lhs;
4796    SCIP_Real* rhs;
4797    int* beg;
4798    int* ind;
4799    SCIP_Real lpobj;
4800    int ncols;
4801    int nrows;
4802    int nnonz;
4803    SCIP_Real funcval;
4804    SCIP_Real treecoef;
4805 
4806    int i;
4807    int j;
4808    int idx;
4809 
4810    SCIP_RETCODE lpret;
4811 
4812    assert(lpi != NULL);
4813    assert(nvars <= 10);
4814 
4815    consdata = SCIPconsGetData(cons);
4816    treecoef = consdata->nonlincoefs[exprtreeidx];
4817 
4818    /* columns are cut coefficients plus constant */
4819    ncols = nvars + 1;
4820    SCIP_CALL( SCIPallocBufferArray(scip, &obj, ncols) );
4821    SCIP_CALL( SCIPallocBufferArray(scip, &lb, ncols) );
4822    SCIP_CALL( SCIPallocBufferArray(scip, &ub, ncols) );
4823    corner = lb; /* will not use lb and corner simultaneously, so can share memory */
4824 
4825    /* one row for each corner of domain, i.e., 2^nvars many */
4826    nrows = (int)(1u << nvars);
4827    SCIP_CALL( SCIPallocBufferArray(scip, &lhs, nrows) );
4828    SCIP_CALL( SCIPallocBufferArray(scip, &rhs, nrows) );
4829 
4830    /* the coefficients matrix will have at most ncols * nrows many nonzeros */
4831    nnonz = nrows * ncols;
4832    SCIP_CALL( SCIPallocBufferArray(scip, &beg, nrows+1) );
4833    SCIP_CALL( SCIPallocBufferArray(scip, &ind, nnonz) );
4834    SCIP_CALL( SCIPallocBufferArray(scip, &val, nnonz) );
4835 
4836    /* setup LP data */
4837    idx = 0;
4838    for( i = 0; i < nrows; ++i )
4839    {
4840       /* assemble corner point */
4841       SCIPdebugMsg(scip, "f(");
4842       for( j = 0; j < nvars; ++j )
4843       {
4844          /* if j'th bit of row index i is set, then take upper bound on var j, otherwise lower bound var j
4845           * we check this by shifting i for j positions to the right and checking whether the j'th bit is set */
4846          if( ((unsigned int)i >> j) & 0x1 )
4847             corner[j] = SCIPvarGetUbLocal(vars[j]);
4848          else
4849             corner[j] = SCIPvarGetLbLocal(vars[j]);
4850          SCIPdebugMsgPrint(scip, "%g, ", corner[j]);
4851          assert(!SCIPisInfinity(scip, REALABS(corner[j])));
4852       }
4853 
4854       /* evaluate function in current corner */
4855       SCIP_CALL( SCIPexprtreeEval(exprtree, corner, &funcval) );
4856       SCIPdebugMsgPrint(scip, ") = %g\n", funcval);
4857 
4858       if( !SCIPisFinite(funcval) || SCIPisInfinity(scip, REALABS(funcval)) )
4859       {
4860          SCIPdebugMsg(scip, "cannot compute underestimator for concave because constaint <%s> cannot be evaluated\n", SCIPconsGetName(cons));
4861          goto TERMINATE;
4862       }
4863 
4864       funcval *= treecoef;
4865 
4866       if( !doupper )
4867       {
4868          lhs[i] = -SCIPlpiInfinity(lpi);
4869          rhs[i] = funcval;
4870       }
4871       else
4872       {
4873          lhs[i] = funcval;
4874          rhs[i] = SCIPlpiInfinity(lpi);
4875       }
4876 
4877       /* add nonzeros of corner to matrix */
4878       beg[i] = idx;
4879       for( j = 0; j < nvars; ++j )
4880       {
4881          if( corner[j] != 0.0 )
4882          {
4883             ind[idx] = j;
4884             val[idx] = corner[j];
4885             ++idx;
4886          }
4887       }
4888 
4889       /* coefficient for constant is 1.0 */
4890       val[idx] = 1.0;
4891       ind[idx] = nvars;
4892       ++idx;
4893    }
4894    nnonz = idx;
4895    beg[nrows] = nnonz;
4896 
4897    for( j = 0; j < ncols; ++j )
4898    {
4899       lb[j] = -SCIPlpiInfinity(lpi);
4900       ub[j] =  SCIPlpiInfinity(lpi);
4901    }
4902 
4903    /* objective coefficients are reference points, and an additional 1.0 for the constant */
4904    BMScopyMemoryArray(obj, ref, nvars);
4905    obj[nvars] = 1.0;
4906 
4907    /* get function value in reference point, so we can use this as a cutoff */
4908    SCIP_CALL( SCIPexprtreeEval(exprtree, ref, &funcval) );
4909    funcval *= treecoef;
4910 
4911    SCIP_CALL( SCIPlpiAddCols(lpi, ncols, obj, lb, ub, NULL, 0, NULL, NULL, NULL) );
4912    SCIP_CALL( SCIPlpiAddRows(lpi, nrows, lhs, rhs, NULL, nnonz, beg, ind, val) );
4913 
4914    /* make use of this convenient features, since for us nrows >> ncols */
4915    /*SCIP_CALL( SCIPlpiSetRealpar(lpi, SCIP_LPPAR_ROWREPSWITCH, 5.0) ); */
4916    /* get accurate coefficients */
4917    SCIP_CALL( SCIPlpiSetRealpar(lpi, SCIP_LPPAR_FEASTOL, SCIPfeastol(scip)/100.0) );
4918    SCIP_CALL( SCIPlpiSetRealpar(lpi, SCIP_LPPAR_OBJLIM, funcval) );
4919    SCIP_CALL( SCIPlpiSetIntpar(lpi, SCIP_LPPAR_LPITLIM, 10 * nvars) );
4920    SCIP_CALL( SCIPlpiSetIntpar(lpi, SCIP_LPPAR_SCALING, 1) );
4921    SCIP_CALL( SCIPlpiSetIntpar(lpi, SCIP_LPPAR_FROMSCRATCH, 1) );
4922 
4923    /* SCIPdebug( SCIP_CALL( SCIPlpiSetIntpar(lpi, SCIP_LPPAR_LPINFO, 1) ) ); */
4924 
4925    lpret = SCIPlpiSolveDual(lpi);
4926    if( lpret != SCIP_OKAY )
4927    {
4928       SCIPwarningMessage(scip, "solving auxiliary LP for underestimator of concave function returned %d\n", lpret);
4929       goto TERMINATE;
4930    }
4931 
4932    if( !SCIPlpiIsPrimalFeasible(lpi) )
4933    {
4934       SCIPdebugMsg(scip, "failed to find feasible solution for auxiliary LP for underestimator of concave function, iterlimexc = %u, cutoff = %u, unbounded = %u\n", SCIPlpiIsIterlimExc(lpi), SCIPlpiIsObjlimExc(lpi), SCIPlpiIsPrimalUnbounded(lpi));
4935       goto TERMINATE;
4936    }
4937    /* should be either solved to optimality, or the objective or iteration limit be hit */
4938    assert(SCIPlpiIsOptimal(lpi) || SCIPlpiIsObjlimExc(lpi) || SCIPlpiIsIterlimExc(lpi));
4939 
4940    /* setup row coefficient, reuse obj array to store LP sol values */
4941    SCIP_CALL( SCIPlpiGetSol(lpi, &lpobj, obj, NULL, NULL, NULL) );
4942 
4943    /* check that computed hyperplane is on right side of function in refpoint
4944     * if numerics is very bad (e.g., st_e32), then even this can happen */
4945    if( (!doupper && SCIPisFeasGT(scip, lpobj, funcval)) || (doupper && SCIPisFeasGT(scip, funcval, lpobj)) )
4946    {
4947       SCIPwarningMessage(scip, "computed cut does not underestimate concave function in refpoint\n");
4948       goto TERMINATE;
4949    }
4950    assert( doupper || SCIPisFeasLE(scip, lpobj, funcval) );
4951    assert(!doupper || SCIPisFeasLE(scip, funcval, lpobj) );
4952 
4953    /* add estimator to rowprep */
4954    SCIPaddRowprepConstant(rowprep, obj[nvars]);
4955    SCIP_CALL( SCIPaddRowprepTerms(scip, rowprep, nvars, vars, obj) );
4956 
4957    *success = TRUE;
4958 
4959 TERMINATE:
4960    SCIPfreeBufferArray(scip, &val);
4961    SCIPfreeBufferArray(scip, &ind);
4962    SCIPfreeBufferArray(scip, &beg);
4963    SCIPfreeBufferArray(scip, &rhs);
4964    SCIPfreeBufferArray(scip, &lhs);
4965    SCIPfreeBufferArray(scip, &ub);
4966    SCIPfreeBufferArray(scip, &lb);
4967    SCIPfreeBufferArray(scip, &obj);
4968 
4969    return SCIP_OKAY;
4970 }
4971 
4972 
4973 
4974 /** adds estimator of a constraints multivariate expression tree to a row
4975  * Given concave function f(x) and reference point ref.
4976  * Let (v_i: i=1,...,n) be corner points of current domain of x.
4977  * Find (coef,constant) such that <coef,v_i> + constant <= f(v_i) (cut validity) and
4978  * such that <coef, ref> + constant is maximized (cut efficacy).
4979  * Then <coef, x> + constant <= f(x) for all x in current domain.
4980  *
4981  * Similar to compute an overestimator for a convex function f(x).
4982  * Find (coef,constant) such that <coef,v_i> + constant >= f(v_i) and
4983  * such that <coef, ref> + constant is minimized.
4984  * Then <coef, x> + constant >= f(x) for all x in current domain.
4985  */
4986 static
addConcaveEstimatorMultivariate(SCIP * scip,SCIP_CONS * cons,int exprtreeidx,SCIP_Real * ref,SCIP_ROWPREP * rowprep,SCIP_Bool * success)4987 SCIP_RETCODE addConcaveEstimatorMultivariate(
4988    SCIP*                 scip,               /**< SCIP data structure */
4989    SCIP_CONS*            cons,               /**< constraint */
4990    int                   exprtreeidx,        /**< for which tree a secant should be added */
4991    SCIP_Real*            ref,                /**< reference values of expression tree variables where to generate cut */
4992    SCIP_ROWPREP*         rowprep,            /**< rowprep where to add estimator */
4993    SCIP_Bool*            success             /**< buffer to store whether a secant was succefully added to the row */
4994    )
4995 {
4996    SCIP_VAR** vars;
4997    SCIP_CONSDATA* consdata;
4998    SCIP_EXPRTREE* exprtree;
4999    SCIP_LPI* lpi;
5000    int nvars;
5001    int j;
5002    SCIP_Bool doupper;
5003 
5004    SCIP_RETCODE retcode;
5005 
5006    static SCIP_Bool warned_highdim_concave = FALSE;
5007 
5008    assert(scip != NULL);
5009    assert(cons != NULL);
5010    assert(ref != NULL);
5011    assert(rowprep != NULL);
5012    assert(success != NULL);
5013 
5014    consdata = SCIPconsGetData(cons);
5015    assert(consdata != NULL);
5016    assert(exprtreeidx >= 0);
5017    assert(exprtreeidx < consdata->nexprtrees);
5018    assert(consdata->exprtrees != NULL);
5019 
5020    exprtree = consdata->exprtrees[exprtreeidx];
5021    assert(exprtree != NULL);
5022 
5023    nvars = SCIPexprtreeGetNVars(exprtree);
5024    assert(nvars >= 2);
5025 
5026    *success = FALSE;
5027 
5028    /* size of LP is exponential in number of variables of tree, so do only for small trees */
5029    if( nvars > 10 )
5030    {
5031       if( !warned_highdim_concave )
5032       {
5033          SCIPwarningMessage(scip, "concave function in constraint <%s> too high-dimensional to compute underestimator\n", SCIPconsGetName(cons));
5034          warned_highdim_concave = TRUE;
5035       }
5036       return SCIP_OKAY;
5037    }
5038 
5039    vars = SCIPexprtreeGetVars(exprtree);
5040 
5041    /* check whether bounds are finite
5042     * make sure reference point is strictly within bounds
5043     * otherwise we can easily get an unbounded LP below, e.g., with instances like ex6_2_* from GlobalLib
5044     */
5045    for( j = 0; j < nvars; ++j )
5046    {
5047       if( SCIPisInfinity(scip, -SCIPvarGetLbLocal(vars[j])) || SCIPisInfinity(scip, SCIPvarGetUbLocal(vars[j])) )
5048       {
5049          SCIPdebugMsg(scip, "cannot compute underestimator for concave because variable <%s> is unbounded\n", SCIPvarGetName(vars[j]));
5050          return SCIP_OKAY;
5051       }
5052       assert(SCIPisFeasLE(scip, SCIPvarGetLbLocal(vars[j]), ref[j]));
5053       assert(SCIPisFeasGE(scip, SCIPvarGetUbLocal(vars[j]), ref[j]));
5054       ref[j] = MIN(SCIPvarGetUbLocal(vars[j]), MAX(SCIPvarGetLbLocal(vars[j]), ref[j]));  /*lint !e666*/
5055    }
5056 
5057    /* create empty auxiliary LP and decide its objective sense */
5058    assert(consdata->curvatures[exprtreeidx] == SCIP_EXPRCURV_CONVEX || consdata->curvatures[exprtreeidx] == SCIP_EXPRCURV_CONCAVE);
5059    doupper = (consdata->curvatures[exprtreeidx] & SCIP_EXPRCURV_CONVEX);  /*lint !e641*/
5060    SCIP_CALL( SCIPlpiCreate(&lpi, SCIPgetMessagehdlr(scip), "concaveunderest", doupper ? SCIP_OBJSEN_MINIMIZE : SCIP_OBJSEN_MAXIMIZE) );
5061    if( lpi == NULL )
5062    {
5063       SCIPerrorMessage("failed to create auxiliary LP\n");
5064       return SCIP_ERROR;
5065    }
5066 
5067    /* capture the retcode, free the LPI afterwards */
5068    retcode = _addConcaveEstimatorMultivariate(scip, lpi, cons, exprtreeidx, ref, rowprep, vars, exprtree, nvars, doupper, success);
5069 
5070    assert(lpi != NULL);
5071    SCIP_CALL( SCIPlpiFree(&lpi) );
5072 
5073    return retcode;
5074 }
5075 
5076 /** Computes the linear coeffs and the constant in a linear expression
5077  * both scaled by a given scalar value.
5078  * The coeffs of the variables will be stored in the given array at
5079  * their variable index.
5080  * The constant of the given linear expression will be added to the given
5081  * buffer.
5082  */
5083 static
getCoeffsAndConstantFromLinearExpr(SCIP_EXPR * expr,SCIP_Real scalar,SCIP_Real * varcoeffs,SCIP_Real * constant)5084 SCIP_RETCODE getCoeffsAndConstantFromLinearExpr(
5085    SCIP_EXPR*            expr,               /**< the linear expression */
5086    SCIP_Real             scalar,             /**< the scalar value, i.e. the coeff of the given expression */
5087    SCIP_Real*            varcoeffs,          /**< buffer array to store the computed coefficients */
5088    SCIP_Real*            constant            /**< buffer to hold the constant value of the given expression */
5089    )
5090 {
5091    switch( SCIPexprGetOperator( expr ) )
5092    {
5093    case SCIP_EXPR_VARIDX: /* set coeff for this variable to current scalar */
5094    {
5095       /* TODO: can a linear expression contain the same variable twice?
5096        * if yes varcoeffs need to be initialized to zero before calling this function
5097        * and coeff must not be overridden but summed up instead. */
5098       varcoeffs[SCIPexprGetOpIndex( expr )] = scalar;
5099       return SCIP_OKAY;
5100    }
5101 
5102    case SCIP_EXPR_CONST:
5103    {
5104       /* constant value increases */
5105       *constant += scalar * SCIPexprGetOpReal( expr );
5106       return SCIP_OKAY;
5107    }
5108 
5109    case SCIP_EXPR_MUL: /* need to find the constant part of the muliplication and then recurse  */
5110    {
5111       SCIP_EXPR** children;
5112       children = SCIPexprGetChildren( expr );
5113 
5114       /* first part is constant */
5115       if( SCIPexprGetOperator( children[0] ) == SCIP_EXPR_CONST )
5116       {
5117          SCIP_CALL( getCoeffsAndConstantFromLinearExpr( children[1], scalar * SCIPexprGetOpReal( children[0] ), varcoeffs, constant ) );
5118          return SCIP_OKAY;
5119       }
5120 
5121       /* second part is constant */
5122       if( SCIPexprGetOperator( children[1] ) == SCIP_EXPR_CONST )
5123       {
5124          SCIP_CALL( getCoeffsAndConstantFromLinearExpr( children[0], scalar * SCIPexprGetOpReal( children[1] ), varcoeffs, constant ) );
5125          return SCIP_OKAY;
5126       }
5127 
5128       /* nonlinear -> break out to error case  */
5129       break;
5130    }
5131 
5132    case SCIP_EXPR_PLUS: /* just recurse */
5133    {
5134       SCIP_EXPR** children;
5135       children = SCIPexprGetChildren( expr );
5136       SCIP_CALL( getCoeffsAndConstantFromLinearExpr( children[0], scalar, varcoeffs, constant ) );
5137       SCIP_CALL( getCoeffsAndConstantFromLinearExpr( children[1], scalar, varcoeffs, constant ) );
5138       return SCIP_OKAY;
5139    }
5140 
5141    case SCIP_EXPR_MINUS: /* recursion on second child is called with negated scalar */
5142    {
5143       SCIP_EXPR** children;
5144       children = SCIPexprGetChildren( expr );
5145       SCIP_CALL( getCoeffsAndConstantFromLinearExpr( children[0], scalar, varcoeffs, constant ) );
5146       SCIP_CALL( getCoeffsAndConstantFromLinearExpr( children[1], -scalar, varcoeffs, constant ) );
5147       return SCIP_OKAY;
5148    }
5149 
5150    case SCIP_EXPR_SUM: /* just recurse */
5151    {
5152       SCIP_EXPR** children;
5153       int nchildren;
5154       int c;
5155 
5156       children = SCIPexprGetChildren(expr);
5157       nchildren = SCIPexprGetNChildren(expr);
5158 
5159       for( c = 0; c < nchildren; ++c )
5160       {
5161          SCIP_CALL( getCoeffsAndConstantFromLinearExpr( children[c], scalar, varcoeffs, constant ) );
5162       }
5163 
5164       return SCIP_OKAY;
5165    }
5166 
5167    case SCIP_EXPR_LINEAR: /* add scaled constant and recurse on children with their coeff multiplied into scalar */
5168    {
5169       SCIP_Real* childCoeffs;
5170       SCIP_EXPR** children;
5171       int i;
5172 
5173       *constant += scalar * SCIPexprGetLinearConstant( expr );
5174 
5175       children = SCIPexprGetChildren( expr );
5176       childCoeffs = SCIPexprGetLinearCoefs( expr );
5177 
5178       for( i = 0; i < SCIPexprGetNChildren( expr ); ++i )
5179       {
5180          SCIP_CALL( getCoeffsAndConstantFromLinearExpr( children[i], scalar * childCoeffs[i], varcoeffs, constant ) );
5181       }
5182 
5183       return SCIP_OKAY;
5184    }
5185 
5186    default:
5187       break;
5188    } /*lint !e788*/
5189 
5190    SCIPerrorMessage( "Cannot extract linear coefficients from expressions with operator %d %s\n", SCIPexprGetOperator( expr ), SCIPexpropGetName(SCIPexprGetOperator( expr )));
5191    SCIPABORT();
5192    return SCIP_ERROR; /*lint !e527*/
5193 }
5194 
5195 /** adds estimator from user callback of a constraints user expression tree to a row
5196  */
5197 static
addUserEstimator(SCIP * scip,SCIP_CONS * cons,int exprtreeidx,SCIP_Real * x,SCIP_Bool overestimate,SCIP_ROWPREP * rowprep,SCIP_Bool * success)5198 SCIP_RETCODE addUserEstimator(
5199    SCIP*                 scip,               /**< SCIP data structure */
5200    SCIP_CONS*            cons,               /**< constraint */
5201    int                   exprtreeidx,        /**< for which tree an estimator should be added */
5202    SCIP_Real*            x,                  /**< value of expression tree variables where to generate cut */
5203    SCIP_Bool             overestimate,       /**< whether to compute an overestimator instead of an underestimator */
5204    SCIP_ROWPREP*         rowprep,            /**< rowprep where to add estimator */
5205    SCIP_Bool*            success             /**< buffer to store whether an estimator was succefully added to the rowprep */
5206    )
5207 {
5208    SCIP_CONSDATA* consdata;
5209    SCIP_EXPRTREE* exprtree;
5210    SCIP_EXPR** children;
5211    SCIP_VAR** vars;
5212    SCIP_Real* params;
5213    SCIP_INTERVAL* varbounds;
5214 
5215    SCIP_INTERVAL* childbounds;
5216    SCIP_Real* childvals;
5217    SCIP_Real* childcoeffs;
5218 
5219    SCIP_Real constant;
5220    SCIP_Real treecoef;
5221    int nvars;
5222    int nchildren;
5223    int i;
5224 
5225    consdata = SCIPconsGetData( cons );
5226    assert( consdata != NULL );
5227    assert( exprtreeidx >= 0 );
5228    assert( exprtreeidx < consdata->nexprtrees );
5229    assert( consdata->exprtrees != NULL );
5230    assert( rowprep != NULL );
5231    assert( success != NULL );
5232 
5233    exprtree = consdata->exprtrees[exprtreeidx];
5234    assert( exprtree != NULL );
5235    assert( SCIPexprGetOperator(SCIPexprtreeGetRoot(exprtree)) == SCIP_EXPR_USER );
5236 
5237    /* if user did not implement estimator callback, then we cannot do anything */
5238    if( !SCIPexprHasUserEstimator(SCIPexprtreeGetRoot(exprtree)) )
5239    {
5240       *success = FALSE;
5241       return SCIP_OKAY;
5242    }
5243 
5244    params = SCIPexprtreeGetParamVals( exprtree );
5245    nvars = SCIPexprtreeGetNVars( exprtree );
5246    vars = SCIPexprtreeGetVars( exprtree );
5247    nchildren = SCIPexprGetNChildren( SCIPexprtreeGetRoot( exprtree ) );
5248    children = SCIPexprGetChildren( SCIPexprtreeGetRoot( exprtree ) );
5249 
5250    /* Get bounds of variables */
5251    SCIP_CALL( SCIPallocBufferArray( scip, &varbounds, nchildren ) );
5252 
5253    for( i = 0; i < nvars; ++i )
5254    {
5255       double lb = SCIPvarGetLbLocal( vars[i] );
5256       double ub = SCIPvarGetUbLocal( vars[i] );
5257       SCIPintervalSetBounds( &varbounds[i],
5258                              -infty2infty( SCIPinfinity( scip ), INTERVALINFTY, -MIN( lb, ub ) ),
5259                              +infty2infty( SCIPinfinity( scip ), INTERVALINFTY,  MAX( lb, ub ) ) );
5260    }
5261 
5262    /* Compute bounds and solution value for the user expressions children */
5263    SCIP_CALL( SCIPallocBufferArray( scip, &childcoeffs, nchildren ) );
5264    SCIP_CALL( SCIPallocBufferArray( scip, &childbounds, nchildren ) );
5265    SCIP_CALL( SCIPallocBufferArray( scip, &childvals, nchildren ) );
5266 
5267    for( i = 0; i < nchildren; ++i )
5268    {
5269       SCIP_CALL( SCIPexprEval( children[i], x, params, &childvals[i] ) );
5270       SCIP_CALL( SCIPexprEvalInt( children[i], INTERVALINFTY, varbounds, params, &childbounds[i] ) );
5271    }
5272 
5273    /* varbounds not needed any longer */
5274    SCIPfreeBufferArray( scip, &varbounds );
5275 
5276    /* call estimator for user expressions to compute coeffs and constant for the user expressions children */
5277    SCIP_CALL( SCIPexprEstimateUser( SCIPexprtreeGetRoot( exprtree ), INTERVALINFTY, childvals, childbounds, overestimate, childcoeffs, &constant, success ) );
5278 
5279    if( *success )
5280    {
5281       SCIP_Real* varcoeffs;
5282       SCIP_CALL( SCIPallocBufferArray( scip, &varcoeffs, nvars ) );
5283 
5284       treecoef = consdata->nonlincoefs[exprtreeidx];
5285       constant *= treecoef;
5286 
5287       for( i = 0; i < nchildren; ++i )
5288       {
5289          SCIP_CALL( getCoeffsAndConstantFromLinearExpr( children[i], childcoeffs[i]*treecoef, varcoeffs, &constant ) );
5290       }
5291 
5292       SCIPaddRowprepConstant(rowprep, constant);
5293       SCIP_CALL( SCIPaddRowprepTerms(scip, rowprep, nvars, vars, varcoeffs) );
5294 
5295       SCIPfreeBufferArray( scip, &varcoeffs );
5296    }
5297 
5298    SCIPfreeBufferArray( scip, &childcoeffs );
5299    SCIPfreeBufferArray( scip, &childbounds );
5300    SCIPfreeBufferArray( scip, &childvals );
5301 
5302    return SCIP_OKAY;
5303 }
5304 
5305 /** adds estimator from interval gradient of a constraints univariate expression tree to a row
5306  * a reference point is used to decide in which corner to generate the cut
5307  */
5308 static
addIntervalGradientEstimator(SCIP * scip,SCIP_EXPRINT * exprint,SCIP_CONS * cons,int exprtreeidx,SCIP_Real * x,SCIP_Bool newx,SCIP_Bool overestimate,SCIP_ROWPREP * rowprep,SCIP_Bool * success)5309 SCIP_RETCODE addIntervalGradientEstimator(
5310    SCIP*                 scip,               /**< SCIP data structure */
5311    SCIP_EXPRINT*         exprint,            /**< expression interpreter */
5312    SCIP_CONS*            cons,               /**< constraint */
5313    int                   exprtreeidx,        /**< for which tree a secant should be added */
5314    SCIP_Real*            x,                  /**< value of expression tree variables where to generate cut */
5315    SCIP_Bool             newx,               /**< whether the last evaluation of the expression with the expression interpreter was not at x */
5316    SCIP_Bool             overestimate,       /**< whether to compute an overestimator instead of an underestimator */
5317    SCIP_ROWPREP*         rowprep,            /**< rowprep where to add estimator */
5318    SCIP_Bool*            success             /**< buffer to store whether an estimator was succefully added to the rowprep */
5319    )
5320 {
5321    SCIP_CONSDATA* consdata;
5322    SCIP_EXPRTREE* exprtree;
5323    SCIP_Real treecoef;
5324    SCIP_Real* coefs;
5325    SCIP_Real constant;
5326    SCIP_Real val;
5327    SCIP_Real lb;
5328    SCIP_Real ub;
5329    SCIP_INTERVAL* box;
5330    SCIP_INTERVAL* intgrad;
5331    SCIP_INTERVAL intval;
5332    SCIP_VAR** vars;
5333    int nvars;
5334    int i;
5335 
5336    assert(scip != NULL);
5337    assert(cons != NULL);
5338    assert(x    != NULL);
5339    assert(rowprep != NULL);
5340    assert(success != NULL);
5341 
5342    consdata = SCIPconsGetData(cons);
5343    assert(consdata != NULL);
5344    assert(exprtreeidx >= 0);
5345    assert(exprtreeidx < consdata->nexprtrees);
5346    assert(consdata->exprtrees != NULL);
5347 
5348    exprtree = consdata->exprtrees[exprtreeidx];
5349    assert(exprtree != NULL);
5350    assert(newx || SCIPexprtreeGetInterpreterData(exprtree) != NULL);
5351 
5352    *success = FALSE;
5353 
5354    /* skip interval gradient if expression interpreter cannot compute interval gradients */
5355    if( !(SCIPexprintGetCapability() & SCIP_EXPRINTCAPABILITY_INTGRADIENT) )
5356       return SCIP_OKAY;
5357 
5358    nvars = SCIPexprtreeGetNVars(exprtree);
5359    vars = SCIPexprtreeGetVars(exprtree);
5360 
5361    box = NULL;
5362    intgrad = NULL;
5363    coefs = NULL;
5364 
5365    SCIP_CALL( SCIPallocBufferArray(scip, &box, nvars) );
5366 
5367    /* move reference point to bounds, setup box */
5368    for( i = 0; i < nvars; ++i )
5369    {
5370       lb  = SCIPvarGetLbLocal(vars[i]);
5371       ub  = SCIPvarGetUbLocal(vars[i]);
5372       if( SCIPisInfinity(scip, -lb) )
5373       {
5374          if( SCIPisInfinity(scip, ub) )
5375          {
5376             SCIPdebugMsg(scip, "skip interval gradient estimator for constraint <%s> because variable <%s> is still unbounded.\n", SCIPconsGetName(cons), SCIPvarGetName(vars[i]));
5377             goto INTGRADESTIMATOR_CLEANUP;
5378          }
5379          x[i] = ub;
5380       }
5381       else
5382       {
5383          if( SCIPisInfinity(scip, ub) )
5384             x[i] = lb;
5385          else
5386             x[i] = (2.0*x[i] < lb+ub) ? lb : ub;
5387       }
5388       SCIPintervalSetBounds(&box[i],
5389          -infty2infty(SCIPinfinity(scip), INTERVALINFTY, -MIN(lb, ub)),
5390          +infty2infty(SCIPinfinity(scip), INTERVALINFTY,  MAX(lb, ub)));
5391    }
5392 
5393    /* compile expression if evaluated the first time; can only happen if newx is FALSE */
5394    if( newx && SCIPexprtreeGetInterpreterData(exprtree) == NULL )
5395    {
5396       SCIP_CALL( SCIPexprintCompile(exprint, exprtree) );
5397    }
5398 
5399    /* evaluate in reference point */
5400    SCIP_CALL( SCIPexprintEval(exprint, exprtree, x, &val) );
5401    if( !SCIPisFinite(val) )
5402    {
5403       SCIPdebugMsg(scip, "Got nonfinite function value from evaluation of constraint %s tree %d. skipping interval gradient estimator.\n", SCIPconsGetName(cons), exprtreeidx);
5404       goto INTGRADESTIMATOR_CLEANUP;
5405    }
5406 
5407    treecoef = consdata->nonlincoefs[exprtreeidx];
5408    val *= treecoef;
5409    constant = val;
5410 
5411    /* compute interval gradient */
5412    SCIP_CALL( SCIPallocBufferArray(scip, &intgrad, nvars) );
5413    SCIP_CALL( SCIPexprintGradInt(exprint, exprtree, INTERVALINFTY, box, TRUE, &intval, intgrad) );
5414    SCIPintervalMulScalar(INTERVALINFTY, &intval, intval, treecoef);
5415 
5416    /* printf("nvars %d side %d xref = %g x = [%g,%g] intval = [%g,%g] intgrad = [%g,%g]\n", nvars, side, x[0],
5417       box[0].inf, box[0].sup, intval.inf, intval.sup, intgrad[0].inf, intgrad[0].sup); */
5418 
5419    /* compute coefficients and constant */
5420    SCIP_CALL( SCIPallocBufferArray(scip, &coefs, nvars) );
5421    for( i = 0; i < nvars; ++i )
5422    {
5423       val = x[i];
5424       lb  = SCIPintervalGetInf(box[i]);
5425       ub  = SCIPintervalGetSup(box[i]);
5426 
5427       SCIPintervalMulScalar(INTERVALINFTY, &intgrad[i], intgrad[i], treecoef);
5428 
5429       if( SCIPisEQ(scip, lb, ub) )
5430          coefs[i] = 0.0;
5431       else if( (overestimate && val == ub) ||  /*lint !e777*/
5432          (!overestimate && val == lb) )   /*lint !e777*/
5433          coefs[i] = SCIPintervalGetInf(intgrad[i]);
5434       else
5435          coefs[i] = SCIPintervalGetSup(intgrad[i]);
5436 
5437       if( SCIPisZero(scip, coefs[i]) )
5438          continue;
5439 
5440       if( SCIPisInfinity(scip, -coefs[i]) || SCIPisInfinity(scip, coefs[i]) )
5441       {
5442          SCIPdebugMsg(scip, "skip intgrad estimator because of infinite interval bound\n");
5443          goto INTGRADESTIMATOR_CLEANUP;
5444       }
5445 
5446       constant -= coefs[i] * val;
5447    }
5448 
5449    /* add interval gradient estimator to row */
5450    SCIPaddRowprepConstant(rowprep, constant);
5451    SCIP_CALL( SCIPaddRowprepTerms(scip, rowprep, nvars, vars, coefs) );
5452 
5453  INTGRADESTIMATOR_CLEANUP:
5454    SCIPfreeBufferArrayNull(scip, &coefs);
5455    SCIPfreeBufferArrayNull(scip, &intgrad);
5456    SCIPfreeBufferArrayNull(scip, &box);
5457 
5458    return SCIP_OKAY;
5459 }
5460 
5461 /** generates a cut based on linearization (if convex), secant (if concave), or intervalgradient (if indefinite)
5462  */
5463 static
generateCut(SCIP * scip,SCIP_EXPRINT * exprint,SCIP_CONS * cons,SCIP_Real ** ref,SCIP_SOL * sol,SCIP_Bool newsol,SCIP_SIDETYPE side,SCIP_ROW ** row,SCIP_Real minviol,SCIP_Real maxrange,SCIP_Bool assumeconvex)5464 SCIP_RETCODE generateCut(
5465    SCIP*                 scip,               /**< SCIP data structure */
5466    SCIP_EXPRINT*         exprint,            /**< expression interpreter */
5467    SCIP_CONS*            cons,               /**< constraint */
5468    SCIP_Real**           ref,                /**< reference point for each exprtree, or NULL if sol should be used */
5469    SCIP_SOL*             sol,                /**< reference solution where cut should be generated, or NULL if LP solution should be used */
5470    SCIP_Bool             newsol,             /**< whether the last evaluation of the expression with the expression interpreter was not at sol */
5471    SCIP_SIDETYPE         side,               /**< for which side a cut should be generated */
5472    SCIP_ROW**            row,                /**< storage for cut */
5473    SCIP_Real             minviol,            /**< minimal absolute violation we try to achieve */
5474    SCIP_Real             maxrange,           /**< maximal range allowed */
5475    SCIP_Bool             assumeconvex        /**< whether to assume convexity in inequalities */
5476    )
5477 {
5478    SCIP_ROWPREP* rowprep;
5479    SCIP_CONSDATA* consdata;
5480    SCIP_Bool success;
5481    SCIP_Real* x;
5482    int i;
5483 
5484    assert(scip != NULL);
5485    assert(cons != NULL);
5486    assert(row  != NULL);
5487 
5488    SCIPdebugMsg(scip, "constructing cut for %s hand side of constraint <%s>\n", side == SCIP_SIDETYPE_LEFT ? "left" : "right", SCIPconsGetName(cons));
5489 
5490    SCIP_CALL( checkCurvature(scip, cons, assumeconvex) );
5491 
5492    consdata = SCIPconsGetData(cons);
5493    assert(consdata != NULL);
5494 
5495    if( consdata->nexprtrees == 0 )
5496    {
5497       char rowname[SCIP_MAXSTRLEN];
5498       (void) SCIPsnprintf(rowname, SCIP_MAXSTRLEN, "%s_%u", SCIPconsGetName(cons), ++(consdata->ncuts));
5499 
5500       /* if we are actually linear, add the constraint as row to the LP */
5501       SCIP_CALL( SCIPcreateEmptyRowCons(scip, row, cons, rowname, consdata->lhs, consdata->rhs, SCIPconsIsLocal(cons), FALSE , TRUE) );
5502       SCIP_CALL( SCIPaddVarsToRow(scip, *row, consdata->nlinvars, consdata->linvars, consdata->lincoefs) );
5503       return SCIP_OKAY;
5504    }
5505 
5506    SCIP_CALL( SCIPcreateRowprep(scip, &rowprep, side,
5507       !(side == SCIP_SIDETYPE_LEFT  && (consdata->curvature & SCIP_EXPRCURV_CONCAVE)) &&
5508       !(side == SCIP_SIDETYPE_RIGHT && (consdata->curvature & SCIP_EXPRCURV_CONVEX ))) ); /*lint --e{845}*/
5509    SCIPaddRowprepSide(rowprep, side == SCIP_SIDETYPE_LEFT  ? consdata->lhs : consdata->rhs);
5510    (void) SCIPsnprintf(rowprep->name, SCIP_MAXSTRLEN, "%s_%u", SCIPconsGetName(cons), ++(consdata->ncuts));
5511 
5512    if( ref == NULL )
5513    {
5514       SCIP_CALL( SCIPallocBufferArray(scip, &x, SCIPexprtreeGetNVars(consdata->exprtrees[0])) );
5515    }
5516 
5517    success = TRUE;
5518    for( i = 0; i < consdata->nexprtrees; ++i )
5519    {
5520       if( ref == NULL )
5521       {
5522          SCIP_CALL( SCIPreallocBufferArray(scip, &x, SCIPexprtreeGetNVars(consdata->exprtrees[i])) );  /*lint !e644*/
5523          SCIP_CALL( SCIPgetSolVals(scip, sol, SCIPexprtreeGetNVars(consdata->exprtrees[i]), SCIPexprtreeGetVars(consdata->exprtrees[i]), x) );
5524       }
5525       else
5526       {
5527          x = ref[i];
5528       }
5529 
5530       if( (side == SCIP_SIDETYPE_LEFT && (consdata->curvatures[i] & SCIP_EXPRCURV_CONCAVE)) ||
5531          (side == SCIP_SIDETYPE_RIGHT && (consdata->curvatures[i] & SCIP_EXPRCURV_CONVEX )) )
5532       {
5533          SCIP_CALL( addLinearization(scip, exprint, cons, i, x, newsol, rowprep, &success) );
5534       }
5535       else if( (side == SCIP_SIDETYPE_LEFT  && (consdata->curvatures[i] & SCIP_EXPRCURV_CONVEX)) ||
5536          (      side == SCIP_SIDETYPE_RIGHT && (consdata->curvatures[i] & SCIP_EXPRCURV_CONCAVE)) )
5537       {
5538          switch( SCIPexprtreeGetNVars(consdata->exprtrees[i]) )
5539          {
5540          case 1:
5541             SCIP_CALL( addConcaveEstimatorUnivariate(scip, cons, i, rowprep, &success) );
5542             break;
5543 
5544          case 2:
5545             SCIP_CALL( addConcaveEstimatorBivariate(scip, cons, i, x, rowprep, &success) );
5546             break;
5547 
5548          default:
5549             SCIP_CALL( addConcaveEstimatorMultivariate(scip, cons, i, x, rowprep, &success) );
5550             break;
5551          }
5552          if( !success )
5553          {
5554             SCIPdebugMsg(scip, "failed to generate polyhedral estimator for %d-dim concave function in exprtree %d, fall back to intervalgradient cut\n", SCIPexprtreeGetNVars(consdata->exprtrees[i]), i);
5555             SCIP_CALL( addIntervalGradientEstimator(scip, exprint, cons, i, x, newsol, side == SCIP_SIDETYPE_LEFT, rowprep, &success) );
5556          }
5557       }
5558       else if( SCIPexprGetOperator( SCIPexprtreeGetRoot( consdata->exprtrees[i] ) ) == SCIP_EXPR_USER )
5559       {
5560          SCIP_CALL( addUserEstimator( scip, cons, i, x, side == SCIP_SIDETYPE_LEFT, rowprep, &success ) );
5561          if( !success ) /* the user estimation may not be implemented -> try interval estimator */
5562          {
5563             SCIP_CALL( addIntervalGradientEstimator(scip, exprint, cons, i, x, newsol, side == SCIP_SIDETYPE_LEFT, rowprep, &success) );
5564          }
5565       }
5566       else
5567       {
5568          SCIP_CALL( addIntervalGradientEstimator(scip, exprint, cons, i, x, newsol, side == SCIP_SIDETYPE_LEFT, rowprep, &success) );
5569       }
5570 
5571       if( !success )
5572          break;
5573    }
5574 
5575    if( ref == NULL )
5576    {
5577       SCIPfreeBufferArray(scip, &x);
5578    }
5579 
5580    if( success )
5581    {
5582       SCIP_Real coefrange;
5583 
5584       /* add coefficients for linear variables */
5585       SCIP_CALL( SCIPaddRowprepTerms(scip, rowprep, consdata->nlinvars, consdata->linvars, consdata->lincoefs) );
5586 
5587       /* merge terms in same variable */
5588       SCIPmergeRowprepTerms(scip, rowprep);
5589 
5590       /* cleanup row */
5591       SCIP_CALL( SCIPcleanupRowprep(scip, rowprep, sol, maxrange, minviol, &coefrange, NULL) );
5592 
5593       /* check that coefficient range is ok */
5594       success = coefrange <= maxrange;
5595 
5596       /* check that side is finite */ /*lint --e{514} */
5597       success &= !SCIPisInfinity(scip, REALABS(rowprep->side));
5598 
5599       /* check whether maximal coef is finite, if any */
5600       success &= (rowprep->nvars == 0) || !SCIPisInfinity(scip, REALABS(rowprep->coefs[0]));
5601    }
5602 
5603    if( success )
5604    {
5605       SCIP_CALL( SCIPgetRowprepRowCons(scip, row, rowprep, cons) );
5606    }
5607    else
5608       *row = NULL;
5609 
5610    SCIPfreeRowprep(scip, &rowprep);
5611 
5612    return SCIP_OKAY;
5613 }
5614 
5615 /** tries to separate solution or LP solution by a linear cut
5616  *
5617  *  assumes that constraint violations have been computed
5618  */
5619 static
separatePoint(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS ** conss,int nconss,int nusefulconss,SCIP_SOL * sol,SCIP_Bool newsol,SCIP_Real minefficacy,SCIP_Bool inenforcement,SCIP_RESULT * result,SCIP_Real * bestefficacy)5620 SCIP_RETCODE separatePoint(
5621    SCIP*                 scip,               /**< SCIP data structure */
5622    SCIP_CONSHDLR*        conshdlr,           /**< nonlinear constraints handler */
5623    SCIP_CONS**           conss,              /**< constraints */
5624    int                   nconss,             /**< number of constraints */
5625    int                   nusefulconss,       /**< number of constraints that seem to be useful */
5626    SCIP_SOL*             sol,                /**< solution to separate, or NULL if LP solution should be used */
5627    SCIP_Bool             newsol,             /**< have the constraints just been evaluated at this point? */
5628    SCIP_Real             minefficacy,        /**< minimal efficacy of a cut if it should be added to the LP */
5629    SCIP_Bool             inenforcement,      /**< whether we are in constraint enforcement */
5630    SCIP_RESULT*          result,             /**< result of separation */
5631    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 */
5632    )
5633 {
5634    SCIP_CONSHDLRDATA* conshdlrdata;
5635    SCIP_CONSDATA*     consdata;
5636    SCIP_Real          efficacy;
5637    SCIP_Real          feasibility;
5638    SCIP_SIDETYPE      violside;
5639    int                c;
5640    SCIP_ROW*          row;
5641 
5642    assert(scip != NULL);
5643    assert(conshdlr != NULL);
5644    assert(conss != NULL || nconss == 0);
5645    assert(nusefulconss <= nconss);
5646    assert(result != NULL);
5647 
5648    *result = SCIP_FEASIBLE;
5649 
5650    conshdlrdata = SCIPconshdlrGetData(conshdlr);
5651    assert(conshdlrdata != NULL);
5652 
5653    if( bestefficacy != NULL )
5654       *bestefficacy = 0.0;
5655 
5656    for( c = 0, violside = SCIP_SIDETYPE_LEFT; c < nconss; c = (violside == SCIP_SIDETYPE_RIGHT ? c+1 : c), violside = (violside == SCIP_SIDETYPE_LEFT ? SCIP_SIDETYPE_RIGHT : SCIP_SIDETYPE_LEFT) )
5657    {
5658       assert(conss != NULL);
5659 
5660       /* skip constraints that are not enabled */
5661       if( !SCIPconsIsEnabled(conss[c]) || SCIPconsIsDeleted(conss[c]) )
5662          continue;
5663       assert(SCIPconsIsActive(conss[c]));
5664 
5665       consdata = SCIPconsGetData(conss[c]);
5666       assert(consdata != NULL);
5667 
5668       /* if side violside of cons c not violated, then continue to next side or next cons */
5669       if( !SCIPisGT(scip, violside == SCIP_SIDETYPE_LEFT ? consdata->lhsviol : consdata->rhsviol, SCIPfeastol(scip)) )
5670          continue;
5671 
5672       /* we are not feasible anymore */
5673       if( *result == SCIP_FEASIBLE )
5674          *result = SCIP_DIDNOTFIND;
5675 
5676       /* generate cut
5677        * if function is defined at sol (activity<infinity) and constraint is violated, then expression interpreter should have evaluated at sol to get gradient before
5678        */
5679       SCIP_CALL( generateCut(scip, conshdlrdata->exprinterpreter, conss[c], NULL, sol, newsol || SCIPisInfinity(scip, consdata->activity), violside, &row, minefficacy, conshdlrdata->cutmaxrange, conshdlrdata->assumeconvex) );
5680 
5681       if( row == NULL ) /* failed to generate cut */
5682          continue;
5683 
5684       if( sol == NULL )
5685          feasibility = SCIPgetRowLPFeasibility(scip, row);
5686       else
5687          feasibility = SCIPgetRowSolFeasibility(scip, row, sol);
5688       efficacy = -feasibility;
5689 
5690       if( SCIPisGT(scip, efficacy, minefficacy) && SCIPisCutApplicable(scip, row) )
5691       {
5692          SCIP_Bool infeasible;
5693 
5694          /* cut cuts off solution */
5695          SCIP_CALL( SCIPaddRow(scip, row, FALSE /* forcecut */, &infeasible) );
5696          if ( infeasible )
5697             *result = SCIP_CUTOFF;
5698          else
5699             *result = SCIP_SEPARATED;
5700 
5701          SCIP_CALL( SCIPresetConsAge(scip, conss[c]) );
5702 
5703          SCIPdebugMsg(scip, "add cut with efficacy %g for constraint <%s> violated by %g\n", efficacy, SCIPconsGetName(conss[c]), MAX(consdata->lhsviol, consdata->rhsviol));
5704          SCIPdebug( SCIP_CALL( SCIPprintRow(scip, row, NULL) ) );
5705 
5706          if( bestefficacy != NULL && efficacy > *bestefficacy )
5707             *bestefficacy = efficacy;
5708 
5709          /* mark row as not removable from LP for current node, if in enforcement */
5710          if( inenforcement && !conshdlrdata->enfocutsremovable )
5711             SCIPmarkRowNotRemovableLocal(scip, row);
5712       }
5713       else
5714       {
5715          SCIPdebugMsg(scip, "drop cut since efficacy %g is too small (< %g)\n", efficacy, minefficacy);
5716       }
5717 
5718       SCIP_CALL( SCIPreleaseRow (scip, &row) );
5719 
5720       if ( *result == SCIP_CUTOFF )
5721          break;
5722 
5723       /* enforce only useful constraints
5724        * others are only checked and enforced if we are still feasible or have not found a separating cut yet
5725        */
5726       if( c >= nusefulconss && *result == SCIP_SEPARATED )
5727          break;
5728    }
5729 
5730    return SCIP_OKAY;
5731 }
5732 
5733 /** adds linearizations cuts for convex constraints w.r.t. a given reference point to cutpool and sepastore
5734  * if separatedlpsol is not NULL, then a cut that separates the LP solution is added to the sepastore and is forced to enter the LP
5735  * if separatedlpsol is not NULL, but cut does not separate the LP solution, then it is added to the cutpool only
5736  * if separatedlpsol is NULL, then cut is added to cutpool only
5737  */
5738 static
addLinearizationCuts(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS ** conss,int nconss,SCIP_SOL * ref,SCIP_Bool * separatedlpsol,SCIP_Real minefficacy)5739 SCIP_RETCODE addLinearizationCuts(
5740    SCIP*                 scip,               /**< SCIP data structure */
5741    SCIP_CONSHDLR*        conshdlr,           /**< quadratic constraints handler */
5742    SCIP_CONS**           conss,              /**< constraints */
5743    int                   nconss,             /**< number of constraints */
5744    SCIP_SOL*             ref,                /**< reference point where to linearize, or NULL for LP solution */
5745    SCIP_Bool*            separatedlpsol,     /**< buffer to store whether a cut that separates the current LP solution was found and added to LP, or NULL if adding to cutpool only */
5746    SCIP_Real             minefficacy         /**< minimal efficacy of a cut when checking for separation of LP solution */
5747    )
5748 {
5749    SCIP_CONSHDLRDATA* conshdlrdata;
5750    SCIP_CONSDATA* consdata;
5751    SCIP_Bool addedtolp;
5752    SCIP_ROW* row;
5753    int c;
5754 
5755    assert(scip != NULL);
5756    assert(conshdlr != NULL);
5757    assert(conss != NULL || nconss == 0);
5758 
5759    conshdlrdata = SCIPconshdlrGetData(conshdlr);
5760    assert(conshdlrdata != NULL);
5761 
5762    if( separatedlpsol != NULL )
5763       *separatedlpsol = FALSE;
5764 
5765    for( c = 0; c < nconss; ++c )
5766    {
5767       assert(conss[c] != NULL);  /*lint !e613*/
5768 
5769       if( SCIPconsIsLocal(conss[c]) )  /*lint !e613*/
5770          continue;
5771 
5772       SCIP_CALL( checkCurvature(scip, conss[c], conshdlrdata->assumeconvex) );  /*lint !e613*/
5773 
5774       consdata = SCIPconsGetData(conss[c]);  /*lint !e613*/
5775       assert(consdata != NULL);
5776 
5777       /* if we cannot linearize, then skip constraint */
5778       if( (!(consdata->curvature & SCIP_EXPRCURV_CONVEX)  || SCIPisInfinity(scip,  consdata->rhs)) &&
5779          ( !(consdata->curvature & SCIP_EXPRCURV_CONCAVE) || SCIPisInfinity(scip, -consdata->lhs)) )
5780          continue;
5781 
5782       SCIP_CALL( generateCut(scip, conshdlrdata->exprinterpreter, conss[c], NULL, ref, TRUE,
5783             (consdata->curvature & SCIP_EXPRCURV_CONVEX) ? SCIP_SIDETYPE_RIGHT : SCIP_SIDETYPE_LEFT,
5784             &row, minefficacy, conshdlrdata->cutmaxrange, FALSE) );  /*lint !e613*/
5785 
5786       if( row == NULL )
5787          continue;
5788 
5789       addedtolp = FALSE;
5790 
5791       /* if caller wants, then check if cut separates LP solution and add to sepastore if so */
5792       if( separatedlpsol != NULL )
5793       {
5794          if( -SCIPgetRowLPFeasibility(scip, row) >= minefficacy )
5795          {
5796             SCIP_Bool infeasible;
5797 
5798             *separatedlpsol = TRUE;
5799             addedtolp = TRUE;
5800             SCIP_CALL( SCIPaddRow(scip, row, TRUE, &infeasible) );
5801             assert( ! infeasible );
5802          }
5803       }
5804 
5805       if( !SCIProwIsLocal(row) && !addedtolp )
5806       {
5807          SCIP_CALL( SCIPaddPoolCut(scip, row) );
5808       }
5809 
5810       SCIP_CALL( SCIPreleaseRow(scip, &row) );
5811    }
5812 
5813    return SCIP_OKAY;
5814 }
5815 
5816 /** processes the event that a new primal solution has been found */
5817 static
SCIP_DECL_EVENTEXEC(processNewSolutionEvent)5818 SCIP_DECL_EVENTEXEC(processNewSolutionEvent)
5819 {
5820    SCIP_CONSHDLRDATA* conshdlrdata;
5821    SCIP_CONSHDLR* conshdlr;
5822    SCIP_CONS**    conss;
5823    int            nconss;
5824    SCIP_SOL*      sol;
5825 
5826    assert(scip != NULL);
5827    assert(event != NULL);
5828    assert(eventdata != NULL);
5829    assert(eventhdlr != NULL);
5830 
5831    assert((SCIPeventGetType(event) & SCIP_EVENTTYPE_SOLFOUND) != 0);
5832 
5833    conshdlr = (SCIP_CONSHDLR*)eventdata;
5834 
5835    nconss = SCIPconshdlrGetNConss(conshdlr);
5836 
5837    if( nconss == 0 )
5838       return SCIP_OKAY;
5839 
5840    sol = SCIPeventGetSol(event);
5841    assert(sol != NULL);
5842 
5843    conshdlrdata = SCIPconshdlrGetData(conshdlr);
5844    assert(conshdlrdata != NULL);
5845 
5846    /* we are only interested in solution coming from some heuristic other than trysol, but not from the tree
5847     * the reason for ignoring trysol solutions is that they may come from an NLP solve in sepalp, where we already added linearizations,
5848     * or are from the tree, but postprocessed via proposeFeasibleSolution
5849     */
5850    if( SCIPsolGetHeur(sol) == NULL || SCIPsolGetHeur(sol) == conshdlrdata->trysolheur )
5851       return SCIP_OKAY;
5852 
5853    conss = SCIPconshdlrGetConss(conshdlr);
5854    assert(conss != NULL);
5855 
5856    SCIPdebugMsg(scip, "catched new sol event %" SCIP_EVENTTYPE_FORMAT " from heur <%s>; have %d conss\n", SCIPeventGetType(event), SCIPheurGetName(SCIPsolGetHeur(sol)), nconss);
5857 
5858    SCIP_CALL( addLinearizationCuts(scip, conshdlr, conss, nconss, sol, NULL, 0.0) );
5859 
5860    return SCIP_OKAY;
5861 }
5862 
5863 /** registers unfixed variables in nonlinear terms of violated constraints as external branching candidates */
5864 static
registerBranchingVariables(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS ** conss,int nconss,int * nnotify)5865 SCIP_RETCODE registerBranchingVariables(
5866    SCIP*                 scip,               /**< SCIP data structure */
5867    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
5868    SCIP_CONS**           conss,              /**< constraints to check */
5869    int                   nconss,             /**< number of constraints to check */
5870    int*                  nnotify             /**< counter for number of notifications performed */
5871    )
5872 {
5873    SCIP_CONSDATA* consdata;
5874    SCIP_VAR* var;
5875    int c;
5876    int i;
5877    int j;
5878 
5879    assert(scip != NULL);
5880    assert(conshdlr != NULL);
5881    assert(conss != NULL || nconss == 0);
5882 
5883    *nnotify = 0;
5884 
5885    for( c = 0; c < nconss; ++c )
5886    {
5887       assert(conss != NULL);
5888       consdata = SCIPconsGetData(conss[c]);
5889       assert(consdata != NULL);
5890 
5891       if( consdata->nexprtrees == 0 )
5892          continue;
5893 
5894       /* do not branch on violation of convex constraint */
5895       if( (!SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) || (consdata->curvature & SCIP_EXPRCURV_CONCAVE)) &&
5896          ( !SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) || (consdata->curvature & SCIP_EXPRCURV_CONVEX )) )
5897          continue;
5898       SCIPdebugMsg(scip, "cons <%s> violation: %g %g  curvature: %s\n", SCIPconsGetName(conss[c]), consdata->lhsviol, consdata->rhsviol, SCIPexprcurvGetName(consdata->curvature));
5899 
5900       for( i = 0; i < consdata->nexprtrees; ++i )
5901       {
5902          /* skip convex summands */
5903          if( (!SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) || (consdata->curvatures[i] & SCIP_EXPRCURV_CONCAVE)) &&
5904             ( !SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) || (consdata->curvatures[i] & SCIP_EXPRCURV_CONVEX )) )
5905             continue;
5906 
5907          for( j = 0; j < SCIPexprtreeGetNVars(consdata->exprtrees[i]); ++j )
5908          {
5909             var = SCIPexprtreeGetVars(consdata->exprtrees[i])[j];
5910             assert(var != NULL);
5911 
5912             if( SCIPisRelEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) )
5913             {
5914                SCIPdebugMsg(scip, "ignore fixed variable <%s>[%g, %g], width %g\n", SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var), SCIPvarGetUbLocal(var) - SCIPvarGetLbLocal(var));
5915                continue;
5916             }
5917 
5918             SCIP_CALL( SCIPaddExternBranchCand(scip, var, MAX(consdata->lhsviol, consdata->rhsviol), SCIP_INVALID) );
5919             ++*nnotify;
5920          }
5921       }
5922    }
5923 
5924    SCIPdebugMsg(scip, "registered %d branching candidates\n", *nnotify);
5925 
5926    return SCIP_OKAY;
5927 }
5928 
5929 /** registers a nonlinear variable from a violated constraint as branching candidate that has a large absolute value in the relaxation */
5930 static
registerLargeRelaxValueVariableForBranching(SCIP * scip,SCIP_CONS ** conss,int nconss,SCIP_SOL * sol,SCIP_VAR ** brvar)5931 SCIP_RETCODE registerLargeRelaxValueVariableForBranching(
5932    SCIP*                 scip,               /**< SCIP data structure */
5933    SCIP_CONS**           conss,              /**< constraints */
5934    int                   nconss,             /**< number of constraints */
5935    SCIP_SOL*             sol,                /**< solution to enforce (NULL for the LP solution) */
5936    SCIP_VAR**            brvar               /**< buffer to store branching variable */
5937    )
5938 {
5939    SCIP_CONSDATA*      consdata;
5940    SCIP_VAR*           var;
5941    SCIP_Real           val;
5942    SCIP_Real           brvarval;
5943    int i;
5944    int j;
5945    int c;
5946 
5947    assert(scip  != NULL);
5948    assert(conss != NULL || nconss == 0);
5949 
5950    *brvar = NULL;
5951    brvarval = -1.0;
5952 
5953    for( c = 0; c < nconss; ++c )
5954    {
5955       assert(conss != NULL);
5956       consdata = SCIPconsGetData(conss[c]);
5957       assert(consdata != NULL);
5958 
5959       if( !SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) && !SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
5960          continue;
5961 
5962       for( j = 0; j < consdata->nexprtrees; ++j )
5963       {
5964          for( i = 0; i < SCIPexprtreeGetNVars(consdata->exprtrees[j]); ++i )
5965          {
5966             var = SCIPexprtreeGetVars(consdata->exprtrees[j])[i];
5967             /* do not propose fixed variables */
5968             if( SCIPisRelEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) )
5969                continue;
5970             val = SCIPgetSolVal(scip, sol, var);
5971             if( REALABS(val) > brvarval )
5972             {
5973                brvarval = ABS(val);
5974                *brvar = var;
5975             }
5976          }
5977       }
5978    }
5979 
5980    if( *brvar != NULL )
5981    {
5982       SCIP_CALL( SCIPaddExternBranchCand(scip, *brvar, brvarval, SCIP_INVALID) );
5983    }
5984 
5985    return SCIP_OKAY;
5986 }
5987 
5988 /** replaces violated nonlinear constraints where all nonlinear variables are almost fixed by linear constraints
5989  * only adds constraint if it is violated in current solution
5990  * first tries to fix almost fixed variables
5991  */
5992 static
replaceViolatedByLinearConstraints(SCIP * scip,SCIP_CONS ** conss,int nconss,SCIP_Bool * addedcons,SCIP_Bool * reduceddom,SCIP_Bool * infeasible)5993 SCIP_RETCODE replaceViolatedByLinearConstraints(
5994    SCIP*                 scip,               /**< SCIP data structure */
5995    SCIP_CONS**           conss,              /**< constraints */
5996    int                   nconss,             /**< number of constraints */
5997    SCIP_Bool*            addedcons,          /**< buffer to store whether a linear constraint was added */
5998    SCIP_Bool*            reduceddom,         /**< whether a domain has been reduced */
5999    SCIP_Bool*            infeasible          /**< whether we detected infeasibility */
6000    )
6001 {
6002    SCIP_CONS*          cons;
6003    SCIP_CONSDATA*      consdata;
6004    SCIP_Real           lhs;
6005    SCIP_Real           rhs;
6006    SCIP_Real           lb;
6007    SCIP_Real           ub;
6008    SCIP_RESULT         checkresult;
6009    SCIP_VAR*           var;
6010    SCIP_Bool           tightened;
6011    int c;
6012    int i;
6013    int v;
6014 
6015    assert(scip  != NULL);
6016    assert(conss != NULL || nconss == 0);
6017    assert(addedcons != NULL);
6018    assert(reduceddom != NULL);
6019    assert(infeasible != NULL);
6020 
6021    *addedcons = FALSE;
6022    *reduceddom = FALSE;
6023    *infeasible = FALSE;
6024 
6025    for( c = 0; c < nconss; ++c )
6026    {
6027       assert(conss != NULL);
6028       consdata = SCIPconsGetData(conss[c]);
6029       assert(consdata != NULL);
6030 
6031       if( !SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) && !SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
6032          continue;
6033 
6034       lhs = consdata->lhs;
6035       rhs = consdata->rhs;
6036 
6037       for( i = 0; i < consdata->nexprtrees; ++i )
6038       {
6039          SCIP_INTERVAL nonlinactivity;
6040 
6041          /* check whether there are almost fixed nonlinear variables that can be fixed */
6042          for( v = 0; v < SCIPexprtreeGetNVars(consdata->exprtrees[i]); ++v )
6043          {
6044             var = SCIPexprtreeGetVars(consdata->exprtrees[i])[v];
6045 
6046             lb = SCIPvarGetLbLocal(var);
6047             ub = SCIPvarGetUbLocal(var);
6048             assert(SCIPisRelEQ(scip, lb, ub)); /* variable should be almost fixed */
6049 
6050             assert(!SCIPisInfinity(scip, -lb));
6051             assert(!SCIPisInfinity(scip,  ub));
6052 
6053             if( !SCIPisEQ(scip, lb, ub) )
6054             {
6055                /* try to fix variable */
6056                SCIP_CALL( SCIPtightenVarLb(scip, var, (lb+ub)/2.0, TRUE, infeasible, &tightened) );
6057                if( *infeasible )
6058                {
6059                   SCIPdebugMsg(scip, "Fixing almost fixed variable <%s> lead to infeasibility.\n", SCIPvarGetName(var));
6060                   return SCIP_OKAY;
6061                }
6062                if( tightened )
6063                {
6064                   SCIPdebugMsg(scip, "Tightened lower bound of almost fixed variable <%s>.\n", SCIPvarGetName(var));
6065                   *reduceddom = TRUE;
6066                }
6067 
6068                SCIP_CALL( SCIPtightenVarUb(scip, var, (lb+ub)/2.0, TRUE, infeasible, &tightened) );
6069                if( *infeasible )
6070                {
6071                   SCIPdebugMsg(scip, "Fixing almost fixed variable <%s> lead to infeasibility.\n", SCIPvarGetName(var));
6072                   return SCIP_OKAY;
6073                }
6074                if( tightened )
6075                {
6076                   SCIPdebugMsg(scip, "Tightened upper bound of almost fixed variable <%s>.\n", SCIPvarGetName(var));
6077                   *reduceddom = TRUE;
6078                }
6079             }
6080          }
6081 
6082          SCIP_CALL( SCIPevalExprtreeLocalBounds(scip, consdata->exprtrees[i], INTERVALINFTY, &nonlinactivity) );
6083          if( SCIPintervalIsEmpty(INTERVALINFTY, nonlinactivity) )
6084          {
6085             SCIPdebugMsg(scip, "Eval expr via exprtree on local bounds lead to infeasibility due to domain violation.\n");
6086             *infeasible = TRUE;
6087             return SCIP_OKAY;
6088          }
6089 
6090          SCIPintervalMulScalar(INTERVALINFTY, &nonlinactivity, nonlinactivity, consdata->nonlincoefs[i]);
6091 
6092          if( !SCIPisInfinity(scip, -lhs) )
6093          {
6094             if( SCIPintervalGetSup(nonlinactivity) >= INTERVALINFTY )
6095                lhs = -SCIPinfinity(scip);
6096             else if( SCIPintervalGetSup(nonlinactivity) <= -INTERVALINFTY )
6097             {
6098                /* lhs <= [...,-infinity] + ...  will never be feasible */
6099                *infeasible = TRUE;
6100                return SCIP_OKAY;
6101             }
6102             else
6103                lhs -= SCIPintervalGetSup(nonlinactivity);
6104          }
6105 
6106          if( !SCIPisInfinity(scip,  rhs) )
6107          {
6108             if( SCIPintervalGetInf(nonlinactivity) <= -INTERVALINFTY )
6109                rhs = SCIPinfinity(scip);
6110             else if( SCIPintervalGetInf(nonlinactivity) >= INTERVALINFTY )
6111             {
6112                /* [infinity,...] + ... <= rhs will never be feasible */
6113                *infeasible = TRUE;
6114                return SCIP_OKAY;
6115             }
6116             else
6117                rhs -= SCIPintervalGetInf(nonlinactivity);
6118          }
6119       }
6120 
6121       /* if some nonlinear variable was fixed now, then restart node (next enfo round) */
6122       if( *reduceddom )
6123          return SCIP_OKAY;
6124 
6125       /* check if we have a bound change */
6126       if ( consdata->nlinvars == 0 )
6127       {
6128          assert(SCIPisFeasLE(scip, lhs, rhs));
6129       }
6130       else if ( consdata->nlinvars == 1 )
6131       {
6132          SCIP_Real coef;
6133 
6134          coef = *consdata->lincoefs;
6135          SCIPdebugMsg(scip, "Linear constraint with one variable: %g <= %g <%s> <= %g\n", lhs, coef, SCIPvarGetName(*consdata->linvars), rhs);
6136 
6137          /* possibly correct lhs/rhs */
6138          assert( ! SCIPisZero(scip, coef) );
6139          if ( coef >= 0.0 )
6140          {
6141             if ( ! SCIPisInfinity(scip, -lhs) )
6142                lhs /= coef;
6143             if ( ! SCIPisInfinity(scip, rhs) )
6144                rhs /= coef;
6145          }
6146          else
6147          {
6148             SCIP_Real h;
6149             h = rhs;
6150             if ( ! SCIPisInfinity(scip, -lhs) )
6151                rhs = lhs/coef;
6152             else
6153                rhs = SCIPinfinity(scip);
6154 
6155             if ( ! SCIPisInfinity(scip, h) )
6156                lhs = h/coef;
6157             else
6158                lhs = -SCIPinfinity(scip);
6159          }
6160          SCIPdebugMsg(scip, "Linear constraint is a bound: %g <= <%s> <= %g\n", lhs, SCIPvarGetName(*consdata->linvars), rhs);
6161 
6162          /* cut off the node if SCIP needs to tight the lb/ub to +/-inf */
6163          if( SCIPisInfinity(scip, lhs) || SCIPisInfinity(scip, -rhs) )
6164          {
6165             *infeasible = TRUE;
6166             assert(consdata->linvars[0] != NULL);
6167             SCIPwarningMessage(scip, "Activity of nonlinear part is beyond SCIP's value for infinity. To enforce "
6168                "the constraint %s SCIP needs to tight bounds of %s to a value beyond +/- infinity. Please check if "
6169                "finite bounds can be added.\n", SCIPconsGetName(conss[c]), SCIPvarGetName(consdata->linvars[0]));
6170             return SCIP_OKAY;
6171          }
6172 
6173          if ( ! SCIPisInfinity(scip, -lhs) )
6174          {
6175             SCIP_CALL( SCIPtightenVarLb(scip, *consdata->linvars, lhs, TRUE, infeasible, &tightened) );
6176             if ( *infeasible )
6177             {
6178                SCIPdebugMsg(scip, "Lower bound leads to infeasibility.\n");
6179                return SCIP_OKAY;
6180             }
6181             if ( tightened )
6182             {
6183                SCIPdebugMsg(scip, "Lower bound changed.\n");
6184                *reduceddom = TRUE;
6185                return SCIP_OKAY;
6186             }
6187          }
6188 
6189          if ( ! SCIPisInfinity(scip, rhs) )
6190          {
6191             SCIP_CALL( SCIPtightenVarUb(scip, *consdata->linvars, rhs, TRUE, infeasible, &tightened) );
6192             if ( *infeasible )
6193             {
6194                SCIPdebugMsg(scip, "Upper bound leads to infeasibility.\n");
6195                return SCIP_OKAY;
6196             }
6197             if ( tightened )
6198             {
6199                SCIPdebugMsg(scip, "Upper bound changed.\n");
6200                *reduceddom = TRUE;
6201                return SCIP_OKAY;
6202             }
6203          }
6204       }
6205       else
6206       {
6207          SCIP_CALL( SCIPcreateConsLinear(scip, &cons, SCIPconsGetName(conss[c]),
6208                consdata->nlinvars, consdata->linvars, consdata->lincoefs, lhs, rhs,
6209                SCIPconsIsInitial(conss[c]), SCIPconsIsSeparated(conss[c]), SCIPconsIsEnforced(conss[c]),
6210                SCIPconsIsChecked(conss[c]), SCIPconsIsPropagated(conss[c]),  TRUE,
6211                SCIPconsIsModifiable(conss[c]), SCIPconsIsDynamic(conss[c]), SCIPconsIsRemovable(conss[c]),
6212                SCIPconsIsStickingAtNode(conss[c])) );
6213 
6214          SCIPdebugMsg(scip, "replace violated nonlinear constraint <%s> by linear constraint after all nonlinear vars have been fixed\n", SCIPconsGetName(conss[c]) );
6215          SCIPdebugPrintCons(scip, conss[c], NULL);
6216          SCIPdebugPrintCons(scip, cons, NULL);
6217 
6218          SCIP_CALL( SCIPcheckCons(scip, cons, NULL, FALSE, FALSE, FALSE, &checkresult) );
6219 
6220          if( checkresult != SCIP_INFEASIBLE && SCIPgetLPSolstat(scip) == SCIP_LPSOLSTAT_OPTIMAL )
6221          {
6222             SCIPdebugMsg(scip, "linear constraint is feasible, thus do not add\n");
6223          }
6224          else
6225          {
6226             SCIP_CALL( SCIPaddConsLocal(scip, cons, NULL) );
6227             *addedcons = TRUE;
6228          }
6229          SCIP_CALL( SCIPreleaseCons(scip, &cons) );
6230       }
6231       SCIP_CALL( SCIPdelConsLocal(scip, conss[c]) );
6232    }
6233 
6234    return SCIP_OKAY;
6235 }
6236 
6237 /* tightens a lower bound on a variable and checks the result */
6238 static
propagateBoundsTightenVarLb(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * var,SCIP_Real bnd,SCIP_RESULT * result,int * nchgbds)6239 SCIP_RETCODE propagateBoundsTightenVarLb(
6240    SCIP*                 scip,               /**< SCIP data structure */
6241    SCIP_CONS*            cons,               /**< constraint where we currently propagate, or NULL if tightening is from expression graph */
6242    SCIP_VAR*             var,                /**< variable which domain we might reduce */
6243    SCIP_Real             bnd,                /**< new lower bound for variable */
6244    SCIP_RESULT*          result,             /**< result to update if there was a tightening or cutoff */
6245    int*                  nchgbds             /**< counter to increase if a bound was tightened */
6246    )
6247 {
6248    SCIP_Bool infeas;
6249    SCIP_Bool tightened;
6250 
6251    assert(scip != NULL);
6252    assert(bnd > -INTERVALINFTY);
6253    assert(var != NULL);
6254    assert(result != NULL);
6255    assert(*result == SCIP_DIDNOTFIND || *result == SCIP_REDUCEDDOM);
6256    assert(nchgbds != NULL);
6257 
6258    if( SCIPisInfinity(scip, bnd) )
6259    { /* domain will be outside [-infty, +infty] -> declare node infeasible */
6260       *result = SCIP_CUTOFF;
6261       if( cons != NULL )
6262       {
6263          SCIP_CALL( SCIPresetConsAge(scip, cons) );
6264       }
6265       return SCIP_OKAY;
6266    }
6267 
6268    /* new lower bound is very low (between -INTERVALINFTY and -SCIPinfinity()) */
6269    if( SCIPisInfinity(scip, -bnd) )
6270       return SCIP_OKAY;
6271 
6272    bnd = SCIPadjustedVarLb(scip, var, bnd);
6273    SCIP_CALL( SCIPtightenVarLb(scip, var, bnd, FALSE, &infeas, &tightened) );
6274    if( infeas )
6275    {
6276       SCIPdebugMsg(scip, "%sfound constraint <%s> infeasible due to tightened lower bound %g for variable <%s>\n", SCIPinProbing(scip) ? "in probing " : "", cons != NULL ? SCIPconsGetName(cons) : "??", bnd, SCIPvarGetName(var));  /*lint !e585*/
6277       *result = SCIP_CUTOFF;
6278       if( cons != NULL )
6279       {
6280          SCIP_CALL( SCIPresetConsAge(scip, cons) );
6281       }
6282       return SCIP_OKAY;
6283    }
6284    if( tightened )
6285    {
6286       SCIPdebugMsg(scip, "%stightened lower bound of variable <%s> in constraint <%s> to %.15g\n", SCIPinProbing(scip) ? "in probing " : "", SCIPvarGetName(var), cons != NULL ? SCIPconsGetName(cons) : "??", bnd);  /*lint !e585*/
6287       ++*nchgbds;
6288       *result = SCIP_REDUCEDDOM;
6289       if( cons != NULL )
6290       {
6291          SCIP_CALL( SCIPresetConsAge(scip, cons) );
6292       }
6293    }
6294 
6295    return SCIP_OKAY;
6296 }
6297 
6298 /* tightens an upper bound on a variable and checks the result */
6299 static
propagateBoundsTightenVarUb(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * var,SCIP_Real bnd,SCIP_RESULT * result,int * nchgbds)6300 SCIP_RETCODE propagateBoundsTightenVarUb(
6301    SCIP*                 scip,               /**< SCIP data structure */
6302    SCIP_CONS*            cons,               /**< constraint where we currently propagate, or NULL if tightening is from expression graph */
6303    SCIP_VAR*             var,                /**< variable which domain we might reduce */
6304    SCIP_Real             bnd,                /**< new upper bound for variable */
6305    SCIP_RESULT*          result,             /**< result to update if there was a tightening or cutoff */
6306    int*                  nchgbds             /**< counter to increase if a bound was tightened */
6307    )
6308 {
6309    SCIP_Bool infeas;
6310    SCIP_Bool tightened;
6311 
6312    assert(scip != NULL);
6313    assert(bnd < INTERVALINFTY);
6314    assert(var != NULL);
6315    assert(result != NULL);
6316    assert(*result == SCIP_DIDNOTFIND || *result == SCIP_REDUCEDDOM);
6317    assert(nchgbds != NULL);
6318 
6319    if( SCIPisInfinity(scip, -bnd) )
6320    { /* domain will be outside [-infty, +infty] -> declare node infeasible */
6321       *result = SCIP_CUTOFF;
6322       if( cons != NULL )
6323       {
6324          SCIP_CALL( SCIPresetConsAge(scip, cons) );
6325       }
6326       return SCIP_OKAY;
6327    }
6328 
6329    /* new upper bound is very high (between SCIPinfinity() and INTERVALINFTY) */
6330    if( SCIPisInfinity(scip, bnd) )
6331       return SCIP_OKAY;
6332 
6333    bnd = SCIPadjustedVarUb(scip, var, bnd);
6334    SCIP_CALL( SCIPtightenVarUb(scip, var, bnd, FALSE, &infeas, &tightened) );
6335    if( infeas )
6336    {
6337       SCIPdebugMsg(scip, "%sfound constraint <%s> infeasible due to tightened upper bound %g for variable <%s>\n", SCIPinProbing(scip) ? "in probing " : "", cons != NULL ? SCIPconsGetName(cons) : "??", bnd, SCIPvarGetName(var));  /*lint !e585*/
6338       *result = SCIP_CUTOFF;
6339       if( cons != NULL )
6340       {
6341          SCIP_CALL( SCIPresetConsAge(scip, cons) );
6342       }
6343       return SCIP_OKAY;
6344    }
6345    if( tightened )
6346    {
6347       SCIPdebugMsg(scip, "%stightened upper bound of variable <%s> in constraint <%s> to %g\n", SCIPinProbing(scip) ? "in probing " : "", SCIPvarGetName(var), cons != NULL ? SCIPconsGetName(cons) : "??", bnd);  /*lint !e585*/
6348       ++*nchgbds;
6349       *result = SCIP_REDUCEDDOM;
6350       if( cons != NULL )
6351       {
6352          SCIP_CALL( SCIPresetConsAge(scip, cons) );
6353       }
6354    }
6355 
6356    return SCIP_OKAY;
6357 }
6358 
6359 /** tightens bounds of linear variables in a single nonlinear constraint */
6360 static
propagateBoundsCons(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS * cons,SCIP_RESULT * result,int * nchgbds,SCIP_Bool * redundant)6361 SCIP_RETCODE propagateBoundsCons(
6362    SCIP*                 scip,               /**< SCIP data structure */
6363    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
6364    SCIP_CONS*            cons,               /**< constraint to process */
6365    SCIP_RESULT*          result,             /**< pointer to store the result of the propagation call */
6366    int*                  nchgbds,            /**< buffer where to add the the number of changed bounds */
6367    SCIP_Bool*            redundant           /**< buffer where to store whether constraint has been found to be redundant */
6368    )
6369 {  /*lint --e{666}*/
6370    SCIP_CONSDATA*     consdata;
6371    SCIP_INTERVAL      consbounds;    /* lower and upper bounds of constraint */
6372    SCIP_INTERVAL      consactivity;  /* activity of linear plus nonlinear part */
6373    SCIP_VAR*          var;
6374    SCIP_INTERVAL      rhs;           /* right hand side of nonlinear equation */
6375    SCIP_ROUNDMODE     roundmode;
6376    SCIP_Real          bnd;
6377    int                i;
6378    SCIP_INTERVAL      nonlinactivity;
6379 
6380    assert(scip != NULL);
6381    assert(conshdlr != NULL);
6382    assert(cons != NULL);
6383    assert(result != NULL);
6384    assert(nchgbds != NULL);
6385 
6386    *result = SCIP_DIDNOTRUN;
6387    *redundant = FALSE;
6388 
6389    if( !SCIPconsIsMarkedPropagate(cons) )
6390       return SCIP_OKAY;
6391 
6392    consdata = SCIPconsGetData(cons);
6393    assert(consdata != NULL);
6394 
6395    *result = SCIP_DIDNOTFIND;
6396 
6397    SCIPdebugMsg(scip, "start linear vars domain propagation for constraint <%s>\n", SCIPconsGetName(cons));
6398 
6399    /* unmark constraint for propagation */
6400    SCIP_CALL( SCIPunmarkConsPropagate(scip, cons) );
6401 
6402    /* make sure we have activity of linear term */
6403    consdataUpdateLinearActivity(scip, consdata);
6404    assert(consdata->minlinactivity != SCIP_INVALID);  /*lint !e777*/
6405    assert(consdata->maxlinactivity != SCIP_INVALID);  /*lint !e777*/
6406    assert(consdata->minlinactivityinf >= 0);
6407    assert(consdata->maxlinactivityinf >= 0);
6408    assert(consdata->exprgraphnode != NULL || consdata->nexprtrees == 0);
6409 
6410    /* get activity of nonlinear part, should have been updated in propagateBounds */
6411    if( consdata->exprgraphnode != NULL )
6412    {
6413       nonlinactivity = SCIPexprgraphGetNodeBounds(consdata->exprgraphnode);
6414    }
6415    else
6416    {
6417       SCIPintervalSet(&nonlinactivity, 0.0);
6418    }
6419    assert(!SCIPintervalIsEmpty(INTERVALINFTY, nonlinactivity) );
6420 
6421    /* get activity of constraint function */
6422    SCIPintervalSetBounds(&consactivity, consdata->minlinactivityinf > 0 ? -INTERVALINFTY : consdata->minlinactivity, consdata->maxlinactivityinf > 0 ? INTERVALINFTY : consdata->maxlinactivity);
6423    SCIPintervalAdd(INTERVALINFTY, &consactivity, consactivity, nonlinactivity);
6424 
6425    /* check infeasibility */
6426    if( (!SCIPisInfinity(scip, -consdata->lhs) && SCIPisGT(scip, consdata->lhs-SCIPfeastol(scip), SCIPintervalGetSup(consactivity))) ||
6427        (!SCIPisInfinity(scip,  consdata->rhs) && SCIPisLT(scip, consdata->rhs+SCIPfeastol(scip), SCIPintervalGetInf(consactivity))) )
6428    {
6429       SCIPdebugMsg(scip, "found constraint <%s> to be infeasible; sides: [%g, %g], activity: [%g, %g], infeas: %.15g\n",
6430          SCIPconsGetName(cons), consdata->lhs, consdata->rhs, SCIPintervalGetInf(consactivity), SCIPintervalGetSup(consactivity),
6431          MAX(consdata->lhs - SCIPintervalGetSup(consactivity), SCIPintervalGetInf(consactivity) - consdata->rhs));
6432       *result = SCIP_CUTOFF;
6433       return SCIP_OKAY;
6434    }
6435 
6436    SCIPintervalSetBounds(&consbounds,
6437       -infty2infty(SCIPinfinity(scip), INTERVALINFTY, -consdata->lhs + SCIPepsilon(scip)),
6438       +infty2infty(SCIPinfinity(scip), INTERVALINFTY,  consdata->rhs + SCIPepsilon(scip)));
6439 
6440    /* check redundancy */
6441    if( SCIPintervalIsSubsetEQ(INTERVALINFTY, consactivity, consbounds) )
6442    {
6443       SCIPdebugMsg(scip, "found constraint <%s> to be redundant: sides: [%g, %g], activity: [%g, %g]\n",
6444          SCIPconsGetName(cons), consdata->lhs, consdata->rhs, SCIPintervalGetInf(consactivity), SCIPintervalGetSup(consactivity));
6445       *redundant = TRUE;
6446       return SCIP_OKAY;
6447    }
6448 
6449    /* propagate linear part in rhs = consbounds - nonlinactivity (use the one from consdata, since that includes infinities) */
6450    SCIPintervalSub(INTERVALINFTY, &rhs, consbounds, nonlinactivity);
6451    if( !SCIPintervalIsEntire(INTERVALINFTY, rhs) )
6452    {
6453       SCIP_Real coef;
6454 
6455       for( i = 0; i < consdata->nlinvars; ++i )
6456       {
6457          coef = consdata->lincoefs[i];
6458          var  = consdata->linvars[i];
6459 
6460          /* skip fixed variables
6461           * @todo is that a good or a bad idea?
6462           *   we can't expect much more tightening, but may detect infeasiblity, but shouldn't the check on the constraints activity detect that?
6463           */
6464          if( SCIPisEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) )
6465             continue;
6466 
6467          if( coef > 0.0 )
6468          {
6469             if( SCIPintervalGetSup(rhs) < INTERVALINFTY )
6470             {
6471                assert(consdata->minlinactivity != SCIP_INVALID);  /*lint !e777*/
6472                /* try to tighten the upper bound on var x */
6473                if( consdata->minlinactivityinf == 0 )
6474                {
6475                   assert(!SCIPisInfinity(scip, -SCIPvarGetLbLocal(var)));
6476                   /* tighten upper bound on x to (rhs.sup - (minlinactivity - coef * xlb)) / coef */
6477                   roundmode = SCIPintervalGetRoundingMode();
6478                   SCIPintervalSetRoundingModeUpwards();
6479                   bnd  = SCIPintervalGetSup(rhs);
6480                   bnd -= consdata->minlinactivity;
6481                   bnd += coef * SCIPvarGetLbLocal(var);
6482                   bnd /= coef;
6483                   SCIPintervalSetRoundingMode(roundmode);
6484                   SCIP_CALL( propagateBoundsTightenVarUb(scip, cons, var, bnd, result, nchgbds) );
6485                   if( *result == SCIP_CUTOFF )
6486                      break;
6487                }
6488                else if( consdata->minlinactivityinf == 1 && SCIPisInfinity(scip, -SCIPvarGetLbLocal(var)) )
6489                {
6490                   /* x was the variable that made the minimal linear activity equal -infinity, so
6491                    * we tighten upper bound on x to just (rhs.sup - minlinactivity) / coef */
6492                   roundmode = SCIPintervalGetRoundingMode();
6493                   SCIPintervalSetRoundingModeUpwards();
6494                   bnd  = SCIPintervalGetSup(rhs);
6495                   bnd -= consdata->minlinactivity;
6496                   bnd /= coef;
6497                   SCIPintervalSetRoundingMode(roundmode);
6498                   SCIP_CALL( propagateBoundsTightenVarUb(scip, cons, var, bnd, result, nchgbds) );
6499                   if( *result == SCIP_CUTOFF )
6500                      break;
6501                }
6502                /* otherwise the minimal activity is -infinity and x is not solely responsible for this */
6503             }
6504 
6505             if( SCIPintervalGetInf(rhs) > -INTERVALINFTY )
6506             {
6507                assert(consdata->maxlinactivity != SCIP_INVALID);  /*lint !e777*/
6508                /* try to tighten the lower bound on var x */
6509                if( consdata->maxlinactivityinf == 0 )
6510                {
6511                   assert(!SCIPisInfinity(scip, SCIPvarGetUbLocal(var)));
6512                   /* tighten lower bound on x to (rhs.inf - (maxlinactivity - coef * xub)) / coef */
6513                   roundmode = SCIPintervalGetRoundingMode();
6514                   SCIPintervalSetRoundingModeDownwards();
6515                   bnd  = SCIPintervalGetInf(rhs);
6516                   bnd -= consdata->maxlinactivity;
6517                   bnd += coef * SCIPvarGetUbLocal(var);
6518                   bnd /= coef;
6519                   SCIPintervalSetRoundingMode(roundmode);
6520                   SCIP_CALL( propagateBoundsTightenVarLb(scip, cons, var, bnd, result, nchgbds) );
6521                   if( *result == SCIP_CUTOFF )
6522                      break;
6523                }
6524                else if( consdata->maxlinactivityinf == 1 && SCIPisInfinity(scip, SCIPvarGetUbLocal(var)) )
6525                {
6526                   /* x was the variable that made the maximal linear activity equal infinity, so
6527                    * we tighten upper bound on x to just (rhs.inf - maxlinactivity) / coef */
6528                   roundmode = SCIPintervalGetRoundingMode();
6529                   SCIPintervalSetRoundingModeDownwards();
6530                   bnd  = SCIPintervalGetInf(rhs);
6531                   bnd -= consdata->maxlinactivity;
6532                   bnd /= coef;
6533                   SCIPintervalSetRoundingMode(roundmode);
6534                   SCIP_CALL( propagateBoundsTightenVarLb(scip, cons, var, bnd, result, nchgbds) );
6535                   if( *result == SCIP_CUTOFF )
6536                      break;
6537                }
6538                /* otherwise the maximal activity is +infinity and x is not solely responsible for this */
6539             }
6540          }
6541          else
6542          {
6543             assert(coef < 0.0 );
6544             if( SCIPintervalGetInf(rhs) > -INTERVALINFTY )
6545             {
6546                assert(consdata->maxlinactivity != SCIP_INVALID);  /*lint !e777*/
6547                /* try to tighten the upper bound on var x */
6548                if( consdata->maxlinactivityinf == 0 )
6549                {
6550                   assert(!SCIPisInfinity(scip, SCIPvarGetLbLocal(var)));
6551                   /* compute upper bound on x to (maxlinactivity - coef * xlb) - rhs.inf / (-coef) */
6552                   roundmode = SCIPintervalGetRoundingMode();
6553                   SCIPintervalSetRoundingModeUpwards();
6554                   bnd  = consdata->maxlinactivity;
6555                   bnd += (-coef) * SCIPvarGetLbLocal(var);
6556                   bnd -= SCIPintervalGetInf(rhs);
6557                   bnd /= (-coef);
6558                   SCIPintervalSetRoundingMode(roundmode);
6559                   SCIP_CALL( propagateBoundsTightenVarUb(scip, cons, var, bnd, result, nchgbds) );
6560                   if( *result == SCIP_CUTOFF )
6561                      break;
6562                }
6563                else if( consdata->maxlinactivityinf == 1 && SCIPisInfinity(scip, -SCIPvarGetLbLocal(var)) )
6564                {
6565                   /* x was the variable that made the maximal linear activity equal infinity, so
6566                    * we tighten upper bound on x to just (maxlinactivity - rhs.inf) / (-coef) */
6567                   roundmode = SCIPintervalGetRoundingMode();
6568                   SCIPintervalSetRoundingModeUpwards();
6569                   bnd  = consdata->maxlinactivity;
6570                   bnd -= SCIPintervalGetInf(rhs);
6571                   bnd /= (-coef);
6572                   SCIPintervalSetRoundingMode(roundmode);
6573                   SCIP_CALL( propagateBoundsTightenVarUb(scip, cons, var, bnd, result, nchgbds) );
6574                   if( *result == SCIP_CUTOFF )
6575                      break;
6576                }
6577                /* otherwise the maximal activity is infinity and x is not solely responsible for this */
6578             }
6579 
6580             if( SCIPintervalGetSup(rhs) < INTERVALINFTY )
6581             {
6582                assert(consdata->minlinactivity != SCIP_INVALID);  /*lint !e777*/
6583                /* try to tighten the lower bound on var x */
6584                if( consdata->minlinactivityinf == 0 )
6585                {
6586                   assert(!SCIPisInfinity(scip, SCIPvarGetUbLocal(var)));
6587                   /* compute lower bound on x to (minlinactivity - coef * xub) - rhs.sup / (-coef) */
6588                   roundmode = SCIPintervalGetRoundingMode();
6589                   SCIPintervalSetRoundingModeDownwards();
6590                   bnd  = consdata->minlinactivity;
6591                   bnd += (-coef) * SCIPvarGetUbLocal(var);
6592                   bnd -= SCIPintervalGetSup(rhs);
6593                   bnd /= (-coef);
6594                   SCIPintervalSetRoundingMode(roundmode);
6595                   SCIP_CALL( propagateBoundsTightenVarLb(scip, cons, var, bnd, result, nchgbds) );
6596                   if( *result == SCIP_CUTOFF )
6597                      break;
6598                }
6599                else if( consdata->minlinactivityinf == 1 && SCIPisInfinity(scip, SCIPvarGetUbLocal(var)) )
6600                {
6601                   /* x was the variable that made the maximal linear activity equal -infinity, so
6602                    * we tighten lower bound on x to just (minlinactivity - rhs.sup) / (-coef) */
6603                   roundmode = SCIPintervalGetRoundingMode();
6604                   SCIPintervalSetRoundingModeDownwards();
6605                   bnd  = consdata->minlinactivity;
6606                   bnd -= SCIPintervalGetSup(rhs);
6607                   bnd /= (-coef);
6608                   SCIPintervalSetRoundingMode(roundmode);
6609                   SCIP_CALL( propagateBoundsTightenVarLb(scip, cons, var, bnd, result, nchgbds) );
6610                   if( *result == SCIP_CUTOFF )
6611                      break;
6612                }
6613                /* otherwise the minimal activity is -infinity and x is not solely responsible for this */
6614             }
6615          }
6616       }
6617       if( *result == SCIP_CUTOFF )
6618          return SCIP_OKAY;
6619    }
6620 
6621    return SCIP_OKAY;
6622 }
6623 
6624 /** propagate constraints sides minus linear activity into nonlinear variables */
6625 static
propagateConstraintSides(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS ** conss,int nconss,SCIP_RESULT * result,int * nchgbds)6626 SCIP_RETCODE propagateConstraintSides(
6627    SCIP*                 scip,               /**< SCIP data structure */
6628    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
6629    SCIP_CONS**           conss,              /**< constraints to process */
6630    int                   nconss,             /**< number of constraints */
6631    SCIP_RESULT*          result,             /**< pointer to store the result of the propagation calls */
6632    int*                  nchgbds             /**< buffer where to add the number of changed bounds */
6633    )
6634 {
6635    SCIP_CONSHDLRDATA* conshdlrdata;
6636    SCIP_CONSDATA* consdata;
6637    int         nvars;
6638    SCIP_VAR**  vars;
6639    SCIP_EXPRGRAPHNODE** varnodes;
6640    SCIP_INTERVAL bounds;
6641    SCIP_Bool   cutoff;
6642    SCIP_ROUNDMODE roundmode;
6643    int         c;
6644    int         i;
6645 
6646    assert(scip != NULL);
6647    assert(conshdlr != NULL);
6648    assert(result != NULL);
6649    assert(nchgbds != NULL);
6650 
6651    *result = SCIP_DIDNOTFIND;
6652 
6653    conshdlrdata = SCIPconshdlrGetData(conshdlr);
6654    assert(conshdlrdata != NULL);
6655    assert(conshdlrdata->exprgraph != NULL);
6656 
6657    SCIPdebugMsg(scip, "start backward propagation in expression graph\n");
6658 
6659 #ifdef SCIP_OUTPUT
6660    {
6661       FILE* file;
6662       file = fopen("exprgraph_propconss1.dot", "w");
6663       if( file != NULL )
6664       {
6665          SCIP_CALL( SCIPexprgraphPrintDot(conshdlrdata->exprgraph, SCIPgetMessagehdlr(scip), file, NULL) );
6666          fclose(file);
6667       }
6668    }
6669 #endif
6670 
6671    /* put constraint sides less linear activity into expression graph nodes
6672     * also add a [-feastol,feastol] range around constraint sides to cope with numerics */
6673    for( c = 0; c < nconss; ++c )
6674    {
6675       /* skip (just) deleted or disabled constraints */
6676       if( SCIPconsIsDeleted(conss[c]) || !SCIPconsIsEnabled(conss[c]) )
6677          continue;
6678 
6679       consdata = SCIPconsGetData(conss[c]);
6680       assert(consdata != NULL);
6681 
6682       if( consdata->exprgraphnode == NULL )
6683          continue;
6684 
6685       roundmode = SCIPintervalGetRoundingMode();
6686       SCIPintervalSetRoundingModeDownwards();
6687 
6688       if( !SCIPisInfinity(scip, -consdata->lhs) && consdata->maxlinactivityinf == 0 )
6689          bounds.inf = consdata->lhs - consdata->maxlinactivity - SCIPfeastol(scip);
6690       else
6691          bounds.inf = -INTERVALINFTY;
6692 
6693       if( !SCIPisInfinity(scip,  consdata->rhs) && consdata->minlinactivityinf == 0 )
6694          bounds.sup = SCIPintervalNegateReal(consdata->minlinactivity - consdata->rhs - SCIPfeastol(scip));
6695       else
6696          bounds.sup =  INTERVALINFTY;
6697 
6698       SCIPintervalSetRoundingMode(roundmode);
6699 
6700       /* if we want the expression graph to propagate the bounds in any case, we set minstrength to a negative value */
6701       SCIPexprgraphTightenNodeBounds(conshdlrdata->exprgraph, consdata->exprgraphnode, bounds,
6702          consdata->forcebackprop ? -1.0 : BOUNDTIGHTENING_MINSTRENGTH, INTERVALINFTY, &cutoff);
6703       consdata->forcebackprop = FALSE; /* do this only once */
6704 
6705       if( cutoff )
6706       {
6707          SCIPdebugMsg(scip, "found constraint <%s> infeasible%s\n", SCIPconsGetName(conss[c]), SCIPinProbing(scip) ? " in probing" : "");
6708          *result = SCIP_CUTOFF;
6709          return SCIP_OKAY;
6710       }
6711    }
6712 
6713    /* compute bound tightenings for nonlinear variables */
6714    SCIPexprgraphPropagateNodeBounds(conshdlrdata->exprgraph, INTERVALINFTY, BOUNDTIGHTENING_MINSTRENGTH, &cutoff);
6715 
6716 #ifdef SCIP_OUTPUT
6717    {
6718       FILE* file;
6719       file = fopen("exprgraph_propconss2.dot", "w");
6720       if( file != NULL )
6721       {
6722          SCIP_CALL( SCIPexprgraphPrintDot(conshdlrdata->exprgraph, SCIPgetMessagehdlr(scip), file, NULL) );
6723          fclose(file);
6724       }
6725    }
6726 #endif
6727 
6728    if( cutoff )
6729    {
6730       SCIPdebugMsg(scip, "backward propagation found problem infeasible%s\n", SCIPinProbing(scip) ? " in probing" : "");
6731       *result = SCIP_CUTOFF;
6732       return SCIP_OKAY;
6733    }
6734 
6735    /* put tighter bounds into variables */
6736    nvars = SCIPexprgraphGetNVars(conshdlrdata->exprgraph);
6737    vars  = (SCIP_VAR**)SCIPexprgraphGetVars(conshdlrdata->exprgraph);
6738    varnodes = SCIPexprgraphGetVarNodes(conshdlrdata->exprgraph);
6739 
6740    /* put back new bounds into SCIP variables */
6741    for( i = 0; i < nvars && *result != SCIP_CUTOFF; ++i )
6742    {
6743       if( !SCIPisInfinity(scip, -SCIPintervalGetInf(SCIPexprgraphGetNodeBounds(varnodes[i]))) )
6744       {
6745          SCIP_CALL( propagateBoundsTightenVarLb(scip, NULL, vars[i], SCIPintervalGetInf(SCIPexprgraphGetNodeBounds(varnodes[i])), result, nchgbds) );
6746       }
6747       if( *result != SCIP_CUTOFF && !SCIPisInfinity(scip,  SCIPintervalGetSup(SCIPexprgraphGetNodeBounds(varnodes[i]))) )
6748       {
6749          SCIP_CALL( propagateBoundsTightenVarUb(scip, NULL, vars[i], SCIPintervalGetSup(SCIPexprgraphGetNodeBounds(varnodes[i])), result, nchgbds) );
6750       }
6751    }
6752 
6753    return SCIP_OKAY;
6754 }
6755 
6756 /** calls domain propagation for a set of constraints */
6757 static
propagateBounds(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS ** conss,int nconss,SCIP_Bool needclear,SCIP_RESULT * result,int * nchgbds,int * ndelconss)6758 SCIP_RETCODE propagateBounds(
6759    SCIP*                 scip,               /**< SCIP data structure */
6760    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
6761    SCIP_CONS**           conss,              /**< constraints to process */
6762    int                   nconss,             /**< number of constraints */
6763    SCIP_Bool             needclear,          /**< whether we may need to clear remainings from a previous backward propagation */
6764    SCIP_RESULT*          result,             /**< pointer to store the result of the propagation calls */
6765    int*                  nchgbds,            /**< buffer where to add the the number of changed bounds */
6766    int*                  ndelconss           /**< buffer where to increase if a constraint was deleted (locally) due to redundancy */
6767    )
6768 {
6769 #ifndef NDEBUG
6770    SCIP_CONSDATA* consdata;
6771 #endif
6772    SCIP_CONSHDLRDATA* conshdlrdata;
6773    SCIP_RESULT propresult;
6774    SCIP_Bool   domainerror;
6775    SCIP_Bool   redundant;
6776    int         roundnr;
6777    SCIP_Bool   success;
6778    int         c;
6779 
6780    assert(scip != NULL);
6781    assert(conshdlr != NULL);
6782    assert(conss != NULL || nconss == 0);
6783    assert(result != NULL);
6784    assert(nchgbds != NULL);
6785    assert(ndelconss != NULL);
6786 
6787    conshdlrdata = SCIPconshdlrGetData(conshdlr);
6788    assert(conshdlrdata != NULL);
6789    assert(conshdlrdata->exprgraph != NULL);
6790 
6791    if( nconss == 0 || conshdlrdata->ispropagated )
6792    {
6793       *result = SCIP_DIDNOTRUN;
6794       return SCIP_OKAY;
6795    }
6796 
6797    *result = SCIP_DIDNOTFIND;
6798 
6799    roundnr = 0;
6800    do
6801    {
6802       success = FALSE;
6803 
6804       SCIPdebugMsg(scip, "starting domain propagation round %d for %d constraints\n", roundnr, nconss);
6805 
6806       conshdlrdata->ispropagated = TRUE;
6807 
6808       /* propagate variable bounds through expression graph
6809        * roundnr == 0 clears remainings from a previous backward propagation
6810        * @todo could give FALSE if no linear variable in the constraints had been relaxed since last time
6811        */
6812       SCIP_CALL( SCIPexprgraphPropagateVarBounds(conshdlrdata->exprgraph, INTERVALINFTY, (roundnr == 0) && needclear, &domainerror) );
6813 
6814 #ifdef SCIP_OUTPUT
6815       {
6816          FILE* file;
6817          file = fopen("exprgraph_propvars.dot", "w");
6818          if( file != NULL )
6819          {
6820             SCIP_CALL( SCIPexprgraphPrintDot(conshdlrdata->exprgraph, SCIPgetMessagehdlr(scip), file, NULL) );
6821             fclose(file);
6822          }
6823       }
6824 #endif
6825 
6826       if( domainerror )
6827       {
6828          SCIPdebugMsg(scip, "current bounds out of domain for some expression, do cutoff\n");
6829          *result = SCIP_CUTOFF;
6830          break;
6831       }
6832 
6833       /* check for redundancy and infeasibility of constraints, tighten bounds on linear variables */
6834       for( c = 0; c < nconss && *result != SCIP_CUTOFF; ++c )
6835       {
6836          assert(conss != NULL);
6837          if( !SCIPconsIsEnabled(conss[c]) || SCIPconsIsDeleted(conss[c]) )
6838             continue;
6839          assert(SCIPconsIsActive(conss[c]));
6840 
6841 #ifndef NDEBUG
6842          consdata = SCIPconsGetData(conss[c]);
6843          assert(consdata != NULL);
6844          assert(consdata->exprgraphnode == NULL || !SCIPintervalIsEmpty(INTERVALINFTY, SCIPexprgraphGetNodeBounds(consdata->exprgraphnode)));
6845 #endif
6846 
6847          SCIP_CALL( propagateBoundsCons(scip, conshdlr, conss[c], &propresult, nchgbds, &redundant) );
6848          if( propresult != SCIP_DIDNOTFIND && propresult != SCIP_DIDNOTRUN )
6849          {
6850             *result = propresult;
6851             success = TRUE;
6852          }
6853          if( redundant )
6854          {
6855             SCIPdebugMsg(scip, "delete redundant constraint <%s> locally\n", SCIPconsGetName(conss[c]));
6856             SCIP_CALL( SCIPdelConsLocal(scip, conss[c]) );
6857             ++*ndelconss;
6858          }
6859       }
6860 
6861       /* propagate backward through expression graph */
6862       if( *result != SCIP_CUTOFF )
6863       {
6864          propresult = SCIP_DIDNOTFIND;
6865          SCIP_CALL( propagateConstraintSides(scip, conshdlr, conss, nconss, &propresult, nchgbds) );
6866 
6867          if( propresult != SCIP_DIDNOTFIND )
6868          {
6869             *result = propresult;
6870             success = TRUE;
6871          }
6872       }
6873    }
6874    while( success && *result != SCIP_CUTOFF && ++roundnr < conshdlrdata->maxproprounds );
6875 
6876    return SCIP_OKAY;
6877 }
6878 
6879 /* checks for a linear variable that can be increase or decreased without harming feasibility */
6880 static
consdataFindUnlockedLinearVar(SCIP * scip,SCIP_CONSDATA * consdata)6881 void consdataFindUnlockedLinearVar(
6882    SCIP*                 scip,               /**< SCIP data structure */
6883    SCIP_CONSDATA*        consdata            /**< constraint data */
6884    )
6885 {
6886    int i;
6887    int downlock;
6888    int uplock;
6889 
6890    consdata->linvar_maydecrease = -1;
6891    consdata->linvar_mayincrease = -1;
6892 
6893    /* check for a linear variable that can be increase or decreased without harming feasibility
6894     * setup lincoefsmin, lincoefsmax */
6895    for( i = 0; i < consdata->nlinvars; ++i )
6896    {
6897       /* compute locks of i'th linear variable */
6898       assert(consdata->lincoefs[i] != 0.0);
6899       if( consdata->lincoefs[i] > 0.0 )
6900       {
6901          downlock = !SCIPisInfinity(scip, -consdata->lhs) ? 1 : 0;  /* lhs <= x -> downlock on x */
6902          uplock = !SCIPisInfinity(scip,  consdata->rhs) ? 1 : 0;    /* x <= rhs -> uplock on x */
6903       }
6904       else
6905       {
6906          downlock = !SCIPisInfinity(scip,  consdata->rhs) ? 1 : 0;  /* -x <= rhs -> downlock on x */
6907          uplock = !SCIPisInfinity(scip, -consdata->lhs) ? 1 : 0;    /* lhs <= -x -> uplock on x */
6908       }
6909 
6910       if( SCIPvarGetNLocksDownType(consdata->linvars[i], SCIP_LOCKTYPE_MODEL) - downlock == 0 )
6911       {
6912          /* for a*x + q(y) \in [lhs, rhs], we can decrease x without harming other constraints */
6913          /* if we have already one candidate, then take the one where the loss in the objective function is less */
6914          if( (consdata->linvar_maydecrease < 0) ||
6915             (SCIPvarGetObj(consdata->linvars[consdata->linvar_maydecrease]) / consdata->lincoefs[consdata->linvar_maydecrease] > SCIPvarGetObj(consdata->linvars[i]) / consdata->lincoefs[i]) )
6916             consdata->linvar_maydecrease = i;
6917       }
6918 
6919       if( SCIPvarGetNLocksUpType(consdata->linvars[i], SCIP_LOCKTYPE_MODEL) - uplock == 0 )
6920       {
6921          /* for a*x + q(y) \in [lhs, rhs], we can increase x without harm */
6922          /* if we have already one candidate, then take the one where the loss in the objective function is less */
6923          if( (consdata->linvar_mayincrease < 0) ||
6924             (SCIPvarGetObj(consdata->linvars[consdata->linvar_mayincrease]) / consdata->lincoefs[consdata->linvar_mayincrease] > SCIPvarGetObj(consdata->linvars[i]) / consdata->lincoefs[i]) )
6925             consdata->linvar_mayincrease = i;
6926       }
6927    }
6928 
6929 #ifdef SCIP_DEBUG
6930    if( consdata->linvar_mayincrease >= 0 )
6931    {
6932       SCIPdebugMsg(scip, "may increase <%s> to become feasible\n", SCIPvarGetName(consdata->linvars[consdata->linvar_mayincrease]));
6933    }
6934    if( consdata->linvar_maydecrease >= 0 )
6935    {
6936       SCIPdebugMsg(scip, "may decrease <%s> to become feasible\n", SCIPvarGetName(consdata->linvars[consdata->linvar_maydecrease]));
6937    }
6938 #endif
6939 }
6940 
6941 /** Given a solution where every nonlinear constraint is either feasible or can be made feasible by
6942  * moving a linear variable, construct the corresponding feasible solution and pass it to the trysol heuristic.
6943  * The method assumes that this is always possible and that not all constraints are feasible already.
6944  */
6945 static
proposeFeasibleSolution(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS ** conss,int nconss,SCIP_SOL * sol,SCIP_Bool * success)6946 SCIP_RETCODE proposeFeasibleSolution(
6947    SCIP*                 scip,               /**< SCIP data structure */
6948    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
6949    SCIP_CONS**           conss,              /**< constraints to process */
6950    int                   nconss,             /**< number of constraints */
6951    SCIP_SOL*             sol,                /**< solution to process */
6952    SCIP_Bool*            success             /**< buffer to store whether we succeeded to construct a solution that satisfies all provided constraints */
6953    )
6954 {
6955    SCIP_CONSHDLRDATA* conshdlrdata;
6956    SCIP_CONSDATA* consdata;
6957    SCIP_SOL* newsol;
6958    SCIP_VAR* var;
6959    int c;
6960    SCIP_Real viol;
6961    SCIP_Real delta;
6962    SCIP_Real gap;
6963    SCIP_Bool solviolbounds;
6964    SCIP_Bool solchanged;
6965 
6966    assert(scip  != NULL);
6967    assert(conshdlr != NULL);
6968    assert(conss != NULL || nconss == 0);
6969    assert(success != NULL);
6970 
6971    conshdlrdata = SCIPconshdlrGetData(conshdlr);
6972    assert(conshdlrdata != NULL);
6973    assert(conshdlrdata->trysolheur != NULL);
6974 
6975    *success = FALSE;
6976 
6977    /* don't propose new solutions if not in presolve or solving */
6978    if( SCIPgetStage(scip) < SCIP_STAGE_INITPRESOLVE || SCIPgetStage(scip) >= SCIP_STAGE_SOLVED )
6979       return SCIP_OKAY;
6980 
6981    if( sol != NULL )
6982    {
6983       SCIP_CALL( SCIPcreateSolCopy(scip, &newsol, sol) );
6984    }
6985    else
6986    {
6987       SCIP_CALL( SCIPcreateLPSol(scip, &newsol, NULL) );
6988    }
6989    SCIP_CALL( SCIPunlinkSol(scip, newsol) );
6990    solchanged = FALSE;  /* so far newsol equals sol */
6991 
6992    for( c = 0; c < nconss; ++c )
6993    {
6994       consdata = SCIPconsGetData(conss[c]);  /*lint !e613*/
6995       assert(consdata != NULL);
6996 
6997       /* recompute violation of constraint in case solution is not original sol anymore */
6998       if( solchanged )
6999       {
7000          SCIP_CALL( computeViolation(scip, conshdlr, conss[c], newsol, &solviolbounds) );  /*lint !e613*/
7001          assert(!solviolbounds);
7002       }
7003 
7004       if( SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) )
7005          viol = consdata->lhs - consdata->activity;
7006       else if( SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
7007          viol = consdata->rhs - consdata->activity;
7008       else
7009          continue; /* constraint is satisfied */
7010 
7011       assert(viol != 0.0);
7012       if( consdata->linvar_mayincrease >= 0 &&
7013          ((  viol > 0.0 && consdata->lincoefs[consdata->linvar_mayincrease] > 0.0) ||
7014             (viol < 0.0 && consdata->lincoefs[consdata->linvar_mayincrease] < 0.0)) )
7015       {
7016          /* have variable where increasing makes the constraint less violated */
7017          var = consdata->linvars[consdata->linvar_mayincrease];
7018          /* compute how much we would like to increase var */
7019          delta = viol / consdata->lincoefs[consdata->linvar_mayincrease];
7020          assert(delta > 0.0);
7021          /* if var has an upper bound, may need to reduce delta */
7022          if( !SCIPisInfinity(scip, SCIPvarGetUbGlobal(var)) )
7023          {
7024             gap = SCIPvarGetUbGlobal(var) - SCIPgetSolVal(scip, newsol, var);
7025             delta = MIN(MAX(0.0, gap), delta);
7026          }
7027          if( SCIPisPositive(scip, delta) )
7028          {
7029             /* if variable is integral, round delta up so that it will still have an integer value */
7030             if( SCIPvarIsIntegral(var) )
7031                delta = SCIPceil(scip, delta);
7032 
7033             SCIP_CALL( SCIPincSolVal(scip, newsol, var, delta) );
7034             SCIPdebugMsg(scip, "increase <%s> by %g to %g\n", SCIPvarGetName(var), delta, SCIPgetSolVal(scip, newsol, var));
7035 
7036             solchanged = TRUE;
7037 
7038             /* adjust constraint violation, if satisfied go on to next constraint */
7039             viol -= consdata->lincoefs[consdata->linvar_mayincrease] * delta;
7040             if( SCIPisZero(scip, viol) )
7041                continue;
7042          }
7043       }
7044 
7045       assert(viol != 0.0);
7046       if( consdata->linvar_maydecrease >= 0 &&
7047          ((  viol > 0.0 && consdata->lincoefs[consdata->linvar_maydecrease] < 0.0) ||
7048             (viol < 0.0 && consdata->lincoefs[consdata->linvar_maydecrease] > 0.0)) )
7049       {
7050          /* have variable where decreasing makes constraint less violated */
7051          var = consdata->linvars[consdata->linvar_maydecrease];
7052          /* compute how much we would like to decrease var */
7053          delta = viol / consdata->lincoefs[consdata->linvar_maydecrease];
7054          assert(delta < 0.0);
7055          /* if var has a lower bound, may need to reduce delta */
7056          if( !SCIPisInfinity(scip, -SCIPvarGetLbGlobal(var)) )
7057          {
7058             gap = SCIPgetSolVal(scip, newsol, var) - SCIPvarGetLbGlobal(var);
7059             delta = MAX(MIN(0.0, gap), delta);
7060          }
7061          if( SCIPisNegative(scip, delta) )
7062          {
7063             /* if variable is integral, round delta down so that it will still have an integer value */
7064             if( SCIPvarIsIntegral(var) )
7065                delta = SCIPfloor(scip, delta);
7066             SCIP_CALL( SCIPincSolVal(scip, newsol, var, delta) );
7067             SCIPdebugMsg(scip, "increase <%s> by %g to %g\n", SCIPvarGetName(var), delta, SCIPgetSolVal(scip, newsol, var));
7068 
7069             solchanged = TRUE;
7070 
7071             /* adjust constraint violation, if satisfied go on to next constraint */
7072             viol -= consdata->lincoefs[consdata->linvar_maydecrease] * delta;
7073             if( SCIPisZero(scip, viol) )
7074                continue;
7075          }
7076       }
7077 
7078       /* still here... so probably we could not make constraint feasible due to variable bounds, thus give up */
7079       break;
7080    }
7081 
7082    /* if we have a solution that should satisfy all nonlinear constraints and has a better objective than the current upper bound,
7083     * then pass it to the trysol heuristic */
7084    if( c == nconss && (SCIPisInfinity(scip, SCIPgetUpperbound(scip)) || SCIPisSumLT(scip, SCIPgetSolTransObj(scip, newsol), SCIPgetUpperbound(scip))) )
7085    {
7086       SCIPdebugMsg(scip, "pass solution with objective value %g to trysol heuristic\n", SCIPgetSolTransObj(scip, newsol));
7087       assert(solchanged);
7088 
7089       SCIP_CALL( SCIPheurPassSolTrySol(scip, conshdlrdata->trysolheur, newsol) );
7090       *success = TRUE;
7091    }
7092 
7093    SCIP_CALL( SCIPfreeSol(scip, &newsol) );
7094 
7095    return SCIP_OKAY;
7096 }
7097 
7098 /** helper function to enforce constraints */
7099 static
enforceConstraint(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS ** conss,int nconss,int nusefulconss,SCIP_SOL * sol,SCIP_Bool solinfeasible,SCIP_RESULT * result)7100 SCIP_RETCODE enforceConstraint(
7101    SCIP*                 scip,               /**< SCIP data structure */
7102    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
7103    SCIP_CONS**           conss,              /**< constraints to process */
7104    int                   nconss,             /**< number of constraints */
7105    int                   nusefulconss,       /**< number of useful (non-obsolete) constraints to process */
7106    SCIP_SOL*             sol,                /**< solution to enforce (NULL for the LP solution) */
7107    SCIP_Bool             solinfeasible,      /**< was the solution already declared infeasible by a constraint handler? */
7108    SCIP_RESULT*          result              /**< pointer to store the result of the enforcing call */
7109    )
7110 {
7111    SCIP_CONSHDLRDATA* conshdlrdata;
7112    SCIP_CONSDATA*     consdata;
7113    SCIP_CONS*         maxviolcons;
7114    SCIP_Real          maxviol;
7115    SCIP_RESULT        propresult;
7116    SCIP_RESULT        separateresult;
7117    int                dummy;
7118    int                nnotify;
7119    SCIP_Real          sepaefficacy;
7120    SCIP_Bool          solviolbounds;
7121 
7122    assert(scip != NULL);
7123    assert(conshdlr != NULL);
7124    assert(conss != NULL || nconss == 0);
7125 
7126    conshdlrdata = SCIPconshdlrGetData(conshdlr);
7127    assert(conshdlrdata != NULL);
7128 
7129    SCIP_CALL( computeViolations(scip, conshdlr, conss, nconss, sol, &solviolbounds, &maxviolcons) );
7130 
7131    if( maxviolcons == NULL )
7132    {
7133       *result = SCIP_FEASIBLE;
7134       return SCIP_OKAY;
7135    }
7136 
7137    *result = SCIP_INFEASIBLE;
7138 
7139    if( solviolbounds )
7140    {
7141       /* if LP solution violates variable bounds, then this should be because a row was added that
7142        * introduced this variable newly to the LP, in which case it gets value 0.0; the row should
7143        * have been added to resolve an infeasibility, so solinfeasible should be TRUE
7144        * see also issue #627
7145        */
7146       assert(solinfeasible);
7147       /* however, if solinfeasible is actually not TRUE, then better cut off the node to avoid that SCIP
7148        * stops because infeasible cannot be resolved */ /*lint --e{774} */
7149       if( !solinfeasible )
7150          *result = SCIP_CUTOFF;
7151       return SCIP_OKAY;
7152    }
7153 
7154    consdata = SCIPconsGetData(maxviolcons);
7155    assert(consdata != NULL);
7156 
7157    maxviol = consdata->lhsviol + consdata->rhsviol;
7158    assert(SCIPisGT(scip, maxviol, SCIPfeastol(scip)));
7159 
7160    SCIPdebugMsg(scip, "enforcement with max violation %g in cons <%s> for %s solution\n", maxviol, SCIPconsGetName(maxviolcons),
7161          sol == NULL ? "LP" : "relaxation");
7162 
7163    /* we propagate and separate constraints only if they are active and enforcing by branching only does not seem much effective */
7164    assert(SCIPconsIsActive(maxviolcons));
7165 
7166    /* if we are above the 100'th enforcement round for this node, something is strange
7167     * (maybe the LP does not think that the cuts we add are violated, or we do ECP on a high-dimensional convex function)
7168     * 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
7169     * (in optimized more, returning SCIP_INFEASIBLE in *result would be sufficient, but in debug mode this would give an assert in scip.c)
7170     * the reason to wait for 100 rounds is to avoid calls to SCIPisStopped in normal runs, which may be expensive
7171     * we only increment nenfolprounds until 101 to avoid an overflow
7172     */
7173    if( conshdlrdata->lastenfonode == SCIPgetCurrentNode(scip) )
7174    {
7175       if( conshdlrdata->nenforounds > 100 )
7176       {
7177          if( SCIPisStopped(scip) )
7178          {
7179             SCIP_NODE* child;
7180 
7181             SCIP_CALL( SCIPcreateChild(scip, &child, 1.0, SCIPnodeGetEstimate(SCIPgetCurrentNode(scip))) );
7182             *result = SCIP_BRANCHED;
7183 
7184             return SCIP_OKAY;
7185          }
7186       }
7187       else
7188          ++conshdlrdata->nenforounds;
7189    }
7190    else
7191    {
7192       conshdlrdata->lastenfonode = SCIPgetCurrentNode(scip);
7193       conshdlrdata->nenforounds = 0;
7194    }
7195 
7196    /* run domain propagation */
7197    dummy = 0;
7198    SCIP_CALL( propagateBounds(scip, conshdlr, conss, nconss, TRUE, &propresult, &dummy, &dummy) );
7199    if( propresult == SCIP_CUTOFF || propresult == SCIP_REDUCEDDOM )
7200    {
7201       *result = propresult;
7202       return SCIP_OKAY;
7203    }
7204 
7205    /* we would like a cut that is efficient enough that it is not redundant in the LP (>lpfeastol)
7206     * however, we also don't want very weak cuts, so try to reach at least feastol (=lpfeastol by default, though)
7207     */
7208    SCIP_CALL( separatePoint(scip, conshdlr, conss, nconss, nusefulconss, sol, TRUE /* because computeviolation projects point onto box */, SCIPfeastol(scip), TRUE, &separateresult, &sepaefficacy) );
7209    if( separateresult == SCIP_CUTOFF )
7210    {
7211       SCIPdebugMsg(scip, "separation found cutoff\n");
7212       *result = SCIP_CUTOFF;
7213       return SCIP_OKAY;
7214    }
7215    if( separateresult == SCIP_SEPARATED )
7216    {
7217       SCIPdebugMsg(scip, "separation succeeded (bestefficacy = %g, minefficacy = %g)\n", sepaefficacy, SCIPfeastol(scip));
7218       *result = SCIP_SEPARATED;
7219       return SCIP_OKAY;
7220    }
7221 
7222    /* we are not feasible, the whole node is not infeasible, and we cannot find a good cut
7223     * -> collect variables for branching
7224     */
7225    SCIPdebugMsg(scip, "separation failed (bestefficacy = %g < %g = minefficacy ); max viol: %g\n", sepaefficacy, SCIPfeastol(scip), maxviol);
7226 
7227    /* find branching candidates */
7228    SCIP_CALL( registerBranchingVariables(scip, conshdlr, conss, nconss, &nnotify) );
7229 
7230    if( nnotify == 0 && !solinfeasible && SCIPfeastol(scip) > SCIPgetLPFeastol(scip) )
7231    {
7232       /* fallback 1: we also have no branching candidates, so try to find a weak cut */
7233       SCIP_CALL( separatePoint(scip, conshdlr, conss, nconss, nusefulconss, sol, FALSE, SCIPgetLPFeastol(scip), TRUE, &separateresult, &sepaefficacy) );
7234       if( separateresult == SCIP_SEPARATED || separateresult == SCIP_CUTOFF )
7235       {
7236          *result = separateresult;
7237          return SCIP_OKAY;
7238       }
7239    }
7240 
7241    if( nnotify == 0 && !solinfeasible )
7242    {
7243       /* fallback 2: separation probably failed because of numerical difficulties with a convex constraint;
7244        * if noone declared solution infeasible yet and we had not even found a weak cut, try to resolve by branching
7245        */
7246       SCIP_VAR* brvar = NULL;
7247       SCIP_CALL( registerLargeRelaxValueVariableForBranching(scip, conss, nconss, sol, &brvar) );
7248       if( brvar == NULL )
7249       {
7250          /* fallback 3: all nonlinear variables in all violated constraints seem to be fixed -> replace by linear constraints */
7251          SCIP_Bool addedcons;
7252          SCIP_Bool reduceddom;
7253          SCIP_Bool infeasible;
7254 
7255          SCIPdebugMsg(scip, "All nonlinear variables seem to be fixed. Replace remaining violated nonlinear constraints by linear constraints.\n");
7256          SCIP_CALL( replaceViolatedByLinearConstraints(scip, conss, nconss, &addedcons, &reduceddom, &infeasible) );
7257          /* if the linear constraints are actually feasible, then adding them and returning SCIP_CONSADDED confuses SCIP
7258           * when it enforces the new constraints again and nothing resolves the infeasiblity that we declare here thus,
7259           * we only add them if considered violated, and otherwise claim the solution is feasible (but print a
7260           * warning) */
7261          if ( infeasible )
7262             *result = SCIP_CUTOFF;
7263          else if ( addedcons )
7264             *result = SCIP_CONSADDED;
7265          else if ( reduceddom )
7266             *result = SCIP_REDUCEDDOM;
7267          else
7268          {
7269             *result = SCIP_FEASIBLE;
7270             SCIPwarningMessage(scip, "could not enforce feasibility by separating or branching; declaring solution with viol %g as feasible\n", maxviol);
7271          }
7272          return SCIP_OKAY;
7273       }
7274       else
7275       {
7276          SCIPdebugMsg(scip, "Could not find any usual branching variable candidate. Proposed variable <%s> with LP value %g for branching.\n",
7277             SCIPvarGetName(brvar), SCIPgetSolVal(scip, sol, brvar));
7278          nnotify = 1;
7279       }
7280    }
7281 
7282    assert(*result == SCIP_INFEASIBLE && (solinfeasible || nnotify > 0));
7283    return SCIP_OKAY;
7284 }
7285 
7286 /*
7287  * Callback methods of constraint handler
7288  */
7289 
7290 /** copy method for constraint handler plugins (called when SCIP copies plugins) */
7291 static
SCIP_DECL_CONSHDLRCOPY(conshdlrCopyNonlinear)7292 SCIP_DECL_CONSHDLRCOPY(conshdlrCopyNonlinear)
7293 {
7294    assert(scip != NULL);
7295    assert(conshdlr != NULL);
7296    /* assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0); */
7297 
7298    /* call inclusion method of constraint handler */
7299    SCIP_CALL( SCIPincludeConshdlrNonlinear(scip) );
7300 
7301    *valid = TRUE;
7302 
7303    return SCIP_OKAY;
7304 }
7305 
7306 /** destructor of constraint handler to free constraint handler data (called when SCIP is exiting) */
7307 static
SCIP_DECL_CONSFREE(consFreeNonlinear)7308 SCIP_DECL_CONSFREE(consFreeNonlinear)
7309 {
7310    SCIP_CONSHDLRDATA* conshdlrdata;
7311    int                i;
7312 
7313    assert(scip     != NULL);
7314    assert(conshdlr != NULL);
7315 
7316    conshdlrdata = SCIPconshdlrGetData(conshdlr);
7317    assert(conshdlrdata != NULL);
7318    assert(conshdlrdata->exprinterpreter != NULL);
7319    assert(conshdlrdata->exprgraph != NULL);
7320    assert(SCIPexprgraphGetNVars(conshdlrdata->exprgraph) == 0);
7321 
7322    /* free expression graph */
7323    SCIP_CALL( SCIPexprgraphFree(&conshdlrdata->exprgraph) );
7324 
7325    /* free upgrade functions */
7326    for( i = 0; i < conshdlrdata->nnlconsupgrades; ++i )
7327    {
7328       assert(conshdlrdata->nlconsupgrades[i] != NULL);
7329       SCIPfreeBlockMemory(scip, &conshdlrdata->nlconsupgrades[i]);  /*lint !e866*/
7330    }
7331    SCIPfreeBlockMemoryArrayNull(scip, &conshdlrdata->nlconsupgrades, conshdlrdata->nlconsupgradessize);
7332 
7333    /* free expressions interpreter */
7334    SCIP_CALL( SCIPexprintFree(&conshdlrdata->exprinterpreter) );
7335 
7336    SCIPfreeBlockMemory(scip, &conshdlrdata);
7337 
7338    return SCIP_OKAY;
7339 }
7340 
7341 /** initialization method of constraint handler (called after problem was transformed) */
7342 static
SCIP_DECL_CONSINIT(consInitNonlinear)7343 SCIP_DECL_CONSINIT(consInitNonlinear)
7344 {
7345    SCIP_CONSHDLRDATA* conshdlrdata;
7346 
7347    assert(scip != NULL);
7348    assert(conshdlr != NULL);
7349 
7350    conshdlrdata = SCIPconshdlrGetData(conshdlr);
7351    assert(conshdlrdata != NULL);
7352 
7353    conshdlrdata->subnlpheur = SCIPfindHeur(scip, "subnlp");
7354    conshdlrdata->trysolheur = SCIPfindHeur(scip, "trysol");
7355 
7356    /* reset counter, since we have a new problem */
7357    conshdlrdata->naddedreformconss = 0;
7358 
7359 #ifdef SCIP_OUTPUT
7360    {
7361       FILE* file;
7362       file = fopen("exprgraph_init.dot", "w");
7363       if( file != NULL )
7364       {
7365          SCIP_CALL( SCIPexprgraphPrintDot(conshdlrdata->exprgraph, SCIPgetMessagehdlr(scip), file, NULL) );
7366          fclose(file);
7367       }
7368    }
7369 #endif
7370 
7371    return SCIP_OKAY;
7372 }  /*lint !e715*/
7373 
7374 /** deinitialization method of constraint handler (called before transformed problem is freed) */
7375 static
SCIP_DECL_CONSEXIT(consExitNonlinear)7376 SCIP_DECL_CONSEXIT(consExitNonlinear)
7377 {
7378    SCIP_CONSHDLRDATA* conshdlrdata;
7379 
7380    assert(scip != NULL);
7381    assert(conshdlr != NULL);
7382 
7383    conshdlrdata = SCIPconshdlrGetData(conshdlr);
7384    assert(conshdlrdata != NULL);
7385 
7386    conshdlrdata->subnlpheur = NULL;
7387    conshdlrdata->trysolheur = NULL;
7388 
7389    return SCIP_OKAY;
7390 }  /*lint !e715*/
7391 
7392 
7393 /** presolving initialization method of constraint handler (called when presolving is about to begin) */
7394 static
SCIP_DECL_CONSINITPRE(consInitpreNonlinear)7395 SCIP_DECL_CONSINITPRE(consInitpreNonlinear)
7396 {  /*lint --e{715}*/
7397    SCIP_CONSDATA* consdata;
7398    int c;
7399 
7400    assert(scip != NULL);
7401    assert(conshdlr != NULL);
7402    assert(conss != NULL || nconss == 0);
7403 
7404    for( c = 0; c < nconss; ++c )
7405    {
7406       /* skip not yet active constraints */
7407       if( !SCIPconsIsActive(conss[c]) )  /*lint !e613*/
7408          continue;
7409 
7410       consdata = SCIPconsGetData(conss[c]);  /*lint !e613*/
7411       assert(consdata != NULL);
7412 
7413       /* forget expression trees */
7414       assert(consdata->nexprtrees == 0 || consdata->exprgraphnode != NULL);
7415       SCIP_CALL( consdataSetExprtrees(scip, consdata, 0, NULL, NULL, FALSE) );
7416 
7417       /* mark constraint for propagation */
7418       SCIP_CALL( SCIPmarkConsPropagate(scip, conss[c]) );  /*lint !e613*/
7419    }
7420 
7421    return SCIP_OKAY;
7422 }
7423 
7424 
7425 /** presolving deinitialization method of constraint handler (called after presolving has been finished) */
7426 static
SCIP_DECL_CONSEXITPRE(consExitpreNonlinear)7427 SCIP_DECL_CONSEXITPRE(consExitpreNonlinear)
7428 {  /*lint --e{715}*/
7429    SCIP_CONSHDLRDATA* conshdlrdata;
7430    SCIP_CONSDATA*     consdata;
7431    SCIP_Bool          havegraphchange;
7432    SCIP_Bool          havechange;
7433    SCIP_Bool          domainerror;
7434 #ifndef NDEBUG
7435    int i;
7436    int j;
7437 #endif
7438    int c;
7439 
7440    assert(scip != NULL);
7441    assert(conshdlr != NULL);
7442    assert(conss != NULL || nconss == 0);
7443 
7444    conshdlrdata = SCIPconshdlrGetData(conshdlr);
7445    assert(conshdlrdata != NULL);
7446 
7447    havegraphchange = FALSE;
7448 
7449    if( !conshdlrdata->isremovedfixings )
7450    {
7451       SCIP_CALL( removeFixedNonlinearVariables(scip, conshdlr) );
7452       assert(conshdlrdata->isremovedfixings);
7453 
7454       havegraphchange = TRUE;
7455    }
7456 
7457    /* if undefined expressions in exprgraph (very unlikely), we will hopefully recognize this during domain propagation later (if it involved an active constraint) */
7458    SCIP_CALL( SCIPexprgraphSimplify(conshdlrdata->exprgraph, SCIPgetMessagehdlr(scip), SCIPepsilon(scip), conshdlrdata->maxexpansionexponent, &havechange, &domainerror) );
7459    SCIPdebugMsg(scip, "expression graph simplifier found %schange, domain error = %u\n", havechange ? "" : "no ", domainerror);
7460    havegraphchange |= havechange;
7461 
7462    /* some of the methods below will not work if there was a domain error (#1148, point 3) */
7463    if( domainerror )
7464       return SCIP_OKAY;
7465 
7466    for( c = 0; c < nconss; ++c )
7467    {
7468       assert(conss != NULL);
7469 
7470       /* skip inactive constraints */
7471       if( !SCIPconsIsActive(conss[c]) )
7472          continue;
7473       assert(SCIPconsIsAdded(conss[c]));
7474 
7475       consdata = SCIPconsGetData(conss[c]);
7476       assert(consdata != NULL);
7477 
7478       if( !consdata->isremovedfixingslin )
7479       {
7480          SCIP_CALL( removeFixedLinearVariables(scip, conss[c]) );
7481       }
7482 
7483       if( !consdata->ispresolved || havegraphchange )
7484       {
7485          SCIP_Bool infeasible;
7486          SCIP_CALL( splitOffLinearPart(scip, conshdlr, conss[c], &infeasible) );
7487 
7488          /* the infeasibility should have been detected during presolve */
7489          assert(!infeasible);
7490       }
7491 
7492       SCIP_CALL( mergeAndCleanLinearVars(scip, conss[c]) );
7493 
7494       assert(consdata->isremovedfixingslin);
7495       assert(consdata->linvarsmerged);
7496 #ifndef NDEBUG
7497       for( i = 0; i < consdata->nlinvars; ++i )
7498          assert(SCIPvarIsActive(consdata->linvars[i]));
7499 #endif
7500 
7501       if( consdata->exprgraphnode != NULL )
7502       {
7503          /* get expression trees from expression graph */
7504          SCIP_EXPRTREE** exprtrees;
7505          SCIP_Real* coefs;
7506          int nexprtrees;
7507          int exprtreessize;
7508 
7509          exprtreessize = SCIPexprgraphGetSumTreesNSummands(consdata->exprgraphnode);
7510 
7511          SCIP_CALL( SCIPallocBufferArray(scip, &exprtrees, exprtreessize) );
7512          SCIP_CALL( SCIPallocBufferArray(scip, &coefs,     exprtreessize) );
7513 
7514          SCIP_CALL( SCIPexprgraphGetSumTrees(conshdlrdata->exprgraph, consdata->exprgraphnode,
7515                exprtreessize, &nexprtrees, exprtrees, coefs) );
7516          assert(nexprtrees > 0);
7517 
7518          SCIP_CALL( consdataSetExprtrees(scip, consdata, nexprtrees, exprtrees, coefs, FALSE) );
7519 
7520          SCIPfreeBufferArray(scip, &coefs);
7521          SCIPfreeBufferArray(scip, &exprtrees);
7522 
7523          assert(consdata->nexprtrees > 0 );
7524 #ifndef NDEBUG
7525          for( j = 0; j < consdata->nexprtrees; ++j )
7526             for( i = 0; i < SCIPexprtreeGetNVars(consdata->exprtrees[j]); ++i )
7527                assert(SCIPvarIsActive(SCIPexprtreeGetVars(consdata->exprtrees[j])[i]));
7528 #endif
7529 
7530          /* tell SCIP that we have something nonlinear */
7531          SCIPenableNLP(scip);
7532       }
7533    }
7534 
7535    return SCIP_OKAY;
7536 }
7537 
7538 /** solving process initialization method of constraint handler (called when branch and bound process is about to begin) */
7539 static
SCIP_DECL_CONSINITSOL(consInitsolNonlinear)7540 SCIP_DECL_CONSINITSOL(consInitsolNonlinear)
7541 {
7542    SCIP_CONSHDLRDATA* conshdlrdata;
7543    SCIP_CONSDATA*     consdata;
7544    int                c;
7545    int                i;
7546 
7547    assert(scip     != NULL);
7548    assert(conshdlr != NULL);
7549    assert(conss    != NULL || nconss == 0);
7550 
7551    conshdlrdata = SCIPconshdlrGetData(conshdlr);
7552    assert(conshdlrdata != NULL);
7553 
7554    for( c = 0; c < nconss; ++c )
7555    {
7556       assert(conss != NULL);
7557       consdata = SCIPconsGetData(conss[c]);
7558       assert(consdata != NULL);
7559 
7560       /* check for a linear variable that can be increase or decreased without harming feasibility */
7561       consdataFindUnlockedLinearVar(scip, consdata);
7562 
7563       /* setup lincoefsmin, lincoefsmax */
7564       consdata->lincoefsmin = SCIPinfinity(scip);
7565       consdata->lincoefsmax = 0.0;
7566       for( i = 0; i < consdata->nlinvars; ++i )
7567       {
7568          consdata->lincoefsmin = MIN(consdata->lincoefsmin, REALABS(consdata->lincoefs[i]));  /*lint !e666*/
7569          consdata->lincoefsmax = MAX(consdata->lincoefsmax, REALABS(consdata->lincoefs[i]));  /*lint !e666*/
7570       }
7571 
7572       /* add nlrow respresentation to NLP, if NLP had been constructed */
7573       if( SCIPisNLPConstructed(scip) && SCIPconsIsEnabled(conss[c]) )
7574       {
7575          if( consdata->nlrow == NULL )
7576          {
7577             /* compute curvature for the nonlinear constraint if not done yet */
7578             SCIP_CALL( checkCurvature(scip, conss[c], conshdlrdata->assumeconvex) );
7579 
7580             SCIP_CALL( createNlRow(scip, conss[c]) );
7581             assert(consdata->nlrow != NULL);
7582          }
7583          SCIP_CALL( SCIPaddNlRow(scip, consdata->nlrow) );
7584       }
7585    }
7586 
7587    conshdlrdata->newsoleventfilterpos = -1;
7588    if( nconss != 0 )
7589    {
7590       SCIP_EVENTHDLR* eventhdlr;
7591 
7592       eventhdlr = SCIPfindEventhdlr(scip, CONSHDLR_NAME"_newsolution");
7593       assert(eventhdlr != NULL);
7594 
7595       SCIP_CALL( SCIPcatchEvent(scip, SCIP_EVENTTYPE_SOLFOUND, eventhdlr, (SCIP_EVENTDATA*)conshdlr, &conshdlrdata->newsoleventfilterpos) );
7596    }
7597 
7598    /* reset flags and counters */
7599    conshdlrdata->sepanlp = FALSE;
7600    conshdlrdata->lastenfonode = NULL;
7601    conshdlrdata->nenforounds = 0;
7602 
7603    return SCIP_OKAY;
7604 }
7605 
7606 /** solving process deinitialization method of constraint handler (called before branch and bound process data is freed) */
7607 static
SCIP_DECL_CONSEXITSOL(consExitsolNonlinear)7608 SCIP_DECL_CONSEXITSOL(consExitsolNonlinear)
7609 {
7610    SCIP_CONSHDLRDATA* conshdlrdata;
7611    SCIP_CONSDATA* consdata;
7612    int c;
7613 
7614    assert(scip != NULL);
7615    assert(conshdlr != NULL);
7616    assert(conss != NULL || nconss == 0);
7617 
7618    conshdlrdata = SCIPconshdlrGetData(conshdlr);
7619    assert(conshdlrdata != NULL);
7620 
7621    if( conshdlrdata->newsoleventfilterpos >= 0 )
7622    {
7623       SCIP_EVENTHDLR* eventhdlr;
7624 
7625       eventhdlr = SCIPfindEventhdlr(scip, CONSHDLR_NAME"_newsolution");
7626       assert(eventhdlr != NULL);
7627 
7628       SCIP_CALL( SCIPdropEvent(scip, SCIP_EVENTTYPE_SOLFOUND, eventhdlr, (SCIP_EVENTDATA*)conshdlr, conshdlrdata->newsoleventfilterpos) );
7629       conshdlrdata->newsoleventfilterpos = -1;
7630    }
7631 
7632    for( c = 0; c < nconss; ++c )
7633    {
7634       consdata = SCIPconsGetData(conss[c]);  /*lint !e613*/
7635       assert(consdata != NULL);
7636 
7637       /* free nonlinear row representation */
7638       if( consdata->nlrow != NULL )
7639       {
7640          SCIP_CALL( SCIPreleaseNlRow(scip, &consdata->nlrow) );
7641       }
7642    }
7643 
7644    return SCIP_OKAY;
7645 }  /*lint !e715*/
7646 
7647 
7648 /** frees specific constraint data */
7649 static
SCIP_DECL_CONSDELETE(consDeleteNonlinear)7650 SCIP_DECL_CONSDELETE(consDeleteNonlinear)
7651 {
7652    assert(scip != NULL);
7653    assert(conshdlr != NULL);
7654    assert(cons != NULL);
7655    assert(!SCIPconsIsActive(cons));
7656    assert(consdata != NULL);
7657    assert(SCIPconsGetData(cons) == *consdata);
7658 
7659    SCIPdebugMsg(scip, "consDelete for cons <%s>\n", SCIPconsGetName(cons));
7660 
7661    /* expression should have been removed from expression graph when constraint was deactivated */
7662    assert((*consdata)->exprgraphnode == NULL);
7663 
7664    SCIP_CALL( consdataFree(scip, consdata) );
7665 
7666    assert(*consdata == NULL);
7667 
7668    return SCIP_OKAY;
7669 }
7670 
7671 /** transforms constraint data into data belonging to the transformed problem */
7672 static
SCIP_DECL_CONSTRANS(consTransNonlinear)7673 SCIP_DECL_CONSTRANS(consTransNonlinear)
7674 {
7675    SCIP_CONSDATA* sourcedata;
7676    SCIP_CONSDATA* targetdata;
7677    int            i;
7678 
7679    sourcedata = SCIPconsGetData(sourcecons);
7680    assert(sourcedata != NULL);
7681 
7682    SCIP_CALL( consdataCreate(scip, &targetdata,
7683          sourcedata->lhs, sourcedata->rhs,
7684          sourcedata->nlinvars, sourcedata->linvars, sourcedata->lincoefs,
7685          sourcedata->nexprtrees, sourcedata->exprtrees, sourcedata->nonlincoefs,
7686          FALSE) );
7687 
7688    /* copy information on curvature, if known in original constraint */
7689    if( sourcedata->iscurvchecked && sourcedata->nexprtrees > 0 )
7690    {
7691       BMScopyMemoryArray(targetdata->curvatures, sourcedata->curvatures, sourcedata->nexprtrees);
7692       targetdata->curvature = sourcedata->curvature;
7693       targetdata->iscurvchecked = TRUE;
7694    }
7695 
7696    for( i = 0; i < targetdata->nlinvars; ++i )
7697    {
7698       SCIP_CALL( SCIPgetTransformedVar(scip, targetdata->linvars[i], &targetdata->linvars[i]) );
7699       SCIP_CALL( SCIPcaptureVar(scip, targetdata->linvars[i]) );
7700    }
7701 
7702    for( i = 0; i < targetdata->nexprtrees; ++i )
7703    {
7704       SCIP_CALL( SCIPgetExprtreeTransformedVars(scip, targetdata->exprtrees[i]) );
7705    }
7706 
7707    /* create target constraint */
7708    SCIP_CALL( SCIPcreateCons(scip, targetcons, SCIPconsGetName(sourcecons), conshdlr, targetdata,
7709          SCIPconsIsInitial(sourcecons), SCIPconsIsSeparated(sourcecons), SCIPconsIsEnforced(sourcecons),
7710          SCIPconsIsChecked(sourcecons), SCIPconsIsPropagated(sourcecons),  SCIPconsIsLocal(sourcecons),
7711          SCIPconsIsModifiable(sourcecons), SCIPconsIsDynamic(sourcecons), SCIPconsIsRemovable(sourcecons),
7712          SCIPconsIsStickingAtNode(sourcecons)) );
7713 
7714    SCIPdebugMsg(scip, "created transformed nonlinear constraint ");
7715    SCIPdebugPrintCons(scip, *targetcons, NULL);
7716 
7717    return SCIP_OKAY;
7718 }
7719 
7720 /** LP initialization method of constraint handler (called before the initial LP relaxation at a node is solved) */
7721 static
SCIP_DECL_CONSINITLP(consInitlpNonlinear)7722 SCIP_DECL_CONSINITLP(consInitlpNonlinear)
7723 {
7724    SCIP_CONSHDLRDATA* conshdlrdata;
7725    SCIP_CONSDATA*     consdata;
7726    SCIP_ROW*          row;
7727    int                c;
7728    SCIP_Real**        x;
7729    int                nvars;
7730    int                i;
7731    int                j;
7732    SCIP_VAR*          var;
7733    SCIP_Bool          haveunboundedvar;
7734 
7735    assert(scip != NULL);
7736    assert(conshdlr != NULL);
7737    assert(conss != NULL || nconss == 0);
7738 
7739    conshdlrdata = SCIPconshdlrGetData(conshdlr);
7740    assert(conshdlrdata != NULL);
7741 
7742    *infeasible = FALSE;
7743 
7744    for( c = 0; c < nconss && !(*infeasible); ++c )
7745    {
7746       assert(conss[c] != NULL);  /*lint !e613*/
7747 
7748       SCIP_CALL( checkCurvature(scip, conss[c], conshdlrdata->assumeconvex) );  /*lint !e613*/
7749 
7750       consdata = SCIPconsGetData(conss[c]);  /*lint !e613*/
7751       assert(consdata != NULL);
7752 
7753       row = NULL;
7754 
7755       if( consdata->nexprtrees == 0 )
7756       {
7757          assert(consdata->exprgraphnode == NULL);
7758          /* if we are actually linear, add the constraint as row to the LP */
7759          SCIP_CALL( SCIPcreateEmptyRowCons(scip, &row, conss[c], SCIPconsGetName(conss[c]), consdata->lhs, consdata->rhs,
7760                SCIPconsIsLocal(conss[c]), FALSE , TRUE) );  /*lint !e613*/
7761          SCIP_CALL( SCIPaddVarsToRow(scip, row, consdata->nlinvars, consdata->linvars, consdata->lincoefs) );
7762          SCIP_CALL( SCIPaddRow(scip, row, FALSE, infeasible) );
7763          SCIP_CALL( SCIPreleaseRow (scip, &row) );
7764          continue;
7765       }
7766 
7767       /* setup reference points for each exprtree */
7768       SCIP_CALL( SCIPallocBufferArray(scip, &x, consdata->nexprtrees) );
7769       haveunboundedvar = FALSE;
7770       for( j = 0; j < consdata->nexprtrees; ++j )
7771       {
7772          nvars = SCIPexprtreeGetNVars(consdata->exprtrees[j]);
7773 
7774          SCIP_CALL( SCIPallocBufferArray(scip, &x[j], nvars) );  /*lint !e866*/
7775          for( i = 0; i < nvars; ++i )
7776          {
7777             var = SCIPexprtreeGetVars(consdata->exprtrees[j])[i];
7778             assert(var != NULL);
7779             /* use midpoint as reference value, if both bounds are finite
7780              * otherwise use 0.0, projected on bounds
7781              */
7782             if( SCIPisInfinity(scip, -SCIPvarGetLbGlobal(var)) )
7783             {
7784                if( SCIPisInfinity(scip, SCIPvarGetUbGlobal(var)) )
7785                {
7786                   x[j][i] = 0.0;
7787                   haveunboundedvar = TRUE;
7788                }
7789                else
7790                   x[j][i] = MIN(0.0, SCIPvarGetUbGlobal(var));  /*lint !e666*/
7791             }
7792             else
7793             {
7794                if( SCIPisInfinity(scip, SCIPvarGetUbGlobal(var)) )
7795                   x[j][i] = MAX(0.0, SCIPvarGetLbGlobal(var));  /*lint !e666*/
7796                else
7797                {
7798                   x[j][i] = (SCIPvarGetLbGlobal(var) + SCIPvarGetUbGlobal(var)) / 2.0;
7799                   /* shift refpoint into [-INITLPMAXVARVAL, INITLPMAXVARVAL], if bounds allow */
7800                   if( x[j][i] < -INITLPMAXVARVAL && SCIPvarGetUbGlobal(var) >= -INITLPMAXVARVAL )
7801                      x[j][i] = -INITLPMAXVARVAL;
7802                   else if( x[j][i] > INITLPMAXVARVAL && SCIPvarGetLbGlobal(var) <= INITLPMAXVARVAL )
7803                      x[j][i] =  INITLPMAXVARVAL;
7804                }
7805             }
7806          }
7807       }
7808 
7809       /* for inequalities that are convex or that have bounded variables, try to generate a cut */
7810       if( !SCIPisInfinity(scip,  consdata->rhs) && ((consdata->curvature & SCIP_EXPRCURV_CONVEX)  || !haveunboundedvar) )
7811       {
7812          SCIP_CALL( generateCut(scip, conshdlrdata->exprinterpreter, conss[c], x, NULL, TRUE, SCIP_SIDETYPE_RIGHT, &row,
7813                -SCIPinfinity(scip), conshdlrdata->cutmaxrange, FALSE) );  /*lint !e613*/
7814 
7815          if( row != NULL )
7816          {
7817             SCIP_CALL( SCIPaddRow(scip, row, FALSE /* forcecut */, infeasible) );
7818             SCIPdebug( SCIP_CALL( SCIPprintRow(scip, row, NULL) ) );
7819             SCIP_CALL( SCIPreleaseRow(scip, &row) );
7820          }
7821       }
7822 
7823       if( !(*infeasible) && !SCIPisInfinity(scip, -consdata->lhs) &&
7824          ((consdata->curvature & SCIP_EXPRCURV_CONCAVE) || !haveunboundedvar) )
7825       {
7826          SCIP_CALL( generateCut(scip, conshdlrdata->exprinterpreter, conss[c], x, NULL, TRUE, SCIP_SIDETYPE_LEFT, &row,
7827             -SCIPinfinity(scip), conshdlrdata->cutmaxrange, FALSE) );  /*lint !e613*/
7828 
7829          if( row != NULL )
7830          {
7831             SCIP_CALL( SCIPaddRow(scip, row, FALSE /* forcecut */, infeasible) );
7832             SCIPdebug( SCIP_CALL( SCIPprintRow(scip, row, NULL) ) );
7833             SCIP_CALL( SCIPreleaseRow(scip, &row) );
7834          }
7835       }
7836 
7837       /* @todo could add more linearizations for convex or multivariate concave inequ. */
7838 
7839       for( j = consdata->nexprtrees - 1; j >= 0; --j )
7840       {
7841          SCIPfreeBufferArray(scip, &x[j]);
7842       }
7843       SCIPfreeBufferArray(scip, &x);
7844    }
7845 
7846    return SCIP_OKAY;
7847 }
7848 
7849 /** separation method of constraint handler for LP solutions */
7850 static
SCIP_DECL_CONSSEPALP(consSepalpNonlinear)7851 SCIP_DECL_CONSSEPALP(consSepalpNonlinear)
7852 {
7853    SCIP_CONSHDLRDATA* conshdlrdata;
7854    SCIP_CONS*         maxviolcon;
7855    SCIP_Bool          solviolbounds;
7856 
7857    assert(scip != NULL);
7858    assert(conshdlr != NULL);
7859    assert(conss != NULL || nconss == 0);
7860    assert(result != NULL);
7861 
7862    *result = SCIP_DIDNOTFIND;
7863 
7864    conshdlrdata = SCIPconshdlrGetData(conshdlr);
7865    assert(conshdlrdata != NULL);
7866 
7867    SCIP_CALL( computeViolations(scip, conshdlr, conss, nconss, NULL, &solviolbounds, &maxviolcon) );
7868 
7869    /* it can happen here that the solution violates some bound - we then just don't separate, see also discussion in issue #627 */
7870    if( solviolbounds )
7871       return SCIP_OKAY;
7872 
7873    /* nothing violated -> nothing to separate */
7874    if( maxviolcon == NULL )
7875       return SCIP_OKAY;
7876 
7877    /* at root, check if we want to solve the NLP relaxation and use its solutions as reference point
7878     * if there is something convex, then linearizing in the solution of the NLP relaxation can be very useful
7879     */
7880    if( SCIPgetDepth(scip) == 0 && !conshdlrdata->sepanlp &&
7881       (SCIPgetNContVars(scip) >= conshdlrdata->sepanlpmincont * SCIPgetNVars(scip) || (SCIPgetLPSolstat(scip) == SCIP_LPSOLSTAT_UNBOUNDEDRAY && conshdlrdata->sepanlpmincont <= 1.0)) &&
7882       SCIPisNLPConstructed(scip) && SCIPgetNNlpis(scip) > 0 )
7883    {
7884       SCIP_CONSDATA* consdata;
7885       SCIP_NLPSOLSTAT solstat;
7886       SCIP_Bool solvednlp;   /* whether we invoked an NLP solve here */
7887       int c;
7888 
7889       solstat = SCIPgetNLPSolstat(scip);
7890       solvednlp = FALSE;
7891       if( solstat == SCIP_NLPSOLSTAT_UNKNOWN )
7892       {
7893          /* NLP is not solved yet, so we might want to do this
7894           * but first check whether there is a violated constraint side which corresponds to a convex function
7895           * @todo put this check into initsol and update via consenable/consdisable
7896           */
7897          for( c = 0; c < nconss; ++c )
7898          {
7899             assert(conss[c] != NULL);  /*lint !e613*/
7900 
7901             consdata = SCIPconsGetData(conss[c]);  /*lint !e613*/
7902             assert(consdata != NULL);
7903 
7904             /* skip feasible constraints */
7905             if( !SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) && !SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
7906                continue;
7907 
7908             /* make sure curvature has been checked */
7909             SCIP_CALL( checkCurvature(scip, conss[c], conshdlrdata->assumeconvex) );  /*lint !e613*/
7910 
7911             if( (SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) && (consdata->curvature & SCIP_EXPRCURV_CONVEX )) ||
7912                ( SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) && (consdata->curvature & SCIP_EXPRCURV_CONCAVE)) )
7913                break;
7914          }
7915 
7916          if( c < nconss )
7917          {
7918             /* try to solve NLP and update solstat */
7919 
7920             /* ensure linear conss are in NLP */
7921             if( conshdlrdata->subnlpheur != NULL )
7922             {
7923                SCIP_CALL( SCIPaddLinearConsToNlpHeurSubNlp(scip, conshdlrdata->subnlpheur, TRUE, TRUE) );
7924             }
7925 
7926             /* set LP solution as starting values, if available */
7927             if( SCIPgetLPSolstat(scip) == SCIP_LPSOLSTAT_OPTIMAL )
7928             {
7929                SCIP_CALL( SCIPsetNLPInitialGuessSol(scip, NULL) );
7930             }
7931 
7932             /* SCIP_CALL( SCIPsetNLPIntPar(scip, SCIP_NLPPAR_VERBLEVEL, 1) ); */
7933             SCIP_CALL( SCIPsolveNLP(scip) );
7934 
7935             solstat = SCIPgetNLPSolstat(scip);
7936             SCIPdebugMsg(scip, "solved NLP relax, solution status: %d\n", solstat);
7937 
7938             solvednlp = TRUE;
7939          }
7940       }
7941 
7942       conshdlrdata->sepanlp = TRUE;
7943 
7944       if( solstat == SCIP_NLPSOLSTAT_GLOBINFEASIBLE )
7945       {
7946          SCIPdebugMsg(scip, "NLP relaxation is globally infeasible, thus can cutoff node\n");
7947          *result = SCIP_CUTOFF;
7948          return SCIP_OKAY;
7949       }
7950 
7951       if( solstat <= SCIP_NLPSOLSTAT_FEASIBLE )
7952       {
7953          /* if we have feasible NLP solution, generate linearization cuts there */
7954          SCIP_Bool lpsolseparated;
7955          SCIP_SOL* nlpsol;
7956 
7957          SCIP_CALL( SCIPcreateNLPSol(scip, &nlpsol, NULL) );
7958          assert(nlpsol != NULL);
7959 
7960          /* if we solved the NLP and solution is integral, then pass it to trysol heuristic */
7961          if( solvednlp && conshdlrdata->trysolheur != NULL )
7962          {
7963             int nfracvars;
7964 
7965             nfracvars = 0;
7966             if( SCIPgetNBinVars(scip) > 0 || SCIPgetNIntVars(scip) > 0 )
7967             {
7968                SCIP_CALL( SCIPgetNLPFracVars(scip, NULL, NULL, NULL, &nfracvars, NULL) );
7969             }
7970 
7971             if( nfracvars == 0 )
7972             {
7973                SCIP_CALL( SCIPheurPassSolTrySol(scip, conshdlrdata->trysolheur, nlpsol) );
7974             }
7975          }
7976 
7977          SCIP_CALL( addLinearizationCuts(scip, conshdlr, conss, nconss, nlpsol, &lpsolseparated, SCIPgetSepaMinEfficacy(scip)) );
7978 
7979          SCIP_CALL( SCIPfreeSol(scip, &nlpsol) );
7980 
7981          /* if a cut that separated the LP solution was added, then return, otherwise continue with usual separation in LP solution */
7982          if( lpsolseparated )
7983          {
7984             SCIPdebugMsg(scip, "linearization cuts separate LP solution\n");
7985 
7986             *result = SCIP_SEPARATED;
7987 
7988             return SCIP_OKAY;
7989          }
7990       }
7991    }
7992    /* if we do not want to try solving the NLP, or have no NLP, or have no NLP solver, or solving the NLP failed,
7993     * or separating with NLP solution as reference point failed, then try (again) with LP solution as reference point
7994     */
7995 
7996    SCIP_CALL( separatePoint(scip, conshdlr, conss, nconss, nusefulconss, NULL, TRUE, SCIPgetSepaMinEfficacy(scip), FALSE, result, NULL) );
7997 
7998    return SCIP_OKAY;
7999 }
8000 
8001 /** separation method of constraint handler for arbitrary primal solutions */
8002 static
SCIP_DECL_CONSSEPASOL(consSepasolNonlinear)8003 SCIP_DECL_CONSSEPASOL(consSepasolNonlinear)
8004 {
8005    SCIP_CONS*         maxviolcon;
8006    SCIP_Bool          solviolbounds;
8007 
8008    assert(scip != NULL);
8009    assert(conshdlr != NULL);
8010    assert(conss != NULL || nconss == 0);
8011    assert(sol != NULL);
8012    assert(result != NULL);
8013 
8014    *result = SCIP_DIDNOTFIND;
8015 
8016    SCIP_CALL( computeViolations(scip, conshdlr, conss, nconss, sol, &solviolbounds, &maxviolcon) );
8017 
8018    /* odd, if this happens for non-LP solutions, but luckily we can just give up here */
8019    if( solviolbounds )
8020       return SCIP_OKAY;
8021 
8022    /* nothing violated -> nothing to separate */
8023    if( maxviolcon == NULL )
8024       return SCIP_OKAY;
8025 
8026    /* computeViolations already evaluated all constraints, so can pass newsol = FALSE here
8027     * in contrast to Sepalp, a sol != NULL is not projected onto the box in computeViolation
8028     */
8029    SCIP_CALL( separatePoint(scip, conshdlr, conss, nconss, nusefulconss, sol, FALSE, SCIPgetSepaMinEfficacy(scip), FALSE, result, NULL) );
8030 
8031    return SCIP_OKAY;
8032 }
8033 
8034 /** constraint enforcing method of constraint handler for LP solutions */
8035 static
SCIP_DECL_CONSENFOLP(consEnfolpNonlinear)8036 SCIP_DECL_CONSENFOLP(consEnfolpNonlinear)
8037 {  /*lint --e{715}*/
8038    SCIP_CALL( enforceConstraint(scip, conshdlr, conss, nconss, nusefulconss, NULL, solinfeasible, result) );
8039 
8040    return SCIP_OKAY;
8041 }
8042 
8043 /** constraint enforcing method of constraint handler for relaxation solutions */
8044 static
SCIP_DECL_CONSENFORELAX(consEnforelaxNonlinear)8045 SCIP_DECL_CONSENFORELAX(consEnforelaxNonlinear)
8046 {  /*lint --e{715}*/
8047    SCIP_CALL( enforceConstraint(scip, conshdlr, conss, nconss, nusefulconss, sol, solinfeasible, result) );
8048 
8049    return SCIP_OKAY;
8050 }
8051 
8052 /** constraint enforcing method of constraint handler for pseudo solutions */
8053 static
SCIP_DECL_CONSENFOPS(consEnfopsNonlinear)8054 SCIP_DECL_CONSENFOPS(consEnfopsNonlinear)
8055 {
8056    SCIP_CONS*         maxviolcons;
8057    SCIP_CONSDATA*     consdata;
8058    SCIP_RESULT        propresult;
8059    SCIP_VAR*          var;
8060    int                dummy;
8061    int                nnotify;
8062    int                c;
8063    int                i;
8064    int                j;
8065    SCIP_Bool          solviolbounds = FALSE;
8066 
8067    assert(scip != NULL);
8068    assert(conss != NULL || nconss == 0);
8069 
8070    SCIP_CALL( computeViolations(scip, conshdlr, conss, nconss, NULL, &solviolbounds, &maxviolcons) );
8071 
8072    /* we enforce a pseudo-solution, which should be within (read: at) bounds by definition */
8073    assert(!solviolbounds);
8074 
8075    if( maxviolcons == NULL )
8076    {
8077       *result = SCIP_FEASIBLE;
8078       return SCIP_OKAY;
8079    }
8080 
8081    *result = SCIP_INFEASIBLE;
8082 
8083    SCIPdebugMsg(scip, "enfops with max violation in cons <%s>\n", SCIPconsGetName(maxviolcons));
8084 
8085    /* we propagate constraints only if they are active and enforcing by branching only does not seem much effective */
8086    assert(SCIPconsIsActive(maxviolcons));
8087 
8088    /* run domain propagation */
8089    dummy = 0;
8090    SCIP_CALL( propagateBounds(scip, conshdlr, conss, nconss, TRUE, &propresult, &dummy, &dummy) );
8091    if( propresult == SCIP_CUTOFF || propresult == SCIP_REDUCEDDOM )
8092    {
8093       *result = propresult;
8094       return SCIP_OKAY;
8095    }
8096 
8097    /* We are not feasible and we cannot prove that the whole node is infeasible -> collect all variables in violated
8098     * constraints for branching. */
8099    nnotify = 0;
8100    for( c = 0; c < nconss; ++c )
8101    {
8102       assert(conss != NULL);
8103       consdata = SCIPconsGetData(conss[c]);
8104       assert(consdata != NULL);
8105 
8106       if( !SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) && !SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
8107          continue;
8108 
8109       for( i = 0; i < consdata->nlinvars; ++i )
8110       {
8111          var = consdata->linvars[i];
8112          if( !SCIPisRelEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) )
8113          {
8114             SCIP_CALL( SCIPaddExternBranchCand(scip, var, MAX(consdata->lhsviol, consdata->rhsviol), SCIP_INVALID) );
8115             ++nnotify;
8116          }
8117       }
8118 
8119       for( j = 0; j < consdata->nexprtrees; ++j )
8120       {
8121          for( i = 0; i < SCIPexprtreeGetNVars(consdata->exprtrees[j]); ++i )
8122          {
8123             var = SCIPexprtreeGetVars(consdata->exprtrees[j])[i];
8124             if( !SCIPisRelEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) )
8125             {
8126                SCIP_CALL( SCIPaddExternBranchCand(scip, var, MAX(consdata->lhsviol, consdata->rhsviol), SCIP_INVALID) );
8127                ++nnotify;
8128             }
8129          }
8130       }
8131    }
8132 
8133    if( nnotify == 0 )
8134    {
8135       SCIPdebugMsg(scip, "All variables in violated constraints fixed (up to epsilon). Cannot find branching candidate. Forcing solution of LP.\n");
8136       *result = SCIP_SOLVELP;
8137    }
8138 
8139    assert(*result == SCIP_SOLVELP || (*result == SCIP_INFEASIBLE && nnotify > 0));
8140    return SCIP_OKAY;
8141 }  /*lint !e715*/
8142 
8143 
8144 /** feasibility check method of constraint handler for integral solutions */
8145 static
SCIP_DECL_CONSCHECK(consCheckNonlinear)8146 SCIP_DECL_CONSCHECK(consCheckNonlinear)
8147 {
8148    SCIP_CONSHDLRDATA* conshdlrdata;
8149    SCIP_CONSDATA*     consdata;
8150    SCIP_Real          maxviol;
8151    int                c;
8152    SCIP_Bool          maypropfeasible; /* whether we may be able to propose a feasible solution */
8153    SCIP_Bool          solviolbounds;
8154 
8155    assert(scip != NULL);
8156    assert(conss != NULL || nconss == 0);
8157    assert(result != NULL);
8158 
8159    conshdlrdata = SCIPconshdlrGetData(conshdlr);
8160    assert(conshdlrdata != NULL);
8161 
8162    *result = SCIP_FEASIBLE;
8163 
8164    /* during presolve, we do not have exprtrees in the constraints, but we can get values from the expression graph, if we have evaluated it */
8165    if( SCIPgetStage(scip) >= SCIP_STAGE_INITPRESOLVE && SCIPgetStage(scip) <= SCIP_STAGE_EXITPRESOLVE )
8166    {
8167       SCIP_Real* varvals;
8168 
8169       assert(conshdlrdata->exprgraph != NULL);
8170 
8171       SCIP_CALL( SCIPallocBufferArray(scip, &varvals, SCIPexprgraphGetNVars(conshdlrdata->exprgraph)) );
8172       SCIP_CALL( SCIPgetSolVals(scip, sol, SCIPexprgraphGetNVars(conshdlrdata->exprgraph), (SCIP_VAR**)SCIPexprgraphGetVars(conshdlrdata->exprgraph), varvals) );
8173 
8174       SCIP_CALL( SCIPexprgraphEval(conshdlrdata->exprgraph, varvals) );
8175 
8176       SCIPfreeBufferArray(scip, &varvals);
8177    }
8178 
8179    /* @todo adapt proposeFeasibleSolution to function also during presolving */
8180    maxviol = 0.0;
8181    maypropfeasible = conshdlrdata->linfeasshift && (conshdlrdata->trysolheur != NULL) &&
8182       SCIPgetStage(scip) >= SCIP_STAGE_TRANSFORMED &&
8183       (SCIPgetStage(scip) < SCIP_STAGE_INITPRESOLVE || SCIPgetStage(scip) > SCIP_STAGE_EXITPRESOLVE) &&
8184       SCIPgetStage(scip) <= SCIP_STAGE_SOLVING;
8185 
8186    for( c = 0; c < nconss; ++c )
8187    {
8188       assert(conss != NULL);
8189       SCIP_CALL( computeViolation(scip, conshdlr, conss[c], sol, &solviolbounds) );
8190       assert(!solviolbounds);  /* see also issue #627 */
8191 
8192       consdata = SCIPconsGetData(conss[c]);
8193       assert(consdata != NULL);
8194 
8195       if( SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) || SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
8196       {
8197          *result = SCIP_INFEASIBLE;
8198          if( printreason )
8199          {
8200             SCIP_CALL( SCIPprintCons(scip, conss[c], NULL) );
8201             SCIPinfoMessage(scip, NULL, ";\n");
8202             if( SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) )
8203             {
8204                SCIPinfoMessage(scip, NULL, "violation: left hand side is violated by %.15g\n", consdata->lhsviol);
8205             }
8206             if( SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
8207             {
8208                SCIPinfoMessage(scip, NULL, "violation: right hand side is violated by %.15g\n", consdata->rhsviol);
8209             }
8210          }
8211 
8212          if( (conshdlrdata->subnlpheur == NULL || sol == NULL) && !maypropfeasible && !completely )
8213             return SCIP_OKAY;
8214 
8215          if( consdata->lhsviol > maxviol || consdata->rhsviol > maxviol )
8216             maxviol = MAX(consdata->lhsviol, consdata->rhsviol);
8217 
8218          /* do not try to shift linear variables if activity is at infinity (leads to setting variable to infinity in solution, which is not allowed) */
8219          if( maypropfeasible && SCIPisInfinity(scip, REALABS(consdata->activity)) )
8220             maypropfeasible = FALSE;
8221 
8222          if( maypropfeasible )
8223          {
8224             /* update information on linear variables that may be in- or decreased */
8225             if( SCIPgetStage(scip) != SCIP_STAGE_SOLVING )
8226                consdataFindUnlockedLinearVar(scip, consdata);
8227 
8228             if( SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) )
8229             {
8230                /* check if there is a variable which may help to get the left hand side satisfied
8231                 * if there is no such var, then we cannot get feasible */
8232                if( !(consdata->linvar_mayincrease >= 0 && consdata->lincoefs[consdata->linvar_mayincrease] > 0.0) &&
8233                   ! (consdata->linvar_maydecrease >= 0 && consdata->lincoefs[consdata->linvar_maydecrease] < 0.0) )
8234                   maypropfeasible = FALSE;
8235             }
8236             else
8237             {
8238                assert(SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)));
8239                /* check if there is a variable which may help to get the right hand side satisfied
8240                 * if there is no such var, then we cannot get feasible */
8241                if( !(consdata->linvar_mayincrease >= 0 && consdata->lincoefs[consdata->linvar_mayincrease] < 0.0) &&
8242                   ! (consdata->linvar_maydecrease >= 0 && consdata->lincoefs[consdata->linvar_maydecrease] > 0.0) )
8243                   maypropfeasible = FALSE;
8244             }
8245          }
8246       }
8247       else
8248       {
8249          /* SCIPdebugMsg(scip, "constraint <%s> is feasible (%g, %g) in check, activity = %g, sides = [%g, %g]\n", SCIPconsGetName(conss[c]), consdata->lhsviol, consdata->rhsviol, consdata->activity, consdata->lhs, consdata->rhs); */
8250       }
8251    }
8252 
8253    if( *result == SCIP_INFEASIBLE && maypropfeasible )
8254    {
8255       SCIP_Bool success;
8256 
8257       SCIP_CALL( proposeFeasibleSolution(scip, conshdlr, conss, nconss, sol, &success) );
8258 
8259       /* do not pass solution to NLP heuristic if we made it feasible this way */
8260       if( success )
8261          return SCIP_OKAY;
8262    }
8263 
8264    if( *result == SCIP_INFEASIBLE && conshdlrdata->subnlpheur != NULL && sol != NULL && !SCIPisInfinity(scip, maxviol) )
8265    {
8266       SCIP_CALL( SCIPupdateStartpointHeurSubNlp(scip, conshdlrdata->subnlpheur, sol, maxviol) );
8267    }
8268 
8269    return SCIP_OKAY;
8270 }  /*lint !e715*/
8271 
8272 
8273 /** domain propagation method of constraint handler */
8274 static
SCIP_DECL_CONSPROP(consPropNonlinear)8275 SCIP_DECL_CONSPROP(consPropNonlinear)
8276 {
8277    int dummy;
8278 
8279    assert(scip != NULL);
8280    assert(conshdlr != NULL);
8281    assert(conss != NULL || nconss == 0);
8282    assert(result != NULL);
8283 
8284    dummy = 0;
8285    SCIP_CALL( propagateBounds(scip, conshdlr, conss, nmarkedconss, TRUE, result, &dummy, &dummy) );
8286 
8287    return SCIP_OKAY;
8288 }  /*lint !e715*/
8289 
8290 /** presolving method of constraint handler */
8291 static
SCIP_DECL_CONSPRESOL(consPresolNonlinear)8292 SCIP_DECL_CONSPRESOL(consPresolNonlinear)
8293 {
8294    SCIP_CONSHDLRDATA* conshdlrdata;
8295    SCIP_CONSDATA*     consdata;
8296    SCIP_RESULT        propresult;
8297    SCIP_Bool          havechange;
8298    SCIP_Bool          domainerror;
8299    SCIP_Bool          havegraphchange;
8300    SCIP_Bool          tryupgrades;
8301    int                c;
8302 
8303    assert(scip     != NULL);
8304    assert(conshdlr != NULL);
8305    assert(conss    != NULL || nconss == 0);
8306    assert(result   != NULL);
8307 
8308    *result = SCIP_DIDNOTFIND;
8309 
8310    conshdlrdata = SCIPconshdlrGetData(conshdlr);
8311    assert(conshdlrdata != NULL);
8312    assert(conshdlrdata->exprgraph != NULL);
8313 
8314    havegraphchange = FALSE;
8315 
8316    if( !conshdlrdata->isremovedfixings )
8317    {
8318       SCIP_CALL( removeFixedNonlinearVariables(scip, conshdlr) );
8319       assert(conshdlrdata->isremovedfixings);
8320 
8321       havegraphchange = TRUE;
8322    }
8323 
8324    SCIP_CALL( SCIPexprgraphSimplify(conshdlrdata->exprgraph, SCIPgetMessagehdlr(scip), SCIPepsilon(scip), conshdlrdata->maxexpansionexponent, &havechange, &domainerror) );
8325    SCIPdebugMsg(scip, "expression graph simplifier found %schange, domain error = %u\n", havechange ? "" : "no ", domainerror);
8326 
8327    /* if simplifier found some undefined expression, then declare problem as infeasible
8328     * usually, this should be discovered during domain propagation already, but since that is using interval arithmetics,
8329     *   it may overestimate in a way that actually undefined expressions still get a value assigned (e.g., 0^(-1) = [-inf,inf])
8330     */
8331    if( domainerror )
8332       *result = SCIP_CUTOFF;
8333 
8334    havegraphchange |= havechange;
8335 
8336    /* if graph has changed, then we will try upgrades, otherwise we only do for changing or not-yet-presolved constraints */
8337    tryupgrades = havegraphchange;
8338 
8339    /* remove fixed vars, do some algebraic manipulation, etc; this loop needs to finish, even if a cutoff is found, because data
8340     * might be inconsistent otherwise (i.e. some asserts might pop later, e.g. exitpresol, etc) */
8341    for( c = 0; c < nconss; ++c )
8342    {
8343       assert(conss != NULL);
8344 
8345       consdata = SCIPconsGetData(conss[c]);
8346       assert(consdata != NULL);
8347 
8348       SCIPdebugMsg(scip, "process constraint <%s>\n", SCIPconsGetName(conss[c]));
8349       SCIPdebugPrintCons(scip, conss[c], NULL);
8350 
8351       havechange = FALSE;
8352 
8353       if( !consdata->isremovedfixingslin )
8354       {
8355          SCIP_CALL( removeFixedLinearVariables(scip, conss[c]) );
8356          assert(consdata->isremovedfixingslin);
8357          havechange = TRUE;
8358       }
8359 
8360       /* the reductions below require the constraint nonlinear function to be in the expression graph, which is only the
8361        * case for active constraints
8362        */
8363       if( !SCIPconsIsActive(conss[c]) )
8364          continue;
8365 
8366       if( !consdata->ispresolved || havegraphchange )
8367       {
8368          SCIP_Bool infeasible;
8369 
8370          SCIP_CALL( splitOffLinearPart(scip, conshdlr, conss[c], &infeasible) );
8371 
8372          if( infeasible )
8373          {
8374             *result = SCIP_CUTOFF;
8375             continue;
8376          }
8377       }
8378 
8379       if( consdata->nlinvars == 0 && consdata->exprgraphnode == NULL )
8380       {
8381          /* all variables fixed or removed, constraint function is 0.0 now */
8382          if( (!SCIPisInfinity(scip, -consdata->lhs) && SCIPisFeasPositive(scip, consdata->lhs)) ||
8383             ( !SCIPisInfinity(scip,  consdata->rhs) && SCIPisFeasNegative(scip, consdata->rhs)) )
8384          {
8385             /* left hand side positive or right hand side negative */
8386             SCIPdebugMsg(scip, "constraint <%s> is constant and infeasible\n", SCIPconsGetName(conss[c]));
8387             SCIP_CALL( SCIPdelCons(scip, conss[c]) );
8388             *result = SCIP_CUTOFF;
8389          }
8390          else
8391          {
8392             /* left and right hand side are consistent */
8393             SCIPdebugMsg(scip, "constraint <%s> is constant and feasible, deleting\n", SCIPconsGetName(conss[c]));
8394             SCIP_CALL( SCIPdelCons(scip, conss[c]) );
8395             ++*ndelconss;
8396 
8397             if( *result != SCIP_CUTOFF )
8398                *result = SCIP_SUCCESS;
8399             continue;
8400          }
8401       }
8402 
8403       /* remember that we want to call upgrade methods for the current constraint */
8404       if( havechange )
8405          consdata->ispresolved = FALSE;
8406 
8407       /* if a constraint is not finished presolving yet, then we will try upgrade methods */
8408       if( !consdata->ispresolved )
8409          tryupgrades = TRUE;
8410    }
8411 
8412    /* if a cutoff was found, return; data is consistent at this point */
8413    if( *result == SCIP_CUTOFF )
8414       return SCIP_OKAY;
8415 
8416    if( tryupgrades )
8417    {
8418       /* upgrade methods may look at expression graph bounds, which are not present in the first presolving round yet and may be invalid in later rounds (e.g., due to probing) */
8419       SCIP_CALL( SCIPexprgraphPropagateVarBounds(conshdlrdata->exprgraph, INTERVALINFTY, TRUE, &domainerror) );
8420 
8421       if( domainerror )
8422       {
8423          SCIPdebugMsg(scip, "propagating variable bounds through expression graph found that some expressions cannot be evaluated w.r.t. current bounds, thus cutoff\n");
8424          *result = SCIP_CUTOFF;
8425          return SCIP_OKAY;
8426       }
8427 
8428       for( c = 0; c < nconss; ++c )
8429       {
8430          consdata = SCIPconsGetData(conss[c]);  /*lint !e794*/
8431          assert(consdata != NULL);
8432 
8433          /* call upgrade methods if constraint was not presolved, has been changed, or the expression graph has changed */
8434          if( !consdata->ispresolved || havegraphchange )
8435          {
8436             SCIP_Bool upgraded;
8437 
8438             SCIP_CALL( presolveUpgrade(scip, conshdlr, conss[c], &upgraded, nupgdconss, naddconss) );  /*lint !e794*/
8439             if( upgraded )
8440             {
8441                *result = SCIP_SUCCESS;
8442                continue;
8443             }
8444          }
8445 
8446          consdata->ispresolved = TRUE;
8447       }
8448    }
8449 
8450    /* run domain propagation (if updated bounds in graph above, then can skip cleanup) */
8451    if( (presoltiming & SCIP_PRESOLTIMING_FAST) != 0 )
8452    {
8453       SCIP_CALL( propagateBounds(scip, conshdlr, conss, nconss, !tryupgrades, &propresult, nchgbds, ndelconss) );
8454       switch( propresult )
8455       {
8456       case SCIP_REDUCEDDOM:
8457          *result = SCIP_SUCCESS;
8458          break;
8459       case SCIP_CUTOFF:
8460          SCIPdebugMsg(scip, "propagation says problem is infeasible in presolve\n");
8461          *result = SCIP_CUTOFF;
8462          return SCIP_OKAY;
8463       default:
8464          assert(propresult == SCIP_DIDNOTFIND || propresult == SCIP_DIDNOTRUN);
8465       }  /*lint !e788*/
8466    }
8467 
8468    if( conshdlrdata->reformulate && !conshdlrdata->assumeconvex )
8469    {
8470       /* if other presolvers did not find enough changes for another presolving round,
8471        * then try the reformulations (replacing products with binaries, disaggregation, setting default variable bounds)
8472        * otherwise, we wait with these
8473        */
8474       if( SCIPisPresolveFinished(scip) || (presoltiming & SCIP_PRESOLTIMING_EXHAUSTIVE) != 0 )
8475       {
8476          int naddconssbefore;
8477 
8478          SCIPdebugMsg(scip, "reformulating expression graph\n");
8479 
8480          naddconssbefore = conshdlrdata->naddedreformconss;
8481          SCIP_CALL( reformulate(scip, conshdlr, conss, nconss, &conshdlrdata->naddedreformconss) );
8482 
8483          if( conshdlrdata->naddedreformconss > naddconssbefore )
8484          {
8485             *result = SCIP_SUCCESS;
8486             *naddconss += conshdlrdata->naddedreformconss - naddconssbefore;
8487 
8488             /* if expression graph changed, ensure that we apply all presolving techniques (esp. upgrades) in next round again */
8489             for( c = 0; c < nconss; ++c )
8490             {
8491                assert(conss[c] != NULL);  /*lint !e794*/
8492 
8493                consdata = SCIPconsGetData(conss[c]);  /*lint !e794*/
8494                assert(consdata != NULL);
8495 
8496                consdata->ispresolved = FALSE;
8497             }
8498          }
8499       }
8500    }
8501 
8502    return SCIP_OKAY;
8503 }  /*lint !e715*/
8504 
8505 
8506 /** variable rounding lock method of constraint handler */
8507 static
SCIP_DECL_CONSLOCK(consLockNonlinear)8508 SCIP_DECL_CONSLOCK(consLockNonlinear)
8509 {
8510    SCIP_CONSDATA* consdata;
8511    SCIP_Bool      havelhs;
8512    SCIP_Bool      haverhs;
8513    int            i;
8514 
8515    assert(scip != NULL);
8516    assert(cons != NULL);
8517    assert(locktype == SCIP_LOCKTYPE_MODEL);
8518 
8519    /* variable locking for nonlinear part is done w.r.t. variables in the expression graph
8520     * since only active constraints have their nonlinear part in the expression graph, we can lock only active constraints
8521     */
8522    assert(SCIPconsIsActive(cons) || SCIPconsIsDeleted(cons));
8523 
8524    consdata = SCIPconsGetData(cons);
8525    assert(consdata != NULL);
8526 
8527    havelhs = !SCIPisInfinity(scip, -consdata->lhs);
8528    haverhs = !SCIPisInfinity(scip,  consdata->rhs);
8529 
8530    for( i = 0; i < consdata->nlinvars; ++i )
8531    {
8532       if( consdata->lincoefs[i] > 0 )
8533       {
8534          if( havelhs )
8535          {
8536             SCIP_CALL( SCIPaddVarLocksType(scip, consdata->linvars[i], locktype, nlockspos, nlocksneg) );
8537          }
8538          if( haverhs )
8539          {
8540             SCIP_CALL( SCIPaddVarLocksType(scip, consdata->linvars[i], locktype, nlocksneg, nlockspos) );
8541          }
8542       }
8543       else
8544       {
8545          if( havelhs )
8546          {
8547             SCIP_CALL( SCIPaddVarLocksType(scip, consdata->linvars[i], locktype, nlocksneg, nlockspos) );
8548          }
8549          if( haverhs )
8550          {
8551             SCIP_CALL( SCIPaddVarLocksType(scip, consdata->linvars[i], locktype, nlockspos, nlocksneg) );
8552          }
8553       }
8554    }
8555 
8556    return SCIP_OKAY;
8557 }  /*lint !e715*/
8558 
8559 /** constraint activation notification method of constraint handler */
8560 static
SCIP_DECL_CONSACTIVE(consActiveNonlinear)8561 SCIP_DECL_CONSACTIVE(consActiveNonlinear)
8562 {  /*lint --e{715}*/
8563    SCIP_CONSHDLRDATA* conshdlrdata;
8564    SCIP_CONSDATA* consdata;
8565 
8566    assert(scip != NULL);
8567    assert(conshdlr != NULL);
8568    assert(cons != NULL);
8569    assert(SCIPconsIsTransformed(cons));
8570 
8571    conshdlrdata = SCIPconshdlrGetData(conshdlr);
8572    assert(conshdlrdata != NULL);
8573    assert(conshdlrdata->exprgraph != NULL);
8574 
8575    consdata = SCIPconsGetData(cons);
8576    assert(consdata != NULL);
8577 
8578    SCIPdebugMsg(scip, "activate cons <%s>\n", SCIPconsGetName(cons));
8579 
8580    if( consdata->nexprtrees > 0 )
8581    {
8582       SCIP_Bool exprtreeisnew;
8583 
8584       assert(consdata->exprgraphnode == NULL);
8585 
8586       /* add exprtrees to expression graph */
8587       SCIP_CALL( SCIPexprgraphAddExprtreeSum(conshdlrdata->exprgraph, consdata->nexprtrees, consdata->exprtrees, consdata->nonlincoefs, &consdata->exprgraphnode, &exprtreeisnew) );
8588       assert(consdata->exprgraphnode != NULL);
8589       /* @todo do something with exprtreeisnew? */
8590 
8591       /* if during presolving, then forget expression trees */
8592       if( SCIPgetStage(scip) >= SCIP_STAGE_INITPRESOLVE && SCIPgetStage(scip) < SCIP_STAGE_EXITPRESOLVE )
8593       {
8594          SCIP_CALL( consdataSetExprtrees(scip, consdata, 0, NULL, NULL, FALSE) );
8595       }
8596 
8597       /* remember that we should run reformulation again */
8598       conshdlrdata->isreformulated = FALSE;
8599 
8600       /* remember that we should force backward propagation on our subgraph propagating the next time,
8601        * so possible domain restrictions are propagated into variable bounds
8602        */
8603       consdata->forcebackprop = TRUE;
8604    }
8605    else if( consdata->exprgraphnode != NULL )
8606    {
8607       /* if constraint already comes with node in expression graph, then also remember that we should run reformulation again */
8608       conshdlrdata->isreformulated = FALSE;
8609 
8610       /* remember that we should force backward propagation on our subgraph propagating the next time,
8611        * so possible domain restrictions are propagated into variable bounds
8612        */
8613       consdata->forcebackprop = TRUE;
8614    }
8615 
8616    return SCIP_OKAY;
8617 }
8618 
8619 /** constraint deactivation notification method of constraint handler */
8620 static
SCIP_DECL_CONSDEACTIVE(consDeactiveNonlinear)8621 SCIP_DECL_CONSDEACTIVE(consDeactiveNonlinear)
8622 {  /*lint --e{715}*/
8623    SCIP_CONSHDLRDATA* conshdlrdata;
8624    SCIP_CONSDATA* consdata;
8625 
8626    assert(scip != NULL);
8627    assert(conshdlr != NULL);
8628    assert(cons != NULL);
8629    assert(SCIPconsIsTransformed(cons));
8630 
8631    conshdlrdata = SCIPconshdlrGetData(conshdlr);
8632    assert(conshdlrdata != NULL);
8633    assert(conshdlrdata->exprgraph != NULL);
8634 
8635    consdata = SCIPconsGetData(cons);
8636    assert(consdata != NULL);
8637    assert(consdata->exprgraphnode != NULL || consdata->nexprtrees == 0);
8638 
8639    SCIPdebugMsg(scip, "deactivate cons <%s>\n", SCIPconsGetName(cons));
8640 
8641    if( consdata->exprgraphnode != NULL )
8642    {
8643       if( consdata->nexprtrees == 0 )
8644       {
8645          /* during presolving, the exprtrees in the constraint are removed, so put them back before releasing the exprgraphnode */
8646          SCIP_EXPRTREE* exprtree;
8647 
8648          /* if only presolve is run and problem is found infeasible there, then constraints may not be deactivated there, but in a later call to freeTransform */
8649          /* @todo if infeasible in presolve, will constraints be deactivated still in presolving stage, or in exitpre? */
8650          assert(SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING || SCIPgetStage(scip) <= SCIP_STAGE_EXITPRESOLVE || SCIPgetStage(scip) == SCIP_STAGE_FREETRANS);
8651 
8652          SCIP_CALL( SCIPexprgraphGetTree(conshdlrdata->exprgraph, consdata->exprgraphnode, &exprtree) );
8653          SCIP_CALL( consdataSetExprtrees(scip, consdata, 1, &exprtree, NULL, FALSE) );
8654       }
8655 
8656       SCIP_CALL( SCIPexprgraphReleaseNode(conshdlrdata->exprgraph, &consdata->exprgraphnode) );
8657    }
8658 
8659    return SCIP_OKAY;
8660 }
8661 
8662 /** constraint enabling notification method of constraint handler */
8663 static
SCIP_DECL_CONSENABLE(consEnableNonlinear)8664 SCIP_DECL_CONSENABLE(consEnableNonlinear)
8665 {  /*lint --e{715}*/
8666    SCIP_CONSHDLRDATA* conshdlrdata;
8667    SCIP_CONSDATA* consdata;
8668    int i;
8669 
8670    assert(scip != NULL);
8671    assert(conshdlr != NULL);
8672    assert(cons != NULL);
8673    assert(SCIPconsIsTransformed(cons));
8674    assert(SCIPconsIsActive(cons));
8675 
8676    conshdlrdata = SCIPconshdlrGetData(conshdlr);
8677    assert(conshdlrdata != NULL);
8678    assert(conshdlrdata->exprgraph != NULL);
8679 
8680    consdata = SCIPconsGetData(cons);
8681    assert(consdata != NULL);
8682 
8683    SCIPdebugMsg(scip, "enable cons <%s>\n", SCIPconsGetName(cons));
8684 
8685    if( consdata->exprgraphnode != NULL )
8686    {
8687       /* enable node of expression in expression graph */
8688       SCIPexprgraphEnableNode(conshdlrdata->exprgraph, consdata->exprgraphnode);
8689    }
8690 
8691    /* enable event catching for linear variables */
8692    consdata->isremovedfixingslin = TRUE;
8693    for( i = 0; i < consdata->nlinvars; ++i )
8694    {
8695       SCIP_CALL( catchLinearVarEvents(scip, cons, i) );
8696 
8697       consdata->isremovedfixingslin = consdata->isremovedfixingslin && SCIPvarIsActive(consdata->linvars[i]);
8698    }
8699 
8700    return SCIP_OKAY;
8701 }
8702 
8703 /** constraint disabling notification method of constraint handler */
8704 static
SCIP_DECL_CONSDISABLE(consDisableNonlinear)8705 SCIP_DECL_CONSDISABLE(consDisableNonlinear)
8706 {  /*lint --e{715}*/
8707    SCIP_CONSHDLRDATA* conshdlrdata;
8708    SCIP_CONSDATA* consdata;
8709    int i;
8710 
8711    assert(scip != NULL);
8712    assert(conshdlr != NULL);
8713    assert(cons != NULL);
8714    assert(SCIPconsIsTransformed(cons));
8715 
8716    conshdlrdata = SCIPconshdlrGetData(conshdlr);
8717    assert(conshdlrdata != NULL);
8718    assert(conshdlrdata->exprgraph != NULL);
8719 
8720    consdata = SCIPconsGetData(cons);
8721    assert(consdata != NULL);
8722    assert(consdata->lineventdata != NULL || consdata->nlinvars == 0);
8723 
8724    SCIPdebugMsg(scip, "disable cons <%s>\n", SCIPconsGetName(cons));
8725 
8726    /* disable node of expression in expression graph */
8727    if( consdata->exprgraphnode != NULL )
8728    {
8729       SCIPexprgraphDisableNode(conshdlrdata->exprgraph, consdata->exprgraphnode);
8730    }
8731 
8732    for( i = 0; i < consdata->nlinvars; ++i )
8733    {
8734       SCIP_CALL( dropLinearVarEvents(scip, cons, i) );
8735    }
8736 
8737    return SCIP_OKAY;
8738 }
8739 
8740 
8741 /** constraint display method of constraint handler */
8742 static
SCIP_DECL_CONSPRINT(consPrintNonlinear)8743 SCIP_DECL_CONSPRINT(consPrintNonlinear)
8744 {
8745    SCIP_CONSDATA* consdata;
8746    int            j;
8747 
8748    assert(scip != NULL);
8749    assert(cons != NULL);
8750 
8751    consdata = SCIPconsGetData(cons);
8752    assert(consdata != NULL);
8753 
8754    /* print left hand side for ranged rows */
8755    if( !SCIPisInfinity(scip, -consdata->lhs)
8756       && !SCIPisInfinity(scip, consdata->rhs)
8757       && !SCIPisEQ(scip, consdata->lhs, consdata->rhs) )
8758       SCIPinfoMessage(scip, file, "%.15g <= ", consdata->lhs);
8759 
8760    /* print coefficients and variables */
8761    if( consdata->nlinvars == 0 && consdata->nexprtrees == 0 && consdata->exprgraphnode == 0 )
8762    {
8763       SCIPinfoMessage(scip, file, "0 ");
8764    }
8765    else
8766    {
8767       if( consdata->nexprtrees > 0 )
8768       {
8769          for( j = 0; j < consdata->nexprtrees; ++j )
8770          {
8771             if( j > 0 || consdata->nonlincoefs[j] != 1.0 )
8772                SCIPinfoMessage(scip, file, " %+.15g ", consdata->nonlincoefs[j]);
8773             SCIP_CALL( SCIPexprtreePrintWithNames(consdata->exprtrees[j], SCIPgetMessagehdlr(scip), file) );
8774          }
8775       }
8776       else if( consdata->exprgraphnode != NULL )
8777       {
8778          SCIP_CONSHDLRDATA* conshdlrdata;
8779          SCIP_EXPRTREE* tree;
8780 
8781          conshdlrdata = SCIPconshdlrGetData(conshdlr);
8782          assert(conshdlrdata != NULL);
8783          SCIP_CALL( SCIPexprgraphGetTree(conshdlrdata->exprgraph, consdata->exprgraphnode, &tree) );
8784 
8785          SCIP_CALL( SCIPexprtreePrintWithNames(tree, SCIPgetMessagehdlr(scip), file) );
8786 
8787          SCIP_CALL( SCIPexprtreeFree(&tree) );
8788       }
8789 
8790       for( j = 0; j < consdata->nlinvars; ++j )
8791       {
8792          SCIPinfoMessage(scip, file, " %+.15g <%s>[%c] ", consdata->lincoefs[j], SCIPvarGetName(consdata->linvars[j]),
8793             SCIPvarGetType(consdata->linvars[j]) == SCIP_VARTYPE_BINARY ? 'B' :
8794             SCIPvarGetType(consdata->linvars[j]) == SCIP_VARTYPE_INTEGER ? 'I' :
8795             SCIPvarGetType(consdata->linvars[j]) == SCIP_VARTYPE_IMPLINT ? 'I' : 'C');
8796       }
8797    }
8798 
8799    /* print right hand side */
8800    if( SCIPisEQ(scip, consdata->lhs, consdata->rhs) )
8801    {
8802       SCIPinfoMessage(scip, file, " == %.15g", consdata->rhs);
8803    }
8804    else if( !SCIPisInfinity(scip, consdata->rhs) )
8805    {
8806       SCIPinfoMessage(scip, file, " <= %.15g", consdata->rhs);
8807    }
8808    else if( !SCIPisInfinity(scip, -consdata->lhs) )
8809    {
8810       SCIPinfoMessage(scip, file, " >= %.15g", consdata->lhs);
8811    }
8812    else
8813    {
8814       SCIPinfoMessage(scip, file, " [free]");
8815    }
8816 
8817    return SCIP_OKAY;
8818 }
8819 
8820 /** constraint copying method of constraint handler */
8821 static
SCIP_DECL_CONSCOPY(consCopyNonlinear)8822 SCIP_DECL_CONSCOPY(consCopyNonlinear)
8823 {
8824    SCIP_CONSDATA*    consdata;
8825    SCIP_CONSDATA*    targetconsdata;
8826    SCIP_VAR**        linvars;
8827    SCIP_Real*        nonlincoefs;
8828    SCIP_EXPRTREE**   exprtrees;
8829    int               nexprtrees;
8830    int i;
8831    int j;
8832 
8833    assert(scip != NULL);
8834    assert(cons != NULL);
8835    assert(sourcescip != NULL);
8836    assert(sourceconshdlr != NULL);
8837    assert(sourcecons != NULL);
8838    assert(varmap != NULL);
8839    assert(valid != NULL);
8840 
8841    consdata = SCIPconsGetData(sourcecons);
8842    assert(consdata != NULL);
8843 
8844    linvars = NULL;
8845    exprtrees = NULL;
8846 
8847    *valid = TRUE;
8848 
8849    if( consdata->nlinvars != 0 )
8850    {
8851       SCIP_CALL( SCIPallocBufferArray(sourcescip, &linvars, consdata->nlinvars) );
8852       for( i = 0; i < consdata->nlinvars && *valid; ++i )
8853       {
8854          SCIP_CALL( SCIPgetVarCopy(sourcescip, scip, consdata->linvars[i], &linvars[i], varmap, consmap, global, valid) );
8855          assert(!*valid || linvars[i] != NULL);
8856       }
8857    }
8858 
8859    nexprtrees = 0;
8860    nonlincoefs = NULL;
8861 
8862    if( *valid && consdata->nexprtrees > 0 )
8863    {
8864       SCIP_VAR** nonlinvars;
8865 
8866       nonlincoefs = consdata->nonlincoefs;
8867       nexprtrees = consdata->nexprtrees;
8868 
8869       SCIP_CALL( SCIPallocBufferArray(sourcescip, &exprtrees, nexprtrees) );
8870       BMSclearMemoryArray(exprtrees, nexprtrees);
8871       SCIP_CALL( SCIPallocBufferArray(sourcescip, &nonlinvars, SCIPexprtreeGetNVars(consdata->exprtrees[0])) );
8872 
8873       for( j = 0; j < consdata->nexprtrees; ++j )
8874       {
8875          SCIP_CALL( SCIPreallocBufferArray(sourcescip, &nonlinvars, SCIPexprtreeGetNVars(consdata->exprtrees[j])) );
8876          for( i = 0; i < SCIPexprtreeGetNVars(consdata->exprtrees[j]) && *valid; ++i )
8877          {
8878             SCIP_CALL( SCIPgetVarCopy(sourcescip, scip, SCIPexprtreeGetVars(consdata->exprtrees[j])[i], &nonlinvars[i], varmap, consmap, global, valid) );
8879             assert(!*valid || nonlinvars[i] != NULL);
8880          }
8881 
8882          if( *valid )
8883          {
8884             SCIP_CALL( SCIPexprtreeCopy(SCIPblkmem(scip), &exprtrees[j], consdata->exprtrees[j]) );
8885             SCIP_CALL( SCIPexprtreeSetVars(exprtrees[j], SCIPexprtreeGetNVars(consdata->exprtrees[j]), nonlinvars) );
8886          }
8887          else
8888             break;
8889       }
8890 
8891       SCIPfreeBufferArray(sourcescip, &nonlinvars);
8892    }
8893 
8894    if( *valid && consdata->nexprtrees == 0 && consdata->exprgraphnode != NULL )
8895    {
8896       SCIP_CONSHDLRDATA* conshdlrdata;
8897       SCIP_VAR** nonlinvars;
8898 
8899       conshdlrdata = SCIPconshdlrGetData(sourceconshdlr);
8900 
8901       nexprtrees = 1;
8902       SCIP_CALL( SCIPallocBufferArray(sourcescip, &exprtrees, 1) );
8903 
8904       SCIP_CALL( SCIPexprgraphGetTree(conshdlrdata->exprgraph, consdata->exprgraphnode, &exprtrees[0]) );
8905 
8906       nonlinvars = SCIPexprtreeGetVars(exprtrees[0]);
8907       for( i = 0; i < SCIPexprtreeGetNVars(exprtrees[0]); ++i )
8908       {
8909          SCIP_CALL( SCIPgetVarCopy(sourcescip, scip, nonlinvars[i], &nonlinvars[i], varmap, consmap, global, valid) );
8910          assert(!*valid || nonlinvars[i] != NULL);
8911       }
8912    }
8913 
8914    if( *valid )
8915    {
8916       SCIP_CALL( SCIPcreateConsNonlinear(scip, cons, name ? name : SCIPconsGetName(sourcecons),
8917             consdata->nlinvars, linvars, consdata->lincoefs,
8918             nexprtrees, exprtrees, nonlincoefs,
8919             consdata->lhs, consdata->rhs,
8920             initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, stickingatnode) );
8921 
8922       /* copy information on curvature */
8923       targetconsdata = SCIPconsGetData(*cons);
8924       targetconsdata->curvature     = consdata->curvature;
8925       targetconsdata->iscurvchecked = consdata->iscurvchecked && global; /* if the copy is local, then curvature may change (get stronger) */
8926    }
8927 
8928    if( exprtrees != NULL )
8929    {
8930       for( j = 0; j < nexprtrees; ++j )
8931       {
8932          if( exprtrees[j] != NULL )
8933          {
8934             SCIP_CALL( SCIPexprtreeFree(&exprtrees[j]) );
8935          }
8936       }
8937       SCIPfreeBufferArray(sourcescip, &exprtrees);
8938    }
8939    SCIPfreeBufferArrayNull(sourcescip, &linvars);
8940 
8941    return SCIP_OKAY;
8942 }
8943 
8944 /** constraint method of constraint handler which returns the variables (if possible) */
8945 static
SCIP_DECL_CONSGETVARS(consGetVarsNonlinear)8946 SCIP_DECL_CONSGETVARS(consGetVarsNonlinear)
8947 {  /*lint --e{715}*/
8948    SCIP_CONSDATA* consdata;
8949    int cnt;
8950 
8951    assert(cons != NULL);
8952 
8953    consdata = SCIPconsGetData(cons);
8954    assert(consdata != NULL);
8955 
8956    *success = TRUE;
8957 
8958    if( varssize < consdata->nlinvars )
8959    {
8960       *success = FALSE;
8961       return SCIP_OKAY;
8962    }
8963 
8964    BMScopyMemoryArray(vars, consdata->linvars, consdata->nlinvars);
8965    cnt = consdata->nlinvars;
8966 
8967    if( consdata->exprgraphnode != NULL )
8968    {
8969       SCIP_CONSHDLRDATA* conshdlrdata;
8970       int* varsusage;
8971       int i;
8972 
8973       conshdlrdata = SCIPconshdlrGetData(conshdlr);
8974       assert(conshdlrdata != NULL);
8975 
8976       SCIP_CALL( SCIPallocBufferArray(scip, &varsusage, SCIPexprgraphGetNVars(conshdlrdata->exprgraph)) );
8977 
8978       SCIPexprgraphGetSubtreeVarsUsage(conshdlrdata->exprgraph, consdata->exprgraphnode, varsusage);
8979 
8980       for( i = 0; i < SCIPexprgraphGetNVars(conshdlrdata->exprgraph); ++i )
8981       {
8982          if( varsusage[i] == 0 )
8983             continue;
8984 
8985          if( cnt >= varssize )
8986          {
8987             *success = FALSE;
8988             break;
8989          }
8990 
8991          vars[cnt] = (SCIP_VAR*)(SCIPexprgraphGetVars(conshdlrdata->exprgraph)[i]);
8992          ++cnt;
8993       }
8994 
8995       SCIPfreeBufferArray(scip, &varsusage);
8996    }
8997    else
8998    {
8999       SCIP_VAR** exprvars;
9000       int nexprvars;
9001       int e;
9002 
9003       for( e = 0; e < consdata->nexprtrees; ++e )
9004       {
9005          exprvars  = SCIPexprtreeGetVars(consdata->exprtrees[e]);
9006          nexprvars = SCIPexprtreeGetNVars(consdata->exprtrees[e]);
9007          assert(exprvars != NULL || nexprvars == 0);
9008 
9009          if( cnt + nexprvars > varssize )
9010          {
9011             *success = FALSE;
9012             break;
9013          }
9014 
9015          BMScopyMemoryArray(&vars[cnt], exprvars, nexprvars);  /*lint !e866*/
9016          cnt += nexprvars;
9017       }
9018    }
9019 
9020    return SCIP_OKAY;
9021 }
9022 
9023 /** constraint method of constraint handler which returns the number of variables (if possible) */
9024 static
SCIP_DECL_CONSGETNVARS(consGetNVarsNonlinear)9025 SCIP_DECL_CONSGETNVARS(consGetNVarsNonlinear)
9026 {  /*lint --e{715}*/
9027    SCIP_CONSDATA* consdata;
9028 
9029    consdata = SCIPconsGetData(cons);
9030    assert(consdata != NULL);
9031 
9032    *nvars = consdata->nlinvars;
9033 
9034    if( consdata->exprgraphnode != NULL )
9035    {
9036       SCIP_CONSHDLRDATA* conshdlrdata;
9037       int* varsusage;
9038       int i;
9039 
9040       conshdlrdata = SCIPconshdlrGetData(conshdlr);
9041       assert(conshdlrdata != NULL);
9042 
9043       SCIP_CALL( SCIPallocBufferArray(scip, &varsusage, SCIPexprgraphGetNVars(conshdlrdata->exprgraph)) );
9044 
9045       SCIPexprgraphGetSubtreeVarsUsage(conshdlrdata->exprgraph, consdata->exprgraphnode, varsusage);
9046 
9047       for( i = 0; i < SCIPexprgraphGetNVars(conshdlrdata->exprgraph); ++i )
9048          if( varsusage[i] > 0 )
9049             ++*nvars;
9050 
9051       SCIPfreeBufferArray(scip, &varsusage);
9052    }
9053    else
9054    {
9055       int e;
9056 
9057       for( e = 0; e < consdata->nexprtrees; ++e )
9058          *nvars += SCIPexprtreeGetNVars(consdata->exprtrees[e]);
9059    }
9060 
9061    *success = TRUE;
9062 
9063    return SCIP_OKAY;
9064 }
9065 
9066 /** constraint parsing method of constraint handler */
9067 static
SCIP_DECL_CONSPARSE(consParseNonlinear)9068 SCIP_DECL_CONSPARSE(consParseNonlinear)
9069 {  /*lint --e{715}*/
9070    SCIP_EXPRTREE* exprtree;
9071    SCIP_EXPR* expr;
9072    SCIP_VAR** exprvars;
9073    SCIP_RETCODE retcode;
9074    int        nvars;
9075    SCIP_Real  lhs;
9076    SCIP_Real  rhs;
9077    const char* endptr;
9078    char*       nonconstendptr;
9079    const char* exprstart;
9080    const char* exprlastchar;
9081    int* varnames;
9082    int* curvarname;
9083    int varnameslength;
9084    int i;
9085 
9086    SCIPdebugMsg(scip, "cons_nonlinear::consparse parsing %s\n",str);
9087 
9088    assert(scip != NULL);
9089    assert(success != NULL);
9090    assert(str != NULL);
9091    assert(name != NULL);
9092    assert(cons != NULL);
9093 
9094    /* return if string empty */
9095    if( !*str )
9096       return SCIP_OKAY;
9097 
9098    endptr = str;
9099 
9100    expr = NULL;
9101    nvars = 0;
9102 
9103    /* set left and right hand side to their default values */
9104    lhs = -SCIPinfinity(scip);
9105    rhs =  SCIPinfinity(scip);
9106 
9107    /* parse constraint to get lhs, rhs, and expression in between (from cons_linear.c::consparse, but parsing whole string first, then getting expression) */
9108 
9109    /* check for left hand side */
9110    if( isdigit((unsigned char)str[0]) || ((str[0] == '-' || str[0] == '+') && isdigit((unsigned char)str[1])) )
9111    {
9112       /* there is a number coming, maybe it is a left-hand-side */
9113       if( !SCIPstrToRealValue(str, &lhs, &nonconstendptr) )
9114       {
9115          SCIPerrorMessage("error parsing number from <%s>\n", str);
9116          return SCIP_READERROR;
9117       }
9118       endptr = nonconstendptr;
9119 
9120       /* ignore whitespace */
9121       while( isspace((unsigned char)*endptr) )
9122          ++endptr;
9123 
9124       if( endptr[0] != '<' || endptr[1] != '=' )
9125       {
9126          /* no '<=' coming, so it was the first coefficient, but not a left-hand-side */
9127          lhs = -SCIPinfinity(scip);
9128       }
9129       else
9130       {
9131          /* it was indeed a left-hand-side, so continue parsing after it */
9132          str = endptr + 2;
9133 
9134          /* ignore whitespace */
9135          while( isspace((unsigned char)*str) )
9136             ++str;
9137       }
9138    }
9139 
9140    /* Move endptr forward until we find end of expression */
9141    while( !(strncmp(endptr, "[free]", 6) == 0)    &&
9142           !(endptr[0] == '<' && endptr[1] == '=') &&
9143           !(endptr[0] == '=' && endptr[1] == '=') &&
9144           !(endptr[0] == '>' && endptr[1] == '=') &&
9145           !(endptr[0] == '\0') )
9146       ++endptr;
9147 
9148    exprstart = str;
9149    exprlastchar = endptr - 1;
9150 
9151    *success = FALSE;
9152    str = endptr;
9153 
9154    /* check for left or right hand side */
9155    while( isspace((unsigned char)*str) )
9156       ++str;
9157 
9158    /* check for free constraint */
9159    if( strncmp(str, "[free]", 6) == 0 )
9160    {
9161       if( !SCIPisInfinity(scip, -lhs) )
9162       {
9163          SCIPerrorMessage("cannot have left hand side and [free] status \n");
9164          return SCIP_OKAY;
9165       }
9166       (*success) = TRUE;
9167    }
9168    else
9169    {
9170       switch( *str )
9171       {
9172          case '<':
9173             *success = SCIPstrToRealValue(str+2, &rhs, &nonconstendptr);
9174             break;
9175          case '=':
9176             if( !SCIPisInfinity(scip, -lhs) )
9177             {
9178                SCIPerrorMessage("cannot have == on rhs if there was a <= on lhs\n");
9179                return SCIP_OKAY;
9180             }
9181             else
9182             {
9183                *success = SCIPstrToRealValue(str+2, &rhs, &nonconstendptr);
9184                lhs = rhs;
9185             }
9186             break;
9187          case '>':
9188             if( !SCIPisInfinity(scip, -lhs) )
9189             {
9190                SCIPerrorMessage("cannot have => on rhs if there was a <= on lhs\n");
9191                return SCIP_OKAY;
9192             }
9193             else
9194             {
9195                *success = SCIPstrToRealValue(str+2, &lhs, &nonconstendptr);
9196                break;
9197             }
9198          case '\0':
9199             *success = TRUE;
9200             break;
9201          default:
9202             SCIPerrorMessage("unexpected character %c\n", *str);
9203             return SCIP_OKAY;
9204       }
9205    }
9206 
9207    /* alloc some space for variable names incl. indices; shouldn't be longer than expression string, and we even give it sizeof(int) times this length (plus 5) */
9208    varnameslength = (int) (exprlastchar - exprstart) + 5;
9209    SCIP_CALL( SCIPallocBufferArray(scip, &varnames, varnameslength) );
9210 
9211    /* parse expression */
9212    retcode = SCIPexprParse(SCIPblkmem(scip), SCIPgetMessagehdlr(scip), &expr, exprstart, exprlastchar, &nvars, varnames, varnameslength);
9213 
9214    if( retcode != SCIP_OKAY )
9215    {
9216       SCIPfreeBufferArray(scip, &varnames);
9217       return retcode;
9218    }
9219 
9220    /* get SCIP variables corresponding to variable names stored in varnames buffer */
9221    SCIP_CALL( SCIPallocBufferArray(scip, &exprvars, nvars) );
9222 
9223    assert( retcode == SCIP_OKAY );
9224    curvarname = varnames;
9225    for( i = 0; i < nvars; ++i )
9226    {
9227       assert(*curvarname == i);
9228       ++curvarname;
9229 
9230       exprvars[i] = SCIPfindVar(scip, (char*)curvarname);
9231       if( exprvars[i] == NULL )
9232       {
9233          SCIPerrorMessage("Unknown SCIP variable <%s> encountered in expression.\n", (char*)curvarname);
9234          retcode = SCIP_READERROR;
9235          goto TERMINATE;
9236       }
9237 
9238       curvarname += (strlen((char*)curvarname) + 1)/sizeof(int) + 1;
9239    }
9240 
9241    /* create expression tree */
9242    SCIP_CALL( SCIPexprtreeCreate(SCIPblkmem(scip), &exprtree, expr, nvars, 0, NULL) );
9243    SCIP_CALL( SCIPexprtreeSetVars(exprtree, nvars, exprvars) );
9244 
9245    /* create constraint */
9246    SCIP_CALL( SCIPcreateConsNonlinear(scip, cons, name,
9247       0, NULL, NULL,
9248       1, &exprtree, NULL,
9249       lhs, rhs,
9250       initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, stickingatnode) );
9251 
9252    SCIPdebugMsg(scip, "created nonlinear constraint:\n");
9253    SCIPdebugPrintCons(scip, *cons, NULL);
9254 
9255    SCIP_CALL( SCIPexprtreeFree(&exprtree) );
9256 
9257  TERMINATE:
9258    SCIPfreeBufferArray(scip, &exprvars);
9259    SCIPfreeBufferArray(scip, &varnames);
9260 
9261    return retcode;
9262 }
9263 
9264 /*
9265  * constraint specific interface methods
9266  */
9267 
9268 /** creates the handler for nonlinear constraints and includes it in SCIP */
SCIPincludeConshdlrNonlinear(SCIP * scip)9269 SCIP_RETCODE SCIPincludeConshdlrNonlinear(
9270    SCIP*                 scip                /**< SCIP data structure */
9271    )
9272 {
9273    SCIP_CONSHDLRDATA* conshdlrdata;
9274    SCIP_CONSHDLR* conshdlr;
9275 
9276    /* create nonlinear constraint handler data */
9277    SCIP_CALL( SCIPallocBlockMemory(scip, &conshdlrdata) );
9278    BMSclearMemory(conshdlrdata);
9279 
9280    /* include constraint handler */
9281    SCIP_CALL( SCIPincludeConshdlrBasic(scip, &conshdlr, CONSHDLR_NAME, CONSHDLR_DESC,
9282          CONSHDLR_ENFOPRIORITY, CONSHDLR_CHECKPRIORITY, CONSHDLR_EAGERFREQ, CONSHDLR_NEEDSCONS,
9283          consEnfolpNonlinear, consEnfopsNonlinear, consCheckNonlinear, consLockNonlinear,
9284          conshdlrdata) );
9285    assert(conshdlr != NULL);
9286 
9287    /* set non-fundamental callbacks via specific setter functions */
9288    SCIP_CALL( SCIPsetConshdlrActive(scip, conshdlr, consActiveNonlinear) );
9289    SCIP_CALL( SCIPsetConshdlrCopy(scip, conshdlr, conshdlrCopyNonlinear, consCopyNonlinear) );
9290    SCIP_CALL( SCIPsetConshdlrDeactive(scip, conshdlr, consDeactiveNonlinear) );
9291    SCIP_CALL( SCIPsetConshdlrDelete(scip, conshdlr, consDeleteNonlinear) );
9292    SCIP_CALL( SCIPsetConshdlrDisable(scip, conshdlr, consDisableNonlinear) );
9293    SCIP_CALL( SCIPsetConshdlrEnable(scip, conshdlr, consEnableNonlinear) );
9294    SCIP_CALL( SCIPsetConshdlrExit(scip, conshdlr, consExitNonlinear) );
9295    SCIP_CALL( SCIPsetConshdlrExitpre(scip, conshdlr, consExitpreNonlinear) );
9296    SCIP_CALL( SCIPsetConshdlrExitsol(scip, conshdlr, consExitsolNonlinear) );
9297    SCIP_CALL( SCIPsetConshdlrFree(scip, conshdlr, consFreeNonlinear) );
9298    SCIP_CALL( SCIPsetConshdlrGetVars(scip, conshdlr, consGetVarsNonlinear) );
9299    SCIP_CALL( SCIPsetConshdlrGetNVars(scip, conshdlr, consGetNVarsNonlinear) );
9300    SCIP_CALL( SCIPsetConshdlrInit(scip, conshdlr, consInitNonlinear) );
9301    SCIP_CALL( SCIPsetConshdlrInitpre(scip, conshdlr, consInitpreNonlinear) );
9302    SCIP_CALL( SCIPsetConshdlrInitsol(scip, conshdlr, consInitsolNonlinear) );
9303    SCIP_CALL( SCIPsetConshdlrInitlp(scip, conshdlr, consInitlpNonlinear) );
9304    SCIP_CALL( SCIPsetConshdlrPresol(scip, conshdlr, consPresolNonlinear, CONSHDLR_MAXPREROUNDS, CONSHDLR_PRESOLTIMING) );
9305    SCIP_CALL( SCIPsetConshdlrPrint(scip, conshdlr, consPrintNonlinear) );
9306    SCIP_CALL( SCIPsetConshdlrProp(scip, conshdlr, consPropNonlinear, CONSHDLR_PROPFREQ, CONSHDLR_DELAYPROP,
9307          CONSHDLR_PROP_TIMING) );
9308    SCIP_CALL( SCIPsetConshdlrSepa(scip, conshdlr, consSepalpNonlinear, consSepasolNonlinear, CONSHDLR_SEPAFREQ,
9309          CONSHDLR_SEPAPRIORITY, CONSHDLR_DELAYSEPA) );
9310    SCIP_CALL( SCIPsetConshdlrTrans(scip, conshdlr, consTransNonlinear) );
9311    SCIP_CALL( SCIPsetConshdlrParse(scip, conshdlr, consParseNonlinear) );
9312    SCIP_CALL( SCIPsetConshdlrEnforelax(scip, conshdlr, consEnforelaxNonlinear) );
9313 
9314    /* add nonlinear constraint handler parameters */
9315    SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/cutmaxrange",
9316          "maximal coef range of a cut (maximal coefficient divided by minimal coefficient) in order to be added to LP relaxation",
9317          &conshdlrdata->cutmaxrange, FALSE, 1e+7, 0.0, SCIPinfinity(scip), NULL, NULL) );
9318 
9319    SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/linfeasshift",
9320          "whether to try to make solutions in check function feasible by shifting a linear variable (esp. useful if constraint was actually objective function)",
9321          &conshdlrdata->linfeasshift, FALSE, TRUE, NULL, NULL) );
9322 
9323    SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/assumeconvex",
9324          "whether to assume that nonlinear functions in inequalities (<=) are convex (disables reformulation)",
9325          &conshdlrdata->assumeconvex, TRUE, FALSE, NULL, NULL) );
9326 
9327    SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/maxproprounds",
9328          "limit on number of propagation rounds for a single constraint within one round of SCIP propagation",
9329          &conshdlrdata->maxproprounds, FALSE, 1, 0, INT_MAX, NULL, NULL) );
9330 
9331    SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/reformulate",
9332          "whether to reformulate expression graph",
9333          &conshdlrdata->reformulate, FALSE, TRUE, NULL, NULL) );
9334 
9335    SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/maxexpansionexponent",
9336          "maximal exponent where still expanding non-monomial polynomials in expression simplification",
9337          &conshdlrdata->maxexpansionexponent, TRUE, 2, 1, INT_MAX, NULL, NULL) );
9338 
9339    SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/sepanlpmincont",
9340          "minimal required fraction of continuous variables in problem to use solution of NLP relaxation in root for separation",
9341          &conshdlrdata->sepanlpmincont, FALSE, 1.0, 0.0, 2.0, NULL, NULL) );
9342 
9343    SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/enfocutsremovable",
9344          "are cuts added during enforcement removable from the LP in the same node?",
9345          &conshdlrdata->enfocutsremovable, TRUE, FALSE, NULL, NULL) );
9346 
9347    conshdlrdata->linvareventhdlr = NULL;
9348    SCIP_CALL( SCIPincludeEventhdlrBasic(scip, &(conshdlrdata->linvareventhdlr), CONSHDLR_NAME"_boundchange", "signals a bound change to a nonlinear constraint",
9349          processLinearVarEvent, NULL) );
9350    assert(conshdlrdata->linvareventhdlr != NULL);
9351 
9352    conshdlrdata->nonlinvareventhdlr = NULL;
9353    SCIP_CALL( SCIPincludeEventhdlrBasic(scip, &(conshdlrdata->nonlinvareventhdlr), CONSHDLR_NAME"_boundchange2", "signals a bound change to a nonlinear constraint handler",
9354          processNonlinearVarEvent, (SCIP_EVENTHDLRDATA*)conshdlrdata) );
9355    assert(conshdlrdata->nonlinvareventhdlr != NULL);
9356 
9357    SCIP_CALL( SCIPincludeEventhdlrBasic(scip, NULL, CONSHDLR_NAME"_newsolution", "handles the event that a new primal solution has been found",
9358          processNewSolutionEvent, NULL) );
9359 
9360    /* create expression interpreter */
9361    SCIP_CALL( SCIPexprintCreate(SCIPblkmem(scip), &conshdlrdata->exprinterpreter) );
9362 
9363    /* create expression graph */
9364    SCIP_CALL( SCIPexprgraphCreate(SCIPblkmem(scip), &conshdlrdata->exprgraph, -1, -1,
9365          exprgraphVarAdded, exprgraphVarRemove, NULL, (void*)conshdlrdata) );
9366    conshdlrdata->isremovedfixings = TRUE;
9367    conshdlrdata->ispropagated = TRUE;
9368 
9369    conshdlrdata->scip = scip;
9370 
9371    return SCIP_OKAY;
9372 }
9373 
9374 /** includes a nonlinear constraint upgrade method into the nonlinear constraint handler */
SCIPincludeNonlinconsUpgrade(SCIP * scip,SCIP_DECL_NONLINCONSUPGD ((* nonlinconsupgd)),SCIP_DECL_EXPRGRAPHNODEREFORM ((* nodereform)),int priority,SCIP_Bool active,const char * conshdlrname)9375 SCIP_RETCODE SCIPincludeNonlinconsUpgrade(
9376    SCIP*                 scip,               /**< SCIP data structure */
9377    SCIP_DECL_NONLINCONSUPGD((*nonlinconsupgd)),/**< method to call for upgrading nonlinear constraint, or NULL */
9378    SCIP_DECL_EXPRGRAPHNODEREFORM((*nodereform)),/**< method to call for reformulating expression graph node, or NULL */
9379    int                   priority,           /**< priority of upgrading method */
9380    SCIP_Bool             active,             /**< should the upgrading method by active by default? */
9381    const char*           conshdlrname        /**< name of the constraint handler */
9382    )
9383 {
9384    SCIP_CONSHDLR*        conshdlr;
9385    SCIP_CONSHDLRDATA*    conshdlrdata;
9386    SCIP_NLCONSUPGRADE*   nlconsupgrade;
9387    char                  paramname[SCIP_MAXSTRLEN];
9388    char                  paramdesc[SCIP_MAXSTRLEN];
9389    int                   i;
9390 
9391    assert(conshdlrname != NULL );
9392 
9393    /* ignore empty upgrade functions */
9394    if( nonlinconsupgd == NULL && nodereform == NULL )
9395       return SCIP_OKAY;
9396 
9397    /* find the nonlinear constraint handler */
9398    conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
9399    if( conshdlr == NULL )
9400    {
9401       SCIPerrorMessage("nonlinear constraint handler not found\n");
9402       return SCIP_PLUGINNOTFOUND;
9403    }
9404 
9405    conshdlrdata = SCIPconshdlrGetData(conshdlr);
9406    assert(conshdlrdata != NULL);
9407 
9408    /* check whether upgrade method exists already */
9409    for( i = conshdlrdata->nnlconsupgrades - 1; i >= 0; --i )
9410    {
9411       if( conshdlrdata->nlconsupgrades[i]->nlconsupgd == nonlinconsupgd && conshdlrdata->nlconsupgrades[i]->nodereform == nodereform)
9412       {
9413 #ifdef SCIP_DEBUG
9414          SCIPwarningMessage(scip, "Try to add already known upgrade method pair for constraint handler <%s>.\n", conshdlrname); /*lint !e611*/
9415 #endif
9416          return SCIP_OKAY;
9417       }
9418    }
9419 
9420    /* create a nonlinear constraint upgrade data object */
9421    SCIP_CALL( SCIPallocBlockMemory(scip, &nlconsupgrade) );
9422    nlconsupgrade->nlconsupgd = nonlinconsupgd;
9423    nlconsupgrade->nodereform = nodereform;
9424    nlconsupgrade->priority   = priority;
9425    nlconsupgrade->active     = active;
9426 
9427    /* insert nonlinear constraint upgrade method into constraint handler data */
9428    assert(conshdlrdata->nnlconsupgrades <= conshdlrdata->nlconsupgradessize);
9429    if( conshdlrdata->nnlconsupgrades+1 > conshdlrdata->nlconsupgradessize )
9430    {
9431       int newsize;
9432 
9433       newsize = SCIPcalcMemGrowSize(scip, conshdlrdata->nnlconsupgrades+1);
9434       SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &conshdlrdata->nlconsupgrades, conshdlrdata->nnlconsupgrades, newsize) );
9435       conshdlrdata->nlconsupgradessize = newsize;
9436    }
9437    assert(conshdlrdata->nnlconsupgrades+1 <= conshdlrdata->nlconsupgradessize);
9438 
9439    for( i = conshdlrdata->nnlconsupgrades; i > 0 && conshdlrdata->nlconsupgrades[i-1]->priority < nlconsupgrade->priority; --i )
9440       conshdlrdata->nlconsupgrades[i] = conshdlrdata->nlconsupgrades[i-1];
9441    assert(0 <= i && i <= conshdlrdata->nnlconsupgrades);
9442    conshdlrdata->nlconsupgrades[i] = nlconsupgrade;
9443    conshdlrdata->nnlconsupgrades++;
9444 
9445    /* adds parameter to turn on and off the upgrading step */
9446    (void) SCIPsnprintf(paramname, SCIP_MAXSTRLEN, "constraints/" CONSHDLR_NAME "/upgrade/%s", conshdlrname);
9447    (void) SCIPsnprintf(paramdesc, SCIP_MAXSTRLEN, "enable nonlinear upgrading for constraint handler <%s>", conshdlrname);
9448    SCIP_CALL( SCIPaddBoolParam(scip,
9449          paramname, paramdesc,
9450          &nlconsupgrade->active, FALSE, active, NULL, NULL) );
9451 
9452    return SCIP_OKAY;
9453 }
9454 
9455 /** creates and captures a nonlinear constraint
9456  *  this variant takes expression trees as input
9457  *
9458  *  @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
9459  */
SCIPcreateConsNonlinear(SCIP * scip,SCIP_CONS ** cons,const char * name,int nlinvars,SCIP_VAR ** linvars,SCIP_Real * lincoefs,int nexprtrees,SCIP_EXPRTREE ** exprtrees,SCIP_Real * nonlincoefs,SCIP_Real lhs,SCIP_Real rhs,SCIP_Bool initial,SCIP_Bool separate,SCIP_Bool enforce,SCIP_Bool check,SCIP_Bool propagate,SCIP_Bool local,SCIP_Bool modifiable,SCIP_Bool dynamic,SCIP_Bool removable,SCIP_Bool stickingatnode)9460 SCIP_RETCODE SCIPcreateConsNonlinear(
9461    SCIP*                 scip,               /**< SCIP data structure */
9462    SCIP_CONS**           cons,               /**< pointer to hold the created constraint */
9463    const char*           name,               /**< name of constraint */
9464    int                   nlinvars,           /**< number of linear variables in the constraint */
9465    SCIP_VAR**            linvars,            /**< array with linear variables of constraint entries */
9466    SCIP_Real*            lincoefs,           /**< array with coefficients of constraint linear entries */
9467    int                   nexprtrees,         /**< number of expression trees for nonlinear part of constraint */
9468    SCIP_EXPRTREE**       exprtrees,          /**< expression trees for nonlinear part of constraint */
9469    SCIP_Real*            nonlincoefs,        /**< coefficients for expression trees for nonlinear part, or NULL if all 1.0 */
9470    SCIP_Real             lhs,                /**< left hand side of constraint */
9471    SCIP_Real             rhs,                /**< right hand side of constraint */
9472    SCIP_Bool             initial,            /**< should the LP relaxation of constraint be in the initial LP?
9473                                               *   Usually set to TRUE. Set to FALSE for 'lazy constraints'. */
9474    SCIP_Bool             separate,           /**< should the constraint be separated during LP processing?
9475                                               *   Usually set to TRUE. */
9476    SCIP_Bool             enforce,            /**< should the constraint be enforced during node processing?
9477                                               *   TRUE for model constraints, FALSE for additional, redundant constraints. */
9478    SCIP_Bool             check,              /**< should the constraint be checked for feasibility?
9479                                               *   TRUE for model constraints, FALSE for additional, redundant constraints. */
9480    SCIP_Bool             propagate,          /**< should the constraint be propagated during node processing?
9481                                               *   Usually set to TRUE. */
9482    SCIP_Bool             local,              /**< is constraint only valid locally?
9483                                               *   Usually set to FALSE. Has to be set to TRUE, e.g., for branching constraints. */
9484    SCIP_Bool             modifiable,         /**< is constraint modifiable (subject to column generation)?
9485                                               *   Usually set to FALSE. In column generation applications, set to TRUE if pricing
9486                                               *   adds coefficients to this constraint. */
9487    SCIP_Bool             dynamic,            /**< is constraint subject to aging?
9488                                               *   Usually set to FALSE. Set to TRUE for own cuts which
9489                                               *   are seperated as constraints. */
9490    SCIP_Bool             removable,          /**< should the relaxation be removed from the LP due to aging or cleanup?
9491                                               *   Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */
9492    SCIP_Bool             stickingatnode      /**< should the constraint always be kept at the node where it was added, even
9493                                               *   if it may be moved to a more global node?
9494                                               *   Usually set to FALSE. Set to TRUE to for constraints that represent node data. */
9495    )
9496 {
9497    SCIP_CONSHDLR* conshdlr;
9498    SCIP_CONSDATA* consdata;
9499    int i;
9500 
9501    assert(linvars  != NULL || nlinvars == 0);
9502    assert(lincoefs != NULL || nlinvars == 0);
9503    assert(exprtrees   != NULL || nexprtrees == 0);
9504    assert(modifiable == FALSE); /* we do not support column generation */
9505 
9506    /* find the nonlinear constraint handler */
9507    conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
9508    if( conshdlr == NULL )
9509    {
9510       SCIPerrorMessage("nonlinear constraint handler not found\n");
9511       return SCIP_PLUGINNOTFOUND;
9512    }
9513 
9514    /* create constraint data */
9515    SCIP_CALL( consdataCreateEmpty(scip, &consdata) );
9516 
9517    consdata->lhs = lhs;
9518    consdata->rhs = rhs;
9519 
9520    /* create constraint */
9521    SCIP_CALL( SCIPcreateCons(scip, cons, name, conshdlr, consdata, initial, separate, enforce, check, propagate,
9522          local, modifiable, dynamic, removable, stickingatnode) );
9523 
9524    /* add linear variables */
9525    SCIP_CALL( consdataEnsureLinearVarsSize(scip, consdata, nlinvars) );
9526    for( i = 0; i < nlinvars; ++i )
9527    {
9528       if( SCIPisZero(scip, lincoefs[i]) )  /*lint !e613*/
9529          continue;
9530 
9531       SCIP_CALL( addLinearCoef(scip, *cons, linvars[i], lincoefs[i]) );  /*lint !e613*/
9532    }
9533 
9534    /* set expression trees */
9535    SCIP_CALL( consdataSetExprtrees(scip, consdata, nexprtrees, exprtrees, nonlincoefs, TRUE) );
9536 
9537    SCIPdebugMsg(scip, "created nonlinear constraint ");
9538    SCIPdebugPrintCons(scip, *cons, NULL);
9539 
9540    return SCIP_OKAY;
9541 }
9542 
9543 /** creates and captures a nonlinear constraint
9544  *  in its most basic version, i. e., all constraint flags are set to their basic value as explained for the
9545  *  method SCIPcreateConsNonlinear(); all flags can be set via SCIPsetConsFLAGNAME-methods in scip.h
9546  *
9547  *  this variant takes expression trees as input
9548  *
9549  *  @see SCIPcreateConsNonlinear() for information about the basic constraint flag configuration
9550  *
9551  *  @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
9552  */
SCIPcreateConsBasicNonlinear(SCIP * scip,SCIP_CONS ** cons,const char * name,int nlinvars,SCIP_VAR ** linvars,SCIP_Real * lincoefs,int nexprtrees,SCIP_EXPRTREE ** exprtrees,SCIP_Real * nonlincoefs,SCIP_Real lhs,SCIP_Real rhs)9553 SCIP_RETCODE SCIPcreateConsBasicNonlinear(
9554    SCIP*                 scip,               /**< SCIP data structure */
9555    SCIP_CONS**           cons,               /**< pointer to hold the created constraint */
9556    const char*           name,               /**< name of constraint */
9557    int                   nlinvars,           /**< number of linear variables in the constraint */
9558    SCIP_VAR**            linvars,            /**< array with linear variables of constraint entries */
9559    SCIP_Real*            lincoefs,           /**< array with coefficients of constraint linear entries */
9560    int                   nexprtrees,         /**< number of expression trees for nonlinear part of constraint */
9561    SCIP_EXPRTREE**       exprtrees,          /**< expression trees for nonlinear part of constraint */
9562    SCIP_Real*            nonlincoefs,        /**< coefficients for expression trees for nonlinear part, or NULL if all 1.0 */
9563    SCIP_Real             lhs,                /**< left hand side of constraint */
9564    SCIP_Real             rhs                 /**< right hand side of constraint */
9565    )
9566 {
9567    assert(scip != NULL);
9568 
9569    SCIP_CALL( SCIPcreateConsNonlinear(scip, cons, name, nlinvars, linvars, lincoefs, nexprtrees, exprtrees,
9570          nonlincoefs, lhs, rhs,
9571          TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
9572 
9573    return SCIP_OKAY;
9574 }
9575 
9576 /** creates and captures a nonlinear constraint
9577  * this variant takes a node of the expression graph as input and can only be used during presolving
9578  * it is assumed that the nonlinear constraint will be added to the transformed problem short after creation
9579  * the given exprgraphnode is captured in this method
9580  *
9581  *  @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
9582  */
SCIPcreateConsNonlinear2(SCIP * scip,SCIP_CONS ** cons,const char * name,int nlinvars,SCIP_VAR ** linvars,SCIP_Real * lincoefs,SCIP_EXPRGRAPHNODE * exprgraphnode,SCIP_Real lhs,SCIP_Real rhs,SCIP_Bool initial,SCIP_Bool separate,SCIP_Bool enforce,SCIP_Bool check,SCIP_Bool propagate,SCIP_Bool local,SCIP_Bool modifiable,SCIP_Bool dynamic,SCIP_Bool removable,SCIP_Bool stickingatnode)9583 SCIP_RETCODE SCIPcreateConsNonlinear2(
9584    SCIP*                 scip,               /**< SCIP data structure */
9585    SCIP_CONS**           cons,               /**< pointer to hold the created constraint */
9586    const char*           name,               /**< name of constraint */
9587    int                   nlinvars,           /**< number of linear variables in the constraint */
9588    SCIP_VAR**            linvars,            /**< array with linear variables of constraint entries */
9589    SCIP_Real*            lincoefs,           /**< array with coefficients of constraint linear entries */
9590    SCIP_EXPRGRAPHNODE*   exprgraphnode,      /**< expression graph node associated to nonlinear expression */
9591    SCIP_Real             lhs,                /**< left hand side of constraint */
9592    SCIP_Real             rhs,                /**< right hand side of constraint */
9593    SCIP_Bool             initial,            /**< should the LP relaxation of constraint be in the initial LP?
9594                                               *   Usually set to TRUE. Set to FALSE for 'lazy constraints'. */
9595    SCIP_Bool             separate,           /**< should the constraint be separated during LP processing?
9596                                               *   Usually set to TRUE. */
9597    SCIP_Bool             enforce,            /**< should the constraint be enforced during node processing?
9598                                               *   TRUE for model constraints, FALSE for additional, redundant constraints. */
9599    SCIP_Bool             check,              /**< should the constraint be checked for feasibility?
9600                                               *   TRUE for model constraints, FALSE for additional, redundant constraints. */
9601    SCIP_Bool             propagate,          /**< should the constraint be propagated during node processing?
9602                                               *   Usually set to TRUE. */
9603    SCIP_Bool             local,              /**< is constraint only valid locally?
9604                                               *   Usually set to FALSE. Has to be set to TRUE, e.g., for branching constraints. */
9605    SCIP_Bool             modifiable,         /**< is constraint modifiable (subject to column generation)?
9606                                               *   Usually set to FALSE. In column generation applications, set to TRUE if pricing
9607                                               *   adds coefficients to this constraint. */
9608    SCIP_Bool             dynamic,            /**< is constraint subject to aging?
9609                                               *   Usually set to FALSE. Set to TRUE for own cuts which
9610                                               *   are seperated as constraints. */
9611    SCIP_Bool             removable,          /**< should the relaxation be removed from the LP due to aging or cleanup?
9612                                               *   Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */
9613    SCIP_Bool             stickingatnode      /**< should the constraint always be kept at the node where it was added, even
9614                                               *   if it may be moved to a more global node?
9615                                               *   Usually set to FALSE. Set to TRUE to for constraints that represent node data. */
9616    )
9617 {
9618    SCIP_CONSHDLR* conshdlr;
9619    SCIP_CONSDATA* consdata;
9620    int i;
9621 
9622    assert(modifiable == FALSE); /* we do not support column generation */
9623    assert(SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING);
9624 
9625    /* find the nonlinear constraint handler */
9626    conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
9627    if( conshdlr == NULL )
9628    {
9629       SCIPerrorMessage("nonlinear constraint handler not found\n");
9630       return SCIP_PLUGINNOTFOUND;
9631    }
9632 
9633    /* create constraint data */
9634    SCIP_CALL( consdataCreateEmpty(scip, &consdata) );
9635 
9636    consdata->lhs = lhs;
9637    consdata->rhs = rhs;
9638 
9639    /* create constraint */
9640    SCIP_CALL( SCIPcreateCons(scip, cons, name, conshdlr, consdata, initial, separate, enforce, check, propagate,
9641          local, modifiable, dynamic, removable, stickingatnode) );
9642 
9643    /* add linear variables */
9644    SCIP_CALL( consdataEnsureLinearVarsSize(scip, consdata, nlinvars) );
9645    for( i = 0; i < nlinvars; ++i )
9646    {
9647       if( SCIPisZero(scip, lincoefs[i]) )
9648          continue;
9649 
9650       SCIP_CALL( addLinearCoef(scip, *cons, linvars[i], lincoefs[i]) );
9651    }
9652 
9653    /* set expression graph node */
9654    if( exprgraphnode != NULL )
9655    {
9656       consdata->exprgraphnode = exprgraphnode;
9657       consdata->curvature = SCIP_EXPRCURV_UNKNOWN;
9658       consdata->iscurvchecked = FALSE;
9659       consdata->activity = SCIP_INVALID;
9660       SCIPexprgraphCaptureNode(exprgraphnode);
9661    }
9662 
9663    SCIPdebugMsg(scip, "created nonlinear constraint ");
9664    SCIPdebugPrintCons(scip, *cons, NULL);
9665 
9666    return SCIP_OKAY;
9667 }
9668 
9669 /** creates and captures a nonlinear constraint
9670  *  in its most basic version, i. e., all constraint flags are set to their basic value as explained for the
9671  *  method SCIPcreateConsNonlinear(); all flags can be set via SCIPsetConsFLAGNAME-methods in scip.h
9672  *
9673  *  this variant takes a node of the expression graph as input and can only be used during presolving
9674  *  it is assumed that the nonlinear constraint will be added to the transformed problem short after creation
9675  *  the given exprgraphnode is captured in this method
9676  *
9677  *  @see SCIPcreateConsNonlinear() for information about the basic constraint flag configuration
9678  *
9679  *  @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
9680  */
SCIPcreateConsBasicNonlinear2(SCIP * scip,SCIP_CONS ** cons,const char * name,int nlinvars,SCIP_VAR ** linvars,SCIP_Real * lincoefs,SCIP_EXPRGRAPHNODE * exprgraphnode,SCIP_Real lhs,SCIP_Real rhs)9681 SCIP_RETCODE SCIPcreateConsBasicNonlinear2(
9682    SCIP*                 scip,               /**< SCIP data structure */
9683    SCIP_CONS**           cons,               /**< pointer to hold the created constraint */
9684    const char*           name,               /**< name of constraint */
9685    int                   nlinvars,           /**< number of linear variables in the constraint */
9686    SCIP_VAR**            linvars,            /**< array with linear variables of constraint entries */
9687    SCIP_Real*            lincoefs,           /**< array with coefficients of constraint linear entries */
9688    SCIP_EXPRGRAPHNODE*   exprgraphnode,      /**< expression graph node associated to nonlinear expression */
9689    SCIP_Real             lhs,                /**< left hand side of constraint */
9690    SCIP_Real             rhs                 /**< right hand side of constraint */
9691    )
9692 {
9693    assert(scip != NULL);
9694 
9695    SCIP_CALL( SCIPcreateConsNonlinear2(scip, cons, name, nlinvars, linvars, lincoefs, exprgraphnode, lhs, rhs,
9696          TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
9697 
9698    return SCIP_OKAY;
9699 }
9700 
9701 /** adds a linear variable with coefficient to a nonlinear constraint */
SCIPaddLinearVarNonlinear(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * var,SCIP_Real coef)9702 SCIP_RETCODE SCIPaddLinearVarNonlinear(
9703    SCIP*                 scip,               /**< SCIP data structure */
9704    SCIP_CONS*            cons,               /**< constraint */
9705    SCIP_VAR*             var,                /**< variable */
9706    SCIP_Real             coef                /**< coefficient of variable */
9707    )
9708 {
9709    assert(scip != NULL);
9710    assert(cons != NULL);
9711    assert(var  != NULL);
9712    assert(!SCIPisInfinity(scip, REALABS(coef)));
9713 
9714    SCIP_CALL( addLinearCoef(scip, cons, var, coef) );
9715 
9716    return SCIP_OKAY;
9717 }
9718 
9719 /** sets the expression trees in a nonlinear constraint
9720  * constraint must not be active yet
9721  */
SCIPsetExprtreesNonlinear(SCIP * scip,SCIP_CONS * cons,int nexprtrees,SCIP_EXPRTREE ** exprtrees,SCIP_Real * coefs)9722 SCIP_RETCODE SCIPsetExprtreesNonlinear(
9723    SCIP*                 scip,               /**< SCIP data structure */
9724    SCIP_CONS*            cons,               /**< constraint */
9725    int                   nexprtrees,         /**< number of expression trees */
9726    SCIP_EXPRTREE**       exprtrees,          /**< new expression trees, or NULL if nexprtrees is 0 */
9727    SCIP_Real*            coefs               /**< coefficients of expression trees, or NULL if all 1.0 */
9728    )
9729 {
9730    assert(scip != NULL);
9731    assert(cons != NULL);
9732    assert(!SCIPconsIsActive(cons));
9733    assert(SCIPconsGetData(cons) != NULL);
9734    assert(exprtrees != NULL || nexprtrees == 0);
9735 
9736    SCIP_CALL( consdataSetExprtrees(scip, SCIPconsGetData(cons), nexprtrees, exprtrees, coefs, TRUE) );
9737 
9738    return SCIP_OKAY;
9739 }
9740 
9741 /** adds expression trees to a nonlinear constraint
9742  * constraint must not be active yet
9743  */
SCIPaddExprtreesNonlinear(SCIP * scip,SCIP_CONS * cons,int nexprtrees,SCIP_EXPRTREE ** exprtrees,SCIP_Real * coefs)9744 SCIP_RETCODE SCIPaddExprtreesNonlinear(
9745    SCIP*                 scip,               /**< SCIP data structure */
9746    SCIP_CONS*            cons,               /**< constraint */
9747    int                   nexprtrees,         /**< number of expression trees */
9748    SCIP_EXPRTREE**       exprtrees,          /**< new expression trees, or NULL if nexprtrees is 0 */
9749    SCIP_Real*            coefs               /**< coefficients of expression trees, or NULL if all 1.0 */
9750    )
9751 {
9752    assert(scip != NULL);
9753    assert(cons != NULL);
9754    assert(!SCIPconsIsActive(cons));
9755    assert(SCIPconsGetData(cons) != NULL);
9756    assert(exprtrees != NULL || nexprtrees == 0);
9757 
9758    SCIP_CALL( consdataAddExprtrees(scip, SCIPconsGetData(cons), nexprtrees, exprtrees, coefs, TRUE) );
9759 
9760    return SCIP_OKAY;
9761 }
9762 
9763 /** gets the nonlinear constraint as a nonlinear row representation */
SCIPgetNlRowNonlinear(SCIP * scip,SCIP_CONS * cons,SCIP_NLROW ** nlrow)9764 SCIP_RETCODE SCIPgetNlRowNonlinear(
9765    SCIP*                 scip,               /**< SCIP data structure */
9766    SCIP_CONS*            cons,               /**< constraint */
9767    SCIP_NLROW**          nlrow               /**< pointer to store nonlinear row */
9768    )
9769 {
9770    SCIP_CONSDATA* consdata;
9771 
9772    assert(cons  != NULL);
9773    assert(nlrow != NULL);
9774 
9775    consdata = SCIPconsGetData(cons);
9776    assert(consdata != NULL);
9777 
9778    if( consdata->nlrow == NULL )
9779    {
9780       SCIP_CALL( createNlRow(scip, cons) );
9781    }
9782    assert(consdata->nlrow != NULL);
9783    *nlrow = consdata->nlrow;
9784 
9785    return SCIP_OKAY;
9786 }
9787 
9788 /** gets the number of variables in the linear term of a nonlinear constraint */
SCIPgetNLinearVarsNonlinear(SCIP * scip,SCIP_CONS * cons)9789 int SCIPgetNLinearVarsNonlinear(
9790    SCIP*                 scip,               /**< SCIP data structure */
9791    SCIP_CONS*            cons                /**< constraint */
9792    )
9793 {
9794    assert(scip != NULL);
9795    assert(cons != NULL);
9796    assert(SCIPconsGetData(cons) != NULL);
9797 
9798    return SCIPconsGetData(cons)->nlinvars;
9799 }
9800 
9801 /** gets the variables in the linear part of a nonlinear constraint */
SCIPgetLinearVarsNonlinear(SCIP * scip,SCIP_CONS * cons)9802 SCIP_VAR** SCIPgetLinearVarsNonlinear(
9803    SCIP*                 scip,               /**< SCIP data structure */
9804    SCIP_CONS*            cons                /**< constraint */
9805    )
9806 {
9807    assert(scip != NULL);
9808    assert(cons != NULL);
9809    assert(SCIPconsGetData(cons) != NULL);
9810 
9811    return SCIPconsGetData(cons)->linvars;
9812 }
9813 
9814 /** gets the coefficients in the linear part of a nonlinear constraint */
SCIPgetLinearCoefsNonlinear(SCIP * scip,SCIP_CONS * cons)9815 SCIP_Real* SCIPgetLinearCoefsNonlinear(
9816    SCIP*                 scip,               /**< SCIP data structure */
9817    SCIP_CONS*            cons                /**< constraint */
9818    )
9819 {
9820    assert(scip != NULL);
9821    assert(cons != NULL);
9822    assert(SCIPconsGetData(cons) != NULL);
9823 
9824    return SCIPconsGetData(cons)->lincoefs;
9825 }
9826 
9827 /** gets the number of expression trees of a nonlinear constraint */
SCIPgetNExprtreesNonlinear(SCIP * scip,SCIP_CONS * cons)9828 int SCIPgetNExprtreesNonlinear(
9829    SCIP*                 scip,               /**< SCIP data structure */
9830    SCIP_CONS*            cons                /**< constraint */
9831    )
9832 {
9833    assert(scip != NULL);
9834    assert(cons != NULL);
9835    assert(SCIPconsGetData(cons) != NULL);
9836    assert(SCIPgetStage(scip) < SCIP_STAGE_INITPRESOLVE || SCIPgetStage(scip) > SCIP_STAGE_EXITPRESOLVE);
9837 
9838    return SCIPconsGetData(cons)->nexprtrees;
9839 }
9840 
9841 /** gets the expression trees of a nonlinear constraint */
SCIPgetExprtreesNonlinear(SCIP * scip,SCIP_CONS * cons)9842 SCIP_EXPRTREE** SCIPgetExprtreesNonlinear(
9843    SCIP*                 scip,               /**< SCIP data structure */
9844    SCIP_CONS*            cons                /**< constraint */
9845    )
9846 {
9847    assert(scip != NULL);
9848    assert(cons != NULL);
9849    assert(SCIPconsGetData(cons) != NULL);
9850    assert(SCIPgetStage(scip) < SCIP_STAGE_INITPRESOLVE || SCIPgetStage(scip) > SCIP_STAGE_EXITPRESOLVE);
9851 
9852    return SCIPconsGetData(cons)->exprtrees;
9853 }
9854 
9855 /** gets the coefficients of the expression trees of a nonlinear constraint */
SCIPgetExprtreeCoefsNonlinear(SCIP * scip,SCIP_CONS * cons)9856 SCIP_Real* SCIPgetExprtreeCoefsNonlinear(
9857    SCIP*                 scip,               /**< SCIP data structure */
9858    SCIP_CONS*            cons                /**< constraint */
9859    )
9860 {
9861    assert(scip != NULL);
9862    assert(cons != NULL);
9863    assert(SCIPconsGetData(cons) != NULL);
9864    assert(SCIPgetStage(scip) < SCIP_STAGE_INITPRESOLVE || SCIPgetStage(scip) > SCIP_STAGE_EXITPRESOLVE);
9865 
9866    return SCIPconsGetData(cons)->nonlincoefs;
9867 }
9868 
9869 /** gets the expression graph node of a nonlinear constraint */
SCIPgetExprgraphNodeNonlinear(SCIP * scip,SCIP_CONS * cons)9870 SCIP_EXPRGRAPHNODE* SCIPgetExprgraphNodeNonlinear(
9871    SCIP*                 scip,               /**< SCIP data structure */
9872    SCIP_CONS*            cons                /**< constraint */
9873    )
9874 {
9875    assert(scip != NULL);
9876    assert(cons != NULL);
9877    assert(SCIPconsGetData(cons) != NULL);
9878 
9879    return SCIPconsGetData(cons)->exprgraphnode;
9880 }
9881 
9882 /** gets the left hand side of a nonlinear constraint */
SCIPgetLhsNonlinear(SCIP * scip,SCIP_CONS * cons)9883 SCIP_Real SCIPgetLhsNonlinear(
9884    SCIP*                 scip,               /**< SCIP data structure */
9885    SCIP_CONS*            cons                /**< constraint */
9886    )
9887 {
9888    assert(scip != NULL);
9889    assert(cons != NULL);
9890    assert(SCIPconsGetData(cons) != NULL);
9891 
9892    return SCIPconsGetData(cons)->lhs;
9893 }
9894 
9895 /** gets the right hand side of a nonlinear constraint */
SCIPgetRhsNonlinear(SCIP * scip,SCIP_CONS * cons)9896 SCIP_Real SCIPgetRhsNonlinear(
9897    SCIP*                 scip,               /**< SCIP data structure */
9898    SCIP_CONS*            cons                /**< constraint */
9899    )
9900 {
9901    assert(scip != NULL);
9902    assert(cons != NULL);
9903    assert(SCIPconsGetData(cons) != NULL);
9904 
9905    return SCIPconsGetData(cons)->rhs;
9906 }
9907 
9908 /** check the function of a nonlinear constraint for convexity/concavity, if not done yet */
SCIPcheckCurvatureNonlinear(SCIP * scip,SCIP_CONS * cons)9909 SCIP_RETCODE SCIPcheckCurvatureNonlinear(
9910    SCIP*                 scip,               /**< SCIP data structure */
9911    SCIP_CONS*            cons                /**< constraint */
9912    )
9913 {
9914    SCIP_CONSHDLR* conshdlr;
9915    SCIP_CONSHDLRDATA* conshdlrdata;
9916 
9917    assert(scip != NULL);
9918    assert(cons != NULL);
9919 
9920    conshdlr = SCIPconsGetHdlr(cons);
9921    assert(conshdlr != NULL);
9922    conshdlrdata = SCIPconshdlrGetData(conshdlr);
9923    assert(conshdlrdata != NULL);
9924 
9925    SCIP_CALL( checkCurvature(scip, cons, conshdlrdata->assumeconvex) );
9926 
9927    return SCIP_OKAY;
9928 }
9929 
9930 /** gets the curvature of the nonlinear function of a nonlinear constraint */
SCIPgetCurvatureNonlinear(SCIP * scip,SCIP_CONS * cons,SCIP_Bool checkcurv,SCIP_EXPRCURV * curvature)9931 SCIP_RETCODE SCIPgetCurvatureNonlinear(
9932    SCIP*                 scip,               /**< SCIP data structure */
9933    SCIP_CONS*            cons,               /**< constraint */
9934    SCIP_Bool             checkcurv,          /**< whether to check constraint curvature, if not checked before */
9935    SCIP_EXPRCURV*        curvature           /**< pointer to store curvature of constraint */
9936    )
9937 {
9938    SCIP_CONSHDLR* conshdlr;
9939    SCIP_CONSHDLRDATA* conshdlrdata;
9940    SCIP_CONSDATA* consdata;
9941 
9942    assert(scip != NULL);
9943    assert(cons != NULL);
9944    assert(curvature != NULL);
9945 
9946    consdata = SCIPconsGetData(cons);
9947    assert(consdata != NULL);
9948 
9949    conshdlr = SCIPconsGetHdlr(cons);
9950    assert(conshdlr != NULL);
9951    conshdlrdata = SCIPconshdlrGetData(conshdlr);
9952    assert(conshdlrdata != NULL);
9953 
9954    if( checkcurv && !consdata->iscurvchecked )
9955    {
9956       SCIP_CALL( checkCurvature(scip, cons, conshdlrdata->assumeconvex) );
9957    }
9958 
9959    *curvature = consdata->curvature;
9960 
9961    return SCIP_OKAY;
9962 }
9963 
9964 /** gets the curvature of the expression trees (multiplied by their coefficient) of a nonlinear constraint */
SCIPgetExprtreeCurvaturesNonlinear(SCIP * scip,SCIP_CONS * cons,SCIP_Bool checkcurv,SCIP_EXPRCURV ** curvatures)9965 SCIP_RETCODE SCIPgetExprtreeCurvaturesNonlinear(
9966    SCIP*                 scip,               /**< SCIP data structure */
9967    SCIP_CONS*            cons,               /**< constraint */
9968    SCIP_Bool             checkcurv,          /**< whether to check constraint curvature, if not checked before */
9969    SCIP_EXPRCURV**       curvatures          /**< buffer to store curvatures of exprtrees */
9970    )
9971 {
9972    SCIP_CONSHDLR* conshdlr;
9973    SCIP_CONSHDLRDATA* conshdlrdata;
9974    SCIP_CONSDATA* consdata;
9975 
9976    assert(scip != NULL);
9977    assert(cons != NULL);
9978    assert(curvatures != NULL);
9979    assert(SCIPgetStage(scip) < SCIP_STAGE_INITPRESOLVE || SCIPgetStage(scip) > SCIP_STAGE_EXITPRESOLVE);
9980 
9981    consdata = SCIPconsGetData(cons);
9982    assert(consdata != NULL);
9983 
9984    conshdlr = SCIPconsGetHdlr(cons);
9985    assert(conshdlr != NULL);
9986    conshdlrdata = SCIPconshdlrGetData(conshdlr);
9987    assert(conshdlrdata != NULL);
9988 
9989    assert(SCIPconsGetData(cons) != NULL);
9990 
9991    if( checkcurv && !consdata->iscurvchecked )
9992    {
9993       SCIP_CALL( checkCurvature(scip, cons, conshdlrdata->assumeconvex) );
9994    }
9995 
9996    *curvatures = consdata->curvatures;
9997 
9998    return SCIP_OKAY;
9999 }
10000 
10001 /** computes the violation of a nonlinear constraint by a solution */
SCIPgetViolationNonlinear(SCIP * scip,SCIP_CONS * cons,SCIP_SOL * sol,SCIP_Real * violation)10002 SCIP_RETCODE SCIPgetViolationNonlinear(
10003    SCIP*                 scip,               /**< SCIP data structure */
10004    SCIP_CONS*            cons,               /**< constraint */
10005    SCIP_SOL*             sol,                /**< solution which violation to calculate, or NULL for LP solution */
10006    SCIP_Real*            violation           /**< pointer to store violation of constraint */
10007    )
10008 {
10009    SCIP_CONSHDLR* conshdlr;
10010    SCIP_CONSDATA* consdata;
10011    SCIP_Bool      solviolbounds;
10012 
10013    assert(scip != NULL);
10014    assert(cons != NULL);
10015    assert(violation != NULL);
10016 
10017    if( SCIPgetStage(scip) >= SCIP_STAGE_INITPRESOLVE && SCIPgetStage(scip) <= SCIP_STAGE_EXITPRESOLVE && SCIPconsIsActive(cons) )
10018    {
10019       /* @todo make available */
10020       SCIPwarningMessage(scip, "SCIPgetViolationNonlinear is not available for active constraints during presolve.\n");
10021       *violation = SCIP_INVALID;
10022       return SCIP_OKAY;
10023    }
10024 
10025    conshdlr = SCIPconsGetHdlr(cons);
10026    assert(conshdlr != NULL);
10027 
10028    SCIP_CALL( computeViolation(scip, conshdlr, cons, sol, &solviolbounds) );
10029 
10030    if( solviolbounds )
10031    {
10032       SCIPerrorMessage("Solution passed to SCIPgetViolationNonlinear() does not satisfy variable bounds.\n");
10033       return SCIP_ERROR;
10034    }
10035 
10036    consdata = SCIPconsGetData(cons);
10037    assert(consdata != NULL);
10038 
10039    *violation = MAX(consdata->lhsviol, consdata->rhsviol);
10040 
10041    return SCIP_OKAY;
10042 }
10043 
10044 /** get index of a linear variable of a nonlinear constraint that may be decreased without making any other constraint infeasible, or -1 if none */
SCIPgetLinvarMayDecreaseNonlinear(SCIP * scip,SCIP_CONS * cons)10045 int SCIPgetLinvarMayDecreaseNonlinear(
10046    SCIP*                 scip,               /**< SCIP data structure */
10047    SCIP_CONS*            cons                /**< constraint */
10048    )
10049 {
10050    SCIP_CONSDATA* consdata;
10051 
10052    assert(scip != NULL);
10053    assert(cons != NULL);
10054 
10055    consdata = SCIPconsGetData(cons);
10056    assert(consdata != NULL);
10057 
10058    if( consdata->linvar_mayincrease == -1 && consdata->linvar_maydecrease == -1 )
10059       consdataFindUnlockedLinearVar(scip, consdata);
10060 
10061    return consdata->linvar_maydecrease;
10062 }
10063 
10064 /** get index of a linear variable of a nonlinear constraint that may be increased without making any other constraint infeasible, or -1 if none */
SCIPgetLinvarMayIncreaseNonlinear(SCIP * scip,SCIP_CONS * cons)10065 int SCIPgetLinvarMayIncreaseNonlinear(
10066    SCIP*                 scip,               /**< SCIP data structure */
10067    SCIP_CONS*            cons                /**< constraint */
10068    )
10069 {
10070    SCIP_CONSDATA* consdata;
10071 
10072    assert(scip != NULL);
10073    assert(cons != NULL);
10074 
10075    consdata = SCIPconsGetData(cons);
10076    assert(consdata != NULL);
10077 
10078    if( consdata->linvar_mayincrease == -1 && consdata->linvar_maydecrease == -1 )
10079       consdataFindUnlockedLinearVar(scip, consdata);
10080 
10081    return consdata->linvar_mayincrease;
10082 }
10083 
10084 /** gets expression graph of nonlinear constraint handler */
SCIPgetExprgraphNonlinear(SCIP * scip,SCIP_CONSHDLR * conshdlr)10085 SCIP_EXPRGRAPH* SCIPgetExprgraphNonlinear(
10086    SCIP*                 scip,               /**< SCIP data structure */
10087    SCIP_CONSHDLR*        conshdlr            /**< nonlinear constraint handler */
10088    )
10089 {
10090    SCIP_CONSHDLRDATA* conshdlrdata;
10091 
10092    assert(scip != NULL);
10093    assert(conshdlr != NULL);
10094 
10095    conshdlrdata = SCIPconshdlrGetData(conshdlr);
10096    assert(conshdlrdata != NULL);
10097    assert(conshdlrdata->exprgraph != NULL);
10098 
10099    return conshdlrdata->exprgraph;
10100 }
10101 
10102 /** given three points, constructs coefficient of equation for hyperplane generated by these three points
10103  * Three points a, b, and c are given.
10104  * Computes coefficients alpha, beta, gamma, and delta, such that a, b, and c, satisfy
10105  * alpha * x1 + beta * x2 + gamma * x3 = delta and gamma >= 0.0.
10106  */
SCIPcomputeHyperplaneThreePoints(SCIP * scip,SCIP_Real a1,SCIP_Real a2,SCIP_Real a3,SCIP_Real b1,SCIP_Real b2,SCIP_Real b3,SCIP_Real c1,SCIP_Real c2,SCIP_Real c3,SCIP_Real * alpha,SCIP_Real * beta,SCIP_Real * gamma_,SCIP_Real * delta)10107 SCIP_RETCODE SCIPcomputeHyperplaneThreePoints(
10108    SCIP*                 scip,               /**< SCIP data structure */
10109    SCIP_Real             a1,                 /**< first coordinate of a */
10110    SCIP_Real             a2,                 /**< second coordinate of a */
10111    SCIP_Real             a3,                 /**< third coordinate of a */
10112    SCIP_Real             b1,                 /**< first coordinate of b */
10113    SCIP_Real             b2,                 /**< second coordinate of b */
10114    SCIP_Real             b3,                 /**< third coordinate of b */
10115    SCIP_Real             c1,                 /**< first coordinate of c */
10116    SCIP_Real             c2,                 /**< second coordinate of c */
10117    SCIP_Real             c3,                 /**< third coordinate of c */
10118    SCIP_Real*            alpha,              /**< coefficient of first coordinate */
10119    SCIP_Real*            beta,               /**< coefficient of second coordinate */
10120    SCIP_Real*            gamma_,             /**< coefficient of third coordinate */
10121    SCIP_Real*            delta               /**< constant right-hand side */
10122    )
10123 {
10124    assert(scip != NULL);
10125    assert(alpha != NULL);
10126    assert(beta  != NULL);
10127    assert(gamma_ != NULL);
10128    assert(delta != NULL);
10129 
10130    *alpha  = -b3*c2 + a3*(-b2+c2) + a2*(b3-c3) + b2*c3;
10131    *beta   = -(-b3*c1 + a3*(-b1+c1) + a1*(b3-c3) + b1*c3);
10132    *gamma_ = -a2*b1 + a1*b2 + a2*c1 - b2*c1 - a1*c2 + b1*c2;
10133    *delta  = -a3*b2*c1 + a2*b3*c1 + a3*b1*c2 - a1*b3*c2 - a2*b1*c3 + a1*b2*c3;
10134 
10135    /* SCIPdebugMsg(scip, "alpha: %g beta: %g gamma: %g delta: %g\n", *alpha, *beta, *gamma_, *delta); */
10136 
10137    if( SCIPisInfinity(scip, REALABS(*gamma_ * a3)) ||
10138       SCIPisInfinity(scip, REALABS(*gamma_ * b3)) ||
10139       SCIPisInfinity(scip, REALABS(*gamma_ * c3)) )
10140    {
10141       SCIPdebugMsg(scip, "activity above SCIP infinity\n");
10142       *delta  = 0.0;
10143       *alpha  = 0.0;
10144       *beta   = 0.0;
10145       *gamma_ = 0.0;
10146       return SCIP_OKAY;
10147    }
10148 
10149    /* check if hyperplane contains all three points (necessary because of numerical troubles) */
10150    if( !SCIPisRelEQ(scip, *alpha * a1 + *beta * a2 - *delta, -*gamma_ * a3) ||
10151       !SCIPisRelEQ(scip, *alpha * b1 + *beta * b2 - *delta, -*gamma_ * b3) ||
10152       !SCIPisRelEQ(scip, *alpha * c1 + *beta * c2 - *delta, -*gamma_ * c3) )
10153    {
10154       SCIP_Real m[9];
10155       SCIP_Real rhs[3];
10156       SCIP_Real x[3];
10157       SCIP_Bool success;
10158 
10159       /*
10160       SCIPdebugMsg(scip, "a = (%g,%g,%g) hyperplane: %g rhs %g EQdelta: %d\n", a1, a2, a3, *alpha * a1 + *beta * a2 - *delta, -*gamma_ * a3, SCIPisRelEQ(scip, *alpha * a1 + *beta * a2 - *delta, -*gamma_ * a3));
10161       SCIPdebugMsg(scip, "b = (%g,%g,%g) hyperplane: %g rhs %g EQdelta: %d\n", b1, b2, b3, *alpha * b1 + *beta * b2 - *delta, -*gamma_ * b3, SCIPisRelEQ(scip, *alpha * b1 + *beta * b2 - *delta, -*gamma_ * b3));
10162       SCIPdebugMsg(scip, "c = (%g,%g,%g) hyperplane: %g rhs %g EQdelta: %d\n", c1, c2, c3, *alpha * c1 + *beta * c2 - *delta, -*gamma_ * c3, SCIPisRelEQ(scip, *alpha * c1 + *beta * c2 - *delta, -*gamma_ * c3));
10163       */
10164 
10165       /* initialize matrix column-wise */
10166       m[0] = a1;
10167       m[1] = b1;
10168       m[2] = c1;
10169       m[3] = a2;
10170       m[4] = b2;
10171       m[5] = c2;
10172       m[6] = a3;
10173       m[7] = b3;
10174       m[8] = c3;
10175 
10176       rhs[0] = 1.0;
10177       rhs[1] = 1.0;
10178       rhs[2] = 1.0;
10179 
10180       SCIPdebugMsg(scip, "numerical troubles - try to solve the linear system via an LU factorization\n");
10181 
10182       /* solve the linear problem */
10183       SCIP_CALL( SCIPsolveLinearProb(3, m, rhs, x, &success) );
10184       /* assert(success); */
10185 
10186       *delta  = rhs[0];
10187       *alpha  = x[0];
10188       *beta   = x[1];
10189       *gamma_ = x[2];
10190 
10191       /* set all coefficients to zero if one of the points is not contained in the hyperplane; this ensures that we do
10192        * not add a cut to SCIP and that all assertions are trivially fulfilled
10193        */
10194       if( !success || !SCIPisRelEQ(scip, *alpha * a1 + *beta * a2 - *delta, -*gamma_ * a3) ||
10195          !SCIPisRelEQ(scip, *alpha * b1 + *beta * b2 - *delta, -*gamma_ * b3) ||
10196          !SCIPisRelEQ(scip, *alpha * c1 + *beta * c2 - *delta, -*gamma_ * c3) ) /*lint !e774*/
10197       {
10198          SCIPdebugMsg(scip, "could not resolve numerical difficulties\n");
10199          *delta  = 0.0;
10200          *alpha  = 0.0;
10201          *beta   = 0.0;
10202          *gamma_ = 0.0;
10203       }
10204    }
10205 
10206    if( *gamma_ < 0.0 )
10207    {
10208       *alpha  = -*alpha;
10209       *beta   = -*beta;
10210       *gamma_ = -*gamma_;
10211       *delta  = -*delta;
10212    }
10213 
10214    return SCIP_OKAY;
10215 }
10216