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