Scippy

SCIP

Solving Constraint Integer Programs

cons_nonlinear.c
Go to the documentation of this file.
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2/* */
3/* This file is part of the program and library */
4/* SCIP --- Solving Constraint Integer Programs */
5/* */
6/* Copyright (c) 2002-2024 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"
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"
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 50 /**< 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 &ge; 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 }
139FILE* enfologfile = NULL;
140#else
141#define ENFOLOG(x)
142#endif
143
144/*
145 * Data structures
146 */
147
148/** enforcement data of an expression */
149typedef 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 */
161struct 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 */
201struct 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 */
240typedef 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 */
246
247/** constraint handler data */
248struct 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 branchfracweight; /**< weight by how much to consider fractionality of integer variables in branching score for spatial branching */
313 SCIP_Real branchdualweight; /**< weight by how much to consider the dual values of rows that contain a variable for its branching score */
314 SCIP_Real branchpscostweight; /**< weight by how much to consider the pseudo cost of a variable for its branching score */
315 SCIP_Real branchdomainweight; /**< weight by how much to consider the domain width in branching score */
316 SCIP_Real branchvartypeweight;/**< weight by how much to consider variable type in branching score */
317 char branchscoreagg; /**< how to aggregate several branching scores given for the same expression ('a'verage, 'm'aximum, or 's'um) */
318 char branchviolsplit; /**< method used to split violation in expression onto variables ('u'niform, 'm'idness of solution, 'd'omain width, 'l'ogarithmic domain width) */
319 SCIP_Real branchpscostreliable; /**< minimum pseudo-cost update count required to consider pseudo-costs reliable */
320 SCIP_Real branchmixfractional; /**< minimal average pseudo cost count for discrete variables at which to start considering spatial branching before branching on fractional integer variables */
321 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) */
322 SCIP_Bool assumeconvex; /**< whether to assume that any constraint is convex */
323
324 /* statistics */
325 SCIP_Longint nweaksepa; /**< number of times we used "weak" cuts for enforcement */
326 SCIP_Longint ntightenlp; /**< number of times we requested solving the LP with a smaller feasibility tolerance when enforcing */
327 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 */
328 SCIP_Longint ndesperatebranch; /**< number of times we branched on some variable because normal enforcement was not successful */
329 SCIP_Longint ndesperatecutoff; /**< number of times we cut off a node in enforcement because no branching candidate could be found */
330 SCIP_Longint nforcelp; /**< number of times we forced solving the LP when enforcing a pseudo solution */
331 SCIP_CLOCK* canonicalizetime; /**< time spend for canonicalization */
332 SCIP_Longint ncanonicalizecalls; /**< number of times we called canonicalization */
333
334 /* facets of envelops of vertex-polyhedral functions */
335 SCIP_RANDNUMGEN* vp_randnumgen; /**< random number generator used to perturb reference point */
336 SCIP_LPI* vp_lp[SCIP_MAXVERTEXPOLYDIM+1]; /**< LPs used to compute facets for functions of different dimension */
337
338 /* hashing of bilinear terms */
339 SCIP_HASHTABLE* bilinhashtable; /**< hash table for bilinear terms */
340 SCIP_CONSNONLINEAR_BILINTERM* bilinterms; /**< bilinear terms */
341 int nbilinterms; /**< total number of bilinear terms */
342 int bilintermssize; /**< size of bilinterms array */
343 int bilinmaxnauxexprs; /**< maximal number of auxiliary expressions per bilinear term */
344
345 /* branching */
346 SCIP_RANDNUMGEN* branchrandnumgen; /**< random number generated used in branching variable selection */
347 char branchpscostupdatestrategy; /**< value of parameter branching/lpgainnormalize */
348
349 /* misc */
350 SCIP_Bool checkedvarlocks; /**< whether variables contained in a single constraint have been already considered */
351 SCIP_HASHMAP* var2expr; /**< hashmap to map SCIP variables to variable-expressions */
352 int newsoleventfilterpos; /**< filter position of new solution event handler, if caught */
353};
354
355/** branching candidate with various scores */
356typedef struct
357{
358 SCIP_EXPR* expr; /**< expression that holds branching candidate, NULL if candidate is due to fractionality of integer variable */
359 SCIP_VAR* var; /**< variable that is branching candidate */
360 SCIP_Real auxviol; /**< aux-violation score of candidate */
361 SCIP_Real domain; /**< domain score of candidate */
362 SCIP_Real dual; /**< dual score of candidate */
363 SCIP_Real pscost; /**< pseudo-cost score of candidate */
364 SCIP_Real vartype; /**< variable type score of candidate */
365 SCIP_Real fractionality; /**< fractionality score of candidate */
366 SCIP_Real weighted; /**< weighted sum of other scores, see scoreBranchingCandidates() */
367} BRANCHCAND;
368
369/*
370 * Local methods
371 */
372
373/* forward declaration */
374static
376 SCIP* scip, /**< SCIP data structure */
377 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
378 SCIP_EXPR* rootexpr, /**< expression */
379 SCIP_Bool tightenauxvars, /**< should the bounds of auxiliary variables be tightened? */
380 SCIP_Bool* infeasible, /**< buffer to store whether the problem is infeasible (NULL if not needed) */
381 int* ntightenings /**< buffer to store the number of auxiliary variable tightenings (NULL if not needed) */
382 );
383
384/** frees auxiliary variables of expression, if any */
385static
387 SCIP* scip, /**< SCIP data structure */
388 SCIP_EXPR* expr /**< expression which auxvar to free, if any */
389 )
390{
391 SCIP_EXPR_OWNERDATA* mydata;
392
393 assert(scip != NULL);
394 assert(expr != NULL);
395
396 mydata = SCIPexprGetOwnerData(expr);
397 assert(mydata != NULL);
398
399 if( mydata->auxvar == NULL )
400 return SCIP_OKAY;
401
402 SCIPdebugMsg(scip, "remove auxiliary variable <%s> for expression %p\n", SCIPvarGetName(mydata->auxvar), (void*)expr);
403
404 /* remove variable locks
405 * as this is a relaxation-only variable, no other plugin should use it for deducing any type of reductions or cutting planes
406 */
407 SCIP_CALL( SCIPaddVarLocks(scip, mydata->auxvar, -1, -1) );
408
409 /* release auxiliary variable */
410 SCIP_CALL( SCIPreleaseVar(scip, &mydata->auxvar) );
411 assert(mydata->auxvar == NULL);
412
413 return SCIP_OKAY;
414}
415
416/** frees data used for enforcement of expression, that is, nonlinear handlers
417 *
418 * can also clear indicators whether expr needs enforcement methods, that is,
419 * free an associated auxiliary variable and reset the nactivityuses counts
420 */
421static
423 SCIP* scip, /**< SCIP data structure */
424 SCIP_EXPR* expr, /**< expression whose enforcement data will be released */
425 SCIP_Bool freeauxvar /**< whether aux var should be released and activity usage counts be reset */
426 )
427{
428 SCIP_EXPR_OWNERDATA* mydata;
429 int e;
430
431 mydata = SCIPexprGetOwnerData(expr);
432 assert(mydata != NULL);
433
434 if( freeauxvar )
435 {
436 /* free auxiliary variable */
437 SCIP_CALL( freeAuxVar(scip, expr) );
438 assert(mydata->auxvar == NULL);
439
440 /* reset count on activity and auxvar usage */
441 mydata->nactivityusesprop = 0;
442 mydata->nactivityusessepa = 0;
443 mydata->nauxvaruses = 0;
444 }
445
446 /* free data stored by nonlinear handlers */
447 for( e = 0; e < mydata->nenfos; ++e )
448 {
449 SCIP_NLHDLR* nlhdlr;
450
451 assert(mydata->enfos[e] != NULL);
452
453 nlhdlr = mydata->enfos[e]->nlhdlr;
454 assert(nlhdlr != NULL);
455
456 if( mydata->enfos[e]->issepainit )
457 {
458 /* call the separation deinitialization callback of the nonlinear handler */
459 SCIP_CALL( SCIPnlhdlrExitsepa(scip, nlhdlr, expr, mydata->enfos[e]->nlhdlrexprdata) );
460 mydata->enfos[e]->issepainit = FALSE;
461 }
462
463 /* free nlhdlr exprdata, if there is any and there is a method to free this data */
464 if( mydata->enfos[e]->nlhdlrexprdata != NULL )
465 {
466 SCIP_CALL( SCIPnlhdlrFreeexprdata(scip, nlhdlr, expr, &mydata->enfos[e]->nlhdlrexprdata) );
467 assert(mydata->enfos[e]->nlhdlrexprdata == NULL);
468 }
469
470 /* free enfo data */
471 SCIPfreeBlockMemory(scip, &mydata->enfos[e]);
472 }
473
474 /* free array with enfo data */
475 SCIPfreeBlockMemoryArrayNull(scip, &mydata->enfos, mydata->nenfos);
476
477 /* we need to look at this expression in detect again */
478 mydata->nenfos = -1;
479
480 return SCIP_OKAY;
481}
482
483/** callback that frees data that this conshdlr stored in an expression */
484static
486{
487 assert(scip != NULL);
488 assert(expr != NULL);
489 assert(ownerdata != NULL);
490 assert(*ownerdata != NULL);
491
492 /* expression should not be locked anymore */
493 assert((*ownerdata)->nlockspos == 0);
494 assert((*ownerdata)->nlocksneg == 0);
495
496 SCIP_CALL( freeEnfoData(scip, expr, TRUE) );
497
498 /* expression should not be enforced anymore */
499 assert((*ownerdata)->nenfos <= 0);
500 assert((*ownerdata)->auxvar == NULL);
501
502 if( SCIPisExprVar(scip, expr) )
503 {
504 SCIP_CONSHDLRDATA* conshdlrdata;
505 SCIP_VAR* var;
506
507 /* there should be no constraints left that still use this variable */
508 assert((*ownerdata)->nconss == 0);
509 /* thus, there should also be no variable event catched (via this exprhdlr) */
510 assert((*ownerdata)->filterpos == -1);
511
512 SCIPfreeBlockMemoryArrayNull(scip, &(*ownerdata)->conss, (*ownerdata)->consssize);
513
514 /* update var2expr hashmap in conshdlrdata */
515 conshdlrdata = SCIPconshdlrGetData((*ownerdata)->conshdlr);
516 assert(conshdlrdata != NULL);
517
518 var = SCIPgetVarExprVar(expr);
519 assert(var != NULL);
520
521 /* remove var -> expr map from hashmap if present
522 * (if no variable-expression stored for var hashmap, then the var hasn't been used in any constraint, so do nothing
523 * if variable-expression stored for var is different, then also do nothing)
524 */
525 if( SCIPhashmapGetImage(conshdlrdata->var2expr, var) == (void*)expr )
526 {
527 SCIP_CALL( SCIPhashmapRemove(conshdlrdata->var2expr, var) );
528 }
529 }
530
531 SCIPfreeBlockMemory(scip, ownerdata);
532
533 return SCIP_OKAY;
534}
535
536static
538{ /*lint --e{715}*/
539 assert(ownerdata != NULL);
540
541 /* print nl handlers associated to expr */
542 if( ownerdata->nenfos > 0 )
543 {
544 int i;
545 SCIPinfoMessage(scip, file, " {");
546
547 for( i = 0; i < ownerdata->nenfos; ++i )
548 {
549 SCIPinfoMessage(scip, file, "%s:", SCIPnlhdlrGetName(ownerdata->enfos[i]->nlhdlr));
550 if( ownerdata->enfos[i]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_ACTIVITY )
551 SCIPinfoMessage(scip, file, "a");
552 if( ownerdata->enfos[i]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPABELOW )
553 SCIPinfoMessage(scip, file, "u");
554 if( ownerdata->enfos[i]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPAABOVE )
555 SCIPinfoMessage(scip, file, "o");
556 if( i < ownerdata->nenfos-1 )
557 SCIPinfoMessage(scip, file, ", ");
558 }
559
560 SCIPinfoMessage(scip, file, "}");
561 }
562
563 /* print aux var associated to expr */
564 if( ownerdata->auxvar != NULL )
565 {
566 SCIPinfoMessage(scip, file, " (<%s> in [%g, %g])", SCIPvarGetName(ownerdata->auxvar), SCIPvarGetLbLocal(ownerdata->auxvar), SCIPvarGetUbLocal(ownerdata->auxvar));
567 }
568 SCIPinfoMessage(scip, file, "\n");
569
570 return SCIP_OKAY;
571}
572
573/** possibly reevaluates and then returns the activity of the expression
574 *
575 * Reevaluate activity if currently stored is not up to date (some bound was changed since last evaluation).
576 */
577static
579{
580 SCIP_CONSHDLRDATA* conshdlrdata;
581
582 assert(scip != NULL);
583 assert(expr != NULL);
584 assert(ownerdata != NULL);
585
586 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr);
587 assert(conshdlrdata != NULL);
588
589 if( SCIPexprGetActivityTag(expr) < conshdlrdata->curboundstag )
590 {
591 /* update activity of expression */
592 SCIP_CALL( forwardPropExpr(scip, ownerdata->conshdlr, expr, FALSE, NULL, NULL) );
593
594 assert(SCIPexprGetActivityTag(expr) == conshdlrdata->curboundstag);
595 }
596
597 return SCIP_OKAY;
598}
599
600/** callback that creates data that this conshdlr wants to store in an expression */
601static
603{
604 assert(scip != NULL);
605 assert(expr != NULL);
606 assert(ownerdata != NULL);
607
609 (*ownerdata)->nenfos = -1;
610 (*ownerdata)->conshdlr = (SCIP_CONSHDLR*)ownercreatedata;
611
612 if( SCIPisExprVar(scip, expr) )
613 {
614 SCIP_CONSHDLRDATA* conshdlrdata;
615 SCIP_VAR* var;
616
617 (*ownerdata)->filterpos = -1;
618
619 /* add to var2expr hashmap if not having expr for var yet */
620
621 conshdlrdata = SCIPconshdlrGetData((*ownerdata)->conshdlr);
622 assert(conshdlrdata != NULL);
623
624 var = SCIPgetVarExprVar(expr);
625
626 if( !SCIPhashmapExists(conshdlrdata->var2expr, (void*)var) )
627 {
628 /* store the variable expression in the hashmap */
629 SCIP_CALL( SCIPhashmapInsert(conshdlrdata->var2expr, (void*)var, (void*)expr) );
630 }
631 else
632 {
633 /* if expr was just created, then it shouldn't already be stored as image of var */
634 assert(SCIPhashmapGetImage(conshdlrdata->var2expr, (void*)var) != (void*)expr);
635 }
636 }
637 else
638 {
639 /* just so that we can use filterpos to recognize whether an expr is a varexpr if not having a SCIP pointer around */
640 (*ownerdata)->filterpos = -2;
641 }
642
643 *ownerfree = exprownerFree;
644 *ownerprint = exprownerPrint;
645 *ownerevalactivity = exprownerEvalactivity;
646
647 return SCIP_OKAY;
648}
649
650/** creates a variable expression or retrieves from hashmap in conshdlr data */
651static
653 SCIP* scip, /**< SCIP data structure */
654 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */
655 SCIP_EXPR** expr, /**< pointer where to store expression */
656 SCIP_VAR* var /**< variable to be stored */
657 )
658{
659 assert(conshdlr != NULL);
660 assert(expr != NULL);
661 assert(var != NULL);
662
663 /* get variable expression representing the given variable if there is one already */
664 *expr = (SCIP_EXPR*) SCIPhashmapGetImage(SCIPconshdlrGetData(conshdlr)->var2expr, (void*) var);
665
666 if( *expr == NULL )
667 {
668 /* create a new variable expression; this also captures the expression */
669 SCIP_CALL( SCIPcreateExprVar(scip, expr, var, exprownerCreate, (void*)conshdlr) );
670 assert(*expr != NULL);
671 /* exprownerCreate should have added var->expr to var2expr */
672 assert(SCIPhashmapGetImage(SCIPconshdlrGetData(conshdlr)->var2expr, (void*)var) == (void*)*expr);
673 }
674 else
675 {
676 /* only capture already existing expr to get a consistent uses-count */
677 SCIPcaptureExpr(*expr);
678 }
679
680 return SCIP_OKAY;
681}
682
683/* map var exprs to var-expr from var2expr hashmap */
684static
686{ /*lint --e{715}*/
687 SCIP_CONSHDLR* conshdlr = (SCIP_CONSHDLR*)mapexprdata;
688
689 assert(sourcescip != NULL);
690 assert(targetscip != NULL);
691 assert(sourceexpr != NULL);
692 assert(targetexpr != NULL);
693 assert(*targetexpr == NULL);
694 assert(mapexprdata != NULL);
695
696 /* do not provide map if not variable */
697 if( !SCIPisExprVar(sourcescip, sourceexpr) )
698 return SCIP_OKAY;
699
700 SCIP_CALL( createExprVar(targetscip, conshdlr, targetexpr, SCIPgetVarExprVar(sourceexpr)) );
701
702 return SCIP_OKAY;
703}
704
705/* map var exprs to var-expr from var2expr hashmap corresponding to transformed var */
706static
708{ /*lint --e{715}*/
709 SCIP_CONSHDLR* conshdlr = (SCIP_CONSHDLR*)mapexprdata;
710 SCIP_VAR* var;
711
712 assert(sourcescip != NULL);
713 assert(targetscip != NULL);
714 assert(sourceexpr != NULL);
715 assert(targetexpr != NULL);
716 assert(*targetexpr == NULL);
717 assert(mapexprdata != NULL);
718
719 /* do not provide map if not variable */
720 if( !SCIPisExprVar(sourcescip, sourceexpr) )
721 return SCIP_OKAY;
722
723 var = SCIPgetVarExprVar(sourceexpr);
724 assert(var != NULL);
725
726 /* transform variable */
727 SCIP_CALL( SCIPgetTransformedVar(sourcescip, var, &var) );
728 assert(var != NULL);
729
730 SCIP_CALL( createExprVar(targetscip, conshdlr, targetexpr, var) );
731
732 return SCIP_OKAY;
733}
734
735/** stores all variable expressions into a given constraint */
736static
738 SCIP* scip, /**< SCIP data structure */
739 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
740 SCIP_CONSDATA* consdata /**< constraint data */
741 )
742{
743 SCIP_CONSHDLRDATA* conshdlrdata;
744 int varexprssize;
745 int i;
746
747 assert(consdata != NULL);
748
749 /* skip if we have stored the variable expressions already */
750 if( consdata->varexprs != NULL )
751 return SCIP_OKAY;
752
753 assert(consdata->varexprs == NULL);
754 assert(consdata->nvarexprs == 0);
755
756 /* get an upper bound on number of variable expressions */
757 if( consdata->issimplified )
758 {
759 /* if simplified, then we should have removed inactive variables and replaced common subexpressions,
760 * so we cannot have more variable expression than the number of active variables
761 */
762 varexprssize = SCIPgetNVars(scip);
763 }
764 else
765 {
766 SCIP_CALL( SCIPgetExprNVars(scip, consdata->expr, &varexprssize) );
767 }
768
769 /* create array to store all variable expressions */
770 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consdata->varexprs, varexprssize) );
771
772 SCIP_CALL( SCIPgetExprVarExprs(scip, consdata->expr, consdata->varexprs, &(consdata->nvarexprs)) );
773 assert(varexprssize >= consdata->nvarexprs);
774
775 /* shrink array if there are less variables in the expression than in the problem */
776 if( varexprssize > consdata->nvarexprs )
777 {
778 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->varexprs, varexprssize, consdata->nvarexprs) );
779 }
780
781 conshdlrdata = SCIPconshdlrGetData(conshdlr);
782 assert(conshdlrdata != NULL);
783 assert(conshdlrdata->var2expr != NULL);
784
785 /* ensure that for every variable an entry exists in the var2expr hashmap
786 * when removing duplicate subexpressions it can happen that a var->varexpr map was removed from the hashmap
787 */
788 for( i = 0; i < consdata->nvarexprs; ++i )
789 {
790 if( !SCIPhashmapExists(conshdlrdata->var2expr, SCIPgetVarExprVar(consdata->varexprs[i])) )
791 {
792 SCIP_CALL( SCIPhashmapInsert(conshdlrdata->var2expr, SCIPgetVarExprVar(consdata->varexprs[i]), consdata->varexprs[i]) );
793 }
794 }
795
796 return SCIP_OKAY;
797}
798
799/** frees all variable expression stored in storeVarExprs() */
800static
802 SCIP* scip, /**< SCIP data structure */
803 SCIP_CONSDATA* consdata /**< constraint data */
804 )
805{
806 int i;
807
808 assert(consdata != NULL);
809
810 /* skip if we have stored the variable expressions already */
811 if( consdata->varexprs == NULL )
812 return SCIP_OKAY;
813
814 assert(consdata->varexprs != NULL);
815 assert(consdata->nvarexprs >= 0);
816 assert(!consdata->catchedevents);
817
818 /* release variable expressions */
819 for( i = 0; i < consdata->nvarexprs; ++i )
820 {
821 assert(consdata->varexprs[i] != NULL);
822 SCIP_CALL( SCIPreleaseExpr(scip, &consdata->varexprs[i]) );
823 assert(consdata->varexprs[i] == NULL);
824 }
825
826 /* free variable expressions */
827 SCIPfreeBlockMemoryArrayNull(scip, &consdata->varexprs, consdata->nvarexprs);
828 consdata->varexprs = NULL;
829 consdata->nvarexprs = 0;
830
831 return SCIP_OKAY;
832}
833
834/** interval evaluation of variables as used in bound tightening
835 *
836 * Returns slightly relaxed local variable bounds of a variable as interval.
837 * Does not relax beyond integer values, thus does not relax bounds on integer variables at all.
838 */
839static
840SCIP_DECL_EXPR_INTEVALVAR(intEvalVarBoundTightening)
841{
842 SCIP_INTERVAL interval;
843 SCIP_CONSHDLRDATA* conshdlrdata;
844 SCIP_Real lb;
845 SCIP_Real ub;
846
847 assert(scip != NULL);
848 assert(var != NULL);
849
850 conshdlrdata = (SCIP_CONSHDLRDATA*)intevalvardata;
851 assert(conshdlrdata != NULL);
852
853 if( conshdlrdata->globalbounds )
854 {
855 lb = SCIPvarGetLbGlobal(var);
856 ub = SCIPvarGetUbGlobal(var);
857 }
858 else
859 {
860 lb = SCIPvarGetLbLocal(var);
861 ub = SCIPvarGetUbLocal(var);
862 }
863 assert(lb <= ub); /* SCIP should ensure that variable bounds are not contradicting */
864
865 /* implicit integer variables may have non-integer bounds, apparently (run space25a) */
867 {
868 lb = EPSROUND(lb, 0.0); /*lint !e835*/
869 ub = EPSROUND(ub, 0.0); /*lint !e835*/
870 }
871
872 /* integer variables should always have integral bounds in SCIP */
873 assert(EPSFRAC(lb, 0.0) == 0.0 || !SCIPvarIsIntegral(var)); /*lint !e835*/
874 assert(EPSFRAC(ub, 0.0) == 0.0 || !SCIPvarIsIntegral(var)); /*lint !e835*/
875
876 switch( conshdlrdata->varboundrelax )
877 {
878 case 'n' : /* no relaxation */
879 break;
880
881 case 'a' : /* relax by absolute value */
882 {
883 /* do not look at integer variables, they already have integral bounds, so wouldn't be relaxed */
884 if( SCIPvarIsIntegral(var) )
885 break;
886
887 if( !SCIPisInfinity(scip, -lb) )
888 {
889 /* reduce lb by epsilon, or to the next integer value, which ever is larger */
890 SCIP_Real bnd = floor(lb);
891 lb = MAX(bnd, lb - conshdlrdata->varboundrelaxamount);
892 }
893
894 if( !SCIPisInfinity(scip, ub) )
895 {
896 /* increase ub by epsilon, or to the next integer value, which ever is smaller */
897 SCIP_Real bnd = ceil(ub);
898 ub = MIN(bnd, ub + conshdlrdata->varboundrelaxamount);
899 }
900
901 break;
902 }
903
904 case 'b' : /* relax always by absolute value */
905 {
906 /* do not look at integer variables, they already have integral bounds, so wouldn't be relaxed */
907 if( SCIPvarIsIntegral(var) )
908 break;
909
910 if( !SCIPisInfinity(scip, -lb) )
911 lb -= conshdlrdata->varboundrelaxamount;
912
913 if( !SCIPisInfinity(scip, ub) )
914 ub += conshdlrdata->varboundrelaxamount;
915
916 break;
917 }
918
919 case 'r' : /* relax by relative value */
920 {
921 /* do not look at integer variables, they already have integral bounds, so wouldn't be relaxed */
922 if( SCIPvarIsIntegral(var) )
923 break;
924
925 /* relax bounds by epsilon*max(1,|bnd|), instead of just epsilon as in case 'a', thus we trust the first log(epsilon) digits
926 * however, when domains get small, relaxing can excessively weaken bound tightening, thus do only fraction of |ub-lb| if that is smaller
927 * further, do not relax beyond next integer value
928 */
929 if( !SCIPisInfinity(scip, -lb) )
930 {
931 SCIP_Real bnd = floor(lb);
932 lb = MAX(bnd, lb - MIN(conshdlrdata->varboundrelaxamount * MAX(1.0, REALABS(lb)), 0.001 * REALABS(ub-lb)));
933 }
934
935 if( !SCIPisInfinity(scip, ub) )
936 {
937 SCIP_Real bnd = ceil(ub);
938 ub = MIN(bnd, ub + MIN(conshdlrdata->varboundrelaxamount * MAX(1.0, REALABS(ub)), 0.001 * REALABS(ub-lb)));
939 }
940
941 break;
942 }
943
944 default :
945 {
946 SCIPerrorMessage("Unsupported value '%c' for varboundrelax option.\n", conshdlrdata->varboundrelax);
947 SCIPABORT();
948 break;
949 }
950 }
951
952 /* convert SCIPinfinity() to SCIP_INTERVAL_INFINITY */
955 assert(lb <= ub);
956
957 SCIPintervalSetBounds(&interval, lb, ub);
958
959 return interval;
960}
961
962/** compares two nonlinear constraints by its index
963 *
964 * Usable as compare operator in array sort functions.
965 */
966static
967SCIP_DECL_SORTPTRCOMP(compIndexConsNonlinear)
968{
969 SCIP_CONSDATA* consdata1 = SCIPconsGetData((SCIP_CONS*)elem1);
970 SCIP_CONSDATA* consdata2 = SCIPconsGetData((SCIP_CONS*)elem2);
971
972 assert(consdata1 != NULL);
973 assert(consdata2 != NULL);
974
975 return consdata1->consindex - consdata2->consindex;
976}
977
978/** processes variable fixing or bound change event */
979static
980SCIP_DECL_EVENTEXEC(processVarEvent)
981{ /*lint --e{715}*/
982 SCIP_EVENTTYPE eventtype;
983 SCIP_EXPR* expr;
984 SCIP_EXPR_OWNERDATA* ownerdata;
985 SCIP_Bool boundtightened = FALSE;
986
987 eventtype = SCIPeventGetType(event);
989
990 assert(eventdata != NULL);
991 expr = (SCIP_EXPR*) eventdata;
992 assert(SCIPisExprVar(scip, expr));
993
994 SCIPdebugMsg(scip, " exec event %" SCIP_EVENTTYPE_FORMAT " for variable <%s> (local [%g,%g], global [%g,%g])\n", eventtype,
998
999 ownerdata = SCIPexprGetOwnerData(expr);
1000 assert(ownerdata != NULL);
1001 /* we only catch varevents for variables in constraints, so there should be constraints */
1002 assert(ownerdata->nconss > 0);
1003 assert(ownerdata->conss != NULL);
1004
1005 if( eventtype & SCIP_EVENTTYPE_BOUNDTIGHTENED )
1006 boundtightened = TRUE;
1007
1008 /* usually, if fixing a variable results in a boundchange, we should have seen a boundtightened-event as well
1009 * however, if the boundchange is smaller than epsilon, such an event will be omitted
1010 * but we still want to make sure the activity of the var-expr is reevaluated (mainly to avoid a failing assert) in this case
1011 * since we cannot easily see whether a variable bound was actually changed in a varfixed event, we treat any varfixed event
1012 * as a boundtightening (and usually it is, I would think)
1013 */
1014 if( eventtype & SCIP_EVENTTYPE_VARFIXED )
1015 boundtightened = TRUE;
1016
1017 /* if a variable is changed to implicit-integer and has a fractional bound, then the behavior of intEvalVarBoundTightening is changing,
1018 * because we will round the bounds and no longer consider relaxing them
1019 * we will mark corresponding constraints as not-propagated in this case to get the tightened bounds on the var-expr
1020 * (mainly to avoid a failing assert, see github issue #70)
1021 * 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
1022 */
1023 if( (eventtype & SCIP_EVENTTYPE_TYPECHANGED) && (SCIPeventGetNewtype(event) == SCIP_VARTYPE_IMPLINT) &&
1024 (!EPSISINT(SCIPvarGetLbGlobal(SCIPeventGetVar(event)), 0.0) || !EPSISINT(SCIPvarGetUbGlobal(SCIPeventGetVar(event)), 0.0)) ) /*lint !e835*/
1025 boundtightened = TRUE;
1026
1027 /* notify constraints that use this variable expression (expr) to repropagate and possibly resimplify
1028 * - propagation can only find something new if a bound was tightened
1029 * - simplify can only find something new if a var is fixed (or maybe a bound is tightened)
1030 * and we look at global changes (that is, we are not looking at boundchanges in probing)
1031 */
1032 if( boundtightened )
1033 {
1034 SCIP_CONSDATA* consdata;
1035 int c;
1036
1037 for( c = 0; c < ownerdata->nconss; ++c )
1038 {
1039 assert(ownerdata->conss[c] != NULL);
1040 consdata = SCIPconsGetData(ownerdata->conss[c]);
1041
1042 /* if bound tightening, then mark constraints to be propagated again
1043 * TODO we could try be more selective here and only trigger a propagation if a relevant bound has changed,
1044 * that is, we don't need to repropagate x + ... <= rhs if only the upper bound of x has been tightened
1045 * the locks don't help since they are not available separately for each constraint
1046 */
1047 consdata->ispropagated = FALSE;
1048 SCIPdebugMsg(scip, " marked <%s> for propagate\n", SCIPconsGetName(ownerdata->conss[c]));
1049
1050 /* if still in presolve (but not probing), then mark constraints to be unsimplified */
1052 {
1053 consdata->issimplified = FALSE;
1054 SCIPdebugMsg(scip, " marked <%s> for simplify\n", SCIPconsGetName(ownerdata->conss[c]));
1055 }
1056 }
1057 }
1058
1059 /* update curboundstag, lastboundrelax, and expr activity */
1060 if( (eventtype & SCIP_EVENTTYPE_BOUNDCHANGED) || boundtightened )
1061 {
1062 SCIP_CONSHDLRDATA* conshdlrdata;
1063 SCIP_INTERVAL activity;
1064
1065 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr);
1066 assert(conshdlrdata != NULL);
1067
1068 /* increase tag on bounds */
1069 ++conshdlrdata->curboundstag;
1070 assert(conshdlrdata->curboundstag > 0);
1071
1072 /* remember also if we relaxed bounds now */
1073 if( eventtype & SCIP_EVENTTYPE_BOUNDRELAXED )
1074 conshdlrdata->lastboundrelax = conshdlrdata->curboundstag;
1075
1076 /* update the activity of the var-expr here immediately
1077 * (we could call expr->activity = intevalvar(var, consdhlr) directly, but then the exprhdlr statistics are not updated)
1078 */
1079 SCIP_CALL( SCIPcallExprInteval(scip, expr, &activity, conshdlrdata->intevalvar, conshdlrdata) );
1080 /* activity = conshdlrdata->intevalvar(scip, SCIPgetVarExprVar(expr), conshdlrdata); */
1081#ifdef DEBUG_PROP
1082 SCIPdebugMsg(scip, " var-exprhdlr::inteval = [%.20g, %.20g]\n", activity.inf, activity.sup);
1083#endif
1084 SCIPexprSetActivity(expr, activity, conshdlrdata->curboundstag);
1085 }
1086
1087 return SCIP_OKAY;
1088}
1089
1090/** registers event handler to catch variable events on variable
1091 *
1092 * Additionally, the given constraint is stored in the ownerdata of the variable-expression.
1093 * When an event occurs, all stored constraints are notified.
1094 */
1095static
1097 SCIP* scip, /**< SCIP data structure */
1098 SCIP_EVENTHDLR* eventhdlr, /**< event handler */
1099 SCIP_EXPR* expr, /**< variable expression */
1100 SCIP_CONS* cons /**< nonlinear constraint */
1101 )
1102{
1103 SCIP_EXPR_OWNERDATA* ownerdata;
1104
1105 assert(eventhdlr != NULL);
1106 assert(expr != NULL);
1107 assert(SCIPisExprVar(scip, expr));
1108 assert(cons != NULL);
1109
1110 ownerdata = SCIPexprGetOwnerData(expr);
1111 assert(ownerdata != NULL);
1112
1113#ifndef NDEBUG
1114 /* assert that constraint does not double-catch variable */
1115 {
1116 int i;
1117 for( i = 0; i < ownerdata->nconss; ++i )
1118 assert(ownerdata->conss[i] != cons);
1119 }
1120#endif
1121
1122 /* append cons to ownerdata->conss */
1123 SCIP_CALL( SCIPensureBlockMemoryArray(scip, &ownerdata->conss, &ownerdata->consssize, ownerdata->nconss + 1) );
1124 ownerdata->conss[ownerdata->nconss++] = cons;
1125 /* we're not capturing the constraint here to avoid circular references */
1126
1127 /* updated sorted flag */
1128 if( ownerdata->nconss <= 1 )
1129 ownerdata->consssorted = TRUE;
1130 else if( ownerdata->consssorted )
1131 ownerdata->consssorted = compIndexConsNonlinear(ownerdata->conss[ownerdata->nconss-2], ownerdata->conss[ownerdata->nconss-1]) > 0;
1132
1133 /* catch variable events, if not done so yet (first constraint) */
1134 if( ownerdata->filterpos < 0 )
1135 {
1136 SCIP_EVENTTYPE eventtype;
1137
1138 assert(ownerdata->nconss == 1);
1139
1141
1142 SCIP_CALL( SCIPcatchVarEvent(scip, SCIPgetVarExprVar(expr), eventtype, eventhdlr, (SCIP_EVENTDATA*)expr, &ownerdata->filterpos) );
1143 assert(ownerdata->filterpos >= 0);
1144 }
1145
1146 return SCIP_OKAY;
1147}
1148
1149/** catch variable events */
1150static
1152 SCIP* scip, /**< SCIP data structure */
1153 SCIP_EVENTHDLR* eventhdlr, /**< event handler */
1154 SCIP_CONS* cons /**< constraint for which to catch bound change events */
1155 )
1156{
1157 SCIP_CONSHDLRDATA* conshdlrdata;
1158 SCIP_CONSDATA* consdata;
1159 SCIP_EXPR* expr;
1160 int i;
1161
1162 assert(eventhdlr != NULL);
1163 assert(cons != NULL);
1164
1165 consdata = SCIPconsGetData(cons);
1166 assert(consdata != NULL);
1167 assert(consdata->varexprs != NULL);
1168 assert(consdata->nvarexprs >= 0);
1169
1170 /* check if we have catched variable events already */
1171 if( consdata->catchedevents )
1172 return SCIP_OKAY;
1173
1174 conshdlrdata = SCIPconshdlrGetData(SCIPconsGetHdlr(cons));
1175 assert(conshdlrdata != NULL);
1176#ifndef CR_API /* this assert may not work in unittests due to having this code compiled twice, #3543 */
1177 assert(conshdlrdata->intevalvar == intEvalVarBoundTightening);
1178#endif
1179
1180 SCIPdebugMsg(scip, "catchVarEvents for %s\n", SCIPconsGetName(cons));
1181
1182 for( i = 0; i < consdata->nvarexprs; ++i )
1183 {
1184 expr = consdata->varexprs[i];
1185
1186 assert(expr != NULL);
1187 assert(SCIPisExprVar(scip, expr));
1188
1189 SCIP_CALL( catchVarEvent(scip, eventhdlr, expr, cons) );
1190
1191 /* from now on, activity of var-expr will usually be updated in processVarEvent if variable bound is changing
1192 * since we just registered this eventhdlr, we should make sure that the activity is also up to date now
1193 */
1194 if( SCIPexprGetActivityTag(expr) < conshdlrdata->curboundstag )
1195 {
1196 SCIP_INTERVAL activity;
1197 SCIP_CALL( SCIPcallExprInteval(scip, expr, &activity, intEvalVarBoundTightening, conshdlrdata) );
1198 /* activity = intEvalVarBoundTightening(scip, SCIPgetVarExprVar(expr), conshdlrdata); */
1199 SCIPexprSetActivity(expr, activity, conshdlrdata->curboundstag);
1200#ifdef DEBUG_PROP
1201 SCIPdebugMsg(scip, "var-exprhdlr::inteval for var <%s> = [%.20g, %.20g]\n", SCIPvarGetName(SCIPgetVarExprVar(expr)), activity.inf, activity.sup);
1202#endif
1203 }
1204 }
1205
1206 consdata->catchedevents = TRUE;
1207
1208 return SCIP_OKAY;
1209}
1210
1211/** unregisters event handler to catch variable events on variable
1212 *
1213 * The given constraint is removed from the constraints array in the ownerdata of the variable-expression.
1214 * If this was the last constraint, then the event handler is unregistered for this variable.
1215 */
1216static
1218 SCIP* scip, /**< SCIP data structure */
1219 SCIP_EVENTHDLR* eventhdlr, /**< event handler */
1220 SCIP_EXPR* expr, /**< variable expression */
1221 SCIP_CONS* cons /**< expr constraint */
1222 )
1223{
1224 SCIP_EXPR_OWNERDATA* ownerdata;
1225 int pos;
1226
1227 assert(eventhdlr != NULL);
1228 assert(expr != NULL);
1229 assert(SCIPisExprVar(scip, expr));
1230 assert(cons != NULL);
1231
1232 ownerdata = SCIPexprGetOwnerData(expr);
1233 assert(ownerdata != NULL);
1234 assert(ownerdata->nconss > 0);
1235
1236 if( ownerdata->conss[ownerdata->nconss-1] == cons )
1237 {
1238 pos = ownerdata->nconss-1;
1239 }
1240 else
1241 {
1242 if( !ownerdata->consssorted )
1243 {
1244 SCIPsortPtr((void**)ownerdata->conss, compIndexConsNonlinear, ownerdata->nconss);
1245 ownerdata->consssorted = TRUE;
1246 }
1247
1248 if( !SCIPsortedvecFindPtr((void**)ownerdata->conss, compIndexConsNonlinear, cons, ownerdata->nconss, &pos) )
1249 {
1250 SCIPerrorMessage("Constraint <%s> not in constraint array of expression for variable <%s>\n", SCIPconsGetName(cons), SCIPvarGetName(SCIPgetVarExprVar(expr)));
1251 return SCIP_ERROR;
1252 }
1253 assert(pos >= 0 && pos < ownerdata->nconss);
1254 }
1255 assert(ownerdata->conss[pos] == cons);
1256
1257 /* move last constraint into position of removed constraint */
1258 if( pos < ownerdata->nconss-1 )
1259 {
1260 ownerdata->conss[pos] = ownerdata->conss[ownerdata->nconss-1];
1261 ownerdata->consssorted = FALSE;
1262 }
1263 --ownerdata->nconss;
1264
1265 /* drop variable events if that was the last constraint */
1266 if( ownerdata->nconss == 0 )
1267 {
1268 SCIP_EVENTTYPE eventtype;
1269
1270 assert(ownerdata->filterpos >= 0);
1271
1273
1274 SCIP_CALL( SCIPdropVarEvent(scip, SCIPgetVarExprVar(expr), eventtype, eventhdlr, (SCIP_EVENTDATA*)expr, ownerdata->filterpos) );
1275 ownerdata->filterpos = -1;
1276 }
1277
1278 return SCIP_OKAY;
1279}
1280
1281/** drop variable events */
1282static
1284 SCIP* scip, /**< SCIP data structure */
1285 SCIP_EVENTHDLR* eventhdlr, /**< event handler */
1286 SCIP_CONS* cons /**< constraint for which to drop bound change events */
1287 )
1288{
1289 SCIP_CONSDATA* consdata;
1290 int i;
1291
1292 assert(eventhdlr != NULL);
1293 assert(cons != NULL);
1294
1295 consdata = SCIPconsGetData(cons);
1296 assert(consdata != NULL);
1297
1298 /* check if we have catched variable events already */
1299 if( !consdata->catchedevents )
1300 return SCIP_OKAY;
1301
1302 assert(consdata->varexprs != NULL);
1303 assert(consdata->nvarexprs >= 0);
1304
1305 SCIPdebugMsg(scip, "dropVarEvents for %s\n", SCIPconsGetName(cons));
1306
1307 for( i = consdata->nvarexprs - 1; i >= 0; --i )
1308 {
1309 assert(consdata->varexprs[i] != NULL);
1310
1311 SCIP_CALL( dropVarEvent(scip, eventhdlr, consdata->varexprs[i], cons) );
1312 }
1313
1314 consdata->catchedevents = FALSE;
1315
1316 return SCIP_OKAY;
1317}
1318
1319/** creates and captures a nonlinear constraint
1320 *
1321 * @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
1322 */
1323static
1325 SCIP* scip, /**< SCIP data structure */
1326 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
1327 SCIP_CONS** cons, /**< pointer to hold the created constraint */
1328 const char* name, /**< name of constraint */
1329 SCIP_EXPR* expr, /**< expression of constraint (must not be NULL) */
1330 SCIP_Real lhs, /**< left hand side of constraint */
1331 SCIP_Real rhs, /**< right hand side of constraint */
1332 SCIP_Bool copyexpr, /**< whether to copy the expression or reuse the given expr (capture it) */
1333 SCIP_Bool initial, /**< should the LP relaxation of constraint be in the initial LP?
1334 * Usually set to TRUE. Set to FALSE for 'lazy constraints'. */
1335 SCIP_Bool separate, /**< should the constraint be separated during LP processing?
1336 * Usually set to TRUE. */
1337 SCIP_Bool enforce, /**< should the constraint be enforced during node processing?
1338 * TRUE for model constraints, FALSE for additional, redundant constraints. */
1339 SCIP_Bool check, /**< should the constraint be checked for feasibility?
1340 * TRUE for model constraints, FALSE for additional, redundant constraints. */
1341 SCIP_Bool propagate, /**< should the constraint be propagated during node processing?
1342 * Usually set to TRUE. */
1343 SCIP_Bool local, /**< is constraint only valid locally?
1344 * Usually set to FALSE. Has to be set to TRUE, e.g., for branching constraints. */
1345 SCIP_Bool modifiable, /**< is constraint modifiable (subject to column generation)?
1346 * Usually set to FALSE. In column generation applications, set to TRUE if pricing
1347 * adds coefficients to this constraint. */
1348 SCIP_Bool dynamic, /**< is constraint subject to aging?
1349 * Usually set to FALSE. Set to TRUE for own cuts which
1350 * are separated as constraints. */
1351 SCIP_Bool removable /**< should the relaxation be removed from the LP due to aging or cleanup?
1352 * Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */
1353 )
1354{
1355 SCIP_CONSHDLRDATA* conshdlrdata;
1356 SCIP_CONSDATA* consdata;
1357
1358 assert(conshdlr != NULL);
1359 assert(expr != NULL);
1360
1361 conshdlrdata = SCIPconshdlrGetData(conshdlr);
1362 assert(conshdlrdata != NULL);
1363
1364 if( local && SCIPgetDepth(scip) != 0 )
1365 {
1366 SCIPerrorMessage("Locally valid nonlinear constraints are not supported, yet.\n");
1367 return SCIP_INVALIDCALL;
1368 }
1369
1370 /* TODO we should allow for non-initial nonlinear constraints */
1371 if( !initial )
1372 {
1373 SCIPerrorMessage("Non-initial nonlinear constraints are not supported, yet.\n");
1374 return SCIP_INVALIDCALL;
1375 }
1376
1377 /* create constraint data */
1379
1380 if( copyexpr )
1381 {
1382 /* copy expression, thereby map variables expressions to already existing variables expressions in var2expr map, or augment var2expr map */
1383 SCIP_CALL( SCIPduplicateExpr(scip, expr, &consdata->expr, mapexprvar, conshdlr, exprownerCreate, (void*)conshdlr) );
1384 }
1385 else
1386 {
1387 consdata->expr = expr;
1388 SCIPcaptureExpr(consdata->expr);
1389 }
1390 consdata->lhs = lhs;
1391 consdata->rhs = rhs;
1392 consdata->consindex = conshdlrdata->lastconsindex++;
1393 consdata->curv = SCIP_EXPRCURV_UNKNOWN;
1394
1395 /* create constraint */
1396 SCIP_CALL( SCIPcreateCons(scip, cons, name, conshdlr, consdata, initial, separate, enforce, check, propagate,
1397 local, modifiable, dynamic, removable, FALSE) );
1398
1399 return SCIP_OKAY;
1400}
1401
1402/** returns absolute violation for auxvar relation in an expression w.r.t. original variables
1403 *
1404 * Assume the expression is f(x), where x are original (i.e., not auxiliary) variables.
1405 * Assume that f(x) is associated with auxiliary variable z.
1406 *
1407 * If there are negative locks, then return the violation of z &le; f(x) and sets `violover` to TRUE.
1408 * If there are positive locks, then return the violation of z &ge; f(x) and sets `violunder` to TRUE.
1409 * Of course, if there both negative and positive locks, then return the violation of z = f(x).
1410 * If f could not be evaluated, then return SCIPinfinity() and set both `violover` and `violunder` to TRUE.
1411 *
1412 * @note This does not reevaluate the violation, but assumes that the expression has been evaluated
1413 */
1414static
1416 SCIP* scip, /**< SCIP data structure */
1417 SCIP_EXPR* expr, /**< expression */
1418 SCIP_SOL* sol, /**< solution that has been evaluated */
1419 SCIP_Bool* violunder, /**< buffer to store whether z >= f(x) is violated, or NULL */
1420 SCIP_Bool* violover /**< buffer to store whether z <= f(x) is violated, or NULL */
1421 )
1422{
1423 SCIP_EXPR_OWNERDATA* ownerdata;
1424 SCIP_Real auxvarvalue;
1425
1426 assert(expr != NULL);
1427
1428 ownerdata = SCIPexprGetOwnerData(expr);
1429 assert(ownerdata != NULL);
1430 assert(ownerdata->auxvar != NULL);
1431
1432 if( SCIPexprGetEvalValue(expr) == SCIP_INVALID )
1433 {
1434 if( violunder != NULL )
1435 *violunder = TRUE;
1436 if( violover != NULL )
1437 *violover = TRUE;
1438 return SCIPinfinity(scip);
1439 }
1440
1441 auxvarvalue = SCIPgetSolVal(scip, sol, ownerdata->auxvar);
1442
1443 if( ownerdata->nlocksneg > 0 && auxvarvalue > SCIPexprGetEvalValue(expr) )
1444 {
1445 if( violunder != NULL )
1446 *violunder = FALSE;
1447 if( violover != NULL )
1448 *violover = TRUE;
1449 return auxvarvalue - SCIPexprGetEvalValue(expr);
1450 }
1451
1452 if( ownerdata->nlockspos > 0 && SCIPexprGetEvalValue(expr) > auxvarvalue )
1453 {
1454 if( violunder != NULL )
1455 *violunder = TRUE;
1456 if( violover != NULL )
1457 *violover = FALSE;
1458 return SCIPexprGetEvalValue(expr) - auxvarvalue;
1459 }
1460
1461 if( violunder != NULL )
1462 *violunder = FALSE;
1463 if( violover != NULL )
1464 *violover = FALSE;
1465 return 0.0;
1466}
1467
1468/** returns absolute violation for auxvar relation in an expression w.r.t. auxiliary variables
1469 *
1470 * Assume the expression is f(w), where w are auxiliary variables that were introduced by some nlhdlr.
1471 * Assume that f(w) is associated with auxiliary variable z.
1472 *
1473 * If there are negative locks, then return the violation of z &le; f(w) and sets `violover` to TRUE.
1474 * If there are positive locks, then return the violation of z &ge; f(w) and sets `violunder` to TRUE.
1475 * Of course, if there both negative and positive locks, then return the violation of z = f(w).
1476 * If f could not be evaluated, then return SCIPinfinity() and set both `violover` and `violunder` to TRUE.
1477 *
1478 * @note This does not reevaluate the violation, but assumes that f(w) is passed in with auxvalue.
1479 */
1480static
1482 SCIP* scip, /**< SCIP data structure */
1483 SCIP_EXPR* expr, /**< expression */
1484 SCIP_Real auxvalue, /**< value of f(w) */
1485 SCIP_SOL* sol, /**< solution that has been evaluated */
1486 SCIP_Bool* violunder, /**< buffer to store whether z >= f(w) is violated, or NULL */
1487 SCIP_Bool* violover /**< buffer to store whether z <= f(w) is violated, or NULL */
1488 )
1489{
1490 SCIP_EXPR_OWNERDATA* ownerdata;
1491 SCIP_Real auxvarvalue;
1492
1493 assert(expr != NULL);
1494
1495 ownerdata = SCIPexprGetOwnerData(expr);
1496 assert(ownerdata != NULL);
1497 assert(ownerdata->auxvar != NULL);
1498
1499 if( auxvalue == SCIP_INVALID )
1500 {
1501 if( violunder != NULL )
1502 *violunder = TRUE;
1503 if( violover != NULL )
1504 *violover = TRUE;
1505 return SCIPinfinity(scip);
1506 }
1507
1508 auxvarvalue = SCIPgetSolVal(scip, sol, ownerdata->auxvar);
1509
1510 if( ownerdata->nlocksneg > 0 && auxvarvalue > auxvalue )
1511 {
1512 if( violunder != NULL )
1513 *violunder = FALSE;
1514 if( violover != NULL )
1515 *violover = TRUE;
1516 return auxvarvalue - auxvalue;
1517 }
1518
1519 if( ownerdata->nlockspos > 0 && auxvalue > auxvarvalue )
1520 {
1521 if( violunder != NULL )
1522 *violunder = TRUE;
1523 if( violover != NULL )
1524 *violover = FALSE;
1525 return auxvalue - auxvarvalue;
1526 }
1527
1528 if( violunder != NULL )
1529 *violunder = FALSE;
1530 if( violover != NULL )
1531 *violover = FALSE;
1532
1533 return 0.0;
1534}
1535
1536/** computes violation of a constraint */
1537static
1539 SCIP* scip, /**< SCIP data structure */
1540 SCIP_CONS* cons, /**< constraint */
1541 SCIP_SOL* sol, /**< solution or NULL if LP solution should be used */
1542 SCIP_Longint soltag /**< tag that uniquely identifies the solution (with its values), or 0. */
1543 )
1544{
1545 SCIP_CONSDATA* consdata;
1546 SCIP_Real activity;
1547
1548 assert(scip != NULL);
1549 assert(cons != NULL);
1550
1551 consdata = SCIPconsGetData(cons);
1552 assert(consdata != NULL);
1553
1554 SCIP_CALL( SCIPevalExpr(scip, consdata->expr, sol, soltag) );
1555 activity = SCIPexprGetEvalValue(consdata->expr);
1556
1557 /* consider constraint as violated if it is undefined in the current point */
1558 if( activity == SCIP_INVALID )
1559 {
1560 consdata->lhsviol = SCIPinfinity(scip);
1561 consdata->rhsviol = SCIPinfinity(scip);
1562 return SCIP_OKAY;
1563 }
1564
1565 /* compute violations */
1566 consdata->lhsviol = SCIPisInfinity(scip, -consdata->lhs) ? -SCIPinfinity(scip) : consdata->lhs - activity;
1567 consdata->rhsviol = SCIPisInfinity(scip, consdata->rhs) ? -SCIPinfinity(scip) : activity - consdata->rhs;
1568
1569 return SCIP_OKAY;
1570}
1571
1572/** returns absolute violation of a constraint
1573 *
1574 * @note This does not reevaluate the violation, but assumes that computeViolation() has been called before.
1575 */
1576static
1578 SCIP_CONS* cons /**< constraint */
1579 )
1580{
1581 SCIP_CONSDATA* consdata;
1582
1583 assert(cons != NULL);
1584
1585 consdata = SCIPconsGetData(cons);
1586 assert(consdata != NULL);
1587
1588 return MAX3(0.0, consdata->lhsviol, consdata->rhsviol);
1589}
1590
1591/** computes relative violation of a constraint
1592 *
1593 * @note This does not reevaluate the violation, but assumes that computeViolation() has been called before.
1594 */
1595static
1597 SCIP* scip, /**< SCIP data structure */
1598 SCIP_CONS* cons, /**< constraint */
1599 SCIP_Real* viol, /**< buffer to store violation */
1600 SCIP_SOL* sol, /**< solution or NULL if LP solution should be used */
1601 SCIP_Longint soltag /**< tag that uniquely identifies the solution (with its values), or 0 */
1602 )
1603{
1604 SCIP_CONSHDLR* conshdlr;
1605 SCIP_CONSHDLRDATA* conshdlrdata;
1606 SCIP_CONSDATA* consdata;
1607 SCIP_Real scale;
1608
1609 assert(cons != NULL);
1610 assert(viol != NULL);
1611
1612 conshdlr = SCIPconsGetHdlr(cons);
1613 assert(conshdlr != NULL);
1614
1615 conshdlrdata = SCIPconshdlrGetData(conshdlr);
1616 assert(conshdlrdata != NULL);
1617
1618 *viol = getConsAbsViolation(cons);
1619
1620 if( conshdlrdata->violscale == 'n' )
1621 return SCIP_OKAY;
1622
1623 if( SCIPisInfinity(scip, *viol) )
1624 return SCIP_OKAY;
1625
1626 consdata = SCIPconsGetData(cons);
1627 assert(consdata != NULL);
1628
1629 if( conshdlrdata->violscale == 'a' )
1630 {
1631 scale = MAX(1.0, REALABS(SCIPexprGetEvalValue(consdata->expr)));
1632
1633 /* consider value of side that is violated for scaling, too */
1634 if( consdata->lhsviol > 0.0 && REALABS(consdata->lhs) > scale )
1635 {
1636 assert(!SCIPisInfinity(scip, -consdata->lhs));
1637 scale = REALABS(consdata->lhs);
1638 }
1639 else if( consdata->rhsviol > 0.0 && REALABS(consdata->rhs) > scale )
1640 {
1641 assert(!SCIPisInfinity(scip, consdata->rhs));
1642 scale = REALABS(consdata->rhs);
1643 }
1644
1645 *viol /= scale;
1646 return SCIP_OKAY;
1647 }
1648
1649 /* if not 'n' or 'a', then it has to be 'g' at the moment */
1650 assert(conshdlrdata->violscale == 'g');
1651 if( soltag == 0L || consdata->gradnormsoltag != soltag )
1652 {
1653 /* we need the varexprs to conveniently access the gradient */
1654 SCIP_CALL( storeVarExprs(scip, conshdlr, consdata) );
1655
1656 /* update cached value of norm of gradient */
1657 consdata->gradnorm = 0.0;
1658
1659 /* compute gradient */
1660 SCIP_CALL( SCIPevalExprGradient(scip, consdata->expr, sol, soltag) );
1661
1662 /* gradient evaluation error -> no scaling */
1663 if( SCIPexprGetDerivative(consdata->expr) != SCIP_INVALID )
1664 {
1665 int i;
1666 for( i = 0; i < consdata->nvarexprs; ++i )
1667 {
1668 SCIP_Real deriv;
1669
1670 assert(SCIPexprGetDiffTag(consdata->expr) == SCIPexprGetDiffTag(consdata->varexprs[i]));
1671 deriv = SCIPexprGetDerivative(consdata->varexprs[i]);
1672 if( deriv == SCIP_INVALID )
1673 {
1674 /* SCIPdebugMsg(scip, "gradient evaluation error for component %d\n", i); */
1675 consdata->gradnorm = 0.0;
1676 break;
1677 }
1678
1679 consdata->gradnorm += deriv*deriv;
1680 }
1681 }
1682 consdata->gradnorm = sqrt(consdata->gradnorm);
1683 consdata->gradnormsoltag = soltag;
1684 }
1685
1686 *viol /= MAX(1.0, consdata->gradnorm);
1687
1688 return SCIP_OKAY;
1689}
1690
1691/** returns whether constraint is currently violated
1692 *
1693 * @note This does not reevaluate the violation, but assumes that computeViolation() has been called before.
1694 */
1695static
1697 SCIP* scip, /**< SCIP data structure */
1698 SCIP_CONS* cons /**< constraint */
1699 )
1700{
1701 return getConsAbsViolation(cons) > SCIPfeastol(scip);
1702}
1703
1704/** checks for a linear variable that can be increased or decreased without harming feasibility */
1705static
1707 SCIP* scip, /**< SCIP data structure */
1708 SCIP_CONS* cons /**< constraint */
1709 )
1710{
1711 SCIP_CONSDATA* consdata;
1712 int poslock;
1713 int neglock;
1714 int i;
1715
1716 assert(cons != NULL);
1717
1718 consdata = SCIPconsGetData(cons);
1719 assert(consdata != NULL);
1720
1721 consdata->linvarincr = NULL;
1722 consdata->linvardecr = NULL;
1723 consdata->linvarincrcoef = 0.0;
1724 consdata->linvardecrcoef = 0.0;
1725
1726 /* root expression is not a sum -> no unlocked linear variable available */
1727 if( !SCIPisExprSum(scip, consdata->expr) )
1728 return;
1729
1730 for( i = 0; i < SCIPexprGetNChildren(consdata->expr); ++i )
1731 {
1732 SCIP_EXPR* child;
1733
1734 child = SCIPexprGetChildren(consdata->expr)[i];
1735 assert(child != NULL);
1736
1737 /* check whether the child is a variable expression */
1738 if( SCIPisExprVar(scip, child) )
1739 {
1740 SCIP_VAR* var = SCIPgetVarExprVar(child);
1741 SCIP_Real coef = SCIPgetCoefsExprSum(consdata->expr)[i];
1742
1743 if( coef > 0.0 )
1744 {
1745 poslock = !SCIPisInfinity(scip, consdata->rhs) ? 1 : 0;
1746 neglock = !SCIPisInfinity(scip, -consdata->lhs) ? 1 : 0;
1747 }
1748 else
1749 {
1750 poslock = !SCIPisInfinity(scip, -consdata->lhs) ? 1 : 0;
1751 neglock = !SCIPisInfinity(scip, consdata->rhs) ? 1 : 0;
1752 }
1754
1755 if( SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL) - neglock == 0 )
1756 {
1757 /* for a*x + f(y) \in [lhs, rhs], we can decrease x without harming other constraints
1758 * if we have already one candidate, then take the one where the loss in the objective function is less
1759 */
1760 if( (consdata->linvardecr == NULL) ||
1761 (SCIPvarGetObj(consdata->linvardecr) / consdata->linvardecrcoef > SCIPvarGetObj(var) / coef) )
1762 {
1763 consdata->linvardecr = var;
1764 consdata->linvardecrcoef = coef;
1765 }
1766 }
1767
1768 if( SCIPvarGetNLocksUpType(var, SCIP_LOCKTYPE_MODEL) - poslock == 0 )
1769 {
1770 /* for a*x + f(y) \in [lhs, rhs], we can increase x without harm
1771 * if we have already one candidate, then take the one where the loss in the objective function is less
1772 */
1773 if( (consdata->linvarincr == NULL) ||
1774 (SCIPvarGetObj(consdata->linvarincr) / consdata->linvarincrcoef > SCIPvarGetObj(var) / coef) )
1775 {
1776 consdata->linvarincr = var;
1777 consdata->linvarincrcoef = coef;
1778 }
1779 }
1780 }
1781 }
1782
1783 assert(consdata->linvarincr == NULL || consdata->linvarincrcoef != 0.0);
1784 assert(consdata->linvardecr == NULL || consdata->linvardecrcoef != 0.0);
1785
1786 if( consdata->linvarincr != NULL )
1787 {
1788 SCIPdebugMsg(scip, "may increase <%s> to become feasible\n", SCIPvarGetName(consdata->linvarincr));
1789 }
1790 if( consdata->linvardecr != NULL )
1791 {
1792 SCIPdebugMsg(scip, "may decrease <%s> to become feasible\n", SCIPvarGetName(consdata->linvardecr));
1793 }
1794}
1795
1796/** Given a solution where every nonlinear constraint is either feasible or can be made feasible by
1797 * moving a linear variable, construct the corresponding feasible solution and pass it to the trysol heuristic.
1798 *
1799 * The method assumes that this is always possible and that not all constraints are feasible already.
1800 */
1801static
1803 SCIP* scip, /**< SCIP data structure */
1804 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
1805 SCIP_CONS** conss, /**< constraints to process */
1806 int nconss, /**< number of constraints */
1807 SCIP_SOL* sol, /**< solution to process */
1808 SCIP_Bool* success /**< buffer to store whether we succeeded to construct a solution that satisfies all provided constraints */
1809 )
1810{
1811 SCIP_CONSHDLRDATA* conshdlrdata;
1812 SCIP_SOL* newsol;
1813 int c;
1814
1815 assert(scip != NULL);
1816 assert(conshdlr != NULL);
1817 assert(conss != NULL || nconss == 0);
1818 assert(success != NULL);
1819
1820 *success = FALSE;
1821
1822 /* don't propose new solutions if not in presolve or solving */
1824 return SCIP_OKAY;
1825
1826 conshdlrdata = SCIPconshdlrGetData(conshdlr);
1827 assert(conshdlrdata != NULL);
1828
1829 if( sol != NULL )
1830 {
1831 SCIP_CALL( SCIPcreateSolCopy(scip, &newsol, sol) );
1832 }
1833 else
1834 {
1835 SCIP_CALL( SCIPcreateLPSol(scip, &newsol, NULL) );
1836 }
1837 SCIP_CALL( SCIPunlinkSol(scip, newsol) );
1838 SCIPdebugMsg(scip, "attempt to make solution from <%s> feasible by shifting linear variable\n",
1839 sol != NULL ? (SCIPsolGetHeur(sol) != NULL ? SCIPheurGetName(SCIPsolGetHeur(sol)) : "tree") : "LP");
1840
1841 for( c = 0; c < nconss; ++c )
1842 {
1843 SCIP_CONSDATA* consdata = SCIPconsGetData(conss[c]); /*lint !e613*/
1844 SCIP_Real viol = 0.0;
1845 SCIP_Real delta;
1846 SCIP_Real gap;
1847
1848 assert(consdata != NULL);
1849
1850 /* get absolute violation and sign */
1851 if( consdata->lhsviol > SCIPfeastol(scip) )
1852 viol = consdata->lhsviol; /* lhs - activity */
1853 else if( consdata->rhsviol > SCIPfeastol(scip) )
1854 viol = -consdata->rhsviol; /* rhs - activity */
1855 else
1856 continue; /* constraint is satisfied */
1857
1858 if( consdata->linvarincr != NULL &&
1859 ((viol > 0.0 && consdata->linvarincrcoef > 0.0) || (viol < 0.0 && consdata->linvarincrcoef < 0.0)) )
1860 {
1861 SCIP_VAR* var = consdata->linvarincr;
1862
1863 /* compute how much we would like to increase var */
1864 delta = viol / consdata->linvarincrcoef;
1865 assert(delta > 0.0);
1866
1867 /* if var has an upper bound, may need to reduce delta */
1869 {
1870 gap = SCIPvarGetUbGlobal(var) - SCIPgetSolVal(scip, newsol, var);
1871 delta = MIN(MAX(0.0, gap), delta);
1872 }
1873 if( SCIPisPositive(scip, delta) )
1874 {
1875 /* if variable is integral, round delta up so that it will still have an integer value */
1876 if( SCIPvarIsIntegral(var) )
1877 delta = SCIPceil(scip, delta);
1878
1879 SCIP_CALL( SCIPincSolVal(scip, newsol, var, delta) );
1880 SCIPdebugMsg(scip, "increase <%s> by %g to %g to remedy lhs-violation %g of cons <%s>\n",
1881 SCIPvarGetName(var), delta, SCIPgetSolVal(scip, newsol, var), viol, SCIPconsGetName(conss[c])); /*lint !e613*/
1882
1883 /* adjust constraint violation, if satisfied go on to next constraint */
1884 viol -= consdata->linvarincrcoef * delta;
1885 if( SCIPisZero(scip, viol) )
1886 continue;
1887 }
1888 }
1889
1890 assert(viol != 0.0);
1891 if( consdata->linvardecr != NULL &&
1892 ((viol > 0.0 && consdata->linvardecrcoef < 0.0) || (viol < 0.0 && consdata->linvardecrcoef > 0.0)) )
1893 {
1894 SCIP_VAR* var = consdata->linvardecr;
1895
1896 /* compute how much we would like to decrease var */
1897 delta = viol / consdata->linvardecrcoef;
1898 assert(delta < 0.0);
1899
1900 /* if var has a lower bound, may need to reduce delta */
1902 {
1903 gap = SCIPgetSolVal(scip, newsol, var) - SCIPvarGetLbGlobal(var);
1904 delta = MAX(MIN(0.0, gap), delta);
1905 }
1906 if( SCIPisNegative(scip, delta) )
1907 {
1908 /* if variable is integral, round delta down so that it will still have an integer value */
1909 if( SCIPvarIsIntegral(var) )
1910 delta = SCIPfloor(scip, delta);
1911 SCIP_CALL( SCIPincSolVal(scip, newsol, consdata->linvardecr, delta) );
1912 /*lint --e{613} */
1913 SCIPdebugMsg(scip, "increase <%s> by %g to %g to remedy rhs-violation %g of cons <%s>\n",
1914 SCIPvarGetName(var), delta, SCIPgetSolVal(scip, newsol, var), viol, SCIPconsGetName(conss[c]));
1915
1916 /* adjust constraint violation, if satisfied go on to next constraint */
1917 viol -= consdata->linvardecrcoef * delta;
1918 if( SCIPisZero(scip, viol) )
1919 continue;
1920 }
1921 }
1922
1923 /* still here... so probably we could not make constraint feasible due to variable bounds, thus give up */
1924 break;
1925 }
1926
1927 /* if we have a solution that should satisfy all quadratic constraints and has a better objective than the current upper bound,
1928 * then pass it to the trysol heuristic
1929 */
1931 {
1932 SCIPdebugMsg(scip, "pass solution with objective val %g to trysol heuristic\n", SCIPgetSolTransObj(scip, newsol));
1933
1934 assert(conshdlrdata->trysolheur != NULL);
1935 SCIP_CALL( SCIPheurPassSolTrySol(scip, conshdlrdata->trysolheur, newsol) );
1936
1937 *success = TRUE;
1938 }
1939
1940 SCIP_CALL( SCIPfreeSol(scip, &newsol) );
1941
1942 return SCIP_OKAY;
1943}
1944
1945/** notify nonlinear handlers to add linearization in new solution that has been found
1946 *
1947 * The idea is that nonlinear handlers add globally valid tight estimators in a given solution as cuts to the cutpool.
1948 *
1949 * Essentially we want to ensure that the LP relaxation is tight in the new solution, if possible.
1950 * As the nonlinear handlers define the extended formulation, they should know whether it is possible to generate a
1951 * cut that is valid and supporting in the given solution.
1952 * For example, for convex constraints, we achieve this by linearizing.
1953 * For SOC, we also linearize, but on a a convex reformulation.
1954 *
1955 * Since linearization may happen in auxiliary variables, we ensure that auxiliary variables are set
1956 * to the eval-value of its expression, i.e., we change sol so it is also feasible in the extended formulation.
1957 */
1958static
1960 SCIP* scip, /**< SCIP data structure */
1961 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
1962 SCIP_CONS** conss, /**< constraints */
1963 int nconss, /**< number of constraints */
1964 SCIP_SOL* sol, /**< reference point where to estimate */
1965 SCIP_Bool solisbest /**< whether solution is best */
1966 )
1967{
1968 SCIP_CONSDATA* consdata;
1969 SCIP_Longint soltag;
1970 SCIP_EXPRITER* it;
1971 SCIP_EXPR* expr;
1972 int c, e;
1973
1974 assert(scip != NULL);
1975 assert(conshdlr != NULL);
1976 assert(conss != NULL || nconss == 0);
1977
1978 ENFOLOG( SCIPinfoMessage(scip, enfologfile, "call nlhdlr sollinearize in new solution from <%s>\n", SCIPheurGetName(SCIPsolGetHeur(sol))); )
1979
1980 /* TODO probably we just evaluated all expressions when checking the sol before it was added
1981 * would be nice to recognize this and skip reevaluating
1982 */
1983 soltag = SCIPgetExprNewSoltag(scip);
1984
1988
1989 for( c = 0; c < nconss; ++c )
1990 {
1991 /* skip constraints that are not enabled or deleted or have separation disabled */
1992 if( !SCIPconsIsEnabled(conss[c]) || SCIPconsIsDeleted(conss[c]) || !SCIPconsIsSeparationEnabled(conss[c]) )
1993 continue;
1994 assert(SCIPconsIsActive(conss[c]));
1995
1996 consdata = SCIPconsGetData(conss[c]);
1997 assert(consdata != NULL);
1998
1999 ENFOLOG(
2000 {
2001 int i;
2002 SCIPinfoMessage(scip, enfologfile, " constraint ");
2003 SCIP_CALL( SCIPprintCons(scip, conss[c], enfologfile) );
2004 SCIPinfoMessage(scip, enfologfile, "\n and point\n");
2005 for( i = 0; i < consdata->nvarexprs; ++i )
2006 {
2007 SCIP_VAR* var;
2008 var = SCIPgetVarExprVar(consdata->varexprs[i]);
2009 SCIPinfoMessage(scip, enfologfile, " %-10s = %15g bounds: [%15g,%15g]\n", SCIPvarGetName(var),
2010 SCIPgetSolVal(scip, sol, var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var));
2011 }
2012 })
2013
2014 SCIP_CALL( SCIPevalExpr(scip, consdata->expr, sol, soltag) );
2015 assert(SCIPexprGetEvalValue(consdata->expr) != SCIP_INVALID);
2016
2017 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
2018 {
2019 SCIP_EXPR_OWNERDATA* ownerdata;
2020
2021 ownerdata = SCIPexprGetOwnerData(expr);
2022 assert(ownerdata != NULL);
2023
2024 /* set value for auxvar in sol to value of expr, in case it is used to compute estimators higher up of this expression */
2025 assert(SCIPexprGetEvalTag(expr) == soltag);
2026 assert(SCIPexprGetEvalValue(expr) != SCIP_INVALID);
2027 if( ownerdata->auxvar != NULL )
2028 {
2029 SCIP_CALL( SCIPsetSolVal(scip, sol, ownerdata->auxvar, SCIPexprGetEvalValue(expr)) );
2030 }
2031
2032 /* let nonlinear handler generate cuts by calling the sollinearize callback */
2033 for( e = 0; e < ownerdata->nenfos; ++e )
2034 {
2035 /* call sollinearize callback, if implemented by nlhdlr */
2036 SCIP_CALL( SCIPnlhdlrSollinearize(scip, conshdlr, conss[c],
2037 ownerdata->enfos[e]->nlhdlr, expr, ownerdata->enfos[e]->nlhdlrexprdata, sol, solisbest,
2038 ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPAABOVE,
2039 ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPABELOW) );
2040 }
2041 }
2042 }
2043
2044 SCIPfreeExpriter(&it);
2045
2046 return SCIP_OKAY;
2047}
2048
2049/** processes the event that a new primal solution has been found */
2050static
2051SCIP_DECL_EVENTEXEC(processNewSolutionEvent)
2052{
2053 SCIP_CONSHDLR* conshdlr;
2054 SCIP_CONSHDLRDATA* conshdlrdata;
2055 SCIP_SOL* sol;
2056
2057 assert(scip != NULL);
2058 assert(event != NULL);
2059 assert(eventdata != NULL);
2060 assert(eventhdlr != NULL);
2062
2063 conshdlr = (SCIP_CONSHDLR*)eventdata;
2064
2065 if( SCIPconshdlrGetNConss(conshdlr) == 0 )
2066 return SCIP_OKAY;
2067
2068 sol = SCIPeventGetSol(event);
2069 assert(sol != NULL);
2070
2071 conshdlrdata = SCIPconshdlrGetData(conshdlr);
2072 assert(conshdlrdata != NULL);
2073
2074 /* we are only interested in solution coming from some heuristic other than trysol, but not from the tree
2075 * the reason for ignoring trysol solutions is that they may come ~~from an NLP solve in sepalp, where we already added linearizations, or are~~
2076 * from the tree, but postprocessed via proposeFeasibleSolution
2077 */
2078 if( SCIPsolGetHeur(sol) == NULL || SCIPsolGetHeur(sol) == conshdlrdata->trysolheur )
2079 return SCIP_OKAY;
2080
2081 SCIPdebugMsg(scip, "caught new sol event %" SCIP_EVENTTYPE_FORMAT " from heur <%s>\n", SCIPeventGetType(event), SCIPheurGetName(SCIPsolGetHeur(sol)));
2082
2084
2085 return SCIP_OKAY;
2086}
2087
2088/** tightens the bounds of the auxiliary variable associated with an expression (or original variable if being a variable-expression) according to given bounds
2089 *
2090 * The given bounds may very well be the exprs activity (when called from forwardPropExpr()), but can also be some
2091 * tighter bounds (when called from SCIPtightenExprIntervalNonlinear()).
2092 *
2093 * Nothing will happen if SCIP is not in presolve or solve.
2094 */
2095static
2097 SCIP* scip, /**< SCIP data structure */
2098 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
2099 SCIP_EXPR* expr, /**< expression whose auxvar is to be tightened */
2100 SCIP_INTERVAL bounds, /**< bounds to be used for tightening (must not be empty) */
2101 SCIP_Bool* cutoff, /**< buffer to store whether a cutoff was detected */
2102 int* ntightenings /**< buffer to add the total number of tightenings, or NULL */
2103 )
2104{
2105 SCIP_VAR* var;
2106 SCIP_Bool tightenedlb;
2107 SCIP_Bool tightenedub;
2108 SCIP_Bool force;
2109
2110 assert(scip != NULL);
2111 assert(conshdlr != NULL);
2112 assert(expr != NULL);
2113 assert(cutoff != NULL);
2114
2115 /* the given bounds must not be empty (we could cope, but we shouldn't be called in this situation) */
2117
2118 *cutoff = FALSE;
2119
2120 var = SCIPgetExprAuxVarNonlinear(expr);
2121 if( var == NULL )
2122 return SCIP_OKAY;
2123
2124 /* force tightening if conshdlrdata says so or it would mean fixing the variable */
2125 force = SCIPconshdlrGetData(conshdlr)->forceboundtightening || SCIPisEQ(scip, bounds.inf, bounds.sup);
2126
2127 /* try to tighten lower bound of (auxiliary) variable */
2128 SCIP_CALL( SCIPtightenVarLb(scip, var, bounds.inf, force, cutoff, &tightenedlb) );
2129 if( tightenedlb )
2130 {
2131 if( ntightenings != NULL )
2132 ++*ntightenings;
2133 SCIPdebugMsg(scip, "tightened lb on auxvar <%s> to %.15g (forced:%u)\n", SCIPvarGetName(var), SCIPvarGetLbLocal(var), force);
2134 }
2135 if( *cutoff )
2136 {
2137 SCIPdebugMsg(scip, "cutoff when tightening lb on auxvar <%s> to %.15g\n", SCIPvarGetName(var), bounds.inf);
2138 return SCIP_OKAY;
2139 }
2140
2141 /* try to tighten upper bound of (auxiliary) variable */
2142 SCIP_CALL( SCIPtightenVarUb(scip, var, bounds.sup, force, cutoff, &tightenedub) );
2143 if( tightenedub )
2144 {
2145 if( ntightenings != NULL )
2146 ++*ntightenings;
2147 SCIPdebugMsg(scip, "tightened ub on auxvar <%s> to %.15g (forced:%u)\n", SCIPvarGetName(var), SCIPvarGetUbLocal(var), force);
2148 }
2149 if( *cutoff )
2150 {
2151 SCIPdebugMsg(scip, "cutoff when tightening ub on auxvar <%s> to %.15g\n", SCIPvarGetName(var), bounds.sup);
2152 return SCIP_OKAY;
2153 }
2154
2155 /* TODO expr->activity should have been reevaluated now due to boundchange-events, but it used to relax bounds
2156 * that seems unnecessary and we could easily undo this here, e.g.,
2157 * if( tightenedlb ) expr->activity.inf = bounds.inf
2158 */
2159
2160 return SCIP_OKAY;
2161}
2162
2163/** propagate bounds of the expressions in a given expression tree (that is, updates activity intervals)
2164 * and tries to tighten the bounds of the auxiliary variables accordingly
2165 */
2166static
2168 SCIP* scip, /**< SCIP data structure */
2169 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
2170 SCIP_EXPR* rootexpr, /**< expression */
2171 SCIP_Bool tightenauxvars, /**< should the bounds of auxiliary variables be tightened? */
2172 SCIP_Bool* infeasible, /**< buffer to store whether the problem is infeasible (NULL if not needed) */
2173 int* ntightenings /**< buffer to store the number of auxiliary variable tightenings (NULL if not needed) */
2174 )
2175{
2176 SCIP_EXPRITER* it;
2177 SCIP_EXPR* expr;
2178 SCIP_EXPR_OWNERDATA* ownerdata;
2179 SCIP_CONSHDLRDATA* conshdlrdata;
2180
2181 assert(scip != NULL);
2182 assert(rootexpr != NULL);
2183
2184 if( infeasible != NULL )
2185 *infeasible = FALSE;
2186 if( ntightenings != NULL )
2187 *ntightenings = 0;
2188
2189 conshdlrdata = SCIPconshdlrGetData(conshdlr);
2190 assert(conshdlrdata != NULL);
2191
2192 /* if value is valid and empty, then we cannot improve, so do nothing */
2193 if( SCIPexprGetActivityTag(rootexpr) >= conshdlrdata->lastboundrelax && SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(rootexpr)) )
2194 {
2195 SCIPdebugMsg(scip, "stored activity of root expr is empty and valid (activitytag >= lastboundrelax (%" SCIP_LONGINT_FORMAT ")), skip forwardPropExpr -> cutoff\n", conshdlrdata->lastboundrelax);
2196
2197 if( infeasible != NULL )
2198 *infeasible = TRUE;
2199
2200 /* just update tag to curboundstag */
2201 SCIPexprSetActivity(rootexpr, SCIPexprGetActivity(rootexpr), conshdlrdata->curboundstag);
2202
2203 return SCIP_OKAY;
2204 }
2205
2206 /* if value is up-to-date, then nothing to do */
2207 if( SCIPexprGetActivityTag(rootexpr) == conshdlrdata->curboundstag )
2208 {
2209 SCIPdebugMsg(scip, "activitytag of root expr equals curboundstag (%" SCIP_LONGINT_FORMAT "), skip forwardPropExpr\n", conshdlrdata->curboundstag);
2210
2211 assert(!SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(rootexpr))); /* handled in previous if() */
2212
2213 return SCIP_OKAY;
2214 }
2215
2216 ownerdata = SCIPexprGetOwnerData(rootexpr);
2217 assert(ownerdata != NULL);
2218
2219 /* if activity of rootexpr is not used, but expr participated in detect (nenfos >= 0), then we do nothing
2220 * it seems wrong to be called for such an expression (unless we are in detect at the moment), so I add a SCIPABORT()
2221 * during detect, we are in some in-between state where we may want to eval activity
2222 * on exprs that we did not notify about their activity usage
2223 */
2224 if( ownerdata->nenfos >= 0 && ownerdata->nactivityusesprop == 0 && ownerdata->nactivityusessepa == 0 && !conshdlrdata->indetect)
2225 {
2226#ifdef DEBUG_PROP
2227 SCIPdebugMsg(scip, "root expr activity is not used but enfo initialized, skip inteval\n");
2228#endif
2229 SCIPABORT();
2230 return SCIP_OKAY;
2231 }
2232
2236
2237 for( expr = SCIPexpriterGetCurrent(it); !SCIPexpriterIsEnd(it); )
2238 {
2239 switch( SCIPexpriterGetStageDFS(it) )
2240 {
2242 {
2243 /* skip child if it has been evaluated already */
2244 SCIP_EXPR* child;
2245
2246 child = SCIPexpriterGetChildExprDFS(it);
2247 if( conshdlrdata->curboundstag == SCIPexprGetActivityTag(child) )
2248 {
2250 *infeasible = TRUE;
2251
2252 expr = SCIPexpriterSkipDFS(it);
2253 continue;
2254 }
2255
2256 break;
2257 }
2258
2260 {
2261 SCIP_INTERVAL activity;
2262
2263 /* we should not have entered this expression if its activity was already up to date */
2264 assert(SCIPexprGetActivityTag(expr) < conshdlrdata->curboundstag);
2265
2266 ownerdata = SCIPexprGetOwnerData(expr);
2267 assert(ownerdata != NULL);
2268
2269 /* for var exprs where varevents are catched, activity is updated immediately when the varbound has been changed
2270 * so we can assume that the activity is up to date for all these variables
2271 * UNLESS we changed the method used to evaluate activity of variable expressions
2272 * or we currently use global bounds (varevents are catched for local bound changes only)
2273 */
2274 if( SCIPisExprVar(scip, expr) && ownerdata->filterpos >= 0 &&
2275 SCIPexprGetActivityTag(expr) >= conshdlrdata->lastvaractivitymethodchange && !conshdlrdata->globalbounds )
2276 {
2277#ifndef NDEBUG
2278 SCIP_INTERVAL exprhdlrinterval;
2279
2280 SCIP_CALL( SCIPcallExprInteval(scip, expr, &exprhdlrinterval, conshdlrdata->intevalvar, conshdlrdata) );
2281 assert(SCIPisRelEQ(scip, exprhdlrinterval.inf, SCIPexprGetActivity(expr).inf));
2282 assert(SCIPisRelEQ(scip, exprhdlrinterval.sup, SCIPexprGetActivity(expr).sup));
2283#endif
2284#ifdef DEBUG_PROP
2285 SCIPdebugMsg(scip, "skip interval evaluation of expr for var <%s> [%g,%g]\n", SCIPvarGetName(SCIPgetVarExprVar(expr)), SCIPexprGetActivity(expr).inf, SCIPexprGetActivity(expr).sup);
2286#endif
2287 SCIPexprSetActivity(expr, SCIPexprGetActivity(expr), conshdlrdata->curboundstag);
2288
2289 break;
2290 }
2291
2292 if( SCIPexprGetActivityTag(expr) < conshdlrdata->lastboundrelax )
2293 {
2294 /* start with entire activity if current one is invalid */
2296 }
2298 {
2299 /* If already empty, then don't try to compute even better activity.
2300 * If cons_nonlinear were alone, then we should have noted that we are infeasible
2301 * so an assert(infeasible == NULL || *infeasible) should work here.
2302 * However, after reporting a cutoff due to expr->activity being empty,
2303 * SCIP may wander to a different node and call propagation again.
2304 * If no bounds in a nonlinear constraint have been relaxed when switching nodes
2305 * (so expr->activitytag >= conshdlrdata->lastboundrelax), then
2306 * we will still have expr->activity being empty, but will have forgotten
2307 * that we found infeasibility here before (!2221#note_134120).
2308 * Therefore we just set *infeasibility=TRUE here and stop.
2309 */
2310 if( infeasible != NULL )
2311 *infeasible = TRUE;
2312 SCIPdebugMsg(scip, "expr %p already has empty activity -> cutoff\n", (void*)expr);
2313 break;
2314 }
2315 else
2316 {
2317 /* start with current activity, since it is valid */
2318 activity = SCIPexprGetActivity(expr);
2319 }
2320
2321 /* if activity of expr is not used, but expr participated in detect (nenfos >= 0), then do nothing */
2322 if( ownerdata->nenfos >= 0 && ownerdata->nactivityusesprop == 0 && ownerdata->nactivityusessepa == 0 && !conshdlrdata->indetect )
2323 {
2324#ifdef DEBUG_PROP
2325 SCIPdebugMsg(scip, "expr %p activity is not used but enfo initialized, skip inteval\n", (void*)expr);
2326#endif
2327 break;
2328 }
2329
2330#ifdef DEBUG_PROP
2331 SCIPdebugMsg(scip, "interval evaluation of expr %p ", (void*)expr);
2332 SCIP_CALL( SCIPprintExpr(scip, expr, NULL) );
2333 SCIPdebugMsgPrint(scip, ", current activity = [%.20g, %.20g]\n", SCIPexprGetActivity(expr).inf, SCIPexprGetActivity(expr).sup);
2334#endif
2335
2336 /* run interval eval of nonlinear handlers or expression handler */
2337 if( ownerdata->nenfos > 0 )
2338 {
2339 SCIP_NLHDLR* nlhdlr;
2340 SCIP_INTERVAL nlhdlrinterval;
2341 int e;
2342
2343 /* for expressions with enforcement, nlhdlrs take care of interval evaluation */
2344 for( e = 0; e < ownerdata->nenfos && !SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, activity); ++e )
2345 {
2346 /* skip nlhdlr if it does not want to participate in activity computation */
2347 if( (ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_ACTIVITY) == 0 )
2348 continue;
2349
2350 nlhdlr = ownerdata->enfos[e]->nlhdlr;
2351 assert(nlhdlr != NULL);
2352
2353 /* skip nlhdlr if it does not provide interval evaluation (so it may only provide reverse propagation) */
2354 if( !SCIPnlhdlrHasIntEval(nlhdlr) )
2355 continue;
2356
2357 /* let nlhdlr evaluate current expression */
2358 nlhdlrinterval = activity;
2359 SCIP_CALL( SCIPnlhdlrInteval(scip, nlhdlr, expr, ownerdata->enfos[e]->nlhdlrexprdata,
2360 &nlhdlrinterval, conshdlrdata->intevalvar, conshdlrdata) );
2361#ifdef DEBUG_PROP
2362 SCIPdebugMsg(scip, " nlhdlr <%s>::inteval = [%.20g, %.20g]", SCIPnlhdlrGetName(nlhdlr), nlhdlrinterval.inf, nlhdlrinterval.sup);
2363#endif
2364
2365 /* update activity by intersecting with computed activity */
2366 SCIPintervalIntersectEps(&activity, SCIPepsilon(scip), activity, nlhdlrinterval);
2367#ifdef DEBUG_PROP
2368 SCIPdebugMsgPrint(scip, " -> new activity: [%.20g, %.20g]\n", activity.inf, activity.sup);
2369#endif
2370 }
2371 }
2372 else
2373 {
2374 /* for node without enforcement (before or during detect), call the callback of the exprhdlr directly */
2375 SCIP_INTERVAL exprhdlrinterval = activity;
2376 SCIP_CALL( SCIPcallExprInteval(scip, expr, &exprhdlrinterval, conshdlrdata->intevalvar, conshdlrdata) );
2377#ifdef DEBUG_PROP
2378 SCIPdebugMsg(scip, " exprhdlr <%s>::inteval = [%.20g, %.20g]", SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)), exprhdlrinterval.inf, exprhdlrinterval.sup);
2379#endif
2380
2381 /* update expr->activity by intersecting with computed activity */
2382 SCIPintervalIntersectEps(&activity, SCIPepsilon(scip), activity, exprhdlrinterval);
2383#ifdef DEBUG_PROP
2384 SCIPdebugMsgPrint(scip, " -> new activity: [%.20g, %.20g]\n", activity.inf, activity.sup);
2385#endif
2386 }
2387
2388 /* if expression is integral, then we try to tighten the interval bounds a bit
2389 * this should undo the addition of some unnecessary safety added by use of nextafter() in interval arithmetics, e.g., when doing pow()
2390 * it would be ok to use ceil() and floor(), but for safety we use SCIPceil and SCIPfloor for now
2391 * do this only if using boundtightening-inteval and not in redundancy check (there we really want to relax all variables)
2392 * boundtightening-inteval does not relax integer variables, so can omit expressions without children
2393 * (constants should be ok, too)
2394 */
2395 if( SCIPexprIsIntegral(expr) && conshdlrdata->intevalvar == intEvalVarBoundTightening && SCIPexprGetNChildren(expr) > 0 )
2396 {
2397 if( activity.inf > -SCIP_INTERVAL_INFINITY )
2398 activity.inf = SCIPceil(scip, activity.inf);
2399 if( activity.sup < SCIP_INTERVAL_INFINITY )
2400 activity.sup = SCIPfloor(scip, activity.sup);
2401#ifdef DEBUG_PROP
2402 SCIPdebugMsg(scip, " applying integrality: [%.20g, %.20g]\n", activity.inf, activity.sup);
2403#endif
2404 }
2405
2406 /* mark the current node to be infeasible if either the lower/upper bound is above/below +/- SCIPinfinity()
2407 * TODO this is a problem if dual-presolve fixed a variable to +/- infinity
2408 */
2409 if( SCIPisInfinity(scip, activity.inf) || SCIPisInfinity(scip, -activity.sup) )
2410 {
2411 SCIPdebugMsg(scip, "cut off due to activity [%g,%g] beyond infinity\n", activity.inf, activity.sup);
2412 SCIPintervalSetEmpty(&activity);
2413 }
2414
2415 /* now finally store activity in expr */
2416 SCIPexprSetActivity(expr, activity, conshdlrdata->curboundstag);
2417
2419 {
2420 if( infeasible != NULL )
2421 *infeasible = TRUE;
2422 }
2423 else if( tightenauxvars && ownerdata->auxvar != NULL )
2424 {
2425 SCIP_Bool tighteninfeasible;
2426
2427 SCIP_CALL( tightenAuxVarBounds(scip, conshdlr, expr, activity, &tighteninfeasible, ntightenings) );
2428 if( tighteninfeasible )
2429 {
2430 if( infeasible != NULL )
2431 *infeasible = TRUE;
2432 SCIPintervalSetEmpty(&activity);
2433 SCIPexprSetActivity(expr, activity, conshdlrdata->curboundstag);
2434 }
2435 }
2436
2437 break;
2438 }
2439
2440 default:
2441 /* you should never be here */
2442 SCIPerrorMessage("unexpected iterator stage\n");
2443 SCIPABORT();
2444 break;
2445 }
2446
2447 expr = SCIPexpriterGetNext(it);
2448 }
2449
2450 SCIPfreeExpriter(&it);
2451
2452 return SCIP_OKAY;
2453}
2454
2455/** returns whether intersecting `oldinterval` with `newinterval` would provide a properly smaller interval
2456 *
2457 * If `subsetsufficient` is TRUE, then the intersection being smaller than oldinterval is sufficient.
2458 *
2459 * If `subsetsufficient` is FALSE, then we require
2460 * - a change from an unbounded interval to a bounded one, or
2461 * - or a change from an unfixed (width > epsilon) to a fixed interval, or
2462 * - a minimal tightening of one of the interval bounds as defined by SCIPis{Lb,Ub}Better().
2463 */
2464static
2466 SCIP* scip, /**< SCIP data structure */
2467 SCIP_Bool subsetsufficient, /**< whether the intersection being a proper subset of oldinterval is sufficient */
2468 SCIP_INTERVAL newinterval, /**< new interval */
2469 SCIP_INTERVAL oldinterval /**< old interval */
2470 )
2471{
2472 assert(scip != NULL);
2473 assert(!SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, newinterval));
2474 assert(!SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, oldinterval));
2475
2476 if( subsetsufficient )
2477 /* oldinterval \cap newinterval < oldinterval iff not oldinterval is subset of newinterval */
2478 return !SCIPintervalIsSubsetEQ(SCIP_INTERVAL_INFINITY, oldinterval, newinterval);
2479
2480 /* check whether lower bound of interval becomes finite */
2481 if( oldinterval.inf <= -SCIP_INTERVAL_INFINITY && newinterval.inf > -SCIP_INTERVAL_INFINITY )
2482 return TRUE;
2483
2484 /* check whether upper bound of interval becomes finite */
2485 if( oldinterval.sup >= SCIP_INTERVAL_INFINITY && newinterval.sup > SCIP_INTERVAL_INFINITY )
2486 return TRUE;
2487
2488 /* check whether intersection will have width <= epsilon, if oldinterval doesn't have yet */
2489 if( !SCIPisEQ(scip, oldinterval.inf, oldinterval.sup) && SCIPisEQ(scip, MAX(oldinterval.inf, newinterval.inf), MIN(oldinterval.sup, newinterval.sup)) )
2490 return TRUE;
2491
2492 /* check whether lower bound on interval will be better by SCIP's quality measures for boundchanges */
2493 if( SCIPisLbBetter(scip, newinterval.inf, oldinterval.inf, oldinterval.sup) )
2494 return TRUE;
2495
2496 /* check whether upper bound on interval will be better by SCIP's quality measures for boundchanges */
2497 if( SCIPisUbBetter(scip, newinterval.sup, oldinterval.inf, oldinterval.sup) )
2498 return TRUE;
2499
2500 return FALSE;
2501}
2502
2503/** propagates bounds for each sub-expression in the `reversepropqueue` by starting from the root expressions
2504 *
2505 * The expression will be traversed in breadth first search by using this queue.
2506 *
2507 * @note Calling this function requires feasible intervals for each sub-expression; this is guaranteed by calling
2508 * forwardPropExpr() before calling this function.
2509 *
2510 * @note Calling this function with `*infeasible` = TRUE will only empty the queue.
2511 */
2512static
2514 SCIP* scip, /**< SCIP data structure */
2515 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
2516 SCIP_Bool* infeasible, /**< buffer to update whether an expression's bounds were propagated to an empty interval */
2517 int* ntightenings /**< buffer to store the number of (variable) tightenings */
2518 )
2519{
2520 SCIP_CONSHDLRDATA* conshdlrdata;
2521 SCIP_EXPR* expr;
2522 SCIP_EXPR_OWNERDATA* ownerdata;
2523
2524 assert(infeasible != NULL);
2525 assert(ntightenings != NULL);
2526
2527 conshdlrdata = SCIPconshdlrGetData(conshdlr);
2528 assert(conshdlrdata != NULL);
2529
2530 *ntightenings = 0;
2531
2532 /* main loop that calls reverse propagation for expressions on the queue
2533 * when reverseprop finds a tightening for an expression, then that expression is added to the queue (within the reverseprop call)
2534 */
2535 while( !SCIPqueueIsEmpty(conshdlrdata->reversepropqueue) && !(*infeasible) )
2536 {
2537 SCIP_INTERVAL propbounds;
2538 int e;
2539
2540 expr = (SCIP_EXPR*) SCIPqueueRemove(conshdlrdata->reversepropqueue);
2541 assert(expr != NULL);
2542
2543 ownerdata = SCIPexprGetOwnerData(expr);
2544 assert(ownerdata != NULL);
2545
2546 assert(ownerdata->inpropqueue);
2547 /* mark that the expression is not in the queue anymore */
2548 ownerdata->inpropqueue = FALSE;
2549
2550 /* since the expr was in the propagation queue, the propbounds should belong to current propagation and should not be empty
2551 * (propbounds being entire doesn't make much sense, so assert this for now, too, but that could be removed)
2552 */
2553 assert(ownerdata->propboundstag == conshdlrdata->curpropboundstag);
2554 assert(!SCIPintervalIsEntire(SCIP_INTERVAL_INFINITY, ownerdata->propbounds));
2555 assert(!SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, ownerdata->propbounds));
2556
2557 /* this intersects propbounds with activity and auxvar bounds
2558 * I doubt this would be much helpful, since propbounds are already subset of activity and we also propagate
2559 * auxvar bounds separately, so disabling this for now
2560 */
2561#ifdef SCIP_DISABLED_CODE
2562 propbounds = SCIPgetExprBoundsNonlinear(scip, expr);
2564 {
2565 *infeasible = TRUE;
2566 break;
2567 }
2568#else
2569 propbounds = ownerdata->propbounds;
2570#endif
2571
2572 if( ownerdata->nenfos > 0 )
2573 {
2574 /* for nodes with enforcement, call reverse propagation callbacks of nlhdlrs */
2575 for( e = 0; e < ownerdata->nenfos && !*infeasible; ++e )
2576 {
2577 SCIP_NLHDLR* nlhdlr;
2578 int nreds;
2579
2580 /* skip nlhdlr if it does not want to participate in activity computation */
2581 if( (ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_ACTIVITY) == 0 )
2582 continue;
2583
2584 nlhdlr = ownerdata->enfos[e]->nlhdlr;
2585 assert(nlhdlr != NULL);
2586
2587 /* call the reverseprop of the nlhdlr */
2588#ifdef SCIP_DEBUG
2589 SCIPdebugMsg(scip, "call reverse propagation for ");
2590 SCIP_CALL( SCIPprintExpr(scip, expr, NULL) );
2591 SCIPdebugMsgPrint(scip, " in [%g,%g] using nlhdlr <%s>\n", propbounds.inf, propbounds.sup, SCIPnlhdlrGetName(nlhdlr));
2592#endif
2593
2594 nreds = 0;
2595 SCIP_CALL( SCIPnlhdlrReverseprop(scip, conshdlr, nlhdlr, expr, ownerdata->enfos[e]->nlhdlrexprdata, propbounds, infeasible, &nreds) );
2596 assert(nreds >= 0);
2597 *ntightenings += nreds;
2598 }
2599 }
2601 {
2602 /* if expr without enforcement (before detect), call reverse propagation callback of exprhdlr directly */
2603 SCIP_INTERVAL* childrenbounds;
2604 int c;
2605
2606#ifdef SCIP_DEBUG
2607 SCIPdebugMsg(scip, "call reverse propagation for ");
2608 SCIP_CALL( SCIPprintExpr(scip, expr, NULL) );
2609 SCIPdebugMsgPrint(scip, " in [%g,%g] using exprhdlr <%s>\n", SCIPexprGetActivity(expr).inf, SCIPexprGetActivity(expr).sup, SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)));
2610#endif
2611
2612 /* if someone added an expr without nlhdlr into the reversepropqueue, then this must be because its enfo hasn't
2613 * been initialized in detectNlhdlr yet (nenfos < 0)
2614 */
2615 assert(ownerdata->nenfos < 0);
2616
2617 SCIP_CALL( SCIPallocBufferArray(scip, &childrenbounds, SCIPexprGetNChildren(expr)) );
2618 for( c = 0; c < SCIPexprGetNChildren(expr); ++c )
2619 childrenbounds[c] = SCIPgetExprBoundsNonlinear(scip, SCIPexprGetChildren(expr)[c]);
2620
2621 /* call the reverseprop of the exprhdlr */
2622 SCIP_CALL( SCIPcallExprReverseprop(scip, expr, propbounds, childrenbounds, infeasible) );
2623
2624 if( !*infeasible )
2625 for( c = 0; c < SCIPexprGetNChildren(expr); ++c )
2626 {
2627 SCIP_CALL( SCIPtightenExprIntervalNonlinear(scip, SCIPexprGetChildren(expr)[c], childrenbounds[c], infeasible, ntightenings) );
2628 }
2629
2630 SCIPfreeBufferArray(scip, &childrenbounds);
2631 }
2632 }
2633
2634 /* reset inpropqueue for all remaining expr's in queue (can happen in case of early stop due to infeasibility) */
2635 while( !SCIPqueueIsEmpty(conshdlrdata->reversepropqueue) )
2636 {
2637 expr = (SCIP_EXPR*) SCIPqueueRemove(conshdlrdata->reversepropqueue);
2638 assert(expr != NULL);
2639
2640 ownerdata = SCIPexprGetOwnerData(expr);
2641 assert(ownerdata != NULL);
2642
2643 /* mark that the expression is not in the queue anymore */
2644 ownerdata->inpropqueue = FALSE;
2645 }
2646
2647 return SCIP_OKAY;
2648}
2649
2650/** calls domain propagation for a given set of constraints
2651 *
2652 * The algorithm alternates calls of forward and reverse propagation.
2653 * Forward propagation ensures that activity of expressions is up to date.
2654 * Reverse propagation tries to derive tighter variable bounds by reversing the activity computation, using the constraints
2655 * [lhs,rhs] interval as starting point.
2656 *
2657 * The propagation algorithm works as follows:
2658 * 1. apply forward propagation (update activities) for all constraints not marked as propagated
2659 * 2. if presolve or propauxvars is disabled: collect expressions for which the constraint sides provide tighter bounds
2660 * if solve and propauxvars is enabled: collect expressions for which auxvars (including those in root exprs)
2661 * provide tighter bounds
2662 * 3. apply reverse propagation to all collected expressions; don't explore
2663 * sub-expressions which have not changed since the beginning of the propagation loop
2664 * 4. if we have found enough tightenings go to 1, otherwise leave propagation loop
2665 *
2666 * @note After calling forward propagation for a constraint, we mark this constraint as propagated. This flag might be
2667 * reset during the reverse propagation when we find a bound tightening of a variable expression contained in the
2668 * constraint. Resetting this flag is done in the EVENTEXEC callback of the event handler
2669 *
2670 * TODO should we distinguish between expressions where activity information is used for separation and those where not,
2671 * e.g., try less to propagate on convex constraints?
2672 */
2673static
2675 SCIP* scip, /**< SCIP data structure */
2676 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
2677 SCIP_CONS** conss, /**< constraints to propagate */
2678 int nconss, /**< total number of constraints */
2679 SCIP_Bool force, /**< force tightening even if below bound strengthening tolerance */
2680 SCIP_RESULT* result, /**< pointer to store the result */
2681 int* nchgbds /**< buffer to add the number of changed bounds */
2682 )
2683{
2684 SCIP_CONSHDLRDATA* conshdlrdata;
2685 SCIP_CONSDATA* consdata;
2686 SCIP_EXPR_OWNERDATA* ownerdata;
2687 SCIP_Bool cutoff = FALSE;
2688 SCIP_INTERVAL conssides;
2689 int ntightenings;
2690 int roundnr;
2691 SCIP_EXPRITER* revpropcollectit = NULL;
2692 int i;
2693
2694 assert(scip != NULL);
2695 assert(conshdlr != NULL);
2696 assert(conss != NULL);
2697 assert(nconss >= 0);
2698 assert(result != NULL);
2699 assert(nchgbds != NULL);
2700 assert(*nchgbds >= 0);
2701
2702 /* no constraints to propagate */
2703 if( nconss == 0 )
2704 {
2705 *result = SCIP_DIDNOTRUN;
2706 return SCIP_OKAY;
2707 }
2708
2709 conshdlrdata = SCIPconshdlrGetData(conshdlr);
2710 assert(conshdlrdata != NULL);
2711#ifndef CR_API /* this assert may not work in unittests due to having this code compiled twice, #3543 */
2712 assert(conshdlrdata->intevalvar == intEvalVarBoundTightening);
2713#endif
2714 assert(!conshdlrdata->globalbounds);
2715
2716 *result = SCIP_DIDNOTFIND;
2717 roundnr = 0;
2718
2719 /* tightenAuxVarBounds() needs to know whether boundtightenings are to be forced */
2720 conshdlrdata->forceboundtightening = force;
2721
2722 /* invalidate all propbounds (probably not needed) */
2723 ++conshdlrdata->curpropboundstag;
2724
2725 /* create iterator that we will use if we need to look at all auxvars */
2726 if( conshdlrdata->propauxvars )
2727 {
2728 SCIP_CALL( SCIPcreateExpriter(scip, &revpropcollectit) );
2729 }
2730
2731 /* main propagation loop */
2732 do
2733 {
2734 SCIPdebugMsg(scip, "start propagation round %d\n", roundnr);
2735
2736 assert(SCIPqueueIsEmpty(conshdlrdata->reversepropqueue));
2737
2738 /* apply forward propagation (update expression activities)
2739 * and add promising root expressions into queue for reversepropagation
2740 */
2741 for( i = 0; i < nconss; ++i )
2742 {
2743 consdata = SCIPconsGetData(conss[i]);
2744 assert(consdata != NULL);
2745
2746 /* skip deleted, non-active, or propagation-disabled constraints */
2747 if( SCIPconsIsDeleted(conss[i]) || !SCIPconsIsActive(conss[i]) || !SCIPconsIsPropagationEnabled(conss[i]) )
2748 continue;
2749
2750 /* skip already propagated constraints, i.e., constraints where no (original) variable has changed and thus
2751 * activity didn't change
2752 */
2753 if( consdata->ispropagated )
2754 continue;
2755
2756 /* update activities in expression */
2757 SCIPdebugMsg(scip, "call forwardPropExpr() for constraint <%s> (round %d): ", SCIPconsGetName(conss[i]), roundnr);
2758 SCIPdebugPrintCons(scip, conss[i], NULL);
2759
2760 ntightenings = 0;
2761 SCIP_CALL( forwardPropExpr(scip, conshdlr, consdata->expr, TRUE, &cutoff, &ntightenings) );
2762 assert(cutoff || !SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(consdata->expr)));
2763
2764 if( cutoff )
2765 {
2766 SCIPdebugMsg(scip, " -> cutoff in forwardPropExpr (due to domain error or auxvar tightening) of constraint <%s>\n", SCIPconsGetName(conss[i]));
2767 *result = SCIP_CUTOFF;
2768 break;
2769 }
2770
2771 ownerdata = SCIPexprGetOwnerData(consdata->expr);
2772
2773 /* 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 */
2774 if( !conshdlrdata->propauxvars || ownerdata->auxvar == NULL )
2775 {
2776 /* check whether constraint sides (relaxed by epsilon) or auxvar bounds provide a tightening
2777 * (if we have auxvar (not in presolve), then bounds of the auxvar are initially set to constraint sides,
2778 * so taking auxvar bounds is enough)
2779 */
2780 if( ownerdata->auxvar == NULL )
2781 {
2782 /* relax sides by SCIPepsilon() and handle infinite sides */
2783 SCIP_Real lhs = SCIPisInfinity(scip, -consdata->lhs) ? -SCIP_INTERVAL_INFINITY : consdata->lhs - conshdlrdata->conssiderelaxamount;
2784 SCIP_Real rhs = SCIPisInfinity(scip, consdata->rhs) ? SCIP_INTERVAL_INFINITY : consdata->rhs + conshdlrdata->conssiderelaxamount;
2785 SCIPintervalSetBounds(&conssides, lhs, rhs);
2786 }
2787 else
2788 {
2789 conssides = intEvalVarBoundTightening(scip, ownerdata->auxvar, (void*)conshdlrdata);
2790 }
2791 SCIP_CALL( SCIPtightenExprIntervalNonlinear(scip, consdata->expr, conssides, &cutoff, &ntightenings) );
2792 }
2793 else
2794 {
2795 /* check whether bounds of any auxvar used in constraint provides a tightening
2796 * (for the root expression, bounds of auxvar are initially set to constraint sides)
2797 * but skip exprs that have an auxvar, but do not participate in propagation
2798 */
2799 SCIP_EXPR* expr;
2800
2801 assert(revpropcollectit != NULL);
2802 SCIP_CALL( SCIPexpriterInit(revpropcollectit, consdata->expr, SCIP_EXPRITER_BFS, FALSE) );
2803 for( expr = SCIPexpriterGetCurrent(revpropcollectit); !SCIPexpriterIsEnd(revpropcollectit) && !cutoff; expr = SCIPexpriterGetNext(revpropcollectit) )
2804 {
2805 ownerdata = SCIPexprGetOwnerData(expr);
2806 assert(ownerdata != NULL);
2807
2808 if( ownerdata->auxvar == NULL )
2809 continue;
2810
2811 if( ownerdata->nactivityusesprop == 0 && ownerdata->nactivityusessepa == 0 )
2812 continue;
2813
2814 conssides = intEvalVarBoundTightening(scip, ownerdata->auxvar, (void*)conshdlrdata);
2815 SCIP_CALL( SCIPtightenExprIntervalNonlinear(scip, expr, conssides, &cutoff, &ntightenings) );
2816 }
2817 }
2818
2819 if( cutoff )
2820 {
2821 SCIPdebugMsg(scip, " -> cutoff after intersect with conssides of constraint <%s>\n", SCIPconsGetName(conss[i]));
2822 *result = SCIP_CUTOFF;
2823 break;
2824 }
2825
2826 assert(ntightenings >= 0);
2827 if( ntightenings > 0 )
2828 {
2829 *nchgbds += ntightenings;
2830 *result = SCIP_REDUCEDDOM;
2831 }
2832
2833 /* mark constraint as propagated; this will be reset via the event system when we find a variable tightening */
2834 consdata->ispropagated = TRUE;
2835 }
2836
2837 /* apply backward propagation (if cutoff is TRUE, then this call empties the queue) */
2838 SCIP_CALL( reversePropQueue(scip, conshdlr, &cutoff, &ntightenings) );
2839 assert(ntightenings >= 0);
2840 assert(SCIPqueueIsEmpty(conshdlrdata->reversepropqueue));
2841
2842 if( cutoff )
2843 {
2844 SCIPdebugMsg(scip, " -> cutoff\n");
2845 *result = SCIP_CUTOFF;
2846 break;
2847 }
2848
2849 if( ntightenings > 0 )
2850 {
2851 *nchgbds += ntightenings;
2852 *result = SCIP_REDUCEDDOM;
2853 }
2854 }
2855 while( ntightenings > 0 && ++roundnr < conshdlrdata->maxproprounds );
2856
2857 if( conshdlrdata->propauxvars )
2858 {
2859 SCIPfreeExpriter(&revpropcollectit);
2860 }
2861
2862 conshdlrdata->forceboundtightening = FALSE;
2863
2864 /* invalidate propbounds in all exprs, so noone accidentally uses them outside propagation */
2865 ++conshdlrdata->curpropboundstag;
2866
2867 return SCIP_OKAY;
2868}
2869
2870/** calls the reverseprop callbacks of all nlhdlrs in all expressions in all constraints using activity as bounds
2871 *
2872 * This is meant to propagate any domain restrictions on functions onto variable bounds, if possible.
2873 *
2874 * Assumes that activities are still valid and curpropboundstag does not need to be increased.
2875 * Therefore, a good place to call this function is immediately after propConss() or after forwardPropExpr() if outside propagation.
2876 */
2877static
2879 SCIP* scip, /**< SCIP data structure */
2880 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
2881 SCIP_CONS** conss, /**< constraints to propagate */
2882 int nconss, /**< total number of constraints */
2883 SCIP_RESULT* result, /**< pointer to store the result */
2884 int* nchgbds /**< buffer to add the number of changed bounds */
2885 )
2886{
2887 SCIP_CONSDATA* consdata;
2888 SCIP_EXPRITER* it;
2889 SCIP_EXPR* expr;
2890 SCIP_EXPR_OWNERDATA* ownerdata;
2891 SCIP_Bool cutoff = FALSE;
2892 int ntightenings;
2893 int c;
2894 int e;
2895
2896 assert(scip != NULL);
2897 assert(conshdlr != NULL);
2898 assert(conss != NULL);
2899 assert(nconss >= 0);
2900 assert(result != NULL);
2901 assert(nchgbds != NULL);
2902 assert(*nchgbds >= 0);
2903
2904#ifndef CR_API /* this assert may not work in unittests due to having this code compiled twice, #3543 */
2905 assert(SCIPconshdlrGetData(conshdlr)->intevalvar == intEvalVarBoundTightening);
2906#endif
2907 assert(!SCIPconshdlrGetData(conshdlr)->globalbounds);
2908 assert(SCIPqueueIsEmpty(SCIPconshdlrGetData(conshdlr)->reversepropqueue));
2909
2910 *result = SCIP_DIDNOTFIND;
2911
2914
2915 for( c = 0; c < nconss && !cutoff; ++c )
2916 {
2917 /* skip deleted, non-active, or propagation-disabled constraints */
2918 if( SCIPconsIsDeleted(conss[c]) || !SCIPconsIsActive(conss[c]) || !SCIPconsIsPropagationEnabled(conss[c]) )
2919 continue;
2920
2921 consdata = SCIPconsGetData(conss[c]);
2922 assert(consdata != NULL);
2923
2924 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it) && !cutoff; expr = SCIPexpriterGetNext(it) )
2925 {
2926 ownerdata = SCIPexprGetOwnerData(expr);
2927 assert(ownerdata != NULL);
2928
2929 /* call reverseprop for those nlhdlr that participate in this expr's activity computation
2930 * this will propagate the current activity
2931 */
2932 for( e = 0; e < ownerdata->nenfos; ++e )
2933 {
2934 SCIP_NLHDLR* nlhdlr;
2935 assert(ownerdata->enfos[e] != NULL);
2936
2937 nlhdlr = ownerdata->enfos[e]->nlhdlr;
2938 assert(nlhdlr != NULL);
2939 if( (ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_ACTIVITY) == 0 )
2940 continue;
2941
2942 SCIPdebugMsg(scip, "propExprDomains calling reverseprop for expression %p [%g,%g]\n", (void*)expr,
2943 SCIPexprGetActivity(expr).inf, SCIPexprGetActivity(expr).sup);
2944 ntightenings = 0;
2945 SCIP_CALL( SCIPnlhdlrReverseprop(scip, conshdlr, nlhdlr, expr, ownerdata->enfos[e]->nlhdlrexprdata,
2946 SCIPexprGetActivity(expr), &cutoff, &ntightenings) );
2947
2948 if( cutoff )
2949 {
2950 /* stop everything if we detected infeasibility */
2951 SCIPdebugMsg(scip, "detect infeasibility for constraint <%s> during reverseprop()\n", SCIPconsGetName(conss[c]));
2952 *result = SCIP_CUTOFF;
2953 break;
2954 }
2955
2956 assert(ntightenings >= 0);
2957 if( ntightenings > 0 )
2958 {
2959 *nchgbds += ntightenings;
2960 *result = SCIP_REDUCEDDOM;
2961 }
2962 }
2963 }
2964 }
2965
2966 /* apply backward propagation (if cutoff is TRUE, then this call empties the queue) */
2967 SCIP_CALL( reversePropQueue(scip, conshdlr, &cutoff, &ntightenings) );
2968 assert(ntightenings >= 0);
2969
2970 if( cutoff )
2971 {
2972 SCIPdebugMsg(scip, " -> cutoff\n");
2973 *result = SCIP_CUTOFF;
2974 }
2975 else if( ntightenings > 0 )
2976 {
2977 *nchgbds += ntightenings;
2978 *result = SCIP_REDUCEDDOM;
2979 }
2980
2981 SCIPfreeExpriter(&it);
2982
2983 /* invalidate propbounds in all exprs, so noone accidentally uses them outside propagation */
2984 ++SCIPconshdlrGetData(conshdlr)->curpropboundstag;
2985
2986 return SCIP_OKAY;
2987}
2988
2989/** propagates variable locks through expression and adds locks to variables */
2990static
2992 SCIP* scip, /**< SCIP data structure */
2993 SCIP_EXPR* expr, /**< expression */
2994 int nlockspos, /**< number of positive locks */
2995 int nlocksneg /**< number of negative locks */
2996 )
2997{
2998 SCIP_EXPR_OWNERDATA* ownerdata;
2999 SCIP_EXPRITER* it;
3000 SCIP_EXPRITER_USERDATA ituserdata;
3001
3002 assert(expr != NULL);
3003
3004 /* if no locks, then nothing to propagate */
3005 if( nlockspos == 0 && nlocksneg == 0 )
3006 return SCIP_OKAY;
3007
3011 assert(SCIPexpriterGetCurrent(it) == expr); /* iterator should not have moved */
3012
3013 /* store locks in root node */
3014 ituserdata.intvals[0] = nlockspos;
3015 ituserdata.intvals[1] = nlocksneg;
3016 SCIPexpriterSetCurrentUserData(it, ituserdata);
3017
3018 while( !SCIPexpriterIsEnd(it) )
3019 {
3020 /* collect locks */
3021 ituserdata = SCIPexpriterGetCurrentUserData(it);
3022 nlockspos = ituserdata.intvals[0];
3023 nlocksneg = ituserdata.intvals[1];
3024
3025 ownerdata = SCIPexprGetOwnerData(expr);
3026
3027 switch( SCIPexpriterGetStageDFS(it) )
3028 {
3030 {
3031 if( SCIPisExprVar(scip, expr) )
3032 {
3033 /* if a variable, then also add nlocksneg/nlockspos via SCIPaddVarLocks() */
3034 SCIP_CALL( SCIPaddVarLocks(scip, SCIPgetVarExprVar(expr), nlocksneg, nlockspos) );
3035 }
3036
3037 /* add locks to expression */
3038 ownerdata->nlockspos += nlockspos;
3039 ownerdata->nlocksneg += nlocksneg;
3040
3041 /* add monotonicity information if expression has been locked for the first time */
3042 if( ownerdata->nlockspos == nlockspos && ownerdata->nlocksneg == nlocksneg && SCIPexprGetNChildren(expr) > 0
3044 {
3045 int i;
3046
3047 assert(ownerdata->monotonicity == NULL);
3048 assert(ownerdata->monotonicitysize == 0);
3049
3050 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &ownerdata->monotonicity, SCIPexprGetNChildren(expr)) );
3051 ownerdata->monotonicitysize = SCIPexprGetNChildren(expr);
3052
3053 /* store the monotonicity for each child */
3054 for( i = 0; i < SCIPexprGetNChildren(expr); ++i )
3055 {
3056 SCIP_CALL( SCIPcallExprMonotonicity(scip, expr, i, &ownerdata->monotonicity[i]) );
3057 }
3058 }
3059 break;
3060 }
3061
3063 {
3064 /* remove monotonicity information if expression has been unlocked */
3065 if( ownerdata->nlockspos == 0 && ownerdata->nlocksneg == 0 && ownerdata->monotonicity != NULL )
3066 {
3067 assert(ownerdata->monotonicitysize > 0);
3068 /* keep this assert for checking whether someone changed an expression without updating locks properly */
3069 assert(ownerdata->monotonicitysize == SCIPexprGetNChildren(expr));
3070
3071 SCIPfreeBlockMemoryArray(scip, &ownerdata->monotonicity, ownerdata->monotonicitysize);
3072 ownerdata->monotonicitysize = 0;
3073 }
3074 break;
3075 }
3076
3078 {
3079 SCIP_MONOTONE monotonicity;
3080
3081 /* get monotonicity of child */
3082 /* NOTE: the monotonicity stored in an expression might be different from the result obtained by
3083 * SCIPcallExprMonotonicity
3084 */
3085 monotonicity = ownerdata->monotonicity != NULL ? ownerdata->monotonicity[SCIPexpriterGetChildIdxDFS(it)] : SCIP_MONOTONE_UNKNOWN;
3086
3087 /* compute resulting locks of the child expression */
3088 switch( monotonicity )
3089 {
3090 case SCIP_MONOTONE_INC:
3091 ituserdata.intvals[0] = nlockspos;
3092 ituserdata.intvals[1] = nlocksneg;
3093 break;
3094 case SCIP_MONOTONE_DEC:
3095 ituserdata.intvals[0] = nlocksneg;
3096 ituserdata.intvals[1] = nlockspos;
3097 break;
3099 ituserdata.intvals[0] = nlockspos + nlocksneg;
3100 ituserdata.intvals[1] = nlockspos + nlocksneg;
3101 break;
3103 ituserdata.intvals[0] = 0;
3104 ituserdata.intvals[1] = 0;
3105 break;
3106 }
3107 /* set locks in child expression */
3108 SCIPexpriterSetChildUserData(it, ituserdata);
3109
3110 break;
3111 }
3112
3113 default :
3114 /* you should never be here */
3115 SCIPABORT();
3116 break;
3117 }
3118
3119 expr = SCIPexpriterGetNext(it);
3120 }
3121
3122 SCIPfreeExpriter(&it);
3123
3124 return SCIP_OKAY;
3125}
3126
3127/** main function for adding locks to expressions and variables
3128 *
3129 * Locks for a nonlinear constraint are used to update locks for all sub-expressions and variables.
3130 * Locks of expressions depend on the monotonicity of expressions w.r.t. their children, e.g.,
3131 * consider the constraint \f$x^2 \leq 1\f$ with \f$x \in [-2,-1]\f$ implies an up-lock for the root
3132 * expression (pow) and a down-lock for its child \f$x\f$ because \f$x^2\f$ is decreasing on [-2,-1].
3133 * Since the monotonicity (and thus the locks) might also depend on variable bounds, the function remembers
3134 * the computed monotonicity information of each expression until all locks of an expression have been removed,
3135 * which implies that updating the monotonicity information during the next locking of this expression does not
3136 * break existing locks.
3137 *
3138 * @note When modifying the structure of an expression, e.g., during simplification, it is necessary to remove all
3139 * locks from an expression and repropagating them after the structural changes have been applied.
3140 * Because of existing common sub-expressions, it might be necessary to remove the locks of all constraints
3141 * to ensure that an expression is unlocked (see canonicalizeConstraints() for an example)
3142 */
3143static
3145 SCIP* scip, /**< SCIP data structure */
3146 SCIP_CONS* cons, /**< nonlinear constraint */
3147 int nlockspos, /**< number of positive rounding locks */
3148 int nlocksneg /**< number of negative rounding locks */
3149 )
3150{
3151 SCIP_CONSDATA* consdata;
3152
3153 assert(cons != NULL);
3154
3155 if( nlockspos == 0 && nlocksneg == 0 )
3156 return SCIP_OKAY;
3157
3158 consdata = SCIPconsGetData(cons);
3159 assert(consdata != NULL);
3160
3161 /* no constraint sides -> nothing to lock */
3162 if( SCIPisInfinity(scip, consdata->rhs) && SCIPisInfinity(scip, -consdata->lhs) )
3163 return SCIP_OKAY;
3164
3165 /* remember locks */
3166 consdata->nlockspos += nlockspos;
3167 consdata->nlocksneg += nlocksneg;
3168
3169 assert(consdata->nlockspos >= 0);
3170 assert(consdata->nlocksneg >= 0);
3171
3172 /* compute locks for lock propagation */
3173 if( !SCIPisInfinity(scip, consdata->rhs) && !SCIPisInfinity(scip, -consdata->lhs) )
3174 {
3175 SCIP_CALL( propagateLocks(scip, consdata->expr, nlockspos + nlocksneg, nlockspos + nlocksneg));
3176 }
3177 else if( !SCIPisInfinity(scip, consdata->rhs) )
3178 {
3179 SCIP_CALL( propagateLocks(scip, consdata->expr, nlockspos, nlocksneg));
3180 }
3181 else
3182 {
3183 assert(!SCIPisInfinity(scip, -consdata->lhs));
3184 SCIP_CALL( propagateLocks(scip, consdata->expr, nlocksneg, nlockspos));
3185 }
3186
3187 return SCIP_OKAY;
3188}
3189
3190/** create a nonlinear row representation of a nonlinear constraint and stores them in consdata */
3191static
3193 SCIP* scip, /**< SCIP data structure */
3194 SCIP_CONS* cons /**< nonlinear constraint */
3195 )
3196{
3197 SCIP_CONSDATA* consdata;
3198
3199 assert(scip != NULL);
3200 assert(cons != NULL);
3201
3202 consdata = SCIPconsGetData(cons);
3203 assert(consdata != NULL);
3204 assert(consdata->expr != NULL);
3205
3206 if( consdata->nlrow != NULL )
3207 {
3208 SCIP_CALL( SCIPreleaseNlRow(scip, &consdata->nlrow) );
3209 }
3210
3211 /* better curvature info will be set in initSolve() just before nlrow is added to NLP */
3212 SCIP_CALL( SCIPcreateNlRow(scip, &consdata->nlrow, SCIPconsGetName(cons), 0.0,
3213 0, NULL, NULL, NULL, consdata->lhs, consdata->rhs, SCIP_EXPRCURV_UNKNOWN) );
3214
3215 if( SCIPisExprSum(scip, consdata->expr) )
3216 {
3217 /* if root is a sum, then split into linear and nonlinear terms */
3218 SCIP_EXPR* nonlinpart;
3219 SCIP_EXPR* child;
3220 SCIP_Real* coefs;
3221 int i;
3222
3223 coefs = SCIPgetCoefsExprSum(consdata->expr);
3224
3225 /* constant term of sum */
3226 SCIP_CALL( SCIPchgNlRowConstant(scip, consdata->nlrow, SCIPgetConstantExprSum(consdata->expr)) );
3227
3228 /* a sum-expression that will hold the nonlinear terms and be passed to the nlrow eventually */
3229 SCIP_CALL( SCIPcreateExprSum(scip, &nonlinpart, 0, NULL, NULL, 0.0, exprownerCreate, (void*)SCIPconsGetHdlr(cons)) );
3230
3231 for( i = 0; i < SCIPexprGetNChildren(consdata->expr); ++i )
3232 {
3233 child = SCIPexprGetChildren(consdata->expr)[i];
3234 if( SCIPisExprVar(scip, child) )
3235 {
3236 /* linear term */
3237 SCIP_CALL( SCIPaddLinearCoefToNlRow(scip, consdata->nlrow, SCIPgetVarExprVar(child), coefs[i]) );
3238 }
3239 else
3240 {
3241 /* nonlinear term */
3242 SCIP_CALL( SCIPappendExprSumExpr(scip, nonlinpart, child, coefs[i]) );
3243 }
3244 }
3245
3246 if( SCIPexprGetNChildren(nonlinpart) > 0 )
3247 {
3248 /* add expression to nlrow (this will make a copy) */
3249 SCIP_CALL( SCIPsetNlRowExpr(scip, consdata->nlrow, nonlinpart) );
3250 }
3251 SCIP_CALL( SCIPreleaseExpr(scip, &nonlinpart) );
3252 }
3253 else
3254 {
3255 SCIP_CALL( SCIPsetNlRowExpr(scip, consdata->nlrow, consdata->expr) );
3256 }
3257
3258 return SCIP_OKAY;
3259}
3260
3261/** compares enfodata by enforcement priority of nonlinear handler
3262 *
3263 * If handlers have same enforcement priority, then compare by detection priority, then by name.
3264 */
3265static
3267{
3268 SCIP_NLHDLR* h1;
3269 SCIP_NLHDLR* h2;
3270
3271 assert(elem1 != NULL);
3272 assert(elem2 != NULL);
3273
3274 h1 = ((EXPRENFO*)elem1)->nlhdlr;
3275 h2 = ((EXPRENFO*)elem2)->nlhdlr;
3276
3277 assert(h1 != NULL);
3278 assert(h2 != NULL);
3279
3282
3285
3286 return strcmp(SCIPnlhdlrGetName(h1), SCIPnlhdlrGetName(h2));
3287}
3288
3289/** install nlhdlrs in one expression */
3290static
3292 SCIP* scip, /**< SCIP data structure */
3293 SCIP_EXPR* expr, /**< expression for which to run detection routines */
3294 SCIP_CONS* cons /**< constraint for which expr == consdata->expr, otherwise NULL */
3295 )
3296{
3297 SCIP_EXPR_OWNERDATA* ownerdata;
3298 SCIP_CONSHDLRDATA* conshdlrdata;
3299 SCIP_NLHDLR_METHOD enforcemethodsallowed;
3300 SCIP_NLHDLR_METHOD enforcemethods;
3301 SCIP_NLHDLR_METHOD enforcemethodsnew;
3302 SCIP_NLHDLR_METHOD nlhdlrenforcemethods;
3303 SCIP_NLHDLR_METHOD nlhdlrparticipating;
3304 SCIP_NLHDLREXPRDATA* nlhdlrexprdata;
3305 int enfossize; /* allocated length of expr->enfos array */
3306 int h;
3307
3308 assert(expr != NULL);
3309
3310 ownerdata = SCIPexprGetOwnerData(expr);
3311 assert(ownerdata != NULL);
3312
3313 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr);
3314 assert(conshdlrdata != NULL);
3315 assert(conshdlrdata->auxvarid >= 0);
3316 assert(!conshdlrdata->indetect);
3317
3318 /* there should be no enforcer yet and detection should not even have considered expr yet */
3319 assert(ownerdata->nenfos < 0);
3320 assert(ownerdata->enfos == NULL);
3321
3322 /* check which enforcement methods are required by setting flags in enforcemethods for those that are NOT required
3323 * - if no auxiliary variable is used, then do not need sepabelow or sepaabove
3324 * - if auxiliary variable is used, but nobody positively (up) locks expr -> only need to enforce expr >= auxvar -> no need for underestimation
3325 * - if auxiliary variable is used, but nobody negatively (down) locks expr -> only need to enforce expr <= auxvar -> no need for overestimation
3326 * - if no one uses activity, then do not need activity methods
3327 */
3328 enforcemethods = SCIP_NLHDLR_METHOD_NONE;
3329 if( ownerdata->nauxvaruses == 0 )
3330 enforcemethods |= SCIP_NLHDLR_METHOD_SEPABOTH;
3331 else
3332 {
3333 if( ownerdata->nlockspos == 0 ) /* no need for underestimation */
3334 enforcemethods |= SCIP_NLHDLR_METHOD_SEPABELOW;
3335 if( ownerdata->nlocksneg == 0 ) /* no need for overestimation */
3336 enforcemethods |= SCIP_NLHDLR_METHOD_SEPAABOVE;
3337 }
3338 if( ownerdata->nactivityusesprop == 0 && ownerdata->nactivityusessepa == 0 )
3339 enforcemethods |= SCIP_NLHDLR_METHOD_ACTIVITY;
3340
3341 /* it doesn't make sense to have been called on detectNlhdlr, if the expr isn't used for anything */
3342 assert(enforcemethods != SCIP_NLHDLR_METHOD_ALL);
3343
3344 /* all methods that have not been flagged above are the ones that we want to be handled by nlhdlrs */
3345 enforcemethodsallowed = ~enforcemethods & SCIP_NLHDLR_METHOD_ALL;
3346
3347 ownerdata->nenfos = 0;
3348 enfossize = 2;
3349 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &ownerdata->enfos, enfossize) );
3350 conshdlrdata->indetect = TRUE;
3351
3352 SCIPdebugMsg(scip, "detecting nlhdlrs for %s expression %p (%s); requiring%s%s%s\n",
3353 cons != NULL ? "root" : "non-root", (void*)expr, SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)),
3354 (enforcemethods & SCIP_NLHDLR_METHOD_SEPABELOW) != 0 ? "" : " sepabelow",
3355 (enforcemethods & SCIP_NLHDLR_METHOD_SEPAABOVE) != 0 ? "" : " sepaabove",
3356 (enforcemethods & SCIP_NLHDLR_METHOD_ACTIVITY) != 0 ? "" : " activity");
3357
3358 for( h = 0; h < conshdlrdata->nnlhdlrs; ++h )
3359 {
3360 SCIP_NLHDLR* nlhdlr;
3361
3362 nlhdlr = conshdlrdata->nlhdlrs[h];
3363 assert(nlhdlr != NULL);
3364
3365 /* skip disabled nlhdlrs */
3366 if( !SCIPnlhdlrIsEnabled(nlhdlr) )
3367 continue;
3368
3369 /* call detect routine of nlhdlr */
3370 nlhdlrexprdata = NULL;
3371 enforcemethodsnew = enforcemethods;
3372 nlhdlrparticipating = SCIP_NLHDLR_METHOD_NONE;
3373 conshdlrdata->registerusesactivitysepabelow = FALSE; /* SCIPregisterExprUsageNonlinear() as called by detect may set this to TRUE */
3374 conshdlrdata->registerusesactivitysepaabove = FALSE; /* SCIPregisterExprUsageNonlinear() as called by detect may set this to TRUE */
3375 /* coverity[var_deref_model] */
3376 SCIP_CALL( SCIPnlhdlrDetect(scip, ownerdata->conshdlr, nlhdlr, expr, cons, &enforcemethodsnew, &nlhdlrparticipating, &nlhdlrexprdata) );
3377
3378 /* nlhdlr might have claimed more than needed: clean up sepa flags */
3379 nlhdlrparticipating &= enforcemethodsallowed;
3380
3381 /* detection is only allowed to augment to nlhdlrenforcemethods, so previous enforcemethods must still be set */
3382 assert((enforcemethodsnew & enforcemethods) == enforcemethods);
3383
3384 /* Because of the previous assert, nlhdlrenforcenew ^ enforcemethods are the methods enforced by this nlhdlr.
3385 * They are also cleaned up here to ensure that only the needed methods are claimed.
3386 */
3387 nlhdlrenforcemethods = (enforcemethodsnew ^ enforcemethods) & enforcemethodsallowed;
3388
3389 /* nlhdlr needs to participate for the methods it is enforcing */
3390 assert((nlhdlrparticipating & nlhdlrenforcemethods) == nlhdlrenforcemethods);
3391
3392 if( nlhdlrparticipating == SCIP_NLHDLR_METHOD_NONE )
3393 {
3394 /* nlhdlr might not have detected anything, or all set flags might have been removed by
3395 * clean up; in the latter case, we may need to free nlhdlrexprdata */
3396
3397 /* free nlhdlr exprdata, if there is any and there is a method to free this data */
3398 if( nlhdlrexprdata != NULL )
3399 {
3400 SCIP_CALL( SCIPnlhdlrFreeexprdata(scip, nlhdlr, expr, &nlhdlrexprdata) );
3401 }
3402 /* nlhdlr cannot have added an enforcement method if it doesn't participate (actually redundant due to previous asserts) */
3403 assert(nlhdlrenforcemethods == SCIP_NLHDLR_METHOD_NONE);
3404
3405 SCIPdebugMsg(scip, "nlhdlr <%s> detect unsuccessful\n", SCIPnlhdlrGetName(nlhdlr));
3406
3407 continue;
3408 }
3409
3410 SCIPdebugMsg(scip, "nlhdlr <%s> detect successful; sepabelow: %s, sepaabove: %s, activity: %s\n",
3411 SCIPnlhdlrGetName(nlhdlr),
3412 ((nlhdlrenforcemethods & SCIP_NLHDLR_METHOD_SEPABELOW) != 0) ? "enforcing" : ((nlhdlrparticipating & SCIP_NLHDLR_METHOD_SEPABELOW) != 0) ? "participating" : "no",
3413 ((nlhdlrenforcemethods & SCIP_NLHDLR_METHOD_SEPAABOVE) != 0) ? "enforcing" : ((nlhdlrparticipating & SCIP_NLHDLR_METHOD_SEPAABOVE) != 0) ? "participating" : "no",
3414 ((nlhdlrenforcemethods & SCIP_NLHDLR_METHOD_ACTIVITY) != 0) ? "enforcing" : ((nlhdlrparticipating & SCIP_NLHDLR_METHOD_ACTIVITY) != 0) ? "participating" : "no");
3415
3416 /* store nlhdlr and its data */
3417 SCIP_CALL( SCIPensureBlockMemoryArray(scip, &ownerdata->enfos, &enfossize, ownerdata->nenfos+1) );
3418 SCIP_CALL( SCIPallocBlockMemory(scip, &ownerdata->enfos[ownerdata->nenfos]) );
3419 ownerdata->enfos[ownerdata->nenfos]->nlhdlr = nlhdlr;
3420 ownerdata->enfos[ownerdata->nenfos]->nlhdlrexprdata = nlhdlrexprdata;
3421 ownerdata->enfos[ownerdata->nenfos]->nlhdlrparticipation = nlhdlrparticipating;
3422 ownerdata->enfos[ownerdata->nenfos]->issepainit = FALSE;
3423 ownerdata->enfos[ownerdata->nenfos]->sepabelowusesactivity = conshdlrdata->registerusesactivitysepabelow;
3424 ownerdata->enfos[ownerdata->nenfos]->sepaaboveusesactivity = conshdlrdata->registerusesactivitysepaabove;
3425 ownerdata->nenfos++;
3426
3427 /* update enforcement flags */
3428 enforcemethods = enforcemethodsnew;
3429 }
3430
3431 conshdlrdata->indetect = FALSE;
3432
3433 /* stop if an enforcement method is missing but we are already in solving stage
3434 * (as long as the expression provides its callbacks, the default nlhdlr should have provided all enforcement methods)
3435 */
3436 if( enforcemethods != SCIP_NLHDLR_METHOD_ALL && SCIPgetStage(scip) == SCIP_STAGE_SOLVING )
3437 {
3438 SCIPerrorMessage("no nonlinear handler provided some of the required enforcement methods\n");
3439 return SCIP_ERROR;
3440 }
3441
3442 assert(ownerdata->nenfos > 0);
3443
3444 /* sort nonlinear handlers by enforcement priority, in decreasing order */
3445 if( ownerdata->nenfos > 1 )
3446 SCIPsortDownPtr((void**)ownerdata->enfos, enfodataCmp, ownerdata->nenfos);
3447
3448 /* resize enfos array to be nenfos long */
3449 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &ownerdata->enfos, enfossize, ownerdata->nenfos) );
3450
3451 return SCIP_OKAY;
3452}
3453
3454/** detect nlhdlrs that can handle the expressions */
3455static
3457 SCIP* scip, /**< SCIP data structure */
3458 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
3459 SCIP_CONS** conss, /**< constraints for which to run nlhdlr detect */
3460 int nconss /**< total number of constraints */
3461 )
3462{
3463 SCIP_CONSHDLRDATA* conshdlrdata;
3464 SCIP_CONSDATA* consdata;
3465 SCIP_EXPR* expr;
3466 SCIP_EXPR_OWNERDATA* ownerdata;
3467 SCIP_EXPRITER* it;
3468 int i;
3469
3470 assert(conss != NULL || nconss == 0);
3471 assert(nconss >= 0);
3472 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 */
3473
3474 conshdlrdata = SCIPconshdlrGetData(conshdlr);
3475 assert(conshdlrdata != NULL);
3476
3479
3481 {
3482 /* ensure that activities are recomputed w.r.t. the global variable bounds if CONSACTIVE is called in a local node;
3483 * for example, this happens if globally valid nonlinear constraints are added during the tree search
3484 */
3486 conshdlrdata->globalbounds = TRUE;
3487 conshdlrdata->lastvaractivitymethodchange = conshdlrdata->curboundstag;
3488 }
3489
3490 for( i = 0; i < nconss; ++i )
3491 {
3492 assert(conss != NULL && conss[i] != NULL);
3493
3494 consdata = SCIPconsGetData(conss[i]);
3495 assert(consdata != NULL);
3496 assert(consdata->expr != NULL);
3497
3498 /* if a constraint is separated, we currently need it to be initial, too
3499 * this is because INITLP will create the auxiliary variables that are used for any separation
3500 * TODO we may relax this with a little more programming effort when required, see also TODO in INITLP
3501 */
3502 assert((!SCIPconsIsSeparated(conss[i]) && !SCIPconsIsEnforced(conss[i])) || SCIPconsIsInitial(conss[i]));
3503
3504 ownerdata = SCIPexprGetOwnerData(consdata->expr);
3505 assert(ownerdata != NULL);
3506
3507 /* because of common sub-expressions it might happen that we already detected a nonlinear handler and added it to the expr
3508 * then we would normally skip to run DETECT again
3509 * HOWEVER: most likely we have been running DETECT with cons == NULL, which may interest less nlhdlrs
3510 * thus, if expr is the root expression, we rerun DETECT
3511 */
3512 if( ownerdata->nenfos > 0 )
3513 {
3514 SCIP_CALL( freeEnfoData(scip, consdata->expr, FALSE) );
3515 assert(ownerdata->nenfos < 0);
3516 }
3517
3518 /* if constraint will be enforced, and we are in solve, then ensure auxiliary variable for root expression
3519 * this way we can treat the root expression like any other expression when enforcing via separation
3520 * if constraint will be propagated, then register activity usage of root expression
3521 * this can trigger a call to forwardPropExpr, for which we better have the indetect flag set
3522 */
3523 conshdlrdata->indetect = TRUE;
3526 SCIPconsIsPropagated(conss[i]),
3527 FALSE, FALSE) );
3528 conshdlrdata->indetect = FALSE;
3529
3530 /* compute integrality information for all subexpressions */
3531 SCIP_CALL( SCIPcomputeExprIntegrality(scip, consdata->expr) );
3532
3533 /* run detectNlhdlr on all expr where required */
3534 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
3535 {
3536 ownerdata = SCIPexprGetOwnerData(expr);
3537 assert(ownerdata != NULL);
3538
3539 /* skip exprs that we already looked at */
3540 if( ownerdata->nenfos >= 0 )
3541 continue;
3542
3543 /* if there is use of the auxvar, then someone requires that
3544 * auxvar == expr (or auxvar >= expr or auxvar <= expr) or we are at the root expression (expr==consdata->expr)
3545 * thus, we need to find nlhdlrs that separate or estimate
3546 * if there is use of the activity, then there is someone requiring that
3547 * activity of this expression is updated; this someone would also benefit from better bounds on the activity of this expression
3548 * thus, we need to find nlhdlrs that do interval-evaluation
3549 */
3550 if( ownerdata->nauxvaruses > 0 || ownerdata->nactivityusesprop > 0 || ownerdata->nactivityusessepa > 0 )
3551 {
3552 SCIP_CALL( detectNlhdlr(scip, expr, expr == consdata->expr ? conss[i] : NULL) );
3553
3554 assert(ownerdata->nenfos >= 0);
3555 }
3556 else
3557 {
3558 /* remember that we looked at this expression during detectNlhdlrs
3559 * even though we have not actually run detectNlhdlr, because no nlhdlr showed interest in this expr,
3560 * in some situations (forwardPropExpr, to be specific) we will have to distinguish between exprs for which
3561 * we have not initialized enforcement yet (nenfos < 0) and expressions which are just not used in enforcement (nenfos == 0)
3562 */
3563 ownerdata->nenfos = 0;
3564 }
3565 }
3566
3567 /* include this constraint into the next propagation round because the added nlhdlr may do find tighter bounds now */
3568 if( SCIPconsIsPropagated(conss[i]) )
3569 consdata->ispropagated = FALSE;
3570 }
3571
3573 {
3574 /* ensure that the local bounds are used again when reevaluating the expressions later;
3575 * this is only needed if CONSACTIVE is called in a local node (see begin of this function)
3576 */
3578 conshdlrdata->globalbounds = FALSE;
3579 conshdlrdata->lastvaractivitymethodchange = conshdlrdata->curboundstag;
3580 }
3581 else
3582 {
3583 /* ensure that all activities (except for var-exprs) are reevaluated since better methods may be available now */
3585 }
3586
3587 SCIPfreeExpriter(&it);
3588
3589 return SCIP_OKAY;
3590}
3591
3592/** initializes (pre)solving data of constraints
3593 *
3594 * This initializes data in a constraint that is used for separation, propagation, etc, and assumes that expressions will
3595 * not be modified.
3596 * In particular, this function
3597 * - runs the detection method of nlhldrs
3598 * - looks for unlocked linear variables
3599 * - checks curvature (if not in presolve)
3600 * - creates and add row to NLP (if not in presolve)
3601 *
3602 * This function can be called in presolve and solve and can be called several times with different sets of constraints,
3603 * e.g., it should be called in INITSOL and for constraints that are added during solve.
3604 */
3605static
3607 SCIP* scip, /**< SCIP data structure */
3608 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
3609 SCIP_CONS** conss, /**< constraints */
3610 int nconss /**< number of constraints */
3611 )
3612{
3613 int c;
3614
3615 for( c = 0; c < nconss; ++c )
3616 {
3617 /* check for a linear variable that can be increase or decreased without harming feasibility */
3618 findUnlockedLinearVar(scip, conss[c]);
3619
3621 {
3622 SCIP_CONSDATA* consdata;
3623 SCIP_Bool success = FALSE;
3624
3625 consdata = SCIPconsGetData(conss[c]); /*lint !e613*/
3626 assert(consdata != NULL);
3627 assert(consdata->expr != NULL);
3628
3629 if( !SCIPconshdlrGetData(conshdlr)->assumeconvex )
3630 {
3631 /* call the curvature detection algorithm of the convex nonlinear handler
3632 * Check only for those curvature that may result in a convex inequality, i.e.,
3633 * whether f(x) is concave when f(x) >= lhs and/or f(x) is convex when f(x) <= rhs.
3634 * Also we can assume that we are nonlinear, so do not check for convex if already concave.
3635 */
3636 if( !SCIPisInfinity(scip, -consdata->lhs) )
3637 {
3638 SCIP_CALL( SCIPhasExprCurvature(scip, consdata->expr, SCIP_EXPRCURV_CONCAVE, &success, NULL) );
3639 if( success )
3640 consdata->curv = SCIP_EXPRCURV_CONCAVE;
3641 }
3642 if( !success && !SCIPisInfinity(scip, consdata->rhs) )
3643 {
3644 SCIP_CALL( SCIPhasExprCurvature(scip, consdata->expr, SCIP_EXPRCURV_CONVEX, &success, NULL) );
3645 if( success )
3646 consdata->curv = SCIP_EXPRCURV_CONVEX;
3647 }
3648 }
3649 else
3650 {
3651 if( !SCIPisInfinity(scip, -consdata->lhs) && !SCIPisInfinity(scip, consdata->rhs) )
3652 {
3653 SCIPwarningMessage(scip, "Nonlinear constraint <%s> has finite left- and right-hand side, but constraints/nonlinear/assumeconvex is enabled.\n", SCIPconsGetName(conss[c]));
3654 consdata->curv = SCIP_EXPRCURV_LINEAR;
3655 }
3656 else
3657 {
3658 consdata->curv = !SCIPisInfinity(scip, consdata->rhs) ? SCIP_EXPRCURV_CONVEX : SCIP_EXPRCURV_CONCAVE;
3659 }
3660 }
3661 SCIPdebugMsg(scip, "root curvature of constraint %s = %d\n", SCIPconsGetName(conss[c]), consdata->curv);
3662
3663 /* add nlrow representation to NLP, if NLP had been constructed */
3664 if( SCIPisNLPConstructed(scip) && SCIPconsIsActive(conss[c]) )
3665 {
3666 if( consdata->nlrow == NULL )
3667 {
3668 SCIP_CALL( createNlRow(scip, conss[c]) );
3669 assert(consdata->nlrow != NULL);
3670 }
3671 SCIPsetNlRowCurvature(scip, consdata->nlrow, consdata->curv);
3672 SCIP_CALL( SCIPaddNlRow(scip, consdata->nlrow) );
3673 }
3674 }
3675 }
3676
3677 /* register non linear handlers */
3678 SCIP_CALL( detectNlhdlrs(scip, conshdlr, conss, nconss) );
3679
3680 return SCIP_OKAY;
3681}
3682
3683/** deinitializes (pre)solving data of constraints
3684 *
3685 * This removes the initialization data created in initSolve().
3686 *
3687 * This function can be called in presolve and solve.
3688 *
3689 * TODO At the moment, it should not be called for a constraint if there are other constraints
3690 * that use the same expressions but still require their nlhdlr.
3691 * We should probably only decrement the auxvar and activity usage for the root expr and then
3692 * proceed as in detectNlhdlrs(), i.e., free enfo data only where none is used.
3693 */
3694static
3696 SCIP* scip, /**< SCIP data structure */
3697 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
3698 SCIP_CONS** conss, /**< constraints */
3699 int nconss /**< number of constraints */
3700 )
3701{
3702 SCIP_EXPRITER* it;
3703 SCIP_EXPR* expr;
3704 SCIP_CONSDATA* consdata;
3705 SCIP_Bool rootactivityvalid;
3706 int c;
3707
3711
3712 /* call deinitialization callbacks of expression and nonlinear handlers
3713 * free nonlinear handlers information from expressions
3714 * remove auxiliary variables and nactivityuses counts from expressions
3715 */
3716 for( c = 0; c < nconss; ++c )
3717 {
3718 assert(conss != NULL);
3719 assert(conss[c] != NULL);
3720
3721 consdata = SCIPconsGetData(conss[c]);
3722 assert(consdata != NULL);
3723 assert(consdata->expr != NULL);
3724
3725 /* check and remember whether activity in root is valid */
3726 rootactivityvalid = SCIPexprGetActivityTag(consdata->expr) >= SCIPconshdlrGetData(conshdlr)->lastboundrelax;
3727
3728 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
3729 {
3730 SCIPdebugMsg(scip, "exitsepa and free nonlinear handler data for expression %p\n", (void*)expr);
3731
3732 /* remove nonlinear handlers in expression and their data and auxiliary variables; reset activityusage count */
3733 SCIP_CALL( freeEnfoData(scip, expr, TRUE) );
3734
3735 /* remove quadratic info */
3737
3738 if( rootactivityvalid )
3739 {
3740 /* ensure activity is valid if consdata->expr activity is valid
3741 * this is mainly to ensure that we do not leave invalid activities in parts of the expression tree where activity was not used,
3742 * e.g., an expr's activity was kept up to date by a nlhdlr, but without using some childs activity
3743 * so this childs activity would be invalid, which can generate confusion
3744 */
3746 }
3747 }
3748
3749 if( consdata->nlrow != NULL )
3750 {
3751 /* remove row from NLP, if still in solving
3752 * if we are in exitsolve, the whole NLP will be freed anyway
3753 */
3755 {
3756 SCIP_CALL( SCIPdelNlRow(scip, consdata->nlrow) );
3757 }
3758
3759 SCIP_CALL( SCIPreleaseNlRow(scip, &consdata->nlrow) );
3760 }
3761
3762 /* forget about linear variables that can be increased or decreased without harming feasibility */
3763 consdata->linvardecr = NULL;
3764 consdata->linvarincr = NULL;
3765
3766 /* forget about curvature */
3767 consdata->curv = SCIP_EXPRCURV_UNKNOWN;
3768 }
3769
3770 SCIPfreeExpriter(&it);
3771
3772 return SCIP_OKAY;
3773}
3774
3775/** helper method to decide whether a given expression is product of at least two binary variables */
3776static
3778 SCIP* scip, /**< SCIP data structure */
3779 SCIP_EXPR* expr /**< expression */
3780 )
3781{
3782 int i;
3783
3784 assert(expr != NULL);
3785
3786 /* check whether the expression is a product */
3787 if( !SCIPisExprProduct(scip, expr) )
3788 return FALSE;
3789
3790 /* don't consider products with a coefficient != 1 and products with a single child
3791 * simplification will take care of this expression later
3792 */
3793 if( SCIPexprGetNChildren(expr) <= 1 || SCIPgetCoefExprProduct(expr) != 1.0 )
3794 return FALSE;
3795
3796 for( i = 0; i < SCIPexprGetNChildren(expr); ++i )
3797 {
3798 SCIP_EXPR* child;
3799 SCIP_VAR* var;
3800 SCIP_Real ub;
3801 SCIP_Real lb;
3802
3803 child = SCIPexprGetChildren(expr)[i];
3804 assert(child != NULL);
3805
3806 if( !SCIPisExprVar(scip, child) )
3807 return FALSE;
3808
3809 var = SCIPgetVarExprVar(child);
3810 lb = SCIPvarGetLbLocal(var);
3811 ub = SCIPvarGetUbLocal(var);
3812
3813 /* check whether variable is integer and has [0,1] as variable bounds */
3814 if( !SCIPvarIsIntegral(var) || !SCIPisEQ(scip, lb, 0.0) || !SCIPisEQ(scip, ub, 1.0) )
3815 return FALSE;
3816 }
3817
3818 return TRUE;
3819}
3820
3821/** helper method to collect all bilinear binary product terms */
3822static
3824 SCIP* scip, /**< SCIP data structure */
3825 SCIP_EXPR* sumexpr, /**< sum expression */
3826 SCIP_VAR** xs, /**< array to collect first variable of each bilinear binary product */
3827 SCIP_VAR** ys, /**< array to collect second variable of each bilinear binary product */
3828 int* childidxs, /**< array to store the index of the child of each stored bilinear binary product */
3829 int* nterms /**< pointer to store the total number of bilinear binary terms */
3830 )
3831{
3832 int i;
3833
3834 assert(sumexpr != NULL);
3835 assert(SCIPisExprSum(scip, sumexpr));
3836 assert(xs != NULL);
3837 assert(ys != NULL);
3838 assert(childidxs != NULL);
3839 assert(nterms != NULL);
3840
3841 *nterms = 0;
3842
3843 for( i = 0; i < SCIPexprGetNChildren(sumexpr); ++i )
3844 {
3845 SCIP_EXPR* child;
3846
3847 child = SCIPexprGetChildren(sumexpr)[i];
3848 assert(child != NULL);
3849
3850 if( SCIPexprGetNChildren(child) == 2 && isBinaryProduct(scip, child) )
3851 {
3854
3855 assert(x != NULL);
3856 assert(y != NULL);
3857
3858 if( x != y )
3859 {
3860 xs[*nterms] = x;
3861 ys[*nterms] = y;
3862 childidxs[*nterms] = i;
3863 ++(*nterms);
3864 }
3865 }
3866 }
3867
3868 return SCIP_OKAY;
3869}
3870
3871/** helper method to reformulate \f$x_i \sum_j c_{ij} x_j\f$ */
3872static
3874 SCIP* scip, /**< SCIP data structure */
3875 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
3876 SCIP_CONS* cons, /**< constraint */
3877 SCIP_VAR* facvar, /**< variable that has been factorized */
3878 SCIP_VAR** vars, /**< variables of sum_j c_ij x_j */
3879 SCIP_Real* coefs, /**< coefficients of sum_j c_ij x_j */
3880 int nvars, /**< total number of variables in sum_j c_ij x_j */
3881 SCIP_EXPR** newexpr, /**< pointer to store the new expression */
3882 int* naddconss /**< pointer to update the total number of added constraints (might be NULL) */
3883 )
3884{
3885 SCIP_VAR* auxvar;
3886 SCIP_CONS* newcons;
3887 SCIP_Real minact = 0.0;
3888 SCIP_Real maxact = 0.0;
3889 SCIP_Bool integral = TRUE;
3890 char name [SCIP_MAXSTRLEN];
3891 int i;
3892
3893 assert(facvar != NULL);
3894 assert(vars != NULL);
3895 assert(nvars > 1);
3896 assert(newexpr != NULL);
3897
3898 /* compute minimum and maximum activity of sum_j c_ij x_j */
3899 /* 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 */
3900 for( i = 0; i < nvars; ++i )
3901 {
3902 minact += MIN(coefs[i], 0.0);
3903 maxact += MAX(coefs[i], 0.0);
3904 integral = integral && SCIPisIntegral(scip, coefs[i]);
3905 }
3906 assert(minact <= maxact);
3907
3908 /* create and add auxiliary variable */
3909 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s", SCIPconsGetName(cons), SCIPvarGetName(facvar));
3910 SCIP_CALL( SCIPcreateVarBasic(scip, &auxvar, name, minact, maxact, 0.0, integral ? SCIP_VARTYPE_IMPLINT : SCIP_VARTYPE_CONTINUOUS) );
3911 SCIP_CALL( SCIPaddVar(scip, auxvar) );
3912
3913#ifdef WITH_DEBUG_SOLUTION
3914 if( SCIPdebugIsMainscip(scip) )
3915 {
3916 SCIP_Real debugsolval; /* value of auxvar in debug solution */
3917 SCIP_Real val;
3918
3919 /* compute value of new variable in debug solution */
3920 /* first \sum_j c_{ij} x_j (coefs[j] * vars[j]) */
3921 debugsolval = 0.0;
3922 for( i = 0; i < nvars; ++i )
3923 {
3924 SCIP_CALL( SCIPdebugGetSolVal(scip, vars[i], &val) );
3925 debugsolval += coefs[i] * val;
3926 }
3927
3928 /* now multiply by x_i (facvar) */
3929 SCIP_CALL( SCIPdebugGetSolVal(scip, facvar, &val) );
3930 debugsolval *= val;
3931
3932 /* store debug solution value of auxiliary variable */
3933 SCIP_CALL( SCIPdebugAddSolVal(scip, auxvar, debugsolval) );
3934 }
3935#endif
3936
3937 /* create and add z - maxact x <= 0 */
3938 if( !SCIPisZero(scip, maxact) )
3939 {
3940 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s_1", SCIPconsGetName(cons), SCIPvarGetName(facvar));
3941 SCIP_CALL( SCIPcreateConsBasicVarbound(scip, &newcons, name, auxvar, facvar, -maxact, -SCIPinfinity(scip), 0.0) );
3942 SCIP_CALL( SCIPaddCons(scip, newcons) );
3943 SCIP_CALL( SCIPreleaseCons(scip, &newcons) );
3944 if( naddconss != NULL )
3945 ++(*naddconss);
3946 }
3947
3948 /* create and add 0 <= z - minact x */
3949 if( !SCIPisZero(scip, minact) )
3950 {
3951 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s_2", SCIPconsGetName(cons), SCIPvarGetName(facvar));
3952 SCIP_CALL( SCIPcreateConsBasicVarbound(scip, &newcons, name, auxvar, facvar, -minact, 0.0, SCIPinfinity(scip)) );
3953 SCIP_CALL( SCIPaddCons(scip, newcons) );
3954 SCIP_CALL( SCIPreleaseCons(scip, &newcons) );
3955 if( naddconss != NULL )
3956 ++(*naddconss);
3957 }
3958
3959 /* create and add minact <= sum_j c_j x_j - z + minact x_i */
3960 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s_3", SCIPconsGetName(cons), SCIPvarGetName(facvar));
3961 SCIP_CALL( SCIPcreateConsBasicLinear(scip, &newcons, name, nvars, vars, coefs, minact, SCIPinfinity(scip)) );
3962 SCIP_CALL( SCIPaddCoefLinear(scip, newcons, auxvar, -1.0) );
3963 if( !SCIPisZero(scip, minact) )
3964 {
3965 SCIP_CALL( SCIPaddCoefLinear(scip, newcons, facvar, minact) );
3966 }
3967 SCIP_CALL( SCIPaddCons(scip, newcons) );
3968 SCIP_CALL( SCIPreleaseCons(scip, &newcons) );
3969 if( naddconss != NULL )
3970 ++(*naddconss);
3971
3972 /* create and add sum_j c_j x_j - z + maxact x_i <= maxact */
3973 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s_4", SCIPconsGetName(cons), SCIPvarGetName(facvar));
3974 SCIP_CALL( SCIPcreateConsBasicLinear(scip, &newcons, name, nvars, vars, coefs, -SCIPinfinity(scip), maxact) );
3975 SCIP_CALL( SCIPaddCoefLinear(scip, newcons, auxvar, -1.0) );
3976 if( !SCIPisZero(scip, maxact) )
3977 {
3978 SCIP_CALL( SCIPaddCoefLinear(scip, newcons, facvar, maxact) );
3979 }
3980 SCIP_CALL( SCIPaddCons(scip, newcons) );
3981 SCIP_CALL( SCIPreleaseCons(scip, &newcons) );
3982 if( naddconss != NULL )
3983 ++(*naddconss);
3984
3985 /* create variable expression */
3986 SCIP_CALL( createExprVar(scip, conshdlr, newexpr, auxvar) );
3987
3988 /* release auxvar */
3989 SCIP_CALL( SCIPreleaseVar(scip, &auxvar) );
3990
3991 return SCIP_OKAY;
3992}
3993
3994/** helper method to generate an expression for a sum of products of binary variables; note that the method captures the generated expression */
3995static
3997 SCIP* scip, /**< SCIP data structure */
3998 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
3999 SCIP_CONS* cons, /**< constraint */
4000 SCIP_EXPR* sumexpr, /**< expression */
4001 int minterms, /**< minimum number of terms in a the sum of x_i sum_j c_j x_j */
4002 SCIP_EXPR** newexpr, /**< pointer to store the expression that represents the binary quadratic */
4003 int* naddconss /**< pointer to update the total number of added constraints (might be NULL) */
4004 )
4005{
4006 SCIP_EXPR** exprs = NULL;
4007 SCIP_VAR** tmpvars = NULL;
4008 SCIP_VAR** vars = NULL;
4009 SCIP_VAR** xs = NULL;
4010 SCIP_VAR** ys = NULL;
4011 SCIP_Real* exprcoefs = NULL;
4012 SCIP_Real* tmpcoefs = NULL;
4013 SCIP_Real* sumcoefs;
4014 SCIP_Bool* isused = NULL;
4015 int* childidxs = NULL;
4016 int* count = NULL;
4017 int nchildren;
4018 int nexprs = 0;
4019 int nterms;
4020 int nvars;
4021 int ntotalvars;
4022 int i;
4023
4024 assert(sumexpr != NULL);
4025 assert(minterms > 1);
4026 assert(newexpr != NULL);
4027
4028 *newexpr = NULL;
4029
4030 /* check whether sumexpr is indeed a sum */
4031 if( !SCIPisExprSum(scip, sumexpr) )
4032 return SCIP_OKAY;
4033
4034 nchildren = SCIPexprGetNChildren(sumexpr);
4035 sumcoefs = SCIPgetCoefsExprSum(sumexpr);
4036 nvars = SCIPgetNVars(scip);
4037 ntotalvars = SCIPgetNTotalVars(scip);
4038
4039 /* check whether there are enough terms available */
4040 if( nchildren < minterms )
4041 return SCIP_OKAY;
4042
4043 /* allocate memory */
4044 SCIP_CALL( SCIPallocBufferArray(scip, &xs, nchildren) );
4045 SCIP_CALL( SCIPallocBufferArray(scip, &ys, nchildren) );
4046 SCIP_CALL( SCIPallocBufferArray(scip, &childidxs, nchildren) );
4047
4048 /* collect all bilinear binary product terms */
4049 SCIP_CALL( getBilinearBinaryTerms(scip, sumexpr, xs, ys, childidxs, &nterms) );
4050
4051 /* check whether there are enough terms available */
4052 if( nterms < minterms )
4053 goto TERMINATE;
4054
4055 /* store how often each variable appears in a bilinear binary product */
4057 SCIP_CALL( SCIPallocClearBufferArray(scip, &count, ntotalvars) );
4058 SCIP_CALL( SCIPallocClearBufferArray(scip, &isused, nchildren) );
4059
4060 SCIP_CALL( SCIPallocBufferArray(scip, &exprs, nchildren) );
4061 SCIP_CALL( SCIPallocBufferArray(scip, &exprcoefs, nchildren) );
4062 SCIP_CALL( SCIPallocBufferArray(scip, &tmpvars, MIN(nterms, nvars)) );
4063 SCIP_CALL( SCIPallocBufferArray(scip, &tmpcoefs, MIN(nterms, nvars)) );
4064
4065 for( i = 0; i < nterms; ++i )
4066 {
4067 int xidx;
4068 int yidx;
4069
4070 assert(xs[i] != NULL);
4071 assert(ys[i] != NULL);
4072
4073 xidx = SCIPvarGetIndex(xs[i]);
4074 assert(xidx < ntotalvars);
4075 yidx = SCIPvarGetIndex(ys[i]);
4076 assert(yidx < ntotalvars);
4077
4078 ++count[xidx];
4079 ++count[yidx];
4080
4081 SCIPdebugMsg(scip, "increase counter for %s to %d\n", SCIPvarGetName(xs[i]), count[xidx]);
4082 SCIPdebugMsg(scip, "increase counter for %s to %d\n", SCIPvarGetName(ys[i]), count[yidx]);
4083 }
4084
4085 /* sort variables; don't change order of count array because it depends on problem indices */
4086 {
4087 int* tmpcount;
4088
4089 SCIP_CALL( SCIPduplicateBufferArray(scip, &tmpcount, count, nvars) );
4090 SCIPsortDownIntPtr(tmpcount, (void**)vars, nvars);
4091 SCIPfreeBufferArray(scip, &tmpcount);
4092 }
4093
4094 for( i = 0; i < nvars; ++i )
4095 {
4096 SCIP_VAR* facvar = vars[i];
4097 int ntmpvars = 0;
4098 int j;
4099
4100 /* skip candidate if there are not enough terms left */
4101 if( count[SCIPvarGetIndex(vars[i])] < minterms )
4102 continue;
4103
4104 SCIPdebugMsg(scip, "consider facvar = %s with count = %d\n", SCIPvarGetName(facvar), count[SCIPvarGetIndex(vars[i])]);
4105
4106 /* collect variables for x_i * sum_j c_ij x_j */
4107 for( j = 0; j < nterms; ++j )
4108 {
4109 int childidx = childidxs[j];
4110 assert(childidx >= 0 && childidx < nchildren);
4111
4112 if( !isused[childidx] && (xs[j] == facvar || ys[j] == facvar) )
4113 {
4114 SCIP_Real coef;
4115 int xidx;
4116 int yidx;
4117
4118 coef = sumcoefs[childidx];
4119 assert(coef != 0.0);
4120
4121 /* collect corresponding variable */
4122 tmpvars[ntmpvars] = (xs[j] == facvar) ? ys[j] : xs[j];
4123 tmpcoefs[ntmpvars] = coef;
4124 ++ntmpvars;
4125
4126 /* update counters */
4127 xidx = SCIPvarGetIndex(xs[j]);
4128 assert(xidx < ntotalvars);
4129 yidx = SCIPvarGetIndex(ys[j]);
4130 assert(yidx < ntotalvars);
4131 --count[xidx];
4132 --count[yidx];
4133 assert(count[xidx] >= 0);
4134 assert(count[yidx] >= 0);
4135
4136 /* mark term to be used */
4137 isused[childidx] = TRUE;
4138 }
4139 }
4140 assert(ntmpvars >= minterms);
4141 assert(SCIPvarGetIndex(facvar) < ntotalvars);
4142 assert(count[SCIPvarGetIndex(facvar)] == 0); /* facvar should not appear in any other bilinear term */
4143
4144 /* create required constraints and store the generated expression */
4145 SCIP_CALL( reformulateFactorizedBinaryQuadratic(scip, conshdlr, cons, facvar, tmpvars, tmpcoefs, ntmpvars, &exprs[nexprs], naddconss) );
4146 exprcoefs[nexprs] = 1.0;
4147 ++nexprs;
4148 }
4149
4150 /* factorization was only successful if at least one expression has been generated */
4151 if( nexprs > 0 )
4152 {
4153 int nexprsold = nexprs;
4154
4155 /* add all children of the sum that have not been used */
4156 for( i = 0; i < nchildren; ++i )
4157 {
4158 if( !isused[i] )
4159 {
4160 exprs[nexprs] = SCIPexprGetChildren(sumexpr)[i];
4161 exprcoefs[nexprs] = sumcoefs[i];
4162 ++nexprs;
4163 }
4164 }
4165
4166 /* create a new sum expression */
4167 SCIP_CALL( SCIPcreateExprSum(scip, newexpr, nexprs, exprs, exprcoefs, SCIPgetConstantExprSum(sumexpr), exprownerCreate, (void*)conshdlr) );
4168
4169 /* release all expressions that have been generated by reformulateFactorizedBinaryQuadratic() */
4170 for( i = 0; i < nexprsold; ++i )
4171 {
4172 SCIP_CALL( SCIPreleaseExpr(scip, &exprs[i]) );
4173 }
4174 }
4175
4176TERMINATE:
4177 /* free memory */
4178 SCIPfreeBufferArrayNull(scip, &tmpcoefs);
4179 SCIPfreeBufferArrayNull(scip, &tmpvars);
4180 SCIPfreeBufferArrayNull(scip, &exprcoefs);
4183 SCIPfreeBufferArrayNull(scip, &isused);
4185 SCIPfreeBufferArray(scip, &childidxs);
4188
4189 return SCIP_OKAY;
4190}
4191
4192/** helper method to create an AND constraint or varbound constraints for a given binary product expression */
4193static
4195 SCIP* scip, /**< SCIP data structure */
4196 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
4197 SCIP_EXPR* prodexpr, /**< product expression */
4198 SCIP_EXPR** newexpr, /**< pointer to store the expression that represents the product */
4199 int* naddconss, /**< pointer to update the total number of added constraints (might be NULL) */
4200 SCIP_Bool empathy4and /**< whether to use an AND constraint, if possible */
4201 )
4202{
4203 SCIP_VAR** vars;
4204 SCIP_CONS* cons;
4205 SCIP_Real* coefs;
4206 SCIP_VAR* w;
4207 char* name;
4208 int nchildren;
4209 int i;
4210
4211 assert(conshdlr != NULL);
4212 assert(prodexpr != NULL);
4213 assert(SCIPisExprProduct(scip, prodexpr));
4214 assert(newexpr != NULL);
4215
4216 nchildren = SCIPexprGetNChildren(prodexpr);
4217 assert(nchildren >= 2);
4218
4219 /* memory to store the variables of the variable expressions (+1 for w) and their name */
4220 SCIP_CALL( SCIPallocBufferArray(scip, &vars, nchildren + 1) );
4221 SCIP_CALL( SCIPallocBufferArray(scip, &coefs, nchildren + 1) );
4222 SCIP_CALL( SCIPallocBufferArray(scip, &name, nchildren * (SCIP_MAXSTRLEN + 1) + 20) );
4223
4224 /* prepare the names of the variable and the constraints */
4225 /* coverity[secure_coding] */
4226 strcpy(name, "binreform");
4227 for( i = 0; i < nchildren; ++i )
4228 {
4229 vars[i] = SCIPgetVarExprVar(SCIPexprGetChildren(prodexpr)[i]);
4230 coefs[i] = 1.0;
4231 assert(vars[i] != NULL);
4232 (void) strcat(name, "_");
4233 (void) strcat(name, SCIPvarGetName(vars[i]));
4234 }
4235
4236 /* create and add variable */
4237 SCIP_CALL( SCIPcreateVarBasic(scip, &w, name, 0.0, 1.0, 0.0, SCIP_VARTYPE_IMPLINT) );
4239 SCIPdebugMsg(scip, " created auxiliary variable %s\n", name);
4240
4241#ifdef WITH_DEBUG_SOLUTION
4242 if( SCIPdebugIsMainscip(scip) )
4243 {
4244 SCIP_Real debugsolval; /* value of auxvar in debug solution */
4245 SCIP_Real val;
4246
4247 /* compute value of new variable in debug solution (\prod_i vars[i]) */
4248 debugsolval = 1.0;
4249 for( i = 0; i < nchildren; ++i )
4250 {
4251 SCIP_CALL( SCIPdebugGetSolVal(scip, vars[i], &val) );
4252 debugsolval *= val;
4253 }
4254
4255 /* store debug solution value of auxiliary variable */
4256 SCIP_CALL( SCIPdebugAddSolVal(scip, w, debugsolval) );
4257 }
4258#endif
4259
4260 /* use variable bound constraints if it is a bilinear product and there is no empathy for an AND constraint */
4261 if( nchildren == 2 && !empathy4and )
4262 {
4263 SCIP_VAR* x = vars[0];
4264 SCIP_VAR* y = vars[1];
4265
4266 assert(x != NULL);
4267 assert(y != NULL);
4268 assert(x != y);
4269
4270 /* create and add x - w >= 0 */
4271 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s_1", SCIPvarGetName(x), SCIPvarGetName(y));
4272 SCIP_CALL( SCIPcreateConsBasicVarbound(scip, &cons, name, x, w, -1.0, 0.0, SCIPinfinity(scip)) );
4273 SCIP_CALL( SCIPaddCons(scip, cons) );
4274 SCIP_CALL( SCIPreleaseCons(scip, &cons) );
4275
4276 /* create and add y - w >= 0 */
4277 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s_2", SCIPvarGetName(x), SCIPvarGetName(y));
4278 SCIP_CALL( SCIPcreateConsBasicVarbound(scip, &cons, name, y, w, -1.0, 0.0, SCIPinfinity(scip)) );
4279 SCIP_CALL( SCIPaddCons(scip, cons) );
4280 SCIP_CALL( SCIPreleaseCons(scip, &cons) );
4281
4282 /* create and add x + y - w <= 1 */
4283 vars[2] = w;
4284 coefs[2] = -1.0;
4285 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s_3", SCIPvarGetName(x), SCIPvarGetName(y));
4286 SCIP_CALL( SCIPcreateConsBasicLinear(scip, &cons, name, 3, vars, coefs, -SCIPinfinity(scip), 1.0) );
4287 SCIP_CALL( SCIPaddCons(scip, cons) );
4288 SCIP_CALL( SCIPreleaseCons(scip, &cons) );
4289
4290 /* update number of added constraints */
4291 if( naddconss != NULL )
4292 *naddconss += 3;
4293 }
4294 else
4295 {
4296 /* create, add, and release AND constraint */
4297 SCIP_CALL( SCIPcreateConsBasicAnd(scip, &cons, name, w, nchildren, vars) );
4298 SCIP_CALL( SCIPaddCons(scip, cons) );
4299 SCIP_CALL( SCIPreleaseCons(scip, &cons) );
4300 SCIPdebugMsg(scip, " create AND constraint\n");
4301
4302 /* update number of added constraints */
4303 if( naddconss != NULL )
4304 *naddconss += 1;
4305 }
4306
4307 /* create variable expression */
4308 SCIP_CALL( createExprVar(scip, conshdlr, newexpr, w) );
4309
4310 /* release created variable */
4312
4313 /* free memory */
4314 SCIPfreeBufferArray(scip, &name);
4315 SCIPfreeBufferArray(scip, &coefs);
4316 SCIPfreeBufferArray(scip, &vars);
4317
4318 return SCIP_OKAY;
4319}
4320
4321/** helper method to generate an expression for the product of binary variables; note that the method captures the generated expression */
4322static
4324 SCIP* scip, /**< SCIP data structure */
4325 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
4326 SCIP_HASHMAP* exprmap, /**< map to remember generated variables for visited product expressions */
4327 SCIP_EXPR* prodexpr, /**< product expression */
4328 SCIP_EXPR** newexpr, /**< pointer to store the expression that represents the product */
4329 int* naddconss, /**< pointer to update the total number of added constraints (might be NULL) */
4330 int* nchgcoefs /**< pointer to update the total number of changed coefficients (might be NULL) */
4331 )
4332{
4333 SCIP_CONSHDLRDATA* conshdlrdata;
4334 int nchildren;
4335
4336 assert(prodexpr != NULL);
4337 assert(newexpr != NULL);
4338
4339 *newexpr = NULL;
4340
4341 /* only consider products of binary variables */
4342 if( !isBinaryProduct(scip, prodexpr) )
4343 return SCIP_OKAY;
4344
4345 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4346 assert(conshdlrdata != NULL);
4347 nchildren = SCIPexprGetNChildren(prodexpr);
4348 assert(nchildren >= 2);
4349
4350 /* check whether there is already an expression that represents the product */
4351 if( SCIPhashmapExists(exprmap, (void*)prodexpr) )
4352 {
4353 *newexpr = (SCIP_EXPR*) SCIPhashmapGetImage(exprmap, (void*)prodexpr);
4354 assert(*newexpr != NULL);
4355
4356 /* capture expression */
4357 SCIPcaptureExpr(*newexpr);
4358 }
4359 else
4360 {
4361 SCIPdebugMsg(scip, " product expression %p has been considered for the first time\n", (void*)prodexpr);
4362
4363 if( nchildren == 2 )
4364 {
4365 SCIP_CLIQUE** xcliques;
4366 SCIP_VAR* x;
4367 SCIP_VAR* y;
4368 SCIP_Bool found_clique = FALSE;
4369 int c;
4370
4371 /* get variables from the product expression */
4372 x = SCIPgetVarExprVar(SCIPexprGetChildren(prodexpr)[0]);
4373 assert(x != NULL);
4374 y = SCIPgetVarExprVar(SCIPexprGetChildren(prodexpr)[1]);
4375 assert(y != NULL);
4376 assert(x != y);
4377
4378 /* first try to find a clique containing both variables */
4379 xcliques = SCIPvarGetCliques(x, TRUE);
4380
4381 /* look in cliques containing x */
4382 for( c = 0; c < SCIPvarGetNCliques(x, TRUE); ++c )
4383 {
4384 if( SCIPcliqueHasVar(xcliques[c], y, TRUE) ) /* x + y <= 1 => x*y = 0 */
4385 {
4386 /* create zero value expression */
4387 SCIP_CALL( SCIPcreateExprValue(scip, newexpr, 0.0, exprownerCreate, (void*)conshdlr) );
4388
4389 if( nchgcoefs != NULL )
4390 *nchgcoefs += 1;
4391
4392 found_clique = TRUE;
4393 break;
4394 }
4395
4396 if( SCIPcliqueHasVar(xcliques[c], y, FALSE) ) /* x + (1-y) <= 1 => x*y = x */
4397 {
4398 /* create variable expression for x */
4399 SCIP_CALL( createExprVar(scip, conshdlr, newexpr, x) );
4400
4401 if( nchgcoefs != NULL )
4402 *nchgcoefs += 2;
4403
4404 found_clique = TRUE;
4405 break;
4406 }
4407 }
4408
4409 if( !found_clique )
4410 {
4411 xcliques = SCIPvarGetCliques(x, FALSE);
4412
4413 /* look in cliques containing complement of x */
4414 for( c = 0; c < SCIPvarGetNCliques(x, FALSE); ++c )
4415 {
4416 if( SCIPcliqueHasVar(xcliques[c], y, TRUE) ) /* (1-x) + y <= 1 => x*y = y */
4417 {
4418 /* create variable expression for y */
4419 SCIP_CALL( createExprVar(scip, conshdlr, newexpr, y) );
4420
4421 if( nchgcoefs != NULL )
4422 *nchgcoefs += 1;
4423
4424 found_clique = TRUE;
4425 break;
4426 }
4427
4428 if( SCIPcliqueHasVar(xcliques[c], y, FALSE) ) /* (1-x) + (1-y) <= 1 => x*y = x + y - 1 */
4429 {
4430 /* create sum expression */
4431 SCIP_EXPR* sum_children[2];
4432 SCIP_Real sum_coefs[2];
4433 SCIP_CALL( createExprVar(scip, conshdlr, &sum_children[0], x) );
4434 SCIP_CALL( createExprVar(scip, conshdlr, &sum_children[1], y) );
4435 sum_coefs[0] = 1.0;
4436 sum_coefs[1] = 1.0;
4437 SCIP_CALL( SCIPcreateExprSum(scip, newexpr, 2, sum_children, sum_coefs, -1.0, exprownerCreate, (void*)conshdlr) );
4438
4439 SCIP_CALL( SCIPreleaseExpr(scip, &sum_children[0]) );
4440 SCIP_CALL( SCIPreleaseExpr(scip, &sum_children[1]) );
4441
4442 if( nchgcoefs != NULL )
4443 *nchgcoefs += 3;
4444
4445 found_clique = TRUE;
4446 break;
4447 }
4448 }
4449 }
4450
4451 /* if the variables are not in a clique, do standard linearization */
4452 if( !found_clique )
4453 {
4454 SCIP_CALL( getBinaryProductExprDo(scip, conshdlr, prodexpr, newexpr, naddconss, conshdlrdata->reformbinprodsand) );
4455 }
4456 }
4457 else
4458 {
4459 /* linearize binary product using an AND constraint because nchildren > 2 */
4460 SCIP_CALL( getBinaryProductExprDo(scip, conshdlr, prodexpr, newexpr, naddconss, conshdlrdata->reformbinprodsand) );
4461 }
4462
4463 /* hash variable expression */
4464 SCIP_CALL( SCIPhashmapInsert(exprmap, (void*)prodexpr, *newexpr) );
4465 }
4466
4467 return SCIP_OKAY;
4468}
4469
4470/** helper function to replace binary products in a given constraint */
4471static
4473 SCIP* scip, /**< SCIP data structure */
4474 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
4475 SCIP_CONS* cons, /**< constraint */
4476 SCIP_HASHMAP* exprmap, /**< map to remember generated variables for visited product expressions */
4477 SCIP_EXPRITER* it, /**< expression iterator */
4478 int* naddconss, /**< pointer to update the total number of added constraints (might be NULL) */
4479 int* nchgcoefs /**< pointer to update the total number of changed coefficients (might be NULL) */
4480 )
4481{
4482 SCIP_CONSHDLRDATA* conshdlrdata;
4483 SCIP_CONSDATA* consdata;
4484 SCIP_EXPR* expr;
4485
4486 assert(conshdlr != NULL);
4487 assert(cons != NULL);
4488 assert(exprmap != NULL);
4489 assert(it != NULL);
4490
4491 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4492 assert(conshdlrdata != NULL);
4493
4494 consdata = SCIPconsGetData(cons);
4495 assert(consdata != NULL);
4496 assert(consdata->expr != NULL);
4497
4498 SCIPdebugMsg(scip, " check constraint %s\n", SCIPconsGetName(cons));
4499
4500 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
4501 {
4502 SCIP_EXPR* newexpr = NULL;
4503 SCIP_EXPR* childexpr;
4504 int childexpridx;
4505
4506 childexpridx = SCIPexpriterGetChildIdxDFS(it);
4507 assert(childexpridx >= 0 && childexpridx < SCIPexprGetNChildren(expr));
4508 childexpr = SCIPexpriterGetChildExprDFS(it);
4509 assert(childexpr != NULL);
4510
4511 /* try to factorize variables in a sum expression that contains several products of binary variables */
4512 if( conshdlrdata->reformbinprodsfac > 1 )
4513 {
4514 SCIP_CALL( getFactorizedBinaryQuadraticExpr(scip, conshdlr, cons, childexpr, conshdlrdata->reformbinprodsfac, &newexpr, naddconss) );
4515 }
4516
4517 /* try to create an expression that represents a product of binary variables */
4518 if( newexpr == NULL )
4519 {
4520 SCIP_CALL( getBinaryProductExpr(scip, conshdlr, exprmap, childexpr, &newexpr, naddconss, nchgcoefs) );
4521 }
4522
4523 if( newexpr != NULL )
4524 {
4525 assert(naddconss == NULL || *naddconss > 0 || nchgcoefs == NULL || *nchgcoefs > 0);
4526
4527 /* replace product expression */
4528 SCIP_CALL( SCIPreplaceExprChild(scip, expr, childexpridx, newexpr) );
4529
4530 /* note that the expression has been captured by getBinaryProductExpr and SCIPreplaceExprChild */
4531 SCIP_CALL( SCIPreleaseExpr(scip, &newexpr) );
4532
4533 /* mark the constraint to not be simplified anymore */
4534 consdata->issimplified = FALSE;
4535 }
4536 }
4537
4538 return SCIP_OKAY;
4539}
4540
4541/** reformulates products of binary variables during presolving in the following way:
4542 *
4543 * Let \f$\sum_{i,j} Q_{ij} x_i x_j\f$ be a subexpression that only contains binary variables.
4544 * 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}:
4545 * \f[
4546 * z_{ij} \leq x_i, \qquad z_{ij} \leq x_j, \qquad x_i + x_j - z_{ij} \leq 1.
4547 * \f]
4548 *
4549 * 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$.
4550 * These cliques allow for a better reformulation. There are four cases:
4551 *
4552 * 1. \f$x_i + x_j \leq 1\f$ implies that \f$x_i x_j = 0\f$
4553 * 2. \f$x_i + (1 - x_j) \leq 1\f$ implies \f$x_i x_j = x_i\f$
4554 * 3. \f$(1 - x_i) + x_j \leq 1\f$ implies \f$x_i x_j = x_j\f$
4555 * 4. \f$(1 - x_i) + (1 - x_j) \leq 1\f$ implies \f$x_i x_j = x_i + x_j - 1\f$
4556 *
4557 * The reformulation using \f$z_{ij}\f$ or the cliques is implemented in getBinaryProductExpr().
4558 *
4559 * Introducing too many extra variables and constraints can have a negative impact on the performance (e.g., due to
4560 * slow probing). For this reason, it is checked in getFactorizedBinaryQuadraticExpr() whether \f$\sum_{i,j} Q_{ij} x_i x_j\f$
4561 * contains large (&ge; `reformbinprodsfac` parameter) lower sums of the form \f$x_i \sum_j Q_{ij} x_j\f$.
4562 * Such a lower sum is reformulated with only one extra variable w_i:
4563 * \f{align}{
4564 * \text{maxact} & := \sum_j \max(0, Q_{ij}), \\
4565 * \text{minact} & := \sum_j \min(0, Q_{ij}), \\
4566 * \text{minact}\, x_i & \leq w_i, \\
4567 * w_i &\leq \text{maxact}\, x_i, \\
4568 * \text{minact} &\leq \sum_j Q_{ij} x_j - w_i + \text{minact}\, x_i \\
4569 * \text{maxact} &\geq \sum_j Q_{ij} x_j - w_i + \text{maxact}\, x_i
4570 * \f}
4571 * 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
4572 * is checked whether there are enough terms left to factorize other binary variables. Lower sums with a larger number
4573 * of terms are prioritized.
4574 */
4575static
4577 SCIP* scip, /**< SCIP data structure */
4578 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
4579 SCIP_CONS** conss, /**< constraints */
4580 int nconss, /**< total number of constraints */
4581 int* naddconss, /**< pointer to store the total number of added constraints (might be NULL) */
4582 int* nchgcoefs /**< pointer to store the total number of changed coefficients (might be NULL) */
4583 )
4584{
4585 SCIP_CONSHDLRDATA* conshdlrdata;
4586 SCIP_HASHMAP* exprmap;
4587 SCIP_EXPRITER* it;
4588 int c;
4589
4590 assert(conshdlr != NULL);
4591
4592 /* no nonlinear constraints or binary variables -> skip */
4593 if( nconss == 0 || SCIPgetNBinVars(scip) == 0 )
4594 return SCIP_OKAY;
4595 assert(conss != NULL);
4596
4597 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4598 assert(conshdlrdata != NULL);
4599
4600 /* create expression hash map */
4602
4603 /* create expression iterator */
4607
4608 SCIPdebugMsg(scip, "call presolveBinaryProducts()\n");
4609
4610 for( c = 0; c < nconss; ++c )
4611 {
4612 SCIP_CONSDATA* consdata;
4613 SCIP_EXPR* newexpr = NULL;
4614
4615 assert(conss[c] != NULL);
4616
4617 consdata = SCIPconsGetData(conss[c]);
4618 assert(consdata != NULL);
4619
4620 /* try to reformulate the root expression */
4621 if( conshdlrdata->reformbinprodsfac > 1 )
4622 {
4623 SCIP_CALL( getFactorizedBinaryQuadraticExpr(scip, conshdlr, conss[c], consdata->expr, conshdlrdata->reformbinprodsfac, &newexpr, naddconss) );
4624 }
4625
4626 /* release the root node if another expression has been found */
4627 if( newexpr != NULL )
4628 {
4629 SCIP_CALL( SCIPreleaseExpr(scip, &consdata->expr) );
4630 consdata->expr = newexpr;
4631
4632 /* mark constraint to be not simplified anymore */
4633 consdata->issimplified = FALSE;
4634 }
4635
4636 /* replace each product of binary variables separately */
4637 SCIP_CALL( replaceBinaryProducts(scip, conshdlr, conss[c], exprmap, it, naddconss, nchgcoefs) );
4638 }
4639
4640 /* free memory */
4641 SCIPhashmapFree(&exprmap);
4642 SCIPfreeExpriter(&it);
4643
4644 return SCIP_OKAY;
4645}
4646
4647/** scales the sides of the constraint \f$\ell \leq \sum_i c_i f_i(x) \leq r\f$.
4648 *
4649 * Let \f$n_+\f$ the number of positive coefficients \f$c_i\f$ and \f$n_-\f$ be the number of negative coefficients.
4650 * Then scale by -1 if
4651 * - \f$n_+ < n_-\f$, or
4652 * - \f$n_+ = n_-\f$ and \f$r = \infty\f$.
4653 */
4654static
4656 SCIP* scip, /**< SCIP data structure */
4657 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */
4658 SCIP_CONS* cons, /**< nonlinear constraint */
4659 SCIP_Bool* changed /**< buffer to store if the expression of cons changed */
4660 )
4661{
4662 SCIP_CONSDATA* consdata;
4663 int i;
4664
4665 assert(cons != NULL);
4666
4667 consdata = SCIPconsGetData(cons);
4668 assert(consdata != NULL);
4669
4670 if( SCIPisExprSum(scip, consdata->expr) )
4671 {
4672 SCIP_Real* coefs;
4673 SCIP_Real constant;
4674 int nchildren;
4675 int counter = 0;
4676
4677 coefs = SCIPgetCoefsExprSum(consdata->expr);
4678 constant = SCIPgetConstantExprSum(consdata->expr);
4679 nchildren = SCIPexprGetNChildren(consdata->expr);
4680
4681 /* handle special case when constraint is l <= -f(x) <= r and f(x) not a sum: simplfy ensures f is not a sum */
4682 if( nchildren == 1 && constant == 0.0 && coefs[0] == -1.0 )
4683 {
4684 SCIP_EXPR* expr;
4685 expr = consdata->expr;
4686
4687 consdata->expr = SCIPexprGetChildren(expr)[0];
4688 assert(!SCIPisExprSum(scip, consdata->expr));
4689
4690 SCIPcaptureExpr(consdata->expr);
4691
4692 SCIPswapReals(&consdata->lhs, &consdata->rhs);
4693 consdata->lhs = -consdata->lhs;
4694 consdata->rhs = -consdata->rhs;
4695
4696 SCIP_CALL( SCIPreleaseExpr(scip, &expr) );
4697 *changed = TRUE;
4698 return SCIP_OKAY;
4699 }
4700
4701 /* compute n_+ - n_i */
4702 for( i = 0; i < nchildren; ++i )
4703 counter += coefs[i] > 0 ? 1 : -1;
4704
4705 if( counter < 0 || (counter == 0 && SCIPisInfinity(scip, consdata->rhs)) )
4706 {
4707 SCIP_EXPR* expr;
4708 SCIP_Real* newcoefs;
4709
4710 /* allocate memory */
4711 SCIP_CALL( SCIPallocBufferArray(scip, &newcoefs, nchildren) );
4712
4713 for( i = 0; i < nchildren; ++i )
4714 newcoefs[i] = -coefs[i];
4715
4716 /* create a new sum expression */
4717 SCIP_CALL( SCIPcreateExprSum(scip, &expr, nchildren, SCIPexprGetChildren(consdata->expr), newcoefs, -constant, exprownerCreate, (void*)conshdlr) );
4718
4719 /* replace expression in constraint data and scale sides */
4720 SCIP_CALL( SCIPreleaseExpr(scip, &consdata->expr) );
4721 consdata->expr = expr;
4722 SCIPswapReals(&consdata->lhs, &consdata->rhs);
4723 consdata->lhs = -consdata->lhs;
4724 consdata->rhs = -consdata->rhs;
4725
4726 /* free memory */
4727 SCIPfreeBufferArray(scip, &newcoefs);
4728
4729 *changed = TRUE;
4730 }
4731 }
4732
4733 return SCIP_OKAY;
4734}
4735
4736/** forbid multiaggrations of variables that appear nonlinear in constraints */
4737static
4739 SCIP* scip, /**< SCIP data structure */
4740 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
4741 SCIP_CONS** conss, /**< constraints */
4742 int nconss /**< number of constraints */
4743 )
4744{
4745 SCIP_EXPRITER* it;
4746 SCIP_CONSDATA* consdata;
4747 SCIP_EXPR* expr;
4748 int c;
4749
4750 assert(scip != NULL);
4751 assert(conshdlr != NULL);
4752
4753 if( !SCIPconshdlrGetData(conshdlr)->forbidmultaggrnlvar )
4754 return SCIP_OKAY;
4755
4758
4759 for( c = 0; c < nconss; ++c )
4760 {
4761 consdata = SCIPconsGetData(conss[c]);
4762 assert(consdata != NULL);
4763
4764 /* if root expression is sum, then forbid multiaggregation only for variables that are not in linear terms of sum,
4765 * i.e., skip children of sum that are variables
4766 */
4767 if( SCIPisExprSum(scip, consdata->expr) )
4768 {
4769 int i;
4770 SCIP_EXPR* child;
4771 for( i = 0; i < SCIPexprGetNChildren(consdata->expr); ++i )
4772 {
4773 child = SCIPexprGetChildren(consdata->expr)[i];
4774
4775 /* skip variable expression, as they correspond to a linear term */
4776 if( SCIPisExprVar(scip, child) )
4777 continue;
4778
4779 for( expr = SCIPexpriterRestartDFS(it, child); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
4780 if( SCIPisExprVar(scip, expr) )
4781 {
4783 }
4784 }
4785 }
4786 else
4787 {
4788 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
4789 if( SCIPisExprVar(scip, expr) )
4790 {
4792 }
4793 }
4794 }
4795
4796 SCIPfreeExpriter(&it);
4797
4798 return SCIP_OKAY;
4799}
4800
4801/** simplifies expressions and replaces common subexpressions for a set of constraints
4802 * @todo put the constant to the constraint sides
4803 */
4804static
4806 SCIP* scip, /**< SCIP data structure */
4807 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
4808 SCIP_CONS** conss, /**< constraints */
4809 int nconss, /**< total number of constraints */
4810 SCIP_PRESOLTIMING presoltiming, /**< presolve timing (SCIP_PRESOLTIMING_ALWAYS if not in presolving) */
4811 SCIP_Bool* infeasible, /**< buffer to store whether infeasibility has been detected */
4812 int* ndelconss, /**< counter to add number of deleted constraints, or NULL */
4813 int* naddconss, /**< counter to add number of added constraints, or NULL */
4814 int* nchgcoefs /**< counter to add number of changed coefficients, or NULL */
4815 )
4816{
4817 SCIP_CONSHDLRDATA* conshdlrdata;
4818 SCIP_CONSDATA* consdata;
4819 int* nlockspos;
4820 int* nlocksneg;
4821 SCIP_Bool havechange;
4822 int i;
4823
4824 assert(scip != NULL);
4825 assert(conshdlr != NULL);
4826 assert(conss != NULL);
4827 assert(nconss > 0);
4828 assert(infeasible != NULL);
4829
4830 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4831 assert(conshdlrdata != NULL);
4832
4833 /* update number of canonicalize calls */
4834 ++(conshdlrdata->ncanonicalizecalls);
4835
4836 SCIP_CALL( SCIPstartClock(scip, conshdlrdata->canonicalizetime) );
4837
4838 *infeasible = FALSE;
4839
4840 /* set havechange to TRUE in the first call of canonicalize; otherwise we might not replace common subexpressions */
4841 havechange = conshdlrdata->ncanonicalizecalls == 1;
4842
4843 /* free nonlinear handlers information from expressions */ /* TODO can skip this in first presolve round */
4844 SCIP_CALL( deinitSolve(scip, conshdlr, conss, nconss) );
4845
4846 /* allocate memory for storing locks of each constraint */
4847 SCIP_CALL( SCIPallocBufferArray(scip, &nlockspos, nconss) );
4848 SCIP_CALL( SCIPallocBufferArray(scip, &nlocksneg, nconss) );
4849
4850 /* unlock all constraints */
4851 for( i = 0; i < nconss; ++i )
4852 {
4853 assert(conss[i] != NULL);
4854
4855 consdata = SCIPconsGetData(conss[i]);
4856 assert(consdata != NULL);
4857
4858 /* remember locks */
4859 nlockspos[i] = consdata->nlockspos;
4860 nlocksneg[i] = consdata->nlocksneg;
4861
4862 /* remove locks */
4863 SCIP_CALL( addLocks(scip, conss[i], -consdata->nlockspos, -consdata->nlocksneg) );
4864 assert(consdata->nlockspos == 0);
4865 assert(consdata->nlocksneg == 0);
4866 }
4867
4868#ifndef NDEBUG
4869 /* check whether all locks of each expression have been removed */
4870 for( i = 0; i < nconss; ++i )
4871 {
4872 SCIP_EXPR* expr;
4873 SCIP_EXPRITER* it;
4874
4876
4877 consdata = SCIPconsGetData(conss[i]);
4878 assert(consdata != NULL);
4879
4881 for( expr = consdata->expr; !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
4882 {
4883 assert(expr != NULL);
4884 assert(SCIPexprGetOwnerData(expr)->nlocksneg == 0);
4885 assert(SCIPexprGetOwnerData(expr)->nlockspos == 0);
4886 }
4887 SCIPfreeExpriter(&it);
4888 }
4889#endif
4890
4891 /* reformulate products of binary variables */
4892 if( conshdlrdata->reformbinprods && SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING
4893 && (presoltiming & SCIP_PRESOLTIMING_EXHAUSTIVE) )
4894 {
4895 int tmpnaddconss = 0;
4896 int tmpnchgcoefs = 0;
4897
4898 /* call this function before simplification because expressions might not be simplified after reformulating
4899 * binary products; the detection of some nonlinear handlers might assume that expressions are simplified
4900 */
4901 SCIP_CALL( presolveBinaryProducts(scip, conshdlr, conss, nconss, &tmpnaddconss, &tmpnchgcoefs) );
4902
4903 /* update counters */
4904 if( naddconss != NULL )
4905 *naddconss = tmpnaddconss;
4906 if( nchgcoefs != NULL )
4907 *nchgcoefs = tmpnchgcoefs;
4908
4909 /* check whether at least one expression has changed */
4910 if( tmpnaddconss + tmpnchgcoefs > 0 )
4911 havechange = TRUE;
4912 }
4913
4914 for( i = 0; i < nconss; ++i )
4915 {
4916 consdata = SCIPconsGetData(conss[i]);
4917 assert(consdata != NULL);
4918
4919 /* call simplify for each expression */
4920 if( !consdata->issimplified && consdata->expr != NULL )
4921 {
4922 SCIP_EXPR* simplified;
4923 SCIP_Bool changed;
4924
4925 changed = FALSE;
4926 SCIP_CALL( SCIPsimplifyExpr(scip, consdata->expr, &simplified, &changed, infeasible, exprownerCreate, (void*)conshdlr) );
4927 consdata->issimplified = TRUE;
4928
4929 if( changed )
4930 havechange = TRUE;
4931
4932 /* 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").
4933 * If root expression did not change, some subexpression may still have changed, but the locks were taking care of in the corresponding SCIPreplaceExprChild() call.
4934 */
4935 if( simplified != consdata->expr )
4936 {
4937 assert(changed);
4938
4939 /* release old expression */
4940 SCIP_CALL( SCIPreleaseExpr(scip, &consdata->expr) );
4941
4942 /* store simplified expression */
4943 consdata->expr = simplified;
4944 }
4945 else
4946 {
4947 /* The simplify captures simplified in any case, also if nothing has changed.
4948 * Therefore, we have to release it here.
4949 */
4950 SCIP_CALL( SCIPreleaseExpr(scip, &simplified) );
4951 }
4952
4953 if( *infeasible )
4954 break;
4955
4956 /* scale constraint sides */
4957 SCIP_CALL( scaleConsSides(scip, conshdlr, conss[i], &changed) );
4958
4959 if( changed )
4960 havechange = TRUE;
4961
4962 /* handle constant root expression; either the problem is infeasible or the constraint is redundant */
4963 if( SCIPisExprValue(scip, consdata->expr) )
4964 {
4965 SCIP_Real value = SCIPgetValueExprValue(consdata->expr);
4966 if( (!SCIPisInfinity(scip, -consdata->lhs) && SCIPisFeasNegative(scip, value - consdata->lhs)) ||
4967 (!SCIPisInfinity(scip, consdata->rhs) && SCIPisFeasPositive(scip, value - consdata->rhs)) )
4968 {
4969 SCIPdebugMsg(scip, "<%s> with constant expression found infeasible\n", SCIPconsGetName(conss[i]));
4970 SCIPdebugPrintCons(scip, conss[i], NULL);
4971 *infeasible = TRUE;
4972 break;
4973 }
4974 else
4975 {
4976 SCIP_CALL( addLocks(scip, conss[i], nlockspos[i], nlocksneg[i]) );
4977 SCIP_CALL( SCIPdelCons(scip, conss[i]) );
4978 if( ndelconss != NULL )
4979 ++*ndelconss;
4980 havechange = TRUE;
4981 }
4982 }
4983 }
4984 }
4985
4986 /* replace common subexpressions */
4987 if( havechange && !*infeasible )
4988 {
4989 SCIP_CONS** consssorted;
4990 SCIP_EXPR** rootexprs;
4991 SCIP_Bool replacedroot;
4992
4993 SCIP_CALL( SCIPallocBufferArray(scip, &rootexprs, nconss) );
4994 for( i = 0; i < nconss; ++i )
4995 rootexprs[i] = SCIPconsGetData(conss[i])->expr;
4996
4997 SCIP_CALL( SCIPreplaceCommonSubexpressions(scip, rootexprs, nconss, &replacedroot) );
4998
4999 /* update pointer to root expr in constraints, if any has changed
5000 * SCIPreplaceCommonSubexpressions will have released the old expr and captures the new one
5001 */
5002 if( replacedroot )
5003 for( i = 0; i < nconss; ++i )
5004 SCIPconsGetData(conss[i])->expr = rootexprs[i];
5005
5006 SCIPfreeBufferArray(scip, &rootexprs);
5007
5008 /* TODO this is a possibly expensive way to update the variable expressions stored inside an expression which might have
5009 * been changed after simplification; now we completely recollect all variable expression and variable events
5010 */
5011
5012 /* Each variable stores the constraints for which it catched varbound events sorted by the constraint index.
5013 * Thus, for performance reasons, it is better to call dropVarEvents in descending order of constraint index.
5014 */
5015 SCIP_CALL( SCIPduplicateBufferArray(scip, &consssorted, conss, nconss) );
5016 SCIPsortPtr((void**)consssorted, compIndexConsNonlinear, nconss);
5017
5018 for( i = nconss-1; i >= 0; --i )
5019 {
5020 assert(i == 0 || compIndexConsNonlinear((void*)consssorted[i-1], (void*)consssorted[i]) < 0);
5021 if( SCIPconsIsDeleted(consssorted[i]) )
5022 continue;
5023
5024 SCIP_CALL( dropVarEvents(scip, conshdlrdata->eventhdlr, consssorted[i]) );
5025 SCIP_CALL( freeVarExprs(scip, SCIPconsGetData(consssorted[i])) );
5026 }
5027 for( i = 0; i < nconss; ++i )
5028 {
5029 if( SCIPconsIsDeleted(consssorted[i]) )
5030 continue;
5031
5032 SCIP_CALL( storeVarExprs(scip, conshdlr, SCIPconsGetData(consssorted[i])) );
5033 SCIP_CALL( catchVarEvents(scip, conshdlrdata->eventhdlr, consssorted[i]) );
5034 }
5035
5036 SCIPfreeBufferArray(scip, &consssorted);
5037
5038 /* forbid multiaggregation for nonlinear variables again (in case new variables appeared now)
5039 * a multiaggregation of a nonlinear variable can yield to a large increase in expressions due to
5040 * expanding terms in simplify, e.g. ,(sum_i x_i)^2, so we just forbid these
5041 */
5042 SCIP_CALL( forbidNonlinearVariablesMultiaggration(scip, conshdlr, conss, nconss) );
5043 }
5044
5045 /* restore locks */
5046 for( i = 0; i < nconss; ++i )
5047 {
5048 if( SCIPconsIsDeleted(conss[i]) )
5049 continue;
5050
5051 SCIP_CALL( addLocks(scip, conss[i], nlockspos[i], nlocksneg[i]) );
5052 }
5053
5054 /* run nlhdlr detect if in presolving stage (that is, not in exitpre)
5055 * TODO can we skip this in presoltiming fast?
5056 */
5057 if( SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING && !*infeasible )
5058 {
5059 /* reset one of the number of detections counter to count only current presolving round */
5060 for( i = 0; i < conshdlrdata->nnlhdlrs; ++i )
5061 SCIPnlhdlrResetNDetectionslast(conshdlrdata->nlhdlrs[i]);
5062
5063 SCIP_CALL( initSolve(scip, conshdlr, conss, nconss) );
5064 }
5065
5066 /* free allocated memory */
5067 SCIPfreeBufferArray(scip, &nlocksneg);
5068 SCIPfreeBufferArray(scip, &nlockspos);
5069
5070 SCIP_CALL( SCIPstopClock(scip, conshdlrdata->canonicalizetime) );
5071
5072 return SCIP_OKAY;
5073}
5074
5075/** merges constraints that have the same root expression */
5076static
5078 SCIP* scip, /**< SCIP data structure */
5079 SCIP_CONS** conss, /**< constraints to process */
5080 int nconss, /**< number of constraints */
5081 SCIP_Bool* success /**< pointer to store whether at least one constraint could be deleted */
5082 )
5083{
5084 SCIP_HASHMAP* expr2cons;
5085 SCIP_Bool* updatelocks;
5086 int* nlockspos;
5087 int* nlocksneg;
5088 int c;
5089
5090 assert(success != NULL);
5091
5092 *success =