1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the program and library */
4 /* SCIP --- Solving Constraint Integer Programs */
5 /* */
6 /* Copyright (C) 2002-2021 Konrad-Zuse-Zentrum */
7 /* fuer Informationstechnik Berlin */
8 /* */
9 /* SCIP is distributed under the terms of the ZIB Academic License. */
10 /* */
11 /* You should have received a copy of the ZIB Academic License */
12 /* along with SCIP; see the file COPYING. If not visit scipopt.org. */
13 /* */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15
16 /**@file cons_abspower.c
17 * @ingroup DEFPLUGINS_CONS
18 * @brief Constraint handler for absolute power constraints \f$\textrm{lhs} \leq \textrm{sign}(x+a) |x+a|^n + c z \leq \textrm{rhs}\f$
19 * @author Stefan Vigerske
20 */
21
22 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
23
24 #define SCIP_PRIVATE_ROWPREP
25
26 #include <ctype.h>
27 #include "nlpi/pub_expr.h"
28 #include "nlpi/type_expr.h"
29 #include "nlpi/type_nlpi.h"
30 #include "scip/cons_abspower.h"
31 #include "scip/cons_indicator.h"
32 #include "scip/cons_linear.h"
33 #include "scip/cons_nonlinear.h"
34 #include "scip/cons_quadratic.h"
35 #include "scip/cons_varbound.h"
36 #include "scip/debug.h"
37 #include "scip/heur_subnlp.h"
38 #include "scip/heur_trysol.h"
39 #include "scip/intervalarith.h"
40 #include "scip/pub_cons.h"
41 #include "scip/pub_event.h"
42 #include "scip/pub_heur.h"
43 #include "scip/pub_lp.h"
44 #include "scip/pub_message.h"
45 #include "scip/pub_misc.h"
46 #include "scip/pub_nlp.h"
47 #include "scip/pub_sol.h"
48 #include "scip/pub_tree.h"
49 #include "scip/pub_var.h"
50 #include "scip/scip_branch.h"
51 #include "scip/scip_conflict.h"
52 #include "scip/scip_cons.h"
53 #include "scip/scip_copy.h"
54 #include "scip/scip_cut.h"
55 #include "scip/scip_event.h"
56 #include "scip/scip_general.h"
57 #include "scip/scip_heur.h"
58 #include "scip/scip_lp.h"
59 #include "scip/scip_mem.h"
60 #include "scip/scip_message.h"
61 #include "scip/scip_nlp.h"
62 #include "scip/scip_numerics.h"
63 #include "scip/scip_param.h"
64 #include "scip/scip_prob.h"
65 #include "scip/scip_probing.h"
66 #include "scip/scip_sepa.h"
67 #include "scip/scip_sol.h"
68 #include "scip/scip_tree.h"
69 #include "scip/scip_var.h"
70 #include <string.h>
71
72 /* constraint handler properties */
73 #define CONSHDLR_NAME "abspower"
74 #define CONSHDLR_DESC "constraint handler for absolute power constraints lhs <= sign(x+offset)abs(x+offset)^n + c*z <= rhs"
75 #define CONSHDLR_SEPAPRIORITY 0 /**< priority of the constraint handler for separation */
76 #define CONSHDLR_ENFOPRIORITY -30 /**< priority of the constraint handler for constraint enforcing */
77 #define CONSHDLR_CHECKPRIORITY -3500000 /**< priority of the constraint handler for checking feasibility */
78 #define CONSHDLR_SEPAFREQ 1 /**< frequency for separating cuts; zero means to separate only in the root node */
79 #define CONSHDLR_PROPFREQ 1 /**< frequency for propagating domains; zero means only preprocessing propagation */
80 #define CONSHDLR_EAGERFREQ 100 /**< frequency for using all instead of only the useful constraints in separation,
81 * propagation and enforcement, -1 for no eager evaluations, 0 for first only */
82 #define CONSHDLR_MAXPREROUNDS -1 /**< maximal number of presolving rounds the constraint handler participates in (-1: no limit) */
83 #define CONSHDLR_DELAYSEPA FALSE /**< should separation method be delayed, if other separators found cuts? */
84 #define CONSHDLR_DELAYPROP FALSE /**< should propagation method be delayed, if other propagators found reductions? */
85 #define CONSHDLR_NEEDSCONS TRUE /**< should the constraint handler be skipped, if no constraints are available? */
86
87 #define CONSHDLR_PRESOLTIMING SCIP_PRESOLTIMING_FAST | SCIP_PRESOLTIMING_MEDIUM
88 #define CONSHDLR_PROP_TIMING SCIP_PROPTIMING_ALWAYS /**< when should the constraint handlers propagation routines be called? */
89
90 #define QUADCONSUPGD_PRIORITY 50000 /**< priority of the constraint handler for upgrading of quadratic constraints */
91 #define NONLINCONSUPGD_PRIORITY 50000 /**< priority of the constraint handler for upgrading of nonlinear constraints and reformulating expression graph nodes */
92
93 /*
94 * Local defines
95 */
96
97 #define PROPVARTOL SCIPepsilon(scip) /**< tolerance to add to variable bounds in domain propagation */
98 #define PROPSIDETOL SCIPepsilon(scip) /**< tolerance to add to constraint sides in domain propagation */
99 #define INITLPMAXVARVAL 1000.0 /**< maximal absolute value of variable for still generating a linearization cut at that point in initlp */
100
101 /** power function type to be used by a constraint instead of the general pow */
102 #define DECL_MYPOW(x) SCIP_Real x (SCIP_Real base, SCIP_Real exponent)
103
104 /** sign of a value (-1 or +1)
105 *
106 * 0.0 has sign +1
107 */
108 #define SIGN(x) ((x) >= 0.0 ? 1.0 : -1.0)
109
110
111 /*
112 * Data structures
113 */
114
115 #define ROOTS_KNOWN 10 /**< up to which (integer) exponents precomputed roots have been stored */
116
117 /** The positive root of the polynomial (n-1) y^n + n y^(n-1) - 1 is needed in separation.
118 * Here we store these roots for small integer values of n.
119 */
120 static
121 SCIP_Real roots[ROOTS_KNOWN+1] = {
122 -1.0, /* no root for n=0 */
123 -1.0, /* no root for n=1 */
124 0.41421356237309504880, /* root for n=2 (-1+sqrt(2)) */
125 0.5, /* root for n=3 */
126 0.56042566045031785945, /* root for n=4 */
127 0.60582958618826802099, /* root for n=5 */
128 0.64146546982884663257, /* root for n=6 */
129 0.67033204760309682774, /* root for n=7 */
130 0.69428385661425826738, /* root for n=8 */
131 0.71453772716733489700, /* root for n=9 */
132 0.73192937842370733350 /* root for n=10 */
133 };
134
135 /** constraint data for absolute power constraints */
136 struct SCIP_ConsData
137 {
138 SCIP_VAR* x; /**< variable x in sign(x+offset)|x+offset|^n term */
139 SCIP_VAR* z; /**< linear variable in constraint */
140 SCIP_Real exponent; /**< exponent n of |x+offset| */
141 SCIP_Real xoffset; /**< offset in x+offset */
142 SCIP_Real zcoef; /**< coefficient of linear variable z */
143 SCIP_Real lhs; /**< left hand side of constraint */
144 SCIP_Real rhs; /**< right hand side of constraint */
145
146 SCIP_Real root; /**< root of polynomial */
147 DECL_MYPOW ((*power)); /**< function for computing power*/
148
149 SCIP_Real lhsviol; /**< current violation of left hand side */
150 SCIP_Real rhsviol; /**< current violation of right hand side */
151
152 int xeventfilterpos; /**< position of x var event in SCIP event filter */
153 int zeventfilterpos; /**< position of z var event in SCIP event filter */
154 unsigned int propvarbounds:1; /**< have variable bounds been propagated? */
155
156 SCIP_NLROW* nlrow; /**< nonlinear row representation of constraint */
157 };
158
159 /** constraint handler data */
160 struct SCIP_ConshdlrData
161 {
162 SCIP_Real cutmaxrange; /**< maximal coef range (maximal abs coef / minimal abs coef) of a cut in order to be added to LP */
163 SCIP_Bool projectrefpoint; /**< whether to project the reference point when linearizing a absolute power constraint in a convex region */
164 int preferzerobranch; /**< how much we prefer to branch on 0.0 first */
165 SCIP_Bool branchminconverror; /**< whether to compute branching point such that the convexification error is minimized after branching on 0.0 */
166 SCIP_Bool addvarboundcons; /**< should variable bound constraints be added? */
167 SCIP_Bool linfeasshift; /**< try linear feasibility shift heuristic in CONSCHECK */
168 SCIP_Bool dualpresolve; /**< should dual presolve be applied? */
169 SCIP_Bool sepainboundsonly; /**< should tangents only be generated in variable bounds during separation? */
170 SCIP_Real sepanlpmincont; /**< minimal required fraction of continuous variables in problem to use solution of NLP relaxation in root for separation */
171 SCIP_Bool enfocutsremovable; /**< are cuts added during enforcement removable from the LP in the same node? */
172
173 SCIP_HEUR* subnlpheur; /**< a pointer to the subnlp heuristic */
174 SCIP_HEUR* trysolheur; /**< a pointer to the trysol heuristic */
175 SCIP_EVENTHDLR* eventhdlr; /**< our handler for bound change events on variable x */
176 SCIP_CONSHDLR* conshdlrindicator; /**< a pointer to the indicator constraint handler */
177 int newsoleventfilterpos;/**< filter position of new solution event handler, if catched */
178 SCIP_Bool comparedpairwise; /**< did we compare absolute power constraints pairwise in this run? */
179 SCIP_Bool sepanlp; /**< where linearization of the NLP relaxation solution added? */
180 SCIP_NODE* lastenfonode; /**< the node for which enforcement was called the last time (and some constraint was violated) */
181 int nenforounds; /**< counter on number of enforcement rounds for the current node */
182 unsigned int nsecantcuts; /**< number of secant cuts created so far */
183 unsigned int ncuts; /**< number of linearization cuts created so far */
184 };
185
186 /*
187 * Propagation rules
188 */
189
190 enum Proprule
191 {
192 PROPRULE_1, /**< left hand side and bounds on z -> lower bound on x */
193 PROPRULE_2, /**< left hand side and upper bound on x -> bound on z */
194 PROPRULE_3, /**< right hand side and bounds on z -> upper bound on x */
195 PROPRULE_4, /**< right hand side and lower bound on x -> bound on z */
196 PROPRULE_INVALID /**< propagation was applied without a specific propagation rule */
197 };
198 typedef enum Proprule PROPRULE;
199
200 /*
201 * Local methods
202 */
203
204 /** computes bounds on x in a absolute power constraints for given bounds on z */
205 static
206 void computeBoundsX(
207 SCIP* scip, /**< SCIP data structure */
208 SCIP_CONS* cons, /**< constraint */
209 SCIP_INTERVAL zbnds, /**< bounds on x that are to be propagated */
210 SCIP_INTERVAL* xbnds /**< buffer to store corresponding bounds on z */
211 );
212
213 /** power function for square, that should be faster than using pow(x, 2.0) */
214 static
DECL_MYPOW(square)215 DECL_MYPOW(square)
216 {
217 assert(exponent == 2.0);
218 return base*base;
219 }
220
221 /** process variable event */
222 static
SCIP_DECL_EVENTEXEC(processVarEvent)223 SCIP_DECL_EVENTEXEC(processVarEvent)
224 {
225 SCIP_CONS* cons;
226
227 assert(scip != NULL);
228 assert(event != NULL);
229 assert(SCIPeventGetType(event) & SCIP_EVENTTYPE_BOUNDTIGHTENED);
230
231 cons = (SCIP_CONS*) eventdata;
232 assert(cons != NULL);
233
234 assert(SCIPconsGetData(cons) != NULL);
235 assert(SCIPeventGetVar(event) == SCIPconsGetData(cons)->x || SCIPeventGetVar(event) == SCIPconsGetData(cons)->z);
236
237 SCIP_CALL( SCIPmarkConsPropagate(scip, cons) );
238
239 return SCIP_OKAY;
240 } /*lint !e715*/
241
242 /** catch variable bound tightening events */
243 static
catchVarEvents(SCIP * scip,SCIP_EVENTHDLR * eventhdlr,SCIP_CONS * cons)244 SCIP_RETCODE catchVarEvents(
245 SCIP* scip, /**< SCIP data structure */
246 SCIP_EVENTHDLR* eventhdlr, /**< event handler for variables */
247 SCIP_CONS* cons /**< constraint for which to catch bound change events */
248 )
249 {
250 SCIP_CONSDATA* consdata;
251 SCIP_EVENTTYPE eventtype;
252
253 assert(scip != NULL);
254 assert(cons != NULL);
255 assert(eventhdlr != NULL);
256
257 consdata = SCIPconsGetData(cons);
258 assert(consdata != NULL);
259
260 /* if z is multiaggregated, then bound changes on x could not be propagated, so we do not need to catch them */
261 if( SCIPvarGetStatus(consdata->z) != SCIP_VARSTATUS_MULTAGGR )
262 {
263 eventtype = SCIP_EVENTTYPE_DISABLED;
264 if( !SCIPisInfinity(scip, -consdata->lhs) )
265 eventtype |= SCIP_EVENTTYPE_UBTIGHTENED;
266 if( !SCIPisInfinity(scip, consdata->rhs) )
267 eventtype |= SCIP_EVENTTYPE_LBTIGHTENED;
268
269 SCIP_CALL( SCIPcatchVarEvent(scip, consdata->x, eventtype, eventhdlr, (SCIP_EVENTDATA*)cons, &consdata->xeventfilterpos) );
270
271 SCIP_CALL( SCIPmarkConsPropagate(scip, cons) );
272 }
273
274 /* if x is multiaggregated, then bound changes on z could not be propagated, so we do not need to catch them */
275 if( SCIPvarGetStatus(consdata->x) != SCIP_VARSTATUS_MULTAGGR )
276 {
277 eventtype = SCIP_EVENTTYPE_DISABLED;
278 if( consdata->zcoef > 0.0 )
279 {
280 if( !SCIPisInfinity(scip, -consdata->lhs) )
281 eventtype |= SCIP_EVENTTYPE_UBTIGHTENED;
282 if( !SCIPisInfinity(scip, consdata->rhs) )
283 eventtype |= SCIP_EVENTTYPE_LBTIGHTENED;
284 }
285 else
286 {
287 if( !SCIPisInfinity(scip, -consdata->lhs) )
288 eventtype |= SCIP_EVENTTYPE_LBTIGHTENED;
289 if( !SCIPisInfinity(scip, consdata->rhs) )
290 eventtype |= SCIP_EVENTTYPE_UBTIGHTENED;
291 }
292
293 SCIP_CALL( SCIPcatchVarEvent(scip, consdata->z, eventtype, eventhdlr, (SCIP_EVENTDATA*)cons, &consdata->zeventfilterpos) );
294
295 SCIP_CALL( SCIPmarkConsPropagate(scip, cons) );
296 }
297
298 return SCIP_OKAY;
299 }
300
301 /** drop variable bound tightening events */
302 static
dropVarEvents(SCIP * scip,SCIP_EVENTHDLR * eventhdlr,SCIP_CONS * cons)303 SCIP_RETCODE dropVarEvents(
304 SCIP* scip, /**< SCIP data structure */
305 SCIP_EVENTHDLR* eventhdlr, /**< event handler for variables */
306 SCIP_CONS* cons /**< constraint for which to drop bound change events */
307 )
308 {
309 SCIP_CONSDATA* consdata;
310 SCIP_EVENTTYPE eventtype;
311
312 assert(scip != NULL);
313 assert(cons != NULL);
314 assert(eventhdlr != NULL);
315
316 consdata = SCIPconsGetData(cons);
317 assert(consdata != NULL);
318
319 if( SCIPvarGetStatus(consdata->z) != SCIP_VARSTATUS_MULTAGGR )
320 {
321 eventtype = SCIP_EVENTTYPE_DISABLED;
322 if( !SCIPisInfinity(scip, -consdata->lhs) )
323 eventtype |= SCIP_EVENTTYPE_UBTIGHTENED;
324 if( !SCIPisInfinity(scip, consdata->rhs) )
325 eventtype |= SCIP_EVENTTYPE_LBTIGHTENED;
326
327 SCIP_CALL( SCIPdropVarEvent(scip, consdata->x, eventtype, eventhdlr, (SCIP_EVENTDATA*)cons, consdata->xeventfilterpos) );
328 consdata->xeventfilterpos = -1;
329 }
330
331 if( SCIPvarGetStatus(consdata->x) != SCIP_VARSTATUS_MULTAGGR )
332 {
333 eventtype = SCIP_EVENTTYPE_DISABLED;
334 if( consdata->zcoef > 0.0 )
335 {
336 if( !SCIPisInfinity(scip, -consdata->lhs) )
337 eventtype |= SCIP_EVENTTYPE_UBTIGHTENED;
338 if( !SCIPisInfinity(scip, consdata->rhs) )
339 eventtype |= SCIP_EVENTTYPE_LBTIGHTENED;
340 }
341 else
342 {
343 if( !SCIPisInfinity(scip, -consdata->lhs) )
344 eventtype |= SCIP_EVENTTYPE_LBTIGHTENED;
345 if( !SCIPisInfinity(scip, consdata->rhs) )
346 eventtype |= SCIP_EVENTTYPE_UBTIGHTENED;
347 }
348
349 SCIP_CALL( SCIPdropVarEvent(scip, consdata->z, eventtype, eventhdlr, (SCIP_EVENTDATA*)cons, consdata->zeventfilterpos) );
350 consdata->zeventfilterpos = -1;
351 }
352
353 assert(consdata->xeventfilterpos == -1);
354 assert(consdata->zeventfilterpos == -1);
355
356 return SCIP_OKAY;
357 }
358
359 /** get key of hash element */
360 static
SCIP_DECL_HASHGETKEY(presolveFindDuplicatesGetKey)361 SCIP_DECL_HASHGETKEY(presolveFindDuplicatesGetKey)
362 {
363 return elem;
364 } /*lint !e715*/
365
366 /** checks if two constraints have the same x variable, the same exponent, and either the same offset or the same linear variable and are both equality constraint */
367 static
SCIP_DECL_HASHKEYEQ(presolveFindDuplicatesKeyEQ)368 SCIP_DECL_HASHKEYEQ(presolveFindDuplicatesKeyEQ)
369 {
370 SCIP_CONSDATA* consdata1;
371 SCIP_CONSDATA* consdata2;
372
373 consdata1 = SCIPconsGetData((SCIP_CONS*)key1);
374 consdata2 = SCIPconsGetData((SCIP_CONS*)key2);
375 assert(consdata1 != NULL);
376 assert(consdata2 != NULL);
377
378 if( consdata1->x != consdata2->x )
379 return FALSE;
380
381 if( consdata1->exponent != consdata2->exponent ) /*lint !e777*/
382 return FALSE;
383
384 if( consdata1->xoffset != consdata2->xoffset && consdata1->z != consdata2->z ) /*lint !e777*/
385 return FALSE;
386
387 return TRUE;
388 } /*lint !e715*/
389
390 /** get value of hash element when comparing on x */
391 static
SCIP_DECL_HASHKEYVAL(presolveFindDuplicatesKeyVal)392 SCIP_DECL_HASHKEYVAL(presolveFindDuplicatesKeyVal)
393 {
394 SCIP_CONSDATA* consdata;
395
396 consdata = SCIPconsGetData((SCIP_CONS*)key);
397 assert(consdata != NULL);
398
399 return SCIPhashTwo(SCIPvarGetIndex(consdata->x),
400 SCIPrealHashCode(consdata->exponent));
401 } /*lint !e715*/
402
403 /** checks if two constraints have the same z variable and the same exponent */
404 static
SCIP_DECL_HASHKEYEQ(presolveFindDuplicatesKeyEQ2)405 SCIP_DECL_HASHKEYEQ(presolveFindDuplicatesKeyEQ2)
406 {
407 SCIP_CONSDATA* consdata1;
408 SCIP_CONSDATA* consdata2;
409
410 consdata1 = SCIPconsGetData((SCIP_CONS*)key1);
411 consdata2 = SCIPconsGetData((SCIP_CONS*)key2);
412 assert(consdata1 != NULL);
413 assert(consdata2 != NULL);
414
415 if( consdata1->z != consdata2->z )
416 return FALSE;
417
418 if( consdata1->exponent != consdata2->exponent ) /*lint !e777*/
419 return FALSE;
420
421 return TRUE;
422 } /*lint !e715*/
423
424 /** get value of hash element when comparing on z */
425 static
SCIP_DECL_HASHKEYVAL(presolveFindDuplicatesKeyVal2)426 SCIP_DECL_HASHKEYVAL(presolveFindDuplicatesKeyVal2)
427 {
428 SCIP_CONSDATA* consdata;
429
430 consdata = SCIPconsGetData((SCIP_CONS*)key);
431 assert(consdata != NULL);
432
433 return SCIPhashTwo(SCIPvarGetIndex(consdata->z),
434 SCIPrealHashCode(consdata->exponent));
435 } /*lint !e715*/
436
437 /** upgrades a signpower constraint to a linear constraint if a second signpower constraint with same nonlinear term is available */
438 static
presolveFindDuplicatesUpgradeCons(SCIP * scip,SCIP_CONS * cons1,SCIP_CONS * cons2,SCIP_Bool * infeas,int * nupgdconss,int * ndelconss,int * naggrvars)439 SCIP_RETCODE presolveFindDuplicatesUpgradeCons(
440 SCIP* scip, /**< SCIP data structure */
441 SCIP_CONS* cons1, /**< constraint to upgrade to a linear constraint */
442 SCIP_CONS* cons2, /**< constraint which defines a relation for x|x|^{n-1} */
443 SCIP_Bool* infeas, /**< buffer where to indicate if infeasibility has been detected */
444 int* nupgdconss, /**< buffer where to add number of upgraded conss */
445 int* ndelconss, /**< buffer where to add number of deleted conss */
446 int* naggrvars /**< buffer where to add number of aggregated variables */
447 )
448 {
449 SCIP_CONSDATA* consdata1;
450 SCIP_CONSDATA* consdata2;
451 SCIP_CONS* lincons;
452 SCIP_Real lhs;
453 SCIP_Real rhs;
454 SCIP_VAR* vars[2];
455 SCIP_Real coefs[2];
456
457 assert(scip != NULL);
458 assert(cons1 != NULL);
459 assert(cons2 != NULL);
460 assert(infeas != NULL);
461 assert(nupgdconss != NULL);
462 assert(ndelconss != NULL);
463 assert(naggrvars != NULL);
464
465 consdata1 = SCIPconsGetData(cons1);
466 consdata2 = SCIPconsGetData(cons2);
467 assert(consdata1 != NULL);
468 assert(consdata2 != NULL);
469
470 assert(SCIPisEQ(scip, consdata2->lhs, consdata2->rhs));
471 assert(!SCIPisInfinity(scip, consdata2->lhs));
472 assert(consdata1->x == consdata2->x);
473 assert(consdata1->exponent == consdata2->exponent); /*lint !e777*/
474 assert(consdata1->xoffset == consdata2->xoffset); /*lint !e777*/
475
476 lhs = consdata1->lhs;
477 if( !SCIPisInfinity(scip, -lhs) )
478 lhs -= consdata2->lhs;
479 rhs = consdata1->rhs;
480 if( !SCIPisInfinity(scip, rhs) )
481 rhs -= consdata2->lhs;
482
483 vars[0] = consdata1->z;
484 vars[1] = consdata2->z;
485
486 coefs[0] = consdata1->zcoef;
487 coefs[1] = -consdata2->zcoef;
488
489 if( SCIPisEQ(scip, lhs, rhs) )
490 {
491 SCIP_Bool redundant;
492 SCIP_Bool aggregated;
493
494 /* try aggregation */
495 SCIP_CALL( SCIPaggregateVars(scip, consdata1->z, consdata2->z, consdata1->zcoef, -consdata2->zcoef, rhs, infeas, &redundant, &aggregated) );
496
497 /* if infeasibility has been detected, stop here */
498 if( *infeas )
499 return SCIP_OKAY;
500
501 if( redundant )
502 {
503 /* if redundant is TRUE, then either the aggregation has been done, or it was redundant */
504 if( aggregated )
505 ++*naggrvars;
506
507 ++*ndelconss;
508
509 SCIP_CALL( SCIPdelCons(scip, cons1) );
510 return SCIP_OKAY;
511 }
512 }
513
514 /* if aggregation did not succeed, then either because some variable is multi-aggregated or due to numerics or because lhs != rhs
515 * we then add a linear constraint instead
516 */
517 vars[0] = consdata1->z;
518 vars[1] = consdata2->z;
519 coefs[0] = consdata1->zcoef;
520 coefs[1] = -consdata2->zcoef;
521
522 SCIP_CALL( SCIPcreateConsLinear(scip, &lincons, SCIPconsGetName(cons1), 2, vars, coefs, lhs, rhs,
523 SCIPconsIsInitial(cons1), SCIPconsIsSeparated(cons1), SCIPconsIsEnforced(cons1),
524 SCIPconsIsChecked(cons1), SCIPconsIsPropagated(cons1), SCIPconsIsLocal(cons1),
525 SCIPconsIsModifiable(cons1), SCIPconsIsDynamic(cons1), SCIPconsIsRemovable(cons1),
526 SCIPconsIsStickingAtNode(cons1)) );
527 SCIP_CALL( SCIPaddCons(scip, lincons) );
528 SCIP_CALL( SCIPreleaseCons(scip, &lincons) );
529
530 SCIP_CALL( SCIPdelCons(scip, cons1) );
531 ++*nupgdconss;
532
533 return SCIP_OKAY;
534 }
535
536 /** solves a system of two absolute power equations
537 * Given: (x+xoffset1)|x+xoffset1|^{exponent-1} + zcoef1 * z == rhs1
538 * and (x+xoffset2)|x+xoffset2|^{exponent-1} + zcoef2 * z == rhs2
539 * with xoffset1 != xoffset2 and zcoef1 * rhs2 == zcoef2 * rhs1 and exponent == 2,
540 * finds values for x and z that satisfy these equations, or reports infeasibility if no solution exists.
541 *
542 * Multiplying the second equation by -zcoef1/zcoef2 and adding it to the first one gives
543 * (x+xoffset1)|x+xoffset1| - zcoef1/zcoef2 (x+offset2)|x+offset2| == 0
544 *
545 * If zcoef1 == zcoef2, then there exists, due to monotonicity of x|x|, no x such that
546 * (x+xoffset1)|x+xoffset1| == (x+xoffset2)|x+xoffset2|.
547 *
548 * In general, for zcoef1 / zcoef2 > 0.0, we get
549 * x = (xoffset2 - xoffset1) / (sqrt(zcoef2 / zcoef1) - 1.0) - xoffset1,
550 * and for zcoef1 / zcoef2 < 0.0, we get
551 * x = (xoffset2 - xoffset1) / (-sqrt(-zcoef2 / zcoef1) - 1.0) - xoffset1.
552 *
553 * This then yields z = (rhs1 - (x+xoffset1)|x+xoffset1|) / zcoef1.
554 */
555 static
presolveFindDuplicatesSolveEquations(SCIP * scip,SCIP_Bool * infeas,SCIP_Real * xval,SCIP_Real * zval,SCIP_Real exponent,SCIP_Real xoffset1,SCIP_Real zcoef1,SCIP_Real rhs1,SCIP_Real xoffset2,SCIP_Real zcoef2,SCIP_Real rhs2)556 void presolveFindDuplicatesSolveEquations(
557 SCIP* scip, /**< SCIP data structure */
558 SCIP_Bool* infeas, /**< buffer to indicate if the system of equations has no solution */
559 SCIP_Real* xval, /**< buffer to store value of x in the solution, if any */
560 SCIP_Real* zval, /**< buffer to store value of z in the solution, if any */
561 SCIP_Real exponent, /**< exponent in absolute power equations */
562 SCIP_Real xoffset1, /**< offset for x in first absolute power equation */
563 SCIP_Real zcoef1, /**< coefficient of z in first absolute power equation */
564 SCIP_Real rhs1, /**< right-hand-side in first absolute power equation */
565 SCIP_Real xoffset2, /**< offset for x in second absolute power equation */
566 SCIP_Real zcoef2, /**< coefficient of z in second absolute power equation */
567 SCIP_Real rhs2 /**< right-hand-side in second absolute power equation */
568 )
569 {
570 assert(scip != NULL);
571 assert(infeas != NULL);
572 assert(xval != NULL);
573 assert(zval != NULL);
574 assert(exponent == 2.0);
575 assert(!SCIPisEQ(scip, xoffset1, xoffset2));
576 assert(SCIPisEQ(scip, zcoef1 * rhs2, zcoef2 * rhs1));
577 assert(zcoef1 != 0.0);
578 assert(zcoef2 != 0.0);
579
580 if( xoffset2 < xoffset1 )
581 {
582 presolveFindDuplicatesSolveEquations(scip, infeas, xval, zval, exponent, xoffset2, zcoef2, rhs2, xoffset1, zcoef1, rhs1);
583 return;
584 }
585
586 if( SCIPisEQ(scip, zcoef1, zcoef2) )
587 {
588 *infeas = TRUE;
589 return;
590 }
591
592 *infeas = FALSE;
593
594 if( SCIPisEQ(scip, zcoef1, -zcoef2) )
595 {
596 *xval = - (xoffset1 + xoffset2) / 2.0;
597 }
598 else if( zcoef2 * zcoef1 > 0.0 )
599 {
600 *xval = (xoffset2 - xoffset1) / (sqrt(zcoef2 / zcoef1) - 1.0) - xoffset1;
601 }
602 else
603 {
604 assert(zcoef2 * zcoef1 < 0.0);
605 *xval = (xoffset2 - xoffset1) / (-sqrt(-zcoef2 / zcoef1) - 1.0) - xoffset1;
606 }
607
608 *zval = rhs1 - (*xval + xoffset1) * REALABS(*xval + xoffset1);
609 *zval /= zcoef1;
610
611 assert(SCIPisFeasEQ(scip, (*xval + xoffset1) * REALABS(*xval + xoffset1) + zcoef1 * *zval, rhs1));
612 assert(SCIPisFeasEQ(scip, (*xval + xoffset2) * REALABS(*xval + xoffset2) + zcoef2 * *zval, rhs2));
613 }
614
615 /** finds and removes duplicates in a set of absolute power constraints */
616 static
presolveFindDuplicates(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS ** conss,int nconss,int * nupgdconss,int * ndelconss,int * naddconss,int * nfixedvars,int * naggrvars,SCIP_Bool * success,SCIP_Bool * infeas)617 SCIP_RETCODE presolveFindDuplicates(
618 SCIP* scip, /**< SCIP data structure */
619 SCIP_CONSHDLR* conshdlr, /**< constraint handler for absolute power constraints */
620 SCIP_CONS** conss, /**< constraints */
621 int nconss, /**< number of constraints */
622 int* nupgdconss, /**< pointer where to add number of upgraded constraints */
623 int* ndelconss, /**< pointer where to add number of deleted constraints */
624 int* naddconss, /**< pointer where to add number of added constraints */
625 int* nfixedvars, /**< pointer where to add number of fixed variables */
626 int* naggrvars, /**< pointer where to add number of aggregated variables */
627 SCIP_Bool* success, /**< pointer to store whether a duplicate was found (and removed) */
628 SCIP_Bool* infeas /**< pointer to store whether infeasibility was detected */
629 )
630 {
631 SCIP_MULTIHASH* multihash;
632 SCIP_MULTIHASHLIST* multihashlist;
633 SCIP_CONSHDLRDATA* conshdlrdata;
634 int c;
635
636 assert(scip != NULL);
637 assert(conshdlr != NULL);
638 assert(conss != NULL || nconss == 0);
639 assert(nupgdconss != NULL);
640 assert(ndelconss != NULL);
641 assert(naddconss != NULL);
642 assert(nfixedvars != NULL);
643 assert(naggrvars != NULL);
644 assert(success != NULL);
645 assert(infeas != NULL);
646
647 *success = FALSE;
648 *infeas = FALSE;
649
650 if( nconss <= 1 )
651 return SCIP_OKAY;
652
653 conshdlrdata = SCIPconshdlrGetData(conshdlr);
654 assert(conshdlrdata != NULL);
655
656 /* check all constraints in the given set for duplicates, dominance, or possible simplifications w.r.t. the x variable */
657
658 SCIP_CALL( SCIPmultihashCreate(&multihash, SCIPblkmem(scip), SCIPcalcMultihashSize(nconss),
659 presolveFindDuplicatesGetKey, presolveFindDuplicatesKeyEQ, presolveFindDuplicatesKeyVal, (void*)scip) );
660
661 for( c = 0; c < nconss && !*infeas; ++c )
662 {
663 SCIP_CONS* cons0;
664 SCIP_CONS* cons1;
665
666 cons0 = conss[c]; /*lint !e613*/
667
668 assert(!SCIPconsIsModifiable(cons0)); /* absolute power constraints aren't modifiable */
669 assert(!SCIPconsIsLocal(cons0)); /* shouldn't have local constraints in presolve */
670 assert(SCIPconsIsActive(cons0)); /* shouldn't get inactive constraints here */
671
672 multihashlist = NULL;
673
674 do
675 {
676 SCIP_CONSDATA* consdata0;
677 SCIP_CONSDATA* consdata1;
678
679 /* get constraint from current hash table with same x variable as cons0 and same exponent */
680 cons1 = (SCIP_CONS*)(SCIPmultihashRetrieveNext(multihash, &multihashlist, (void*)cons0));
681 if( cons1 == NULL )
682 {
683 /* processed all constraints like cons0 from hash table, so insert cons0 and go to conss[c+1] */
684 SCIP_CALL( SCIPmultihashInsert(multihash, (void*) cons0) );
685 break;
686 }
687
688 assert(cons0 != cons1);
689
690 consdata0 = SCIPconsGetData(cons0);
691 consdata1 = SCIPconsGetData(cons1);
692 assert(consdata0 != NULL);
693 assert(consdata1 != NULL);
694
695 SCIPdebugPrintCons(scip, cons0, NULL);
696 SCIPdebugPrintCons(scip, cons1, NULL);
697
698 assert(consdata0->x == consdata1->x);
699 assert(consdata0->exponent == consdata1->exponent); /*lint !e777*/
700
701 if( SCIPisEQ(scip, consdata0->xoffset, consdata1->xoffset) )
702 {
703 /* we have two constraints with the same (x+offset)|x+offset|^n term */
704
705 /* if both constraints have the same functions; strengthen sides of cons1 and throw cons0 away */
706 if( consdata0->z == consdata1->z && SCIPisEQ(scip, consdata0->zcoef, consdata1->zcoef) )
707 {
708 /* check if side strenghtening would result in inconsistency */
709 if( SCIPisGT(scip, consdata0->lhs, consdata1->rhs) || SCIPisGT(scip, consdata1->lhs, consdata0->rhs) )
710 {
711 SCIPdebugMsg(scip, "<%s> and <%s> are contradictory; declare infeasibility\n", SCIPconsGetName(cons0), SCIPconsGetName(cons1));
712 *infeas = TRUE;
713 break;
714 }
715
716 SCIPdebugMsg(scip, "<%s> and <%s> are equivalent; dropping the first\n", SCIPconsGetName(cons0), SCIPconsGetName(cons1));
717
718 /* if a side of cons1 gets finite via merging with cons0, then this changes locks and events */
719 if( (SCIPisInfinity(scip, -consdata1->lhs) && !SCIPisInfinity(scip, -consdata0->lhs)) ||
720 ( SCIPisInfinity(scip, consdata1->rhs) && !SCIPisInfinity(scip, consdata0->rhs)) )
721 {
722 SCIP_CALL( dropVarEvents(scip, conshdlrdata->eventhdlr, cons1) );
723 SCIP_CALL( SCIPunlockVarCons(scip, consdata1->x, cons1, !SCIPisInfinity(scip, -consdata1->lhs), !SCIPisInfinity(scip, consdata1->rhs)) );
724 if( consdata1->zcoef > 0.0 )
725 SCIP_CALL( SCIPunlockVarCons(scip, consdata1->z, cons1, !SCIPisInfinity(scip, -consdata1->lhs), !SCIPisInfinity(scip, consdata1->rhs)) );
726 else
727 SCIP_CALL( SCIPunlockVarCons(scip, consdata1->z, cons1, !SCIPisInfinity(scip, consdata1->rhs), !SCIPisInfinity(scip, -consdata1->lhs)) );
728
729 consdata1->lhs = MAX(consdata0->lhs, consdata1->lhs);
730 consdata1->rhs = MIN(consdata0->rhs, consdata1->rhs);
731
732 SCIP_CALL( catchVarEvents(scip, conshdlrdata->eventhdlr, cons1) );
733 SCIP_CALL( SCIPlockVarCons(scip, consdata1->x, cons1, !SCIPisInfinity(scip, -consdata1->lhs), !SCIPisInfinity(scip, consdata1->rhs)) );
734 if( consdata1->zcoef > 0.0 )
735 SCIP_CALL( SCIPlockVarCons(scip, consdata1->z, cons1, !SCIPisInfinity(scip, -consdata1->lhs), !SCIPisInfinity(scip, consdata1->rhs)) );
736 else
737 SCIP_CALL( SCIPlockVarCons(scip, consdata1->z, cons1, !SCIPisInfinity(scip, consdata1->rhs), !SCIPisInfinity(scip, -consdata1->lhs)) );
738 }
739 else
740 {
741 consdata1->lhs = MAX(consdata0->lhs, consdata1->lhs);
742 consdata1->rhs = MIN(consdata0->rhs, consdata1->rhs);
743 }
744
745 SCIP_CALL( SCIPdelCons(scip, cons0) );
746 ++*ndelconss;
747 *success = TRUE;
748
749 break;
750 }
751
752 /* if cons1 defines a linear expression for sign(x+offset)|x+offset|^n, use it to replace cons0 by a linear constraint */
753 if( SCIPisEQ(scip, consdata1->lhs, consdata1->rhs) )
754 {
755 SCIPdebugMsg(scip, "substitute <%s> in <%s> to make linear constraint\n", SCIPconsGetName(cons1), SCIPconsGetName(cons0));
756 SCIP_CALL( presolveFindDuplicatesUpgradeCons(scip, cons0, cons1, infeas, nupgdconss, ndelconss, naggrvars) );
757
758 *success = TRUE;
759 break;
760 }
761
762 /* if cons0 defines a linear expression for sign(x+offset)|x+offset|^n, use it to replace cons1 by a linear constraint */
763 if( SCIPisEQ(scip, consdata0->lhs, consdata0->rhs) )
764 {
765 SCIPdebugMsg(scip, "substitute <%s> in <%s> to make linear constraint\n", SCIPconsGetName(cons0), SCIPconsGetName(cons1));
766 SCIP_CALL( presolveFindDuplicatesUpgradeCons(scip, cons1, cons0, infeas, nupgdconss, ndelconss, naggrvars) );
767
768 SCIP_CALL( SCIPmultihashRemove(multihash, cons1) );
769 *success = TRUE;
770
771 if( *infeas )
772 break;
773 }
774 else
775 {
776 /* introduce a new equality constraint for sign(x+offset)|x+offset|^n and use it to replace cons0 and cons1 */
777 /* @todo maybe we could be more clever by looking which constraint sides are finite */
778 SCIP_VAR* auxvar;
779 SCIP_CONS* auxcons;
780 char name[SCIP_MAXSTRLEN];
781 SCIP_VAR* vars[2];
782 SCIP_Real coefs[2];
783
784 SCIPdebugMsg(scip, "introduce new auxvar for signpower(%s+%g, %g) to make <%s> and <%s> linear constraint\n", SCIPvarGetName(consdata0->x), consdata0->exponent, consdata0->xoffset, SCIPconsGetName(cons0), SCIPconsGetName(cons1));
785
786 /* create auxiliary variable to represent sign(x+offset)|x+offset|^n */
787 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "auxvar_abspower%s_%g_%g", SCIPvarGetName(consdata0->x), consdata0->exponent, consdata0->xoffset);
788 SCIP_CALL( SCIPcreateVar(scip, &auxvar, name, -SCIPinfinity(scip), SCIPinfinity(scip), 0.0, SCIP_VARTYPE_CONTINUOUS,
789 TRUE, TRUE, NULL, NULL, NULL, NULL, NULL) );
790 SCIP_CALL( SCIPaddVar(scip, auxvar) );
791
792 /* create auxiliary constraint auxvar = sign(x+offset)|x+offset|^n
793 * as we introduced a new variable, the constraint that "defines" the value for this variable need to be enforced, that is, is not redundent
794 */
795 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "auxcons_abspower%s_%g_%g", SCIPvarGetName(consdata0->x), consdata0->exponent, consdata0->xoffset);
796 SCIP_CALL( SCIPcreateConsAbspower(scip, &auxcons, name, consdata0->x, auxvar, consdata0->exponent, consdata0->xoffset, -1.0, 0.0, 0.0,
797 SCIPconsIsInitial(cons0) || SCIPconsIsInitial(cons1),
798 SCIPconsIsSeparated(cons0) || SCIPconsIsSeparated(cons1),
799 TRUE,
800 TRUE,
801 SCIPconsIsPropagated(cons0) || SCIPconsIsPropagated(cons1),
802 FALSE,
803 FALSE,
804 SCIPconsIsDynamic(cons0) || SCIPconsIsDynamic(cons1),
805 SCIPconsIsRemovable(cons0) || SCIPconsIsRemovable(cons1),
806 SCIPconsIsStickingAtNode(cons0) || SCIPconsIsStickingAtNode(cons1)
807 ) );
808 SCIP_CALL( SCIPaddCons(scip, auxcons) );
809 SCIP_CALL( SCIPreleaseCons(scip, &auxcons) );
810 ++*naddconss;
811
812 #ifdef WITH_DEBUG_SOLUTION
813 if( SCIPdebugIsMainscip(scip) )
814 {
815 SCIP_Real xval;
816
817 SCIP_CALL( SCIPdebugGetSolVal(scip, consdata0->x, &xval) );
818 SCIP_CALL( SCIPdebugAddSolVal(scip, auxvar, SIGN(xval + consdata0->xoffset) * pow(REALABS(xval + consdata0->xoffset), consdata0->exponent)) );
819 }
820 #endif
821
822 /* create linear constraint equivalent for cons0 */
823 vars[0] = auxvar;
824 vars[1] = consdata0->z;
825 coefs[0] = 1.0;
826 coefs[1] = consdata0->zcoef;
827 SCIP_CALL( SCIPcreateConsLinear(scip, &auxcons, SCIPconsGetName(cons0), 2, vars, coefs, consdata0->lhs, consdata0->rhs,
828 SCIPconsIsInitial(cons0), SCIPconsIsSeparated(cons0), SCIPconsIsEnforced(cons0),
829 SCIPconsIsChecked(cons0), SCIPconsIsPropagated(cons0), SCIPconsIsLocal(cons0),
830 SCIPconsIsModifiable(cons0), SCIPconsIsDynamic(cons0), SCIPconsIsRemovable(cons0),
831 SCIPconsIsStickingAtNode(cons0)) );
832 SCIP_CALL( SCIPaddCons(scip, auxcons) );
833 SCIP_CALL( SCIPreleaseCons(scip, &auxcons) );
834 ++*nupgdconss;
835
836 /* create linear constraint equivalent for cons1 */
837 vars[1] = consdata1->z;
838 coefs[1] = consdata1->zcoef;
839 SCIP_CALL( SCIPcreateConsLinear(scip, &auxcons, SCIPconsGetName(cons1), 2, vars, coefs, consdata1->lhs, consdata1->rhs,
840 SCIPconsIsInitial(cons1), SCIPconsIsSeparated(cons1), SCIPconsIsEnforced(cons1),
841 SCIPconsIsChecked(cons1), SCIPconsIsPropagated(cons1), SCIPconsIsLocal(cons1),
842 SCIPconsIsModifiable(cons1), SCIPconsIsDynamic(cons1), SCIPconsIsRemovable(cons1),
843 SCIPconsIsStickingAtNode(cons1)) );
844 SCIP_CALL( SCIPaddCons(scip, auxcons) );
845 SCIP_CALL( SCIPreleaseCons(scip, &auxcons) );
846 ++*nupgdconss;
847
848 SCIP_CALL( SCIPreleaseVar(scip, &auxvar) );
849
850 SCIP_CALL( SCIPdelCons(scip, cons0) );
851 SCIP_CALL( SCIPdelCons(scip, cons1) );
852 SCIP_CALL( SCIPmultihashRemove(multihash, cons1) );
853 *success = TRUE;
854
855 break;
856 }
857 }
858 else if( consdata0->z == consdata1->z &&
859 consdata0->exponent == 2.0 &&
860 !SCIPisZero(scip, consdata0->zcoef) &&
861 !SCIPisZero(scip, consdata1->zcoef) &&
862 SCIPisEQ(scip, consdata0->lhs, consdata0->rhs) &&
863 SCIPisEQ(scip, consdata1->lhs, consdata1->rhs) &&
864 SCIPisEQ(scip, consdata0->lhs * consdata1->zcoef, consdata1->lhs * consdata0->zcoef) )
865 {
866 /* If we have two equality constraints with the same variables and the same exponent and compatible constants,
867 * then this system of equations should have either no or a single solution.
868 * Thus, we can report cutoff or fix the variables to this solution, and forget about the constraints.
869 * @todo think about inequalities, differing exponents, and exponents != 2
870 */
871
872 SCIP_Real xval;
873 SCIP_Real zval;
874
875 assert(consdata0->x == consdata1->x);
876 assert(consdata0->exponent == consdata1->exponent); /*lint !e777*/
877 assert(!SCIPisEQ(scip, consdata0->xoffset, consdata1->xoffset));
878
879 presolveFindDuplicatesSolveEquations(scip, infeas, &xval, &zval,
880 consdata0->exponent,
881 consdata0->xoffset, consdata0->zcoef, consdata0->lhs,
882 consdata1->xoffset, consdata1->zcoef, consdata1->lhs);
883
884 if( *infeas )
885 {
886 SCIPdebugMsg(scip, "infeasibility detected while solving the equations, no solution exists\n");
887 SCIPdebugPrintCons(scip, cons0, NULL);
888 SCIPdebugPrintCons(scip, cons1, NULL);
889 break;
890 }
891
892 SCIPdebugMsg(scip, "fixing variables <%s>[%g, %g] to %g and <%s>[%g, %g] to %g due to equations\n",
893 SCIPvarGetName(consdata0->x), SCIPvarGetLbLocal(consdata0->x), SCIPvarGetUbLocal(consdata0->x), xval,
894 SCIPvarGetName(consdata0->z), SCIPvarGetLbLocal(consdata0->z), SCIPvarGetUbLocal(consdata0->z), zval);
895 SCIPdebugPrintCons(scip, cons0, NULL);
896 SCIPdebugPrintCons(scip, cons1, NULL);
897
898 if( SCIPvarGetStatus(SCIPvarGetProbvar(consdata0->x)) != SCIP_VARSTATUS_MULTAGGR )
899 {
900 SCIP_Bool fixed;
901
902 SCIP_CALL( SCIPfixVar(scip, consdata0->x, xval, infeas, &fixed) );
903 ++*ndelconss;
904
905 if( fixed )
906 ++*nfixedvars;
907
908 if( *infeas )
909 {
910 SCIPdebugMsg(scip, "infeasibility detected after fixing <%s>\n", SCIPvarGetName(consdata0->x));
911 break;
912 }
913 }
914 else
915 {
916 SCIP_CONS* lincons;
917 SCIP_Real one;
918
919 one = 1.0;
920 SCIP_CALL( SCIPcreateConsLinear(scip, &lincons, SCIPconsGetName(cons0), 1, &consdata0->x, &one, xval, xval,
921 TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE) );
922 SCIP_CALL( SCIPaddCons(scip, lincons) );
923 SCIP_CALL( SCIPreleaseCons(scip, &lincons) );
924 ++*nupgdconss;
925 }
926
927 if( SCIPvarGetStatus(SCIPvarGetProbvar(consdata0->z)) != SCIP_VARSTATUS_MULTAGGR )
928 {
929 SCIP_Bool fixed;
930
931 SCIP_CALL( SCIPfixVar(scip, consdata0->z, zval, infeas, &fixed) );
932 ++*ndelconss;
933
934 if( fixed )
935 ++*nfixedvars;
936
937 if( *infeas )
938 {
939 SCIPdebugMsg(scip, "infeasibility detected after fixing <%s>\n", SCIPvarGetName(consdata0->z));
940 break;
941 }
942 }
943 else
944 {
945 SCIP_CONS* lincons;
946 SCIP_Real one;
947
948 one = 1.0;
949 SCIP_CALL( SCIPcreateConsLinear(scip, &lincons, SCIPconsGetName(cons1), 1, &consdata0->z, &one, zval, zval,
950 TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE) );
951 SCIP_CALL( SCIPaddCons(scip, lincons) );
952 SCIP_CALL( SCIPreleaseCons(scip, &lincons) );
953 ++*nupgdconss;
954 }
955
956 SCIP_CALL( SCIPdelCons(scip, cons0) );
957 SCIP_CALL( SCIPdelCons(scip, cons1) );
958 SCIP_CALL( SCIPmultihashRemove(multihash, cons1) );
959 *success = TRUE;
960
961 break;
962 }
963
964 if( multihashlist == NULL )
965 {
966 /* processed all constraints like cons0 from hash table, but cons0 could not be removed, so insert cons0 into hashmap and go to conss[c+1] */
967 SCIP_CALL( SCIPmultihashInsert(multihash, (void*) cons0) );
968 break;
969 }
970 }
971 while( TRUE ); /*lint !e506*/
972 }
973
974 /* free hash table */
975 SCIPmultihashFree(&multihash);
976
977 if( *infeas )
978 return SCIP_OKAY;
979
980 /* check all constraints in the given set for duplicates, dominance, or possible simplifications w.r.t. the z variable */
981
982 SCIP_CALL( SCIPmultihashCreate(&multihash, SCIPblkmem(scip), SCIPcalcMultihashSize(nconss),
983 presolveFindDuplicatesGetKey, presolveFindDuplicatesKeyEQ2, presolveFindDuplicatesKeyVal2, (void*) scip) );
984
985 for( c = 0; c < nconss && !*infeas; ++c )
986 {
987 SCIP_CONS* cons0;
988 SCIP_CONS* cons1;
989 SCIP_CONSDATA* consdata0;
990
991 cons0 = conss[c]; /*lint !e613*/
992
993 assert(!SCIPconsIsModifiable(cons0)); /* absolute power constraints aren't modifiable */
994 assert(!SCIPconsIsLocal(cons0)); /* shouldn't have local constraints in presolve */
995
996 /* do not consider constraints that we have deleted in the above loop */
997 if( SCIPconsIsDeleted(cons0) )
998 continue;
999 assert(SCIPconsIsActive(cons0)); /* shouldn't get inactive constraints here */
1000
1001 consdata0 = SCIPconsGetData(cons0);
1002 assert(consdata0 != NULL);
1003
1004 /* consider only equality constraints so far
1005 * @todo do also something with inequalities
1006 */
1007 if( !SCIPisEQ(scip, consdata0->lhs, consdata0->rhs) )
1008 continue;
1009
1010 multihashlist = NULL;
1011
1012 do
1013 {
1014 SCIP_CONSDATA* consdata1;
1015
1016 /* get constraint from current hash table with same z variable as cons0 and same exponent */
1017 cons1 = (SCIP_CONS*)(SCIPmultihashRetrieveNext(multihash, &multihashlist, (void*)cons0));
1018 if( cons1 == NULL )
1019 {
1020 /* processed all constraints like cons0 from hash table, so insert cons0 and go to conss[c+1] */
1021 SCIP_CALL( SCIPmultihashInsert(multihash, (void*) cons0) );
1022 break;
1023 }
1024
1025 assert(cons0 != cons1);
1026 assert(!SCIPconsIsDeleted(cons1));
1027
1028 consdata1 = SCIPconsGetData(cons1);
1029 assert(consdata1 != NULL);
1030
1031 SCIPdebugPrintCons(scip, cons0, NULL);
1032 SCIPdebugPrintCons(scip, cons1, NULL);
1033
1034 assert(consdata0->z == consdata1->z);
1035 assert(consdata0->exponent == consdata1->exponent); /*lint !e777*/
1036 assert(SCIPisEQ(scip, consdata1->lhs, consdata1->rhs));
1037 assert(!SCIPisZero(scip, consdata1->zcoef));
1038
1039 if( SCIPisEQ(scip, consdata0->lhs*consdata1->zcoef, consdata1->lhs*consdata0->zcoef) )
1040 {
1041 /* have two absolute power equations with same z and compatible constants
1042 * we can then reduce this to one absolute power and one linear equation
1043 * -> x0 + xoffset0 = signpower(zcoef0/zcoef1, 1/exponent) (x1 + xoffset1)
1044 * -> keep cons1
1045 * the latter can be realized as an aggregation (if x0 and x1 are not multiaggregated) or linear constraint
1046 */
1047 SCIP_Bool redundant;
1048 SCIP_Bool aggregated;
1049 SCIP_Real coef;
1050 SCIP_Real rhs;
1051
1052 SCIPdebugMsg(scip, "<%s> and <%s> can be reformulated to one abspower and one aggregation\n", SCIPconsGetName(cons0), SCIPconsGetName(cons1));
1053 SCIPdebugPrintCons(scip, cons0, NULL);
1054 SCIPdebugPrintCons(scip, cons1, NULL);
1055
1056 if( consdata0->exponent == 2.0 )
1057 coef = SIGN(consdata0->zcoef / consdata1->zcoef) * sqrt(REALABS(consdata0->zcoef / consdata1->zcoef));
1058 else
1059 coef = SIGN(consdata0->zcoef / consdata1->zcoef) * pow(REALABS(consdata0->zcoef / consdata1->zcoef), 1.0/consdata0->exponent);
1060 rhs = coef * consdata1->xoffset - consdata0->xoffset;
1061
1062 /* try aggregation */
1063 SCIP_CALL( SCIPaggregateVars(scip, consdata0->x, consdata1->x, 1.0, -coef, rhs, infeas, &redundant, &aggregated) );
1064 if( *infeas )
1065 {
1066 /* if infeasibility has been detected, stop here */
1067 break;
1068 }
1069 else if( redundant )
1070 {
1071 /* if redundant is TRUE, then either the aggregation has been done, or it was redundant */
1072 if( aggregated )
1073 ++*naggrvars;
1074
1075 ++*ndelconss;
1076 }
1077 else
1078 {
1079 /* if aggregation did not succeed, then either because some variable is multi-aggregated or due to numerics
1080 * we then add a linear constraint instead
1081 */
1082 SCIP_CONS* auxcons;
1083 SCIP_VAR* vars[2];
1084 SCIP_Real coefs[2];
1085
1086 vars[0] = consdata0->x;
1087 vars[1] = consdata1->x;
1088 coefs[0] = 1.0;
1089 coefs[1] = -coef;
1090
1091 /* create linear constraint equivalent for cons0 */
1092 SCIP_CALL( SCIPcreateConsLinear(scip, &auxcons, SCIPconsGetName(cons0), 2, vars, coefs, rhs, rhs,
1093 SCIPconsIsInitial(cons0), SCIPconsIsSeparated(cons0), SCIPconsIsEnforced(cons0),
1094 SCIPconsIsChecked(cons0), SCIPconsIsPropagated(cons0), SCIPconsIsLocal(cons0),
1095 SCIPconsIsModifiable(cons0), SCIPconsIsDynamic(cons0), SCIPconsIsRemovable(cons0),
1096 SCIPconsIsStickingAtNode(cons0)) );
1097 SCIP_CALL( SCIPaddCons(scip, auxcons) );
1098 SCIPdebugPrintCons(scip, auxcons, NULL);
1099 SCIP_CALL( SCIPreleaseCons(scip, &auxcons) );
1100
1101 ++*nupgdconss;
1102 }
1103 SCIP_CALL( SCIPdelCons(scip, cons0) );
1104
1105 *success = TRUE;
1106 break;
1107 }
1108
1109 if( multihashlist == NULL )
1110 {
1111 /* processed all constraints like cons0 from hash table, but cons0 could not be removed, so insert cons0 into hashmap and go to conss[c+1] */
1112 SCIP_CALL( SCIPmultihashInsert(multihash, (void*) cons0) );
1113 break;
1114 }
1115 }
1116 while( TRUE ); /*lint !e506*/
1117 }
1118
1119 /* free hash table */
1120 SCIPmultihashFree(&multihash);
1121
1122 return SCIP_OKAY;
1123 }
1124
1125 /** fix variables not appearing in any other constraint
1126 *
1127 * @todo generalize to inequalities
1128 * @todo generalize to support discrete variables
1129 * @todo generalize to arbitrary exponents also if z is in objective
1130 */
1131 static
presolveDual(SCIP * scip,SCIP_CONS * cons,SCIP_Bool * cutoff,int * ndelconss,int * nfixedvars)1132 SCIP_RETCODE presolveDual(
1133 SCIP* scip, /**< SCIP data structure */
1134 SCIP_CONS* cons, /**< constraint */
1135 SCIP_Bool* cutoff, /**< buffer to indicate whether a cutoff was detected */
1136 int* ndelconss, /**< buffer to increase with the number of deleted constraint */
1137 int* nfixedvars /**< buffer to increase with the number of fixed variables */
1138 )
1139 {
1140 SCIP_CONSDATA* consdata;
1141 SCIP_Bool lhsexists;
1142 SCIP_Bool rhsexists;
1143
1144 assert(scip != NULL);
1145 assert(cons != NULL);
1146 assert(cutoff != NULL);
1147 assert(nfixedvars != NULL);
1148 assert(ndelconss != NULL);
1149
1150 /* only process checked constraints (for which the locks are increased);
1151 * otherwise we would have to check for variables with nlocks == 0, and these are already processed by the
1152 * dualfix presolver
1153 */
1154 if( !SCIPconsIsChecked(cons) )
1155 return SCIP_OKAY;
1156
1157 consdata = SCIPconsGetData(cons);
1158 assert(consdata != NULL);
1159
1160 /* skip dual presolve if multiaggregated variables are present for now (bounds are not updated, difficult to fix) */
1161 if( SCIPvarGetStatus(consdata->x) == SCIP_VARSTATUS_MULTAGGR )
1162 return SCIP_OKAY;
1163 if( SCIPvarGetStatus(consdata->z) == SCIP_VARSTATUS_MULTAGGR )
1164 return SCIP_OKAY;
1165
1166 /* skip dual presolve if discrete variables are present for now (more difficult to compute fixing value) */
1167 if( SCIPvarGetType(consdata->x) <= SCIP_VARTYPE_INTEGER )
1168 return SCIP_OKAY;
1169 if( SCIPvarGetType(consdata->z) <= SCIP_VARTYPE_INTEGER )
1170 return SCIP_OKAY;
1171
1172 /* we assume that domain propagation has been run and fixed variables were removed if possible */
1173 assert(!SCIPconsIsMarkedPropagate(cons));
1174 assert(consdata->zcoef != 0.0);
1175
1176 lhsexists = !SCIPisInfinity(scip, -consdata->lhs);
1177 rhsexists = !SCIPisInfinity(scip, consdata->rhs);
1178
1179 if( SCIPvarGetNLocksDownType(consdata->x, SCIP_LOCKTYPE_MODEL) == (lhsexists ? 1 : 0) &&
1180 SCIPvarGetNLocksUpType(consdata->x, SCIP_LOCKTYPE_MODEL) == (rhsexists ? 1 : 0) &&
1181 (consdata->zcoef > 0.0 ? SCIPvarGetNLocksDownType(consdata->z, SCIP_LOCKTYPE_MODEL) :
1182 SCIPvarGetNLocksUpType(consdata->z, SCIP_LOCKTYPE_MODEL)) == (lhsexists ? 1 : 0) &&
1183 (consdata->zcoef > 0.0 ? SCIPvarGetNLocksUpType(consdata->z, SCIP_LOCKTYPE_MODEL) :
1184 SCIPvarGetNLocksDownType(consdata->z, SCIP_LOCKTYPE_MODEL)) == (rhsexists ? 1 : 0) )
1185 {
1186 /* x and z are only locked by cons, so we can fix them to an optimal solution of
1187 * min xobj * x + zobj * z
1188 * s.t. lhs <= sign(x+offset)*abs(x+offset)^exponent + zcoef * z <= rhs
1189 * xlb <= x <= xub
1190 * zlb <= z <= zub
1191 */
1192 if( SCIPisEQ(scip, consdata->lhs, consdata->rhs) )
1193 {
1194 /* much simpler case where we can substitute z:
1195 * min xobj * x + zobj/zcoef * (rhs - sign(x+offset)*abs(x+offset)^exponent)
1196 * s.t. xlb <= x <= xub
1197 */
1198 SCIP_Real xfix;
1199 SCIP_Real xlb;
1200 SCIP_Real xub;
1201 SCIP_Real zfix;
1202 SCIP_INTERVAL xbnds;
1203 SCIP_INTERVAL zbnds;
1204 SCIP_Bool fixed;
1205
1206 /* Since domain propagation has been applied, we would like to assume that for any valid value for x,
1207 * also the corresponding z value is valid. However, domain propagation only applies sufficiently
1208 * strong bound tightenings, so we better recompute the bounds on x.
1209 */
1210 SCIPintervalSetBounds(&zbnds, SCIPvarGetLbGlobal(consdata->z), SCIPvarGetUbGlobal(consdata->z));
1211 computeBoundsX(scip, cons, zbnds, &xbnds);
1212 xlb = MAX(SCIPvarGetLbGlobal(consdata->x), xbnds.inf); /*lint !e666*/
1213 xub = MIN(SCIPvarGetUbGlobal(consdata->x), xbnds.sup); /*lint !e666*/
1214
1215 /* with our own "local" boundtightening, xlb might end slightly above xub,
1216 * which can result in xfix being outside bounds below, see also #2202
1217 */
1218 assert(SCIPisFeasLE(scip, xlb, xub));
1219 if( xub < xlb )
1220 xlb = xub = (xlb + xub)/2.0;
1221
1222 if( SCIPisZero(scip, SCIPvarGetObj(consdata->z)) )
1223 {
1224 /* even simpler case where objective is linear in x */
1225 if( SCIPisZero(scip, SCIPvarGetObj(consdata->x)) )
1226 {
1227 /* simplest case where objective is zero:
1228 * if zero is within bounds, fix to zero, otherwise
1229 * fix x to middle of bounds for numerical stability. */
1230 if(SCIPisLT(scip, xlb, 0.0) && SCIPisGT(scip, xub, 0.0))
1231 xfix = 0.0;
1232 else
1233 xfix = 0.5 * (xlb + xub);
1234 }
1235 else
1236 {
1237 /* fix x to best bound */
1238 xfix = (SCIPvarGetObj(consdata->x) >= 0.0) ? xlb : xub;
1239 }
1240 }
1241 else if( consdata->exponent == 2.0 )
1242 {
1243 /* consider cases x <= -offset and x >= -offset separately */
1244 SCIP_Real a;
1245 SCIP_Real b;
1246 SCIP_Real c;
1247 SCIP_Real cand;
1248 SCIP_Real xfixobjval;
1249
1250 xfix = SCIP_INVALID;
1251 xfixobjval = SCIP_INVALID;
1252
1253 if( SCIPisLT(scip, xlb, -consdata->xoffset) )
1254 {
1255 /* For x <= -offset, the objective is equivalent to
1256 * zobj/zcoef * x^2 + (xobj + 2 offset zobj/zcoef) * x + offset^2 * zobj/zcoef + other constant
1257 * <-> a * x^2 + b * x + c
1258 *
1259 * critical values for x are xlb, MIN(xub,-offset), and -b/(2*a)
1260 */
1261 a = SCIPvarGetObj(consdata->z) / consdata->zcoef;
1262 b = SCIPvarGetObj(consdata->x) + 2 * consdata->xoffset * SCIPvarGetObj(consdata->z) / consdata->zcoef;
1263 c = consdata->xoffset * consdata->xoffset * SCIPvarGetObj(consdata->z) / consdata->zcoef;
1264
1265 if( a < 0.0 && SCIPisInfinity(scip, -xlb) )
1266 {
1267 /* if a < 0.0, then a*x^2 is unbounded for x -> -infinity, thus fix x to -infinity */
1268 xfix = -SCIPinfinity(scip);
1269 xfixobjval = -SCIPinfinity(scip);
1270 }
1271 else
1272 {
1273 /* initialize with value for x=xlb */
1274 xfix = xlb;
1275 xfixobjval = a * xlb * xlb + b * xlb + c;
1276
1277 /* compare with value for x=MIN(-offset,xub) */
1278 cand = MIN(-consdata->xoffset, xub);
1279 if( xfixobjval > a * cand * cand + b * cand + c )
1280 {
1281 xfix = cand;
1282 xfixobjval = a * cand * cand + b * cand + c;
1283 }
1284
1285 /* compare with value for x=-b/(2*a), if within bounds */
1286 cand = -b/(2.0*a);
1287 if( cand > xlb && cand < -consdata->xoffset && cand < xub && xfixobjval > -b*b/(4.0*a) + c )
1288 {
1289 xfix = cand;
1290 xfixobjval = -b*b/(4.0*a) + c;
1291 }
1292 }
1293 }
1294
1295 if( SCIPisGT(scip, xub, -consdata->xoffset) )
1296 {
1297 /* For x >= -offset, the objective is equivalent to
1298 * -zobj/zcoef * x^2 + (xobj - 2 offset zobj/zcoef) * x - offset^2 * zobj/zcoef + constants
1299 * <-> a * x^2 + b * x + c
1300 *
1301 * critical values for x are xub, MAX(xlb,-offset), and -b/(2*a)
1302 */
1303 a = -SCIPvarGetObj(consdata->z) / consdata->zcoef;
1304 b = SCIPvarGetObj(consdata->x) - 2 * consdata->xoffset * SCIPvarGetObj(consdata->z) / consdata->zcoef;
1305 c = -consdata->xoffset * consdata->xoffset * SCIPvarGetObj(consdata->z) / consdata->zcoef;
1306
1307 if( a < 0.0 && SCIPisInfinity(scip, xub) )
1308 {
1309 /* if a < 0.0, then a*x^2 is unbounded for x -> infinity, thus fix x to infinity */
1310 xfix = SCIPinfinity(scip);
1311 /* not needed: xfixobjval = SCIPinfinity(scip); */
1312 }
1313 else
1314 {
1315 if( xfix == SCIP_INVALID ) /*lint !e777*/
1316 {
1317 /* initialize with value for x=xub */
1318 xfix = xub;
1319 xfixobjval = a * xub * xub + b * xub + c;
1320 }
1321 else
1322 {
1323 /* compare with value for x=xub */
1324 cand = xub;
1325 if( xfixobjval > a * cand * cand + b * cand + c )
1326 {
1327 xfix = cand;
1328 xfixobjval = a * cand * cand + b * cand + c;
1329 }
1330 }
1331
1332 /* compare with value for x=MAX(xlb,-offset) */
1333 cand = MAX(xlb, -consdata->xoffset);
1334 if( xfixobjval > a * cand * cand + b * cand + c )
1335 {
1336 xfix = cand;
1337 xfixobjval = a * cand * cand + b * cand + c;
1338 }
1339
1340 /* compare with value for x=-b/(2*a), if within bounds */
1341 cand = -b/(2.0*a);
1342 if( cand > xlb && cand > -consdata->xoffset && cand < xub && xfixobjval > -b*b/(4.0*a) + c )
1343 {
1344 xfix = cand;
1345 /* not needed: xfixobjval = -b*b/(4.0*a) + c; */
1346 }
1347 }
1348 }
1349 assert(xfix != SCIP_INVALID); /*lint !e777*/
1350 assert(SCIPisInfinity(scip, -xlb) || SCIPisLE(scip, xlb, xfix));
1351 assert(SCIPisInfinity(scip, xub) || SCIPisGE(scip, xub, xfix));
1352 }
1353 else
1354 {
1355 /* skip dual presolve for exponents != 2 and z in objective for now */
1356 return SCIP_OKAY;
1357 }
1358
1359 /* compute fixing value for z */
1360 if( SCIPisInfinity(scip, xfix) )
1361 {
1362 if( consdata->zcoef > 0.0 )
1363 {
1364 assert(SCIPisInfinity(scip, -SCIPvarGetLbGlobal(consdata->z)));
1365 zfix = -SCIPinfinity(scip);
1366 }
1367 else
1368 {
1369 assert(SCIPisInfinity(scip, SCIPvarGetUbGlobal(consdata->z)));
1370 zfix = SCIPinfinity(scip);
1371 }
1372 }
1373 else if( SCIPisInfinity(scip, -xfix) )
1374 {
1375 if( consdata->zcoef > 0.0 )
1376 {
1377 assert(SCIPisInfinity(scip, SCIPvarGetUbGlobal(consdata->z)));
1378 zfix = SCIPinfinity(scip);
1379 }
1380 else
1381 {
1382 assert(SCIPisInfinity(scip, -SCIPvarGetLbGlobal(consdata->z)));
1383 zfix = -SCIPinfinity(scip);
1384 }
1385 }
1386 else
1387 {
1388 SCIP_Real zlb;
1389 SCIP_Real zub;
1390
1391 zlb = SCIPvarGetLbGlobal(consdata->z);
1392 zub = SCIPvarGetUbGlobal(consdata->z);
1393 zfix = consdata->rhs - SIGN(xfix + consdata->xoffset) * consdata->power(ABS(xfix + consdata->xoffset), consdata->exponent);
1394 zfix /= consdata->zcoef;
1395
1396 /* project zfix into box, it should be at least very close */
1397 assert(SCIPisFeasLE(scip, zlb, zfix));
1398 assert(SCIPisFeasGE(scip, zub, zfix));
1399 zfix = MAX(zlb, MIN(zub, zfix));
1400 }
1401
1402 /* fix variables according to x=xfix */
1403 SCIPdebugMsg(scip, "dual presolve fixes x=<%s>[%g,%g] to %g and z=<%s>[%g,%g] to %g in cons <%s>\n",
1404 SCIPvarGetName(consdata->x), xlb, xub, xfix,
1405 SCIPvarGetName(consdata->z), SCIPvarGetLbGlobal(consdata->z), SCIPvarGetUbGlobal(consdata->z), zfix,
1406 SCIPconsGetName(cons));
1407 SCIPdebugPrintCons(scip, cons, NULL);
1408
1409 /* fix x */
1410 SCIP_CALL( SCIPfixVar(scip, consdata->x, xfix, cutoff, &fixed) );
1411 if( *cutoff )
1412 return SCIP_OKAY;
1413 if( fixed )
1414 ++*nfixedvars;
1415
1416 /* fix z */
1417 SCIP_CALL( SCIPfixVar(scip, consdata->z, zfix, cutoff, &fixed) );
1418 if( *cutoff )
1419 return SCIP_OKAY;
1420 if( fixed )
1421 ++*nfixedvars;
1422
1423 /* delete constraint */
1424 SCIP_CALL( SCIPdelCons(scip, cons) );
1425 ++*ndelconss;
1426 }
1427 }
1428
1429 return SCIP_OKAY;
1430 }
1431
1432 /** given a variable and an interval, tightens the local bounds of this variable to the given interval */
1433 static
tightenBounds(SCIP * scip,SCIP_VAR * var,SCIP_INTERVAL bounds,SCIP_Bool force,SCIP_CONS * cons,SCIP_RESULT * result,int * nchgbds,int * nfixedvars,int * naddconss)1434 SCIP_RETCODE tightenBounds(
1435 SCIP* scip, /**< SCIP data structure */
1436 SCIP_VAR* var, /**< variable which bounds to tighten */
1437 SCIP_INTERVAL bounds, /**< new bounds */
1438 SCIP_Bool force, /**< force tightening even if below bound strengthening tolerance */
1439 SCIP_CONS* cons, /**< constraint that is propagated */
1440 SCIP_RESULT* result, /**< pointer to store the result of the propagation call */
1441 int* nchgbds, /**< buffer where to add the number of changed bounds */
1442 int* nfixedvars, /**< buffer where to add the number of fixed variables, can be equal to nchgbds */
1443 int* naddconss /**< buffer where to add the number of added constraints, can be NULL if force is FALSE */
1444 )
1445 {
1446 SCIP_Bool infeas;
1447 SCIP_Bool tightened;
1448
1449 assert(scip != NULL);
1450 assert(var != NULL);
1451 assert(cons != NULL);
1452 assert(result != NULL);
1453 assert(nchgbds != NULL);
1454 assert(nfixedvars != NULL);
1455
1456 *result = SCIP_DIDNOTFIND;
1457
1458 if( SCIPisInfinity(scip, SCIPintervalGetInf(bounds)) || SCIPisInfinity(scip, -SCIPintervalGetSup(bounds)) )
1459 {
1460 /* domain outside [-infty, +infty] -> declare as infeasible */
1461 *result = SCIP_CUTOFF;
1462 return SCIP_OKAY;
1463 }
1464
1465 /* if variable is not multiaggregated (or aggregated to a multiaggregated), then try SCIPfixVar or SCIPtightenVarLb/Ub
1466 * otherwise, if bound tightening is forced, add a linear constraint
1467 * otherwise, forget about the bound tightening
1468 */
1469 if( SCIPvarIsActive(SCIPvarGetProbvar(var)) )
1470 {
1471 /* check if variable can be fixed */
1472 if( SCIPisEQ(scip, bounds.inf, bounds.sup) )
1473 {
1474 if( !SCIPisEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) )
1475 {
1476 /* if variable not fixed yet, then do so now */
1477 SCIP_Real fixval;
1478
1479 if( bounds.inf != bounds.sup ) /*lint !e777*/
1480 fixval = (bounds.inf + bounds.sup) / 2.0;
1481 else
1482 fixval = bounds.inf;
1483 SCIP_CALL( SCIPfixVar(scip, var, fixval, &infeas, &tightened) );
1484
1485 if( infeas )
1486 {
1487 SCIPdebugMsg(scip, "found <%s> infeasible due to fixing variable <%s>\n", SCIPconsGetName(cons), SCIPvarGetName(var));
1488 *result = SCIP_CUTOFF;
1489 return SCIP_OKAY;
1490 }
1491 if( tightened )
1492 {
1493 SCIPdebugMsg(scip, "fixed variable <%s> in constraint <%s> to %g\n", SCIPvarGetName(var), SCIPconsGetName(cons), SCIPvarGetLbLocal(var));
1494 ++*nfixedvars;
1495 *result = SCIP_REDUCEDDOM;
1496 }
1497 }
1498 else
1499 {
1500 /* only check if new fixing value is consistent with variable bounds, otherwise cutoff */
1501 if( SCIPisLT(scip, bounds.sup, SCIPvarGetUbLocal(var)) || SCIPisGT(scip, bounds.inf, SCIPvarGetLbLocal(var)) )
1502 {
1503 SCIPdebugMsg(scip, "found <%s> infeasible due to fixing fixed variable <%s>[%.15g,%.15g] to [%.15g,%.15g]\n",
1504 SCIPconsGetName(cons), SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var), bounds.inf, bounds.sup);
1505 *result = SCIP_CUTOFF;
1506 return SCIP_OKAY;
1507 }
1508 }
1509
1510 return SCIP_OKAY;
1511 }
1512
1513 /* check if lower bound can be tightened */
1514 if( SCIPintervalGetInf(bounds) > SCIPvarGetLbLocal(var) )
1515 {
1516 assert(!SCIPisInfinity(scip, -SCIPintervalGetInf(bounds)));
1517 SCIP_CALL( SCIPtightenVarLb(scip, var, SCIPintervalGetInf(bounds), force, &infeas, &tightened) );
1518 if( infeas )
1519 {
1520 SCIPdebugMsg(scip, "found %s infeasible due to domain propagation for variable %s in constraint %s\n", SCIPconsGetName(cons), SCIPvarGetName(var), SCIPconsGetName(cons));
1521 *result = SCIP_CUTOFF;
1522 return SCIP_OKAY;
1523 }
1524 if( tightened )
1525 {
1526 SCIPdebugMsg(scip, "tightened lower bound of variable %s in constraint %s to %g\n", SCIPvarGetName(var), SCIPconsGetName(cons), SCIPvarGetLbLocal(var));
1527 ++*nchgbds;
1528 *result = SCIP_REDUCEDDOM;
1529 }
1530 }
1531
1532 /* check if upper bound can be tightened */
1533 if( SCIPintervalGetSup(bounds) < SCIPvarGetUbLocal(var) )
1534 {
1535 assert(!SCIPisInfinity(scip, SCIPintervalGetSup(bounds)));
1536 SCIP_CALL( SCIPtightenVarUb(scip, var, SCIPintervalGetSup(bounds), force, &infeas, &tightened) );
1537 if( infeas )
1538 {
1539 SCIPdebugMsg(scip, "found %s infeasible due to domain propagation for linear variable %s in constraint %s\n", SCIPconsGetName(cons), SCIPvarGetName(var), SCIPconsGetName(cons));
1540 *result = SCIP_CUTOFF;
1541 return SCIP_OKAY;
1542 }
1543 if( tightened )
1544 {
1545 SCIPdebugMsg(scip, "tightened upper bound of variable %s in constraint %s to %g\n", SCIPvarGetName(var), SCIPconsGetName(cons), SCIPvarGetUbLocal(var));
1546 ++*nchgbds;
1547 *result = SCIP_REDUCEDDOM;
1548 }
1549 }
1550 }
1551 else if( force && (SCIPisLT(scip, SCIPvarGetLbLocal(var), bounds.inf) || SCIPisGT(scip, SCIPvarGetUbLocal(var), bounds.sup)) )
1552 {
1553 /* add a linear constraint bounds.inf <= x <= bounds.sup */
1554 SCIP_CONS* auxcons;
1555 SCIP_Bool local;
1556 SCIP_Real one;
1557
1558 assert(naddconss != NULL);
1559
1560 /* we add constraint as local constraint if we are during probing or if we are during solve and not at the root node */
1561 local = SCIPinProbing(scip) || (SCIPgetStage(scip) == SCIP_STAGE_SOLVING && (SCIPnodeGetDepth(SCIPgetCurrentNode(scip)) > 0));
1562
1563 one = 1.0;
1564 SCIP_CALL( SCIPcreateConsLinear(scip, &auxcons, SCIPconsGetName(cons), 1, &var, &one, bounds.inf, bounds.sup,
1565 SCIPconsIsInitial(cons), SCIPconsIsSeparated(cons), SCIPconsIsEnforced(cons),
1566 SCIPconsIsChecked(cons), SCIPconsIsPropagated(cons), local,
1567 FALSE, FALSE, TRUE, FALSE) );
1568
1569 if( local )
1570 {
1571 SCIP_CALL( SCIPaddConsLocal(scip, auxcons, NULL) );
1572 }
1573 else
1574 {
1575 SCIP_CALL( SCIPaddCons(scip, auxcons) );
1576 }
1577 SCIP_CALL( SCIPreleaseCons(scip, &auxcons) );
1578
1579 ++*naddconss;
1580 *result = SCIP_CONSADDED;
1581 }
1582
1583 return SCIP_OKAY;
1584 }
1585
1586 /** computes bounds on z in a absolute power constraints for given bounds on x */
1587 static
computeBoundsZ(SCIP * scip,SCIP_CONS * cons,SCIP_INTERVAL xbnds,SCIP_INTERVAL * zbnds)1588 void computeBoundsZ(
1589 SCIP* scip, /**< SCIP data structure */
1590 SCIP_CONS* cons, /**< constraint */
1591 SCIP_INTERVAL xbnds, /**< bounds on x that are to be propagated */
1592 SCIP_INTERVAL* zbnds /**< buffer to store corresponding bounds on z */
1593 )
1594 {
1595 SCIP_CONSDATA* consdata;
1596 SCIP_Real bnd;
1597 SCIP_Real x;
1598
1599 assert(scip != NULL);
1600 assert(cons != NULL);
1601 assert(zbnds != NULL);
1602 assert(!SCIPintervalIsEmpty(SCIPinfinity(scip), xbnds));
1603
1604 consdata = SCIPconsGetData(cons);
1605 assert(consdata != NULL);
1606
1607 SCIPintervalSetEntire(SCIPinfinity(scip), zbnds);
1608
1609 /* apply zcoef*z <= rhs - signedpow(xbnds.inf + offset, n) */
1610 if( !SCIPisInfinity(scip, consdata->rhs) && !SCIPisInfinity(scip, -xbnds.inf) )
1611 {
1612 x = xbnds.inf - PROPVARTOL + consdata->xoffset;
1613 bnd = consdata->rhs + PROPSIDETOL - SIGN(x) * consdata->power(REALABS(x), consdata->exponent);
1614
1615 if( consdata->zcoef > 0.0 )
1616 zbnds->sup = bnd / consdata->zcoef;
1617 else
1618 zbnds->inf = bnd / consdata->zcoef;
1619 }
1620
1621 /* apply zcoef*z >= lhs - signedpow(xbnds.sup + offset, n) */
1622 if( !SCIPisInfinity(scip, -consdata->lhs) && !SCIPisInfinity(scip, xbnds.sup) )
1623 {
1624 x = xbnds.sup + PROPVARTOL + consdata->xoffset;
1625 bnd = consdata->lhs - PROPSIDETOL - SIGN(x) * consdata->power(REALABS(x), consdata->exponent);
1626
1627 if( consdata->zcoef > 0.0 )
1628 zbnds->inf = bnd / consdata->zcoef;
1629 else
1630 zbnds->sup = bnd / consdata->zcoef;
1631 }
1632
1633 SCIPdebugMsg(scip, "given x = [%.15g, %.15g], computed z = [%.15g, %.15g] via", xbnds.inf, xbnds.sup, zbnds->inf, zbnds->sup);
1634 SCIPdebugPrintCons(scip, cons, NULL);
1635
1636 assert(!SCIPintervalIsEmpty(SCIPinfinity(scip), *zbnds));
1637 }
1638
1639 /** computes bounds on x in a absolute power constraints for given bounds on z */
1640 static
computeBoundsX(SCIP * scip,SCIP_CONS * cons,SCIP_INTERVAL zbnds,SCIP_INTERVAL * xbnds)1641 void computeBoundsX(
1642 SCIP* scip, /**< SCIP data structure */
1643 SCIP_CONS* cons, /**< constraint */
1644 SCIP_INTERVAL zbnds, /**< bounds on x that are to be propagated */
1645 SCIP_INTERVAL* xbnds /**< buffer to store corresponding bounds on z */
1646 )
1647 {
1648 SCIP_CONSDATA* consdata;
1649 SCIP_Real bnd;
1650 SCIP_Real z;
1651
1652 assert(scip != NULL);
1653 assert(cons != NULL);
1654 assert(xbnds != NULL);
1655 assert(!SCIPintervalIsEmpty(SCIPinfinity(scip), zbnds));
1656
1657 consdata = SCIPconsGetData(cons);
1658 assert(consdata != NULL);
1659
1660 SCIPintervalSetEntire(SCIPinfinity(scip), xbnds);
1661
1662 /* apply signedpow(x+offset, n) <= rhs - (zcoef * zbnds).inf */
1663 z = (consdata->zcoef > 0.0 ? zbnds.inf : zbnds.sup);
1664 if( !SCIPisInfinity(scip, consdata->rhs) && !SCIPisInfinity(scip, REALABS(z)) )
1665 {
1666 bnd = consdata->rhs + PROPSIDETOL - consdata->zcoef * z + REALABS(consdata->zcoef) * PROPVARTOL;
1667 if( consdata->exponent == 2.0 )
1668 bnd = SIGN(bnd) * sqrt(REALABS(bnd));
1669 else
1670 bnd = SIGN(bnd) * pow(REALABS(bnd), 1.0/consdata->exponent);
1671 xbnds->sup = bnd - consdata->xoffset;
1672 }
1673
1674 /* apply signedpow(x+offset, n) >= lhs - (zcoef * zbnds).sup */
1675 z = (consdata->zcoef > 0.0 ? zbnds.sup : zbnds.inf);
1676 if( !SCIPisInfinity(scip, consdata->rhs) && !SCIPisInfinity(scip, REALABS(z)) )
1677 {
1678 bnd = consdata->lhs - PROPSIDETOL - consdata->zcoef * z - REALABS(consdata->zcoef) * PROPVARTOL;
1679 if( consdata->exponent == 2.0 )
1680 bnd = SIGN(bnd) * sqrt(REALABS(bnd));
1681 else
1682 bnd = SIGN(bnd) * pow(REALABS(bnd), 1.0/consdata->exponent);
1683 xbnds->inf = bnd - consdata->xoffset;
1684 }
1685
1686 SCIPdebugMsg(scip, "given z = [%.15g, %.15g], computed x = [%.15g, %.15g] via", zbnds.inf, zbnds.sup, xbnds->inf, xbnds->sup);
1687 SCIPdebugPrintCons(scip, cons, NULL);
1688
1689 assert(!SCIPintervalIsEmpty(SCIPinfinity(scip), *xbnds));
1690 }
1691
1692 /** checks if x or z is fixed and replaces them or deletes constraint */
1693 static
checkFixedVariables(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS * cons,int * ndelconss,int * nupgdconss,int * nchgbds,int * nfixedvars,SCIP_RESULT * result)1694 SCIP_RETCODE checkFixedVariables(
1695 SCIP* scip, /**< SCIP data structure */
1696 SCIP_CONSHDLR* conshdlr, /**< constraint handler for absolute power constraints */
1697 SCIP_CONS* cons, /**< constraint */
1698 int* ndelconss, /**< counter for number of deleted constraints */
1699 int* nupgdconss, /**< counter for number of upgraded constraints */
1700 int* nchgbds, /**< counter for number of variable bound changes */
1701 int* nfixedvars, /**< counter for number of variable fixations */
1702 SCIP_RESULT* result /**< to store result if we detect infeasibility or remove constraint */
1703 )
1704 {
1705 SCIP_CONSHDLRDATA* conshdlrdata;
1706 SCIP_CONSDATA* consdata;
1707 SCIP_Real scalar;
1708 SCIP_Real constant;
1709 SCIP_Real factor;
1710 SCIP_VAR* var;
1711
1712 assert(scip != NULL);
1713 assert(cons != NULL);
1714 assert(ndelconss != NULL);
1715 assert(nupgdconss != NULL);
1716 assert(nchgbds != NULL);
1717 assert(nfixedvars != NULL);
1718
1719 conshdlrdata = SCIPconshdlrGetData(conshdlr);
1720 assert(conshdlrdata != NULL);
1721
1722 consdata = SCIPconsGetData(cons);
1723 assert(consdata != NULL);
1724
1725 *result = SCIP_DIDNOTFIND;
1726
1727 if( !SCIPvarIsActive(consdata->x) && SCIPvarGetStatus(consdata->x) != SCIP_VARSTATUS_MULTAGGR )
1728 {
1729 /* replace x variable */
1730
1731 /* get relation x = scalar * var + constant */
1732 var = consdata->x;
1733 scalar = 1.0;
1734 constant = 0.0;
1735 SCIP_CALL( SCIPgetProbvarSum(scip, &var, &scalar, &constant) );
1736
1737 if( scalar == 0.0 )
1738 {
1739 SCIP_INTERVAL xbnds;
1740 SCIP_INTERVAL zbnds;
1741 int naddconss;
1742
1743 naddconss = 0;
1744
1745 /* x has been fixed to constant */
1746 assert(SCIPisFeasEQ(scip, SCIPvarGetLbGlobal(consdata->x), constant));
1747 assert(SCIPisFeasEQ(scip, SCIPvarGetUbGlobal(consdata->x), constant));
1748
1749 /* compute corresponding bounds on z */
1750 SCIPintervalSet(&xbnds, constant);
1751 computeBoundsZ(scip, cons, xbnds, &zbnds);
1752
1753 SCIPdebugMsg(scip, "in cons <%s>: x = <%s> fixed to %g -> tighten <%s> to [%g, %g]\n", SCIPconsGetName(cons), SCIPvarGetName(consdata->x), constant, SCIPvarGetName(consdata->z), zbnds.inf, zbnds.sup);
1754
1755 if( SCIPisEQ(scip, consdata->lhs, consdata->rhs) )
1756 {
1757 /* if sides are equal, then we should either fix z, or declare infeasibility */
1758 if( SCIPisFeasLT(scip, SCIPvarGetUbGlobal(consdata->z), zbnds.inf) || SCIPisFeasGT(scip, SCIPvarGetLbGlobal(consdata->z), zbnds.sup) )
1759 {
1760 SCIPdebugMsg(scip, "bounds inconsistent -> cutoff\n");
1761 *result = SCIP_CUTOFF;
1762 return SCIP_OKAY;
1763 }
1764 else
1765 {
1766 /* compute fixing value for z as value corresponding to fixing of x, projected onto bounds of z */
1767 SCIP_Real zfix;
1768
1769 zfix = consdata->rhs - SIGN(constant + consdata->xoffset) * consdata->power(REALABS(constant + consdata->xoffset), consdata->exponent);
1770 zfix /= consdata->zcoef;
1771 assert(SCIPisLE(scip, zbnds.inf, zfix));
1772 assert(SCIPisGE(scip, zbnds.sup, zfix));
1773 zfix = MIN(SCIPvarGetUbGlobal(consdata->z), MAX(SCIPvarGetLbGlobal(consdata->z), zfix)); /*lint !e666*/
1774
1775 zbnds.inf = zfix;
1776 zbnds.sup = zfix;
1777 SCIP_CALL( tightenBounds(scip, consdata->z, zbnds, TRUE, cons, result, nchgbds, nfixedvars, &naddconss) );
1778 }
1779 }
1780 else
1781 {
1782 /* tighten bounds on z accordingly */
1783 SCIP_CALL( tightenBounds(scip, consdata->z, zbnds, TRUE, cons, result, nchgbds, nfixedvars, &naddconss) );
1784 }
1785
1786 /* delete constraint */
1787 SCIP_CALL( SCIPdelCons(scip, cons) );
1788
1789 /* if tightenBounds added a constraint (because z was multiaggregated), then count this as constraint upgrade, otherwise as constraint deletion */
1790 if( naddconss > 0 )
1791 ++*nupgdconss;
1792 else
1793 ++*ndelconss;
1794
1795 return SCIP_OKAY;
1796 }
1797
1798 SCIPdebugMsg(scip, "in cons <%s>: x = <%s> replaced by %g*<%s> + %g\n", SCIPconsGetName(cons), SCIPvarGetName(consdata->x), scalar, SCIPvarGetName(var), constant);
1799
1800 /* constraint will be divided by scalar*pow(|scalar|,exponent-1), if scalar is not 1.0 */
1801 if( scalar == 1.0 )
1802 factor = 1.0;
1803 else if( scalar > 0.0 )
1804 factor = consdata->power( scalar, consdata->exponent);
1805 else
1806 factor = -consdata->power(-scalar, consdata->exponent);
1807
1808 /* aggregate only if this would not lead to a vanishing or infinite coefficient for z */
1809 if( !SCIPisZero(scip, consdata->zcoef / factor) && !SCIPisInfinity(scip, REALABS(consdata->zcoef / factor)) )
1810 {
1811 /* we drop here the events for both variables, because if x is replaced by a multiaggregated variable here, then we do not need to catch bound tightenings on z anymore */
1812 SCIP_CALL( dropVarEvents(scip, conshdlrdata->eventhdlr, cons) );
1813 SCIP_CALL( SCIPunlockVarCons(scip, consdata->x, cons, !SCIPisInfinity(scip, -consdata->lhs), !SCIPisInfinity(scip, consdata->rhs)) );
1814
1815 consdata->x = var;
1816 if( SCIPvarIsActive(consdata->x) )
1817 {
1818 SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, consdata->x) );
1819 }
1820
1821 /* add constant to offset */
1822 consdata->xoffset += constant;
1823
1824 /* divide constraint by factor */
1825 if( scalar == 1.0 ) ;
1826 else if( scalar > 0.0 )
1827 {
1828 if( !SCIPisInfinity(scip, -consdata->lhs) )
1829 consdata->lhs /= factor;
1830 if( !SCIPisInfinity(scip, consdata->rhs) )
1831 consdata->rhs /= factor;
1832 consdata->zcoef /= factor;
1833 consdata->xoffset /= scalar;
1834 }
1835 else
1836 {
1837 SCIP_Real oldlhs;
1838
1839 assert(scalar < 0.0);
1840 assert(factor < 0.0);
1841
1842 oldlhs = consdata->lhs;
1843
1844 if( !SCIPisInfinity(scip, consdata->rhs) )
1845 consdata->lhs = consdata->rhs / factor;
1846 else
1847 consdata->lhs = -SCIPinfinity(scip);
1848 if( !SCIPisInfinity(scip, -oldlhs) )
1849 consdata->rhs = oldlhs / factor;
1850 else
1851 consdata->rhs = SCIPinfinity(scip);
1852 consdata->zcoef /= factor;
1853 consdata->xoffset /= scalar;
1854 /* since we flip both constraint sides and the sign of zcoef, the events catched for z remain the same, so update necessary there */
1855 }
1856
1857 SCIP_CALL( SCIPlockVarCons(scip, consdata->x, cons, !SCIPisInfinity(scip, -consdata->lhs), !SCIPisInfinity(scip, consdata->rhs)) );
1858 SCIP_CALL( catchVarEvents(scip, conshdlrdata->eventhdlr, cons) );
1859
1860 SCIPdebugPrintCons(scip, cons, NULL);
1861
1862 /* rerun constraint comparison */
1863 conshdlrdata->comparedpairwise = FALSE;
1864 }
1865 else
1866 {
1867 SCIPwarningMessage(scip, "Skip resolving aggregation of variable <%s> in abspower constraint <%s> to avoid zcoef = %g\n",
1868 SCIPvarGetName(consdata->x), SCIPconsGetName(cons), consdata->zcoef / factor);
1869 }
1870 }
1871
1872 if( !SCIPvarIsActive(consdata->z) && SCIPvarGetStatus(consdata->z) != SCIP_VARSTATUS_MULTAGGR )
1873 {
1874 /* replace z variable */
1875
1876 /* get relation z = scalar * var + constant */
1877 var = consdata->z;
1878 scalar = 1.0;
1879 constant = 0.0;
1880 SCIP_CALL( SCIPgetProbvarSum(scip, &var, &scalar, &constant) );
1881
1882 if( scalar == 0.0 )
1883 {
1884 SCIP_INTERVAL xbnds;
1885 SCIP_INTERVAL zbnds;
1886 int naddconss;
1887
1888 naddconss = 0;
1889
1890 /* z has been fixed to constant */
1891 assert(SCIPisFeasEQ(scip, SCIPvarGetLbGlobal(consdata->z), constant));
1892 assert(SCIPisFeasEQ(scip, SCIPvarGetUbGlobal(consdata->z), constant));
1893
1894 /* compute corresponding bounds on x */
1895 SCIPintervalSet(&zbnds, constant);
1896 computeBoundsX(scip, cons, zbnds, &xbnds);
1897
1898 SCIPdebugMsg(scip, "in cons <%s>: z = <%s> fixed to %g -> tighten <%s> to [%g, %g]\n", SCIPconsGetName(cons), SCIPvarGetName(consdata->z), constant, SCIPvarGetName(consdata->x), xbnds.inf, xbnds.sup);
1899
1900 if( SCIPisEQ(scip, consdata->lhs, consdata->rhs) )
1901 {
1902 /* if sides are equal, then we should either fix x, or declare infeasibility */
1903 if( SCIPisFeasLT(scip, SCIPvarGetUbGlobal(consdata->x), xbnds.inf) || SCIPisFeasGT(scip, SCIPvarGetLbGlobal(consdata->x), xbnds.sup) )
1904 {
1905 SCIPdebugMsg(scip, "bounds inconsistent -> cutoff\n");
1906 *result = SCIP_CUTOFF;
1907 return SCIP_OKAY;
1908 }
1909 else
1910 {
1911 /* compute fixing value for x as value corresponding to fixing of z, projected onto bounds of x */
1912 SCIP_Real xfix;
1913
1914 xfix = consdata->rhs - consdata->zcoef * constant;
1915 if( consdata->exponent == 2.0 )
1916 xfix = SIGN(xfix) * sqrt(REALABS(xfix)) - consdata->xoffset;
1917 else
1918 xfix = SIGN(xfix) * pow(REALABS(xfix), 1.0/consdata->exponent) - consdata->xoffset;
1919 assert(SCIPisLE(scip, xbnds.inf, xfix));
1920 assert(SCIPisGE(scip, xbnds.sup, xfix));
1921 xfix = MIN(SCIPvarGetUbGlobal(consdata->x), MAX(SCIPvarGetLbGlobal(consdata->x), xfix)); /*lint !e666*/
1922
1923 xbnds.inf = xfix;
1924 xbnds.sup = xfix;
1925 SCIP_CALL( tightenBounds(scip, consdata->x, xbnds, TRUE, cons, result, nchgbds, nfixedvars, &naddconss) );
1926 }
1927 }
1928 else
1929 {
1930 /* tighten bounds on x accordingly */
1931 SCIP_CALL( tightenBounds(scip, consdata->x, xbnds, TRUE, cons, result, nchgbds, nfixedvars, &naddconss) );
1932 }
1933
1934 /* delete constraint */
1935 SCIP_CALL( SCIPdelCons(scip, cons) );
1936
1937 /* if tightenBounds added a constraint (because x was multiaggregated), then count this as constraint upgrade, otherwise as constraint deletion */
1938 if( naddconss > 0 )
1939 ++*nupgdconss;
1940 else
1941 ++*ndelconss;
1942
1943 return SCIP_OKAY;
1944 }
1945
1946 SCIPdebugMsg(scip, "in cons <%s>: z = <%s> replaced by %g*<%s> + %g\n", SCIPconsGetName(cons), SCIPvarGetName(consdata->z), scalar, SCIPvarGetName(var), constant);
1947
1948 /* we drop here the events for both variables, because if z is replaced by a multiaggregated variable here, then we do not need to catch bound tightenings on x anymore */
1949 SCIP_CALL( dropVarEvents(scip, conshdlrdata->eventhdlr, cons) );
1950 if( consdata->zcoef > 0.0 )
1951 SCIP_CALL( SCIPunlockVarCons(scip, consdata->z, cons, !SCIPisInfinity(scip, -consdata->lhs), !SCIPisInfinity(scip, consdata->rhs)) );
1952 else
1953 SCIP_CALL( SCIPunlockVarCons(scip, consdata->z, cons, !SCIPisInfinity(scip, consdata->rhs), !SCIPisInfinity(scip, -consdata->lhs)) );
1954
1955 consdata->z = var;
1956 if( SCIPvarIsActive(consdata->z) )
1957 {
1958 SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, consdata->z) );
1959 }
1960
1961 /* substract constant from constraint sides */
1962 if( !SCIPisInfinity(scip, -consdata->lhs) )
1963 consdata->lhs -= consdata->zcoef * constant;
1964 if( !SCIPisInfinity(scip, consdata->rhs) )
1965 consdata->rhs -= consdata->zcoef * constant;
1966
1967 /* multiply zcoef by scalar */
1968 consdata->zcoef *= scalar;
1969
1970 if( consdata->zcoef > 0.0 )
1971 SCIP_CALL( SCIPlockVarCons(scip, consdata->z, cons, !SCIPisInfinity(scip, -consdata->lhs), !SCIPisInfinity(scip, consdata->rhs)) );
1972 else
1973 SCIP_CALL( SCIPlockVarCons(scip, consdata->z, cons, !SCIPisInfinity(scip, consdata->rhs), !SCIPisInfinity(scip, -consdata->lhs)) );
1974 SCIP_CALL( catchVarEvents(scip, conshdlrdata->eventhdlr, cons) );
1975
1976 /* rerun constraint comparison */
1977 conshdlrdata->comparedpairwise = FALSE;
1978 }
1979
1980 assert(SCIPvarIsActive(consdata->z) || SCIPvarGetStatus(consdata->z) == SCIP_VARSTATUS_MULTAGGR);
1981
1982 return SCIP_OKAY;
1983 }
1984
1985 /** computes violation of a constraint */
1986 static
computeViolation(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS * cons,SCIP_SOL * sol,SCIP_Real * viol,SCIP_Bool * solviolbounds)1987 SCIP_RETCODE computeViolation(
1988 SCIP* scip, /**< SCIP data structure */
1989 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
1990 SCIP_CONS* cons, /**< constraint */
1991 SCIP_SOL* sol, /**< solution or NULL if LP solution should be used */
1992 SCIP_Real* viol, /**< pointer to store absolute (unscaled) constraint violation */
1993 SCIP_Bool* solviolbounds /**< buffer to store whether the solution violates bounds on x by more than feastol */
1994 )
1995 {
1996 SCIP_CONSDATA* consdata;
1997 SCIP_Real val;
1998 SCIP_Real xval;
1999 SCIP_Real zval;
2000 SCIP_Real relviol;
2001
2002 assert(scip != NULL);
2003 assert(conshdlr != NULL);
2004 assert(cons != NULL);
2005 assert(viol != NULL);
2006 assert(solviolbounds != NULL);
2007
2008 consdata = SCIPconsGetData(cons);
2009 assert(consdata != NULL);
2010
2011 *solviolbounds = FALSE;
2012 xval = SCIPgetSolVal(scip, sol, consdata->x);
2013 zval = SCIPgetSolVal(scip, sol, consdata->z);
2014
2015 if( SCIPisInfinity(scip, REALABS(xval)) )
2016 {
2017 consdata->lhsviol = (SCIPisInfinity(scip, -consdata->lhs) ? 0.0 : SCIPinfinity(scip));
2018 consdata->rhsviol = (SCIPisInfinity(scip, consdata->rhs) ? 0.0 : SCIPinfinity(scip));
2019
2020 return SCIP_OKAY;
2021 }
2022
2023 if( sol == NULL )
2024 {
2025 SCIP_Real lb;
2026 SCIP_Real ub;
2027
2028 lb = SCIPvarGetLbLocal(consdata->x);
2029 ub = SCIPvarGetUbLocal(consdata->x);
2030
2031 /* with non-initial columns, variables can shortly be a column variable before entering the LP and have value 0.0 in this case, which might be outside bounds */
2032 if( (!SCIPisInfinity(scip, -lb) && !SCIPisFeasGE(scip, xval, lb)) || (!SCIPisInfinity(scip, ub) && !SCIPisFeasLE(scip, xval, ub)) )
2033 *solviolbounds = TRUE;
2034 else
2035 xval = MAX(lb, MIN(ub, xval)); /* project x onto local box if just slightly outside (or even not outside) */
2036 }
2037
2038 xval += consdata->xoffset;
2039
2040 val = SIGN(xval) * consdata->power(REALABS(xval), consdata->exponent);
2041 val += consdata->zcoef * zval;
2042
2043 *viol = 0.0;
2044 relviol = 0.0;
2045 if( val < consdata->lhs && !SCIPisInfinity(scip, -consdata->lhs) )
2046 {
2047 consdata->lhsviol = *viol = consdata->lhs - val;
2048 relviol = SCIPrelDiff(consdata->lhs, val);
2049 }
2050 else
2051 consdata->lhsviol = 0.0;
2052
2053 if( val > consdata->rhs && !SCIPisInfinity(scip, consdata->rhs) )
2054 {
2055 consdata->rhsviol = *viol = val - consdata->rhs;
2056 relviol = SCIPrelDiff(val, consdata->rhs);
2057 }
2058 else
2059 consdata->rhsviol = 0.0;
2060
2061 if( sol != NULL )
2062 SCIPupdateSolConsViolation(scip, sol, *viol, relviol);
2063
2064 return SCIP_OKAY;
2065 }
2066
2067 /** computes violation of a set of constraints */
2068 static
computeViolations(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS ** conss,int nconss,SCIP_SOL * sol,SCIP_Bool * solviolbounds,SCIP_CONS ** maxviolcon)2069 SCIP_RETCODE computeViolations(
2070 SCIP* scip, /**< SCIP data structure */
2071 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
2072 SCIP_CONS** conss, /**< constraints */
2073 int nconss, /**< number of constraints */
2074 SCIP_SOL* sol, /**< solution or NULL if LP solution should be used */
2075 SCIP_Bool* solviolbounds, /**< buffer to store whether the solution violates bounds on x by more than feastol */
2076 SCIP_CONS** maxviolcon /**< buffer to store constraint with largest violation, or NULL if solution is feasible */
2077 )
2078 {
2079 SCIP_CONSDATA* consdata;
2080 SCIP_Real viol;
2081 SCIP_Real maxviol;
2082 SCIP_Bool solviolbounds1;
2083 int c;
2084
2085 assert(scip != NULL);
2086 assert(conss != NULL || nconss == 0);
2087 assert(solviolbounds != NULL);
2088 assert(maxviolcon != NULL);
2089
2090 *solviolbounds = FALSE;
2091 *maxviolcon = NULL;
2092
2093 maxviol = 0.0;
2094
2095 for( c = 0; c < nconss; ++c )
2096 {
2097 assert(conss != NULL);
2098 assert(conss[c] != NULL);
2099
2100 SCIP_CALL( computeViolation(scip, conshdlr, conss[c], sol, &viol, &solviolbounds1) );
2101 *solviolbounds |= solviolbounds1;
2102
2103 consdata = SCIPconsGetData(conss[c]);
2104 assert(consdata != NULL);
2105
2106 viol = MAX(consdata->lhsviol, consdata->rhsviol);
2107 if( viol > maxviol && SCIPisGT(scip, viol, SCIPfeastol(scip)) )
2108 {
2109 maxviol = viol;
2110 *maxviolcon = conss[c];
2111 }
2112 }
2113
2114 return SCIP_OKAY;
2115 }
2116
2117 /** proposes branching point for constraint */
2118 static
proposeBranchingPoint(SCIP * scip,SCIP_CONS * cons,SCIP_SOL * sol,int preferzero,SCIP_Bool branchminconverror)2119 SCIP_Real proposeBranchingPoint(
2120 SCIP* scip, /**< SCIP data structure */
2121 SCIP_CONS* cons, /**< constraint which variable to get branching point for */
2122 SCIP_SOL* sol, /**< solution to branch on (NULL for LP or pseudosol) */
2123 int preferzero, /**< how much we prefer branching on -xoffset (0, 1, or 2) if sign is not fixed */
2124 SCIP_Bool branchminconverror /**< whether to minimize convexification error if sign is fixed */
2125 )
2126 {
2127 SCIP_CONSDATA* consdata;
2128 SCIP_VAR* x;
2129 SCIP_Real xref;
2130 SCIP_Real zref;
2131 SCIP_Real xlb;
2132 SCIP_Real xub;
2133
2134 assert(scip != NULL);
2135 assert(cons != NULL);
2136
2137 consdata = SCIPconsGetData(cons);
2138 assert(consdata != NULL);
2139
2140 x = consdata->x;
2141 xlb = SCIPvarGetLbLocal(x);
2142 xub = SCIPvarGetUbLocal(x);
2143
2144 /* check if sign of x is not fixed yet */
2145 if( SCIPisLT(scip, xlb, -consdata->xoffset) && SCIPisGT(scip, xub, -consdata->xoffset) )
2146 {
2147 /* if preferzero is 0, just return SCIP_INVALID
2148 * if preferzero is 1, then propose -xoffset if branching on -xoffset would cut off solution in both child nodes, otherwise return SCIP_INVALID
2149 * if preferzero is >1, then always propose -xoffset
2150 */
2151 assert(preferzero >= 0);
2152
2153 if( preferzero == 0 )
2154 return SCIP_INVALID;
2155
2156 if( preferzero > 1 || SCIPisInfinity(scip, -xlb) || SCIPisInfinity(scip, xub) )
2157 return -consdata->xoffset;
2158
2159 xlb += consdata->xoffset;
2160 xub += consdata->xoffset;
2161
2162 xref = SCIPgetSolVal(scip, sol, x) + consdata->xoffset;
2163 zref = SCIPgetSolVal(scip, sol, consdata->z);
2164 if( SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
2165 {
2166 /* signpow(x,n,offset) + c*z <= 0 is violated
2167 * if we are close to or right of -offset, then branching on -offset gives a convex function on the right branch, this is good
2168 * otherwise if branching on -offset yields a violated secant cut in left branch, then current solution would be cutoff there, this is also still good
2169 */
2170 if( !SCIPisFeasNegative(scip, xref) || SCIPisFeasPositive(scip, -consdata->power(-xlb, consdata->exponent)*xref/xlb + consdata->zcoef * zref) )
2171 return -consdata->xoffset;
2172 return SCIP_INVALID;
2173 }
2174
2175 assert(SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) );
2176 /* signpow(x,n) + c*z >= 0 is violated
2177 * if we are close to or left of zero, then branching on 0.0 gives a concave function on the left branch, this is good
2178 * otherwise if branching on 0.0 yields a violated secant cut in right branch, then current solution would be cutoff there, this is also still good
2179 */
2180 if( !SCIPisFeasPositive(scip, xref) || SCIPisFeasNegative(scip, -consdata->power(xub, consdata->exponent)*xref/xub + consdata->zcoef * zref) )
2181 return -consdata->xoffset;
2182 return SCIP_INVALID;
2183 }
2184
2185 if( branchminconverror )
2186 {
2187 /* given x^n with xlb <= x <= xub, then the sum of the integrals between the function and its secant on the left and right branches are minimized
2188 * for branching on ( (ub^n - lb^n) / (n*(ub - lb)) ) ^ (1/(n-1))
2189 */
2190 if( SCIPisGE(scip, xlb, -consdata->xoffset) )
2191 {
2192 SCIP_Real ref;
2193 xlb = MAX(0.0, xlb + consdata->xoffset);
2194 xub = MAX(0.0, xub + consdata->xoffset);
2195
2196 ref = (consdata->power(xub, consdata->exponent) - consdata->power(xlb, consdata->exponent)) / (consdata->exponent * (xub - xlb));
2197 ref = pow(ref, 1.0/(consdata->exponent-1.0));
2198 ref -= consdata->xoffset;
2199 assert(SCIPisGE(scip, ref, SCIPvarGetLbLocal(x)));
2200 assert(SCIPisLE(scip, ref, SCIPvarGetUbLocal(x)));
2201
2202 return ref;
2203 }
2204 else
2205 {
2206 SCIP_Real ref;
2207
2208 assert(SCIPisLE(scip, xub, -consdata->xoffset));
2209
2210 xlb = MIN(0.0, xlb + consdata->xoffset);
2211 xub = MIN(0.0, xub + consdata->xoffset);
2212
2213 ref = (consdata->power(-xlb, consdata->exponent) - consdata->power(-xub, consdata->exponent)) / (consdata->exponent * (-xlb + xub));
2214 ref = -pow(ref, 1.0/(consdata->exponent-1.0));
2215 ref -= consdata->xoffset;
2216 assert(SCIPisGE(scip, ref, SCIPvarGetLbLocal(x)));
2217 assert(SCIPisLE(scip, ref, SCIPvarGetUbLocal(x)));
2218
2219 return ref;
2220 }
2221 }
2222
2223 return SCIP_INVALID;
2224 }
2225
2226 /** registers branching variable candidates
2227 * registers x for all violated absolute power constraints where x is not in convex region
2228 */
2229 static
registerBranchingCandidates(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS ** conss,int nconss,SCIP_SOL * sol,int * nnotify)2230 SCIP_RETCODE registerBranchingCandidates(
2231 SCIP* scip, /**< SCIP data structure */
2232 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
2233 SCIP_CONS** conss, /**< constraints to check */
2234 int nconss, /**< number of constraints to check */
2235 SCIP_SOL* sol, /**< solution to enforce (NULL for the LP solution) */
2236 int* nnotify /**< counter for number of notifications performed */
2237 )
2238 {
2239 SCIP_CONSHDLRDATA* conshdlrdata;
2240 SCIP_CONSDATA* consdata;
2241 SCIP_Bool onlynonfixedsign;
2242 int c;
2243
2244 assert(scip != NULL);
2245 assert(conshdlr != NULL);
2246 assert(conss != NULL || nconss == 0);
2247
2248 conshdlrdata = SCIPconshdlrGetData(conshdlr);
2249 assert(conshdlrdata != NULL);
2250
2251 *nnotify = 0;
2252
2253 onlynonfixedsign = conshdlrdata->preferzerobranch == 3;
2254
2255 do
2256 {
2257 for( c = 0; c < nconss; ++c )
2258 {
2259 assert(conss[c] != NULL); /*lint !e613*/
2260
2261 /* skip constraints that have been marked to be removed by propagateCons() */
2262 if( !SCIPconsIsEnabled(conss[c]) ) /*lint !e613*/
2263 continue;
2264
2265 consdata = SCIPconsGetData(conss[c]); /*lint !e613*/
2266 assert(consdata != NULL);
2267
2268 SCIPdebugMsg(scip, "cons <%s> violation: %g %g\n", SCIPconsGetName(conss[c]), consdata->lhsviol, consdata->rhsviol); /*lint !e613*/
2269
2270 /* skip variables which sign is already fixed, if we are only interested in variables with unfixed sign here */
2271 if( onlynonfixedsign &&
2272 ( !SCIPisLT(scip, SCIPvarGetLbLocal(consdata->x), -consdata->xoffset) ||
2273 !SCIPisGT(scip, SCIPvarGetUbLocal(consdata->x), consdata->xoffset)) )
2274 continue;
2275
2276 /* if the value of x lies in a concave range (i.e., where a secant approximation is used), then register x as branching variable */
2277 if( (SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) && (SCIPisInfinity(scip, -SCIPvarGetLbLocal(consdata->x)) || SCIPgetSolVal(scip, sol, consdata->x) + consdata->xoffset <= -consdata->root * (SCIPvarGetLbLocal(consdata->x) + consdata->xoffset))) ||
2278 ( SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) && (SCIPisInfinity(scip, SCIPvarGetUbLocal(consdata->x)) || SCIPgetSolVal(scip, sol, consdata->x) + consdata->xoffset >= -consdata->root * (SCIPvarGetUbLocal(consdata->x) + consdata->xoffset))) )
2279 {
2280 /* domain propagation should have removed constraints with fixed x, at least for violated constraints */
2281 assert(!SCIPisRelEQ(scip, SCIPvarGetLbLocal(consdata->x), SCIPvarGetUbLocal(consdata->x)));
2282
2283 SCIPdebugMsg(scip, "register var <%s> in cons <%s> with violation %g %g\n", SCIPvarGetName(consdata->x), SCIPconsGetName(conss[c]), consdata->lhsviol, consdata->rhsviol); /*lint !e613*/
2284 SCIP_CALL( SCIPaddExternBranchCand(scip, consdata->x, MAX(consdata->lhsviol, consdata->rhsviol), proposeBranchingPoint(scip, conss[c], sol, conshdlrdata->preferzerobranch, conshdlrdata->branchminconverror)) ); /*lint !e613*/
2285 ++*nnotify;
2286 }
2287 }
2288
2289 if( onlynonfixedsign && *nnotify == 0 )
2290 {
2291 /* if we could not a variable in a violated constraint which sign is not already fixed, do another round where we consider all variables again */
2292 onlynonfixedsign = FALSE;
2293 continue;
2294 }
2295 break;
2296 }
2297 while( TRUE ); /*lint !e506 */
2298
2299 return SCIP_OKAY; /*lint !e438*/
2300 }
2301
2302 /** registers a variable from a violated constraint as branching candidate that has a large absolute value in the relaxation */
2303 static
registerLargeRelaxValueVariableForBranching(SCIP * scip,SCIP_CONS ** conss,int nconss,SCIP_SOL * sol,SCIP_VAR ** brvar)2304 SCIP_RETCODE registerLargeRelaxValueVariableForBranching(
2305 SCIP* scip, /**< SCIP data structure */
2306 SCIP_CONS** conss, /**< constraints */
2307 int nconss, /**< number of constraints */
2308 SCIP_SOL* sol, /**< solution to enforce (NULL for the LP solution) */
2309 SCIP_VAR** brvar /**< buffer to store branching variable */
2310 )
2311 {
2312 SCIP_CONSDATA* consdata;
2313 SCIP_Real val;
2314 SCIP_Real brvarval;
2315 int c;
2316
2317 assert(scip != NULL);
2318 assert(conss != NULL || nconss == 0);
2319
2320 *brvar = NULL;
2321 brvarval = -1.0;
2322
2323 for( c = 0; c < nconss; ++c )
2324 {
2325 assert(conss != NULL);
2326 consdata = SCIPconsGetData(conss[c]);
2327 assert(consdata != NULL);
2328
2329 /* skip constraints that have been marked to be removed by propagateCons() */
2330 if( !SCIPconsIsEnabled(conss[c]) )
2331 continue;
2332
2333 if( !SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) && !SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
2334 continue;
2335
2336 val = SCIPgetSolVal(scip, sol, consdata->x) + consdata->xoffset;
2337 if( REALABS(val) > brvarval )
2338 {
2339 brvarval = ABS(val);
2340 *brvar = consdata->x;
2341 }
2342 }
2343
2344 if( *brvar != NULL )
2345 {
2346 SCIP_CALL( SCIPaddExternBranchCand(scip, *brvar, brvarval, SCIP_INVALID) );
2347 }
2348
2349 return SCIP_OKAY;
2350 }
2351
2352 /* try to fix almost fixed x variable in violated constraint */
2353 static
fixAlmostFixedX(SCIP * scip,SCIP_CONS ** conss,int nconss,SCIP_Bool * infeasible,SCIP_Bool * reduceddom)2354 SCIP_RETCODE fixAlmostFixedX(
2355 SCIP* scip, /**< SCIP data structure */
2356 SCIP_CONS** conss, /**< constraints */
2357 int nconss, /**< number of constraints */
2358 SCIP_Bool* infeasible, /**< buffer to store whether infeasibility was detected */
2359 SCIP_Bool* reduceddom /**< buffer to store whether some variable bound was tightened */
2360 )
2361 {
2362 SCIP_CONSDATA* consdata;
2363 SCIP_Real lb;
2364 SCIP_Real ub;
2365 SCIP_Bool tightened;
2366 int c;
2367
2368 assert(scip != NULL);
2369 assert(conss != NULL);
2370 assert(infeasible != NULL);
2371 assert(reduceddom != NULL);
2372
2373 *infeasible = FALSE;
2374 *reduceddom = FALSE;
2375
2376 for( c = 0; c < nconss; ++c )
2377 {
2378 consdata = SCIPconsGetData(conss[c]);
2379 assert(consdata != NULL);
2380
2381 /* if constraint not violated, then continue */
2382 if( !SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) && !SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
2383 continue;
2384
2385 lb = SCIPvarGetLbLocal(consdata->x);
2386 ub = SCIPvarGetUbLocal(consdata->x);
2387
2388 /* if x not almost fixed, then continue */
2389 if( !SCIPisRelEQ(scip, lb, ub) )
2390 continue;
2391
2392 /* if x fixed already, then continue */
2393 if( SCIPisEQ(scip, lb, ub) )
2394 continue;
2395
2396 assert(!SCIPisInfinity(scip, -lb));
2397 assert(!SCIPisInfinity(scip, ub));
2398
2399 /* try to fix variable */
2400 SCIP_CALL( SCIPtightenVarLb(scip, consdata->x, (lb+ub)/2.0, TRUE, infeasible, &tightened) );
2401 if( *infeasible )
2402 {
2403 SCIPdebugMsg(scip, "Fixing almost fixed variable <%s> lead to infeasibility.\n", SCIPvarGetName(consdata->x));
2404 return SCIP_OKAY;
2405 }
2406 if( tightened )
2407 {
2408 SCIPdebugMsg(scip, "Tightened lower bound of almost fixed variable <%s>.\n", SCIPvarGetName(consdata->x));
2409 *reduceddom = TRUE;
2410 }
2411
2412 SCIP_CALL( SCIPtightenVarUb(scip, consdata->x, (lb+ub)/2.0, TRUE, infeasible, &tightened) );
2413 if( *infeasible )
2414 {
2415 SCIPdebugMsg(scip, "Fixing almost fixed variable <%s> lead to infeasibility.\n", SCIPvarGetName(consdata->x));
2416 return SCIP_OKAY;
2417 }
2418 if( tightened )
2419 {
2420 SCIPdebugMsg(scip, "Tightened upper bound of almost fixed variable <%s>.\n", SCIPvarGetName(consdata->x));
2421 *reduceddom = TRUE;
2422 }
2423
2424 /* stop as soon as one variable has been fixed to start another enfo round */
2425 if( *reduceddom )
2426 break;
2427 }
2428
2429 return SCIP_OKAY;
2430 }
2431
2432 /** resolves a propagation on the given variable by supplying the variables needed for applying the corresponding
2433 * propagation rule (see propagateCons()):
2434 * see cons_varbound
2435 */
2436 static
resolvePropagation(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * infervar,PROPRULE proprule,SCIP_BOUNDTYPE boundtype,SCIP_BDCHGIDX * bdchgidx)2437 SCIP_RETCODE resolvePropagation(
2438 SCIP* scip, /**< SCIP data structure */
2439 SCIP_CONS* cons, /**< constraint that inferred the bound change */
2440 SCIP_VAR* infervar, /**< variable that was deduced */
2441 PROPRULE proprule, /**< propagation rule that deduced the bound change */
2442 SCIP_BOUNDTYPE boundtype, /**< the type of the changed bound (lower or upper bound) */
2443 SCIP_BDCHGIDX* bdchgidx /**< bound change index (time stamp of bound change), or NULL for current time */
2444 )
2445 {
2446 SCIP_CONSDATA* consdata;
2447
2448 assert(scip != NULL);
2449 assert(cons != NULL);
2450 assert(infervar != NULL);
2451
2452 consdata = SCIPconsGetData(cons);
2453 assert(consdata != NULL);
2454 assert(consdata->zcoef != 0.0);
2455
2456 switch( proprule )
2457 {
2458 case PROPRULE_1:
2459 /* lhs <= sign(x+offset)|x+offset|^n + c*z: left hand side and bounds on z -> lower bound on x */
2460 assert(infervar == consdata->x);
2461 assert(boundtype == SCIP_BOUNDTYPE_LOWER);
2462 assert(!SCIPisInfinity(scip, -consdata->lhs));
2463 if( consdata->zcoef > 0.0 )
2464 {
2465 SCIP_CALL( SCIPaddConflictUb(scip, consdata->z, bdchgidx) );
2466 }
2467 else
2468 {
2469 SCIP_CALL( SCIPaddConflictLb(scip, consdata->z, bdchgidx) );
2470 }
2471 break;
2472
2473 case PROPRULE_2:
2474 /* lhs <= sign(x+offset)|x+offset|^n + c*z: left hand side and upper bound on x -> bound on z */
2475 assert(infervar == consdata->z);
2476 assert(!SCIPisInfinity(scip, -consdata->lhs));
2477 SCIP_CALL( SCIPaddConflictUb(scip, consdata->x, bdchgidx) );
2478 break;
2479
2480 case PROPRULE_3:
2481 /* sign(x+offset)|x+offset|^n + c*z <= rhs: right hand side and bounds on z -> upper bound on x */
2482 assert(infervar == consdata->x);
2483 assert(boundtype == SCIP_BOUNDTYPE_UPPER);
2484 assert(!SCIPisInfinity(scip, consdata->rhs));
2485 if( consdata->zcoef > 0.0 )
2486 {
2487 SCIP_CALL( SCIPaddConflictLb(scip, consdata->z, bdchgidx) );
2488 }
2489 else
2490 {
2491 SCIP_CALL( SCIPaddConflictUb(scip, consdata->z, bdchgidx) );
2492 }
2493 break;
2494
2495 case PROPRULE_4:
2496 /* sign(x+offset)|x+offset|^n + c*z <= rhs: right hand side and lower bound on x -> bound on z */
2497 assert(infervar == consdata->z);
2498 assert(!SCIPisInfinity(scip, consdata->rhs));
2499 SCIP_CALL( SCIPaddConflictLb(scip, consdata->x, bdchgidx) );
2500 break;
2501
2502 case PROPRULE_INVALID:
2503 default:
2504 SCIPerrorMessage("invalid inference information %d in absolute power constraint <%s>\n", proprule, SCIPconsGetName(cons));
2505 return SCIP_INVALIDDATA;
2506 }
2507
2508 return SCIP_OKAY;
2509 }
2510
2511 /** analyze infeasibility */
2512 static
analyzeConflict(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * infervar,PROPRULE proprule,SCIP_BOUNDTYPE boundtype)2513 SCIP_RETCODE analyzeConflict(
2514 SCIP* scip, /**< SCIP data structure */
2515 SCIP_CONS* cons, /**< variable bound constraint */
2516 SCIP_VAR* infervar, /**< variable that was deduced */
2517 PROPRULE proprule, /**< propagation rule that deduced the bound change */
2518 SCIP_BOUNDTYPE boundtype /**< the type of the changed bound (lower or upper bound) */
2519 )
2520 {
2521 /* conflict analysis can only be applied in solving stage and if it turned on */
2522 if( (SCIPgetStage(scip) != SCIP_STAGE_SOLVING && !SCIPinProbing(scip)) || !SCIPisConflictAnalysisApplicable(scip) )
2523 return SCIP_OKAY;
2524
2525 /* initialize conflict analysis, and add all variables of infeasible constraint to conflict candidate queue */
2526 SCIP_CALL( SCIPinitConflictAnalysis(scip, SCIP_CONFTYPE_PROPAGATION, FALSE) );
2527
2528 /* add the bound which got violated */
2529 if( boundtype == SCIP_BOUNDTYPE_LOWER )
2530 {
2531 SCIP_CALL( SCIPaddConflictUb(scip, infervar, NULL) );
2532 }
2533 else
2534 {
2535 assert(boundtype == SCIP_BOUNDTYPE_UPPER);
2536 SCIP_CALL( SCIPaddConflictLb(scip, infervar, NULL) );
2537 }
2538
2539 /* add the reason for the violated of the bound */
2540 SCIP_CALL( resolvePropagation(scip, cons, infervar, proprule, boundtype, NULL) );
2541
2542 /* analyze the conflict */
2543 SCIP_CALL( SCIPanalyzeConflictCons(scip, cons, NULL) );
2544
2545 return SCIP_OKAY;
2546 }
2547
2548 /** propagation method for absolute power constraint
2549 * SCIPinferVarXbCons to allow for repropagation
2550 */
2551 static
propagateCons(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS * cons,SCIP_Bool canaddcons,SCIP_Bool * cutoff,int * nchgbds,int * naddconss)2552 SCIP_RETCODE propagateCons(
2553 SCIP* scip, /**< SCIP data structure */
2554 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
2555 SCIP_CONS* cons, /**< variable bound constraint */
2556 SCIP_Bool canaddcons, /**< are we allowed to add a linear constraint when enforcing bounds for a multiaggregated variable? */
2557 SCIP_Bool* cutoff, /**< pointer to store whether the node can be cut off */
2558 int* nchgbds, /**< pointer to count number of bound changes */
2559 int* naddconss /**< pointer to count number of added constraints */
2560 )
2561 {
2562 SCIP_CONSDATA* consdata;
2563 SCIP_Real xlb;
2564 SCIP_Real xub;
2565 SCIP_Real zlb;
2566 SCIP_Real zub;
2567 SCIP_Real newlb;
2568 SCIP_Real newub;
2569 SCIP_Bool tightened;
2570 SCIP_Bool tightenedround;
2571 SCIP_Real minact;
2572 SCIP_Real maxact;
2573
2574 assert(conshdlr != NULL);
2575 assert(cutoff != NULL);
2576 assert(nchgbds != NULL);
2577 assert(naddconss != NULL);
2578
2579 consdata = SCIPconsGetData(cons);
2580 assert(consdata != NULL);
2581
2582 SCIPdebugMsg(scip, "propagating absolute power constraint <%s>\n", SCIPconsGetName(cons));
2583
2584 *cutoff = FALSE;
2585
2586 /* get current bounds of variables */
2587 xlb = SCIPvarGetLbLocal(consdata->x);
2588 xub = SCIPvarGetUbLocal(consdata->x);
2589 zlb = SCIPvarGetLbLocal(consdata->z);
2590 zub = SCIPvarGetUbLocal(consdata->z);
2591
2592 /* if some bound is not tightened, tighten bounds of variables as long as possible */
2593 tightenedround = SCIPconsIsMarkedPropagate(cons);
2594 while( tightenedround )
2595 {
2596 tightenedround = FALSE;
2597
2598 /* propagate left hand side inequality: lhs <= (x+offset)*|x+offset|^n + c*z */
2599 if( !SCIPisInfinity(scip, -consdata->lhs) )
2600 {
2601 assert(!*cutoff);
2602
2603 /* propagate bounds on x (if not multiaggregated):
2604 * (1) left hand side and bounds on z -> lower bound on x
2605 */
2606 if( SCIPvarIsActive(SCIPvarGetProbvar(consdata->x)) && (!SCIPisFeasEQ(scip, zlb, zub) || !SCIPisInfinity(scip, REALABS(zlb))) )
2607 {
2608 /* if z is fixed, first compute new lower bound on x without tolerances
2609 * if that is feasible, project new lower bound onto current bounds
2610 * otherwise, recompute with tolerances and continue as usual
2611 * do this only if variable is not essentially fixed to value of infinity
2612 */
2613 if( SCIPisFeasEQ(scip, zlb, zub) && !SCIPisInfinity(scip, zub) )
2614 {
2615 assert(!SCIPisInfinity(scip, -zlb));
2616
2617 newlb = consdata->lhs - consdata->zcoef * (consdata->zcoef > 0.0 ? zub : zlb);
2618
2619 /* invert sign(x+offset)|x+offset|^(n-1) = y -> x = sign(y)|y|^(1/n) - offset */
2620 if( consdata->exponent == 2.0 )
2621 newlb = SIGN(newlb) * sqrt(ABS(newlb));
2622 else
2623 newlb = SIGN(newlb) * pow(ABS(newlb), 1.0/consdata->exponent);
2624 newlb -= consdata->xoffset;
2625
2626 if( SCIPisFeasGT(scip, newlb, xub) )
2627 {
2628 /* if new lower bound for x would yield cutoff, recompute with tolerances */
2629 newlb = consdata->lhs - PROPSIDETOL - consdata->zcoef * (consdata->zcoef > 0.0 ? (zub + PROPVARTOL) : (zlb - PROPVARTOL));
2630
2631 /* invert sign(x+offset)|x+offset|^(n-1) = y -> x = sign(y)|y|^(1/n) - offset */
2632 if( consdata->exponent == 2.0 )
2633 newlb = SIGN(newlb) * sqrt(ABS(newlb));
2634 else
2635 newlb = SIGN(newlb) * pow(ABS(newlb), 1.0/consdata->exponent);
2636 newlb -= consdata->xoffset;
2637 }
2638 else
2639 {
2640 /* project new lower bound onto current bounds */
2641 newlb = MIN(newlb, xub);
2642 }
2643 }
2644 else
2645 {
2646 if( consdata->zcoef > 0.0 )
2647 {
2648 if( !SCIPisInfinity(scip, zub) )
2649 newlb = consdata->lhs - PROPSIDETOL - consdata->zcoef * (zub + PROPVARTOL);
2650 else
2651 newlb = -SCIPinfinity(scip);
2652 }
2653 else
2654 {
2655 if( !SCIPisInfinity(scip, -zlb) )
2656 newlb = consdata->lhs - PROPSIDETOL - consdata->zcoef * (zlb - PROPVARTOL);
2657 else
2658 newlb = -SCIPinfinity(scip);
2659 }
2660
2661 if( !SCIPisInfinity(scip, -newlb) )
2662 {
2663 /* invert sign(x+offset)|x+offset|^(n-1) = y -> x = sign(y)|y|^(1/n) - offset */
2664 if( consdata->exponent == 2.0 )
2665 newlb = SIGN(newlb) * sqrt(ABS(newlb));
2666 else
2667 newlb = SIGN(newlb) * pow(ABS(newlb), 1.0/consdata->exponent);
2668 newlb -= consdata->xoffset;
2669 }
2670 }
2671
2672 if( SCIPisInfinity(scip, newlb) )
2673 {
2674 /* we cannot fix a variable to +infinity, so let's report cutoff (there is no solution within SCIPs limitations to infinity) */
2675 SCIPdebugMsg(scip, " -> tighten <%s>[%.15g,%.15g] -> [%.15g,%.15g] -> cutoff\n", SCIPvarGetName(consdata->x), xlb, xub, newlb, xub);
2676
2677 *cutoff = TRUE;
2678
2679 /* analyze infeasibility */
2680 SCIP_CALL( analyzeConflict(scip, cons, consdata->x, PROPRULE_1, SCIP_BOUNDTYPE_LOWER) );
2681 break;
2682 }
2683
2684 if( !SCIPisInfinity(scip, -newlb) )
2685 {
2686 if( SCIPisLbBetter(scip, newlb, xlb, xub) )
2687 {
2688 SCIPdebugMsg(scip, " -> tighten <%s>[%.15g,%.15g] -> [%.15g,%.15g]\n",
2689 SCIPvarGetName(consdata->x), xlb, xub, newlb, xub);
2690 SCIP_CALL( SCIPinferVarLbCons(scip, consdata->x, newlb, cons, (int)PROPRULE_1, FALSE, cutoff, &tightened) );
2691
2692 if( *cutoff )
2693 {
2694 assert(SCIPisInfinity(scip, newlb) || SCIPisGT(scip, newlb, SCIPvarGetUbLocal(consdata->x)));
2695
2696 /* analyze infeasibility */
2697 SCIP_CALL( analyzeConflict(scip, cons, consdata->x, PROPRULE_1, SCIP_BOUNDTYPE_LOWER) );
2698 break;
2699 }
2700
2701 if( tightened )
2702 {
2703 tightenedround = TRUE;
2704 (*nchgbds)++;
2705 }
2706 xlb = SCIPvarGetLbLocal(consdata->x);
2707 }
2708 }
2709 }
2710
2711 assert(!*cutoff);
2712
2713 /* propagate bounds on z:
2714 * (2) left hand side and upper bound on x -> bound on z
2715 */
2716 if( SCIPvarGetStatus(consdata->z) != SCIP_VARSTATUS_MULTAGGR && !SCIPisInfinity(scip, xub) ) /* cannot change bounds of multaggr vars */
2717 {
2718 SCIP_Real newbd;
2719
2720 /* if x is fixed, first compute new bound on z without tolerances
2721 * if that is feasible, project new bound onto current bounds
2722 * otherwise, recompute with tolerances and continue as usual
2723 */
2724 if( SCIPisFeasEQ(scip, xlb, xub) )
2725 {
2726 newbd = xub + consdata->xoffset;
2727 newbd = consdata->lhs - SIGN(newbd) * consdata->power(REALABS(newbd), consdata->exponent);
2728 newbd /= consdata->zcoef;
2729
2730 if( SCIPisInfinity(scip, newbd) )
2731 newbd = SCIPinfinity(scip);
2732 else if( SCIPisInfinity(scip, -newbd) )
2733 newbd = -SCIPinfinity(scip);
2734
2735 if( (consdata->zcoef > 0.0 && SCIPisFeasGT(scip, newbd, zub)) || (consdata->zcoef < 0.0 && SCIPisFeasLT(scip, newbd, zlb)) )
2736 {
2737 /* if infeasible, recompute with tolerances */
2738 newbd = xub + PROPVARTOL + consdata->xoffset;
2739 newbd = consdata->lhs - PROPSIDETOL - SIGN(newbd) * consdata->power(REALABS(newbd), consdata->exponent);
2740 newbd /= consdata->zcoef;
2741 }
2742 else
2743 {
2744 /* project onto current bounds of z */
2745 newbd = MIN(zub, MAX(zlb, newbd) );
2746 }
2747 }
2748 else
2749 {
2750 newbd = xub + PROPVARTOL + consdata->xoffset;
2751 newbd = consdata->lhs - PROPSIDETOL - SIGN(newbd) * consdata->power(REALABS(newbd), consdata->exponent);
2752 newbd /= consdata->zcoef;
2753 }
2754
2755 if( consdata->zcoef > 0.0 )
2756 {
2757 newlb = newbd;
2758
2759 if( SCIPisInfinity(scip, newlb) )
2760 {
2761 /* we cannot fix a variable to +infinity, so let's report cutoff (there is no solution within SCIPs limitations to infinity) */
2762 SCIPdebugMsg(scip, " -> tighten <%s>[%.15g,%.15g] -> [%.15g,%.15g] -> cutoff\n", SCIPvarGetName(consdata->z), zlb, zub, newlb, zub);
2763
2764 *cutoff = TRUE;
2765
2766 /* analyze infeasibility */
2767 SCIP_CALL( analyzeConflict(scip, cons, consdata->z, PROPRULE_2, SCIP_BOUNDTYPE_LOWER) );
2768 break;
2769 }
2770
2771 if( SCIPisLbBetter(scip, newlb, zlb, zub) )
2772 {
2773 SCIPdebugMsg(scip, " -> tighten <%s>[%.15g,%.15g] -> [%.15g,%.15g]\n",
2774 SCIPvarGetName(consdata->z), zlb, zub, newlb, zub);
2775 SCIP_CALL( SCIPinferVarLbCons(scip, consdata->z, newlb, cons, (int)PROPRULE_2, FALSE, cutoff, &tightened) );
2776
2777 if( *cutoff )
2778 {
2779 assert(SCIPisInfinity(scip, newlb) || SCIPisGT(scip, newlb, SCIPvarGetUbLocal(consdata->z)));
2780
2781 /* analyze infeasibility */
2782 SCIP_CALL( analyzeConflict(scip, cons, consdata->z, PROPRULE_2, SCIP_BOUNDTYPE_LOWER) );
2783 break;
2784 }
2785
2786 if( tightened )
2787 {
2788 tightenedround = TRUE;
2789 (*nchgbds)++;
2790 }
2791 zlb = SCIPvarGetLbLocal(consdata->z);
2792 }
2793 }
2794 else
2795 {
2796 newub = newbd;
2797
2798 if( SCIPisInfinity(scip, -newub) )
2799 {
2800 /* we cannot fix a variable to -infinity, so let's report cutoff (there is no solution within SCIPs limitations to infinity) */
2801 SCIPdebugMsg(scip, " -> tighten <%s>[%.15g,%.15g] -> [%.15g,%.15g] -> cutoff\n", SCIPvarGetName(consdata->z), zlb, zub, zlb, newub);
2802
2803 *cutoff = TRUE;
2804
2805 /* analyze infeasibility */
2806 SCIP_CALL( analyzeConflict(scip, cons, consdata->z, PROPRULE_2, SCIP_BOUNDTYPE_UPPER) );
2807 break;
2808 }
2809
2810 if( SCIPisUbBetter(scip, newub, zlb, zub) )
2811 {
2812 SCIPdebugMsg(scip, " -> tighten <%s>[%.15g,%.15g] -> [%.15g,%.15g]\n",
2813 SCIPvarGetName(consdata->z), zlb, zub, zlb, newub);
2814 SCIP_CALL( SCIPinferVarUbCons(scip, consdata->z, newub, cons, (int)PROPRULE_2, FALSE, cutoff, &tightened) );
2815
2816 if( *cutoff )
2817 {
2818 assert(SCIPisInfinity(scip, -newub) || SCIPisLT(scip, newub, SCIPvarGetLbLocal(consdata->z)));
2819
2820 /* analyze infeasibility */
2821 SCIP_CALL( analyzeConflict(scip, cons, consdata->z, PROPRULE_2, SCIP_BOUNDTYPE_UPPER) );
2822 break;
2823 }
2824
2825 if( tightened )
2826 {
2827 tightenedround = TRUE;
2828 (*nchgbds)++;
2829 }
2830 zub = SCIPvarGetUbLocal(consdata->z);
2831 }
2832 }
2833 }
2834 }
2835
2836 assert(!*cutoff);
2837
2838 /* propagate right hand side inequality: sign(x+offset)|x+offset|^n + c*z <= rhs */
2839 if( !SCIPisInfinity(scip, consdata->rhs) )
2840 {
2841 /* propagate bounds on x:
2842 * (3) right hand side and bounds on z -> upper bound on x
2843 */
2844 if( SCIPvarIsActive(SCIPvarGetProbvar(consdata->x)) && (!SCIPisFeasEQ(scip, zlb, zub) || !SCIPisInfinity(scip, REALABS(zlb))) ) /* cannot change bounds of multaggr or fixed vars */
2845 {
2846 /* if z is fixed, first compute new upper bound on x without tolerances
2847 * if that is feasible, project new upper bound onto current bounds
2848 * otherwise, recompute with tolerances and continue as usual
2849 * do this only if variable is not essentially fixed to value of infinity
2850 */
2851 if( SCIPisFeasEQ(scip, zlb, zub) && !SCIPisInfinity(scip, zub) )
2852 {
2853 assert(!SCIPisInfinity(scip, -zlb));
2854
2855 newub = consdata->rhs - consdata->zcoef * (consdata->zcoef > 0.0 ? zlb : zub);
2856
2857 /* invert sign(x+offset)|x+offset|^(n-1) = y -> x = sign(y)|y|^(1/n) - offset */
2858 if( consdata->exponent == 2.0 )
2859 newub = SIGN(newub) * sqrt(ABS(newub));
2860 else
2861 newub = SIGN(newub) * pow(ABS(newub), 1.0/consdata->exponent);
2862 newub -= consdata->xoffset;
2863
2864 if( SCIPisFeasLT(scip, newub, xlb) )
2865 {
2866 /* if new lower bound for x would yield cutoff, recompute with tolerances */
2867 newub = consdata->rhs + PROPSIDETOL - consdata->zcoef * (consdata->zcoef > 0.0 ? (zlb - PROPVARTOL) : (zub + PROPVARTOL));
2868
2869 /* invert sign(x+offset)|x+offset|^(n-1) = y -> x = sign(y)|y|^(1/n) - offset */
2870 if( consdata->exponent == 2.0 )
2871 newub = SIGN(newub) * sqrt(ABS(newub));
2872 else
2873 newub = SIGN(newub) * pow(ABS(newub), 1.0/consdata->exponent);
2874 newub -= consdata->xoffset;
2875 }
2876 else
2877 {
2878 /* project new upper bound onto current bounds */
2879 newub = MAX(newub, xlb);
2880 }
2881 }
2882 else
2883 {
2884 if( consdata->zcoef > 0.0 )
2885 {
2886 if( !SCIPisInfinity(scip, -zlb) )
2887 newub = consdata->rhs + PROPSIDETOL - consdata->zcoef * (zlb - PROPVARTOL);
2888 else
2889 newub = SCIPinfinity(scip);
2890 }
2891 else
2892 {
2893 if( !SCIPisInfinity(scip, zub) )
2894 newub = consdata->rhs + PROPSIDETOL - consdata->zcoef * (zub + PROPVARTOL);
2895 else
2896 newub = SCIPinfinity(scip);
2897 }
2898 if( !SCIPisInfinity(scip, newub) )
2899 {
2900 /* invert sign(x+offset)|x+offset|^(n-1) = y -> x = sign(y)|y|^(1/n) - offset */
2901 if( consdata->exponent == 2.0 )
2902 newub = SIGN(newub) * sqrt(ABS(newub));
2903 else
2904 newub = SIGN(newub) * pow(ABS(newub), 1.0/consdata->exponent);
2905 newub -= consdata->xoffset;
2906 }
2907 }
2908
2909 if( SCIPisInfinity(scip, -newub) )
2910 {
2911 /* we cannot fix a variable to -infinity, so let's report cutoff (there is no solution within SCIPs limitations to infinity) */
2912 SCIPdebugMsg(scip, " -> tighten <%s>[%.15g,%.15g] -> [%.15g,%.15g] -> cutoff\n", SCIPvarGetName(consdata->x), xlb, xub, xlb, newub);
2913
2914 *cutoff = TRUE;
2915
2916 /* analyze infeasibility */
2917 SCIP_CALL( analyzeConflict(scip, cons, consdata->x, PROPRULE_3, SCIP_BOUNDTYPE_UPPER) );
2918 break;
2919 }
2920
2921 if( !SCIPisInfinity(scip, newub) )
2922 {
2923 if( SCIPisUbBetter(scip, newub, xlb, xub) )
2924 {
2925 SCIPdebugMsg(scip, " -> tighten <%s>[%.15g,%.15g] -> [%.15g,%.15g]\n",
2926 SCIPvarGetName(consdata->x), xlb, xub, xlb, newub);
2927 SCIP_CALL( SCIPinferVarUbCons(scip, consdata->x, newub, cons, (int)PROPRULE_3, FALSE, cutoff, &tightened) );
2928
2929 if( *cutoff )
2930 {
2931 assert(SCIPisInfinity(scip, -newub) || SCIPisLT(scip, newub, SCIPvarGetLbLocal(consdata->x)));
2932
2933 /* analyze infeasibility */
2934 SCIP_CALL( analyzeConflict(scip, cons, consdata->x, PROPRULE_3, SCIP_BOUNDTYPE_UPPER) );
2935 break;
2936 }
2937
2938 if( tightened )
2939 {
2940 tightenedround = TRUE;
2941 (*nchgbds)++;
2942 }
2943 xub = SCIPvarGetUbLocal(consdata->x);
2944 }
2945 }
2946 }
2947
2948 assert(!*cutoff);
2949
2950 /* propagate bounds on z:
2951 * (4) right hand side and lower bound on x -> bound on z
2952 */
2953 if( SCIPvarGetStatus(consdata->z) != SCIP_VARSTATUS_MULTAGGR && !SCIPisInfinity(scip, -xlb) ) /* cannot change bounds of multaggr vars */
2954 {
2955 SCIP_Real newbd;
2956
2957 /* if x is fixed, first compute new bound on z without tolerances
2958 * if that is feasible, project new bound onto current bounds
2959 * otherwise, recompute with tolerances and continue as usual
2960 */
2961 if( SCIPisFeasEQ(scip, xlb, xub) )
2962 {
2963 newbd = xlb + consdata->xoffset;
2964 newbd = consdata->rhs - SIGN(newbd) * consdata->power(REALABS(newbd), consdata->exponent);
2965 newbd /= consdata->zcoef;
2966
2967 if( SCIPisInfinity(scip, newbd) )
2968 newbd = SCIPinfinity(scip);
2969 else if( SCIPisInfinity(scip, -newbd) )
2970 newbd = -SCIPinfinity(scip);
2971
2972 if( (consdata->zcoef > 0.0 && SCIPisFeasLT(scip, newbd, zlb)) || (consdata->zcoef < 0.0 && SCIPisFeasGT(scip, newbd, zub)) )
2973 {
2974 /* if infeasible, recompute with tolerances */
2975 newbd = xlb - PROPVARTOL + consdata->xoffset;
2976 newbd = consdata->rhs + PROPSIDETOL - SIGN(newbd) * consdata->power(REALABS(newbd), consdata->exponent);
2977 newbd /= consdata->zcoef;
2978 }
2979 else
2980 {
2981 /* project onto current bounds of z */
2982 newbd = MIN(zub, MAX(zlb, newbd) );
2983 }
2984 }
2985 else
2986 {
2987 newbd = xlb - PROPVARTOL + consdata->xoffset;
2988 newbd = consdata->rhs + PROPSIDETOL - SIGN(newbd) * consdata->power(REALABS(newbd), consdata->exponent);
2989 newbd /= consdata->zcoef;
2990 }
2991
2992 if( consdata->zcoef > 0.0 )
2993 {
2994 newub = newbd;
2995
2996 if( SCIPisInfinity(scip, -newub) )
2997 {
2998 /* we cannot fix a variable to -infinity, so let's report cutoff (there is no solution within SCIPs limitations to infinity) */
2999 SCIPdebugMsg(scip, " -> tighten <%s>[%.15g,%.15g] -> [%.15g,%.15g] -> cutoff\n", SCIPvarGetName(consdata->z), zlb, zub, zlb, newub);
3000
3001 *cutoff = TRUE;
3002
3003 /* analyze infeasibility */
3004 SCIP_CALL( analyzeConflict(scip, cons, consdata->z, PROPRULE_4, SCIP_BOUNDTYPE_UPPER) );
3005 break;
3006 }
3007
3008 if( SCIPisUbBetter(scip, newub, zlb, zub) )
3009 {
3010 SCIPdebugMsg(scip, " -> tighten <%s>[%.15g,%.15g] -> [%.15g,%.15g]\n",
3011 SCIPvarGetName(consdata->z), zlb, zub, zlb, newub);
3012 SCIP_CALL( SCIPinferVarUbCons(scip, consdata->z, newub, cons, (int)PROPRULE_4, FALSE, cutoff, &tightened) );
3013
3014 if( *cutoff )
3015 {
3016 assert(SCIPisInfinity(scip, -newub) || SCIPisLT(scip, newub, SCIPvarGetLbLocal(consdata->z)));
3017
3018 /* analyze infeasibility */
3019 SCIP_CALL( analyzeConflict(scip, cons, consdata->z, PROPRULE_4, SCIP_BOUNDTYPE_UPPER) );
3020 break;
3021 }
3022
3023 if( tightened )
3024 {
3025 tightenedround = TRUE;
3026 (*nchgbds)++;
3027 }
3028 zub = SCIPvarGetUbLocal(consdata->z);
3029 }
3030 }
3031 else
3032 {
3033 newlb = newbd;
3034
3035 if( SCIPisInfinity(scip, newlb) )
3036 {
3037 /* we cannot fix a variable to +infinity, so let's report cutoff (there is no solution within SCIPs limitations to infinity) */
3038 SCIPdebugMsg(scip, " -> tighten <%s>[%.15g,%.15g] -> [%.15g,%.15g] -> cutoff\n", SCIPvarGetName(consdata->z), zlb, zub, newlb, zub);
3039
3040 *cutoff = TRUE;
3041
3042 /* analyze infeasibility */
3043 SCIP_CALL( analyzeConflict(scip, cons, consdata->z, PROPRULE_4, SCIP_BOUNDTYPE_LOWER) );
3044 break;
3045 }
3046
3047 if( SCIPisLbBetter(scip, newlb, zlb, zub) )
3048 {
3049 SCIPdebugMsg(scip, " -> tighten <%s>[%.15g,%.15g] -> [%.15g,%.15g]\n",
3050 SCIPvarGetName(consdata->z), zlb, zub, newlb, zub);
3051 SCIP_CALL( SCIPinferVarLbCons(scip, consdata->z, newlb, cons, (int)PROPRULE_4, FALSE, cutoff, &tightened) );
3052
3053 if( *cutoff )
3054 {
3055 assert(SCIPisInfinity(scip, newlb) || SCIPisGT(scip, newlb, SCIPvarGetUbLocal(consdata->z)));
3056
3057 /* analyze infeasibility */
3058 SCIP_CALL( analyzeConflict(scip, cons, consdata->z, PROPRULE_4, SCIP_BOUNDTYPE_LOWER) );
3059 break;
3060 }
3061
3062 if( tightened )
3063 {
3064 tightenedround = TRUE;
3065 (*nchgbds)++;
3066 }
3067 zlb = SCIPvarGetLbLocal(consdata->z);
3068 }
3069 }
3070 }
3071 }
3072
3073 assert(!*cutoff);
3074 }
3075
3076 /* mark the constraint propagated */
3077 SCIP_CALL( SCIPunmarkConsPropagate(scip, cons) );
3078
3079 if( *cutoff )
3080 return SCIP_OKAY;
3081
3082 /* check for redundancy */
3083 if( !SCIPisInfinity(scip, -xlb) && !SCIPisInfinity(scip, consdata->zcoef > 0.0 ? -zlb : zub) )
3084 minact = SIGN(xlb + consdata->xoffset) * consdata->power(REALABS(xlb + consdata->xoffset), consdata->exponent) + consdata->zcoef * (consdata->zcoef > 0.0 ? zlb : zub);
3085 else
3086 minact = -SCIPinfinity(scip);
3087
3088 if( !SCIPisInfinity(scip, xub) && !SCIPisInfinity(scip, consdata->zcoef > 0.0 ? zub : -zlb) )
3089 maxact = SIGN(xub + consdata->xoffset) * consdata->power(REALABS(xub + consdata->xoffset), consdata->exponent) + consdata->zcoef * (consdata->zcoef > 0.0 ? zub : zlb);
3090 else
3091 maxact = SCIPinfinity(scip);
3092
3093 if( (SCIPisInfinity(scip, -consdata->lhs) || SCIPisGE(scip, minact, consdata->lhs)) &&
3094 (SCIPisInfinity(scip, consdata->rhs) || SCIPisLE(scip, maxact, consdata->rhs)) )
3095 {
3096 SCIPdebugMsg(scip, "absolute power constraint <%s> is redundant: <%s>[%.15g,%.15g], <%s>[%.15g,%.15g]\n",
3097 SCIPconsGetName(cons),
3098 SCIPvarGetName(consdata->x), SCIPvarGetLbLocal(consdata->x), SCIPvarGetUbLocal(consdata->x),
3099 SCIPvarGetName(consdata->z), SCIPvarGetLbLocal(consdata->z), SCIPvarGetUbLocal(consdata->z));
3100
3101 SCIP_CALL( SCIPdelConsLocal(scip, cons) );
3102
3103 return SCIP_OKAY;
3104 }
3105
3106 /* delete constraint if x has been fixed */
3107 if( SCIPisRelEQ(scip, xlb, xub) && (SCIPvarIsActive(consdata->z) || canaddcons) )
3108 {
3109 SCIP_RESULT tightenresult;
3110 SCIP_INTERVAL xbnds;
3111 SCIP_INTERVAL zbnds;
3112
3113 SCIPdebugMsg(scip, "x-variable in constraint <%s> is fixed: x = <%s>[%.15g,%.15g], z = <%s>[%.15g,%.15g]\n",
3114 SCIPconsGetName(cons), SCIPvarGetName(consdata->x), xlb, xub, SCIPvarGetName(consdata->z), zlb, zub);
3115
3116 SCIPintervalSetBounds(&xbnds, MIN(xlb, xub), MAX(xlb, xub));
3117 computeBoundsZ(scip, cons, xbnds, &zbnds);
3118
3119 /* in difference to the loop above, here we enforce a possible bound tightening on z, and may add a linear cons if z is multiaggregated */
3120 SCIP_CALL( tightenBounds(scip, consdata->z, zbnds, TRUE, cons, &tightenresult, nchgbds, nchgbds, naddconss) );
3121 if( tightenresult == SCIP_CUTOFF )
3122 *cutoff = TRUE;
3123
3124 SCIP_CALL( SCIPdelConsLocal(scip, cons) );
3125
3126 return SCIP_OKAY;
3127 }
3128
3129 /* delete constraint if z has been fixed */
3130 if( SCIPisRelEQ(scip, zlb, zub) && (SCIPvarIsActive(consdata->x) || canaddcons) )
3131 {
3132 SCIP_RESULT tightenresult;
3133 SCIP_INTERVAL xbnds;
3134 SCIP_INTERVAL zbnds;
3135
3136 SCIPdebugMsg(scip, "z-variable in constraint <%s> is fixed: x = <%s>[%.15g,%.15g], z = <%s>[%.15g,%.15g]\n",
3137 SCIPconsGetName(cons), SCIPvarGetName(consdata->x), xlb, xub, SCIPvarGetName(consdata->z), zlb, zub);
3138
3139 SCIPintervalSetBounds(&zbnds, MIN(zlb, zub), MAX(zlb, zub));
3140 computeBoundsX(scip, cons, zbnds, &xbnds);
3141
3142 /* in difference to the loop above, here we enforce a possible bound tightening on x, and may add a linear cons if x is multiaggregated */
3143 SCIP_CALL( tightenBounds(scip, consdata->x, xbnds, TRUE, cons, &tightenresult, nchgbds, nchgbds, naddconss) );
3144 if( tightenresult == SCIP_CUTOFF )
3145 *cutoff = TRUE;
3146
3147 SCIP_CALL( SCIPdelConsLocal(scip, cons) );
3148
3149 return SCIP_OKAY;
3150 }
3151
3152 return SCIP_OKAY;
3153 }
3154
3155 /** notifies SCIP about a variable bound lhs <= x + c*y <= rhs */
3156 static
addVarbound(SCIP * scip,SCIP_CONS * cons,SCIP_Bool addcons,SCIP_VAR * var,SCIP_VAR * vbdvar,SCIP_Real vbdcoef,SCIP_Real lhs,SCIP_Real rhs,SCIP_Bool * infeas,int * nbdchgs,int * naddconss)3157 SCIP_RETCODE addVarbound(
3158 SCIP* scip, /**< SCIP data structure */
3159 SCIP_CONS* cons, /**< absolute power constraint this variable bound is derived form */
3160 SCIP_Bool addcons, /**< should the variable bound be added as constraint to SCIP? */
3161 SCIP_VAR* var, /**< variable x for which we want to add a variable bound */
3162 SCIP_VAR* vbdvar, /**< variable y which makes the bound a variable bound */
3163 SCIP_Real vbdcoef, /**< coefficient c of bounding variable vbdvar */
3164 SCIP_Real lhs, /**< left hand side of varbound constraint */
3165 SCIP_Real rhs, /**< right hand side of varbound constraint */
3166 SCIP_Bool* infeas, /**< pointer to store whether an infeasibility was detected */
3167 int* nbdchgs, /**< pointer where to add number of performed bound changes */
3168 int* naddconss /**< pointer where to add number of added constraints */
3169 )
3170 {
3171 int nbdchgs_local;
3172
3173 assert(scip != NULL);
3174 assert(cons != NULL);
3175 assert(var != NULL);
3176 assert(vbdvar != NULL);
3177 assert(!SCIPisZero(scip, vbdcoef));
3178 assert(!SCIPisInfinity(scip, ABS(vbdcoef)));
3179 assert(infeas != NULL);
3180
3181 *infeas = FALSE;
3182
3183 /* make sure vbdvar is active, so we can search for it in SCIPvarGetVxbdVars() */
3184 if( !SCIPvarIsActive(vbdvar) )
3185 {
3186 SCIP_Real constant;
3187
3188 constant = 0.0;
3189 SCIP_CALL( SCIPgetProbvarSum(scip, &vbdvar, &vbdcoef, &constant) );
3190 if( !SCIPvarIsActive(vbdvar) || (vbdcoef == 0.0) )
3191 return SCIP_OKAY;
3192
3193 if( !SCIPisInfinity(scip, -lhs) )
3194 lhs -= constant;
3195 if( !SCIPisInfinity(scip, rhs) )
3196 rhs -= constant;
3197 }
3198
3199 /* vbdvar should be a non-fixed binary variable */
3200 assert(SCIPvarIsIntegral(vbdvar));
3201 assert(SCIPisZero(scip, SCIPvarGetLbGlobal(vbdvar)));
3202 assert(SCIPisEQ(scip, SCIPvarGetUbGlobal(vbdvar), 1.0));
3203
3204 SCIPdebugMsg(scip, "-> %g <= <%s> + %g*<%s> <= %g\n", lhs, SCIPvarGetName(var), vbdcoef, SCIPvarGetName(vbdvar), rhs);
3205
3206 if( addcons && SCIPvarGetStatus(var) != SCIP_VARSTATUS_MULTAGGR )
3207 {
3208 SCIP_CONS* vbdcons;
3209 char name[SCIP_MAXSTRLEN];
3210
3211 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s_vbnd", SCIPconsGetName(cons));
3212
3213 SCIP_CALL( SCIPcreateConsVarbound(scip, &vbdcons, name, var, vbdvar, vbdcoef, lhs, rhs,
3214 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE) );
3215 SCIP_CALL( SCIPaddCons(scip, vbdcons) );
3216 SCIP_CALL( SCIPreleaseCons(scip, &vbdcons) );
3217
3218 ++*naddconss;
3219
3220 return SCIP_OKAY;
3221 }
3222
3223 if( !SCIPisInfinity(scip, -lhs) )
3224 {
3225 SCIP_CALL( SCIPaddVarVlb(scip, var, vbdvar, -vbdcoef, lhs, infeas, &nbdchgs_local) );
3226 if( *infeas )
3227 return SCIP_OKAY;
3228 *nbdchgs += nbdchgs_local;
3229 }
3230
3231 if( !SCIPisInfinity(scip, rhs) )
3232 {
3233 SCIP_CALL( SCIPaddVarVub(scip, var, vbdvar, -vbdcoef, rhs, infeas, &nbdchgs_local) );
3234 if( *infeas )
3235 return SCIP_OKAY;
3236 *nbdchgs += nbdchgs_local;
3237 }
3238
3239 return SCIP_OKAY;
3240 }
3241
3242 /** propagates varbounds of variables
3243 * Let f(x) = sign(x+offset)|x+offset|^n, f^{-1}(y) = sign(y)|y|^(1/n) - offset.
3244 * Thus, constraint is lhs <= f(x) + c*z <= rhs.
3245 *
3246 * Given a variable bound constraint x <= a*y + b with y a binary variable, one obtains
3247 * y = 0 -> f(x) <= f(b) -> lhs <= f(b) + c*z
3248 * y = 1 -> f(x) <= f(a+b) -> lhs <= f(a+b) + c*z
3249 * => lhs <= f(b) + y * (f(a+b)-f(b)) + c*z
3250 *
3251 * Given a variable bound constraint x >= a*y + b with y a binary variable, one obtains analogously
3252 * f(b) + y * (f(a+b)-f(b)) + c*z <= rhs
3253 *
3254 * Given a variable bound constraint c*z <= a*y + b with y a binary variable, one obtains
3255 * y = 0 -> lhs <= f(x) + b -> x >= f^{-1}(lhs - b)
3256 * y = 1 -> lhs <= f(x) + a+b -> x >= f^{-1}(lhs - (a+b))
3257 * => x >= f^{-1}(lhs - b) + y * (f^{-1}(lhs - (a+b)) - f^{-1}(lhs - b))
3258 *
3259 * Given a variable bound constraint c*z >= a*y + b with y a binary variable, one obtains analogously
3260 * x <= f^{-1}(rhs - b) + y * (f^{-1}(rhs - (a+b)) - f^{-1}(rhs - b))
3261 */
3262 static
propagateVarbounds(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS * cons,SCIP_Bool * infeas,int * nbdchgs,int * naddconss)3263 SCIP_RETCODE propagateVarbounds(
3264 SCIP* scip, /**< SCIP data structure */
3265 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
3266 SCIP_CONS* cons, /**< absolute power constraint */
3267 SCIP_Bool* infeas, /**< pointer to store whether an infeasibility was detected */
3268 int* nbdchgs, /**< pointer where to add number of performed bound changes */
3269 int* naddconss /**< pointer where to add number of added constraints */
3270 )
3271 {
3272 SCIP_CONSHDLRDATA* conshdlrdata;
3273 SCIP_CONSDATA* consdata;
3274 SCIP_VAR* y;
3275 SCIP_Real a;
3276 SCIP_Real b;
3277 SCIP_Real fb;
3278 SCIP_Real fab;
3279 SCIP_Real vbcoef;
3280 SCIP_Real vbconst;
3281 int i;
3282
3283 assert(scip != NULL);
3284 assert(conshdlr != NULL);
3285 assert(cons != NULL);
3286 assert(infeas != NULL);
3287 assert(nbdchgs != NULL);
3288 assert(naddconss != NULL);
3289
3290 *infeas = FALSE;
3291
3292 conshdlrdata = SCIPconshdlrGetData(conshdlr);
3293 assert(conshdlrdata != NULL);
3294
3295 consdata = SCIPconsGetData(cons);
3296 assert(consdata != NULL);
3297 assert(consdata->z != NULL);
3298
3299 /* don't do anything if it looks like we have numerical troubles */
3300 if( SCIPisZero(scip, consdata->zcoef) )
3301 return SCIP_OKAY;
3302
3303 if( !SCIPisInfinity(scip, -consdata->lhs) )
3304 {
3305 /* propagate varbounds x <= a*y+b onto z
3306 * lhs <= f(b) + y * (f(a+b)-f(b)) + c*z
3307 * -> c*z >= lhs-f(b) + y * (f(b)-f(a+b))
3308 */
3309 for( i = 0; i < SCIPvarGetNVubs(consdata->x); ++i )
3310 {
3311 y = SCIPvarGetVubVars(consdata->x)[i];
3312 a = SCIPvarGetVubCoefs(consdata->x)[i];
3313 b = SCIPvarGetVubConstants(consdata->x)[i];
3314
3315 /* skip variable bound if y is not integer or its valid values are not {0,1}
3316 * @todo extend to arbitrary integer variables
3317 */
3318 if( !SCIPvarIsBinary(y) || SCIPvarGetLbGlobal(y) > 0.5 || SCIPvarGetUbGlobal(y) < 0.5 )
3319 continue;
3320
3321 /* skip variable bound if coefficient is very small */
3322 if( SCIPisFeasZero(scip, consdata->power(a, consdata->exponent)) )
3323 continue;
3324
3325 SCIPdebugMsg(scip, "propagate variable bound <%s> <= %g*<%s> + %g\n", SCIPvarGetName(consdata->x), a, SCIPvarGetName(y), b);
3326
3327 fb = SIGN( b + consdata->xoffset) * consdata->power( b + consdata->xoffset, consdata->exponent); /* f( b) = sign( b) | b|^n */
3328 fab = SIGN(a+b + consdata->xoffset) * consdata->power(a+b + consdata->xoffset, consdata->exponent); /* f(a+b) = sign(a+b) |a+b|^n */
3329
3330 vbcoef = (fb - fab) / consdata->zcoef;
3331 vbconst = (consdata->lhs - fb) / consdata->zcoef;
3332
3333 if( consdata->zcoef > 0.0 )
3334 {
3335 /* add varbound z >= (lhs-f(b))/c + y * (f(b)-f(a+b))/c */
3336 SCIP_CALL( addVarbound(scip, cons, conshdlrdata->addvarboundcons, consdata->z, y, -vbcoef, vbconst, SCIPinfinity(scip), infeas, nbdchgs, naddconss) );
3337 }
3338 else
3339 {
3340 /* add varbound z <= (lhs-f(b))/c + y * (f(b)-f(a+b))/c */
3341 SCIP_CALL( addVarbound(scip, cons, conshdlrdata->addvarboundcons, consdata->z, y, -vbcoef, -SCIPinfinity(scip), vbconst, infeas, nbdchgs, naddconss) );
3342 }
3343 if( *infeas )
3344 return SCIP_OKAY;
3345 }
3346 }
3347
3348 /* propagate varbounds x >= a*y+b onto z
3349 * f(b) + y * (f(a+b)-f(b)) + c*z <= rhs
3350 * -> c*z <= rhs-f(b) + y * (f(b)-f(a+b))
3351 */
3352 if( !SCIPisInfinity(scip, consdata->rhs) )
3353 {
3354 for( i = 0; i < SCIPvarGetNVlbs(consdata->x); ++i )
3355 {
3356 y = SCIPvarGetVlbVars(consdata->x)[i];
3357 a = SCIPvarGetVlbCoefs(consdata->x)[i];
3358 b = SCIPvarGetVlbConstants(consdata->x)[i];
3359
3360 /* skip variable bound if y is not integer or its valid values are not {0,1}
3361 * @todo extend to arbitrary integer variables
3362 */
3363 if( !SCIPvarIsBinary(y) || SCIPvarGetLbGlobal(y) > 0.5 || SCIPvarGetUbGlobal(y) < 0.5 )
3364 continue;
3365
3366 /* skip variable bound if coefficient is very small */
3367 if( SCIPisFeasZero(scip, consdata->power(a, consdata->exponent)) )
3368 continue;
3369
3370 SCIPdebugMsg(scip, "propagate variable bound <%s> >= %g*<%s> + %g\n", SCIPvarGetName(consdata->x), a, SCIPvarGetName(y), b);
3371
3372 fb = SIGN( b + consdata->xoffset) * consdata->power( b + consdata->xoffset, consdata->exponent); /* f( b) = sign( b) | b|^n */
3373 fab = SIGN(a+b + consdata->xoffset) * consdata->power(a+b + consdata->xoffset, consdata->exponent); /* f(a+b) = sign(a+b) |a+b|^n */
3374
3375 vbcoef = (fb - fab) / consdata->zcoef;
3376 vbconst = (consdata->rhs - fb) / consdata->zcoef;
3377
3378 if( consdata->zcoef > 0.0 )
3379 {
3380 /* add varbound z <= (rhs-f(b))/c + y * (f(b)-f(a+b))/c */
3381 SCIP_CALL( addVarbound(scip, cons, conshdlrdata->addvarboundcons, consdata->z, y, -vbcoef, -SCIPinfinity(scip), vbconst, infeas, nbdchgs, naddconss) );
3382 }
3383 else
3384 {
3385 /* add varbound z >= (rhs-f(b))/c + y * (f(b)-f(a+b))/c */
3386 SCIP_CALL( addVarbound(scip, cons, conshdlrdata->addvarboundcons, consdata->z, y, -vbcoef, vbconst, SCIPinfinity(scip), infeas, nbdchgs, naddconss) );
3387 }
3388 if( *infeas )
3389 return SCIP_OKAY;
3390 }
3391 }
3392
3393 /* propagate variable upper bounds on z onto x
3394 * c*z <= a*y+b -> x >= f^{-1}(lhs - b) + y * (f^{-1}(lhs - (a+b)) - f^{-1}(lhs - b))
3395 * c*z >= a*y+b -> x <= f^{-1}(rhs - b) + y * (f^{-1}(rhs - (a+b)) - f^{-1}(rhs - b))
3396 */
3397 if( (consdata->zcoef > 0.0 && !SCIPisInfinity(scip, -consdata->lhs)) ||
3398 ( consdata->zcoef < 0.0 && !SCIPisInfinity(scip, consdata->rhs)) )
3399 for( i = 0; i < SCIPvarGetNVubs(consdata->z); ++i )
3400 {
3401 y = SCIPvarGetVubVars(consdata->z)[i];
3402 a = SCIPvarGetVubCoefs(consdata->z)[i] * consdata->zcoef;
3403 b = SCIPvarGetVubConstants(consdata->z)[i] * consdata->zcoef;
3404
3405 SCIPdebugMsg(scip, "propagate variable bound %g*<%s> %c= %g*<%s> + %g\n", consdata->zcoef, SCIPvarGetName(consdata->z), consdata->zcoef > 0 ? '<' : '>', a, SCIPvarGetName(y), b);
3406
3407 /* skip variable bound if y is not integer or its valid values are not {0,1}
3408 * @todo extend to arbitrary integer variables
3409 */
3410 if( !SCIPvarIsBinary(y) || SCIPvarGetLbGlobal(y) > 0.5 || SCIPvarGetUbGlobal(y) < 0.5 )
3411 continue;
3412
3413 if( consdata->zcoef > 0.0 )
3414 {
3415 fb = consdata->lhs - b;
3416 fb = SIGN(fb) * pow(ABS(fb), 1.0/consdata->exponent);
3417 fab = consdata->lhs - (a+b);
3418 fab = SIGN(fab) * pow(ABS(fab), 1.0/consdata->exponent);
3419 SCIP_CALL( addVarbound(scip, cons, conshdlrdata->addvarboundcons, consdata->x, y, fb - fab, fb - consdata->xoffset, SCIPinfinity(scip), infeas, nbdchgs, naddconss) );
3420 }
3421 else
3422 {
3423 fb = consdata->rhs - b;
3424 fb = SIGN(fb) * pow(ABS(fb), 1.0/consdata->exponent);
3425 fab = consdata->rhs - (a+b);
3426 fab = SIGN(fab) * pow(ABS(fab), 1.0/consdata->exponent);
3427 SCIP_CALL( addVarbound(scip, cons, conshdlrdata->addvarboundcons, consdata->x, y, fb - fab, -SCIPinfinity(scip), fb - consdata->xoffset, infeas, nbdchgs, naddconss) );
3428 }
3429 if( *infeas )
3430 return SCIP_OKAY;
3431 }
3432
3433 /* propagate variable lower bounds on z onto x
3434 * c*z <= a*y+b -> x >= f^{-1}(lhs - b) + y * (f^{-1}(lhs - (a+b)) - f^{-1}(lhs - b))
3435 * c*z >= a*y+b -> x <= f^{-1}(rhs - b) + y * (f^{-1}(rhs - (a+b)) - f^{-1}(rhs - b))
3436 */
3437 if( (consdata->zcoef < 0.0 && !SCIPisInfinity(scip, -consdata->lhs)) ||
3438 ( consdata->zcoef > 0.0 && !SCIPisInfinity(scip, consdata->rhs)) )
3439 for( i = 0; i < SCIPvarGetNVlbs(consdata->z); ++i )
3440 {
3441 y = SCIPvarGetVlbVars(consdata->z)[i];
3442 a = SCIPvarGetVlbCoefs(consdata->z)[i] * consdata->zcoef;
3443 b = SCIPvarGetVlbConstants(consdata->z)[i] * consdata->zcoef;
3444
3445 SCIPdebugMsg(scip, "propagate variable bound %g*<%s> %c= %g*<%s> + %g\n", consdata->zcoef, SCIPvarGetName(consdata->z), consdata->zcoef > 0 ? '>' : '<', a, SCIPvarGetName(y), b);
3446
3447 /* skip variable bound if y is not integer or its valid values are not {0,1}
3448 * @todo extend to arbitrary integer variables
3449 */
3450 if( !SCIPvarIsBinary(y) || SCIPvarGetLbGlobal(y) > 0.5 || SCIPvarGetUbGlobal(y) < 0.5 )
3451 continue;
3452
3453 if( consdata->zcoef > 0.0 )
3454 {
3455 fb = consdata->rhs - b;
3456 fb = SIGN(fb) * pow(ABS(fb), 1.0/consdata->exponent);
3457 fab = consdata->rhs - (a+b);
3458 fab = SIGN(fab) * pow(ABS(fab), 1.0/consdata->exponent);
3459 SCIP_CALL( addVarbound(scip, cons, conshdlrdata->addvarboundcons, consdata->x, y, fb - fab, -SCIPinfinity(scip), fb - consdata->xoffset, infeas, nbdchgs, naddconss) );
3460 }
3461 else
3462 {
3463 fb = consdata->lhs - b;
3464 fb = SIGN(fb) * pow(ABS(fb), 1.0/consdata->exponent);
3465 fab = consdata->lhs - (a+b);
3466 fab = SIGN(fab) * pow(ABS(fab), 1.0/consdata->exponent);
3467 SCIP_CALL( addVarbound(scip, cons, conshdlrdata->addvarboundcons, consdata->x, y, fb - fab, fb - consdata->xoffset, SCIPinfinity(scip), infeas, nbdchgs, naddconss) );
3468 }
3469 if( *infeas )
3470 return SCIP_OKAY;
3471 }
3472
3473 return SCIP_OKAY;
3474 }
3475
3476 /** computes linear underestimator for (x+offset)^n + c*z <= rhs by linearization in x
3477 *
3478 * the generated cut is xmul * n * (refpoint+offset)^(n-1) * x + c*z <= rhs + ((n-1)*refpoint-offset) * (refpoint+offset)^(n-1)
3479 */
3480 static
generateLinearizationCut(SCIP * scip,SCIP_ROWPREP ** rowprep,SCIP_CONSHDLR * conshdlr,SCIP_Real refpoint,SCIP_Real exponent,SCIP_Real xoffset,SCIP_Real xmult,SCIP_Real zcoef,SCIP_Real rhs,SCIP_VAR * x,SCIP_VAR * z,SCIP_Bool islocal)3481 SCIP_RETCODE generateLinearizationCut(
3482 SCIP* scip, /**< SCIP data structure */
3483 SCIP_ROWPREP** rowprep, /**< buffer to store rowprep */
3484 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
3485 SCIP_Real refpoint, /**< base point for linearization */
3486 SCIP_Real exponent, /**< exponent n in sign(x)abs(x)^n */
3487 SCIP_Real xoffset, /**< offset of x */
3488 SCIP_Real xmult, /**< multiplier for coefficient of x */
3489 SCIP_Real zcoef, /**< coefficient of z */
3490 SCIP_Real rhs, /**< right hand side */
3491 SCIP_VAR* x, /**< variable x */
3492 SCIP_VAR* z, /**< variable z */
3493 SCIP_Bool islocal /**< whether the cut is valid only locally */
3494 )
3495 {
3496 SCIP_CONSHDLRDATA* conshdlrdata;
3497 SCIP_Real tmp;
3498
3499 assert(scip != NULL);
3500 assert(rowprep != NULL);
3501 assert(!SCIPisFeasNegative(scip, refpoint+xoffset));
3502 assert(!SCIPisInfinity(scip, refpoint));
3503
3504 conshdlrdata = SCIPconshdlrGetData(conshdlr);
3505 assert(conshdlrdata != NULL);
3506
3507 if( refpoint < -xoffset )
3508 refpoint = -xoffset;
3509
3510 tmp = exponent == 2.0 ? refpoint+xoffset : pow(refpoint+xoffset, exponent-1);
3511 if( SCIPisInfinity(scip, tmp) )
3512 {
3513 SCIPdebugMsg(scip, "skip linearization cut because (refpoint+offset)^(exponent-1) > infinity\n");
3514 *rowprep = NULL;
3515 return SCIP_OKAY;
3516 }
3517
3518 rhs += ((exponent-1)*refpoint-xoffset)*tmp; /* now rhs is the rhs of the cut */
3519 /* do not change the right hand side to a value > infinity (this would trigger an assertion in lp.c) */
3520 if( SCIPisInfinity(scip, rhs) )
3521 {
3522 SCIPdebugMsg(scip, "skip linearization cut because its rhs would be > infinity\n");
3523 *rowprep = NULL;
3524 return SCIP_OKAY;
3525 }
3526
3527 SCIP_CALL( SCIPcreateRowprep(scip, rowprep, SCIP_SIDETYPE_RIGHT, islocal) );
3528 (void) SCIPsnprintf((*rowprep)->name, (int)sizeof((*rowprep)->name), "signpowlinearizecut_%u", ++(conshdlrdata->ncuts));
3529 SCIPaddRowprepSide(*rowprep, rhs);
3530 SCIP_CALL( SCIPaddRowprepTerm(scip, *rowprep, x, xmult*exponent*tmp) );
3531 SCIP_CALL( SCIPaddRowprepTerm(scip, *rowprep, z, zcoef) );
3532
3533 return SCIP_OKAY;
3534 }
3535
3536 /** computes linear underestimator for (x+xoffset)^n + c*z <= rhs by linearization in x
3537 *
3538 * the generated cut is xmul * n * (refpoint+offset)^(n-1) * x + c*z <= rhs + ((n-1)*refpoint-offset) * (refpoint+offset)^(n-1)
3539 * where refpoint is computed by projecting (xref, zref) onto the graph of (x+offset)^n w.r.t. euclidean norm
3540 *
3541 * Thus, the projection is computed by minimizing 1/2(x-xref)^2 + 1/2(((x+offset)^n-rhs)/(-c) - zref)^2.
3542 * I.e., we aim to find a root of
3543 * g(x) = x - xref + n/c (x+offset)^(n-1) (zref - rhs/c) + n/c^2 (x+offset)^(2n-1)
3544 * We do this numerically by executing up to five newton iterations. It is
3545 * g'(x) = 1 + n(n-1)/c (x+offset)^(n-2) (zref - rhs/c) + n(2n-1)/c^2 (x+offset)^(2n-2)
3546 */
3547 static
generateLinearizationCutProject(SCIP * scip,SCIP_ROWPREP ** rowprep,SCIP_CONSHDLR * conshdlr,SCIP_Real xref,SCIP_Real zref,SCIP_Real xmin,SCIP_Real exponent,SCIP_Real xoffset,SCIP_Real xmult,SCIP_Real zcoef,SCIP_Real rhs,SCIP_VAR * x,SCIP_VAR * z,SCIP_Bool islocal)3548 SCIP_RETCODE generateLinearizationCutProject(
3549 SCIP* scip, /**< SCIP data structure */
3550 SCIP_ROWPREP** rowprep, /**< buffer to store rowprep */
3551 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
3552 SCIP_Real xref, /**< reference point for x */
3553 SCIP_Real zref, /**< reference point for z */
3554 SCIP_Real xmin, /**< minimal value x is allowed to take */
3555 SCIP_Real exponent, /**< exponent n in sign(x+offset)abs(x+offset)^n */
3556 SCIP_Real xoffset, /**< offset of x */
3557 SCIP_Real xmult, /**< multiplier for coefficient of x */
3558 SCIP_Real zcoef, /**< coefficient of z */
3559 SCIP_Real rhs, /**< right hand side */
3560 SCIP_VAR* x, /**< variable x */
3561 SCIP_VAR* z, /**< variable z */
3562 SCIP_Bool islocal /**< whether the cut is valid only locally */
3563 )
3564 {
3565 SCIP_Real tmp;
3566 SCIP_Real xproj;
3567 SCIP_Real gval;
3568 SCIP_Real gderiv;
3569 int iter;
3570
3571 assert(scip != NULL);
3572 assert(!SCIPisFeasNegative(scip, xref+xoffset));
3573 assert(!SCIPisInfinity(scip, xref));
3574
3575 if( xref < xmin )
3576 xref = xmin;
3577
3578 xproj = xref;
3579 iter = 0;
3580 if( exponent == 2.0 )
3581 {
3582 do
3583 {
3584 tmp = (xproj+xoffset) * (xproj+xoffset);
3585 gval = xproj - xref + 2*(xproj+xoffset) / zcoef * ((tmp-rhs)/zcoef + zref);
3586 if( !SCIPisFeasPositive(scip, ABS(gval)) )
3587 break;
3588
3589 gderiv = 1 + 6 * tmp / (zcoef*zcoef) + 2 / zcoef * (zref - rhs/zcoef);
3590 xproj -= gval / gderiv;
3591 }
3592 while( ++iter <= 5 );
3593 }
3594 else
3595 {
3596 do
3597 {
3598 tmp = pow(xproj + xoffset, exponent-1);
3599 gval = xproj - xref + exponent / zcoef * (pow(xproj+xoffset, 2*exponent-1)/zcoef + tmp * (zref-rhs/zcoef));
3600 if( !SCIPisFeasPositive(scip, ABS(gval)) )
3601 break;
3602
3603 gderiv = 1 + exponent / zcoef * ( (2*exponent-1)*tmp*tmp/zcoef + (exponent-1)*pow(xproj+xoffset, exponent-2) * (zref-rhs/zcoef) );
3604 xproj -= gval / gderiv;
3605 }
3606 while( ++iter <= 5 );
3607 }
3608
3609 if( xproj < xmin )
3610 xproj = xmin;
3611
3612 SCIP_CALL( generateLinearizationCut(scip, rowprep, conshdlr, xproj, exponent, xoffset, xmult, zcoef, rhs, x, z, islocal) );
3613
3614 return SCIP_OKAY;
3615 }
3616
3617 /** computes secant underestimator for sign(x+offset)abs(x+offset)^n + c*z <= rhs
3618 *
3619 * the generated cut is slope*xmult*x + c*z <= rhs + (-xlb-offset)^n + slope*xlb,
3620 * where slope = (sign(xub+offset)*abs(xub+offset)^n + (-xlb-offset)^n) / (xub - xlb).
3621 *
3622 * the cut is not generated if the given solution (or the LP solution) would not be cutoff
3623 */
3624 static
generateSecantCut(SCIP * scip,SCIP_ROWPREP ** rowprep,SCIP_CONSHDLR * conshdlr,SCIP_SOL * sol,SCIP_Real xlb,SCIP_Real xub,SCIP_Real exponent,SCIP_Real xoffset,DECL_MYPOW ((* mypow)),SCIP_Real xmult,SCIP_Real zcoef,SCIP_Real rhs,SCIP_VAR * x,SCIP_VAR * z)3625 SCIP_RETCODE generateSecantCut(
3626 SCIP* scip, /**< SCIP data structure */
3627 SCIP_ROWPREP** rowprep, /**< buffer to store rowprep */
3628 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
3629 SCIP_SOL* sol, /**< point we want to cut off, or NULL for LP solution */
3630 SCIP_Real xlb, /**< lower bound of x */
3631 SCIP_Real xub, /**< upper bound of x */
3632 SCIP_Real exponent, /**< exponent n in sign(x+offset)abs(x+offset)^n */
3633 SCIP_Real xoffset, /**< offset of x */
3634 DECL_MYPOW ((*mypow)), /**< function to use for computing power */
3635 SCIP_Real xmult, /**< multiplier for coefficient of x */
3636 SCIP_Real zcoef, /**< coefficient of z */
3637 SCIP_Real rhs, /**< right hand side */
3638 SCIP_VAR* x, /**< variable x */
3639 SCIP_VAR* z /**< variable z */
3640 )
3641 {
3642 SCIP_CONSHDLRDATA* conshdlrdata;
3643 SCIP_Real slope, tmp, val;
3644
3645 assert(scip != NULL);
3646 assert(SCIPisLE(scip, xlb, xub));
3647 assert(!SCIPisPositive(scip, xlb+xoffset));
3648
3649 conshdlrdata = SCIPconshdlrGetData(conshdlr);
3650 assert(conshdlrdata != NULL);
3651
3652 /* ignore constraints with fixed x (should be removed soon) */
3653 if( SCIPisRelEQ(scip, xlb, xub) )
3654 {
3655 SCIPdebugMsg(scip, "skip secant cut because <%s> is fixed [%.15g,%.15g]\n", SCIPvarGetName(x), SCIPvarGetLbLocal(x), SCIPvarGetUbLocal(x));
3656 return SCIP_OKAY;
3657 }
3658
3659 if( xlb > -xoffset )
3660 xlb = -xoffset;
3661
3662 tmp = mypow(-xlb-xoffset, exponent);
3663 slope = SIGN(xub+xoffset) * mypow(ABS(xub+xoffset), exponent) + tmp;
3664 slope /= xub - xlb;
3665
3666 /* check if cut would violated solution, check that slope is not above value of infinity */
3667 val = -tmp + slope * (xmult * SCIPgetSolVal(scip, sol, x) - xlb) + zcoef * SCIPgetSolVal(scip, sol, z) - rhs;
3668 if( !SCIPisFeasPositive(scip, val) || SCIPisInfinity(scip, REALABS(slope)) )
3669 {
3670 *rowprep = NULL;
3671 return SCIP_OKAY;
3672 }
3673
3674 SCIP_CALL( SCIPcreateRowprep(scip, rowprep, SCIP_SIDETYPE_RIGHT, SCIPnodeGetDepth(SCIPgetCurrentNode(scip)) > 0 /* local */) );
3675 (void) SCIPsnprintf((*rowprep)->name, SCIP_MAXSTRLEN, "signpowsecantcut_%u", ++(conshdlrdata->nsecantcuts));
3676
3677 SCIP_CALL( SCIPaddRowprepTerm(scip, *rowprep, x, xmult*slope) );
3678 SCIP_CALL( SCIPaddRowprepTerm(scip, *rowprep, z, zcoef) );
3679 SCIPaddRowprepSide(*rowprep, rhs + tmp + slope*xlb);
3680
3681 return SCIP_OKAY;
3682 }
3683
3684 /** computes secant underestimator for sign(x+xoffset)abs(x+xoffset)^n + c*z <= rhs
3685 *
3686 * The generated cut is slope*xmult*x + c*z <= rhs + (-xlb-xoffset)^n + slope*xlb,
3687 * where slope = (sign(xub+xoffset)*abs(xub+xoffset)^n + (-xlb-xoffset)^n) / (xub - xlb).
3688 */
3689 static
generateSecantCutNoCheck(SCIP * scip,SCIP_ROWPREP ** rowprep,SCIP_Real xlb,SCIP_Real xub,SCIP_Real exponent,SCIP_Real xoffset,DECL_MYPOW ((* mypow)),SCIP_Real xmult,SCIP_Real zcoef,SCIP_Real rhs,SCIP_VAR * x,SCIP_VAR * z)3690 SCIP_RETCODE generateSecantCutNoCheck(
3691 SCIP* scip, /**< SCIP data structure */
3692 SCIP_ROWPREP** rowprep, /**< buffer to store rowprep */
3693 SCIP_Real xlb, /**< lower bound of x */
3694 SCIP_Real xub, /**< upper bound of x */
3695 SCIP_Real exponent, /**< exponent n in sign(x)abs(x)^n */
3696 SCIP_Real xoffset, /**< offset of x */
3697 DECL_MYPOW ((*mypow)), /**< function to use for computing power */
3698 SCIP_Real xmult, /**< multiplier for coefficient of x */
3699 SCIP_Real zcoef, /**< coefficient of z */
3700 SCIP_Real rhs, /**< right hand side */
3701 SCIP_VAR* x, /**< variable x */
3702 SCIP_VAR* z /**< variable z */
3703 )
3704 {
3705 SCIP_Real slope, tmp;
3706
3707 assert(scip != NULL);
3708 assert(rowprep != NULL);
3709 assert(SCIPisLE(scip, xlb, xub));
3710 assert(!SCIPisPositive(scip, xlb + xoffset));
3711
3712 /* ignore constraints with fixed x (should be removed soon) */
3713 if( SCIPisRelEQ(scip, xlb, xub) )
3714 return SCIP_OKAY;
3715
3716 if( xlb > -xoffset )
3717 xlb = -xoffset;
3718
3719 tmp = mypow(-xlb-xoffset, exponent);
3720 slope = SIGN(xub+xoffset) * mypow(ABS(xub+xoffset), exponent) + tmp;
3721 slope /= xub - xlb;
3722
3723 if( SCIPisInfinity(scip, REALABS(slope)) )
3724 return SCIP_OKAY;
3725
3726 SCIP_CALL( SCIPcreateRowprep(scip, rowprep, SCIP_SIDETYPE_RIGHT, SCIPnodeGetDepth(SCIPgetCurrentNode(scip)) > 0 /* local */) );
3727 (void)SCIPmemccpy((*rowprep)->name, "signpowcut", '\0', 11);
3728 SCIP_CALL( SCIPaddRowprepTerm(scip, *rowprep, x, xmult*slope) );
3729 SCIP_CALL( SCIPaddRowprepTerm(scip, *rowprep, z, zcoef) );
3730 SCIPaddRowprepSide(*rowprep, rhs + tmp + slope*xlb);
3731
3732 return SCIP_OKAY;
3733 }
3734
3735 /** generates a cut
3736 * based on Liberti and Pantelides, Convex Envelopes of Monomials of Odd Degree, J. Global Optimization 25, 157-168, 2003, and previous publications
3737 */
3738 static
generateCut(SCIP * scip,SCIP_CONS * cons,SCIP_SIDETYPE violside,SCIP_SOL * sol,SCIP_ROW ** row,SCIP_Bool onlyinbounds,SCIP_Real minviol)3739 SCIP_RETCODE generateCut(
3740 SCIP* scip, /**< SCIP data structure */
3741 SCIP_CONS* cons, /**< constraint */
3742 SCIP_SIDETYPE violside, /**< side to separate */
3743 SCIP_SOL* sol, /**< solution to separate, or NULL if LP solution should be used */
3744 SCIP_ROW** row, /**< storage for cut */
3745 SCIP_Bool onlyinbounds, /**< whether linearization is allowed only in variable bounds */
3746 SCIP_Real minviol /**< a minimal violation in sol we hope to achieve */
3747 )
3748 {
3749 SCIP_CONSHDLRDATA* conshdlrdata;
3750 SCIP_CONSDATA* consdata;
3751 SCIP_ROWPREP* rowprep = NULL;
3752 SCIP_Real c;
3753 SCIP_Real xlb;
3754 SCIP_Real xglb;
3755 SCIP_Real xub;
3756 SCIP_Real xval;
3757 SCIP_Real xoffset;
3758 SCIP_Real xmult;
3759 SCIP_Real zcoef;
3760 SCIP_Real rhs;
3761
3762 assert(scip != NULL);
3763 assert(cons != NULL);
3764 assert(row != NULL);
3765
3766 conshdlrdata = SCIPconshdlrGetData(SCIPconsGetHdlr(cons));
3767 assert(conshdlrdata != NULL);
3768
3769 consdata = SCIPconsGetData(cons);
3770 assert(consdata != NULL);
3771
3772 assert(SCIPisGT(scip, violside == SCIP_SIDETYPE_LEFT ? consdata->lhsviol : consdata->rhsviol, SCIPfeastol(scip)));
3773
3774 *row = NULL;
3775
3776 SCIPdebugMsg(scip, "generate cut for constraint <%s> with violated side %d\n", SCIPconsGetName(cons), violside);
3777 SCIPdebugPrintCons(scip, cons, NULL);
3778 SCIPdebugMsg(scip, "xlb = %g xub = %g xval = %g zval = %.15g\n", SCIPvarGetLbLocal(consdata->x), SCIPvarGetUbLocal(consdata->x), SCIPgetSolVal(scip, sol, consdata->x), SCIPgetSolVal(scip, sol, consdata->z));
3779
3780 if( violside == SCIP_SIDETYPE_RIGHT )
3781 {
3782 xglb = SCIPvarGetLbGlobal(consdata->x);
3783 xlb = SCIPvarGetLbLocal(consdata->x);
3784 xub = SCIPvarGetUbLocal(consdata->x);
3785 xval = SCIPgetSolVal(scip, sol, consdata->x);
3786 xoffset = consdata->xoffset;
3787 xmult = 1.0;
3788 zcoef = consdata->zcoef;
3789 rhs = consdata->rhs;
3790 }
3791 else
3792 {
3793 xglb = -SCIPvarGetUbGlobal(consdata->x);
3794 xlb = -SCIPvarGetUbLocal(consdata->x);
3795 xub = -SCIPvarGetLbLocal(consdata->x);
3796 xval = -SCIPgetSolVal(scip, sol, consdata->x);
3797 xoffset = -consdata->xoffset;
3798 xmult = -1.0;
3799 zcoef = -consdata->zcoef;
3800 rhs = -consdata->lhs;
3801 }
3802 /* move reference point onto local domain, if clearly (>eps) outside */
3803 if( SCIPisLT(scip, xval, xlb) )
3804 xval = xlb;
3805 else if( SCIPisGT(scip, xval, xub) )
3806 xval = xub;
3807
3808 if( SCIPisInfinity(scip, REALABS(xval)) )
3809 {
3810 SCIPdebugMsg(scip, "skip separation since x is at infinity\n");
3811 return SCIP_OKAY;
3812 }
3813
3814 if( !SCIPisNegative(scip, xlb+xoffset) )
3815 {
3816 /* [xlb, xub] completely in positive orthant -> function is convex on whole domain */
3817 SCIP_Bool islocal;
3818
3819 islocal = (!SCIPconsIsGlobal(cons) || SCIPisNegative(scip, xglb+xoffset)) && SCIPnodeGetDepth(SCIPgetCurrentNode(scip)) > 0;
3820 if( conshdlrdata->projectrefpoint && !onlyinbounds )
3821 {
3822 SCIP_CALL( generateLinearizationCutProject(scip, &rowprep, SCIPconsGetHdlr(cons), xval, SCIPgetSolVal(scip, sol, consdata->z), -xoffset, consdata->exponent,
3823 xoffset, xmult, zcoef, rhs, consdata->x, consdata->z, islocal) );
3824 }
3825 else if( !onlyinbounds )
3826 {
3827 SCIP_CALL( generateLinearizationCut(scip, &rowprep, SCIPconsGetHdlr(cons), xval, consdata->exponent, xoffset, xmult, zcoef, rhs,
3828 consdata->x, consdata->z, islocal) );
3829 }
3830 else
3831 {
3832 SCIP_CALL( generateLinearizationCut(scip, &rowprep, SCIPconsGetHdlr(cons), 2.0*xval > xlb + xub ? xub : xlb, consdata->exponent, xoffset, xmult, zcoef, rhs,
3833 consdata->x, consdata->z, islocal) );
3834 }
3835 }
3836 else if( !SCIPisPositive(scip, xub+xoffset) )
3837 {
3838 /* [xlb, xub] completely in negative orthant -> function is concave on whole domain */
3839 if( SCIPisInfinity(scip, -xlb) )
3840 return SCIP_OKAY;
3841 SCIP_CALL( generateSecantCut(scip, &rowprep, SCIPconsGetHdlr(cons), sol, xlb, xub, consdata->exponent, xoffset, consdata->power, xmult, zcoef, rhs, consdata->x, consdata->z) );
3842 }
3843 else if( (c = - consdata->root * (xlb+xoffset) - xoffset) > xub )
3844 {
3845 /* c is right of xub -> use secant */
3846 if( SCIPisInfinity(scip, -xlb) || SCIPisInfinity(scip, xub) )
3847 return SCIP_OKAY;
3848 SCIP_CALL( generateSecantCut(scip, &rowprep, SCIPconsGetHdlr(cons), sol, xlb, xub, consdata->exponent, xoffset, consdata->power, xmult, zcoef, rhs, consdata->x, consdata->z) );
3849 }
3850 else if( xval >= c )
3851 {
3852 /* xval is right of c -> use linearization */
3853 if( conshdlrdata->projectrefpoint && !onlyinbounds )
3854 SCIP_CALL( generateLinearizationCutProject(scip, &rowprep, SCIPconsGetHdlr(cons), xval, SCIPgetSolVal(scip, sol, consdata->z), c, consdata->exponent,
3855 xoffset, xmult, zcoef, rhs, consdata->x, consdata->z, SCIPnodeGetDepth(SCIPgetCurrentNode(scip)) > 0) );
3856 else if( !onlyinbounds )
3857 SCIP_CALL( generateLinearizationCut(scip, &rowprep, SCIPconsGetHdlr(cons), xval, consdata->exponent, xoffset, xmult, zcoef, rhs,
3858 consdata->x, consdata->z, xval+xoffset < - consdata->root * (xglb+xoffset) && SCIPnodeGetDepth(SCIPgetCurrentNode(scip)) > 0) );
3859 else
3860 SCIP_CALL( generateLinearizationCut(scip, &rowprep, SCIPconsGetHdlr(cons), xub, consdata->exponent, xoffset, xmult, zcoef, rhs,
3861 consdata->x, consdata->z, xub+xoffset < - consdata->root * (xglb+xoffset) && SCIPnodeGetDepth(SCIPgetCurrentNode(scip)) > 0) );
3862 }
3863 else
3864 {
3865 /* xval between xlb and c -> use secant */
3866 if( SCIPisInfinity(scip, -xlb) || SCIPisInfinity(scip, c) )
3867 return SCIP_OKAY;
3868 SCIP_CALL( generateSecantCut(scip, &rowprep, SCIPconsGetHdlr(cons), sol, xlb, c, consdata->exponent, xoffset, consdata->power, xmult, zcoef, rhs, consdata->x, consdata->z) );
3869 }
3870
3871 /* check and improve numerics */
3872 if( rowprep != NULL )
3873 {
3874 SCIP_Real coefrange;
3875
3876 SCIPdebug( SCIPprintRowprep(scip, rowprep, NULL) );
3877
3878 /* we should not need SCIPmergeRowprep() with only 2 vars in the row */
3879 assert(rowprep->nvars <= 2);
3880
3881 SCIP_CALL( SCIPcleanupRowprep(scip, rowprep, sol, conshdlrdata->cutmaxrange, minviol, &coefrange, NULL) );
3882
3883 if( coefrange >= conshdlrdata->cutmaxrange )
3884 {
3885 SCIPdebugMsg(scip, "skip cut for constraint <%s> because of very large range: %g\n", SCIPconsGetName(cons), coefrange);
3886 }
3887 else if( SCIPisInfinity(scip, REALABS(rowprep->side)) )
3888 {
3889 SCIPdebugMsg(scip, "skip cut for constraint <%s> because of very large side: %g\n", SCIPconsGetName(cons), rowprep->side);
3890 }
3891 else if( rowprep->nvars > 0 && SCIPisInfinity(scip, REALABS(rowprep->coefs[0])) )
3892 {
3893 SCIPdebugMsg(scip, "skip cut for constraint <%s> because of very large coef: %g\n", SCIPconsGetName(cons), rowprep->coefs[0]);
3894 }
3895 else
3896 {
3897 SCIP_CALL( SCIPgetRowprepRowConshdlr(scip, row, rowprep, SCIPconsGetHdlr(cons)) );
3898 }
3899
3900 SCIPfreeRowprep(scip, &rowprep);
3901 }
3902
3903 return SCIP_OKAY;
3904 }
3905
3906 /** tries to separate solution or LP solution by a linear cut
3907 * assumes that constraint violations have been computed
3908 */
3909 static
separatePoint(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS ** conss,int nconss,int nusefulconss,SCIP_SOL * sol,SCIP_Real minefficacy,SCIP_Bool inenforcement,SCIP_Bool onlyinbounds,SCIP_Bool * success,SCIP_Bool * cutoff,SCIP_Real * bestefficacy)3910 SCIP_RETCODE separatePoint(
3911 SCIP* scip, /**< SCIP data structure */
3912 SCIP_CONSHDLR* conshdlr, /**< quadratic constraints handler */
3913 SCIP_CONS** conss, /**< constraints */
3914 int nconss, /**< number of constraints */
3915 int nusefulconss, /**< number of constraints that seem to be useful */
3916 SCIP_SOL* sol, /**< solution to separate, or NULL if LP solution should be used */
3917 SCIP_Real minefficacy, /**< minimal efficacy of a cut if it should be added to the LP */
3918 SCIP_Bool inenforcement, /**< whether we are in constraint enforcement */
3919 SCIP_Bool onlyinbounds, /**< whether linearization is allowed only in variable bounds */
3920 SCIP_Bool* success, /**< result of separation: separated point (TRUE) or not (FALSE) */
3921 SCIP_Bool* cutoff, /**< whether a cutoff has been detected */
3922 SCIP_Real* bestefficacy /**< buffer to store best efficacy of a cut that was added to the LP, if found; or NULL if not of interest */
3923 )
3924 {
3925 SCIP_CONSHDLRDATA* conshdlrdata;
3926 SCIP_CONSDATA* consdata;
3927 SCIP_SIDETYPE side;
3928 SCIP_Real efficacy;
3929 int c;
3930 SCIP_ROW* row;
3931
3932 assert(scip != NULL);
3933 assert(conshdlr != NULL);
3934 assert(conss != NULL || nconss == 0);
3935 assert(success != NULL);
3936 assert(cutoff != NULL);
3937
3938 *success = FALSE;
3939 *cutoff = FALSE;
3940
3941 conshdlrdata = SCIPconshdlrGetData(conshdlr);
3942 assert(conshdlrdata != NULL);
3943
3944 if( bestefficacy != NULL )
3945 *bestefficacy = 0.0;
3946
3947 for( c = 0, side = SCIP_SIDETYPE_LEFT; c < nconss && ! (*cutoff); c = (side == SCIP_SIDETYPE_RIGHT ? c+1 : c), side = (side == SCIP_SIDETYPE_LEFT ? SCIP_SIDETYPE_RIGHT : SCIP_SIDETYPE_LEFT) )
3948 {
3949 assert(conss[c] != NULL); /*lint !e613*/
3950
3951 /* skip constraints that are not enabled, e.g., because they were already marked for deletion at this node */
3952 if( !SCIPconsIsEnabled(conss[c]) ) /*lint !e613*/
3953 continue;
3954
3955 consdata = SCIPconsGetData(conss[c]); /*lint !e613*/
3956 assert(consdata != NULL);
3957
3958 if( SCIPisGT(scip, side == SCIP_SIDETYPE_LEFT ? consdata->lhsviol : consdata->rhsviol, SCIPfeastol(scip)) )
3959 {
3960 /* try to generate a cut */
3961 SCIP_CALL( generateCut(scip, conss[c], side, sol, &row, onlyinbounds, minefficacy) ); /*lint !e613*/
3962 if( row == NULL ) /* failed to generate cut */
3963 continue;
3964
3965 /* if cut is violated sufficiently, then add,
3966 * unless it corresponds to a bound change that is too weak (<eps) to be added
3967 */
3968 efficacy = -SCIPgetRowSolFeasibility(scip, row, sol);
3969 if( SCIPisGT(scip, efficacy, minefficacy) && SCIPisCutApplicable(scip, row) )
3970 {
3971 SCIP_Bool infeasible;
3972
3973 SCIP_CALL( SCIPaddRow(scip, row, FALSE, &infeasible) );
3974 if ( infeasible )
3975 *cutoff = TRUE;
3976 else
3977 *success = TRUE;
3978 if( bestefficacy != NULL && efficacy > *bestefficacy )
3979 *bestefficacy = efficacy;
3980
3981 /* notify indicator constraint handler about this cut */
3982 if( conshdlrdata->conshdlrindicator != NULL && !SCIProwIsLocal(row) )
3983 {
3984 SCIP_CALL( SCIPaddRowIndicator(scip, conshdlrdata->conshdlrindicator, row) );
3985 }
3986
3987 /* mark row as not removable from LP for current node, if in enforcement */
3988 if( inenforcement && !conshdlrdata->enfocutsremovable )
3989 SCIPmarkRowNotRemovableLocal(scip, row);
3990 }
3991
3992 SCIP_CALL( SCIPreleaseRow (scip, &row) );
3993 }
3994
3995 /* enforce only useful constraints
3996 * others are only checked and enforced if we are still feasible or have not found a separating cut yet
3997 */
3998 if( c >= nusefulconss && *success )
3999 break;
4000 }
4001
4002 return SCIP_OKAY;
4003 }
4004
4005 /** adds linearizations cuts for convex constraints w.r.t. a given reference point to cutpool and sepastore
4006 * if separatedlpsol is not NULL, then a cut that separates the LP solution is added to the sepastore and is forced to enter the LP
4007 * if separatedlpsol is not NULL, but cut does not separate the LP solution, then it is added to the cutpool only
4008 * if separatedlpsol is NULL, then cut is added to cutpool only
4009 */
4010 static
addLinearizationCuts(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS ** conss,int nconss,SCIP_SOL * ref,SCIP_Bool * separatedlpsol,SCIP_Real minefficacy)4011 SCIP_RETCODE addLinearizationCuts(
4012 SCIP* scip, /**< SCIP data structure */
4013 SCIP_CONSHDLR* conshdlr, /**< quadratic constraints handler */
4014 SCIP_CONS** conss, /**< constraints */
4015 int nconss, /**< number of constraints */
4016 SCIP_SOL* ref, /**< reference point where to linearize, or NULL for LP solution */
4017 SCIP_Bool* separatedlpsol, /**< buffer to store whether a cut that separates the current LP solution was found and added to LP, or NULL if adding to cutpool only */
4018 SCIP_Real minefficacy /**< minimal efficacy of a cut when checking for separation of LP solution */
4019 )
4020 {
4021 SCIP_CONSDATA* consdata;
4022 SCIP_Bool addedtolp;
4023 SCIP_ROW* row;
4024 int c;
4025
4026 assert(scip != NULL);
4027 assert(conshdlr != NULL);
4028 assert(conss != NULL || nconss == 0);
4029
4030 if( separatedlpsol != NULL )
4031 *separatedlpsol = FALSE;
4032
4033 for( c = 0; c < nconss; ++c )
4034 {
4035 assert(conss[c] != NULL); /*lint !e613*/
4036
4037 if( SCIPconsIsLocal(conss[c]) ) /*lint !e613*/
4038 continue;
4039
4040 consdata = SCIPconsGetData(conss[c]); /*lint !e613*/
4041 assert(consdata != NULL);
4042
4043 if( !SCIPisGT(scip, SCIPvarGetUbGlobal(consdata->x), -consdata->xoffset) && !SCIPisInfinity(scip, -consdata->lhs) )
4044 {
4045 /* constraint function is concave for x+offset <= 0.0, so can linearize w.r.t. lhs */
4046 consdata->lhsviol = 1.0;
4047 consdata->rhsviol = 0.0;
4048 SCIP_CALL( generateCut(scip, conss[c], SCIP_SIDETYPE_LEFT, ref, &row, FALSE, minefficacy) ); /*lint !e613*/
4049 }
4050 else if( !SCIPisLT(scip, SCIPvarGetLbGlobal(consdata->x), -consdata->xoffset) && !SCIPisInfinity(scip, -consdata->rhs) )
4051 {
4052 /* constraint function is convex for x+offset >= 0.0, so can linearize w.r.t. rhs */
4053 consdata->lhsviol = 0.0;
4054 consdata->rhsviol = 1.0;
4055 SCIP_CALL( generateCut(scip, conss[c], SCIP_SIDETYPE_RIGHT, ref, &row, FALSE, minefficacy) ); /*lint !e613*/
4056 }
4057 else
4058 {
4059 /* sign not fixed or nothing to linearize */
4060 continue;
4061 }
4062
4063 if( row == NULL )
4064 continue;
4065
4066 addedtolp = FALSE;
4067
4068 /* if caller wants, then check if cut separates LP solution and add to sepastore if so */
4069 if( separatedlpsol != NULL )
4070 {
4071 if( -SCIPgetRowLPFeasibility(scip, row) >= minefficacy )
4072 {
4073 SCIP_Bool infeasible;
4074
4075 *separatedlpsol = TRUE;
4076 addedtolp = TRUE;
4077 SCIP_CALL( SCIPaddRow(scip, row, TRUE, &infeasible) );
4078 assert( ! infeasible );
4079 }
4080 }
4081
4082 if( !addedtolp && !SCIProwIsLocal(row) )
4083 {
4084 SCIP_CALL( SCIPaddPoolCut(scip, row) );
4085 }
4086
4087 SCIP_CALL( SCIPreleaseRow(scip, &row) );
4088 }
4089
4090 return SCIP_OKAY;
4091 }
4092
4093 /** processes the event that a new primal solution has been found */
4094 static
SCIP_DECL_EVENTEXEC(processNewSolutionEvent)4095 SCIP_DECL_EVENTEXEC(processNewSolutionEvent)
4096 {
4097 SCIP_CONSHDLRDATA* conshdlrdata;
4098 SCIP_CONSHDLR* conshdlr;
4099 SCIP_CONS** conss;
4100 int nconss;
4101 SCIP_SOL* sol;
4102
4103 assert(scip != NULL);
4104 assert(event != NULL);
4105 assert(eventdata != NULL);
4106 assert(eventhdlr != NULL);
4107
4108 assert((SCIPeventGetType(event) & SCIP_EVENTTYPE_SOLFOUND) != 0);
4109
4110 conshdlr = (SCIP_CONSHDLR*)eventdata;
4111
4112 nconss = SCIPconshdlrGetNConss(conshdlr);
4113
4114 if( nconss == 0 )
4115 return SCIP_OKAY;
4116
4117 sol = SCIPeventGetSol(event);
4118 assert(sol != NULL);
4119
4120 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4121 assert(conshdlrdata != NULL);
4122
4123 /* we are only interested in solution coming from some heuristic other than trysol, but not from the tree
4124 * the reason for ignoring trysol solutions is that they may come from an NLP solve in sepalp, where we already added linearizations,
4125 * or are from the tree, but postprocessed via proposeFeasibleSolution
4126 */
4127 if( SCIPsolGetHeur(sol) == NULL || SCIPsolGetHeur(sol) == conshdlrdata->trysolheur )
4128 return SCIP_OKAY;
4129
4130 conss = SCIPconshdlrGetConss(conshdlr);
4131 assert(conss != NULL);
4132
4133 SCIPdebugMsg(scip, "catched new sol event %" SCIP_EVENTTYPE_FORMAT " from heur <%s>; have %d conss\n", SCIPeventGetType(event), SCIPheurGetName(SCIPsolGetHeur(sol)), nconss);
4134
4135 SCIP_CALL( addLinearizationCuts(scip, conshdlr, conss, nconss, sol, NULL, 0.0) );
4136
4137 return SCIP_OKAY;
4138 }
4139
4140 /** given a solution, try to make absolute power constraints feasible by shifting the linear variable z and pass this solution to the trysol heuristic */
4141 static
proposeFeasibleSolution(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS ** conss,int nconss,SCIP_SOL * sol)4142 SCIP_RETCODE proposeFeasibleSolution(
4143 SCIP* scip, /**< SCIP data structure */
4144 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
4145 SCIP_CONS** conss, /**< constraints to process */
4146 int nconss, /**< number of constraints */
4147 SCIP_SOL* sol /**< solution to process */
4148 )
4149 {
4150 SCIP_CONSDATA* consdata;
4151 SCIP_SOL* newsol;
4152 SCIP_Real xtermval;
4153 SCIP_Real zval;
4154 SCIP_Real viol;
4155 SCIP_Bool solviolbounds;
4156 int c;
4157
4158 assert(scip != NULL);
4159 assert(conshdlr != NULL);
4160 assert(conss != NULL || nconss == 0);
4161
4162 /* don't propose new solutions if not in presolve or solving */
4163 if( SCIPgetStage(scip) < SCIP_STAGE_INITPRESOLVE || SCIPgetStage(scip) >= SCIP_STAGE_SOLVED )
4164 return SCIP_OKAY;
4165
4166 if( sol != NULL )
4167 {
4168 SCIP_CALL( SCIPcreateSolCopy(scip, &newsol, sol) );
4169 }
4170 else
4171 {
4172 SCIP_CALL( SCIPcreateLPSol(scip, &newsol, NULL) );
4173 }
4174 SCIP_CALL( SCIPunlinkSol(scip, newsol) );
4175
4176 for( c = 0; c < nconss; ++c )
4177 {
4178 consdata = SCIPconsGetData(conss[c]); /*lint !e613*/
4179 assert(consdata != NULL);
4180 assert(consdata->z != NULL);
4181 assert(consdata->zcoef != 0.0);
4182
4183 /* recompute violation w.r.t. current solution */
4184 SCIP_CALL( computeViolation(scip, conshdlr, conss[c], newsol, &viol, &solviolbounds) ); /*lint !e613*/
4185 assert(!solviolbounds);
4186
4187 /* do nothing if constraint is satisfied */
4188 if( !SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) && !SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
4189 continue;
4190
4191 /* if violation is at infinity, give up */
4192 if( SCIPisInfinity(scip, MAX(consdata->lhsviol, consdata->rhsviol)) )
4193 break;
4194
4195 /* @todo could also adjust x while keeping z fixed */
4196
4197 /* if variable is multiaggregated, then cannot set its solution value, so give up */
4198 if( SCIPvarGetStatus(consdata->z) == SCIP_VARSTATUS_MULTAGGR )
4199 break;
4200
4201 /* compute value of x-term */
4202 xtermval = SCIPgetSolVal(scip, newsol, consdata->x);
4203 xtermval += consdata->xoffset;
4204 xtermval = SIGN(xtermval) * consdata->power(ABS(xtermval), consdata->exponent);
4205
4206 /* if left hand side is violated, try to set z such that lhs is active */
4207 if( SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) )
4208 {
4209 assert(!SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip))); /* should only have one side violated (otherwise some variable is at infinity) */
4210
4211 zval = (consdata->lhs - xtermval)/consdata->zcoef;
4212 /* bad luck: z would get value outside of its domain */
4213 if( SCIPisInfinity(scip, REALABS(zval)) || SCIPisFeasLT(scip, zval, SCIPvarGetLbGlobal(consdata->z)) || SCIPisFeasGT(scip, zval, SCIPvarGetUbGlobal(consdata->z)) )
4214 break;
4215 SCIP_CALL( SCIPsetSolVal(scip, newsol, consdata->z, zval) );
4216 }
4217
4218 /* if right hand side is violated, try to set z such that rhs is active */
4219 if( SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
4220 {
4221 zval = (consdata->rhs - xtermval)/consdata->zcoef;
4222 /* bad luck: z would get value outside of its domain */
4223 if( SCIPisInfinity(scip, REALABS(zval)) || SCIPisFeasLT(scip, zval, SCIPvarGetLbGlobal(consdata->z)) || SCIPisFeasGT(scip, zval, SCIPvarGetUbGlobal(consdata->z)) )
4224 break;
4225 SCIP_CALL( SCIPsetSolVal(scip, newsol, consdata->z, zval) );
4226 }
4227 }
4228
4229 /* if we have a solution that should satisfy all absolute power constraints and has a better objective than the current upper bound, then pass it to the trysol heuristic */
4230 if( c == nconss )
4231 {
4232 SCIP_CONSHDLRDATA* conshdlrdata;
4233
4234 SCIPdebugMsg(scip, "pass solution with objective val %g to trysol heuristic\n", SCIPgetSolTransObj(scip, newsol));
4235
4236 conshdlrdata = SCIPconshdlrGetData(conshdlr);
4237 assert(conshdlrdata != NULL);
4238 assert(conshdlrdata->trysolheur != NULL);
4239
4240 SCIP_CALL( SCIPheurPassSolTrySol(scip, conshdlrdata->trysolheur, newsol) );
4241 }
4242
4243 SCIP_CALL( SCIPfreeSol(scip, &newsol) );
4244
4245 return SCIP_OKAY;
4246 }
4247
4248 /** create a nonlinear row representation of the constraint and stores them in consdata */
4249 static
createNlRow(SCIP * scip,SCIP_CONS * cons)4250 SCIP_RETCODE createNlRow(
4251 SCIP* scip, /**< SCIP data structure */
4252 SCIP_CONS* cons /**< absolute power constraint */
4253 )
4254 {
4255 SCIP_CONSDATA* consdata;
4256 SCIP_EXPRTREE* exprtree;
4257 SCIP_EXPRCURV curv;
4258 SCIP_QUADELEM quadelem;
4259 SCIP_VAR* linvars[2];
4260 SCIP_Real lincoefs[2];
4261 SCIP_VAR* quadvar;
4262 SCIP_Real constant;
4263 SCIP_Bool expisint;
4264 int sign;
4265 int nlinvars;
4266 int nquadvars;
4267 int nquadelems;
4268 int n;
4269
4270 assert(scip != NULL);
4271 assert(cons != NULL);
4272
4273 consdata = SCIPconsGetData(cons);
4274 assert(consdata != NULL);
4275
4276 if( consdata->nlrow != NULL )
4277 {
4278 SCIP_CALL( SCIPreleaseNlRow(scip, &consdata->nlrow) );
4279 }
4280
4281 nlinvars = 0;
4282 nquadvars = 0;
4283 nquadelems = 0;
4284 exprtree = NULL;
4285 constant = 0.0;
4286
4287 /* check if sign of x is fixed, determine curvature of abspower function */
4288 if( !SCIPisNegative(scip, SCIPvarGetLbGlobal(consdata->x)+consdata->xoffset) )
4289 {
4290 sign = 1;
4291 curv = SCIP_EXPRCURV_CONVEX;
4292 }
4293 else if( !SCIPisPositive(scip, SCIPvarGetUbGlobal(consdata->x)+consdata->xoffset) )
4294 {
4295 sign = -1;
4296 curv = SCIP_EXPRCURV_CONCAVE;
4297 }
4298 else
4299 {
4300 sign = 0;
4301 curv = SCIP_EXPRCURV_UNKNOWN;
4302 }
4303
4304 /* check if exponent is integral */
4305 expisint = SCIPisIntegral(scip, consdata->exponent);
4306 n = (int)SCIPround(scip, consdata->exponent);
4307
4308 /* create quadelem or expression tree for nonlinear part sign(x+offset)abs(x+offset)^n */
4309 if( sign != 0 || (expisint && (n % 2 == 1)) )
4310 {
4311 /* sign is fixes or exponent is odd integer */
4312 if( expisint && n == 2 )
4313 {
4314 /* sign of x is clear and exponent is 2.0 -> generate quadratic, linear, and constant term for +/- (x+offset)^n */
4315 assert(sign == -1 || sign == 1);
4316 nquadelems = 1;
4317 quadelem.idx1 = 0;
4318 quadelem.idx2 = 0;
4319 quadelem.coef = (SCIP_Real)sign;
4320 nquadvars = 1;
4321 quadvar = consdata->x;
4322
4323 if( consdata->xoffset != 0.0 )
4324 {
4325 linvars[0] = consdata->x;
4326 lincoefs[0] = sign * 2.0 * consdata->xoffset;
4327 nlinvars = 1;
4328 constant = sign * consdata->xoffset * consdata->xoffset;
4329 }
4330 }
4331 else
4332 {
4333 /* exponent is odd or sign of x is clear, generate expression tree for +/- (+/-(x+offset))^exponent */
4334 SCIP_EXPR* expr;
4335 SCIP_EXPR* expr2;
4336
4337 SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_VARIDX, 0) ); /* x */
4338 if( consdata->xoffset != 0.0 )
4339 {
4340 SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr2, SCIP_EXPR_CONST, consdata->xoffset) );
4341 SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_PLUS, expr, expr2) ); /* x + offset */
4342 }
4343 if( sign == -1 && !expisint )
4344 {
4345 /* if exponent is not integer and x is negative, then negate */
4346 SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr2, SCIP_EXPR_CONST, -1.0) ); /* -1 */
4347 SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_MUL, expr, expr2) ); /* -(x+offset) */
4348 }
4349 /* use intpower for integer exponent and realpower for fractional exponent */
4350 if( expisint )
4351 {
4352 SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_INTPOWER, expr, n) ); /* (x+offset)^n */
4353 }
4354 else
4355 {
4356 assert(sign == 1 || sign == -1);
4357 SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_REALPOWER, expr, consdata->exponent) ); /* abs(x+offset)^exponent */
4358 }
4359 /* if exponent is even integer, then negate result; if it's an odd integer, then intpower already takes care of correct sign */
4360 if( sign == -1 && !(expisint && n % 2 == 1) )
4361 {
4362 SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr2, SCIP_EXPR_CONST, -1.0) ); /* -1 */
4363 SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_MUL, expr, expr2) ); /* -abs(x+offset)^exponent */
4364 }
4365 SCIP_CALL( SCIPexprtreeCreate(SCIPblkmem(scip), &exprtree, expr, 1, 0, NULL) );
4366 }
4367 }
4368 else
4369 {
4370 /* exponent is not odd integer and sign of x is not fixed -> generate expression tree for signpower(x+offset, n) */
4371 SCIP_EXPR* expr;
4372 SCIP_EXPR* expr2;
4373
4374 SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_VARIDX, 0) ); /* x */
4375 if( consdata->xoffset != 0.0 )
4376 {
4377 SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr2, SCIP_EXPR_CONST, consdata->xoffset) );
4378 SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_PLUS, expr, expr2) ); /* x + offset */
4379 }
4380 SCIP_CALL( SCIPexprCreate(SCIPblkmem(scip), &expr, SCIP_EXPR_SIGNPOWER, expr, (SCIP_Real)consdata->exponent) ); /* signpower(x+offset, n) */
4381
4382 SCIP_CALL( SCIPexprtreeCreate(SCIPblkmem(scip), &exprtree, expr, 1, 0, NULL) );
4383 }
4384 assert(exprtree != NULL || nquadelems > 0);
4385
4386 /* tell expression tree, which is its variable */
4387 if( exprtree != NULL )
4388 {
4389 SCIP_CALL( SCIPexprtreeSetVars(exprtree, 1, &consdata->x) );
4390 }
4391
4392 assert(nlinvars < 2);
4393 linvars[nlinvars] = consdata->z;
4394 lincoefs[nlinvars] = consdata->zcoef;
4395 ++nlinvars;
4396
4397 /* create nlrow */
4398 SCIP_CALL( SCIPcreateNlRow(scip, &consdata->nlrow, SCIPconsGetName(cons), constant,
4399 nlinvars, linvars, lincoefs,
4400 nquadvars, &quadvar, nquadelems, &quadelem,
4401 exprtree, consdata->lhs, consdata->rhs,
4402 curv
4403 ) );
4404
4405 if( exprtree != NULL )
4406 {
4407 SCIP_CALL( SCIPexprtreeFree(&exprtree) );
4408 }
4409
4410 return SCIP_OKAY;
4411 }
4412
4413 /** upgrades a quadratic constraint where the quadratic part is only a single square term and the quadratic variable sign is fixed to a signpower constraint */
4414 static
SCIP_DECL_QUADCONSUPGD(quadconsUpgdAbspower)4415 SCIP_DECL_QUADCONSUPGD(quadconsUpgdAbspower)
4416 { /*lint --e{715}*/
4417 SCIP_QUADVARTERM quadvarterm;
4418 SCIP_VAR* x;
4419 SCIP_VAR* z;
4420 SCIP_Real xoffset;
4421 SCIP_Real zcoef;
4422 SCIP_Real signpowcoef;
4423 SCIP_Real lhs;
4424 SCIP_Real rhs;
4425
4426 *nupgdconss = 0;
4427
4428 /* need at least one linear variable */
4429 if( SCIPgetNLinearVarsQuadratic(scip, cons) == 0 )
4430 return SCIP_OKAY;
4431
4432 /* consider only quadratic constraints with a single square term */
4433 if( SCIPgetNQuadVarTermsQuadratic(scip, cons) != 1 )
4434 return SCIP_OKAY;
4435 assert(SCIPgetNBilinTermsQuadratic(scip, cons) == 0);
4436
4437 quadvarterm = SCIPgetQuadVarTermsQuadratic(scip, cons)[0];
4438 if( SCIPisZero(scip, quadvarterm.sqrcoef) )
4439 return SCIP_OKAY;
4440
4441 /* don't upgrade if upgrade would scale the constraint down (divide by |sqrcoef|)
4442 * @todo we could still allow this if we were keeping the scaling factor around for the feasibility check
4443 */
4444 if( REALABS(quadvarterm.sqrcoef) > 1.0 )
4445 return SCIP_OKAY;
4446
4447 x = quadvarterm.var;
4448 xoffset = quadvarterm.lincoef / (2.0 * quadvarterm.sqrcoef);
4449
4450 /* check that x has fixed sign */
4451 if( SCIPisNegative(scip, SCIPvarGetLbGlobal(x) + xoffset) && SCIPisPositive(scip, SCIPvarGetUbGlobal(x) + xoffset) )
4452 return SCIP_OKAY;
4453
4454 /* check whether upgdconss array has enough space to store 1 or 2 constraints */
4455 if( SCIPgetNLinearVarsQuadratic(scip, cons) > 1 )
4456 *nupgdconss = -2;
4457 else
4458 *nupgdconss = -1;
4459 if( -*nupgdconss > upgdconsssize )
4460 return SCIP_OKAY;
4461
4462 *nupgdconss = 0;
4463
4464 SCIPdebugMsg(scip, "upgrade quadratic constraint <%s> to absolute power, x = [%g,%g], offset = %g\n", SCIPconsGetName(cons), SCIPvarGetLbGlobal(x), SCIPvarGetUbGlobal(x), xoffset);
4465 SCIPdebugPrintCons(scip, cons, NULL);
4466
4467 lhs = SCIPgetLhsQuadratic(scip, cons);
4468 rhs = SCIPgetRhsQuadratic(scip, cons);
4469
4470 /* get z and its coefficient */
4471 if( SCIPgetNLinearVarsQuadratic(scip, cons) > 1 )
4472 {
4473 /* create auxiliary variable and constraint for linear part, since we can handle only at most one variable in cons_signpower */
4474 char name[SCIP_MAXSTRLEN];
4475 SCIP_VAR* auxvar;
4476
4477 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s_linpart", SCIPconsGetName(cons));
4478 SCIP_CALL( SCIPcreateVar(scip, &auxvar, name, -SCIPinfinity(scip), SCIPinfinity(scip), 0.0, SCIP_VARTYPE_CONTINUOUS,
4479 SCIPconsIsInitial(cons), SCIPconsIsRemovable(cons), NULL, NULL, NULL, NULL, NULL) );
4480 SCIP_CALL( SCIPaddVar(scip, auxvar) );
4481
4482 SCIP_CALL( SCIPcreateConsLinear(scip, &upgdconss[0], name, SCIPgetNLinearVarsQuadratic(scip, cons),
4483 SCIPgetLinearVarsQuadratic(scip, cons), SCIPgetCoefsLinearVarsQuadratic(scip, cons),
4484 SCIPisInfinity(scip, -lhs) ? -SCIPinfinity(scip) : 0.0,
4485 SCIPisInfinity(scip, rhs) ? SCIPinfinity(scip) : 0.0,
4486 SCIPconsIsInitial(cons), SCIPconsIsSeparated(cons), TRUE,
4487 TRUE, SCIPconsIsPropagated(cons), SCIPconsIsLocal(cons),
4488 SCIPconsIsModifiable(cons), SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons),
4489 SCIPconsIsStickingAtNode(cons)) );
4490 SCIP_CALL( SCIPaddCoefLinear(scip, upgdconss[*nupgdconss], auxvar, -1.0) );
4491
4492 z = auxvar;
4493 zcoef = 1.0;
4494
4495 ++*nupgdconss;
4496
4497 /* compute and set value of auxvar in debug solution */
4498 #ifdef WITH_DEBUG_SOLUTION
4499 if( SCIPdebugIsMainscip(scip) )
4500 {
4501 SCIP_Real debugval;
4502 SCIP_Real debugvarval;
4503 int i;
4504
4505 debugval = 0.0;
4506 for( i = 0; i < SCIPgetNLinearVarsQuadratic(scip, cons); ++i )
4507 {
4508 SCIP_CALL( SCIPdebugGetSolVal(scip, SCIPgetLinearVarsQuadratic(scip, cons)[i], &debugvarval) );
4509 debugval += SCIPgetCoefsLinearVarsQuadratic(scip, cons)[i] * debugvarval;
4510 }
4511
4512 SCIP_CALL( SCIPdebugAddSolVal(scip, auxvar, debugval) );
4513 }
4514 #endif
4515
4516 SCIP_CALL( SCIPreleaseVar(scip, &auxvar) );
4517 }
4518 else
4519 {
4520 assert(SCIPgetNLinearVarsQuadratic(scip, cons) == 1);
4521 z = SCIPgetLinearVarsQuadratic(scip, cons)[0];
4522 zcoef = SCIPgetCoefsLinearVarsQuadratic(scip, cons)[0];
4523 }
4524
4525 /* we now have lhs <= sqrcoef * (x + offset)^2 - sqrcoef * offset^2 + zcoef * z <= rhs */
4526
4527 /* move sqrcoef * offset^2 into lhs and rhs */
4528 if( !SCIPisInfinity(scip, -lhs) )
4529 lhs += quadvarterm.sqrcoef * xoffset * xoffset;
4530 if( !SCIPisInfinity(scip, rhs) )
4531 rhs += quadvarterm.sqrcoef * xoffset * xoffset;
4532
4533 /* divide by sqrcoef if x+offset > 0 and by -sqrcoef if < 0 */
4534 signpowcoef = quadvarterm.sqrcoef;
4535 if( SCIPisNegative(scip, SCIPvarGetLbGlobal(x) + xoffset) )
4536 signpowcoef = -signpowcoef;
4537 if( signpowcoef > 0.0 )
4538 {
4539 if( !SCIPisInfinity(scip, -lhs) )
4540 lhs /= signpowcoef;
4541 if( !SCIPisInfinity(scip, rhs) )
4542 rhs /= signpowcoef;
4543 }
4544 else
4545 {
4546 SCIP_Real newrhs;
4547
4548 if( !SCIPisInfinity(scip, -lhs) )
4549 newrhs = lhs / signpowcoef;
4550 else
4551 newrhs = SCIPinfinity(scip);
4552 if( !SCIPisInfinity(scip, rhs) )
4553 lhs = rhs / signpowcoef;
4554 else
4555 lhs = -SCIPinfinity(scip);
4556 rhs = newrhs;
4557 }
4558 zcoef /= signpowcoef;
4559
4560 /* create the absolute power constraint */
4561 SCIP_CALL( SCIPcreateConsAbspower(scip, &upgdconss[*nupgdconss], SCIPconsGetName(cons), x, z, 2.0,
4562 xoffset, zcoef, lhs, rhs,
4563 SCIPconsIsInitial(cons), SCIPconsIsSeparated(cons), SCIPconsIsEnforced(cons),
4564 SCIPconsIsChecked(cons), SCIPconsIsPropagated(cons), SCIPconsIsLocal(cons),
4565 SCIPconsIsModifiable(cons), SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons),
4566 SCIPconsIsStickingAtNode(cons)) );
4567 SCIPdebugPrintCons(scip, upgdconss[*nupgdconss], NULL);
4568 ++*nupgdconss;
4569
4570 return SCIP_OKAY;
4571 }
4572
4573 /** tries to upgrade a nonlinear constraint into a absolute power constraint */
4574 static
SCIP_DECL_NONLINCONSUPGD(nonlinconsUpgdAbspower)4575 SCIP_DECL_NONLINCONSUPGD(nonlinconsUpgdAbspower)
4576 {
4577 SCIP_EXPRGRAPH* exprgraph;
4578 SCIP_EXPRGRAPHNODE* node;
4579 SCIP_EXPRGRAPHNODE* child;
4580 SCIP_Real exponent;
4581 SCIP_VAR* x;
4582 SCIP_VAR* z;
4583 SCIP_Real signpowcoef;
4584 SCIP_Real zcoef;
4585 SCIP_Real xoffset;
4586 SCIP_Real constant;
4587 SCIP_Real lhs;
4588 SCIP_Real rhs;
4589
4590 assert(nupgdconss != NULL);
4591 assert(upgdconss != NULL);
4592
4593 *nupgdconss = 0;
4594
4595 /* absolute power needs at least one linear variable (constraint is trivial, otherwise) */
4596 if( SCIPgetNLinearVarsNonlinear(scip, cons) == 0 )
4597 return SCIP_OKAY;
4598
4599 node = SCIPgetExprgraphNodeNonlinear(scip, cons);
4600
4601 /* no interest in linear constraints */
4602 if( node == NULL )
4603 return SCIP_OKAY;
4604
4605 /* need exactly one argument */
4606 if( SCIPexprgraphGetNodeNChildren(node) != 1 )
4607 return SCIP_OKAY;
4608
4609 constant = 0.0;
4610 signpowcoef = 1.0; /* coefficient of sign(x)abs(x)^n term, to be reformulated away... */
4611
4612 child = SCIPexprgraphGetNodeChildren(node)[0];
4613
4614 /* check if node expression fits to absolute power constraint */
4615 switch( SCIPexprgraphGetNodeOperator(node) )
4616 {
4617 case SCIP_EXPR_REALPOWER:
4618 /* realpower with exponent > 1.0 can always be signpower, since it assumes that argument is >= 0.0 */
4619 exponent = SCIPexprgraphGetNodeRealPowerExponent(node);
4620 if( exponent <= 1.0 )
4621 return SCIP_OKAY;
4622
4623 assert(SCIPexprgraphGetNodeBounds(child).inf >= 0.0);
4624 break;
4625
4626 case SCIP_EXPR_INTPOWER:
4627 {
4628 /* check if exponent > 1.0 and either odd or even with child having fixed sign */
4629 SCIP_INTERVAL childbounds;
4630
4631 exponent = (SCIP_Real)SCIPexprgraphGetNodeIntPowerExponent(node);
4632 if( exponent <= 1.0 )
4633 return SCIP_OKAY;
4634
4635 childbounds = SCIPexprgraphGetNodeBounds(child);
4636 if( (int)exponent % 2 == 0 && childbounds.inf < 0.0 && childbounds.sup > 0.0 )
4637 return SCIP_OKAY;
4638
4639 /* use x^exponent = -sign(x) |x|^exponent if exponent is even and x always negative */
4640 if( (int)exponent % 2 == 0 && childbounds.inf < 0.0 )
4641 signpowcoef = -1.0;
4642
4643 break;
4644 }
4645
4646 case SCIP_EXPR_SQUARE:
4647 {
4648 /* check if child has fixed sign */
4649 SCIP_INTERVAL childbounds;
4650
4651 childbounds = SCIPexprgraphGetNodeBounds(child);
4652 if( childbounds.inf < 0.0 && childbounds.sup > 0.0 )
4653 return SCIP_OKAY;
4654
4655 /* use x^2 = -sign(x) |x|^2 if x is always negative */
4656 if( childbounds.inf < 0.0 )
4657 signpowcoef = -1.0;
4658
4659 exponent = 2.0;
4660 break;
4661 }
4662
4663 case SCIP_EXPR_SIGNPOWER:
4664 /* check if exponent > 1.0 */
4665 exponent = SCIPexprgraphGetNodeSignPowerExponent(node);
4666 if( exponent <= 1.0 )
4667 return SCIP_OKAY;
4668 break;
4669
4670 case SCIP_EXPR_POLYNOMIAL:
4671 {
4672 SCIP_EXPRDATA_MONOMIAL* monomial;
4673 SCIP_INTERVAL childbounds;
4674
4675 /* check if only one univariate monomial with exponent > 1.0 */
4676
4677 /* if sum of univariate monomials, then this should have been taken care of by exprgraphnodeReformSignpower */
4678 if( SCIPexprgraphGetNodePolynomialNMonomials(node) > 1 )
4679 return SCIP_OKAY;
4680 assert(SCIPexprgraphGetNodePolynomialNMonomials(node) == 1); /* assume simplified, i.e., no constant polynomial */
4681
4682 monomial = SCIPexprgraphGetNodePolynomialMonomials(node)[0];
4683 assert(SCIPexprGetMonomialNFactors(monomial) == 1); /* since we have only one children and assume simplified */
4684
4685 exponent = SCIPexprGetMonomialExponents(monomial)[0];
4686 if( exponent <= 1.0 )
4687 return SCIP_OKAY;
4688
4689 /* if exponent is even integer and child has mixed sign, then cannot do
4690 * if exponent is even integer and child is always negative, then can do via multiplication by -1.0 */
4691 childbounds = SCIPexprgraphGetNodeBounds(child);
4692 if( SCIPisIntegral(scip, exponent) && ((int)SCIPround(scip, exponent) % 2 == 0) && childbounds.inf < 0.0 )
4693 {
4694 if( childbounds.sup > 0.0 )
4695 return SCIP_OKAY;
4696 signpowcoef = -1.0;
4697 }
4698
4699 constant = SCIPexprgraphGetNodePolynomialConstant(node);
4700 signpowcoef *= SCIPexprGetMonomialCoef(monomial);
4701
4702 break;
4703 }
4704
4705 default:
4706 return SCIP_OKAY;
4707 } /*lint !e788*/
4708 assert(SCIPexprgraphGetNodeNChildren(node) == 1);
4709
4710 /* check magnitue of coefficient of z in signpower constraint */
4711 zcoef = 1.0;
4712 if( SCIPgetNLinearVarsNonlinear(scip, cons) == 1 )
4713 zcoef = SCIPgetLinearCoefsNonlinear(scip, cons)[0];
4714 zcoef /= signpowcoef;
4715 if( SCIPexprgraphGetNodeOperator(child) == SCIP_EXPR_LINEAR && SCIPexprgraphGetNodeNChildren(child) == 1 )
4716 {
4717 zcoef /= pow(REALABS(SCIPexprgraphGetNodeLinearCoefs(child)[0]), exponent);
4718 }
4719 if( SCIPisZero(scip, zcoef) )
4720 {
4721 SCIPdebugMsg(scip, "skip upgrade to signpower since |zcoef| = %g would be zero\n", zcoef);
4722 return SCIP_OKAY;
4723 }
4724
4725 /* count how many constraints we need to add (use negative numbers, for convenience):
4726 * one constraint for absolute power,
4727 * plus one if we need to replace the linear part by single variable,
4728 * plus one if we need to replace the argument of absolute power by a single variable
4729 */
4730 *nupgdconss = -1;
4731
4732 if( SCIPexprgraphGetNodeOperator(child) != SCIP_EXPR_VARIDX && (SCIPexprgraphGetNodeOperator(child) != SCIP_EXPR_LINEAR || SCIPexprgraphGetNodeNChildren(child) > 1) )
4733 {
4734 /* if node has known curvature and we would add auxiliary var for child, then don't upgrade
4735 * it's not really necessary, but may introduce more numerical troubles
4736 * @todo maybe still do if child is linear?
4737 */
4738 if( SCIPexprgraphGetNodeCurvature(node) != SCIP_EXPRCURV_UNKNOWN )
4739 {
4740 *nupgdconss = 0;
4741 return SCIP_OKAY;
4742 }
4743
4744 --*nupgdconss;
4745 }
4746
4747 if( SCIPgetNLinearVarsNonlinear(scip, cons) > 1 )
4748 --*nupgdconss;
4749
4750 /* request larger upgdconss array */
4751 if( upgdconsssize < -*nupgdconss )
4752 return SCIP_OKAY;
4753
4754 SCIPdebugMsg(scip, "upgrading constraint <%s>\n", SCIPconsGetName(cons));
4755
4756 /* start counting at zero again */
4757 *nupgdconss = 0;
4758
4759 exprgraph = SCIPgetExprgraphNonlinear(scip, SCIPconsGetHdlr(cons));
4760
4761 lhs = SCIPgetLhsNonlinear(scip, cons);
4762 rhs = SCIPgetRhsNonlinear(scip, cons);
4763
4764 /* get x and it's offset */
4765 if( SCIPexprgraphGetNodeOperator(child) == SCIP_EXPR_VARIDX )
4766 {
4767 x = (SCIP_VAR*)SCIPexprgraphGetNodeVar(exprgraph, child);
4768 xoffset = 0.0;
4769 }
4770 else if( SCIPexprgraphGetNodeOperator(child) == SCIP_EXPR_LINEAR && SCIPexprgraphGetNodeNChildren(child) == 1 )
4771 {
4772 SCIP_Real xcoef;
4773
4774 x = (SCIP_VAR*)SCIPexprgraphGetNodeVar(exprgraph, SCIPexprgraphGetNodeChildren(child)[0]);
4775 xcoef = SCIPexprgraphGetNodeLinearCoefs(child)[0];
4776 assert(!SCIPisZero(scip, xcoef));
4777
4778 signpowcoef *= (xcoef < 0.0 ? -1.0 : 1.0) * pow(REALABS(xcoef), exponent);
4779 xoffset = SCIPexprgraphGetNodeLinearConstant(child) / xcoef;
4780 }
4781 else
4782 {
4783 /* reformulate by adding auxiliary variable and constraint for child */
4784 char name[SCIP_MAXSTRLEN];
4785 SCIP_INTERVAL bounds;
4786 SCIP_VAR* auxvar;
4787 SCIP_Real minusone;
4788
4789 bounds = SCIPexprgraphGetNodeBounds(child);
4790 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s_powerarg", SCIPconsGetName(cons));
4791
4792 SCIPdebugMsg(scip, "add auxiliary variable and constraint %s for node %p(%d,%d)\n", name, (void*)child, SCIPexprgraphGetNodeDepth(child), SCIPexprgraphGetNodePosition(child));
4793
4794 SCIP_CALL( SCIPcreateVar(scip, &auxvar, name, SCIPintervalGetInf(bounds), SCIPintervalGetSup(bounds), 0.0,
4795 SCIP_VARTYPE_CONTINUOUS, TRUE, TRUE, NULL, NULL, NULL, NULL, NULL) );
4796 SCIP_CALL( SCIPaddVar(scip, auxvar) );
4797
4798 /* create new constraint child == auxvar
4799 * since signpower is monotonic, we need only child <= auxvar or child >= auxvar, if not both sides are finite, and depending on signpowcoef
4800 * i.e., we need child - auxvar <= 0.0 if rhs is finite and signpowcoef > 0.0 or lhs is finite and signpowcoef < 0.0
4801 * and we need 0.0 <= child - auxvar if lhs is finite and signpowcoef > 0.0 or rhs is finite and signpowcoef < 0.0
4802 */
4803 minusone = -1.0;
4804 assert(upgdconsssize > *nupgdconss);
4805 SCIP_CALL( SCIPcreateConsNonlinear2(scip, &upgdconss[*nupgdconss], name, 1, &auxvar, &minusone, child,
4806 ((signpowcoef > 0.0 && !SCIPisInfinity(scip, -lhs)) || (signpowcoef < 0.0 && !SCIPisInfinity(scip, rhs))) ? 0.0 : -SCIPinfinity(scip),
4807 ((signpowcoef > 0.0 && !SCIPisInfinity(scip, rhs)) || (signpowcoef < 0.0 && !SCIPisInfinity(scip, -lhs))) ? 0.0 : SCIPinfinity(scip),
4808 TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
4809 ++*nupgdconss;
4810
4811 /* use auxvar to setup absolute power constraint */
4812 x = auxvar;
4813 xoffset = 0.0;
4814
4815 /* compute and set value of auxvar in debug solution, if debugging is enabled */
4816 SCIP_CALL( SCIPdebugAddSolVal(scip, auxvar, SCIPexprgraphGetNodeVal(child)) ); /*lint !e506 !e774*/
4817
4818 SCIP_CALL( SCIPreleaseVar(scip, &auxvar) );
4819 }
4820
4821 /* get z and its coefficient */
4822 if( SCIPgetNLinearVarsNonlinear(scip, cons) > 1 )
4823 {
4824 /* create auxiliary variable and constraint for linear part, since we can handle only at most one variable in cons_signpower */
4825 char name[SCIP_MAXSTRLEN];
4826 SCIP_VAR* auxvar;
4827
4828 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s_linpart", SCIPconsGetName(cons));
4829 SCIP_CALL( SCIPcreateVar(scip, &auxvar, name, -SCIPinfinity(scip), SCIPinfinity(scip), 0.0, SCIP_VARTYPE_CONTINUOUS,
4830 SCIPconsIsInitial(cons), SCIPconsIsRemovable(cons), NULL, NULL, NULL, NULL, NULL) );
4831 SCIP_CALL( SCIPaddVar(scip, auxvar) );
4832
4833 assert(upgdconsssize > *nupgdconss);
4834 SCIP_CALL( SCIPcreateConsLinear(scip, &upgdconss[*nupgdconss], name, SCIPgetNLinearVarsNonlinear(scip, cons),
4835 SCIPgetLinearVarsNonlinear(scip, cons), SCIPgetLinearCoefsNonlinear(scip, cons),
4836 SCIPisInfinity(scip, -lhs) ? -SCIPinfinity(scip) : 0.0,
4837 SCIPisInfinity(scip, rhs) ? SCIPinfinity(scip) : 0.0,
4838 SCIPconsIsInitial(cons), SCIPconsIsSeparated(cons), TRUE,
4839 TRUE, SCIPconsIsPropagated(cons), SCIPconsIsLocal(cons),
4840 SCIPconsIsModifiable(cons), SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons),
4841 SCIPconsIsStickingAtNode(cons)) );
4842 SCIP_CALL( SCIPaddCoefLinear(scip, upgdconss[*nupgdconss], auxvar, -1.0) );
4843
4844 z = auxvar;
4845 zcoef = 1.0;
4846
4847 ++*nupgdconss;
4848
4849 /* compute and set value of auxvar in debug solution */
4850 #ifdef WITH_DEBUG_SOLUTION
4851 if( SCIPdebugIsMainscip(scip) )
4852 {
4853 SCIP_Real debugval;
4854 SCIP_Real debugvarval;
4855 int i;
4856
4857 debugval = 0.0;
4858 for( i = 0; i < SCIPgetNLinearVarsNonlinear(scip, cons); ++i )
4859 {
4860 SCIP_CALL( SCIPdebugGetSolVal(scip, SCIPgetLinearVarsNonlinear(scip, cons)[i], &debugvarval) );
4861 debugval += SCIPgetLinearCoefsNonlinear(scip, cons)[i] * debugvarval;
4862 }
4863
4864 SCIP_CALL( SCIPdebugAddSolVal(scip, auxvar, debugval) );
4865 }
4866 #endif
4867
4868 SCIP_CALL( SCIPreleaseVar(scip, &auxvar) );
4869 }
4870 else
4871 {
4872 assert(SCIPgetNLinearVarsNonlinear(scip, cons) == 1);
4873 z = SCIPgetLinearVarsNonlinear(scip, cons)[0];
4874 zcoef = SCIPgetLinearCoefsNonlinear(scip, cons)[0];
4875 }
4876
4877 if( constant != 0.0 )
4878 {
4879 if( !SCIPisInfinity(scip, -lhs) )
4880 lhs -= constant;
4881 if( !SCIPisInfinity(scip, rhs) )
4882 rhs -= constant;
4883 }
4884
4885 /* divide absolute power constraint by signpowcoef */
4886 if( signpowcoef != 1.0 )
4887 {
4888 zcoef /= signpowcoef;
4889 if( signpowcoef < 0.0 )
4890 {
4891 SCIP_Real newrhs;
4892
4893 newrhs = SCIPisInfinity(scip, -lhs) ? SCIPinfinity(scip) : lhs/signpowcoef;
4894 lhs = SCIPisInfinity(scip, rhs) ? -SCIPinfinity(scip) : rhs/signpowcoef;
4895 rhs = newrhs;
4896 }
4897 else
4898 {
4899 if( !SCIPisInfinity(scip, -lhs) )
4900 lhs /= signpowcoef;
4901 if( !SCIPisInfinity(scip, rhs) )
4902 rhs /= signpowcoef;
4903 }
4904 }
4905
4906 /* finally setup a absolute power constraint */
4907
4908 assert(*nupgdconss < upgdconsssize);
4909 SCIP_CALL( SCIPcreateConsAbspower(scip, &upgdconss[*nupgdconss], SCIPconsGetName(cons),
4910 x, z, exponent, xoffset, zcoef, lhs, rhs,
4911 SCIPconsIsInitial(cons), SCIPconsIsSeparated(cons), SCIPconsIsEnforced(cons),
4912 SCIPconsIsChecked(cons), SCIPconsIsPropagated(cons), SCIPconsIsLocal(cons),
4913 SCIPconsIsModifiable(cons), SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons),
4914 SCIPconsIsStickingAtNode(cons)) );
4915 ++*nupgdconss;
4916
4917 return SCIP_OKAY;
4918 }
4919
4920 /** tries to reformulate a expression graph node via introducing a absolute power constraint
4921 * if node fits to absolute power and has indefinte curvature and has no nonlinear parents and has siblings, then replace by auxvar and absolute power constraint
4922 * if it still has nonlinear parents, then we wait to see if reformulation code move node into auxiliary constraint,
4923 * so we do not add unnessary auxiliary variables for something like an x^2 in an exp(x^2)
4924 * if it has no siblings, then we let the upgrading for nonlinear constraints take care of it,
4925 * since it may be able to upgrade the constraint as a whole and can take the constraint sides into account too (may need only <=/>= auxcons)
4926 */
4927 static
SCIP_DECL_EXPRGRAPHNODEREFORM(exprgraphnodeReformAbspower)4928 SCIP_DECL_EXPRGRAPHNODEREFORM(exprgraphnodeReformAbspower)
4929 {
4930 SCIP_EXPRGRAPHNODE* child;
4931 char name[SCIP_MAXSTRLEN];
4932 SCIP_CONS* cons;
4933 SCIP_Real exponent;
4934 SCIP_VAR* x;
4935 SCIP_VAR* z;
4936 SCIP_Real signpowcoef;
4937 SCIP_Real xoffset;
4938 SCIP_Real constant;
4939
4940 assert(scip != NULL);
4941 assert(exprgraph != NULL);
4942 assert(node != NULL);
4943 assert(naddcons != NULL);
4944 assert(reformnode != NULL);
4945
4946 *reformnode = NULL;
4947
4948 if( SCIPexprgraphGetNodeCurvature(node) != SCIP_EXPRCURV_UNKNOWN )
4949 return SCIP_OKAY;
4950
4951 constant = 0.0;
4952 signpowcoef = 1.0; /* coefficient of sign(x)abs(x)^n term, to be move in from of z... */
4953
4954 /* check if node expression fits to absolute power constraint */
4955 switch( SCIPexprgraphGetNodeOperator(node) )
4956 {
4957 case SCIP_EXPR_REALPOWER:
4958 /* realpower with exponent > 1.0 can always be absolute power, since it assumes that argument is >= 0.0
4959 * @todo we should also ensure that argument is >= 0.0
4960 */
4961 exponent = SCIPexprgraphGetNodeRealPowerExponent(node);
4962 if( exponent <= 1.0 )
4963 return SCIP_OKAY;
4964
4965 child = SCIPexprgraphGetNodeChildren(node)[0];
4966 assert(SCIPexprgraphGetNodeBounds(child).inf >= 0.0);
4967 break;
4968
4969 case SCIP_EXPR_INTPOWER:
4970 {
4971 /* check if exponent > 1.0 and either odd or even with child having fixed sign */
4972 SCIP_INTERVAL childbounds;
4973
4974 exponent = (SCIP_Real)SCIPexprgraphGetNodeIntPowerExponent(node);
4975 if( exponent <= 1.0 )
4976 return SCIP_OKAY;
4977
4978 child = SCIPexprgraphGetNodeChildren(node)[0];
4979 childbounds = SCIPexprgraphGetNodeBounds(child);
4980 if( (int)exponent % 2 == 0 && childbounds.inf < 0.0 && childbounds.sup > 0.0 )
4981 return SCIP_OKAY;
4982
4983 /* use x^exponent = -sign(x) |x|^exponent if exponent is even and x always negative */
4984 if( (int)exponent % 2 == 0 && childbounds.inf < 0.0 )
4985 signpowcoef = -1.0;
4986
4987 break;
4988 }
4989
4990 case SCIP_EXPR_SQUARE:
4991 {
4992 /* check if child has fixed sign */
4993 SCIP_INTERVAL childbounds;
4994
4995 child = SCIPexprgraphGetNodeChildren(node)[0];
4996 childbounds = SCIPexprgraphGetNodeBounds(child);
4997 if( childbounds.inf < 0.0 && childbounds.sup > 0.0 )
4998 return SCIP_OKAY;
4999
5000 /* use x^2 = -sign(x) |x|^2 if x is always negative */
5001 if( childbounds.inf < 0.0 )
5002 signpowcoef = -1.0;
5003
5004 exponent = 2.0;
5005 break;
5006 }
5007
5008 case SCIP_EXPR_SIGNPOWER:
5009 /* check if exponent > 1.0 */
5010 exponent = SCIPexprgraphGetNodeSignPowerExponent(node);
5011 if( exponent <= 1.0 )
5012 return SCIP_OKAY;
5013 child = SCIPexprgraphGetNodeChildren(node)[0];
5014 break;
5015
5016 case SCIP_EXPR_POLYNOMIAL:
5017 {
5018 SCIP_EXPRDATA_MONOMIAL* monomial;
5019 SCIP_INTERVAL childbounds;
5020
5021 /* check if only one monomial */
5022 if( SCIPexprgraphGetNodePolynomialNMonomials(node) > 1 )
5023 return SCIP_OKAY;
5024 assert(SCIPexprgraphGetNodePolynomialNMonomials(node) == 1); /* assume simplified, i.e., no constant polynomial */
5025
5026 monomial = SCIPexprgraphGetNodePolynomialMonomials(node)[0];
5027
5028 assert(SCIPexprgraphGetNodeNChildren(node) >= 1);
5029 if( SCIPexprgraphGetNodeNChildren(node) == 1 )
5030 {
5031 /* handle univariate case with exponent > 1.0 */
5032 assert(SCIPexprGetMonomialNFactors(monomial) == 1); /* since we have only one child and assume simplified */
5033
5034 exponent = SCIPexprGetMonomialExponents(monomial)[0];
5035 if( exponent <= 1.0 )
5036 return SCIP_OKAY;
5037
5038 /* if exponent is even integer and child has mixed sign, then cannot do
5039 * if exponent is even integer and child is always negative, then can do via multiplication by -1.0 */
5040 child = SCIPexprgraphGetNodeChildren(node)[0];
5041 childbounds = SCIPexprgraphGetNodeBounds(child);
5042 if( SCIPisIntegral(scip, exponent) && ((int)SCIPround(scip, exponent) % 2 == 0) && childbounds.inf < 0.0 )
5043 {
5044 if( childbounds.sup > 0.0 )
5045 return SCIP_OKAY;
5046 signpowcoef = -1.0;
5047 }
5048
5049 constant = SCIPexprgraphGetNodePolynomialConstant(node);
5050 signpowcoef *= SCIPexprGetMonomialCoef(monomial);
5051 }
5052 else if( SCIPexprgraphGetNodeNChildren(node) == 2 )
5053 {
5054 /* look for abs(x)^p * x or x * abs(x)^p */
5055 SCIP_EXPRGRAPHNODE* child1;
5056 SCIP_EXPRGRAPHNODE* child2;
5057
5058 assert(SCIPexprGetMonomialNFactors(monomial) == 2); /* since we have two children and assume simplified */
5059
5060 child1 = SCIPexprgraphGetNodeChildren(node)[SCIPexprGetMonomialChildIndices(monomial)[0]];
5061 child2 = SCIPexprgraphGetNodeChildren(node)[SCIPexprGetMonomialChildIndices(monomial)[1]];
5062
5063 if( SCIPexprgraphGetNodeOperator(child1) == SCIP_EXPR_ABS && SCIPexprgraphGetNodeChildren(child1)[0] == child2 )
5064 {
5065 /* have abs(x)^p1 * x^p2; need p1 > 0, p2 = 1 */
5066 if( SCIPexprGetMonomialExponents(monomial)[0] <= 0.0 || SCIPexprGetMonomialExponents(monomial)[1] != 1.0 )
5067 return SCIP_OKAY;
5068 exponent = 1.0 + SCIPexprGetMonomialExponents(monomial)[0];
5069 child = child2;
5070 }
5071 else if( SCIPexprgraphGetNodeOperator(child2) == SCIP_EXPR_ABS && SCIPexprgraphGetNodeChildren(child2)[0] == child1 )
5072 {
5073 /* have x^p1 * abs(x)^p2; need p1 = 1, p2 > 0 */
5074 if( SCIPexprGetMonomialExponents(monomial)[0] != 1.0 || SCIPexprGetMonomialExponents(monomial)[1] <= 0.0 )
5075 return SCIP_OKAY;
5076 exponent = 1.0 + SCIPexprGetMonomialExponents(monomial)[1];
5077 child = child1;
5078 }
5079 else
5080 return SCIP_OKAY;
5081
5082 constant = SCIPexprgraphGetNodePolynomialConstant(node);
5083 signpowcoef = SCIPexprGetMonomialCoef(monomial);
5084 }
5085 else
5086 return SCIP_OKAY;
5087
5088 break;
5089 }
5090
5091 case SCIP_EXPR_MUL:
5092 {
5093 /* look for abs(x) * x and x * abs(x) */
5094 SCIP_EXPRGRAPHNODE* child1;
5095 SCIP_EXPRGRAPHNODE* child2;
5096
5097 child1 = SCIPexprgraphGetNodeChildren(node)[0];
5098 child2 = SCIPexprgraphGetNodeChildren(node)[1];
5099
5100 if( SCIPexprgraphGetNodeNChildren(child1) == 1 && SCIPexprgraphGetNodeOperator(child1) == SCIP_EXPR_ABS && SCIPexprgraphGetNodeChildren(child1)[0] == child2 )
5101 {
5102 /* abs(x) * x */
5103 exponent = 2.0;
5104 child = child2;
5105 break;
5106 }
5107
5108 if( SCIPexprgraphGetNodeNChildren(child2) == 1 && SCIPexprgraphGetNodeOperator(child2) == SCIP_EXPR_ABS && SCIPexprgraphGetNodeChildren(child2)[0] == child1 )
5109 {
5110 /* x * abs(x) */
5111 exponent = 2.0;
5112 child = child1;
5113 break;
5114 }
5115
5116 /* the cases below should not happening, because node will be of polynomial type after simplifty */
5117 #ifdef SCIP_DISABLED_CODE
5118 if( SCIPexprgraphGetNodeNChildren(child1) == 1 && (SCIPexprgraphGetNodeOperator(child1) == SCIP_EXPR_INTPOWER || SCIPexprgraphGetNodeOperator(child1) == SCIP_EXPR_REALPOWER) &&
5119 SCIPexprgraphGetNodeOperator(SCIPexprgraphGetNodeChildren(child1)[0]) == SCIP_EXPR_ABS && SCIPexprgraphGetNodeChildren(SCIPexprgraphGetNodeChildren(child1)[0])[0] == child2 )
5120 {
5121 /* abs(x)^p * x */
5122 exponent = 1.0 + SCIPexprgraphGetNodeOperator(child1) == SCIP_EXPR_INTPOWER ? SCIPexprgraphGetNodeIntPowerExponent(child1) : SCIPexprgraphGetNodeRealPowerExponent(child1);
5123 child = child2;
5124 break;
5125 }
5126
5127 if( SCIPexprgraphGetNodeNChildren(child2) == 1 && (SCIPexprgraphGetNodeOperator(child2) == SCIP_EXPR_INTPOWER || SCIPexprgraphGetNodeOperator(child2) == SCIP_EXPR_REALPOWER) &&
5128 SCIPexprgraphGetNodeOperator(SCIPexprgraphGetNodeChildren(child2)[0]) == SCIP_EXPR_ABS && SCIPexprgraphGetNodeChildren(SCIPexprgraphGetNodeChildren(child2)[0])[0] == child1 )
5129 {
5130 /* x * abs(x)^p */
5131 exponent = 1.0 + SCIPexprgraphGetNodeOperator(child2) == SCIP_EXPR_INTPOWER ? SCIPexprgraphGetNodeIntPowerExponent(child2) : SCIPexprgraphGetNodeRealPowerExponent(child2);
5132 child = child1;
5133 break;
5134 }
5135 #endif
5136 return SCIP_OKAY;
5137 }
5138
5139 default:
5140 return SCIP_OKAY;
5141 } /*lint !e788*/
5142
5143 if( SCIPexprgraphHasNodeNonlinearAncestor(node) )
5144 return SCIP_OKAY;
5145 if( SCIPexprgraphGetNodeNChildren(node) == 1 && !SCIPexprgraphHasNodeSibling(node) ) /* why did I require siblings??? */
5146 return SCIP_OKAY;
5147
5148 SCIPdebugMsg(scip, "reformulate node %p via signpower\n", (void*)node);
5149
5150 /* get x and its offset */
5151 if( SCIPexprgraphGetNodeOperator(child) == SCIP_EXPR_VARIDX )
5152 {
5153 x = (SCIP_VAR*)SCIPexprgraphGetNodeVar(exprgraph, child);
5154 xoffset = 0.0;
5155 }
5156 else if( SCIPexprgraphGetNodeOperator(child) == SCIP_EXPR_LINEAR && SCIPexprgraphGetNodeNChildren(child) == 1 )
5157 {
5158 SCIP_Real xcoef;
5159
5160 x = (SCIP_VAR*)SCIPexprgraphGetNodeVar(exprgraph, SCIPexprgraphGetNodeChildren(child)[0]);
5161 xcoef = SCIPexprgraphGetNodeLinearCoefs(child)[0];
5162 assert(!SCIPisZero(scip, xcoef));
5163
5164 signpowcoef *= (xcoef < 0.0 ? -1.0 : 1.0) * pow(REALABS(xcoef), exponent);
5165 xoffset = SCIPexprgraphGetNodeLinearConstant(child) / xcoef;
5166 }
5167 else
5168 {
5169 /* reformulate by adding auxiliary variable and constraint for child */
5170 SCIP_INTERVAL bounds;
5171 SCIP_VAR* auxvar;
5172 SCIP_Real minusone;
5173
5174 bounds = SCIPexprgraphGetNodeBounds(child);
5175 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "nlreform%dsp", *naddcons);
5176
5177 SCIPdebugMsg(scip, "add auxiliary variable and constraint %s for node %p(%d,%d)\n", name, (void*)child, SCIPexprgraphGetNodeDepth(child), SCIPexprgraphGetNodePosition(child));
5178
5179 SCIP_CALL( SCIPcreateVar(scip, &auxvar, name, SCIPintervalGetInf(bounds), SCIPintervalGetSup(bounds), 0.0, SCIP_VARTYPE_CONTINUOUS,
5180 TRUE, TRUE, NULL, NULL, NULL, NULL, NULL) );
5181 SCIP_CALL( SCIPaddVar(scip, auxvar) );
5182
5183 /* create new constraint child == auxvar */
5184 minusone = -1.0;
5185 SCIP_CALL( SCIPcreateConsNonlinear2(scip, &cons, name, 1, &auxvar, &minusone, child, 0.0, 0.0,
5186 TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
5187 SCIP_CALL( SCIPaddCons(scip, cons) );
5188 ++*naddcons;
5189
5190 /* use auxvar to setup signpower constraint */
5191 x = auxvar;
5192 xoffset = 0.0;
5193
5194 SCIP_CALL( SCIPdebugAddSolVal(scip, auxvar, SCIPexprgraphGetNodeVal(child)) ); /*lint !e506 !e774*/
5195
5196 SCIP_CALL( SCIPreleaseCons(scip, &cons) );
5197 SCIP_CALL( SCIPreleaseVar(scip, &auxvar) );
5198 }
5199
5200 /* create auxiliary variable z and add to expression graph */
5201 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "nlreform%dsp", *naddcons);
5202 SCIP_CALL( SCIPcreateVar(scip, &z, name, -SCIPinfinity(scip), SCIPinfinity(scip), 0.0, SCIP_VARTYPE_CONTINUOUS,
5203 TRUE, TRUE, NULL, NULL, NULL, NULL, NULL) );
5204 SCIP_CALL( SCIPaddVar(scip, z) );
5205 SCIP_CALL( SCIPexprgraphAddVars(exprgraph, 1, (void**)&z, reformnode) );
5206
5207 /* setup a absolute power constraint */
5208 if( REALABS(signpowcoef) * SCIPfeastol(scip) < 1.0 )
5209 {
5210 /* if signpowcoef is not huge (<10^6), then put it into absolute power constraint */
5211 SCIP_CALL( SCIPcreateConsAbspower(scip, &cons, name,
5212 x, z, exponent, xoffset, -1.0/signpowcoef, -constant/signpowcoef, -constant/signpowcoef,
5213 TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
5214 SCIP_CALL( SCIPaddCons(scip, cons) );
5215 SCIPdebugPrintCons(scip, cons, NULL);
5216 ++*naddcons;
5217
5218 /* compute value of z and reformnode and set in debug solution and expression graph, resp. */
5219 #ifdef WITH_DEBUG_SOLUTION
5220 if( SCIPdebugIsMainscip(scip) )
5221 {
5222 SCIP_Real xval;
5223 SCIP_Real zval;
5224
5225 SCIP_CALL( SCIPdebugGetSolVal(scip, x, &xval) );
5226 zval = signpowcoef * SIGN(xval + xoffset) * pow(REALABS(xval + xoffset), exponent) + constant;
5227
5228 SCIP_CALL( SCIPdebugAddSolVal(scip, z, zval) );
5229 SCIPexprgraphSetVarNodeValue(*reformnode, zval);
5230 }
5231 #endif
5232 }
5233 else
5234 {
5235 /* if signpowcoef is huge, then avoid very small coefficient of z
5236 * instead create additional node on top of current reformnode */
5237 SCIP_EXPRGRAPHNODE* linnode;
5238
5239 SCIP_CALL( SCIPcreateConsAbspower(scip, &cons, name,
5240 x, z, exponent, xoffset, -1.0, 0.0, 0.0,
5241 TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
5242 SCIP_CALL( SCIPaddCons(scip, cons) );
5243 SCIPdebugPrintCons(scip, cons, NULL);
5244 ++*naddcons;
5245
5246 /* compute value of z and reformnode and set in debug solution and expression graph, resp. */
5247 #ifdef WITH_DEBUG_SOLUTION
5248 if( SCIPdebugIsMainscip(scip) )
5249 {
5250 SCIP_Real xval;
5251 SCIP_Real zval;
5252
5253 SCIP_CALL( SCIPdebugGetSolVal(scip, x, &xval) );
5254 zval = SIGN(xval + xoffset) * pow(REALABS(xval + xoffset), exponent);
5255
5256 SCIP_CALL( SCIPdebugAddSolVal(scip, z, zval) );
5257 SCIPexprgraphSetVarNodeValue(*reformnode, zval);
5258 }
5259 #endif
5260
5261 SCIP_CALL( SCIPexprgraphCreateNodeLinear(SCIPblkmem(scip), &linnode, 1, &signpowcoef, constant) );
5262 SCIP_CALL( SCIPexprgraphAddNode(exprgraph, linnode, -1, 1, reformnode) );
5263
5264 *reformnode = linnode;
5265 }
5266
5267 SCIP_CALL( SCIPreleaseCons(scip, &cons) );
5268 SCIP_CALL( SCIPreleaseVar(scip, &z) );
5269
5270 return SCIP_OKAY;
5271 }
5272
5273 /** helper function to enforce constraints */
5274 static
enforceConstraint(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS ** conss,int nconss,int nusefulconss,SCIP_SOL * sol,SCIP_Bool solinfeasible,SCIP_RESULT * result)5275 SCIP_RETCODE enforceConstraint(
5276 SCIP* scip, /**< SCIP data structure */
5277 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
5278 SCIP_CONS** conss, /**< constraints to process */
5279 int nconss, /**< number of constraints */
5280 int nusefulconss, /**< number of useful (non-obsolete) constraints to process */
5281 SCIP_SOL* sol, /**< solution to enforce (NULL for the LP solution) */
5282 SCIP_Bool solinfeasible, /**< was the solution already declared infeasible by a constraint handler? */
5283 SCIP_RESULT* result /**< pointer to store the result of the enforcing call */
5284 )
5285 {
5286 SCIP_CONSHDLRDATA* conshdlrdata;
5287 SCIP_CONS* maxviolcons;
5288 SCIP_CONSDATA* consdata;
5289 SCIP_Bool success;
5290 SCIP_Bool cutoff;
5291 SCIP_Bool solviolbounds;
5292 SCIP_Real sepaefficacy;
5293 SCIP_Real maxviol;
5294 int nnotify;
5295 int c;
5296
5297 assert(scip != NULL);
5298 assert(conshdlr != NULL);
5299 assert(conss != NULL || nconss == 0);
5300 assert(result != NULL);
5301
5302 conshdlrdata = SCIPconshdlrGetData(conshdlr);
5303 assert(conshdlrdata != NULL);
5304
5305 SCIP_CALL( computeViolations(scip, conshdlr, conss, nconss, sol, &solviolbounds, &maxviolcons) );
5306
5307 if( maxviolcons == NULL )
5308 {
5309 *result = SCIP_FEASIBLE;
5310 return SCIP_OKAY;
5311 }
5312
5313 *result = SCIP_INFEASIBLE;
5314
5315 if( solviolbounds )
5316 {
5317 /* if LP solution violates variable bounds, then this should be because a row was added that
5318 * introduced this variable newly to the LP, in which case it gets value 0.0; the row should
5319 * have been added to resolve an infeasibility, so solinfeasible should be TRUE
5320 * see also issue #627
5321 */
5322 assert(solinfeasible);
5323 /* however, if solinfeasible is actually not TRUE, then better cut off the node to avoid that SCIP
5324 * stops because infeasible cannot be resolved */
5325 /*lint --e{774} */
5326 if( !solinfeasible )
5327 *result = SCIP_CUTOFF;
5328 return SCIP_OKAY;
5329 }
5330
5331 /* if we are above the 100'th enforcement round for this node, something is strange
5332 * (maybe the LP does not think that the cuts we add are violated, or we do ECP on a high-dimensional convex function)
5333 * in this case, check if some limit is hit or SCIP should stop for some other reason and terminate enforcement by creating a dummy node
5334 * (in optimized more, returning SCIP_INFEASIBLE in *result would be sufficient, but in debug mode this would give an assert in scip.c)
5335 * the reason to wait for 100 rounds is to avoid calls to SCIPisStopped in normal runs, which may be expensive
5336 * we only increment nenforounds until 101 to avoid an overflow
5337 */
5338 if( conshdlrdata->lastenfonode == SCIPgetCurrentNode(scip) )
5339 {
5340 if( conshdlrdata->nenforounds > 100 )
5341 {
5342 if( SCIPisStopped(scip) )
5343 {
5344 SCIP_NODE* child;
5345
5346 SCIP_CALL( SCIPcreateChild(scip, &child, 1.0, SCIPnodeGetEstimate(SCIPgetCurrentNode(scip))) );
5347 *result = SCIP_BRANCHED;
5348
5349 return SCIP_OKAY;
5350 }
5351 }
5352 else
5353 ++conshdlrdata->nenforounds;
5354 }
5355 else
5356 {
5357 conshdlrdata->lastenfonode = SCIPgetCurrentNode(scip);
5358 conshdlrdata->nenforounds = 0;
5359 }
5360
5361 /* run domain propagation for violated constraints */
5362 for( c = 0; c < nconss; ++c )
5363 {
5364 int nchgbds;
5365 int naddconss;
5366
5367 assert(conss[c] != NULL); /*lint !e613*/
5368
5369 consdata = SCIPconsGetData(conss[c]); /*lint !e613*/
5370 assert(consdata != NULL);
5371
5372 if( !SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) && !SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
5373 continue;
5374
5375 nchgbds = 0;
5376 naddconss = 0;
5377 SCIP_CALL( propagateCons(scip, conshdlr, conss[c], TRUE, &cutoff, &nchgbds, &naddconss) ); /*lint !e613*/
5378 if( cutoff )
5379 {
5380 *result = SCIP_CUTOFF;
5381 return SCIP_OKAY;
5382 }
5383 if( nchgbds )
5384 *result = SCIP_REDUCEDDOM;
5385 if( naddconss )
5386 *result = SCIP_CONSADDED;
5387 }
5388 if( *result == SCIP_REDUCEDDOM || *result == SCIP_CONSADDED )
5389 return SCIP_OKAY;
5390
5391 consdata = SCIPconsGetData(maxviolcons);
5392 assert(consdata != NULL);
5393 maxviol = consdata->lhsviol + consdata->rhsviol;
5394 assert(SCIPisGT(scip, maxviol, SCIPfeastol(scip)));
5395
5396 /* we would like a cut that is efficient enough that it is not redundant in the LP (>lpfeastol)
5397 * however, we also don't want very weak cuts, so try to reach at least feastol (=lpfeastol by default, though)
5398 */
5399 SCIP_CALL( separatePoint(scip, conshdlr, conss, nconss, nusefulconss, sol, SCIPfeastol(scip), TRUE, FALSE, &success,
5400 &cutoff, &sepaefficacy) );
5401 if( cutoff )
5402 {
5403 SCIPdebugMsg(scip, "separation detected cutoff.\n");
5404 *result = SCIP_CUTOFF;
5405 return SCIP_OKAY;
5406 }
5407 if( success )
5408 {
5409 SCIPdebugMsg(scip, "separation succeeded (bestefficacy = %g, minefficacy = %g)\n", sepaefficacy, SCIPfeastol(scip));
5410 *result = SCIP_SEPARATED;
5411 return SCIP_OKAY;
5412 }
5413 SCIPdebugMsg(scip, "separation failed (bestefficacy = %g < %g = minefficacy ); max viol: %g\n", sepaefficacy, SCIPfeastol(scip),
5414 maxviol);
5415
5416 /* we are not feasible, the whole node is not infeasible, and we cannot find a reasonable cut
5417 * -> collect variables for branching
5418 */
5419 SCIP_CALL( registerBranchingCandidates(scip, conshdlr, conss, nconss, sol, &nnotify) );
5420
5421 if( nnotify == 0 && !solinfeasible && SCIPfeastol(scip) > SCIPgetLPFeastol(scip) )
5422 {
5423 /* fallback 1: we also have no branching candidates, so try to find a weak cut */
5424 SCIP_CALL( separatePoint(scip, conshdlr, conss, nconss, nusefulconss, sol, SCIPgetLPFeastol(scip), TRUE, FALSE,
5425 &success, &cutoff, &sepaefficacy) );
5426 if( cutoff )
5427 {
5428 SCIPdebugMsg(scip, "separation detected cutoff.\n");
5429 *result = SCIP_CUTOFF;
5430 return SCIP_OKAY;
5431 }
5432 if( success )
5433 {
5434 *result = SCIP_SEPARATED;
5435 return SCIP_OKAY;
5436 }
5437 }
5438
5439 if( nnotify == 0 && !solinfeasible )
5440 {
5441 SCIP_Bool reduceddom;
5442 SCIP_VAR* brvar = NULL;
5443
5444 /* fallback 2: fix almost-fixed nonlinear variables */
5445 SCIP_CALL( fixAlmostFixedX(scip, conss, nconss, &cutoff, &reduceddom) );
5446 if( cutoff )
5447 {
5448 SCIPdebugMsg(scip, "fixing almost fixed var lead to infeasibility.\n");
5449 *result = SCIP_CUTOFF;
5450 return SCIP_OKAY;
5451 }
5452 if( reduceddom )
5453 {
5454 *result = SCIP_REDUCEDDOM;
5455 return SCIP_OKAY;
5456 }
5457
5458 /* fallback 3: separation probably failed because of numerical difficulties;
5459 if no-one declared solution infeasible yet and we had not even found a weak cut, try to resolve by branching */
5460 SCIP_CALL( registerLargeRelaxValueVariableForBranching(scip, conss, nconss, sol, &brvar) );
5461 if( brvar == NULL )
5462 {
5463 SCIPwarningMessage(scip, "Could not find any branching variable candidate. Cutting off node. Max viol = %g.\n",
5464 SCIPconsGetData(maxviolcons)->lhsviol+SCIPconsGetData(maxviolcons)->rhsviol);
5465 *result = SCIP_CUTOFF;
5466 return SCIP_OKAY;
5467 }
5468 else
5469 {
5470 SCIPdebugMsg(scip, "Could not find any usual branching variable candidate. Proposed variable %s with LP value %g for branching. Max. viol. cons. <%s>: %g+%g\n",
5471 SCIPvarGetName(brvar), SCIPgetSolVal(scip, sol, brvar), SCIPconsGetName(maxviolcons),
5472 SCIPconsGetData(maxviolcons)->lhsviol, SCIPconsGetData(maxviolcons)->rhsviol);
5473 nnotify = 1;
5474 }
5475 }
5476
5477 assert(*result == SCIP_INFEASIBLE && (solinfeasible || nnotify > 0));
5478 return SCIP_OKAY;
5479 }
5480
5481 /*
5482 * Callback methods of constraint handler
5483 */
5484
5485 /** copy method for constraint handler plugins (called when SCIP copies plugins) */
5486 static
SCIP_DECL_CONSHDLRCOPY(conshdlrCopyAbspower)5487 SCIP_DECL_CONSHDLRCOPY(conshdlrCopyAbspower)
5488 { /*lint --e{715}*/
5489 assert(scip != NULL);
5490 assert(conshdlr != NULL);
5491 assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
5492
5493 /* call inclusion method of constraint handler */
5494 SCIP_CALL( SCIPincludeConshdlrAbspower(scip) );
5495
5496 *valid = TRUE;
5497
5498 return SCIP_OKAY;
5499 }
5500
5501 /** destructor of constraint handler to free constraint handler data (called when SCIP is exiting) */
5502 static
SCIP_DECL_CONSFREE(consFreeAbspower)5503 SCIP_DECL_CONSFREE(consFreeAbspower)
5504 { /*lint --e{715}*/
5505 SCIP_CONSHDLRDATA* conshdlrdata;
5506
5507 assert(scip != NULL);
5508 assert(conshdlr != NULL);
5509
5510 conshdlrdata = SCIPconshdlrGetData(conshdlr);
5511 assert(conshdlrdata != NULL);
5512
5513 SCIPfreeBlockMemory(scip, &conshdlrdata);
5514
5515 return SCIP_OKAY;
5516 }
5517
5518 /** initialization method of constraint handler (called after problem was transformed) */
5519 static
SCIP_DECL_CONSINIT(consInitAbspower)5520 SCIP_DECL_CONSINIT(consInitAbspower)
5521 { /*lint --e{715}*/
5522 SCIP_CONSHDLRDATA* conshdlrdata;
5523
5524 assert(scip != NULL);
5525 assert(conshdlr != NULL);
5526
5527 conshdlrdata = SCIPconshdlrGetData(conshdlr);
5528 assert(conshdlrdata != NULL);
5529
5530 conshdlrdata->subnlpheur = SCIPfindHeur(scip, "subnlp");
5531 conshdlrdata->trysolheur = SCIPfindHeur(scip, "trysol");
5532 conshdlrdata->conshdlrindicator = SCIPfindConshdlr(scip, "indicator");
5533 conshdlrdata->nsecantcuts = 0;
5534 conshdlrdata->ncuts = 0;
5535
5536 return SCIP_OKAY;
5537 }
5538
5539 /** deinitialization method of constraint handler (called before transformed problem is freed) */
5540 static
SCIP_DECL_CONSEXIT(consExitAbspower)5541 SCIP_DECL_CONSEXIT(consExitAbspower)
5542 { /*lint --e{715}*/
5543 SCIP_CONSHDLRDATA* conshdlrdata;
5544
5545 assert(scip != NULL);
5546 assert(conshdlr != NULL);
5547
5548 conshdlrdata = SCIPconshdlrGetData(conshdlr);
5549 assert(conshdlrdata != NULL);
5550
5551 conshdlrdata->subnlpheur = NULL;
5552 conshdlrdata->trysolheur = NULL;
5553 conshdlrdata->conshdlrindicator = NULL;
5554
5555 return SCIP_OKAY;
5556 }
5557
5558 /** presolving initialization method of constraint handler (called when presolving is about to begin) */
5559 static
SCIP_DECL_CONSINITPRE(consInitpreAbspower)5560 SCIP_DECL_CONSINITPRE(consInitpreAbspower)
5561 { /*lint --e{715}*/
5562 SCIP_CONSHDLRDATA* conshdlrdata;
5563
5564 assert(conshdlr != NULL);
5565
5566 conshdlrdata = SCIPconshdlrGetData(conshdlr);
5567 assert(conshdlrdata != NULL);
5568
5569 /* initialize comparedpairwise flag to TRUE, if at most one constraint, otherwise 0 */
5570 conshdlrdata->comparedpairwise = (nconss <= 1);
5571
5572 return SCIP_OKAY;
5573 }
5574
5575 /** presolving deinitialization method of constraint handler (called after presolving has been finished) */
5576 static
SCIP_DECL_CONSEXITPRE(consExitpreAbspower)5577 SCIP_DECL_CONSEXITPRE(consExitpreAbspower)
5578 { /*lint --e{715}*/
5579 int c;
5580
5581 assert(scip != NULL);
5582 assert(conss != NULL || nconss == 0);
5583
5584 /* tell SCIP that we have something nonlinear, and whether we are nonlinear in a continuous variable */
5585 for( c = 0; c < nconss; ++c )
5586 {
5587 assert(conss[c] != NULL); /*lint !e613*/
5588
5589 if( SCIPconsIsAdded(conss[c]) ) /*lint !e613*/
5590 {
5591 SCIPenableNLP(scip);
5592 break;
5593 }
5594 }
5595
5596 return SCIP_OKAY;
5597 }
5598
5599 /** solving process initialization method of constraint handler (called when branch and bound process is about to begin) */
5600 static
SCIP_DECL_CONSINITSOL(consInitsolAbspower)5601 SCIP_DECL_CONSINITSOL(consInitsolAbspower)
5602 { /*lint --e{715}*/
5603 SCIP_CONSHDLRDATA* conshdlrdata;
5604 SCIP_CONSDATA* consdata;
5605 int c;
5606
5607 conshdlrdata = SCIPconshdlrGetData(conshdlr);
5608 assert(conshdlrdata != NULL);
5609
5610 assert(scip != NULL);
5611 assert(conss != NULL || nconss == 0);
5612
5613 for( c = 0; c < nconss; ++c )
5614 {
5615 assert(conss[c] != NULL); /*lint !e613*/
5616
5617 consdata = SCIPconsGetData(conss[c]); /*lint !e613*/
5618 assert(consdata != NULL);
5619
5620 assert(consdata->exponent > 1.0);
5621
5622 /* setup root that corresponds to exponent */
5623 if( SCIPisIntegral(scip, consdata->exponent) && consdata->exponent-0.5 < ROOTS_KNOWN )
5624 {
5625 consdata->root = roots[(int)SCIPfloor(scip, consdata->exponent+0.5)];
5626 }
5627 else if( SCIPisEQ(scip, consdata->exponent, 1.852) )
5628 {
5629 consdata->root = 0.39821689389382575186;
5630 }
5631 else
5632 {
5633 SCIP_Real root;
5634 SCIP_Real polyval;
5635 SCIP_Real gradval;
5636 int iter;
5637
5638 /* search for a positive root of (n-1) y^n + n y^(n-1) - 1
5639 * use the closest precomputed root as starting value */
5640 if( consdata->exponent >= ROOTS_KNOWN )
5641 root = roots[ROOTS_KNOWN];
5642 else if( consdata->exponent <= 2.0 )
5643 root = roots[2];
5644 else
5645 root = roots[(int)SCIPfloor(scip, consdata->exponent)];
5646 iter = 0;
5647 do
5648 {
5649 polyval = (consdata->exponent - 1.0) * consdata->power(root, consdata->exponent) + consdata->exponent * pow(root, consdata->exponent-1.0) - 1.0;
5650 if( SCIPisZero(scip, polyval) )
5651 break;
5652
5653 /* gradient of (n-1) y^n + n y^(n-1) - 1 is n(n-1)y^(n-1) + n(n-1)y^(n-2) */
5654 gradval = (consdata->exponent - 1.0) * consdata->exponent * (pow(root, consdata->exponent - 1.0) + pow(root, consdata->exponent - 2.0));
5655 if( SCIPisZero(scip, gradval) )
5656 break;
5657
5658 /* update root by adding -polyval/gradval (Newton's method) */
5659 root -= polyval / gradval;
5660 if( root < 0.0 )
5661 root = 0.0;
5662 }
5663 while( ++iter < 1000 );
5664
5665 if( !SCIPisZero(scip, polyval) )
5666 {
5667 SCIPerrorMessage("failed to compute root for exponent %g\n", consdata->exponent);
5668 return SCIP_ERROR;
5669 }
5670 SCIPdebugMsg(scip, "root for %g is %.15g, certainty = %g\n", consdata->exponent, root, polyval);
5671 /* @todo cache root value?? (they are actually really fast to compute...) */
5672
5673 consdata->root = root;
5674 }
5675
5676 /* add nlrow respresentation to NLP, if NLP had been constructed */
5677 if( SCIPisNLPConstructed(scip) && SCIPconsIsEnabled(conss[c]) ) /*lint !e613*/
5678 {
5679 if( consdata->nlrow == NULL )
5680 {
5681 SCIP_CALL( createNlRow(scip, conss[c]) ); /*lint !e613*/
5682 assert(consdata->nlrow != NULL);
5683 }
5684 SCIP_CALL( SCIPaddNlRow(scip, consdata->nlrow) );
5685 }
5686 }
5687
5688 conshdlrdata->newsoleventfilterpos = -1;
5689 if( nconss != 0 )
5690 {
5691 SCIP_EVENTHDLR* eventhdlr;
5692
5693 eventhdlr = SCIPfindEventhdlr(scip, CONSHDLR_NAME"_newsolution");
5694 assert(eventhdlr != NULL);
5695
5696 SCIP_CALL( SCIPcatchEvent(scip, SCIP_EVENTTYPE_SOLFOUND, eventhdlr, (SCIP_EVENTDATA*)conshdlr, &conshdlrdata->newsoleventfilterpos) );
5697 }
5698
5699 /* reset flags and counters */
5700 conshdlrdata->sepanlp = FALSE;
5701 conshdlrdata->lastenfonode = NULL;
5702 conshdlrdata->nenforounds = 0;
5703
5704 return SCIP_OKAY;
5705 }
5706
5707 /** solving process deinitialization method of constraint handler (called before branch and bound process data is freed) */
5708 static
SCIP_DECL_CONSEXITSOL(consExitsolAbspower)5709 SCIP_DECL_CONSEXITSOL(consExitsolAbspower)
5710 { /*lint --e{715}*/
5711 SCIP_CONSHDLRDATA* conshdlrdata;
5712 SCIP_CONSDATA* consdata;
5713 int c;
5714
5715 assert(scip != NULL);
5716 assert(conss != NULL || nconss == 0);
5717
5718 conshdlrdata = SCIPconshdlrGetData(conshdlr);
5719 assert(conshdlrdata != NULL);
5720
5721 if( conshdlrdata->newsoleventfilterpos >= 0 )
5722 {
5723 SCIP_EVENTHDLR* eventhdlr;
5724
5725 eventhdlr = SCIPfindEventhdlr(scip, CONSHDLR_NAME"_newsolution");
5726 assert(eventhdlr != NULL);
5727
5728 SCIP_CALL( SCIPdropEvent(scip, SCIP_EVENTTYPE_SOLFOUND, eventhdlr, (SCIP_EVENTDATA*)conshdlr, conshdlrdata->newsoleventfilterpos) );
5729 conshdlrdata->newsoleventfilterpos = -1;
5730 }
5731
5732 for( c = 0; c < nconss; ++c )
5733 {
5734 assert(conss[c] != NULL); /*lint !e613*/
5735
5736 consdata = SCIPconsGetData(conss[c]); /*lint !e613*/
5737 assert(consdata != NULL);
5738
5739 /* free nonlinear row representation */
5740 if( consdata->nlrow != NULL )
5741 {
5742 SCIP_CALL( SCIPreleaseNlRow(scip, &consdata->nlrow) );
5743 }
5744 }
5745
5746 return SCIP_OKAY;
5747 }
5748
5749 /** frees specific constraint data */
5750 static
SCIP_DECL_CONSDELETE(consDeleteAbspower)5751 SCIP_DECL_CONSDELETE(consDeleteAbspower)
5752 { /*lint --e{715}*/
5753 assert(scip != NULL);
5754 assert(conshdlr != NULL);
5755 assert(cons != NULL);
5756 assert(consdata != NULL);
5757 assert((*consdata)->x != NULL);
5758 assert((*consdata)->z != NULL);
5759 assert((*consdata)->xeventfilterpos == -1);
5760 assert((*consdata)->zeventfilterpos == -1);
5761
5762 if( (*consdata)->nlrow != NULL )
5763 {
5764 SCIP_CALL( SCIPreleaseNlRow(scip, &(*consdata)->nlrow) );
5765 }
5766
5767 SCIPfreeBlockMemory(scip, consdata);
5768
5769 return SCIP_OKAY;
5770 }
5771
5772 /** transforms constraint data into data belonging to the transformed problem */
5773 static
SCIP_DECL_CONSTRANS(consTransAbspower)5774 SCIP_DECL_CONSTRANS(consTransAbspower)
5775 { /*lint --e{715}*/
5776 SCIP_CONSDATA* sourcedata;
5777 SCIP_CONSDATA* targetdata;
5778
5779 sourcedata = SCIPconsGetData(sourcecons);
5780 assert(sourcedata != NULL);
5781
5782 SCIP_CALL( SCIPduplicateBlockMemory(scip, &targetdata, sourcedata) );
5783 assert(targetdata->xeventfilterpos == -1);
5784 assert(targetdata->zeventfilterpos == -1);
5785
5786 SCIP_CALL( SCIPgetTransformedVar(scip, sourcedata->x, &targetdata->x) );
5787 SCIP_CALL( SCIPgetTransformedVar(scip, sourcedata->z, &targetdata->z) );
5788
5789 /* branching on multiaggregated variables does not seem to work well, so avoid multiagg. x */
5790 assert( SCIPvarIsActive(targetdata->x) );
5791 SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, targetdata->x) );
5792
5793 /* cannot propagate on multiaggregated vars, so avoid multiagg. z */
5794 assert( SCIPvarIsActive(targetdata->z) );
5795 SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, targetdata->z) );
5796
5797 /* create target constraint */
5798 SCIP_CALL( SCIPcreateCons(scip, targetcons, SCIPconsGetName(sourcecons), conshdlr, targetdata,
5799 SCIPconsIsInitial(sourcecons), SCIPconsIsSeparated(sourcecons), SCIPconsIsEnforced(sourcecons),
5800 SCIPconsIsChecked(sourcecons), SCIPconsIsPropagated(sourcecons), SCIPconsIsLocal(sourcecons),
5801 SCIPconsIsModifiable(sourcecons), SCIPconsIsDynamic(sourcecons), SCIPconsIsRemovable(sourcecons),
5802 SCIPconsIsStickingAtNode(sourcecons)) );
5803
5804 return SCIP_OKAY;
5805 }
5806
5807 /** LP initialization method of constraint handler (called before the initial LP relaxation at a node is solved)
5808 *
5809 * we add secant underestimators
5810 */
5811 static
SCIP_DECL_CONSINITLP(consInitlpAbspower)5812 SCIP_DECL_CONSINITLP(consInitlpAbspower)
5813 { /*lint --e{715}*/
5814 SCIP_CONSDATA* consdata;
5815 SCIP_CONSHDLRDATA* conshdlrdata;
5816 SCIP_ROWPREP* rowprep = NULL;
5817 SCIP_ROW* row = NULL;
5818 int c;
5819 SCIP_Real xlb;
5820 SCIP_Real xub;
5821 SCIP_Real coefrange;
5822
5823 assert(scip != NULL);
5824 assert(conshdlr != NULL);
5825 assert(conss != NULL || nconss == 0);
5826
5827 conshdlrdata = SCIPconshdlrGetData(conshdlr);
5828 assert(conshdlrdata != NULL);
5829
5830 *infeasible = FALSE;
5831
5832 for( c = 0; c < nconss && !(*infeasible); ++c )
5833 {
5834 assert(conss[c] != NULL); /*lint !e613*/
5835
5836 consdata = SCIPconsGetData(conss[c]); /*lint !e613*/
5837 assert(consdata != NULL);
5838
5839 xlb = SCIPvarGetLbGlobal(consdata->x);
5840 xub = SCIPvarGetUbGlobal(consdata->x);
5841
5842 if( SCIPisRelEQ(scip, xlb, xub) )
5843 continue;
5844
5845 if( !SCIPisInfinity(scip, consdata->rhs) )
5846 {
5847 if( !SCIPisInfinity(scip, -xlb) )
5848 {
5849 if( SCIPisNegative(scip, xlb + consdata->xoffset) )
5850 {
5851 /* generate secant between xlb and right changepoint */
5852 SCIP_CALL( generateSecantCutNoCheck(scip, &rowprep, xlb, MIN(-consdata->root * (xlb+consdata->xoffset) - consdata->xoffset, xub),
5853 consdata->exponent, consdata->xoffset, consdata->power, 1.0, consdata->zcoef, consdata->rhs, consdata->x, consdata->z) );
5854 if( rowprep != NULL )
5855 {
5856 SCIP_CALL( SCIPcleanupRowprep(scip, rowprep, NULL, conshdlrdata->cutmaxrange, -SCIPinfinity(scip), &coefrange, NULL) );
5857 if( coefrange < conshdlrdata->cutmaxrange && !SCIPisInfinity(scip, REALABS(rowprep->side)) )
5858 {
5859 SCIP_CALL( SCIPgetRowprepRowConshdlr(scip, &row, rowprep, conshdlr) );
5860
5861 assert(!(*infeasible));
5862 SCIP_CALL( SCIPaddRow(scip, row, FALSE /* forcecut */, infeasible) );
5863
5864 if( conshdlrdata->conshdlrindicator != NULL && !SCIProwIsLocal(row) )
5865 {
5866 SCIP_CALL( SCIPaddRowIndicator(scip, conshdlrdata->conshdlrindicator, row) );
5867 }
5868 SCIP_CALL( SCIPreleaseRow(scip, &row) );
5869 }
5870 SCIPfreeRowprep(scip, &rowprep);
5871 }
5872 }
5873 else if( xlb < INITLPMAXVARVAL )
5874 {
5875 /* generate tangent in lower bound */
5876 SCIP_CALL( generateLinearizationCut(scip, &rowprep, conshdlr, xlb, consdata->exponent, consdata->xoffset, 1.0, consdata->zcoef, consdata->rhs,
5877 consdata->x, consdata->z, FALSE) );
5878 assert(rowprep != NULL);
5879
5880 SCIP_CALL( SCIPcleanupRowprep(scip, rowprep, NULL, conshdlrdata->cutmaxrange, -SCIPinfinity(scip), &coefrange, NULL) );
5881 if( coefrange < conshdlrdata->cutmaxrange && !SCIPisInfinity(scip, REALABS(rowprep->side)) )
5882 {
5883 SCIP_CALL( SCIPgetRowprepRowConshdlr(scip, &row, rowprep, conshdlr) );
5884
5885 assert(!(*infeasible));
5886 SCIP_CALL( SCIPaddRow(scip, row, FALSE /* forcecut */, infeasible) );
5887
5888 if( conshdlrdata->conshdlrindicator != NULL )
5889 {
5890 SCIP_CALL( SCIPaddRowIndicator(scip, conshdlrdata->conshdlrindicator, row) );
5891 }
5892 SCIP_CALL( SCIPreleaseRow(scip, &row) );
5893 }
5894 SCIPfreeRowprep(scip, &rowprep);
5895 }
5896 }
5897
5898 if( !(*infeasible) && !SCIPisInfinity(scip, xub) )
5899 {
5900 /* generate tangent in upper bound */
5901 if( -consdata->root * (xlb+consdata->xoffset) - consdata->xoffset < xub && xub <= INITLPMAXVARVAL )
5902 {
5903 SCIP_CALL( generateLinearizationCut(scip, &rowprep, conshdlr, xub, consdata->exponent, consdata->xoffset, 1.0, consdata->zcoef, consdata->rhs,
5904 consdata->x, consdata->z, FALSE) );
5905 assert(rowprep != NULL);
5906
5907 SCIP_CALL( SCIPcleanupRowprep(scip, rowprep, NULL, conshdlrdata->cutmaxrange, -SCIPinfinity(scip), &coefrange, NULL) );
5908 if( coefrange < conshdlrdata->cutmaxrange && !SCIPisInfinity(scip, REALABS(rowprep->side)) )
5909 {
5910 SCIP_CALL( SCIPgetRowprepRowConshdlr(scip, &row, rowprep, conshdlr) );
5911
5912 assert(!(*infeasible));
5913 SCIP_CALL( SCIPaddRow(scip, row, FALSE /* forcecut */, infeasible) );
5914
5915 if( conshdlrdata->conshdlrindicator != NULL )
5916 {
5917 SCIP_CALL( SCIPaddRowIndicator(scip, conshdlrdata->conshdlrindicator, row) );
5918 }
5919 SCIP_CALL( SCIPreleaseRow(scip, &row) );
5920 }
5921 SCIPfreeRowprep(scip, &rowprep);
5922 }
5923 }
5924 }
5925
5926 if( !(*infeasible) && !SCIPisInfinity(scip, -consdata->lhs) )
5927 {
5928 if( !SCIPisInfinity(scip, xub) )
5929 {
5930 if( SCIPisPositive(scip, xub + consdata->xoffset) )
5931 {
5932 /* generate secant between left change point and upper bound */
5933 SCIP_CALL( generateSecantCutNoCheck(scip, &rowprep, -xub, MIN(consdata->root * (xub+consdata->xoffset) + consdata->xoffset, -xlb),
5934 consdata->exponent, -consdata->xoffset, consdata->power, -1.0, -consdata->zcoef, -consdata->lhs, consdata->x, consdata->z) );
5935 if( rowprep != NULL )
5936 {
5937 SCIP_CALL( SCIPcleanupRowprep(scip, rowprep, NULL, conshdlrdata->cutmaxrange, -SCIPinfinity(scip), &coefrange, NULL) );
5938 if( coefrange < conshdlrdata->cutmaxrange && !SCIPisInfinity(scip, REALABS(rowprep->side)) )
5939 {
5940 SCIP_CALL( SCIPgetRowprepRowConshdlr(scip, &row, rowprep, conshdlr) );
5941
5942 assert(!(*infeasible));
5943 SCIP_CALL( SCIPaddRow(scip, row, FALSE /* forcecut */, infeasible) );
5944
5945 if( conshdlrdata->conshdlrindicator != NULL && !SCIProwIsLocal(row) )
5946 {
5947 SCIP_CALL( SCIPaddRowIndicator(scip, conshdlrdata->conshdlrindicator, row) );
5948 }
5949 SCIP_CALL( SCIPreleaseRow(scip, &row) );
5950 }
5951 SCIPfreeRowprep(scip, &rowprep);
5952 }
5953 }
5954 else if( xub >= -INITLPMAXVARVAL )
5955 {
5956 /* generate tangent in upper bound */
5957 SCIP_CALL( generateLinearizationCut(scip, &rowprep, conshdlr, -xub, consdata->exponent, -consdata->xoffset, -1.0, -consdata->zcoef, -consdata->lhs,
5958 consdata->x, consdata->z, FALSE) );
5959 assert(rowprep != NULL);
5960
5961 SCIP_CALL( SCIPcleanupRowprep(scip, rowprep, NULL, conshdlrdata->cutmaxrange, -SCIPinfinity(scip), &coefrange, NULL) );
5962 if( coefrange < conshdlrdata->cutmaxrange && !SCIPisInfinity(scip, REALABS(rowprep->side)) )
5963 {
5964 SCIP_CALL( SCIPgetRowprepRowConshdlr(scip, &row, rowprep, conshdlr) );
5965
5966 assert(!(*infeasible));
5967 SCIP_CALL( SCIPaddRow(scip, row, FALSE /* forcecut */, infeasible) );
5968
5969 if( conshdlrdata->conshdlrindicator != NULL )
5970 {
5971 SCIP_CALL( SCIPaddRowIndicator(scip, conshdlrdata->conshdlrindicator, row) );
5972 }
5973 SCIP_CALL( SCIPreleaseRow(scip, &row) );
5974 }
5975 SCIPfreeRowprep(scip, &rowprep);
5976 }
5977 }
5978
5979 if( !(*infeasible) && !SCIPisInfinity(scip, -xlb) )
5980 {
5981 /* generate tangent in lower bound */
5982 if( -consdata->root * (xub+consdata->xoffset) - consdata->xoffset > xlb && xlb >= -INITLPMAXVARVAL )
5983 {
5984 SCIP_CALL( generateLinearizationCut(scip, &rowprep, conshdlr, -xlb, consdata->exponent, -consdata->xoffset, -1.0, -consdata->zcoef, -consdata->lhs,
5985 consdata->x, consdata->z, FALSE) );
5986 assert(rowprep != NULL);
5987
5988 SCIP_CALL( SCIPcleanupRowprep(scip, rowprep, NULL, conshdlrdata->cutmaxrange, -SCIPinfinity(scip), &coefrange, NULL) );
5989 if( coefrange < conshdlrdata->cutmaxrange && !SCIPisInfinity(scip, REALABS(rowprep->side)) )
5990 {
5991 SCIP_CALL( SCIPgetRowprepRowConshdlr(scip, &row, rowprep, conshdlr) );
5992
5993 assert(!(*infeasible));
5994 SCIP_CALL( SCIPaddRow(scip, row, FALSE /* forcecut */, infeasible) );
5995
5996 if( conshdlrdata->conshdlrindicator != NULL )
5997 {
5998 SCIP_CALL( SCIPaddRowIndicator(scip, conshdlrdata->conshdlrindicator, row) );
5999 }
6000 SCIP_CALL( SCIPreleaseRow(scip, &row) );
6001 }
6002 SCIPfreeRowprep(scip, &rowprep);
6003 }
6004 }
6005 }
6006 }
6007
6008 return SCIP_OKAY;
6009 }
6010
6011 /** separation method of constraint handler for LP solutions */
6012 static
SCIP_DECL_CONSSEPALP(consSepalpAbspower)6013 SCIP_DECL_CONSSEPALP(consSepalpAbspower)
6014 { /*lint --e{715}*/
6015 SCIP_CONSHDLRDATA* conshdlrdata;
6016 SCIP_CONS* maxviolcon;
6017 SCIP_Bool success;
6018 SCIP_Bool cutoff;
6019 SCIP_Bool solviolbounds;
6020
6021 assert(scip != NULL);
6022 assert(conshdlr != NULL);
6023 assert(conss != NULL || nconss == 0);
6024 assert(result != NULL);
6025
6026 *result = SCIP_DIDNOTFIND;
6027
6028 conshdlrdata = SCIPconshdlrGetData(conshdlr);
6029 assert(conshdlrdata != NULL);
6030
6031 SCIP_CALL( computeViolations(scip, conshdlr, conss, nconss, NULL, &solviolbounds, &maxviolcon) );
6032
6033 /* if LP solution is (temporarily) outside bounds, then don't try to separate it */
6034 if( solviolbounds )
6035 return SCIP_OKAY;
6036
6037 /* if no violation, then nothing to separate */
6038 if( maxviolcon == NULL )
6039 return SCIP_OKAY;
6040
6041 /* at root, check if we want to solve the NLP relaxation and use its solutions as reference point
6042 * if there is something convex, then linearizing in the solution of the NLP relaxation can be very useful
6043 */
6044 if( SCIPgetDepth(scip) == 0 && !conshdlrdata->sepanlp &&
6045 (SCIPgetNContVars(scip) >= conshdlrdata->sepanlpmincont * SCIPgetNVars(scip) || (SCIPgetLPSolstat(scip) == SCIP_LPSOLSTAT_UNBOUNDEDRAY && conshdlrdata->sepanlpmincont <= 1.0)) &&
6046 SCIPisNLPConstructed(scip) && SCIPgetNNlpis(scip) > 0 )
6047 {
6048 SCIP_CONSDATA* consdata;
6049 SCIP_NLPSOLSTAT solstat;
6050 SCIP_Bool solvednlp;
6051 int c;
6052
6053 solstat = SCIPgetNLPSolstat(scip);
6054 solvednlp = FALSE;
6055 if( solstat == SCIP_NLPSOLSTAT_UNKNOWN )
6056 {
6057 /* NLP is not solved yet, so we might want to do this
6058 * but first check whether there is a violated constraint side which corresponds to a convex function
6059 * @todo put this check into initsol and update via consenable/consdisable
6060 */
6061 for( c = 0; c < nconss; ++c )
6062 {
6063 assert(conss[c] != NULL); /*lint !e613*/
6064
6065 consdata = SCIPconsGetData(conss[c]); /*lint !e613*/
6066 assert(consdata != NULL);
6067
6068 /* skip feasible constraints */
6069 if( !SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) && !SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
6070 continue;
6071
6072 if( (!SCIPisGT(scip, SCIPvarGetUbGlobal(consdata->x), -consdata->xoffset) && !SCIPisInfinity(scip, -consdata->lhs)) ||
6073 ( !SCIPisLT(scip, SCIPvarGetLbGlobal(consdata->x), -consdata->xoffset) && !SCIPisInfinity(scip, -consdata->rhs)) )
6074 break;
6075 }
6076
6077 if( c < nconss )
6078 {
6079 /* try to solve NLP and update solstat */
6080
6081 /* ensure linear conss are in NLP */
6082 if( conshdlrdata->subnlpheur != NULL )
6083 {
6084 SCIP_CALL( SCIPaddLinearConsToNlpHeurSubNlp(scip, conshdlrdata->subnlpheur, TRUE, TRUE) );
6085 }
6086
6087 /* set LP solution as starting values, if available */
6088 if( SCIPgetLPSolstat(scip) == SCIP_LPSOLSTAT_OPTIMAL )
6089 {
6090 SCIP_CALL( SCIPsetNLPInitialGuessSol(scip, NULL) );
6091 }
6092
6093 /* SCIP_CALL( SCIPsetNLPIntPar(scip, SCIP_NLPPAR_VERBLEVEL, 1) ); */
6094 SCIP_CALL( SCIPsolveNLP(scip) );
6095
6096 solstat = SCIPgetNLPSolstat(scip);
6097 SCIPdebugMsg(scip, "solved NLP relax, solution status: %d\n", solstat);
6098
6099 solvednlp = TRUE;
6100 }
6101 }
6102
6103 conshdlrdata->sepanlp = TRUE;
6104
6105 if( solstat == SCIP_NLPSOLSTAT_GLOBINFEASIBLE )
6106 {
6107 SCIPdebugMsg(scip, "NLP relaxation is globally infeasible, thus can cutoff node\n");
6108 *result = SCIP_CUTOFF;
6109 return SCIP_OKAY;
6110 }
6111
6112 if( solstat <= SCIP_NLPSOLSTAT_FEASIBLE )
6113 {
6114 /* if we have feasible NLP solution, generate linearization cuts there */
6115 SCIP_Bool lpsolseparated;
6116 SCIP_SOL* nlpsol;
6117
6118 SCIP_CALL( SCIPcreateNLPSol(scip, &nlpsol, NULL) );
6119 assert(nlpsol != NULL);
6120
6121 /* if we solved the NLP and solution is integral, then pass it to trysol heuristic */
6122 if( solvednlp && conshdlrdata->trysolheur != NULL )
6123 {
6124 int nfracvars;
6125
6126 nfracvars = 0;
6127 if( SCIPgetNBinVars(scip) > 0 || SCIPgetNIntVars(scip) > 0 )
6128 {
6129 SCIP_CALL( SCIPgetNLPFracVars(scip, NULL, NULL, NULL, &nfracvars, NULL) );
6130 }
6131
6132 if( nfracvars == 0 )
6133 {
6134 SCIP_CALL( SCIPheurPassSolTrySol(scip, conshdlrdata->trysolheur, nlpsol) );
6135 }
6136 }
6137
6138 SCIP_CALL( addLinearizationCuts(scip, conshdlr, conss, nconss, nlpsol, &lpsolseparated, SCIPgetSepaMinEfficacy(scip)) );
6139
6140 SCIP_CALL( SCIPfreeSol(scip, &nlpsol) );
6141
6142 /* if a cut that separated the LP solution was added, then return, otherwise continue with usual separation in LP solution */
6143 if( lpsolseparated )
6144 {
6145 SCIPdebugMsg(scip, "linearization cuts separate LP solution\n");
6146
6147 *result = SCIP_SEPARATED;
6148
6149 return SCIP_OKAY;
6150 }
6151 }
6152 }
6153 /* if we do not want to try solving the NLP, or have no NLP, or have no NLP solver, or solving the NLP failed,
6154 * or separating with NLP solution as reference point failed, then try (again) with LP solution as reference point
6155 */
6156
6157 SCIP_CALL( separatePoint(scip, conshdlr, conss, nconss, nusefulconss, NULL, SCIPgetSepaMinEfficacy(scip), FALSE, conshdlrdata->sepainboundsonly, &success, &cutoff, NULL) );
6158 if( cutoff )
6159 *result = SCIP_CUTOFF;
6160 else if( success )
6161 *result = SCIP_SEPARATED;
6162
6163 return SCIP_OKAY;
6164 }
6165
6166 /** separation method of constraint handler for arbitrary primal solutions */
6167 static
SCIP_DECL_CONSSEPASOL(consSepasolAbspower)6168 SCIP_DECL_CONSSEPASOL(consSepasolAbspower)
6169 { /*lint --e{715}*/
6170 SCIP_CONS* maxviolcon;
6171 SCIP_Bool success;
6172 SCIP_Bool cutoff;
6173 SCIP_Bool solviolbounds;
6174
6175 assert(scip != NULL);
6176 assert(conshdlr != NULL);
6177 assert(conss != NULL || nconss == 0);
6178 assert(sol != NULL);
6179 assert(result != NULL);
6180
6181 *result = SCIP_DIDNOTFIND;
6182
6183 SCIP_CALL( computeViolations(scip, conshdlr, conss, nconss, sol, &solviolbounds, &maxviolcon) );
6184
6185 /* if solution is not even within bounds, then don't bother trying to separate it */
6186 if( solviolbounds )
6187 return SCIP_OKAY;
6188
6189 /* if nothing violated, then nothing to separate */
6190 if( maxviolcon == NULL )
6191 return SCIP_OKAY;
6192
6193 SCIP_CALL( separatePoint(scip, conshdlr, conss, nconss, nusefulconss, sol, SCIPgetSepaMinEfficacy(scip), FALSE, FALSE, &success, &cutoff, NULL) );
6194 if( cutoff )
6195 *result = SCIP_CUTOFF;
6196 else if( success )
6197 *result = SCIP_SEPARATED;
6198
6199 return SCIP_OKAY;
6200 }
6201
6202 /** constraint enforcing method of constraint handler for LP solutions */
6203 static
SCIP_DECL_CONSENFOLP(consEnfolpAbspower)6204 SCIP_DECL_CONSENFOLP(consEnfolpAbspower)
6205 { /*lint --e{715}*/
6206 SCIP_CALL( enforceConstraint(scip, conshdlr, conss, nconss, nusefulconss, NULL, solinfeasible, result) );
6207
6208 return SCIP_OKAY;
6209 }
6210
6211 /** constraint enforcing method of constraint handler for relaxation solutions */
6212 static
SCIP_DECL_CONSENFORELAX(consEnforelaxAbspower)6213 SCIP_DECL_CONSENFORELAX(consEnforelaxAbspower)
6214 { /*lint --e{715}*/
6215 SCIP_CALL( enforceConstraint(scip, conshdlr, conss, nconss, nusefulconss, sol, solinfeasible, result) );
6216
6217 return SCIP_OKAY;
6218 }
6219
6220 /** constraint enforcing method of constraint handler for pseudo solutions */
6221 static
SCIP_DECL_CONSENFOPS(consEnfopsAbspower)6222 SCIP_DECL_CONSENFOPS(consEnfopsAbspower)
6223 { /*lint --e{715}*/
6224 SCIP_CONSHDLRDATA* conshdlrdata;
6225 SCIP_CONS* maxviolcon;
6226 SCIP_CONSDATA* consdata;
6227 int c;
6228 int nnotify;
6229 SCIP_Bool solviolbounds;
6230
6231 assert(scip != NULL);
6232 assert(conshdlr != NULL);
6233 assert(conss != NULL || nconss == 0);
6234
6235 conshdlrdata = SCIPconshdlrGetData(conshdlr);
6236 assert(conshdlrdata != NULL);
6237
6238 SCIP_CALL( computeViolations(scip, conshdlr, conss, nconss, NULL, &solviolbounds, &maxviolcon) );
6239
6240 assert(!solviolbounds); /* the pseudo-solution should be within bounds by definition */
6241
6242 if( maxviolcon == NULL )
6243 {
6244 *result = SCIP_FEASIBLE;
6245 return SCIP_OKAY;
6246 }
6247
6248 *result = SCIP_INFEASIBLE;
6249
6250 /* run domain propagation for violated constraints */
6251 for( c = 0; c < nconss; ++c )
6252 {
6253 SCIP_Bool cutoff;
6254 int nchgbds;
6255 int naddconss;
6256
6257 assert(conss[c] != NULL); /*lint !e613*/
6258
6259 consdata = SCIPconsGetData(conss[c]); /*lint !e613*/
6260 assert(consdata != NULL);
6261
6262 if( !SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) && !SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
6263 continue;
6264
6265 nchgbds = 0;
6266 naddconss = 0;
6267 SCIP_CALL( propagateCons(scip, conshdlr, conss[c], TRUE, &cutoff, &nchgbds, &naddconss) ); /*lint !e613*/
6268 if( cutoff )
6269 {
6270 *result = SCIP_CUTOFF;
6271 return SCIP_OKAY;
6272 }
6273 if( nchgbds )
6274 *result = SCIP_REDUCEDDOM;
6275 if( naddconss )
6276 *result = SCIP_CONSADDED;
6277 }
6278 if( *result == SCIP_REDUCEDDOM || *result == SCIP_CONSADDED )
6279 return SCIP_OKAY;
6280
6281 /* we are not feasible and we cannot proof that the whole node is infeasible
6282 * -> branch on all unfixed variables in violated constraints
6283 */
6284 nnotify = 0;
6285 for( c = 0; c < nconss; ++c )
6286 {
6287 assert(conss != NULL);
6288 consdata = SCIPconsGetData(conss[c]);
6289 assert(consdata != NULL);
6290 SCIPdebugMsg(scip, "cons <%s> violation: %g %g\n", SCIPconsGetName(conss[c]), consdata->lhsviol, consdata->rhsviol);
6291
6292 if( !SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) && !SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
6293 continue;
6294
6295 SCIPdebugMsg(scip, "cons <%s> violation: %g %g\n", SCIPconsGetName(conss[c]), consdata->lhsviol, consdata->rhsviol);
6296
6297 /* domain propagation should have removed cons when x is fixed */
6298 assert(!SCIPisRelEQ(scip, SCIPvarGetLbLocal(consdata->x), SCIPvarGetUbLocal(consdata->x)));
6299
6300 SCIP_CALL( SCIPaddExternBranchCand(scip, consdata->x, consdata->lhsviol + consdata->rhsviol, proposeBranchingPoint(scip, conss[c], NULL, conshdlrdata->preferzerobranch, conshdlrdata->branchminconverror)) );
6301 ++nnotify;
6302 }
6303
6304 if( nnotify == 0 )
6305 {
6306 SCIPdebugMsg(scip, "All variables in violated constraints fixed (up to epsilon). Cannot find branching candidate. Forcing solution of LP.\n");
6307 *result = SCIP_SOLVELP;
6308 }
6309
6310 assert(*result == SCIP_SOLVELP || (*result == SCIP_INFEASIBLE && nnotify > 0));
6311 return SCIP_OKAY;
6312 }
6313
6314
6315 /** domain propagation method of constraint handler */
6316 static
SCIP_DECL_CONSPROP(consPropAbspower)6317 SCIP_DECL_CONSPROP(consPropAbspower)
6318 { /*lint --e{715}*/
6319 int c;
6320 int nchgbds;
6321 int naddconss;
6322 SCIP_Bool cutoff = FALSE;
6323
6324 assert(scip != NULL);
6325 assert(conshdlr != NULL);
6326 assert(conss != NULL || nconss == 0);
6327 assert(result != NULL);
6328
6329 *result = SCIP_DIDNOTFIND;
6330
6331 for( c = 0; c < nmarkedconss; ++c )
6332 {
6333 assert(conss != NULL);
6334
6335 /* propagate constraint, but do not allow to add a constraint for tightening a multiaggregated variable (not allowed in CONSPROP) */
6336 nchgbds = 0;
6337 naddconss = 0;
6338 SCIP_CALL( propagateCons(scip, conshdlr, conss[c], FALSE, &cutoff, &nchgbds, &naddconss) );
6339 assert(naddconss == 0);
6340
6341 if( cutoff )
6342 {
6343 *result = SCIP_CUTOFF;
6344 break;
6345 }
6346
6347 if( nchgbds )
6348 *result = SCIP_REDUCEDDOM;
6349
6350 if( c >= nusefulconss && *result != SCIP_DIDNOTFIND )
6351 break;
6352 }
6353
6354 return SCIP_OKAY;
6355 }
6356
6357 /** presolving method of constraint handler */
6358 static
SCIP_DECL_CONSPRESOL(consPresolAbspower)6359 SCIP_DECL_CONSPRESOL(consPresolAbspower)
6360 { /*lint --e{715}*/
6361 SCIP_CONSHDLRDATA* conshdlrdata;
6362 SCIP_CONSDATA* consdata;
6363 SCIP_RESULT replaceresult;
6364 SCIP_Bool success;
6365 SCIP_Bool infeas;
6366 int localnchgbds;
6367 int localnaddconss;
6368 int c;
6369
6370 assert(scip != NULL);
6371 assert(conss != NULL || nconss == 0);
6372 assert(result != NULL);
6373
6374 conshdlrdata = SCIPconshdlrGetData(conshdlr);
6375 assert(conshdlrdata != NULL);
6376
6377 *result = SCIP_DIDNOTFIND;
6378
6379 /* check for duplicates, if not done yet or if absolute power constraints were modified (variable fixings) or new absolute power constraints had been added */
6380 if( !conshdlrdata->comparedpairwise && (presoltiming & SCIP_PRESOLTIMING_MEDIUM) != 0 )
6381 {
6382 SCIP_CALL( presolveFindDuplicates(scip, conshdlr, conss, nconss, nupgdconss, ndelconss, naddconss, nfixedvars, naggrvars, &success, &infeas) );
6383 if( infeas )
6384 {
6385 *result = SCIP_CUTOFF;
6386 return SCIP_OKAY;
6387 }
6388 if( success )
6389 *result = SCIP_SUCCESS;
6390
6391 conshdlrdata->comparedpairwise = TRUE;
6392
6393 return SCIP_OKAY;
6394 }
6395
6396 for( c = 0; c < nconss; ++c )
6397 {
6398 assert(conss[c] != NULL); /*lint !e613*/
6399
6400 if( SCIPconsIsDeleted(conss[c]) ) /*lint !e613*/
6401 continue;
6402
6403 consdata = SCIPconsGetData(conss[c]); /*lint !e613*/
6404 assert(consdata != NULL);
6405
6406 SCIPdebugMsg(scip, "presolving constraint <%s>\n", SCIPconsGetName(conss[c])); /*lint !e613*/
6407 SCIPdebugPrintCons(scip, conss[c], NULL); /*lint !e613*/
6408
6409 /* check if we can upgrade to a linear constraint */
6410 if( consdata->exponent == 1.0 )
6411 {
6412 SCIP_VAR* vars[2];
6413 SCIP_Real coefs[2];
6414 SCIP_CONS* lincons;
6415 SCIP_Real lhs;
6416 SCIP_Real rhs;
6417
6418 vars[0] = consdata->x;
6419 vars[1] = consdata->z;
6420 coefs[0] = 1.0;
6421 coefs[1] = consdata->zcoef;
6422 lhs = consdata->lhs;
6423 rhs = consdata->rhs;
6424 if( !SCIPisInfinity(scip, -lhs) )
6425 lhs -= consdata->xoffset;
6426 if( !SCIPisInfinity(scip, rhs) )
6427 rhs -= consdata->xoffset;
6428
6429 SCIP_CALL( SCIPcreateConsLinear(scip, &lincons, SCIPconsGetName(conss[c]), 2, vars, coefs, lhs, rhs,
6430 SCIPconsIsInitial(conss[c]), SCIPconsIsSeparated(conss[c]), SCIPconsIsEnforced(conss[c]),
6431 SCIPconsIsChecked(conss[c]), SCIPconsIsPropagated(conss[c]), SCIPconsIsLocal(conss[c]),
6432 SCIPconsIsModifiable(conss[c]), SCIPconsIsDynamic(conss[c]), SCIPconsIsRemovable(conss[c]),
6433 SCIPconsIsStickingAtNode(conss[c])) ); /*lint !e613*/
6434 SCIP_CALL( SCIPaddCons(scip, lincons) );
6435 SCIP_CALL( SCIPreleaseCons(scip, &lincons) );
6436
6437 SCIP_CALL( SCIPdelCons(scip, conss[c]) ); /*lint !e613*/
6438 ++*nupgdconss;
6439 continue;
6440 }
6441
6442 /* check for fixed variables */
6443 replaceresult = SCIP_DIDNOTFIND;
6444 SCIP_CALL( checkFixedVariables(scip, conshdlr, conss[c], ndelconss, nupgdconss, nchgbds, nfixedvars, &replaceresult) ); /*lint !e613*/
6445 switch( replaceresult )
6446 {
6447 case SCIP_DIDNOTFIND:
6448 break;
6449
6450 case SCIP_CUTOFF:
6451 *result = SCIP_CUTOFF;
6452 return SCIP_OKAY;
6453
6454 case SCIP_REDUCEDDOM:
6455 case SCIP_CONSADDED:
6456 *result = SCIP_SUCCESS;
6457 break;
6458
6459 default:
6460 SCIPerrorMessage("invalid result from checkFixedVariables\n");
6461 SCIPABORT();
6462 return SCIP_INVALIDDATA; /*lint !e527*/
6463 } /*lint !e788*/
6464
6465 if( SCIPconsIsDeleted(conss[c]) ) /*lint !e613*/
6466 {
6467 *result = SCIP_SUCCESS;
6468 continue;
6469 }
6470
6471 /* another check for upgrading to a varbound constraint */
6472 if( SCIPvarIsBinary(consdata->x) )
6473 {
6474 SCIP_CONS* lincons;
6475 SCIP_Real lhs;
6476 SCIP_Real rhs;
6477 SCIP_Real zcoef;
6478
6479 /* for binary variable x,
6480 * sign(x+offset)|x+offset|^n = sign(offset)|offset|^n * (1-x) + sign(offset+1) |offset+1|^n x
6481 * = sign(offset)|offset|^n + (sign(offset+1) |offset+1|^n - sign(offset)|offset|^n) * x
6482 * => constraint is lhs <= sign(offset)|offset|^n + (sign(offset+1) |offset+1|^n - sign(offset)|offset|^n) * x + c*z <= rhs
6483 * upgrade to varbound constraint if z is not continuous, otherwise linear
6484 */
6485 if( consdata->xoffset != 0.0 )
6486 {
6487 SCIP_Real xcoef;
6488
6489 xcoef = SIGN(consdata->xoffset + 1.0) * consdata->power(ABS(consdata->xoffset + 1.0), consdata->exponent)
6490 -SIGN(consdata->xoffset) * consdata->power(ABS(consdata->xoffset), consdata->exponent);
6491
6492 if( xcoef < 0.0 )
6493 {
6494 if( SCIPisInfinity(scip, consdata->rhs) )
6495 lhs = -SCIPinfinity(scip);
6496 else
6497 lhs = (consdata->rhs - SIGN(consdata->xoffset) * consdata->power(ABS(consdata->xoffset), consdata->exponent)) / xcoef;
6498 if( SCIPisInfinity(scip, -consdata->lhs) )
6499 rhs = SCIPinfinity(scip);
6500 else
6501 rhs = (consdata->lhs - SIGN(consdata->xoffset) * consdata->power(ABS(consdata->xoffset), consdata->exponent)) / xcoef;
6502 }
6503 else
6504 {
6505 if( SCIPisInfinity(scip, -consdata->lhs) )
6506 lhs = -SCIPinfinity(scip);
6507 else
6508 lhs = (consdata->lhs - SIGN(consdata->xoffset) * consdata->power(ABS(consdata->xoffset), consdata->exponent)) / xcoef;
6509 if( SCIPisInfinity(scip, consdata->rhs) )
6510 rhs = SCIPinfinity(scip);
6511 else
6512 rhs = (consdata->rhs - SIGN(consdata->xoffset) * consdata->power(ABS(consdata->xoffset), consdata->exponent)) / xcoef;
6513 }
6514 zcoef = consdata->zcoef / xcoef;
6515
6516 /* avoid numerical troubles if xcoef is too large */
6517 if( SCIPisZero(scip, zcoef) )
6518 zcoef = 0.0;
6519 }
6520 else
6521 {
6522 lhs = consdata->lhs;
6523 rhs = consdata->rhs;
6524 zcoef = consdata->zcoef;
6525 }
6526
6527 /* the upgraded constraint reduces to lhs <= x <= rhs, try to fix x instead of creating a constraint */
6528 if( SCIPisZero(scip, zcoef) && SCIPisEQ(scip, lhs, rhs) )
6529 {
6530 /* both sides are integral */
6531 if( SCIPisIntegral(scip, lhs) )
6532 {
6533 SCIP_Bool fixed;
6534
6535 assert(SCIPisIntegral(scip, rhs));
6536
6537 SCIP_CALL( SCIPfixVar(scip, consdata->x, lhs, &infeas, &fixed) );
6538
6539 /* fixing x to lhs is infeasible */
6540 if( infeas || !fixed )
6541 {
6542 SCIPdebugMsg(scip, "propagation on constraint <%s> says problem is infeasible in presolve\n",
6543 SCIPconsGetName(conss[c])); /*lint !e613*/
6544 *result = SCIP_CUTOFF;
6545 return SCIP_OKAY;
6546 }
6547
6548 ++(*nfixedvars);
6549 break;
6550 }
6551 else
6552 {
6553 /* an integer variables cannot be fixed to a fractional value */
6554 SCIPdebugMsg(scip, "propagation on constraint <%s> says problem is infeasible in presolve\n",
6555 SCIPconsGetName(conss[c])); /*lint !e613*/
6556 *result = SCIP_CUTOFF;
6557 return SCIP_OKAY;
6558 }
6559 }
6560
6561 if( SCIPvarGetType(consdata->z) < SCIP_VARTYPE_CONTINUOUS && !SCIPisZero(scip, zcoef)
6562 && SCIPvarGetStatus(consdata->z) != SCIP_VARSTATUS_MULTAGGR )
6563 {
6564 SCIP_CALL( SCIPcreateConsVarbound(scip, &lincons, SCIPconsGetName(conss[c]),
6565 consdata->x, consdata->z, zcoef, lhs, rhs,
6566 SCIPconsIsInitial(conss[c]), SCIPconsIsSeparated(conss[c]), SCIPconsIsEnforced(conss[c]),
6567 SCIPconsIsChecked(conss[c]), SCIPconsIsPropagated(conss[c]), SCIPconsIsLocal(conss[c]),
6568 SCIPconsIsModifiable(conss[c]), SCIPconsIsDynamic(conss[c]), SCIPconsIsRemovable(conss[c]),
6569 SCIPconsIsStickingAtNode(conss[c])) ); /*lint !e613*/
6570 }
6571 else
6572 {
6573 SCIP_CALL( SCIPcreateConsLinear(scip, &lincons, SCIPconsGetName(conss[c]),
6574 1, &consdata->z, &zcoef, lhs, rhs,
6575 SCIPconsIsInitial(conss[c]), SCIPconsIsSeparated(conss[c]), SCIPconsIsEnforced(conss[c]),
6576 SCIPconsIsChecked(conss[c]), SCIPconsIsPropagated(conss[c]), SCIPconsIsLocal(conss[c]),
6577 SCIPconsIsModifiable(conss[c]), SCIPconsIsDynamic(conss[c]), SCIPconsIsRemovable(conss[c]),
6578 SCIPconsIsStickingAtNode(conss[c])) ); /*lint !e613*/
6579 SCIP_CALL( SCIPaddCoefLinear(scip, lincons, consdata->x, 1.0) );
6580 }
6581 SCIP_CALL( SCIPaddCons(scip, lincons) );
6582
6583 SCIPdebugMsg(scip, "upgraded constraint <%s> to linear constraint due to binary x-variable\n", SCIPconsGetName(conss[c])); /*lint !e613*/
6584 SCIPdebugPrintCons(scip, conss[c], NULL); /*lint !e613*/
6585 SCIPdebugPrintCons(scip, lincons, NULL);
6586
6587 SCIP_CALL( SCIPreleaseCons(scip, &lincons) );
6588
6589 SCIP_CALL( SCIPdelCons(scip, conss[c]) ); /*lint !e613*/
6590 ++*nupgdconss;
6591 continue;
6592 }
6593
6594 /* run domain propagation, also checks for redundancy */
6595 localnchgbds = 0;
6596 localnaddconss = 0;
6597 SCIP_CALL( propagateCons(scip, conshdlr, conss[c], TRUE, &infeas, &localnchgbds, &localnaddconss) ); /*lint !e613*/
6598 if( infeas )
6599 {
6600 SCIPdebugMsg(scip, "propagation on constraint <%s> says problem is infeasible in presolve\n", SCIPconsGetName(conss[c])); /*lint !e613*/
6601 *result = SCIP_CUTOFF;
6602 return SCIP_OKAY;
6603 }
6604 if( localnchgbds > 0 || localnaddconss > 0 )
6605 {
6606 *nchgbds += localnchgbds;
6607 *naddconss += localnaddconss;
6608 *result = SCIP_SUCCESS;
6609 }
6610 if( SCIPconsIsDeleted(conss[c]) ) /*lint !e613*/
6611 {
6612 ++*ndelconss;
6613 *result = SCIP_SUCCESS;
6614 continue;
6615 }
6616
6617 if( conshdlrdata->dualpresolve && SCIPallowStrongDualReds(scip) )
6618 {
6619 /* check if a variable can be fixed because it appears in no other constraint */
6620 SCIP_CALL( presolveDual(scip, conss[c], &infeas, ndelconss, nfixedvars) ); /*lint !e613*/
6621 if( infeas )
6622 {
6623 SCIPdebugMsg(scip, "dual presolve on constraint <%s> says problem is infeasible in presolve\n", SCIPconsGetName(conss[c])); /*lint !e613*/
6624 *result = SCIP_CUTOFF;
6625 return SCIP_OKAY;
6626 }
6627 if( SCIPconsIsDeleted(conss[c]) ) /*lint !e613*/
6628 {
6629 *result = SCIP_SUCCESS;
6630 continue;
6631 }
6632 }
6633
6634 /* propagate variable bound constraints */
6635 if( !consdata->propvarbounds && SCIPallowWeakDualReds(scip) )
6636 {
6637 SCIP_CALL( propagateVarbounds(scip, conshdlr, conss[c], &infeas, nchgbds, naddconss) ); /*lint !e613*/
6638
6639 if( infeas )
6640 {
6641 *result = SCIP_CUTOFF;
6642 return SCIP_OKAY;
6643 }
6644
6645 consdata->propvarbounds = TRUE;
6646 }
6647
6648 /* check if we can make z implicit integer
6649 * if constraint is signpow(x,n) + c*z = rhs with x integer, |c| = 1, rhs and n integral, then z is implicit integral
6650 */
6651 if( SCIPvarGetType(consdata->z) == SCIP_VARTYPE_CONTINUOUS && SCIPvarGetType(consdata->x) != SCIP_VARTYPE_CONTINUOUS &&
6652 SCIPisEQ(scip, consdata->lhs, consdata->rhs) && SCIPisIntegral(scip, consdata->rhs) && SCIPisEQ(scip, REALABS(consdata->zcoef), 1.0) && SCIPisIntegral(scip, consdata->exponent)
6653 )
6654 {
6655 SCIPdebugMsg(scip, "make z = <%s> implicit integer in cons <%s>\n", SCIPvarGetName(consdata->z), SCIPconsGetName(conss[c])); /*lint !e613*/
6656 SCIPdebugPrintCons(scip, conss[c], NULL); /*lint !e613*/
6657 SCIP_CALL( SCIPchgVarType(scip, consdata->z, SCIP_VARTYPE_IMPLINT, &infeas) );
6658 if( infeas )
6659 {
6660 SCIPdebugMsg(scip, "problem found infeasible in presolve when making <%s> implicit integer\n", SCIPvarGetName(consdata->z));
6661 *result = SCIP_CUTOFF;
6662 return SCIP_OKAY;
6663 }
6664 else
6665 {
6666 ++*nchgvartypes;
6667 }
6668 }
6669 }
6670
6671 return SCIP_OKAY;
6672 }
6673
6674 /** resolves a propagation on the given variable by supplying the variables needed for applying the corresponding
6675 * propagation rule (see propagateCons()):
6676 * (1) left hand side and bounds on z -> lower bound on x
6677 * (2) left hand side and upper bound on x -> bound on z
6678 * (3) right hand side and bounds on z -> upper bound on x
6679 * (4) right hand side and lower bound on x -> bound on z
6680 */
6681 static
SCIP_DECL_CONSRESPROP(consRespropAbspower)6682 SCIP_DECL_CONSRESPROP(consRespropAbspower)
6683 {
6684 assert(result != NULL);
6685
6686 SCIP_CALL( resolvePropagation(scip, cons, infervar, (PROPRULE)inferinfo, boundtype, bdchgidx) );
6687
6688 *result = SCIP_SUCCESS;
6689
6690 return SCIP_OKAY;
6691 } /*lint !e715*/
6692
6693 /** variable rounding lock method of constraint handler */
6694 static
SCIP_DECL_CONSLOCK(consLockAbspower)6695 SCIP_DECL_CONSLOCK(consLockAbspower)
6696 { /*lint --e{715}*/
6697 SCIP_CONSDATA* consdata;
6698 SCIP_Bool haslb;
6699 SCIP_Bool hasub;
6700
6701 assert(scip != NULL);
6702 assert(cons != NULL);
6703 assert(locktype == SCIP_LOCKTYPE_MODEL);
6704
6705 consdata = SCIPconsGetData(cons);
6706 assert(consdata != NULL);
6707
6708 haslb = !SCIPisInfinity(scip, -consdata->lhs);
6709 hasub = !SCIPisInfinity(scip, consdata->rhs);
6710
6711 if( consdata->x != NULL )
6712 {
6713 if( haslb )
6714 {
6715 SCIP_CALL( SCIPaddVarLocksType(scip, consdata->x, locktype, nlockspos, nlocksneg) );
6716 }
6717 if( hasub )
6718 {
6719 SCIP_CALL( SCIPaddVarLocksType(scip, consdata->x, locktype, nlocksneg, nlockspos) );
6720 }
6721 }
6722
6723 if( consdata->z != NULL )
6724 {
6725 if( consdata->zcoef > 0 )
6726 {
6727 if( haslb )
6728 {
6729 SCIP_CALL( SCIPaddVarLocksType(scip, consdata->z, locktype, nlockspos, nlocksneg) );
6730 }
6731 if( hasub )
6732 {
6733 SCIP_CALL( SCIPaddVarLocksType(scip, consdata->z, locktype, nlocksneg, nlockspos) );
6734 }
6735 }
6736 else
6737 {
6738 if( haslb )
6739 {
6740 SCIP_CALL( SCIPaddVarLocksType(scip, consdata->z, locktype, nlocksneg, nlockspos) );
6741 }
6742 if( hasub )
6743 {
6744 SCIP_CALL( SCIPaddVarLocksType(scip, consdata->z, locktype, nlockspos, nlocksneg) );
6745 }
6746 }
6747 }
6748
6749 return SCIP_OKAY;
6750 }
6751
6752 /** constraint activation notification method of constraint handler */
6753 static
SCIP_DECL_CONSACTIVE(consActiveAbspower)6754 SCIP_DECL_CONSACTIVE(consActiveAbspower)
6755 { /*lint --e{715}*/
6756 SCIP_CONSHDLRDATA* conshdlrdata;
6757
6758 assert(conshdlr != NULL);
6759
6760 conshdlrdata = SCIPconshdlrGetData(conshdlr);
6761 assert(conshdlrdata != NULL);
6762
6763 /* (re)run constraint comparison, since new constraint is added */
6764 conshdlrdata->comparedpairwise = FALSE;
6765
6766 return SCIP_OKAY;
6767 }
6768
6769 /** constraint enabling notification method of constraint handler */
6770 static
SCIP_DECL_CONSENABLE(consEnableAbspower)6771 SCIP_DECL_CONSENABLE(consEnableAbspower)
6772 { /*lint --e{715}*/
6773 SCIP_CONSHDLRDATA* conshdlrdata;
6774
6775 assert(scip != NULL);
6776 assert(cons != NULL);
6777 assert(conshdlr != NULL);
6778
6779 conshdlrdata = SCIPconshdlrGetData(conshdlr);
6780 assert(conshdlrdata != NULL);
6781 assert(conshdlrdata->eventhdlr != NULL);
6782
6783 SCIP_CALL( catchVarEvents(scip, conshdlrdata->eventhdlr, cons) );
6784
6785 return SCIP_OKAY;
6786 }
6787
6788 /** constraint disabling notification method of constraint handler */
6789 static
SCIP_DECL_CONSDISABLE(consDisableAbspower)6790 SCIP_DECL_CONSDISABLE(consDisableAbspower)
6791 { /*lint --e{715}*/
6792 SCIP_CONSHDLRDATA* conshdlrdata;
6793
6794 assert(scip != NULL);
6795 assert(cons != NULL);
6796 assert(conshdlr != NULL);
6797
6798 conshdlrdata = SCIPconshdlrGetData(conshdlr);
6799 assert(conshdlrdata != NULL);
6800 assert(conshdlrdata->eventhdlr != NULL);
6801
6802 SCIP_CALL( dropVarEvents(scip, conshdlrdata->eventhdlr, cons) );
6803
6804 return SCIP_OKAY;
6805 }
6806
6807 /** constraint display method of constraint handler */
6808 static
SCIP_DECL_CONSPRINT(consPrintAbspower)6809 SCIP_DECL_CONSPRINT(consPrintAbspower)
6810 { /*lint --e{715}*/
6811 SCIP_CONSDATA* consdata;
6812
6813 assert(scip != NULL);
6814 assert(cons != NULL);
6815
6816 consdata = SCIPconsGetData(cons);
6817 assert(consdata != NULL);
6818
6819 /* print left hand side for ranged rows */
6820 if( !SCIPisInfinity(scip, -consdata->lhs)
6821 && !SCIPisInfinity(scip, consdata->rhs)
6822 && !SCIPisEQ(scip, consdata->lhs, consdata->rhs) )
6823 SCIPinfoMessage(scip, file, "%.15g <= ", consdata->lhs);
6824
6825 /* print coefficients and variables */
6826 SCIPinfoMessage(scip, file, "signpower(");
6827 SCIP_CALL( SCIPwriteVarName(scip, file, consdata->x, TRUE) );
6828 SCIPinfoMessage(scip, file, " %+.15g, %.15g) ", consdata->xoffset, consdata->exponent);
6829
6830 SCIPinfoMessage(scip, file, "%+.15g", consdata->zcoef);
6831 SCIP_CALL( SCIPwriteVarName(scip, file, consdata->z, TRUE) );
6832
6833 /* print right hand side */
6834 if( SCIPisEQ(scip, consdata->lhs, consdata->rhs) )
6835 {
6836 SCIPinfoMessage(scip, file, " == %.15g", consdata->rhs);
6837 }
6838 else if( !SCIPisInfinity(scip, consdata->rhs) )
6839 {
6840 SCIPinfoMessage(scip, file, " <= %.15g", consdata->rhs);
6841 }
6842 else if( !SCIPisInfinity(scip, -consdata->lhs) )
6843 {
6844 SCIPinfoMessage(scip, file, " >= %.15g", consdata->lhs);
6845 }
6846 else
6847 {
6848 SCIPinfoMessage(scip, file, " [free]");
6849 }
6850
6851 return SCIP_OKAY;
6852 }
6853
6854 /** feasibility check method of constraint handler for integral solutions */
6855 static
SCIP_DECL_CONSCHECK(consCheckAbspower)6856 SCIP_DECL_CONSCHECK(consCheckAbspower)
6857 { /*lint --e{715}*/
6858 SCIP_CONSHDLRDATA* conshdlrdata;
6859 SCIP_CONSDATA* consdata;
6860 SCIP_Bool dolinfeasshift;
6861 SCIP_Bool solviolbounds;
6862 SCIP_Real maxviol;
6863 SCIP_Real viol;
6864 int c;
6865
6866 assert(scip != NULL);
6867 assert(conss != NULL || nconss == 0);
6868 assert(result != NULL);
6869
6870 conshdlrdata = SCIPconshdlrGetData(conshdlr);
6871 assert(conshdlrdata != NULL);
6872
6873 *result = SCIP_FEASIBLE;
6874
6875 maxviol = 0.0;
6876 viol = SCIP_INVALID;
6877
6878 dolinfeasshift = conshdlrdata->linfeasshift && (conshdlrdata->trysolheur != NULL) && SCIPgetStage(scip) > SCIP_STAGE_PROBLEM && SCIPgetStage(scip) < SCIP_STAGE_SOLVED;
6879 for( c = 0; c < nconss; ++c )
6880 {
6881 assert(conss != NULL);
6882 SCIP_CALL( computeViolation(scip, conshdlr, conss[c], sol, &viol, &solviolbounds) );
6883
6884 assert(!solviolbounds); /* see also issue #627 */
6885
6886 consdata = SCIPconsGetData(conss[c]);
6887 assert(consdata != NULL);
6888
6889 if( SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) || SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
6890 {
6891 *result = SCIP_INFEASIBLE;
6892
6893 if( printreason )
6894 {
6895 SCIPinfoMessage(scip, NULL, "absolute power constraint <%s> violated by %g\n\t",
6896 SCIPconsGetName(conss[c]), viol);
6897 SCIP_CALL( consPrintAbspower(scip, conshdlr, conss[c], NULL) );
6898 SCIPinfoMessage(scip, NULL, ";\n");
6899 }
6900
6901 if( conshdlrdata->subnlpheur == NULL && !dolinfeasshift && !completely )
6902 return SCIP_OKAY;
6903 if( consdata->lhsviol > maxviol || consdata->rhsviol > maxviol )
6904 maxviol = MAX(consdata->lhsviol, consdata->rhsviol);
6905 }
6906 }
6907
6908 if( *result == SCIP_INFEASIBLE && dolinfeasshift )
6909 {
6910 SCIP_CALL( proposeFeasibleSolution(scip, conshdlr, conss, nconss, sol) );
6911 }
6912
6913 if( *result == SCIP_INFEASIBLE && conshdlrdata->subnlpheur != NULL && sol != NULL && !SCIPisInfinity(scip, maxviol) )
6914 {
6915 SCIP_CALL( SCIPupdateStartpointHeurSubNlp(scip, conshdlrdata->subnlpheur, sol, maxviol) );
6916 }
6917
6918 return SCIP_OKAY;
6919 }
6920
6921 /** constraint copying method of constraint handler */
6922 static
SCIP_DECL_CONSCOPY(consCopyAbspower)6923 SCIP_DECL_CONSCOPY(consCopyAbspower)
6924 { /*lint --e{715}*/
6925 SCIP_CONSDATA* consdata;
6926 SCIP_VAR* x;
6927 SCIP_VAR* z;
6928
6929 assert(scip != NULL);
6930 assert(cons != NULL);
6931 assert(sourcescip != NULL);
6932 assert(sourcecons != NULL);
6933 assert(varmap != NULL);
6934 assert(valid != NULL);
6935
6936 consdata = SCIPconsGetData(sourcecons);
6937 assert(consdata != NULL);
6938
6939 *valid = TRUE;
6940 *cons = NULL;
6941
6942 SCIP_CALL( SCIPgetVarCopy(sourcescip, scip, consdata->x, &x, varmap, consmap, global, valid) );
6943
6944 if( *valid )
6945 {
6946 SCIP_CALL( SCIPgetVarCopy(sourcescip, scip, consdata->z, &z, varmap, consmap, global, valid) );
6947
6948 if( *valid )
6949 {
6950 assert(z != NULL); /* for lint */
6951 SCIP_CALL( SCIPcreateConsAbspower(scip, cons, name != NULL ? name : SCIPconsGetName(sourcecons),
6952 x, z, consdata->exponent, consdata->xoffset, consdata->zcoef, consdata->lhs, consdata->rhs,
6953 initial, separate, enforce, check, propagate, local, FALSE, dynamic, removable, stickingatnode) ); /*lint !e644*/
6954 }
6955 }
6956
6957 return SCIP_OKAY;
6958 }
6959
6960 /** constraint parsing method of constraint handler */
6961 static
SCIP_DECL_CONSPARSE(consParseAbspower)6962 SCIP_DECL_CONSPARSE(consParseAbspower)
6963 {
6964 SCIP_Real lhs;
6965 SCIP_Real rhs;
6966 SCIP_Real xoffset;
6967 SCIP_Real exponent;
6968 SCIP_Real zcoef;
6969 SCIP_Real value;
6970 char* endptr;
6971 char sense;
6972 SCIP_VAR* x;
6973 SCIP_VAR* z;
6974
6975 *success = TRUE;
6976
6977 /* set right hand and left side to their default values */
6978 lhs = -SCIPinfinity(scip);
6979 rhs = SCIPinfinity(scip);
6980
6981 SCIPdebugMsg(scip, "start parsing absolute power constraint expression %s\n", str);
6982
6983 if( strncmp(str, "signpower(", 10) != 0 )
6984 {
6985 /* str does not start with signpower string, so may be left-hand-side of ranged constraint */
6986 if( !SCIPstrToRealValue(str, &lhs, &endptr) )
6987 {
6988 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "Syntax error: left-hand-side or 'signpower(' expected at begin on '%s'\n", str);
6989 *success = FALSE;
6990 return SCIP_OKAY;
6991 }
6992 str = endptr;
6993 }
6994 else
6995 {
6996 str += 10;
6997 }
6998
6999 /* parse (x +offset, exponent) +coef z */
7000
7001 /* parse variable name */
7002 SCIP_CALL( SCIPparseVarName(scip, str, &x, &endptr) );
7003 if( x == NULL )
7004 {
7005 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "unknown variable name at '%s'\n", str);
7006 *success = FALSE;
7007 return SCIP_OKAY;
7008 }
7009 str = endptr;
7010
7011 /* skip whitespace */
7012 while( isspace((int)*str) )
7013 ++str;
7014
7015 /* parse offset */
7016 if( !SCIPstrToRealValue(str, &xoffset, &endptr) )
7017 {
7018 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "expected coefficient at begin of '%s'\n", str);
7019 *success = FALSE;
7020 return SCIP_OKAY;
7021 }
7022 str = endptr;
7023
7024 if( *str != ',' )
7025 {
7026 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "expected ',' at begin of '%s'\n", str);
7027 *success = FALSE;
7028 return SCIP_OKAY;
7029 }
7030 ++str;
7031
7032 /* skip whitespace */
7033 while( isspace((int)*str) )
7034 ++str;
7035
7036 /* parse exponent */
7037 if( !SCIPstrToRealValue(str, &exponent, &endptr) )
7038 {
7039 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "expected coefficient at begin of '%s'\n", str);
7040 *success = FALSE;
7041 return SCIP_OKAY;
7042 }
7043 str = endptr;
7044
7045 if( *str != ')' )
7046 {
7047 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "expected ')' at begin of '%s'\n", str);
7048 *success = FALSE;
7049 return SCIP_OKAY;
7050 }
7051 ++str;
7052
7053 /* skip whitespace */
7054 while( isspace((int)*str) )
7055 ++str;
7056
7057 /* parse coefficient */
7058 if( !SCIPstrToRealValue(str, &zcoef, &endptr) )
7059 {
7060 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "expected coefficient at begin of '%s'\n", str);
7061 *success = FALSE;
7062 return SCIP_OKAY;
7063 }
7064 str = endptr;
7065
7066 /* parse variable name */
7067 SCIP_CALL( SCIPparseVarName(scip, str, &z, &endptr) );
7068 if( z == NULL )
7069 {
7070 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "unknown variable name at '%s'\n", str);
7071 *success = FALSE;
7072 return SCIP_OKAY;
7073 }
7074 str = endptr;
7075
7076 /* skip whitespace */
7077 while( isspace((int)*str) )
7078 ++str;
7079
7080 if( strncmp(str, "[free]", 6) != 0 )
7081 {
7082 /* parse sense */
7083 if( (*str != '<' && *str != '>' && *str != '=') || str[1] != '=' )
7084 {
7085 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "expected sense at begin of '%s'\n", str);
7086 *success = FALSE;
7087 return SCIP_OKAY;
7088 }
7089 sense = *str;
7090 str += 2;
7091
7092 /* parse value at rhs */
7093 if( !SCIPstrToRealValue(str, &value, &endptr) )
7094 {
7095 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, NULL, "expected rhs value at begin of '%s'\n", str);
7096 *success = FALSE;
7097 return SCIP_OKAY;
7098 }
7099
7100 switch( sense )
7101 {
7102 case '<' :
7103 rhs = value;
7104 break;
7105 case '>' :
7106 lhs = value;
7107 break;
7108 case '=' :
7109 lhs = rhs = value;
7110 break;
7111 default:
7112 SCIPABORT(); /* checked above that this cannot happen */
7113 return SCIP_INVALIDDATA; /*lint !e527*/
7114 }
7115 }
7116
7117 SCIP_CALL( SCIPcreateConsAbspower(scip, cons, name, x, z, exponent, xoffset, zcoef, lhs, rhs,
7118 initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, stickingatnode) );
7119
7120 return SCIP_OKAY;
7121 } /*lint !e715*/
7122
7123 /** constraint method of constraint handler which returns the variables (if possible) */
7124 static
SCIP_DECL_CONSGETVARS(consGetVarsAbspower)7125 SCIP_DECL_CONSGETVARS(consGetVarsAbspower)
7126 { /*lint --e{715}*/
7127 if( varssize < 2 )
7128 (*success) = FALSE;
7129 else
7130 {
7131 SCIP_CONSDATA* consdata;
7132 assert(cons != NULL);
7133 assert(vars != NULL);
7134
7135 consdata = SCIPconsGetData(cons);
7136 assert(consdata != NULL);
7137
7138 vars[0] = consdata->x;
7139 vars[1] = consdata->z;
7140 (*success) = TRUE;
7141 }
7142
7143 return SCIP_OKAY;
7144 }
7145
7146 /** constraint method of constraint handler which returns the number of variables (if possible) */
7147 static
SCIP_DECL_CONSGETNVARS(consGetNVarsAbspower)7148 SCIP_DECL_CONSGETNVARS(consGetNVarsAbspower)
7149 { /*lint --e{715}*/
7150 (*nvars) = 2;
7151 (*success) = TRUE;
7152
7153 return SCIP_OKAY;
7154 }
7155
7156 /*
7157 * constraint specific interface methods
7158 */
7159
7160 /** creates the handler for absolute power constraints and includes it in SCIP */
SCIPincludeConshdlrAbspower(SCIP * scip)7161 SCIP_RETCODE SCIPincludeConshdlrAbspower(
7162 SCIP* scip /**< SCIP data structure */
7163 )
7164 {
7165 SCIP_CONSHDLRDATA* conshdlrdata;
7166 SCIP_CONSHDLR* conshdlr;
7167 SCIP_EVENTHDLR* eventhdlr;
7168
7169 /* create absolute power constraint handler data */
7170 SCIP_CALL( SCIPallocBlockMemory(scip, &conshdlrdata) );
7171 BMSclearMemory(conshdlrdata);
7172
7173 /* include constraint handler */
7174 SCIP_CALL( SCIPincludeConshdlrBasic(scip, &conshdlr, CONSHDLR_NAME, CONSHDLR_DESC,
7175 CONSHDLR_ENFOPRIORITY, CONSHDLR_CHECKPRIORITY, CONSHDLR_EAGERFREQ, CONSHDLR_NEEDSCONS,
7176 consEnfolpAbspower, consEnfopsAbspower, consCheckAbspower, consLockAbspower,
7177 conshdlrdata) );
7178
7179 assert(conshdlr != NULL);
7180
7181 /* set non-fundamental callbacks via specific setter functions */
7182 SCIP_CALL( SCIPsetConshdlrActive(scip, conshdlr, consActiveAbspower) );
7183 SCIP_CALL( SCIPsetConshdlrCopy(scip, conshdlr, conshdlrCopyAbspower, consCopyAbspower) );
7184 SCIP_CALL( SCIPsetConshdlrDelete(scip, conshdlr, consDeleteAbspower) );
7185 SCIP_CALL( SCIPsetConshdlrDisable(scip, conshdlr, consDisableAbspower) );
7186 SCIP_CALL( SCIPsetConshdlrEnable(scip, conshdlr, consEnableAbspower) );
7187 SCIP_CALL( SCIPsetConshdlrExit(scip, conshdlr, consExitAbspower) );
7188 SCIP_CALL( SCIPsetConshdlrExitpre(scip, conshdlr, consExitpreAbspower) );
7189 SCIP_CALL( SCIPsetConshdlrExitsol(scip, conshdlr, consExitsolAbspower) );
7190 SCIP_CALL( SCIPsetConshdlrFree(scip, conshdlr, consFreeAbspower) );
7191 SCIP_CALL( SCIPsetConshdlrGetVars(scip, conshdlr, consGetVarsAbspower) );
7192 SCIP_CALL( SCIPsetConshdlrGetNVars(scip, conshdlr, consGetNVarsAbspower) );
7193 SCIP_CALL( SCIPsetConshdlrInit(scip, conshdlr, consInitAbspower) );
7194 SCIP_CALL( SCIPsetConshdlrInitpre(scip, conshdlr, consInitpreAbspower) );
7195 SCIP_CALL( SCIPsetConshdlrInitsol(scip, conshdlr, consInitsolAbspower) );
7196 SCIP_CALL( SCIPsetConshdlrInitlp(scip, conshdlr, consInitlpAbspower) );
7197 SCIP_CALL( SCIPsetConshdlrParse(scip, conshdlr, consParseAbspower) );
7198 SCIP_CALL( SCIPsetConshdlrPresol(scip, conshdlr, consPresolAbspower, CONSHDLR_MAXPREROUNDS, CONSHDLR_PRESOLTIMING) );
7199 SCIP_CALL( SCIPsetConshdlrPrint(scip, conshdlr, consPrintAbspower) );
7200 SCIP_CALL( SCIPsetConshdlrProp(scip, conshdlr, consPropAbspower, CONSHDLR_PROPFREQ, CONSHDLR_DELAYPROP,
7201 CONSHDLR_PROP_TIMING) );
7202 SCIP_CALL( SCIPsetConshdlrResprop(scip, conshdlr, consRespropAbspower) );
7203 SCIP_CALL( SCIPsetConshdlrSepa(scip, conshdlr, consSepalpAbspower, consSepasolAbspower, CONSHDLR_SEPAFREQ,
7204 CONSHDLR_SEPAPRIORITY, CONSHDLR_DELAYSEPA) );
7205 SCIP_CALL( SCIPsetConshdlrTrans(scip, conshdlr, consTransAbspower) );
7206 SCIP_CALL( SCIPsetConshdlrEnforelax(scip, conshdlr, consEnforelaxAbspower) );
7207
7208 /* include the quadratic constraint upgrade in the quadratic constraint handler */
7209 SCIP_CALL( SCIPincludeQuadconsUpgrade(scip, quadconsUpgdAbspower, QUADCONSUPGD_PRIORITY, TRUE, CONSHDLR_NAME) );
7210
7211 /* include the absolute power constraint upgrade and node reform in the nonlinear constraint handler
7212 * we give it higher priority as quadratic, so it also takes care of x^2 constraints, if possible
7213 */
7214 SCIP_CALL( SCIPincludeNonlinconsUpgrade(scip, nonlinconsUpgdAbspower, exprgraphnodeReformAbspower, NONLINCONSUPGD_PRIORITY, TRUE, CONSHDLR_NAME) );
7215
7216 /* add absolute power constraint handler parameters */
7217 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/cutmaxrange",
7218 "maximal coef range of a cut (maximal coefficient divided by minimal coefficient) in order to be added to LP relaxation",
7219 &conshdlrdata->cutmaxrange, FALSE, 1e+7, 0.0, SCIPinfinity(scip), NULL, NULL) );
7220
7221 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/projectrefpoint",
7222 "whether to project the reference point when linearizing an absolute power constraint in a convex region",
7223 &conshdlrdata->projectrefpoint, FALSE, TRUE, NULL, NULL) );
7224
7225 SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/preferzerobranch",
7226 "how much to prefer branching on 0.0 when sign of variable is not fixed yet: 0 no preference, 1 prefer if LP solution will be cutoff in both child nodes, 2 prefer always, 3 ensure always",
7227 &conshdlrdata->preferzerobranch, FALSE, 1, 0, 3, NULL, NULL) );
7228
7229 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/branchminconverror",
7230 "whether to compute branching point such that the convexification error is minimized (after branching on 0.0)",
7231 &conshdlrdata->branchminconverror, FALSE, FALSE, NULL, NULL) );
7232
7233 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/addvarboundcons",
7234 "should variable bound constraints be added for derived variable bounds?",
7235 &conshdlrdata->addvarboundcons, TRUE, TRUE, NULL, NULL) );
7236
7237 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/linfeasshift",
7238 "whether to try to make solutions in check function feasible by shifting the linear variable z",
7239 &conshdlrdata->linfeasshift, FALSE, TRUE, NULL, NULL) );
7240
7241 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/dualpresolve",
7242 "should dual presolve be applied?",
7243 &conshdlrdata->dualpresolve, FALSE, TRUE, NULL, NULL) );
7244
7245 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/sepainboundsonly",
7246 "whether to separate linearization cuts only in the variable bounds (does not affect enforcement)",
7247 &conshdlrdata->sepainboundsonly, FALSE, FALSE, NULL, NULL) );
7248
7249 SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/sepanlpmincont",
7250 "minimal required fraction of continuous variables in problem to use solution of NLP relaxation in root for separation",
7251 &conshdlrdata->sepanlpmincont, FALSE, 1.0, 0.0, 2.0, NULL, NULL) );
7252
7253 SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/enfocutsremovable",
7254 "are cuts added during enforcement removable from the LP in the same node?",
7255 &conshdlrdata->enfocutsremovable, TRUE, FALSE, NULL, NULL) );
7256
7257 SCIP_CALL( SCIPincludeEventhdlrBasic(scip, &eventhdlr, CONSHDLR_NAME, "signals a bound change on a variable to an absolute power constraint",
7258 processVarEvent, NULL) );
7259 conshdlrdata->eventhdlr = eventhdlr;
7260
7261 SCIP_CALL( SCIPincludeEventhdlrBasic(scip, NULL, CONSHDLR_NAME"_newsolution", "handles the event that a new primal solution has been found",
7262 processNewSolutionEvent, NULL) );
7263
7264 return SCIP_OKAY;
7265 }
7266
7267 /** creates and captures a absolute power constraint
7268 *
7269 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
7270 */
SCIPcreateConsAbspower(SCIP * scip,SCIP_CONS ** cons,const char * name,SCIP_VAR * x,SCIP_VAR * z,SCIP_Real exponent,SCIP_Real xoffset,SCIP_Real zcoef,SCIP_Real lhs,SCIP_Real rhs,SCIP_Bool initial,SCIP_Bool separate,SCIP_Bool enforce,SCIP_Bool check,SCIP_Bool propagate,SCIP_Bool local,SCIP_Bool modifiable,SCIP_Bool dynamic,SCIP_Bool removable,SCIP_Bool stickingatnode)7271 SCIP_RETCODE SCIPcreateConsAbspower(
7272 SCIP* scip, /**< SCIP data structure */
7273 SCIP_CONS** cons, /**< pointer to hold the created constraint */
7274 const char* name, /**< name of constraint */
7275 SCIP_VAR* x, /**< nonlinear variable x in constraint */
7276 SCIP_VAR* z, /**< linear variable z in constraint */
7277 SCIP_Real exponent, /**< exponent n of |x+offset|^n term in constraint */
7278 SCIP_Real xoffset, /**< offset in |x+offset|^n term in constraint */
7279 SCIP_Real zcoef, /**< coefficient of z in constraint */
7280 SCIP_Real lhs, /**< left hand side of constraint */
7281 SCIP_Real rhs, /**< right hand side of constraint */
7282 SCIP_Bool initial, /**< should the LP relaxation of constraint be in the initial LP?
7283 * Usually set to TRUE. Set to FALSE for 'lazy constraints'. */
7284 SCIP_Bool separate, /**< should the constraint be separated during LP processing?
7285 * Usually set to TRUE. */
7286 SCIP_Bool enforce, /**< should the constraint be enforced during node processing?
7287 * TRUE for model constraints, FALSE for additional, redundant constraints. */
7288 SCIP_Bool check, /**< should the constraint be checked for feasibility?
7289 * TRUE for model constraints, FALSE for additional, redundant constraints. */
7290 SCIP_Bool propagate, /**< should the constraint be propagated during node processing?
7291 * Usually set to TRUE. */
7292 SCIP_Bool local, /**< is constraint only valid locally?
7293 * Usually set to FALSE. Has to be set to TRUE, e.g., for branching constraints. */
7294 SCIP_Bool modifiable, /**< is constraint modifiable (subject to column generation)?
7295 * Usually set to FALSE. In column generation applications, set to TRUE if pricing
7296 * adds coefficients to this constraint. */
7297 SCIP_Bool dynamic, /**< is constraint subject to aging?
7298 * Usually set to FALSE. Set to TRUE for own cuts which
7299 * are seperated as constraints. */
7300 SCIP_Bool removable, /**< should the relaxation be removed from the LP due to aging or cleanup?
7301 * Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */
7302 SCIP_Bool stickingatnode /**< should the constraint always be kept at the node where it was added, even
7303 * if it may be moved to a more global node?
7304 * Usually set to FALSE. Set to TRUE to for constraints that represent node data. */
7305 )
7306 {
7307 SCIP_CONSHDLR* conshdlr;
7308 SCIP_CONSDATA* consdata;
7309
7310 assert(x != NULL);
7311 assert(z != NULL);
7312 assert(exponent > 1.0);
7313 assert(!SCIPisZero(scip, zcoef));
7314 assert(!SCIPisInfinity(scip, REALABS(zcoef)));
7315 assert(!modifiable); /* we do not support column generation */
7316
7317 /* find the absolute power constraint handler */
7318 conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
7319 if( conshdlr == NULL )
7320 {
7321 SCIPerrorMessage("absolute power constraint handler not found\n");
7322 return SCIP_PLUGINNOTFOUND;
7323 }
7324
7325 /* create constraint data */
7326 SCIP_CALL( SCIPallocBlockMemory(scip, &consdata) );
7327 BMSclearMemory(consdata);
7328 consdata->xeventfilterpos = -1;
7329 consdata->zeventfilterpos = -1;
7330
7331 consdata->x = x;
7332 consdata->z = z;
7333 consdata->xoffset = xoffset;
7334 consdata->zcoef = zcoef;
7335 consdata->lhs = lhs;
7336 consdata->rhs = rhs;
7337
7338 if( SCIPisEQ(scip, exponent, 2.0) )
7339 {
7340 consdata->exponent = 2.0;
7341 consdata->power = square;
7342 }
7343 else
7344 {
7345 consdata->exponent = exponent;
7346 consdata->power = pow;
7347 }
7348
7349 /* branching on multiaggregated variables does not seem to work well, so try to avoid multiagg. x */
7350 if( SCIPvarIsActive(x) )
7351 SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, x) );
7352
7353 /* cannot propagate on multiaggregated vars, so avoid multiagg. z */
7354 if( SCIPvarIsActive(z) )
7355 SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, z) );
7356
7357 /* create constraint */
7358 SCIP_CALL( SCIPcreateCons(scip, cons, name, conshdlr, consdata, initial, separate, enforce, check, propagate,
7359 local, modifiable, dynamic, removable, stickingatnode) );
7360
7361 return SCIP_OKAY;
7362 }
7363
7364 /** creates and captures an absolute power constraint
7365 * in its most basic version, i. e., all constraint flags are set to their basic value as explained for the
7366 * method SCIPcreateConsAbspower(); all flags can be set via SCIPsetConsFLAGNAME-methods in scip.h
7367 *
7368 * @see SCIPcreateConsAbspower() for information about the basic constraint flag configuration
7369 *
7370 * @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
7371 */
SCIPcreateConsBasicAbspower(SCIP * scip,SCIP_CONS ** cons,const char * name,SCIP_VAR * x,SCIP_VAR * z,SCIP_Real exponent,SCIP_Real xoffset,SCIP_Real zcoef,SCIP_Real lhs,SCIP_Real rhs)7372 SCIP_RETCODE SCIPcreateConsBasicAbspower(
7373 SCIP* scip, /**< SCIP data structure */
7374 SCIP_CONS** cons, /**< pointer to hold the created constraint */
7375 const char* name, /**< name of constraint */
7376 SCIP_VAR* x, /**< nonlinear variable x in constraint */
7377 SCIP_VAR* z, /**< linear variable z in constraint */
7378 SCIP_Real exponent, /**< exponent n of |x+offset|^n term in constraint */
7379 SCIP_Real xoffset, /**< offset in |x+offset|^n term in constraint */
7380 SCIP_Real zcoef, /**< coefficient of z in constraint */
7381 SCIP_Real lhs, /**< left hand side of constraint */
7382 SCIP_Real rhs /**< right hand side of constraint */
7383 )
7384 {
7385 assert(scip != NULL);
7386
7387 SCIP_CALL( SCIPcreateConsAbspower(scip, cons, name, x, z, exponent, xoffset, zcoef, lhs, rhs,
7388 TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
7389
7390 return SCIP_OKAY;
7391 }
7392
7393 /** gets the absolute power constraint as a nonlinear row representation */
SCIPgetNlRowAbspower(SCIP * scip,SCIP_CONS * cons,SCIP_NLROW ** nlrow)7394 SCIP_RETCODE SCIPgetNlRowAbspower(
7395 SCIP* scip, /**< SCIP data structure */
7396 SCIP_CONS* cons, /**< constraint */
7397 SCIP_NLROW** nlrow /**< a buffer where to store pointer to nonlinear row */
7398 )
7399 {
7400 SCIP_CONSDATA* consdata;
7401
7402 assert(cons != NULL);
7403 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0);
7404 assert(nlrow != NULL);
7405
7406 consdata = SCIPconsGetData(cons);
7407 assert(consdata != NULL);
7408
7409 if( consdata->nlrow == NULL )
7410 {
7411 SCIP_CALL( createNlRow(scip, cons) );
7412 }
7413 assert(consdata->nlrow != NULL);
7414 *nlrow = consdata->nlrow;
7415
7416 return SCIP_OKAY;
7417 }
7418
7419 /** gets nonlinear variable x in absolute power constraint */ /*lint -e715*/
SCIPgetNonlinearVarAbspower(SCIP * scip,SCIP_CONS * cons)7420 SCIP_VAR* SCIPgetNonlinearVarAbspower(
7421 SCIP* scip, /**< SCIP data structure */
7422 SCIP_CONS* cons /**< absolute power constraint */
7423 )
7424 {
7425 SCIP_CONSDATA* consdata;
7426
7427 assert(cons != NULL);
7428 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0);
7429
7430 consdata = SCIPconsGetData(cons);
7431 assert(consdata != NULL);
7432
7433 return consdata->x;
7434 }
7435
7436 /** gets linear variable z in absolute power constraint */ /*lint -e715*/
SCIPgetLinearVarAbspower(SCIP * scip,SCIP_CONS * cons)7437 SCIP_VAR* SCIPgetLinearVarAbspower(
7438 SCIP* scip, /**< SCIP data structure */
7439 SCIP_CONS* cons /**< absolute power constraint */
7440 )
7441 {
7442 SCIP_CONSDATA* consdata;
7443
7444 assert(cons != NULL);
7445 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0);
7446
7447 consdata = SCIPconsGetData(cons);
7448 assert(consdata != NULL);
7449
7450 return consdata->z;
7451 }
7452
7453 /** gets exponent in power term in absolute power constraint */
SCIPgetExponentAbspower(SCIP * scip,SCIP_CONS * cons)7454 SCIP_Real SCIPgetExponentAbspower(
7455 SCIP* scip, /**< SCIP data structure */
7456 SCIP_CONS* cons /**< absolute power constraint */
7457 )
7458 { /*lint --e{715}*/
7459 SCIP_CONSDATA* consdata;
7460
7461 assert(cons != NULL);
7462 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0);
7463
7464 consdata = SCIPconsGetData(cons);
7465 assert(consdata != NULL);
7466
7467 return consdata->exponent;
7468 }
7469
7470 /** gets offset in power term in absolute power constraint */
SCIPgetOffsetAbspower(SCIP * scip,SCIP_CONS * cons)7471 SCIP_Real SCIPgetOffsetAbspower(
7472 SCIP* scip, /**< SCIP data structure */
7473 SCIP_CONS* cons /**< absolute power constraint */
7474 )
7475 { /*lint --e{715}*/
7476 SCIP_CONSDATA* consdata;
7477
7478 assert(cons != NULL);
7479 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0);
7480
7481 consdata = SCIPconsGetData(cons);
7482 assert(consdata != NULL);
7483
7484 return consdata->xoffset;
7485 }
7486
7487 /** gets coefficient of linear variable in absolute power constraint */
SCIPgetCoefLinearAbspower(SCIP * scip,SCIP_CONS * cons)7488 SCIP_Real SCIPgetCoefLinearAbspower(
7489 SCIP* scip, /**< SCIP data structure */
7490 SCIP_CONS* cons /**< absolute power constraint */
7491 )
7492 { /*lint --e{715}*/
7493 SCIP_CONSDATA* consdata;
7494
7495 assert(cons != NULL);
7496 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0);
7497
7498 consdata = SCIPconsGetData(cons);
7499 assert(consdata != NULL);
7500
7501 return consdata->zcoef;
7502 }
7503
7504 /** gets left hand side in absolute power constraint */
SCIPgetLhsAbspower(SCIP * scip,SCIP_CONS * cons)7505 SCIP_Real SCIPgetLhsAbspower(
7506 SCIP* scip, /**< SCIP data structure */
7507 SCIP_CONS* cons /**< absolute power constraint */
7508 )
7509 { /*lint --e{715}*/
7510 SCIP_CONSDATA* consdata;
7511
7512 assert(cons != NULL);
7513 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0);
7514
7515 consdata = SCIPconsGetData(cons);
7516 assert(consdata != NULL);
7517
7518 return consdata->lhs;
7519 }
7520
7521 /** gets right hand side in absolute power constraint */
SCIPgetRhsAbspower(SCIP * scip,SCIP_CONS * cons)7522 SCIP_Real SCIPgetRhsAbspower(
7523 SCIP* scip, /**< SCIP data structure */
7524 SCIP_CONS* cons /**< absolute power constraint */
7525 )
7526 { /*lint --e{715}*/
7527 SCIP_CONSDATA* consdata;
7528
7529 assert(cons != NULL);
7530 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0);
7531
7532 consdata = SCIPconsGetData(cons);
7533 assert(consdata != NULL);
7534
7535 return consdata->rhs;
7536 }
7537
7538 /** gets the absolute violation of a absolute power constraint by a solution */
SCIPgetViolationAbspower(SCIP * scip,SCIP_CONS * cons,SCIP_SOL * sol)7539 SCIP_Real SCIPgetViolationAbspower(
7540 SCIP* scip, /**< SCIP data structure */
7541 SCIP_CONS* cons, /**< absolute power constraint */
7542 SCIP_SOL* sol /**< LP solution */
7543 )
7544 {
7545 SCIP_CONSDATA* consdata;
7546 SCIP_Real z_val;
7547 SCIP_Real x_val;
7548 SCIP_Real rhs;
7549 SCIP_Real proj_val;
7550
7551 assert(cons != NULL);
7552 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0);
7553
7554 consdata = SCIPconsGetData(cons);
7555 assert(consdata != NULL);
7556 assert(consdata->lhs == 0.0);
7557 assert(consdata->rhs == 0.0);
7558
7559 z_val = SCIPgetSolVal(scip, sol, consdata->z);
7560 x_val = SCIPgetSolVal(scip, sol, consdata->x);
7561
7562 rhs = -1.0 * consdata->zcoef * z_val;
7563 proj_val = SIGN(rhs) * pow(REALABS(rhs), 1.0 / consdata->exponent) - consdata->xoffset;
7564
7565 SCIPdebugMsg(scip, "computing slack: linear: %f, power: %f, projected: %f\n", z_val, x_val, proj_val);
7566
7567 return x_val - proj_val;
7568 }
7569
7570 /** returns whether constraint is convex w.r.t. global bounds
7571 *
7572 * @note in difference to SCIPisConvexQuadratic, we put convexity/concavity of the constraint function in relation to the constraint sides here
7573 */
SCIPisConvexAbspower(SCIP * scip,SCIP_CONS * cons)7574 SCIP_Bool SCIPisConvexAbspower(
7575 SCIP* scip, /**< SCIP data structure */
7576 SCIP_CONS* cons /**< absolute power constraint */
7577 )
7578 {
7579 SCIP_CONSDATA* consdata;
7580 SCIP_Real xlb;
7581 SCIP_Real xub;
7582
7583 assert(scip != NULL);
7584 assert(cons != NULL);
7585 assert(strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) == 0);
7586
7587 consdata = SCIPconsGetData(cons);
7588 assert(consdata != NULL);
7589
7590 xlb = SCIPvarGetLbGlobal(consdata->x);
7591 xub = SCIPvarGetLbGlobal(consdata->x);
7592
7593 /* if mixed sign, then cannot be convex */
7594 if( SCIPisNegative(scip, xlb + consdata->xoffset) && SCIPisPositive(scip, xub + consdata->xoffset) )
7595 return FALSE;
7596
7597 /* if not negative, then constraint function is like x^n, n > 1, x >= 0, i.e., convex, thus need no lhs to be convex */
7598 if( !SCIPisNegative(scip, xlb + consdata->xoffset) )
7599 return SCIPisInfinity(scip, -consdata->lhs);
7600
7601 /* if not positive, then constraint function is like -|x|^n, n > 0, x <= 0, i.e., concave, thus need no rhs to be convex */
7602 return SCIPisInfinity(scip, consdata->rhs);
7603 }
7604