1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 2 /* */ 3 /* This file is part of the program and library */ 4 /* SCIP --- Solving Constraint Integer Programs */ 5 /* */ 6 /* Copyright (c) 2002-2023 Zuse Institute Berlin (ZIB) */ 7 /* */ 8 /* Licensed under the Apache License, Version 2.0 (the "License"); */ 9 /* you may not use this file except in compliance with the License. */ 10 /* You may obtain a copy of the License at */ 11 /* */ 12 /* http://www.apache.org/licenses/LICENSE-2.0 */ 13 /* */ 14 /* Unless required by applicable law or agreed to in writing, software */ 15 /* distributed under the License is distributed on an "AS IS" BASIS, */ 16 /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ 17 /* See the License for the specific language governing permissions and */ 18 /* limitations under the License. */ 19 /* */ 20 /* You should have received a copy of the Apache-2.0 license */ 21 /* along with SCIP; see the file LICENSE. If not visit scipopt.org. */ 22 /* */ 23 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 24 25 /**@file cons_nonlinear.c 26 * @ingroup DEFPLUGINS_CONS 27 * @brief constraint handler for nonlinear constraints specified by algebraic expressions 28 * @author Ksenia Bestuzheva 29 * @author Benjamin Mueller 30 * @author Felipe Serrano 31 * @author Stefan Vigerske 32 */ 33 34 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/ 35 36 #ifdef SCIP_DEBUG 37 #define ENFO_LOGGING 38 #endif 39 40 /* enable to get log output for enforcement */ 41 /* #define ENFO_LOGGING */ 42 /* define to get enforcement logging into file */ 43 /* #define ENFOLOGFILE "consexpr_enfo.log" */ 44 45 /* define to get more debug output from domain propagation */ 46 /* #define DEBUG_PROP */ 47 48 /*lint -e440*/ 49 /*lint -e441*/ 50 /*lint -e528*/ 51 /*lint -e666*/ 52 /*lint -e777*/ 53 /*lint -e866*/ 54 55 #include <ctype.h> 56 #include "scip/cons_nonlinear.h" 57 #include "scip/nlhdlr.h" 58 #include "scip/expr_var.h" 59 #include "scip/expr_varidx.h" 60 #include "scip/expr_abs.h" 61 #include "scip/expr_sum.h" 62 #include "scip/expr_value.h" 63 #include "scip/expr_pow.h" 64 #include "scip/expr_trig.h" 65 #include "scip/nlhdlr_convex.h" 66 #include "scip/cons_linear.h" 67 #include "scip/cons_varbound.h" 68 #include "scip/cons_and.h" 69 #include "scip/cons_bounddisjunction.h" 70 #include "scip/heur_subnlp.h" 71 #include "scip/heur_trysol.h" 72 #include "scip/lapack_calls.h" 73 #include "scip/debug.h" 74 #include "scip/dialog_default.h" 75 #include "scip/scip_expr.h" 76 #include "scip/symmetry_graph.h" 77 #include "scip/prop_symmetry.h" 78 #include "symmetry/struct_symmetry.h" 79 #include "scip/pub_misc_sort.h" 80 81 82 /* fundamental constraint handler properties */ 83 #define CONSHDLR_NAME "nonlinear" 84 #define CONSHDLR_DESC "handler for nonlinear constraints specified by algebraic expressions" 85 #define CONSHDLR_ENFOPRIORITY -60 /**< priority of the constraint handler for constraint enforcing */ 86 #define CONSHDLR_CHECKPRIORITY -4000010 /**< priority of the constraint handler for checking feasibility */ 87 #define CONSHDLR_EAGERFREQ 100 /**< frequency for using all instead of only the useful constraints in separation, 88 * propagation and enforcement, -1 for no eager evaluations, 0 for first only */ 89 #define CONSHDLR_NEEDSCONS TRUE /**< should the constraint handler be skipped, if no constraints are available? */ 90 91 /* optional constraint handler properties */ 92 #define CONSHDLR_SEPAPRIORITY 10 /**< priority of the constraint handler for separation */ 93 #define CONSHDLR_SEPAFREQ 1 /**< frequency for separating cuts; zero means to separate only in the root node */ 94 #define CONSHDLR_DELAYSEPA FALSE /**< should separation method be delayed, if other separators found cuts? */ 95 96 #define CONSHDLR_PROPFREQ 1 /**< frequency for propagating domains; zero means only preprocessing propagation */ 97 #define CONSHDLR_DELAYPROP FALSE /**< should propagation method be delayed, if other propagators found reductions? */ 98 #define CONSHDLR_PROP_TIMING SCIP_PROPTIMING_BEFORELP /**< propagation timing mask of the constraint handler*/ 99 100 #define CONSHDLR_PRESOLTIMING SCIP_PRESOLTIMING_ALWAYS /**< presolving timing of the constraint handler (fast, medium, or exhaustive) */ 101 #define CONSHDLR_MAXPREROUNDS -1 /**< maximal number of presolving rounds the constraint handler participates in (-1: no limit) */ 102 103 /* properties of the nonlinear constraint handler statistics table */ 104 #define TABLE_NAME_NONLINEAR "cons_nonlinear" 105 #define TABLE_DESC_NONLINEAR "nonlinear constraint handler statistics" 106 #define TABLE_POSITION_NONLINEAR 14600 /**< the position of the statistics table */ 107 #define TABLE_EARLIEST_STAGE_NONLINEAR SCIP_STAGE_TRANSFORMED /**< output of the statistics table is only printed from this stage onwards */ 108 109 /* properties of the nonlinear handler statistics table */ 110 #define TABLE_NAME_NLHDLR "nlhdlr" 111 #define TABLE_DESC_NLHDLR "nonlinear handler statistics" 112 #define TABLE_POSITION_NLHDLR 14601 /**< the position of the statistics table */ 113 #define TABLE_EARLIEST_STAGE_NLHDLR SCIP_STAGE_PRESOLVING /**< output of the statistics table is only printed from this stage onwards */ 114 115 #define DIALOG_NAME "nlhdlrs" 116 #define DIALOG_DESC "display nonlinear handlers" 117 #define DIALOG_ISSUBMENU FALSE 118 119 #define VERTEXPOLY_MAXPERTURBATION 1e-3 /**< maximum perturbation */ 120 #define VERTEXPOLY_USEDUALSIMPLEX TRUE /**< use dual or primal simplex algorithm? */ 121 #define VERTEXPOLY_RANDNUMINITSEED 20181029 /**< seed for random number generator, which is used to move points away from the boundary */ 122 #define VERTEXPOLY_ADJUSTFACETFACTOR 1e1 /**< adjust resulting facets in checkRikun() up to a violation of this value times lpfeastol */ 123 124 #define BRANCH_RANDNUMINITSEED 20191229 /**< seed for random number generator, which is used to select from several similar good branching candidates */ 125 126 #define BILIN_MAXNAUXEXPRS 10 /**< maximal number of auxiliary expressions per bilinear term */ 127 128 /** translate from one value of infinity to another 129 * 130 * if val is ≥ infty1, then give infty2, else give val 131 */ 132 #define infty2infty(infty1, infty2, val) ((val) >= (infty1) ? (infty2) : (val)) 133 134 /** translates x to 2^x for non-negative integer x */ 135 #define POWEROFTWO(x) (0x1u << (x)) 136 137 #ifdef ENFO_LOGGING 138 #define ENFOLOG(x) if( SCIPgetSubscipDepth(scip) == 0 && SCIPgetVerbLevel(scip) >= SCIP_VERBLEVEL_NORMAL ) { x } 139 FILE* enfologfile = NULL; 140 #else 141 #define ENFOLOG(x) 142 #endif 143 144 /* 145 * Data structures 146 */ 147 148 /** enforcement data of an expression */ 149 typedef struct 150 { 151 SCIP_NLHDLR* nlhdlr; /**< nonlinear handler */ 152 SCIP_NLHDLREXPRDATA* nlhdlrexprdata; /**< data of nonlinear handler */ 153 SCIP_NLHDLR_METHOD nlhdlrparticipation;/**< methods where nonlinear handler participates */ 154 SCIP_Bool issepainit; /**< was the initsepa callback of nlhdlr called */ 155 SCIP_Real auxvalue; /**< auxiliary value of expression w.r.t. currently enforced solution */ 156 SCIP_Bool sepabelowusesactivity;/**< whether sepabelow uses activity of some expression */ 157 SCIP_Bool sepaaboveusesactivity;/**< whether sepaabove uses activity of some expression */ 158 } EXPRENFO; 159 160 /** data stored by constraint handler in an expression that belongs to a nonlinear constraint */ 161 struct SCIP_Expr_OwnerData 162 { 163 SCIP_CONSHDLR* conshdlr; /** nonlinear constraint handler */ 164 165 /* locks and monotonicity */ 166 int nlockspos; /**< positive locks counter */ 167 int nlocksneg; /**< negative locks counter */ 168 SCIP_MONOTONE* monotonicity; /**< array containing monotonicity of expression w.r.t. each child */ 169 int monotonicitysize; /**< length of monotonicity array */ 170 171 /* propagation (in addition to activity that is stored in expr) */ 172 SCIP_INTERVAL propbounds; /**< bounds to propagate in reverse propagation */ 173 unsigned int propboundstag; /**< tag to indicate whether propbounds are valid for the current propagation rounds */ 174 SCIP_Bool inpropqueue; /**< whether expression is queued for propagation */ 175 176 /* enforcement of expr == auxvar (or expr <= auxvar, or expr >= auxvar) */ 177 EXPRENFO** enfos; /**< enforcements */ 178 int nenfos; /**< number of enforcements, or -1 if not initialized */ 179 unsigned int lastenforced; /**< last enforcement round where expression was enforced successfully */ 180 unsigned int nactivityusesprop; /**< number of nonlinear handlers whose activity computation (or domain propagation) depends on the activity of the expression */ 181 unsigned int nactivityusessepa; /**< number of nonlinear handlers whose separation (estimate or enfo) depends on the activity of the expression */ 182 unsigned int nauxvaruses; /**< number of nonlinear handlers whose separation uses an auxvar in the expression */ 183 SCIP_VAR* auxvar; /**< auxiliary variable used for outer approximation cuts */ 184 185 /* branching */ 186 SCIP_Real violscoresum; /**< sum of violation scores for branching stored for this expression */ 187 SCIP_Real violscoremax; /**< max of violation scores for branching stored for this expression */ 188 int nviolscores; /**< number of violation scores stored for this expression */ 189 unsigned int violscoretag; /**< tag to decide whether a violation score of an expression needs to be initialized */ 190 191 /* additional data for variable expressions (TODO move into sub-struct?) */ 192 SCIP_CONS** conss; /**< constraints in which this variable appears */ 193 int nconss; /**< current number of constraints in conss */ 194 int consssize; /**< length of conss array */ 195 SCIP_Bool consssorted; /**< is the array of constraints sorted */ 196 197 int filterpos; /**< position of eventdata in SCIP's event filter, -1 if not catching events */ 198 }; 199 200 /** constraint data for nonlinear constraints */ 201 struct SCIP_ConsData 202 { 203 /* data that defines the constraint: expression and sides */ 204 SCIP_EXPR* expr; /**< expression that represents this constraint */ 205 SCIP_Real lhs; /**< left-hand side */ 206 SCIP_Real rhs; /**< right-hand side */ 207 208 /* variables */ 209 SCIP_EXPR** varexprs; /**< array containing all variable expressions */ 210 int nvarexprs; /**< total number of variable expressions */ 211 SCIP_Bool catchedevents; /**< do we catch events on variables? */ 212 213 /* constraint violation */ 214 SCIP_Real lhsviol; /**< violation of left-hand side by current solution */ 215 SCIP_Real rhsviol; /**< violation of right-hand side by current solution */ 216 SCIP_Real gradnorm; /**< norm of gradient of constraint function in current solution (if evaluated) */ 217 SCIP_Longint gradnormsoltag; /**< tag of solution used that gradnorm corresponds to */ 218 219 /* status flags */ 220 unsigned int ispropagated:1; /**< did we propagate the current bounds already? */ 221 unsigned int issimplified:1; /**< did we simplify the expression tree already? */ 222 223 /* locks */ 224 int nlockspos; /**< number of positive locks */ 225 int nlocksneg; /**< number of negative locks */ 226 227 /* repair infeasible solutions */ 228 SCIP_VAR* linvardecr; /**< variable that may be decreased without making any other constraint infeasible, or NULL if none */ 229 SCIP_VAR* linvarincr; /**< variable that may be increased without making any other constraint infeasible, or NULL if none */ 230 SCIP_Real linvardecrcoef; /**< linear coefficient of linvardecr */ 231 SCIP_Real linvarincrcoef; /**< linear coefficient of linvarincr */ 232 233 /* miscellaneous */ 234 SCIP_EXPRCURV curv; /**< curvature of the root expression w.r.t. the original variables */ 235 SCIP_NLROW* nlrow; /**< a nonlinear row representation of this constraint */ 236 int consindex; /**< an index of the constraint that is unique among all expr-constraints in this SCIP instance and is constant */ 237 }; 238 239 /** constraint upgrade method */ 240 typedef struct 241 { 242 SCIP_DECL_NONLINCONSUPGD((*consupgd)); /**< method to call for upgrading nonlinear constraint */ 243 int priority; /**< priority of upgrading method */ 244 SCIP_Bool active; /**< is upgrading enabled */ 245 } CONSUPGRADE; 246 247 /** constraint handler data */ 248 struct SCIP_ConshdlrData 249 { 250 /* nonlinear handler */ 251 SCIP_NLHDLR** nlhdlrs; /**< nonlinear handlers */ 252 int nnlhdlrs; /**< number of nonlinear handlers */ 253 int nlhdlrssize; /**< size of nlhdlrs array */ 254 SCIP_Bool indetect; /**< whether we are currently in detectNlhdlr */ 255 SCIP_Bool registerusesactivitysepabelow; /**< a flag that is used only during \ref @detectNlhdlr() */ 256 SCIP_Bool registerusesactivitysepaabove; /**< a flag that is used only during \ref @detectNlhdlr() */ 257 258 /* constraint upgrades */ 259 CONSUPGRADE** consupgrades; /**< constraint upgrade methods for specializing nonlinear constraints */ 260 int consupgradessize; /**< size of consupgrades array */ 261 int nconsupgrades; /**< number of constraint upgrade methods */ 262 263 /* other plugins */ 264 SCIP_EVENTHDLR* eventhdlr; /**< handler for variable bound change events */ 265 SCIP_HEUR* subnlpheur; /**< a pointer to the subnlp heuristic, if available */ 266 SCIP_HEUR* trysolheur; /**< a pointer to the trysol heuristic, if available */ 267 268 /* tags and counters */ 269 int auxvarid; /**< unique id for the next auxiliary variable */ 270 SCIP_Longint curboundstag; /**< tag indicating current variable bounds */ 271 SCIP_Longint lastboundrelax; /**< tag when bounds where most recently relaxed */ 272 SCIP_Longint lastvaractivitymethodchange; /**< tag when method used to evaluate activity of variables changed last */ 273 unsigned int enforound; /**< total number of enforcement calls, including current one */ 274 int lastconsindex; /**< last used consindex, plus one */ 275 276 /* activity intervals and domain propagation */ 277 SCIP_DECL_EXPR_INTEVALVAR((*intevalvar)); /**< method currently used for activity calculation of variable expressions */ 278 SCIP_Bool globalbounds; /**< whether global variable bounds should be used for activity calculation */ 279 SCIP_QUEUE* reversepropqueue; /**< expression queue to be used in reverse propagation, filled by SCIPtightenExprIntervalNonlinear */ 280 SCIP_Bool forceboundtightening; /**< whether bound change passed to SCIPtightenExprIntervalNonlinear should be forced */ 281 unsigned int curpropboundstag; /**< tag indicating current propagation rounds, to match with expr->propboundstag */ 282 283 /* parameters */ 284 int maxproprounds; /**< limit on number of propagation rounds for a set of constraints within one round of SCIP propagation */ 285 SCIP_Bool propauxvars; /**< whether to check bounds of all auxiliary variable to seed reverse propagation */ 286 char varboundrelax; /**< strategy on how to relax variable bounds during bound tightening */ 287 SCIP_Real varboundrelaxamount; /**< by how much to relax variable bounds during bound tightening */ 288 SCIP_Real conssiderelaxamount; /**< by how much to relax constraint sides during bound tightening */ 289 SCIP_Real vp_maxperturb; /**< maximal relative perturbation of reference point */ 290 SCIP_Real vp_adjfacetthreshold; /**< adjust computed facet up to a violation of this value times lpfeastol */ 291 SCIP_Bool vp_dualsimplex; /**< whether to use dual simplex instead of primal simplex for facet computing LP */ 292 SCIP_Bool reformbinprods; /**< whether to reformulate products of binary variables during presolving */ 293 SCIP_Bool reformbinprodsand; /**< whether to use the AND constraint handler for reformulating binary products */ 294 int reformbinprodsfac; /**< minimum number of terms to reformulate bilinear binary products by factorizing variables (<= 1: disabled) */ 295 SCIP_Bool forbidmultaggrnlvar; /**< whether to forbid multiaggregation of variables that appear in a nonlinear term of a constraint */ 296 SCIP_Bool tightenlpfeastol; /**< whether to tighten LP feasibility tolerance during enforcement, if it seems useful */ 297 SCIP_Bool propinenforce; /**< whether to (re)run propagation in enforcement */ 298 SCIP_Real weakcutthreshold; /**< threshold for when to regard a cut from an estimator as weak */ 299 SCIP_Real strongcutmaxcoef; /**< "strong" cuts will be scaled to have their maximal coef in [1/strongcutmaxcoef,strongcutmaxcoef] */ 300 SCIP_Bool strongcutefficacy; /**< consider efficacy requirement when deciding whether a cut is "strong" */ 301 SCIP_Bool forcestrongcut; /**< whether to force "strong" cuts in enforcement */ 302 SCIP_Real enfoauxviolfactor; /**< an expression will be enforced if the "auxiliary" violation is at least enfoauxviolfactor times the "original" violation */ 303 SCIP_Real weakcutminviolfactor; /**< retry with weak cuts for constraints with violation at least this factor of maximal violated constraints */ 304 char rownotremovable; /**< whether to make rows to be non-removable in the node where they are added (can prevent some cycling): 'o'ff, in 'e'nforcement only, 'a'lways */ 305 char violscale; /**< method how to scale violations to make them comparable (not used for feasibility check) */ 306 char checkvarlocks; /**< whether variables contained in a single constraint should be forced to be at their lower or upper bounds ('d'isable, change 't'ype, add 'b'ound disjunction) */ 307 int branchauxmindepth; /**< from which depth on to allow branching on auxiliary variables */ 308 SCIP_Bool branchexternal; /**< whether to use external branching candidates for branching */ 309 SCIP_Real branchhighviolfactor; /**< consider a constraint highly violated if its violation is >= this factor * maximal violation among all constraints */ 310 SCIP_Real branchhighscorefactor; /**< consider a variable branching score high if its branching score >= this factor * maximal branching score among all variables */ 311 SCIP_Real branchviolweight; /**< weight by how much to consider the violation assigned to a variable for its branching score */ 312 SCIP_Real branchdualweight; /**< weight by how much to consider the dual values of rows that contain a variable for its branching score */ 313 SCIP_Real branchpscostweight; /**< weight by how much to consider the pseudo cost of a variable for its branching score */ 314 SCIP_Real branchdomainweight; /**< weight by how much to consider the domain width in branching score */ 315 SCIP_Real branchvartypeweight;/**< weight by how much to consider variable type in branching score */ 316 char branchscoreagg; /**< how to aggregate several branching scores given for the same expression ('a'verage, 'm'aximum, or 's'um) */ 317 char branchviolsplit; /**< method used to split violation in expression onto variables ('u'niform, 'm'idness of solution, 'd'omain width, 'l'ogarithmic domain width) */ 318 SCIP_Real branchpscostreliable; /**< minimum pseudo-cost update count required to consider pseudo-costs reliable */ 319 char linearizeheursol; /**< whether tight linearizations of nonlinear constraints should be added to cutpool when some heuristics finds a new solution ('o'ff, on new 'i'ncumbents, on 'e'very solution) */ 320 SCIP_Bool assumeconvex; /**< whether to assume that any constraint is convex */ 321 322 /* statistics */ 323 SCIP_Longint nweaksepa; /**< number of times we used "weak" cuts for enforcement */ 324 SCIP_Longint ntightenlp; /**< number of times we requested solving the LP with a smaller feasibility tolerance when enforcing */ 325 SCIP_Longint ndesperatetightenlp; /**< number of times we requested solving the LP with a smaller feasibility tolerance when enforcing because we didn't know anything better */ 326 SCIP_Longint ndesperatebranch; /**< number of times we branched on some variable because normal enforcement was not successful */ 327 SCIP_Longint ndesperatecutoff; /**< number of times we cut off a node in enforcement because no branching candidate could be found */ 328 SCIP_Longint nforcelp; /**< number of times we forced solving the LP when enforcing a pseudo solution */ 329 SCIP_CLOCK* canonicalizetime; /**< time spend for canonicalization */ 330 SCIP_Longint ncanonicalizecalls; /**< number of times we called canonicalization */ 331 332 /* facets of envelops of vertex-polyhedral functions */ 333 SCIP_RANDNUMGEN* vp_randnumgen; /**< random number generator used to perturb reference point */ 334 SCIP_LPI* vp_lp[SCIP_MAXVERTEXPOLYDIM+1]; /**< LPs used to compute facets for functions of different dimension */ 335 336 /* hashing of bilinear terms */ 337 SCIP_HASHTABLE* bilinhashtable; /**< hash table for bilinear terms */ 338 SCIP_CONSNONLINEAR_BILINTERM* bilinterms; /**< bilinear terms */ 339 int nbilinterms; /**< total number of bilinear terms */ 340 int bilintermssize; /**< size of bilinterms array */ 341 int bilinmaxnauxexprs; /**< maximal number of auxiliary expressions per bilinear term */ 342 343 /* branching */ 344 SCIP_RANDNUMGEN* branchrandnumgen; /**< random number generated used in branching variable selection */ 345 char branchpscostupdatestrategy; /**< value of parameter branching/lpgainnormalize */ 346 347 /* misc */ 348 SCIP_Bool checkedvarlocks; /**< whether variables contained in a single constraint have been already considered */ 349 SCIP_HASHMAP* var2expr; /**< hashmap to map SCIP variables to variable-expressions */ 350 int newsoleventfilterpos; /**< filter position of new solution event handler, if caught */ 351 }; 352 353 /** branching candidate with various scores */ 354 typedef struct 355 { 356 SCIP_EXPR* expr; /**< expression that holds branching candidate */ 357 SCIP_Real auxviol; /**< aux-violation score of candidate */ 358 SCIP_Real domain; /**< domain score of candidate */ 359 SCIP_Real dual; /**< dual score of candidate */ 360 SCIP_Real pscost; /**< pseudo-cost score of candidate */ 361 SCIP_Real vartype; /**< variable type score of candidate */ 362 SCIP_Real weighted; /**< weighted sum of other scores, see scoreBranchingCandidates() */ 363 } BRANCHCAND; 364 365 /* 366 * Local methods 367 */ 368 369 /* forward declaration */ 370 static 371 SCIP_RETCODE forwardPropExpr( 372 SCIP* scip, /**< SCIP data structure */ 373 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 374 SCIP_EXPR* rootexpr, /**< expression */ 375 SCIP_Bool tightenauxvars, /**< should the bounds of auxiliary variables be tightened? */ 376 SCIP_Bool* infeasible, /**< buffer to store whether the problem is infeasible (NULL if not needed) */ 377 int* ntightenings /**< buffer to store the number of auxiliary variable tightenings (NULL if not needed) */ 378 ); 379 380 /** frees auxiliary variables of expression, if any */ 381 static 382 SCIP_RETCODE freeAuxVar( 383 SCIP* scip, /**< SCIP data structure */ 384 SCIP_EXPR* expr /**< expression which auxvar to free, if any */ 385 ) 386 { 387 SCIP_EXPR_OWNERDATA* mydata; 388 389 assert(scip != NULL); 390 assert(expr != NULL); 391 392 mydata = SCIPexprGetOwnerData(expr); 393 assert(mydata != NULL); 394 395 if( mydata->auxvar == NULL ) 396 return SCIP_OKAY; 397 398 SCIPdebugMsg(scip, "remove auxiliary variable <%s> for expression %p\n", SCIPvarGetName(mydata->auxvar), (void*)expr); 399 400 /* remove variable locks 401 * as this is a relaxation-only variable, no other plugin should use it for deducing any type of reductions or cutting planes 402 */ 403 SCIP_CALL( SCIPaddVarLocks(scip, mydata->auxvar, -1, -1) ); 404 405 /* release auxiliary variable */ 406 SCIP_CALL( SCIPreleaseVar(scip, &mydata->auxvar) ); 407 assert(mydata->auxvar == NULL); 408 409 return SCIP_OKAY; 410 } 411 412 /** frees data used for enforcement of expression, that is, nonlinear handlers 413 * 414 * can also clear indicators whether expr needs enforcement methods, that is, 415 * free an associated auxiliary variable and reset the nactivityuses counts 416 */ 417 static 418 SCIP_RETCODE freeEnfoData( 419 SCIP* scip, /**< SCIP data structure */ 420 SCIP_EXPR* expr, /**< expression whose enforcement data will be released */ 421 SCIP_Bool freeauxvar /**< whether aux var should be released and activity usage counts be reset */ 422 ) 423 { 424 SCIP_EXPR_OWNERDATA* mydata; 425 int e; 426 427 mydata = SCIPexprGetOwnerData(expr); 428 assert(mydata != NULL); 429 430 if( freeauxvar ) 431 { 432 /* free auxiliary variable */ 433 SCIP_CALL( freeAuxVar(scip, expr) ); 434 assert(mydata->auxvar == NULL); 435 436 /* reset count on activity and auxvar usage */ 437 mydata->nactivityusesprop = 0; 438 mydata->nactivityusessepa = 0; 439 mydata->nauxvaruses = 0; 440 } 441 442 /* free data stored by nonlinear handlers */ 443 for( e = 0; e < mydata->nenfos; ++e ) 444 { 445 SCIP_NLHDLR* nlhdlr; 446 447 assert(mydata->enfos[e] != NULL); 448 449 nlhdlr = mydata->enfos[e]->nlhdlr; 450 assert(nlhdlr != NULL); 451 452 if( mydata->enfos[e]->issepainit ) 453 { 454 /* call the separation deinitialization callback of the nonlinear handler */ 455 SCIP_CALL( SCIPnlhdlrExitsepa(scip, nlhdlr, expr, mydata->enfos[e]->nlhdlrexprdata) ); 456 mydata->enfos[e]->issepainit = FALSE; 457 } 458 459 /* free nlhdlr exprdata, if there is any and there is a method to free this data */ 460 if( mydata->enfos[e]->nlhdlrexprdata != NULL ) 461 { 462 SCIP_CALL( SCIPnlhdlrFreeexprdata(scip, nlhdlr, expr, &mydata->enfos[e]->nlhdlrexprdata) ); 463 assert(mydata->enfos[e]->nlhdlrexprdata == NULL); 464 } 465 466 /* free enfo data */ 467 SCIPfreeBlockMemory(scip, &mydata->enfos[e]); 468 } 469 470 /* free array with enfo data */ 471 SCIPfreeBlockMemoryArrayNull(scip, &mydata->enfos, mydata->nenfos); 472 473 /* we need to look at this expression in detect again */ 474 mydata->nenfos = -1; 475 476 return SCIP_OKAY; 477 } 478 479 /** callback that frees data that this conshdlr stored in an expression */ 480 static 481 SCIP_DECL_EXPR_OWNERFREE(exprownerFree) 482 { 483 assert(scip != NULL); 484 assert(expr != NULL); 485 assert(ownerdata != NULL); 486 assert(*ownerdata != NULL); 487 488 /* expression should not be locked anymore */ 489 assert((*ownerdata)->nlockspos == 0); 490 assert((*ownerdata)->nlocksneg == 0); 491 492 SCIP_CALL( freeEnfoData(scip, expr, TRUE) ); 493 494 /* expression should not be enforced anymore */ 495 assert((*ownerdata)->nenfos <= 0); 496 assert((*ownerdata)->auxvar == NULL); 497 498 if( SCIPisExprVar(scip, expr) ) 499 { 500 SCIP_CONSHDLRDATA* conshdlrdata; 501 SCIP_VAR* var; 502 503 /* there should be no constraints left that still use this variable */ 504 assert((*ownerdata)->nconss == 0); 505 /* thus, there should also be no variable event catched (via this exprhdlr) */ 506 assert((*ownerdata)->filterpos == -1); 507 508 SCIPfreeBlockMemoryArrayNull(scip, &(*ownerdata)->conss, (*ownerdata)->consssize); 509 510 /* update var2expr hashmap in conshdlrdata */ 511 conshdlrdata = SCIPconshdlrGetData((*ownerdata)->conshdlr); 512 assert(conshdlrdata != NULL); 513 514 var = SCIPgetVarExprVar(expr); 515 assert(var != NULL); 516 517 /* remove var -> expr map from hashmap if present 518 * (if no variable-expression stored for var hashmap, then the var hasn't been used in any constraint, so do nothing 519 * if variable-expression stored for var is different, then also do nothing) 520 */ 521 if( SCIPhashmapGetImage(conshdlrdata->var2expr, var) == (void*)expr ) 522 { 523 SCIP_CALL( SCIPhashmapRemove(conshdlrdata->var2expr, var) ); 524 } 525 } 526 527 SCIPfreeBlockMemory(scip, ownerdata); 528 529 return SCIP_OKAY; 530 } 531 532 static 533 SCIP_DECL_EXPR_OWNERPRINT(exprownerPrint) 534 { /*lint --e{715}*/ 535 assert(ownerdata != NULL); 536 537 /* print nl handlers associated to expr */ 538 if( ownerdata->nenfos > 0 ) 539 { 540 int i; 541 SCIPinfoMessage(scip, file, " {"); 542 543 for( i = 0; i < ownerdata->nenfos; ++i ) 544 { 545 SCIPinfoMessage(scip, file, "%s:", SCIPnlhdlrGetName(ownerdata->enfos[i]->nlhdlr)); 546 if( ownerdata->enfos[i]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_ACTIVITY ) 547 SCIPinfoMessage(scip, file, "a"); 548 if( ownerdata->enfos[i]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPABELOW ) 549 SCIPinfoMessage(scip, file, "u"); 550 if( ownerdata->enfos[i]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPAABOVE ) 551 SCIPinfoMessage(scip, file, "o"); 552 if( i < ownerdata->nenfos-1 ) 553 SCIPinfoMessage(scip, file, ", "); 554 } 555 556 SCIPinfoMessage(scip, file, "}"); 557 } 558 559 /* print aux var associated to expr */ 560 if( ownerdata->auxvar != NULL ) 561 { 562 SCIPinfoMessage(scip, file, " (<%s> in [%g, %g])", SCIPvarGetName(ownerdata->auxvar), SCIPvarGetLbLocal(ownerdata->auxvar), SCIPvarGetUbLocal(ownerdata->auxvar)); 563 } 564 SCIPinfoMessage(scip, file, "\n"); 565 566 return SCIP_OKAY; 567 } 568 569 /** possibly reevaluates and then returns the activity of the expression 570 * 571 * Reevaluate activity if currently stored is not up to date (some bound was changed since last evaluation). 572 */ 573 static 574 SCIP_DECL_EXPR_OWNEREVALACTIVITY(exprownerEvalactivity) 575 { 576 SCIP_CONSHDLRDATA* conshdlrdata; 577 578 assert(scip != NULL); 579 assert(expr != NULL); 580 assert(ownerdata != NULL); 581 582 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr); 583 assert(conshdlrdata != NULL); 584 585 if( SCIPexprGetActivityTag(expr) < conshdlrdata->curboundstag ) 586 { 587 /* update activity of expression */ 588 SCIP_CALL( forwardPropExpr(scip, ownerdata->conshdlr, expr, FALSE, NULL, NULL) ); 589 590 assert(SCIPexprGetActivityTag(expr) == conshdlrdata->curboundstag); 591 } 592 593 return SCIP_OKAY; 594 } 595 596 /** callback that creates data that this conshdlr wants to store in an expression */ 597 static 598 SCIP_DECL_EXPR_OWNERCREATE(exprownerCreate) 599 { 600 assert(scip != NULL); 601 assert(expr != NULL); 602 assert(ownerdata != NULL); 603 604 SCIP_CALL( SCIPallocClearBlockMemory(scip, ownerdata) ); 605 (*ownerdata)->nenfos = -1; 606 (*ownerdata)->conshdlr = (SCIP_CONSHDLR*)ownercreatedata; 607 608 if( SCIPisExprVar(scip, expr) ) 609 { 610 SCIP_CONSHDLRDATA* conshdlrdata; 611 SCIP_VAR* var; 612 613 (*ownerdata)->filterpos = -1; 614 615 /* add to var2expr hashmap if not having expr for var yet */ 616 617 conshdlrdata = SCIPconshdlrGetData((*ownerdata)->conshdlr); 618 assert(conshdlrdata != NULL); 619 620 var = SCIPgetVarExprVar(expr); 621 622 if( !SCIPhashmapExists(conshdlrdata->var2expr, (void*)var) ) 623 { 624 /* store the variable expression in the hashmap */ 625 SCIP_CALL( SCIPhashmapInsert(conshdlrdata->var2expr, (void*)var, (void*)expr) ); 626 } 627 else 628 { 629 /* if expr was just created, then it shouldn't already be stored as image of var */ 630 assert(SCIPhashmapGetImage(conshdlrdata->var2expr, (void*)var) != (void*)expr); 631 } 632 } 633 else 634 { 635 /* just so that we can use filterpos to recognize whether an expr is a varexpr if not having a SCIP pointer around */ 636 (*ownerdata)->filterpos = -2; 637 } 638 639 *ownerfree = exprownerFree; 640 *ownerprint = exprownerPrint; 641 *ownerevalactivity = exprownerEvalactivity; 642 643 return SCIP_OKAY; 644 } 645 646 /** creates a variable expression or retrieves from hashmap in conshdlr data */ 647 static 648 SCIP_RETCODE createExprVar( 649 SCIP* scip, /**< SCIP data structure */ 650 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */ 651 SCIP_EXPR** expr, /**< pointer where to store expression */ 652 SCIP_VAR* var /**< variable to be stored */ 653 ) 654 { 655 assert(conshdlr != NULL); 656 assert(expr != NULL); 657 assert(var != NULL); 658 659 /* get variable expression representing the given variable if there is one already */ 660 *expr = (SCIP_EXPR*) SCIPhashmapGetImage(SCIPconshdlrGetData(conshdlr)->var2expr, (void*) var); 661 662 if( *expr == NULL ) 663 { 664 /* create a new variable expression; this also captures the expression */ 665 SCIP_CALL( SCIPcreateExprVar(scip, expr, var, exprownerCreate, (void*)conshdlr) ); 666 assert(*expr != NULL); 667 /* exprownerCreate should have added var->expr to var2expr */ 668 assert(SCIPhashmapGetImage(SCIPconshdlrGetData(conshdlr)->var2expr, (void*)var) == (void*)*expr); 669 } 670 else 671 { 672 /* only capture already existing expr to get a consistent uses-count */ 673 SCIPcaptureExpr(*expr); 674 } 675 676 return SCIP_OKAY; 677 } 678 679 /* map var exprs to var-expr from var2expr hashmap */ 680 static 681 SCIP_DECL_EXPR_MAPEXPR(mapexprvar) 682 { /*lint --e{715}*/ 683 SCIP_CONSHDLR* conshdlr = (SCIP_CONSHDLR*)mapexprdata; 684 685 assert(sourcescip != NULL); 686 assert(targetscip != NULL); 687 assert(sourceexpr != NULL); 688 assert(targetexpr != NULL); 689 assert(*targetexpr == NULL); 690 assert(mapexprdata != NULL); 691 692 /* do not provide map if not variable */ 693 if( !SCIPisExprVar(sourcescip, sourceexpr) ) 694 return SCIP_OKAY; 695 696 SCIP_CALL( createExprVar(targetscip, conshdlr, targetexpr, SCIPgetVarExprVar(sourceexpr)) ); 697 698 return SCIP_OKAY; 699 } 700 701 /* map var exprs to var-expr from var2expr hashmap corresponding to transformed var */ 702 static 703 SCIP_DECL_EXPR_MAPEXPR(mapexprtransvar) 704 { /*lint --e{715}*/ 705 SCIP_CONSHDLR* conshdlr = (SCIP_CONSHDLR*)mapexprdata; 706 SCIP_VAR* var; 707 708 assert(sourcescip != NULL); 709 assert(targetscip != NULL); 710 assert(sourceexpr != NULL); 711 assert(targetexpr != NULL); 712 assert(*targetexpr == NULL); 713 assert(mapexprdata != NULL); 714 715 /* do not provide map if not variable */ 716 if( !SCIPisExprVar(sourcescip, sourceexpr) ) 717 return SCIP_OKAY; 718 719 var = SCIPgetVarExprVar(sourceexpr); 720 assert(var != NULL); 721 722 /* transform variable */ 723 SCIP_CALL( SCIPgetTransformedVar(sourcescip, var, &var) ); 724 assert(var != NULL); 725 726 SCIP_CALL( createExprVar(targetscip, conshdlr, targetexpr, var) ); 727 728 return SCIP_OKAY; 729 } 730 731 /** stores all variable expressions into a given constraint */ 732 static 733 SCIP_RETCODE storeVarExprs( 734 SCIP* scip, /**< SCIP data structure */ 735 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 736 SCIP_CONSDATA* consdata /**< constraint data */ 737 ) 738 { 739 SCIP_CONSHDLRDATA* conshdlrdata; 740 int varexprssize; 741 int i; 742 743 assert(consdata != NULL); 744 745 /* skip if we have stored the variable expressions already */ 746 if( consdata->varexprs != NULL ) 747 return SCIP_OKAY; 748 749 assert(consdata->varexprs == NULL); 750 assert(consdata->nvarexprs == 0); 751 752 /* get an upper bound on number of variable expressions */ 753 if( consdata->issimplified ) 754 { 755 /* if simplified, then we should have removed inactive variables and replaced common subexpressions, 756 * so we cannot have more variable expression than the number of active variables 757 */ 758 varexprssize = SCIPgetNVars(scip); 759 } 760 else 761 { 762 SCIP_CALL( SCIPgetExprNVars(scip, consdata->expr, &varexprssize) ); 763 } 764 765 /* create array to store all variable expressions */ 766 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consdata->varexprs, varexprssize) ); 767 768 SCIP_CALL( SCIPgetExprVarExprs(scip, consdata->expr, consdata->varexprs, &(consdata->nvarexprs)) ); 769 assert(varexprssize >= consdata->nvarexprs); 770 771 /* shrink array if there are less variables in the expression than in the problem */ 772 if( varexprssize > consdata->nvarexprs ) 773 { 774 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->varexprs, varexprssize, consdata->nvarexprs) ); 775 } 776 777 conshdlrdata = SCIPconshdlrGetData(conshdlr); 778 assert(conshdlrdata != NULL); 779 assert(conshdlrdata->var2expr != NULL); 780 781 /* ensure that for every variable an entry exists in the var2expr hashmap 782 * when removing duplicate subexpressions it can happen that a var->varexpr map was removed from the hashmap 783 */ 784 for( i = 0; i < consdata->nvarexprs; ++i ) 785 { 786 if( !SCIPhashmapExists(conshdlrdata->var2expr, SCIPgetVarExprVar(consdata->varexprs[i])) ) 787 { 788 SCIP_CALL( SCIPhashmapInsert(conshdlrdata->var2expr, SCIPgetVarExprVar(consdata->varexprs[i]), consdata->varexprs[i]) ); 789 } 790 } 791 792 return SCIP_OKAY; 793 } 794 795 /** frees all variable expression stored in storeVarExprs() */ 796 static 797 SCIP_RETCODE freeVarExprs( 798 SCIP* scip, /**< SCIP data structure */ 799 SCIP_CONSDATA* consdata /**< constraint data */ 800 ) 801 { 802 int i; 803 804 assert(consdata != NULL); 805 806 /* skip if we have stored the variable expressions already*/ 807 if( consdata->varexprs == NULL ) 808 return SCIP_OKAY; 809 810 assert(consdata->varexprs != NULL); 811 assert(consdata->nvarexprs >= 0); 812 813 /* release variable expressions */ 814 for( i = 0; i < consdata->nvarexprs; ++i ) 815 { 816 assert(consdata->varexprs[i] != NULL); 817 SCIP_CALL( SCIPreleaseExpr(scip, &consdata->varexprs[i]) ); 818 assert(consdata->varexprs[i] == NULL); 819 } 820 821 /* free variable expressions */ 822 SCIPfreeBlockMemoryArrayNull(scip, &consdata->varexprs, consdata->nvarexprs); 823 consdata->varexprs = NULL; 824 consdata->nvarexprs = 0; 825 826 return SCIP_OKAY; 827 } 828 829 /** interval evaluation of variables as used in bound tightening 830 * 831 * Returns slightly relaxed local variable bounds of a variable as interval. 832 * Does not relax beyond integer values, thus does not relax bounds on integer variables at all. 833 */ 834 static 835 SCIP_DECL_EXPR_INTEVALVAR(intEvalVarBoundTightening) 836 { 837 SCIP_INTERVAL interval; 838 SCIP_CONSHDLRDATA* conshdlrdata; 839 SCIP_Real lb; 840 SCIP_Real ub; 841 842 assert(scip != NULL); 843 assert(var != NULL); 844 845 conshdlrdata = (SCIP_CONSHDLRDATA*)intevalvardata; 846 assert(conshdlrdata != NULL); 847 848 if( conshdlrdata->globalbounds ) 849 { 850 lb = SCIPvarGetLbGlobal(var); 851 ub = SCIPvarGetUbGlobal(var); 852 } 853 else 854 { 855 lb = SCIPvarGetLbLocal(var); 856 ub = SCIPvarGetUbLocal(var); 857 } 858 assert(lb <= ub); /* SCIP should ensure that variable bounds are not contradicting */ 859 860 /* implicit integer variables may have non-integer bounds, apparently (run space25a) */ 861 if( SCIPvarGetType(var) == SCIP_VARTYPE_IMPLINT ) 862 { 863 lb = EPSROUND(lb, 0.0); /*lint !e835*/ 864 ub = EPSROUND(ub, 0.0); /*lint !e835*/ 865 } 866 867 /* integer variables should always have integral bounds in SCIP */ 868 assert(EPSFRAC(lb, 0.0) == 0.0 || !SCIPvarIsIntegral(var)); /*lint !e835*/ 869 assert(EPSFRAC(ub, 0.0) == 0.0 || !SCIPvarIsIntegral(var)); /*lint !e835*/ 870 871 switch( conshdlrdata->varboundrelax ) 872 { 873 case 'n' : /* no relaxation */ 874 break; 875 876 case 'a' : /* relax by absolute value */ 877 { 878 /* do not look at integer variables, they already have integral bounds, so wouldn't be relaxed */ 879 if( SCIPvarIsIntegral(var) ) 880 break; 881 882 if( !SCIPisInfinity(scip, -lb) ) 883 { 884 /* reduce lb by epsilon, or to the next integer value, which ever is larger */ 885 SCIP_Real bnd = floor(lb); 886 lb = MAX(bnd, lb - conshdlrdata->varboundrelaxamount); 887 } 888 889 if( !SCIPisInfinity(scip, ub) ) 890 { 891 /* increase ub by epsilon, or to the next integer value, which ever is smaller */ 892 SCIP_Real bnd = ceil(ub); 893 ub = MIN(bnd, ub + conshdlrdata->varboundrelaxamount); 894 } 895 896 break; 897 } 898 899 case 'b' : /* relax always by absolute value */ 900 { 901 /* do not look at integer variables, they already have integral bounds, so wouldn't be relaxed */ 902 if( SCIPvarIsIntegral(var) ) 903 break; 904 905 if( !SCIPisInfinity(scip, -lb) ) 906 lb -= conshdlrdata->varboundrelaxamount; 907 908 if( !SCIPisInfinity(scip, ub) ) 909 ub += conshdlrdata->varboundrelaxamount; 910 911 break; 912 } 913 914 case 'r' : /* relax by relative value */ 915 { 916 /* do not look at integer variables, they already have integral bounds, so wouldn't be relaxed */ 917 if( SCIPvarIsIntegral(var) ) 918 break; 919 920 /* relax bounds by epsilon*max(1,|bnd|), instead of just epsilon as in case 'a', thus we trust the first log(epsilon) digits 921 * however, when domains get small, relaxing can excessively weaken bound tightening, thus do only fraction of |ub-lb| if that is smaller 922 * further, do not relax beyond next integer value 923 */ 924 if( !SCIPisInfinity(scip, -lb) ) 925 { 926 SCIP_Real bnd = floor(lb); 927 lb = MAX(bnd, lb - MIN(conshdlrdata->varboundrelaxamount * MAX(1.0, REALABS(lb)), 0.001 * REALABS(ub-lb))); 928 } 929 930 if( !SCIPisInfinity(scip, ub) ) 931 { 932 SCIP_Real bnd = ceil(ub); 933 ub = MIN(bnd, ub + MIN(conshdlrdata->varboundrelaxamount * MAX(1.0, REALABS(ub)), 0.001 * REALABS(ub-lb))); 934 } 935 936 break; 937 } 938 939 default : 940 { 941 SCIPerrorMessage("Unsupported value '%c' for varboundrelax option.\n", conshdlrdata->varboundrelax); 942 SCIPABORT(); 943 break; 944 } 945 } 946 947 /* convert SCIPinfinity() to SCIP_INTERVAL_INFINITY */ 948 lb = -infty2infty(SCIPinfinity(scip), SCIP_INTERVAL_INFINITY, -lb); 949 ub = infty2infty(SCIPinfinity(scip), SCIP_INTERVAL_INFINITY, ub); 950 assert(lb <= ub); 951 952 SCIPintervalSetBounds(&interval, lb, ub); 953 954 return interval; 955 } 956 957 /** compares two nonlinear constraints by its index 958 * 959 * Usable as compare operator in array sort functions. 960 */ 961 static 962 SCIP_DECL_SORTPTRCOMP(compIndexConsNonlinear) 963 { 964 SCIP_CONSDATA* consdata1 = SCIPconsGetData((SCIP_CONS*)elem1); 965 SCIP_CONSDATA* consdata2 = SCIPconsGetData((SCIP_CONS*)elem2); 966 967 assert(consdata1 != NULL); 968 assert(consdata2 != NULL); 969 970 return consdata1->consindex - consdata2->consindex; 971 } 972 973 /** processes variable fixing or bound change event */ 974 static 975 SCIP_DECL_EVENTEXEC(processVarEvent) 976 { /*lint --e{715}*/ 977 SCIP_EVENTTYPE eventtype; 978 SCIP_EXPR* expr; 979 SCIP_EXPR_OWNERDATA* ownerdata; 980 SCIP_Bool boundtightened = FALSE; 981 982 eventtype = SCIPeventGetType(event); 983 assert(eventtype & (SCIP_EVENTTYPE_BOUNDCHANGED | SCIP_EVENTTYPE_VARFIXED | SCIP_EVENTTYPE_TYPECHANGED)); 984 985 assert(eventdata != NULL); 986 expr = (SCIP_EXPR*) eventdata; 987 assert(SCIPisExprVar(scip, expr)); 988 989 SCIPdebugMsg(scip, " exec event %" SCIP_EVENTTYPE_FORMAT " for variable <%s> (local [%g,%g], global [%g,%g])\n", eventtype, 990 SCIPvarGetName(SCIPeventGetVar(event)), 991 SCIPvarGetLbLocal(SCIPeventGetVar(event)), SCIPvarGetUbLocal(SCIPeventGetVar(event)), 992 SCIPvarGetLbGlobal(SCIPeventGetVar(event)), SCIPvarGetUbGlobal(SCIPeventGetVar(event))); 993 994 ownerdata = SCIPexprGetOwnerData(expr); 995 assert(ownerdata != NULL); 996 /* we only catch varevents for variables in constraints, so there should be constraints */ 997 assert(ownerdata->nconss > 0); 998 assert(ownerdata->conss != NULL); 999 1000 if( eventtype & SCIP_EVENTTYPE_BOUNDTIGHTENED ) 1001 boundtightened = TRUE; 1002 1003 /* usually, if fixing a variable results in a boundchange, we should have seen a boundtightened-event as well 1004 * however, if the boundchange is smaller than epsilon, such an event will be omitted 1005 * but we still want to make sure the activity of the var-expr is reevaluated (mainly to avoid a failing assert) in this case 1006 * since we cannot easily see whether a variable bound was actually changed in a varfixed event, we treat any varfixed event 1007 * as a boundtightening (and usually it is, I would think) 1008 */ 1009 if( eventtype & SCIP_EVENTTYPE_VARFIXED ) 1010 boundtightened = TRUE; 1011 1012 /* if a variable is changed to implicit-integer and has a fractional bound, then the behavior of intEvalVarBoundTightening is changing, 1013 * because we will round the bounds and no longer consider relaxing them 1014 * we will mark corresponding constraints as not-propagated in this case to get the tightened bounds on the var-expr 1015 * (mainly to avoid a failing assert, see github issue #70) 1016 * usually, a change to implicit-integer would result in a boundchange on the variable as well, but not if the bound was already almost integral 1017 */ 1018 if( (eventtype & SCIP_EVENTTYPE_TYPECHANGED) && (SCIPeventGetNewtype(event) == SCIP_VARTYPE_IMPLINT) && 1019 (!EPSISINT(SCIPvarGetLbGlobal(SCIPeventGetVar(event)), 0.0) || !EPSISINT(SCIPvarGetUbGlobal(SCIPeventGetVar(event)), 0.0)) ) /*lint !e835*/ 1020 boundtightened = TRUE; 1021 1022 /* notify constraints that use this variable expression (expr) to repropagate and possibly resimplify 1023 * - propagation can only find something new if a bound was tightened 1024 * - simplify can only find something new if a var is fixed (or maybe a bound is tightened) 1025 * and we look at global changes (that is, we are not looking at boundchanges in probing) 1026 */ 1027 if( boundtightened ) 1028 { 1029 SCIP_CONSDATA* consdata; 1030 int c; 1031 1032 for( c = 0; c < ownerdata->nconss; ++c ) 1033 { 1034 assert(ownerdata->conss[c] != NULL); 1035 consdata = SCIPconsGetData(ownerdata->conss[c]); 1036 1037 /* if bound tightening, then mark constraints to be propagated again 1038 * TODO we could try be more selective here and only trigger a propagation if a relevant bound has changed, 1039 * that is, we don't need to repropagate x + ... <= rhs if only the upper bound of x has been tightened 1040 * the locks don't help since they are not available separately for each constraint 1041 */ 1042 consdata->ispropagated = FALSE; 1043 SCIPdebugMsg(scip, " marked <%s> for propagate\n", SCIPconsGetName(ownerdata->conss[c])); 1044 1045 /* if still in presolve (but not probing), then mark constraints to be unsimplified */ 1046 if( SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING && !SCIPinProbing(scip) ) 1047 { 1048 consdata->issimplified = FALSE; 1049 SCIPdebugMsg(scip, " marked <%s> for simplify\n", SCIPconsGetName(ownerdata->conss[c])); 1050 } 1051 } 1052 } 1053 1054 /* update curboundstag, lastboundrelax, and expr activity */ 1055 if( (eventtype & SCIP_EVENTTYPE_BOUNDCHANGED) || boundtightened ) 1056 { 1057 SCIP_CONSHDLRDATA* conshdlrdata; 1058 SCIP_INTERVAL activity; 1059 1060 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr); 1061 assert(conshdlrdata != NULL); 1062 1063 /* increase tag on bounds */ 1064 ++conshdlrdata->curboundstag; 1065 assert(conshdlrdata->curboundstag > 0); 1066 1067 /* remember also if we relaxed bounds now */ 1068 if( eventtype & SCIP_EVENTTYPE_BOUNDRELAXED ) 1069 conshdlrdata->lastboundrelax = conshdlrdata->curboundstag; 1070 1071 /* update the activity of the var-expr here immediately 1072 * (we could call expr->activity = intevalvar(var, consdhlr) directly, but then the exprhdlr statistics are not updated) 1073 */ 1074 SCIP_CALL( SCIPcallExprInteval(scip, expr, &activity, conshdlrdata->intevalvar, conshdlrdata) ); 1075 /* activity = conshdlrdata->intevalvar(scip, SCIPgetVarExprVar(expr), conshdlrdata); */ 1076 #ifdef DEBUG_PROP 1077 SCIPdebugMsg(scip, " var-exprhdlr::inteval = [%.20g, %.20g]\n", activity.inf, activity.sup); 1078 #endif 1079 SCIPexprSetActivity(expr, activity, conshdlrdata->curboundstag); 1080 } 1081 1082 return SCIP_OKAY; 1083 } 1084 1085 /** registers event handler to catch variable events on variable 1086 * 1087 * Additionally, the given constraint is stored in the ownerdata of the variable-expression. 1088 * When an event occurs, all stored constraints are notified. 1089 */ 1090 static 1091 SCIP_RETCODE catchVarEvent( 1092 SCIP* scip, /**< SCIP data structure */ 1093 SCIP_EVENTHDLR* eventhdlr, /**< event handler */ 1094 SCIP_EXPR* expr, /**< variable expression */ 1095 SCIP_CONS* cons /**< nonlinear constraint */ 1096 ) 1097 { 1098 SCIP_EXPR_OWNERDATA* ownerdata; 1099 1100 assert(eventhdlr != NULL); 1101 assert(expr != NULL); 1102 assert(SCIPisExprVar(scip, expr)); 1103 assert(cons != NULL); 1104 1105 ownerdata = SCIPexprGetOwnerData(expr); 1106 assert(ownerdata != NULL); 1107 1108 #ifndef NDEBUG 1109 /* assert that constraint does not double-catch variable */ 1110 { 1111 int i; 1112 for( i = 0; i < ownerdata->nconss; ++i ) 1113 assert(ownerdata->conss[i] != cons); 1114 } 1115 #endif 1116 1117 /* append cons to ownerdata->conss */ 1118 SCIP_CALL( SCIPensureBlockMemoryArray(scip, &ownerdata->conss, &ownerdata->consssize, ownerdata->nconss + 1) ); 1119 ownerdata->conss[ownerdata->nconss++] = cons; 1120 /* we're not capturing the constraint here to avoid circular references */ 1121 1122 /* updated sorted flag */ 1123 if( ownerdata->nconss <= 1 ) 1124 ownerdata->consssorted = TRUE; 1125 else if( ownerdata->consssorted ) 1126 ownerdata->consssorted = compIndexConsNonlinear(ownerdata->conss[ownerdata->nconss-2], ownerdata->conss[ownerdata->nconss-1]) > 0; 1127 1128 /* catch variable events, if not done so yet (first constraint) */ 1129 if( ownerdata->filterpos < 0 ) 1130 { 1131 SCIP_EVENTTYPE eventtype; 1132 1133 assert(ownerdata->nconss == 1); 1134 1135 eventtype = SCIP_EVENTTYPE_BOUNDCHANGED | SCIP_EVENTTYPE_VARFIXED | SCIP_EVENTTYPE_TYPECHANGED; 1136 1137 SCIP_CALL( SCIPcatchVarEvent(scip, SCIPgetVarExprVar(expr), eventtype, eventhdlr, (SCIP_EVENTDATA*)expr, &ownerdata->filterpos) ); 1138 assert(ownerdata->filterpos >= 0); 1139 } 1140 1141 return SCIP_OKAY; 1142 } 1143 1144 /** catch variable events */ 1145 static 1146 SCIP_RETCODE catchVarEvents( 1147 SCIP* scip, /**< SCIP data structure */ 1148 SCIP_EVENTHDLR* eventhdlr, /**< event handler */ 1149 SCIP_CONS* cons /**< constraint for which to catch bound change events */ 1150 ) 1151 { 1152 SCIP_CONSHDLRDATA* conshdlrdata; 1153 SCIP_CONSDATA* consdata; 1154 SCIP_EXPR* expr; 1155 int i; 1156 1157 assert(eventhdlr != NULL); 1158 assert(cons != NULL); 1159 1160 consdata = SCIPconsGetData(cons); 1161 assert(consdata != NULL); 1162 assert(consdata->varexprs != NULL); 1163 assert(consdata->nvarexprs >= 0); 1164 1165 /* check if we have catched variable events already */ 1166 if( consdata->catchedevents ) 1167 return SCIP_OKAY; 1168 1169 conshdlrdata = SCIPconshdlrGetData(SCIPconsGetHdlr(cons)); 1170 assert(conshdlrdata != NULL); 1171 #ifndef CR_API /* this assert may not work in unittests due to having this code compiled twice, #3543 */ 1172 assert(conshdlrdata->intevalvar == intEvalVarBoundTightening); 1173 #endif 1174 1175 SCIPdebugMsg(scip, "catchVarEvents for %s\n", SCIPconsGetName(cons)); 1176 1177 for( i = 0; i < consdata->nvarexprs; ++i ) 1178 { 1179 expr = consdata->varexprs[i]; 1180 1181 assert(expr != NULL); 1182 assert(SCIPisExprVar(scip, expr)); 1183 1184 SCIP_CALL( catchVarEvent(scip, eventhdlr, expr, cons) ); 1185 1186 /* from now on, activity of var-expr will usually be updated in processVarEvent if variable bound is changing 1187 * since we just registered this eventhdlr, we should make sure that the activity is also up to date now 1188 */ 1189 if( SCIPexprGetActivityTag(expr) < conshdlrdata->curboundstag ) 1190 { 1191 SCIP_INTERVAL activity; 1192 SCIP_CALL( SCIPcallExprInteval(scip, expr, &activity, intEvalVarBoundTightening, conshdlrdata) ); 1193 /* activity = intEvalVarBoundTightening(scip, SCIPgetVarExprVar(expr), conshdlrdata); */ 1194 SCIPexprSetActivity(expr, activity, conshdlrdata->curboundstag); 1195 #ifdef DEBUG_PROP 1196 SCIPdebugMsg(scip, "var-exprhdlr::inteval for var <%s> = [%.20g, %.20g]\n", SCIPvarGetName(SCIPgetVarExprVar(expr)), activity.inf, activity.sup); 1197 #endif 1198 } 1199 } 1200 1201 consdata->catchedevents = TRUE; 1202 1203 return SCIP_OKAY; 1204 } 1205 1206 /** unregisters event handler to catch variable events on variable 1207 * 1208 * The given constraint is removed from the constraints array in the ownerdata of the variable-expression. 1209 * If this was the last constraint, then the event handler is unregistered for this variable. 1210 */ 1211 static 1212 SCIP_RETCODE dropVarEvent( 1213 SCIP* scip, /**< SCIP data structure */ 1214 SCIP_EVENTHDLR* eventhdlr, /**< event handler */ 1215 SCIP_EXPR* expr, /**< variable expression */ 1216 SCIP_CONS* cons /**< expr constraint */ 1217 ) 1218 { 1219 SCIP_EXPR_OWNERDATA* ownerdata; 1220 int pos; 1221 1222 assert(eventhdlr != NULL); 1223 assert(expr != NULL); 1224 assert(SCIPisExprVar(scip, expr)); 1225 assert(cons != NULL); 1226 1227 ownerdata = SCIPexprGetOwnerData(expr); 1228 assert(ownerdata != NULL); 1229 assert(ownerdata->nconss > 0); 1230 1231 if( ownerdata->conss[ownerdata->nconss-1] == cons ) 1232 { 1233 pos = ownerdata->nconss-1; 1234 } 1235 else 1236 { 1237 if( !ownerdata->consssorted ) 1238 { 1239 SCIPsortPtr((void**)ownerdata->conss, compIndexConsNonlinear, ownerdata->nconss); 1240 ownerdata->consssorted = TRUE; 1241 } 1242 1243 if( !SCIPsortedvecFindPtr((void**)ownerdata->conss, compIndexConsNonlinear, cons, ownerdata->nconss, &pos) ) 1244 { 1245 SCIPerrorMessage("Constraint <%s> not in constraint array of expression for variable <%s>\n", SCIPconsGetName(cons), SCIPvarGetName(SCIPgetVarExprVar(expr))); 1246 return SCIP_ERROR; 1247 } 1248 assert(pos >= 0 && pos < ownerdata->nconss); 1249 } 1250 assert(ownerdata->conss[pos] == cons); 1251 1252 /* move last constraint into position of removed constraint */ 1253 if( pos < ownerdata->nconss-1 ) 1254 { 1255 ownerdata->conss[pos] = ownerdata->conss[ownerdata->nconss-1]; 1256 ownerdata->consssorted = FALSE; 1257 } 1258 --ownerdata->nconss; 1259 1260 /* drop variable events if that was the last constraint */ 1261 if( ownerdata->nconss == 0 ) 1262 { 1263 SCIP_EVENTTYPE eventtype; 1264 1265 assert(ownerdata->filterpos >= 0); 1266 1267 eventtype = SCIP_EVENTTYPE_BOUNDCHANGED | SCIP_EVENTTYPE_VARFIXED | SCIP_EVENTTYPE_TYPECHANGED; 1268 1269 SCIP_CALL( SCIPdropVarEvent(scip, SCIPgetVarExprVar(expr), eventtype, eventhdlr, (SCIP_EVENTDATA*)expr, ownerdata->filterpos) ); 1270 ownerdata->filterpos = -1; 1271 } 1272 1273 return SCIP_OKAY; 1274 } 1275 1276 /** drop variable events */ 1277 static 1278 SCIP_RETCODE dropVarEvents( 1279 SCIP* scip, /**< SCIP data structure */ 1280 SCIP_EVENTHDLR* eventhdlr, /**< event handler */ 1281 SCIP_CONS* cons /**< constraint for which to drop bound change events */ 1282 ) 1283 { 1284 SCIP_CONSDATA* consdata; 1285 int i; 1286 1287 assert(eventhdlr != NULL); 1288 assert(cons != NULL); 1289 1290 consdata = SCIPconsGetData(cons); 1291 assert(consdata != NULL); 1292 1293 /* check if we have catched variable events already */ 1294 if( !consdata->catchedevents ) 1295 return SCIP_OKAY; 1296 1297 assert(consdata->varexprs != NULL); 1298 assert(consdata->nvarexprs >= 0); 1299 1300 SCIPdebugMsg(scip, "dropVarEvents for %s\n", SCIPconsGetName(cons)); 1301 1302 for( i = consdata->nvarexprs - 1; i >= 0; --i ) 1303 { 1304 assert(consdata->varexprs[i] != NULL); 1305 1306 SCIP_CALL( dropVarEvent(scip, eventhdlr, consdata->varexprs[i], cons) ); 1307 } 1308 1309 consdata->catchedevents = FALSE; 1310 1311 return SCIP_OKAY; 1312 } 1313 1314 /** creates and captures a nonlinear constraint 1315 * 1316 * @attention Use copyexpr=FALSE only if expr is already "owned" by conshdlr, that is, if expressions were created with exprownerCreate() and ownerdata passed in the last two arguments 1317 */ 1318 static 1319 SCIP_RETCODE createCons( 1320 SCIP* scip, /**< SCIP data structure */ 1321 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 1322 SCIP_CONS** cons, /**< pointer to hold the created constraint */ 1323 const char* name, /**< name of constraint */ 1324 SCIP_EXPR* expr, /**< expression of constraint (must not be NULL) */ 1325 SCIP_Real lhs, /**< left hand side of constraint */ 1326 SCIP_Real rhs, /**< right hand side of constraint */ 1327 SCIP_Bool copyexpr, /**< whether to copy the expression or reuse the given expr (capture it) */ 1328 SCIP_Bool initial, /**< should the LP relaxation of constraint be in the initial LP? 1329 * Usually set to TRUE. Set to FALSE for 'lazy constraints'. */ 1330 SCIP_Bool separate, /**< should the constraint be separated during LP processing? 1331 * Usually set to TRUE. */ 1332 SCIP_Bool enforce, /**< should the constraint be enforced during node processing? 1333 * TRUE for model constraints, FALSE for additional, redundant constraints. */ 1334 SCIP_Bool check, /**< should the constraint be checked for feasibility? 1335 * TRUE for model constraints, FALSE for additional, redundant constraints. */ 1336 SCIP_Bool propagate, /**< should the constraint be propagated during node processing? 1337 * Usually set to TRUE. */ 1338 SCIP_Bool local, /**< is constraint only valid locally? 1339 * Usually set to FALSE. Has to be set to TRUE, e.g., for branching constraints. */ 1340 SCIP_Bool modifiable, /**< is constraint modifiable (subject to column generation)? 1341 * Usually set to FALSE. In column generation applications, set to TRUE if pricing 1342 * adds coefficients to this constraint. */ 1343 SCIP_Bool dynamic, /**< is constraint subject to aging? 1344 * Usually set to FALSE. Set to TRUE for own cuts which 1345 * are separated as constraints. */ 1346 SCIP_Bool removable /**< should the relaxation be removed from the LP due to aging or cleanup? 1347 * Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */ 1348 ) 1349 { 1350 SCIP_CONSHDLRDATA* conshdlrdata; 1351 SCIP_CONSDATA* consdata; 1352 1353 assert(conshdlr != NULL); 1354 assert(expr != NULL); 1355 1356 conshdlrdata = SCIPconshdlrGetData(conshdlr); 1357 assert(conshdlrdata != NULL); 1358 1359 if( local && SCIPgetDepth(scip) != 0 ) 1360 { 1361 SCIPerrorMessage("Locally valid nonlinear constraints are not supported, yet.\n"); 1362 return SCIP_INVALIDCALL; 1363 } 1364 1365 /* TODO we should allow for non-initial nonlinear constraints */ 1366 if( !initial ) 1367 { 1368 SCIPerrorMessage("Non-initial nonlinear constraints are not supported, yet.\n"); 1369 return SCIP_INVALIDCALL; 1370 } 1371 1372 /* create constraint data */ 1373 SCIP_CALL( SCIPallocClearBlockMemory(scip, &consdata) ); 1374 1375 if( copyexpr ) 1376 { 1377 /* copy expression, thereby map variables expressions to already existing variables expressions in var2expr map, or augment var2expr map */ 1378 SCIP_CALL( SCIPduplicateExpr(scip, expr, &consdata->expr, mapexprvar, conshdlr, exprownerCreate, (void*)conshdlr) ); 1379 } 1380 else 1381 { 1382 consdata->expr = expr; 1383 SCIPcaptureExpr(consdata->expr); 1384 } 1385 consdata->lhs = lhs; 1386 consdata->rhs = rhs; 1387 consdata->consindex = conshdlrdata->lastconsindex++; 1388 consdata->curv = SCIP_EXPRCURV_UNKNOWN; 1389 1390 /* create constraint */ 1391 SCIP_CALL( SCIPcreateCons(scip, cons, name, conshdlr, consdata, initial, separate, enforce, check, propagate, 1392 local, modifiable, dynamic, removable, FALSE) ); 1393 1394 return SCIP_OKAY; 1395 } 1396 1397 /** returns absolute violation for auxvar relation in an expression w.r.t. original variables 1398 * 1399 * Assume the expression is f(x), where x are original (i.e., not auxiliary) variables. 1400 * Assume that f(x) is associated with auxiliary variable z. 1401 * 1402 * If there are negative locks, then return the violation of z ≤ f(x) and sets `violover` to TRUE. 1403 * If there are positive locks, then return the violation of z ≥ f(x) and sets `violunder` to TRUE. 1404 * Of course, if there both negative and positive locks, then return the violation of z = f(x). 1405 * If f could not be evaluated, then return SCIPinfinity() and set both `violover` and `violunder` to TRUE. 1406 * 1407 * @note This does not reevaluate the violation, but assumes that the expression has been evaluated 1408 */ 1409 static 1410 SCIP_Real getExprAbsOrigViolation( 1411 SCIP* scip, /**< SCIP data structure */ 1412 SCIP_EXPR* expr, /**< expression */ 1413 SCIP_SOL* sol, /**< solution that has been evaluated */ 1414 SCIP_Bool* violunder, /**< buffer to store whether z >= f(x) is violated, or NULL */ 1415 SCIP_Bool* violover /**< buffer to store whether z <= f(x) is violated, or NULL */ 1416 ) 1417 { 1418 SCIP_EXPR_OWNERDATA* ownerdata; 1419 SCIP_Real auxvarvalue; 1420 1421 assert(expr != NULL); 1422 1423 ownerdata = SCIPexprGetOwnerData(expr); 1424 assert(ownerdata != NULL); 1425 assert(ownerdata->auxvar != NULL); 1426 1427 if( SCIPexprGetEvalValue(expr) == SCIP_INVALID ) 1428 { 1429 if( violunder != NULL ) 1430 *violunder = TRUE; 1431 if( violover != NULL ) 1432 *violover = TRUE; 1433 return SCIPinfinity(scip); 1434 } 1435 1436 auxvarvalue = SCIPgetSolVal(scip, sol, ownerdata->auxvar); 1437 1438 if( ownerdata->nlocksneg > 0 && auxvarvalue > SCIPexprGetEvalValue(expr) ) 1439 { 1440 if( violunder != NULL ) 1441 *violunder = FALSE; 1442 if( violover != NULL ) 1443 *violover = TRUE; 1444 return auxvarvalue - SCIPexprGetEvalValue(expr); 1445 } 1446 1447 if( ownerdata->nlockspos > 0 && SCIPexprGetEvalValue(expr) > auxvarvalue ) 1448 { 1449 if( violunder != NULL ) 1450 *violunder = TRUE; 1451 if( violover != NULL ) 1452 *violover = FALSE; 1453 return SCIPexprGetEvalValue(expr) - auxvarvalue; 1454 } 1455 1456 if( violunder != NULL ) 1457 *violunder = FALSE; 1458 if( violover != NULL ) 1459 *violover = FALSE; 1460 return 0.0; 1461 } 1462 1463 /** returns absolute violation for auxvar relation in an expression w.r.t. auxiliary variables 1464 * 1465 * Assume the expression is f(w), where w are auxiliary variables that were introduced by some nlhdlr. 1466 * Assume that f(w) is associated with auxiliary variable z. 1467 * 1468 * If there are negative locks, then return the violation of z ≤ f(w) and sets `violover` to TRUE. 1469 * If there are positive locks, then return the violation of z ≥ f(w) and sets `violunder` to TRUE. 1470 * Of course, if there both negative and positive locks, then return the violation of z = f(w). 1471 * If f could not be evaluated, then return SCIPinfinity() and set both `violover` and `violunder` to TRUE. 1472 * 1473 * @note This does not reevaluate the violation, but assumes that f(w) is passed in with auxvalue. 1474 */ 1475 static 1476 SCIP_Real getExprAbsAuxViolation( 1477 SCIP* scip, /**< SCIP data structure */ 1478 SCIP_EXPR* expr, /**< expression */ 1479 SCIP_Real auxvalue, /**< value of f(w) */ 1480 SCIP_SOL* sol, /**< solution that has been evaluated */ 1481 SCIP_Bool* violunder, /**< buffer to store whether z >= f(w) is violated, or NULL */ 1482 SCIP_Bool* violover /**< buffer to store whether z <= f(w) is violated, or NULL */ 1483 ) 1484 { 1485 SCIP_EXPR_OWNERDATA* ownerdata; 1486 SCIP_Real auxvarvalue; 1487 1488 assert(expr != NULL); 1489 1490 ownerdata = SCIPexprGetOwnerData(expr); 1491 assert(ownerdata != NULL); 1492 assert(ownerdata->auxvar != NULL); 1493 1494 if( auxvalue == SCIP_INVALID ) 1495 { 1496 if( violunder != NULL ) 1497 *violunder = TRUE; 1498 if( violover != NULL ) 1499 *violover = TRUE; 1500 return SCIPinfinity(scip); 1501 } 1502 1503 auxvarvalue = SCIPgetSolVal(scip, sol, ownerdata->auxvar); 1504 1505 if( ownerdata->nlocksneg > 0 && auxvarvalue > auxvalue ) 1506 { 1507 if( violunder != NULL ) 1508 *violunder = FALSE; 1509 if( violover != NULL ) 1510 *violover = TRUE; 1511 return auxvarvalue - auxvalue; 1512 } 1513 1514 if( ownerdata->nlockspos > 0 && auxvalue > auxvarvalue ) 1515 { 1516 if( violunder != NULL ) 1517 *violunder = TRUE; 1518 if( violover != NULL ) 1519 *violover = FALSE; 1520 return auxvalue - auxvarvalue; 1521 } 1522 1523 if( violunder != NULL ) 1524 *violunder = FALSE; 1525 if( violover != NULL ) 1526 *violover = FALSE; 1527 1528 return 0.0; 1529 } 1530 1531 /** computes violation of a constraint */ 1532 static 1533 SCIP_RETCODE computeViolation( 1534 SCIP* scip, /**< SCIP data structure */ 1535 SCIP_CONS* cons, /**< constraint */ 1536 SCIP_SOL* sol, /**< solution or NULL if LP solution should be used */ 1537 SCIP_Longint soltag /**< tag that uniquely identifies the solution (with its values), or 0. */ 1538 ) 1539 { 1540 SCIP_CONSDATA* consdata; 1541 SCIP_Real activity; 1542 1543 assert(scip != NULL); 1544 assert(cons != NULL); 1545 1546 consdata = SCIPconsGetData(cons); 1547 assert(consdata != NULL); 1548 1549 SCIP_CALL( SCIPevalExpr(scip, consdata->expr, sol, soltag) ); 1550 activity = SCIPexprGetEvalValue(consdata->expr); 1551 1552 /* consider constraint as violated if it is undefined in the current point */ 1553 if( activity == SCIP_INVALID ) 1554 { 1555 consdata->lhsviol = SCIPinfinity(scip); 1556 consdata->rhsviol = SCIPinfinity(scip); 1557 return SCIP_OKAY; 1558 } 1559 1560 /* compute violations */ 1561 consdata->lhsviol = SCIPisInfinity(scip, -consdata->lhs) ? -SCIPinfinity(scip) : consdata->lhs - activity; 1562 consdata->rhsviol = SCIPisInfinity(scip, consdata->rhs) ? -SCIPinfinity(scip) : activity - consdata->rhs; 1563 1564 return SCIP_OKAY; 1565 } 1566 1567 /** returns absolute violation of a constraint 1568 * 1569 * @note This does not reevaluate the violation, but assumes that computeViolation() has been called before. 1570 */ 1571 static 1572 SCIP_Real getConsAbsViolation( 1573 SCIP_CONS* cons /**< constraint */ 1574 ) 1575 { 1576 SCIP_CONSDATA* consdata; 1577 1578 assert(cons != NULL); 1579 1580 consdata = SCIPconsGetData(cons); 1581 assert(consdata != NULL); 1582 1583 return MAX3(0.0, consdata->lhsviol, consdata->rhsviol); 1584 } 1585 1586 /** computes relative violation of a constraint 1587 * 1588 * @note This does not reevaluate the violation, but assumes that computeViolation() has been called before. 1589 */ 1590 static 1591 SCIP_RETCODE getConsRelViolation( 1592 SCIP* scip, /**< SCIP data structure */ 1593 SCIP_CONS* cons, /**< constraint */ 1594 SCIP_Real* viol, /**< buffer to store violation */ 1595 SCIP_SOL* sol, /**< solution or NULL if LP solution should be used */ 1596 SCIP_Longint soltag /**< tag that uniquely identifies the solution (with its values), or 0 */ 1597 ) 1598 { 1599 SCIP_CONSHDLR* conshdlr; 1600 SCIP_CONSHDLRDATA* conshdlrdata; 1601 SCIP_CONSDATA* consdata; 1602 SCIP_Real scale; 1603 1604 assert(cons != NULL); 1605 assert(viol != NULL); 1606 1607 conshdlr = SCIPconsGetHdlr(cons); 1608 assert(conshdlr != NULL); 1609 1610 conshdlrdata = SCIPconshdlrGetData(conshdlr); 1611 assert(conshdlrdata != NULL); 1612 1613 *viol = getConsAbsViolation(cons); 1614 1615 if( conshdlrdata->violscale == 'n' ) 1616 return SCIP_OKAY; 1617 1618 if( SCIPisInfinity(scip, *viol) ) 1619 return SCIP_OKAY; 1620 1621 consdata = SCIPconsGetData(cons); 1622 assert(consdata != NULL); 1623 1624 if( conshdlrdata->violscale == 'a' ) 1625 { 1626 scale = MAX(1.0, REALABS(SCIPexprGetEvalValue(consdata->expr))); 1627 1628 /* consider value of side that is violated for scaling, too */ 1629 if( consdata->lhsviol > 0.0 && REALABS(consdata->lhs) > scale ) 1630 { 1631 assert(!SCIPisInfinity(scip, -consdata->lhs)); 1632 scale = REALABS(consdata->lhs); 1633 } 1634 else if( consdata->rhsviol > 0.0 && REALABS(consdata->rhs) > scale ) 1635 { 1636 assert(!SCIPisInfinity(scip, consdata->rhs)); 1637 scale = REALABS(consdata->rhs); 1638 } 1639 1640 *viol /= scale; 1641 return SCIP_OKAY; 1642 } 1643 1644 /* if not 'n' or 'a', then it has to be 'g' at the moment */ 1645 assert(conshdlrdata->violscale == 'g'); 1646 if( soltag == 0L || consdata->gradnormsoltag != soltag ) 1647 { 1648 /* we need the varexprs to conveniently access the gradient */ 1649 SCIP_CALL( storeVarExprs(scip, conshdlr, consdata) ); 1650 1651 /* update cached value of norm of gradient */ 1652 consdata->gradnorm = 0.0; 1653 1654 /* compute gradient */ 1655 SCIP_CALL( SCIPevalExprGradient(scip, consdata->expr, sol, soltag) ); 1656 1657 /* gradient evaluation error -> no scaling */ 1658 if( SCIPexprGetDerivative(consdata->expr) != SCIP_INVALID ) 1659 { 1660 int i; 1661 for( i = 0; i < consdata->nvarexprs; ++i ) 1662 { 1663 SCIP_Real deriv; 1664 1665 assert(SCIPexprGetDiffTag(consdata->expr) == SCIPexprGetDiffTag(consdata->varexprs[i])); 1666 deriv = SCIPexprGetDerivative(consdata->varexprs[i]); 1667 if( deriv == SCIP_INVALID ) 1668 { 1669 /* SCIPdebugMsg(scip, "gradient evaluation error for component %d\n", i); */ 1670 consdata->gradnorm = 0.0; 1671 break; 1672 } 1673 1674 consdata->gradnorm += deriv*deriv; 1675 } 1676 } 1677 consdata->gradnorm = sqrt(consdata->gradnorm); 1678 consdata->gradnormsoltag = soltag; 1679 } 1680 1681 *viol /= MAX(1.0, consdata->gradnorm); 1682 1683 return SCIP_OKAY; 1684 } 1685 1686 /** returns whether constraint is currently violated 1687 * 1688 * @note This does not reevaluate the violation, but assumes that computeViolation() has been called before. 1689 */ 1690 static 1691 SCIP_Bool isConsViolated( 1692 SCIP* scip, /**< SCIP data structure */ 1693 SCIP_CONS* cons /**< constraint */ 1694 ) 1695 { 1696 return getConsAbsViolation(cons) > SCIPfeastol(scip); 1697 } 1698 1699 /** checks for a linear variable that can be increased or decreased without harming feasibility */ 1700 static 1701 void findUnlockedLinearVar( 1702 SCIP* scip, /**< SCIP data structure */ 1703 SCIP_CONS* cons /**< constraint */ 1704 ) 1705 { 1706 SCIP_CONSDATA* consdata; 1707 int poslock; 1708 int neglock; 1709 int i; 1710 1711 assert(cons != NULL); 1712 1713 consdata = SCIPconsGetData(cons); 1714 assert(consdata != NULL); 1715 1716 consdata->linvarincr = NULL; 1717 consdata->linvardecr = NULL; 1718 consdata->linvarincrcoef = 0.0; 1719 consdata->linvardecrcoef = 0.0; 1720 1721 /* root expression is not a sum -> no unlocked linear variable available */ 1722 if( !SCIPisExprSum(scip, consdata->expr) ) 1723 return; 1724 1725 for( i = 0; i < SCIPexprGetNChildren(consdata->expr); ++i ) 1726 { 1727 SCIP_EXPR* child; 1728 1729 child = SCIPexprGetChildren(consdata->expr)[i]; 1730 assert(child != NULL); 1731 1732 /* check whether the child is a variable expression */ 1733 if( SCIPisExprVar(scip, child) ) 1734 { 1735 SCIP_VAR* var = SCIPgetVarExprVar(child); 1736 SCIP_Real coef = SCIPgetCoefsExprSum(consdata->expr)[i]; 1737 1738 if( coef > 0.0 ) 1739 { 1740 poslock = !SCIPisInfinity(scip, consdata->rhs) ? 1 : 0; 1741 neglock = !SCIPisInfinity(scip, -consdata->lhs) ? 1 : 0; 1742 } 1743 else 1744 { 1745 poslock = !SCIPisInfinity(scip, -consdata->lhs) ? 1 : 0; 1746 neglock = !SCIPisInfinity(scip, consdata->rhs) ? 1 : 0; 1747 } 1748 SCIPdebugMsg(scip, "child <%s> locks: %d %d\n", SCIPvarGetName(var), SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL), SCIPvarGetNLocksUpType(var, SCIP_LOCKTYPE_MODEL)); 1749 1750 if( SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL) - neglock == 0 ) 1751 { 1752 /* for a*x + f(y) \in [lhs, rhs], we can decrease x without harming other constraints 1753 * if we have already one candidate, then take the one where the loss in the objective function is less 1754 */ 1755 if( (consdata->linvardecr == NULL) || 1756 (SCIPvarGetObj(consdata->linvardecr) / consdata->linvardecrcoef > SCIPvarGetObj(var) / coef) ) 1757 { 1758 consdata->linvardecr = var; 1759 consdata->linvardecrcoef = coef; 1760 } 1761 } 1762 1763 if( SCIPvarGetNLocksUpType(var, SCIP_LOCKTYPE_MODEL) - poslock == 0 ) 1764 { 1765 /* for a*x + f(y) \in [lhs, rhs], we can increase x without harm 1766 * if we have already one candidate, then take the one where the loss in the objective function is less 1767 */ 1768 if( (consdata->linvarincr == NULL) || 1769 (SCIPvarGetObj(consdata->linvarincr) / consdata->linvarincrcoef > SCIPvarGetObj(var) / coef) ) 1770 { 1771 consdata->linvarincr = var; 1772 consdata->linvarincrcoef = coef; 1773 } 1774 } 1775 } 1776 } 1777 1778 assert(consdata->linvarincr == NULL || consdata->linvarincrcoef != 0.0); 1779 assert(consdata->linvardecr == NULL || consdata->linvardecrcoef != 0.0); 1780 1781 if( consdata->linvarincr != NULL ) 1782 { 1783 SCIPdebugMsg(scip, "may increase <%s> to become feasible\n", SCIPvarGetName(consdata->linvarincr)); 1784 } 1785 if( consdata->linvardecr != NULL ) 1786 { 1787 SCIPdebugMsg(scip, "may decrease <%s> to become feasible\n", SCIPvarGetName(consdata->linvardecr)); 1788 } 1789 } 1790 1791 /** Given a solution where every nonlinear constraint is either feasible or can be made feasible by 1792 * moving a linear variable, construct the corresponding feasible solution and pass it to the trysol heuristic. 1793 * 1794 * The method assumes that this is always possible and that not all constraints are feasible already. 1795 */ 1796 static 1797 SCIP_RETCODE proposeFeasibleSolution( 1798 SCIP* scip, /**< SCIP data structure */ 1799 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 1800 SCIP_CONS** conss, /**< constraints to process */ 1801 int nconss, /**< number of constraints */ 1802 SCIP_SOL* sol, /**< solution to process */ 1803 SCIP_Bool* success /**< buffer to store whether we succeeded to construct a solution that satisfies all provided constraints */ 1804 ) 1805 { 1806 SCIP_CONSHDLRDATA* conshdlrdata; 1807 SCIP_SOL* newsol; 1808 int c; 1809 1810 assert(scip != NULL); 1811 assert(conshdlr != NULL); 1812 assert(conss != NULL || nconss == 0); 1813 assert(success != NULL); 1814 1815 *success = FALSE; 1816 1817 /* don't propose new solutions if not in presolve or solving */ 1818 if( SCIPgetStage(scip) < SCIP_STAGE_INITPRESOLVE || SCIPgetStage(scip) >= SCIP_STAGE_SOLVED ) 1819 return SCIP_OKAY; 1820 1821 conshdlrdata = SCIPconshdlrGetData(conshdlr); 1822 assert(conshdlrdata != NULL); 1823 1824 if( sol != NULL ) 1825 { 1826 SCIP_CALL( SCIPcreateSolCopy(scip, &newsol, sol) ); 1827 } 1828 else 1829 { 1830 SCIP_CALL( SCIPcreateLPSol(scip, &newsol, NULL) ); 1831 } 1832 SCIP_CALL( SCIPunlinkSol(scip, newsol) ); 1833 SCIPdebugMsg(scip, "attempt to make solution from <%s> feasible by shifting linear variable\n", 1834 sol != NULL ? (SCIPsolGetHeur(sol) != NULL ? SCIPheurGetName(SCIPsolGetHeur(sol)) : "tree") : "LP"); 1835 1836 for( c = 0; c < nconss; ++c ) 1837 { 1838 SCIP_CONSDATA* consdata = SCIPconsGetData(conss[c]); /*lint !e613*/ 1839 SCIP_Real viol = 0.0; 1840 SCIP_Real delta; 1841 SCIP_Real gap; 1842 1843 assert(consdata != NULL); 1844 1845 /* get absolute violation and sign */ 1846 if( consdata->lhsviol > SCIPfeastol(scip) ) 1847 viol = consdata->lhsviol; /* lhs - activity */ 1848 else if( consdata->rhsviol > SCIPfeastol(scip) ) 1849 viol = -consdata->rhsviol; /* rhs - activity */ 1850 else 1851 continue; /* constraint is satisfied */ 1852 1853 if( consdata->linvarincr != NULL && 1854 ((viol > 0.0 && consdata->linvarincrcoef > 0.0) || (viol < 0.0 && consdata->linvarincrcoef < 0.0)) ) 1855 { 1856 SCIP_VAR* var = consdata->linvarincr; 1857 1858 /* compute how much we would like to increase var */ 1859 delta = viol / consdata->linvarincrcoef; 1860 assert(delta > 0.0); 1861 1862 /* if var has an upper bound, may need to reduce delta */ 1863 if( !SCIPisInfinity(scip, SCIPvarGetUbGlobal(var)) ) 1864 { 1865 gap = SCIPvarGetUbGlobal(var) - SCIPgetSolVal(scip, newsol, var); 1866 delta = MIN(MAX(0.0, gap), delta); 1867 } 1868 if( SCIPisPositive(scip, delta) ) 1869 { 1870 /* if variable is integral, round delta up so that it will still have an integer value */ 1871 if( SCIPvarIsIntegral(var) ) 1872 delta = SCIPceil(scip, delta); 1873 1874 SCIP_CALL( SCIPincSolVal(scip, newsol, var, delta) ); 1875 SCIPdebugMsg(scip, "increase <%s> by %g to %g to remedy lhs-violation %g of cons <%s>\n", 1876 SCIPvarGetName(var), delta, SCIPgetSolVal(scip, newsol, var), viol, SCIPconsGetName(conss[c])); /*lint !e613*/ 1877 1878 /* adjust constraint violation, if satisfied go on to next constraint */ 1879 viol -= consdata->linvarincrcoef * delta; 1880 if( SCIPisZero(scip, viol) ) 1881 continue; 1882 } 1883 } 1884 1885 assert(viol != 0.0); 1886 if( consdata->linvardecr != NULL && 1887 ((viol > 0.0 && consdata->linvardecrcoef < 0.0) || (viol < 0.0 && consdata->linvardecrcoef > 0.0)) ) 1888 { 1889 SCIP_VAR* var = consdata->linvardecr; 1890 1891 /* compute how much we would like to decrease var */ 1892 delta = viol / consdata->linvardecrcoef; 1893 assert(delta < 0.0); 1894 1895 /* if var has a lower bound, may need to reduce delta */ 1896 if( !SCIPisInfinity(scip, -SCIPvarGetLbGlobal(var)) ) 1897 { 1898 gap = SCIPgetSolVal(scip, newsol, var) - SCIPvarGetLbGlobal(var); 1899 delta = MAX(MIN(0.0, gap), delta); 1900 } 1901 if( SCIPisNegative(scip, delta) ) 1902 { 1903 /* if variable is integral, round delta down so that it will still have an integer value */ 1904 if( SCIPvarIsIntegral(var) ) 1905 delta = SCIPfloor(scip, delta); 1906 SCIP_CALL( SCIPincSolVal(scip, newsol, consdata->linvardecr, delta) ); 1907 /*lint --e{613} */ 1908 SCIPdebugMsg(scip, "increase <%s> by %g to %g to remedy rhs-violation %g of cons <%s>\n", 1909 SCIPvarGetName(var), delta, SCIPgetSolVal(scip, newsol, var), viol, SCIPconsGetName(conss[c])); 1910 1911 /* adjust constraint violation, if satisfied go on to next constraint */ 1912 viol -= consdata->linvardecrcoef * delta; 1913 if( SCIPisZero(scip, viol) ) 1914 continue; 1915 } 1916 } 1917 1918 /* still here... so probably we could not make constraint feasible due to variable bounds, thus give up */ 1919 break; 1920 } 1921 1922 /* if we have a solution that should satisfy all quadratic constraints and has a better objective than the current upper bound, 1923 * then pass it to the trysol heuristic 1924 */ 1925 if( c == nconss && (SCIPisInfinity(scip, SCIPgetUpperbound(scip)) || SCIPisSumLT(scip, SCIPgetSolTransObj(scip, newsol), SCIPgetUpperbound(scip))) ) 1926 { 1927 SCIPdebugMsg(scip, "pass solution with objective val %g to trysol heuristic\n", SCIPgetSolTransObj(scip, newsol)); 1928 1929 assert(conshdlrdata->trysolheur != NULL); 1930 SCIP_CALL( SCIPheurPassSolTrySol(scip, conshdlrdata->trysolheur, newsol) ); 1931 1932 *success = TRUE; 1933 } 1934 1935 SCIP_CALL( SCIPfreeSol(scip, &newsol) ); 1936 1937 return SCIP_OKAY; 1938 } 1939 1940 /** notify nonlinear handlers to add linearization in new solution that has been found 1941 * 1942 * The idea is that nonlinear handlers add globally valid tight estimators in a given solution as cuts to the cutpool. 1943 * 1944 * Essentially we want to ensure that the LP relaxation is tight in the new solution, if possible. 1945 * As the nonlinear handlers define the extended formulation, they should know whether it is possible to generate a 1946 * cut that is valid and supporting in the given solution. 1947 * For example, for convex constraints, we achieve this by linearizing. 1948 * For SOC, we also linearize, but on a a convex reformulation. 1949 * 1950 * Since linearization may happen in auxiliary variables, we ensure that auxiliary variables are set 1951 * to the eval-value of its expression, i.e., we change sol so it is also feasible in the extended formulation. 1952 */ 1953 static 1954 SCIP_RETCODE notifyNlhdlrNewsol( 1955 SCIP* scip, /**< SCIP data structure */ 1956 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 1957 SCIP_CONS** conss, /**< constraints */ 1958 int nconss, /**< number of constraints */ 1959 SCIP_SOL* sol, /**< reference point where to estimate */ 1960 SCIP_Bool solisbest /**< whether solution is best */ 1961 ) 1962 { 1963 SCIP_CONSDATA* consdata; 1964 SCIP_Longint soltag; 1965 SCIP_EXPRITER* it; 1966 SCIP_EXPR* expr; 1967 int c, e; 1968 1969 assert(scip != NULL); 1970 assert(conshdlr != NULL); 1971 assert(conss != NULL || nconss == 0); 1972 1973 ENFOLOG( SCIPinfoMessage(scip, enfologfile, "call nlhdlr sollinearize in new solution from <%s>\n", SCIPheurGetName(SCIPsolGetHeur(sol))); ) 1974 1975 /* TODO probably we just evaluated all expressions when checking the sol before it was added 1976 * would be nice to recognize this and skip reevaluating 1977 */ 1978 soltag = SCIPgetExprNewSoltag(scip); 1979 1980 SCIP_CALL( SCIPcreateExpriter(scip, &it) ); 1981 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) ); 1982 SCIPexpriterSetStagesDFS(it, SCIP_EXPRITER_LEAVEEXPR); 1983 1984 for( c = 0; c < nconss; ++c ) 1985 { 1986 /* skip constraints that are not enabled or deleted or have separation disabled */ 1987 if( !SCIPconsIsEnabled(conss[c]) || SCIPconsIsDeleted(conss[c]) || !SCIPconsIsSeparationEnabled(conss[c]) ) 1988 continue; 1989 assert(SCIPconsIsActive(conss[c])); 1990 1991 consdata = SCIPconsGetData(conss[c]); 1992 assert(consdata != NULL); 1993 1994 ENFOLOG( 1995 { 1996 int i; 1997 SCIPinfoMessage(scip, enfologfile, " constraint "); 1998 SCIP_CALL( SCIPprintCons(scip, conss[c], enfologfile) ); 1999 SCIPinfoMessage(scip, enfologfile, "\n and point\n"); 2000 for( i = 0; i < consdata->nvarexprs; ++i ) 2001 { 2002 SCIP_VAR* var; 2003 var = SCIPgetVarExprVar(consdata->varexprs[i]); 2004 SCIPinfoMessage(scip, enfologfile, " %-10s = %15g bounds: [%15g,%15g]\n", SCIPvarGetName(var), 2005 SCIPgetSolVal(scip, sol, var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)); 2006 } 2007 }) 2008 2009 SCIP_CALL( SCIPevalExpr(scip, consdata->expr, sol, soltag) ); 2010 assert(SCIPexprGetEvalValue(consdata->expr) != SCIP_INVALID); 2011 2012 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) ) 2013 { 2014 SCIP_EXPR_OWNERDATA* ownerdata; 2015 2016 ownerdata = SCIPexprGetOwnerData(expr); 2017 assert(ownerdata != NULL); 2018 2019 /* set value for auxvar in sol to value of expr, in case it is used to compute estimators higher up of this expression */ 2020 assert(SCIPexprGetEvalTag(expr) == soltag); 2021 assert(SCIPexprGetEvalValue(expr) != SCIP_INVALID); 2022 if( ownerdata->auxvar != NULL ) 2023 { 2024 SCIP_CALL( SCIPsetSolVal(scip, sol, ownerdata->auxvar, SCIPexprGetEvalValue(expr)) ); 2025 } 2026 2027 /* let nonlinear handler generate cuts by calling the sollinearize callback */ 2028 for( e = 0; e < ownerdata->nenfos; ++e ) 2029 { 2030 /* call sollinearize callback, if implemented by nlhdlr */ 2031 SCIP_CALL( SCIPnlhdlrSollinearize(scip, conshdlr, conss[c], 2032 ownerdata->enfos[e]->nlhdlr, expr, ownerdata->enfos[e]->nlhdlrexprdata, sol, solisbest, 2033 ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPAABOVE, 2034 ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPABELOW) ); 2035 } 2036 } 2037 } 2038 2039 SCIPfreeExpriter(&it); 2040 2041 return SCIP_OKAY; 2042 } 2043 2044 /** processes the event that a new primal solution has been found */ 2045 static 2046 SCIP_DECL_EVENTEXEC(processNewSolutionEvent) 2047 { 2048 SCIP_CONSHDLR* conshdlr; 2049 SCIP_CONSHDLRDATA* conshdlrdata; 2050 SCIP_SOL* sol; 2051 2052 assert(scip != NULL); 2053 assert(event != NULL); 2054 assert(eventdata != NULL); 2055 assert(eventhdlr != NULL); 2056 assert(SCIPeventGetType(event) & SCIP_EVENTTYPE_SOLFOUND); 2057 2058 conshdlr = (SCIP_CONSHDLR*)eventdata; 2059 2060 if( SCIPconshdlrGetNConss(conshdlr) == 0 ) 2061 return SCIP_OKAY; 2062 2063 sol = SCIPeventGetSol(event); 2064 assert(sol != NULL); 2065 2066 conshdlrdata = SCIPconshdlrGetData(conshdlr); 2067 assert(conshdlrdata != NULL); 2068 2069 /* we are only interested in solution coming from some heuristic other than trysol, but not from the tree 2070 * the reason for ignoring trysol solutions is that they may come ~~from an NLP solve in sepalp, where we already added linearizations, or are~~ 2071 * from the tree, but postprocessed via proposeFeasibleSolution 2072 */ 2073 if( SCIPsolGetHeur(sol) == NULL || SCIPsolGetHeur(sol) == conshdlrdata->trysolheur ) 2074 return SCIP_OKAY; 2075 2076 SCIPdebugMsg(scip, "caught new sol event %" SCIP_EVENTTYPE_FORMAT " from heur <%s>\n", SCIPeventGetType(event), SCIPheurGetName(SCIPsolGetHeur(sol))); 2077 2078 SCIP_CALL( notifyNlhdlrNewsol(scip, conshdlr, SCIPconshdlrGetConss(conshdlr), SCIPconshdlrGetNConss(conshdlr), sol, (SCIPeventGetType(event) & SCIP_EVENTTYPE_BESTSOLFOUND) != 0) ); 2079 2080 return SCIP_OKAY; 2081 } 2082 2083 /** tightens the bounds of the auxiliary variable associated with an expression (or original variable if being a variable-expression) according to given bounds 2084 * 2085 * The given bounds may very well be the exprs activity (when called from forwardPropExpr()), but can also be some 2086 * tighter bounds (when called from SCIPtightenExprIntervalNonlinear()). 2087 * 2088 * Nothing will happen if SCIP is not in presolve or solve. 2089 */ 2090 static 2091 SCIP_RETCODE tightenAuxVarBounds( 2092 SCIP* scip, /**< SCIP data structure */ 2093 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 2094 SCIP_EXPR* expr, /**< expression whose auxvar is to be tightened */ 2095 SCIP_INTERVAL bounds, /**< bounds to be used for tightening (must not be empty) */ 2096 SCIP_Bool* cutoff, /**< buffer to store whether a cutoff was detected */ 2097 int* ntightenings /**< buffer to add the total number of tightenings, or NULL */ 2098 ) 2099 { 2100 SCIP_VAR* var; 2101 SCIP_Bool tightenedlb; 2102 SCIP_Bool tightenedub; 2103 SCIP_Bool force; 2104 2105 assert(scip != NULL); 2106 assert(conshdlr != NULL); 2107 assert(expr != NULL); 2108 assert(cutoff != NULL); 2109 2110 /* the given bounds must not be empty (we could cope, but we shouldn't be called in this situation) */ 2111 assert(!SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, bounds)); 2112 2113 *cutoff = FALSE; 2114 2115 /* do not tighten variable in problem stage (important for unittests) 2116 * TODO put some kind of #ifdef UNITTEST around this 2117 */ 2118 if( SCIPgetStage(scip) < SCIP_STAGE_INITPRESOLVE && SCIPgetStage(scip) > SCIP_STAGE_SOLVING ) 2119 return SCIP_OKAY; 2120 2121 var = SCIPgetExprAuxVarNonlinear(expr); 2122 if( var == NULL ) 2123 return SCIP_OKAY; 2124 2125 /* force tightening if conshdlrdata says so or it would mean fixing the variable */ 2126 force = SCIPconshdlrGetData(conshdlr)->forceboundtightening || SCIPisEQ(scip, bounds.inf, bounds.sup); 2127 2128 /* try to tighten lower bound of (auxiliary) variable */ 2129 SCIP_CALL( SCIPtightenVarLb(scip, var, bounds.inf, force, cutoff, &tightenedlb) ); 2130 if( tightenedlb ) 2131 { 2132 if( ntightenings != NULL ) 2133 ++*ntightenings; 2134 SCIPdebugMsg(scip, "tightened lb on auxvar <%s> to %.15g (forced:%u)\n", SCIPvarGetName(var), SCIPvarGetLbLocal(var), force); 2135 } 2136 if( *cutoff ) 2137 { 2138 SCIPdebugMsg(scip, "cutoff when tightening lb on auxvar <%s> to %.15g\n", SCIPvarGetName(var), bounds.inf); 2139 return SCIP_OKAY; 2140 } 2141 2142 /* try to tighten upper bound of (auxiliary) variable */ 2143 SCIP_CALL( SCIPtightenVarUb(scip, var, bounds.sup, force, cutoff, &tightenedub) ); 2144 if( tightenedub ) 2145 { 2146 if( ntightenings != NULL ) 2147 ++*ntightenings; 2148 SCIPdebugMsg(scip, "tightened ub on auxvar <%s> to %.15g (forced:%u)\n", SCIPvarGetName(var), SCIPvarGetUbLocal(var), force); 2149 } 2150 if( *cutoff ) 2151 { 2152 SCIPdebugMsg(scip, "cutoff when tightening ub on auxvar <%s> to %.15g\n", SCIPvarGetName(var), bounds.sup); 2153 return SCIP_OKAY; 2154 } 2155 2156 /* TODO expr->activity should have been reevaluated now due to boundchange-events, but it used to relax bounds 2157 * that seems unnecessary and we could easily undo this here, e.g., 2158 * if( tightenedlb ) expr->activity.inf = bounds.inf 2159 */ 2160 2161 return SCIP_OKAY; 2162 } 2163 2164 /** propagate bounds of the expressions in a given expression tree (that is, updates activity intervals) 2165 * and tries to tighten the bounds of the auxiliary variables accordingly 2166 */ 2167 static 2168 SCIP_RETCODE forwardPropExpr( 2169 SCIP* scip, /**< SCIP data structure */ 2170 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 2171 SCIP_EXPR* rootexpr, /**< expression */ 2172 SCIP_Bool tightenauxvars, /**< should the bounds of auxiliary variables be tightened? */ 2173 SCIP_Bool* infeasible, /**< buffer to store whether the problem is infeasible (NULL if not needed) */ 2174 int* ntightenings /**< buffer to store the number of auxiliary variable tightenings (NULL if not needed) */ 2175 ) 2176 { 2177 SCIP_EXPRITER* it; 2178 SCIP_EXPR* expr; 2179 SCIP_EXPR_OWNERDATA* ownerdata; 2180 SCIP_CONSHDLRDATA* conshdlrdata; 2181 2182 assert(scip != NULL); 2183 assert(rootexpr != NULL); 2184 2185 if( infeasible != NULL ) 2186 *infeasible = FALSE; 2187 if( ntightenings != NULL ) 2188 *ntightenings = 0; 2189 2190 conshdlrdata = SCIPconshdlrGetData(conshdlr); 2191 assert(conshdlrdata != NULL); 2192 2193 /* if value is valid and empty, then we cannot improve, so do nothing */ 2194 if( SCIPexprGetActivityTag(rootexpr) >= conshdlrdata->lastboundrelax && SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(rootexpr)) ) 2195 { 2196 SCIPdebugMsg(scip, "stored activity of root expr is empty and valid (activitytag >= lastboundrelax (%" SCIP_LONGINT_FORMAT ")), skip forwardPropExpr -> cutoff\n", conshdlrdata->lastboundrelax); 2197 2198 if( infeasible != NULL ) 2199 *infeasible = TRUE; 2200 2201 /* just update tag to curboundstag */ 2202 SCIPexprSetActivity(rootexpr, SCIPexprGetActivity(rootexpr), conshdlrdata->curboundstag); 2203 2204 return SCIP_OKAY; 2205 } 2206 2207 /* if value is up-to-date, then nothing to do */ 2208 if( SCIPexprGetActivityTag(rootexpr) == conshdlrdata->curboundstag ) 2209 { 2210 SCIPdebugMsg(scip, "activitytag of root expr equals curboundstag (%" SCIP_LONGINT_FORMAT "), skip forwardPropExpr\n", conshdlrdata->curboundstag); 2211 2212 assert(!SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(rootexpr))); /* handled in previous if() */ 2213 2214 return SCIP_OKAY; 2215 } 2216 2217 ownerdata = SCIPexprGetOwnerData(rootexpr); 2218 assert(ownerdata != NULL); 2219 2220 /* if activity of rootexpr is not used, but expr participated in detect (nenfos >= 0), then we do nothing 2221 * it seems wrong to be called for such an expression (unless we are in detect at the moment), so I add a SCIPABORT() 2222 * during detect, we are in some in-between state where we may want to eval activity 2223 * on exprs that we did not notify about their activity usage 2224 */ 2225 if( ownerdata->nenfos >= 0 && ownerdata->nactivityusesprop == 0 && ownerdata->nactivityusessepa == 0 && !conshdlrdata->indetect) 2226 { 2227 #ifdef DEBUG_PROP 2228 SCIPdebugMsg(scip, "root expr activity is not used but enfo initialized, skip inteval\n"); 2229 #endif 2230 SCIPABORT(); 2231 return SCIP_OKAY; 2232 } 2233 2234 SCIP_CALL( SCIPcreateExpriter(scip, &it) ); 2235 SCIP_CALL( SCIPexpriterInit(it, rootexpr, SCIP_EXPRITER_DFS, TRUE) ); 2236 SCIPexpriterSetStagesDFS(it, SCIP_EXPRITER_VISITINGCHILD | SCIP_EXPRITER_LEAVEEXPR); 2237 2238 for( expr = SCIPexpriterGetCurrent(it); !SCIPexpriterIsEnd(it); ) 2239 { 2240 switch( SCIPexpriterGetStageDFS(it) ) 2241 { 2242 case SCIP_EXPRITER_VISITINGCHILD : 2243 { 2244 /* skip child if it has been evaluated already */ 2245 SCIP_EXPR* child; 2246 2247 child = SCIPexpriterGetChildExprDFS(it); 2248 if( conshdlrdata->curboundstag == SCIPexprGetActivityTag(child) ) 2249 { 2250 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(child)) && infeasible != NULL ) 2251 *infeasible = TRUE; 2252 2253 expr = SCIPexpriterSkipDFS(it); 2254 continue; 2255 } 2256 2257 break; 2258 } 2259 2260 case SCIP_EXPRITER_LEAVEEXPR : 2261 { 2262 SCIP_INTERVAL activity; 2263 2264 /* we should not have entered this expression if its activity was already up to date */ 2265 assert(SCIPexprGetActivityTag(expr) < conshdlrdata->curboundstag); 2266 2267 ownerdata = SCIPexprGetOwnerData(expr); 2268 assert(ownerdata != NULL); 2269 2270 /* for var exprs where varevents are catched, activity is updated immediately when the varbound has been changed 2271 * so we can assume that the activity is up to date for all these variables 2272 * UNLESS we changed the method used to evaluate activity of variable expressions 2273 * or we currently use global bounds (varevents are catched for local bound changes only) 2274 */ 2275 if( SCIPisExprVar(scip, expr) && ownerdata->filterpos >= 0 && 2276 SCIPexprGetActivityTag(expr) >= conshdlrdata->lastvaractivitymethodchange && !conshdlrdata->globalbounds ) 2277 { 2278 #ifndef NDEBUG 2279 SCIP_INTERVAL exprhdlrinterval; 2280 2281 SCIP_CALL( SCIPcallExprInteval(scip, expr, &exprhdlrinterval, conshdlrdata->intevalvar, conshdlrdata) ); 2282 assert(SCIPisRelEQ(scip, exprhdlrinterval.inf, SCIPexprGetActivity(expr).inf)); 2283 assert(SCIPisRelEQ(scip, exprhdlrinterval.sup, SCIPexprGetActivity(expr).sup)); 2284 #endif 2285 #ifdef DEBUG_PROP 2286 SCIPdebugMsg(scip, "skip interval evaluation of expr for var <%s> [%g,%g]\n", SCIPvarGetName(SCIPgetVarExprVar(expr)), SCIPexprGetActivity(expr).inf, SCIPexprGetActivity(expr).sup); 2287 #endif 2288 SCIPexprSetActivity(expr, SCIPexprGetActivity(expr), conshdlrdata->curboundstag); 2289 2290 break; 2291 } 2292 2293 if( SCIPexprGetActivityTag(expr) < conshdlrdata->lastboundrelax ) 2294 { 2295 /* start with entire activity if current one is invalid */ 2296 SCIPintervalSetEntire(SCIP_INTERVAL_INFINITY, &activity); 2297 } 2298 else if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(expr)) ) 2299 { 2300 /* If already empty, then don't try to compute even better activity. 2301 * If cons_nonlinear were alone, then we should have noted that we are infeasible 2302 * so an assert(infeasible == NULL || *infeasible) should work here. 2303 * However, after reporting a cutoff due to expr->activity being empty, 2304 * SCIP may wander to a different node and call propagation again. 2305 * If no bounds in a nonlinear constraint have been relaxed when switching nodes 2306 * (so expr->activitytag >= conshdlrdata->lastboundrelax), then 2307 * we will still have expr->activity being empty, but will have forgotten 2308 * that we found infeasibility here before (!2221#note_134120). 2309 * Therefore we just set *infeasibility=TRUE here and stop. 2310 */ 2311 if( infeasible != NULL ) 2312 *infeasible = TRUE; 2313 SCIPdebugMsg(scip, "expr %p already has empty activity -> cutoff\n", (void*)expr); 2314 break; 2315 } 2316 else 2317 { 2318 /* start with current activity, since it is valid */ 2319 activity = SCIPexprGetActivity(expr); 2320 } 2321 2322 /* if activity of expr is not used, but expr participated in detect (nenfos >= 0), then do nothing */ 2323 if( ownerdata->nenfos >= 0 && ownerdata->nactivityusesprop == 0 && ownerdata->nactivityusessepa == 0 && !conshdlrdata->indetect ) 2324 { 2325 #ifdef DEBUG_PROP 2326 SCIPdebugMsg(scip, "expr %p activity is not used but enfo initialized, skip inteval\n", (void*)expr); 2327 #endif 2328 break; 2329 } 2330 2331 #ifdef DEBUG_PROP 2332 SCIPdebugMsg(scip, "interval evaluation of expr %p ", (void*)expr); 2333 SCIP_CALL( SCIPprintExpr(scip, expr, NULL) ); 2334 SCIPdebugMsgPrint(scip, ", current activity = [%.20g, %.20g]\n", SCIPexprGetActivity(expr).inf, SCIPexprGetActivity(expr).sup); 2335 #endif 2336 2337 /* run interval eval of nonlinear handlers or expression handler */ 2338 if( ownerdata->nenfos > 0 ) 2339 { 2340 SCIP_NLHDLR* nlhdlr; 2341 SCIP_INTERVAL nlhdlrinterval; 2342 int e; 2343 2344 /* for expressions with enforcement, nlhdlrs take care of interval evaluation */ 2345 for( e = 0; e < ownerdata->nenfos && !SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, activity); ++e ) 2346 { 2347 /* skip nlhdlr if it does not want to participate in activity computation */ 2348 if( (ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_ACTIVITY) == 0 ) 2349 continue; 2350 2351 nlhdlr = ownerdata->enfos[e]->nlhdlr; 2352 assert(nlhdlr != NULL); 2353 2354 /* skip nlhdlr if it does not provide interval evaluation (so it may only provide reverse propagation) */ 2355 if( !SCIPnlhdlrHasIntEval(nlhdlr) ) 2356 continue; 2357 2358 /* let nlhdlr evaluate current expression */ 2359 nlhdlrinterval = activity; 2360 SCIP_CALL( SCIPnlhdlrInteval(scip, nlhdlr, expr, ownerdata->enfos[e]->nlhdlrexprdata, 2361 &nlhdlrinterval, conshdlrdata->intevalvar, conshdlrdata) ); 2362 #ifdef DEBUG_PROP 2363 SCIPdebugMsg(scip, " nlhdlr <%s>::inteval = [%.20g, %.20g]", SCIPnlhdlrGetName(nlhdlr), nlhdlrinterval.inf, nlhdlrinterval.sup); 2364 #endif 2365 2366 /* update activity by intersecting with computed activity */ 2367 SCIPintervalIntersectEps(&activity, SCIPepsilon(scip), activity, nlhdlrinterval); 2368 #ifdef DEBUG_PROP 2369 SCIPdebugMsgPrint(scip, " -> new activity: [%.20g, %.20g]\n", activity.inf, activity.sup); 2370 #endif 2371 } 2372 } 2373 else 2374 { 2375 /* for node without enforcement (before or during detect), call the callback of the exprhdlr directly */ 2376 SCIP_INTERVAL exprhdlrinterval = activity; 2377 SCIP_CALL( SCIPcallExprInteval(scip, expr, &exprhdlrinterval, conshdlrdata->intevalvar, conshdlrdata) ); 2378 #ifdef DEBUG_PROP 2379 SCIPdebugMsg(scip, " exprhdlr <%s>::inteval = [%.20g, %.20g]", SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)), exprhdlrinterval.inf, exprhdlrinterval.sup); 2380 #endif 2381 2382 /* update expr->activity by intersecting with computed activity */ 2383 SCIPintervalIntersectEps(&activity, SCIPepsilon(scip), activity, exprhdlrinterval); 2384 #ifdef DEBUG_PROP 2385 SCIPdebugMsgPrint(scip, " -> new activity: [%.20g, %.20g]\n", activity.inf, activity.sup); 2386 #endif 2387 } 2388 2389 /* if expression is integral, then we try to tighten the interval bounds a bit 2390 * this should undo the addition of some unnecessary safety added by use of nextafter() in interval arithmetics, e.g., when doing pow() 2391 * it would be ok to use ceil() and floor(), but for safety we use SCIPceil and SCIPfloor for now 2392 * do this only if using boundtightening-inteval and not in redundancy check (there we really want to relax all variables) 2393 * boundtightening-inteval does not relax integer variables, so can omit expressions without children 2394 * (constants should be ok, too) 2395 */ 2396 if( SCIPexprIsIntegral(expr) && conshdlrdata->intevalvar == intEvalVarBoundTightening && SCIPexprGetNChildren(expr) > 0 ) 2397 { 2398 if( activity.inf > -SCIP_INTERVAL_INFINITY ) 2399 activity.inf = SCIPceil(scip, activity.inf); 2400 if( activity.sup < SCIP_INTERVAL_INFINITY ) 2401 activity.sup = SCIPfloor(scip, activity.sup); 2402 #ifdef DEBUG_PROP 2403 SCIPdebugMsg(scip, " applying integrality: [%.20g, %.20g]\n", activity.inf, activity.sup); 2404 #endif 2405 } 2406 2407 /* mark the current node to be infeasible if either the lower/upper bound is above/below +/- SCIPinfinity() 2408 * TODO this is a problem if dual-presolve fixed a variable to +/- infinity 2409 */ 2410 if( SCIPisInfinity(scip, activity.inf) || SCIPisInfinity(scip, -activity.sup) ) 2411 { 2412 SCIPdebugMsg(scip, "cut off due to activity [%g,%g] beyond infinity\n", activity.inf, activity.sup); 2413 SCIPintervalSetEmpty(&activity); 2414 } 2415 2416 /* now finally store activity in expr */ 2417 SCIPexprSetActivity(expr, activity, conshdlrdata->curboundstag); 2418 2419 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, activity) ) 2420 { 2421 if( infeasible != NULL ) 2422 *infeasible = TRUE; 2423 } 2424 else if( tightenauxvars && ownerdata->auxvar != NULL ) 2425 { 2426 SCIP_Bool tighteninfeasible; 2427 2428 SCIP_CALL( tightenAuxVarBounds(scip, conshdlr, expr, activity, &tighteninfeasible, ntightenings) ); 2429 if( tighteninfeasible ) 2430 { 2431 if( infeasible != NULL ) 2432 *infeasible = TRUE; 2433 SCIPintervalSetEmpty(&activity); 2434 SCIPexprSetActivity(expr, activity, conshdlrdata->curboundstag); 2435 } 2436 } 2437 2438 break; 2439 } 2440 2441 default: 2442 /* you should never be here */ 2443 SCIPerrorMessage("unexpected iterator stage\n"); 2444 SCIPABORT(); 2445 break; 2446 } 2447 2448 expr = SCIPexpriterGetNext(it); 2449 } 2450 2451 SCIPfreeExpriter(&it); 2452 2453 return SCIP_OKAY; 2454 } 2455 2456 /** returns whether intersecting `oldinterval` with `newinterval` would provide a properly smaller interval 2457 * 2458 * If `subsetsufficient` is TRUE, then the intersection being smaller than oldinterval is sufficient. 2459 * 2460 * If `subsetsufficient` is FALSE, then we require 2461 * - a change from an unbounded interval to a bounded one, or 2462 * - or a change from an unfixed (width > epsilon) to a fixed interval, or 2463 * - a minimal tightening of one of the interval bounds as defined by SCIPis{Lb,Ub}Better(). 2464 */ 2465 static 2466 SCIP_Bool isIntervalBetter( 2467 SCIP* scip, /**< SCIP data structure */ 2468 SCIP_Bool subsetsufficient, /**< whether the intersection being a proper subset of oldinterval is sufficient */ 2469 SCIP_INTERVAL newinterval, /**< new interval */ 2470 SCIP_INTERVAL oldinterval /**< old interval */ 2471 ) 2472 { 2473 assert(scip != NULL); 2474 assert(!SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, newinterval)); 2475 assert(!SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, oldinterval)); 2476 2477 if( subsetsufficient ) 2478 /* oldinterval \cap newinterval < oldinterval iff not oldinterval is subset of newinterval */ 2479 return !SCIPintervalIsSubsetEQ(SCIP_INTERVAL_INFINITY, oldinterval, newinterval); 2480 2481 /* check whether lower bound of interval becomes finite */ 2482 if( oldinterval.inf <= -SCIP_INTERVAL_INFINITY && newinterval.inf > -SCIP_INTERVAL_INFINITY ) 2483 return TRUE; 2484 2485 /* check whether upper bound of interval becomes finite */ 2486 if( oldinterval.sup >= SCIP_INTERVAL_INFINITY && newinterval.sup > SCIP_INTERVAL_INFINITY ) 2487 return TRUE; 2488 2489 /* check whether intersection will have width <= epsilon, if oldinterval doesn't have yet */ 2490 if( !SCIPisEQ(scip, oldinterval.inf, oldinterval.sup) && SCIPisEQ(scip, MAX(oldinterval.inf, newinterval.inf), MIN(oldinterval.sup, newinterval.sup)) ) 2491 return TRUE; 2492 2493 /* check whether lower bound on interval will be better by SCIP's quality measures for boundchanges */ 2494 if( SCIPisLbBetter(scip, newinterval.inf, oldinterval.inf, oldinterval.sup) ) 2495 return TRUE; 2496 2497 /* check whether upper bound on interval will be better by SCIP's quality measures for boundchanges */ 2498 if( SCIPisUbBetter(scip, newinterval.sup, oldinterval.inf, oldinterval.sup) ) 2499 return TRUE; 2500 2501 return FALSE; 2502 } 2503 2504 /** propagates bounds for each sub-expression in the `reversepropqueue` by starting from the root expressions 2505 * 2506 * The expression will be traversed in breadth first search by using this queue. 2507 * 2508 * @note Calling this function requires feasible intervals for each sub-expression; this is guaranteed by calling 2509 * forwardPropExpr() before calling this function. 2510 * 2511 * @note Calling this function with `*infeasible` = TRUE will only empty the queue. 2512 */ 2513 static 2514 SCIP_RETCODE reversePropQueue( 2515 SCIP* scip, /**< SCIP data structure */ 2516 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 2517 SCIP_Bool* infeasible, /**< buffer to update whether an expression's bounds were propagated to an empty interval */ 2518 int* ntightenings /**< buffer to store the number of (variable) tightenings */ 2519 ) 2520 { 2521 SCIP_CONSHDLRDATA* conshdlrdata; 2522 SCIP_EXPR* expr; 2523 SCIP_EXPR_OWNERDATA* ownerdata; 2524 2525 assert(infeasible != NULL); 2526 assert(ntightenings != NULL); 2527 2528 conshdlrdata = SCIPconshdlrGetData(conshdlr); 2529 assert(conshdlrdata != NULL); 2530 2531 *ntightenings = 0; 2532 2533 /* main loop that calls reverse propagation for expressions on the queue 2534 * when reverseprop finds a tightening for an expression, then that expression is added to the queue (within the reverseprop call) 2535 */ 2536 while( !SCIPqueueIsEmpty(conshdlrdata->reversepropqueue) && !(*infeasible) ) 2537 { 2538 SCIP_INTERVAL propbounds; 2539 int e; 2540 2541 expr = (SCIP_EXPR*) SCIPqueueRemove(conshdlrdata->reversepropqueue); 2542 assert(expr != NULL); 2543 2544 ownerdata = SCIPexprGetOwnerData(expr); 2545 assert(ownerdata != NULL); 2546 2547 assert(ownerdata->inpropqueue); 2548 /* mark that the expression is not in the queue anymore */ 2549 ownerdata->inpropqueue = FALSE; 2550 2551 /* since the expr was in the propagation queue, the propbounds should belong to current propagation and should not be empty 2552 * (propbounds being entire doesn't make much sense, so assert this for now, too, but that could be removed) 2553 */ 2554 assert(ownerdata->propboundstag == conshdlrdata->curpropboundstag); 2555 assert(!SCIPintervalIsEntire(SCIP_INTERVAL_INFINITY, ownerdata->propbounds)); 2556 assert(!SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, ownerdata->propbounds)); 2557 2558 /* this intersects propbounds with activity and auxvar bounds 2559 * I doubt this would be much helpful, since propbounds are already subset of activity and we also propagate 2560 * auxvar bounds separately, so disabling this for now 2561 */ 2562 #ifdef SCIP_DISABLED_CODE 2563 propbounds = SCIPgetExprBoundsNonlinear(scip, expr); 2564 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, propbounds) ) 2565 { 2566 *infeasible = TRUE; 2567 break; 2568 } 2569 #else 2570 propbounds = ownerdata->propbounds; 2571 #endif 2572 2573 if( ownerdata->nenfos > 0 ) 2574 { 2575 /* for nodes with enforcement, call reverse propagation callbacks of nlhdlrs */ 2576 for( e = 0; e < ownerdata->nenfos && !*infeasible; ++e ) 2577 { 2578 SCIP_NLHDLR* nlhdlr; 2579 int nreds; 2580 2581 /* skip nlhdlr if it does not want to participate in activity computation */ 2582 if( (ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_ACTIVITY) == 0 ) 2583 continue; 2584 2585 nlhdlr = ownerdata->enfos[e]->nlhdlr; 2586 assert(nlhdlr != NULL); 2587 2588 /* call the reverseprop of the nlhdlr */ 2589 #ifdef SCIP_DEBUG 2590 SCIPdebugMsg(scip, "call reverse propagation for "); 2591 SCIP_CALL( SCIPprintExpr(scip, expr, NULL) ); 2592 SCIPdebugMsgPrint(scip, " in [%g,%g] using nlhdlr <%s>\n", propbounds.inf, propbounds.sup, SCIPnlhdlrGetName(nlhdlr)); 2593 #endif 2594 2595 nreds = 0; 2596 SCIP_CALL( SCIPnlhdlrReverseprop(scip, conshdlr, nlhdlr, expr, ownerdata->enfos[e]->nlhdlrexprdata, propbounds, infeasible, &nreds) ); 2597 assert(nreds >= 0); 2598 *ntightenings += nreds; 2599 } 2600 } 2601 else if( SCIPexprhdlrHasReverseProp(SCIPexprGetHdlr(expr)) ) 2602 { 2603 /* if expr without enforcement (before detect), call reverse propagation callback of exprhdlr directly */ 2604 SCIP_INTERVAL* childrenbounds; 2605 int c; 2606 2607 #ifdef SCIP_DEBUG 2608 SCIPdebugMsg(scip, "call reverse propagation for "); 2609 SCIP_CALL( SCIPprintExpr(scip, expr, NULL) ); 2610 SCIPdebugMsgPrint(scip, " in [%g,%g] using exprhdlr <%s>\n", SCIPexprGetActivity(expr).inf, SCIPexprGetActivity(expr).sup, SCIPexprhdlrGetName(SCIPexprGetHdlr(expr))); 2611 #endif 2612 2613 /* if someone added an expr without nlhdlr into the reversepropqueue, then this must be because its enfo hasn't 2614 * been initialized in detectNlhdlr yet (nenfos < 0) 2615 */ 2616 assert(ownerdata->nenfos < 0); 2617 2618 SCIP_CALL( SCIPallocBufferArray(scip, &childrenbounds, SCIPexprGetNChildren(expr)) ); 2619 for( c = 0; c < SCIPexprGetNChildren(expr); ++c ) 2620 childrenbounds[c] = SCIPgetExprBoundsNonlinear(scip, SCIPexprGetChildren(expr)[c]); 2621 2622 /* call the reverseprop of the exprhdlr */ 2623 SCIP_CALL( SCIPcallExprReverseprop(scip, expr, propbounds, childrenbounds, infeasible) ); 2624 2625 if( !*infeasible ) 2626 for( c = 0; c < SCIPexprGetNChildren(expr); ++c ) 2627 { 2628 SCIP_CALL( SCIPtightenExprIntervalNonlinear(scip, SCIPexprGetChildren(expr)[c], childrenbounds[c], infeasible, ntightenings) ); 2629 } 2630 2631 SCIPfreeBufferArray(scip, &childrenbounds); 2632 } 2633 } 2634 2635 /* reset inpropqueue for all remaining expr's in queue (can happen in case of early stop due to infeasibility) */ 2636 while( !SCIPqueueIsEmpty(conshdlrdata->reversepropqueue) ) 2637 { 2638 expr = (SCIP_EXPR*) SCIPqueueRemove(conshdlrdata->reversepropqueue); 2639 assert(expr != NULL); 2640 2641 ownerdata = SCIPexprGetOwnerData(expr); 2642 assert(ownerdata != NULL); 2643 2644 /* mark that the expression is not in the queue anymore */ 2645 ownerdata->inpropqueue = FALSE; 2646 } 2647 2648 return SCIP_OKAY; 2649 } 2650 2651 /** calls domain propagation for a given set of constraints 2652 * 2653 * The algorithm alternates calls of forward and reverse propagation. 2654 * Forward propagation ensures that activity of expressions is up to date. 2655 * Reverse propagation tries to derive tighter variable bounds by reversing the activity computation, using the constraints 2656 * [lhs,rhs] interval as starting point. 2657 * 2658 * The propagation algorithm works as follows: 2659 * 1. apply forward propagation (update activities) for all constraints not marked as propagated 2660 * 2. if presolve or propauxvars is disabled: collect expressions for which the constraint sides provide tighter bounds 2661 * if solve and propauxvars is enabled: collect expressions for which auxvars (including those in root exprs) 2662 * provide tighter bounds 2663 * 3. apply reverse propagation to all collected expressions; don't explore 2664 * sub-expressions which have not changed since the beginning of the propagation loop 2665 * 4. if we have found enough tightenings go to 1, otherwise leave propagation loop 2666 * 2667 * @note After calling forward propagation for a constraint, we mark this constraint as propagated. This flag might be 2668 * reset during the reverse propagation when we find a bound tightening of a variable expression contained in the 2669 * constraint. Resetting this flag is done in the EVENTEXEC callback of the event handler 2670 * 2671 * TODO should we distinguish between expressions where activity information is used for separation and those where not, 2672 * e.g., try less to propagate on convex constraints? 2673 */ 2674 static 2675 SCIP_RETCODE propConss( 2676 SCIP* scip, /**< SCIP data structure */ 2677 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 2678 SCIP_CONS** conss, /**< constraints to propagate */ 2679 int nconss, /**< total number of constraints */ 2680 SCIP_Bool force, /**< force tightening even if below bound strengthening tolerance */ 2681 SCIP_RESULT* result, /**< pointer to store the result */ 2682 int* nchgbds /**< buffer to add the number of changed bounds */ 2683 ) 2684 { 2685 SCIP_CONSHDLRDATA* conshdlrdata; 2686 SCIP_CONSDATA* consdata; 2687 SCIP_EXPR_OWNERDATA* ownerdata; 2688 SCIP_Bool cutoff = FALSE; 2689 SCIP_INTERVAL conssides; 2690 int ntightenings; 2691 int roundnr; 2692 SCIP_EXPRITER* revpropcollectit = NULL; 2693 int i; 2694 2695 assert(scip != NULL); 2696 assert(conshdlr != NULL); 2697 assert(conss != NULL); 2698 assert(nconss >= 0); 2699 assert(result != NULL); 2700 assert(nchgbds != NULL); 2701 assert(*nchgbds >= 0); 2702 2703 /* no constraints to propagate */ 2704 if( nconss == 0 ) 2705 { 2706 *result = SCIP_DIDNOTRUN; 2707 return SCIP_OKAY; 2708 } 2709 2710 conshdlrdata = SCIPconshdlrGetData(conshdlr); 2711 assert(conshdlrdata != NULL); 2712 #ifndef CR_API /* this assert may not work in unittests due to having this code compiled twice, #3543 */ 2713 assert(conshdlrdata->intevalvar == intEvalVarBoundTightening); 2714 #endif 2715 assert(!conshdlrdata->globalbounds); 2716 2717 *result = SCIP_DIDNOTFIND; 2718 roundnr = 0; 2719 2720 /* tightenAuxVarBounds() needs to know whether boundtightenings are to be forced */ 2721 conshdlrdata->forceboundtightening = force; 2722 2723 /* invalidate all propbounds (probably not needed) */ 2724 ++conshdlrdata->curpropboundstag; 2725 2726 /* create iterator that we will use if we need to look at all auxvars */ 2727 if( conshdlrdata->propauxvars ) 2728 { 2729 SCIP_CALL( SCIPcreateExpriter(scip, &revpropcollectit) ); 2730 } 2731 2732 /* main propagation loop */ 2733 do 2734 { 2735 SCIPdebugMsg(scip, "start propagation round %d\n", roundnr); 2736 2737 assert(SCIPqueueIsEmpty(conshdlrdata->reversepropqueue)); 2738 2739 /* apply forward propagation (update expression activities) 2740 * and add promising root expressions into queue for reversepropagation 2741 */ 2742 for( i = 0; i < nconss; ++i ) 2743 { 2744 consdata = SCIPconsGetData(conss[i]); 2745 assert(consdata != NULL); 2746 2747 /* skip deleted, non-active, or propagation-disabled constraints */ 2748 if( SCIPconsIsDeleted(conss[i]) || !SCIPconsIsActive(conss[i]) || !SCIPconsIsPropagationEnabled(conss[i]) ) 2749 continue; 2750 2751 /* skip already propagated constraints, i.e., constraints where no (original) variable has changed and thus 2752 * activity didn't change 2753 */ 2754 if( consdata->ispropagated ) 2755 continue; 2756 2757 /* update activities in expression */ 2758 SCIPdebugMsg(scip, "call forwardPropExpr() for constraint <%s> (round %d): ", SCIPconsGetName(conss[i]), roundnr); 2759 SCIPdebugPrintCons(scip, conss[i], NULL); 2760 2761 ntightenings = 0; 2762 SCIP_CALL( forwardPropExpr(scip, conshdlr, consdata->expr, TRUE, &cutoff, &ntightenings) ); 2763 assert(cutoff || !SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(consdata->expr))); 2764 2765 if( cutoff ) 2766 { 2767 SCIPdebugMsg(scip, " -> cutoff in forwardPropExpr (due to domain error or auxvar tightening) of constraint <%s>\n", SCIPconsGetName(conss[i])); 2768 *result = SCIP_CUTOFF; 2769 break; 2770 } 2771 2772 ownerdata = SCIPexprGetOwnerData(consdata->expr); 2773 2774 /* TODO for a constraint that only has an auxvar for consdata->expr (e.g., convex quadratic), we could also just do the if(TRUE)-branch */ 2775 if( !conshdlrdata->propauxvars || ownerdata->auxvar == NULL ) 2776 { 2777 /* check whether constraint sides (relaxed by epsilon) or auxvar bounds provide a tightening 2778 * (if we have auxvar (not in presolve), then bounds of the auxvar are initially set to constraint sides, 2779 * so taking auxvar bounds is enough) 2780 */ 2781 if( ownerdata->auxvar == NULL ) 2782 { 2783 /* relax sides by SCIPepsilon() and handle infinite sides */ 2784 SCIP_Real lhs = SCIPisInfinity(scip, -consdata->lhs) ? -SCIP_INTERVAL_INFINITY : consdata->lhs - conshdlrdata->conssiderelaxamount; 2785 SCIP_Real rhs = SCIPisInfinity(scip, consdata->rhs) ? SCIP_INTERVAL_INFINITY : consdata->rhs + conshdlrdata->conssiderelaxamount; 2786 SCIPintervalSetBounds(&conssides, lhs, rhs); 2787 } 2788 else 2789 { 2790 conssides = intEvalVarBoundTightening(scip, ownerdata->auxvar, (void*)conshdlrdata); 2791 } 2792 SCIP_CALL( SCIPtightenExprIntervalNonlinear(scip, consdata->expr, conssides, &cutoff, &ntightenings) ); 2793 } 2794 else 2795 { 2796 /* check whether bounds of any auxvar used in constraint provides a tightening 2797 * (for the root expression, bounds of auxvar are initially set to constraint sides) 2798 * but skip exprs that have an auxvar, but do not participate in propagation 2799 */ 2800 SCIP_EXPR* expr; 2801 2802 assert(revpropcollectit != NULL); 2803 SCIP_CALL( SCIPexpriterInit(revpropcollectit, consdata->expr, SCIP_EXPRITER_BFS, FALSE) ); 2804 for( expr = SCIPexpriterGetCurrent(revpropcollectit); !SCIPexpriterIsEnd(revpropcollectit) && !cutoff; expr = SCIPexpriterGetNext(revpropcollectit) ) 2805 { 2806 ownerdata = SCIPexprGetOwnerData(expr); 2807 assert(ownerdata != NULL); 2808 2809 if( ownerdata->auxvar == NULL ) 2810 continue; 2811 2812 if( ownerdata->nactivityusesprop == 0 && ownerdata->nactivityusessepa == 0 ) 2813 continue; 2814 2815 conssides = intEvalVarBoundTightening(scip, ownerdata->auxvar, (void*)conshdlrdata); 2816 SCIP_CALL( SCIPtightenExprIntervalNonlinear(scip, expr, conssides, &cutoff, &ntightenings) ); 2817 } 2818 } 2819 2820 if( cutoff ) 2821 { 2822 SCIPdebugMsg(scip, " -> cutoff after intersect with conssides of constraint <%s>\n", SCIPconsGetName(conss[i])); 2823 *result = SCIP_CUTOFF; 2824 break; 2825 } 2826 2827 assert(ntightenings >= 0); 2828 if( ntightenings > 0 ) 2829 { 2830 *nchgbds += ntightenings; 2831 *result = SCIP_REDUCEDDOM; 2832 } 2833 2834 /* mark constraint as propagated; this will be reset via the event system when we find a variable tightening */ 2835 consdata->ispropagated = TRUE; 2836 } 2837 2838 /* apply backward propagation (if cutoff is TRUE, then this call empties the queue) */ 2839 SCIP_CALL( reversePropQueue(scip, conshdlr, &cutoff, &ntightenings) ); 2840 assert(ntightenings >= 0); 2841 assert(SCIPqueueIsEmpty(conshdlrdata->reversepropqueue)); 2842 2843 if( cutoff ) 2844 { 2845 SCIPdebugMsg(scip, " -> cutoff\n"); 2846 *result = SCIP_CUTOFF; 2847 break; 2848 } 2849 2850 if( ntightenings > 0 ) 2851 { 2852 *nchgbds += ntightenings; 2853 *result = SCIP_REDUCEDDOM; 2854 } 2855 } 2856 while( ntightenings > 0 && ++roundnr < conshdlrdata->maxproprounds ); 2857 2858 if( conshdlrdata->propauxvars ) 2859 { 2860 SCIPfreeExpriter(&revpropcollectit); 2861 } 2862 2863 conshdlrdata->forceboundtightening = FALSE; 2864 2865 /* invalidate propbounds in all exprs, so noone accidentally uses them outside propagation */ 2866 ++conshdlrdata->curpropboundstag; 2867 2868 return SCIP_OKAY; 2869 } 2870 2871 /** calls the reverseprop callbacks of all nlhdlrs in all expressions in all constraints using activity as bounds 2872 * 2873 * This is meant to propagate any domain restrictions on functions onto variable bounds, if possible. 2874 * 2875 * Assumes that activities are still valid and curpropboundstag does not need to be increased. 2876 * Therefore, a good place to call this function is immediately after propConss() or after forwardPropExpr() if outside propagation. 2877 */ 2878 static 2879 SCIP_RETCODE propExprDomains( 2880 SCIP* scip, /**< SCIP data structure */ 2881 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 2882 SCIP_CONS** conss, /**< constraints to propagate */ 2883 int nconss, /**< total number of constraints */ 2884 SCIP_RESULT* result, /**< pointer to store the result */ 2885 int* nchgbds /**< buffer to add the number of changed bounds */ 2886 ) 2887 { 2888 SCIP_CONSDATA* consdata; 2889 SCIP_EXPRITER* it; 2890 SCIP_EXPR* expr; 2891 SCIP_EXPR_OWNERDATA* ownerdata; 2892 SCIP_Bool cutoff = FALSE; 2893 int ntightenings; 2894 int c; 2895 int e; 2896 2897 assert(scip != NULL); 2898 assert(conshdlr != NULL); 2899 assert(conss != NULL); 2900 assert(nconss >= 0); 2901 assert(result != NULL); 2902 assert(nchgbds != NULL); 2903 assert(*nchgbds >= 0); 2904 2905 #ifndef CR_API /* this assert may not work in unittests due to having this code compiled twice, #3543 */ 2906 assert(SCIPconshdlrGetData(conshdlr)->intevalvar == intEvalVarBoundTightening); 2907 #endif 2908 assert(!SCIPconshdlrGetData(conshdlr)->globalbounds); 2909 assert(SCIPqueueIsEmpty(SCIPconshdlrGetData(conshdlr)->reversepropqueue)); 2910 2911 *result = SCIP_DIDNOTFIND; 2912 2913 SCIP_CALL( SCIPcreateExpriter(scip, &it) ); 2914 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) ); 2915 2916 for( c = 0; c < nconss && !cutoff; ++c ) 2917 { 2918 /* skip deleted, non-active, or propagation-disabled constraints */ 2919 if( SCIPconsIsDeleted(conss[c]) || !SCIPconsIsActive(conss[c]) || !SCIPconsIsPropagationEnabled(conss[c]) ) 2920 continue; 2921 2922 consdata = SCIPconsGetData(conss[c]); 2923 assert(consdata != NULL); 2924 2925 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it) && !cutoff; expr = SCIPexpriterGetNext(it) ) 2926 { 2927 ownerdata = SCIPexprGetOwnerData(expr); 2928 assert(ownerdata != NULL); 2929 2930 /* call reverseprop for those nlhdlr that participate in this expr's activity computation 2931 * this will propagate the current activity 2932 */ 2933 for( e = 0; e < ownerdata->nenfos; ++e ) 2934 { 2935 SCIP_NLHDLR* nlhdlr; 2936 assert(ownerdata->enfos[e] != NULL); 2937 2938 nlhdlr = ownerdata->enfos[e]->nlhdlr; 2939 assert(nlhdlr != NULL); 2940 if( (ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_ACTIVITY) == 0 ) 2941 continue; 2942 2943 SCIPdebugMsg(scip, "propExprDomains calling reverseprop for expression %p [%g,%g]\n", (void*)expr, 2944 SCIPexprGetActivity(expr).inf, SCIPexprGetActivity(expr).sup); 2945 ntightenings = 0; 2946 SCIP_CALL( SCIPnlhdlrReverseprop(scip, conshdlr, nlhdlr, expr, ownerdata->enfos[e]->nlhdlrexprdata, 2947 SCIPexprGetActivity(expr), &cutoff, &ntightenings) ); 2948 2949 if( cutoff ) 2950 { 2951 /* stop everything if we detected infeasibility */ 2952 SCIPdebugMsg(scip, "detect infeasibility for constraint <%s> during reverseprop()\n", SCIPconsGetName(conss[c])); 2953 *result = SCIP_CUTOFF; 2954 break; 2955 } 2956 2957 assert(ntightenings >= 0); 2958 if( ntightenings > 0 ) 2959 { 2960 *nchgbds += ntightenings; 2961 *result = SCIP_REDUCEDDOM; 2962 } 2963 } 2964 } 2965 } 2966 2967 /* apply backward propagation (if cutoff is TRUE, then this call empties the queue) */ 2968 SCIP_CALL( reversePropQueue(scip, conshdlr, &cutoff, &ntightenings) ); 2969 assert(ntightenings >= 0); 2970 2971 if( cutoff ) 2972 { 2973 SCIPdebugMsg(scip, " -> cutoff\n"); 2974 *result = SCIP_CUTOFF; 2975 } 2976 else if( ntightenings > 0 ) 2977 { 2978 *nchgbds += ntightenings; 2979 *result = SCIP_REDUCEDDOM; 2980 } 2981 2982 SCIPfreeExpriter(&it); 2983 2984 /* invalidate propbounds in all exprs, so noone accidentally uses them outside propagation */ 2985 ++SCIPconshdlrGetData(conshdlr)->curpropboundstag; 2986 2987 return SCIP_OKAY; 2988 } 2989 2990 /** propagates variable locks through expression and adds locks to variables */ 2991 static 2992 SCIP_RETCODE propagateLocks( 2993 SCIP* scip, /**< SCIP data structure */ 2994 SCIP_EXPR* expr, /**< expression */ 2995 int nlockspos, /**< number of positive locks */ 2996 int nlocksneg /**< number of negative locks */ 2997 ) 2998 { 2999 SCIP_EXPR_OWNERDATA* ownerdata; 3000 SCIP_EXPRITER* it; 3001 SCIP_EXPRITER_USERDATA ituserdata; 3002 3003 assert(expr != NULL); 3004 3005 /* if no locks, then nothing to propagate */ 3006 if( nlockspos == 0 && nlocksneg == 0 ) 3007 return SCIP_OKAY; 3008 3009 SCIP_CALL( SCIPcreateExpriter(scip, &it) ); 3010 SCIP_CALL( SCIPexpriterInit(it, expr, SCIP_EXPRITER_DFS, TRUE) ); 3011 SCIPexpriterSetStagesDFS(it, SCIP_EXPRITER_ENTEREXPR | SCIP_EXPRITER_VISITINGCHILD | SCIP_EXPRITER_LEAVEEXPR); 3012 assert(SCIPexpriterGetCurrent(it) == expr); /* iterator should not have moved */ 3013 3014 /* store locks in root node */ 3015 ituserdata.intvals[0] = nlockspos; 3016 ituserdata.intvals[1] = nlocksneg; 3017 SCIPexpriterSetCurrentUserData(it, ituserdata); 3018 3019 while( !SCIPexpriterIsEnd(it) ) 3020 { 3021 /* collect locks */ 3022 ituserdata = SCIPexpriterGetCurrentUserData(it); 3023 nlockspos = ituserdata.intvals[0]; 3024 nlocksneg = ituserdata.intvals[1]; 3025 3026 ownerdata = SCIPexprGetOwnerData(expr); 3027 3028 switch( SCIPexpriterGetStageDFS(it) ) 3029 { 3030 case SCIP_EXPRITER_ENTEREXPR: 3031 { 3032 if( SCIPisExprVar(scip, expr) ) 3033 { 3034 /* if a variable, then also add nlocksneg/nlockspos via SCIPaddVarLocks() */ 3035 SCIP_CALL( SCIPaddVarLocks(scip, SCIPgetVarExprVar(expr), nlocksneg, nlockspos) ); 3036 } 3037 3038 /* add locks to expression */ 3039 ownerdata->nlockspos += nlockspos; 3040 ownerdata->nlocksneg += nlocksneg; 3041 3042 /* add monotonicity information if expression has been locked for the first time */ 3043 if( ownerdata->nlockspos == nlockspos && ownerdata->nlocksneg == nlocksneg && SCIPexprGetNChildren(expr) > 0 3044 && SCIPexprhdlrHasMonotonicity(SCIPexprGetHdlr(expr)) ) 3045 { 3046 int i; 3047 3048 assert(ownerdata->monotonicity == NULL); 3049 assert(ownerdata->monotonicitysize == 0); 3050 3051 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &ownerdata->monotonicity, SCIPexprGetNChildren(expr)) ); 3052 ownerdata->monotonicitysize = SCIPexprGetNChildren(expr); 3053 3054 /* store the monotonicity for each child */ 3055 for( i = 0; i < SCIPexprGetNChildren(expr); ++i ) 3056 { 3057 SCIP_CALL( SCIPcallExprMonotonicity(scip, expr, i, &ownerdata->monotonicity[i]) ); 3058 } 3059 } 3060 break; 3061 } 3062 3063 case SCIP_EXPRITER_LEAVEEXPR : 3064 { 3065 /* remove monotonicity information if expression has been unlocked */ 3066 if( ownerdata->nlockspos == 0 && ownerdata->nlocksneg == 0 && ownerdata->monotonicity != NULL ) 3067 { 3068 assert(ownerdata->monotonicitysize > 0); 3069 /* keep this assert for checking whether someone changed an expression without updating locks properly */ 3070 assert(ownerdata->monotonicitysize == SCIPexprGetNChildren(expr)); 3071 3072 SCIPfreeBlockMemoryArray(scip, &ownerdata->monotonicity, ownerdata->monotonicitysize); 3073 ownerdata->monotonicitysize = 0; 3074 } 3075 break; 3076 } 3077 3078 case SCIP_EXPRITER_VISITINGCHILD : 3079 { 3080 SCIP_MONOTONE monotonicity; 3081 3082 /* get monotonicity of child */ 3083 /* NOTE: the monotonicity stored in an expression might be different from the result obtained by 3084 * SCIPcallExprMonotonicity 3085 */ 3086 monotonicity = ownerdata->monotonicity != NULL ? ownerdata->monotonicity[SCIPexpriterGetChildIdxDFS(it)] : SCIP_MONOTONE_UNKNOWN; 3087 3088 /* compute resulting locks of the child expression */ 3089 switch( monotonicity ) 3090 { 3091 case SCIP_MONOTONE_INC: 3092 ituserdata.intvals[0] = nlockspos; 3093 ituserdata.intvals[1] = nlocksneg; 3094 break; 3095 case SCIP_MONOTONE_DEC: 3096 ituserdata.intvals[0] = nlocksneg; 3097 ituserdata.intvals[1] = nlockspos; 3098 break; 3099 case SCIP_MONOTONE_UNKNOWN: 3100 ituserdata.intvals[0] = nlockspos + nlocksneg; 3101 ituserdata.intvals[1] = nlockspos + nlocksneg; 3102 break; 3103 case SCIP_MONOTONE_CONST: 3104 ituserdata.intvals[0] = 0; 3105 ituserdata.intvals[1] = 0; 3106 break; 3107 } 3108 /* set locks in child expression */ 3109 SCIPexpriterSetChildUserData(it, ituserdata); 3110 3111 break; 3112 } 3113 3114 default : 3115 /* you should never be here */ 3116 SCIPABORT(); 3117 break; 3118 } 3119 3120 expr = SCIPexpriterGetNext(it); 3121 } 3122 3123 SCIPfreeExpriter(&it); 3124 3125 return SCIP_OKAY; 3126 } 3127 3128 /** main function for adding locks to expressions and variables 3129 * 3130 * Locks for a nonlinear constraint are used to update locks for all sub-expressions and variables. 3131 * Locks of expressions depend on the monotonicity of expressions w.r.t. their children, e.g., 3132 * consider the constraint \f$x^2 \leq 1\f$ with \f$x \in [-2,-1]\f$ implies an up-lock for the root 3133 * expression (pow) and a down-lock for its child \f$x\f$ because \f$x^2\f$ is decreasing on [-2,-1]. 3134 * Since the monotonicity (and thus the locks) might also depend on variable bounds, the function remembers 3135 * the computed monotonicity information of each expression until all locks of an expression have been removed, 3136 * which implies that updating the monotonicity information during the next locking of this expression does not 3137 * break existing locks. 3138 * 3139 * @note When modifying the structure of an expression, e.g., during simplification, it is necessary to remove all 3140 * locks from an expression and repropagating them after the structural changes have been applied. 3141 * Because of existing common sub-expressions, it might be necessary to remove the locks of all constraints 3142 * to ensure that an expression is unlocked (see canonicalizeConstraints() for an example) 3143 */ 3144 static 3145 SCIP_RETCODE addLocks( 3146 SCIP* scip, /**< SCIP data structure */ 3147 SCIP_CONS* cons, /**< nonlinear constraint */ 3148 int nlockspos, /**< number of positive rounding locks */ 3149 int nlocksneg /**< number of negative rounding locks */ 3150 ) 3151 { 3152 SCIP_CONSDATA* consdata; 3153 3154 assert(cons != NULL); 3155 3156 if( nlockspos == 0 && nlocksneg == 0 ) 3157 return SCIP_OKAY; 3158 3159 consdata = SCIPconsGetData(cons); 3160 assert(consdata != NULL); 3161 3162 /* no constraint sides -> nothing to lock */ 3163 if( SCIPisInfinity(scip, consdata->rhs) && SCIPisInfinity(scip, -consdata->lhs) ) 3164 return SCIP_OKAY; 3165 3166 /* remember locks */ 3167 consdata->nlockspos += nlockspos; 3168 consdata->nlocksneg += nlocksneg; 3169 3170 assert(consdata->nlockspos >= 0); 3171 assert(consdata->nlocksneg >= 0); 3172 3173 /* compute locks for lock propagation */ 3174 if( !SCIPisInfinity(scip, consdata->rhs) && !SCIPisInfinity(scip, -consdata->lhs) ) 3175 { 3176 SCIP_CALL( propagateLocks(scip, consdata->expr, nlockspos + nlocksneg, nlockspos + nlocksneg)); 3177 } 3178 else if( !SCIPisInfinity(scip, consdata->rhs) ) 3179 { 3180 SCIP_CALL( propagateLocks(scip, consdata->expr, nlockspos, nlocksneg)); 3181 } 3182 else 3183 { 3184 assert(!SCIPisInfinity(scip, -consdata->lhs)); 3185 SCIP_CALL( propagateLocks(scip, consdata->expr, nlocksneg, nlockspos)); 3186 } 3187 3188 return SCIP_OKAY; 3189 } 3190 3191 /** create a nonlinear row representation of a nonlinear constraint and stores them in consdata */ 3192 static 3193 SCIP_RETCODE createNlRow( 3194 SCIP* scip, /**< SCIP data structure */ 3195 SCIP_CONS* cons /**< nonlinear constraint */ 3196 ) 3197 { 3198 SCIP_CONSDATA* consdata; 3199 3200 assert(scip != NULL); 3201 assert(cons != NULL); 3202 3203 consdata = SCIPconsGetData(cons); 3204 assert(consdata != NULL); 3205 assert(consdata->expr != NULL); 3206 3207 if( consdata->nlrow != NULL ) 3208 { 3209 SCIP_CALL( SCIPreleaseNlRow(scip, &consdata->nlrow) ); 3210 } 3211 3212 /* better curvature info will be set in initSolve() just before nlrow is added to NLP */ 3213 SCIP_CALL( SCIPcreateNlRow(scip, &consdata->nlrow, SCIPconsGetName(cons), 0.0, 3214 0, NULL, NULL, NULL, consdata->lhs, consdata->rhs, SCIP_EXPRCURV_UNKNOWN) ); 3215 3216 if( SCIPisExprSum(scip, consdata->expr) ) 3217 { 3218 /* if root is a sum, then split into linear and nonlinear terms */ 3219 SCIP_EXPR* nonlinpart; 3220 SCIP_EXPR* child; 3221 SCIP_Real* coefs; 3222 int i; 3223 3224 coefs = SCIPgetCoefsExprSum(consdata->expr); 3225 3226 /* constant term of sum */ 3227 SCIP_CALL( SCIPchgNlRowConstant(scip, consdata->nlrow, SCIPgetConstantExprSum(consdata->expr)) ); 3228 3229 /* a sum-expression that will hold the nonlinear terms and be passed to the nlrow eventually */ 3230 SCIP_CALL( SCIPcreateExprSum(scip, &nonlinpart, 0, NULL, NULL, 0.0, exprownerCreate, (void*)SCIPconsGetHdlr(cons)) ); 3231 3232 for( i = 0; i < SCIPexprGetNChildren(consdata->expr); ++i ) 3233 { 3234 child = SCIPexprGetChildren(consdata->expr)[i]; 3235 if( SCIPisExprVar(scip, child) ) 3236 { 3237 /* linear term */ 3238 SCIP_CALL( SCIPaddLinearCoefToNlRow(scip, consdata->nlrow, SCIPgetVarExprVar(child), coefs[i]) ); 3239 } 3240 else 3241 { 3242 /* nonlinear term */ 3243 SCIP_CALL( SCIPappendExprSumExpr(scip, nonlinpart, child, coefs[i]) ); 3244 } 3245 } 3246 3247 if( SCIPexprGetNChildren(nonlinpart) > 0 ) 3248 { 3249 /* add expression to nlrow (this will make a copy) */ 3250 SCIP_CALL( SCIPsetNlRowExpr(scip, consdata->nlrow, nonlinpart) ); 3251 } 3252 SCIP_CALL( SCIPreleaseExpr(scip, &nonlinpart) ); 3253 } 3254 else 3255 { 3256 SCIP_CALL( SCIPsetNlRowExpr(scip, consdata->nlrow, consdata->expr) ); 3257 } 3258 3259 return SCIP_OKAY; 3260 } 3261 3262 /** compares enfodata by enforcement priority of nonlinear handler 3263 * 3264 * If handlers have same enforcement priority, then compare by detection priority, then by name. 3265 */ 3266 static 3267 SCIP_DECL_SORTPTRCOMP(enfodataCmp) 3268 { 3269 SCIP_NLHDLR* h1; 3270 SCIP_NLHDLR* h2; 3271 3272 assert(elem1 != NULL); 3273 assert(elem2 != NULL); 3274 3275 h1 = ((EXPRENFO*)elem1)->nlhdlr; 3276 h2 = ((EXPRENFO*)elem2)->nlhdlr; 3277 3278 assert(h1 != NULL); 3279 assert(h2 != NULL); 3280 3281 if( SCIPnlhdlrGetEnfoPriority(h1) != SCIPnlhdlrGetEnfoPriority(h2) ) 3282 return SCIPnlhdlrGetEnfoPriority(h1) - SCIPnlhdlrGetEnfoPriority(h2); 3283 3284 if( SCIPnlhdlrGetDetectPriority(h1) != SCIPnlhdlrGetDetectPriority(h2) ) 3285 return SCIPnlhdlrGetDetectPriority(h1) - SCIPnlhdlrGetDetectPriority(h2); 3286 3287 return strcmp(SCIPnlhdlrGetName(h1), SCIPnlhdlrGetName(h2)); 3288 } 3289 3290 /** install nlhdlrs in one expression */ 3291 static 3292 SCIP_RETCODE detectNlhdlr( 3293 SCIP* scip, /**< SCIP data structure */ 3294 SCIP_EXPR* expr, /**< expression for which to run detection routines */ 3295 SCIP_CONS* cons /**< constraint for which expr == consdata->expr, otherwise NULL */ 3296 ) 3297 { 3298 SCIP_EXPR_OWNERDATA* ownerdata; 3299 SCIP_CONSHDLRDATA* conshdlrdata; 3300 SCIP_NLHDLR_METHOD enforcemethodsallowed; 3301 SCIP_NLHDLR_METHOD enforcemethods; 3302 SCIP_NLHDLR_METHOD enforcemethodsnew; 3303 SCIP_NLHDLR_METHOD nlhdlrenforcemethods; 3304 SCIP_NLHDLR_METHOD nlhdlrparticipating; 3305 SCIP_NLHDLREXPRDATA* nlhdlrexprdata; 3306 int enfossize; /* allocated length of expr->enfos array */ 3307 int h; 3308 3309 assert(expr != NULL); 3310 3311 ownerdata = SCIPexprGetOwnerData(expr); 3312 assert(ownerdata != NULL); 3313 3314 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr); 3315 assert(conshdlrdata != NULL); 3316 assert(conshdlrdata->auxvarid >= 0); 3317 assert(!conshdlrdata->indetect); 3318 3319 /* there should be no enforcer yet and detection should not even have considered expr yet */ 3320 assert(ownerdata->nenfos < 0); 3321 assert(ownerdata->enfos == NULL); 3322 3323 /* check which enforcement methods are required by setting flags in enforcemethods for those that are NOT required 3324 * - if no auxiliary variable is used, then do not need sepabelow or sepaabove 3325 * - if auxiliary variable is used, but nobody positively (up) locks expr -> only need to enforce expr >= auxvar -> no need for underestimation 3326 * - if auxiliary variable is used, but nobody negatively (down) locks expr -> only need to enforce expr <= auxvar -> no need for overestimation 3327 * - if no one uses activity, then do not need activity methods 3328 */ 3329 enforcemethods = SCIP_NLHDLR_METHOD_NONE; 3330 if( ownerdata->nauxvaruses == 0 ) 3331 enforcemethods |= SCIP_NLHDLR_METHOD_SEPABOTH; 3332 else 3333 { 3334 if( ownerdata->nlockspos == 0 ) /* no need for underestimation */ 3335 enforcemethods |= SCIP_NLHDLR_METHOD_SEPABELOW; 3336 if( ownerdata->nlocksneg == 0 ) /* no need for overestimation */ 3337 enforcemethods |= SCIP_NLHDLR_METHOD_SEPAABOVE; 3338 } 3339 if( ownerdata->nactivityusesprop == 0 && ownerdata->nactivityusessepa == 0 ) 3340 enforcemethods |= SCIP_NLHDLR_METHOD_ACTIVITY; 3341 3342 /* it doesn't make sense to have been called on detectNlhdlr, if the expr isn't used for anything */ 3343 assert(enforcemethods != SCIP_NLHDLR_METHOD_ALL); 3344 3345 /* all methods that have not been flagged above are the ones that we want to be handled by nlhdlrs */ 3346 enforcemethodsallowed = ~enforcemethods & SCIP_NLHDLR_METHOD_ALL; 3347 3348 ownerdata->nenfos = 0; 3349 enfossize = 2; 3350 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &ownerdata->enfos, enfossize) ); 3351 conshdlrdata->indetect = TRUE; 3352 3353 SCIPdebugMsg(scip, "detecting nlhdlrs for %s expression %p (%s); requiring%s%s%s\n", 3354 cons != NULL ? "root" : "non-root", (void*)expr, SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)), 3355 (enforcemethods & SCIP_NLHDLR_METHOD_SEPABELOW) != 0 ? "" : " sepabelow", 3356 (enforcemethods & SCIP_NLHDLR_METHOD_SEPAABOVE) != 0 ? "" : " sepaabove", 3357 (enforcemethods & SCIP_NLHDLR_METHOD_ACTIVITY) != 0 ? "" : " activity"); 3358 3359 for( h = 0; h < conshdlrdata->nnlhdlrs; ++h ) 3360 { 3361 SCIP_NLHDLR* nlhdlr; 3362 3363 nlhdlr = conshdlrdata->nlhdlrs[h]; 3364 assert(nlhdlr != NULL); 3365 3366 /* skip disabled nlhdlrs */ 3367 if( !SCIPnlhdlrIsEnabled(nlhdlr) ) 3368 continue; 3369 3370 /* call detect routine of nlhdlr */ 3371 nlhdlrexprdata = NULL; 3372 enforcemethodsnew = enforcemethods; 3373 nlhdlrparticipating = SCIP_NLHDLR_METHOD_NONE; 3374 conshdlrdata->registerusesactivitysepabelow = FALSE; /* SCIPregisterExprUsageNonlinear() as called by detect may set this to TRUE */ 3375 conshdlrdata->registerusesactivitysepaabove = FALSE; /* SCIPregisterExprUsageNonlinear() as called by detect may set this to TRUE */ 3376 /* coverity[forward_null] */ 3377 SCIP_CALL( SCIPnlhdlrDetect(scip, ownerdata->conshdlr, nlhdlr, expr, cons, &enforcemethodsnew, &nlhdlrparticipating, &nlhdlrexprdata) ); 3378 3379 /* nlhdlr might have claimed more than needed: clean up sepa flags */ 3380 nlhdlrparticipating &= enforcemethodsallowed; 3381 3382 /* detection is only allowed to augment to nlhdlrenforcemethods, so previous enforcemethods must still be set */ 3383 assert((enforcemethodsnew & enforcemethods) == enforcemethods); 3384 3385 /* Because of the previous assert, nlhdlrenforcenew ^ enforcemethods are the methods enforced by this nlhdlr. 3386 * They are also cleaned up here to ensure that only the needed methods are claimed. 3387 */ 3388 nlhdlrenforcemethods = (enforcemethodsnew ^ enforcemethods) & enforcemethodsallowed; 3389 3390 /* nlhdlr needs to participate for the methods it is enforcing */ 3391 assert((nlhdlrparticipating & nlhdlrenforcemethods) == nlhdlrenforcemethods); 3392 3393 if( nlhdlrparticipating == SCIP_NLHDLR_METHOD_NONE ) 3394 { 3395 /* nlhdlr might not have detected anything, or all set flags might have been removed by 3396 * clean up; in the latter case, we may need to free nlhdlrexprdata */ 3397 3398 /* free nlhdlr exprdata, if there is any and there is a method to free this data */ 3399 if( nlhdlrexprdata != NULL ) 3400 { 3401 SCIP_CALL( SCIPnlhdlrFreeexprdata(scip, nlhdlr, expr, &nlhdlrexprdata) ); 3402 } 3403 /* nlhdlr cannot have added an enforcement method if it doesn't participate (actually redundant due to previous asserts) */ 3404 assert(nlhdlrenforcemethods == SCIP_NLHDLR_METHOD_NONE); 3405 3406 SCIPdebugMsg(scip, "nlhdlr <%s> detect unsuccessful\n", SCIPnlhdlrGetName(nlhdlr)); 3407 3408 continue; 3409 } 3410 3411 SCIPdebugMsg(scip, "nlhdlr <%s> detect successful; sepabelow: %s, sepaabove: %s, activity: %s\n", 3412 SCIPnlhdlrGetName(nlhdlr), 3413 ((nlhdlrenforcemethods & SCIP_NLHDLR_METHOD_SEPABELOW) != 0) ? "enforcing" : ((nlhdlrparticipating & SCIP_NLHDLR_METHOD_SEPABELOW) != 0) ? "participating" : "no", 3414 ((nlhdlrenforcemethods & SCIP_NLHDLR_METHOD_SEPAABOVE) != 0) ? "enforcing" : ((nlhdlrparticipating & SCIP_NLHDLR_METHOD_SEPAABOVE) != 0) ? "participating" : "no", 3415 ((nlhdlrenforcemethods & SCIP_NLHDLR_METHOD_ACTIVITY) != 0) ? "enforcing" : ((nlhdlrparticipating & SCIP_NLHDLR_METHOD_ACTIVITY) != 0) ? "participating" : "no"); 3416 3417 /* store nlhdlr and its data */ 3418 SCIP_CALL( SCIPensureBlockMemoryArray(scip, &ownerdata->enfos, &enfossize, ownerdata->nenfos+1) ); 3419 SCIP_CALL( SCIPallocBlockMemory(scip, &ownerdata->enfos[ownerdata->nenfos]) ); 3420 ownerdata->enfos[ownerdata->nenfos]->nlhdlr = nlhdlr; 3421 ownerdata->enfos[ownerdata->nenfos]->nlhdlrexprdata = nlhdlrexprdata; 3422 ownerdata->enfos[ownerdata->nenfos]->nlhdlrparticipation = nlhdlrparticipating; 3423 ownerdata->enfos[ownerdata->nenfos]->issepainit = FALSE; 3424 ownerdata->enfos[ownerdata->nenfos]->sepabelowusesactivity = conshdlrdata->registerusesactivitysepabelow; 3425 ownerdata->enfos[ownerdata->nenfos]->sepaaboveusesactivity = conshdlrdata->registerusesactivitysepaabove; 3426 ownerdata->nenfos++; 3427 3428 /* update enforcement flags */ 3429 enforcemethods = enforcemethodsnew; 3430 } 3431 3432 conshdlrdata->indetect = FALSE; 3433 3434 /* stop if an enforcement method is missing but we are already in solving stage 3435 * (as long as the expression provides its callbacks, the default nlhdlr should have provided all enforcement methods) 3436 */ 3437 if( enforcemethods != SCIP_NLHDLR_METHOD_ALL && SCIPgetStage(scip) == SCIP_STAGE_SOLVING ) 3438 { 3439 SCIPerrorMessage("no nonlinear handler provided some of the required enforcement methods\n"); 3440 return SCIP_ERROR; 3441 } 3442 3443 assert(ownerdata->nenfos > 0); 3444 3445 /* sort nonlinear handlers by enforcement priority, in decreasing order */ 3446 if( ownerdata->nenfos > 1 ) 3447 SCIPsortDownPtr((void**)ownerdata->enfos, enfodataCmp, ownerdata->nenfos); 3448 3449 /* resize enfos array to be nenfos long */ 3450 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &ownerdata->enfos, enfossize, ownerdata->nenfos) ); 3451 3452 return SCIP_OKAY; 3453 } 3454 3455 /** detect nlhdlrs that can handle the expressions */ 3456 static 3457 SCIP_RETCODE detectNlhdlrs( 3458 SCIP* scip, /**< SCIP data structure */ 3459 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 3460 SCIP_CONS** conss, /**< constraints for which to run nlhdlr detect */ 3461 int nconss /**< total number of constraints */ 3462 ) 3463 { 3464 SCIP_CONSHDLRDATA* conshdlrdata; 3465 SCIP_CONSDATA* consdata; 3466 SCIP_EXPR* expr; 3467 SCIP_EXPR_OWNERDATA* ownerdata; 3468 SCIP_EXPRITER* it; 3469 int i; 3470 3471 assert(conss != NULL || nconss == 0); 3472 assert(nconss >= 0); 3473 assert(SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING || SCIPgetStage(scip) == SCIP_STAGE_INITSOLVE || SCIPgetStage(scip) == SCIP_STAGE_SOLVING); /* should only be called in presolve or initsolve or consactive */ 3474 3475 conshdlrdata = SCIPconshdlrGetData(conshdlr); 3476 assert(conshdlrdata != NULL); 3477 3478 SCIP_CALL( SCIPcreateExpriter(scip, &it) ); 3479 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, TRUE) ); 3480 3481 if( SCIPgetStage(scip) == SCIP_STAGE_SOLVING && SCIPgetDepth(scip) != 0 ) 3482 { 3483 /* ensure that activities are recomputed w.r.t. the global variable bounds if CONSACTIVE is called in a local node; 3484 * for example, this happens if globally valid nonlinear constraints are added during the tree search 3485 */ 3486 SCIPincrementCurBoundsTagNonlinear(conshdlr, TRUE); 3487 conshdlrdata->globalbounds = TRUE; 3488 conshdlrdata->lastvaractivitymethodchange = conshdlrdata->curboundstag; 3489 } 3490 3491 for( i = 0; i < nconss; ++i ) 3492 { 3493 assert(conss != NULL && conss[i] != NULL); 3494 3495 consdata = SCIPconsGetData(conss[i]); 3496 assert(consdata != NULL); 3497 assert(consdata->expr != NULL); 3498 3499 /* if a constraint is separated, we currently need it to be initial, too 3500 * this is because INITLP will create the auxiliary variables that are used for any separation 3501 * TODO we may relax this with a little more programming effort when required, see also TODO in INITLP 3502 */ 3503 assert((!SCIPconsIsSeparated(conss[i]) && !SCIPconsIsEnforced(conss[i])) || SCIPconsIsInitial(conss[i])); 3504 3505 ownerdata = SCIPexprGetOwnerData(consdata->expr); 3506 assert(ownerdata != NULL); 3507 3508 /* because of common sub-expressions it might happen that we already detected a nonlinear handler and added it to the expr 3509 * then we would normally skip to run DETECT again 3510 * HOWEVER: most likely we have been running DETECT with cons == NULL, which may interest less nlhdlrs 3511 * thus, if expr is the root expression, we rerun DETECT 3512 */ 3513 if( ownerdata->nenfos > 0 ) 3514 { 3515 SCIP_CALL( freeEnfoData(scip, consdata->expr, FALSE) ); 3516 assert(ownerdata->nenfos < 0); 3517 } 3518 3519 /* if constraint will be enforced, and we are in solve, then ensure auxiliary variable for root expression 3520 * this way we can treat the root expression like any other expression when enforcing via separation 3521 * if constraint will be propagated, then register activity usage of root expression 3522 * this can trigger a call to forwardPropExpr, for which we better have the indetect flag set 3523 */ 3524 conshdlrdata->indetect = TRUE; 3525 SCIP_CALL( SCIPregisterExprUsageNonlinear(scip, consdata->expr, 3526 SCIPgetStage(scip) >= SCIP_STAGE_INITSOLVE && (SCIPconsIsSeparated(conss[i]) || SCIPconsIsEnforced(conss[i])), 3527 SCIPconsIsPropagated(conss[i]), 3528 FALSE, FALSE) ); 3529 conshdlrdata->indetect = FALSE; 3530 3531 /* compute integrality information for all subexpressions */ 3532 SCIP_CALL( SCIPcomputeExprIntegrality(scip, consdata->expr) ); 3533 3534 /* run detectNlhdlr on all expr where required */ 3535 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) ) 3536 { 3537 ownerdata = SCIPexprGetOwnerData(expr); 3538 assert(ownerdata != NULL); 3539 3540 /* skip exprs that we already looked at */ 3541 if( ownerdata->nenfos >= 0 ) 3542 continue; 3543 3544 /* if there is use of the auxvar, then someone requires that 3545 * auxvar == expr (or auxvar >= expr or auxvar <= expr) or we are at the root expression (expr==consdata->expr) 3546 * thus, we need to find nlhdlrs that separate or estimate 3547 * if there is use of the activity, then there is someone requiring that 3548 * activity of this expression is updated; this someone would also benefit from better bounds on the activity of this expression 3549 * thus, we need to find nlhdlrs that do interval-evaluation 3550 */ 3551 if( ownerdata->nauxvaruses > 0 || ownerdata->nactivityusesprop > 0 || ownerdata->nactivityusessepa > 0 ) 3552 { 3553 SCIP_CALL( detectNlhdlr(scip, expr, expr == consdata->expr ? conss[i] : NULL) ); 3554 3555 assert(ownerdata->nenfos >= 0); 3556 } 3557 else 3558 { 3559 /* remember that we looked at this expression during detectNlhdlrs 3560 * even though we have not actually run detectNlhdlr, because no nlhdlr showed interest in this expr, 3561 * in some situations (forwardPropExpr, to be specific) we will have to distinguish between exprs for which 3562 * we have not initialized enforcement yet (nenfos < 0) and expressions which are just not used in enforcement (nenfos == 0) 3563 */ 3564 ownerdata->nenfos = 0; 3565 } 3566 } 3567 3568 /* include this constraint into the next propagation round because the added nlhdlr may do find tighter bounds now */ 3569 if( SCIPconsIsPropagated(conss[i]) ) 3570 consdata->ispropagated = FALSE; 3571 } 3572 3573 if( SCIPgetStage(scip) == SCIP_STAGE_SOLVING && SCIPgetDepth(scip) != 0 ) 3574 { 3575 /* ensure that the local bounds are used again when reevaluating the expressions later; 3576 * this is only needed if CONSACTIVE is called in a local node (see begin of this function) 3577 */ 3578 SCIPincrementCurBoundsTagNonlinear(conshdlr, FALSE); 3579 conshdlrdata->globalbounds = FALSE; 3580 conshdlrdata->lastvaractivitymethodchange = conshdlrdata->curboundstag; 3581 } 3582 else 3583 { 3584 /* ensure that all activities (except for var-exprs) are reevaluated since better methods may be available now */ 3585 SCIPincrementCurBoundsTagNonlinear(conshdlr, FALSE); 3586 } 3587 3588 SCIPfreeExpriter(&it); 3589 3590 return SCIP_OKAY; 3591 } 3592 3593 /** initializes (pre)solving data of constraints 3594 * 3595 * This initializes data in a constraint that is used for separation, propagation, etc, and assumes that expressions will 3596 * not be modified. 3597 * In particular, this function 3598 * - runs the detection method of nlhldrs 3599 * - looks for unlocked linear variables 3600 * - checks curvature (if not in presolve) 3601 * - creates and add row to NLP (if not in presolve) 3602 * 3603 * This function can be called in presolve and solve and can be called several times with different sets of constraints, 3604 * e.g., it should be called in INITSOL and for constraints that are added during solve. 3605 */ 3606 static 3607 SCIP_RETCODE initSolve( 3608 SCIP* scip, /**< SCIP data structure */ 3609 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 3610 SCIP_CONS** conss, /**< constraints */ 3611 int nconss /**< number of constraints */ 3612 ) 3613 { 3614 int c; 3615 3616 for( c = 0; c < nconss; ++c ) 3617 { 3618 /* check for a linear variable that can be increase or decreased without harming feasibility */ 3619 findUnlockedLinearVar(scip, conss[c]); 3620 3621 if( SCIPgetStage(scip) == SCIP_STAGE_INITSOLVE || SCIPgetStage(scip) == SCIP_STAGE_SOLVING ) 3622 { 3623 SCIP_CONSDATA* consdata; 3624 SCIP_Bool success = FALSE; 3625 3626 consdata = SCIPconsGetData(conss[c]); /*lint !e613*/ 3627 assert(consdata != NULL); 3628 assert(consdata->expr != NULL); 3629 3630 if( !SCIPconshdlrGetData(conshdlr)->assumeconvex ) 3631 { 3632 /* call the curvature detection algorithm of the convex nonlinear handler 3633 * Check only for those curvature that may result in a convex inequality, i.e., 3634 * whether f(x) is concave when f(x) >= lhs and/or f(x) is convex when f(x) <= rhs. 3635 * Also we can assume that we are nonlinear, so do not check for convex if already concave. 3636 */ 3637 if( !SCIPisInfinity(scip, -consdata->lhs) ) 3638 { 3639 SCIP_CALL( SCIPhasExprCurvature(scip, consdata->expr, SCIP_EXPRCURV_CONCAVE, &success, NULL) ); 3640 if( success ) 3641 consdata->curv = SCIP_EXPRCURV_CONCAVE; 3642 } 3643 if( !success && !SCIPisInfinity(scip, consdata->rhs) ) 3644 { 3645 SCIP_CALL( SCIPhasExprCurvature(scip, consdata->expr, SCIP_EXPRCURV_CONVEX, &success, NULL) ); 3646 if( success ) 3647 consdata->curv = SCIP_EXPRCURV_CONVEX; 3648 } 3649 } 3650 else 3651 { 3652 if( !SCIPisInfinity(scip, -consdata->lhs) && !SCIPisInfinity(scip, consdata->rhs) ) 3653 { 3654 SCIPwarningMessage(scip, "Nonlinear constraint <%s> has finite left- and right-hand side, but constraints/nonlinear/assumeconvex is enabled.\n", SCIPconsGetName(conss[c])); 3655 consdata->curv = SCIP_EXPRCURV_LINEAR; 3656 } 3657 else 3658 { 3659 consdata->curv = !SCIPisInfinity(scip, consdata->rhs) ? SCIP_EXPRCURV_CONVEX : SCIP_EXPRCURV_CONCAVE; 3660 } 3661 } 3662 SCIPdebugMsg(scip, "root curvature of constraint %s = %d\n", SCIPconsGetName(conss[c]), consdata->curv); 3663 3664 /* add nlrow representation to NLP, if NLP had been constructed */ 3665 if( SCIPisNLPConstructed(scip) && SCIPconsIsActive(conss[c]) ) 3666 { 3667 if( consdata->nlrow == NULL ) 3668 { 3669 SCIP_CALL( createNlRow(scip, conss[c]) ); 3670 assert(consdata->nlrow != NULL); 3671 } 3672 SCIPsetNlRowCurvature(scip, consdata->nlrow, consdata->curv); 3673 SCIP_CALL( SCIPaddNlRow(scip, consdata->nlrow) ); 3674 } 3675 } 3676 } 3677 3678 /* register non linear handlers */ 3679 SCIP_CALL( detectNlhdlrs(scip, conshdlr, conss, nconss) ); 3680 3681 return SCIP_OKAY; 3682 } 3683 3684 /** deinitializes (pre)solving data of constraints 3685 * 3686 * This removes the initialization data created in initSolve(). 3687 * 3688 * This function can be called in presolve and solve. 3689 * 3690 * TODO At the moment, it should not be called for a constraint if there are other constraints 3691 * that use the same expressions but still require their nlhdlr. 3692 * We should probably only decrement the auxvar and activity usage for the root expr and then 3693 * proceed as in detectNlhdlrs(), i.e., free enfo data only where none is used. 3694 */ 3695 static 3696 SCIP_RETCODE deinitSolve( 3697 SCIP* scip, /**< SCIP data structure */ 3698 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 3699 SCIP_CONS** conss, /**< constraints */ 3700 int nconss /**< number of constraints */ 3701 ) 3702 { 3703 SCIP_EXPRITER* it; 3704 SCIP_EXPR* expr; 3705 SCIP_CONSDATA* consdata; 3706 SCIP_Bool rootactivityvalid; 3707 int c; 3708 3709 SCIP_CALL( SCIPcreateExpriter(scip, &it) ); 3710 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) ); 3711 SCIPexpriterSetStagesDFS(it, SCIP_EXPRITER_LEAVEEXPR); 3712 3713 /* call deinitialization callbacks of expression and nonlinear handlers 3714 * free nonlinear handlers information from expressions 3715 * remove auxiliary variables and nactivityuses counts from expressions 3716 */ 3717 for( c = 0; c < nconss; ++c ) 3718 { 3719 assert(conss != NULL); 3720 assert(conss[c] != NULL); 3721 3722 consdata = SCIPconsGetData(conss[c]); 3723 assert(consdata != NULL); 3724 assert(consdata->expr != NULL); 3725 3726 /* check and remember whether activity in root is valid */ 3727 rootactivityvalid = SCIPexprGetActivityTag(consdata->expr) >= SCIPconshdlrGetData(conshdlr)->lastboundrelax; 3728 3729 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) ) 3730 { 3731 SCIPdebugMsg(scip, "exitsepa and free nonlinear handler data for expression %p\n", (void*)expr); 3732 3733 /* remove nonlinear handlers in expression and their data and auxiliary variables; reset activityusage count */ 3734 SCIP_CALL( freeEnfoData(scip, expr, TRUE) ); 3735 3736 /* remove quadratic info */ 3737 SCIPfreeExprQuadratic(scip, expr); 3738 3739 if( rootactivityvalid ) 3740 { 3741 /* ensure activity is valid if consdata->expr activity is valid 3742 * this is mainly to ensure that we do not leave invalid activities in parts of the expression tree where activity was not used, 3743 * e.g., an expr's activity was kept up to date by a nlhdlr, but without using some childs activity 3744 * so this childs activity would be invalid, which can generate confusion 3745 */ 3746 SCIP_CALL( SCIPevalExprActivity(scip, expr) ); 3747 } 3748 } 3749 3750 if( consdata->nlrow != NULL ) 3751 { 3752 /* remove row from NLP, if still in solving 3753 * if we are in exitsolve, the whole NLP will be freed anyway 3754 */ 3755 if( SCIPgetStage(scip) == SCIP_STAGE_SOLVING ) 3756 { 3757 SCIP_CALL( SCIPdelNlRow(scip, consdata->nlrow) ); 3758 } 3759 3760 SCIP_CALL( SCIPreleaseNlRow(scip, &consdata->nlrow) ); 3761 } 3762 3763 /* forget about linear variables that can be increased or decreased without harming feasibility */ 3764 consdata->linvardecr = NULL; 3765 consdata->linvarincr = NULL; 3766 3767 /* forget about curvature */ 3768 consdata->curv = SCIP_EXPRCURV_UNKNOWN; 3769 } 3770 3771 SCIPfreeExpriter(&it); 3772 3773 return SCIP_OKAY; 3774 } 3775 3776 /** helper method to decide whether a given expression is product of at least two binary variables */ 3777 static 3778 SCIP_Bool isBinaryProduct( 3779 SCIP* scip, /**< SCIP data structure */ 3780 SCIP_EXPR* expr /**< expression */ 3781 ) 3782 { 3783 int i; 3784 3785 assert(expr != NULL); 3786 3787 /* check whether the expression is a product */ 3788 if( !SCIPisExprProduct(scip, expr) ) 3789 return FALSE; 3790 3791 /* don't consider products with a coefficient != 1 and products with a single child 3792 * simplification will take care of this expression later 3793 */ 3794 if( SCIPexprGetNChildren(expr) <= 1 || SCIPgetCoefExprProduct(expr) != 1.0 ) 3795 return FALSE; 3796 3797 for( i = 0; i < SCIPexprGetNChildren(expr); ++i ) 3798 { 3799 SCIP_EXPR* child; 3800 SCIP_VAR* var; 3801 SCIP_Real ub; 3802 SCIP_Real lb; 3803 3804 child = SCIPexprGetChildren(expr)[i]; 3805 assert(child != NULL); 3806 3807 if( !SCIPisExprVar(scip, child) ) 3808 return FALSE; 3809 3810 var = SCIPgetVarExprVar(child); 3811 lb = SCIPvarGetLbLocal(var); 3812 ub = SCIPvarGetUbLocal(var); 3813 3814 /* check whether variable is integer and has [0,1] as variable bounds */ 3815 if( !SCIPvarIsIntegral(var) || !SCIPisEQ(scip, lb, 0.0) || !SCIPisEQ(scip, ub, 1.0) ) 3816 return FALSE; 3817 } 3818 3819 return TRUE; 3820 } 3821 3822 /** helper method to collect all bilinear binary product terms */ 3823 static 3824 SCIP_RETCODE getBilinearBinaryTerms( 3825 SCIP* scip, /**< SCIP data structure */ 3826 SCIP_EXPR* sumexpr, /**< sum expression */ 3827 SCIP_VAR** xs, /**< array to collect first variable of each bilinear binary product */ 3828 SCIP_VAR** ys, /**< array to collect second variable of each bilinear binary product */ 3829 int* childidxs, /**< array to store the index of the child of each stored bilinear binary product */ 3830 int* nterms /**< pointer to store the total number of bilinear binary terms */ 3831 ) 3832 { 3833 int i; 3834 3835 assert(sumexpr != NULL); 3836 assert(SCIPisExprSum(scip, sumexpr)); 3837 assert(xs != NULL); 3838 assert(ys != NULL); 3839 assert(childidxs != NULL); 3840 assert(nterms != NULL); 3841 3842 *nterms = 0; 3843 3844 for( i = 0; i < SCIPexprGetNChildren(sumexpr); ++i ) 3845 { 3846 SCIP_EXPR* child; 3847 3848 child = SCIPexprGetChildren(sumexpr)[i]; 3849 assert(child != NULL); 3850 3851 if( SCIPexprGetNChildren(child) == 2 && isBinaryProduct(scip, child) ) 3852 { 3853 SCIP_VAR* x = SCIPgetVarExprVar(SCIPexprGetChildren(child)[0]); 3854 SCIP_VAR* y = SCIPgetVarExprVar(SCIPexprGetChildren(child)[1]); 3855 3856 assert(x != NULL); 3857 assert(y != NULL); 3858 3859 if( x != y ) 3860 { 3861 xs[*nterms] = x; 3862 ys[*nterms] = y; 3863 childidxs[*nterms] = i; 3864 ++(*nterms); 3865 } 3866 } 3867 } 3868 3869 return SCIP_OKAY; 3870 } 3871 3872 /** helper method to reformulate \f$x_i \sum_j c_{ij} x_j\f$ */ 3873 static 3874 SCIP_RETCODE reformulateFactorizedBinaryQuadratic( 3875 SCIP* scip, /**< SCIP data structure */ 3876 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 3877 SCIP_CONS* cons, /**< constraint */ 3878 SCIP_VAR* facvar, /**< variable that has been factorized */ 3879 SCIP_VAR** vars, /**< variables of sum_j c_ij x_j */ 3880 SCIP_Real* coefs, /**< coefficients of sum_j c_ij x_j */ 3881 int nvars, /**< total number of variables in sum_j c_ij x_j */ 3882 SCIP_EXPR** newexpr, /**< pointer to store the new expression */ 3883 int* naddconss /**< pointer to update the total number of added constraints (might be NULL) */ 3884 ) 3885 { 3886 SCIP_VAR* auxvar; 3887 SCIP_CONS* newcons; 3888 SCIP_Real minact = 0.0; 3889 SCIP_Real maxact = 0.0; 3890 SCIP_Bool integral = TRUE; 3891 char name [SCIP_MAXSTRLEN]; 3892 int i; 3893 3894 assert(facvar != NULL); 3895 assert(vars != NULL); 3896 assert(nvars > 1); 3897 assert(newexpr != NULL); 3898 3899 /* compute minimum and maximum activity of sum_j c_ij x_j */ 3900 /* TODO could compute minact and maxact for facvar=0 and facvar=1 separately, taking implied bounds into account, allowing for possibly tighter big-M's below */ 3901 for( i = 0; i < nvars; ++i ) 3902 { 3903 minact += MIN(coefs[i], 0.0); 3904 maxact += MAX(coefs[i], 0.0); 3905 integral = integral && SCIPisIntegral(scip, coefs[i]); 3906 } 3907 assert(minact <= maxact); 3908 3909 /* create and add auxiliary variable */ 3910 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s", SCIPconsGetName(cons), SCIPvarGetName(facvar)); 3911 SCIP_CALL( SCIPcreateVarBasic(scip, &auxvar, name, minact, maxact, 0.0, integral ? SCIP_VARTYPE_IMPLINT : SCIP_VARTYPE_CONTINUOUS) ); 3912 SCIP_CALL( SCIPaddVar(scip, auxvar) ); 3913 3914 /* create and add z - maxact x <= 0 */ 3915 if( !SCIPisZero(scip, maxact) ) 3916 { 3917 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s_1", SCIPconsGetName(cons), SCIPvarGetName(facvar)); 3918 SCIP_CALL( SCIPcreateConsBasicVarbound(scip, &newcons, name, auxvar, facvar, -maxact, -SCIPinfinity(scip), 0.0) ); 3919 SCIP_CALL( SCIPaddCons(scip, newcons) ); 3920 SCIP_CALL( SCIPreleaseCons(scip, &newcons) ); 3921 if( naddconss != NULL ) 3922 ++(*naddconss); 3923 } 3924 3925 /* create and add 0 <= z - minact x */ 3926 if( !SCIPisZero(scip, minact) ) 3927 { 3928 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s_2", SCIPconsGetName(cons), SCIPvarGetName(facvar)); 3929 SCIP_CALL( SCIPcreateConsBasicVarbound(scip, &newcons, name, auxvar, facvar, -minact, 0.0, SCIPinfinity(scip)) ); 3930 SCIP_CALL( SCIPaddCons(scip, newcons) ); 3931 SCIP_CALL( SCIPreleaseCons(scip, &newcons) ); 3932 if( naddconss != NULL ) 3933 ++(*naddconss); 3934 } 3935 3936 /* create and add minact <= sum_j c_j x_j - z + minact x_i */ 3937 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s_3", SCIPconsGetName(cons), SCIPvarGetName(facvar)); 3938 SCIP_CALL( SCIPcreateConsBasicLinear(scip, &newcons, name, nvars, vars, coefs, minact, SCIPinfinity(scip)) ); 3939 SCIP_CALL( SCIPaddCoefLinear(scip, newcons, auxvar, -1.0) ); 3940 if( !SCIPisZero(scip, minact) ) 3941 { 3942 SCIP_CALL( SCIPaddCoefLinear(scip, newcons, facvar, minact) ); 3943 } 3944 SCIP_CALL( SCIPaddCons(scip, newcons) ); 3945 SCIP_CALL( SCIPreleaseCons(scip, &newcons) ); 3946 if( naddconss != NULL ) 3947 ++(*naddconss); 3948 3949 /* create and add sum_j c_j x_j - z + maxact x_i <= maxact */ 3950 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s_4", SCIPconsGetName(cons), SCIPvarGetName(facvar)); 3951 SCIP_CALL( SCIPcreateConsBasicLinear(scip, &newcons, name, nvars, vars, coefs, -SCIPinfinity(scip), maxact) ); 3952 SCIP_CALL( SCIPaddCoefLinear(scip, newcons, auxvar, -1.0) ); 3953 if( !SCIPisZero(scip, maxact) ) 3954 { 3955 SCIP_CALL( SCIPaddCoefLinear(scip, newcons, facvar, maxact) ); 3956 } 3957 SCIP_CALL( SCIPaddCons(scip, newcons) ); 3958 SCIP_CALL( SCIPreleaseCons(scip, &newcons) ); 3959 if( naddconss != NULL ) 3960 ++(*naddconss); 3961 3962 /* create variable expression */ 3963 SCIP_CALL( createExprVar(scip, conshdlr, newexpr, auxvar) ); 3964 3965 /* release auxvar */ 3966 SCIP_CALL( SCIPreleaseVar(scip, &auxvar) ); 3967 3968 return SCIP_OKAY; 3969 } 3970 3971 /** helper method to generate an expression for a sum of products of binary variables; note that the method captures the generated expression */ 3972 static 3973 SCIP_RETCODE getFactorizedBinaryQuadraticExpr( 3974 SCIP* scip, /**< SCIP data structure */ 3975 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 3976 SCIP_CONS* cons, /**< constraint */ 3977 SCIP_EXPR* sumexpr, /**< expression */ 3978 int minterms, /**< minimum number of terms in a the sum of x_i sum_j c_j x_j */ 3979 SCIP_EXPR** newexpr, /**< pointer to store the expression that represents the binary quadratic */ 3980 int* naddconss /**< pointer to update the total number of added constraints (might be NULL) */ 3981 ) 3982 { 3983 SCIP_EXPR** exprs = NULL; 3984 SCIP_VAR** tmpvars = NULL; 3985 SCIP_VAR** vars = NULL; 3986 SCIP_VAR** xs = NULL; 3987 SCIP_VAR** ys = NULL; 3988 SCIP_Real* exprcoefs = NULL; 3989 SCIP_Real* tmpcoefs = NULL; 3990 SCIP_Real* sumcoefs; 3991 SCIP_Bool* isused = NULL; 3992 int* childidxs = NULL; 3993 int* count = NULL; 3994 int nchildren; 3995 int nexprs = 0; 3996 int nterms; 3997 int nvars; 3998 int ntotalvars; 3999 int i; 4000 4001 assert(sumexpr != NULL); 4002 assert(minterms > 1); 4003 assert(newexpr != NULL); 4004 4005 *newexpr = NULL; 4006 4007 /* check whether sumexpr is indeed a sum */ 4008 if( !SCIPisExprSum(scip, sumexpr) ) 4009 return SCIP_OKAY; 4010 4011 nchildren = SCIPexprGetNChildren(sumexpr); 4012 sumcoefs = SCIPgetCoefsExprSum(sumexpr); 4013 nvars = SCIPgetNVars(scip); 4014 ntotalvars = SCIPgetNTotalVars(scip); 4015 4016 /* check whether there are enough terms available */ 4017 if( nchildren < minterms ) 4018 return SCIP_OKAY; 4019 4020 /* allocate memory */ 4021 SCIP_CALL( SCIPallocBufferArray(scip, &xs, nchildren) ); 4022 SCIP_CALL( SCIPallocBufferArray(scip, &ys, nchildren) ); 4023 SCIP_CALL( SCIPallocBufferArray(scip, &childidxs, nchildren) ); 4024 4025 /* collect all bilinear binary product terms */ 4026 SCIP_CALL( getBilinearBinaryTerms(scip, sumexpr, xs, ys, childidxs, &nterms) ); 4027 4028 /* check whether there are enough terms available */ 4029 if( nterms < minterms ) 4030 goto TERMINATE; 4031 4032 /* store how often each variable appears in a bilinear binary product */ 4033 SCIP_CALL( SCIPduplicateBufferArray(scip, &vars, SCIPgetVars(scip), nvars) ); 4034 SCIP_CALL( SCIPallocClearBufferArray(scip, &count, ntotalvars) ); 4035 SCIP_CALL( SCIPallocClearBufferArray(scip, &isused, nchildren) ); 4036 4037 SCIP_CALL( SCIPallocBufferArray(scip, &exprs, nchildren) ); 4038 SCIP_CALL( SCIPallocBufferArray(scip, &exprcoefs, nchildren) ); 4039 SCIP_CALL( SCIPallocBufferArray(scip, &tmpvars, MIN(nterms, nvars)) ); 4040 SCIP_CALL( SCIPallocBufferArray(scip, &tmpcoefs, MIN(nterms, nvars)) ); 4041 4042 for( i = 0; i < nterms; ++i ) 4043 { 4044 int xidx; 4045 int yidx; 4046 4047 assert(xs[i] != NULL); 4048 assert(ys[i] != NULL); 4049 4050 xidx = SCIPvarGetIndex(xs[i]); 4051 assert(xidx < ntotalvars); 4052 yidx = SCIPvarGetIndex(ys[i]); 4053 assert(yidx < ntotalvars); 4054 4055 ++count[xidx]; 4056 ++count[yidx]; 4057 4058 SCIPdebugMsg(scip, "increase counter for %s to %d\n", SCIPvarGetName(xs[i]), count[xidx]); 4059 SCIPdebugMsg(scip, "increase counter for %s to %d\n", SCIPvarGetName(ys[i]), count[yidx]); 4060 } 4061 4062 /* sort variables; don't change order of count array because it depends on problem indices */ 4063 { 4064 int* tmpcount; 4065 4066 SCIP_CALL( SCIPduplicateBufferArray(scip, &tmpcount, count, nvars) ); 4067 SCIPsortDownIntPtr(tmpcount, (void**)vars, nvars); 4068 SCIPfreeBufferArray(scip, &tmpcount); 4069 } 4070 4071 for( i = 0; i < nvars; ++i ) 4072 { 4073 SCIP_VAR* facvar = vars[i]; 4074 int ntmpvars = 0; 4075 int j; 4076 4077 /* skip candidate if there are not enough terms left */ 4078 if( count[SCIPvarGetIndex(vars[i])] < minterms ) 4079 continue; 4080 4081 SCIPdebugMsg(scip, "consider facvar = %s with count = %d\n", SCIPvarGetName(facvar), count[SCIPvarGetIndex(vars[i])]); 4082 4083 /* collect variables for x_i * sum_j c_ij x_j */ 4084 for( j = 0; j < nterms; ++j ) 4085 { 4086 int childidx = childidxs[j]; 4087 assert(childidx >= 0 && childidx < nchildren); 4088 4089 if( !isused[childidx] && (xs[j] == facvar || ys[j] == facvar) ) 4090 { 4091 SCIP_Real coef; 4092 int xidx; 4093 int yidx; 4094 4095 coef = sumcoefs[childidx]; 4096 assert(coef != 0.0); 4097 4098 /* collect corresponding variable */ 4099 tmpvars[ntmpvars] = (xs[j] == facvar) ? ys[j] : xs[j]; 4100 tmpcoefs[ntmpvars] = coef; 4101 ++ntmpvars; 4102 4103 /* update counters */ 4104 xidx = SCIPvarGetIndex(xs[j]); 4105 assert(xidx < ntotalvars); 4106 yidx = SCIPvarGetIndex(ys[j]); 4107 assert(yidx < ntotalvars); 4108 --count[xidx]; 4109 --count[yidx]; 4110 assert(count[xidx] >= 0); 4111 assert(count[yidx] >= 0); 4112 4113 /* mark term to be used */ 4114 isused[childidx] = TRUE; 4115 } 4116 } 4117 assert(ntmpvars >= minterms); 4118 assert(SCIPvarGetIndex(facvar) < ntotalvars); 4119 assert(count[SCIPvarGetIndex(facvar)] == 0); /* facvar should not appear in any other bilinear term */ 4120 4121 /* create required constraints and store the generated expression */ 4122 SCIP_CALL( reformulateFactorizedBinaryQuadratic(scip, conshdlr, cons, facvar, tmpvars, tmpcoefs, ntmpvars, &exprs[nexprs], naddconss) ); 4123 exprcoefs[nexprs] = 1.0; 4124 ++nexprs; 4125 } 4126 4127 /* factorization was only successful if at least one expression has been generated */ 4128 if( nexprs > 0 ) 4129 { 4130 int nexprsold = nexprs; 4131 4132 /* add all children of the sum that have not been used */ 4133 for( i = 0; i < nchildren; ++i ) 4134 { 4135 if( !isused[i] ) 4136 { 4137 exprs[nexprs] = SCIPexprGetChildren(sumexpr)[i]; 4138 exprcoefs[nexprs] = sumcoefs[i]; 4139 ++nexprs; 4140 } 4141 } 4142 4143 /* create a new sum expression */ 4144 SCIP_CALL( SCIPcreateExprSum(scip, newexpr, nexprs, exprs, exprcoefs, SCIPgetConstantExprSum(sumexpr), exprownerCreate, (void*)conshdlr) ); 4145 4146 /* release all expressions that have been generated by reformulateFactorizedBinaryQuadratic() */ 4147 for( i = 0; i < nexprsold; ++i ) 4148 { 4149 SCIP_CALL( SCIPreleaseExpr(scip, &exprs[i]) ); 4150 } 4151 } 4152 4153 TERMINATE: 4154 /* free memory */ 4155 SCIPfreeBufferArrayNull(scip, &tmpcoefs); 4156 SCIPfreeBufferArrayNull(scip, &tmpvars); 4157 SCIPfreeBufferArrayNull(scip, &exprcoefs); 4158 SCIPfreeBufferArrayNull(scip, &exprs); 4159 SCIPfreeBufferArrayNull(scip, &vars); 4160 SCIPfreeBufferArrayNull(scip, &isused); 4161 SCIPfreeBufferArrayNull(scip, &count); 4162 SCIPfreeBufferArray(scip, &childidxs); 4163 SCIPfreeBufferArray(scip, &ys); 4164 SCIPfreeBufferArray(scip, &xs); 4165 4166 return SCIP_OKAY; 4167 } 4168 4169 /** helper method to create an AND constraint or varbound constraints for a given binary product expression */ 4170 static 4171 SCIP_RETCODE getBinaryProductExprDo( 4172 SCIP* scip, /**< SCIP data structure */ 4173 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 4174 SCIP_EXPR* prodexpr, /**< product expression */ 4175 SCIP_EXPR** newexpr, /**< pointer to store the expression that represents the product */ 4176 int* naddconss, /**< pointer to update the total number of added constraints (might be NULL) */ 4177 SCIP_Bool empathy4and /**< whether to use an AND constraint, if possible */ 4178 ) 4179 { 4180 SCIP_VAR** vars; 4181 SCIP_CONS* cons; 4182 SCIP_Real* coefs; 4183 SCIP_VAR* w; 4184 char* name; 4185 int nchildren; 4186 int i; 4187 4188 assert(conshdlr != NULL); 4189 assert(prodexpr != NULL); 4190 assert(SCIPisExprProduct(scip, prodexpr)); 4191 assert(newexpr != NULL); 4192 4193 nchildren = SCIPexprGetNChildren(prodexpr); 4194 assert(nchildren >= 2); 4195 4196 /* memory to store the variables of the variable expressions (+1 for w) and their name */ 4197 SCIP_CALL( SCIPallocBufferArray(scip, &vars, nchildren + 1) ); 4198 SCIP_CALL( SCIPallocBufferArray(scip, &coefs, nchildren + 1) ); 4199 SCIP_CALL( SCIPallocBufferArray(scip, &name, nchildren * (SCIP_MAXSTRLEN + 1) + 20) ); 4200 4201 /* prepare the names of the variable and the constraints */ 4202 strcpy(name, "binreform"); 4203 for( i = 0; i < nchildren; ++i ) 4204 { 4205 vars[i] = SCIPgetVarExprVar(SCIPexprGetChildren(prodexpr)[i]); 4206 coefs[i] = 1.0; 4207 assert(vars[i] != NULL); 4208 (void) strcat(name, "_"); 4209 (void) strcat(name, SCIPvarGetName(vars[i])); 4210 } 4211 4212 /* create and add variable */ 4213 SCIP_CALL( SCIPcreateVarBasic(scip, &w, name, 0.0, 1.0, 0.0, SCIP_VARTYPE_IMPLINT) ); 4214 SCIP_CALL( SCIPaddVar(scip, w) ); 4215 SCIPdebugMsg(scip, " created auxiliary variable %s\n", name); 4216 4217 /* use variable bound constraints if it is a bilinear product and there is no empathy for an AND constraint */ 4218 if( nchildren == 2 && !empathy4and ) 4219 { 4220 SCIP_VAR* x = vars[0]; 4221 SCIP_VAR* y = vars[1]; 4222 4223 assert(x != NULL); 4224 assert(y != NULL); 4225 assert(x != y); 4226 4227 /* create and add x - w >= 0 */ 4228 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s_1", SCIPvarGetName(x), SCIPvarGetName(y)); 4229 SCIP_CALL( SCIPcreateConsBasicVarbound(scip, &cons, name, x, w, -1.0, 0.0, SCIPinfinity(scip)) ); 4230 SCIP_CALL( SCIPaddCons(scip, cons) ); 4231 SCIP_CALL( SCIPreleaseCons(scip, &cons) ); 4232 4233 /* create and add y - w >= 0 */ 4234 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s_2", SCIPvarGetName(x), SCIPvarGetName(y)); 4235 SCIP_CALL( SCIPcreateConsBasicVarbound(scip, &cons, name, y, w, -1.0, 0.0, SCIPinfinity(scip)) ); 4236 SCIP_CALL( SCIPaddCons(scip, cons) ); 4237 SCIP_CALL( SCIPreleaseCons(scip, &cons) ); 4238 4239 /* create and add x + y - w <= 1 */ 4240 vars[2] = w; 4241 coefs[2] = -1.0; 4242 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "binreform_%s_%s_3", SCIPvarGetName(x), SCIPvarGetName(y)); 4243 SCIP_CALL( SCIPcreateConsBasicLinear(scip, &cons, name, 3, vars, coefs, -SCIPinfinity(scip), 1.0) ); 4244 SCIP_CALL( SCIPaddCons(scip, cons) ); 4245 SCIP_CALL( SCIPreleaseCons(scip, &cons) ); 4246 4247 /* update number of added constraints */ 4248 if( naddconss != NULL ) 4249 *naddconss += 3; 4250 } 4251 else 4252 { 4253 /* create, add, and release AND constraint */ 4254 SCIP_CALL( SCIPcreateConsBasicAnd(scip, &cons, name, w, nchildren, vars) ); 4255 SCIP_CALL( SCIPaddCons(scip, cons) ); 4256 SCIP_CALL( SCIPreleaseCons(scip, &cons) ); 4257 SCIPdebugMsg(scip, " create AND constraint\n"); 4258 4259 /* update number of added constraints */ 4260 if( naddconss != NULL ) 4261 *naddconss += 1; 4262 } 4263 4264 /* create variable expression */ 4265 SCIP_CALL( createExprVar(scip, conshdlr, newexpr, w) ); 4266 4267 /* release created variable */ 4268 SCIP_CALL( SCIPreleaseVar(scip, &w) ); 4269 4270 /* free memory */ 4271 SCIPfreeBufferArray(scip, &name); 4272 SCIPfreeBufferArray(scip, &coefs); 4273 SCIPfreeBufferArray(scip, &vars); 4274 4275 return SCIP_OKAY; 4276 } 4277 4278 /** helper method to generate an expression for the product of binary variables; note that the method captures the generated expression */ 4279 static 4280 SCIP_RETCODE getBinaryProductExpr( 4281 SCIP* scip, /**< SCIP data structure */ 4282 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 4283 SCIP_HASHMAP* exprmap, /**< map to remember generated variables for visited product expressions */ 4284 SCIP_EXPR* prodexpr, /**< product expression */ 4285 SCIP_EXPR** newexpr, /**< pointer to store the expression that represents the product */ 4286 int* naddconss, /**< pointer to update the total number of added constraints (might be NULL) */ 4287 int* nchgcoefs /**< pointer to update the total number of changed coefficients (might be NULL) */ 4288 ) 4289 { 4290 SCIP_CONSHDLRDATA* conshdlrdata; 4291 int nchildren; 4292 4293 assert(prodexpr != NULL); 4294 assert(newexpr != NULL); 4295 4296 *newexpr = NULL; 4297 4298 /* only consider products of binary variables */ 4299 if( !isBinaryProduct(scip, prodexpr) ) 4300 return SCIP_OKAY; 4301 4302 conshdlrdata = SCIPconshdlrGetData(conshdlr); 4303 assert(conshdlrdata != NULL); 4304 nchildren = SCIPexprGetNChildren(prodexpr); 4305 assert(nchildren >= 2); 4306 4307 /* check whether there is already an expression that represents the product */ 4308 if( SCIPhashmapExists(exprmap, (void*)prodexpr) ) 4309 { 4310 *newexpr = (SCIP_EXPR*) SCIPhashmapGetImage(exprmap, (void*)prodexpr); 4311 assert(*newexpr != NULL); 4312 4313 /* capture expression */ 4314 SCIPcaptureExpr(*newexpr); 4315 } 4316 else 4317 { 4318 SCIPdebugMsg(scip, " product expression %p has been considered for the first time\n", (void*)prodexpr); 4319 4320 if( nchildren == 2 ) 4321 { 4322 SCIP_CLIQUE** xcliques; 4323 SCIP_VAR* x; 4324 SCIP_VAR* y; 4325 SCIP_Bool found_clique = FALSE; 4326 int c; 4327 4328 /* get variables from the product expression */ 4329 x = SCIPgetVarExprVar(SCIPexprGetChildren(prodexpr)[0]); 4330 assert(x != NULL); 4331 y = SCIPgetVarExprVar(SCIPexprGetChildren(prodexpr)[1]); 4332 assert(y != NULL); 4333 assert(x != y); 4334 4335 /* first try to find a clique containing both variables */ 4336 xcliques = SCIPvarGetCliques(x, TRUE); 4337 4338 /* look in cliques containing x */ 4339 for( c = 0; c < SCIPvarGetNCliques(x, TRUE); ++c ) 4340 { 4341 if( SCIPcliqueHasVar(xcliques[c], y, TRUE) ) /* x + y <= 1 => x*y = 0 */ 4342 { 4343 /* create zero value expression */ 4344 SCIP_CALL( SCIPcreateExprValue(scip, newexpr, 0.0, exprownerCreate, (void*)conshdlr) ); 4345 4346 if( nchgcoefs != NULL ) 4347 *nchgcoefs += 1; 4348 4349 found_clique = TRUE; 4350 break; 4351 } 4352 4353 if( SCIPcliqueHasVar(xcliques[c], y, FALSE) ) /* x + (1-y) <= 1 => x*y = x */ 4354 { 4355 /* create variable expression for x */ 4356 SCIP_CALL( createExprVar(scip, conshdlr, newexpr, x) ); 4357 4358 if( nchgcoefs != NULL ) 4359 *nchgcoefs += 2; 4360 4361 found_clique = TRUE; 4362 break; 4363 } 4364 } 4365 4366 if( !found_clique ) 4367 { 4368 xcliques = SCIPvarGetCliques(x, FALSE); 4369 4370 /* look in cliques containing complement of x */ 4371 for( c = 0; c < SCIPvarGetNCliques(x, FALSE); ++c ) 4372 { 4373 if( SCIPcliqueHasVar(xcliques[c], y, TRUE) ) /* (1-x) + y <= 1 => x*y = y */ 4374 { 4375 /* create variable expression for y */ 4376 SCIP_CALL( createExprVar(scip, conshdlr, newexpr, y) ); 4377 4378 if( nchgcoefs != NULL ) 4379 *nchgcoefs += 1; 4380 4381 found_clique = TRUE; 4382 break; 4383 } 4384 4385 if( SCIPcliqueHasVar(xcliques[c], y, FALSE) ) /* (1-x) + (1-y) <= 1 => x*y = x + y - 1 */ 4386 { 4387 /* create sum expression */ 4388 SCIP_EXPR* sum_children[2]; 4389 SCIP_Real sum_coefs[2]; 4390 SCIP_CALL( createExprVar(scip, conshdlr, &sum_children[0], x) ); 4391 SCIP_CALL( createExprVar(scip, conshdlr, &sum_children[1], y) ); 4392 sum_coefs[0] = 1.0; 4393 sum_coefs[1] = 1.0; 4394 SCIP_CALL( SCIPcreateExprSum(scip, newexpr, 2, sum_children, sum_coefs, -1.0, exprownerCreate, (void*)conshdlr) ); 4395 4396 SCIP_CALL( SCIPreleaseExpr(scip, &sum_children[0]) ); 4397 SCIP_CALL( SCIPreleaseExpr(scip, &sum_children[1]) ); 4398 4399 if( nchgcoefs != NULL ) 4400 *nchgcoefs += 3; 4401 4402 found_clique = TRUE; 4403 break; 4404 } 4405 } 4406 } 4407 4408 /* if the variables are not in a clique, do standard linearization */ 4409 if( !found_clique ) 4410 { 4411 SCIP_CALL( getBinaryProductExprDo(scip, conshdlr, prodexpr, newexpr, naddconss, conshdlrdata->reformbinprodsand) ); 4412 } 4413 } 4414 else 4415 { 4416 /* linearize binary product using an AND constraint because nchildren > 2 */ 4417 SCIP_CALL( getBinaryProductExprDo(scip, conshdlr, prodexpr, newexpr, naddconss, conshdlrdata->reformbinprodsand) ); 4418 } 4419 4420 /* hash variable expression */ 4421 SCIP_CALL( SCIPhashmapInsert(exprmap, (void*)prodexpr, *newexpr) ); 4422 } 4423 4424 return SCIP_OKAY; 4425 } 4426 4427 /** helper function to replace binary products in a given constraint */ 4428 static 4429 SCIP_RETCODE replaceBinaryProducts( 4430 SCIP* scip, /**< SCIP data structure */ 4431 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 4432 SCIP_CONS* cons, /**< constraint */ 4433 SCIP_HASHMAP* exprmap, /**< map to remember generated variables for visited product expressions */ 4434 SCIP_EXPRITER* it, /**< expression iterator */ 4435 int* naddconss, /**< pointer to update the total number of added constraints (might be NULL) */ 4436 int* nchgcoefs /**< pointer to update the total number of changed coefficients (might be NULL) */ 4437 ) 4438 { 4439 SCIP_CONSHDLRDATA* conshdlrdata; 4440 SCIP_CONSDATA* consdata; 4441 SCIP_EXPR* expr; 4442 4443 assert(conshdlr != NULL); 4444 assert(cons != NULL); 4445 assert(exprmap != NULL); 4446 assert(it != NULL); 4447 4448 conshdlrdata = SCIPconshdlrGetData(conshdlr); 4449 assert(conshdlrdata != NULL); 4450 4451 consdata = SCIPconsGetData(cons); 4452 assert(consdata != NULL); 4453 assert(consdata->expr != NULL); 4454 4455 SCIPdebugMsg(scip, " check constraint %s\n", SCIPconsGetName(cons)); 4456 4457 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) ) 4458 { 4459 SCIP_EXPR* newexpr = NULL; 4460 SCIP_EXPR* childexpr; 4461 int childexpridx; 4462 4463 childexpridx = SCIPexpriterGetChildIdxDFS(it); 4464 assert(childexpridx >= 0 && childexpridx < SCIPexprGetNChildren(expr)); 4465 childexpr = SCIPexpriterGetChildExprDFS(it); 4466 assert(childexpr != NULL); 4467 4468 /* try to factorize variables in a sum expression that contains several products of binary variables */ 4469 if( conshdlrdata->reformbinprodsfac > 1 ) 4470 { 4471 SCIP_CALL( getFactorizedBinaryQuadraticExpr(scip, conshdlr, cons, childexpr, conshdlrdata->reformbinprodsfac, &newexpr, naddconss) ); 4472 } 4473 4474 /* try to create an expression that represents a product of binary variables */ 4475 if( newexpr == NULL ) 4476 { 4477 SCIP_CALL( getBinaryProductExpr(scip, conshdlr, exprmap, childexpr, &newexpr, naddconss, nchgcoefs) ); 4478 } 4479 4480 if( newexpr != NULL ) 4481 { 4482 assert(naddconss == NULL || *naddconss > 0 || nchgcoefs == NULL || *nchgcoefs > 0); 4483 4484 /* replace product expression */ 4485 SCIP_CALL( SCIPreplaceExprChild(scip, expr, childexpridx, newexpr) ); 4486 4487 /* note that the expression has been captured by getBinaryProductExpr and SCIPreplaceExprChild */ 4488 SCIP_CALL( SCIPreleaseExpr(scip, &newexpr) ); 4489 4490 /* mark the constraint to not be simplified anymore */ 4491 consdata->issimplified = FALSE; 4492 } 4493 } 4494 4495 return SCIP_OKAY; 4496 } 4497 4498 /** reformulates products of binary variables during presolving in the following way: 4499 * 4500 * Let \f$\sum_{i,j} Q_{ij} x_i x_j\f$ be a subexpression that only contains binary variables. 4501 * Each term \f$x_i x_j\f$ is reformulated with the help of an extra (implicit integer) variable \f$z_{ij}\f$ in {0,1}: 4502 * \f[ 4503 * z_{ij} \leq x_i, \qquad z_{ij} \leq x_j, \qquad x_i + x_j - z_{ij} \leq 1. 4504 * \f] 4505 * 4506 * Before reformulating \f$x_i x_j\f$ in this way, it is checked whether there is a clique that contains \f$x_i\f$ and \f$x_j\f$. 4507 * These cliques allow for a better reformulation. There are four cases: 4508 * 4509 * 1. \f$x_i + x_j \leq 1\f$ implies that \f$x_i x_j = 0\f$ 4510 * 2. \f$x_i + (1 - x_j) \leq 1\f$ implies \f$x_i x_j = x_i\f$ 4511 * 3. \f$(1 - x_i) + x_j \leq 1\f$ implies \f$x_i x_j = x_j\f$ 4512 * 4. \f$(1 - x_i) + (1 - x_j) \leq 1\f$ implies \f$x_i x_j = x_i + x_j - 1\f$ 4513 * 4514 * The reformulation using \f$z_{ij}\f$ or the cliques is implemented in getBinaryProductExpr(). 4515 * 4516 * Introducing too many extra variables and constraints can have a negative impact on the performance (e.g., due to 4517 * slow probing). For this reason, it is checked in getFactorizedBinaryQuadraticExpr() whether \f$\sum_{i,j} Q_{ij} x_i x_j\f$ 4518 * contains large (≥ `reformbinprodsfac` parameter) lower sums of the form \f$x_i \sum_j Q_{ij} x_j\f$. 4519 * Such a lower sum is reformulated with only one extra variable w_i: 4520 * \f{align}{ 4521 * \text{maxact} & := \sum_j \max(0, Q_{ij}), \\ 4522 * \text{minact} & := \sum_j \min(0, Q_{ij}), \\ 4523 * \text{minact}\, x_i & \leq w_i, \\ 4524 * w_i &\leq \text{maxact}\, x_i, \\ 4525 * \text{minact} &\leq \sum_j Q_{ij} x_j - w_i + \text{minact}\, x_i \\ 4526 * \text{maxact} &\geq \sum_j Q_{ij} x_j - w_i + \text{maxact}\, x_i 4527 * \f} 4528 * We mark \f$w_i\f$ to be implicit integer if all \f$Q_{ij}\f$ are integer. After each replacement of a lower sum, it 4529 * is checked whether there are enough terms left to factorize other binary variables. Lower sums with a larger number 4530 * of terms are prioritized. 4531 */ 4532 static 4533 SCIP_RETCODE presolveBinaryProducts( 4534 SCIP* scip, /**< SCIP data structure */ 4535 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 4536 SCIP_CONS** conss, /**< constraints */ 4537 int nconss, /**< total number of constraints */ 4538 int* naddconss, /**< pointer to store the total number of added constraints (might be NULL) */ 4539 int* nchgcoefs /**< pointer to store the total number of changed coefficients (might be NULL) */ 4540 ) 4541 { 4542 SCIP_CONSHDLRDATA* conshdlrdata; 4543 SCIP_HASHMAP* exprmap; 4544 SCIP_EXPRITER* it; 4545 int c; 4546 4547 assert(conshdlr != NULL); 4548 4549 /* no nonlinear constraints or binary variables -> skip */ 4550 if( nconss == 0 || SCIPgetNBinVars(scip) == 0 ) 4551 return SCIP_OKAY; 4552 assert(conss != NULL); 4553 4554 conshdlrdata = SCIPconshdlrGetData(conshdlr); 4555 assert(conshdlrdata != NULL); 4556 4557 /* create expression hash map */ 4558 SCIP_CALL( SCIPhashmapCreate(&exprmap, SCIPblkmem(scip), SCIPgetNVars(scip)) ); 4559 4560 /* create expression iterator */ 4561 SCIP_CALL( SCIPcreateExpriter(scip, &it) ); 4562 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) ); 4563 SCIPexpriterSetStagesDFS(it, SCIP_EXPRITER_VISITINGCHILD); 4564 4565 SCIPdebugMsg(scip, "call presolveBinaryProducts()\n"); 4566 4567 for( c = 0; c < nconss; ++c ) 4568 { 4569 SCIP_CONSDATA* consdata; 4570 SCIP_EXPR* newexpr = NULL; 4571 4572 assert(conss[c] != NULL); 4573 4574 consdata = SCIPconsGetData(conss[c]); 4575 assert(consdata != NULL); 4576 4577 /* try to reformulate the root expression */ 4578 if( conshdlrdata->reformbinprodsfac > 1 ) 4579 { 4580 SCIP_CALL( getFactorizedBinaryQuadraticExpr(scip, conshdlr, conss[c], consdata->expr, conshdlrdata->reformbinprodsfac, &newexpr, naddconss) ); 4581 } 4582 4583 /* release the root node if another expression has been found */ 4584 if( newexpr != NULL ) 4585 { 4586 SCIP_CALL( SCIPreleaseExpr(scip, &consdata->expr) ); 4587 consdata->expr = newexpr; 4588 4589 /* mark constraint to be not simplified anymore */ 4590 consdata->issimplified = FALSE; 4591 } 4592 4593 /* replace each product of binary variables separately */ 4594 SCIP_CALL( replaceBinaryProducts(scip, conshdlr, conss[c], exprmap, it, naddconss, nchgcoefs) ); 4595 } 4596 4597 /* free memory */ 4598 SCIPhashmapFree(&exprmap); 4599 SCIPfreeExpriter(&it); 4600 4601 return SCIP_OKAY; 4602 } 4603 4604 /** scales the sides of the constraint \f$\ell \leq \sum_i c_i f_i(x) \leq r\f$. 4605 * 4606 * Let \f$n_+\f$ the number of positive coefficients \f$c_i\f$ and \f$n_-\f$ be the number of negative coefficients. 4607 * Then scale by -1 if 4608 * - \f$n_+ < n_-\f$, or 4609 * - \f$n_+ = n_-\f$ and \f$r = \infty\f$. 4610 */ 4611 static 4612 SCIP_RETCODE scaleConsSides( 4613 SCIP* scip, /**< SCIP data structure */ 4614 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */ 4615 SCIP_CONS* cons, /**< nonlinear constraint */ 4616 SCIP_Bool* changed /**< buffer to store if the expression of cons changed */ 4617 ) 4618 { 4619 SCIP_CONSDATA* consdata; 4620 int i; 4621 4622 assert(cons != NULL); 4623 4624 consdata = SCIPconsGetData(cons); 4625 assert(consdata != NULL); 4626 4627 if( SCIPisExprSum(scip, consdata->expr) ) 4628 { 4629 SCIP_Real* coefs; 4630 SCIP_Real constant; 4631 int nchildren; 4632 int counter = 0; 4633 4634 coefs = SCIPgetCoefsExprSum(consdata->expr); 4635 constant = SCIPgetConstantExprSum(consdata->expr); 4636 nchildren = SCIPexprGetNChildren(consdata->expr); 4637 4638 /* handle special case when constraint is l <= -f(x) <= r and f(x) not a sum: simplfy ensures f is not a sum */ 4639 if( nchildren == 1 && constant == 0.0 && coefs[0] == -1.0 ) 4640 { 4641 SCIP_EXPR* expr; 4642 expr = consdata->expr; 4643 4644 consdata->expr = SCIPexprGetChildren(expr)[0]; 4645 assert(!SCIPisExprSum(scip, consdata->expr)); 4646 4647 SCIPcaptureExpr(consdata->expr); 4648 4649 SCIPswapReals(&consdata->lhs, &consdata->rhs); 4650 consdata->lhs = -consdata->lhs; 4651 consdata->rhs = -consdata->rhs; 4652 4653 SCIP_CALL( SCIPreleaseExpr(scip, &expr) ); 4654 *changed = TRUE; 4655 return SCIP_OKAY; 4656 } 4657 4658 /* compute n_+ - n_i */ 4659 for( i = 0; i < nchildren; ++i ) 4660 counter += coefs[i] > 0 ? 1 : -1; 4661 4662 if( counter < 0 || (counter == 0 && SCIPisInfinity(scip, consdata->rhs)) ) 4663 { 4664 SCIP_EXPR* expr; 4665 SCIP_Real* newcoefs; 4666 4667 /* allocate memory */ 4668 SCIP_CALL( SCIPallocBufferArray(scip, &newcoefs, nchildren) ); 4669 4670 for( i = 0; i < nchildren; ++i ) 4671 newcoefs[i] = -coefs[i]; 4672 4673 /* create a new sum expression */ 4674 SCIP_CALL( SCIPcreateExprSum(scip, &expr, nchildren, SCIPexprGetChildren(consdata->expr), newcoefs, -constant, exprownerCreate, (void*)conshdlr) ); 4675 4676 /* replace expression in constraint data and scale sides */ 4677 SCIP_CALL( SCIPreleaseExpr(scip, &consdata->expr) ); 4678 consdata->expr = expr; 4679 SCIPswapReals(&consdata->lhs, &consdata->rhs); 4680 consdata->lhs = -consdata->lhs; 4681 consdata->rhs = -consdata->rhs; 4682 4683 /* free memory */ 4684 SCIPfreeBufferArray(scip, &newcoefs); 4685 4686 *changed = TRUE; 4687 } 4688 } 4689 4690 return SCIP_OKAY; 4691 } 4692 4693 /** forbid multiaggrations of variables that appear nonlinear in constraints */ 4694 static 4695 SCIP_RETCODE forbidNonlinearVariablesMultiaggration( 4696 SCIP* scip, /**< SCIP data structure */ 4697 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 4698 SCIP_CONS** conss, /**< constraints */ 4699 int nconss /**< number of constraints */ 4700 ) 4701 { 4702 SCIP_EXPRITER* it; 4703 SCIP_CONSDATA* consdata; 4704 SCIP_EXPR* expr; 4705 int c; 4706 4707 assert(scip != NULL); 4708 assert(conshdlr != NULL); 4709 4710 if( !SCIPconshdlrGetData(conshdlr)->forbidmultaggrnlvar ) 4711 return SCIP_OKAY; 4712 4713 SCIP_CALL( SCIPcreateExpriter(scip, &it) ); 4714 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) ); 4715 4716 for( c = 0; c < nconss; ++c ) 4717 { 4718 consdata = SCIPconsGetData(conss[c]); 4719 assert(consdata != NULL); 4720 4721 /* if root expression is sum, then forbid multiaggregation only for variables that are not in linear terms of sum, 4722 * i.e., skip children of sum that are variables 4723 */ 4724 if( SCIPisExprSum(scip, consdata->expr) ) 4725 { 4726 int i; 4727 SCIP_EXPR* child; 4728 for( i = 0; i < SCIPexprGetNChildren(consdata->expr); ++i ) 4729 { 4730 child = SCIPexprGetChildren(consdata->expr)[i]; 4731 4732 /* skip variable expression, as they correspond to a linear term */ 4733 if( SCIPisExprVar(scip, child) ) 4734 continue; 4735 4736 for( expr = SCIPexpriterRestartDFS(it, child); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) ) 4737 if( SCIPisExprVar(scip, expr) ) 4738 { 4739 SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, SCIPgetVarExprVar(expr)) ); 4740 } 4741 } 4742 } 4743 else 4744 { 4745 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) ) 4746 if( SCIPisExprVar(scip, expr) ) 4747 { 4748 SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, SCIPgetVarExprVar(expr)) ); 4749 } 4750 } 4751 } 4752 4753 SCIPfreeExpriter(&it); 4754 4755 return SCIP_OKAY; 4756 } 4757 4758 /** simplifies expressions and replaces common subexpressions for a set of constraints 4759 * @todo put the constant to the constraint sides 4760 */ 4761 static 4762 SCIP_RETCODE canonicalizeConstraints( 4763 SCIP* scip, /**< SCIP data structure */ 4764 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 4765 SCIP_CONS** conss, /**< constraints */ 4766 int nconss, /**< total number of constraints */ 4767 SCIP_PRESOLTIMING presoltiming, /**< presolve timing (SCIP_PRESOLTIMING_ALWAYS if not in presolving) */ 4768 SCIP_Bool* infeasible, /**< buffer to store whether infeasibility has been detected */ 4769 int* ndelconss, /**< counter to add number of deleted constraints, or NULL */ 4770 int* naddconss, /**< counter to add number of added constraints, or NULL */ 4771 int* nchgcoefs /**< counter to add number of changed coefficients, or NULL */ 4772 ) 4773 { 4774 SCIP_CONSHDLRDATA* conshdlrdata; 4775 SCIP_CONSDATA* consdata; 4776 int* nlockspos; 4777 int* nlocksneg; 4778 SCIP_Bool havechange; 4779 int i; 4780 4781 assert(scip != NULL); 4782 assert(conshdlr != NULL); 4783 assert(conss != NULL); 4784 assert(nconss > 0); 4785 assert(infeasible != NULL); 4786 4787 conshdlrdata = SCIPconshdlrGetData(conshdlr); 4788 assert(conshdlrdata != NULL); 4789 4790 /* update number of canonicalize calls */ 4791 ++(conshdlrdata->ncanonicalizecalls); 4792 4793 SCIP_CALL( SCIPstartClock(scip, conshdlrdata->canonicalizetime) ); 4794 4795 *infeasible = FALSE; 4796 4797 /* set havechange to TRUE in the first call of canonicalize; otherwise we might not replace common subexpressions */ 4798 havechange = conshdlrdata->ncanonicalizecalls == 1; 4799 4800 /* free nonlinear handlers information from expressions */ /* TODO can skip this in first presolve round */ 4801 SCIP_CALL( deinitSolve(scip, conshdlr, conss, nconss) ); 4802 4803 /* allocate memory for storing locks of each constraint */ 4804 SCIP_CALL( SCIPallocBufferArray(scip, &nlockspos, nconss) ); 4805 SCIP_CALL( SCIPallocBufferArray(scip, &nlocksneg, nconss) ); 4806 4807 /* unlock all constraints */ 4808 for( i = 0; i < nconss; ++i ) 4809 { 4810 assert(conss[i] != NULL); 4811 4812 consdata = SCIPconsGetData(conss[i]); 4813 assert(consdata != NULL); 4814 4815 /* remember locks */ 4816 nlockspos[i] = consdata->nlockspos; 4817 nlocksneg[i] = consdata->nlocksneg; 4818 4819 /* remove locks */ 4820 SCIP_CALL( addLocks(scip, conss[i], -consdata->nlockspos, -consdata->nlocksneg) ); 4821 assert(consdata->nlockspos == 0); 4822 assert(consdata->nlocksneg == 0); 4823 } 4824 4825 #ifndef NDEBUG 4826 /* check whether all locks of each expression have been removed */ 4827 for( i = 0; i < nconss; ++i ) 4828 { 4829 SCIP_EXPR* expr; 4830 SCIP_EXPRITER* it; 4831 4832 SCIP_CALL( SCIPcreateExpriter(scip, &it) ); 4833 4834 consdata = SCIPconsGetData(conss[i]); 4835 assert(consdata != NULL); 4836 4837 SCIP_CALL( SCIPexpriterInit(it, consdata->expr, SCIP_EXPRITER_RTOPOLOGIC, TRUE) ); 4838 for( expr = consdata->expr; !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) ) 4839 { 4840 assert(expr != NULL); 4841 assert(SCIPexprGetOwnerData(expr)->nlocksneg == 0); 4842 assert(SCIPexprGetOwnerData(expr)->nlockspos == 0); 4843 } 4844 SCIPfreeExpriter(&it); 4845 } 4846 #endif 4847 4848 /* reformulate products of binary variables */ 4849 if( conshdlrdata->reformbinprods && SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING 4850 && (presoltiming & SCIP_PRESOLTIMING_EXHAUSTIVE) ) 4851 { 4852 int tmpnaddconss = 0; 4853 int tmpnchgcoefs = 0; 4854 4855 /* call this function before simplification because expressions might not be simplified after reformulating 4856 * binary products; the detection of some nonlinear handlers might assume that expressions are simplified 4857 */ 4858 SCIP_CALL( presolveBinaryProducts(scip, conshdlr, conss, nconss, &tmpnaddconss, &tmpnchgcoefs) ); 4859 4860 /* update counters */ 4861 if( naddconss != NULL ) 4862 *naddconss = tmpnaddconss; 4863 if( nchgcoefs != NULL ) 4864 *nchgcoefs = tmpnchgcoefs; 4865 4866 /* check whether at least one expression has changed */ 4867 if( tmpnaddconss + tmpnchgcoefs > 0 ) 4868 havechange = TRUE; 4869 } 4870 4871 for( i = 0; i < nconss; ++i ) 4872 { 4873 consdata = SCIPconsGetData(conss[i]); 4874 assert(consdata != NULL); 4875 4876 /* call simplify for each expression */ 4877 if( !consdata->issimplified && consdata->expr != NULL ) 4878 { 4879 SCIP_EXPR* simplified; 4880 SCIP_Bool changed; 4881 4882 changed = FALSE; 4883 SCIP_CALL( SCIPsimplifyExpr(scip, consdata->expr, &simplified, &changed, infeasible, exprownerCreate, (void*)conshdlr) ); 4884 consdata->issimplified = TRUE; 4885 4886 if( changed ) 4887 havechange = TRUE; 4888 4889 /* If root expression changed, then we need to take care updating the locks as well (the consdata is the one holding consdata->expr "as a child"). 4890 * If root expression did not change, some subexpression may still have changed, but the locks were taking care of in the corresponding SCIPreplaceExprChild() call. 4891 */ 4892 if( simplified != consdata->expr ) 4893 { 4894 assert(changed); 4895 4896 /* release old expression */ 4897 SCIP_CALL( SCIPreleaseExpr(scip, &consdata->expr) ); 4898 4899 /* store simplified expression */ 4900 consdata->expr = simplified; 4901 } 4902 else 4903 { 4904 /* The simplify captures simplified in any case, also if nothing has changed. 4905 * Therefore, we have to release it here. 4906 */ 4907 SCIP_CALL( SCIPreleaseExpr(scip, &simplified) ); 4908 } 4909 4910 if( *infeasible ) 4911 break; 4912 4913 /* scale constraint sides */ 4914 SCIP_CALL( scaleConsSides(scip, conshdlr, conss[i], &changed) ); 4915 4916 if( changed ) 4917 havechange = TRUE; 4918 4919 /* handle constant root expression; either the problem is infeasible or the constraint is redundant */ 4920 if( SCIPisExprValue(scip, consdata->expr) ) 4921 { 4922 SCIP_Real value = SCIPgetValueExprValue(consdata->expr); 4923 if( (!SCIPisInfinity(scip, -consdata->lhs) && SCIPisFeasNegative(scip, value - consdata->lhs)) || 4924 (!SCIPisInfinity(scip, consdata->rhs) && SCIPisFeasPositive(scip, value - consdata->rhs)) ) 4925 { 4926 SCIPdebugMsg(scip, "<%s> with constant expression found infeasible\n", SCIPconsGetName(conss[i])); 4927 SCIPdebugPrintCons(scip, conss[i], NULL); 4928 *infeasible = TRUE; 4929 break; 4930 } 4931 else 4932 { 4933 SCIP_CALL( addLocks(scip, conss[i], nlockspos[i], nlocksneg[i]) ); 4934 SCIP_CALL( SCIPdelCons(scip, conss[i]) ); 4935 if( ndelconss != NULL ) 4936 ++*ndelconss; 4937 havechange = TRUE; 4938 } 4939 } 4940 } 4941 } 4942 4943 /* replace common subexpressions */ 4944 if( havechange && !*infeasible ) 4945 { 4946 SCIP_CONS** consssorted; 4947 SCIP_EXPR** rootexprs; 4948 SCIP_Bool replacedroot; 4949 4950 SCIP_CALL( SCIPallocBufferArray(scip, &rootexprs, nconss) ); 4951 for( i = 0; i < nconss; ++i ) 4952 rootexprs[i] = SCIPconsGetData(conss[i])->expr; 4953 4954 SCIP_CALL( SCIPreplaceCommonSubexpressions(scip, rootexprs, nconss, &replacedroot) ); 4955 4956 /* update pointer to root expr in constraints, if any has changed 4957 * SCIPreplaceCommonSubexpressions will have released the old expr and captures the new one 4958 */ 4959 if( replacedroot ) 4960 for( i = 0; i < nconss; ++i ) 4961 SCIPconsGetData(conss[i])->expr = rootexprs[i]; 4962 4963 SCIPfreeBufferArray(scip, &rootexprs); 4964 4965 /* TODO this is a possibly expensive way to update the variable expressions stored inside an expression which might have 4966 * been changed after simplification; now we completely recollect all variable expression and variable events 4967 */ 4968 4969 /* Each variable stores the constraints for which it catched varbound events sorted by the constraint index. 4970 * Thus, for performance reasons, it is better to call dropVarEvents in descending order of constraint index. 4971 */ 4972 SCIP_CALL( SCIPduplicateBufferArray(scip, &consssorted, conss, nconss) ); 4973 SCIPsortPtr((void**)consssorted, compIndexConsNonlinear, nconss); 4974 4975 for( i = nconss-1; i >= 0; --i ) 4976 { 4977 assert(i == 0 || compIndexConsNonlinear((void*)consssorted[i-1], (void*)consssorted[i]) < 0); 4978 if( SCIPconsIsDeleted(consssorted[i]) ) 4979 continue; 4980 4981 SCIP_CALL( dropVarEvents(scip, conshdlrdata->eventhdlr, consssorted[i]) ); 4982 SCIP_CALL( freeVarExprs(scip, SCIPconsGetData(consssorted[i])) ); 4983 } 4984 for( i = 0; i < nconss; ++i ) 4985 { 4986 if( SCIPconsIsDeleted(consssorted[i]) ) 4987 continue; 4988 4989 SCIP_CALL( storeVarExprs(scip, conshdlr, SCIPconsGetData(consssorted[i])) ); 4990 SCIP_CALL( catchVarEvents(scip, conshdlrdata->eventhdlr, consssorted[i]) ); 4991 } 4992 4993 SCIPfreeBufferArray(scip, &consssorted); 4994 4995 /* forbid multiaggregation for nonlinear variables again (in case new variables appeared now) 4996 * a multiaggregation of a nonlinear variable can yield to a large increase in expressions due to 4997 * expanding terms in simplify, e.g. ,(sum_i x_i)^2, so we just forbid these 4998 */ 4999 SCIP_CALL( forbidNonlinearVariablesMultiaggration(scip, conshdlr, conss, nconss) ); 5000 } 5001 5002 /* restore locks */ 5003 for( i = 0; i < nconss; ++i ) 5004 { 5005 if( SCIPconsIsDeleted(conss[i]) ) 5006 continue; 5007 5008 SCIP_CALL( addLocks(scip, conss[i], nlockspos[i], nlocksneg[i]) ); 5009 } 5010 5011 /* run nlhdlr detect if in presolving stage (that is, not in exitpre) 5012 * TODO can we skip this in presoltiming fast? 5013 */ 5014 if( SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING && !*infeasible ) 5015 { 5016 /* reset one of the number of detections counter to count only current presolving round */ 5017 for( i = 0; i < conshdlrdata->nnlhdlrs; ++i ) 5018 SCIPnlhdlrResetNDetectionslast(conshdlrdata->nlhdlrs[i]); 5019 5020 SCIP_CALL( initSolve(scip, conshdlr, conss, nconss) ); 5021 } 5022 5023 /* free allocated memory */ 5024 SCIPfreeBufferArray(scip, &nlocksneg); 5025 SCIPfreeBufferArray(scip, &nlockspos); 5026 5027 SCIP_CALL( SCIPstopClock(scip, conshdlrdata->canonicalizetime) ); 5028 5029 return SCIP_OKAY; 5030 } 5031 5032 /** merges constraints that have the same root expression */ 5033 static 5034 SCIP_RETCODE presolveMergeConss( 5035 SCIP* scip, /**< SCIP data structure */ 5036 SCIP_CONS** conss, /**< constraints to process */ 5037 int nconss, /**< number of constraints */ 5038 SCIP_Bool* success /**< pointer to store whether at least one constraint could be deleted */ 5039 ) 5040 { 5041 SCIP_HASHMAP* expr2cons; 5042 SCIP_Bool* updatelocks; 5043 int* nlockspos; 5044 int* nlocksneg; 5045 int c; 5046 5047 assert(success != NULL); 5048 5049 *success = FALSE; 5050 5051 /* not enough constraints available */ 5052 if( nconss <= 1 ) 5053 return SCIP_OKAY; 5054 5055 SCIP_CALL( SCIPhashmapCreate(&expr2cons, SCIPblkmem(scip), nconss) ); 5056 SCIP_CALL( SCIPallocClearBufferArray(scip, &updatelocks, nconss) ); 5057 SCIP_CALL( SCIPallocBufferArray(scip, &nlockspos, nconss) ); 5058 SCIP_CALL( SCIPallocBufferArray(scip, &nlocksneg, nconss) ); 5059 5060 for( c = 0; c < nconss; ++c ) 5061 { 5062 SCIP_CONSDATA* consdata; 5063 5064 /* ignore deleted constraints */ 5065 if( SCIPconsIsDeleted(conss[c]) ) 5066 continue; 5067 5068 consdata = SCIPconsGetData(conss[c]); 5069 assert(consdata != NULL); 5070 5071 /* add expression to the hash map if not seen so far */ 5072 if( !SCIPhashmapExists(expr2cons, (void*)consdata->expr) ) 5073 { 5074 SCIP_CALL( SCIPhashmapInsertInt(expr2cons, (void*)consdata->expr, c) ); 5075 } 5076 else 5077 { 5078 SCIP_CONSDATA* imgconsdata; 5079 int idx; 5080 5081 idx = SCIPhashmapGetImageInt(expr2cons, (void*)consdata->expr); 5082 assert(idx >= 0 && idx < nconss); 5083 5084 imgconsdata = SCIPconsGetData(conss[idx]); 5085 assert(imgconsdata != NULL); 5086 assert(imgconsdata->expr == consdata->expr); 5087 5088 SCIPdebugMsg(scip, "merge constraint %g <= %s <= %g with %g <= %s <= %g\n", consdata->lhs, 5089 SCIPconsGetName(conss[c]), consdata->rhs, imgconsdata->lhs, SCIPconsGetName(conss[idx]), imgconsdata->rhs); 5090 5091 /* check whether locks need to be updated */ 5092 if( !updatelocks[idx] && ((SCIPisInfinity(scip, -imgconsdata->lhs) && !SCIPisInfinity(scip, -consdata->lhs)) 5093 || (SCIPisInfinity(scip, imgconsdata->rhs) && !SCIPisInfinity(scip, consdata->rhs))) ) 5094 { 5095 nlockspos[idx] = imgconsdata->nlockspos; 5096 nlocksneg[idx] = imgconsdata->nlocksneg; 5097 SCIP_CALL( addLocks(scip, conss[idx], -imgconsdata->nlockspos, -imgconsdata->nlocksneg) ); 5098 updatelocks[idx] = TRUE; 5099 } 5100 5101 /* update constraint sides */ 5102 imgconsdata->lhs = MAX(imgconsdata->lhs, consdata->lhs); 5103 imgconsdata->rhs = MIN(imgconsdata->rhs, consdata->rhs); 5104 5105 /* delete constraint */ 5106 SCIP_CALL( SCIPdelCons(scip, conss[c]) ); 5107 *success = TRUE; 5108 } 5109 } 5110 5111 /* restore locks of updated constraints */ 5112 if( *success ) 5113 { 5114 for( c = 0; c < nconss; ++c ) 5115 { 5116 if( updatelocks[c] ) 5117 { 5118 SCIP_CALL( addLocks(scip, conss[c], nlockspos[c], nlocksneg[c]) ); 5119 } 5120 } 5121 } 5122 5123 /* free memory */ 5124 SCIPfreeBufferArray(scip, &nlocksneg); 5125 SCIPfreeBufferArray(scip, &nlockspos); 5126 SCIPfreeBufferArray(scip, &updatelocks); 5127 SCIPhashmapFree(&expr2cons); 5128 5129 return SCIP_OKAY; 5130 } 5131 5132 /** interval evaluation of variables as used in redundancy check 5133 * 5134 * Returns local variable bounds of a variable, relaxed by feastol, as interval. 5135 */ 5136 static 5137 SCIP_DECL_EXPR_INTEVALVAR(intEvalVarRedundancyCheck) 5138 { /*lint --e{715}*/ 5139 SCIP_CONSHDLRDATA* conshdlrdata; 5140 SCIP_INTERVAL interval; 5141 SCIP_Real lb; 5142 SCIP_Real ub; 5143 5144 assert(scip != NULL); 5145 assert(var != NULL); 5146 5147 conshdlrdata = (SCIP_CONSHDLRDATA*)intevalvardata; 5148 assert(conshdlrdata != NULL); 5149 5150 if( conshdlrdata->globalbounds ) 5151 { 5152 lb = SCIPvarGetLbGlobal(var); 5153 ub = SCIPvarGetUbGlobal(var); 5154 } 5155 else 5156 { 5157 lb = SCIPvarGetLbLocal(var); 5158 ub = SCIPvarGetUbLocal(var); 5159 } 5160 assert(lb <= ub); /* can SCIP ensure by now that variable bounds are not contradicting? */ 5161 5162 /* relax variable bounds, if there are bounds and variable is not fixed 5163 * (actually some assert complains if trying SCIPisRelEQ if both bounds are at different infinity) 5164 */ 5165 if( !(SCIPisInfinity(scip, -lb) && SCIPisInfinity(scip, ub)) && !SCIPisRelEQ(scip, lb, ub) ) 5166 { 5167 if( !SCIPisInfinity(scip, -lb) ) 5168 lb -= SCIPfeastol(scip); 5169 5170 if( !SCIPisInfinity(scip, ub) ) 5171 ub += SCIPfeastol(scip); 5172 } 5173 5174 /* convert SCIPinfinity() to SCIP_INTERVAL_INFINITY */ 5175 lb = -infty2infty(SCIPinfinity(scip), SCIP_INTERVAL_INFINITY, -lb); 5176 ub = infty2infty(SCIPinfinity(scip), SCIP_INTERVAL_INFINITY, ub); 5177 assert(lb <= ub); 5178 5179 SCIPintervalSetBounds(&interval, lb, ub); 5180 5181 return interval; 5182 } 5183 5184 /** removes constraints that are always feasible or very simple 5185 * 5186 * Checks whether the activity of constraint functions is a subset of the constraint sides (relaxed by feastol). 5187 * To compute the activity, we use forwardPropExpr(), but relax variable bounds by feastol, because solutions to be checked 5188 * might violate variable bounds by up to feastol, too. 5189 * This is the main reason why the redundancy check is not done in propConss(), which relaxes variable bounds by epsilon only. 5190 * 5191 * Also removes constraints of the form lhs ≤ variable ≤ rhs. 5192 * 5193 * @todo it would be sufficient to check constraints for which we know that they are not currently violated by a valid solution 5194 * 5195 * @note This could should not run during solving, because the forwardProp takes the bounds of auxiliary variables into account. 5196 * For the root expression, these bounds are already set to the constraint sides, so that the activity of every expression 5197 * would appear as if the constraint is redundant. 5198 */ 5199 static 5200 SCIP_RETCODE presolveRedundantConss( 5201 SCIP* scip, /**< SCIP data structure */ 5202 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 5203 SCIP_CONS** conss, /**< constraints to propagate */ 5204 int nconss, /**< total number of constraints */ 5205 SCIP_Bool* cutoff, /**< pointer to store whether infeasibility has been identified */ 5206 int* ndelconss, /**< buffer to add the number of deleted constraints */ 5207 int* nchgbds /**< buffer to add the number of variable bound tightenings */ 5208 ) 5209 { 5210 SCIP_CONSHDLRDATA* conshdlrdata; 5211 SCIP_CONSDATA* consdata; 5212 SCIP_INTERVAL activity; 5213 SCIP_INTERVAL sides; 5214 int i; 5215 5216 assert(scip != NULL); 5217 assert(conshdlr != NULL); 5218 assert(conss != NULL); 5219 assert(nconss >= 0); 5220 assert(cutoff != NULL); 5221 assert(ndelconss != NULL); 5222 assert(nchgbds != NULL); 5223 5224 /* no constraints to check */ 5225 if( nconss == 0 ) 5226 return SCIP_OKAY; 5227 5228 conshdlrdata = SCIPconshdlrGetData(conshdlr); 5229 assert(conshdlrdata != NULL); 5230 5231 /* increase curboundstag and set lastvaractivitymethodchange 5232 * we do this here to trigger a reevaluation of all variable bounds, since we will relax variable bounds 5233 * for the redundancy check differently than for domain propagation 5234 * we also update lastboundrelax to ensure activites of all expressions are indeed reevaluated 5235 */ 5236 ++conshdlrdata->curboundstag; 5237 assert(conshdlrdata->curboundstag > 0); 5238 conshdlrdata->lastvaractivitymethodchange = conshdlrdata->curboundstag; 5239 conshdlrdata->lastboundrelax = conshdlrdata->curboundstag; 5240 conshdlrdata->intevalvar = intEvalVarRedundancyCheck; 5241 5242 SCIPdebugMsg(scip, "checking %d constraints for redundancy\n", nconss); 5243 5244 *cutoff = FALSE; 5245 for( i = 0; i < nconss; ++i ) 5246 { 5247 if( !SCIPconsIsActive(conss[i]) || SCIPconsIsDeleted(conss[i]) ) 5248 continue; 5249 5250 consdata = SCIPconsGetData(conss[i]); 5251 assert(consdata != NULL); 5252 5253 /* handle constant expressions separately: either the problem is infeasible or the constraint is redundant */ 5254 if( SCIPisExprValue(scip, consdata->expr) ) 5255 { 5256 SCIP_Real value = SCIPgetValueExprValue(consdata->expr); 5257 5258 if( (!SCIPisInfinity(scip, -consdata->lhs) && value < consdata->lhs - SCIPfeastol(scip)) || 5259 (!SCIPisInfinity(scip, consdata->rhs) && value > consdata->rhs + SCIPfeastol(scip)) ) 5260 { 5261 SCIPdebugMsg(scip, "constant constraint <%s> is infeasible: %g in [%g,%g] ", SCIPconsGetName(conss[i]), value, consdata->lhs, consdata->rhs); 5262 *cutoff = TRUE; 5263 5264 goto TERMINATE; 5265 } 5266 5267 SCIPdebugMsg(scip, "constant constraint <%s> is redundant: %g in [%g,%g] ", SCIPconsGetName(conss[i]), value, consdata->lhs, consdata->rhs); 5268 5269 SCIP_CALL( SCIPdelConsLocal(scip, conss[i]) ); 5270 ++*ndelconss; 5271 5272 continue; 5273 } 5274 5275 /* handle variable expressions separately: tighten variable bounds to constraint sides, then remove constraint (now redundant) */ 5276 if( SCIPisExprVar(scip, consdata->expr) ) 5277 { 5278 SCIP_VAR* var; 5279 SCIP_Bool tightened; 5280 5281 var = SCIPgetVarExprVar(consdata->expr); 5282 assert(var != NULL); 5283 5284 SCIPdebugMsg(scip, "variable constraint <%s> can be made redundant: <%s>[%g,%g] in [%g,%g]\n", SCIPconsGetName(conss[i]), SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var), consdata->lhs, consdata->rhs); 5285 5286 /* ensure that variable bounds are within constraint sides */ 5287 if( !SCIPisInfinity(scip, -consdata->lhs) ) 5288 { 5289 SCIP_CALL( SCIPtightenVarLb(scip, var, consdata->lhs, TRUE, cutoff, &tightened) ); 5290 5291 if( tightened ) 5292 ++*nchgbds; 5293 5294 if( *cutoff ) 5295 goto TERMINATE; 5296 } 5297 5298 if( !SCIPisInfinity(scip, consdata->rhs) ) 5299 { 5300 SCIP_CALL( SCIPtightenVarUb(scip, var, consdata->rhs, TRUE, cutoff, &tightened) ); 5301 5302 if( tightened ) 5303 ++*nchgbds; 5304 5305 if( *cutoff ) 5306 goto TERMINATE; 5307 } 5308 5309 /* delete the (now) redundant constraint locally */ 5310 SCIP_CALL( SCIPdelConsLocal(scip, conss[i]) ); 5311 ++*ndelconss; 5312 5313 continue; 5314 } 5315 5316 /* reevaluate expression activity, now using intEvalVarRedundancyCheck 5317 * we relax variable bounds by feastol here, as solutions that are checked later can also violate 5318 * variable bounds by up to feastol 5319 * (relaxing fixed variables seems to be too much, but they would be removed by presolve soon anyway) 5320 */ 5321 SCIPdebugMsg(scip, "call forwardPropExpr() for constraint <%s>: ", SCIPconsGetName(conss[i])); 5322 SCIPdebugPrintCons(scip, conss[i], NULL); 5323 5324 SCIP_CALL( forwardPropExpr(scip, conshdlr, consdata->expr, FALSE, cutoff, NULL) ); 5325 assert(*cutoff || !SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(consdata->expr))); 5326 5327 /* it is unlikely that we detect infeasibility by doing forward propagation */ 5328 if( *cutoff ) 5329 { 5330 SCIPdebugMsg(scip, " -> cutoff\n"); 5331 goto TERMINATE; 5332 } 5333 5334 assert(SCIPexprGetActivityTag(consdata->expr) == conshdlrdata->curboundstag); 5335 activity = SCIPexprGetActivity(consdata->expr); 5336 5337 /* relax sides by feastol 5338 * we could accept every solution that violates constraints up to feastol as redundant, so this is the most permissive we can be 5339 */ 5340 SCIPintervalSetBounds(&sides, 5341 SCIPisInfinity(scip, -consdata->lhs) ? -SCIP_INTERVAL_INFINITY : consdata->lhs - SCIPfeastol(scip), 5342 SCIPisInfinity(scip, consdata->rhs) ? SCIP_INTERVAL_INFINITY : consdata->rhs + SCIPfeastol(scip)); 5343 5344 if( SCIPintervalIsSubsetEQ(SCIP_INTERVAL_INFINITY, activity, sides) ) 5345 { 5346 SCIPdebugMsg(scip, " -> redundant: activity [%g,%g] within sides [%g,%g]\n", activity.inf, activity.sup, consdata->lhs, consdata->rhs); 5347 5348 SCIP_CALL( SCIPdelConsLocal(scip, conss[i]) ); 5349 ++*ndelconss; 5350 5351 continue; 5352 } 5353 5354 SCIPdebugMsg(scip, " -> not redundant: activity [%g,%g] not within sides [%g,%g]\n", activity.inf, activity.sup, consdata->lhs, consdata->rhs); 5355 } 5356 5357 TERMINATE: 5358 /* make sure all activities are reevaluated again, since we relaxed bounds in a different way */ 5359 ++conshdlrdata->curboundstag; 5360 conshdlrdata->lastvaractivitymethodchange = conshdlrdata->curboundstag; 5361 conshdlrdata->lastboundrelax = conshdlrdata->curboundstag; 5362 conshdlrdata->intevalvar = intEvalVarBoundTightening; 5363 5364 return SCIP_OKAY; 5365 } 5366 5367 /** tries to automatically convert a nonlinear constraint into a more specific and more specialized constraint */ 5368 static 5369 SCIP_RETCODE presolveUpgrade( 5370 SCIP* scip, /**< SCIP data structure */ 5371 SCIP_CONSHDLR* conshdlr, /**< constraint handler data structure */ 5372 SCIP_CONS* cons, /**< source constraint to try to convert */ 5373 SCIP_Bool* upgraded, /**< buffer to store whether constraint was upgraded */ 5374 int* nupgdconss, /**< buffer to increase if constraint was upgraded */ 5375 int* naddconss /**< buffer to increase with number of additional constraints created during upgrade */ 5376 ) 5377 { 5378 SCIP_CONSHDLRDATA* conshdlrdata; 5379 SCIP_CONSDATA* consdata; 5380 SCIP_CONS** upgdconss; 5381 int upgdconsssize; 5382 int nupgdconss_; 5383 int i; 5384 5385 assert(scip != NULL); 5386 assert(conshdlr != NULL); 5387 assert(cons != NULL); 5388 assert(!SCIPconsIsModifiable(cons)); 5389 assert(upgraded != NULL); 5390 assert(nupgdconss != NULL); 5391 assert(naddconss != NULL); 5392 5393 *upgraded = FALSE; 5394 5395 nupgdconss_ = 0; 5396 5397 conshdlrdata = SCIPconshdlrGetData(conshdlr); 5398 assert(conshdlrdata != NULL); 5399 5400 /* if there are no upgrade methods, we can stop */ 5401 if( conshdlrdata->nconsupgrades == 0 ) 5402 return SCIP_OKAY; 5403 5404 upgdconsssize = 2; 5405 SCIP_CALL( SCIPallocBufferArray(scip, &upgdconss, upgdconsssize) ); 5406 5407 /* call the upgrading methods */ 5408 SCIPdebugMsg(scip, "upgrading nonlinear constraint <%s> (up to %d upgrade methods): ", SCIPconsGetName(cons), conshdlrdata->nconsupgrades); 5409 SCIPdebugPrintCons(scip, cons, NULL); 5410 5411 consdata = SCIPconsGetData(cons); 5412 assert(consdata != NULL); 5413 5414 /* try all upgrading methods in priority order in case the upgrading step is enable */ 5415 for( i = 0; i < conshdlrdata->nconsupgrades; ++i ) 5416 { 5417 if( !conshdlrdata->consupgrades[i]->active ) 5418 continue; 5419 5420 assert(conshdlrdata->consupgrades[i]->consupgd != NULL); 5421 5422 SCIP_CALL( conshdlrdata->consupgrades[i]->consupgd(scip, cons, consdata->nvarexprs, &nupgdconss_, upgdconss, upgdconsssize) ); 5423 5424 while( nupgdconss_ < 0 ) 5425 { 5426 /* upgrade function requires more memory: resize upgdconss and call again */ 5427 assert(-nupgdconss_ > upgdconsssize); 5428 upgdconsssize = -nupgdconss_; 5429 SCIP_CALL( SCIPreallocBufferArray(scip, &upgdconss, -nupgdconss_) ); 5430 5431 SCIP_CALL( conshdlrdata->consupgrades[i]->consupgd(scip, cons, consdata->nvarexprs, &nupgdconss_, upgdconss, upgdconsssize) ); 5432 5433 assert(nupgdconss_ != 0); 5434 } 5435 5436 if( nupgdconss_ > 0 ) 5437 { 5438 /* got upgrade */ 5439 int j; 5440 5441 SCIPdebugMsg(scip, " -> upgraded to %d constraints:\n", nupgdconss_); 5442 5443 /* add the upgraded constraints to the problem and forget them */ 5444 for( j = 0; j < nupgdconss_; ++j ) 5445 { 5446 SCIPdebugMsgPrint(scip, "\t"); 5447 SCIPdebugPrintCons(scip, upgdconss[j], NULL); 5448 5449 SCIP_CALL( SCIPaddCons(scip, upgdconss[j]) ); /*lint !e613*/ 5450 SCIP_CALL( SCIPreleaseCons(scip, &upgdconss[j]) ); /*lint !e613*/ 5451 } 5452 5453 /* count the first upgrade constraint as constraint upgrade and the remaining ones as added constraints */ 5454 *nupgdconss += 1; 5455 *naddconss += nupgdconss_ - 1; 5456 *upgraded = TRUE; 5457 5458 /* delete upgraded constraint */ 5459 SCIPdebugMsg(scip, "delete constraint <%s> after upgrade\n", SCIPconsGetName(cons)); 5460 SCIP_CALL( SCIPdelCons(scip, cons) ); 5461 5462 break; 5463 } 5464 } 5465 5466 SCIPfreeBufferArray(scip, &upgdconss); 5467 5468 return SCIP_OKAY; 5469 } 5470 5471 /** returns whether the variable of a given variable expression is a candidate for presolveSingleLockedVars(), i.e., 5472 * the variable is only contained in a single nonlinear constraint, has no objective coefficient, has finite 5473 * variable bounds, and is not binary 5474 */ 5475 static 5476 SCIP_Bool isSingleLockedCand( 5477 SCIP* scip, /**< SCIP data structure */ 5478 SCIP_EXPR* expr /**< variable expression */ 5479 ) 5480 { 5481 SCIP_VAR* var; 5482 SCIP_EXPR_OWNERDATA* ownerdata; 5483 5484 assert(SCIPisExprVar(scip, expr)); 5485 5486 var = SCIPgetVarExprVar(expr); 5487 assert(var != NULL); 5488 5489 ownerdata = SCIPexprGetOwnerData(expr); 5490 assert(ownerdata != NULL); 5491 5492 return SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL) == ownerdata->nlocksneg 5493 && SCIPvarGetNLocksUpType(var, SCIP_LOCKTYPE_MODEL) == ownerdata->nlockspos 5494 && ownerdata->nconss == 1 && SCIPisZero(scip, SCIPvarGetObj(var)) 5495 && !SCIPisInfinity(scip, -SCIPvarGetLbGlobal(var)) && !SCIPisInfinity(scip, SCIPvarGetUbGlobal(var)) 5496 && SCIPvarGetType(var) != SCIP_VARTYPE_BINARY 5497 && !SCIPisEQ(scip, SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var)); 5498 } 5499 5500 /** removes all variable expressions that are contained in a given expression from a hash map */ 5501 static 5502 SCIP_RETCODE removeSingleLockedVars( 5503 SCIP* scip, /**< SCIP data structure */ 5504 SCIP_EXPR* expr, /**< expression */ 5505 SCIP_EXPRITER* it, /**< expression iterator */ 5506 SCIP_HASHMAP* exprcands /**< map to hash variable expressions */ 5507 ) 5508 { 5509 SCIP_EXPR* e; 5510 5511 for( e = SCIPexpriterRestartDFS(it, expr); !SCIPexpriterIsEnd(it); e = SCIPexpriterGetNext(it) ) 5512 { 5513 if( SCIPisExprVar(scip, e) && SCIPhashmapExists(exprcands, (void*)e) ) 5514 { 5515 SCIP_CALL( SCIPhashmapRemove(exprcands, (void*)e) ); 5516 } 5517 } 5518 5519 return SCIP_OKAY; 5520 } 5521 5522 /** presolving method to fix a variable \f$x_i\f$ to one of its bounds if the variable is only contained in a single 5523 * nonlinear constraint g(x) ≤ rhs (≥ lhs) if g() is concave (convex) in \f$x_i\f$ 5524 * 5525 * If a continuous variable has bounds [0,1], then the variable type is changed to be binary. 5526 * Otherwise, a bound disjunction constraint is added. 5527 * 5528 * @todo the same reduction can be applied if g(x) is not concave, but monotone in \f$x_i\f$ for g(x) ≤ rhs 5529 * @todo extend this to cases where a variable can appear in a monomial with an exponent, essentially relax 5530 * g(x) to \f$\sum_i [a_i,b_i] x^{p_i}\f$ for a single variable \f$x\f$ and try to conclude montonicity or convexity/concavity 5531 * on this (probably have one or two flags per variable and update this whenever another \f$x^{p_i}\f$ is found) 5532 */ 5533 static 5534 SCIP_RETCODE presolveSingleLockedVars( 5535 SCIP* scip, /**< SCIP data structure */ 5536 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */ 5537 SCIP_CONS* cons, /**< nonlinear constraint */ 5538 int* nchgvartypes, /**< pointer to store the total number of changed variable types */ 5539 int* naddconss, /**< pointer to store the total number of added constraints */ 5540 SCIP_Bool* infeasible /**< pointer to store whether problem is infeasible */ 5541 ) 5542 { 5543 SCIP_CONSHDLRDATA* conshdlrdata; 5544 SCIP_CONSDATA* consdata; 5545 SCIP_EXPR** singlelocked; 5546 SCIP_HASHMAP* exprcands; 5547 SCIP_Bool hasbounddisj; 5548 SCIP_Bool haslhs; 5549 SCIP_Bool hasrhs; 5550 int nsinglelocked = 0; 5551 int i; 5552 5553 assert(conshdlr != NULL); 5554 assert(cons != NULL); 5555 assert(nchgvartypes != NULL); 5556 assert(naddconss != NULL); 5557 assert(infeasible != NULL); 5558 5559 *nchgvartypes = 0; 5560 *naddconss = 0; 5561 *infeasible = FALSE; 5562 5563 conshdlrdata = SCIPconshdlrGetData(conshdlr); 5564 assert(conshdlrdata != NULL); 5565 consdata = SCIPconsGetData(cons); 5566 assert(consdata != NULL); 5567 5568 /* only consider constraints with one finite side */ 5569 if( !SCIPisInfinity(scip, -consdata->lhs) && !SCIPisInfinity(scip, consdata->rhs) ) 5570 return SCIP_OKAY; 5571 5572 /* only consider sum expressions */ 5573 if( !SCIPisExprSum(scip, consdata->expr) ) 5574 return SCIP_OKAY; 5575 5576 /* remember which side is finite */ 5577 haslhs = !SCIPisInfinity(scip, -consdata->lhs); 5578 hasrhs = !SCIPisInfinity(scip, consdata->rhs); 5579 5580 /* allocate memory */ 5581 SCIP_CALL( SCIPhashmapCreate(&exprcands, SCIPblkmem(scip), consdata->nvarexprs) ); 5582 SCIP_CALL( SCIPallocBufferArray(scip, &singlelocked, consdata->nvarexprs) ); 5583 5584 /* check all variable expressions for single locked variables */ 5585 for( i = 0; i < consdata->nvarexprs; ++i ) 5586 { 5587 assert(consdata->varexprs[i] != NULL); 5588 5589 if( isSingleLockedCand(scip, consdata->varexprs[i]) ) 5590 { 5591 SCIP_CALL( SCIPhashmapInsert(exprcands, (void*)consdata->varexprs[i], NULL) ); 5592 singlelocked[nsinglelocked++] = consdata->varexprs[i]; 5593 } 5594 } 5595 SCIPdebugMsg(scip, "found %d single locked variables for constraint %s\n", nsinglelocked, SCIPconsGetName(cons)); 5596 5597 if( nsinglelocked > 0 ) 5598 { 5599 SCIP_EXPR** children; 5600 SCIP_EXPRITER* it; 5601 int nchildren; 5602 5603 children = SCIPexprGetChildren(consdata->expr); 5604 nchildren = SCIPexprGetNChildren(consdata->expr); 5605 5606 /* create iterator */ 5607 SCIP_CALL( SCIPcreateExpriter(scip, &it) ); 5608 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) ); 5609 SCIPexpriterSetStagesDFS(it, SCIP_EXPRITER_ENTEREXPR); 5610 5611 for( i = 0; i < nchildren; ++i ) 5612 { 5613 SCIP_EXPR* child; 5614 SCIP_Real coef; 5615 5616 child = children[i]; 5617 assert(child != NULL); 5618 coef = SCIPgetCoefsExprSum(consdata->expr)[i]; 5619 5620 /* ignore linear terms */ 5621 if( SCIPisExprVar(scip, child) ) 5622 continue; 5623 5624 /* consider products prod_j f_j(x); ignore f_j(x) if it is a single variable, otherwise iterate through the 5625 * expression that represents f_j and remove each variable expression from exprcands 5626 */ 5627 else if( SCIPisExprProduct(scip, child) ) 5628 { 5629 int j; 5630 5631 for( j = 0; j < SCIPexprGetNChildren(child); ++j ) 5632 { 5633 SCIP_EXPR* grandchild = SCIPexprGetChildren(child)[j]; 5634 5635 if( !SCIPisExprVar(scip, grandchild) ) 5636 { 5637 /* mark all variable expressions that are contained in the expression */ 5638 SCIP_CALL( removeSingleLockedVars(scip, grandchild, it, exprcands) ); 5639 } 5640 } 5641 } 5642 /* fixing a variable x to one of its bounds is only valid for ... +x^p >= lhs or ... -x^p <= rhs if p = 2k 5643 * for an integer k >= 1 5644 */ 5645 else if( SCIPisExprPower(scip, child) ) 5646 { 5647 SCIP_EXPR* grandchild = SCIPexprGetChildren(child)[0]; 5648 SCIP_Real exponent = SCIPgetExponentExprPow(child); 5649 SCIP_Bool valid; 5650 5651 /* check for even integral exponent */ 5652 valid = exponent > 1.0 && fmod(exponent, 2.0) == 0.0; 5653 5654 if( !valid || !SCIPisExprVar(scip, grandchild) || (hasrhs && coef > 0.0) || (haslhs && coef < 0.0) ) 5655 { 5656 /* mark all variable expressions that are contained in the expression */ 5657 SCIP_CALL( removeSingleLockedVars(scip, grandchild, it, exprcands) ); 5658 } 5659 } 5660 /* all other cases cannot be handled */ 5661 else 5662 { 5663 /* mark all variable expressions that are contained in the expression */ 5664 SCIP_CALL( removeSingleLockedVars(scip, child, it, exprcands) ); 5665 } 5666 } 5667 5668 /* free expression iterator */ 5669 SCIPfreeExpriter(&it); 5670 } 5671 5672 /* check whether the bound disjunction constraint handler is available */ 5673 hasbounddisj = SCIPfindConshdlr(scip, "bounddisjunction") != NULL; 5674 5675 /* fix variable to one of its bounds by either changing its variable type or adding a disjunction constraint */ 5676 for( i = 0; i < nsinglelocked; ++i ) 5677 { 5678 /* only consider expressions that are still contained in the exprcands map */ 5679 if( SCIPhashmapExists(exprcands, (void*)singlelocked[i]) ) 5680 { 5681 SCIP_CONS* newcons; 5682 SCIP_VAR* vars[2]; 5683 SCIP_BOUNDTYPE boundtypes[2]; 5684 SCIP_Real bounds[2]; 5685 char name[SCIP_MAXSTRLEN]; 5686 SCIP_VAR* var; 5687 5688 var = SCIPgetVarExprVar(singlelocked[i]); 5689 assert(var != NULL); 5690 SCIPdebugMsg(scip, "found single locked variable %s in [%g,%g] that can be fixed to one of its bounds\n", 5691 SCIPvarGetName(var), SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var)); 5692 5693 /* try to change the variable type to binary */ 5694 if( conshdlrdata->checkvarlocks == 't' && SCIPisEQ(scip, SCIPvarGetLbGlobal(var), 0.0) && SCIPisEQ(scip, SCIPvarGetUbGlobal(var), 1.0) ) 5695 { 5696 assert(SCIPvarGetType(var) != SCIP_VARTYPE_BINARY); 5697 SCIP_CALL( SCIPchgVarType(scip, var, SCIP_VARTYPE_BINARY, infeasible) ); 5698 ++(*nchgvartypes); 5699 5700 if( *infeasible ) 5701 { 5702 SCIPdebugMsg(scip, "detect infeasibility after changing variable type of <%s>\n", SCIPvarGetName(var)); 5703 break; 5704 } 5705 } 5706 /* add bound disjunction constraint if bounds of the variable are finite */ 5707 else if( hasbounddisj && !SCIPisInfinity(scip, -SCIPvarGetLbGlobal(var)) && !SCIPisInfinity(scip, SCIPvarGetUbGlobal(var)) ) 5708 { 5709 vars[0] = var; 5710 vars[1] = var; 5711 boundtypes[0] = SCIP_BOUNDTYPE_LOWER; 5712 boundtypes[1] = SCIP_BOUNDTYPE_UPPER; 5713 bounds[0] = SCIPvarGetUbGlobal(var); 5714 bounds[1] = SCIPvarGetLbGlobal(var); 5715 5716 SCIPdebugMsg(scip, "add bound disjunction constraint for %s\n", SCIPvarGetName(var)); 5717 5718 /* create, add, and release bound disjunction constraint */ 5719 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "quadvarbnddisj_%s", SCIPvarGetName(var)); 5720 SCIP_CALL( SCIPcreateConsBounddisjunction(scip, &newcons, name, 2, vars, boundtypes, bounds, TRUE, TRUE, 5721 TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) ); 5722 SCIP_CALL( SCIPaddCons(scip, newcons) ); 5723 SCIP_CALL( SCIPreleaseCons(scip, &newcons) ); 5724 ++(*naddconss); 5725 } 5726 } 5727 } 5728 5729 /* free memory */ 5730 SCIPfreeBufferArray(scip, &singlelocked); 5731 SCIPhashmapFree(&exprcands); 5732 5733 return SCIP_OKAY; 5734 } 5735 5736 /** presolving method to check if there is a single linear continuous variable that can be made implicit integer */ 5737 static 5738 SCIP_RETCODE presolveImplint( 5739 SCIP* scip, /**< SCIP data structure */ 5740 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 5741 SCIP_CONS** conss, /**< nonlinear constraints */ 5742 int nconss, /**< total number of nonlinear constraints */ 5743 int* nchgvartypes, /**< pointer to update the total number of changed variable types */ 5744 SCIP_Bool* infeasible /**< pointer to store whether problem is infeasible */ 5745 ) 5746 { 5747 int c; 5748 5749 assert(scip != NULL); 5750 assert(conshdlr != NULL); 5751 assert(conss != NULL || nconss == 0); 5752 assert(nchgvartypes != NULL); 5753 assert(infeasible != NULL); 5754 5755 *infeasible = FALSE; 5756 5757 /* nothing can be done if there are no binary and integer variables available */ 5758 if( SCIPgetNBinVars(scip) == 0 && SCIPgetNIntVars(scip) == 0 ) 5759 return SCIP_OKAY; 5760 5761 /* no continuous var can be made implicit-integer if there are no continuous variables */ 5762 if( SCIPgetNContVars(scip) == 0 ) 5763 return SCIP_OKAY; 5764 5765 for( c = 0; c < nconss; ++c ) 5766 { 5767 SCIP_CONSDATA* consdata; 5768 SCIP_EXPR** children; 5769 int nchildren; 5770 SCIP_Real* coefs; 5771 SCIP_EXPR* cand = NULL; 5772 SCIP_Real candcoef = 0.0; 5773 int i; 5774 5775 assert(conss != NULL && conss[c] != NULL); 5776 5777 consdata = SCIPconsGetData(conss[c]); 5778 assert(consdata != NULL); 5779 5780 /* the constraint must be an equality constraint */ 5781 if( !SCIPisEQ(scip, consdata->lhs, consdata->rhs) ) 5782 continue; 5783 5784 /* the root expression needs to be a sum expression */ 5785 if( !SCIPisExprSum(scip, consdata->expr) ) 5786 continue; 5787 5788 children = SCIPexprGetChildren(consdata->expr); 5789 nchildren = SCIPexprGetNChildren(consdata->expr); 5790 5791 /* the sum expression must have at least two children 5792 * (with one child, we would look for a coef*x = constant, which is presolved away anyway) 5793 */ 5794 if( nchildren <= 1 ) 5795 continue; 5796 5797 coefs = SCIPgetCoefsExprSum(consdata->expr); 5798 5799 /* find first continuous variable and get value of its coefficient */ 5800 for( i = 0; i < nchildren; ++i ) 5801 { 5802 if( !SCIPisExprVar(scip, children[i]) || SCIPvarIsIntegral(SCIPgetVarExprVar(children[i])) ) 5803 continue; 5804 5805 candcoef = coefs[i]; 5806 assert(candcoef != 0.0); 5807 5808 /* lhs/rhs - constant divided by candcoef must be integral 5809 * if not, break with cand == NULL, so give up 5810 */ 5811 if( SCIPisIntegral(scip, (consdata->lhs - SCIPgetConstantExprSum(consdata->expr)) / candcoef) ) 5812 cand = children[i]; 5813 5814 break; 5815 } 5816 5817 /* no suitable continuous variable found */ 5818 if( cand == NULL ) 5819 continue; 5820 5821 /* check whether all other coefficients are integral when diving by candcoef and all other children are integral */ 5822 for( i = 0; i < nchildren; ++i ) 5823 { 5824 if( children[i] == cand ) 5825 continue; 5826 5827 /* child i must be integral */ 5828 if( !SCIPexprIsIntegral(children[i]) ) 5829 { 5830 cand = NULL; 5831 break; 5832 } 5833 5834 /* coefficient of child i must be integral if diving by candcoef */ 5835 if( !SCIPisIntegral(scip, coefs[i] / candcoef) ) /*lint !e414*/ 5836 { 5837 cand = NULL; 5838 break; 5839 } 5840 } 5841 5842 if( cand == NULL ) 5843 continue; 5844 5845 SCIPdebugMsg(scip, "make variable <%s> implicit integer due to constraint <%s>\n", 5846 SCIPvarGetName(SCIPgetVarExprVar(cand)), SCIPconsGetName(conss[c])); 5847 5848 /* change variable type */ 5849 SCIP_CALL( SCIPchgVarType(scip, SCIPgetVarExprVar(cand), SCIP_VARTYPE_IMPLINT, infeasible) ); 5850 5851 if( *infeasible ) 5852 return SCIP_OKAY; 5853 5854 /* mark expression as being integral (as would be done by expr_var.c in the next round of updating integrality info) */ 5855 SCIPexprSetIntegrality(cand, TRUE); 5856 } 5857 5858 return SCIP_OKAY; 5859 } 5860 5861 /** creates auxiliary variable for a given expression 5862 * 5863 * @note for a variable expression it does nothing 5864 * @note this function can only be called in stage SCIP_STAGE_SOLVING 5865 */ 5866 static 5867 SCIP_RETCODE createAuxVar( 5868 SCIP* scip, /**< SCIP data structure */ 5869 SCIP_EXPR* expr /**< expression */ 5870 ) 5871 { 5872 SCIP_EXPR_OWNERDATA* ownerdata; 5873 SCIP_CONSHDLRDATA* conshdlrdata; 5874 SCIP_VARTYPE vartype; 5875 SCIP_INTERVAL activity; 5876 char name[SCIP_MAXSTRLEN]; 5877 5878 assert(scip != NULL); 5879 assert(expr != NULL); 5880 5881 ownerdata = SCIPexprGetOwnerData(expr); 5882 assert(ownerdata != NULL); 5883 assert(ownerdata->nauxvaruses > 0); 5884 5885 /* if we already have auxvar, then do nothing */ 5886 if( ownerdata->auxvar != NULL ) 5887 return SCIP_OKAY; 5888 5889 /* if expression is a variable-expression, then do nothing */ 5890 if( SCIPisExprVar(scip, expr) ) 5891 return SCIP_OKAY; 5892 5893 if( SCIPgetStage(scip) != SCIP_STAGE_SOLVING ) 5894 { 5895 SCIPerrorMessage("it is not possible to create auxiliary variables during stage=%d\n", SCIPgetStage(scip)); 5896 return SCIP_INVALIDCALL; 5897 } 5898 5899 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr); 5900 assert(conshdlrdata != NULL); 5901 assert(conshdlrdata->auxvarid >= 0); 5902 5903 /* it doesn't harm much to have an auxvar for a constant, as this can be handled well by the default hdlr, 5904 * but it usually indicates a missing simplify 5905 * if we find situations where we need to have an auxvar for a constant, then remove this assert 5906 */ 5907 assert(!SCIPisExprValue(scip, expr)); 5908 5909 /* create and capture auxiliary variable */ 5910 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "auxvar_%s_%d", SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)), conshdlrdata->auxvarid); 5911 ++conshdlrdata->auxvarid; 5912 5913 /* type of auxiliary variable depends on integrality information of the expression */ 5914 vartype = SCIPexprIsIntegral(expr) ? SCIP_VARTYPE_IMPLINT : SCIP_VARTYPE_CONTINUOUS; 5915 5916 /* get activity of expression to initialize variable bounds, if something valid is available (evalActivity was called in initSepa) */ 5917 if( SCIPexprGetActivityTag(expr) >= conshdlrdata->lastboundrelax ) 5918 { 5919 activity = SCIPexprGetActivity(expr); 5920 /* we cannot handle a domain error here at the moment, but it seems unlikely that it could occur 5921 * if it appear, then we could change code to handle this properly, but for now we just ensure that we continue correctly 5922 * and abort in debug mode only 5923 */ 5924 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, activity) ) 5925 { 5926 SCIPABORT(); 5927 SCIPintervalSetEntire(SCIP_INTERVAL_INFINITY, &activity); 5928 } 5929 } 5930 else 5931 SCIPintervalSetEntire(SCIP_INTERVAL_INFINITY, &activity); 5932 5933 /* if root node, then activity is globally valid, so use it to initialize the global bounds of the auxvar 5934 * otherwise, we create var without bounds here and use activity to set local bounds below (needs to be after adding var) 5935 */ 5936 if( SCIPgetDepth(scip) == 0 ) 5937 { 5938 SCIP_CALL( SCIPcreateVarBasic(scip, &ownerdata->auxvar, name, MAX(-SCIPinfinity(scip), activity.inf), MIN(SCIPinfinity(scip), activity.sup), 0.0, vartype) ); 5939 } 5940 else 5941 { 5942 SCIP_CALL( SCIPcreateVarBasic(scip, &ownerdata->auxvar, name, -SCIPinfinity(scip), SCIPinfinity(scip), 0.0, vartype) ); 5943 } 5944 5945 /* mark the auxiliary variable to be added for the relaxation only 5946 * this prevents SCIP to create linear constraints from cuts or conflicts that contain auxiliary variables, 5947 * or to copy the variable to a subscip 5948 */ 5949 SCIPvarMarkRelaxationOnly(ownerdata->auxvar); 5950 5951 SCIP_CALL( SCIPaddVar(scip, ownerdata->auxvar) ); 5952 5953 SCIPdebugMsg(scip, "added auxiliary variable <%s> [%g,%g] for expression %p\n", SCIPvarGetName(ownerdata->auxvar), SCIPvarGetLbGlobal(ownerdata->auxvar), SCIPvarGetUbGlobal(ownerdata->auxvar), (void*)expr); 5954 5955 /* add variable locks in both directions 5956 * TODO should be sufficient to lock only according to expr->nlockspos/neg, 5957 * but then we need to also update the auxvars locks when the expr locks change 5958 */ 5959 SCIP_CALL( SCIPaddVarLocks(scip, ownerdata->auxvar, 1, 1) ); 5960 5961 #ifdef WITH_DEBUG_SOLUTION 5962 if( SCIPdebugIsMainscip(scip) ) 5963 { 5964 /* store debug solution value of auxiliary variable 5965 * assumes that expression has been evaluated in debug solution before 5966 */ 5967 SCIP_CALL( SCIPdebugAddSolVal(scip, ownerdata->auxvar, SCIPexprGetEvalValue(expr)) ); 5968 } 5969 #endif 5970 5971 if( SCIPgetDepth(scip) > 0 ) 5972 { 5973 /* initialize local bounds to (locally valid) activity */ 5974 SCIP_Bool cutoff; 5975 SCIP_CALL( tightenAuxVarBounds(scip, ownerdata->conshdlr, expr, activity, &cutoff, NULL) ); 5976 assert(!cutoff); /* should not happen as activity wasn't empty and variable is new */ 5977 } 5978 5979 return SCIP_OKAY; 5980 } 5981 5982 /** initializes separation for constraint 5983 * 5984 * - ensures that activities are up to date in all expressions 5985 * - creates auxiliary variables where required 5986 * - calls propExprDomains() to possibly tighten auxvar bounds 5987 * - calls separation initialization callback of nlhdlrs 5988 */ 5989 static 5990 SCIP_RETCODE initSepa( 5991 SCIP* scip, /**< SCIP data structure */ 5992 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraints handler */ 5993 SCIP_CONS** conss, /**< constraints */ 5994 int nconss, /**< number of constraints */ 5995 SCIP_Bool* infeasible /**< pointer to store whether the problem is infeasible or not */ 5996 ) 5997 { 5998 SCIP_CONSDATA* consdata; 5999 SCIP_CONSHDLRDATA* conshdlrdata; 6000 SCIP_EXPRITER* it; 6001 SCIP_EXPR* expr; 6002 SCIP_RESULT result; 6003 SCIP_VAR* auxvar; 6004 int nreductions = 0; 6005 int c, e; 6006 6007 assert(scip != NULL); 6008 assert(conshdlr != NULL); 6009 assert(conss != NULL || nconss == 0); 6010 assert(nconss >= 0); 6011 assert(infeasible != NULL); 6012 6013 conshdlrdata = SCIPconshdlrGetData(conshdlr); 6014 assert(conshdlrdata != NULL); 6015 6016 /* start with new propbounds (just to be sure, should not be needed) */ 6017 ++conshdlrdata->curpropboundstag; 6018 6019 SCIP_CALL( SCIPcreateExpriter(scip, &it) ); 6020 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) ); 6021 6022 /* first ensure activities are up to date and create auxvars */ 6023 *infeasible = FALSE; 6024 for( c = 0; c < nconss; ++c ) 6025 { 6026 assert(conss != NULL); 6027 assert(conss[c] != NULL); 6028 6029 consdata = SCIPconsGetData(conss[c]); 6030 assert(consdata != NULL); 6031 assert(consdata->expr != NULL); 6032 6033 #ifdef WITH_DEBUG_SOLUTION 6034 if( SCIPdebugIsMainscip(scip) ) 6035 { 6036 SCIP_SOL* debugsol; 6037 6038 SCIP_CALL( SCIPdebugGetSol(scip, &debugsol) ); 6039 6040 if( debugsol != NULL ) /* it can be compiled WITH_DEBUG_SOLUTION, but still no solution given */ 6041 { 6042 /* evaluate expression in debug solution, so we can set the solution value of created auxiliary variables 6043 * in createAuxVar() 6044 */ 6045 SCIP_CALL( SCIPevalExpr(scip, consdata->expr, debugsol, 0) ); 6046 } 6047 } 6048 #endif 6049 6050 /* ensure we have a valid activity for auxvars and propExprDomains() call below */ 6051 SCIP_CALL( SCIPevalExprActivity(scip, consdata->expr) ); 6052 6053 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) ) 6054 { 6055 if( SCIPexprGetOwnerData(expr)->nauxvaruses > 0 ) 6056 { 6057 SCIP_CALL( createAuxVar(scip, expr) ); 6058 } 6059 } 6060 6061 auxvar = SCIPexprGetOwnerData(consdata->expr)->auxvar; 6062 if( auxvar != NULL ) 6063 { 6064 SCIPdebugMsg(scip, "tighten auxvar <%s> bounds using constraint sides [%g,%g]\n", 6065 SCIPvarGetName(auxvar), consdata->lhs, consdata->rhs); 6066 /* change the bounds of the auxiliary variable of the root node to [lhs,rhs] */ 6067 SCIP_CALL( SCIPtightenVarLb(scip, auxvar, consdata->lhs, TRUE, infeasible, NULL) ); 6068 if( *infeasible ) 6069 { 6070 SCIPdebugMsg(scip, "infeasibility detected while tightening auxvar lb (%g) using lhs of constraint (%g)\n", SCIPvarGetLbLocal(auxvar), consdata->lhs); 6071 break; 6072 } 6073 6074 SCIP_CALL( SCIPtightenVarUb(scip, auxvar, consdata->rhs, TRUE, infeasible, NULL) ); 6075 if( *infeasible ) 6076 { 6077 SCIPdebugMsg(scip, "infeasibility detected while tightening auxvar ub (%g) using rhs of constraint (%g)\n", SCIPvarGetUbLocal(auxvar), consdata->rhs); 6078 break; 6079 } 6080 } 6081 } 6082 6083 /* now run a special version of reverseprop to ensure that important bound information (like function domains) is stored in bounds of auxvars, 6084 * since sometimes they cannot be recovered from activity evaluation even after some rounds of domain propagation 6085 * (e.g., log(x*y), which becomes log(w), w=x*y 6086 * log(w) implies w >= 0, but we may not be able to derive bounds on x and y such that w >= 0 is ensured) 6087 */ 6088 SCIP_CALL( propExprDomains(scip, conshdlr, conss, nconss, &result, &nreductions) ); 6089 if( result == SCIP_CUTOFF ) 6090 *infeasible = TRUE; 6091 6092 /* now call initsepa of nlhdlrs 6093 * TODO skip if !SCIPconsIsInitial(conss[c]) ? 6094 * but at the moment, initSepa() is called from INITLP anyway, so we have SCIPconsIsInitial(conss[c]) anyway 6095 */ 6096 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) ); 6097 for( c = 0; c < nconss && !*infeasible; ++c ) 6098 { 6099 assert(conss != NULL); 6100 assert(conss[c] != NULL); 6101 6102 consdata = SCIPconsGetData(conss[c]); 6103 assert(consdata != NULL); 6104 assert(consdata->expr != NULL); 6105 6106 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it) && !*infeasible; expr = SCIPexpriterGetNext(it) ) 6107 { 6108 SCIP_EXPR_OWNERDATA* ownerdata; 6109 6110 ownerdata = SCIPexprGetOwnerData(expr); 6111 assert(ownerdata != NULL); 6112 6113 if( ownerdata->nauxvaruses == 0 ) 6114 continue; 6115 6116 for( e = 0; e < ownerdata->nenfos; ++e ) 6117 { 6118 SCIP_NLHDLR* nlhdlr; 6119 SCIP_Bool underestimate; 6120 SCIP_Bool overestimate; 6121 assert(ownerdata->enfos[e] != NULL); 6122 6123 /* skip if initsepa was already called, e.g., because this expression is also part of a constraint 6124 * which participated in a previous initSepa() call 6125 */ 6126 if( ownerdata->enfos[e]->issepainit ) 6127 continue; 6128 6129 /* only call initsepa if it will actually separate */ 6130 if( (ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPABOTH) == 0 ) 6131 continue; 6132 6133 nlhdlr = ownerdata->enfos[e]->nlhdlr; 6134 assert(nlhdlr != NULL); 6135 6136 /* only init sepa if there is an initsepa callback */ 6137 if( !SCIPnlhdlrHasInitSepa(nlhdlr) ) 6138 continue; 6139 6140 /* check whether expression needs to be under- or overestimated */ 6141 overestimate = ownerdata->nlocksneg > 0; 6142 underestimate = ownerdata->nlockspos > 0; 6143 assert(underestimate || overestimate); 6144 6145 SCIPdebugMsg(scip, "initsepa under=%u over=%u for expression %p\n", underestimate, overestimate, (void*)expr); 6146 6147 /* call the separation initialization callback of the nonlinear handler */ 6148 SCIP_CALL( SCIPnlhdlrInitsepa(scip, conshdlr, conss[c], nlhdlr, expr, 6149 ownerdata->enfos[e]->nlhdlrexprdata, overestimate, underestimate, infeasible) ); 6150 ownerdata->enfos[e]->issepainit = TRUE; 6151 6152 if( *infeasible ) 6153 { 6154 /* stop everything if we detected infeasibility */ 6155 SCIPdebugMsg(scip, "detect infeasibility for constraint %s during initsepa()\n", SCIPconsGetName(conss[c])); 6156 break; 6157 } 6158 } 6159 } 6160 } 6161 6162 SCIPfreeExpriter(&it); 6163 6164 return SCIP_OKAY; 6165 } 6166 6167 /** returns whether we are ok to branch on auxiliary variables 6168 * 6169 * Currently returns whether depth of node in B&B tree is at least value of constraints/nonlinear/branching/aux parameter. 6170 */ 6171 static 6172 SCIP_Bool branchAuxNonlinear( 6173 SCIP* scip, /**< SCIP data structure */ 6174 SCIP_CONSHDLR* conshdlr /**< constraint handler */ 6175 ) 6176 { 6177 SCIP_CONSHDLRDATA* conshdlrdata; 6178 6179 assert(conshdlr != NULL); 6180 6181 conshdlrdata = SCIPconshdlrGetData(conshdlr); 6182 assert(conshdlrdata != NULL); 6183 6184 return conshdlrdata->branchauxmindepth <= SCIPgetDepth(scip); 6185 } 6186 6187 /** gets weight of variable when splitting violation score onto several variables in an expression */ 6188 static 6189 SCIP_Real getViolSplitWeight( 6190 SCIP* scip, /**< SCIP data structure */ 6191 SCIP_CONSHDLR* conshdlr, /**< expr constraint handler */ 6192 SCIP_VAR* var, /**< variable */ 6193 SCIP_SOL* sol /**< current solution */ 6194 ) 6195 { 6196 SCIP_CONSHDLRDATA* conshdlrdata; 6197 6198 conshdlrdata = SCIPconshdlrGetData(conshdlr); 6199 assert(conshdlrdata != NULL); 6200 6201 switch( conshdlrdata->branchviolsplit ) 6202 { 6203 case 'u' : /* uniform: everyone gets the same score */ 6204 return 1.0; 6205 6206 case 'm' : /* midness of solution: 0.5 if in middle of domain, 0.05 if close to lower or upper bound */ 6207 { 6208 SCIP_Real weight; 6209 weight = MIN(SCIPgetSolVal(scip, sol, var) - SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var) - SCIPgetSolVal(scip, sol, var)) / (SCIPvarGetUbLocal(var) - SCIPvarGetLbLocal(var)); 6210 return MAX(0.05, weight); 6211 } 6212 6213 case 'd' : /* domain width */ 6214 return SCIPvarGetUbLocal(var) - SCIPvarGetLbLocal(var); 6215 6216 case 'l' : /* logarithmic domain width: log-scale if width is below 0.1 or above 10, otherwise actual width */ 6217 { 6218 SCIP_Real width = SCIPvarGetUbLocal(var) - SCIPvarGetLbLocal(var); 6219 assert(width > 0.0); 6220 if( width > 10.0 ) 6221 return 10.0*log10(width); 6222 if( width < 0.1 ) 6223 return 0.1/(-log10(width)); 6224 return width; 6225 } 6226 6227 default : 6228 SCIPerrorMessage("invalid value for parameter constraints/expr/branching/violsplit"); 6229 SCIPABORT(); 6230 return SCIP_INVALID; 6231 } 6232 } 6233 6234 /** adds violation-branching score to a set of expressions, thereby distributing the score 6235 * 6236 * Each expression must either be a variable expression or have an aux-variable. 6237 * 6238 * If unbounded variables are present, each unbounded var gets an even score. 6239 * If no unbounded variables, then parameter constraints/nonlinear/branching/violsplit decides weight for each var. 6240 */ 6241 static 6242 void addExprsViolScore( 6243 SCIP* scip, /**< SCIP data structure */ 6244 SCIP_EXPR** exprs, /**< expressions where to add branching score */ 6245 int nexprs, /**< number of expressions */ 6246 SCIP_Real violscore, /**< violation-branching score to add to expression */ 6247 SCIP_SOL* sol, /**< current solution */ 6248 SCIP_Bool* success /**< buffer to store whether at least one violscore was added */ 6249 ) 6250 { 6251 SCIP_CONSHDLR* conshdlr; 6252 SCIP_VAR* var; 6253 SCIP_Real weight; 6254 SCIP_Real weightsum = 0.0; /* sum of weights over all candidates with bounded domain */ 6255 int nunbounded = 0; /* number of candidates with unbounded domain */ 6256 int i; 6257 6258 assert(exprs != NULL); 6259 assert(nexprs > 0); 6260 assert(success != NULL); 6261 6262 if( nexprs == 1 ) 6263 { 6264 SCIPaddExprViolScoreNonlinear(scip, exprs[0], violscore); 6265 SCIPdebugMsg(scip, "add score %g to <%s>[%g,%g]\n", violscore, 6266 SCIPvarGetName(SCIPgetExprAuxVarNonlinear(exprs[0])), SCIPvarGetLbLocal(SCIPgetExprAuxVarNonlinear(exprs[0])), SCIPvarGetUbLocal(SCIPgetExprAuxVarNonlinear(exprs[0]))); 6267 *success = TRUE; 6268 return; 6269 } 6270 6271 conshdlr = SCIPexprGetOwnerData(exprs[0])->conshdlr; 6272 6273 for( i = 0; i < nexprs; ++i ) 6274 { 6275 var = SCIPgetExprAuxVarNonlinear(exprs[i]); 6276 assert(var != NULL); 6277 6278 if( SCIPisInfinity(scip, -SCIPvarGetLbLocal(var)) || SCIPisInfinity(scip, SCIPvarGetUbLocal(var)) ) 6279 ++nunbounded; 6280 else if( !SCIPisEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) ) 6281 weightsum += getViolSplitWeight(scip, conshdlr, var, sol); 6282 } 6283 6284 *success = FALSE; 6285 for( i = 0; i < nexprs; ++i ) 6286 { 6287 var = SCIPgetExprAuxVarNonlinear(exprs[i]); 6288 assert(var != NULL); 6289 6290 if( nunbounded > 0 ) 6291 { 6292 if( SCIPisInfinity(scip, -SCIPvarGetLbLocal(var)) || SCIPisInfinity(scip, SCIPvarGetUbLocal(var)) ) 6293 { 6294 SCIPaddExprViolScoreNonlinear(scip, exprs[i], violscore / nunbounded); 6295 SCIPdebugMsg(scip, "add score %g (%g%% of %g) to <%s>[%g,%g]\n", violscore / nunbounded, 6296 100.0/nunbounded, violscore, 6297 SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)); 6298 *success = TRUE; 6299 } 6300 } 6301 else if( !SCIPisEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) ) 6302 { 6303 assert(weightsum > 0.0); 6304 6305 weight = getViolSplitWeight(scip, conshdlr, var, sol); 6306 SCIPaddExprViolScoreNonlinear(scip, exprs[i], violscore * weight / weightsum); 6307 SCIPdebugMsg(scip, "add score %g (%g%% of %g) to <%s>[%g,%g]\n", violscore * weight / weightsum, 6308 100*weight / weightsum, violscore, 6309 SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)); 6310 *success = TRUE; 6311 } 6312 else 6313 { 6314 SCIPdebugMsg(scip, "skip score for fixed variable <%s>[%g,%g]\n", 6315 SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)); 6316 } 6317 } 6318 } 6319 6320 /** adds violation-branching score to children of expression for given auxiliary variables 6321 * 6322 * Iterates over the successors of `expr` to find expressions that are associated with one of the given auxiliary variables. 6323 * Adds violation-branching scores to all found exprs by means of SCIPaddExprsViolScoreNonlinear(). 6324 * 6325 * @note This method may modify the given auxvars array by means of sorting. 6326 */ 6327 static 6328 SCIP_RETCODE addExprViolScoresAuxVars( 6329 SCIP* scip, /**< SCIP data structure */ 6330 SCIP_EXPR* expr, /**< expression where to start searching */ 6331 SCIP_Real violscore, /**< violation score to add to expression */ 6332 SCIP_VAR** auxvars, /**< auxiliary variables for which to find expression */ 6333 int nauxvars, /**< number of auxiliary variables */ 6334 SCIP_SOL* sol, /**< current solution (NULL for the LP solution) */ 6335 SCIP_Bool* success /**< buffer to store whether at least one violscore was added */ 6336 ) 6337 { 6338 SCIP_EXPRITER* it; 6339 SCIP_VAR* auxvar; 6340 SCIP_EXPR** exprs; 6341 int nexprs; 6342 int pos; 6343 6344 assert(scip != NULL); 6345 assert(expr != NULL); 6346 assert(auxvars != NULL); 6347 assert(success != NULL); 6348 6349 /* sort variables to make lookup below faster */ 6350 SCIPsortPtr((void**)auxvars, SCIPvarComp, nauxvars); 6351 6352 SCIP_CALL( SCIPcreateExpriter(scip, &it) ); 6353 SCIP_CALL( SCIPexpriterInit(it, expr, SCIP_EXPRITER_BFS, FALSE) ); 6354 6355 SCIP_CALL( SCIPallocBufferArray(scip, &exprs, nauxvars) ); 6356 nexprs = 0; 6357 6358 for( expr = SCIPexpriterGetNext(it); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) ) 6359 { 6360 auxvar = SCIPgetExprAuxVarNonlinear(expr); 6361 if( auxvar == NULL ) 6362 continue; 6363 6364 /* if auxvar of expr is contained in auxvars array, add branching score to expr */ 6365 if( SCIPsortedvecFindPtr((void**)auxvars, SCIPvarComp, auxvar, nauxvars, &pos) ) 6366 { 6367 assert(auxvars[pos] == auxvar); 6368 6369 SCIPdebugMsg(scip, "adding branchingscore for expr %p with auxvar <%s>\n", (void*)expr, SCIPvarGetName(auxvar)); 6370 exprs[nexprs++] = expr; 6371 6372 if( nexprs == nauxvars ) 6373 break; 6374 } 6375 } 6376 6377 SCIPfreeExpriter(&it); 6378 6379 if( nexprs > 0 ) 6380 { 6381 SCIP_CALL( SCIPaddExprsViolScoreNonlinear(scip, exprs, nexprs, violscore, sol, success) ); 6382 } 6383 else 6384 *success = FALSE; 6385 6386 SCIPfreeBufferArray(scip, &exprs); 6387 6388 return SCIP_OKAY; 6389 } 6390 6391 /** registers all unfixed variables in violated constraints as branching candidates */ 6392 static 6393 SCIP_RETCODE registerBranchingCandidatesAllUnfixed( 6394 SCIP* scip, /**< SCIP data structure */ 6395 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraints handler */ 6396 SCIP_CONS** conss, /**< constraints */ 6397 int nconss, /**< number of constraints */ 6398 int* nnotify /**< counter for number of notifications performed */ 6399 ) 6400 { 6401 SCIP_CONSDATA* consdata; 6402 SCIP_VAR* var; 6403 int c; 6404 int i; 6405 6406 assert(conshdlr != NULL); 6407 assert(conss != NULL || nconss == 0); 6408 assert(nnotify != NULL); 6409 6410 *nnotify = 0; 6411 6412 for( c = 0; c < nconss; ++c ) 6413 { 6414 assert(conss != NULL && conss[c] != NULL); 6415 6416 consdata = SCIPconsGetData(conss[c]); 6417 assert(consdata != NULL); 6418 6419 /* consider only violated constraints */ 6420 if( !isConsViolated(scip, conss[c]) ) 6421 continue; 6422 6423 /* register all variables that have not been fixed yet */ 6424 assert(consdata->varexprs != NULL); 6425 for( i = 0; i < consdata->nvarexprs; ++i ) 6426 { 6427 var = SCIPgetVarExprVar(consdata->varexprs[i]); 6428 assert(var != NULL); 6429 6430 if( !SCIPisEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) ) 6431 { 6432 SCIP_CALL( SCIPaddExternBranchCand(scip, var, getConsAbsViolation(conss[c]), SCIP_INVALID) ); 6433 ++(*nnotify); 6434 } 6435 } 6436 } 6437 6438 return SCIP_OKAY; 6439 } 6440 6441 /** registers all variables in violated constraints with branching scores as external branching candidates */ 6442 static 6443 SCIP_RETCODE registerBranchingCandidates( 6444 SCIP* scip, /**< SCIP data structure */ 6445 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraints handler */ 6446 SCIP_CONS** conss, /**< constraints */ 6447 int nconss, /**< number of constraints */ 6448 SCIP_Bool* success /**< buffer to store whether at least one branching candidate was added */ 6449 ) 6450 { 6451 SCIP_CONSDATA* consdata; 6452 SCIP_EXPRITER* it = NULL; 6453 int c; 6454 6455 assert(conshdlr != NULL); 6456 assert(success != NULL); 6457 6458 *success = FALSE; 6459 6460 if( branchAuxNonlinear(scip, conshdlr) ) 6461 { 6462 SCIP_CALL( SCIPcreateExpriter(scip, &it) ); 6463 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) ); 6464 } 6465 6466 /* register external branching candidates */ 6467 for( c = 0; c < nconss; ++c ) 6468 { 6469 assert(conss != NULL && conss[c] != NULL); 6470 6471 consdata = SCIPconsGetData(conss[c]); 6472 assert(consdata != NULL); 6473 assert(consdata->varexprs != NULL); 6474 6475 /* consider only violated constraints */ 6476 if( !isConsViolated(scip, conss[c]) ) 6477 continue; 6478 6479 if( !branchAuxNonlinear(scip, conshdlr) ) 6480 { 6481 int i; 6482 6483 /* if not branching on auxvars, then violation-branching scores will have been added to original variables 6484 * only, so we can loop over variable expressions 6485 */ 6486 for( i = 0; i < consdata->nvarexprs; ++i ) 6487 { 6488 SCIP_Real violscore; 6489 SCIP_Real lb; 6490 SCIP_Real ub; 6491 SCIP_VAR* var; 6492 6493 violscore = SCIPgetExprViolScoreNonlinear(consdata->varexprs[i]); 6494 6495 /* skip variable expressions that do not have a violation score */ 6496 if( violscore == 0.0 ) 6497 continue; 6498 6499 var = SCIPgetVarExprVar(consdata->varexprs[i]); 6500 assert(var != NULL); 6501 6502 lb = SCIPvarGetLbLocal(var); 6503 ub = SCIPvarGetUbLocal(var); 6504 6505 /* consider variable for branching if it has not been fixed yet */ 6506 if( !SCIPisEQ(scip, lb, ub) ) 6507 { 6508 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " add variable <%s>[%g,%g] as extern branching candidate with score %g\n", SCIPvarGetName(var), lb, ub, violscore); ) 6509 SCIP_CALL( SCIPaddExternBranchCand(scip, var, violscore, SCIP_INVALID) ); 6510 *success = TRUE; 6511 } 6512 else 6513 { 6514 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " skip fixed variable <%s>[%.15g,%.15g]\n", SCIPvarGetName(var), lb, ub); ) 6515 } 6516 6517 /* invalidate violscore-tag, so that we do not register variables that appear in multiple constraints 6518 * several times as external branching candidate, see SCIPgetExprViolScoreNonlinear() 6519 */ 6520 SCIPexprGetOwnerData(consdata->varexprs[i])->violscoretag = 0; 6521 } 6522 } 6523 else 6524 { 6525 SCIP_EXPR* expr; 6526 SCIP_VAR* var; 6527 SCIP_Real lb; 6528 SCIP_Real ub; 6529 SCIP_Real violscore; 6530 6531 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) ) 6532 { 6533 violscore = SCIPgetExprViolScoreNonlinear(expr); 6534 if( violscore == 0.0 ) 6535 continue; 6536 6537 /* if some nlhdlr added a branching score for this expression, then it considered this expression as a 6538 * variable, so this expression should either be an original variable or have an auxiliary variable 6539 */ 6540 var = SCIPgetExprAuxVarNonlinear(expr); 6541 assert(var != NULL); 6542 6543 lb = SCIPvarGetLbLocal(var); 6544 ub = SCIPvarGetUbLocal(var); 6545 6546 /* consider variable for branching if it has not been fixed yet */ 6547 if( !SCIPisEQ(scip, lb, ub) ) 6548 { 6549 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " add variable <%s>[%g,%g] as extern branching candidate with score %g\n", SCIPvarGetName(var), lb, ub, violscore); ) 6550 6551 SCIP_CALL( SCIPaddExternBranchCand(scip, var, violscore, SCIP_INVALID) ); 6552 *success = TRUE; 6553 } 6554 else 6555 { 6556 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " skip fixed variable <%s>[%.15g,%.15g]\n", SCIPvarGetName(var), lb, ub); ) 6557 } 6558 } 6559 } 6560 } 6561 6562 if( it != NULL ) 6563 SCIPfreeExpriter(&it); 6564 6565 return SCIP_OKAY; 6566 } 6567 6568 /** collect branching candidates from violated constraints 6569 * 6570 * Fills array with expressions that serve as branching candidates. 6571 * Collects those expressions that have a branching score assigned and stores the score in the auxviol field of the 6572 * branching candidate. 6573 * 6574 * If branching on aux-variables is allowed, then iterate through expressions of violated constraints, otherwise iterate 6575 * through variable-expressions only. 6576 */ 6577 static 6578 SCIP_RETCODE collectBranchingCandidates( 6579 SCIP* scip, /**< SCIP data structure */ 6580 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 6581 SCIP_CONS** conss, /**< constraints to process */ 6582 int nconss, /**< number of constraints */ 6583 SCIP_Real maxrelconsviol, /**< maximal scaled constraint violation */ 6584 SCIP_SOL* sol, /**< solution to enforce (NULL for the LP solution) */ 6585 SCIP_Longint soltag, /**< tag of solution */ 6586 BRANCHCAND* cands, /**< array where to store candidates, must be at least SCIPgetNVars() long */ 6587 int* ncands /**< number of candidates found */ 6588 ) 6589 { 6590 SCIP_CONSHDLRDATA* conshdlrdata; 6591 SCIP_CONSDATA* consdata; 6592 SCIP_EXPRITER* it = NULL; 6593 int c; 6594 int attempt; 6595 SCIP_VAR* var; 6596 6597 assert(scip != NULL); 6598 assert(conshdlr != NULL); 6599 assert(cands != NULL); 6600 assert(ncands != NULL); 6601 6602 conshdlrdata = SCIPconshdlrGetData(conshdlr); 6603 assert(conshdlrdata != NULL); 6604 6605 if( branchAuxNonlinear(scip, conshdlr) ) 6606 { 6607 SCIP_CALL( SCIPcreateExpriter(scip, &it) ); 6608 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) ); 6609 } 6610 6611 *ncands = 0; 6612 for( attempt = 0; attempt < 2; ++attempt ) 6613 { 6614 /* collect branching candidates from violated constraints 6615 * in the first attempt, consider only constraints with large violation 6616 * in the second attempt, consider all remaining violated constraints 6617 */ 6618 for( c = 0; c < nconss; ++c ) 6619 { 6620 SCIP_Real consviol; 6621 6622 assert(conss != NULL && conss[c] != NULL); 6623 6624 /* consider only violated constraints */ 6625 if( !isConsViolated(scip, conss[c]) ) 6626 continue; 6627 6628 consdata = SCIPconsGetData(conss[c]); 6629 assert(consdata != NULL); 6630 assert(consdata->varexprs != NULL); 6631 6632 SCIP_CALL( getConsRelViolation(scip, conss[c], &consviol, sol, soltag) ); 6633 6634 if( attempt == 0 && consviol < conshdlrdata->branchhighviolfactor * maxrelconsviol ) 6635 continue; 6636 else if( attempt == 1 && consviol >= conshdlrdata->branchhighviolfactor * maxrelconsviol ) 6637 continue; 6638 6639 if( !branchAuxNonlinear(scip, conshdlr) ) 6640 { 6641 int i; 6642 6643 /* if not branching on auxvars, then violation-branching scores will be available for original variables 6644 * only, so we can loop over variable expressions 6645 * unfortunately, we don't know anymore which constraint contributed the violation-branching score to the 6646 * variable, therefore we invalidate the score of a variable after processing it. 6647 */ 6648 for( i = 0; i < consdata->nvarexprs; ++i ) 6649 { 6650 SCIP_Real lb; 6651 SCIP_Real ub; 6652 6653 /* skip variable expressions that do not have a valid violation score */ 6654 if( conshdlrdata->enforound != SCIPexprGetOwnerData(consdata->varexprs[i])->violscoretag ) 6655 continue; 6656 6657 var = SCIPgetVarExprVar(consdata->varexprs[i]); 6658 assert(var != NULL); 6659 6660 lb = SCIPvarGetLbLocal(var); 6661 ub = SCIPvarGetUbLocal(var); 6662 6663 /* skip already fixed variable */ 6664 if( SCIPisEQ(scip, lb, ub) ) 6665 { 6666 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " skip fixed variable <%s>[%.15g,%.15g]\n", SCIPvarGetName(var), lb, ub); ) 6667 continue; 6668 } 6669 6670 assert(*ncands + 1 < SCIPgetNVars(scip)); 6671 cands[*ncands].expr = consdata->varexprs[i]; 6672 cands[*ncands].auxviol = SCIPgetExprViolScoreNonlinear(consdata->varexprs[i]); 6673 ++(*ncands); 6674 6675 /* invalidate violscore-tag, so that we do not register variables that appear in multiple constraints 6676 * several times as external branching candidate */ 6677 SCIPexprGetOwnerData(consdata->varexprs[i])->violscoretag = 0; 6678 } 6679 } 6680 else 6681 { 6682 SCIP_EXPR* expr; 6683 SCIP_Real lb; 6684 SCIP_Real ub; 6685 6686 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) ) 6687 { 6688 if( SCIPexprGetOwnerData(expr)->violscoretag != conshdlrdata->enforound ) 6689 continue; 6690 6691 /* if some nlhdlr added a branching score for this expression, then it considered this expression as 6692 * variables, so this expression should either be an original variable or have an auxiliary variable 6693 */ 6694 var = SCIPgetExprAuxVarNonlinear(expr); 6695 assert(var != NULL); 6696 6697 lb = SCIPvarGetLbLocal(var); 6698 ub = SCIPvarGetUbLocal(var); 6699 6700 /* skip already fixed variable */ 6701 if( SCIPisEQ(scip, lb, ub) ) 6702 { 6703 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " skip fixed variable <%s>[%.15g,%.15g]\n", SCIPvarGetName(var), lb, ub); ) 6704 continue; 6705 } 6706 6707 assert(*ncands + 1 < SCIPgetNVars(scip)); 6708 cands[*ncands].expr = expr; 6709 cands[*ncands].auxviol = SCIPgetExprViolScoreNonlinear(expr); 6710 ++(*ncands); 6711 } 6712 } 6713 } 6714 6715 /* if we have branching candidates, then we don't need another attempt */ 6716 if( *ncands > 0 ) 6717 break; 6718 } 6719 6720 if( it != NULL ) 6721 SCIPfreeExpriter(&it); 6722 6723 return SCIP_OKAY; 6724 } 6725 6726 /** computes a branching score for a variable that reflects how important branching on this variable would be for 6727 * improving the dual bound from the LP relaxation 6728 * 6729 * Assume the Lagrangian for the current LP is something of the form 6730 * L(x,z,lambda) = c'x + sum_i lambda_i (a_i'x - z_i + b_i) + ... 6731 * where x are the original variables, z the auxiliary variables, 6732 * and a_i'x - z_i + b_i <= 0 are the rows of the LP. 6733 * 6734 * Assume that a_i'x + b_i <= z_i was derived from some nonlinear constraint f(x) <= z and drop index i. 6735 * If we could have used not only an estimator, but the actual function f(x), then this would 6736 * have contributed lambda*(f(x) - z) to the Lagrangian function (though the value of z would be different). 6737 * Using a lot of handwaving, we claim that 6738 * lambda_i * (f(x) - a_i'x + b_i) 6739 * is a value that can be used to quantity how much improving the estimator a'x + b <= z could change the dual bound. 6740 * If an estimator depended on local bounds, then it could be improved by branching. 6741 * We use row-is-local as proxy for estimator-depending-on-lower-bounds. 6742 * 6743 * To score a variable, we then sum the values lambda_i * (f(x) - a_i'x + b_i) for all rows in which the variable appears. 6744 * To scale, we divide by the LP objective value (if >1). 6745 * 6746 * TODO if we branch only on original variables, we neglect here estimators that are build on auxiliary variables; 6747 * these are affected by the bounds on original variables indirectly (through forward-propagation) 6748 * 6749 * TODO if we branch also on auxiliary variables, then separating z from the x-variables in the row a'x+b <= z should happen; 6750 * in effect, we should go from the row to the expression for which it was generated and consider only variables that 6751 * would also be branching candidates 6752 */ 6753 static 6754 SCIP_Real getDualBranchscore( 6755 SCIP* scip, /**< SCIP data structure */ 6756 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraints handler */ 6757 SCIP_VAR* var /**< variable */ 6758 ) 6759 { 6760 SCIP_COL* col; 6761 SCIP_ROW** rows; 6762 int nrows; 6763 int r; 6764 SCIP_Real dualscore; 6765 6766 assert(scip != NULL); 6767 assert(conshdlr != NULL); 6768 assert(var != NULL); 6769 6770 /* if LP not solved, then the dual branching score is not available */ 6771 if( SCIPgetLPSolstat(scip) != SCIP_LPSOLSTAT_OPTIMAL ) 6772 return 0.0; 6773 6774 /* if var is not in the LP, then the dual branching score is not available */ 6775 if( SCIPvarGetStatus(var) != SCIP_VARSTATUS_COLUMN ) 6776 return 0.0; 6777 6778 col = SCIPvarGetCol(var); 6779 assert(col != NULL); 6780 6781 if( !SCIPcolIsInLP(col) ) 6782 return 0.0; 6783 6784 nrows = SCIPcolGetNLPNonz(col); /* TODO there is a big warning on when not to use this method; is the check for SCIPcolIsInLP sufficient? */ 6785 rows = SCIPcolGetRows(col); 6786 6787 /* SCIPinfoMessage(scip, enfologfile, " dualscoring <%s>\n", SCIPvarGetName(var)); */ 6788 6789 /* aggregate duals from all rows from consexpr with non-zero dual 6790 * TODO: this is a quick-and-dirty implementation, and not used by default 6791 * in the long run, this should be either removed or replaced by a proper implementation 6792 */ 6793 dualscore = 0.0; 6794 for( r = 0; r < nrows; ++r ) 6795 { 6796 SCIP_Real estimategap; 6797 const char* estimategapstr; 6798 6799 /* rows from cuts that may be replaced by tighter ones after branching are the interesting ones 6800 * these would typically be local, unless they are created at the root node 6801 * so not check for local now, but trust that estimators that do not improve after branching will have an estimategap of 0 6802 if( !SCIProwIsLocal(rows[r]) ) 6803 continue; 6804 */ 6805 if( SCIProwGetOriginConshdlr(rows[r]) != conshdlr ) 6806 continue; 6807 if( SCIPisZero(scip, SCIProwGetDualsol(rows[r])) ) 6808 continue; 6809 6810 estimategapstr = strstr(SCIProwGetName(rows[r]), "_estimategap="); 6811 if( estimategapstr == NULL ) /* gap not stored, maybe because it was 0 */ 6812 continue; 6813 estimategap = atof(estimategapstr + 13); 6814 assert(estimategap >= 0.0); 6815 if( !SCIPisFinite(estimategap) || SCIPisHugeValue(scip, estimategap) ) 6816 estimategap = SCIPgetHugeValue(scip); 6817 6818 /* SCIPinfoMessage(scip, enfologfile, " row <%s> contributes %g*|%g|: ", SCIProwGetName(rows[r]), estimategap, SCIProwGetDualsol(rows[r])); 6819 SCIP_CALL( SCIPprintRow(scip, rows[r], enfologfile) ); */ 6820 6821 dualscore += estimategap * REALABS(SCIProwGetDualsol(rows[r])); 6822 } 6823 6824 /* divide by optimal value of LP for scaling */ 6825 dualscore /= MAX(1.0, REALABS(SCIPgetLPObjval(scip))); 6826 6827 return dualscore; 6828 } 6829 6830 /** computes branching scores (including weighted score) for a set of candidates 6831 * 6832 * For each candidate in the array, compute and store the various branching scores (violation, pseudo-costs, vartype, domainwidth). 6833 * For pseudo-costs, it's possible that the score is not available, in which case cands[c].pscost will be set to SCIP_INVALID. 6834 * 6835 * For each score, compute the maximum over all candidates. 6836 * 6837 * Then compute for each candidate a "weighted" score using the weights as specified by parameters 6838 * and the scores as previously computed, but scale each score to be in [0,1], i.e., divide each score by the maximum 6839 * score of all candidates. 6840 * Further divide by the sum of all weights where a score was available (even if the score was 0). 6841 * 6842 * For example: 6843 * - Let variable x have violation-score 10.0 and pseudo-cost-score 5.0. 6844 * - Let variable y have violation-score 12.0 but no pseudo-cost-score (because it hasn't yet been branched on sufficiently often). 6845 * - Assuming violation is weighted by 2.0 and pseudo-costs are weighted by 3.0. 6846 * - Then the weighted scores for x will be (2.0 * 10.0/12.0 + 3.0 * 5.0/5.0) / (2.0 + 3.0) = 0.9333. 6847 * The weighted score for y will be (2.0 * 12.0/12.0) / 2.0 = 1.0. 6848 */ 6849 static 6850 void scoreBranchingCandidates( 6851 SCIP* scip, /**< SCIP data structure */ 6852 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 6853 BRANCHCAND* cands, /**< branching candidates */ 6854 int ncands, /**< number of candidates */ 6855 SCIP_SOL* sol /**< solution to enforce (NULL for the LP solution) */ 6856 ) 6857 { 6858 SCIP_CONSHDLRDATA* conshdlrdata; 6859 BRANCHCAND maxscore; 6860 int c; 6861 6862 assert(scip != NULL); 6863 assert(conshdlr != NULL); 6864 assert(cands != NULL); 6865 assert(ncands > 0); 6866 6867 conshdlrdata = SCIPconshdlrGetData(conshdlr); 6868 assert(conshdlrdata != NULL); 6869 6870 /* initialize counts to 0 */ 6871 memset(&maxscore, 0, sizeof(BRANCHCAND)); 6872 6873 for( c = 0; c < ncands; ++c ) 6874 { 6875 if( conshdlrdata->branchviolweight > 0.0 ) 6876 { 6877 /* cands[c].auxviol was set in collectBranchingCandidates, so only update maxscore here */ 6878 maxscore.auxviol = MAX(maxscore.auxviol, cands[c].auxviol); 6879 } 6880 6881 if( conshdlrdata->branchdomainweight > 0.0 ) 6882 { 6883 SCIP_Real domainwidth; 6884 SCIP_VAR* var; 6885 6886 var = SCIPgetExprAuxVarNonlinear(cands[c].expr); 6887 assert(var != NULL); 6888 6889 /* get domain width, taking infinity at 1e20 on purpose */ 6890 domainwidth = SCIPvarGetUbLocal(var) - SCIPvarGetLbLocal(var); 6891 6892 /* domain-score is going to be log(2*infinity / domainwidth) if domain width >= 1 6893 * and log(2 * infinity * MAX(epsilon, domainwidth)) for domain width < 1 6894 * the idea is to penalize very large and very small domains 6895 */ 6896 if( domainwidth >= 1.0 ) 6897 cands[c].domain = log10(2 * SCIPinfinity(scip) / domainwidth); 6898 else 6899 cands[c].domain = log10(2 * SCIPinfinity(scip) * MAX(SCIPepsilon(scip), domainwidth)); 6900 6901 maxscore.domain = MAX(cands[c].domain, maxscore.domain); 6902 } 6903 else 6904 cands[c].domain = 0.0; 6905 6906 if( conshdlrdata->branchdualweight > 0.0 ) 6907 { 6908 SCIP_VAR* var; 6909 6910 var = SCIPgetExprAuxVarNonlinear(cands[c].expr); 6911 assert(var != NULL); 6912 6913 cands[c].dual = getDualBranchscore(scip, conshdlr, var); 6914 maxscore.dual = MAX(cands[c].dual, maxscore.dual); 6915 } 6916 6917 if( conshdlrdata->branchpscostweight > 0.0 && SCIPgetNObjVars(scip) > 0 ) 6918 { 6919 SCIP_VAR* var; 6920 6921 var = SCIPgetExprAuxVarNonlinear(cands[c].expr); 6922 assert(var != NULL); 6923 6924 if( SCIPisInfinity(scip, -SCIPvarGetLbLocal(var)) || SCIPisInfinity(scip, SCIPvarGetUbLocal(var)) ) 6925 cands[c].pscost = SCIP_INVALID; 6926 else 6927 { 6928 SCIP_Real brpoint; 6929 SCIP_Real pscostdown; 6930 SCIP_Real pscostup; 6931 char strategy; 6932 6933 /* decide how to compute pseudo-cost scores 6934 * this should be consistent with the way how pseudo-costs are updated in the core, which is decided by 6935 * branching/lpgainnormalize for continuous variables and move in LP-value for non-continuous variables 6936 */ 6937 if( SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS ) 6938 strategy = conshdlrdata->branchpscostupdatestrategy; 6939 else 6940 strategy = 'l'; 6941 6942 brpoint = SCIPgetBranchingPoint(scip, var, SCIP_INVALID); 6943 6944 /* branch_relpscost deems pscosts as reliable, if the pseudo-count is at least something between 1 and 4 6945 * or it uses some statistical tests involving SCIPisVarPscostRelerrorReliable 6946 * For here, I use a simple #counts >= branchpscostreliable. 6947 * TODO use SCIPgetVarPseudocostCount() instead? 6948 */ 6949 if( SCIPgetVarPseudocostCountCurrentRun(scip, var, SCIP_BRANCHDIR_DOWNWARDS) >= conshdlrdata->branchpscostreliable ) 6950 { 6951 switch( strategy ) 6952 { 6953 case 's' : 6954 pscostdown = SCIPgetVarPseudocostVal(scip, var, -(SCIPvarGetUbLocal(var) - SCIPadjustedVarLb(scip, var, brpoint))); 6955 break; 6956 case 'd' : 6957 pscostdown = SCIPgetVarPseudocostVal(scip, var, -(SCIPadjustedVarUb(scip, var, brpoint) - SCIPvarGetLbLocal(var))); 6958 break; 6959 case 'l' : 6960 if( SCIPisInfinity(scip, SCIPgetSolVal(scip, sol, var)) ) 6961 pscostdown = SCIP_INVALID; 6962 else if( SCIPgetSolVal(scip, sol, var) <= SCIPadjustedVarUb(scip, var, brpoint) ) 6963 pscostdown = SCIPgetVarPseudocostVal(scip, var, 0.0); 6964 else 6965 pscostdown = SCIPgetVarPseudocostVal(scip, var, -(SCIPgetSolVal(scip, NULL, var) - SCIPadjustedVarUb(scip, var, brpoint))); 6966 break; 6967 default : 6968 SCIPerrorMessage("pscost update strategy %c unknown\n", strategy); 6969 pscostdown = SCIP_INVALID; 6970 } 6971 } 6972 else 6973 pscostdown = SCIP_INVALID; 6974 6975 if( SCIPgetVarPseudocostCountCurrentRun(scip, var, SCIP_BRANCHDIR_UPWARDS) >= conshdlrdata->branchpscostreliable ) 6976 { 6977 switch( strategy ) 6978 { 6979 case 's' : 6980 pscostup = SCIPgetVarPseudocostVal(scip, var, SCIPadjustedVarUb(scip, var, brpoint) - SCIPvarGetLbLocal(var)); 6981 break; 6982 case 'd' : 6983 pscostup = SCIPgetVarPseudocostVal(scip, var, SCIPvarGetUbLocal(var) - SCIPadjustedVarLb(scip, var, brpoint)); 6984 break; 6985 case 'l' : 6986 if( SCIPisInfinity(scip, -SCIPgetSolVal(scip, sol, var)) ) 6987 pscostup = SCIP_INVALID; 6988 else if( SCIPgetSolVal(scip, NULL, var) >= SCIPadjustedVarLb(scip, var, brpoint) ) 6989 pscostup = SCIPgetVarPseudocostVal(scip, var, 0.0); 6990 else 6991 pscostup = SCIPgetVarPseudocostVal(scip, var, SCIPadjustedVarLb(scip, var, brpoint) - SCIPgetSolVal(scip, NULL, var) ); 6992 break; 6993 default : 6994 SCIPerrorMessage("pscost update strategy %c unknown\n", strategy); 6995 pscostup = SCIP_INVALID; 6996 } 6997 } 6998 else 6999 pscostup = SCIP_INVALID; 7000 7001 /* TODO if both are valid, we get pscostdown*pscostup, but does this compare well with vars were only pscostdown or pscostup is used? 7002 * maybe we should use (pscostdown+pscostup)/2 or sqrt(pscostdown*pscostup) ? 7003 */ 7004 if( pscostdown == SCIP_INVALID && pscostup == SCIP_INVALID ) 7005 cands[c].pscost = SCIP_INVALID; 7006 else if( pscostdown == SCIP_INVALID ) 7007 cands[c].pscost = pscostup; 7008 else if( pscostup == SCIP_INVALID ) 7009 cands[c].pscost = pscostdown; 7010 else 7011 cands[c].pscost = SCIPgetBranchScore(scip, NULL, pscostdown, pscostup); /* pass NULL for var to avoid multiplication with branch-factor */ 7012 } 7013 7014 if( cands[c].pscost != SCIP_INVALID ) 7015 maxscore.pscost = MAX(cands[c].pscost, maxscore.pscost); 7016 } 7017 7018 if( conshdlrdata->branchvartypeweight > 0.0 ) 7019 { 7020 SCIP_VAR* var; 7021 7022 var = SCIPgetExprAuxVarNonlinear(cands[c].expr); 7023 assert(var != NULL); 7024 7025 switch( SCIPvarGetType(var) ) 7026 { 7027 case SCIP_VARTYPE_BINARY : 7028 cands[c].vartype = 1.0; 7029 break; 7030 case SCIP_VARTYPE_INTEGER : 7031 cands[c].vartype = 0.1; 7032 break; 7033 case SCIP_VARTYPE_IMPLINT : 7034 cands[c].vartype = 0.01; 7035 break; 7036 case SCIP_VARTYPE_CONTINUOUS : 7037 default: 7038 cands[c].vartype = 0.0; 7039 } 7040 maxscore.vartype = MAX(cands[c].vartype, maxscore.vartype); 7041 } 7042 } 7043 7044 /* now compute a weighted score for each candidate from the single scores 7045 * the single scores are scaled to be in [0,1] for this 7046 */ 7047 for( c = 0; c < ncands; ++c ) 7048 { 7049 SCIP_Real weightsum; 7050 7051 ENFOLOG( 7052 SCIP_VAR* var; 7053 var = SCIPgetExprAuxVarNonlinear(cands[c].expr); 7054 SCIPinfoMessage(scip, enfologfile, " scoring <%8s>[%7.1g,%7.1g]:(", SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)); 7055 ) 7056 7057 cands[c].weighted = 0.0; 7058 weightsum = 0.0; 7059 7060 if( maxscore.auxviol > 0.0 ) 7061 { 7062 cands[c].weighted += conshdlrdata->branchviolweight * cands[c].auxviol / maxscore.auxviol; 7063 weightsum += conshdlrdata->branchviolweight; 7064 7065 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " %+g*%7.2g(viol)", conshdlrdata->branchviolweight, cands[c].auxviol / maxscore.auxviol); ) 7066 } 7067 7068 if( maxscore.domain > 0.0 ) 7069 { 7070 cands[c].weighted += conshdlrdata->branchdomainweight * cands[c].domain / maxscore.domain; 7071 weightsum += conshdlrdata->branchdomainweight; 7072 7073 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " %+g*%7.2g(domain)", conshdlrdata->branchdomainweight, cands[c].domain / maxscore.domain); ) 7074 } 7075 7076 if( maxscore.dual > 0.0 ) 7077 { 7078 cands[c].weighted += conshdlrdata->branchdualweight * cands[c].dual / maxscore.dual; 7079 weightsum += conshdlrdata->branchdualweight; 7080 7081 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " %+g*%7.2g(dual)", conshdlrdata->branchdualweight, cands[c].dual / maxscore.dual); ) 7082 } 7083 7084 if( maxscore.pscost > 0.0 ) 7085 { 7086 /* use pseudo-costs only if available */ 7087 if( cands[c].pscost != SCIP_INVALID ) 7088 { 7089 cands[c].weighted += conshdlrdata->branchpscostweight * cands[c].pscost / maxscore.pscost; 7090 weightsum += conshdlrdata->branchpscostweight; 7091 7092 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " %+g*%7.2g(pscost)", conshdlrdata->branchpscostweight, cands[c].pscost / maxscore.pscost); ) 7093 } 7094 else 7095 { 7096 /* do not add pscostscore, if not available, also do not add into weightsum */ 7097 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " +0.0* n/a(pscost)"); ) 7098 } 7099 } 7100 7101 if( maxscore.vartype > 0.0 ) 7102 { 7103 cands[c].weighted += conshdlrdata->branchvartypeweight * cands[c].vartype / maxscore.vartype; 7104 weightsum += conshdlrdata->branchvartypeweight; 7105 7106 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " %+g*%6.2g(vartype)", conshdlrdata->branchvartypeweight, cands[c].vartype / maxscore.vartype); ) 7107 } 7108 assert(weightsum > 0.0); /* we should have got at least one valid score */ 7109 cands[c].weighted /= weightsum; 7110 7111 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " ) / %g = %g\n", weightsum, cands[c].weighted); ) 7112 } 7113 } 7114 7115 /** compare two branching candidates by their weighted score 7116 * 7117 * if weighted score is equal, use variable index of (aux)var 7118 */ 7119 static 7120 SCIP_DECL_SORTINDCOMP(branchcandCompare) 7121 { 7122 BRANCHCAND* cands = (BRANCHCAND*)dataptr; 7123 7124 if( cands[ind1].weighted != cands[ind2].weighted ) 7125 return cands[ind1].weighted < cands[ind2].weighted ? -1 : 1; 7126 else 7127 return SCIPvarGetIndex(SCIPgetExprAuxVarNonlinear(cands[ind1].expr)) - SCIPvarGetIndex(SCIPgetExprAuxVarNonlinear(cands[ind2].expr)); 7128 } 7129 7130 /** do branching or register branching candidates */ 7131 static 7132 SCIP_RETCODE branching( 7133 SCIP* scip, /**< SCIP data structure */ 7134 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 7135 SCIP_CONS** conss, /**< constraints to process */ 7136 int nconss, /**< number of constraints */ 7137 SCIP_Real maxrelconsviol, /**< maximal scaled constraint violation */ 7138 SCIP_SOL* sol, /**< solution to enforce (NULL for the LP solution) */ 7139 SCIP_Longint soltag, /**< tag of solution */ 7140 SCIP_RESULT* result /**< pointer to store the result of branching */ 7141 ) 7142 { 7143 SCIP_CONSHDLRDATA* conshdlrdata; 7144 BRANCHCAND* cands; 7145 int ncands; 7146 SCIP_VAR* var; 7147 SCIP_NODE* downchild; 7148 SCIP_NODE* eqchild; 7149 SCIP_NODE* upchild; 7150 7151 assert(conshdlr != NULL); 7152 assert(result != NULL); 7153 7154 *result = SCIP_DIDNOTFIND; 7155 7156 conshdlrdata = SCIPconshdlrGetData(conshdlr); 7157 assert(conshdlrdata != NULL); 7158 7159 if( conshdlrdata->branchexternal ) 7160 { 7161 /* just register branching candidates as external */ 7162 SCIP_Bool success; 7163 7164 SCIP_CALL( registerBranchingCandidates(scip, conshdlr, conss, nconss, &success) ); 7165 if( success ) 7166 *result = SCIP_INFEASIBLE; 7167 7168 return SCIP_OKAY; 7169 } 7170 7171 /* collect branching candidates and their auxviol-score */ 7172 SCIP_CALL( SCIPallocBufferArray(scip, &cands, SCIPgetNVars(scip)) ); 7173 SCIP_CALL( collectBranchingCandidates(scip, conshdlr, conss, nconss, maxrelconsviol, sol, soltag, cands, &ncands) ); 7174 7175 /* if no unfixed branching candidate in all violated constraint, then it's probably numerics that prevented us to separate or decide a cutoff 7176 * we will return here and let the fallbacks in consEnfo() decide how to proceed 7177 */ 7178 if( ncands == 0 ) 7179 goto TERMINATE; 7180 7181 if( ncands > 1 ) 7182 { 7183 /* if there are more than one candidate, then compute scores and select */ 7184 int* perm; 7185 int c; 7186 int left; 7187 int right; 7188 SCIP_Real threshold; 7189 7190 /* compute additional scores on branching candidates and weighted score */ 7191 scoreBranchingCandidates(scip, conshdlr, cands, ncands, sol); 7192 7193 /* sort candidates by weighted score */ 7194 SCIP_CALL( SCIPallocBufferArray(scip, &perm, ncands) ); 7195 SCIPsortDown(perm, branchcandCompare, (void*)cands, ncands); 7196 7197 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " %d branching candidates <%s>(%g)...<%s>(%g)\n", ncands, 7198 SCIPvarGetName(SCIPgetExprAuxVarNonlinear(cands[perm[0]].expr)), cands[perm[0]].weighted, 7199 SCIPvarGetName(SCIPgetExprAuxVarNonlinear(cands[perm[ncands - 1]].expr)), cands[perm[ncands - 1]].weighted); ) 7200 7201 /* binary search to find first low-scored (score below branchhighscorefactor * maximal-score) candidate */ 7202 left = 0; 7203 right = ncands - 1; 7204 threshold = conshdlrdata->branchhighscorefactor * cands[perm[0]].weighted; 7205 while( left < right ) 7206 { 7207 int mid = (left + right) / 2; 7208 if( cands[perm[mid]].weighted >= threshold ) 7209 left = mid + 1; 7210 else 7211 right = mid; 7212 } 7213 assert(left <= ncands); 7214 7215 if( left < ncands ) 7216 { 7217 if( cands[perm[left]].weighted >= threshold ) 7218 { 7219 assert(left + 1 == ncands || cands[perm[left + 1]].weighted < threshold); 7220 ncands = left + 1; 7221 } 7222 else 7223 { 7224 assert(cands[perm[left]].weighted < threshold); 7225 ncands = left; 7226 } 7227 } 7228 assert(ncands > 0); 7229 7230 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " %d branching candidates <%s>(%g)...<%s>(%g) after removing low scores\n", ncands, 7231 SCIPvarGetName(SCIPgetExprAuxVarNonlinear(cands[perm[0]].expr)), cands[perm[0]].weighted, 7232 SCIPvarGetName(SCIPgetExprAuxVarNonlinear(cands[perm[ncands - 1]].expr)), cands[perm[ncands - 1]].weighted); ) 7233 7234 if( ncands > 1 ) 7235 { 7236 /* choose at random from candidates 0..ncands-1 */ 7237 if( conshdlrdata->branchrandnumgen == NULL ) 7238 { 7239 SCIP_CALL( SCIPcreateRandom(scip, &conshdlrdata->branchrandnumgen, BRANCH_RANDNUMINITSEED, TRUE) ); 7240 } 7241 c = SCIPrandomGetInt(conshdlrdata->branchrandnumgen, 0, ncands - 1); 7242 var = SCIPgetExprAuxVarNonlinear(cands[perm[c]].expr); 7243 } 7244 else 7245 var = SCIPgetExprAuxVarNonlinear(cands[perm[0]].expr); 7246 7247 SCIPfreeBufferArray(scip, &perm); 7248 } 7249 else 7250 { 7251 var = SCIPgetExprAuxVarNonlinear(cands[0].expr); 7252 } 7253 assert(var != NULL); 7254 7255 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " branching on variable <%s>[%g,%g]\n", SCIPvarGetName(var), 7256 SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)); ) 7257 7258 SCIP_CALL( SCIPbranchVarVal(scip, var, SCIPgetBranchingPoint(scip, var, SCIP_INVALID), &downchild, &eqchild, 7259 &upchild) ); 7260 if( downchild != NULL || eqchild != NULL || upchild != NULL ) 7261 *result = SCIP_BRANCHED; 7262 else 7263 /* if there are no children, then variable should have been fixed by SCIPbranchVarVal */ 7264 *result = SCIP_REDUCEDDOM; 7265 7266 TERMINATE: 7267 SCIPfreeBufferArray(scip, &cands); 7268 7269 return SCIP_OKAY; 7270 } 7271 7272 /** call enforcement or estimate callback of nonlinear handler 7273 * 7274 * Calls the enforcement callback, if available. 7275 * Otherwise, calls the estimate callback, if available, and constructs a cut from the estimator. 7276 * 7277 * If cut is weak, but estimator is not tight, tries to add branching candidates. 7278 */ 7279 static 7280 SCIP_RETCODE enforceExprNlhdlr( 7281 SCIP* scip, /**< SCIP main data structure */ 7282 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 7283 SCIP_CONS* cons, /**< nonlinear constraint */ 7284 SCIP_NLHDLR* nlhdlr, /**< nonlinear handler */ 7285 SCIP_EXPR* expr, /**< expression */ 7286 SCIP_NLHDLREXPRDATA* nlhdlrexprdata, /**< nonlinear handler data of expression */ 7287 SCIP_SOL* sol, /**< solution to be separated (NULL for the LP solution) */ 7288 SCIP_Real auxvalue, /**< current value of expression w.r.t. auxiliary variables as obtained from EVALAUX */ 7289 SCIP_Bool overestimate, /**< whether the expression needs to be over- or underestimated */ 7290 SCIP_Bool separated, /**< whether another nonlinear handler already added a cut for this expression */ 7291 SCIP_Bool allowweakcuts, /**< whether we allow for weak cuts */ 7292 SCIP_Bool inenforcement, /**< whether we are in enforcement (and not just separation) */ 7293 SCIP_RESULT* result /**< pointer to store the result */ 7294 ) 7295 { 7296 assert(result != NULL); 7297 7298 /* call enforcement callback of the nlhdlr */ 7299 SCIP_CALL( SCIPnlhdlrEnfo(scip, conshdlr, cons, nlhdlr, expr, nlhdlrexprdata, sol, auxvalue, overestimate, 7300 allowweakcuts, separated, inenforcement, result) ); 7301 7302 /* if it was not running (e.g., because it was not available) or did not find anything, then try with estimator callback */ 7303 if( *result != SCIP_DIDNOTRUN && *result != SCIP_DIDNOTFIND ) 7304 { 7305 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " enfo of nlhdlr <%s> succeeded with result %d\n", 7306 SCIPnlhdlrGetName(nlhdlr), *result); ) 7307 return SCIP_OKAY; 7308 } 7309 else 7310 { 7311 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " enfo of nlhdlr <%s> did not succeed with result %d\n", SCIPnlhdlrGetName(nlhdlr), *result); ) 7312 } 7313 7314 *result = SCIP_DIDNOTFIND; 7315 7316 /* now call the estimator callback of the nlhdlr */ 7317 if( SCIPnlhdlrHasEstimate(nlhdlr) ) 7318 { 7319 SCIP_VAR* auxvar; 7320 SCIP_Bool sepasuccess = FALSE; 7321 SCIP_Bool branchscoresuccess = FALSE; 7322 SCIP_PTRARRAY* rowpreps; 7323 int minidx; 7324 int maxidx; 7325 int r; 7326 SCIP_ROWPREP* rowprep; 7327 7328 SCIP_CALL( SCIPcreatePtrarray(scip, &rowpreps) ); 7329 7330 auxvar = SCIPgetExprAuxVarNonlinear(expr); 7331 assert(auxvar != NULL); 7332 7333 SCIP_CALL( SCIPnlhdlrEstimate(scip, conshdlr, nlhdlr, expr, nlhdlrexprdata, sol, auxvalue, overestimate, 7334 SCIPgetSolVal(scip, sol, auxvar), inenforcement, rowpreps, &sepasuccess, &branchscoresuccess) ); 7335 7336 minidx = SCIPgetPtrarrayMinIdx(scip, rowpreps); 7337 maxidx = SCIPgetPtrarrayMaxIdx(scip, rowpreps); 7338 7339 assert((sepasuccess && minidx <= maxidx) || (!sepasuccess && minidx > maxidx)); 7340 7341 if( !sepasuccess ) 7342 { 7343 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " estimate of nlhdlr %s failed\n", 7344 SCIPnlhdlrGetName(nlhdlr)); ) 7345 } 7346 7347 for( r = minidx; r <= maxidx; ++r ) 7348 { 7349 rowprep = (SCIP_ROWPREP*) SCIPgetPtrarrayVal(scip, rowpreps, r); 7350 7351 assert(rowprep != NULL); 7352 assert(SCIProwprepGetSidetype(rowprep) == (overestimate ? SCIP_SIDETYPE_LEFT : SCIP_SIDETYPE_RIGHT)); 7353 7354 /* complete estimator to cut */ 7355 SCIP_CALL( SCIPaddRowprepTerm(scip, rowprep, auxvar, -1.0) ); 7356 7357 /* add the cut and/or branching scores */ 7358 SCIP_CALL( SCIPprocessRowprepNonlinear(scip, nlhdlr, cons, expr, rowprep, overestimate, auxvar, 7359 auxvalue, allowweakcuts, branchscoresuccess, inenforcement, sol, result) ); 7360 7361 SCIPfreeRowprep(scip, &rowprep); 7362 } 7363 7364 SCIP_CALL( SCIPfreePtrarray(scip, &rowpreps) ); 7365 } 7366 7367 return SCIP_OKAY; 7368 } 7369 7370 /** tries to enforce violation in an expression by separation, bound tightening, or finding a branching candidate 7371 * 7372 * if not inenforcement, then we should be called by consSepa(), and thus only try separation 7373 */ 7374 static 7375 SCIP_RETCODE enforceExpr( 7376 SCIP* scip, /**< SCIP data structure */ 7377 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraints handler */ 7378 SCIP_CONS* cons, /**< nonlinear constraint */ 7379 SCIP_EXPR* expr, /**< expression */ 7380 SCIP_SOL* sol, /**< solution to separate, or NULL if LP solution should be used */ 7381 SCIP_Longint soltag, /**< tag of solution */ 7382 SCIP_Bool allowweakcuts, /**< whether we allow weak cuts */ 7383 SCIP_Bool inenforcement, /**< whether we are in enforcement (and not just separation) */ 7384 SCIP_RESULT* result /**< pointer to store the result of the enforcing call */ 7385 ) 7386 { 7387 SCIP_CONSHDLRDATA* conshdlrdata; 7388 SCIP_EXPR_OWNERDATA* ownerdata; 7389 SCIP_Real origviol; 7390 SCIP_Bool underestimate; 7391 SCIP_Bool overestimate; 7392 SCIP_Real auxviol; 7393 SCIP_Bool auxunderestimate; 7394 SCIP_Bool auxoverestimate; 7395 SCIP_RESULT hdlrresult; 7396 int e; 7397 7398 assert(scip != NULL); 7399 assert(expr != NULL); 7400 assert(result != NULL); 7401 7402 ownerdata = SCIPexprGetOwnerData(expr); 7403 assert(ownerdata != NULL); 7404 assert(ownerdata->auxvar != NULL); /* there must be a variable attached to the expression in order to construct a cut here */ 7405 7406 *result = SCIP_DIDNOTFIND; 7407 7408 /* make sure that this expression has been evaluated */ 7409 SCIP_CALL( SCIPevalExpr(scip, expr, sol, soltag) ); 7410 7411 /* decide whether under- or overestimate is required and get amount of violation */ 7412 origviol = getExprAbsOrigViolation(scip, expr, sol, &underestimate, &overestimate); 7413 7414 conshdlrdata = SCIPconshdlrGetData(conshdlr); 7415 assert(conshdlrdata != NULL); 7416 7417 /* no sufficient violation w.r.t. the original variables -> skip expression */ 7418 if( !overestimate && !underestimate ) 7419 { 7420 return SCIP_OKAY; 7421 } 7422 7423 /* check aux-violation w.r.t. each nonlinear handlers and try to enforce when there is a decent violation */ 7424 for( e = 0; e < ownerdata->nenfos; ++e ) 7425 { 7426 SCIP_NLHDLR* nlhdlr; 7427 7428 /* skip nlhdlr that do not want to participate in any separation */ 7429 if( (ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPABOTH) == 0 ) 7430 continue; 7431 7432 nlhdlr = ownerdata->enfos[e]->nlhdlr; 7433 assert(nlhdlr != NULL); 7434 7435 /* evaluate the expression w.r.t. the nlhdlrs auxiliary variables */ 7436 SCIP_CALL( SCIPnlhdlrEvalaux(scip, nlhdlr, expr, ownerdata->enfos[e]->nlhdlrexprdata, &ownerdata->enfos[e]->auxvalue, sol) ); 7437 ENFOLOG( 7438 SCIPinfoMessage(scip, enfologfile, " expr "); 7439 SCIPprintExpr(scip, expr, enfologfile); 7440 SCIPinfoMessage(scip, enfologfile, " (%p): evalvalue %.15g auxvarvalue %.15g [%.15g,%.15g], nlhdlr <%s> " \ 7441 "auxvalue: %.15g\n", (void*)expr, SCIPexprGetEvalValue(expr), SCIPgetSolVal(scip, sol, ownerdata->auxvar), 7442 SCIPexprGetActivity(expr).inf, SCIPexprGetActivity(expr).sup, SCIPnlhdlrGetName(nlhdlr), ownerdata->enfos[e]->auxvalue); 7443 ) 7444 7445 /* TODO if expr is root of constraint (consdata->expr == expr), 7446 * then compare auxvalue with constraint sides instead of auxvarvalue, as the former is what actually matters 7447 * that is, if auxvalue is good enough for the constraint to be satisfied, but when looking at evalvalue we see 7448 * the the constraint is violated, then some of the auxvars that nlhdlr uses is not having a good enough value, 7449 * so we should enforce in these auxiliaries first 7450 * if changing this here, we must also adapt analyzeViolation() 7451 */ 7452 7453 auxviol = getExprAbsAuxViolation(scip, expr, ownerdata->enfos[e]->auxvalue, sol, &auxunderestimate, &auxoverestimate); 7454 assert(auxviol >= 0.0); 7455 7456 /* if aux-violation is much smaller than orig-violation, then better enforce further down in the expression first */ 7457 if( !SCIPisInfinity(scip, auxviol) && auxviol < conshdlrdata->enfoauxviolfactor * origviol ) 7458 { 7459 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " skip enforce using nlhdlr <%s> for expr %p (%s) with " \ 7460 "auxviolation %g << origviolation %g under:%d over:%d\n", SCIPnlhdlrGetName(nlhdlr), (void*)expr, 7461 SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)), auxviol, origviol, underestimate, overestimate); ) 7462 7463 /* TODO should we do expr->lastenforced = conshdlrdata->enforound even though we haven't enforced, but only decided not to enforce? */ 7464 continue; 7465 } 7466 7467 /* if aux-violation is small (below feastol) and we look only for strong cuts, then it's unlikely to give a strong cut, so skip it */ 7468 if( !allowweakcuts && auxviol < SCIPfeastol(scip) ) 7469 { 7470 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " skip enforce using nlhdlr <%s> for expr %p (%s) with tiny " \ 7471 "auxviolation %g under:%d over:%d\n", SCIPnlhdlrGetName(nlhdlr), (void*)expr, SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)), auxviol, 7472 underestimate, overestimate); ) 7473 7474 /* TODO should we do expr->lastenforced = conshdlrdata->enforound even though we haven't enforced, but only decided not to enforce? */ 7475 continue; 7476 } 7477 7478 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " enforce using nlhdlr <%s> for expr %p (%s) with auxviolation " \ 7479 "%g origviolation %g under:%d over:%d weak:%d\n", SCIPnlhdlrGetName(nlhdlr), (void*)expr, SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)), 7480 auxviol, origviol, underestimate, overestimate, allowweakcuts); ) 7481 7482 /* if we want to overestimate and violation w.r.t. auxiliary variables is also present on this side and nlhdlr 7483 * wants to be called for separation on this side, then call separation of nlhdlr 7484 */ 7485 if( overestimate && auxoverestimate && (ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPAABOVE) != 0 ) 7486 { 7487 /* call the separation or estimation callback of the nonlinear handler for overestimation */ 7488 hdlrresult = SCIP_DIDNOTFIND; 7489 SCIP_CALL( enforceExprNlhdlr(scip, conshdlr, cons, nlhdlr, expr, ownerdata->enfos[e]->nlhdlrexprdata, sol, 7490 ownerdata->enfos[e]->auxvalue, TRUE, *result == SCIP_SEPARATED, allowweakcuts, inenforcement, &hdlrresult) ); 7491 7492 if( hdlrresult == SCIP_CUTOFF ) 7493 { 7494 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " found a cutoff -> stop separation\n"); ) 7495 *result = SCIP_CUTOFF; 7496 ownerdata->lastenforced = conshdlrdata->enforound; 7497 break; 7498 } 7499 7500 if( hdlrresult == SCIP_SEPARATED ) 7501 { 7502 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " nlhdlr <%s> separating the current solution by cut\n", SCIPnlhdlrGetName(nlhdlr)); ) 7503 *result = SCIP_SEPARATED; 7504 ownerdata->lastenforced = conshdlrdata->enforound; 7505 /* TODO or should we give other nlhdlr another chance? (also #3070) */ 7506 break; 7507 } 7508 7509 if( hdlrresult == SCIP_REDUCEDDOM ) 7510 { 7511 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " nlhdlr <%s> separating the current solution by boundchange\n", SCIPnlhdlrGetName(nlhdlr)); ) 7512 *result = SCIP_REDUCEDDOM; 7513 ownerdata->lastenforced = conshdlrdata->enforound; 7514 /* TODO or should we always just stop here? */ 7515 } 7516 7517 if( hdlrresult == SCIP_BRANCHED ) 7518 { 7519 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " nlhdlr <%s> added branching candidate\n", SCIPnlhdlrGetName(nlhdlr)); ) 7520 assert(inenforcement); 7521 7522 /* separation and domain reduction takes precedence over branching */ 7523 assert(*result == SCIP_DIDNOTFIND || *result == SCIP_SEPARATED || *result == SCIP_REDUCEDDOM || *result == SCIP_BRANCHED); 7524 if( *result == SCIP_DIDNOTFIND ) 7525 *result = SCIP_BRANCHED; 7526 ownerdata->lastenforced = conshdlrdata->enforound; 7527 } 7528 } 7529 7530 /* if we want to underestimate and violation w.r.t. auxiliary variables is also present on this side and nlhdlr 7531 * wants to be called for separation on this side, then call separation of nlhdlr 7532 */ 7533 if( underestimate && auxunderestimate && (ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPABELOW) != 0 ) 7534 { 7535 /* call the separation or estimation callback of the nonlinear handler for underestimation */ 7536 hdlrresult = SCIP_DIDNOTFIND; 7537 SCIP_CALL( enforceExprNlhdlr(scip, conshdlr, cons, nlhdlr, expr, ownerdata->enfos[e]->nlhdlrexprdata, sol, 7538 ownerdata->enfos[e]->auxvalue, FALSE, *result == SCIP_SEPARATED, allowweakcuts, inenforcement, &hdlrresult) ); 7539 7540 if( hdlrresult == SCIP_CUTOFF ) 7541 { 7542 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " found a cutoff -> stop separation\n"); ) 7543 *result = SCIP_CUTOFF; 7544 ownerdata->lastenforced = conshdlrdata->enforound; 7545 break; 7546 } 7547 7548 if( hdlrresult == SCIP_SEPARATED ) 7549 { 7550 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " nlhdlr <%s> separating the current solution by cut\n", SCIPnlhdlrGetName(nlhdlr)); ) 7551 *result = SCIP_SEPARATED; 7552 ownerdata->lastenforced = conshdlrdata->enforound; 7553 /* TODO or should we give other nlhdlr another chance? (also #3070) */ 7554 break; 7555 } 7556 7557 if( hdlrresult == SCIP_REDUCEDDOM ) 7558 { 7559 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " nlhdlr <%s> separating the current solution by boundchange\n", SCIPnlhdlrGetName(nlhdlr)); ) 7560 *result = SCIP_REDUCEDDOM; 7561 ownerdata->lastenforced = conshdlrdata->enforound; 7562 /* TODO or should we always just stop here? */ 7563 } 7564 7565 if( hdlrresult == SCIP_BRANCHED ) 7566 { 7567 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " nlhdlr <%s> added branching candidate\n", SCIPnlhdlrGetName(nlhdlr)); ) 7568 assert(inenforcement); 7569 7570 /* separation takes precedence over branching */ 7571 assert(*result == SCIP_DIDNOTFIND || *result == SCIP_SEPARATED || *result == SCIP_REDUCEDDOM || *result == SCIP_BRANCHED); 7572 if( *result == SCIP_DIDNOTFIND ) 7573 *result = SCIP_BRANCHED; 7574 ownerdata->lastenforced = conshdlrdata->enforound; 7575 } 7576 } 7577 } 7578 7579 return SCIP_OKAY; 7580 } 7581 7582 /** helper function to enforce a single constraint */ 7583 static 7584 SCIP_RETCODE enforceConstraint( 7585 SCIP* scip, /**< SCIP data structure */ 7586 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 7587 SCIP_CONS* cons, /**< constraint to process */ 7588 SCIP_SOL* sol, /**< solution to enforce (NULL for the LP solution) */ 7589 SCIP_Longint soltag, /**< tag of solution */ 7590 SCIP_EXPRITER* it, /**< expression iterator that we can just use here */ 7591 SCIP_Bool allowweakcuts, /**< whether to allow weak cuts in this round */ 7592 SCIP_Bool inenforcement, /**< whether to we are in enforcement, and not just separation */ 7593 SCIP_RESULT* result, /**< pointer to update with result of the enforcing call */ 7594 SCIP_Bool* success /**< buffer to store whether some enforcement took place */ 7595 ) 7596 { 7597 SCIP_CONSDATA* consdata; 7598 SCIP_CONSHDLRDATA* conshdlrdata; 7599 SCIP_EXPR* expr; 7600 7601 assert(conshdlr != NULL); 7602 assert(cons != NULL); 7603 assert(it != NULL); 7604 assert(result != NULL); 7605 assert(success != NULL); 7606 7607 conshdlrdata = SCIPconshdlrGetData(conshdlr); 7608 assert(conshdlrdata != NULL); 7609 7610 consdata = SCIPconsGetData(cons); 7611 assert(consdata != NULL); 7612 assert(SCIPexprGetOwnerData(consdata->expr)->nenfos >= 0); 7613 7614 *success = FALSE; 7615 7616 if( inenforcement && !consdata->ispropagated ) 7617 { 7618 /* If there are boundchanges that haven't been propagated to activities yet, then do this now and update bounds of 7619 * auxiliary variables, since some nlhdlr/exprhdlr may look at auxvar bounds or activities 7620 * (TODO: nlhdlr tells us now whether they do and so we could skip). 7621 * For now, update bounds of auxiliary variables only if called from enforcement, since updating auxvar bounds in 7622 * separation doesn't seem to be right (it would be ok if the boundchange cuts off the current LP solution by a 7623 * nice amount, but if not, we may just add a boundchange that doesn't change the dual bound much and could 7624 * confuse the stalling check for how long to do separation). 7625 */ 7626 SCIP_Bool infeasible; 7627 int ntightenings; 7628 7629 SCIP_CALL( forwardPropExpr(scip, conshdlr, consdata->expr, inenforcement, &infeasible, &ntightenings) ); 7630 if( infeasible ) 7631 { 7632 *result = SCIP_CUTOFF; 7633 return SCIP_OKAY; 7634 } 7635 /* if we tightened an auxvar bound, we better communicate that */ 7636 if( ntightenings > 0 ) 7637 *result = SCIP_REDUCEDDOM; 7638 } 7639 7640 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) ) 7641 { 7642 SCIP_EXPR_OWNERDATA* ownerdata; 7643 SCIP_RESULT resultexpr; 7644 7645 ownerdata = SCIPexprGetOwnerData(expr); 7646 assert(ownerdata != NULL); 7647 7648 /* we can only enforce if there is an auxvar to compare with */ 7649 if( ownerdata->auxvar == NULL ) 7650 continue; 7651 7652 assert(ownerdata->lastenforced <= conshdlrdata->enforound); 7653 if( ownerdata->lastenforced == conshdlrdata->enforound ) 7654 { 7655 ENFOLOG( 7656 SCIPinfoMessage(scip, enfologfile, " skip expr "); 7657 SCIPprintExpr(scip, expr, enfologfile); 7658 SCIPinfoMessage(scip, enfologfile, " as already enforced in this enforound\n"); 7659 ) 7660 *success = TRUE; 7661 continue; 7662 } 7663 7664 SCIP_CALL( enforceExpr(scip, conshdlr, cons, expr, sol, soltag, allowweakcuts, inenforcement, &resultexpr) ); 7665 7666 /* if not enforced, then we must not have found a cutoff, cut, domain reduction, or branchscore */ 7667 assert((ownerdata->lastenforced == conshdlrdata->enforound) == (resultexpr != SCIP_DIDNOTFIND)); 7668 if( ownerdata->lastenforced == conshdlrdata->enforound ) 7669 *success = TRUE; 7670 7671 if( resultexpr == SCIP_CUTOFF ) 7672 { 7673 *result = SCIP_CUTOFF; 7674 break; 7675 } 7676 7677 if( resultexpr == SCIP_SEPARATED ) 7678 *result = SCIP_SEPARATED; 7679 7680 if( resultexpr == SCIP_REDUCEDDOM && *result != SCIP_SEPARATED ) 7681 *result = SCIP_REDUCEDDOM; 7682 7683 if( resultexpr == SCIP_BRANCHED && *result != SCIP_SEPARATED && *result != SCIP_REDUCEDDOM ) 7684 *result = SCIP_BRANCHED; 7685 } 7686 7687 return SCIP_OKAY; 7688 } 7689 7690 /** try to separate violated constraints and, if in enforcement, register branching scores 7691 * 7692 * Sets result to 7693 * - SCIP_DIDNOTFIND, if nothing of the below has been done 7694 * - SCIP_CUTOFF, if node can be cutoff, 7695 * - SCIP_SEPARATED, if a cut has been added, 7696 * - SCIP_REDUCEDDOM, if a domain reduction has been found, 7697 * - SCIP_BRANCHED, if branching has been done, 7698 * - SCIP_REDUCEDDOM, if a variable got fixed (in an attempt to branch on it), 7699 * - SCIP_INFEASIBLE, if external branching candidates were registered 7700 */ 7701 static 7702 SCIP_RETCODE enforceConstraints( 7703 SCIP* scip, /**< SCIP data structure */ 7704 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 7705 SCIP_CONS** conss, /**< constraints to process */ 7706 int nconss, /**< number of constraints */ 7707 SCIP_SOL* sol, /**< solution to enforce (NULL for the LP solution) */ 7708 SCIP_Longint soltag, /**< tag of solution */ 7709 SCIP_Bool inenforcement, /**< whether we are in enforcement, and not just separation */ 7710 SCIP_Real maxrelconsviol, /**< largest scaled violation among all violated expr-constraints, only used if in enforcement */ 7711 SCIP_RESULT* result /**< pointer to store the result of the enforcing call */ 7712 ) 7713 { 7714 SCIP_CONSHDLRDATA* conshdlrdata; 7715 SCIP_EXPRITER* it; 7716 SCIP_Bool consenforced; /* whether any expression in constraint could be enforced */ 7717 int c; 7718 7719 assert(conshdlr != NULL); 7720 assert(conss != NULL || nconss == 0); 7721 assert(result != NULL); 7722 7723 conshdlrdata = SCIPconshdlrGetData(conshdlr); 7724 assert(conshdlrdata != NULL); 7725 7726 /* increase tag to tell whether branching scores in expression belong to this sweep 7727 * and which expressions have already been enforced in this sweep 7728 * (we also want to distinguish sepa rounds, so this need to be here and not in consEnfo) 7729 */ 7730 ++(conshdlrdata->enforound); 7731 7732 *result = SCIP_DIDNOTFIND; 7733 7734 SCIP_CALL( SCIPcreateExpriter(scip, &it) ); 7735 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, TRUE) ); 7736 7737 for( c = 0; c < nconss; ++c ) 7738 { 7739 assert(conss != NULL && conss[c] != NULL); 7740 7741 /* skip constraints that are not enabled or deleted */ 7742 if( !SCIPconsIsEnabled(conss[c]) || SCIPconsIsDeleted(conss[c]) ) 7743 continue; 7744 assert(SCIPconsIsActive(conss[c])); 7745 7746 /* skip constraints that have separation disabled if we are only in separation */ 7747 if( !inenforcement && !SCIPconsIsSeparationEnabled(conss[c]) ) 7748 continue; 7749 7750 /* skip non-violated constraints */ 7751 if( !isConsViolated(scip, conss[c]) ) 7752 continue; 7753 7754 ENFOLOG( 7755 { 7756 SCIP_CONSDATA* consdata; 7757 int i; 7758 consdata = SCIPconsGetData(conss[c]); 7759 assert(consdata != NULL); 7760 SCIPinfoMessage(scip, enfologfile, " constraint "); 7761 SCIP_CALL( SCIPprintCons(scip, conss[c], enfologfile) ); 7762 SCIPinfoMessage(scip, enfologfile, "\n with viol %g and point\n", getConsAbsViolation(conss[c])); 7763 for( i = 0; i < consdata->nvarexprs; ++i ) 7764 { 7765 SCIP_VAR* var; 7766 var = SCIPgetVarExprVar(consdata->varexprs[i]); 7767 SCIPinfoMessage(scip, enfologfile, " %-10s = %15g bounds: [%15g,%15g]\n", SCIPvarGetName(var), 7768 SCIPgetSolVal(scip, sol, var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)); 7769 } 7770 }) 7771 7772 SCIP_CALL( enforceConstraint(scip, conshdlr, conss[c], sol, soltag, it, FALSE, inenforcement, result, &consenforced) ); 7773 7774 if( *result == SCIP_CUTOFF ) 7775 break; 7776 7777 if( !consenforced && inenforcement ) 7778 { 7779 SCIP_Real viol; 7780 7781 SCIP_CALL( getConsRelViolation(scip, conss[c], &viol, sol, soltag) ); 7782 if( viol > conshdlrdata->weakcutminviolfactor * maxrelconsviol ) 7783 { 7784 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " constraint <%s> could not be enforced, try again with weak "\ 7785 "cuts allowed\n", SCIPconsGetName(conss[c])); ) 7786 7787 SCIP_CALL( enforceConstraint(scip, conshdlr, conss[c], sol, soltag, it, TRUE, inenforcement, result, &consenforced) ); 7788 7789 if( consenforced ) 7790 ++conshdlrdata->nweaksepa; /* TODO maybe this should not be counted per constraint, but per enforcement round? */ 7791 7792 if( *result == SCIP_CUTOFF ) 7793 break; 7794 } 7795 } 7796 } 7797 7798 SCIPfreeExpriter(&it); 7799 7800 ENFOLOG( if( enfologfile != NULL ) fflush( enfologfile); ) 7801 7802 /* if having branching scores, then propagate them from expressions with children to variable expressions */ 7803 if( *result == SCIP_BRANCHED ) 7804 { 7805 /* having result set to branched here means only that we have branching candidates, we still need to do the actual 7806 * branching 7807 */ 7808 SCIP_CALL( branching(scip, conshdlr, conss, nconss, maxrelconsviol, sol, soltag, result) ); 7809 7810 /* branching should either have branched: result == SCIP_BRANCHED, 7811 * or fixed a variable: result == SCIP_REDUCEDDOM, 7812 * or have registered external branching candidates: result == SCIP_INFEASIBLE, 7813 * or have not done anything: result == SCIP_DIDNOTFIND 7814 */ 7815 assert(*result == SCIP_BRANCHED || *result == SCIP_REDUCEDDOM || *result == SCIP_INFEASIBLE || *result == SCIP_DIDNOTFIND); 7816 } 7817 7818 ENFOLOG( if( enfologfile != NULL ) fflush( enfologfile); ) 7819 7820 return SCIP_OKAY; 7821 } 7822 7823 /** collect (and print (if debugging enfo)) information on violation in expressions 7824 * 7825 * assumes that constraint violations have been computed 7826 */ 7827 static 7828 SCIP_RETCODE analyzeViolation( 7829 SCIP* scip, /**< SCIP data structure */ 7830 SCIP_CONS** conss, /**< constraints */ 7831 int nconss, /**< number of constraints */ 7832 SCIP_SOL* sol, /**< solution to separate, or NULL if LP solution should be used */ 7833 SCIP_Longint soltag, /**< tag of solution */ 7834 SCIP_Real* maxabsconsviol, /**< buffer to store maximal absolute violation of constraints */ 7835 SCIP_Real* maxrelconsviol, /**< buffer to store maximal relative violation of constraints */ 7836 SCIP_Real* minauxviol, /**< buffer to store minimal (nonzero) violation of auxiliaries */ 7837 SCIP_Real* maxauxviol, /**< buffer to store maximal violation of auxiliaries (violation in "extended formulation") */ 7838 SCIP_Real* maxvarboundviol /**< buffer to store maximal violation of variable bounds */ 7839 ) 7840 { 7841 SCIP_CONSDATA* consdata; 7842 SCIP_EXPRITER* it; 7843 SCIP_EXPR* expr; 7844 SCIP_Real v; 7845 int c; 7846 7847 assert(conss != NULL || nconss == 0); 7848 assert(maxabsconsviol != NULL); 7849 assert(maxrelconsviol != NULL); 7850 assert(maxauxviol != NULL); 7851 assert(maxvarboundviol != NULL); 7852 7853 SCIP_CALL( SCIPcreateExpriter(scip, &it) ); 7854 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) ); 7855 7856 *maxabsconsviol = 0.0; 7857 *maxrelconsviol = 0.0; 7858 *minauxviol = SCIPinfinity(scip); 7859 *maxauxviol = 0.0; 7860 *maxvarboundviol = 0.0; 7861 7862 for( c = 0; c < nconss; ++c ) 7863 { 7864 assert(conss != NULL && conss[c] != NULL); 7865 7866 consdata = SCIPconsGetData(conss[c]); 7867 assert(consdata != NULL); 7868 7869 /* skip constraints that are not enabled, deleted, or have separation disabled */ 7870 if( !SCIPconsIsEnabled(conss[c]) || SCIPconsIsDeleted(conss[c]) || !SCIPconsIsSeparationEnabled(conss[c]) ) 7871 continue; 7872 assert(SCIPconsIsActive(conss[c])); 7873 7874 v = getConsAbsViolation(conss[c]); 7875 *maxabsconsviol = MAX(*maxabsconsviol, v); 7876 7877 /* skip non-violated constraints */ 7878 if( !isConsViolated(scip, conss[c]) ) 7879 continue; 7880 7881 SCIP_CALL( getConsRelViolation(scip, conss[c], &v, sol, soltag) ); 7882 *maxrelconsviol = MAX(*maxrelconsviol, v); 7883 7884 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) ) 7885 { 7886 SCIP_EXPR_OWNERDATA* ownerdata; 7887 SCIP_Real auxvarvalue; 7888 SCIP_Real auxvarlb; 7889 SCIP_Real auxvarub; 7890 SCIP_Bool violunder; 7891 SCIP_Bool violover; 7892 SCIP_Real origviol; 7893 SCIP_Real auxviol; 7894 int e; 7895 7896 ownerdata = SCIPexprGetOwnerData(expr); 7897 assert(ownerdata != NULL); 7898 7899 if( ownerdata->auxvar == NULL ) 7900 { 7901 /* check violation of variable bounds of original variable */ 7902 if( SCIPisExprVar(scip, expr) ) 7903 { 7904 SCIP_VAR* var; 7905 var = SCIPgetVarExprVar(expr); 7906 auxvarvalue = SCIPgetSolVal(scip, sol, var); 7907 auxvarlb = SCIPvarGetLbLocal(var); 7908 auxvarub = SCIPvarGetUbLocal(var); 7909 7910 origviol = 0.0; 7911 if( auxvarlb > auxvarvalue && !SCIPisInfinity(scip, -auxvarlb) ) 7912 origviol = auxvarlb - auxvarvalue; 7913 else if( auxvarub < auxvarvalue && !SCIPisInfinity(scip, auxvarub) ) 7914 origviol = auxvarvalue - auxvarub; 7915 if( origviol <= 0.0 ) 7916 continue; 7917 7918 *maxvarboundviol = MAX(*maxvarboundviol, origviol); 7919 7920 ENFOLOG( 7921 SCIPinfoMessage(scip, enfologfile, "var <%s>[%.15g,%.15g] = %.15g", SCIPvarGetName(var), auxvarlb, auxvarub, auxvarvalue); 7922 if( auxvarlb > auxvarvalue && !SCIPisInfinity(scip, -auxvarlb) ) 7923 SCIPinfoMessage(scip, enfologfile, " var >= lb violated by %g", auxvarlb - auxvarvalue); 7924 if( auxvarub < auxvarvalue && !SCIPisInfinity(scip, auxvarub) ) 7925 SCIPinfoMessage(scip, enfologfile, " var <= ub violated by %g", auxvarvalue - auxvarub); 7926 SCIPinfoMessage(scip, enfologfile, "\n"); 7927 ) 7928 } 7929 7930 continue; 7931 } 7932 7933 auxvarvalue = SCIPgetSolVal(scip, sol, ownerdata->auxvar); 7934 auxvarlb = SCIPvarGetLbLocal(ownerdata->auxvar); 7935 auxvarub = SCIPvarGetUbLocal(ownerdata->auxvar); 7936 7937 /* check violation of variable bounds of auxiliary variable */ 7938 if( auxvarlb - auxvarvalue > *maxvarboundviol && !SCIPisInfinity(scip, -auxvarlb) ) 7939 *maxvarboundviol = auxvarlb - auxvarvalue; 7940 else if( auxvarvalue - auxvarub > *maxvarboundviol && !SCIPisInfinity(scip, auxvarub) ) 7941 *maxvarboundviol = auxvarvalue - auxvarub; 7942 7943 origviol = getExprAbsOrigViolation(scip, expr, sol, &violunder, &violover); 7944 7945 ENFOLOG( 7946 if( origviol > 0.0 || auxvarlb > auxvarvalue || auxvarub < auxvarvalue ) 7947 { 7948 SCIPinfoMessage(scip, enfologfile, "expr "); 7949 SCIP_CALL( SCIPprintExpr(scip, expr, enfologfile) ); 7950 SCIPinfoMessage(scip, enfologfile, " (%p)[%.15g,%.15g] = %.15g\n", (void*)expr, SCIPexprGetActivity(expr).inf, SCIPexprGetActivity(expr).sup, SCIPexprGetEvalValue(expr)); 7951 7952 SCIPinfoMessage(scip, enfologfile, " auxvar <%s>[%.15g,%.15g] = %.15g", SCIPvarGetName(ownerdata->auxvar), auxvarlb, auxvarub, auxvarvalue); 7953 if( origviol > 0.0 ) 7954 SCIPinfoMessage(scip, enfologfile, " auxvar %s expr violated by %g", violunder ? ">=" : "<=", origviol); 7955 if( auxvarlb > auxvarvalue && !SCIPisInfinity(scip, -auxvarlb) ) 7956 SCIPinfoMessage(scip, enfologfile, " auxvar >= auxvar's lb violated by %g", auxvarlb - auxvarvalue); 7957 if( auxvarub < auxvarvalue && !SCIPisInfinity(scip, auxvarub) ) 7958 SCIPinfoMessage(scip, enfologfile, " auxvar <= auxvar's ub violated by %g", auxvarvalue - auxvarub); 7959 SCIPinfoMessage(scip, enfologfile, "\n"); 7960 } 7961 ) 7962 7963 /* no violation w.r.t. the original variables -> skip expression */ 7964 if( origviol == 0.0 ) 7965 continue; 7966 7967 /* compute aux-violation for each nonlinear handlers */ 7968 for( e = 0; e < ownerdata->nenfos; ++e ) 7969 { 7970 SCIP_NLHDLR* nlhdlr; 7971 7972 /* eval in auxvars is only defined for nlhdrs that separate; there might not even be auxvars otherwise */ 7973 if( (ownerdata->enfos[e]->nlhdlrparticipation & SCIP_NLHDLR_METHOD_SEPABOTH) == 0 ) 7974 continue; 7975 7976 nlhdlr = ownerdata->enfos[e]->nlhdlr; 7977 assert(nlhdlr != NULL); 7978 7979 /* evaluate the expression w.r.t. the nlhdlrs auxiliary variables */ 7980 SCIP_CALL( SCIPnlhdlrEvalaux(scip, nlhdlr, expr, ownerdata->enfos[e]->nlhdlrexprdata, &ownerdata->enfos[e]->auxvalue, sol) ); 7981 7982 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " nlhdlr <%s> = %.15g", SCIPnlhdlrGetName(nlhdlr), ownerdata->enfos[e]->auxvalue); ) 7983 7984 auxviol = getExprAbsAuxViolation(scip, expr, ownerdata->enfos[e]->auxvalue, sol, &violunder, &violover); 7985 7986 if( auxviol > 0.0 ) 7987 { 7988 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " auxvar %s nlhdlr-expr violated by %g", violover ? "<=" : ">=", auxviol); ) 7989 *maxauxviol = MAX(*maxauxviol, auxviol); 7990 *minauxviol = MIN(*minauxviol, auxviol); 7991 } 7992 ENFOLOG( SCIPinfoMessage(scip, enfologfile, "\n"); ) 7993 } 7994 } 7995 } 7996 7997 SCIPfreeExpriter(&it); 7998 7999 return SCIP_OKAY; 8000 } /*lint !e715*/ 8001 8002 /** enforcement of constraints called by enfolp and enforelax */ 8003 static 8004 SCIP_RETCODE consEnfo( 8005 SCIP* scip, /**< SCIP data structure */ 8006 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 8007 SCIP_CONS** conss, /**< constraints to process */ 8008 int nconss, /**< number of constraints */ 8009 SCIP_SOL* sol, /**< solution to enforce (NULL for the LP solution) */ 8010 SCIP_RESULT* result /**< pointer to store the result of the enforcing call */ 8011 ) 8012 { 8013 SCIP_CONSHDLRDATA* conshdlrdata; 8014 SCIP_Real maxabsconsviol; 8015 SCIP_Real maxrelconsviol; 8016 SCIP_Real minauxviol; 8017 SCIP_Real maxauxviol; 8018 SCIP_Real maxvarboundviol; 8019 SCIP_Longint soltag; 8020 int nnotify; 8021 int c; 8022 8023 conshdlrdata = SCIPconshdlrGetData(conshdlr); 8024 assert(conshdlr != NULL); 8025 8026 soltag = SCIPgetExprNewSoltag(scip); 8027 8028 *result = SCIP_FEASIBLE; 8029 for( c = 0; c < nconss; ++c ) 8030 { 8031 SCIP_CALL( computeViolation(scip, conss[c], sol, soltag) ); 8032 8033 if( isConsViolated(scip, conss[c]) ) 8034 *result = SCIP_INFEASIBLE; 8035 } 8036 8037 if( *result == SCIP_FEASIBLE ) 8038 { 8039 ENFOLOG( SCIPinfoMessage(scip, enfologfile, "node %lld: all expr-constraints feasible, skip enforcing\n", 8040 SCIPnodeGetNumber(SCIPgetCurrentNode(scip))); ) 8041 return SCIP_OKAY; 8042 } 8043 8044 SCIP_CALL( analyzeViolation(scip, conss, nconss, sol, soltag, &maxabsconsviol, &maxrelconsviol, 8045 &minauxviol, &maxauxviol, &maxvarboundviol) ); 8046 8047 ENFOLOG( SCIPinfoMessage(scip, enfologfile, "node %lld: enforcing constraints with max conssviol=%e (rel=%e), "\ 8048 "auxviolations in %g..%g, variable bounds violated by at most %g, LP feastol=%e\n", 8049 SCIPnodeGetNumber(SCIPgetCurrentNode(scip)), maxabsconsviol, maxrelconsviol, minauxviol, maxauxviol, 8050 maxvarboundviol, SCIPgetLPFeastol(scip)); ) 8051 8052 assert(maxvarboundviol <= SCIPgetLPFeastol(scip)); 8053 8054 /* try to propagate */ 8055 if( conshdlrdata->propinenforce ) 8056 { 8057 SCIP_RESULT propresult; 8058 int nchgbds = 0; 8059 8060 SCIP_CALL( propConss(scip, conshdlr, conss, nconss, TRUE, &propresult, &nchgbds) ); 8061 8062 if( propresult == SCIP_CUTOFF || propresult == SCIP_REDUCEDDOM ) 8063 { 8064 *result = propresult; 8065 return SCIP_OKAY; 8066 } 8067 } 8068 8069 /* tighten the LP tolerance if violation in variables bounds is larger than aux-violation (max |expr - auxvar| over 8070 * all violated expr/auxvar in violated constraints) 8071 */ 8072 if( conshdlrdata->tightenlpfeastol && maxvarboundviol > maxauxviol && SCIPisPositive(scip, SCIPgetLPFeastol(scip)) && 8073 sol == NULL ) 8074 { 8075 SCIPsetLPFeastol(scip, MAX(SCIPepsilon(scip), MIN(maxvarboundviol / 2.0, SCIPgetLPFeastol(scip) / 2.0))); 8076 ++conshdlrdata->ntightenlp; 8077 8078 *result = SCIP_SOLVELP; 8079 8080 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " variable bound violation %g larger than auxiliary violation %g, "\ 8081 "reducing LP feastol to %g\n", maxvarboundviol, maxauxviol, SCIPgetLPFeastol(scip)); ) 8082 8083 return SCIP_OKAY; 8084 } 8085 8086 /* tighten the LP tolerance if violation in auxiliaries is below LP feastol, as we could have problems to find a cut 8087 * with violation above LP tolerance (especially when auxviolation is below 10*eps = ROWPREP_SCALEUP_VIOLNONZERO in misc_rowprep.c) 8088 */ 8089 if( conshdlrdata->tightenlpfeastol && maxauxviol < SCIPgetLPFeastol(scip) && SCIPisPositive(scip, SCIPgetLPFeastol(scip)) && sol == NULL ) 8090 { 8091 SCIPsetLPFeastol(scip, MAX(SCIPepsilon(scip), maxauxviol/2.0)); 8092 ++conshdlrdata->ntightenlp; 8093 8094 *result = SCIP_SOLVELP; 8095 8096 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " auxiliary violation %g below LP feastol, reducing LP feastol to %g\n", maxauxviol, SCIPgetLPFeastol(scip)); ) 8097 8098 return SCIP_OKAY; 8099 } 8100 8101 SCIP_CALL( enforceConstraints(scip, conshdlr, conss, nconss, sol, soltag, TRUE, maxrelconsviol, result) ); 8102 8103 if( *result == SCIP_CUTOFF || *result == SCIP_SEPARATED || *result == SCIP_REDUCEDDOM || *result == SCIP_BRANCHED || 8104 *result == SCIP_INFEASIBLE ) 8105 return SCIP_OKAY; 8106 8107 assert(*result == SCIP_DIDNOTFIND); 8108 8109 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " could not enforce violation %g in regular ways, LP feastol=%g, "\ 8110 "becoming desperate now...\n", maxabsconsviol, SCIPgetLPFeastol(scip)); ) 8111 8112 if( conshdlrdata->tightenlpfeastol && SCIPisPositive(scip, maxvarboundviol) && SCIPisPositive(scip, SCIPgetLPFeastol(scip)) && sol == NULL ) 8113 { 8114 SCIPsetLPFeastol(scip, MAX(SCIPepsilon(scip), MIN(maxvarboundviol / 2.0, SCIPgetLPFeastol(scip) / 2.0))); 8115 ++conshdlrdata->ntightenlp; 8116 8117 *result = SCIP_SOLVELP; 8118 8119 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " variable bounds are violated by more than eps, reduced LP "\ 8120 "feasibility tolerance to %g\n", SCIPgetLPFeastol(scip)); ) 8121 8122 return SCIP_OKAY; 8123 } 8124 8125 if( conshdlrdata->tightenlpfeastol && SCIPisPositive(scip, maxauxviol) && SCIPisPositive(scip, 8126 SCIPgetLPFeastol(scip)) && sol == NULL ) 8127 { 8128 /* try whether tighten the LP feasibility tolerance could help 8129 * maybe it is just some cut that hasn't been taken into account sufficiently 8130 * in the next enforcement round, we would then also allow even weaker cuts, as we want a minimal cut violation of LP's feastol 8131 * unfortunately, we do not know the current LP solution primal infeasibility, so sometimes this just repeats without effect 8132 * until the LP feastol reaches epsilon 8133 * (this is similar to the "tighten the LP tolerance if violation in auxiliaries is below LP feastol..." case above, but applies 8134 * when maxauxviol is above LP feastol) 8135 */ 8136 SCIPsetLPFeastol(scip, MAX(SCIPepsilon(scip), MIN(maxauxviol / 2.0, SCIPgetLPFeastol(scip) / 10.0))); 8137 ++conshdlrdata->ndesperatetightenlp; 8138 8139 *result = SCIP_SOLVELP; 8140 8141 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " reduced LP feasibility tolerance to %g and hope\n", SCIPgetLPFeastol(scip)); ) 8142 8143 return SCIP_OKAY; 8144 } 8145 8146 /* try to propagate, if not tried above TODO(?) allow to disable this as well */ 8147 if( !conshdlrdata->propinenforce ) 8148 { 8149 SCIP_RESULT propresult; 8150 int nchgbds = 0; 8151 8152 SCIP_CALL( propConss(scip, conshdlr, conss, nconss, TRUE, &propresult, &nchgbds) ); 8153 8154 if( propresult == SCIP_CUTOFF || propresult == SCIP_REDUCEDDOM ) 8155 { 8156 *result = propresult; 8157 return SCIP_OKAY; 8158 } 8159 } 8160 8161 /* could not find branching candidates even when looking at minimal violated (>eps) expressions 8162 * now look if we find any unfixed variable that we could still branch on 8163 */ 8164 SCIP_CALL( registerBranchingCandidatesAllUnfixed(scip, conshdlr, conss, nconss, &nnotify) ); 8165 8166 if( nnotify > 0 ) 8167 { 8168 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " registered %d unfixed variables as branching candidates\n", nnotify); ) 8169 ++conshdlrdata->ndesperatebranch; 8170 8171 *result = SCIP_INFEASIBLE; /* enforceConstraints may have changed it to SCIP_DIDNOTFIND */ 8172 8173 return SCIP_OKAY; 8174 } 8175 8176 /* if everything is fixed in violated constraints, then let's cut off the node 8177 * - bound tightening with all vars fixed should prove cutoff, but interval arithmetic overestimates and so the 8178 * result may not be conclusive (when constraint violations are small) 8179 * - if tightenlpfeastol=FALSE, then the LP solution that we try to enforce here may just not be within bounds 8180 * sufficiently (see st_e40) 8181 * - but if the LP solution is really within bounds and since variables are fixed, cutting off the node is actually 8182 * not "desperate", but a pretty obvious thing to do 8183 */ 8184 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " enforcement with max. violation %g failed; cutting off node\n", maxabsconsviol); ) 8185 *result = SCIP_CUTOFF; 8186 8187 /* it's only "desperate" if the LP solution does not coincide with variable fixings (should we use something tighter than epsilon here?) */ 8188 if( !SCIPisZero(scip, maxvarboundviol) ) 8189 ++conshdlrdata->ndesperatecutoff; 8190 8191 return SCIP_OKAY; 8192 } 8193 8194 /** separation for all violated constraints to be used by SEPA callbacks */ 8195 static 8196 SCIP_RETCODE consSepa( 8197 SCIP* scip, /**< SCIP data structure */ 8198 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 8199 SCIP_CONS** conss, /**< constraints to process */ 8200 int nconss, /**< number of constraints */ 8201 SCIP_SOL* sol, /**< solution to enforce (NULL for the LP solution) */ 8202 SCIP_RESULT* result /**< pointer to store the result of the enforcing call */ 8203 ) 8204 { 8205 SCIP_Longint soltag; 8206 SCIP_Bool haveviol = FALSE; 8207 int c; 8208 8209 *result = SCIP_DIDNOTFIND; 8210 8211 soltag = SCIPgetExprNewSoltag(scip); 8212 8213 /* compute violations */ 8214 for( c = 0; c < nconss; ++c ) 8215 { 8216 assert(conss[c] != NULL); 8217 8218 /* skip constraints that are not enabled, deleted, or have separation disabled */ 8219 if( !SCIPconsIsEnabled(conss[c]) || SCIPconsIsDeleted(conss[c]) || !SCIPconsIsSeparationEnabled(conss[c]) ) 8220 continue; 8221 assert(SCIPconsIsActive(conss[c])); 8222 8223 SCIP_CALL( computeViolation(scip, conss[c], sol, soltag) ); 8224 8225 if( isConsViolated(scip, conss[c]) ) 8226 haveviol = TRUE; 8227 } 8228 8229 /* if none of our constraints are violated, don't attempt separation */ 8230 if( !haveviol ) 8231 { 8232 ENFOLOG( SCIPinfoMessage(scip, enfologfile, "node %lld: skip separation of non-violated constraints\n", SCIPnodeGetNumber(SCIPgetCurrentNode(scip))); ) 8233 return SCIP_OKAY; 8234 } 8235 8236 ENFOLOG( SCIPinfoMessage(scip, enfologfile, "node %lld: separation\n", SCIPnodeGetNumber(SCIPgetCurrentNode(scip))); ) 8237 8238 /* call separation */ 8239 SCIP_CALL( enforceConstraints(scip, conshdlr, conss, nconss, sol, soltag, FALSE, SCIP_INVALID, result) ); 8240 8241 return SCIP_OKAY; 8242 } 8243 8244 /** hash key retrieval function for bilinear term entries */ 8245 static 8246 SCIP_DECL_HASHGETKEY(bilinearTermsGetHashkey) 8247 { /*lint --e{715}*/ 8248 SCIP_CONSHDLRDATA* conshdlrdata; 8249 int idx; 8250 8251 conshdlrdata = (SCIP_CONSHDLRDATA*)userptr; 8252 assert(conshdlrdata != NULL); 8253 8254 idx = ((int)(size_t)elem) - 1; 8255 assert(idx >= 0 && idx < conshdlrdata->nbilinterms); 8256 8257 return (void*)&conshdlrdata->bilinterms[idx]; 8258 } 8259 8260 /** returns TRUE iff the bilinear term entries are equal */ 8261 static 8262 SCIP_DECL_HASHKEYEQ(bilinearTermsIsHashkeyEq) 8263 { /*lint --e{715}*/ 8264 SCIP_CONSNONLINEAR_BILINTERM* entry1; 8265 SCIP_CONSNONLINEAR_BILINTERM* entry2; 8266 8267 /* get corresponding entries */ 8268 entry1 = (SCIP_CONSNONLINEAR_BILINTERM*)key1; 8269 entry2 = (SCIP_CONSNONLINEAR_BILINTERM*)key2; 8270 assert(entry1->x != NULL && entry1->y != NULL); 8271 assert(entry2->x != NULL && entry2->y != NULL); 8272 assert(SCIPvarCompare(entry1->x, entry1->y) < 1); 8273 assert(SCIPvarCompare(entry2->x, entry2->y) < 1); 8274 8275 return entry1->x == entry2->x && entry1->y == entry2->y; 8276 } 8277 8278 /** returns the hash value of the key */ 8279 static 8280 SCIP_DECL_HASHKEYVAL(bilinearTermsGetHashkeyVal) 8281 { /*lint --e{715}*/ 8282 SCIP_CONSNONLINEAR_BILINTERM* entry; 8283 8284 entry = (SCIP_CONSNONLINEAR_BILINTERM*)key; 8285 assert(entry->x != NULL && entry->y != NULL); 8286 assert(SCIPvarCompare(entry->x, entry->y) < 1); 8287 8288 return SCIPhashTwo(SCIPvarGetIndex(entry->x), SCIPvarGetIndex(entry->y)); 8289 } 8290 8291 /** compare two auxiliary expressions 8292 * 8293 * Compares auxiliary variables, followed by coefficients, and then constants. 8294 */ 8295 static 8296 SCIP_DECL_SORTPTRCOMP(auxexprComp) 8297 { 8298 SCIP_CONSNONLINEAR_AUXEXPR* auxexpr1 = (SCIP_CONSNONLINEAR_AUXEXPR*)elem1; 8299 SCIP_CONSNONLINEAR_AUXEXPR* auxexpr2 = (SCIP_CONSNONLINEAR_AUXEXPR*)elem2; 8300 int compvars; 8301 int i; 8302 8303 /* compare the auxiliary variables */ 8304 compvars = SCIPvarCompare(auxexpr1->auxvar, auxexpr2->auxvar); /* TODO can one of these be NULL? */ 8305 8306 if( compvars != 0 ) 8307 return compvars; 8308 8309 /* compare the coefficients and constants */ 8310 for( i = 0; i < 3; ++i ) 8311 { 8312 if( auxexpr1->coefs[i] != auxexpr2->coefs[i] ) 8313 return auxexpr1->coefs[i] < auxexpr2->coefs[i] ? -1 : 1; 8314 } 8315 8316 return auxexpr1->cst < auxexpr2->cst ? -1 : auxexpr1->cst == auxexpr2->cst ? 0 : 1; 8317 } 8318 8319 /* add an auxiliary expression to a bilinear term */ 8320 static 8321 SCIP_RETCODE bilinTermAddAuxExpr( 8322 SCIP* scip, /**< SCIP data structure */ 8323 SCIP_CONSHDLRDATA* conshdlrdata, /**< nonlinear constraint handler data */ 8324 SCIP_CONSNONLINEAR_BILINTERM* term, /**< bilinear term */ 8325 SCIP_CONSNONLINEAR_AUXEXPR* auxexpr, /**< auxiliary expression to add */ 8326 SCIP_Bool* added /**< pointer to store whether auxexpr has been added */ 8327 ) 8328 { 8329 SCIP_Bool found; 8330 int pos; 8331 int i; 8332 8333 *added = FALSE; 8334 8335 /* check if auxexpr has already been added to term */ 8336 if( term->nauxexprs == 0 ) 8337 { 8338 found = FALSE; 8339 pos = 0; 8340 } 8341 else 8342 { 8343 found = SCIPsortedvecFindPtr((void**)term->aux.exprs, auxexprComp, auxexpr, term->nauxexprs, &pos); 8344 } 8345 8346 if( !found ) 8347 { 8348 if( term->nauxexprs >= conshdlrdata->bilinmaxnauxexprs ) 8349 return SCIP_OKAY; 8350 8351 SCIP_CALL( SCIPensureBlockMemoryArray(scip, &term->aux.exprs, &term->auxexprssize, term->nauxexprs + 1) ); 8352 assert(term->auxexprssize >= term->nauxexprs + 1); 8353 8354 /* insert expression at the correct position */ 8355 for( i = term->nauxexprs; i > pos; --i ) 8356 { 8357 term->aux.exprs[i] = term->aux.exprs[i-1]; 8358 } 8359 term->aux.exprs[pos] = auxexpr; 8360 ++(term->nauxexprs); 8361 *added = TRUE; 8362 } 8363 else 8364 { 8365 term->aux.exprs[pos]->underestimate |= auxexpr->underestimate; 8366 term->aux.exprs[pos]->overestimate |= auxexpr->overestimate; 8367 } 8368 8369 return SCIP_OKAY; 8370 } 8371 8372 /** iterates through all expressions of all nonlinear constraints and adds the corresponding bilinear terms to the hash table */ 8373 static 8374 SCIP_RETCODE bilinearTermsInsertAll( 8375 SCIP* scip, /**< SCIP data structure */ 8376 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 8377 SCIP_CONS** conss, /**< nonlinear constraints */ 8378 int nconss /**< total number of nonlinear constraints */ 8379 ) 8380 { 8381 SCIP_CONSHDLRDATA* conshdlrdata; 8382 SCIP_EXPRITER* it; 8383 int c; 8384 8385 assert(conss != NULL || nconss == 0); 8386 8387 if( nconss == 0 ) 8388 return SCIP_OKAY; 8389 8390 conshdlrdata = SCIPconshdlrGetData(conshdlr); 8391 assert(conshdlrdata != NULL); 8392 8393 /* check whether the bilinear terms have been stored already */ 8394 if( conshdlrdata->bilinterms != NULL ) 8395 return SCIP_OKAY; 8396 8397 /* create and initialize iterator */ 8398 SCIP_CALL( SCIPcreateExpriter(scip, &it) ); 8399 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) ); 8400 SCIPexpriterSetStagesDFS(it, SCIP_EXPRITER_ENTEREXPR); 8401 8402 /* iterate through all constraints */ 8403 for( c = 0; c < nconss; ++c ) 8404 { 8405 SCIP_CONSDATA* consdata; 8406 SCIP_EXPR* expr; 8407 8408 assert(conss != NULL && conss[c] != NULL); 8409 consdata = SCIPconsGetData(conss[c]); 8410 assert(consdata != NULL); 8411 8412 /* iterate through all expressions */ 8413 for( expr = SCIPexpriterRestartDFS(it, consdata->expr); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) ) 8414 { 8415 SCIP_EXPR** children = SCIPexprGetChildren(expr); 8416 SCIP_VAR* x = NULL; 8417 SCIP_VAR* y = NULL; 8418 8419 /* check whether the expression is of the form f(..)^2 */ 8420 if( SCIPisExprPower(scip, expr) && SCIPgetExponentExprPow(expr) == 2.0 ) 8421 { 8422 x = SCIPgetExprAuxVarNonlinear(children[0]); 8423 y = x; 8424 } 8425 /* check whether the expression is of the form f(..) * g(..) */ 8426 else if( SCIPisExprProduct(scip, expr) && SCIPexprGetNChildren(expr) == 2 ) 8427 { 8428 x = SCIPgetExprAuxVarNonlinear(children[0]); 8429 y = SCIPgetExprAuxVarNonlinear(children[1]); 8430 } 8431 8432 /* add variables to the hash table */ 8433 if( x != NULL && y != NULL ) 8434 { 8435 SCIP_CALL( SCIPinsertBilinearTermExistingNonlinear(scip, conshdlr, x, y, SCIPgetExprAuxVarNonlinear(expr), 8436 SCIPgetExprNLocksPosNonlinear(expr), SCIPgetExprNLocksNegNonlinear(expr)) ); 8437 } 8438 } 8439 } 8440 8441 /* release iterator */ 8442 SCIPfreeExpriter(&it); 8443 8444 return SCIP_OKAY; 8445 } 8446 8447 /** store x, y and the locks in a new bilinear term */ 8448 static 8449 SCIP_RETCODE bilinearTermsInsertEntry( 8450 SCIP* scip, /**< SCIP data structure */ 8451 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */ 8452 SCIP_VAR* x, /**< the first variable */ 8453 SCIP_VAR* y, /**< the second variable */ 8454 int nlockspos, /**< number of positive locks of the bilinear term */ 8455 int nlocksneg, /**< number of negative locks of the bilinear term */ 8456 int* idx, /**< pointer to store the position of the term in bilinterms array */ 8457 SCIP_Bool existing /**< whether the term exists explicitly in the problem */ 8458 ) 8459 { 8460 SCIP_CONSHDLRDATA* conshdlrdata; 8461 SCIP_CONSNONLINEAR_BILINTERM* term; 8462 8463 assert(conshdlr != NULL); 8464 assert(x != NULL); 8465 assert(y != NULL); 8466 assert(nlockspos >= 0); 8467 assert(nlocksneg >= 0); 8468 8469 conshdlrdata = SCIPconshdlrGetData(conshdlr); 8470 assert(conshdlrdata != NULL); 8471 8472 /* ensure that x.index <= y.index */ 8473 if( SCIPvarCompare(x, y) == 1 ) 8474 { 8475 SCIPswapPointers((void**)&x, (void**)&y); 8476 } 8477 assert(SCIPvarCompare(x, y) < 1); 8478 8479 *idx = SCIPgetBilinTermIdxNonlinear(conshdlr, x, y); 8480 8481 /* update or create the term */ 8482 if( *idx >= 0 ) 8483 { /* the term has already been added */ 8484 assert(conshdlrdata->bilinterms[*idx].x == x); 8485 assert(conshdlrdata->bilinterms[*idx].y == y); 8486 8487 /* get term and add locks */ 8488 term = &conshdlrdata->bilinterms[*idx]; 8489 assert(existing <= term->existing); /* implicit terms are added after existing ones */ 8490 term->nlockspos += nlockspos; 8491 term->nlocksneg += nlocksneg; 8492 } 8493 else 8494 { /* this is the first time we encounter this product */ 8495 /* ensure size of bilinterms array */ 8496 SCIP_CALL( SCIPensureBlockMemoryArray(scip, &conshdlrdata->bilinterms, &conshdlrdata->bilintermssize, conshdlrdata->nbilinterms + 1) ); 8497 8498 *idx = conshdlrdata->nbilinterms; 8499 8500 /* get term and set values in the created bilinear term */ 8501 term = &conshdlrdata->bilinterms[*idx]; 8502 assert(term != NULL); 8503 term->x = x; 8504 term->y = y; 8505 term->nauxexprs = 0; 8506 term->auxexprssize = 0; 8507 term->nlockspos = nlockspos; 8508 term->nlocksneg = nlocksneg; 8509 term->existing = existing; 8510 if( existing ) 8511 term->aux.var = NULL; 8512 else 8513 term->aux.exprs = NULL; 8514 8515 /* increase the total number of bilinear terms */ 8516 ++(conshdlrdata->nbilinterms); 8517 8518 /* save to the hashtable */ 8519 if( conshdlrdata->bilinhashtable == NULL ) 8520 { 8521 SCIP_CALL( SCIPhashtableCreate(&conshdlrdata->bilinhashtable, SCIPblkmem(scip), conshdlrdata->nbilinterms, 8522 bilinearTermsGetHashkey, bilinearTermsIsHashkeyEq, bilinearTermsGetHashkeyVal, 8523 (void*)conshdlrdata) ); 8524 } 8525 assert(conshdlrdata->bilinhashtable != NULL); 8526 8527 /* insert the index of the bilinear term into the hash table; note that the index of the i-th element is (i+1) 8528 * because zero can not be inserted into hash table 8529 */ 8530 SCIP_CALL( SCIPhashtableInsert(conshdlrdata->bilinhashtable, (void*)(size_t)(*idx + 1)) ); /*lint !e571 !e776*/ 8531 8532 /* capture product variables */ 8533 SCIP_CALL( SCIPcaptureVar(scip, x) ); 8534 SCIP_CALL( SCIPcaptureVar(scip, y) ); 8535 } 8536 8537 return SCIP_OKAY; 8538 } 8539 8540 /** frees array of bilinear terms and hash table */ 8541 static 8542 SCIP_RETCODE bilinearTermsFree( 8543 SCIP* scip, /**< SCIP data structure */ 8544 SCIP_CONSHDLRDATA* conshdlrdata /**< constraint handler data */ 8545 ) 8546 { 8547 int i; 8548 int j; 8549 8550 assert(conshdlrdata != NULL); 8551 8552 /* check whether bilinear terms have been stored */ 8553 if( conshdlrdata->bilinterms == NULL ) 8554 { 8555 assert(conshdlrdata->bilinterms == NULL); 8556 assert(conshdlrdata->nbilinterms == 0); 8557 assert(conshdlrdata->bilintermssize == 0); 8558 8559 return SCIP_OKAY; 8560 } 8561 8562 /* release variables */ 8563 for( i = 0; i < conshdlrdata->nbilinterms; ++i ) 8564 { 8565 SCIP_CALL( SCIPreleaseVar(scip, &conshdlrdata->bilinterms[i].y) ); 8566 SCIP_CALL( SCIPreleaseVar(scip, &conshdlrdata->bilinterms[i].x) ); 8567 8568 for( j = 0; j < conshdlrdata->bilinterms[i].nauxexprs; ++j ) 8569 { 8570 if( conshdlrdata->bilinterms[i].aux.exprs[j]->auxvar != NULL ) 8571 { 8572 SCIP_CALL( SCIPreleaseVar(scip, &conshdlrdata->bilinterms[i].aux.exprs[j]->auxvar) ); 8573 } 8574 SCIPfreeBlockMemory(scip, &(conshdlrdata->bilinterms[i].aux.exprs[j])); 8575 } 8576 8577 if( conshdlrdata->bilinterms[i].nauxexprs > 0 ) 8578 { 8579 SCIPfreeBlockMemoryArray(scip, &(conshdlrdata->bilinterms[i].aux.exprs), conshdlrdata->bilinterms[i].auxexprssize); 8580 continue; 8581 } 8582 8583 /* the rest is for simple terms with a single auxvar */ 8584 8585 /* it might be that there is a bilinear term without a corresponding auxiliary variable */ 8586 if( conshdlrdata->bilinterms[i].aux.var != NULL ) 8587 { 8588 SCIP_CALL( SCIPreleaseVar(scip, &conshdlrdata->bilinterms[i].aux.var) ); 8589 } 8590 } 8591 8592 /* free hash table */ 8593 if( conshdlrdata->bilinhashtable != NULL ) 8594 { 8595 SCIPhashtableFree(&conshdlrdata->bilinhashtable); 8596 } 8597 8598 /* free bilinterms array; reset counters */ 8599 SCIPfreeBlockMemoryArrayNull(scip, &conshdlrdata->bilinterms, conshdlrdata->bilintermssize); 8600 conshdlrdata->nbilinterms = 0; 8601 conshdlrdata->bilintermssize = 0; 8602 8603 return SCIP_OKAY; 8604 } 8605 8606 /* 8607 * vertex polyhedral separation 8608 */ 8609 8610 /** builds LP used to compute facets of the convex envelope of vertex-polyhedral functions */ 8611 static 8612 SCIP_RETCODE buildVertexPolyhedralSeparationLP( 8613 SCIP* scip, /**< SCIP data structure */ 8614 int nvars, /**< number of (unfixed) variables in vertex-polyhedral functions */ 8615 SCIP_LPI** lp /**< pointer to store created LP */ 8616 ) 8617 { 8618 SCIP_Real* obj; 8619 SCIP_Real* lb; 8620 SCIP_Real* ub; 8621 SCIP_Real* val; 8622 int* beg; 8623 int* ind; 8624 unsigned int nnonz; 8625 unsigned int ncols; 8626 unsigned int nrows; 8627 unsigned int i; 8628 unsigned int k; 8629 8630 assert(scip != NULL); 8631 assert(lp != NULL); 8632 assert(nvars > 0); 8633 assert(nvars <= SCIP_MAXVERTEXPOLYDIM); 8634 8635 SCIPdebugMsg(scip, "Building LP for computing facets of convex envelope of vertex-polyhedral function\n"); 8636 8637 /* create lpi to store the LP */ 8638 SCIP_CALL( SCIPlpiCreate(lp, SCIPgetMessagehdlr(scip), "facet finding LP", SCIP_OBJSEN_MINIMIZE) ); 8639 8640 nrows = (unsigned int)nvars + 1; 8641 ncols = POWEROFTWO((unsigned int)nvars); 8642 nnonz = (ncols * (nrows + 1)) / 2; 8643 8644 /* allocate necessary memory; set obj, lb, and ub to zero */ 8645 SCIP_CALL( SCIPallocClearBufferArray(scip, &obj, ncols) ); 8646 SCIP_CALL( SCIPallocClearBufferArray(scip, &lb, ncols) ); 8647 SCIP_CALL( SCIPallocBufferArray(scip, &ub, ncols) ); 8648 SCIP_CALL( SCIPallocBufferArray(scip, &beg, ncols) ); 8649 SCIP_CALL( SCIPallocBufferArray(scip, &val, nnonz) ); 8650 SCIP_CALL( SCIPallocBufferArray(scip, &ind, nnonz) ); 8651 8652 /* calculate nonzero entries in the LP */ 8653 for( i = 0, k = 0; i < ncols; ++i ) 8654 { 8655 int row; 8656 unsigned int a; 8657 8658 /* an upper bound of 1.0 is implied by the last row, but I presume that LP solvers prefer unbounded variables */ 8659 ub[i] = SCIPlpiInfinity(*lp); 8660 8661 SCIPdebugMsg(scip, "col %u starts at position %u\n", i, k); 8662 beg[i] = (int)k; 8663 row = 0; 8664 8665 /* iterate through the bit representation of i */ 8666 a = 1; 8667 while( a <= i ) 8668 { 8669 if( (a & i) != 0 ) 8670 { 8671 val[k] = 1.0; 8672 ind[k] = row; 8673 8674 SCIPdebugMsg(scip, " val[%d][%u] = 1 (position %u)\n", row, i, k); 8675 8676 ++k; 8677 } 8678 8679 a <<= 1; 8680 ++row; 8681 assert(0 <= row && row <= SCIP_MAXVERTEXPOLYDIM); 8682 assert(POWEROFTWO(row) == a); 8683 } 8684 8685 /* put 1 as a coefficient for sum_{i} \lambda_i = 1 row (last row) */ 8686 val[k] = 1.0; 8687 ind[k] = (int)nrows - 1; 8688 ++k; 8689 SCIPdebugMsg(scip, " val[%u][%u] = 1 (position %u)\n", nrows - 1, i, k); 8690 } 8691 assert(k == nnonz); 8692 8693 /* load all data into LP interface 8694 * we can assume nrows (=nvars+1) <= ncols (=2^nvars), so we can pass lb as dummy lhs and rhs 8695 */ 8696 assert(nrows <= ncols); 8697 SCIP_CALL( SCIPlpiLoadColLP(*lp, SCIP_OBJSEN_MINIMIZE, 8698 (int)ncols, obj, lb, ub, NULL, 8699 (int)nrows, lb, lb, NULL, 8700 (int)nnonz, beg, ind, val) ); 8701 8702 /* for the last row, we can set the rhs to 1.0 already */ 8703 ind[0] = (int)nrows - 1; 8704 val[0] = 1.0; 8705 SCIP_CALL( SCIPlpiChgSides(*lp, 1, ind, val, val) ); 8706 8707 /* free allocated memory */ 8708 SCIPfreeBufferArray(scip, &ind); 8709 SCIPfreeBufferArray(scip, &val); 8710 SCIPfreeBufferArray(scip, &beg); 8711 SCIPfreeBufferArray(scip, &ub); 8712 SCIPfreeBufferArray(scip, &lb); 8713 SCIPfreeBufferArray(scip, &obj); 8714 8715 return SCIP_OKAY; 8716 } 8717 8718 /** the given facet might not be a valid under(over)estimator, because of numerics and bad fixings; we compute \f$ 8719 * \max_{v \in V} f(v) - (\alpha v + \beta) \f$ (\f$\max_{v \in V} \alpha v + \beta - f(v) \f$) where \f$ V \f$ is the 8720 * set of vertices of the domain 8721 */ 8722 static 8723 SCIP_Real computeVertexPolyhedralMaxFacetError( 8724 SCIP* scip, /**< SCIP data structure */ 8725 SCIP_Bool overestimate, /**< whether we check for an over or underestimator */ 8726 SCIP_Real* funvals, /**< array containing the evaluation of the function at all corners, length: 2^nvars */ 8727 SCIP_Real* box, /**< box for which facet was computed, length: 2*nallvars */ 8728 int nallvars, /**< number of all variables */ 8729 int nvars, /**< number of unfixed variables */ 8730 int* nonfixedpos, /**< indices of unfixed variables, length: nvars */ 8731 SCIP_Real* facetcoefs, /**< current facet candidate's coefficients, length: nallvars */ 8732 SCIP_Real facetconstant /**< current facet candidate's constant, length: nallvars */ 8733 ) 8734 { 8735 SCIP_Real maxerror; 8736 SCIP_Real facetval; 8737 SCIP_Real funval; 8738 SCIP_Real error; 8739 unsigned int i; 8740 unsigned int ncorners; 8741 unsigned int prev; 8742 8743 assert(scip != NULL); 8744 assert(funvals != NULL); 8745 assert(box != NULL); 8746 assert(nonfixedpos != NULL); 8747 assert(facetcoefs != NULL); 8748 8749 ncorners = POWEROFTWO(nvars); 8750 maxerror = 0.0; 8751 8752 /* check the origin (all variables at lower bound) */ 8753 facetval = facetconstant; 8754 for( i = 0; i < (unsigned int) nallvars; ++i ) 8755 facetval += facetcoefs[i] * box[2*i]; 8756 8757 /* compute largest/smallest possible value of function, depending on whether we are over/under-estimating */ 8758 funval = funvals[0]; 8759 if( overestimate ) 8760 error = funval - facetval; 8761 else 8762 error = facetval - funval; 8763 8764 /* update maximum error */ 8765 maxerror = MAX(error, maxerror); 8766 8767 prev = 0; 8768 for( i = 1; i < ncorners; ++i ) 8769 { 8770 unsigned int gray; 8771 unsigned int diff; 8772 unsigned int pos; 8773 int origpos; 8774 8775 gray = i ^ (i >> 1); 8776 diff = gray ^ prev; 8777 8778 /* compute position of unique 1 of diff */ 8779 pos = 0; 8780 while( (diff >>= 1) != 0 ) 8781 ++pos; 8782 assert(pos < (unsigned int)nvars); 8783 8784 origpos = nonfixedpos[pos]; 8785 8786 if( gray > prev ) 8787 facetval += facetcoefs[origpos] * (box[2*origpos+1] - box[2*origpos]); 8788 else 8789 facetval -= facetcoefs[origpos] * (box[2*origpos+1] - box[2*origpos]); 8790 8791 /* compute largest/smallest possible value of function, depending on whether we are over/under-estimating */ 8792 funval = funvals[gray]; 8793 if( overestimate ) 8794 error = funval - facetval; 8795 else 8796 error = facetval - funval; 8797 8798 /* update maximum error */ 8799 maxerror = MAX(error, maxerror); 8800 8801 prev = gray; 8802 } 8803 8804 SCIPdebugMsg(scip, "maximum error of facet: %2.8e\n", maxerror); 8805 8806 return maxerror; 8807 } 8808 8809 /** computes a facet of the convex or concave envelope of a vertex polyhedral function by solving an LP */ /*lint -e{715}*/ 8810 static 8811 SCIP_RETCODE computeVertexPolyhedralFacetLP( 8812 SCIP* scip, /**< SCIP data structure */ 8813 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */ 8814 SCIP_Bool overestimate, /**< whether to compute facet of concave (TRUE) or convex (FALSE) envelope */ 8815 SCIP_Real* xstar, /**< point to be separated */ 8816 SCIP_Real* box, /**< box where to compute facet: should be lb_1, ub_1, lb_2, ub_2... */ 8817 int nallvars, /**< half of the length of box */ 8818 int* nonfixedpos, /**< indices of nonfixed variables */ 8819 SCIP_Real* funvals, /**< values of function in all corner points (w.r.t. nonfixed variables) */ 8820 int nvars, /**< number of nonfixed variables */ 8821 SCIP_Real targetvalue, /**< target value: no need to compute facet if value in xstar would be worse than this value */ 8822 SCIP_Bool* success, /**< buffer to store whether a facet could be computed successfully */ 8823 SCIP_Real* facetcoefs, /**< buffer to store coefficients of facet defining inequality; must be an zero'ed array of length at least nallvars */ 8824 SCIP_Real* facetconstant /**< buffer to store constant part of facet defining inequality */ 8825 ) 8826 { /*lint --e{715}*/ 8827 SCIP_CONSHDLRDATA* conshdlrdata; 8828 SCIP_LPI* lp; 8829 SCIP_Real* aux; /* used to transform x^* and then to store LP solution */ 8830 int* inds; 8831 int ncols; 8832 int nrows; 8833 int i; 8834 SCIP_Real facetvalue; 8835 SCIP_Real mindomwidth; 8836 SCIP_RETCODE lpsolveretcode; 8837 8838 assert(scip != NULL); 8839 assert(conshdlr != NULL); 8840 assert(xstar != NULL); 8841 assert(box != NULL); 8842 assert(nonfixedpos != NULL); 8843 assert(funvals != NULL); 8844 assert(nvars >= 0); 8845 assert(nvars <= SCIP_MAXVERTEXPOLYDIM); 8846 assert(success != NULL); 8847 assert(facetcoefs != NULL); 8848 assert(facetconstant != NULL); 8849 8850 *success = FALSE; 8851 8852 conshdlrdata = SCIPconshdlrGetData(conshdlr); 8853 assert(conshdlrdata != NULL); 8854 8855 if( conshdlrdata->vp_randnumgen == NULL && conshdlrdata->vp_maxperturb > 0.0 ) 8856 { 8857 SCIP_CALL( SCIPcreateRandom(scip, &conshdlrdata->vp_randnumgen, VERTEXPOLY_RANDNUMINITSEED, TRUE) ); 8858 } 8859 8860 /* construct an LP for this size, if not having one already */ 8861 if( conshdlrdata->vp_lp[nvars] == NULL ) 8862 { 8863 SCIP_CALL( buildVertexPolyhedralSeparationLP(scip, nvars, &conshdlrdata->vp_lp[nvars]) ); 8864 } 8865 lp = conshdlrdata->vp_lp[nvars]; 8866 assert(lp != NULL); 8867 8868 /* get number of cols and rows of separation lp */ 8869 SCIP_CALL( SCIPlpiGetNCols(lp, &ncols) ); 8870 SCIP_CALL( SCIPlpiGetNRows(lp, &nrows) ); 8871 8872 /* number of columns should equal the number of corners = 2^nvars */ 8873 assert(ncols == (int)POWEROFTWO(nvars)); 8874 8875 /* allocate necessary memory */ 8876 SCIP_CALL( SCIPallocBufferArray(scip, &aux, nrows) ); 8877 SCIP_CALL( SCIPallocBufferArray(scip, &inds, ncols) ); 8878 8879 /* 8880 * set up the described LP on the transformed space 8881 */ 8882 8883 for( i = 0; i < ncols; ++i ) 8884 inds[i] = i; 8885 8886 /* compute T^-1(x^*), i.e. T^-1(x^*)_i = (x^*_i - lb_i)/(ub_i - lb_i) */ 8887 mindomwidth = 2*SCIPinfinity(scip); 8888 for( i = 0; i < nrows-1; ++i ) 8889 { 8890 SCIP_Real solval; 8891 SCIP_Real lb; 8892 SCIP_Real ub; 8893 int varpos; 8894 8895 assert(i < nvars); 8896 8897 varpos = nonfixedpos[i]; 8898 lb = box[2 * varpos]; 8899 ub = box[2 * varpos + 1]; 8900 solval = xstar[varpos]; 8901 8902 if( ub - lb < mindomwidth ) 8903 mindomwidth = ub - lb; 8904 8905 /* explicitly handle solution which violate bounds of variables (this can happen because of tolerances) */ 8906 if( solval <= lb ) 8907 aux[i] = 0.0; 8908 else if( solval >= ub ) 8909 aux[i] = 1.0; 8910 else 8911 aux[i] = (solval - lb) / (ub - lb); 8912 8913 /* perturb point to hopefully obtain a facet of the convex envelope */ 8914 if( conshdlrdata->vp_maxperturb > 0.0 ) 8915 { 8916 assert(conshdlrdata->vp_randnumgen != NULL); 8917 8918 if( aux[i] == 1.0 ) 8919 aux[i] -= SCIPrandomGetReal(conshdlrdata->vp_randnumgen, 0.0, conshdlrdata->vp_maxperturb); 8920 else if( aux[i] == 0.0 ) 8921 aux[i] += SCIPrandomGetReal(conshdlrdata->vp_randnumgen, 0.0, conshdlrdata->vp_maxperturb); 8922 else 8923 { 8924 SCIP_Real perturbation; 8925 8926 perturbation = MIN( aux[i], 1.0 - aux[i] ) / 2.0; 8927 perturbation = MIN( perturbation, conshdlrdata->vp_maxperturb ); 8928 aux[i] += SCIPrandomGetReal(conshdlrdata->vp_randnumgen, -perturbation, perturbation); 8929 } 8930 assert(0.0 < aux[i] && aux[i] < 1.0); 8931 } 8932 8933 SCIPdebugMsg(scip, "LP row %d in [%e, %e]\n", i, aux[i], aux[i]); 8934 } 8935 8936 /* update LP */ 8937 SCIP_CALL( SCIPlpiChgObj(lp, ncols, inds, funvals) ); 8938 SCIP_CALL( SCIPlpiChgSides(lp, nrows-1, inds, aux, aux) ); 8939 SCIP_CALL( SCIPlpiChgObjsen(lp, overestimate ? SCIP_OBJSEN_MAXIMIZE : SCIP_OBJSEN_MINIMIZE) ); 8940 8941 /* we can stop the LP solve if will not meet the target value anyway, but only if xstar hasn't been perturbed */ 8942 if( conshdlrdata->vp_maxperturb == 0.0 && !SCIPisInfinity(scip, REALABS(targetvalue)) ) 8943 { 8944 SCIP_CALL( SCIPlpiSetRealpar(lp, SCIP_LPPAR_OBJLIM, targetvalue) ); 8945 } 8946 /* set an iteration limit so we do not run forever */ 8947 SCIP_CALL( SCIPlpiSetIntpar(lp, SCIP_LPPAR_LPITLIM, 100*ncols) ); 8948 /* since we work with the dual of the LP, primal feastol determines how much we want the computed facet to be the best possible one */ 8949 SCIP_CALL( SCIPlpiSetRealpar(lp, SCIP_LPPAR_FEASTOL, SCIPfeastol(scip)) ); 8950 /* since we work with the dual of the LP, dual feastol determines validity of the facet 8951 * if some ub-lb is small, we need higher accuracy, since below we divide coefs by ub-lb (we moved and scaled the box) 8952 * thus, we set the dual feastol to be between SCIPepsilon and SCIPfeastol 8953 */ 8954 SCIP_CALL( SCIPlpiSetRealpar(lp, SCIP_LPPAR_DUALFEASTOL, MIN(SCIPfeastol(scip), MAX(SCIPepsilon(scip), mindomwidth * SCIPfeastol(scip)))) ); 8955 8956 #ifdef SCIP_DEBUG 8957 SCIP_CALL( SCIPlpiSetIntpar(lp, SCIP_LPPAR_LPINFO, 1) ); 8958 #endif 8959 8960 /* 8961 * solve the LP and store the resulting facet for the transformed space 8962 */ 8963 if( conshdlrdata->vp_dualsimplex ) 8964 { 8965 lpsolveretcode = SCIPlpiSolveDual(lp); 8966 } 8967 else 8968 { 8969 lpsolveretcode = SCIPlpiSolvePrimal(lp); 8970 } 8971 if( lpsolveretcode == SCIP_LPERROR ) 8972 { 8973 SCIPdebugMsg(scip, "LP error, aborting.\n"); 8974 goto CLEANUP; 8975 } 8976 SCIP_CALL( lpsolveretcode ); 8977 8978 /* any dual feasible solution should provide a valid estimator (and a dual optimal one a facet) */ 8979 if( !SCIPlpiIsDualFeasible(lp) ) 8980 { 8981 SCIPdebugMsg(scip, "LP not solved to dual feasibility, aborting.\n"); 8982 goto CLEANUP; 8983 } 8984 8985 /* get dual solution (facet of convex envelope); again, we have to be careful since the LP can have more rows and 8986 * columns than needed, in particular, \bar \beta is the last dual multiplier 8987 */ 8988 SCIP_CALL( SCIPlpiGetSol(lp, NULL, NULL, aux, NULL, NULL) ); 8989 8990 for( i = 0; i < nvars; ++i ) 8991 facetcoefs[nonfixedpos[i]] = aux[i]; 8992 /* last dual multiplier is the constant */ 8993 *facetconstant = aux[nrows - 1]; 8994 8995 #ifdef SCIP_DEBUG 8996 SCIPdebugMsg(scip, "facet for the transformed problem: "); 8997 for( i = 0; i < nallvars; ++i ) 8998 { 8999 SCIPdebugMsgPrint(scip, "%3.4e * x%d + ", facetcoefs[i], i); 9000 } 9001 SCIPdebugMsgPrint(scip, "%3.4e\n", *facetconstant); 9002 #endif 9003 9004 /* 9005 * transform the facet to original space and compute value at x^*, i.e., alpha x + beta 9006 */ 9007 9008 SCIPdebugMsg(scip, "facet in orig. space: "); 9009 9010 facetvalue = 0.0; 9011 for( i = 0; i < nvars; ++i ) 9012 { 9013 SCIP_Real lb; 9014 SCIP_Real ub; 9015 int varpos; 9016 9017 varpos = nonfixedpos[i]; 9018 lb = box[2 * varpos]; 9019 ub = box[2 * varpos + 1]; 9020 assert(!SCIPisEQ(scip, lb, ub)); 9021 9022 /* alpha_i := alpha_bar_i / (ub_i - lb_i) */ 9023 facetcoefs[varpos] = facetcoefs[varpos] / (ub - lb); 9024 9025 /* beta = beta_bar - sum_i alpha_i * lb_i */ 9026 *facetconstant -= facetcoefs[varpos] * lb; 9027 9028 /* evaluate */ 9029 facetvalue += facetcoefs[varpos] * xstar[varpos]; 9030 9031 SCIPdebugMsgPrint(scip, "%3.4e * x%d + ", facetcoefs[varpos], varpos); 9032 } 9033 SCIPdebugMsgPrint(scip, "%3.4e ", *facetconstant); 9034 9035 /* add beta to the facetvalue: at this point in the code, facetvalue = g(x^*) */ 9036 facetvalue += *facetconstant; 9037 9038 SCIPdebugMsgPrint(scip, "has value %g, target = %g\n", facetvalue, targetvalue); 9039 9040 /* if overestimate, then we want facetvalue < targetvalue 9041 * if underestimate, then we want facetvalue > targetvalue 9042 * if none holds, give up 9043 * so maybe here we should check against the minimal violation 9044 */ 9045 if( overestimate == (facetvalue > targetvalue) ) 9046 { 9047 SCIPdebugMsg(scip, "missed the target, facetvalue %g targetvalue %g, overestimate=%u\n", facetvalue, targetvalue, overestimate); 9048 goto CLEANUP; 9049 } 9050 9051 /* if we made it until here, then we have a nice facet */ 9052 *success = TRUE; 9053 9054 CLEANUP: 9055 /* free allocated memory */ 9056 SCIPfreeBufferArray(scip, &inds); 9057 SCIPfreeBufferArray(scip, &aux); 9058 9059 return SCIP_OKAY; 9060 } 9061 9062 /** computes a facet of the convex or concave envelope of a univariate vertex polyhedral function 9063 * 9064 * In other words, compute the line that passes through two given points. 9065 */ 9066 static 9067 SCIP_RETCODE computeVertexPolyhedralFacetUnivariate( 9068 SCIP* scip, /**< SCIP data structure */ 9069 SCIP_Real left, /**< left coordinate */ 9070 SCIP_Real right, /**< right coordinate */ 9071 SCIP_Real funleft, /**< value of function in left coordinate */ 9072 SCIP_Real funright, /**< value of function in right coordinate */ 9073 SCIP_Bool* success, /**< buffer to store whether a facet could be computed successfully */ 9074 SCIP_Real* facetcoef, /**< buffer to store coefficient of facet defining inequality */ 9075 SCIP_Real* facetconstant /**< buffer to store constant part of facet defining inequality */ 9076 ) 9077 { 9078 assert(scip != NULL); 9079 assert(SCIPisLE(scip, left, right)); 9080 assert(!SCIPisInfinity(scip, -left)); 9081 assert(!SCIPisInfinity(scip, right)); 9082 assert(SCIPisFinite(funleft) && funleft != SCIP_INVALID); 9083 assert(SCIPisFinite(funright) && funright != SCIP_INVALID); 9084 assert(success != NULL); 9085 assert(facetcoef != NULL); 9086 assert(facetconstant != NULL); 9087 9088 *facetcoef = (funright - funleft) / (right - left); 9089 *facetconstant = funleft - *facetcoef * left; 9090 9091 *success = TRUE; 9092 9093 return SCIP_OKAY; 9094 } 9095 9096 /** given three points, constructs coefficient of equation for hyperplane generated by these three points 9097 * 9098 * Three points a, b, and c are given. 9099 * Computes coefficients alpha, beta, gamma, and delta, such that a, b, and c, satisfy 9100 * alpha * x1 + beta * x2 + gamma * x3 = delta and gamma >= 0.0. 9101 */ 9102 static 9103 SCIP_RETCODE computeHyperplaneThreePoints( 9104 SCIP* scip, /**< SCIP data structure */ 9105 SCIP_Real a1, /**< first coordinate of a */ 9106 SCIP_Real a2, /**< second coordinate of a */ 9107 SCIP_Real a3, /**< third coordinate of a */ 9108 SCIP_Real b1, /**< first coordinate of b */ 9109 SCIP_Real b2, /**< second coordinate of b */ 9110 SCIP_Real b3, /**< third coordinate of b */ 9111 SCIP_Real c1, /**< first coordinate of c */ 9112 SCIP_Real c2, /**< second coordinate of c */ 9113 SCIP_Real c3, /**< third coordinate of c */ 9114 SCIP_Real* alpha, /**< coefficient of first coordinate */ 9115 SCIP_Real* beta, /**< coefficient of second coordinate */ 9116 SCIP_Real* gamma_, /**< coefficient of third coordinate */ 9117 SCIP_Real* delta /**< constant right-hand side */ 9118 ) 9119 { 9120 assert(scip != NULL); 9121 assert(alpha != NULL); 9122 assert(beta != NULL); 9123 assert(gamma_ != NULL); 9124 assert(delta != NULL); 9125 9126 *alpha = -b3*c2 + a3*(-b2+c2) + a2*(b3-c3) + b2*c3; 9127 *beta = -(-b3*c1 + a3*(-b1+c1) + a1*(b3-c3) + b1*c3); 9128 *gamma_ = -a2*b1 + a1*b2 + a2*c1 - b2*c1 - a1*c2 + b1*c2; 9129 *delta = -a3*b2*c1 + a2*b3*c1 + a3*b1*c2 - a1*b3*c2 - a2*b1*c3 + a1*b2*c3; 9130 9131 /* SCIPdebugMsg(scip, "alpha: %g beta: %g gamma: %g delta: %g\n", *alpha, *beta, *gamma_, *delta); */ 9132 9133 if( SCIPisInfinity(scip, REALABS(*gamma_ * a3)) || 9134 SCIPisInfinity(scip, REALABS(*gamma_ * b3)) || 9135 SCIPisInfinity(scip, REALABS(*gamma_ * c3)) ) 9136 { 9137 SCIPdebugMsg(scip, "activity above SCIP infinity\n"); 9138 *delta = 0.0; 9139 *alpha = 0.0; 9140 *beta = 0.0; 9141 *gamma_ = 0.0; 9142 return SCIP_OKAY; 9143 } 9144 9145 /* check if hyperplane contains all three points (necessary because of numerical troubles) */ 9146 if( !SCIPisRelEQ(scip, *alpha * a1 + *beta * a2 - *delta, -*gamma_ * a3) || 9147 !SCIPisRelEQ(scip, *alpha * b1 + *beta * b2 - *delta, -*gamma_ * b3) || 9148 !SCIPisRelEQ(scip, *alpha * c1 + *beta * c2 - *delta, -*gamma_ * c3) ) 9149 { 9150 SCIP_Real m[9]; 9151 SCIP_Real rhs[3]; 9152 SCIP_Real x[3]; 9153 SCIP_Bool success; 9154 9155 /* 9156 SCIPdebugMsg(scip, "a = (%g,%g,%g) hyperplane: %g rhs %g EQdelta: %d\n", a1, a2, a3, *alpha * a1 + *beta * a2 - *delta, -*gamma_ * a3, SCIPisRelEQ(scip, *alpha * a1 + *beta * a2 - *delta, -*gamma_ * a3)); 9157 SCIPdebugMsg(scip, "b = (%g,%g,%g) hyperplane: %g rhs %g EQdelta: %d\n", b1, b2, b3, *alpha * b1 + *beta * b2 - *delta, -*gamma_ * b3, SCIPisRelEQ(scip, *alpha * b1 + *beta * b2 - *delta, -*gamma_ * b3)); 9158 SCIPdebugMsg(scip, "c = (%g,%g,%g) hyperplane: %g rhs %g EQdelta: %d\n", c1, c2, c3, *alpha * c1 + *beta * c2 - *delta, -*gamma_ * c3, SCIPisRelEQ(scip, *alpha * c1 + *beta * c2 - *delta, -*gamma_ * c3)); 9159 */ 9160 9161 /* initialize matrix column-wise */ 9162 m[0] = a1; 9163 m[1] = b1; 9164 m[2] = c1; 9165 m[3] = a2; 9166 m[4] = b2; 9167 m[5] = c2; 9168 m[6] = a3; 9169 m[7] = b3; 9170 m[8] = c3; 9171 9172 rhs[0] = 1.0; 9173 rhs[1] = 1.0; 9174 rhs[2] = 1.0; 9175 9176 SCIPdebugMsg(scip, "numerical troubles - try to solve the linear system via an LU factorization\n"); 9177 9178 /* solve the linear problem */ 9179 SCIP_CALL( SCIPlapackSolveLinearEquations(SCIPbuffer(scip), 3, m, rhs, x, &success) ); 9180 9181 *delta = rhs[0]; 9182 *alpha = x[0]; 9183 *beta = x[1]; 9184 *gamma_ = x[2]; 9185 9186 /* set all coefficients to zero if one of the points is not contained in the hyperplane; this ensures that we do 9187 * not add a cut to SCIP and that all assertions are trivially fulfilled 9188 */ 9189 if( !success || !SCIPisRelEQ(scip, *alpha * a1 + *beta * a2 - *delta, -*gamma_ * a3) || 9190 !SCIPisRelEQ(scip, *alpha * b1 + *beta * b2 - *delta, -*gamma_ * b3) || 9191 !SCIPisRelEQ(scip, *alpha * c1 + *beta * c2 - *delta, -*gamma_ * c3) ) /*lint !e774*/ 9192 { 9193 SCIPdebugMsg(scip, "could not resolve numerical difficulties\n"); 9194 *delta = 0.0; 9195 *alpha = 0.0; 9196 *beta = 0.0; 9197 *gamma_ = 0.0; 9198 } 9199 } 9200 9201 if( *gamma_ < 0.0 ) 9202 { 9203 *alpha = -*alpha; 9204 *beta = -*beta; 9205 *gamma_ = -*gamma_; 9206 *delta = -*delta; 9207 } 9208 9209 return SCIP_OKAY; 9210 } 9211 9212 /** computes a facet of the convex or concave envelope of a bivariate vertex polyhedral function */ 9213 static 9214 SCIP_RETCODE computeVertexPolyhedralFacetBivariate( 9215 SCIP* scip, /**< SCIP data structure */ 9216 SCIP_Bool overestimate, /**< whether to compute facet of concave (TRUE) or convex (FALSE) envelope */ 9217 SCIP_Real p1[2], /**< first vertex of box */ 9218 SCIP_Real p2[2], /**< second vertex of box */ 9219 SCIP_Real p3[2], /**< third vertex of box */ 9220 SCIP_Real p4[2], /**< forth vertex of box */ 9221 SCIP_Real p1val, /**< value in p1 */ 9222 SCIP_Real p2val, /**< value in p2 */ 9223 SCIP_Real p3val, /**< value in p3 */ 9224 SCIP_Real p4val, /**< value in p4 */ 9225 SCIP_Real xstar[2], /**< point to be separated */ 9226 SCIP_Real targetvalue, /**< target value: no need to compute facet if value in xstar would be worse than this value */ 9227 SCIP_Bool* success, /**< buffer to store whether a facet could be computed successfully */ 9228 SCIP_Real* facetcoefs, /**< buffer to store coefficients of facet defining inequality; must be an array of length at least 2 */ 9229 SCIP_Real* facetconstant /**< buffer to store constant part of facet defining inequality */ 9230 ) 9231 { 9232 SCIP_Real alpha, beta, gamma_, delta; 9233 SCIP_Real xstarval, candxstarval = 0.0; 9234 int leaveout; 9235 9236 assert(scip != NULL); 9237 assert(success != NULL); 9238 assert(SCIPisFinite(p1val) && p1val != SCIP_INVALID); 9239 assert(SCIPisFinite(p2val) && p2val != SCIP_INVALID); 9240 assert(SCIPisFinite(p3val) && p3val != SCIP_INVALID); 9241 assert(SCIPisFinite(p4val) && p4val != SCIP_INVALID); 9242 assert(facetcoefs != NULL); 9243 assert(facetconstant != NULL); 9244 9245 *success = FALSE; 9246 9247 /* if we want an underestimator, flip f(x,y), i.e., do as if we compute an overestimator for -f(x,y) */ 9248 if( !overestimate ) 9249 { 9250 p1val = -p1val; 9251 p2val = -p2val; 9252 p3val = -p3val; 9253 p4val = -p4val; 9254 targetvalue = -targetvalue; 9255 } 9256 9257 SCIPdebugMsg(scip, "p1 = (%g, %g), f(p1) = %g\n", p1[0], p1[1], p1val); 9258 SCIPdebugMsg(scip, "p2 = (%g, %g), f(p2) = %g\n", p2[0], p2[1], p2val); 9259 SCIPdebugMsg(scip, "p3 = (%g, %g), f(p3) = %g\n", p3[0], p3[1], p3val); 9260 SCIPdebugMsg(scip, "p4 = (%g, %g), f(p4) = %g\n", p4[0], p4[1], p4val); 9261 9262 /* Compute coefficients alpha, beta, gamma (>0), delta such that 9263 * alpha*x + beta*y + gamma*z = delta 9264 * is satisfied by at least three of the corner points (p1,f(p1)), ..., (p4,f(p4)) and 9265 * the fourth corner point lies below this hyperplane. 9266 * Since we assume that f is vertex-polyhedral, we then know that all points (x,y,f(x,y)) are below this hyperplane, i.e., 9267 * alpha*x + beta*y - delta <= -gamma * f(x,y), 9268 * or, equivalently, 9269 * -alpha/gamma*x - beta/gamma*y + delta/gamma >= f(x,y). 9270 */ 9271 for( leaveout = 1; leaveout <= 4; ++leaveout ) 9272 { 9273 switch( leaveout) 9274 { 9275 case 1 : 9276 /* get hyperplane through p2, p3, p4 */ 9277 SCIP_CALL( computeHyperplaneThreePoints(scip, p2[0], p2[1], p2val, p3[0], p3[1], p3val, p4[0], p4[1], p4val, 9278 &alpha, &beta, &gamma_, &delta) ); 9279 /* if not underestimating in p1, then go to next candidate */ 9280 if( alpha * p1[0] + beta * p1[1] + gamma_ * p1val - delta > 0.0 ) 9281 continue; 9282 break; 9283 9284 case 2 : 9285 /* get hyperplane through p1, p3, p4 */ 9286 SCIP_CALL( computeHyperplaneThreePoints(scip, p1[0], p1[1], p1val, p3[0], p3[1], p3val, p4[0], p4[1], p4val, 9287 &alpha, &beta, &gamma_, &delta) ); 9288 /* if not underestimating in p2, then go to next candidate */ 9289 if( alpha * p2[0] + beta * p2[1] + gamma_ * p2val - delta > 0.0 ) 9290 continue; 9291 break; 9292 9293 case 3 : 9294 /* get hyperplane through p1, p2, p4 */ 9295 SCIP_CALL( computeHyperplaneThreePoints(scip, p1[0], p1[1], p1val, p2[0], p2[1], p2val, p4[0], p4[1], p4val, 9296 &alpha, &beta, &gamma_, &delta) ); 9297 /* if not underestimating in p3, then go to next candidate */ 9298 if( alpha * p3[0] + beta * p3[1] + gamma_ * p3val - delta > 0.0 ) 9299 continue; 9300 break; 9301 9302 case 4 : 9303 /* get hyperplane through p1, p2, p3 */ 9304 SCIP_CALL( computeHyperplaneThreePoints(scip, p1[0], p1[1], p1val, p2[0], p2[1], p2val, p3[0], p3[1], p3val, 9305 &alpha, &beta, &gamma_, &delta) ); 9306 /* if not underestimating in p4, then stop */ 9307 if( alpha * p4[0] + beta * p4[1] + gamma_ * p4val - delta > 0.0 ) 9308 continue; 9309 break; 9310 9311 default: /* only for lint */ 9312 alpha = SCIP_INVALID; 9313 beta = SCIP_INVALID; 9314 gamma_ = SCIP_INVALID; 9315 delta = SCIP_INVALID; 9316 break; 9317 } 9318 9319 /* check if bad luck: should not happen if numerics are fine */ 9320 if( SCIPisZero(scip, gamma_) ) 9321 continue; 9322 assert(!SCIPisNegative(scip, gamma_)); 9323 9324 /* if coefficients become tiny because division by gamma makes them < SCIPepsilon(scip), then skip, too */ 9325 if( (!SCIPisZero(scip, alpha) && SCIPisZero(scip, alpha/gamma_)) || 9326 ( !SCIPisZero(scip, beta) && SCIPisZero(scip, beta/gamma_)) ) 9327 continue; 9328 9329 SCIPdebugMsg(scip, "alpha = %g, beta = %g, gamma = %g, delta = %g\n", alpha, beta, gamma_, delta); 9330 9331 /* value of hyperplane candidate in xstar */ 9332 xstarval = -alpha/gamma_ * xstar[0] -beta/gamma_ * xstar[1] + delta/gamma_; 9333 9334 /* if reaching target and first or better than previous candidate, then update */ 9335 if( xstarval <= targetvalue && (!*success || xstarval < candxstarval) ) 9336 { 9337 /* flip hyperplane */ 9338 if( !overestimate ) 9339 gamma_ = -gamma_; 9340 9341 facetcoefs[0] = -alpha/gamma_; 9342 facetcoefs[1] = -beta/gamma_; 9343 *facetconstant = delta/gamma_; 9344 9345 *success = TRUE; 9346 candxstarval = xstarval; 9347 } 9348 } 9349 9350 return SCIP_OKAY; 9351 } 9352 9353 /** ensures that we can store information about open expressions (i.e., not fully encoded in the symmetry detection 9354 * graph yet) in an array 9355 */ 9356 static 9357 SCIP_RETCODE ensureOpenArraySizeSymdetect( 9358 SCIP* scip, /**< SCIP pointer */ 9359 int** openidx, /**< address of openidx array */ 9360 int nelems, /**< number of elements that need to be stored */ 9361 int* maxnelems /**< pointer to store maximum number that can be stored */ 9362 ) 9363 { 9364 assert(scip != NULL); 9365 assert(openidx != NULL); 9366 assert(maxnelems != NULL); 9367 9368 if( nelems > *maxnelems ) 9369 { 9370 int newsize; 9371 9372 newsize = SCIPcalcMemGrowSize(scip, nelems); 9373 assert(newsize >= nelems); 9374 9375 SCIP_CALL( SCIPreallocBufferArray(scip, openidx, newsize) ); 9376 9377 *maxnelems = newsize; 9378 } 9379 9380 return SCIP_OKAY; 9381 } 9382 9383 /** ensures that we can store information about local variables in an array */ 9384 static 9385 SCIP_RETCODE ensureLocVarsArraySize( 9386 SCIP* scip, /**< SCIP pointer */ 9387 SCIP_VAR*** vars, /**< address of variable array */ 9388 SCIP_Real** vals, /**< address of value array */ 9389 int nelems, /**< number of elements that need to be stored */ 9390 int* maxnelems /**< pointer to store maximum number that can be stored */ 9391 ) 9392 { 9393 assert(scip != NULL); 9394 assert(vars != NULL); 9395 assert(vals != NULL); 9396 assert(maxnelems != NULL); 9397 9398 if( nelems > *maxnelems ) 9399 { 9400 int newsize; 9401 9402 newsize = SCIPcalcMemGrowSize(scip, nelems); 9403 assert(newsize > *maxnelems); 9404 9405 SCIP_CALL( SCIPreallocBufferArray(scip, vars, newsize) ); 9406 SCIP_CALL( SCIPreallocBufferArray(scip, vals, newsize) ); 9407 9408 *maxnelems = newsize; 9409 } 9410 9411 return SCIP_OKAY; 9412 } 9413 9414 /** tries to add gadget for finding signed permutations of bilinear products 9415 * 9416 * If a product has exactly two children being variables, negating both simultanteoulsy 9417 * is a signed permutation. 9418 */ 9419 static 9420 SCIP_RETCODE tryAddGadgetBilinearProductSignedPerm( 9421 SCIP* scip, /**< SCIP pointer */ 9422 SCIP_EXPR* expr, /**< product expression for which gadget is tried to be added */ 9423 SCIP_CONS* cons, /**< constraint containing product expression */ 9424 SYM_GRAPH* graph, /**< symmetry detection graph to be extended by gadget */ 9425 int parentidx, /**< index of parent node in symmetry detection graph for gadget */ 9426 SCIP_Bool hasparentcoef, /**< whether the parent gives a coefficient to the expression */ 9427 SCIP_Real parentcoef, /**< the parent coefficient (if it exists) */ 9428 SCIP_VAR*** consvars, /**< pointer to allocated array to store temporary variables */ 9429 SCIP_Real** consvals, /**< pointer to allocated arrat to store temporary values */ 9430 int* maxnconsvars, /**< pointer to maximum number consvars/consvals can hold */ 9431 SCIP_HASHSET* handledexprs, /**< hashset to store handled expressions */ 9432 SCIP_Bool* success /**< pointer to store whether gadget could be added successfully */ 9433 ) 9434 { 9435 SYM_EXPRDATA* symdata; 9436 SCIP_EXPR** children; 9437 SCIP_VAR* var1 = NULL; 9438 SCIP_VAR* var2 = NULL; 9439 SCIP_Real val1 = 0.0; 9440 SCIP_Real val2 = 0.0; 9441 SCIP_Real coef; 9442 SCIP_Real prodval; 9443 SCIP_Real constant; 9444 int nlocvars; 9445 int optype; 9446 int nchildren; 9447 int prodidx; 9448 int coefidx1; 9449 int coefidx2; 9450 int childidx; 9451 9452 assert(scip != NULL); 9453 assert(expr != NULL); 9454 assert(SCIPisExprProduct(scip, expr)); 9455 assert(graph != NULL); 9456 assert(0 <= parentidx && parentidx < SCIPgetSymgraphNNodes(graph)); 9457 assert(consvars != NULL); 9458 assert(consvals != NULL); 9459 assert(maxnconsvars != NULL); 9460 assert(*maxnconsvars > 0); 9461 assert(handledexprs != NULL); 9462 assert(success != NULL); 9463 9464 *success = FALSE; 9465 9466 /* we require exactly two children being variables */ 9467 nchildren = SCIPexprGetNChildren(expr); 9468 if( nchildren != 2 ) 9469 return SCIP_OKAY; 9470 9471 children = SCIPexprGetChildren(expr); 9472 if( !SCIPisExprVar(scip, children[0]) || !SCIPisExprVar(scip, children[1]) ) 9473 return SCIP_OKAY; 9474 9475 /* check whether each child is not multi-aggregated and is not shifted */ 9476 SCIP_CALL( ensureLocVarsArraySize(scip, consvars, consvals, SCIPexprGetNChildren(expr), maxnconsvars) ); 9477 9478 for( childidx = 0; childidx < 2; ++childidx ) 9479 { 9480 (*consvars)[0] = SCIPgetVarExprVar(children[childidx]); 9481 (*consvals)[0] = 1.0; 9482 nlocvars = 1; 9483 constant = 0.0; 9484 9485 SCIP_CALL( SCIPgetActiveVariables(scip, SYM_SYMTYPE_SIGNPERM, consvars, consvals, &nlocvars, 9486 &constant, SCIPconsIsTransformed(cons)) ); 9487 9488 if( nlocvars != 1 || !SCIPisZero(scip, constant) ) 9489 return SCIP_OKAY; 9490 9491 if( (SCIPisInfinity(scip, SCIPvarGetUbGlobal((*consvars)[0])) 9492 != SCIPisInfinity(scip, -SCIPvarGetLbGlobal((*consvars)[0]))) ) 9493 return SCIP_OKAY; 9494 9495 /* store information about variables */ 9496 if( childidx == 0 ) 9497 { 9498 var1 = (*consvars)[0]; 9499 val1 = (*consvals)[0]; 9500 } 9501 else 9502 { 9503 var2 = (*consvars)[0]; 9504 val2 = (*consvals)[0]; 9505 } 9506 } 9507 assert(var1 != NULL); 9508 assert(var2 != NULL); 9509 9510 /* store the we handle the children */ 9511 SCIP_CALL( SCIPhashsetInsert(handledexprs, SCIPblkmem(scip), (void*) children[0]) ); 9512 SCIP_CALL( SCIPhashsetInsert(handledexprs, SCIPblkmem(scip), (void*) children[1]) ); 9513 9514 SCIP_CALL( SCIPgetSymDataExpr(scip, expr, &symdata) ); 9515 assert(symdata != NULL); 9516 assert(SCIPgetSymExprdataNConstants(symdata) == 1); 9517 9518 coef = SCIPgetSymExprdataConstants(symdata)[0]; 9519 9520 SCIP_CALL( SCIPfreeSymDataExpr(scip, &symdata) ); 9521 9522 /* add gadget modeling the product 9523 * 9524 * Since the constants are 0, each variable is centered at the origin, which leads to 9525 * a product of the form \f$(\alpha x)\cdot(\gamma y)\f$. Manipulating the formula leads 9526 * to \f$\alpha \gamma (x \cdot y)\f$, which is modeled in a gadget that allows to 9527 * negate both variables simulataneously. 9528 */ 9529 SCIP_CALL( SCIPgetSymOpNodeType(scip, SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)), &optype) ); 9530 SCIP_CALL( SCIPaddSymgraphOpnode(scip, graph, optype, &prodidx) ); 9531 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, parentidx, prodidx, hasparentcoef, parentcoef) ); 9532 9533 prodval = coef * val1 * val2; 9534 9535 /* introduce nodes for the product value and its negation; since flipping both variables 9536 * simultaneously is a signed symmetry, assign both nodes the same value 9537 */ 9538 SCIP_CALL( SCIPaddSymgraphValnode(scip, graph, prodval, &coefidx1) ); 9539 SCIP_CALL( SCIPaddSymgraphValnode(scip, graph, prodval, &coefidx2) ); 9540 9541 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, prodidx, coefidx1, FALSE, 0.0) ); 9542 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, prodidx, coefidx2, FALSE, 0.0) ); 9543 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, coefidx1, coefidx2, FALSE, 0.0) ); 9544 9545 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, coefidx1, 9546 SCIPgetSymgraphVarnodeidx(scip, graph, var1), FALSE, 0.0) ); 9547 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, coefidx1, 9548 SCIPgetSymgraphVarnodeidx(scip, graph, var2), FALSE, 0.0) ); 9549 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, coefidx2, 9550 SCIPgetSymgraphNegatedVarnodeidx(scip, graph, var1), FALSE, 0.0) ); 9551 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, coefidx2, 9552 SCIPgetSymgraphNegatedVarnodeidx(scip, graph, var2), FALSE, 0.0) ); 9553 9554 *success = TRUE; 9555 9556 return SCIP_OKAY; 9557 } 9558 9559 /** returns whether an operator is even and, if yes, stores data about operator */ 9560 static 9561 SCIP_Bool isEvenOperator( 9562 SCIP* scip, /**< SCIP pointer */ 9563 SCIP_EXPR* expr, /**< expression corresponding to operator */ 9564 SCIP_Bool* hasvalue, /**< pointer to store whether even operator has a value 9565 * needed for symmetry computation */ 9566 SCIP_Real* value /**< pointer to store value for symmetry computation */ 9567 ) 9568 { 9569 SYM_EXPRDATA* symdata; 9570 9571 assert(scip != NULL); 9572 assert(expr != NULL); 9573 assert(hasvalue != NULL); 9574 assert(value != NULL); 9575 9576 /* check for different operators known to be even */ 9577 if( SCIPisExprSignpower(scip, expr) || SCIPisExprCos(scip, expr) ) 9578 { 9579 /* get remaining information needed for symmetry detection */ 9580 if( SCIPisExprSignpower(scip, expr) ) 9581 { 9582 SCIP_CALL_ABORT( SCIPgetSymDataExpr(scip, expr, &symdata) ); 9583 assert(symdata != NULL); 9584 assert(SCIPgetSymExprdataNConstants(symdata) == 1); 9585 9586 *value = SCIPgetSymExprdataConstants(symdata)[0]; 9587 *hasvalue = !SCIPisEQ(scip, *value, 1.0); 9588 9589 SCIP_CALL_ABORT( SCIPfreeSymDataExpr(scip, &symdata) ); 9590 } 9591 else 9592 { 9593 assert(SCIPisExprCos(scip, expr)); 9594 *hasvalue = FALSE; 9595 } 9596 9597 return TRUE; 9598 } 9599 else if( SCIPisExprPower(scip, expr) ) 9600 { 9601 SCIP_Real exponent; 9602 int safeexponent; 9603 9604 /* only consider expressions corresponding to an even power */ 9605 SCIP_CALL_ABORT( SCIPgetSymDataExpr(scip, expr, &symdata) ); 9606 assert(symdata != NULL); 9607 assert(SCIPgetSymExprdataNConstants(symdata) == 1); 9608 9609 exponent = SCIPgetSymExprdataConstants(symdata)[0]; 9610 SCIP_CALL_ABORT( SCIPfreeSymDataExpr(scip, &symdata) ); 9611 9612 /* check whether the exponent is an even integer */ 9613 if( !SCIPisIntegral(scip, exponent) || SCIPisLE(scip, exponent, 0.0) ) 9614 return FALSE; 9615 9616 /* deal with numerics */ 9617 safeexponent = (int) (exponent + 0.5); 9618 if( safeexponent % 2 != 0 ) 9619 return FALSE; 9620 9621 *hasvalue = TRUE; 9622 *value = exponent; 9623 9624 return TRUE; 9625 } 9626 else if( SCIPisExprAbs(scip, expr) ) 9627 { 9628 *hasvalue = FALSE; 9629 9630 return TRUE; 9631 } 9632 9633 return FALSE; 9634 } 9635 9636 /** returns whether a variable is centered at 0 */ 9637 static 9638 SCIP_Bool varIsCenteredAt0( 9639 SCIP* scip, /**< SCIP pointer */ 9640 SCIP_VAR* var /**< variable to be checked */ 9641 ) 9642 { 9643 assert(scip != NULL); 9644 assert(var != NULL); 9645 9646 if( (SCIPisInfinity(scip, SCIPvarGetUbGlobal(var)) != SCIPisInfinity(scip, -SCIPvarGetLbGlobal(var))) ) 9647 return FALSE; 9648 9649 if( SCIPisInfinity(scip, SCIPvarGetUbGlobal(var)) ) 9650 return TRUE; 9651 9652 if( SCIPisEQ(scip, SCIPvarGetUbGlobal(var), -SCIPvarGetLbGlobal(var)) ) 9653 return TRUE; 9654 9655 return FALSE; 9656 } 9657 9658 /** tries to add gadget for finding signed permutation of even univariate operators with variable child */ 9659 static 9660 SCIP_RETCODE tryAddGadgetEvenOperatorVariable( 9661 SCIP* scip, /**< SCIP pointer */ 9662 SCIP_EXPR* evenopexpr, /**< even operator expression for which gadget is tried to be added */ 9663 SCIP_EXPR* child, /**< child expression of evenopexpr */ 9664 SCIP_CONS* cons, /**< constraint containing expression */ 9665 SYM_GRAPH* graph, /**< symmetry detection graph to be extended by gadget */ 9666 int parentidx, /**< index of parent node in symmetry detection graph for gadget */ 9667 SCIP_Bool hasparentcoef, /**< whether the parent gives a coefficient to the expression */ 9668 SCIP_Real parentcoef, /**< the parent coefficient (if it exists) */ 9669 SCIP_Bool hassymval, /**< whether evenopexpr has a value needed for symmetry detection */ 9670 SCIP_Real symval, /**< value needed for symmetry detection (if hassymval is TRUE) */ 9671 SCIP_VAR*** consvars, /**< pointer to allocated array to store temporary variables */ 9672 SCIP_Real** consvals, /**< pointer to allocated arrat to store temporary values */ 9673 int* maxnconsvars, /**< pointer to maximum number consvars/consvals can hold */ 9674 SCIP_Bool* success /**< pointer to store whether gadget could be added successfully */ 9675 ) 9676 { 9677 SCIP_VAR* var; 9678 SCIP_Real constant; 9679 SCIP_Real edgeweight; 9680 int nlocvars; 9681 int nodeidx; 9682 int optype; 9683 int thisopidx; 9684 9685 assert(scip != NULL); 9686 assert(evenopexpr != NULL); 9687 assert(child != NULL); 9688 assert(SCIPisExprVar(scip, child)); 9689 assert(cons != NULL); 9690 assert(graph != NULL); 9691 assert(parentidx >= 0); 9692 assert(consvars != NULL); 9693 assert(consvals != NULL); 9694 assert(maxnconsvars != NULL); 9695 assert(success != NULL); 9696 9697 *success = FALSE; 9698 9699 /* check whether child variable is (multi-)aggregated */ 9700 var = SCIPgetVarExprVar(child); 9701 (*consvars)[0] = var; 9702 (*consvals)[0] = 1.0; 9703 constant = 0.0; 9704 nlocvars = 1; 9705 9706 SCIP_CALL( ensureLocVarsArraySize(scip, consvars, consvals, nlocvars, maxnconsvars) ); 9707 SCIP_CALL( SCIPgetActiveVariables(scip, SYM_SYMTYPE_SIGNPERM, consvars, consvals, &nlocvars, &constant, 9708 SCIPconsIsTransformed(cons)) ); 9709 9710 /* skip multi-aggregated variables or variables with domain not centered at 0 */ 9711 if( nlocvars != 1 || !SCIPisZero(scip, constant) ) 9712 return SCIP_OKAY; 9713 9714 if( !varIsCenteredAt0(scip, var) ) 9715 return SCIP_OKAY; 9716 9717 /* store partial information for gadget */ 9718 var = (*consvars)[0]; 9719 edgeweight = (*consvals)[0]; 9720 9721 /* add gadget to graph for even univariate expression */ 9722 *success = TRUE; 9723 9724 SCIP_CALL( SCIPgetSymOpNodeType(scip, SCIPexprhdlrGetName(SCIPexprGetHdlr(evenopexpr)), &optype) ); 9725 9726 SCIP_CALL( SCIPaddSymgraphOpnode(scip, graph, optype, &thisopidx) ); 9727 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, parentidx, thisopidx, hasparentcoef, parentcoef) ); 9728 9729 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, thisopidx, SCIPgetSymgraphVarnodeidx(scip, graph, var), 9730 TRUE, edgeweight) ); 9731 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, thisopidx, SCIPgetSymgraphNegatedVarnodeidx(scip, graph, var), 9732 TRUE, edgeweight) ); 9733 9734 if( hassymval ) 9735 { 9736 SCIP_CALL( SCIPaddSymgraphValnode(scip, graph, symval, &nodeidx) ); 9737 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, thisopidx, nodeidx, FALSE, 0.0) ); 9738 } 9739 9740 return SCIP_OKAY; 9741 } 9742 9743 /** tries to add gadget for finding signed permutation of even univariate operators with sum child */ 9744 static 9745 SCIP_RETCODE tryAddGadgetEvenOperatorSum( 9746 SCIP* scip, /**< SCIP pointer */ 9747 SCIP_EXPR* evenopexpr, /**< even operator expression for which gadget is tried to be added */ 9748 SCIP_EXPR* child, /**< child expression of evenopexpr */ 9749 SCIP_CONS* cons, /**< constraint containing expression */ 9750 SYM_GRAPH* graph, /**< symmetry detection graph to be extended by gadget */ 9751 int parentidx, /**< index of parent node in symmetry detection graph for gadget */ 9752 SCIP_Bool hasparentcoef, /**< whether the parent gives a coefficient to the expression */ 9753 SCIP_Real parentcoef, /**< the parent coefficient (if it exists) */ 9754 SCIP_Bool hassymval, /**< whether evenopexpr has a value needed for symmetry detection */ 9755 SCIP_Real symval, /**< value needed for symmetry detection (if hassymval is TRUE) */ 9756 SCIP_VAR*** consvars, /**< pointer to allocated array to store temporary variables */ 9757 SCIP_Real** consvals, /**< pointer to allocated arrat to store temporary values */ 9758 int* maxnconsvars, /**< pointer to maximum number consvars/consvals can hold */ 9759 SCIP_HASHSET* handledexprs, /**< hashset to store handled expressions */ 9760 SCIP_Bool* success /**< pointer to store whether gadget could be added successfully */ 9761 ) 9762 { 9763 SCIP_VAR* var; 9764 SCIP_Real constant; 9765 SCIP_Real weight; 9766 int nlocvars; 9767 int nodeidx; 9768 int optype; 9769 int thisopidx; 9770 int i; 9771 9772 assert(scip != NULL); 9773 assert(evenopexpr != NULL); 9774 assert(child != NULL); 9775 assert(SCIPisExprSum(scip, child)); 9776 assert(cons != NULL); 9777 assert(graph != NULL); 9778 assert(parentidx >= 0); 9779 assert(consvars != NULL); 9780 assert(consvals != NULL); 9781 assert(maxnconsvars != NULL); 9782 assert(handledexprs != NULL); 9783 assert(success != NULL); 9784 9785 *success = FALSE; 9786 9787 /* check whether child variable is (multi-)aggregated and whether all children are variables */ 9788 nlocvars = SCIPexprGetNChildren(child); 9789 9790 SCIP_CALL( ensureLocVarsArraySize(scip, consvars, consvals, nlocvars, maxnconsvars) ); 9791 9792 for( i = 0; i < nlocvars; ++i) 9793 { 9794 if( SCIPisExprVar(scip, SCIPexprGetChildren(child)[i]) ) 9795 { 9796 (*consvars)[i] = SCIPgetVarExprVar(SCIPexprGetChildren(child)[i]); 9797 (*consvals)[i] = SCIPgetCoefsExprSum(child)[i]; 9798 } 9799 else 9800 return SCIP_OKAY; 9801 } 9802 constant = SCIPgetConstantExprSum(child); 9803 9804 SCIP_CALL( SCIPgetActiveVariables(scip, SYM_SYMTYPE_SIGNPERM, consvars, consvals, &nlocvars, &constant, 9805 SCIPconsIsTransformed(cons)) ); 9806 9807 /* we can only handle the case without constant and two variables with domain centered at origin */ 9808 if( nlocvars > 2 || !SCIPisZero(scip, constant) ) 9809 return SCIP_OKAY; 9810 assert(nlocvars > 0); 9811 9812 var = (*consvars)[0]; 9813 if( !varIsCenteredAt0(scip, var) ) 9814 return SCIP_OKAY; 9815 9816 if( nlocvars == 2 ) 9817 { 9818 var = (*consvars)[1]; 9819 if( !varIsCenteredAt0(scip, var) ) 9820 return SCIP_OKAY; 9821 } 9822 9823 /* add gadget to graph for even univariate expression that have a sum of at most two variables as child */ 9824 *success = TRUE; 9825 for( i = 0; i < SCIPexprGetNChildren(child); ++i ) 9826 { 9827 SCIP_CALL( SCIPhashsetInsert(handledexprs, SCIPblkmem(scip), (void*) SCIPexprGetChildren(child)[i]) ); 9828 } 9829 9830 SCIP_CALL( SCIPgetSymOpNodeType(scip, SCIPexprhdlrGetName(SCIPexprGetHdlr(evenopexpr)), &optype) ); 9831 9832 SCIP_CALL( SCIPaddSymgraphOpnode(scip, graph, optype, &thisopidx) ); 9833 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, parentidx, thisopidx, hasparentcoef, parentcoef) ); 9834 9835 if( hassymval ) 9836 { 9837 SCIP_CALL( SCIPaddSymgraphValnode(scip, graph, symval, &nodeidx) ); 9838 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, thisopidx, nodeidx, FALSE, 0.0) ); 9839 } 9840 9841 if( nlocvars == 1 ) 9842 { 9843 var = (*consvars)[0]; 9844 weight = (*consvals)[0]; 9845 9846 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, thisopidx, SCIPgetSymgraphVarnodeidx(scip, graph, var), 9847 TRUE, weight) ); 9848 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, thisopidx, SCIPgetSymgraphNegatedVarnodeidx(scip, graph, var), 9849 TRUE, weight) ); 9850 } 9851 else 9852 { 9853 int dummyidx1; 9854 int dummyidx2; 9855 9856 /* add dummy nodes for gadget */ 9857 SCIP_CALL( SCIPaddSymgraphOpnode(scip, graph, (int) SYM_CONSOPTYPE_SUM, &dummyidx1) ); 9858 SCIP_CALL( SCIPaddSymgraphOpnode(scip, graph, (int) SYM_CONSOPTYPE_SUM, &dummyidx2) ); 9859 9860 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, dummyidx1, thisopidx, FALSE, 0.0) ); 9861 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, dummyidx2, thisopidx, FALSE, 0.0) ); 9862 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, dummyidx1, dummyidx2, FALSE, 0.0) ); 9863 9864 /* connect dummy nodes with variables */ 9865 for( i = 0; i < 2; ++i) 9866 { 9867 var = (*consvars)[i]; 9868 weight = ABS((*consvals)[i]); 9869 9870 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, dummyidx1, SCIPgetSymgraphVarnodeidx(scip, graph, var), 9871 TRUE, weight) ); 9872 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, dummyidx2, SCIPgetSymgraphNegatedVarnodeidx(scip, graph, var), 9873 TRUE, weight) ); 9874 } 9875 } 9876 9877 return SCIP_OKAY; 9878 } 9879 9880 /** tries to add gadget for finding signed permutations of even univariate operators 9881 * 9882 * We handle two cases. First, if a univariate operator is even and has a variable 9883 * as child, negating the child is signed permutation. Second, the univariate operator 9884 * is even and has a weighted sum of two variables as child. 9885 */ 9886 static 9887 SCIP_RETCODE tryAddGadgetEvenOperator( 9888 SCIP* scip, /**< SCIP pointer */ 9889 SCIP_EXPR* expr, /**< expression for which gadget is tried to be added */ 9890 SCIP_CONS* cons, /**< constraint containing expression */ 9891 SYM_GRAPH* graph, /**< symmetry detection graph to be extended by gadget */ 9892 int parentidx, /**< index of parent node in symmetry detection graph for gadget */ 9893 SCIP_Bool hasparentcoef, /**< whether the parent gives a coefficient to the expression */ 9894 SCIP_Real parentcoef, /**< the parent coefficient (if it exists) */ 9895 SCIP_VAR*** consvars, /**< pointer to allocated array to store temporary variables */ 9896 SCIP_Real** consvals, /**< pointer to allocated arrat to store temporary values */ 9897 int* maxnconsvars, /**< pointer to maximum number consvars/consvals can hold */ 9898 SCIP_HASHSET* handledexprs, /**< hashset to store handled expressions */ 9899 SCIP_Bool* success /**< pointer to store whether gadget could be added successfully */ 9900 ) 9901 { 9902 SCIP_EXPR* child; 9903 SCIP_Real val = 0.0; 9904 SCIP_Bool hasval = FALSE; 9905 9906 assert(scip != NULL); 9907 assert(expr != NULL); 9908 assert(graph != NULL); 9909 assert(0 <= parentidx && parentidx < SCIPgetSymgraphNNodes(graph)); 9910 assert(consvars != NULL); 9911 assert(consvals != NULL); 9912 assert(maxnconsvars != NULL); 9913 assert(*maxnconsvars > 0); 9914 assert(handledexprs != NULL); 9915 assert(success != NULL); 9916 9917 *success = FALSE; 9918 9919 /* ignore variable or value expressions */ 9920 if( SCIPisExprVar(scip, expr) || SCIPisExprValue(scip, expr) || SCIPisExprVaridx(scip, expr) ) 9921 return SCIP_OKAY; 9922 assert(SCIPexprGetNChildren(expr) > 0); 9923 9924 /* ignore operators with too many children */ 9925 if( SCIPexprGetNChildren(expr) > 1 ) 9926 return SCIP_OKAY; 9927 9928 /* check whether operator is even */ 9929 if( !isEvenOperator(scip, expr, &hasval, &val) ) 9930 return SCIP_OKAY; 9931 9932 /* we can only treat the operator if its child is a variable or a sum */ 9933 child = SCIPexprGetChildren(expr)[0]; 9934 if( SCIPisExprVar(scip, child) ) 9935 { 9936 SCIP_CALL( tryAddGadgetEvenOperatorVariable(scip, expr, child, cons, graph, parentidx, hasparentcoef, parentcoef, 9937 hasval, val, consvars, consvals, maxnconsvars, success) ); 9938 } 9939 else if( SCIPisExprSum(scip, child) ) 9940 { 9941 SCIP_CALL( tryAddGadgetEvenOperatorSum(scip, expr, child, cons, graph, parentidx, hasparentcoef, parentcoef, 9942 hasval, val, consvars, consvals, maxnconsvars, handledexprs, success) ); 9943 } 9944 9945 if( *success ) 9946 { 9947 SCIP_CALL( SCIPhashsetInsert(handledexprs, SCIPblkmem(scip), (void*) child) ); 9948 } 9949 9950 return SCIP_OKAY; 9951 } 9952 9953 /** compares two variable pointers */ 9954 static 9955 SCIP_DECL_SORTINDCOMP(SCIPsortVarPtr) 9956 { /*lint --e{715}*/ 9957 SCIP_VAR** vars; 9958 SCIP_VAR* var1; 9959 SCIP_VAR* var2; 9960 9961 vars = (SCIP_VAR**) dataptr; 9962 9963 var1 = vars[ind1]; 9964 var2 = vars[ind2]; 9965 assert(var1 != NULL); 9966 assert(var2 != NULL); 9967 9968 /* sort variables by their unique index */ 9969 if( SCIPvarGetIndex(var1) < SCIPvarGetIndex(var2) ) 9970 return -1; 9971 if( SCIPvarGetIndex(var1) > SCIPvarGetIndex(var2) ) 9972 return 1; 9973 9974 return 0; 9975 } 9976 9977 /** gets domain center of a variable which has not semi-infinite domain */ 9978 static 9979 SCIP_Real getDomainCenter( 9980 SCIP* scip, /**< SCIP pointer */ 9981 SCIP_VAR* var /**< variable */ 9982 ) 9983 { 9984 SCIP_Real ub; 9985 SCIP_Real lb; 9986 9987 ub = SCIPvarGetUbGlobal(var); 9988 lb = SCIPvarGetLbGlobal(var); 9989 9990 assert( SCIPisInfinity(scip, ub) == SCIPisInfinity(scip, -lb) ); 9991 9992 if ( SCIPisInfinity(scip, ub) ) 9993 return 0.0; 9994 9995 return (ub + lb) / 2; 9996 } 9997 9998 /** tries to add gadget for finding signed permutations for squared differences in a sum expression */ 9999 static 10000 SCIP_RETCODE tryAddGadgetSquaredDifference( 10001 SCIP* scip, /**< SCIP pointer */ 10002 SCIP_EXPR* sumexpr, /**< sum expression */ 10003 SCIP_CONS* cons, /**< constraint containing the sum expression */ 10004 SYM_GRAPH* graph, /**< symmetry detection graph to be extended by gadget */ 10005 int sumnodeidx, /**< index of sum node in symmetry detection graph for gadget */ 10006 SCIP_VAR*** consvars, /**< pointer to allocated array to store temporary variables */ 10007 SCIP_Real** consvals, /**< pointer to allocated arrat to store temporary values */ 10008 int* maxnconsvars, /**< pointer to maximum number consvars/consvals can hold */ 10009 SCIP_HASHSET* handledexprs /**< hashset to store handled expressions */ 10010 ) 10011 { 10012 SYM_EXPRDATA* symdata; 10013 SCIP_EXPR** children; 10014 SCIP_EXPR** powexprs; 10015 SCIP_EXPR** prodexprs; 10016 SCIP_EXPR* child; 10017 SCIP_VAR** powvars; 10018 SCIP_VAR** prodvars; 10019 SCIP_VAR* actvar; 10020 SCIP_VAR* actvar2; 10021 SCIP_VAR* var; 10022 SCIP_VAR* var2; 10023 SCIP_Real* sumcoefs; 10024 SCIP_Real constant; 10025 SCIP_Real constant2; 10026 SCIP_Real val; 10027 SCIP_Real val2; 10028 SCIP_Bool* powexprused = NULL; 10029 int* powperm = NULL; 10030 int* prodperm = NULL; 10031 int nchildren; 10032 int nlocvars; 10033 int nodeidx; 10034 int coefnodeidx1; 10035 int coefnodeidx2; 10036 int cnt; 10037 int i; 10038 int j; 10039 int nterms; 10040 int npowexprs = 0; 10041 int nprodexprs = 0; 10042 int powcoef = 0; 10043 10044 assert(scip != NULL); 10045 assert(sumexpr != NULL); 10046 assert(cons != NULL); 10047 assert(SCIPisExprSum(scip, sumexpr)); 10048 assert(consvars != NULL); 10049 assert(consvals != NULL); 10050 assert(maxnconsvars != NULL); 10051 assert(*maxnconsvars > 0); 10052 assert(handledexprs != NULL); 10053 10054 /* iterate over sum expression and extract all power and product expressions */ 10055 sumcoefs = SCIPgetCoefsExprSum(sumexpr); 10056 children = SCIPexprGetChildren(sumexpr); 10057 nchildren = SCIPexprGetNChildren(sumexpr); 10058 SCIP_CALL( SCIPallocBufferArray(scip, &powexprs, nchildren) ); 10059 SCIP_CALL( SCIPallocBufferArray(scip, &prodexprs, 2 * nchildren) ); 10060 SCIP_CALL( SCIPallocBufferArray(scip, &powvars, nchildren) ); 10061 SCIP_CALL( SCIPallocBufferArray(scip, &prodvars, 2 * nchildren) ); 10062 10063 /* we scan for norm constraints, i.e., the number of powexpr needs to be twice the prodexpr */ 10064 /** @todo make this work in a more general case */ 10065 for( i = 0; i < nchildren; ++i ) 10066 { 10067 if( SCIPisExprPower(scip, children[i]) ) 10068 { 10069 SCIP_Real exponent; 10070 10071 /* we require a coefficient of +/- 1 from the sum and all power expressions have the same coefficient */ 10072 if( powcoef == 0 ) 10073 { 10074 if( SCIPisEQ(scip, sumcoefs[i], 1.0) || SCIPisEQ(scip, sumcoefs[i], -1.0) ) 10075 powcoef = (int) SCIPround(scip, sumcoefs[i]); 10076 } 10077 else if( !SCIPisEQ(scip, (SCIP_Real) powcoef, sumcoefs[i]) ) 10078 continue; 10079 10080 /* we only store power expressions if their child is a variable */ 10081 assert(SCIPexprGetNChildren(children[i]) == 1); 10082 child = SCIPexprGetChildren(children[i])[0]; 10083 if( !SCIPisExprVar(scip, child) ) 10084 continue; 10085 10086 /* the power is required to be a 2 */ 10087 SCIP_CALL( SCIPgetSymDataExpr(scip, children[i], &symdata) ); 10088 assert(symdata != NULL); 10089 assert(SCIPgetSymExprdataNConstants(symdata) == 1); 10090 10091 exponent = SCIPgetSymExprdataConstants(symdata)[0]; 10092 SCIP_CALL( SCIPfreeSymDataExpr(scip, &symdata) ); 10093 10094 if( !SCIPisEQ(scip, exponent, 2.0) ) 10095 continue; 10096 10097 /* we only store power expressions if the child is not multi-aggregated */ 10098 var = SCIPgetVarExprVar(child); 10099 if( SCIPvarGetStatus(var) != SCIP_VARSTATUS_MULTAGGR ) 10100 { 10101 powexprs[npowexprs] = children[i]; 10102 powvars[npowexprs++] = var; 10103 } 10104 } 10105 else if( SCIPisExprProduct(scip, children[i]) ) 10106 { 10107 /* we require a coefficient of +/- 2 from the sum and all product expressions have the same coefficient */ 10108 if( powcoef == 0 ) 10109 { 10110 if( SCIPisEQ(scip, sumcoefs[i], 2.0) || SCIPisEQ(scip, sumcoefs[i], -2.0) ) 10111 powcoef = (int) -SCIPround(scip, sumcoefs[i]); 10112 } 10113 else if( !SCIPisEQ(scip, (SCIP_Real) 2 * powcoef, -sumcoefs[i]) ) 10114 continue; 10115 10116 /* we only store power expressions if they have exactly two children being variables */ 10117 if( SCIPexprGetNChildren(children[i]) != 2 ) 10118 continue; 10119 if( !SCIPisExprVar(scip, SCIPexprGetChildren(children[i])[0]) 10120 || !SCIPisExprVar(scip, SCIPexprGetChildren(children[i])[1]) ) 10121 continue; 10122 10123 var = SCIPgetVarExprVar(SCIPexprGetChildren(children[i])[0]); 10124 var2 = SCIPgetVarExprVar(SCIPexprGetChildren(children[i])[1]); 10125 10126 /* we only store product expressions if the children are not multi-aggregated */ 10127 if( SCIPvarGetStatus(var) != SCIP_VARSTATUS_MULTAGGR 10128 && SCIPvarGetStatus(var2) != SCIP_VARSTATUS_MULTAGGR ) 10129 { 10130 prodexprs[nprodexprs] = children[i]; 10131 prodvars[nprodexprs++] = var; 10132 prodexprs[nprodexprs] = children[i]; 10133 prodvars[nprodexprs++] = var2; 10134 } 10135 } 10136 } 10137 10138 if( npowexprs == 0 || nprodexprs != npowexprs ) 10139 goto FREEMEMORY; 10140 10141 /* check whether the power variables and product variables match */ 10142 SCIP_CALL( SCIPallocBufferArray(scip, &powperm, nprodexprs) ); 10143 SCIP_CALL( SCIPallocBufferArray(scip, &prodperm, nprodexprs) ); 10144 10145 SCIPsort(powperm, SCIPsortVarPtr, (void*) powvars, npowexprs); 10146 SCIPsort(prodperm, SCIPsortVarPtr, (void*) prodvars, npowexprs); 10147 10148 for( i = 0; i < npowexprs; ++i ) 10149 { 10150 if( SCIPvarGetIndex(prodvars[prodperm[i]]) != SCIPvarGetIndex(powvars[powperm[i]]) ) 10151 goto FREEMEMORY; 10152 } 10153 10154 /* if we reach this line, the variables match: we have found a potential norm constraint */ 10155 assert(npowexprs % 2 == 0); 10156 nterms = npowexprs / 2; 10157 SCIP_CALL( SCIPallocClearBufferArray(scip, &powexprused, npowexprs) ); 10158 10159 /* add gadget of each squared difference term */ 10160 cnt = 0; 10161 for( i = 0; i < nterms; ++i ) 10162 { 10163 SCIP_Bool var1found = FALSE; 10164 SCIP_Bool var2found = FALSE; 10165 10166 (*consvals)[0] = 1.0; 10167 (*consvars)[0] = prodvars[cnt++]; 10168 constant = 0.0; 10169 nlocvars = 1; 10170 10171 SCIP_CALL( SCIPgetActiveVariables(scip, SYM_SYMTYPE_SIGNPERM, consvars, consvals, 10172 &nlocvars, &constant, SCIPconsIsTransformed(cons)) ); 10173 10174 if( nlocvars != 1 ) 10175 { 10176 ++cnt; 10177 continue; 10178 } 10179 actvar = (*consvars)[0]; 10180 val = (*consvals)[0]; 10181 10182 (*consvals)[0] = 1.0; 10183 (*consvars)[0] = prodvars[cnt++]; 10184 constant2 = 0.0; 10185 nlocvars = 1; 10186 10187 SCIP_CALL( SCIPgetActiveVariables(scip, SYM_SYMTYPE_SIGNPERM, consvars, consvals, 10188 &nlocvars, &constant2, SCIPconsIsTransformed(cons)) ); 10189 10190 if( nlocvars != 1 ) 10191 continue; 10192 actvar2 = (*consvars)[0]; 10193 val2 = (*consvals)[0]; 10194 10195 /* we cannot handle the pair of variables if their constant/scalar differs or one variable 10196 * cannot be centered at the origin or they are not centered around the same point 10197 */ 10198 if( !SCIPisEQ(scip, constant, constant2) || !SCIPisEQ(scip, val, val2) 10199 || (SCIPisInfinity(scip, SCIPvarGetUbGlobal(actvar)) != SCIPisInfinity(scip, -SCIPvarGetLbGlobal(actvar))) 10200 || (SCIPisInfinity(scip, SCIPvarGetUbGlobal(actvar2)) != SCIPisInfinity(scip, -SCIPvarGetLbGlobal(actvar2))) 10201 || (SCIPisInfinity(scip, SCIPvarGetUbGlobal(actvar)) != SCIPisInfinity(scip, SCIPvarGetLbGlobal(actvar2))) 10202 || !SCIPisEQ(scip, getDomainCenter(scip, actvar), getDomainCenter(scip, actvar2)) ) 10203 continue; 10204 10205 /* add gadget */ 10206 SCIP_CALL( SCIPaddSymgraphOpnode(scip, graph, (int) SYM_CONSOPTYPE_SQDIFF, &nodeidx) ); 10207 SCIP_CALL( SCIPaddSymgraphValnode(scip, graph, val, &coefnodeidx1) ); 10208 SCIP_CALL( SCIPaddSymgraphValnode(scip, graph, val2, &coefnodeidx2) ); 10209 10210 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, sumnodeidx, nodeidx, TRUE, (SCIP_Real) powcoef) ); 10211 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, nodeidx, coefnodeidx1, TRUE, (SCIP_Real) powcoef) ); 10212 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, nodeidx, coefnodeidx2, TRUE, (SCIP_Real) powcoef) ); 10213 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, coefnodeidx1, 10214 SCIPgetSymgraphVarnodeidx(scip, graph, actvar), FALSE, 0.0) ); 10215 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, coefnodeidx1, 10216 SCIPgetSymgraphVarnodeidx(scip, graph, actvar2), FALSE, 0.0) ); 10217 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, coefnodeidx2, 10218 SCIPgetSymgraphNegatedVarnodeidx(scip, graph, actvar), FALSE, 0.0) ); 10219 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, coefnodeidx2, 10220 SCIPgetSymgraphNegatedVarnodeidx(scip, graph, actvar2), FALSE, 0.0) ); 10221 10222 /* mark product expression as handled */ 10223 SCIP_CALL( SCIPhashsetInsert(handledexprs, SCIPblkmem(scip), (void*) prodexprs[2*i]) ); 10224 10225 /* find corresponding unused power expressions and mark them as handled */ 10226 for( j = 0; j < npowexprs && !(var1found && var2found); ++j ) 10227 { 10228 if( powexprused[j] ) 10229 continue; 10230 assert(cnt >= 2); 10231 10232 if( !var1found && powvars[j] == prodvars[cnt - 2] ) 10233 { 10234 SCIP_CALL( SCIPhashsetInsert(handledexprs, SCIPblkmem(scip), (void*) powexprs[j]) ); 10235 powexprused[j] = TRUE; 10236 } 10237 else if( !var2found && powvars[j] == prodvars[cnt - 1] ) 10238 { 10239 SCIP_CALL( SCIPhashsetInsert(handledexprs, SCIPblkmem(scip), (void*) powexprs[j]) ); 10240 powexprused[j] = TRUE; 10241 } 10242 } 10243 } 10244 10245 FREEMEMORY: 10246 SCIPfreeBufferArrayNull(scip, &powexprused); 10247 SCIPfreeBufferArrayNull(scip, &prodperm); 10248 SCIPfreeBufferArrayNull(scip, &powperm); 10249 SCIPfreeBufferArray(scip, &prodvars); 10250 SCIPfreeBufferArray(scip, &powvars); 10251 SCIPfreeBufferArray(scip, &prodexprs); 10252 SCIPfreeBufferArray(scip, &powexprs); 10253 10254 return SCIP_OKAY; 10255 } 10256 10257 /** adds symmetry information of constraint to a symmetry detection graph */ 10258 static 10259 SCIP_RETCODE addSymmetryInformation( 10260 SCIP* scip, /**< SCIP pointer */ 10261 SYM_SYMTYPE symtype, /**< type of symmetries that need to be added */ 10262 SCIP_CONS* cons, /**< constraint */ 10263 SYM_GRAPH* graph, /**< symmetry detection graph */ 10264 SCIP_Bool* success /**< pointer to store whether symmetry information could be added */ 10265 ) 10266 { /*lint --e{850}*/ 10267 SCIP_EXPRITER* it; 10268 SCIP_HASHSET* handledexprs; 10269 SCIP_EXPR* rootexpr; 10270 SCIP_EXPR* expr; 10271 SCIP_VAR** consvars; 10272 SCIP_Real* consvals; 10273 SCIP_Real constant; 10274 SCIP_Real parentcoef = 0.0; 10275 int* openidx; 10276 int maxnopenidx; 10277 int parentidx; 10278 int nconsvars; 10279 int maxnconsvars; 10280 int nlocvars; 10281 int nopenidx = 0; 10282 int consnodeidx; 10283 int nodeidx; 10284 int i; 10285 SCIP_Bool iscolored; 10286 SCIP_Bool hasparentcoef; 10287 10288 assert(scip != NULL); 10289 assert(cons != NULL); 10290 assert(graph != NULL); 10291 assert(success != NULL); 10292 10293 /* store lhs/rhs */ 10294 SCIP_CALL( SCIPaddSymgraphConsnode(scip, graph, cons, 10295 SCIPgetLhsNonlinear(cons), SCIPgetRhsNonlinear(cons), &consnodeidx) ); 10296 10297 rootexpr = SCIPgetExprNonlinear(cons); 10298 assert(rootexpr != NULL); 10299 10300 /* allocate arrays to store operators not completely handled yet (due to DFS) and variables in constraint */ 10301 expr = SCIPgetExprNonlinear(cons); 10302 assert(expr != NULL); 10303 10304 SCIP_CALL( SCIPcreateExpriter(scip, &it) ); 10305 SCIP_CALL( SCIPexpriterInit(it, expr, SCIP_EXPRITER_DFS, TRUE) ); 10306 SCIPexpriterSetStagesDFS(it, SCIP_EXPRITER_ENTEREXPR | SCIP_EXPRITER_LEAVEEXPR); 10307 10308 /* find potential number of nodes in graph */ 10309 maxnopenidx = 0; 10310 for( ; !SCIPexpriterIsEnd(it); (void) SCIPexpriterGetNext(it) ) 10311 { 10312 if( SCIPexpriterGetStageDFS(it) == SCIP_EXPRITER_LEAVEEXPR ) 10313 continue; 10314 10315 ++maxnopenidx; 10316 } 10317 10318 SCIP_CALL( SCIPallocBufferArray(scip, &openidx, maxnopenidx) ); 10319 10320 maxnconsvars = SCIPgetNVars(scip); 10321 SCIP_CALL( SCIPallocBufferArray(scip, &consvars, maxnconsvars) ); 10322 SCIP_CALL( SCIPallocBufferArray(scip, &consvals, maxnconsvars) ); 10323 10324 /* for finding special subexpressions, use hashset to store which expressions have been handled completely */ 10325 SCIP_CALL( SCIPhashsetCreate(&handledexprs, SCIPblkmem(scip), maxnopenidx) ); 10326 10327 /* iterate over expression tree and store nodes/edges */ 10328 expr = SCIPgetExprNonlinear(cons); /*lint !e838*/ 10329 SCIP_CALL( SCIPexpriterInit(it, expr, SCIP_EXPRITER_DFS, TRUE) ); 10330 SCIPexpriterSetStagesDFS(it, SCIP_EXPRITER_ENTEREXPR | SCIP_EXPRITER_LEAVEEXPR); 10331 10332 for( expr = SCIPexpriterGetCurrent(it); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) ) 10333 { 10334 /* if an expression has already been handled by an ancestor, increase iterator until we leave it */ 10335 if( !SCIPhashsetIsEmpty(handledexprs) && SCIPhashsetExists(handledexprs, expr) ) 10336 { 10337 SCIP_EXPR* baseexpr; 10338 10339 baseexpr = expr; 10340 while( SCIPexpriterGetStageDFS(it) != SCIP_EXPRITER_LEAVEEXPR || expr != baseexpr ) 10341 expr = SCIPexpriterGetNext(it); 10342 10343 SCIP_CALL( SCIPhashsetRemove(handledexprs, (void*) expr) ); 10344 10345 /* leave the expression */ 10346 continue; 10347 } 10348 10349 /* due to DFS and expression has not been handled by ancestor, remove expression from list of open expressions */ 10350 if( SCIPexpriterGetStageDFS(it) == SCIP_EXPRITER_LEAVEEXPR ) 10351 { 10352 --nopenidx; 10353 continue; 10354 } 10355 assert(SCIPexpriterGetStageDFS(it) == SCIP_EXPRITER_ENTEREXPR); 10356 10357 /* find parentidx */ 10358 if( expr == rootexpr ) 10359 parentidx = consnodeidx; 10360 else 10361 { 10362 assert(nopenidx >= 1); 10363 parentidx = openidx[nopenidx - 1]; 10364 } 10365 10366 /* possibly find a coefficient assigned to the expression by the parent */ 10367 hasparentcoef = FALSE; 10368 if ( expr != rootexpr ) 10369 { 10370 SCIP_CALL( SCIPgetCoefSymData(scip, expr, SCIPexpriterGetParentDFS(it), &parentcoef, &hasparentcoef) ); 10371 } 10372 10373 /* deal with different kinds of expressions and store them in the symmetry data structure */ 10374 if( SCIPisExprVar(scip, expr) ) 10375 { 10376 /* needed to correctly reset value when leaving expression */ 10377 SCIP_CALL( ensureOpenArraySizeSymdetect(scip, &openidx, nopenidx + 1, &maxnopenidx) ); 10378 10379 openidx[nopenidx++] = -1; 10380 10381 assert(maxnconsvars > 0); 10382 assert(parentidx > 0); 10383 10384 /* if the parent assigns the variable a coefficient, introduce an intermediate node */ 10385 if( hasparentcoef ) 10386 { 10387 SCIP_CALL( SCIPaddSymgraphOpnode(scip, graph, (int) SYM_CONSOPTYPE_COEF, &nodeidx) ); /*lint !e641*/ 10388 10389 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, parentidx, nodeidx, TRUE, parentcoef) ); /*lint !e644*/ 10390 parentidx = nodeidx; 10391 } 10392 10393 /* connect (aggregation of) variable expression with its parent */ 10394 nconsvars = 1; 10395 consvars[0] = SCIPgetVarExprVar(expr); 10396 consvals[0] = 1.0; 10397 constant = 0.0; 10398 10399 SCIP_CALL( SCIPgetActiveVariables(scip, symtype, &consvars, &consvals, 10400 &nconsvars, &constant, SCIPconsIsTransformed(cons)) ); 10401 10402 /* check whether variable is aggregated */ 10403 if( nconsvars > 1 || !SCIPisZero(scip, constant) || !SCIPisEQ(scip, consvals[0], 1.0) ) 10404 { 10405 int thisidx; 10406 10407 SCIP_CALL( SCIPaddSymgraphOpnode(scip, graph, (int) SYM_CONSOPTYPE_SUM, &thisidx) ); /*lint !e641*/ 10408 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, parentidx, thisidx, FALSE, 0.0) ); 10409 10410 parentidx = thisidx; 10411 } 10412 SCIP_CALL( SCIPaddSymgraphVarAggegration(scip, graph, parentidx, consvars, consvals, 10413 nconsvars, constant) ); 10414 } 10415 else if( SCIPisExprValue(scip, expr) ) 10416 { 10417 assert(parentidx > 0); 10418 10419 SCIP_CALL( SCIPaddSymgraphValnode(scip, graph, SCIPgetValueExprValue(expr), &nodeidx) ); 10420 10421 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, parentidx, nodeidx, hasparentcoef, parentcoef) ); 10422 10423 /* needed to correctly reset value when leaving expression */ 10424 SCIP_CALL( ensureOpenArraySizeSymdetect(scip, &openidx, nopenidx + 1, &maxnopenidx) ); 10425 10426 openidx[nopenidx++] = -1; 10427 } 10428 else 10429 { 10430 SCIP_Bool usedefaultgadget = TRUE; 10431 10432 assert(expr == rootexpr || parentidx > 0); 10433 assert(SCIPhashsetIsEmpty(handledexprs) || !SCIPhashsetExists(handledexprs, expr)); 10434 10435 if( SCIPisExprSum(scip, expr) ) 10436 { 10437 /* deal with sum expressions differently, because we can possibly aggregate linear sums */ 10438 SCIP_EXPR** children; 10439 int sumidx; 10440 int optype; 10441 int childidx = 0; 10442 10443 /* sums are handled by a special gadget */ 10444 usedefaultgadget = FALSE; 10445 10446 /* extract all children being variables and compute the sum of active variables expression */ 10447 nlocvars = 0; 10448 children = SCIPexprGetChildren(expr); 10449 10450 SCIP_CALL( ensureLocVarsArraySize(scip, &consvars, &consvals, SCIPexprGetNChildren(expr), &maxnconsvars) ); 10451 10452 for( childidx = 0; childidx < SCIPexprGetNChildren(expr); ++childidx ) 10453 { 10454 if( !SCIPisExprVar(scip, children[childidx]) ) 10455 continue; 10456 10457 consvars[nlocvars] = SCIPgetVarExprVar(children[childidx]); 10458 consvals[nlocvars++] = SCIPgetCoefsExprSum(expr)[childidx]; 10459 10460 /* store that we have already handled this expression */ 10461 SCIP_CALL( SCIPhashsetInsert(handledexprs, SCIPblkmem(scip), (void*) children[childidx]) ); 10462 } 10463 10464 constant = SCIPgetConstantExprSum(expr); 10465 10466 SCIP_CALL( SCIPgetActiveVariables(scip, symtype, &consvars, &consvals, 10467 &nlocvars, &constant, SCIPconsIsTransformed(cons)) ); 10468 10469 SCIP_CALL( SCIPgetSymOpNodeType(scip, SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)), &optype) ); 10470 10471 SCIP_CALL( SCIPaddSymgraphOpnode(scip, graph, optype, &sumidx) ); 10472 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, parentidx, sumidx, hasparentcoef, parentcoef) ); 10473 10474 /* add the linear part of the sum */ 10475 SCIP_CALL( SCIPaddSymgraphVarAggegration(scip, graph, sumidx, consvars, consvals, nlocvars, constant) ); 10476 10477 SCIP_CALL( ensureOpenArraySizeSymdetect(scip, &openidx, nopenidx + 1, &maxnopenidx) ); 10478 10479 /* check whether the sum encodes expressions of type \f$(x - y)^2\f$ */ 10480 if( symtype == SYM_SYMTYPE_SIGNPERM ) 10481 { 10482 SCIP_CALL( tryAddGadgetSquaredDifference(scip, expr, cons, graph, sumidx, 10483 &consvars, &consvals, &maxnconsvars, handledexprs) ); 10484 } 10485 10486 /* store sumidx for children that have not been treated */ 10487 openidx[nopenidx++] = sumidx; 10488 } 10489 else if( symtype == SYM_SYMTYPE_SIGNPERM && SCIPisExprProduct(scip, expr) ) 10490 { 10491 SCIP_Bool succ; 10492 10493 SCIP_CALL( tryAddGadgetBilinearProductSignedPerm(scip, expr, cons, graph, parentidx, hasparentcoef, 10494 parentcoef, &consvars, &consvals, &maxnconsvars, handledexprs, &succ) ); 10495 10496 if( succ ) 10497 { 10498 usedefaultgadget = FALSE; 10499 SCIP_CALL( SCIPhashsetInsert(handledexprs, SCIPblkmem(scip), (void*) expr) ); 10500 } 10501 } 10502 else if( symtype == SYM_SYMTYPE_SIGNPERM ) 10503 { 10504 SCIP_Bool succ; 10505 10506 /* we can find more signed permutations for even univariate operators */ 10507 SCIP_CALL( tryAddGadgetEvenOperator(scip, expr, cons, graph, parentidx, hasparentcoef, parentcoef, 10508 &consvars, &consvals, &maxnconsvars, handledexprs, &succ) ); 10509 10510 if( succ ) 10511 { 10512 usedefaultgadget = FALSE; 10513 SCIP_CALL( SCIPhashsetInsert(handledexprs, SCIPblkmem(scip), (void*) expr) ); 10514 } 10515 } 10516 10517 if( usedefaultgadget ) 10518 { 10519 int opidx; 10520 int optype; 10521 10522 SCIP_CALL( SCIPgetSymOpNodeType(scip, SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)), &optype) ); 10523 SCIP_CALL( SCIPaddSymgraphOpnode(scip, graph, optype, &opidx) ); 10524 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, parentidx, opidx, hasparentcoef, parentcoef) ); 10525 10526 /* possibly add constants of expression */ 10527 if( SCIPexprhdlrHasGetSymData(SCIPexprGetHdlr(expr)) ) 10528 { 10529 SYM_EXPRDATA* symdata; 10530 10531 SCIP_CALL( SCIPgetSymDataExpr(scip, expr, &symdata) ); 10532 assert(symdata != NULL); 10533 10534 /* if expression has multiple constants, assign colors to edges to distinguish them */ 10535 iscolored = SCIPgetSymExprdataNConstants(symdata) > 1 ? TRUE : FALSE; 10536 for( i = 0; i < SCIPgetSymExprdataNConstants(symdata); ++i ) 10537 { 10538 SCIP_CALL( SCIPaddSymgraphValnode(scip, graph, SCIPgetSymExprdataConstants(symdata)[i], &nodeidx) ); 10539 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, opidx, nodeidx, iscolored, (SCIP_Real) i+1) ); 10540 } 10541 10542 SCIP_CALL( SCIPfreeSymDataExpr(scip, &symdata) ); 10543 } 10544 10545 SCIP_CALL( ensureOpenArraySizeSymdetect(scip, &openidx, nopenidx + 1, &maxnopenidx) ); 10546 10547 openidx[nopenidx++] = opidx; 10548 } 10549 } 10550 } 10551 10552 SCIPhashsetFree(&handledexprs, SCIPblkmem(scip)); 10553 SCIPfreeBufferArray(scip, &consvals); 10554 SCIPfreeBufferArray(scip, &consvars); 10555 SCIPfreeBufferArray(scip, &openidx); 10556 SCIPfreeExpriter(&it); 10557 10558 return SCIP_OKAY; 10559 } 10560 10561 /* 10562 * Callback methods of constraint handler 10563 */ 10564 10565 /** copy method for constraint handler plugins (called when SCIP copies plugins) */ 10566 static 10567 SCIP_DECL_CONSHDLRCOPY(conshdlrCopyNonlinear) 10568 { /*lint --e{715}*/ 10569 SCIP_CONSHDLR* targetconshdlr; 10570 SCIP_CONSHDLRDATA* sourceconshdlrdata; 10571 int i; 10572 10573 assert(scip != NULL); 10574 assert(conshdlr != NULL); 10575 assert(valid != NULL); 10576 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0); 10577 10578 /* create basic data of constraint handler and include it to scip */ 10579 SCIP_CALL( SCIPincludeConshdlrNonlinear(scip) ); 10580 10581 targetconshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME); 10582 assert(targetconshdlr != NULL); 10583 assert(targetconshdlr != conshdlr); 10584 10585 sourceconshdlrdata = SCIPconshdlrGetData(conshdlr); 10586 assert(sourceconshdlrdata != NULL); 10587 10588 /* copy nonlinear handlers */ 10589 for( i = 0; i < sourceconshdlrdata->nnlhdlrs; ++i ) 10590 { 10591 SCIP_CALL( SCIPnlhdlrCopyhdlr(scip, targetconshdlr, conshdlr, sourceconshdlrdata->nlhdlrs[i]) ); 10592 } 10593 10594 *valid = TRUE; 10595 10596 return SCIP_OKAY; 10597 } 10598 10599 /** destructor of constraint handler to free constraint handler data (called when SCIP is exiting) */ 10600 static 10601 SCIP_DECL_CONSFREE(consFreeNonlinear) 10602 { /*lint --e{715}*/ 10603 SCIP_CONSHDLRDATA* conshdlrdata; 10604 int i; 10605 10606 conshdlrdata = SCIPconshdlrGetData(conshdlr); 10607 assert(conshdlrdata != NULL); 10608 10609 /* free nonlinear handlers */ 10610 for( i = 0; i < conshdlrdata->nnlhdlrs; ++i ) 10611 { 10612 SCIP_CALL( SCIPnlhdlrFree(scip, &conshdlrdata->nlhdlrs[i]) ); 10613 assert(conshdlrdata->nlhdlrs[i] == NULL); 10614 } 10615 SCIPfreeBlockMemoryArrayNull(scip, &conshdlrdata->nlhdlrs, conshdlrdata->nlhdlrssize); 10616 conshdlrdata->nlhdlrssize = 0; 10617 10618 /* free upgrade functions */ 10619 for( i = 0; i < conshdlrdata->nconsupgrades; ++i ) 10620 { 10621 assert(conshdlrdata->consupgrades[i] != NULL); 10622 SCIPfreeBlockMemory(scip, &conshdlrdata->consupgrades[i]); 10623 } 10624 SCIPfreeBlockMemoryArrayNull(scip, &conshdlrdata->consupgrades, conshdlrdata->consupgradessize); 10625 10626 SCIP_CALL( SCIPfreeClock(scip, &conshdlrdata->canonicalizetime) ); 10627 10628 SCIPqueueFree(&conshdlrdata->reversepropqueue); 10629 10630 if( conshdlrdata->vp_randnumgen != NULL ) 10631 SCIPfreeRandom(scip, &conshdlrdata->vp_randnumgen); 10632 10633 /* free LPs used to construct facets of envelops of vertex-polyhedral functions */ 10634 for( i = 0; i <= SCIP_MAXVERTEXPOLYDIM; ++i ) 10635 { 10636 if( conshdlrdata->vp_lp[i] != NULL ) 10637 { 10638 SCIP_CALL( SCIPlpiFree(&conshdlrdata->vp_lp[i]) ); 10639 } 10640 } 10641 10642 assert(conshdlrdata->branchrandnumgen == NULL); 10643 10644 assert(SCIPhashmapGetNElements(conshdlrdata->var2expr) == 0); 10645 SCIPhashmapFree(&conshdlrdata->var2expr); 10646 10647 SCIPfreeBlockMemory(scip, &conshdlrdata); 10648 SCIPconshdlrSetData(conshdlr, NULL); 10649 10650 return SCIP_OKAY; 10651 } 10652 10653 10654 /** initialization method of constraint handler (called after problem was transformed) */ 10655 static 10656 SCIP_DECL_CONSINIT(consInitNonlinear) 10657 { /*lint --e{715}*/ 10658 SCIP_CONSHDLRDATA* conshdlrdata; 10659 int i; 10660 10661 conshdlrdata = SCIPconshdlrGetData(conshdlr); 10662 assert(conshdlrdata != NULL); 10663 10664 /* make sure current activity tags in expressions are invalid, because we start catching variable events only now */ 10665 conshdlrdata->lastboundrelax = ++conshdlrdata->curboundstag; 10666 /* set to 1 so it is larger than initial value of lastenforound in exprs */ 10667 conshdlrdata->enforound = 1; 10668 /* reset numbering for auxiliary variables */ 10669 conshdlrdata->auxvarid = 0; 10670 10671 for( i = 0; i < nconss; ++i ) 10672 { 10673 SCIP_CALL( storeVarExprs(scip, conshdlr, SCIPconsGetData(conss[i])) ); 10674 SCIP_CALL( catchVarEvents(scip, conshdlrdata->eventhdlr, conss[i]) ); 10675 } 10676 10677 /* sort nonlinear handlers by detection priority, in decreasing order */ 10678 if( conshdlrdata->nnlhdlrs > 1 ) 10679 SCIPsortDownPtr((void**)conshdlrdata->nlhdlrs, SCIPnlhdlrComp, conshdlrdata->nnlhdlrs); 10680 10681 /* get heuristics for later use */ 10682 conshdlrdata->subnlpheur = SCIPfindHeur(scip, "subnlp"); 10683 conshdlrdata->trysolheur = SCIPfindHeur(scip, "trysol"); 10684 10685 /* reset statistics in nonlinear handlers (TODO only if misc/resetstat == TRUE) and call nlhdlrInit */ 10686 for( i = 0; i < conshdlrdata->nnlhdlrs; ++i ) 10687 { 10688 SCIP_CALL( SCIPnlhdlrInit(scip, conshdlrdata->nlhdlrs[i]) ); 10689 } 10690 10691 /* reset statistics in constraint handler */ 10692 conshdlrdata->nweaksepa = 0; 10693 conshdlrdata->ntightenlp = 0; 10694 conshdlrdata->ndesperatebranch = 0; 10695 conshdlrdata->ndesperatecutoff = 0; 10696 conshdlrdata->ndesperatetightenlp = 0; 10697 conshdlrdata->nforcelp = 0; 10698 SCIP_CALL( SCIPresetClock(scip, conshdlrdata->canonicalizetime) ); 10699 conshdlrdata->ncanonicalizecalls = 0; 10700 10701 #ifdef ENFOLOGFILE 10702 ENFOLOG( enfologfile = fopen(ENFOLOGFILE, "w"); ) 10703 #endif 10704 10705 return SCIP_OKAY; 10706 } 10707 10708 10709 /** deinitialization method of constraint handler (called before transformed problem is freed) */ 10710 static 10711 SCIP_DECL_CONSEXIT(consExitNonlinear) 10712 { /*lint --e{715}*/ 10713 SCIP_CONSHDLRDATA* conshdlrdata; 10714 SCIP_CONS** consssorted; 10715 int i; 10716 10717 conshdlrdata = SCIPconshdlrGetData(conshdlr); 10718 assert(conshdlrdata != NULL); 10719 10720 if( nconss > 0 ) 10721 { 10722 /* for better performance of dropVarEvents, we sort by index, descending */ 10723 SCIP_CALL( SCIPduplicateBufferArray(scip, &consssorted, conss, nconss) ); 10724 SCIPsortDownPtr((void**)consssorted, compIndexConsNonlinear, nconss); 10725 10726 for( i = 0; i < nconss; ++i ) 10727 { 10728 SCIP_CALL( dropVarEvents(scip, conshdlrdata->eventhdlr, consssorted[i]) ); 10729 SCIP_CALL( freeVarExprs(scip, SCIPconsGetData(consssorted[i])) ); 10730 } 10731 10732 SCIPfreeBufferArray(scip, &consssorted); 10733 } 10734 10735 conshdlrdata->subnlpheur = NULL; 10736 conshdlrdata->trysolheur = NULL; 10737 10738 if( conshdlrdata->vp_randnumgen != NULL ) 10739 SCIPfreeRandom(scip, &conshdlrdata->vp_randnumgen); 10740 10741 /* free LPs used to construct facets of envelops of vertex-polyhedral functions */ 10742 for( i = 0; i <= SCIP_MAXVERTEXPOLYDIM; ++i ) 10743 { 10744 if( conshdlrdata->vp_lp[i] != NULL ) 10745 { 10746 SCIP_CALL( SCIPlpiFree(&conshdlrdata->vp_lp[i]) ); 10747 } 10748 } 10749 10750 if( conshdlrdata->branchrandnumgen != NULL ) 10751 SCIPfreeRandom(scip, &conshdlrdata->branchrandnumgen); 10752 10753 /* deinitialize nonlinear handlers */ 10754 for( i = 0; i < conshdlrdata->nnlhdlrs; ++i ) 10755 { 10756 SCIP_CALL( SCIPnlhdlrExit(scip, conshdlrdata->nlhdlrs[i]) ); 10757 } 10758 10759 ENFOLOG( 10760 if( enfologfile != NULL ) 10761 { 10762 fclose(enfologfile); 10763 enfologfile = NULL; 10764 }) 10765 10766 return SCIP_OKAY; 10767 } 10768 10769 10770 /** presolving initialization method of constraint handler (called when presolving is about to begin) */ 10771 #ifdef SCIP_DISABLED_CODE 10772 static 10773 SCIP_DECL_CONSINITPRE(consInitpreNonlinear) 10774 { /*lint --e{715}*/ 10775 SCIPerrorMessage("method of nonlinear constraint handler not implemented yet\n"); 10776 SCIPABORT(); /*lint --e{527}*/ 10777 10778 return SCIP_OKAY; 10779 } 10780 #else 10781 #define consInitpreNonlinear NULL 10782 #endif 10783 10784 10785 /** presolving deinitialization method of constraint handler (called after presolving has been finished) */ 10786 static 10787 SCIP_DECL_CONSEXITPRE(consExitpreNonlinear) 10788 { /*lint --e{715}*/ 10789 SCIP_Bool infeasible; 10790 10791 if( nconss == 0 ) 10792 return SCIP_OKAY; 10793 10794 /* skip some extra work if already known to be infeasible */ 10795 if( SCIPgetStatus(scip) == SCIP_STATUS_INFEASIBLE ) 10796 return SCIP_OKAY; 10797 10798 /* simplify constraints and replace common subexpressions */ 10799 SCIP_CALL( canonicalizeConstraints(scip, conshdlr, conss, nconss, SCIP_PRESOLTIMING_ALWAYS, &infeasible, NULL, NULL, NULL) ); 10800 10801 /* currently SCIP does not offer to communicate this, 10802 * but at the moment this can only become true if canonicalizeConstraints called detectNlhdlrs (which it doesn't do in EXITPRESOLVE stage) 10803 * or if a constraint expression became constant 10804 * the latter happened on tls4 within fiberscip, so I'm disabling this assert for now 10805 */ 10806 /* assert(!infeasible); */ 10807 10808 /* tell SCIP that we have something nonlinear */ 10809 SCIPenableNLP(scip); 10810 10811 return SCIP_OKAY; 10812 } 10813 10814 10815 /** solving process initialization method of constraint handler (called when branch and bound process is about to begin) */ 10816 static 10817 SCIP_DECL_CONSINITSOL(consInitsolNonlinear) 10818 { /*lint --e{715}*/ 10819 SCIP_CONSHDLRDATA* conshdlrdata; 10820 int i; 10821 10822 /* skip remaining initializations if we have solved already 10823 * if infeasibility was found by our boundtightening, then curvature check may also fail as some exprhdlr (e.g., pow) 10824 * assumes nonempty activities in expressions 10825 */ 10826 switch( SCIPgetStatus(scip) ) 10827 { 10828 case SCIP_STATUS_OPTIMAL: 10829 case SCIP_STATUS_INFEASIBLE: 10830 case SCIP_STATUS_UNBOUNDED: 10831 case SCIP_STATUS_INFORUNBD: 10832 return SCIP_OKAY; 10833 default: ; 10834 } /*lint !e788 */ 10835 10836 conshdlrdata = SCIPconshdlrGetData(conshdlr); 10837 assert(conshdlrdata != NULL); 10838 10839 /* reset one of the number of detections counter to count only current round */ 10840 for( i = 0; i < conshdlrdata->nnlhdlrs; ++i ) 10841 SCIPnlhdlrResetNDetectionslast(conshdlrdata->nlhdlrs[i]); 10842 10843 SCIP_CALL( initSolve(scip, conshdlr, conss, nconss) ); 10844 10845 /* check that branching/lpgainnormalize is set to a known value if pseudo-costs are used in branching */ 10846 if( conshdlrdata->branchpscostweight > 0.0 ) 10847 { 10848 SCIP_CALL( SCIPgetCharParam(scip, "branching/lpgainnormalize", &(conshdlrdata->branchpscostupdatestrategy)) ); 10849 if( strchr("lds", conshdlrdata->branchpscostupdatestrategy) == NULL ) 10850 { 10851 SCIPerrorMessage("branching/lpgainnormalize strategy %c unknown\n", conshdlrdata->branchpscostupdatestrategy); 10852 SCIPABORT(); 10853 return SCIP_INVALIDDATA; 10854 } 10855 } 10856 10857 return SCIP_OKAY; 10858 } 10859 10860 10861 /** solving process deinitialization method of constraint handler (called before branch and bound process data is freed) */ 10862 static 10863 SCIP_DECL_CONSEXITSOL(consExitsolNonlinear) 10864 { /*lint --e{715}*/ 10865 SCIP_CONSHDLRDATA* conshdlrdata; 10866 10867 SCIP_CALL( deinitSolve(scip, conshdlr, conss, nconss) ); 10868 10869 conshdlrdata = SCIPconshdlrGetData(conshdlr); 10870 assert(conshdlrdata != NULL); 10871 10872 /* free hash table for bilinear terms */ 10873 SCIP_CALL( bilinearTermsFree(scip, conshdlrdata) ); 10874 10875 /* reset flag to allow another call of presolSingleLockedVars() after a restart */ 10876 conshdlrdata->checkedvarlocks = FALSE; 10877 10878 /* drop catching new solution event, if catched before */ 10879 if( conshdlrdata->newsoleventfilterpos >= 0 ) 10880 { 10881 SCIP_EVENTHDLR* eventhdlr; 10882 10883 eventhdlr = SCIPfindEventhdlr(scip, CONSHDLR_NAME "_newsolution"); 10884 assert(eventhdlr != NULL); 10885 10886 SCIP_CALL( SCIPdropEvent(scip, conshdlrdata->linearizeheursol == 'i' ? SCIP_EVENTTYPE_BESTSOLFOUND : SCIP_EVENTTYPE_SOLFOUND, eventhdlr, (SCIP_EVENTDATA*)conshdlr, conshdlrdata->newsoleventfilterpos) ); 10887 conshdlrdata->newsoleventfilterpos = -1; 10888 } 10889 10890 return SCIP_OKAY; 10891 } 10892 10893 10894 /** frees specific constraint data */ 10895 static 10896 SCIP_DECL_CONSDELETE(consDeleteNonlinear) 10897 { /*lint --e{715}*/ 10898 assert(consdata != NULL); 10899 assert(*consdata != NULL); 10900 assert((*consdata)->expr != NULL); 10901 10902 /* constraint locks should have been removed */ 10903 assert((*consdata)->nlockspos == 0); 10904 assert((*consdata)->nlocksneg == 0); 10905 10906 /* free variable expressions */ 10907 SCIP_CALL( freeVarExprs(scip, *consdata) ); 10908 10909 SCIP_CALL( SCIPreleaseExpr(scip, &(*consdata)->expr) ); 10910 10911 /* free nonlinear row representation */ 10912 if( (*consdata)->nlrow != NULL ) 10913 { 10914 SCIP_CALL( SCIPreleaseNlRow(scip, &(*consdata)->nlrow) ); 10915 } 10916 10917 SCIPfreeBlockMemory(scip, consdata); 10918 10919 return SCIP_OKAY; 10920 } 10921 10922 10923 /** transforms constraint data into data belonging to the transformed problem */ 10924 static 10925 SCIP_DECL_CONSTRANS(consTransNonlinear) 10926 { /*lint --e{715}*/ 10927 SCIP_EXPR* targetexpr; 10928 SCIP_CONSDATA* sourcedata; 10929 10930 sourcedata = SCIPconsGetData(sourcecons); 10931 assert(sourcedata != NULL); 10932 10933 /* get a copy of sourceexpr with transformed vars */ 10934 SCIP_CALL( SCIPduplicateExpr(scip, sourcedata->expr, &targetexpr, mapexprtransvar, conshdlr, exprownerCreate, (void*)conshdlr) ); 10935 assert(targetexpr != NULL); /* SCIPduplicateExpr cannot fail */ 10936 10937 /* create transformed cons (only captures targetexpr, no need to copy again) */ 10938 SCIP_CALL( createCons(scip, conshdlr, targetcons, SCIPconsGetName(sourcecons), 10939 targetexpr, sourcedata->lhs, sourcedata->rhs, FALSE, 10940 SCIPconsIsInitial(sourcecons), SCIPconsIsSeparated(sourcecons), SCIPconsIsEnforced(sourcecons), 10941 SCIPconsIsChecked(sourcecons), SCIPconsIsPropagated(sourcecons), 10942 SCIPconsIsLocal(sourcecons), SCIPconsIsModifiable(sourcecons), 10943 SCIPconsIsDynamic(sourcecons), SCIPconsIsRemovable(sourcecons)) ); 10944 10945 /* release target expr */ 10946 SCIP_CALL( SCIPreleaseExpr(scip, &targetexpr) ); 10947 10948 return SCIP_OKAY; 10949 } 10950 10951 10952 /** LP initialization method of constraint handler (called before the initial LP relaxation at a node is solved) */ 10953 static 10954 SCIP_DECL_CONSINITLP(consInitlpNonlinear) 10955 { /*lint --e{715}*/ 10956 SCIP_CONSHDLRDATA* conshdlrdata; 10957 10958 /* create auxiliary variables and call separation initialization callbacks of the expression handlers 10959 * TODO if we ever want to allow constraints that are separated but not initial, then we need to call initSepa also 10960 * during SEPALP, ENFOLP, etc, whenever a constraint may be separated the first time 10961 * for now, there is an assert in detectNlhdlrs to require initial if separated 10962 */ 10963 SCIP_CALL( initSepa(scip, conshdlr, conss, nconss, infeasible) ); 10964 10965 conshdlrdata = SCIPconshdlrGetData(conshdlr); 10966 assert(conshdlrdata != NULL); 10967 10968 /* catch new solution event */ 10969 if( conshdlrdata->linearizeheursol != 'o' && conshdlrdata->newsoleventfilterpos == -1 ) 10970 { 10971 SCIP_EVENTHDLR* eventhdlr; 10972 10973 eventhdlr = SCIPfindEventhdlr(scip, CONSHDLR_NAME "_newsolution"); 10974 assert(eventhdlr != NULL); 10975 10976 SCIP_CALL( SCIPcatchEvent(scip, conshdlrdata->linearizeheursol == 'i' ? SCIP_EVENTTYPE_BESTSOLFOUND : SCIP_EVENTTYPE_SOLFOUND, 10977 eventhdlr, (SCIP_EVENTDATA*)conshdlr, &conshdlrdata->newsoleventfilterpos) ); 10978 } 10979 10980 /* collect all bilinear terms for which an auxvar is present 10981 * TODO this will only do something for the first call of initlp after initsol, because it cannot handle 10982 * addition (and removal?) of constraints during solve 10983 * this is typically the majority of constraints, but the method should be made more flexible 10984 */ 10985 SCIP_CALL( bilinearTermsInsertAll(scip, conshdlr, conss, nconss) ); 10986 10987 return SCIP_OKAY; 10988 } 10989 10990 10991 /** separation method of constraint handler for LP solutions */ 10992 static 10993 SCIP_DECL_CONSSEPALP(consSepalpNonlinear) 10994 { /*lint --e{715}*/ 10995 SCIP_CALL( consSepa(scip, conshdlr, conss, nconss, NULL, result) ); 10996 10997 return SCIP_OKAY; 10998 } 10999 11000 11001 /** separation method of constraint handler for arbitrary primal solutions */ 11002 static 11003 SCIP_DECL_CONSSEPASOL(consSepasolNonlinear) 11004 { /*lint --e{715}*/ 11005 SCIP_CALL( consSepa(scip, conshdlr, conss, nconss, sol, result) ); 11006 11007 return SCIP_OKAY; 11008 } 11009 11010 11011 /** constraint enforcing method of constraint handler for LP solutions */ 11012 static 11013 SCIP_DECL_CONSENFOLP(consEnfolpNonlinear) 11014 { /*lint --e{715}*/ 11015 SCIP_CALL( consEnfo(scip, conshdlr, conss, nconss, NULL, result) ); 11016 11017 return SCIP_OKAY; 11018 } 11019 11020 11021 /** constraint enforcing method of constraint handler for relaxation solutions */ 11022 static 11023 SCIP_DECL_CONSENFORELAX(consEnforelaxNonlinear) 11024 { /*lint --e{715}*/ 11025 SCIP_CALL( consEnfo(scip, conshdlr, conss, nconss, sol, result) ); 11026 11027 return SCIP_OKAY; 11028 } 11029 11030 11031 /** constraint enforcing method of constraint handler for pseudo solutions */ 11032 static 11033 SCIP_DECL_CONSENFOPS(consEnfopsNonlinear) 11034 { /*lint --e{715}*/ 11035 SCIP_RESULT propresult; 11036 SCIP_Longint soltag; 11037 int nchgbds; 11038 int nnotify; 11039 int c; 11040 11041 soltag = SCIPgetExprNewSoltag(scip); 11042 11043 *result = SCIP_FEASIBLE; 11044 for( c = 0; c < nconss; ++c ) 11045 { 11046 SCIP_CALL( computeViolation(scip, conss[c], NULL, soltag) ); 11047 11048 if( isConsViolated(scip, conss[c]) ) 11049 *result = SCIP_INFEASIBLE; 11050 } 11051 11052 if( *result == SCIP_FEASIBLE ) 11053 return SCIP_OKAY; 11054 11055 /* try to propagate 11056 * TODO obey propinenfo parameter, but we need something to recognize cutoff 11057 */ 11058 nchgbds = 0; 11059 SCIP_CALL( propConss(scip, conshdlr, conss, nconss, TRUE, &propresult, &nchgbds) ); 11060 11061 if( (propresult == SCIP_CUTOFF) || (propresult == SCIP_REDUCEDDOM) ) 11062 { 11063 *result = propresult; 11064 return SCIP_OKAY; 11065 } 11066 11067 /* register all unfixed variables in all violated constraints as branching candidates */ 11068 SCIP_CALL( registerBranchingCandidatesAllUnfixed(scip, conshdlr, conss, nconss, &nnotify) ); 11069 if( nnotify > 0 ) 11070 { 11071 SCIPdebugMsg(scip, "registered %d external branching candidates\n", nnotify); 11072 11073 return SCIP_OKAY; 11074 } 11075 11076 SCIPdebugMsg(scip, "could not find branching candidates, forcing to solve LP\n"); 11077 *result = SCIP_SOLVELP; 11078 ++SCIPconshdlrGetData(conshdlr)->nforcelp; 11079 11080 return SCIP_OKAY; 11081 } 11082 11083 11084 /** feasibility check method of constraint handler for integral solutions */ 11085 static 11086 SCIP_DECL_CONSCHECK(consCheckNonlinear) 11087 { /*lint --e{715}*/ 11088 SCIP_CONSHDLRDATA* conshdlrdata; 11089 SCIP_CONSDATA* consdata; 11090 SCIP_Real maxviol; 11091 SCIP_Bool maypropfeasible; 11092 SCIP_Longint soltag; 11093 int c; 11094 11095 assert(scip != NULL); 11096 assert(conshdlr != NULL); 11097 assert(conss != NULL || nconss == 0); 11098 assert(result != NULL); 11099 11100 conshdlrdata = SCIPconshdlrGetData(conshdlr); 11101 assert(conshdlrdata != NULL); 11102 11103 *result = SCIP_FEASIBLE; 11104 soltag = SCIPgetExprNewSoltag(scip); 11105 maxviol = 0.0; 11106 maypropfeasible = conshdlrdata->trysolheur != NULL && SCIPgetStage(scip) >= SCIP_STAGE_TRANSFORMED 11107 && SCIPgetStage(scip) <= SCIP_STAGE_SOLVING; 11108 11109 if( maypropfeasible && (sol == NULL || SCIPsolGetOrigin(sol) == SCIP_SOLORIGIN_LPSOL) && SCIPgetLPSolstat(scip) == SCIP_LPSOLSTAT_UNBOUNDEDRAY ) 11110 maypropfeasible = FALSE; 11111 11112 /* check nonlinear constraints for feasibility */ 11113 for( c = 0; c < nconss; ++c ) 11114 { 11115 assert(conss != NULL && conss[c] != NULL); 11116 SCIP_CALL( computeViolation(scip, conss[c], sol, soltag) ); 11117 11118 if( isConsViolated(scip, conss[c]) ) 11119 { 11120 *result = SCIP_INFEASIBLE; 11121 maxviol = MAX(maxviol, getConsAbsViolation(conss[c])); 11122 11123 consdata = SCIPconsGetData(conss[c]); 11124 assert(consdata != NULL); 11125 11126 /* print reason for infeasibility */ 11127 if( printreason ) 11128 { 11129 SCIP_CALL( SCIPprintCons(scip, conss[c], NULL) ); 11130 SCIPinfoMessage(scip, NULL, ";\n"); 11131 11132 if( consdata->lhsviol > SCIPfeastol(scip) ) 11133 { 11134 SCIPinfoMessage(scip, NULL, "violation: left hand side is violated by %.15g\n", consdata->lhsviol); 11135 } 11136 if( consdata->rhsviol > SCIPfeastol(scip) ) 11137 { 11138 SCIPinfoMessage(scip, NULL, "violation: right hand side is violated by %.15g\n", consdata->rhsviol); 11139 } 11140 } 11141 else if( (conshdlrdata->subnlpheur == NULL || sol == NULL) && !maypropfeasible && !completely ) 11142 { 11143 /* if we don't want to pass to subnlp heuristic and don't need to print reasons, then can stop checking here */ 11144 return SCIP_OKAY; 11145 } 11146 11147 /* do not try to shift linear variables if violation is at infinity (leads to setting variable to infinity in solution, which is not allowed) */ 11148 if( maypropfeasible && SCIPisInfinity(scip, getConsAbsViolation(conss[c])) ) 11149 maypropfeasible = FALSE; 11150 11151 if( maypropfeasible ) 11152 { 11153 if( consdata->lhsviol > SCIPfeastol(scip) ) 11154 { 11155 /* check if there is a variable which may help to get the left hand side satisfied 11156 * if there is no such variable, then we cannot get feasible 11157 */ 11158 if( !(consdata->linvarincr != NULL && consdata->linvarincrcoef > 0.0) && 11159 !(consdata->linvardecr != NULL && consdata->linvardecrcoef < 0.0) ) 11160 maypropfeasible = FALSE; 11161 } 11162 else 11163 { 11164 assert(consdata->rhsviol > SCIPfeastol(scip)); 11165 /* check if there is a variable which may help to get the right hand side satisfied 11166 * if there is no such variable, then we cannot get feasible 11167 */ 11168 if( !(consdata->linvarincr != NULL && consdata->linvarincrcoef < 0.0) && 11169 !(consdata->linvardecr != NULL && consdata->linvardecrcoef > 0.0) ) 11170 maypropfeasible = FALSE; 11171 } 11172 } 11173 } 11174 } 11175 11176 if( *result == SCIP_INFEASIBLE && maypropfeasible ) 11177 { 11178 SCIP_Bool success; 11179 11180 SCIP_CALL( proposeFeasibleSolution(scip, conshdlr, conss, nconss, sol, &success) ); 11181 11182 /* do not pass solution to NLP heuristic if we made it feasible this way */ 11183 if( success ) 11184 return SCIP_OKAY; 11185 } 11186 11187 if( *result == SCIP_INFEASIBLE && conshdlrdata->subnlpheur != NULL && sol != NULL && !SCIPisInfinity(scip, maxviol) ) 11188 { 11189 SCIP_CALL( SCIPupdateStartpointHeurSubNlp(scip, conshdlrdata->subnlpheur, sol, maxviol) ); 11190 } 11191 11192 return SCIP_OKAY; 11193 } 11194 11195 11196 /** domain propagation method of constraint handler */ 11197 static 11198 SCIP_DECL_CONSPROP(consPropNonlinear) 11199 { /*lint --e{715}*/ 11200 int nchgbds = 0; 11201 11202 SCIP_CALL( propConss(scip, conshdlr, conss, nconss, FALSE, result, &nchgbds) ); 11203 assert(nchgbds >= 0); 11204 11205 /* TODO would it make sense to check for redundant constraints? */ 11206 11207 return SCIP_OKAY; 11208 } 11209 11210 11211 /** presolving method of constraint handler */ 11212 static 11213 SCIP_DECL_CONSPRESOL(consPresolNonlinear) 11214 { /*lint --e{715}*/ 11215 SCIP_CONSHDLRDATA* conshdlrdata; 11216 SCIP_Bool infeasible; 11217 int c; 11218 11219 *result = SCIP_DIDNOTFIND; 11220 11221 if( nconss == 0 ) 11222 { 11223 *result = SCIP_DIDNOTRUN; 11224 return SCIP_OKAY; 11225 } 11226 11227 conshdlrdata = SCIPconshdlrGetData(conshdlr); 11228 assert(conshdlrdata != NULL); 11229 11230 /* simplify constraints and replace common subexpressions, reinit nlhdlrs */ 11231 SCIP_CALL( canonicalizeConstraints(scip, conshdlr, conss, nconss, presoltiming, &infeasible, ndelconss, naddconss, nchgcoefs) ); 11232 if( infeasible ) 11233 { 11234 *result = SCIP_CUTOFF; 11235 return SCIP_OKAY; 11236 } 11237 11238 /* merge constraints with the same root expression */ 11239 if( presoltiming & SCIP_PRESOLTIMING_EXHAUSTIVE ) 11240 { 11241 SCIP_Bool success; 11242 11243 SCIP_CALL( presolveMergeConss(scip, conss, nconss, &success) ); 11244 if( success ) 11245 *result = SCIP_SUCCESS; 11246 } 11247 11248 /* propagate constraints */ 11249 SCIP_CALL( propConss(scip, conshdlr, conss, nconss, FALSE, result, nchgbds) ); 11250 if( *result == SCIP_CUTOFF ) 11251 return SCIP_OKAY; 11252 11253 /* propagate function domains (TODO integrate with simplify?) */ 11254 if( (presoltiming & SCIP_PRESOLTIMING_EXHAUSTIVE) || nrounds == 0 ) 11255 { 11256 SCIP_RESULT localresult; 11257 SCIP_CALL( propExprDomains(scip, conshdlr, conss, nconss, &localresult, nchgbds) ); 11258 if( localresult == SCIP_CUTOFF ) 11259 { 11260 *result = SCIP_CUTOFF; 11261 return SCIP_OKAY; 11262 } 11263 if( localresult == SCIP_REDUCEDDOM ) 11264 *result = SCIP_REDUCEDDOM; 11265 } 11266 11267 /* check for redundant constraints, remove constraints that are a value expression */ 11268 SCIP_CALL( presolveRedundantConss(scip, conshdlr, conss, nconss, &infeasible, ndelconss, nchgbds) ); 11269 if( infeasible ) 11270 { 11271 *result = SCIP_CUTOFF; 11272 return SCIP_OKAY; 11273 } 11274 11275 /* try to upgrade constraints */ 11276 for( c = 0; c < nconss; ++c ) 11277 { 11278 SCIP_Bool upgraded; 11279 11280 /* skip inactive and deleted constraints */ 11281 if( SCIPconsIsDeleted(conss[c]) || !SCIPconsIsActive(conss[c]) ) 11282 continue; 11283 11284 SCIP_CALL( presolveUpgrade(scip, conshdlr, conss[c], &upgraded, nupgdconss, naddconss) ); 11285 } 11286 11287 /* try to change continuous variables that appear linearly to be implicit integer */ 11288 if( presoltiming & SCIP_PRESOLTIMING_MEDIUM ) 11289 { 11290 SCIP_CALL( presolveImplint(scip, conshdlr, conss, nconss, nchgvartypes, &infeasible) ); 11291 11292 if( infeasible ) 11293 { 11294 SCIPdebugMsg(scip, "presolveImplint() detected infeasibility\n"); 11295 *result = SCIP_CUTOFF; 11296 return SCIP_OKAY; 11297 } 11298 } 11299 11300 /* fix variables that are contained in only one nonlinear constraint to their upper or lower bounds, if possible */ 11301 if( (presoltiming & SCIP_PRESOLTIMING_EXHAUSTIVE) && SCIPisPresolveFinished(scip) 11302 && !conshdlrdata->checkedvarlocks && conshdlrdata->checkvarlocks != 'd' ) 11303 { 11304 /* run this presolving technique only once because we don't want to generate identical bound disjunction 11305 * constraints multiple times 11306 */ 11307 conshdlrdata->checkedvarlocks = TRUE; 11308 11309 for( c = 0; c < nconss; ++c ) 11310 { 11311 int tmpnchgvartypes = 0; 11312 int tmpnaddconss = 0; 11313 11314 SCIP_CALL( presolveSingleLockedVars(scip, conshdlr, conss[c], &tmpnchgvartypes, &tmpnaddconss, &infeasible) ); 11315 SCIPdebugMsg(scip, "presolSingleLockedVars() for %s: nchgvartypes=%d naddconss=%d infeas=%u\n", 11316 SCIPconsGetName(conss[c]), tmpnchgvartypes, tmpnaddconss, infeasible); 11317 11318 if( infeasible ) 11319 { 11320 SCIPdebugMsg(scip, "presolSingleLockedVars() detected infeasibility\n"); 11321 *result = SCIP_CUTOFF; 11322 return SCIP_OKAY; 11323 } 11324 11325 (*nchgvartypes) += tmpnchgvartypes; 11326 (*naddconss) += tmpnaddconss; 11327 } 11328 } 11329 11330 if( *ndelconss > 0 || *nchgbds > 0 || *nupgdconss > 0 || *naddconss > 0 || *nchgvartypes > 0 ) 11331 *result = SCIP_SUCCESS; 11332 else 11333 *result = SCIP_DIDNOTFIND; 11334 11335 return SCIP_OKAY; 11336 } 11337 11338 11339 /** propagation conflict resolving method of constraint handler */ 11340 #ifdef SCIP_DISABLED_CODE 11341 static 11342 SCIP_DECL_CONSRESPROP(consRespropNonlinear) 11343 { /*lint --e{715}*/ 11344 SCIPerrorMessage("method of nonlinear constraint handler not implemented yet\n"); 11345 SCIPABORT(); /*lint --e{527}*/ 11346 11347 return SCIP_OKAY; 11348 } 11349 #else 11350 #define consRespropNonlinear NULL 11351 #endif 11352 11353 11354 /** variable rounding lock method of constraint handler */ 11355 static 11356 SCIP_DECL_CONSLOCK(consLockNonlinear) 11357 { /*lint --e{715}*/ 11358 SCIP_CONSDATA* consdata; 11359 SCIP_EXPR_OWNERDATA* ownerdata; 11360 SCIP_Bool reinitsolve = FALSE; 11361 11362 assert(conshdlr != NULL); 11363 assert(cons != NULL); 11364 11365 consdata = SCIPconsGetData(cons); 11366 assert(consdata != NULL); 11367 assert(consdata->expr != NULL); 11368 11369 ownerdata = SCIPexprGetOwnerData(consdata->expr); 11370 11371 /* check whether we need to initSolve again because 11372 * - we have enfo initialized (nenfos >= 0) 11373 * - and locks appeared (going from zero to nonzero) or disappeared (going from nonzero to zero) now 11374 */ 11375 if( ownerdata->nenfos >= 0 ) 11376 { 11377 if( (consdata->nlockspos == 0) != (nlockspos == 0) ) 11378 reinitsolve = TRUE; 11379 if( (consdata->nlocksneg == 0) != (nlocksneg == 0) ) 11380 reinitsolve = TRUE; 11381 } 11382 11383 if( reinitsolve ) 11384 { 11385 SCIP_CALL( deinitSolve(scip, conshdlr, &cons, 1) ); 11386 } 11387 11388 /* add locks */ 11389 SCIP_CALL( addLocks(scip, cons, nlockspos, nlocksneg) ); 11390 11391 if( reinitsolve ) 11392 { 11393 SCIP_CALL( initSolve(scip, conshdlr, &cons, 1) ); 11394 } 11395 11396 return SCIP_OKAY; 11397 } 11398 11399 11400 /** constraint activation notification method of constraint handler */ 11401 static 11402 SCIP_DECL_CONSACTIVE(consActiveNonlinear) 11403 { /*lint --e{715}*/ 11404 SCIP_CONSDATA* consdata; 11405 SCIP_Bool infeasible = FALSE; 11406 11407 consdata = SCIPconsGetData(cons); 11408 assert(consdata != NULL); 11409 11410 /* simplify root expression if the constraint has been added after presolving */ 11411 if( SCIPgetStage(scip) > SCIP_STAGE_EXITPRESOLVE ) 11412 { 11413 SCIP_Bool replacedroot; 11414 11415 if( !consdata->issimplified ) 11416 { 11417 SCIP_EXPR* simplified; 11418 SCIP_Bool changed; 11419 11420 /* simplify constraint */ 11421 SCIP_CALL( SCIPsimplifyExpr(scip, consdata->expr, &simplified, &changed, &infeasible, exprownerCreate, (void*)conshdlr) ); 11422 SCIP_CALL( SCIPreleaseExpr(scip, &consdata->expr) ); 11423 assert(simplified != NULL); 11424 consdata->expr = simplified; 11425 consdata->issimplified = TRUE; 11426 } 11427 11428 /* ensure each variable is represented by one variable expression only (need this for storeVarExprs() with simplified=TRUE below) */ 11429 SCIP_CALL( SCIPreplaceCommonSubexpressions(scip, &consdata->expr, 1, &replacedroot) ); 11430 assert(!replacedroot); /* root expression cannot have been equal to one of its subexpressions */ 11431 11432 /* ensure that varexprs in consdata->expr are the one from var2expr hashmap */ 11433 { 11434 SCIP_CONSHDLRDATA* conshdlrdata; 11435 SCIP_EXPRITER* it; 11436 SCIP_EXPR* expr; 11437 11438 conshdlrdata = SCIPconshdlrGetData(conshdlr); 11439 assert(conshdlrdata != NULL); 11440 11441 SCIP_CALL( SCIPcreateExpriter(scip, &it) ); 11442 SCIP_CALL( SCIPexpriterInit(it, consdata->expr, SCIP_EXPRITER_DFS, FALSE) ); 11443 SCIPexpriterSetStagesDFS(it, SCIP_EXPRITER_VISITINGCHILD); 11444 for( expr = SCIPexpriterGetCurrent(it); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) ) 11445 { 11446 SCIP_EXPR* child; 11447 SCIP_EXPR* hashmapexpr; 11448 11449 child = SCIPexpriterGetChildExprDFS(it); 11450 if( !SCIPisExprVar(scip, child) ) 11451 continue; 11452 11453 /* check which expression is stored in the hashmap for the var of child */ 11454 hashmapexpr = (SCIP_EXPR*)SCIPhashmapGetImage(conshdlrdata->var2expr, SCIPgetVarExprVar(child)); 11455 /* if a varexpr exists already in the hashmap, but it is child, then replace child by the one in the hashmap */ 11456 if( hashmapexpr != NULL && hashmapexpr != child ) 11457 { 11458 SCIP_CALL( SCIPreplaceExprChild(scip, expr, SCIPexpriterGetChildIdxDFS(it), hashmapexpr) ); 11459 } 11460 } 11461 SCIPfreeExpriter(&it); 11462 } 11463 } 11464 11465 /* store variable expressions */ 11466 if( SCIPgetStage(scip) > SCIP_STAGE_TRANSFORMED ) 11467 { 11468 SCIP_CALL( storeVarExprs(scip, conshdlr, consdata) ); 11469 } 11470 11471 /* add manually locks to constraints that are not checked for feasibility */ 11472 if( !SCIPconsIsChecked(cons) ) 11473 { 11474 assert(consdata->nlockspos == 0); 11475 assert(consdata->nlocksneg == 0); 11476 11477 SCIP_CALL( addLocks(scip, cons, 1, 0) ); 11478 } 11479 11480 if( SCIPgetStage(scip) > SCIP_STAGE_INITPRESOLVE && !infeasible ) 11481 { 11482 SCIP_CALL( initSolve(scip, conshdlr, &cons, 1) ); 11483 } 11484 11485 /* TODO deal with infeasibility */ 11486 assert(!infeasible); 11487 11488 return SCIP_OKAY; 11489 } 11490 11491 11492 /** constraint deactivation notification method of constraint handler */ 11493 static 11494 SCIP_DECL_CONSDEACTIVE(consDeactiveNonlinear) 11495 { /*lint --e{715}*/ 11496 SCIP_CONSHDLRDATA* conshdlrdata; 11497 11498 conshdlrdata = SCIPconshdlrGetData(conshdlr); 11499 assert(conshdlrdata != NULL); 11500 11501 if( SCIPgetStage(scip) < SCIP_STAGE_EXITSOLVE ) 11502 { 11503 SCIP_CALL( deinitSolve(scip, conshdlr, &cons, 1) ); 11504 } 11505 11506 if( SCIPgetStage(scip) > SCIP_STAGE_TRANSFORMED ) 11507 { 11508 SCIP_CALL( dropVarEvents(scip, conshdlrdata->eventhdlr, cons) ); 11509 SCIP_CALL( freeVarExprs(scip, SCIPconsGetData(cons)) ); 11510 } 11511 11512 /* remove locks that have been added in consActiveExpr() */ 11513 if( !SCIPconsIsChecked(cons) ) 11514 { 11515 SCIP_CALL( addLocks(scip, cons, -1, 0) ); 11516 11517 assert(SCIPconsGetData(cons)->nlockspos == 0); 11518 assert(SCIPconsGetData(cons)->nlocksneg == 0); 11519 } 11520 11521 return SCIP_OKAY; 11522 } 11523 11524 11525 /** constraint enabling notification method of constraint handler */ 11526 static 11527 SCIP_DECL_CONSENABLE(consEnableNonlinear) 11528 { /*lint --e{715}*/ 11529 SCIP_CONSHDLRDATA* conshdlrdata; 11530 11531 conshdlrdata = SCIPconshdlrGetData(conshdlr); 11532 assert(conshdlrdata != NULL); 11533 11534 if( SCIPgetStage(scip) >= SCIP_STAGE_TRANSFORMED ) 11535 { 11536 SCIP_CALL( catchVarEvents(scip, conshdlrdata->eventhdlr, cons) ); 11537 } 11538 11539 return SCIP_OKAY; 11540 } 11541 11542 11543 /** constraint disabling notification method of constraint handler */ 11544 static 11545 SCIP_DECL_CONSDISABLE(consDisableNonlinear) 11546 { /*lint --e{715}*/ 11547 SCIP_CONSHDLRDATA* conshdlrdata; 11548 11549 conshdlrdata = SCIPconshdlrGetData(conshdlr); 11550 assert(conshdlrdata != NULL); 11551 11552 if( SCIPgetStage(scip) >= SCIP_STAGE_TRANSFORMED ) 11553 { 11554 SCIP_CALL( dropVarEvents(scip, conshdlrdata->eventhdlr, cons) ); 11555 } 11556 11557 return SCIP_OKAY; 11558 } 11559 11560 /** variable deletion of constraint handler */ 11561 #ifdef SCIP_DISABLED_CODE 11562 static 11563 SCIP_DECL_CONSDELVARS(consDelvarsNonlinear) 11564 { /*lint --e{715}*/ 11565 SCIPerrorMessage("method of nonlinear constraint handler not implemented yet\n"); 11566 SCIPABORT(); /*lint --e{527}*/ 11567 11568 return SCIP_OKAY; 11569 } 11570 #else 11571 #define consDelvarsNonlinear NULL 11572 #endif 11573 11574 11575 /** constraint display method of constraint handler */ 11576 static 11577 SCIP_DECL_CONSPRINT(consPrintNonlinear) 11578 { /*lint --e{715}*/ 11579 SCIP_CONSDATA* consdata; 11580 11581 consdata = SCIPconsGetData(cons); 11582 assert(consdata != NULL); 11583 assert(consdata->expr != NULL); 11584 11585 /* print left hand side for ranged constraints */ 11586 if( !SCIPisInfinity(scip, -consdata->lhs) && !SCIPisInfinity(scip, consdata->rhs) && !SCIPisEQ(scip, consdata->lhs, consdata->rhs) ) 11587 { 11588 SCIPinfoMessage(scip, file, "%.15g <= ", consdata->lhs); 11589 } 11590 11591 /* print expression */ 11592 SCIP_CALL( SCIPprintExpr(scip, consdata->expr, file) ); 11593 11594 /* print right hand side */ 11595 if( SCIPisEQ(scip, consdata->lhs, consdata->rhs) ) 11596 SCIPinfoMessage(scip, file, " == %.15g", consdata->rhs); 11597 else if( !SCIPisInfinity(scip, consdata->rhs) ) 11598 SCIPinfoMessage(scip, file, " <= %.15g", consdata->rhs); 11599 else if( !SCIPisInfinity(scip, -consdata->lhs) ) 11600 SCIPinfoMessage(scip, file, " >= %.15g", consdata->lhs); 11601 else 11602 SCIPinfoMessage(scip, file, " [free]"); 11603 11604 return SCIP_OKAY; 11605 } 11606 11607 11608 /** constraint copying method of constraint handler */ 11609 static 11610 SCIP_DECL_CONSCOPY(consCopyNonlinear) 11611 { /*lint --e{715}*/ 11612 SCIP_CONSHDLR* targetconshdlr; 11613 SCIP_EXPR* targetexpr = NULL; 11614 SCIP_CONSDATA* sourcedata; 11615 11616 assert(cons != NULL); 11617 11618 sourcedata = SCIPconsGetData(sourcecons); 11619 assert(sourcedata != NULL); 11620 11621 targetconshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME); 11622 assert(targetconshdlr != NULL); 11623 11624 SCIP_CALL( SCIPcopyExpr(sourcescip, scip, sourcedata->expr, &targetexpr, exprownerCreate, (void*)targetconshdlr, varmap, consmap, global, valid) ); 11625 11626 if( targetexpr == NULL ) 11627 *valid = FALSE; 11628 11629 *cons = NULL; 11630 if( *valid ) 11631 { 11632 /* create copy (only capture targetexpr, no need to copy again) */ 11633 SCIP_CALL( createCons(scip, targetconshdlr, cons, name != NULL ? name : SCIPconsGetName(sourcecons), 11634 targetexpr, sourcedata->lhs, sourcedata->rhs, FALSE, 11635 initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable) ); 11636 } 11637 11638 if( targetexpr != NULL ) 11639 { 11640 /* release target expr */ 11641 SCIP_CALL( SCIPreleaseExpr(scip, &targetexpr) ); 11642 } 11643 11644 return SCIP_OKAY; 11645 } 11646 11647 11648 /** constraint parsing method of constraint handler */ 11649 static 11650 SCIP_DECL_CONSPARSE(consParseNonlinear) 11651 { /*lint --e{715}*/ 11652 SCIP_Real lhs; 11653 SCIP_Real rhs; 11654 char* endptr; 11655 SCIP_EXPR* consexprtree; 11656 11657 SCIPdebugMsg(scip, "cons_nonlinear::consparse parsing %s\n", str); 11658 11659 assert(scip != NULL); 11660 assert(success != NULL); 11661 assert(str != NULL); 11662 assert(name != NULL); 11663 assert(cons != NULL); 11664 11665 *success = FALSE; 11666 11667 /* return if string empty */ 11668 if( !*str ) 11669 return SCIP_OKAY; 11670 11671 endptr = (char*)str; 11672 11673 /* set left and right hand side to their default values */ 11674 lhs = -SCIPinfinity(scip); 11675 rhs = SCIPinfinity(scip); 11676 11677 /* parse constraint to get lhs, rhs, and expression in between (from cons_linear.c::consparse, but parsing whole string first, then getting expression) */ 11678 11679 /* check for left hand side */ 11680 if( isdigit((unsigned char)str[0]) || ((str[0] == '-' || str[0] == '+') && isdigit((unsigned char)str[1])) ) 11681 { 11682 /* there is a number coming, maybe it is a left-hand-side */ 11683 if( !SCIPparseReal(scip, str, &lhs, &endptr) ) 11684 { 11685 SCIPerrorMessage("error parsing number from <%s>\n", str); 11686 return SCIP_READERROR; 11687 } 11688 11689 /* ignore whitespace */ 11690 SCIP_CALL( SCIPskipSpace(&endptr) ); 11691 11692 if( endptr[0] != '<' || endptr[1] != '=' ) 11693 { 11694 /* no '<=' coming, so it was the beginning of the expression and not a left-hand-side */ 11695 lhs = -SCIPinfinity(scip); 11696 } 11697 else 11698 { 11699 /* it was indeed a left-hand-side, so continue parsing after it */ 11700 str = endptr + 2; 11701 11702 /* ignore whitespace */ 11703 SCIP_CALL( SCIPskipSpace((char**)&str) ); 11704 } 11705 } 11706 11707 SCIPdebugMsg(scip, "str should start at beginning of expr: %s\n", str); 11708 11709 /* parse expression: so far we did not allocate memory, so can just return in case of readerror */ 11710 SCIP_CALL( SCIPparseExpr(scip, &consexprtree, str, &str, exprownerCreate, (void*)conshdlr) ); 11711 11712 /* check for left or right hand side */ 11713 SCIP_CALL( SCIPskipSpace((char**)&str) ); 11714 11715 /* check for free constraint */ 11716 if( strncmp(str, "[free]", 6) == 0 ) 11717 { 11718 if( !SCIPisInfinity(scip, -lhs) ) 11719 { 11720 SCIPerrorMessage("cannot have left hand side and [free] status \n"); 11721 SCIP_CALL( SCIPreleaseExpr(scip, &consexprtree) ); 11722 return SCIP_OKAY; 11723 } 11724 *success = TRUE; 11725 } 11726 else 11727 { 11728 switch( *str ) 11729 { 11730 case '<': 11731 *success = *(str+1) == '=' ? SCIPparseReal(scip, str+2, &rhs, &endptr) : FALSE; 11732 break; 11733 case '=': 11734 if( !SCIPisInfinity(scip, -lhs) ) 11735 { 11736 SCIPerrorMessage("cannot have == on rhs if there was a <= on lhs\n"); 11737 SCIP_CALL( SCIPreleaseExpr(scip, &consexprtree) ); 11738 return SCIP_OKAY; 11739 } 11740 else 11741 { 11742 *success = *(str+1) == '=' ? SCIPparseReal(scip, str+2, &rhs, &endptr) : FALSE; 11743 lhs = rhs; 11744 } 11745 break; 11746 case '>': 11747 if( !SCIPisInfinity(scip, -lhs) ) 11748 { 11749 SCIPerrorMessage("cannot have => on rhs if there was a <= on lhs\n"); 11750 SCIP_CALL( SCIPreleaseExpr(scip, &consexprtree) ); 11751 return SCIP_OKAY; 11752 } 11753 else 11754 { 11755 *success = *(str+1) == '=' ? SCIPparseReal(scip, str+2, &lhs, &endptr) : FALSE; 11756 break; 11757 } 11758 case '\0': 11759 *success = TRUE; 11760 break; 11761 default: 11762 SCIPerrorMessage("unexpected character %c\n", *str); 11763 SCIP_CALL( SCIPreleaseExpr(scip, &consexprtree) ); 11764 return SCIP_OKAY; 11765 } 11766 } 11767 11768 /* create constraint */ 11769 SCIP_CALL( createCons(scip, conshdlr, cons, name, 11770 consexprtree, lhs, rhs, FALSE, 11771 initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable) ); 11772 assert(*cons != NULL); 11773 11774 SCIP_CALL( SCIPreleaseExpr(scip, &consexprtree) ); 11775 11776 SCIPdebugMsg(scip, "created nonlinear constraint: <%s>\n", SCIPconsGetName(*cons)); 11777 11778 return SCIP_OKAY; 11779 } 11780 11781 11782 /** constraint method of constraint handler which returns the variables (if possible) */ 11783 static 11784 SCIP_DECL_CONSGETVARS(consGetVarsNonlinear) 11785 { /*lint --e{715}*/ 11786 SCIP_CONSDATA* consdata; 11787 int i; 11788 11789 consdata = SCIPconsGetData(cons); 11790 assert(consdata != NULL); 11791 11792 /* store variable expressions if not done so far */ 11793 SCIP_CALL( storeVarExprs(scip, conshdlr, consdata) ); 11794 11795 /* check whether array is too small in order to store all variables */ 11796 if( varssize < consdata->nvarexprs ) 11797 { 11798 *success = FALSE; 11799 return SCIP_OKAY; 11800 } 11801 11802 for( i = 0; i < consdata->nvarexprs; ++i ) 11803 { 11804 vars[i] = SCIPgetVarExprVar(consdata->varexprs[i]); 11805 assert(vars[i] != NULL); 11806 } 11807 11808 *success = TRUE; 11809 11810 return SCIP_OKAY; 11811 } 11812 11813 /** constraint method of constraint handler which returns the number of variables (if possible) */ 11814 static 11815 SCIP_DECL_CONSGETNVARS(consGetNVarsNonlinear) 11816 { /*lint --e{715}*/ 11817 SCIP_CONSDATA* consdata; 11818 11819 consdata = SCIPconsGetData(cons); 11820 assert(consdata != NULL); 11821 11822 /* store variable expressions if not done so far */ 11823 SCIP_CALL( storeVarExprs(scip, conshdlr, consdata) ); 11824 11825 *nvars = consdata->nvarexprs; 11826 *success = TRUE; 11827 11828 return SCIP_OKAY; 11829 } 11830 11831 /** constraint handler method to suggest dive bound changes during the generic diving algorithm */ 11832 #ifdef SCIP_DISABLED_CODE 11833 static 11834 SCIP_DECL_CONSGETDIVEBDCHGS(consGetDiveBdChgsNonlinear) 11835 { /*lint --e{715}*/ 11836 SCIPerrorMessage("method of nonlinear constraint handler not implemented yet\n"); 11837 SCIPABORT(); /*lint --e{527}*/ 11838 11839 return SCIP_OKAY; 11840 } 11841 #else 11842 #define consGetDiveBdChgsNonlinear NULL 11843 #endif 11844 11845 /** constraint handler method which returns the permutation symmetry detection graph of a constraint (if possible) */ 11846 static 11847 SCIP_DECL_CONSGETPERMSYMGRAPH(consGetPermsymGraphNonlinear) 11848 { /*lint --e{715}*/ 11849 SCIP_CALL( addSymmetryInformation(scip, SYM_SYMTYPE_PERM, cons, graph, success) ); 11850 11851 return SCIP_OKAY; 11852 } 11853 11854 /** constraint handler method which returns the signed permutation symmetry detection graph of a constraint (if possible) */ 11855 static 11856 SCIP_DECL_CONSGETSIGNEDPERMSYMGRAPH(consGetSignedPermsymGraphNonlinear) 11857 { /*lint --e{715}*/ 11858 SCIP_CALL( addSymmetryInformation(scip, SYM_SYMTYPE_SIGNPERM, cons, graph, success) ); 11859 11860 return SCIP_OKAY; 11861 } 11862 11863 /** output method of statistics table to output file stream 'file' */ 11864 static 11865 SCIP_DECL_TABLEOUTPUT(tableOutputNonlinear) 11866 { /*lint --e{715}*/ 11867 SCIP_CONSHDLR* conshdlr; 11868 SCIP_CONSHDLRDATA* conshdlrdata; 11869 11870 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME); 11871 assert(conshdlr != NULL); 11872 11873 conshdlrdata = SCIPconshdlrGetData(conshdlr); 11874 assert(conshdlrdata != NULL); 11875 11876 /* print statistics for constraint handler */ 11877 SCIPinfoMessage(scip, file, "Nonlinear Conshdlr : %10s %10s %10s %10s %10s %10s %10s\n", "WeakSepa", "TightenLP", "DespTghtLP", "DespBranch", "DespCutoff", "ForceLP", "CanonTime"); 11878 SCIPinfoMessage(scip, file, " enforce%-10s:", ""); 11879 SCIPinfoMessage(scip, file, " %10lld", conshdlrdata->nweaksepa); 11880 SCIPinfoMessage(scip, file, " %10lld", conshdlrdata->ntightenlp); 11881 SCIPinfoMessage(scip, file, " %10lld", conshdlrdata->ndesperatetightenlp); 11882 SCIPinfoMessage(scip, file, " %10lld", conshdlrdata->ndesperatebranch); 11883 SCIPinfoMessage(scip, file, " %10lld", conshdlrdata->ndesperatecutoff); 11884 SCIPinfoMessage(scip, file, " %10lld", conshdlrdata->nforcelp); 11885 SCIPinfoMessage(scip, file, "\n"); 11886 SCIPinfoMessage(scip, file, " presolve%-9s: %-65s", "", ""); 11887 SCIPinfoMessage(scip, file, " %10.2f", SCIPgetClockTime(scip, conshdlrdata->canonicalizetime)); 11888 SCIPinfoMessage(scip, file, "\n"); 11889 11890 return SCIP_OKAY; 11891 } 11892 11893 /** output method of statistics table to output file stream 'file' */ 11894 static 11895 SCIP_DECL_TABLEOUTPUT(tableOutputNlhdlr) 11896 { /*lint --e{715}*/ 11897 SCIP_CONSHDLR* conshdlr; 11898 SCIP_CONSHDLRDATA* conshdlrdata; 11899 11900 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME); 11901 assert(conshdlr != NULL); 11902 11903 /* skip nlhdlr table if there never were active nonlinear constraints */ 11904 if( SCIPconshdlrGetMaxNActiveConss(conshdlr) == 0 ) 11905 return SCIP_OKAY; 11906 11907 conshdlrdata = SCIPconshdlrGetData(conshdlr); 11908 assert(conshdlrdata != NULL); 11909 11910 /* print statistics for nonlinear handlers */ 11911 SCIPnlhdlrPrintStatistics(scip, conshdlrdata->nlhdlrs, conshdlrdata->nnlhdlrs, file); 11912 11913 return SCIP_OKAY; 11914 } 11915 11916 /** execution method of display nlhdlrs dialog */ 11917 static 11918 SCIP_DECL_DIALOGEXEC(dialogExecDisplayNlhdlrs) 11919 { /*lint --e{715}*/ 11920 SCIP_CONSHDLR* conshdlr; 11921 SCIP_CONSHDLRDATA* conshdlrdata; 11922 int i; 11923 11924 /* add dialog to history of dialogs that have been executed */ 11925 SCIP_CALL( SCIPdialoghdlrAddHistory(dialoghdlr, dialog, NULL, FALSE) ); 11926 11927 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME); 11928 assert(conshdlr != NULL); 11929 11930 conshdlrdata = SCIPconshdlrGetData(conshdlr); 11931 assert(conshdlrdata != NULL); 11932 11933 /* display list of nonlinear handler */ 11934 SCIPdialogMessage(scip, NULL, "\n"); 11935 SCIPdialogMessage(scip, NULL, " nonlinear handler enabled detectprio enforceprio description\n"); 11936 SCIPdialogMessage(scip, NULL, " ----------------- ------- ---------- ----------- -----------\n"); 11937 for( i = 0; i < conshdlrdata->nnlhdlrs; ++i ) 11938 { 11939 SCIP_NLHDLR* nlhdlr = conshdlrdata->nlhdlrs[i]; 11940 assert(nlhdlr != NULL); 11941 11942 SCIPdialogMessage(scip, NULL, " %-17s ", SCIPnlhdlrGetName(nlhdlr)); 11943 SCIPdialogMessage(scip, NULL, " %7s ", SCIPnlhdlrIsEnabled(nlhdlr) ? "yes" : "no"); 11944 SCIPdialogMessage(scip, NULL, " %10d ", SCIPnlhdlrGetDetectPriority(nlhdlr)); 11945 SCIPdialogMessage(scip, NULL, " %11d ", SCIPnlhdlrGetEnfoPriority(nlhdlr)); 11946 SCIPdialogMessage(scip, NULL, " %s", SCIPnlhdlrGetDesc(nlhdlr)); 11947 SCIPdialogMessage(scip, NULL, "\n"); 11948 } 11949 SCIPdialogMessage(scip, NULL, "\n"); 11950 11951 /* next dialog will be root dialog again */ 11952 *nextdialog = SCIPdialoghdlrGetRoot(dialoghdlr); 11953 11954 return SCIP_OKAY; 11955 } 11956 11957 /* 11958 * constraint handler specific interface methods 11959 */ 11960 11961 /** creates the handler for nonlinear constraints and includes it in SCIP */ 11962 SCIP_RETCODE SCIPincludeConshdlrNonlinear( 11963 SCIP* scip /**< SCIP data structure */ 11964 ) 11965 { 11966 SCIP_CONSHDLRDATA* conshdlrdata; 11967 SCIP_DIALOG* parentdialog; 11968 11969 /* create nonlinear constraint handler data */ 11970 SCIP_CALL( SCIPallocClearBlockMemory(scip, &conshdlrdata) ); 11971 conshdlrdata->intevalvar = intEvalVarBoundTightening; 11972 conshdlrdata->curboundstag = 1; 11973 conshdlrdata->lastboundrelax = 1; 11974 conshdlrdata->curpropboundstag = 1; 11975 conshdlrdata->newsoleventfilterpos = -1; 11976 SCIP_CALL( SCIPcreateClock(scip, &conshdlrdata->canonicalizetime) ); 11977 SCIP_CALL( SCIPqueueCreate(&conshdlrdata->reversepropqueue, 100, 2.0) ); 11978 SCIP_CALL( SCIPhashmapCreate(&conshdlrdata->var2expr, SCIPblkmem(scip), 100) ); 11979 11980 /* include constraint handler */ 11981 SCIP_CALL( SCIPincludeConshdlr(scip, CONSHDLR_NAME, CONSHDLR_DESC, 11982 CONSHDLR_SEPAPRIORITY, CONSHDLR_ENFOPRIORITY, CONSHDLR_CHECKPRIORITY, 11983 CONSHDLR_SEPAFREQ, CONSHDLR_PROPFREQ, CONSHDLR_EAGERFREQ, CONSHDLR_MAXPREROUNDS, 11984 CONSHDLR_DELAYSEPA, CONSHDLR_DELAYPROP, CONSHDLR_NEEDSCONS, 11985 CONSHDLR_PROP_TIMING, CONSHDLR_PRESOLTIMING, 11986 conshdlrCopyNonlinear, 11987 consFreeNonlinear, consInitNonlinear, consExitNonlinear, 11988 consInitpreNonlinear, consExitpreNonlinear, consInitsolNonlinear, consExitsolNonlinear, 11989 consDeleteNonlinear, consTransNonlinear, consInitlpNonlinear, 11990 consSepalpNonlinear, consSepasolNonlinear, consEnfolpNonlinear, consEnforelaxNonlinear, consEnfopsNonlinear, consCheckNonlinear, 11991 consPropNonlinear, consPresolNonlinear, consRespropNonlinear, consLockNonlinear, 11992 consActiveNonlinear, consDeactiveNonlinear, 11993 consEnableNonlinear, consDisableNonlinear, consDelvarsNonlinear, 11994 consPrintNonlinear, consCopyNonlinear, consParseNonlinear, 11995 consGetVarsNonlinear, consGetNVarsNonlinear, consGetDiveBdChgsNonlinear, consGetPermsymGraphNonlinear, 11996 consGetSignedPermsymGraphNonlinear, conshdlrdata) ); 11997 11998 /* add nonlinear constraint handler parameters */ 11999 /* TODO organize into more subcategories */ 12000 SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/maxproprounds", 12001 "limit on number of propagation rounds for a set of constraints within one round of SCIP propagation", 12002 &conshdlrdata->maxproprounds, FALSE, 10, 0, INT_MAX, NULL, NULL) ); 12003 12004 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/propauxvars", 12005 "whether to check bounds of all auxiliary variable to seed reverse propagation", 12006 &conshdlrdata->propauxvars, TRUE, TRUE, NULL, NULL) ); 12007 12008 SCIP_CALL( SCIPaddCharParam(scip, "constraints/" CONSHDLR_NAME "/varboundrelax", 12009 "strategy on how to relax variable bounds during bound tightening: relax (n)ot, relax by (a)bsolute value, relax always by a(b)solute value, relax by (r)relative value", 12010 &conshdlrdata->varboundrelax, TRUE, 'r', "nabr", NULL, NULL) ); 12011 12012 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/varboundrelaxamount", 12013 "by how much to relax variable bounds during bound tightening if strategy 'a', 'b', or 'r'", 12014 &conshdlrdata->varboundrelaxamount, TRUE, SCIPepsilon(scip), 0.0, 1.0, NULL, NULL) ); 12015 12016 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/conssiderelaxamount", 12017 "by how much to relax constraint sides during bound tightening", 12018 &conshdlrdata->conssiderelaxamount, TRUE, SCIPepsilon(scip), 0.0, 1.0, NULL, NULL) ); 12019 12020 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/vpmaxperturb", 12021 "maximal relative perturbation of reference point when computing facet of envelope of vertex-polyhedral function (dim>2)", 12022 &conshdlrdata->vp_maxperturb, TRUE, VERTEXPOLY_MAXPERTURBATION, 0.0, 1.0, NULL, NULL) ); 12023 12024 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/vpadjfacetthresh", 12025 "adjust computed facet of envelope of vertex-polyhedral function up to a violation of this value times LP feasibility tolerance", 12026 &conshdlrdata->vp_adjfacetthreshold, TRUE, VERTEXPOLY_ADJUSTFACETFACTOR, 0.0, SCIP_REAL_MAX, NULL, NULL) ); 12027 12028 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/vpdualsimplex", 12029 "whether to use dual simplex instead of primal simplex for LP that computes facet of vertex-polyhedral function", 12030 &conshdlrdata->vp_dualsimplex, TRUE, VERTEXPOLY_USEDUALSIMPLEX, NULL, NULL) ); 12031 12032 SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/bilinmaxnauxexprs", 12033 "maximal number of auxiliary expressions per bilinear term", 12034 &conshdlrdata->bilinmaxnauxexprs, FALSE, BILIN_MAXNAUXEXPRS, 0, INT_MAX, NULL, NULL) ); 12035 12036 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/reformbinprods", 12037 "whether to reformulate products of binary variables during presolving", 12038 &conshdlrdata->reformbinprods, FALSE, TRUE, NULL, NULL) ); 12039 12040 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/reformbinprodsand", 12041 "whether to use the AND constraint handler for reformulating binary products", 12042 &conshdlrdata->reformbinprodsand, FALSE, TRUE, NULL, NULL) ); 12043 12044 SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/reformbinprodsfac", 12045 "minimum number of terms to reformulate bilinear binary products by factorizing variables (<= 1: disabled)", 12046 &conshdlrdata->reformbinprodsfac, FALSE, 50, 1, INT_MAX, NULL, NULL) ); 12047 12048 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/forbidmultaggrnlvar", 12049 "whether to forbid multiaggregation of nonlinear variables", 12050 &conshdlrdata->forbidmultaggrnlvar, TRUE, TRUE, NULL, NULL) ); 12051 12052 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/tightenlpfeastol", 12053 "whether to tighten LP feasibility tolerance during enforcement, if it seems useful", 12054 &conshdlrdata->tightenlpfeastol, TRUE, TRUE, NULL, NULL) ); 12055 12056 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/propinenforce", 12057 "whether to (re)run propagation in enforcement", 12058 &conshdlrdata->propinenforce, TRUE, FALSE, NULL, NULL) ); 12059 12060 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/weakcutthreshold", 12061 "threshold for when to regard a cut from an estimator as weak (lower values allow more weak cuts)", 12062 &conshdlrdata->weakcutthreshold, TRUE, 0.2, 0.0, 1.0, NULL, NULL) ); 12063 12064 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/strongcutmaxcoef", 12065 "\"strong\" cuts will be scaled to have their maximal coef in [1/strongcutmaxcoef,strongcutmaxcoef]", 12066 &conshdlrdata->strongcutmaxcoef, TRUE, 1000.0, 1.0, SCIPinfinity(scip), NULL, NULL) ); 12067 12068 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/strongcutefficacy", 12069 "consider efficacy requirement when deciding whether a cut is \"strong\"", 12070 &conshdlrdata->strongcutefficacy, TRUE, FALSE, NULL, NULL) ); 12071 12072 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/forcestrongcut", 12073 "whether to force \"strong\" cuts in enforcement", 12074 &conshdlrdata->forcestrongcut, TRUE, FALSE, NULL, NULL) ); 12075 12076 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/enfoauxviolfactor", 12077 "an expression will be enforced if the \"auxiliary\" violation is at least this factor times the \"original\" violation", 12078 &conshdlrdata->enfoauxviolfactor, TRUE, 0.01, 0.0, 1.0, NULL, NULL) ); 12079 12080 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/weakcutminviolfactor", 12081 "retry enfo of constraint with weak cuts if violation is least this factor of maximal violated constraints", 12082 &conshdlrdata->weakcutminviolfactor, TRUE, 0.5, 0.0, 2.0, NULL, NULL) ); 12083 12084 SCIP_CALL( SCIPaddCharParam(scip, "constraints/" CONSHDLR_NAME "/rownotremovable", 12085 "whether to make rows to be non-removable in the node where they are added (can prevent some cycling): 'o'ff, in 'e'nforcement only, 'a'lways", 12086 &conshdlrdata->rownotremovable, TRUE, 'o', "oea", NULL, NULL) ); 12087 12088 SCIP_CALL( SCIPaddCharParam(scip, "constraints/" CONSHDLR_NAME "/violscale", 12089 "method how to scale violations to make them comparable (not used for feasibility check): (n)one, (a)ctivity and side, norm of (g)radient", 12090 &conshdlrdata->violscale, TRUE, 'n', "nag", NULL, NULL) ); 12091 12092 SCIP_CALL( SCIPaddCharParam(scip, "constraints/" CONSHDLR_NAME "/checkvarlocks", 12093 "whether variables contained in a single constraint should be forced to be at their lower or upper bounds ('d'isable, change 't'ype, add 'b'ound disjunction)", 12094 &conshdlrdata->checkvarlocks, TRUE, 't', "bdt", NULL, NULL) ); 12095 12096 SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/branching/aux", 12097 "from which depth on in the tree to allow branching on auxiliary variables (variables added for extended formulation)", 12098 &conshdlrdata->branchauxmindepth, FALSE, INT_MAX, 0, INT_MAX, NULL, NULL) ); 12099 12100 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/branching/external", 12101 "whether to use external branching candidates and branching rules for branching", 12102 &conshdlrdata->branchexternal, FALSE, FALSE, NULL, NULL) ); 12103 12104 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/branching/highviolfactor", 12105 "consider a constraint highly violated if its violation is >= this factor * maximal violation among all constraints", 12106 &conshdlrdata->branchhighviolfactor, FALSE, 0.0, 0.0, 1.0, NULL, NULL) ); 12107 12108 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/branching/highscorefactor", 12109 "consider a variable branching score high if its branching score >= this factor * maximal branching score among all variables", 12110 &conshdlrdata->branchhighscorefactor, FALSE, 0.9, 0.0, 1.0, NULL, NULL) ); 12111 12112 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/branching/violweight", 12113 "weight by how much to consider the violation assigned to a variable for its branching score", 12114 &conshdlrdata->branchviolweight, FALSE, 1.0, 0.0, SCIPinfinity(scip), NULL, NULL) ); 12115 12116 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/branching/dualweight", 12117 "weight by how much to consider the dual values of rows that contain a variable for its branching score", 12118 &conshdlrdata->branchdualweight, FALSE, 0.0, 0.0, SCIPinfinity(scip), NULL, NULL) ); 12119 12120 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/branching/pscostweight", 12121 "weight by how much to consider the pseudo cost of a variable for its branching score", 12122 &conshdlrdata->branchpscostweight, FALSE, 1.0, 0.0, SCIPinfinity(scip), NULL, NULL) ); 12123 12124 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/branching/domainweight", 12125 "weight by how much to consider the domain width in branching score", 12126 &conshdlrdata->branchdomainweight, FALSE, 0.0, 0.0, SCIPinfinity(scip), NULL, NULL) ); 12127 12128 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/branching/vartypeweight", 12129 "weight by how much to consider variable type (continuous: 0, binary: 1, integer: 0.1, impl-integer: 0.01) in branching score", 12130 &conshdlrdata->branchvartypeweight, FALSE, 0.5, 0.0, SCIPinfinity(scip), NULL, NULL) ); 12131 12132 SCIP_CALL( SCIPaddCharParam(scip, "constraints/" CONSHDLR_NAME "/branching/scoreagg", 12133 "how to aggregate several branching scores given for the same expression: 'a'verage, 'm'aximum, 's'um", 12134 &conshdlrdata->branchscoreagg, FALSE, 's', "ams", NULL, NULL) ); 12135 12136 SCIP_CALL( SCIPaddCharParam(scip, "constraints/" CONSHDLR_NAME "/branching/violsplit", 12137 "method used to split violation in expression onto variables: 'u'niform, 'm'idness of solution, 'd'omain width, 'l'ogarithmic domain width", 12138 &conshdlrdata->branchviolsplit, FALSE, 'm', "umdl", NULL, NULL) ); 12139 12140 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/branching/pscostreliable", 12141 "minimum pseudo-cost update count required to consider pseudo-costs reliable", 12142 &conshdlrdata->branchpscostreliable, FALSE, 2.0, 0.0, SCIPinfinity(scip), NULL, NULL) ); 12143 12144 SCIP_CALL( SCIPaddCharParam(scip, "constraints/" CONSHDLR_NAME "/linearizeheursol", 12145 "whether tight linearizations of nonlinear constraints should be added to cutpool when some heuristics finds a new solution ('o'ff, on new 'i'ncumbents, on 'e'very solution)", 12146 &conshdlrdata->linearizeheursol, FALSE, 'o', "oie", NULL, NULL) ); 12147 12148 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/assumeconvex", 12149 "whether to assume that any constraint is convex", 12150 &conshdlrdata->assumeconvex, FALSE, FALSE, NULL, NULL) ); 12151 12152 /* include handler for bound change events */ 12153 SCIP_CALL( SCIPincludeEventhdlrBasic(scip, &conshdlrdata->eventhdlr, CONSHDLR_NAME "_boundchange", 12154 "signals a bound change to a nonlinear constraint", processVarEvent, NULL) ); 12155 assert(conshdlrdata->eventhdlr != NULL); 12156 12157 /* include tables for statistics */ 12158 assert(SCIPfindTable(scip, TABLE_NAME_NONLINEAR) == NULL); 12159 SCIP_CALL( SCIPincludeTable(scip, TABLE_NAME_NONLINEAR, TABLE_DESC_NONLINEAR, FALSE, 12160 NULL, NULL, NULL, NULL, NULL, NULL, tableOutputNonlinear, 12161 NULL, TABLE_POSITION_NONLINEAR, TABLE_EARLIEST_STAGE_NONLINEAR) ); 12162 12163 assert(SCIPfindTable(scip, TABLE_NAME_NLHDLR) == NULL); 12164 SCIP_CALL( SCIPincludeTable(scip, TABLE_NAME_NLHDLR, TABLE_DESC_NLHDLR, TRUE, 12165 NULL, NULL, NULL, NULL, NULL, NULL, tableOutputNlhdlr, 12166 NULL, TABLE_POSITION_NLHDLR, TABLE_EARLIEST_STAGE_NLHDLR) ); 12167 12168 /* create, include, and release display nlhdlrs dialog */ 12169 if( SCIPgetRootDialog(scip) != NULL && SCIPdialogFindEntry(SCIPgetRootDialog(scip), "display", &parentdialog) == 1 ) 12170 { 12171 SCIP_DIALOG* dialog; 12172 12173 assert(parentdialog != NULL); 12174 assert(!SCIPdialogHasEntry(parentdialog, DIALOG_NAME)); 12175 12176 SCIP_CALL( SCIPincludeDialog(scip, &dialog, 12177 NULL, dialogExecDisplayNlhdlrs, NULL, NULL, 12178 DIALOG_NAME, DIALOG_DESC, DIALOG_ISSUBMENU, NULL) ); 12179 SCIP_CALL( SCIPaddDialogEntry(scip, parentdialog, dialog) ); 12180 SCIP_CALL( SCIPreleaseDialog(scip, &dialog) ); 12181 } 12182 12183 SCIP_CALL( SCIPincludeEventhdlrBasic(scip, NULL, CONSHDLR_NAME "_newsolution", "handles the event that a new primal solution has been found", 12184 processNewSolutionEvent, NULL) ); 12185 12186 return SCIP_OKAY; 12187 } 12188 12189 /** includes a nonlinear constraint upgrade method into the nonlinear constraint handler */ 12190 SCIP_RETCODE SCIPincludeConsUpgradeNonlinear( 12191 SCIP* scip, /**< SCIP data structure */ 12192 SCIP_DECL_NONLINCONSUPGD((*nlconsupgd)), /**< method to call for upgrading nonlinear constraint */ 12193 int priority, /**< priority of upgrading method */ 12194 SCIP_Bool active, /**< should the upgrading method by active by default? */ 12195 const char* conshdlrname /**< name of the constraint handler */ 12196 ) 12197 { 12198 SCIP_CONSHDLR* conshdlr; 12199 SCIP_CONSHDLRDATA* conshdlrdata; 12200 CONSUPGRADE* consupgrade; 12201 char paramname[SCIP_MAXSTRLEN]; 12202 char paramdesc[SCIP_MAXSTRLEN]; 12203 int i; 12204 12205 assert(conshdlrname != NULL ); 12206 assert(nlconsupgd != NULL); 12207 12208 /* find the nonlinear constraint handler */ 12209 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME); 12210 if( conshdlr == NULL ) 12211 { 12212 SCIPerrorMessage("nonlinear constraint handler not found\n"); 12213 return SCIP_PLUGINNOTFOUND; 12214 } 12215 12216 conshdlrdata = SCIPconshdlrGetData(conshdlr); 12217 assert(conshdlrdata != NULL); 12218 12219 /* check whether upgrade method exists already */ 12220 for( i = conshdlrdata->nconsupgrades - 1; i >= 0; --i ) 12221 { 12222 if( conshdlrdata->consupgrades[i]->consupgd == nlconsupgd ) 12223 { 12224 #ifdef SCIP_DEBUG 12225 SCIPwarningMessage(scip, "Try to add already known upgrade method for constraint handler <%s>.\n", conshdlrname); 12226 #endif 12227 return SCIP_OKAY; 12228 } 12229 } 12230 12231 /* create a nonlinear constraint upgrade data object */ 12232 SCIP_CALL( SCIPallocBlockMemory(scip, &consupgrade) ); 12233 consupgrade->consupgd = nlconsupgd; 12234 consupgrade->priority = priority; 12235 consupgrade->active = active; 12236 12237 /* insert nonlinear constraint upgrade method into constraint handler data */ 12238 SCIP_CALL( SCIPensureBlockMemoryArray(scip, &conshdlrdata->consupgrades, &conshdlrdata->consupgradessize, conshdlrdata->nconsupgrades+1) ); 12239 assert(conshdlrdata->nconsupgrades+1 <= conshdlrdata->consupgradessize); 12240 12241 for( i = conshdlrdata->nconsupgrades; i > 0 && conshdlrdata->consupgrades[i-1]->priority < consupgrade->priority; --i ) 12242 conshdlrdata->consupgrades[i] = conshdlrdata->consupgrades[i-1]; 12243 assert(0 <= i && i <= conshdlrdata->nconsupgrades); 12244 conshdlrdata->consupgrades[i] = consupgrade; 12245 conshdlrdata->nconsupgrades++; 12246 12247 /* adds parameter to turn on and off the upgrading step */ 12248 (void) SCIPsnprintf(paramname, SCIP_MAXSTRLEN, "constraints/" CONSHDLR_NAME "/upgrade/%s", conshdlrname); 12249 (void) SCIPsnprintf(paramdesc, SCIP_MAXSTRLEN, "enable nonlinear upgrading for constraint handler <%s>", conshdlrname); 12250 SCIP_CALL( SCIPaddBoolParam(scip, 12251 paramname, paramdesc, 12252 &consupgrade->active, FALSE, active, NULL, NULL) ); 12253 12254 return SCIP_OKAY; 12255 } 12256 12257 /** creates and captures a nonlinear constraint 12258 * 12259 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons() 12260 */ 12261 SCIP_RETCODE SCIPcreateConsNonlinear( 12262 SCIP* scip, /**< SCIP data structure */ 12263 SCIP_CONS** cons, /**< pointer to hold the created constraint */ 12264 const char* name, /**< name of constraint */ 12265 SCIP_EXPR* expr, /**< expression of constraint (must not be NULL) */ 12266 SCIP_Real lhs, /**< left hand side of constraint */ 12267 SCIP_Real rhs, /**< right hand side of constraint */ 12268 SCIP_Bool initial, /**< should the LP relaxation of constraint be in the initial LP? 12269 * Usually set to TRUE. Set to FALSE for 'lazy constraints'. */ 12270 SCIP_Bool separate, /**< should the constraint be separated during LP processing? 12271 * Usually set to TRUE. */ 12272 SCIP_Bool enforce, /**< should the constraint be enforced during node processing? 12273 * TRUE for model constraints, FALSE for additional, redundant constraints. */ 12274 SCIP_Bool check, /**< should the constraint be checked for feasibility? 12275 * TRUE for model constraints, FALSE for additional, redundant constraints. */ 12276 SCIP_Bool propagate, /**< should the constraint be propagated during node processing? 12277 * Usually set to TRUE. */ 12278 SCIP_Bool local, /**< is constraint only valid locally? 12279 * Usually set to FALSE. Has to be set to TRUE, e.g., for branching constraints. */ 12280 SCIP_Bool modifiable, /**< is constraint modifiable (subject to column generation)? 12281 * Usually set to FALSE. In column generation applications, set to TRUE if pricing 12282 * adds coefficients to this constraint. */ 12283 SCIP_Bool dynamic, /**< is constraint subject to aging? 12284 * Usually set to FALSE. Set to TRUE for own cuts which 12285 * are separated as constraints. */ 12286 SCIP_Bool removable /**< should the relaxation be removed from the LP due to aging or cleanup? 12287 * Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */ 12288 ) 12289 { 12290 /* TODO: (optional) modify the definition of the SCIPcreateConsNonlinear() call, if you don't need all the information */ 12291 SCIP_CONSHDLR* conshdlr; 12292 12293 /* find the nonlinear constraint handler */ 12294 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME); 12295 if( conshdlr == NULL ) 12296 { 12297 SCIPerrorMessage("nonlinear constraint handler not found\n"); 12298 return SCIP_PLUGINNOTFOUND; 12299 } 12300 12301 /* create constraint */ 12302 SCIP_CALL( createCons(scip, conshdlr, cons, name, expr, lhs, rhs, TRUE, 12303 initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable) ); 12304 12305 return SCIP_OKAY; 12306 } 12307 12308 /** creates and captures a nonlinear constraint with all its constraint flags set to their default values 12309 * 12310 * All flags can be set via SCIPconsSetFLAGNAME-methods. 12311 * 12312 * @see SCIPcreateConsNonlinear() for information about the basic constraint flag configuration. 12313 * 12314 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons() 12315 */ 12316 SCIP_RETCODE SCIPcreateConsBasicNonlinear( 12317 SCIP* scip, /**< SCIP data structure */ 12318 SCIP_CONS** cons, /**< pointer to hold the created constraint */ 12319 const char* name, /**< name of constraint */ 12320 SCIP_EXPR* expr, /**< expression of constraint (must not be NULL) */ 12321 SCIP_Real lhs, /**< left hand side of constraint */ 12322 SCIP_Real rhs /**< right hand side of constraint */ 12323 ) 12324 { 12325 SCIP_CALL( SCIPcreateConsNonlinear(scip, cons, name, expr, lhs, rhs, 12326 TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE) ); 12327 12328 return SCIP_OKAY; 12329 } 12330 12331 /** creates and captures a quadratic nonlinear constraint 12332 * 12333 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons() 12334 */ 12335 SCIP_RETCODE SCIPcreateConsQuadraticNonlinear( 12336 SCIP* scip, /**< SCIP data structure */ 12337 SCIP_CONS** cons, /**< pointer to hold the created constraint */ 12338 const char* name, /**< name of constraint */ 12339 int nlinvars, /**< number of linear terms */ 12340 SCIP_VAR** linvars, /**< array with variables in linear part */ 12341 SCIP_Real* lincoefs, /**< array with coefficients of variables in linear part */ 12342 int nquadterms, /**< number of quadratic terms */ 12343 SCIP_VAR** quadvars1, /**< array with first variables in quadratic terms */ 12344 SCIP_VAR** quadvars2, /**< array with second variables in quadratic terms */ 12345 SCIP_Real* quadcoefs, /**< array with coefficients of quadratic terms */ 12346 SCIP_Real lhs, /**< left hand side of quadratic equation */ 12347 SCIP_Real rhs, /**< right hand side of quadratic equation */ 12348 SCIP_Bool initial, /**< should the LP relaxation of constraint be in the initial LP? 12349 * Usually set to TRUE. Set to FALSE for 'lazy constraints'. */ 12350 SCIP_Bool separate, /**< should the constraint be separated during LP processing? 12351 * Usually set to TRUE. */ 12352 SCIP_Bool enforce, /**< should the constraint be enforced during node processing? 12353 * TRUE for model constraints, FALSE for additional, redundant constraints. */ 12354 SCIP_Bool check, /**< should the constraint be checked for feasibility? 12355 * TRUE for model constraints, FALSE for additional, redundant constraints. */ 12356 SCIP_Bool propagate, /**< should the constraint be propagated during node processing? 12357 * Usually set to TRUE. */ 12358 SCIP_Bool local, /**< is constraint only valid locally? 12359 * Usually set to FALSE. Has to be set to TRUE, e.g., for branching constraints. */ 12360 SCIP_Bool modifiable, /**< is constraint modifiable (subject to column generation)? 12361 * Usually set to FALSE. In column generation applications, set to TRUE if pricing 12362 * adds coefficients to this constraint. */ 12363 SCIP_Bool dynamic, /**< is constraint subject to aging? 12364 * Usually set to FALSE. Set to TRUE for own cuts which 12365 * are separated as constraints. */ 12366 SCIP_Bool removable /**< should the relaxation be removed from the LP due to aging or cleanup? 12367 * Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */ 12368 ) 12369 { 12370 SCIP_CONSHDLR* conshdlr; 12371 SCIP_EXPR* expr; 12372 12373 assert(nlinvars == 0 || (linvars != NULL && lincoefs != NULL)); 12374 assert(nquadterms == 0 || (quadvars1 != NULL && quadvars2 != NULL && quadcoefs != NULL)); 12375 12376 /* get nonlinear constraint handler */ 12377 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME); 12378 if( conshdlr == NULL ) 12379 { 12380 SCIPerrorMessage("nonlinear constraint handler not found\n"); 12381 return SCIP_PLUGINNOTFOUND; 12382 } 12383 12384 /* create quadratic expression */ 12385 SCIP_CALL( SCIPcreateExprQuadratic(scip, &expr, nlinvars, linvars, lincoefs, nquadterms, quadvars1, quadvars2, quadcoefs, exprownerCreate, (void*)conshdlr) ); 12386 assert(expr != NULL); 12387 12388 /* create nonlinear constraint */ 12389 SCIP_CALL( createCons(scip, conshdlr, cons, name, expr, lhs, rhs, FALSE, 12390 initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable) ); 12391 12392 /* release quadratic expression (captured by constraint now) */ 12393 SCIP_CALL( SCIPreleaseExpr(scip, &expr) ); 12394 12395 return SCIP_OKAY; 12396 } 12397 12398 /** creates and captures a quadratic nonlinear constraint with all its constraint flags set to their default values 12399 * 12400 * All flags can be set via SCIPconsSetFLAGNAME-methods. 12401 * 12402 * @see SCIPcreateConsQuadraticNonlinear() for information about the basic constraint flag configuration. 12403 * 12404 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons() 12405 */ 12406 SCIP_RETCODE SCIPcreateConsBasicQuadraticNonlinear( 12407 SCIP* scip, /**< SCIP data structure */ 12408 SCIP_CONS** cons, /**< pointer to hold the created constraint */ 12409 const char* name, /**< name of constraint */ 12410 int nlinvars, /**< number of linear terms */ 12411 SCIP_VAR** linvars, /**< array with variables in linear part */ 12412 SCIP_Real* lincoefs, /**< array with coefficients of variables in linear part */ 12413 int nquadterms, /**< number of quadratic terms */ 12414 SCIP_VAR** quadvars1, /**< array with first variables in quadratic terms */ 12415 SCIP_VAR** quadvars2, /**< array with second variables in quadratic terms */ 12416 SCIP_Real* quadcoefs, /**< array with coefficients of quadratic terms */ 12417 SCIP_Real lhs, /**< left hand side of quadratic equation */ 12418 SCIP_Real rhs /**< right hand side of quadratic equation */ 12419 ) 12420 { 12421 SCIP_CALL( SCIPcreateConsQuadraticNonlinear(scip, cons, name, nlinvars, linvars, lincoefs, nquadterms, quadvars1, quadvars2, quadcoefs, lhs, rhs, 12422 TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE) ); 12423 12424 return SCIP_OKAY; 12425 } 12426 12427 /** creates and captures a nonlinear constraint that is a second-order cone constraint with all its constraint flags set to their default values 12428 * 12429 * \f$\sqrt{\gamma + \sum_{i=1}^{n} (\alpha_i\, (x_i + \beta_i))^2} \leq \alpha_{n+1}\, (x_{n+1}+\beta_{n+1})\f$ 12430 * 12431 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons() 12432 */ 12433 SCIP_RETCODE SCIPcreateConsBasicSOCNonlinear( 12434 SCIP* scip, /**< SCIP data structure */ 12435 SCIP_CONS** cons, /**< pointer to hold the created constraint */ 12436 const char* name, /**< name of constraint */ 12437 int nvars, /**< number of variables on left hand side of constraint (n) */ 12438 SCIP_VAR** vars, /**< array with variables on left hand side (x_i) */ 12439 SCIP_Real* coefs, /**< array with coefficients of left hand side variables (alpha_i), or NULL if all 1.0 */ 12440 SCIP_Real* offsets, /**< array with offsets of variables (beta_i), or NULL if all 0.0 */ 12441 SCIP_Real constant, /**< constant on left hand side (gamma) */ 12442 SCIP_VAR* rhsvar, /**< variable on right hand side of constraint (x_{n+1}) */ 12443 SCIP_Real rhscoeff, /**< coefficient of variable on right hand side (alpha_{n+1}) */ 12444 SCIP_Real rhsoffset /**< offset of variable on right hand side (beta_{n+1}) */ 12445 ) 12446 { 12447 SCIP_EXPR* expr; 12448 SCIP_EXPR* lhssum; 12449 SCIP_EXPR* terms[2]; 12450 SCIP_Real termcoefs[2]; 12451 int i; 12452 12453 assert(vars != NULL || nvars == 0); 12454 12455 SCIP_CALL( SCIPcreateExprSum(scip, &lhssum, 0, NULL, NULL, constant, NULL, NULL) ); /* gamma */ 12456 for( i = 0; i < nvars; ++i ) 12457 { 12458 SCIP_EXPR* varexpr; 12459 SCIP_EXPR* powexpr; 12460 12461 SCIP_CALL( SCIPcreateExprVar(scip, &varexpr, vars[i], NULL, NULL) ); /* x_i */ 12462 if( offsets != NULL && offsets[i] != 0.0 ) 12463 { 12464 SCIP_EXPR* sum; 12465 SCIP_CALL( SCIPcreateExprSum(scip, &sum, 1, &varexpr, NULL, offsets[i], NULL, NULL) ); /* x_i + beta_i */ 12466 SCIP_CALL( SCIPcreateExprPow(scip, &powexpr, sum, 2.0, NULL, NULL) ); /* (x_i + beta_i)^2 */ 12467 SCIP_CALL( SCIPreleaseExpr(scip, &sum) ); 12468 } 12469 else 12470 { 12471 SCIP_CALL( SCIPcreateExprPow(scip, &powexpr, varexpr, 2.0, NULL, NULL) ); /* x_i^2 */ 12472 } 12473 12474 SCIP_CALL( SCIPappendExprSumExpr(scip, lhssum, powexpr, coefs != NULL ? coefs[i]*coefs[i] : 1.0) ); /* + alpha_i^2 (x_i + beta_i)^2 */ 12475 SCIP_CALL( SCIPreleaseExpr(scip, &varexpr) ); 12476 SCIP_CALL( SCIPreleaseExpr(scip, &powexpr) ); 12477 } 12478 12479 SCIP_CALL( SCIPcreateExprPow(scip, &terms[0], lhssum, 0.5, NULL, NULL) ); /* sqrt(...) */ 12480 SCIP_CALL( SCIPreleaseExpr(scip, &lhssum) ); 12481 termcoefs[0] = 1.0; 12482 12483 SCIP_CALL( SCIPcreateExprVar(scip, &terms[1], rhsvar, NULL, NULL) ); /* x_{n+1} */ 12484 termcoefs[1] = -rhscoeff; 12485 12486 SCIP_CALL( SCIPcreateExprSum(scip, &expr, 2, terms, termcoefs, 0.0, NULL, NULL) ); /* sqrt(...) - alpha_{n+1}x_{n_1} */ 12487 12488 SCIP_CALL( SCIPreleaseExpr(scip, &terms[1]) ); 12489 SCIP_CALL( SCIPreleaseExpr(scip, &terms[0]) ); 12490 12491 SCIP_CALL( SCIPcreateConsBasicNonlinear(scip, cons, name, expr, -SCIPinfinity(scip), rhscoeff * rhsoffset) ); 12492 12493 SCIP_CALL( SCIPreleaseExpr(scip, &expr) ); 12494 12495 return SCIP_OKAY; 12496 } 12497 12498 /** creates and captures a signpower nonlinear constraint with all its constraint flags set to their default values 12499 * 12500 * \f$\textrm{lhs} \leq \textrm{sign}(x+a) |x+a|^n + c z \leq \textrm{rhs}\f$ 12501 * 12502 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons() 12503 */ 12504 SCIP_RETCODE SCIPcreateConsBasicSignpowerNonlinear( 12505 SCIP* scip, /**< SCIP data structure */ 12506 SCIP_CONS** cons, /**< pointer to hold the created constraint */ 12507 const char* name, /**< name of constraint */ 12508 SCIP_VAR* x, /**< nonlinear variable x in constraint */ 12509 SCIP_VAR* z, /**< linear variable z in constraint */ 12510 SCIP_Real exponent, /**< exponent n of |x+offset|^n term in constraint */ 12511 SCIP_Real xoffset, /**< offset in |x+offset|^n term in constraint */ 12512 SCIP_Real zcoef, /**< coefficient of z in constraint */ 12513 SCIP_Real lhs, /**< left hand side of constraint */ 12514 SCIP_Real rhs /**< right hand side of constraint */ 12515 ) 12516 { 12517 SCIP_EXPR* xexpr; 12518 SCIP_EXPR* terms[2]; 12519 SCIP_Real coefs[2]; 12520 SCIP_EXPR* sumexpr; 12521 12522 assert(x != NULL); 12523 assert(z != NULL); 12524 12525 SCIP_CALL( SCIPcreateExprVar(scip, &xexpr, x, NULL, NULL) ); 12526 if( xoffset != 0.0 ) 12527 { 12528 SCIP_CALL( SCIPcreateExprSum(scip, &sumexpr, 1, &xexpr, NULL, xoffset, NULL, NULL) ); /* x + xoffset */ 12529 SCIP_CALL( SCIPcreateExprSignpower(scip, &terms[0], sumexpr, exponent, NULL, NULL) ); /* signpow(x + xoffset, exponent) */ 12530 12531 SCIP_CALL( SCIPreleaseExpr(scip, &sumexpr) ); 12532 } 12533 else 12534 { 12535 SCIP_CALL( SCIPcreateExprSignpower(scip, &terms[0], xexpr, exponent, NULL, NULL) ); /* signpow(x, exponent) */ 12536 } 12537 coefs[0] = 1.0; 12538 12539 SCIP_CALL( SCIPcreateExprVar(scip, &terms[1], z, NULL, NULL) ); 12540 coefs[1] = zcoef; 12541 12542 SCIP_CALL( SCIPcreateExprSum(scip, &sumexpr, 2, terms, coefs, 0.0, NULL, NULL) ); /* signpowexpr + zcoef * z */ 12543 12544 SCIP_CALL( SCIPcreateConsBasicNonlinear(scip, cons, name, sumexpr, lhs, rhs) ); 12545 12546 SCIP_CALL( SCIPreleaseExpr(scip, &sumexpr) ); 12547 SCIP_CALL( SCIPreleaseExpr(scip, &terms[1]) ); 12548 SCIP_CALL( SCIPreleaseExpr(scip, &terms[0]) ); 12549 SCIP_CALL( SCIPreleaseExpr(scip, &xexpr) ); 12550 12551 return SCIP_OKAY; 12552 } 12553 12554 /** gets tag indicating current local variable bounds */ 12555 SCIP_Longint SCIPgetCurBoundsTagNonlinear( 12556 SCIP_CONSHDLR* conshdlr /**< nonlinear constraint handler */ 12557 ) 12558 { 12559 SCIP_CONSHDLRDATA* conshdlrdata; 12560 12561 assert(conshdlr != NULL); 12562 conshdlrdata = SCIPconshdlrGetData(conshdlr); 12563 12564 return conshdlrdata->curboundstag; 12565 } 12566 12567 /** gets the `curboundstag` from the last time where variable bounds were relaxed */ 12568 SCIP_Longint SCIPgetLastBoundRelaxTagNonlinear( 12569 SCIP_CONSHDLR* conshdlr /**< nonlinear constraint handler */ 12570 ) 12571 { 12572 SCIP_CONSHDLRDATA* conshdlrdata; 12573 12574 assert(conshdlr != NULL); 12575 conshdlrdata = SCIPconshdlrGetData(conshdlr); 12576 12577 return conshdlrdata->lastboundrelax; 12578 } 12579 12580 /** increments `curboundstag` and resets `lastboundrelax` in constraint handler data 12581 * 12582 * @attention This method is not intended for normal use. 12583 * These tags are maintained by the event handler for variable bound change events. 12584 * This method is used by some unittests. 12585 */ 12586 void SCIPincrementCurBoundsTagNonlinear( 12587 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */ 12588 SCIP_Bool boundrelax /**< indicates whether a bound was relaxed, i.e., lastboundrelax should be set too */ 12589 ) 12590 { 12591 SCIP_CONSHDLRDATA* conshdlrdata; 12592 12593 conshdlrdata = SCIPconshdlrGetData(conshdlr); 12594 assert(conshdlrdata != NULL); 12595 12596 ++conshdlrdata->curboundstag; 12597 assert(conshdlrdata->curboundstag > 0); 12598 12599 if( boundrelax ) 12600 conshdlrdata->lastboundrelax = conshdlrdata->curboundstag; 12601 } 12602 12603 /** returns the hashmap that is internally used to map variables to their corresponding variable expressions */ 12604 SCIP_HASHMAP* SCIPgetVarExprHashmapNonlinear( 12605 SCIP_CONSHDLR* conshdlr /**< nonlinear constraint handler */ 12606 ) 12607 { 12608 assert(conshdlr != NULL); 12609 12610 return SCIPconshdlrGetData(conshdlr)->var2expr; 12611 } 12612 12613 /** processes a rowprep for cut addition and maybe report branchscores */ 12614 SCIP_RETCODE SCIPprocessRowprepNonlinear( 12615 SCIP* scip, /**< SCIP data structure */ 12616 SCIP_NLHDLR* nlhdlr, /**< nonlinear handler which provided the estimator */ 12617 SCIP_CONS* cons, /**< nonlinear constraint */ 12618 SCIP_EXPR* expr, /**< expression */ 12619 SCIP_ROWPREP* rowprep, /**< cut to be added */ 12620 SCIP_Bool overestimate, /**< whether the expression needs to be over- or underestimated */ 12621 SCIP_VAR* auxvar, /**< auxiliary variable */ 12622 SCIP_Real auxvalue, /**< current value of expression w.r.t. auxiliary variables as obtained from EVALAUX */ 12623 SCIP_Bool allowweakcuts, /**< whether we should only look for "strong" cuts, or anything that separates is fine */ 12624 SCIP_Bool branchscoresuccess, /**< whether the estimator generation generated branching scores */ 12625 SCIP_Bool inenforcement, /**< whether we are in enforcement, or only in separation */ 12626 SCIP_SOL* sol, /**< solution to be separated (NULL for the LP solution) */ 12627 SCIP_RESULT* result /**< pointer to store the result */ 12628 ) 12629 { 12630 SCIP_Real cutviol; 12631 SCIP_CONSHDLRDATA* conshdlrdata; 12632 SCIP_Real auxvarvalue = SCIP_INVALID; 12633 SCIP_Bool sepasuccess; 12634 SCIP_Real estimateval = SCIP_INVALID; 12635 SCIP_Real mincutviolation; 12636 12637 assert(nlhdlr != NULL); 12638 assert(cons != NULL); 12639 assert(expr != NULL); 12640 assert(rowprep != NULL); 12641 assert(auxvar != NULL); 12642 assert(result != NULL); 12643 12644 /* decide on minimal violation of cut */ 12645 if( sol == NULL ) 12646 mincutviolation = SCIPgetLPFeastol(scip); /* we enforce an LP solution */ 12647 else 12648 mincutviolation = SCIPfeastol(scip); 12649 12650 conshdlrdata = SCIPconshdlrGetData(SCIPconsGetHdlr(cons)); 12651 assert(conshdlrdata != NULL); 12652 12653 sepasuccess = TRUE; 12654 12655 cutviol = SCIPgetRowprepViolation(scip, rowprep, sol, NULL); 12656 if( cutviol > 0.0 ) 12657 { 12658 auxvarvalue = SCIPgetSolVal(scip, sol, auxvar); 12659 12660 /* check whether cut is weak (if f(x) not defined, then it's never weak) */ 12661 if( !allowweakcuts && auxvalue != SCIP_INVALID ) 12662 { 12663 /* let the estimator be c'x-b, the auxvar is z (=auxvarvalue), and the expression is f(x) (=auxvalue) 12664 * then if we are underestimating and since the cut is violated, we should have z <= c'x-b <= f(x) 12665 * cutviol is c'x-b - z, so estimator value is c'x-b = z + cutviol 12666 * if the estimator value (c'x-b) is too close to z (auxvarvalue), when compared to f(x) (auxvalue), 12667 * then let's call this a weak cut that is, it's a weak cut if c'x-b <= z + weakcutthreshold * (f(x)-z) 12668 * <-> c'x-b - z <= weakcutthreshold * (f(x)-z) 12669 * 12670 * if we are overestimating, we have z >= c'x-b >= f(x) 12671 * cutviol is z - (c'x-b), so estimator value is c'x-b = z - cutviol 12672 * it's weak if c'x-b >= f(x) + (1-weakcutthreshold) * (z - f(x)) 12673 * <-> c'x-b - z >= weakcutthreshold * (f(x)-z) 12674 * 12675 * when linearizing convex expressions, then we should have c'x-b = f(x), so they would never be weak 12676 */ 12677 if( (!overestimate && ( cutviol <= conshdlrdata->weakcutthreshold * (auxvalue - auxvarvalue))) || 12678 ( overestimate && (-cutviol >= conshdlrdata->weakcutthreshold * (auxvalue - auxvarvalue))) ) 12679 { 12680 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " estimate of nlhdlr %s succeeded, but cut is too "\ 12681 "weak: auxvarvalue %g estimateval %g auxvalue %g (over %d)\n", 12682 SCIPnlhdlrGetName(nlhdlr), auxvarvalue, 12683 auxvarvalue + (overestimate ? -cutviol : cutviol), auxvalue, overestimate); ) 12684 sepasuccess = FALSE; 12685 } 12686 } 12687 12688 /* save estimator value for later, see long comment above why this gives the value for c'x-b */ 12689 estimateval = auxvarvalue + (!overestimate ? cutviol : -cutviol); 12690 } 12691 else 12692 { 12693 sepasuccess = FALSE; 12694 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " estimate of nlhdlr %s succeeded, but cut does not "\ 12695 "separate\n", SCIPnlhdlrGetName(nlhdlr)); ) 12696 } 12697 12698 /* clean up estimator */ 12699 if( sepasuccess ) 12700 { 12701 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " estimate of nlhdlr %s succeeded: auxvarvalue %g "\ 12702 "estimateval %g auxvalue %g (over %d)\n ", SCIPnlhdlrGetName(nlhdlr), auxvarvalue, 12703 auxvarvalue + (overestimate ? -cutviol : cutviol), auxvalue, overestimate); 12704 SCIPprintRowprep(scip, rowprep, enfologfile); ) 12705 12706 /* if not allowweakcuts, then do not attempt to get cuts more violated by scaling them up, 12707 * instead, may even scale them down, that is, scale so that max coef is close to 1 12708 */ 12709 if( !allowweakcuts ) 12710 { 12711 SCIP_CALL( SCIPcleanupRowprep2(scip, rowprep, sol, conshdlrdata->strongcutmaxcoef, &sepasuccess) ); 12712 12713 if( !sepasuccess ) 12714 { 12715 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " cleanup cut failed due to bad numerics\n"); ) 12716 } 12717 else 12718 { 12719 cutviol = SCIPgetRowprepViolation(scip, rowprep, sol, &sepasuccess); 12720 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " cleanup succeeded, violation = %g and %sreliable, "\ 12721 "min requ viol = %g\n", cutviol, sepasuccess ? "" : "not ", mincutviolation); ) 12722 if( sepasuccess ) 12723 sepasuccess = cutviol > mincutviolation; 12724 } 12725 12726 if( sepasuccess && auxvalue != SCIP_INVALID ) 12727 { 12728 /* check whether cut is weak now 12729 * auxvar z may now have a coefficient due to scaling (down) in cleanup - take this into account when 12730 * reconstructing estimateval from cutviol (TODO improve or remove?) 12731 */ 12732 SCIP_Real auxvarcoef = 0.0; 12733 int i; 12734 12735 /* get absolute value of coef of auxvar in row - this makes the whole check here more expensive than 12736 * it should be... 12737 */ 12738 for( i = 0; i < SCIProwprepGetNVars(rowprep); ++i ) 12739 { 12740 if( SCIProwprepGetVars(rowprep)[i] == auxvar ) 12741 { 12742 auxvarcoef = REALABS(SCIProwprepGetCoefs(rowprep)[i]); 12743 break; 12744 } 12745 } 12746 12747 if( auxvarcoef == 0.0 || 12748 (!overestimate && ( cutviol / auxvarcoef <= conshdlrdata->weakcutthreshold * (auxvalue - auxvarvalue))) || 12749 ( overestimate && (-cutviol / auxvarcoef >= conshdlrdata->weakcutthreshold * (auxvalue - auxvarvalue))) ) 12750 { 12751 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " cut is too weak after cleanup: auxvarvalue %g estimateval %g auxvalue %g (over %d)\n", 12752 auxvarvalue, auxvarvalue + (overestimate ? -cutviol : cutviol) / auxvarcoef, auxvalue, overestimate); ) 12753 sepasuccess = FALSE; 12754 } 12755 } 12756 } 12757 else 12758 { 12759 /* TODO if violations are really tiny, then maybe handle special (decrease LP feastol, for example) */ 12760 12761 /* if estimate didn't report branchscores explicitly, then consider branching on those children for 12762 * which the following cleanup changes coefficients (we had/have this in expr_sum this way) 12763 */ 12764 if( !branchscoresuccess ) 12765 SCIProwprepRecordModifications(rowprep); 12766 12767 SCIP_CALL( SCIPcleanupRowprep(scip, rowprep, sol, mincutviolation, &cutviol, &sepasuccess) ); 12768 12769 if( !sepasuccess ) 12770 { 12771 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " cleanup failed, %d coefs modified, cutviol %g\n", 12772 SCIProwprepGetNModifiedVars(rowprep), cutviol); ) 12773 } 12774 12775 /* if cleanup left us with a useless cut, then consider branching on variables for which coef were 12776 * changed 12777 */ 12778 if( !sepasuccess && !branchscoresuccess && SCIProwprepGetNModifiedVars(rowprep) > 0 ) 12779 { 12780 SCIP_Real violscore; 12781 12782 #ifdef BRSCORE_ABSVIOL 12783 violscore = getExprAbsAuxViolation(scip, expr, auxvalue, sol, NULL, NULL); 12784 #else 12785 SCIP_CALL( SCIPgetExprRelAuxViolationNonlinear(scip, expr, auxvalue, sol, &violscore, NULL, NULL) ); 12786 #endif 12787 SCIP_CALL( addExprViolScoresAuxVars(scip, expr, violscore, SCIProwprepGetModifiedVars(rowprep), SCIProwprepGetNModifiedVars(rowprep), sol, &branchscoresuccess) ); 12788 12789 /* addConsExprExprBranchScoresAuxVars can fail if the only vars for which the coef was changed 12790 * - were fixed, 12791 * - are this expr's auxvar (I don't think it makes sense to branch on that one (would it?)), or 12792 * - if a variable in the rowprep is not in expr (can happen with indicator added by perspective) 12793 * the first case came up again in #3085 and I don't see how to exclude this in the assert, 12794 * so I'm disabling the assert for now 12795 */ 12796 /* assert(branchscoresuccess || (rowprep->nmodifiedvars == 1 && rowprep->modifiedvars[0] == auxvar) || 12797 strcmp(SCIPnlhdlrGetName(nlhdlr), "perspective")==0); */ 12798 } 12799 } 12800 } 12801 12802 /* if cut looks good (numerics ok and cutting off solution), then turn into row and add to sepastore */ 12803 if( sepasuccess ) 12804 { 12805 SCIP_ROW* row; 12806 12807 if( conshdlrdata->branchdualweight > 0.0 ) 12808 { 12809 /* store remaining gap |f(x)-estimateval| in row name, which could be used in getDualBranchscore 12810 * skip if gap is zero 12811 */ 12812 if( auxvalue == SCIP_INVALID ) 12813 strcat(SCIProwprepGetName(rowprep), "_estimategap=inf"); 12814 else if( !SCIPisEQ(scip, auxvalue, estimateval) ) 12815 { 12816 char gap[40]; 12817 (void) sprintf(gap, "_estimategap=%g", REALABS(auxvalue - estimateval)); 12818 strcat(SCIProwprepGetName(rowprep), gap); 12819 } 12820 } 12821 12822 SCIP_CALL( SCIPgetRowprepRowCons(scip, &row, rowprep, cons) ); 12823 12824 if( !allowweakcuts && conshdlrdata->strongcutefficacy && !SCIPisCutEfficacious(scip, sol, row) ) 12825 { 12826 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " cut efficacy %g is too low (minefficacy=%g)\n", 12827 SCIPgetCutEfficacy(scip, sol, row), SCIPgetSepaMinEfficacy(scip)); ) 12828 } 12829 else if( !SCIPisCutApplicable(scip, row) ) 12830 { 12831 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " cut not applicable (e.g., cut is boundchange below eps)\n"); ) 12832 } 12833 else 12834 { 12835 SCIP_Bool infeasible; 12836 12837 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " adding cut "); 12838 SCIP_CALL( SCIPprintRow(scip, row, enfologfile) ); ) 12839 12840 /* I take !allowweakcuts as equivalent for having a strong cut (we usually have allowweakcuts=TRUE only 12841 * if we haven't found strong cuts before) 12842 */ 12843 SCIP_CALL( SCIPaddRow(scip, row, conshdlrdata->forcestrongcut && !allowweakcuts && inenforcement, &infeasible) ); 12844 12845 /* mark row as not removable from LP for current node (this can prevent some cycling) */ 12846 if( conshdlrdata->rownotremovable == 'a' || (conshdlrdata->rownotremovable == 'e' && inenforcement) ) 12847 SCIPmarkRowNotRemovableLocal(scip, row); 12848 12849 if( infeasible ) 12850 { 12851 *result = SCIP_CUTOFF; 12852 SCIPnlhdlrIncrementNCutoffs(nlhdlr); 12853 } 12854 else 12855 { 12856 *result = SCIP_SEPARATED; 12857 SCIPnlhdlrIncrementNSeparated(nlhdlr); 12858 } 12859 } 12860 12861 SCIP_CALL( SCIPreleaseRow(scip, &row) ); 12862 } 12863 else if( branchscoresuccess ) 12864 { 12865 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " separation with estimate of nlhdlr %s failed, but "\ 12866 "branching candidates added\n", SCIPnlhdlrGetName(nlhdlr)); ) 12867 12868 /* well, not branched, but addConsExprExprViolScoresAuxVars() added scores to (aux)variables and that makes the 12869 * expressions eligible for branching candidate, see enforceConstraints() and branching() 12870 */ 12871 *result = SCIP_BRANCHED; 12872 } 12873 else 12874 { 12875 ENFOLOG( SCIPinfoMessage(scip, enfologfile, " separation with estimate of nlhdlr %s failed and no "\ 12876 "branching candidates%s\n", SCIPnlhdlrGetName(nlhdlr), (allowweakcuts && inenforcement) ? 12877 " (!)" : ""); ) 12878 } 12879 12880 return SCIP_OKAY; 12881 } 12882 12883 /** returns whether all nonlinear constraints are assumed to be convex */ 12884 SCIP_Bool SCIPassumeConvexNonlinear( 12885 SCIP_CONSHDLR* conshdlr 12886 ) 12887 { 12888 SCIP_CONSHDLRDATA* conshdlrdata; 12889 12890 assert(conshdlr != NULL); 12891 12892 conshdlrdata = SCIPconshdlrGetData(conshdlr); 12893 assert(conshdlrdata != NULL); 12894 12895 return conshdlrdata->assumeconvex; 12896 } 12897 12898 /** collects all bilinear terms for a given set of constraints 12899 * 12900 * @attention This method should only be used for unit tests that depend on SCIPgetBilinTermsNonlinear(), 12901 * SCIPgetBilinTermNonlinear() or SCIPgetBilinTermIdxNonlinear(). 12902 */ 12903 SCIP_RETCODE SCIPcollectBilinTermsNonlinear( 12904 SCIP* scip, /**< SCIP data structure */ 12905 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */ 12906 SCIP_CONS** conss, /**< nonlinear constraints */ 12907 int nconss /**< total number of nonlinear constraints */ 12908 ) 12909 { 12910 assert(conshdlr != NULL); 12911 assert(conss != NULL || nconss == 0); 12912 12913 SCIP_CALL( bilinearTermsInsertAll(scip, conshdlr, conss, nconss) ); 12914 12915 return SCIP_OKAY; 12916 } 12917 12918 /** returns the total number of bilinear terms that are contained in all nonlinear constraints 12919 * 12920 * @note This method should only be used after auxiliary variables have been created, i.e., after CONSINITLP. 12921 */ 12922 int SCIPgetNBilinTermsNonlinear( 12923 SCIP_CONSHDLR* conshdlr /**< nonlinear constraint handler */ 12924 ) 12925 { 12926 SCIP_CONSHDLRDATA* conshdlrdata; 12927 12928 assert(conshdlr != NULL); 12929 12930 conshdlrdata = SCIPconshdlrGetData(conshdlr); 12931 assert(conshdlrdata != NULL); 12932 12933 return conshdlrdata->nbilinterms; 12934 } 12935 12936 /** returns all bilinear terms that are contained in all nonlinear constraints 12937 * 12938 * @note This method should only be used after auxiliary variables have been created, i.e., after CONSINITLP. 12939 * @note The value of the auxiliary variable of a bilinear term might be NULL, which indicates that the term does not have an auxiliary variable. 12940 */ 12941 SCIP_CONSNONLINEAR_BILINTERM* SCIPgetBilinTermsNonlinear( 12942 SCIP_CONSHDLR* conshdlr /**< nonlinear constraint handler */ 12943 ) 12944 { 12945 SCIP_CONSHDLRDATA* conshdlrdata; 12946 12947 assert(conshdlr != NULL); 12948 12949 conshdlrdata = SCIPconshdlrGetData(conshdlr); 12950 assert(conshdlrdata != NULL); 12951 12952 return conshdlrdata->bilinterms; 12953 } 12954 12955 /** returns the index of the bilinear term representing the product of the two given variables 12956 * 12957 * @note The method should only be used after auxiliary variables have been created, i.e., after CONSINITLP. 12958 * @return The method returns -1 if the variables do not appear bilinearly. 12959 */ 12960 int SCIPgetBilinTermIdxNonlinear( 12961 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */ 12962 SCIP_VAR* x, /**< first variable */ 12963 SCIP_VAR* y /**< second variable */ 12964 ) 12965 { 12966 SCIP_CONSHDLRDATA* conshdlrdata; 12967 SCIP_CONSNONLINEAR_BILINTERM entry; 12968 int idx; 12969 12970 assert(conshdlr != NULL); 12971 assert(x != NULL); 12972 assert(y != NULL); 12973 12974 conshdlrdata = SCIPconshdlrGetData(conshdlr); 12975 assert(conshdlrdata != NULL); 12976 12977 if( conshdlrdata->bilinhashtable == NULL ) 12978 { 12979 return -1; 12980 } 12981 12982 /* ensure that x.index <= y.index */ 12983 if( SCIPvarCompare(x, y) == 1 ) 12984 { 12985 SCIPswapPointers((void**)&x, (void**)&y); 12986 } 12987 assert(SCIPvarCompare(x, y) < 1); 12988 12989 /* use a new entry to find the image in the bilinear hash table */ 12990 entry.x = x; 12991 entry.y = y; 12992 idx = (int)(size_t)SCIPhashtableRetrieve(conshdlrdata->bilinhashtable, (void*)&entry) - 1; 12993 assert(idx >= -1 && idx < conshdlrdata->nbilinterms); 12994 assert(idx < 0 || conshdlrdata->bilinterms[idx].x == x); 12995 assert(idx < 0 || conshdlrdata->bilinterms[idx].y == y); 12996 12997 return idx; 12998 } 12999 13000 /** returns the bilinear term that represents the product of two given variables 13001 * 13002 * @note The method should only be used after auxiliary variables have been created, i.e., after CONSINITLP. 13003 * @return The method returns NULL if the variables do not appear bilinearly. 13004 */ 13005 SCIP_CONSNONLINEAR_BILINTERM* SCIPgetBilinTermNonlinear( 13006 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */ 13007 SCIP_VAR* x, /**< first variable */ 13008 SCIP_VAR* y /**< second variable */ 13009 ) 13010 { 13011 SCIP_CONSHDLRDATA* conshdlrdata; 13012 int idx; 13013 13014 assert(conshdlr != NULL); 13015 assert(x != NULL); 13016 assert(y != NULL); 13017 13018 conshdlrdata = SCIPconshdlrGetData(conshdlr); 13019 assert(conshdlrdata != NULL); 13020 13021 idx = SCIPgetBilinTermIdxNonlinear(conshdlr, x, y); 13022 assert(idx >= -1 && idx < conshdlrdata->nbilinterms); 13023 13024 if( idx >= 0 ) 13025 { 13026 return &conshdlrdata->bilinterms[idx]; 13027 } 13028 13029 return NULL; 13030 } 13031 13032 /** evaluates an auxiliary expression for a bilinear term */ 13033 SCIP_Real SCIPevalBilinAuxExprNonlinear( 13034 SCIP* scip, /**< SCIP data structure */ 13035 SCIP_VAR* x, /**< first variable of the bilinear term */ 13036 SCIP_VAR* y, /**< second variable of the bilinear term */ 13037 SCIP_CONSNONLINEAR_AUXEXPR* auxexpr, /**< auxiliary expression */ 13038 SCIP_SOL* sol /**< solution at which to evaluate (can be NULL) */ 13039 ) 13040 { 13041 assert(scip != NULL); 13042 assert(x != NULL); 13043 assert(y != NULL); 13044 assert(auxexpr != NULL); 13045 assert(auxexpr->auxvar != NULL); 13046 13047 return auxexpr->cst + auxexpr->coefs[0] * SCIPgetSolVal(scip, sol, auxexpr->auxvar) + 13048 auxexpr->coefs[1] * SCIPgetSolVal(scip, sol, x) + auxexpr->coefs[2] * SCIPgetSolVal(scip, sol, y); 13049 } 13050 13051 /** stores the variables of a bilinear term in the data of the constraint handler */ 13052 SCIP_RETCODE SCIPinsertBilinearTermExistingNonlinear( 13053 SCIP* scip, /**< SCIP data structure */ 13054 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 13055 SCIP_VAR* x, /**< first variable */ 13056 SCIP_VAR* y, /**< second variable */ 13057 SCIP_VAR* auxvar, /**< auxiliary variable (might be NULL) */ 13058 int nlockspos, /**< number of positive expression locks */ 13059 int nlocksneg /**< number of negative expression locks */ 13060 ) 13061 { 13062 SCIP_CONSHDLRDATA* conshdlrdata; 13063 SCIP_CONSNONLINEAR_BILINTERM* term; 13064 int idx; 13065 13066 assert(conshdlr != NULL); 13067 13068 conshdlrdata = SCIPconshdlrGetData(conshdlr); 13069 assert(conshdlrdata != NULL); 13070 13071 SCIP_CALL( bilinearTermsInsertEntry(scip, conshdlr, x, y, nlockspos, nlocksneg, &idx, TRUE) ); 13072 13073 term = &conshdlrdata->bilinterms[idx]; 13074 assert(term != NULL); 13075 assert(term->nauxexprs == 0); /* existing terms should be added before implicit terms */ 13076 assert(term->aux.var == NULL); /* there should not already be an auxvar, that is, existing terms should exist only once (common subexprs should have been eliminated) */ 13077 13078 /* store and capture auxiliary variable */ 13079 if( auxvar != NULL ) 13080 { 13081 term->aux.var = auxvar; 13082 SCIP_CALL( SCIPcaptureVar(scip, auxvar) ); 13083 } 13084 13085 return SCIP_OKAY; 13086 } 13087 13088 /** stores the variables of a bilinear term in the data of the constraint handler */ 13089 SCIP_RETCODE SCIPinsertBilinearTermImplicitNonlinear( 13090 SCIP* scip, /**< SCIP data structure */ 13091 SCIP_CONSHDLR* conshdlr, /**< constraint handler */ 13092 SCIP_VAR* x, /**< first variable */ 13093 SCIP_VAR* y, /**< second variable */ 13094 SCIP_VAR* auxvar, /**< auxiliary variable (might be NULL) */ 13095 SCIP_Real coefx, /**< coefficient of x in the auxiliary expression */ 13096 SCIP_Real coefy, /**< coefficient of y in the auxiliary expression */ 13097 SCIP_Real coefaux, /**< coefficient of auxvar in the auxiliary expression */ 13098 SCIP_Real cst, /**< constant of the auxiliary expression */ 13099 SCIP_Bool overestimate /**< whether the auxiliary expression overestimates the bilinear product */ 13100 ) 13101 { 13102 SCIP_CONSHDLRDATA* conshdlrdata; 13103 SCIP_CONSNONLINEAR_BILINTERM* term; 13104 SCIP_CONSNONLINEAR_AUXEXPR* auxexpr; 13105 int idx; 13106 int nlockspos; 13107 int nlocksneg; 13108 SCIP_Bool added; 13109 13110 assert(conshdlr != NULL); 13111 13112 conshdlrdata = SCIPconshdlrGetData(conshdlr); 13113 assert(conshdlrdata != NULL); 13114 13115 nlockspos = overestimate ? 1 : 0; 13116 nlocksneg = overestimate ? 0 : 1; 13117 13118 SCIP_CALL( bilinearTermsInsertEntry(scip, conshdlr, x, y, nlockspos, nlocksneg, &idx, FALSE) ); 13119 13120 term = &conshdlrdata->bilinterms[idx]; 13121 assert(term != NULL); 13122 assert(SCIPvarCompare(term->x, term->y) < 1); 13123 13124 if( term->existing && term->nauxexprs == 0 && term->aux.var != NULL ) 13125 { 13126 SCIP_CONSNONLINEAR_AUXEXPR* auxvarexpr; 13127 /* this is the case where we are adding an implicitly defined relation for a product that has already 13128 * been explicitly defined; convert auxvar into an auxexpr */ 13129 13130 /* nothing to do if we aren't allowed to add more than one auxexpr per term */ 13131 if( conshdlrdata->bilinmaxnauxexprs <= 1 ) 13132 return SCIP_OKAY; 13133 13134 SCIP_CALL( SCIPallocBlockMemory(scip, &auxvarexpr) ); 13135 auxvarexpr->cst = 0.0; 13136 auxvarexpr->coefs[0] = 1.0; 13137 auxvarexpr->coefs[1] = 0.0; 13138 auxvarexpr->coefs[2] = 0.0; 13139 auxvarexpr->auxvar = term->aux.var; 13140 auxvarexpr->underestimate = term->nlocksneg > 0; 13141 auxvarexpr->overestimate = term->nlockspos > 0; 13142 13143 /* before we were working with term->aux.var; now aux.var has been saved and aux.exprs can be initialised to NULL */ 13144 term->aux.exprs = NULL; 13145 13146 SCIP_CALL( bilinTermAddAuxExpr(scip, conshdlrdata, term, auxvarexpr, &added) ); 13147 13148 /* since there were no auxexprs before and we've already checked for bilinmaxnauxexprs, auxvarexpr should always be added */ 13149 assert(added); 13150 } 13151 13152 /* create and add auxexpr */ 13153 SCIP_CALL( SCIPallocBlockMemory(scip, &auxexpr) ); 13154 auxexpr->underestimate = !overestimate; 13155 auxexpr->overestimate = overestimate; 13156 auxexpr->auxvar = auxvar; 13157 auxexpr->coefs[0] = coefaux; 13158 if( term->x == x ) 13159 { 13160 assert(term->y == y); 13161 auxexpr->coefs[1] = coefx; 13162 auxexpr->coefs[2] = coefy; 13163 } 13164 else 13165 { 13166 assert(term->x == y); 13167 assert(term->y == x); 13168 auxexpr->coefs[1] = coefy; 13169 auxexpr->coefs[2] = coefx; 13170 } 13171 auxexpr->cst = cst; 13172 SCIP_CALL( bilinTermAddAuxExpr(scip, conshdlrdata, term, auxexpr, &added) ); 13173 13174 if( !added ) 13175 { 13176 SCIPfreeBlockMemory(scip, &auxexpr); 13177 } 13178 else if( auxvar != NULL ) 13179 { /* capture auxiliary variable */ 13180 SCIP_CALL( SCIPcaptureVar(scip, auxvar) ); 13181 } 13182 13183 return SCIP_OKAY; 13184 } 13185 13186 /* replication of long comment on SCIPcomputeFacetVertexPolyhedralNonlinear() in cons_nonlinear.h omitted here */ 13187 SCIP_RETCODE SCIPcomputeFacetVertexPolyhedralNonlinear( 13188 SCIP* scip, /**< SCIP data structure */ 13189 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */ 13190 SCIP_Bool overestimate, /**< whether to compute facet of concave (TRUE) or convex (FALSE) envelope */ 13191 SCIP_DECL_VERTEXPOLYFUN((*function)), /**< pointer to vertex polyhedral function */ 13192 void* fundata, /**< data for function evaluation (can be NULL) */ 13193 SCIP_Real* xstar, /**< point to be separated */ 13194 SCIP_Real* box, /**< box where to compute facet: should be lb_1, ub_1, lb_2, ub_2... */ 13195 int nallvars, /**< half of the length of box */ 13196 SCIP_Real targetvalue, /**< target value: no need to compute facet if value in xstar would be worse than this value */ 13197 SCIP_Bool* success, /**< buffer to store whether a facet could be computed successfully */ 13198 SCIP_Real* facetcoefs, /**< buffer to store coefficients of facet defining inequality; must be an array of length at least nallvars */ 13199 SCIP_Real* facetconstant /**< buffer to store constant part of facet defining inequality */ 13200 ) 13201 { 13202 SCIP_Real* corner; 13203 SCIP_Real* funvals; 13204 int* nonfixedpos; 13205 SCIP_Real maxfaceterror; 13206 int nvars; /* number of nonfixed variables */ 13207 unsigned int ncorners; 13208 unsigned int i; 13209 int j; 13210 13211 assert(scip != NULL); 13212 assert(conshdlr != NULL); 13213 assert(function != NULL); 13214 assert(xstar != NULL); 13215 assert(box != NULL); 13216 assert(success != NULL); 13217 assert(facetcoefs != NULL); 13218 assert(facetconstant != NULL); 13219 13220 *success = FALSE; 13221 13222 /* identify fixed variables */ 13223 SCIP_CALL( SCIPallocBufferArray(scip, &nonfixedpos, nallvars) ); 13224 nvars = 0; 13225 for( j = 0; j < nallvars; ++j ) 13226 { 13227 if( SCIPisRelEQ(scip, box[2 * j], box[2 * j + 1]) ) 13228 continue; 13229 nonfixedpos[nvars] = j; 13230 nvars++; 13231 } 13232 13233 /* if all variables are fixed, then we could provide something trivial, but that wouldn't be the job of separation 13234 * if too many variables are not fixed, then we do nothing currently 13235 */ 13236 if( nvars == 0 || nvars > SCIP_MAXVERTEXPOLYDIM ) 13237 { 13238 SCIPwarningMessage(scip, "SCIPcomputeFacetVertexPolyhedralNonlinear() called with %d nonfixed variables. Must be between [1,%d].\n", nvars, SCIP_MAXVERTEXPOLYDIM); 13239 SCIPfreeBufferArray(scip, &nonfixedpos); 13240 return SCIP_OKAY; 13241 } 13242 13243 /* compute f(v^i) for each corner v^i of [l,u] */ 13244 ncorners = POWEROFTWO(nvars); 13245 SCIP_CALL( SCIPallocBufferArray(scip, &funvals, ncorners) ); 13246 SCIP_CALL( SCIPallocBufferArray(scip, &corner, nallvars) ); 13247 for( j = 0; j < nallvars; ++j ) 13248 { 13249 if( SCIPisRelEQ(scip, box[2 * j], box[2 * j + 1]) ) 13250 corner[j] = (box[2 * j] + box[2 * j + 1]) / 2.0; 13251 } 13252 for( i = 0; i < ncorners; ++i ) 13253 { 13254 SCIPdebugMsg(scip, "corner %u: ", i); 13255 for( j = 0; j < nvars; ++j ) 13256 { 13257 int varpos = nonfixedpos[j]; 13258 /* if j'th bit of row index i is set, then take upper bound on var j, otherwise lower bound var j 13259 * we check this by shifting i for j positions to the right and checking whether the last bit is set 13260 */ 13261 if( (i >> j) & 0x1 ) 13262 corner[varpos] = box[2 * varpos + 1]; /* ub of var */ 13263 else 13264 corner[varpos] = box[2 * varpos ]; /* lb of var */ 13265 SCIPdebugMsgPrint(scip, "%g, ", corner[varpos]); 13266 assert(!SCIPisInfinity(scip, REALABS(corner[varpos]))); 13267 } 13268 13269 funvals[i] = function(corner, nallvars, fundata); 13270 13271 SCIPdebugMsgPrint(scip, "obj = %e\n", funvals[i]); 13272 13273 if( funvals[i] == SCIP_INVALID || SCIPisInfinity(scip, REALABS(funvals[i])) ) 13274 { 13275 SCIPdebugMsg(scip, "cannot compute underestimator; function value at corner is too large %g\n", funvals[i]); 13276 goto CLEANUP; 13277 } 13278 } 13279 13280 /* clear coefs array; below we only fill in coefs for nonfixed variables */ 13281 BMSclearMemoryArray(facetcoefs, nallvars); 13282 13283 if( nvars == 1 ) 13284 { 13285 SCIP_CALL( computeVertexPolyhedralFacetUnivariate(scip, box[2 * nonfixedpos[0]], box[2 * nonfixedpos[0] + 1], funvals[0], funvals[1], success, &facetcoefs[nonfixedpos[0]], facetconstant) ); 13286 13287 /* check whether target has been missed */ 13288 if( *success && overestimate == (*facetconstant + facetcoefs[nonfixedpos[0]] * xstar[nonfixedpos[0]] > targetvalue) ) 13289 { 13290 SCIPdebugMsg(scip, "computed secant, but missed target %g (facetvalue=%g, overestimate=%u)\n", targetvalue, *facetconstant + facetcoefs[nonfixedpos[0]] * xstar[nonfixedpos[0]], overestimate); 13291 *success = FALSE; 13292 } 13293 } 13294 else if( nvars == 2 && SCIPlapackIsAvailable() ) 13295 { 13296 int idx1 = nonfixedpos[0]; 13297 int idx2 = nonfixedpos[1]; 13298 SCIP_Real p1[2] = { box[2*idx1], box[2*idx2] }; /* corner 0: 0>>0 & 0x1 = 0, 0>>1 & 0x1 = 0 */ 13299 SCIP_Real p2[2] = { box[2*idx1+1], box[2*idx2] }; /* corner 1: 1>>0 & 0x1 = 1, 1>>1 & 0x1 = 0 */ 13300 SCIP_Real p3[2] = { box[2*idx1], box[2*idx2+1] }; /* corner 2: 2>>0 & 0x1 = 0, 2>>1 & 0x1 = 1 */ 13301 SCIP_Real p4[2] = { box[2*idx1+1], box[2*idx2+1] }; /* corner 3: 3>>0 & 0x1 = 1, 3>>1 & 0x1 = 1 */ 13302 SCIP_Real xstar2[2] = { xstar[idx1], xstar[idx2] }; 13303 SCIP_Real coefs[2] = { 0.0, 0.0 }; 13304 13305 SCIP_CALL( computeVertexPolyhedralFacetBivariate(scip, overestimate, p1, p2, p3, p4, funvals[0], funvals[1], funvals[2], funvals[3], xstar2, targetvalue, success, coefs, facetconstant) ); 13306 13307 facetcoefs[idx1] = coefs[0]; 13308 facetcoefs[idx2] = coefs[1]; 13309 } 13310 else 13311 { 13312 SCIP_CALL( computeVertexPolyhedralFacetLP(scip, conshdlr, overestimate, xstar, box, nallvars, nonfixedpos, funvals, nvars, targetvalue, success, facetcoefs, facetconstant) ); 13313 } 13314 if( !*success ) 13315 { 13316 SCIPdebugMsg(scip, "no success computing facet, %d vars\n", nvars); 13317 goto CLEANUP; 13318 } 13319 13320 /* 13321 * check and adjust facet with the algorithm of Rikun et al. 13322 */ 13323 13324 maxfaceterror = computeVertexPolyhedralMaxFacetError(scip, overestimate, funvals, box, nallvars, nvars, nonfixedpos, facetcoefs, *facetconstant); 13325 13326 /* adjust constant part of the facet by maxerror to make it a valid over/underestimator (not facet though) */ 13327 if( maxfaceterror > 0.0 ) 13328 { 13329 SCIP_CONSHDLRDATA* conshdlrdata; 13330 SCIP_Real midval; 13331 SCIP_Real feastol; 13332 13333 feastol = SCIPgetStage(scip) == SCIP_STAGE_SOLVING ? SCIPgetLPFeastol(scip) : SCIPfeastol(scip); 13334 13335 /* evaluate function in middle point to get some idea for a scaling */ 13336 for( j = 0; j < nvars; ++j ) 13337 corner[nonfixedpos[j]] = (box[2 * nonfixedpos[j]] + box[2 * nonfixedpos[j] + 1]) / 2.0; 13338 midval = function(corner, nallvars, fundata); 13339 if( midval == SCIP_INVALID ) 13340 midval = 1.0; 13341 13342 conshdlrdata = SCIPconshdlrGetData(conshdlr); 13343 assert(conshdlrdata != NULL); 13344 13345 /* there seem to be numerical problems if the error is too large; in this case we reject the facet */ 13346 if( maxfaceterror > conshdlrdata->vp_adjfacetthreshold * feastol * fabs(midval) ) 13347 { 13348 SCIPdebugMsg(scip, "ignoring facet due to instability, it cuts off a vertex by %g (midval=%g).\n", maxfaceterror, midval); 13349 *success = FALSE; 13350 goto CLEANUP; 13351 } 13352 13353 SCIPdebugMsg(scip, "maximum facet error %g (midval=%g), adjust constant to make cut valid!\n", maxfaceterror, midval); 13354 13355 if( overestimate ) 13356 *facetconstant += maxfaceterror; 13357 else 13358 *facetconstant -= maxfaceterror; 13359 } 13360 13361 /* if we made it until here, then we have a nice facet */ 13362 assert(*success); 13363 13364 CLEANUP: 13365 /* free allocated memory */ 13366 SCIPfreeBufferArray(scip, &corner); 13367 SCIPfreeBufferArray(scip, &funvals); 13368 SCIPfreeBufferArray(scip, &nonfixedpos); 13369 13370 return SCIP_OKAY; 13371 } 13372 13373 /* 13374 * constraint specific interface methods 13375 */ 13376 13377 /** returns the expression of the given nonlinear constraint */ 13378 SCIP_EXPR* SCIPgetExprNonlinear( 13379 SCIP_CONS* cons /**< constraint data */ 13380 ) 13381 { 13382 SCIP_CONSDATA* consdata; 13383 13384 assert(cons != NULL); 13385 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0); 13386 13387 consdata = SCIPconsGetData(cons); 13388 assert(consdata != NULL); 13389 13390 return consdata->expr; 13391 } 13392 13393 /** gets the left hand side of a nonlinear constraint */ 13394 SCIP_Real SCIPgetLhsNonlinear( 13395 SCIP_CONS* cons /**< constraint data */ 13396 ) 13397 { 13398 SCIP_CONSDATA* consdata; 13399 13400 assert(cons != NULL); 13401 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0); 13402 13403 consdata = SCIPconsGetData(cons); 13404 assert(consdata != NULL); 13405 13406 return consdata->lhs; 13407 } 13408 13409 /** gets the right hand side of a nonlinear constraint */ 13410 SCIP_Real SCIPgetRhsNonlinear( 13411 SCIP_CONS* cons /**< constraint data */ 13412 ) 13413 { 13414 SCIP_CONSDATA* consdata; 13415 13416 assert(cons != NULL); 13417 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0); 13418 13419 consdata = SCIPconsGetData(cons); 13420 assert(consdata != NULL); 13421 13422 return consdata->rhs; 13423 } 13424 13425 /** gets the nonlinear constraint as a nonlinear row representation. */ 13426 SCIP_RETCODE SCIPgetNlRowNonlinear( 13427 SCIP* scip, /**< SCIP data structure */ 13428 SCIP_CONS* cons, /**< constraint */ 13429 SCIP_NLROW** nlrow /**< pointer to store nonlinear row */ 13430 ) 13431 { 13432 SCIP_CONSDATA* consdata; 13433 13434 assert(cons != NULL); 13435 assert(nlrow != NULL); 13436 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0); 13437 13438 consdata = SCIPconsGetData(cons); 13439 assert(consdata != NULL); 13440 13441 if( consdata->nlrow == NULL ) 13442 { 13443 SCIP_CALL( createNlRow(scip, cons) ); 13444 } 13445 assert(consdata->nlrow != NULL); 13446 *nlrow = consdata->nlrow; 13447 13448 return SCIP_OKAY; 13449 } 13450 13451 /** returns the curvature of the expression of a given nonlinear constraint 13452 * 13453 * @note The curvature information is computed during CONSINITSOL. 13454 */ 13455 SCIP_EXPRCURV SCIPgetCurvatureNonlinear( 13456 SCIP_CONS* cons /**< constraint data */ 13457 ) 13458 { 13459 SCIP_CONSDATA* consdata; 13460 13461 assert(cons != NULL); 13462 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0); 13463 13464 consdata = SCIPconsGetData(cons); 13465 assert(consdata != NULL); 13466 13467 return consdata->curv; 13468 } 13469 13470 /** checks whether expression of constraint can be represented as quadratic form 13471 * 13472 * Only sets `*isquadratic` to TRUE if the whole expression is quadratic (in the non-extended formulation) and non-linear. 13473 * That is, the expression in each \ref SCIP_QUADEXPR_QUADTERM will be a variable expressions and 13474 * \ref SCIPgetVarExprVar() can be used to retrieve the variable. 13475 */ 13476 SCIP_RETCODE SCIPcheckQuadraticNonlinear( 13477 SCIP* scip, /**< SCIP data structure */ 13478 SCIP_CONS* cons, /**< constraint data */ 13479 SCIP_Bool* isquadratic /**< buffer to store whether constraint is quadratic */ 13480 ) 13481 { 13482 SCIP_CONSDATA* consdata; 13483 13484 assert(scip != NULL); 13485 assert(cons != NULL); 13486 assert(isquadratic != NULL); 13487 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0); 13488 13489 consdata = SCIPconsGetData(cons); 13490 assert(consdata != NULL); 13491 assert(consdata->expr != NULL); 13492 13493 /* check whether constraint expression is quadratic in extended formulation */ 13494 SCIP_CALL( SCIPcheckExprQuadratic(scip, consdata->expr, isquadratic) ); 13495 13496 /* if not quadratic in non-extended formulation, then do indicate quadratic */ 13497 if( *isquadratic ) 13498 *isquadratic = SCIPexprAreQuadraticExprsVariables(consdata->expr); 13499 13500 return SCIP_OKAY; 13501 } 13502 13503 /** changes left-hand-side of a nonlinear constraint 13504 * 13505 * @attention This method can only be called in the problem stage. 13506 */ 13507 SCIP_RETCODE SCIPchgLhsNonlinear( 13508 SCIP* scip, /**< SCIP data structure */ 13509 SCIP_CONS* cons, /**< constraint data */ 13510 SCIP_Real lhs /**< new left-hand-side */ 13511 ) 13512 { 13513 SCIP_CONSDATA* consdata; 13514 13515 assert(scip != NULL); 13516 assert(cons != NULL); 13517 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0); 13518 13519 if( SCIPgetStage(scip) != SCIP_STAGE_PROBLEM ) 13520 { 13521 SCIPerrorMessage("SCIPchgLhsNonlinear can only be called in problem stage.\n"); 13522 return SCIP_INVALIDCALL; 13523 } 13524 13525 /* we should have an original constraint */ 13526 assert(SCIPconsIsOriginal(cons)); 13527 13528 consdata = SCIPconsGetData(cons); 13529 assert(consdata != NULL); 13530 13531 if( consdata->lhs == lhs ) 13532 return SCIP_OKAY; 13533 13534 consdata->lhs = lhs; 13535 13536 /* not sure we care about any of these flags for original constraints */ 13537 consdata->ispropagated = FALSE; 13538 13539 return SCIP_OKAY; 13540 } 13541 13542 /** changes right-hand-side of a nonlinear constraint 13543 * 13544 * @attention This method can only be called in the problem stage. 13545 */ 13546 SCIP_RETCODE SCIPchgRhsNonlinear( 13547 SCIP* scip, /**< SCIP data structure */ 13548 SCIP_CONS* cons, /**< constraint data */ 13549 SCIP_Real rhs /**< new right-hand-side */ 13550 ) 13551 { 13552 SCIP_CONSDATA* consdata; 13553 13554 assert(scip != NULL); 13555 assert(cons != NULL); 13556 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0); 13557 13558 if( SCIPgetStage(scip) != SCIP_STAGE_PROBLEM ) 13559 { 13560 SCIPerrorMessage("SCIPchgLhsNonlinear can only be called in problem stage.\n"); 13561 return SCIP_INVALIDCALL; 13562 } 13563 13564 /* we should have an original constraint */ 13565 assert(SCIPconsIsOriginal(cons)); 13566 13567 consdata = SCIPconsGetData(cons); 13568 assert(consdata != NULL); 13569 13570 if( consdata->rhs == rhs ) 13571 return SCIP_OKAY; 13572 13573 consdata->rhs = rhs; 13574 13575 /* not sure we care about any of these flags for original constraints */ 13576 consdata->ispropagated = FALSE; 13577 13578 return SCIP_OKAY; 13579 } 13580 13581 /** changes expression of a nonlinear constraint 13582 * 13583 * @attention This method can only be called in the problem stage. 13584 */ 13585 SCIP_RETCODE SCIPchgExprNonlinear( 13586 SCIP* scip, /**< SCIP data structure */ 13587 SCIP_CONS* cons, /**< constraint data */ 13588 SCIP_EXPR* expr /**< new expression */ 13589 ) 13590 { 13591 SCIP_CONSHDLR* conshdlr; 13592 SCIP_CONSDATA* consdata; 13593 13594 assert(scip != NULL); 13595 assert(cons != NULL); 13596 assert(expr != NULL); 13597 13598 if( SCIPgetStage(scip) != SCIP_STAGE_PROBLEM ) 13599 { 13600 SCIPerrorMessage("SCIPchgExprNonlinear can only be called in problem stage.\n"); 13601 return SCIP_INVALIDCALL; 13602 } 13603 13604 /* we should have an original constraint */ 13605 assert(SCIPconsIsOriginal(cons)); 13606 13607 conshdlr = SCIPconsGetHdlr(cons); 13608 assert(conshdlr != NULL); 13609 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0); 13610 13611 consdata = SCIPconsGetData(cons); 13612 assert(consdata != NULL); 13613 assert(consdata->expr != NULL); 13614 13615 /* we should not have collected additional data for the expr 13616 * if some of these asserts fail, we may have to remove it and add some code to keep information up to date 13617 */ 13618 assert(consdata->nvarexprs == 0); 13619 assert(consdata->varexprs == NULL); 13620 assert(!consdata->catchedevents); 13621 13622 SCIP_CALL( SCIPreleaseExpr(scip, &consdata->expr) ); 13623 13624 /* copy expression, thereby map variables expressions to already existing variables expressions in var2expr map, or augment var2expr map */ 13625 SCIP_CALL( SCIPduplicateExpr(scip, expr, &consdata->expr, mapexprvar, conshdlr, exprownerCreate, (void*)conshdlr) ); 13626 13627 /* not sure we care about any of these flags for original constraints */ 13628 consdata->curv = SCIP_EXPRCURV_UNKNOWN; 13629 consdata->issimplified = FALSE; 13630 consdata->ispropagated = FALSE; 13631 13632 return SCIP_OKAY; 13633 } 13634 13635 /** adds coef * var to nonlinear constraint 13636 * 13637 * @attention This method can only be called in the problem stage. 13638 */ 13639 SCIP_RETCODE SCIPaddLinearVarNonlinear( 13640 SCIP* scip, /**< SCIP data structure */ 13641 SCIP_CONS* cons, /**< constraint data */ 13642 SCIP_VAR* var, /**< variable */ 13643 SCIP_Real coef /**< coefficient */ 13644 ) 13645 { 13646 SCIP_CONSHDLR* conshdlr; 13647 SCIP_CONSDATA* consdata; 13648 SCIP_EXPR* varexpr; 13649 13650 assert(scip != NULL); 13651 assert(cons != NULL); 13652 13653 if( SCIPgetStage(scip) != SCIP_STAGE_PROBLEM ) 13654 { 13655 SCIPerrorMessage("SCIPaddLinearVarNonlinear can only be called in problem stage.\n"); 13656 return SCIP_INVALIDCALL; 13657 } 13658 13659 /* we should have an original constraint */ 13660 assert(SCIPconsIsOriginal(cons)); 13661 13662 if( coef == 0.0 ) 13663 return SCIP_OKAY; 13664 13665 conshdlr = SCIPconsGetHdlr(cons); 13666 assert(conshdlr != NULL); 13667 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0); 13668 13669 consdata = SCIPconsGetData(cons); 13670 assert(consdata != NULL); 13671 assert(consdata->expr != NULL); 13672 13673 /* we should not have collected additional data for it 13674 * if some of these asserts fail, we may have to remove it and add some code to keep information up to date 13675 */ 13676 assert(consdata->nvarexprs == 0); 13677 assert(consdata->varexprs == NULL); 13678 assert(!consdata->catchedevents); 13679 13680 SCIP_CALL( createExprVar(scip, conshdlr, &varexpr, var) ); 13681 13682 /* append to sum, if consdata->expr is sum and not used anywhere else */ 13683 if( SCIPexprGetNUses(consdata->expr) == 1 && SCIPisExprSum(scip, consdata->expr) ) 13684 { 13685 SCIP_CALL( SCIPappendExprSumExpr(scip, consdata->expr, varexpr, coef) ); 13686 } 13687 else 13688 { 13689 /* create new expression = 1 * consdata->expr + coef * var */ 13690 SCIP_EXPR* children[2] = { consdata->expr, varexpr }; 13691 SCIP_Real coefs[2] = { 1.0, coef }; 13692 13693 SCIP_CALL( SCIPcreateExprSum(scip, &consdata->expr, 2, children, coefs, 0.0, exprownerCreate, (void*)conshdlr) ); 13694 13695 /* release old root expr */ 13696 SCIP_CALL( SCIPreleaseExpr(scip, &children[0]) ); 13697 } 13698 13699 SCIP_CALL( SCIPreleaseExpr(scip, &varexpr) ); 13700 13701 /* not sure we care about any of these flags for original constraints */ 13702 consdata->issimplified = FALSE; 13703 consdata->ispropagated = FALSE; 13704 13705 return SCIP_OKAY; 13706 } 13707 13708 /** adds coef * expr to nonlinear constraint 13709 * 13710 * @attention This method can only be called in the problem stage. 13711 */ 13712 SCIP_RETCODE SCIPaddExprNonlinear( 13713 SCIP* scip, /**< SCIP data structure */ 13714 SCIP_CONS* cons, /**< nonlinear constraint */ 13715 SCIP_EXPR* expr, /**< expression */ 13716 SCIP_Real coef /**< coefficient */ 13717 ) 13718 { 13719 SCIP_CONSHDLR* conshdlr; 13720 SCIP_CONSDATA* consdata; 13721 SCIP_EXPR* exprowned; 13722 13723 assert(scip != NULL); 13724 assert(cons != NULL); 13725 13726 if( SCIPgetStage(scip) != SCIP_STAGE_PROBLEM ) 13727 { 13728 SCIPerrorMessage("SCIPaddLinearVarNonlinear can only be called in problem stage.\n"); 13729 return SCIP_INVALIDCALL; 13730 } 13731 13732 /* we should have an original constraint */ 13733 assert(SCIPconsIsOriginal(cons)); 13734 13735 if( coef == 0.0 ) 13736 return SCIP_OKAY; 13737 13738 conshdlr = SCIPconsGetHdlr(cons); 13739 assert(conshdlr != NULL); 13740 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0); 13741 13742 consdata = SCIPconsGetData(cons); 13743 assert(consdata != NULL); 13744 assert(consdata->expr != NULL); 13745 13746 /* we should not have collected additional data for it 13747 * if some of these asserts fail, we may have to remove it and add some code to keep information up to date 13748 */ 13749 assert(consdata->nvarexprs == 0); 13750 assert(consdata->varexprs == NULL); 13751 assert(!consdata->catchedevents); 13752 13753 /* copy expression, thereby map variables expressions to already existing variables expressions in var2expr map, or augment var2expr map */ 13754 SCIP_CALL( SCIPduplicateExpr(scip, expr, &exprowned, mapexprvar, conshdlr, exprownerCreate, (void*)conshdlr) ); 13755 13756 /* append to sum, if consdata->expr is sum and not used anywhere else */ 13757 if( SCIPexprGetNUses(consdata->expr) == 1 && SCIPisExprSum(scip, consdata->expr) ) 13758 { 13759 SCIP_CALL( SCIPappendExprSumExpr(scip, consdata->expr, exprowned, coef) ); 13760 } 13761 else 13762 { 13763 /* create new expression = 1 * consdata->expr + coef * var */ 13764 SCIP_EXPR* children[2] = { consdata->expr, exprowned }; 13765 SCIP_Real coefs[2] = { 1.0, coef }; 13766 13767 SCIP_CALL( SCIPcreateExprSum(scip, &consdata->expr, 2, children, coefs, 0.0, exprownerCreate, (void*)conshdlr) ); 13768 13769 /* release old root expr */ 13770 SCIP_CALL( SCIPreleaseExpr(scip, &children[0]) ); 13771 } 13772 13773 SCIP_CALL( SCIPreleaseExpr(scip, &exprowned) ); 13774 13775 /* not sure we care about any of these flags for original constraints */ 13776 consdata->issimplified = FALSE; 13777 consdata->ispropagated = FALSE; 13778 13779 return SCIP_OKAY; 13780 } 13781 13782 /** gets absolute violation of nonlinear constraint 13783 * 13784 * This function evaluates the constraints in the given solution. 13785 * 13786 * If this value is at most SCIPfeastol(), the constraint would be considered feasible. 13787 */ 13788 SCIP_RETCODE SCIPgetAbsViolationNonlinear( 13789 SCIP* scip, /**< SCIP data structure */ 13790 SCIP_CONS* cons, /**< constraint */ 13791 SCIP_SOL* sol, /**< solution to check */ 13792 SCIP_Real* viol /**< buffer to store computed violation */ 13793 ) 13794 { 13795 assert(cons != NULL); 13796 assert(viol != NULL); 13797 13798 SCIP_CALL( computeViolation(scip, cons, sol, 0L) ); 13799 *viol = getConsAbsViolation(cons); 13800 13801 return SCIP_OKAY; 13802 } 13803 13804 /** gets scaled violation of nonlinear constraint 13805 * 13806 * This function evaluates the constraints in the given solution. 13807 * 13808 * The scaling that is applied to the absolute violation of the constraint 13809 * depends on the setting of parameter constraints/nonlinear/violscale. 13810 */ 13811 SCIP_RETCODE SCIPgetRelViolationNonlinear( 13812 SCIP* scip, /**< SCIP data structure */ 13813 SCIP_CONS* cons, /**< constraint */ 13814 SCIP_SOL* sol, /**< solution to check */ 13815 SCIP_Real* viol /**< buffer to store computed violation */ 13816 ) 13817 { 13818 assert(cons != NULL); 13819 assert(viol != NULL); 13820 13821 SCIP_CALL( computeViolation(scip, cons, sol, 0L) ); 13822 SCIP_CALL( getConsRelViolation(scip, cons, viol, sol, 0L) ); 13823 13824 return SCIP_OKAY; 13825 } 13826 13827 /** returns a variable that appears linearly that may be decreased without making any other constraint infeasible */ 13828 void SCIPgetLinvarMayDecreaseNonlinear( 13829 SCIP* scip, /**< SCIP data structure */ 13830 SCIP_CONS* cons, /**< nonlinear constraint */ 13831 SCIP_VAR** var, /**< pointer to store the variable */ 13832 SCIP_Real* coef /**< pointer to store the coefficient */ 13833 ) 13834 { 13835 SCIP_CONSDATA* consdata; 13836 13837 assert(cons != NULL); 13838 assert(var != NULL); 13839 assert(coef != NULL); 13840 13841 /* check for a linear variable that can be increased or decreased without harming feasibility */ 13842 findUnlockedLinearVar(scip, cons); 13843 13844 consdata = SCIPconsGetData(cons); 13845 assert(consdata != NULL); 13846 13847 *var = consdata->linvardecr; 13848 *coef = consdata->linvardecrcoef; 13849 } 13850 13851 /** returns a variable that appears linearly that may be increased without making any other constraint infeasible */ 13852 void SCIPgetLinvarMayIncreaseNonlinear( 13853 SCIP* scip, /**< SCIP data structure */ 13854 SCIP_CONS* cons, /**< nonlinear constraint */ 13855 SCIP_VAR** var, /**< pointer to store the variable */ 13856 SCIP_Real* coef /**< pointer to store the coefficient */ 13857 ) 13858 { 13859 SCIP_CONSDATA* consdata; 13860 13861 assert(cons != NULL); 13862 assert(var != NULL); 13863 assert(coef != NULL); 13864 13865 /* check for a linear variable that can be increased or decreased without harming feasibility */ 13866 findUnlockedLinearVar(scip, cons); 13867 13868 consdata = SCIPconsGetData(cons); 13869 assert(consdata != NULL); 13870 13871 *var = consdata->linvarincr; 13872 *coef = consdata->linvarincrcoef; 13873 } 13874 13875 13876 /* 13877 * Methods for Expressions in Nonlinear Constraints 13878 */ 13879 13880 /** returns the number of positive rounding locks of an expression */ 13881 int SCIPgetExprNLocksPosNonlinear( 13882 SCIP_EXPR* expr /**< expression */ 13883 ) 13884 { 13885 assert(expr != NULL); 13886 assert(SCIPexprGetOwnerData(expr) != NULL); 13887 13888 return SCIPexprGetOwnerData(expr)->nlockspos; 13889 } 13890 13891 /** returns the number of negative rounding locks of an expression */ 13892 int SCIPgetExprNLocksNegNonlinear( 13893 SCIP_EXPR* expr /**< expression */ 13894 ) 13895 { 13896 assert(expr != NULL); 13897 assert(SCIPexprGetOwnerData(expr) != NULL); 13898 13899 return SCIPexprGetOwnerData(expr)->nlocksneg; 13900 } 13901 13902 /** returns the variable used for linearizing a given expression (return value might be NULL) 13903 * 13904 * @note for variable expression it returns the corresponding variable 13905 */ 13906 SCIP_VAR* SCIPgetExprAuxVarNonlinear( 13907 SCIP_EXPR* expr /**< expression */ 13908 ) 13909 { 13910 SCIP_EXPR_OWNERDATA* ownerdata; 13911 13912 assert(expr != NULL); 13913 13914 ownerdata = SCIPexprGetOwnerData(expr); 13915 assert(ownerdata != NULL); 13916 13917 return ownerdata->filterpos >= -1 ? SCIPgetVarExprVar(expr) : ownerdata->auxvar; 13918 } 13919 13920 /** returns the number of enforcements for an expression */ 13921 int SCIPgetExprNEnfosNonlinear( 13922 SCIP_EXPR* expr /**< expression */ 13923 ) 13924 { 13925 assert(expr != NULL); 13926 assert(SCIPexprGetOwnerData(expr) != NULL); 13927 13928 return SCIPexprGetOwnerData(expr)->nenfos; 13929 } 13930 13931 /** returns the data for one of the enforcements of an expression */ 13932 void SCIPgetExprEnfoDataNonlinear( 13933 SCIP_EXPR* expr, /**< expression */ 13934 int idx, /**< position of enforcement in enfos array */ 13935 SCIP_NLHDLR** nlhdlr, /**< buffer to store nlhldr */ 13936 SCIP_NLHDLREXPRDATA** nlhdlrexprdata, /**< buffer to store nlhdlr data for expression, or NULL */ 13937 SCIP_NLHDLR_METHOD* nlhdlrparticipation, /**< buffer to store methods where nonlinear handler participates, or NULL */ 13938 SCIP_Bool* sepabelowusesactivity, /**< buffer to store whether sepabelow uses activity of some expression, or NULL */ 13939 SCIP_Bool* sepaaboveusesactivity, /**< buffer to store whether sepaabove uses activity of some expression, or NULL */ 13940 SCIP_Real* auxvalue /**< buffer to store current auxvalue, or NULL */ 13941 ) 13942 { 13943 SCIP_EXPR_OWNERDATA* ownerdata; 13944 13945 assert(expr != NULL); 13946 13947 ownerdata = SCIPexprGetOwnerData(expr); 13948 assert(ownerdata != NULL); 13949 assert(idx >= 0); 13950 assert(idx < ownerdata->nenfos); 13951 assert(ownerdata->enfos[idx] != NULL); 13952 assert(nlhdlr != NULL); 13953 13954 *nlhdlr = ownerdata->enfos[idx]->nlhdlr; 13955 13956 if( nlhdlrexprdata != NULL ) 13957 *nlhdlrexprdata = ownerdata->enfos[idx]->nlhdlrexprdata; 13958 13959 if( nlhdlrparticipation != NULL ) 13960 *nlhdlrparticipation = ownerdata->enfos[idx]->nlhdlrparticipation; 13961 13962 if( sepabelowusesactivity != NULL ) 13963 *sepabelowusesactivity = ownerdata->enfos[idx]->sepabelowusesactivity; 13964 13965 if( sepaaboveusesactivity != NULL ) 13966 *sepaaboveusesactivity = ownerdata->enfos[idx]->sepaaboveusesactivity; 13967 13968 if( auxvalue != NULL ) 13969 *auxvalue = ownerdata->enfos[idx]->auxvalue; 13970 } 13971 13972 /** sets the auxiliary value of expression for one of the enforcements of an expression */ 13973 void SCIPsetExprEnfoAuxValueNonlinear( 13974 SCIP_EXPR* expr, /**< expression */ 13975 int idx, /**< position of enforcement in enfos array */ 13976 SCIP_Real auxvalue /**< the new value of auxval */ 13977 ) 13978 { 13979 SCIP_EXPR_OWNERDATA* ownerdata; 13980 13981 assert(expr != NULL); 13982 13983 ownerdata = SCIPexprGetOwnerData(expr); 13984 assert(ownerdata != NULL); 13985 13986 assert(idx >= 0); 13987 assert(idx < ownerdata->nenfos); 13988 assert(ownerdata->enfos[idx] != NULL); 13989 13990 ownerdata->enfos[idx]->auxvalue = auxvalue; 13991 } 13992 13993 /** number of nonlinear handlers whose activity computation and propagation methods depend on the activity of the expression 13994 * 13995 * @note This method can only be used after the detection methods of the nonlinear handlers have been called. 13996 */ 13997 unsigned int SCIPgetExprNPropUsesActivityNonlinear( 13998 SCIP_EXPR* expr /**< expression */ 13999 ) 14000 { 14001 assert(expr != NULL); 14002 assert(SCIPexprGetOwnerData(expr) != NULL); 14003 14004 return SCIPexprGetOwnerData(expr)->nactivityusesprop; 14005 } 14006 14007 /** number of nonlinear handlers whose separation methods (estimate or enforcement) depend on the activity of the expression 14008 * 14009 * @note This method can only be used after the detection methods of the nonlinear handlers have been called. 14010 */ 14011 unsigned int SCIPgetExprNSepaUsesActivityNonlinear( 14012 SCIP_EXPR* expr /**< expression */ 14013 ) 14014 { 14015 assert(expr != NULL); 14016 assert(SCIPexprGetOwnerData(expr) != NULL); 14017 14018 return SCIPexprGetOwnerData(expr)->nactivityusessepa; 14019 } 14020 14021 /** number of nonlinear handlers whose separation methods (estimate or enforcement) use auxiliary variable of the expression 14022 * 14023 * @note This method can only be used after the detection methods of the nonlinear handlers have been called. 14024 */ 14025 unsigned int SCIPgetExprNAuxvarUsesNonlinear( 14026 SCIP_EXPR* expr /**< expression */ 14027 ) 14028 { 14029 assert(expr != NULL); 14030 assert(SCIPexprGetOwnerData(expr) != NULL); 14031 14032 return SCIPexprGetOwnerData(expr)->nauxvaruses; 14033 } 14034 14035 /** method to be called by a nlhdlr during NLHDLRDETECT to notify an expression that it will be used 14036 * 14037 * - if `useauxvar` is enabled, then ensures that an auxiliary variable will be created in INITLP 14038 * - if `useactivityforprop` or `useactivityforsepa{below,above}` is enabled, then ensured that activity will be updated for `expr` 14039 * - if `useactivityforprop` is enabled, then increments the count returned by SCIPgetExprNPropUsesActivityNonlinear() 14040 * - if `useactivityforsepa{below,above}` is enabled, then increments the count returned by SCIPgetExprNSepaUsesActivityNonlinear() 14041 * and also increments this count for all variables in the expression. 14042 * 14043 * The distinction into `useactivityforprop` and `useactivityforsepa{below,above}` is to recognize variables which domain influences 14044 * under/overestimators. Domain propagation routines (like OBBT) may invest more work for these variables. 14045 * The distinction into `useactivityforsepabelow` and `useactivityforsepaabove` is to recognize whether a nlhdlr that called this method 14046 * will use activity of `expr` in enfomethod \ref SCIP_NLHDLR_METHOD_SEPABELOW or \ref SCIP_NLHDLR_METHOD_SEPAABOVE. 14047 */ 14048 SCIP_RETCODE SCIPregisterExprUsageNonlinear( 14049 SCIP* scip, /**< SCIP data structure */ 14050 SCIP_EXPR* expr, /**< expression */ 14051 SCIP_Bool useauxvar, /**< whether an auxiliary variable will be used for estimate or cut generation */ 14052 SCIP_Bool useactivityforprop, /**< whether activity of expr will be used by domain propagation or activity calculation (inteval) */ 14053 SCIP_Bool useactivityforsepabelow, /**< whether activity of expr will be used by underestimation */ 14054 SCIP_Bool useactivityforsepaabove /**< whether activity of expr will be used by overestimation */ 14055 ) 14056 { 14057 SCIP_EXPR_OWNERDATA* ownerdata; 14058 14059 assert(expr != NULL); 14060 14061 ownerdata = SCIPexprGetOwnerData(expr); 14062 assert(ownerdata != NULL); 14063 14064 /* do not store auxvar request for variable expressions */ 14065 if( useauxvar && SCIPisExprVar(scip, expr) ) 14066 useauxvar = FALSE; 14067 14068 if( ownerdata->nenfos >= 0 && 14069 ( (ownerdata->nactivityusesprop == 0 && ownerdata->nactivityusessepa == 0 && (useactivityforprop || useactivityforsepabelow || useactivityforsepaabove)) || 14070 (ownerdata->nauxvaruses == 0 && useauxvar) 14071 ) ) 14072 { 14073 /* if we already have ran detect of nlhdlrs on expr (nenfos >= 0), then we need to rerun detection if 14074 * we require additional enforcement methods, that is, 14075 * - activity of expr was not used before but will be used now, or 14076 * - auxiliary variable of expr was not required before but will be used now 14077 */ 14078 SCIP_CALL( freeEnfoData(scip, expr, FALSE) ); 14079 } 14080 14081 if( useauxvar ) 14082 ++ownerdata->nauxvaruses; 14083 14084 if( useactivityforprop ) 14085 ++ownerdata->nactivityusesprop; 14086 14087 if( useactivityforsepabelow || useactivityforsepaabove ) 14088 ++ownerdata->nactivityusessepa; 14089 14090 /* remember that SCIPregisterExprUsageNonlinear() has been called with useactivityforsepa{below,above}=TRUE; this 14091 * information is used in detectNlhdlr() 14092 */ 14093 if( useactivityforsepabelow ) 14094 SCIPconshdlrGetData(ownerdata->conshdlr)->registerusesactivitysepabelow = TRUE; 14095 if( useactivityforsepaabove ) 14096 SCIPconshdlrGetData(ownerdata->conshdlr)->registerusesactivitysepaabove = TRUE; 14097 14098 if( useactivityforprop ) 14099 { 14100 /* if activity will be used for propagation, then make sure there is a valid activity 14101 * this way, we can do a reversepropcall after detectNlhdlr 14102 */ 14103 SCIP_CALL( SCIPevalExprActivity(scip, expr) ); 14104 } 14105 14106 /* increase the nactivityusedsepa counter for all variables used in the given expression */ 14107 if( (useactivityforsepabelow || useactivityforsepaabove) && SCIPexprGetNChildren(expr) > 0 ) 14108 { 14109 SCIP_EXPRITER* it; 14110 14111 /* create and initialize iterator */ 14112 SCIP_CALL( SCIPcreateExpriter(scip, &it) ); 14113 SCIP_CALL( SCIPexpriterInit(it, expr, SCIP_EXPRITER_DFS, FALSE) ); 14114 14115 for( ; !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) ) 14116 if( SCIPisExprVar(scip, expr) ) 14117 ++SCIPexprGetOwnerData(expr)->nactivityusessepa; 14118 14119 /* free iterator */ 14120 SCIPfreeExpriter(&it); 14121 } 14122 14123 return SCIP_OKAY; 14124 } 14125 14126 /** computes absolute violation for auxvar relation in an expression w.r.t. original variables 14127 * 14128 * Assume the expression is f(x), where x are original (i.e., not auxiliary) variables. 14129 * Assume that f(x) is associated with auxiliary variable z. 14130 * 14131 * If there are negative locks, then returns the violation of z ≤ f(x) and sets `violover` to TRUE. 14132 * If there are positive locks, then returns the violation of z ≥ f(x) and sets `violunder` to TRUE. 14133 * Of course, if there both negative and positive locks, then return the violation of z = f(x). 14134 * 14135 * If necessary, f is evaluated in the given solution. If that fails (domain error), 14136 * then `viol` is set to SCIPinfinity() and both `violover` and `violunder` are set to TRUE. 14137 */ 14138 SCIP_RETCODE SCIPgetExprAbsOrigViolationNonlinear( 14139 SCIP* scip, /**< SCIP data structure */ 14140 SCIP_EXPR* expr, /**< expression */ 14141 SCIP_SOL* sol, /**< solution */ 14142 SCIP_Longint soltag, /**< tag of solution */ 14143 SCIP_Real* viol, /**< buffer to store computed violation */ 14144 SCIP_Bool* violunder, /**< buffer to store whether z >= f(x) is violated, or NULL */ 14145 SCIP_Bool* violover /**< buffer to store whether z <= f(x) is violated, or NULL */ 14146 ) 14147 { 14148 assert(scip != NULL); 14149 assert(expr != NULL); 14150 assert(viol != NULL); 14151 14152 /* make sure expression has been evaluated */ 14153 SCIP_CALL( SCIPevalExpr(scip, expr, sol, soltag) ); 14154 14155 /* get violation from internal method */ 14156 *viol = getExprAbsOrigViolation(scip, expr, sol, violunder, violover); 14157 14158 return SCIP_OKAY; 14159 } 14160 14161 /** computes absolute violation for auxvar relation in an expression w.r.t. auxiliary variables 14162 * 14163 * Assume the expression is f(w), where w are auxiliary variables that were introduced by some nlhdlr. 14164 * Assume that f(w) is associated with auxiliary variable z. 14165 * 14166 * If there are negative locks, then returns the violation of z ≤ f(w) and sets `violover` to TRUE. 14167 * If there are positive locks, then returns the violation of z ≥ f(w) and sets `violunder` to TRUE. 14168 * Of course, if there both negative and positive locks, then return the violation of z = f(w). 14169 * 14170 * If the given value of f(w) is SCIP_INVALID, then `viol` is set to SCIPinfinity() and 14171 * both `violover` and `violunder` are set to TRUE. 14172 */ 14173 SCIP_RETCODE SCIPgetExprAbsAuxViolationNonlinear( 14174 SCIP* scip, /**< SCIP data structure */ 14175 SCIP_EXPR* expr, /**< expression */ 14176 SCIP_Real auxvalue, /**< the value of f(w) */ 14177 SCIP_SOL* sol, /**< solution that has been evaluated */ 14178 SCIP_Real* viol, /**< buffer to store computed violation */ 14179 SCIP_Bool* violunder, /**< buffer to store whether z >= f(w) is violated, or NULL */ 14180 SCIP_Bool* violover /**< buffer to store whether z <= f(w) is violated, or NULL */ 14181 ) 14182 { 14183 assert(scip != NULL); 14184 assert(expr != NULL); 14185 assert(viol != NULL); 14186 14187 /* get violation from internal method */ 14188 *viol = getExprAbsAuxViolation(scip, expr, auxvalue, sol, violunder, violover); 14189 14190 return SCIP_OKAY; 14191 } 14192 14193 14194 /** computes relative violation for auxvar relation in an expression w.r.t. auxiliary variables 14195 * 14196 * Assume the expression is f(w), where w are auxiliary variables that were introduced by some nlhdlr. 14197 * Assume that f(w) is associated with auxiliary variable z. 14198 * 14199 * Taking the absolute violation from SCIPgetExprAbsAuxViolationNonlinear(), this function returns 14200 * the absolute violation divided by max(1,|f(w)|). 14201 * 14202 * If the given value of f(w) is SCIP_INVALID, then `viol` is set to SCIPinfinity() and 14203 * both `violover` and `violunder` are set to TRUE. 14204 */ 14205 SCIP_RETCODE SCIPgetExprRelAuxViolationNonlinear( 14206 SCIP* scip, /**< SCIP data structure */ 14207 SCIP_EXPR* expr, /**< expression */ 14208 SCIP_Real auxvalue, /**< the value of f(w) */ 14209 SCIP_SOL* sol, /**< solution that has been evaluated */ 14210 SCIP_Real* viol, /**< buffer to store computed violation */ 14211 SCIP_Bool* violunder, /**< buffer to store whether z >= f(w) is violated, or NULL */ 14212 SCIP_Bool* violover /**< buffer to store whether z <= f(w) is violated, or NULL */ 14213 ) 14214 { 14215 assert(scip != NULL); 14216 assert(expr != NULL); 14217 assert(viol != NULL); 14218 14219 /* get violation from internal method */ 14220 *viol = getExprAbsAuxViolation(scip, expr, auxvalue, sol, violunder, violover); 14221 14222 if( !SCIPisInfinity(scip, *viol) ) 14223 { 14224 assert(auxvalue != SCIP_INVALID); 14225 /* TODO maybe we should rather use max(eps,|auxvalue|)? */ 14226 *viol /= MAX(1.0, REALABS(auxvalue)); 14227 } 14228 14229 return SCIP_OKAY; 14230 } 14231 14232 /** returns bounds on the expression 14233 * 14234 * This gives an intersection of bounds from 14235 * - activity calculation (SCIPexprGetActivity()), if valid, 14236 * - auxiliary variable, if present, 14237 * - stored by SCIPtightenExprIntervalNonlinear() during domain propagation 14238 * 14239 * @note The returned interval can be empty! 14240 */ 14241 SCIP_INTERVAL SCIPgetExprBoundsNonlinear( 14242 SCIP* scip, /**< SCIP data structure */ 14243 SCIP_EXPR* expr /**< expression */ 14244 ) 14245 { 14246 SCIP_EXPR_OWNERDATA* ownerdata; 14247 SCIP_CONSHDLRDATA* conshdlrdata; 14248 SCIP_INTERVAL bounds; 14249 14250 assert(scip != NULL); 14251 assert(expr != NULL); 14252 14253 ownerdata = SCIPexprGetOwnerData(expr); 14254 assert(ownerdata != NULL); 14255 14256 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr); 14257 assert(conshdlrdata != NULL); 14258 14259 /* SCIPdebugMsg(scip, "get bounds expr %p:", expr); */ 14260 14261 /* start with propbounds if they belong to current propagation */ 14262 if( ownerdata->propboundstag == conshdlrdata->curpropboundstag ) 14263 { 14264 bounds = ownerdata->propbounds; 14265 /* SCIPdebugMsgPrint(scip, " propbounds [%.15g,%.15g]", ownerdata->propbounds.inf, ownerdata->propbounds.sup); */ 14266 } 14267 else 14268 SCIPintervalSetEntire(SCIP_INTERVAL_INFINITY, &bounds); 14269 14270 if( SCIPexprGetActivityTag(expr) >= conshdlrdata->lastboundrelax ) 14271 { 14272 /* apply propbounds to expr activity, but ensure it's not-empty if very close disjoint intervals */ 14273 /* SCIPdebugMsgPrint(scip, " activity [%.15g,%.15g]", expr->activity.inf, expr->activity.sup); */ 14274 SCIPintervalIntersectEps(&bounds, SCIPepsilon(scip), SCIPexprGetActivity(expr), bounds); 14275 } 14276 14277 if( ownerdata->auxvar != NULL ) 14278 { 14279 /* apply auxiliary variable bounds to bounds */ 14280 SCIP_INTERVAL auxvarbounds; 14281 14282 auxvarbounds = conshdlrdata->intevalvar(scip, ownerdata->auxvar, conshdlrdata); 14283 /* SCIPdebugMsgPrint(scip, " auxvar [%.15g,%.15g]", auxvarbounds.inf, auxvarbounds.sup); */ 14284 SCIPintervalIntersectEps(&bounds, SCIPepsilon(scip), bounds, auxvarbounds); 14285 } 14286 14287 /* SCIPdebugMsgPrint(scip, " -> [%.15g,%.15g]\n", bounds.inf, bounds.sup); */ 14288 14289 return bounds; 14290 } 14291 14292 /** informs the expression about new bounds that can be used for reverse-propagation and to tighten bounds of 14293 * corresponding (auxiliary) variable (if any) 14294 * 14295 * @attention this function should only be called during domain propagation in cons_nonlinear 14296 */ 14297 SCIP_RETCODE SCIPtightenExprIntervalNonlinear( 14298 SCIP* scip, /**< SCIP data structure */ 14299 SCIP_EXPR* expr, /**< expression to be tightened */ 14300 SCIP_INTERVAL newbounds, /**< new bounds for the expression */ 14301 SCIP_Bool* cutoff, /**< buffer to store whether a cutoff was detected */ 14302 int* ntightenings /**< buffer to add the total number of tightenings, or NULL */ 14303 ) 14304 { 14305 SCIP_EXPR_OWNERDATA* ownerdata; 14306 SCIP_CONSHDLRDATA* conshdlrdata; 14307 14308 assert(scip != NULL); 14309 assert(expr != NULL); 14310 assert(cutoff != NULL); 14311 14312 ownerdata = SCIPexprGetOwnerData(expr); 14313 assert(ownerdata != NULL); 14314 assert(ownerdata->conshdlr != NULL); 14315 14316 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr); 14317 assert(conshdlrdata != NULL); 14318 14319 /* the code below assumes that current activity is valid 14320 * if it turns out that we cannot ensure that, then we should change code 14321 */ 14322 assert(SCIPexprGetActivityTag(expr) >= conshdlrdata->lastboundrelax || SCIPintervalIsEntire(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(expr))); 14323 assert(!SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(expr))); 14324 14325 *cutoff = FALSE; 14326 14327 #ifdef DEBUG_PROP 14328 SCIPdebugMsg(scip, "Trying to tighten bounds of expr "); 14329 SCIP_CALL( SCIPprintExpr(scip, expr, NULL) ); 14330 SCIPdebugMsgPrint(scip, " with activity [%.15g,%.15g] to [%.15g,%.15g] (force=%d)\n", SCIPexprGetActivity(expr).inf, SCIPexprGetActivity(expr).sup, newbounds.inf, newbounds.sup, conshdlrdata->forceboundtightening); 14331 #endif 14332 14333 if( SCIPexprIsIntegral(expr) ) 14334 { 14335 /* apply integrality to new bounds 14336 * it should be ok to use normal ceil() and floor(), but for safety, we use SCIPceil and SCIPfloor for now 14337 */ 14338 if( newbounds.inf > -SCIP_INTERVAL_INFINITY ) 14339 newbounds.inf = SCIPceil(scip, newbounds.inf); 14340 if( newbounds.sup < SCIP_INTERVAL_INFINITY ) 14341 newbounds.sup = SCIPfloor(scip, newbounds.sup); 14342 #ifdef DEBUG_PROP 14343 SCIPdebugMsg(scip, " applied integrality: [%.15g,%.15g]\n", newbounds.inf, newbounds.sup); 14344 #endif 14345 } 14346 14347 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, newbounds) ) 14348 { 14349 SCIPdebugMsg(scip, " cut off due to new bounds being empty\n"); 14350 14351 *cutoff = TRUE; 14352 return SCIP_OKAY; 14353 } 14354 14355 /* treat the new bounds as empty if either the lower/upper bound is above/below +/- SCIPinfinity() */ 14356 if( SCIPisInfinity(scip, newbounds.inf) || SCIPisInfinity(scip, -newbounds.sup) ) 14357 { 14358 SCIPdebugMsg(scip, " cut off due to new bounds being beyond infinity\n"); 14359 14360 *cutoff = TRUE; 14361 return SCIP_OKAY; 14362 } 14363 14364 /* tighten newbounds w.r.t. existing expr->propbounds or activity */ 14365 if( ownerdata->propboundstag == conshdlrdata->curpropboundstag ) 14366 { 14367 /* if already having propbounds in expr, then tighten newbounds by propbounds */ 14368 SCIPintervalIntersectEps(&newbounds, SCIPepsilon(scip), ownerdata->propbounds, newbounds); 14369 } 14370 else 14371 { 14372 /* first time we have propbounds for expr in this propagation rounds: 14373 * intersect with activity (though don't let it become empty if very close intervals) 14374 */ 14375 SCIPintervalIntersectEps(&newbounds, SCIPepsilon(scip), SCIPexprGetActivity(expr), newbounds); 14376 } 14377 #ifdef DEBUG_PROP 14378 SCIPdebugMsg(scip, " applied %s: [%.20g,%.20g]\n", ownerdata->propboundstag == conshdlrdata->curpropboundstag ? "previous propbounds" : "activity", newbounds.inf, newbounds.sup); 14379 #endif 14380 14381 /* check if the new bounds lead to an empty interval */ 14382 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, newbounds) ) 14383 { 14384 SCIPdebugMsg(scip, " cut off due to empty intersection with previous propbounds or activity\n"); 14385 14386 *cutoff = TRUE; 14387 return SCIP_OKAY; 14388 } 14389 14390 /* if expr is not constant or variable, then store newbounds in expr->propbounds 14391 * - for constant, the intersection with activity should have been sufficient to determine infeasibilty 14392 * - for variable, the tightenAuxVarBounds call below should be suffient to have to new bounds acknowledged 14393 */ 14394 if( SCIPexprGetNChildren(expr) > 0 ) 14395 { 14396 ownerdata->propbounds = newbounds; 14397 ownerdata->propboundstag = conshdlrdata->curpropboundstag; 14398 } 14399 14400 /* if updated propbounds do not allow a sufficient tightening, then do not consider adding to queue for reverse 14401 * propagation or update of auxvar bounds 14402 * TODO? if we first had a considerable tightening and then only get small tightenings under the same 14403 * curpropboundstag, then these will still be considered as isIntervalBetter, since we compare with activity here and 14404 * not with the propbounds as set in the beginning; I'm not sure, though, that comparing always with previous 14405 * propbounds would be better, since a number of small updates to propbounds could eventually lead to a considerable 14406 * one or should we not even update propbounds to newbounds if the update is small? 14407 */ 14408 if( !isIntervalBetter(scip, conshdlrdata->forceboundtightening, newbounds, SCIPexprGetActivity(expr)) ) 14409 { 14410 #ifdef DEBUG_PROP 14411 SCIPdebugMsg(scip, " new bounds [%g,%g] for expr %p not sufficiently tighter than activity -- not adding to propqueue or tightening auxvar\n", newbounds.inf, newbounds.sup, (void*)expr); 14412 #endif 14413 return SCIP_OKAY; 14414 } 14415 14416 if( SCIPexprGetNChildren(expr) > 0 && !ownerdata->inpropqueue && (ownerdata->nactivityusesprop > 0 || ownerdata->nactivityusessepa > 0 || ownerdata->nenfos < 0) ) 14417 { 14418 /* add expression to propagation queue if not there yet and not var or constant and 14419 * if it should have a nlhdlr with a reverseprop callback or nlhdlrs are not initialized yet (nenfos < 0) 14420 */ 14421 #ifdef DEBUG_PROP 14422 SCIPdebugMsg(scip, " insert expr <%p> (%s) into reversepropqueue\n", (void*)expr, SCIPexprhdlrGetName(SCIPexprGetHdlr(expr))); 14423 #endif 14424 SCIP_CALL( SCIPqueueInsert(conshdlrdata->reversepropqueue, expr) ); 14425 ownerdata->inpropqueue = TRUE; 14426 } 14427 14428 /* update bounds on variable or auxiliary variable */ 14429 SCIP_CALL( tightenAuxVarBounds(scip, ownerdata->conshdlr, expr, newbounds, cutoff, ntightenings) ); 14430 14431 return SCIP_OKAY; 14432 } 14433 14434 /** mark constraints that include this expression to be propagated again 14435 * 14436 * This can be used by, e.g., nlhdlrs, to trigger a new propagation of constraints without 14437 * a change of variable bounds, e.g., because new information on the expression is available 14438 * that could potentially lead to tighter expression activity values. 14439 * 14440 * Note, that this call marks also constraints for propagation which only share some variable 14441 * with this expression. 14442 */ 14443 SCIP_RETCODE SCIPmarkExprPropagateNonlinear( 14444 SCIP* scip, /**< SCIP data structure */ 14445 SCIP_EXPR* expr /**< expression to propagate again */ 14446 ) 14447 { 14448 SCIP_EXPRITER* it; 14449 SCIP_CONSDATA* consdata; 14450 SCIP_EXPR_OWNERDATA* ownerdata; 14451 int c; 14452 14453 assert(scip != NULL); 14454 assert(expr != NULL); 14455 14456 ownerdata = SCIPexprGetOwnerData(expr); 14457 assert(ownerdata != NULL); 14458 14459 SCIPincrementCurBoundsTagNonlinear(ownerdata->conshdlr, FALSE); 14460 14461 SCIP_CALL( SCIPcreateExpriter(scip, &it) ); 14462 SCIP_CALL( SCIPexpriterInit(it, expr, SCIP_EXPRITER_DFS, FALSE) ); 14463 14464 for( ; !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) ) 14465 { 14466 if( !SCIPisExprVar(scip, expr) ) 14467 continue; 14468 14469 ownerdata = SCIPexprGetOwnerData(expr); 14470 assert(ownerdata != NULL); 14471 14472 for( c = 0; c < ownerdata->nconss; ++c ) 14473 { 14474 consdata = SCIPconsGetData(ownerdata->conss[c]); 14475 assert(consdata != NULL); 14476 consdata->ispropagated = FALSE; 14477 } 14478 } 14479 14480 SCIPfreeExpriter(&it); 14481 14482 return SCIP_OKAY; 14483 } 14484 14485 /** adds violation-branching score to an expression 14486 * 14487 * Adds a score to the expression-specific violation-branching score, thereby marking it as branching candidate. 14488 * The expression must either be a variable expression or have an aux-variable. 14489 * In the latter case, branching on auxiliary variables must have been enabled. 14490 * In case of doubt, use SCIPaddExprsViolScoreNonlinear(). Roughly, the difference between these functions is that the current 14491 * function adds `violscore` to the expression directly, while SCIPaddExprsViolScoreNonlinear() will split the 14492 * violation score among all the given expressions according to parameter constraints/nonlinear/branching/violsplit. 14493 * 14494 * @see SCIPaddExprsViolScoreNonlinear() 14495 */ 14496 void SCIPaddExprViolScoreNonlinear( 14497 SCIP* scip, /**< SCIP data structure */ 14498 SCIP_EXPR* expr, /**< expression where to add branching score */ 14499 SCIP_Real violscore /**< violation score to add to expression */ 14500 ) 14501 { 14502 SCIP_EXPR_OWNERDATA* ownerdata; 14503 SCIP_CONSHDLRDATA* conshdlrdata; 14504 14505 assert(scip != NULL); 14506 assert(expr != NULL); 14507 assert(violscore >= 0.0); 14508 14509 ownerdata = SCIPexprGetOwnerData(expr); 14510 assert(ownerdata != NULL); 14511 14512 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr); 14513 assert(conshdlrdata != NULL); 14514 14515 /* if not allowing to branch on auxvars, then expr must be a var-expr */ 14516 assert(branchAuxNonlinear(scip, ownerdata->conshdlr) || SCIPisExprVar(scip, expr)); 14517 /* if allowing to branch on auxvars, then expr must be a var-expr or have an auxvar */ 14518 assert(!branchAuxNonlinear(scip, ownerdata->conshdlr) || SCIPisExprVar(scip, expr) || ownerdata->auxvar != NULL); 14519 14520 /* reset branching score if we are in a different enfo round */ 14521 if( ownerdata->violscoretag != conshdlrdata->enforound ) 14522 { 14523 ownerdata->violscoresum = violscore; 14524 ownerdata->violscoremax = violscore; 14525 ownerdata->nviolscores = 1; 14526 ownerdata->violscoretag = conshdlrdata->enforound; 14527 return; 14528 } 14529 14530 ownerdata->violscoresum += violscore; 14531 if( violscore > ownerdata->violscoremax ) 14532 ownerdata->violscoremax = violscore; 14533 ++ownerdata->nviolscores; 14534 } 14535 14536 /** adds violation-branching score to a set of expressions, distributing the score among all the expressions 14537 * 14538 * Each expression must either be a variable expression or have an aux-variable. 14539 * If branching on aux-variables is disabled, then the violation branching score will be distributed among all 14540 * variables present in `exprs`. 14541 */ 14542 SCIP_RETCODE SCIPaddExprsViolScoreNonlinear( 14543 SCIP* scip, /**< SCIP data structure */ 14544 SCIP_EXPR** exprs, /**< expressions where to add branching score */ 14545 int nexprs, /**< number of expressions */ 14546 SCIP_Real violscore, /**< violation score to add to expression */ 14547 SCIP_SOL* sol, /**< current solution */ 14548 SCIP_Bool* success /**< buffer to store whether at least one violscore was added */ 14549 ) 14550 { 14551 SCIP_EXPRITER* it; 14552 SCIP_EXPR** varexprs; 14553 SCIP_EXPR* e; 14554 int nvars; 14555 int varssize; 14556 int i; 14557 14558 assert(exprs != NULL || nexprs == 0); 14559 assert(success != NULL); 14560 14561 if( nexprs == 0 ) 14562 { 14563 *success = FALSE; 14564 return SCIP_OKAY; 14565 } 14566 14567 /* if allowing to branch on auxiliary variables, then call internal addConsExprExprsViolScore immediately */ 14568 if( branchAuxNonlinear(scip, SCIPexprGetOwnerData(exprs[0])->conshdlr) ) 14569 { 14570 addExprsViolScore(scip, exprs, nexprs, violscore, sol, success); 14571 return SCIP_OKAY; 14572 } 14573 14574 /* if not allowing to branch on aux vars, then create new array containing var expressions that exprs depend on */ 14575 nvars = 0; 14576 varssize = 5; 14577 SCIP_CALL( SCIPallocBufferArray(scip, &varexprs, varssize) ); 14578 14579 SCIP_CALL( SCIPcreateExpriter(scip, &it) ); 14580 SCIP_CALL( SCIPexpriterInit(it, NULL, SCIP_EXPRITER_DFS, FALSE) ); 14581 14582 for( i = 0; i < nexprs; ++i ) 14583 { 14584 for( e = SCIPexpriterRestartDFS(it, exprs[i]); !SCIPexpriterIsEnd(it); e = SCIPexpriterGetNext(it) ) 14585 { 14586 assert(e != NULL); 14587 14588 if( SCIPisExprVar(scip, e) ) 14589 { 14590 /* add variable expression to vars array */ 14591 if( varssize == nvars ) 14592 { 14593 varssize = SCIPcalcMemGrowSize(scip, nvars + 1); 14594 SCIP_CALL( SCIPreallocBufferArray(scip, &varexprs, varssize) ); 14595 } 14596 assert(varssize > nvars); 14597 14598 varexprs[nvars++] = e; 14599 } 14600 } 14601 } 14602 14603 SCIPfreeExpriter(&it); 14604 14605 addExprsViolScore(scip, varexprs, nvars, violscore, sol, success); 14606 14607 SCIPfreeBufferArray(scip, &varexprs); 14608 14609 return SCIP_OKAY; 14610 } 14611 14612 /** gives violation-branching score stored in expression, or 0.0 if no valid score has been stored */ 14613 SCIP_Real SCIPgetExprViolScoreNonlinear( 14614 SCIP_EXPR* expr /**< expression */ 14615 ) 14616 { 14617 SCIP_EXPR_OWNERDATA* ownerdata; 14618 SCIP_CONSHDLRDATA* conshdlrdata; 14619 14620 assert(expr != NULL); 14621 14622 ownerdata = SCIPexprGetOwnerData(expr); 14623 assert(ownerdata != NULL); 14624 14625 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr); 14626 assert(conshdlrdata != NULL); 14627 14628 if( conshdlrdata->enforound != ownerdata->violscoretag ) 14629 return 0.0; 14630 14631 if( ownerdata->nviolscores == 0 ) 14632 return 0.0; 14633 14634 switch( conshdlrdata->branchscoreagg ) 14635 { 14636 case 'a' : 14637 /* average */ 14638 return ownerdata->violscoresum / ownerdata->nviolscores; 14639 14640 case 'm' : 14641 /* maximum */ 14642 return ownerdata->violscoremax; 14643 14644 case 's' : 14645 /* sum */ 14646 return ownerdata->violscoresum; 14647 14648 default: 14649 SCIPerrorMessage("Invalid value %c for branchscoreagg parameter\n", conshdlrdata->branchscoreagg); 14650 SCIPABORT(); 14651 return SCIP_INVALID; 14652 } 14653 } 14654 14655 /** returns the partial derivative of an expression w.r.t. a variable (or SCIP_INVALID if there was an evaluation error) 14656 * 14657 * @see SCIPexprGetDerivative() 14658 */ 14659 SCIP_Real SCIPgetExprPartialDiffNonlinear( 14660 SCIP* scip, /**< SCIP data structure */ 14661 SCIP_EXPR* expr, /**< root expression of constraint used in the last SCIPevalExprGradient() call */ 14662 SCIP_VAR* var /**< variable (needs to be in the expression) */ 14663 ) 14664 { 14665 SCIP_EXPR_OWNERDATA* ownerdata; 14666 SCIP_CONSHDLRDATA* conshdlrdata; 14667 SCIP_EXPR* varexpr; 14668 14669 assert(scip != NULL); 14670 assert(expr != NULL); 14671 assert(var != NULL); 14672 14673 /* return 0.0 for value expression */ 14674 if( SCIPisExprValue(scip, expr) ) 14675 { 14676 assert(SCIPexprGetDerivative(expr) == 0.0); 14677 return 0.0; 14678 } 14679 14680 /* check if an error occurred during the last SCIPevalExprGradient() call */ 14681 if( SCIPexprGetDerivative(expr) == SCIP_INVALID ) 14682 return SCIP_INVALID; 14683 14684 ownerdata = SCIPexprGetOwnerData(expr); 14685 assert(ownerdata != NULL); 14686 14687 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr); 14688 assert(conshdlrdata != NULL); 14689 14690 /* use variable to expressions mapping which is stored in the constraint handler data */ 14691 assert(SCIPhashmapExists(conshdlrdata->var2expr, var)); 14692 14693 varexpr = (SCIP_EXPR*)SCIPhashmapGetImage(conshdlrdata->var2expr, var); 14694 assert(varexpr != NULL); 14695 assert(SCIPisExprVar(scip, varexpr)); 14696 14697 /* use difftag to decide whether the variable belongs to the expression */ 14698 return (SCIPexprGetDiffTag(expr) != SCIPexprGetDiffTag(varexpr)) ? 0.0 : SCIPexprGetDerivative(varexpr); 14699 } 14700 14701 /** returns the var's coordinate of Hu partial derivative of an expression w.r.t. a variable (or SCIP_INVALID if there was an evaluation error) 14702 * 14703 * @see SCIPexprGetBardot() 14704 */ 14705 SCIP_Real SCIPgetExprPartialDiffGradientDirNonlinear( 14706 SCIP* scip, /**< SCIP data structure */ 14707 SCIP_EXPR* expr, /**< root expression of constraint used in the last SCIPevalExprHessianDir() call */ 14708 SCIP_VAR* var /**< variable (needs to be in the expression) */ 14709 ) 14710 { 14711 SCIP_EXPR_OWNERDATA* ownerdata; 14712 SCIP_CONSHDLRDATA* conshdlrdata; 14713 SCIP_EXPR* varexpr; 14714 14715 assert(scip != NULL); 14716 assert(expr != NULL); 14717 assert(var != NULL); 14718 14719 /* return 0.0 for value expression */ 14720 if( SCIPisExprValue(scip, expr) ) 14721 return 0.0; 14722 14723 /* check if an error occurred during the last SCIPevalExprHessianDir() call */ 14724 if( SCIPexprGetBardot(expr) == SCIP_INVALID ) 14725 return SCIP_INVALID; 14726 14727 ownerdata = SCIPexprGetOwnerData(expr); 14728 assert(ownerdata != NULL); 14729 14730 conshdlrdata = SCIPconshdlrGetData(ownerdata->conshdlr); 14731 assert(conshdlrdata != NULL); 14732 14733 /* use variable to expressions mapping which is stored in the constraint handler data; 14734 * if this fails it means that we are asking for the var's component of H*u for a var 14735 * that doesn't appear in any nonlinear constraint, so maybe we can also just return 0.0 14736 */ 14737 assert(SCIPhashmapExists(conshdlrdata->var2expr, var)); 14738 14739 varexpr = (SCIP_EXPR*)SCIPhashmapGetImage(conshdlrdata->var2expr, var); 14740 assert(varexpr != NULL); 14741 assert(SCIPisExprVar(scip, varexpr)); 14742 14743 /* use difftag to decide whether the variable belongs to the expression */ 14744 return (SCIPexprGetDiffTag(expr) != SCIPexprGetDiffTag(varexpr)) ? 0.0 : SCIPexprGetBardot(varexpr); 14745 } 14746 14747 /** evaluates quadratic term in a solution w.r.t. auxiliary variables 14748 * 14749 * \note This requires that for every expr used in the quadratic data, a variable or auxiliary variable is available. 14750 */ 14751 SCIP_Real SCIPevalExprQuadraticAuxNonlinear( 14752 SCIP* scip, /**< SCIP data structure */ 14753 SCIP_EXPR* expr, /**< quadratic expression */ 14754 SCIP_SOL* sol /**< solution to evaluate, or NULL for LP solution */ 14755 ) 14756 { 14757 SCIP_Real auxvalue; 14758 int nlinexprs; 14759 SCIP_Real* lincoefs; 14760 SCIP_EXPR** linexprs; 14761 int nquadexprs; 14762 int nbilinexprs; 14763 int i; 14764 14765 assert(scip != NULL); 14766 assert(expr != NULL); 14767 14768 SCIPexprGetQuadraticData(expr, &auxvalue, &nlinexprs, &linexprs, &lincoefs, &nquadexprs, &nbilinexprs, NULL, NULL); 14769 14770 /* linear terms */ 14771 for( i = 0; i < nlinexprs; ++i ) 14772 { 14773 assert(SCIPgetExprAuxVarNonlinear(linexprs[i]) != NULL); 14774 auxvalue += lincoefs[i] * SCIPgetSolVal(scip, sol, SCIPgetExprAuxVarNonlinear(linexprs[i])); 14775 } 14776 14777 /* quadratic terms */ 14778 for( i = 0; i < nquadexprs; ++i ) 14779 { 14780 SCIP_EXPR* quadexprterm; 14781 SCIP_Real lincoef; 14782 SCIP_Real sqrcoef; 14783 SCIP_Real solval; 14784 14785 SCIPexprGetQuadraticQuadTerm(expr, i, &quadexprterm, &lincoef, &sqrcoef, NULL, NULL, NULL); 14786 14787 assert(SCIPgetExprAuxVarNonlinear(quadexprterm) != NULL); 14788 14789 solval = SCIPgetSolVal(scip, sol, SCIPgetExprAuxVarNonlinear(quadexprterm)); 14790 auxvalue += (lincoef + sqrcoef * solval) * solval; 14791 } 14792 14793 /* bilinear terms */ 14794 for( i = 0; i < nbilinexprs; ++i ) 14795 { 14796 SCIP_EXPR* expr1; 14797 SCIP_EXPR* expr2; 14798 SCIP_Real coef; 14799 14800 SCIPexprGetQuadraticBilinTerm(expr, i, &expr1, &expr2, &coef, NULL, NULL); 14801 14802 assert(SCIPgetExprAuxVarNonlinear(expr1) != NULL); 14803 assert(SCIPgetExprAuxVarNonlinear(expr2) != NULL); 14804 auxvalue += coef * SCIPgetSolVal(scip, sol, SCIPgetExprAuxVarNonlinear(expr1)) * SCIPgetSolVal(scip, sol, SCIPgetExprAuxVarNonlinear(expr2)); 14805 } 14806 14807 return auxvalue; 14808 } 14809 14810 /**@addtogroup PublicNlhdlrInterfaceMethods 14811 * @{ 14812 */ 14813 14814 /** creates a nonlinear handler and includes it into the nonlinear constraint handler */ 14815 SCIP_RETCODE SCIPincludeNlhdlrNonlinear( 14816 SCIP* scip, /**< SCIP data structure */ 14817 SCIP_NLHDLR** nlhdlr, /**< buffer where to store nonlinear handler */ 14818 const char* name, /**< name of nonlinear handler (must not be NULL) */ 14819 const char* desc, /**< description of nonlinear handler (can be NULL) */ 14820 int detectpriority, /**< detection priority of nonlinear handler */ 14821 int enfopriority, /**< enforcement priority of nonlinear handler */ 14822 SCIP_DECL_NLHDLRDETECT((*detect)), /**< structure detection callback of nonlinear handler */ 14823 SCIP_DECL_NLHDLREVALAUX((*evalaux)), /**< auxiliary evaluation callback of nonlinear handler */ 14824 SCIP_NLHDLRDATA* nlhdlrdata /**< data of nonlinear handler (can be NULL) */ 14825 ) 14826 { 14827 SCIP_CONSHDLR* conshdlr; 14828 SCIP_CONSHDLRDATA* conshdlrdata; 14829 14830 assert(scip != NULL); 14831 assert(nlhdlr != NULL); 14832 assert(detect != NULL); 14833 14834 /* find myself */ 14835 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME); 14836 if( conshdlr == NULL ) 14837 { 14838 SCIPerrorMessage("nonlinear constraint handler not found"); 14839 return SCIP_PLUGINNOTFOUND; 14840 } 14841 14842 /* create nlhdlr */ 14843 SCIP_CALL( SCIPnlhdlrCreate(scip, nlhdlr, name, desc, detectpriority, enfopriority, detect, evalaux, nlhdlrdata) ); 14844 14845 /* include into constraint handler */ 14846 conshdlrdata = SCIPconshdlrGetData(conshdlr); 14847 assert(conshdlrdata != NULL); 14848 14849 SCIP_CALL( SCIPensureBlockMemoryArray(scip, &conshdlrdata->nlhdlrs, &conshdlrdata->nlhdlrssize, conshdlrdata->nnlhdlrs+1) ); 14850 14851 conshdlrdata->nlhdlrs[conshdlrdata->nnlhdlrs] = *nlhdlr; 14852 ++conshdlrdata->nnlhdlrs; 14853 14854 /* sort nonlinear handlers by detection priority, in decreasing order 14855 * will happen in INIT, so only do when called late 14856 */ 14857 if( SCIPgetStage(scip) > SCIP_STAGE_INIT && conshdlrdata->nnlhdlrs > 1 ) 14858 SCIPsortDownPtr((void**)conshdlrdata->nlhdlrs, SCIPnlhdlrComp, conshdlrdata->nnlhdlrs); 14859 14860 return SCIP_OKAY; 14861 } 14862 14863 /** get number of nonlinear handler */ 14864 int SCIPgetNNlhdlrsNonlinear( 14865 SCIP_CONSHDLR* conshdlr /**< nonlinear constraint handler */ 14866 ) 14867 { 14868 SCIP_CONSHDLRDATA* conshdlrdata; 14869 14870 assert(conshdlr != NULL); 14871 14872 conshdlrdata = SCIPconshdlrGetData(conshdlr); 14873 assert(conshdlrdata != NULL); 14874 14875 return conshdlrdata->nnlhdlrs; 14876 } 14877 14878 /** get nonlinear handlers */ 14879 SCIP_NLHDLR** SCIPgetNlhdlrsNonlinear( 14880 SCIP_CONSHDLR* conshdlr /**< nonlinear constraint handler */ 14881 ) 14882 { 14883 SCIP_CONSHDLRDATA* conshdlrdata; 14884 14885 assert(conshdlr != NULL); 14886 14887 conshdlrdata = SCIPconshdlrGetData(conshdlr); 14888 assert(conshdlrdata != NULL); 14889 14890 return conshdlrdata->nlhdlrs; 14891 } 14892 14893 /** returns a nonlinear handler of a given name (or NULL if not found) */ 14894 SCIP_NLHDLR* SCIPfindNlhdlrNonlinear( 14895 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */ 14896 const char* name /**< name of nonlinear handler */ 14897 ) 14898 { 14899 SCIP_CONSHDLRDATA* conshdlrdata; 14900 int h; 14901 14902 assert(conshdlr != NULL); 14903 assert(name != NULL); 14904 14905 conshdlrdata = SCIPconshdlrGetData(conshdlr); 14906 assert(conshdlrdata != NULL); 14907 14908 for( h = 0; h < conshdlrdata->nnlhdlrs; ++h ) 14909 if( strcmp(SCIPnlhdlrGetName(conshdlrdata->nlhdlrs[h]), name) == 0 ) 14910 return conshdlrdata->nlhdlrs[h]; 14911 14912 return NULL; 14913 } 14914 14915 /** gives expression data that a given nonlinear handler stored in an expression 14916 * 14917 * Returns NULL if expr has not been detected by nlhdlr or nlhdlr did not store data. 14918 */ 14919 SCIP_NLHDLREXPRDATA* SCIPgetNlhdlrExprDataNonlinear( 14920 SCIP_NLHDLR* nlhdlr, /**< nonlinear handler */ 14921 SCIP_EXPR* expr /**< expression */ 14922 ) 14923 { 14924 SCIP_EXPR_OWNERDATA* ownerdata; 14925 int e; 14926 14927 assert(nlhdlr != NULL); 14928 assert(expr != NULL); 14929 14930 ownerdata = SCIPexprGetOwnerData(expr); 14931 assert(ownerdata != NULL); 14932 14933 for( e = 0; e < ownerdata->nenfos; ++e ) 14934 if( ownerdata->enfos[e]->nlhdlr == nlhdlr ) 14935 return ownerdata->enfos[e]->nlhdlrexprdata; 14936 14937 return NULL; 14938 } 14939 14940 /** @} */ 14941