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 specified by algebraic expressions
19 * @author Ksenia Bestuzheva
20 * @author Benjamin Mueller
21 * @author Felipe Serrano
22 * @author Stefan Vigerske
23 */
24
25 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
26
27 #ifdef SCIP_DEBUG
28 #define ENFO_LOGGING
29 #endif
30
31 /* enable to get log output for enforcement */
32 /* #define ENFO_LOGGING */
33 /* define to get enforcement logging into file */
34 /* #define ENFOLOGFILE "consexpr_enfo.log" */
35
36 /* define to get more debug output from domain propagation */
37 /* #define DEBUG_PROP */
38
39 /*lint -e440*/
40 /*lint -e441*/
41 /*lint -e528*/
42 /*lint -e666*/
43 /*lint -e777*/
44 /*lint -e866*/
45
46 #include <assert.h>
47 #include <ctype.h>
48
49 #include "scip/cons_nonlinear.h"
50 #include "scip/nlhdlr.h"
51 #include "scip/expr_var.h"
52 #include "scip/expr_sum.h"
53 #include "scip/expr_value.h"
54 #include "scip/expr_pow.h"
55 #include "scip/nlhdlr_convex.h"
56 #include "scip/cons_linear.h"
57 #include "scip/cons_varbound.h"
58 #include "scip/cons_and.h"
59 #include "scip/cons_bounddisjunction.h"
60 #include "scip/heur_subnlp.h"
61 #include "scip/heur_trysol.h"
62 #include "scip/nlpi_ipopt.h" /* for SCIPsolveLinearEquationsIpopt */
63 #include "scip/debug.h"
64 #include "scip/dialog_default.h"
65
66 /* fundamental constraint handler properties */
67 #define CONSHDLR_NAME "nonlinear"
68 #define CONSHDLR_DESC "handler for nonlinear constraints specified by algebraic expressions"
69 #define CONSHDLR_ENFOPRIORITY -60 /**< priority of the constraint handler for constraint enforcing */
70 #define CONSHDLR_CHECKPRIORITY -4000010 /**< priority of the constraint handler for checking feasibility */
71 #define CONSHDLR_EAGERFREQ 100 /**< frequency for using all instead of only the useful constraints in separation,
72 * propagation and enforcement, -1 for no eager evaluations, 0 for first only */
73 #define CONSHDLR_NEEDSCONS TRUE /**< should the constraint handler be skipped, if no constraints are available? */
74
75 /* optional constraint handler properties */
76 #define CONSHDLR_SEPAPRIORITY 10 /**< priority of the constraint handler for separation */
77 #define CONSHDLR_SEPAFREQ 1 /**< frequency for separating cuts; zero means to separate only in the root node */
78 #define CONSHDLR_DELAYSEPA FALSE /**< should separation method be delayed, if other separators found cuts? */
79
80 #define CONSHDLR_PROPFREQ 1 /**< frequency for propagating domains; zero means only preprocessing propagation */
81 #define CONSHDLR_DELAYPROP FALSE /**< should propagation method be delayed, if other propagators found reductions? */
82 #define CONSHDLR_PROP_TIMING SCIP_PROPTIMING_BEFORELP /**< propagation timing mask of the constraint handler*/
83
84 #define CONSHDLR_PRESOLTIMING SCIP_PRESOLTIMING_ALWAYS /**< presolving timing of the constraint handler (fast, medium, or exhaustive) */
85 #define CONSHDLR_MAXPREROUNDS -1 /**< maximal number of presolving rounds the constraint handler participates in (-1: no limit) */
86
87 /* properties of the nonlinear constraint handler statistics table */
88 #define TABLE_NAME_NONLINEAR "cons_nonlinear"
89 #define TABLE_DESC_NONLINEAR "nonlinear constraint handler statistics"
90 #define TABLE_POSITION_NONLINEAR 14600 /**< the position of the statistics table */
91 #define TABLE_EARLIEST_STAGE_NONLINEAR SCIP_STAGE_TRANSFORMED /**< output of the statistics table is only printed from this stage onwards */
92
93 /* properties of the nonlinear handler statistics table */
94 #define TABLE_NAME_NLHDLR "nlhdlr"
95 #define TABLE_DESC_NLHDLR "nonlinear handler statistics"
96 #define TABLE_POSITION_NLHDLR 14601 /**< the position of the statistics table */
97 #define TABLE_EARLIEST_STAGE_NLHDLR SCIP_STAGE_PRESOLVING /**< output of the statistics table is only printed from this stage onwards */
98
99 #define DIALOG_NAME "nlhdlrs"
100 #define DIALOG_DESC "display nonlinear handlers"
101 #define DIALOG_ISSUBMENU FALSE
102
103 #define VERTEXPOLY_MAXPERTURBATION 1e-3 /**< maximum perturbation */
104 #define VERTEXPOLY_USEDUALSIMPLEX TRUE /**< use dual or primal simplex algorithm? */
105 #define VERTEXPOLY_RANDNUMINITSEED 20181029 /**< seed for random number generator, which is used to move points away from the boundary */
106 #define VERTEXPOLY_ADJUSTFACETFACTOR 1e1 /**< adjust resulting facets in checkRikun() up to a violation of this value times lpfeastol */
107
108 #define BRANCH_RANDNUMINITSEED 20191229 /**< seed for random number generator, which is used to select from several similar good branching candidates */
109
110 #define BILIN_MAXNAUXEXPRS 10 /**< maximal number of auxiliary expressions per bilinear term */
111
112 /** translate from one value of infinity to another
113 *
114 * if val is ≥ infty1, then give infty2, else give val
115 */
116 #define infty2infty(infty1, infty2, val) ((val) >= (infty1) ? (infty2) : (val))
117
118 /** translates x to 2^x for non-negative integer x */
119 #define POWEROFTWO(x) (0x1u << (x))
120
121 #ifdef ENFO_LOGGING
122 #define ENFOLOG(x) if( SCIPgetSubscipDepth(scip) == 0 && SCIPgetVerbLevel(scip) >= SCIP_VERBLEVEL_NORMAL ) { x }
123 FILE* enfologfile = NULL;
124 #else
125 #define ENFOLOG(x)
126 #endif
127
128 /*
129 * Data structures
130 */
131
132 /** enforcement data of an expression */
133 typedef struct
134 {
135 SCIP_NLHDLR* nlhdlr; /**< nonlinear handler */
136 SCIP_NLHDLREXPRDATA* nlhdlrexprdata; /**< data of nonlinear handler */
137 SCIP_NLHDLR_METHOD nlhdlrparticipation; /**< methods where nonlinear handler participates */
138 SCIP_Bool issepainit; /**< was the initsepa callback of nlhdlr called */
139 SCIP_Real auxvalue; /**< auxiliary value of expression w.r.t. currently enforced solution */
140 SCIP_Bool sepabelowusesactivity;/**< whether sepabelow uses activity of some expression */
141 SCIP_Bool sepaaboveusesactivity;/**< whether sepaabove uses activity of some expression */
142 } EXPRENFO;
143
144 /** data stored by constraint handler in an expression that belongs to a nonlinear constraint */
145 struct SCIP_Expr_OwnerData
146 {
147 SCIP_CONSHDLR* conshdlr; /** nonlinear constraint handler */
148
149 /* locks and monotonicity */
150 int nlockspos; /**< positive locks counter */
151 int nlocksneg; /**< negative locks counter */
152 SCIP_MONOTONE* monotonicity; /**< array containing monotonicity of expression w.r.t. each child */
153 int monotonicitysize; /**< length of monotonicity array */
154
155 /* propagation (in addition to activity that is stored in expr) */
156 SCIP_INTERVAL propbounds; /**< bounds to propagate in reverse propagation */
157 unsigned int propboundstag; /**< tag to indicate whether propbounds are valid for the current propagation rounds */
158 SCIP_Bool inpropqueue; /**< whether expression is queued for propagation */
159
160 /* enforcement of expr == auxvar (or expr <= auxvar, or expr >= auxvar) */
161 EXPRENFO** enfos; /**< enforcements */
162 int nenfos; /**< number of enforcements, or -1 if not initialized */
163 unsigned int lastenforced; /**< last enforcement round where expression was enforced successfully */
164 unsigned int nactivityusesprop; /**< number of nonlinear handlers whose activity computation (or domain propagation) depends on the activity of the expression */
165 unsigned int nactivityusessepa; /**< number of nonlinear handlers whose separation (estimate or enfo) depends on the activity of the expression */
166 unsigned int nauxvaruses; /**< number of nonlinear handlers whose separation uses an auxvar in the expression */
167 SCIP_VAR* auxvar; /**< auxiliary variable used for outer approximation cuts */
168
169 /* branching */
170 SCIP_Real violscoresum; /**< sum of violation scores for branching stored for this expression */
171 SCIP_Real violscoremax; /**< max of violation scores for branching stored for this expression */
172 int nviolscores; /**< number of violation scores stored for this expression */
173 unsigned int violscoretag; /**< tag to decide whether a violation score of an expression needs to be initialized */
174
175 /* additional data for variable expressions (TODO move into sub-struct?) */
176 SCIP_CONS** conss; /**< constraints in which this variable appears */
177 int nconss; /**< current number of constraints in conss */
178 int consssize; /**< length of conss array */
179 SCIP_Bool consssorted; /**< is the array of constraints sorted */
180
181 int filterpos; /**< position of eventdata in SCIP's event filter, -1 if not catching events */
182 };
183
184 /** constraint data for nonlinear constraints */
185 struct SCIP_ConsData
186 {
187 /* data that defines the constraint: expression and sides */
188 SCIP_EXPR* expr; /**< expression that represents this constraint */
189 SCIP_Real lhs; /**< left-hand side */
190 SCIP_Real rhs; /**< right-hand side */
191
192 /* variables */
193 SCIP_EXPR** varexprs; /**< array containing all variable expressions */
194 int nvarexprs; /**< total number of variable expressions */
195 SCIP_Bool catchedevents; /**< do we catch events on variables? */
196
197 /* constraint violation */
198 SCIP_Real lhsviol; /**< violation of left-hand side by current solution */
199 SCIP_Real rhsviol; /**< violation of right-hand side by current solution */
200 SCIP_Real gradnorm; /**< norm of gradient of constraint function in current solution (if evaluated) */
201 SCIP_Longint gradnormsoltag; /**< tag of solution used that gradnorm corresponds to */
202
203 /* status flags */
204 unsigned int ispropagated:1; /**< did we propagate the current bounds already? */
205 unsigned int issimplified:1; /**< did we simplify the expression tree already? */
206
207 /* locks */
208 int nlockspos; /**< number of positive locks */
209 int nlocksneg; /**< number of negative locks */
210
211 /* repair infeasible solutions */
212 SCIP_VAR* linvardecr; /**< variable that may be decreased without making any other constraint infeasible, or NULL if none */
213 SCIP_VAR* linvarincr; /**< variable that may be increased without making any other constraint infeasible, or NULL if none */
214 SCIP_Real linvardecrcoef; /**< linear coefficient of linvardecr */
215 SCIP_Real linvarincrcoef; /**< linear coefficient of linvarincr */
216
217 /* miscellaneous */
218 SCIP_EXPRCURV curv; /**< curvature of the root expression w.r.t. the original variables */
219 SCIP_NLROW* nlrow; /**< a nonlinear row representation of this constraint */
220 int consindex; /**< an index of the constraint that is unique among all expr-constraints in this SCIP instance and is constant */
221 };
222
223 /** constraint upgrade method */
224 typedef struct
225 {
226 SCIP_DECL_NONLINCONSUPGD((*consupgd)); /**< method to call for upgrading nonlinear constraint */
227 int priority; /**< priority of upgrading method */
228 SCIP_Bool active; /**< is upgrading enabled */
229 } CONSUPGRADE;
230
231 /** constraint handler data */
232 struct SCIP_ConshdlrData
233 {
234 /* nonlinear handler */
235 SCIP_NLHDLR** nlhdlrs; /**< nonlinear handlers */
236 int nnlhdlrs; /**< number of nonlinear handlers */
237 int nlhdlrssize; /**< size of nlhdlrs array */
238 SCIP_Bool indetect; /**< whether we are currently in detectNlhdlr */
239 SCIP_Bool registerusesactivitysepabelow; /**< a flag that is used only during \ref @detectNlhdlr() */
240 SCIP_Bool registerusesactivitysepaabove; /**< a flag that is used only during \ref @detectNlhdlr() */
241
242 /* constraint upgrades */
243 CONSUPGRADE** consupgrades; /**< constraint upgrade methods for specializing nonlinear constraints */
244 int consupgradessize; /**< size of consupgrades array */
245 int nconsupgrades; /**< number of constraint upgrade methods */
246
247 /* other plugins */
248 SCIP_EVENTHDLR* eventhdlr; /**< handler for variable bound change events */
249 SCIP_HEUR* subnlpheur; /**< a pointer to the subnlp heuristic, if available */
250 SCIP_HEUR* trysolheur; /**< a pointer to the trysol heuristic, if available */
251
252 /* tags and counters */
253 int auxvarid; /**< unique id for the next auxiliary variable */
254 SCIP_Longint curboundstag; /**< tag indicating current variable bounds */
255 SCIP_Longint lastboundrelax; /**< tag when bounds where most recently relaxed */
256 SCIP_Longint lastvaractivitymethodchange; /**< tag when method used to evaluate activity of variables changed last */
257 unsigned int enforound; /**< total number of enforcement calls, including current one */
258 int lastconsindex; /**< last used consindex, plus one */
259
260 /* activity intervals and domain propagation */
261 SCIP_DECL_EXPR_INTEVALVAR((*intevalvar)); /**< method currently used for activity calculation of variable expressions */
262 SCIP_Bool globalbounds; /**< whether global variable bounds should be used for activity calculation */
263 SCIP_QUEUE* reversepropqueue; /**< expression queue to be used in reverse propagation, filled by SCIPtightenExprIntervalNonlinear */
264 SCIP_Bool forceboundtightening; /**< whether bound change passed to SCIPtightenExprIntervalNonlinear should be forced */
265 unsigned int curpropboundstag; /**< tag indicating current propagation rounds, to match with expr->propboundstag */
266
267 /* parameters */
268 int maxproprounds; /**< limit on number of propagation rounds for a set of constraints within one round of SCIP propagation */
269 SCIP_Bool propauxvars; /**< whether to check bounds of all auxiliary variable to seed reverse propagation */
270 char varboundrelax; /**< strategy on how to relax variable bounds during bound tightening */
271 SCIP_Real varboundrelaxamount; /**< by how much to relax variable bounds during bound tightening */
272 SCIP_Real conssiderelaxamount; /**< by how much to relax constraint sides during bound tightening */
273 SCIP_Real vp_maxperturb; /**< maximal relative perturbation of reference point */
274 SCIP_Real vp_adjfacetthreshold; /**< adjust computed facet up to a violation of this value times lpfeastol */
275 SCIP_Bool vp_dualsimplex; /**< whether to use dual simplex instead of primal simplex for facet computing LP */
276 SCIP_Bool reformbinprods; /**< whether to reformulate products of binary variables during presolving */
277 SCIP_Bool reformbinprodsand; /**< whether to use the AND constraint handler for reformulating binary products */
278 int reformbinprodsfac; /**< minimum number of terms to reformulate bilinear binary products by factorizing variables (<= 1: disabled) */
279 SCIP_Bool forbidmultaggrnlvar; /**< whether to forbid multiaggregation of variables that appear in a nonlinear term of a constraint */
280 SCIP_Bool tightenlpfeastol; /**< whether to tighten LP feasibility tolerance during enforcement, if it seems useful */
281 SCIP_Bool propinenforce; /**< whether to (re)run propagation in enforcement */
282 SCIP_Real weakcutthreshold; /**< threshold for when to regard a cut from an estimator as weak */
283 SCIP_Real strongcutmaxcoef; /**< "strong" cuts will be scaled to have their maximal coef in [1/strongcutmaxcoef,strongcutmaxcoef] */
284 SCIP_Bool strongcutefficacy; /**< consider efficacy requirement when deciding whether a cut is "strong" */
285 SCIP_Bool forcestrongcut; /**< whether to force "strong" cuts in enforcement */
286 SCIP_Real enfoauxviolfactor; /**< an expression will be enforced if the "auxiliary" violation is at least enfoauxviolfactor times the "original" violation */
287 SCIP_Real weakcutminviolfactor; /**< retry with weak cuts for constraints with violation at least this factor of maximal violated constraints */
288 char rownotremovable; /**< whether to make rows to be non-removable in the node where they are added (can prevent some cycling): 'o'ff, in 'e'nforcement only, 'a'lways */
289 char violscale; /**< method how to scale violations to make them comparable (not used for feasibility check) */
290 char checkvarlocks; /**< whether variables contained in a single constraint should be forced to be at their lower or upper bounds ('d'isable, change 't'ype, add 'b'ound disjunction) */
291 int branchauxmindepth; /**< from which depth on to allow branching on auxiliary variables */
292 SCIP_Bool branchexternal; /**< whether to use external branching candidates for branching */
293 SCIP_Real branchhighviolfactor; /**< consider a constraint highly violated if its violation is >= this factor * maximal violation among all constraints */
294 SCIP_Real branchhighscorefactor; /**< consider a variable branching score high if its branching score >= this factor * maximal branching score among all variables */
295 SCIP_Real branchviolweight; /**< weight by how much to consider the violation assigned to a variable for its branching score */
296 SCIP_Real branchdualweight; /**< weight by how much to consider the dual values of rows that contain a variable for its branching score */
297 SCIP_Real branchpscostweight; /**< weight by how much to consider the pseudo cost of a variable for its branching score */
298 SCIP_Real branchdomainweight; /**< weight by how much to consider the domain width in branching score */
299 SCIP_Real branchvartypeweight;/**< weight by how much to consider variable type in branching score */
300 char branchscoreagg; /**< how to aggregate several branching scores given for the same expression ('a'verage, 'm'aximum, or 's'um) */
301 char branchviolsplit; /**< method used to split violation in expression onto variables ('u'niform, 'm'idness of solution, 'd'omain width, 'l'ogarithmic domain width) */
302 SCIP_Real branchpscostreliable; /**< minimum pseudo-cost update count required to consider pseudo-costs reliable */
303 char linearizeheursol; /**< whether tight linearizations of nonlinear constraints should be added to cutpool when some heuristics finds a new solution ('o'ff, on new 'i'ncumbents, on 'e'very solution) */
304
305 /* statistics */
306 SCIP_Longint nweaksepa; /**< number of times we used "weak" cuts for enforcement */
307 SCIP_Longint ntightenlp; /**< number of times we requested solving the LP with a smaller feasibility tolerance when enforcing */
308 SCIP_Longint ndesperatetightenlp; /**< number of times we requested solving the LP with a smaller feasibility tolerance when enforcing because we didn't know anything better */
309 SCIP_Longint ndesperatebranch; /**< number of times we branched on some variable because normal enforcement was not successful */
310 SCIP_Longint ndesperatecutoff; /**< number of times we cut off a node in enforcement because no branching candidate could be found */
311 SCIP_Longint nforcelp; /**< number of times we forced solving the LP when enforcing a pseudo solution */
312 SCIP_CLOCK* canonicalizetime; /**< time spend for canonicalization */
313 SCIP_Longint ncanonicalizecalls; /**< number of times we called canonicalization */
314
315 /* facets of envelops of vertex-polyhedral functions */
316 SCIP_RANDNUMGEN* vp_randnumgen; /**< random number generator used to perturb reference point */
317 SCIP_LPI* vp_lp[SCIP_MAXVERTEXPOLYDIM+1]; /**< LPs used to compute facets for functions of different dimension */
318
319 /* hashing of bilinear terms */
320 SCIP_HASHTABLE* bilinhashtable; /**< hash table for bilinear terms */
321 SCIP_CONSNONLINEAR_BILINTERM* bilinterms; /**< bilinear terms */
322 int nbilinterms; /**< total number of bilinear terms */
323 int bilintermssize; /**< size of bilinterms array */
324 int bilinmaxnauxexprs; /**< maximal number of auxiliary expressions per bilinear term */
325
326 /* branching */
327 SCIP_RANDNUMGEN* branchrandnumgen; /**< random number generated used in branching variable selection */
328 char branchpscostupdatestrategy; /**< value of parameter branching/lpgainnormalize */
329
330 /* misc */
331 SCIP_Bool checkedvarlocks; /**< whether variables contained in a single constraint have been already considered */
332 SCIP_HASHMAP* var2expr; /**< hashmap to map SCIP variables to variable-expressions */
333 int newsoleventfilterpos; /**< filter position of new solution event handler, if caught */
334 };
335
336 /** branching candidate with various scores */
337 typedef struct
338 {
339 SCIP_EXPR* expr; /**< expression that holds branching candidate */
340 SCIP_Real auxviol; /**< aux-violation score of candidate */
341 SCIP_Real domain; /**< domain score of candidate */
342 SCIP_Real dual; /**< dual score of candidate */
343 SCIP_Real pscost; /**< pseudo-cost score of candidate */
344 SCIP_Real vartype; /**< variable type score of candidate */
345 SCIP_Real weighted; /**< weighted sum of other scores, see scoreBranchingCandidates() */
346 } BRANCHCAND;
347
348 /*
349 * Local methods
350 */
351
352 /* forward declaration */
353 static
354 SCIP_RETCODE forwardPropExpr(
355 SCIP* scip, /**< SCIP data structure */
356 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
357 SCIP_EXPR* rootexpr, /**< expression */
358 SCIP_Bool tightenauxvars, /**< should the bounds of auxiliary variables be tightened? */
359 SCIP_Bool* infeasible, /**< buffer to store whether the problem is infeasible (NULL if not needed) */
360 int* ntightenings /**< buffer to store the number of auxiliary variable tightenings (NULL if not needed) */
361 );
362
363 /** frees auxiliary variables of expression, if any */
364 static
365 SCIP_RETCODE freeAuxVar(
366 SCIP* scip, /**< SCIP data structure */
367 SCIP_EXPR* expr /**< expression which auxvar to free, if any */
368 )
369 {
370 SCIP_EXPR_OWNERDATA* mydata;
371
372 assert(scip != NULL);
373 assert(expr != NULL);
374
375 mydata = SCIPexprGetOwnerData(expr);
376 assert(mydata != NULL);
377
378 if( mydata->auxvar == NULL )
379 return SCIP_OKAY;
380
381 SCIPdebugMsg(scip, "remove auxiliary variable <%s> for expression %p\n", SCIPvarGetName(mydata->auxvar), (void*)expr);
382
383 /* remove variable locks
384 * as this is a relaxation-only variable, no other plugin should use it for deducing any type of reductions or cutting planes
385 */
386 SCIP_CALL( SCIPaddVarLocks(scip, mydata->auxvar, -1, -1) );
387
388 /* release auxiliary variable */
389 SCIP_CALL( SCIPreleaseVar(scip, &mydata->auxvar) );
390 assert(mydata->auxvar == NULL);
391
392 return SCIP_OKAY;
393 }
394
395 /** frees data used for enforcement of expression, that is, nonlinear handlers
396 *
397 * can also clear indicators whether expr needs enforcement methods, that is,
398 * free an associated auxiliary variable and reset the nactivityuses counts
399 */
400 static
401 SCIP_RETCODE freeEnfoData(
402 SCIP* scip, /**< SCIP data structure */
403 SCIP_EXPR* expr, /**< expression whose enforcement data will be released */
404 SCIP_Bool freeauxvar /**< whether aux var should be released and activity usage counts be reset */
405 )
406 {
407 SCIP_EXPR_OWNERDATA* mydata;
408 int e;
409
410 mydata = SCIPexprGetOwnerData(expr);
411 assert(mydata != NULL);
412
413 if( freeauxvar )
414 {
415 /* free auxiliary variable */
416 SCIP_CALL( freeAuxVar(scip, expr) );
417 assert(mydata->auxvar == NULL);
418
419 /* reset count on activity and auxvar usage */
420 mydata->nactivityusesprop = 0;
421 mydata->nactivityusessepa = 0;
422 mydata->nauxvaruses = 0;
423 }
424
425 /* free data stored by nonlinear handlers */
426 for( e = 0; e < mydata->nenfos; ++e )
427 {
428 SCIP_NLHDLR* nlhdlr;
429
430 assert(mydata->enfos[e] != NULL);
431
432 nlhdlr = mydata->enfos[e]->nlhdlr;
433 assert(nlhdlr != NULL);
434
435 if( mydata->enfos[e]->issepainit )
436 {
437 /* call the separation deinitialization callback of the nonlinear handler */
438 SCIP_CALL( SCIPnlhdlrExitsepa(scip, nlhdlr, expr, mydata->enfos[e]->nlhdlrexprdata) );
439 mydata->enfos[e]->issepainit = FALSE;
440 }
441
442 /* free nlhdlr exprdata, if there is any and there is a method to free this data */
443 if( mydata->enfos[e]->nlhdlrexprdata != NULL )
444 {
445 SCIP_CALL( SCIPnlhdlrFreeexprdata(scip, nlhdlr, expr, &mydata->enfos[e]->nlhdlrexprdata) );
446 assert(mydata->enfos[e]->nlhdlrexprdata == NULL);
447 }
448
449 /* free enfo data */
450 SCIPfreeBlockMemory(scip, &mydata->enfos[e]);
451 }
452
453 /* free array with enfo data */
454 SCIPfreeBlockMemoryArrayNull(scip, &mydata->enfos, mydata->nenfos);
455
456 /* we need to look at this expression in detect again */
457 mydata->nenfos = -1;
458
459 return SCIP_OKAY;
460 }
461
462 /** callback that frees data that this conshdlr stored in an expression */
463 static
464 SCIP_DECL_EXPR_OWNERFREE(exprownerFree)
465 {
466 assert(scip != NULL);
467 assert(expr != NULL);
468 assert(ownerdata != NULL);
469 assert(*ownerdata != NULL);
470
471 /* expression should not be locked anymore */
472 assert((*ownerdata)->nlockspos == 0);
473 assert((*ownerdata)->nlocksneg == 0);
474
475 SCIP_CALL( freeEnfoData(scip, expr, TRUE) );
476
477 /* expression should not be enforced anymore */
478 assert((*ownerdata)->nenfos <= 0);
479 assert((*ownerdata)->auxvar == NULL);
480
481 if( SCIPisExprVar(scip, expr) )
482 {
483 SCIP_CONSHDLRDATA* conshdlrdata;
484 SCIP_VAR* var;
485
486 /* there should be no constraints left that still use this variable */
487 assert((*ownerdata)->nconss == 0);
488 /* thus, there should also be no variable event catched (via this exprhdlr) */
489 assert((*ownerdata)->filterpos == -1);
490
491 SCIPfreeBlockMemoryArrayNull(scip, &(*ownerdata)->conss, (*ownerdata)->consssize);
492
493 /* update var2expr hashmap in conshdlrdata */
494 conshdlrdata = SCIPconshdlrGetData((*ownerdata)->conshdlr);
495 assert(conshdlrdata != NULL);
496
497 var = SCIPgetVarExprVar(expr);
498 assert(var != NULL);
499
500 /* remove var -> expr map from hashmap if present
501 * (if no variable-expression stored for var hashmap, then the var hasn't been used in any constraint, so do nothing
502 * if variable-expression stored for var is different, then also do nothing)
503 */
504 if( SCIPhashmapGetImage(conshdlrdata->var2expr, var) == (void*)expr )
505 {
506 SCIP_CALL( SCIPhashmapRemove(conshdlrdata->var2expr, var) );
507 }
508 }
509
510 SCIPfreeBlockMemory(scip, ownerdata);
511
512 return SCIP_OKAY;
513 }
514
515 static
516 SCIP_DECL_EXPR_OWNERPRINT(exprownerPrint)
517 { /*lint --e{715}*/
518 assert(ownerdata != NULL);
519
520 /* print nl handlers associated to expr */
521 if( ownerdata->nenfos > 0 )
522 {
523 int i;
524 SCIPinfoMessage(scip, file, " {");
525
526 for( i = 0; i < ownerdata->nenfos; ++i )
527 {
528 SCIPinfoMessage(scip, file, "%s:", SCIPnlhdlrGetName(ownerdata->enfos[i]->nlhdlr));
529 if( ownerdata->enfos[i]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_ACTIVITY )
530 SCIPinfoMessage(scip, file, "a");
531 if( ownerdata->enfos[i]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPABELOW )
532 SCIPinfoMessage(scip, file, "u");
533 if( ownerdata->enfos[i]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPAABOVE )
534 SCIPinfoMessage(scip, file, "o");
535 if( i < ownerdata->nenfos-1 )
536 SCIPinfoMessage(scip, file, ", ");
537 }
538
539 SCIPinfoMessage(scip, file, "}");
540 }
541
542 /* print aux var associated to expr */
543 if( ownerdata->auxvar != NULL )
544 {
545 SCIPinfoMessage(scip, file, " (<%s> in [%g, %g])", SCIPvarGetName(ownerdata->auxvar), SCIPvarGetLbLocal(ownerdata->auxvar), SCIPvarGetUbLocal(ownerdata->auxvar));
546 }
547 SCIPinfoMessage(scip, file, "\n");
548
549 return SCIP_OKAY;
550 }
551
552 /** possibly reevaluates and then returns the activity of the expression
553 *
554 * Reevaluate activity if currently stored is not up to date (some bound was changed since last evaluation).
555 */
556 static
557 SCIP_DECL_EXPR_OWNEREVALACTIVITY(exprownerEvalactivity)
558 {
559 SCIP_CONSHDLRDATA* conshdlrdata;
560
561 assert(scip != NULL);
562 assert(expr != NULL);
563 assert(ownerdata != NULL);
564
565 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr);
566 assert(conshdlrdata != NULL);
567
568 if( SCIPexprGetActivityTag(expr) < conshdlrdata->curboundstag )
569 {
570 /* update activity of expression */
571 SCIP_CALL( forwardPropExpr(scip, ownerdata->conshdlr, expr, FALSE, NULL, NULL) );
572
573 assert(SCIPexprGetActivityTag(expr) == conshdlrdata->curboundstag);
574 }
575
576 return SCIP_OKAY;
577 }
578
579 /** callback that creates data that this conshdlr wants to store in an expression */
580 static
581 SCIP_DECL_EXPR_OWNERCREATE(exprownerCreate)
582 {
583 assert(scip != NULL);
584 assert(expr != NULL);
585 assert(ownerdata != NULL);
586
587 SCIP_CALL( SCIPallocClearBlockMemory(scip, ownerdata) );
588 (*ownerdata)->nenfos = -1;
589 (*ownerdata)->conshdlr = (SCIP_CONSHDLR*)ownercreatedata;
590
591 if( SCIPisExprVar(scip, expr) )
592 {
593 SCIP_CONSHDLRDATA* conshdlrdata;
594 SCIP_VAR* var;
595
596 (*ownerdata)->filterpos = -1;
597
598 /* add to var2expr hashmap if not having expr for var yet */
599
600 conshdlrdata = SCIPconshdlrGetData((*ownerdata)->conshdlr);
601 assert(conshdlrdata != NULL);
602
603 var = SCIPgetVarExprVar(expr);
604
605 if( !SCIPhashmapExists(conshdlrdata->var2expr, (void*)var) )
606 {
607 /* store the variable expression in the hashmap */
608 SCIP_CALL( SCIPhashmapInsert(conshdlrdata->var2expr, (void*)var, (void*)expr) );
609 }
610 else
611 {
612 /* if expr was just created, then it shouldn't already be stored as image of var */
613 assert(SCIPhashmapGetImage(conshdlrdata->var2expr, (void*)var) != (void*)expr);
614 }
615 }
616 else
617 {
618 /* just so that we can use filterpos to recognize whether an expr is a varexpr if not having a SCIP pointer around */
619 (*ownerdata)->filterpos = -2;
620 }
621
622 *ownerfree = exprownerFree;
623 *ownerprint = exprownerPrint;
624 *ownerevalactivity = exprownerEvalactivity;
625
626 return SCIP_OKAY;
627 }
628
629 /** creates a variable expression or retrieves from hashmap in conshdlr data */
630 static
631 SCIP_RETCODE createExprVar(
632 SCIP* scip, /**< SCIP data structure */
633 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */
634 SCIP_EXPR** expr, /**< pointer where to store expression */
635 SCIP_VAR* var /**< variable to be stored */
636 )
637 {
638 assert(conshdlr != NULL);
639 assert(expr != NULL);
640 assert(var != NULL);
641
642 /* get variable expression representing the given variable if there is one already */
643 *expr = (SCIP_EXPR*) SCIPhashmapGetImage(SCIPconshdlrGetData(conshdlr)->var2expr, (void*) var);
644
645 if( *expr == NULL )
646 {
647 /* create a new variable expression; this also captures the expression */
648 SCIP_CALL( SCIPcreateExprVar(scip, expr, var, exprownerCreate, (void*)conshdlr) );
649 assert(*expr != NULL);
650 /* exprownerCreate should have added var->expr to var2expr */
651 assert(SCIPhashmapGetImage(SCIPconshdlrGetData(conshdlr)->var2expr, (void*)var) == (void*)*expr);
652 }
653 else
654 {
655 /* only capture already existing expr to get a consistent uses-count */
656 SCIPcaptureExpr(*expr);
657 }
658
659 return SCIP_OKAY;
660 }
661
662 /* map var exprs to var-expr from var2expr hashmap */
663 static
664 SCIP_DECL_EXPR_MAPEXPR(mapexprvar)
665 { /*lint --e{715}*/
666 SCIP_CONSHDLR* conshdlr = (SCIP_CONSHDLR*)mapexprdata;
667
668 assert(sourcescip != NULL);
669 assert(targetscip != NULL);
670 assert(sourceexpr != NULL);
671 assert(targetexpr != NULL);
672 assert(*targetexpr == NULL);
673 assert(mapexprdata != NULL);
674
675 /* do not provide map if not variable */
676 if( !SCIPisExprVar(sourcescip, sourceexpr) )
677 return SCIP_OKAY;
678
679 SCIP_CALL( createExprVar(targetscip, conshdlr, targetexpr, SCIPgetVarExprVar(sourceexpr)) );
680
681 return SCIP_OKAY;
682 }
683
684 /* map var exprs to var-expr from var2expr hashmap corresponding to transformed var */
685 static
686 SCIP_DECL_EXPR_MAPEXPR(mapexprtransvar)
687 { /*lint --e{715}*/
688 SCIP_CONSHDLR* conshdlr = (SCIP_CONSHDLR*)mapexprdata;
689 SCIP_VAR* var;
690
691 assert(sourcescip != NULL);
692 assert(targetscip != NULL);
693 assert(sourceexpr != NULL);
694 assert(targetexpr != NULL);
695 assert(*targetexpr == NULL);
696 assert(mapexprdata != NULL);
697
698 /* do not provide map if not variable */
699 if( !SCIPisExprVar(sourcescip, sourceexpr) )
700 return SCIP_OKAY;
701
702 var = SCIPgetVarExprVar(sourceexpr);
703 assert(var != NULL);
704
705 /* transform variable */
706 SCIP_CALL( SCIPgetTransformedVar(sourcescip, var, &var) );
707 assert(var != NULL);
708
709 SCIP_CALL( createExprVar(targetscip, conshdlr, targetexpr, var) );
710
711 return SCIP_OKAY;
712 }
713
714 /** stores all variable expressions into a given constraint */
715 static
716 SCIP_RETCODE storeVarExprs(
717 SCIP* scip, /**< SCIP data structure */
718 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
719 SCIP_CONSDATA* consdata /**< constraint data */
720 )
721 {
722 SCIP_CONSHDLRDATA* conshdlrdata;
723 int i;
724
725 assert(consdata != NULL);
726
727 /* skip if we have stored the variable expressions already */
728 if( consdata->varexprs != NULL )
729 return SCIP_OKAY;
730
731 assert(consdata->varexprs == NULL);
732 assert(consdata->nvarexprs == 0);
733
734 /* create array to store all variable expressions; the number of variable expressions is bounded by SCIPgetNTotalVars() */
735 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consdata->varexprs, SCIPgetNTotalVars(scip)) );
736
737 SCIP_CALL( SCIPgetExprVarExprs(scip, consdata->expr, consdata->varexprs, &(consdata->nvarexprs)) );
738 assert(SCIPgetNTotalVars(scip) >= consdata->nvarexprs);
739
740 /* shrink array if there are less variables in the expression than in the problem */
741 if( SCIPgetNTotalVars(scip) > consdata->nvarexprs )
742 {
743 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->varexprs, SCIPgetNTotalVars(scip), consdata->nvarexprs) );
744 }
745
746 conshdlrdata = SCIPconshdlrGetData(conshdlr);
747 assert(conshdlrdata != NULL);
748 assert(conshdlrdata->var2expr != NULL);
749
750 /* ensure that for every variable an entry exists in the var2expr hashmap
751 * when removing duplicate subexpressions it can happen than a var->varexpr map was removed from the hashmap
752 */
753 for( i = 0; i < consdata->nvarexprs; ++i )
754 {
755 if( !SCIPhashmapExists(conshdlrdata->var2expr, SCIPgetVarExprVar(consdata->varexprs[i])) )
756 {
757 SCIP_CALL( SCIPhashmapInsert(conshdlrdata->var2expr, SCIPgetVarExprVar(consdata->varexprs[i]), consdata->varexprs[i]) );
758 }
759 }
760
761 return SCIP_OKAY;
762 }
763
764 /** frees all variable expression stored in storeVarExprs() */
765 static
766 SCIP_RETCODE freeVarExprs(
767 SCIP* scip, /**< SCIP data structure */
768 SCIP_CONSDATA* consdata /**< constraint data */
769 )
770 {
771 int i;
772
773 assert(consdata != NULL);
774
775 /* skip if we have stored the variable expressions already*/
776 if( consdata->varexprs == NULL )
777 return SCIP_OKAY;
778
779 assert(consdata->varexprs != NULL);
780 assert(consdata->nvarexprs >= 0);
781
782 /* release variable expressions */
783 for( i = 0; i < consdata->nvarexprs; ++i )
784 {
785 assert(consdata->varexprs[i] != NULL);
786 SCIP_CALL( SCIPreleaseExpr(scip, &consdata->varexprs[i]) );
787 assert(consdata->varexprs[i] == NULL);
788 }
789
790 /* free variable expressions */
791 SCIPfreeBlockMemoryArrayNull(scip, &consdata->varexprs, consdata->nvarexprs);
792 consdata->varexprs = NULL;
793 consdata->nvarexprs = 0;
794
795 return SCIP_OKAY;
796 }
797
798 /** interval evaluation of variables as used in bound tightening
799 *
800 * Returns slightly relaxed local variable bounds of a variable as interval.
801 * Does not relax beyond integer values, thus does not relax bounds on integer variables at all.
802 */
803 static
804 SCIP_DECL_EXPR_INTEVALVAR(intEvalVarBoundTightening)
805 {
806 SCIP_INTERVAL interval;
807 SCIP_CONSHDLRDATA* conshdlrdata;
808 SCIP_Real lb;
809 SCIP_Real ub;
810
811 assert(scip != NULL);
812 assert(var != NULL);
813
814 conshdlrdata = (SCIP_CONSHDLRDATA*)intevalvardata;
815 assert(conshdlrdata != NULL);
816
817 if( conshdlrdata->globalbounds )
818 {
819 lb = SCIPvarGetLbGlobal(var);
820 ub = SCIPvarGetUbGlobal(var);
821 }
822 else
823 {
824 lb = SCIPvarGetLbLocal(var);
825 ub = SCIPvarGetUbLocal(var);
826 }
827 assert(lb <= ub); /* SCIP should ensure that variable bounds are not contradicting */
828
829 /* implicit integer variables may have non-integer bounds, apparently (run space25a) */
830 if( SCIPvarGetType(var) == SCIP_VARTYPE_IMPLINT )
831 {
832 lb = EPSROUND(lb, 0.0); /*lint !e835*/
833 ub = EPSROUND(ub, 0.0); /*lint !e835*/
834 }
835
836 /* integer variables should always have integral bounds in SCIP */
837 assert(EPSFRAC(lb, 0.0) == 0.0 || !SCIPvarIsIntegral(var)); /*lint !e835*/
838 assert(EPSFRAC(ub, 0.0) == 0.0 || !SCIPvarIsIntegral(var)); /*lint !e835*/
839
840 switch( conshdlrdata->varboundrelax )
841 {
842 case 'n' : /* no relaxation */
843 break;
844
845 case 'a' : /* relax by absolute value */
846 {
847 /* do not look at integer variables, they already have integral bounds, so wouldn't be relaxed */
848 if( SCIPvarIsIntegral(var) )
849 break;
850
851 if( !SCIPisInfinity(scip, -lb) )
852 {
853 /* reduce lb by epsilon, or to the next integer value, which ever is larger */
854 SCIP_Real bnd = floor(lb);
855 lb = MAX(bnd, lb - conshdlrdata->varboundrelaxamount);
856 }
857
858 if( !SCIPisInfinity(scip, ub) )
859 {
860 /* increase ub by epsilon, or to the next integer value, which ever is smaller */
861 SCIP_Real bnd = ceil(ub);
862 ub = MIN(bnd, ub + conshdlrdata->varboundrelaxamount);
863 }
864
865 break;
866 }
867
868 case 'b' : /* relax always by absolute value */
869 {
870 /* do not look at integer variables, they already have integral bounds, so wouldn't be relaxed */
871 if( SCIPvarIsIntegral(var) )
872 break;
873
874 if( !SCIPisInfinity(scip, -lb) )
875 lb -= conshdlrdata->varboundrelaxamount;
876
877 if( !SCIPisInfinity(scip, ub) )
878 ub += conshdlrdata->varboundrelaxamount;
879
880 break;
881 }
882
883 case 'r' : /* relax by relative value */
884 {
885 /* do not look at integer variables, they already have integral bounds, so wouldn't be relaxed */
886 if( SCIPvarIsIntegral(var) )
887 break;
888
889 /* relax bounds by epsilon*max(1,|bnd|), instead of just epsilon as in case 'a', thus we trust the first log(epsilon) digits
890 * however, when domains get small, relaxing can excessively weaken bound tightening, thus do only fraction of |ub-lb| if that is smaller
891 * further, do not relax beyond next integer value
892 */
893 if( !SCIPisInfinity(scip, -lb) )
894 {
895 SCIP_Real bnd = floor(lb);
896 lb = MAX(bnd, lb - MIN(conshdlrdata->varboundrelaxamount * MAX(1.0, REALABS(lb)), 0.001 * REALABS(ub-lb)));
897 }
898
899 if( !SCIPisInfinity(scip, ub) )
900 {
901 SCIP_Real bnd = ceil(ub);
902 ub = MIN(bnd, ub + MIN(conshdlrdata->varboundrelaxamount * MAX(1.0, REALABS(ub)), 0.001 * REALABS(ub-lb)));
903 }
904
905 break;
906 }
907
908 default :
909 {
910 SCIPerrorMessage("Unsupported value '%c' for varboundrelax option.\n", conshdlrdata->varboundrelax);
911 SCIPABORT();
912 break;
913 }
914 }
915
916 /* convert SCIPinfinity() to SCIP_INTERVAL_INFINITY */
917 lb = -infty2infty(SCIPinfinity(scip), SCIP_INTERVAL_INFINITY, -lb);
918 ub = infty2infty(SCIPinfinity(scip), SCIP_INTERVAL_INFINITY, ub);
919 assert(lb <= ub);
920
921 SCIPintervalSetBounds(&interval, lb, ub);
922
923 return interval;
924 }
925
926 /** compares two nonlinear constraints by its index
927 *
928 * Usable as compare operator in array sort functions.
929 */
930 static
931 SCIP_DECL_SORTPTRCOMP(compIndexConsNonlinear)
932 {
933 SCIP_CONSDATA* consdata1 = SCIPconsGetData((SCIP_CONS*)elem1);
934 SCIP_CONSDATA* consdata2 = SCIPconsGetData((SCIP_CONS*)elem2);
935
936 assert(consdata1 != NULL);
937 assert(consdata2 != NULL);
938
939 return consdata1->consindex - consdata2->consindex;
940 }
941
942 /** processes variable fixing or bound change event */
943 static
944 SCIP_DECL_EVENTEXEC(processVarEvent)
945 { /*lint --e{715}*/
946 SCIP_EVENTTYPE eventtype;
947 SCIP_EXPR* expr;
948 SCIP_EXPR_OWNERDATA* ownerdata;
949
950 eventtype = SCIPeventGetType(event);
951 assert(eventtype & (SCIP_EVENTTYPE_BOUNDCHANGED | SCIP_EVENTTYPE_VARFIXED));
952
953 assert(eventdata != NULL);
954 expr = (SCIP_EXPR*) eventdata;
955 assert(SCIPisExprVar(scip, expr));
956
957 SCIPdebugMsg(scip, " exec event %" SCIP_EVENTTYPE_FORMAT " for variable <%s> (local [%g,%g], global [%g,%g])\n", eventtype,
958 SCIPvarGetName(SCIPeventGetVar(event)),
959 SCIPvarGetLbLocal(SCIPeventGetVar(event)), SCIPvarGetUbLocal(SCIPeventGetVar(event)),
960 SCIPvarGetLbGlobal(SCIPeventGetVar(event)), SCIPvarGetUbGlobal(SCIPeventGetVar(event)));
961
962 ownerdata = SCIPexprGetOwnerData(expr);
963 assert(ownerdata != NULL);
964 /* we only catch varevents for variables in constraints, so there should be constraints */
965 assert(ownerdata->nconss > 0);
966 assert(ownerdata->conss != NULL);
967
968 /* notify constraints that use this variable expression (expr) to repropagate and possibly resimplify
969 * - propagation can only find something new if a bound was tightened
970 * - simplify can only find something new if a var is fixed (or maybe a bound is tightened)
971 * and we look at global changes (that is, we are not looking at boundchanges in probing)
972 */
973 if( eventtype & (SCIP_EVENTTYPE_BOUNDTIGHTENED | SCIP_EVENTTYPE_VARFIXED) )
974 {
975 SCIP_CONSDATA* consdata;
976 int c;
977
978 for( c = 0; c < ownerdata->nconss; ++c )
979 {
980 assert(ownerdata->conss[c] != NULL);
981 consdata = SCIPconsGetData(ownerdata->conss[c]);
982
983 /* if bound tightening, then mark constraints to be propagated again
984 * TODO we could try be more selective here and only trigger a propagation if a relevant bound has changed,
985 * that is, we don't need to repropagate x + ... <= rhs if only the upper bound of x has been tightened
986 * the locks don't help since they are not available separately for each constraint
987 */
988 if( eventtype & SCIP_EVENTTYPE_BOUNDTIGHTENED )
989 {
990 consdata->ispropagated = FALSE;
991 SCIPdebugMsg(scip, " marked <%s> for propagate\n", SCIPconsGetName(ownerdata->conss[c]));
992 }
993
994 /* if still in presolve (but not probing), then mark constraints to be unsimplified */
995 if( SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING && !SCIPinProbing(scip) )
996 {
997 consdata->issimplified = FALSE;
998 SCIPdebugMsg(scip, " marked <%s> for simplify\n", SCIPconsGetName(ownerdata->conss[c]));
999 }
1000 }
1001 }
1002
1003 /* update curboundstag, lastboundrelax, and expr activity */
1004 if( eventtype & SCIP_EVENTTYPE_BOUNDCHANGED )
1005 {
1006 SCIP_CONSHDLRDATA* conshdlrdata;
1007 SCIP_INTERVAL activity;
1008
1009 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr);
1010 assert(conshdlrdata != NULL);
1011
1012 /* increase tag on bounds */
1013 ++conshdlrdata->curboundstag;
1014 assert(conshdlrdata->curboundstag > 0);
1015
1016 /* remember also if we relaxed bounds now */
1017 if( eventtype & SCIP_EVENTTYPE_BOUNDRELAXED )
1018 conshdlrdata->lastboundrelax = conshdlrdata->curboundstag;
1019
1020 /* update the activity of the var-expr here immediately
1021 * (we could call expr->activity = intevalvar(var, consdhlr) directly, but then the exprhdlr statistics are not updated)
1022 */
1023 SCIP_CALL( SCIPcallExprInteval(scip, expr, &activity, conshdlrdata->intevalvar, conshdlrdata) );
1024 /* activity = conshdlrdata->intevalvar(scip, SCIPgetVarExprVar(expr), conshdlrdata); */
1025 #ifdef DEBUG_PROP
1026 SCIPdebugMsg(scip, " var-exprhdlr::inteval = [%.20g, %.20g]\n", activity.inf, activity.sup);
1027 #endif
1028 SCIPexprSetActivity(expr, activity, conshdlrdata->curboundstag);
1029 }
1030
1031 return SCIP_OKAY;
1032 }
1033
1034 /** registers event handler to catch variable events on variable
1035 *
1036 * Additionally, the given constraint is stored in the ownerdata of the variable-expression.
1037 * When an event occurs, all stored constraints are notified.
1038 */
1039 static
1040 SCIP_RETCODE catchVarEvent(
1041 SCIP* scip, /**< SCIP data structure */
1042 SCIP_EVENTHDLR* eventhdlr, /**< event handler */
1043 SCIP_EXPR* expr, /**< variable expression */
1044 SCIP_CONS* cons /**< nonlinear constraint */
1045 )
1046 {
1047 SCIP_EXPR_OWNERDATA* ownerdata;
1048
1049 assert(eventhdlr != NULL);
1050 assert(expr != NULL);
1051 assert(SCIPisExprVar(scip, expr));
1052 assert(cons != NULL);
1053
1054 ownerdata = SCIPexprGetOwnerData(expr);
1055 assert(ownerdata != NULL);
1056
1057 #ifndef NDEBUG
1058 /* assert that constraint does not double-catch variable */
1059 {
1060 int i;
1061 for( i = 0; i < ownerdata->nconss; ++i )
1062 assert(ownerdata->conss[i] != cons);
1063 }
1064 #endif
1065
1066 /* append cons to ownerdata->conss */
1067 SCIP_CALL( SCIPensureBlockMemoryArray(scip, &ownerdata->conss, &ownerdata->consssize, ownerdata->nconss + 1) );
1068 ownerdata->conss[ownerdata->nconss++] = cons;
1069 /* we're not capturing the constraint here to avoid circular references */
1070
1071 /* updated sorted flag */
1072 if( ownerdata->nconss <= 1 )
1073 ownerdata->consssorted = TRUE;
1074 else if( ownerdata->consssorted )
1075 ownerdata->consssorted = compIndexConsNonlinear(ownerdata->conss[ownerdata->nconss-2], ownerdata->conss[ownerdata->nconss-1]) > 0;
1076
1077 /* catch variable events, if not done so yet (first constraint) */
1078 if( ownerdata->filterpos < 0 )
1079 {
1080 SCIP_EVENTTYPE eventtype;
1081
1082 assert(ownerdata->nconss == 1);
1083
1084 eventtype = SCIP_EVENTTYPE_BOUNDCHANGED | SCIP_EVENTTYPE_VARFIXED;
1085
1086 SCIP_CALL( SCIPcatchVarEvent(scip, SCIPgetVarExprVar(expr), eventtype, eventhdlr, (SCIP_EVENTDATA*)expr, &ownerdata->filterpos) );
1087 assert(ownerdata->filterpos >= 0);
1088 }
1089
1090 return SCIP_OKAY;
1091 }
1092
1093 /** catch variable events */
1094 static
1095 SCIP_RETCODE catchVarEvents(
1096 SCIP* scip, /**< SCIP data structure */
1097 SCIP_EVENTHDLR* eventhdlr, /**< event handler */
1098 SCIP_CONS* cons /**< constraint for which to catch bound change events */
1099 )
1100 {
1101 SCIP_CONSHDLRDATA* conshdlrdata;
1102 SCIP_CONSDATA* consdata;
1103 SCIP_EXPR* expr;
1104 int i;
1105
1106 assert(eventhdlr != NULL);
1107 assert(cons != NULL);
1108
1109 consdata = SCIPconsGetData(cons);
1110 assert(consdata != NULL);
1111 assert(consdata->varexprs != NULL);
1112 assert(consdata->nvarexprs >= 0);
1113
1114 /* check if we have catched variable events already */
1115 if( consdata->catchedevents )
1116 return SCIP_OKAY;
1117
1118 conshdlrdata = SCIPconshdlrGetData(SCIPconsGetHdlr(cons));
1119 assert(conshdlrdata != NULL);
1120 assert(conshdlrdata->intevalvar == intEvalVarBoundTightening);
1121
1122 SCIPdebugMsg(scip, "catchVarEvents for %s\n", SCIPconsGetName(cons));
1123
1124 for( i = 0; i < consdata->nvarexprs; ++i )
1125 {
1126 expr = consdata->varexprs[i];
1127
1128 assert(expr != NULL);
1129 assert(SCIPisExprVar(scip, expr));
1130
1131 SCIP_CALL( catchVarEvent(scip, eventhdlr, expr, cons) );
1132
1133 /* from now on, activity of var-expr will usually be updated in processVarEvent if variable bound is changing
1134 * since we just registered this eventhdlr, we should make sure that the activity is also up to date now
1135 */
1136 if( SCIPexprGetActivityTag(expr) < conshdlrdata->curboundstag )
1137 {
1138 SCIP_INTERVAL activity;
1139 SCIP_CALL( SCIPcallExprInteval(scip, expr, &activity, intEvalVarBoundTightening, conshdlrdata) );
1140 /* activity = intEvalVarBoundTightening(scip, SCIPgetVarExprVar(expr), conshdlrdata); */
1141 SCIPexprSetActivity(expr, activity, conshdlrdata->curboundstag);
1142 #ifdef DEBUG_PROP
1143 SCIPdebugMsg(scip, "var-exprhdlr::inteval for var <%s> = [%.20g, %.20g]\n", SCIPvarGetName(SCIPgetVarExprVar(expr)), activity.inf, activity.sup);
1144 #endif
1145 }
1146 }
1147
1148 consdata->catchedevents = TRUE;
1149
1150 return SCIP_OKAY;
1151 }
1152
1153 /** unregisters event handler to catch variable events on variable
1154 *
1155 * The given constraint is removed from the constraints array in the ownerdata of the variable-expression.
1156 * If this was the last constraint, then the event handler is unregistered for this variable.
1157 */
1158 static
1159 SCIP_RETCODE dropVarEvent(
1160 SCIP* scip, /**< SCIP data structure */
1161 SCIP_EVENTHDLR* eventhdlr, /**< event handler */
1162 SCIP_EXPR* expr, /**< variable expression */
1163 SCIP_CONS* cons /**< expr constraint */
1164 )
1165 {
1166 SCIP_EXPR_OWNERDATA* ownerdata;
1167 int pos;
1168
1169 assert(eventhdlr != NULL);
1170 assert(expr != NULL);
1171 assert(SCIPisExprVar(scip, expr));
1172 assert(cons != NULL);
1173
1174 ownerdata = SCIPexprGetOwnerData(expr);
1175 assert(ownerdata != NULL);
1176 assert(ownerdata->nconss > 0);
1177
1178 if( ownerdata->conss[ownerdata->nconss-1] == cons )
1179 {
1180 pos = ownerdata->nconss-1;
1181 }
1182 else
1183 {
1184 if( !ownerdata->consssorted )
1185 {
1186 SCIPsortPtr((void**)ownerdata->conss, compIndexConsNonlinear, ownerdata->nconss);
1187 ownerdata->consssorted = TRUE;
1188 }
1189
1190 if( !SCIPsortedvecFindPtr((void**)ownerdata->conss, compIndexConsNonlinear, cons, ownerdata->nconss, &pos) )
1191 {
1192 SCIPerrorMessage("Constraint <%s> not in constraint array of expression for variable <%s>\n", SCIPconsGetName(cons), SCIPvarGetName(SCIPgetVarExprVar(expr)));
1193 return SCIP_ERROR;
1194 }
1195 assert(pos >= 0 && pos < ownerdata->nconss);
1196 }
1197 assert(ownerdata->conss[pos] == cons);
1198
1199 /* move last constraint into position of removed constraint */
1200 if( pos < ownerdata->nconss-1 )
1201 {
1202 ownerdata->conss[pos] = ownerdata->conss[ownerdata->nconss-1];
1203 ownerdata->consssorted = FALSE;
1204 }
1205 --ownerdata->nconss;
1206
1207 /* drop variable events if that was the last constraint */
1208 if( ownerdata->nconss == 0 )
1209 {
1210 SCIP_EVENTTYPE eventtype;
1211
1212 assert(ownerdata->filterpos >= 0);
1213
1214 eventtype = SCIP_EVENTTYPE_BOUNDCHANGED | SCIP_EVENTTYPE_VARFIXED;
1215
1216 SCIP_CALL( SCIPdropVarEvent(scip, SCIPgetVarExprVar(expr), eventtype, eventhdlr, (SCIP_EVENTDATA*)expr, ownerdata->filterpos) );
1217 ownerdata->filterpos = -1;
1218 }
1219
1220 return SCIP_OKAY;
1221 }
1222
1223 /** drop variable events */
1224 static
1225 SCIP_RETCODE dropVarEvents(
1226 SCIP* scip, /**< SCIP data structure */
1227 SCIP_EVENTHDLR* eventhdlr, /**< event handler */
1228 SCIP_CONS* cons /**< constraint for which to drop bound change events */
1229 )
1230 {
1231 SCIP_CONSDATA* consdata;
1232 int i;
1233
1234 assert(eventhdlr != NULL);
1235 assert(cons != NULL);
1236
1237 consdata = SCIPconsGetData(cons);
1238 assert(consdata != NULL);
1239
1240 /* check if we have catched variable events already */
1241 if( !consdata->catchedevents )
1242 return SCIP_OKAY;
1243
1244 assert(consdata->varexprs != NULL);
1245 assert(consdata->nvarexprs >= 0);
1246
1247 SCIPdebugMsg(scip, "dropVarEvents for %s\n", SCIPconsGetName(cons));
1248
1249 for( i = consdata->nvarexprs - 1; i >= 0; --i )
1250 {
1251 assert(consdata->varexprs[i] != NULL);
1252
1253 SCIP_CALL( dropVarEvent(scip, eventhdlr, consdata->varexprs[i], cons) );
1254 }
1255
1256 consdata->catchedevents = FALSE;
1257
1258 return SCIP_OKAY;
1259 }
1260
1261 /** creates and captures a nonlinear constraint
1262 *
1263 * @attention Use copyexpr=FALSE only if expr is already "owned" by conshdlr, that is, if expressions were created with exprownerCreate() and ownerdata passed in the last two arguments
1264 */
1265 static
1266 SCIP_RETCODE createCons(
1267 SCIP* scip, /**< SCIP data structure */
1268 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
1269 SCIP_CONS** cons, /**< pointer to hold the created constraint */
1270 const char* name, /**< name of constraint */
1271 SCIP_EXPR* expr, /**< expression of constraint (must not be NULL) */
1272 SCIP_Real lhs, /**< left hand side of constraint */
1273 SCIP_Real rhs, /**< right hand side of constraint */
1274 SCIP_Bool copyexpr, /**< whether to copy the expression or reuse the given expr (capture it) */
1275 SCIP_Bool initial, /**< should the LP relaxation of constraint be in the initial LP?
1276 * Usually set to TRUE. Set to FALSE for 'lazy constraints'. */
1277 SCIP_Bool separate, /**< should the constraint be separated during LP processing?
1278 * Usually set to TRUE. */
1279 SCIP_Bool enforce, /**< should the constraint be enforced during node processing?
1280 * TRUE for model constraints, FALSE for additional, redundant constraints. */
1281 SCIP_Bool check, /**< should the constraint be checked for feasibility?
1282 * TRUE for model constraints, FALSE for additional, redundant constraints. */
1283 SCIP_Bool propagate, /**< should the constraint be propagated during node processing?
1284 * Usually set to TRUE. */
1285 SCIP_Bool local, /**< is constraint only valid locally?
1286 * Usually set to FALSE. Has to be set to TRUE, e.g., for branching constraints. */
1287 SCIP_Bool modifiable, /**< is constraint modifiable (subject to column generation)?
1288 * Usually set to FALSE. In column generation applications, set to TRUE if pricing
1289 * adds coefficients to this constraint. */
1290 SCIP_Bool dynamic, /**< is constraint subject to aging?
1291 * Usually set to FALSE. Set to TRUE for own cuts which
1292 * are separated as constraints. */
1293 SCIP_Bool removable /**< should the relaxation be removed from the LP due to aging or cleanup?
1294 * Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */
1295 )
1296 {
1297 SCIP_CONSHDLRDATA* conshdlrdata;
1298 SCIP_CONSDATA* consdata;
1299
1300 assert(conshdlr != NULL);
1301 assert(expr != NULL);
1302
1303 conshdlrdata = SCIPconshdlrGetData(conshdlr);
1304 assert(conshdlrdata != NULL);
1305
1306 if( local && SCIPgetDepth(scip) != 0 )
1307 {
1308 SCIPerrorMessage("Locally valid nonlinear constraints are not supported, yet.\n");
1309 return SCIP_INVALIDCALL;
1310 }
1311
1312 /* TODO we should allow for non-initial nonlinear constraints */
1313 if( !initial )
1314 {
1315 SCIPerrorMessage("Non-initial nonlinear constraints are not supported, yet.\n");
1316 return SCIP_INVALIDCALL;
1317 }
1318
1319 /* create constraint data */
1320 SCIP_CALL( SCIPallocClearBlockMemory(scip, &consdata) );
1321
1322 if( copyexpr )
1323 {
1324 /* copy expression, thereby map variables expressions to already existing variables expressions in var2expr map, or augment var2expr map */
1325 SCIP_CALL( SCIPduplicateExpr(scip, expr, &consdata->expr, mapexprvar, conshdlr, exprownerCreate, (void*)conshdlr) );
1326 }
1327 else
1328 {
1329 consdata->expr = expr;
1330 SCIPcaptureExpr(consdata->expr);
1331 }
1332 consdata->lhs = lhs;
1333 consdata->rhs = rhs;
1334 consdata->consindex = conshdlrdata->lastconsindex++;
1335 consdata->curv = SCIP_EXPRCURV_UNKNOWN;
1336
1337 /* create constraint */
1338 SCIP_CALL( SCIPcreateCons(scip, cons, name, conshdlr, consdata, initial, separate, enforce, check, propagate,
1339 local, modifiable, dynamic, removable, FALSE) );
1340
1341 return SCIP_OKAY;
1342 }
1343
1344 /** returns absolute violation for auxvar relation in an expression w.r.t. original variables
1345 *
1346 * Assume the expression is f(x), where x are original (i.e., not auxiliary) variables.
1347 * Assume that f(x) is associated with auxiliary variable z.
1348 *
1349 * If there are negative locks, then return the violation of z ≤ f(x) and sets `violover` to TRUE.
1350 * If there are positive locks, then return the violation of z ≥ f(x) and sets `violunder` to TRUE.
1351 * Of course, if there both negative and positive locks, then return the violation of z = f(x).
1352 * If f could not be evaluated, then return SCIPinfinity() and set both `violover` and `violunder` to TRUE.
1353 *
1354 * @note This does not reevaluate the violation, but assumes that the expression has been evaluated
1355 */
1356 static
1357 SCIP_Real getExprAbsOrigViolation(
1358 SCIP* scip, /**< SCIP data structure */
1359 SCIP_EXPR* expr, /**< expression */
1360 SCIP_SOL* sol, /**< solution that has been evaluated */
1361 SCIP_Bool* violunder, /**< buffer to store whether z >= f(x) is violated, or NULL */
1362 SCIP_Bool* violover /**< buffer to store whether z <= f(x) is violated, or NULL */
1363 )
1364 {
1365 SCIP_EXPR_OWNERDATA* ownerdata;
1366 SCIP_Real auxvarvalue;
1367
1368 assert(expr != NULL);
1369
1370 ownerdata = SCIPexprGetOwnerData(expr);
1371 assert(ownerdata != NULL);
1372 assert(ownerdata->auxvar != NULL);
1373
1374 if( SCIPexprGetEvalValue(expr) == SCIP_INVALID )
1375 {
1376 if( violunder != NULL )
1377 *violunder = TRUE;
1378 if( violover != NULL )
1379 *violover = TRUE;
1380 return SCIPinfinity(scip);
1381 }
1382
1383 auxvarvalue = SCIPgetSolVal(scip, sol, ownerdata->auxvar);
1384
1385 if( ownerdata->nlocksneg > 0 && auxvarvalue > SCIPexprGetEvalValue(expr) )
1386 {
1387 if( violunder != NULL )
1388 *violunder = FALSE;
1389 if( violover != NULL )
1390 *violover = TRUE;
1391 return auxvarvalue - SCIPexprGetEvalValue(expr);
1392 }
1393
1394 if( ownerdata->nlockspos > 0 && SCIPexprGetEvalValue(expr) > auxvarvalue )
1395 {
1396 if( violunder != NULL )
1397 *violunder = TRUE;
1398 if( violover != NULL )
1399 *violover = FALSE;
1400 return SCIPexprGetEvalValue(expr) - auxvarvalue;
1401 }
1402
1403 if( violunder != NULL )
1404 *violunder = FALSE;
1405 if( violover != NULL )
1406 *violover = FALSE;
1407 return 0.0;
1408 }
1409
1410 /** returns absolute violation for auxvar relation in an expression w.r.t. auxiliary variables
1411 *
1412 * Assume the expression is f(w), where w are auxiliary variables that were introduced by some nlhdlr.
1413 * Assume that f(w) is associated with auxiliary variable z.
1414 *
1415 * If there are negative locks, then return the violation of z ≤ f(w) and sets `violover` to TRUE.
1416 * If there are positive locks, then return the violation of z ≥ f(w) and sets `violunder` to TRUE.
1417 * Of course, if there both negative and positive locks, then return the violation of z = f(w).
1418 * If f could not be evaluated, then return SCIPinfinity() and set both `violover` and `violunder` to TRUE.
1419 *
1420 * @note This does not reevaluate the violation, but assumes that f(w) is passed in with auxvalue.
1421 */
1422 static
1423 SCIP_Real getExprAbsAuxViolation(
1424 SCIP* scip, /**< SCIP data structure */
1425 SCIP_EXPR* expr, /**< expression */
1426 SCIP_Real auxvalue, /**< value of f(w) */
1427 SCIP_SOL* sol, /**< solution that has been evaluated */
1428 SCIP_Bool* violunder, /**< buffer to store whether z >= f(w) is violated, or NULL */
1429 SCIP_Bool* violover /**< buffer to store whether z <= f(w) is violated, or NULL */
1430 )
1431 {
1432 SCIP_EXPR_OWNERDATA* ownerdata;
1433 SCIP_Real auxvarvalue;
1434
1435 assert(expr != NULL);
1436
1437 ownerdata = SCIPexprGetOwnerData(expr);
1438 assert(ownerdata != NULL);
1439 assert(ownerdata->auxvar != NULL);
1440
1441 if( auxvalue == SCIP_INVALID )
1442 {
1443 if( violunder != NULL )
1444 *violunder = TRUE;
1445 if( violover != NULL )
1446 *violover = TRUE;
1447 return SCIPinfinity(scip);
1448 }
1449
1450 auxvarvalue = SCIPgetSolVal(scip, sol, ownerdata->auxvar);
1451
1452 if( ownerdata->nlocksneg > 0 && auxvarvalue > auxvalue )
1453 {
1454 if( violunder != NULL )
1455 *violunder = FALSE;
1456 if( violover != NULL )
1457 *violover = TRUE;
1458 return auxvarvalue - auxvalue;
1459 }
1460
1461 if( ownerdata->nlockspos > 0 && auxvalue > auxvarvalue )
1462 {
1463 if( violunder != NULL )
1464 *violunder = TRUE;
1465 if( violover != NULL )
1466 *violover = FALSE;
1467 return auxvalue - auxvarvalue;
1468 }
1469
1470 if( violunder != NULL )
1471 *violunder = FALSE;
1472 if( violover != NULL )
1473 *violover = FALSE;
1474
1475 return 0.0;
1476 }
1477
1478 /** computes violation of a constraint */
1479 static
1480 SCIP_RETCODE computeViolation(
1481 SCIP* scip, /**< SCIP data structure */
1482 SCIP_CONS* cons, /**< constraint */
1483 SCIP_SOL* sol, /**< solution or NULL if LP solution should be used */
1484 SCIP_Longint soltag /**< tag that uniquely identifies the solution (with its values), or 0. */
1485 )
1486 {
1487 SCIP_CONSDATA* consdata;
1488 SCIP_Real activity;
1489
1490 assert(scip != NULL);
1491 assert(cons != NULL);
1492
1493 consdata = SCIPconsGetData(cons);
1494 assert(consdata != NULL);
1495
1496 SCIP_CALL( SCIPevalExpr(scip, consdata->expr, sol, soltag) );
1497 activity = SCIPexprGetEvalValue(consdata->expr);
1498
1499 /* consider constraint as violated if it is undefined in the current point */
1500 if( activity == SCIP_INVALID )
1501 {
1502 consdata->lhsviol = SCIPinfinity(scip);
1503 consdata->rhsviol = SCIPinfinity(scip);
1504 return SCIP_OKAY;
1505 }
1506
1507 /* compute violations */
1508 consdata->lhsviol = SCIPisInfinity(scip, -consdata->lhs) ? -SCIPinfinity(scip) : consdata->lhs - activity;
1509 consdata->rhsviol = SCIPisInfinity(scip, consdata->rhs) ? -SCIPinfinity(scip) : activity - consdata->rhs;
1510
1511 return SCIP_OKAY;
1512 }
1513
1514 /** returns absolute violation of a constraint
1515 *
1516 * @note This does not reevaluate the violation, but assumes that computeViolation() has been called before.
1517 */
1518 static
1519 SCIP_Real getConsAbsViolation(
1520 SCIP_CONS* cons /**< constraint */
1521 )
1522 {
1523 SCIP_CONSDATA* consdata;
1524
1525 assert(cons != NULL);
1526
1527 consdata = SCIPconsGetData(cons);
1528 assert(consdata != NULL);
1529
1530 return MAX3(0.0, consdata->lhsviol, consdata->rhsviol);
1531 }
1532
1533 /** computes relative violation of a constraint
1534 *
1535 * @note This does not reevaluate the violation, but assumes that computeViolation() has been called before.
1536 */
1537 static
1538 SCIP_RETCODE getConsRelViolation(
1539 SCIP* scip, /**< SCIP data structure */
1540 SCIP_CONS* cons, /**< constraint */
1541 SCIP_Real* viol, /**< buffer to store violation */
1542 SCIP_SOL* sol, /**< solution or NULL if LP solution should be used */
1543 SCIP_Longint soltag /**< tag that uniquely identifies the solution (with its values), or 0 */
1544 )
1545 {
1546 SCIP_CONSHDLR* conshdlr;
1547 SCIP_CONSHDLRDATA* conshdlrdata;
1548 SCIP_CONSDATA* consdata;
1549 SCIP_Real scale;
1550
1551 assert(cons != NULL);
1552 assert(viol != NULL);
1553
1554 conshdlr = SCIPconsGetHdlr(cons);
1555 assert(conshdlr != NULL);
1556
1557 conshdlrdata = SCIPconshdlrGetData(conshdlr);
1558 assert(conshdlrdata != NULL);
1559
1560 *viol = getConsAbsViolation(cons);
1561
1562 if( conshdlrdata->violscale == 'n' )
1563 return SCIP_OKAY;
1564
1565 if( SCIPisInfinity(scip, *viol) )
1566 return SCIP_OKAY;
1567
1568 consdata = SCIPconsGetData(cons);
1569 assert(consdata != NULL);
1570
1571 if( conshdlrdata->violscale == 'a' )
1572 {
1573 scale = MAX(1.0, REALABS(SCIPexprGetEvalValue(consdata->expr)));
1574
1575 /* consider value of side that is violated for scaling, too */
1576 if( consdata->lhsviol > 0.0 && REALABS(consdata->lhs) > scale )
1577 {
1578 assert(!SCIPisInfinity(scip, -consdata->lhs));
1579 scale = REALABS(consdata->lhs);
1580 }
1581 else if( consdata->rhsviol > 0.0 && REALABS(consdata->rhs) > scale )
1582 {
1583 assert(!SCIPisInfinity(scip, consdata->rhs));
1584 scale = REALABS(consdata->rhs);
1585 }
1586
1587 *viol /= scale;
1588 return SCIP_OKAY;
1589 }
1590
1591 /* if not 'n' or 'a', then it has to be 'g' at the moment */
1592 assert(conshdlrdata->violscale == 'g');
1593 if( soltag == 0L || consdata->gradnormsoltag != soltag )
1594 {
1595 /* we need the varexprs to conveniently access the gradient */
1596 SCIP_CALL( storeVarExprs(scip, conshdlr, consdata) );
1597
1598 /* update cached value of norm of gradient */
1599 consdata->gradnorm = 0.0;
1600
1601 /* compute gradient */
1602 SCIP_CALL( SCIPevalExprGradient(scip, consdata->expr, sol, soltag) );
1603
1604 /* gradient evaluation error -> no scaling */
1605 if( SCIPexprGetDerivative(consdata->expr) != SCIP_INVALID )
1606 {
1607 int i;
1608 for( i = 0; i < consdata->nvarexprs; ++i )
1609 {
1610 SCIP_Real deriv;
1611
1612 assert(SCIPexprGetDiffTag(consdata->expr) == SCIPexprGetDiffTag(consdata->varexprs[i]));
1613 deriv = SCIPexprGetDerivative(consdata->varexprs[i]);
1614 if( deriv == SCIP_INVALID )
1615 {
1616 /* SCIPdebugMsg(scip, "gradient evaluation error for component %d\n", i); */
1617 consdata->gradnorm = 0.0;
1618 break;
1619 }
1620
1621 consdata->gradnorm += deriv*deriv;
1622 }
1623 }
1624 consdata->gradnorm = sqrt(consdata->gradnorm);
1625 consdata->gradnormsoltag = soltag;
1626 }
1627
1628 *viol /= MAX(1.0, consdata->gradnorm);
1629
1630 return SCIP_OKAY;
1631 }
1632
1633 /** returns whether constraint is currently violated
1634 *
1635 * @note This does not reevaluate the violation, but assumes that computeViolation() has been called before.
1636 */
1637 static
1638 SCIP_Bool isConsViolated(
1639 SCIP* scip, /**< SCIP data structure */
1640 SCIP_CONS* cons /**< constraint */
1641 )
1642 {
1643 return getConsAbsViolation(cons) > SCIPfeastol(scip);
1644 }
1645
1646 /** checks for a linear variable that can be increased or decreased without harming feasibility */
1647 static
1648 void findUnlockedLinearVar(
1649 SCIP* scip, /**< SCIP data structure */
1650 SCIP_CONS* cons /**< constraint */
1651 )
1652 {
1653 SCIP_CONSDATA* consdata;
1654 int poslock;
1655 int neglock;
1656 int i;
1657
1658 assert(cons != NULL);
1659
1660 consdata = SCIPconsGetData(cons);
1661 assert(consdata != NULL);
1662
1663 consdata->linvarincr = NULL;
1664 consdata->linvardecr = NULL;
1665 consdata->linvarincrcoef = 0.0;
1666 consdata->linvardecrcoef = 0.0;
1667
1668 /* root expression is not a sum -> no unlocked linear variable available */
1669 if( !SCIPisExprSum(scip, consdata->expr) )
1670 return;
1671
1672 for( i = 0; i < SCIPexprGetNChildren(consdata->expr); ++i )
1673 {
1674 SCIP_EXPR* child;
1675
1676 child = SCIPexprGetChildren(consdata->expr)[i];
1677 assert(child != NULL);
1678
1679 /* check whether the child is a variable expression */
1680 if( SCIPisExprVar(scip, child) )
1681 {
1682 SCIP_VAR* var = SCIPgetVarExprVar(child);
1683 SCIP_Real coef = SCIPgetCoefsExprSum(consdata->expr)[i];
1684
1685 if( coef > 0.0 )
1686 {
1687 poslock = !SCIPisInfinity(scip, consdata->rhs) ? 1 : 0;
1688 neglock = !SCIPisInfinity(scip, -consdata->lhs) ? 1 : 0;
1689 }
1690 else
1691 {
1692 poslock = !SCIPisInfinity(scip, -consdata->lhs) ? 1 : 0;
1693 neglock = !SCIPisInfinity(scip, consdata->rhs) ? 1 : 0;
1694 }
1695 SCIPdebugMsg(scip, "child <%s> locks: %d %d\n", SCIPvarGetName(var), SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL), SCIPvarGetNLocksUpType(var, SCIP_LOCKTYPE_MODEL));
1696
1697 if( SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL) - neglock == 0 )
1698 {
1699 /* for a*x + f(y) \in [lhs, rhs], we can decrease x without harming other constraints
1700 * if we have already one candidate, then take the one where the loss in the objective function is less
1701 */
1702 if( (consdata->linvardecr == NULL) ||
1703 (SCIPvarGetObj(consdata->linvardecr) / consdata->linvardecrcoef > SCIPvarGetObj(var) / coef) )
1704 {
1705 consdata->linvardecr = var;
1706 consdata->linvardecrcoef = coef;
1707 }
1708 }
1709
1710 if( SCIPvarGetNLocksUpType(var, SCIP_LOCKTYPE_MODEL) - poslock == 0 )
1711 {
1712 /* for a*x + f(y) \in [lhs, rhs], we can increase x without harm
1713 * if we have already one candidate, then take the one where the loss in the objective function is less
1714 */
1715 if( (consdata->linvarincr == NULL) ||
1716 (SCIPvarGetObj(consdata->linvarincr) / consdata->linvarincrcoef > SCIPvarGetObj(var) / coef) )
1717 {
1718 consdata->linvarincr = var;
1719 consdata->linvarincrcoef = coef;
1720 }
1721 }
1722 }
1723 }
1724
1725 assert(consdata->linvarincr == NULL || consdata->linvarincrcoef != 0.0);
1726 assert(consdata->linvardecr == NULL || consdata->linvardecrcoef != 0.0);
1727
1728 if( consdata->linvarincr != NULL )
1729 {
1730 SCIPdebugMsg(scip, "may increase <%s> to become feasible\n", SCIPvarGetName(consdata->linvarincr));
1731 }
1732 if( consdata->linvardecr != NULL )
1733 {
1734 SCIPdebugMsg(scip, "may decrease <%s> to become feasible\n", SCIPvarGetName(consdata->linvardecr));
1735 }
1736 }
1737
1738 /** Given a solution where every nonlinear constraint is either feasible or can be made feasible by
1739 * moving a linear variable, construct the corresponding feasible solution and pass it to the trysol heuristic.
1740 *
1741 * The method assumes that this is always possible and that not all constraints are feasible already.
1742 */
1743 static
1744 SCIP_RETCODE proposeFeasibleSolution(
1745 SCIP* scip, /**< SCIP data structure */
1746 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
1747 SCIP_CONS** conss, /**< constraints to process */
1748 int nconss, /**< number of constraints */
1749 SCIP_SOL* sol, /**< solution to process */
1750 SCIP_Bool* success /**< buffer to store whether we succeeded to construct a solution that satisfies all provided constraints */
1751 )
1752 {
1753 SCIP_CONSHDLRDATA* conshdlrdata;
1754 SCIP_SOL* newsol;
1755 int c;
1756
1757 assert(scip != NULL);
1758 assert(conshdlr != NULL);
1759 assert(conss != NULL || nconss == 0);
1760 assert(success != NULL);
1761
1762 *success = FALSE;
1763
1764 /* don't propose new solutions if not in presolve or solving */
1765 if( SCIPgetStage(scip) < SCIP_STAGE_INITPRESOLVE || SCIPgetStage(scip) >= SCIP_STAGE_SOLVED )
1766 return SCIP_OKAY;
1767
1768 conshdlrdata = SCIPconshdlrGetData(conshdlr);
1769 assert(conshdlrdata != NULL);
1770
1771 if( sol != NULL )
1772 {
1773 SCIP_CALL( SCIPcreateSolCopy(scip, &newsol, sol) );
1774 }
1775 else
1776 {
1777 SCIP_CALL( SCIPcreateLPSol(scip, &newsol, NULL) );
1778 }
1779 SCIP_CALL( SCIPunlinkSol(scip, newsol) );
1780 SCIPdebugMsg(scip, "attempt to make solution from <%s> feasible by shifting linear variable\n",
1781 sol != NULL ? (SCIPsolGetHeur(sol) != NULL ? SCIPheurGetName(SCIPsolGetHeur(sol)) : "tree") : "LP");
1782
1783 for( c = 0; c < nconss; ++c )
1784 {
1785 SCIP_CONSDATA* consdata = SCIPconsGetData(conss[c]); /*lint !e613*/
1786 SCIP_Real viol = 0.0;
1787 SCIP_Real delta;
1788 SCIP_Real gap;
1789
1790 assert(consdata != NULL);
1791
1792 /* get absolute violation and sign */
1793 if( consdata->lhsviol > SCIPfeastol(scip) )
1794 viol = consdata->lhsviol; /* lhs - activity */
1795 else if( consdata->rhsviol > SCIPfeastol(scip) )
1796 viol = -consdata->rhsviol; /* rhs - activity */
1797 else
1798 continue; /* constraint is satisfied */
1799
1800 if( consdata->linvarincr != NULL &&
1801 ((viol > 0.0 && consdata->linvarincrcoef > 0.0) || (viol < 0.0 && consdata->linvarincrcoef < 0.0)) )
1802 {
1803 SCIP_VAR* var = consdata->linvarincr;
1804
1805 /* compute how much we would like to increase var */
1806 delta = viol / consdata->linvarincrcoef;
1807 assert(delta > 0.0);
1808
1809 /* if var has an upper bound, may need to reduce delta */
1810 if( !SCIPisInfinity(scip, SCIPvarGetUbGlobal(var)) )
1811 {
1812 gap = SCIPvarGetUbGlobal(var) - SCIPgetSolVal(scip, newsol, var);
1813 delta = MIN(MAX(0.0, gap), delta);
1814 }
1815 if( SCIPisPositive(scip, delta) )
1816 {
1817 /* if variable is integral, round delta up so that it will still have an integer value */
1818 if( SCIPvarIsIntegral(var) )
1819 delta = SCIPceil(scip, delta);
1820
1821 SCIP_CALL( SCIPincSolVal(scip, newsol, var, delta) );
1822 SCIPdebugMsg(scip, "increase <%s> by %g to %g to remedy lhs-violation %g of cons <%s>\n",
1823 SCIPvarGetName(var), delta, SCIPgetSolVal(scip, newsol, var), viol, SCIPconsGetName(conss[c])); /*lint !e613*/
1824
1825 /* adjust constraint violation, if satisfied go on to next constraint */
1826 viol -= consdata->linvarincrcoef * delta;
1827 if( SCIPisZero(scip, viol) )
1828 continue;
1829 }
1830 }
1831
1832 assert(viol != 0.0);
1833 if( consdata->linvardecr != NULL &&
1834 ((viol > 0.0 && consdata->linvardecrcoef < 0.0) || (viol < 0.0 && consdata->linvardecrcoef > 0.0)) )
1835 {
1836 SCIP_VAR* var = consdata->linvardecr;
1837
1838 /* compute how much we would like to decrease var */
1839 delta = viol / consdata->linvardecrcoef;
1840 assert(delta < 0.0);
1841
1842 /* if var has a lower bound, may need to reduce delta */
1843 if( !SCIPisInfinity(scip, -SCIPvarGetLbGlobal(var)) )
1844 {
1845 gap = SCIPgetSolVal(scip, newsol, var) - SCIPvarGetLbGlobal(var);
1846 delta = MAX(MIN(0.0, gap), delta);
1847 }
1848 if( SCIPisNegative(scip, delta) )
1849 {
1850 /* if variable is integral, round delta down so that it will still have an integer value */
1851 if( SCIPvarIsIntegral(var) )
1852 delta = SCIPfloor(scip, delta);
1853 SCIP_CALL( SCIPincSolVal(scip, newsol, consdata->linvardecr, delta) );
1854 /*lint --e{613} */
1855 SCIPdebugMsg(scip, "increase <%s> by %g to %g to remedy rhs-violation %g of cons <%s>\n",
1856 SCIPvarGetName(var), delta, SCIPgetSolVal(scip, newsol, var), viol, SCIPconsGetName(conss[c]));
1857
1858 /* adjust constraint violation, if satisfied go on to next constraint */
1859 viol -= consdata->linvardecrcoef * delta;
1860 if( SCIPisZero(scip, viol) )
1861 continue;
1862 }
1863 }
1864
1865 /* still here... so probably we could not make constraint feasible due to variable bounds, thus give up */
1866 break;
1867 }
1868
1869 /* if we have a solution that should satisfy all quadratic constraints and has a better objective than the current upper bound,
1870 * then pass it to the trysol heuristic
1871 */
1872 if( c == nconss && (SCIPisInfinity(scip, SCIPgetUpperbound(scip)) || SCIPisSumLT(scip, SCIPgetSolTransObj(scip, newsol), SCIPgetUpperbound(scip))) )
1873 {
1874 SCIPdebugMsg(scip, "pass solution with objective val %g to trysol heuristic\n", SCIPgetSolTransObj(scip, newsol));
1875
1876 assert(conshdlrdata->trysolheur != NULL);
1877 SCIP_CALL( SCIPheurPassSolTrySol(scip, conshdlrdata->trysolheur, newsol) );
1878
1879 *success = TRUE;
1880 }
1881
1882 SCIP_CALL( SCIPfreeSol(scip, &newsol) );
1883
1884 return SCIP_OKAY;
1885 }
1886
1887 /** adds globally valid tight estimators in a given solution as cut to cutpool
1888 *
1889 * Called by addTightEstimatorCuts() for a specific expression, nlhdlr, and estimate-direction (over or under).
1890 */
1891 static
1892 SCIP_RETCODE addTightEstimatorCut(
1893 SCIP* scip, /**< SCIP data structure */
1894 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
1895 SCIP_CONS* cons, /**< constraint */
1896 SCIP_EXPR* expr, /**< expression */
1897 EXPRENFO* exprenfo, /**< expression enfo data, e.g., nlhdlr to use */
1898 SCIP_SOL* sol, /**< reference point where to estimate */
1899 SCIP_Bool overestimate, /**< whether to overestimate */
1900 SCIP_PTRARRAY* rowpreps /**< array for rowpreps */
1901 )
1902 {
1903 SCIP_Bool estimatesuccess = FALSE;
1904 SCIP_Bool branchscoresuccess = FALSE;
1905 int minidx;
1906 int maxidx;
1907 int r;
1908
1909 assert(scip != NULL);
1910 assert(expr != NULL);
1911 assert(exprenfo != NULL);
1912 assert(rowpreps != NULL);
1913
1914 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " %sestimate using nlhdlr <%s> for expr %p (%s)\n",
1915 overestimate ? "over" : "under", SCIPnlhdlrGetName(exprenfo->nlhdlr), (void*)expr, SCIPexprhdlrGetName(SCIPexprGetHdlr(expr))); )
1916
1917 SCIP_CALL( SCIPnlhdlrEstimate(scip, conshdlr, exprenfo->nlhdlr, expr, exprenfo->nlhdlrexprdata, sol,
1918 exprenfo->auxvalue, overestimate, SCIPinfinity(scip), FALSE, rowpreps, &estimatesuccess, &branchscoresuccess) );
1919
1920 minidx = SCIPgetPtrarrayMinIdx(scip, rowpreps);
1921 maxidx = SCIPgetPtrarrayMaxIdx(scip, rowpreps);
1922 assert(estimatesuccess == (minidx <= maxidx));
1923
1924 if( !estimatesuccess )
1925 {
1926 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " estimate of nlhdlr %s failed\n", SCIPnlhdlrGetName(exprenfo->nlhdlr)); )
1927 return SCIP_OKAY;
1928 }
1929
1930 for( r = minidx; r <= maxidx; ++r )
1931 {
1932 SCIP_ROWPREP* rowprep;
1933 SCIP_ROW* row;
1934 SCIP_Real estimateval;
1935 int i;
1936
1937 rowprep = (SCIP_ROWPREP*) SCIPgetPtrarrayVal(scip, rowpreps, r);
1938 assert(rowprep != NULL);
1939 assert(SCIProwprepGetSidetype(rowprep) == (overestimate ? SCIP_SIDETYPE_LEFT : SCIP_SIDETYPE_RIGHT));
1940
1941 /* if estimators is only local valid, then skip */
1942 if( SCIProwprepIsLocal(rowprep) )
1943 {
1944 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " skip local estimator\n"); )
1945 SCIPfreeRowprep(scip, &rowprep);
1946 continue;
1947 }
1948
1949 /* compute value of estimator */
1950 estimateval = -SCIProwprepGetSide(rowprep);
1951 for( i = 0; i < SCIProwprepGetNVars(rowprep); ++i )
1952 estimateval += SCIProwprepGetCoefs(rowprep)[i] * SCIPgetSolVal(scip, sol, SCIProwprepGetVars(rowprep)[i]);
1953
1954 /* if estimator value is not tight (or even "more than tight", e.g., when estimating in integer vars), then skip */
1955 if( (overestimate && !SCIPisFeasLE(scip, estimateval, SCIPexprGetEvalValue(expr))) ||
1956 (!overestimate && !SCIPisFeasGE(scip, estimateval, SCIPexprGetEvalValue(expr))) )
1957 {
1958 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " skip non-tight estimator with value %g, expr value %g\n", estimateval, SCIPexprGetEvalValue(expr)); )
1959 SCIPfreeRowprep(scip, &rowprep);
1960 continue;
1961 }
1962
1963 /* complete estimator to cut and clean it up */
1964 SCIP_CALL( SCIPaddRowprepTerm(scip, rowprep, SCIPgetExprAuxVarNonlinear(expr), -1.0) );
1965 SCIP_CALL( SCIPcleanupRowprep2(scip, rowprep, sol, SCIPinfinity(scip), &estimatesuccess) );
1966
1967 /* if cleanup failed or rowprep is local now, then skip */
1968 if( !estimatesuccess || SCIProwprepIsLocal(rowprep) )
1969 {
1970 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " skip after cleanup failed or made estimator locally valid\n"); )
1971 SCIPfreeRowprep(scip, &rowprep);
1972 continue;
1973 }
1974
1975 /* generate row and add to cutpool */
1976 SCIP_CALL( SCIPgetRowprepRowCons(scip, &row, rowprep, cons) );
1977
1978 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " adding cut ");
1979 SCIP_CALL( SCIPprintRow(scip, row, enfologfile) ); )
1980
1981 SCIP_CALL( SCIPaddPoolCut(scip, row) );
1982 /* SCIPnlhdlrIncrementNSeparated(nlhdlr); */
1983
1984 SCIP_CALL( SCIPreleaseRow(scip, &row) );
1985 SCIPfreeRowprep(scip, &rowprep);
1986 }
1987
1988 SCIP_CALL( SCIPclearPtrarray(scip, rowpreps) );
1989
1990 return SCIP_OKAY;
1991 }
1992
1993 /** adds globally valid tight estimators in a given solution as cuts to cutpool
1994 *
1995 * Essentially we want to ensure that the LP relaxation is tight in the new solution, if possible.
1996 * For convex constraints, we would achieve this by linearizing.
1997 * To avoid checking explicitly for convexity, we compute estimators via any nlhdlr that didn't say it would
1998 * use bound information and check whether the estimator is tight.
1999 *
2000 * Since linearization may happen in auxiliary variables, we ensure that auxiliary variables are set
2001 * to the eval-value of its expression, i.e., we change sol so it is also feasible in the extended formulation.
2002 */
2003 static
2004 SCIP_RETCODE addTightEstimatorCuts(
2005 SCIP* scip, /**< SCIP data structure */
2006 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
2007 SCIP_CONS** conss, /**< constraints */
2008 int nconss, /**< number of constraints */
2009 SCIP_SOL* sol /**< reference point where to estimate */
2010 )
2011 {
2012 SCIP_CONSDATA* consdata;
2013 SCIP_Longint soltag;
2014 SCIP_EXPRITER* it;
2015 SCIP_EXPR* expr;
2016 SCIP_PTRARRAY* rowpreps;
2017 int c, e;
2018
2019 assert(scip != NULL);
2020 assert(conshdlr != NULL);
2021 assert(conss != NULL || nconss == 0);
2022
2023 ENFOLOG( SCIPinfoMessage(scip, enfologfile, "add tight estimators in new solution from <%s> to cutpool\n", SCIPheurGetName(SCIPsolGetHeur(sol))); )
2024
2025 /* TODO probably we just evaluated all expressions when checking the sol before it was added
2026 * would be nice to recognize this and skip reevaluating
2027 */
2028 soltag = SCIPgetExprNewSoltag(scip);
2029
2030 SCIP_CALL( SCIPcreatePtrarray(scip, &rowpreps) );
2031
2032 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
2033 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) );
2034 SCIPexpriterSetStagesDFS(it, SCIP_EXPRITER_LEAVEEXPR);
2035
2036 for( c = 0; c < nconss; ++c )
2037 {
2038 /* skip constraints that are not enabled or deleted or have separation disabled */
2039 if( !SCIPconsIsEnabled(conss[c]) || SCIPconsIsDeleted(conss[c]) || !SCIPconsIsSeparationEnabled(conss[c]) )
2040 continue;
2041 assert(SCIPconsIsActive(conss[c]));
2042
2043 consdata = SCIPconsGetData(conss[c]);
2044 assert(consdata != NULL);
2045
2046 /* TODO we could remember for which constraints there is a chance that we would add anything,
2047 * i.e., there is some convex-like expression, and skip other constraints
2048 */
2049
2050 ENFOLOG(
2051 {
2052 int i;
2053 SCIPinfoMessage(scip, enfologfile, " constraint ");
2054 SCIP_CALL( SCIPprintCons(scip, conss[c], enfologfile) );
2055 SCIPinfoMessage(scip, enfologfile, "\n and point\n");
2056 for( i = 0; i < consdata->nvarexprs; ++i )
2057 {
2058 SCIP_VAR* var;
2059 var = SCIPgetVarExprVar(consdata->varexprs[i]);
2060 SCIPinfoMessage(scip, enfologfile, " %-10s = %15g bounds: [%15g,%15g]\n", SCIPvarGetName(var),
2061 SCIPgetSolVal(scip, sol, var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var));
2062 }
2063 })
2064
2065 SCIP_CALL( SCIPevalExpr(scip, consdata->expr, sol, soltag) );
2066 assert(SCIPexprGetEvalValue(consdata->expr) != SCIP_INVALID);
2067
2068 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
2069 {
2070 SCIP_EXPR_OWNERDATA* ownerdata;
2071
2072 ownerdata = SCIPexprGetOwnerData(expr);
2073 assert(ownerdata != NULL);
2074
2075 /* we can only generate a cut from an estimator if there is an auxvar */
2076 if( ownerdata->auxvar == NULL )
2077 continue;
2078
2079 /* set value for auxvar in sol to value of expr, in case it is used to compute estimators higher up of this expression */
2080 assert(SCIPexprGetEvalTag(expr) == soltag);
2081 assert(SCIPexprGetEvalValue(expr) != SCIP_INVALID);
2082 SCIP_CALL( SCIPsetSolVal(scip, sol, ownerdata->auxvar, SCIPexprGetEvalValue(expr)) );
2083
2084 /* generate cuts from estimators of each nonlinear handler that provides estimates */
2085 for( e = 0; e < ownerdata->nenfos; ++e )
2086 {
2087 SCIP_NLHDLR* nlhdlr;
2088
2089 nlhdlr = ownerdata->enfos[e]->nlhdlr;
2090 assert(nlhdlr != NULL);
2091
2092 /* skip nlhdlr that does not implement estimate (so it does enfo) */
2093 if( !SCIPnlhdlrHasEstimate(nlhdlr) )
2094 continue;
2095
2096 /* skip nlhdlr that does not participate in separation or looks like it would give only locally-valid estimators
2097 * (because it uses activities on vars/auxvars)
2098 */
2099 if( ((ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPAABOVE) == 0 || ownerdata->enfos[e]->sepaaboveusesactivity) &&
2100 ((ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPABELOW) == 0 || ownerdata->enfos[e]->sepabelowusesactivity) )
2101 continue;
2102
2103 /* skip nlhdlr_default on sum, as the estimator doesn't depend on the reference point (expr is linear in auxvars) */
2104 if( SCIPisExprSum(scip, expr) && strcmp(SCIPnlhdlrGetName(nlhdlr), "default") == 0 )
2105 continue;
2106
2107 /* evaluate the expression w.r.t. the nlhdlrs auxiliary variables, since some nlhdlr expect this before their estimate is called */
2108 SCIP_CALL( SCIPnlhdlrEvalaux(scip, nlhdlr, expr, ownerdata->enfos[e]->nlhdlrexprdata, &ownerdata->enfos[e]->auxvalue, sol) );
2109 ENFOLOG(
2110 SCIPinfoMessage(scip, enfologfile, " expr ");
2111 SCIPprintExpr(scip, expr, enfologfile);
2112 SCIPinfoMessage(scip, enfologfile, " (%p): evalvalue %.15g auxvarvalue %.15g, nlhdlr <%s> auxvalue: %.15g\n",
2113 (void*)expr, SCIPexprGetEvalValue(expr), SCIPgetSolVal(scip, sol, ownerdata->auxvar), SCIPnlhdlrGetName(nlhdlr), ownerdata->enfos[e]->auxvalue);
2114 )
2115 /* due to setting values of auxvars to expr values in sol, the auxvalue should equal to expr evalvalue */
2116 assert(SCIPisEQ(scip, ownerdata->enfos[e]->auxvalue, SCIPexprGetEvalValue(expr)));
2117
2118 /* if nlhdlr wants to be called for overestimate and does not use local bounds, then call estimate of nlhdlr */
2119 if( (ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPAABOVE) && !ownerdata->enfos[e]->sepaaboveusesactivity )
2120 {
2121 SCIP_CALL( addTightEstimatorCut(scip, conshdlr, conss[c], expr, ownerdata->enfos[e], sol, TRUE, rowpreps) );
2122 }
2123
2124 /* if nlhdlr wants to be called for underestimate and does not use local bounds, then call estimate of nlhdlr */
2125 if( (ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPABELOW) && !ownerdata->enfos[e]->sepabelowusesactivity )
2126 {
2127 SCIP_CALL( addTightEstimatorCut(scip, conshdlr, conss[c], expr, ownerdata->enfos[e], sol, FALSE, rowpreps) );
2128 }
2129 }
2130 }
2131 }
2132
2133 SCIPfreeExpriter(&it);
2134 SCIP_CALL( SCIPfreePtrarray(scip, &rowpreps) );
2135
2136 return SCIP_OKAY;
2137 }
2138
2139 /** processes the event that a new primal solution has been found */
2140 static
2141 SCIP_DECL_EVENTEXEC(processNewSolutionEvent)
2142 {
2143 SCIP_CONSHDLR* conshdlr;
2144 SCIP_CONSHDLRDATA* conshdlrdata;
2145 SCIP_SOL* sol;
2146
2147 assert(scip != NULL);
2148 assert(event != NULL);
2149 assert(eventdata != NULL);
2150 assert(eventhdlr != NULL);
2151 assert(SCIPeventGetType(event) & SCIP_EVENTTYPE_SOLFOUND);
2152
2153 conshdlr = (SCIP_CONSHDLR*)eventdata;
2154
2155 if( SCIPconshdlrGetNConss(conshdlr) == 0 )
2156 return SCIP_OKAY;
2157
2158 sol = SCIPeventGetSol(event);
2159 assert(sol != NULL);
2160
2161 conshdlrdata = SCIPconshdlrGetData(conshdlr);
2162 assert(conshdlrdata != NULL);
2163
2164 /* we are only interested in solution coming from some heuristic other than trysol, but not from the tree
2165 * the reason for ignoring trysol solutions is that they may come ~~from an NLP solve in sepalp, where we already added linearizations, or are~~
2166 * from the tree, but postprocessed via proposeFeasibleSolution
2167 */
2168 if( SCIPsolGetHeur(sol) == NULL || SCIPsolGetHeur(sol) == conshdlrdata->trysolheur )
2169 return SCIP_OKAY;
2170
2171 SCIPdebugMsg(scip, "caught new sol event %" SCIP_EVENTTYPE_FORMAT " from heur <%s>\n", SCIPeventGetType(event), SCIPheurGetName(SCIPsolGetHeur(sol)));
2172
2173 SCIP_CALL( addTightEstimatorCuts(scip, conshdlr, SCIPconshdlrGetConss(conshdlr), SCIPconshdlrGetNConss(conshdlr), sol) );
2174
2175 return SCIP_OKAY;
2176 }
2177
2178 /** tightens the bounds of the auxiliary variable associated with an expression (or original variable if being a variable-expression) according to given bounds
2179 *
2180 * The given bounds may very well be the exprs activity (when called from forwardPropExpr()), but can also be some
2181 * tighter bounds (when called from SCIPtightenExprIntervalNonlinear()).
2182 *
2183 * Nothing will happen if SCIP is not in presolve or solve.
2184 */
2185 static
2186 SCIP_RETCODE tightenAuxVarBounds(
2187 SCIP* scip, /**< SCIP data structure */
2188 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
2189 SCIP_EXPR* expr, /**< expression whose auxvar is to be tightened */
2190 SCIP_INTERVAL bounds, /**< bounds to be used for tightening (must not be empty) */
2191 SCIP_Bool* cutoff, /**< buffer to store whether a cutoff was detected */
2192 int* ntightenings /**< buffer to add the total number of tightenings, or NULL */
2193 )
2194 {
2195 SCIP_VAR* var;
2196 SCIP_Bool tightenedlb;
2197 SCIP_Bool tightenedub;
2198 SCIP_Bool force;
2199
2200 assert(scip != NULL);
2201 assert(conshdlr != NULL);
2202 assert(expr != NULL);
2203 assert(cutoff != NULL);
2204
2205 /* the given bounds must not be empty (we could cope, but we shouldn't be called in this situation) */
2206 assert(!SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, bounds));
2207
2208 *cutoff = FALSE;
2209
2210 /* do not tighten variable in problem stage (important for unittests)
2211 * TODO put some kind of #ifdef UNITTEST around this
2212 */
2213 if( SCIPgetStage(scip) < SCIP_STAGE_INITPRESOLVE && SCIPgetStage(scip) > SCIP_STAGE_SOLVING )
2214 return SCIP_OKAY;
2215
2216 var = SCIPgetExprAuxVarNonlinear(expr);
2217 if( var == NULL )
2218 return SCIP_OKAY;
2219
2220 /* force tightening if conshdlrdata says so or it would mean fixing the variable */
2221 force = SCIPconshdlrGetData(conshdlr)->forceboundtightening || SCIPisEQ(scip, bounds.inf, bounds.sup);
2222
2223 /* try to tighten lower bound of (auxiliary) variable */
2224 SCIP_CALL( SCIPtightenVarLb(scip, var, bounds.inf, force, cutoff, &tightenedlb) );
2225 if( tightenedlb )
2226 {
2227 if( ntightenings != NULL )
2228 ++*ntightenings;
2229 SCIPdebugMsg(scip, "tightened lb on auxvar <%s> to %.15g (forced:%u)\n", SCIPvarGetName(var), SCIPvarGetLbLocal(var), force);
2230 }
2231 if( *cutoff )
2232 {
2233 SCIPdebugMsg(scip, "cutoff when tightening lb on auxvar <%s> to %.15g\n", SCIPvarGetName(var), bounds.inf);
2234 return SCIP_OKAY;
2235 }
2236
2237 /* try to tighten upper bound of (auxiliary) variable */
2238 SCIP_CALL( SCIPtightenVarUb(scip, var, bounds.sup, force, cutoff, &tightenedub) );
2239 if( tightenedub )
2240 {
2241 if( ntightenings != NULL )
2242 ++*ntightenings;
2243 SCIPdebugMsg(scip, "tightened ub on auxvar <%s> to %.15g (forced:%u)\n", SCIPvarGetName(var), SCIPvarGetUbLocal(var), force);
2244 }
2245 if( *cutoff )
2246 {
2247 SCIPdebugMsg(scip, "cutoff when tightening ub on auxvar <%s> to %.15g\n", SCIPvarGetName(var), bounds.sup);
2248 return SCIP_OKAY;
2249 }
2250
2251 /* TODO expr->activity should have been reevaluated now due to boundchange-events, but it used to relax bounds
2252 * that seems unnecessary and we could easily undo this here, e.g.,
2253 * if( tightenedlb ) expr->activity.inf = bounds.inf
2254 */
2255
2256 return SCIP_OKAY;
2257 }
2258
2259 /** propagate bounds of the expressions in a given expression tree (that is, updates activity intervals)
2260 * and tries to tighten the bounds of the auxiliary variables accordingly
2261 */
2262 static
2263 SCIP_RETCODE forwardPropExpr(
2264 SCIP* scip, /**< SCIP data structure */
2265 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
2266 SCIP_EXPR* rootexpr, /**< expression */
2267 SCIP_Bool tightenauxvars, /**< should the bounds of auxiliary variables be tightened? */
2268 SCIP_Bool* infeasible, /**< buffer to store whether the problem is infeasible (NULL if not needed) */
2269 int* ntightenings /**< buffer to store the number of auxiliary variable tightenings (NULL if not needed) */
2270 )
2271 {
2272 SCIP_EXPRITER* it;
2273 SCIP_EXPR* expr;
2274 SCIP_EXPR_OWNERDATA* ownerdata;
2275 SCIP_CONSHDLRDATA* conshdlrdata;
2276
2277 assert(scip != NULL);
2278 assert(rootexpr != NULL);
2279
2280 if( infeasible != NULL )
2281 *infeasible = FALSE;
2282 if( ntightenings != NULL )
2283 *ntightenings = 0;
2284
2285 conshdlrdata = SCIPconshdlrGetData(conshdlr);
2286 assert(conshdlrdata != NULL);
2287
2288 /* if value is valid and empty, then we cannot improve, so do nothing */
2289 if( SCIPexprGetActivityTag(rootexpr) >= conshdlrdata->lastboundrelax && SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(rootexpr)) )
2290 {
2291 SCIPdebugMsg(scip, "stored activity of root expr is empty and valid (activitytag >= lastboundrelax (%" SCIP_LONGINT_FORMAT ")), skip forwardPropExpr -> cutoff\n", conshdlrdata->lastboundrelax);
2292
2293 if( infeasible != NULL )
2294 *infeasible = TRUE;
2295
2296 /* just update tag to curboundstag */
2297 SCIPexprSetActivity(rootexpr, SCIPexprGetActivity(rootexpr), conshdlrdata->curboundstag);
2298
2299 return SCIP_OKAY;
2300 }
2301
2302 /* if value is up-to-date, then nothing to do */
2303 if( SCIPexprGetActivityTag(rootexpr) == conshdlrdata->curboundstag )
2304 {
2305 SCIPdebugMsg(scip, "activitytag of root expr equals curboundstag (%" SCIP_LONGINT_FORMAT "), skip forwardPropExpr\n", conshdlrdata->curboundstag);
2306
2307 assert(!SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(rootexpr))); /* handled in previous if() */
2308
2309 return SCIP_OKAY;
2310 }
2311
2312 ownerdata = SCIPexprGetOwnerData(rootexpr);
2313 assert(ownerdata != NULL);
2314
2315 /* if activity of rootexpr is not used, but expr participated in detect (nenfos >= 0), then we do nothing
2316 * it seems wrong to be called for such an expression (unless we are in detect at the moment), so I add a SCIPABORT()
2317 * during detect, we are in some in-between state where we may want to eval activity
2318 * on exprs that we did not notify about their activity usage
2319 */
2320 if( ownerdata->nenfos >= 0 && ownerdata->nactivityusesprop == 0 && ownerdata->nactivityusessepa == 0 && !conshdlrdata->indetect)
2321 {
2322 #ifdef DEBUG_PROP
2323 SCIPdebugMsg(scip, "root expr activity is not used but enfo initialized, skip inteval\n");
2324 #endif
2325 SCIPABORT();
2326 return SCIP_OKAY;
2327 }
2328
2329 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
2330 SCIP_CALL( SCIPexpriterInit(it, rootexpr, SCIP_EXPRITER_DFS, TRUE) );
2331 SCIPexpriterSetStagesDFS(it, SCIP_EXPRITER_VISITINGCHILD | SCIP_EXPRITER_LEAVEEXPR);
2332
2333 for( expr = SCIPexpriterGetCurrent(it); !SCIPexpriterIsEnd(it); )
2334 {
2335 switch( SCIPexpriterGetStageDFS(it) )
2336 {
2337 case SCIP_EXPRITER_VISITINGCHILD :
2338 {
2339 /* skip child if it has been evaluated already */
2340 SCIP_EXPR* child;
2341
2342 child = SCIPexpriterGetChildExprDFS(it);
2343 if( conshdlrdata->curboundstag == SCIPexprGetActivityTag(child) )
2344 {
2345 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(child)) && infeasible != NULL )
2346 *infeasible = TRUE;
2347
2348 expr = SCIPexpriterSkipDFS(it);
2349 continue;
2350 }
2351
2352 break;
2353 }
2354
2355 case SCIP_EXPRITER_LEAVEEXPR :
2356 {
2357 SCIP_INTERVAL activity;
2358
2359 /* we should not have entered this expression if its activity was already up to date */
2360 assert(SCIPexprGetActivityTag(expr) < conshdlrdata->curboundstag);
2361
2362 ownerdata = SCIPexprGetOwnerData(expr);
2363 assert(ownerdata != NULL);
2364
2365 /* for var exprs where varevents are catched, activity is updated immediately when the varbound has been changed
2366 * so we can assume that the activity is up to date for all these variables
2367 * UNLESS we changed the method used to evaluate activity of variable expressions
2368 * or we currently use global bounds (varevents are catched for local bound changes only)
2369 */
2370 if( SCIPisExprVar(scip, expr) && ownerdata->filterpos >= 0 &&
2371 SCIPexprGetActivityTag(expr) >= conshdlrdata->lastvaractivitymethodchange && !conshdlrdata->globalbounds )
2372 {
2373 #ifndef NDEBUG
2374 SCIP_INTERVAL exprhdlrinterval;
2375
2376 SCIP_CALL( SCIPcallExprInteval(scip, expr, &exprhdlrinterval, conshdlrdata->intevalvar, conshdlrdata) );
2377 assert(SCIPisRelEQ(scip, exprhdlrinterval.inf, SCIPexprGetActivity(expr).inf));
2378 assert(SCIPisRelEQ(scip, exprhdlrinterval.sup, SCIPexprGetActivity(expr).sup));
2379 #endif
2380 #ifdef DEBUG_PROP
2381 SCIPdebugMsg(scip, "skip interval evaluation of expr for var <%s> [%g,%g]\n", SCIPvarGetName(SCIPgetVarExprVar(expr)), SCIPexprGetActivity(expr).inf, SCIPexprGetActivity(expr).sup);
2382 #endif
2383 SCIPexprSetActivity(expr, SCIPexprGetActivity(expr), conshdlrdata->curboundstag);
2384
2385 break;
2386 }
2387
2388 if( SCIPexprGetActivityTag(expr) < conshdlrdata->lastboundrelax )
2389 {
2390 /* start with entire activity if current one is invalid */
2391 SCIPintervalSetEntire(SCIP_INTERVAL_INFINITY, &activity);
2392 }
2393 else if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(expr)) )
2394 {
2395 /* If already empty, then don't try to compute even better activity.
2396 * If cons_nonlinear were alone, then we should have noted that we are infeasible
2397 * so an assert(infeasible == NULL || *infeasible) should work here.
2398 * However, after reporting a cutoff due to expr->activity being empty,
2399 * SCIP may wander to a different node and call propagation again.
2400 * If no bounds in a nonlinear constraint have been relaxed when switching nodes
2401 * (so expr->activitytag >= conshdlrdata->lastboundrelax), then
2402 * we will still have expr->activity being empty, but will have forgotten
2403 * that we found infeasibility here before (!2221#note_134120).
2404 * Therefore we just set *infeasibility=TRUE here and stop.
2405 */
2406 if( infeasible != NULL )
2407 *infeasible = TRUE;
2408 SCIPdebugMsg(scip, "expr %p already has empty activity -> cutoff\n", (void*)expr);
2409 break;
2410 }
2411 else
2412 {
2413 /* start with current activity, since it is valid */
2414 activity = SCIPexprGetActivity(expr);
2415 }
2416
2417 /* if activity of expr is not used, but expr participated in detect (nenfos >= 0), then do nothing */
2418 if( ownerdata->nenfos >= 0 && ownerdata->nactivityusesprop == 0 && ownerdata->nactivityusessepa == 0 && !conshdlrdata->indetect )
2419 {
2420 #ifdef DEBUG_PROP
2421 SCIPdebugMsg(scip, "expr %p activity is not used but enfo initialized, skip inteval\n", (void*)expr);
2422 #endif
2423 break;
2424 }
2425
2426 #ifdef DEBUG_PROP
2427 SCIPdebugMsg(scip, "interval evaluation of expr %p ", (void*)expr);
2428 SCIP_CALL( SCIPprintExpr(scip, expr, NULL) );
2429 SCIPdebugMsgPrint(scip, ", current activity = [%.20g, %.20g]\n", SCIPexprGetActivity(expr).inf, SCIPexprGetActivity(expr).sup);
2430 #endif
2431
2432 /* run interval eval of nonlinear handlers or expression handler */
2433 if( ownerdata->nenfos > 0 )
2434 {
2435 SCIP_NLHDLR* nlhdlr;
2436 SCIP_INTERVAL nlhdlrinterval;
2437 int e;
2438
2439 /* for expressions with enforcement, nlhdlrs take care of interval evaluation */
2440 for( e = 0; e < ownerdata->nenfos && !SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, activity); ++e )
2441 {
2442 /* skip nlhdlr if it does not want to participate in activity computation */
2443 if( (ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_ACTIVITY) == 0 )
2444 continue;
2445
2446 nlhdlr = ownerdata->enfos[e]->nlhdlr;
2447 assert(nlhdlr != NULL);
2448
2449 /* skip nlhdlr if it does not provide interval evaluation (so it may only provide reverse propagation) */
2450 if( !SCIPnlhdlrHasIntEval(nlhdlr) )
2451 continue;
2452
2453 /* let nlhdlr evaluate current expression */
2454 nlhdlrinterval = activity;
2455 SCIP_CALL( SCIPnlhdlrInteval(scip, nlhdlr, expr, ownerdata->enfos[e]->nlhdlrexprdata,
2456 &nlhdlrinterval, conshdlrdata->intevalvar, conshdlrdata) );
2457 #ifdef DEBUG_PROP
2458 SCIPdebugMsg(scip, " nlhdlr <%s>::inteval = [%.20g, %.20g]", SCIPnlhdlrGetName(nlhdlr), nlhdlrinterval.inf, nlhdlrinterval.sup);
2459 #endif
2460
2461 /* update activity by intersecting with computed activity */
2462 SCIPintervalIntersectEps(&activity, SCIPepsilon(scip), activity, nlhdlrinterval);
2463 #ifdef DEBUG_PROP
2464 SCIPdebugMsgPrint(scip, " -> new activity: [%.20g, %.20g]\n", activity.inf, activity.sup);
2465 #endif
2466 }
2467 }
2468 else
2469 {
2470 /* for node without enforcement (before or during detect), call the callback of the exprhdlr directly */
2471 SCIP_INTERVAL exprhdlrinterval = activity;
2472 SCIP_CALL( SCIPcallExprInteval(scip, expr, &exprhdlrinterval, conshdlrdata->intevalvar, conshdlrdata) );
2473 #ifdef DEBUG_PROP
2474 SCIPdebugMsg(scip, " exprhdlr <%s>::inteval = [%.20g, %.20g]", SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)), exprhdlrinterval.inf, exprhdlrinterval.sup);
2475 #endif
2476
2477 /* update expr->activity by intersecting with computed activity */
2478 SCIPintervalIntersectEps(&activity, SCIPepsilon(scip), activity, exprhdlrinterval);
2479 #ifdef DEBUG_PROP
2480 SCIPdebugMsgPrint(scip, " -> new activity: [%.20g, %.20g]\n", activity.inf, activity.sup);
2481 #endif
2482 }
2483
2484 /* if expression is integral, then we try to tighten the interval bounds a bit
2485 * this should undo the addition of some unnecessary safety added by use of nextafter() in interval arithmetics, e.g., when doing pow()
2486 * it would be ok to use ceil() and floor(), but for safety we use SCIPceil and SCIPfloor for now
2487 * do this only if using boundtightening-inteval and not in redundancy check (there we really want to relax all variables)
2488 * boundtightening-inteval does not relax integer variables, so can omit expressions without children
2489 * (constants should be ok, too)
2490 */
2491 if( SCIPexprIsIntegral(expr) && conshdlrdata->intevalvar == intEvalVarBoundTightening && SCIPexprGetNChildren(expr) > 0 )
2492 {
2493 if( activity.inf > -SCIP_INTERVAL_INFINITY )
2494 activity.inf = SCIPceil(scip, activity.inf);
2495 if( activity.sup < SCIP_INTERVAL_INFINITY )
2496 activity.sup = SCIPfloor(scip, activity.sup);
2497 #ifdef DEBUG_PROP
2498 SCIPdebugMsg(scip, " applying integrality: [%.20g, %.20g]\n", activity.inf, activity.sup);
2499 #endif
2500 }
2501
2502 /* mark the current node to be infeasible if either the lower/upper bound is above/below +/- SCIPinfinity()
2503 * TODO this is a problem if dual-presolve fixed a variable to +/- infinity
2504 */
2505 if( SCIPisInfinity(scip, activity.inf) || SCIPisInfinity(scip, -activity.sup) )
2506 {
2507 SCIPdebugMsg(scip, "cut off due to activity [%g,%g] beyond infinity\n", activity.inf, activity.sup);
2508 SCIPintervalSetEmpty(&activity);
2509 }
2510
2511 /* now finally store activity in expr */
2512 SCIPexprSetActivity(expr, activity, conshdlrdata->curboundstag);
2513
2514 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, activity) )
2515 {
2516 if( infeasible != NULL )
2517 *infeasible = TRUE;
2518 }
2519 else if( tightenauxvars && ownerdata->auxvar != NULL )
2520 {
2521 SCIP_Bool tighteninfeasible;
2522
2523 SCIP_CALL( tightenAuxVarBounds(scip, conshdlr, expr, activity, &tighteninfeasible, ntightenings) );
2524 if( tighteninfeasible )
2525 {
2526 if( infeasible != NULL )
2527 *infeasible = TRUE;
2528 SCIPintervalSetEmpty(&activity);
2529 SCIPexprSetActivity(expr, activity, conshdlrdata->curboundstag);
2530 }
2531 }
2532
2533 break;
2534 }
2535
2536 default:
2537 /* you should never be here */
2538 SCIPerrorMessage("unexpected iterator stage\n");
2539 SCIPABORT();
2540 break;
2541 }
2542
2543 expr = SCIPexpriterGetNext(it);
2544 }
2545
2546 SCIPfreeExpriter(&it);
2547
2548 return SCIP_OKAY;
2549 }
2550
2551 /** returns whether intersecting `oldinterval` with `newinterval` would provide a properly smaller interval
2552 *
2553 * If `subsetsufficient` is TRUE, then the intersection being smaller than oldinterval is sufficient.
2554 *
2555 * If `subsetsufficient` is FALSE, then we require
2556 * - a change from an unbounded interval to a bounded one, or
2557 * - or a change from an unfixed (width > epsilon) to a fixed interval, or
2558 * - a minimal tightening of one of the interval bounds as defined by SCIPis{Lb,Ub}Better().
2559 */
2560 static
2561 SCIP_Bool isIntervalBetter(
2562 SCIP* scip, /**< SCIP data structure */
2563 SCIP_Bool subsetsufficient, /**< whether the intersection being a proper subset of oldinterval is sufficient */
2564 SCIP_INTERVAL newinterval, /**< new interval */
2565 SCIP_INTERVAL oldinterval /**< old interval */
2566 )
2567 {
2568 assert(scip != NULL);
2569 assert(!SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, newinterval));
2570 assert(!SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, oldinterval));
2571
2572 if( subsetsufficient )
2573 /* oldinterval \cap newinterval < oldinterval iff not oldinterval is subset of newinterval */
2574 return !SCIPintervalIsSubsetEQ(SCIP_INTERVAL_INFINITY, oldinterval, newinterval);
2575
2576 /* check whether lower bound of interval becomes finite */
2577 if( oldinterval.inf <= -SCIP_INTERVAL_INFINITY && newinterval.inf > -SCIP_INTERVAL_INFINITY )
2578 return TRUE;
2579
2580 /* check whether upper bound of interval becomes finite */
2581 if( oldinterval.sup >= SCIP_INTERVAL_INFINITY && newinterval.sup > SCIP_INTERVAL_INFINITY )
2582 return TRUE;
2583
2584 /* check whether intersection will have width <= epsilon, if oldinterval doesn't have yet */
2585 if( !SCIPisEQ(scip, oldinterval.inf, oldinterval.sup) && SCIPisEQ(scip, MAX(oldinterval.inf, newinterval.inf), MIN(oldinterval.sup, newinterval.sup)) )
2586 return TRUE;
2587
2588 /* check whether lower bound on interval will be better by SCIP's quality measures for boundchanges */
2589 if( SCIPisLbBetter(scip, newinterval.inf, oldinterval.inf, oldinterval.sup) )
2590 return TRUE;
2591
2592 /* check whether upper bound on interval will be better by SCIP's quality measures for boundchanges */
2593 if( SCIPisUbBetter(scip, newinterval.sup, oldinterval.inf, oldinterval.sup) )
2594 return TRUE;
2595
2596 return FALSE;
2597 }
2598
2599 /** propagates bounds for each sub-expression in the `reversepropqueue` by starting from the root expressions
2600 *
2601 * The expression will be traversed in breadth first search by using this queue.
2602 *
2603 * @note Calling this function requires feasible intervals for each sub-expression; this is guaranteed by calling
2604 * forwardPropExpr() before calling this function.
2605 *
2606 * @note Calling this function with `*infeasible` = TRUE will only empty the queue.
2607 */
2608 static
2609 SCIP_RETCODE reversePropQueue(
2610 SCIP* scip, /**< SCIP data structure */
2611 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
2612 SCIP_Bool* infeasible, /**< buffer to update whether an expression's bounds were propagated to an empty interval */
2613 int* ntightenings /**< buffer to store the number of (variable) tightenings */
2614 )
2615 {
2616 SCIP_CONSHDLRDATA* conshdlrdata;
2617 SCIP_EXPR* expr;
2618 SCIP_EXPR_OWNERDATA* ownerdata;
2619
2620 assert(infeasible != NULL);
2621 assert(ntightenings != NULL);
2622
2623 conshdlrdata = SCIPconshdlrGetData(conshdlr);
2624 assert(conshdlrdata != NULL);
2625
2626 *ntightenings = 0;
2627
2628 /* main loop that calls reverse propagation for expressions on the queue
2629 * when reverseprop finds a tightening for an expression, then that expression is added to the queue (within the reverseprop call)
2630 */
2631 while( !SCIPqueueIsEmpty(conshdlrdata->reversepropqueue) && !(*infeasible) )
2632 {
2633 SCIP_INTERVAL propbounds;
2634 int e;
2635
2636 expr = (SCIP_EXPR*) SCIPqueueRemove(conshdlrdata->reversepropqueue);
2637 assert(expr != NULL);
2638
2639 ownerdata = SCIPexprGetOwnerData(expr);
2640 assert(ownerdata != NULL);
2641
2642 assert(ownerdata->inpropqueue);
2643 /* mark that the expression is not in the queue anymore */
2644 ownerdata->inpropqueue = FALSE;
2645
2646 /* since the expr was in the propagation queue, the propbounds should belong to current propagation and should not be empty
2647 * (propbounds being entire doesn't make much sense, so assert this for now, too, but that could be removed)
2648 */
2649 assert(ownerdata->propboundstag == conshdlrdata->curpropboundstag);
2650 assert(!SCIPintervalIsEntire(SCIP_INTERVAL_INFINITY, ownerdata->propbounds));
2651 assert(!SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, ownerdata->propbounds));
2652
2653 /* this intersects propbounds with activity and auxvar bounds
2654 * I doubt this would be much helpful, since propbounds are already subset of activity and we also propagate
2655 * auxvar bounds separately, so disabling this for now
2656 */
2657 #ifdef SCIP_DISABLED_CODE
2658 propbounds = SCIPgetExprBoundsNonlinear(scip, expr);
2659 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, propbounds) )
2660 {
2661 *infeasible = TRUE;
2662 break;
2663 }
2664 #else
2665 propbounds = ownerdata->propbounds;
2666 #endif
2667
2668 if( ownerdata->nenfos > 0 )
2669 {
2670 /* for nodes with enforcement, call reverse propagation callbacks of nlhdlrs */
2671 for( e = 0; e < ownerdata->nenfos && !*infeasible; ++e )
2672 {
2673 SCIP_NLHDLR* nlhdlr;
2674 int nreds;
2675
2676 /* skip nlhdlr if it does not want to participate in activity computation */
2677 if( (ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_ACTIVITY) == 0 )
2678 continue;
2679
2680 nlhdlr = ownerdata->enfos[e]->nlhdlr;
2681 assert(nlhdlr != NULL);
2682
2683 /* call the reverseprop of the nlhdlr */
2684 #ifdef SCIP_DEBUG
2685 SCIPdebugMsg(scip, "call reverse propagation for ");
2686 SCIP_CALL( SCIPprintExpr(scip, expr, NULL) );
2687 SCIPdebugMsgPrint(scip, " in [%g,%g] using nlhdlr <%s>\n", propbounds.inf, propbounds.sup, SCIPnlhdlrGetName(nlhdlr));
2688 #endif
2689
2690 nreds = 0;
2691 SCIP_CALL( SCIPnlhdlrReverseprop(scip, conshdlr, nlhdlr, expr, ownerdata->enfos[e]->nlhdlrexprdata, propbounds, infeasible, &nreds) );
2692 assert(nreds >= 0);
2693 *ntightenings += nreds;
2694 }
2695 }
2696 else if( SCIPexprhdlrHasReverseProp(SCIPexprGetHdlr(expr)) )
2697 {
2698 /* if expr without enforcement (before detect), call reverse propagation callback of exprhdlr directly */
2699 SCIP_INTERVAL* childrenbounds;
2700 int c;
2701
2702 #ifdef SCIP_DEBUG
2703 SCIPdebugMsg(scip, "call reverse propagation for ");
2704 SCIP_CALL( SCIPprintExpr(scip, expr, NULL) );
2705 SCIPdebugMsgPrint(scip, " in [%g,%g] using exprhdlr <%s>\n", SCIPexprGetActivity(expr).inf, SCIPexprGetActivity(expr).sup, SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)));
2706 #endif
2707
2708 /* if someone added an expr without nlhdlr into the reversepropqueue, then this must be because its enfo hasn't
2709 * been initialized in detectNlhdlr yet (nenfos < 0)
2710 */
2711 assert(ownerdata->nenfos < 0);
2712
2713 SCIP_CALL( SCIPallocBufferArray(scip, &childrenbounds, SCIPexprGetNChildren(expr)) );
2714 for( c = 0; c < SCIPexprGetNChildren(expr); ++c )
2715 childrenbounds[c] = SCIPgetExprBoundsNonlinear(scip, SCIPexprGetChildren(expr)[c]);
2716
2717 /* call the reverseprop of the exprhdlr */
2718 SCIP_CALL( SCIPcallExprReverseprop(scip, expr, propbounds, childrenbounds, infeasible) );
2719
2720 if( !*infeasible )
2721 for( c = 0; c < SCIPexprGetNChildren(expr); ++c )
2722 {
2723 SCIP_CALL( SCIPtightenExprIntervalNonlinear(scip, SCIPexprGetChildren(expr)[c], childrenbounds[c], infeasible, ntightenings) );
2724 }
2725
2726 SCIPfreeBufferArray(scip, &childrenbounds);
2727 }
2728 }
2729
2730 /* reset inpropqueue for all remaining expr's in queue (can happen in case of early stop due to infeasibility) */
2731 while( !SCIPqueueIsEmpty(conshdlrdata->reversepropqueue) )
2732 {
2733 expr = (SCIP_EXPR*) SCIPqueueRemove(conshdlrdata->reversepropqueue);
2734 assert(expr != NULL);
2735
2736 ownerdata = SCIPexprGetOwnerData(expr);
2737 assert(ownerdata != NULL);
2738
2739 /* mark that the expression is not in the queue anymore */
2740 ownerdata->inpropqueue = FALSE;
2741 }
2742
2743 return SCIP_OKAY;
2744 }
2745
2746 /** calls domain propagation for a given set of constraints
2747 *
2748 * The algorithm alternates calls of forward and reverse propagation.
2749 * Forward propagation ensures that activity of expressions is up to date.
2750 * Reverse propagation tries to derive tighter variable bounds by reversing the activity computation, using the constraints
2751 * [lhs,rhs] interval as starting point.
2752 *
2753 * The propagation algorithm works as follows:
2754 * 1. apply forward propagation (update activities) for all constraints not marked as propagated
2755 * 2. if presolve or propauxvars is disabled: collect expressions for which the constraint sides provide tighter bounds
2756 * if solve and propauxvars is enabled: collect expressions for which auxvars (including those in root exprs)
2757 * provide tighter bounds
2758 * 3. apply reverse propagation to all collected expressions; don't explore
2759 * sub-expressions which have not changed since the beginning of the propagation loop
2760 * 4. if we have found enough tightenings go to 1, otherwise leave propagation loop
2761 *
2762 * @note After calling forward propagation for a constraint, we mark this constraint as propagated. This flag might be
2763 * reset during the reverse propagation when we find a bound tightening of a variable expression contained in the
2764 * constraint. Resetting this flag is done in the EVENTEXEC callback of the event handler
2765 *
2766 * TODO should we distinguish between expressions where activity information is used for separation and those where not,
2767 * e.g., try less to propagate on convex constraints?
2768 */
2769 static
2770 SCIP_RETCODE propConss(
2771 SCIP* scip, /**< SCIP data structure */
2772 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
2773 SCIP_CONS** conss, /**< constraints to propagate */
2774 int nconss, /**< total number of constraints */
2775 SCIP_Bool force, /**< force tightening even if below bound strengthening tolerance */
2776 SCIP_RESULT* result, /**< pointer to store the result */
2777 int* nchgbds /**< buffer to add the number of changed bounds */
2778 )
2779 {
2780 SCIP_CONSHDLRDATA* conshdlrdata;
2781 SCIP_CONSDATA* consdata;
2782 SCIP_EXPR_OWNERDATA* ownerdata;
2783 SCIP_Bool cutoff = FALSE;
2784 SCIP_INTERVAL conssides;
2785 int ntightenings;
2786 int roundnr;
2787 SCIP_EXPRITER* revpropcollectit = NULL;
2788 int i;
2789
2790 assert(scip != NULL);
2791 assert(conshdlr != NULL);
2792 assert(conss != NULL);
2793 assert(nconss >= 0);
2794 assert(result != NULL);
2795 assert(nchgbds != NULL);
2796 assert(*nchgbds >= 0);
2797
2798 /* no constraints to propagate */
2799 if( nconss == 0 )
2800 {
2801 *result = SCIP_DIDNOTRUN;
2802 return SCIP_OKAY;
2803 }
2804
2805 conshdlrdata = SCIPconshdlrGetData(conshdlr);
2806 assert(conshdlrdata != NULL);
2807 assert(conshdlrdata->intevalvar == intEvalVarBoundTightening);
2808 assert(!conshdlrdata->globalbounds);
2809
2810 *result = SCIP_DIDNOTFIND;
2811 roundnr = 0;
2812
2813 /* tightenAuxVarBounds() needs to know whether boundtightenings are to be forced */
2814 conshdlrdata->forceboundtightening = force;
2815
2816 /* invalidate all propbounds (probably not needed) */
2817 ++conshdlrdata->curpropboundstag;
2818
2819 /* create iterator that we will use if we need to look at all auxvars */
2820 if( conshdlrdata->propauxvars )
2821 {
2822 SCIP_CALL( SCIPcreateExpriter(scip, &revpropcollectit) );
2823 }
2824
2825 /* main propagation loop */
2826 do
2827 {
2828 SCIPdebugMsg(scip, "start propagation round %d\n", roundnr);
2829
2830 assert(SCIPqueueIsEmpty(conshdlrdata->reversepropqueue));
2831
2832 /* apply forward propagation (update expression activities)
2833 * and add promising root expressions into queue for reversepropagation
2834 */
2835 for( i = 0; i < nconss; ++i )
2836 {
2837 consdata = SCIPconsGetData(conss[i]);
2838 assert(consdata != NULL);
2839
2840 /* skip deleted, non-active, or propagation-disabled constraints */
2841 if( SCIPconsIsDeleted(conss[i]) || !SCIPconsIsActive(conss[i]) || !SCIPconsIsPropagationEnabled(conss[i]) )
2842 continue;
2843
2844 /* skip already propagated constraints, i.e., constraints where no (original) variable has changed and thus
2845 * activity didn't change
2846 */
2847 if( consdata->ispropagated )
2848 continue;
2849
2850 /* update activities in expression */
2851 SCIPdebugMsg(scip, "call forwardPropExpr() for constraint <%s> (round %d): ", SCIPconsGetName(conss[i]), roundnr);
2852 SCIPdebugPrintCons(scip, conss[i], NULL);
2853
2854 ntightenings = 0;
2855 SCIP_CALL( forwardPropExpr(scip, conshdlr, consdata->expr, TRUE, &cutoff, &ntightenings) );
2856 assert(cutoff || !SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(consdata->expr)));
2857
2858 if( cutoff )
2859 {
2860 SCIPdebugMsg(scip, " -> cutoff in forwardPropExpr (due to domain error or auxvar tightening) of constraint <%s>\n", SCIPconsGetName(conss[i]));
2861 *result = SCIP_CUTOFF;
2862 break;
2863 }
2864
2865 ownerdata = SCIPexprGetOwnerData(consdata->expr);
2866
2867 /* TODO for a constraint that only has an auxvar for consdata->expr (e.g., convex quadratic), we could also just do the if(TRUE)-branch */
2868 if( !conshdlrdata->propauxvars || ownerdata->auxvar == NULL )
2869 {
2870 /* check whether constraint sides (relaxed by epsilon) or auxvar bounds provide a tightening
2871 * (if we have auxvar (not in presolve), then bounds of the auxvar are initially set to constraint sides,
2872 * so taking auxvar bounds is enough)
2873 */
2874 if( ownerdata->auxvar == NULL )
2875 {
2876 /* relax sides by SCIPepsilon() and handle infinite sides */
2877 SCIP_Real lhs = SCIPisInfinity(scip, -consdata->lhs) ? -SCIP_INTERVAL_INFINITY : consdata->lhs - conshdlrdata->conssiderelaxamount;
2878 SCIP_Real rhs = SCIPisInfinity(scip, consdata->rhs) ? SCIP_INTERVAL_INFINITY : consdata->rhs + conshdlrdata->conssiderelaxamount;
2879 SCIPintervalSetBounds(&conssides, lhs, rhs);
2880 }
2881 else
2882 {
2883 conssides = intEvalVarBoundTightening(scip, ownerdata->auxvar, (void*)conshdlrdata);
2884 }
2885 SCIP_CALL( SCIPtightenExprIntervalNonlinear(scip, consdata->expr, conssides, &cutoff, &ntightenings) );
2886 }
2887 else
2888 {
2889 /* check whether bounds of any auxvar used in constraint provides a tightening
2890 * (for the root expression, bounds of auxvar are initially set to constraint sides)
2891 * but skip exprs that have an auxvar, but do not participate in propagation
2892 */
2893 SCIP_EXPR* expr;
2894
2895 assert(revpropcollectit != NULL);
2896 SCIP_CALL( SCIPexpriterInit(revpropcollectit, consdata->expr, SCIP_EXPRITER_BFS, FALSE) );
2897 for( expr = SCIPexpriterGetCurrent(revpropcollectit); !SCIPexpriterIsEnd(revpropcollectit) && !cutoff; expr = SCIPexpriterGetNext(revpropcollectit) )
2898 {
2899 ownerdata = SCIPexprGetOwnerData(expr);
2900 assert(ownerdata != NULL);
2901
2902 if( ownerdata->auxvar == NULL )
2903 continue;
2904
2905 if( ownerdata->nactivityusesprop == 0 && ownerdata->nactivityusessepa == 0 )
2906 continue;
2907
2908 conssides = intEvalVarBoundTightening(scip, ownerdata->auxvar, (void*)conshdlrdata);
2909 SCIP_CALL( SCIPtightenExprIntervalNonlinear(scip, expr, conssides, &cutoff, &ntightenings) );
2910 }
2911 }
2912
2913 if( cutoff )
2914 {
2915 SCIPdebugMsg(scip, " -> cutoff after intersect with conssides of constraint <%s>\n", SCIPconsGetName(conss[i]));
2916 *result = SCIP_CUTOFF;
2917 break;
2918 }
2919
2920 assert(ntightenings >= 0);
2921 if( ntightenings > 0 )
2922 {
2923 *nchgbds += ntightenings;
2924 *result = SCIP_REDUCEDDOM;
2925 }
2926
2927 /* mark constraint as propagated; this will be reset via the event system when we find a variable tightening */
2928 consdata->ispropagated = TRUE;
2929 }
2930
2931 /* apply backward propagation (if cutoff is TRUE, then this call empties the queue) */
2932 SCIP_CALL( reversePropQueue(scip, conshdlr, &cutoff, &ntightenings) );
2933 assert(ntightenings >= 0);
2934 assert(SCIPqueueIsEmpty(conshdlrdata->reversepropqueue));
2935
2936 if( cutoff )
2937 {
2938 SCIPdebugMsg(scip, " -> cutoff\n");
2939 *result = SCIP_CUTOFF;
2940 break;
2941 }
2942
2943 if( ntightenings > 0 )
2944 {
2945 *nchgbds += ntightenings;
2946 *result = SCIP_REDUCEDDOM;
2947 }
2948 }
2949 while( ntightenings > 0 && ++roundnr < conshdlrdata->maxproprounds );
2950
2951 if( conshdlrdata->propauxvars )
2952 {
2953 SCIPfreeExpriter(&revpropcollectit);
2954 }
2955
2956 conshdlrdata->forceboundtightening = FALSE;
2957
2958 /* invalidate propbounds in all exprs, so noone accidentally uses them outside propagation */
2959 ++conshdlrdata->curpropboundstag;
2960
2961 return SCIP_OKAY;
2962 }
2963
2964 /** calls the reverseprop callbacks of all nlhdlrs in all expressions in all constraints using activity as bounds
2965 *
2966 * This is meant to propagate any domain restrictions on functions onto variable bounds, if possible.
2967 *
2968 * Assumes that activities are still valid and curpropboundstag does not need to be increased.
2969 * Therefore, a good place to call this function is immediately after propConss() or after forwardPropExpr() if outside propagation.
2970 */
2971 static
2972 SCIP_RETCODE propExprDomains(
2973 SCIP* scip, /**< SCIP data structure */
2974 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
2975 SCIP_CONS** conss, /**< constraints to propagate */
2976 int nconss, /**< total number of constraints */
2977 SCIP_RESULT* result, /**< pointer to store the result */
2978 int* nchgbds /**< buffer to add the number of changed bounds */
2979 )
2980 {
2981 SCIP_CONSDATA* consdata;
2982 SCIP_EXPRITER* it;
2983 SCIP_EXPR* expr;
2984 SCIP_EXPR_OWNERDATA* ownerdata;
2985 SCIP_Bool cutoff = FALSE;
2986 int ntightenings;
2987 int c;
2988 int e;
2989
2990 assert(scip != NULL);
2991 assert(conshdlr != NULL);
2992 assert(conss != NULL);
2993 assert(nconss >= 0);
2994 assert(result != NULL);
2995 assert(nchgbds != NULL);
2996 assert(*nchgbds >= 0);
2997
2998 assert(SCIPconshdlrGetData(conshdlr)->intevalvar == intEvalVarBoundTightening);
2999 assert(!SCIPconshdlrGetData(conshdlr)->globalbounds);
3000 assert(SCIPqueueIsEmpty(SCIPconshdlrGetData(conshdlr)->reversepropqueue));
3001
3002 *result = SCIP_DIDNOTFIND;
3003
3004 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
3005 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) );
3006
3007 for( c = 0; c < nconss && !cutoff; ++c )
3008 {
3009 /* skip deleted, non-active, or propagation-disabled constraints */
3010 if( SCIPconsIsDeleted(conss[c]) || !SCIPconsIsActive(conss[c]) || !SCIPconsIsPropagationEnabled(conss[c]) )
3011 continue;
3012
3013 consdata = SCIPconsGetData(conss[c]);
3014 assert(consdata != NULL);
3015
3016 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it) && !cutoff; expr = SCIPexpriterGetNext(it) )
3017 {
3018 ownerdata = SCIPexprGetOwnerData(expr);
3019 assert(ownerdata != NULL);
3020
3021 /* call reverseprop for those nlhdlr that participate in this expr's activity computation
3022 * this will propagate the current activity
3023 */
3024 for( e = 0; e < ownerdata->nenfos; ++e )
3025 {
3026 SCIP_NLHDLR* nlhdlr;
3027 assert(ownerdata->enfos[e] != NULL);
3028
3029 nlhdlr = ownerdata->enfos[e]->nlhdlr;
3030 assert(nlhdlr != NULL);
3031 if( (ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_ACTIVITY) == 0 )
3032 continue;
3033
3034 SCIPdebugMsg(scip, "propExprDomains calling reverseprop for expression %p [%g,%g]\n", (void*)expr,
3035 SCIPexprGetActivity(expr).inf, SCIPexprGetActivity(expr).sup);
3036 ntightenings = 0;
3037 SCIP_CALL( SCIPnlhdlrReverseprop(scip, conshdlr, nlhdlr, expr, ownerdata->enfos[e]->nlhdlrexprdata,
3038 SCIPexprGetActivity(expr), &cutoff, &ntightenings) );
3039
3040 if( cutoff )
3041 {
3042 /* stop everything if we detected infeasibility */
3043 SCIPdebugMsg(scip, "detect infeasibility for constraint <%s> during reverseprop()\n", SCIPconsGetName(conss[c]));
3044 *result = SCIP_CUTOFF;
3045 break;
3046 }
3047
3048 assert(ntightenings >= 0);
3049 if( ntightenings > 0 )
3050 {
3051 *nchgbds += ntightenings;
3052 *result = SCIP_REDUCEDDOM;
3053 }
3054 }
3055 }
3056 }
3057
3058 /* apply backward propagation (if cutoff is TRUE, then this call empties the queue) */
3059 SCIP_CALL( reversePropQueue(scip, conshdlr, &cutoff, &ntightenings) );
3060 assert(ntightenings >= 0);
3061
3062 if( cutoff )
3063 {
3064 SCIPdebugMsg(scip, " -> cutoff\n");
3065 *result = SCIP_CUTOFF;
3066 }
3067 else if( ntightenings > 0 )
3068 {
3069 *nchgbds += ntightenings;
3070 *result = SCIP_REDUCEDDOM;
3071 }
3072
3073 SCIPfreeExpriter(&it);
3074
3075 /* invalidate propbounds in all exprs, so noone accidentally uses them outside propagation */
3076 ++SCIPconshdlrGetData(conshdlr)->curpropboundstag;
3077
3078 return SCIP_OKAY;
3079 }
3080
3081 /** propagates variable locks through expression and adds locks to variables */
3082 static
3083 SCIP_RETCODE propagateLocks(
3084 SCIP* scip, /**< SCIP data structure */
3085 SCIP_EXPR* expr, /**< expression */
3086 int nlockspos, /**< number of positive locks */
3087 int nlocksneg /**< number of negative locks */
3088 )
3089 {
3090 SCIP_EXPR_OWNERDATA* ownerdata;
3091 SCIP_EXPRITER* it;
3092 SCIP_EXPRITER_USERDATA ituserdata;
3093
3094 assert(expr != NULL);
3095
3096 /* if no locks, then nothing to propagate */
3097 if( nlockspos == 0 && nlocksneg == 0 )
3098 return SCIP_OKAY;
3099
3100 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
3101 SCIP_CALL( SCIPexpriterInit(it, expr, SCIP_EXPRITER_DFS, TRUE) );
3102 SCIPexpriterSetStagesDFS(it, SCIP_EXPRITER_ENTEREXPR | SCIP_EXPRITER_VISITINGCHILD | SCIP_EXPRITER_LEAVEEXPR);
3103 assert(SCIPexpriterGetCurrent(it) == expr); /* iterator should not have moved */
3104
3105 /* store locks in root node */
3106 ituserdata.intvals[0] = nlockspos;
3107 ituserdata.intvals[1] = nlocksneg;
3108 SCIPexpriterSetCurrentUserData(it, ituserdata);
3109
3110 while( !SCIPexpriterIsEnd(it) )
3111 {
3112 /* collect locks */
3113 ituserdata = SCIPexpriterGetCurrentUserData(it);
3114 nlockspos = ituserdata.intvals[0];
3115 nlocksneg = ituserdata.intvals[1];
3116
3117 ownerdata = SCIPexprGetOwnerData(expr);
3118
3119 switch( SCIPexpriterGetStageDFS(it) )
3120 {
3121 case SCIP_EXPRITER_ENTEREXPR:
3122 {
3123 if( SCIPisExprVar(scip, expr) )
3124 {
3125 /* if a variable, then also add nlocksneg/nlockspos via SCIPaddVarLocks() */
3126 SCIP_CALL( SCIPaddVarLocks(scip, SCIPgetVarExprVar(expr), nlocksneg, nlockspos) );
3127 }
3128
3129 /* add locks to expression */
3130 ownerdata->nlockspos += nlockspos;
3131 ownerdata->nlocksneg += nlocksneg;
3132
3133 /* add monotonicity information if expression has been locked for the first time */
3134 if( ownerdata->nlockspos == nlockspos && ownerdata->nlocksneg == nlocksneg && SCIPexprGetNChildren(expr) > 0
3135 && SCIPexprhdlrHasMonotonicity(SCIPexprGetHdlr(expr)) )
3136 {
3137 int i;
3138
3139 assert(ownerdata->monotonicity == NULL);
3140 assert(ownerdata->monotonicitysize == 0);
3141
3142 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &ownerdata->monotonicity, SCIPexprGetNChildren(expr)) );
3143 ownerdata->monotonicitysize = SCIPexprGetNChildren(expr);
3144
3145 /* store the monotonicity for each child */
3146 for( i = 0; i < SCIPexprGetNChildren(expr); ++i )
3147 {
3148 SCIP_CALL( SCIPcallExprMonotonicity(scip, expr, i, &ownerdata->monotonicity[i]) );
3149 }
3150 }
3151 break;
3152 }
3153
3154 case SCIP_EXPRITER_LEAVEEXPR :
3155 {
3156 /* remove monotonicity information if expression has been unlocked */
3157 if( ownerdata->nlockspos == 0 && ownerdata->nlocksneg == 0 && ownerdata->monotonicity != NULL )
3158 {
3159 assert(ownerdata->monotonicitysize > 0);
3160 /* keep this assert for checking whether someone changed an expression without updating locks properly */
3161 assert(ownerdata->monotonicitysize == SCIPexprGetNChildren(expr));
3162
3163 SCIPfreeBlockMemoryArray(scip, &ownerdata->monotonicity, ownerdata->monotonicitysize);
3164 ownerdata->monotonicitysize = 0;
3165 }
3166 break;
3167 }
3168
3169 case SCIP_EXPRITER_VISITINGCHILD :
3170 {
3171 SCIP_MONOTONE monotonicity;
3172
3173 /* get monotonicity of child */
3174 /* NOTE: the monotonicity stored in an expression might be different from the result obtained by
3175 * SCIPcallExprMonotonicity
3176 */
3177 monotonicity = ownerdata->monotonicity != NULL ? ownerdata->monotonicity[SCIPexpriterGetChildIdxDFS(it)] : SCIP_MONOTONE_UNKNOWN;
3178
3179 /* compute resulting locks of the child expression */
3180 switch( monotonicity )
3181 {
3182 case SCIP_MONOTONE_INC:
3183 ituserdata.intvals[0] = nlockspos;
3184 ituserdata.intvals[1] = nlocksneg;
3185 break;
3186 case SCIP_MONOTONE_DEC:
3187 ituserdata.intvals[0] = nlocksneg;
3188 ituserdata.intvals[1] = nlockspos;
3189 break;
3190 case SCIP_MONOTONE_UNKNOWN:
3191 ituserdata.intvals[0] = nlockspos + nlocksneg;
3192 ituserdata.intvals[1] = nlockspos + nlocksneg;
3193 break;
3194 case SCIP_MONOTONE_CONST:
3195 ituserdata.intvals[0] = 0;
3196 ituserdata.intvals[1] = 0;
3197 break;
3198 }
3199 /* set locks in child expression */
3200 SCIPexpriterSetChildUserData(it, ituserdata);
3201
3202 break;
3203 }
3204
3205 default :
3206 /* you should never be here */
3207 SCIPABORT();
3208 break;
3209 }
3210
3211 expr = SCIPexpriterGetNext(it);
3212 }
3213
3214 SCIPfreeExpriter(&it);
3215
3216 return SCIP_OKAY;
3217 }
3218
3219 /** main function for adding locks to expressions and variables
3220 *
3221 * Locks for a nonlinear constraint are used to update locks for all sub-expressions and variables.
3222 * Locks of expressions depend on the monotonicity of expressions w.r.t. their children, e.g.,
3223 * consider the constraint \f$x^2 \leq 1\f$ with \f$x \in [-2,-1]\f$ implies an up-lock for the root
3224 * expression (pow) and a down-lock for its child \f$x\f$ because \f$x^2\f$ is decreasing on [-2,-1].
3225 * Since the monotonicity (and thus the locks) might also depend on variable bounds, the function remembers
3226 * the computed monotonicity information of each expression until all locks of an expression have been removed,
3227 * which implies that updating the monotonicity information during the next locking of this expression does not
3228 * break existing locks.
3229 *
3230 * @note When modifying the structure of an expression, e.g., during simplification, it is necessary to remove all
3231 * locks from an expression and repropagating them after the structural changes have been applied.
3232 * Because of existing common sub-expressions, it might be necessary to remove the locks of all constraints
3233 * to ensure that an expression is unlocked (see canonicalizeConstraints() for an example)
3234 */
3235 static
3236 SCIP_RETCODE addLocks(
3237 SCIP* scip, /**< SCIP data structure */
3238 SCIP_CONS* cons, /**< nonlinear constraint */
3239 int nlockspos, /**< number of positive rounding locks */
3240 int nlocksneg /**< number of negative rounding locks */
3241 )
3242 {
3243 SCIP_CONSDATA* consdata;
3244
3245 assert(cons != NULL);
3246
3247 if( nlockspos == 0 && nlocksneg == 0 )
3248 return SCIP_OKAY;
3249
3250 consdata = SCIPconsGetData(cons);
3251 assert(consdata != NULL);
3252
3253 /* no constraint sides -> nothing to lock */
3254 if( SCIPisInfinity(scip, consdata->rhs) && SCIPisInfinity(scip, -consdata->lhs) )
3255 return SCIP_OKAY;
3256
3257 /* remember locks */
3258 consdata->nlockspos += nlockspos;
3259 consdata->nlocksneg += nlocksneg;
3260
3261 assert(consdata->nlockspos >= 0);
3262 assert(consdata->nlocksneg >= 0);
3263
3264 /* compute locks for lock propagation */
3265 if( !SCIPisInfinity(scip, consdata->rhs) && !SCIPisInfinity(scip, -consdata->lhs) )
3266 {
3267 SCIP_CALL( propagateLocks(scip, consdata->expr, nlockspos + nlocksneg, nlockspos + nlocksneg));
3268 }
3269 else if( !SCIPisInfinity(scip, consdata->rhs) )
3270 {
3271 SCIP_CALL( propagateLocks(scip, consdata->expr, nlockspos, nlocksneg));
3272 }
3273 else
3274 {
3275 assert(!SCIPisInfinity(scip, -consdata->lhs));
3276 SCIP_CALL( propagateLocks(scip, consdata->expr, nlocksneg, nlockspos));
3277 }
3278
3279 return SCIP_OKAY;
3280 }
3281
3282 /** create a nonlinear row representation of a nonlinear constraint and stores them in consdata */
3283 static
3284 SCIP_RETCODE createNlRow(
3285 SCIP* scip, /**< SCIP data structure */
3286 SCIP_CONS* cons /**< nonlinear constraint */
3287 )
3288 {
3289 SCIP_CONSDATA* consdata;
3290
3291 assert(scip != NULL);
3292 assert(cons != NULL);
3293
3294 consdata = SCIPconsGetData(cons);
3295 assert(consdata != NULL);
3296 assert(consdata->expr != NULL);
3297
3298 if( consdata->nlrow != NULL )
3299 {
3300 SCIP_CALL( SCIPreleaseNlRow(scip, &consdata->nlrow) );
3301 }
3302
3303 /* better curvature info will be set in initSolve() just before nlrow is added to NLP */
3304 SCIP_CALL( SCIPcreateNlRow(scip, &consdata->nlrow, SCIPconsGetName(cons), 0.0,
3305 0, NULL, NULL, NULL, consdata->lhs, consdata->rhs, SCIP_EXPRCURV_UNKNOWN) );
3306
3307 if( SCIPisExprSum(scip, consdata->expr) )
3308 {
3309 /* if root is a sum, then split into linear and nonlinear terms */
3310 SCIP_EXPR* nonlinpart;
3311 SCIP_EXPR* child;
3312 SCIP_Real* coefs;
3313 int i;
3314
3315 coefs = SCIPgetCoefsExprSum(consdata->expr);
3316
3317 /* constant term of sum */
3318 SCIP_CALL( SCIPchgNlRowConstant(scip, consdata->nlrow, SCIPgetConstantExprSum(consdata->expr)) );
3319
3320 /* a sum-expression that will hold the nonlinear terms and be passed to the nlrow eventually */
3321 SCIP_CALL( SCIPcreateExprSum(scip, &nonlinpart, 0, NULL, NULL, 0.0, exprownerCreate, (void*)SCIPconsGetHdlr(cons)) );
3322
3323 for( i = 0; i < SCIPexprGetNChildren(consdata->expr); ++i )
3324 {
3325 child = SCIPexprGetChildren(consdata->expr)[i];
3326 if( SCIPisExprVar(scip, child) )
3327 {
3328 /* linear term */
3329 SCIP_CALL( SCIPaddLinearCoefToNlRow(scip, consdata->nlrow, SCIPgetVarExprVar(child), coefs[i]) );
3330 }
3331 else
3332 {
3333 /* nonlinear term */
3334 SCIP_CALL( SCIPappendExprSumExpr(scip, nonlinpart, child, coefs[i]) );
3335 }
3336 }
3337
3338 if( SCIPexprGetNChildren(nonlinpart) > 0 )
3339 {
3340 /* add expression to nlrow (this will make a copy) */
3341 SCIP_CALL( SCIPsetNlRowExpr(scip, consdata->nlrow, nonlinpart) );
3342 }
3343 SCIP_CALL( SCIPreleaseExpr(scip, &nonlinpart) );
3344 }
3345 else
3346 {
3347 SCIP_CALL( SCIPsetNlRowExpr(scip, consdata->nlrow, consdata->expr) );
3348 }
3349
3350 return SCIP_OKAY;
3351 }
3352
3353 /** compares enfodata by enforcement priority of nonlinear handler
3354 *
3355 * If handlers have same enforcement priority, then compare by detection priority, then by name.
3356 */
3357 static
3358 SCIP_DECL_SORTPTRCOMP(enfodataCmp)
3359 {
3360 SCIP_NLHDLR* h1;
3361 SCIP_NLHDLR* h2;
3362
3363 assert(elem1 != NULL);
3364 assert(elem2 != NULL);
3365
3366 h1 = ((EXPRENFO*)elem1)->nlhdlr;
3367 h2 = ((EXPRENFO*)elem2)->nlhdlr;
3368
3369 assert(h1 != NULL);
3370 assert(h2 != NULL);
3371
3372 if( SCIPnlhdlrGetEnfoPriority(h1) != SCIPnlhdlrGetEnfoPriority(h2) )
3373 return SCIPnlhdlrGetEnfoPriority(h1) - SCIPnlhdlrGetEnfoPriority(h2);
3374
3375 if( SCIPnlhdlrGetDetectPriority(h1) != SCIPnlhdlrGetDetectPriority(h2) )
3376 return SCIPnlhdlrGetDetectPriority(h1) - SCIPnlhdlrGetDetectPriority(h2);
3377
3378 return strcmp(SCIPnlhdlrGetName(h1), SCIPnlhdlrGetName(h2));
3379 }
3380
3381 /** install nlhdlrs in one expression */
3382 static
3383 SCIP_RETCODE detectNlhdlr(
3384 SCIP* scip, /**< SCIP data structure */
3385 SCIP_EXPR* expr, /**< expression for which to run detection routines */
3386 SCIP_CONS* cons /**< constraint for which expr == consdata->expr, otherwise NULL */
3387 )
3388 {
3389 SCIP_EXPR_OWNERDATA* ownerdata;
3390 SCIP_CONSHDLRDATA* conshdlrdata;
3391 SCIP_NLHDLR_METHOD enforcemethodsallowed;
3392 SCIP_NLHDLR_METHOD enforcemethods;
3393 SCIP_NLHDLR_METHOD enforcemethodsnew;
3394 SCIP_NLHDLR_METHOD nlhdlrenforcemethods;
3395 SCIP_NLHDLR_METHOD nlhdlrparticipating;
3396 SCIP_NLHDLREXPRDATA* nlhdlrexprdata;
3397 int enfossize; /* allocated length of expr->enfos array */
3398 int h;
3399
3400 assert(expr != NULL);
3401
3402 ownerdata = SCIPexprGetOwnerData(expr);
3403 assert(ownerdata != NULL);
3404
3405 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr);
3406 assert(conshdlrdata != NULL);
3407 assert(conshdlrdata->auxvarid >= 0);
3408 assert(!conshdlrdata->indetect);
3409
3410 /* there should be no enforcer yet and detection should not even have considered expr yet */
3411 assert(ownerdata->nenfos < 0);
3412 assert(ownerdata->enfos == NULL);
3413
3414 /* check which enforcement methods are required by setting flags in enforcemethods for those that are NOT required
3415 * - if no auxiliary variable is used, then do not need sepabelow or sepaabove
3416 * - if auxiliary variable is used, but nobody positively (up) locks expr -> only need to enforce expr >= auxvar -> no need for underestimation
3417 * - if auxiliary variable is used, but nobody negatively (down) locks expr -> only need to enforce expr <= auxvar -> no need for overestimation
3418 * - if no one uses activity, then do not need activity methods
3419 */
3420 enforcemethods = SCIP_NLHDLR_METHOD_NONE;
3421 if( ownerdata->nauxvaruses == 0 )
3422 enforcemethods |= SCIP_NLHDLR_METHOD_SEPABOTH;
3423 else
3424 {
3425 if( ownerdata->nlockspos == 0 ) /* no need for underestimation */
3426 enforcemethods |= SCIP_NLHDLR_METHOD_SEPABELOW;
3427 if( ownerdata->nlocksneg == 0 ) /* no need for overestimation */
3428 enforcemethods |= SCIP_NLHDLR_METHOD_SEPAABOVE;
3429 }
3430 if( ownerdata->nactivityusesprop == 0 && ownerdata->nactivityusessepa == 0 )
3431 enforcemethods |= SCIP_NLHDLR_METHOD_ACTIVITY;
3432
3433 /* it doesn't make sense to have been called on detectNlhdlr, if the expr isn't used for anything */
3434 assert(enforcemethods != SCIP_NLHDLR_METHOD_ALL);
3435
3436 /* all methods that have not been flagged above are the ones that we want to be handled by nlhdlrs */
3437 enforcemethodsallowed = ~enforcemethods & SCIP_NLHDLR_METHOD_ALL;
3438
3439 ownerdata->nenfos = 0;
3440 enfossize = 2;
3441 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &ownerdata->enfos, enfossize) );
3442 conshdlrdata->indetect = TRUE;
3443
3444 SCIPdebugMsg(scip, "detecting nlhdlrs for %s expression %p (%s); requiring%s%s%s\n",
3445 cons != NULL ? "root" : "non-root", (void*)expr, SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)),
3446 (enforcemethods & SCIP_NLHDLR_METHOD_SEPABELOW) != 0 ? "" : " sepabelow",
3447 (enforcemethods & SCIP_NLHDLR_METHOD_SEPAABOVE) != 0 ? "" : " sepaabove",
3448 (enforcemethods & SCIP_NLHDLR_METHOD_ACTIVITY) != 0 ? "" : " activity");
3449
3450 for( h = 0; h < conshdlrdata->nnlhdlrs; ++h )
3451 {
3452 SCIP_NLHDLR* nlhdlr;
3453
3454 nlhdlr = conshdlrdata->nlhdlrs[h];
3455 assert(nlhdlr != NULL);
3456
3457 /* skip disabled nlhdlrs */
3458 if( !SCIPnlhdlrIsEnabled(nlhdlr) )
3459 continue;
3460
3461 /* call detect routine of nlhdlr */
3462 nlhdlrexprdata = NULL;
3463 enforcemethodsnew = enforcemethods;
3464 nlhdlrparticipating = SCIP_NLHDLR_METHOD_NONE;
3465 conshdlrdata->registerusesactivitysepabelow = FALSE; /* SCIPregisterExprUsageNonlinear() as called by detect may set this to TRUE */
3466 conshdlrdata->registerusesactivitysepaabove = FALSE; /* SCIPregisterExprUsageNonlinear() as called by detect may set this to TRUE */
3467 SCIP_CALL( SCIPnlhdlrDetect(scip, ownerdata->conshdlr, nlhdlr, expr, cons, &enforcemethodsnew, &nlhdlrparticipating, &nlhdlrexprdata) );
3468
3469 /* nlhdlr might have claimed more than needed: clean up sepa flags */
3470 nlhdlrparticipating &= enforcemethodsallowed;
3471
3472 /* detection is only allowed to augment to nlhdlrenforcemethods, so previous enforcemethods must still be set */
3473 assert((enforcemethodsnew & enforcemethods) == enforcemethods);
3474
3475 /* Because of the previous assert, nlhdlrenforcenew ^ enforcemethods are the methods enforced by this nlhdlr.
3476 * They are also cleaned up here to ensure that only the needed methods are claimed.
3477 */
3478 nlhdlrenforcemethods = (enforcemethodsnew ^ enforcemethods) & enforcemethodsallowed;
3479
3480 /* nlhdlr needs to participate for the methods it is enforcing */
3481 assert((nlhdlrparticipating & nlhdlrenforcemethods) == nlhdlrenforcemethods);
3482
3483 if( nlhdlrparticipating == SCIP_NLHDLR_METHOD_NONE )
3484 {
3485 /* nlhdlr might not have detected anything, or all set flags might have been removed by
3486 * clean up; in the latter case, we may need to free nlhdlrexprdata */
3487
3488 /* free nlhdlr exprdata, if there is any and there is a method to free this data */
3489 if( nlhdlrexprdata != NULL )
3490 {
3491 SCIP_CALL( SCIPnlhdlrFreeexprdata(scip, nlhdlr, expr, &nlhdlrexprdata) );
3492 }
3493 /* nlhdlr cannot have added an enforcement method if it doesn't participate (actually redundant due to previous asserts) */
3494 assert(nlhdlrenforcemethods == SCIP_NLHDLR_METHOD_NONE);
3495
3496 SCIPdebugMsg(scip, "nlhdlr <%s> detect unsuccessful\n", SCIPnlhdlrGetName(nlhdlr));
3497
3498 continue;
3499 }
3500
3501 SCIPdebugMsg(scip, "nlhdlr <%s> detect successful; sepabelow: %s, sepaabove: %s, activity: %s\n",
3502 SCIPnlhdlrGetName(nlhdlr),
3503 ((nlhdlrenforcemethods & SCIP_NLHDLR_METHOD_SEPABELOW) != 0) ? "enforcing" : ((nlhdlrparticipating & SCIP_NLHDLR_METHOD_SEPABELOW) != 0) ? "participating" : "no",
3504 ((nlhdlrenforcemethods & SCIP_NLHDLR_METHOD_SEPAABOVE) != 0) ? "enforcing" : ((nlhdlrparticipating & SCIP_NLHDLR_METHOD_SEPAABOVE) != 0) ? "participating" : "no",
3505 ((nlhdlrenforcemethods & SCIP_NLHDLR_METHOD_ACTIVITY) != 0) ? "enforcing" : ((nlhdlrparticipating & SCIP_NLHDLR_METHOD_ACTIVITY) != 0) ? "participating" : "no");
3506
3507 /* store nlhdlr and its data */
3508 SCIP_CALL( SCIPensureBlockMemoryArray(scip, &ownerdata->enfos, &enfossize, ownerdata->nenfos+1) );
3509 SCIP_CALL( SCIPallocBlockMemory(scip, &ownerdata->enfos[ownerdata->nenfos]) );
3510 ownerdata->enfos[ownerdata->nenfos]->nlhdlr = nlhdlr;
3511 ownerdata->enfos[ownerdata->nenfos]->nlhdlrexprdata = nlhdlrexprdata;
3512 ownerdata->enfos[ownerdata->nenfos]->nlhdlrparticipation = nlhdlrparticipating;
3513 ownerdata->enfos[ownerdata->nenfos]->issepainit = FALSE;
3514 ownerdata->enfos[ownerdata->nenfos]->sepabelowusesactivity = conshdlrdata->registerusesactivitysepabelow;
3515 ownerdata->enfos[ownerdata->nenfos]->sepaaboveusesactivity = conshdlrdata->registerusesactivitysepaabove;
3516 ownerdata->nenfos++;
3517
3518 /* update enforcement flags */
3519 enforcemethods = enforcemethodsnew;
3520 }
3521
3522 conshdlrdata->indetect = FALSE;
3523
3524 /* stop if an enforcement method is missing but we are already in solving stage
3525 * (as long as the expression provides its callbacks, the default nlhdlr should have provided all enforcement methods)
3526 */
3527 if( enforcemethods != SCIP_NLHDLR_METHOD_ALL && SCIPgetStage(scip) == SCIP_STAGE_SOLVING )
3528 {
3529 SCIPerrorMessage("no nonlinear handler provided some of the required enforcement methods\n");
3530 return SCIP_ERROR;
3531 }
3532
3533 assert(ownerdata->nenfos > 0);
3534
3535 /* sort nonlinear handlers by enforcement priority, in decreasing order */
3536 if( ownerdata->nenfos > 1 )
3537 SCIPsortDownPtr((void**)ownerdata->enfos, enfodataCmp, ownerdata->nenfos);
3538
3539 /* resize enfos array to be nenfos long */
3540 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &ownerdata->enfos, enfossize, ownerdata->nenfos) );
3541
3542 return SCIP_OKAY;
3543 }
3544
3545 /** detect nlhdlrs that can handle the expressions */
3546 static
3547 SCIP_RETCODE detectNlhdlrs(
3548 SCIP* scip, /**< SCIP data structure */
3549 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
3550 SCIP_CONS** conss, /**< constraints for which to run nlhdlr detect */
3551 int nconss /**< total number of constraints */
3552 )
3553 {
3554 SCIP_CONSHDLRDATA* conshdlrdata;
3555 SCIP_CONSDATA* consdata;
3556 SCIP_EXPR* expr;
3557 SCIP_EXPR_OWNERDATA* ownerdata;
3558 SCIP_EXPRITER* it;
3559 int i;
3560
3561 assert(conss != NULL || nconss == 0);
3562 assert(nconss >= 0);
3563 assert(SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING || SCIPgetStage(scip) == SCIP_STAGE_INITSOLVE || SCIPgetStage(scip) == SCIP_STAGE_SOLVING); /* should only be called in presolve or initsolve or consactive */
3564
3565 conshdlrdata = SCIPconshdlrGetData(conshdlr);
3566 assert(conshdlrdata != NULL);
3567
3568 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
3569 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, TRUE) );
3570
3571 if( SCIPgetStage(scip) == SCIP_STAGE_SOLVING && SCIPgetDepth(scip) != 0 )
3572 {
3573 /* ensure that activities are recomputed w.r.t. the global variable bounds if CONSACTIVE is called in a local node;
3574 * for example, this happens if globally valid nonlinear constraints are added during the tree search
3575 */
3576 SCIPincrementCurBoundsTagNonlinear(conshdlr, TRUE);
3577 conshdlrdata->globalbounds = TRUE;
3578 conshdlrdata->lastvaractivitymethodchange = conshdlrdata->curboundstag;
3579 }
3580
3581 for( i = 0; i < nconss; ++i )
3582 {
3583 assert(conss != NULL && conss[i] != NULL);
3584
3585 consdata = SCIPconsGetData(conss[i]);
3586 assert(consdata != NULL);
3587 assert(consdata->expr != NULL);
3588
3589 /* if a constraint is separated, we currently need it to be initial, too
3590 * this is because INITLP will create the auxiliary variables that are used for any separation
3591 * TODO we may relax this with a little more programming effort when required, see also TODO in INITLP
3592 */
3593 assert((!SCIPconsIsSeparated(conss[i]) && !SCIPconsIsEnforced(conss[i])) || SCIPconsIsInitial(conss[i]));
3594
3595 ownerdata = SCIPexprGetOwnerData(consdata->expr);
3596 assert(ownerdata != NULL);
3597
3598 /* because of common sub-expressions it might happen that we already detected a nonlinear handler and added it to the expr
3599 * then we would normally skip to run DETECT again
3600 * HOWEVER: most likely we have been running DETECT with cons == NULL, which may interest less nlhdlrs
3601 * thus, if expr is the root expression, we rerun DETECT
3602 */
3603 if( ownerdata->nenfos > 0 )
3604 {
3605 SCIP_CALL( freeEnfoData(scip, consdata->expr, FALSE) );
3606 assert(ownerdata->nenfos < 0);
3607 }
3608
3609 /* if constraint will be enforced, and we are in solve, then ensure auxiliary variable for root expression
3610 * this way we can treat the root expression like any other expression when enforcing via separation
3611 * if constraint will be propagated, then register activity usage of root expression
3612 * this can trigger a call to forwardPropExpr, for which we better have the indetect flag set
3613 */
3614 conshdlrdata->indetect = TRUE;
3615 SCIP_CALL( SCIPregisterExprUsageNonlinear(scip, consdata->expr,
3616 SCIPgetStage(scip) >= SCIP_STAGE_INITSOLVE && (SCIPconsIsSeparated(conss[i]) || SCIPconsIsEnforced(conss[i])),
3617 SCIPconsIsPropagated(conss[i]),
3618 FALSE, FALSE) );
3619 conshdlrdata->indetect = FALSE;
3620
3621 /* compute integrality information for all subexpressions */
3622 SCIP_CALL( SCIPcomputeExprIntegrality(scip, consdata->expr) );
3623
3624 /* run detectNlhdlr on all expr where required */
3625 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
3626 {
3627 ownerdata = SCIPexprGetOwnerData(expr);
3628 assert(ownerdata != NULL);
3629
3630 /* skip exprs that we already looked at */
3631 if( ownerdata->nenfos >= 0 )
3632 continue;
3633
3634 /* if there is use of the auxvar, then someone requires that
3635 * auxvar == expr (or auxvar >= expr or auxvar <= expr) or we are at the root expression (expr==consdata->expr)
3636 * thus, we need to find nlhdlrs that separate or estimate
3637 * if there is use of the activity, then there is someone requiring that
3638 * activity of this expression is updated; this someone would also benefit from better bounds on the activity of this expression
3639 * thus, we need to find nlhdlrs that do interval-evaluation
3640 */
3641 if( ownerdata->nauxvaruses > 0 || ownerdata->nactivityusesprop > 0 || ownerdata->nactivityusessepa > 0 )
3642 {
3643 SCIP_CALL( detectNlhdlr(scip, expr, expr == consdata->expr ? conss[i] : NULL) );
3644
3645 assert(ownerdata->nenfos >= 0);
3646 }
3647 else
3648 {
3649 /* remember that we looked at this expression during detectNlhdlrs
3650 * even though we have not actually run detectNlhdlr, because no nlhdlr showed interest in this expr,
3651 * in some situations (forwardPropExpr, to be specific) we will have to distinguish between exprs for which
3652 * we have not initialized enforcement yet (nenfos < 0) and expressions which are just not used in enforcement (nenfos == 0)
3653 */
3654 ownerdata->nenfos = 0;
3655 }
3656 }
3657
3658 /* include this constraint into the next propagation round because the added nlhdlr may do find tighter bounds now */
3659 if( SCIPconsIsPropagated(conss[i]) )
3660 consdata->ispropagated = FALSE;
3661 }
3662
3663 if( SCIPgetStage(scip) == SCIP_STAGE_SOLVING && SCIPgetDepth(scip) != 0 )
3664 {
3665 /* ensure that the local bounds are used again when reevaluating the expressions later;
3666 * this is only needed if CONSACTIVE is called in a local node (see begin of this function)
3667 */
3668 SCIPincrementCurBoundsTagNonlinear(conshdlr, FALSE);
3669 conshdlrdata->globalbounds = FALSE;
3670 conshdlrdata->lastvaractivitymethodchange = conshdlrdata->curboundstag;
3671 }
3672 else
3673 {
3674 /* ensure that all activities (except for var-exprs) are reevaluated since better methods may be available now */
3675 SCIPincrementCurBoundsTagNonlinear(conshdlr, FALSE);
3676 }
3677
3678 SCIPfreeExpriter(&it);
3679
3680 return SCIP_OKAY;
3681 }
3682
3683 /** initializes (pre)solving data of constraints
3684 *
3685 * This initializes data in a constraint that is used for separation, propagation, etc, and assumes that expressions will
3686 * not be modified.
3687 * In particular, this function
3688 * - runs the detection method of nlhldrs
3689 * - looks for unlocked linear variables
3690 * - checks curvature (if not in presolve)
3691 * - creates and add row to NLP (if not in presolve)
3692 *
3693 * This function can be called in presolve and solve and can be called several times with different sets of constraints,
3694 * e.g., it should be called in INITSOL and for constraints that are added during solve.
3695 */
3696 static
3697 SCIP_RETCODE initSolve(
3698 SCIP* scip, /**< SCIP data structure */
3699 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
3700 SCIP_CONS** conss, /**< constraints */
3701 int nconss /**< number of constraints */
3702 )
3703 {
3704 int c;
3705
3706 for( c = 0; c < nconss; ++c )
3707 {
3708 /* check for a linear variable that can be increase or decreased without harming feasibility */
3709 findUnlockedLinearVar(scip, conss[c]);
3710
3711 if( SCIPgetStage(scip) == SCIP_STAGE_INITSOLVE || SCIPgetStage(scip) == SCIP_STAGE_SOLVING )
3712 {
3713 SCIP_CONSDATA* consdata;
3714 SCIP_Bool success = FALSE;
3715
3716 consdata = SCIPconsGetData(conss[c]); /*lint !e613*/
3717 assert(consdata != NULL);
3718 assert(consdata->expr != NULL);
3719
3720 /* call the curvature detection algorithm of the convex nonlinear handler
3721 * Check only for those curvature that may result in a convex inequality, i.e.,
3722 * whether f(x) is concave when f(x) >= lhs and/or f(x) is convex when f(x) <= rhs.
3723 * Also we can assume that we are nonlinear, so do not check for convex if already concave.
3724 */
3725 if( !SCIPisInfinity(scip, -consdata->lhs) )
3726 {
3727 SCIP_CALL( SCIPhasExprCurvature(scip, consdata->expr, SCIP_EXPRCURV_CONCAVE, &success, NULL) );
3728 if( success )
3729 consdata->curv = SCIP_EXPRCURV_CONCAVE;
3730 }
3731 if( !success && !SCIPisInfinity(scip, consdata->rhs) )
3732 {
3733 SCIP_CALL( SCIPhasExprCurvature(scip, consdata->expr, SCIP_EXPRCURV_CONVEX, &success, NULL) );
3734 if( success )
3735 consdata->curv = SCIP_EXPRCURV_CONVEX;
3736 }
3737 SCIPdebugMsg(scip, "root curvature of constraint %s = %d\n", SCIPconsGetName(conss[c]), consdata->curv);
3738
3739 /* add nlrow representation to NLP, if NLP had been constructed */
3740 if( SCIPisNLPConstructed(scip) && SCIPconsIsActive(conss[c]) )
3741 {
3742 if( consdata->nlrow == NULL )
3743 {
3744 SCIP_CALL( createNlRow(scip, conss[c]) );
3745 assert(consdata->nlrow != NULL);
3746 }
3747 SCIPnlrowSetCurvature(consdata->nlrow, consdata->curv);
3748 SCIP_CALL( SCIPaddNlRow(scip, consdata->nlrow) );
3749 }
3750 }
3751 }
3752
3753 /* register non linear handlers */
3754 SCIP_CALL( detectNlhdlrs(scip, conshdlr, conss, nconss) );
3755
3756 return SCIP_OKAY;
3757 }
3758
3759 /** deinitializes (pre)solving data of constraints
3760 *
3761 * This removes the initialization data created in initSolve().
3762 *
3763 * This function can be called in presolve and solve.
3764 *
3765 * TODO At the moment, it should not be called for a constraint if there are other constraints
3766 * that use the same expressions but still require their nlhdlr.
3767 * We should probably only decrement the auxvar and activity usage for the root expr and then
3768 * proceed as in detectNlhdlrs(), i.e., free enfo data only where none is used.
3769 */
3770 static
3771 SCIP_RETCODE deinitSolve(
3772 SCIP* scip, /**< SCIP data structure */
3773 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
3774 SCIP_CONS** conss, /**< constraints */
3775 int nconss /**< number of constraints */
3776 )
3777 {
3778 SCIP_EXPRITER* it;
3779 SCIP_EXPR* expr;
3780 SCIP_CONSDATA* consdata;
3781 SCIP_Bool rootactivityvalid;
3782 int c;
3783
3784 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
3785 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) );
3786 SCIPexpriterSetStagesDFS(it, SCIP_EXPRITER_LEAVEEXPR);
3787
3788 /* call deinitialization callbacks of expression and nonlinear handlers
3789 * free nonlinear handlers information from expressions
3790 * remove auxiliary variables and nactivityuses counts from expressions
3791 */
3792 for( c = 0; c < nconss; ++c )
3793 {
3794 assert(conss != NULL);
3795 assert(conss[c] != NULL);
3796
3797 consdata = SCIPconsGetData(conss[c]);
3798 assert(consdata != NULL);
3799 assert(consdata->expr != NULL);
3800
3801 /* check and remember whether activity in root is valid */
3802 rootactivityvalid = SCIPexprGetActivityTag(consdata->expr) >= SCIPconshdlrGetData(conshdlr)->lastboundrelax;
3803
3804 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
3805 {
3806 SCIPdebugMsg(scip, "exitsepa and free nonlinear handler data for expression %p\n", (void*)expr);
3807
3808 /* remove nonlinear handlers in expression and their data and auxiliary variables; reset activityusage count */
3809 SCIP_CALL( freeEnfoData(scip, expr, TRUE) );
3810
3811 /* remove quadratic info */
3812 SCIPfreeExprQuadratic(scip, expr);
3813
3814 if( rootactivityvalid )
3815 {
3816 /* ensure activity is valid if consdata->expr activity is valid
3817 * this is mainly to ensure that we do not leave invalid activities in parts of the expression tree where activity was not used,
3818 * e.g., an expr's activity was kept up to date by a nlhdlr, but without using some childs activity
3819 * so this childs activity would be invalid, which can generate confusion
3820 */
3821 SCIP_CALL( SCIPevalExprActivity(scip, expr) );
3822 }
3823 }
3824
3825 if( consdata->nlrow != NULL )
3826 {
3827 /* remove row from NLP, if still in solving
3828 * if we are in exitsolve, the whole NLP will be freed anyway
3829 */
3830 if( SCIPgetStage(scip) == SCIP_STAGE_SOLVING )
3831 {
3832 SCIP_CALL( SCIPdelNlRow(scip, consdata->nlrow) );
3833 }
3834
3835 SCIP_CALL( SCIPreleaseNlRow(scip, &consdata->nlrow) );
3836 }
3837
3838 /* forget about linear variables that can be increased or decreased without harming feasibility */
3839 consdata->linvardecr = NULL;
3840 consdata->linvarincr = NULL;
3841
3842 /* forget about curvature */
3843 consdata->curv = SCIP_EXPRCURV_UNKNOWN;
3844 }
3845
3846 SCIPfreeExpriter(&it);
3847
3848 return SCIP_OKAY;
3849 }
3850
3851 /** helper method to decide whether a given expression is product of at least two binary variables */
3852 static
3853 SCIP_Bool isBinaryProduct(
3854 SCIP* scip, /**< SCIP data structure */
3855 SCIP_EXPR* expr /**< expression */
3856 )
3857 {
3858 int i;
3859
3860 assert(expr != NULL);
3861
3862 /* check whether the expression is a product */
3863 if( !SCIPisExprProduct(scip, expr) )
3864 return FALSE;
3865
3866 /* don't consider products with a coefficient != 1 and products with a single child
3867 * simplification will take care of this expression later
3868 */
3869 if( SCIPexprGetNChildren(expr) <= 1 || SCIPgetCoefExprProduct(expr) != 1.0 )
3870 return FALSE;
3871
3872 for( i = 0; i < SCIPexprGetNChildren(expr); ++i )
3873 {
3874 SCIP_EXPR* child;
3875 SCIP_VAR* var;
3876 SCIP_Real ub;
3877 SCIP_Real lb;
3878
3879 child = SCIPexprGetChildren(expr)[i];
3880 assert(child != NULL);
3881
3882 if( !SCIPisExprVar(scip, child) )
3883 return FALSE;
3884
3885 var = SCIPgetVarExprVar(child);
3886 lb = SCIPvarGetLbLocal(var);
3887 ub = SCIPvarGetUbLocal(var);
3888
3889 /* check whether variable is integer and has [0,1] as variable bounds */
3890 if( !SCIPvarIsIntegral(var) || !SCIPisEQ(scip, lb, 0.0) || !SCIPisEQ(scip, ub, 1.0) )
3891 return FALSE;
3892 }
3893
3894 return TRUE;
3895 }
3896
3897 /** helper method to collect all bilinear binary product terms */
3898 static
3899 SCIP_RETCODE getBilinearBinaryTerms(
3900 SCIP* scip, /**< SCIP data structure */
3901 SCIP_EXPR* sumexpr, /**< sum expression */
3902 SCIP_VAR** xs, /**< array to collect first variable of each bilinear binary product */
3903 SCIP_VAR** ys, /**< array to collect second variable of each bilinear binary product */
3904 int* childidxs, /**< array to store the index of the child of each stored bilinear binary product */
3905 int* nterms /**< pointer to store the total number of bilinear binary terms */
3906 )
3907 {
3908 int i;
3909
3910 assert(sumexpr != NULL);
3911 assert(SCIPisExprSum(scip, sumexpr));
3912 assert(xs != NULL);
3913 assert(ys != NULL);
3914 assert(childidxs != NULL);
3915 assert(nterms != NULL);
3916
3917 *nterms = 0;
3918
3919 for( i = 0; i < SCIPexprGetNChildren(sumexpr); ++i )
3920 {
3921 SCIP_EXPR* child;
3922
3923 child = SCIPexprGetChildren(sumexpr)[i];
3924 assert(child != NULL);
3925
3926 if( SCIPexprGetNChildren(child) == 2 && isBinaryProduct(scip, child) )
3927 {
3928 SCIP_VAR* x = SCIPgetVarExprVar(SCIPexprGetChildren(child)[0]);
3929 SCIP_VAR* y = SCIPgetVarExprVar(SCIPexprGetChildren(child)[1]);
3930
3931 assert(x != NULL);
3932 assert(y != NULL);
3933
3934 if( x != y )
3935 {
3936 xs[*nterms] = x;
3937 ys[*nterms] = y;
3938 childidxs[*nterms] = i;
3939 ++(*nterms);
3940 }
3941 }
3942 }
3943
3944 return SCIP_OKAY;
3945 }
3946
3947 /** helper method to reformulate \f$x_i \sum_j c_{ij} x_j\f$ */
3948 static
3949 SCIP_RETCODE reformulateFactorizedBinaryQuadratic(
3950 SCIP* scip, /**< SCIP data structure */
3951 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
3952 SCIP_CONS* cons, /**< constraint */
3953 SCIP_VAR* facvar, /**< variable that has been factorized */
3954 SCIP_VAR** vars, /**< variables of sum_j c_ij x_j */
3955 SCIP_Real* coefs, /**< coefficients of sum_j c_ij x_j */
3956 int nvars, /**< total number of variables in sum_j c_ij x_j */
3957 SCIP_EXPR** newexpr, /**< pointer to store the new expression */
3958 int* naddconss /**< pointer to update the total number of added constraints (might be NULL) */
3959 )
3960 {
3961 SCIP_VAR* auxvar;
3962 SCIP_CONS* newcons;
3963 SCIP_Real minact = 0.0;
3964 SCIP_Real maxact = 0.0;
3965 SCIP_Bool integral = TRUE;
3966 char name [SCIP_MAXSTRLEN];
3967 int i;
3968
3969 assert(facvar != NULL);
3970 assert(vars != NULL);
3971 assert(nvars > 1);
3972 assert(newexpr != NULL);
3973
3974 /* compute minimum and maximum activity of sum_j c_ij x_j */
3975 /* TODO could compute minact and maxact for facvar=0 and facvar=1 separately, taking implied bounds into account, allowing for possibly tighter big-M's below */
3976 for( i = 0; i < nvars; ++i )
3977 {
3978 minact += MIN(coefs[i], 0.0);
3979 maxact += MAX(coefs[i], 0.0);
3980 integral = integral && SCIPisIntegral(scip, coefs[i]);
3981 }
3982 assert(minact <= maxact);
3983
3984 /* create and add auxiliary variable */
3985 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s", SCIPconsGetName(cons), SCIPvarGetName(facvar));
3986 SCIP_CALL( SCIPcreateVarBasic(scip, &auxvar, name, minact, maxact, 0.0, integral ? SCIP_VARTYPE_IMPLINT : SCIP_VARTYPE_CONTINUOUS) );
3987 SCIP_CALL( SCIPaddVar(scip, auxvar) );
3988
3989 /* create and add z - maxact x <= 0 */
3990 if( !SCIPisZero(scip, maxact) )
3991 {
3992 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s_1", SCIPconsGetName(cons), SCIPvarGetName(facvar));
3993 SCIP_CALL( SCIPcreateConsBasicVarbound(scip, &newcons, name, auxvar, facvar, -maxact, -SCIPinfinity(scip), 0.0) );
3994 SCIP_CALL( SCIPaddCons(scip, newcons) );
3995 SCIP_CALL( SCIPreleaseCons(scip, &newcons) );
3996 if( naddconss != NULL )
3997 ++(*naddconss);
3998 }
3999
4000 /* create and add 0 <= z - minact x */
4001 if( !SCIPisZero(scip, minact) )
4002 {
4003 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s_2", SCIPconsGetName(cons), SCIPvarGetName(facvar));
4004 SCIP_CALL( SCIPcreateConsBasicVarbound(scip, &newcons, name, auxvar, facvar, -minact, 0.0, SCIPinfinity(scip)) );
4005 SCIP_CALL( SCIPaddCons(scip, newcons) );
4006 SCIP_CALL( SCIPreleaseCons(scip, &newcons) );
4007 if( naddconss != NULL )
4008 ++(*naddconss);
4009 }
4010
4011 /* create and add minact <= sum_j c_j x_j - z + minact x_i */
4012 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s_3", SCIPconsGetName(cons), SCIPvarGetName(facvar));
4013 SCIP_CALL( SCIPcreateConsBasicLinear(scip, &newcons, name, nvars, vars, coefs, minact, SCIPinfinity(scip)) );
4014 SCIP_CALL( SCIPaddCoefLinear(scip, newcons, auxvar, -1.0) );
4015 if( !SCIPisZero(scip, minact) )
4016 {
4017 SCIP_CALL( SCIPaddCoefLinear(scip, newcons, facvar, minact) );
4018 }
4019 SCIP_CALL( SCIPaddCons(scip, newcons) );
4020 SCIP_CALL( SCIPreleaseCons(scip, &newcons) );
4021 if( naddconss != NULL )
4022 ++(*naddconss);
4023
4024 /* create and add sum_j c_j x_j - z + maxact x_i <= maxact */
4025 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s_4", SCIPconsGetName(cons), SCIPvarGetName(facvar));
4026 SCIP_CALL( SCIPcreateConsBasicLinear(scip, &newcons, name, nvars, vars, coefs, -SCIPinfinity(scip), maxact) );
4027 SCIP_CALL( SCIPaddCoefLinear(scip, newcons, auxvar, -1.0) );
4028 if( !SCIPisZero(scip, maxact) )
4029 {
4030 SCIP_CALL( SCIPaddCoefLinear(scip, newcons, facvar, maxact) );
4031 }
4032 SCIP_CALL( SCIPaddCons(scip, newcons) );
4033 SCIP_CALL( SCIPreleaseCons(scip, &newcons) );
4034 if( naddconss != NULL )
4035 ++(*naddconss);
4036
4037 /* create variable expression */
4038 SCIP_CALL( createExprVar(scip, conshdlr, newexpr, auxvar) );
4039
4040 /* release auxvar */
4041 SCIP_CALL( SCIPreleaseVar(scip, &auxvar) );
4042
4043 return SCIP_OKAY;
4044 }
4045
4046 /** helper method to generate an expression for a sum of products of binary variables; note that the method captures the generated expression */
4047 static
4048 SCIP_RETCODE getFactorizedBinaryQuadraticExpr(
4049 SCIP* scip, /**< SCIP data structure */
4050 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
4051 SCIP_CONS* cons, /**< constraint */
4052 SCIP_EXPR* sumexpr, /**< expression */
4053 int minterms, /**< minimum number of terms in a the sum of x_i sum_j c_j x_j */
4054 SCIP_EXPR** newexpr, /**< pointer to store the expression that represents the binary quadratic */
4055 int* naddconss /**< pointer to update the total number of added constraints (might be NULL) */
4056 )
4057 {
4058 SCIP_EXPR** exprs = NULL;
4059 SCIP_VAR** tmpvars = NULL;
4060 SCIP_VAR** vars = NULL;
4061 SCIP_VAR** xs = NULL;
4062 SCIP_VAR** ys = NULL;
4063 SCIP_Real* exprcoefs = NULL;
4064 SCIP_Real* tmpcoefs = NULL;
4065 SCIP_Real* sumcoefs;
4066 SCIP_Bool* isused = NULL;
4067 int* childidxs = NULL;
4068 int* count = NULL;
4069 int nchildren;
4070 int nexprs = 0;
4071 int nterms;
4072 int nvars;
4073 int ntotalvars;
4074 int i;
4075
4076 assert(sumexpr != NULL);
4077 assert(minterms > 1);
4078 assert(newexpr != NULL);
4079
4080 *newexpr = NULL;
4081
4082 /* check whether sumexpr is indeed a sum */
4083 if( !SCIPisExprSum(scip, sumexpr) )
4084 return SCIP_OKAY;
4085
4086 nchildren = SCIPexprGetNChildren(sumexpr);
4087 sumcoefs = SCIPgetCoefsExprSum(sumexpr);
4088 nvars = SCIPgetNVars(scip);
4089 ntotalvars = SCIPgetNTotalVars(scip);
4090
4091 /* check whether there are enough terms available */
4092 if( nchildren < minterms )
4093 return SCIP_OKAY;
4094
4095 /* allocate memory */
4096 SCIP_CALL( SCIPallocBufferArray(scip, &xs, nchildren) );
4097 SCIP_CALL( SCIPallocBufferArray(scip, &ys, nchildren) );
4098 SCIP_CALL( SCIPallocBufferArray(scip, &childidxs, nchildren) );
4099
4100 /* collect all bilinear binary product terms */
4101 SCIP_CALL( getBilinearBinaryTerms(scip, sumexpr, xs, ys, childidxs, &nterms) );
4102
4103 /* check whether there are enough terms available */
4104 if( nterms < minterms )
4105 goto TERMINATE;
4106
4107 /* store how often each variable appears in a bilinear binary product */
4108 SCIP_CALL( SCIPduplicateBufferArray(scip, &vars, SCIPgetVars(scip), nvars) );
4109 SCIP_CALL( SCIPallocClearBufferArray(scip, &count, ntotalvars) );
4110 SCIP_CALL( SCIPallocClearBufferArray(scip, &isused, nchildren) );
4111
4112 SCIP_CALL( SCIPallocBufferArray(scip, &exprs, nchildren) );
4113 SCIP_CALL( SCIPallocBufferArray(scip, &exprcoefs, nchildren) );
4114 SCIP_CALL( SCIPallocBufferArray(scip, &tmpvars, MIN(nterms, nvars)) );
4115 SCIP_CALL( SCIPallocBufferArray(scip, &tmpcoefs, MIN(nterms, nvars)) );
4116
4117 for( i = 0; i < nterms; ++i )
4118 {
4119 int xidx;
4120 int yidx;
4121
4122 assert(xs[i] != NULL);
4123 assert(ys[i] != NULL);
4124
4125 xidx = SCIPvarGetIndex(xs[i]);
4126 assert(xidx < ntotalvars);
4127 yidx = SCIPvarGetIndex(ys[i]);
4128 assert(yidx < ntotalvars);
4129
4130 ++count[xidx];
4131 ++count[yidx];
4132
4133 SCIPdebugMsg(scip, "increase counter for %s to %d\n", SCIPvarGetName(xs[i]), count[xidx]);
4134 SCIPdebugMsg(scip, "increase counter for %s to %d\n", SCIPvarGetName(ys[i]), count[yidx]);
4135 }
4136
4137 /* sort variables; don't change order of count array because it depends on problem indices */
4138 {
4139 int* tmpcount;
4140
4141 SCIP_CALL( SCIPduplicateBufferArray(scip, &tmpcount, count, nvars) );
4142 SCIPsortDownIntPtr(tmpcount, (void**)vars, nvars);
4143 SCIPfreeBufferArray(scip, &tmpcount);
4144 }
4145
4146 for( i = 0; i < nvars; ++i )
4147 {
4148 SCIP_VAR* facvar = vars[i];
4149 int ntmpvars = 0;
4150 int j;
4151
4152 /* skip candidate if there are not enough terms left */
4153 if( count[SCIPvarGetIndex(vars[i])] < minterms )
4154 continue;
4155
4156 SCIPdebugMsg(scip, "consider facvar = %s with count = %d\n", SCIPvarGetName(facvar), count[SCIPvarGetIndex(vars[i])]);
4157
4158 /* collect variables for x_i * sum_j c_ij x_j */
4159 for( j = 0; j < nterms; ++j )
4160 {
4161 int childidx = childidxs[j];
4162 assert(childidx >= 0 && childidx < nchildren);
4163
4164 if( !isused[childidx] && (xs[j] == facvar || ys[j] == facvar) )
4165 {
4166 SCIP_Real coef;
4167 int xidx;
4168 int yidx;
4169
4170 coef = sumcoefs[childidx];
4171 assert(coef != 0.0);
4172
4173 /* collect corresponding variable */
4174 tmpvars[ntmpvars] = (xs[j] == facvar) ? ys[j] : xs[j];
4175 tmpcoefs[ntmpvars] = coef;
4176 ++ntmpvars;
4177
4178 /* update counters */
4179 xidx = SCIPvarGetIndex(xs[j]);
4180 assert(xidx < ntotalvars);
4181 yidx = SCIPvarGetIndex(ys[j]);
4182 assert(yidx < ntotalvars);
4183 --count[xidx];
4184 --count[yidx];
4185 assert(count[xidx] >= 0);
4186 assert(count[yidx] >= 0);
4187
4188 /* mark term to be used */
4189 isused[childidx] = TRUE;
4190 }
4191 }
4192 assert(ntmpvars >= minterms);
4193 assert(SCIPvarGetIndex(facvar) < ntotalvars);
4194 assert(count[SCIPvarGetIndex(facvar)] == 0); /* facvar should not appear in any other bilinear term */
4195
4196 /* create required constraints and store the generated expression */
4197 SCIP_CALL( reformulateFactorizedBinaryQuadratic(scip, conshdlr, cons, facvar, tmpvars, tmpcoefs, ntmpvars, &exprs[nexprs], naddconss) );
4198 exprcoefs[nexprs] = 1.0;
4199 ++nexprs;
4200 }
4201
4202 /* factorization was only successful if at least one expression has been generated */
4203 if( nexprs > 0 )
4204 {
4205 int nexprsold = nexprs;
4206
4207 /* add all children of the sum that have not been used */
4208 for( i = 0; i < nchildren; ++i )
4209 {
4210 if( !isused[i] )
4211 {
4212 exprs[nexprs] = SCIPexprGetChildren(sumexpr)[i];
4213 exprcoefs[nexprs] = sumcoefs[i];
4214 ++nexprs;
4215 }
4216 }
4217
4218 /* create a new sum expression */
4219 SCIP_CALL( SCIPcreateExprSum(scip, newexpr, nexprs, exprs, exprcoefs, SCIPgetConstantExprSum(sumexpr), exprownerCreate, (void*)conshdlr) );
4220
4221 /* release all expressions that have been generated by reformulateFactorizedBinaryQuadratic() */
4222 for( i = 0; i < nexprsold; ++i )
4223 {
4224 SCIP_CALL( SCIPreleaseExpr(scip, &exprs[i]) );
4225 }
4226 }
4227
4228 TERMINATE:
4229 /* free memory */
4230 SCIPfreeBufferArrayNull(scip, &tmpcoefs);
4231 SCIPfreeBufferArrayNull(scip, &tmpvars);
4232 SCIPfreeBufferArrayNull(scip, &exprcoefs);
4233 SCIPfreeBufferArrayNull(scip, &exprs);
4234 SCIPfreeBufferArrayNull(scip, &vars);
4235 SCIPfreeBufferArrayNull(scip, &isused);
4236 SCIPfreeBufferArrayNull(scip, &count);
4237 SCIPfreeBufferArray(scip, &childidxs);
4238 SCIPfreeBufferArray(scip, &ys);
4239 SCIPfreeBufferArray(scip, &xs);
4240
4241 return SCIP_OKAY;
4242 }
4243
4244 /** helper method to create an AND constraint or varbound constraints for a given binary product expression */
4245 static
4246 SCIP_RETCODE getBinaryProductExprDo(
4247 SCIP* scip, /**< SCIP data structure */
4248 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
4249 SCIP_EXPR* prodexpr, /**< product expression */
4250 SCIP_EXPR** newexpr, /**< pointer to store the expression that represents the product */
4251 int* naddconss, /**< pointer to update the total number of added constraints (might be NULL) */
4252 SCIP_Bool empathy4and /**< whether to use an AND constraint, if possible */
4253 )
4254 {
4255 SCIP_VAR** vars;
4256 SCIP_CONS* cons;
4257 SCIP_Real* coefs;
4258 SCIP_VAR* w;
4259 char name[SCIP_MAXSTRLEN];
4260 int nchildren;
4261 int i;
4262
4263 assert(conshdlr != NULL);
4264 assert(prodexpr != NULL);
4265 assert(SCIPisExprProduct(scip, prodexpr));
4266 assert(newexpr != NULL);
4267
4268 nchildren = SCIPexprGetNChildren(prodexpr);
4269 assert(nchildren >= 2);
4270
4271 /* memory to store the variables of the variable expressions (+1 for w) */
4272 SCIP_CALL( SCIPallocBufferArray(scip, &vars, nchildren + 1) );
4273 SCIP_CALL( SCIPallocBufferArray(scip, &coefs, nchildren + 1) );
4274
4275 /* prepare the names of the variable and the constraints */
4276 (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform");
4277 for( i = 0; i < nchildren; ++i )
4278 {
4279 vars[i] = SCIPgetVarExprVar(SCIPexprGetChildren(prodexpr)[i]);
4280 coefs[i] = 1.0;
4281 assert(vars[i] != NULL);
4282 (void) strcat(name, "_");
4283 (void) strcat(name, SCIPvarGetName(vars[i]));
4284 }
4285
4286 /* create and add variable */
4287 SCIP_CALL( SCIPcreateVarBasic(scip, &w, name, 0.0, 1.0, 0.0, SCIP_VARTYPE_IMPLINT) );
4288 SCIP_CALL( SCIPaddVar(scip, w) );
4289 SCIPdebugMsg(scip, " created auxiliary variable %s\n", name);
4290
4291 /* use variable bound constraints if it is a bilinear product and there is no empathy for an AND constraint */
4292 if( nchildren == 2 && !empathy4and )
4293 {
4294 SCIP_VAR* x = vars[0];
4295 SCIP_VAR* y = vars[1];
4296
4297 assert(x != NULL);
4298 assert(y != NULL);
4299 assert(x != y);
4300
4301 /* create and add x - w >= 0 */
4302 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s_1", SCIPvarGetName(x), SCIPvarGetName(y));
4303 SCIP_CALL( SCIPcreateConsBasicVarbound(scip, &cons, name, x, w, -1.0, 0.0, SCIPinfinity(scip)) );
4304 SCIP_CALL( SCIPaddCons(scip, cons) );
4305 SCIP_CALL( SCIPreleaseCons(scip, &cons) );
4306
4307 /* create and add y - w >= 0 */
4308 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s_2", SCIPvarGetName(x), SCIPvarGetName(y));
4309 SCIP_CALL( SCIPcreateConsBasicVarbound(scip, &cons, name, y, w, -1.0, 0.0, SCIPinfinity(scip)) );
4310 SCIP_CALL( SCIPaddCons(scip, cons) );
4311 SCIP_CALL( SCIPreleaseCons(scip, &cons) );
4312
4313 /* create and add x + y - w <= 1 */
4314 vars[2] = w;
4315 coefs[2] = -1.0;
4316 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s_3", SCIPvarGetName(x), SCIPvarGetName(y));
4317 SCIP_CALL( SCIPcreateConsBasicLinear(scip, &cons, name, 3, vars, coefs, -SCIPinfinity(scip), 1.0) );
4318 SCIP_CALL( SCIPaddCons(scip, cons) );
4319 SCIP_CALL( SCIPreleaseCons(scip, &cons) );
4320
4321 /* update number of added constraints */
4322 if( naddconss != NULL )
4323 *naddconss += 3;
4324 }
4325 else
4326 {
4327 /* create, add, and release AND constraint */
4328 SCIP_CALL( SCIPcreateConsBasicAnd(scip, &cons, name, w, nchildren, vars) );
4329 SCIP_CALL( SCIPaddCons(scip, cons) );
4330 SCIP_CALL( SCIPreleaseCons(scip, &cons) );
4331 SCIPdebugMsg(scip, " create AND constraint\n");
4332
4333 /* update number of added constraints */
4334 if( naddconss != NULL )
4335 *naddconss += 1;
4336 }
4337
4338 /* create variable expression */
4339 SCIP_CALL( createExprVar(scip, conshdlr, newexpr, w) );
4340
4341 /* release created variable */
4342 SCIP_CALL( SCIPreleaseVar(scip, &w) );
4343
4344 /* free memory */
4345 SCIPfreeBufferArray(scip, &coefs);
4346 SCIPfreeBufferArray(scip, &vars);
4347
4348 return SCIP_OKAY;
4349 }
4350
4351 /** helper method to generate an expression for the product of binary variables; note that the method captures the generated expression */
4352 static
4353 SCIP_RETCODE getBinaryProductExpr(
4354 SCIP* scip, /**< SCIP data structure */
4355 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
4356 SCIP_HASHMAP* exprmap, /**< map to remember generated variables for visited product expressions */
4357 SCIP_EXPR* prodexpr, /**< product expression */
4358 SCIP_EXPR** newexpr, /**< pointer to store the expression that represents the product */
4359 int* naddconss, /**< pointer to update the total number of added constraints (might be NULL) */
4360 int* nchgcoefs /**< pointer to update the total number of changed coefficients (might be NULL) */
4361 )
4362 {
4363 SCIP_CONSHDLRDATA* conshdlrdata;
4364 int nchildren;
4365
4366 assert(prodexpr != NULL);
4367 assert(newexpr != NULL);
4368
4369 *newexpr = NULL;
4370
4371 /* only consider products of binary variables */
4372 if( !isBinaryProduct(scip, prodexpr) )
4373 return SCIP_OKAY;
4374
4375 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4376 assert(conshdlrdata != NULL);
4377 nchildren = SCIPexprGetNChildren(prodexpr);
4378 assert(nchildren >= 2);
4379
4380 /* check whether there is already an expression that represents the product */
4381 if( SCIPhashmapExists(exprmap, (void*)prodexpr) )
4382 {
4383 *newexpr = (SCIP_EXPR*) SCIPhashmapGetImage(exprmap, (void*)prodexpr);
4384 assert(*newexpr != NULL);
4385
4386 /* capture expression */
4387 SCIPcaptureExpr(*newexpr);
4388 }
4389 else
4390 {
4391 SCIPdebugMsg(scip, " product expression %p has been considered for the first time\n", (void*)prodexpr);
4392
4393 if( nchildren == 2 )
4394 {
4395 SCIP_CLIQUE** xcliques;
4396 SCIP_VAR* x;
4397 SCIP_VAR* y;
4398 SCIP_Bool found_clique = FALSE;
4399 int c;
4400
4401 /* get variables from the product expression */
4402 x = SCIPgetVarExprVar(SCIPexprGetChildren(prodexpr)[0]);
4403 assert(x != NULL);
4404 y = SCIPgetVarExprVar(SCIPexprGetChildren(prodexpr)[1]);
4405 assert(y != NULL);
4406 assert(x != y);
4407
4408 /* first try to find a clique containing both variables */
4409 xcliques = SCIPvarGetCliques(x, TRUE);
4410
4411 /* look in cliques containing x */
4412 for( c = 0; c < SCIPvarGetNCliques(x, TRUE); ++c )
4413 {
4414 if( SCIPcliqueHasVar(xcliques[c], y, TRUE) ) /* x + y <= 1 => x*y = 0 */
4415 {
4416 /* create zero value expression */
4417 SCIP_CALL( SCIPcreateExprValue(scip, newexpr, 0.0, exprownerCreate, (void*)conshdlr) );
4418
4419 if( nchgcoefs != NULL )
4420 *nchgcoefs += 1;
4421
4422 found_clique = TRUE;
4423 break;
4424 }
4425
4426 if( SCIPcliqueHasVar(xcliques[c], y, FALSE) ) /* x + (1-y) <= 1 => x*y = x */
4427 {
4428 /* create variable expression for x */
4429 SCIP_CALL( createExprVar(scip, conshdlr, newexpr, x) );
4430
4431 if( nchgcoefs != NULL )
4432 *nchgcoefs += 2;
4433
4434 found_clique = TRUE;
4435 break;
4436 }
4437 }
4438
4439 if( !found_clique )
4440 {
4441 xcliques = SCIPvarGetCliques(x, FALSE);
4442
4443 /* look in cliques containing complement of x */
4444 for( c = 0; c < SCIPvarGetNCliques(x, FALSE); ++c )
4445 {
4446 if( SCIPcliqueHasVar(xcliques[c], y, TRUE) ) /* (1-x) + y <= 1 => x*y = y */
4447 {
4448 /* create variable expression for y */
4449 SCIP_CALL( createExprVar(scip, conshdlr, newexpr, y) );
4450
4451 if( nchgcoefs != NULL )
4452 *nchgcoefs += 1;
4453
4454 found_clique = TRUE;
4455 break;
4456 }
4457
4458 if( SCIPcliqueHasVar(xcliques[c], y, FALSE) ) /* (1-x) + (1-y) <= 1 => x*y = x + y - 1 */
4459 {
4460 /* create sum expression */
4461 SCIP_EXPR* sum_children[2];
4462 SCIP_Real sum_coefs[2];
4463 SCIP_CALL( createExprVar(scip, conshdlr, &sum_children[0], x) );
4464 SCIP_CALL( createExprVar(scip, conshdlr, &sum_children[1], y) );
4465 sum_coefs[0] = 1.0;
4466 sum_coefs[1] = 1.0;
4467 SCIP_CALL( SCIPcreateExprSum(scip, newexpr, 2, sum_children, sum_coefs, -1.0, exprownerCreate, (void*)conshdlr) );
4468
4469 SCIP_CALL( SCIPreleaseExpr(scip, &sum_children[0]) );
4470 SCIP_CALL( SCIPreleaseExpr(scip, &sum_children[1]) );
4471
4472 if( nchgcoefs != NULL )
4473 *nchgcoefs += 3;
4474
4475 found_clique = TRUE;
4476 break;
4477 }
4478 }
4479 }
4480
4481 /* if the variables are not in a clique, do standard linearization */
4482 if( !found_clique )
4483 {
4484 SCIP_CALL( getBinaryProductExprDo(scip, conshdlr, prodexpr, newexpr, naddconss, conshdlrdata->reformbinprodsand) );
4485 }
4486 }
4487 else
4488 {
4489 /* linearize binary product using an AND constraint because nchildren > 2 */
4490 SCIP_CALL( getBinaryProductExprDo(scip, conshdlr, prodexpr, newexpr, naddconss, conshdlrdata->reformbinprodsand) );
4491 }
4492
4493 /* hash variable expression */
4494 SCIP_CALL( SCIPhashmapInsert(exprmap, (void*)prodexpr, *newexpr) );
4495 }
4496
4497 return SCIP_OKAY;
4498 }
4499
4500 /** helper function to replace binary products in a given constraint */
4501 static
4502 SCIP_RETCODE replaceBinaryProducts(
4503 SCIP* scip, /**< SCIP data structure */
4504 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
4505 SCIP_CONS* cons, /**< constraint */
4506 SCIP_HASHMAP* exprmap, /**< map to remember generated variables for visited product expressions */
4507 SCIP_EXPRITER* it, /**< expression iterator */
4508 int* naddconss, /**< pointer to update the total number of added constraints (might be NULL) */
4509 int* nchgcoefs /**< pointer to update the total number of changed coefficients (might be NULL) */
4510 )
4511 {
4512 SCIP_CONSHDLRDATA* conshdlrdata;
4513 SCIP_CONSDATA* consdata;
4514 SCIP_EXPR* expr;
4515
4516 assert(conshdlr != NULL);
4517 assert(cons != NULL);
4518 assert(exprmap != NULL);
4519 assert(it != NULL);
4520
4521 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4522 assert(conshdlrdata != NULL);
4523
4524 consdata = SCIPconsGetData(cons);
4525 assert(consdata != NULL);
4526 assert(consdata->expr != NULL);
4527
4528 SCIPdebugMsg(scip, " check constraint %s\n", SCIPconsGetName(cons));
4529
4530 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
4531 {
4532 SCIP_EXPR* newexpr = NULL;
4533 SCIP_EXPR* childexpr;
4534 int childexpridx;
4535
4536 childexpridx = SCIPexpriterGetChildIdxDFS(it);
4537 assert(childexpridx >= 0 && childexpridx < SCIPexprGetNChildren(expr));
4538 childexpr = SCIPexpriterGetChildExprDFS(it);
4539 assert(childexpr != NULL);
4540
4541 /* try to factorize variables in a sum expression that contains several products of binary variables */
4542 if( conshdlrdata->reformbinprodsfac > 1 )
4543 {
4544 SCIP_CALL( getFactorizedBinaryQuadraticExpr(scip, conshdlr, cons, childexpr, conshdlrdata->reformbinprodsfac, &newexpr, naddconss) );
4545 }
4546
4547 /* try to create an expression that represents a product of binary variables */
4548 if( newexpr == NULL )
4549 {
4550 SCIP_CALL( getBinaryProductExpr(scip, conshdlr, exprmap, childexpr, &newexpr, naddconss, nchgcoefs) );
4551 }
4552
4553 if( newexpr != NULL )
4554 {
4555 assert(naddconss == NULL || *naddconss > 0 || nchgcoefs == NULL || *nchgcoefs > 0);
4556
4557 /* replace product expression */
4558 SCIP_CALL( SCIPreplaceExprChild(scip, expr, childexpridx, newexpr) );
4559
4560 /* note that the expression has been captured by getBinaryProductExpr and SCIPreplaceExprChild */
4561 SCIP_CALL( SCIPreleaseExpr(scip, &newexpr) );
4562
4563 /* mark the constraint to not be simplified anymore */
4564 consdata->issimplified = FALSE;
4565 }
4566 }
4567
4568 return SCIP_OKAY;
4569 }
4570
4571 /** reformulates products of binary variables during presolving in the following way:
4572 *
4573 * Let \f$\sum_{i,j} Q_{ij} x_i x_j\f$ be a subexpression that only contains binary variables.
4574 * Each term \f$x_i x_j\f$ is reformulated with the help of an extra (implicit integer) variable \f$z_{ij}\f$ in {0,1}:
4575 * \f[
4576 * z_{ij} \leq x_i, \qquad z_{ij} \leq x_j, \qquad x_i + x_j - z_{ij} \leq 1.
4577 * \f]
4578 *
4579 * Before reformulating \f$x_i x_j\f$ in this way, it is checked whether there is a clique that contains \f$x_i\f$ and \f$x_j\f$.
4580 * These cliques allow for a better reformulation. There are four cases:
4581 *
4582 * 1. \f$x_i + x_j \leq 1\f$ implies that \f$x_i x_j = 0\f$
4583 * 2. \f$x_i + (1 - x_j) \leq 1\f$ implies \f$x_i x_j = x_i\f$
4584 * 3. \f$(1 - x_i) + x_j \leq 1\f$ implies \f$x_i x_j = x_j\f$
4585 * 4. \f$(1 - x_i) + (1 - x_j) \leq 1\f$ implies \f$x_i x_j = x_i + x_j - 1\f$
4586 *
4587 * The reformulation using \f$z_{ij}\f$ or the cliques is implemented in getBinaryProductExpr().
4588 *
4589 * Introducing too many extra variables and constraints can have a negative impact on the performance (e.g., due to
4590 * slow probing). For this reason, it is checked in getFactorizedBinaryQuadraticExpr() whether \f$\sum_{i,j} Q_{ij} x_i x_j\f$
4591 * contains large (≥ `reformbinprodsfac` parameter) lower sums of the form \f$x_i \sum_j Q_{ij} x_j\f$.
4592 * Such a lower sum is reformulated with only one extra variable w_i:
4593 * \f{align}{
4594 * \text{maxact} & := \sum_j \max(0, Q_{ij}), \\
4595 * \text{minact} & := \sum_j \min(0, Q_{ij}), \\
4596 * \text{minact}\, x_i & \leq w_i, \\
4597 * w_i &\leq \text{maxact}\, x_i, \\
4598 * \text{minact} &\leq \sum_j Q_{ij} x_j - w_i + \text{minact}\, x_i \\
4599 * \text{maxact} &\geq \sum_j Q_{ij} x_j - w_i + \text{maxact}\, x_i
4600 * \f}
4601 * We mark \f$w_i\f$ to be implicit integer if all \f$Q_{ij}\f$ are integer. After each replacement of a lower sum, it
4602 * is checked whether there are enough terms left to factorize other binary variables. Lower sums with a larger number
4603 * of terms are prioritized.
4604 */
4605 static
4606 SCIP_RETCODE presolveBinaryProducts(
4607 SCIP* scip, /**< SCIP data structure */
4608 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
4609 SCIP_CONS** conss, /**< constraints */
4610 int nconss, /**< total number of constraints */
4611 int* naddconss, /**< pointer to store the total number of added constraints (might be NULL) */
4612 int* nchgcoefs /**< pointer to store the total number of changed coefficients (might be NULL) */
4613 )
4614 {
4615 SCIP_CONSHDLRDATA* conshdlrdata;
4616 SCIP_HASHMAP* exprmap;
4617 SCIP_EXPRITER* it;
4618 int c;
4619
4620 assert(conshdlr != NULL);
4621
4622 /* no nonlinear constraints or binary variables -> skip */
4623 if( nconss == 0 || SCIPgetNBinVars(scip) == 0 )
4624 return SCIP_OKAY;
4625 assert(conss != NULL);
4626
4627 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4628 assert(conshdlrdata != NULL);
4629
4630 /* create expression hash map */
4631 SCIP_CALL( SCIPhashmapCreate(&exprmap, SCIPblkmem(scip), SCIPgetNVars(scip)) );
4632
4633 /* create expression iterator */
4634 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
4635 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) );
4636 SCIPexpriterSetStagesDFS(it, SCIP_EXPRITER_VISITINGCHILD);
4637
4638 SCIPdebugMsg(scip, "call presolveBinaryProducts()\n");
4639
4640 for( c = 0; c < nconss; ++c )
4641 {
4642 SCIP_CONSDATA* consdata;
4643 SCIP_EXPR* newexpr = NULL;
4644
4645 assert(conss[c] != NULL);
4646
4647 consdata = SCIPconsGetData(conss[c]);
4648 assert(consdata != NULL);
4649
4650 /* try to reformulate the root expression */
4651 if( conshdlrdata->reformbinprodsfac > 1 )
4652 {
4653 SCIP_CALL( getFactorizedBinaryQuadraticExpr(scip, conshdlr, conss[c], consdata->expr, conshdlrdata->reformbinprodsfac, &newexpr, naddconss) );
4654 }
4655
4656 /* release the root node if another expression has been found */
4657 if( newexpr != NULL )
4658 {
4659 SCIP_CALL( SCIPreleaseExpr(scip, &consdata->expr) );
4660 consdata->expr = newexpr;
4661
4662 /* mark constraint to be not simplified anymore */
4663 consdata->issimplified = FALSE;
4664 }
4665
4666 /* replace each product of binary variables separately */
4667 SCIP_CALL( replaceBinaryProducts(scip, conshdlr, conss[c], exprmap, it, naddconss, nchgcoefs) );
4668 }
4669
4670 /* free memory */
4671 SCIPhashmapFree(&exprmap);
4672 SCIPfreeExpriter(&it);
4673
4674 return SCIP_OKAY;
4675 }
4676
4677 /** scales the sides of the constraint \f$\ell \leq \sum_i c_i f_i(x) \leq r\f$.
4678 *
4679 * Let \f$n_+\f$ the number of positive coefficients \f$c_i\f$ and \f$n_-\f$ be the number of negative coefficients.
4680 * Then scale by -1 if
4681 * - \f$n_+ < n_-\f$, or
4682 * - \f$n_+ = n_-\f$ and \f$r = \infty\f$.
4683 */
4684 static
4685 SCIP_RETCODE scaleConsSides(
4686 SCIP* scip, /**< SCIP data structure */
4687 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */
4688 SCIP_CONS* cons, /**< nonlinear constraint */
4689 SCIP_Bool* changed /**< buffer to store if the expression of cons changed */
4690 )
4691 {
4692 SCIP_CONSDATA* consdata;
4693 int i;
4694
4695 assert(cons != NULL);
4696
4697 consdata = SCIPconsGetData(cons);
4698 assert(consdata != NULL);
4699
4700 if( SCIPisExprSum(scip, consdata->expr) )
4701 {
4702 SCIP_Real* coefs;
4703 SCIP_Real constant;
4704 int nchildren;
4705 int counter = 0;
4706
4707 coefs = SCIPgetCoefsExprSum(consdata->expr);
4708 constant = SCIPgetConstantExprSum(consdata->expr);
4709 nchildren = SCIPexprGetNChildren(consdata->expr);
4710
4711 /* handle special case when constraint is l <= -f(x) <= r and f(x) not a sum: simplfy ensures f is not a sum */
4712 if( nchildren == 1 && constant == 0.0 && coefs[0] == -1.0 )
4713 {
4714 SCIP_EXPR* expr;
4715 expr = consdata->expr;
4716
4717 consdata->expr = SCIPexprGetChildren(expr)[0];
4718 assert(!SCIPisExprSum(scip, consdata->expr));
4719
4720 SCIPcaptureExpr(consdata->expr);
4721
4722 SCIPswapReals(&consdata->lhs, &consdata->rhs);
4723 consdata->lhs = -consdata->lhs;
4724 consdata->rhs = -consdata->rhs;
4725
4726 SCIP_CALL( SCIPreleaseExpr(scip, &expr) );
4727 *changed = TRUE;
4728 return SCIP_OKAY;
4729 }
4730
4731 /* compute n_+ - n_i */
4732 for( i = 0; i < nchildren; ++i )
4733 counter += coefs[i] > 0 ? 1 : -1;
4734
4735 if( counter < 0 || (counter == 0 && SCIPisInfinity(scip, consdata->rhs)) )
4736 {
4737 SCIP_EXPR* expr;
4738 SCIP_Real* newcoefs;
4739
4740 /* allocate memory */
4741 SCIP_CALL( SCIPallocBufferArray(scip, &newcoefs, nchildren) );
4742
4743 for( i = 0; i < nchildren; ++i )
4744 newcoefs[i] = -coefs[i];
4745
4746 /* create a new sum expression */
4747 SCIP_CALL( SCIPcreateExprSum(scip, &expr, nchildren, SCIPexprGetChildren(consdata->expr), newcoefs, -constant, exprownerCreate, (void*)conshdlr) );
4748
4749 /* replace expression in constraint data and scale sides */
4750 SCIP_CALL( SCIPreleaseExpr(scip, &consdata->expr) );
4751 consdata->expr = expr;
4752 SCIPswapReals(&consdata->lhs, &consdata->rhs);
4753 consdata->lhs = -consdata->lhs;
4754 consdata->rhs = -consdata->rhs;
4755
4756 /* free memory */
4757 SCIPfreeBufferArray(scip, &newcoefs);
4758
4759 *changed = TRUE;
4760 }
4761 }
4762
4763 return SCIP_OKAY;
4764 }
4765
4766 /** forbid multiaggrations of variables that appear nonlinear in constraints */
4767 static
4768 SCIP_RETCODE forbidNonlinearVariablesMultiaggration(
4769 SCIP* scip, /**< SCIP data structure */
4770 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
4771 SCIP_CONS** conss, /**< constraints */
4772 int nconss /**< number of constraints */
4773 )
4774 {
4775 SCIP_EXPRITER* it;
4776 SCIP_CONSDATA* consdata;
4777 SCIP_EXPR* expr;
4778 int c;
4779
4780 assert(scip != NULL);
4781 assert(conshdlr != NULL);
4782
4783 if( !SCIPconshdlrGetData(conshdlr)->forbidmultaggrnlvar )
4784 return SCIP_OKAY;
4785
4786 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
4787 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) );
4788
4789 for( c = 0; c < nconss; ++c )
4790 {
4791 consdata = SCIPconsGetData(conss[c]);
4792 assert(consdata != NULL);
4793
4794 /* if root expression is sum, then forbid multiaggregation only for variables that are not in linear terms of sum,
4795 * i.e., skip children of sum that are variables
4796 */
4797 if( SCIPisExprSum(scip, consdata->expr) )
4798 {
4799 int i;
4800 SCIP_EXPR* child;
4801 for( i = 0; i < SCIPexprGetNChildren(consdata->expr); ++i )
4802 {
4803 child = SCIPexprGetChildren(consdata->expr)[i];
4804
4805 /* skip variable expression, as they correspond to a linear term */
4806 if( SCIPisExprVar(scip, child) )
4807 continue;
4808
4809 for( expr = SCIPexpriterRestartDFS(it, child); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
4810 if( SCIPisExprVar(scip, expr) )
4811 {
4812 SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, SCIPgetVarExprVar(expr)) );
4813 }
4814 }
4815 }
4816 else
4817 {
4818 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
4819 if( SCIPisExprVar(scip, expr) )
4820 {
4821 SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, SCIPgetVarExprVar(expr)) );
4822 }
4823 }
4824 }
4825
4826 SCIPfreeExpriter(&it);
4827
4828 return SCIP_OKAY;
4829 }
4830
4831 /** simplifies expressions and replaces common subexpressions for a set of constraints
4832 * @todo put the constant to the constraint sides
4833 */
4834 static
4835 SCIP_RETCODE canonicalizeConstraints(
4836 SCIP* scip, /**< SCIP data structure */
4837 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
4838 SCIP_CONS** conss, /**< constraints */
4839 int nconss, /**< total number of constraints */
4840 SCIP_PRESOLTIMING presoltiming, /**< presolve timing (SCIP_PRESOLTIMING_ALWAYS if not in presolving) */
4841 SCIP_Bool* infeasible, /**< buffer to store whether infeasibility has been detected */
4842 int* ndelconss, /**< counter to add number of deleted constraints, or NULL */
4843 int* naddconss, /**< counter to add number of added constraints, or NULL */
4844 int* nchgcoefs /**< counter to add number of changed coefficients, or NULL */
4845 )
4846 {
4847 SCIP_CONSHDLRDATA* conshdlrdata;
4848 SCIP_CONSDATA* consdata;
4849 int* nlockspos;
4850 int* nlocksneg;
4851 SCIP_Bool havechange;
4852 int i;
4853
4854 assert(scip != NULL);
4855 assert(conshdlr != NULL);
4856 assert(conss != NULL);
4857 assert(nconss > 0);
4858 assert(infeasible != NULL);
4859
4860 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4861 assert(conshdlrdata != NULL);
4862
4863 /* update number of canonicalize calls */
4864 ++(conshdlrdata->ncanonicalizecalls);
4865
4866 SCIP_CALL( SCIPstartClock(scip, conshdlrdata->canonicalizetime) );
4867
4868 *infeasible = FALSE;
4869
4870 /* set havechange to TRUE in the first call of canonicalize; otherwise we might not replace common subexpressions */
4871 havechange = conshdlrdata->ncanonicalizecalls == 1;
4872
4873 /* free nonlinear handlers information from expressions */ /* TODO can skip this in first presolve round */
4874 SCIP_CALL( deinitSolve(scip, conshdlr, conss, nconss) );
4875
4876 /* allocate memory for storing locks of each constraint */
4877 SCIP_CALL( SCIPallocBufferArray(scip, &nlockspos, nconss) );
4878 SCIP_CALL( SCIPallocBufferArray(scip, &nlocksneg, nconss) );
4879
4880 /* unlock all constraints */
4881 for( i = 0; i < nconss; ++i )
4882 {
4883 assert(conss[i] != NULL);
4884
4885 consdata = SCIPconsGetData(conss[i]);
4886 assert(consdata != NULL);
4887
4888 /* remember locks */
4889 nlockspos[i] = consdata->nlockspos;
4890 nlocksneg[i] = consdata->nlocksneg;
4891
4892 /* remove locks */
4893 SCIP_CALL( addLocks(scip, conss[i], -consdata->nlockspos, -consdata->nlocksneg) );
4894 assert(consdata->nlockspos == 0);
4895 assert(consdata->nlocksneg == 0);
4896 }
4897
4898 #ifndef NDEBUG
4899 /* check whether all locks of each expression have been removed */
4900 for( i = 0; i < nconss; ++i )
4901 {
4902 SCIP_EXPR* expr;
4903 SCIP_EXPRITER* it;
4904
4905 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
4906
4907 consdata = SCIPconsGetData(conss[i]);
4908 assert(consdata != NULL);
4909
4910 SCIP_CALL( SCIPexpriterInit(it, consdata->expr, SCIP_EXPRITER_RTOPOLOGIC, TRUE) );
4911 for( expr = consdata->expr; !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
4912 {
4913 assert(expr != NULL);
4914 assert(SCIPexprGetOwnerData(expr)->nlocksneg == 0);
4915 assert(SCIPexprGetOwnerData(expr)->nlockspos == 0);
4916 }
4917 SCIPfreeExpriter(&it);
4918 }
4919 #endif
4920
4921 /* reformulate products of binary variables */
4922 if( conshdlrdata->reformbinprods && SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING
4923 && (presoltiming & SCIP_PRESOLTIMING_EXHAUSTIVE) )
4924 {
4925 int tmpnaddconss = 0;
4926 int tmpnchgcoefs = 0;
4927
4928 /* call this function before simplification because expressions might not be simplified after reformulating
4929 * binary products; the detection of some nonlinear handlers might assume that expressions are simplified
4930 */
4931 SCIP_CALL( presolveBinaryProducts(scip, conshdlr, conss, nconss, &tmpnaddconss, &tmpnchgcoefs) );
4932
4933 /* update counters */
4934 if( naddconss != NULL )
4935 *naddconss = tmpnaddconss;
4936 if( nchgcoefs != NULL )
4937 *nchgcoefs = tmpnchgcoefs;
4938
4939 /* check whether at least one expression has changed */
4940 if( tmpnaddconss + tmpnchgcoefs > 0 )
4941 havechange = TRUE;
4942 }
4943
4944 for( i = 0; i < nconss; ++i )
4945 {
4946 consdata = SCIPconsGetData(conss[i]);
4947 assert(consdata != NULL);
4948
4949 /* call simplify for each expression */
4950 if( !consdata->issimplified && consdata->expr != NULL )
4951 {
4952 SCIP_EXPR* simplified;
4953 SCIP_Bool changed;
4954
4955 changed = FALSE;
4956 SCIP_CALL( SCIPsimplifyExpr(scip, consdata->expr, &simplified, &changed, infeasible, exprownerCreate, (void*)conshdlr) );
4957 consdata->issimplified = TRUE;
4958
4959 if( changed )
4960 havechange = TRUE;
4961
4962 /* If root expression changed, then we need to take care updating the locks as well (the consdata is the one holding consdata->expr "as a child").
4963 * If root expression did not change, some subexpression may still have changed, but the locks were taking care of in the corresponding SCIPreplaceExprChild() call.
4964 */
4965 if( simplified != consdata->expr )
4966 {
4967 assert(changed);
4968
4969 /* release old expression */
4970 SCIP_CALL( SCIPreleaseExpr(scip, &consdata->expr) );
4971
4972 /* store simplified expression */
4973 consdata->expr = simplified;
4974 }
4975 else
4976 {
4977 /* The simplify captures simplified in any case, also if nothing has changed.
4978 * Therefore, we have to release it here.
4979 */
4980 SCIP_CALL( SCIPreleaseExpr(scip, &simplified) );
4981 }
4982
4983 if( *infeasible )
4984 break;
4985
4986 /* scale constraint sides */
4987 SCIP_CALL( scaleConsSides(scip, conshdlr, conss[i], &changed) );
4988
4989 if( changed )
4990 havechange = TRUE;
4991
4992 /* handle constant root expression; either the problem is infeasible or the constraint is redundant */
4993 if( SCIPisExprValue(scip, consdata->expr) )
4994 {
4995 SCIP_Real value = SCIPgetValueExprValue(consdata->expr);
4996 if( (!SCIPisInfinity(scip, -consdata->lhs) && SCIPisFeasNegative(scip, value - consdata->lhs)) ||
4997 (!SCIPisInfinity(scip, consdata->rhs) && SCIPisFeasPositive(scip, value - consdata->rhs)) )
4998 {
4999 SCIPdebugMsg(scip, "<%s> with constant expression found infeasible\n", SCIPconsGetName(conss[i]));
5000 SCIPdebugPrintCons(scip, conss[i], NULL);
5001 *infeasible = TRUE;
5002 break;
5003 }
5004 else
5005 {
5006 SCIP_CALL( addLocks(scip, conss[i], nlockspos[i], nlocksneg[i]) );
5007 SCIP_CALL( SCIPdelCons(scip, conss[i]) );
5008 if( ndelconss != NULL )
5009 ++*ndelconss;
5010 havechange = TRUE;
5011 }
5012 }
5013 }
5014 }
5015
5016 /* replace common subexpressions */
5017 if( havechange && !*infeasible )
5018 {
5019 SCIP_CONS** consssorted;
5020 SCIP_EXPR** rootexprs;
5021 SCIP_Bool replacedroot;
5022
5023 SCIP_CALL( SCIPallocBufferArray(scip, &rootexprs, nconss) );
5024 for( i = 0; i < nconss; ++i )
5025 rootexprs[i] = SCIPconsGetData(conss[i])->expr;
5026
5027 SCIP_CALL( SCIPreplaceCommonSubexpressions(scip, rootexprs, nconss, &replacedroot) );
5028
5029 /* update pointer to root expr in constraints, if any has changed
5030 * SCIPreplaceCommonSubexpressions will have released the old expr and captures the new one
5031 */
5032 if( replacedroot )
5033 for( i = 0; i < nconss; ++i )
5034 SCIPconsGetData(conss[i])->expr = rootexprs[i];
5035
5036 SCIPfreeBufferArray(scip, &rootexprs);
5037
5038 /* TODO this is a possibly expensive way to update the variable expressions stored inside an expression which might have
5039 * been changed after simplification; now we completely recollect all variable expression and variable events
5040 */
5041
5042 /* Each variable stores the constraints for which it catched varbound events sorted by the constraint index.
5043 * Thus, for performance reasons, it is better to call dropVarEvents in descending order of constraint index.
5044 */
5045 SCIP_CALL( SCIPduplicateBufferArray(scip, &consssorted, conss, nconss) );
5046 SCIPsortPtr((void**)consssorted, compIndexConsNonlinear, nconss);
5047
5048 for( i = nconss-1; i >= 0; --i )
5049 {
5050 assert(i == 0 || compIndexConsNonlinear((void*)consssorted[i-1], (void*)consssorted[i]) < 0);
5051 if( SCIPconsIsDeleted(consssorted[i]) )
5052 continue;
5053
5054 SCIP_CALL( dropVarEvents(scip, conshdlrdata->eventhdlr, consssorted[i]) );
5055 SCIP_CALL( freeVarExprs(scip, SCIPconsGetData(consssorted[i])) );
5056 }
5057 for( i = 0; i < nconss; ++i )
5058 {
5059 if( SCIPconsIsDeleted(consssorted[i]) )
5060 continue;
5061
5062 SCIP_CALL( storeVarExprs(scip, conshdlr, SCIPconsGetData(consssorted[i])) );
5063 SCIP_CALL( catchVarEvents(scip, conshdlrdata->eventhdlr, consssorted[i]) );
5064 }
5065
5066 SCIPfreeBufferArray(scip, &consssorted);
5067
5068 /* forbid multiaggregation for nonlinear variables again (in case new variables appeared now)
5069 * a multiaggregation of a nonlinear variable can yield to a large increase in expressions due to
5070 * expanding terms in simplify, e.g. ,(sum_i x_i)^2, so we just forbid these
5071 */
5072 SCIP_CALL( forbidNonlinearVariablesMultiaggration(scip, conshdlr, conss, nconss) );
5073 }
5074
5075 /* restore locks */
5076 for( i = 0; i < nconss; ++i )
5077 {
5078 if( SCIPconsIsDeleted(conss[i]) )
5079 continue;
5080
5081 SCIP_CALL( addLocks(scip, conss[i], nlockspos[i], nlocksneg[i]) );
5082 }
5083
5084 /* run nlhdlr detect if in presolving stage (that is, not in exitpre)
5085 * TODO can we skip this in presoltiming fast?
5086 */
5087 if( SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING && !*infeasible )
5088 {
5089 /* reset one of the number of detections counter to count only current presolving round */
5090 for( i = 0; i < conshdlrdata->nnlhdlrs; ++i )
5091 SCIPnlhdlrResetNDetectionslast(conshdlrdata->nlhdlrs[i]);
5092
5093 SCIP_CALL( initSolve(scip, conshdlr, conss, nconss) );
5094 }
5095
5096 /* free allocated memory */
5097 SCIPfreeBufferArray(scip, &nlocksneg);
5098 SCIPfreeBufferArray(scip, &nlockspos);
5099
5100 SCIP_CALL( SCIPstopClock(scip, conshdlrdata->canonicalizetime) );
5101
5102 return SCIP_OKAY;
5103 }
5104
5105 /** merges constraints that have the same root expression */
5106 static
5107 SCIP_RETCODE presolveMergeConss(
5108 SCIP* scip, /**< SCIP data structure */
5109 SCIP_CONS** conss, /**< constraints to process */
5110 int nconss, /**< number of constraints */
5111 SCIP_Bool* success /**< pointer to store whether at least one constraint could be deleted */
5112 )
5113 {
5114 SCIP_HASHMAP* expr2cons;
5115 SCIP_Bool* updatelocks;
5116 int* nlockspos;
5117 int* nlocksneg;
5118 int c;
5119
5120 assert(success != NULL);
5121
5122 *success = FALSE;
5123
5124 /* not enough constraints available */
5125 if( nconss <= 1 )
5126 return SCIP_OKAY;
5127
5128 SCIP_CALL( SCIPhashmapCreate(&expr2cons, SCIPblkmem(scip), nconss) );
5129 SCIP_CALL( SCIPallocClearBufferArray(scip, &updatelocks, nconss) );
5130 SCIP_CALL( SCIPallocBufferArray(scip, &nlockspos, nconss) );
5131 SCIP_CALL( SCIPallocBufferArray(scip, &nlocksneg, nconss) );
5132
5133 for( c = 0; c < nconss; ++c )
5134 {
5135 SCIP_CONSDATA* consdata;
5136
5137 /* ignore deleted constraints */
5138 if( SCIPconsIsDeleted(conss[c]) )
5139 continue;
5140
5141 consdata = SCIPconsGetData(conss[c]);
5142 assert(consdata != NULL);
5143
5144 /* add expression to the hash map if not seen so far */
5145 if( !SCIPhashmapExists(expr2cons, (void*)consdata->expr) )
5146 {
5147 SCIP_CALL( SCIPhashmapInsertInt(expr2cons, (void*)consdata->expr, c) );
5148 }
5149 else
5150 {
5151 SCIP_CONSDATA* imgconsdata;
5152 int idx;
5153
5154 idx = SCIPhashmapGetImageInt(expr2cons, (void*)consdata->expr);
5155 assert(idx >= 0 && idx < nconss);
5156
5157 imgconsdata = SCIPconsGetData(conss[idx]);
5158 assert(imgconsdata != NULL);
5159 assert(imgconsdata->expr == consdata->expr);
5160
5161 SCIPdebugMsg(scip, "merge constraint %g <= %s <= %g with %g <= %s <= %g\n", consdata->lhs,
5162 SCIPconsGetName(conss[c]), consdata->rhs, imgconsdata->lhs, SCIPconsGetName(conss[idx]), imgconsdata->rhs);
5163
5164 /* check whether locks need to be updated */
5165 if( !updatelocks[idx] && ((SCIPisInfinity(scip, -imgconsdata->lhs) && !SCIPisInfinity(scip, -consdata->lhs))
5166 || (SCIPisInfinity(scip, imgconsdata->rhs) && !SCIPisInfinity(scip, consdata->rhs))) )
5167 {
5168 nlockspos[idx] = imgconsdata->nlockspos;
5169 nlocksneg[idx] = imgconsdata->nlocksneg;
5170 SCIP_CALL( addLocks(scip, conss[idx], -imgconsdata->nlockspos, -imgconsdata->nlocksneg) );
5171 updatelocks[idx] = TRUE;
5172 }
5173
5174 /* update constraint sides */
5175 imgconsdata->lhs = MAX(imgconsdata->lhs, consdata->lhs);
5176 imgconsdata->rhs = MIN(imgconsdata->rhs, consdata->rhs);
5177
5178 /* delete constraint */
5179 SCIP_CALL( SCIPdelCons(scip, conss[c]) );
5180 *success = TRUE;
5181 }
5182 }
5183
5184 /* restore locks of updated constraints */
5185 if( *success )
5186 {
5187 for( c = 0; c < nconss; ++c )
5188 {
5189 if( updatelocks[c] )
5190 {
5191 SCIP_CALL( addLocks(scip, conss[c], nlockspos[c], nlocksneg[c]) );
5192 }
5193 }
5194 }
5195
5196 /* free memory */
5197 SCIPfreeBufferArray(scip, &nlocksneg);
5198 SCIPfreeBufferArray(scip, &nlockspos);
5199 SCIPfreeBufferArray(scip, &updatelocks);
5200 SCIPhashmapFree(&expr2cons);
5201
5202 return SCIP_OKAY;
5203 }
5204
5205 /** interval evaluation of variables as used in redundancy check
5206 *
5207 * Returns local variable bounds of a variable, relaxed by feastol, as interval.
5208 */
5209 static
5210 SCIP_DECL_EXPR_INTEVALVAR(intEvalVarRedundancyCheck)
5211 { /*lint --e{715}*/
5212 SCIP_CONSHDLRDATA* conshdlrdata;
5213 SCIP_INTERVAL interval;
5214 SCIP_Real lb;
5215 SCIP_Real ub;
5216
5217 assert(scip != NULL);
5218 assert(var != NULL);
5219
5220 conshdlrdata = (SCIP_CONSHDLRDATA*)intevalvardata;
5221 assert(conshdlrdata != NULL);
5222
5223 if( conshdlrdata->globalbounds )
5224 {
5225 lb = SCIPvarGetLbGlobal(var);
5226 ub = SCIPvarGetUbGlobal(var);
5227 }
5228 else
5229 {
5230 lb = SCIPvarGetLbLocal(var);
5231 ub = SCIPvarGetUbLocal(var);
5232 }
5233 assert(lb <= ub); /* can SCIP ensure by now that variable bounds are not contradicting? */
5234
5235 /* relax variable bounds, if there are bounds and variable is not fixed
5236 * (actually some assert complains if trying SCIPisRelEQ if both bounds are at different infinity)
5237 */
5238 if( !(SCIPisInfinity(scip, -lb) && SCIPisInfinity(scip, ub)) && !SCIPisRelEQ(scip, lb, ub) )
5239 {
5240 if( !SCIPisInfinity(scip, -lb) )
5241 lb -= SCIPfeastol(scip);
5242
5243 if( !SCIPisInfinity(scip, ub) )
5244 ub += SCIPfeastol(scip);
5245 }
5246
5247 /* convert SCIPinfinity() to SCIP_INTERVAL_INFINITY */
5248 lb = -infty2infty(SCIPinfinity(scip), SCIP_INTERVAL_INFINITY, -lb);
5249 ub = infty2infty(SCIPinfinity(scip), SCIP_INTERVAL_INFINITY, ub);
5250 assert(lb <= ub);
5251
5252 SCIPintervalSetBounds(&interval, lb, ub);
5253
5254 return interval;
5255 }
5256
5257 /** removes constraints that are always feasible or very simple
5258 *
5259 * Checks whether the activity of constraint functions is a subset of the constraint sides (relaxed by feastol).
5260 * To compute the activity, we use forwardPropExpr(), but relax variable bounds by feastol, because solutions to be checked
5261 * might violate variable bounds by up to feastol, too.
5262 * This is the main reason why the redundancy check is not done in propConss(), which relaxes variable bounds by epsilon only.
5263 *
5264 * Also removes constraints of the form lhs ≤ variable ≤ rhs.
5265 *
5266 * @todo it would be sufficient to check constraints for which we know that they are not currently violated by a valid solution
5267 *
5268 * @note This could should not run during solving, because the forwardProp takes the bounds of auxiliary variables into account.
5269 * For the root expression, these bounds are already set to the constraint sides, so that the activity of every expression
5270 * would appear as if the constraint is redundant.
5271 */
5272 static
5273 SCIP_RETCODE presolveRedundantConss(
5274 SCIP* scip, /**< SCIP data structure */
5275 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
5276 SCIP_CONS** conss, /**< constraints to propagate */
5277 int nconss, /**< total number of constraints */
5278 SCIP_Bool* cutoff, /**< pointer to store whether infeasibility has been identified */
5279 int* ndelconss, /**< buffer to add the number of deleted constraints */
5280 int* nchgbds /**< buffer to add the number of variable bound tightenings */
5281 )
5282 {
5283 SCIP_CONSHDLRDATA* conshdlrdata;
5284 SCIP_CONSDATA* consdata;
5285 SCIP_INTERVAL activity;
5286 SCIP_INTERVAL sides;
5287 int i;
5288
5289 assert(scip != NULL);
5290 assert(conshdlr != NULL);
5291 assert(conss != NULL);
5292 assert(nconss >= 0);
5293 assert(cutoff != NULL);
5294 assert(ndelconss != NULL);
5295 assert(nchgbds != NULL);
5296
5297 /* no constraints to check */
5298 if( nconss == 0 )
5299 return SCIP_OKAY;
5300
5301 conshdlrdata = SCIPconshdlrGetData(conshdlr);
5302 assert(conshdlrdata != NULL);
5303
5304 /* increase curboundstag and set lastvaractivitymethodchange
5305 * we do this here to trigger a reevaluation of all variable bounds, since we will relax variable bounds
5306 * for the redundancy check differently than for domain propagation
5307 * we also update lastboundrelax to ensure activites of all expressions are indeed reevaluated
5308 */
5309 ++conshdlrdata->curboundstag;
5310 assert(conshdlrdata->curboundstag > 0);
5311 conshdlrdata->lastvaractivitymethodchange = conshdlrdata->curboundstag;
5312 conshdlrdata->lastboundrelax = conshdlrdata->curboundstag;
5313 conshdlrdata->intevalvar = intEvalVarRedundancyCheck;
5314
5315 SCIPdebugMsg(scip, "checking %d constraints for redundancy\n", nconss);
5316
5317 *cutoff = FALSE;
5318 for( i = 0; i < nconss; ++i )
5319 {
5320 if( !SCIPconsIsActive(conss[i]) || SCIPconsIsDeleted(conss[i]) )
5321 continue;
5322
5323 consdata = SCIPconsGetData(conss[i]);
5324 assert(consdata != NULL);
5325
5326 /* handle constant expressions separately: either the problem is infeasible or the constraint is redundant */
5327 if( SCIPisExprValue(scip, consdata->expr) )
5328 {
5329 SCIP_Real value = SCIPgetValueExprValue(consdata->expr);
5330
5331 if( (!SCIPisInfinity(scip, -consdata->lhs) && value < consdata->lhs - SCIPfeastol(scip)) ||
5332 (!SCIPisInfinity(scip, consdata->rhs) && value > consdata->rhs + SCIPfeastol(scip)) )
5333 {
5334 SCIPdebugMsg(scip, "constant constraint <%s> is infeasible: %g in [%g,%g] ", SCIPconsGetName(conss[i]), value, consdata->lhs, consdata->rhs);
5335 *cutoff = TRUE;
5336
5337 goto TERMINATE;
5338 }
5339
5340 SCIPdebugMsg(scip, "constant constraint <%s> is redundant: %g in [%g,%g] ", SCIPconsGetName(conss[i]), value, consdata->lhs, consdata->rhs);
5341
5342 SCIP_CALL( SCIPdelConsLocal(scip, conss[i]) );
5343 ++*ndelconss;
5344
5345 continue;
5346 }
5347
5348 /* handle variable expressions separately: tighten variable bounds to constraint sides, then remove constraint (now redundant) */
5349 if( SCIPisExprVar(scip, consdata->expr) )
5350 {
5351 SCIP_VAR* var;
5352 SCIP_Bool tightened;
5353
5354 var = SCIPgetVarExprVar(consdata->expr);
5355 assert(var != NULL);
5356
5357 SCIPdebugMsg(scip, "variable constraint <%s> can be made redundant: <%s>[%g,%g] in [%g,%g]\n", SCIPconsGetName(conss[i]), SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var), consdata->lhs, consdata->rhs);
5358
5359 /* ensure that variable bounds are within constraint sides */
5360 if( !SCIPisInfinity(scip, -consdata->lhs) )
5361 {
5362 SCIP_CALL( SCIPtightenVarLb(scip, var, consdata->lhs, TRUE, cutoff, &tightened) );
5363
5364 if( tightened )
5365 ++*nchgbds;
5366
5367 if( *cutoff )
5368 goto TERMINATE;
5369 }
5370
5371 if( !SCIPisInfinity(scip, consdata->rhs) )
5372 {
5373 SCIP_CALL( SCIPtightenVarUb(scip, var, consdata->rhs, TRUE, cutoff, &tightened) );
5374
5375 if( tightened )
5376 ++*nchgbds;
5377
5378 if( *cutoff )
5379 goto TERMINATE;
5380 }
5381
5382 /* delete the (now) redundant constraint locally */
5383 SCIP_CALL( SCIPdelConsLocal(scip, conss[i]) );
5384 ++*ndelconss;
5385
5386 continue;
5387 }
5388
5389 /* reevaluate expression activity, now using intEvalVarRedundancyCheck
5390 * we relax variable bounds by feastol here, as solutions that are checked later can also violate
5391 * variable bounds by up to feastol
5392 * (relaxing fixed variables seems to be too much, but they would be removed by presolve soon anyway)
5393 */
5394 SCIPdebugMsg(scip, "call forwardPropExpr() for constraint <%s>: ", SCIPconsGetName(conss[i]));
5395 SCIPdebugPrintCons(scip, conss[i], NULL);
5396
5397 SCIP_CALL( forwardPropExpr(scip, conshdlr, consdata->expr, FALSE, cutoff, NULL) );
5398 assert(*cutoff || !SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(consdata->expr)));
5399
5400 /* it is unlikely that we detect infeasibility by doing forward propagation */
5401 if( *cutoff )
5402 {
5403 SCIPdebugMsg(scip, " -> cutoff\n");
5404 goto TERMINATE;
5405 }
5406
5407 assert(SCIPexprGetActivityTag(consdata->expr) == conshdlrdata->curboundstag);
5408 activity = SCIPexprGetActivity(consdata->expr);
5409
5410 /* relax sides by feastol
5411 * we could accept every solution that violates constraints up to feastol as redundant, so this is the most permissive we can be
5412 */
5413 SCIPintervalSetBounds(&sides,
5414 SCIPisInfinity(scip, -consdata->lhs) ? -SCIP_INTERVAL_INFINITY : consdata->lhs - SCIPfeastol(scip),
5415 SCIPisInfinity(scip, consdata->rhs) ? SCIP_INTERVAL_INFINITY : consdata->rhs + SCIPfeastol(scip));
5416
5417 if( SCIPintervalIsSubsetEQ(SCIP_INTERVAL_INFINITY, activity, sides) )
5418 {
5419 SCIPdebugMsg(scip, " -> redundant: activity [%g,%g] within sides [%g,%g]\n", activity.inf, activity.sup, consdata->lhs, consdata->rhs);
5420
5421 SCIP_CALL( SCIPdelConsLocal(scip, conss[i]) );
5422 ++*ndelconss;
5423
5424 continue;
5425 }
5426
5427 SCIPdebugMsg(scip, " -> not redundant: activity [%g,%g] not within sides [%g,%g]\n", activity.inf, activity.sup, consdata->lhs, consdata->rhs);
5428 }
5429
5430 TERMINATE:
5431 /* make sure all activities are reevaluated again, since we relaxed bounds in a different way */
5432 ++conshdlrdata->curboundstag;
5433 conshdlrdata->lastvaractivitymethodchange = conshdlrdata->curboundstag;
5434 conshdlrdata->lastboundrelax = conshdlrdata->curboundstag;
5435 conshdlrdata->intevalvar = intEvalVarBoundTightening;
5436
5437 return SCIP_OKAY;
5438 }
5439
5440 /** tries to automatically convert a nonlinear constraint into a more specific and more specialized constraint */
5441 static
5442 SCIP_RETCODE presolveUpgrade(
5443 SCIP* scip, /**< SCIP data structure */
5444 SCIP_CONSHDLR* conshdlr, /**< constraint handler data structure */
5445 SCIP_CONS* cons, /**< source constraint to try to convert */
5446 SCIP_Bool* upgraded, /**< buffer to store whether constraint was upgraded */
5447 int* nupgdconss, /**< buffer to increase if constraint was upgraded */
5448 int* naddconss /**< buffer to increase with number of additional constraints created during upgrade */
5449 )
5450 {
5451 SCIP_CONSHDLRDATA* conshdlrdata;
5452 SCIP_CONSDATA* consdata;
5453 SCIP_CONS** upgdconss;
5454 int upgdconsssize;
5455 int nupgdconss_;
5456 int i;
5457
5458 assert(scip != NULL);
5459 assert(conshdlr != NULL);
5460 assert(cons != NULL);
5461 assert(!SCIPconsIsModifiable(cons));
5462 assert(upgraded != NULL);
5463 assert(nupgdconss != NULL);
5464 assert(naddconss != NULL);
5465
5466 *upgraded = FALSE;
5467
5468 nupgdconss_ = 0;
5469
5470 conshdlrdata = SCIPconshdlrGetData(conshdlr);
5471 assert(conshdlrdata != NULL);
5472
5473 /* if there are no upgrade methods, we can stop */
5474 if( conshdlrdata->nconsupgrades == 0 )
5475 return SCIP_OKAY;
5476
5477 upgdconsssize = 2;
5478 SCIP_CALL( SCIPallocBufferArray(scip, &upgdconss, upgdconsssize) );
5479
5480 /* call the upgrading methods */
5481 SCIPdebugMsg(scip, "upgrading nonlinear constraint <%s> (up to %d upgrade methods): ", SCIPconsGetName(cons), conshdlrdata->nconsupgrades);
5482 SCIPdebugPrintCons(scip, cons, NULL);
5483
5484 consdata = SCIPconsGetData(cons);
5485 assert(consdata != NULL);
5486
5487 /* try all upgrading methods in priority order in case the upgrading step is enable */
5488 for( i = 0; i < conshdlrdata->nconsupgrades; ++i )
5489 {
5490 if( !conshdlrdata->consupgrades[i]->active )
5491 continue;
5492
5493 assert(conshdlrdata->consupgrades[i]->consupgd != NULL);
5494
5495 SCIP_CALL( conshdlrdata->consupgrades[i]->consupgd(scip, cons, consdata->nvarexprs, &nupgdconss_, upgdconss, upgdconsssize) );
5496
5497 while( nupgdconss_ < 0 )
5498 {
5499 /* upgrade function requires more memory: resize upgdconss and call again */
5500 assert(-nupgdconss_ > upgdconsssize);
5501 upgdconsssize = -nupgdconss_;
5502 SCIP_CALL( SCIPreallocBufferArray(scip, &upgdconss, -nupgdconss_) );
5503
5504 SCIP_CALL( conshdlrdata->consupgrades[i]->consupgd(scip, cons, consdata->nvarexprs, &nupgdconss_, upgdconss, upgdconsssize) );
5505
5506 assert(nupgdconss_ != 0);
5507 }
5508
5509 if( nupgdconss_ > 0 )
5510 {
5511 /* got upgrade */
5512 int j;
5513
5514 SCIPdebugMsg(scip, " -> upgraded to %d constraints:\n", nupgdconss_);
5515
5516 /* add the upgraded constraints to the problem and forget them */
5517 for( j = 0; j < nupgdconss_; ++j )
5518 {
5519 SCIPdebugMsgPrint(scip, "\t");
5520 SCIPdebugPrintCons(scip, upgdconss[j], NULL);
5521
5522 SCIP_CALL( SCIPaddCons(scip, upgdconss[j]) ); /*lint !e613*/
5523 SCIP_CALL( SCIPreleaseCons(scip, &upgdconss[j]) ); /*lint !e613*/
5524 }
5525
5526 /* count the first upgrade constraint as constraint upgrade and the remaining ones as added constraints */
5527 *nupgdconss += 1;
5528 *naddconss += nupgdconss_ - 1;
5529 *upgraded = TRUE;
5530
5531 /* delete upgraded constraint */
5532 SCIPdebugMsg(scip, "delete constraint <%s> after upgrade\n", SCIPconsGetName(cons));
5533 SCIP_CALL( SCIPdelCons(scip, cons) );
5534
5535 break;
5536 }
5537 }
5538
5539 SCIPfreeBufferArray(scip, &upgdconss);
5540
5541 return SCIP_OKAY;
5542 }
5543
5544 /** returns whether the variable of a given variable expression is a candidate for presolveSingleLockedVars(), i.e.,
5545 * the variable is only contained in a single nonlinear constraint, has no objective coefficient, has finite
5546 * variable bounds, and is not binary
5547 */
5548 static
5549 SCIP_Bool isSingleLockedCand(
5550 SCIP* scip, /**< SCIP data structure */
5551 SCIP_EXPR* expr /**< variable expression */
5552 )
5553 {
5554 SCIP_VAR* var;
5555 SCIP_EXPR_OWNERDATA* ownerdata;
5556
5557 assert(SCIPisExprVar(scip, expr));
5558
5559 var = SCIPgetVarExprVar(expr);
5560 assert(var != NULL);
5561
5562 ownerdata = SCIPexprGetOwnerData(expr);
5563 assert(ownerdata != NULL);
5564
5565 return SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL) == ownerdata->nlocksneg
5566 && SCIPvarGetNLocksUpType(var, SCIP_LOCKTYPE_MODEL) == ownerdata->nlockspos
5567 && ownerdata->nconss == 1 && SCIPisZero(scip, SCIPvarGetObj(var))
5568 && !SCIPisInfinity(scip, -SCIPvarGetLbGlobal(var)) && !SCIPisInfinity(scip, SCIPvarGetUbGlobal(var))
5569 && SCIPvarGetType(var) != SCIP_VARTYPE_BINARY
5570 && !SCIPisEQ(scip, SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var));
5571 }
5572
5573 /** removes all variable expressions that are contained in a given expression from a hash map */
5574 static
5575 SCIP_RETCODE removeSingleLockedVars(
5576 SCIP* scip, /**< SCIP data structure */
5577 SCIP_EXPR* expr, /**< expression */
5578 SCIP_EXPRITER* it, /**< expression iterator */
5579 SCIP_HASHMAP* exprcands /**< map to hash variable expressions */
5580 )
5581 {
5582 SCIP_EXPR* e;
5583
5584 for( e = SCIPexpriterRestartDFS(it, expr); !SCIPexpriterIsEnd(it); e = SCIPexpriterGetNext(it) )
5585 {
5586 if( SCIPisExprVar(scip, e) && SCIPhashmapExists(exprcands, (void*)e) )
5587 {
5588 SCIP_CALL( SCIPhashmapRemove(exprcands, (void*)e) );
5589 }
5590 }
5591
5592 return SCIP_OKAY;
5593 }
5594
5595 /** presolving method to fix a variable \f$x_i\f$ to one of its bounds if the variable is only contained in a single
5596 * nonlinear constraint g(x) ≤ rhs (≥ lhs) if g() is concave (convex) in \f$x_i\f$
5597 *
5598 * If a continuous variable has bounds [0,1], then the variable type is changed to be binary.
5599 * Otherwise, a bound disjunction constraint is added.
5600 *
5601 * @todo the same reduction can be applied if g(x) is not concave, but monotone in \f$x_i\f$ for g(x) ≤ rhs
5602 * @todo extend this to cases where a variable can appear in a monomial with an exponent, essentially relax
5603 * g(x) to \f$\sum_i [a_i,b_i] x^{p_i}\f$ for a single variable \f$x\f$ and try to conclude montonicity or convexity/concavity
5604 * on this (probably have one or two flags per variable and update this whenever another \f$x^{p_i}\f$ is found)
5605 */
5606 static
5607 SCIP_RETCODE presolveSingleLockedVars(
5608 SCIP* scip, /**< SCIP data structure */
5609 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */
5610 SCIP_CONS* cons, /**< nonlinear constraint */
5611 int* nchgvartypes, /**< pointer to store the total number of changed variable types */
5612 int* naddconss, /**< pointer to store the total number of added constraints */
5613 SCIP_Bool* infeasible /**< pointer to store whether problem is infeasible */
5614 )
5615 {
5616 SCIP_CONSHDLRDATA* conshdlrdata;
5617 SCIP_CONSDATA* consdata;
5618 SCIP_EXPR** singlelocked;
5619 SCIP_HASHMAP* exprcands;
5620 SCIP_Bool hasbounddisj;
5621 SCIP_Bool haslhs;
5622 SCIP_Bool hasrhs;
5623 int nsinglelocked = 0;
5624 int i;
5625
5626 assert(conshdlr != NULL);
5627 assert(cons != NULL);
5628 assert(nchgvartypes != NULL);
5629 assert(naddconss != NULL);
5630 assert(infeasible != NULL);
5631
5632 *nchgvartypes = 0;
5633 *naddconss = 0;
5634 *infeasible = FALSE;
5635
5636 conshdlrdata = SCIPconshdlrGetData(conshdlr);
5637 assert(conshdlrdata != NULL);
5638 consdata = SCIPconsGetData(cons);
5639 assert(consdata != NULL);
5640
5641 /* only consider constraints with one finite side */
5642 if( !SCIPisInfinity(scip, -consdata->lhs) && !SCIPisInfinity(scip, consdata->rhs) )
5643 return SCIP_OKAY;
5644
5645 /* only consider sum expressions */
5646 if( !SCIPisExprSum(scip, consdata->expr) )
5647 return SCIP_OKAY;
5648
5649 /* remember which side is finite */
5650 haslhs = !SCIPisInfinity(scip, -consdata->lhs);
5651 hasrhs = !SCIPisInfinity(scip, consdata->rhs);
5652
5653 /* allocate memory */
5654 SCIP_CALL( SCIPhashmapCreate(&exprcands, SCIPblkmem(scip), consdata->nvarexprs) );
5655 SCIP_CALL( SCIPallocBufferArray(scip, &singlelocked, consdata->nvarexprs) );
5656
5657 /* check all variable expressions for single locked variables */
5658 for( i = 0; i < consdata->nvarexprs; ++i )
5659 {
5660 assert(consdata->varexprs[i] != NULL);
5661
5662 if( isSingleLockedCand(scip, consdata->varexprs[i]) )
5663 {
5664 SCIP_CALL( SCIPhashmapInsert(exprcands, (void*)consdata->varexprs[i], NULL) );
5665 singlelocked[nsinglelocked++] = consdata->varexprs[i];
5666 }
5667 }
5668 SCIPdebugMsg(scip, "found %d single locked variables for constraint %s\n", nsinglelocked, SCIPconsGetName(cons));
5669
5670 if( nsinglelocked > 0 )
5671 {
5672 SCIP_EXPR** children;
5673 SCIP_EXPRITER* it;
5674 int nchildren;
5675
5676 children = SCIPexprGetChildren(consdata->expr);
5677 nchildren = SCIPexprGetNChildren(consdata->expr);
5678
5679 /* create iterator */
5680 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
5681 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) );
5682 SCIPexpriterSetStagesDFS(it, SCIP_EXPRITER_ENTEREXPR);
5683
5684 for( i = 0; i < nchildren; ++i )
5685 {
5686 SCIP_EXPR* child;
5687 SCIP_Real coef;
5688
5689 child = children[i];
5690 assert(child != NULL);
5691 coef = SCIPgetCoefsExprSum(consdata->expr)[i];
5692
5693 /* ignore linear terms */
5694 if( SCIPisExprVar(scip, child) )
5695 continue;
5696
5697 /* consider products prod_j f_j(x); ignore f_j(x) if it is a single variable, otherwise iterate through the
5698 * expression that represents f_j and remove each variable expression from exprcands
5699 */
5700 else if( SCIPisExprProduct(scip, child) )
5701 {
5702 int j;
5703
5704 for( j = 0; j < SCIPexprGetNChildren(child); ++j )
5705 {
5706 SCIP_EXPR* grandchild = SCIPexprGetChildren(child)[j];
5707
5708 if( !SCIPisExprVar(scip, grandchild) )
5709 {
5710 /* mark all variable expressions that are contained in the expression */
5711 SCIP_CALL( removeSingleLockedVars(scip, grandchild, it, exprcands) );
5712 }
5713 }
5714 }
5715 /* fixing a variable x to one of its bounds is only valid for ... +x^p >= lhs or ... -x^p <= rhs if p = 2k
5716 * for an integer k >= 1
5717 */
5718 else if( SCIPisExprPower(scip, child) )
5719 {
5720 SCIP_EXPR* grandchild = SCIPexprGetChildren(child)[0];
5721 SCIP_Real exponent = SCIPgetExponentExprPow(child);
5722 SCIP_Bool valid;
5723
5724 /* check for even integral exponent */
5725 valid = exponent > 1.0 && fmod(exponent, 2.0) == 0.0;
5726
5727 if( !valid || !SCIPisExprVar(scip, grandchild) || (hasrhs && coef > 0.0) || (haslhs && coef < 0.0) )
5728 {
5729 /* mark all variable expressions that are contained in the expression */
5730 SCIP_CALL( removeSingleLockedVars(scip, grandchild, it, exprcands) );
5731 }
5732 }
5733 /* all other cases cannot be handled */
5734 else
5735 {
5736 /* mark all variable expressions that are contained in the expression */
5737 SCIP_CALL( removeSingleLockedVars(scip, child, it, exprcands) );
5738 }
5739 }
5740
5741 /* free expression iterator */
5742 SCIPfreeExpriter(&it);
5743 }
5744
5745 /* check whether the bound disjunction constraint handler is available */
5746 hasbounddisj = SCIPfindConshdlr(scip, "bounddisjunction") != NULL;
5747
5748 /* fix variable to one of its bounds by either changing its variable type or adding a disjunction constraint */
5749 for( i = 0; i < nsinglelocked; ++i )
5750 {
5751 /* only consider expressions that are still contained in the exprcands map */
5752 if( SCIPhashmapExists(exprcands, (void*)singlelocked[i]) )
5753 {
5754 SCIP_CONS* newcons;
5755 SCIP_VAR* vars[2];
5756 SCIP_BOUNDTYPE boundtypes[2];
5757 SCIP_Real bounds[2];
5758 char name[SCIP_MAXSTRLEN];
5759 SCIP_VAR* var;
5760
5761 var = SCIPgetVarExprVar(singlelocked[i]);
5762 assert(var != NULL);
5763 SCIPdebugMsg(scip, "found single locked variable %s in [%g,%g] that can be fixed to one of its bounds\n",
5764 SCIPvarGetName(var), SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var));
5765
5766 /* try to change the variable type to binary */
5767 if( conshdlrdata->checkvarlocks == 't' && SCIPisEQ(scip, SCIPvarGetLbGlobal(var), 0.0) && SCIPisEQ(scip, SCIPvarGetUbGlobal(var), 1.0) )
5768 {
5769 assert(SCIPvarGetType(var) != SCIP_VARTYPE_BINARY);
5770 SCIP_CALL( SCIPchgVarType(scip, var, SCIP_VARTYPE_BINARY, infeasible) );
5771 ++(*nchgvartypes);
5772
5773 if( *infeasible )
5774 {
5775 SCIPdebugMsg(scip, "detect infeasibility after changing variable type of <%s>\n", SCIPvarGetName(var));
5776 break;
5777 }
5778 }
5779 /* add bound disjunction constraint if bounds of the variable are finite */
5780 else if( hasbounddisj && !SCIPisInfinity(scip, -SCIPvarGetLbGlobal(var)) && !SCIPisInfinity(scip, SCIPvarGetUbGlobal(var)) )
5781 {
5782 vars[0] = var;
5783 vars[1] = var;
5784 boundtypes[0] = SCIP_BOUNDTYPE_LOWER;
5785 boundtypes[1] = SCIP_BOUNDTYPE_UPPER;
5786 bounds[0] = SCIPvarGetUbGlobal(var);
5787 bounds[1] = SCIPvarGetLbGlobal(var);
5788
5789 SCIPdebugMsg(scip, "add bound disjunction constraint for %s\n", SCIPvarGetName(var));
5790
5791 /* create, add, and release bound disjunction constraint */
5792 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "quadvarbnddisj_%s", SCIPvarGetName(var));
5793 SCIP_CALL( SCIPcreateConsBounddisjunction(scip, &newcons, name, 2, vars, boundtypes, bounds, TRUE, TRUE,
5794 TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
5795 SCIP_CALL( SCIPaddCons(scip, newcons) );
5796 SCIP_CALL( SCIPreleaseCons(scip, &newcons) );
5797 ++(*naddconss);
5798 }
5799 }
5800 }
5801
5802 /* free memory */
5803 SCIPfreeBufferArray(scip, &singlelocked);
5804 SCIPhashmapFree(&exprcands);
5805
5806 return SCIP_OKAY;
5807 }
5808
5809 /** presolving method to check if there is a single linear continuous variable that can be made implicit integer */
5810 static
5811 SCIP_RETCODE presolveImplint(
5812 SCIP* scip, /**< SCIP data structure */
5813 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
5814 SCIP_CONS** conss, /**< nonlinear constraints */
5815 int nconss, /**< total number of nonlinear constraints */
5816 int* nchgvartypes, /**< pointer to update the total number of changed variable types */
5817 SCIP_Bool* infeasible /**< pointer to store whether problem is infeasible */
5818 )
5819 {
5820 int c;
5821
5822 assert(scip != NULL);
5823 assert(conshdlr != NULL);
5824 assert(conss != NULL || nconss == 0);
5825 assert(nchgvartypes != NULL);
5826 assert(infeasible != NULL);
5827
5828 *infeasible = FALSE;
5829
5830 /* nothing can be done if there are no binary and integer variables available */
5831 if( SCIPgetNBinVars(scip) == 0 && SCIPgetNIntVars(scip) == 0 )
5832 return SCIP_OKAY;
5833
5834 /* no continuous var can be made implicit-integer if there are no continuous variables */
5835 if( SCIPgetNContVars(scip) == 0 )
5836 return SCIP_OKAY;
5837
5838 for( c = 0; c < nconss; ++c )
5839 {
5840 SCIP_CONSDATA* consdata;
5841 SCIP_EXPR** children;
5842 int nchildren;
5843 SCIP_Real* coefs;
5844 SCIP_EXPR* cand = NULL;
5845 SCIP_Real candcoef = 0.0;
5846 int i;
5847
5848 assert(conss != NULL && conss[c] != NULL);
5849
5850 consdata = SCIPconsGetData(conss[c]);
5851 assert(consdata != NULL);
5852
5853 /* the constraint must be an equality constraint */
5854 if( !SCIPisEQ(scip, consdata->lhs, consdata->rhs) )
5855 continue;
5856
5857 /* the root expression needs to be a sum expression */
5858 if( !SCIPisExprSum(scip, consdata->expr) )
5859 continue;
5860
5861 children = SCIPexprGetChildren(consdata->expr);
5862 nchildren = SCIPexprGetNChildren(consdata->expr);
5863
5864 /* the sum expression must have at least two children
5865 * (with one child, we would look for a coef*x = constant, which is presolved away anyway)
5866 */
5867 if( nchildren <= 1 )
5868 continue;
5869
5870 coefs = SCIPgetCoefsExprSum(consdata->expr);
5871
5872 /* find first continuous variable and get value of its coefficient */
5873 for( i = 0; i < nchildren; ++i )
5874 {
5875 if( !SCIPisExprVar(scip, children[i]) || SCIPvarIsIntegral(SCIPgetVarExprVar(children[i])) )
5876 continue;
5877
5878 candcoef = coefs[i];
5879 assert(candcoef != 0.0);
5880
5881 /* lhs/rhs - constant divided by candcoef must be integral
5882 * if not, break with cand == NULL, so give up
5883 */
5884 if( SCIPisIntegral(scip, (consdata->lhs - SCIPgetConstantExprSum(consdata->expr)) / candcoef) )
5885 cand = children[i];
5886
5887 break;
5888 }
5889
5890 /* no suitable continuous variable found */
5891 if( cand == NULL )
5892 continue;
5893
5894 /* check whether all other coefficients are integral when diving by candcoef and all other children are integral */
5895 for( i = 0; i < nchildren; ++i )
5896 {
5897 if( children[i] == cand )
5898 continue;
5899
5900 /* child i must be integral */
5901 if( !SCIPexprIsIntegral(children[i]) )
5902 {
5903 cand = NULL;
5904 break;
5905 }
5906
5907 /* coefficient of child i must be integral if diving by candcoef */
5908 if( !SCIPisIntegral(scip, coefs[i] / candcoef) ) /*lint !e414*/
5909 {
5910 cand = NULL;
5911 break;
5912 }
5913 }
5914
5915 if( cand == NULL )
5916 continue;
5917
5918 SCIPdebugMsg(scip, "make variable <%s> implicit integer due to constraint <%s>\n",
5919 SCIPvarGetName(SCIPgetVarExprVar(cand)), SCIPconsGetName(conss[c]));
5920
5921 /* change variable type */
5922 SCIP_CALL( SCIPchgVarType(scip, SCIPgetVarExprVar(cand), SCIP_VARTYPE_IMPLINT, infeasible) );
5923
5924 if( *infeasible )
5925 return SCIP_OKAY;
5926
5927 /* mark expression as being integral (as would be done by expr_var.c in the next round of updating integrality info) */
5928 SCIPexprSetIntegrality(cand, TRUE);
5929 }
5930
5931 return SCIP_OKAY;
5932 }
5933
5934 /** creates auxiliary variable for a given expression
5935 *
5936 * @note for a variable expression it does nothing
5937 * @note this function can only be called in stage SCIP_STAGE_SOLVING
5938 */
5939 static
5940 SCIP_RETCODE createAuxVar(
5941 SCIP* scip, /**< SCIP data structure */
5942 SCIP_EXPR* expr /**< expression */
5943 )
5944 {
5945 SCIP_EXPR_OWNERDATA* ownerdata;
5946 SCIP_CONSHDLRDATA* conshdlrdata;
5947 SCIP_VARTYPE vartype;
5948 SCIP_INTERVAL activity;
5949 char name[SCIP_MAXSTRLEN];
5950
5951 assert(scip != NULL);
5952 assert(expr != NULL);
5953
5954 ownerdata = SCIPexprGetOwnerData(expr);
5955 assert(ownerdata != NULL);
5956 assert(ownerdata->nauxvaruses > 0);
5957
5958 /* if we already have auxvar, then do nothing */
5959 if( ownerdata->auxvar != NULL )
5960 return SCIP_OKAY;
5961
5962 /* if expression is a variable-expression, then do nothing */
5963 if( SCIPisExprVar(scip, expr) )
5964 return SCIP_OKAY;
5965
5966 if( SCIPgetStage(scip) != SCIP_STAGE_SOLVING )
5967 {
5968 SCIPerrorMessage("it is not possible to create auxiliary variables during stage=%d\n", SCIPgetStage(scip));
5969 return SCIP_INVALIDCALL;
5970 }
5971
5972 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr);
5973 assert(conshdlrdata != NULL);
5974 assert(conshdlrdata->auxvarid >= 0);
5975
5976 /* it doesn't harm much to have an auxvar for a constant, as this can be handled well by the default hdlr,
5977 * but it usually indicates a missing simplify
5978 * if we find situations where we need to have an auxvar for a constant, then remove this assert
5979 */
5980 assert(!SCIPisExprValue(scip, expr));
5981
5982 /* create and capture auxiliary variable */
5983 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "auxvar_%s_%d", SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)), conshdlrdata->auxvarid);
5984 ++conshdlrdata->auxvarid;
5985
5986 /* type of auxiliary variable depends on integrality information of the expression */
5987 vartype = SCIPexprIsIntegral(expr) ? SCIP_VARTYPE_IMPLINT : SCIP_VARTYPE_CONTINUOUS;
5988
5989 /* get activity of expression to initialize variable bounds, if something valid is available (evalActivity was called in initSepa) */
5990 if( SCIPexprGetActivityTag(expr) >= conshdlrdata->lastboundrelax )
5991 {
5992 activity = SCIPexprGetActivity(expr);
5993 /* we cannot handle a domain error here at the moment, but it seems unlikely that it could occur
5994 * if it appear, then we could change code to handle this properly, but for now we just ensure that we continue correctly
5995 * and abort in debug mode only
5996 */
5997 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, activity) )
5998 {
5999 SCIPABORT();
6000 SCIPintervalSetEntire(SCIP_INTERVAL_INFINITY, &activity);
6001 }
6002 }
6003 else
6004 SCIPintervalSetEntire(SCIP_INTERVAL_INFINITY, &activity);
6005
6006 /* if root node, then activity is globally valid, so use it to initialize the global bounds of the auxvar
6007 * otherwise, we create var without bounds here and use activity to set local bounds below (needs to be after adding var)
6008 */
6009 if( SCIPgetDepth(scip) == 0 )
6010 {
6011 SCIP_CALL( SCIPcreateVarBasic(scip, &ownerdata->auxvar, name, MAX(-SCIPinfinity(scip), activity.inf), MIN(SCIPinfinity(scip), activity.sup), 0.0, vartype) );
6012 }
6013 else
6014 {
6015 SCIP_CALL( SCIPcreateVarBasic(scip, &ownerdata->auxvar, name, -SCIPinfinity(scip), SCIPinfinity(scip), 0.0, vartype) );
6016 }
6017
6018 /* mark the auxiliary variable to be added for the relaxation only
6019 * this prevents SCIP to create linear constraints from cuts or conflicts that contain auxiliary variables,
6020 * or to copy the variable to a subscip
6021 */
6022 SCIPvarMarkRelaxationOnly(ownerdata->auxvar);
6023
6024 SCIP_CALL( SCIPaddVar(scip, ownerdata->auxvar) );
6025
6026 SCIPdebugMsg(scip, "added auxiliary variable <%s> [%g,%g] for expression %p\n", SCIPvarGetName(ownerdata->auxvar), SCIPvarGetLbGlobal(ownerdata->auxvar), SCIPvarGetUbGlobal(ownerdata->auxvar), (void*)expr);
6027
6028 /* add variable locks in both directions
6029 * TODO should be sufficient to lock only according to expr->nlockspos/neg,
6030 * but then we need to also update the auxvars locks when the expr locks change
6031 */
6032 SCIP_CALL( SCIPaddVarLocks(scip, ownerdata->auxvar, 1, 1) );
6033
6034 #ifdef WITH_DEBUG_SOLUTION
6035 if( SCIPdebugIsMainscip(scip) )
6036 {
6037 /* store debug solution value of auxiliary variable
6038 * assumes that expression has been evaluated in debug solution before
6039 */
6040 SCIP_CALL( SCIPdebugAddSolVal(scip, ownerdata->auxvar, SCIPexprGetEvalValue(expr)) );
6041 }
6042 #endif
6043
6044 if( SCIPgetDepth(scip) > 0 )
6045 {
6046 /* initialize local bounds to (locally valid) activity */
6047 SCIP_Bool cutoff;
6048 SCIP_CALL( tightenAuxVarBounds(scip, ownerdata->conshdlr, expr, activity, &cutoff, NULL) );
6049 assert(!cutoff); /* should not happen as activity wasn't empty and variable is new */
6050 }
6051
6052 return SCIP_OKAY;
6053 }
6054
6055 /** initializes separation for constraint
6056 *
6057 * - ensures that activities are up to date in all expressions
6058 * - creates auxiliary variables where required
6059 * - calls propExprDomains() to possibly tighten auxvar bounds
6060 * - calls separation initialization callback of nlhdlrs
6061 */
6062 static
6063 SCIP_RETCODE initSepa(
6064 SCIP* scip, /**< SCIP data structure */
6065 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraints handler */
6066 SCIP_CONS** conss, /**< constraints */
6067 int nconss, /**< number of constraints */
6068 SCIP_Bool* infeasible /**< pointer to store whether the problem is infeasible or not */
6069 )
6070 {
6071 SCIP_CONSDATA* consdata;
6072 SCIP_CONSHDLRDATA* conshdlrdata;
6073 SCIP_EXPRITER* it;
6074 SCIP_EXPR* expr;
6075 SCIP_RESULT result;
6076 SCIP_VAR* auxvar;
6077 int nreductions = 0;
6078 int c, e;
6079
6080 assert(scip != NULL);
6081 assert(conshdlr != NULL);
6082 assert(conss != NULL || nconss == 0);
6083 assert(nconss >= 0);
6084 assert(infeasible != NULL);
6085
6086 conshdlrdata = SCIPconshdlrGetData(conshdlr);
6087 assert(conshdlrdata != NULL);
6088
6089 /* start with new propbounds (just to be sure, should not be needed) */
6090 ++conshdlrdata->curpropboundstag;
6091
6092 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
6093 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) );
6094
6095 /* first ensure activities are up to date and create auxvars */
6096 *infeasible = FALSE;
6097 for( c = 0; c < nconss; ++c )
6098 {
6099 assert(conss != NULL);
6100 assert(conss[c] != NULL);
6101
6102 consdata = SCIPconsGetData(conss[c]);
6103 assert(consdata != NULL);
6104 assert(consdata->expr != NULL);
6105
6106 #ifdef WITH_DEBUG_SOLUTION
6107 if( SCIPdebugIsMainscip(scip) )
6108 {
6109 SCIP_SOL* debugsol;
6110
6111 SCIP_CALL( SCIPdebugGetSol(scip, &debugsol) );
6112
6113 if( debugsol != NULL ) /* it can be compiled WITH_DEBUG_SOLUTION, but still no solution given */
6114 {
6115 /* evaluate expression in debug solution, so we can set the solution value of created auxiliary variables
6116 * in createAuxVar()
6117 */
6118 SCIP_CALL( SCIPevalExpr(scip, consdata->expr, debugsol, 0) );
6119 }
6120 }
6121 #endif
6122
6123 /* ensure we have a valid activity for auxvars and propExprDomains() call below */
6124 SCIP_CALL( SCIPevalExprActivity(scip, consdata->expr) );
6125
6126 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
6127 {
6128 if( SCIPexprGetOwnerData(expr)->nauxvaruses > 0 )
6129 {
6130 SCIP_CALL( createAuxVar(scip, expr) );
6131 }
6132 }
6133
6134 auxvar = SCIPexprGetOwnerData(consdata->expr)->auxvar;
6135 if( auxvar != NULL )
6136 {
6137 SCIPdebugMsg(scip, "tighten auxvar <%s> bounds using constraint sides [%g,%g]\n",
6138 SCIPvarGetName(auxvar), consdata->lhs, consdata->rhs);
6139 /* change the bounds of the auxiliary variable of the root node to [lhs,rhs] */
6140 SCIP_CALL( SCIPtightenVarLb(scip, auxvar, consdata->lhs, TRUE, infeasible, NULL) );
6141 if( *infeasible )
6142 {
6143 SCIPdebugMsg(scip, "infeasibility detected while tightening auxvar lb (%g) using lhs of constraint (%g)\n", SCIPvarGetLbLocal(auxvar), consdata->lhs);
6144 break;
6145 }
6146
6147 SCIP_CALL( SCIPtightenVarUb(scip, auxvar, consdata->rhs, TRUE, infeasible, NULL) );
6148 if( *infeasible )
6149 {
6150 SCIPdebugMsg(scip, "infeasibility detected while tightening auxvar ub (%g) using rhs of constraint (%g)\n", SCIPvarGetUbLocal(auxvar), consdata->rhs);
6151 break;
6152 }
6153 }
6154 }
6155
6156 /* now run a special version of reverseprop to ensure that important bound information (like function domains) is stored in bounds of auxvars,
6157 * since sometimes they cannot be recovered from activity evaluation even after some rounds of domain propagation
6158 * (e.g., log(x*y), which becomes log(w), w=x*y
6159 * log(w) implies w >= 0, but we may not be able to derive bounds on x and y such that w >= 0 is ensured)
6160 */
6161 SCIP_CALL( propExprDomains(scip, conshdlr, conss, nconss, &result, &nreductions) );
6162 if( result == SCIP_CUTOFF )
6163 *infeasible = TRUE;
6164
6165 /* now call initsepa of nlhdlrs
6166 * TODO skip if !SCIPconsIsInitial(conss[c]) ?
6167 * but at the moment, initSepa() is called from INITLP anyway, so we have SCIPconsIsInitial(conss[c]) anyway
6168 */
6169 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) );
6170 for( c = 0; c < nconss && !*infeasible; ++c )
6171 {
6172 assert(conss != NULL);
6173 assert(conss[c] != NULL);
6174
6175 consdata = SCIPconsGetData(conss[c]);
6176 assert(consdata != NULL);
6177 assert(consdata->expr != NULL);
6178
6179 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it) && !*infeasible; expr = SCIPexpriterGetNext(it) )
6180 {
6181 SCIP_EXPR_OWNERDATA* ownerdata;
6182
6183 ownerdata = SCIPexprGetOwnerData(expr);
6184 assert(ownerdata != NULL);
6185
6186 if( ownerdata->nauxvaruses == 0 )
6187 continue;
6188
6189 for( e = 0; e < ownerdata->nenfos; ++e )
6190 {
6191 SCIP_NLHDLR* nlhdlr;
6192 SCIP_Bool underestimate;
6193 SCIP_Bool overestimate;
6194 assert(ownerdata->enfos[e] != NULL);
6195
6196 /* skip if initsepa was already called, e.g., because this expression is also part of a constraint
6197 * which participated in a previous initSepa() call
6198 */
6199 if( ownerdata->enfos[e]->issepainit )
6200 continue;
6201
6202 /* only call initsepa if it will actually separate */
6203 if( (ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPABOTH) == 0 )
6204 continue;
6205
6206 nlhdlr = ownerdata->enfos[e]->nlhdlr;
6207 assert(nlhdlr != NULL);
6208
6209 /* only init sepa if there is an initsepa callback */
6210 if( !SCIPnlhdlrHasInitSepa(nlhdlr) )
6211 continue;
6212
6213 /* check whether expression needs to be under- or overestimated */
6214 overestimate = ownerdata->nlocksneg > 0;
6215 underestimate = ownerdata->nlockspos > 0;
6216 assert(underestimate || overestimate);
6217
6218 SCIPdebugMsg(scip, "initsepa under=%u over=%u for expression %p\n", underestimate, overestimate, (void*)expr);
6219
6220 /* call the separation initialization callback of the nonlinear handler */
6221 SCIP_CALL( SCIPnlhdlrInitsepa(scip, conshdlr, conss[c], nlhdlr, expr,
6222 ownerdata->enfos[e]->nlhdlrexprdata, overestimate, underestimate, infeasible) );
6223 ownerdata->enfos[e]->issepainit = TRUE;
6224
6225 if( *infeasible )
6226 {
6227 /* stop everything if we detected infeasibility */
6228 SCIPdebugMsg(scip, "detect infeasibility for constraint %s during initsepa()\n", SCIPconsGetName(conss[c]));
6229 break;
6230 }
6231 }
6232 }
6233 }
6234
6235 SCIPfreeExpriter(&it);
6236
6237 return SCIP_OKAY;
6238 }
6239
6240 /** returns whether we are ok to branch on auxiliary variables
6241 *
6242 * Currently returns whether depth of node in B&B tree is at least value of constraints/nonlinear/branching/aux parameter.
6243 */
6244 static
6245 SCIP_Bool branchAuxNonlinear(
6246 SCIP* scip, /**< SCIP data structure */
6247 SCIP_CONSHDLR* conshdlr /**< constraint handler */
6248 )
6249 {
6250 SCIP_CONSHDLRDATA* conshdlrdata;
6251
6252 assert(conshdlr != NULL);
6253
6254 conshdlrdata = SCIPconshdlrGetData(conshdlr);
6255 assert(conshdlrdata != NULL);
6256
6257 return conshdlrdata->branchauxmindepth <= SCIPgetDepth(scip);
6258 }
6259
6260 /** gets weight of variable when splitting violation score onto several variables in an expression */
6261 static
6262 SCIP_Real getViolSplitWeight(
6263 SCIP* scip, /**< SCIP data structure */
6264 SCIP_CONSHDLR* conshdlr, /**< expr constraint handler */
6265 SCIP_VAR* var, /**< variable */
6266 SCIP_SOL* sol /**< current solution */
6267 )
6268 {
6269 SCIP_CONSHDLRDATA* conshdlrdata;
6270
6271 conshdlrdata = SCIPconshdlrGetData(conshdlr);
6272 assert(conshdlrdata != NULL);
6273
6274 switch( conshdlrdata->branchviolsplit )
6275 {
6276 case 'u' : /* uniform: everyone gets the same score */
6277 return 1.0;
6278
6279 case 'm' : /* midness of solution: 0.5 if in middle of domain, 0.05 if close to lower or upper bound */
6280 {
6281 SCIP_Real weight;
6282 weight = MIN(SCIPgetSolVal(scip, sol, var) - SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var) - SCIPgetSolVal(scip, sol, var)) / (SCIPvarGetUbLocal(var) - SCIPvarGetLbLocal(var));
6283 return MAX(0.05, weight);
6284 }
6285
6286 case 'd' : /* domain width */
6287 return SCIPvarGetUbLocal(var) - SCIPvarGetLbLocal(var);
6288
6289 case 'l' : /* logarithmic domain width: log-scale if width is below 0.1 or above 10, otherwise actual width */
6290 {
6291 SCIP_Real width = SCIPvarGetUbLocal(var) - SCIPvarGetLbLocal(var);
6292 assert(width > 0.0);
6293 if( width > 10.0 )
6294 return 10.0*log10(width);
6295 if( width < 0.1 )
6296 return 0.1/(-log10(width));
6297 return width;
6298 }
6299
6300 default :
6301 SCIPerrorMessage("invalid value for parameter constraints/expr/branching/violsplit");
6302 SCIPABORT();
6303 return SCIP_INVALID;
6304 }
6305 }
6306
6307 /** adds violation-branching score to a set of expressions, thereby distributing the score
6308 *
6309 * Each expression must either be a variable expression or have an aux-variable.
6310 *
6311 * If unbounded variables are present, each unbounded var gets an even score.
6312 * If no unbounded variables, then parameter constraints/nonlinear/branching/violsplit decides weight for each var.
6313 */
6314 static
6315 void addExprsViolScore(
6316 SCIP* scip, /**< SCIP data structure */
6317 SCIP_EXPR** exprs, /**< expressions where to add branching score */
6318 int nexprs, /**< number of expressions */
6319 SCIP_Real violscore, /**< violation-branching score to add to expression */
6320 SCIP_SOL* sol, /**< current solution */
6321 SCIP_Bool* success /**< buffer to store whether at least one violscore was added */
6322 )
6323 {
6324 SCIP_CONSHDLR* conshdlr;
6325 SCIP_VAR* var;
6326 SCIP_Real weight;
6327 SCIP_Real weightsum = 0.0; /* sum of weights over all candidates with bounded domain */
6328 int nunbounded = 0; /* number of candidates with unbounded domain */
6329 int i;
6330
6331 assert(exprs != NULL);
6332 assert(nexprs > 0);
6333 assert(success != NULL);
6334
6335 if( nexprs == 1 )
6336 {
6337 SCIPaddExprViolScoreNonlinear(scip, exprs[0], violscore);
6338 SCIPdebug( var = SCIPgetExprAuxVarNonlinear(exprs[0]); )
6339 SCIPdebugMsg(scip, "add score %g to <%s>[%g,%g]\n", violscore,
6340 SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var));
6341 *success = TRUE;
6342 return;
6343 }
6344
6345 conshdlr = SCIPexprGetOwnerData(exprs[0])->conshdlr;
6346
6347 for( i = 0; i < nexprs; ++i )
6348 {
6349 var = SCIPgetExprAuxVarNonlinear(exprs[i]);
6350 assert(var != NULL);
6351
6352 if( SCIPisInfinity(scip, -SCIPvarGetLbLocal(var)) || SCIPisInfinity(scip, SCIPvarGetUbLocal(var)) )
6353 ++nunbounded;
6354 else if( !SCIPisEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) )
6355 weightsum += getViolSplitWeight(scip, conshdlr, var, sol);
6356 }
6357
6358 *success = FALSE;
6359 for( i = 0; i < nexprs; ++i )
6360 {
6361 var = SCIPgetExprAuxVarNonlinear(exprs[i]);
6362 assert(var != NULL);
6363
6364 if( nunbounded > 0 )
6365 {
6366 if( SCIPisInfinity(scip, -SCIPvarGetLbLocal(var)) || SCIPisInfinity(scip, SCIPvarGetUbLocal(var)) )
6367 {
6368 SCIPaddExprViolScoreNonlinear(scip, exprs[i], violscore / nunbounded);
6369 SCIPdebugMsg(scip, "add score %g (%g%% of %g) to <%s>[%g,%g]\n", violscore / nunbounded,
6370 100.0/nunbounded, violscore,
6371 SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var));
6372 *success = TRUE;
6373 }
6374 }
6375 else if( !SCIPisEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) )
6376 {
6377 assert(weightsum > 0.0);
6378
6379 weight = getViolSplitWeight(scip, conshdlr, var, sol);
6380 SCIPaddExprViolScoreNonlinear(scip, exprs[i], violscore * weight / weightsum);
6381 SCIPdebugMsg(scip, "add score %g (%g%% of %g) to <%s>[%g,%g]\n", violscore * weight / weightsum,
6382 100*weight / weightsum, violscore,
6383 SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var));
6384 *success = TRUE;
6385 }
6386 else
6387 {
6388 SCIPdebugMsg(scip, "skip score for fixed variable <%s>[%g,%g]\n",
6389 SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var));
6390 }
6391 }
6392 }
6393
6394 /** adds violation-branching score to children of expression for given auxiliary variables
6395 *
6396 * Iterates over the successors of `expr` to find expressions that are associated with one of the given auxiliary variables.
6397 * Adds violation-branching scores to all found exprs by means of SCIPaddExprsViolScoreNonlinear().
6398 *
6399 * @note This method may modify the given auxvars array by means of sorting.
6400 */
6401 static
6402 SCIP_RETCODE addExprViolScoresAuxVars(
6403 SCIP* scip, /**< SCIP data structure */
6404 SCIP_EXPR* expr, /**< expression where to start searching */
6405 SCIP_Real violscore, /**< violation score to add to expression */
6406 SCIP_VAR** auxvars, /**< auxiliary variables for which to find expression */
6407 int nauxvars, /**< number of auxiliary variables */
6408 SCIP_SOL* sol, /**< current solution (NULL for the LP solution) */
6409 SCIP_Bool* success /**< buffer to store whether at least one violscore was added */
6410 )
6411 {
6412 SCIP_EXPRITER* it;
6413 SCIP_VAR* auxvar;
6414 SCIP_EXPR** exprs;
6415 int nexprs;
6416 int pos;
6417
6418 assert(scip != NULL);
6419 assert(expr != NULL);
6420 assert(auxvars != NULL);
6421 assert(success != NULL);
6422
6423 /* sort variables to make lookup below faster */
6424 SCIPsortPtr((void**)auxvars, SCIPvarComp, nauxvars);
6425
6426 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
6427 SCIP_CALL( SCIPexpriterInit(it, expr, SCIP_EXPRITER_BFS, FALSE) );
6428
6429 SCIP_CALL( SCIPallocBufferArray(scip, &exprs, nauxvars) );
6430 nexprs = 0;
6431
6432 for( expr = SCIPexpriterGetNext(it); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
6433 {
6434 auxvar = SCIPgetExprAuxVarNonlinear(expr);
6435 if( auxvar == NULL )
6436 continue;
6437
6438 /* if auxvar of expr is contained in auxvars array, add branching score to expr */
6439 if( SCIPsortedvecFindPtr((void**)auxvars, SCIPvarComp, auxvar, nauxvars, &pos) )
6440 {
6441 assert(auxvars[pos] == auxvar);
6442
6443 SCIPdebugMsg(scip, "adding branchingscore for expr %p with auxvar <%s>\n", (void*)expr, SCIPvarGetName(auxvar));
6444 exprs[nexprs++] = expr;
6445
6446 if( nexprs == nauxvars )
6447 break;
6448 }
6449 }
6450
6451 SCIPfreeExpriter(&it);
6452
6453 if( nexprs > 0 )
6454 {
6455 SCIP_CALL( SCIPaddExprsViolScoreNonlinear(scip, exprs, nexprs, violscore, sol, success) );
6456 }
6457 else
6458 *success = FALSE;
6459
6460 SCIPfreeBufferArray(scip, &exprs);
6461
6462 return SCIP_OKAY;
6463 }
6464
6465 /** registers all unfixed variables in violated constraints as branching candidates */
6466 static
6467 SCIP_RETCODE registerBranchingCandidatesAllUnfixed(
6468 SCIP* scip, /**< SCIP data structure */
6469 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraints handler */
6470 SCIP_CONS** conss, /**< constraints */
6471 int nconss, /**< number of constraints */
6472 int* nnotify /**< counter for number of notifications performed */
6473 )
6474 {
6475 SCIP_CONSDATA* consdata;
6476 SCIP_VAR* var;
6477 int c;
6478 int i;
6479
6480 assert(conshdlr != NULL);
6481 assert(conss != NULL || nconss == 0);
6482 assert(nnotify != NULL);
6483
6484 *nnotify = 0;
6485
6486 for( c = 0; c < nconss; ++c )
6487 {
6488 assert(conss != NULL && conss[c] != NULL);
6489
6490 consdata = SCIPconsGetData(conss[c]);
6491 assert(consdata != NULL);
6492
6493 /* consider only violated constraints */
6494 if( !isConsViolated(scip, conss[c]) )
6495 continue;
6496
6497 /* register all variables that have not been fixed yet */
6498 assert(consdata->varexprs != NULL);
6499 for( i = 0; i < consdata->nvarexprs; ++i )
6500 {
6501 var = SCIPgetVarExprVar(consdata->varexprs[i]);
6502 assert(var != NULL);
6503
6504 if( !SCIPisEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) )
6505 {
6506 SCIP_CALL( SCIPaddExternBranchCand(scip, var, getConsAbsViolation(conss[c]), SCIP_INVALID) );
6507 ++(*nnotify);
6508 }
6509 }
6510 }
6511
6512 return SCIP_OKAY;
6513 }
6514
6515 /** registers all variables in violated constraints with branching scores as external branching candidates */
6516 static
6517 SCIP_RETCODE registerBranchingCandidates(
6518 SCIP* scip, /**< SCIP data structure */
6519 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraints handler */
6520 SCIP_CONS** conss, /**< constraints */
6521 int nconss, /**< number of constraints */
6522 SCIP_Bool* success /**< buffer to store whether at least one branching candidate was added */
6523 )
6524 {
6525 SCIP_CONSDATA* consdata;
6526 SCIP_EXPRITER* it = NULL;
6527 int c;
6528
6529 assert(conshdlr != NULL);
6530 assert(success != NULL);
6531
6532 *success = FALSE;
6533
6534 if( branchAuxNonlinear(scip, conshdlr) )
6535 {
6536 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
6537 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) );
6538 }
6539
6540 /* register external branching candidates */
6541 for( c = 0; c < nconss; ++c )
6542 {
6543 assert(conss != NULL && conss[c] != NULL);
6544
6545 consdata = SCIPconsGetData(conss[c]);
6546 assert(consdata != NULL);
6547 assert(consdata->varexprs != NULL);
6548
6549 /* consider only violated constraints */
6550 if( !isConsViolated(scip, conss[c]) )
6551 continue;
6552
6553 if( !branchAuxNonlinear(scip, conshdlr) )
6554 {
6555 int i;
6556
6557 /* if not branching on auxvars, then violation-branching scores will have been added to original variables
6558 * only, so we can loop over variable expressions
6559 */
6560 for( i = 0; i < consdata->nvarexprs; ++i )
6561 {
6562 SCIP_Real violscore;
6563 SCIP_Real lb;
6564 SCIP_Real ub;
6565 SCIP_VAR* var;
6566
6567 violscore = SCIPgetExprViolScoreNonlinear(consdata->varexprs[i]);
6568
6569 /* skip variable expressions that do not have a violation score */
6570 if( violscore == 0.0 )
6571 continue;
6572
6573 var = SCIPgetVarExprVar(consdata->varexprs[i]);
6574 assert(var != NULL);
6575
6576 lb = SCIPvarGetLbLocal(var);
6577 ub = SCIPvarGetUbLocal(var);
6578
6579 /* consider variable for branching if it has not been fixed yet */
6580 if( !SCIPisEQ(scip, lb, ub) )
6581 {
6582 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " add variable <%s>[%g,%g] as extern branching candidate with score %g\n", SCIPvarGetName(var), lb, ub, violscore); )
6583 SCIP_CALL( SCIPaddExternBranchCand(scip, var, violscore, SCIP_INVALID) );
6584 *success = TRUE;
6585 }
6586 else
6587 {
6588 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " skip fixed variable <%s>[%.15g,%.15g]\n", SCIPvarGetName(var), lb, ub); )
6589 }
6590
6591 /* invalidate violscore-tag, so that we do not register variables that appear in multiple constraints
6592 * several times as external branching candidate, see SCIPgetExprViolScoreNonlinear()
6593 */
6594 SCIPexprGetOwnerData(consdata->varexprs[i])->violscoretag = 0;
6595 }
6596 }
6597 else
6598 {
6599 SCIP_EXPR* expr;
6600 SCIP_VAR* var;
6601 SCIP_Real lb;
6602 SCIP_Real ub;
6603 SCIP_Real violscore;
6604
6605 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
6606 {
6607 violscore = SCIPgetExprViolScoreNonlinear(expr);
6608 if( violscore == 0.0 )
6609 continue;
6610
6611 /* if some nlhdlr added a branching score for this expression, then it considered this expression as a
6612 * variable, so this expression should either be an original variable or have an auxiliary variable
6613 */
6614 var = SCIPgetExprAuxVarNonlinear(expr);
6615 assert(var != NULL);
6616
6617 lb = SCIPvarGetLbLocal(var);
6618 ub = SCIPvarGetUbLocal(var);
6619
6620 /* consider variable for branching if it has not been fixed yet */
6621 if( !SCIPisEQ(scip, lb, ub) )
6622 {
6623 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " add variable <%s>[%g,%g] as extern branching candidate with score %g\n", SCIPvarGetName(var), lb, ub, violscore); )
6624
6625 SCIP_CALL( SCIPaddExternBranchCand(scip, var, violscore, SCIP_INVALID) );
6626 *success = TRUE;
6627 }
6628 else
6629 {
6630 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " skip fixed variable <%s>[%.15g,%.15g]\n", SCIPvarGetName(var), lb, ub); )
6631 }
6632 }
6633 }
6634 }
6635
6636 if( it != NULL )
6637 SCIPfreeExpriter(&it);
6638
6639 return SCIP_OKAY;
6640 }
6641
6642 /** collect branching candidates from violated constraints
6643 *
6644 * Fills array with expressions that serve as branching candidates.
6645 * Collects those expressions that have a branching score assigned and stores the score in the auxviol field of the
6646 * branching candidate.
6647 *
6648 * If branching on aux-variables is allowed, then iterate through expressions of violated constraints, otherwise iterate
6649 * through variable-expressions only.
6650 */
6651 static
6652 SCIP_RETCODE collectBranchingCandidates(
6653 SCIP* scip, /**< SCIP data structure */
6654 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
6655 SCIP_CONS** conss, /**< constraints to process */
6656 int nconss, /**< number of constraints */
6657 SCIP_Real maxrelconsviol, /**< maximal scaled constraint violation */
6658 SCIP_SOL* sol, /**< solution to enforce (NULL for the LP solution) */
6659 SCIP_Longint soltag, /**< tag of solution */
6660 BRANCHCAND* cands, /**< array where to store candidates, must be at least SCIPgetNVars() long */
6661 int* ncands /**< number of candidates found */
6662 )
6663 {
6664 SCIP_CONSHDLRDATA* conshdlrdata;
6665 SCIP_CONSDATA* consdata;
6666 SCIP_EXPRITER* it = NULL;
6667 int c;
6668 int attempt;
6669 SCIP_VAR* var;
6670
6671 assert(scip != NULL);
6672 assert(conshdlr != NULL);
6673 assert(cands != NULL);
6674 assert(ncands != NULL);
6675
6676 conshdlrdata = SCIPconshdlrGetData(conshdlr);
6677 assert(conshdlrdata != NULL);
6678
6679 if( branchAuxNonlinear(scip, conshdlr) )
6680 {
6681 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
6682 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) );
6683 }
6684
6685 *ncands = 0;
6686 for( attempt = 0; attempt < 2; ++attempt )
6687 {
6688 /* collect branching candidates from violated constraints
6689 * in the first attempt, consider only constraints with large violation
6690 * in the second attempt, consider all remaining violated constraints
6691 */
6692 for( c = 0; c < nconss; ++c )
6693 {
6694 SCIP_Real consviol;
6695
6696 assert(conss != NULL && conss[c] != NULL);
6697
6698 /* consider only violated constraints */
6699 if( !isConsViolated(scip, conss[c]) )
6700 continue;
6701
6702 consdata = SCIPconsGetData(conss[c]);
6703 assert(consdata != NULL);
6704 assert(consdata->varexprs != NULL);
6705
6706 SCIP_CALL( getConsRelViolation(scip, conss[c], &consviol, sol, soltag) );
6707
6708 if( attempt == 0 && consviol < conshdlrdata->branchhighviolfactor * maxrelconsviol )
6709 continue;
6710 else if( attempt == 1 && consviol >= conshdlrdata->branchhighviolfactor * maxrelconsviol )
6711 continue;
6712
6713 if( !branchAuxNonlinear(scip, conshdlr) )
6714 {
6715 int i;
6716
6717 /* if not branching on auxvars, then violation-branching scores will be available for original variables
6718 * only, so we can loop over variable expressions
6719 * unfortunately, we don't know anymore which constraint contributed the violation-branching score to the
6720 * variable, therefore we invalidate the score of a variable after processing it.
6721 */
6722 for( i = 0; i < consdata->nvarexprs; ++i )
6723 {
6724 SCIP_Real lb;
6725 SCIP_Real ub;
6726
6727 /* skip variable expressions that do not have a valid violation score */
6728 if( conshdlrdata->enforound != SCIPexprGetOwnerData(consdata->varexprs[i])->violscoretag )
6729 continue;
6730
6731 var = SCIPgetVarExprVar(consdata->varexprs[i]);
6732 assert(var != NULL);
6733
6734 lb = SCIPvarGetLbLocal(var);
6735 ub = SCIPvarGetUbLocal(var);
6736
6737 /* skip already fixed variable */
6738 if( SCIPisEQ(scip, lb, ub) )
6739 {
6740 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " skip fixed variable <%s>[%.15g,%.15g]\n", SCIPvarGetName(var), lb, ub); )
6741 continue;
6742 }
6743
6744 assert(*ncands + 1 < SCIPgetNVars(scip));
6745 cands[*ncands].expr = consdata->varexprs[i];
6746 cands[*ncands].auxviol = SCIPgetExprViolScoreNonlinear(consdata->varexprs[i]);
6747 ++(*ncands);
6748
6749 /* invalidate violscore-tag, so that we do not register variables that appear in multiple constraints
6750 * several times as external branching candidate */
6751 SCIPexprGetOwnerData(consdata->varexprs[i])->violscoretag = 0;
6752 }
6753 }
6754 else
6755 {
6756 SCIP_EXPR* expr;
6757 SCIP_Real lb;
6758 SCIP_Real ub;
6759
6760 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
6761 {
6762 if( SCIPexprGetOwnerData(expr)->violscoretag != conshdlrdata->enforound )
6763 continue;
6764
6765 /* if some nlhdlr added a branching score for this expression, then it considered this expression as
6766 * variables, so this expression should either be an original variable or have an auxiliary variable
6767 */
6768 var = SCIPgetExprAuxVarNonlinear(expr);
6769 assert(var != NULL);
6770
6771 lb = SCIPvarGetLbLocal(var);
6772 ub = SCIPvarGetUbLocal(var);
6773
6774 /* skip already fixed variable */
6775 if( SCIPisEQ(scip, lb, ub) )
6776 {
6777 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " skip fixed variable <%s>[%.15g,%.15g]\n", SCIPvarGetName(var), lb, ub); )
6778 continue;
6779 }
6780
6781 assert(*ncands + 1 < SCIPgetNVars(scip));
6782 cands[*ncands].expr = expr;
6783 cands[*ncands].auxviol = SCIPgetExprViolScoreNonlinear(expr);
6784 ++(*ncands);
6785 }
6786 }
6787 }
6788
6789 /* if we have branching candidates, then we don't need another attempt */
6790 if( *ncands > 0 )
6791 break;
6792 }
6793
6794 if( it != NULL )
6795 SCIPfreeExpriter(&it);
6796
6797 return SCIP_OKAY;
6798 }
6799
6800 /** computes a branching score for a variable that reflects how important branching on this variable would be for
6801 * improving the dual bound from the LP relaxation
6802 *
6803 * Assume the Lagrangian for the current LP is something of the form
6804 * L(x,z,lambda) = c'x + sum_i lambda_i (a_i'x - z_i + b_i) + ...
6805 * where x are the original variables, z the auxiliary variables,
6806 * and a_i'x - z_i + b_i <= 0 are the rows of the LP.
6807 *
6808 * Assume that a_i'x + b_i <= z_i was derived from some nonlinear constraint f(x) <= z and drop index i.
6809 * If we could have used not only an estimator, but the actual function f(x), then this would
6810 * have contributed lambda*(f(x) - z) to the Lagrangian function (though the value of z would be different).
6811 * Using a lot of handwaving, we claim that
6812 * lambda_i * (f(x) - a_i'x + b_i)
6813 * is a value that can be used to quantity how much improving the estimator a'x + b <= z could change the dual bound.
6814 * If an estimator depended on local bounds, then it could be improved by branching.
6815 * We use row-is-local as proxy for estimator-depending-on-lower-bounds.
6816 *
6817 * To score a variable, we then sum the values lambda_i * (f(x) - a_i'x + b_i) for all rows in which the variable appears.
6818 * To scale, we divide by the LP objective value (if >1).
6819 *
6820 * TODO if we branch only on original variables, we neglect here estimators that are build on auxiliary variables;
6821 * these are affected by the bounds on original variables indirectly (through forward-propagation)
6822 *
6823 * TODO if we branch also on auxiliary variables, then separating z from the x-variables in the row a'x+b <= z should happen;
6824 * in effect, we should go from the row to the expression for which it was generated and consider only variables that
6825 * would also be branching candidates
6826 */
6827 static
6828 SCIP_Real getDualBranchscore(
6829 SCIP* scip, /**< SCIP data structure */
6830 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraints handler */
6831 SCIP_VAR* var /**< variable */
6832 )
6833 {
6834 SCIP_COL* col;
6835 SCIP_ROW** rows;
6836 int nrows;
6837 int r;
6838 SCIP_Real dualscore;
6839
6840 assert(scip != NULL);
6841 assert(conshdlr != NULL);
6842 assert(var != NULL);
6843
6844 /* if LP not solved, then the dual branching score is not available */
6845 if( SCIPgetLPSolstat(scip) != SCIP_LPSOLSTAT_OPTIMAL )
6846 return 0.0;
6847
6848 /* if var is not in the LP, then the dual branching score is not available */
6849 if( SCIPvarGetStatus(var) != SCIP_VARSTATUS_COLUMN )
6850 return 0.0;
6851
6852 col = SCIPvarGetCol(var);
6853 assert(col != NULL);
6854
6855 if( !SCIPcolIsInLP(col) )
6856 return 0.0;
6857
6858 nrows = SCIPcolGetNLPNonz(col); /* TODO there is a big warning on when not to use this method; is the check for SCIPcolIsInLP sufficient? */
6859 rows = SCIPcolGetRows(col);
6860
6861 /* SCIPinfoMessage(scip, enfologfile, " dualscoring <%s>\n", SCIPvarGetName(var)); */
6862
6863 /* aggregate duals from all rows from consexpr with non-zero dual
6864 * TODO: this is a quick-and-dirty implementation, and not used by default
6865 * in the long run, this should be either removed or replaced by a proper implementation
6866 */
6867 dualscore = 0.0;
6868 for( r = 0; r < nrows; ++r )
6869 {
6870 SCIP_Real estimategap;
6871 const char* estimategapstr;
6872
6873 /* rows from cuts that may be replaced by tighter ones after branching are the interesting ones
6874 * these would typically be local, unless they are created at the root node
6875 * so not check for local now, but trust that estimators that do not improve after branching will have an estimategap of 0
6876 if( !SCIProwIsLocal(rows[r]) )
6877 continue;
6878 */
6879 if( SCIProwGetOriginConshdlr(rows[r]) != conshdlr )
6880 continue;
6881 if( SCIPisZero(scip, SCIProwGetDualsol(rows[r])) )
6882 continue;
6883
6884 estimategapstr = strstr(SCIProwGetName(rows[r]), "_estimategap=");
6885 if( estimategapstr == NULL ) /* gap not stored, maybe because it was 0 */
6886 continue;
6887 estimategap = atof(estimategapstr + 13);
6888 assert(estimategap >= 0.0);
6889 if( !SCIPisFinite(estimategap) || SCIPisHugeValue(scip, estimategap) )
6890 estimategap = SCIPgetHugeValue(scip);
6891
6892 /* SCIPinfoMessage(scip, enfologfile, " row <%s> contributes %g*|%g|: ", SCIProwGetName(rows[r]), estimategap, SCIProwGetDualsol(rows[r]));
6893 SCIP_CALL( SCIPprintRow(scip, rows[r], enfologfile) ); */
6894
6895 dualscore += estimategap * REALABS(SCIProwGetDualsol(rows[r]));
6896 }
6897
6898 /* divide by optimal value of LP for scaling */
6899 dualscore /= MAX(1.0, REALABS(SCIPgetLPObjval(scip)));
6900
6901 return dualscore;
6902 }
6903
6904 /** computes branching scores (including weighted score) for a set of candidates
6905 *
6906 * For each candidate in the array, compute and store the various branching scores (violation, pseudo-costs, vartype, domainwidth).
6907 * For pseudo-costs, it's possible that the score is not available, in which case cands[c].pscost will be set to SCIP_INVALID.
6908 *
6909 * For each score, compute the maximum over all candidates.
6910 *
6911 * Then compute for each candidate a "weighted" score using the weights as specified by parameters
6912 * and the scores as previously computed, but scale each score to be in [0,1], i.e., divide each score by the maximum
6913 * score of all candidates.
6914 * Further divide by the sum of all weights where a score was available (even if the score was 0).
6915 *
6916 * For example:
6917 * - Let variable x have violation-score 10.0 and pseudo-cost-score 5.0.
6918 * - Let variable y have violation-score 12.0 but no pseudo-cost-score (because it hasn't yet been branched on sufficiently often).
6919 * - Assuming violation is weighted by 2.0 and pseudo-costs are weighted by 3.0.
6920 * - Then the weighted scores for x will be (2.0 * 10.0/12.0 + 3.0 * 5.0/5.0) / (2.0 + 3.0) = 0.9333.
6921 * The weighted score for y will be (2.0 * 12.0/12.0) / 2.0 = 1.0.
6922 */
6923 static
6924 void scoreBranchingCandidates(
6925 SCIP* scip, /**< SCIP data structure */
6926 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
6927 BRANCHCAND* cands, /**< branching candidates */
6928 int ncands, /**< number of candidates */
6929 SCIP_SOL* sol /**< solution to enforce (NULL for the LP solution) */
6930 )
6931 {
6932 SCIP_CONSHDLRDATA* conshdlrdata;
6933 BRANCHCAND maxscore;
6934 int c;
6935
6936 assert(scip != NULL);
6937 assert(conshdlr != NULL);
6938 assert(cands != NULL);
6939 assert(ncands > 0);
6940
6941 conshdlrdata = SCIPconshdlrGetData(conshdlr);
6942 assert(conshdlrdata != NULL);
6943
6944 /* initialize counts to 0 */
6945 memset(&maxscore, 0, sizeof(BRANCHCAND));
6946
6947 for( c = 0; c < ncands; ++c )
6948 {
6949 if( conshdlrdata->branchviolweight > 0.0 )
6950 {
6951 /* cands[c].auxviol was set in collectBranchingCandidates, so only update maxscore here */
6952 maxscore.auxviol = MAX(maxscore.auxviol, cands[c].auxviol);
6953 }
6954
6955 if( conshdlrdata->branchdomainweight > 0.0 )
6956 {
6957 SCIP_Real domainwidth;
6958 SCIP_VAR* var;
6959
6960 var = SCIPgetExprAuxVarNonlinear(cands[c].expr);
6961 assert(var != NULL);
6962
6963 /* get domain width, taking infinity at 1e20 on purpose */
6964 domainwidth = SCIPvarGetUbLocal(var) - SCIPvarGetLbLocal(var);
6965
6966 /* domain-score is going to be log(2*infinity / domainwidth) if domain width >= 1
6967 * and log(2 * infinity * MAX(epsilon, domainwidth)) for domain width < 1
6968 * the idea is to penalize very large and very small domains
6969 */
6970 if( domainwidth >= 1.0 )
6971 cands[c].domain = log10(2 * SCIPinfinity(scip) / domainwidth);
6972 else
6973 cands[c].domain = log10(2 * SCIPinfinity(scip) * MAX(SCIPepsilon(scip), domainwidth));
6974
6975 maxscore.domain = MAX(cands[c].domain, maxscore.domain);
6976 }
6977 else
6978 cands[c].domain = 0.0;
6979
6980 if( conshdlrdata->branchdualweight > 0.0 )
6981 {
6982 SCIP_VAR* var;
6983
6984 var = SCIPgetExprAuxVarNonlinear(cands[c].expr);
6985 assert(var != NULL);
6986
6987 cands[c].dual = getDualBranchscore(scip, conshdlr, var);
6988 maxscore.dual = MAX(cands[c].dual, maxscore.dual);
6989 }
6990
6991 if( conshdlrdata->branchpscostweight > 0.0 && SCIPgetNObjVars(scip) > 0 )
6992 {
6993 SCIP_VAR* var;
6994
6995 var = SCIPgetExprAuxVarNonlinear(cands[c].expr);
6996 assert(var != NULL);
6997
6998 if( SCIPisInfinity(scip, -SCIPvarGetLbLocal(var)) || SCIPisInfinity(scip, SCIPvarGetUbLocal(var)) )
6999 cands[c].pscost = SCIP_INVALID;
7000 else
7001 {
7002 SCIP_Real brpoint;
7003 SCIP_Real pscostdown;
7004 SCIP_Real pscostup;
7005 char strategy;
7006
7007 /* decide how to compute pseudo-cost scores
7008 * this should be consistent with the way how pseudo-costs are updated in the core, which is decided by
7009 * branching/lpgainnormalize for continuous variables and move in LP-value for non-continuous variables
7010 */
7011 if( SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS )
7012 strategy = conshdlrdata->branchpscostupdatestrategy;
7013 else
7014 strategy = 'l';
7015
7016 brpoint = SCIPgetBranchingPoint(scip, var, SCIP_INVALID);
7017
7018 /* branch_relpscost deems pscosts as reliable, if the pseudo-count is at least something between 1 and 4
7019 * or it uses some statistical tests involving SCIPisVarPscostRelerrorReliable
7020 * For here, I use a simple #counts >= branchpscostreliable.
7021 * TODO use SCIPgetVarPseudocostCount() instead?
7022 */
7023 if( SCIPgetVarPseudocostCountCurrentRun(scip, var, SCIP_BRANCHDIR_DOWNWARDS) >= conshdlrdata->branchpscostreliable )
7024 {
7025 switch( strategy )
7026 {
7027 case 's' :
7028 pscostdown = SCIPgetVarPseudocostVal(scip, var, -(SCIPvarGetUbLocal(var) - SCIPadjustedVarLb(scip, var, brpoint)));
7029 break;
7030 case 'd' :
7031 pscostdown = SCIPgetVarPseudocostVal(scip, var, -(SCIPadjustedVarUb(scip, var, brpoint) - SCIPvarGetLbLocal(var)));
7032 break;
7033 case 'l' :
7034 if( SCIPisInfinity(scip, SCIPgetSolVal(scip, sol, var)) )
7035 pscostdown = SCIP_INVALID;
7036 else if( SCIPgetSolVal(scip, sol, var) <= SCIPadjustedVarUb(scip, var, brpoint) )
7037 pscostdown = SCIPgetVarPseudocostVal(scip, var, 0.0);
7038 else
7039 pscostdown = SCIPgetVarPseudocostVal(scip, var, -(SCIPgetSolVal(scip, NULL, var) - SCIPadjustedVarUb(scip, var, brpoint)));
7040 break;
7041 default :
7042 SCIPerrorMessage("pscost update strategy %c unknown\n", strategy);
7043 pscostdown = SCIP_INVALID;
7044 }
7045 }
7046 else
7047 pscostdown = SCIP_INVALID;
7048
7049 if( SCIPgetVarPseudocostCountCurrentRun(scip, var, SCIP_BRANCHDIR_UPWARDS) >= conshdlrdata->branchpscostreliable )
7050 {
7051 switch( strategy )
7052 {
7053 case 's' :
7054 pscostup = SCIPgetVarPseudocostVal(scip, var, SCIPadjustedVarUb(scip, var, brpoint) - SCIPvarGetLbLocal(var));
7055 break;
7056 case 'd' :
7057 pscostup = SCIPgetVarPseudocostVal(scip, var, SCIPvarGetUbLocal(var) - SCIPadjustedVarLb(scip, var, brpoint));
7058 break;
7059 case 'l' :
7060 if( SCIPisInfinity(scip, -SCIPgetSolVal(scip, sol, var)) )
7061 pscostup = SCIP_INVALID;
7062 else if( SCIPgetSolVal(scip, NULL, var) >= SCIPadjustedVarLb(scip, var, brpoint) )
7063 pscostup = SCIPgetVarPseudocostVal(scip, var, 0.0);
7064 else
7065 pscostup = SCIPgetVarPseudocostVal(scip, var, SCIPadjustedVarLb(scip, var, brpoint) - SCIPgetSolVal(scip, NULL, var) );
7066 break;
7067 default :
7068 SCIPerrorMessage("pscost update strategy %c unknown\n", strategy);
7069 pscostup = SCIP_INVALID;
7070 }
7071 }
7072 else
7073 pscostup = SCIP_INVALID;
7074
7075 /* TODO if both are valid, we get pscostdown*pscostup, but does this compare well with vars were only pscostdown or pscostup is used?
7076 * maybe we should use (pscostdown+pscostup)/2 or sqrt(pscostdown*pscostup) ?
7077 */
7078 if( pscostdown == SCIP_INVALID && pscostup == SCIP_INVALID )
7079 cands[c].pscost = SCIP_INVALID;
7080 else if( pscostdown == SCIP_INVALID )
7081 cands[c].pscost = pscostup;
7082 else if( pscostup == SCIP_INVALID )
7083 cands[c].pscost = pscostdown;
7084 else
7085 cands[c].pscost = SCIPgetBranchScore(scip, NULL, pscostdown, pscostup); /* pass NULL for var to avoid multiplication with branch-factor */
7086 }
7087
7088 if( cands[c].pscost != SCIP_INVALID )
7089 maxscore.pscost = MAX(cands[c].pscost, maxscore.pscost);
7090 }
7091
7092 if( conshdlrdata->branchvartypeweight > 0.0 )
7093 {
7094 SCIP_VAR* var;
7095
7096 var = SCIPgetExprAuxVarNonlinear(cands[c].expr);
7097 assert(var != NULL);
7098
7099 switch( SCIPvarGetType(var) )
7100 {
7101 case SCIP_VARTYPE_BINARY :
7102 cands[c].vartype = 1.0;
7103 break;
7104 case SCIP_VARTYPE_INTEGER :
7105 cands[c].vartype = 0.1;
7106 break;
7107 case SCIP_VARTYPE_IMPLINT :
7108 cands[c].vartype = 0.01;
7109 break;
7110 case SCIP_VARTYPE_CONTINUOUS :
7111 default:
7112 cands[c].vartype = 0.0;
7113 }
7114 maxscore.vartype = MAX(cands[c].vartype, maxscore.vartype);
7115 }
7116 }
7117
7118 /* now compute a weighted score for each candidate from the single scores
7119 * the single scores are scaled to be in [0,1] for this
7120 */
7121 for( c = 0; c < ncands; ++c )
7122 {
7123 SCIP_Real weightsum;
7124
7125 ENFOLOG(
7126 SCIP_VAR* var;
7127 var = SCIPgetExprAuxVarNonlinear(cands[c].expr);
7128 SCIPinfoMessage(scip, enfologfile, " scoring <%8s>[%7.1g,%7.1g]:(", SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var));
7129 )
7130
7131 cands[c].weighted = 0.0;
7132 weightsum = 0.0;
7133
7134 if( maxscore.auxviol > 0.0 )
7135 {
7136 cands[c].weighted += conshdlrdata->branchviolweight * cands[c].auxviol / maxscore.auxviol;
7137 weightsum += conshdlrdata->branchviolweight;
7138
7139 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " %+g*%7.2g(viol)", conshdlrdata->branchviolweight, cands[c].auxviol / maxscore.auxviol); )
7140 }
7141
7142 if( maxscore.domain > 0.0 )
7143 {
7144 cands[c].weighted += conshdlrdata->branchdomainweight * cands[c].domain / maxscore.domain;
7145 weightsum += conshdlrdata->branchdomainweight;
7146
7147 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " %+g*%7.2g(domain)", conshdlrdata->branchdomainweight, cands[c].domain / maxscore.domain); )
7148 }
7149
7150 if( maxscore.dual > 0.0 )
7151 {
7152 cands[c].weighted += conshdlrdata->branchdualweight * cands[c].dual / maxscore.dual;
7153 weightsum += conshdlrdata->branchdualweight;
7154
7155 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " %+g*%7.2g(dual)", conshdlrdata->branchdualweight, cands[c].dual / maxscore.dual); )
7156 }
7157
7158 if( maxscore.pscost > 0.0 )
7159 {
7160 /* use pseudo-costs only if available */
7161 if( cands[c].pscost != SCIP_INVALID )
7162 {
7163 cands[c].weighted += conshdlrdata->branchpscostweight * cands[c].pscost / maxscore.pscost;
7164 weightsum += conshdlrdata->branchpscostweight;
7165
7166 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " %+g*%7.2g(pscost)", conshdlrdata->branchpscostweight, cands[c].pscost / maxscore.pscost); )
7167 }
7168 else
7169 {
7170 /* do not add pscostscore, if not available, also do not add into weightsum */
7171 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " +0.0* n/a(pscost)"); )
7172 }
7173 }
7174
7175 if( maxscore.vartype > 0.0 )
7176 {
7177 cands[c].weighted += conshdlrdata->branchvartypeweight * cands[c].vartype / maxscore.vartype;
7178 weightsum += conshdlrdata->branchvartypeweight;
7179
7180 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " %+g*%6.2g(vartype)", conshdlrdata->branchvartypeweight, cands[c].vartype / maxscore.vartype); )
7181 }
7182 assert(weightsum > 0.0); /* we should have got at least one valid score */
7183 cands[c].weighted /= weightsum;
7184
7185 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " ) / %g = %g\n", weightsum, cands[c].weighted); )
7186 }
7187 }
7188
7189 /** compare two branching candidates by their weighted score
7190 *
7191 * if weighted score is equal, use variable index of (aux)var
7192 */
7193 static
7194 SCIP_DECL_SORTINDCOMP(branchcandCompare)
7195 {
7196 BRANCHCAND* cands = (BRANCHCAND*)dataptr;
7197
7198 if( cands[ind1].weighted != cands[ind2].weighted )
7199 return cands[ind1].weighted < cands[ind2].weighted ? -1 : 1;
7200 else
7201 return SCIPvarGetIndex(SCIPgetExprAuxVarNonlinear(cands[ind1].expr)) - SCIPvarGetIndex(SCIPgetExprAuxVarNonlinear(cands[ind2].expr));
7202 }
7203
7204 /** do branching or register branching candidates */
7205 static
7206 SCIP_RETCODE branching(
7207 SCIP* scip, /**< SCIP data structure */
7208 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
7209 SCIP_CONS** conss, /**< constraints to process */
7210 int nconss, /**< number of constraints */
7211 SCIP_Real maxrelconsviol, /**< maximal scaled constraint violation */
7212 SCIP_SOL* sol, /**< solution to enforce (NULL for the LP solution) */
7213 SCIP_Longint soltag, /**< tag of solution */
7214 SCIP_RESULT* result /**< pointer to store the result of branching */
7215 )
7216 {
7217 SCIP_CONSHDLRDATA* conshdlrdata;
7218 BRANCHCAND* cands;
7219 int ncands;
7220 SCIP_VAR* var;
7221 SCIP_NODE* downchild;
7222 SCIP_NODE* eqchild;
7223 SCIP_NODE* upchild;
7224
7225 assert(conshdlr != NULL);
7226 assert(result != NULL);
7227
7228 *result = SCIP_DIDNOTFIND;
7229
7230 conshdlrdata = SCIPconshdlrGetData(conshdlr);
7231 assert(conshdlrdata != NULL);
7232
7233 if( conshdlrdata->branchexternal )
7234 {
7235 /* just register branching candidates as external */
7236 SCIP_Bool success;
7237
7238 SCIP_CALL( registerBranchingCandidates(scip, conshdlr, conss, nconss, &success) );
7239 if( success )
7240 *result = SCIP_INFEASIBLE;
7241
7242 return SCIP_OKAY;
7243 }
7244
7245 /* collect branching candidates and their auxviol-score */
7246 SCIP_CALL( SCIPallocBufferArray(scip, &cands, SCIPgetNVars(scip)) );
7247 SCIP_CALL( collectBranchingCandidates(scip, conshdlr, conss, nconss, maxrelconsviol, sol, soltag, cands, &ncands) );
7248
7249 /* if no unfixed branching candidate in all violated constraint, then it's probably numerics that prevented us to separate or decide a cutoff
7250 * we will return here and let the fallbacks in consEnfo() decide how to proceed
7251 */
7252 if( ncands == 0 )
7253 goto TERMINATE;
7254
7255 if( ncands > 1 )
7256 {
7257 /* if there are more than one candidate, then compute scores and select */
7258 int* perm;
7259 int c;
7260 int left;
7261 int right;
7262 SCIP_Real threshold;
7263
7264 /* compute additional scores on branching candidates and weighted score */
7265 scoreBranchingCandidates(scip, conshdlr, cands, ncands, sol);
7266
7267 /* sort candidates by weighted score */
7268 SCIP_CALL( SCIPallocBufferArray(scip, &perm, ncands) );
7269 SCIPsortDown(perm, branchcandCompare, (void*)cands, ncands);
7270
7271 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " %d branching candidates <%s>(%g)...<%s>(%g)\n", ncands,
7272 SCIPvarGetName(SCIPgetExprAuxVarNonlinear(cands[perm[0]].expr)), cands[perm[0]].weighted,
7273 SCIPvarGetName(SCIPgetExprAuxVarNonlinear(cands[perm[ncands - 1]].expr)), cands[perm[ncands - 1]].weighted); )
7274
7275 /* binary search to find first low-scored (score below branchhighscorefactor * maximal-score) candidate */
7276 left = 0;
7277 right = ncands - 1;
7278 threshold = conshdlrdata->branchhighscorefactor * cands[perm[0]].weighted;
7279 while( left < right )
7280 {
7281 int mid = (left + right) / 2;
7282 if( cands[perm[mid]].weighted >= threshold )
7283 left = mid + 1;
7284 else
7285 right = mid;
7286 }
7287 assert(left <= ncands);
7288
7289 if( left < ncands )
7290 {
7291 if( cands[perm[left]].weighted >= threshold )
7292 {
7293 assert(left + 1 == ncands || cands[perm[left + 1]].weighted < threshold);
7294 ncands = left + 1;
7295 }
7296 else
7297 {
7298 assert(cands[perm[left]].weighted < threshold);
7299 ncands = left;
7300 }
7301 }
7302 assert(ncands > 0);
7303
7304 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " %d branching candidates <%s>(%g)...<%s>(%g) after removing low scores\n", ncands,
7305 SCIPvarGetName(SCIPgetExprAuxVarNonlinear(cands[perm[0]].expr)), cands[perm[0]].weighted,
7306 SCIPvarGetName(SCIPgetExprAuxVarNonlinear(cands[perm[ncands - 1]].expr)), cands[perm[ncands - 1]].weighted); )
7307
7308 if( ncands > 1 )
7309 {
7310 /* choose at random from candidates 0..ncands-1 */
7311 if( conshdlrdata->branchrandnumgen == NULL )
7312 {
7313 SCIP_CALL( SCIPcreateRandom(scip, &conshdlrdata->branchrandnumgen, BRANCH_RANDNUMINITSEED, TRUE) );
7314 }
7315 c = SCIPrandomGetInt(conshdlrdata->branchrandnumgen, 0, ncands - 1);
7316 var = SCIPgetExprAuxVarNonlinear(cands[perm[c]].expr);
7317 }
7318 else
7319 var = SCIPgetExprAuxVarNonlinear(cands[perm[0]].expr);
7320
7321 SCIPfreeBufferArray(scip, &perm);
7322 }
7323 else
7324 {
7325 var = SCIPgetExprAuxVarNonlinear(cands[0].expr);
7326 }
7327 assert(var != NULL);
7328
7329 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " branching on variable <%s>[%g,%g]\n", SCIPvarGetName(var),
7330 SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)); )
7331
7332 SCIP_CALL( SCIPbranchVarVal(scip, var, SCIPgetBranchingPoint(scip, var, SCIP_INVALID), &downchild, &eqchild,
7333 &upchild) );
7334 if( downchild != NULL || eqchild != NULL || upchild != NULL )
7335 *result = SCIP_BRANCHED;
7336 else
7337 /* if there are no children, then variable should have been fixed by SCIPbranchVarVal */
7338 *result = SCIP_REDUCEDDOM;
7339
7340 TERMINATE:
7341 SCIPfreeBufferArray(scip, &cands);
7342
7343 return SCIP_OKAY;
7344 }
7345
7346 /** call enforcement or estimate callback of nonlinear handler
7347 *
7348 * Calls the enforcement callback, if available.
7349 * Otherwise, calls the estimate callback, if available, and constructs a cut from the estimator.
7350 *
7351 * If cut is weak, but estimator is not tight, tries to add branching candidates.
7352 */
7353 static
7354 SCIP_RETCODE enforceExprNlhdlr(
7355 SCIP* scip, /**< SCIP main data structure */
7356 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
7357 SCIP_CONS* cons, /**< nonlinear constraint */
7358 SCIP_NLHDLR* nlhdlr, /**< nonlinear handler */
7359 SCIP_EXPR* expr, /**< expression */
7360 SCIP_NLHDLREXPRDATA* nlhdlrexprdata, /**< nonlinear handler data of expression */
7361 SCIP_SOL* sol, /**< solution to be separated (NULL for the LP solution) */
7362 SCIP_Real auxvalue, /**< current value of expression w.r.t. auxiliary variables as obtained from EVALAUX */
7363 SCIP_Bool overestimate, /**< whether the expression needs to be over- or underestimated */
7364 SCIP_Bool separated, /**< whether another nonlinear handler already added a cut for this expression */
7365 SCIP_Bool allowweakcuts, /**< whether we allow for weak cuts */
7366 SCIP_Bool inenforcement, /**< whether we are in enforcement (and not just separation) */
7367 SCIP_RESULT* result /**< pointer to store the result */
7368 )
7369 {
7370 assert(result != NULL);
7371
7372 /* call enforcement callback of the nlhdlr */
7373 SCIP_CALL( SCIPnlhdlrEnfo(scip, conshdlr, cons, nlhdlr, expr, nlhdlrexprdata, sol, auxvalue, overestimate,
7374 allowweakcuts, separated, inenforcement, result) );
7375
7376 /* if it was not running (e.g., because it was not available) or did not find anything, then try with estimator callback */
7377 if( *result != SCIP_DIDNOTRUN && *result != SCIP_DIDNOTFIND )
7378 {
7379 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " sepa of nlhdlr %s succeeded with result %d\n",
7380 SCIPnlhdlrGetName(nlhdlr), *result); )
7381 return SCIP_OKAY;
7382 }
7383 else
7384 {
7385 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " sepa of nlhdlr <%s> did not succeed with result %d\n", SCIPnlhdlrGetName(nlhdlr), *result); )
7386 }
7387
7388 *result = SCIP_DIDNOTFIND;
7389
7390 /* now call the estimator callback of the nlhdlr */
7391 if( SCIPnlhdlrHasEstimate(nlhdlr) )
7392 {
7393 SCIP_VAR* auxvar;
7394 SCIP_Bool sepasuccess = FALSE;
7395 SCIP_Bool branchscoresuccess = FALSE;
7396 SCIP_PTRARRAY* rowpreps;
7397 int minidx;
7398 int maxidx;
7399 int r;
7400 SCIP_ROWPREP* rowprep;
7401
7402 SCIP_CALL( SCIPcreatePtrarray(scip, &rowpreps) );
7403
7404 auxvar = SCIPgetExprAuxVarNonlinear(expr);
7405 assert(auxvar != NULL);
7406
7407 SCIP_CALL( SCIPnlhdlrEstimate(scip, conshdlr, nlhdlr, expr, nlhdlrexprdata, sol, auxvalue, overestimate,
7408 SCIPgetSolVal(scip, sol, auxvar), inenforcement, rowpreps, &sepasuccess, &branchscoresuccess) );
7409
7410 minidx = SCIPgetPtrarrayMinIdx(scip, rowpreps);
7411 maxidx = SCIPgetPtrarrayMaxIdx(scip, rowpreps);
7412
7413 assert((sepasuccess && minidx <= maxidx) || (!sepasuccess && minidx > maxidx));
7414
7415 if( !sepasuccess )
7416 {
7417 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " estimate of nlhdlr %s failed\n",
7418 SCIPnlhdlrGetName(nlhdlr)); )
7419 }
7420
7421 for( r = minidx; r <= maxidx; ++r )
7422 {
7423 rowprep = (SCIP_ROWPREP*) SCIPgetPtrarrayVal(scip, rowpreps, r);
7424
7425 assert(rowprep != NULL);
7426 assert(SCIProwprepGetSidetype(rowprep) == (overestimate ? SCIP_SIDETYPE_LEFT : SCIP_SIDETYPE_RIGHT));
7427
7428 /* complete estimator to cut */
7429 SCIP_CALL( SCIPaddRowprepTerm(scip, rowprep, auxvar, -1.0) );
7430
7431 /* add the cut and/or branching scores */
7432 SCIP_CALL( SCIPprocessRowprepNonlinear(scip, nlhdlr, cons, expr, rowprep, overestimate, auxvar,
7433 auxvalue, allowweakcuts, branchscoresuccess, inenforcement, sol, result) );
7434
7435 SCIPfreeRowprep(scip, &rowprep);
7436 }
7437
7438 SCIP_CALL( SCIPfreePtrarray(scip, &rowpreps) );
7439 }
7440
7441 return SCIP_OKAY;
7442 }
7443
7444 /** tries to enforce violation in an expression by separation, bound tightening, or finding a branching candidate
7445 *
7446 * if not inenforcement, then we should be called by consSepa(), and thus only try separation
7447 */
7448 static
7449 SCIP_RETCODE enforceExpr(
7450 SCIP* scip, /**< SCIP data structure */
7451 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraints handler */
7452 SCIP_CONS* cons, /**< nonlinear constraint */
7453 SCIP_EXPR* expr, /**< expression */
7454 SCIP_SOL* sol, /**< solution to separate, or NULL if LP solution should be used */
7455 SCIP_Longint soltag, /**< tag of solution */
7456 SCIP_Bool allowweakcuts, /**< whether we allow weak cuts */
7457 SCIP_Bool inenforcement, /**< whether we are in enforcement (and not just separation) */
7458 SCIP_RESULT* result /**< pointer to store the result of the enforcing call */
7459 )
7460 {
7461 SCIP_CONSHDLRDATA* conshdlrdata;
7462 SCIP_EXPR_OWNERDATA* ownerdata;
7463 SCIP_Real origviol;
7464 SCIP_Bool underestimate;
7465 SCIP_Bool overestimate;
7466 SCIP_Real auxviol;
7467 SCIP_Bool auxunderestimate;
7468 SCIP_Bool auxoverestimate;
7469 SCIP_RESULT hdlrresult;
7470 int e;
7471
7472 assert(scip != NULL);
7473 assert(expr != NULL);
7474 assert(result != NULL);
7475
7476 ownerdata = SCIPexprGetOwnerData(expr);
7477 assert(ownerdata != NULL);
7478 assert(ownerdata->auxvar != NULL); /* there must be a variable attached to the expression in order to construct a cut here */
7479
7480 *result = SCIP_DIDNOTFIND;
7481
7482 /* make sure that this expression has been evaluated */
7483 SCIP_CALL( SCIPevalExpr(scip, expr, sol, soltag) );
7484
7485 /* decide whether under- or overestimate is required and get amount of violation */
7486 origviol = getExprAbsOrigViolation(scip, expr, sol, &underestimate, &overestimate);
7487
7488 conshdlrdata = SCIPconshdlrGetData(conshdlr);
7489 assert(conshdlrdata != NULL);
7490
7491 /* no sufficient violation w.r.t. the original variables -> skip expression */
7492 if( !overestimate && !underestimate )
7493 {
7494 return SCIP_OKAY;
7495 }
7496
7497 /* check aux-violation w.r.t. each nonlinear handlers and try to enforce when there is a decent violation */
7498 for( e = 0; e < ownerdata->nenfos; ++e )
7499 {
7500 SCIP_NLHDLR* nlhdlr;
7501
7502 /* skip nlhdlr that do not want to participate in any separation */
7503 if( (ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPABOTH) == 0 )
7504 continue;
7505
7506 nlhdlr = ownerdata->enfos[e]->nlhdlr;
7507 assert(nlhdlr != NULL);
7508
7509 /* evaluate the expression w.r.t. the nlhdlrs auxiliary variables */
7510 SCIP_CALL( SCIPnlhdlrEvalaux(scip, nlhdlr, expr, ownerdata->enfos[e]->nlhdlrexprdata, &ownerdata->enfos[e]->auxvalue, sol) );
7511 ENFOLOG(
7512 SCIPinfoMessage(scip, enfologfile, " expr ");
7513 SCIPprintExpr(scip, expr, enfologfile);
7514 SCIPinfoMessage(scip, enfologfile, " (%p): evalvalue %.15g auxvarvalue %.15g [%.15g,%.15g], nlhdlr <%s> " \
7515 "auxvalue: %.15g\n", (void*)expr, SCIPexprGetEvalValue(expr), SCIPgetSolVal(scip, sol, ownerdata->auxvar),
7516 SCIPexprGetActivity(expr).inf, SCIPexprGetActivity(expr).sup, SCIPnlhdlrGetName(nlhdlr), ownerdata->enfos[e]->auxvalue);
7517 )
7518
7519 /* TODO if expr is root of constraint (consdata->expr == expr),
7520 * then compare auxvalue with constraint sides instead of auxvarvalue, as the former is what actually matters
7521 * that is, if auxvalue is good enough for the constraint to be satisfied, but when looking at evalvalue we see
7522 * the the constraint is violated, then some of the auxvars that nlhdlr uses is not having a good enough value,
7523 * so we should enforce in these auxiliaries first
7524 * if changing this here, we must also adapt analyzeViolation()
7525 */
7526
7527 auxviol = getExprAbsAuxViolation(scip, expr, ownerdata->enfos[e]->auxvalue, sol, &auxunderestimate, &auxoverestimate);
7528 assert(auxviol >= 0.0);
7529
7530 /* if aux-violation is much smaller than orig-violation, then better enforce further down in the expression first */
7531 if( !SCIPisInfinity(scip, auxviol) && auxviol < conshdlrdata->enfoauxviolfactor * origviol )
7532 {
7533 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " skip enforce using nlhdlr <%s> for expr %p (%s) with " \
7534 "auxviolation %g << origviolation %g under:%d over:%d\n", SCIPnlhdlrGetName(nlhdlr), (void*)expr,
7535 SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)), auxviol, origviol, underestimate, overestimate); )
7536
7537 /* TODO should we do expr->lastenforced = conshdlrdata->enforound even though we haven't enforced, but only decided not to enforce? */
7538 continue;
7539 }
7540
7541 /* if aux-violation is small (below feastol) and we look only for strong cuts, then it's unlikely to give a strong cut, so skip it */
7542 if( !allowweakcuts && auxviol < SCIPfeastol(scip) )
7543 {
7544 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " skip enforce using nlhdlr <%s> for expr %p (%s) with tiny " \
7545 "auxviolation %g under:%d over:%d\n", SCIPnlhdlrGetName(nlhdlr), (void*)expr, SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)), auxviol,
7546 underestimate, overestimate); )
7547
7548 /* TODO should we do expr->lastenforced = conshdlrdata->enforound even though we haven't enforced, but only decided not to enforce? */
7549 continue;
7550 }
7551
7552 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " enforce using nlhdlr <%s> for expr %p (%s) with auxviolation " \
7553 "%g origviolation %g under:%d over:%d weak:%d\n", SCIPnlhdlrGetName(nlhdlr), (void*)expr, SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)),
7554 auxviol, origviol, underestimate, overestimate, allowweakcuts); )
7555
7556 /* if we want to overestimate and violation w.r.t. auxiliary variables is also present on this side and nlhdlr
7557 * wants to be called for separation on this side, then call separation of nlhdlr
7558 */
7559 if( overestimate && auxoverestimate && (ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPAABOVE) != 0 )
7560 {
7561 /* call the separation or estimation callback of the nonlinear handler for overestimation */
7562 hdlrresult = SCIP_DIDNOTFIND;
7563 SCIP_CALL( enforceExprNlhdlr(scip, conshdlr, cons, nlhdlr, expr, ownerdata->enfos[e]->nlhdlrexprdata, sol,
7564 ownerdata->enfos[e]->auxvalue, TRUE, *result == SCIP_SEPARATED, allowweakcuts, inenforcement, &hdlrresult) );
7565
7566 if( hdlrresult == SCIP_CUTOFF )
7567 {
7568 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " found a cutoff -> stop separation\n"); )
7569 *result = SCIP_CUTOFF;
7570 ownerdata->lastenforced = conshdlrdata->enforound;
7571 break;
7572 }
7573
7574 if( hdlrresult == SCIP_SEPARATED )
7575 {
7576 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " nlhdlr <%s> separating the current solution by cut\n", SCIPnlhdlrGetName(nlhdlr)); )
7577 *result = SCIP_SEPARATED;
7578 ownerdata->lastenforced = conshdlrdata->enforound;
7579 /* TODO or should we give other nlhdlr another chance? (also #3070) */
7580 break;
7581 }
7582
7583 if( hdlrresult == SCIP_REDUCEDDOM )
7584 {
7585 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " nlhdlr <%s> separating the current solution by boundchange\n", SCIPnlhdlrGetName(nlhdlr)); )
7586 *result = SCIP_REDUCEDDOM;
7587 ownerdata->lastenforced = conshdlrdata->enforound;
7588 /* TODO or should we always just stop here? */
7589 }
7590
7591 if( hdlrresult == SCIP_BRANCHED )
7592 {
7593 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " nlhdlr <%s> added branching candidate\n", SCIPnlhdlrGetName(nlhdlr)); )
7594 assert(inenforcement);
7595
7596 /* separation and domain reduction takes precedence over branching */
7597 assert(*result == SCIP_DIDNOTFIND || *result == SCIP_SEPARATED || *result == SCIP_REDUCEDDOM || *result == SCIP_BRANCHED);
7598 if( *result == SCIP_DIDNOTFIND )
7599 *result = SCIP_BRANCHED;
7600 ownerdata->lastenforced = conshdlrdata->enforound;
7601 }
7602 }
7603
7604 /* if we want to underestimate and violation w.r.t. auxiliary variables is also present on this side and nlhdlr
7605 * wants to be called for separation on this side, then call separation of nlhdlr
7606 */
7607 if( underestimate && auxunderestimate && (ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPABELOW) != 0 )
7608 {
7609 /* call the separation or estimation callback of the nonlinear handler for underestimation */
7610 hdlrresult = SCIP_DIDNOTFIND;
7611 SCIP_CALL( enforceExprNlhdlr(scip, conshdlr, cons, nlhdlr, expr, ownerdata->enfos[e]->nlhdlrexprdata, sol,
7612 ownerdata->enfos[e]->auxvalue, FALSE, *result == SCIP_SEPARATED, allowweakcuts, inenforcement, &hdlrresult) );
7613
7614 if( hdlrresult == SCIP_CUTOFF )
7615 {
7616 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " found a cutoff -> stop separation\n"); )
7617 *result = SCIP_CUTOFF;
7618 ownerdata->lastenforced = conshdlrdata->enforound;
7619 break;
7620 }
7621
7622 if( hdlrresult == SCIP_SEPARATED )
7623 {
7624 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " nlhdlr <%s> separating the current solution by cut\n", SCIPnlhdlrGetName(nlhdlr)); )
7625 *result = SCIP_SEPARATED;
7626 ownerdata->lastenforced = conshdlrdata->enforound;
7627 /* TODO or should we give other nlhdlr another chance? (also #3070) */
7628 break;
7629 }
7630
7631 if( hdlrresult == SCIP_REDUCEDDOM )
7632 {
7633 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " nlhdlr <%s> separating the current solution by boundchange\n", SCIPnlhdlrGetName(nlhdlr)); )
7634 *result = SCIP_REDUCEDDOM;
7635 ownerdata->lastenforced = conshdlrdata->enforound;
7636 /* TODO or should we always just stop here? */
7637 }
7638
7639 if( hdlrresult == SCIP_BRANCHED )
7640 {
7641 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " nlhdlr <%s> added branching candidate\n", SCIPnlhdlrGetName(nlhdlr)); )
7642 assert(inenforcement);
7643
7644 /* separation takes precedence over branching */
7645 assert(*result == SCIP_DIDNOTFIND || *result == SCIP_SEPARATED || *result == SCIP_REDUCEDDOM || *result == SCIP_BRANCHED);
7646 if( *result == SCIP_DIDNOTFIND )
7647 *result = SCIP_BRANCHED;
7648 ownerdata->lastenforced = conshdlrdata->enforound;
7649 }
7650 }
7651 }
7652
7653 return SCIP_OKAY;
7654 }
7655
7656 /** helper function to enforce a single constraint */
7657 static
7658 SCIP_RETCODE enforceConstraint(
7659 SCIP* scip, /**< SCIP data structure */
7660 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
7661 SCIP_CONS* cons, /**< constraint to process */
7662 SCIP_SOL* sol, /**< solution to enforce (NULL for the LP solution) */
7663 SCIP_Longint soltag, /**< tag of solution */
7664 SCIP_EXPRITER* it, /**< expression iterator that we can just use here */
7665 SCIP_Bool allowweakcuts, /**< whether to allow weak cuts in this round */
7666 SCIP_Bool inenforcement, /**< whether to we are in enforcement, and not just separation */
7667 SCIP_RESULT* result, /**< pointer to update with result of the enforcing call */
7668 SCIP_Bool* success /**< buffer to store whether some enforcement took place */
7669 )
7670 {
7671 SCIP_CONSDATA* consdata;
7672 SCIP_CONSHDLRDATA* conshdlrdata;
7673 SCIP_EXPR* expr;
7674
7675 assert(conshdlr != NULL);
7676 assert(cons != NULL);
7677 assert(it != NULL);
7678 assert(result != NULL);
7679 assert(success != NULL);
7680
7681 conshdlrdata = SCIPconshdlrGetData(conshdlr);
7682 assert(conshdlrdata != NULL);
7683
7684 consdata = SCIPconsGetData(cons);
7685 assert(consdata != NULL);
7686 assert(SCIPexprGetOwnerData(consdata->expr)->nenfos >= 0);
7687
7688 *success = FALSE;
7689
7690 if( inenforcement && !consdata->ispropagated )
7691 {
7692 /* If there are boundchanges that haven't been propagated to activities yet, then do this now and update bounds of
7693 * auxiliary variables, since some nlhdlr/exprhdlr may look at auxvar bounds or activities
7694 * (TODO: nlhdlr tells us now whether they do and so we could skip).
7695 * For now, update bounds of auxiliary variables only if called from enforcement, since updating auxvar bounds in
7696 * separation doesn't seem to be right (it would be ok if the boundchange cuts off the current LP solution by a
7697 * nice amount, but if not, we may just add a boundchange that doesn't change the dual bound much and could
7698 * confuse the stalling check for how long to do separation).
7699 */
7700 SCIP_Bool infeasible;
7701 int ntightenings;
7702
7703 SCIP_CALL( forwardPropExpr(scip, conshdlr, consdata->expr, inenforcement, &infeasible, &ntightenings) );
7704 if( infeasible )
7705 {
7706 *result = SCIP_CUTOFF;
7707 return SCIP_OKAY;
7708 }
7709 /* if we tightened an auxvar bound, we better communicate that */
7710 if( ntightenings > 0 )
7711 *result = SCIP_REDUCEDDOM;
7712 }
7713
7714 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
7715 {
7716 SCIP_EXPR_OWNERDATA* ownerdata;
7717 SCIP_RESULT resultexpr;
7718
7719 ownerdata = SCIPexprGetOwnerData(expr);
7720 assert(ownerdata != NULL);
7721
7722 /* we can only enforce if there is an auxvar to compare with */
7723 if( ownerdata->auxvar == NULL )
7724 continue;
7725
7726 assert(ownerdata->lastenforced <= conshdlrdata->enforound);
7727 if( ownerdata->lastenforced == conshdlrdata->enforound )
7728 {
7729 ENFOLOG(
7730 SCIPinfoMessage(scip, enfologfile, " skip expr ");
7731 SCIPprintExpr(scip, expr, enfologfile);
7732 SCIPinfoMessage(scip, enfologfile, " as already enforced in this enforound\n");
7733 )
7734 *success = TRUE;
7735 continue;
7736 }
7737
7738 SCIP_CALL( enforceExpr(scip, conshdlr, cons, expr, sol, soltag, allowweakcuts, inenforcement, &resultexpr) );
7739
7740 /* if not enforced, then we must not have found a cutoff, cut, domain reduction, or branchscore */
7741 assert((ownerdata->lastenforced == conshdlrdata->enforound) == (resultexpr != SCIP_DIDNOTFIND));
7742 if( ownerdata->lastenforced == conshdlrdata->enforound )
7743 *success = TRUE;
7744
7745 if( resultexpr == SCIP_CUTOFF )
7746 {
7747 *result = SCIP_CUTOFF;
7748 break;
7749 }
7750
7751 if( resultexpr == SCIP_SEPARATED )
7752 *result = SCIP_SEPARATED;
7753
7754 if( resultexpr == SCIP_REDUCEDDOM && *result != SCIP_SEPARATED )
7755 *result = SCIP_REDUCEDDOM;
7756
7757 if( resultexpr == SCIP_BRANCHED && *result != SCIP_SEPARATED && *result != SCIP_REDUCEDDOM )
7758 *result = SCIP_BRANCHED;
7759 }
7760
7761 return SCIP_OKAY;
7762 }
7763
7764 /** try to separate violated constraints and, if in enforcement, register branching scores
7765 *
7766 * Sets result to
7767 * - SCIP_DIDNOTFIND, if nothing of the below has been done
7768 * - SCIP_CUTOFF, if node can be cutoff,
7769 * - SCIP_SEPARATED, if a cut has been added,
7770 * - SCIP_REDUCEDDOM, if a domain reduction has been found,
7771 * - SCIP_BRANCHED, if branching has been done,
7772 * - SCIP_REDUCEDDOM, if a variable got fixed (in an attempt to branch on it),
7773 * - SCIP_INFEASIBLE, if external branching candidates were registered
7774 */
7775 static
7776 SCIP_RETCODE enforceConstraints(
7777 SCIP* scip, /**< SCIP data structure */
7778 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
7779 SCIP_CONS** conss, /**< constraints to process */
7780 int nconss, /**< number of constraints */
7781 SCIP_SOL* sol, /**< solution to enforce (NULL for the LP solution) */
7782 SCIP_Longint soltag, /**< tag of solution */
7783 SCIP_Bool inenforcement, /**< whether we are in enforcement, and not just separation */
7784 SCIP_Real maxrelconsviol, /**< largest scaled violation among all violated expr-constraints, only used if in enforcement */
7785 SCIP_RESULT* result /**< pointer to store the result of the enforcing call */
7786 )
7787 {
7788 SCIP_CONSHDLRDATA* conshdlrdata;
7789 SCIP_EXPRITER* it;
7790 SCIP_Bool consenforced; /* whether any expression in constraint could be enforced */
7791 int c;
7792
7793 assert(conshdlr != NULL);
7794 assert(conss != NULL || nconss == 0);
7795 assert(result != NULL);
7796
7797 conshdlrdata = SCIPconshdlrGetData(conshdlr);
7798 assert(conshdlrdata != NULL);
7799
7800 /* increase tag to tell whether branching scores in expression belong to this sweep
7801 * and which expressions have already been enforced in this sweep
7802 * (we also want to distinguish sepa rounds, so this need to be here and not in consEnfo)
7803 */
7804 ++(conshdlrdata->enforound);
7805
7806 *result = SCIP_DIDNOTFIND;
7807
7808 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
7809 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, TRUE) );
7810
7811 for( c = 0; c < nconss; ++c )
7812 {
7813 assert(conss != NULL && conss[c] != NULL);
7814
7815 /* skip constraints that are not enabled or deleted */
7816 if( !SCIPconsIsEnabled(conss[c]) || SCIPconsIsDeleted(conss[c]) )
7817 continue;
7818 assert(SCIPconsIsActive(conss[c]));
7819
7820 /* skip constraints that have separation disabled if we are only in separation */
7821 if( !inenforcement && !SCIPconsIsSeparationEnabled(conss[c]) )
7822 continue;
7823
7824 /* skip non-violated constraints */
7825 if( !isConsViolated(scip, conss[c]) )
7826 continue;
7827
7828 ENFOLOG(
7829 {
7830 SCIP_CONSDATA* consdata;
7831 int i;
7832 consdata = SCIPconsGetData(conss[c]);
7833 assert(consdata != NULL);
7834 SCIPinfoMessage(scip, enfologfile, " constraint ");
7835 SCIP_CALL( SCIPprintCons(scip, conss[c], enfologfile) );
7836 SCIPinfoMessage(scip, enfologfile, "\n with viol %g and point\n", getConsAbsViolation(conss[c]));
7837 for( i = 0; i < consdata->nvarexprs; ++i )
7838 {
7839 SCIP_VAR* var;
7840 var = SCIPgetVarExprVar(consdata->varexprs[i]);
7841 SCIPinfoMessage(scip, enfologfile, " %-10s = %15g bounds: [%15g,%15g]\n", SCIPvarGetName(var),
7842 SCIPgetSolVal(scip, sol, var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var));
7843 }
7844 })
7845
7846 SCIP_CALL( enforceConstraint(scip, conshdlr, conss[c], sol, soltag, it, FALSE, inenforcement, result, &consenforced) );
7847
7848 if( *result == SCIP_CUTOFF )
7849 break;
7850
7851 if( !consenforced && inenforcement )
7852 {
7853 SCIP_Real viol;
7854
7855 SCIP_CALL( getConsRelViolation(scip, conss[c], &viol, sol, soltag) );
7856 if( viol > conshdlrdata->weakcutminviolfactor * maxrelconsviol )
7857 {
7858 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " constraint <%s> could not be enforced, try again with weak "\
7859 "cuts allowed\n", SCIPconsGetName(conss[c])); )
7860
7861 SCIP_CALL( enforceConstraint(scip, conshdlr, conss[c], sol, soltag, it, TRUE, inenforcement, result, &consenforced) );
7862
7863 if( consenforced )
7864 ++conshdlrdata->nweaksepa; /* TODO maybe this should not be counted per constraint, but per enforcement round? */
7865
7866 if( *result == SCIP_CUTOFF )
7867 break;
7868 }
7869 }
7870 }
7871
7872 SCIPfreeExpriter(&it);
7873
7874 ENFOLOG( if( enfologfile != NULL ) fflush( enfologfile); )
7875
7876 /* if having branching scores, then propagate them from expressions with children to variable expressions */
7877 if( *result == SCIP_BRANCHED )
7878 {
7879 /* having result set to branched here means only that we have branching candidates, we still need to do the actual
7880 * branching
7881 */
7882 SCIP_CALL( branching(scip, conshdlr, conss, nconss, maxrelconsviol, sol, soltag, result) );
7883
7884 /* branching should either have branched: result == SCIP_BRANCHED,
7885 * or fixed a variable: result == SCIP_REDUCEDDOM,
7886 * or have registered external branching candidates: result == SCIP_INFEASIBLE,
7887 * or have not done anything: result == SCIP_DIDNOTFIND
7888 */
7889 assert(*result == SCIP_BRANCHED || *result == SCIP_REDUCEDDOM || *result == SCIP_INFEASIBLE || *result == SCIP_DIDNOTFIND);
7890 }
7891
7892 ENFOLOG( if( enfologfile != NULL ) fflush( enfologfile); )
7893
7894 return SCIP_OKAY;
7895 }
7896
7897 /** collect (and print (if debugging enfo)) information on violation in expressions
7898 *
7899 * assumes that constraint violations have been computed
7900 */
7901 static
7902 SCIP_RETCODE analyzeViolation(
7903 SCIP* scip, /**< SCIP data structure */
7904 SCIP_CONS** conss, /**< constraints */
7905 int nconss, /**< number of constraints */
7906 SCIP_SOL* sol, /**< solution to separate, or NULL if LP solution should be used */
7907 SCIP_Longint soltag, /**< tag of solution */
7908 SCIP_Real* maxabsconsviol, /**< buffer to store maximal absolute violation of constraints */
7909 SCIP_Real* maxrelconsviol, /**< buffer to store maximal relative violation of constraints */
7910 SCIP_Real* minauxviol, /**< buffer to store minimal (nonzero) violation of auxiliaries */
7911 SCIP_Real* maxauxviol, /**< buffer to store maximal violation of auxiliaries (violation in "extended formulation") */
7912 SCIP_Real* maxvarboundviol /**< buffer to store maximal violation of variable bounds */
7913 )
7914 {
7915 SCIP_CONSDATA* consdata;
7916 SCIP_EXPRITER* it;
7917 SCIP_EXPR* expr;
7918 SCIP_Real v;
7919 int c;
7920
7921 assert(conss != NULL || nconss == 0);
7922 assert(maxabsconsviol != NULL);
7923 assert(maxrelconsviol != NULL);
7924 assert(maxauxviol != NULL);
7925 assert(maxvarboundviol != NULL);
7926
7927 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
7928 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) );
7929
7930 *maxabsconsviol = 0.0;
7931 *maxrelconsviol = 0.0;
7932 *minauxviol = SCIPinfinity(scip);
7933 *maxauxviol = 0.0;
7934 *maxvarboundviol = 0.0;
7935
7936 for( c = 0; c < nconss; ++c )
7937 {
7938 assert(conss != NULL && conss[c] != NULL);
7939
7940 consdata = SCIPconsGetData(conss[c]);
7941 assert(consdata != NULL);
7942
7943 /* skip constraints that are not enabled, deleted, or have separation disabled */
7944 if( !SCIPconsIsEnabled(conss[c]) || SCIPconsIsDeleted(conss[c]) || !SCIPconsIsSeparationEnabled(conss[c]) )
7945 continue;
7946 assert(SCIPconsIsActive(conss[c]));
7947
7948 v = getConsAbsViolation(conss[c]);
7949 *maxabsconsviol = MAX(*maxabsconsviol, v);
7950
7951 /* skip non-violated constraints */
7952 if( !isConsViolated(scip, conss[c]) )
7953 continue;
7954
7955 SCIP_CALL( getConsRelViolation(scip, conss[c], &v, sol, soltag) );
7956 *maxrelconsviol = MAX(*maxrelconsviol, v);
7957
7958 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
7959 {
7960 SCIP_EXPR_OWNERDATA* ownerdata;
7961 SCIP_Real auxvarvalue;
7962 SCIP_Real auxvarlb;
7963 SCIP_Real auxvarub;
7964 SCIP_Bool violunder;
7965 SCIP_Bool violover;
7966 SCIP_Real origviol;
7967 SCIP_Real auxviol;
7968 int e;
7969
7970 ownerdata = SCIPexprGetOwnerData(expr);
7971 assert(ownerdata != NULL);
7972
7973 if( ownerdata->auxvar == NULL )
7974 {
7975 /* check violation of variable bounds of original variable */
7976 if( SCIPisExprVar(scip, expr) )
7977 {
7978 SCIP_VAR* var;
7979 var = SCIPgetVarExprVar(expr);
7980 auxvarvalue = SCIPgetSolVal(scip, sol, var);
7981 auxvarlb = SCIPvarGetLbLocal(var);
7982 auxvarub = SCIPvarGetUbLocal(var);
7983
7984 origviol = 0.0;
7985 if( auxvarlb > auxvarvalue && !SCIPisInfinity(scip, -auxvarlb) )
7986 origviol = auxvarlb - auxvarvalue;
7987 else if( auxvarub < auxvarvalue && !SCIPisInfinity(scip, auxvarub) )
7988 origviol = auxvarvalue - auxvarub;
7989 if( origviol <= 0.0 )
7990 continue;
7991
7992 *maxvarboundviol = MAX(*maxvarboundviol, origviol);
7993
7994 ENFOLOG(
7995 SCIPinfoMessage(scip, enfologfile, "var <%s>[%.15g,%.15g] = %.15g", SCIPvarGetName(var), auxvarlb, auxvarub, auxvarvalue);
7996 if( auxvarlb > auxvarvalue && !SCIPisInfinity(scip, -auxvarlb) )
7997 SCIPinfoMessage(scip, enfologfile, " var >= lb violated by %g", auxvarlb - auxvarvalue);
7998 if( auxvarub < auxvarvalue && !SCIPisInfinity(scip, auxvarub) )
7999 SCIPinfoMessage(scip, enfologfile, " var <= ub violated by %g", auxvarvalue - auxvarub);
8000 SCIPinfoMessage(scip, enfologfile, "\n");
8001 )
8002 }
8003
8004 continue;
8005 }
8006
8007 auxvarvalue = SCIPgetSolVal(scip, sol, ownerdata->auxvar);
8008 auxvarlb = SCIPvarGetLbLocal(ownerdata->auxvar);
8009 auxvarub = SCIPvarGetUbLocal(ownerdata->auxvar);
8010
8011 /* check violation of variable bounds of auxiliary variable */
8012 if( auxvarlb - auxvarvalue > *maxvarboundviol && !SCIPisInfinity(scip, -auxvarlb) )
8013 *maxvarboundviol = auxvarlb - auxvarvalue;
8014 else if( auxvarvalue - auxvarub > *maxvarboundviol && !SCIPisInfinity(scip, auxvarub) )
8015 *maxvarboundviol = auxvarvalue - auxvarub;
8016
8017 origviol = getExprAbsOrigViolation(scip, expr, sol, &violunder, &violover);
8018
8019 ENFOLOG(
8020 if( origviol > 0.0 || auxvarlb > auxvarvalue || auxvarub < auxvarvalue )
8021 {
8022 SCIPinfoMessage(scip, enfologfile, "expr ");
8023 SCIP_CALL( SCIPprintExpr(scip, expr, enfologfile) );
8024 SCIPinfoMessage(scip, enfologfile, " (%p)[%.15g,%.15g] = %.15g\n", (void*)expr, SCIPexprGetActivity(expr).inf, SCIPexprGetActivity(expr).sup, SCIPexprGetEvalValue(expr));
8025
8026 SCIPinfoMessage(scip, enfologfile, " auxvar <%s>[%.15g,%.15g] = %.15g", SCIPvarGetName(ownerdata->auxvar), auxvarlb, auxvarub, auxvarvalue);
8027 if( origviol > 0.0 )
8028 SCIPinfoMessage(scip, enfologfile, " auxvar %s expr violated by %g", violunder ? ">=" : "<=", origviol);
8029 if( auxvarlb > auxvarvalue && !SCIPisInfinity(scip, -auxvarlb) )
8030 SCIPinfoMessage(scip, enfologfile, " auxvar >= auxvar's lb violated by %g", auxvarlb - auxvarvalue);
8031 if( auxvarub < auxvarvalue && !SCIPisInfinity(scip, auxvarub) )
8032 SCIPinfoMessage(scip, enfologfile, " auxvar <= auxvar's ub violated by %g", auxvarvalue - auxvarub);
8033 SCIPinfoMessage(scip, enfologfile, "\n");
8034 }
8035 )
8036
8037 /* no violation w.r.t. the original variables -> skip expression */
8038 if( origviol == 0.0 )
8039 continue;
8040
8041 /* compute aux-violation for each nonlinear handlers */
8042 for( e = 0; e < ownerdata->nenfos; ++e )
8043 {
8044 SCIP_NLHDLR* nlhdlr;
8045
8046 /* eval in auxvars is only defined for nlhdrs that separate; there might not even be auxvars otherwise */
8047 if( (ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPABOTH) == 0 )
8048 continue;
8049
8050 nlhdlr = ownerdata->enfos[e]->nlhdlr;
8051 assert(nlhdlr != NULL);
8052
8053 /* evaluate the expression w.r.t. the nlhdlrs auxiliary variables */
8054 SCIP_CALL( SCIPnlhdlrEvalaux(scip, nlhdlr, expr, ownerdata->enfos[e]->nlhdlrexprdata, &ownerdata->enfos[e]->auxvalue, sol) );
8055
8056 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " nlhdlr <%s> = %.15g", SCIPnlhdlrGetName(nlhdlr), ownerdata->enfos[e]->auxvalue); )
8057
8058 auxviol = getExprAbsAuxViolation(scip, expr, ownerdata->enfos[e]->auxvalue, sol, &violunder, &violover);
8059
8060 if( auxviol > 0.0 )
8061 {
8062 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " auxvar %s nlhdlr-expr violated by %g", violover ? "<=" : ">=", auxviol); )
8063 *maxauxviol = MAX(*maxauxviol, auxviol);
8064 *minauxviol = MIN(*minauxviol, auxviol);
8065 }
8066 ENFOLOG( SCIPinfoMessage(scip, enfologfile, "\n"); )
8067 }
8068 }
8069 }
8070
8071 SCIPfreeExpriter(&it);
8072
8073 return SCIP_OKAY;
8074 } /*lint !e715*/
8075
8076 /** enforcement of constraints called by enfolp and enforelax */
8077 static
8078 SCIP_RETCODE consEnfo(
8079 SCIP* scip, /**< SCIP data structure */
8080 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
8081 SCIP_CONS** conss, /**< constraints to process */
8082 int nconss, /**< number of constraints */
8083 SCIP_SOL* sol, /**< solution to enforce (NULL for the LP solution) */
8084 SCIP_RESULT* result /**< pointer to store the result of the enforcing call */
8085 )
8086 {
8087 SCIP_CONSHDLRDATA* conshdlrdata;
8088 SCIP_Real maxabsconsviol;
8089 SCIP_Real maxrelconsviol;
8090 SCIP_Real minauxviol;
8091 SCIP_Real maxauxviol;
8092 SCIP_Real maxvarboundviol;
8093 SCIP_Longint soltag;
8094 int nnotify;
8095 int c;
8096
8097 conshdlrdata = SCIPconshdlrGetData(conshdlr);
8098 assert(conshdlr != NULL);
8099
8100 soltag = SCIPgetExprNewSoltag(scip);
8101
8102 *result = SCIP_FEASIBLE;
8103 for( c = 0; c < nconss; ++c )
8104 {
8105 SCIP_CALL( computeViolation(scip, conss[c], sol, soltag) );
8106
8107 if( isConsViolated(scip, conss[c]) )
8108 *result = SCIP_INFEASIBLE;
8109 }
8110
8111 if( *result == SCIP_FEASIBLE )
8112 {
8113 ENFOLOG( SCIPinfoMessage(scip, enfologfile, "node %lld: all expr-constraints feasible, skip enforcing\n",
8114 SCIPnodeGetNumber(SCIPgetCurrentNode(scip))); )
8115 return SCIP_OKAY;
8116 }
8117
8118 SCIP_CALL( analyzeViolation(scip, conss, nconss, sol, soltag, &maxabsconsviol, &maxrelconsviol,
8119 &minauxviol, &maxauxviol, &maxvarboundviol) );
8120
8121 ENFOLOG( SCIPinfoMessage(scip, enfologfile, "node %lld: enforcing constraints with max conssviol=%e (rel=%e), "\
8122 "auxviolations in %g..%g, variable bounds violated by at most %g, LP feastol=%e\n",
8123 SCIPnodeGetNumber(SCIPgetCurrentNode(scip)), maxabsconsviol, maxrelconsviol, minauxviol, maxauxviol,
8124 maxvarboundviol, SCIPgetLPFeastol(scip)); )
8125
8126 assert(maxvarboundviol <= SCIPgetLPFeastol(scip));
8127
8128 /* try to propagate */
8129 if( conshdlrdata->propinenforce )
8130 {
8131 SCIP_RESULT propresult;
8132 int nchgbds = 0;
8133
8134 SCIP_CALL( propConss(scip, conshdlr, conss, nconss, TRUE, &propresult, &nchgbds) );
8135
8136 if( propresult == SCIP_CUTOFF || propresult == SCIP_REDUCEDDOM )
8137 {
8138 *result = propresult;
8139 return SCIP_OKAY;
8140 }
8141 }
8142
8143 /* tighten the LP tolerance if violation in variables bounds is larger than aux-violation (max |expr - auxvar| over
8144 * all violated expr/auxvar in violated constraints)
8145 */
8146 if( conshdlrdata->tightenlpfeastol && maxvarboundviol > maxauxviol && SCIPisPositive(scip, SCIPgetLPFeastol(scip)) &&
8147 sol == NULL )
8148 {
8149 SCIPsetLPFeastol(scip, MAX(SCIPepsilon(scip), MIN(maxvarboundviol / 2.0, SCIPgetLPFeastol(scip) / 2.0)));
8150 ++conshdlrdata->ntightenlp;
8151
8152 *result = SCIP_SOLVELP;
8153
8154 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " variable bound violation %g larger than auxiliary violation %g, "\
8155 "reducing LP feastol to %g\n", maxvarboundviol, maxauxviol, SCIPgetLPFeastol(scip)); )
8156
8157 return SCIP_OKAY;
8158 }
8159
8160 /* tighten the LP tolerance if violation in auxiliaries is below LP feastol, as we could have problems to find a cut
8161 * with violation above LP tolerance (especially when auxviolation is below 10*eps = ROWPREP_SCALEUP_VIOLNONZERO in misc_rowprep.c)
8162 */
8163 if( conshdlrdata->tightenlpfeastol && maxauxviol < SCIPgetLPFeastol(scip) && SCIPisPositive(scip, SCIPgetLPFeastol(scip)) && sol == NULL )
8164 {
8165 SCIPsetLPFeastol(scip, MAX(SCIPepsilon(scip), maxauxviol/2.0));
8166 ++conshdlrdata->ntightenlp;
8167
8168 *result = SCIP_SOLVELP;
8169
8170 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " auxiliary violation %g below LP feastol, reducing LP feastol to %g\n", maxauxviol, SCIPgetLPFeastol(scip)); )
8171
8172 return SCIP_OKAY;
8173 }
8174
8175 SCIP_CALL( enforceConstraints(scip, conshdlr, conss, nconss, sol, soltag, TRUE, maxrelconsviol, result) );
8176
8177 if( *result == SCIP_CUTOFF || *result == SCIP_SEPARATED || *result == SCIP_REDUCEDDOM || *result == SCIP_BRANCHED ||
8178 *result == SCIP_INFEASIBLE )
8179 return SCIP_OKAY;
8180
8181 assert(*result == SCIP_DIDNOTFIND);
8182
8183 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " could not enforce violation %g in regular ways, LP feastol=%g, "\
8184 "becoming desperate now...\n", maxabsconsviol, SCIPgetLPFeastol(scip)); )
8185
8186 if( conshdlrdata->tightenlpfeastol && SCIPisPositive(scip, maxvarboundviol) && SCIPisPositive(scip, SCIPgetLPFeastol(scip)) && sol == NULL )
8187 {
8188 SCIPsetLPFeastol(scip, MAX(SCIPepsilon(scip), MIN(maxvarboundviol / 2.0, SCIPgetLPFeastol(scip) / 2.0)));
8189 ++conshdlrdata->ntightenlp;
8190
8191 *result = SCIP_SOLVELP;
8192
8193 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " variable bounds are violated by more than eps, reduced LP "\
8194 "feasibility tolerance to %g\n", SCIPgetLPFeastol(scip)); )
8195
8196 return SCIP_OKAY;
8197 }
8198
8199 if( conshdlrdata->tightenlpfeastol && SCIPisPositive(scip, maxauxviol) && SCIPisPositive(scip,
8200 SCIPgetLPFeastol(scip)) && sol == NULL )
8201 {
8202 /* try whether tighten the LP feasibility tolerance could help
8203 * maybe it is just some cut that hasn't been taken into account sufficiently
8204 * in the next enforcement round, we would then also allow even weaker cuts, as we want a minimal cut violation of LP's feastol
8205 * unfortunately, we do not know the current LP solution primal infeasibility, so sometimes this just repeats without effect
8206 * until the LP feastol reaches epsilon
8207 * (this is similar to the "tighten the LP tolerance if violation in auxiliaries is below LP feastol..." case above, but applies
8208 * when maxauxviol is above LP feastol)
8209 */
8210 SCIPsetLPFeastol(scip, MAX(SCIPepsilon(scip), MIN(maxauxviol / 2.0, SCIPgetLPFeastol(scip) / 10.0)));
8211 ++conshdlrdata->ndesperatetightenlp;
8212
8213 *result = SCIP_SOLVELP;
8214
8215 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " reduced LP feasibility tolerance to %g and hope\n", SCIPgetLPFeastol(scip)); )
8216
8217 return SCIP_OKAY;
8218 }
8219
8220 /* try to propagate, if not tried above TODO(?) allow to disable this as well */
8221 if( !conshdlrdata->propinenforce )
8222 {
8223 SCIP_RESULT propresult;
8224 int nchgbds = 0;
8225
8226 SCIP_CALL( propConss(scip, conshdlr, conss, nconss, TRUE, &propresult, &nchgbds) );
8227
8228 if( propresult == SCIP_CUTOFF || propresult == SCIP_REDUCEDDOM )
8229 {
8230 *result = propresult;
8231 return SCIP_OKAY;
8232 }
8233 }
8234
8235 /* could not find branching candidates even when looking at minimal violated (>eps) expressions
8236 * now look if we find any unfixed variable that we could still branch on
8237 */
8238 SCIP_CALL( registerBranchingCandidatesAllUnfixed(scip, conshdlr, conss, nconss, &nnotify) );
8239
8240 if( nnotify > 0 )
8241 {
8242 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " registered %d unfixed variables as branching candidates\n", nnotify); )
8243 ++conshdlrdata->ndesperatebranch;
8244
8245 *result = SCIP_INFEASIBLE; /* enforceConstraints may have changed it to SCIP_DIDNOTFIND */
8246
8247 return SCIP_OKAY;
8248 }
8249
8250 /* if everything is fixed in violated constraints, then let's cut off the node
8251 * - bound tightening with all vars fixed should prove cutoff, but interval arithmetic overestimates and so the
8252 * result may not be conclusive (when constraint violations are small)
8253 * - if tightenlpfeastol=FALSE, then the LP solution that we try to enforce here may just not be within bounds
8254 * sufficiently (see st_e40)
8255 * - but if the LP solution is really within bounds and since variables are fixed, cutting off the node is actually
8256 * not "desperate", but a pretty obvious thing to do
8257 */
8258 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " enforcement with max. violation %g failed; cutting off node\n", maxabsconsviol); )
8259 *result = SCIP_CUTOFF;
8260
8261 /* it's only "desperate" if the LP solution does not coincide with variable fixings (should we use something tighter than epsilon here?) */
8262 if( !SCIPisZero(scip, maxvarboundviol) )
8263 ++conshdlrdata->ndesperatecutoff;
8264
8265 return SCIP_OKAY;
8266 }
8267
8268 /** separation for all violated constraints to be used by SEPA callbacks */
8269 static
8270 SCIP_RETCODE consSepa(
8271 SCIP* scip, /**< SCIP data structure */
8272 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
8273 SCIP_CONS** conss, /**< constraints to process */
8274 int nconss, /**< number of constraints */
8275 SCIP_SOL* sol, /**< solution to enforce (NULL for the LP solution) */
8276 SCIP_RESULT* result /**< pointer to store the result of the enforcing call */
8277 )
8278 {
8279 SCIP_Longint soltag;
8280 SCIP_Bool haveviol = FALSE;
8281 int c;
8282
8283 *result = SCIP_DIDNOTFIND;
8284
8285 soltag = SCIPgetExprNewSoltag(scip);
8286
8287 /* compute violations */
8288 for( c = 0; c < nconss; ++c )
8289 {
8290 assert(conss[c] != NULL);
8291
8292 /* skip constraints that are not enabled, deleted, or have separation disabled */
8293 if( !SCIPconsIsEnabled(conss[c]) || SCIPconsIsDeleted(conss[c]) || !SCIPconsIsSeparationEnabled(conss[c]) )
8294 continue;
8295 assert(SCIPconsIsActive(conss[c]));
8296
8297 SCIP_CALL( computeViolation(scip, conss[c], sol, soltag) );
8298
8299 if( isConsViolated(scip, conss[c]) )
8300 haveviol = TRUE;
8301 }
8302
8303 /* if none of our constraints are violated, don't attempt separation */
8304 if( !haveviol )
8305 {
8306 ENFOLOG( SCIPinfoMessage(scip, enfologfile, "node %lld: skip separation of non-violated constraints\n", SCIPnodeGetNumber(SCIPgetCurrentNode(scip))); )
8307 return SCIP_OKAY;
8308 }
8309
8310 ENFOLOG( SCIPinfoMessage(scip, enfologfile, "node %lld: separation\n", SCIPnodeGetNumber(SCIPgetCurrentNode(scip))); )
8311
8312 /* call separation */
8313 SCIP_CALL( enforceConstraints(scip, conshdlr, conss, nconss, sol, soltag, FALSE, SCIP_INVALID, result) );
8314
8315 return SCIP_OKAY;
8316 }
8317
8318 /** hash key retrieval function for bilinear term entries */
8319 static
8320 SCIP_DECL_HASHGETKEY(bilinearTermsGetHashkey)
8321 { /*lint --e{715}*/
8322 SCIP_CONSHDLRDATA* conshdlrdata;
8323 int idx;
8324
8325 conshdlrdata = (SCIP_CONSHDLRDATA*)userptr;
8326 assert(conshdlrdata != NULL);
8327
8328 idx = ((int)(size_t)elem) - 1;
8329 assert(idx >= 0 && idx < conshdlrdata->nbilinterms);
8330
8331 return (void*)&conshdlrdata->bilinterms[idx];
8332 }
8333
8334 /** returns TRUE iff the bilinear term entries are equal */
8335 static
8336 SCIP_DECL_HASHKEYEQ(bilinearTermsIsHashkeyEq)
8337 { /*lint --e{715}*/
8338 SCIP_CONSNONLINEAR_BILINTERM* entry1;
8339 SCIP_CONSNONLINEAR_BILINTERM* entry2;
8340
8341 /* get corresponding entries */
8342 entry1 = (SCIP_CONSNONLINEAR_BILINTERM*)key1;
8343 entry2 = (SCIP_CONSNONLINEAR_BILINTERM*)key2;
8344 assert(entry1->x != NULL && entry1->y != NULL);
8345 assert(entry2->x != NULL && entry2->y != NULL);
8346 assert(SCIPvarCompare(entry1->x, entry1->y) < 1);
8347 assert(SCIPvarCompare(entry2->x, entry2->y) < 1);
8348
8349 return entry1->x == entry2->x && entry1->y == entry2->y;
8350 }
8351
8352 /** returns the hash value of the key */
8353 static
8354 SCIP_DECL_HASHKEYVAL(bilinearTermsGetHashkeyVal)
8355 { /*lint --e{715}*/
8356 SCIP_CONSNONLINEAR_BILINTERM* entry;
8357
8358 entry = (SCIP_CONSNONLINEAR_BILINTERM*)key;
8359 assert(entry->x != NULL && entry->y != NULL);
8360 assert(SCIPvarCompare(entry->x, entry->y) < 1);
8361
8362 return SCIPhashTwo(SCIPvarGetIndex(entry->x), SCIPvarGetIndex(entry->y));
8363 }
8364
8365 /** compare two auxiliary expressions
8366 *
8367 * Compares auxiliary variables, followed by coefficients, and then constants.
8368 */
8369 static
8370 SCIP_DECL_SORTPTRCOMP(auxexprComp)
8371 {
8372 SCIP_CONSNONLINEAR_AUXEXPR* auxexpr1 = (SCIP_CONSNONLINEAR_AUXEXPR*)elem1;
8373 SCIP_CONSNONLINEAR_AUXEXPR* auxexpr2 = (SCIP_CONSNONLINEAR_AUXEXPR*)elem2;
8374 int compvars;
8375 int i;
8376
8377 /* compare the auxiliary variables */
8378 compvars = SCIPvarCompare(auxexpr1->auxvar, auxexpr2->auxvar); /* TODO can one of these be NULL? */
8379
8380 if( compvars != 0 )
8381 return compvars;
8382
8383 /* compare the coefficients and constants */
8384 for( i = 0; i < 3; ++i )
8385 {
8386 if( auxexpr1->coefs[i] != auxexpr2->coefs[i] )
8387 return auxexpr1->coefs[i] < auxexpr2->coefs[i] ? -1 : 1;
8388 }
8389
8390 return auxexpr1->cst < auxexpr2->cst ? -1 : auxexpr1->cst == auxexpr2->cst ? 0 : 1;
8391 }
8392
8393 /* add an auxiliary expression to a bilinear term */
8394 static
8395 SCIP_RETCODE bilinTermAddAuxExpr(
8396 SCIP* scip, /**< SCIP data structure */
8397 SCIP_CONSHDLRDATA* conshdlrdata, /**< nonlinear constraint handler data */
8398 SCIP_CONSNONLINEAR_BILINTERM* term, /**< bilinear term */
8399 SCIP_CONSNONLINEAR_AUXEXPR* auxexpr, /**< auxiliary expression to add */
8400 SCIP_Bool* added /**< pointer to store whether auxexpr has been added */
8401 )
8402 {
8403 SCIP_Bool found;
8404 int pos;
8405 int i;
8406
8407 *added = FALSE;
8408
8409 /* check if auxexpr has already been added to term */
8410 if( term->nauxexprs == 0 )
8411 {
8412 found = FALSE;
8413 pos = 0;
8414 }
8415 else
8416 {
8417 found = SCIPsortedvecFindPtr((void**)term->aux.exprs, auxexprComp, auxexpr, term->nauxexprs, &pos);
8418 }
8419
8420 if( !found )
8421 {
8422 if( term->nauxexprs >= conshdlrdata->bilinmaxnauxexprs )
8423 return SCIP_OKAY;
8424
8425 SCIP_CALL( SCIPensureBlockMemoryArray(scip, &term->aux.exprs, &term->auxexprssize, term->nauxexprs + 1) );
8426 assert(term->auxexprssize >= term->nauxexprs + 1);
8427
8428 /* insert expression at the correct position */
8429 for( i = term->nauxexprs; i > pos; --i )
8430 {
8431 term->aux.exprs[i] = term->aux.exprs[i-1];
8432 }
8433 term->aux.exprs[pos] = auxexpr;
8434 ++(term->nauxexprs);
8435 *added = TRUE;
8436 }
8437 else
8438 {
8439 term->aux.exprs[pos]->underestimate |= auxexpr->underestimate;
8440 term->aux.exprs[pos]->overestimate |= auxexpr->overestimate;
8441 }
8442
8443 return SCIP_OKAY;
8444 }
8445
8446 /** iterates through all expressions of all nonlinear constraints and adds the corresponding bilinear terms to the hash table */
8447 static
8448 SCIP_RETCODE bilinearTermsInsertAll(
8449 SCIP* scip, /**< SCIP data structure */
8450 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
8451 SCIP_CONS** conss, /**< nonlinear constraints */
8452 int nconss /**< total number of nonlinear constraints */
8453 )
8454 {
8455 SCIP_CONSHDLRDATA* conshdlrdata;
8456 SCIP_EXPRITER* it;
8457 int c;
8458
8459 assert(conss != NULL || nconss == 0);
8460
8461 if( nconss == 0 )
8462 return SCIP_OKAY;
8463
8464 conshdlrdata = SCIPconshdlrGetData(conshdlr);
8465 assert(conshdlrdata != NULL);
8466
8467 /* check whether the bilinear terms have been stored already */
8468 if( conshdlrdata->bilinterms != NULL )
8469 return SCIP_OKAY;
8470
8471 /* create and initialize iterator */
8472 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
8473 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) );
8474 SCIPexpriterSetStagesDFS(it, SCIP_EXPRITER_ENTEREXPR);
8475
8476 /* iterate through all constraints */
8477 for( c = 0; c < nconss; ++c )
8478 {
8479 SCIP_CONSDATA* consdata;
8480 SCIP_EXPR* expr;
8481
8482 assert(conss != NULL && conss[c] != NULL);
8483 consdata = SCIPconsGetData(conss[c]);
8484 assert(consdata != NULL);
8485
8486 /* iterate through all expressions */
8487 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
8488 {
8489 SCIP_EXPR** children = SCIPexprGetChildren(expr);
8490 SCIP_VAR* x = NULL;
8491 SCIP_VAR* y = NULL;
8492
8493 /* check whether the expression is of the form f(..)^2 */
8494 if( SCIPisExprPower(scip, expr) && SCIPgetExponentExprPow(expr) == 2.0 )
8495 {
8496 x = SCIPgetExprAuxVarNonlinear(children[0]);
8497 y = x;
8498 }
8499 /* check whether the expression is of the form f(..) * g(..) */
8500 else if( SCIPisExprProduct(scip, expr) && SCIPexprGetNChildren(expr) == 2 )
8501 {
8502 x = SCIPgetExprAuxVarNonlinear(children[0]);
8503 y = SCIPgetExprAuxVarNonlinear(children[1]);
8504 }
8505
8506 /* add variables to the hash table */
8507 if( x != NULL && y != NULL )
8508 {
8509 SCIP_CALL( SCIPinsertBilinearTermExistingNonlinear(scip, conshdlr, x, y, SCIPgetExprAuxVarNonlinear(expr),
8510 SCIPgetExprNLocksPosNonlinear(expr), SCIPgetExprNLocksNegNonlinear(expr)) );
8511 }
8512 }
8513 }
8514
8515 /* release iterator */
8516 SCIPfreeExpriter(&it);
8517
8518 return SCIP_OKAY;
8519 }
8520
8521 /** store x, y and the locks in a new bilinear term */
8522 static
8523 SCIP_RETCODE bilinearTermsInsertEntry(
8524 SCIP* scip, /**< SCIP data structure */
8525 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */
8526 SCIP_VAR* x, /**< the first variable */
8527 SCIP_VAR* y, /**< the second variable */
8528 int nlockspos, /**< number of positive locks of the bilinear term */
8529 int nlocksneg, /**< number of negative locks of the bilinear term */
8530 int* idx, /**< pointer to store the position of the term in bilinterms array */
8531 SCIP_Bool existing /**< whether the term exists explicitly in the problem */
8532 )
8533 {
8534 SCIP_CONSHDLRDATA* conshdlrdata;
8535 SCIP_CONSNONLINEAR_BILINTERM* term;
8536
8537 assert(conshdlr != NULL);
8538 assert(x != NULL);
8539 assert(y != NULL);
8540 assert(nlockspos >= 0);
8541 assert(nlocksneg >= 0);
8542
8543 conshdlrdata = SCIPconshdlrGetData(conshdlr);
8544 assert(conshdlrdata != NULL);
8545
8546 /* ensure that x.index <= y.index */
8547 if( SCIPvarCompare(x, y) == 1 )
8548 {
8549 SCIPswapPointers((void**)&x, (void**)&y);
8550 }
8551 assert(SCIPvarCompare(x, y) < 1);
8552
8553 *idx = SCIPgetBilinTermIdxNonlinear(conshdlr, x, y);
8554
8555 /* update or create the term */
8556 if( *idx >= 0 )
8557 { /* the term has already been added */
8558 assert(conshdlrdata->bilinterms[*idx].x == x);
8559 assert(conshdlrdata->bilinterms[*idx].y == y);
8560
8561 /* get term and add locks */
8562 term = &conshdlrdata->bilinterms[*idx];
8563 assert(existing <= term->existing); /* implicit terms are added after existing ones */
8564 term->nlockspos += nlockspos;
8565 term->nlocksneg += nlocksneg;
8566 }
8567 else
8568 { /* this is the first time we encounter this product */
8569 /* ensure size of bilinterms array */
8570 SCIP_CALL( SCIPensureBlockMemoryArray(scip, &conshdlrdata->bilinterms, &conshdlrdata->bilintermssize, conshdlrdata->nbilinterms + 1) );
8571
8572 *idx = conshdlrdata->nbilinterms;
8573
8574 /* get term and set values in the created bilinear term */
8575 term = &conshdlrdata->bilinterms[*idx];
8576 assert(term != NULL);
8577 term->x = x;
8578 term->y = y;
8579 term->nauxexprs = 0;
8580 term->auxexprssize = 0;
8581 term->nlockspos = nlockspos;
8582 term->nlocksneg = nlocksneg;
8583 term->existing = existing;
8584 if( existing )
8585 term->aux.var = NULL;
8586 else
8587 term->aux.exprs = NULL;
8588
8589 /* increase the total number of bilinear terms */
8590 ++(conshdlrdata->nbilinterms);
8591
8592 /* save to the hashtable */
8593 if( conshdlrdata->bilinhashtable == NULL )
8594 {
8595 SCIP_CALL( SCIPhashtableCreate(&conshdlrdata->bilinhashtable, SCIPblkmem(scip), conshdlrdata->nbilinterms,
8596 bilinearTermsGetHashkey, bilinearTermsIsHashkeyEq, bilinearTermsGetHashkeyVal,
8597 (void*)conshdlrdata) );
8598 }
8599 assert(conshdlrdata->bilinhashtable != NULL);
8600
8601 /* insert the index of the bilinear term into the hash table; note that the index of the i-th element is (i+1)
8602 * because zero can not be inserted into hash table
8603 */
8604 SCIP_CALL( SCIPhashtableInsert(conshdlrdata->bilinhashtable, (void*)(size_t)(*idx + 1)) ); /*lint !e571 !e776*/
8605
8606 /* capture product variables */
8607 SCIP_CALL( SCIPcaptureVar(scip, x) );
8608 SCIP_CALL( SCIPcaptureVar(scip, y) );
8609 }
8610
8611 return SCIP_OKAY;
8612 }
8613
8614 /** frees array of bilinear terms and hash table */
8615 static
8616 SCIP_RETCODE bilinearTermsFree(
8617 SCIP* scip, /**< SCIP data structure */
8618 SCIP_CONSHDLRDATA* conshdlrdata /**< constraint handler data */
8619 )
8620 {
8621 int i;
8622 int j;
8623
8624 assert(conshdlrdata != NULL);
8625
8626 /* check whether bilinear terms have been stored */
8627 if( conshdlrdata->bilinterms == NULL )
8628 {
8629 assert(conshdlrdata->bilinterms == NULL);
8630 assert(conshdlrdata->nbilinterms == 0);
8631 assert(conshdlrdata->bilintermssize == 0);
8632
8633 return SCIP_OKAY;
8634 }
8635
8636 /* release variables */
8637 for( i = 0; i < conshdlrdata->nbilinterms; ++i )
8638 {
8639 SCIP_CALL( SCIPreleaseVar(scip, &conshdlrdata->bilinterms[i].y) );
8640 SCIP_CALL( SCIPreleaseVar(scip, &conshdlrdata->bilinterms[i].x) );
8641
8642 for( j = 0; j < conshdlrdata->bilinterms[i].nauxexprs; ++j )
8643 {
8644 if( conshdlrdata->bilinterms[i].aux.exprs[j]->auxvar != NULL )
8645 {
8646 SCIP_CALL( SCIPreleaseVar(scip, &conshdlrdata->bilinterms[i].aux.exprs[j]->auxvar) );
8647 }
8648 SCIPfreeBlockMemory(scip, &(conshdlrdata->bilinterms[i].aux.exprs[j]));
8649 }
8650
8651 if( conshdlrdata->bilinterms[i].nauxexprs > 0 )
8652 {
8653 SCIPfreeBlockMemoryArray(scip, &(conshdlrdata->bilinterms[i].aux.exprs), conshdlrdata->bilinterms[i].auxexprssize);
8654 continue;
8655 }
8656
8657 /* the rest is for simple terms with a single auxvar */
8658
8659 /* it might be that there is a bilinear term without a corresponding auxiliary variable */
8660 if( conshdlrdata->bilinterms[i].aux.var != NULL )
8661 {
8662 SCIP_CALL( SCIPreleaseVar(scip, &conshdlrdata->bilinterms[i].aux.var) );
8663 }
8664 }
8665
8666 /* free hash table */
8667 if( conshdlrdata->bilinhashtable != NULL )
8668 {
8669 SCIPhashtableFree(&conshdlrdata->bilinhashtable);
8670 }
8671
8672 /* free bilinterms array; reset counters */
8673 SCIPfreeBlockMemoryArrayNull(scip, &conshdlrdata->bilinterms, conshdlrdata->bilintermssize);
8674 conshdlrdata->nbilinterms = 0;
8675 conshdlrdata->bilintermssize = 0;
8676
8677 return SCIP_OKAY;
8678 }
8679
8680 /*
8681 * vertex polyhedral separation
8682 */
8683
8684 /** builds LP used to compute facets of the convex envelope of vertex-polyhedral functions */
8685 static
8686 SCIP_RETCODE buildVertexPolyhedralSeparationLP(
8687 SCIP* scip, /**< SCIP data structure */
8688 int nvars, /**< number of (unfixed) variables in vertex-polyhedral functions */
8689 SCIP_LPI** lp /**< pointer to store created LP */
8690 )
8691 {
8692 SCIP_Real* obj;
8693 SCIP_Real* lb;
8694 SCIP_Real* ub;
8695 SCIP_Real* val;
8696 int* beg;
8697 int* ind;
8698 unsigned int nnonz;
8699 unsigned int ncols;
8700 unsigned int nrows;
8701 unsigned int i;
8702 unsigned int k;
8703
8704 assert(scip != NULL);
8705 assert(lp != NULL);
8706 assert(nvars > 0);
8707 assert(nvars <= SCIP_MAXVERTEXPOLYDIM);
8708
8709 SCIPdebugMsg(scip, "Building LP for computing facets of convex envelope of vertex-polyhedral function\n");
8710
8711 /* create lpi to store the LP */
8712 SCIP_CALL( SCIPlpiCreate(lp, SCIPgetMessagehdlr(scip), "facet finding LP", SCIP_OBJSEN_MINIMIZE) );
8713
8714 nrows = (unsigned int)nvars + 1;
8715 ncols = POWEROFTWO((unsigned int)nvars);
8716 nnonz = (ncols * (nrows + 1)) / 2;
8717
8718 /* allocate necessary memory; set obj, lb, and ub to zero */
8719 SCIP_CALL( SCIPallocClearBufferArray(scip, &obj, ncols) );
8720 SCIP_CALL( SCIPallocClearBufferArray(scip, &lb, ncols) );
8721 SCIP_CALL( SCIPallocBufferArray(scip, &ub, ncols) );
8722 SCIP_CALL( SCIPallocBufferArray(scip, &beg, ncols) );
8723 SCIP_CALL( SCIPallocBufferArray(scip, &val, nnonz) );
8724 SCIP_CALL( SCIPallocBufferArray(scip, &ind, nnonz) );
8725
8726 /* calculate nonzero entries in the LP */
8727 for( i = 0, k = 0; i < ncols; ++i )
8728 {
8729 int row;
8730 unsigned int a;
8731
8732 /* an upper bound of 1.0 is implied by the last row, but I presume that LP solvers prefer unbounded variables */
8733 ub[i] = SCIPlpiInfinity(*lp);
8734
8735 SCIPdebugMsg(scip, "col %u starts at position %u\n", i, k);
8736 beg[i] = (int)k;
8737 row = 0;
8738
8739 /* iterate through the bit representation of i */
8740 a = 1;
8741 while( a <= i )
8742 {
8743 if( (a & i) != 0 )
8744 {
8745 val[k] = 1.0;
8746 ind[k] = row;
8747
8748 SCIPdebugMsg(scip, " val[%d][%u] = 1 (position %u)\n", row, i, k);
8749
8750 ++k;
8751 }
8752
8753 a <<= 1;
8754 ++row;
8755 assert(0 <= row && row <= SCIP_MAXVERTEXPOLYDIM);
8756 assert(POWEROFTWO(row) == a);
8757 }
8758
8759 /* put 1 as a coefficient for sum_{i} \lambda_i = 1 row (last row) */
8760 val[k] = 1.0;
8761 ind[k] = (int)nrows - 1;
8762 ++k;
8763 SCIPdebugMsg(scip, " val[%u][%u] = 1 (position %u)\n", nrows - 1, i, k);
8764 }
8765 assert(k == nnonz);
8766
8767 /* load all data into LP interface
8768 * we can assume nrows (=nvars+1) <= ncols (=2^nvars), so we can pass lb as dummy lhs and rhs
8769 */
8770 assert(nrows <= ncols);
8771 SCIP_CALL( SCIPlpiLoadColLP(*lp, SCIP_OBJSEN_MINIMIZE,
8772 (int)ncols, obj, lb, ub, NULL,
8773 (int)nrows, lb, lb, NULL,
8774 (int)nnonz, beg, ind, val) );
8775
8776 /* for the last row, we can set the rhs to 1.0 already */
8777 ind[0] = (int)nrows - 1;
8778 val[0] = 1.0;
8779 SCIP_CALL( SCIPlpiChgSides(*lp, 1, ind, val, val) );
8780
8781 /* free allocated memory */
8782 SCIPfreeBufferArray(scip, &ind);
8783 SCIPfreeBufferArray(scip, &val);
8784 SCIPfreeBufferArray(scip, &beg);
8785 SCIPfreeBufferArray(scip, &ub);
8786 SCIPfreeBufferArray(scip, &lb);
8787 SCIPfreeBufferArray(scip, &obj);
8788
8789 return SCIP_OKAY;
8790 }
8791
8792 /** the given facet might not be a valid under(over)estimator, because of numerics and bad fixings; we compute \f$
8793 * \max_{v \in V} f(v) - (\alpha v + \beta) \f$ (\f$\max_{v \in V} \alpha v + \beta - f(v) \f$) where \f$ V \f$ is the
8794 * set of vertices of the domain
8795 */
8796 static
8797 SCIP_Real computeVertexPolyhedralMaxFacetError(
8798 SCIP* scip, /**< SCIP data structure */
8799 SCIP_Bool overestimate, /**< whether we check for an over or underestimator */
8800 SCIP_Real* funvals, /**< array containing the evaluation of the function at all corners, length: 2^nvars */
8801 SCIP_Real* box, /**< box for which facet was computed, length: 2*nallvars */
8802 int nallvars, /**< number of all variables */
8803 int nvars, /**< number of unfixed variables */
8804 int* nonfixedpos, /**< indices of unfixed variables, length: nvars */
8805 SCIP_Real* facetcoefs, /**< current facet candidate's coefficients, length: nallvars */
8806 SCIP_Real facetconstant /**< current facet candidate's constant, length: nallvars */
8807 )
8808 {
8809 SCIP_Real maxerror;
8810 SCIP_Real facetval;
8811 SCIP_Real funval;
8812 SCIP_Real error;
8813 unsigned int i;
8814 unsigned int ncorners;
8815 unsigned int prev;
8816
8817 assert(scip != NULL);
8818 assert(funvals != NULL);
8819 assert(box != NULL);
8820 assert(nonfixedpos != NULL);
8821 assert(facetcoefs != NULL);
8822
8823 ncorners = POWEROFTWO(nvars);
8824 maxerror = 0.0;
8825
8826 /* check the origin (all variables at lower bound) */
8827 facetval = facetconstant;
8828 for( i = 0; i < (unsigned int) nallvars; ++i )
8829 facetval += facetcoefs[i] * box[2*i];
8830
8831 /* compute largest/smallest possible value of function, depending on whether we are over/under-estimating */
8832 funval = funvals[0];
8833 if( overestimate )
8834 error = funval - facetval;
8835 else
8836 error = facetval - funval;
8837
8838 /* update maximum error */
8839 maxerror = MAX(error, maxerror);
8840
8841 prev = 0;
8842 for( i = 1; i < ncorners; ++i )
8843 {
8844 unsigned int gray;
8845 unsigned int diff;
8846 unsigned int pos;
8847 int origpos;
8848
8849 gray = i ^ (i >> 1);
8850 diff = gray ^ prev;
8851
8852 /* compute position of unique 1 of diff */
8853 pos = 0;
8854 while( (diff >>= 1) != 0 )
8855 ++pos;
8856 assert(pos < (unsigned int)nvars);
8857
8858 origpos = nonfixedpos[pos];
8859
8860 if( gray > prev )
8861 facetval += facetcoefs[origpos] * (box[2*origpos+1] - box[2*origpos]);
8862 else
8863 facetval -= facetcoefs[origpos] * (box[2*origpos+1] - box[2*origpos]);
8864
8865 /* compute largest/smallest possible value of function, depending on whether we are over/under-estimating */
8866 funval = funvals[gray];
8867 if( overestimate )
8868 error = funval - facetval;
8869 else
8870 error = facetval - funval;
8871
8872 /* update maximum error */
8873 maxerror = MAX(error, maxerror);
8874
8875 prev = gray;
8876 }
8877
8878 SCIPdebugMsg(scip, "maximum error of facet: %2.8e\n", maxerror);
8879
8880 return maxerror;
8881 }
8882
8883 /** computes a facet of the convex or concave envelope of a vertex polyhedral function by solving an LP */ /*lint -e{715}*/
8884 static
8885 SCIP_RETCODE computeVertexPolyhedralFacetLP(
8886 SCIP* scip, /**< SCIP data structure */
8887 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */
8888 SCIP_Bool overestimate, /**< whether to compute facet of concave (TRUE) or convex (FALSE) envelope */
8889 SCIP_Real* xstar, /**< point to be separated */
8890 SCIP_Real* box, /**< box where to compute facet: should be lb_1, ub_1, lb_2, ub_2... */
8891 int nallvars, /**< half of the length of box */
8892 int* nonfixedpos, /**< indices of nonfixed variables */
8893 SCIP_Real* funvals, /**< values of function in all corner points (w.r.t. nonfixed variables) */
8894 int nvars, /**< number of nonfixed variables */
8895 SCIP_Real targetvalue, /**< target value: no need to compute facet if value in xstar would be worse than this value */
8896 SCIP_Bool* success, /**< buffer to store whether a facet could be computed successfully */
8897 SCIP_Real* facetcoefs, /**< buffer to store coefficients of facet defining inequality; must be an zero'ed array of length at least nallvars */
8898 SCIP_Real* facetconstant /**< buffer to store constant part of facet defining inequality */
8899 )
8900 { /*lint --e{715}*/
8901 SCIP_CONSHDLRDATA* conshdlrdata;
8902 SCIP_LPI* lp;
8903 SCIP_Real* aux; /* used to transform x^* and then to store LP solution */
8904 int* inds;
8905 int ncols;
8906 int nrows;
8907 int i;
8908 SCIP_Real facetvalue;
8909 SCIP_Real mindomwidth;
8910 SCIP_RETCODE lpsolveretcode;
8911
8912 assert(scip != NULL);
8913 assert(conshdlr != NULL);
8914 assert(xstar != NULL);
8915 assert(box != NULL);
8916 assert(nonfixedpos != NULL);
8917 assert(funvals != NULL);
8918 assert(nvars >= 0);
8919 assert(nvars <= SCIP_MAXVERTEXPOLYDIM);
8920 assert(success != NULL);
8921 assert(facetcoefs != NULL);
8922 assert(facetconstant != NULL);
8923
8924 *success = FALSE;
8925
8926 conshdlrdata = SCIPconshdlrGetData(conshdlr);
8927 assert(conshdlrdata != NULL);
8928
8929 if( conshdlrdata->vp_randnumgen == NULL && conshdlrdata->vp_maxperturb > 0.0 )
8930 {
8931 SCIP_CALL( SCIPcreateRandom(scip, &conshdlrdata->vp_randnumgen, VERTEXPOLY_RANDNUMINITSEED, TRUE) );
8932 }
8933
8934 /* construct an LP for this size, if not having one already */
8935 if( conshdlrdata->vp_lp[nvars] == NULL )
8936 {
8937 SCIP_CALL( buildVertexPolyhedralSeparationLP(scip, nvars, &conshdlrdata->vp_lp[nvars]) );
8938 }
8939 lp = conshdlrdata->vp_lp[nvars];
8940 assert(lp != NULL);
8941
8942 /* get number of cols and rows of separation lp */
8943 SCIP_CALL( SCIPlpiGetNCols(lp, &ncols) );
8944 SCIP_CALL( SCIPlpiGetNRows(lp, &nrows) );
8945
8946 /* number of columns should equal the number of corners = 2^nvars */
8947 assert(ncols == (int)POWEROFTWO(nvars));
8948
8949 /* allocate necessary memory */
8950 SCIP_CALL( SCIPallocBufferArray(scip, &aux, nrows) );
8951 SCIP_CALL( SCIPallocBufferArray(scip, &inds, ncols) );
8952
8953 /*
8954 * set up the described LP on the transformed space
8955 */
8956
8957 for( i = 0; i < ncols; ++i )
8958 inds[i] = i;
8959
8960 /* compute T^-1(x^*), i.e. T^-1(x^*)_i = (x^*_i - lb_i)/(ub_i - lb_i) */
8961 mindomwidth = 2*SCIPinfinity(scip);
8962 for( i = 0; i < nrows-1; ++i )
8963 {
8964 SCIP_Real solval;
8965 SCIP_Real lb;
8966 SCIP_Real ub;
8967 int varpos;
8968
8969 assert(i < nvars);
8970
8971 varpos = nonfixedpos[i];
8972 lb = box[2 * varpos];
8973 ub = box[2 * varpos + 1];
8974 solval = xstar[varpos];
8975
8976 if( ub - lb < mindomwidth )
8977 mindomwidth = ub - lb;
8978
8979 /* explicitly handle solution which violate bounds of variables (this can happen because of tolerances) */
8980 if( solval <= lb )
8981 aux[i] = 0.0;
8982 else if( solval >= ub )
8983 aux[i] = 1.0;
8984 else
8985 aux[i] = (solval - lb) / (ub - lb);
8986
8987 /* perturb point to hopefully obtain a facet of the convex envelope */
8988 if( conshdlrdata->vp_maxperturb > 0.0 )
8989 {
8990 assert(conshdlrdata->vp_randnumgen != NULL);
8991
8992 if( aux[i] == 1.0 )
8993 aux[i] -= SCIPrandomGetReal(conshdlrdata->vp_randnumgen, 0.0, conshdlrdata->vp_maxperturb);
8994 else if( aux[i] == 0.0 )
8995 aux[i] += SCIPrandomGetReal(conshdlrdata->vp_randnumgen, 0.0, conshdlrdata->vp_maxperturb);
8996 else
8997 {
8998 SCIP_Real perturbation;
8999
9000 perturbation = MIN( aux[i], 1.0 - aux[i] ) / 2.0;
9001 perturbation = MIN( perturbation, conshdlrdata->vp_maxperturb );
9002 aux[i] += SCIPrandomGetReal(conshdlrdata->vp_randnumgen, -perturbation, perturbation);
9003 }
9004 assert(0.0 < aux[i] && aux[i] < 1.0);
9005 }
9006
9007 SCIPdebugMsg(scip, "LP row %d in [%e, %e]\n", i, aux[i], aux[i]);
9008 }
9009
9010 /* update LP */
9011 SCIP_CALL( SCIPlpiChgObj(lp, ncols, inds, funvals) );
9012 SCIP_CALL( SCIPlpiChgSides(lp, nrows-1, inds, aux, aux) );
9013 SCIP_CALL( SCIPlpiChgObjsen(lp, overestimate ? SCIP_OBJSEN_MAXIMIZE : SCIP_OBJSEN_MINIMIZE) );
9014
9015 /* we can stop the LP solve if will not meet the target value anyway, but only if xstar hasn't been perturbed */
9016 if( conshdlrdata->vp_maxperturb == 0.0 && !SCIPisInfinity(scip, REALABS(targetvalue)) )
9017 {
9018 SCIP_CALL( SCIPlpiSetRealpar(lp, SCIP_LPPAR_OBJLIM, targetvalue) );
9019 }
9020 /* set an iteration limit so we do not run forever */
9021 SCIP_CALL( SCIPlpiSetIntpar(lp, SCIP_LPPAR_LPITLIM, 100*ncols) );
9022 /* since we work with the dual of the LP, primal feastol determines how much we want the computed facet to be the best possible one */
9023 SCIP_CALL( SCIPlpiSetRealpar(lp, SCIP_LPPAR_FEASTOL, SCIPfeastol(scip)) );
9024 /* since we work with the dual of the LP, dual feastol determines validity of the facet
9025 * if some ub-lb is small, we need higher accuracy, since below we divide coefs by ub-lb (we moved and scaled the box)
9026 * thus, we set the dual feastol to be between SCIPepsilon and SCIPfeastol
9027 */
9028 SCIP_CALL( SCIPlpiSetRealpar(lp, SCIP_LPPAR_DUALFEASTOL, MIN(SCIPfeastol(scip), MAX(SCIPepsilon(scip), mindomwidth * SCIPfeastol(scip)))) );
9029
9030 #ifdef SCIP_DEBUG
9031 SCIP_CALL( SCIPlpiSetIntpar(lp, SCIP_LPPAR_LPINFO, 1) );
9032 #endif
9033
9034 /*
9035 * solve the LP and store the resulting facet for the transformed space
9036 */
9037 if( conshdlrdata->vp_dualsimplex )
9038 {
9039 lpsolveretcode = SCIPlpiSolveDual(lp);
9040 }
9041 else
9042 {
9043 lpsolveretcode = SCIPlpiSolvePrimal(lp);
9044 }
9045 if( lpsolveretcode == SCIP_LPERROR )
9046 {
9047 SCIPdebugMsg(scip, "LP error, aborting.\n");
9048 goto CLEANUP;
9049 }
9050 SCIP_CALL( lpsolveretcode );
9051
9052 /* any dual feasible solution should provide a valid estimator (and a dual optimal one a facet) */
9053 if( !SCIPlpiIsDualFeasible(lp) )
9054 {
9055 SCIPdebugMsg(scip, "LP not solved to dual feasibility, aborting.\n");
9056 goto CLEANUP;
9057 }
9058
9059 /* get dual solution (facet of convex envelope); again, we have to be careful since the LP can have more rows and
9060 * columns than needed, in particular, \bar \beta is the last dual multiplier
9061 */
9062 SCIP_CALL( SCIPlpiGetSol(lp, NULL, NULL, aux, NULL, NULL) );
9063
9064 for( i = 0; i < nvars; ++i )
9065 facetcoefs[nonfixedpos[i]] = aux[i];
9066 /* last dual multiplier is the constant */
9067 *facetconstant = aux[nrows - 1];
9068
9069 #ifdef SCIP_DEBUG
9070 SCIPdebugMsg(scip, "facet for the transformed problem: ");
9071 for( i = 0; i < nallvars; ++i )
9072 {
9073 SCIPdebugMsgPrint(scip, "%3.4e * x%d + ", facetcoefs[i], i);
9074 }
9075 SCIPdebugMsgPrint(scip, "%3.4e\n", *facetconstant);
9076 #endif
9077
9078 /*
9079 * transform the facet to original space and compute value at x^*, i.e., alpha x + beta
9080 */
9081
9082 SCIPdebugMsg(scip, "facet in orig. space: ");
9083
9084 facetvalue = 0.0;
9085 for( i = 0; i < nvars; ++i )
9086 {
9087 SCIP_Real lb;
9088 SCIP_Real ub;
9089 int varpos;
9090
9091 varpos = nonfixedpos[i];
9092 lb = box[2 * varpos];
9093 ub = box[2 * varpos + 1];
9094 assert(!SCIPisEQ(scip, lb, ub));
9095
9096 /* alpha_i := alpha_bar_i / (ub_i - lb_i) */
9097 facetcoefs[varpos] = facetcoefs[varpos] / (ub - lb);
9098
9099 /* beta = beta_bar - sum_i alpha_i * lb_i */
9100 *facetconstant -= facetcoefs[varpos] * lb;
9101
9102 /* evaluate */
9103 facetvalue += facetcoefs[varpos] * xstar[varpos];
9104
9105 SCIPdebugMsgPrint(scip, "%3.4e * x%d + ", facetcoefs[varpos], varpos);
9106 }
9107 SCIPdebugMsgPrint(scip, "%3.4e ", *facetconstant);
9108
9109 /* add beta to the facetvalue: at this point in the code, facetvalue = g(x^*) */
9110 facetvalue += *facetconstant;
9111
9112 SCIPdebugMsgPrint(scip, "has value %g, target = %g\n", facetvalue, targetvalue);
9113
9114 /* if overestimate, then we want facetvalue < targetvalue
9115 * if underestimate, then we want facetvalue > targetvalue
9116 * if none holds, give up
9117 * so maybe here we should check against the minimal violation
9118 */
9119 if( overestimate == (facetvalue > targetvalue) )
9120 {
9121 SCIPdebugMsg(scip, "missed the target, facetvalue %g targetvalue %g, overestimate=%u\n", facetvalue, targetvalue, overestimate);
9122 goto CLEANUP;
9123 }
9124
9125 /* if we made it until here, then we have a nice facet */
9126 *success = TRUE;
9127
9128 CLEANUP:
9129 /* free allocated memory */
9130 SCIPfreeBufferArray(scip, &inds);
9131 SCIPfreeBufferArray(scip, &aux);
9132
9133 return SCIP_OKAY;
9134 }
9135
9136 /** computes a facet of the convex or concave envelope of a univariant vertex polyhedral function
9137 *
9138 * In other words, compute the line that passes through two given points.
9139 */
9140 static
9141 SCIP_RETCODE computeVertexPolyhedralFacetUnivariate(
9142 SCIP* scip, /**< SCIP data structure */
9143 SCIP_Real left, /**< left coordinate */
9144 SCIP_Real right, /**< right coordinate */
9145 SCIP_Real funleft, /**< value of function in left coordinate */
9146 SCIP_Real funright, /**< value of function in right coordinate */
9147 SCIP_Bool* success, /**< buffer to store whether a facet could be computed successfully */
9148 SCIP_Real* facetcoef, /**< buffer to store coefficient of facet defining inequality */
9149 SCIP_Real* facetconstant /**< buffer to store constant part of facet defining inequality */
9150 )
9151 {
9152 assert(scip != NULL);
9153 assert(SCIPisLE(scip, left, right));
9154 assert(!SCIPisInfinity(scip, -left));
9155 assert(!SCIPisInfinity(scip, right));
9156 assert(SCIPisFinite(funleft) && funleft != SCIP_INVALID);
9157 assert(SCIPisFinite(funright) && funright != SCIP_INVALID);
9158 assert(success != NULL);
9159 assert(facetcoef != NULL);
9160 assert(facetconstant != NULL);
9161
9162 *facetcoef = (funright - funleft) / (right - left);
9163 *facetconstant = funleft - *facetcoef * left;
9164
9165 *success = TRUE;
9166
9167 return SCIP_OKAY;
9168 }
9169
9170 /** given three points, constructs coefficient of equation for hyperplane generated by these three points
9171 *
9172 * Three points a, b, and c are given.
9173 * Computes coefficients alpha, beta, gamma, and delta, such that a, b, and c, satisfy
9174 * alpha * x1 + beta * x2 + gamma * x3 = delta and gamma >= 0.0.
9175 */
9176 static
9177 SCIP_RETCODE computeHyperplaneThreePoints(
9178 SCIP* scip, /**< SCIP data structure */
9179 SCIP_Real a1, /**< first coordinate of a */
9180 SCIP_Real a2, /**< second coordinate of a */
9181 SCIP_Real a3, /**< third coordinate of a */
9182 SCIP_Real b1, /**< first coordinate of b */
9183 SCIP_Real b2, /**< second coordinate of b */
9184 SCIP_Real b3, /**< third coordinate of b */
9185 SCIP_Real c1, /**< first coordinate of c */
9186 SCIP_Real c2, /**< second coordinate of c */
9187 SCIP_Real c3, /**< third coordinate of c */
9188 SCIP_Real* alpha, /**< coefficient of first coordinate */
9189 SCIP_Real* beta, /**< coefficient of second coordinate */
9190 SCIP_Real* gamma_, /**< coefficient of third coordinate */
9191 SCIP_Real* delta /**< constant right-hand side */
9192 )
9193 {
9194 assert(scip != NULL);
9195 assert(alpha != NULL);
9196 assert(beta != NULL);
9197 assert(gamma_ != NULL);
9198 assert(delta != NULL);
9199
9200 *alpha = -b3*c2 + a3*(-b2+c2) + a2*(b3-c3) + b2*c3;
9201 *beta = -(-b3*c1 + a3*(-b1+c1) + a1*(b3-c3) + b1*c3);
9202 *gamma_ = -a2*b1 + a1*b2 + a2*c1 - b2*c1 - a1*c2 + b1*c2;
9203 *delta = -a3*b2*c1 + a2*b3*c1 + a3*b1*c2 - a1*b3*c2 - a2*b1*c3 + a1*b2*c3;
9204
9205 /* SCIPdebugMsg(scip, "alpha: %g beta: %g gamma: %g delta: %g\n", *alpha, *beta, *gamma_, *delta); */
9206
9207 if( SCIPisInfinity(scip, REALABS(*gamma_ * a3)) ||
9208 SCIPisInfinity(scip, REALABS(*gamma_ * b3)) ||
9209 SCIPisInfinity(scip, REALABS(*gamma_ * c3)) )
9210 {
9211 SCIPdebugMsg(scip, "activity above SCIP infinity\n");
9212 *delta = 0.0;
9213 *alpha = 0.0;
9214 *beta = 0.0;
9215 *gamma_ = 0.0;
9216 return SCIP_OKAY;
9217 }
9218
9219 /* check if hyperplane contains all three points (necessary because of numerical troubles) */
9220 if( !SCIPisRelEQ(scip, *alpha * a1 + *beta * a2 - *delta, -*gamma_ * a3) ||
9221 !SCIPisRelEQ(scip, *alpha * b1 + *beta * b2 - *delta, -*gamma_ * b3) ||
9222 !SCIPisRelEQ(scip, *alpha * c1 + *beta * c2 - *delta, -*gamma_ * c3) )
9223 {
9224 SCIP_Real m[9];
9225 SCIP_Real rhs[3];
9226 SCIP_Real x[3];
9227 SCIP_Bool success;
9228
9229 /*
9230 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));
9231 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));
9232 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));
9233 */
9234
9235 /* initialize matrix column-wise */
9236 m[0] = a1;
9237 m[1] = b1;
9238 m[2] = c1;
9239 m[3] = a2;
9240 m[4] = b2;
9241 m[5] = c2;
9242 m[6] = a3;
9243 m[7] = b3;
9244 m[8] = c3;
9245
9246 rhs[0] = 1.0;
9247 rhs[1] = 1.0;
9248 rhs[2] = 1.0;
9249
9250 SCIPdebugMsg(scip, "numerical troubles - try to solve the linear system via an LU factorization\n");
9251
9252 /* solve the linear problem */
9253 SCIP_CALL( SCIPsolveLinearEquationsIpopt(3, m, rhs, x, &success) );
9254
9255 *delta = rhs[0];
9256 *alpha = x[0];
9257 *beta = x[1];
9258 *gamma_ = x[2];
9259
9260 /* set all coefficients to zero if one of the points is not contained in the hyperplane; this ensures that we do
9261 * not add a cut to SCIP and that all assertions are trivially fulfilled
9262 */
9263 if( !success || !SCIPisRelEQ(scip, *alpha * a1 + *beta * a2 - *delta, -*gamma_ * a3) ||
9264 !SCIPisRelEQ(scip, *alpha * b1 + *beta * b2 - *delta, -*gamma_ * b3) ||
9265 !SCIPisRelEQ(scip, *alpha * c1 + *beta * c2 - *delta, -*gamma_ * c3) ) /*lint !e774*/
9266 {
9267 SCIPdebugMsg(scip, "could not resolve numerical difficulties\n");
9268 *delta = 0.0;
9269 *alpha = 0.0;
9270 *beta = 0.0;
9271 *gamma_ = 0.0;
9272 }
9273 }
9274
9275 if( *gamma_ < 0.0 )
9276 {
9277 *alpha = -*alpha;
9278 *beta = -*beta;
9279 *gamma_ = -*gamma_;
9280 *delta = -*delta;
9281 }
9282
9283 return SCIP_OKAY;
9284 }
9285
9286 /** computes a facet of the convex or concave envelope of a bivariate vertex polyhedral function */
9287 static
9288 SCIP_RETCODE computeVertexPolyhedralFacetBivariate(
9289 SCIP* scip, /**< SCIP data structure */
9290 SCIP_Bool overestimate, /**< whether to compute facet of concave (TRUE) or convex (FALSE) envelope */
9291 SCIP_Real p1[2], /**< first vertex of box */
9292 SCIP_Real p2[2], /**< second vertex of box */
9293 SCIP_Real p3[2], /**< third vertex of box */
9294 SCIP_Real p4[2], /**< forth vertex of box */
9295 SCIP_Real p1val, /**< value in p1 */
9296 SCIP_Real p2val, /**< value in p2 */
9297 SCIP_Real p3val, /**< value in p3 */
9298 SCIP_Real p4val, /**< value in p4 */
9299 SCIP_Real xstar[2], /**< point to be separated */
9300 SCIP_Real targetvalue, /**< target value: no need to compute facet if value in xstar would be worse than this value */
9301 SCIP_Bool* success, /**< buffer to store whether a facet could be computed successfully */
9302 SCIP_Real* facetcoefs, /**< buffer to store coefficients of facet defining inequality; must be an array of length at least 2 */
9303 SCIP_Real* facetconstant /**< buffer to store constant part of facet defining inequality */
9304 )
9305 {
9306 SCIP_Real alpha, beta, gamma_, delta;
9307 SCIP_Real xstarval, candxstarval = 0.0;
9308 int leaveout;
9309
9310 assert(scip != NULL);
9311 assert(success != NULL);
9312 assert(SCIPisFinite(p1val) && p1val != SCIP_INVALID);
9313 assert(SCIPisFinite(p2val) && p2val != SCIP_INVALID);
9314 assert(SCIPisFinite(p3val) && p3val != SCIP_INVALID);
9315 assert(SCIPisFinite(p4val) && p4val != SCIP_INVALID);
9316 assert(facetcoefs != NULL);
9317 assert(facetconstant != NULL);
9318
9319 *success = FALSE;
9320
9321 /* if we want an underestimator, flip f(x,y), i.e., do as if we compute an overestimator for -f(x,y) */
9322 if( !overestimate )
9323 {
9324 p1val = -p1val;
9325 p2val = -p2val;
9326 p3val = -p3val;
9327 p4val = -p4val;
9328 targetvalue = -targetvalue;
9329 }
9330
9331 SCIPdebugMsg(scip, "p1 = (%g, %g), f(p1) = %g\n", p1[0], p1[1], p1val);
9332 SCIPdebugMsg(scip, "p2 = (%g, %g), f(p2) = %g\n", p2[0], p2[1], p2val);
9333 SCIPdebugMsg(scip, "p3 = (%g, %g), f(p3) = %g\n", p3[0], p3[1], p3val);
9334 SCIPdebugMsg(scip, "p4 = (%g, %g), f(p4) = %g\n", p4[0], p4[1], p4val);
9335
9336 /* Compute coefficients alpha, beta, gamma (>0), delta such that
9337 * alpha*x + beta*y + gamma*z = delta
9338 * is satisfied by at least three of the corner points (p1,f(p1)), ..., (p4,f(p4)) and
9339 * the fourth corner point lies below this hyperplane.
9340 * Since we assume that f is vertex-polyhedral, we then know that all points (x,y,f(x,y)) are below this hyperplane, i.e.,
9341 * alpha*x + beta*y - delta <= -gamma * f(x,y),
9342 * or, equivalently,
9343 * -alpha/gamma*x - beta/gamma*y + delta/gamma >= f(x,y).
9344 */
9345 for( leaveout = 1; leaveout <= 4; ++leaveout )
9346 {
9347 switch( leaveout)
9348 {
9349 case 1 :
9350 /* get hyperplane through p2, p3, p4 */
9351 SCIP_CALL( computeHyperplaneThreePoints(scip, p2[0], p2[1], p2val, p3[0], p3[1], p3val, p4[0], p4[1], p4val,
9352 &alpha, &beta, &gamma_, &delta) );
9353 /* if not underestimating in p1, then go to next candidate */
9354 if( alpha * p1[0] + beta * p1[1] + gamma_ * p1val - delta > 0.0 )
9355 continue;
9356 break;
9357
9358 case 2 :
9359 /* get hyperplane through p1, p3, p4 */
9360 SCIP_CALL( computeHyperplaneThreePoints(scip, p1[0], p1[1], p1val, p3[0], p3[1], p3val, p4[0], p4[1], p4val,
9361 &alpha, &beta, &gamma_, &delta) );
9362 /* if not underestimating in p2, then go to next candidate */
9363 if( alpha * p2[0] + beta * p2[1] + gamma_ * p2val - delta > 0.0 )
9364 continue;
9365 break;
9366
9367 case 3 :
9368 /* get hyperplane through p1, p2, p4 */
9369 SCIP_CALL( computeHyperplaneThreePoints(scip, p1[0], p1[1], p1val, p2[0], p2[1], p2val, p4[0], p4[1], p4val,
9370 &alpha, &beta, &gamma_, &delta) );
9371 /* if not underestimating in p3, then go to next candidate */
9372 if( alpha * p3[0] + beta * p3[1] + gamma_ * p3val - delta > 0.0 )
9373 continue;
9374 break;
9375
9376 case 4 :
9377 /* get hyperplane through p1, p2, p3 */
9378 SCIP_CALL( computeHyperplaneThreePoints(scip, p1[0], p1[1], p1val, p2[0], p2[1], p2val, p3[0], p3[1], p3val,
9379 &alpha, &beta, &gamma_, &delta) );
9380 /* if not underestimating in p4, then stop */
9381 if( alpha * p4[0] + beta * p4[1] + gamma_ * p4val - delta > 0.0 )
9382 continue;
9383 break;
9384
9385 default: /* only for lint */
9386 alpha = SCIP_INVALID;
9387 beta = SCIP_INVALID;
9388 gamma_ = SCIP_INVALID;
9389 delta = SCIP_INVALID;
9390 break;
9391 }
9392
9393 /* check if bad luck: should not happen if numerics are fine */
9394 if( SCIPisZero(scip, gamma_) )
9395 continue;
9396 assert(!SCIPisNegative(scip, gamma_));
9397
9398 /* if coefficients become tiny because division by gamma makes them < SCIPepsilon(scip), then skip, too */
9399 if( (!SCIPisZero(scip, alpha) && SCIPisZero(scip, alpha/gamma_)) ||
9400 ( !SCIPisZero(scip, beta) && SCIPisZero(scip, beta/gamma_)) )
9401 continue;
9402
9403 SCIPdebugMsg(scip, "alpha = %g, beta = %g, gamma = %g, delta = %g\n", alpha, beta, gamma_, delta);
9404
9405 /* value of hyperplane candidate in xstar */
9406 xstarval = -alpha/gamma_ * xstar[0] -beta/gamma_ * xstar[1] + delta/gamma_;
9407
9408 /* if reaching target and first or better than previous candidate, then update */
9409 if( xstarval <= targetvalue && (!*success || xstarval < candxstarval) )
9410 {
9411 /* flip hyperplane */
9412 if( !overestimate )
9413 gamma_ = -gamma_;
9414
9415 facetcoefs[0] = -alpha/gamma_;
9416 facetcoefs[1] = -beta/gamma_;
9417 *facetconstant = delta/gamma_;
9418
9419 *success = TRUE;
9420 candxstarval = xstarval;
9421 }
9422 }
9423
9424 return SCIP_OKAY;
9425 }
9426
9427 /*
9428 * Callback methods of constraint handler
9429 */
9430
9431 /** copy method for constraint handler plugins (called when SCIP copies plugins) */
9432 static
9433 SCIP_DECL_CONSHDLRCOPY(conshdlrCopyNonlinear)
9434 { /*lint --e{715}*/
9435 SCIP_CONSHDLR* targetconshdlr;
9436 SCIP_CONSHDLRDATA* sourceconshdlrdata;
9437 int i;
9438
9439 assert(scip != NULL);
9440 assert(conshdlr != NULL);
9441 assert(valid != NULL);
9442 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
9443
9444 /* create basic data of constraint handler and include it to scip */
9445 SCIP_CALL( SCIPincludeConshdlrNonlinear(scip) );
9446
9447 targetconshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
9448 assert(targetconshdlr != NULL);
9449 assert(targetconshdlr != conshdlr);
9450
9451 sourceconshdlrdata = SCIPconshdlrGetData(conshdlr);
9452 assert(sourceconshdlrdata != NULL);
9453
9454 /* copy nonlinear handlers */
9455 for( i = 0; i < sourceconshdlrdata->nnlhdlrs; ++i )
9456 {
9457 SCIP_CALL( SCIPnlhdlrCopyhdlr(scip, targetconshdlr, conshdlr, sourceconshdlrdata->nlhdlrs[i]) );
9458 }
9459
9460 *valid = TRUE;
9461
9462 return SCIP_OKAY;
9463 }
9464
9465 /** destructor of constraint handler to free constraint handler data (called when SCIP is exiting) */
9466 static
9467 SCIP_DECL_CONSFREE(consFreeNonlinear)
9468 { /*lint --e{715}*/
9469 SCIP_CONSHDLRDATA* conshdlrdata;
9470 int i;
9471
9472 conshdlrdata = SCIPconshdlrGetData(conshdlr);
9473 assert(conshdlrdata != NULL);
9474
9475 /* free nonlinear handlers */
9476 for( i = 0; i < conshdlrdata->nnlhdlrs; ++i )
9477 {
9478 SCIP_CALL( SCIPnlhdlrFree(scip, &conshdlrdata->nlhdlrs[i]) );
9479 assert(conshdlrdata->nlhdlrs[i] == NULL);
9480 }
9481 SCIPfreeBlockMemoryArrayNull(scip, &conshdlrdata->nlhdlrs, conshdlrdata->nlhdlrssize);
9482 conshdlrdata->nlhdlrssize = 0;
9483
9484 /* free upgrade functions */
9485 for( i = 0; i < conshdlrdata->nconsupgrades; ++i )
9486 {
9487 assert(conshdlrdata->consupgrades[i] != NULL);
9488 SCIPfreeBlockMemory(scip, &conshdlrdata->consupgrades[i]);
9489 }
9490 SCIPfreeBlockMemoryArrayNull(scip, &conshdlrdata->consupgrades, conshdlrdata->consupgradessize);
9491
9492 SCIP_CALL( SCIPfreeClock(scip, &conshdlrdata->canonicalizetime) );
9493
9494 SCIPqueueFree(&conshdlrdata->reversepropqueue);
9495
9496 if( conshdlrdata->vp_randnumgen != NULL )
9497 SCIPfreeRandom(scip, &conshdlrdata->vp_randnumgen);
9498
9499 /* free LPs used to construct facets of envelops of vertex-polyhedral functions */
9500 for( i = 0; i <= SCIP_MAXVERTEXPOLYDIM; ++i )
9501 {
9502 if( conshdlrdata->vp_lp[i] != NULL )
9503 {
9504 SCIP_CALL( SCIPlpiFree(&conshdlrdata->vp_lp[i]) );
9505 }
9506 }
9507
9508 assert(conshdlrdata->branchrandnumgen == NULL);
9509
9510 assert(SCIPhashmapGetNElements(conshdlrdata->var2expr) == 0);
9511 SCIPhashmapFree(&conshdlrdata->var2expr);
9512
9513 SCIPfreeMemory(scip, &conshdlrdata);
9514 SCIPconshdlrSetData(conshdlr, NULL);
9515
9516 return SCIP_OKAY;
9517 }
9518
9519
9520 /** initialization method of constraint handler (called after problem was transformed) */
9521 static
9522 SCIP_DECL_CONSINIT(consInitNonlinear)
9523 { /*lint --e{715}*/
9524 SCIP_CONSHDLRDATA* conshdlrdata;
9525 int i;
9526
9527 conshdlrdata = SCIPconshdlrGetData(conshdlr);
9528 assert(conshdlrdata != NULL);
9529
9530 /* make sure current activity tags in expressions are invalid, because we start catching variable events only now */
9531 conshdlrdata->lastboundrelax = ++conshdlrdata->curboundstag;
9532 /* set to 1 so it is larger than initial value of lastenforound in exprs */
9533 conshdlrdata->enforound = 1;
9534
9535 for( i = 0; i < nconss; ++i )
9536 {
9537 SCIP_CALL( storeVarExprs(scip, conshdlr, SCIPconsGetData(conss[i])) );
9538 SCIP_CALL( catchVarEvents(scip, conshdlrdata->eventhdlr, conss[i]) );
9539 }
9540
9541 /* sort nonlinear handlers by detection priority, in decreasing order */
9542 if( conshdlrdata->nnlhdlrs > 1 )
9543 SCIPsortDownPtr((void**)conshdlrdata->nlhdlrs, SCIPnlhdlrComp, conshdlrdata->nnlhdlrs);
9544
9545 /* get heuristics for later use */
9546 conshdlrdata->subnlpheur = SCIPfindHeur(scip, "subnlp");
9547 conshdlrdata->trysolheur = SCIPfindHeur(scip, "trysol");
9548
9549 /* reset statistics in nonlinear handlers (TODO only if misc/resetstat == TRUE) */
9550 for( i = 0; i < conshdlrdata->nnlhdlrs; ++i )
9551 {
9552 SCIP_CALL( SCIPnlhdlrInit(scip, conshdlrdata->nlhdlrs[i]) );
9553 }
9554
9555 /* reset statistics in constraint handler */
9556 conshdlrdata->nweaksepa = 0;
9557 conshdlrdata->ntightenlp = 0;
9558 conshdlrdata->ndesperatebranch = 0;
9559 conshdlrdata->ndesperatecutoff = 0;
9560 conshdlrdata->ndesperatetightenlp = 0;
9561 conshdlrdata->nforcelp = 0;
9562 SCIP_CALL( SCIPresetClock(scip, conshdlrdata->canonicalizetime) );
9563 conshdlrdata->ncanonicalizecalls = 0;
9564
9565 #ifdef ENFOLOGFILE
9566 ENFOLOG( enfologfile = fopen(ENFOLOGFILE, "w"); )
9567 #endif
9568
9569 return SCIP_OKAY;
9570 }
9571
9572
9573 /** deinitialization method of constraint handler (called before transformed problem is freed) */
9574 static
9575 SCIP_DECL_CONSEXIT(consExitNonlinear)
9576 { /*lint --e{715}*/
9577 SCIP_CONSHDLRDATA* conshdlrdata;
9578 SCIP_CONS** consssorted;
9579 int i;
9580
9581 conshdlrdata = SCIPconshdlrGetData(conshdlr);
9582 assert(conshdlrdata != NULL);
9583
9584 if( nconss > 0 )
9585 {
9586 /* for better performance of dropVarEvents, we sort by index, descending */
9587 SCIP_CALL( SCIPduplicateBufferArray(scip, &consssorted, conss, nconss) );
9588 SCIPsortDownPtr((void**)consssorted, compIndexConsNonlinear, nconss);
9589
9590 for( i = 0; i < nconss; ++i )
9591 {
9592 SCIP_CALL( dropVarEvents(scip, conshdlrdata->eventhdlr, consssorted[i]) );
9593 SCIP_CALL( freeVarExprs(scip, SCIPconsGetData(consssorted[i])) );
9594 }
9595
9596 SCIPfreeBufferArray(scip, &consssorted);
9597 }
9598
9599 conshdlrdata->subnlpheur = NULL;
9600 conshdlrdata->trysolheur = NULL;
9601
9602 if( conshdlrdata->vp_randnumgen != NULL )
9603 SCIPfreeRandom(scip, &conshdlrdata->vp_randnumgen);
9604
9605 /* free LPs used to construct facets of envelops of vertex-polyhedral functions */
9606 for( i = 0; i <= SCIP_MAXVERTEXPOLYDIM; ++i )
9607 {
9608 if( conshdlrdata->vp_lp[i] != NULL )
9609 {
9610 SCIP_CALL( SCIPlpiFree(&conshdlrdata->vp_lp[i]) );
9611 }
9612 }
9613
9614 if( conshdlrdata->branchrandnumgen != NULL )
9615 SCIPfreeRandom(scip, &conshdlrdata->branchrandnumgen);
9616
9617 /* deinitialize nonlinear handlers */
9618 for( i = 0; i < conshdlrdata->nnlhdlrs; ++i )
9619 {
9620 SCIP_CALL( SCIPnlhdlrExit(scip, conshdlrdata->nlhdlrs[i]) );
9621 }
9622
9623 ENFOLOG(
9624 if( enfologfile != NULL )
9625 {
9626 fclose(enfologfile);
9627 enfologfile = NULL;
9628 })
9629
9630 return SCIP_OKAY;
9631 }
9632
9633
9634 /** presolving initialization method of constraint handler (called when presolving is about to begin) */
9635 #ifdef SCIP_DISABLED_CODE
9636 static
9637 SCIP_DECL_CONSINITPRE(consInitpreNonlinear)
9638 { /*lint --e{715}*/
9639 SCIPerrorMessage("method of nonlinear constraint handler not implemented yet\n");
9640 SCIPABORT(); /*lint --e{527}*/
9641
9642 return SCIP_OKAY;
9643 }
9644 #else
9645 #define consInitpreNonlinear NULL
9646 #endif
9647
9648
9649 /** presolving deinitialization method of constraint handler (called after presolving has been finished) */
9650 static
9651 SCIP_DECL_CONSEXITPRE(consExitpreNonlinear)
9652 { /*lint --e{715}*/
9653 SCIP_Bool infeasible;
9654
9655 if( nconss == 0 )
9656 return SCIP_OKAY;
9657
9658 /* skip some extra work if already known to be infeasible */
9659 if( SCIPgetStatus(scip) == SCIP_STATUS_INFEASIBLE )
9660 return SCIP_OKAY;
9661
9662 /* simplify constraints and replace common subexpressions */
9663 SCIP_CALL( canonicalizeConstraints(scip, conshdlr, conss, nconss, SCIP_PRESOLTIMING_ALWAYS, &infeasible, NULL, NULL, NULL) );
9664
9665 /* currently SCIP does not offer to communicate this,
9666 * but at the moment this can only become true if canonicalizeConstraints called detectNlhdlrs (which it doesn't do in EXITPRESOLVE stage)
9667 * or if a constraint expression became constant
9668 */
9669 assert(!infeasible);
9670
9671 /* tell SCIP that we have something nonlinear */
9672 SCIPenableNLP(scip);
9673
9674 return SCIP_OKAY;
9675 }
9676
9677
9678 /** solving process initialization method of constraint handler (called when branch and bound process is about to begin) */
9679 static
9680 SCIP_DECL_CONSINITSOL(consInitsolNonlinear)
9681 { /*lint --e{715}*/
9682 SCIP_CONSHDLRDATA* conshdlrdata;
9683 int i;
9684
9685 /* skip remaining initializations if we have solved already
9686 * if infeasibility was found by our boundtightening, then curvature check may also fail as some exprhdlr (e.g., pow)
9687 * assumes nonempty activities in expressions
9688 */
9689 switch( SCIPgetStatus(scip) )
9690 {
9691 case SCIP_STATUS_OPTIMAL:
9692 case SCIP_STATUS_INFEASIBLE:
9693 case SCIP_STATUS_UNBOUNDED:
9694 case SCIP_STATUS_INFORUNBD:
9695 return SCIP_OKAY;
9696 default: ;
9697 } /*lint !e788 */
9698
9699 conshdlrdata = SCIPconshdlrGetData(conshdlr);
9700 assert(conshdlrdata != NULL);
9701
9702 /* reset one of the number of detections counter to count only current round */
9703 for( i = 0; i < conshdlrdata->nnlhdlrs; ++i )
9704 SCIPnlhdlrResetNDetectionslast(conshdlrdata->nlhdlrs[i]);
9705
9706 SCIP_CALL( initSolve(scip, conshdlr, conss, nconss) );
9707
9708 /* catch new solution event */
9709 if( nconss != 0 && conshdlrdata->linearizeheursol != 'o' )
9710 {
9711 SCIP_EVENTHDLR* eventhdlr;
9712
9713 eventhdlr = SCIPfindEventhdlr(scip, CONSHDLR_NAME "_newsolution");
9714 assert(eventhdlr != NULL);
9715
9716 SCIP_CALL( SCIPcatchEvent(scip, conshdlrdata->linearizeheursol == 'i' ? SCIP_EVENTTYPE_BESTSOLFOUND : SCIP_EVENTTYPE_SOLFOUND,
9717 eventhdlr, (SCIP_EVENTDATA*)conshdlr, &conshdlrdata->newsoleventfilterpos) );
9718 }
9719
9720 /* check that branching/lpgainnormalize is set to a known value if pseudo-costs are used in branching */
9721 if( conshdlrdata->branchpscostweight > 0.0 )
9722 {
9723 SCIP_CALL( SCIPgetCharParam(scip, "branching/lpgainnormalize", &(conshdlrdata->branchpscostupdatestrategy)) );
9724 if( strchr("lds", conshdlrdata->branchpscostupdatestrategy) == NULL )
9725 {
9726 SCIPerrorMessage("branching/lpgainnormalize strategy %c unknown\n", conshdlrdata->branchpscostupdatestrategy);
9727 SCIPABORT();
9728 return SCIP_INVALIDDATA;
9729 }
9730 }
9731
9732 return SCIP_OKAY;
9733 }
9734
9735
9736 /** solving process deinitialization method of constraint handler (called before branch and bound process data is freed) */
9737 static
9738 SCIP_DECL_CONSEXITSOL(consExitsolNonlinear)
9739 { /*lint --e{715}*/
9740 SCIP_CONSHDLRDATA* conshdlrdata;
9741
9742 SCIP_CALL( deinitSolve(scip, conshdlr, conss, nconss) );
9743
9744 conshdlrdata = SCIPconshdlrGetData(conshdlr);
9745 assert(conshdlrdata != NULL);
9746
9747 /* free hash table for bilinear terms */
9748 SCIP_CALL( bilinearTermsFree(scip, conshdlrdata) );
9749
9750 /* reset flag to allow another call of presolSingleLockedVars() after a restart */
9751 conshdlrdata->checkedvarlocks = FALSE;
9752
9753 /* drop catching new solution event, if catched before */
9754 if( conshdlrdata->newsoleventfilterpos >= 0 )
9755 {
9756 SCIP_EVENTHDLR* eventhdlr;
9757
9758 eventhdlr = SCIPfindEventhdlr(scip, CONSHDLR_NAME "_newsolution");
9759 assert(eventhdlr != NULL);
9760
9761 SCIP_CALL( SCIPdropEvent(scip, conshdlrdata->linearizeheursol == 'i' ? SCIP_EVENTTYPE_BESTSOLFOUND : SCIP_EVENTTYPE_SOLFOUND, eventhdlr, (SCIP_EVENTDATA*)conshdlr, conshdlrdata->newsoleventfilterpos) );
9762 conshdlrdata->newsoleventfilterpos = -1;
9763 }
9764
9765 return SCIP_OKAY;
9766 }
9767
9768
9769 /** frees specific constraint data */
9770 static
9771 SCIP_DECL_CONSDELETE(consDeleteNonlinear)
9772 { /*lint --e{715}*/
9773 assert(consdata != NULL);
9774 assert(*consdata != NULL);
9775 assert((*consdata)->expr != NULL);
9776
9777 /* constraint locks should have been removed */
9778 assert((*consdata)->nlockspos == 0);
9779 assert((*consdata)->nlocksneg == 0);
9780
9781 /* free variable expressions */
9782 SCIP_CALL( freeVarExprs(scip, *consdata) );
9783
9784 SCIP_CALL( SCIPreleaseExpr(scip, &(*consdata)->expr) );
9785
9786 /* free nonlinear row representation */
9787 if( (*consdata)->nlrow != NULL )
9788 {
9789 SCIP_CALL( SCIPreleaseNlRow(scip, &(*consdata)->nlrow) );
9790 }
9791
9792 SCIPfreeBlockMemory(scip, consdata);
9793
9794 return SCIP_OKAY;
9795 }
9796
9797
9798 /** transforms constraint data into data belonging to the transformed problem */
9799 static
9800 SCIP_DECL_CONSTRANS(consTransNonlinear)
9801 { /*lint --e{715}*/
9802 SCIP_EXPR* targetexpr;
9803 SCIP_CONSDATA* sourcedata;
9804
9805 sourcedata = SCIPconsGetData(sourcecons);
9806 assert(sourcedata != NULL);
9807
9808 /* get a copy of sourceexpr with transformed vars */
9809 SCIP_CALL( SCIPduplicateExpr(scip, sourcedata->expr, &targetexpr, mapexprtransvar, conshdlr, exprownerCreate, (void*)conshdlr) );
9810 assert(targetexpr != NULL); /* SCIPduplicateExpr cannot fail */
9811
9812 /* create transformed cons (only captures targetexpr, no need to copy again) */
9813 SCIP_CALL( createCons(scip, conshdlr, targetcons, SCIPconsGetName(sourcecons),
9814 targetexpr, sourcedata->lhs, sourcedata->rhs, FALSE,
9815 SCIPconsIsInitial(sourcecons), SCIPconsIsSeparated(sourcecons), SCIPconsIsEnforced(sourcecons),
9816 SCIPconsIsChecked(sourcecons), SCIPconsIsPropagated(sourcecons),
9817 SCIPconsIsLocal(sourcecons), SCIPconsIsModifiable(sourcecons),
9818 SCIPconsIsDynamic(sourcecons), SCIPconsIsRemovable(sourcecons)) );
9819
9820 /* release target expr */
9821 SCIP_CALL( SCIPreleaseExpr(scip, &targetexpr) );
9822
9823 return SCIP_OKAY;
9824 }
9825
9826
9827 /** LP initialization method of constraint handler (called before the initial LP relaxation at a node is solved) */
9828 static
9829 SCIP_DECL_CONSINITLP(consInitlpNonlinear)
9830 { /*lint --e{715}*/
9831 /* create auxiliary variables and call separation initialization callbacks of the expression handlers
9832 * TODO if we ever want to allow constraints that are separated but not initial, then we need to call initSepa also
9833 * during SEPALP, ENFOLP, etc, whenever a constraint may be separated the first time
9834 * for now, there is an assert in detectNlhdlrs to require initial if separated
9835 */
9836 SCIP_CALL( initSepa(scip, conshdlr, conss, nconss, infeasible) );
9837
9838 /* collect all bilinear terms for which an auxvar is present
9839 * TODO this will only do something for the first call of initlp after initsol, because it cannot handle
9840 * addition (and removal?) of constraints during solve
9841 * this is typically the majority of constraints, but the method should be made more flexible
9842 */
9843 SCIP_CALL( bilinearTermsInsertAll(scip, conshdlr, conss, nconss) );
9844
9845 return SCIP_OKAY;
9846 }
9847
9848
9849 /** separation method of constraint handler for LP solutions */
9850 static
9851 SCIP_DECL_CONSSEPALP(consSepalpNonlinear)
9852 { /*lint --e{715}*/
9853 SCIP_CALL( consSepa(scip, conshdlr, conss, nconss, NULL, result) );
9854
9855 return SCIP_OKAY;
9856 }
9857
9858
9859 /** separation method of constraint handler for arbitrary primal solutions */
9860 static
9861 SCIP_DECL_CONSSEPASOL(consSepasolNonlinear)
9862 { /*lint --e{715}*/
9863 SCIP_CALL( consSepa(scip, conshdlr, conss, nconss, sol, result) );
9864
9865 return SCIP_OKAY;
9866 }
9867
9868
9869 /** constraint enforcing method of constraint handler for LP solutions */
9870 static
9871 SCIP_DECL_CONSENFOLP(consEnfolpNonlinear)
9872 { /*lint --e{715}*/
9873 SCIP_CALL( consEnfo(scip, conshdlr, conss, nconss, NULL, result) );
9874
9875 return SCIP_OKAY;
9876 }
9877
9878
9879 /** constraint enforcing method of constraint handler for relaxation solutions */
9880 static
9881 SCIP_DECL_CONSENFORELAX(consEnforelaxNonlinear)
9882 { /*lint --e{715}*/
9883 SCIP_CALL( consEnfo(scip, conshdlr, conss, nconss, sol, result) );
9884
9885 return SCIP_OKAY;
9886 }
9887
9888
9889 /** constraint enforcing method of constraint handler for pseudo solutions */
9890 static
9891 SCIP_DECL_CONSENFOPS(consEnfopsNonlinear)
9892 { /*lint --e{715}*/
9893 SCIP_RESULT propresult;
9894 SCIP_Longint soltag;
9895 int nchgbds;
9896 int nnotify;
9897 int c;
9898
9899 soltag = SCIPgetExprNewSoltag(scip);
9900
9901 *result = SCIP_FEASIBLE;
9902 for( c = 0; c < nconss; ++c )
9903 {
9904 SCIP_CALL( computeViolation(scip, conss[c], NULL, soltag) );
9905
9906 if( isConsViolated(scip, conss[c]) )
9907 *result = SCIP_INFEASIBLE;
9908 }
9909
9910 if( *result == SCIP_FEASIBLE )
9911 return SCIP_OKAY;
9912
9913 /* try to propagate
9914 * TODO obey propinenfo parameter, but we need something to recognize cutoff
9915 */
9916 nchgbds = 0;
9917 SCIP_CALL( propConss(scip, conshdlr, conss, nconss, TRUE, &propresult, &nchgbds) );
9918
9919 if( (propresult == SCIP_CUTOFF) || (propresult == SCIP_REDUCEDDOM) )
9920 {
9921 *result = propresult;
9922 return SCIP_OKAY;
9923 }
9924
9925 /* register all unfixed variables in all violated constraints as branching candidates */
9926 SCIP_CALL( registerBranchingCandidatesAllUnfixed(scip, conshdlr, conss, nconss, &nnotify) );
9927 if( nnotify > 0 )
9928 {
9929 SCIPdebugMsg(scip, "registered %d external branching candidates\n", nnotify);
9930
9931 return SCIP_OKAY;
9932 }
9933
9934 SCIPdebugMsg(scip, "could not find branching candidates, forcing to solve LP\n");
9935 *result = SCIP_SOLVELP;
9936 ++SCIPconshdlrGetData(conshdlr)->nforcelp;
9937
9938 return SCIP_OKAY;
9939 }
9940
9941
9942 /** feasibility check method of constraint handler for integral solutions */
9943 static
9944 SCIP_DECL_CONSCHECK(consCheckNonlinear)
9945 { /*lint --e{715}*/
9946 SCIP_CONSHDLRDATA* conshdlrdata;
9947 SCIP_CONSDATA* consdata;
9948 SCIP_Real maxviol;
9949 SCIP_Bool maypropfeasible;
9950 SCIP_Longint soltag;
9951 int c;
9952
9953 assert(scip != NULL);
9954 assert(conshdlr != NULL);
9955 assert(conss != NULL || nconss == 0);
9956 assert(result != NULL);
9957
9958 conshdlrdata = SCIPconshdlrGetData(conshdlr);
9959 assert(conshdlrdata != NULL);
9960
9961 *result = SCIP_FEASIBLE;
9962 soltag = SCIPgetExprNewSoltag(scip);
9963 maxviol = 0.0;
9964 maypropfeasible = conshdlrdata->trysolheur != NULL && SCIPgetStage(scip) >= SCIP_STAGE_TRANSFORMED
9965 && SCIPgetStage(scip) <= SCIP_STAGE_SOLVING;
9966
9967 /* check nonlinear constraints for feasibility */
9968 for( c = 0; c < nconss; ++c )
9969 {
9970 assert(conss != NULL && conss[c] != NULL);
9971 SCIP_CALL( computeViolation(scip, conss[c], sol, soltag) );
9972
9973 if( isConsViolated(scip, conss[c]) )
9974 {
9975 *result = SCIP_INFEASIBLE;
9976 maxviol = MAX(maxviol, getConsAbsViolation(conss[c]));
9977
9978 consdata = SCIPconsGetData(conss[c]);
9979 assert(consdata != NULL);
9980
9981 /* print reason for infeasibility */
9982 if( printreason )
9983 {
9984 SCIP_CALL( SCIPprintCons(scip, conss[c], NULL) );
9985 SCIPinfoMessage(scip, NULL, ";\n");
9986
9987 if( consdata->lhsviol > SCIPfeastol(scip) )
9988 {
9989 SCIPinfoMessage(scip, NULL, "violation: left hand side is violated by %.15g\n", consdata->lhsviol);
9990 }
9991 if( consdata->rhsviol > SCIPfeastol(scip) )
9992 {
9993 SCIPinfoMessage(scip, NULL, "violation: right hand side is violated by %.15g\n", consdata->rhsviol);
9994 }
9995 }
9996 else if( (conshdlrdata->subnlpheur == NULL || sol == NULL) && !maypropfeasible && !completely )
9997 {
9998 /* if we don't want to pass to subnlp heuristic and don't need to print reasons, then can stop checking here */
9999 return SCIP_OKAY;
10000 }
10001
10002 /* do not try to shift linear variables if violation is at infinity (leads to setting variable to infinity in solution, which is not allowed) */
10003 if( maypropfeasible && SCIPisInfinity(scip, getConsAbsViolation(conss[c])) )
10004 maypropfeasible = FALSE;
10005
10006 if( maypropfeasible )
10007 {
10008 if( consdata->lhsviol > SCIPfeastol(scip) )
10009 {
10010 /* check if there is a variable which may help to get the left hand side satisfied
10011 * if there is no such variable, then we cannot get feasible
10012 */
10013 if( !(consdata->linvarincr != NULL && consdata->linvarincrcoef > 0.0) &&
10014 !(consdata->linvardecr != NULL && consdata->linvardecrcoef < 0.0) )
10015 maypropfeasible = FALSE;
10016 }
10017 else
10018 {
10019 assert(consdata->rhsviol > SCIPfeastol(scip));
10020 /* check if there is a variable which may help to get the right hand side satisfied
10021 * if there is no such variable, then we cannot get feasible
10022 */
10023 if( !(consdata->linvarincr != NULL && consdata->linvarincrcoef < 0.0) &&
10024 !(consdata->linvardecr != NULL && consdata->linvardecrcoef > 0.0) )
10025 maypropfeasible = FALSE;
10026 }
10027 }
10028 }
10029 }
10030
10031 if( *result == SCIP_INFEASIBLE && maypropfeasible )
10032 {
10033 SCIP_Bool success;
10034
10035 SCIP_CALL( proposeFeasibleSolution(scip, conshdlr, conss, nconss, sol, &success) );
10036
10037 /* do not pass solution to NLP heuristic if we made it feasible this way */
10038 if( success )
10039 return SCIP_OKAY;
10040 }
10041
10042 if( *result == SCIP_INFEASIBLE && conshdlrdata->subnlpheur != NULL && sol != NULL && !SCIPisInfinity(scip, maxviol) )
10043 {
10044 SCIP_CALL( SCIPupdateStartpointHeurSubNlp(scip, conshdlrdata->subnlpheur, sol, maxviol) );
10045 }
10046
10047 return SCIP_OKAY;
10048 }
10049
10050
10051 /** domain propagation method of constraint handler */
10052 static
10053 SCIP_DECL_CONSPROP(consPropNonlinear)
10054 { /*lint --e{715}*/
10055 int nchgbds = 0;
10056
10057 SCIP_CALL( propConss(scip, conshdlr, conss, nconss, FALSE, result, &nchgbds) );
10058 assert(nchgbds >= 0);
10059
10060 /* TODO would it make sense to check for redundant constraints? */
10061
10062 return SCIP_OKAY;
10063 }
10064
10065
10066 /** presolving method of constraint handler */
10067 static
10068 SCIP_DECL_CONSPRESOL(consPresolNonlinear)
10069 { /*lint --e{715}*/
10070 SCIP_CONSHDLRDATA* conshdlrdata;
10071 SCIP_Bool infeasible;
10072 int c;
10073
10074 *result = SCIP_DIDNOTFIND;
10075
10076 if( nconss == 0 )
10077 {
10078 *result = SCIP_DIDNOTRUN;
10079 return SCIP_OKAY;
10080 }
10081
10082 conshdlrdata = SCIPconshdlrGetData(conshdlr);
10083 assert(conshdlrdata != NULL);
10084
10085 /* simplify constraints and replace common subexpressions, reinit nlhdlrs */
10086 SCIP_CALL( canonicalizeConstraints(scip, conshdlr, conss, nconss, presoltiming, &infeasible, ndelconss, naddconss, nchgcoefs) );
10087 if( infeasible )
10088 {
10089 *result = SCIP_CUTOFF;
10090 return SCIP_OKAY;
10091 }
10092
10093 /* merge constraints with the same root expression */
10094 if( presoltiming & SCIP_PRESOLTIMING_EXHAUSTIVE )
10095 {
10096 SCIP_Bool success;
10097
10098 SCIP_CALL( presolveMergeConss(scip, conss, nconss, &success) );
10099 if( success )
10100 *result = SCIP_SUCCESS;
10101 }
10102
10103 /* propagate constraints */
10104 SCIP_CALL( propConss(scip, conshdlr, conss, nconss, FALSE, result, nchgbds) );
10105 if( *result == SCIP_CUTOFF )
10106 return SCIP_OKAY;
10107
10108 /* propagate function domains (TODO integrate with simplify?) */
10109 if( (presoltiming & SCIP_PRESOLTIMING_EXHAUSTIVE) || nrounds == 0 )
10110 {
10111 SCIP_RESULT localresult;
10112 SCIP_CALL( propExprDomains(scip, conshdlr, conss, nconss, &localresult, nchgbds) );
10113 if( localresult == SCIP_CUTOFF )
10114 {
10115 *result = SCIP_CUTOFF;
10116 return SCIP_OKAY;
10117 }
10118 if( localresult == SCIP_REDUCEDDOM )
10119 *result = SCIP_REDUCEDDOM;
10120 }
10121
10122 /* check for redundant constraints, remove constraints that are a value expression */
10123 SCIP_CALL( presolveRedundantConss(scip, conshdlr, conss, nconss, &infeasible, ndelconss, nchgbds) );
10124 if( infeasible )
10125 {
10126 *result = SCIP_CUTOFF;
10127 return SCIP_OKAY;
10128 }
10129
10130 /* try to upgrade constraints */
10131 for( c = 0; c < nconss; ++c )
10132 {
10133 SCIP_Bool upgraded;
10134
10135 /* skip inactive and deleted constraints */
10136 if( SCIPconsIsDeleted(conss[c]) || !SCIPconsIsActive(conss[c]) )
10137 continue;
10138
10139 SCIP_CALL( presolveUpgrade(scip, conshdlr, conss[c], &upgraded, nupgdconss, naddconss) );
10140 }
10141
10142 /* try to change continuous variables that appear linearly to be implicit integer */
10143 if( presoltiming & SCIP_PRESOLTIMING_MEDIUM )
10144 {
10145 SCIP_CALL( presolveImplint(scip, conshdlr, conss, nconss, nchgvartypes, &infeasible) );
10146
10147 if( infeasible )
10148 {
10149 SCIPdebugMsg(scip, "presolveImplint() detected infeasibility\n");
10150 *result = SCIP_CUTOFF;
10151 return SCIP_OKAY;
10152 }
10153 }
10154
10155 /* fix variables that are contained in only one nonlinear constraint to their upper or lower bounds, if possible */
10156 if( (presoltiming & SCIP_PRESOLTIMING_EXHAUSTIVE) && SCIPisPresolveFinished(scip)
10157 && !conshdlrdata->checkedvarlocks && conshdlrdata->checkvarlocks != 'd' )
10158 {
10159 /* run this presolving technique only once because we don't want to generate identical bound disjunction
10160 * constraints multiple times
10161 */
10162 conshdlrdata->checkedvarlocks = TRUE;
10163
10164 for( c = 0; c < nconss; ++c )
10165 {
10166 int tmpnchgvartypes = 0;
10167 int tmpnaddconss = 0;
10168
10169 SCIP_CALL( presolveSingleLockedVars(scip, conshdlr, conss[c], &tmpnchgvartypes, &tmpnaddconss, &infeasible) );
10170 SCIPdebugMsg(scip, "presolSingleLockedVars() for %s: nchgvartypes=%d naddconss=%d infeas=%u\n",
10171 SCIPconsGetName(conss[c]), tmpnchgvartypes, tmpnaddconss, infeasible);
10172
10173 if( infeasible )
10174 {
10175 SCIPdebugMsg(scip, "presolSingleLockedVars() detected infeasibility\n");
10176 *result = SCIP_CUTOFF;
10177 return SCIP_OKAY;
10178 }
10179
10180 (*nchgvartypes) += tmpnchgvartypes;
10181 (*naddconss) += tmpnaddconss;
10182 }
10183 }
10184
10185 if( *ndelconss > 0 || *nchgbds > 0 || *nupgdconss > 0 || *naddconss > 0 || *nchgvartypes > 0 )
10186 *result = SCIP_SUCCESS;
10187 else
10188 *result = SCIP_DIDNOTFIND;
10189
10190 return SCIP_OKAY;
10191 }
10192
10193
10194 /** propagation conflict resolving method of constraint handler */
10195 #ifdef SCIP_DISABLED_CODE
10196 static
10197 SCIP_DECL_CONSRESPROP(consRespropNonlinear)
10198 { /*lint --e{715}*/
10199 SCIPerrorMessage("method of nonlinear constraint handler not implemented yet\n");
10200 SCIPABORT(); /*lint --e{527}*/
10201
10202 return SCIP_OKAY;
10203 }
10204 #else
10205 #define consRespropNonlinear NULL
10206 #endif
10207
10208
10209 /** variable rounding lock method of constraint handler */
10210 static
10211 SCIP_DECL_CONSLOCK(consLockNonlinear)
10212 { /*lint --e{715}*/
10213 SCIP_CONSDATA* consdata;
10214 SCIP_EXPR_OWNERDATA* ownerdata;
10215 SCIP_Bool reinitsolve = FALSE;
10216
10217 assert(conshdlr != NULL);
10218 assert(cons != NULL);
10219
10220 consdata = SCIPconsGetData(cons);
10221 assert(consdata != NULL);
10222 assert(consdata->expr != NULL);
10223
10224 ownerdata = SCIPexprGetOwnerData(consdata->expr);
10225
10226 /* check whether we need to initSolve again because
10227 * - we have enfo initialized (nenfos >= 0)
10228 * - and locks appeared (going from zero to nonzero) or disappeared (going from nonzero to zero) now
10229 */
10230 if( ownerdata->nenfos >= 0 )
10231 {
10232 if( (consdata->nlockspos == 0) != (nlockspos == 0) )
10233 reinitsolve = TRUE;
10234 if( (consdata->nlocksneg == 0) != (nlocksneg == 0) )
10235 reinitsolve = TRUE;
10236 }
10237
10238 if( reinitsolve )
10239 {
10240 SCIP_CALL( deinitSolve(scip, conshdlr, &cons, 1) );
10241 }
10242
10243 /* add locks */
10244 SCIP_CALL( addLocks(scip, cons, nlockspos, nlocksneg) );
10245
10246 if( reinitsolve )
10247 {
10248 SCIP_CALL( initSolve(scip, conshdlr, &cons, 1) );
10249 }
10250
10251 return SCIP_OKAY;
10252 }
10253
10254
10255 /** constraint activation notification method of constraint handler */
10256 static
10257 SCIP_DECL_CONSACTIVE(consActiveNonlinear)
10258 { /*lint --e{715}*/
10259 SCIP_CONSDATA* consdata;
10260 SCIP_Bool infeasible = FALSE;
10261
10262 consdata = SCIPconsGetData(cons);
10263 assert(consdata != NULL);
10264
10265 /* simplify root expression if the constraint has been added after presolving */
10266 if( SCIPgetStage(scip) > SCIP_STAGE_EXITPRESOLVE )
10267 {
10268 if( !consdata->issimplified )
10269 {
10270 SCIP_EXPR* simplified;
10271 SCIP_Bool changed;
10272
10273 /* simplify constraint */
10274 SCIP_CALL( SCIPsimplifyExpr(scip, consdata->expr, &simplified, &changed, &infeasible, exprownerCreate, (void*)conshdlr) );
10275 SCIP_CALL( SCIPreleaseExpr(scip, &consdata->expr) );
10276 assert(simplified != NULL);
10277 consdata->expr = simplified;
10278 consdata->issimplified = TRUE;
10279 }
10280
10281 /* ensure that varexprs in consdata->expr are the one from var2expr hashmap */
10282 {
10283 SCIP_CONSHDLRDATA* conshdlrdata;
10284 SCIP_EXPRITER* it;
10285 SCIP_EXPR* expr;
10286
10287 conshdlrdata = SCIPconshdlrGetData(conshdlr);
10288 assert(conshdlrdata != NULL);
10289
10290 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
10291 SCIP_CALL( SCIPexpriterInit(it, consdata->expr, SCIP_EXPRITER_DFS, FALSE) );
10292 SCIPexpriterSetStagesDFS(it, SCIP_EXPRITER_VISITINGCHILD);
10293 for( expr = SCIPexpriterGetCurrent(it); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
10294 {
10295 SCIP_EXPR* child;
10296 SCIP_EXPR* hashmapexpr;
10297
10298 child = SCIPexpriterGetChildExprDFS(it);
10299 if( !SCIPisExprVar(scip, child) )
10300 continue;
10301
10302 /* check which expression is stored in the hashmap for the var of child */
10303 hashmapexpr = (SCIP_EXPR*)SCIPhashmapGetImage(conshdlrdata->var2expr, SCIPgetVarExprVar(child));
10304 /* if a varexpr exists already in the hashmap, but it is child, then replace child by the one in the hashmap */
10305 if( hashmapexpr != NULL && hashmapexpr != child )
10306 {
10307 SCIP_CALL( SCIPreplaceExprChild(scip, expr, SCIPexpriterGetChildIdxDFS(it), hashmapexpr) );
10308 }
10309 }
10310 SCIPfreeExpriter(&it);
10311 }
10312 }
10313
10314 /* store variable expressions */
10315 if( SCIPgetStage(scip) > SCIP_STAGE_TRANSFORMED )
10316 {
10317 SCIP_CALL( storeVarExprs(scip, conshdlr, consdata) );
10318 }
10319
10320 /* add manually locks to constraints that are not checked for feasibility */
10321 if( !SCIPconsIsChecked(cons) )
10322 {
10323 assert(consdata->nlockspos == 0);
10324 assert(consdata->nlocksneg == 0);
10325
10326 SCIP_CALL( addLocks(scip, cons, 1, 0) );
10327 }
10328
10329 if( SCIPgetStage(scip) > SCIP_STAGE_INITPRESOLVE && !infeasible )
10330 {
10331 SCIP_CALL( initSolve(scip, conshdlr, &cons, 1) );
10332 }
10333
10334 /* TODO deal with infeasibility */
10335 assert(!infeasible);
10336
10337 return SCIP_OKAY;
10338 }
10339
10340
10341 /** constraint deactivation notification method of constraint handler */
10342 static
10343 SCIP_DECL_CONSDEACTIVE(consDeactiveNonlinear)
10344 { /*lint --e{715}*/
10345 SCIP_CONSHDLRDATA* conshdlrdata;
10346
10347 conshdlrdata = SCIPconshdlrGetData(conshdlr);
10348 assert(conshdlrdata != NULL);
10349
10350 if( SCIPgetStage(scip) < SCIP_STAGE_EXITSOLVE )
10351 {
10352 SCIP_CALL( deinitSolve(scip, conshdlr, &cons, 1) );
10353 }
10354
10355 if( SCIPgetStage(scip) > SCIP_STAGE_TRANSFORMED )
10356 {
10357 SCIP_CALL( dropVarEvents(scip, conshdlrdata->eventhdlr, cons) );
10358 SCIP_CALL( freeVarExprs(scip, SCIPconsGetData(cons)) );
10359 }
10360
10361 /* remove locks that have been added in consActiveExpr() */
10362 if( !SCIPconsIsChecked(cons) )
10363 {
10364 SCIP_CALL( addLocks(scip, cons, -1, 0) );
10365
10366 assert(SCIPconsGetData(cons)->nlockspos == 0);
10367 assert(SCIPconsGetData(cons)->nlocksneg == 0);
10368 }
10369
10370 return SCIP_OKAY;
10371 }
10372
10373
10374 /** constraint enabling notification method of constraint handler */
10375 static
10376 SCIP_DECL_CONSENABLE(consEnableNonlinear)
10377 { /*lint --e{715}*/
10378 SCIP_CONSHDLRDATA* conshdlrdata;
10379
10380 conshdlrdata = SCIPconshdlrGetData(conshdlr);
10381 assert(conshdlrdata != NULL);
10382
10383 if( SCIPgetStage(scip) >= SCIP_STAGE_TRANSFORMED )
10384 {
10385 SCIP_CALL( catchVarEvents(scip, conshdlrdata->eventhdlr, cons) );
10386 }
10387
10388 return SCIP_OKAY;
10389 }
10390
10391
10392 /** constraint disabling notification method of constraint handler */
10393 static
10394 SCIP_DECL_CONSDISABLE(consDisableNonlinear)
10395 { /*lint --e{715}*/
10396 SCIP_CONSHDLRDATA* conshdlrdata;
10397
10398 conshdlrdata = SCIPconshdlrGetData(conshdlr);
10399 assert(conshdlrdata != NULL);
10400
10401 if( SCIPgetStage(scip) >= SCIP_STAGE_TRANSFORMED )
10402 {
10403 SCIP_CALL( dropVarEvents(scip, conshdlrdata->eventhdlr, cons) );
10404 }
10405
10406 return SCIP_OKAY;
10407 }
10408
10409 /** variable deletion of constraint handler */
10410 #ifdef SCIP_DISABLED_CODE
10411 static
10412 SCIP_DECL_CONSDELVARS(consDelvarsNonlinear)
10413 { /*lint --e{715}*/
10414 SCIPerrorMessage("method of nonlinear constraint handler not implemented yet\n");
10415 SCIPABORT(); /*lint --e{527}*/
10416
10417 return SCIP_OKAY;
10418 }
10419 #else
10420 #define consDelvarsNonlinear NULL
10421 #endif
10422
10423
10424 /** constraint display method of constraint handler */
10425 static
10426 SCIP_DECL_CONSPRINT(consPrintNonlinear)
10427 { /*lint --e{715}*/
10428 SCIP_CONSDATA* consdata;
10429
10430 consdata = SCIPconsGetData(cons);
10431 assert(consdata != NULL);
10432 assert(consdata->expr != NULL);
10433
10434 /* print left hand side for ranged constraints */
10435 if( !SCIPisInfinity(scip, -consdata->lhs) && !SCIPisInfinity(scip, consdata->rhs) && !SCIPisEQ(scip, consdata->lhs, consdata->rhs) )
10436 {
10437 SCIPinfoMessage(scip, file, "%.15g <= ", consdata->lhs);
10438 }
10439
10440 /* print expression */
10441 SCIP_CALL( SCIPprintExpr(scip, consdata->expr, file) );
10442
10443 /* print right hand side */
10444 if( SCIPisEQ(scip, consdata->lhs, consdata->rhs) )
10445 SCIPinfoMessage(scip, file, " == %.15g", consdata->rhs);
10446 else if( !SCIPisInfinity(scip, consdata->rhs) )
10447 SCIPinfoMessage(scip, file, " <= %.15g", consdata->rhs);
10448 else if( !SCIPisInfinity(scip, -consdata->lhs) )
10449 SCIPinfoMessage(scip, file, " >= %.15g", consdata->lhs);
10450 else
10451 SCIPinfoMessage(scip, file, " [free]");
10452
10453 return SCIP_OKAY;
10454 }
10455
10456
10457 /** constraint copying method of constraint handler */
10458 static
10459 SCIP_DECL_CONSCOPY(consCopyNonlinear)
10460 { /*lint --e{715}*/
10461 SCIP_CONSHDLR* targetconshdlr;
10462 SCIP_EXPR* targetexpr = NULL;
10463 SCIP_CONSDATA* sourcedata;
10464
10465 assert(cons != NULL);
10466
10467 sourcedata = SCIPconsGetData(sourcecons);
10468 assert(sourcedata != NULL);
10469
10470 targetconshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
10471 assert(targetconshdlr != NULL);
10472
10473 SCIP_CALL( SCIPcopyExpr(sourcescip, scip, sourcedata->expr, &targetexpr, exprownerCreate, (void*)targetconshdlr, varmap, consmap, global, valid) );
10474
10475 if( targetexpr == NULL )
10476 *valid = FALSE;
10477
10478 *cons = NULL;
10479 if( *valid )
10480 {
10481 /* create copy (only capture targetexpr, no need to copy again) */
10482 SCIP_CALL( createCons(scip, targetconshdlr, cons, name != NULL ? name : SCIPconsGetName(sourcecons),
10483 targetexpr, sourcedata->lhs, sourcedata->rhs, FALSE,
10484 initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable) );
10485 }
10486
10487 if( targetexpr != NULL )
10488 {
10489 /* release target expr */
10490 SCIP_CALL( SCIPreleaseExpr(scip, &targetexpr) );
10491 }
10492
10493 return SCIP_OKAY;
10494 }
10495
10496
10497 /** constraint parsing method of constraint handler */
10498 static
10499 SCIP_DECL_CONSPARSE(consParseNonlinear)
10500 { /*lint --e{715}*/
10501 SCIP_Real lhs;
10502 SCIP_Real rhs;
10503 const char* endptr;
10504 SCIP_EXPR* consexprtree;
10505
10506 SCIPdebugMsg(scip, "cons_nonlinear::consparse parsing %s\n", str);
10507
10508 assert(scip != NULL);
10509 assert(success != NULL);
10510 assert(str != NULL);
10511 assert(name != NULL);
10512 assert(cons != NULL);
10513
10514 *success = FALSE;
10515
10516 /* return if string empty */
10517 if( !*str )
10518 return SCIP_OKAY;
10519
10520 endptr = str;
10521
10522 /* set left and right hand side to their default values */
10523 lhs = -SCIPinfinity(scip);
10524 rhs = SCIPinfinity(scip);
10525
10526 /* parse constraint to get lhs, rhs, and expression in between (from cons_linear.c::consparse, but parsing whole string first, then getting expression) */
10527
10528 /* check for left hand side */
10529 if( isdigit((unsigned char)str[0]) || ((str[0] == '-' || str[0] == '+') && isdigit((unsigned char)str[1])) )
10530 {
10531 /* there is a number coming, maybe it is a left-hand-side */
10532 if( !SCIPstrToRealValue(str, &lhs, (char**)&endptr) )
10533 {
10534 SCIPerrorMessage("error parsing number from <%s>\n", str);
10535 return SCIP_READERROR;
10536 }
10537
10538 /* ignore whitespace */
10539 while( isspace((unsigned char)*endptr) )
10540 ++endptr;
10541
10542 if( endptr[0] != '<' || endptr[1] != '=' )
10543 {
10544 /* no '<=' coming, so it was the beginning of the expression and not a left-hand-side */
10545 lhs = -SCIPinfinity(scip);
10546 }
10547 else
10548 {
10549 /* it was indeed a left-hand-side, so continue parsing after it */
10550 str = endptr + 2;
10551
10552 /* ignore whitespace */
10553 while( isspace((unsigned char)*str) )
10554 ++str;
10555 }
10556 }
10557
10558 SCIPdebugMsg(scip, "str should start at beginning of expr: %s\n", str);
10559
10560 /* parse expression: so far we did not allocate memory, so can just return in case of readerror */
10561 SCIP_CALL( SCIPparseExpr(scip, &consexprtree, str, &str, exprownerCreate, (void*)conshdlr) );
10562
10563 /* check for left or right hand side */
10564 while( isspace((unsigned char)*str) )
10565 ++str;
10566
10567 /* check for free constraint */
10568 if( strncmp(str, "[free]", 6) == 0 )
10569 {
10570 if( !SCIPisInfinity(scip, -lhs) )
10571 {
10572 SCIPerrorMessage("cannot have left hand side and [free] status \n");
10573 SCIP_CALL( SCIPreleaseExpr(scip, &consexprtree) );
10574 return SCIP_OKAY;
10575 }
10576 *success = TRUE;
10577 }
10578 else
10579 {
10580 switch( *str )
10581 {
10582 case '<':
10583 *success = SCIPstrToRealValue(str+2, &rhs, (char**)&endptr);
10584 break;
10585 case '=':
10586 if( !SCIPisInfinity(scip, -lhs) )
10587 {
10588 SCIPerrorMessage("cannot have == on rhs if there was a <= on lhs\n");
10589 SCIP_CALL( SCIPreleaseExpr(scip, &consexprtree) );
10590 return SCIP_OKAY;
10591 }
10592 else
10593 {
10594 *success = SCIPstrToRealValue(str+2, &rhs, (char**)&endptr);
10595 lhs = rhs;
10596 }
10597 break;
10598 case '>':
10599 if( !SCIPisInfinity(scip, -lhs) )
10600 {
10601 SCIPerrorMessage("cannot have => on rhs if there was a <= on lhs\n");
10602 SCIP_CALL( SCIPreleaseExpr(scip, &consexprtree) );
10603 return SCIP_OKAY;
10604 }
10605 else
10606 {
10607 *success = SCIPstrToRealValue(str+2, &lhs, (char**)&endptr);
10608 break;
10609 }
10610 case '\0':
10611 *success = TRUE;
10612 break;
10613 default:
10614 SCIPerrorMessage("unexpected character %c\n", *str);
10615 SCIP_CALL( SCIPreleaseExpr(scip, &consexprtree) );
10616 return SCIP_OKAY;
10617 }
10618 }
10619
10620 /* create constraint */
10621 SCIP_CALL( createCons(scip, conshdlr, cons, name,
10622 consexprtree, lhs, rhs, FALSE,
10623 initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable) );
10624 assert(*cons != NULL);
10625
10626 SCIP_CALL( SCIPreleaseExpr(scip, &consexprtree) );
10627
10628 SCIPdebugMsg(scip, "created nonlinear constraint: <%s>\n", SCIPconsGetName(*cons));
10629
10630 return SCIP_OKAY;
10631 }
10632
10633
10634 /** constraint method of constraint handler which returns the variables (if possible) */
10635 static
10636 SCIP_DECL_CONSGETVARS(consGetVarsNonlinear)
10637 { /*lint --e{715}*/
10638 SCIP_CONSDATA* consdata;
10639 int i;
10640
10641 consdata = SCIPconsGetData(cons);
10642 assert(consdata != NULL);
10643
10644 /* store variable expressions if not done so far */
10645 SCIP_CALL( storeVarExprs(scip, conshdlr, consdata) );
10646
10647 /* check whether array is too small in order to store all variables */
10648 if( varssize < consdata->nvarexprs )
10649 {
10650 *success = FALSE;
10651 return SCIP_OKAY;
10652 }
10653
10654 for( i = 0; i < consdata->nvarexprs; ++i )
10655 {
10656 vars[i] = SCIPgetVarExprVar(consdata->varexprs[i]);
10657 assert(vars[i] != NULL);
10658 }
10659
10660 *success = TRUE;
10661
10662 return SCIP_OKAY;
10663 }
10664
10665 /** constraint method of constraint handler which returns the number of variables (if possible) */
10666 static
10667 SCIP_DECL_CONSGETNVARS(consGetNVarsNonlinear)
10668 { /*lint --e{715}*/
10669 SCIP_CONSDATA* consdata;
10670
10671 consdata = SCIPconsGetData(cons);
10672 assert(consdata != NULL);
10673
10674 /* store variable expressions if not done so far */
10675 SCIP_CALL( storeVarExprs(scip, conshdlr, consdata) );
10676
10677 *nvars = consdata->nvarexprs;
10678 *success = TRUE;
10679
10680 return SCIP_OKAY;
10681 }
10682
10683 /** constraint handler method to suggest dive bound changes during the generic diving algorithm */
10684 #ifdef SCIP_DISABLED_CODE
10685 static
10686 SCIP_DECL_CONSGETDIVEBDCHGS(consGetDiveBdChgsNonlinear)
10687 { /*lint --e{715}*/
10688 SCIPerrorMessage("method of nonlinear constraint handler not implemented yet\n");
10689 SCIPABORT(); /*lint --e{527}*/
10690
10691 return SCIP_OKAY;
10692 }
10693 #else
10694 #define consGetDiveBdChgsNonlinear NULL
10695 #endif
10696
10697 /** output method of statistics table to output file stream 'file' */
10698 static
10699 SCIP_DECL_TABLEOUTPUT(tableOutputNonlinear)
10700 { /*lint --e{715}*/
10701 SCIP_CONSHDLR* conshdlr;
10702 SCIP_CONSHDLRDATA* conshdlrdata;
10703
10704 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
10705 assert(conshdlr != NULL);
10706
10707 conshdlrdata = SCIPconshdlrGetData(conshdlr);
10708 assert(conshdlrdata != NULL);
10709
10710 /* print statistics for constraint handler */
10711 SCIPinfoMessage(scip, file, "Nonlinear Conshdlr : %10s %10s %10s %10s %10s %10s %10s\n", "WeakSepa", "TightenLP", "DespTghtLP", "DespBranch", "DespCutoff", "ForceLP", "CanonTime");
10712 SCIPinfoMessage(scip, file, " enforce%-10s:", "");
10713 SCIPinfoMessage(scip, file, " %10lld", conshdlrdata->nweaksepa);
10714 SCIPinfoMessage(scip, file, " %10lld", conshdlrdata->ntightenlp);
10715 SCIPinfoMessage(scip, file, " %10lld", conshdlrdata->ndesperatetightenlp);
10716 SCIPinfoMessage(scip, file, " %10lld", conshdlrdata->ndesperatebranch);
10717 SCIPinfoMessage(scip, file, " %10lld", conshdlrdata->ndesperatecutoff);
10718 SCIPinfoMessage(scip, file, " %10lld", conshdlrdata->nforcelp);
10719 SCIPinfoMessage(scip, file, "\n");
10720 SCIPinfoMessage(scip, file, " presolve%-9s: %-65s", "", "");
10721 SCIPinfoMessage(scip, file, " %10.2f", SCIPgetClockTime(scip, conshdlrdata->canonicalizetime));
10722 SCIPinfoMessage(scip, file, "\n");
10723
10724 return SCIP_OKAY;
10725 }
10726
10727 /** output method of statistics table to output file stream 'file' */
10728 static
10729 SCIP_DECL_TABLEOUTPUT(tableOutputNlhdlr)
10730 { /*lint --e{715}*/
10731 SCIP_CONSHDLR* conshdlr;
10732 SCIP_CONSHDLRDATA* conshdlrdata;
10733
10734 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
10735 assert(conshdlr != NULL);
10736
10737 /* skip nlhdlr table if there never were active nonlinear constraints */
10738 if( SCIPconshdlrGetMaxNActiveConss(conshdlr) == 0 )
10739 return SCIP_OKAY;
10740
10741 conshdlrdata = SCIPconshdlrGetData(conshdlr);
10742 assert(conshdlrdata != NULL);
10743
10744 /* print statistics for nonlinear handlers */
10745 SCIPnlhdlrPrintStatistics(scip, conshdlrdata->nlhdlrs, conshdlrdata->nnlhdlrs, file);
10746
10747 return SCIP_OKAY;
10748 }
10749
10750 /** execution method of display nlhdlrs dialog */
10751 static
10752 SCIP_DECL_DIALOGEXEC(dialogExecDisplayNlhdlrs)
10753 { /*lint --e{715}*/
10754 SCIP_CONSHDLR* conshdlr;
10755 SCIP_CONSHDLRDATA* conshdlrdata;
10756 int i;
10757
10758 /* add dialog to history of dialogs that have been executed */
10759 SCIP_CALL( SCIPdialoghdlrAddHistory(dialoghdlr, dialog, NULL, FALSE) );
10760
10761 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
10762 assert(conshdlr != NULL);
10763
10764 conshdlrdata = SCIPconshdlrGetData(conshdlr);
10765 assert(conshdlrdata != NULL);
10766
10767 /* display list of nonlinear handler */
10768 SCIPdialogMessage(scip, NULL, "\n");
10769 SCIPdialogMessage(scip, NULL, " nonlinear handler enabled detectprio enforceprio description\n");
10770 SCIPdialogMessage(scip, NULL, " ----------------- ------- ---------- ----------- -----------\n");
10771 for( i = 0; i < conshdlrdata->nnlhdlrs; ++i )
10772 {
10773 SCIP_NLHDLR* nlhdlr = conshdlrdata->nlhdlrs[i];
10774 assert(nlhdlr != NULL);
10775
10776 SCIPdialogMessage(scip, NULL, " %-17s ", SCIPnlhdlrGetName(nlhdlr));
10777 SCIPdialogMessage(scip, NULL, " %7s ", SCIPnlhdlrIsEnabled(nlhdlr) ? "yes" : "no");
10778 SCIPdialogMessage(scip, NULL, " %10d ", SCIPnlhdlrGetDetectPriority(nlhdlr));
10779 SCIPdialogMessage(scip, NULL, " %11d ", SCIPnlhdlrGetEnfoPriority(nlhdlr));
10780 SCIPdialogMessage(scip, NULL, " %s", SCIPnlhdlrGetDesc(nlhdlr));
10781 SCIPdialogMessage(scip, NULL, "\n");
10782 }
10783 SCIPdialogMessage(scip, NULL, "\n");
10784
10785 /* next dialog will be root dialog again */
10786 *nextdialog = SCIPdialoghdlrGetRoot(dialoghdlr);
10787
10788 return SCIP_OKAY;
10789 }
10790
10791 /*
10792 * constraint handler specific interface methods
10793 */
10794
10795 /** creates the handler for nonlinear constraints and includes it in SCIP */
10796 SCIP_RETCODE SCIPincludeConshdlrNonlinear(
10797 SCIP* scip /**< SCIP data structure */
10798 )
10799 {
10800 SCIP_CONSHDLRDATA* conshdlrdata;
10801 SCIP_DIALOG* parentdialog;
10802
10803 /* create nonlinear constraint handler data */
10804 SCIP_CALL( SCIPallocClearMemory(scip, &conshdlrdata) );
10805 conshdlrdata->intevalvar = intEvalVarBoundTightening;
10806 conshdlrdata->curboundstag = 1;
10807 conshdlrdata->lastboundrelax = 1;
10808 conshdlrdata->curpropboundstag = 1;
10809 conshdlrdata->newsoleventfilterpos = -1;
10810 SCIP_CALL( SCIPcreateClock(scip, &conshdlrdata->canonicalizetime) );
10811 SCIP_CALL( SCIPqueueCreate(&conshdlrdata->reversepropqueue, 100, 2.0) );
10812 SCIP_CALL( SCIPhashmapCreate(&conshdlrdata->var2expr, SCIPblkmem(scip), 100) );
10813
10814 /* include constraint handler */
10815 SCIP_CALL( SCIPincludeConshdlr(scip, CONSHDLR_NAME, CONSHDLR_DESC,
10816 CONSHDLR_SEPAPRIORITY, CONSHDLR_ENFOPRIORITY, CONSHDLR_CHECKPRIORITY,
10817 CONSHDLR_SEPAFREQ, CONSHDLR_PROPFREQ, CONSHDLR_EAGERFREQ, CONSHDLR_MAXPREROUNDS,
10818 CONSHDLR_DELAYSEPA, CONSHDLR_DELAYPROP, CONSHDLR_NEEDSCONS,
10819 CONSHDLR_PROP_TIMING, CONSHDLR_PRESOLTIMING,
10820 conshdlrCopyNonlinear,
10821 consFreeNonlinear, consInitNonlinear, consExitNonlinear,
10822 consInitpreNonlinear, consExitpreNonlinear, consInitsolNonlinear, consExitsolNonlinear,
10823 consDeleteNonlinear, consTransNonlinear, consInitlpNonlinear,
10824 consSepalpNonlinear, consSepasolNonlinear, consEnfolpNonlinear, consEnforelaxNonlinear, consEnfopsNonlinear, consCheckNonlinear,
10825 consPropNonlinear, consPresolNonlinear, consRespropNonlinear, consLockNonlinear,
10826 consActiveNonlinear, consDeactiveNonlinear,
10827 consEnableNonlinear, consDisableNonlinear, consDelvarsNonlinear,
10828 consPrintNonlinear, consCopyNonlinear, consParseNonlinear,
10829 consGetVarsNonlinear, consGetNVarsNonlinear, consGetDiveBdChgsNonlinear, conshdlrdata) );
10830
10831 /* add nonlinear constraint handler parameters */
10832 /* TODO organize into more subcategories */
10833 SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/maxproprounds",
10834 "limit on number of propagation rounds for a set of constraints within one round of SCIP propagation",
10835 &conshdlrdata->maxproprounds, FALSE, 10, 0, INT_MAX, NULL, NULL) );
10836
10837 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/propauxvars",
10838 "whether to check bounds of all auxiliary variable to seed reverse propagation",
10839 &conshdlrdata->propauxvars, TRUE, TRUE, NULL, NULL) );
10840
10841 SCIP_CALL( SCIPaddCharParam(scip, "constraints/" CONSHDLR_NAME "/varboundrelax",
10842 "strategy on how to relax variable bounds during bound tightening: relax (n)ot, relax by (a)bsolute value, relax always by a(b)solute value, relax by (r)relative value",
10843 &conshdlrdata->varboundrelax, TRUE, 'r', "nabr", NULL, NULL) );
10844
10845 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/varboundrelaxamount",
10846 "by how much to relax variable bounds during bound tightening if strategy 'a', 'b', or 'r'",
10847 &conshdlrdata->varboundrelaxamount, TRUE, SCIPepsilon(scip), 0.0, 1.0, NULL, NULL) );
10848
10849 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/conssiderelaxamount",
10850 "by how much to relax constraint sides during bound tightening",
10851 &conshdlrdata->conssiderelaxamount, TRUE, SCIPepsilon(scip), 0.0, 1.0, NULL, NULL) );
10852
10853 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/vpmaxperturb",
10854 "maximal relative perturbation of reference point when computing facet of envelope of vertex-polyhedral function (dim>2)",
10855 &conshdlrdata->vp_maxperturb, TRUE, VERTEXPOLY_MAXPERTURBATION, 0.0, 1.0, NULL, NULL) );
10856
10857 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/vpadjfacetthresh",
10858 "adjust computed facet of envelope of vertex-polyhedral function up to a violation of this value times LP feasibility tolerance",
10859 &conshdlrdata->vp_adjfacetthreshold, TRUE, VERTEXPOLY_ADJUSTFACETFACTOR, 0.0, SCIP_REAL_MAX, NULL, NULL) );
10860
10861 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/vpdualsimplex",
10862 "whether to use dual simplex instead of primal simplex for LP that computes facet of vertex-polyhedral function",
10863 &conshdlrdata->vp_dualsimplex, TRUE, VERTEXPOLY_USEDUALSIMPLEX, NULL, NULL) );
10864
10865 SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/bilinmaxnauxexprs",
10866 "maximal number of auxiliary expressions per bilinear term",
10867 &conshdlrdata->bilinmaxnauxexprs, FALSE, BILIN_MAXNAUXEXPRS, 0, INT_MAX, NULL, NULL) );
10868
10869 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/reformbinprods",
10870 "whether to reformulate products of binary variables during presolving",
10871 &conshdlrdata->reformbinprods, FALSE, TRUE, NULL, NULL) );
10872
10873 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/reformbinprodsand",
10874 "whether to use the AND constraint handler for reformulating binary products",
10875 &conshdlrdata->reformbinprodsand, FALSE, TRUE, NULL, NULL) );
10876
10877 SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/reformbinprodsfac",
10878 "minimum number of terms to reformulate bilinear binary products by factorizing variables (<= 1: disabled)",
10879 &conshdlrdata->reformbinprodsfac, FALSE, 50, 1, INT_MAX, NULL, NULL) );
10880
10881 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/forbidmultaggrnlvar",
10882 "whether to forbid multiaggregation of nonlinear variables",
10883 &conshdlrdata->forbidmultaggrnlvar, TRUE, TRUE, NULL, NULL) );
10884
10885 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/tightenlpfeastol",
10886 "whether to tighten LP feasibility tolerance during enforcement, if it seems useful",
10887 &conshdlrdata->tightenlpfeastol, TRUE, TRUE, NULL, NULL) );
10888
10889 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/propinenforce",
10890 "whether to (re)run propagation in enforcement",
10891 &conshdlrdata->propinenforce, TRUE, FALSE, NULL, NULL) );
10892
10893 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/weakcutthreshold",
10894 "threshold for when to regard a cut from an estimator as weak (lower values allow more weak cuts)",
10895 &conshdlrdata->weakcutthreshold, TRUE, 0.2, 0.0, 1.0, NULL, NULL) );
10896
10897 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/strongcutmaxcoef",
10898 "\"strong\" cuts will be scaled to have their maximal coef in [1/strongcutmaxcoef,strongcutmaxcoef]",
10899 &conshdlrdata->strongcutmaxcoef, TRUE, 1000.0, 1.0, SCIPinfinity(scip), NULL, NULL) );
10900
10901 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/strongcutefficacy",
10902 "consider efficacy requirement when deciding whether a cut is \"strong\"",
10903 &conshdlrdata->strongcutefficacy, TRUE, FALSE, NULL, NULL) );
10904
10905 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/forcestrongcut",
10906 "whether to force \"strong\" cuts in enforcement",
10907 &conshdlrdata->forcestrongcut, TRUE, FALSE, NULL, NULL) );
10908
10909 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/enfoauxviolfactor",
10910 "an expression will be enforced if the \"auxiliary\" violation is at least this factor times the \"original\" violation",
10911 &conshdlrdata->enfoauxviolfactor, TRUE, 0.01, 0.0, 1.0, NULL, NULL) );
10912
10913 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/weakcutminviolfactor",
10914 "retry enfo of constraint with weak cuts if violation is least this factor of maximal violated constraints",
10915 &conshdlrdata->weakcutminviolfactor, TRUE, 0.5, 0.0, 2.0, NULL, NULL) );
10916
10917 SCIP_CALL( SCIPaddCharParam(scip, "constraints/" CONSHDLR_NAME "/rownotremovable",
10918 "whether to make rows to be non-removable in the node where they are added (can prevent some cycling): 'o'ff, in 'e'nforcement only, 'a'lways",
10919 &conshdlrdata->rownotremovable, TRUE, 'o', "oea", NULL, NULL) );
10920
10921 SCIP_CALL( SCIPaddCharParam(scip, "constraints/" CONSHDLR_NAME "/violscale",
10922 "method how to scale violations to make them comparable (not used for feasibility check): (n)one, (a)ctivity and side, norm of (g)radient",
10923 &conshdlrdata->violscale, TRUE, 'n', "nag", NULL, NULL) );
10924
10925 SCIP_CALL( SCIPaddCharParam(scip, "constraints/" CONSHDLR_NAME "/checkvarlocks",
10926 "whether variables contained in a single constraint should be forced to be at their lower or upper bounds ('d'isable, change 't'ype, add 'b'ound disjunction)",
10927 &conshdlrdata->checkvarlocks, TRUE, 't', "bdt", NULL, NULL) );
10928
10929 SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/branching/aux",
10930 "from which depth on in the tree to allow branching on auxiliary variables (variables added for extended formulation)",
10931 &conshdlrdata->branchauxmindepth, FALSE, INT_MAX, 0, INT_MAX, NULL, NULL) );
10932
10933 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/branching/external",
10934 "whether to use external branching candidates and branching rules for branching",
10935 &conshdlrdata->branchexternal, FALSE, FALSE, NULL, NULL) );
10936
10937 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/branching/highviolfactor",
10938 "consider a constraint highly violated if its violation is >= this factor * maximal violation among all constraints",
10939 &conshdlrdata->branchhighviolfactor, FALSE, 0.0, 0.0, 1.0, NULL, NULL) );
10940
10941 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/branching/highscorefactor",
10942 "consider a variable branching score high if its branching score >= this factor * maximal branching score among all variables",
10943 &conshdlrdata->branchhighscorefactor, FALSE, 0.9, 0.0, 1.0, NULL, NULL) );
10944
10945 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/branching/violweight",
10946 "weight by how much to consider the violation assigned to a variable for its branching score",
10947 &conshdlrdata->branchviolweight, FALSE, 1.0, 0.0, SCIPinfinity(scip), NULL, NULL) );
10948
10949 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/branching/dualweight",
10950 "weight by how much to consider the dual values of rows that contain a variable for its branching score",
10951 &conshdlrdata->branchdualweight, FALSE, 0.0, 0.0, SCIPinfinity(scip), NULL, NULL) );
10952
10953 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/branching/pscostweight",
10954 "weight by how much to consider the pseudo cost of a variable for its branching score",
10955 &conshdlrdata->branchpscostweight, FALSE, 1.0, 0.0, SCIPinfinity(scip), NULL, NULL) );
10956
10957 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/branching/domainweight",
10958 "weight by how much to consider the domain width in branching score",
10959 &conshdlrdata->branchdomainweight, FALSE, 0.0, 0.0, SCIPinfinity(scip), NULL, NULL) );
10960
10961 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/branching/vartypeweight",
10962 "weight by how much to consider variable type (continuous: 0, binary: 1, integer: 0.1, impl-integer: 0.01) in branching score",
10963 &conshdlrdata->branchvartypeweight, FALSE, 0.5, 0.0, SCIPinfinity(scip), NULL, NULL) );
10964
10965 SCIP_CALL( SCIPaddCharParam(scip, "constraints/" CONSHDLR_NAME "/branching/scoreagg",
10966 "how to aggregate several branching scores given for the same expression: 'a'verage, 'm'aximum, 's'um",
10967 &conshdlrdata->branchscoreagg, FALSE, 's', "ams", NULL, NULL) );
10968
10969 SCIP_CALL( SCIPaddCharParam(scip, "constraints/" CONSHDLR_NAME "/branching/violsplit",
10970 "method used to split violation in expression onto variables: 'u'niform, 'm'idness of solution, 'd'omain width, 'l'ogarithmic domain width",
10971 &conshdlrdata->branchviolsplit, FALSE, 'm', "umdl", NULL, NULL) );
10972
10973 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/branching/pscostreliable",
10974 "minimum pseudo-cost update count required to consider pseudo-costs reliable",
10975 &conshdlrdata->branchpscostreliable, FALSE, 2.0, 0.0, SCIPinfinity(scip), NULL, NULL) );
10976
10977 SCIP_CALL( SCIPaddCharParam(scip, "constraints/" CONSHDLR_NAME "/linearizeheursol",
10978 "whether tight linearizations of nonlinear constraints should be added to cutpool when some heuristics finds a new solution ('o'ff, on new 'i'ncumbents, on 'e'very solution)",
10979 &conshdlrdata->linearizeheursol, FALSE, 'o', "oie", NULL, NULL) );
10980
10981 /* include handler for bound change events */
10982 SCIP_CALL( SCIPincludeEventhdlrBasic(scip, &conshdlrdata->eventhdlr, CONSHDLR_NAME "_boundchange",
10983 "signals a bound change to a nonlinear constraint", processVarEvent, NULL) );
10984 assert(conshdlrdata->eventhdlr != NULL);
10985
10986 /* include tables for statistics */
10987 assert(SCIPfindTable(scip, TABLE_NAME_NONLINEAR) == NULL);
10988 SCIP_CALL( SCIPincludeTable(scip, TABLE_NAME_NONLINEAR, TABLE_DESC_NONLINEAR, FALSE,
10989 NULL, NULL, NULL, NULL, NULL, NULL, tableOutputNonlinear,
10990 NULL, TABLE_POSITION_NONLINEAR, TABLE_EARLIEST_STAGE_NONLINEAR) );
10991
10992 assert(SCIPfindTable(scip, TABLE_NAME_NLHDLR) == NULL);
10993 SCIP_CALL( SCIPincludeTable(scip, TABLE_NAME_NLHDLR, TABLE_DESC_NLHDLR, TRUE,
10994 NULL, NULL, NULL, NULL, NULL, NULL, tableOutputNlhdlr,
10995 NULL, TABLE_POSITION_NLHDLR, TABLE_EARLIEST_STAGE_NLHDLR) );
10996
10997 /* create, include, and release display nlhdlrs dialog */
10998 if( SCIPgetRootDialog(scip) != NULL && SCIPdialogFindEntry(SCIPgetRootDialog(scip), "display", &parentdialog) == 1 )
10999 {
11000 SCIP_DIALOG* dialog;
11001
11002 assert(parentdialog != NULL);
11003 assert(!SCIPdialogHasEntry(parentdialog, DIALOG_NAME));
11004
11005 SCIP_CALL( SCIPincludeDialog(scip, &dialog,
11006 NULL, dialogExecDisplayNlhdlrs, NULL, NULL,
11007 DIALOG_NAME, DIALOG_DESC, DIALOG_ISSUBMENU, NULL) );
11008 SCIP_CALL( SCIPaddDialogEntry(scip, parentdialog, dialog) );
11009 SCIP_CALL( SCIPreleaseDialog(scip, &dialog) );
11010 }
11011
11012 SCIP_CALL( SCIPincludeEventhdlrBasic(scip, NULL, CONSHDLR_NAME "_newsolution", "handles the event that a new primal solution has been found",
11013 processNewSolutionEvent, NULL) );
11014
11015 return SCIP_OKAY;
11016 }
11017
11018 /** includes a nonlinear constraint upgrade method into the nonlinear constraint handler */
11019 SCIP_RETCODE SCIPincludeConsUpgradeNonlinear(
11020 SCIP* scip, /**< SCIP data structure */
11021 SCIP_DECL_NONLINCONSUPGD((*nlconsupgd)), /**< method to call for upgrading nonlinear constraint */
11022 int priority, /**< priority of upgrading method */
11023 SCIP_Bool active, /**< should the upgrading method by active by default? */
11024 const char* conshdlrname /**< name of the constraint handler */
11025 )
11026 {
11027 SCIP_CONSHDLR* conshdlr;
11028 SCIP_CONSHDLRDATA* conshdlrdata;
11029 CONSUPGRADE* consupgrade;
11030 char paramname[SCIP_MAXSTRLEN];
11031 char paramdesc[SCIP_MAXSTRLEN];
11032 int i;
11033
11034 assert(conshdlrname != NULL );
11035 assert(nlconsupgd != NULL);
11036
11037 /* find the nonlinear constraint handler */
11038 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
11039 if( conshdlr == NULL )
11040 {
11041 SCIPerrorMessage("nonlinear constraint handler not found\n");
11042 return SCIP_PLUGINNOTFOUND;
11043 }
11044
11045 conshdlrdata = SCIPconshdlrGetData(conshdlr);
11046 assert(conshdlrdata != NULL);
11047
11048 /* check whether upgrade method exists already */
11049 for( i = conshdlrdata->nconsupgrades - 1; i >= 0; --i )
11050 {
11051 if( conshdlrdata->consupgrades[i]->consupgd == nlconsupgd )
11052 {
11053 #ifdef SCIP_DEBUG
11054 SCIPwarningMessage(scip, "Try to add already known upgrade method for constraint handler <%s>.\n", conshdlrname);
11055 #endif
11056 return SCIP_OKAY;
11057 }
11058 }
11059
11060 /* create a nonlinear constraint upgrade data object */
11061 SCIP_CALL( SCIPallocBlockMemory(scip, &consupgrade) );
11062 consupgrade->consupgd = nlconsupgd;
11063 consupgrade->priority = priority;
11064 consupgrade->active = active;
11065
11066 /* insert nonlinear constraint upgrade method into constraint handler data */
11067 SCIP_CALL( SCIPensureBlockMemoryArray(scip, &conshdlrdata->consupgrades, &conshdlrdata->consupgradessize, conshdlrdata->nconsupgrades+1) );
11068 assert(conshdlrdata->nconsupgrades+1 <= conshdlrdata->consupgradessize);
11069
11070 for( i = conshdlrdata->nconsupgrades; i > 0 && conshdlrdata->consupgrades[i-1]->priority < consupgrade->priority; --i )
11071 conshdlrdata->consupgrades[i] = conshdlrdata->consupgrades[i-1];
11072 assert(0 <= i && i <= conshdlrdata->nconsupgrades);
11073 conshdlrdata->consupgrades[i] = consupgrade;
11074 conshdlrdata->nconsupgrades++;
11075
11076 /* adds parameter to turn on and off the upgrading step */
11077 (void) SCIPsnprintf(paramname, SCIP_MAXSTRLEN, "constraints/" CONSHDLR_NAME "/upgrade/%s", conshdlrname);
11078 (void) SCIPsnprintf(paramdesc, SCIP_MAXSTRLEN, "enable nonlinear upgrading for constraint handler <%s>", conshdlrname);
11079 SCIP_CALL( SCIPaddBoolParam(scip,
11080 paramname, paramdesc,
11081 &consupgrade->active, FALSE, active, NULL, NULL) );
11082
11083 return SCIP_OKAY;
11084 }
11085
11086 /** creates and captures a nonlinear constraint
11087 *
11088 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
11089 */
11090 SCIP_RETCODE SCIPcreateConsNonlinear(
11091 SCIP* scip, /**< SCIP data structure */
11092 SCIP_CONS** cons, /**< pointer to hold the created constraint */
11093 const char* name, /**< name of constraint */
11094 SCIP_EXPR* expr, /**< expression of constraint (must not be NULL) */
11095 SCIP_Real lhs, /**< left hand side of constraint */
11096 SCIP_Real rhs, /**< right hand side of constraint */
11097 SCIP_Bool initial, /**< should the LP relaxation of constraint be in the initial LP?
11098 * Usually set to TRUE. Set to FALSE for 'lazy constraints'. */
11099 SCIP_Bool separate, /**< should the constraint be separated during LP processing?
11100 * Usually set to TRUE. */
11101 SCIP_Bool enforce, /**< should the constraint be enforced during node processing?
11102 * TRUE for model constraints, FALSE for additional, redundant constraints. */
11103 SCIP_Bool check, /**< should the constraint be checked for feasibility?
11104 * TRUE for model constraints, FALSE for additional, redundant constraints. */
11105 SCIP_Bool propagate, /**< should the constraint be propagated during node processing?
11106 * Usually set to TRUE. */
11107 SCIP_Bool local, /**< is constraint only valid locally?
11108 * Usually set to FALSE. Has to be set to TRUE, e.g., for branching constraints. */
11109 SCIP_Bool modifiable, /**< is constraint modifiable (subject to column generation)?
11110 * Usually set to FALSE. In column generation applications, set to TRUE if pricing
11111 * adds coefficients to this constraint. */
11112 SCIP_Bool dynamic, /**< is constraint subject to aging?
11113 * Usually set to FALSE. Set to TRUE for own cuts which
11114 * are separated as constraints. */
11115 SCIP_Bool removable /**< should the relaxation be removed from the LP due to aging or cleanup?
11116 * Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */
11117 )
11118 {
11119 /* TODO: (optional) modify the definition of the SCIPcreateConsNonlinear() call, if you don't need all the information */
11120 SCIP_CONSHDLR* conshdlr;
11121
11122 /* find the nonlinear constraint handler */
11123 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
11124 if( conshdlr == NULL )
11125 {
11126 SCIPerrorMessage("nonlinear constraint handler not found\n");
11127 return SCIP_PLUGINNOTFOUND;
11128 }
11129
11130 /* create constraint */
11131 SCIP_CALL( createCons(scip, conshdlr, cons, name, expr, lhs, rhs, TRUE,
11132 initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable) );
11133
11134 return SCIP_OKAY;
11135 }
11136
11137 /** creates and captures a nonlinear constraint with all its constraint flags set to their default values
11138 *
11139 * All flags can be set via SCIPconsSetFLAGNAME-methods.
11140 *
11141 * @see SCIPcreateConsNonlinear() for information about the basic constraint flag configuration.
11142 *
11143 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
11144 */
11145 SCIP_RETCODE SCIPcreateConsBasicNonlinear(
11146 SCIP* scip, /**< SCIP data structure */
11147 SCIP_CONS** cons, /**< pointer to hold the created constraint */
11148 const char* name, /**< name of constraint */
11149 SCIP_EXPR* expr, /**< expression of constraint (must not be NULL) */
11150 SCIP_Real lhs, /**< left hand side of constraint */
11151 SCIP_Real rhs /**< right hand side of constraint */
11152 )
11153 {
11154 SCIP_CALL( SCIPcreateConsNonlinear(scip, cons, name, expr, lhs, rhs,
11155 TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE) );
11156
11157 return SCIP_OKAY;
11158 }
11159
11160 /** creates and captures a quadratic nonlinear constraint
11161 *
11162 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
11163 */
11164 SCIP_RETCODE SCIPcreateConsQuadraticNonlinear(
11165 SCIP* scip, /**< SCIP data structure */
11166 SCIP_CONS** cons, /**< pointer to hold the created constraint */
11167 const char* name, /**< name of constraint */
11168 int nlinvars, /**< number of linear terms */
11169 SCIP_VAR** linvars, /**< array with variables in linear part */
11170 SCIP_Real* lincoefs, /**< array with coefficients of variables in linear part */
11171 int nquadterms, /**< number of quadratic terms */
11172 SCIP_VAR** quadvars1, /**< array with first variables in quadratic terms */
11173 SCIP_VAR** quadvars2, /**< array with second variables in quadratic terms */
11174 SCIP_Real* quadcoefs, /**< array with coefficients of quadratic terms */
11175 SCIP_Real lhs, /**< left hand side of quadratic equation */
11176 SCIP_Real rhs, /**< right hand side of quadratic equation */
11177 SCIP_Bool initial, /**< should the LP relaxation of constraint be in the initial LP?
11178 * Usually set to TRUE. Set to FALSE for 'lazy constraints'. */
11179 SCIP_Bool separate, /**< should the constraint be separated during LP processing?
11180 * Usually set to TRUE. */
11181 SCIP_Bool enforce, /**< should the constraint be enforced during node processing?
11182 * TRUE for model constraints, FALSE for additional, redundant constraints. */
11183 SCIP_Bool check, /**< should the constraint be checked for feasibility?
11184 * TRUE for model constraints, FALSE for additional, redundant constraints. */
11185 SCIP_Bool propagate, /**< should the constraint be propagated during node processing?
11186 * Usually set to TRUE. */
11187 SCIP_Bool local, /**< is constraint only valid locally?
11188 * Usually set to FALSE. Has to be set to TRUE, e.g., for branching constraints. */
11189 SCIP_Bool modifiable, /**< is constraint modifiable (subject to column generation)?
11190 * Usually set to FALSE. In column generation applications, set to TRUE if pricing
11191 * adds coefficients to this constraint. */
11192 SCIP_Bool dynamic, /**< is constraint subject to aging?
11193 * Usually set to FALSE. Set to TRUE for own cuts which
11194 * are separated as constraints. */
11195 SCIP_Bool removable /**< should the relaxation be removed from the LP due to aging or cleanup?
11196 * Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */
11197 )
11198 {
11199 SCIP_CONSHDLR* conshdlr;
11200 SCIP_EXPR* expr;
11201
11202 assert(nlinvars == 0 || (linvars != NULL && lincoefs != NULL));
11203 assert(nquadterms == 0 || (quadvars1 != NULL && quadvars2 != NULL && quadcoefs != NULL));
11204
11205 /* get nonlinear constraint handler */
11206 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
11207 if( conshdlr == NULL )
11208 {
11209 SCIPerrorMessage("nonlinear constraint handler not found\n");
11210 return SCIP_PLUGINNOTFOUND;
11211 }
11212
11213 /* create quadratic expression */
11214 SCIP_CALL( SCIPcreateExprQuadratic(scip, &expr, nlinvars, linvars, lincoefs, nquadterms, quadvars1, quadvars2, quadcoefs, exprownerCreate, (void*)conshdlr) );
11215 assert(expr != NULL);
11216
11217 /* create nonlinear constraint */
11218 SCIP_CALL( createCons(scip, conshdlr, cons, name, expr, lhs, rhs, FALSE,
11219 initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable) );
11220
11221 /* release quadratic expression (captured by constraint now) */
11222 SCIP_CALL( SCIPreleaseExpr(scip, &expr) );
11223
11224 return SCIP_OKAY;
11225 }
11226
11227 /** creates and captures a quadratic nonlinear constraint with all its constraint flags set to their default values
11228 *
11229 * All flags can be set via SCIPconsSetFLAGNAME-methods.
11230 *
11231 * @see SCIPcreateConsQuadraticNonlinear() for information about the basic constraint flag configuration.
11232 *
11233 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
11234 */
11235 SCIP_RETCODE SCIPcreateConsBasicQuadraticNonlinear(
11236 SCIP* scip, /**< SCIP data structure */
11237 SCIP_CONS** cons, /**< pointer to hold the created constraint */
11238 const char* name, /**< name of constraint */
11239 int nlinvars, /**< number of linear terms */
11240 SCIP_VAR** linvars, /**< array with variables in linear part */
11241 SCIP_Real* lincoefs, /**< array with coefficients of variables in linear part */
11242 int nquadterms, /**< number of quadratic terms */
11243 SCIP_VAR** quadvars1, /**< array with first variables in quadratic terms */
11244 SCIP_VAR** quadvars2, /**< array with second variables in quadratic terms */
11245 SCIP_Real* quadcoefs, /**< array with coefficients of quadratic terms */
11246 SCIP_Real lhs, /**< left hand side of quadratic equation */
11247 SCIP_Real rhs /**< right hand side of quadratic equation */
11248 )
11249 {
11250 SCIP_CALL( SCIPcreateConsQuadraticNonlinear(scip, cons, name, nlinvars, linvars, lincoefs, nquadterms, quadvars1, quadvars2, quadcoefs, lhs, rhs,
11251 TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE) );
11252
11253 return SCIP_OKAY;
11254 }
11255
11256 /** creates and captures a signpower nonlinear constraint with all its constraint flags set to their default values
11257 *
11258 * \f$\textrm{lhs} \leq \textrm{sign}(x+a) |x+a|^n + c z \leq \textrm{rhs}\f$
11259 *
11260 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
11261 */
11262 SCIP_RETCODE SCIPcreateConsBasicSignpowerNonlinear(
11263 SCIP* scip, /**< SCIP data structure */
11264 SCIP_CONS** cons, /**< pointer to hold the created constraint */
11265 const char* name, /**< name of constraint */
11266 SCIP_VAR* x, /**< nonlinear variable x in constraint */
11267 SCIP_VAR* z, /**< linear variable z in constraint */
11268 SCIP_Real exponent, /**< exponent n of |x+offset|^n term in constraint */
11269 SCIP_Real xoffset, /**< offset in |x+offset|^n term in constraint */
11270 SCIP_Real zcoef, /**< coefficient of z in constraint */
11271 SCIP_Real lhs, /**< left hand side of constraint */
11272 SCIP_Real rhs /**< right hand side of constraint */
11273 )
11274 {
11275 SCIP_EXPR* xexpr;
11276 SCIP_EXPR* terms[2];
11277 SCIP_Real coefs[2];
11278 SCIP_EXPR* sumexpr;
11279
11280 assert(x != NULL);
11281 assert(z != NULL);
11282
11283 SCIP_CALL( SCIPcreateExprVar(scip, &xexpr, x, NULL, NULL) );
11284 if( xoffset != 0.0 )
11285 {
11286 SCIP_CALL( SCIPcreateExprSum(scip, &sumexpr, 1, &xexpr, NULL, xoffset, NULL, NULL) ); /* x + xoffset */
11287 SCIP_CALL( SCIPcreateExprSignpower(scip, &terms[0], sumexpr, exponent, NULL, NULL) ); /* signpow(x + xoffset, exponent) */
11288
11289 SCIP_CALL( SCIPreleaseExpr(scip, &sumexpr) );
11290 }
11291 else
11292 {
11293 SCIP_CALL( SCIPcreateExprSignpower(scip, &terms[0], xexpr, exponent, NULL, NULL) ); /* signpow(x, exponent) */
11294 }
11295 coefs[0] = 1.0;
11296
11297 SCIP_CALL( SCIPcreateExprVar(scip, &terms[1], z, NULL, NULL) );
11298 coefs[1] = zcoef;
11299
11300 SCIP_CALL( SCIPcreateExprSum(scip, &sumexpr, 2, terms, coefs, 0.0, NULL, NULL) ); /* signpowexpr + zcoef * z */
11301
11302 SCIP_CALL( SCIPcreateConsBasicNonlinear(scip, cons, name, sumexpr, lhs, rhs) );
11303
11304 SCIP_CALL( SCIPreleaseExpr(scip, &sumexpr) );
11305 SCIP_CALL( SCIPreleaseExpr(scip, &terms[1]) );
11306 SCIP_CALL( SCIPreleaseExpr(scip, &terms[0]) );
11307 SCIP_CALL( SCIPreleaseExpr(scip, &xexpr) );
11308
11309 return SCIP_OKAY;
11310 }
11311
11312 /** gets tag indicating current local variable bounds */
11313 SCIP_Longint SCIPgetCurBoundsTagNonlinear(
11314 SCIP_CONSHDLR* conshdlr /**< nonlinear constraint handler */
11315 )
11316 {
11317 SCIP_CONSHDLRDATA* conshdlrdata;
11318
11319 assert(conshdlr != NULL);
11320 conshdlrdata = SCIPconshdlrGetData(conshdlr);
11321
11322 return conshdlrdata->curboundstag;
11323 }
11324
11325 /** gets the `curboundstag` from the last time where variable bounds were relaxed */
11326 SCIP_Longint SCIPgetLastBoundRelaxTagNonlinear(
11327 SCIP_CONSHDLR* conshdlr /**< nonlinear constraint handler */
11328 )
11329 {
11330 SCIP_CONSHDLRDATA* conshdlrdata;
11331
11332 assert(conshdlr != NULL);
11333 conshdlrdata = SCIPconshdlrGetData(conshdlr);
11334
11335 return conshdlrdata->lastboundrelax;
11336 }
11337
11338 /** increments `curboundstag` and resets `lastboundrelax` in constraint handler data
11339 *
11340 * @attention This method is not intended for normal use.
11341 * These tags are maintained by the event handler for variable bound change events.
11342 * This method is used by some unittests.
11343 */
11344 void SCIPincrementCurBoundsTagNonlinear(
11345 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */
11346 SCIP_Bool boundrelax /**< indicates whether a bound was relaxed, i.e., lastboundrelax should be set too */
11347 )
11348 {
11349 SCIP_CONSHDLRDATA* conshdlrdata;
11350
11351 conshdlrdata = SCIPconshdlrGetData(conshdlr);
11352 assert(conshdlrdata != NULL);
11353
11354 ++conshdlrdata->curboundstag;
11355 assert(conshdlrdata->curboundstag > 0);
11356
11357 if( boundrelax )
11358 conshdlrdata->lastboundrelax = conshdlrdata->curboundstag;
11359 }
11360
11361 /** returns the hashmap that is internally used to map variables to their corresponding variable expressions */
11362 SCIP_HASHMAP* SCIPgetVarExprHashmapNonlinear(
11363 SCIP_CONSHDLR* conshdlr /**< nonlinear constraint handler */
11364 )
11365 {
11366 assert(conshdlr != NULL);
11367
11368 return SCIPconshdlrGetData(conshdlr)->var2expr;
11369 }
11370
11371 /** processes a rowprep for cut addition and maybe report branchscores */
11372 SCIP_RETCODE SCIPprocessRowprepNonlinear(
11373 SCIP* scip, /**< SCIP data structure */
11374 SCIP_NLHDLR* nlhdlr, /**< nonlinear handler which provided the estimator */
11375 SCIP_CONS* cons, /**< nonlinear constraint */
11376 SCIP_EXPR* expr, /**< expression */
11377 SCIP_ROWPREP* rowprep, /**< cut to be added */
11378 SCIP_Bool overestimate, /**< whether the expression needs to be over- or underestimated */
11379 SCIP_VAR* auxvar, /**< auxiliary variable */
11380 SCIP_Real auxvalue, /**< current value of expression w.r.t. auxiliary variables as obtained from EVALAUX */
11381 SCIP_Bool allowweakcuts, /**< whether we should only look for "strong" cuts, or anything that separates is fine */
11382 SCIP_Bool branchscoresuccess, /**< whether the estimator generation generated branching scores */
11383 SCIP_Bool inenforcement, /**< whether we are in enforcement, or only in separation */
11384 SCIP_SOL* sol, /**< solution to be separated (NULL for the LP solution) */
11385 SCIP_RESULT* result /**< pointer to store the result */
11386 )
11387 {
11388 SCIP_Real cutviol;
11389 SCIP_CONSHDLRDATA* conshdlrdata;
11390 SCIP_Real auxvarvalue = SCIP_INVALID;
11391 SCIP_Bool sepasuccess;
11392 SCIP_Real estimateval = SCIP_INVALID;
11393 SCIP_Real mincutviolation;
11394
11395 assert(nlhdlr != NULL);
11396 assert(cons != NULL);
11397 assert(expr != NULL);
11398 assert(rowprep != NULL);
11399 assert(auxvar != NULL);
11400 assert(result != NULL);
11401
11402 /* decide on minimal violation of cut */
11403 if( sol == NULL )
11404 mincutviolation = SCIPgetLPFeastol(scip); /* we enforce an LP solution */
11405 else
11406 mincutviolation = SCIPfeastol(scip);
11407
11408 conshdlrdata = SCIPconshdlrGetData(SCIPconsGetHdlr(cons));
11409 assert(conshdlrdata != NULL);
11410
11411 sepasuccess = TRUE;
11412
11413 cutviol = SCIPgetRowprepViolation(scip, rowprep, sol, NULL);
11414 if( cutviol > 0.0 )
11415 {
11416 auxvarvalue = SCIPgetSolVal(scip, sol, auxvar);
11417
11418 /* check whether cut is weak (if f(x) not defined, then it's never weak) */
11419 if( !allowweakcuts && auxvalue != SCIP_INVALID )
11420 {
11421 /* let the estimator be c'x-b, the auxvar is z (=auxvarvalue), and the expression is f(x) (=auxvalue)
11422 * then if we are underestimating and since the cut is violated, we should have z <= c'x-b <= f(x)
11423 * cutviol is c'x-b - z, so estimator value is c'x-b = z + cutviol
11424 * if the estimator value (c'x-b) is too close to z (auxvarvalue), when compared to f(x) (auxvalue),
11425 * then let's call this a weak cut that is, it's a weak cut if c'x-b <= z + weakcutthreshold * (f(x)-z)
11426 * <-> c'x-b - z <= weakcutthreshold * (f(x)-z)
11427 *
11428 * if we are overestimating, we have z >= c'x-b >= f(x)
11429 * cutviol is z - (c'x-b), so estimator value is c'x-b = z - cutviol
11430 * it's weak if c'x-b >= f(x) + (1-weakcutthreshold) * (z - f(x))
11431 * <-> c'x-b - z >= weakcutthreshold * (f(x)-z)
11432 *
11433 * when linearizing convex expressions, then we should have c'x-b = f(x), so they would never be weak
11434 */
11435 if( (!overestimate && ( cutviol <= conshdlrdata->weakcutthreshold * (auxvalue - auxvarvalue))) ||
11436 ( overestimate && (-cutviol >= conshdlrdata->weakcutthreshold * (auxvalue - auxvarvalue))) )
11437 {
11438 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " estimate of nlhdlr %s succeeded, but cut is too "\
11439 "weak: auxvarvalue %g estimateval %g auxvalue %g (over %d)\n",
11440 SCIPnlhdlrGetName(nlhdlr), auxvarvalue,
11441 auxvarvalue + (overestimate ? -cutviol : cutviol), auxvalue, overestimate); )
11442 sepasuccess = FALSE;
11443 }
11444 }
11445
11446 /* save estimator value for later, see long comment above why this gives the value for c'x-b */
11447 estimateval = auxvarvalue + (!overestimate ? cutviol : -cutviol);
11448 }
11449 else
11450 {
11451 sepasuccess = FALSE;
11452 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " estimate of nlhdlr %s succeeded, but cut does not "\
11453 "separate\n", SCIPnlhdlrGetName(nlhdlr)); )
11454 }
11455
11456 /* clean up estimator */
11457 if( sepasuccess )
11458 {
11459 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " estimate of nlhdlr %s succeeded: auxvarvalue %g "\
11460 "estimateval %g auxvalue %g (over %d)\n ", SCIPnlhdlrGetName(nlhdlr), auxvarvalue,
11461 auxvarvalue + (overestimate ? -cutviol : cutviol), auxvalue, overestimate);
11462 SCIPprintRowprep(scip, rowprep, enfologfile); )
11463
11464 /* if not allowweakcuts, then do not attempt to get cuts more violated by scaling them up,
11465 * instead, may even scale them down, that is, scale so that max coef is close to 1
11466 */
11467 if( !allowweakcuts )
11468 {
11469 SCIP_CALL( SCIPcleanupRowprep2(scip, rowprep, sol, conshdlrdata->strongcutmaxcoef, &sepasuccess) );
11470
11471 if( !sepasuccess )
11472 {
11473 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " cleanup cut failed due to bad numerics\n"); )
11474 }
11475 else
11476 {
11477 cutviol = SCIPgetRowprepViolation(scip, rowprep, sol, &sepasuccess);
11478 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " cleanup succeeded, violation = %g and %sreliable, "\
11479 "min requ viol = %g\n", cutviol, sepasuccess ? "" : "not ", mincutviolation); )
11480 if( sepasuccess )
11481 sepasuccess = cutviol > mincutviolation;
11482 }
11483
11484 if( sepasuccess && auxvalue != SCIP_INVALID )
11485 {
11486 /* check whether cut is weak now
11487 * auxvar z may now have a coefficient due to scaling (down) in cleanup - take this into account when
11488 * reconstructing estimateval from cutviol (TODO improve or remove?)
11489 */
11490 SCIP_Real auxvarcoef = 0.0;
11491 int i;
11492
11493 /* get absolute value of coef of auxvar in row - this makes the whole check here more expensive than
11494 * it should be...
11495 */
11496 for( i = 0; i < SCIProwprepGetNVars(rowprep); ++i )
11497 {
11498 if( SCIProwprepGetVars(rowprep)[i] == auxvar )
11499 {
11500 auxvarcoef = REALABS(SCIProwprepGetCoefs(rowprep)[i]);
11501 break;
11502 }
11503 }
11504
11505 if( auxvarcoef == 0.0 ||
11506 (!overestimate && ( cutviol / auxvarcoef <= conshdlrdata->weakcutthreshold * (auxvalue - auxvarvalue))) ||
11507 ( overestimate && (-cutviol / auxvarcoef >= conshdlrdata->weakcutthreshold * (auxvalue - auxvarvalue))) )
11508 {
11509 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " cut is too weak after cleanup: auxvarvalue %g estimateval %g auxvalue %g (over %d)\n",
11510 auxvarvalue, auxvarvalue + (overestimate ? -cutviol : cutviol) / auxvarcoef, auxvalue, overestimate); )
11511 sepasuccess = FALSE;
11512 }
11513 }
11514 }
11515 else
11516 {
11517 /* TODO if violations are really tiny, then maybe handle special (decrease LP feastol, for example) */
11518
11519 /* if estimate didn't report branchscores explicitly, then consider branching on those children for
11520 * which the following cleanup changes coefficients (we had/have this in expr_sum this way)
11521 */
11522 if( !branchscoresuccess )
11523 SCIProwprepRecordModifications(rowprep);
11524
11525 SCIP_CALL( SCIPcleanupRowprep(scip, rowprep, sol, mincutviolation, &cutviol, &sepasuccess) );
11526
11527 if( !sepasuccess )
11528 {
11529 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " cleanup failed, %d coefs modified, cutviol %g\n",
11530 SCIProwprepGetNModifiedVars(rowprep), cutviol); )
11531 }
11532
11533 /* if cleanup left us with a useless cut, then consider branching on variables for which coef were
11534 * changed
11535 */
11536 if( !sepasuccess && !branchscoresuccess && SCIProwprepGetNModifiedVars(rowprep) > 0 )
11537 {
11538 SCIP_Real violscore;
11539
11540 #ifdef BRSCORE_ABSVIOL
11541 violscore = getExprAbsAuxViolation(scip, expr, auxvalue, sol, NULL, NULL);
11542 #else
11543 SCIP_CALL( SCIPgetExprRelAuxViolationNonlinear(scip, expr, auxvalue, sol, &violscore, NULL, NULL) );
11544 #endif
11545 SCIP_CALL( addExprViolScoresAuxVars(scip, expr, violscore, SCIProwprepGetModifiedVars(rowprep), SCIProwprepGetNModifiedVars(rowprep), sol, &branchscoresuccess) );
11546
11547 /* addConsExprExprBranchScoresAuxVars can fail if the only vars for which the coef was changed
11548 * - were fixed,
11549 * - are this expr's auxvar (I don't think it makes sense to branch on that one (would it?)), or
11550 * - if a variable in the rowprep is not in expr (can happen with indicator added by perspective)
11551 * the first case came up again in #3085 and I don't see how to exclude this in the assert,
11552 * so I'm disabling the assert for now
11553 */
11554 /* assert(branchscoresuccess || (rowprep->nmodifiedvars == 1 && rowprep->modifiedvars[0] == auxvar) ||
11555 strcmp(SCIPnlhdlrGetName(nlhdlr), "perspective")==0); */
11556 }
11557 }
11558 }
11559
11560 /* if cut looks good (numerics ok and cutting off solution), then turn into row and add to sepastore */
11561 if( sepasuccess )
11562 {
11563 SCIP_ROW* row;
11564
11565 if( conshdlrdata->branchdualweight > 0.0 )
11566 {
11567 /* store remaining gap |f(x)-estimateval| in row name, which could be used in getDualBranchscore
11568 * skip if gap is zero
11569 */
11570 if( auxvalue == SCIP_INVALID )
11571 strcat(SCIProwprepGetName(rowprep), "_estimategap=inf");
11572 else if( !SCIPisEQ(scip, auxvalue, estimateval) )
11573 {
11574 char gap[40];
11575 (void) sprintf(gap, "_estimategap=%g", REALABS(auxvalue - estimateval));
11576 strcat(SCIProwprepGetName(rowprep), gap);
11577 }
11578 }
11579
11580 SCIP_CALL( SCIPgetRowprepRowCons(scip, &row, rowprep, cons) );
11581
11582 if( !allowweakcuts && conshdlrdata->strongcutefficacy && !SCIPisCutEfficacious(scip, sol, row) )
11583 {
11584 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " cut efficacy %g is too low (minefficacy=%g)\n",
11585 SCIPgetCutEfficacy(scip, sol, row), SCIPgetSepaMinEfficacy(scip)); )
11586 }
11587 else
11588 {
11589 SCIP_Bool infeasible;
11590
11591 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " adding cut ");
11592 SCIP_CALL( SCIPprintRow(scip, row, enfologfile) ); )
11593
11594 /* I take !allowweakcuts as equivalent for having a strong cut (we usually have allowweakcuts=TRUE only
11595 * if we haven't found strong cuts before)
11596 */
11597 SCIP_CALL( SCIPaddRow(scip, row, conshdlrdata->forcestrongcut && !allowweakcuts && inenforcement, &infeasible) );
11598
11599 /* mark row as not removable from LP for current node (this can prevent some cycling) */
11600 if( conshdlrdata->rownotremovable == 'a' || (conshdlrdata->rownotremovable == 'e' && inenforcement) )
11601 SCIPmarkRowNotRemovableLocal(scip, row);
11602
11603 if( infeasible )
11604 {
11605 *result = SCIP_CUTOFF;
11606 SCIPnlhdlrIncrementNCutoffs(nlhdlr);
11607 }
11608 else
11609 {
11610 *result = SCIP_SEPARATED;
11611 SCIPnlhdlrIncrementNSeparated(nlhdlr);
11612 }
11613 }
11614
11615 SCIP_CALL( SCIPreleaseRow(scip, &row) );
11616 }
11617 else if( branchscoresuccess )
11618 {
11619 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " separation with estimate of nlhdlr %s failed, but "\
11620 "branching candidates added\n", SCIPnlhdlrGetName(nlhdlr)); )
11621
11622 /* well, not branched, but addConsExprExprViolScoresAuxVars() added scores to (aux)variables and that makes the
11623 * expressions eligible for branching candidate, see enforceConstraints() and branching()
11624 */
11625 *result = SCIP_BRANCHED;
11626 }
11627 else
11628 {
11629 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " separation with estimate of nlhdlr %s failed and no "\
11630 "branching candidates%s\n", SCIPnlhdlrGetName(nlhdlr), (allowweakcuts && inenforcement) ?
11631 " (!)" : ""); )
11632 }
11633
11634 return SCIP_OKAY;
11635 }
11636
11637 /** collects all bilinear terms for a given set of constraints
11638 *
11639 * @attention This method should only be used for unit tests that depend on SCIPgetBilinTermsNonlinear(),
11640 * SCIPgetBilinTermNonlinear() or SCIPgetBilinTermIdxNonlinear().
11641 */
11642 SCIP_RETCODE SCIPcollectBilinTermsNonlinear(
11643 SCIP* scip, /**< SCIP data structure */
11644 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */
11645 SCIP_CONS** conss, /**< nonlinear constraints */
11646 int nconss /**< total number of nonlinear constraints */
11647 )
11648 {
11649 assert(conshdlr != NULL);
11650 assert(conss != NULL || nconss == 0);
11651
11652 SCIP_CALL( bilinearTermsInsertAll(scip, conshdlr, conss, nconss) );
11653
11654 return SCIP_OKAY;
11655 }
11656
11657 /** returns the total number of bilinear terms that are contained in all nonlinear constraints
11658 *
11659 * @note This method should only be used after auxiliary variables have been created, i.e., after CONSINITLP.
11660 */
11661 int SCIPgetNBilinTermsNonlinear(
11662 SCIP_CONSHDLR* conshdlr /**< nonlinear constraint handler */
11663 )
11664 {
11665 SCIP_CONSHDLRDATA* conshdlrdata;
11666
11667 assert(conshdlr != NULL);
11668
11669 conshdlrdata = SCIPconshdlrGetData(conshdlr);
11670 assert(conshdlrdata != NULL);
11671
11672 return conshdlrdata->nbilinterms;
11673 }
11674
11675 /** returns all bilinear terms that are contained in all nonlinear constraints
11676 *
11677 * @note This method should only be used after auxiliary variables have been created, i.e., after CONSINITLP.
11678 * @note The value of the auxiliary variable of a bilinear term might be NULL, which indicates that the term does not have an auxiliary variable.
11679 */
11680 SCIP_CONSNONLINEAR_BILINTERM* SCIPgetBilinTermsNonlinear(
11681 SCIP_CONSHDLR* conshdlr /**< nonlinear constraint handler */
11682 )
11683 {
11684 SCIP_CONSHDLRDATA* conshdlrdata;
11685
11686 assert(conshdlr != NULL);
11687
11688 conshdlrdata = SCIPconshdlrGetData(conshdlr);
11689 assert(conshdlrdata != NULL);
11690
11691 return conshdlrdata->bilinterms;
11692 }
11693
11694 /** returns the index of the bilinear term representing the product of the two given variables
11695 *
11696 * @note The method should only be used after auxiliary variables have been created, i.e., after CONSINITLP.
11697 * @return The method returns -1 if the variables do not appear bilinearly.
11698 */
11699 int SCIPgetBilinTermIdxNonlinear(
11700 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */
11701 SCIP_VAR* x, /**< first variable */
11702 SCIP_VAR* y /**< second variable */
11703 )
11704 {
11705 SCIP_CONSHDLRDATA* conshdlrdata;
11706 SCIP_CONSNONLINEAR_BILINTERM entry;
11707 int idx;
11708
11709 assert(conshdlr != NULL);
11710 assert(x != NULL);
11711 assert(y != NULL);
11712
11713 conshdlrdata = SCIPconshdlrGetData(conshdlr);
11714 assert(conshdlrdata != NULL);
11715
11716 if( conshdlrdata->bilinhashtable == NULL )
11717 {
11718 return -1;
11719 }
11720
11721 /* ensure that x.index <= y.index */
11722 if( SCIPvarCompare(x, y) == 1 )
11723 {
11724 SCIPswapPointers((void**)&x, (void**)&y);
11725 }
11726 assert(SCIPvarCompare(x, y) < 1);
11727
11728 /* use a new entry to find the image in the bilinear hash table */
11729 entry.x = x;
11730 entry.y = y;
11731 idx = (int)(size_t)SCIPhashtableRetrieve(conshdlrdata->bilinhashtable, (void*)&entry) - 1;
11732 assert(idx >= -1 && idx < conshdlrdata->nbilinterms);
11733 assert(idx < 0 || conshdlrdata->bilinterms[idx].x == x);
11734 assert(idx < 0 || conshdlrdata->bilinterms[idx].y == y);
11735
11736 return idx;
11737 }
11738
11739 /** returns the bilinear term that represents the product of two given variables
11740 *
11741 * @note The method should only be used after auxiliary variables have been created, i.e., after CONSINITLP.
11742 * @return The method returns NULL if the variables do not appear bilinearly.
11743 */
11744 SCIP_CONSNONLINEAR_BILINTERM* SCIPgetBilinTermNonlinear(
11745 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */
11746 SCIP_VAR* x, /**< first variable */
11747 SCIP_VAR* y /**< second variable */
11748 )
11749 {
11750 SCIP_CONSHDLRDATA* conshdlrdata;
11751 int idx;
11752
11753 assert(conshdlr != NULL);
11754 assert(x != NULL);
11755 assert(y != NULL);
11756
11757 conshdlrdata = SCIPconshdlrGetData(conshdlr);
11758 assert(conshdlrdata != NULL);
11759
11760 idx = SCIPgetBilinTermIdxNonlinear(conshdlr, x, y);
11761 assert(idx >= -1 && idx < conshdlrdata->nbilinterms);
11762
11763 if( idx >= 0 )
11764 {
11765 return &conshdlrdata->bilinterms[idx];
11766 }
11767
11768 return NULL;
11769 }
11770
11771 /** evaluates an auxiliary expression for a bilinear term */
11772 SCIP_Real SCIPevalBilinAuxExprNonlinear(
11773 SCIP* scip, /**< SCIP data structure */
11774 SCIP_VAR* x, /**< first variable of the bilinear term */
11775 SCIP_VAR* y, /**< second variable of the bilinear term */
11776 SCIP_CONSNONLINEAR_AUXEXPR* auxexpr, /**< auxiliary expression */
11777 SCIP_SOL* sol /**< solution at which to evaluate (can be NULL) */
11778 )
11779 {
11780 assert(scip != NULL);
11781 assert(x != NULL);
11782 assert(y != NULL);
11783 assert(auxexpr != NULL);
11784 assert(auxexpr->auxvar != NULL);
11785
11786 return auxexpr->cst + auxexpr->coefs[0] * SCIPgetSolVal(scip, sol, auxexpr->auxvar) +
11787 auxexpr->coefs[1] * SCIPgetSolVal(scip, sol, x) + auxexpr->coefs[2] * SCIPgetSolVal(scip, sol, y);
11788 }
11789
11790 /** stores the variables of a bilinear term in the data of the constraint handler */
11791 SCIP_RETCODE SCIPinsertBilinearTermExistingNonlinear(
11792 SCIP* scip, /**< SCIP data structure */
11793 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
11794 SCIP_VAR* x, /**< first variable */
11795 SCIP_VAR* y, /**< second variable */
11796 SCIP_VAR* auxvar, /**< auxiliary variable (might be NULL) */
11797 int nlockspos, /**< number of positive expression locks */
11798 int nlocksneg /**< number of negative expression locks */
11799 )
11800 {
11801 SCIP_CONSHDLRDATA* conshdlrdata;
11802 SCIP_CONSNONLINEAR_BILINTERM* term;
11803 int idx;
11804
11805 assert(conshdlr != NULL);
11806
11807 conshdlrdata = SCIPconshdlrGetData(conshdlr);
11808 assert(conshdlrdata != NULL);
11809
11810 SCIP_CALL( bilinearTermsInsertEntry(scip, conshdlr, x, y, nlockspos, nlocksneg, &idx, TRUE) );
11811
11812 term = &conshdlrdata->bilinterms[idx];
11813 assert(term != NULL);
11814 assert(term->nauxexprs == 0); /* existing terms should be added before implicit terms */
11815 assert(term->aux.var == NULL); /* there should not already be an auxvar, that is, existing terms should exist only once (common subexprs should have been eliminated) */
11816
11817 /* store and capture auxiliary variable */
11818 if( auxvar != NULL )
11819 {
11820 term->aux.var = auxvar;
11821 SCIP_CALL( SCIPcaptureVar(scip, auxvar) );
11822 }
11823
11824 return SCIP_OKAY;
11825 }
11826
11827 /** stores the variables of a bilinear term in the data of the constraint handler */
11828 SCIP_RETCODE SCIPinsertBilinearTermImplicitNonlinear(
11829 SCIP* scip, /**< SCIP data structure */
11830 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
11831 SCIP_VAR* x, /**< first variable */
11832 SCIP_VAR* y, /**< second variable */
11833 SCIP_VAR* auxvar, /**< auxiliary variable (might be NULL) */
11834 SCIP_Real coefx, /**< coefficient of x in the auxiliary expression */
11835 SCIP_Real coefy, /**< coefficient of y in the auxiliary expression */
11836 SCIP_Real coefaux, /**< coefficient of auxvar in the auxiliary expression */
11837 SCIP_Real cst, /**< constant of the auxiliary expression */
11838 SCIP_Bool overestimate /**< whether the auxiliary expression overestimates the bilinear product */
11839 )
11840 {
11841 SCIP_CONSHDLRDATA* conshdlrdata;
11842 SCIP_CONSNONLINEAR_BILINTERM* term;
11843 SCIP_CONSNONLINEAR_AUXEXPR* auxexpr;
11844 int idx;
11845 int nlockspos;
11846 int nlocksneg;
11847 SCIP_Bool added;
11848
11849 assert(conshdlr != NULL);
11850
11851 conshdlrdata = SCIPconshdlrGetData(conshdlr);
11852 assert(conshdlrdata != NULL);
11853
11854 nlockspos = overestimate ? 1 : 0;
11855 nlocksneg = overestimate ? 0 : 1;
11856
11857 SCIP_CALL( bilinearTermsInsertEntry(scip, conshdlr, x, y, nlockspos, nlocksneg, &idx, FALSE) );
11858
11859 term = &conshdlrdata->bilinterms[idx];
11860 assert(term != NULL);
11861 assert(SCIPvarCompare(term->x, term->y) < 1);
11862
11863 if( term->existing && term->nauxexprs == 0 && term->aux.var != NULL )
11864 {
11865 SCIP_CONSNONLINEAR_AUXEXPR* auxvarexpr;
11866 /* this is the case where we are adding an implicitly defined relation for a product that has already
11867 * been explicitly defined; convert auxvar into an auxexpr */
11868
11869 /* nothing to do if we aren't allowed to add more than one auxexpr per term */
11870 if( conshdlrdata->bilinmaxnauxexprs <= 1 )
11871 return SCIP_OKAY;
11872
11873 SCIP_CALL( SCIPallocBlockMemory(scip, &auxvarexpr) );
11874 auxvarexpr->cst = 0.0;
11875 auxvarexpr->coefs[0] = 1.0;
11876 auxvarexpr->coefs[1] = 0.0;
11877 auxvarexpr->coefs[2] = 0.0;
11878 auxvarexpr->auxvar = term->aux.var;
11879 auxvarexpr->underestimate = term->nlocksneg > 0;
11880 auxvarexpr->overestimate = term->nlockspos > 0;
11881
11882 /* before we were working with term->aux.var; now aux.var has been saved and aux.exprs can be initialised to NULL */
11883 term->aux.exprs = NULL;
11884
11885 SCIP_CALL( bilinTermAddAuxExpr(scip, conshdlrdata, term, auxvarexpr, &added) );
11886
11887 /* since there were no auxexprs before and we've already checked for bilinmaxnauxexprs, auxvarexpr should always be added */
11888 assert(added);
11889 }
11890
11891 /* create and add auxexpr */
11892 SCIP_CALL( SCIPallocBlockMemory(scip, &auxexpr) );
11893 auxexpr->underestimate = !overestimate;
11894 auxexpr->overestimate = overestimate;
11895 auxexpr->auxvar = auxvar;
11896 auxexpr->coefs[0] = coefaux;
11897 if( term->x == x )
11898 {
11899 assert(term->y == y);
11900 auxexpr->coefs[1] = coefx;
11901 auxexpr->coefs[2] = coefy;
11902 }
11903 else
11904 {
11905 assert(term->x == y);
11906 assert(term->y == x);
11907 auxexpr->coefs[1] = coefy;
11908 auxexpr->coefs[2] = coefx;
11909 }
11910 auxexpr->cst = cst;
11911 SCIP_CALL( bilinTermAddAuxExpr(scip, conshdlrdata, term, auxexpr, &added) );
11912
11913 if( !added )
11914 {
11915 SCIPfreeBlockMemory(scip, &auxexpr);
11916 }
11917 else if( auxvar != NULL )
11918 { /* capture auxiliary variable */
11919 SCIP_CALL( SCIPcaptureVar(scip, auxvar) );
11920 }
11921
11922 return SCIP_OKAY;
11923 }
11924
11925 /* replication of long comment on SCIPcomputeFacetVertexPolyhedralNonlinear() in cons_nonlinear.h omitted here */
11926 SCIP_RETCODE SCIPcomputeFacetVertexPolyhedralNonlinear(
11927 SCIP* scip, /**< SCIP data structure */
11928 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */
11929 SCIP_Bool overestimate, /**< whether to compute facet of concave (TRUE) or convex (FALSE) envelope */
11930 SCIP_DECL_VERTEXPOLYFUN((*function)), /**< pointer to vertex polyhedral function */
11931 void* fundata, /**< data for function evaluation (can be NULL) */
11932 SCIP_Real* xstar, /**< point to be separated */
11933 SCIP_Real* box, /**< box where to compute facet: should be lb_1, ub_1, lb_2, ub_2... */
11934 int nallvars, /**< half of the length of box */
11935 SCIP_Real targetvalue, /**< target value: no need to compute facet if value in xstar would be worse than this value */
11936 SCIP_Bool* success, /**< buffer to store whether a facet could be computed successfully */
11937 SCIP_Real* facetcoefs, /**< buffer to store coefficients of facet defining inequality; must be an array of length at least nallvars */
11938 SCIP_Real* facetconstant /**< buffer to store constant part of facet defining inequality */
11939 )
11940 {
11941 SCIP_Real* corner;
11942 SCIP_Real* funvals;
11943 int* nonfixedpos;
11944 SCIP_Real maxfaceterror;
11945 int nvars; /* number of nonfixed variables */
11946 unsigned int ncorners;
11947 unsigned int i;
11948 int j;
11949
11950 assert(scip != NULL);
11951 assert(conshdlr != NULL);
11952 assert(function != NULL);
11953 assert(xstar != NULL);
11954 assert(box != NULL);
11955 assert(success != NULL);
11956 assert(facetcoefs != NULL);
11957 assert(facetconstant != NULL);
11958
11959 *success = FALSE;
11960
11961 /* identify fixed variables */
11962 SCIP_CALL( SCIPallocBufferArray(scip, &nonfixedpos, nallvars) );
11963 nvars = 0;
11964 for( j = 0; j < nallvars; ++j )
11965 {
11966 if( SCIPisRelEQ(scip, box[2 * j], box[2 * j + 1]) )
11967 continue;
11968 nonfixedpos[nvars] = j;
11969 nvars++;
11970 }
11971
11972 /* if all variables are fixed, then we could provide something trivial, but that wouldn't be the job of separation
11973 * if too many variables are not fixed, then we do nothing currently
11974 */
11975 if( nvars == 0 || nvars > SCIP_MAXVERTEXPOLYDIM )
11976 {
11977 SCIPwarningMessage(scip, "SCIPcomputeFacetVertexPolyhedralNonlinear() called with %d nonfixed variables. Must be between [1,%d].\n", nvars, SCIP_MAXVERTEXPOLYDIM);
11978 SCIPfreeBufferArray(scip, &nonfixedpos);
11979 return SCIP_OKAY;
11980 }
11981
11982 /* compute f(v^i) for each corner v^i of [l,u] */
11983 ncorners = POWEROFTWO(nvars);
11984 SCIP_CALL( SCIPallocBufferArray(scip, &funvals, ncorners) );
11985 SCIP_CALL( SCIPallocBufferArray(scip, &corner, nallvars) );
11986 for( j = 0; j < nallvars; ++j )
11987 {
11988 if( SCIPisRelEQ(scip, box[2 * j], box[2 * j + 1]) )
11989 corner[j] = (box[2 * j] + box[2 * j + 1]) / 2.0;
11990 }
11991 for( i = 0; i < ncorners; ++i )
11992 {
11993 SCIPdebugMsg(scip, "corner %u: ", i);
11994 for( j = 0; j < nvars; ++j )
11995 {
11996 int varpos = nonfixedpos[j];
11997 /* if j'th bit of row index i is set, then take upper bound on var j, otherwise lower bound var j
11998 * we check this by shifting i for j positions to the right and checking whether the last bit is set
11999 */
12000 if( (i >> j) & 0x1 )
12001 corner[varpos] = box[2 * varpos + 1]; /* ub of var */
12002 else
12003 corner[varpos] = box[2 * varpos ]; /* lb of var */
12004 SCIPdebugMsgPrint(scip, "%g, ", corner[varpos]);
12005 assert(!SCIPisInfinity(scip, REALABS(corner[varpos])));
12006 }
12007
12008 funvals[i] = function(corner, nallvars, fundata);
12009
12010 SCIPdebugMsgPrint(scip, "obj = %e\n", funvals[i]);
12011
12012 if( funvals[i] == SCIP_INVALID || SCIPisInfinity(scip, REALABS(funvals[i])) )
12013 {
12014 SCIPdebugMsg(scip, "cannot compute underestimator; function value at corner is too large %g\n", funvals[i]);
12015 goto CLEANUP;
12016 }
12017 }
12018
12019 /* clear coefs array; below we only fill in coefs for nonfixed variables */
12020 BMSclearMemoryArray(facetcoefs, nallvars);
12021
12022 if( nvars == 1 )
12023 {
12024 SCIP_CALL( computeVertexPolyhedralFacetUnivariate(scip, box[2 * nonfixedpos[0]], box[2 * nonfixedpos[0] + 1], funvals[0], funvals[1], success, &facetcoefs[nonfixedpos[0]], facetconstant) );
12025
12026 /* check whether target has been missed */
12027 if( *success && overestimate == (*facetconstant + facetcoefs[nonfixedpos[0]] * xstar[nonfixedpos[0]] > targetvalue) )
12028 {
12029 SCIPdebugMsg(scip, "computed secant, but missed target %g (facetvalue=%g, overestimate=%u)\n", targetvalue, *facetconstant + facetcoefs[nonfixedpos[0]] * xstar[nonfixedpos[0]], overestimate);
12030 *success = FALSE;
12031 }
12032 }
12033 else if( nvars == 2 )
12034 {
12035 int idx1 = nonfixedpos[0];
12036 int idx2 = nonfixedpos[1];
12037 SCIP_Real p1[2] = { box[2*idx1], box[2*idx2] }; /* corner 0: 0>>0 & 0x1 = 0, 0>>1 & 0x1 = 0 */
12038 SCIP_Real p2[2] = { box[2*idx1+1], box[2*idx2] }; /* corner 1: 1>>0 & 0x1 = 1, 1>>1 & 0x1 = 0 */
12039 SCIP_Real p3[2] = { box[2*idx1], box[2*idx2+1] }; /* corner 2: 2>>0 & 0x1 = 0, 2>>1 & 0x1 = 1 */
12040 SCIP_Real p4[2] = { box[2*idx1+1], box[2*idx2+1] }; /* corner 3: 3>>0 & 0x1 = 1, 3>>1 & 0x1 = 1 */
12041 SCIP_Real xstar2[2] = { xstar[idx1], xstar[idx2] };
12042 SCIP_Real coefs[2] = { 0.0, 0.0 };
12043
12044 SCIP_CALL( computeVertexPolyhedralFacetBivariate(scip, overestimate, p1, p2, p3, p4, funvals[0], funvals[1], funvals[2], funvals[3], xstar2, targetvalue, success, coefs, facetconstant) );
12045
12046 facetcoefs[idx1] = coefs[0];
12047 facetcoefs[idx2] = coefs[1];
12048 }
12049 else
12050 {
12051 SCIP_CALL( computeVertexPolyhedralFacetLP(scip, conshdlr, overestimate, xstar, box, nallvars, nonfixedpos, funvals, nvars, targetvalue, success, facetcoefs, facetconstant) );
12052 }
12053 if( !*success )
12054 {
12055 SCIPdebugMsg(scip, "no success computing facet, %d vars\n", nvars);
12056 goto CLEANUP;
12057 }
12058
12059 /*
12060 * check and adjust facet with the algorithm of Rikun et al.
12061 */
12062
12063 maxfaceterror = computeVertexPolyhedralMaxFacetError(scip, overestimate, funvals, box, nallvars, nvars, nonfixedpos, facetcoefs, *facetconstant);
12064
12065 /* adjust constant part of the facet by maxerror to make it a valid over/underestimator (not facet though) */
12066 if( maxfaceterror > 0.0 )
12067 {
12068 SCIP_CONSHDLRDATA* conshdlrdata;
12069 SCIP_Real midval;
12070 SCIP_Real feastol;
12071
12072 feastol = SCIPgetStage(scip) == SCIP_STAGE_SOLVING ? SCIPgetLPFeastol(scip) : SCIPfeastol(scip);
12073
12074 /* evaluate function in middle point to get some idea for a scaling */
12075 for( j = 0; j < nvars; ++j )
12076 corner[nonfixedpos[j]] = (box[2 * nonfixedpos[j]] + box[2 * nonfixedpos[j] + 1]) / 2.0;
12077 midval = function(corner, nallvars, fundata);
12078 if( midval == SCIP_INVALID )
12079 midval = 1.0;
12080
12081 conshdlrdata = SCIPconshdlrGetData(conshdlr);
12082 assert(conshdlrdata != NULL);
12083
12084 /* there seem to be numerical problems if the error is too large; in this case we reject the facet */
12085 if( maxfaceterror > conshdlrdata->vp_adjfacetthreshold * feastol * fabs(midval) )
12086 {
12087 SCIPdebugMsg(scip, "ignoring facet due to instability, it cuts off a vertex by %g (midval=%g).\n", maxfaceterror, midval);
12088 *success = FALSE;
12089 goto CLEANUP;
12090 }
12091
12092 SCIPdebugMsg(scip, "maximum facet error %g (midval=%g), adjust constant to make cut valid!\n", maxfaceterror, midval);
12093
12094 if( overestimate )
12095 *facetconstant += maxfaceterror;
12096 else
12097 *facetconstant -= maxfaceterror;
12098 }
12099
12100 /* if we made it until here, then we have a nice facet */
12101 assert(*success);
12102
12103 CLEANUP:
12104 /* free allocated memory */
12105 SCIPfreeBufferArray(scip, &corner);
12106 SCIPfreeBufferArray(scip, &funvals);
12107 SCIPfreeBufferArray(scip, &nonfixedpos);
12108
12109 return SCIP_OKAY;
12110 }
12111
12112 /*
12113 * constraint specific interface methods
12114 */
12115
12116 /** returns the expression of the given nonlinear constraint */
12117 SCIP_EXPR* SCIPgetExprNonlinear(
12118 SCIP_CONS* cons /**< constraint data */
12119 )
12120 {
12121 SCIP_CONSDATA* consdata;
12122
12123 assert(cons != NULL);
12124 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0);
12125
12126 consdata = SCIPconsGetData(cons);
12127 assert(consdata != NULL);
12128
12129 return consdata->expr;
12130 }
12131
12132 /** gets the left hand side of a nonlinear constraint */
12133 SCIP_Real SCIPgetLhsNonlinear(
12134 SCIP_CONS* cons /**< constraint data */
12135 )
12136 {
12137 SCIP_CONSDATA* consdata;
12138
12139 assert(cons != NULL);
12140 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0);
12141
12142 consdata = SCIPconsGetData(cons);
12143 assert(consdata != NULL);
12144
12145 return consdata->lhs;
12146 }
12147
12148 /** gets the right hand side of a nonlinear constraint */
12149 SCIP_Real SCIPgetRhsNonlinear(
12150 SCIP_CONS* cons /**< constraint data */
12151 )
12152 {
12153 SCIP_CONSDATA* consdata;
12154
12155 assert(cons != NULL);
12156 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0);
12157
12158 consdata = SCIPconsGetData(cons);
12159 assert(consdata != NULL);
12160
12161 return consdata->rhs;
12162 }
12163
12164 /** gets the nonlinear constraint as a nonlinear row representation. */
12165 SCIP_RETCODE SCIPgetNlRowNonlinear(
12166 SCIP* scip, /**< SCIP data structure */
12167 SCIP_CONS* cons, /**< constraint */
12168 SCIP_NLROW** nlrow /**< pointer to store nonlinear row */
12169 )
12170 {
12171 SCIP_CONSDATA* consdata;
12172
12173 assert(cons != NULL);
12174 assert(nlrow != NULL);
12175 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0);
12176
12177 consdata = SCIPconsGetData(cons);
12178 assert(consdata != NULL);
12179
12180 if( consdata->nlrow == NULL )
12181 {
12182 SCIP_CALL( createNlRow(scip, cons) );
12183 }
12184 assert(consdata->nlrow != NULL);
12185 *nlrow = consdata->nlrow;
12186
12187 return SCIP_OKAY;
12188 }
12189
12190 /** returns the curvature of the expression of a given nonlinear constraint
12191 *
12192 * @note The curvature information is computed during CONSINITSOL.
12193 */
12194 SCIP_EXPRCURV SCIPgetCurvatureNonlinear(
12195 SCIP_CONS* cons /**< constraint data */
12196 )
12197 {
12198 SCIP_CONSDATA* consdata;
12199
12200 assert(cons != NULL);
12201 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0);
12202
12203 consdata = SCIPconsGetData(cons);
12204 assert(consdata != NULL);
12205
12206 return consdata->curv;
12207 }
12208
12209 /** checks whether expression of constraint can be represented as quadratic form
12210 *
12211 * Only sets `*isquadratic` to TRUE if the whole expression is quadratic (in the non-extended formulation) and non-linear.
12212 * That is, the expression in each \ref SCIP_QUADEXPR_QUADTERM will be a variable expressions and
12213 * \ref SCIPgetVarExprVar() can be used to retrieve the variable.
12214 */
12215 SCIP_RETCODE SCIPcheckQuadraticNonlinear(
12216 SCIP* scip, /**< SCIP data structure */
12217 SCIP_CONS* cons, /**< constraint data */
12218 SCIP_Bool* isquadratic /**< buffer to store whether constraint is quadratic */
12219 )
12220 {
12221 SCIP_CONSDATA* consdata;
12222
12223 assert(scip != NULL);
12224 assert(cons != NULL);
12225 assert(isquadratic != NULL);
12226 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0);
12227
12228 consdata = SCIPconsGetData(cons);
12229 assert(consdata != NULL);
12230 assert(consdata->expr != NULL);
12231
12232 /* check whether constraint expression is quadratic in extended formulation */
12233 SCIP_CALL( SCIPcheckExprQuadratic(scip, consdata->expr, isquadratic) );
12234
12235 /* if not quadratic in non-extended formulation, then do indicate quadratic */
12236 if( *isquadratic )
12237 *isquadratic = SCIPexprAreQuadraticExprsVariables(consdata->expr);
12238
12239 return SCIP_OKAY;
12240 }
12241
12242 /** changes left-hand-side of a nonlinear constraint
12243 *
12244 * @attention This method can only be called in the problem stage.
12245 */
12246 SCIP_RETCODE SCIPchgLhsNonlinear(
12247 SCIP* scip, /**< SCIP data structure */
12248 SCIP_CONS* cons, /**< constraint data */
12249 SCIP_Real lhs /**< new left-hand-side */
12250 )
12251 {
12252 SCIP_CONSDATA* consdata;
12253
12254 assert(scip != NULL);
12255 assert(cons != NULL);
12256 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0);
12257
12258 if( SCIPgetStage(scip) != SCIP_STAGE_PROBLEM )
12259 {
12260 SCIPerrorMessage("SCIPchgLhsNonlinear can only be called in problem stage.\n");
12261 return SCIP_INVALIDCALL;
12262 }
12263
12264 /* we should have an original constraint */
12265 assert(SCIPconsIsOriginal(cons));
12266
12267 consdata = SCIPconsGetData(cons);
12268 assert(consdata != NULL);
12269
12270 if( consdata->lhs == lhs )
12271 return SCIP_OKAY;
12272
12273 consdata->lhs = lhs;
12274
12275 /* not sure we care about any of these flags for original constraints */
12276 consdata->ispropagated = FALSE;
12277
12278 return SCIP_OKAY;
12279 }
12280
12281 /** changes right-hand-side of a nonlinear constraint
12282 *
12283 * @attention This method can only be called in the problem stage.
12284 */
12285 SCIP_RETCODE SCIPchgRhsNonlinear(
12286 SCIP* scip, /**< SCIP data structure */
12287 SCIP_CONS* cons, /**< constraint data */
12288 SCIP_Real rhs /**< new right-hand-side */
12289 )
12290 {
12291 SCIP_CONSDATA* consdata;
12292
12293 assert(scip != NULL);
12294 assert(cons != NULL);
12295 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0);
12296
12297 if( SCIPgetStage(scip) != SCIP_STAGE_PROBLEM )
12298 {
12299 SCIPerrorMessage("SCIPchgLhsNonlinear can only be called in problem stage.\n");
12300 return SCIP_INVALIDCALL;
12301 }
12302
12303 /* we should have an original constraint */
12304 assert(SCIPconsIsOriginal(cons));
12305
12306 consdata = SCIPconsGetData(cons);
12307 assert(consdata != NULL);
12308
12309 if( consdata->rhs == rhs )
12310 return SCIP_OKAY;
12311
12312 consdata->rhs = rhs;
12313
12314 /* not sure we care about any of these flags for original constraints */
12315 consdata->ispropagated = FALSE;
12316
12317 return SCIP_OKAY;
12318 }
12319
12320 /** changes expression of a nonlinear constraint
12321 *
12322 * @attention This method can only be called in the problem stage.
12323 */
12324 SCIP_RETCODE SCIPchgExprNonlinear(
12325 SCIP* scip, /**< SCIP data structure */
12326 SCIP_CONS* cons, /**< constraint data */
12327 SCIP_EXPR* expr /**< new expression */
12328 )
12329 {
12330 SCIP_CONSHDLR* conshdlr;
12331 SCIP_CONSDATA* consdata;
12332
12333 assert(scip != NULL);
12334 assert(cons != NULL);
12335 assert(expr != NULL);
12336
12337 if( SCIPgetStage(scip) != SCIP_STAGE_PROBLEM )
12338 {
12339 SCIPerrorMessage("SCIPchgExprNonlinear can only be called in problem stage.\n");
12340 return SCIP_INVALIDCALL;
12341 }
12342
12343 /* we should have an original constraint */
12344 assert(SCIPconsIsOriginal(cons));
12345
12346 conshdlr = SCIPconsGetHdlr(cons);
12347 assert(conshdlr != NULL);
12348 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
12349
12350 consdata = SCIPconsGetData(cons);
12351 assert(consdata != NULL);
12352 assert(consdata->expr != NULL);
12353
12354 /* we should not have collected additional data for the expr
12355 * if some of these asserts fail, we may have to remove it and add some code to keep information up to date
12356 */
12357 assert(consdata->nvarexprs == 0);
12358 assert(consdata->varexprs == NULL);
12359 assert(!consdata->catchedevents);
12360
12361 SCIP_CALL( SCIPreleaseExpr(scip, &consdata->expr) );
12362
12363 /* copy expression, thereby map variables expressions to already existing variables expressions in var2expr map, or augment var2expr map */
12364 SCIP_CALL( SCIPduplicateExpr(scip, expr, &consdata->expr, mapexprvar, conshdlr, exprownerCreate, (void*)conshdlr) );
12365
12366 /* not sure we care about any of these flags for original constraints */
12367 consdata->curv = SCIP_EXPRCURV_UNKNOWN;
12368 consdata->issimplified = FALSE;
12369 consdata->ispropagated = FALSE;
12370
12371 return SCIP_OKAY;
12372 }
12373
12374 /** adds coef * var to nonlinear constraint
12375 *
12376 * @attention This method can only be called in the problem stage.
12377 */
12378 SCIP_RETCODE SCIPaddLinearVarNonlinear(
12379 SCIP* scip, /**< SCIP data structure */
12380 SCIP_CONS* cons, /**< constraint data */
12381 SCIP_VAR* var, /**< variable */
12382 SCIP_Real coef /**< coefficient */
12383 )
12384 {
12385 SCIP_CONSHDLR* conshdlr;
12386 SCIP_CONSDATA* consdata;
12387 SCIP_EXPR* varexpr;
12388
12389 assert(scip != NULL);
12390 assert(cons != NULL);
12391
12392 if( SCIPgetStage(scip) != SCIP_STAGE_PROBLEM )
12393 {
12394 SCIPerrorMessage("SCIPaddLinearVarNonlinear can only be called in problem stage.\n");
12395 return SCIP_INVALIDCALL;
12396 }
12397
12398 /* we should have an original constraint */
12399 assert(SCIPconsIsOriginal(cons));
12400
12401 if( coef == 0.0 )
12402 return SCIP_OKAY;
12403
12404 conshdlr = SCIPconsGetHdlr(cons);
12405 assert(conshdlr != NULL);
12406 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
12407
12408 consdata = SCIPconsGetData(cons);
12409 assert(consdata != NULL);
12410 assert(consdata->expr != NULL);
12411
12412 /* we should not have collected additional data for it
12413 * if some of these asserts fail, we may have to remove it and add some code to keep information up to date
12414 */
12415 assert(consdata->nvarexprs == 0);
12416 assert(consdata->varexprs == NULL);
12417 assert(!consdata->catchedevents);
12418
12419 SCIP_CALL( createExprVar(scip, conshdlr, &varexpr, var) );
12420
12421 /* append to sum, if consdata->expr is sum and not used anywhere else */
12422 if( SCIPexprGetNUses(consdata->expr) == 1 && SCIPisExprSum(scip, consdata->expr) )
12423 {
12424 SCIP_CALL( SCIPappendExprSumExpr(scip, consdata->expr, varexpr, coef) );
12425 }
12426 else
12427 {
12428 /* create new expression = 1 * consdata->expr + coef * var */
12429 SCIP_EXPR* children[2] = { consdata->expr, varexpr };
12430 SCIP_Real coefs[2] = { 1.0, coef };
12431
12432 SCIP_CALL( SCIPcreateExprSum(scip, &consdata->expr, 2, children, coefs, 0.0, exprownerCreate, (void*)conshdlr) );
12433
12434 /* release old root expr */
12435 SCIP_CALL( SCIPreleaseExpr(scip, &children[0]) );
12436 }
12437
12438 SCIP_CALL( SCIPreleaseExpr(scip, &varexpr) );
12439
12440 /* not sure we care about any of these flags for original constraints */
12441 consdata->issimplified = FALSE;
12442 consdata->ispropagated = FALSE;
12443
12444 return SCIP_OKAY;
12445 }
12446
12447 /** adds coef * expr to nonlinear constraint
12448 *
12449 * @attention This method can only be called in the problem stage.
12450 */
12451 SCIP_RETCODE SCIPaddExprNonlinear(
12452 SCIP* scip, /**< SCIP data structure */
12453 SCIP_CONS* cons, /**< nonlinear constraint */
12454 SCIP_EXPR* expr, /**< expression */
12455 SCIP_Real coef /**< coefficient */
12456 )
12457 {
12458 SCIP_CONSHDLR* conshdlr;
12459 SCIP_CONSDATA* consdata;
12460 SCIP_EXPR* exprowned;
12461
12462 assert(scip != NULL);
12463 assert(cons != NULL);
12464
12465 if( SCIPgetStage(scip) != SCIP_STAGE_PROBLEM )
12466 {
12467 SCIPerrorMessage("SCIPaddLinearVarNonlinear can only be called in problem stage.\n");
12468 return SCIP_INVALIDCALL;
12469 }
12470
12471 /* we should have an original constraint */
12472 assert(SCIPconsIsOriginal(cons));
12473
12474 if( coef == 0.0 )
12475 return SCIP_OKAY;
12476
12477 conshdlr = SCIPconsGetHdlr(cons);
12478 assert(conshdlr != NULL);
12479 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
12480
12481 consdata = SCIPconsGetData(cons);
12482 assert(consdata != NULL);
12483 assert(consdata->expr != NULL);
12484
12485 /* we should not have collected additional data for it
12486 * if some of these asserts fail, we may have to remove it and add some code to keep information up to date
12487 */
12488 assert(consdata->nvarexprs == 0);
12489 assert(consdata->varexprs == NULL);
12490 assert(!consdata->catchedevents);
12491
12492 /* copy expression, thereby map variables expressions to already existing variables expressions in var2expr map, or augment var2expr map */
12493 SCIP_CALL( SCIPduplicateExpr(scip, expr, &exprowned, mapexprvar, conshdlr, exprownerCreate, (void*)conshdlr) );
12494
12495 /* append to sum, if consdata->expr is sum and not used anywhere else */
12496 if( SCIPexprGetNUses(consdata->expr) == 1 && SCIPisExprSum(scip, consdata->expr) )
12497 {
12498 SCIP_CALL( SCIPappendExprSumExpr(scip, consdata->expr, exprowned, coef) );
12499 }
12500 else
12501 {
12502 /* create new expression = 1 * consdata->expr + coef * var */
12503 SCIP_EXPR* children[2] = { consdata->expr, exprowned };
12504 SCIP_Real coefs[2] = { 1.0, coef };
12505
12506 SCIP_CALL( SCIPcreateExprSum(scip, &consdata->expr, 2, children, coefs, 0.0, exprownerCreate, (void*)conshdlr) );
12507
12508 /* release old root expr */
12509 SCIP_CALL( SCIPreleaseExpr(scip, &children[0]) );
12510 }
12511
12512 SCIP_CALL( SCIPreleaseExpr(scip, &exprowned) );
12513
12514 /* not sure we care about any of these flags for original constraints */
12515 consdata->issimplified = FALSE;
12516 consdata->ispropagated = FALSE;
12517
12518 return SCIP_OKAY;
12519 }
12520
12521 /** gets absolute violation of nonlinear constraint
12522 *
12523 * This function evaluates the constraints in the given solution.
12524 *
12525 * If this value is at most SCIPfeastol(), the constraint would be considered feasible.
12526 */
12527 SCIP_RETCODE SCIPgetAbsViolationNonlinear(
12528 SCIP* scip, /**< SCIP data structure */
12529 SCIP_CONS* cons, /**< constraint */
12530 SCIP_SOL* sol, /**< solution to check */
12531 SCIP_Real* viol /**< buffer to store computed violation */
12532 )
12533 {
12534 assert(cons != NULL);
12535 assert(viol != NULL);
12536
12537 SCIP_CALL( computeViolation(scip, cons, sol, 0L) );
12538 *viol = getConsAbsViolation(cons);
12539
12540 return SCIP_OKAY;
12541 }
12542
12543 /** gets scaled violation of nonlinear constraint
12544 *
12545 * This function evaluates the constraints in the given solution.
12546 *
12547 * The scaling that is applied to the absolute violation of the constraint
12548 * depends on the setting of parameter constraints/nonlinear/violscale.
12549 */
12550 SCIP_RETCODE SCIPgetRelViolationNonlinear(
12551 SCIP* scip, /**< SCIP data structure */
12552 SCIP_CONS* cons, /**< constraint */
12553 SCIP_SOL* sol, /**< solution to check */
12554 SCIP_Real* viol /**< buffer to store computed violation */
12555 )
12556 {
12557 assert(cons != NULL);
12558 assert(viol != NULL);
12559
12560 SCIP_CALL( computeViolation(scip, cons, sol, 0L) );
12561 SCIP_CALL( getConsRelViolation(scip, cons, viol, sol, 0L) );
12562
12563 return SCIP_OKAY;
12564 }
12565
12566 /** returns a variable that appears linearly that may be decreased without making any other constraint infeasible */
12567 void SCIPgetLinvarMayDecreaseNonlinear(
12568 SCIP* scip, /**< SCIP data structure */
12569 SCIP_CONS* cons, /**< nonlinear constraint */
12570 SCIP_VAR** var, /**< pointer to store the variable */
12571 SCIP_Real* coef /**< pointer to store the coefficient */
12572 )
12573 {
12574 SCIP_CONSDATA* consdata;
12575
12576 assert(cons != NULL);
12577 assert(var != NULL);
12578 assert(coef != NULL);
12579
12580 /* check for a linear variable that can be increased or decreased without harming feasibility */
12581 findUnlockedLinearVar(scip, cons);
12582
12583 consdata = SCIPconsGetData(cons);
12584 assert(consdata != NULL);
12585
12586 *var = consdata->linvardecr;
12587 *coef = consdata->linvardecrcoef;
12588 }
12589
12590 /** returns a variable that appears linearly that may be increased without making any other constraint infeasible */
12591 void SCIPgetLinvarMayIncreaseNonlinear(
12592 SCIP* scip, /**< SCIP data structure */
12593 SCIP_CONS* cons, /**< nonlinear constraint */
12594 SCIP_VAR** var, /**< pointer to store the variable */
12595 SCIP_Real* coef /**< pointer to store the coefficient */
12596 )
12597 {
12598 SCIP_CONSDATA* consdata;
12599
12600 assert(cons != NULL);
12601 assert(var != NULL);
12602 assert(coef != NULL);
12603
12604 /* check for a linear variable that can be increased or decreased without harming feasibility */
12605 findUnlockedLinearVar(scip, cons);
12606
12607 consdata = SCIPconsGetData(cons);
12608 assert(consdata != NULL);
12609
12610 *var = consdata->linvarincr;
12611 *coef = consdata->linvarincrcoef;
12612 }
12613
12614
12615 /*
12616 * Methods for Expressions in Nonlinear Constraints
12617 */
12618
12619 /** returns the number of positive rounding locks of an expression */
12620 int SCIPgetExprNLocksPosNonlinear(
12621 SCIP_EXPR* expr /**< expression */
12622 )
12623 {
12624 assert(expr != NULL);
12625 assert(SCIPexprGetOwnerData(expr) != NULL);
12626
12627 return SCIPexprGetOwnerData(expr)->nlockspos;
12628 }
12629
12630 /** returns the number of negative rounding locks of an expression */
12631 int SCIPgetExprNLocksNegNonlinear(
12632 SCIP_EXPR* expr /**< expression */
12633 )
12634 {
12635 assert(expr != NULL);
12636 assert(SCIPexprGetOwnerData(expr) != NULL);
12637
12638 return SCIPexprGetOwnerData(expr)->nlocksneg;
12639 }
12640
12641 /** returns the variable used for linearizing a given expression (return value might be NULL)
12642 *
12643 * @note for variable expression it returns the corresponding variable
12644 */
12645 SCIP_VAR* SCIPgetExprAuxVarNonlinear(
12646 SCIP_EXPR* expr /**< expression */
12647 )
12648 {
12649 SCIP_EXPR_OWNERDATA* ownerdata;
12650
12651 assert(expr != NULL);
12652
12653 ownerdata = SCIPexprGetOwnerData(expr);
12654 assert(ownerdata != NULL);
12655
12656 return ownerdata->filterpos >= -1 ? SCIPgetVarExprVar(expr) : ownerdata->auxvar;
12657 }
12658
12659 /** returns the number of enforcements for an expression */
12660 int SCIPgetExprNEnfosNonlinear(
12661 SCIP_EXPR* expr /**< expression */
12662 )
12663 {
12664 assert(expr != NULL);
12665 assert(SCIPexprGetOwnerData(expr) != NULL);
12666
12667 return SCIPexprGetOwnerData(expr)->nenfos;
12668 }
12669
12670 /** returns the data for one of the enforcements of an expression */
12671 void SCIPgetExprEnfoDataNonlinear(
12672 SCIP_EXPR* expr, /**< expression */
12673 int idx, /**< position of enforcement in enfos array */
12674 SCIP_NLHDLR** nlhdlr, /**< buffer to store nlhldr */
12675 SCIP_NLHDLREXPRDATA** nlhdlrexprdata, /**< buffer to store nlhdlr data for expression, or NULL */
12676 SCIP_NLHDLR_METHOD* nlhdlrparticipation, /**< buffer to store methods where nonlinear handler participates, or NULL */
12677 SCIP_Bool* sepabelowusesactivity, /**< buffer to store whether sepabelow uses activity of some expression, or NULL */
12678 SCIP_Bool* sepaaboveusesactivity, /**< buffer to store whether sepaabove uses activity of some expression, or NULL */
12679 SCIP_Real* auxvalue /**< buffer to store current auxvalue, or NULL */
12680 )
12681 {
12682 SCIP_EXPR_OWNERDATA* ownerdata;
12683
12684 assert(expr != NULL);
12685
12686 ownerdata = SCIPexprGetOwnerData(expr);
12687 assert(ownerdata != NULL);
12688 assert(idx >= 0);
12689 assert(idx < ownerdata->nenfos);
12690 assert(ownerdata->enfos[idx] != NULL);
12691 assert(nlhdlr != NULL);
12692
12693 *nlhdlr = ownerdata->enfos[idx]->nlhdlr;
12694
12695 if( nlhdlrexprdata != NULL )
12696 *nlhdlrexprdata = ownerdata->enfos[idx]->nlhdlrexprdata;
12697
12698 if( nlhdlrparticipation != NULL )
12699 *nlhdlrparticipation = ownerdata->enfos[idx]->nlhdlrparticipation;
12700
12701 if( sepabelowusesactivity != NULL )
12702 *sepabelowusesactivity = ownerdata->enfos[idx]->sepabelowusesactivity;
12703
12704 if( sepaaboveusesactivity != NULL )
12705 *sepaaboveusesactivity = ownerdata->enfos[idx]->sepaaboveusesactivity;
12706
12707 if( auxvalue != NULL )
12708 *auxvalue = ownerdata->enfos[idx]->auxvalue;
12709 }
12710
12711 /** sets the auxiliary value of expression for one of the enforcements of an expression */
12712 void SCIPsetExprEnfoAuxValueNonlinear(
12713 SCIP_EXPR* expr, /**< expression */
12714 int idx, /**< position of enforcement in enfos array */
12715 SCIP_Real auxvalue /**< the new value of auxval */
12716 )
12717 {
12718 SCIP_EXPR_OWNERDATA* ownerdata;
12719
12720 assert(expr != NULL);
12721
12722 ownerdata = SCIPexprGetOwnerData(expr);
12723 assert(ownerdata != NULL);
12724
12725 assert(idx >= 0);
12726 assert(idx < ownerdata->nenfos);
12727 assert(ownerdata->enfos[idx] != NULL);
12728
12729 ownerdata->enfos[idx]->auxvalue = auxvalue;
12730 }
12731
12732 /** number of nonlinear handlers whose activity computation and propagation methods depend on the activity of the expression
12733 *
12734 * @note This method can only be used after the detection methods of the nonlinear handlers have been called.
12735 */
12736 unsigned int SCIPgetExprNPropUsesActivityNonlinear(
12737 SCIP_EXPR* expr /**< expression */
12738 )
12739 {
12740 assert(expr != NULL);
12741 assert(SCIPexprGetOwnerData(expr) != NULL);
12742
12743 return SCIPexprGetOwnerData(expr)->nactivityusesprop;
12744 }
12745
12746 /** number of nonlinear handlers whose separation methods (estimate or enforcement) depend on the activity of the expression
12747 *
12748 * @note This method can only be used after the detection methods of the nonlinear handlers have been called.
12749 */
12750 unsigned int SCIPgetExprNSepaUsesActivityNonlinear(
12751 SCIP_EXPR* expr /**< expression */
12752 )
12753 {
12754 assert(expr != NULL);
12755 assert(SCIPexprGetOwnerData(expr) != NULL);
12756
12757 return SCIPexprGetOwnerData(expr)->nactivityusessepa;
12758 }
12759
12760 /** number of nonlinear handlers whose separation methods (estimate or enforcement) use auxiliary variable of the expression
12761 *
12762 * @note This method can only be used after the detection methods of the nonlinear handlers have been called.
12763 */
12764 unsigned int SCIPgetExprNAuxvarUsesNonlinear(
12765 SCIP_EXPR* expr /**< expression */
12766 )
12767 {
12768 assert(expr != NULL);
12769 assert(SCIPexprGetOwnerData(expr) != NULL);
12770
12771 return SCIPexprGetOwnerData(expr)->nauxvaruses;
12772 }
12773
12774 /** method to be called by a nlhdlr during NLHDLRDETECT to notify an expression that it will be used
12775 *
12776 * - if `useauxvar` is enabled, then ensures that an auxiliary variable will be created in INITLP
12777 * - if `useactivityforprop` or `useactivityforsepa{below,above}` is enabled, then ensured that activity will be updated for `expr`
12778 * - if `useactivityforprop` is enabled, then increments the count returned by SCIPgetExprNPropUsesActivityNonlinear()
12779 * - if `useactivityforsepa{below,above}` is enabled, then increments the count returned by SCIPgetExprNSepaUsesActivityNonlinear()
12780 * and also increments this count for all variables in the expression.
12781 *
12782 * The distinction into `useactivityforprop` and `useactivityforsepa{below,above}` is to recognize variables which domain influences
12783 * under/overestimators. Domain propagation routines (like OBBT) may invest more work for these variables.
12784 * The distinction into `useactivityforsepabelow` and `useactivityforsepaabove` is to recognize whether a nlhdlr that called this method
12785 * will use activity of `expr` in enfomethod \ref SCIP_NLHDLR_METHOD_SEPABELOW or \ref SCIP_NLHDLR_METHOD_SEPAABOVE.
12786 */
12787 SCIP_RETCODE SCIPregisterExprUsageNonlinear(
12788 SCIP* scip, /**< SCIP data structure */
12789 SCIP_EXPR* expr, /**< expression */
12790 SCIP_Bool useauxvar, /**< whether an auxiliary variable will be used for estimate or cut generation */
12791 SCIP_Bool useactivityforprop, /**< whether activity of expr will be used by domain propagation or activity calculation (inteval) */
12792 SCIP_Bool useactivityforsepabelow, /**< whether activity of expr will be used by underestimation */
12793 SCIP_Bool useactivityforsepaabove /**< whether activity of expr will be used by overestimation */
12794 )
12795 {
12796 SCIP_EXPR_OWNERDATA* ownerdata;
12797
12798 assert(expr != NULL);
12799
12800 ownerdata = SCIPexprGetOwnerData(expr);
12801 assert(ownerdata != NULL);
12802
12803 /* do not store auxvar request for variable expressions */
12804 if( useauxvar && SCIPisExprVar(scip, expr) )
12805 useauxvar = FALSE;
12806
12807 if( ownerdata->nenfos >= 0 &&
12808 ( (ownerdata->nactivityusesprop == 0 && ownerdata->nactivityusessepa == 0 && (useactivityforprop || useactivityforsepabelow || useactivityforsepaabove)) ||
12809 (ownerdata->nauxvaruses == 0 && useauxvar)
12810 ) )
12811 {
12812 /* if we already have ran detect of nlhdlrs on expr (nenfos >= 0), then we need to rerun detection if
12813 * we require additional enforcement methods, that is,
12814 * - activity of expr was not used before but will be used now, or
12815 * - auxiliary variable of expr was not required before but will be used now
12816 */
12817 SCIP_CALL( freeEnfoData(scip, expr, FALSE) );
12818 }
12819
12820 if( useauxvar )
12821 ++ownerdata->nauxvaruses;
12822
12823 if( useactivityforprop )
12824 ++ownerdata->nactivityusesprop;
12825
12826 if( useactivityforsepabelow || useactivityforsepaabove )
12827 ++ownerdata->nactivityusessepa;
12828
12829 /* remember that SCIPregisterExprUsageNonlinear() has been called with useactivityforsepa{below,above}=TRUE; this
12830 * information is used in detectNlhdlr()
12831 */
12832 if( useactivityforsepabelow )
12833 SCIPconshdlrGetData(ownerdata->conshdlr)->registerusesactivitysepabelow = TRUE;
12834 if( useactivityforsepaabove )
12835 SCIPconshdlrGetData(ownerdata->conshdlr)->registerusesactivitysepaabove = TRUE;
12836
12837 if( useactivityforprop )
12838 {
12839 /* if activity will be used for propagation, then make sure there is a valid activity
12840 * this way, we can do a reversepropcall after detectNlhdlr
12841 */
12842 SCIP_CALL( SCIPevalExprActivity(scip, expr) );
12843 }
12844
12845 /* increase the nactivityusedsepa counter for all variables used in the given expression */
12846 if(( useactivityforsepabelow || useactivityforsepaabove) && SCIPexprGetNChildren(expr) > 0 )
12847 {
12848 SCIP_EXPRITER* it;
12849
12850 /* create and initialize iterator */
12851 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
12852 SCIP_CALL( SCIPexpriterInit(it, expr, SCIP_EXPRITER_DFS, FALSE) );
12853
12854 for( ; !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
12855 if( SCIPisExprVar(scip, expr) )
12856 ++SCIPexprGetOwnerData(expr)->nactivityusessepa;
12857
12858 /* free iterator */
12859 SCIPfreeExpriter(&it);
12860 }
12861
12862 return SCIP_OKAY;
12863 }
12864
12865 /** computes absolute violation for auxvar relation in an expression w.r.t. original variables
12866 *
12867 * Assume the expression is f(x), where x are original (i.e., not auxiliary) variables.
12868 * Assume that f(x) is associated with auxiliary variable z.
12869 *
12870 * If there are negative locks, then returns the violation of z ≤ f(x) and sets `violover` to TRUE.
12871 * If there are positive locks, then returns the violation of z ≥ f(x) and sets `violunder` to TRUE.
12872 * Of course, if there both negative and positive locks, then return the violation of z = f(x).
12873 *
12874 * If necessary, f is evaluated in the given solution. If that fails (domain error),
12875 * then `viol` is set to SCIPinfinity() and both `violover` and `violunder` are set to TRUE.
12876 */
12877 SCIP_RETCODE SCIPgetExprAbsOrigViolationNonlinear(
12878 SCIP* scip, /**< SCIP data structure */
12879 SCIP_EXPR* expr, /**< expression */
12880 SCIP_SOL* sol, /**< solution */
12881 SCIP_Longint soltag, /**< tag of solution */
12882 SCIP_Real* viol, /**< buffer to store computed violation */
12883 SCIP_Bool* violunder, /**< buffer to store whether z >= f(x) is violated, or NULL */
12884 SCIP_Bool* violover /**< buffer to store whether z <= f(x) is violated, or NULL */
12885 )
12886 {
12887 assert(scip != NULL);
12888 assert(expr != NULL);
12889 assert(viol != NULL);
12890
12891 /* make sure expression has been evaluated */
12892 SCIP_CALL( SCIPevalExpr(scip, expr, sol, soltag) );
12893
12894 /* get violation from internal method */
12895 *viol = getExprAbsOrigViolation(scip, expr, sol, violunder, violover);
12896
12897 return SCIP_OKAY;
12898 }
12899
12900 /** computes absolute violation for auxvar relation in an expression w.r.t. auxiliary variables
12901 *
12902 * Assume the expression is f(w), where w are auxiliary variables that were introduced by some nlhdlr.
12903 * Assume that f(w) is associated with auxiliary variable z.
12904 *
12905 * If there are negative locks, then returns the violation of z ≤ f(w) and sets `violover` to TRUE.
12906 * If there are positive locks, then returns the violation of z ≥ f(w) and sets `violunder` to TRUE.
12907 * Of course, if there both negative and positive locks, then return the violation of z = f(w).
12908 *
12909 * If the given value of f(w) is SCIP_INVALID, then `viol` is set to SCIPinfinity() and
12910 * both `violover` and `violunder` are set to TRUE.
12911 */
12912 SCIP_RETCODE SCIPgetExprAbsAuxViolationNonlinear(
12913 SCIP* scip, /**< SCIP data structure */
12914 SCIP_EXPR* expr, /**< expression */
12915 SCIP_Real auxvalue, /**< the value of f(w) */
12916 SCIP_SOL* sol, /**< solution that has been evaluated */
12917 SCIP_Real* viol, /**< buffer to store computed violation */
12918 SCIP_Bool* violunder, /**< buffer to store whether z >= f(w) is violated, or NULL */
12919 SCIP_Bool* violover /**< buffer to store whether z <= f(w) is violated, or NULL */
12920 )
12921 {
12922 assert(scip != NULL);
12923 assert(expr != NULL);
12924 assert(viol != NULL);
12925
12926 /* get violation from internal method */
12927 *viol = getExprAbsAuxViolation(scip, expr, auxvalue, sol, violunder, violover);
12928
12929 return SCIP_OKAY;
12930 }
12931
12932
12933 /** computes relative violation for auxvar relation in an expression w.r.t. auxiliary variables
12934 *
12935 * Assume the expression is f(w), where w are auxiliary variables that were introduced by some nlhdlr.
12936 * Assume that f(w) is associated with auxiliary variable z.
12937 *
12938 * Taking the absolute violation from SCIPgetExprAbsAuxViolationNonlinear(), this function returns
12939 * the absolute violation divided by max(1,|f(w)|).
12940 *
12941 * If the given value of f(w) is SCIP_INVALID, then `viol` is set to SCIPinfinity() and
12942 * both `violover` and `violunder` are set to TRUE.
12943 */
12944 SCIP_RETCODE SCIPgetExprRelAuxViolationNonlinear(
12945 SCIP* scip, /**< SCIP data structure */
12946 SCIP_EXPR* expr, /**< expression */
12947 SCIP_Real auxvalue, /**< the value of f(w) */
12948 SCIP_SOL* sol, /**< solution that has been evaluated */
12949 SCIP_Real* viol, /**< buffer to store computed violation */
12950 SCIP_Bool* violunder, /**< buffer to store whether z >= f(w) is violated, or NULL */
12951 SCIP_Bool* violover /**< buffer to store whether z <= f(w) is violated, or NULL */
12952 )
12953 {
12954 assert(scip != NULL);
12955 assert(expr != NULL);
12956 assert(viol != NULL);
12957
12958 /* get violation from internal method */
12959 *viol = getExprAbsAuxViolation(scip, expr, auxvalue, sol, violunder, violover);
12960
12961 if( !SCIPisInfinity(scip, *viol) )
12962 {
12963 assert(auxvalue != SCIP_INVALID);
12964 /* TODO maybe we should rather use max(eps,|auxvalue|)? */
12965 *viol /= MAX(1.0, REALABS(auxvalue));
12966 }
12967
12968 return SCIP_OKAY;
12969 }
12970
12971 /** returns bounds on the expression
12972 *
12973 * This gives an intersection of bounds from
12974 * - activity calculation (SCIPexprGetActivity()), if valid,
12975 * - auxiliary variable, if present,
12976 * - stored by SCIPtightenExprIntervalNonlinear() during domain propagation
12977 *
12978 * @note The returned interval can be empty!
12979 */
12980 SCIP_INTERVAL SCIPgetExprBoundsNonlinear(
12981 SCIP* scip, /**< SCIP data structure */
12982 SCIP_EXPR* expr /**< expression */
12983 )
12984 {
12985 SCIP_EXPR_OWNERDATA* ownerdata;
12986 SCIP_CONSHDLRDATA* conshdlrdata;
12987 SCIP_INTERVAL bounds;
12988
12989 assert(scip != NULL);
12990 assert(expr != NULL);
12991
12992 ownerdata = SCIPexprGetOwnerData(expr);
12993 assert(ownerdata != NULL);
12994
12995 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr);
12996 assert(conshdlrdata != NULL);
12997
12998 /* SCIPdebugMsg(scip, "get bounds expr %p:", expr); */
12999
13000 /* start with propbounds if they belong to current propagation */
13001 if( ownerdata->propboundstag == conshdlrdata->curpropboundstag )
13002 {
13003 bounds = ownerdata->propbounds;
13004 /* SCIPdebugMsgPrint(scip, " propbounds [%.15g,%.15g]", ownerdata->propbounds.inf, ownerdata->propbounds.sup); */
13005 }
13006 else
13007 SCIPintervalSetEntire(SCIP_INTERVAL_INFINITY, &bounds);
13008
13009 if( SCIPexprGetActivityTag(expr) >= conshdlrdata->lastboundrelax )
13010 {
13011 /* apply propbounds to expr activity, but ensure it's not-empty if very close disjoint intervals */
13012 /* SCIPdebugMsgPrint(scip, " activity [%.15g,%.15g]", expr->activity.inf, expr->activity.sup); */
13013 SCIPintervalIntersectEps(&bounds, SCIPepsilon(scip), SCIPexprGetActivity(expr), bounds);
13014 }
13015
13016 if( ownerdata->auxvar != NULL )
13017 {
13018 /* apply auxiliary variable bounds to bounds */
13019 SCIP_INTERVAL auxvarbounds;
13020
13021 auxvarbounds = conshdlrdata->intevalvar(scip, ownerdata->auxvar, conshdlrdata);
13022 /* SCIPdebugMsgPrint(scip, " auxvar [%.15g,%.15g]", auxvarbounds.inf, auxvarbounds.sup); */
13023 SCIPintervalIntersectEps(&bounds, SCIPepsilon(scip), bounds, auxvarbounds);
13024 }
13025
13026 /* SCIPdebugMsgPrint(scip, " -> [%.15g,%.15g]\n", bounds.inf, bounds.sup); */
13027
13028 return bounds;
13029 }
13030
13031 /** informs the expression about new bounds that can be used for reverse-propagation and to tighten bounds of
13032 * corresponding (auxiliary) variable (if any)
13033 *
13034 * @attention this function should only be called during domain propagation in cons_nonlinear
13035 */
13036 SCIP_RETCODE SCIPtightenExprIntervalNonlinear(
13037 SCIP* scip, /**< SCIP data structure */
13038 SCIP_EXPR* expr, /**< expression to be tightened */
13039 SCIP_INTERVAL newbounds, /**< new bounds for the expression */
13040 SCIP_Bool* cutoff, /**< buffer to store whether a cutoff was detected */
13041 int* ntightenings /**< buffer to add the total number of tightenings, or NULL */
13042 )
13043 {
13044 SCIP_EXPR_OWNERDATA* ownerdata;
13045 SCIP_CONSHDLRDATA* conshdlrdata;
13046
13047 assert(scip != NULL);
13048 assert(expr != NULL);
13049 assert(cutoff != NULL);
13050
13051 ownerdata = SCIPexprGetOwnerData(expr);
13052 assert(ownerdata != NULL);
13053 assert(ownerdata->conshdlr != NULL);
13054
13055 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr);
13056 assert(conshdlrdata != NULL);
13057
13058 /* the code below assumes that current activity is valid
13059 * if it turns out that we cannot ensure that, then we should change code
13060 */
13061 assert(SCIPexprGetActivityTag(expr) >= conshdlrdata->lastboundrelax || SCIPintervalIsEntire(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(expr)));
13062 assert(!SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(expr)));
13063
13064 *cutoff = FALSE;
13065
13066 #ifdef DEBUG_PROP
13067 SCIPdebugMsg(scip, "Trying to tighten bounds of expr ");
13068 SCIP_CALL( SCIPprintExpr(scip, expr, NULL) );
13069 SCIPdebugMsgPrint(scip, " with activity [%.15g,%.15g] to [%.15g,%.15g] (force=%d)\n", SCIPexprGetActivity(expr).inf, SCIPexprGetActivity(expr).sup, newbounds.inf, newbounds.sup, conshdlrdata->forceboundtightening);
13070 #endif
13071
13072 if( SCIPexprIsIntegral(expr) )
13073 {
13074 /* apply integrality to new bounds
13075 * it should be ok to use normal ceil() and floor(), but for safety, we use SCIPceil and SCIPfloor for now
13076 */
13077 if( newbounds.inf > -SCIP_INTERVAL_INFINITY )
13078 newbounds.inf = SCIPceil(scip, newbounds.inf);
13079 if( newbounds.sup < SCIP_INTERVAL_INFINITY )
13080 newbounds.sup = SCIPfloor(scip, newbounds.sup);
13081 #ifdef DEBUG_PROP
13082 SCIPdebugMsg(scip, " applied integrality: [%.15g,%.15g]\n", newbounds.inf, newbounds.sup);
13083 #endif
13084 }
13085
13086 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, newbounds) )
13087 {
13088 SCIPdebugMsg(scip, " cut off due to new bounds being empty\n");
13089
13090 *cutoff = TRUE;
13091 return SCIP_OKAY;
13092 }
13093
13094 /* treat the new bounds as empty if either the lower/upper bound is above/below +/- SCIPinfinity() */
13095 if( SCIPisInfinity(scip, newbounds.inf) || SCIPisInfinity(scip, -newbounds.sup) )
13096 {
13097 SCIPdebugMsg(scip, " cut off due to new bounds being beyond infinity\n");
13098
13099 *cutoff = TRUE;
13100 return SCIP_OKAY;
13101 }
13102
13103 /* tighten newbounds w.r.t. existing expr->propbounds or activity */
13104 if( ownerdata->propboundstag == conshdlrdata->curpropboundstag )
13105 {
13106 /* if already having propbounds in expr, then tighten newbounds by propbounds */
13107 SCIPintervalIntersectEps(&newbounds, SCIPepsilon(scip), ownerdata->propbounds, newbounds);
13108 }
13109 else
13110 {
13111 /* first time we have propbounds for expr in this propagation rounds:
13112 * intersect with activity (though don't let it become empty if very close intervals)
13113 */
13114 SCIPintervalIntersectEps(&newbounds, SCIPepsilon(scip), SCIPexprGetActivity(expr), newbounds);
13115 }
13116 #ifdef DEBUG_PROP
13117 SCIPdebugMsg(scip, " applied %s: [%.20g,%.20g]\n", ownerdata->propboundstag == conshdlrdata->curpropboundstag ? "previous propbounds" : "activity", newbounds.inf, newbounds.sup);
13118 #endif
13119
13120 /* check if the new bounds lead to an empty interval */
13121 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, newbounds) )
13122 {
13123 SCIPdebugMsg(scip, " cut off due to empty intersection with previous propbounds or activity\n");
13124
13125 *cutoff = TRUE;
13126 return SCIP_OKAY;
13127 }
13128
13129 /* if expr is not constant or variable, then store newbounds in expr->propbounds
13130 * - for constant, the intersection with activity should have been sufficient to determine infeasibilty
13131 * - for variable, the tightenAuxVarBounds call below should be suffient to have to new bounds acknowledged
13132 */
13133 if( SCIPexprGetNChildren(expr) > 0 )
13134 {
13135 ownerdata->propbounds = newbounds;
13136 ownerdata->propboundstag = conshdlrdata->curpropboundstag;
13137 }
13138
13139 /* if updated propbounds do not allow a sufficient tightening, then do not consider adding to queue for reverse
13140 * propagation or update of auxvar bounds
13141 * TODO? if we first had a considerable tightening and then only get small tightenings under the same
13142 * curpropboundstag, then these will still be considered as isIntervalBetter, since we compare with activity here and
13143 * not with the propbounds as set in the beginning; I'm not sure, though, that comparing always with previous
13144 * propbounds would be better, since a number of small updates to propbounds could eventually lead to a considerable
13145 * one or should we not even update propbounds to newbounds if the update is small?
13146 */
13147 if( !isIntervalBetter(scip, conshdlrdata->forceboundtightening, newbounds, SCIPexprGetActivity(expr)) )
13148 {
13149 #ifdef DEBUG_PROP
13150 SCIPdebugMsg(scip, " new bounds [%g,%g] for expr %p not sufficiently tighter than activity -- not adding to propqueue or tightening auxvar\n", newbounds.inf, newbounds.sup, (void*)expr);
13151 #endif
13152 return SCIP_OKAY;
13153 }
13154
13155 if( SCIPexprGetNChildren(expr) > 0 && !ownerdata->inpropqueue && (ownerdata->nactivityusesprop > 0 || ownerdata->nactivityusessepa > 0 || ownerdata->nenfos < 0) )
13156 {
13157 /* add expression to propagation queue if not there yet and not var or constant and
13158 * if it should have a nlhdlr with a reverseprop callback or nlhdlrs are not initialized yet (nenfos < 0)
13159 */
13160 #ifdef DEBUG_PROP
13161 SCIPdebugMsg(scip, " insert expr <%p> (%s) into reversepropqueue\n", (void*)expr, SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)));
13162 #endif
13163 SCIP_CALL( SCIPqueueInsert(conshdlrdata->reversepropqueue, expr) );
13164 ownerdata->inpropqueue = TRUE;
13165 }
13166
13167 /* update bounds on variable or auxiliary variable */
13168 SCIP_CALL( tightenAuxVarBounds(scip, ownerdata->conshdlr, expr, newbounds, cutoff, ntightenings) );
13169
13170 return SCIP_OKAY;
13171 }
13172
13173 /** mark constraints that include this expression to be propagated again
13174 *
13175 * This can be used by, e.g., nlhdlrs, to trigger a new propagation of constraints without
13176 * a change of variable bounds, e.g., because new information on the expression is available
13177 * that could potentially lead to tighter expression activity values.
13178 *
13179 * Note, that this call marks also constraints for propagation which only share some variable
13180 * with this expression.
13181 */
13182 SCIP_RETCODE SCIPmarkExprPropagateNonlinear(
13183 SCIP* scip, /**< SCIP data structure */
13184 SCIP_EXPR* expr /**< expression to propagate again */
13185 )
13186 {
13187 SCIP_EXPRITER* it;
13188 SCIP_CONSDATA* consdata;
13189 SCIP_EXPR_OWNERDATA* ownerdata;
13190 int c;
13191
13192 assert(scip != NULL);
13193 assert(expr != NULL);
13194
13195 ownerdata = SCIPexprGetOwnerData(expr);
13196 assert(ownerdata != NULL);
13197
13198 SCIPincrementCurBoundsTagNonlinear(ownerdata->conshdlr, FALSE);
13199
13200 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
13201 SCIP_CALL( SCIPexpriterInit(it, expr, SCIP_EXPRITER_DFS, FALSE) );
13202
13203 for( ; !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
13204 {
13205 if( !SCIPisExprVar(scip, expr) )
13206 continue;
13207
13208 ownerdata = SCIPexprGetOwnerData(expr);
13209 assert(ownerdata != NULL);
13210
13211 for( c = 0; c < ownerdata->nconss; ++c )
13212 {
13213 consdata = SCIPconsGetData(ownerdata->conss[c]);
13214 assert(consdata != NULL);
13215 consdata->ispropagated = FALSE;
13216 }
13217 }
13218
13219 SCIPfreeExpriter(&it);
13220
13221 return SCIP_OKAY;
13222 }
13223
13224 /** adds violation-branching score to an expression
13225 *
13226 * Adds a score to the expression-specific violation-branching score, thereby marking it as branching candidate.
13227 * The expression must either be a variable expression or have an aux-variable.
13228 * In the latter case, branching on auxiliary variables must have been enabled.
13229 * In case of doubt, use SCIPaddExprsViolScoreNonlinear(). Roughly, the difference between these functions is that the current
13230 * function adds `violscore` to the expression directly, while SCIPaddExprsViolScoreNonlinear() will split the
13231 * violation score among all the given expressions according to parameter constraints/nonlinear/branching/violsplit.
13232 *
13233 * @see SCIPaddExprsViolScoreNonlinear()
13234 */
13235 void SCIPaddExprViolScoreNonlinear(
13236 SCIP* scip, /**< SCIP data structure */
13237 SCIP_EXPR* expr, /**< expression where to add branching score */
13238 SCIP_Real violscore /**< violation score to add to expression */
13239 )
13240 {
13241 SCIP_EXPR_OWNERDATA* ownerdata;
13242 SCIP_CONSHDLRDATA* conshdlrdata;
13243
13244 assert(scip != NULL);
13245 assert(expr != NULL);
13246 assert(violscore >= 0.0);
13247
13248 ownerdata = SCIPexprGetOwnerData(expr);
13249 assert(ownerdata != NULL);
13250
13251 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr);
13252 assert(conshdlrdata != NULL);
13253
13254 /* if not allowing to branch on auxvars, then expr must be a var-expr */
13255 assert(branchAuxNonlinear(scip, ownerdata->conshdlr) || SCIPisExprVar(scip, expr));
13256 /* if allowing to branch on auxvars, then expr must be a var-expr or have an auxvar */
13257 assert(!branchAuxNonlinear(scip, ownerdata->conshdlr) || SCIPisExprVar(scip, expr) || ownerdata->auxvar != NULL);
13258
13259 /* reset branching score if we are in a different enfo round */
13260 if( ownerdata->violscoretag != conshdlrdata->enforound )
13261 {
13262 ownerdata->violscoresum = violscore;
13263 ownerdata->violscoremax = violscore;
13264 ownerdata->nviolscores = 1;
13265 ownerdata->violscoretag = conshdlrdata->enforound;
13266 return;
13267 }
13268
13269 ownerdata->violscoresum += violscore;
13270 if( violscore > ownerdata->violscoremax )
13271 ownerdata->violscoremax = violscore;
13272 ++ownerdata->nviolscores;
13273 }
13274
13275 /** adds violation-branching score to a set of expressions, distributing the score among all the expressions
13276 *
13277 * Each expression must either be a variable expression or have an aux-variable.
13278 * If branching on aux-variables is disabled, then the violation branching score will be distributed among all
13279 * variables present in `exprs`.
13280 */
13281 SCIP_RETCODE SCIPaddExprsViolScoreNonlinear(
13282 SCIP* scip, /**< SCIP data structure */
13283 SCIP_EXPR** exprs, /**< expressions where to add branching score */
13284 int nexprs, /**< number of expressions */
13285 SCIP_Real violscore, /**< violation score to add to expression */
13286 SCIP_SOL* sol, /**< current solution */
13287 SCIP_Bool* success /**< buffer to store whether at least one violscore was added */
13288 )
13289 {
13290 SCIP_EXPRITER* it;
13291 SCIP_EXPR** varexprs;
13292 SCIP_EXPR* e;
13293 int nvars;
13294 int varssize;
13295 int i;
13296
13297 assert(exprs != NULL || nexprs == 0);
13298 assert(success != NULL);
13299
13300 if( nexprs == 0 )
13301 {
13302 *success = FALSE;
13303 return SCIP_OKAY;
13304 }
13305
13306 /* if allowing to branch on auxiliary variables, then call internal addConsExprExprsViolScore immediately */
13307 if( branchAuxNonlinear(scip, SCIPexprGetOwnerData(exprs[0])->conshdlr) )
13308 {
13309 addExprsViolScore(scip, exprs, nexprs, violscore, sol, success);
13310 return SCIP_OKAY;
13311 }
13312
13313 /* if not allowing to branch on aux vars, then create new array containing var expressions that exprs depend on */
13314 nvars = 0;
13315 varssize = 5;
13316 SCIP_CALL( SCIPallocBufferArray(scip, &varexprs, varssize) );
13317
13318 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
13319 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) );
13320
13321 for( i = 0; i < nexprs; ++i )
13322 {
13323 for( e = SCIPexpriterRestartDFS(it, exprs[i]); !SCIPexpriterIsEnd(it); e = SCIPexpriterGetNext(it) )
13324 {
13325 assert(e != NULL);
13326
13327 if( SCIPisExprVar(scip, e) )
13328 {
13329 /* add variable expression to vars array */
13330 if( varssize == nvars )
13331 {
13332 varssize = SCIPcalcMemGrowSize(scip, nvars + 1);
13333 SCIP_CALL( SCIPreallocBufferArray(scip, &varexprs, varssize) );
13334 }
13335 assert(varssize > nvars);
13336
13337 varexprs[nvars++] = e;
13338 }
13339 }
13340 }
13341
13342 SCIPfreeExpriter(&it);
13343
13344 addExprsViolScore(scip, varexprs, nvars, violscore, sol, success);
13345
13346 SCIPfreeBufferArray(scip, &varexprs);
13347
13348 return SCIP_OKAY;
13349 }
13350
13351 /** gives violation-branching score stored in expression, or 0.0 if no valid score has been stored */
13352 SCIP_Real SCIPgetExprViolScoreNonlinear(
13353 SCIP_EXPR* expr /**< expression */
13354 )
13355 {
13356 SCIP_EXPR_OWNERDATA* ownerdata;
13357 SCIP_CONSHDLRDATA* conshdlrdata;
13358
13359 assert(expr != NULL);
13360
13361 ownerdata = SCIPexprGetOwnerData(expr);
13362 assert(ownerdata != NULL);
13363
13364 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr);
13365 assert(conshdlrdata != NULL);
13366
13367 if( conshdlrdata->enforound != ownerdata->violscoretag )
13368 return 0.0;
13369
13370 if( ownerdata->nviolscores == 0 )
13371 return 0.0;
13372
13373 switch( conshdlrdata->branchscoreagg )
13374 {
13375 case 'a' :
13376 /* average */
13377 return ownerdata->violscoresum / ownerdata->nviolscores;
13378
13379 case 'm' :
13380 /* maximum */
13381 return ownerdata->violscoremax;
13382
13383 case 's' :
13384 /* sum */
13385 return ownerdata->violscoresum;
13386
13387 default:
13388 SCIPerrorMessage("Invalid value %c for branchscoreagg parameter\n", conshdlrdata->branchscoreagg);
13389 SCIPABORT();
13390 return SCIP_INVALID;
13391 }
13392 }
13393
13394 /** returns the partial derivative of an expression w.r.t. a variable (or SCIP_INVALID if there was an evaluation error)
13395 *
13396 * @see SCIPexprGetDerivative()
13397 */
13398 SCIP_Real SCIPgetExprPartialDiffNonlinear(
13399 SCIP* scip, /**< SCIP data structure */
13400 SCIP_EXPR* expr, /**< root expression of constraint used in the last SCIPevalExprGradient() call */
13401 SCIP_VAR* var /**< variable (needs to be in the expression) */
13402 )
13403 {
13404 SCIP_EXPR_OWNERDATA* ownerdata;
13405 SCIP_CONSHDLRDATA* conshdlrdata;
13406 SCIP_EXPR* varexpr;
13407
13408 assert(scip != NULL);
13409 assert(expr != NULL);
13410 assert(var != NULL);
13411
13412 /* return 0.0 for value expression */
13413 if( SCIPisExprValue(scip, expr) )
13414 {
13415 assert(SCIPexprGetDerivative(expr) == 0.0);
13416 return 0.0;
13417 }
13418
13419 /* check if an error occurred during the last SCIPevalExprGradient() call */
13420 if( SCIPexprGetDerivative(expr) == SCIP_INVALID )
13421 return SCIP_INVALID;
13422
13423 ownerdata = SCIPexprGetOwnerData(expr);
13424 assert(ownerdata != NULL);
13425
13426 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr);
13427 assert(conshdlrdata != NULL);
13428
13429 /* use variable to expressions mapping which is stored in the constraint handler data */
13430 assert(SCIPhashmapExists(conshdlrdata->var2expr, var));
13431
13432 varexpr = (SCIP_EXPR*)SCIPhashmapGetImage(conshdlrdata->var2expr, var);
13433 assert(varexpr != NULL);
13434 assert(SCIPisExprVar(scip, varexpr));
13435
13436 /* use difftag to decide whether the variable belongs to the expression */
13437 return (SCIPexprGetDiffTag(expr) != SCIPexprGetDiffTag(varexpr)) ? 0.0 : SCIPexprGetDerivative(varexpr);
13438 }
13439
13440 /** returns the var's coordinate of Hu partial derivative of an expression w.r.t. a variable (or SCIP_INVALID if there was an evaluation error)
13441 *
13442 * @see SCIPexprGetBardot()
13443 */
13444 SCIP_Real SCIPgetExprPartialDiffGradientDirNonlinear(
13445 SCIP* scip, /**< SCIP data structure */
13446 SCIP_EXPR* expr, /**< root expression of constraint used in the last SCIPevalExprHessianDir() call */
13447 SCIP_VAR* var /**< variable (needs to be in the expression) */
13448 )
13449 {
13450 SCIP_EXPR_OWNERDATA* ownerdata;
13451 SCIP_CONSHDLRDATA* conshdlrdata;
13452 SCIP_EXPR* varexpr;
13453
13454 assert(scip != NULL);
13455 assert(expr != NULL);
13456 assert(var != NULL);
13457
13458 /* return 0.0 for value expression */
13459 if( SCIPisExprValue(scip, expr) )
13460 return 0.0;
13461
13462 /* check if an error occurred during the last SCIPevalExprHessianDir() call */
13463 if( SCIPexprGetBardot(expr) == SCIP_INVALID )
13464 return SCIP_INVALID;
13465
13466 ownerdata = SCIPexprGetOwnerData(expr);
13467 assert(ownerdata != NULL);
13468
13469 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr);
13470 assert(conshdlrdata != NULL);
13471
13472 /* use variable to expressions mapping which is stored in the constraint handler data;
13473 * if this fails it means that we are asking for the var's component of H*u for a var
13474 * that doesn't appear in any nonlinear constraint, so maybe we can also just return 0.0
13475 */
13476 assert(SCIPhashmapExists(conshdlrdata->var2expr, var));
13477
13478 varexpr = (SCIP_EXPR*)SCIPhashmapGetImage(conshdlrdata->var2expr, var);
13479 assert(varexpr != NULL);
13480 assert(SCIPisExprVar(scip, varexpr));
13481
13482 /* use difftag to decide whether the variable belongs to the expression */
13483 return (SCIPexprGetDiffTag(expr) != SCIPexprGetDiffTag(varexpr)) ? 0.0 : SCIPexprGetBardot(varexpr);
13484 }
13485
13486 /** evaluates quadratic term in a solution w.r.t. auxiliary variables
13487 *
13488 * \note This requires that for every expr used in the quadratic data, a variable or auxiliary variable is available.
13489 */
13490 SCIP_Real SCIPevalExprQuadraticAuxNonlinear(
13491 SCIP* scip, /**< SCIP data structure */
13492 SCIP_EXPR* expr, /**< quadratic expression */
13493 SCIP_SOL* sol /**< solution to evaluate, or NULL for LP solution */
13494 )
13495 {
13496 SCIP_Real auxvalue;
13497 int nlinexprs;
13498 SCIP_Real* lincoefs;
13499 SCIP_EXPR** linexprs;
13500 int nquadexprs;
13501 int nbilinexprs;
13502 int i;
13503
13504 assert(scip != NULL);
13505 assert(expr != NULL);
13506
13507 SCIPexprGetQuadraticData(expr, &auxvalue, &nlinexprs, &linexprs, &lincoefs, &nquadexprs, &nbilinexprs, NULL, NULL);
13508
13509 /* linear terms */
13510 for( i = 0; i < nlinexprs; ++i )
13511 {
13512 assert(SCIPgetExprAuxVarNonlinear(linexprs[i]) != NULL);
13513 auxvalue += lincoefs[i] * SCIPgetSolVal(scip, sol, SCIPgetExprAuxVarNonlinear(linexprs[i]));
13514 }
13515
13516 /* quadratic terms */
13517 for( i = 0; i < nquadexprs; ++i )
13518 {
13519 SCIP_EXPR* quadexprterm;
13520 SCIP_Real lincoef;
13521 SCIP_Real sqrcoef;
13522 SCIP_Real solval;
13523
13524 SCIPexprGetQuadraticQuadTerm(expr, i, &quadexprterm, &lincoef, &sqrcoef, NULL, NULL, NULL);
13525
13526 assert(SCIPgetExprAuxVarNonlinear(quadexprterm) != NULL);
13527
13528 solval = SCIPgetSolVal(scip, sol, SCIPgetExprAuxVarNonlinear(quadexprterm));
13529 auxvalue += (lincoef + sqrcoef * solval) * solval;
13530 }
13531
13532 /* bilinear terms */
13533 for( i = 0; i < nbilinexprs; ++i )
13534 {
13535 SCIP_EXPR* expr1;
13536 SCIP_EXPR* expr2;
13537 SCIP_Real coef;
13538
13539 SCIPexprGetQuadraticBilinTerm(expr, i, &expr1, &expr2, &coef, NULL, NULL);
13540
13541 assert(SCIPgetExprAuxVarNonlinear(expr1) != NULL);
13542 assert(SCIPgetExprAuxVarNonlinear(expr2) != NULL);
13543 auxvalue += coef * SCIPgetSolVal(scip, sol, SCIPgetExprAuxVarNonlinear(expr1)) * SCIPgetSolVal(scip, sol, SCIPgetExprAuxVarNonlinear(expr2));
13544 }
13545
13546 return auxvalue;
13547 }
13548
13549 /**@addtogroup PublicNlhdlrInterfaceMethods
13550 * @{
13551 */
13552
13553 /** creates a nonlinear handler and includes it into the nonlinear constraint handler */
13554 SCIP_RETCODE SCIPincludeNlhdlrNonlinear(
13555 SCIP* scip, /**< SCIP data structure */
13556 SCIP_NLHDLR** nlhdlr, /**< buffer where to store nonlinear handler */
13557 const char* name, /**< name of nonlinear handler (must not be NULL) */
13558 const char* desc, /**< description of nonlinear handler (can be NULL) */
13559 int detectpriority, /**< detection priority of nonlinear handler */
13560 int enfopriority, /**< enforcement priority of nonlinear handler */
13561 SCIP_DECL_NLHDLRDETECT((*detect)), /**< structure detection callback of nonlinear handler */
13562 SCIP_DECL_NLHDLREVALAUX((*evalaux)), /**< auxiliary evaluation callback of nonlinear handler */
13563 SCIP_NLHDLRDATA* nlhdlrdata /**< data of nonlinear handler (can be NULL) */
13564 )
13565 {
13566 SCIP_CONSHDLR* conshdlr;
13567 SCIP_CONSHDLRDATA* conshdlrdata;
13568
13569 assert(scip != NULL);
13570 assert(nlhdlr != NULL);
13571 assert(detect != NULL);
13572
13573 /* find myself */
13574 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
|
(1) Event cond_false: |
Condition "conshdlr == NULL", taking false branch. |
13575 if( conshdlr == NULL )
13576 {
13577 SCIPerrorMessage("nonlinear constraint handler not found");
13578 return SCIP_PLUGINNOTFOUND;
|
(2) Event if_end: |
End of if statement. |
13579 }
13580
13581 /* create nlhdlr */
|
(3) Event alloc_arg: |
"SCIPnlhdlrCreate" allocates memory that is stored into "*nlhdlr". [details] |
|
(4) Event cond_true: |
Condition "(_restat_ = SCIPnlhdlrCreate(scip, nlhdlr, name, desc, detectpriority, enfopriority, detect, evalaux, nlhdlrdata)) != SCIP_OKAY", taking true branch. |
13582 SCIP_CALL( SCIPnlhdlrCreate(scip, nlhdlr, name, desc, detectpriority, enfopriority, detect, evalaux, nlhdlrdata) );
13583
13584 /* include into constraint handler */
13585 conshdlrdata = SCIPconshdlrGetData(conshdlr);
13586 assert(conshdlrdata != NULL);
13587
13588 SCIP_CALL( SCIPensureBlockMemoryArray(scip, &conshdlrdata->nlhdlrs, &conshdlrdata->nlhdlrssize, conshdlrdata->nnlhdlrs+1) );
13589
13590 conshdlrdata->nlhdlrs[conshdlrdata->nnlhdlrs] = *nlhdlr;
13591 ++conshdlrdata->nnlhdlrs;
13592
13593 /* sort nonlinear handlers by detection priority, in decreasing order
13594 * will happen in INIT, so only do when called late
13595 */
13596 if( SCIPgetStage(scip) > SCIP_STAGE_INIT && conshdlrdata->nnlhdlrs > 1 )
13597 SCIPsortDownPtr((void**)conshdlrdata->nlhdlrs, SCIPnlhdlrComp, conshdlrdata->nnlhdlrs);
13598
13599 return SCIP_OKAY;
13600 }
13601
13602 /** returns a nonlinear handler of a given name (or NULL if not found) */
13603 SCIP_NLHDLR* SCIPfindNlhdlrNonlinear(
13604 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */
13605 const char* name /**< name of nonlinear handler */
13606 )
13607 {
13608 SCIP_CONSHDLRDATA* conshdlrdata;
13609 int h;
13610
13611 assert(conshdlr != NULL);
13612 assert(name != NULL);
13613
13614 conshdlrdata = SCIPconshdlrGetData(conshdlr);
13615 assert(conshdlrdata != NULL);
13616
13617 for( h = 0; h < conshdlrdata->nnlhdlrs; ++h )
13618 if( strcmp(SCIPnlhdlrGetName(conshdlrdata->nlhdlrs[h]), name) == 0 )
13619 return conshdlrdata->nlhdlrs[h];
13620
13621 return NULL;
13622 }
13623
13624 /** gives expression data that a given nonlinear handler stored in an expression
13625 *
13626 * Returns NULL if expr has not been detected by nlhdlr or nlhdlr did not store data.
13627 */
13628 SCIP_NLHDLREXPRDATA* SCIPgetNlhdlrExprDataNonlinear(
13629 SCIP_NLHDLR* nlhdlr, /**< nonlinear handler */
13630 SCIP_EXPR* expr /**< expression */
13631 )
13632 {
13633 SCIP_EXPR_OWNERDATA* ownerdata;
13634 int e;
13635
13636 assert(nlhdlr != NULL);
13637 assert(expr != NULL);
13638
13639 ownerdata = SCIPexprGetOwnerData(expr);
13640 assert(ownerdata != NULL);
13641
13642 for( e = 0; e < ownerdata->nenfos; ++e )
13643 if( ownerdata->enfos[e]->nlhdlr == nlhdlr )
13644 return ownerdata->enfos[e]->nlhdlrexprdata;
13645
13646 return NULL;
13647 }
13648
13649 /** @} */
13650