1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the program and library */
4 /* SCIP --- Solving Constraint Integer Programs */
5 /* */
6 /* Copyright (c) 2002-2023 Zuse Institute Berlin (ZIB) */
7 /* */
8 /* Licensed under the Apache License, Version 2.0 (the "License"); */
9 /* you may not use this file except in compliance with the License. */
10 /* You may obtain a copy of the License at */
11 /* */
12 /* http://www.apache.org/licenses/LICENSE-2.0 */
13 /* */
14 /* Unless required by applicable law or agreed to in writing, software */
15 /* distributed under the License is distributed on an "AS IS" BASIS, */
16 /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
17 /* See the License for the specific language governing permissions and */
18 /* limitations under the License. */
19 /* */
20 /* You should have received a copy of the Apache-2.0 license */
21 /* along with SCIP; see the file LICENSE. If not visit scipopt.org. */
22 /* */
23 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
24
25 /**@file cons_nonlinear.c
26 * @ingroup DEFPLUGINS_CONS
27 * @brief constraint handler for nonlinear constraints specified by algebraic expressions
28 * @author Ksenia Bestuzheva
29 * @author Benjamin Mueller
30 * @author Felipe Serrano
31 * @author Stefan Vigerske
32 */
33
34 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
35
36 #ifdef SCIP_DEBUG
37 #define ENFO_LOGGING
38 #endif
39
40 /* enable to get log output for enforcement */
41 /* #define ENFO_LOGGING */
42 /* define to get enforcement logging into file */
43 /* #define ENFOLOGFILE "consexpr_enfo.log" */
44
45 /* define to get more debug output from domain propagation */
46 /* #define DEBUG_PROP */
47
48 /*lint -e440*/
49 /*lint -e441*/
50 /*lint -e528*/
51 /*lint -e666*/
52 /*lint -e777*/
53 /*lint -e866*/
54
55 #include <ctype.h>
56 #include "scip/cons_nonlinear.h"
57 #include "scip/nlhdlr.h"
58 #include "scip/expr_var.h"
59 #include "scip/expr_varidx.h"
60 #include "scip/expr_abs.h"
61 #include "scip/expr_sum.h"
62 #include "scip/expr_value.h"
63 #include "scip/expr_pow.h"
64 #include "scip/expr_trig.h"
65 #include "scip/nlhdlr_convex.h"
66 #include "scip/cons_linear.h"
67 #include "scip/cons_varbound.h"
68 #include "scip/cons_and.h"
69 #include "scip/cons_bounddisjunction.h"
70 #include "scip/heur_subnlp.h"
71 #include "scip/heur_trysol.h"
72 #include "scip/lapack_calls.h"
73 #include "scip/debug.h"
74 #include "scip/dialog_default.h"
75 #include "scip/scip_expr.h"
76 #include "scip/symmetry_graph.h"
77 #include "scip/prop_symmetry.h"
78 #include "symmetry/struct_symmetry.h"
79 #include "scip/pub_misc_sort.h"
80
81
82 /* fundamental constraint handler properties */
83 #define CONSHDLR_NAME "nonlinear"
84 #define CONSHDLR_DESC "handler for nonlinear constraints specified by algebraic expressions"
85 #define CONSHDLR_ENFOPRIORITY -60 /**< priority of the constraint handler for constraint enforcing */
86 #define CONSHDLR_CHECKPRIORITY -4000010 /**< priority of the constraint handler for checking feasibility */
87 #define CONSHDLR_EAGERFREQ 100 /**< frequency for using all instead of only the useful constraints in separation,
88 * propagation and enforcement, -1 for no eager evaluations, 0 for first only */
89 #define CONSHDLR_NEEDSCONS TRUE /**< should the constraint handler be skipped, if no constraints are available? */
90
91 /* optional constraint handler properties */
92 #define CONSHDLR_SEPAPRIORITY 10 /**< priority of the constraint handler for separation */
93 #define CONSHDLR_SEPAFREQ 1 /**< frequency for separating cuts; zero means to separate only in the root node */
94 #define CONSHDLR_DELAYSEPA FALSE /**< should separation method be delayed, if other separators found cuts? */
95
96 #define CONSHDLR_PROPFREQ 1 /**< frequency for propagating domains; zero means only preprocessing propagation */
97 #define CONSHDLR_DELAYPROP FALSE /**< should propagation method be delayed, if other propagators found reductions? */
98 #define CONSHDLR_PROP_TIMING SCIP_PROPTIMING_BEFORELP /**< propagation timing mask of the constraint handler*/
99
100 #define CONSHDLR_PRESOLTIMING SCIP_PRESOLTIMING_ALWAYS /**< presolving timing of the constraint handler (fast, medium, or exhaustive) */
101 #define CONSHDLR_MAXPREROUNDS -1 /**< maximal number of presolving rounds the constraint handler participates in (-1: no limit) */
102
103 /* properties of the nonlinear constraint handler statistics table */
104 #define TABLE_NAME_NONLINEAR "cons_nonlinear"
105 #define TABLE_DESC_NONLINEAR "nonlinear constraint handler statistics"
106 #define TABLE_POSITION_NONLINEAR 14600 /**< the position of the statistics table */
107 #define TABLE_EARLIEST_STAGE_NONLINEAR SCIP_STAGE_TRANSFORMED /**< output of the statistics table is only printed from this stage onwards */
108
109 /* properties of the nonlinear handler statistics table */
110 #define TABLE_NAME_NLHDLR "nlhdlr"
111 #define TABLE_DESC_NLHDLR "nonlinear handler statistics"
112 #define TABLE_POSITION_NLHDLR 14601 /**< the position of the statistics table */
113 #define TABLE_EARLIEST_STAGE_NLHDLR SCIP_STAGE_PRESOLVING /**< output of the statistics table is only printed from this stage onwards */
114
115 #define DIALOG_NAME "nlhdlrs"
116 #define DIALOG_DESC "display nonlinear handlers"
117 #define DIALOG_ISSUBMENU FALSE
118
119 #define VERTEXPOLY_MAXPERTURBATION 1e-3 /**< maximum perturbation */
120 #define VERTEXPOLY_USEDUALSIMPLEX TRUE /**< use dual or primal simplex algorithm? */
121 #define VERTEXPOLY_RANDNUMINITSEED 20181029 /**< seed for random number generator, which is used to move points away from the boundary */
122 #define VERTEXPOLY_ADJUSTFACETFACTOR 1e1 /**< adjust resulting facets in checkRikun() up to a violation of this value times lpfeastol */
123
124 #define BRANCH_RANDNUMINITSEED 20191229 /**< seed for random number generator, which is used to select from several similar good branching candidates */
125
126 #define BILIN_MAXNAUXEXPRS 10 /**< maximal number of auxiliary expressions per bilinear term */
127
128 /** translate from one value of infinity to another
129 *
130 * if val is ≥ infty1, then give infty2, else give val
131 */
132 #define infty2infty(infty1, infty2, val) ((val) >= (infty1) ? (infty2) : (val))
133
134 /** translates x to 2^x for non-negative integer x */
135 #define POWEROFTWO(x) (0x1u << (x))
136
137 #ifdef ENFO_LOGGING
138 #define ENFOLOG(x) if( SCIPgetSubscipDepth(scip) == 0 && SCIPgetVerbLevel(scip) >= SCIP_VERBLEVEL_NORMAL ) { x }
139 FILE* enfologfile = NULL;
140 #else
141 #define ENFOLOG(x)
142 #endif
143
144 /*
145 * Data structures
146 */
147
148 /** enforcement data of an expression */
149 typedef struct
150 {
151 SCIP_NLHDLR* nlhdlr; /**< nonlinear handler */
152 SCIP_NLHDLREXPRDATA* nlhdlrexprdata; /**< data of nonlinear handler */
153 SCIP_NLHDLR_METHOD nlhdlrparticipation;/**< methods where nonlinear handler participates */
154 SCIP_Bool issepainit; /**< was the initsepa callback of nlhdlr called */
155 SCIP_Real auxvalue; /**< auxiliary value of expression w.r.t. currently enforced solution */
156 SCIP_Bool sepabelowusesactivity;/**< whether sepabelow uses activity of some expression */
157 SCIP_Bool sepaaboveusesactivity;/**< whether sepaabove uses activity of some expression */
158 } EXPRENFO;
159
160 /** data stored by constraint handler in an expression that belongs to a nonlinear constraint */
161 struct SCIP_Expr_OwnerData
162 {
163 SCIP_CONSHDLR* conshdlr; /** nonlinear constraint handler */
164
165 /* locks and monotonicity */
166 int nlockspos; /**< positive locks counter */
167 int nlocksneg; /**< negative locks counter */
168 SCIP_MONOTONE* monotonicity; /**< array containing monotonicity of expression w.r.t. each child */
169 int monotonicitysize; /**< length of monotonicity array */
170
171 /* propagation (in addition to activity that is stored in expr) */
172 SCIP_INTERVAL propbounds; /**< bounds to propagate in reverse propagation */
173 unsigned int propboundstag; /**< tag to indicate whether propbounds are valid for the current propagation rounds */
174 SCIP_Bool inpropqueue; /**< whether expression is queued for propagation */
175
176 /* enforcement of expr == auxvar (or expr <= auxvar, or expr >= auxvar) */
177 EXPRENFO** enfos; /**< enforcements */
178 int nenfos; /**< number of enforcements, or -1 if not initialized */
179 unsigned int lastenforced; /**< last enforcement round where expression was enforced successfully */
180 unsigned int nactivityusesprop; /**< number of nonlinear handlers whose activity computation (or domain propagation) depends on the activity of the expression */
181 unsigned int nactivityusessepa; /**< number of nonlinear handlers whose separation (estimate or enfo) depends on the activity of the expression */
182 unsigned int nauxvaruses; /**< number of nonlinear handlers whose separation uses an auxvar in the expression */
183 SCIP_VAR* auxvar; /**< auxiliary variable used for outer approximation cuts */
184
185 /* branching */
186 SCIP_Real violscoresum; /**< sum of violation scores for branching stored for this expression */
187 SCIP_Real violscoremax; /**< max of violation scores for branching stored for this expression */
188 int nviolscores; /**< number of violation scores stored for this expression */
189 unsigned int violscoretag; /**< tag to decide whether a violation score of an expression needs to be initialized */
190
191 /* additional data for variable expressions (TODO move into sub-struct?) */
192 SCIP_CONS** conss; /**< constraints in which this variable appears */
193 int nconss; /**< current number of constraints in conss */
194 int consssize; /**< length of conss array */
195 SCIP_Bool consssorted; /**< is the array of constraints sorted */
196
197 int filterpos; /**< position of eventdata in SCIP's event filter, -1 if not catching events */
198 };
199
200 /** constraint data for nonlinear constraints */
201 struct SCIP_ConsData
202 {
203 /* data that defines the constraint: expression and sides */
204 SCIP_EXPR* expr; /**< expression that represents this constraint */
205 SCIP_Real lhs; /**< left-hand side */
206 SCIP_Real rhs; /**< right-hand side */
207
208 /* variables */
209 SCIP_EXPR** varexprs; /**< array containing all variable expressions */
210 int nvarexprs; /**< total number of variable expressions */
211 SCIP_Bool catchedevents; /**< do we catch events on variables? */
212
213 /* constraint violation */
214 SCIP_Real lhsviol; /**< violation of left-hand side by current solution */
215 SCIP_Real rhsviol; /**< violation of right-hand side by current solution */
216 SCIP_Real gradnorm; /**< norm of gradient of constraint function in current solution (if evaluated) */
217 SCIP_Longint gradnormsoltag; /**< tag of solution used that gradnorm corresponds to */
218
219 /* status flags */
220 unsigned int ispropagated:1; /**< did we propagate the current bounds already? */
221 unsigned int issimplified:1; /**< did we simplify the expression tree already? */
222
223 /* locks */
224 int nlockspos; /**< number of positive locks */
225 int nlocksneg; /**< number of negative locks */
226
227 /* repair infeasible solutions */
228 SCIP_VAR* linvardecr; /**< variable that may be decreased without making any other constraint infeasible, or NULL if none */
229 SCIP_VAR* linvarincr; /**< variable that may be increased without making any other constraint infeasible, or NULL if none */
230 SCIP_Real linvardecrcoef; /**< linear coefficient of linvardecr */
231 SCIP_Real linvarincrcoef; /**< linear coefficient of linvarincr */
232
233 /* miscellaneous */
234 SCIP_EXPRCURV curv; /**< curvature of the root expression w.r.t. the original variables */
235 SCIP_NLROW* nlrow; /**< a nonlinear row representation of this constraint */
236 int consindex; /**< an index of the constraint that is unique among all expr-constraints in this SCIP instance and is constant */
237 };
238
239 /** constraint upgrade method */
240 typedef struct
241 {
242 SCIP_DECL_NONLINCONSUPGD((*consupgd)); /**< method to call for upgrading nonlinear constraint */
243 int priority; /**< priority of upgrading method */
244 SCIP_Bool active; /**< is upgrading enabled */
245 } CONSUPGRADE;
246
247 /** constraint handler data */
248 struct SCIP_ConshdlrData
249 {
250 /* nonlinear handler */
251 SCIP_NLHDLR** nlhdlrs; /**< nonlinear handlers */
252 int nnlhdlrs; /**< number of nonlinear handlers */
253 int nlhdlrssize; /**< size of nlhdlrs array */
254 SCIP_Bool indetect; /**< whether we are currently in detectNlhdlr */
255 SCIP_Bool registerusesactivitysepabelow; /**< a flag that is used only during \ref @detectNlhdlr() */
256 SCIP_Bool registerusesactivitysepaabove; /**< a flag that is used only during \ref @detectNlhdlr() */
257
258 /* constraint upgrades */
259 CONSUPGRADE** consupgrades; /**< constraint upgrade methods for specializing nonlinear constraints */
260 int consupgradessize; /**< size of consupgrades array */
261 int nconsupgrades; /**< number of constraint upgrade methods */
262
263 /* other plugins */
264 SCIP_EVENTHDLR* eventhdlr; /**< handler for variable bound change events */
265 SCIP_HEUR* subnlpheur; /**< a pointer to the subnlp heuristic, if available */
266 SCIP_HEUR* trysolheur; /**< a pointer to the trysol heuristic, if available */
267
268 /* tags and counters */
269 int auxvarid; /**< unique id for the next auxiliary variable */
270 SCIP_Longint curboundstag; /**< tag indicating current variable bounds */
271 SCIP_Longint lastboundrelax; /**< tag when bounds where most recently relaxed */
272 SCIP_Longint lastvaractivitymethodchange; /**< tag when method used to evaluate activity of variables changed last */
273 unsigned int enforound; /**< total number of enforcement calls, including current one */
274 int lastconsindex; /**< last used consindex, plus one */
275
276 /* activity intervals and domain propagation */
277 SCIP_DECL_EXPR_INTEVALVAR((*intevalvar)); /**< method currently used for activity calculation of variable expressions */
278 SCIP_Bool globalbounds; /**< whether global variable bounds should be used for activity calculation */
279 SCIP_QUEUE* reversepropqueue; /**< expression queue to be used in reverse propagation, filled by SCIPtightenExprIntervalNonlinear */
280 SCIP_Bool forceboundtightening; /**< whether bound change passed to SCIPtightenExprIntervalNonlinear should be forced */
281 unsigned int curpropboundstag; /**< tag indicating current propagation rounds, to match with expr->propboundstag */
282
283 /* parameters */
284 int maxproprounds; /**< limit on number of propagation rounds for a set of constraints within one round of SCIP propagation */
285 SCIP_Bool propauxvars; /**< whether to check bounds of all auxiliary variable to seed reverse propagation */
286 char varboundrelax; /**< strategy on how to relax variable bounds during bound tightening */
287 SCIP_Real varboundrelaxamount; /**< by how much to relax variable bounds during bound tightening */
288 SCIP_Real conssiderelaxamount; /**< by how much to relax constraint sides during bound tightening */
289 SCIP_Real vp_maxperturb; /**< maximal relative perturbation of reference point */
290 SCIP_Real vp_adjfacetthreshold; /**< adjust computed facet up to a violation of this value times lpfeastol */
291 SCIP_Bool vp_dualsimplex; /**< whether to use dual simplex instead of primal simplex for facet computing LP */
292 SCIP_Bool reformbinprods; /**< whether to reformulate products of binary variables during presolving */
293 SCIP_Bool reformbinprodsand; /**< whether to use the AND constraint handler for reformulating binary products */
294 int reformbinprodsfac; /**< minimum number of terms to reformulate bilinear binary products by factorizing variables (<= 1: disabled) */
295 SCIP_Bool forbidmultaggrnlvar; /**< whether to forbid multiaggregation of variables that appear in a nonlinear term of a constraint */
296 SCIP_Bool tightenlpfeastol; /**< whether to tighten LP feasibility tolerance during enforcement, if it seems useful */
297 SCIP_Bool propinenforce; /**< whether to (re)run propagation in enforcement */
298 SCIP_Real weakcutthreshold; /**< threshold for when to regard a cut from an estimator as weak */
299 SCIP_Real strongcutmaxcoef; /**< "strong" cuts will be scaled to have their maximal coef in [1/strongcutmaxcoef,strongcutmaxcoef] */
300 SCIP_Bool strongcutefficacy; /**< consider efficacy requirement when deciding whether a cut is "strong" */
301 SCIP_Bool forcestrongcut; /**< whether to force "strong" cuts in enforcement */
302 SCIP_Real enfoauxviolfactor; /**< an expression will be enforced if the "auxiliary" violation is at least enfoauxviolfactor times the "original" violation */
303 SCIP_Real weakcutminviolfactor; /**< retry with weak cuts for constraints with violation at least this factor of maximal violated constraints */
304 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 */
305 char violscale; /**< method how to scale violations to make them comparable (not used for feasibility check) */
306 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) */
307 int branchauxmindepth; /**< from which depth on to allow branching on auxiliary variables */
308 SCIP_Bool branchexternal; /**< whether to use external branching candidates for branching */
309 SCIP_Real branchhighviolfactor; /**< consider a constraint highly violated if its violation is >= this factor * maximal violation among all constraints */
310 SCIP_Real branchhighscorefactor; /**< consider a variable branching score high if its branching score >= this factor * maximal branching score among all variables */
311 SCIP_Real branchviolweight; /**< weight by how much to consider the violation assigned to a variable for its branching score */
312 SCIP_Real branchdualweight; /**< weight by how much to consider the dual values of rows that contain a variable for its branching score */
313 SCIP_Real branchpscostweight; /**< weight by how much to consider the pseudo cost of a variable for its branching score */
314 SCIP_Real branchdomainweight; /**< weight by how much to consider the domain width in branching score */
315 SCIP_Real branchvartypeweight;/**< weight by how much to consider variable type in branching score */
316 char branchscoreagg; /**< how to aggregate several branching scores given for the same expression ('a'verage, 'm'aximum, or 's'um) */
317 char branchviolsplit; /**< method used to split violation in expression onto variables ('u'niform, 'm'idness of solution, 'd'omain width, 'l'ogarithmic domain width) */
318 SCIP_Real branchpscostreliable; /**< minimum pseudo-cost update count required to consider pseudo-costs reliable */
319 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) */
320 SCIP_Bool assumeconvex; /**< whether to assume that any constraint is convex */
321
322 /* statistics */
323 SCIP_Longint nweaksepa; /**< number of times we used "weak" cuts for enforcement */
324 SCIP_Longint ntightenlp; /**< number of times we requested solving the LP with a smaller feasibility tolerance when enforcing */
325 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 */
326 SCIP_Longint ndesperatebranch; /**< number of times we branched on some variable because normal enforcement was not successful */
327 SCIP_Longint ndesperatecutoff; /**< number of times we cut off a node in enforcement because no branching candidate could be found */
328 SCIP_Longint nforcelp; /**< number of times we forced solving the LP when enforcing a pseudo solution */
329 SCIP_CLOCK* canonicalizetime; /**< time spend for canonicalization */
330 SCIP_Longint ncanonicalizecalls; /**< number of times we called canonicalization */
331
332 /* facets of envelops of vertex-polyhedral functions */
333 SCIP_RANDNUMGEN* vp_randnumgen; /**< random number generator used to perturb reference point */
334 SCIP_LPI* vp_lp[SCIP_MAXVERTEXPOLYDIM+1]; /**< LPs used to compute facets for functions of different dimension */
335
336 /* hashing of bilinear terms */
337 SCIP_HASHTABLE* bilinhashtable; /**< hash table for bilinear terms */
338 SCIP_CONSNONLINEAR_BILINTERM* bilinterms; /**< bilinear terms */
339 int nbilinterms; /**< total number of bilinear terms */
340 int bilintermssize; /**< size of bilinterms array */
341 int bilinmaxnauxexprs; /**< maximal number of auxiliary expressions per bilinear term */
342
343 /* branching */
344 SCIP_RANDNUMGEN* branchrandnumgen; /**< random number generated used in branching variable selection */
345 char branchpscostupdatestrategy; /**< value of parameter branching/lpgainnormalize */
346
347 /* misc */
348 SCIP_Bool checkedvarlocks; /**< whether variables contained in a single constraint have been already considered */
349 SCIP_HASHMAP* var2expr; /**< hashmap to map SCIP variables to variable-expressions */
350 int newsoleventfilterpos; /**< filter position of new solution event handler, if caught */
351 };
352
353 /** branching candidate with various scores */
354 typedef struct
355 {
356 SCIP_EXPR* expr; /**< expression that holds branching candidate */
357 SCIP_Real auxviol; /**< aux-violation score of candidate */
358 SCIP_Real domain; /**< domain score of candidate */
359 SCIP_Real dual; /**< dual score of candidate */
360 SCIP_Real pscost; /**< pseudo-cost score of candidate */
361 SCIP_Real vartype; /**< variable type score of candidate */
362 SCIP_Real weighted; /**< weighted sum of other scores, see scoreBranchingCandidates() */
363 } BRANCHCAND;
364
365 /*
366 * Local methods
367 */
368
369 /* forward declaration */
370 static
371 SCIP_RETCODE forwardPropExpr(
372 SCIP* scip, /**< SCIP data structure */
373 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
374 SCIP_EXPR* rootexpr, /**< expression */
375 SCIP_Bool tightenauxvars, /**< should the bounds of auxiliary variables be tightened? */
376 SCIP_Bool* infeasible, /**< buffer to store whether the problem is infeasible (NULL if not needed) */
377 int* ntightenings /**< buffer to store the number of auxiliary variable tightenings (NULL if not needed) */
378 );
379
380 /** frees auxiliary variables of expression, if any */
381 static
382 SCIP_RETCODE freeAuxVar(
383 SCIP* scip, /**< SCIP data structure */
384 SCIP_EXPR* expr /**< expression which auxvar to free, if any */
385 )
386 {
387 SCIP_EXPR_OWNERDATA* mydata;
388
389 assert(scip != NULL);
390 assert(expr != NULL);
391
392 mydata = SCIPexprGetOwnerData(expr);
393 assert(mydata != NULL);
394
395 if( mydata->auxvar == NULL )
396 return SCIP_OKAY;
397
398 SCIPdebugMsg(scip, "remove auxiliary variable <%s> for expression %p\n", SCIPvarGetName(mydata->auxvar), (void*)expr);
399
400 /* remove variable locks
401 * as this is a relaxation-only variable, no other plugin should use it for deducing any type of reductions or cutting planes
402 */
403 SCIP_CALL( SCIPaddVarLocks(scip, mydata->auxvar, -1, -1) );
404
405 /* release auxiliary variable */
406 SCIP_CALL( SCIPreleaseVar(scip, &mydata->auxvar) );
407 assert(mydata->auxvar == NULL);
408
409 return SCIP_OKAY;
410 }
411
412 /** frees data used for enforcement of expression, that is, nonlinear handlers
413 *
414 * can also clear indicators whether expr needs enforcement methods, that is,
415 * free an associated auxiliary variable and reset the nactivityuses counts
416 */
417 static
418 SCIP_RETCODE freeEnfoData(
419 SCIP* scip, /**< SCIP data structure */
420 SCIP_EXPR* expr, /**< expression whose enforcement data will be released */
421 SCIP_Bool freeauxvar /**< whether aux var should be released and activity usage counts be reset */
422 )
423 {
424 SCIP_EXPR_OWNERDATA* mydata;
425 int e;
426
427 mydata = SCIPexprGetOwnerData(expr);
428 assert(mydata != NULL);
429
430 if( freeauxvar )
431 {
432 /* free auxiliary variable */
433 SCIP_CALL( freeAuxVar(scip, expr) );
434 assert(mydata->auxvar == NULL);
435
436 /* reset count on activity and auxvar usage */
437 mydata->nactivityusesprop = 0;
438 mydata->nactivityusessepa = 0;
439 mydata->nauxvaruses = 0;
440 }
441
442 /* free data stored by nonlinear handlers */
443 for( e = 0; e < mydata->nenfos; ++e )
444 {
445 SCIP_NLHDLR* nlhdlr;
446
447 assert(mydata->enfos[e] != NULL);
448
449 nlhdlr = mydata->enfos[e]->nlhdlr;
450 assert(nlhdlr != NULL);
451
452 if( mydata->enfos[e]->issepainit )
453 {
454 /* call the separation deinitialization callback of the nonlinear handler */
455 SCIP_CALL( SCIPnlhdlrExitsepa(scip, nlhdlr, expr, mydata->enfos[e]->nlhdlrexprdata) );
456 mydata->enfos[e]->issepainit = FALSE;
457 }
458
459 /* free nlhdlr exprdata, if there is any and there is a method to free this data */
460 if( mydata->enfos[e]->nlhdlrexprdata != NULL )
461 {
462 SCIP_CALL( SCIPnlhdlrFreeexprdata(scip, nlhdlr, expr, &mydata->enfos[e]->nlhdlrexprdata) );
463 assert(mydata->enfos[e]->nlhdlrexprdata == NULL);
464 }
465
466 /* free enfo data */
467 SCIPfreeBlockMemory(scip, &mydata->enfos[e]);
468 }
469
470 /* free array with enfo data */
471 SCIPfreeBlockMemoryArrayNull(scip, &mydata->enfos, mydata->nenfos);
472
473 /* we need to look at this expression in detect again */
474 mydata->nenfos = -1;
475
476 return SCIP_OKAY;
477 }
478
479 /** callback that frees data that this conshdlr stored in an expression */
480 static
481 SCIP_DECL_EXPR_OWNERFREE(exprownerFree)
482 {
483 assert(scip != NULL);
484 assert(expr != NULL);
485 assert(ownerdata != NULL);
486 assert(*ownerdata != NULL);
487
488 /* expression should not be locked anymore */
489 assert((*ownerdata)->nlockspos == 0);
490 assert((*ownerdata)->nlocksneg == 0);
491
492 SCIP_CALL( freeEnfoData(scip, expr, TRUE) );
493
494 /* expression should not be enforced anymore */
495 assert((*ownerdata)->nenfos <= 0);
496 assert((*ownerdata)->auxvar == NULL);
497
498 if( SCIPisExprVar(scip, expr) )
499 {
500 SCIP_CONSHDLRDATA* conshdlrdata;
501 SCIP_VAR* var;
502
503 /* there should be no constraints left that still use this variable */
504 assert((*ownerdata)->nconss == 0);
505 /* thus, there should also be no variable event catched (via this exprhdlr) */
506 assert((*ownerdata)->filterpos == -1);
507
508 SCIPfreeBlockMemoryArrayNull(scip, &(*ownerdata)->conss, (*ownerdata)->consssize);
509
510 /* update var2expr hashmap in conshdlrdata */
511 conshdlrdata = SCIPconshdlrGetData((*ownerdata)->conshdlr);
512 assert(conshdlrdata != NULL);
513
514 var = SCIPgetVarExprVar(expr);
515 assert(var != NULL);
516
517 /* remove var -> expr map from hashmap if present
518 * (if no variable-expression stored for var hashmap, then the var hasn't been used in any constraint, so do nothing
519 * if variable-expression stored for var is different, then also do nothing)
520 */
521 if( SCIPhashmapGetImage(conshdlrdata->var2expr, var) == (void*)expr )
522 {
523 SCIP_CALL( SCIPhashmapRemove(conshdlrdata->var2expr, var) );
524 }
525 }
526
527 SCIPfreeBlockMemory(scip, ownerdata);
528
529 return SCIP_OKAY;
530 }
531
532 static
533 SCIP_DECL_EXPR_OWNERPRINT(exprownerPrint)
534 { /*lint --e{715}*/
535 assert(ownerdata != NULL);
536
537 /* print nl handlers associated to expr */
538 if( ownerdata->nenfos > 0 )
539 {
540 int i;
541 SCIPinfoMessage(scip, file, " {");
542
543 for( i = 0; i < ownerdata->nenfos; ++i )
544 {
545 SCIPinfoMessage(scip, file, "%s:", SCIPnlhdlrGetName(ownerdata->enfos[i]->nlhdlr));
546 if( ownerdata->enfos[i]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_ACTIVITY )
547 SCIPinfoMessage(scip, file, "a");
548 if( ownerdata->enfos[i]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPABELOW )
549 SCIPinfoMessage(scip, file, "u");
550 if( ownerdata->enfos[i]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPAABOVE )
551 SCIPinfoMessage(scip, file, "o");
552 if( i < ownerdata->nenfos-1 )
553 SCIPinfoMessage(scip, file, ", ");
554 }
555
556 SCIPinfoMessage(scip, file, "}");
557 }
558
559 /* print aux var associated to expr */
560 if( ownerdata->auxvar != NULL )
561 {
562 SCIPinfoMessage(scip, file, " (<%s> in [%g, %g])", SCIPvarGetName(ownerdata->auxvar), SCIPvarGetLbLocal(ownerdata->auxvar), SCIPvarGetUbLocal(ownerdata->auxvar));
563 }
564 SCIPinfoMessage(scip, file, "\n");
565
566 return SCIP_OKAY;
567 }
568
569 /** possibly reevaluates and then returns the activity of the expression
570 *
571 * Reevaluate activity if currently stored is not up to date (some bound was changed since last evaluation).
572 */
573 static
574 SCIP_DECL_EXPR_OWNEREVALACTIVITY(exprownerEvalactivity)
575 {
576 SCIP_CONSHDLRDATA* conshdlrdata;
577
578 assert(scip != NULL);
579 assert(expr != NULL);
580 assert(ownerdata != NULL);
581
582 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr);
583 assert(conshdlrdata != NULL);
584
585 if( SCIPexprGetActivityTag(expr) < conshdlrdata->curboundstag )
586 {
587 /* update activity of expression */
588 SCIP_CALL( forwardPropExpr(scip, ownerdata->conshdlr, expr, FALSE, NULL, NULL) );
589
590 assert(SCIPexprGetActivityTag(expr) == conshdlrdata->curboundstag);
591 }
592
593 return SCIP_OKAY;
594 }
595
596 /** callback that creates data that this conshdlr wants to store in an expression */
597 static
598 SCIP_DECL_EXPR_OWNERCREATE(exprownerCreate)
599 {
600 assert(scip != NULL);
601 assert(expr != NULL);
602 assert(ownerdata != NULL);
603
604 SCIP_CALL( SCIPallocClearBlockMemory(scip, ownerdata) );
605 (*ownerdata)->nenfos = -1;
606 (*ownerdata)->conshdlr = (SCIP_CONSHDLR*)ownercreatedata;
607
608 if( SCIPisExprVar(scip, expr) )
609 {
610 SCIP_CONSHDLRDATA* conshdlrdata;
611 SCIP_VAR* var;
612
613 (*ownerdata)->filterpos = -1;
614
615 /* add to var2expr hashmap if not having expr for var yet */
616
617 conshdlrdata = SCIPconshdlrGetData((*ownerdata)->conshdlr);
618 assert(conshdlrdata != NULL);
619
620 var = SCIPgetVarExprVar(expr);
621
622 if( !SCIPhashmapExists(conshdlrdata->var2expr, (void*)var) )
623 {
624 /* store the variable expression in the hashmap */
625 SCIP_CALL( SCIPhashmapInsert(conshdlrdata->var2expr, (void*)var, (void*)expr) );
626 }
627 else
628 {
629 /* if expr was just created, then it shouldn't already be stored as image of var */
630 assert(SCIPhashmapGetImage(conshdlrdata->var2expr, (void*)var) != (void*)expr);
631 }
632 }
633 else
634 {
635 /* just so that we can use filterpos to recognize whether an expr is a varexpr if not having a SCIP pointer around */
636 (*ownerdata)->filterpos = -2;
637 }
638
639 *ownerfree = exprownerFree;
640 *ownerprint = exprownerPrint;
641 *ownerevalactivity = exprownerEvalactivity;
642
643 return SCIP_OKAY;
644 }
645
646 /** creates a variable expression or retrieves from hashmap in conshdlr data */
647 static
648 SCIP_RETCODE createExprVar(
649 SCIP* scip, /**< SCIP data structure */
650 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */
651 SCIP_EXPR** expr, /**< pointer where to store expression */
652 SCIP_VAR* var /**< variable to be stored */
653 )
654 {
655 assert(conshdlr != NULL);
656 assert(expr != NULL);
657 assert(var != NULL);
658
659 /* get variable expression representing the given variable if there is one already */
660 *expr = (SCIP_EXPR*) SCIPhashmapGetImage(SCIPconshdlrGetData(conshdlr)->var2expr, (void*) var);
661
662 if( *expr == NULL )
663 {
664 /* create a new variable expression; this also captures the expression */
665 SCIP_CALL( SCIPcreateExprVar(scip, expr, var, exprownerCreate, (void*)conshdlr) );
666 assert(*expr != NULL);
667 /* exprownerCreate should have added var->expr to var2expr */
668 assert(SCIPhashmapGetImage(SCIPconshdlrGetData(conshdlr)->var2expr, (void*)var) == (void*)*expr);
669 }
670 else
671 {
672 /* only capture already existing expr to get a consistent uses-count */
673 SCIPcaptureExpr(*expr);
674 }
675
676 return SCIP_OKAY;
677 }
678
679 /* map var exprs to var-expr from var2expr hashmap */
680 static
681 SCIP_DECL_EXPR_MAPEXPR(mapexprvar)
682 { /*lint --e{715}*/
683 SCIP_CONSHDLR* conshdlr = (SCIP_CONSHDLR*)mapexprdata;
684
685 assert(sourcescip != NULL);
686 assert(targetscip != NULL);
687 assert(sourceexpr != NULL);
688 assert(targetexpr != NULL);
689 assert(*targetexpr == NULL);
690 assert(mapexprdata != NULL);
691
692 /* do not provide map if not variable */
693 if( !SCIPisExprVar(sourcescip, sourceexpr) )
694 return SCIP_OKAY;
695
696 SCIP_CALL( createExprVar(targetscip, conshdlr, targetexpr, SCIPgetVarExprVar(sourceexpr)) );
697
698 return SCIP_OKAY;
699 }
700
701 /* map var exprs to var-expr from var2expr hashmap corresponding to transformed var */
702 static
703 SCIP_DECL_EXPR_MAPEXPR(mapexprtransvar)
704 { /*lint --e{715}*/
705 SCIP_CONSHDLR* conshdlr = (SCIP_CONSHDLR*)mapexprdata;
706 SCIP_VAR* var;
707
708 assert(sourcescip != NULL);
709 assert(targetscip != NULL);
710 assert(sourceexpr != NULL);
711 assert(targetexpr != NULL);
712 assert(*targetexpr == NULL);
713 assert(mapexprdata != NULL);
714
715 /* do not provide map if not variable */
716 if( !SCIPisExprVar(sourcescip, sourceexpr) )
717 return SCIP_OKAY;
718
719 var = SCIPgetVarExprVar(sourceexpr);
720 assert(var != NULL);
721
722 /* transform variable */
723 SCIP_CALL( SCIPgetTransformedVar(sourcescip, var, &var) );
724 assert(var != NULL);
725
726 SCIP_CALL( createExprVar(targetscip, conshdlr, targetexpr, var) );
727
728 return SCIP_OKAY;
729 }
730
731 /** stores all variable expressions into a given constraint */
732 static
733 SCIP_RETCODE storeVarExprs(
734 SCIP* scip, /**< SCIP data structure */
735 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
736 SCIP_CONSDATA* consdata /**< constraint data */
737 )
738 {
739 SCIP_CONSHDLRDATA* conshdlrdata;
740 int varexprssize;
741 int i;
742
743 assert(consdata != NULL);
744
745 /* skip if we have stored the variable expressions already */
746 if( consdata->varexprs != NULL )
747 return SCIP_OKAY;
748
749 assert(consdata->varexprs == NULL);
750 assert(consdata->nvarexprs == 0);
751
752 /* get an upper bound on number of variable expressions */
753 if( consdata->issimplified )
754 {
755 /* if simplified, then we should have removed inactive variables and replaced common subexpressions,
756 * so we cannot have more variable expression than the number of active variables
757 */
758 varexprssize = SCIPgetNVars(scip);
759 }
760 else
761 {
762 SCIP_CALL( SCIPgetExprNVars(scip, consdata->expr, &varexprssize) );
763 }
764
765 /* create array to store all variable expressions */
766 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consdata->varexprs, varexprssize) );
767
768 SCIP_CALL( SCIPgetExprVarExprs(scip, consdata->expr, consdata->varexprs, &(consdata->nvarexprs)) );
769 assert(varexprssize >= consdata->nvarexprs);
770
771 /* shrink array if there are less variables in the expression than in the problem */
772 if( varexprssize > consdata->nvarexprs )
773 {
774 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->varexprs, varexprssize, consdata->nvarexprs) );
775 }
776
777 conshdlrdata = SCIPconshdlrGetData(conshdlr);
778 assert(conshdlrdata != NULL);
779 assert(conshdlrdata->var2expr != NULL);
780
781 /* ensure that for every variable an entry exists in the var2expr hashmap
782 * when removing duplicate subexpressions it can happen that a var->varexpr map was removed from the hashmap
783 */
784 for( i = 0; i < consdata->nvarexprs; ++i )
785 {
786 if( !SCIPhashmapExists(conshdlrdata->var2expr, SCIPgetVarExprVar(consdata->varexprs[i])) )
787 {
788 SCIP_CALL( SCIPhashmapInsert(conshdlrdata->var2expr, SCIPgetVarExprVar(consdata->varexprs[i]), consdata->varexprs[i]) );
789 }
790 }
791
792 return SCIP_OKAY;
793 }
794
795 /** frees all variable expression stored in storeVarExprs() */
796 static
797 SCIP_RETCODE freeVarExprs(
798 SCIP* scip, /**< SCIP data structure */
799 SCIP_CONSDATA* consdata /**< constraint data */
800 )
801 {
802 int i;
803
804 assert(consdata != NULL);
805
806 /* skip if we have stored the variable expressions already*/
807 if( consdata->varexprs == NULL )
808 return SCIP_OKAY;
809
810 assert(consdata->varexprs != NULL);
811 assert(consdata->nvarexprs >= 0);
812
813 /* release variable expressions */
814 for( i = 0; i < consdata->nvarexprs; ++i )
815 {
816 assert(consdata->varexprs[i] != NULL);
817 SCIP_CALL( SCIPreleaseExpr(scip, &consdata->varexprs[i]) );
818 assert(consdata->varexprs[i] == NULL);
819 }
820
821 /* free variable expressions */
822 SCIPfreeBlockMemoryArrayNull(scip, &consdata->varexprs, consdata->nvarexprs);
823 consdata->varexprs = NULL;
824 consdata->nvarexprs = 0;
825
826 return SCIP_OKAY;
827 }
828
829 /** interval evaluation of variables as used in bound tightening
830 *
831 * Returns slightly relaxed local variable bounds of a variable as interval.
832 * Does not relax beyond integer values, thus does not relax bounds on integer variables at all.
833 */
834 static
835 SCIP_DECL_EXPR_INTEVALVAR(intEvalVarBoundTightening)
836 {
837 SCIP_INTERVAL interval;
838 SCIP_CONSHDLRDATA* conshdlrdata;
839 SCIP_Real lb;
840 SCIP_Real ub;
841
842 assert(scip != NULL);
843 assert(var != NULL);
844
845 conshdlrdata = (SCIP_CONSHDLRDATA*)intevalvardata;
846 assert(conshdlrdata != NULL);
847
848 if( conshdlrdata->globalbounds )
849 {
850 lb = SCIPvarGetLbGlobal(var);
851 ub = SCIPvarGetUbGlobal(var);
852 }
853 else
854 {
855 lb = SCIPvarGetLbLocal(var);
856 ub = SCIPvarGetUbLocal(var);
857 }
858 assert(lb <= ub); /* SCIP should ensure that variable bounds are not contradicting */
859
860 /* implicit integer variables may have non-integer bounds, apparently (run space25a) */
861 if( SCIPvarGetType(var) == SCIP_VARTYPE_IMPLINT )
862 {
863 lb = EPSROUND(lb, 0.0); /*lint !e835*/
864 ub = EPSROUND(ub, 0.0); /*lint !e835*/
865 }
866
867 /* integer variables should always have integral bounds in SCIP */
868 assert(EPSFRAC(lb, 0.0) == 0.0 || !SCIPvarIsIntegral(var)); /*lint !e835*/
869 assert(EPSFRAC(ub, 0.0) == 0.0 || !SCIPvarIsIntegral(var)); /*lint !e835*/
870
871 switch( conshdlrdata->varboundrelax )
872 {
873 case 'n' : /* no relaxation */
874 break;
875
876 case 'a' : /* relax by absolute value */
877 {
878 /* do not look at integer variables, they already have integral bounds, so wouldn't be relaxed */
879 if( SCIPvarIsIntegral(var) )
880 break;
881
882 if( !SCIPisInfinity(scip, -lb) )
883 {
884 /* reduce lb by epsilon, or to the next integer value, which ever is larger */
885 SCIP_Real bnd = floor(lb);
886 lb = MAX(bnd, lb - conshdlrdata->varboundrelaxamount);
887 }
888
889 if( !SCIPisInfinity(scip, ub) )
890 {
891 /* increase ub by epsilon, or to the next integer value, which ever is smaller */
892 SCIP_Real bnd = ceil(ub);
893 ub = MIN(bnd, ub + conshdlrdata->varboundrelaxamount);
894 }
895
896 break;
897 }
898
899 case 'b' : /* relax always by absolute value */
900 {
901 /* do not look at integer variables, they already have integral bounds, so wouldn't be relaxed */
902 if( SCIPvarIsIntegral(var) )
903 break;
904
905 if( !SCIPisInfinity(scip, -lb) )
906 lb -= conshdlrdata->varboundrelaxamount;
907
908 if( !SCIPisInfinity(scip, ub) )
909 ub += conshdlrdata->varboundrelaxamount;
910
911 break;
912 }
913
914 case 'r' : /* relax by relative value */
915 {
916 /* do not look at integer variables, they already have integral bounds, so wouldn't be relaxed */
917 if( SCIPvarIsIntegral(var) )
918 break;
919
920 /* relax bounds by epsilon*max(1,|bnd|), instead of just epsilon as in case 'a', thus we trust the first log(epsilon) digits
921 * however, when domains get small, relaxing can excessively weaken bound tightening, thus do only fraction of |ub-lb| if that is smaller
922 * further, do not relax beyond next integer value
923 */
924 if( !SCIPisInfinity(scip, -lb) )
925 {
926 SCIP_Real bnd = floor(lb);
927 lb = MAX(bnd, lb - MIN(conshdlrdata->varboundrelaxamount * MAX(1.0, REALABS(lb)), 0.001 * REALABS(ub-lb)));
928 }
929
930 if( !SCIPisInfinity(scip, ub) )
931 {
932 SCIP_Real bnd = ceil(ub);
933 ub = MIN(bnd, ub + MIN(conshdlrdata->varboundrelaxamount * MAX(1.0, REALABS(ub)), 0.001 * REALABS(ub-lb)));
934 }
935
936 break;
937 }
938
939 default :
940 {
941 SCIPerrorMessage("Unsupported value '%c' for varboundrelax option.\n", conshdlrdata->varboundrelax);
942 SCIPABORT();
943 break;
944 }
945 }
946
947 /* convert SCIPinfinity() to SCIP_INTERVAL_INFINITY */
948 lb = -infty2infty(SCIPinfinity(scip), SCIP_INTERVAL_INFINITY, -lb);
949 ub = infty2infty(SCIPinfinity(scip), SCIP_INTERVAL_INFINITY, ub);
950 assert(lb <= ub);
951
952 SCIPintervalSetBounds(&interval, lb, ub);
953
954 return interval;
955 }
956
957 /** compares two nonlinear constraints by its index
958 *
959 * Usable as compare operator in array sort functions.
960 */
961 static
962 SCIP_DECL_SORTPTRCOMP(compIndexConsNonlinear)
963 {
964 SCIP_CONSDATA* consdata1 = SCIPconsGetData((SCIP_CONS*)elem1);
965 SCIP_CONSDATA* consdata2 = SCIPconsGetData((SCIP_CONS*)elem2);
966
967 assert(consdata1 != NULL);
968 assert(consdata2 != NULL);
969
970 return consdata1->consindex - consdata2->consindex;
971 }
972
973 /** processes variable fixing or bound change event */
974 static
975 SCIP_DECL_EVENTEXEC(processVarEvent)
976 { /*lint --e{715}*/
977 SCIP_EVENTTYPE eventtype;
978 SCIP_EXPR* expr;
979 SCIP_EXPR_OWNERDATA* ownerdata;
980 SCIP_Bool boundtightened = FALSE;
981
982 eventtype = SCIPeventGetType(event);
983 assert(eventtype & (SCIP_EVENTTYPE_BOUNDCHANGED | SCIP_EVENTTYPE_VARFIXED | SCIP_EVENTTYPE_TYPECHANGED));
984
985 assert(eventdata != NULL);
986 expr = (SCIP_EXPR*) eventdata;
987 assert(SCIPisExprVar(scip, expr));
988
989 SCIPdebugMsg(scip, " exec event %" SCIP_EVENTTYPE_FORMAT " for variable <%s> (local [%g,%g], global [%g,%g])\n", eventtype,
990 SCIPvarGetName(SCIPeventGetVar(event)),
991 SCIPvarGetLbLocal(SCIPeventGetVar(event)), SCIPvarGetUbLocal(SCIPeventGetVar(event)),
992 SCIPvarGetLbGlobal(SCIPeventGetVar(event)), SCIPvarGetUbGlobal(SCIPeventGetVar(event)));
993
994 ownerdata = SCIPexprGetOwnerData(expr);
995 assert(ownerdata != NULL);
996 /* we only catch varevents for variables in constraints, so there should be constraints */
997 assert(ownerdata->nconss > 0);
998 assert(ownerdata->conss != NULL);
999
1000 if( eventtype & SCIP_EVENTTYPE_BOUNDTIGHTENED )
1001 boundtightened = TRUE;
1002
1003 /* usually, if fixing a variable results in a boundchange, we should have seen a boundtightened-event as well
1004 * however, if the boundchange is smaller than epsilon, such an event will be omitted
1005 * but we still want to make sure the activity of the var-expr is reevaluated (mainly to avoid a failing assert) in this case
1006 * since we cannot easily see whether a variable bound was actually changed in a varfixed event, we treat any varfixed event
1007 * as a boundtightening (and usually it is, I would think)
1008 */
1009 if( eventtype & SCIP_EVENTTYPE_VARFIXED )
1010 boundtightened = TRUE;
1011
1012 /* if a variable is changed to implicit-integer and has a fractional bound, then the behavior of intEvalVarBoundTightening is changing,
1013 * because we will round the bounds and no longer consider relaxing them
1014 * we will mark corresponding constraints as not-propagated in this case to get the tightened bounds on the var-expr
1015 * (mainly to avoid a failing assert, see github issue #70)
1016 * usually, a change to implicit-integer would result in a boundchange on the variable as well, but not if the bound was already almost integral
1017 */
1018 if( (eventtype & SCIP_EVENTTYPE_TYPECHANGED) && (SCIPeventGetNewtype(event) == SCIP_VARTYPE_IMPLINT) &&
1019 (!EPSISINT(SCIPvarGetLbGlobal(SCIPeventGetVar(event)), 0.0) || !EPSISINT(SCIPvarGetUbGlobal(SCIPeventGetVar(event)), 0.0)) ) /*lint !e835*/
1020 boundtightened = TRUE;
1021
1022 /* notify constraints that use this variable expression (expr) to repropagate and possibly resimplify
1023 * - propagation can only find something new if a bound was tightened
1024 * - simplify can only find something new if a var is fixed (or maybe a bound is tightened)
1025 * and we look at global changes (that is, we are not looking at boundchanges in probing)
1026 */
1027 if( boundtightened )
1028 {
1029 SCIP_CONSDATA* consdata;
1030 int c;
1031
1032 for( c = 0; c < ownerdata->nconss; ++c )
1033 {
1034 assert(ownerdata->conss[c] != NULL);
1035 consdata = SCIPconsGetData(ownerdata->conss[c]);
1036
1037 /* if bound tightening, then mark constraints to be propagated again
1038 * TODO we could try be more selective here and only trigger a propagation if a relevant bound has changed,
1039 * that is, we don't need to repropagate x + ... <= rhs if only the upper bound of x has been tightened
1040 * the locks don't help since they are not available separately for each constraint
1041 */
1042 consdata->ispropagated = FALSE;
1043 SCIPdebugMsg(scip, " marked <%s> for propagate\n", SCIPconsGetName(ownerdata->conss[c]));
1044
1045 /* if still in presolve (but not probing), then mark constraints to be unsimplified */
1046 if( SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING && !SCIPinProbing(scip) )
1047 {
1048 consdata->issimplified = FALSE;
1049 SCIPdebugMsg(scip, " marked <%s> for simplify\n", SCIPconsGetName(ownerdata->conss[c]));
1050 }
1051 }
1052 }
1053
1054 /* update curboundstag, lastboundrelax, and expr activity */
1055 if( (eventtype & SCIP_EVENTTYPE_BOUNDCHANGED) || boundtightened )
1056 {
1057 SCIP_CONSHDLRDATA* conshdlrdata;
1058 SCIP_INTERVAL activity;
1059
1060 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr);
1061 assert(conshdlrdata != NULL);
1062
1063 /* increase tag on bounds */
1064 ++conshdlrdata->curboundstag;
1065 assert(conshdlrdata->curboundstag > 0);
1066
1067 /* remember also if we relaxed bounds now */
1068 if( eventtype & SCIP_EVENTTYPE_BOUNDRELAXED )
1069 conshdlrdata->lastboundrelax = conshdlrdata->curboundstag;
1070
1071 /* update the activity of the var-expr here immediately
1072 * (we could call expr->activity = intevalvar(var, consdhlr) directly, but then the exprhdlr statistics are not updated)
1073 */
1074 SCIP_CALL( SCIPcallExprInteval(scip, expr, &activity, conshdlrdata->intevalvar, conshdlrdata) );
1075 /* activity = conshdlrdata->intevalvar(scip, SCIPgetVarExprVar(expr), conshdlrdata); */
1076 #ifdef DEBUG_PROP
1077 SCIPdebugMsg(scip, " var-exprhdlr::inteval = [%.20g, %.20g]\n", activity.inf, activity.sup);
1078 #endif
1079 SCIPexprSetActivity(expr, activity, conshdlrdata->curboundstag);
1080 }
1081
1082 return SCIP_OKAY;
1083 }
1084
1085 /** registers event handler to catch variable events on variable
1086 *
1087 * Additionally, the given constraint is stored in the ownerdata of the variable-expression.
1088 * When an event occurs, all stored constraints are notified.
1089 */
1090 static
1091 SCIP_RETCODE catchVarEvent(
1092 SCIP* scip, /**< SCIP data structure */
1093 SCIP_EVENTHDLR* eventhdlr, /**< event handler */
1094 SCIP_EXPR* expr, /**< variable expression */
1095 SCIP_CONS* cons /**< nonlinear constraint */
1096 )
1097 {
1098 SCIP_EXPR_OWNERDATA* ownerdata;
1099
1100 assert(eventhdlr != NULL);
1101 assert(expr != NULL);
1102 assert(SCIPisExprVar(scip, expr));
1103 assert(cons != NULL);
1104
1105 ownerdata = SCIPexprGetOwnerData(expr);
1106 assert(ownerdata != NULL);
1107
1108 #ifndef NDEBUG
1109 /* assert that constraint does not double-catch variable */
1110 {
1111 int i;
1112 for( i = 0; i < ownerdata->nconss; ++i )
1113 assert(ownerdata->conss[i] != cons);
1114 }
1115 #endif
1116
1117 /* append cons to ownerdata->conss */
1118 SCIP_CALL( SCIPensureBlockMemoryArray(scip, &ownerdata->conss, &ownerdata->consssize, ownerdata->nconss + 1) );
1119 ownerdata->conss[ownerdata->nconss++] = cons;
1120 /* we're not capturing the constraint here to avoid circular references */
1121
1122 /* updated sorted flag */
1123 if( ownerdata->nconss <= 1 )
1124 ownerdata->consssorted = TRUE;
1125 else if( ownerdata->consssorted )
1126 ownerdata->consssorted = compIndexConsNonlinear(ownerdata->conss[ownerdata->nconss-2], ownerdata->conss[ownerdata->nconss-1]) > 0;
1127
1128 /* catch variable events, if not done so yet (first constraint) */
1129 if( ownerdata->filterpos < 0 )
1130 {
1131 SCIP_EVENTTYPE eventtype;
1132
1133 assert(ownerdata->nconss == 1);
1134
1135 eventtype = SCIP_EVENTTYPE_BOUNDCHANGED | SCIP_EVENTTYPE_VARFIXED | SCIP_EVENTTYPE_TYPECHANGED;
1136
1137 SCIP_CALL( SCIPcatchVarEvent(scip, SCIPgetVarExprVar(expr), eventtype, eventhdlr, (SCIP_EVENTDATA*)expr, &ownerdata->filterpos) );
1138 assert(ownerdata->filterpos >= 0);
1139 }
1140
1141 return SCIP_OKAY;
1142 }
1143
1144 /** catch variable events */
1145 static
1146 SCIP_RETCODE catchVarEvents(
1147 SCIP* scip, /**< SCIP data structure */
1148 SCIP_EVENTHDLR* eventhdlr, /**< event handler */
1149 SCIP_CONS* cons /**< constraint for which to catch bound change events */
1150 )
1151 {
1152 SCIP_CONSHDLRDATA* conshdlrdata;
1153 SCIP_CONSDATA* consdata;
1154 SCIP_EXPR* expr;
1155 int i;
1156
1157 assert(eventhdlr != NULL);
1158 assert(cons != NULL);
1159
1160 consdata = SCIPconsGetData(cons);
1161 assert(consdata != NULL);
1162 assert(consdata->varexprs != NULL);
1163 assert(consdata->nvarexprs >= 0);
1164
1165 /* check if we have catched variable events already */
1166 if( consdata->catchedevents )
1167 return SCIP_OKAY;
1168
1169 conshdlrdata = SCIPconshdlrGetData(SCIPconsGetHdlr(cons));
1170 assert(conshdlrdata != NULL);
1171 #ifndef CR_API /* this assert may not work in unittests due to having this code compiled twice, #3543 */
1172 assert(conshdlrdata->intevalvar == intEvalVarBoundTightening);
1173 #endif
1174
1175 SCIPdebugMsg(scip, "catchVarEvents for %s\n", SCIPconsGetName(cons));
1176
1177 for( i = 0; i < consdata->nvarexprs; ++i )
1178 {
1179 expr = consdata->varexprs[i];
1180
1181 assert(expr != NULL);
1182 assert(SCIPisExprVar(scip, expr));
1183
1184 SCIP_CALL( catchVarEvent(scip, eventhdlr, expr, cons) );
1185
1186 /* from now on, activity of var-expr will usually be updated in processVarEvent if variable bound is changing
1187 * since we just registered this eventhdlr, we should make sure that the activity is also up to date now
1188 */
1189 if( SCIPexprGetActivityTag(expr) < conshdlrdata->curboundstag )
1190 {
1191 SCIP_INTERVAL activity;
1192 SCIP_CALL( SCIPcallExprInteval(scip, expr, &activity, intEvalVarBoundTightening, conshdlrdata) );
1193 /* activity = intEvalVarBoundTightening(scip, SCIPgetVarExprVar(expr), conshdlrdata); */
1194 SCIPexprSetActivity(expr, activity, conshdlrdata->curboundstag);
1195 #ifdef DEBUG_PROP
1196 SCIPdebugMsg(scip, "var-exprhdlr::inteval for var <%s> = [%.20g, %.20g]\n", SCIPvarGetName(SCIPgetVarExprVar(expr)), activity.inf, activity.sup);
1197 #endif
1198 }
1199 }
1200
1201 consdata->catchedevents = TRUE;
1202
1203 return SCIP_OKAY;
1204 }
1205
1206 /** unregisters event handler to catch variable events on variable
1207 *
1208 * The given constraint is removed from the constraints array in the ownerdata of the variable-expression.
1209 * If this was the last constraint, then the event handler is unregistered for this variable.
1210 */
1211 static
1212 SCIP_RETCODE dropVarEvent(
1213 SCIP* scip, /**< SCIP data structure */
1214 SCIP_EVENTHDLR* eventhdlr, /**< event handler */
1215 SCIP_EXPR* expr, /**< variable expression */
1216 SCIP_CONS* cons /**< expr constraint */
1217 )
1218 {
1219 SCIP_EXPR_OWNERDATA* ownerdata;
1220 int pos;
1221
1222 assert(eventhdlr != NULL);
1223 assert(expr != NULL);
1224 assert(SCIPisExprVar(scip, expr));
1225 assert(cons != NULL);
1226
1227 ownerdata = SCIPexprGetOwnerData(expr);
1228 assert(ownerdata != NULL);
1229 assert(ownerdata->nconss > 0);
1230
1231 if( ownerdata->conss[ownerdata->nconss-1] == cons )
1232 {
1233 pos = ownerdata->nconss-1;
1234 }
1235 else
1236 {
1237 if( !ownerdata->consssorted )
1238 {
1239 SCIPsortPtr((void**)ownerdata->conss, compIndexConsNonlinear, ownerdata->nconss);
1240 ownerdata->consssorted = TRUE;
1241 }
1242
1243 if( !SCIPsortedvecFindPtr((void**)ownerdata->conss, compIndexConsNonlinear, cons, ownerdata->nconss, &pos) )
1244 {
1245 SCIPerrorMessage("Constraint <%s> not in constraint array of expression for variable <%s>\n", SCIPconsGetName(cons), SCIPvarGetName(SCIPgetVarExprVar(expr)));
1246 return SCIP_ERROR;
1247 }
1248 assert(pos >= 0 && pos < ownerdata->nconss);
1249 }
1250 assert(ownerdata->conss[pos] == cons);
1251
1252 /* move last constraint into position of removed constraint */
1253 if( pos < ownerdata->nconss-1 )
1254 {
1255 ownerdata->conss[pos] = ownerdata->conss[ownerdata->nconss-1];
1256 ownerdata->consssorted = FALSE;
1257 }
1258 --ownerdata->nconss;
1259
1260 /* drop variable events if that was the last constraint */
1261 if( ownerdata->nconss == 0 )
1262 {
1263 SCIP_EVENTTYPE eventtype;
1264
1265 assert(ownerdata->filterpos >= 0);
1266
1267 eventtype = SCIP_EVENTTYPE_BOUNDCHANGED | SCIP_EVENTTYPE_VARFIXED | SCIP_EVENTTYPE_TYPECHANGED;
1268
1269 SCIP_CALL( SCIPdropVarEvent(scip, SCIPgetVarExprVar(expr), eventtype, eventhdlr, (SCIP_EVENTDATA*)expr, ownerdata->filterpos) );
1270 ownerdata->filterpos = -1;
1271 }
1272
1273 return SCIP_OKAY;
1274 }
1275
1276 /** drop variable events */
1277 static
1278 SCIP_RETCODE dropVarEvents(
1279 SCIP* scip, /**< SCIP data structure */
1280 SCIP_EVENTHDLR* eventhdlr, /**< event handler */
1281 SCIP_CONS* cons /**< constraint for which to drop bound change events */
1282 )
1283 {
1284 SCIP_CONSDATA* consdata;
1285 int i;
1286
1287 assert(eventhdlr != NULL);
1288 assert(cons != NULL);
1289
1290 consdata = SCIPconsGetData(cons);
1291 assert(consdata != NULL);
1292
1293 /* check if we have catched variable events already */
1294 if( !consdata->catchedevents )
1295 return SCIP_OKAY;
1296
1297 assert(consdata->varexprs != NULL);
1298 assert(consdata->nvarexprs >= 0);
1299
1300 SCIPdebugMsg(scip, "dropVarEvents for %s\n", SCIPconsGetName(cons));
1301
1302 for( i = consdata->nvarexprs - 1; i >= 0; --i )
1303 {
1304 assert(consdata->varexprs[i] != NULL);
1305
1306 SCIP_CALL( dropVarEvent(scip, eventhdlr, consdata->varexprs[i], cons) );
1307 }
1308
1309 consdata->catchedevents = FALSE;
1310
1311 return SCIP_OKAY;
1312 }
1313
1314 /** creates and captures a nonlinear constraint
1315 *
1316 * @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
1317 */
1318 static
1319 SCIP_RETCODE createCons(
1320 SCIP* scip, /**< SCIP data structure */
1321 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
1322 SCIP_CONS** cons, /**< pointer to hold the created constraint */
1323 const char* name, /**< name of constraint */
1324 SCIP_EXPR* expr, /**< expression of constraint (must not be NULL) */
1325 SCIP_Real lhs, /**< left hand side of constraint */
1326 SCIP_Real rhs, /**< right hand side of constraint */
1327 SCIP_Bool copyexpr, /**< whether to copy the expression or reuse the given expr (capture it) */
1328 SCIP_Bool initial, /**< should the LP relaxation of constraint be in the initial LP?
1329 * Usually set to TRUE. Set to FALSE for 'lazy constraints'. */
1330 SCIP_Bool separate, /**< should the constraint be separated during LP processing?
1331 * Usually set to TRUE. */
1332 SCIP_Bool enforce, /**< should the constraint be enforced during node processing?
1333 * TRUE for model constraints, FALSE for additional, redundant constraints. */
1334 SCIP_Bool check, /**< should the constraint be checked for feasibility?
1335 * TRUE for model constraints, FALSE for additional, redundant constraints. */
1336 SCIP_Bool propagate, /**< should the constraint be propagated during node processing?
1337 * Usually set to TRUE. */
1338 SCIP_Bool local, /**< is constraint only valid locally?
1339 * Usually set to FALSE. Has to be set to TRUE, e.g., for branching constraints. */
1340 SCIP_Bool modifiable, /**< is constraint modifiable (subject to column generation)?
1341 * Usually set to FALSE. In column generation applications, set to TRUE if pricing
1342 * adds coefficients to this constraint. */
1343 SCIP_Bool dynamic, /**< is constraint subject to aging?
1344 * Usually set to FALSE. Set to TRUE for own cuts which
1345 * are separated as constraints. */
1346 SCIP_Bool removable /**< should the relaxation be removed from the LP due to aging or cleanup?
1347 * Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */
1348 )
1349 {
1350 SCIP_CONSHDLRDATA* conshdlrdata;
1351 SCIP_CONSDATA* consdata;
1352
1353 assert(conshdlr != NULL);
1354 assert(expr != NULL);
1355
1356 conshdlrdata = SCIPconshdlrGetData(conshdlr);
1357 assert(conshdlrdata != NULL);
1358
1359 if( local && SCIPgetDepth(scip) != 0 )
1360 {
1361 SCIPerrorMessage("Locally valid nonlinear constraints are not supported, yet.\n");
1362 return SCIP_INVALIDCALL;
1363 }
1364
1365 /* TODO we should allow for non-initial nonlinear constraints */
1366 if( !initial )
1367 {
1368 SCIPerrorMessage("Non-initial nonlinear constraints are not supported, yet.\n");
1369 return SCIP_INVALIDCALL;
1370 }
1371
1372 /* create constraint data */
1373 SCIP_CALL( SCIPallocClearBlockMemory(scip, &consdata) );
1374
1375 if( copyexpr )
1376 {
1377 /* copy expression, thereby map variables expressions to already existing variables expressions in var2expr map, or augment var2expr map */
1378 SCIP_CALL( SCIPduplicateExpr(scip, expr, &consdata->expr, mapexprvar, conshdlr, exprownerCreate, (void*)conshdlr) );
1379 }
1380 else
1381 {
1382 consdata->expr = expr;
1383 SCIPcaptureExpr(consdata->expr);
1384 }
1385 consdata->lhs = lhs;
1386 consdata->rhs = rhs;
1387 consdata->consindex = conshdlrdata->lastconsindex++;
1388 consdata->curv = SCIP_EXPRCURV_UNKNOWN;
1389
1390 /* create constraint */
1391 SCIP_CALL( SCIPcreateCons(scip, cons, name, conshdlr, consdata, initial, separate, enforce, check, propagate,
1392 local, modifiable, dynamic, removable, FALSE) );
1393
1394 return SCIP_OKAY;
1395 }
1396
1397 /** returns absolute violation for auxvar relation in an expression w.r.t. original variables
1398 *
1399 * Assume the expression is f(x), where x are original (i.e., not auxiliary) variables.
1400 * Assume that f(x) is associated with auxiliary variable z.
1401 *
1402 * If there are negative locks, then return the violation of z ≤ f(x) and sets `violover` to TRUE.
1403 * If there are positive locks, then return the violation of z ≥ f(x) and sets `violunder` to TRUE.
1404 * Of course, if there both negative and positive locks, then return the violation of z = f(x).
1405 * If f could not be evaluated, then return SCIPinfinity() and set both `violover` and `violunder` to TRUE.
1406 *
1407 * @note This does not reevaluate the violation, but assumes that the expression has been evaluated
1408 */
1409 static
1410 SCIP_Real getExprAbsOrigViolation(
1411 SCIP* scip, /**< SCIP data structure */
1412 SCIP_EXPR* expr, /**< expression */
1413 SCIP_SOL* sol, /**< solution that has been evaluated */
1414 SCIP_Bool* violunder, /**< buffer to store whether z >= f(x) is violated, or NULL */
1415 SCIP_Bool* violover /**< buffer to store whether z <= f(x) is violated, or NULL */
1416 )
1417 {
1418 SCIP_EXPR_OWNERDATA* ownerdata;
1419 SCIP_Real auxvarvalue;
1420
1421 assert(expr != NULL);
1422
1423 ownerdata = SCIPexprGetOwnerData(expr);
1424 assert(ownerdata != NULL);
1425 assert(ownerdata->auxvar != NULL);
1426
1427 if( SCIPexprGetEvalValue(expr) == SCIP_INVALID )
1428 {
1429 if( violunder != NULL )
1430 *violunder = TRUE;
1431 if( violover != NULL )
1432 *violover = TRUE;
1433 return SCIPinfinity(scip);
1434 }
1435
1436 auxvarvalue = SCIPgetSolVal(scip, sol, ownerdata->auxvar);
1437
1438 if( ownerdata->nlocksneg > 0 && auxvarvalue > SCIPexprGetEvalValue(expr) )
1439 {
1440 if( violunder != NULL )
1441 *violunder = FALSE;
1442 if( violover != NULL )
1443 *violover = TRUE;
1444 return auxvarvalue - SCIPexprGetEvalValue(expr);
1445 }
1446
1447 if( ownerdata->nlockspos > 0 && SCIPexprGetEvalValue(expr) > auxvarvalue )
1448 {
1449 if( violunder != NULL )
1450 *violunder = TRUE;
1451 if( violover != NULL )
1452 *violover = FALSE;
1453 return SCIPexprGetEvalValue(expr) - auxvarvalue;
1454 }
1455
1456 if( violunder != NULL )
1457 *violunder = FALSE;
1458 if( violover != NULL )
1459 *violover = FALSE;
1460 return 0.0;
1461 }
1462
1463 /** returns absolute violation for auxvar relation in an expression w.r.t. auxiliary variables
1464 *
1465 * Assume the expression is f(w), where w are auxiliary variables that were introduced by some nlhdlr.
1466 * Assume that f(w) is associated with auxiliary variable z.
1467 *
1468 * If there are negative locks, then return the violation of z ≤ f(w) and sets `violover` to TRUE.
1469 * If there are positive locks, then return the violation of z ≥ f(w) and sets `violunder` to TRUE.
1470 * Of course, if there both negative and positive locks, then return the violation of z = f(w).
1471 * If f could not be evaluated, then return SCIPinfinity() and set both `violover` and `violunder` to TRUE.
1472 *
1473 * @note This does not reevaluate the violation, but assumes that f(w) is passed in with auxvalue.
1474 */
1475 static
1476 SCIP_Real getExprAbsAuxViolation(
1477 SCIP* scip, /**< SCIP data structure */
1478 SCIP_EXPR* expr, /**< expression */
1479 SCIP_Real auxvalue, /**< value of f(w) */
1480 SCIP_SOL* sol, /**< solution that has been evaluated */
1481 SCIP_Bool* violunder, /**< buffer to store whether z >= f(w) is violated, or NULL */
1482 SCIP_Bool* violover /**< buffer to store whether z <= f(w) is violated, or NULL */
1483 )
1484 {
1485 SCIP_EXPR_OWNERDATA* ownerdata;
1486 SCIP_Real auxvarvalue;
1487
1488 assert(expr != NULL);
1489
1490 ownerdata = SCIPexprGetOwnerData(expr);
1491 assert(ownerdata != NULL);
1492 assert(ownerdata->auxvar != NULL);
1493
1494 if( auxvalue == SCIP_INVALID )
1495 {
1496 if( violunder != NULL )
1497 *violunder = TRUE;
1498 if( violover != NULL )
1499 *violover = TRUE;
1500 return SCIPinfinity(scip);
1501 }
1502
1503 auxvarvalue = SCIPgetSolVal(scip, sol, ownerdata->auxvar);
1504
1505 if( ownerdata->nlocksneg > 0 && auxvarvalue > auxvalue )
1506 {
1507 if( violunder != NULL )
1508 *violunder = FALSE;
1509 if( violover != NULL )
1510 *violover = TRUE;
1511 return auxvarvalue - auxvalue;
1512 }
1513
1514 if( ownerdata->nlockspos > 0 && auxvalue > auxvarvalue )
1515 {
1516 if( violunder != NULL )
1517 *violunder = TRUE;
1518 if( violover != NULL )
1519 *violover = FALSE;
1520 return auxvalue - auxvarvalue;
1521 }
1522
1523 if( violunder != NULL )
1524 *violunder = FALSE;
1525 if( violover != NULL )
1526 *violover = FALSE;
1527
1528 return 0.0;
1529 }
1530
1531 /** computes violation of a constraint */
1532 static
1533 SCIP_RETCODE computeViolation(
1534 SCIP* scip, /**< SCIP data structure */
1535 SCIP_CONS* cons, /**< constraint */
1536 SCIP_SOL* sol, /**< solution or NULL if LP solution should be used */
1537 SCIP_Longint soltag /**< tag that uniquely identifies the solution (with its values), or 0. */
1538 )
1539 {
1540 SCIP_CONSDATA* consdata;
1541 SCIP_Real activity;
1542
1543 assert(scip != NULL);
1544 assert(cons != NULL);
1545
1546 consdata = SCIPconsGetData(cons);
1547 assert(consdata != NULL);
1548
1549 SCIP_CALL( SCIPevalExpr(scip, consdata->expr, sol, soltag) );
1550 activity = SCIPexprGetEvalValue(consdata->expr);
1551
1552 /* consider constraint as violated if it is undefined in the current point */
1553 if( activity == SCIP_INVALID )
1554 {
1555 consdata->lhsviol = SCIPinfinity(scip);
1556 consdata->rhsviol = SCIPinfinity(scip);
1557 return SCIP_OKAY;
1558 }
1559
1560 /* compute violations */
1561 consdata->lhsviol = SCIPisInfinity(scip, -consdata->lhs) ? -SCIPinfinity(scip) : consdata->lhs - activity;
1562 consdata->rhsviol = SCIPisInfinity(scip, consdata->rhs) ? -SCIPinfinity(scip) : activity - consdata->rhs;
1563
1564 return SCIP_OKAY;
1565 }
1566
1567 /** returns absolute violation of a constraint
1568 *
1569 * @note This does not reevaluate the violation, but assumes that computeViolation() has been called before.
1570 */
1571 static
1572 SCIP_Real getConsAbsViolation(
1573 SCIP_CONS* cons /**< constraint */
1574 )
1575 {
1576 SCIP_CONSDATA* consdata;
1577
1578 assert(cons != NULL);
1579
1580 consdata = SCIPconsGetData(cons);
1581 assert(consdata != NULL);
1582
1583 return MAX3(0.0, consdata->lhsviol, consdata->rhsviol);
1584 }
1585
1586 /** computes relative violation of a constraint
1587 *
1588 * @note This does not reevaluate the violation, but assumes that computeViolation() has been called before.
1589 */
1590 static
1591 SCIP_RETCODE getConsRelViolation(
1592 SCIP* scip, /**< SCIP data structure */
1593 SCIP_CONS* cons, /**< constraint */
1594 SCIP_Real* viol, /**< buffer to store violation */
1595 SCIP_SOL* sol, /**< solution or NULL if LP solution should be used */
1596 SCIP_Longint soltag /**< tag that uniquely identifies the solution (with its values), or 0 */
1597 )
1598 {
1599 SCIP_CONSHDLR* conshdlr;
1600 SCIP_CONSHDLRDATA* conshdlrdata;
1601 SCIP_CONSDATA* consdata;
1602 SCIP_Real scale;
1603
1604 assert(cons != NULL);
1605 assert(viol != NULL);
1606
1607 conshdlr = SCIPconsGetHdlr(cons);
1608 assert(conshdlr != NULL);
1609
1610 conshdlrdata = SCIPconshdlrGetData(conshdlr);
1611 assert(conshdlrdata != NULL);
1612
1613 *viol = getConsAbsViolation(cons);
1614
1615 if( conshdlrdata->violscale == 'n' )
1616 return SCIP_OKAY;
1617
1618 if( SCIPisInfinity(scip, *viol) )
1619 return SCIP_OKAY;
1620
1621 consdata = SCIPconsGetData(cons);
1622 assert(consdata != NULL);
1623
1624 if( conshdlrdata->violscale == 'a' )
1625 {
1626 scale = MAX(1.0, REALABS(SCIPexprGetEvalValue(consdata->expr)));
1627
1628 /* consider value of side that is violated for scaling, too */
1629 if( consdata->lhsviol > 0.0 && REALABS(consdata->lhs) > scale )
1630 {
1631 assert(!SCIPisInfinity(scip, -consdata->lhs));
1632 scale = REALABS(consdata->lhs);
1633 }
1634 else if( consdata->rhsviol > 0.0 && REALABS(consdata->rhs) > scale )
1635 {
1636 assert(!SCIPisInfinity(scip, consdata->rhs));
1637 scale = REALABS(consdata->rhs);
1638 }
1639
1640 *viol /= scale;
1641 return SCIP_OKAY;
1642 }
1643
1644 /* if not 'n' or 'a', then it has to be 'g' at the moment */
1645 assert(conshdlrdata->violscale == 'g');
1646 if( soltag == 0L || consdata->gradnormsoltag != soltag )
1647 {
1648 /* we need the varexprs to conveniently access the gradient */
1649 SCIP_CALL( storeVarExprs(scip, conshdlr, consdata) );
1650
1651 /* update cached value of norm of gradient */
1652 consdata->gradnorm = 0.0;
1653
1654 /* compute gradient */
1655 SCIP_CALL( SCIPevalExprGradient(scip, consdata->expr, sol, soltag) );
1656
1657 /* gradient evaluation error -> no scaling */
1658 if( SCIPexprGetDerivative(consdata->expr) != SCIP_INVALID )
1659 {
1660 int i;
1661 for( i = 0; i < consdata->nvarexprs; ++i )
1662 {
1663 SCIP_Real deriv;
1664
1665 assert(SCIPexprGetDiffTag(consdata->expr) == SCIPexprGetDiffTag(consdata->varexprs[i]));
1666 deriv = SCIPexprGetDerivative(consdata->varexprs[i]);
1667 if( deriv == SCIP_INVALID )
1668 {
1669 /* SCIPdebugMsg(scip, "gradient evaluation error for component %d\n", i); */
1670 consdata->gradnorm = 0.0;
1671 break;
1672 }
1673
1674 consdata->gradnorm += deriv*deriv;
1675 }
1676 }
1677 consdata->gradnorm = sqrt(consdata->gradnorm);
1678 consdata->gradnormsoltag = soltag;
1679 }
1680
1681 *viol /= MAX(1.0, consdata->gradnorm);
1682
1683 return SCIP_OKAY;
1684 }
1685
1686 /** returns whether constraint is currently violated
1687 *
1688 * @note This does not reevaluate the violation, but assumes that computeViolation() has been called before.
1689 */
1690 static
1691 SCIP_Bool isConsViolated(
1692 SCIP* scip, /**< SCIP data structure */
1693 SCIP_CONS* cons /**< constraint */
1694 )
1695 {
1696 return getConsAbsViolation(cons) > SCIPfeastol(scip);
1697 }
1698
1699 /** checks for a linear variable that can be increased or decreased without harming feasibility */
1700 static
1701 void findUnlockedLinearVar(
1702 SCIP* scip, /**< SCIP data structure */
1703 SCIP_CONS* cons /**< constraint */
1704 )
1705 {
1706 SCIP_CONSDATA* consdata;
1707 int poslock;
1708 int neglock;
1709 int i;
1710
1711 assert(cons != NULL);
1712
1713 consdata = SCIPconsGetData(cons);
1714 assert(consdata != NULL);
1715
1716 consdata->linvarincr = NULL;
1717 consdata->linvardecr = NULL;
1718 consdata->linvarincrcoef = 0.0;
1719 consdata->linvardecrcoef = 0.0;
1720
1721 /* root expression is not a sum -> no unlocked linear variable available */
1722 if( !SCIPisExprSum(scip, consdata->expr) )
1723 return;
1724
1725 for( i = 0; i < SCIPexprGetNChildren(consdata->expr); ++i )
1726 {
1727 SCIP_EXPR* child;
1728
1729 child = SCIPexprGetChildren(consdata->expr)[i];
1730 assert(child != NULL);
1731
1732 /* check whether the child is a variable expression */
1733 if( SCIPisExprVar(scip, child) )
1734 {
1735 SCIP_VAR* var = SCIPgetVarExprVar(child);
1736 SCIP_Real coef = SCIPgetCoefsExprSum(consdata->expr)[i];
1737
1738 if( coef > 0.0 )
1739 {
1740 poslock = !SCIPisInfinity(scip, consdata->rhs) ? 1 : 0;
1741 neglock = !SCIPisInfinity(scip, -consdata->lhs) ? 1 : 0;
1742 }
1743 else
1744 {
1745 poslock = !SCIPisInfinity(scip, -consdata->lhs) ? 1 : 0;
1746 neglock = !SCIPisInfinity(scip, consdata->rhs) ? 1 : 0;
1747 }
1748 SCIPdebugMsg(scip, "child <%s> locks: %d %d\n", SCIPvarGetName(var), SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL), SCIPvarGetNLocksUpType(var, SCIP_LOCKTYPE_MODEL));
1749
1750 if( SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL) - neglock == 0 )
1751 {
1752 /* for a*x + f(y) \in [lhs, rhs], we can decrease x without harming other constraints
1753 * if we have already one candidate, then take the one where the loss in the objective function is less
1754 */
1755 if( (consdata->linvardecr == NULL) ||
1756 (SCIPvarGetObj(consdata->linvardecr) / consdata->linvardecrcoef > SCIPvarGetObj(var) / coef) )
1757 {
1758 consdata->linvardecr = var;
1759 consdata->linvardecrcoef = coef;
1760 }
1761 }
1762
1763 if( SCIPvarGetNLocksUpType(var, SCIP_LOCKTYPE_MODEL) - poslock == 0 )
1764 {
1765 /* for a*x + f(y) \in [lhs, rhs], we can increase x without harm
1766 * if we have already one candidate, then take the one where the loss in the objective function is less
1767 */
1768 if( (consdata->linvarincr == NULL) ||
1769 (SCIPvarGetObj(consdata->linvarincr) / consdata->linvarincrcoef > SCIPvarGetObj(var) / coef) )
1770 {
1771 consdata->linvarincr = var;
1772 consdata->linvarincrcoef = coef;
1773 }
1774 }
1775 }
1776 }
1777
1778 assert(consdata->linvarincr == NULL || consdata->linvarincrcoef != 0.0);
1779 assert(consdata->linvardecr == NULL || consdata->linvardecrcoef != 0.0);
1780
1781 if( consdata->linvarincr != NULL )
1782 {
1783 SCIPdebugMsg(scip, "may increase <%s> to become feasible\n", SCIPvarGetName(consdata->linvarincr));
1784 }
1785 if( consdata->linvardecr != NULL )
1786 {
1787 SCIPdebugMsg(scip, "may decrease <%s> to become feasible\n", SCIPvarGetName(consdata->linvardecr));
1788 }
1789 }
1790
1791 /** Given a solution where every nonlinear constraint is either feasible or can be made feasible by
1792 * moving a linear variable, construct the corresponding feasible solution and pass it to the trysol heuristic.
1793 *
1794 * The method assumes that this is always possible and that not all constraints are feasible already.
1795 */
1796 static
1797 SCIP_RETCODE proposeFeasibleSolution(
1798 SCIP* scip, /**< SCIP data structure */
1799 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
1800 SCIP_CONS** conss, /**< constraints to process */
1801 int nconss, /**< number of constraints */
1802 SCIP_SOL* sol, /**< solution to process */
1803 SCIP_Bool* success /**< buffer to store whether we succeeded to construct a solution that satisfies all provided constraints */
1804 )
1805 {
1806 SCIP_CONSHDLRDATA* conshdlrdata;
1807 SCIP_SOL* newsol;
1808 int c;
1809
1810 assert(scip != NULL);
1811 assert(conshdlr != NULL);
1812 assert(conss != NULL || nconss == 0);
1813 assert(success != NULL);
1814
1815 *success = FALSE;
1816
1817 /* don't propose new solutions if not in presolve or solving */
1818 if( SCIPgetStage(scip) < SCIP_STAGE_INITPRESOLVE || SCIPgetStage(scip) >= SCIP_STAGE_SOLVED )
1819 return SCIP_OKAY;
1820
1821 conshdlrdata = SCIPconshdlrGetData(conshdlr);
1822 assert(conshdlrdata != NULL);
1823
1824 if( sol != NULL )
1825 {
1826 SCIP_CALL( SCIPcreateSolCopy(scip, &newsol, sol) );
1827 }
1828 else
1829 {
1830 SCIP_CALL( SCIPcreateLPSol(scip, &newsol, NULL) );
1831 }
1832 SCIP_CALL( SCIPunlinkSol(scip, newsol) );
1833 SCIPdebugMsg(scip, "attempt to make solution from <%s> feasible by shifting linear variable\n",
1834 sol != NULL ? (SCIPsolGetHeur(sol) != NULL ? SCIPheurGetName(SCIPsolGetHeur(sol)) : "tree") : "LP");
1835
1836 for( c = 0; c < nconss; ++c )
1837 {
1838 SCIP_CONSDATA* consdata = SCIPconsGetData(conss[c]); /*lint !e613*/
1839 SCIP_Real viol = 0.0;
1840 SCIP_Real delta;
1841 SCIP_Real gap;
1842
1843 assert(consdata != NULL);
1844
1845 /* get absolute violation and sign */
1846 if( consdata->lhsviol > SCIPfeastol(scip) )
1847 viol = consdata->lhsviol; /* lhs - activity */
1848 else if( consdata->rhsviol > SCIPfeastol(scip) )
1849 viol = -consdata->rhsviol; /* rhs - activity */
1850 else
1851 continue; /* constraint is satisfied */
1852
1853 if( consdata->linvarincr != NULL &&
1854 ((viol > 0.0 && consdata->linvarincrcoef > 0.0) || (viol < 0.0 && consdata->linvarincrcoef < 0.0)) )
1855 {
1856 SCIP_VAR* var = consdata->linvarincr;
1857
1858 /* compute how much we would like to increase var */
1859 delta = viol / consdata->linvarincrcoef;
1860 assert(delta > 0.0);
1861
1862 /* if var has an upper bound, may need to reduce delta */
1863 if( !SCIPisInfinity(scip, SCIPvarGetUbGlobal(var)) )
1864 {
1865 gap = SCIPvarGetUbGlobal(var) - SCIPgetSolVal(scip, newsol, var);
1866 delta = MIN(MAX(0.0, gap), delta);
1867 }
1868 if( SCIPisPositive(scip, delta) )
1869 {
1870 /* if variable is integral, round delta up so that it will still have an integer value */
1871 if( SCIPvarIsIntegral(var) )
1872 delta = SCIPceil(scip, delta);
1873
1874 SCIP_CALL( SCIPincSolVal(scip, newsol, var, delta) );
1875 SCIPdebugMsg(scip, "increase <%s> by %g to %g to remedy lhs-violation %g of cons <%s>\n",
1876 SCIPvarGetName(var), delta, SCIPgetSolVal(scip, newsol, var), viol, SCIPconsGetName(conss[c])); /*lint !e613*/
1877
1878 /* adjust constraint violation, if satisfied go on to next constraint */
1879 viol -= consdata->linvarincrcoef * delta;
1880 if( SCIPisZero(scip, viol) )
1881 continue;
1882 }
1883 }
1884
1885 assert(viol != 0.0);
1886 if( consdata->linvardecr != NULL &&
1887 ((viol > 0.0 && consdata->linvardecrcoef < 0.0) || (viol < 0.0 && consdata->linvardecrcoef > 0.0)) )
1888 {
1889 SCIP_VAR* var = consdata->linvardecr;
1890
1891 /* compute how much we would like to decrease var */
1892 delta = viol / consdata->linvardecrcoef;
1893 assert(delta < 0.0);
1894
1895 /* if var has a lower bound, may need to reduce delta */
1896 if( !SCIPisInfinity(scip, -SCIPvarGetLbGlobal(var)) )
1897 {
1898 gap = SCIPgetSolVal(scip, newsol, var) - SCIPvarGetLbGlobal(var);
1899 delta = MAX(MIN(0.0, gap), delta);
1900 }
1901 if( SCIPisNegative(scip, delta) )
1902 {
1903 /* if variable is integral, round delta down so that it will still have an integer value */
1904 if( SCIPvarIsIntegral(var) )
1905 delta = SCIPfloor(scip, delta);
1906 SCIP_CALL( SCIPincSolVal(scip, newsol, consdata->linvardecr, delta) );
1907 /*lint --e{613} */
1908 SCIPdebugMsg(scip, "increase <%s> by %g to %g to remedy rhs-violation %g of cons <%s>\n",
1909 SCIPvarGetName(var), delta, SCIPgetSolVal(scip, newsol, var), viol, SCIPconsGetName(conss[c]));
1910
1911 /* adjust constraint violation, if satisfied go on to next constraint */
1912 viol -= consdata->linvardecrcoef * delta;
1913 if( SCIPisZero(scip, viol) )
1914 continue;
1915 }
1916 }
1917
1918 /* still here... so probably we could not make constraint feasible due to variable bounds, thus give up */
1919 break;
1920 }
1921
1922 /* if we have a solution that should satisfy all quadratic constraints and has a better objective than the current upper bound,
1923 * then pass it to the trysol heuristic
1924 */
1925 if( c == nconss && (SCIPisInfinity(scip, SCIPgetUpperbound(scip)) || SCIPisSumLT(scip, SCIPgetSolTransObj(scip, newsol), SCIPgetUpperbound(scip))) )
1926 {
1927 SCIPdebugMsg(scip, "pass solution with objective val %g to trysol heuristic\n", SCIPgetSolTransObj(scip, newsol));
1928
1929 assert(conshdlrdata->trysolheur != NULL);
1930 SCIP_CALL( SCIPheurPassSolTrySol(scip, conshdlrdata->trysolheur, newsol) );
1931
1932 *success = TRUE;
1933 }
1934
1935 SCIP_CALL( SCIPfreeSol(scip, &newsol) );
1936
1937 return SCIP_OKAY;
1938 }
1939
1940 /** notify nonlinear handlers to add linearization in new solution that has been found
1941 *
1942 * The idea is that nonlinear handlers add globally valid tight estimators in a given solution as cuts to the cutpool.
1943 *
1944 * Essentially we want to ensure that the LP relaxation is tight in the new solution, if possible.
1945 * As the nonlinear handlers define the extended formulation, they should know whether it is possible to generate a
1946 * cut that is valid and supporting in the given solution.
1947 * For example, for convex constraints, we achieve this by linearizing.
1948 * For SOC, we also linearize, but on a a convex reformulation.
1949 *
1950 * Since linearization may happen in auxiliary variables, we ensure that auxiliary variables are set
1951 * to the eval-value of its expression, i.e., we change sol so it is also feasible in the extended formulation.
1952 */
1953 static
1954 SCIP_RETCODE notifyNlhdlrNewsol(
1955 SCIP* scip, /**< SCIP data structure */
1956 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
1957 SCIP_CONS** conss, /**< constraints */
1958 int nconss, /**< number of constraints */
1959 SCIP_SOL* sol, /**< reference point where to estimate */
1960 SCIP_Bool solisbest /**< whether solution is best */
1961 )
1962 {
1963 SCIP_CONSDATA* consdata;
1964 SCIP_Longint soltag;
1965 SCIP_EXPRITER* it;
1966 SCIP_EXPR* expr;
1967 int c, e;
1968
1969 assert(scip != NULL);
1970 assert(conshdlr != NULL);
1971 assert(conss != NULL || nconss == 0);
1972
1973 ENFOLOG( SCIPinfoMessage(scip, enfologfile, "call nlhdlr sollinearize in new solution from <%s>\n", SCIPheurGetName(SCIPsolGetHeur(sol))); )
1974
1975 /* TODO probably we just evaluated all expressions when checking the sol before it was added
1976 * would be nice to recognize this and skip reevaluating
1977 */
1978 soltag = SCIPgetExprNewSoltag(scip);
1979
1980 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
1981 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) );
1982 SCIPexpriterSetStagesDFS(it, SCIP_EXPRITER_LEAVEEXPR);
1983
1984 for( c = 0; c < nconss; ++c )
1985 {
1986 /* skip constraints that are not enabled or deleted or have separation disabled */
1987 if( !SCIPconsIsEnabled(conss[c]) || SCIPconsIsDeleted(conss[c]) || !SCIPconsIsSeparationEnabled(conss[c]) )
1988 continue;
1989 assert(SCIPconsIsActive(conss[c]));
1990
1991 consdata = SCIPconsGetData(conss[c]);
1992 assert(consdata != NULL);
1993
1994 ENFOLOG(
1995 {
1996 int i;
1997 SCIPinfoMessage(scip, enfologfile, " constraint ");
1998 SCIP_CALL( SCIPprintCons(scip, conss[c], enfologfile) );
1999 SCIPinfoMessage(scip, enfologfile, "\n and point\n");
2000 for( i = 0; i < consdata->nvarexprs; ++i )
2001 {
2002 SCIP_VAR* var;
2003 var = SCIPgetVarExprVar(consdata->varexprs[i]);
2004 SCIPinfoMessage(scip, enfologfile, " %-10s = %15g bounds: [%15g,%15g]\n", SCIPvarGetName(var),
2005 SCIPgetSolVal(scip, sol, var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var));
2006 }
2007 })
2008
2009 SCIP_CALL( SCIPevalExpr(scip, consdata->expr, sol, soltag) );
2010 assert(SCIPexprGetEvalValue(consdata->expr) != SCIP_INVALID);
2011
2012 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
2013 {
2014 SCIP_EXPR_OWNERDATA* ownerdata;
2015
2016 ownerdata = SCIPexprGetOwnerData(expr);
2017 assert(ownerdata != NULL);
2018
2019 /* set value for auxvar in sol to value of expr, in case it is used to compute estimators higher up of this expression */
2020 assert(SCIPexprGetEvalTag(expr) == soltag);
2021 assert(SCIPexprGetEvalValue(expr) != SCIP_INVALID);
2022 if( ownerdata->auxvar != NULL )
2023 {
2024 SCIP_CALL( SCIPsetSolVal(scip, sol, ownerdata->auxvar, SCIPexprGetEvalValue(expr)) );
2025 }
2026
2027 /* let nonlinear handler generate cuts by calling the sollinearize callback */
2028 for( e = 0; e < ownerdata->nenfos; ++e )
2029 {
2030 /* call sollinearize callback, if implemented by nlhdlr */
2031 SCIP_CALL( SCIPnlhdlrSollinearize(scip, conshdlr, conss[c],
2032 ownerdata->enfos[e]->nlhdlr, expr, ownerdata->enfos[e]->nlhdlrexprdata, sol, solisbest,
2033 ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPAABOVE,
2034 ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPABELOW) );
2035 }
2036 }
2037 }
2038
2039 SCIPfreeExpriter(&it);
2040
2041 return SCIP_OKAY;
2042 }
2043
2044 /** processes the event that a new primal solution has been found */
2045 static
2046 SCIP_DECL_EVENTEXEC(processNewSolutionEvent)
2047 {
2048 SCIP_CONSHDLR* conshdlr;
2049 SCIP_CONSHDLRDATA* conshdlrdata;
2050 SCIP_SOL* sol;
2051
2052 assert(scip != NULL);
2053 assert(event != NULL);
2054 assert(eventdata != NULL);
2055 assert(eventhdlr != NULL);
2056 assert(SCIPeventGetType(event) & SCIP_EVENTTYPE_SOLFOUND);
2057
2058 conshdlr = (SCIP_CONSHDLR*)eventdata;
2059
2060 if( SCIPconshdlrGetNConss(conshdlr) == 0 )
2061 return SCIP_OKAY;
2062
2063 sol = SCIPeventGetSol(event);
2064 assert(sol != NULL);
2065
2066 conshdlrdata = SCIPconshdlrGetData(conshdlr);
2067 assert(conshdlrdata != NULL);
2068
2069 /* we are only interested in solution coming from some heuristic other than trysol, but not from the tree
2070 * the reason for ignoring trysol solutions is that they may come ~~from an NLP solve in sepalp, where we already added linearizations, or are~~
2071 * from the tree, but postprocessed via proposeFeasibleSolution
2072 */
2073 if( SCIPsolGetHeur(sol) == NULL || SCIPsolGetHeur(sol) == conshdlrdata->trysolheur )
2074 return SCIP_OKAY;
2075
2076 SCIPdebugMsg(scip, "caught new sol event %" SCIP_EVENTTYPE_FORMAT " from heur <%s>\n", SCIPeventGetType(event), SCIPheurGetName(SCIPsolGetHeur(sol)));
2077
2078 SCIP_CALL( notifyNlhdlrNewsol(scip, conshdlr, SCIPconshdlrGetConss(conshdlr), SCIPconshdlrGetNConss(conshdlr), sol, (SCIPeventGetType(event) & SCIP_EVENTTYPE_BESTSOLFOUND) != 0) );
2079
2080 return SCIP_OKAY;
2081 }
2082
2083 /** tightens the bounds of the auxiliary variable associated with an expression (or original variable if being a variable-expression) according to given bounds
2084 *
2085 * The given bounds may very well be the exprs activity (when called from forwardPropExpr()), but can also be some
2086 * tighter bounds (when called from SCIPtightenExprIntervalNonlinear()).
2087 *
2088 * Nothing will happen if SCIP is not in presolve or solve.
2089 */
2090 static
2091 SCIP_RETCODE tightenAuxVarBounds(
2092 SCIP* scip, /**< SCIP data structure */
2093 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
2094 SCIP_EXPR* expr, /**< expression whose auxvar is to be tightened */
2095 SCIP_INTERVAL bounds, /**< bounds to be used for tightening (must not be empty) */
2096 SCIP_Bool* cutoff, /**< buffer to store whether a cutoff was detected */
2097 int* ntightenings /**< buffer to add the total number of tightenings, or NULL */
2098 )
2099 {
2100 SCIP_VAR* var;
2101 SCIP_Bool tightenedlb;
2102 SCIP_Bool tightenedub;
2103 SCIP_Bool force;
2104
2105 assert(scip != NULL);
2106 assert(conshdlr != NULL);
2107 assert(expr != NULL);
2108 assert(cutoff != NULL);
2109
2110 /* the given bounds must not be empty (we could cope, but we shouldn't be called in this situation) */
2111 assert(!SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, bounds));
2112
2113 *cutoff = FALSE;
2114
2115 /* do not tighten variable in problem stage (important for unittests)
2116 * TODO put some kind of #ifdef UNITTEST around this
2117 */
2118 if( SCIPgetStage(scip) < SCIP_STAGE_INITPRESOLVE && SCIPgetStage(scip) > SCIP_STAGE_SOLVING )
2119 return SCIP_OKAY;
2120
2121 var = SCIPgetExprAuxVarNonlinear(expr);
2122 if( var == NULL )
2123 return SCIP_OKAY;
2124
2125 /* force tightening if conshdlrdata says so or it would mean fixing the variable */
2126 force = SCIPconshdlrGetData(conshdlr)->forceboundtightening || SCIPisEQ(scip, bounds.inf, bounds.sup);
2127
2128 /* try to tighten lower bound of (auxiliary) variable */
2129 SCIP_CALL( SCIPtightenVarLb(scip, var, bounds.inf, force, cutoff, &tightenedlb) );
2130 if( tightenedlb )
2131 {
2132 if( ntightenings != NULL )
2133 ++*ntightenings;
2134 SCIPdebugMsg(scip, "tightened lb on auxvar <%s> to %.15g (forced:%u)\n", SCIPvarGetName(var), SCIPvarGetLbLocal(var), force);
2135 }
2136 if( *cutoff )
2137 {
2138 SCIPdebugMsg(scip, "cutoff when tightening lb on auxvar <%s> to %.15g\n", SCIPvarGetName(var), bounds.inf);
2139 return SCIP_OKAY;
2140 }
2141
2142 /* try to tighten upper bound of (auxiliary) variable */
2143 SCIP_CALL( SCIPtightenVarUb(scip, var, bounds.sup, force, cutoff, &tightenedub) );
2144 if( tightenedub )
2145 {
2146 if( ntightenings != NULL )
2147 ++*ntightenings;
2148 SCIPdebugMsg(scip, "tightened ub on auxvar <%s> to %.15g (forced:%u)\n", SCIPvarGetName(var), SCIPvarGetUbLocal(var), force);
2149 }
2150 if( *cutoff )
2151 {
2152 SCIPdebugMsg(scip, "cutoff when tightening ub on auxvar <%s> to %.15g\n", SCIPvarGetName(var), bounds.sup);
2153 return SCIP_OKAY;
2154 }
2155
2156 /* TODO expr->activity should have been reevaluated now due to boundchange-events, but it used to relax bounds
2157 * that seems unnecessary and we could easily undo this here, e.g.,
2158 * if( tightenedlb ) expr->activity.inf = bounds.inf
2159 */
2160
2161 return SCIP_OKAY;
2162 }
2163
2164 /** propagate bounds of the expressions in a given expression tree (that is, updates activity intervals)
2165 * and tries to tighten the bounds of the auxiliary variables accordingly
2166 */
2167 static
2168 SCIP_RETCODE forwardPropExpr(
2169 SCIP* scip, /**< SCIP data structure */
2170 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
2171 SCIP_EXPR* rootexpr, /**< expression */
2172 SCIP_Bool tightenauxvars, /**< should the bounds of auxiliary variables be tightened? */
2173 SCIP_Bool* infeasible, /**< buffer to store whether the problem is infeasible (NULL if not needed) */
2174 int* ntightenings /**< buffer to store the number of auxiliary variable tightenings (NULL if not needed) */
2175 )
2176 {
2177 SCIP_EXPRITER* it;
2178 SCIP_EXPR* expr;
2179 SCIP_EXPR_OWNERDATA* ownerdata;
2180 SCIP_CONSHDLRDATA* conshdlrdata;
2181
2182 assert(scip != NULL);
2183 assert(rootexpr != NULL);
2184
2185 if( infeasible != NULL )
2186 *infeasible = FALSE;
2187 if( ntightenings != NULL )
2188 *ntightenings = 0;
2189
2190 conshdlrdata = SCIPconshdlrGetData(conshdlr);
2191 assert(conshdlrdata != NULL);
2192
2193 /* if value is valid and empty, then we cannot improve, so do nothing */
2194 if( SCIPexprGetActivityTag(rootexpr) >= conshdlrdata->lastboundrelax && SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(rootexpr)) )
2195 {
2196 SCIPdebugMsg(scip, "stored activity of root expr is empty and valid (activitytag >= lastboundrelax (%" SCIP_LONGINT_FORMAT ")), skip forwardPropExpr -> cutoff\n", conshdlrdata->lastboundrelax);
2197
2198 if( infeasible != NULL )
2199 *infeasible = TRUE;
2200
2201 /* just update tag to curboundstag */
2202 SCIPexprSetActivity(rootexpr, SCIPexprGetActivity(rootexpr), conshdlrdata->curboundstag);
2203
2204 return SCIP_OKAY;
2205 }
2206
2207 /* if value is up-to-date, then nothing to do */
2208 if( SCIPexprGetActivityTag(rootexpr) == conshdlrdata->curboundstag )
2209 {
2210 SCIPdebugMsg(scip, "activitytag of root expr equals curboundstag (%" SCIP_LONGINT_FORMAT "), skip forwardPropExpr\n", conshdlrdata->curboundstag);
2211
2212 assert(!SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(rootexpr))); /* handled in previous if() */
2213
2214 return SCIP_OKAY;
2215 }
2216
2217 ownerdata = SCIPexprGetOwnerData(rootexpr);
2218 assert(ownerdata != NULL);
2219
2220 /* if activity of rootexpr is not used, but expr participated in detect (nenfos >= 0), then we do nothing
2221 * it seems wrong to be called for such an expression (unless we are in detect at the moment), so I add a SCIPABORT()
2222 * during detect, we are in some in-between state where we may want to eval activity
2223 * on exprs that we did not notify about their activity usage
2224 */
2225 if( ownerdata->nenfos >= 0 && ownerdata->nactivityusesprop == 0 && ownerdata->nactivityusessepa == 0 && !conshdlrdata->indetect)
2226 {
2227 #ifdef DEBUG_PROP
2228 SCIPdebugMsg(scip, "root expr activity is not used but enfo initialized, skip inteval\n");
2229 #endif
2230 SCIPABORT();
2231 return SCIP_OKAY;
2232 }
2233
2234 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
2235 SCIP_CALL( SCIPexpriterInit(it, rootexpr, SCIP_EXPRITER_DFS, TRUE) );
2236 SCIPexpriterSetStagesDFS(it, SCIP_EXPRITER_VISITINGCHILD | SCIP_EXPRITER_LEAVEEXPR);
2237
2238 for( expr = SCIPexpriterGetCurrent(it); !SCIPexpriterIsEnd(it); )
2239 {
2240 switch( SCIPexpriterGetStageDFS(it) )
2241 {
2242 case SCIP_EXPRITER_VISITINGCHILD :
2243 {
2244 /* skip child if it has been evaluated already */
2245 SCIP_EXPR* child;
2246
2247 child = SCIPexpriterGetChildExprDFS(it);
2248 if( conshdlrdata->curboundstag == SCIPexprGetActivityTag(child) )
2249 {
2250 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(child)) && infeasible != NULL )
2251 *infeasible = TRUE;
2252
2253 expr = SCIPexpriterSkipDFS(it);
2254 continue;
2255 }
2256
2257 break;
2258 }
2259
2260 case SCIP_EXPRITER_LEAVEEXPR :
2261 {
2262 SCIP_INTERVAL activity;
2263
2264 /* we should not have entered this expression if its activity was already up to date */
2265 assert(SCIPexprGetActivityTag(expr) < conshdlrdata->curboundstag);
2266
2267 ownerdata = SCIPexprGetOwnerData(expr);
2268 assert(ownerdata != NULL);
2269
2270 /* for var exprs where varevents are catched, activity is updated immediately when the varbound has been changed
2271 * so we can assume that the activity is up to date for all these variables
2272 * UNLESS we changed the method used to evaluate activity of variable expressions
2273 * or we currently use global bounds (varevents are catched for local bound changes only)
2274 */
2275 if( SCIPisExprVar(scip, expr) && ownerdata->filterpos >= 0 &&
2276 SCIPexprGetActivityTag(expr) >= conshdlrdata->lastvaractivitymethodchange && !conshdlrdata->globalbounds )
2277 {
2278 #ifndef NDEBUG
2279 SCIP_INTERVAL exprhdlrinterval;
2280
2281 SCIP_CALL( SCIPcallExprInteval(scip, expr, &exprhdlrinterval, conshdlrdata->intevalvar, conshdlrdata) );
2282 assert(SCIPisRelEQ(scip, exprhdlrinterval.inf, SCIPexprGetActivity(expr).inf));
2283 assert(SCIPisRelEQ(scip, exprhdlrinterval.sup, SCIPexprGetActivity(expr).sup));
2284 #endif
2285 #ifdef DEBUG_PROP
2286 SCIPdebugMsg(scip, "skip interval evaluation of expr for var <%s> [%g,%g]\n", SCIPvarGetName(SCIPgetVarExprVar(expr)), SCIPexprGetActivity(expr).inf, SCIPexprGetActivity(expr).sup);
2287 #endif
2288 SCIPexprSetActivity(expr, SCIPexprGetActivity(expr), conshdlrdata->curboundstag);
2289
2290 break;
2291 }
2292
2293 if( SCIPexprGetActivityTag(expr) < conshdlrdata->lastboundrelax )
2294 {
2295 /* start with entire activity if current one is invalid */
2296 SCIPintervalSetEntire(SCIP_INTERVAL_INFINITY, &activity);
2297 }
2298 else if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(expr)) )
2299 {
2300 /* If already empty, then don't try to compute even better activity.
2301 * If cons_nonlinear were alone, then we should have noted that we are infeasible
2302 * so an assert(infeasible == NULL || *infeasible) should work here.
2303 * However, after reporting a cutoff due to expr->activity being empty,
2304 * SCIP may wander to a different node and call propagation again.
2305 * If no bounds in a nonlinear constraint have been relaxed when switching nodes
2306 * (so expr->activitytag >= conshdlrdata->lastboundrelax), then
2307 * we will still have expr->activity being empty, but will have forgotten
2308 * that we found infeasibility here before (!2221#note_134120).
2309 * Therefore we just set *infeasibility=TRUE here and stop.
2310 */
2311 if( infeasible != NULL )
2312 *infeasible = TRUE;
2313 SCIPdebugMsg(scip, "expr %p already has empty activity -> cutoff\n", (void*)expr);
2314 break;
2315 }
2316 else
2317 {
2318 /* start with current activity, since it is valid */
2319 activity = SCIPexprGetActivity(expr);
2320 }
2321
2322 /* if activity of expr is not used, but expr participated in detect (nenfos >= 0), then do nothing */
2323 if( ownerdata->nenfos >= 0 && ownerdata->nactivityusesprop == 0 && ownerdata->nactivityusessepa == 0 && !conshdlrdata->indetect )
2324 {
2325 #ifdef DEBUG_PROP
2326 SCIPdebugMsg(scip, "expr %p activity is not used but enfo initialized, skip inteval\n", (void*)expr);
2327 #endif
2328 break;
2329 }
2330
2331 #ifdef DEBUG_PROP
2332 SCIPdebugMsg(scip, "interval evaluation of expr %p ", (void*)expr);
2333 SCIP_CALL( SCIPprintExpr(scip, expr, NULL) );
2334 SCIPdebugMsgPrint(scip, ", current activity = [%.20g, %.20g]\n", SCIPexprGetActivity(expr).inf, SCIPexprGetActivity(expr).sup);
2335 #endif
2336
2337 /* run interval eval of nonlinear handlers or expression handler */
2338 if( ownerdata->nenfos > 0 )
2339 {
2340 SCIP_NLHDLR* nlhdlr;
2341 SCIP_INTERVAL nlhdlrinterval;
2342 int e;
2343
2344 /* for expressions with enforcement, nlhdlrs take care of interval evaluation */
2345 for( e = 0; e < ownerdata->nenfos && !SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, activity); ++e )
2346 {
2347 /* skip nlhdlr if it does not want to participate in activity computation */
2348 if( (ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_ACTIVITY) == 0 )
2349 continue;
2350
2351 nlhdlr = ownerdata->enfos[e]->nlhdlr;
2352 assert(nlhdlr != NULL);
2353
2354 /* skip nlhdlr if it does not provide interval evaluation (so it may only provide reverse propagation) */
2355 if( !SCIPnlhdlrHasIntEval(nlhdlr) )
2356 continue;
2357
2358 /* let nlhdlr evaluate current expression */
2359 nlhdlrinterval = activity;
2360 SCIP_CALL( SCIPnlhdlrInteval(scip, nlhdlr, expr, ownerdata->enfos[e]->nlhdlrexprdata,
2361 &nlhdlrinterval, conshdlrdata->intevalvar, conshdlrdata) );
2362 #ifdef DEBUG_PROP
2363 SCIPdebugMsg(scip, " nlhdlr <%s>::inteval = [%.20g, %.20g]", SCIPnlhdlrGetName(nlhdlr), nlhdlrinterval.inf, nlhdlrinterval.sup);
2364 #endif
2365
2366 /* update activity by intersecting with computed activity */
2367 SCIPintervalIntersectEps(&activity, SCIPepsilon(scip), activity, nlhdlrinterval);
2368 #ifdef DEBUG_PROP
2369 SCIPdebugMsgPrint(scip, " -> new activity: [%.20g, %.20g]\n", activity.inf, activity.sup);
2370 #endif
2371 }
2372 }
2373 else
2374 {
2375 /* for node without enforcement (before or during detect), call the callback of the exprhdlr directly */
2376 SCIP_INTERVAL exprhdlrinterval = activity;
2377 SCIP_CALL( SCIPcallExprInteval(scip, expr, &exprhdlrinterval, conshdlrdata->intevalvar, conshdlrdata) );
2378 #ifdef DEBUG_PROP
2379 SCIPdebugMsg(scip, " exprhdlr <%s>::inteval = [%.20g, %.20g]", SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)), exprhdlrinterval.inf, exprhdlrinterval.sup);
2380 #endif
2381
2382 /* update expr->activity by intersecting with computed activity */
2383 SCIPintervalIntersectEps(&activity, SCIPepsilon(scip), activity, exprhdlrinterval);
2384 #ifdef DEBUG_PROP
2385 SCIPdebugMsgPrint(scip, " -> new activity: [%.20g, %.20g]\n", activity.inf, activity.sup);
2386 #endif
2387 }
2388
2389 /* if expression is integral, then we try to tighten the interval bounds a bit
2390 * this should undo the addition of some unnecessary safety added by use of nextafter() in interval arithmetics, e.g., when doing pow()
2391 * it would be ok to use ceil() and floor(), but for safety we use SCIPceil and SCIPfloor for now
2392 * do this only if using boundtightening-inteval and not in redundancy check (there we really want to relax all variables)
2393 * boundtightening-inteval does not relax integer variables, so can omit expressions without children
2394 * (constants should be ok, too)
2395 */
2396 if( SCIPexprIsIntegral(expr) && conshdlrdata->intevalvar == intEvalVarBoundTightening && SCIPexprGetNChildren(expr) > 0 )
2397 {
2398 if( activity.inf > -SCIP_INTERVAL_INFINITY )
2399 activity.inf = SCIPceil(scip, activity.inf);
2400 if( activity.sup < SCIP_INTERVAL_INFINITY )
2401 activity.sup = SCIPfloor(scip, activity.sup);
2402 #ifdef DEBUG_PROP
2403 SCIPdebugMsg(scip, " applying integrality: [%.20g, %.20g]\n", activity.inf, activity.sup);
2404 #endif
2405 }
2406
2407 /* mark the current node to be infeasible if either the lower/upper bound is above/below +/- SCIPinfinity()
2408 * TODO this is a problem if dual-presolve fixed a variable to +/- infinity
2409 */
2410 if( SCIPisInfinity(scip, activity.inf) || SCIPisInfinity(scip, -activity.sup) )
2411 {
2412 SCIPdebugMsg(scip, "cut off due to activity [%g,%g] beyond infinity\n", activity.inf, activity.sup);
2413 SCIPintervalSetEmpty(&activity);
2414 }
2415
2416 /* now finally store activity in expr */
2417 SCIPexprSetActivity(expr, activity, conshdlrdata->curboundstag);
2418
2419 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, activity) )
2420 {
2421 if( infeasible != NULL )
2422 *infeasible = TRUE;
2423 }
2424 else if( tightenauxvars && ownerdata->auxvar != NULL )
2425 {
2426 SCIP_Bool tighteninfeasible;
2427
2428 SCIP_CALL( tightenAuxVarBounds(scip, conshdlr, expr, activity, &tighteninfeasible, ntightenings) );
2429 if( tighteninfeasible )
2430 {
2431 if( infeasible != NULL )
2432 *infeasible = TRUE;
2433 SCIPintervalSetEmpty(&activity);
2434 SCIPexprSetActivity(expr, activity, conshdlrdata->curboundstag);
2435 }
2436 }
2437
2438 break;
2439 }
2440
2441 default:
2442 /* you should never be here */
2443 SCIPerrorMessage("unexpected iterator stage\n");
2444 SCIPABORT();
2445 break;
2446 }
2447
2448 expr = SCIPexpriterGetNext(it);
2449 }
2450
2451 SCIPfreeExpriter(&it);
2452
2453 return SCIP_OKAY;
2454 }
2455
2456 /** returns whether intersecting `oldinterval` with `newinterval` would provide a properly smaller interval
2457 *
2458 * If `subsetsufficient` is TRUE, then the intersection being smaller than oldinterval is sufficient.
2459 *
2460 * If `subsetsufficient` is FALSE, then we require
2461 * - a change from an unbounded interval to a bounded one, or
2462 * - or a change from an unfixed (width > epsilon) to a fixed interval, or
2463 * - a minimal tightening of one of the interval bounds as defined by SCIPis{Lb,Ub}Better().
2464 */
2465 static
2466 SCIP_Bool isIntervalBetter(
2467 SCIP* scip, /**< SCIP data structure */
2468 SCIP_Bool subsetsufficient, /**< whether the intersection being a proper subset of oldinterval is sufficient */
2469 SCIP_INTERVAL newinterval, /**< new interval */
2470 SCIP_INTERVAL oldinterval /**< old interval */
2471 )
2472 {
2473 assert(scip != NULL);
2474 assert(!SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, newinterval));
2475 assert(!SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, oldinterval));
2476
2477 if( subsetsufficient )
2478 /* oldinterval \cap newinterval < oldinterval iff not oldinterval is subset of newinterval */
2479 return !SCIPintervalIsSubsetEQ(SCIP_INTERVAL_INFINITY, oldinterval, newinterval);
2480
2481 /* check whether lower bound of interval becomes finite */
2482 if( oldinterval.inf <= -SCIP_INTERVAL_INFINITY && newinterval.inf > -SCIP_INTERVAL_INFINITY )
2483 return TRUE;
2484
2485 /* check whether upper bound of interval becomes finite */
2486 if( oldinterval.sup >= SCIP_INTERVAL_INFINITY && newinterval.sup > SCIP_INTERVAL_INFINITY )
2487 return TRUE;
2488
2489 /* check whether intersection will have width <= epsilon, if oldinterval doesn't have yet */
2490 if( !SCIPisEQ(scip, oldinterval.inf, oldinterval.sup) && SCIPisEQ(scip, MAX(oldinterval.inf, newinterval.inf), MIN(oldinterval.sup, newinterval.sup)) )
2491 return TRUE;
2492
2493 /* check whether lower bound on interval will be better by SCIP's quality measures for boundchanges */
2494 if( SCIPisLbBetter(scip, newinterval.inf, oldinterval.inf, oldinterval.sup) )
2495 return TRUE;
2496
2497 /* check whether upper bound on interval will be better by SCIP's quality measures for boundchanges */
2498 if( SCIPisUbBetter(scip, newinterval.sup, oldinterval.inf, oldinterval.sup) )
2499 return TRUE;
2500
2501 return FALSE;
2502 }
2503
2504 /** propagates bounds for each sub-expression in the `reversepropqueue` by starting from the root expressions
2505 *
2506 * The expression will be traversed in breadth first search by using this queue.
2507 *
2508 * @note Calling this function requires feasible intervals for each sub-expression; this is guaranteed by calling
2509 * forwardPropExpr() before calling this function.
2510 *
2511 * @note Calling this function with `*infeasible` = TRUE will only empty the queue.
2512 */
2513 static
2514 SCIP_RETCODE reversePropQueue(
2515 SCIP* scip, /**< SCIP data structure */
2516 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
2517 SCIP_Bool* infeasible, /**< buffer to update whether an expression's bounds were propagated to an empty interval */
2518 int* ntightenings /**< buffer to store the number of (variable) tightenings */
2519 )
2520 {
2521 SCIP_CONSHDLRDATA* conshdlrdata;
2522 SCIP_EXPR* expr;
2523 SCIP_EXPR_OWNERDATA* ownerdata;
2524
2525 assert(infeasible != NULL);
2526 assert(ntightenings != NULL);
2527
2528 conshdlrdata = SCIPconshdlrGetData(conshdlr);
2529 assert(conshdlrdata != NULL);
2530
2531 *ntightenings = 0;
2532
2533 /* main loop that calls reverse propagation for expressions on the queue
2534 * when reverseprop finds a tightening for an expression, then that expression is added to the queue (within the reverseprop call)
2535 */
2536 while( !SCIPqueueIsEmpty(conshdlrdata->reversepropqueue) && !(*infeasible) )
2537 {
2538 SCIP_INTERVAL propbounds;
2539 int e;
2540
2541 expr = (SCIP_EXPR*) SCIPqueueRemove(conshdlrdata->reversepropqueue);
2542 assert(expr != NULL);
2543
2544 ownerdata = SCIPexprGetOwnerData(expr);
2545 assert(ownerdata != NULL);
2546
2547 assert(ownerdata->inpropqueue);
2548 /* mark that the expression is not in the queue anymore */
2549 ownerdata->inpropqueue = FALSE;
2550
2551 /* since the expr was in the propagation queue, the propbounds should belong to current propagation and should not be empty
2552 * (propbounds being entire doesn't make much sense, so assert this for now, too, but that could be removed)
2553 */
2554 assert(ownerdata->propboundstag == conshdlrdata->curpropboundstag);
2555 assert(!SCIPintervalIsEntire(SCIP_INTERVAL_INFINITY, ownerdata->propbounds));
2556 assert(!SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, ownerdata->propbounds));
2557
2558 /* this intersects propbounds with activity and auxvar bounds
2559 * I doubt this would be much helpful, since propbounds are already subset of activity and we also propagate
2560 * auxvar bounds separately, so disabling this for now
2561 */
2562 #ifdef SCIP_DISABLED_CODE
2563 propbounds = SCIPgetExprBoundsNonlinear(scip, expr);
2564 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, propbounds) )
2565 {
2566 *infeasible = TRUE;
2567 break;
2568 }
2569 #else
2570 propbounds = ownerdata->propbounds;
2571 #endif
2572
2573 if( ownerdata->nenfos > 0 )
2574 {
2575 /* for nodes with enforcement, call reverse propagation callbacks of nlhdlrs */
2576 for( e = 0; e < ownerdata->nenfos && !*infeasible; ++e )
2577 {
2578 SCIP_NLHDLR* nlhdlr;
2579 int nreds;
2580
2581 /* skip nlhdlr if it does not want to participate in activity computation */
2582 if( (ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_ACTIVITY) == 0 )
2583 continue;
2584
2585 nlhdlr = ownerdata->enfos[e]->nlhdlr;
2586 assert(nlhdlr != NULL);
2587
2588 /* call the reverseprop of the nlhdlr */
2589 #ifdef SCIP_DEBUG
2590 SCIPdebugMsg(scip, "call reverse propagation for ");
2591 SCIP_CALL( SCIPprintExpr(scip, expr, NULL) );
2592 SCIPdebugMsgPrint(scip, " in [%g,%g] using nlhdlr <%s>\n", propbounds.inf, propbounds.sup, SCIPnlhdlrGetName(nlhdlr));
2593 #endif
2594
2595 nreds = 0;
2596 SCIP_CALL( SCIPnlhdlrReverseprop(scip, conshdlr, nlhdlr, expr, ownerdata->enfos[e]->nlhdlrexprdata, propbounds, infeasible, &nreds) );
2597 assert(nreds >= 0);
2598 *ntightenings += nreds;
2599 }
2600 }
2601 else if( SCIPexprhdlrHasReverseProp(SCIPexprGetHdlr(expr)) )
2602 {
2603 /* if expr without enforcement (before detect), call reverse propagation callback of exprhdlr directly */
2604 SCIP_INTERVAL* childrenbounds;
2605 int c;
2606
2607 #ifdef SCIP_DEBUG
2608 SCIPdebugMsg(scip, "call reverse propagation for ");
2609 SCIP_CALL( SCIPprintExpr(scip, expr, NULL) );
2610 SCIPdebugMsgPrint(scip, " in [%g,%g] using exprhdlr <%s>\n", SCIPexprGetActivity(expr).inf, SCIPexprGetActivity(expr).sup, SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)));
2611 #endif
2612
2613 /* if someone added an expr without nlhdlr into the reversepropqueue, then this must be because its enfo hasn't
2614 * been initialized in detectNlhdlr yet (nenfos < 0)
2615 */
2616 assert(ownerdata->nenfos < 0);
2617
2618 SCIP_CALL( SCIPallocBufferArray(scip, &childrenbounds, SCIPexprGetNChildren(expr)) );
2619 for( c = 0; c < SCIPexprGetNChildren(expr); ++c )
2620 childrenbounds[c] = SCIPgetExprBoundsNonlinear(scip, SCIPexprGetChildren(expr)[c]);
2621
2622 /* call the reverseprop of the exprhdlr */
2623 SCIP_CALL( SCIPcallExprReverseprop(scip, expr, propbounds, childrenbounds, infeasible) );
2624
2625 if( !*infeasible )
2626 for( c = 0; c < SCIPexprGetNChildren(expr); ++c )
2627 {
2628 SCIP_CALL( SCIPtightenExprIntervalNonlinear(scip, SCIPexprGetChildren(expr)[c], childrenbounds[c], infeasible, ntightenings) );
2629 }
2630
2631 SCIPfreeBufferArray(scip, &childrenbounds);
2632 }
2633 }
2634
2635 /* reset inpropqueue for all remaining expr's in queue (can happen in case of early stop due to infeasibility) */
2636 while( !SCIPqueueIsEmpty(conshdlrdata->reversepropqueue) )
2637 {
2638 expr = (SCIP_EXPR*) SCIPqueueRemove(conshdlrdata->reversepropqueue);
2639 assert(expr != NULL);
2640
2641 ownerdata = SCIPexprGetOwnerData(expr);
2642 assert(ownerdata != NULL);
2643
2644 /* mark that the expression is not in the queue anymore */
2645 ownerdata->inpropqueue = FALSE;
2646 }
2647
2648 return SCIP_OKAY;
2649 }
2650
2651 /** calls domain propagation for a given set of constraints
2652 *
2653 * The algorithm alternates calls of forward and reverse propagation.
2654 * Forward propagation ensures that activity of expressions is up to date.
2655 * Reverse propagation tries to derive tighter variable bounds by reversing the activity computation, using the constraints
2656 * [lhs,rhs] interval as starting point.
2657 *
2658 * The propagation algorithm works as follows:
2659 * 1. apply forward propagation (update activities) for all constraints not marked as propagated
2660 * 2. if presolve or propauxvars is disabled: collect expressions for which the constraint sides provide tighter bounds
2661 * if solve and propauxvars is enabled: collect expressions for which auxvars (including those in root exprs)
2662 * provide tighter bounds
2663 * 3. apply reverse propagation to all collected expressions; don't explore
2664 * sub-expressions which have not changed since the beginning of the propagation loop
2665 * 4. if we have found enough tightenings go to 1, otherwise leave propagation loop
2666 *
2667 * @note After calling forward propagation for a constraint, we mark this constraint as propagated. This flag might be
2668 * reset during the reverse propagation when we find a bound tightening of a variable expression contained in the
2669 * constraint. Resetting this flag is done in the EVENTEXEC callback of the event handler
2670 *
2671 * TODO should we distinguish between expressions where activity information is used for separation and those where not,
2672 * e.g., try less to propagate on convex constraints?
2673 */
2674 static
2675 SCIP_RETCODE propConss(
2676 SCIP* scip, /**< SCIP data structure */
2677 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
2678 SCIP_CONS** conss, /**< constraints to propagate */
2679 int nconss, /**< total number of constraints */
2680 SCIP_Bool force, /**< force tightening even if below bound strengthening tolerance */
2681 SCIP_RESULT* result, /**< pointer to store the result */
2682 int* nchgbds /**< buffer to add the number of changed bounds */
2683 )
2684 {
2685 SCIP_CONSHDLRDATA* conshdlrdata;
2686 SCIP_CONSDATA* consdata;
2687 SCIP_EXPR_OWNERDATA* ownerdata;
2688 SCIP_Bool cutoff = FALSE;
2689 SCIP_INTERVAL conssides;
2690 int ntightenings;
2691 int roundnr;
2692 SCIP_EXPRITER* revpropcollectit = NULL;
2693 int i;
2694
2695 assert(scip != NULL);
2696 assert(conshdlr != NULL);
2697 assert(conss != NULL);
2698 assert(nconss >= 0);
2699 assert(result != NULL);
2700 assert(nchgbds != NULL);
2701 assert(*nchgbds >= 0);
2702
2703 /* no constraints to propagate */
2704 if( nconss == 0 )
2705 {
2706 *result = SCIP_DIDNOTRUN;
2707 return SCIP_OKAY;
2708 }
2709
2710 conshdlrdata = SCIPconshdlrGetData(conshdlr);
2711 assert(conshdlrdata != NULL);
2712 #ifndef CR_API /* this assert may not work in unittests due to having this code compiled twice, #3543 */
2713 assert(conshdlrdata->intevalvar == intEvalVarBoundTightening);
2714 #endif
2715 assert(!conshdlrdata->globalbounds);
2716
2717 *result = SCIP_DIDNOTFIND;
2718 roundnr = 0;
2719
2720 /* tightenAuxVarBounds() needs to know whether boundtightenings are to be forced */
2721 conshdlrdata->forceboundtightening = force;
2722
2723 /* invalidate all propbounds (probably not needed) */
2724 ++conshdlrdata->curpropboundstag;
2725
2726 /* create iterator that we will use if we need to look at all auxvars */
2727 if( conshdlrdata->propauxvars )
2728 {
2729 SCIP_CALL( SCIPcreateExpriter(scip, &revpropcollectit) );
2730 }
2731
2732 /* main propagation loop */
2733 do
2734 {
2735 SCIPdebugMsg(scip, "start propagation round %d\n", roundnr);
2736
2737 assert(SCIPqueueIsEmpty(conshdlrdata->reversepropqueue));
2738
2739 /* apply forward propagation (update expression activities)
2740 * and add promising root expressions into queue for reversepropagation
2741 */
2742 for( i = 0; i < nconss; ++i )
2743 {
2744 consdata = SCIPconsGetData(conss[i]);
2745 assert(consdata != NULL);
2746
2747 /* skip deleted, non-active, or propagation-disabled constraints */
2748 if( SCIPconsIsDeleted(conss[i]) || !SCIPconsIsActive(conss[i]) || !SCIPconsIsPropagationEnabled(conss[i]) )
2749 continue;
2750
2751 /* skip already propagated constraints, i.e., constraints where no (original) variable has changed and thus
2752 * activity didn't change
2753 */
2754 if( consdata->ispropagated )
2755 continue;
2756
2757 /* update activities in expression */
2758 SCIPdebugMsg(scip, "call forwardPropExpr() for constraint <%s> (round %d): ", SCIPconsGetName(conss[i]), roundnr);
2759 SCIPdebugPrintCons(scip, conss[i], NULL);
2760
2761 ntightenings = 0;
2762 SCIP_CALL( forwardPropExpr(scip, conshdlr, consdata->expr, TRUE, &cutoff, &ntightenings) );
2763 assert(cutoff || !SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(consdata->expr)));
2764
2765 if( cutoff )
2766 {
2767 SCIPdebugMsg(scip, " -> cutoff in forwardPropExpr (due to domain error or auxvar tightening) of constraint <%s>\n", SCIPconsGetName(conss[i]));
2768 *result = SCIP_CUTOFF;
2769 break;
2770 }
2771
2772 ownerdata = SCIPexprGetOwnerData(consdata->expr);
2773
2774 /* 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 */
2775 if( !conshdlrdata->propauxvars || ownerdata->auxvar == NULL )
2776 {
2777 /* check whether constraint sides (relaxed by epsilon) or auxvar bounds provide a tightening
2778 * (if we have auxvar (not in presolve), then bounds of the auxvar are initially set to constraint sides,
2779 * so taking auxvar bounds is enough)
2780 */
2781 if( ownerdata->auxvar == NULL )
2782 {
2783 /* relax sides by SCIPepsilon() and handle infinite sides */
2784 SCIP_Real lhs = SCIPisInfinity(scip, -consdata->lhs) ? -SCIP_INTERVAL_INFINITY : consdata->lhs - conshdlrdata->conssiderelaxamount;
2785 SCIP_Real rhs = SCIPisInfinity(scip, consdata->rhs) ? SCIP_INTERVAL_INFINITY : consdata->rhs + conshdlrdata->conssiderelaxamount;
2786 SCIPintervalSetBounds(&conssides, lhs, rhs);
2787 }
2788 else
2789 {
2790 conssides = intEvalVarBoundTightening(scip, ownerdata->auxvar, (void*)conshdlrdata);
2791 }
2792 SCIP_CALL( SCIPtightenExprIntervalNonlinear(scip, consdata->expr, conssides, &cutoff, &ntightenings) );
2793 }
2794 else
2795 {
2796 /* check whether bounds of any auxvar used in constraint provides a tightening
2797 * (for the root expression, bounds of auxvar are initially set to constraint sides)
2798 * but skip exprs that have an auxvar, but do not participate in propagation
2799 */
2800 SCIP_EXPR* expr;
2801
2802 assert(revpropcollectit != NULL);
2803 SCIP_CALL( SCIPexpriterInit(revpropcollectit, consdata->expr, SCIP_EXPRITER_BFS, FALSE) );
2804 for( expr = SCIPexpriterGetCurrent(revpropcollectit); !SCIPexpriterIsEnd(revpropcollectit) && !cutoff; expr = SCIPexpriterGetNext(revpropcollectit) )
2805 {
2806 ownerdata = SCIPexprGetOwnerData(expr);
2807 assert(ownerdata != NULL);
2808
2809 if( ownerdata->auxvar == NULL )
2810 continue;
2811
2812 if( ownerdata->nactivityusesprop == 0 && ownerdata->nactivityusessepa == 0 )
2813 continue;
2814
2815 conssides = intEvalVarBoundTightening(scip, ownerdata->auxvar, (void*)conshdlrdata);
2816 SCIP_CALL( SCIPtightenExprIntervalNonlinear(scip, expr, conssides, &cutoff, &ntightenings) );
2817 }
2818 }
2819
2820 if( cutoff )
2821 {
2822 SCIPdebugMsg(scip, " -> cutoff after intersect with conssides of constraint <%s>\n", SCIPconsGetName(conss[i]));
2823 *result = SCIP_CUTOFF;
2824 break;
2825 }
2826
2827 assert(ntightenings >= 0);
2828 if( ntightenings > 0 )
2829 {
2830 *nchgbds += ntightenings;
2831 *result = SCIP_REDUCEDDOM;
2832 }
2833
2834 /* mark constraint as propagated; this will be reset via the event system when we find a variable tightening */
2835 consdata->ispropagated = TRUE;
2836 }
2837
2838 /* apply backward propagation (if cutoff is TRUE, then this call empties the queue) */
2839 SCIP_CALL( reversePropQueue(scip, conshdlr, &cutoff, &ntightenings) );
2840 assert(ntightenings >= 0);
2841 assert(SCIPqueueIsEmpty(conshdlrdata->reversepropqueue));
2842
2843 if( cutoff )
2844 {
2845 SCIPdebugMsg(scip, " -> cutoff\n");
2846 *result = SCIP_CUTOFF;
2847 break;
2848 }
2849
2850 if( ntightenings > 0 )
2851 {
2852 *nchgbds += ntightenings;
2853 *result = SCIP_REDUCEDDOM;
2854 }
2855 }
2856 while( ntightenings > 0 && ++roundnr < conshdlrdata->maxproprounds );
2857
2858 if( conshdlrdata->propauxvars )
2859 {
2860 SCIPfreeExpriter(&revpropcollectit);
2861 }
2862
2863 conshdlrdata->forceboundtightening = FALSE;
2864
2865 /* invalidate propbounds in all exprs, so noone accidentally uses them outside propagation */
2866 ++conshdlrdata->curpropboundstag;
2867
2868 return SCIP_OKAY;
2869 }
2870
2871 /** calls the reverseprop callbacks of all nlhdlrs in all expressions in all constraints using activity as bounds
2872 *
2873 * This is meant to propagate any domain restrictions on functions onto variable bounds, if possible.
2874 *
2875 * Assumes that activities are still valid and curpropboundstag does not need to be increased.
2876 * Therefore, a good place to call this function is immediately after propConss() or after forwardPropExpr() if outside propagation.
2877 */
2878 static
2879 SCIP_RETCODE propExprDomains(
2880 SCIP* scip, /**< SCIP data structure */
2881 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
2882 SCIP_CONS** conss, /**< constraints to propagate */
2883 int nconss, /**< total number of constraints */
2884 SCIP_RESULT* result, /**< pointer to store the result */
2885 int* nchgbds /**< buffer to add the number of changed bounds */
2886 )
2887 {
2888 SCIP_CONSDATA* consdata;
2889 SCIP_EXPRITER* it;
2890 SCIP_EXPR* expr;
2891 SCIP_EXPR_OWNERDATA* ownerdata;
2892 SCIP_Bool cutoff = FALSE;
2893 int ntightenings;
2894 int c;
2895 int e;
2896
2897 assert(scip != NULL);
2898 assert(conshdlr != NULL);
2899 assert(conss != NULL);
2900 assert(nconss >= 0);
2901 assert(result != NULL);
2902 assert(nchgbds != NULL);
2903 assert(*nchgbds >= 0);
2904
2905 #ifndef CR_API /* this assert may not work in unittests due to having this code compiled twice, #3543 */
2906 assert(SCIPconshdlrGetData(conshdlr)->intevalvar == intEvalVarBoundTightening);
2907 #endif
2908 assert(!SCIPconshdlrGetData(conshdlr)->globalbounds);
2909 assert(SCIPqueueIsEmpty(SCIPconshdlrGetData(conshdlr)->reversepropqueue));
2910
2911 *result = SCIP_DIDNOTFIND;
2912
2913 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
2914 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) );
2915
2916 for( c = 0; c < nconss && !cutoff; ++c )
2917 {
2918 /* skip deleted, non-active, or propagation-disabled constraints */
2919 if( SCIPconsIsDeleted(conss[c]) || !SCIPconsIsActive(conss[c]) || !SCIPconsIsPropagationEnabled(conss[c]) )
2920 continue;
2921
2922 consdata = SCIPconsGetData(conss[c]);
2923 assert(consdata != NULL);
2924
2925 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it) && !cutoff; expr = SCIPexpriterGetNext(it) )
2926 {
2927 ownerdata = SCIPexprGetOwnerData(expr);
2928 assert(ownerdata != NULL);
2929
2930 /* call reverseprop for those nlhdlr that participate in this expr's activity computation
2931 * this will propagate the current activity
2932 */
2933 for( e = 0; e < ownerdata->nenfos; ++e )
2934 {
2935 SCIP_NLHDLR* nlhdlr;
2936 assert(ownerdata->enfos[e] != NULL);
2937
2938 nlhdlr = ownerdata->enfos[e]->nlhdlr;
2939 assert(nlhdlr != NULL);
2940 if( (ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_ACTIVITY) == 0 )
2941 continue;
2942
2943 SCIPdebugMsg(scip, "propExprDomains calling reverseprop for expression %p [%g,%g]\n", (void*)expr,
2944 SCIPexprGetActivity(expr).inf, SCIPexprGetActivity(expr).sup);
2945 ntightenings = 0;
2946 SCIP_CALL( SCIPnlhdlrReverseprop(scip, conshdlr, nlhdlr, expr, ownerdata->enfos[e]->nlhdlrexprdata,
2947 SCIPexprGetActivity(expr), &cutoff, &ntightenings) );
2948
2949 if( cutoff )
2950 {
2951 /* stop everything if we detected infeasibility */
2952 SCIPdebugMsg(scip, "detect infeasibility for constraint <%s> during reverseprop()\n", SCIPconsGetName(conss[c]));
2953 *result = SCIP_CUTOFF;
2954 break;
2955 }
2956
2957 assert(ntightenings >= 0);
2958 if( ntightenings > 0 )
2959 {
2960 *nchgbds += ntightenings;
2961 *result = SCIP_REDUCEDDOM;
2962 }
2963 }
2964 }
2965 }
2966
2967 /* apply backward propagation (if cutoff is TRUE, then this call empties the queue) */
2968 SCIP_CALL( reversePropQueue(scip, conshdlr, &cutoff, &ntightenings) );
2969 assert(ntightenings >= 0);
2970
2971 if( cutoff )
2972 {
2973 SCIPdebugMsg(scip, " -> cutoff\n");
2974 *result = SCIP_CUTOFF;
2975 }
2976 else if( ntightenings > 0 )
2977 {
2978 *nchgbds += ntightenings;
2979 *result = SCIP_REDUCEDDOM;
2980 }
2981
2982 SCIPfreeExpriter(&it);
2983
2984 /* invalidate propbounds in all exprs, so noone accidentally uses them outside propagation */
2985 ++SCIPconshdlrGetData(conshdlr)->curpropboundstag;
2986
2987 return SCIP_OKAY;
2988 }
2989
2990 /** propagates variable locks through expression and adds locks to variables */
2991 static
2992 SCIP_RETCODE propagateLocks(
2993 SCIP* scip, /**< SCIP data structure */
2994 SCIP_EXPR* expr, /**< expression */
2995 int nlockspos, /**< number of positive locks */
2996 int nlocksneg /**< number of negative locks */
2997 )
2998 {
2999 SCIP_EXPR_OWNERDATA* ownerdata;
3000 SCIP_EXPRITER* it;
3001 SCIP_EXPRITER_USERDATA ituserdata;
3002
3003 assert(expr != NULL);
3004
3005 /* if no locks, then nothing to propagate */
3006 if( nlockspos == 0 && nlocksneg == 0 )
3007 return SCIP_OKAY;
3008
3009 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
3010 SCIP_CALL( SCIPexpriterInit(it, expr, SCIP_EXPRITER_DFS, TRUE) );
3011 SCIPexpriterSetStagesDFS(it, SCIP_EXPRITER_ENTEREXPR | SCIP_EXPRITER_VISITINGCHILD | SCIP_EXPRITER_LEAVEEXPR);
3012 assert(SCIPexpriterGetCurrent(it) == expr); /* iterator should not have moved */
3013
3014 /* store locks in root node */
3015 ituserdata.intvals[0] = nlockspos;
3016 ituserdata.intvals[1] = nlocksneg;
3017 SCIPexpriterSetCurrentUserData(it, ituserdata);
3018
3019 while( !SCIPexpriterIsEnd(it) )
3020 {
3021 /* collect locks */
3022 ituserdata = SCIPexpriterGetCurrentUserData(it);
3023 nlockspos = ituserdata.intvals[0];
3024 nlocksneg = ituserdata.intvals[1];
3025
3026 ownerdata = SCIPexprGetOwnerData(expr);
3027
3028 switch( SCIPexpriterGetStageDFS(it) )
3029 {
3030 case SCIP_EXPRITER_ENTEREXPR:
3031 {
3032 if( SCIPisExprVar(scip, expr) )
3033 {
3034 /* if a variable, then also add nlocksneg/nlockspos via SCIPaddVarLocks() */
3035 SCIP_CALL( SCIPaddVarLocks(scip, SCIPgetVarExprVar(expr), nlocksneg, nlockspos) );
3036 }
3037
3038 /* add locks to expression */
3039 ownerdata->nlockspos += nlockspos;
3040 ownerdata->nlocksneg += nlocksneg;
3041
3042 /* add monotonicity information if expression has been locked for the first time */
3043 if( ownerdata->nlockspos == nlockspos && ownerdata->nlocksneg == nlocksneg && SCIPexprGetNChildren(expr) > 0
3044 && SCIPexprhdlrHasMonotonicity(SCIPexprGetHdlr(expr)) )
3045 {
3046 int i;
3047
3048 assert(ownerdata->monotonicity == NULL);
3049 assert(ownerdata->monotonicitysize == 0);
3050
3051 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &ownerdata->monotonicity, SCIPexprGetNChildren(expr)) );
3052 ownerdata->monotonicitysize = SCIPexprGetNChildren(expr);
3053
3054 /* store the monotonicity for each child */
3055 for( i = 0; i < SCIPexprGetNChildren(expr); ++i )
3056 {
3057 SCIP_CALL( SCIPcallExprMonotonicity(scip, expr, i, &ownerdata->monotonicity[i]) );
3058 }
3059 }
3060 break;
3061 }
3062
3063 case SCIP_EXPRITER_LEAVEEXPR :
3064 {
3065 /* remove monotonicity information if expression has been unlocked */
3066 if( ownerdata->nlockspos == 0 && ownerdata->nlocksneg == 0 && ownerdata->monotonicity != NULL )
3067 {
3068 assert(ownerdata->monotonicitysize > 0);
3069 /* keep this assert for checking whether someone changed an expression without updating locks properly */
3070 assert(ownerdata->monotonicitysize == SCIPexprGetNChildren(expr));
3071
3072 SCIPfreeBlockMemoryArray(scip, &ownerdata->monotonicity, ownerdata->monotonicitysize);
3073 ownerdata->monotonicitysize = 0;
3074 }
3075 break;
3076 }
3077
3078 case SCIP_EXPRITER_VISITINGCHILD :
3079 {
3080 SCIP_MONOTONE monotonicity;
3081
3082 /* get monotonicity of child */
3083 /* NOTE: the monotonicity stored in an expression might be different from the result obtained by
3084 * SCIPcallExprMonotonicity
3085 */
3086 monotonicity = ownerdata->monotonicity != NULL ? ownerdata->monotonicity[SCIPexpriterGetChildIdxDFS(it)] : SCIP_MONOTONE_UNKNOWN;
3087
3088 /* compute resulting locks of the child expression */
3089 switch( monotonicity )
3090 {
3091 case SCIP_MONOTONE_INC:
3092 ituserdata.intvals[0] = nlockspos;
3093 ituserdata.intvals[1] = nlocksneg;
3094 break;
3095 case SCIP_MONOTONE_DEC:
3096 ituserdata.intvals[0] = nlocksneg;
3097 ituserdata.intvals[1] = nlockspos;
3098 break;
3099 case SCIP_MONOTONE_UNKNOWN:
3100 ituserdata.intvals[0] = nlockspos + nlocksneg;
3101 ituserdata.intvals[1] = nlockspos + nlocksneg;
3102 break;
3103 case SCIP_MONOTONE_CONST:
3104 ituserdata.intvals[0] = 0;
3105 ituserdata.intvals[1] = 0;
3106 break;
3107 }
3108 /* set locks in child expression */
3109 SCIPexpriterSetChildUserData(it, ituserdata);
3110
3111 break;
3112 }
3113
3114 default :
3115 /* you should never be here */
3116 SCIPABORT();
3117 break;
3118 }
3119
3120 expr = SCIPexpriterGetNext(it);
3121 }
3122
3123 SCIPfreeExpriter(&it);
3124
3125 return SCIP_OKAY;
3126 }
3127
3128 /** main function for adding locks to expressions and variables
3129 *
3130 * Locks for a nonlinear constraint are used to update locks for all sub-expressions and variables.
3131 * Locks of expressions depend on the monotonicity of expressions w.r.t. their children, e.g.,
3132 * consider the constraint \f$x^2 \leq 1\f$ with \f$x \in [-2,-1]\f$ implies an up-lock for the root
3133 * expression (pow) and a down-lock for its child \f$x\f$ because \f$x^2\f$ is decreasing on [-2,-1].
3134 * Since the monotonicity (and thus the locks) might also depend on variable bounds, the function remembers
3135 * the computed monotonicity information of each expression until all locks of an expression have been removed,
3136 * which implies that updating the monotonicity information during the next locking of this expression does not
3137 * break existing locks.
3138 *
3139 * @note When modifying the structure of an expression, e.g., during simplification, it is necessary to remove all
3140 * locks from an expression and repropagating them after the structural changes have been applied.
3141 * Because of existing common sub-expressions, it might be necessary to remove the locks of all constraints
3142 * to ensure that an expression is unlocked (see canonicalizeConstraints() for an example)
3143 */
3144 static
3145 SCIP_RETCODE addLocks(
3146 SCIP* scip, /**< SCIP data structure */
3147 SCIP_CONS* cons, /**< nonlinear constraint */
3148 int nlockspos, /**< number of positive rounding locks */
3149 int nlocksneg /**< number of negative rounding locks */
3150 )
3151 {
3152 SCIP_CONSDATA* consdata;
3153
3154 assert(cons != NULL);
3155
3156 if( nlockspos == 0 && nlocksneg == 0 )
3157 return SCIP_OKAY;
3158
3159 consdata = SCIPconsGetData(cons);
3160 assert(consdata != NULL);
3161
3162 /* no constraint sides -> nothing to lock */
3163 if( SCIPisInfinity(scip, consdata->rhs) && SCIPisInfinity(scip, -consdata->lhs) )
3164 return SCIP_OKAY;
3165
3166 /* remember locks */
3167 consdata->nlockspos += nlockspos;
3168 consdata->nlocksneg += nlocksneg;
3169
3170 assert(consdata->nlockspos >= 0);
3171 assert(consdata->nlocksneg >= 0);
3172
3173 /* compute locks for lock propagation */
3174 if( !SCIPisInfinity(scip, consdata->rhs) && !SCIPisInfinity(scip, -consdata->lhs) )
3175 {
3176 SCIP_CALL( propagateLocks(scip, consdata->expr, nlockspos + nlocksneg, nlockspos + nlocksneg));
3177 }
3178 else if( !SCIPisInfinity(scip, consdata->rhs) )
3179 {
3180 SCIP_CALL( propagateLocks(scip, consdata->expr, nlockspos, nlocksneg));
3181 }
3182 else
3183 {
3184 assert(!SCIPisInfinity(scip, -consdata->lhs));
3185 SCIP_CALL( propagateLocks(scip, consdata->expr, nlocksneg, nlockspos));
3186 }
3187
3188 return SCIP_OKAY;
3189 }
3190
3191 /** create a nonlinear row representation of a nonlinear constraint and stores them in consdata */
3192 static
3193 SCIP_RETCODE createNlRow(
3194 SCIP* scip, /**< SCIP data structure */
3195 SCIP_CONS* cons /**< nonlinear constraint */
3196 )
3197 {
3198 SCIP_CONSDATA* consdata;
3199
3200 assert(scip != NULL);
3201 assert(cons != NULL);
3202
3203 consdata = SCIPconsGetData(cons);
3204 assert(consdata != NULL);
3205 assert(consdata->expr != NULL);
3206
3207 if( consdata->nlrow != NULL )
3208 {
3209 SCIP_CALL( SCIPreleaseNlRow(scip, &consdata->nlrow) );
3210 }
3211
3212 /* better curvature info will be set in initSolve() just before nlrow is added to NLP */
3213 SCIP_CALL( SCIPcreateNlRow(scip, &consdata->nlrow, SCIPconsGetName(cons), 0.0,
3214 0, NULL, NULL, NULL, consdata->lhs, consdata->rhs, SCIP_EXPRCURV_UNKNOWN) );
3215
3216 if( SCIPisExprSum(scip, consdata->expr) )
3217 {
3218 /* if root is a sum, then split into linear and nonlinear terms */
3219 SCIP_EXPR* nonlinpart;
3220 SCIP_EXPR* child;
3221 SCIP_Real* coefs;
3222 int i;
3223
3224 coefs = SCIPgetCoefsExprSum(consdata->expr);
3225
3226 /* constant term of sum */
3227 SCIP_CALL( SCIPchgNlRowConstant(scip, consdata->nlrow, SCIPgetConstantExprSum(consdata->expr)) );
3228
3229 /* a sum-expression that will hold the nonlinear terms and be passed to the nlrow eventually */
3230 SCIP_CALL( SCIPcreateExprSum(scip, &nonlinpart, 0, NULL, NULL, 0.0, exprownerCreate, (void*)SCIPconsGetHdlr(cons)) );
3231
3232 for( i = 0; i < SCIPexprGetNChildren(consdata->expr); ++i )
3233 {
3234 child = SCIPexprGetChildren(consdata->expr)[i];
3235 if( SCIPisExprVar(scip, child) )
3236 {
3237 /* linear term */
3238 SCIP_CALL( SCIPaddLinearCoefToNlRow(scip, consdata->nlrow, SCIPgetVarExprVar(child), coefs[i]) );
3239 }
3240 else
3241 {
3242 /* nonlinear term */
3243 SCIP_CALL( SCIPappendExprSumExpr(scip, nonlinpart, child, coefs[i]) );
3244 }
3245 }
3246
3247 if( SCIPexprGetNChildren(nonlinpart) > 0 )
3248 {
3249 /* add expression to nlrow (this will make a copy) */
3250 SCIP_CALL( SCIPsetNlRowExpr(scip, consdata->nlrow, nonlinpart) );
3251 }
3252 SCIP_CALL( SCIPreleaseExpr(scip, &nonlinpart) );
3253 }
3254 else
3255 {
3256 SCIP_CALL( SCIPsetNlRowExpr(scip, consdata->nlrow, consdata->expr) );
3257 }
3258
3259 return SCIP_OKAY;
3260 }
3261
3262 /** compares enfodata by enforcement priority of nonlinear handler
3263 *
3264 * If handlers have same enforcement priority, then compare by detection priority, then by name.
3265 */
3266 static
3267 SCIP_DECL_SORTPTRCOMP(enfodataCmp)
3268 {
3269 SCIP_NLHDLR* h1;
3270 SCIP_NLHDLR* h2;
3271
3272 assert(elem1 != NULL);
3273 assert(elem2 != NULL);
3274
3275 h1 = ((EXPRENFO*)elem1)->nlhdlr;
3276 h2 = ((EXPRENFO*)elem2)->nlhdlr;
3277
3278 assert(h1 != NULL);
3279 assert(h2 != NULL);
3280
3281 if( SCIPnlhdlrGetEnfoPriority(h1) != SCIPnlhdlrGetEnfoPriority(h2) )
3282 return SCIPnlhdlrGetEnfoPriority(h1) - SCIPnlhdlrGetEnfoPriority(h2);
3283
3284 if( SCIPnlhdlrGetDetectPriority(h1) != SCIPnlhdlrGetDetectPriority(h2) )
3285 return SCIPnlhdlrGetDetectPriority(h1) - SCIPnlhdlrGetDetectPriority(h2);
3286
3287 return strcmp(SCIPnlhdlrGetName(h1), SCIPnlhdlrGetName(h2));
3288 }
3289
3290 /** install nlhdlrs in one expression */
3291 static
3292 SCIP_RETCODE detectNlhdlr(
3293 SCIP* scip, /**< SCIP data structure */
3294 SCIP_EXPR* expr, /**< expression for which to run detection routines */
3295 SCIP_CONS* cons /**< constraint for which expr == consdata->expr, otherwise NULL */
3296 )
3297 {
3298 SCIP_EXPR_OWNERDATA* ownerdata;
3299 SCIP_CONSHDLRDATA* conshdlrdata;
3300 SCIP_NLHDLR_METHOD enforcemethodsallowed;
3301 SCIP_NLHDLR_METHOD enforcemethods;
3302 SCIP_NLHDLR_METHOD enforcemethodsnew;
3303 SCIP_NLHDLR_METHOD nlhdlrenforcemethods;
3304 SCIP_NLHDLR_METHOD nlhdlrparticipating;
3305 SCIP_NLHDLREXPRDATA* nlhdlrexprdata;
3306 int enfossize; /* allocated length of expr->enfos array */
3307 int h;
3308
3309 assert(expr != NULL);
3310
3311 ownerdata = SCIPexprGetOwnerData(expr);
3312 assert(ownerdata != NULL);
3313
3314 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr);
3315 assert(conshdlrdata != NULL);
3316 assert(conshdlrdata->auxvarid >= 0);
3317 assert(!conshdlrdata->indetect);
3318
3319 /* there should be no enforcer yet and detection should not even have considered expr yet */
3320 assert(ownerdata->nenfos < 0);
3321 assert(ownerdata->enfos == NULL);
3322
3323 /* check which enforcement methods are required by setting flags in enforcemethods for those that are NOT required
3324 * - if no auxiliary variable is used, then do not need sepabelow or sepaabove
3325 * - if auxiliary variable is used, but nobody positively (up) locks expr -> only need to enforce expr >= auxvar -> no need for underestimation
3326 * - if auxiliary variable is used, but nobody negatively (down) locks expr -> only need to enforce expr <= auxvar -> no need for overestimation
3327 * - if no one uses activity, then do not need activity methods
3328 */
3329 enforcemethods = SCIP_NLHDLR_METHOD_NONE;
3330 if( ownerdata->nauxvaruses == 0 )
3331 enforcemethods |= SCIP_NLHDLR_METHOD_SEPABOTH;
3332 else
3333 {
3334 if( ownerdata->nlockspos == 0 ) /* no need for underestimation */
3335 enforcemethods |= SCIP_NLHDLR_METHOD_SEPABELOW;
3336 if( ownerdata->nlocksneg == 0 ) /* no need for overestimation */
3337 enforcemethods |= SCIP_NLHDLR_METHOD_SEPAABOVE;
3338 }
3339 if( ownerdata->nactivityusesprop == 0 && ownerdata->nactivityusessepa == 0 )
3340 enforcemethods |= SCIP_NLHDLR_METHOD_ACTIVITY;
3341
3342 /* it doesn't make sense to have been called on detectNlhdlr, if the expr isn't used for anything */
3343 assert(enforcemethods != SCIP_NLHDLR_METHOD_ALL);
3344
3345 /* all methods that have not been flagged above are the ones that we want to be handled by nlhdlrs */
3346 enforcemethodsallowed = ~enforcemethods & SCIP_NLHDLR_METHOD_ALL;
3347
3348 ownerdata->nenfos = 0;
3349 enfossize = 2;
3350 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &ownerdata->enfos, enfossize) );
3351 conshdlrdata->indetect = TRUE;
3352
3353 SCIPdebugMsg(scip, "detecting nlhdlrs for %s expression %p (%s); requiring%s%s%s\n",
3354 cons != NULL ? "root" : "non-root", (void*)expr, SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)),
3355 (enforcemethods & SCIP_NLHDLR_METHOD_SEPABELOW) != 0 ? "" : " sepabelow",
3356 (enforcemethods & SCIP_NLHDLR_METHOD_SEPAABOVE) != 0 ? "" : " sepaabove",
3357 (enforcemethods & SCIP_NLHDLR_METHOD_ACTIVITY) != 0 ? "" : " activity");
3358
3359 for( h = 0; h < conshdlrdata->nnlhdlrs; ++h )
3360 {
3361 SCIP_NLHDLR* nlhdlr;
3362
3363 nlhdlr = conshdlrdata->nlhdlrs[h];
3364 assert(nlhdlr != NULL);
3365
3366 /* skip disabled nlhdlrs */
3367 if( !SCIPnlhdlrIsEnabled(nlhdlr) )
3368 continue;
3369
3370 /* call detect routine of nlhdlr */
3371 nlhdlrexprdata = NULL;
3372 enforcemethodsnew = enforcemethods;
3373 nlhdlrparticipating = SCIP_NLHDLR_METHOD_NONE;
3374 conshdlrdata->registerusesactivitysepabelow = FALSE; /* SCIPregisterExprUsageNonlinear() as called by detect may set this to TRUE */
3375 conshdlrdata->registerusesactivitysepaabove = FALSE; /* SCIPregisterExprUsageNonlinear() as called by detect may set this to TRUE */
3376 /* coverity[forward_null] */
3377 SCIP_CALL( SCIPnlhdlrDetect(scip, ownerdata->conshdlr, nlhdlr, expr, cons, &enforcemethodsnew, &nlhdlrparticipating, &nlhdlrexprdata) );
3378
3379 /* nlhdlr might have claimed more than needed: clean up sepa flags */
3380 nlhdlrparticipating &= enforcemethodsallowed;
3381
3382 /* detection is only allowed to augment to nlhdlrenforcemethods, so previous enforcemethods must still be set */
3383 assert((enforcemethodsnew & enforcemethods) == enforcemethods);
3384
3385 /* Because of the previous assert, nlhdlrenforcenew ^ enforcemethods are the methods enforced by this nlhdlr.
3386 * They are also cleaned up here to ensure that only the needed methods are claimed.
3387 */
3388 nlhdlrenforcemethods = (enforcemethodsnew ^ enforcemethods) & enforcemethodsallowed;
3389
3390 /* nlhdlr needs to participate for the methods it is enforcing */
3391 assert((nlhdlrparticipating & nlhdlrenforcemethods) == nlhdlrenforcemethods);
3392
3393 if( nlhdlrparticipating == SCIP_NLHDLR_METHOD_NONE )
3394 {
3395 /* nlhdlr might not have detected anything, or all set flags might have been removed by
3396 * clean up; in the latter case, we may need to free nlhdlrexprdata */
3397
3398 /* free nlhdlr exprdata, if there is any and there is a method to free this data */
3399 if( nlhdlrexprdata != NULL )
3400 {
3401 SCIP_CALL( SCIPnlhdlrFreeexprdata(scip, nlhdlr, expr, &nlhdlrexprdata) );
3402 }
3403 /* nlhdlr cannot have added an enforcement method if it doesn't participate (actually redundant due to previous asserts) */
3404 assert(nlhdlrenforcemethods == SCIP_NLHDLR_METHOD_NONE);
3405
3406 SCIPdebugMsg(scip, "nlhdlr <%s> detect unsuccessful\n", SCIPnlhdlrGetName(nlhdlr));
3407
3408 continue;
3409 }
3410
3411 SCIPdebugMsg(scip, "nlhdlr <%s> detect successful; sepabelow: %s, sepaabove: %s, activity: %s\n",
3412 SCIPnlhdlrGetName(nlhdlr),
3413 ((nlhdlrenforcemethods & SCIP_NLHDLR_METHOD_SEPABELOW) != 0) ? "enforcing" : ((nlhdlrparticipating & SCIP_NLHDLR_METHOD_SEPABELOW) != 0) ? "participating" : "no",
3414 ((nlhdlrenforcemethods & SCIP_NLHDLR_METHOD_SEPAABOVE) != 0) ? "enforcing" : ((nlhdlrparticipating & SCIP_NLHDLR_METHOD_SEPAABOVE) != 0) ? "participating" : "no",
3415 ((nlhdlrenforcemethods & SCIP_NLHDLR_METHOD_ACTIVITY) != 0) ? "enforcing" : ((nlhdlrparticipating & SCIP_NLHDLR_METHOD_ACTIVITY) != 0) ? "participating" : "no");
3416
3417 /* store nlhdlr and its data */
3418 SCIP_CALL( SCIPensureBlockMemoryArray(scip, &ownerdata->enfos, &enfossize, ownerdata->nenfos+1) );
3419 SCIP_CALL( SCIPallocBlockMemory(scip, &ownerdata->enfos[ownerdata->nenfos]) );
3420 ownerdata->enfos[ownerdata->nenfos]->nlhdlr = nlhdlr;
3421 ownerdata->enfos[ownerdata->nenfos]->nlhdlrexprdata = nlhdlrexprdata;
3422 ownerdata->enfos[ownerdata->nenfos]->nlhdlrparticipation = nlhdlrparticipating;
3423 ownerdata->enfos[ownerdata->nenfos]->issepainit = FALSE;
3424 ownerdata->enfos[ownerdata->nenfos]->sepabelowusesactivity = conshdlrdata->registerusesactivitysepabelow;
3425 ownerdata->enfos[ownerdata->nenfos]->sepaaboveusesactivity = conshdlrdata->registerusesactivitysepaabove;
3426 ownerdata->nenfos++;
3427
3428 /* update enforcement flags */
3429 enforcemethods = enforcemethodsnew;
3430 }
3431
3432 conshdlrdata->indetect = FALSE;
3433
3434 /* stop if an enforcement method is missing but we are already in solving stage
3435 * (as long as the expression provides its callbacks, the default nlhdlr should have provided all enforcement methods)
3436 */
3437 if( enforcemethods != SCIP_NLHDLR_METHOD_ALL && SCIPgetStage(scip) == SCIP_STAGE_SOLVING )
3438 {
3439 SCIPerrorMessage("no nonlinear handler provided some of the required enforcement methods\n");
3440 return SCIP_ERROR;
3441 }
3442
3443 assert(ownerdata->nenfos > 0);
3444
3445 /* sort nonlinear handlers by enforcement priority, in decreasing order */
3446 if( ownerdata->nenfos > 1 )
3447 SCIPsortDownPtr((void**)ownerdata->enfos, enfodataCmp, ownerdata->nenfos);
3448
3449 /* resize enfos array to be nenfos long */
3450 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &ownerdata->enfos, enfossize, ownerdata->nenfos) );
3451
3452 return SCIP_OKAY;
3453 }
3454
3455 /** detect nlhdlrs that can handle the expressions */
3456 static
3457 SCIP_RETCODE detectNlhdlrs(
3458 SCIP* scip, /**< SCIP data structure */
3459 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
3460 SCIP_CONS** conss, /**< constraints for which to run nlhdlr detect */
3461 int nconss /**< total number of constraints */
3462 )
3463 {
3464 SCIP_CONSHDLRDATA* conshdlrdata;
3465 SCIP_CONSDATA* consdata;
3466 SCIP_EXPR* expr;
3467 SCIP_EXPR_OWNERDATA* ownerdata;
3468 SCIP_EXPRITER* it;
3469 int i;
3470
3471 assert(conss != NULL || nconss == 0);
3472 assert(nconss >= 0);
3473 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 */
3474
3475 conshdlrdata = SCIPconshdlrGetData(conshdlr);
3476 assert(conshdlrdata != NULL);
3477
3478 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
3479 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, TRUE) );
3480
3481 if( SCIPgetStage(scip) == SCIP_STAGE_SOLVING && SCIPgetDepth(scip) != 0 )
3482 {
3483 /* ensure that activities are recomputed w.r.t. the global variable bounds if CONSACTIVE is called in a local node;
3484 * for example, this happens if globally valid nonlinear constraints are added during the tree search
3485 */
3486 SCIPincrementCurBoundsTagNonlinear(conshdlr, TRUE);
3487 conshdlrdata->globalbounds = TRUE;
3488 conshdlrdata->lastvaractivitymethodchange = conshdlrdata->curboundstag;
3489 }
3490
3491 for( i = 0; i < nconss; ++i )
3492 {
3493 assert(conss != NULL && conss[i] != NULL);
3494
3495 consdata = SCIPconsGetData(conss[i]);
3496 assert(consdata != NULL);
3497 assert(consdata->expr != NULL);
3498
3499 /* if a constraint is separated, we currently need it to be initial, too
3500 * this is because INITLP will create the auxiliary variables that are used for any separation
3501 * TODO we may relax this with a little more programming effort when required, see also TODO in INITLP
3502 */
3503 assert((!SCIPconsIsSeparated(conss[i]) && !SCIPconsIsEnforced(conss[i])) || SCIPconsIsInitial(conss[i]));
3504
3505 ownerdata = SCIPexprGetOwnerData(consdata->expr);
3506 assert(ownerdata != NULL);
3507
3508 /* because of common sub-expressions it might happen that we already detected a nonlinear handler and added it to the expr
3509 * then we would normally skip to run DETECT again
3510 * HOWEVER: most likely we have been running DETECT with cons == NULL, which may interest less nlhdlrs
3511 * thus, if expr is the root expression, we rerun DETECT
3512 */
3513 if( ownerdata->nenfos > 0 )
3514 {
3515 SCIP_CALL( freeEnfoData(scip, consdata->expr, FALSE) );
3516 assert(ownerdata->nenfos < 0);
3517 }
3518
3519 /* if constraint will be enforced, and we are in solve, then ensure auxiliary variable for root expression
3520 * this way we can treat the root expression like any other expression when enforcing via separation
3521 * if constraint will be propagated, then register activity usage of root expression
3522 * this can trigger a call to forwardPropExpr, for which we better have the indetect flag set
3523 */
3524 conshdlrdata->indetect = TRUE;
3525 SCIP_CALL( SCIPregisterExprUsageNonlinear(scip, consdata->expr,
3526 SCIPgetStage(scip) >= SCIP_STAGE_INITSOLVE && (SCIPconsIsSeparated(conss[i]) || SCIPconsIsEnforced(conss[i])),
3527 SCIPconsIsPropagated(conss[i]),
3528 FALSE, FALSE) );
3529 conshdlrdata->indetect = FALSE;
3530
3531 /* compute integrality information for all subexpressions */
3532 SCIP_CALL( SCIPcomputeExprIntegrality(scip, consdata->expr) );
3533
3534 /* run detectNlhdlr on all expr where required */
3535 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
3536 {
3537 ownerdata = SCIPexprGetOwnerData(expr);
3538 assert(ownerdata != NULL);
3539
3540 /* skip exprs that we already looked at */
3541 if( ownerdata->nenfos >= 0 )
3542 continue;
3543
3544 /* if there is use of the auxvar, then someone requires that
3545 * auxvar == expr (or auxvar >= expr or auxvar <= expr) or we are at the root expression (expr==consdata->expr)
3546 * thus, we need to find nlhdlrs that separate or estimate
3547 * if there is use of the activity, then there is someone requiring that
3548 * activity of this expression is updated; this someone would also benefit from better bounds on the activity of this expression
3549 * thus, we need to find nlhdlrs that do interval-evaluation
3550 */
3551 if( ownerdata->nauxvaruses > 0 || ownerdata->nactivityusesprop > 0 || ownerdata->nactivityusessepa > 0 )
3552 {
3553 SCIP_CALL( detectNlhdlr(scip, expr, expr == consdata->expr ? conss[i] : NULL) );
3554
3555 assert(ownerdata->nenfos >= 0);
3556 }
3557 else
3558 {
3559 /* remember that we looked at this expression during detectNlhdlrs
3560 * even though we have not actually run detectNlhdlr, because no nlhdlr showed interest in this expr,
3561 * in some situations (forwardPropExpr, to be specific) we will have to distinguish between exprs for which
3562 * we have not initialized enforcement yet (nenfos < 0) and expressions which are just not used in enforcement (nenfos == 0)
3563 */
3564 ownerdata->nenfos = 0;
3565 }
3566 }
3567
3568 /* include this constraint into the next propagation round because the added nlhdlr may do find tighter bounds now */
3569 if( SCIPconsIsPropagated(conss[i]) )
3570 consdata->ispropagated = FALSE;
3571 }
3572
3573 if( SCIPgetStage(scip) == SCIP_STAGE_SOLVING && SCIPgetDepth(scip) != 0 )
3574 {
3575 /* ensure that the local bounds are used again when reevaluating the expressions later;
3576 * this is only needed if CONSACTIVE is called in a local node (see begin of this function)
3577 */
3578 SCIPincrementCurBoundsTagNonlinear(conshdlr, FALSE);
3579 conshdlrdata->globalbounds = FALSE;
3580 conshdlrdata->lastvaractivitymethodchange = conshdlrdata->curboundstag;
3581 }
3582 else
3583 {
3584 /* ensure that all activities (except for var-exprs) are reevaluated since better methods may be available now */
3585 SCIPincrementCurBoundsTagNonlinear(conshdlr, FALSE);
3586 }
3587
3588 SCIPfreeExpriter(&it);
3589
3590 return SCIP_OKAY;
3591 }
3592
3593 /** initializes (pre)solving data of constraints
3594 *
3595 * This initializes data in a constraint that is used for separation, propagation, etc, and assumes that expressions will
3596 * not be modified.
3597 * In particular, this function
3598 * - runs the detection method of nlhldrs
3599 * - looks for unlocked linear variables
3600 * - checks curvature (if not in presolve)
3601 * - creates and add row to NLP (if not in presolve)
3602 *
3603 * This function can be called in presolve and solve and can be called several times with different sets of constraints,
3604 * e.g., it should be called in INITSOL and for constraints that are added during solve.
3605 */
3606 static
3607 SCIP_RETCODE initSolve(
3608 SCIP* scip, /**< SCIP data structure */
3609 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
3610 SCIP_CONS** conss, /**< constraints */
3611 int nconss /**< number of constraints */
3612 )
3613 {
3614 int c;
3615
3616 for( c = 0; c < nconss; ++c )
3617 {
3618 /* check for a linear variable that can be increase or decreased without harming feasibility */
3619 findUnlockedLinearVar(scip, conss[c]);
3620
3621 if( SCIPgetStage(scip) == SCIP_STAGE_INITSOLVE || SCIPgetStage(scip) == SCIP_STAGE_SOLVING )
3622 {
3623 SCIP_CONSDATA* consdata;
3624 SCIP_Bool success = FALSE;
3625
3626 consdata = SCIPconsGetData(conss[c]); /*lint !e613*/
3627 assert(consdata != NULL);
3628 assert(consdata->expr != NULL);
3629
3630 if( !SCIPconshdlrGetData(conshdlr)->assumeconvex )
3631 {
3632 /* call the curvature detection algorithm of the convex nonlinear handler
3633 * Check only for those curvature that may result in a convex inequality, i.e.,
3634 * whether f(x) is concave when f(x) >= lhs and/or f(x) is convex when f(x) <= rhs.
3635 * Also we can assume that we are nonlinear, so do not check for convex if already concave.
3636 */
3637 if( !SCIPisInfinity(scip, -consdata->lhs) )
3638 {
3639 SCIP_CALL( SCIPhasExprCurvature(scip, consdata->expr, SCIP_EXPRCURV_CONCAVE, &success, NULL) );
3640 if( success )
3641 consdata->curv = SCIP_EXPRCURV_CONCAVE;
3642 }
3643 if( !success && !SCIPisInfinity(scip, consdata->rhs) )
3644 {
3645 SCIP_CALL( SCIPhasExprCurvature(scip, consdata->expr, SCIP_EXPRCURV_CONVEX, &success, NULL) );
3646 if( success )
3647 consdata->curv = SCIP_EXPRCURV_CONVEX;
3648 }
3649 }
3650 else
3651 {
3652 if( !SCIPisInfinity(scip, -consdata->lhs) && !SCIPisInfinity(scip, consdata->rhs) )
3653 {
3654 SCIPwarningMessage(scip, "Nonlinear constraint <%s> has finite left- and right-hand side, but constraints/nonlinear/assumeconvex is enabled.\n", SCIPconsGetName(conss[c]));
3655 consdata->curv = SCIP_EXPRCURV_LINEAR;
3656 }
3657 else
3658 {
3659 consdata->curv = !SCIPisInfinity(scip, consdata->rhs) ? SCIP_EXPRCURV_CONVEX : SCIP_EXPRCURV_CONCAVE;
3660 }
3661 }
3662 SCIPdebugMsg(scip, "root curvature of constraint %s = %d\n", SCIPconsGetName(conss[c]), consdata->curv);
3663
3664 /* add nlrow representation to NLP, if NLP had been constructed */
3665 if( SCIPisNLPConstructed(scip) && SCIPconsIsActive(conss[c]) )
3666 {
3667 if( consdata->nlrow == NULL )
3668 {
3669 SCIP_CALL( createNlRow(scip, conss[c]) );
3670 assert(consdata->nlrow != NULL);
3671 }
3672 SCIPsetNlRowCurvature(scip, consdata->nlrow, consdata->curv);
3673 SCIP_CALL( SCIPaddNlRow(scip, consdata->nlrow) );
3674 }
3675 }
3676 }
3677
3678 /* register non linear handlers */
3679 SCIP_CALL( detectNlhdlrs(scip, conshdlr, conss, nconss) );
3680
3681 return SCIP_OKAY;
3682 }
3683
3684 /** deinitializes (pre)solving data of constraints
3685 *
3686 * This removes the initialization data created in initSolve().
3687 *
3688 * This function can be called in presolve and solve.
3689 *
3690 * TODO At the moment, it should not be called for a constraint if there are other constraints
3691 * that use the same expressions but still require their nlhdlr.
3692 * We should probably only decrement the auxvar and activity usage for the root expr and then
3693 * proceed as in detectNlhdlrs(), i.e., free enfo data only where none is used.
3694 */
3695 static
3696 SCIP_RETCODE deinitSolve(
3697 SCIP* scip, /**< SCIP data structure */
3698 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
3699 SCIP_CONS** conss, /**< constraints */
3700 int nconss /**< number of constraints */
3701 )
3702 {
3703 SCIP_EXPRITER* it;
3704 SCIP_EXPR* expr;
3705 SCIP_CONSDATA* consdata;
3706 SCIP_Bool rootactivityvalid;
3707 int c;
3708
3709 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
3710 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) );
3711 SCIPexpriterSetStagesDFS(it, SCIP_EXPRITER_LEAVEEXPR);
3712
3713 /* call deinitialization callbacks of expression and nonlinear handlers
3714 * free nonlinear handlers information from expressions
3715 * remove auxiliary variables and nactivityuses counts from expressions
3716 */
3717 for( c = 0; c < nconss; ++c )
3718 {
3719 assert(conss != NULL);
3720 assert(conss[c] != NULL);
3721
3722 consdata = SCIPconsGetData(conss[c]);
3723 assert(consdata != NULL);
3724 assert(consdata->expr != NULL);
3725
3726 /* check and remember whether activity in root is valid */
3727 rootactivityvalid = SCIPexprGetActivityTag(consdata->expr) >= SCIPconshdlrGetData(conshdlr)->lastboundrelax;
3728
3729 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
3730 {
3731 SCIPdebugMsg(scip, "exitsepa and free nonlinear handler data for expression %p\n", (void*)expr);
3732
3733 /* remove nonlinear handlers in expression and their data and auxiliary variables; reset activityusage count */
3734 SCIP_CALL( freeEnfoData(scip, expr, TRUE) );
3735
3736 /* remove quadratic info */
3737 SCIPfreeExprQuadratic(scip, expr);
3738
3739 if( rootactivityvalid )
3740 {
3741 /* ensure activity is valid if consdata->expr activity is valid
3742 * this is mainly to ensure that we do not leave invalid activities in parts of the expression tree where activity was not used,
3743 * e.g., an expr's activity was kept up to date by a nlhdlr, but without using some childs activity
3744 * so this childs activity would be invalid, which can generate confusion
3745 */
3746 SCIP_CALL( SCIPevalExprActivity(scip, expr) );
3747 }
3748 }
3749
3750 if( consdata->nlrow != NULL )
3751 {
3752 /* remove row from NLP, if still in solving
3753 * if we are in exitsolve, the whole NLP will be freed anyway
3754 */
3755 if( SCIPgetStage(scip) == SCIP_STAGE_SOLVING )
3756 {
3757 SCIP_CALL( SCIPdelNlRow(scip, consdata->nlrow) );
3758 }
3759
3760 SCIP_CALL( SCIPreleaseNlRow(scip, &consdata->nlrow) );
3761 }
3762
3763 /* forget about linear variables that can be increased or decreased without harming feasibility */
3764 consdata->linvardecr = NULL;
3765 consdata->linvarincr = NULL;
3766
3767 /* forget about curvature */
3768 consdata->curv = SCIP_EXPRCURV_UNKNOWN;
3769 }
3770
3771 SCIPfreeExpriter(&it);
3772
3773 return SCIP_OKAY;
3774 }
3775
3776 /** helper method to decide whether a given expression is product of at least two binary variables */
3777 static
3778 SCIP_Bool isBinaryProduct(
3779 SCIP* scip, /**< SCIP data structure */
3780 SCIP_EXPR* expr /**< expression */
3781 )
3782 {
3783 int i;
3784
3785 assert(expr != NULL);
3786
3787 /* check whether the expression is a product */
3788 if( !SCIPisExprProduct(scip, expr) )
3789 return FALSE;
3790
3791 /* don't consider products with a coefficient != 1 and products with a single child
3792 * simplification will take care of this expression later
3793 */
3794 if( SCIPexprGetNChildren(expr) <= 1 || SCIPgetCoefExprProduct(expr) != 1.0 )
3795 return FALSE;
3796
3797 for( i = 0; i < SCIPexprGetNChildren(expr); ++i )
3798 {
3799 SCIP_EXPR* child;
3800 SCIP_VAR* var;
3801 SCIP_Real ub;
3802 SCIP_Real lb;
3803
3804 child = SCIPexprGetChildren(expr)[i];
3805 assert(child != NULL);
3806
3807 if( !SCIPisExprVar(scip, child) )
3808 return FALSE;
3809
3810 var = SCIPgetVarExprVar(child);
3811 lb = SCIPvarGetLbLocal(var);
3812 ub = SCIPvarGetUbLocal(var);
3813
3814 /* check whether variable is integer and has [0,1] as variable bounds */
3815 if( !SCIPvarIsIntegral(var) || !SCIPisEQ(scip, lb, 0.0) || !SCIPisEQ(scip, ub, 1.0) )
3816 return FALSE;
3817 }
3818
3819 return TRUE;
3820 }
3821
3822 /** helper method to collect all bilinear binary product terms */
3823 static
3824 SCIP_RETCODE getBilinearBinaryTerms(
3825 SCIP* scip, /**< SCIP data structure */
3826 SCIP_EXPR* sumexpr, /**< sum expression */
3827 SCIP_VAR** xs, /**< array to collect first variable of each bilinear binary product */
3828 SCIP_VAR** ys, /**< array to collect second variable of each bilinear binary product */
3829 int* childidxs, /**< array to store the index of the child of each stored bilinear binary product */
3830 int* nterms /**< pointer to store the total number of bilinear binary terms */
3831 )
3832 {
3833 int i;
3834
3835 assert(sumexpr != NULL);
3836 assert(SCIPisExprSum(scip, sumexpr));
3837 assert(xs != NULL);
3838 assert(ys != NULL);
3839 assert(childidxs != NULL);
3840 assert(nterms != NULL);
3841
3842 *nterms = 0;
3843
3844 for( i = 0; i < SCIPexprGetNChildren(sumexpr); ++i )
3845 {
3846 SCIP_EXPR* child;
3847
3848 child = SCIPexprGetChildren(sumexpr)[i];
3849 assert(child != NULL);
3850
3851 if( SCIPexprGetNChildren(child) == 2 && isBinaryProduct(scip, child) )
3852 {
3853 SCIP_VAR* x = SCIPgetVarExprVar(SCIPexprGetChildren(child)[0]);
3854 SCIP_VAR* y = SCIPgetVarExprVar(SCIPexprGetChildren(child)[1]);
3855
3856 assert(x != NULL);
3857 assert(y != NULL);
3858
3859 if( x != y )
3860 {
3861 xs[*nterms] = x;
3862 ys[*nterms] = y;
3863 childidxs[*nterms] = i;
3864 ++(*nterms);
3865 }
3866 }
3867 }
3868
3869 return SCIP_OKAY;
3870 }
3871
3872 /** helper method to reformulate \f$x_i \sum_j c_{ij} x_j\f$ */
3873 static
3874 SCIP_RETCODE reformulateFactorizedBinaryQuadratic(
3875 SCIP* scip, /**< SCIP data structure */
3876 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
3877 SCIP_CONS* cons, /**< constraint */
3878 SCIP_VAR* facvar, /**< variable that has been factorized */
3879 SCIP_VAR** vars, /**< variables of sum_j c_ij x_j */
3880 SCIP_Real* coefs, /**< coefficients of sum_j c_ij x_j */
3881 int nvars, /**< total number of variables in sum_j c_ij x_j */
3882 SCIP_EXPR** newexpr, /**< pointer to store the new expression */
3883 int* naddconss /**< pointer to update the total number of added constraints (might be NULL) */
3884 )
3885 {
3886 SCIP_VAR* auxvar;
3887 SCIP_CONS* newcons;
3888 SCIP_Real minact = 0.0;
3889 SCIP_Real maxact = 0.0;
3890 SCIP_Bool integral = TRUE;
3891 char name [SCIP_MAXSTRLEN];
3892 int i;
3893
3894 assert(facvar != NULL);
3895 assert(vars != NULL);
3896 assert(nvars > 1);
3897 assert(newexpr != NULL);
3898
3899 /* compute minimum and maximum activity of sum_j c_ij x_j */
3900 /* 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 */
3901 for( i = 0; i < nvars; ++i )
3902 {
3903 minact += MIN(coefs[i], 0.0);
3904 maxact += MAX(coefs[i], 0.0);
3905 integral = integral && SCIPisIntegral(scip, coefs[i]);
3906 }
3907 assert(minact <= maxact);
3908
3909 /* create and add auxiliary variable */
3910 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s", SCIPconsGetName(cons), SCIPvarGetName(facvar));
3911 SCIP_CALL( SCIPcreateVarBasic(scip, &auxvar, name, minact, maxact, 0.0, integral ? SCIP_VARTYPE_IMPLINT : SCIP_VARTYPE_CONTINUOUS) );
3912 SCIP_CALL( SCIPaddVar(scip, auxvar) );
3913
3914 /* create and add z - maxact x <= 0 */
3915 if( !SCIPisZero(scip, maxact) )
3916 {
3917 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s_1", SCIPconsGetName(cons), SCIPvarGetName(facvar));
3918 SCIP_CALL( SCIPcreateConsBasicVarbound(scip, &newcons, name, auxvar, facvar, -maxact, -SCIPinfinity(scip), 0.0) );
3919 SCIP_CALL( SCIPaddCons(scip, newcons) );
3920 SCIP_CALL( SCIPreleaseCons(scip, &newcons) );
3921 if( naddconss != NULL )
3922 ++(*naddconss);
3923 }
3924
3925 /* create and add 0 <= z - minact x */
3926 if( !SCIPisZero(scip, minact) )
3927 {
3928 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s_2", SCIPconsGetName(cons), SCIPvarGetName(facvar));
3929 SCIP_CALL( SCIPcreateConsBasicVarbound(scip, &newcons, name, auxvar, facvar, -minact, 0.0, SCIPinfinity(scip)) );
3930 SCIP_CALL( SCIPaddCons(scip, newcons) );
3931 SCIP_CALL( SCIPreleaseCons(scip, &newcons) );
3932 if( naddconss != NULL )
3933 ++(*naddconss);
3934 }
3935
3936 /* create and add minact <= sum_j c_j x_j - z + minact x_i */
3937 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s_3", SCIPconsGetName(cons), SCIPvarGetName(facvar));
3938 SCIP_CALL( SCIPcreateConsBasicLinear(scip, &newcons, name, nvars, vars, coefs, minact, SCIPinfinity(scip)) );
3939 SCIP_CALL( SCIPaddCoefLinear(scip, newcons, auxvar, -1.0) );
3940 if( !SCIPisZero(scip, minact) )
3941 {
3942 SCIP_CALL( SCIPaddCoefLinear(scip, newcons, facvar, minact) );
3943 }
3944 SCIP_CALL( SCIPaddCons(scip, newcons) );
3945 SCIP_CALL( SCIPreleaseCons(scip, &newcons) );
3946 if( naddconss != NULL )
3947 ++(*naddconss);
3948
3949 /* create and add sum_j c_j x_j - z + maxact x_i <= maxact */
3950 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s_4", SCIPconsGetName(cons), SCIPvarGetName(facvar));
3951 SCIP_CALL( SCIPcreateConsBasicLinear(scip, &newcons, name, nvars, vars, coefs, -SCIPinfinity(scip), maxact) );
3952 SCIP_CALL( SCIPaddCoefLinear(scip, newcons, auxvar, -1.0) );
3953 if( !SCIPisZero(scip, maxact) )
3954 {
3955 SCIP_CALL( SCIPaddCoefLinear(scip, newcons, facvar, maxact) );
3956 }
3957 SCIP_CALL( SCIPaddCons(scip, newcons) );
3958 SCIP_CALL( SCIPreleaseCons(scip, &newcons) );
3959 if( naddconss != NULL )
3960 ++(*naddconss);
3961
3962 /* create variable expression */
3963 SCIP_CALL( createExprVar(scip, conshdlr, newexpr, auxvar) );
3964
3965 /* release auxvar */
3966 SCIP_CALL( SCIPreleaseVar(scip, &auxvar) );
3967
3968 return SCIP_OKAY;
3969 }
3970
3971 /** helper method to generate an expression for a sum of products of binary variables; note that the method captures the generated expression */
3972 static
3973 SCIP_RETCODE getFactorizedBinaryQuadraticExpr(
3974 SCIP* scip, /**< SCIP data structure */
3975 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
3976 SCIP_CONS* cons, /**< constraint */
3977 SCIP_EXPR* sumexpr, /**< expression */
3978 int minterms, /**< minimum number of terms in a the sum of x_i sum_j c_j x_j */
3979 SCIP_EXPR** newexpr, /**< pointer to store the expression that represents the binary quadratic */
3980 int* naddconss /**< pointer to update the total number of added constraints (might be NULL) */
3981 )
3982 {
3983 SCIP_EXPR** exprs = NULL;
3984 SCIP_VAR** tmpvars = NULL;
3985 SCIP_VAR** vars = NULL;
3986 SCIP_VAR** xs = NULL;
3987 SCIP_VAR** ys = NULL;
3988 SCIP_Real* exprcoefs = NULL;
3989 SCIP_Real* tmpcoefs = NULL;
3990 SCIP_Real* sumcoefs;
3991 SCIP_Bool* isused = NULL;
3992 int* childidxs = NULL;
3993 int* count = NULL;
3994 int nchildren;
3995 int nexprs = 0;
3996 int nterms;
3997 int nvars;
3998 int ntotalvars;
3999 int i;
4000
4001 assert(sumexpr != NULL);
4002 assert(minterms > 1);
4003 assert(newexpr != NULL);
4004
4005 *newexpr = NULL;
4006
4007 /* check whether sumexpr is indeed a sum */
4008 if( !SCIPisExprSum(scip, sumexpr) )
4009 return SCIP_OKAY;
4010
4011 nchildren = SCIPexprGetNChildren(sumexpr);
4012 sumcoefs = SCIPgetCoefsExprSum(sumexpr);
4013 nvars = SCIPgetNVars(scip);
4014 ntotalvars = SCIPgetNTotalVars(scip);
4015
4016 /* check whether there are enough terms available */
4017 if( nchildren < minterms )
4018 return SCIP_OKAY;
4019
4020 /* allocate memory */
4021 SCIP_CALL( SCIPallocBufferArray(scip, &xs, nchildren) );
4022 SCIP_CALL( SCIPallocBufferArray(scip, &ys, nchildren) );
4023 SCIP_CALL( SCIPallocBufferArray(scip, &childidxs, nchildren) );
4024
4025 /* collect all bilinear binary product terms */
4026 SCIP_CALL( getBilinearBinaryTerms(scip, sumexpr, xs, ys, childidxs, &nterms) );
4027
4028 /* check whether there are enough terms available */
4029 if( nterms < minterms )
4030 goto TERMINATE;
4031
4032 /* store how often each variable appears in a bilinear binary product */
4033 SCIP_CALL( SCIPduplicateBufferArray(scip, &vars, SCIPgetVars(scip), nvars) );
4034 SCIP_CALL( SCIPallocClearBufferArray(scip, &count, ntotalvars) );
4035 SCIP_CALL( SCIPallocClearBufferArray(scip, &isused, nchildren) );
4036
4037 SCIP_CALL( SCIPallocBufferArray(scip, &exprs, nchildren) );
4038 SCIP_CALL( SCIPallocBufferArray(scip, &exprcoefs, nchildren) );
4039 SCIP_CALL( SCIPallocBufferArray(scip, &tmpvars, MIN(nterms, nvars)) );
4040 SCIP_CALL( SCIPallocBufferArray(scip, &tmpcoefs, MIN(nterms, nvars)) );
4041
4042 for( i = 0; i < nterms; ++i )
4043 {
4044 int xidx;
4045 int yidx;
4046
4047 assert(xs[i] != NULL);
4048 assert(ys[i] != NULL);
4049
4050 xidx = SCIPvarGetIndex(xs[i]);
4051 assert(xidx < ntotalvars);
4052 yidx = SCIPvarGetIndex(ys[i]);
4053 assert(yidx < ntotalvars);
4054
4055 ++count[xidx];
4056 ++count[yidx];
4057
4058 SCIPdebugMsg(scip, "increase counter for %s to %d\n", SCIPvarGetName(xs[i]), count[xidx]);
4059 SCIPdebugMsg(scip, "increase counter for %s to %d\n", SCIPvarGetName(ys[i]), count[yidx]);
4060 }
4061
4062 /* sort variables; don't change order of count array because it depends on problem indices */
4063 {
4064 int* tmpcount;
4065
4066 SCIP_CALL( SCIPduplicateBufferArray(scip, &tmpcount, count, nvars) );
4067 SCIPsortDownIntPtr(tmpcount, (void**)vars, nvars);
4068 SCIPfreeBufferArray(scip, &tmpcount);
4069 }
4070
4071 for( i = 0; i < nvars; ++i )
4072 {
4073 SCIP_VAR* facvar = vars[i];
4074 int ntmpvars = 0;
4075 int j;
4076
4077 /* skip candidate if there are not enough terms left */
4078 if( count[SCIPvarGetIndex(vars[i])] < minterms )
4079 continue;
4080
4081 SCIPdebugMsg(scip, "consider facvar = %s with count = %d\n", SCIPvarGetName(facvar), count[SCIPvarGetIndex(vars[i])]);
4082
4083 /* collect variables for x_i * sum_j c_ij x_j */
4084 for( j = 0; j < nterms; ++j )
4085 {
4086 int childidx = childidxs[j];
4087 assert(childidx >= 0 && childidx < nchildren);
4088
4089 if( !isused[childidx] && (xs[j] == facvar || ys[j] == facvar) )
4090 {
4091 SCIP_Real coef;
4092 int xidx;
4093 int yidx;
4094
4095 coef = sumcoefs[childidx];
4096 assert(coef != 0.0);
4097
4098 /* collect corresponding variable */
4099 tmpvars[ntmpvars] = (xs[j] == facvar) ? ys[j] : xs[j];
4100 tmpcoefs[ntmpvars] = coef;
4101 ++ntmpvars;
4102
4103 /* update counters */
4104 xidx = SCIPvarGetIndex(xs[j]);
4105 assert(xidx < ntotalvars);
4106 yidx = SCIPvarGetIndex(ys[j]);
4107 assert(yidx < ntotalvars);
4108 --count[xidx];
4109 --count[yidx];
4110 assert(count[xidx] >= 0);
4111 assert(count[yidx] >= 0);
4112
4113 /* mark term to be used */
4114 isused[childidx] = TRUE;
4115 }
4116 }
4117 assert(ntmpvars >= minterms);
4118 assert(SCIPvarGetIndex(facvar) < ntotalvars);
4119 assert(count[SCIPvarGetIndex(facvar)] == 0); /* facvar should not appear in any other bilinear term */
4120
4121 /* create required constraints and store the generated expression */
4122 SCIP_CALL( reformulateFactorizedBinaryQuadratic(scip, conshdlr, cons, facvar, tmpvars, tmpcoefs, ntmpvars, &exprs[nexprs], naddconss) );
4123 exprcoefs[nexprs] = 1.0;
4124 ++nexprs;
4125 }
4126
4127 /* factorization was only successful if at least one expression has been generated */
4128 if( nexprs > 0 )
4129 {
4130 int nexprsold = nexprs;
4131
4132 /* add all children of the sum that have not been used */
4133 for( i = 0; i < nchildren; ++i )
4134 {
4135 if( !isused[i] )
4136 {
4137 exprs[nexprs] = SCIPexprGetChildren(sumexpr)[i];
4138 exprcoefs[nexprs] = sumcoefs[i];
4139 ++nexprs;
4140 }
4141 }
4142
4143 /* create a new sum expression */
4144 SCIP_CALL( SCIPcreateExprSum(scip, newexpr, nexprs, exprs, exprcoefs, SCIPgetConstantExprSum(sumexpr), exprownerCreate, (void*)conshdlr) );
4145
4146 /* release all expressions that have been generated by reformulateFactorizedBinaryQuadratic() */
4147 for( i = 0; i < nexprsold; ++i )
4148 {
4149 SCIP_CALL( SCIPreleaseExpr(scip, &exprs[i]) );
4150 }
4151 }
4152
4153 TERMINATE:
4154 /* free memory */
4155 SCIPfreeBufferArrayNull(scip, &tmpcoefs);
4156 SCIPfreeBufferArrayNull(scip, &tmpvars);
4157 SCIPfreeBufferArrayNull(scip, &exprcoefs);
4158 SCIPfreeBufferArrayNull(scip, &exprs);
4159 SCIPfreeBufferArrayNull(scip, &vars);
4160 SCIPfreeBufferArrayNull(scip, &isused);
4161 SCIPfreeBufferArrayNull(scip, &count);
4162 SCIPfreeBufferArray(scip, &childidxs);
4163 SCIPfreeBufferArray(scip, &ys);
4164 SCIPfreeBufferArray(scip, &xs);
4165
4166 return SCIP_OKAY;
4167 }
4168
4169 /** helper method to create an AND constraint or varbound constraints for a given binary product expression */
4170 static
4171 SCIP_RETCODE getBinaryProductExprDo(
4172 SCIP* scip, /**< SCIP data structure */
4173 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
4174 SCIP_EXPR* prodexpr, /**< product expression */
4175 SCIP_EXPR** newexpr, /**< pointer to store the expression that represents the product */
4176 int* naddconss, /**< pointer to update the total number of added constraints (might be NULL) */
4177 SCIP_Bool empathy4and /**< whether to use an AND constraint, if possible */
4178 )
4179 {
4180 SCIP_VAR** vars;
4181 SCIP_CONS* cons;
4182 SCIP_Real* coefs;
4183 SCIP_VAR* w;
4184 char* name;
4185 int nchildren;
4186 int i;
4187
4188 assert(conshdlr != NULL);
4189 assert(prodexpr != NULL);
4190 assert(SCIPisExprProduct(scip, prodexpr));
4191 assert(newexpr != NULL);
4192
4193 nchildren = SCIPexprGetNChildren(prodexpr);
4194 assert(nchildren >= 2);
4195
4196 /* memory to store the variables of the variable expressions (+1 for w) and their name */
4197 SCIP_CALL( SCIPallocBufferArray(scip, &vars, nchildren + 1) );
4198 SCIP_CALL( SCIPallocBufferArray(scip, &coefs, nchildren + 1) );
4199 SCIP_CALL( SCIPallocBufferArray(scip, &name, nchildren * (SCIP_MAXSTRLEN + 1) + 20) );
4200
4201 /* prepare the names of the variable and the constraints */
(1) Event secure_coding: |
[VERY RISKY]. Using "strcpy(char *, char const *)" can cause a buffer overflow when done incorrectly. If the destination string of a strcpy() is not large enough then anything might happen. Use strncpy() instead. |
4202 strcpy(name, "binreform");
4203 for( i = 0; i < nchildren; ++i )
4204 {
4205 vars[i] = SCIPgetVarExprVar(SCIPexprGetChildren(prodexpr)[i]);
4206 coefs[i] = 1.0;
4207 assert(vars[i] != NULL);
4208 (void) strcat(name, "_");
4209 (void) strcat(name, SCIPvarGetName(vars[i]));
4210 }
4211
4212 /* create and add variable */
4213 SCIP_CALL( SCIPcreateVarBasic(scip, &w, name, 0.0, 1.0, 0.0, SCIP_VARTYPE_IMPLINT) );
4214 SCIP_CALL( SCIPaddVar(scip, w) );
4215 SCIPdebugMsg(scip, " created auxiliary variable %s\n", name);
4216
4217 /* use variable bound constraints if it is a bilinear product and there is no empathy for an AND constraint */
4218 if( nchildren == 2 && !empathy4and )
4219 {
4220 SCIP_VAR* x = vars[0];
4221 SCIP_VAR* y = vars[1];
4222
4223 assert(x != NULL);
4224 assert(y != NULL);
4225 assert(x != y);
4226
4227 /* create and add x - w >= 0 */
4228 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s_1", SCIPvarGetName(x), SCIPvarGetName(y));
4229 SCIP_CALL( SCIPcreateConsBasicVarbound(scip, &cons, name, x, w, -1.0, 0.0, SCIPinfinity(scip)) );
4230 SCIP_CALL( SCIPaddCons(scip, cons) );
4231 SCIP_CALL( SCIPreleaseCons(scip, &cons) );
4232
4233 /* create and add y - w >= 0 */
4234 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s_2", SCIPvarGetName(x), SCIPvarGetName(y));
4235 SCIP_CALL( SCIPcreateConsBasicVarbound(scip, &cons, name, y, w, -1.0, 0.0, SCIPinfinity(scip)) );
4236 SCIP_CALL( SCIPaddCons(scip, cons) );
4237 SCIP_CALL( SCIPreleaseCons(scip, &cons) );
4238
4239 /* create and add x + y - w <= 1 */
4240 vars[2] = w;
4241 coefs[2] = -1.0;
4242 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s_3", SCIPvarGetName(x), SCIPvarGetName(y));
4243 SCIP_CALL( SCIPcreateConsBasicLinear(scip, &cons, name, 3, vars, coefs, -SCIPinfinity(scip), 1.0) );
4244 SCIP_CALL( SCIPaddCons(scip, cons) );
4245 SCIP_CALL( SCIPreleaseCons(scip, &cons) );
4246
4247 /* update number of added constraints */
4248 if( naddconss != NULL )
4249 *naddconss += 3;
4250 }
4251 else
4252 {
4253 /* create, add, and release AND constraint */
4254 SCIP_CALL( SCIPcreateConsBasicAnd(scip, &cons, name, w, nchildren, vars) );
4255 SCIP_CALL( SCIPaddCons(scip, cons) );
4256 SCIP_CALL( SCIPreleaseCons(scip, &cons) );
4257 SCIPdebugMsg(scip, " create AND constraint\n");
4258
4259 /* update number of added constraints */
4260 if( naddconss != NULL )
4261 *naddconss += 1;
4262 }
4263
4264 /* create variable expression */
4265 SCIP_CALL( createExprVar(scip, conshdlr, newexpr, w) );
4266
4267 /* release created variable */
4268 SCIP_CALL( SCIPreleaseVar(scip, &w) );
4269
4270 /* free memory */
4271 SCIPfreeBufferArray(scip, &name);
4272 SCIPfreeBufferArray(scip, &coefs);
4273 SCIPfreeBufferArray(scip, &vars);
4274
4275 return SCIP_OKAY;
4276 }
4277
4278 /** helper method to generate an expression for the product of binary variables; note that the method captures the generated expression */
4279 static
4280 SCIP_RETCODE getBinaryProductExpr(
4281 SCIP* scip, /**< SCIP data structure */
4282 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
4283 SCIP_HASHMAP* exprmap, /**< map to remember generated variables for visited product expressions */
4284 SCIP_EXPR* prodexpr, /**< product expression */
4285 SCIP_EXPR** newexpr, /**< pointer to store the expression that represents the product */
4286 int* naddconss, /**< pointer to update the total number of added constraints (might be NULL) */
4287 int* nchgcoefs /**< pointer to update the total number of changed coefficients (might be NULL) */
4288 )
4289 {
4290 SCIP_CONSHDLRDATA* conshdlrdata;
4291 int nchildren;
4292
4293 assert(prodexpr != NULL);
4294 assert(newexpr != NULL);
4295
4296 *newexpr = NULL;
4297
4298 /* only consider products of binary variables */
4299 if( !isBinaryProduct(scip, prodexpr) )
4300 return SCIP_OKAY;
4301
4302 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4303 assert(conshdlrdata != NULL);
4304 nchildren = SCIPexprGetNChildren(prodexpr);
4305 assert(nchildren >= 2);
4306
4307 /* check whether there is already an expression that represents the product */
4308 if( SCIPhashmapExists(exprmap, (void*)prodexpr) )
4309 {
4310 *newexpr = (SCIP_EXPR*) SCIPhashmapGetImage(exprmap, (void*)prodexpr);
4311 assert(*newexpr != NULL);
4312
4313 /* capture expression */
4314 SCIPcaptureExpr(*newexpr);
4315 }
4316 else
4317 {
4318 SCIPdebugMsg(scip, " product expression %p has been considered for the first time\n", (void*)prodexpr);
4319
4320 if( nchildren == 2 )
4321 {
4322 SCIP_CLIQUE** xcliques;
4323 SCIP_VAR* x;
4324 SCIP_VAR* y;
4325 SCIP_Bool found_clique = FALSE;
4326 int c;
4327
4328 /* get variables from the product expression */
4329 x = SCIPgetVarExprVar(SCIPexprGetChildren(prodexpr)[0]);
4330 assert(x != NULL);
4331 y = SCIPgetVarExprVar(SCIPexprGetChildren(prodexpr)[1]);
4332 assert(y != NULL);
4333 assert(x != y);
4334
4335 /* first try to find a clique containing both variables */
4336 xcliques = SCIPvarGetCliques(x, TRUE);
4337
4338 /* look in cliques containing x */
4339 for( c = 0; c < SCIPvarGetNCliques(x, TRUE); ++c )
4340 {
4341 if( SCIPcliqueHasVar(xcliques[c], y, TRUE) ) /* x + y <= 1 => x*y = 0 */
4342 {
4343 /* create zero value expression */
4344 SCIP_CALL( SCIPcreateExprValue(scip, newexpr, 0.0, exprownerCreate, (void*)conshdlr) );
4345
4346 if( nchgcoefs != NULL )
4347 *nchgcoefs += 1;
4348
4349 found_clique = TRUE;
4350 break;
4351 }
4352
4353 if( SCIPcliqueHasVar(xcliques[c], y, FALSE) ) /* x + (1-y) <= 1 => x*y = x */
4354 {
4355 /* create variable expression for x */
4356 SCIP_CALL( createExprVar(scip, conshdlr, newexpr, x) );
4357
4358 if( nchgcoefs != NULL )
4359 *nchgcoefs += 2;
4360
4361 found_clique = TRUE;
4362 break;
4363 }
4364 }
4365
4366 if( !found_clique )
4367 {
4368 xcliques = SCIPvarGetCliques(x, FALSE);
4369
4370 /* look in cliques containing complement of x */
4371 for( c = 0; c < SCIPvarGetNCliques(x, FALSE); ++c )
4372 {
4373 if( SCIPcliqueHasVar(xcliques[c], y, TRUE) ) /* (1-x) + y <= 1 => x*y = y */
4374 {
4375 /* create variable expression for y */
4376 SCIP_CALL( createExprVar(scip, conshdlr, newexpr, y) );
4377
4378 if( nchgcoefs != NULL )
4379 *nchgcoefs += 1;
4380
4381 found_clique = TRUE;
4382 break;
4383 }
4384
4385 if( SCIPcliqueHasVar(xcliques[c], y, FALSE) ) /* (1-x) + (1-y) <= 1 => x*y = x + y - 1 */
4386 {
4387 /* create sum expression */
4388 SCIP_EXPR* sum_children[2];
4389 SCIP_Real sum_coefs[2];
4390 SCIP_CALL( createExprVar(scip, conshdlr, &sum_children[0], x) );
4391 SCIP_CALL( createExprVar(scip, conshdlr, &sum_children[1], y) );
4392 sum_coefs[0] = 1.0;
4393 sum_coefs[1] = 1.0;
4394 SCIP_CALL( SCIPcreateExprSum(scip, newexpr, 2, sum_children, sum_coefs, -1.0, exprownerCreate, (void*)conshdlr) );
4395
4396 SCIP_CALL( SCIPreleaseExpr(scip, &sum_children[0]) );
4397 SCIP_CALL( SCIPreleaseExpr(scip, &sum_children[1]) );
4398
4399 if( nchgcoefs != NULL )
4400 *nchgcoefs += 3;
4401
4402 found_clique = TRUE;
4403 break;
4404 }
4405 }
4406 }
4407
4408 /* if the variables are not in a clique, do standard linearization */
4409 if( !found_clique )
4410 {
4411 SCIP_CALL( getBinaryProductExprDo(scip, conshdlr, prodexpr, newexpr, naddconss, conshdlrdata->reformbinprodsand) );
4412 }
4413 }
4414 else
4415 {
4416 /* linearize binary product using an AND constraint because nchildren > 2 */
4417 SCIP_CALL( getBinaryProductExprDo(scip, conshdlr, prodexpr, newexpr, naddconss, conshdlrdata->reformbinprodsand) );
4418 }
4419
4420 /* hash variable expression */
4421 SCIP_CALL( SCIPhashmapInsert(exprmap, (void*)prodexpr, *newexpr) );
4422 }
4423
4424 return SCIP_OKAY;
4425 }
4426
4427 /** helper function to replace binary products in a given constraint */
4428 static
4429 SCIP_RETCODE replaceBinaryProducts(
4430 SCIP* scip, /**< SCIP data structure */
4431 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
4432 SCIP_CONS* cons, /**< constraint */
4433 SCIP_HASHMAP* exprmap, /**< map to remember generated variables for visited product expressions */
4434 SCIP_EXPRITER* it, /**< expression iterator */
4435 int* naddconss, /**< pointer to update the total number of added constraints (might be NULL) */
4436 int* nchgcoefs /**< pointer to update the total number of changed coefficients (might be NULL) */
4437 )
4438 {
4439 SCIP_CONSHDLRDATA* conshdlrdata;
4440 SCIP_CONSDATA* consdata;
4441 SCIP_EXPR* expr;
4442
4443 assert(conshdlr != NULL);
4444 assert(cons != NULL);
4445 assert(exprmap != NULL);
4446 assert(it != NULL);
4447
4448 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4449 assert(conshdlrdata != NULL);
4450
4451 consdata = SCIPconsGetData(cons);
4452 assert(consdata != NULL);
4453 assert(consdata->expr != NULL);
4454
4455 SCIPdebugMsg(scip, " check constraint %s\n", SCIPconsGetName(cons));
4456
4457 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
4458 {
4459 SCIP_EXPR* newexpr = NULL;
4460 SCIP_EXPR* childexpr;
4461 int childexpridx;
4462
4463 childexpridx = SCIPexpriterGetChildIdxDFS(it);
4464 assert(childexpridx >= 0 && childexpridx < SCIPexprGetNChildren(expr));
4465 childexpr = SCIPexpriterGetChildExprDFS(it);
4466 assert(childexpr != NULL);
4467
4468 /* try to factorize variables in a sum expression that contains several products of binary variables */
4469 if( conshdlrdata->reformbinprodsfac > 1 )
4470 {
4471 SCIP_CALL( getFactorizedBinaryQuadraticExpr(scip, conshdlr, cons, childexpr, conshdlrdata->reformbinprodsfac, &newexpr, naddconss) );
4472 }
4473
4474 /* try to create an expression that represents a product of binary variables */
4475 if( newexpr == NULL )
4476 {
4477 SCIP_CALL( getBinaryProductExpr(scip, conshdlr, exprmap, childexpr, &newexpr, naddconss, nchgcoefs) );
4478 }
4479
4480 if( newexpr != NULL )
4481 {
4482 assert(naddconss == NULL || *naddconss > 0 || nchgcoefs == NULL || *nchgcoefs > 0);
4483
4484 /* replace product expression */
4485 SCIP_CALL( SCIPreplaceExprChild(scip, expr, childexpridx, newexpr) );
4486
4487 /* note that the expression has been captured by getBinaryProductExpr and SCIPreplaceExprChild */
4488 SCIP_CALL( SCIPreleaseExpr(scip, &newexpr) );
4489
4490 /* mark the constraint to not be simplified anymore */
4491 consdata->issimplified = FALSE;
4492 }
4493 }
4494
4495 return SCIP_OKAY;
4496 }
4497
4498 /** reformulates products of binary variables during presolving in the following way:
4499 *
4500 * Let \f$\sum_{i,j} Q_{ij} x_i x_j\f$ be a subexpression that only contains binary variables.
4501 * 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}:
4502 * \f[
4503 * z_{ij} \leq x_i, \qquad z_{ij} \leq x_j, \qquad x_i + x_j - z_{ij} \leq 1.
4504 * \f]
4505 *
4506 * 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$.
4507 * These cliques allow for a better reformulation. There are four cases:
4508 *
4509 * 1. \f$x_i + x_j \leq 1\f$ implies that \f$x_i x_j = 0\f$
4510 * 2. \f$x_i + (1 - x_j) \leq 1\f$ implies \f$x_i x_j = x_i\f$
4511 * 3. \f$(1 - x_i) + x_j \leq 1\f$ implies \f$x_i x_j = x_j\f$
4512 * 4. \f$(1 - x_i) + (1 - x_j) \leq 1\f$ implies \f$x_i x_j = x_i + x_j - 1\f$
4513 *
4514 * The reformulation using \f$z_{ij}\f$ or the cliques is implemented in getBinaryProductExpr().
4515 *
4516 * Introducing too many extra variables and constraints can have a negative impact on the performance (e.g., due to
4517 * slow probing). For this reason, it is checked in getFactorizedBinaryQuadraticExpr() whether \f$\sum_{i,j} Q_{ij} x_i x_j\f$
4518 * contains large (≥ `reformbinprodsfac` parameter) lower sums of the form \f$x_i \sum_j Q_{ij} x_j\f$.
4519 * Such a lower sum is reformulated with only one extra variable w_i:
4520 * \f{align}{
4521 * \text{maxact} & := \sum_j \max(0, Q_{ij}), \\
4522 * \text{minact} & := \sum_j \min(0, Q_{ij}), \\
4523 * \text{minact}\, x_i & \leq w_i, \\
4524 * w_i &\leq \text{maxact}\, x_i, \\
4525 * \text{minact} &\leq \sum_j Q_{ij} x_j - w_i + \text{minact}\, x_i \\
4526 * \text{maxact} &\geq \sum_j Q_{ij} x_j - w_i + \text{maxact}\, x_i
4527 * \f}
4528 * 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
4529 * is checked whether there are enough terms left to factorize other binary variables. Lower sums with a larger number
4530 * of terms are prioritized.
4531 */
4532 static
4533 SCIP_RETCODE presolveBinaryProducts(
4534 SCIP* scip, /**< SCIP data structure */
4535 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
4536 SCIP_CONS** conss, /**< constraints */
4537 int nconss, /**< total number of constraints */
4538 int* naddconss, /**< pointer to store the total number of added constraints (might be NULL) */
4539 int* nchgcoefs /**< pointer to store the total number of changed coefficients (might be NULL) */
4540 )
4541 {
4542 SCIP_CONSHDLRDATA* conshdlrdata;
4543 SCIP_HASHMAP* exprmap;
4544 SCIP_EXPRITER* it;
4545 int c;
4546
4547 assert(conshdlr != NULL);
4548
4549 /* no nonlinear constraints or binary variables -> skip */
4550 if( nconss == 0 || SCIPgetNBinVars(scip) == 0 )
4551 return SCIP_OKAY;
4552 assert(conss != NULL);
4553
4554 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4555 assert(conshdlrdata != NULL);
4556
4557 /* create expression hash map */
4558 SCIP_CALL( SCIPhashmapCreate(&exprmap, SCIPblkmem(scip), SCIPgetNVars(scip)) );
4559
4560 /* create expression iterator */
4561 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
4562 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) );
4563 SCIPexpriterSetStagesDFS(it, SCIP_EXPRITER_VISITINGCHILD);
4564
4565 SCIPdebugMsg(scip, "call presolveBinaryProducts()\n");
4566
4567 for( c = 0; c < nconss; ++c )
4568 {
4569 SCIP_CONSDATA* consdata;
4570 SCIP_EXPR* newexpr = NULL;
4571
4572 assert(conss[c] != NULL);
4573
4574 consdata = SCIPconsGetData(conss[c]);
4575 assert(consdata != NULL);
4576
4577 /* try to reformulate the root expression */
4578 if( conshdlrdata->reformbinprodsfac > 1 )
4579 {
4580 SCIP_CALL( getFactorizedBinaryQuadraticExpr(scip, conshdlr, conss[c], consdata->expr, conshdlrdata->reformbinprodsfac, &newexpr, naddconss) );
4581 }
4582
4583 /* release the root node if another expression has been found */
4584 if( newexpr != NULL )
4585 {
4586 SCIP_CALL( SCIPreleaseExpr(scip, &consdata->expr) );
4587 consdata->expr = newexpr;
4588
4589 /* mark constraint to be not simplified anymore */
4590 consdata->issimplified = FALSE;
4591 }
4592
4593 /* replace each product of binary variables separately */
4594 SCIP_CALL( replaceBinaryProducts(scip, conshdlr, conss[c], exprmap, it, naddconss, nchgcoefs) );
4595 }
4596
4597 /* free memory */
4598 SCIPhashmapFree(&exprmap);
4599 SCIPfreeExpriter(&it);
4600
4601 return SCIP_OKAY;
4602 }
4603
4604 /** scales the sides of the constraint \f$\ell \leq \sum_i c_i f_i(x) \leq r\f$.
4605 *
4606 * Let \f$n_+\f$ the number of positive coefficients \f$c_i\f$ and \f$n_-\f$ be the number of negative coefficients.
4607 * Then scale by -1 if
4608 * - \f$n_+ < n_-\f$, or
4609 * - \f$n_+ = n_-\f$ and \f$r = \infty\f$.
4610 */
4611 static
4612 SCIP_RETCODE scaleConsSides(
4613 SCIP* scip, /**< SCIP data structure */
4614 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */
4615 SCIP_CONS* cons, /**< nonlinear constraint */
4616 SCIP_Bool* changed /**< buffer to store if the expression of cons changed */
4617 )
4618 {
4619 SCIP_CONSDATA* consdata;
4620 int i;
4621
4622 assert(cons != NULL);
4623
4624 consdata = SCIPconsGetData(cons);
4625 assert(consdata != NULL);
4626
4627 if( SCIPisExprSum(scip, consdata->expr) )
4628 {
4629 SCIP_Real* coefs;
4630 SCIP_Real constant;
4631 int nchildren;
4632 int counter = 0;
4633
4634 coefs = SCIPgetCoefsExprSum(consdata->expr);
4635 constant = SCIPgetConstantExprSum(consdata->expr);
4636 nchildren = SCIPexprGetNChildren(consdata->expr);
4637
4638 /* handle special case when constraint is l <= -f(x) <= r and f(x) not a sum: simplfy ensures f is not a sum */
4639 if( nchildren == 1 && constant == 0.0 && coefs[0] == -1.0 )
4640 {
4641 SCIP_EXPR* expr;
4642 expr = consdata->expr;
4643
4644 consdata->expr = SCIPexprGetChildren(expr)[0];
4645 assert(!SCIPisExprSum(scip, consdata->expr));
4646
4647 SCIPcaptureExpr(consdata->expr);
4648
4649 SCIPswapReals(&consdata->lhs, &consdata->rhs);
4650 consdata->lhs = -consdata->lhs;
4651 consdata->rhs = -consdata->rhs;
4652
4653 SCIP_CALL( SCIPreleaseExpr(scip, &expr) );
4654 *changed = TRUE;
4655 return SCIP_OKAY;
4656 }
4657
4658 /* compute n_+ - n_i */
4659 for( i = 0; i < nchildren; ++i )
4660 counter += coefs[i] > 0 ? 1 : -1;
4661
4662 if( counter < 0 || (counter == 0 && SCIPisInfinity(scip, consdata->rhs)) )
4663 {
4664 SCIP_EXPR* expr;
4665 SCIP_Real* newcoefs;
4666
4667 /* allocate memory */
4668 SCIP_CALL( SCIPallocBufferArray(scip, &newcoefs, nchildren) );
4669
4670 for( i = 0; i < nchildren; ++i )
4671 newcoefs[i] = -coefs[i];
4672
4673 /* create a new sum expression */
4674 SCIP_CALL( SCIPcreateExprSum(scip, &expr, nchildren, SCIPexprGetChildren(consdata->expr), newcoefs, -constant, exprownerCreate, (void*)conshdlr) );
4675
4676 /* replace expression in constraint data and scale sides */
4677 SCIP_CALL( SCIPreleaseExpr(scip, &consdata->expr) );
4678 consdata->expr = expr;
4679 SCIPswapReals(&consdata->lhs, &consdata->rhs);
4680 consdata->lhs = -consdata->lhs;
4681 consdata->rhs = -consdata->rhs;
4682
4683 /* free memory */
4684 SCIPfreeBufferArray(scip, &newcoefs);
4685
4686 *changed = TRUE;
4687 }
4688 }
4689
4690 return SCIP_OKAY;
4691 }
4692
4693 /** forbid multiaggrations of variables that appear nonlinear in constraints */
4694 static
4695 SCIP_RETCODE forbidNonlinearVariablesMultiaggration(
4696 SCIP* scip, /**< SCIP data structure */
4697 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
4698 SCIP_CONS** conss, /**< constraints */
4699 int nconss /**< number of constraints */
4700 )
4701 {
4702 SCIP_EXPRITER* it;
4703 SCIP_CONSDATA* consdata;
4704 SCIP_EXPR* expr;
4705 int c;
4706
4707 assert(scip != NULL);
4708 assert(conshdlr != NULL);
4709
4710 if( !SCIPconshdlrGetData(conshdlr)->forbidmultaggrnlvar )
4711 return SCIP_OKAY;
4712
4713 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
4714 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) );
4715
4716 for( c = 0; c < nconss; ++c )
4717 {
4718 consdata = SCIPconsGetData(conss[c]);
4719 assert(consdata != NULL);
4720
4721 /* if root expression is sum, then forbid multiaggregation only for variables that are not in linear terms of sum,
4722 * i.e., skip children of sum that are variables
4723 */
4724 if( SCIPisExprSum(scip, consdata->expr) )
4725 {
4726 int i;
4727 SCIP_EXPR* child;
4728 for( i = 0; i < SCIPexprGetNChildren(consdata->expr); ++i )
4729 {
4730 child = SCIPexprGetChildren(consdata->expr)[i];
4731
4732 /* skip variable expression, as they correspond to a linear term */
4733 if( SCIPisExprVar(scip, child) )
4734 continue;
4735
4736 for( expr = SCIPexpriterRestartDFS(it, child); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
4737 if( SCIPisExprVar(scip, expr) )
4738 {
4739 SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, SCIPgetVarExprVar(expr)) );
4740 }
4741 }
4742 }
4743 else
4744 {
4745 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
4746 if( SCIPisExprVar(scip, expr) )
4747 {
4748 SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, SCIPgetVarExprVar(expr)) );
4749 }
4750 }
4751 }
4752
4753 SCIPfreeExpriter(&it);
4754
4755 return SCIP_OKAY;
4756 }
4757
4758 /** simplifies expressions and replaces common subexpressions for a set of constraints
4759 * @todo put the constant to the constraint sides
4760 */
4761 static
4762 SCIP_RETCODE canonicalizeConstraints(
4763 SCIP* scip, /**< SCIP data structure */
4764 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
4765 SCIP_CONS** conss, /**< constraints */
4766 int nconss, /**< total number of constraints */
4767 SCIP_PRESOLTIMING presoltiming, /**< presolve timing (SCIP_PRESOLTIMING_ALWAYS if not in presolving) */
4768 SCIP_Bool* infeasible, /**< buffer to store whether infeasibility has been detected */
4769 int* ndelconss, /**< counter to add number of deleted constraints, or NULL */
4770 int* naddconss, /**< counter to add number of added constraints, or NULL */
4771 int* nchgcoefs /**< counter to add number of changed coefficients, or NULL */
4772 )
4773 {
4774 SCIP_CONSHDLRDATA* conshdlrdata;
4775 SCIP_CONSDATA* consdata;
4776 int* nlockspos;
4777 int* nlocksneg;
4778 SCIP_Bool havechange;
4779 int i;
4780
4781 assert(scip != NULL);
4782 assert(conshdlr != NULL);
4783 assert(conss != NULL);
4784 assert(nconss > 0);
4785 assert(infeasible != NULL);
4786
4787 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4788 assert(conshdlrdata != NULL);
4789
4790 /* update number of canonicalize calls */
4791 ++(conshdlrdata->ncanonicalizecalls);
4792
4793 SCIP_CALL( SCIPstartClock(scip, conshdlrdata->canonicalizetime) );
4794
4795 *infeasible = FALSE;
4796
4797 /* set havechange to TRUE in the first call of canonicalize; otherwise we might not replace common subexpressions */
4798 havechange = conshdlrdata->ncanonicalizecalls == 1;
4799
4800 /* free nonlinear handlers information from expressions */ /* TODO can skip this in first presolve round */
4801 SCIP_CALL( deinitSolve(scip, conshdlr, conss, nconss) );
4802
4803 /* allocate memory for storing locks of each constraint */
4804 SCIP_CALL( SCIPallocBufferArray(scip, &nlockspos, nconss) );
4805 SCIP_CALL( SCIPallocBufferArray(scip, &nlocksneg, nconss) );
4806
4807 /* unlock all constraints */
4808 for( i = 0; i < nconss; ++i )
4809 {
4810 assert(conss[i] != NULL);
4811
4812 consdata = SCIPconsGetData(conss[i]);
4813 assert(consdata != NULL);
4814
4815 /* remember locks */
4816 nlockspos[i] = consdata->nlockspos;
4817 nlocksneg[i] = consdata->nlocksneg;
4818
4819 /* remove locks */
4820 SCIP_CALL( addLocks(scip, conss[i], -consdata->nlockspos, -consdata->nlocksneg) );
4821 assert(consdata->nlockspos == 0);
4822 assert(consdata->nlocksneg == 0);
4823 }
4824
4825 #ifndef NDEBUG
4826 /* check whether all locks of each expression have been removed */
4827 for( i = 0; i < nconss; ++i )
4828 {
4829 SCIP_EXPR* expr;
4830 SCIP_EXPRITER* it;
4831
4832 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
4833
4834 consdata = SCIPconsGetData(conss[i]);
4835 assert(consdata != NULL);
4836
4837 SCIP_CALL( SCIPexpriterInit(it, consdata->expr, SCIP_EXPRITER_RTOPOLOGIC, TRUE) );
4838 for( expr = consdata->expr; !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
4839 {
4840 assert(expr != NULL);
4841 assert(SCIPexprGetOwnerData(expr)->nlocksneg == 0);
4842 assert(SCIPexprGetOwnerData(expr)->nlockspos == 0);
4843 }
4844 SCIPfreeExpriter(&it);
4845 }
4846 #endif
4847
4848 /* reformulate products of binary variables */
4849 if( conshdlrdata->reformbinprods && SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING
4850 && (presoltiming & SCIP_PRESOLTIMING_EXHAUSTIVE) )
4851 {
4852 int tmpnaddconss = 0;
4853 int tmpnchgcoefs = 0;
4854
4855 /* call this function before simplification because expressions might not be simplified after reformulating
4856 * binary products; the detection of some nonlinear handlers might assume that expressions are simplified
4857 */
4858 SCIP_CALL( presolveBinaryProducts(scip, conshdlr, conss, nconss, &tmpnaddconss, &tmpnchgcoefs) );
4859
4860 /* update counters */
4861 if( naddconss != NULL )
4862 *naddconss = tmpnaddconss;
4863 if( nchgcoefs != NULL )
4864 *nchgcoefs = tmpnchgcoefs;
4865
4866 /* check whether at least one expression has changed */
4867 if( tmpnaddconss + tmpnchgcoefs > 0 )
4868 havechange = TRUE;
4869 }
4870
4871 for( i = 0; i < nconss; ++i )
4872 {
4873 consdata = SCIPconsGetData(conss[i]);
4874 assert(consdata != NULL);
4875
4876 /* call simplify for each expression */
4877 if( !consdata->issimplified && consdata->expr != NULL )
4878 {
4879 SCIP_EXPR* simplified;
4880 SCIP_Bool changed;
4881
4882 changed = FALSE;
4883 SCIP_CALL( SCIPsimplifyExpr(scip, consdata->expr, &simplified, &changed, infeasible, exprownerCreate, (void*)conshdlr) );
4884 consdata->issimplified = TRUE;
4885
4886 if( changed )
4887 havechange = TRUE;
4888
4889 /* 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").
4890 * If root expression did not change, some subexpression may still have changed, but the locks were taking care of in the corresponding SCIPreplaceExprChild() call.
4891 */
4892 if( simplified != consdata->expr )
4893 {
4894 assert(changed);
4895
4896 /* release old expression */
4897 SCIP_CALL( SCIPreleaseExpr(scip, &consdata->expr) );
4898
4899 /* store simplified expression */
4900 consdata->expr = simplified;
4901 }
4902 else
4903 {
4904 /* The simplify captures simplified in any case, also if nothing has changed.
4905 * Therefore, we have to release it here.
4906 */
4907 SCIP_CALL( SCIPreleaseExpr(scip, &simplified) );
4908 }
4909
4910 if( *infeasible )
4911 break;
4912
4913 /* scale constraint sides */
4914 SCIP_CALL( scaleConsSides(scip, conshdlr, conss[i], &changed) );
4915
4916 if( changed )
4917 havechange = TRUE;
4918
4919 /* handle constant root expression; either the problem is infeasible or the constraint is redundant */
4920 if( SCIPisExprValue(scip, consdata->expr) )
4921 {
4922 SCIP_Real value = SCIPgetValueExprValue(consdata->expr);
4923 if( (!SCIPisInfinity(scip, -consdata->lhs) && SCIPisFeasNegative(scip, value - consdata->lhs)) ||
4924 (!SCIPisInfinity(scip, consdata->rhs) && SCIPisFeasPositive(scip, value - consdata->rhs)) )
4925 {
4926 SCIPdebugMsg(scip, "<%s> with constant expression found infeasible\n", SCIPconsGetName(conss[i]));
4927 SCIPdebugPrintCons(scip, conss[i], NULL);
4928 *infeasible = TRUE;
4929 break;
4930 }
4931 else
4932 {
4933 SCIP_CALL( addLocks(scip, conss[i], nlockspos[i], nlocksneg[i]) );
4934 SCIP_CALL( SCIPdelCons(scip, conss[i]) );
4935 if( ndelconss != NULL )
4936 ++*ndelconss;
4937 havechange = TRUE;
4938 }
4939 }
4940 }
4941 }
4942
4943 /* replace common subexpressions */
4944 if( havechange && !*infeasible )
4945 {
4946 SCIP_CONS** consssorted;
4947 SCIP_EXPR** rootexprs;
4948 SCIP_Bool replacedroot;
4949
4950 SCIP_CALL( SCIPallocBufferArray(scip, &rootexprs, nconss) );
4951 for( i = 0; i < nconss; ++i )
4952 rootexprs[i] = SCIPconsGetData(conss[i])->expr;
4953
4954 SCIP_CALL( SCIPreplaceCommonSubexpressions(scip, rootexprs, nconss, &replacedroot) );
4955
4956 /* update pointer to root expr in constraints, if any has changed
4957 * SCIPreplaceCommonSubexpressions will have released the old expr and captures the new one
4958 */
4959 if( replacedroot )
4960 for( i = 0; i < nconss; ++i )
4961 SCIPconsGetData(conss[i])->expr = rootexprs[i];
4962
4963 SCIPfreeBufferArray(scip, &rootexprs);
4964
4965 /* TODO this is a possibly expensive way to update the variable expressions stored inside an expression which might have
4966 * been changed after simplification; now we completely recollect all variable expression and variable events
4967 */
4968
4969 /* Each variable stores the constraints for which it catched varbound events sorted by the constraint index.
4970 * Thus, for performance reasons, it is better to call dropVarEvents in descending order of constraint index.
4971 */
4972 SCIP_CALL( SCIPduplicateBufferArray(scip, &consssorted, conss, nconss) );
4973 SCIPsortPtr((void**)consssorted, compIndexConsNonlinear, nconss);
4974
4975 for( i = nconss-1; i >= 0; --i )
4976 {
4977 assert(i == 0 || compIndexConsNonlinear((void*)consssorted[i-1], (void*)consssorted[i]) < 0);
4978 if( SCIPconsIsDeleted(consssorted[i]) )
4979 continue;
4980
4981 SCIP_CALL( dropVarEvents(scip, conshdlrdata->eventhdlr, consssorted[i]) );
4982 SCIP_CALL( freeVarExprs(scip, SCIPconsGetData(consssorted[i])) );
4983 }
4984 for( i = 0; i < nconss; ++i )
4985 {
4986 if( SCIPconsIsDeleted(consssorted[i]) )
4987 continue;
4988
4989 SCIP_CALL( storeVarExprs(scip, conshdlr, SCIPconsGetData(consssorted[i])) );
4990 SCIP_CALL( catchVarEvents(scip, conshdlrdata->eventhdlr, consssorted[i]) );
4991 }
4992
4993 SCIPfreeBufferArray(scip, &consssorted);
4994
4995 /* forbid multiaggregation for nonlinear variables again (in case new variables appeared now)
4996 * a multiaggregation of a nonlinear variable can yield to a large increase in expressions due to
4997 * expanding terms in simplify, e.g. ,(sum_i x_i)^2, so we just forbid these
4998 */
4999 SCIP_CALL( forbidNonlinearVariablesMultiaggration(scip, conshdlr, conss, nconss) );
5000 }
5001
5002 /* restore locks */
5003 for( i = 0; i < nconss; ++i )
5004 {
5005 if( SCIPconsIsDeleted(conss[i]) )
5006 continue;
5007
5008 SCIP_CALL( addLocks(scip, conss[i], nlockspos[i], nlocksneg[i]) );
5009 }
5010
5011 /* run nlhdlr detect if in presolving stage (that is, not in exitpre)
5012 * TODO can we skip this in presoltiming fast?
5013 */
5014 if( SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING && !*infeasible )
5015 {
5016 /* reset one of the number of detections counter to count only current presolving round */
5017 for( i = 0; i < conshdlrdata->nnlhdlrs; ++i )
5018 SCIPnlhdlrResetNDetectionslast(conshdlrdata->nlhdlrs[i]);
5019
5020 SCIP_CALL( initSolve(scip, conshdlr, conss, nconss) );
5021 }
5022
5023 /* free allocated memory */
5024 SCIPfreeBufferArray(scip, &nlocksneg);
5025 SCIPfreeBufferArray(scip, &nlockspos);
5026
5027 SCIP_CALL( SCIPstopClock(scip, conshdlrdata->canonicalizetime) );
5028
5029 return SCIP_OKAY;
5030 }
5031
5032 /** merges constraints that have the same root expression */
5033 static
5034 SCIP_RETCODE presolveMergeConss(
5035 SCIP* scip, /**< SCIP data structure */
5036 SCIP_CONS** conss, /**< constraints to process */
5037 int nconss, /**< number of constraints */
5038 SCIP_Bool* success /**< pointer to store whether at least one constraint could be deleted */
5039 )
5040 {
5041 SCIP_HASHMAP* expr2cons;
5042 SCIP_Bool* updatelocks;
5043 int* nlockspos;
5044 int* nlocksneg;
5045 int c;
5046
5047 assert(success != NULL);
5048
5049 *success = FALSE;
5050
5051 /* not enough constraints available */
5052 if( nconss <= 1 )
5053 return SCIP_OKAY;
5054
5055 SCIP_CALL( SCIPhashmapCreate(&expr2cons, SCIPblkmem(scip), nconss) );
5056 SCIP_CALL( SCIPallocClearBufferArray(scip, &updatelocks, nconss) );
5057 SCIP_CALL( SCIPallocBufferArray(scip, &nlockspos, nconss) );
5058 SCIP_CALL( SCIPallocBufferArray(scip, &nlocksneg, nconss) );
5059
5060 for( c = 0; c < nconss; ++c )
5061 {
5062 SCIP_CONSDATA* consdata;
5063
5064 /* ignore deleted constraints */
5065 if( SCIPconsIsDeleted(conss[c]) )
5066 continue;
5067
5068 consdata = SCIPconsGetData(conss[c]);
5069 assert(consdata != NULL);
5070
5071 /* add expression to the hash map if not seen so far */
5072 if( !SCIPhashmapExists(expr2cons, (void*)consdata->expr) )
5073 {
5074 SCIP_CALL( SCIPhashmapInsertInt(expr2cons, (void*)consdata->expr, c) );
5075 }
5076 else
5077 {
5078 SCIP_CONSDATA* imgconsdata;
5079 int idx;
5080
5081 idx = SCIPhashmapGetImageInt(expr2cons, (void*)consdata->expr);
5082 assert(idx >= 0 && idx < nconss);
5083
5084 imgconsdata = SCIPconsGetData(conss[idx]);
5085 assert(imgconsdata != NULL);
5086 assert(imgconsdata->expr == consdata->expr);
5087
5088 SCIPdebugMsg(scip, "merge constraint %g <= %s <= %g with %g <= %s <= %g\n", consdata->lhs,
5089 SCIPconsGetName(conss[c]), consdata->rhs, imgconsdata->lhs, SCIPconsGetName(conss[idx]), imgconsdata->rhs);
5090
5091 /* check whether locks need to be updated */
5092 if( !updatelocks[idx] && ((SCIPisInfinity(scip, -imgconsdata->lhs) && !SCIPisInfinity(scip, -consdata->lhs))
5093 || (SCIPisInfinity(scip, imgconsdata->rhs) && !SCIPisInfinity(scip, consdata->rhs))) )
5094 {
5095 nlockspos[idx] = imgconsdata->nlockspos;
5096 nlocksneg[idx] = imgconsdata->nlocksneg;
5097 SCIP_CALL( addLocks(scip, conss[idx], -imgconsdata->nlockspos, -imgconsdata->nlocksneg) );
5098 updatelocks[idx] = TRUE;
5099 }
5100
5101 /* update constraint sides */
5102 imgconsdata->lhs = MAX(imgconsdata->lhs, consdata->lhs);
5103 imgconsdata->rhs = MIN(imgconsdata->rhs, consdata->rhs);
5104
5105 /* delete constraint */
5106 SCIP_CALL( SCIPdelCons(scip, conss[c]) );
5107 *success = TRUE;
5108 }
5109 }
5110
5111 /* restore locks of updated constraints */
5112 if( *success )
5113 {
5114 for( c = 0; c < nconss; ++c )
5115 {
5116 if( updatelocks[c] )
5117 {
5118 SCIP_CALL( addLocks(scip, conss[c], nlockspos[c], nlocksneg[c]) );
5119 }
5120 }
5121 }
5122
5123 /* free memory */
5124 SCIPfreeBufferArray(scip, &nlocksneg);
5125 SCIPfreeBufferArray(scip, &nlockspos);
5126 SCIPfreeBufferArray(scip, &updatelocks);
5127 SCIPhashmapFree(&expr2cons);
5128
5129 return SCIP_OKAY;
5130 }
5131
5132 /** interval evaluation of variables as used in redundancy check
5133 *
5134 * Returns local variable bounds of a variable, relaxed by feastol, as interval.
5135 */
5136 static
5137 SCIP_DECL_EXPR_INTEVALVAR(intEvalVarRedundancyCheck)
5138 { /*lint --e{715}*/
5139 SCIP_CONSHDLRDATA* conshdlrdata;
5140 SCIP_INTERVAL interval;
5141 SCIP_Real lb;
5142 SCIP_Real ub;
5143
5144 assert(scip != NULL);
5145 assert(var != NULL);
5146
5147 conshdlrdata = (SCIP_CONSHDLRDATA*)intevalvardata;
5148 assert(conshdlrdata != NULL);
5149
5150 if( conshdlrdata->globalbounds )
5151 {
5152 lb = SCIPvarGetLbGlobal(var);
5153 ub = SCIPvarGetUbGlobal(var);
5154 }
5155 else
5156 {
5157 lb = SCIPvarGetLbLocal(var);
5158 ub = SCIPvarGetUbLocal(var);
5159 }
5160 assert(lb <= ub); /* can SCIP ensure by now that variable bounds are not contradicting? */
5161
5162 /* relax variable bounds, if there are bounds and variable is not fixed
5163 * (actually some assert complains if trying SCIPisRelEQ if both bounds are at different infinity)
5164 */
5165 if( !(SCIPisInfinity(scip, -lb) && SCIPisInfinity(scip, ub)) && !SCIPisRelEQ(scip, lb, ub) )
5166 {
5167 if( !SCIPisInfinity(scip, -lb) )
5168 lb -= SCIPfeastol(scip);
5169
5170 if( !SCIPisInfinity(scip, ub) )
5171 ub += SCIPfeastol(scip);
5172 }
5173
5174 /* convert SCIPinfinity() to SCIP_INTERVAL_INFINITY */
5175 lb = -infty2infty(SCIPinfinity(scip), SCIP_INTERVAL_INFINITY, -lb);
5176 ub = infty2infty(SCIPinfinity(scip), SCIP_INTERVAL_INFINITY, ub);
5177 assert(lb <= ub);
5178
5179 SCIPintervalSetBounds(&interval, lb, ub);
5180
5181 return interval;
5182 }
5183
5184 /** removes constraints that are always feasible or very simple
5185 *
5186 * Checks whether the activity of constraint functions is a subset of the constraint sides (relaxed by feastol).
5187 * To compute the activity, we use forwardPropExpr(), but relax variable bounds by feastol, because solutions to be checked
5188 * might violate variable bounds by up to feastol, too.
5189 * This is the main reason why the redundancy check is not done in propConss(), which relaxes variable bounds by epsilon only.
5190 *
5191 * Also removes constraints of the form lhs ≤ variable ≤ rhs.
5192 *
5193 * @todo it would be sufficient to check constraints for which we know that they are not currently violated by a valid solution
5194 *
5195 * @note This could should not run during solving, because the forwardProp takes the bounds of auxiliary variables into account.
5196 * For the root expression, these bounds are already set to the constraint sides, so that the activity of every expression
5197 * would appear as if the constraint is redundant.
5198 */
5199 static
5200 SCIP_RETCODE presolveRedundantConss(
5201 SCIP* scip, /**< SCIP data structure */
5202 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
5203 SCIP_CONS** conss, /**< constraints to propagate */
5204 int nconss, /**< total number of constraints */
5205 SCIP_Bool* cutoff, /**< pointer to store whether infeasibility has been identified */
5206 int* ndelconss, /**< buffer to add the number of deleted constraints */
5207 int* nchgbds /**< buffer to add the number of variable bound tightenings */
5208 )
5209 {
5210 SCIP_CONSHDLRDATA* conshdlrdata;
5211 SCIP_CONSDATA* consdata;
5212 SCIP_INTERVAL activity;
5213 SCIP_INTERVAL sides;
5214 int i;
5215
5216 assert(scip != NULL);
5217 assert(conshdlr != NULL);
5218 assert(conss != NULL);
5219 assert(nconss >= 0);
5220 assert(cutoff != NULL);
5221 assert(ndelconss != NULL);
5222 assert(nchgbds != NULL);
5223
5224 /* no constraints to check */
5225 if( nconss == 0 )
5226 return SCIP_OKAY;
5227
5228 conshdlrdata = SCIPconshdlrGetData(conshdlr);
5229 assert(conshdlrdata != NULL);
5230
5231 /* increase curboundstag and set lastvaractivitymethodchange
5232 * we do this here to trigger a reevaluation of all variable bounds, since we will relax variable bounds
5233 * for the redundancy check differently than for domain propagation
5234 * we also update lastboundrelax to ensure activites of all expressions are indeed reevaluated
5235 */
5236 ++conshdlrdata->curboundstag;
5237 assert(conshdlrdata->curboundstag > 0);
5238 conshdlrdata->lastvaractivitymethodchange = conshdlrdata->curboundstag;
5239 conshdlrdata->lastboundrelax = conshdlrdata->curboundstag;
5240 conshdlrdata->intevalvar = intEvalVarRedundancyCheck;
5241
5242 SCIPdebugMsg(scip, "checking %d constraints for redundancy\n", nconss);
5243
5244 *cutoff = FALSE;
5245 for( i = 0; i < nconss; ++i )
5246 {
5247 if( !SCIPconsIsActive(conss[i]) || SCIPconsIsDeleted(conss[i]) )
5248 continue;
5249
5250 consdata = SCIPconsGetData(conss[i]);
5251 assert(consdata != NULL);
5252
5253 /* handle constant expressions separately: either the problem is infeasible or the constraint is redundant */
5254 if( SCIPisExprValue(scip, consdata->expr) )
5255 {
5256 SCIP_Real value = SCIPgetValueExprValue(consdata->expr);
5257
5258 if( (!SCIPisInfinity(scip, -consdata->lhs) && value < consdata->lhs - SCIPfeastol(scip)) ||
5259 (!SCIPisInfinity(scip, consdata->rhs) && value > consdata->rhs + SCIPfeastol(scip)) )
5260 {
5261 SCIPdebugMsg(scip, "constant constraint <%s> is infeasible: %g in [%g,%g] ", SCIPconsGetName(conss[i]), value, consdata->lhs, consdata->rhs);
5262 *cutoff = TRUE;
5263
5264 goto TERMINATE;
5265 }
5266
5267 SCIPdebugMsg(scip, "constant constraint <%s> is redundant: %g in [%g,%g] ", SCIPconsGetName(conss[i]), value, consdata->lhs, consdata->rhs);
5268
5269 SCIP_CALL( SCIPdelConsLocal(scip, conss[i]) );
5270 ++*ndelconss;
5271
5272 continue;
5273 }
5274
5275 /* handle variable expressions separately: tighten variable bounds to constraint sides, then remove constraint (now redundant) */
5276 if( SCIPisExprVar(scip, consdata->expr) )
5277 {
5278 SCIP_VAR* var;
5279 SCIP_Bool tightened;
5280
5281 var = SCIPgetVarExprVar(consdata->expr);
5282 assert(var != NULL);
5283
5284 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);
5285
5286 /* ensure that variable bounds are within constraint sides */
5287 if( !SCIPisInfinity(scip, -consdata->lhs) )
5288 {
5289 SCIP_CALL( SCIPtightenVarLb(scip, var, consdata->lhs, TRUE, cutoff, &tightened) );
5290
5291 if( tightened )
5292 ++*nchgbds;
5293
5294 if( *cutoff )
5295 goto TERMINATE;
5296 }
5297
5298 if( !SCIPisInfinity(scip, consdata->rhs) )
5299 {
5300 SCIP_CALL( SCIPtightenVarUb(scip, var, consdata->rhs, TRUE, cutoff, &tightened) );
5301
5302 if( tightened )
5303 ++*nchgbds;
5304
5305 if( *cutoff )
5306 goto TERMINATE;
5307 }
5308
5309 /* delete the (now) redundant constraint locally */
5310 SCIP_CALL( SCIPdelConsLocal(scip, conss[i]) );
5311 ++*ndelconss;
5312
5313 continue;
5314 }
5315
5316 /* reevaluate expression activity, now using intEvalVarRedundancyCheck
5317 * we relax variable bounds by feastol here, as solutions that are checked later can also violate
5318 * variable bounds by up to feastol
5319 * (relaxing fixed variables seems to be too much, but they would be removed by presolve soon anyway)
5320 */
5321 SCIPdebugMsg(scip, "call forwardPropExpr() for constraint <%s>: ", SCIPconsGetName(conss[i]));
5322 SCIPdebugPrintCons(scip, conss[i], NULL);
5323
5324 SCIP_CALL( forwardPropExpr(scip, conshdlr, consdata->expr, FALSE, cutoff, NULL) );
5325 assert(*cutoff || !SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(consdata->expr)));
5326
5327 /* it is unlikely that we detect infeasibility by doing forward propagation */
5328 if( *cutoff )
5329 {
5330 SCIPdebugMsg(scip, " -> cutoff\n");
5331 goto TERMINATE;
5332 }
5333
5334 assert(SCIPexprGetActivityTag(consdata->expr) == conshdlrdata->curboundstag);
5335 activity = SCIPexprGetActivity(consdata->expr);
5336
5337 /* relax sides by feastol
5338 * we could accept every solution that violates constraints up to feastol as redundant, so this is the most permissive we can be
5339 */
5340 SCIPintervalSetBounds(&sides,
5341 SCIPisInfinity(scip, -consdata->lhs) ? -SCIP_INTERVAL_INFINITY : consdata->lhs - SCIPfeastol(scip),
5342 SCIPisInfinity(scip, consdata->rhs) ? SCIP_INTERVAL_INFINITY : consdata->rhs + SCIPfeastol(scip));
5343
5344 if( SCIPintervalIsSubsetEQ(SCIP_INTERVAL_INFINITY, activity, sides) )
5345 {
5346 SCIPdebugMsg(scip, " -> redundant: activity [%g,%g] within sides [%g,%g]\n", activity.inf, activity.sup, consdata->lhs, consdata->rhs);
5347
5348 SCIP_CALL( SCIPdelConsLocal(scip, conss[i]) );
5349 ++*ndelconss;
5350
5351 continue;
5352 }
5353
5354 SCIPdebugMsg(scip, " -> not redundant: activity [%g,%g] not within sides [%g,%g]\n", activity.inf, activity.sup, consdata->lhs, consdata->rhs);
5355 }
5356
5357 TERMINATE:
5358 /* make sure all activities are reevaluated again, since we relaxed bounds in a different way */
5359 ++conshdlrdata->curboundstag;
5360 conshdlrdata->lastvaractivitymethodchange = conshdlrdata->curboundstag;
5361 conshdlrdata->lastboundrelax = conshdlrdata->curboundstag;
5362 conshdlrdata->intevalvar = intEvalVarBoundTightening;
5363
5364 return SCIP_OKAY;
5365 }
5366
5367 /** tries to automatically convert a nonlinear constraint into a more specific and more specialized constraint */
5368 static
5369 SCIP_RETCODE presolveUpgrade(
5370 SCIP* scip, /**< SCIP data structure */
5371 SCIP_CONSHDLR* conshdlr, /**< constraint handler data structure */
5372 SCIP_CONS* cons, /**< source constraint to try to convert */
5373 SCIP_Bool* upgraded, /**< buffer to store whether constraint was upgraded */
5374 int* nupgdconss, /**< buffer to increase if constraint was upgraded */
5375 int* naddconss /**< buffer to increase with number of additional constraints created during upgrade */
5376 )
5377 {
5378 SCIP_CONSHDLRDATA* conshdlrdata;
5379 SCIP_CONSDATA* consdata;
5380 SCIP_CONS** upgdconss;
5381 int upgdconsssize;
5382 int nupgdconss_;
5383 int i;
5384
5385 assert(scip != NULL);
5386 assert(conshdlr != NULL);
5387 assert(cons != NULL);
5388 assert(!SCIPconsIsModifiable(cons));
5389 assert(upgraded != NULL);
5390 assert(nupgdconss != NULL);
5391 assert(naddconss != NULL);
5392
5393 *upgraded = FALSE;
5394
5395 nupgdconss_ = 0;
5396
5397 conshdlrdata = SCIPconshdlrGetData(conshdlr);
5398 assert(conshdlrdata != NULL);
5399
5400 /* if there are no upgrade methods, we can stop */
5401 if( conshdlrdata->nconsupgrades == 0 )
5402 return SCIP_OKAY;
5403
5404 upgdconsssize = 2;
5405 SCIP_CALL( SCIPallocBufferArray(scip, &upgdconss, upgdconsssize) );
5406
5407 /* call the upgrading methods */
5408 SCIPdebugMsg(scip, "upgrading nonlinear constraint <%s> (up to %d upgrade methods): ", SCIPconsGetName(cons), conshdlrdata->nconsupgrades);
5409 SCIPdebugPrintCons(scip, cons, NULL);
5410
5411 consdata = SCIPconsGetData(cons);
5412 assert(consdata != NULL);
5413
5414 /* try all upgrading methods in priority order in case the upgrading step is enable */
5415 for( i = 0; i < conshdlrdata->nconsupgrades; ++i )
5416 {
5417 if( !conshdlrdata->consupgrades[i]->active )
5418 continue;
5419
5420 assert(conshdlrdata->consupgrades[i]->consupgd != NULL);
5421
5422 SCIP_CALL( conshdlrdata->consupgrades[i]->consupgd(scip, cons, consdata->nvarexprs, &nupgdconss_, upgdconss, upgdconsssize) );
5423
5424 while( nupgdconss_ < 0 )
5425 {
5426 /* upgrade function requires more memory: resize upgdconss and call again */
5427 assert(-nupgdconss_ > upgdconsssize);
5428 upgdconsssize = -nupgdconss_;
5429 SCIP_CALL( SCIPreallocBufferArray(scip, &upgdconss, -nupgdconss_) );
5430
5431 SCIP_CALL( conshdlrdata->consupgrades[i]->consupgd(scip, cons, consdata->nvarexprs, &nupgdconss_, upgdconss, upgdconsssize) );
5432
5433 assert(nupgdconss_ != 0);
5434 }
5435
5436 if( nupgdconss_ > 0 )
5437 {
5438 /* got upgrade */
5439 int j;
5440
5441 SCIPdebugMsg(scip, " -> upgraded to %d constraints:\n", nupgdconss_);
5442
5443 /* add the upgraded constraints to the problem and forget them */
5444 for( j = 0; j < nupgdconss_; ++j )
5445 {
5446 SCIPdebugMsgPrint(scip, "\t");
5447 SCIPdebugPrintCons(scip, upgdconss[j], NULL);
5448
5449 SCIP_CALL( SCIPaddCons(scip, upgdconss[j]) ); /*lint !e613*/
5450 SCIP_CALL( SCIPreleaseCons(scip, &upgdconss[j]) ); /*lint !e613*/
5451 }
5452
5453 /* count the first upgrade constraint as constraint upgrade and the remaining ones as added constraints */
5454 *nupgdconss += 1;
5455 *naddconss += nupgdconss_ - 1;
5456 *upgraded = TRUE;
5457
5458 /* delete upgraded constraint */
5459 SCIPdebugMsg(scip, "delete constraint <%s> after upgrade\n", SCIPconsGetName(cons));
5460 SCIP_CALL( SCIPdelCons(scip, cons) );
5461
5462 break;
5463 }
5464 }
5465
5466 SCIPfreeBufferArray(scip, &upgdconss);
5467
5468 return SCIP_OKAY;
5469 }
5470
5471 /** returns whether the variable of a given variable expression is a candidate for presolveSingleLockedVars(), i.e.,
5472 * the variable is only contained in a single nonlinear constraint, has no objective coefficient, has finite
5473 * variable bounds, and is not binary
5474 */
5475 static
5476 SCIP_Bool isSingleLockedCand(
5477 SCIP* scip, /**< SCIP data structure */
5478 SCIP_EXPR* expr /**< variable expression */
5479 )
5480 {
5481 SCIP_VAR* var;
5482 SCIP_EXPR_OWNERDATA* ownerdata;
5483
5484 assert(SCIPisExprVar(scip, expr));
5485
5486 var = SCIPgetVarExprVar(expr);
5487 assert(var != NULL);
5488
5489 ownerdata = SCIPexprGetOwnerData(expr);
5490 assert(ownerdata != NULL);
5491
5492 return SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL) == ownerdata->nlocksneg
5493 && SCIPvarGetNLocksUpType(var, SCIP_LOCKTYPE_MODEL) == ownerdata->nlockspos
5494 && ownerdata->nconss == 1 && SCIPisZero(scip, SCIPvarGetObj(var))
5495 && !SCIPisInfinity(scip, -SCIPvarGetLbGlobal(var)) && !SCIPisInfinity(scip, SCIPvarGetUbGlobal(var))
5496 && SCIPvarGetType(var) != SCIP_VARTYPE_BINARY
5497 && !SCIPisEQ(scip, SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var));
5498 }
5499
5500 /** removes all variable expressions that are contained in a given expression from a hash map */
5501 static
5502 SCIP_RETCODE removeSingleLockedVars(
5503 SCIP* scip, /**< SCIP data structure */
5504 SCIP_EXPR* expr, /**< expression */
5505 SCIP_EXPRITER* it, /**< expression iterator */
5506 SCIP_HASHMAP* exprcands /**< map to hash variable expressions */
5507 )
5508 {
5509 SCIP_EXPR* e;
5510
5511 for( e = SCIPexpriterRestartDFS(it, expr); !SCIPexpriterIsEnd(it); e = SCIPexpriterGetNext(it) )
5512 {
5513 if( SCIPisExprVar(scip, e) && SCIPhashmapExists(exprcands, (void*)e) )
5514 {
5515 SCIP_CALL( SCIPhashmapRemove(exprcands, (void*)e) );
5516 }
5517 }
5518
5519 return SCIP_OKAY;
5520 }
5521
5522 /** presolving method to fix a variable \f$x_i\f$ to one of its bounds if the variable is only contained in a single
5523 * nonlinear constraint g(x) ≤ rhs (≥ lhs) if g() is concave (convex) in \f$x_i\f$
5524 *
5525 * If a continuous variable has bounds [0,1], then the variable type is changed to be binary.
5526 * Otherwise, a bound disjunction constraint is added.
5527 *
5528 * @todo the same reduction can be applied if g(x) is not concave, but monotone in \f$x_i\f$ for g(x) ≤ rhs
5529 * @todo extend this to cases where a variable can appear in a monomial with an exponent, essentially relax
5530 * 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
5531 * on this (probably have one or two flags per variable and update this whenever another \f$x^{p_i}\f$ is found)
5532 */
5533 static
5534 SCIP_RETCODE presolveSingleLockedVars(
5535 SCIP* scip, /**< SCIP data structure */
5536 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */
5537 SCIP_CONS* cons, /**< nonlinear constraint */
5538 int* nchgvartypes, /**< pointer to store the total number of changed variable types */
5539 int* naddconss, /**< pointer to store the total number of added constraints */
5540 SCIP_Bool* infeasible /**< pointer to store whether problem is infeasible */
5541 )
5542 {
5543 SCIP_CONSHDLRDATA* conshdlrdata;
5544 SCIP_CONSDATA* consdata;
5545 SCIP_EXPR** singlelocked;
5546 SCIP_HASHMAP* exprcands;
5547 SCIP_Bool hasbounddisj;
5548 SCIP_Bool haslhs;
5549 SCIP_Bool hasrhs;
5550 int nsinglelocked = 0;
5551 int i;
5552
5553 assert(conshdlr != NULL);
5554 assert(cons != NULL);
5555 assert(nchgvartypes != NULL);
5556 assert(naddconss != NULL);
5557 assert(infeasible != NULL);
5558
5559 *nchgvartypes = 0;
5560 *naddconss = 0;
5561 *infeasible = FALSE;
5562
5563 conshdlrdata = SCIPconshdlrGetData(conshdlr);
5564 assert(conshdlrdata != NULL);
5565 consdata = SCIPconsGetData(cons);
5566 assert(consdata != NULL);
5567
5568 /* only consider constraints with one finite side */
5569 if( !SCIPisInfinity(scip, -consdata->lhs) && !SCIPisInfinity(scip, consdata->rhs) )
5570 return SCIP_OKAY;
5571
5572 /* only consider sum expressions */
5573 if( !SCIPisExprSum(scip, consdata->expr) )
5574 return SCIP_OKAY;
5575
5576 /* remember which side is finite */
5577 haslhs = !SCIPisInfinity(scip, -consdata->lhs);
5578 hasrhs = !SCIPisInfinity(scip, consdata->rhs);
5579
5580 /* allocate memory */
5581 SCIP_CALL( SCIPhashmapCreate(&exprcands, SCIPblkmem(scip), consdata->nvarexprs) );
5582 SCIP_CALL( SCIPallocBufferArray(scip, &singlelocked, consdata->nvarexprs) );
5583
5584 /* check all variable expressions for single locked variables */
5585 for( i = 0; i < consdata->nvarexprs; ++i )
5586 {
5587 assert(consdata->varexprs[i] != NULL);
5588
5589 if( isSingleLockedCand(scip, consdata->varexprs[i]) )
5590 {
5591 SCIP_CALL( SCIPhashmapInsert(exprcands, (void*)consdata->varexprs[i], NULL) );
5592 singlelocked[nsinglelocked++] = consdata->varexprs[i];
5593 }
5594 }
5595 SCIPdebugMsg(scip, "found %d single locked variables for constraint %s\n", nsinglelocked, SCIPconsGetName(cons));
5596
5597 if( nsinglelocked > 0 )
5598 {
5599 SCIP_EXPR** children;
5600 SCIP_EXPRITER* it;
5601 int nchildren;
5602
5603 children = SCIPexprGetChildren(consdata->expr);
5604 nchildren = SCIPexprGetNChildren(consdata->expr);
5605
5606 /* create iterator */
5607 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
5608 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) );
5609 SCIPexpriterSetStagesDFS(it, SCIP_EXPRITER_ENTEREXPR);
5610
5611 for( i = 0; i < nchildren; ++i )
5612 {
5613 SCIP_EXPR* child;
5614 SCIP_Real coef;
5615
5616 child = children[i];
5617 assert(child != NULL);
5618 coef = SCIPgetCoefsExprSum(consdata->expr)[i];
5619
5620 /* ignore linear terms */
5621 if( SCIPisExprVar(scip, child) )
5622 continue;
5623
5624 /* consider products prod_j f_j(x); ignore f_j(x) if it is a single variable, otherwise iterate through the
5625 * expression that represents f_j and remove each variable expression from exprcands
5626 */
5627 else if( SCIPisExprProduct(scip, child) )
5628 {
5629 int j;
5630
5631 for( j = 0; j < SCIPexprGetNChildren(child); ++j )
5632 {
5633 SCIP_EXPR* grandchild = SCIPexprGetChildren(child)[j];
5634
5635 if( !SCIPisExprVar(scip, grandchild) )
5636 {
5637 /* mark all variable expressions that are contained in the expression */
5638 SCIP_CALL( removeSingleLockedVars(scip, grandchild, it, exprcands) );
5639 }
5640 }
5641 }
5642 /* fixing a variable x to one of its bounds is only valid for ... +x^p >= lhs or ... -x^p <= rhs if p = 2k
5643 * for an integer k >= 1
5644 */
5645 else if( SCIPisExprPower(scip, child) )
5646 {
5647 SCIP_EXPR* grandchild = SCIPexprGetChildren(child)[0];
5648 SCIP_Real exponent = SCIPgetExponentExprPow(child);
5649 SCIP_Bool valid;
5650
5651 /* check for even integral exponent */
5652 valid = exponent > 1.0 && fmod(exponent, 2.0) == 0.0;
5653
5654 if( !valid || !SCIPisExprVar(scip, grandchild) || (hasrhs && coef > 0.0) || (haslhs && coef < 0.0) )
5655 {
5656 /* mark all variable expressions that are contained in the expression */
5657 SCIP_CALL( removeSingleLockedVars(scip, grandchild, it, exprcands) );
5658 }
5659 }
5660 /* all other cases cannot be handled */
5661 else
5662 {
5663 /* mark all variable expressions that are contained in the expression */
5664 SCIP_CALL( removeSingleLockedVars(scip, child, it, exprcands) );
5665 }
5666 }
5667
5668 /* free expression iterator */
5669 SCIPfreeExpriter(&it);
5670 }
5671
5672 /* check whether the bound disjunction constraint handler is available */
5673 hasbounddisj = SCIPfindConshdlr(scip, "bounddisjunction") != NULL;
5674
5675 /* fix variable to one of its bounds by either changing its variable type or adding a disjunction constraint */
5676 for( i = 0; i < nsinglelocked; ++i )
5677 {
5678 /* only consider expressions that are still contained in the exprcands map */
5679 if( SCIPhashmapExists(exprcands, (void*)singlelocked[i]) )
5680 {
5681 SCIP_CONS* newcons;
5682 SCIP_VAR* vars[2];
5683 SCIP_BOUNDTYPE boundtypes[2];
5684 SCIP_Real bounds[2];
5685 char name[SCIP_MAXSTRLEN];
5686 SCIP_VAR* var;
5687
5688 var = SCIPgetVarExprVar(singlelocked[i]);
5689 assert(var != NULL);
5690 SCIPdebugMsg(scip, "found single locked variable %s in [%g,%g] that can be fixed to one of its bounds\n",
5691 SCIPvarGetName(var), SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var));
5692
5693 /* try to change the variable type to binary */
5694 if( conshdlrdata->checkvarlocks == 't' && SCIPisEQ(scip, SCIPvarGetLbGlobal(var), 0.0) && SCIPisEQ(scip, SCIPvarGetUbGlobal(var), 1.0) )
5695 {
5696 assert(SCIPvarGetType(var) != SCIP_VARTYPE_BINARY);
5697 SCIP_CALL( SCIPchgVarType(scip, var, SCIP_VARTYPE_BINARY, infeasible) );
5698 ++(*nchgvartypes);
5699
5700 if( *infeasible )
5701 {
5702 SCIPdebugMsg(scip, "detect infeasibility after changing variable type of <%s>\n", SCIPvarGetName(var));
5703 break;
5704 }
5705 }
5706 /* add bound disjunction constraint if bounds of the variable are finite */
5707 else if( hasbounddisj && !SCIPisInfinity(scip, -SCIPvarGetLbGlobal(var)) && !SCIPisInfinity(scip, SCIPvarGetUbGlobal(var)) )
5708 {
5709 vars[0] = var;
5710 vars[1] = var;
5711 boundtypes[0] = SCIP_BOUNDTYPE_LOWER;
5712 boundtypes[1] = SCIP_BOUNDTYPE_UPPER;
5713 bounds[0] = SCIPvarGetUbGlobal(var);
5714 bounds[1] = SCIPvarGetLbGlobal(var);
5715
5716 SCIPdebugMsg(scip, "add bound disjunction constraint for %s\n", SCIPvarGetName(var));
5717
5718 /* create, add, and release bound disjunction constraint */
5719 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "quadvarbnddisj_%s", SCIPvarGetName(var));
5720 SCIP_CALL( SCIPcreateConsBounddisjunction(scip, &newcons, name, 2, vars, boundtypes, bounds, TRUE, TRUE,
5721 TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
5722 SCIP_CALL( SCIPaddCons(scip, newcons) );
5723 SCIP_CALL( SCIPreleaseCons(scip, &newcons) );
5724 ++(*naddconss);
5725 }
5726 }
5727 }
5728
5729 /* free memory */
5730 SCIPfreeBufferArray(scip, &singlelocked);
5731 SCIPhashmapFree(&exprcands);
5732
5733 return SCIP_OKAY;
5734 }
5735
5736 /** presolving method to check if there is a single linear continuous variable that can be made implicit integer */
5737 static
5738 SCIP_RETCODE presolveImplint(
5739 SCIP* scip, /**< SCIP data structure */
5740 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
5741 SCIP_CONS** conss, /**< nonlinear constraints */
5742 int nconss, /**< total number of nonlinear constraints */
5743 int* nchgvartypes, /**< pointer to update the total number of changed variable types */
5744 SCIP_Bool* infeasible /**< pointer to store whether problem is infeasible */
5745 )
5746 {
5747 int c;
5748
5749 assert(scip != NULL);
5750 assert(conshdlr != NULL);
5751 assert(conss != NULL || nconss == 0);
5752 assert(nchgvartypes != NULL);
5753 assert(infeasible != NULL);
5754
5755 *infeasible = FALSE;
5756
5757 /* nothing can be done if there are no binary and integer variables available */
5758 if( SCIPgetNBinVars(scip) == 0 && SCIPgetNIntVars(scip) == 0 )
5759 return SCIP_OKAY;
5760
5761 /* no continuous var can be made implicit-integer if there are no continuous variables */
5762 if( SCIPgetNContVars(scip) == 0 )
5763 return SCIP_OKAY;
5764
5765 for( c = 0; c < nconss; ++c )
5766 {
5767 SCIP_CONSDATA* consdata;
5768 SCIP_EXPR** children;
5769 int nchildren;
5770 SCIP_Real* coefs;
5771 SCIP_EXPR* cand = NULL;
5772 SCIP_Real candcoef = 0.0;
5773 int i;
5774
5775 assert(conss != NULL && conss[c] != NULL);
5776
5777 consdata = SCIPconsGetData(conss[c]);
5778 assert(consdata != NULL);
5779
5780 /* the constraint must be an equality constraint */
5781 if( !SCIPisEQ(scip, consdata->lhs, consdata->rhs) )
5782 continue;
5783
5784 /* the root expression needs to be a sum expression */
5785 if( !SCIPisExprSum(scip, consdata->expr) )
5786 continue;
5787
5788 children = SCIPexprGetChildren(consdata->expr);
5789 nchildren = SCIPexprGetNChildren(consdata->expr);
5790
5791 /* the sum expression must have at least two children
5792 * (with one child, we would look for a coef*x = constant, which is presolved away anyway)
5793 */
5794 if( nchildren <= 1 )
5795 continue;
5796
5797 coefs = SCIPgetCoefsExprSum(consdata->expr);
5798
5799 /* find first continuous variable and get value of its coefficient */
5800 for( i = 0; i < nchildren; ++i )
5801 {
5802 if( !SCIPisExprVar(scip, children[i]) || SCIPvarIsIntegral(SCIPgetVarExprVar(children[i])) )
5803 continue;
5804
5805 candcoef = coefs[i];
5806 assert(candcoef != 0.0);
5807
5808 /* lhs/rhs - constant divided by candcoef must be integral
5809 * if not, break with cand == NULL, so give up
5810 */
5811 if( SCIPisIntegral(scip, (consdata->lhs - SCIPgetConstantExprSum(consdata->expr)) / candcoef) )
5812 cand = children[i];
5813
5814 break;
5815 }
5816
5817 /* no suitable continuous variable found */
5818 if( cand == NULL )
5819 continue;
5820
5821 /* check whether all other coefficients are integral when diving by candcoef and all other children are integral */
5822 for( i = 0; i < nchildren; ++i )
5823 {
5824 if( children[i] == cand )
5825 continue;
5826
5827 /* child i must be integral */
5828 if( !SCIPexprIsIntegral(children[i]) )
5829 {
5830 cand = NULL;
5831 break;
5832 }
5833
5834 /* coefficient of child i must be integral if diving by candcoef */
5835 if( !SCIPisIntegral(scip, coefs[i] / candcoef) ) /*lint !e414*/
5836 {
5837 cand = NULL;
5838 break;
5839 }
5840 }
5841
5842 if( cand == NULL )
5843 continue;
5844
5845 SCIPdebugMsg(scip, "make variable <%s> implicit integer due to constraint <%s>\n",
5846 SCIPvarGetName(SCIPgetVarExprVar(cand)), SCIPconsGetName(conss[c]));
5847
5848 /* change variable type */
5849 SCIP_CALL( SCIPchgVarType(scip, SCIPgetVarExprVar(cand), SCIP_VARTYPE_IMPLINT, infeasible) );
5850
5851 if( *infeasible )
5852 return SCIP_OKAY;
5853
5854 /* mark expression as being integral (as would be done by expr_var.c in the next round of updating integrality info) */
5855 SCIPexprSetIntegrality(cand, TRUE);
5856 }
5857
5858 return SCIP_OKAY;
5859 }
5860
5861 /** creates auxiliary variable for a given expression
5862 *
5863 * @note for a variable expression it does nothing
5864 * @note this function can only be called in stage SCIP_STAGE_SOLVING
5865 */
5866 static
5867 SCIP_RETCODE createAuxVar(
5868 SCIP* scip, /**< SCIP data structure */
5869 SCIP_EXPR* expr /**< expression */
5870 )
5871 {
5872 SCIP_EXPR_OWNERDATA* ownerdata;
5873 SCIP_CONSHDLRDATA* conshdlrdata;
5874 SCIP_VARTYPE vartype;
5875 SCIP_INTERVAL activity;
5876 char name[SCIP_MAXSTRLEN];
5877
5878 assert(scip != NULL);
5879 assert(expr != NULL);
5880
5881 ownerdata = SCIPexprGetOwnerData(expr);
5882 assert(ownerdata != NULL);
5883 assert(ownerdata->nauxvaruses > 0);
5884
5885 /* if we already have auxvar, then do nothing */
5886 if( ownerdata->auxvar != NULL )
5887 return SCIP_OKAY;
5888
5889 /* if expression is a variable-expression, then do nothing */
5890 if( SCIPisExprVar(scip, expr) )
5891 return SCIP_OKAY;
5892
5893 if( SCIPgetStage(scip) != SCIP_STAGE_SOLVING )
5894 {
5895 SCIPerrorMessage("it is not possible to create auxiliary variables during stage=%d\n", SCIPgetStage(scip));
5896 return SCIP_INVALIDCALL;
5897 }
5898
5899 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr);
5900 assert(conshdlrdata != NULL);
5901 assert(conshdlrdata->auxvarid >= 0);
5902
5903 /* it doesn't harm much to have an auxvar for a constant, as this can be handled well by the default hdlr,
5904 * but it usually indicates a missing simplify
5905 * if we find situations where we need to have an auxvar for a constant, then remove this assert
5906 */
5907 assert(!SCIPisExprValue(scip, expr));
5908
5909 /* create and capture auxiliary variable */
5910 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "auxvar_%s_%d", SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)), conshdlrdata->auxvarid);
5911 ++conshdlrdata->auxvarid;
5912
5913 /* type of auxiliary variable depends on integrality information of the expression */
5914 vartype = SCIPexprIsIntegral(expr) ? SCIP_VARTYPE_IMPLINT : SCIP_VARTYPE_CONTINUOUS;
5915
5916 /* get activity of expression to initialize variable bounds, if something valid is available (evalActivity was called in initSepa) */
5917 if( SCIPexprGetActivityTag(expr) >= conshdlrdata->lastboundrelax )
5918 {
5919 activity = SCIPexprGetActivity(expr);
5920 /* we cannot handle a domain error here at the moment, but it seems unlikely that it could occur
5921 * if it appear, then we could change code to handle this properly, but for now we just ensure that we continue correctly
5922 * and abort in debug mode only
5923 */
5924 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, activity) )
5925 {
5926 SCIPABORT();
5927 SCIPintervalSetEntire(SCIP_INTERVAL_INFINITY, &activity);
5928 }
5929 }
5930 else
5931 SCIPintervalSetEntire(SCIP_INTERVAL_INFINITY, &activity);
5932
5933 /* if root node, then activity is globally valid, so use it to initialize the global bounds of the auxvar
5934 * otherwise, we create var without bounds here and use activity to set local bounds below (needs to be after adding var)
5935 */
5936 if( SCIPgetDepth(scip) == 0 )
5937 {
5938 SCIP_CALL( SCIPcreateVarBasic(scip, &ownerdata->auxvar, name, MAX(-SCIPinfinity(scip), activity.inf), MIN(SCIPinfinity(scip), activity.sup), 0.0, vartype) );
5939 }
5940 else
5941 {
5942 SCIP_CALL( SCIPcreateVarBasic(scip, &ownerdata->auxvar, name, -SCIPinfinity(scip), SCIPinfinity(scip), 0.0, vartype) );
5943 }
5944
5945 /* mark the auxiliary variable to be added for the relaxation only
5946 * this prevents SCIP to create linear constraints from cuts or conflicts that contain auxiliary variables,
5947 * or to copy the variable to a subscip
5948 */
5949 SCIPvarMarkRelaxationOnly(ownerdata->auxvar);
5950
5951 SCIP_CALL( SCIPaddVar(scip, ownerdata->auxvar) );
5952
5953 SCIPdebugMsg(scip, "added auxiliary variable <%s> [%g,%g] for expression %p\n", SCIPvarGetName(ownerdata->auxvar), SCIPvarGetLbGlobal(ownerdata->auxvar), SCIPvarGetUbGlobal(ownerdata->auxvar), (void*)expr);
5954
5955 /* add variable locks in both directions
5956 * TODO should be sufficient to lock only according to expr->nlockspos/neg,
5957 * but then we need to also update the auxvars locks when the expr locks change
5958 */
5959 SCIP_CALL( SCIPaddVarLocks(scip, ownerdata->auxvar, 1, 1) );
5960
5961 #ifdef WITH_DEBUG_SOLUTION
5962 if( SCIPdebugIsMainscip(scip) )
5963 {
5964 /* store debug solution value of auxiliary variable
5965 * assumes that expression has been evaluated in debug solution before
5966 */
5967 SCIP_CALL( SCIPdebugAddSolVal(scip, ownerdata->auxvar, SCIPexprGetEvalValue(expr)) );
5968 }
5969 #endif
5970
5971 if( SCIPgetDepth(scip) > 0 )
5972 {
5973 /* initialize local bounds to (locally valid) activity */
5974 SCIP_Bool cutoff;
5975 SCIP_CALL( tightenAuxVarBounds(scip, ownerdata->conshdlr, expr, activity, &cutoff, NULL) );
5976 assert(!cutoff); /* should not happen as activity wasn't empty and variable is new */
5977 }
5978
5979 return SCIP_OKAY;
5980 }
5981
5982 /** initializes separation for constraint
5983 *
5984 * - ensures that activities are up to date in all expressions
5985 * - creates auxiliary variables where required
5986 * - calls propExprDomains() to possibly tighten auxvar bounds
5987 * - calls separation initialization callback of nlhdlrs
5988 */
5989 static
5990 SCIP_RETCODE initSepa(
5991 SCIP* scip, /**< SCIP data structure */
5992 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraints handler */
5993 SCIP_CONS** conss, /**< constraints */
5994 int nconss, /**< number of constraints */
5995 SCIP_Bool* infeasible /**< pointer to store whether the problem is infeasible or not */
5996 )
5997 {
5998 SCIP_CONSDATA* consdata;
5999 SCIP_CONSHDLRDATA* conshdlrdata;
6000 SCIP_EXPRITER* it;
6001 SCIP_EXPR* expr;
6002 SCIP_RESULT result;
6003 SCIP_VAR* auxvar;
6004 int nreductions = 0;
6005 int c, e;
6006
6007 assert(scip != NULL);
6008 assert(conshdlr != NULL);
6009 assert(conss != NULL || nconss == 0);
6010 assert(nconss >= 0);
6011 assert(infeasible != NULL);
6012
6013 conshdlrdata = SCIPconshdlrGetData(conshdlr);
6014 assert(conshdlrdata != NULL);
6015
6016 /* start with new propbounds (just to be sure, should not be needed) */
6017 ++conshdlrdata->curpropboundstag;
6018
6019 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
6020 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) );
6021
6022 /* first ensure activities are up to date and create auxvars */
6023 *infeasible = FALSE;
6024 for( c = 0; c < nconss; ++c )
6025 {
6026 assert(conss != NULL);
6027 assert(conss[c] != NULL);
6028
6029 consdata = SCIPconsGetData(conss[c]);
6030 assert(consdata != NULL);
6031 assert(consdata->expr != NULL);
6032
6033 #ifdef WITH_DEBUG_SOLUTION
6034 if( SCIPdebugIsMainscip(scip) )
6035 {
6036 SCIP_SOL* debugsol;
6037
6038 SCIP_CALL( SCIPdebugGetSol(scip, &debugsol) );
6039
6040 if( debugsol != NULL ) /* it can be compiled WITH_DEBUG_SOLUTION, but still no solution given */
6041 {
6042 /* evaluate expression in debug solution, so we can set the solution value of created auxiliary variables
6043 * in createAuxVar()
6044 */
6045 SCIP_CALL( SCIPevalExpr(scip, consdata->expr, debugsol, 0) );
6046 }
6047 }
6048 #endif
6049
6050 /* ensure we have a valid activity for auxvars and propExprDomains() call below */
6051 SCIP_CALL( SCIPevalExprActivity(scip, consdata->expr) );
6052
6053 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
6054 {
6055 if( SCIPexprGetOwnerData(expr)->nauxvaruses > 0 )
6056 {
6057 SCIP_CALL( createAuxVar(scip, expr) );
6058 }
6059 }
6060
6061 auxvar = SCIPexprGetOwnerData(consdata->expr)->auxvar;
6062 if( auxvar != NULL )
6063 {
6064 SCIPdebugMsg(scip, "tighten auxvar <%s> bounds using constraint sides [%g,%g]\n",
6065 SCIPvarGetName(auxvar), consdata->lhs, consdata->rhs);
6066 /* change the bounds of the auxiliary variable of the root node to [lhs,rhs] */
6067 SCIP_CALL( SCIPtightenVarLb(scip, auxvar, consdata->lhs, TRUE, infeasible, NULL) );
6068 if( *infeasible )
6069 {
6070 SCIPdebugMsg(scip, "infeasibility detected while tightening auxvar lb (%g) using lhs of constraint (%g)\n", SCIPvarGetLbLocal(auxvar), consdata->lhs);
6071 break;
6072 }
6073
6074 SCIP_CALL( SCIPtightenVarUb(scip, auxvar, consdata->rhs, TRUE, infeasible, NULL) );
6075 if( *infeasible )
6076 {
6077 SCIPdebugMsg(scip, "infeasibility detected while tightening auxvar ub (%g) using rhs of constraint (%g)\n", SCIPvarGetUbLocal(auxvar), consdata->rhs);
6078 break;
6079 }
6080 }
6081 }
6082
6083 /* now run a special version of reverseprop to ensure that important bound information (like function domains) is stored in bounds of auxvars,
6084 * since sometimes they cannot be recovered from activity evaluation even after some rounds of domain propagation
6085 * (e.g., log(x*y), which becomes log(w), w=x*y
6086 * log(w) implies w >= 0, but we may not be able to derive bounds on x and y such that w >= 0 is ensured)
6087 */
6088 SCIP_CALL( propExprDomains(scip, conshdlr, conss, nconss, &result, &nreductions) );
6089 if( result == SCIP_CUTOFF )
6090 *infeasible = TRUE;
6091
6092 /* now call initsepa of nlhdlrs
6093 * TODO skip if !SCIPconsIsInitial(conss[c]) ?
6094 * but at the moment, initSepa() is called from INITLP anyway, so we have SCIPconsIsInitial(conss[c]) anyway
6095 */
6096 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) );
6097 for( c = 0; c < nconss && !*infeasible; ++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 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it) && !*infeasible; expr = SCIPexpriterGetNext(it) )
6107 {
6108 SCIP_EXPR_OWNERDATA* ownerdata;
6109
6110 ownerdata = SCIPexprGetOwnerData(expr);
6111 assert(ownerdata != NULL);
6112
6113 if( ownerdata->nauxvaruses == 0 )
6114 continue;
6115
6116 for( e = 0; e < ownerdata->nenfos; ++e )
6117 {
6118 SCIP_NLHDLR* nlhdlr;
6119 SCIP_Bool underestimate;
6120 SCIP_Bool overestimate;
6121 assert(ownerdata->enfos[e] != NULL);
6122
6123 /* skip if initsepa was already called, e.g., because this expression is also part of a constraint
6124 * which participated in a previous initSepa() call
6125 */
6126 if( ownerdata->enfos[e]->issepainit )
6127 continue;
6128
6129 /* only call initsepa if it will actually separate */
6130 if( (ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPABOTH) == 0 )
6131 continue;
6132
6133 nlhdlr = ownerdata->enfos[e]->nlhdlr;
6134 assert(nlhdlr != NULL);
6135
6136 /* only init sepa if there is an initsepa callback */
6137 if( !SCIPnlhdlrHasInitSepa(nlhdlr) )
6138 continue;
6139
6140 /* check whether expression needs to be under- or overestimated */
6141 overestimate = ownerdata->nlocksneg > 0;
6142 underestimate = ownerdata->nlockspos > 0;
6143 assert(underestimate || overestimate);
6144
6145 SCIPdebugMsg(scip, "initsepa under=%u over=%u for expression %p\n", underestimate, overestimate, (void*)expr);
6146
6147 /* call the separation initialization callback of the nonlinear handler */
6148 SCIP_CALL( SCIPnlhdlrInitsepa(scip, conshdlr, conss[c], nlhdlr, expr,
6149 ownerdata->enfos[e]->nlhdlrexprdata, overestimate, underestimate, infeasible) );
6150 ownerdata->enfos[e]->issepainit = TRUE;
6151
6152 if( *infeasible )
6153 {
6154 /* stop everything if we detected infeasibility */
6155 SCIPdebugMsg(scip, "detect infeasibility for constraint %s during initsepa()\n", SCIPconsGetName(conss[c]));
6156 break;
6157 }
6158 }
6159 }
6160 }
6161
6162 SCIPfreeExpriter(&it);
6163
6164 return SCIP_OKAY;
6165 }
6166
6167 /** returns whether we are ok to branch on auxiliary variables
6168 *
6169 * Currently returns whether depth of node in B&B tree is at least value of constraints/nonlinear/branching/aux parameter.
6170 */
6171 static
6172 SCIP_Bool branchAuxNonlinear(
6173 SCIP* scip, /**< SCIP data structure */
6174 SCIP_CONSHDLR* conshdlr /**< constraint handler */
6175 )
6176 {
6177 SCIP_CONSHDLRDATA* conshdlrdata;
6178
6179 assert(conshdlr != NULL);
6180
6181 conshdlrdata = SCIPconshdlrGetData(conshdlr);
6182 assert(conshdlrdata != NULL);
6183
6184 return conshdlrdata->branchauxmindepth <= SCIPgetDepth(scip);
6185 }
6186
6187 /** gets weight of variable when splitting violation score onto several variables in an expression */
6188 static
6189 SCIP_Real getViolSplitWeight(
6190 SCIP* scip, /**< SCIP data structure */
6191 SCIP_CONSHDLR* conshdlr, /**< expr constraint handler */
6192 SCIP_VAR* var, /**< variable */
6193 SCIP_SOL* sol /**< current solution */
6194 )
6195 {
6196 SCIP_CONSHDLRDATA* conshdlrdata;
6197
6198 conshdlrdata = SCIPconshdlrGetData(conshdlr);
6199 assert(conshdlrdata != NULL);
6200
6201 switch( conshdlrdata->branchviolsplit )
6202 {
6203 case 'u' : /* uniform: everyone gets the same score */
6204 return 1.0;
6205
6206 case 'm' : /* midness of solution: 0.5 if in middle of domain, 0.05 if close to lower or upper bound */
6207 {
6208 SCIP_Real weight;
6209 weight = MIN(SCIPgetSolVal(scip, sol, var) - SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var) - SCIPgetSolVal(scip, sol, var)) / (SCIPvarGetUbLocal(var) - SCIPvarGetLbLocal(var));
6210 return MAX(0.05, weight);
6211 }
6212
6213 case 'd' : /* domain width */
6214 return SCIPvarGetUbLocal(var) - SCIPvarGetLbLocal(var);
6215
6216 case 'l' : /* logarithmic domain width: log-scale if width is below 0.1 or above 10, otherwise actual width */
6217 {
6218 SCIP_Real width = SCIPvarGetUbLocal(var) - SCIPvarGetLbLocal(var);
6219 assert(width > 0.0);
6220 if( width > 10.0 )
6221 return 10.0*log10(width);
6222 if( width < 0.1 )
6223 return 0.1/(-log10(width));
6224 return width;
6225 }
6226
6227 default :
6228 SCIPerrorMessage("invalid value for parameter constraints/expr/branching/violsplit");
6229 SCIPABORT();
6230 return SCIP_INVALID;
6231 }
6232 }
6233
6234 /** adds violation-branching score to a set of expressions, thereby distributing the score
6235 *
6236 * Each expression must either be a variable expression or have an aux-variable.
6237 *
6238 * If unbounded variables are present, each unbounded var gets an even score.
6239 * If no unbounded variables, then parameter constraints/nonlinear/branching/violsplit decides weight for each var.
6240 */
6241 static
6242 void addExprsViolScore(
6243 SCIP* scip, /**< SCIP data structure */
6244 SCIP_EXPR** exprs, /**< expressions where to add branching score */
6245 int nexprs, /**< number of expressions */
6246 SCIP_Real violscore, /**< violation-branching score to add to expression */
6247 SCIP_SOL* sol, /**< current solution */
6248 SCIP_Bool* success /**< buffer to store whether at least one violscore was added */
6249 )
6250 {
6251 SCIP_CONSHDLR* conshdlr;
6252 SCIP_VAR* var;
6253 SCIP_Real weight;
6254 SCIP_Real weightsum = 0.0; /* sum of weights over all candidates with bounded domain */
6255 int nunbounded = 0; /* number of candidates with unbounded domain */
6256 int i;
6257
6258 assert(exprs != NULL);
6259 assert(nexprs > 0);
6260 assert(success != NULL);
6261
6262 if( nexprs == 1 )
6263 {
6264 SCIPaddExprViolScoreNonlinear(scip, exprs[0], violscore);
6265 SCIPdebugMsg(scip, "add score %g to <%s>[%g,%g]\n", violscore,
6266 SCIPvarGetName(SCIPgetExprAuxVarNonlinear(exprs[0])), SCIPvarGetLbLocal(SCIPgetExprAuxVarNonlinear(exprs[0])), SCIPvarGetUbLocal(SCIPgetExprAuxVarNonlinear(exprs[0])));
6267 *success = TRUE;
6268 return;
6269 }
6270
6271 conshdlr = SCIPexprGetOwnerData(exprs[0])->conshdlr;
6272
6273 for( i = 0; i < nexprs; ++i )
6274 {
6275 var = SCIPgetExprAuxVarNonlinear(exprs[i]);
6276 assert(var != NULL);
6277
6278 if( SCIPisInfinity(scip, -SCIPvarGetLbLocal(var)) || SCIPisInfinity(scip, SCIPvarGetUbLocal(var)) )
6279 ++nunbounded;
6280 else if( !SCIPisEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) )
6281 weightsum += getViolSplitWeight(scip, conshdlr, var, sol);
6282 }
6283
6284 *success = FALSE;
6285 for( i = 0; i < nexprs; ++i )
6286 {
6287 var = SCIPgetExprAuxVarNonlinear(exprs[i]);
6288 assert(var != NULL);
6289
6290 if( nunbounded > 0 )
6291 {
6292 if( SCIPisInfinity(scip, -SCIPvarGetLbLocal(var)) || SCIPisInfinity(scip, SCIPvarGetUbLocal(var)) )
6293 {
6294 SCIPaddExprViolScoreNonlinear(scip, exprs[i], violscore / nunbounded);
6295 SCIPdebugMsg(scip, "add score %g (%g%% of %g) to <%s>[%g,%g]\n", violscore / nunbounded,
6296 100.0/nunbounded, violscore,
6297 SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var));
6298 *success = TRUE;
6299 }
6300 }
6301 else if( !SCIPisEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) )
6302 {
6303 assert(weightsum > 0.0);
6304
6305 weight = getViolSplitWeight(scip, conshdlr, var, sol);
6306 SCIPaddExprViolScoreNonlinear(scip, exprs[i], violscore * weight / weightsum);
6307 SCIPdebugMsg(scip, "add score %g (%g%% of %g) to <%s>[%g,%g]\n", violscore * weight / weightsum,
6308 100*weight / weightsum, violscore,
6309 SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var));
6310 *success = TRUE;
6311 }
6312 else
6313 {
6314 SCIPdebugMsg(scip, "skip score for fixed variable <%s>[%g,%g]\n",
6315 SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var));
6316 }
6317 }
6318 }
6319
6320 /** adds violation-branching score to children of expression for given auxiliary variables
6321 *
6322 * Iterates over the successors of `expr` to find expressions that are associated with one of the given auxiliary variables.
6323 * Adds violation-branching scores to all found exprs by means of SCIPaddExprsViolScoreNonlinear().
6324 *
6325 * @note This method may modify the given auxvars array by means of sorting.
6326 */
6327 static
6328 SCIP_RETCODE addExprViolScoresAuxVars(
6329 SCIP* scip, /**< SCIP data structure */
6330 SCIP_EXPR* expr, /**< expression where to start searching */
6331 SCIP_Real violscore, /**< violation score to add to expression */
6332 SCIP_VAR** auxvars, /**< auxiliary variables for which to find expression */
6333 int nauxvars, /**< number of auxiliary variables */
6334 SCIP_SOL* sol, /**< current solution (NULL for the LP solution) */
6335 SCIP_Bool* success /**< buffer to store whether at least one violscore was added */
6336 )
6337 {
6338 SCIP_EXPRITER* it;
6339 SCIP_VAR* auxvar;
6340 SCIP_EXPR** exprs;
6341 int nexprs;
6342 int pos;
6343
6344 assert(scip != NULL);
6345 assert(expr != NULL);
6346 assert(auxvars != NULL);
6347 assert(success != NULL);
6348
6349 /* sort variables to make lookup below faster */
6350 SCIPsortPtr((void**)auxvars, SCIPvarComp, nauxvars);
6351
6352 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
6353 SCIP_CALL( SCIPexpriterInit(it, expr, SCIP_EXPRITER_BFS, FALSE) );
6354
6355 SCIP_CALL( SCIPallocBufferArray(scip, &exprs, nauxvars) );
6356 nexprs = 0;
6357
6358 for( expr = SCIPexpriterGetNext(it); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
6359 {
6360 auxvar = SCIPgetExprAuxVarNonlinear(expr);
6361 if( auxvar == NULL )
6362 continue;
6363
6364 /* if auxvar of expr is contained in auxvars array, add branching score to expr */
6365 if( SCIPsortedvecFindPtr((void**)auxvars, SCIPvarComp, auxvar, nauxvars, &pos) )
6366 {
6367 assert(auxvars[pos] == auxvar);
6368
6369 SCIPdebugMsg(scip, "adding branchingscore for expr %p with auxvar <%s>\n", (void*)expr, SCIPvarGetName(auxvar));
6370 exprs[nexprs++] = expr;
6371
6372 if( nexprs == nauxvars )
6373 break;
6374 }
6375 }
6376
6377 SCIPfreeExpriter(&it);
6378
6379 if( nexprs > 0 )
6380 {
6381 SCIP_CALL( SCIPaddExprsViolScoreNonlinear(scip, exprs, nexprs, violscore, sol, success) );
6382 }
6383 else
6384 *success = FALSE;
6385
6386 SCIPfreeBufferArray(scip, &exprs);
6387
6388 return SCIP_OKAY;
6389 }
6390
6391 /** registers all unfixed variables in violated constraints as branching candidates */
6392 static
6393 SCIP_RETCODE registerBranchingCandidatesAllUnfixed(
6394 SCIP* scip, /**< SCIP data structure */
6395 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraints handler */
6396 SCIP_CONS** conss, /**< constraints */
6397 int nconss, /**< number of constraints */
6398 int* nnotify /**< counter for number of notifications performed */
6399 )
6400 {
6401 SCIP_CONSDATA* consdata;
6402 SCIP_VAR* var;
6403 int c;
6404 int i;
6405
6406 assert(conshdlr != NULL);
6407 assert(conss != NULL || nconss == 0);
6408 assert(nnotify != NULL);
6409
6410 *nnotify = 0;
6411
6412 for( c = 0; c < nconss; ++c )
6413 {
6414 assert(conss != NULL && conss[c] != NULL);
6415
6416 consdata = SCIPconsGetData(conss[c]);
6417 assert(consdata != NULL);
6418
6419 /* consider only violated constraints */
6420 if( !isConsViolated(scip, conss[c]) )
6421 continue;
6422
6423 /* register all variables that have not been fixed yet */
6424 assert(consdata->varexprs != NULL);
6425 for( i = 0; i < consdata->nvarexprs; ++i )
6426 {
6427 var = SCIPgetVarExprVar(consdata->varexprs[i]);
6428 assert(var != NULL);
6429
6430 if( !SCIPisEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) )
6431 {
6432 SCIP_CALL( SCIPaddExternBranchCand(scip, var, getConsAbsViolation(conss[c]), SCIP_INVALID) );
6433 ++(*nnotify);
6434 }
6435 }
6436 }
6437
6438 return SCIP_OKAY;
6439 }
6440
6441 /** registers all variables in violated constraints with branching scores as external branching candidates */
6442 static
6443 SCIP_RETCODE registerBranchingCandidates(
6444 SCIP* scip, /**< SCIP data structure */
6445 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraints handler */
6446 SCIP_CONS** conss, /**< constraints */
6447 int nconss, /**< number of constraints */
6448 SCIP_Bool* success /**< buffer to store whether at least one branching candidate was added */
6449 )
6450 {
6451 SCIP_CONSDATA* consdata;
6452 SCIP_EXPRITER* it = NULL;
6453 int c;
6454
6455 assert(conshdlr != NULL);
6456 assert(success != NULL);
6457
6458 *success = FALSE;
6459
6460 if( branchAuxNonlinear(scip, conshdlr) )
6461 {
6462 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
6463 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) );
6464 }
6465
6466 /* register external branching candidates */
6467 for( c = 0; c < nconss; ++c )
6468 {
6469 assert(conss != NULL && conss[c] != NULL);
6470
6471 consdata = SCIPconsGetData(conss[c]);
6472 assert(consdata != NULL);
6473 assert(consdata->varexprs != NULL);
6474
6475 /* consider only violated constraints */
6476 if( !isConsViolated(scip, conss[c]) )
6477 continue;
6478
6479 if( !branchAuxNonlinear(scip, conshdlr) )
6480 {
6481 int i;
6482
6483 /* if not branching on auxvars, then violation-branching scores will have been added to original variables
6484 * only, so we can loop over variable expressions
6485 */
6486 for( i = 0; i < consdata->nvarexprs; ++i )
6487 {
6488 SCIP_Real violscore;
6489 SCIP_Real lb;
6490 SCIP_Real ub;
6491 SCIP_VAR* var;
6492
6493 violscore = SCIPgetExprViolScoreNonlinear(consdata->varexprs[i]);
6494
6495 /* skip variable expressions that do not have a violation score */
6496 if( violscore == 0.0 )
6497 continue;
6498
6499 var = SCIPgetVarExprVar(consdata->varexprs[i]);
6500 assert(var != NULL);
6501
6502 lb = SCIPvarGetLbLocal(var);
6503 ub = SCIPvarGetUbLocal(var);
6504
6505 /* consider variable for branching if it has not been fixed yet */
6506 if( !SCIPisEQ(scip, lb, ub) )
6507 {
6508 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " add variable <%s>[%g,%g] as extern branching candidate with score %g\n", SCIPvarGetName(var), lb, ub, violscore); )
6509 SCIP_CALL( SCIPaddExternBranchCand(scip, var, violscore, SCIP_INVALID) );
6510 *success = TRUE;
6511 }
6512 else
6513 {
6514 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " skip fixed variable <%s>[%.15g,%.15g]\n", SCIPvarGetName(var), lb, ub); )
6515 }
6516
6517 /* invalidate violscore-tag, so that we do not register variables that appear in multiple constraints
6518 * several times as external branching candidate, see SCIPgetExprViolScoreNonlinear()
6519 */
6520 SCIPexprGetOwnerData(consdata->varexprs[i])->violscoretag = 0;
6521 }
6522 }
6523 else
6524 {
6525 SCIP_EXPR* expr;
6526 SCIP_VAR* var;
6527 SCIP_Real lb;
6528 SCIP_Real ub;
6529 SCIP_Real violscore;
6530
6531 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
6532 {
6533 violscore = SCIPgetExprViolScoreNonlinear(expr);
6534 if( violscore == 0.0 )
6535 continue;
6536
6537 /* if some nlhdlr added a branching score for this expression, then it considered this expression as a
6538 * variable, so this expression should either be an original variable or have an auxiliary variable
6539 */
6540 var = SCIPgetExprAuxVarNonlinear(expr);
6541 assert(var != NULL);
6542
6543 lb = SCIPvarGetLbLocal(var);
6544 ub = SCIPvarGetUbLocal(var);
6545
6546 /* consider variable for branching if it has not been fixed yet */
6547 if( !SCIPisEQ(scip, lb, ub) )
6548 {
6549 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " add variable <%s>[%g,%g] as extern branching candidate with score %g\n", SCIPvarGetName(var), lb, ub, violscore); )
6550
6551 SCIP_CALL( SCIPaddExternBranchCand(scip, var, violscore, SCIP_INVALID) );
6552 *success = TRUE;
6553 }
6554 else
6555 {
6556 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " skip fixed variable <%s>[%.15g,%.15g]\n", SCIPvarGetName(var), lb, ub); )
6557 }
6558 }
6559 }
6560 }
6561
6562 if( it != NULL )
6563 SCIPfreeExpriter(&it);
6564
6565 return SCIP_OKAY;
6566 }
6567
6568 /** collect branching candidates from violated constraints
6569 *
6570 * Fills array with expressions that serve as branching candidates.
6571 * Collects those expressions that have a branching score assigned and stores the score in the auxviol field of the
6572 * branching candidate.
6573 *
6574 * If branching on aux-variables is allowed, then iterate through expressions of violated constraints, otherwise iterate
6575 * through variable-expressions only.
6576 */
6577 static
6578 SCIP_RETCODE collectBranchingCandidates(
6579 SCIP* scip, /**< SCIP data structure */
6580 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
6581 SCIP_CONS** conss, /**< constraints to process */
6582 int nconss, /**< number of constraints */
6583 SCIP_Real maxrelconsviol, /**< maximal scaled constraint violation */
6584 SCIP_SOL* sol, /**< solution to enforce (NULL for the LP solution) */
6585 SCIP_Longint soltag, /**< tag of solution */
6586 BRANCHCAND* cands, /**< array where to store candidates, must be at least SCIPgetNVars() long */
6587 int* ncands /**< number of candidates found */
6588 )
6589 {
6590 SCIP_CONSHDLRDATA* conshdlrdata;
6591 SCIP_CONSDATA* consdata;
6592 SCIP_EXPRITER* it = NULL;
6593 int c;
6594 int attempt;
6595 SCIP_VAR* var;
6596
6597 assert(scip != NULL);
6598 assert(conshdlr != NULL);
6599 assert(cands != NULL);
6600 assert(ncands != NULL);
6601
6602 conshdlrdata = SCIPconshdlrGetData(conshdlr);
6603 assert(conshdlrdata != NULL);
6604
6605 if( branchAuxNonlinear(scip, conshdlr) )
6606 {
6607 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
6608 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) );
6609 }
6610
6611 *ncands = 0;
6612 for( attempt = 0; attempt < 2; ++attempt )
6613 {
6614 /* collect branching candidates from violated constraints
6615 * in the first attempt, consider only constraints with large violation
6616 * in the second attempt, consider all remaining violated constraints
6617 */
6618 for( c = 0; c < nconss; ++c )
6619 {
6620 SCIP_Real consviol;
6621
6622 assert(conss != NULL && conss[c] != NULL);
6623
6624 /* consider only violated constraints */
6625 if( !isConsViolated(scip, conss[c]) )
6626 continue;
6627
6628 consdata = SCIPconsGetData(conss[c]);
6629 assert(consdata != NULL);
6630 assert(consdata->varexprs != NULL);
6631
6632 SCIP_CALL( getConsRelViolation(scip, conss[c], &consviol, sol, soltag) );
6633
6634 if( attempt == 0 && consviol < conshdlrdata->branchhighviolfactor * maxrelconsviol )
6635 continue;
6636 else if( attempt == 1 && consviol >= conshdlrdata->branchhighviolfactor * maxrelconsviol )
6637 continue;
6638
6639 if( !branchAuxNonlinear(scip, conshdlr) )
6640 {
6641 int i;
6642
6643 /* if not branching on auxvars, then violation-branching scores will be available for original variables
6644 * only, so we can loop over variable expressions
6645 * unfortunately, we don't know anymore which constraint contributed the violation-branching score to the
6646 * variable, therefore we invalidate the score of a variable after processing it.
6647 */
6648 for( i = 0; i < consdata->nvarexprs; ++i )
6649 {
6650 SCIP_Real lb;
6651 SCIP_Real ub;
6652
6653 /* skip variable expressions that do not have a valid violation score */
6654 if( conshdlrdata->enforound != SCIPexprGetOwnerData(consdata->varexprs[i])->violscoretag )
6655 continue;
6656
6657 var = SCIPgetVarExprVar(consdata->varexprs[i]);
6658 assert(var != NULL);
6659
6660 lb = SCIPvarGetLbLocal(var);
6661 ub = SCIPvarGetUbLocal(var);
6662
6663 /* skip already fixed variable */
6664 if( SCIPisEQ(scip, lb, ub) )
6665 {
6666 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " skip fixed variable <%s>[%.15g,%.15g]\n", SCIPvarGetName(var), lb, ub); )
6667 continue;
6668 }
6669
6670 assert(*ncands + 1 < SCIPgetNVars(scip));
6671 cands[*ncands].expr = consdata->varexprs[i];
6672 cands[*ncands].auxviol = SCIPgetExprViolScoreNonlinear(consdata->varexprs[i]);
6673 ++(*ncands);
6674
6675 /* invalidate violscore-tag, so that we do not register variables that appear in multiple constraints
6676 * several times as external branching candidate */
6677 SCIPexprGetOwnerData(consdata->varexprs[i])->violscoretag = 0;
6678 }
6679 }
6680 else
6681 {
6682 SCIP_EXPR* expr;
6683 SCIP_Real lb;
6684 SCIP_Real ub;
6685
6686 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
6687 {
6688 if( SCIPexprGetOwnerData(expr)->violscoretag != conshdlrdata->enforound )
6689 continue;
6690
6691 /* if some nlhdlr added a branching score for this expression, then it considered this expression as
6692 * variables, so this expression should either be an original variable or have an auxiliary variable
6693 */
6694 var = SCIPgetExprAuxVarNonlinear(expr);
6695 assert(var != NULL);
6696
6697 lb = SCIPvarGetLbLocal(var);
6698 ub = SCIPvarGetUbLocal(var);
6699
6700 /* skip already fixed variable */
6701 if( SCIPisEQ(scip, lb, ub) )
6702 {
6703 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " skip fixed variable <%s>[%.15g,%.15g]\n", SCIPvarGetName(var), lb, ub); )
6704 continue;
6705 }
6706
6707 assert(*ncands + 1 < SCIPgetNVars(scip));
6708 cands[*ncands].expr = expr;
6709 cands[*ncands].auxviol = SCIPgetExprViolScoreNonlinear(expr);
6710 ++(*ncands);
6711 }
6712 }
6713 }
6714
6715 /* if we have branching candidates, then we don't need another attempt */
6716 if( *ncands > 0 )
6717 break;
6718 }
6719
6720 if( it != NULL )
6721 SCIPfreeExpriter(&it);
6722
6723 return SCIP_OKAY;
6724 }
6725
6726 /** computes a branching score for a variable that reflects how important branching on this variable would be for
6727 * improving the dual bound from the LP relaxation
6728 *
6729 * Assume the Lagrangian for the current LP is something of the form
6730 * L(x,z,lambda) = c'x + sum_i lambda_i (a_i'x - z_i + b_i) + ...
6731 * where x are the original variables, z the auxiliary variables,
6732 * and a_i'x - z_i + b_i <= 0 are the rows of the LP.
6733 *
6734 * Assume that a_i'x + b_i <= z_i was derived from some nonlinear constraint f(x) <= z and drop index i.
6735 * If we could have used not only an estimator, but the actual function f(x), then this would
6736 * have contributed lambda*(f(x) - z) to the Lagrangian function (though the value of z would be different).
6737 * Using a lot of handwaving, we claim that
6738 * lambda_i * (f(x) - a_i'x + b_i)
6739 * is a value that can be used to quantity how much improving the estimator a'x + b <= z could change the dual bound.
6740 * If an estimator depended on local bounds, then it could be improved by branching.
6741 * We use row-is-local as proxy for estimator-depending-on-lower-bounds.
6742 *
6743 * 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.
6744 * To scale, we divide by the LP objective value (if >1).
6745 *
6746 * TODO if we branch only on original variables, we neglect here estimators that are build on auxiliary variables;
6747 * these are affected by the bounds on original variables indirectly (through forward-propagation)
6748 *
6749 * TODO if we branch also on auxiliary variables, then separating z from the x-variables in the row a'x+b <= z should happen;
6750 * in effect, we should go from the row to the expression for which it was generated and consider only variables that
6751 * would also be branching candidates
6752 */
6753 static
6754 SCIP_Real getDualBranchscore(
6755 SCIP* scip, /**< SCIP data structure */
6756 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraints handler */
6757 SCIP_VAR* var /**< variable */
6758 )
6759 {
6760 SCIP_COL* col;
6761 SCIP_ROW** rows;
6762 int nrows;
6763 int r;
6764 SCIP_Real dualscore;
6765
6766 assert(scip != NULL);
6767 assert(conshdlr != NULL);
6768 assert(var != NULL);
6769
6770 /* if LP not solved, then the dual branching score is not available */
6771 if( SCIPgetLPSolstat(scip) != SCIP_LPSOLSTAT_OPTIMAL )
6772 return 0.0;
6773
6774 /* if var is not in the LP, then the dual branching score is not available */
6775 if( SCIPvarGetStatus(var) != SCIP_VARSTATUS_COLUMN )
6776 return 0.0;
6777
6778 col = SCIPvarGetCol(var);
6779 assert(col != NULL);
6780
6781 if( !SCIPcolIsInLP(col) )
6782 return 0.0;
6783
6784 nrows = SCIPcolGetNLPNonz(col); /* TODO there is a big warning on when not to use this method; is the check for SCIPcolIsInLP sufficient? */
6785 rows = SCIPcolGetRows(col);
6786
6787 /* SCIPinfoMessage(scip, enfologfile, " dualscoring <%s>\n", SCIPvarGetName(var)); */
6788
6789 /* aggregate duals from all rows from consexpr with non-zero dual
6790 * TODO: this is a quick-and-dirty implementation, and not used by default
6791 * in the long run, this should be either removed or replaced by a proper implementation
6792 */
6793 dualscore = 0.0;
6794 for( r = 0; r < nrows; ++r )
6795 {
6796 SCIP_Real estimategap;
6797 const char* estimategapstr;
6798
6799 /* rows from cuts that may be replaced by tighter ones after branching are the interesting ones
6800 * these would typically be local, unless they are created at the root node
6801 * so not check for local now, but trust that estimators that do not improve after branching will have an estimategap of 0
6802 if( !SCIProwIsLocal(rows[r]) )
6803 continue;
6804 */
6805 if( SCIProwGetOriginConshdlr(rows[r]) != conshdlr )
6806 continue;
6807 if( SCIPisZero(scip, SCIProwGetDualsol(rows[r])) )
6808 continue;
6809
6810 estimategapstr = strstr(SCIProwGetName(rows[r]), "_estimategap=");
6811 if( estimategapstr == NULL ) /* gap not stored, maybe because it was 0 */
6812 continue;
6813 estimategap = atof(estimategapstr + 13);
6814 assert(estimategap >= 0.0);
6815 if( !SCIPisFinite(estimategap) || SCIPisHugeValue(scip, estimategap) )
6816 estimategap = SCIPgetHugeValue(scip);
6817
6818 /* SCIPinfoMessage(scip, enfologfile, " row <%s> contributes %g*|%g|: ", SCIProwGetName(rows[r]), estimategap, SCIProwGetDualsol(rows[r]));
6819 SCIP_CALL( SCIPprintRow(scip, rows[r], enfologfile) ); */
6820
6821 dualscore += estimategap * REALABS(SCIProwGetDualsol(rows[r]));
6822 }
6823
6824 /* divide by optimal value of LP for scaling */
6825 dualscore /= MAX(1.0, REALABS(SCIPgetLPObjval(scip)));
6826
6827 return dualscore;
6828 }
6829
6830 /** computes branching scores (including weighted score) for a set of candidates
6831 *
6832 * For each candidate in the array, compute and store the various branching scores (violation, pseudo-costs, vartype, domainwidth).
6833 * For pseudo-costs, it's possible that the score is not available, in which case cands[c].pscost will be set to SCIP_INVALID.
6834 *
6835 * For each score, compute the maximum over all candidates.
6836 *
6837 * Then compute for each candidate a "weighted" score using the weights as specified by parameters
6838 * and the scores as previously computed, but scale each score to be in [0,1], i.e., divide each score by the maximum
6839 * score of all candidates.
6840 * Further divide by the sum of all weights where a score was available (even if the score was 0).
6841 *
6842 * For example:
6843 * - Let variable x have violation-score 10.0 and pseudo-cost-score 5.0.
6844 * - Let variable y have violation-score 12.0 but no pseudo-cost-score (because it hasn't yet been branched on sufficiently often).
6845 * - Assuming violation is weighted by 2.0 and pseudo-costs are weighted by 3.0.
6846 * - 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.
6847 * The weighted score for y will be (2.0 * 12.0/12.0) / 2.0 = 1.0.
6848 */
6849 static
6850 void scoreBranchingCandidates(
6851 SCIP* scip, /**< SCIP data structure */
6852 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
6853 BRANCHCAND* cands, /**< branching candidates */
6854 int ncands, /**< number of candidates */
6855 SCIP_SOL* sol /**< solution to enforce (NULL for the LP solution) */
6856 )
6857 {
6858 SCIP_CONSHDLRDATA* conshdlrdata;
6859 BRANCHCAND maxscore;
6860 int c;
6861
6862 assert(scip != NULL);
6863 assert(conshdlr != NULL);
6864 assert(cands != NULL);
6865 assert(ncands > 0);
6866
6867 conshdlrdata = SCIPconshdlrGetData(conshdlr);
6868 assert(conshdlrdata != NULL);
6869
6870 /* initialize counts to 0 */
6871 memset(&maxscore, 0, sizeof(BRANCHCAND));
6872
6873 for( c = 0; c < ncands; ++c )
6874 {
6875 if( conshdlrdata->branchviolweight > 0.0 )
6876 {
6877 /* cands[c].auxviol was set in collectBranchingCandidates, so only update maxscore here */
6878 maxscore.auxviol = MAX(maxscore.auxviol, cands[c].auxviol);
6879 }
6880
6881 if( conshdlrdata->branchdomainweight > 0.0 )
6882 {
6883 SCIP_Real domainwidth;
6884 SCIP_VAR* var;
6885
6886 var = SCIPgetExprAuxVarNonlinear(cands[c].expr);
6887 assert(var != NULL);
6888
6889 /* get domain width, taking infinity at 1e20 on purpose */
6890 domainwidth = SCIPvarGetUbLocal(var) - SCIPvarGetLbLocal(var);
6891
6892 /* domain-score is going to be log(2*infinity / domainwidth) if domain width >= 1
6893 * and log(2 * infinity * MAX(epsilon, domainwidth)) for domain width < 1
6894 * the idea is to penalize very large and very small domains
6895 */
6896 if( domainwidth >= 1.0 )
6897 cands[c].domain = log10(2 * SCIPinfinity(scip) / domainwidth);
6898 else
6899 cands[c].domain = log10(2 * SCIPinfinity(scip) * MAX(SCIPepsilon(scip), domainwidth));
6900
6901 maxscore.domain = MAX(cands[c].domain, maxscore.domain);
6902 }
6903 else
6904 cands[c].domain = 0.0;
6905
6906 if( conshdlrdata->branchdualweight > 0.0 )
6907 {
6908 SCIP_VAR* var;
6909
6910 var = SCIPgetExprAuxVarNonlinear(cands[c].expr);
6911 assert(var != NULL);
6912
6913 cands[c].dual = getDualBranchscore(scip, conshdlr, var);
6914 maxscore.dual = MAX(cands[c].dual, maxscore.dual);
6915 }
6916
6917 if( conshdlrdata->branchpscostweight > 0.0 && SCIPgetNObjVars(scip) > 0 )
6918 {
6919 SCIP_VAR* var;
6920
6921 var = SCIPgetExprAuxVarNonlinear(cands[c].expr);
6922 assert(var != NULL);
6923
6924 if( SCIPisInfinity(scip, -SCIPvarGetLbLocal(var)) || SCIPisInfinity(scip, SCIPvarGetUbLocal(var)) )
6925 cands[c].pscost = SCIP_INVALID;
6926 else
6927 {
6928 SCIP_Real brpoint;
6929 SCIP_Real pscostdown;
6930 SCIP_Real pscostup;
6931 char strategy;
6932
6933 /* decide how to compute pseudo-cost scores
6934 * this should be consistent with the way how pseudo-costs are updated in the core, which is decided by
6935 * branching/lpgainnormalize for continuous variables and move in LP-value for non-continuous variables
6936 */
6937 if( SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS )
6938 strategy = conshdlrdata->branchpscostupdatestrategy;
6939 else
6940 strategy = 'l';
6941
6942 brpoint = SCIPgetBranchingPoint(scip, var, SCIP_INVALID);
6943
6944 /* branch_relpscost deems pscosts as reliable, if the pseudo-count is at least something between 1 and 4
6945 * or it uses some statistical tests involving SCIPisVarPscostRelerrorReliable
6946 * For here, I use a simple #counts >= branchpscostreliable.
6947 * TODO use SCIPgetVarPseudocostCount() instead?
6948 */
6949 if( SCIPgetVarPseudocostCountCurrentRun(scip, var, SCIP_BRANCHDIR_DOWNWARDS) >= conshdlrdata->branchpscostreliable )
6950 {
6951 switch( strategy )
6952 {
6953 case 's' :
6954 pscostdown = SCIPgetVarPseudocostVal(scip, var, -(SCIPvarGetUbLocal(var) - SCIPadjustedVarLb(scip, var, brpoint)));
6955 break;
6956 case 'd' :
6957 pscostdown = SCIPgetVarPseudocostVal(scip, var, -(SCIPadjustedVarUb(scip, var, brpoint) - SCIPvarGetLbLocal(var)));
6958 break;
6959 case 'l' :
6960 if( SCIPisInfinity(scip, SCIPgetSolVal(scip, sol, var)) )
6961 pscostdown = SCIP_INVALID;
6962 else if( SCIPgetSolVal(scip, sol, var) <= SCIPadjustedVarUb(scip, var, brpoint) )
6963 pscostdown = SCIPgetVarPseudocostVal(scip, var, 0.0);
6964 else
6965 pscostdown = SCIPgetVarPseudocostVal(scip, var, -(SCIPgetSolVal(scip, NULL, var) - SCIPadjustedVarUb(scip, var, brpoint)));
6966 break;
6967 default :
6968 SCIPerrorMessage("pscost update strategy %c unknown\n", strategy);
6969 pscostdown = SCIP_INVALID;
6970 }
6971 }
6972 else
6973 pscostdown = SCIP_INVALID;
6974
6975 if( SCIPgetVarPseudocostCountCurrentRun(scip, var, SCIP_BRANCHDIR_UPWARDS) >= conshdlrdata->branchpscostreliable )
6976 {
6977 switch( strategy )
6978 {
6979 case 's' :
6980 pscostup = SCIPgetVarPseudocostVal(scip, var, SCIPadjustedVarUb(scip, var, brpoint) - SCIPvarGetLbLocal(var));
6981 break;
6982 case 'd' :
6983 pscostup = SCIPgetVarPseudocostVal(scip, var, SCIPvarGetUbLocal(var) - SCIPadjustedVarLb(scip, var, brpoint));
6984 break;
6985 case 'l' :
6986 if( SCIPisInfinity(scip, -SCIPgetSolVal(scip, sol, var)) )
6987 pscostup = SCIP_INVALID;
6988 else if( SCIPgetSolVal(scip, NULL, var) >= SCIPadjustedVarLb(scip, var, brpoint) )
6989 pscostup = SCIPgetVarPseudocostVal(scip, var, 0.0);
6990 else
6991 pscostup = SCIPgetVarPseudocostVal(scip, var, SCIPadjustedVarLb(scip, var, brpoint) - SCIPgetSolVal(scip, NULL, var) );
6992 break;
6993 default :
6994 SCIPerrorMessage("pscost update strategy %c unknown\n", strategy);
6995 pscostup = SCIP_INVALID;
6996 }
6997 }
6998 else
6999 pscostup = SCIP_INVALID;
7000
7001 /* TODO if both are valid, we get pscostdown*pscostup, but does this compare well with vars were only pscostdown or pscostup is used?
7002 * maybe we should use (pscostdown+pscostup)/2 or sqrt(pscostdown*pscostup) ?
7003 */
7004 if( pscostdown == SCIP_INVALID && pscostup == SCIP_INVALID )
7005 cands[c].pscost = SCIP_INVALID;
7006 else if( pscostdown == SCIP_INVALID )
7007 cands[c].pscost = pscostup;
7008 else if( pscostup == SCIP_INVALID )
7009 cands[c].pscost = pscostdown;
7010 else
7011 cands[c].pscost = SCIPgetBranchScore(scip, NULL, pscostdown, pscostup); /* pass NULL for var to avoid multiplication with branch-factor */
7012 }
7013
7014 if( cands[c].pscost != SCIP_INVALID )
7015 maxscore.pscost = MAX(cands[c].pscost, maxscore.pscost);
7016 }
7017
7018 if( conshdlrdata->branchvartypeweight > 0.0 )
7019 {
7020 SCIP_VAR* var;
7021
7022 var = SCIPgetExprAuxVarNonlinear(cands[c].expr);
7023 assert(var != NULL);
7024
7025 switch( SCIPvarGetType(var) )
7026 {
7027 case SCIP_VARTYPE_BINARY :
7028 cands[c].vartype = 1.0;
7029 break;
7030 case SCIP_VARTYPE_INTEGER :
7031 cands[c].vartype = 0.1;
7032 break;
7033 case SCIP_VARTYPE_IMPLINT :
7034 cands[c].vartype = 0.01;
7035 break;
7036 case SCIP_VARTYPE_CONTINUOUS :
7037 default:
7038 cands[c].vartype = 0.0;
7039 }
7040 maxscore.vartype = MAX(cands[c].vartype, maxscore.vartype);
7041 }
7042 }
7043
7044 /* now compute a weighted score for each candidate from the single scores
7045 * the single scores are scaled to be in [0,1] for this
7046 */
7047 for( c = 0; c < ncands; ++c )
7048 {
7049 SCIP_Real weightsum;
7050
7051 ENFOLOG(
7052 SCIP_VAR* var;
7053 var = SCIPgetExprAuxVarNonlinear(cands[c].expr);
7054 SCIPinfoMessage(scip, enfologfile, " scoring <%8s>[%7.1g,%7.1g]:(", SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var));
7055 )
7056
7057 cands[c].weighted = 0.0;
7058 weightsum = 0.0;
7059
7060 if( maxscore.auxviol > 0.0 )
7061 {
7062 cands[c].weighted += conshdlrdata->branchviolweight * cands[c].auxviol / maxscore.auxviol;
7063 weightsum += conshdlrdata->branchviolweight;
7064
7065 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " %+g*%7.2g(viol)", conshdlrdata->branchviolweight, cands[c].auxviol / maxscore.auxviol); )
7066 }
7067
7068 if( maxscore.domain > 0.0 )
7069 {
7070 cands[c].weighted += conshdlrdata->branchdomainweight * cands[c].domain / maxscore.domain;
7071 weightsum += conshdlrdata->branchdomainweight;
7072
7073 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " %+g*%7.2g(domain)", conshdlrdata->branchdomainweight, cands[c].domain / maxscore.domain); )
7074 }
7075
7076 if( maxscore.dual > 0.0 )
7077 {
7078 cands[c].weighted += conshdlrdata->branchdualweight * cands[c].dual / maxscore.dual;
7079 weightsum += conshdlrdata->branchdualweight;
7080
7081 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " %+g*%7.2g(dual)", conshdlrdata->branchdualweight, cands[c].dual / maxscore.dual); )
7082 }
7083
7084 if( maxscore.pscost > 0.0 )
7085 {
7086 /* use pseudo-costs only if available */
7087 if( cands[c].pscost != SCIP_INVALID )
7088 {
7089 cands[c].weighted += conshdlrdata->branchpscostweight * cands[c].pscost / maxscore.pscost;
7090 weightsum += conshdlrdata->branchpscostweight;
7091
7092 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " %+g*%7.2g(pscost)", conshdlrdata->branchpscostweight, cands[c].pscost / maxscore.pscost); )
7093 }
7094 else
7095 {
7096 /* do not add pscostscore, if not available, also do not add into weightsum */
7097 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " +0.0* n/a(pscost)"); )
7098 }
7099 }
7100
7101 if( maxscore.vartype > 0.0 )
7102 {
7103 cands[c].weighted += conshdlrdata->branchvartypeweight * cands[c].vartype / maxscore.vartype;
7104 weightsum += conshdlrdata->branchvartypeweight;
7105
7106 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " %+g*%6.2g(vartype)", conshdlrdata->branchvartypeweight, cands[c].vartype / maxscore.vartype); )
7107 }
7108 assert(weightsum > 0.0); /* we should have got at least one valid score */
7109 cands[c].weighted /= weightsum;
7110
7111 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " ) / %g = %g\n", weightsum, cands[c].weighted); )
7112 }
7113 }
7114
7115 /** compare two branching candidates by their weighted score
7116 *
7117 * if weighted score is equal, use variable index of (aux)var
7118 */
7119 static
7120 SCIP_DECL_SORTINDCOMP(branchcandCompare)
7121 {
7122 BRANCHCAND* cands = (BRANCHCAND*)dataptr;
7123
7124 if( cands[ind1].weighted != cands[ind2].weighted )
7125 return cands[ind1].weighted < cands[ind2].weighted ? -1 : 1;
7126 else
7127 return SCIPvarGetIndex(SCIPgetExprAuxVarNonlinear(cands[ind1].expr)) - SCIPvarGetIndex(SCIPgetExprAuxVarNonlinear(cands[ind2].expr));
7128 }
7129
7130 /** do branching or register branching candidates */
7131 static
7132 SCIP_RETCODE branching(
7133 SCIP* scip, /**< SCIP data structure */
7134 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
7135 SCIP_CONS** conss, /**< constraints to process */
7136 int nconss, /**< number of constraints */
7137 SCIP_Real maxrelconsviol, /**< maximal scaled constraint violation */
7138 SCIP_SOL* sol, /**< solution to enforce (NULL for the LP solution) */
7139 SCIP_Longint soltag, /**< tag of solution */
7140 SCIP_RESULT* result /**< pointer to store the result of branching */
7141 )
7142 {
7143 SCIP_CONSHDLRDATA* conshdlrdata;
7144 BRANCHCAND* cands;
7145 int ncands;
7146 SCIP_VAR* var;
7147 SCIP_NODE* downchild;
7148 SCIP_NODE* eqchild;
7149 SCIP_NODE* upchild;
7150
7151 assert(conshdlr != NULL);
7152 assert(result != NULL);
7153
7154 *result = SCIP_DIDNOTFIND;
7155
7156 conshdlrdata = SCIPconshdlrGetData(conshdlr);
7157 assert(conshdlrdata != NULL);
7158
7159 if( conshdlrdata->branchexternal )
7160 {
7161 /* just register branching candidates as external */
7162 SCIP_Bool success;
7163
7164 SCIP_CALL( registerBranchingCandidates(scip, conshdlr, conss, nconss, &success) );
7165 if( success )
7166 *result = SCIP_INFEASIBLE;
7167
7168 return SCIP_OKAY;
7169 }
7170
7171 /* collect branching candidates and their auxviol-score */
7172 SCIP_CALL( SCIPallocBufferArray(scip, &cands, SCIPgetNVars(scip)) );
7173 SCIP_CALL( collectBranchingCandidates(scip, conshdlr, conss, nconss, maxrelconsviol, sol, soltag, cands, &ncands) );
7174
7175 /* if no unfixed branching candidate in all violated constraint, then it's probably numerics that prevented us to separate or decide a cutoff
7176 * we will return here and let the fallbacks in consEnfo() decide how to proceed
7177 */
7178 if( ncands == 0 )
7179 goto TERMINATE;
7180
7181 if( ncands > 1 )
7182 {
7183 /* if there are more than one candidate, then compute scores and select */
7184 int* perm;
7185 int c;
7186 int left;
7187 int right;
7188 SCIP_Real threshold;
7189
7190 /* compute additional scores on branching candidates and weighted score */
7191 scoreBranchingCandidates(scip, conshdlr, cands, ncands, sol);
7192
7193 /* sort candidates by weighted score */
7194 SCIP_CALL( SCIPallocBufferArray(scip, &perm, ncands) );
7195 SCIPsortDown(perm, branchcandCompare, (void*)cands, ncands);
7196
7197 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " %d branching candidates <%s>(%g)...<%s>(%g)\n", ncands,
7198 SCIPvarGetName(SCIPgetExprAuxVarNonlinear(cands[perm[0]].expr)), cands[perm[0]].weighted,
7199 SCIPvarGetName(SCIPgetExprAuxVarNonlinear(cands[perm[ncands - 1]].expr)), cands[perm[ncands - 1]].weighted); )
7200
7201 /* binary search to find first low-scored (score below branchhighscorefactor * maximal-score) candidate */
7202 left = 0;
7203 right = ncands - 1;
7204 threshold = conshdlrdata->branchhighscorefactor * cands[perm[0]].weighted;
7205 while( left < right )
7206 {
7207 int mid = (left + right) / 2;
7208 if( cands[perm[mid]].weighted >= threshold )
7209 left = mid + 1;
7210 else
7211 right = mid;
7212 }
7213 assert(left <= ncands);
7214
7215 if( left < ncands )
7216 {
7217 if( cands[perm[left]].weighted >= threshold )
7218 {
7219 assert(left + 1 == ncands || cands[perm[left + 1]].weighted < threshold);
7220 ncands = left + 1;
7221 }
7222 else
7223 {
7224 assert(cands[perm[left]].weighted < threshold);
7225 ncands = left;
7226 }
7227 }
7228 assert(ncands > 0);
7229
7230 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " %d branching candidates <%s>(%g)...<%s>(%g) after removing low scores\n", ncands,
7231 SCIPvarGetName(SCIPgetExprAuxVarNonlinear(cands[perm[0]].expr)), cands[perm[0]].weighted,
7232 SCIPvarGetName(SCIPgetExprAuxVarNonlinear(cands[perm[ncands - 1]].expr)), cands[perm[ncands - 1]].weighted); )
7233
7234 if( ncands > 1 )
7235 {
7236 /* choose at random from candidates 0..ncands-1 */
7237 if( conshdlrdata->branchrandnumgen == NULL )
7238 {
7239 SCIP_CALL( SCIPcreateRandom(scip, &conshdlrdata->branchrandnumgen, BRANCH_RANDNUMINITSEED, TRUE) );
7240 }
7241 c = SCIPrandomGetInt(conshdlrdata->branchrandnumgen, 0, ncands - 1);
7242 var = SCIPgetExprAuxVarNonlinear(cands[perm[c]].expr);
7243 }
7244 else
7245 var = SCIPgetExprAuxVarNonlinear(cands[perm[0]].expr);
7246
7247 SCIPfreeBufferArray(scip, &perm);
7248 }
7249 else
7250 {
7251 var = SCIPgetExprAuxVarNonlinear(cands[0].expr);
7252 }
7253 assert(var != NULL);
7254
7255 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " branching on variable <%s>[%g,%g]\n", SCIPvarGetName(var),
7256 SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)); )
7257
7258 SCIP_CALL( SCIPbranchVarVal(scip, var, SCIPgetBranchingPoint(scip, var, SCIP_INVALID), &downchild, &eqchild,
7259 &upchild) );
7260 if( downchild != NULL || eqchild != NULL || upchild != NULL )
7261 *result = SCIP_BRANCHED;
7262 else
7263 /* if there are no children, then variable should have been fixed by SCIPbranchVarVal */
7264 *result = SCIP_REDUCEDDOM;
7265
7266 TERMINATE:
7267 SCIPfreeBufferArray(scip, &cands);
7268
7269 return SCIP_OKAY;
7270 }
7271
7272 /** call enforcement or estimate callback of nonlinear handler
7273 *
7274 * Calls the enforcement callback, if available.
7275 * Otherwise, calls the estimate callback, if available, and constructs a cut from the estimator.
7276 *
7277 * If cut is weak, but estimator is not tight, tries to add branching candidates.
7278 */
7279 static
7280 SCIP_RETCODE enforceExprNlhdlr(
7281 SCIP* scip, /**< SCIP main data structure */
7282 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
7283 SCIP_CONS* cons, /**< nonlinear constraint */
7284 SCIP_NLHDLR* nlhdlr, /**< nonlinear handler */
7285 SCIP_EXPR* expr, /**< expression */
7286 SCIP_NLHDLREXPRDATA* nlhdlrexprdata, /**< nonlinear handler data of expression */
7287 SCIP_SOL* sol, /**< solution to be separated (NULL for the LP solution) */
7288 SCIP_Real auxvalue, /**< current value of expression w.r.t. auxiliary variables as obtained from EVALAUX */
7289 SCIP_Bool overestimate, /**< whether the expression needs to be over- or underestimated */
7290 SCIP_Bool separated, /**< whether another nonlinear handler already added a cut for this expression */
7291 SCIP_Bool allowweakcuts, /**< whether we allow for weak cuts */
7292 SCIP_Bool inenforcement, /**< whether we are in enforcement (and not just separation) */
7293 SCIP_RESULT* result /**< pointer to store the result */
7294 )
7295 {
7296 assert(result != NULL);
7297
7298 /* call enforcement callback of the nlhdlr */
7299 SCIP_CALL( SCIPnlhdlrEnfo(scip, conshdlr, cons, nlhdlr, expr, nlhdlrexprdata, sol, auxvalue, overestimate,
7300 allowweakcuts, separated, inenforcement, result) );
7301
7302 /* if it was not running (e.g., because it was not available) or did not find anything, then try with estimator callback */
7303 if( *result != SCIP_DIDNOTRUN && *result != SCIP_DIDNOTFIND )
7304 {
7305 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " enfo of nlhdlr <%s> succeeded with result %d\n",
7306 SCIPnlhdlrGetName(nlhdlr), *result); )
7307 return SCIP_OKAY;
7308 }
7309 else
7310 {
7311 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " enfo of nlhdlr <%s> did not succeed with result %d\n", SCIPnlhdlrGetName(nlhdlr), *result); )
7312 }
7313
7314 *result = SCIP_DIDNOTFIND;
7315
7316 /* now call the estimator callback of the nlhdlr */
7317 if( SCIPnlhdlrHasEstimate(nlhdlr) )
7318 {
7319 SCIP_VAR* auxvar;
7320 SCIP_Bool sepasuccess = FALSE;
7321 SCIP_Bool branchscoresuccess = FALSE;
7322 SCIP_PTRARRAY* rowpreps;
7323 int minidx;
7324 int maxidx;
7325 int r;
7326 SCIP_ROWPREP* rowprep;
7327
7328 SCIP_CALL( SCIPcreatePtrarray(scip, &rowpreps) );
7329
7330 auxvar = SCIPgetExprAuxVarNonlinear(expr);
7331 assert(auxvar != NULL);
7332
7333 SCIP_CALL( SCIPnlhdlrEstimate(scip, conshdlr, nlhdlr, expr, nlhdlrexprdata, sol, auxvalue, overestimate,
7334 SCIPgetSolVal(scip, sol, auxvar), inenforcement, rowpreps, &sepasuccess, &branchscoresuccess) );
7335
7336 minidx = SCIPgetPtrarrayMinIdx(scip, rowpreps);
7337 maxidx = SCIPgetPtrarrayMaxIdx(scip, rowpreps);
7338
7339 assert((sepasuccess && minidx <= maxidx) || (!sepasuccess && minidx > maxidx));
7340
7341 if( !sepasuccess )
7342 {
7343 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " estimate of nlhdlr %s failed\n",
7344 SCIPnlhdlrGetName(nlhdlr)); )
7345 }
7346
7347 for( r = minidx; r <= maxidx; ++r )
7348 {
7349 rowprep = (SCIP_ROWPREP*) SCIPgetPtrarrayVal(scip, rowpreps, r);
7350
7351 assert(rowprep != NULL);
7352 assert(SCIProwprepGetSidetype(rowprep) == (overestimate ? SCIP_SIDETYPE_LEFT : SCIP_SIDETYPE_RIGHT));
7353
7354 /* complete estimator to cut */
7355 SCIP_CALL( SCIPaddRowprepTerm(scip, rowprep, auxvar, -1.0) );
7356
7357 /* add the cut and/or branching scores */
7358 SCIP_CALL( SCIPprocessRowprepNonlinear(scip, nlhdlr, cons, expr, rowprep, overestimate, auxvar,
7359 auxvalue, allowweakcuts, branchscoresuccess, inenforcement, sol, result) );
7360
7361 SCIPfreeRowprep(scip, &rowprep);
7362 }
7363
7364 SCIP_CALL( SCIPfreePtrarray(scip, &rowpreps) );
7365 }
7366
7367 return SCIP_OKAY;
7368 }
7369
7370 /** tries to enforce violation in an expression by separation, bound tightening, or finding a branching candidate
7371 *
7372 * if not inenforcement, then we should be called by consSepa(), and thus only try separation
7373 */
7374 static
7375 SCIP_RETCODE enforceExpr(
7376 SCIP* scip, /**< SCIP data structure */
7377 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraints handler */
7378 SCIP_CONS* cons, /**< nonlinear constraint */
7379 SCIP_EXPR* expr, /**< expression */
7380 SCIP_SOL* sol, /**< solution to separate, or NULL if LP solution should be used */
7381 SCIP_Longint soltag, /**< tag of solution */
7382 SCIP_Bool allowweakcuts, /**< whether we allow weak cuts */
7383 SCIP_Bool inenforcement, /**< whether we are in enforcement (and not just separation) */
7384 SCIP_RESULT* result /**< pointer to store the result of the enforcing call */
7385 )
7386 {
7387 SCIP_CONSHDLRDATA* conshdlrdata;
7388 SCIP_EXPR_OWNERDATA* ownerdata;
7389 SCIP_Real origviol;
7390 SCIP_Bool underestimate;
7391 SCIP_Bool overestimate;
7392 SCIP_Real auxviol;
7393 SCIP_Bool auxunderestimate;
7394 SCIP_Bool auxoverestimate;
7395 SCIP_RESULT hdlrresult;
7396 int e;
7397
7398 assert(scip != NULL);
7399 assert(expr != NULL);
7400 assert(result != NULL);
7401
7402 ownerdata = SCIPexprGetOwnerData(expr);
7403 assert(ownerdata != NULL);
7404 assert(ownerdata->auxvar != NULL); /* there must be a variable attached to the expression in order to construct a cut here */
7405
7406 *result = SCIP_DIDNOTFIND;
7407
7408 /* make sure that this expression has been evaluated */
7409 SCIP_CALL( SCIPevalExpr(scip, expr, sol, soltag) );
7410
7411 /* decide whether under- or overestimate is required and get amount of violation */
7412 origviol = getExprAbsOrigViolation(scip, expr, sol, &underestimate, &overestimate);
7413
7414 conshdlrdata = SCIPconshdlrGetData(conshdlr);
7415 assert(conshdlrdata != NULL);
7416
7417 /* no sufficient violation w.r.t. the original variables -> skip expression */
7418 if( !overestimate && !underestimate )
7419 {
7420 return SCIP_OKAY;
7421 }
7422
7423 /* check aux-violation w.r.t. each nonlinear handlers and try to enforce when there is a decent violation */
7424 for( e = 0; e < ownerdata->nenfos; ++e )
7425 {
7426 SCIP_NLHDLR* nlhdlr;
7427
7428 /* skip nlhdlr that do not want to participate in any separation */
7429 if( (ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPABOTH) == 0 )
7430 continue;
7431
7432 nlhdlr = ownerdata->enfos[e]->nlhdlr;
7433 assert(nlhdlr != NULL);
7434
7435 /* evaluate the expression w.r.t. the nlhdlrs auxiliary variables */
7436 SCIP_CALL( SCIPnlhdlrEvalaux(scip, nlhdlr, expr, ownerdata->enfos[e]->nlhdlrexprdata, &ownerdata->enfos[e]->auxvalue, sol) );
7437 ENFOLOG(
7438 SCIPinfoMessage(scip, enfologfile, " expr ");
7439 SCIPprintExpr(scip, expr, enfologfile);
7440 SCIPinfoMessage(scip, enfologfile, " (%p): evalvalue %.15g auxvarvalue %.15g [%.15g,%.15g], nlhdlr <%s> " \
7441 "auxvalue: %.15g\n", (void*)expr, SCIPexprGetEvalValue(expr), SCIPgetSolVal(scip, sol, ownerdata->auxvar),
7442 SCIPexprGetActivity(expr).inf, SCIPexprGetActivity(expr).sup, SCIPnlhdlrGetName(nlhdlr), ownerdata->enfos[e]->auxvalue);
7443 )
7444
7445 /* TODO if expr is root of constraint (consdata->expr == expr),
7446 * then compare auxvalue with constraint sides instead of auxvarvalue, as the former is what actually matters
7447 * that is, if auxvalue is good enough for the constraint to be satisfied, but when looking at evalvalue we see
7448 * the the constraint is violated, then some of the auxvars that nlhdlr uses is not having a good enough value,
7449 * so we should enforce in these auxiliaries first
7450 * if changing this here, we must also adapt analyzeViolation()
7451 */
7452
7453 auxviol = getExprAbsAuxViolation(scip, expr, ownerdata->enfos[e]->auxvalue, sol, &auxunderestimate, &auxoverestimate);
7454 assert(auxviol >= 0.0);
7455
7456 /* if aux-violation is much smaller than orig-violation, then better enforce further down in the expression first */
7457 if( !SCIPisInfinity(scip, auxviol) && auxviol < conshdlrdata->enfoauxviolfactor * origviol )
7458 {
7459 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " skip enforce using nlhdlr <%s> for expr %p (%s) with " \
7460 "auxviolation %g << origviolation %g under:%d over:%d\n", SCIPnlhdlrGetName(nlhdlr), (void*)expr,
7461 SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)), auxviol, origviol, underestimate, overestimate); )
7462
7463 /* TODO should we do expr->lastenforced = conshdlrdata->enforound even though we haven't enforced, but only decided not to enforce? */
7464 continue;
7465 }
7466
7467 /* 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 */
7468 if( !allowweakcuts && auxviol < SCIPfeastol(scip) )
7469 {
7470 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " skip enforce using nlhdlr <%s> for expr %p (%s) with tiny " \
7471 "auxviolation %g under:%d over:%d\n", SCIPnlhdlrGetName(nlhdlr), (void*)expr, SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)), auxviol,
7472 underestimate, overestimate); )
7473
7474 /* TODO should we do expr->lastenforced = conshdlrdata->enforound even though we haven't enforced, but only decided not to enforce? */
7475 continue;
7476 }
7477
7478 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " enforce using nlhdlr <%s> for expr %p (%s) with auxviolation " \
7479 "%g origviolation %g under:%d over:%d weak:%d\n", SCIPnlhdlrGetName(nlhdlr), (void*)expr, SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)),
7480 auxviol, origviol, underestimate, overestimate, allowweakcuts); )
7481
7482 /* if we want to overestimate and violation w.r.t. auxiliary variables is also present on this side and nlhdlr
7483 * wants to be called for separation on this side, then call separation of nlhdlr
7484 */
7485 if( overestimate && auxoverestimate && (ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPAABOVE) != 0 )
7486 {
7487 /* call the separation or estimation callback of the nonlinear handler for overestimation */
7488 hdlrresult = SCIP_DIDNOTFIND;
7489 SCIP_CALL( enforceExprNlhdlr(scip, conshdlr, cons, nlhdlr, expr, ownerdata->enfos[e]->nlhdlrexprdata, sol,
7490 ownerdata->enfos[e]->auxvalue, TRUE, *result == SCIP_SEPARATED, allowweakcuts, inenforcement, &hdlrresult) );
7491
7492 if( hdlrresult == SCIP_CUTOFF )
7493 {
7494 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " found a cutoff -> stop separation\n"); )
7495 *result = SCIP_CUTOFF;
7496 ownerdata->lastenforced = conshdlrdata->enforound;
7497 break;
7498 }
7499
7500 if( hdlrresult == SCIP_SEPARATED )
7501 {
7502 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " nlhdlr <%s> separating the current solution by cut\n", SCIPnlhdlrGetName(nlhdlr)); )
7503 *result = SCIP_SEPARATED;
7504 ownerdata->lastenforced = conshdlrdata->enforound;
7505 /* TODO or should we give other nlhdlr another chance? (also #3070) */
7506 break;
7507 }
7508
7509 if( hdlrresult == SCIP_REDUCEDDOM )
7510 {
7511 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " nlhdlr <%s> separating the current solution by boundchange\n", SCIPnlhdlrGetName(nlhdlr)); )
7512 *result = SCIP_REDUCEDDOM;
7513 ownerdata->lastenforced = conshdlrdata->enforound;
7514 /* TODO or should we always just stop here? */
7515 }
7516
7517 if( hdlrresult == SCIP_BRANCHED )
7518 {
7519 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " nlhdlr <%s> added branching candidate\n", SCIPnlhdlrGetName(nlhdlr)); )
7520 assert(inenforcement);
7521
7522 /* separation and domain reduction takes precedence over branching */
7523 assert(*result == SCIP_DIDNOTFIND || *result == SCIP_SEPARATED || *result == SCIP_REDUCEDDOM || *result == SCIP_BRANCHED);
7524 if( *result == SCIP_DIDNOTFIND )
7525 *result = SCIP_BRANCHED;
7526 ownerdata->lastenforced = conshdlrdata->enforound;
7527 }
7528 }
7529
7530 /* if we want to underestimate and violation w.r.t. auxiliary variables is also present on this side and nlhdlr
7531 * wants to be called for separation on this side, then call separation of nlhdlr
7532 */
7533 if( underestimate && auxunderestimate && (ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPABELOW) != 0 )
7534 {
7535 /* call the separation or estimation callback of the nonlinear handler for underestimation */
7536 hdlrresult = SCIP_DIDNOTFIND;
7537 SCIP_CALL( enforceExprNlhdlr(scip, conshdlr, cons, nlhdlr, expr, ownerdata->enfos[e]->nlhdlrexprdata, sol,
7538 ownerdata->enfos[e]->auxvalue, FALSE, *result == SCIP_SEPARATED, allowweakcuts, inenforcement, &hdlrresult) );
7539
7540 if( hdlrresult == SCIP_CUTOFF )
7541 {
7542 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " found a cutoff -> stop separation\n"); )
7543 *result = SCIP_CUTOFF;
7544 ownerdata->lastenforced = conshdlrdata->enforound;
7545 break;
7546 }
7547
7548 if( hdlrresult == SCIP_SEPARATED )
7549 {
7550 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " nlhdlr <%s> separating the current solution by cut\n", SCIPnlhdlrGetName(nlhdlr)); )
7551 *result = SCIP_SEPARATED;
7552 ownerdata->lastenforced = conshdlrdata->enforound;
7553 /* TODO or should we give other nlhdlr another chance? (also #3070) */
7554 break;
7555 }
7556
7557 if( hdlrresult == SCIP_REDUCEDDOM )
7558 {
7559 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " nlhdlr <%s> separating the current solution by boundchange\n", SCIPnlhdlrGetName(nlhdlr)); )
7560 *result = SCIP_REDUCEDDOM;
7561 ownerdata->lastenforced = conshdlrdata->enforound;
7562 /* TODO or should we always just stop here? */
7563 }
7564
7565 if( hdlrresult == SCIP_BRANCHED )
7566 {
7567 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " nlhdlr <%s> added branching candidate\n", SCIPnlhdlrGetName(nlhdlr)); )
7568 assert(inenforcement);
7569
7570 /* separation takes precedence over branching */
7571 assert(*result == SCIP_DIDNOTFIND || *result == SCIP_SEPARATED || *result == SCIP_REDUCEDDOM || *result == SCIP_BRANCHED);
7572 if( *result == SCIP_DIDNOTFIND )
7573 *result = SCIP_BRANCHED;
7574 ownerdata->lastenforced = conshdlrdata->enforound;
7575 }
7576 }
7577 }
7578
7579 return SCIP_OKAY;
7580 }
7581
7582 /** helper function to enforce a single constraint */
7583 static
7584 SCIP_RETCODE enforceConstraint(
7585 SCIP* scip, /**< SCIP data structure */
7586 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
7587 SCIP_CONS* cons, /**< constraint to process */
7588 SCIP_SOL* sol, /**< solution to enforce (NULL for the LP solution) */
7589 SCIP_Longint soltag, /**< tag of solution */
7590 SCIP_EXPRITER* it, /**< expression iterator that we can just use here */
7591 SCIP_Bool allowweakcuts, /**< whether to allow weak cuts in this round */
7592 SCIP_Bool inenforcement, /**< whether to we are in enforcement, and not just separation */
7593 SCIP_RESULT* result, /**< pointer to update with result of the enforcing call */
7594 SCIP_Bool* success /**< buffer to store whether some enforcement took place */
7595 )
7596 {
7597 SCIP_CONSDATA* consdata;
7598 SCIP_CONSHDLRDATA* conshdlrdata;
7599 SCIP_EXPR* expr;
7600
7601 assert(conshdlr != NULL);
7602 assert(cons != NULL);
7603 assert(it != NULL);
7604 assert(result != NULL);
7605 assert(success != NULL);
7606
7607 conshdlrdata = SCIPconshdlrGetData(conshdlr);
7608 assert(conshdlrdata != NULL);
7609
7610 consdata = SCIPconsGetData(cons);
7611 assert(consdata != NULL);
7612 assert(SCIPexprGetOwnerData(consdata->expr)->nenfos >= 0);
7613
7614 *success = FALSE;
7615
7616 if( inenforcement && !consdata->ispropagated )
7617 {
7618 /* If there are boundchanges that haven't been propagated to activities yet, then do this now and update bounds of
7619 * auxiliary variables, since some nlhdlr/exprhdlr may look at auxvar bounds or activities
7620 * (TODO: nlhdlr tells us now whether they do and so we could skip).
7621 * For now, update bounds of auxiliary variables only if called from enforcement, since updating auxvar bounds in
7622 * separation doesn't seem to be right (it would be ok if the boundchange cuts off the current LP solution by a
7623 * nice amount, but if not, we may just add a boundchange that doesn't change the dual bound much and could
7624 * confuse the stalling check for how long to do separation).
7625 */
7626 SCIP_Bool infeasible;
7627 int ntightenings;
7628
7629 SCIP_CALL( forwardPropExpr(scip, conshdlr, consdata->expr, inenforcement, &infeasible, &ntightenings) );
7630 if( infeasible )
7631 {
7632 *result = SCIP_CUTOFF;
7633 return SCIP_OKAY;
7634 }
7635 /* if we tightened an auxvar bound, we better communicate that */
7636 if( ntightenings > 0 )
7637 *result = SCIP_REDUCEDDOM;
7638 }
7639
7640 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
7641 {
7642 SCIP_EXPR_OWNERDATA* ownerdata;
7643 SCIP_RESULT resultexpr;
7644
7645 ownerdata = SCIPexprGetOwnerData(expr);
7646 assert(ownerdata != NULL);
7647
7648 /* we can only enforce if there is an auxvar to compare with */
7649 if( ownerdata->auxvar == NULL )
7650 continue;
7651
7652 assert(ownerdata->lastenforced <= conshdlrdata->enforound);
7653 if( ownerdata->lastenforced == conshdlrdata->enforound )
7654 {
7655 ENFOLOG(
7656 SCIPinfoMessage(scip, enfologfile, " skip expr ");
7657 SCIPprintExpr(scip, expr, enfologfile);
7658 SCIPinfoMessage(scip, enfologfile, " as already enforced in this enforound\n");
7659 )
7660 *success = TRUE;
7661 continue;
7662 }
7663
7664 SCIP_CALL( enforceExpr(scip, conshdlr, cons, expr, sol, soltag, allowweakcuts, inenforcement, &resultexpr) );
7665
7666 /* if not enforced, then we must not have found a cutoff, cut, domain reduction, or branchscore */
7667 assert((ownerdata->lastenforced == conshdlrdata->enforound) == (resultexpr != SCIP_DIDNOTFIND));
7668 if( ownerdata->lastenforced == conshdlrdata->enforound )
7669 *success = TRUE;
7670
7671 if( resultexpr == SCIP_CUTOFF )
7672 {
7673 *result = SCIP_CUTOFF;
7674 break;
7675 }
7676
7677 if( resultexpr == SCIP_SEPARATED )
7678 *result = SCIP_SEPARATED;
7679
7680 if( resultexpr == SCIP_REDUCEDDOM && *result != SCIP_SEPARATED )
7681 *result = SCIP_REDUCEDDOM;
7682
7683 if( resultexpr == SCIP_BRANCHED && *result != SCIP_SEPARATED && *result != SCIP_REDUCEDDOM )
7684 *result = SCIP_BRANCHED;
7685 }
7686
7687 return SCIP_OKAY;
7688 }
7689
7690 /** try to separate violated constraints and, if in enforcement, register branching scores
7691 *
7692 * Sets result to
7693 * - SCIP_DIDNOTFIND, if nothing of the below has been done
7694 * - SCIP_CUTOFF, if node can be cutoff,
7695 * - SCIP_SEPARATED, if a cut has been added,
7696 * - SCIP_REDUCEDDOM, if a domain reduction has been found,
7697 * - SCIP_BRANCHED, if branching has been done,
7698 * - SCIP_REDUCEDDOM, if a variable got fixed (in an attempt to branch on it),
7699 * - SCIP_INFEASIBLE, if external branching candidates were registered
7700 */
7701 static
7702 SCIP_RETCODE enforceConstraints(
7703 SCIP* scip, /**< SCIP data structure */
7704 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
7705 SCIP_CONS** conss, /**< constraints to process */
7706 int nconss, /**< number of constraints */
7707 SCIP_SOL* sol, /**< solution to enforce (NULL for the LP solution) */
7708 SCIP_Longint soltag, /**< tag of solution */
7709 SCIP_Bool inenforcement, /**< whether we are in enforcement, and not just separation */
7710 SCIP_Real maxrelconsviol, /**< largest scaled violation among all violated expr-constraints, only used if in enforcement */
7711 SCIP_RESULT* result /**< pointer to store the result of the enforcing call */
7712 )
7713 {
7714 SCIP_CONSHDLRDATA* conshdlrdata;
7715 SCIP_EXPRITER* it;
7716 SCIP_Bool consenforced; /* whether any expression in constraint could be enforced */
7717 int c;
7718
7719 assert(conshdlr != NULL);
7720 assert(conss != NULL || nconss == 0);
7721 assert(result != NULL);
7722
7723 conshdlrdata = SCIPconshdlrGetData(conshdlr);
7724 assert(conshdlrdata != NULL);
7725
7726 /* increase tag to tell whether branching scores in expression belong to this sweep
7727 * and which expressions have already been enforced in this sweep
7728 * (we also want to distinguish sepa rounds, so this need to be here and not in consEnfo)
7729 */
7730 ++(conshdlrdata->enforound);
7731
7732 *result = SCIP_DIDNOTFIND;
7733
7734 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
7735 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, TRUE) );
7736
7737 for( c = 0; c < nconss; ++c )
7738 {
7739 assert(conss != NULL && conss[c] != NULL);
7740
7741 /* skip constraints that are not enabled or deleted */
7742 if( !SCIPconsIsEnabled(conss[c]) || SCIPconsIsDeleted(conss[c]) )
7743 continue;
7744 assert(SCIPconsIsActive(conss[c]));
7745
7746 /* skip constraints that have separation disabled if we are only in separation */
7747 if( !inenforcement && !SCIPconsIsSeparationEnabled(conss[c]) )
7748 continue;
7749
7750 /* skip non-violated constraints */
7751 if( !isConsViolated(scip, conss[c]) )
7752 continue;
7753
7754 ENFOLOG(
7755 {
7756 SCIP_CONSDATA* consdata;
7757 int i;
7758 consdata = SCIPconsGetData(conss[c]);
7759 assert(consdata != NULL);
7760 SCIPinfoMessage(scip, enfologfile, " constraint ");
7761 SCIP_CALL( SCIPprintCons(scip, conss[c], enfologfile) );
7762 SCIPinfoMessage(scip, enfologfile, "\n with viol %g and point\n", getConsAbsViolation(conss[c]));
7763 for( i = 0; i < consdata->nvarexprs; ++i )
7764 {
7765 SCIP_VAR* var;
7766 var = SCIPgetVarExprVar(consdata->varexprs[i]);
7767 SCIPinfoMessage(scip, enfologfile, " %-10s = %15g bounds: [%15g,%15g]\n", SCIPvarGetName(var),
7768 SCIPgetSolVal(scip, sol, var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var));
7769 }
7770 })
7771
7772 SCIP_CALL( enforceConstraint(scip, conshdlr, conss[c], sol, soltag, it, FALSE, inenforcement, result, &consenforced) );
7773
7774 if( *result == SCIP_CUTOFF )
7775 break;
7776
7777 if( !consenforced && inenforcement )
7778 {
7779 SCIP_Real viol;
7780
7781 SCIP_CALL( getConsRelViolation(scip, conss[c], &viol, sol, soltag) );
7782 if( viol > conshdlrdata->weakcutminviolfactor * maxrelconsviol )
7783 {
7784 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " constraint <%s> could not be enforced, try again with weak "\
7785 "cuts allowed\n", SCIPconsGetName(conss[c])); )
7786
7787 SCIP_CALL( enforceConstraint(scip, conshdlr, conss[c], sol, soltag, it, TRUE, inenforcement, result, &consenforced) );
7788
7789 if( consenforced )
7790 ++conshdlrdata->nweaksepa; /* TODO maybe this should not be counted per constraint, but per enforcement round? */
7791
7792 if( *result == SCIP_CUTOFF )
7793 break;
7794 }
7795 }
7796 }
7797
7798 SCIPfreeExpriter(&it);
7799
7800 ENFOLOG( if( enfologfile != NULL ) fflush( enfologfile); )
7801
7802 /* if having branching scores, then propagate them from expressions with children to variable expressions */
7803 if( *result == SCIP_BRANCHED )
7804 {
7805 /* having result set to branched here means only that we have branching candidates, we still need to do the actual
7806 * branching
7807 */
7808 SCIP_CALL( branching(scip, conshdlr, conss, nconss, maxrelconsviol, sol, soltag, result) );
7809
7810 /* branching should either have branched: result == SCIP_BRANCHED,
7811 * or fixed a variable: result == SCIP_REDUCEDDOM,
7812 * or have registered external branching candidates: result == SCIP_INFEASIBLE,
7813 * or have not done anything: result == SCIP_DIDNOTFIND
7814 */
7815 assert(*result == SCIP_BRANCHED || *result == SCIP_REDUCEDDOM || *result == SCIP_INFEASIBLE || *result == SCIP_DIDNOTFIND);
7816 }
7817
7818 ENFOLOG( if( enfologfile != NULL ) fflush( enfologfile); )
7819
7820 return SCIP_OKAY;
7821 }
7822
7823 /** collect (and print (if debugging enfo)) information on violation in expressions
7824 *
7825 * assumes that constraint violations have been computed
7826 */
7827 static
7828 SCIP_RETCODE analyzeViolation(
7829 SCIP* scip, /**< SCIP data structure */
7830 SCIP_CONS** conss, /**< constraints */
7831 int nconss, /**< number of constraints */
7832 SCIP_SOL* sol, /**< solution to separate, or NULL if LP solution should be used */
7833 SCIP_Longint soltag, /**< tag of solution */
7834 SCIP_Real* maxabsconsviol, /**< buffer to store maximal absolute violation of constraints */
7835 SCIP_Real* maxrelconsviol, /**< buffer to store maximal relative violation of constraints */
7836 SCIP_Real* minauxviol, /**< buffer to store minimal (nonzero) violation of auxiliaries */
7837 SCIP_Real* maxauxviol, /**< buffer to store maximal violation of auxiliaries (violation in "extended formulation") */
7838 SCIP_Real* maxvarboundviol /**< buffer to store maximal violation of variable bounds */
7839 )
7840 {
7841 SCIP_CONSDATA* consdata;
7842 SCIP_EXPRITER* it;
7843 SCIP_EXPR* expr;
7844 SCIP_Real v;
7845 int c;
7846
7847 assert(conss != NULL || nconss == 0);
7848 assert(maxabsconsviol != NULL);
7849 assert(maxrelconsviol != NULL);
7850 assert(maxauxviol != NULL);
7851 assert(maxvarboundviol != NULL);
7852
7853 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
7854 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) );
7855
7856 *maxabsconsviol = 0.0;
7857 *maxrelconsviol = 0.0;
7858 *minauxviol = SCIPinfinity(scip);
7859 *maxauxviol = 0.0;
7860 *maxvarboundviol = 0.0;
7861
7862 for( c = 0; c < nconss; ++c )
7863 {
7864 assert(conss != NULL && conss[c] != NULL);
7865
7866 consdata = SCIPconsGetData(conss[c]);
7867 assert(consdata != NULL);
7868
7869 /* skip constraints that are not enabled, deleted, or have separation disabled */
7870 if( !SCIPconsIsEnabled(conss[c]) || SCIPconsIsDeleted(conss[c]) || !SCIPconsIsSeparationEnabled(conss[c]) )
7871 continue;
7872 assert(SCIPconsIsActive(conss[c]));
7873
7874 v = getConsAbsViolation(conss[c]);
7875 *maxabsconsviol = MAX(*maxabsconsviol, v);
7876
7877 /* skip non-violated constraints */
7878 if( !isConsViolated(scip, conss[c]) )
7879 continue;
7880
7881 SCIP_CALL( getConsRelViolation(scip, conss[c], &v, sol, soltag) );
7882 *maxrelconsviol = MAX(*maxrelconsviol, v);
7883
7884 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
7885 {
7886 SCIP_EXPR_OWNERDATA* ownerdata;
7887 SCIP_Real auxvarvalue;
7888 SCIP_Real auxvarlb;
7889 SCIP_Real auxvarub;
7890 SCIP_Bool violunder;
7891 SCIP_Bool violover;
7892 SCIP_Real origviol;
7893 SCIP_Real auxviol;
7894 int e;
7895
7896 ownerdata = SCIPexprGetOwnerData(expr);
7897 assert(ownerdata != NULL);
7898
7899 if( ownerdata->auxvar == NULL )
7900 {
7901 /* check violation of variable bounds of original variable */
7902 if( SCIPisExprVar(scip, expr) )
7903 {
7904 SCIP_VAR* var;
7905 var = SCIPgetVarExprVar(expr);
7906 auxvarvalue = SCIPgetSolVal(scip, sol, var);
7907 auxvarlb = SCIPvarGetLbLocal(var);
7908 auxvarub = SCIPvarGetUbLocal(var);
7909
7910 origviol = 0.0;
7911 if( auxvarlb > auxvarvalue && !SCIPisInfinity(scip, -auxvarlb) )
7912 origviol = auxvarlb - auxvarvalue;
7913 else if( auxvarub < auxvarvalue && !SCIPisInfinity(scip, auxvarub) )
7914 origviol = auxvarvalue - auxvarub;
7915 if( origviol <= 0.0 )
7916 continue;
7917
7918 *maxvarboundviol = MAX(*maxvarboundviol, origviol);
7919
7920 ENFOLOG(
7921 SCIPinfoMessage(scip, enfologfile, "var <%s>[%.15g,%.15g] = %.15g", SCIPvarGetName(var), auxvarlb, auxvarub, auxvarvalue);
7922 if( auxvarlb > auxvarvalue && !SCIPisInfinity(scip, -auxvarlb) )
7923 SCIPinfoMessage(scip, enfologfile, " var >= lb violated by %g", auxvarlb - auxvarvalue);
7924 if( auxvarub < auxvarvalue && !SCIPisInfinity(scip, auxvarub) )
7925 SCIPinfoMessage(scip, enfologfile, " var <= ub violated by %g", auxvarvalue - auxvarub);
7926 SCIPinfoMessage(scip, enfologfile, "\n");
7927 )
7928 }
7929
7930 continue;
7931 }
7932
7933 auxvarvalue = SCIPgetSolVal(scip, sol, ownerdata->auxvar);
7934 auxvarlb = SCIPvarGetLbLocal(ownerdata->auxvar);
7935 auxvarub = SCIPvarGetUbLocal(ownerdata->auxvar);
7936
7937 /* check violation of variable bounds of auxiliary variable */
7938 if( auxvarlb - auxvarvalue > *maxvarboundviol && !SCIPisInfinity(scip, -auxvarlb) )
7939 *maxvarboundviol = auxvarlb - auxvarvalue;
7940 else if( auxvarvalue - auxvarub > *maxvarboundviol && !SCIPisInfinity(scip, auxvarub) )
7941 *maxvarboundviol = auxvarvalue - auxvarub;
7942
7943 origviol = getExprAbsOrigViolation(scip, expr, sol, &violunder, &violover);
7944
7945 ENFOLOG(
7946 if( origviol > 0.0 || auxvarlb > auxvarvalue || auxvarub < auxvarvalue )
7947 {
7948 SCIPinfoMessage(scip, enfologfile, "expr ");
7949 SCIP_CALL( SCIPprintExpr(scip, expr, enfologfile) );
7950 SCIPinfoMessage(scip, enfologfile, " (%p)[%.15g,%.15g] = %.15g\n", (void*)expr, SCIPexprGetActivity(expr).inf, SCIPexprGetActivity(expr).sup, SCIPexprGetEvalValue(expr));
7951
7952 SCIPinfoMessage(scip, enfologfile, " auxvar <%s>[%.15g,%.15g] = %.15g", SCIPvarGetName(ownerdata->auxvar), auxvarlb, auxvarub, auxvarvalue);
7953 if( origviol > 0.0 )
7954 SCIPinfoMessage(scip, enfologfile, " auxvar %s expr violated by %g", violunder ? ">=" : "<=", origviol);
7955 if( auxvarlb > auxvarvalue && !SCIPisInfinity(scip, -auxvarlb) )
7956 SCIPinfoMessage(scip, enfologfile, " auxvar >= auxvar's lb violated by %g", auxvarlb - auxvarvalue);
7957 if( auxvarub < auxvarvalue && !SCIPisInfinity(scip, auxvarub) )
7958 SCIPinfoMessage(scip, enfologfile, " auxvar <= auxvar's ub violated by %g", auxvarvalue - auxvarub);
7959 SCIPinfoMessage(scip, enfologfile, "\n");
7960 }
7961 )
7962
7963 /* no violation w.r.t. the original variables -> skip expression */
7964 if( origviol == 0.0 )
7965 continue;
7966
7967 /* compute aux-violation for each nonlinear handlers */
7968 for( e = 0; e < ownerdata->nenfos; ++e )
7969 {
7970 SCIP_NLHDLR* nlhdlr;
7971
7972 /* eval in auxvars is only defined for nlhdrs that separate; there might not even be auxvars otherwise */
7973 if( (ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPABOTH) == 0 )
7974 continue;
7975
7976 nlhdlr = ownerdata->enfos[e]->nlhdlr;
7977 assert(nlhdlr != NULL);
7978
7979 /* evaluate the expression w.r.t. the nlhdlrs auxiliary variables */
7980 SCIP_CALL( SCIPnlhdlrEvalaux(scip, nlhdlr, expr, ownerdata->enfos[e]->nlhdlrexprdata, &ownerdata->enfos[e]->auxvalue, sol) );
7981
7982 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " nlhdlr <%s> = %.15g", SCIPnlhdlrGetName(nlhdlr), ownerdata->enfos[e]->auxvalue); )
7983
7984 auxviol = getExprAbsAuxViolation(scip, expr, ownerdata->enfos[e]->auxvalue, sol, &violunder, &violover);
7985
7986 if( auxviol > 0.0 )
7987 {
7988 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " auxvar %s nlhdlr-expr violated by %g", violover ? "<=" : ">=", auxviol); )
7989 *maxauxviol = MAX(*maxauxviol, auxviol);
7990 *minauxviol = MIN(*minauxviol, auxviol);
7991 }
7992 ENFOLOG( SCIPinfoMessage(scip, enfologfile, "\n"); )
7993 }
7994 }
7995 }
7996
7997 SCIPfreeExpriter(&it);
7998
7999 return SCIP_OKAY;
8000 } /*lint !e715*/
8001
8002 /** enforcement of constraints called by enfolp and enforelax */
8003 static
8004 SCIP_RETCODE consEnfo(
8005 SCIP* scip, /**< SCIP data structure */
8006 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
8007 SCIP_CONS** conss, /**< constraints to process */
8008 int nconss, /**< number of constraints */
8009 SCIP_SOL* sol, /**< solution to enforce (NULL for the LP solution) */
8010 SCIP_RESULT* result /**< pointer to store the result of the enforcing call */
8011 )
8012 {
8013 SCIP_CONSHDLRDATA* conshdlrdata;
8014 SCIP_Real maxabsconsviol;
8015 SCIP_Real maxrelconsviol;
8016 SCIP_Real minauxviol;
8017 SCIP_Real maxauxviol;
8018 SCIP_Real maxvarboundviol;
8019 SCIP_Longint soltag;
8020 int nnotify;
8021 int c;
8022
8023 conshdlrdata = SCIPconshdlrGetData(conshdlr);
8024 assert(conshdlr != NULL);
8025
8026 soltag = SCIPgetExprNewSoltag(scip);
8027
8028 *result = SCIP_FEASIBLE;
8029 for( c = 0; c < nconss; ++c )
8030 {
8031 SCIP_CALL( computeViolation(scip, conss[c], sol, soltag) );
8032
8033 if( isConsViolated(scip, conss[c]) )
8034 *result = SCIP_INFEASIBLE;
8035 }
8036
8037 if( *result == SCIP_FEASIBLE )
8038 {
8039 ENFOLOG( SCIPinfoMessage(scip, enfologfile, "node %lld: all expr-constraints feasible, skip enforcing\n",
8040 SCIPnodeGetNumber(SCIPgetCurrentNode(scip))); )
8041 return SCIP_OKAY;
8042 }
8043
8044 SCIP_CALL( analyzeViolation(scip, conss, nconss, sol, soltag, &maxabsconsviol, &maxrelconsviol,
8045 &minauxviol, &maxauxviol, &maxvarboundviol) );
8046
8047 ENFOLOG( SCIPinfoMessage(scip, enfologfile, "node %lld: enforcing constraints with max conssviol=%e (rel=%e), "\
8048 "auxviolations in %g..%g, variable bounds violated by at most %g, LP feastol=%e\n",
8049 SCIPnodeGetNumber(SCIPgetCurrentNode(scip)), maxabsconsviol, maxrelconsviol, minauxviol, maxauxviol,
8050 maxvarboundviol, SCIPgetLPFeastol(scip)); )
8051
8052 assert(maxvarboundviol <= SCIPgetLPFeastol(scip));
8053
8054 /* try to propagate */
8055 if( conshdlrdata->propinenforce )
8056 {
8057 SCIP_RESULT propresult;
8058 int nchgbds = 0;
8059
8060 SCIP_CALL( propConss(scip, conshdlr, conss, nconss, TRUE, &propresult, &nchgbds) );
8061
8062 if( propresult == SCIP_CUTOFF || propresult == SCIP_REDUCEDDOM )
8063 {
8064 *result = propresult;
8065 return SCIP_OKAY;
8066 }
8067 }
8068
8069 /* tighten the LP tolerance if violation in variables bounds is larger than aux-violation (max |expr - auxvar| over
8070 * all violated expr/auxvar in violated constraints)
8071 */
8072 if( conshdlrdata->tightenlpfeastol && maxvarboundviol > maxauxviol && SCIPisPositive(scip, SCIPgetLPFeastol(scip)) &&
8073 sol == NULL )
8074 {
8075 SCIPsetLPFeastol(scip, MAX(SCIPepsilon(scip), MIN(maxvarboundviol / 2.0, SCIPgetLPFeastol(scip) / 2.0)));
8076 ++conshdlrdata->ntightenlp;
8077
8078 *result = SCIP_SOLVELP;
8079
8080 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " variable bound violation %g larger than auxiliary violation %g, "\
8081 "reducing LP feastol to %g\n", maxvarboundviol, maxauxviol, SCIPgetLPFeastol(scip)); )
8082
8083 return SCIP_OKAY;
8084 }
8085
8086 /* tighten the LP tolerance if violation in auxiliaries is below LP feastol, as we could have problems to find a cut
8087 * with violation above LP tolerance (especially when auxviolation is below 10*eps = ROWPREP_SCALEUP_VIOLNONZERO in misc_rowprep.c)
8088 */
8089 if( conshdlrdata->tightenlpfeastol && maxauxviol < SCIPgetLPFeastol(scip) && SCIPisPositive(scip, SCIPgetLPFeastol(scip)) && sol == NULL )
8090 {
8091 SCIPsetLPFeastol(scip, MAX(SCIPepsilon(scip), maxauxviol/2.0));
8092 ++conshdlrdata->ntightenlp;
8093
8094 *result = SCIP_SOLVELP;
8095
8096 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " auxiliary violation %g below LP feastol, reducing LP feastol to %g\n", maxauxviol, SCIPgetLPFeastol(scip)); )
8097
8098 return SCIP_OKAY;
8099 }
8100
8101 SCIP_CALL( enforceConstraints(scip, conshdlr, conss, nconss, sol, soltag, TRUE, maxrelconsviol, result) );
8102
8103 if( *result == SCIP_CUTOFF || *result == SCIP_SEPARATED || *result == SCIP_REDUCEDDOM || *result == SCIP_BRANCHED ||
8104 *result == SCIP_INFEASIBLE )
8105 return SCIP_OKAY;
8106
8107 assert(*result == SCIP_DIDNOTFIND);
8108
8109 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " could not enforce violation %g in regular ways, LP feastol=%g, "\
8110 "becoming desperate now...\n", maxabsconsviol, SCIPgetLPFeastol(scip)); )
8111
8112 if( conshdlrdata->tightenlpfeastol && SCIPisPositive(scip, maxvarboundviol) && SCIPisPositive(scip, SCIPgetLPFeastol(scip)) && sol == NULL )
8113 {
8114 SCIPsetLPFeastol(scip, MAX(SCIPepsilon(scip), MIN(maxvarboundviol / 2.0, SCIPgetLPFeastol(scip) / 2.0)));
8115 ++conshdlrdata->ntightenlp;
8116
8117 *result = SCIP_SOLVELP;
8118
8119 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " variable bounds are violated by more than eps, reduced LP "\
8120 "feasibility tolerance to %g\n", SCIPgetLPFeastol(scip)); )
8121
8122 return SCIP_OKAY;
8123 }
8124
8125 if( conshdlrdata->tightenlpfeastol && SCIPisPositive(scip, maxauxviol) && SCIPisPositive(scip,
8126 SCIPgetLPFeastol(scip)) && sol == NULL )
8127 {
8128 /* try whether tighten the LP feasibility tolerance could help
8129 * maybe it is just some cut that hasn't been taken into account sufficiently
8130 * in the next enforcement round, we would then also allow even weaker cuts, as we want a minimal cut violation of LP's feastol
8131 * unfortunately, we do not know the current LP solution primal infeasibility, so sometimes this just repeats without effect
8132 * until the LP feastol reaches epsilon
8133 * (this is similar to the "tighten the LP tolerance if violation in auxiliaries is below LP feastol..." case above, but applies
8134 * when maxauxviol is above LP feastol)
8135 */
8136 SCIPsetLPFeastol(scip, MAX(SCIPepsilon(scip), MIN(maxauxviol / 2.0, SCIPgetLPFeastol(scip) / 10.0)));
8137 ++conshdlrdata->ndesperatetightenlp;
8138
8139 *result = SCIP_SOLVELP;
8140
8141 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " reduced LP feasibility tolerance to %g and hope\n", SCIPgetLPFeastol(scip)); )
8142
8143 return SCIP_OKAY;
8144 }
8145
8146 /* try to propagate, if not tried above TODO(?) allow to disable this as well */
8147 if( !conshdlrdata->propinenforce )
8148 {
8149 SCIP_RESULT propresult;
8150 int nchgbds = 0;
8151
8152 SCIP_CALL( propConss(scip, conshdlr, conss, nconss, TRUE, &propresult, &nchgbds) );
8153
8154 if( propresult == SCIP_CUTOFF || propresult == SCIP_REDUCEDDOM )
8155 {
8156 *result = propresult;
8157 return SCIP_OKAY;
8158 }
8159 }
8160
8161 /* could not find branching candidates even when looking at minimal violated (>eps) expressions
8162 * now look if we find any unfixed variable that we could still branch on
8163 */
8164 SCIP_CALL( registerBranchingCandidatesAllUnfixed(scip, conshdlr, conss, nconss, &nnotify) );
8165
8166 if( nnotify > 0 )
8167 {
8168 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " registered %d unfixed variables as branching candidates\n", nnotify); )
8169 ++conshdlrdata->ndesperatebranch;
8170
8171 *result = SCIP_INFEASIBLE; /* enforceConstraints may have changed it to SCIP_DIDNOTFIND */
8172
8173 return SCIP_OKAY;
8174 }
8175
8176 /* if everything is fixed in violated constraints, then let's cut off the node
8177 * - bound tightening with all vars fixed should prove cutoff, but interval arithmetic overestimates and so the
8178 * result may not be conclusive (when constraint violations are small)
8179 * - if tightenlpfeastol=FALSE, then the LP solution that we try to enforce here may just not be within bounds
8180 * sufficiently (see st_e40)
8181 * - but if the LP solution is really within bounds and since variables are fixed, cutting off the node is actually
8182 * not "desperate", but a pretty obvious thing to do
8183 */
8184 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " enforcement with max. violation %g failed; cutting off node\n", maxabsconsviol); )
8185 *result = SCIP_CUTOFF;
8186
8187 /* it's only "desperate" if the LP solution does not coincide with variable fixings (should we use something tighter than epsilon here?) */
8188 if( !SCIPisZero(scip, maxvarboundviol) )
8189 ++conshdlrdata->ndesperatecutoff;
8190
8191 return SCIP_OKAY;
8192 }
8193
8194 /** separation for all violated constraints to be used by SEPA callbacks */
8195 static
8196 SCIP_RETCODE consSepa(
8197 SCIP* scip, /**< SCIP data structure */
8198 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
8199 SCIP_CONS** conss, /**< constraints to process */
8200 int nconss, /**< number of constraints */
8201 SCIP_SOL* sol, /**< solution to enforce (NULL for the LP solution) */
8202 SCIP_RESULT* result /**< pointer to store the result of the enforcing call */
8203 )
8204 {
8205 SCIP_Longint soltag;
8206 SCIP_Bool haveviol = FALSE;
8207 int c;
8208
8209 *result = SCIP_DIDNOTFIND;
8210
8211 soltag = SCIPgetExprNewSoltag(scip);
8212
8213 /* compute violations */
8214 for( c = 0; c < nconss; ++c )
8215 {
8216 assert(conss[c] != NULL);
8217
8218 /* skip constraints that are not enabled, deleted, or have separation disabled */
8219 if( !SCIPconsIsEnabled(conss[c]) || SCIPconsIsDeleted(conss[c]) || !SCIPconsIsSeparationEnabled(conss[c]) )
8220 continue;
8221 assert(SCIPconsIsActive(conss[c]));
8222
8223 SCIP_CALL( computeViolation(scip, conss[c], sol, soltag) );
8224
8225 if( isConsViolated(scip, conss[c]) )
8226 haveviol = TRUE;
8227 }
8228
8229 /* if none of our constraints are violated, don't attempt separation */
8230 if( !haveviol )
8231 {
8232 ENFOLOG( SCIPinfoMessage(scip, enfologfile, "node %lld: skip separation of non-violated constraints\n", SCIPnodeGetNumber(SCIPgetCurrentNode(scip))); )
8233 return SCIP_OKAY;
8234 }
8235
8236 ENFOLOG( SCIPinfoMessage(scip, enfologfile, "node %lld: separation\n", SCIPnodeGetNumber(SCIPgetCurrentNode(scip))); )
8237
8238 /* call separation */
8239 SCIP_CALL( enforceConstraints(scip, conshdlr, conss, nconss, sol, soltag, FALSE, SCIP_INVALID, result) );
8240
8241 return SCIP_OKAY;
8242 }
8243
8244 /** hash key retrieval function for bilinear term entries */
8245 static
8246 SCIP_DECL_HASHGETKEY(bilinearTermsGetHashkey)
8247 { /*lint --e{715}*/
8248 SCIP_CONSHDLRDATA* conshdlrdata;
8249 int idx;
8250
8251 conshdlrdata = (SCIP_CONSHDLRDATA*)userptr;
8252 assert(conshdlrdata != NULL);
8253
8254 idx = ((int)(size_t)elem) - 1;
8255 assert(idx >= 0 && idx < conshdlrdata->nbilinterms);
8256
8257 return (void*)&conshdlrdata->bilinterms[idx];
8258 }
8259
8260 /** returns TRUE iff the bilinear term entries are equal */
8261 static
8262 SCIP_DECL_HASHKEYEQ(bilinearTermsIsHashkeyEq)
8263 { /*lint --e{715}*/
8264 SCIP_CONSNONLINEAR_BILINTERM* entry1;
8265 SCIP_CONSNONLINEAR_BILINTERM* entry2;
8266
8267 /* get corresponding entries */
8268 entry1 = (SCIP_CONSNONLINEAR_BILINTERM*)key1;
8269 entry2 = (SCIP_CONSNONLINEAR_BILINTERM*)key2;
8270 assert(entry1->x != NULL && entry1->y != NULL);
8271 assert(entry2->x != NULL && entry2->y != NULL);
8272 assert(SCIPvarCompare(entry1->x, entry1->y) < 1);
8273 assert(SCIPvarCompare(entry2->x, entry2->y) < 1);
8274
8275 return entry1->x == entry2->x && entry1->y == entry2->y;
8276 }
8277
8278 /** returns the hash value of the key */
8279 static
8280 SCIP_DECL_HASHKEYVAL(bilinearTermsGetHashkeyVal)
8281 { /*lint --e{715}*/
8282 SCIP_CONSNONLINEAR_BILINTERM* entry;
8283
8284 entry = (SCIP_CONSNONLINEAR_BILINTERM*)key;
8285 assert(entry->x != NULL && entry->y != NULL);
8286 assert(SCIPvarCompare(entry->x, entry->y) < 1);
8287
8288 return SCIPhashTwo(SCIPvarGetIndex(entry->x), SCIPvarGetIndex(entry->y));
8289 }
8290
8291 /** compare two auxiliary expressions
8292 *
8293 * Compares auxiliary variables, followed by coefficients, and then constants.
8294 */
8295 static
8296 SCIP_DECL_SORTPTRCOMP(auxexprComp)
8297 {
8298 SCIP_CONSNONLINEAR_AUXEXPR* auxexpr1 = (SCIP_CONSNONLINEAR_AUXEXPR*)elem1;
8299 SCIP_CONSNONLINEAR_AUXEXPR* auxexpr2 = (SCIP_CONSNONLINEAR_AUXEXPR*)elem2;
8300 int compvars;
8301 int i;
8302
8303 /* compare the auxiliary variables */
8304 compvars = SCIPvarCompare(auxexpr1->auxvar, auxexpr2->auxvar); /* TODO can one of these be NULL? */
8305
8306 if( compvars != 0 )
8307 return compvars;
8308
8309 /* compare the coefficients and constants */
8310 for( i = 0; i < 3; ++i )
8311 {
8312 if( auxexpr1->coefs[i] != auxexpr2->coefs[i] )
8313 return auxexpr1->coefs[i] < auxexpr2->coefs[i] ? -1 : 1;
8314 }
8315
8316 return auxexpr1->cst < auxexpr2->cst ? -1 : auxexpr1->cst == auxexpr2->cst ? 0 : 1;
8317 }
8318
8319 /* add an auxiliary expression to a bilinear term */
8320 static
8321 SCIP_RETCODE bilinTermAddAuxExpr(
8322 SCIP* scip, /**< SCIP data structure */
8323 SCIP_CONSHDLRDATA* conshdlrdata, /**< nonlinear constraint handler data */
8324 SCIP_CONSNONLINEAR_BILINTERM* term, /**< bilinear term */
8325 SCIP_CONSNONLINEAR_AUXEXPR* auxexpr, /**< auxiliary expression to add */
8326 SCIP_Bool* added /**< pointer to store whether auxexpr has been added */
8327 )
8328 {
8329 SCIP_Bool found;
8330 int pos;
8331 int i;
8332
8333 *added = FALSE;
8334
8335 /* check if auxexpr has already been added to term */
8336 if( term->nauxexprs == 0 )
8337 {
8338 found = FALSE;
8339 pos = 0;
8340 }
8341 else
8342 {
8343 found = SCIPsortedvecFindPtr((void**)term->aux.exprs, auxexprComp, auxexpr, term->nauxexprs, &pos);
8344 }
8345
8346 if( !found )
8347 {
8348 if( term->nauxexprs >= conshdlrdata->bilinmaxnauxexprs )
8349 return SCIP_OKAY;
8350
8351 SCIP_CALL( SCIPensureBlockMemoryArray(scip, &term->aux.exprs, &term->auxexprssize, term->nauxexprs + 1) );
8352 assert(term->auxexprssize >= term->nauxexprs + 1);
8353
8354 /* insert expression at the correct position */
8355 for( i = term->nauxexprs; i > pos; --i )
8356 {
8357 term->aux.exprs[i] = term->aux.exprs[i-1];
8358 }
8359 term->aux.exprs[pos] = auxexpr;
8360 ++(term->nauxexprs);
8361 *added = TRUE;
8362 }
8363 else
8364 {
8365 term->aux.exprs[pos]->underestimate |= auxexpr->underestimate;
8366 term->aux.exprs[pos]->overestimate |= auxexpr->overestimate;
8367 }
8368
8369 return SCIP_OKAY;
8370 }
8371
8372 /** iterates through all expressions of all nonlinear constraints and adds the corresponding bilinear terms to the hash table */
8373 static
8374 SCIP_RETCODE bilinearTermsInsertAll(
8375 SCIP* scip, /**< SCIP data structure */
8376 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
8377 SCIP_CONS** conss, /**< nonlinear constraints */
8378 int nconss /**< total number of nonlinear constraints */
8379 )
8380 {
8381 SCIP_CONSHDLRDATA* conshdlrdata;
8382 SCIP_EXPRITER* it;
8383 int c;
8384
8385 assert(conss != NULL || nconss == 0);
8386
8387 if( nconss == 0 )
8388 return SCIP_OKAY;
8389
8390 conshdlrdata = SCIPconshdlrGetData(conshdlr);
8391 assert(conshdlrdata != NULL);
8392
8393 /* check whether the bilinear terms have been stored already */
8394 if( conshdlrdata->bilinterms != NULL )
8395 return SCIP_OKAY;
8396
8397 /* create and initialize iterator */
8398 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
8399 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) );
8400 SCIPexpriterSetStagesDFS(it, SCIP_EXPRITER_ENTEREXPR);
8401
8402 /* iterate through all constraints */
8403 for( c = 0; c < nconss; ++c )
8404 {
8405 SCIP_CONSDATA* consdata;
8406 SCIP_EXPR* expr;
8407
8408 assert(conss != NULL && conss[c] != NULL);
8409 consdata = SCIPconsGetData(conss[c]);
8410 assert(consdata != NULL);
8411
8412 /* iterate through all expressions */
8413 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
8414 {
8415 SCIP_EXPR** children = SCIPexprGetChildren(expr);
8416 SCIP_VAR* x = NULL;
8417 SCIP_VAR* y = NULL;
8418
8419 /* check whether the expression is of the form f(..)^2 */
8420 if( SCIPisExprPower(scip, expr) && SCIPgetExponentExprPow(expr) == 2.0 )
8421 {
8422 x = SCIPgetExprAuxVarNonlinear(children[0]);
8423 y = x;
8424 }
8425 /* check whether the expression is of the form f(..) * g(..) */
8426 else if( SCIPisExprProduct(scip, expr) && SCIPexprGetNChildren(expr) == 2 )
8427 {
8428 x = SCIPgetExprAuxVarNonlinear(children[0]);
8429 y = SCIPgetExprAuxVarNonlinear(children[1]);
8430 }
8431
8432 /* add variables to the hash table */
8433 if( x != NULL && y != NULL )
8434 {
8435 SCIP_CALL( SCIPinsertBilinearTermExistingNonlinear(scip, conshdlr, x, y, SCIPgetExprAuxVarNonlinear(expr),
8436 SCIPgetExprNLocksPosNonlinear(expr), SCIPgetExprNLocksNegNonlinear(expr)) );
8437 }
8438 }
8439 }
8440
8441 /* release iterator */
8442 SCIPfreeExpriter(&it);
8443
8444 return SCIP_OKAY;
8445 }
8446
8447 /** store x, y and the locks in a new bilinear term */
8448 static
8449 SCIP_RETCODE bilinearTermsInsertEntry(
8450 SCIP* scip, /**< SCIP data structure */
8451 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */
8452 SCIP_VAR* x, /**< the first variable */
8453 SCIP_VAR* y, /**< the second variable */
8454 int nlockspos, /**< number of positive locks of the bilinear term */
8455 int nlocksneg, /**< number of negative locks of the bilinear term */
8456 int* idx, /**< pointer to store the position of the term in bilinterms array */
8457 SCIP_Bool existing /**< whether the term exists explicitly in the problem */
8458 )
8459 {
8460 SCIP_CONSHDLRDATA* conshdlrdata;
8461 SCIP_CONSNONLINEAR_BILINTERM* term;
8462
8463 assert(conshdlr != NULL);
8464 assert(x != NULL);
8465 assert(y != NULL);
8466 assert(nlockspos >= 0);
8467 assert(nlocksneg >= 0);
8468
8469 conshdlrdata = SCIPconshdlrGetData(conshdlr);
8470 assert(conshdlrdata != NULL);
8471
8472 /* ensure that x.index <= y.index */
8473 if( SCIPvarCompare(x, y) == 1 )
8474 {
8475 SCIPswapPointers((void**)&x, (void**)&y);
8476 }
8477 assert(SCIPvarCompare(x, y) < 1);
8478
8479 *idx = SCIPgetBilinTermIdxNonlinear(conshdlr, x, y);
8480
8481 /* update or create the term */
8482 if( *idx >= 0 )
8483 { /* the term has already been added */
8484 assert(conshdlrdata->bilinterms[*idx].x == x);
8485 assert(conshdlrdata->bilinterms[*idx].y == y);
8486
8487 /* get term and add locks */
8488 term = &conshdlrdata->bilinterms[*idx];
8489 assert(existing <= term->existing); /* implicit terms are added after existing ones */
8490 term->nlockspos += nlockspos;
8491 term->nlocksneg += nlocksneg;
8492 }
8493 else
8494 { /* this is the first time we encounter this product */
8495 /* ensure size of bilinterms array */
8496 SCIP_CALL( SCIPensureBlockMemoryArray(scip, &conshdlrdata->bilinterms, &conshdlrdata->bilintermssize, conshdlrdata->nbilinterms + 1) );
8497
8498 *idx = conshdlrdata->nbilinterms;
8499
8500 /* get term and set values in the created bilinear term */
8501 term = &conshdlrdata->bilinterms[*idx];
8502 assert(term != NULL);
8503 term->x = x;
8504 term->y = y;
8505 term->nauxexprs = 0;
8506 term->auxexprssize = 0;
8507 term->nlockspos = nlockspos;
8508 term->nlocksneg = nlocksneg;
8509 term->existing = existing;
8510 if( existing )
8511 term->aux.var = NULL;
8512 else
8513 term->aux.exprs = NULL;
8514
8515 /* increase the total number of bilinear terms */
8516 ++(conshdlrdata->nbilinterms);
8517
8518 /* save to the hashtable */
8519 if( conshdlrdata->bilinhashtable == NULL )
8520 {
8521 SCIP_CALL( SCIPhashtableCreate(&conshdlrdata->bilinhashtable, SCIPblkmem(scip), conshdlrdata->nbilinterms,
8522 bilinearTermsGetHashkey, bilinearTermsIsHashkeyEq, bilinearTermsGetHashkeyVal,
8523 (void*)conshdlrdata) );
8524 }
8525 assert(conshdlrdata->bilinhashtable != NULL);
8526
8527 /* insert the index of the bilinear term into the hash table; note that the index of the i-th element is (i+1)
8528 * because zero can not be inserted into hash table
8529 */
8530 SCIP_CALL( SCIPhashtableInsert(conshdlrdata->bilinhashtable, (void*)(size_t)(*idx + 1)) ); /*lint !e571 !e776*/
8531
8532 /* capture product variables */
8533 SCIP_CALL( SCIPcaptureVar(scip, x) );
8534 SCIP_CALL( SCIPcaptureVar(scip, y) );
8535 }
8536
8537 return SCIP_OKAY;
8538 }
8539
8540 /** frees array of bilinear terms and hash table */
8541 static
8542 SCIP_RETCODE bilinearTermsFree(
8543 SCIP* scip, /**< SCIP data structure */
8544 SCIP_CONSHDLRDATA* conshdlrdata /**< constraint handler data */
8545 )
8546 {
8547 int i;
8548 int j;
8549
8550 assert(conshdlrdata != NULL);
8551
8552 /* check whether bilinear terms have been stored */
8553 if( conshdlrdata->bilinterms == NULL )
8554 {
8555 assert(conshdlrdata->bilinterms == NULL);
8556 assert(conshdlrdata->nbilinterms == 0);
8557 assert(conshdlrdata->bilintermssize == 0);
8558
8559 return SCIP_OKAY;
8560 }
8561
8562 /* release variables */
8563 for( i = 0; i < conshdlrdata->nbilinterms; ++i )
8564 {
8565 SCIP_CALL( SCIPreleaseVar(scip, &conshdlrdata->bilinterms[i].y) );
8566 SCIP_CALL( SCIPreleaseVar(scip, &conshdlrdata->bilinterms[i].x) );
8567
8568 for( j = 0; j < conshdlrdata->bilinterms[i].nauxexprs; ++j )
8569 {
8570 if( conshdlrdata->bilinterms[i].aux.exprs[j]->auxvar != NULL )
8571 {
8572 SCIP_CALL( SCIPreleaseVar(scip, &conshdlrdata->bilinterms[i].aux.exprs[j]->auxvar) );
8573 }
8574 SCIPfreeBlockMemory(scip, &(conshdlrdata->bilinterms[i].aux.exprs[j]));
8575 }
8576
8577 if( conshdlrdata->bilinterms[i].nauxexprs > 0 )
8578 {
8579 SCIPfreeBlockMemoryArray(scip, &(conshdlrdata->bilinterms[i].aux.exprs), conshdlrdata->bilinterms[i].auxexprssize);
8580 continue;
8581 }
8582
8583 /* the rest is for simple terms with a single auxvar */
8584
8585 /* it might be that there is a bilinear term without a corresponding auxiliary variable */
8586 if( conshdlrdata->bilinterms[i].aux.var != NULL )
8587 {
8588 SCIP_CALL( SCIPreleaseVar(scip, &conshdlrdata->bilinterms[i].aux.var) );
8589 }
8590 }
8591
8592 /* free hash table */
8593 if( conshdlrdata->bilinhashtable != NULL )
8594 {
8595 SCIPhashtableFree(&conshdlrdata->bilinhashtable);
8596 }
8597
8598 /* free bilinterms array; reset counters */
8599 SCIPfreeBlockMemoryArrayNull(scip, &conshdlrdata->bilinterms, conshdlrdata->bilintermssize);
8600 conshdlrdata->nbilinterms = 0;
8601 conshdlrdata->bilintermssize = 0;
8602
8603 return SCIP_OKAY;
8604 }
8605
8606 /*
8607 * vertex polyhedral separation
8608 */
8609
8610 /** builds LP used to compute facets of the convex envelope of vertex-polyhedral functions */
8611 static
8612 SCIP_RETCODE buildVertexPolyhedralSeparationLP(
8613 SCIP* scip, /**< SCIP data structure */
8614 int nvars, /**< number of (unfixed) variables in vertex-polyhedral functions */
8615 SCIP_LPI** lp /**< pointer to store created LP */
8616 )
8617 {
8618 SCIP_Real* obj;
8619 SCIP_Real* lb;
8620 SCIP_Real* ub;
8621 SCIP_Real* val;
8622 int* beg;
8623 int* ind;
8624 unsigned int nnonz;
8625 unsigned int ncols;
8626 unsigned int nrows;
8627 unsigned int i;
8628 unsigned int k;
8629
8630 assert(scip != NULL);
8631 assert(lp != NULL);
8632 assert(nvars > 0);
8633 assert(nvars <= SCIP_MAXVERTEXPOLYDIM);
8634
8635 SCIPdebugMsg(scip, "Building LP for computing facets of convex envelope of vertex-polyhedral function\n");
8636
8637 /* create lpi to store the LP */
8638 SCIP_CALL( SCIPlpiCreate(lp, SCIPgetMessagehdlr(scip), "facet finding LP", SCIP_OBJSEN_MINIMIZE) );
8639
8640 nrows = (unsigned int)nvars + 1;
8641 ncols = POWEROFTWO((unsigned int)nvars);
8642 nnonz = (ncols * (nrows + 1)) / 2;
8643
8644 /* allocate necessary memory; set obj, lb, and ub to zero */
8645 SCIP_CALL( SCIPallocClearBufferArray(scip, &obj, ncols) );
8646 SCIP_CALL( SCIPallocClearBufferArray(scip, &lb, ncols) );
8647 SCIP_CALL( SCIPallocBufferArray(scip, &ub, ncols) );
8648 SCIP_CALL( SCIPallocBufferArray(scip, &beg, ncols) );
8649 SCIP_CALL( SCIPallocBufferArray(scip, &val, nnonz) );
8650 SCIP_CALL( SCIPallocBufferArray(scip, &ind, nnonz) );
8651
8652 /* calculate nonzero entries in the LP */
8653 for( i = 0, k = 0; i < ncols; ++i )
8654 {
8655 int row;
8656 unsigned int a;
8657
8658 /* an upper bound of 1.0 is implied by the last row, but I presume that LP solvers prefer unbounded variables */
8659 ub[i] = SCIPlpiInfinity(*lp);
8660
8661 SCIPdebugMsg(scip, "col %u starts at position %u\n", i, k);
8662 beg[i] = (int)k;
8663 row = 0;
8664
8665 /* iterate through the bit representation of i */
8666 a = 1;
8667 while( a <= i )
8668 {
8669 if( (a & i) != 0 )
8670 {
8671 val[k] = 1.0;
8672 ind[k] = row;
8673
8674 SCIPdebugMsg(scip, " val[%d][%u] = 1 (position %u)\n", row, i, k);
8675
8676 ++k;
8677 }
8678
8679 a <<= 1;
8680 ++row;
8681 assert(0 <= row && row <= SCIP_MAXVERTEXPOLYDIM);
8682 assert(POWEROFTWO(row) == a);
8683 }
8684
8685 /* put 1 as a coefficient for sum_{i} \lambda_i = 1 row (last row) */
8686 val[k] = 1.0;
8687 ind[k] = (int)nrows - 1;
8688 ++k;
8689 SCIPdebugMsg(scip, " val[%u][%u] = 1 (position %u)\n", nrows - 1, i, k);
8690 }
8691 assert(k == nnonz);
8692
8693 /* load all data into LP interface
8694 * we can assume nrows (=nvars+1) <= ncols (=2^nvars), so we can pass lb as dummy lhs and rhs
8695 */
8696 assert(nrows <= ncols);
8697 SCIP_CALL( SCIPlpiLoadColLP(*lp, SCIP_OBJSEN_MINIMIZE,
8698 (int)ncols, obj, lb, ub, NULL,
8699 (int)nrows, lb, lb, NULL,
8700 (int)nnonz, beg, ind, val) );
8701
8702 /* for the last row, we can set the rhs to 1.0 already */
8703 ind[0] = (int)nrows - 1;
8704 val[0] = 1.0;
8705 SCIP_CALL( SCIPlpiChgSides(*lp, 1, ind, val, val) );
8706
8707 /* free allocated memory */
8708 SCIPfreeBufferArray(scip, &ind);
8709 SCIPfreeBufferArray(scip, &val);
8710 SCIPfreeBufferArray(scip, &beg);
8711 SCIPfreeBufferArray(scip, &ub);
8712 SCIPfreeBufferArray(scip, &lb);
8713 SCIPfreeBufferArray(scip, &obj);
8714
8715 return SCIP_OKAY;
8716 }
8717
8718 /** the given facet might not be a valid under(over)estimator, because of numerics and bad fixings; we compute \f$
8719 * \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
8720 * set of vertices of the domain
8721 */
8722 static
8723 SCIP_Real computeVertexPolyhedralMaxFacetError(
8724 SCIP* scip, /**< SCIP data structure */
8725 SCIP_Bool overestimate, /**< whether we check for an over or underestimator */
8726 SCIP_Real* funvals, /**< array containing the evaluation of the function at all corners, length: 2^nvars */
8727 SCIP_Real* box, /**< box for which facet was computed, length: 2*nallvars */
8728 int nallvars, /**< number of all variables */
8729 int nvars, /**< number of unfixed variables */
8730 int* nonfixedpos, /**< indices of unfixed variables, length: nvars */
8731 SCIP_Real* facetcoefs, /**< current facet candidate's coefficients, length: nallvars */
8732 SCIP_Real facetconstant /**< current facet candidate's constant, length: nallvars */
8733 )
8734 {
8735 SCIP_Real maxerror;
8736 SCIP_Real facetval;
8737 SCIP_Real funval;
8738 SCIP_Real error;
8739 unsigned int i;
8740 unsigned int ncorners;
8741 unsigned int prev;
8742
8743 assert(scip != NULL);
8744 assert(funvals != NULL);
8745 assert(box != NULL);
8746 assert(nonfixedpos != NULL);
8747 assert(facetcoefs != NULL);
8748
8749 ncorners = POWEROFTWO(nvars);
8750 maxerror = 0.0;
8751
8752 /* check the origin (all variables at lower bound) */
8753 facetval = facetconstant;
8754 for( i = 0; i < (unsigned int) nallvars; ++i )
8755 facetval += facetcoefs[i] * box[2*i];
8756
8757 /* compute largest/smallest possible value of function, depending on whether we are over/under-estimating */
8758 funval = funvals[0];
8759 if( overestimate )
8760 error = funval - facetval;
8761 else
8762 error = facetval - funval;
8763
8764 /* update maximum error */
8765 maxerror = MAX(error, maxerror);
8766
8767 prev = 0;
8768 for( i = 1; i < ncorners; ++i )
8769 {
8770 unsigned int gray;
8771 unsigned int diff;
8772 unsigned int pos;
8773 int origpos;
8774
8775 gray = i ^ (i >> 1);
8776 diff = gray ^ prev;
8777
8778 /* compute position of unique 1 of diff */
8779 pos = 0;
8780 while( (diff >>= 1) != 0 )
8781 ++pos;
8782 assert(pos < (unsigned int)nvars);
8783
8784 origpos = nonfixedpos[pos];
8785
8786 if( gray > prev )
8787 facetval += facetcoefs[origpos] * (box[2*origpos+1] - box[2*origpos]);
8788 else
8789 facetval -= facetcoefs[origpos] * (box[2*origpos+1] - box[2*origpos]);
8790
8791 /* compute largest/smallest possible value of function, depending on whether we are over/under-estimating */
8792 funval = funvals[gray];
8793 if( overestimate )
8794 error = funval - facetval;
8795 else
8796 error = facetval - funval;
8797
8798 /* update maximum error */
8799 maxerror = MAX(error, maxerror);
8800
8801 prev = gray;
8802 }
8803
8804 SCIPdebugMsg(scip, "maximum error of facet: %2.8e\n", maxerror);
8805
8806 return maxerror;
8807 }
8808
8809 /** computes a facet of the convex or concave envelope of a vertex polyhedral function by solving an LP */ /*lint -e{715}*/
8810 static
8811 SCIP_RETCODE computeVertexPolyhedralFacetLP(
8812 SCIP* scip, /**< SCIP data structure */
8813 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */
8814 SCIP_Bool overestimate, /**< whether to compute facet of concave (TRUE) or convex (FALSE) envelope */
8815 SCIP_Real* xstar, /**< point to be separated */
8816 SCIP_Real* box, /**< box where to compute facet: should be lb_1, ub_1, lb_2, ub_2... */
8817 int nallvars, /**< half of the length of box */
8818 int* nonfixedpos, /**< indices of nonfixed variables */
8819 SCIP_Real* funvals, /**< values of function in all corner points (w.r.t. nonfixed variables) */
8820 int nvars, /**< number of nonfixed variables */
8821 SCIP_Real targetvalue, /**< target value: no need to compute facet if value in xstar would be worse than this value */
8822 SCIP_Bool* success, /**< buffer to store whether a facet could be computed successfully */
8823 SCIP_Real* facetcoefs, /**< buffer to store coefficients of facet defining inequality; must be an zero'ed array of length at least nallvars */
8824 SCIP_Real* facetconstant /**< buffer to store constant part of facet defining inequality */
8825 )
8826 { /*lint --e{715}*/
8827 SCIP_CONSHDLRDATA* conshdlrdata;
8828 SCIP_LPI* lp;
8829 SCIP_Real* aux; /* used to transform x^* and then to store LP solution */
8830 int* inds;
8831 int ncols;
8832 int nrows;
8833 int i;
8834 SCIP_Real facetvalue;
8835 SCIP_Real mindomwidth;
8836 SCIP_RETCODE lpsolveretcode;
8837
8838 assert(scip != NULL);
8839 assert(conshdlr != NULL);
8840 assert(xstar != NULL);
8841 assert(box != NULL);
8842 assert(nonfixedpos != NULL);
8843 assert(funvals != NULL);
8844 assert(nvars >= 0);
8845 assert(nvars <= SCIP_MAXVERTEXPOLYDIM);
8846 assert(success != NULL);
8847 assert(facetcoefs != NULL);
8848 assert(facetconstant != NULL);
8849
8850 *success = FALSE;
8851
8852 conshdlrdata = SCIPconshdlrGetData(conshdlr);
8853 assert(conshdlrdata != NULL);
8854
8855 if( conshdlrdata->vp_randnumgen == NULL && conshdlrdata->vp_maxperturb > 0.0 )
8856 {
8857 SCIP_CALL( SCIPcreateRandom(scip, &conshdlrdata->vp_randnumgen, VERTEXPOLY_RANDNUMINITSEED, TRUE) );
8858 }
8859
8860 /* construct an LP for this size, if not having one already */
8861 if( conshdlrdata->vp_lp[nvars] == NULL )
8862 {
8863 SCIP_CALL( buildVertexPolyhedralSeparationLP(scip, nvars, &conshdlrdata->vp_lp[nvars]) );
8864 }
8865 lp = conshdlrdata->vp_lp[nvars];
8866 assert(lp != NULL);
8867
8868 /* get number of cols and rows of separation lp */
8869 SCIP_CALL( SCIPlpiGetNCols(lp, &ncols) );
8870 SCIP_CALL( SCIPlpiGetNRows(lp, &nrows) );
8871
8872 /* number of columns should equal the number of corners = 2^nvars */
8873 assert(ncols == (int)POWEROFTWO(nvars));
8874
8875 /* allocate necessary memory */
8876 SCIP_CALL( SCIPallocBufferArray(scip, &aux, nrows) );
8877 SCIP_CALL( SCIPallocBufferArray(scip, &inds, ncols) );
8878
8879 /*
8880 * set up the described LP on the transformed space
8881 */
8882
8883 for( i = 0; i < ncols; ++i )
8884 inds[i] = i;
8885
8886 /* compute T^-1(x^*), i.e. T^-1(x^*)_i = (x^*_i - lb_i)/(ub_i - lb_i) */
8887 mindomwidth = 2*SCIPinfinity(scip);
8888 for( i = 0; i < nrows-1; ++i )
8889 {
8890 SCIP_Real solval;
8891 SCIP_Real lb;
8892 SCIP_Real ub;
8893 int varpos;
8894
8895 assert(i < nvars);
8896
8897 varpos = nonfixedpos[i];
8898 lb = box[2 * varpos];
8899 ub = box[2 * varpos + 1];
8900 solval = xstar[varpos];
8901
8902 if( ub - lb < mindomwidth )
8903 mindomwidth = ub - lb;
8904
8905 /* explicitly handle solution which violate bounds of variables (this can happen because of tolerances) */
8906 if( solval <= lb )
8907 aux[i] = 0.0;
8908 else if( solval >= ub )
8909 aux[i] = 1.0;
8910 else
8911 aux[i] = (solval - lb) / (ub - lb);
8912
8913 /* perturb point to hopefully obtain a facet of the convex envelope */
8914 if( conshdlrdata->vp_maxperturb > 0.0 )
8915 {
8916 assert(conshdlrdata->vp_randnumgen != NULL);
8917
8918 if( aux[i] == 1.0 )
8919 aux[i] -= SCIPrandomGetReal(conshdlrdata->vp_randnumgen, 0.0, conshdlrdata->vp_maxperturb);
8920 else if( aux[i] == 0.0 )
8921 aux[i] += SCIPrandomGetReal(conshdlrdata->vp_randnumgen, 0.0, conshdlrdata->vp_maxperturb);
8922 else
8923 {
8924 SCIP_Real perturbation;
8925
8926 perturbation = MIN( aux[i], 1.0 - aux[i] ) / 2.0;
8927 perturbation = MIN( perturbation, conshdlrdata->vp_maxperturb );
8928 aux[i] += SCIPrandomGetReal(conshdlrdata->vp_randnumgen, -perturbation, perturbation);
8929 }
8930 assert(0.0 < aux[i] && aux[i] < 1.0);
8931 }
8932
8933 SCIPdebugMsg(scip, "LP row %d in [%e, %e]\n", i, aux[i], aux[i]);
8934 }
8935
8936 /* update LP */
8937 SCIP_CALL( SCIPlpiChgObj(lp, ncols, inds, funvals) );
8938 SCIP_CALL( SCIPlpiChgSides(lp, nrows-1, inds, aux, aux) );
8939 SCIP_CALL( SCIPlpiChgObjsen(lp, overestimate ? SCIP_OBJSEN_MAXIMIZE : SCIP_OBJSEN_MINIMIZE) );
8940
8941 /* we can stop the LP solve if will not meet the target value anyway, but only if xstar hasn't been perturbed */
8942 if( conshdlrdata->vp_maxperturb == 0.0 && !SCIPisInfinity(scip, REALABS(targetvalue)) )
8943 {
8944 SCIP_CALL( SCIPlpiSetRealpar(lp, SCIP_LPPAR_OBJLIM, targetvalue) );
8945 }
8946 /* set an iteration limit so we do not run forever */
8947 SCIP_CALL( SCIPlpiSetIntpar(lp, SCIP_LPPAR_LPITLIM, 100*ncols) );
8948 /* 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 */
8949 SCIP_CALL( SCIPlpiSetRealpar(lp, SCIP_LPPAR_FEASTOL, SCIPfeastol(scip)) );
8950 /* since we work with the dual of the LP, dual feastol determines validity of the facet
8951 * if some ub-lb is small, we need higher accuracy, since below we divide coefs by ub-lb (we moved and scaled the box)
8952 * thus, we set the dual feastol to be between SCIPepsilon and SCIPfeastol
8953 */
8954 SCIP_CALL( SCIPlpiSetRealpar(lp, SCIP_LPPAR_DUALFEASTOL, MIN(SCIPfeastol(scip), MAX(SCIPepsilon(scip), mindomwidth * SCIPfeastol(scip)))) );
8955
8956 #ifdef SCIP_DEBUG
8957 SCIP_CALL( SCIPlpiSetIntpar(lp, SCIP_LPPAR_LPINFO, 1) );
8958 #endif
8959
8960 /*
8961 * solve the LP and store the resulting facet for the transformed space
8962 */
8963 if( conshdlrdata->vp_dualsimplex )
8964 {
8965 lpsolveretcode = SCIPlpiSolveDual(lp);
8966 }
8967 else
8968 {
8969 lpsolveretcode = SCIPlpiSolvePrimal(lp);
8970 }
8971 if( lpsolveretcode == SCIP_LPERROR )
8972 {
8973 SCIPdebugMsg(scip, "LP error, aborting.\n");
8974 goto CLEANUP;
8975 }
8976 SCIP_CALL( lpsolveretcode );
8977
8978 /* any dual feasible solution should provide a valid estimator (and a dual optimal one a facet) */
8979 if( !SCIPlpiIsDualFeasible(lp) )
8980 {
8981 SCIPdebugMsg(scip, "LP not solved to dual feasibility, aborting.\n");
8982 goto CLEANUP;
8983 }
8984
8985 /* get dual solution (facet of convex envelope); again, we have to be careful since the LP can have more rows and
8986 * columns than needed, in particular, \bar \beta is the last dual multiplier
8987 */
8988 SCIP_CALL( SCIPlpiGetSol(lp, NULL, NULL, aux, NULL, NULL) );
8989
8990 for( i = 0; i < nvars; ++i )
8991 facetcoefs[nonfixedpos[i]] = aux[i];
8992 /* last dual multiplier is the constant */
8993 *facetconstant = aux[nrows - 1];
8994
8995 #ifdef SCIP_DEBUG
8996 SCIPdebugMsg(scip, "facet for the transformed problem: ");
8997 for( i = 0; i < nallvars; ++i )
8998 {
8999 SCIPdebugMsgPrint(scip, "%3.4e * x%d + ", facetcoefs[i], i);
9000 }
9001 SCIPdebugMsgPrint(scip, "%3.4e\n", *facetconstant);
9002 #endif
9003
9004 /*
9005 * transform the facet to original space and compute value at x^*, i.e., alpha x + beta
9006 */
9007
9008 SCIPdebugMsg(scip, "facet in orig. space: ");
9009
9010 facetvalue = 0.0;
9011 for( i = 0; i < nvars; ++i )
9012 {
9013 SCIP_Real lb;
9014 SCIP_Real ub;
9015 int varpos;
9016
9017 varpos = nonfixedpos[i];
9018 lb = box[2 * varpos];
9019 ub = box[2 * varpos + 1];
9020 assert(!SCIPisEQ(scip, lb, ub));
9021
9022 /* alpha_i := alpha_bar_i / (ub_i - lb_i) */
9023 facetcoefs[varpos] = facetcoefs[varpos] / (ub - lb);
9024
9025 /* beta = beta_bar - sum_i alpha_i * lb_i */
9026 *facetconstant -= facetcoefs[varpos] * lb;
9027
9028 /* evaluate */
9029 facetvalue += facetcoefs[varpos] * xstar[varpos];
9030
9031 SCIPdebugMsgPrint(scip, "%3.4e * x%d + ", facetcoefs[varpos], varpos);
9032 }
9033 SCIPdebugMsgPrint(scip, "%3.4e ", *facetconstant);
9034
9035 /* add beta to the facetvalue: at this point in the code, facetvalue = g(x^*) */
9036 facetvalue += *facetconstant;
9037
9038 SCIPdebugMsgPrint(scip, "has value %g, target = %g\n", facetvalue, targetvalue);
9039
9040 /* if overestimate, then we want facetvalue < targetvalue
9041 * if underestimate, then we want facetvalue > targetvalue
9042 * if none holds, give up
9043 * so maybe here we should check against the minimal violation
9044 */
9045 if( overestimate == (facetvalue > targetvalue) )
9046 {
9047 SCIPdebugMsg(scip, "missed the target, facetvalue %g targetvalue %g, overestimate=%u\n", facetvalue, targetvalue, overestimate);
9048 goto CLEANUP;
9049 }
9050
9051 /* if we made it until here, then we have a nice facet */
9052 *success = TRUE;
9053
9054 CLEANUP:
9055 /* free allocated memory */
9056 SCIPfreeBufferArray(scip, &inds);
9057 SCIPfreeBufferArray(scip, &aux);
9058
9059 return SCIP_OKAY;
9060 }
9061
9062 /** computes a facet of the convex or concave envelope of a univariate vertex polyhedral function
9063 *
9064 * In other words, compute the line that passes through two given points.
9065 */
9066 static
9067 SCIP_RETCODE computeVertexPolyhedralFacetUnivariate(
9068 SCIP* scip, /**< SCIP data structure */
9069 SCIP_Real left, /**< left coordinate */
9070 SCIP_Real right, /**< right coordinate */
9071 SCIP_Real funleft, /**< value of function in left coordinate */
9072 SCIP_Real funright, /**< value of function in right coordinate */
9073 SCIP_Bool* success, /**< buffer to store whether a facet could be computed successfully */
9074 SCIP_Real* facetcoef, /**< buffer to store coefficient of facet defining inequality */
9075 SCIP_Real* facetconstant /**< buffer to store constant part of facet defining inequality */
9076 )
9077 {
9078 assert(scip != NULL);
9079 assert(SCIPisLE(scip, left, right));
9080 assert(!SCIPisInfinity(scip, -left));
9081 assert(!SCIPisInfinity(scip, right));
9082 assert(SCIPisFinite(funleft) && funleft != SCIP_INVALID);
9083 assert(SCIPisFinite(funright) && funright != SCIP_INVALID);
9084 assert(success != NULL);
9085 assert(facetcoef != NULL);
9086 assert(facetconstant != NULL);
9087
9088 *facetcoef = (funright - funleft) / (right - left);
9089 *facetconstant = funleft - *facetcoef * left;
9090
9091 *success = TRUE;
9092
9093 return SCIP_OKAY;
9094 }
9095
9096 /** given three points, constructs coefficient of equation for hyperplane generated by these three points
9097 *
9098 * Three points a, b, and c are given.
9099 * Computes coefficients alpha, beta, gamma, and delta, such that a, b, and c, satisfy
9100 * alpha * x1 + beta * x2 + gamma * x3 = delta and gamma >= 0.0.
9101 */
9102 static
9103 SCIP_RETCODE computeHyperplaneThreePoints(
9104 SCIP* scip, /**< SCIP data structure */
9105 SCIP_Real a1, /**< first coordinate of a */
9106 SCIP_Real a2, /**< second coordinate of a */
9107 SCIP_Real a3, /**< third coordinate of a */
9108 SCIP_Real b1, /**< first coordinate of b */
9109 SCIP_Real b2, /**< second coordinate of b */
9110 SCIP_Real b3, /**< third coordinate of b */
9111 SCIP_Real c1, /**< first coordinate of c */
9112 SCIP_Real c2, /**< second coordinate of c */
9113 SCIP_Real c3, /**< third coordinate of c */
9114 SCIP_Real* alpha, /**< coefficient of first coordinate */
9115 SCIP_Real* beta, /**< coefficient of second coordinate */
9116 SCIP_Real* gamma_, /**< coefficient of third coordinate */
9117 SCIP_Real* delta /**< constant right-hand side */
9118 )
9119 {
9120 assert(scip != NULL);
9121 assert(alpha != NULL);
9122 assert(beta != NULL);
9123 assert(gamma_ != NULL);
9124 assert(delta != NULL);
9125
9126 *alpha = -b3*c2 + a3*(-b2+c2) + a2*(b3-c3) + b2*c3;
9127 *beta = -(-b3*c1 + a3*(-b1+c1) + a1*(b3-c3) + b1*c3);
9128 *gamma_ = -a2*b1 + a1*b2 + a2*c1 - b2*c1 - a1*c2 + b1*c2;
9129 *delta = -a3*b2*c1 + a2*b3*c1 + a3*b1*c2 - a1*b3*c2 - a2*b1*c3 + a1*b2*c3;
9130
9131 /* SCIPdebugMsg(scip, "alpha: %g beta: %g gamma: %g delta: %g\n", *alpha, *beta, *gamma_, *delta); */
9132
9133 if( SCIPisInfinity(scip, REALABS(*gamma_ * a3)) ||
9134 SCIPisInfinity(scip, REALABS(*gamma_ * b3)) ||
9135 SCIPisInfinity(scip, REALABS(*gamma_ * c3)) )
9136 {
9137 SCIPdebugMsg(scip, "activity above SCIP infinity\n");
9138 *delta = 0.0;
9139 *alpha = 0.0;
9140 *beta = 0.0;
9141 *gamma_ = 0.0;
9142 return SCIP_OKAY;
9143 }
9144
9145 /* check if hyperplane contains all three points (necessary because of numerical troubles) */
9146 if( !SCIPisRelEQ(scip, *alpha * a1 + *beta * a2 - *delta, -*gamma_ * a3) ||
9147 !SCIPisRelEQ(scip, *alpha * b1 + *beta * b2 - *delta, -*gamma_ * b3) ||
9148 !SCIPisRelEQ(scip, *alpha * c1 + *beta * c2 - *delta, -*gamma_ * c3) )
9149 {
9150 SCIP_Real m[9];
9151 SCIP_Real rhs[3];
9152 SCIP_Real x[3];
9153 SCIP_Bool success;
9154
9155 /*
9156 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));
9157 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));
9158 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));
9159 */
9160
9161 /* initialize matrix column-wise */
9162 m[0] = a1;
9163 m[1] = b1;
9164 m[2] = c1;
9165 m[3] = a2;
9166 m[4] = b2;
9167 m[5] = c2;
9168 m[6] = a3;
9169 m[7] = b3;
9170 m[8] = c3;
9171
9172 rhs[0] = 1.0;
9173 rhs[1] = 1.0;
9174 rhs[2] = 1.0;
9175
9176 SCIPdebugMsg(scip, "numerical troubles - try to solve the linear system via an LU factorization\n");
9177
9178 /* solve the linear problem */
9179 SCIP_CALL( SCIPlapackSolveLinearEquations(SCIPbuffer(scip), 3, m, rhs, x, &success) );
9180
9181 *delta = rhs[0];
9182 *alpha = x[0];
9183 *beta = x[1];
9184 *gamma_ = x[2];
9185
9186 /* set all coefficients to zero if one of the points is not contained in the hyperplane; this ensures that we do
9187 * not add a cut to SCIP and that all assertions are trivially fulfilled
9188 */
9189 if( !success || !SCIPisRelEQ(scip, *alpha * a1 + *beta * a2 - *delta, -*gamma_ * a3) ||
9190 !SCIPisRelEQ(scip, *alpha * b1 + *beta * b2 - *delta, -*gamma_ * b3) ||
9191 !SCIPisRelEQ(scip, *alpha * c1 + *beta * c2 - *delta, -*gamma_ * c3) ) /*lint !e774*/
9192 {
9193 SCIPdebugMsg(scip, "could not resolve numerical difficulties\n");
9194 *delta = 0.0;
9195 *alpha = 0.0;
9196 *beta = 0.0;
9197 *gamma_ = 0.0;
9198 }
9199 }
9200
9201 if( *gamma_ < 0.0 )
9202 {
9203 *alpha = -*alpha;
9204 *beta = -*beta;
9205 *gamma_ = -*gamma_;
9206 *delta = -*delta;
9207 }
9208
9209 return SCIP_OKAY;
9210 }
9211
9212 /** computes a facet of the convex or concave envelope of a bivariate vertex polyhedral function */
9213 static
9214 SCIP_RETCODE computeVertexPolyhedralFacetBivariate(
9215 SCIP* scip, /**< SCIP data structure */
9216 SCIP_Bool overestimate, /**< whether to compute facet of concave (TRUE) or convex (FALSE) envelope */
9217 SCIP_Real p1[2], /**< first vertex of box */
9218 SCIP_Real p2[2], /**< second vertex of box */
9219 SCIP_Real p3[2], /**< third vertex of box */
9220 SCIP_Real p4[2], /**< forth vertex of box */
9221 SCIP_Real p1val, /**< value in p1 */
9222 SCIP_Real p2val, /**< value in p2 */
9223 SCIP_Real p3val, /**< value in p3 */
9224 SCIP_Real p4val, /**< value in p4 */
9225 SCIP_Real xstar[2], /**< point to be separated */
9226 SCIP_Real targetvalue, /**< target value: no need to compute facet if value in xstar would be worse than this value */
9227 SCIP_Bool* success, /**< buffer to store whether a facet could be computed successfully */
9228 SCIP_Real* facetcoefs, /**< buffer to store coefficients of facet defining inequality; must be an array of length at least 2 */
9229 SCIP_Real* facetconstant /**< buffer to store constant part of facet defining inequality */
9230 )
9231 {
9232 SCIP_Real alpha, beta, gamma_, delta;
9233 SCIP_Real xstarval, candxstarval = 0.0;
9234 int leaveout;
9235
9236 assert(scip != NULL);
9237 assert(success != NULL);
9238 assert(SCIPisFinite(p1val) && p1val != SCIP_INVALID);
9239 assert(SCIPisFinite(p2val) && p2val != SCIP_INVALID);
9240 assert(SCIPisFinite(p3val) && p3val != SCIP_INVALID);
9241 assert(SCIPisFinite(p4val) && p4val != SCIP_INVALID);
9242 assert(facetcoefs != NULL);
9243 assert(facetconstant != NULL);
9244
9245 *success = FALSE;
9246
9247 /* if we want an underestimator, flip f(x,y), i.e., do as if we compute an overestimator for -f(x,y) */
9248 if( !overestimate )
9249 {
9250 p1val = -p1val;
9251 p2val = -p2val;
9252 p3val = -p3val;
9253 p4val = -p4val;
9254 targetvalue = -targetvalue;
9255 }
9256
9257 SCIPdebugMsg(scip, "p1 = (%g, %g), f(p1) = %g\n", p1[0], p1[1], p1val);
9258 SCIPdebugMsg(scip, "p2 = (%g, %g), f(p2) = %g\n", p2[0], p2[1], p2val);
9259 SCIPdebugMsg(scip, "p3 = (%g, %g), f(p3) = %g\n", p3[0], p3[1], p3val);
9260 SCIPdebugMsg(scip, "p4 = (%g, %g), f(p4) = %g\n", p4[0], p4[1], p4val);
9261
9262 /* Compute coefficients alpha, beta, gamma (>0), delta such that
9263 * alpha*x + beta*y + gamma*z = delta
9264 * is satisfied by at least three of the corner points (p1,f(p1)), ..., (p4,f(p4)) and
9265 * the fourth corner point lies below this hyperplane.
9266 * 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.,
9267 * alpha*x + beta*y - delta <= -gamma * f(x,y),
9268 * or, equivalently,
9269 * -alpha/gamma*x - beta/gamma*y + delta/gamma >= f(x,y).
9270 */
9271 for( leaveout = 1; leaveout <= 4; ++leaveout )
9272 {
9273 switch( leaveout)
9274 {
9275 case 1 :
9276 /* get hyperplane through p2, p3, p4 */
9277 SCIP_CALL( computeHyperplaneThreePoints(scip, p2[0], p2[1], p2val, p3[0], p3[1], p3val, p4[0], p4[1], p4val,
9278 &alpha, &beta, &gamma_, &delta) );
9279 /* if not underestimating in p1, then go to next candidate */
9280 if( alpha * p1[0] + beta * p1[1] + gamma_ * p1val - delta > 0.0 )
9281 continue;
9282 break;
9283
9284 case 2 :
9285 /* get hyperplane through p1, p3, p4 */
9286 SCIP_CALL( computeHyperplaneThreePoints(scip, p1[0], p1[1], p1val, p3[0], p3[1], p3val, p4[0], p4[1], p4val,
9287 &alpha, &beta, &gamma_, &delta) );
9288 /* if not underestimating in p2, then go to next candidate */
9289 if( alpha * p2[0] + beta * p2[1] + gamma_ * p2val - delta > 0.0 )
9290 continue;
9291 break;
9292
9293 case 3 :
9294 /* get hyperplane through p1, p2, p4 */
9295 SCIP_CALL( computeHyperplaneThreePoints(scip, p1[0], p1[1], p1val, p2[0], p2[1], p2val, p4[0], p4[1], p4val,
9296 &alpha, &beta, &gamma_, &delta) );
9297 /* if not underestimating in p3, then go to next candidate */
9298 if( alpha * p3[0] + beta * p3[1] + gamma_ * p3val - delta > 0.0 )
9299 continue;
9300 break;
9301
9302 case 4 :
9303 /* get hyperplane through p1, p2, p3 */
9304 SCIP_CALL( computeHyperplaneThreePoints(scip, p1[0], p1[1], p1val, p2[0], p2[1], p2val, p3[0], p3[1], p3val,
9305 &alpha, &beta, &gamma_, &delta) );
9306 /* if not underestimating in p4, then stop */
9307 if( alpha * p4[0] + beta * p4[1] + gamma_ * p4val - delta > 0.0 )
9308 continue;
9309 break;
9310
9311 default: /* only for lint */
9312 alpha = SCIP_INVALID;
9313 beta = SCIP_INVALID;
9314 gamma_ = SCIP_INVALID;
9315 delta = SCIP_INVALID;
9316 break;
9317 }
9318
9319 /* check if bad luck: should not happen if numerics are fine */
9320 if( SCIPisZero(scip, gamma_) )
9321 continue;
9322 assert(!SCIPisNegative(scip, gamma_));
9323
9324 /* if coefficients become tiny because division by gamma makes them < SCIPepsilon(scip), then skip, too */
9325 if( (!SCIPisZero(scip, alpha) && SCIPisZero(scip, alpha/gamma_)) ||
9326 ( !SCIPisZero(scip, beta) && SCIPisZero(scip, beta/gamma_)) )
9327 continue;
9328
9329 SCIPdebugMsg(scip, "alpha = %g, beta = %g, gamma = %g, delta = %g\n", alpha, beta, gamma_, delta);
9330
9331 /* value of hyperplane candidate in xstar */
9332 xstarval = -alpha/gamma_ * xstar[0] -beta/gamma_ * xstar[1] + delta/gamma_;
9333
9334 /* if reaching target and first or better than previous candidate, then update */
9335 if( xstarval <= targetvalue && (!*success || xstarval < candxstarval) )
9336 {
9337 /* flip hyperplane */
9338 if( !overestimate )
9339 gamma_ = -gamma_;
9340
9341 facetcoefs[0] = -alpha/gamma_;
9342 facetcoefs[1] = -beta/gamma_;
9343 *facetconstant = delta/gamma_;
9344
9345 *success = TRUE;
9346 candxstarval = xstarval;
9347 }
9348 }
9349
9350 return SCIP_OKAY;
9351 }
9352
9353 /** ensures that we can store information about open expressions (i.e., not fully encoded in the symmetry detection
9354 * graph yet) in an array
9355 */
9356 static
9357 SCIP_RETCODE ensureOpenArraySizeSymdetect(
9358 SCIP* scip, /**< SCIP pointer */
9359 int** openidx, /**< address of openidx array */
9360 int nelems, /**< number of elements that need to be stored */
9361 int* maxnelems /**< pointer to store maximum number that can be stored */
9362 )
9363 {
9364 assert(scip != NULL);
9365 assert(openidx != NULL);
9366 assert(maxnelems != NULL);
9367
9368 if( nelems > *maxnelems )
9369 {
9370 int newsize;
9371
9372 newsize = SCIPcalcMemGrowSize(scip, nelems);
9373 assert(newsize >= nelems);
9374
9375 SCIP_CALL( SCIPreallocBufferArray(scip, openidx, newsize) );
9376
9377 *maxnelems = newsize;
9378 }
9379
9380 return SCIP_OKAY;
9381 }
9382
9383 /** ensures that we can store information about local variables in an array */
9384 static
9385 SCIP_RETCODE ensureLocVarsArraySize(
9386 SCIP* scip, /**< SCIP pointer */
9387 SCIP_VAR*** vars, /**< address of variable array */
9388 SCIP_Real** vals, /**< address of value array */
9389 int nelems, /**< number of elements that need to be stored */
9390 int* maxnelems /**< pointer to store maximum number that can be stored */
9391 )
9392 {
9393 assert(scip != NULL);
9394 assert(vars != NULL);
9395 assert(vals != NULL);
9396 assert(maxnelems != NULL);
9397
9398 if( nelems > *maxnelems )
9399 {
9400 int newsize;
9401
9402 newsize = SCIPcalcMemGrowSize(scip, nelems);
9403 assert(newsize > *maxnelems);
9404
9405 SCIP_CALL( SCIPreallocBufferArray(scip, vars, newsize) );
9406 SCIP_CALL( SCIPreallocBufferArray(scip, vals, newsize) );
9407
9408 *maxnelems = newsize;
9409 }
9410
9411 return SCIP_OKAY;
9412 }
9413
9414 /** tries to add gadget for finding signed permutations of bilinear products
9415 *
9416 * If a product has exactly two children being variables, negating both simultanteoulsy
9417 * is a signed permutation.
9418 */
9419 static
9420 SCIP_RETCODE tryAddGadgetBilinearProductSignedPerm(
9421 SCIP* scip, /**< SCIP pointer */
9422 SCIP_EXPR* expr, /**< product expression for which gadget is tried to be added */
9423 SCIP_CONS* cons, /**< constraint containing product expression */
9424 SYM_GRAPH* graph, /**< symmetry detection graph to be extended by gadget */
9425 int parentidx, /**< index of parent node in symmetry detection graph for gadget */
9426 SCIP_Bool hasparentcoef, /**< whether the parent gives a coefficient to the expression */
9427 SCIP_Real parentcoef, /**< the parent coefficient (if it exists) */
9428 SCIP_VAR*** consvars, /**< pointer to allocated array to store temporary variables */
9429 SCIP_Real** consvals, /**< pointer to allocated arrat to store temporary values */
9430 int* maxnconsvars, /**< pointer to maximum number consvars/consvals can hold */
9431 SCIP_HASHSET* handledexprs, /**< hashset to store handled expressions */
9432 SCIP_Bool* success /**< pointer to store whether gadget could be added successfully */
9433 )
9434 {
9435 SYM_EXPRDATA* symdata;
9436 SCIP_EXPR** children;
9437 SCIP_VAR* var1 = NULL;
9438 SCIP_VAR* var2 = NULL;
9439 SCIP_Real val1 = 0.0;
9440 SCIP_Real val2 = 0.0;
9441 SCIP_Real coef;
9442 SCIP_Real prodval;
9443 SCIP_Real constant;
9444 int nlocvars;
9445 int optype;
9446 int nchildren;
9447 int prodidx;
9448 int coefidx1;
9449 int coefidx2;
9450 int childidx;
9451
9452 assert(scip != NULL);
9453 assert(expr != NULL);
9454 assert(SCIPisExprProduct(scip, expr));
9455 assert(graph != NULL);
9456 assert(0 <= parentidx && parentidx < SCIPgetSymgraphNNodes(graph));
9457 assert(consvars != NULL);
9458 assert(consvals != NULL);
9459 assert(maxnconsvars != NULL);
9460 assert(*maxnconsvars > 0);
9461 assert(handledexprs != NULL);
9462 assert(success != NULL);
9463
9464 *success = FALSE;
9465
9466 /* we require exactly two children being variables */
9467 nchildren = SCIPexprGetNChildren(expr);
9468 if( nchildren != 2 )
9469 return SCIP_OKAY;
9470
9471 children = SCIPexprGetChildren(expr);
9472 if( !SCIPisExprVar(scip, children[0]) || !SCIPisExprVar(scip, children[1]) )
9473 return SCIP_OKAY;
9474
9475 /* check whether each child is not multi-aggregated and is not shifted */
9476 SCIP_CALL( ensureLocVarsArraySize(scip, consvars, consvals, SCIPexprGetNChildren(expr), maxnconsvars) );
9477
9478 for( childidx = 0; childidx < 2; ++childidx )
9479 {
9480 (*consvars)[0] = SCIPgetVarExprVar(children[childidx]);
9481 (*consvals)[0] = 1.0;
9482 nlocvars = 1;
9483 constant = 0.0;
9484
9485 SCIP_CALL( SCIPgetActiveVariables(scip, SYM_SYMTYPE_SIGNPERM, consvars, consvals, &nlocvars,
9486 &constant, SCIPconsIsTransformed(cons)) );
9487
9488 if( nlocvars != 1 || !SCIPisZero(scip, constant) )
9489 return SCIP_OKAY;
9490
9491 if( (SCIPisInfinity(scip, SCIPvarGetUbGlobal((*consvars)[0]))
9492 != SCIPisInfinity(scip, -SCIPvarGetLbGlobal((*consvars)[0]))) )
9493 return SCIP_OKAY;
9494
9495 /* store information about variables */
9496 if( childidx == 0 )
9497 {
9498 var1 = (*consvars)[0];
9499 val1 = (*consvals)[0];
9500 }
9501 else
9502 {
9503 var2 = (*consvars)[0];
9504 val2 = (*consvals)[0];
9505 }
9506 }
9507 assert(var1 != NULL);
9508 assert(var2 != NULL);
9509
9510 /* store the we handle the children */
9511 SCIP_CALL( SCIPhashsetInsert(handledexprs, SCIPblkmem(scip), (void*) children[0]) );
9512 SCIP_CALL( SCIPhashsetInsert(handledexprs, SCIPblkmem(scip), (void*) children[1]) );
9513
9514 SCIP_CALL( SCIPgetSymDataExpr(scip, expr, &symdata) );
9515 assert(symdata != NULL);
9516 assert(SCIPgetSymExprdataNConstants(symdata) == 1);
9517
9518 coef = SCIPgetSymExprdataConstants(symdata)[0];
9519
9520 SCIP_CALL( SCIPfreeSymDataExpr(scip, &symdata) );
9521
9522 /* add gadget modeling the product
9523 *
9524 * Since the constants are 0, each variable is centered at the origin, which leads to
9525 * a product of the form \f$(\alpha x)\cdot(\gamma y)\f$. Manipulating the formula leads
9526 * to \f$\alpha \gamma (x \cdot y)\f$, which is modeled in a gadget that allows to
9527 * negate both variables simulataneously.
9528 */
9529 SCIP_CALL( SCIPgetSymOpNodeType(scip, SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)), &optype) );
9530 SCIP_CALL( SCIPaddSymgraphOpnode(scip, graph, optype, &prodidx) );
9531 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, parentidx, prodidx, hasparentcoef, parentcoef) );
9532
9533 prodval = coef * val1 * val2;
9534
9535 /* introduce nodes for the product value and its negation; since flipping both variables
9536 * simultaneously is a signed symmetry, assign both nodes the same value
9537 */
9538 SCIP_CALL( SCIPaddSymgraphValnode(scip, graph, prodval, &coefidx1) );
9539 SCIP_CALL( SCIPaddSymgraphValnode(scip, graph, prodval, &coefidx2) );
9540
9541 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, prodidx, coefidx1, FALSE, 0.0) );
9542 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, prodidx, coefidx2, FALSE, 0.0) );
9543 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, coefidx1, coefidx2, FALSE, 0.0) );
9544
9545 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, coefidx1,
9546 SCIPgetSymgraphVarnodeidx(scip, graph, var1), FALSE, 0.0) );
9547 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, coefidx1,
9548 SCIPgetSymgraphVarnodeidx(scip, graph, var2), FALSE, 0.0) );
9549 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, coefidx2,
9550 SCIPgetSymgraphNegatedVarnodeidx(scip, graph, var1), FALSE, 0.0) );
9551 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, coefidx2,
9552 SCIPgetSymgraphNegatedVarnodeidx(scip, graph, var2), FALSE, 0.0) );
9553
9554 *success = TRUE;
9555
9556 return SCIP_OKAY;
9557 }
9558
9559 /** returns whether an operator is even and, if yes, stores data about operator */
9560 static
9561 SCIP_Bool isEvenOperator(
9562 SCIP* scip, /**< SCIP pointer */
9563 SCIP_EXPR* expr, /**< expression corresponding to operator */
9564 SCIP_Bool* hasvalue, /**< pointer to store whether even operator has a value
9565 * needed for symmetry computation */
9566 SCIP_Real* value /**< pointer to store value for symmetry computation */
9567 )
9568 {
9569 SYM_EXPRDATA* symdata;
9570
9571 assert(scip != NULL);
9572 assert(expr != NULL);
9573 assert(hasvalue != NULL);
9574 assert(value != NULL);
9575
9576 /* check for different operators known to be even */
9577 if( SCIPisExprSignpower(scip, expr) || SCIPisExprCos(scip, expr) )
9578 {
9579 /* get remaining information needed for symmetry detection */
9580 if( SCIPisExprSignpower(scip, expr) )
9581 {
9582 SCIP_CALL_ABORT( SCIPgetSymDataExpr(scip, expr, &symdata) );
9583 assert(symdata != NULL);
9584 assert(SCIPgetSymExprdataNConstants(symdata) == 1);
9585
9586 *value = SCIPgetSymExprdataConstants(symdata)[0];
9587 *hasvalue = !SCIPisEQ(scip, *value, 1.0);
9588
9589 SCIP_CALL_ABORT( SCIPfreeSymDataExpr(scip, &symdata) );
9590 }
9591 else
9592 {
9593 assert(SCIPisExprCos(scip, expr));
9594 *hasvalue = FALSE;
9595 }
9596
9597 return TRUE;
9598 }
9599 else if( SCIPisExprPower(scip, expr) )
9600 {
9601 SCIP_Real exponent;
9602 int safeexponent;
9603
9604 /* only consider expressions corresponding to an even power */
9605 SCIP_CALL_ABORT( SCIPgetSymDataExpr(scip, expr, &symdata) );
9606 assert(symdata != NULL);
9607 assert(SCIPgetSymExprdataNConstants(symdata) == 1);
9608
9609 exponent = SCIPgetSymExprdataConstants(symdata)[0];
9610 SCIP_CALL_ABORT( SCIPfreeSymDataExpr(scip, &symdata) );
9611
9612 /* check whether the exponent is an even integer */
9613 if( !SCIPisIntegral(scip, exponent) || SCIPisLE(scip, exponent, 0.0) )
9614 return FALSE;
9615
9616 /* deal with numerics */
9617 safeexponent = (int) (exponent + 0.5);
9618 if( safeexponent % 2 != 0 )
9619 return FALSE;
9620
9621 *hasvalue = TRUE;
9622 *value = exponent;
9623
9624 return TRUE;
9625 }
9626 else if( SCIPisExprAbs(scip, expr) )
9627 {
9628 *hasvalue = FALSE;
9629
9630 return TRUE;
9631 }
9632
9633 return FALSE;
9634 }
9635
9636 /** returns whether a variable is centered at 0 */
9637 static
9638 SCIP_Bool varIsCenteredAt0(
9639 SCIP* scip, /**< SCIP pointer */
9640 SCIP_VAR* var /**< variable to be checked */
9641 )
9642 {
9643 assert(scip != NULL);
9644 assert(var != NULL);
9645
9646 if( (SCIPisInfinity(scip, SCIPvarGetUbGlobal(var)) != SCIPisInfinity(scip, -SCIPvarGetLbGlobal(var))) )
9647 return FALSE;
9648
9649 if( SCIPisInfinity(scip, SCIPvarGetUbGlobal(var)) )
9650 return TRUE;
9651
9652 if( SCIPisEQ(scip, SCIPvarGetUbGlobal(var), -SCIPvarGetLbGlobal(var)) )
9653 return TRUE;
9654
9655 return FALSE;
9656 }
9657
9658 /** tries to add gadget for finding signed permutation of even univariate operators with variable child */
9659 static
9660 SCIP_RETCODE tryAddGadgetEvenOperatorVariable(
9661 SCIP* scip, /**< SCIP pointer */
9662 SCIP_EXPR* evenopexpr, /**< even operator expression for which gadget is tried to be added */
9663 SCIP_EXPR* child, /**< child expression of evenopexpr */
9664 SCIP_CONS* cons, /**< constraint containing expression */
9665 SYM_GRAPH* graph, /**< symmetry detection graph to be extended by gadget */
9666 int parentidx, /**< index of parent node in symmetry detection graph for gadget */
9667 SCIP_Bool hasparentcoef, /**< whether the parent gives a coefficient to the expression */
9668 SCIP_Real parentcoef, /**< the parent coefficient (if it exists) */
9669 SCIP_Bool hassymval, /**< whether evenopexpr has a value needed for symmetry detection */
9670 SCIP_Real symval, /**< value needed for symmetry detection (if hassymval is TRUE) */
9671 SCIP_VAR*** consvars, /**< pointer to allocated array to store temporary variables */
9672 SCIP_Real** consvals, /**< pointer to allocated arrat to store temporary values */
9673 int* maxnconsvars, /**< pointer to maximum number consvars/consvals can hold */
9674 SCIP_Bool* success /**< pointer to store whether gadget could be added successfully */
9675 )
9676 {
9677 SCIP_VAR* var;
9678 SCIP_Real constant;
9679 SCIP_Real edgeweight;
9680 int nlocvars;
9681 int nodeidx;
9682 int optype;
9683 int thisopidx;
9684
9685 assert(scip != NULL);
9686 assert(evenopexpr != NULL);
9687 assert(child != NULL);
9688 assert(SCIPisExprVar(scip, child));
9689 assert(cons != NULL);
9690 assert(graph != NULL);
9691 assert(parentidx >= 0);
9692 assert(consvars != NULL);
9693 assert(consvals != NULL);
9694 assert(maxnconsvars != NULL);
9695 assert(success != NULL);
9696
9697 *success = FALSE;
9698
9699 /* check whether child variable is (multi-)aggregated */
9700 var = SCIPgetVarExprVar(child);
9701 (*consvars)[0] = var;
9702 (*consvals)[0] = 1.0;
9703 constant = 0.0;
9704 nlocvars = 1;
9705
9706 SCIP_CALL( ensureLocVarsArraySize(scip, consvars, consvals, nlocvars, maxnconsvars) );
9707 SCIP_CALL( SCIPgetActiveVariables(scip, SYM_SYMTYPE_SIGNPERM, consvars, consvals, &nlocvars, &constant,
9708 SCIPconsIsTransformed(cons)) );
9709
9710 /* skip multi-aggregated variables or variables with domain not centered at 0 */
9711 if( nlocvars != 1 || !SCIPisZero(scip, constant) )
9712 return SCIP_OKAY;
9713
9714 if( !varIsCenteredAt0(scip, var) )
9715 return SCIP_OKAY;
9716
9717 /* store partial information for gadget */
9718 var = (*consvars)[0];
9719 edgeweight = (*consvals)[0];
9720
9721 /* add gadget to graph for even univariate expression */
9722 *success = TRUE;
9723
9724 SCIP_CALL( SCIPgetSymOpNodeType(scip, SCIPexprhdlrGetName(SCIPexprGetHdlr(evenopexpr)), &optype) );
9725
9726 SCIP_CALL( SCIPaddSymgraphOpnode(scip, graph, optype, &thisopidx) );
9727 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, parentidx, thisopidx, hasparentcoef, parentcoef) );
9728
9729 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, thisopidx, SCIPgetSymgraphVarnodeidx(scip, graph, var),
9730 TRUE, edgeweight) );
9731 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, thisopidx, SCIPgetSymgraphNegatedVarnodeidx(scip, graph, var),
9732 TRUE, edgeweight) );
9733
9734 if( hassymval )
9735 {
9736 SCIP_CALL( SCIPaddSymgraphValnode(scip, graph, symval, &nodeidx) );
9737 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, thisopidx, nodeidx, FALSE, 0.0) );
9738 }
9739
9740 return SCIP_OKAY;
9741 }
9742
9743 /** tries to add gadget for finding signed permutation of even univariate operators with sum child */
9744 static
9745 SCIP_RETCODE tryAddGadgetEvenOperatorSum(
9746 SCIP* scip, /**< SCIP pointer */
9747 SCIP_EXPR* evenopexpr, /**< even operator expression for which gadget is tried to be added */
9748 SCIP_EXPR* child, /**< child expression of evenopexpr */
9749 SCIP_CONS* cons, /**< constraint containing expression */
9750 SYM_GRAPH* graph, /**< symmetry detection graph to be extended by gadget */
9751 int parentidx, /**< index of parent node in symmetry detection graph for gadget */
9752 SCIP_Bool hasparentcoef, /**< whether the parent gives a coefficient to the expression */
9753 SCIP_Real parentcoef, /**< the parent coefficient (if it exists) */
9754 SCIP_Bool hassymval, /**< whether evenopexpr has a value needed for symmetry detection */
9755 SCIP_Real symval, /**< value needed for symmetry detection (if hassymval is TRUE) */
9756 SCIP_VAR*** consvars, /**< pointer to allocated array to store temporary variables */
9757 SCIP_Real** consvals, /**< pointer to allocated arrat to store temporary values */
9758 int* maxnconsvars, /**< pointer to maximum number consvars/consvals can hold */
9759 SCIP_HASHSET* handledexprs, /**< hashset to store handled expressions */
9760 SCIP_Bool* success /**< pointer to store whether gadget could be added successfully */
9761 )
9762 {
9763 SCIP_VAR* var;
9764 SCIP_Real constant;
9765 SCIP_Real weight;
9766 int nlocvars;
9767 int nodeidx;
9768 int optype;
9769 int thisopidx;
9770 int i;
9771
9772 assert(scip != NULL);
9773 assert(evenopexpr != NULL);
9774 assert(child != NULL);
9775 assert(SCIPisExprSum(scip, child));
9776 assert(cons != NULL);
9777 assert(graph != NULL);
9778 assert(parentidx >= 0);
9779 assert(consvars != NULL);
9780 assert(consvals != NULL);
9781 assert(maxnconsvars != NULL);
9782 assert(handledexprs != NULL);
9783 assert(success != NULL);
9784
9785 *success = FALSE;
9786
9787 /* check whether child variable is (multi-)aggregated and whether all children are variables */
9788 nlocvars = SCIPexprGetNChildren(child);
9789
9790 SCIP_CALL( ensureLocVarsArraySize(scip, consvars, consvals, nlocvars, maxnconsvars) );
9791
9792 for( i = 0; i < nlocvars; ++i)
9793 {
9794 if( SCIPisExprVar(scip, SCIPexprGetChildren(child)[i]) )
9795 {
9796 (*consvars)[i] = SCIPgetVarExprVar(SCIPexprGetChildren(child)[i]);
9797 (*consvals)[i] = SCIPgetCoefsExprSum(child)[i];
9798 }
9799 else
9800 return SCIP_OKAY;
9801 }
9802 constant = SCIPgetConstantExprSum(child);
9803
9804 SCIP_CALL( SCIPgetActiveVariables(scip, SYM_SYMTYPE_SIGNPERM, consvars, consvals, &nlocvars, &constant,
9805 SCIPconsIsTransformed(cons)) );
9806
9807 /* we can only handle the case without constant and two variables with domain centered at origin */
9808 if( nlocvars > 2 || !SCIPisZero(scip, constant) )
9809 return SCIP_OKAY;
9810 assert(nlocvars > 0);
9811
9812 var = (*consvars)[0];
9813 if( !varIsCenteredAt0(scip, var) )
9814 return SCIP_OKAY;
9815
9816 if( nlocvars == 2 )
9817 {
9818 var = (*consvars)[1];
9819 if( !varIsCenteredAt0(scip, var) )
9820 return SCIP_OKAY;
9821 }
9822
9823 /* add gadget to graph for even univariate expression that have a sum of at most two variables as child */
9824 *success = TRUE;
9825 for( i = 0; i < SCIPexprGetNChildren(child); ++i )
9826 {
9827 SCIP_CALL( SCIPhashsetInsert(handledexprs, SCIPblkmem(scip), (void*) SCIPexprGetChildren(child)[i]) );
9828 }
9829
9830 SCIP_CALL( SCIPgetSymOpNodeType(scip, SCIPexprhdlrGetName(SCIPexprGetHdlr(evenopexpr)), &optype) );
9831
9832 SCIP_CALL( SCIPaddSymgraphOpnode(scip, graph, optype, &thisopidx) );
9833 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, parentidx, thisopidx, hasparentcoef, parentcoef) );
9834
9835 if( hassymval )
9836 {
9837 SCIP_CALL( SCIPaddSymgraphValnode(scip, graph, symval, &nodeidx) );
9838 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, thisopidx, nodeidx, FALSE, 0.0) );
9839 }
9840
9841 if( nlocvars == 1 )
9842 {
9843 var = (*consvars)[0];
9844 weight = (*consvals)[0];
9845
9846 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, thisopidx, SCIPgetSymgraphVarnodeidx(scip, graph, var),
9847 TRUE, weight) );
9848 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, thisopidx, SCIPgetSymgraphNegatedVarnodeidx(scip, graph, var),
9849 TRUE, weight) );
9850 }
9851 else
9852 {
9853 int dummyidx1;
9854 int dummyidx2;
9855
9856 /* add dummy nodes for gadget */
9857 SCIP_CALL( SCIPaddSymgraphOpnode(scip, graph, (int) SYM_CONSOPTYPE_SUM, &dummyidx1) );
9858 SCIP_CALL( SCIPaddSymgraphOpnode(scip, graph, (int) SYM_CONSOPTYPE_SUM, &dummyidx2) );
9859
9860 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, dummyidx1, thisopidx, FALSE, 0.0) );
9861 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, dummyidx2, thisopidx, FALSE, 0.0) );
9862 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, dummyidx1, dummyidx2, FALSE, 0.0) );
9863
9864 /* connect dummy nodes with variables */
9865 for( i = 0; i < 2; ++i)
9866 {
9867 var = (*consvars)[i];
9868 weight = ABS((*consvals)[i]);
9869
9870 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, dummyidx1, SCIPgetSymgraphVarnodeidx(scip, graph, var),
9871 TRUE, weight) );
9872 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, dummyidx2, SCIPgetSymgraphNegatedVarnodeidx(scip, graph, var),
9873 TRUE, weight) );
9874 }
9875 }
9876
9877 return SCIP_OKAY;
9878 }
9879
9880 /** tries to add gadget for finding signed permutations of even univariate operators
9881 *
9882 * We handle two cases. First, if a univariate operator is even and has a variable
9883 * as child, negating the child is signed permutation. Second, the univariate operator
9884 * is even and has a weighted sum of two variables as child.
9885 */
9886 static
9887 SCIP_RETCODE tryAddGadgetEvenOperator(
9888 SCIP* scip, /**< SCIP pointer */
9889 SCIP_EXPR* expr, /**< expression for which gadget is tried to be added */
9890 SCIP_CONS* cons, /**< constraint containing expression */
9891 SYM_GRAPH* graph, /**< symmetry detection graph to be extended by gadget */
9892 int parentidx, /**< index of parent node in symmetry detection graph for gadget */
9893 SCIP_Bool hasparentcoef, /**< whether the parent gives a coefficient to the expression */
9894 SCIP_Real parentcoef, /**< the parent coefficient (if it exists) */
9895 SCIP_VAR*** consvars, /**< pointer to allocated array to store temporary variables */
9896 SCIP_Real** consvals, /**< pointer to allocated arrat to store temporary values */
9897 int* maxnconsvars, /**< pointer to maximum number consvars/consvals can hold */
9898 SCIP_HASHSET* handledexprs, /**< hashset to store handled expressions */
9899 SCIP_Bool* success /**< pointer to store whether gadget could be added successfully */
9900 )
9901 {
9902 SCIP_EXPR* child;
9903 SCIP_Real val = 0.0;
9904 SCIP_Bool hasval = FALSE;
9905
9906 assert(scip != NULL);
9907 assert(expr != NULL);
9908 assert(graph != NULL);
9909 assert(0 <= parentidx && parentidx < SCIPgetSymgraphNNodes(graph));
9910 assert(consvars != NULL);
9911 assert(consvals != NULL);
9912 assert(maxnconsvars != NULL);
9913 assert(*maxnconsvars > 0);
9914 assert(handledexprs != NULL);
9915 assert(success != NULL);
9916
9917 *success = FALSE;
9918
9919 /* ignore variable or value expressions */
9920 if( SCIPisExprVar(scip, expr) || SCIPisExprValue(scip, expr) || SCIPisExprVaridx(scip, expr) )
9921 return SCIP_OKAY;
9922 assert(SCIPexprGetNChildren(expr) > 0);
9923
9924 /* ignore operators with too many children */
9925 if( SCIPexprGetNChildren(expr) > 1 )
9926 return SCIP_OKAY;
9927
9928 /* check whether operator is even */
9929 if( !isEvenOperator(scip, expr, &hasval, &val) )
9930 return SCIP_OKAY;
9931
9932 /* we can only treat the operator if its child is a variable or a sum */
9933 child = SCIPexprGetChildren(expr)[0];
9934 if( SCIPisExprVar(scip, child) )
9935 {
9936 SCIP_CALL( tryAddGadgetEvenOperatorVariable(scip, expr, child, cons, graph, parentidx, hasparentcoef, parentcoef,
9937 hasval, val, consvars, consvals, maxnconsvars, success) );
9938 }
9939 else if( SCIPisExprSum(scip, child) )
9940 {
9941 SCIP_CALL( tryAddGadgetEvenOperatorSum(scip, expr, child, cons, graph, parentidx, hasparentcoef, parentcoef,
9942 hasval, val, consvars, consvals, maxnconsvars, handledexprs, success) );
9943 }
9944
9945 if( *success )
9946 {
9947 SCIP_CALL( SCIPhashsetInsert(handledexprs, SCIPblkmem(scip), (void*) child) );
9948 }
9949
9950 return SCIP_OKAY;
9951 }
9952
9953 /** compares two variable pointers */
9954 static
9955 SCIP_DECL_SORTINDCOMP(SCIPsortVarPtr)
9956 { /*lint --e{715}*/
9957 SCIP_VAR** vars;
9958 SCIP_VAR* var1;
9959 SCIP_VAR* var2;
9960
9961 vars = (SCIP_VAR**) dataptr;
9962
9963 var1 = vars[ind1];
9964 var2 = vars[ind2];
9965 assert(var1 != NULL);
9966 assert(var2 != NULL);
9967
9968 /* sort variables by their unique index */
9969 if( SCIPvarGetIndex(var1) < SCIPvarGetIndex(var2) )
9970 return -1;
9971 if( SCIPvarGetIndex(var1) > SCIPvarGetIndex(var2) )
9972 return 1;
9973
9974 return 0;
9975 }
9976
9977 /** gets domain center of a variable which has not semi-infinite domain */
9978 static
9979 SCIP_Real getDomainCenter(
9980 SCIP* scip, /**< SCIP pointer */
9981 SCIP_VAR* var /**< variable */
9982 )
9983 {
9984 SCIP_Real ub;
9985 SCIP_Real lb;
9986
9987 ub = SCIPvarGetUbGlobal(var);
9988 lb = SCIPvarGetLbGlobal(var);
9989
9990 assert( SCIPisInfinity(scip, ub) == SCIPisInfinity(scip, -lb) );
9991
9992 if ( SCIPisInfinity(scip, ub) )
9993 return 0.0;
9994
9995 return (ub + lb) / 2;
9996 }
9997
9998 /** tries to add gadget for finding signed permutations for squared differences in a sum expression */
9999 static
10000 SCIP_RETCODE tryAddGadgetSquaredDifference(
10001 SCIP* scip, /**< SCIP pointer */
10002 SCIP_EXPR* sumexpr, /**< sum expression */
10003 SCIP_CONS* cons, /**< constraint containing the sum expression */
10004 SYM_GRAPH* graph, /**< symmetry detection graph to be extended by gadget */
10005 int sumnodeidx, /**< index of sum node in symmetry detection graph for gadget */
10006 SCIP_VAR*** consvars, /**< pointer to allocated array to store temporary variables */
10007 SCIP_Real** consvals, /**< pointer to allocated arrat to store temporary values */
10008 int* maxnconsvars, /**< pointer to maximum number consvars/consvals can hold */
10009 SCIP_HASHSET* handledexprs /**< hashset to store handled expressions */
10010 )
10011 {
10012 SYM_EXPRDATA* symdata;
10013 SCIP_EXPR** children;
10014 SCIP_EXPR** powexprs;
10015 SCIP_EXPR** prodexprs;
10016 SCIP_EXPR* child;
10017 SCIP_VAR** powvars;
10018 SCIP_VAR** prodvars;
10019 SCIP_VAR* actvar;
10020 SCIP_VAR* actvar2;
10021 SCIP_VAR* var;
10022 SCIP_VAR* var2;
10023 SCIP_Real* sumcoefs;
10024 SCIP_Real constant;
10025 SCIP_Real constant2;
10026 SCIP_Real val;
10027 SCIP_Real val2;
10028 SCIP_Bool* powexprused = NULL;
10029 int* powperm = NULL;
10030 int* prodperm = NULL;
10031 int nchildren;
10032 int nlocvars;
10033 int nodeidx;
10034 int coefnodeidx1;
10035 int coefnodeidx2;
10036 int cnt;
10037 int i;
10038 int j;
10039 int nterms;
10040 int npowexprs = 0;
10041 int nprodexprs = 0;
10042 int powcoef = 0;
10043
10044 assert(scip != NULL);
10045 assert(sumexpr != NULL);
10046 assert(cons != NULL);
10047 assert(SCIPisExprSum(scip, sumexpr));
10048 assert(consvars != NULL);
10049 assert(consvals != NULL);
10050 assert(maxnconsvars != NULL);
10051 assert(*maxnconsvars > 0);
10052 assert(handledexprs != NULL);
10053
10054 /* iterate over sum expression and extract all power and product expressions */
10055 sumcoefs = SCIPgetCoefsExprSum(sumexpr);
10056 children = SCIPexprGetChildren(sumexpr);
10057 nchildren = SCIPexprGetNChildren(sumexpr);
10058 SCIP_CALL( SCIPallocBufferArray(scip, &powexprs, nchildren) );
10059 SCIP_CALL( SCIPallocBufferArray(scip, &prodexprs, 2 * nchildren) );
10060 SCIP_CALL( SCIPallocBufferArray(scip, &powvars, nchildren) );
10061 SCIP_CALL( SCIPallocBufferArray(scip, &prodvars, 2 * nchildren) );
10062
10063 /* we scan for norm constraints, i.e., the number of powexpr needs to be twice the prodexpr */
10064 /** @todo make this work in a more general case */
10065 for( i = 0; i < nchildren; ++i )
10066 {
10067 if( SCIPisExprPower(scip, children[i]) )
10068 {
10069 SCIP_Real exponent;
10070
10071 /* we require a coefficient of +/- 1 from the sum and all power expressions have the same coefficient */
10072 if( powcoef == 0 )
10073 {
10074 if( SCIPisEQ(scip, sumcoefs[i], 1.0) || SCIPisEQ(scip, sumcoefs[i], -1.0) )
10075 powcoef = (int) SCIPround(scip, sumcoefs[i]);
10076 }
10077 else if( !SCIPisEQ(scip, (SCIP_Real) powcoef, sumcoefs[i]) )
10078 continue;
10079
10080 /* we only store power expressions if their child is a variable */
10081 assert(SCIPexprGetNChildren(children[i]) == 1);
10082 child = SCIPexprGetChildren(children[i])[0];
10083 if( !SCIPisExprVar(scip, child) )
10084 continue;
10085
10086 /* the power is required to be a 2 */
10087 SCIP_CALL( SCIPgetSymDataExpr(scip, children[i], &symdata) );
10088 assert(symdata != NULL);
10089 assert(SCIPgetSymExprdataNConstants(symdata) == 1);
10090
10091 exponent = SCIPgetSymExprdataConstants(symdata)[0];
10092 SCIP_CALL( SCIPfreeSymDataExpr(scip, &symdata) );
10093
10094 if( !SCIPisEQ(scip, exponent, 2.0) )
10095 continue;
10096
10097 /* we only store power expressions if the child is not multi-aggregated */
10098 var = SCIPgetVarExprVar(child);
10099 if( SCIPvarGetStatus(var) != SCIP_VARSTATUS_MULTAGGR )
10100 {
10101 powexprs[npowexprs] = children[i];
10102 powvars[npowexprs++] = var;
10103 }
10104 }
10105 else if( SCIPisExprProduct(scip, children[i]) )
10106 {
10107 /* we require a coefficient of +/- 2 from the sum and all product expressions have the same coefficient */
10108 if( powcoef == 0 )
10109 {
10110 if( SCIPisEQ(scip, sumcoefs[i], 2.0) || SCIPisEQ(scip, sumcoefs[i], -2.0) )
10111 powcoef = (int) -SCIPround(scip, sumcoefs[i]);
10112 }
10113 else if( !SCIPisEQ(scip, (SCIP_Real) 2 * powcoef, -sumcoefs[i]) )
10114 continue;
10115
10116 /* we only store power expressions if they have exactly two children being variables */
10117 if( SCIPexprGetNChildren(children[i]) != 2 )
10118 continue;
10119 if( !SCIPisExprVar(scip, SCIPexprGetChildren(children[i])[0])
10120 || !SCIPisExprVar(scip, SCIPexprGetChildren(children[i])[1]) )
10121 continue;
10122
10123 var = SCIPgetVarExprVar(SCIPexprGetChildren(children[i])[0]);
10124 var2 = SCIPgetVarExprVar(SCIPexprGetChildren(children[i])[1]);
10125
10126 /* we only store product expressions if the children are not multi-aggregated */
10127 if( SCIPvarGetStatus(var) != SCIP_VARSTATUS_MULTAGGR
10128 && SCIPvarGetStatus(var2) != SCIP_VARSTATUS_MULTAGGR )
10129 {
10130 prodexprs[nprodexprs] = children[i];
10131 prodvars[nprodexprs++] = var;
10132 prodexprs[nprodexprs] = children[i];
10133 prodvars[nprodexprs++] = var2;
10134 }
10135 }
10136 }
10137
10138 if( npowexprs == 0 || nprodexprs != npowexprs )
10139 goto FREEMEMORY;
10140
10141 /* check whether the power variables and product variables match */
10142 SCIP_CALL( SCIPallocBufferArray(scip, &powperm, nprodexprs) );
10143 SCIP_CALL( SCIPallocBufferArray(scip, &prodperm, nprodexprs) );
10144
10145 SCIPsort(powperm, SCIPsortVarPtr, (void*) powvars, npowexprs);
10146 SCIPsort(prodperm, SCIPsortVarPtr, (void*) prodvars, npowexprs);
10147
10148 for( i = 0; i < npowexprs; ++i )
10149 {
10150 if( SCIPvarGetIndex(prodvars[prodperm[i]]) != SCIPvarGetIndex(powvars[powperm[i]]) )
10151 goto FREEMEMORY;
10152 }
10153
10154 /* if we reach this line, the variables match: we have found a potential norm constraint */
10155 assert(npowexprs % 2 == 0);
10156 nterms = npowexprs / 2;
10157 SCIP_CALL( SCIPallocClearBufferArray(scip, &powexprused, npowexprs) );
10158
10159 /* add gadget of each squared difference term */
10160 cnt = 0;
10161 for( i = 0; i < nterms; ++i )
10162 {
10163 SCIP_Bool var1found = FALSE;
10164 SCIP_Bool var2found = FALSE;
10165
10166 (*consvals)[0] = 1.0;
10167 (*consvars)[0] = prodvars[cnt++];
10168 constant = 0.0;
10169 nlocvars = 1;
10170
10171 SCIP_CALL( SCIPgetActiveVariables(scip, SYM_SYMTYPE_SIGNPERM, consvars, consvals,
10172 &nlocvars, &constant, SCIPconsIsTransformed(cons)) );
10173
10174 if( nlocvars != 1 )
10175 {
10176 ++cnt;
10177 continue;
10178 }
10179 actvar = (*consvars)[0];
10180 val = (*consvals)[0];
10181
10182 (*consvals)[0] = 1.0;
10183 (*consvars)[0] = prodvars[cnt++];
10184 constant2 = 0.0;
10185 nlocvars = 1;
10186
10187 SCIP_CALL( SCIPgetActiveVariables(scip, SYM_SYMTYPE_SIGNPERM, consvars, consvals,
10188 &nlocvars, &constant2, SCIPconsIsTransformed(cons)) );
10189
10190 if( nlocvars != 1 )
10191 continue;
10192 actvar2 = (*consvars)[0];
10193 val2 = (*consvals)[0];
10194
10195 /* we cannot handle the pair of variables if their constant/scalar differs or one variable
10196 * cannot be centered at the origin or they are not centered around the same point
10197 */
10198 if( !SCIPisEQ(scip, constant, constant2) || !SCIPisEQ(scip, val, val2)
10199 || (SCIPisInfinity(scip, SCIPvarGetUbGlobal(actvar)) != SCIPisInfinity(scip, -SCIPvarGetLbGlobal(actvar)))
10200 || (SCIPisInfinity(scip, SCIPvarGetUbGlobal(actvar2)) != SCIPisInfinity(scip, -SCIPvarGetLbGlobal(actvar2)))
10201 || (SCIPisInfinity(scip, SCIPvarGetUbGlobal(actvar)) != SCIPisInfinity(scip, SCIPvarGetLbGlobal(actvar2)))
10202 || !SCIPisEQ(scip, getDomainCenter(scip, actvar), getDomainCenter(scip, actvar2)) )
10203 continue;
10204
10205 /* add gadget */
10206 SCIP_CALL( SCIPaddSymgraphOpnode(scip, graph, (int) SYM_CONSOPTYPE_SQDIFF, &nodeidx) );
10207 SCIP_CALL( SCIPaddSymgraphValnode(scip, graph, val, &coefnodeidx1) );
10208 SCIP_CALL( SCIPaddSymgraphValnode(scip, graph, val2, &coefnodeidx2) );
10209
10210 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, sumnodeidx, nodeidx, TRUE, (SCIP_Real) powcoef) );
10211 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, nodeidx, coefnodeidx1, TRUE, (SCIP_Real) powcoef) );
10212 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, nodeidx, coefnodeidx2, TRUE, (SCIP_Real) powcoef) );
10213 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, coefnodeidx1,
10214 SCIPgetSymgraphVarnodeidx(scip, graph, actvar), FALSE, 0.0) );
10215 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, coefnodeidx1,
10216 SCIPgetSymgraphVarnodeidx(scip, graph, actvar2), FALSE, 0.0) );
10217 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, coefnodeidx2,
10218 SCIPgetSymgraphNegatedVarnodeidx(scip, graph, actvar), FALSE, 0.0) );
10219 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, coefnodeidx2,
10220 SCIPgetSymgraphNegatedVarnodeidx(scip, graph, actvar2), FALSE, 0.0) );
10221
10222 /* mark product expression as handled */
10223 SCIP_CALL( SCIPhashsetInsert(handledexprs, SCIPblkmem(scip), (void*) prodexprs[2*i]) );
10224
10225 /* find corresponding unused power expressions and mark them as handled */
10226 for( j = 0; j < npowexprs && !(var1found && var2found); ++j )
10227 {
10228 if( powexprused[j] )
10229 continue;
10230 assert(cnt >= 2);
10231
10232 if( !var1found && powvars[j] == prodvars[cnt - 2] )
10233 {
10234 SCIP_CALL( SCIPhashsetInsert(handledexprs, SCIPblkmem(scip), (void*) powexprs[j]) );
10235 powexprused[j] = TRUE;
10236 }
10237 else if( !var2found && powvars[j] == prodvars[cnt - 1] )
10238 {
10239 SCIP_CALL( SCIPhashsetInsert(handledexprs, SCIPblkmem(scip), (void*) powexprs[j]) );
10240 powexprused[j] = TRUE;
10241 }
10242 }
10243 }
10244
10245 FREEMEMORY:
10246 SCIPfreeBufferArrayNull(scip, &powexprused);
10247 SCIPfreeBufferArrayNull(scip, &prodperm);
10248 SCIPfreeBufferArrayNull(scip, &powperm);
10249 SCIPfreeBufferArray(scip, &prodvars);
10250 SCIPfreeBufferArray(scip, &powvars);
10251 SCIPfreeBufferArray(scip, &prodexprs);
10252 SCIPfreeBufferArray(scip, &powexprs);
10253
10254 return SCIP_OKAY;
10255 }
10256
10257 /** adds symmetry information of constraint to a symmetry detection graph */
10258 static
10259 SCIP_RETCODE addSymmetryInformation(
10260 SCIP* scip, /**< SCIP pointer */
10261 SYM_SYMTYPE symtype, /**< type of symmetries that need to be added */
10262 SCIP_CONS* cons, /**< constraint */
10263 SYM_GRAPH* graph, /**< symmetry detection graph */
10264 SCIP_Bool* success /**< pointer to store whether symmetry information could be added */
10265 )
10266 { /*lint --e{850}*/
10267 SCIP_EXPRITER* it;
10268 SCIP_HASHSET* handledexprs;
10269 SCIP_EXPR* rootexpr;
10270 SCIP_EXPR* expr;
10271 SCIP_VAR** consvars;
10272 SCIP_Real* consvals;
10273 SCIP_Real constant;
10274 SCIP_Real parentcoef = 0.0;
10275 int* openidx;
10276 int maxnopenidx;
10277 int parentidx;
10278 int nconsvars;
10279 int maxnconsvars;
10280 int nlocvars;
10281 int nopenidx = 0;
10282 int consnodeidx;
10283 int nodeidx;
10284 int i;
10285 SCIP_Bool iscolored;
10286 SCIP_Bool hasparentcoef;
10287
10288 assert(scip != NULL);
10289 assert(cons != NULL);
10290 assert(graph != NULL);
10291 assert(success != NULL);
10292
10293 /* store lhs/rhs */
10294 SCIP_CALL( SCIPaddSymgraphConsnode(scip, graph, cons,
10295 SCIPgetLhsNonlinear(cons), SCIPgetRhsNonlinear(cons), &consnodeidx) );
10296
10297 rootexpr = SCIPgetExprNonlinear(cons);
10298 assert(rootexpr != NULL);
10299
10300 /* allocate arrays to store operators not completely handled yet (due to DFS) and variables in constraint */
10301 expr = SCIPgetExprNonlinear(cons);
10302 assert(expr != NULL);
10303
10304 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
10305 SCIP_CALL( SCIPexpriterInit(it, expr, SCIP_EXPRITER_DFS, TRUE) );
10306 SCIPexpriterSetStagesDFS(it, SCIP_EXPRITER_ENTEREXPR | SCIP_EXPRITER_LEAVEEXPR);
10307
10308 /* find potential number of nodes in graph */
10309 maxnopenidx = 0;
10310 for( ; !SCIPexpriterIsEnd(it); (void) SCIPexpriterGetNext(it) )
10311 {
10312 if( SCIPexpriterGetStageDFS(it) == SCIP_EXPRITER_LEAVEEXPR )
10313 continue;
10314
10315 ++maxnopenidx;
10316 }
10317
10318 SCIP_CALL( SCIPallocBufferArray(scip, &openidx, maxnopenidx) );
10319
10320 maxnconsvars = SCIPgetNVars(scip);
10321 SCIP_CALL( SCIPallocBufferArray(scip, &consvars, maxnconsvars) );
10322 SCIP_CALL( SCIPallocBufferArray(scip, &consvals, maxnconsvars) );
10323
10324 /* for finding special subexpressions, use hashset to store which expressions have been handled completely */
10325 SCIP_CALL( SCIPhashsetCreate(&handledexprs, SCIPblkmem(scip), maxnopenidx) );
10326
10327 /* iterate over expression tree and store nodes/edges */
10328 expr = SCIPgetExprNonlinear(cons); /*lint !e838*/
10329 SCIP_CALL( SCIPexpriterInit(it, expr, SCIP_EXPRITER_DFS, TRUE) );
10330 SCIPexpriterSetStagesDFS(it, SCIP_EXPRITER_ENTEREXPR | SCIP_EXPRITER_LEAVEEXPR);
10331
10332 for( expr = SCIPexpriterGetCurrent(it); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
10333 {
10334 /* if an expression has already been handled by an ancestor, increase iterator until we leave it */
10335 if( !SCIPhashsetIsEmpty(handledexprs) && SCIPhashsetExists(handledexprs, expr) )
10336 {
10337 SCIP_EXPR* baseexpr;
10338
10339 baseexpr = expr;
10340 while( SCIPexpriterGetStageDFS(it) != SCIP_EXPRITER_LEAVEEXPR || expr != baseexpr )
10341 expr = SCIPexpriterGetNext(it);
10342
10343 SCIP_CALL( SCIPhashsetRemove(handledexprs, (void*) expr) );
10344
10345 /* leave the expression */
10346 continue;
10347 }
10348
10349 /* due to DFS and expression has not been handled by ancestor, remove expression from list of open expressions */
10350 if( SCIPexpriterGetStageDFS(it) == SCIP_EXPRITER_LEAVEEXPR )
10351 {
10352 --nopenidx;
10353 continue;
10354 }
10355 assert(SCIPexpriterGetStageDFS(it) == SCIP_EXPRITER_ENTEREXPR);
10356
10357 /* find parentidx */
10358 if( expr == rootexpr )
10359 parentidx = consnodeidx;
10360 else
10361 {
10362 assert(nopenidx >= 1);
10363 parentidx = openidx[nopenidx - 1];
10364 }
10365
10366 /* possibly find a coefficient assigned to the expression by the parent */
10367 hasparentcoef = FALSE;
10368 if ( expr != rootexpr )
10369 {
10370 SCIP_CALL( SCIPgetCoefSymData(scip, expr, SCIPexpriterGetParentDFS(it), &parentcoef, &hasparentcoef) );
10371 }
10372
10373 /* deal with different kinds of expressions and store them in the symmetry data structure */
10374 if( SCIPisExprVar(scip, expr) )
10375 {
10376 /* needed to correctly reset value when leaving expression */
10377 SCIP_CALL( ensureOpenArraySizeSymdetect(scip, &openidx, nopenidx + 1, &maxnopenidx) );
10378
10379 openidx[nopenidx++] = -1;
10380
10381 assert(maxnconsvars > 0);
10382 assert(parentidx > 0);
10383
10384 /* if the parent assigns the variable a coefficient, introduce an intermediate node */
10385 if( hasparentcoef )
10386 {
10387 SCIP_CALL( SCIPaddSymgraphOpnode(scip, graph, (int) SYM_CONSOPTYPE_COEF, &nodeidx) ); /*lint !e641*/
10388
10389 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, parentidx, nodeidx, TRUE, parentcoef) ); /*lint !e644*/
10390 parentidx = nodeidx;
10391 }
10392
10393 /* connect (aggregation of) variable expression with its parent */
10394 nconsvars = 1;
10395 consvars[0] = SCIPgetVarExprVar(expr);
10396 consvals[0] = 1.0;
10397 constant = 0.0;
10398
10399 SCIP_CALL( SCIPgetActiveVariables(scip, symtype, &consvars, &consvals,
10400 &nconsvars, &constant, SCIPconsIsTransformed(cons)) );
10401
10402 /* check whether variable is aggregated */
10403 if( nconsvars > 1 || !SCIPisZero(scip, constant) || !SCIPisEQ(scip, consvals[0], 1.0) )
10404 {
10405 int thisidx;
10406
10407 SCIP_CALL( SCIPaddSymgraphOpnode(scip, graph, (int) SYM_CONSOPTYPE_SUM, &thisidx) ); /*lint !e641*/
10408 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, parentidx, thisidx, FALSE, 0.0) );
10409
10410 parentidx = thisidx;
10411 }
10412 SCIP_CALL( SCIPaddSymgraphVarAggegration(scip, graph, parentidx, consvars, consvals,
10413 nconsvars, constant) );
10414 }
10415 else if( SCIPisExprValue(scip, expr) )
10416 {
10417 assert(parentidx > 0);
10418
10419 SCIP_CALL( SCIPaddSymgraphValnode(scip, graph, SCIPgetValueExprValue(expr), &nodeidx) );
10420
10421 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, parentidx, nodeidx, hasparentcoef, parentcoef) );
10422
10423 /* needed to correctly reset value when leaving expression */
10424 SCIP_CALL( ensureOpenArraySizeSymdetect(scip, &openidx, nopenidx + 1, &maxnopenidx) );
10425
10426 openidx[nopenidx++] = -1;
10427 }
10428 else
10429 {
10430 SCIP_Bool usedefaultgadget = TRUE;
10431
10432 assert(expr == rootexpr || parentidx > 0);
10433 assert(SCIPhashsetIsEmpty(handledexprs) || !SCIPhashsetExists(handledexprs, expr));
10434
10435 if( SCIPisExprSum(scip, expr) )
10436 {
10437 /* deal with sum expressions differently, because we can possibly aggregate linear sums */
10438 SCIP_EXPR** children;
10439 int sumidx;
10440 int optype;
10441 int childidx = 0;
10442
10443 /* sums are handled by a special gadget */
10444 usedefaultgadget = FALSE;
10445
10446 /* extract all children being variables and compute the sum of active variables expression */
10447 nlocvars = 0;
10448 children = SCIPexprGetChildren(expr);
10449
10450 SCIP_CALL( ensureLocVarsArraySize(scip, &consvars, &consvals, SCIPexprGetNChildren(expr), &maxnconsvars) );
10451
10452 for( childidx = 0; childidx < SCIPexprGetNChildren(expr); ++childidx )
10453 {
10454 if( !SCIPisExprVar(scip, children[childidx]) )
10455 continue;
10456
10457 consvars[nlocvars] = SCIPgetVarExprVar(children[childidx]);
10458 consvals[nlocvars++] = SCIPgetCoefsExprSum(expr)[childidx];
10459
10460 /* store that we have already handled this expression */
10461 SCIP_CALL( SCIPhashsetInsert(handledexprs, SCIPblkmem(scip), (void*) children[childidx]) );
10462 }
10463
10464 constant = SCIPgetConstantExprSum(expr);
10465
10466 SCIP_CALL( SCIPgetActiveVariables(scip, symtype, &consvars, &consvals,
10467 &nlocvars, &constant, SCIPconsIsTransformed(cons)) );
10468
10469 SCIP_CALL( SCIPgetSymOpNodeType(scip, SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)), &optype) );
10470
10471 SCIP_CALL( SCIPaddSymgraphOpnode(scip, graph, optype, &sumidx) );
10472 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, parentidx, sumidx, hasparentcoef, parentcoef) );
10473
10474 /* add the linear part of the sum */
10475 SCIP_CALL( SCIPaddSymgraphVarAggegration(scip, graph, sumidx, consvars, consvals, nlocvars, constant) );
10476
10477 SCIP_CALL( ensureOpenArraySizeSymdetect(scip, &openidx, nopenidx + 1, &maxnopenidx) );
10478
10479 /* check whether the sum encodes expressions of type \f$(x - y)^2\f$ */
10480 if( symtype == SYM_SYMTYPE_SIGNPERM )
10481 {
10482 SCIP_CALL( tryAddGadgetSquaredDifference(scip, expr, cons, graph, sumidx,
10483 &consvars, &consvals, &maxnconsvars, handledexprs) );
10484 }
10485
10486 /* store sumidx for children that have not been treated */
10487 openidx[nopenidx++] = sumidx;
10488 }
10489 else if( symtype == SYM_SYMTYPE_SIGNPERM && SCIPisExprProduct(scip, expr) )
10490 {
10491 SCIP_Bool succ;
10492
10493 SCIP_CALL( tryAddGadgetBilinearProductSignedPerm(scip, expr, cons, graph, parentidx, hasparentcoef,
10494 parentcoef, &consvars, &consvals, &maxnconsvars, handledexprs, &succ) );
10495
10496 if( succ )
10497 {
10498 usedefaultgadget = FALSE;
10499 SCIP_CALL( SCIPhashsetInsert(handledexprs, SCIPblkmem(scip), (void*) expr) );
10500 }
10501 }
10502 else if( symtype == SYM_SYMTYPE_SIGNPERM )
10503 {
10504 SCIP_Bool succ;
10505
10506 /* we can find more signed permutations for even univariate operators */
10507 SCIP_CALL( tryAddGadgetEvenOperator(scip, expr, cons, graph, parentidx, hasparentcoef, parentcoef,
10508 &consvars, &consvals, &maxnconsvars, handledexprs, &succ) );
10509
10510 if( succ )
10511 {
10512 usedefaultgadget = FALSE;
10513 SCIP_CALL( SCIPhashsetInsert(handledexprs, SCIPblkmem(scip), (void*) expr) );
10514 }
10515 }
10516
10517 if( usedefaultgadget )
10518 {
10519 int opidx;
10520 int optype;
10521
10522 SCIP_CALL( SCIPgetSymOpNodeType(scip, SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)), &optype) );
10523 SCIP_CALL( SCIPaddSymgraphOpnode(scip, graph, optype, &opidx) );
10524 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, parentidx, opidx, hasparentcoef, parentcoef) );
10525
10526 /* possibly add constants of expression */
10527 if( SCIPexprhdlrHasGetSymData(SCIPexprGetHdlr(expr)) )
10528 {
10529 SYM_EXPRDATA* symdata;
10530
10531 SCIP_CALL( SCIPgetSymDataExpr(scip, expr, &symdata) );
10532 assert(symdata != NULL);
10533
10534 /* if expression has multiple constants, assign colors to edges to distinguish them */
10535 iscolored = SCIPgetSymExprdataNConstants(symdata) > 1 ? TRUE : FALSE;
10536 for( i = 0; i < SCIPgetSymExprdataNConstants(symdata); ++i )
10537 {
10538 SCIP_CALL( SCIPaddSymgraphValnode(scip, graph, SCIPgetSymExprdataConstants(symdata)[i], &nodeidx) );
10539 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, opidx, nodeidx, iscolored, (SCIP_Real) i+1) );
10540 }
10541
10542 SCIP_CALL( SCIPfreeSymDataExpr(scip, &symdata) );
10543 }
10544
10545 SCIP_CALL( ensureOpenArraySizeSymdetect(scip, &openidx, nopenidx + 1, &maxnopenidx) );
10546
10547 openidx[nopenidx++] = opidx;
10548 }
10549 }
10550 }
10551
10552 SCIPhashsetFree(&handledexprs, SCIPblkmem(scip));
10553 SCIPfreeBufferArray(scip, &consvals);
10554 SCIPfreeBufferArray(scip, &consvars);
10555 SCIPfreeBufferArray(scip, &openidx);
10556 SCIPfreeExpriter(&it);
10557
10558 return SCIP_OKAY;
10559 }
10560
10561 /*
10562 * Callback methods of constraint handler
10563 */
10564
10565 /** copy method for constraint handler plugins (called when SCIP copies plugins) */
10566 static
10567 SCIP_DECL_CONSHDLRCOPY(conshdlrCopyNonlinear)
10568 { /*lint --e{715}*/
10569 SCIP_CONSHDLR* targetconshdlr;
10570 SCIP_CONSHDLRDATA* sourceconshdlrdata;
10571 int i;
10572
10573 assert(scip != NULL);
10574 assert(conshdlr != NULL);
10575 assert(valid != NULL);
10576 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
10577
10578 /* create basic data of constraint handler and include it to scip */
10579 SCIP_CALL( SCIPincludeConshdlrNonlinear(scip) );
10580
10581 targetconshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
10582 assert(targetconshdlr != NULL);
10583 assert(targetconshdlr != conshdlr);
10584
10585 sourceconshdlrdata = SCIPconshdlrGetData(conshdlr);
10586 assert(sourceconshdlrdata != NULL);
10587
10588 /* copy nonlinear handlers */
10589 for( i = 0; i < sourceconshdlrdata->nnlhdlrs; ++i )
10590 {
10591 SCIP_CALL( SCIPnlhdlrCopyhdlr(scip, targetconshdlr, conshdlr, sourceconshdlrdata->nlhdlrs[i]) );
10592 }
10593
10594 *valid = TRUE;
10595
10596 return SCIP_OKAY;
10597 }
10598
10599 /** destructor of constraint handler to free constraint handler data (called when SCIP is exiting) */
10600 static
10601 SCIP_DECL_CONSFREE(consFreeNonlinear)
10602 { /*lint --e{715}*/
10603 SCIP_CONSHDLRDATA* conshdlrdata;
10604 int i;
10605
10606 conshdlrdata = SCIPconshdlrGetData(conshdlr);
10607 assert(conshdlrdata != NULL);
10608
10609 /* free nonlinear handlers */
10610 for( i = 0; i < conshdlrdata->nnlhdlrs; ++i )
10611 {
10612 SCIP_CALL( SCIPnlhdlrFree(scip, &conshdlrdata->nlhdlrs[i]) );
10613 assert(conshdlrdata->nlhdlrs[i] == NULL);
10614 }
10615 SCIPfreeBlockMemoryArrayNull(scip, &conshdlrdata->nlhdlrs, conshdlrdata->nlhdlrssize);
10616 conshdlrdata->nlhdlrssize = 0;
10617
10618 /* free upgrade functions */
10619 for( i = 0; i < conshdlrdata->nconsupgrades; ++i )
10620 {
10621 assert(conshdlrdata->consupgrades[i] != NULL);
10622 SCIPfreeBlockMemory(scip, &conshdlrdata->consupgrades[i]);
10623 }
10624 SCIPfreeBlockMemoryArrayNull(scip, &conshdlrdata->consupgrades, conshdlrdata->consupgradessize);
10625
10626 SCIP_CALL( SCIPfreeClock(scip, &conshdlrdata->canonicalizetime) );
10627
10628 SCIPqueueFree(&conshdlrdata->reversepropqueue);
10629
10630 if( conshdlrdata->vp_randnumgen != NULL )
10631 SCIPfreeRandom(scip, &conshdlrdata->vp_randnumgen);
10632
10633 /* free LPs used to construct facets of envelops of vertex-polyhedral functions */
10634 for( i = 0; i <= SCIP_MAXVERTEXPOLYDIM; ++i )
10635 {
10636 if( conshdlrdata->vp_lp[i] != NULL )
10637 {
10638 SCIP_CALL( SCIPlpiFree(&conshdlrdata->vp_lp[i]) );
10639 }
10640 }
10641
10642 assert(conshdlrdata->branchrandnumgen == NULL);
10643
10644 assert(SCIPhashmapGetNElements(conshdlrdata->var2expr) == 0);
10645 SCIPhashmapFree(&conshdlrdata->var2expr);
10646
10647 SCIPfreeBlockMemory(scip, &conshdlrdata);
10648 SCIPconshdlrSetData(conshdlr, NULL);
10649
10650 return SCIP_OKAY;
10651 }
10652
10653
10654 /** initialization method of constraint handler (called after problem was transformed) */
10655 static
10656 SCIP_DECL_CONSINIT(consInitNonlinear)
10657 { /*lint --e{715}*/
10658 SCIP_CONSHDLRDATA* conshdlrdata;
10659 int i;
10660
10661 conshdlrdata = SCIPconshdlrGetData(conshdlr);
10662 assert(conshdlrdata != NULL);
10663
10664 /* make sure current activity tags in expressions are invalid, because we start catching variable events only now */
10665 conshdlrdata->lastboundrelax = ++conshdlrdata->curboundstag;
10666 /* set to 1 so it is larger than initial value of lastenforound in exprs */
10667 conshdlrdata->enforound = 1;
10668 /* reset numbering for auxiliary variables */
10669 conshdlrdata->auxvarid = 0;
10670
10671 for( i = 0; i < nconss; ++i )
10672 {
10673 SCIP_CALL( storeVarExprs(scip, conshdlr, SCIPconsGetData(conss[i])) );
10674 SCIP_CALL( catchVarEvents(scip, conshdlrdata->eventhdlr, conss[i]) );
10675 }
10676
10677 /* sort nonlinear handlers by detection priority, in decreasing order */
10678 if( conshdlrdata->nnlhdlrs > 1 )
10679 SCIPsortDownPtr((void**)conshdlrdata->nlhdlrs, SCIPnlhdlrComp, conshdlrdata->nnlhdlrs);
10680
10681 /* get heuristics for later use */
10682 conshdlrdata->subnlpheur = SCIPfindHeur(scip, "subnlp");
10683 conshdlrdata->trysolheur = SCIPfindHeur(scip, "trysol");
10684
10685 /* reset statistics in nonlinear handlers (TODO only if misc/resetstat == TRUE) and call nlhdlrInit */
10686 for( i = 0; i < conshdlrdata->nnlhdlrs; ++i )
10687 {
10688 SCIP_CALL( SCIPnlhdlrInit(scip, conshdlrdata->nlhdlrs[i]) );
10689 }
10690
10691 /* reset statistics in constraint handler */
10692 conshdlrdata->nweaksepa = 0;
10693 conshdlrdata->ntightenlp = 0;
10694 conshdlrdata->ndesperatebranch = 0;
10695 conshdlrdata->ndesperatecutoff = 0;
10696 conshdlrdata->ndesperatetightenlp = 0;
10697 conshdlrdata->nforcelp = 0;
10698 SCIP_CALL( SCIPresetClock(scip, conshdlrdata->canonicalizetime) );
10699 conshdlrdata->ncanonicalizecalls = 0;
10700
10701 #ifdef ENFOLOGFILE
10702 ENFOLOG( enfologfile = fopen(ENFOLOGFILE, "w"); )
10703 #endif
10704
10705 return SCIP_OKAY;
10706 }
10707
10708
10709 /** deinitialization method of constraint handler (called before transformed problem is freed) */
10710 static
10711 SCIP_DECL_CONSEXIT(consExitNonlinear)
10712 { /*lint --e{715}*/
10713 SCIP_CONSHDLRDATA* conshdlrdata;
10714 SCIP_CONS** consssorted;
10715 int i;
10716
10717 conshdlrdata = SCIPconshdlrGetData(conshdlr);
10718 assert(conshdlrdata != NULL);
10719
10720 if( nconss > 0 )
10721 {
10722 /* for better performance of dropVarEvents, we sort by index, descending */
10723 SCIP_CALL( SCIPduplicateBufferArray(scip, &consssorted, conss, nconss) );
10724 SCIPsortDownPtr((void**)consssorted, compIndexConsNonlinear, nconss);
10725
10726 for( i = 0; i < nconss; ++i )
10727 {
10728 SCIP_CALL( dropVarEvents(scip, conshdlrdata->eventhdlr, consssorted[i]) );
10729 SCIP_CALL( freeVarExprs(scip, SCIPconsGetData(consssorted[i])) );
10730 }
10731
10732 SCIPfreeBufferArray(scip, &consssorted);
10733 }
10734
10735 conshdlrdata->subnlpheur = NULL;
10736 conshdlrdata->trysolheur = NULL;
10737
10738 if( conshdlrdata->vp_randnumgen != NULL )
10739 SCIPfreeRandom(scip, &conshdlrdata->vp_randnumgen);
10740
10741 /* free LPs used to construct facets of envelops of vertex-polyhedral functions */
10742 for( i = 0; i <= SCIP_MAXVERTEXPOLYDIM; ++i )
10743 {
10744 if( conshdlrdata->vp_lp[i] != NULL )
10745 {
10746 SCIP_CALL( SCIPlpiFree(&conshdlrdata->vp_lp[i]) );
10747 }
10748 }
10749
10750 if( conshdlrdata->branchrandnumgen != NULL )
10751 SCIPfreeRandom(scip, &conshdlrdata->branchrandnumgen);
10752
10753 /* deinitialize nonlinear handlers */
10754 for( i = 0; i < conshdlrdata->nnlhdlrs; ++i )
10755 {
10756 SCIP_CALL( SCIPnlhdlrExit(scip, conshdlrdata->nlhdlrs[i]) );
10757 }
10758
10759 ENFOLOG(
10760 if( enfologfile != NULL )
10761 {
10762 fclose(enfologfile);
10763 enfologfile = NULL;
10764 })
10765
10766 return SCIP_OKAY;
10767 }
10768
10769
10770 /** presolving initialization method of constraint handler (called when presolving is about to begin) */
10771 #ifdef SCIP_DISABLED_CODE
10772 static
10773 SCIP_DECL_CONSINITPRE(consInitpreNonlinear)
10774 { /*lint --e{715}*/
10775 SCIPerrorMessage("method of nonlinear constraint handler not implemented yet\n");
10776 SCIPABORT(); /*lint --e{527}*/
10777
10778 return SCIP_OKAY;
10779 }
10780 #else
10781 #define consInitpreNonlinear NULL
10782 #endif
10783
10784
10785 /** presolving deinitialization method of constraint handler (called after presolving has been finished) */
10786 static
10787 SCIP_DECL_CONSEXITPRE(consExitpreNonlinear)
10788 { /*lint --e{715}*/
10789 SCIP_Bool infeasible;
10790
10791 if( nconss == 0 )
10792 return SCIP_OKAY;
10793
10794 /* skip some extra work if already known to be infeasible */
10795 if( SCIPgetStatus(scip) == SCIP_STATUS_INFEASIBLE )
10796 return SCIP_OKAY;
10797
10798 /* simplify constraints and replace common subexpressions */
10799 SCIP_CALL( canonicalizeConstraints(scip, conshdlr, conss, nconss, SCIP_PRESOLTIMING_ALWAYS, &infeasible, NULL, NULL, NULL) );
10800
10801 /* currently SCIP does not offer to communicate this,
10802 * but at the moment this can only become true if canonicalizeConstraints called detectNlhdlrs (which it doesn't do in EXITPRESOLVE stage)
10803 * or if a constraint expression became constant
10804 * the latter happened on tls4 within fiberscip, so I'm disabling this assert for now
10805 */
10806 /* assert(!infeasible); */
10807
10808 /* tell SCIP that we have something nonlinear */
10809 SCIPenableNLP(scip);
10810
10811 return SCIP_OKAY;
10812 }
10813
10814
10815 /** solving process initialization method of constraint handler (called when branch and bound process is about to begin) */
10816 static
10817 SCIP_DECL_CONSINITSOL(consInitsolNonlinear)
10818 { /*lint --e{715}*/
10819 SCIP_CONSHDLRDATA* conshdlrdata;
10820 int i;
10821
10822 /* skip remaining initializations if we have solved already
10823 * if infeasibility was found by our boundtightening, then curvature check may also fail as some exprhdlr (e.g., pow)
10824 * assumes nonempty activities in expressions
10825 */
10826 switch( SCIPgetStatus(scip) )
10827 {
10828 case SCIP_STATUS_OPTIMAL:
10829 case SCIP_STATUS_INFEASIBLE:
10830 case SCIP_STATUS_UNBOUNDED:
10831 case SCIP_STATUS_INFORUNBD:
10832 return SCIP_OKAY;
10833 default: ;
10834 } /*lint !e788 */
10835
10836 conshdlrdata = SCIPconshdlrGetData(conshdlr);
10837 assert(conshdlrdata != NULL);
10838
10839 /* reset one of the number of detections counter to count only current round */
10840 for( i = 0; i < conshdlrdata->nnlhdlrs; ++i )
10841 SCIPnlhdlrResetNDetectionslast(conshdlrdata->nlhdlrs[i]);
10842
10843 SCIP_CALL( initSolve(scip, conshdlr, conss, nconss) );
10844
10845 /* check that branching/lpgainnormalize is set to a known value if pseudo-costs are used in branching */
10846 if( conshdlrdata->branchpscostweight > 0.0 )
10847 {
10848 SCIP_CALL( SCIPgetCharParam(scip, "branching/lpgainnormalize", &(conshdlrdata->branchpscostupdatestrategy)) );
10849 if( strchr("lds", conshdlrdata->branchpscostupdatestrategy) == NULL )
10850 {
10851 SCIPerrorMessage("branching/lpgainnormalize strategy %c unknown\n", conshdlrdata->branchpscostupdatestrategy);
10852 SCIPABORT();
10853 return SCIP_INVALIDDATA;
10854 }
10855 }
10856
10857 return SCIP_OKAY;
10858 }
10859
10860
10861 /** solving process deinitialization method of constraint handler (called before branch and bound process data is freed) */
10862 static
10863 SCIP_DECL_CONSEXITSOL(consExitsolNonlinear)
10864 { /*lint --e{715}*/
10865 SCIP_CONSHDLRDATA* conshdlrdata;
10866
10867 SCIP_CALL( deinitSolve(scip, conshdlr, conss, nconss) );
10868
10869 conshdlrdata = SCIPconshdlrGetData(conshdlr);
10870 assert(conshdlrdata != NULL);
10871
10872 /* free hash table for bilinear terms */
10873 SCIP_CALL( bilinearTermsFree(scip, conshdlrdata) );
10874
10875 /* reset flag to allow another call of presolSingleLockedVars() after a restart */
10876 conshdlrdata->checkedvarlocks = FALSE;
10877
10878 /* drop catching new solution event, if catched before */
10879 if( conshdlrdata->newsoleventfilterpos >= 0 )
10880 {
10881 SCIP_EVENTHDLR* eventhdlr;
10882
10883 eventhdlr = SCIPfindEventhdlr(scip, CONSHDLR_NAME "_newsolution");
10884 assert(eventhdlr != NULL);
10885
10886 SCIP_CALL( SCIPdropEvent(scip, conshdlrdata->linearizeheursol == 'i' ? SCIP_EVENTTYPE_BESTSOLFOUND : SCIP_EVENTTYPE_SOLFOUND, eventhdlr, (SCIP_EVENTDATA*)conshdlr, conshdlrdata->newsoleventfilterpos) );
10887 conshdlrdata->newsoleventfilterpos = -1;
10888 }
10889
10890 return SCIP_OKAY;
10891 }
10892
10893
10894 /** frees specific constraint data */
10895 static
10896 SCIP_DECL_CONSDELETE(consDeleteNonlinear)
10897 { /*lint --e{715}*/
10898 assert(consdata != NULL);
10899 assert(*consdata != NULL);
10900 assert((*consdata)->expr != NULL);
10901
10902 /* constraint locks should have been removed */
10903 assert((*consdata)->nlockspos == 0);
10904 assert((*consdata)->nlocksneg == 0);
10905
10906 /* free variable expressions */
10907 SCIP_CALL( freeVarExprs(scip, *consdata) );
10908
10909 SCIP_CALL( SCIPreleaseExpr(scip, &(*consdata)->expr) );
10910
10911 /* free nonlinear row representation */
10912 if( (*consdata)->nlrow != NULL )
10913 {
10914 SCIP_CALL( SCIPreleaseNlRow(scip, &(*consdata)->nlrow) );
10915 }
10916
10917 SCIPfreeBlockMemory(scip, consdata);
10918
10919 return SCIP_OKAY;
10920 }
10921
10922
10923 /** transforms constraint data into data belonging to the transformed problem */
10924 static
10925 SCIP_DECL_CONSTRANS(consTransNonlinear)
10926 { /*lint --e{715}*/
10927 SCIP_EXPR* targetexpr;
10928 SCIP_CONSDATA* sourcedata;
10929
10930 sourcedata = SCIPconsGetData(sourcecons);
10931 assert(sourcedata != NULL);
10932
10933 /* get a copy of sourceexpr with transformed vars */
10934 SCIP_CALL( SCIPduplicateExpr(scip, sourcedata->expr, &targetexpr, mapexprtransvar, conshdlr, exprownerCreate, (void*)conshdlr) );
10935 assert(targetexpr != NULL); /* SCIPduplicateExpr cannot fail */
10936
10937 /* create transformed cons (only captures targetexpr, no need to copy again) */
10938 SCIP_CALL( createCons(scip, conshdlr, targetcons, SCIPconsGetName(sourcecons),
10939 targetexpr, sourcedata->lhs, sourcedata->rhs, FALSE,
10940 SCIPconsIsInitial(sourcecons), SCIPconsIsSeparated(sourcecons), SCIPconsIsEnforced(sourcecons),
10941 SCIPconsIsChecked(sourcecons), SCIPconsIsPropagated(sourcecons),
10942 SCIPconsIsLocal(sourcecons), SCIPconsIsModifiable(sourcecons),
10943 SCIPconsIsDynamic(sourcecons), SCIPconsIsRemovable(sourcecons)) );
10944
10945 /* release target expr */
10946 SCIP_CALL( SCIPreleaseExpr(scip, &targetexpr) );
10947
10948 return SCIP_OKAY;
10949 }
10950
10951
10952 /** LP initialization method of constraint handler (called before the initial LP relaxation at a node is solved) */
10953 static
10954 SCIP_DECL_CONSINITLP(consInitlpNonlinear)
10955 { /*lint --e{715}*/
10956 SCIP_CONSHDLRDATA* conshdlrdata;
10957
10958 /* create auxiliary variables and call separation initialization callbacks of the expression handlers
10959 * TODO if we ever want to allow constraints that are separated but not initial, then we need to call initSepa also
10960 * during SEPALP, ENFOLP, etc, whenever a constraint may be separated the first time
10961 * for now, there is an assert in detectNlhdlrs to require initial if separated
10962 */
10963 SCIP_CALL( initSepa(scip, conshdlr, conss, nconss, infeasible) );
10964
10965 conshdlrdata = SCIPconshdlrGetData(conshdlr);
10966 assert(conshdlrdata != NULL);
10967
10968 /* catch new solution event */
10969 if( conshdlrdata->linearizeheursol != 'o' && conshdlrdata->newsoleventfilterpos == -1 )
10970 {
10971 SCIP_EVENTHDLR* eventhdlr;
10972
10973 eventhdlr = SCIPfindEventhdlr(scip, CONSHDLR_NAME "_newsolution");
10974 assert(eventhdlr != NULL);
10975
10976 SCIP_CALL( SCIPcatchEvent(scip, conshdlrdata->linearizeheursol == 'i' ? SCIP_EVENTTYPE_BESTSOLFOUND : SCIP_EVENTTYPE_SOLFOUND,
10977 eventhdlr, (SCIP_EVENTDATA*)conshdlr, &conshdlrdata->newsoleventfilterpos) );
10978 }
10979
10980 /* collect all bilinear terms for which an auxvar is present
10981 * TODO this will only do something for the first call of initlp after initsol, because it cannot handle
10982 * addition (and removal?) of constraints during solve
10983 * this is typically the majority of constraints, but the method should be made more flexible
10984 */
10985 SCIP_CALL( bilinearTermsInsertAll(scip, conshdlr, conss, nconss) );
10986
10987 return SCIP_OKAY;
10988 }
10989
10990
10991 /** separation method of constraint handler for LP solutions */
10992 static
10993 SCIP_DECL_CONSSEPALP(consSepalpNonlinear)
10994 { /*lint --e{715}*/
10995 SCIP_CALL( consSepa(scip, conshdlr, conss, nconss, NULL, result) );
10996
10997 return SCIP_OKAY;
10998 }
10999
11000
11001 /** separation method of constraint handler for arbitrary primal solutions */
11002 static
11003 SCIP_DECL_CONSSEPASOL(consSepasolNonlinear)
11004 { /*lint --e{715}*/
11005 SCIP_CALL( consSepa(scip, conshdlr, conss, nconss, sol, result) );
11006
11007 return SCIP_OKAY;
11008 }
11009
11010
11011 /** constraint enforcing method of constraint handler for LP solutions */
11012 static
11013 SCIP_DECL_CONSENFOLP(consEnfolpNonlinear)
11014 { /*lint --e{715}*/
11015 SCIP_CALL( consEnfo(scip, conshdlr, conss, nconss, NULL, result) );
11016
11017 return SCIP_OKAY;
11018 }
11019
11020
11021 /** constraint enforcing method of constraint handler for relaxation solutions */
11022 static
11023 SCIP_DECL_CONSENFORELAX(consEnforelaxNonlinear)
11024 { /*lint --e{715}*/
11025 SCIP_CALL( consEnfo(scip, conshdlr, conss, nconss, sol, result) );
11026
11027 return SCIP_OKAY;
11028 }
11029
11030
11031 /** constraint enforcing method of constraint handler for pseudo solutions */
11032 static
11033 SCIP_DECL_CONSENFOPS(consEnfopsNonlinear)
11034 { /*lint --e{715}*/
11035 SCIP_RESULT propresult;
11036 SCIP_Longint soltag;
11037 int nchgbds;
11038 int nnotify;
11039 int c;
11040
11041 soltag = SCIPgetExprNewSoltag(scip);
11042
11043 *result = SCIP_FEASIBLE;
11044 for( c = 0; c < nconss; ++c )
11045 {
11046 SCIP_CALL( computeViolation(scip, conss[c], NULL, soltag) );
11047
11048 if( isConsViolated(scip, conss[c]) )
11049 *result = SCIP_INFEASIBLE;
11050 }
11051
11052 if( *result == SCIP_FEASIBLE )
11053 return SCIP_OKAY;
11054
11055 /* try to propagate
11056 * TODO obey propinenfo parameter, but we need something to recognize cutoff
11057 */
11058 nchgbds = 0;
11059 SCIP_CALL( propConss(scip, conshdlr, conss, nconss, TRUE, &propresult, &nchgbds) );
11060
11061 if( (propresult == SCIP_CUTOFF) || (propresult == SCIP_REDUCEDDOM) )
11062 {
11063 *result = propresult;
11064 return SCIP_OKAY;
11065 }
11066
11067 /* register all unfixed variables in all violated constraints as branching candidates */
11068 SCIP_CALL( registerBranchingCandidatesAllUnfixed(scip, conshdlr, conss, nconss, &nnotify) );
11069 if( nnotify > 0 )
11070 {
11071 SCIPdebugMsg(scip, "registered %d external branching candidates\n", nnotify);
11072
11073 return SCIP_OKAY;
11074 }
11075
11076 SCIPdebugMsg(scip, "could not find branching candidates, forcing to solve LP\n");
11077 *result = SCIP_SOLVELP;
11078 ++SCIPconshdlrGetData(conshdlr)->nforcelp;
11079
11080 return SCIP_OKAY;
11081 }
11082
11083
11084 /** feasibility check method of constraint handler for integral solutions */
11085 static
11086 SCIP_DECL_CONSCHECK(consCheckNonlinear)
11087 { /*lint --e{715}*/
11088 SCIP_CONSHDLRDATA* conshdlrdata;
11089 SCIP_CONSDATA* consdata;
11090 SCIP_Real maxviol;
11091 SCIP_Bool maypropfeasible;
11092 SCIP_Longint soltag;
11093 int c;
11094
11095 assert(scip != NULL);
11096 assert(conshdlr != NULL);
11097 assert(conss != NULL || nconss == 0);
11098 assert(result != NULL);
11099
11100 conshdlrdata = SCIPconshdlrGetData(conshdlr);
11101 assert(conshdlrdata != NULL);
11102
11103 *result = SCIP_FEASIBLE;
11104 soltag = SCIPgetExprNewSoltag(scip);
11105 maxviol = 0.0;
11106 maypropfeasible = conshdlrdata->trysolheur != NULL && SCIPgetStage(scip) >= SCIP_STAGE_TRANSFORMED
11107 && SCIPgetStage(scip) <= SCIP_STAGE_SOLVING;
11108
11109 if( maypropfeasible && (sol == NULL || SCIPsolGetOrigin(sol) == SCIP_SOLORIGIN_LPSOL) && SCIPgetLPSolstat(scip) == SCIP_LPSOLSTAT_UNBOUNDEDRAY )
11110 maypropfeasible = FALSE;
11111
11112 /* check nonlinear constraints for feasibility */
11113 for( c = 0; c < nconss; ++c )
11114 {
11115 assert(conss != NULL && conss[c] != NULL);
11116 SCIP_CALL( computeViolation(scip, conss[c], sol, soltag) );
11117
11118 if( isConsViolated(scip, conss[c]) )
11119 {
11120 *result = SCIP_INFEASIBLE;
11121 maxviol = MAX(maxviol, getConsAbsViolation(conss[c]));
11122
11123 consdata = SCIPconsGetData(conss[c]);
11124 assert(consdata != NULL);
11125
11126 /* print reason for infeasibility */
11127 if( printreason )
11128 {
11129 SCIP_CALL( SCIPprintCons(scip, conss[c], NULL) );
11130 SCIPinfoMessage(scip, NULL, ";\n");
11131
11132 if( consdata->lhsviol > SCIPfeastol(scip) )
11133 {
11134 SCIPinfoMessage(scip, NULL, "violation: left hand side is violated by %.15g\n", consdata->lhsviol);
11135 }
11136 if( consdata->rhsviol > SCIPfeastol(scip) )
11137 {
11138 SCIPinfoMessage(scip, NULL, "violation: right hand side is violated by %.15g\n", consdata->rhsviol);
11139 }
11140 }
11141 else if( (conshdlrdata->subnlpheur == NULL || sol == NULL) && !maypropfeasible && !completely )
11142 {
11143 /* if we don't want to pass to subnlp heuristic and don't need to print reasons, then can stop checking here */
11144 return SCIP_OKAY;
11145 }
11146
11147 /* do not try to shift linear variables if violation is at infinity (leads to setting variable to infinity in solution, which is not allowed) */
11148 if( maypropfeasible && SCIPisInfinity(scip, getConsAbsViolation(conss[c])) )
11149 maypropfeasible = FALSE;
11150
11151 if( maypropfeasible )
11152 {
11153 if( consdata->lhsviol > SCIPfeastol(scip) )
11154 {
11155 /* check if there is a variable which may help to get the left hand side satisfied
11156 * if there is no such variable, then we cannot get feasible
11157 */
11158 if( !(consdata->linvarincr != NULL && consdata->linvarincrcoef > 0.0) &&
11159 !(consdata->linvardecr != NULL && consdata->linvardecrcoef < 0.0) )
11160 maypropfeasible = FALSE;
11161 }
11162 else
11163 {
11164 assert(consdata->rhsviol > SCIPfeastol(scip));
11165 /* check if there is a variable which may help to get the right hand side satisfied
11166 * if there is no such variable, then we cannot get feasible
11167 */
11168 if( !(consdata->linvarincr != NULL && consdata->linvarincrcoef < 0.0) &&
11169 !(consdata->linvardecr != NULL && consdata->linvardecrcoef > 0.0) )
11170 maypropfeasible = FALSE;
11171 }
11172 }
11173 }
11174 }
11175
11176 if( *result == SCIP_INFEASIBLE && maypropfeasible )
11177 {
11178 SCIP_Bool success;
11179
11180 SCIP_CALL( proposeFeasibleSolution(scip, conshdlr, conss, nconss, sol, &success) );
11181
11182 /* do not pass solution to NLP heuristic if we made it feasible this way */
11183 if( success )
11184 return SCIP_OKAY;
11185 }
11186
11187 if( *result == SCIP_INFEASIBLE && conshdlrdata->subnlpheur != NULL && sol != NULL && !SCIPisInfinity(scip, maxviol) )
11188 {
11189 SCIP_CALL( SCIPupdateStartpointHeurSubNlp(scip, conshdlrdata->subnlpheur, sol, maxviol) );
11190 }
11191
11192 return SCIP_OKAY;
11193 }
11194
11195
11196 /** domain propagation method of constraint handler */
11197 static
11198 SCIP_DECL_CONSPROP(consPropNonlinear)
11199 { /*lint --e{715}*/
11200 int nchgbds = 0;
11201
11202 SCIP_CALL( propConss(scip, conshdlr, conss, nconss, FALSE, result, &nchgbds) );
11203 assert(nchgbds >= 0);
11204
11205 /* TODO would it make sense to check for redundant constraints? */
11206
11207 return SCIP_OKAY;
11208 }
11209
11210
11211 /** presolving method of constraint handler */
11212 static
11213 SCIP_DECL_CONSPRESOL(consPresolNonlinear)
11214 { /*lint --e{715}*/
11215 SCIP_CONSHDLRDATA* conshdlrdata;
11216 SCIP_Bool infeasible;
11217 int c;
11218
11219 *result = SCIP_DIDNOTFIND;
11220
11221 if( nconss == 0 )
11222 {
11223 *result = SCIP_DIDNOTRUN;
11224 return SCIP_OKAY;
11225 }
11226
11227 conshdlrdata = SCIPconshdlrGetData(conshdlr);
11228 assert(conshdlrdata != NULL);
11229
11230 /* simplify constraints and replace common subexpressions, reinit nlhdlrs */
11231 SCIP_CALL( canonicalizeConstraints(scip, conshdlr, conss, nconss, presoltiming, &infeasible, ndelconss, naddconss, nchgcoefs) );
11232 if( infeasible )
11233 {
11234 *result = SCIP_CUTOFF;
11235 return SCIP_OKAY;
11236 }
11237
11238 /* merge constraints with the same root expression */
11239 if( presoltiming & SCIP_PRESOLTIMING_EXHAUSTIVE )
11240 {
11241 SCIP_Bool success;
11242
11243 SCIP_CALL( presolveMergeConss(scip, conss, nconss, &success) );
11244 if( success )
11245 *result = SCIP_SUCCESS;
11246 }
11247
11248 /* propagate constraints */
11249 SCIP_CALL( propConss(scip, conshdlr, conss, nconss, FALSE, result, nchgbds) );
11250 if( *result == SCIP_CUTOFF )
11251 return SCIP_OKAY;
11252
11253 /* propagate function domains (TODO integrate with simplify?) */
11254 if( (presoltiming & SCIP_PRESOLTIMING_EXHAUSTIVE) || nrounds == 0 )
11255 {
11256 SCIP_RESULT localresult;
11257 SCIP_CALL( propExprDomains(scip, conshdlr, conss, nconss, &localresult, nchgbds) );
11258 if( localresult == SCIP_CUTOFF )
11259 {
11260 *result = SCIP_CUTOFF;
11261 return SCIP_OKAY;
11262 }
11263 if( localresult == SCIP_REDUCEDDOM )
11264 *result = SCIP_REDUCEDDOM;
11265 }
11266
11267 /* check for redundant constraints, remove constraints that are a value expression */
11268 SCIP_CALL( presolveRedundantConss(scip, conshdlr, conss, nconss, &infeasible, ndelconss, nchgbds) );
11269 if( infeasible )
11270 {
11271 *result = SCIP_CUTOFF;
11272 return SCIP_OKAY;
11273 }
11274
11275 /* try to upgrade constraints */
11276 for( c = 0; c < nconss; ++c )
11277 {
11278 SCIP_Bool upgraded;
11279
11280 /* skip inactive and deleted constraints */
11281 if( SCIPconsIsDeleted(conss[c]) || !SCIPconsIsActive(conss[c]) )
11282 continue;
11283
11284 SCIP_CALL( presolveUpgrade(scip, conshdlr, conss[c], &upgraded, nupgdconss, naddconss) );
11285 }
11286
11287 /* try to change continuous variables that appear linearly to be implicit integer */
11288 if( presoltiming & SCIP_PRESOLTIMING_MEDIUM )
11289 {
11290 SCIP_CALL( presolveImplint(scip, conshdlr, conss, nconss, nchgvartypes, &infeasible) );
11291
11292 if( infeasible )
11293 {
11294 SCIPdebugMsg(scip, "presolveImplint() detected infeasibility\n");
11295 *result = SCIP_CUTOFF;
11296 return SCIP_OKAY;
11297 }
11298 }
11299
11300 /* fix variables that are contained in only one nonlinear constraint to their upper or lower bounds, if possible */
11301 if( (presoltiming & SCIP_PRESOLTIMING_EXHAUSTIVE) && SCIPisPresolveFinished(scip)
11302 && !conshdlrdata->checkedvarlocks && conshdlrdata->checkvarlocks != 'd' )
11303 {
11304 /* run this presolving technique only once because we don't want to generate identical bound disjunction
11305 * constraints multiple times
11306 */
11307 conshdlrdata->checkedvarlocks = TRUE;
11308
11309 for( c = 0; c < nconss; ++c )
11310 {
11311 int tmpnchgvartypes = 0;
11312 int tmpnaddconss = 0;
11313
11314 SCIP_CALL( presolveSingleLockedVars(scip, conshdlr, conss[c], &tmpnchgvartypes, &tmpnaddconss, &infeasible) );
11315 SCIPdebugMsg(scip, "presolSingleLockedVars() for %s: nchgvartypes=%d naddconss=%d infeas=%u\n",
11316 SCIPconsGetName(conss[c]), tmpnchgvartypes, tmpnaddconss, infeasible);
11317
11318 if( infeasible )
11319 {
11320 SCIPdebugMsg(scip, "presolSingleLockedVars() detected infeasibility\n");
11321 *result = SCIP_CUTOFF;
11322 return SCIP_OKAY;
11323 }
11324
11325 (*nchgvartypes) += tmpnchgvartypes;
11326 (*naddconss) += tmpnaddconss;
11327 }
11328 }
11329
11330 if( *ndelconss > 0 || *nchgbds > 0 || *nupgdconss > 0 || *naddconss > 0 || *nchgvartypes > 0 )
11331 *result = SCIP_SUCCESS;
11332 else
11333 *result = SCIP_DIDNOTFIND;
11334
11335 return SCIP_OKAY;
11336 }
11337
11338
11339 /** propagation conflict resolving method of constraint handler */
11340 #ifdef SCIP_DISABLED_CODE
11341 static
11342 SCIP_DECL_CONSRESPROP(consRespropNonlinear)
11343 { /*lint --e{715}*/
11344 SCIPerrorMessage("method of nonlinear constraint handler not implemented yet\n");
11345 SCIPABORT(); /*lint --e{527}*/
11346
11347 return SCIP_OKAY;
11348 }
11349 #else
11350 #define consRespropNonlinear NULL
11351 #endif
11352
11353
11354 /** variable rounding lock method of constraint handler */
11355 static
11356 SCIP_DECL_CONSLOCK(consLockNonlinear)
11357 { /*lint --e{715}*/
11358 SCIP_CONSDATA* consdata;
11359 SCIP_EXPR_OWNERDATA* ownerdata;
11360 SCIP_Bool reinitsolve = FALSE;
11361
11362 assert(conshdlr != NULL);
11363 assert(cons != NULL);
11364
11365 consdata = SCIPconsGetData(cons);
11366 assert(consdata != NULL);
11367 assert(consdata->expr != NULL);
11368
11369 ownerdata = SCIPexprGetOwnerData(consdata->expr);
11370
11371 /* check whether we need to initSolve again because
11372 * - we have enfo initialized (nenfos >= 0)
11373 * - and locks appeared (going from zero to nonzero) or disappeared (going from nonzero to zero) now
11374 */
11375 if( ownerdata->nenfos >= 0 )
11376 {
11377 if( (consdata->nlockspos == 0) != (nlockspos == 0) )
11378 reinitsolve = TRUE;
11379 if( (consdata->nlocksneg == 0) != (nlocksneg == 0) )
11380 reinitsolve = TRUE;
11381 }
11382
11383 if( reinitsolve )
11384 {
11385 SCIP_CALL( deinitSolve(scip, conshdlr, &cons, 1) );
11386 }
11387
11388 /* add locks */
11389 SCIP_CALL( addLocks(scip, cons, nlockspos, nlocksneg) );
11390
11391 if( reinitsolve )
11392 {
11393 SCIP_CALL( initSolve(scip, conshdlr, &cons, 1) );
11394 }
11395
11396 return SCIP_OKAY;
11397 }
11398
11399
11400 /** constraint activation notification method of constraint handler */
11401 static
11402 SCIP_DECL_CONSACTIVE(consActiveNonlinear)
11403 { /*lint --e{715}*/
11404 SCIP_CONSDATA* consdata;
11405 SCIP_Bool infeasible = FALSE;
11406
11407 consdata = SCIPconsGetData(cons);
11408 assert(consdata != NULL);
11409
11410 /* simplify root expression if the constraint has been added after presolving */
11411 if( SCIPgetStage(scip) > SCIP_STAGE_EXITPRESOLVE )
11412 {
11413 SCIP_Bool replacedroot;
11414
11415 if( !consdata->issimplified )
11416 {
11417 SCIP_EXPR* simplified;
11418 SCIP_Bool changed;
11419
11420 /* simplify constraint */
11421 SCIP_CALL( SCIPsimplifyExpr(scip, consdata->expr, &simplified, &changed, &infeasible, exprownerCreate, (void*)conshdlr) );
11422 SCIP_CALL( SCIPreleaseExpr(scip, &consdata->expr) );
11423 assert(simplified != NULL);
11424 consdata->expr = simplified;
11425 consdata->issimplified = TRUE;
11426 }
11427
11428 /* ensure each variable is represented by one variable expression only (need this for storeVarExprs() with simplified=TRUE below) */
11429 SCIP_CALL( SCIPreplaceCommonSubexpressions(scip, &consdata->expr, 1, &replacedroot) );
11430 assert(!replacedroot); /* root expression cannot have been equal to one of its subexpressions */
11431
11432 /* ensure that varexprs in consdata->expr are the one from var2expr hashmap */
11433 {
11434 SCIP_CONSHDLRDATA* conshdlrdata;
11435 SCIP_EXPRITER* it;
11436 SCIP_EXPR* expr;
11437
11438 conshdlrdata = SCIPconshdlrGetData(conshdlr);
11439 assert(conshdlrdata != NULL);
11440
11441 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
11442 SCIP_CALL( SCIPexpriterInit(it, consdata->expr, SCIP_EXPRITER_DFS, FALSE) );
11443 SCIPexpriterSetStagesDFS(it, SCIP_EXPRITER_VISITINGCHILD);
11444 for( expr = SCIPexpriterGetCurrent(it); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
11445 {
11446 SCIP_EXPR* child;
11447 SCIP_EXPR* hashmapexpr;
11448
11449 child = SCIPexpriterGetChildExprDFS(it);
11450 if( !SCIPisExprVar(scip, child) )
11451 continue;
11452
11453 /* check which expression is stored in the hashmap for the var of child */
11454 hashmapexpr = (SCIP_EXPR*)SCIPhashmapGetImage(conshdlrdata->var2expr, SCIPgetVarExprVar(child));
11455 /* if a varexpr exists already in the hashmap, but it is child, then replace child by the one in the hashmap */
11456 if( hashmapexpr != NULL && hashmapexpr != child )
11457 {
11458 SCIP_CALL( SCIPreplaceExprChild(scip, expr, SCIPexpriterGetChildIdxDFS(it), hashmapexpr) );
11459 }
11460 }
11461 SCIPfreeExpriter(&it);
11462 }
11463 }
11464
11465 /* store variable expressions */
11466 if( SCIPgetStage(scip) > SCIP_STAGE_TRANSFORMED )
11467 {
11468 SCIP_CALL( storeVarExprs(scip, conshdlr, consdata) );
11469 }
11470
11471 /* add manually locks to constraints that are not checked for feasibility */
11472 if( !SCIPconsIsChecked(cons) )
11473 {
11474 assert(consdata->nlockspos == 0);
11475 assert(consdata->nlocksneg == 0);
11476
11477 SCIP_CALL( addLocks(scip, cons, 1, 0) );
11478 }
11479
11480 if( SCIPgetStage(scip) > SCIP_STAGE_INITPRESOLVE && !infeasible )
11481 {
11482 SCIP_CALL( initSolve(scip, conshdlr, &cons, 1) );
11483 }
11484
11485 /* TODO deal with infeasibility */
11486 assert(!infeasible);
11487
11488 return SCIP_OKAY;
11489 }
11490
11491
11492 /** constraint deactivation notification method of constraint handler */
11493 static
11494 SCIP_DECL_CONSDEACTIVE(consDeactiveNonlinear)
11495 { /*lint --e{715}*/
11496 SCIP_CONSHDLRDATA* conshdlrdata;
11497
11498 conshdlrdata = SCIPconshdlrGetData(conshdlr);
11499 assert(conshdlrdata != NULL);
11500
11501 if( SCIPgetStage(scip) < SCIP_STAGE_EXITSOLVE )
11502 {
11503 SCIP_CALL( deinitSolve(scip, conshdlr, &cons, 1) );
11504 }
11505
11506 if( SCIPgetStage(scip) > SCIP_STAGE_TRANSFORMED )
11507 {
11508 SCIP_CALL( dropVarEvents(scip, conshdlrdata->eventhdlr, cons) );
11509 SCIP_CALL( freeVarExprs(scip, SCIPconsGetData(cons)) );
11510 }
11511
11512 /* remove locks that have been added in consActiveExpr() */
11513 if( !SCIPconsIsChecked(cons) )
11514 {
11515 SCIP_CALL( addLocks(scip, cons, -1, 0) );
11516
11517 assert(SCIPconsGetData(cons)->nlockspos == 0);
11518 assert(SCIPconsGetData(cons)->nlocksneg == 0);
11519 }
11520
11521 return SCIP_OKAY;
11522 }
11523
11524
11525 /** constraint enabling notification method of constraint handler */
11526 static
11527 SCIP_DECL_CONSENABLE(consEnableNonlinear)
11528 { /*lint --e{715}*/
11529 SCIP_CONSHDLRDATA* conshdlrdata;
11530
11531 conshdlrdata = SCIPconshdlrGetData(conshdlr);
11532 assert(conshdlrdata != NULL);
11533
11534 if( SCIPgetStage(scip) >= SCIP_STAGE_TRANSFORMED )
11535 {
11536 SCIP_CALL( catchVarEvents(scip, conshdlrdata->eventhdlr, cons) );
11537 }
11538
11539 return SCIP_OKAY;
11540 }
11541
11542
11543 /** constraint disabling notification method of constraint handler */
11544 static
11545 SCIP_DECL_CONSDISABLE(consDisableNonlinear)
11546 { /*lint --e{715}*/
11547 SCIP_CONSHDLRDATA* conshdlrdata;
11548
11549 conshdlrdata = SCIPconshdlrGetData(conshdlr);
11550 assert(conshdlrdata != NULL);
11551
11552 if( SCIPgetStage(scip) >= SCIP_STAGE_TRANSFORMED )
11553 {
11554 SCIP_CALL( dropVarEvents(scip, conshdlrdata->eventhdlr, cons) );
11555 }
11556
11557 return SCIP_OKAY;
11558 }
11559
11560 /** variable deletion of constraint handler */
11561 #ifdef SCIP_DISABLED_CODE
11562 static
11563 SCIP_DECL_CONSDELVARS(consDelvarsNonlinear)
11564 { /*lint --e{715}*/
11565 SCIPerrorMessage("method of nonlinear constraint handler not implemented yet\n");
11566 SCIPABORT(); /*lint --e{527}*/
11567
11568 return SCIP_OKAY;
11569 }
11570 #else
11571 #define consDelvarsNonlinear NULL
11572 #endif
11573
11574
11575 /** constraint display method of constraint handler */
11576 static
11577 SCIP_DECL_CONSPRINT(consPrintNonlinear)
11578 { /*lint --e{715}*/
11579 SCIP_CONSDATA* consdata;
11580
11581 consdata = SCIPconsGetData(cons);
11582 assert(consdata != NULL);
11583 assert(consdata->expr != NULL);
11584
11585 /* print left hand side for ranged constraints */
11586 if( !SCIPisInfinity(scip, -consdata->lhs) && !SCIPisInfinity(scip, consdata->rhs) && !SCIPisEQ(scip, consdata->lhs, consdata->rhs) )
11587 {
11588 SCIPinfoMessage(scip, file, "%.15g <= ", consdata->lhs);
11589 }
11590
11591 /* print expression */
11592 SCIP_CALL( SCIPprintExpr(scip, consdata->expr, file) );
11593
11594 /* print right hand side */
11595 if( SCIPisEQ(scip, consdata->lhs, consdata->rhs) )
11596 SCIPinfoMessage(scip, file, " == %.15g", consdata->rhs);
11597 else if( !SCIPisInfinity(scip, consdata->rhs) )
11598 SCIPinfoMessage(scip, file, " <= %.15g", consdata->rhs);
11599 else if( !SCIPisInfinity(scip, -consdata->lhs) )
11600 SCIPinfoMessage(scip, file, " >= %.15g", consdata->lhs);
11601 else
11602 SCIPinfoMessage(scip, file, " [free]");
11603
11604 return SCIP_OKAY;
11605 }
11606
11607
11608 /** constraint copying method of constraint handler */
11609 static
11610 SCIP_DECL_CONSCOPY(consCopyNonlinear)
11611 { /*lint --e{715}*/
11612 SCIP_CONSHDLR* targetconshdlr;
11613 SCIP_EXPR* targetexpr = NULL;
11614 SCIP_CONSDATA* sourcedata;
11615
11616 assert(cons != NULL);
11617
11618 sourcedata = SCIPconsGetData(sourcecons);
11619 assert(sourcedata != NULL);
11620
11621 targetconshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
11622 assert(targetconshdlr != NULL);
11623
11624 SCIP_CALL( SCIPcopyExpr(sourcescip, scip, sourcedata->expr, &targetexpr, exprownerCreate, (void*)targetconshdlr, varmap, consmap, global, valid) );
11625
11626 if( targetexpr == NULL )
11627 *valid = FALSE;
11628
11629 *cons = NULL;
11630 if( *valid )
11631 {
11632 /* create copy (only capture targetexpr, no need to copy again) */
11633 SCIP_CALL( createCons(scip, targetconshdlr, cons, name != NULL ? name : SCIPconsGetName(sourcecons),
11634 targetexpr, sourcedata->lhs, sourcedata->rhs, FALSE,
11635 initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable) );
11636 }
11637
11638 if( targetexpr != NULL )
11639 {
11640 /* release target expr */
11641 SCIP_CALL( SCIPreleaseExpr(scip, &targetexpr) );
11642 }
11643
11644 return SCIP_OKAY;
11645 }
11646
11647
11648 /** constraint parsing method of constraint handler */
11649 static
11650 SCIP_DECL_CONSPARSE(consParseNonlinear)
11651 { /*lint --e{715}*/
11652 SCIP_Real lhs;
11653 SCIP_Real rhs;
11654 char* endptr;
11655 SCIP_EXPR* consexprtree;
11656
11657 SCIPdebugMsg(scip, "cons_nonlinear::consparse parsing %s\n", str);
11658
11659 assert(scip != NULL);
11660 assert(success != NULL);
11661 assert(str != NULL);
11662 assert(name != NULL);
11663 assert(cons != NULL);
11664
11665 *success = FALSE;
11666
11667 /* return if string empty */
11668 if( !*str )
11669 return SCIP_OKAY;
11670
11671 endptr = (char*)str;
11672
11673 /* set left and right hand side to their default values */
11674 lhs = -SCIPinfinity(scip);
11675 rhs = SCIPinfinity(scip);
11676
11677 /* parse constraint to get lhs, rhs, and expression in between (from cons_linear.c::consparse, but parsing whole string first, then getting expression) */
11678
11679 /* check for left hand side */
11680 if( isdigit((unsigned char)str[0]) || ((str[0] == '-' || str[0] == '+') && isdigit((unsigned char)str[1])) )
11681 {
11682 /* there is a number coming, maybe it is a left-hand-side */
11683 if( !SCIPparseReal(scip, str, &lhs, &endptr) )
11684 {
11685 SCIPerrorMessage("error parsing number from <%s>\n", str);
11686 return SCIP_READERROR;
11687 }
11688
11689 /* ignore whitespace */
11690 SCIP_CALL( SCIPskipSpace(&endptr) );
11691
11692 if( endptr[0] != '<' || endptr[1] != '=' )
11693 {
11694 /* no '<=' coming, so it was the beginning of the expression and not a left-hand-side */
11695 lhs = -SCIPinfinity(scip);
11696 }
11697 else
11698 {
11699 /* it was indeed a left-hand-side, so continue parsing after it */
11700 str = endptr + 2;
11701
11702 /* ignore whitespace */
11703 SCIP_CALL( SCIPskipSpace((char**)&str) );
11704 }
11705 }
11706
11707 SCIPdebugMsg(scip, "str should start at beginning of expr: %s\n", str);
11708
11709 /* parse expression: so far we did not allocate memory, so can just return in case of readerror */
11710 SCIP_CALL( SCIPparseExpr(scip, &consexprtree, str, &str, exprownerCreate, (void*)conshdlr) );
11711
11712 /* check for left or right hand side */
11713 SCIP_CALL( SCIPskipSpace((char**)&str) );
11714
11715 /* check for free constraint */
11716 if( strncmp(str, "[free]", 6) == 0 )
11717 {
11718 if( !SCIPisInfinity(scip, -lhs) )
11719 {
11720 SCIPerrorMessage("cannot have left hand side and [free] status \n");
11721 SCIP_CALL( SCIPreleaseExpr(scip, &consexprtree) );
11722 return SCIP_OKAY;
11723 }
11724 *success = TRUE;
11725 }
11726 else
11727 {
11728 switch( *str )
11729 {
11730 case '<':
11731 *success = *(str+1) == '=' ? SCIPparseReal(scip, str+2, &rhs, &endptr) : FALSE;
11732 break;
11733 case '=':
11734 if( !SCIPisInfinity(scip, -lhs) )
11735 {
11736 SCIPerrorMessage("cannot have == on rhs if there was a <= on lhs\n");
11737 SCIP_CALL( SCIPreleaseExpr(scip, &consexprtree) );
11738 return SCIP_OKAY;
11739 }
11740 else
11741 {
11742 *success = *(str+1) == '=' ? SCIPparseReal(scip, str+2, &rhs, &endptr) : FALSE;
11743 lhs = rhs;
11744 }
11745 break;
11746 case '>':
11747 if( !SCIPisInfinity(scip, -lhs) )
11748 {
11749 SCIPerrorMessage("cannot have => on rhs if there was a <= on lhs\n");
11750 SCIP_CALL( SCIPreleaseExpr(scip, &consexprtree) );
11751 return SCIP_OKAY;
11752 }
11753 else
11754 {
11755 *success = *(str+1) == '=' ? SCIPparseReal(scip, str+2, &lhs, &endptr) : FALSE;
11756 break;
11757 }
11758 case '\0':
11759 *success = TRUE;
11760 break;
11761 default:
11762 SCIPerrorMessage("unexpected character %c\n", *str);
11763 SCIP_CALL( SCIPreleaseExpr(scip, &consexprtree) );
11764 return SCIP_OKAY;
11765 }
11766 }
11767
11768 /* create constraint */
11769 SCIP_CALL( createCons(scip, conshdlr, cons, name,
11770 consexprtree, lhs, rhs, FALSE,
11771 initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable) );
11772 assert(*cons != NULL);
11773
11774 SCIP_CALL( SCIPreleaseExpr(scip, &consexprtree) );
11775
11776 SCIPdebugMsg(scip, "created nonlinear constraint: <%s>\n", SCIPconsGetName(*cons));
11777
11778 return SCIP_OKAY;
11779 }
11780
11781
11782 /** constraint method of constraint handler which returns the variables (if possible) */
11783 static
11784 SCIP_DECL_CONSGETVARS(consGetVarsNonlinear)
11785 { /*lint --e{715}*/
11786 SCIP_CONSDATA* consdata;
11787 int i;
11788
11789 consdata = SCIPconsGetData(cons);
11790 assert(consdata != NULL);
11791
11792 /* store variable expressions if not done so far */
11793 SCIP_CALL( storeVarExprs(scip, conshdlr, consdata) );
11794
11795 /* check whether array is too small in order to store all variables */
11796 if( varssize < consdata->nvarexprs )
11797 {
11798 *success = FALSE;
11799 return SCIP_OKAY;
11800 }
11801
11802 for( i = 0; i < consdata->nvarexprs; ++i )
11803 {
11804 vars[i] = SCIPgetVarExprVar(consdata->varexprs[i]);
11805 assert(vars[i] != NULL);
11806 }
11807
11808 *success = TRUE;
11809
11810 return SCIP_OKAY;
11811 }
11812
11813 /** constraint method of constraint handler which returns the number of variables (if possible) */
11814 static
11815 SCIP_DECL_CONSGETNVARS(consGetNVarsNonlinear)
11816 { /*lint --e{715}*/
11817 SCIP_CONSDATA* consdata;
11818
11819 consdata = SCIPconsGetData(cons);
11820 assert(consdata != NULL);
11821
11822 /* store variable expressions if not done so far */
11823 SCIP_CALL( storeVarExprs(scip, conshdlr, consdata) );
11824
11825 *nvars = consdata->nvarexprs;
11826 *success = TRUE;
11827
11828 return SCIP_OKAY;
11829 }
11830
11831 /** constraint handler method to suggest dive bound changes during the generic diving algorithm */
11832 #ifdef SCIP_DISABLED_CODE
11833 static
11834 SCIP_DECL_CONSGETDIVEBDCHGS(consGetDiveBdChgsNonlinear)
11835 { /*lint --e{715}*/
11836 SCIPerrorMessage("method of nonlinear constraint handler not implemented yet\n");
11837 SCIPABORT(); /*lint --e{527}*/
11838
11839 return SCIP_OKAY;
11840 }
11841 #else
11842 #define consGetDiveBdChgsNonlinear NULL
11843 #endif
11844
11845 /** constraint handler method which returns the permutation symmetry detection graph of a constraint (if possible) */
11846 static
11847 SCIP_DECL_CONSGETPERMSYMGRAPH(consGetPermsymGraphNonlinear)
11848 { /*lint --e{715}*/
11849 SCIP_CALL( addSymmetryInformation(scip, SYM_SYMTYPE_PERM, cons, graph, success) );
11850
11851 return SCIP_OKAY;
11852 }
11853
11854 /** constraint handler method which returns the signed permutation symmetry detection graph of a constraint (if possible) */
11855 static
11856 SCIP_DECL_CONSGETSIGNEDPERMSYMGRAPH(consGetSignedPermsymGraphNonlinear)
11857 { /*lint --e{715}*/
11858 SCIP_CALL( addSymmetryInformation(scip, SYM_SYMTYPE_SIGNPERM, cons, graph, success) );
11859
11860 return SCIP_OKAY;
11861 }
11862
11863 /** output method of statistics table to output file stream 'file' */
11864 static
11865 SCIP_DECL_TABLEOUTPUT(tableOutputNonlinear)
11866 { /*lint --e{715}*/
11867 SCIP_CONSHDLR* conshdlr;
11868 SCIP_CONSHDLRDATA* conshdlrdata;
11869
11870 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
11871 assert(conshdlr != NULL);
11872
11873 conshdlrdata = SCIPconshdlrGetData(conshdlr);
11874 assert(conshdlrdata != NULL);
11875
11876 /* print statistics for constraint handler */
11877 SCIPinfoMessage(scip, file, "Nonlinear Conshdlr : %10s %10s %10s %10s %10s %10s %10s\n", "WeakSepa", "TightenLP", "DespTghtLP", "DespBranch", "DespCutoff", "ForceLP", "CanonTime");
11878 SCIPinfoMessage(scip, file, " enforce%-10s:", "");
11879 SCIPinfoMessage(scip, file, " %10lld", conshdlrdata->nweaksepa);
11880 SCIPinfoMessage(scip, file, " %10lld", conshdlrdata->ntightenlp);
11881 SCIPinfoMessage(scip, file, " %10lld", conshdlrdata->ndesperatetightenlp);
11882 SCIPinfoMessage(scip, file, " %10lld", conshdlrdata->ndesperatebranch);
11883 SCIPinfoMessage(scip, file, " %10lld", conshdlrdata->ndesperatecutoff);
11884 SCIPinfoMessage(scip, file, " %10lld", conshdlrdata->nforcelp);
11885 SCIPinfoMessage(scip, file, "\n");
11886 SCIPinfoMessage(scip, file, " presolve%-9s: %-65s", "", "");
11887 SCIPinfoMessage(scip, file, " %10.2f", SCIPgetClockTime(scip, conshdlrdata->canonicalizetime));
11888 SCIPinfoMessage(scip, file, "\n");
11889
11890 return SCIP_OKAY;
11891 }
11892
11893 /** output method of statistics table to output file stream 'file' */
11894 static
11895 SCIP_DECL_TABLEOUTPUT(tableOutputNlhdlr)
11896 { /*lint --e{715}*/
11897 SCIP_CONSHDLR* conshdlr;
11898 SCIP_CONSHDLRDATA* conshdlrdata;
11899
11900 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
11901 assert(conshdlr != NULL);
11902
11903 /* skip nlhdlr table if there never were active nonlinear constraints */
11904 if( SCIPconshdlrGetMaxNActiveConss(conshdlr) == 0 )
11905 return SCIP_OKAY;
11906
11907 conshdlrdata = SCIPconshdlrGetData(conshdlr);
11908 assert(conshdlrdata != NULL);
11909
11910 /* print statistics for nonlinear handlers */
11911 SCIPnlhdlrPrintStatistics(scip, conshdlrdata->nlhdlrs, conshdlrdata->nnlhdlrs, file);
11912
11913 return SCIP_OKAY;
11914 }
11915
11916 /** execution method of display nlhdlrs dialog */
11917 static
11918 SCIP_DECL_DIALOGEXEC(dialogExecDisplayNlhdlrs)
11919 { /*lint --e{715}*/
11920 SCIP_CONSHDLR* conshdlr;
11921 SCIP_CONSHDLRDATA* conshdlrdata;
11922 int i;
11923
11924 /* add dialog to history of dialogs that have been executed */
11925 SCIP_CALL( SCIPdialoghdlrAddHistory(dialoghdlr, dialog, NULL, FALSE) );
11926
11927 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
11928 assert(conshdlr != NULL);
11929
11930 conshdlrdata = SCIPconshdlrGetData(conshdlr);
11931 assert(conshdlrdata != NULL);
11932
11933 /* display list of nonlinear handler */
11934 SCIPdialogMessage(scip, NULL, "\n");
11935 SCIPdialogMessage(scip, NULL, " nonlinear handler enabled detectprio enforceprio description\n");
11936 SCIPdialogMessage(scip, NULL, " ----------------- ------- ---------- ----------- -----------\n");
11937 for( i = 0; i < conshdlrdata->nnlhdlrs; ++i )
11938 {
11939 SCIP_NLHDLR* nlhdlr = conshdlrdata->nlhdlrs[i];
11940 assert(nlhdlr != NULL);
11941
11942 SCIPdialogMessage(scip, NULL, " %-17s ", SCIPnlhdlrGetName(nlhdlr));
11943 SCIPdialogMessage(scip, NULL, " %7s ", SCIPnlhdlrIsEnabled(nlhdlr) ? "yes" : "no");
11944 SCIPdialogMessage(scip, NULL, " %10d ", SCIPnlhdlrGetDetectPriority(nlhdlr));
11945 SCIPdialogMessage(scip, NULL, " %11d ", SCIPnlhdlrGetEnfoPriority(nlhdlr));
11946 SCIPdialogMessage(scip, NULL, " %s", SCIPnlhdlrGetDesc(nlhdlr));
11947 SCIPdialogMessage(scip, NULL, "\n");
11948 }
11949 SCIPdialogMessage(scip, NULL, "\n");
11950
11951 /* next dialog will be root dialog again */
11952 *nextdialog = SCIPdialoghdlrGetRoot(dialoghdlr);
11953
11954 return SCIP_OKAY;
11955 }
11956
11957 /*
11958 * constraint handler specific interface methods
11959 */
11960
11961 /** creates the handler for nonlinear constraints and includes it in SCIP */
11962 SCIP_RETCODE SCIPincludeConshdlrNonlinear(
11963 SCIP* scip /**< SCIP data structure */
11964 )
11965 {
11966 SCIP_CONSHDLRDATA* conshdlrdata;
11967 SCIP_DIALOG* parentdialog;
11968
11969 /* create nonlinear constraint handler data */
11970 SCIP_CALL( SCIPallocClearBlockMemory(scip, &conshdlrdata) );
11971 conshdlrdata->intevalvar = intEvalVarBoundTightening;
11972 conshdlrdata->curboundstag = 1;
11973 conshdlrdata->lastboundrelax = 1;
11974 conshdlrdata->curpropboundstag = 1;
11975 conshdlrdata->newsoleventfilterpos = -1;
11976 SCIP_CALL( SCIPcreateClock(scip, &conshdlrdata->canonicalizetime) );
11977 SCIP_CALL( SCIPqueueCreate(&conshdlrdata->reversepropqueue, 100, 2.0) );
11978 SCIP_CALL( SCIPhashmapCreate(&conshdlrdata->var2expr, SCIPblkmem(scip), 100) );
11979
11980 /* include constraint handler */
11981 SCIP_CALL( SCIPincludeConshdlr(scip, CONSHDLR_NAME, CONSHDLR_DESC,
11982 CONSHDLR_SEPAPRIORITY, CONSHDLR_ENFOPRIORITY, CONSHDLR_CHECKPRIORITY,
11983 CONSHDLR_SEPAFREQ, CONSHDLR_PROPFREQ, CONSHDLR_EAGERFREQ, CONSHDLR_MAXPREROUNDS,
11984 CONSHDLR_DELAYSEPA, CONSHDLR_DELAYPROP, CONSHDLR_NEEDSCONS,
11985 CONSHDLR_PROP_TIMING, CONSHDLR_PRESOLTIMING,
11986 conshdlrCopyNonlinear,
11987 consFreeNonlinear, consInitNonlinear, consExitNonlinear,
11988 consInitpreNonlinear, consExitpreNonlinear, consInitsolNonlinear, consExitsolNonlinear,
11989 consDeleteNonlinear, consTransNonlinear, consInitlpNonlinear,
11990 consSepalpNonlinear, consSepasolNonlinear, consEnfolpNonlinear, consEnforelaxNonlinear, consEnfopsNonlinear, consCheckNonlinear,
11991 consPropNonlinear, consPresolNonlinear, consRespropNonlinear, consLockNonlinear,
11992 consActiveNonlinear, consDeactiveNonlinear,
11993 consEnableNonlinear, consDisableNonlinear, consDelvarsNonlinear,
11994 consPrintNonlinear, consCopyNonlinear, consParseNonlinear,
11995 consGetVarsNonlinear, consGetNVarsNonlinear, consGetDiveBdChgsNonlinear, consGetPermsymGraphNonlinear,
11996 consGetSignedPermsymGraphNonlinear, conshdlrdata) );
11997
11998 /* add nonlinear constraint handler parameters */
11999 /* TODO organize into more subcategories */
12000 SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/maxproprounds",
12001 "limit on number of propagation rounds for a set of constraints within one round of SCIP propagation",
12002 &conshdlrdata->maxproprounds, FALSE, 10, 0, INT_MAX, NULL, NULL) );
12003
12004 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/propauxvars",
12005 "whether to check bounds of all auxiliary variable to seed reverse propagation",
12006 &conshdlrdata->propauxvars, TRUE, TRUE, NULL, NULL) );
12007
12008 SCIP_CALL( SCIPaddCharParam(scip, "constraints/" CONSHDLR_NAME "/varboundrelax",
12009 "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",
12010 &conshdlrdata->varboundrelax, TRUE, 'r', "nabr", NULL, NULL) );
12011
12012 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/varboundrelaxamount",
12013 "by how much to relax variable bounds during bound tightening if strategy 'a', 'b', or 'r'",
12014 &conshdlrdata->varboundrelaxamount, TRUE, SCIPepsilon(scip), 0.0, 1.0, NULL, NULL) );
12015
12016 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/conssiderelaxamount",
12017 "by how much to relax constraint sides during bound tightening",
12018 &conshdlrdata->conssiderelaxamount, TRUE, SCIPepsilon(scip), 0.0, 1.0, NULL, NULL) );
12019
12020 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/vpmaxperturb",
12021 "maximal relative perturbation of reference point when computing facet of envelope of vertex-polyhedral function (dim>2)",
12022 &conshdlrdata->vp_maxperturb, TRUE, VERTEXPOLY_MAXPERTURBATION, 0.0, 1.0, NULL, NULL) );
12023
12024 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/vpadjfacetthresh",
12025 "adjust computed facet of envelope of vertex-polyhedral function up to a violation of this value times LP feasibility tolerance",
12026 &conshdlrdata->vp_adjfacetthreshold, TRUE, VERTEXPOLY_ADJUSTFACETFACTOR, 0.0, SCIP_REAL_MAX, NULL, NULL) );
12027
12028 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/vpdualsimplex",
12029 "whether to use dual simplex instead of primal simplex for LP that computes facet of vertex-polyhedral function",
12030 &conshdlrdata->vp_dualsimplex, TRUE, VERTEXPOLY_USEDUALSIMPLEX, NULL, NULL) );
12031
12032 SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/bilinmaxnauxexprs",
12033 "maximal number of auxiliary expressions per bilinear term",
12034 &conshdlrdata->bilinmaxnauxexprs, FALSE, BILIN_MAXNAUXEXPRS, 0, INT_MAX, NULL, NULL) );
12035
12036 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/reformbinprods",
12037 "whether to reformulate products of binary variables during presolving",
12038 &conshdlrdata->reformbinprods, FALSE, TRUE, NULL, NULL) );
12039
12040 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/reformbinprodsand",
12041 "whether to use the AND constraint handler for reformulating binary products",
12042 &conshdlrdata->reformbinprodsand, FALSE, TRUE, NULL, NULL) );
12043
12044 SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/reformbinprodsfac",
12045 "minimum number of terms to reformulate bilinear binary products by factorizing variables (<= 1: disabled)",
12046 &conshdlrdata->reformbinprodsfac, FALSE, 50, 1, INT_MAX, NULL, NULL) );
12047
12048 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/forbidmultaggrnlvar",
12049 "whether to forbid multiaggregation of nonlinear variables",
12050 &conshdlrdata->forbidmultaggrnlvar, TRUE, TRUE, NULL, NULL) );
12051
12052 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/tightenlpfeastol",
12053 "whether to tighten LP feasibility tolerance during enforcement, if it seems useful",
12054 &conshdlrdata->tightenlpfeastol, TRUE, TRUE, NULL, NULL) );
12055
12056 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/propinenforce",
12057 "whether to (re)run propagation in enforcement",
12058 &conshdlrdata->propinenforce, TRUE, FALSE, NULL, NULL) );
12059
12060 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/weakcutthreshold",
12061 "threshold for when to regard a cut from an estimator as weak (lower values allow more weak cuts)",
12062 &conshdlrdata->weakcutthreshold, TRUE, 0.2, 0.0, 1.0, NULL, NULL) );
12063
12064 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/strongcutmaxcoef",
12065 "\"strong\" cuts will be scaled to have their maximal coef in [1/strongcutmaxcoef,strongcutmaxcoef]",
12066 &conshdlrdata->strongcutmaxcoef, TRUE, 1000.0, 1.0, SCIPinfinity(scip), NULL, NULL) );
12067
12068 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/strongcutefficacy",
12069 "consider efficacy requirement when deciding whether a cut is \"strong\"",
12070 &conshdlrdata->strongcutefficacy, TRUE, FALSE, NULL, NULL) );
12071
12072 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/forcestrongcut",
12073 "whether to force \"strong\" cuts in enforcement",
12074 &conshdlrdata->forcestrongcut, TRUE, FALSE, NULL, NULL) );
12075
12076 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/enfoauxviolfactor",
12077 "an expression will be enforced if the \"auxiliary\" violation is at least this factor times the \"original\" violation",
12078 &conshdlrdata->enfoauxviolfactor, TRUE, 0.01, 0.0, 1.0, NULL, NULL) );
12079
12080 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/weakcutminviolfactor",
12081 "retry enfo of constraint with weak cuts if violation is least this factor of maximal violated constraints",
12082 &conshdlrdata->weakcutminviolfactor, TRUE, 0.5, 0.0, 2.0, NULL, NULL) );
12083
12084 SCIP_CALL( SCIPaddCharParam(scip, "constraints/" CONSHDLR_NAME "/rownotremovable",
12085 "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",
12086 &conshdlrdata->rownotremovable, TRUE, 'o', "oea", NULL, NULL) );
12087
12088 SCIP_CALL( SCIPaddCharParam(scip, "constraints/" CONSHDLR_NAME "/violscale",
12089 "method how to scale violations to make them comparable (not used for feasibility check): (n)one, (a)ctivity and side, norm of (g)radient",
12090 &conshdlrdata->violscale, TRUE, 'n', "nag", NULL, NULL) );
12091
12092 SCIP_CALL( SCIPaddCharParam(scip, "constraints/" CONSHDLR_NAME "/checkvarlocks",
12093 "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)",
12094 &conshdlrdata->checkvarlocks, TRUE, 't', "bdt", NULL, NULL) );
12095
12096 SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/branching/aux",
12097 "from which depth on in the tree to allow branching on auxiliary variables (variables added for extended formulation)",
12098 &conshdlrdata->branchauxmindepth, FALSE, INT_MAX, 0, INT_MAX, NULL, NULL) );
12099
12100 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/branching/external",
12101 "whether to use external branching candidates and branching rules for branching",
12102 &conshdlrdata->branchexternal, FALSE, FALSE, NULL, NULL) );
12103
12104 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/branching/highviolfactor",
12105 "consider a constraint highly violated if its violation is >= this factor * maximal violation among all constraints",
12106 &conshdlrdata->branchhighviolfactor, FALSE, 0.0, 0.0, 1.0, NULL, NULL) );
12107
12108 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/branching/highscorefactor",
12109 "consider a variable branching score high if its branching score >= this factor * maximal branching score among all variables",
12110 &conshdlrdata->branchhighscorefactor, FALSE, 0.9, 0.0, 1.0, NULL, NULL) );
12111
12112 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/branching/violweight",
12113 "weight by how much to consider the violation assigned to a variable for its branching score",
12114 &conshdlrdata->branchviolweight, FALSE, 1.0, 0.0, SCIPinfinity(scip), NULL, NULL) );
12115
12116 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/branching/dualweight",
12117 "weight by how much to consider the dual values of rows that contain a variable for its branching score",
12118 &conshdlrdata->branchdualweight, FALSE, 0.0, 0.0, SCIPinfinity(scip), NULL, NULL) );
12119
12120 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/branching/pscostweight",
12121 "weight by how much to consider the pseudo cost of a variable for its branching score",
12122 &conshdlrdata->branchpscostweight, FALSE, 1.0, 0.0, SCIPinfinity(scip), NULL, NULL) );
12123
12124 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/branching/domainweight",
12125 "weight by how much to consider the domain width in branching score",
12126 &conshdlrdata->branchdomainweight, FALSE, 0.0, 0.0, SCIPinfinity(scip), NULL, NULL) );
12127
12128 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/branching/vartypeweight",
12129 "weight by how much to consider variable type (continuous: 0, binary: 1, integer: 0.1, impl-integer: 0.01) in branching score",
12130 &conshdlrdata->branchvartypeweight, FALSE, 0.5, 0.0, SCIPinfinity(scip), NULL, NULL) );
12131
12132 SCIP_CALL( SCIPaddCharParam(scip, "constraints/" CONSHDLR_NAME "/branching/scoreagg",
12133 "how to aggregate several branching scores given for the same expression: 'a'verage, 'm'aximum, 's'um",
12134 &conshdlrdata->branchscoreagg, FALSE, 's', "ams", NULL, NULL) );
12135
12136 SCIP_CALL( SCIPaddCharParam(scip, "constraints/" CONSHDLR_NAME "/branching/violsplit",
12137 "method used to split violation in expression onto variables: 'u'niform, 'm'idness of solution, 'd'omain width, 'l'ogarithmic domain width",
12138 &conshdlrdata->branchviolsplit, FALSE, 'm', "umdl", NULL, NULL) );
12139
12140 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/branching/pscostreliable",
12141 "minimum pseudo-cost update count required to consider pseudo-costs reliable",
12142 &conshdlrdata->branchpscostreliable, FALSE, 2.0, 0.0, SCIPinfinity(scip), NULL, NULL) );
12143
12144 SCIP_CALL( SCIPaddCharParam(scip, "constraints/" CONSHDLR_NAME "/linearizeheursol",
12145 "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)",
12146 &conshdlrdata->linearizeheursol, FALSE, 'o', "oie", NULL, NULL) );
12147
12148 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/assumeconvex",
12149 "whether to assume that any constraint is convex",
12150 &conshdlrdata->assumeconvex, FALSE, FALSE, NULL, NULL) );
12151
12152 /* include handler for bound change events */
12153 SCIP_CALL( SCIPincludeEventhdlrBasic(scip, &conshdlrdata->eventhdlr, CONSHDLR_NAME "_boundchange",
12154 "signals a bound change to a nonlinear constraint", processVarEvent, NULL) );
12155 assert(conshdlrdata->eventhdlr != NULL);
12156
12157 /* include tables for statistics */
12158 assert(SCIPfindTable(scip, TABLE_NAME_NONLINEAR) == NULL);
12159 SCIP_CALL( SCIPincludeTable(scip, TABLE_NAME_NONLINEAR, TABLE_DESC_NONLINEAR, FALSE,
12160 NULL, NULL, NULL, NULL, NULL, NULL, tableOutputNonlinear,
12161 NULL, TABLE_POSITION_NONLINEAR, TABLE_EARLIEST_STAGE_NONLINEAR) );
12162
12163 assert(SCIPfindTable(scip, TABLE_NAME_NLHDLR) == NULL);
12164 SCIP_CALL( SCIPincludeTable(scip, TABLE_NAME_NLHDLR, TABLE_DESC_NLHDLR, TRUE,
12165 NULL, NULL, NULL, NULL, NULL, NULL, tableOutputNlhdlr,
12166 NULL, TABLE_POSITION_NLHDLR, TABLE_EARLIEST_STAGE_NLHDLR) );
12167
12168 /* create, include, and release display nlhdlrs dialog */
12169 if( SCIPgetRootDialog(scip) != NULL && SCIPdialogFindEntry(SCIPgetRootDialog(scip), "display", &parentdialog) == 1 )
12170 {
12171 SCIP_DIALOG* dialog;
12172
12173 assert(parentdialog != NULL);
12174 assert(!SCIPdialogHasEntry(parentdialog, DIALOG_NAME));
12175
12176 SCIP_CALL( SCIPincludeDialog(scip, &dialog,
12177 NULL, dialogExecDisplayNlhdlrs, NULL, NULL,
12178 DIALOG_NAME, DIALOG_DESC, DIALOG_ISSUBMENU, NULL) );
12179 SCIP_CALL( SCIPaddDialogEntry(scip, parentdialog, dialog) );
12180 SCIP_CALL( SCIPreleaseDialog(scip, &dialog) );
12181 }
12182
12183 SCIP_CALL( SCIPincludeEventhdlrBasic(scip, NULL, CONSHDLR_NAME "_newsolution", "handles the event that a new primal solution has been found",
12184 processNewSolutionEvent, NULL) );
12185
12186 return SCIP_OKAY;
12187 }
12188
12189 /** includes a nonlinear constraint upgrade method into the nonlinear constraint handler */
12190 SCIP_RETCODE SCIPincludeConsUpgradeNonlinear(
12191 SCIP* scip, /**< SCIP data structure */
12192 SCIP_DECL_NONLINCONSUPGD((*nlconsupgd)), /**< method to call for upgrading nonlinear constraint */
12193 int priority, /**< priority of upgrading method */
12194 SCIP_Bool active, /**< should the upgrading method by active by default? */
12195 const char* conshdlrname /**< name of the constraint handler */
12196 )
12197 {
12198 SCIP_CONSHDLR* conshdlr;
12199 SCIP_CONSHDLRDATA* conshdlrdata;
12200 CONSUPGRADE* consupgrade;
12201 char paramname[SCIP_MAXSTRLEN];
12202 char paramdesc[SCIP_MAXSTRLEN];
12203 int i;
12204
12205 assert(conshdlrname != NULL );
12206 assert(nlconsupgd != NULL);
12207
12208 /* find the nonlinear constraint handler */
12209 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
12210 if( conshdlr == NULL )
12211 {
12212 SCIPerrorMessage("nonlinear constraint handler not found\n");
12213 return SCIP_PLUGINNOTFOUND;
12214 }
12215
12216 conshdlrdata = SCIPconshdlrGetData(conshdlr);
12217 assert(conshdlrdata != NULL);
12218
12219 /* check whether upgrade method exists already */
12220 for( i = conshdlrdata->nconsupgrades - 1; i >= 0; --i )
12221 {
12222 if( conshdlrdata->consupgrades[i]->consupgd == nlconsupgd )
12223 {
12224 #ifdef SCIP_DEBUG
12225 SCIPwarningMessage(scip, "Try to add already known upgrade method for constraint handler <%s>.\n", conshdlrname);
12226 #endif
12227 return SCIP_OKAY;
12228 }
12229 }
12230
12231 /* create a nonlinear constraint upgrade data object */
12232 SCIP_CALL( SCIPallocBlockMemory(scip, &consupgrade) );
12233 consupgrade->consupgd = nlconsupgd;
12234 consupgrade->priority = priority;
12235 consupgrade->active = active;
12236
12237 /* insert nonlinear constraint upgrade method into constraint handler data */
12238 SCIP_CALL( SCIPensureBlockMemoryArray(scip, &conshdlrdata->consupgrades, &conshdlrdata->consupgradessize, conshdlrdata->nconsupgrades+1) );
12239 assert(conshdlrdata->nconsupgrades+1 <= conshdlrdata->consupgradessize);
12240
12241 for( i = conshdlrdata->nconsupgrades; i > 0 && conshdlrdata->consupgrades[i-1]->priority < consupgrade->priority; --i )
12242 conshdlrdata->consupgrades[i] = conshdlrdata->consupgrades[i-1];
12243 assert(0 <= i && i <= conshdlrdata->nconsupgrades);
12244 conshdlrdata->consupgrades[i] = consupgrade;
12245 conshdlrdata->nconsupgrades++;
12246
12247 /* adds parameter to turn on and off the upgrading step */
12248 (void) SCIPsnprintf(paramname, SCIP_MAXSTRLEN, "constraints/" CONSHDLR_NAME "/upgrade/%s", conshdlrname);
12249 (void) SCIPsnprintf(paramdesc, SCIP_MAXSTRLEN, "enable nonlinear upgrading for constraint handler <%s>", conshdlrname);
12250 SCIP_CALL( SCIPaddBoolParam(scip,
12251 paramname, paramdesc,
12252 &consupgrade->active, FALSE, active, NULL, NULL) );
12253
12254 return SCIP_OKAY;
12255 }
12256
12257 /** creates and captures a nonlinear constraint
12258 *
12259 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
12260 */
12261 SCIP_RETCODE SCIPcreateConsNonlinear(
12262 SCIP* scip, /**< SCIP data structure */
12263 SCIP_CONS** cons, /**< pointer to hold the created constraint */
12264 const char* name, /**< name of constraint */
12265 SCIP_EXPR* expr, /**< expression of constraint (must not be NULL) */
12266 SCIP_Real lhs, /**< left hand side of constraint */
12267 SCIP_Real rhs, /**< right hand side of constraint */
12268 SCIP_Bool initial, /**< should the LP relaxation of constraint be in the initial LP?
12269 * Usually set to TRUE. Set to FALSE for 'lazy constraints'. */
12270 SCIP_Bool separate, /**< should the constraint be separated during LP processing?
12271 * Usually set to TRUE. */
12272 SCIP_Bool enforce, /**< should the constraint be enforced during node processing?
12273 * TRUE for model constraints, FALSE for additional, redundant constraints. */
12274 SCIP_Bool check, /**< should the constraint be checked for feasibility?
12275 * TRUE for model constraints, FALSE for additional, redundant constraints. */
12276 SCIP_Bool propagate, /**< should the constraint be propagated during node processing?
12277 * Usually set to TRUE. */
12278 SCIP_Bool local, /**< is constraint only valid locally?
12279 * Usually set to FALSE. Has to be set to TRUE, e.g., for branching constraints. */
12280 SCIP_Bool modifiable, /**< is constraint modifiable (subject to column generation)?
12281 * Usually set to FALSE. In column generation applications, set to TRUE if pricing
12282 * adds coefficients to this constraint. */
12283 SCIP_Bool dynamic, /**< is constraint subject to aging?
12284 * Usually set to FALSE. Set to TRUE for own cuts which
12285 * are separated as constraints. */
12286 SCIP_Bool removable /**< should the relaxation be removed from the LP due to aging or cleanup?
12287 * Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */
12288 )
12289 {
12290 /* TODO: (optional) modify the definition of the SCIPcreateConsNonlinear() call, if you don't need all the information */
12291 SCIP_CONSHDLR* conshdlr;
12292
12293 /* find the nonlinear constraint handler */
12294 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
12295 if( conshdlr == NULL )
12296 {
12297 SCIPerrorMessage("nonlinear constraint handler not found\n");
12298 return SCIP_PLUGINNOTFOUND;
12299 }
12300
12301 /* create constraint */
12302 SCIP_CALL( createCons(scip, conshdlr, cons, name, expr, lhs, rhs, TRUE,
12303 initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable) );
12304
12305 return SCIP_OKAY;
12306 }
12307
12308 /** creates and captures a nonlinear constraint with all its constraint flags set to their default values
12309 *
12310 * All flags can be set via SCIPconsSetFLAGNAME-methods.
12311 *
12312 * @see SCIPcreateConsNonlinear() for information about the basic constraint flag configuration.
12313 *
12314 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
12315 */
12316 SCIP_RETCODE SCIPcreateConsBasicNonlinear(
12317 SCIP* scip, /**< SCIP data structure */
12318 SCIP_CONS** cons, /**< pointer to hold the created constraint */
12319 const char* name, /**< name of constraint */
12320 SCIP_EXPR* expr, /**< expression of constraint (must not be NULL) */
12321 SCIP_Real lhs, /**< left hand side of constraint */
12322 SCIP_Real rhs /**< right hand side of constraint */
12323 )
12324 {
12325 SCIP_CALL( SCIPcreateConsNonlinear(scip, cons, name, expr, lhs, rhs,
12326 TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE) );
12327
12328 return SCIP_OKAY;
12329 }
12330
12331 /** creates and captures a quadratic nonlinear constraint
12332 *
12333 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
12334 */
12335 SCIP_RETCODE SCIPcreateConsQuadraticNonlinear(
12336 SCIP* scip, /**< SCIP data structure */
12337 SCIP_CONS** cons, /**< pointer to hold the created constraint */
12338 const char* name, /**< name of constraint */
12339 int nlinvars, /**< number of linear terms */
12340 SCIP_VAR** linvars, /**< array with variables in linear part */
12341 SCIP_Real* lincoefs, /**< array with coefficients of variables in linear part */
12342 int nquadterms, /**< number of quadratic terms */
12343 SCIP_VAR** quadvars1, /**< array with first variables in quadratic terms */
12344 SCIP_VAR** quadvars2, /**< array with second variables in quadratic terms */
12345 SCIP_Real* quadcoefs, /**< array with coefficients of quadratic terms */
12346 SCIP_Real lhs, /**< left hand side of quadratic equation */
12347 SCIP_Real rhs, /**< right hand side of quadratic equation */
12348 SCIP_Bool initial, /**< should the LP relaxation of constraint be in the initial LP?
12349 * Usually set to TRUE. Set to FALSE for 'lazy constraints'. */
12350 SCIP_Bool separate, /**< should the constraint be separated during LP processing?
12351 * Usually set to TRUE. */
12352 SCIP_Bool enforce, /**< should the constraint be enforced during node processing?
12353 * TRUE for model constraints, FALSE for additional, redundant constraints. */
12354 SCIP_Bool check, /**< should the constraint be checked for feasibility?
12355 * TRUE for model constraints, FALSE for additional, redundant constraints. */
12356 SCIP_Bool propagate, /**< should the constraint be propagated during node processing?
12357 * Usually set to TRUE. */
12358 SCIP_Bool local, /**< is constraint only valid locally?
12359 * Usually set to FALSE. Has to be set to TRUE, e.g., for branching constraints. */
12360 SCIP_Bool modifiable, /**< is constraint modifiable (subject to column generation)?
12361 * Usually set to FALSE. In column generation applications, set to TRUE if pricing
12362 * adds coefficients to this constraint. */
12363 SCIP_Bool dynamic, /**< is constraint subject to aging?
12364 * Usually set to FALSE. Set to TRUE for own cuts which
12365 * are separated as constraints. */
12366 SCIP_Bool removable /**< should the relaxation be removed from the LP due to aging or cleanup?
12367 * Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */
12368 )
12369 {
12370 SCIP_CONSHDLR* conshdlr;
12371 SCIP_EXPR* expr;
12372
12373 assert(nlinvars == 0 || (linvars != NULL && lincoefs != NULL));
12374 assert(nquadterms == 0 || (quadvars1 != NULL && quadvars2 != NULL && quadcoefs != NULL));
12375
12376 /* get nonlinear constraint handler */
12377 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
12378 if( conshdlr == NULL )
12379 {
12380 SCIPerrorMessage("nonlinear constraint handler not found\n");
12381 return SCIP_PLUGINNOTFOUND;
12382 }
12383
12384 /* create quadratic expression */
12385 SCIP_CALL( SCIPcreateExprQuadratic(scip, &expr, nlinvars, linvars, lincoefs, nquadterms, quadvars1, quadvars2, quadcoefs, exprownerCreate, (void*)conshdlr) );
12386 assert(expr != NULL);
12387
12388 /* create nonlinear constraint */
12389 SCIP_CALL( createCons(scip, conshdlr, cons, name, expr, lhs, rhs, FALSE,
12390 initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable) );
12391
12392 /* release quadratic expression (captured by constraint now) */
12393 SCIP_CALL( SCIPreleaseExpr(scip, &expr) );
12394
12395 return SCIP_OKAY;
12396 }
12397
12398 /** creates and captures a quadratic nonlinear constraint with all its constraint flags set to their default values
12399 *
12400 * All flags can be set via SCIPconsSetFLAGNAME-methods.
12401 *
12402 * @see SCIPcreateConsQuadraticNonlinear() for information about the basic constraint flag configuration.
12403 *
12404 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
12405 */
12406 SCIP_RETCODE SCIPcreateConsBasicQuadraticNonlinear(
12407 SCIP* scip, /**< SCIP data structure */
12408 SCIP_CONS** cons, /**< pointer to hold the created constraint */
12409 const char* name, /**< name of constraint */
12410 int nlinvars, /**< number of linear terms */
12411 SCIP_VAR** linvars, /**< array with variables in linear part */
12412 SCIP_Real* lincoefs, /**< array with coefficients of variables in linear part */
12413 int nquadterms, /**< number of quadratic terms */
12414 SCIP_VAR** quadvars1, /**< array with first variables in quadratic terms */
12415 SCIP_VAR** quadvars2, /**< array with second variables in quadratic terms */
12416 SCIP_Real* quadcoefs, /**< array with coefficients of quadratic terms */
12417 SCIP_Real lhs, /**< left hand side of quadratic equation */
12418 SCIP_Real rhs /**< right hand side of quadratic equation */
12419 )
12420 {
12421 SCIP_CALL( SCIPcreateConsQuadraticNonlinear(scip, cons, name, nlinvars, linvars, lincoefs, nquadterms, quadvars1, quadvars2, quadcoefs, lhs, rhs,
12422 TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE) );
12423
12424 return SCIP_OKAY;
12425 }
12426
12427 /** creates and captures a nonlinear constraint that is a second-order cone constraint with all its constraint flags set to their default values
12428 *
12429 * \f$\sqrt{\gamma + \sum_{i=1}^{n} (\alpha_i\, (x_i + \beta_i))^2} \leq \alpha_{n+1}\, (x_{n+1}+\beta_{n+1})\f$
12430 *
12431 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
12432 */
12433 SCIP_RETCODE SCIPcreateConsBasicSOCNonlinear(
12434 SCIP* scip, /**< SCIP data structure */
12435 SCIP_CONS** cons, /**< pointer to hold the created constraint */
12436 const char* name, /**< name of constraint */
12437 int nvars, /**< number of variables on left hand side of constraint (n) */
12438 SCIP_VAR** vars, /**< array with variables on left hand side (x_i) */
12439 SCIP_Real* coefs, /**< array with coefficients of left hand side variables (alpha_i), or NULL if all 1.0 */
12440 SCIP_Real* offsets, /**< array with offsets of variables (beta_i), or NULL if all 0.0 */
12441 SCIP_Real constant, /**< constant on left hand side (gamma) */
12442 SCIP_VAR* rhsvar, /**< variable on right hand side of constraint (x_{n+1}) */
12443 SCIP_Real rhscoeff, /**< coefficient of variable on right hand side (alpha_{n+1}) */
12444 SCIP_Real rhsoffset /**< offset of variable on right hand side (beta_{n+1}) */
12445 )
12446 {
12447 SCIP_EXPR* expr;
12448 SCIP_EXPR* lhssum;
12449 SCIP_EXPR* terms[2];
12450 SCIP_Real termcoefs[2];
12451 int i;
12452
12453 assert(vars != NULL || nvars == 0);
12454
12455 SCIP_CALL( SCIPcreateExprSum(scip, &lhssum, 0, NULL, NULL, constant, NULL, NULL) ); /* gamma */
12456 for( i = 0; i < nvars; ++i )
12457 {
12458 SCIP_EXPR* varexpr;
12459 SCIP_EXPR* powexpr;
12460
12461 SCIP_CALL( SCIPcreateExprVar(scip, &varexpr, vars[i], NULL, NULL) ); /* x_i */
12462 if( offsets != NULL && offsets[i] != 0.0 )
12463 {
12464 SCIP_EXPR* sum;
12465 SCIP_CALL( SCIPcreateExprSum(scip, &sum, 1, &varexpr, NULL, offsets[i], NULL, NULL) ); /* x_i + beta_i */
12466 SCIP_CALL( SCIPcreateExprPow(scip, &powexpr, sum, 2.0, NULL, NULL) ); /* (x_i + beta_i)^2 */
12467 SCIP_CALL( SCIPreleaseExpr(scip, &sum) );
12468 }
12469 else
12470 {
12471 SCIP_CALL( SCIPcreateExprPow(scip, &powexpr, varexpr, 2.0, NULL, NULL) ); /* x_i^2 */
12472 }
12473
12474 SCIP_CALL( SCIPappendExprSumExpr(scip, lhssum, powexpr, coefs != NULL ? coefs[i]*coefs[i] : 1.0) ); /* + alpha_i^2 (x_i + beta_i)^2 */
12475 SCIP_CALL( SCIPreleaseExpr(scip, &varexpr) );
12476 SCIP_CALL( SCIPreleaseExpr(scip, &powexpr) );
12477 }
12478
12479 SCIP_CALL( SCIPcreateExprPow(scip, &terms[0], lhssum, 0.5, NULL, NULL) ); /* sqrt(...) */
12480 SCIP_CALL( SCIPreleaseExpr(scip, &lhssum) );
12481 termcoefs[0] = 1.0;
12482
12483 SCIP_CALL( SCIPcreateExprVar(scip, &terms[1], rhsvar, NULL, NULL) ); /* x_{n+1} */
12484 termcoefs[1] = -rhscoeff;
12485
12486 SCIP_CALL( SCIPcreateExprSum(scip, &expr, 2, terms, termcoefs, 0.0, NULL, NULL) ); /* sqrt(...) - alpha_{n+1}x_{n_1} */
12487
12488 SCIP_CALL( SCIPreleaseExpr(scip, &terms[1]) );
12489 SCIP_CALL( SCIPreleaseExpr(scip, &terms[0]) );
12490
12491 SCIP_CALL( SCIPcreateConsBasicNonlinear(scip, cons, name, expr, -SCIPinfinity(scip), rhscoeff * rhsoffset) );
12492
12493 SCIP_CALL( SCIPreleaseExpr(scip, &expr) );
12494
12495 return SCIP_OKAY;
12496 }
12497
12498 /** creates and captures a signpower nonlinear constraint with all its constraint flags set to their default values
12499 *
12500 * \f$\textrm{lhs} \leq \textrm{sign}(x+a) |x+a|^n + c z \leq \textrm{rhs}\f$
12501 *
12502 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
12503 */
12504 SCIP_RETCODE SCIPcreateConsBasicSignpowerNonlinear(
12505 SCIP* scip, /**< SCIP data structure */
12506 SCIP_CONS** cons, /**< pointer to hold the created constraint */
12507 const char* name, /**< name of constraint */
12508 SCIP_VAR* x, /**< nonlinear variable x in constraint */
12509 SCIP_VAR* z, /**< linear variable z in constraint */
12510 SCIP_Real exponent, /**< exponent n of |x+offset|^n term in constraint */
12511 SCIP_Real xoffset, /**< offset in |x+offset|^n term in constraint */
12512 SCIP_Real zcoef, /**< coefficient of z in constraint */
12513 SCIP_Real lhs, /**< left hand side of constraint */
12514 SCIP_Real rhs /**< right hand side of constraint */
12515 )
12516 {
12517 SCIP_EXPR* xexpr;
12518 SCIP_EXPR* terms[2];
12519 SCIP_Real coefs[2];
12520 SCIP_EXPR* sumexpr;
12521
12522 assert(x != NULL);
12523 assert(z != NULL);
12524
12525 SCIP_CALL( SCIPcreateExprVar(scip, &xexpr, x, NULL, NULL) );
12526 if( xoffset != 0.0 )
12527 {
12528 SCIP_CALL( SCIPcreateExprSum(scip, &sumexpr, 1, &xexpr, NULL, xoffset, NULL, NULL) ); /* x + xoffset */
12529 SCIP_CALL( SCIPcreateExprSignpower(scip, &terms[0], sumexpr, exponent, NULL, NULL) ); /* signpow(x + xoffset, exponent) */
12530
12531 SCIP_CALL( SCIPreleaseExpr(scip, &sumexpr) );
12532 }
12533 else
12534 {
12535 SCIP_CALL( SCIPcreateExprSignpower(scip, &terms[0], xexpr, exponent, NULL, NULL) ); /* signpow(x, exponent) */
12536 }
12537 coefs[0] = 1.0;
12538
12539 SCIP_CALL( SCIPcreateExprVar(scip, &terms[1], z, NULL, NULL) );
12540 coefs[1] = zcoef;
12541
12542 SCIP_CALL( SCIPcreateExprSum(scip, &sumexpr, 2, terms, coefs, 0.0, NULL, NULL) ); /* signpowexpr + zcoef * z */
12543
12544 SCIP_CALL( SCIPcreateConsBasicNonlinear(scip, cons, name, sumexpr, lhs, rhs) );
12545
12546 SCIP_CALL( SCIPreleaseExpr(scip, &sumexpr) );
12547 SCIP_CALL( SCIPreleaseExpr(scip, &terms[1]) );
12548 SCIP_CALL( SCIPreleaseExpr(scip, &terms[0]) );
12549 SCIP_CALL( SCIPreleaseExpr(scip, &xexpr) );
12550
12551 return SCIP_OKAY;
12552 }
12553
12554 /** gets tag indicating current local variable bounds */
12555 SCIP_Longint SCIPgetCurBoundsTagNonlinear(
12556 SCIP_CONSHDLR* conshdlr /**< nonlinear constraint handler */
12557 )
12558 {
12559 SCIP_CONSHDLRDATA* conshdlrdata;
12560
12561 assert(conshdlr != NULL);
12562 conshdlrdata = SCIPconshdlrGetData(conshdlr);
12563
12564 return conshdlrdata->curboundstag;
12565 }
12566
12567 /** gets the `curboundstag` from the last time where variable bounds were relaxed */
12568 SCIP_Longint SCIPgetLastBoundRelaxTagNonlinear(
12569 SCIP_CONSHDLR* conshdlr /**< nonlinear constraint handler */
12570 )
12571 {
12572 SCIP_CONSHDLRDATA* conshdlrdata;
12573
12574 assert(conshdlr != NULL);
12575 conshdlrdata = SCIPconshdlrGetData(conshdlr);
12576
12577 return conshdlrdata->lastboundrelax;
12578 }
12579
12580 /** increments `curboundstag` and resets `lastboundrelax` in constraint handler data
12581 *
12582 * @attention This method is not intended for normal use.
12583 * These tags are maintained by the event handler for variable bound change events.
12584 * This method is used by some unittests.
12585 */
12586 void SCIPincrementCurBoundsTagNonlinear(
12587 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */
12588 SCIP_Bool boundrelax /**< indicates whether a bound was relaxed, i.e., lastboundrelax should be set too */
12589 )
12590 {
12591 SCIP_CONSHDLRDATA* conshdlrdata;
12592
12593 conshdlrdata = SCIPconshdlrGetData(conshdlr);
12594 assert(conshdlrdata != NULL);
12595
12596 ++conshdlrdata->curboundstag;
12597 assert(conshdlrdata->curboundstag > 0);
12598
12599 if( boundrelax )
12600 conshdlrdata->lastboundrelax = conshdlrdata->curboundstag;
12601 }
12602
12603 /** returns the hashmap that is internally used to map variables to their corresponding variable expressions */
12604 SCIP_HASHMAP* SCIPgetVarExprHashmapNonlinear(
12605 SCIP_CONSHDLR* conshdlr /**< nonlinear constraint handler */
12606 )
12607 {
12608 assert(conshdlr != NULL);
12609
12610 return SCIPconshdlrGetData(conshdlr)->var2expr;
12611 }
12612
12613 /** processes a rowprep for cut addition and maybe report branchscores */
12614 SCIP_RETCODE SCIPprocessRowprepNonlinear(
12615 SCIP* scip, /**< SCIP data structure */
12616 SCIP_NLHDLR* nlhdlr, /**< nonlinear handler which provided the estimator */
12617 SCIP_CONS* cons, /**< nonlinear constraint */
12618 SCIP_EXPR* expr, /**< expression */
12619 SCIP_ROWPREP* rowprep, /**< cut to be added */
12620 SCIP_Bool overestimate, /**< whether the expression needs to be over- or underestimated */
12621 SCIP_VAR* auxvar, /**< auxiliary variable */
12622 SCIP_Real auxvalue, /**< current value of expression w.r.t. auxiliary variables as obtained from EVALAUX */
12623 SCIP_Bool allowweakcuts, /**< whether we should only look for "strong" cuts, or anything that separates is fine */
12624 SCIP_Bool branchscoresuccess, /**< whether the estimator generation generated branching scores */
12625 SCIP_Bool inenforcement, /**< whether we are in enforcement, or only in separation */
12626 SCIP_SOL* sol, /**< solution to be separated (NULL for the LP solution) */
12627 SCIP_RESULT* result /**< pointer to store the result */
12628 )
12629 {
12630 SCIP_Real cutviol;
12631 SCIP_CONSHDLRDATA* conshdlrdata;
12632 SCIP_Real auxvarvalue = SCIP_INVALID;
12633 SCIP_Bool sepasuccess;
12634 SCIP_Real estimateval = SCIP_INVALID;
12635 SCIP_Real mincutviolation;
12636
12637 assert(nlhdlr != NULL);
12638 assert(cons != NULL);
12639 assert(expr != NULL);
12640 assert(rowprep != NULL);
12641 assert(auxvar != NULL);
12642 assert(result != NULL);
12643
12644 /* decide on minimal violation of cut */
12645 if( sol == NULL )
12646 mincutviolation = SCIPgetLPFeastol(scip); /* we enforce an LP solution */
12647 else
12648 mincutviolation = SCIPfeastol(scip);
12649
12650 conshdlrdata = SCIPconshdlrGetData(SCIPconsGetHdlr(cons));
12651 assert(conshdlrdata != NULL);
12652
12653 sepasuccess = TRUE;
12654
12655 cutviol = SCIPgetRowprepViolation(scip, rowprep, sol, NULL);
12656 if( cutviol > 0.0 )
12657 {
12658 auxvarvalue = SCIPgetSolVal(scip, sol, auxvar);
12659
12660 /* check whether cut is weak (if f(x) not defined, then it's never weak) */
12661 if( !allowweakcuts && auxvalue != SCIP_INVALID )
12662 {
12663 /* let the estimator be c'x-b, the auxvar is z (=auxvarvalue), and the expression is f(x) (=auxvalue)
12664 * then if we are underestimating and since the cut is violated, we should have z <= c'x-b <= f(x)
12665 * cutviol is c'x-b - z, so estimator value is c'x-b = z + cutviol
12666 * if the estimator value (c'x-b) is too close to z (auxvarvalue), when compared to f(x) (auxvalue),
12667 * then let's call this a weak cut that is, it's a weak cut if c'x-b <= z + weakcutthreshold * (f(x)-z)
12668 * <-> c'x-b - z <= weakcutthreshold * (f(x)-z)
12669 *
12670 * if we are overestimating, we have z >= c'x-b >= f(x)
12671 * cutviol is z - (c'x-b), so estimator value is c'x-b = z - cutviol
12672 * it's weak if c'x-b >= f(x) + (1-weakcutthreshold) * (z - f(x))
12673 * <-> c'x-b - z >= weakcutthreshold * (f(x)-z)
12674 *
12675 * when linearizing convex expressions, then we should have c'x-b = f(x), so they would never be weak
12676 */
12677 if( (!overestimate && ( cutviol <= conshdlrdata->weakcutthreshold * (auxvalue - auxvarvalue))) ||
12678 ( overestimate && (-cutviol >= conshdlrdata->weakcutthreshold * (auxvalue - auxvarvalue))) )
12679 {
12680 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " estimate of nlhdlr %s succeeded, but cut is too "\
12681 "weak: auxvarvalue %g estimateval %g auxvalue %g (over %d)\n",
12682 SCIPnlhdlrGetName(nlhdlr), auxvarvalue,
12683 auxvarvalue + (overestimate ? -cutviol : cutviol), auxvalue, overestimate); )
12684 sepasuccess = FALSE;
12685 }
12686 }
12687
12688 /* save estimator value for later, see long comment above why this gives the value for c'x-b */
12689 estimateval = auxvarvalue + (!overestimate ? cutviol : -cutviol);
12690 }
12691 else
12692 {
12693 sepasuccess = FALSE;
12694 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " estimate of nlhdlr %s succeeded, but cut does not "\
12695 "separate\n", SCIPnlhdlrGetName(nlhdlr)); )
12696 }
12697
12698 /* clean up estimator */
12699 if( sepasuccess )
12700 {
12701 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " estimate of nlhdlr %s succeeded: auxvarvalue %g "\
12702 "estimateval %g auxvalue %g (over %d)\n ", SCIPnlhdlrGetName(nlhdlr), auxvarvalue,
12703 auxvarvalue + (overestimate ? -cutviol : cutviol), auxvalue, overestimate);
12704 SCIPprintRowprep(scip, rowprep, enfologfile); )
12705
12706 /* if not allowweakcuts, then do not attempt to get cuts more violated by scaling them up,
12707 * instead, may even scale them down, that is, scale so that max coef is close to 1
12708 */
12709 if( !allowweakcuts )
12710 {
12711 SCIP_CALL( SCIPcleanupRowprep2(scip, rowprep, sol, conshdlrdata->strongcutmaxcoef, &sepasuccess) );
12712
12713 if( !sepasuccess )
12714 {
12715 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " cleanup cut failed due to bad numerics\n"); )
12716 }
12717 else
12718 {
12719 cutviol = SCIPgetRowprepViolation(scip, rowprep, sol, &sepasuccess);
12720 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " cleanup succeeded, violation = %g and %sreliable, "\
12721 "min requ viol = %g\n", cutviol, sepasuccess ? "" : "not ", mincutviolation); )
12722 if( sepasuccess )
12723 sepasuccess = cutviol > mincutviolation;
12724 }
12725
12726 if( sepasuccess && auxvalue != SCIP_INVALID )
12727 {
12728 /* check whether cut is weak now
12729 * auxvar z may now have a coefficient due to scaling (down) in cleanup - take this into account when
12730 * reconstructing estimateval from cutviol (TODO improve or remove?)
12731 */
12732 SCIP_Real auxvarcoef = 0.0;
12733 int i;
12734
12735 /* get absolute value of coef of auxvar in row - this makes the whole check here more expensive than
12736 * it should be...
12737 */
12738 for( i = 0; i < SCIProwprepGetNVars(rowprep); ++i )
12739 {
12740 if( SCIProwprepGetVars(rowprep)[i] == auxvar )
12741 {
12742 auxvarcoef = REALABS(SCIProwprepGetCoefs(rowprep)[i]);
12743 break;
12744 }
12745 }
12746
12747 if( auxvarcoef == 0.0 ||
12748 (!overestimate && ( cutviol / auxvarcoef <= conshdlrdata->weakcutthreshold * (auxvalue - auxvarvalue))) ||
12749 ( overestimate && (-cutviol / auxvarcoef >= conshdlrdata->weakcutthreshold * (auxvalue - auxvarvalue))) )
12750 {
12751 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " cut is too weak after cleanup: auxvarvalue %g estimateval %g auxvalue %g (over %d)\n",
12752 auxvarvalue, auxvarvalue + (overestimate ? -cutviol : cutviol) / auxvarcoef, auxvalue, overestimate); )
12753 sepasuccess = FALSE;
12754 }
12755 }
12756 }
12757 else
12758 {
12759 /* TODO if violations are really tiny, then maybe handle special (decrease LP feastol, for example) */
12760
12761 /* if estimate didn't report branchscores explicitly, then consider branching on those children for
12762 * which the following cleanup changes coefficients (we had/have this in expr_sum this way)
12763 */
12764 if( !branchscoresuccess )
12765 SCIProwprepRecordModifications(rowprep);
12766
12767 SCIP_CALL( SCIPcleanupRowprep(scip, rowprep, sol, mincutviolation, &cutviol, &sepasuccess) );
12768
12769 if( !sepasuccess )
12770 {
12771 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " cleanup failed, %d coefs modified, cutviol %g\n",
12772 SCIProwprepGetNModifiedVars(rowprep), cutviol); )
12773 }
12774
12775 /* if cleanup left us with a useless cut, then consider branching on variables for which coef were
12776 * changed
12777 */
12778 if( !sepasuccess && !branchscoresuccess && SCIProwprepGetNModifiedVars(rowprep) > 0 )
12779 {
12780 SCIP_Real violscore;
12781
12782 #ifdef BRSCORE_ABSVIOL
12783 violscore = getExprAbsAuxViolation(scip, expr, auxvalue, sol, NULL, NULL);
12784 #else
12785 SCIP_CALL( SCIPgetExprRelAuxViolationNonlinear(scip, expr, auxvalue, sol, &violscore, NULL, NULL) );
12786 #endif
12787 SCIP_CALL( addExprViolScoresAuxVars(scip, expr, violscore, SCIProwprepGetModifiedVars(rowprep), SCIProwprepGetNModifiedVars(rowprep), sol, &branchscoresuccess) );
12788
12789 /* addConsExprExprBranchScoresAuxVars can fail if the only vars for which the coef was changed
12790 * - were fixed,
12791 * - are this expr's auxvar (I don't think it makes sense to branch on that one (would it?)), or
12792 * - if a variable in the rowprep is not in expr (can happen with indicator added by perspective)
12793 * the first case came up again in #3085 and I don't see how to exclude this in the assert,
12794 * so I'm disabling the assert for now
12795 */
12796 /* assert(branchscoresuccess || (rowprep->nmodifiedvars == 1 && rowprep->modifiedvars[0] == auxvar) ||
12797 strcmp(SCIPnlhdlrGetName(nlhdlr), "perspective")==0); */
12798 }
12799 }
12800 }
12801
12802 /* if cut looks good (numerics ok and cutting off solution), then turn into row and add to sepastore */
12803 if( sepasuccess )
12804 {
12805 SCIP_ROW* row;
12806
12807 if( conshdlrdata->branchdualweight > 0.0 )
12808 {
12809 /* store remaining gap |f(x)-estimateval| in row name, which could be used in getDualBranchscore
12810 * skip if gap is zero
12811 */
12812 if( auxvalue == SCIP_INVALID )
12813 strcat(SCIProwprepGetName(rowprep), "_estimategap=inf");
12814 else if( !SCIPisEQ(scip, auxvalue, estimateval) )
12815 {
12816 char gap[40];
12817 (void) sprintf(gap, "_estimategap=%g", REALABS(auxvalue - estimateval));
12818 strcat(SCIProwprepGetName(rowprep), gap);
12819 }
12820 }
12821
12822 SCIP_CALL( SCIPgetRowprepRowCons(scip, &row, rowprep, cons) );
12823
12824 if( !allowweakcuts && conshdlrdata->strongcutefficacy && !SCIPisCutEfficacious(scip, sol, row) )
12825 {
12826 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " cut efficacy %g is too low (minefficacy=%g)\n",
12827 SCIPgetCutEfficacy(scip, sol, row), SCIPgetSepaMinEfficacy(scip)); )
12828 }
12829 else if( !SCIPisCutApplicable(scip, row) )
12830 {
12831 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " cut not applicable (e.g., cut is boundchange below eps)\n"); )
12832 }
12833 else
12834 {
12835 SCIP_Bool infeasible;
12836
12837 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " adding cut ");
12838 SCIP_CALL( SCIPprintRow(scip, row, enfologfile) ); )
12839
12840 /* I take !allowweakcuts as equivalent for having a strong cut (we usually have allowweakcuts=TRUE only
12841 * if we haven't found strong cuts before)
12842 */
12843 SCIP_CALL( SCIPaddRow(scip, row, conshdlrdata->forcestrongcut && !allowweakcuts && inenforcement, &infeasible) );
12844
12845 /* mark row as not removable from LP for current node (this can prevent some cycling) */
12846 if( conshdlrdata->rownotremovable == 'a' || (conshdlrdata->rownotremovable == 'e' && inenforcement) )
12847 SCIPmarkRowNotRemovableLocal(scip, row);
12848
12849 if( infeasible )
12850 {
12851 *result = SCIP_CUTOFF;
12852 SCIPnlhdlrIncrementNCutoffs(nlhdlr);
12853 }
12854 else
12855 {
12856 *result = SCIP_SEPARATED;
12857 SCIPnlhdlrIncrementNSeparated(nlhdlr);
12858 }
12859 }
12860
12861 SCIP_CALL( SCIPreleaseRow(scip, &row) );
12862 }
12863 else if( branchscoresuccess )
12864 {
12865 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " separation with estimate of nlhdlr %s failed, but "\
12866 "branching candidates added\n", SCIPnlhdlrGetName(nlhdlr)); )
12867
12868 /* well, not branched, but addConsExprExprViolScoresAuxVars() added scores to (aux)variables and that makes the
12869 * expressions eligible for branching candidate, see enforceConstraints() and branching()
12870 */
12871 *result = SCIP_BRANCHED;
12872 }
12873 else
12874 {
12875 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " separation with estimate of nlhdlr %s failed and no "\
12876 "branching candidates%s\n", SCIPnlhdlrGetName(nlhdlr), (allowweakcuts && inenforcement) ?
12877 " (!)" : ""); )
12878 }
12879
12880 return SCIP_OKAY;
12881 }
12882
12883 /** returns whether all nonlinear constraints are assumed to be convex */
12884 SCIP_Bool SCIPassumeConvexNonlinear(
12885 SCIP_CONSHDLR* conshdlr
12886 )
12887 {
12888 SCIP_CONSHDLRDATA* conshdlrdata;
12889
12890 assert(conshdlr != NULL);
12891
12892 conshdlrdata = SCIPconshdlrGetData(conshdlr);
12893 assert(conshdlrdata != NULL);
12894
12895 return conshdlrdata->assumeconvex;
12896 }
12897
12898 /** collects all bilinear terms for a given set of constraints
12899 *
12900 * @attention This method should only be used for unit tests that depend on SCIPgetBilinTermsNonlinear(),
12901 * SCIPgetBilinTermNonlinear() or SCIPgetBilinTermIdxNonlinear().
12902 */
12903 SCIP_RETCODE SCIPcollectBilinTermsNonlinear(
12904 SCIP* scip, /**< SCIP data structure */
12905 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */
12906 SCIP_CONS** conss, /**< nonlinear constraints */
12907 int nconss /**< total number of nonlinear constraints */
12908 )
12909 {
12910 assert(conshdlr != NULL);
12911 assert(conss != NULL || nconss == 0);
12912
12913 SCIP_CALL( bilinearTermsInsertAll(scip, conshdlr, conss, nconss) );
12914
12915 return SCIP_OKAY;
12916 }
12917
12918 /** returns the total number of bilinear terms that are contained in all nonlinear constraints
12919 *
12920 * @note This method should only be used after auxiliary variables have been created, i.e., after CONSINITLP.
12921 */
12922 int SCIPgetNBilinTermsNonlinear(
12923 SCIP_CONSHDLR* conshdlr /**< nonlinear constraint handler */
12924 )
12925 {
12926 SCIP_CONSHDLRDATA* conshdlrdata;
12927
12928 assert(conshdlr != NULL);
12929
12930 conshdlrdata = SCIPconshdlrGetData(conshdlr);
12931 assert(conshdlrdata != NULL);
12932
12933 return conshdlrdata->nbilinterms;
12934 }
12935
12936 /** returns all bilinear terms that are contained in all nonlinear constraints
12937 *
12938 * @note This method should only be used after auxiliary variables have been created, i.e., after CONSINITLP.
12939 * @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.
12940 */
12941 SCIP_CONSNONLINEAR_BILINTERM* SCIPgetBilinTermsNonlinear(
12942 SCIP_CONSHDLR* conshdlr /**< nonlinear constraint handler */
12943 )
12944 {
12945 SCIP_CONSHDLRDATA* conshdlrdata;
12946
12947 assert(conshdlr != NULL);
12948
12949 conshdlrdata = SCIPconshdlrGetData(conshdlr);
12950 assert(conshdlrdata != NULL);
12951
12952 return conshdlrdata->bilinterms;
12953 }
12954
12955 /** returns the index of the bilinear term representing the product of the two given variables
12956 *
12957 * @note The method should only be used after auxiliary variables have been created, i.e., after CONSINITLP.
12958 * @return The method returns -1 if the variables do not appear bilinearly.
12959 */
12960 int SCIPgetBilinTermIdxNonlinear(
12961 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */
12962 SCIP_VAR* x, /**< first variable */
12963 SCIP_VAR* y /**< second variable */
12964 )
12965 {
12966 SCIP_CONSHDLRDATA* conshdlrdata;
12967 SCIP_CONSNONLINEAR_BILINTERM entry;
12968 int idx;
12969
12970 assert(conshdlr != NULL);
12971 assert(x != NULL);
12972 assert(y != NULL);
12973
12974 conshdlrdata = SCIPconshdlrGetData(conshdlr);
12975 assert(conshdlrdata != NULL);
12976
12977 if( conshdlrdata->bilinhashtable == NULL )
12978 {
12979 return -1;
12980 }
12981
12982 /* ensure that x.index <= y.index */
12983 if( SCIPvarCompare(x, y) == 1 )
12984 {
12985 SCIPswapPointers((void**)&x, (void**)&y);
12986 }
12987 assert(SCIPvarCompare(x, y) < 1);
12988
12989 /* use a new entry to find the image in the bilinear hash table */
12990 entry.x = x;
12991 entry.y = y;
12992 idx = (int)(size_t)SCIPhashtableRetrieve(conshdlrdata->bilinhashtable, (void*)&entry) - 1;
12993 assert(idx >= -1 && idx < conshdlrdata->nbilinterms);
12994 assert(idx < 0 || conshdlrdata->bilinterms[idx].x == x);
12995 assert(idx < 0 || conshdlrdata->bilinterms[idx].y == y);
12996
12997 return idx;
12998 }
12999
13000 /** returns the bilinear term that represents the product of two given variables
13001 *
13002 * @note The method should only be used after auxiliary variables have been created, i.e., after CONSINITLP.
13003 * @return The method returns NULL if the variables do not appear bilinearly.
13004 */
13005 SCIP_CONSNONLINEAR_BILINTERM* SCIPgetBilinTermNonlinear(
13006 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */
13007 SCIP_VAR* x, /**< first variable */
13008 SCIP_VAR* y /**< second variable */
13009 )
13010 {
13011 SCIP_CONSHDLRDATA* conshdlrdata;
13012 int idx;
13013
13014 assert(conshdlr != NULL);
13015 assert(x != NULL);
13016 assert(y != NULL);
13017
13018 conshdlrdata = SCIPconshdlrGetData(conshdlr);
13019 assert(conshdlrdata != NULL);
13020
13021 idx = SCIPgetBilinTermIdxNonlinear(conshdlr, x, y);
13022 assert(idx >= -1 && idx < conshdlrdata->nbilinterms);
13023
13024 if( idx >= 0 )
13025 {
13026 return &conshdlrdata->bilinterms[idx];
13027 }
13028
13029 return NULL;
13030 }
13031
13032 /** evaluates an auxiliary expression for a bilinear term */
13033 SCIP_Real SCIPevalBilinAuxExprNonlinear(
13034 SCIP* scip, /**< SCIP data structure */
13035 SCIP_VAR* x, /**< first variable of the bilinear term */
13036 SCIP_VAR* y, /**< second variable of the bilinear term */
13037 SCIP_CONSNONLINEAR_AUXEXPR* auxexpr, /**< auxiliary expression */
13038 SCIP_SOL* sol /**< solution at which to evaluate (can be NULL) */
13039 )
13040 {
13041 assert(scip != NULL);
13042 assert(x != NULL);
13043 assert(y != NULL);
13044 assert(auxexpr != NULL);
13045 assert(auxexpr->auxvar != NULL);
13046
13047 return auxexpr->cst + auxexpr->coefs[0] * SCIPgetSolVal(scip, sol, auxexpr->auxvar) +
13048 auxexpr->coefs[1] * SCIPgetSolVal(scip, sol, x) + auxexpr->coefs[2] * SCIPgetSolVal(scip, sol, y);
13049 }
13050
13051 /** stores the variables of a bilinear term in the data of the constraint handler */
13052 SCIP_RETCODE SCIPinsertBilinearTermExistingNonlinear(
13053 SCIP* scip, /**< SCIP data structure */
13054 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
13055 SCIP_VAR* x, /**< first variable */
13056 SCIP_VAR* y, /**< second variable */
13057 SCIP_VAR* auxvar, /**< auxiliary variable (might be NULL) */
13058 int nlockspos, /**< number of positive expression locks */
13059 int nlocksneg /**< number of negative expression locks */
13060 )
13061 {
13062 SCIP_CONSHDLRDATA* conshdlrdata;
13063 SCIP_CONSNONLINEAR_BILINTERM* term;
13064 int idx;
13065
13066 assert(conshdlr != NULL);
13067
13068 conshdlrdata = SCIPconshdlrGetData(conshdlr);
13069 assert(conshdlrdata != NULL);
13070
13071 SCIP_CALL( bilinearTermsInsertEntry(scip, conshdlr, x, y, nlockspos, nlocksneg, &idx, TRUE) );
13072
13073 term = &conshdlrdata->bilinterms[idx];
13074 assert(term != NULL);
13075 assert(term->nauxexprs == 0); /* existing terms should be added before implicit terms */
13076 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) */
13077
13078 /* store and capture auxiliary variable */
13079 if( auxvar != NULL )
13080 {
13081 term->aux.var = auxvar;
13082 SCIP_CALL( SCIPcaptureVar(scip, auxvar) );
13083 }
13084
13085 return SCIP_OKAY;
13086 }
13087
13088 /** stores the variables of a bilinear term in the data of the constraint handler */
13089 SCIP_RETCODE SCIPinsertBilinearTermImplicitNonlinear(
13090 SCIP* scip, /**< SCIP data structure */
13091 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
13092 SCIP_VAR* x, /**< first variable */
13093 SCIP_VAR* y, /**< second variable */
13094 SCIP_VAR* auxvar, /**< auxiliary variable (might be NULL) */
13095 SCIP_Real coefx, /**< coefficient of x in the auxiliary expression */
13096 SCIP_Real coefy, /**< coefficient of y in the auxiliary expression */
13097 SCIP_Real coefaux, /**< coefficient of auxvar in the auxiliary expression */
13098 SCIP_Real cst, /**< constant of the auxiliary expression */
13099 SCIP_Bool overestimate /**< whether the auxiliary expression overestimates the bilinear product */
13100 )
13101 {
13102 SCIP_CONSHDLRDATA* conshdlrdata;
13103 SCIP_CONSNONLINEAR_BILINTERM* term;
13104 SCIP_CONSNONLINEAR_AUXEXPR* auxexpr;
13105 int idx;
13106 int nlockspos;
13107 int nlocksneg;
13108 SCIP_Bool added;
13109
13110 assert(conshdlr != NULL);
13111
13112 conshdlrdata = SCIPconshdlrGetData(conshdlr);
13113 assert(conshdlrdata != NULL);
13114
13115 nlockspos = overestimate ? 1 : 0;
13116 nlocksneg = overestimate ? 0 : 1;
13117
13118 SCIP_CALL( bilinearTermsInsertEntry(scip, conshdlr, x, y, nlockspos, nlocksneg, &idx, FALSE) );
13119
13120 term = &conshdlrdata->bilinterms[idx];
13121 assert(term != NULL);
13122 assert(SCIPvarCompare(term->x, term->y) < 1);
13123
13124 if( term->existing && term->nauxexprs == 0 && term->aux.var != NULL )
13125 {
13126 SCIP_CONSNONLINEAR_AUXEXPR* auxvarexpr;
13127 /* this is the case where we are adding an implicitly defined relation for a product that has already
13128 * been explicitly defined; convert auxvar into an auxexpr */
13129
13130 /* nothing to do if we aren't allowed to add more than one auxexpr per term */
13131 if( conshdlrdata->bilinmaxnauxexprs <= 1 )
13132 return SCIP_OKAY;
13133
13134 SCIP_CALL( SCIPallocBlockMemory(scip, &auxvarexpr) );
13135 auxvarexpr->cst = 0.0;
13136 auxvarexpr->coefs[0] = 1.0;
13137 auxvarexpr->coefs[1] = 0.0;
13138 auxvarexpr->coefs[2] = 0.0;
13139 auxvarexpr->auxvar = term->aux.var;
13140 auxvarexpr->underestimate = term->nlocksneg > 0;
13141 auxvarexpr->overestimate = term->nlockspos > 0;
13142
13143 /* before we were working with term->aux.var; now aux.var has been saved and aux.exprs can be initialised to NULL */
13144 term->aux.exprs = NULL;
13145
13146 SCIP_CALL( bilinTermAddAuxExpr(scip, conshdlrdata, term, auxvarexpr, &added) );
13147
13148 /* since there were no auxexprs before and we've already checked for bilinmaxnauxexprs, auxvarexpr should always be added */
13149 assert(added);
13150 }
13151
13152 /* create and add auxexpr */
13153 SCIP_CALL( SCIPallocBlockMemory(scip, &auxexpr) );
13154 auxexpr->underestimate = !overestimate;
13155 auxexpr->overestimate = overestimate;
13156 auxexpr->auxvar = auxvar;
13157 auxexpr->coefs[0] = coefaux;
13158 if( term->x == x )
13159 {
13160 assert(term->y == y);
13161 auxexpr->coefs[1] = coefx;
13162 auxexpr->coefs[2] = coefy;
13163 }
13164 else
13165 {
13166 assert(term->x == y);
13167 assert(term->y == x);
13168 auxexpr->coefs[1] = coefy;
13169 auxexpr->coefs[2] = coefx;
13170 }
13171 auxexpr->cst = cst;
13172 SCIP_CALL( bilinTermAddAuxExpr(scip, conshdlrdata, term, auxexpr, &added) );
13173
13174 if( !added )
13175 {
13176 SCIPfreeBlockMemory(scip, &auxexpr);
13177 }
13178 else if( auxvar != NULL )
13179 { /* capture auxiliary variable */
13180 SCIP_CALL( SCIPcaptureVar(scip, auxvar) );
13181 }
13182
13183 return SCIP_OKAY;
13184 }
13185
13186 /* replication of long comment on SCIPcomputeFacetVertexPolyhedralNonlinear() in cons_nonlinear.h omitted here */
13187 SCIP_RETCODE SCIPcomputeFacetVertexPolyhedralNonlinear(
13188 SCIP* scip, /**< SCIP data structure */
13189 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */
13190 SCIP_Bool overestimate, /**< whether to compute facet of concave (TRUE) or convex (FALSE) envelope */
13191 SCIP_DECL_VERTEXPOLYFUN((*function)), /**< pointer to vertex polyhedral function */
13192 void* fundata, /**< data for function evaluation (can be NULL) */
13193 SCIP_Real* xstar, /**< point to be separated */
13194 SCIP_Real* box, /**< box where to compute facet: should be lb_1, ub_1, lb_2, ub_2... */
13195 int nallvars, /**< half of the length of box */
13196 SCIP_Real targetvalue, /**< target value: no need to compute facet if value in xstar would be worse than this value */
13197 SCIP_Bool* success, /**< buffer to store whether a facet could be computed successfully */
13198 SCIP_Real* facetcoefs, /**< buffer to store coefficients of facet defining inequality; must be an array of length at least nallvars */
13199 SCIP_Real* facetconstant /**< buffer to store constant part of facet defining inequality */
13200 )
13201 {
13202 SCIP_Real* corner;
13203 SCIP_Real* funvals;
13204 int* nonfixedpos;
13205 SCIP_Real maxfaceterror;
13206 int nvars; /* number of nonfixed variables */
13207 unsigned int ncorners;
13208 unsigned int i;
13209 int j;
13210
13211 assert(scip != NULL);
13212 assert(conshdlr != NULL);
13213 assert(function != NULL);
13214 assert(xstar != NULL);
13215 assert(box != NULL);
13216 assert(success != NULL);
13217 assert(facetcoefs != NULL);
13218 assert(facetconstant != NULL);
13219
13220 *success = FALSE;
13221
13222 /* identify fixed variables */
13223 SCIP_CALL( SCIPallocBufferArray(scip, &nonfixedpos, nallvars) );
13224 nvars = 0;
13225 for( j = 0; j < nallvars; ++j )
13226 {
13227 if( SCIPisRelEQ(scip, box[2 * j], box[2 * j + 1]) )
13228 continue;
13229 nonfixedpos[nvars] = j;
13230 nvars++;
13231 }
13232
13233 /* if all variables are fixed, then we could provide something trivial, but that wouldn't be the job of separation
13234 * if too many variables are not fixed, then we do nothing currently
13235 */
13236 if( nvars == 0 || nvars > SCIP_MAXVERTEXPOLYDIM )
13237 {
13238 SCIPwarningMessage(scip, "SCIPcomputeFacetVertexPolyhedralNonlinear() called with %d nonfixed variables. Must be between [1,%d].\n", nvars, SCIP_MAXVERTEXPOLYDIM);
13239 SCIPfreeBufferArray(scip, &nonfixedpos);
13240 return SCIP_OKAY;
13241 }
13242
13243 /* compute f(v^i) for each corner v^i of [l,u] */
13244 ncorners = POWEROFTWO(nvars);
13245 SCIP_CALL( SCIPallocBufferArray(scip, &funvals, ncorners) );
13246 SCIP_CALL( SCIPallocBufferArray(scip, &corner, nallvars) );
13247 for( j = 0; j < nallvars; ++j )
13248 {
13249 if( SCIPisRelEQ(scip, box[2 * j], box[2 * j + 1]) )
13250 corner[j] = (box[2 * j] + box[2 * j + 1]) / 2.0;
13251 }
13252 for( i = 0; i < ncorners; ++i )
13253 {
13254 SCIPdebugMsg(scip, "corner %u: ", i);
13255 for( j = 0; j < nvars; ++j )
13256 {
13257 int varpos = nonfixedpos[j];
13258 /* if j'th bit of row index i is set, then take upper bound on var j, otherwise lower bound var j
13259 * we check this by shifting i for j positions to the right and checking whether the last bit is set
13260 */
13261 if( (i >> j) & 0x1 )
13262 corner[varpos] = box[2 * varpos + 1]; /* ub of var */
13263 else
13264 corner[varpos] = box[2 * varpos ]; /* lb of var */
13265 SCIPdebugMsgPrint(scip, "%g, ", corner[varpos]);
13266 assert(!SCIPisInfinity(scip, REALABS(corner[varpos])));
13267 }
13268
13269 funvals[i] = function(corner, nallvars, fundata);
13270
13271 SCIPdebugMsgPrint(scip, "obj = %e\n", funvals[i]);
13272
13273 if( funvals[i] == SCIP_INVALID || SCIPisInfinity(scip, REALABS(funvals[i])) )
13274 {
13275 SCIPdebugMsg(scip, "cannot compute underestimator; function value at corner is too large %g\n", funvals[i]);
13276 goto CLEANUP;
13277 }
13278 }
13279
13280 /* clear coefs array; below we only fill in coefs for nonfixed variables */
13281 BMSclearMemoryArray(facetcoefs, nallvars);
13282
13283 if( nvars == 1 )
13284 {
13285 SCIP_CALL( computeVertexPolyhedralFacetUnivariate(scip, box[2 * nonfixedpos[0]], box[2 * nonfixedpos[0] + 1], funvals[0], funvals[1], success, &facetcoefs[nonfixedpos[0]], facetconstant) );
13286
13287 /* check whether target has been missed */
13288 if( *success && overestimate == (*facetconstant + facetcoefs[nonfixedpos[0]] * xstar[nonfixedpos[0]] > targetvalue) )
13289 {
13290 SCIPdebugMsg(scip, "computed secant, but missed target %g (facetvalue=%g, overestimate=%u)\n", targetvalue, *facetconstant + facetcoefs[nonfixedpos[0]] * xstar[nonfixedpos[0]], overestimate);
13291 *success = FALSE;
13292 }
13293 }
13294 else if( nvars == 2 && SCIPlapackIsAvailable() )
13295 {
13296 int idx1 = nonfixedpos[0];
13297 int idx2 = nonfixedpos[1];
13298 SCIP_Real p1[2] = { box[2*idx1], box[2*idx2] }; /* corner 0: 0>>0 & 0x1 = 0, 0>>1 & 0x1 = 0 */
13299 SCIP_Real p2[2] = { box[2*idx1+1], box[2*idx2] }; /* corner 1: 1>>0 & 0x1 = 1, 1>>1 & 0x1 = 0 */
13300 SCIP_Real p3[2] = { box[2*idx1], box[2*idx2+1] }; /* corner 2: 2>>0 & 0x1 = 0, 2>>1 & 0x1 = 1 */
13301 SCIP_Real p4[2] = { box[2*idx1+1], box[2*idx2+1] }; /* corner 3: 3>>0 & 0x1 = 1, 3>>1 & 0x1 = 1 */
13302 SCIP_Real xstar2[2] = { xstar[idx1], xstar[idx2] };
13303 SCIP_Real coefs[2] = { 0.0, 0.0 };
13304
13305 SCIP_CALL( computeVertexPolyhedralFacetBivariate(scip, overestimate, p1, p2, p3, p4, funvals[0], funvals[1], funvals[2], funvals[3], xstar2, targetvalue, success, coefs, facetconstant) );
13306
13307 facetcoefs[idx1] = coefs[0];
13308 facetcoefs[idx2] = coefs[1];
13309 }
13310 else
13311 {
13312 SCIP_CALL( computeVertexPolyhedralFacetLP(scip, conshdlr, overestimate, xstar, box, nallvars, nonfixedpos, funvals, nvars, targetvalue, success, facetcoefs, facetconstant) );
13313 }
13314 if( !*success )
13315 {
13316 SCIPdebugMsg(scip, "no success computing facet, %d vars\n", nvars);
13317 goto CLEANUP;
13318 }
13319
13320 /*
13321 * check and adjust facet with the algorithm of Rikun et al.
13322 */
13323
13324 maxfaceterror = computeVertexPolyhedralMaxFacetError(scip, overestimate, funvals, box, nallvars, nvars, nonfixedpos, facetcoefs, *facetconstant);
13325
13326 /* adjust constant part of the facet by maxerror to make it a valid over/underestimator (not facet though) */
13327 if( maxfaceterror > 0.0 )
13328 {
13329 SCIP_CONSHDLRDATA* conshdlrdata;
13330 SCIP_Real midval;
13331 SCIP_Real feastol;
13332
13333 feastol = SCIPgetStage(scip) == SCIP_STAGE_SOLVING ? SCIPgetLPFeastol(scip) : SCIPfeastol(scip);
13334
13335 /* evaluate function in middle point to get some idea for a scaling */
13336 for( j = 0; j < nvars; ++j )
13337 corner[nonfixedpos[j]] = (box[2 * nonfixedpos[j]] + box[2 * nonfixedpos[j] + 1]) / 2.0;
13338 midval = function(corner, nallvars, fundata);
13339 if( midval == SCIP_INVALID )
13340 midval = 1.0;
13341
13342 conshdlrdata = SCIPconshdlrGetData(conshdlr);
13343 assert(conshdlrdata != NULL);
13344
13345 /* there seem to be numerical problems if the error is too large; in this case we reject the facet */
13346 if( maxfaceterror > conshdlrdata->vp_adjfacetthreshold * feastol * fabs(midval) )
13347 {
13348 SCIPdebugMsg(scip, "ignoring facet due to instability, it cuts off a vertex by %g (midval=%g).\n", maxfaceterror, midval);
13349 *success = FALSE;
13350 goto CLEANUP;
13351 }
13352
13353 SCIPdebugMsg(scip, "maximum facet error %g (midval=%g), adjust constant to make cut valid!\n", maxfaceterror, midval);
13354
13355 if( overestimate )
13356 *facetconstant += maxfaceterror;
13357 else
13358 *facetconstant -= maxfaceterror;
13359 }
13360
13361 /* if we made it until here, then we have a nice facet */
13362 assert(*success);
13363
13364 CLEANUP:
13365 /* free allocated memory */
13366 SCIPfreeBufferArray(scip, &corner);
13367 SCIPfreeBufferArray(scip, &funvals);
13368 SCIPfreeBufferArray(scip, &nonfixedpos);
13369
13370 return SCIP_OKAY;
13371 }
13372
13373 /*
13374 * constraint specific interface methods
13375 */
13376
13377 /** returns the expression of the given nonlinear constraint */
13378 SCIP_EXPR* SCIPgetExprNonlinear(
13379 SCIP_CONS* cons /**< constraint data */
13380 )
13381 {
13382 SCIP_CONSDATA* consdata;
13383
13384 assert(cons != NULL);
13385 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0);
13386
13387 consdata = SCIPconsGetData(cons);
13388 assert(consdata != NULL);
13389
13390 return consdata->expr;
13391 }
13392
13393 /** gets the left hand side of a nonlinear constraint */
13394 SCIP_Real SCIPgetLhsNonlinear(
13395 SCIP_CONS* cons /**< constraint data */
13396 )
13397 {
13398 SCIP_CONSDATA* consdata;
13399
13400 assert(cons != NULL);
13401 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0);
13402
13403 consdata = SCIPconsGetData(cons);
13404 assert(consdata != NULL);
13405
13406 return consdata->lhs;
13407 }
13408
13409 /** gets the right hand side of a nonlinear constraint */
13410 SCIP_Real SCIPgetRhsNonlinear(
13411 SCIP_CONS* cons /**< constraint data */
13412 )
13413 {
13414 SCIP_CONSDATA* consdata;
13415
13416 assert(cons != NULL);
13417 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0);
13418
13419 consdata = SCIPconsGetData(cons);
13420 assert(consdata != NULL);
13421
13422 return consdata->rhs;
13423 }
13424
13425 /** gets the nonlinear constraint as a nonlinear row representation. */
13426 SCIP_RETCODE SCIPgetNlRowNonlinear(
13427 SCIP* scip, /**< SCIP data structure */
13428 SCIP_CONS* cons, /**< constraint */
13429 SCIP_NLROW** nlrow /**< pointer to store nonlinear row */
13430 )
13431 {
13432 SCIP_CONSDATA* consdata;
13433
13434 assert(cons != NULL);
13435 assert(nlrow != NULL);
13436 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0);
13437
13438 consdata = SCIPconsGetData(cons);
13439 assert(consdata != NULL);
13440
13441 if( consdata->nlrow == NULL )
13442 {
13443 SCIP_CALL( createNlRow(scip, cons) );
13444 }
13445 assert(consdata->nlrow != NULL);
13446 *nlrow = consdata->nlrow;
13447
13448 return SCIP_OKAY;
13449 }
13450
13451 /** returns the curvature of the expression of a given nonlinear constraint
13452 *
13453 * @note The curvature information is computed during CONSINITSOL.
13454 */
13455 SCIP_EXPRCURV SCIPgetCurvatureNonlinear(
13456 SCIP_CONS* cons /**< constraint data */
13457 )
13458 {
13459 SCIP_CONSDATA* consdata;
13460
13461 assert(cons != NULL);
13462 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0);
13463
13464 consdata = SCIPconsGetData(cons);
13465 assert(consdata != NULL);
13466
13467 return consdata->curv;
13468 }
13469
13470 /** checks whether expression of constraint can be represented as quadratic form
13471 *
13472 * Only sets `*isquadratic` to TRUE if the whole expression is quadratic (in the non-extended formulation) and non-linear.
13473 * That is, the expression in each \ref SCIP_QUADEXPR_QUADTERM will be a variable expressions and
13474 * \ref SCIPgetVarExprVar() can be used to retrieve the variable.
13475 */
13476 SCIP_RETCODE SCIPcheckQuadraticNonlinear(
13477 SCIP* scip, /**< SCIP data structure */
13478 SCIP_CONS* cons, /**< constraint data */
13479 SCIP_Bool* isquadratic /**< buffer to store whether constraint is quadratic */
13480 )
13481 {
13482 SCIP_CONSDATA* consdata;
13483
13484 assert(scip != NULL);
13485 assert(cons != NULL);
13486 assert(isquadratic != NULL);
13487 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0);
13488
13489 consdata = SCIPconsGetData(cons);
13490 assert(consdata != NULL);
13491 assert(consdata->expr != NULL);
13492
13493 /* check whether constraint expression is quadratic in extended formulation */
13494 SCIP_CALL( SCIPcheckExprQuadratic(scip, consdata->expr, isquadratic) );
13495
13496 /* if not quadratic in non-extended formulation, then do indicate quadratic */
13497 if( *isquadratic )
13498 *isquadratic = SCIPexprAreQuadraticExprsVariables(consdata->expr);
13499
13500 return SCIP_OKAY;
13501 }
13502
13503 /** changes left-hand-side of a nonlinear constraint
13504 *
13505 * @attention This method can only be called in the problem stage.
13506 */
13507 SCIP_RETCODE SCIPchgLhsNonlinear(
13508 SCIP* scip, /**< SCIP data structure */
13509 SCIP_CONS* cons, /**< constraint data */
13510 SCIP_Real lhs /**< new left-hand-side */
13511 )
13512 {
13513 SCIP_CONSDATA* consdata;
13514
13515 assert(scip != NULL);
13516 assert(cons != NULL);
13517 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0);
13518
13519 if( SCIPgetStage(scip) != SCIP_STAGE_PROBLEM )
13520 {
13521 SCIPerrorMessage("SCIPchgLhsNonlinear can only be called in problem stage.\n");
13522 return SCIP_INVALIDCALL;
13523 }
13524
13525 /* we should have an original constraint */
13526 assert(SCIPconsIsOriginal(cons));
13527
13528 consdata = SCIPconsGetData(cons);
13529 assert(consdata != NULL);
13530
13531 if( consdata->lhs == lhs )
13532 return SCIP_OKAY;
13533
13534 consdata->lhs = lhs;
13535
13536 /* not sure we care about any of these flags for original constraints */
13537 consdata->ispropagated = FALSE;
13538
13539 return SCIP_OKAY;
13540 }
13541
13542 /** changes right-hand-side of a nonlinear constraint
13543 *
13544 * @attention This method can only be called in the problem stage.
13545 */
13546 SCIP_RETCODE SCIPchgRhsNonlinear(
13547 SCIP* scip, /**< SCIP data structure */
13548 SCIP_CONS* cons, /**< constraint data */
13549 SCIP_Real rhs /**< new right-hand-side */
13550 )
13551 {
13552 SCIP_CONSDATA* consdata;
13553
13554 assert(scip != NULL);
13555 assert(cons != NULL);
13556 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0);
13557
13558 if( SCIPgetStage(scip) != SCIP_STAGE_PROBLEM )
13559 {
13560 SCIPerrorMessage("SCIPchgLhsNonlinear can only be called in problem stage.\n");
13561 return SCIP_INVALIDCALL;
13562 }
13563
13564 /* we should have an original constraint */
13565 assert(SCIPconsIsOriginal(cons));
13566
13567 consdata = SCIPconsGetData(cons);
13568 assert(consdata != NULL);
13569
13570 if( consdata->rhs == rhs )
13571 return SCIP_OKAY;
13572
13573 consdata->rhs = rhs;
13574
13575 /* not sure we care about any of these flags for original constraints */
13576 consdata->ispropagated = FALSE;
13577
13578 return SCIP_OKAY;
13579 }
13580
13581 /** changes expression of a nonlinear constraint
13582 *
13583 * @attention This method can only be called in the problem stage.
13584 */
13585 SCIP_RETCODE SCIPchgExprNonlinear(
13586 SCIP* scip, /**< SCIP data structure */
13587 SCIP_CONS* cons, /**< constraint data */
13588 SCIP_EXPR* expr /**< new expression */
13589 )
13590 {
13591 SCIP_CONSHDLR* conshdlr;
13592 SCIP_CONSDATA* consdata;
13593
13594 assert(scip != NULL);
13595 assert(cons != NULL);
13596 assert(expr != NULL);
13597
13598 if( SCIPgetStage(scip) != SCIP_STAGE_PROBLEM )
13599 {
13600 SCIPerrorMessage("SCIPchgExprNonlinear can only be called in problem stage.\n");
13601 return SCIP_INVALIDCALL;
13602 }
13603
13604 /* we should have an original constraint */
13605 assert(SCIPconsIsOriginal(cons));
13606
13607 conshdlr = SCIPconsGetHdlr(cons);
13608 assert(conshdlr != NULL);
13609 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
13610
13611 consdata = SCIPconsGetData(cons);
13612 assert(consdata != NULL);
13613 assert(consdata->expr != NULL);
13614
13615 /* we should not have collected additional data for the expr
13616 * if some of these asserts fail, we may have to remove it and add some code to keep information up to date
13617 */
13618 assert(consdata->nvarexprs == 0);
13619 assert(consdata->varexprs == NULL);
13620 assert(!consdata->catchedevents);
13621
13622 SCIP_CALL( SCIPreleaseExpr(scip, &consdata->expr) );
13623
13624 /* copy expression, thereby map variables expressions to already existing variables expressions in var2expr map, or augment var2expr map */
13625 SCIP_CALL( SCIPduplicateExpr(scip, expr, &consdata->expr, mapexprvar, conshdlr, exprownerCreate, (void*)conshdlr) );
13626
13627 /* not sure we care about any of these flags for original constraints */
13628 consdata->curv = SCIP_EXPRCURV_UNKNOWN;
13629 consdata->issimplified = FALSE;
13630 consdata->ispropagated = FALSE;
13631
13632 return SCIP_OKAY;
13633 }
13634
13635 /** adds coef * var to nonlinear constraint
13636 *
13637 * @attention This method can only be called in the problem stage.
13638 */
13639 SCIP_RETCODE SCIPaddLinearVarNonlinear(
13640 SCIP* scip, /**< SCIP data structure */
13641 SCIP_CONS* cons, /**< constraint data */
13642 SCIP_VAR* var, /**< variable */
13643 SCIP_Real coef /**< coefficient */
13644 )
13645 {
13646 SCIP_CONSHDLR* conshdlr;
13647 SCIP_CONSDATA* consdata;
13648 SCIP_EXPR* varexpr;
13649
13650 assert(scip != NULL);
13651 assert(cons != NULL);
13652
13653 if( SCIPgetStage(scip) != SCIP_STAGE_PROBLEM )
13654 {
13655 SCIPerrorMessage("SCIPaddLinearVarNonlinear can only be called in problem stage.\n");
13656 return SCIP_INVALIDCALL;
13657 }
13658
13659 /* we should have an original constraint */
13660 assert(SCIPconsIsOriginal(cons));
13661
13662 if( coef == 0.0 )
13663 return SCIP_OKAY;
13664
13665 conshdlr = SCIPconsGetHdlr(cons);
13666 assert(conshdlr != NULL);
13667 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
13668
13669 consdata = SCIPconsGetData(cons);
13670 assert(consdata != NULL);
13671 assert(consdata->expr != NULL);
13672
13673 /* we should not have collected additional data for it
13674 * if some of these asserts fail, we may have to remove it and add some code to keep information up to date
13675 */
13676 assert(consdata->nvarexprs == 0);
13677 assert(consdata->varexprs == NULL);
13678 assert(!consdata->catchedevents);
13679
13680 SCIP_CALL( createExprVar(scip, conshdlr, &varexpr, var) );
13681
13682 /* append to sum, if consdata->expr is sum and not used anywhere else */
13683 if( SCIPexprGetNUses(consdata->expr) == 1 && SCIPisExprSum(scip, consdata->expr) )
13684 {
13685 SCIP_CALL( SCIPappendExprSumExpr(scip, consdata->expr, varexpr, coef) );
13686 }
13687 else
13688 {
13689 /* create new expression = 1 * consdata->expr + coef * var */
13690 SCIP_EXPR* children[2] = { consdata->expr, varexpr };
13691 SCIP_Real coefs[2] = { 1.0, coef };
13692
13693 SCIP_CALL( SCIPcreateExprSum(scip, &consdata->expr, 2, children, coefs, 0.0, exprownerCreate, (void*)conshdlr) );
13694
13695 /* release old root expr */
13696 SCIP_CALL( SCIPreleaseExpr(scip, &children[0]) );
13697 }
13698
13699 SCIP_CALL( SCIPreleaseExpr(scip, &varexpr) );
13700
13701 /* not sure we care about any of these flags for original constraints */
13702 consdata->issimplified = FALSE;
13703 consdata->ispropagated = FALSE;
13704
13705 return SCIP_OKAY;
13706 }
13707
13708 /** adds coef * expr to nonlinear constraint
13709 *
13710 * @attention This method can only be called in the problem stage.
13711 */
13712 SCIP_RETCODE SCIPaddExprNonlinear(
13713 SCIP* scip, /**< SCIP data structure */
13714 SCIP_CONS* cons, /**< nonlinear constraint */
13715 SCIP_EXPR* expr, /**< expression */
13716 SCIP_Real coef /**< coefficient */
13717 )
13718 {
13719 SCIP_CONSHDLR* conshdlr;
13720 SCIP_CONSDATA* consdata;
13721 SCIP_EXPR* exprowned;
13722
13723 assert(scip != NULL);
13724 assert(cons != NULL);
13725
13726 if( SCIPgetStage(scip) != SCIP_STAGE_PROBLEM )
13727 {
13728 SCIPerrorMessage("SCIPaddLinearVarNonlinear can only be called in problem stage.\n");
13729 return SCIP_INVALIDCALL;
13730 }
13731
13732 /* we should have an original constraint */
13733 assert(SCIPconsIsOriginal(cons));
13734
13735 if( coef == 0.0 )
13736 return SCIP_OKAY;
13737
13738 conshdlr = SCIPconsGetHdlr(cons);
13739 assert(conshdlr != NULL);
13740 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
13741
13742 consdata = SCIPconsGetData(cons);
13743 assert(consdata != NULL);
13744 assert(consdata->expr != NULL);
13745
13746 /* we should not have collected additional data for it
13747 * if some of these asserts fail, we may have to remove it and add some code to keep information up to date
13748 */
13749 assert(consdata->nvarexprs == 0);
13750 assert(consdata->varexprs == NULL);
13751 assert(!consdata->catchedevents);
13752
13753 /* copy expression, thereby map variables expressions to already existing variables expressions in var2expr map, or augment var2expr map */
13754 SCIP_CALL( SCIPduplicateExpr(scip, expr, &exprowned, mapexprvar, conshdlr, exprownerCreate, (void*)conshdlr) );
13755
13756 /* append to sum, if consdata->expr is sum and not used anywhere else */
13757 if( SCIPexprGetNUses(consdata->expr) == 1 && SCIPisExprSum(scip, consdata->expr) )
13758 {
13759 SCIP_CALL( SCIPappendExprSumExpr(scip, consdata->expr, exprowned, coef) );
13760 }
13761 else
13762 {
13763 /* create new expression = 1 * consdata->expr + coef * var */
13764 SCIP_EXPR* children[2] = { consdata->expr, exprowned };
13765 SCIP_Real coefs[2] = { 1.0, coef };
13766
13767 SCIP_CALL( SCIPcreateExprSum(scip, &consdata->expr, 2, children, coefs, 0.0, exprownerCreate, (void*)conshdlr) );
13768
13769 /* release old root expr */
13770 SCIP_CALL( SCIPreleaseExpr(scip, &children[0]) );
13771 }
13772
13773 SCIP_CALL( SCIPreleaseExpr(scip, &exprowned) );
13774
13775 /* not sure we care about any of these flags for original constraints */
13776 consdata->issimplified = FALSE;
13777 consdata->ispropagated = FALSE;
13778
13779 return SCIP_OKAY;
13780 }
13781
13782 /** gets absolute violation of nonlinear constraint
13783 *
13784 * This function evaluates the constraints in the given solution.
13785 *
13786 * If this value is at most SCIPfeastol(), the constraint would be considered feasible.
13787 */
13788 SCIP_RETCODE SCIPgetAbsViolationNonlinear(
13789 SCIP* scip, /**< SCIP data structure */
13790 SCIP_CONS* cons, /**< constraint */
13791 SCIP_SOL* sol, /**< solution to check */
13792 SCIP_Real* viol /**< buffer to store computed violation */
13793 )
13794 {
13795 assert(cons != NULL);
13796 assert(viol != NULL);
13797
13798 SCIP_CALL( computeViolation(scip, cons, sol, 0L) );
13799 *viol = getConsAbsViolation(cons);
13800
13801 return SCIP_OKAY;
13802 }
13803
13804 /** gets scaled violation of nonlinear constraint
13805 *
13806 * This function evaluates the constraints in the given solution.
13807 *
13808 * The scaling that is applied to the absolute violation of the constraint
13809 * depends on the setting of parameter constraints/nonlinear/violscale.
13810 */
13811 SCIP_RETCODE SCIPgetRelViolationNonlinear(
13812 SCIP* scip, /**< SCIP data structure */
13813 SCIP_CONS* cons, /**< constraint */
13814 SCIP_SOL* sol, /**< solution to check */
13815 SCIP_Real* viol /**< buffer to store computed violation */
13816 )
13817 {
13818 assert(cons != NULL);
13819 assert(viol != NULL);
13820
13821 SCIP_CALL( computeViolation(scip, cons, sol, 0L) );
13822 SCIP_CALL( getConsRelViolation(scip, cons, viol, sol, 0L) );
13823
13824 return SCIP_OKAY;
13825 }
13826
13827 /** returns a variable that appears linearly that may be decreased without making any other constraint infeasible */
13828 void SCIPgetLinvarMayDecreaseNonlinear(
13829 SCIP* scip, /**< SCIP data structure */
13830 SCIP_CONS* cons, /**< nonlinear constraint */
13831 SCIP_VAR** var, /**< pointer to store the variable */
13832 SCIP_Real* coef /**< pointer to store the coefficient */
13833 )
13834 {
13835 SCIP_CONSDATA* consdata;
13836
13837 assert(cons != NULL);
13838 assert(var != NULL);
13839 assert(coef != NULL);
13840
13841 /* check for a linear variable that can be increased or decreased without harming feasibility */
13842 findUnlockedLinearVar(scip, cons);
13843
13844 consdata = SCIPconsGetData(cons);
13845 assert(consdata != NULL);
13846
13847 *var = consdata->linvardecr;
13848 *coef = consdata->linvardecrcoef;
13849 }
13850
13851 /** returns a variable that appears linearly that may be increased without making any other constraint infeasible */
13852 void SCIPgetLinvarMayIncreaseNonlinear(
13853 SCIP* scip, /**< SCIP data structure */
13854 SCIP_CONS* cons, /**< nonlinear constraint */
13855 SCIP_VAR** var, /**< pointer to store the variable */
13856 SCIP_Real* coef /**< pointer to store the coefficient */
13857 )
13858 {
13859 SCIP_CONSDATA* consdata;
13860
13861 assert(cons != NULL);
13862 assert(var != NULL);
13863 assert(coef != NULL);
13864
13865 /* check for a linear variable that can be increased or decreased without harming feasibility */
13866 findUnlockedLinearVar(scip, cons);
13867
13868 consdata = SCIPconsGetData(cons);
13869 assert(consdata != NULL);
13870
13871 *var = consdata->linvarincr;
13872 *coef = consdata->linvarincrcoef;
13873 }
13874
13875
13876 /*
13877 * Methods for Expressions in Nonlinear Constraints
13878 */
13879
13880 /** returns the number of positive rounding locks of an expression */
13881 int SCIPgetExprNLocksPosNonlinear(
13882 SCIP_EXPR* expr /**< expression */
13883 )
13884 {
13885 assert(expr != NULL);
13886 assert(SCIPexprGetOwnerData(expr) != NULL);
13887
13888 return SCIPexprGetOwnerData(expr)->nlockspos;
13889 }
13890
13891 /** returns the number of negative rounding locks of an expression */
13892 int SCIPgetExprNLocksNegNonlinear(
13893 SCIP_EXPR* expr /**< expression */
13894 )
13895 {
13896 assert(expr != NULL);
13897 assert(SCIPexprGetOwnerData(expr) != NULL);
13898
13899 return SCIPexprGetOwnerData(expr)->nlocksneg;
13900 }
13901
13902 /** returns the variable used for linearizing a given expression (return value might be NULL)
13903 *
13904 * @note for variable expression it returns the corresponding variable
13905 */
13906 SCIP_VAR* SCIPgetExprAuxVarNonlinear(
13907 SCIP_EXPR* expr /**< expression */
13908 )
13909 {
13910 SCIP_EXPR_OWNERDATA* ownerdata;
13911
13912 assert(expr != NULL);
13913
13914 ownerdata = SCIPexprGetOwnerData(expr);
13915 assert(ownerdata != NULL);
13916
13917 return ownerdata->filterpos >= -1 ? SCIPgetVarExprVar(expr) : ownerdata->auxvar;
13918 }
13919
13920 /** returns the number of enforcements for an expression */
13921 int SCIPgetExprNEnfosNonlinear(
13922 SCIP_EXPR* expr /**< expression */
13923 )
13924 {
13925 assert(expr != NULL);
13926 assert(SCIPexprGetOwnerData(expr) != NULL);
13927
13928 return SCIPexprGetOwnerData(expr)->nenfos;
13929 }
13930
13931 /** returns the data for one of the enforcements of an expression */
13932 void SCIPgetExprEnfoDataNonlinear(
13933 SCIP_EXPR* expr, /**< expression */
13934 int idx, /**< position of enforcement in enfos array */
13935 SCIP_NLHDLR** nlhdlr, /**< buffer to store nlhldr */
13936 SCIP_NLHDLREXPRDATA** nlhdlrexprdata, /**< buffer to store nlhdlr data for expression, or NULL */
13937 SCIP_NLHDLR_METHOD* nlhdlrparticipation, /**< buffer to store methods where nonlinear handler participates, or NULL */
13938 SCIP_Bool* sepabelowusesactivity, /**< buffer to store whether sepabelow uses activity of some expression, or NULL */
13939 SCIP_Bool* sepaaboveusesactivity, /**< buffer to store whether sepaabove uses activity of some expression, or NULL */
13940 SCIP_Real* auxvalue /**< buffer to store current auxvalue, or NULL */
13941 )
13942 {
13943 SCIP_EXPR_OWNERDATA* ownerdata;
13944
13945 assert(expr != NULL);
13946
13947 ownerdata = SCIPexprGetOwnerData(expr);
13948 assert(ownerdata != NULL);
13949 assert(idx >= 0);
13950 assert(idx < ownerdata->nenfos);
13951 assert(ownerdata->enfos[idx] != NULL);
13952 assert(nlhdlr != NULL);
13953
13954 *nlhdlr = ownerdata->enfos[idx]->nlhdlr;
13955
13956 if( nlhdlrexprdata != NULL )
13957 *nlhdlrexprdata = ownerdata->enfos[idx]->nlhdlrexprdata;
13958
13959 if( nlhdlrparticipation != NULL )
13960 *nlhdlrparticipation = ownerdata->enfos[idx]->nlhdlrparticipation;
13961
13962 if( sepabelowusesactivity != NULL )
13963 *sepabelowusesactivity = ownerdata->enfos[idx]->sepabelowusesactivity;
13964
13965 if( sepaaboveusesactivity != NULL )
13966 *sepaaboveusesactivity = ownerdata->enfos[idx]->sepaaboveusesactivity;
13967
13968 if( auxvalue != NULL )
13969 *auxvalue = ownerdata->enfos[idx]->auxvalue;
13970 }
13971
13972 /** sets the auxiliary value of expression for one of the enforcements of an expression */
13973 void SCIPsetExprEnfoAuxValueNonlinear(
13974 SCIP_EXPR* expr, /**< expression */
13975 int idx, /**< position of enforcement in enfos array */
13976 SCIP_Real auxvalue /**< the new value of auxval */
13977 )
13978 {
13979 SCIP_EXPR_OWNERDATA* ownerdata;
13980
13981 assert(expr != NULL);
13982
13983 ownerdata = SCIPexprGetOwnerData(expr);
13984 assert(ownerdata != NULL);
13985
13986 assert(idx >= 0);
13987 assert(idx < ownerdata->nenfos);
13988 assert(ownerdata->enfos[idx] != NULL);
13989
13990 ownerdata->enfos[idx]->auxvalue = auxvalue;
13991 }
13992
13993 /** number of nonlinear handlers whose activity computation and propagation methods depend on the activity of the expression
13994 *
13995 * @note This method can only be used after the detection methods of the nonlinear handlers have been called.
13996 */
13997 unsigned int SCIPgetExprNPropUsesActivityNonlinear(
13998 SCIP_EXPR* expr /**< expression */
13999 )
14000 {
14001 assert(expr != NULL);
14002 assert(SCIPexprGetOwnerData(expr) != NULL);
14003
14004 return SCIPexprGetOwnerData(expr)->nactivityusesprop;
14005 }
14006
14007 /** number of nonlinear handlers whose separation methods (estimate or enforcement) depend on the activity of the expression
14008 *
14009 * @note This method can only be used after the detection methods of the nonlinear handlers have been called.
14010 */
14011 unsigned int SCIPgetExprNSepaUsesActivityNonlinear(
14012 SCIP_EXPR* expr /**< expression */
14013 )
14014 {
14015 assert(expr != NULL);
14016 assert(SCIPexprGetOwnerData(expr) != NULL);
14017
14018 return SCIPexprGetOwnerData(expr)->nactivityusessepa;
14019 }
14020
14021 /** number of nonlinear handlers whose separation methods (estimate or enforcement) use auxiliary variable of the expression
14022 *
14023 * @note This method can only be used after the detection methods of the nonlinear handlers have been called.
14024 */
14025 unsigned int SCIPgetExprNAuxvarUsesNonlinear(
14026 SCIP_EXPR* expr /**< expression */
14027 )
14028 {
14029 assert(expr != NULL);
14030 assert(SCIPexprGetOwnerData(expr) != NULL);
14031
14032 return SCIPexprGetOwnerData(expr)->nauxvaruses;
14033 }
14034
14035 /** method to be called by a nlhdlr during NLHDLRDETECT to notify an expression that it will be used
14036 *
14037 * - if `useauxvar` is enabled, then ensures that an auxiliary variable will be created in INITLP
14038 * - if `useactivityforprop` or `useactivityforsepa{below,above}` is enabled, then ensured that activity will be updated for `expr`
14039 * - if `useactivityforprop` is enabled, then increments the count returned by SCIPgetExprNPropUsesActivityNonlinear()
14040 * - if `useactivityforsepa{below,above}` is enabled, then increments the count returned by SCIPgetExprNSepaUsesActivityNonlinear()
14041 * and also increments this count for all variables in the expression.
14042 *
14043 * The distinction into `useactivityforprop` and `useactivityforsepa{below,above}` is to recognize variables which domain influences
14044 * under/overestimators. Domain propagation routines (like OBBT) may invest more work for these variables.
14045 * The distinction into `useactivityforsepabelow` and `useactivityforsepaabove` is to recognize whether a nlhdlr that called this method
14046 * will use activity of `expr` in enfomethod \ref SCIP_NLHDLR_METHOD_SEPABELOW or \ref SCIP_NLHDLR_METHOD_SEPAABOVE.
14047 */
14048 SCIP_RETCODE SCIPregisterExprUsageNonlinear(
14049 SCIP* scip, /**< SCIP data structure */
14050 SCIP_EXPR* expr, /**< expression */
14051 SCIP_Bool useauxvar, /**< whether an auxiliary variable will be used for estimate or cut generation */
14052 SCIP_Bool useactivityforprop, /**< whether activity of expr will be used by domain propagation or activity calculation (inteval) */
14053 SCIP_Bool useactivityforsepabelow, /**< whether activity of expr will be used by underestimation */
14054 SCIP_Bool useactivityforsepaabove /**< whether activity of expr will be used by overestimation */
14055 )
14056 {
14057 SCIP_EXPR_OWNERDATA* ownerdata;
14058
14059 assert(expr != NULL);
14060
14061 ownerdata = SCIPexprGetOwnerData(expr);
14062 assert(ownerdata != NULL);
14063
14064 /* do not store auxvar request for variable expressions */
14065 if( useauxvar && SCIPisExprVar(scip, expr) )
14066 useauxvar = FALSE;
14067
14068 if( ownerdata->nenfos >= 0 &&
14069 ( (ownerdata->nactivityusesprop == 0 && ownerdata->nactivityusessepa == 0 && (useactivityforprop || useactivityforsepabelow || useactivityforsepaabove)) ||
14070 (ownerdata->nauxvaruses == 0 && useauxvar)
14071 ) )
14072 {
14073 /* if we already have ran detect of nlhdlrs on expr (nenfos >= 0), then we need to rerun detection if
14074 * we require additional enforcement methods, that is,
14075 * - activity of expr was not used before but will be used now, or
14076 * - auxiliary variable of expr was not required before but will be used now
14077 */
14078 SCIP_CALL( freeEnfoData(scip, expr, FALSE) );
14079 }
14080
14081 if( useauxvar )
14082 ++ownerdata->nauxvaruses;
14083
14084 if( useactivityforprop )
14085 ++ownerdata->nactivityusesprop;
14086
14087 if( useactivityforsepabelow || useactivityforsepaabove )
14088 ++ownerdata->nactivityusessepa;
14089
14090 /* remember that SCIPregisterExprUsageNonlinear() has been called with useactivityforsepa{below,above}=TRUE; this
14091 * information is used in detectNlhdlr()
14092 */
14093 if( useactivityforsepabelow )
14094 SCIPconshdlrGetData(ownerdata->conshdlr)->registerusesactivitysepabelow = TRUE;
14095 if( useactivityforsepaabove )
14096 SCIPconshdlrGetData(ownerdata->conshdlr)->registerusesactivitysepaabove = TRUE;
14097
14098 if( useactivityforprop )
14099 {
14100 /* if activity will be used for propagation, then make sure there is a valid activity
14101 * this way, we can do a reversepropcall after detectNlhdlr
14102 */
14103 SCIP_CALL( SCIPevalExprActivity(scip, expr) );
14104 }
14105
14106 /* increase the nactivityusedsepa counter for all variables used in the given expression */
14107 if( (useactivityforsepabelow || useactivityforsepaabove) && SCIPexprGetNChildren(expr) > 0 )
14108 {
14109 SCIP_EXPRITER* it;
14110
14111 /* create and initialize iterator */
14112 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
14113 SCIP_CALL( SCIPexpriterInit(it, expr, SCIP_EXPRITER_DFS, FALSE) );
14114
14115 for( ; !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
14116 if( SCIPisExprVar(scip, expr) )
14117 ++SCIPexprGetOwnerData(expr)->nactivityusessepa;
14118
14119 /* free iterator */
14120 SCIPfreeExpriter(&it);
14121 }
14122
14123 return SCIP_OKAY;
14124 }
14125
14126 /** computes absolute violation for auxvar relation in an expression w.r.t. original variables
14127 *
14128 * Assume the expression is f(x), where x are original (i.e., not auxiliary) variables.
14129 * Assume that f(x) is associated with auxiliary variable z.
14130 *
14131 * If there are negative locks, then returns the violation of z ≤ f(x) and sets `violover` to TRUE.
14132 * If there are positive locks, then returns the violation of z ≥ f(x) and sets `violunder` to TRUE.
14133 * Of course, if there both negative and positive locks, then return the violation of z = f(x).
14134 *
14135 * If necessary, f is evaluated in the given solution. If that fails (domain error),
14136 * then `viol` is set to SCIPinfinity() and both `violover` and `violunder` are set to TRUE.
14137 */
14138 SCIP_RETCODE SCIPgetExprAbsOrigViolationNonlinear(
14139 SCIP* scip, /**< SCIP data structure */
14140 SCIP_EXPR* expr, /**< expression */
14141 SCIP_SOL* sol, /**< solution */
14142 SCIP_Longint soltag, /**< tag of solution */
14143 SCIP_Real* viol, /**< buffer to store computed violation */
14144 SCIP_Bool* violunder, /**< buffer to store whether z >= f(x) is violated, or NULL */
14145 SCIP_Bool* violover /**< buffer to store whether z <= f(x) is violated, or NULL */
14146 )
14147 {
14148 assert(scip != NULL);
14149 assert(expr != NULL);
14150 assert(viol != NULL);
14151
14152 /* make sure expression has been evaluated */
14153 SCIP_CALL( SCIPevalExpr(scip, expr, sol, soltag) );
14154
14155 /* get violation from internal method */
14156 *viol = getExprAbsOrigViolation(scip, expr, sol, violunder, violover);
14157
14158 return SCIP_OKAY;
14159 }
14160
14161 /** computes absolute violation for auxvar relation in an expression w.r.t. auxiliary variables
14162 *
14163 * Assume the expression is f(w), where w are auxiliary variables that were introduced by some nlhdlr.
14164 * Assume that f(w) is associated with auxiliary variable z.
14165 *
14166 * If there are negative locks, then returns the violation of z ≤ f(w) and sets `violover` to TRUE.
14167 * If there are positive locks, then returns the violation of z ≥ f(w) and sets `violunder` to TRUE.
14168 * Of course, if there both negative and positive locks, then return the violation of z = f(w).
14169 *
14170 * If the given value of f(w) is SCIP_INVALID, then `viol` is set to SCIPinfinity() and
14171 * both `violover` and `violunder` are set to TRUE.
14172 */
14173 SCIP_RETCODE SCIPgetExprAbsAuxViolationNonlinear(
14174 SCIP* scip, /**< SCIP data structure */
14175 SCIP_EXPR* expr, /**< expression */
14176 SCIP_Real auxvalue, /**< the value of f(w) */
14177 SCIP_SOL* sol, /**< solution that has been evaluated */
14178 SCIP_Real* viol, /**< buffer to store computed violation */
14179 SCIP_Bool* violunder, /**< buffer to store whether z >= f(w) is violated, or NULL */
14180 SCIP_Bool* violover /**< buffer to store whether z <= f(w) is violated, or NULL */
14181 )
14182 {
14183 assert(scip != NULL);
14184 assert(expr != NULL);
14185 assert(viol != NULL);
14186
14187 /* get violation from internal method */
14188 *viol = getExprAbsAuxViolation(scip, expr, auxvalue, sol, violunder, violover);
14189
14190 return SCIP_OKAY;
14191 }
14192
14193
14194 /** computes relative violation for auxvar relation in an expression w.r.t. auxiliary variables
14195 *
14196 * Assume the expression is f(w), where w are auxiliary variables that were introduced by some nlhdlr.
14197 * Assume that f(w) is associated with auxiliary variable z.
14198 *
14199 * Taking the absolute violation from SCIPgetExprAbsAuxViolationNonlinear(), this function returns
14200 * the absolute violation divided by max(1,|f(w)|).
14201 *
14202 * If the given value of f(w) is SCIP_INVALID, then `viol` is set to SCIPinfinity() and
14203 * both `violover` and `violunder` are set to TRUE.
14204 */
14205 SCIP_RETCODE SCIPgetExprRelAuxViolationNonlinear(
14206 SCIP* scip, /**< SCIP data structure */
14207 SCIP_EXPR* expr, /**< expression */
14208 SCIP_Real auxvalue, /**< the value of f(w) */
14209 SCIP_SOL* sol, /**< solution that has been evaluated */
14210 SCIP_Real* viol, /**< buffer to store computed violation */
14211 SCIP_Bool* violunder, /**< buffer to store whether z >= f(w) is violated, or NULL */
14212 SCIP_Bool* violover /**< buffer to store whether z <= f(w) is violated, or NULL */
14213 )
14214 {
14215 assert(scip != NULL);
14216 assert(expr != NULL);
14217 assert(viol != NULL);
14218
14219 /* get violation from internal method */
14220 *viol = getExprAbsAuxViolation(scip, expr, auxvalue, sol, violunder, violover);
14221
14222 if( !SCIPisInfinity(scip, *viol) )
14223 {
14224 assert(auxvalue != SCIP_INVALID);
14225 /* TODO maybe we should rather use max(eps,|auxvalue|)? */
14226 *viol /= MAX(1.0, REALABS(auxvalue));
14227 }
14228
14229 return SCIP_OKAY;
14230 }
14231
14232 /** returns bounds on the expression
14233 *
14234 * This gives an intersection of bounds from
14235 * - activity calculation (SCIPexprGetActivity()), if valid,
14236 * - auxiliary variable, if present,
14237 * - stored by SCIPtightenExprIntervalNonlinear() during domain propagation
14238 *
14239 * @note The returned interval can be empty!
14240 */
14241 SCIP_INTERVAL SCIPgetExprBoundsNonlinear(
14242 SCIP* scip, /**< SCIP data structure */
14243 SCIP_EXPR* expr /**< expression */
14244 )
14245 {
14246 SCIP_EXPR_OWNERDATA* ownerdata;
14247 SCIP_CONSHDLRDATA* conshdlrdata;
14248 SCIP_INTERVAL bounds;
14249
14250 assert(scip != NULL);
14251 assert(expr != NULL);
14252
14253 ownerdata = SCIPexprGetOwnerData(expr);
14254 assert(ownerdata != NULL);
14255
14256 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr);
14257 assert(conshdlrdata != NULL);
14258
14259 /* SCIPdebugMsg(scip, "get bounds expr %p:", expr); */
14260
14261 /* start with propbounds if they belong to current propagation */
14262 if( ownerdata->propboundstag == conshdlrdata->curpropboundstag )
14263 {
14264 bounds = ownerdata->propbounds;
14265 /* SCIPdebugMsgPrint(scip, " propbounds [%.15g,%.15g]", ownerdata->propbounds.inf, ownerdata->propbounds.sup); */
14266 }
14267 else
14268 SCIPintervalSetEntire(SCIP_INTERVAL_INFINITY, &bounds);
14269
14270 if( SCIPexprGetActivityTag(expr) >= conshdlrdata->lastboundrelax )
14271 {
14272 /* apply propbounds to expr activity, but ensure it's not-empty if very close disjoint intervals */
14273 /* SCIPdebugMsgPrint(scip, " activity [%.15g,%.15g]", expr->activity.inf, expr->activity.sup); */
14274 SCIPintervalIntersectEps(&bounds, SCIPepsilon(scip), SCIPexprGetActivity(expr), bounds);
14275 }
14276
14277 if( ownerdata->auxvar != NULL )
14278 {
14279 /* apply auxiliary variable bounds to bounds */
14280 SCIP_INTERVAL auxvarbounds;
14281
14282 auxvarbounds = conshdlrdata->intevalvar(scip, ownerdata->auxvar, conshdlrdata);
14283 /* SCIPdebugMsgPrint(scip, " auxvar [%.15g,%.15g]", auxvarbounds.inf, auxvarbounds.sup); */
14284 SCIPintervalIntersectEps(&bounds, SCIPepsilon(scip), bounds, auxvarbounds);
14285 }
14286
14287 /* SCIPdebugMsgPrint(scip, " -> [%.15g,%.15g]\n", bounds.inf, bounds.sup); */
14288
14289 return bounds;
14290 }
14291
14292 /** informs the expression about new bounds that can be used for reverse-propagation and to tighten bounds of
14293 * corresponding (auxiliary) variable (if any)
14294 *
14295 * @attention this function should only be called during domain propagation in cons_nonlinear
14296 */
14297 SCIP_RETCODE SCIPtightenExprIntervalNonlinear(
14298 SCIP* scip, /**< SCIP data structure */
14299 SCIP_EXPR* expr, /**< expression to be tightened */
14300 SCIP_INTERVAL newbounds, /**< new bounds for the expression */
14301 SCIP_Bool* cutoff, /**< buffer to store whether a cutoff was detected */
14302 int* ntightenings /**< buffer to add the total number of tightenings, or NULL */
14303 )
14304 {
14305 SCIP_EXPR_OWNERDATA* ownerdata;
14306 SCIP_CONSHDLRDATA* conshdlrdata;
14307
14308 assert(scip != NULL);
14309 assert(expr != NULL);
14310 assert(cutoff != NULL);
14311
14312 ownerdata = SCIPexprGetOwnerData(expr);
14313 assert(ownerdata != NULL);
14314 assert(ownerdata->conshdlr != NULL);
14315
14316 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr);
14317 assert(conshdlrdata != NULL);
14318
14319 /* the code below assumes that current activity is valid
14320 * if it turns out that we cannot ensure that, then we should change code
14321 */
14322 assert(SCIPexprGetActivityTag(expr) >= conshdlrdata->lastboundrelax || SCIPintervalIsEntire(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(expr)));
14323 assert(!SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(expr)));
14324
14325 *cutoff = FALSE;
14326
14327 #ifdef DEBUG_PROP
14328 SCIPdebugMsg(scip, "Trying to tighten bounds of expr ");
14329 SCIP_CALL( SCIPprintExpr(scip, expr, NULL) );
14330 SCIPdebugMsgPrint(scip, " with activity [%.15g,%.15g] to [%.15g,%.15g] (force=%d)\n", SCIPexprGetActivity(expr).inf, SCIPexprGetActivity(expr).sup, newbounds.inf, newbounds.sup, conshdlrdata->forceboundtightening);
14331 #endif
14332
14333 if( SCIPexprIsIntegral(expr) )
14334 {
14335 /* apply integrality to new bounds
14336 * it should be ok to use normal ceil() and floor(), but for safety, we use SCIPceil and SCIPfloor for now
14337 */
14338 if( newbounds.inf > -SCIP_INTERVAL_INFINITY )
14339 newbounds.inf = SCIPceil(scip, newbounds.inf);
14340 if( newbounds.sup < SCIP_INTERVAL_INFINITY )
14341 newbounds.sup = SCIPfloor(scip, newbounds.sup);
14342 #ifdef DEBUG_PROP
14343 SCIPdebugMsg(scip, " applied integrality: [%.15g,%.15g]\n", newbounds.inf, newbounds.sup);
14344 #endif
14345 }
14346
14347 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, newbounds) )
14348 {
14349 SCIPdebugMsg(scip, " cut off due to new bounds being empty\n");
14350
14351 *cutoff = TRUE;
14352 return SCIP_OKAY;
14353 }
14354
14355 /* treat the new bounds as empty if either the lower/upper bound is above/below +/- SCIPinfinity() */
14356 if( SCIPisInfinity(scip, newbounds.inf) || SCIPisInfinity(scip, -newbounds.sup) )
14357 {
14358 SCIPdebugMsg(scip, " cut off due to new bounds being beyond infinity\n");
14359
14360 *cutoff = TRUE;
14361 return SCIP_OKAY;
14362 }
14363
14364 /* tighten newbounds w.r.t. existing expr->propbounds or activity */
14365 if( ownerdata->propboundstag == conshdlrdata->curpropboundstag )
14366 {
14367 /* if already having propbounds in expr, then tighten newbounds by propbounds */
14368 SCIPintervalIntersectEps(&newbounds, SCIPepsilon(scip), ownerdata->propbounds, newbounds);
14369 }
14370 else
14371 {
14372 /* first time we have propbounds for expr in this propagation rounds:
14373 * intersect with activity (though don't let it become empty if very close intervals)
14374 */
14375 SCIPintervalIntersectEps(&newbounds, SCIPepsilon(scip), SCIPexprGetActivity(expr), newbounds);
14376 }
14377 #ifdef DEBUG_PROP
14378 SCIPdebugMsg(scip, " applied %s: [%.20g,%.20g]\n", ownerdata->propboundstag == conshdlrdata->curpropboundstag ? "previous propbounds" : "activity", newbounds.inf, newbounds.sup);
14379 #endif
14380
14381 /* check if the new bounds lead to an empty interval */
14382 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, newbounds) )
14383 {
14384 SCIPdebugMsg(scip, " cut off due to empty intersection with previous propbounds or activity\n");
14385
14386 *cutoff = TRUE;
14387 return SCIP_OKAY;
14388 }
14389
14390 /* if expr is not constant or variable, then store newbounds in expr->propbounds
14391 * - for constant, the intersection with activity should have been sufficient to determine infeasibilty
14392 * - for variable, the tightenAuxVarBounds call below should be suffient to have to new bounds acknowledged
14393 */
14394 if( SCIPexprGetNChildren(expr) > 0 )
14395 {
14396 ownerdata->propbounds = newbounds;
14397 ownerdata->propboundstag = conshdlrdata->curpropboundstag;
14398 }
14399
14400 /* if updated propbounds do not allow a sufficient tightening, then do not consider adding to queue for reverse
14401 * propagation or update of auxvar bounds
14402 * TODO? if we first had a considerable tightening and then only get small tightenings under the same
14403 * curpropboundstag, then these will still be considered as isIntervalBetter, since we compare with activity here and
14404 * not with the propbounds as set in the beginning; I'm not sure, though, that comparing always with previous
14405 * propbounds would be better, since a number of small updates to propbounds could eventually lead to a considerable
14406 * one or should we not even update propbounds to newbounds if the update is small?
14407 */
14408 if( !isIntervalBetter(scip, conshdlrdata->forceboundtightening, newbounds, SCIPexprGetActivity(expr)) )
14409 {
14410 #ifdef DEBUG_PROP
14411 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);
14412 #endif
14413 return SCIP_OKAY;
14414 }
14415
14416 if( SCIPexprGetNChildren(expr) > 0 && !ownerdata->inpropqueue && (ownerdata->nactivityusesprop > 0 || ownerdata->nactivityusessepa > 0 || ownerdata->nenfos < 0) )
14417 {
14418 /* add expression to propagation queue if not there yet and not var or constant and
14419 * if it should have a nlhdlr with a reverseprop callback or nlhdlrs are not initialized yet (nenfos < 0)
14420 */
14421 #ifdef DEBUG_PROP
14422 SCIPdebugMsg(scip, " insert expr <%p> (%s) into reversepropqueue\n", (void*)expr, SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)));
14423 #endif
14424 SCIP_CALL( SCIPqueueInsert(conshdlrdata->reversepropqueue, expr) );
14425 ownerdata->inpropqueue = TRUE;
14426 }
14427
14428 /* update bounds on variable or auxiliary variable */
14429 SCIP_CALL( tightenAuxVarBounds(scip, ownerdata->conshdlr, expr, newbounds, cutoff, ntightenings) );
14430
14431 return SCIP_OKAY;
14432 }
14433
14434 /** mark constraints that include this expression to be propagated again
14435 *
14436 * This can be used by, e.g., nlhdlrs, to trigger a new propagation of constraints without
14437 * a change of variable bounds, e.g., because new information on the expression is available
14438 * that could potentially lead to tighter expression activity values.
14439 *
14440 * Note, that this call marks also constraints for propagation which only share some variable
14441 * with this expression.
14442 */
14443 SCIP_RETCODE SCIPmarkExprPropagateNonlinear(
14444 SCIP* scip, /**< SCIP data structure */
14445 SCIP_EXPR* expr /**< expression to propagate again */
14446 )
14447 {
14448 SCIP_EXPRITER* it;
14449 SCIP_CONSDATA* consdata;
14450 SCIP_EXPR_OWNERDATA* ownerdata;
14451 int c;
14452
14453 assert(scip != NULL);
14454 assert(expr != NULL);
14455
14456 ownerdata = SCIPexprGetOwnerData(expr);
14457 assert(ownerdata != NULL);
14458
14459 SCIPincrementCurBoundsTagNonlinear(ownerdata->conshdlr, FALSE);
14460
14461 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
14462 SCIP_CALL( SCIPexpriterInit(it, expr, SCIP_EXPRITER_DFS, FALSE) );
14463
14464 for( ; !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
14465 {
14466 if( !SCIPisExprVar(scip, expr) )
14467 continue;
14468
14469 ownerdata = SCIPexprGetOwnerData(expr);
14470 assert(ownerdata != NULL);
14471
14472 for( c = 0; c < ownerdata->nconss; ++c )
14473 {
14474 consdata = SCIPconsGetData(ownerdata->conss[c]);
14475 assert(consdata != NULL);
14476 consdata->ispropagated = FALSE;
14477 }
14478 }
14479
14480 SCIPfreeExpriter(&it);
14481
14482 return SCIP_OKAY;
14483 }
14484
14485 /** adds violation-branching score to an expression
14486 *
14487 * Adds a score to the expression-specific violation-branching score, thereby marking it as branching candidate.
14488 * The expression must either be a variable expression or have an aux-variable.
14489 * In the latter case, branching on auxiliary variables must have been enabled.
14490 * In case of doubt, use SCIPaddExprsViolScoreNonlinear(). Roughly, the difference between these functions is that the current
14491 * function adds `violscore` to the expression directly, while SCIPaddExprsViolScoreNonlinear() will split the
14492 * violation score among all the given expressions according to parameter constraints/nonlinear/branching/violsplit.
14493 *
14494 * @see SCIPaddExprsViolScoreNonlinear()
14495 */
14496 void SCIPaddExprViolScoreNonlinear(
14497 SCIP* scip, /**< SCIP data structure */
14498 SCIP_EXPR* expr, /**< expression where to add branching score */
14499 SCIP_Real violscore /**< violation score to add to expression */
14500 )
14501 {
14502 SCIP_EXPR_OWNERDATA* ownerdata;
14503 SCIP_CONSHDLRDATA* conshdlrdata;
14504
14505 assert(scip != NULL);
14506 assert(expr != NULL);
14507 assert(violscore >= 0.0);
14508
14509 ownerdata = SCIPexprGetOwnerData(expr);
14510 assert(ownerdata != NULL);
14511
14512 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr);
14513 assert(conshdlrdata != NULL);
14514
14515 /* if not allowing to branch on auxvars, then expr must be a var-expr */
14516 assert(branchAuxNonlinear(scip, ownerdata->conshdlr) || SCIPisExprVar(scip, expr));
14517 /* if allowing to branch on auxvars, then expr must be a var-expr or have an auxvar */
14518 assert(!branchAuxNonlinear(scip, ownerdata->conshdlr) || SCIPisExprVar(scip, expr) || ownerdata->auxvar != NULL);
14519
14520 /* reset branching score if we are in a different enfo round */
14521 if( ownerdata->violscoretag != conshdlrdata->enforound )
14522 {
14523 ownerdata->violscoresum = violscore;
14524 ownerdata->violscoremax = violscore;
14525 ownerdata->nviolscores = 1;
14526 ownerdata->violscoretag = conshdlrdata->enforound;
14527 return;
14528 }
14529
14530 ownerdata->violscoresum += violscore;
14531 if( violscore > ownerdata->violscoremax )
14532 ownerdata->violscoremax = violscore;
14533 ++ownerdata->nviolscores;
14534 }
14535
14536 /** adds violation-branching score to a set of expressions, distributing the score among all the expressions
14537 *
14538 * Each expression must either be a variable expression or have an aux-variable.
14539 * If branching on aux-variables is disabled, then the violation branching score will be distributed among all
14540 * variables present in `exprs`.
14541 */
14542 SCIP_RETCODE SCIPaddExprsViolScoreNonlinear(
14543 SCIP* scip, /**< SCIP data structure */
14544 SCIP_EXPR** exprs, /**< expressions where to add branching score */
14545 int nexprs, /**< number of expressions */
14546 SCIP_Real violscore, /**< violation score to add to expression */
14547 SCIP_SOL* sol, /**< current solution */
14548 SCIP_Bool* success /**< buffer to store whether at least one violscore was added */
14549 )
14550 {
14551 SCIP_EXPRITER* it;
14552 SCIP_EXPR** varexprs;
14553 SCIP_EXPR* e;
14554 int nvars;
14555 int varssize;
14556 int i;
14557
14558 assert(exprs != NULL || nexprs == 0);
14559 assert(success != NULL);
14560
14561 if( nexprs == 0 )
14562 {
14563 *success = FALSE;
14564 return SCIP_OKAY;
14565 }
14566
14567 /* if allowing to branch on auxiliary variables, then call internal addConsExprExprsViolScore immediately */
14568 if( branchAuxNonlinear(scip, SCIPexprGetOwnerData(exprs[0])->conshdlr) )
14569 {
14570 addExprsViolScore(scip, exprs, nexprs, violscore, sol, success);
14571 return SCIP_OKAY;
14572 }
14573
14574 /* if not allowing to branch on aux vars, then create new array containing var expressions that exprs depend on */
14575 nvars = 0;
14576 varssize = 5;
14577 SCIP_CALL( SCIPallocBufferArray(scip, &varexprs, varssize) );
14578
14579 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
14580 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) );
14581
14582 for( i = 0; i < nexprs; ++i )
14583 {
14584 for( e = SCIPexpriterRestartDFS(it, exprs[i]); !SCIPexpriterIsEnd(it); e = SCIPexpriterGetNext(it) )
14585 {
14586 assert(e != NULL);
14587
14588 if( SCIPisExprVar(scip, e) )
14589 {
14590 /* add variable expression to vars array */
14591 if( varssize == nvars )
14592 {
14593 varssize = SCIPcalcMemGrowSize(scip, nvars + 1);
14594 SCIP_CALL( SCIPreallocBufferArray(scip, &varexprs, varssize) );
14595 }
14596 assert(varssize > nvars);
14597
14598 varexprs[nvars++] = e;
14599 }
14600 }
14601 }
14602
14603 SCIPfreeExpriter(&it);
14604
14605 addExprsViolScore(scip, varexprs, nvars, violscore, sol, success);
14606
14607 SCIPfreeBufferArray(scip, &varexprs);
14608
14609 return SCIP_OKAY;
14610 }
14611
14612 /** gives violation-branching score stored in expression, or 0.0 if no valid score has been stored */
14613 SCIP_Real SCIPgetExprViolScoreNonlinear(
14614 SCIP_EXPR* expr /**< expression */
14615 )
14616 {
14617 SCIP_EXPR_OWNERDATA* ownerdata;
14618 SCIP_CONSHDLRDATA* conshdlrdata;
14619
14620 assert(expr != NULL);
14621
14622 ownerdata = SCIPexprGetOwnerData(expr);
14623 assert(ownerdata != NULL);
14624
14625 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr);
14626 assert(conshdlrdata != NULL);
14627
14628 if( conshdlrdata->enforound != ownerdata->violscoretag )
14629 return 0.0;
14630
14631 if( ownerdata->nviolscores == 0 )
14632 return 0.0;
14633
14634 switch( conshdlrdata->branchscoreagg )
14635 {
14636 case 'a' :
14637 /* average */
14638 return ownerdata->violscoresum / ownerdata->nviolscores;
14639
14640 case 'm' :
14641 /* maximum */
14642 return ownerdata->violscoremax;
14643
14644 case 's' :
14645 /* sum */
14646 return ownerdata->violscoresum;
14647
14648 default:
14649 SCIPerrorMessage("Invalid value %c for branchscoreagg parameter\n", conshdlrdata->branchscoreagg);
14650 SCIPABORT();
14651 return SCIP_INVALID;
14652 }
14653 }
14654
14655 /** returns the partial derivative of an expression w.r.t. a variable (or SCIP_INVALID if there was an evaluation error)
14656 *
14657 * @see SCIPexprGetDerivative()
14658 */
14659 SCIP_Real SCIPgetExprPartialDiffNonlinear(
14660 SCIP* scip, /**< SCIP data structure */
14661 SCIP_EXPR* expr, /**< root expression of constraint used in the last SCIPevalExprGradient() call */
14662 SCIP_VAR* var /**< variable (needs to be in the expression) */
14663 )
14664 {
14665 SCIP_EXPR_OWNERDATA* ownerdata;
14666 SCIP_CONSHDLRDATA* conshdlrdata;
14667 SCIP_EXPR* varexpr;
14668
14669 assert(scip != NULL);
14670 assert(expr != NULL);
14671 assert(var != NULL);
14672
14673 /* return 0.0 for value expression */
14674 if( SCIPisExprValue(scip, expr) )
14675 {
14676 assert(SCIPexprGetDerivative(expr) == 0.0);
14677 return 0.0;
14678 }
14679
14680 /* check if an error occurred during the last SCIPevalExprGradient() call */
14681 if( SCIPexprGetDerivative(expr) == SCIP_INVALID )
14682 return SCIP_INVALID;
14683
14684 ownerdata = SCIPexprGetOwnerData(expr);
14685 assert(ownerdata != NULL);
14686
14687 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr);
14688 assert(conshdlrdata != NULL);
14689
14690 /* use variable to expressions mapping which is stored in the constraint handler data */
14691 assert(SCIPhashmapExists(conshdlrdata->var2expr, var));
14692
14693 varexpr = (SCIP_EXPR*)SCIPhashmapGetImage(conshdlrdata->var2expr, var);
14694 assert(varexpr != NULL);
14695 assert(SCIPisExprVar(scip, varexpr));
14696
14697 /* use difftag to decide whether the variable belongs to the expression */
14698 return (SCIPexprGetDiffTag(expr) != SCIPexprGetDiffTag(varexpr)) ? 0.0 : SCIPexprGetDerivative(varexpr);
14699 }
14700
14701 /** 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)
14702 *
14703 * @see SCIPexprGetBardot()
14704 */
14705 SCIP_Real SCIPgetExprPartialDiffGradientDirNonlinear(
14706 SCIP* scip, /**< SCIP data structure */
14707 SCIP_EXPR* expr, /**< root expression of constraint used in the last SCIPevalExprHessianDir() call */
14708 SCIP_VAR* var /**< variable (needs to be in the expression) */
14709 )
14710 {
14711 SCIP_EXPR_OWNERDATA* ownerdata;
14712 SCIP_CONSHDLRDATA* conshdlrdata;
14713 SCIP_EXPR* varexpr;
14714
14715 assert(scip != NULL);
14716 assert(expr != NULL);
14717 assert(var != NULL);
14718
14719 /* return 0.0 for value expression */
14720 if( SCIPisExprValue(scip, expr) )
14721 return 0.0;
14722
14723 /* check if an error occurred during the last SCIPevalExprHessianDir() call */
14724 if( SCIPexprGetBardot(expr) == SCIP_INVALID )
14725 return SCIP_INVALID;
14726
14727 ownerdata = SCIPexprGetOwnerData(expr);
14728 assert(ownerdata != NULL);
14729
14730 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr);
14731 assert(conshdlrdata != NULL);
14732
14733 /* use variable to expressions mapping which is stored in the constraint handler data;
14734 * if this fails it means that we are asking for the var's component of H*u for a var
14735 * that doesn't appear in any nonlinear constraint, so maybe we can also just return 0.0
14736 */
14737 assert(SCIPhashmapExists(conshdlrdata->var2expr, var));
14738
14739 varexpr = (SCIP_EXPR*)SCIPhashmapGetImage(conshdlrdata->var2expr, var);
14740 assert(varexpr != NULL);
14741 assert(SCIPisExprVar(scip, varexpr));
14742
14743 /* use difftag to decide whether the variable belongs to the expression */
14744 return (SCIPexprGetDiffTag(expr) != SCIPexprGetDiffTag(varexpr)) ? 0.0 : SCIPexprGetBardot(varexpr);
14745 }
14746
14747 /** evaluates quadratic term in a solution w.r.t. auxiliary variables
14748 *
14749 * \note This requires that for every expr used in the quadratic data, a variable or auxiliary variable is available.
14750 */
14751 SCIP_Real SCIPevalExprQuadraticAuxNonlinear(
14752 SCIP* scip, /**< SCIP data structure */
14753 SCIP_EXPR* expr, /**< quadratic expression */
14754 SCIP_SOL* sol /**< solution to evaluate, or NULL for LP solution */
14755 )
14756 {
14757 SCIP_Real auxvalue;
14758 int nlinexprs;
14759 SCIP_Real* lincoefs;
14760 SCIP_EXPR** linexprs;
14761 int nquadexprs;
14762 int nbilinexprs;
14763 int i;
14764
14765 assert(scip != NULL);
14766 assert(expr != NULL);
14767
14768 SCIPexprGetQuadraticData(expr, &auxvalue, &nlinexprs, &linexprs, &lincoefs, &nquadexprs, &nbilinexprs, NULL, NULL);
14769
14770 /* linear terms */
14771 for( i = 0; i < nlinexprs; ++i )
14772 {
14773 assert(SCIPgetExprAuxVarNonlinear(linexprs[i]) != NULL);
14774 auxvalue += lincoefs[i] * SCIPgetSolVal(scip, sol, SCIPgetExprAuxVarNonlinear(linexprs[i]));
14775 }
14776
14777 /* quadratic terms */
14778 for( i = 0; i < nquadexprs; ++i )
14779 {
14780 SCIP_EXPR* quadexprterm;
14781 SCIP_Real lincoef;
14782 SCIP_Real sqrcoef;
14783 SCIP_Real solval;
14784
14785 SCIPexprGetQuadraticQuadTerm(expr, i, &quadexprterm, &lincoef, &sqrcoef, NULL, NULL, NULL);
14786
14787 assert(SCIPgetExprAuxVarNonlinear(quadexprterm) != NULL);
14788
14789 solval = SCIPgetSolVal(scip, sol, SCIPgetExprAuxVarNonlinear(quadexprterm));
14790 auxvalue += (lincoef + sqrcoef * solval) * solval;
14791 }
14792
14793 /* bilinear terms */
14794 for( i = 0; i < nbilinexprs; ++i )
14795 {
14796 SCIP_EXPR* expr1;
14797 SCIP_EXPR* expr2;
14798 SCIP_Real coef;
14799
14800 SCIPexprGetQuadraticBilinTerm(expr, i, &expr1, &expr2, &coef, NULL, NULL);
14801
14802 assert(SCIPgetExprAuxVarNonlinear(expr1) != NULL);
14803 assert(SCIPgetExprAuxVarNonlinear(expr2) != NULL);
14804 auxvalue += coef * SCIPgetSolVal(scip, sol, SCIPgetExprAuxVarNonlinear(expr1)) * SCIPgetSolVal(scip, sol, SCIPgetExprAuxVarNonlinear(expr2));
14805 }
14806
14807 return auxvalue;
14808 }
14809
14810 /**@addtogroup PublicNlhdlrInterfaceMethods
14811 * @{
14812 */
14813
14814 /** creates a nonlinear handler and includes it into the nonlinear constraint handler */
14815 SCIP_RETCODE SCIPincludeNlhdlrNonlinear(
14816 SCIP* scip, /**< SCIP data structure */
14817 SCIP_NLHDLR** nlhdlr, /**< buffer where to store nonlinear handler */
14818 const char* name, /**< name of nonlinear handler (must not be NULL) */
14819 const char* desc, /**< description of nonlinear handler (can be NULL) */
14820 int detectpriority, /**< detection priority of nonlinear handler */
14821 int enfopriority, /**< enforcement priority of nonlinear handler */
14822 SCIP_DECL_NLHDLRDETECT((*detect)), /**< structure detection callback of nonlinear handler */
14823 SCIP_DECL_NLHDLREVALAUX((*evalaux)), /**< auxiliary evaluation callback of nonlinear handler */
14824 SCIP_NLHDLRDATA* nlhdlrdata /**< data of nonlinear handler (can be NULL) */
14825 )
14826 {
14827 SCIP_CONSHDLR* conshdlr;
14828 SCIP_CONSHDLRDATA* conshdlrdata;
14829
14830 assert(scip != NULL);
14831 assert(nlhdlr != NULL);
14832 assert(detect != NULL);
14833
14834 /* find myself */
14835 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
14836 if( conshdlr == NULL )
14837 {
14838 SCIPerrorMessage("nonlinear constraint handler not found");
14839 return SCIP_PLUGINNOTFOUND;
14840 }
14841
14842 /* create nlhdlr */
14843 SCIP_CALL( SCIPnlhdlrCreate(scip, nlhdlr, name, desc, detectpriority, enfopriority, detect, evalaux, nlhdlrdata) );
14844
14845 /* include into constraint handler */
14846 conshdlrdata = SCIPconshdlrGetData(conshdlr);
14847 assert(conshdlrdata != NULL);
14848
14849 SCIP_CALL( SCIPensureBlockMemoryArray(scip, &conshdlrdata->nlhdlrs, &conshdlrdata->nlhdlrssize, conshdlrdata->nnlhdlrs+1) );
14850
14851 conshdlrdata->nlhdlrs[conshdlrdata->nnlhdlrs] = *nlhdlr;
14852 ++conshdlrdata->nnlhdlrs;
14853
14854 /* sort nonlinear handlers by detection priority, in decreasing order
14855 * will happen in INIT, so only do when called late
14856 */
14857 if( SCIPgetStage(scip) > SCIP_STAGE_INIT && conshdlrdata->nnlhdlrs > 1 )
14858 SCIPsortDownPtr((void**)conshdlrdata->nlhdlrs, SCIPnlhdlrComp, conshdlrdata->nnlhdlrs);
14859
14860 return SCIP_OKAY;
14861 }
14862
14863 /** get number of nonlinear handler */
14864 int SCIPgetNNlhdlrsNonlinear(
14865 SCIP_CONSHDLR* conshdlr /**< nonlinear constraint handler */
14866 )
14867 {
14868 SCIP_CONSHDLRDATA* conshdlrdata;
14869
14870 assert(conshdlr != NULL);
14871
14872 conshdlrdata = SCIPconshdlrGetData(conshdlr);
14873 assert(conshdlrdata != NULL);
14874
14875 return conshdlrdata->nnlhdlrs;
14876 }
14877
14878 /** get nonlinear handlers */
14879 SCIP_NLHDLR** SCIPgetNlhdlrsNonlinear(
14880 SCIP_CONSHDLR* conshdlr /**< nonlinear constraint handler */
14881 )
14882 {
14883 SCIP_CONSHDLRDATA* conshdlrdata;
14884
14885 assert(conshdlr != NULL);
14886
14887 conshdlrdata = SCIPconshdlrGetData(conshdlr);
14888 assert(conshdlrdata != NULL);
14889
14890 return conshdlrdata->nlhdlrs;
14891 }
14892
14893 /** returns a nonlinear handler of a given name (or NULL if not found) */
14894 SCIP_NLHDLR* SCIPfindNlhdlrNonlinear(
14895 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */
14896 const char* name /**< name of nonlinear handler */
14897 )
14898 {
14899 SCIP_CONSHDLRDATA* conshdlrdata;
14900 int h;
14901
14902 assert(conshdlr != NULL);
14903 assert(name != NULL);
14904
14905 conshdlrdata = SCIPconshdlrGetData(conshdlr);
14906 assert(conshdlrdata != NULL);
14907
14908 for( h = 0; h < conshdlrdata->nnlhdlrs; ++h )
14909 if( strcmp(SCIPnlhdlrGetName(conshdlrdata->nlhdlrs[h]), name) == 0 )
14910 return conshdlrdata->nlhdlrs[h];
14911
14912 return NULL;
14913 }
14914
14915 /** gives expression data that a given nonlinear handler stored in an expression
14916 *
14917 * Returns NULL if expr has not been detected by nlhdlr or nlhdlr did not store data.
14918 */
14919 SCIP_NLHDLREXPRDATA* SCIPgetNlhdlrExprDataNonlinear(
14920 SCIP_NLHDLR* nlhdlr, /**< nonlinear handler */
14921 SCIP_EXPR* expr /**< expression */
14922 )
14923 {
14924 SCIP_EXPR_OWNERDATA* ownerdata;
14925 int e;
14926
14927 assert(nlhdlr != NULL);
14928 assert(expr != NULL);
14929
14930 ownerdata = SCIPexprGetOwnerData(expr);
14931 assert(ownerdata != NULL);
14932
14933 for( e = 0; e < ownerdata->nenfos; ++e )
14934 if( ownerdata->enfos[e]->nlhdlr == nlhdlr )
14935 return ownerdata->enfos[e]->nlhdlrexprdata;
14936
14937 return NULL;
14938 }
14939
14940 /** @} */
14941