SCIP Doxygen Documentation
Loading...
Searching...
No Matches
nlhdlr_quotient.c
Go to the documentation of this file.
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2/* */
3/* This file is part of the program and library */
4/* SCIP --- Solving Constraint Integer Programs */
5/* */
6/* Copyright (c) 2002-2026 Zuse Institute Berlin (ZIB) */
7/* */
8/* Licensed under the Apache License, Version 2.0 (the "License"); */
9/* you may not use this file except in compliance with the License. */
10/* You may obtain a copy of the License at */
11/* */
12/* http://www.apache.org/licenses/LICENSE-2.0 */
13/* */
14/* Unless required by applicable law or agreed to in writing, software */
15/* distributed under the License is distributed on an "AS IS" BASIS, */
16/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
17/* See the License for the specific language governing permissions and */
18/* limitations under the License. */
19/* */
20/* You should have received a copy of the Apache-2.0 license */
21/* along with SCIP; see the file LICENSE. If not visit scipopt.org. */
22/* */
23/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
24
25/**@file nlhdlr_quotient.c
26 * @ingroup DEFPLUGINS_NLHDLR
27 * @brief quotient nonlinear handler
28 * @author Benjamin Mueller
29 * @author Fabian Wegscheider
30 *
31 * @todo implement INITSEPA
32 * @todo use the convex envelope for x/y described in Tawarmalani and Sahinidis (2002) if y has a finite upper bound
33 */
34
36#include "scip/cons_nonlinear.h"
38#include "scip/nlhdlr.h"
40
41/* fundamental nonlinear handler properties */
42#define NLHDLR_NAME "quotient"
43#define NLHDLR_DESC "nonlinear handler for quotient expressions"
44#define NLHDLR_DETECTPRIORITY 20
45#define NLHDLR_ENFOPRIORITY 20
46
47/** translate from one value of infinity to another
48 *
49 * if val is ≥ infty1, then give infty2, else give val
50 */
51#define infty2infty(infty1, infty2, val) ((val) >= (infty1) ? (infty2) : (val))
52
53/*lint -e666*/
54
55/*
56 * Data structures
57 */
58
59/** nonlinear handler expression data */
60struct SCIP_NlhdlrExprData
61{
62 SCIP_EXPR* numexpr; /**< expression of the numerator */
63 SCIP_Real numcoef; /**< coefficient of the numerator */
64 SCIP_Real numconst; /**< constant of the numerator */
65 SCIP_EXPR* denomexpr; /**< expression of the denominator */
66 SCIP_Real denomcoef; /**< coefficient of the denominator */
67 SCIP_Real denomconst; /**< constant of the denominator */
68 SCIP_Real constant; /**< constant */
69};
70
71/*
72 * Local methods
73 */
74
75/** helper method to create nonlinear handler expression data */
76static
78 SCIP* scip, /**< SCIP data structure */
79 SCIP_NLHDLREXPRDATA** nlhdlrexprdata, /**< nonlinear handler expression data */
80 SCIP_EXPR* numexpr, /**< expression of the numerator */
81 SCIP_Real numcoef, /**< coefficient of the numerator */
82 SCIP_Real numconst, /**< constant of the numerator */
83 SCIP_EXPR* denomexpr, /**< expression of the denominator */
84 SCIP_Real denomcoef, /**< coefficient of the denominator */
85 SCIP_Real denomconst, /**< constant of the denominator */
86 SCIP_Real constant /**< constant */
87 )
88{
89 assert(nlhdlrexprdata != NULL);
90 assert(numexpr != NULL);
91 assert(denomexpr != NULL);
92 assert(!SCIPisZero(scip, numcoef));
93 assert(!SCIPisZero(scip, denomcoef));
94
95 /* allocate memory */
96 SCIP_CALL( SCIPallocBlockMemory(scip, nlhdlrexprdata) );
97
98 /* store values */
99 (*nlhdlrexprdata)->numexpr = numexpr;
100 (*nlhdlrexprdata)->numcoef = numcoef;
101 (*nlhdlrexprdata)->numconst = numconst;
102 (*nlhdlrexprdata)->denomexpr = denomexpr;
103 (*nlhdlrexprdata)->denomcoef = denomcoef;
104 (*nlhdlrexprdata)->denomconst = denomconst;
105 (*nlhdlrexprdata)->constant = constant;
106
107 /* capture expressions */
108 SCIPcaptureExpr(numexpr);
109 SCIPcaptureExpr(denomexpr);
110
111 return SCIP_OKAY;
112}
113
114/** helper method to free nonlinear handler expression data */
115static
117 SCIP* scip, /**< SCIP data structure */
118 SCIP_NLHDLREXPRDATA** nlhdlrexprdata /**< nonlinear handler expression data */
119 )
120{
121 assert(nlhdlrexprdata != NULL);
122 assert(*nlhdlrexprdata != NULL);
123 assert((*nlhdlrexprdata)->numexpr != NULL);
124 assert((*nlhdlrexprdata)->denomexpr != NULL);
125
126 /* release expressions */
127 SCIP_CALL( SCIPreleaseExpr(scip, &(*nlhdlrexprdata)->denomexpr) );
128 SCIP_CALL( SCIPreleaseExpr(scip, &(*nlhdlrexprdata)->numexpr) );
129
130 /* free expression data of nonlinear handler */
131 SCIPfreeBlockMemory(scip, nlhdlrexprdata);
132
133 return SCIP_OKAY;
134}
135
136/** helper method to transform an expression g(x) into a*f(x) + b */
137static
139 SCIP* scip, /**< SCIP data structure */
140 SCIP_EXPR* expr, /**< expression */
141 SCIP_EXPR** target, /**< pointer to store the expression f(x) */
142 SCIP_Real* coef, /**< pointer to store the coefficient */
143 SCIP_Real* constant /**< pointer to store the constant */
144 )
145{
146 assert(expr != NULL);
147 assert(target != NULL);
148 assert(coef != NULL);
149 assert(constant != NULL);
150
151 /* expression is a sum with one child */
152 if( SCIPisExprSum(scip, expr) && SCIPexprGetNChildren(expr) == 1 )
153 {
154 *target = SCIPexprGetChildren(expr)[0];
155 *coef = SCIPgetCoefsExprSum(expr)[0];
156 *constant = SCIPgetConstantExprSum(expr);
157 }
158 else /* otherwise return 1 * f(x) + 0 */
159 {
160 *target = expr;
161 *coef = 1.0;
162 *constant = 0.0;
163 }
164}
165
166/** helper method to detect an expression of the form (a*x + b) / (c*y + d) + e
167 *
168 * Due to the expansion of products, there are two types of expressions that can be detected:
169 *
170 * 1. prod(f(x), pow(g(y),-1))
171 * 2. sum(prod(f(x),pow(g(y),-1)), pow(g(y),-1))
172 *
173 * @todo At the moment quotients like xy / z are not detected, because they are turned into a product expression
174 * with three children, i.e., x * y * (1 / z).
175 */
176static
178 SCIP* scip, /**< SCIP data structure */
179 SCIP_EXPR* expr, /**< expression */
180 SCIP_NLHDLREXPRDATA** nlhdlrexprdata, /**< pointer to store nonlinear handler expression data */
181 SCIP_Bool* success /**< pointer to store whether nonlinear handler should be called for this expression */
182 )
183{
184 SCIP_EXPR** children;
185 SCIP_EXPR* denomexpr = NULL;
186 SCIP_EXPR* numexpr = NULL;
187 SCIP_EXPR* xexpr = NULL;
188 SCIP_EXPR* yexpr = NULL;
189 SCIP_Real a, b, c, d, e;
190 SCIP_Real nomfac = 1.0;
191 SCIP_Real numconst = 0.0;
192
193 assert(scip != NULL);
194 assert(expr != NULL);
195
196 *success = FALSE;
197 a = 0.0;
198 b = 0.0;
199 c = 0.0;
200 d = 0.0;
201 e = 0.0;
202
203 /* possible structures only have two children */
204 if( SCIPexprGetNChildren(expr) != 2 )
205 return SCIP_OKAY;
206
207 /* expression must be either a product or a sum */
208 if( !SCIPisExprProduct(scip, expr) && !SCIPisExprSum(scip, expr) )
209 return SCIP_OKAY;
210
211 children = SCIPexprGetChildren(expr);
212 assert(children != NULL);
213
214 /* case: prod(f(x), pow(g(y),-1)) */
215 if( SCIPisExprProduct(scip, expr) )
216 {
217 if( SCIPisExprPower(scip, children[0]) && SCIPgetExponentExprPow(children[0]) == -1.0 ) /*lint !e777*/
218 {
219 denomexpr = SCIPexprGetChildren(children[0])[0];
220 numexpr = children[1];
221 }
222 else if( SCIPisExprPower(scip, children[1]) && SCIPgetExponentExprPow(children[1]) == -1.0 ) /*lint !e777*/
223 {
224 denomexpr = SCIPexprGetChildren(children[1])[0];
225 numexpr = children[0];
226 }
227
228 /* remember to scale the numerator by the coefficient stored in the product expression */
229 nomfac = SCIPgetCoefExprProduct(expr);
230 }
231 /* case: sum(prod(f(x),pow(g(y),-1)), pow(g(y),-1)) */
232 else
233 {
234 SCIP_Real* sumcoefs;
235
236 assert(SCIPisExprSum(scip, expr));
237 sumcoefs = SCIPgetCoefsExprSum(expr);
238
239 /* children[0] is 1/g(y) and children[1] is a product of f(x) and 1/g(y) */
240 if( SCIPisExprPower(scip, children[0]) && SCIPgetExponentExprPow(children[0]) == -1.0
241 && SCIPisExprProduct(scip, children[1]) && SCIPexprGetNChildren(children[1]) == 2 ) /* lint !e777 */
242 {
243 SCIP_Real prodcoef = SCIPgetCoefExprProduct(children[1]);
244
245 if( children[0] == SCIPexprGetChildren(children[1])[0] )
246 {
247 denomexpr = SCIPexprGetChildren(children[0])[0];
248 numexpr = SCIPexprGetChildren(children[1])[1];
249 }
250 else if( children[0] == SCIPexprGetChildren(children[1])[1] )
251 {
252 denomexpr = SCIPexprGetChildren(children[0])[0];
253 numexpr = SCIPexprGetChildren(children[1])[0];
254 }
255
256 /* remember scalar and constant for numerator */
257 nomfac = sumcoefs[1] * prodcoef;
258 numconst = sumcoefs[0];
259 }
260 /* children[1] is 1/g(y) and children[0] is a product of f(x) and 1/g(y) */
261 else if( SCIPisExprPower(scip, children[1]) && SCIPgetExponentExprPow(children[1]) == -1.0
262 && SCIPisExprProduct(scip, children[0]) && SCIPexprGetNChildren(children[0]) == 2 ) /* lint !e777 */
263 {
264 SCIP_Real prodcoef = SCIPgetCoefExprProduct(children[0]);
265
266 if( children[1] == SCIPexprGetChildren(children[0])[0] )
267 {
268 denomexpr = SCIPexprGetChildren(children[1])[0];
269 numexpr = SCIPexprGetChildren(children[0])[1];
270 }
271 else if( children[1] == SCIPexprGetChildren(children[0])[1] )
272 {
273 denomexpr = SCIPexprGetChildren(children[1])[0];
274 numexpr = SCIPexprGetChildren(children[0])[0];
275 }
276
277 /* remember scalar and constant for numerator */
278 nomfac = sumcoefs[0] * prodcoef;
279 numconst = sumcoefs[1];
280 }
281
282 /* remember the constant of the sum expression */
283 e = SCIPgetConstantExprSum(expr);
284 }
285
286 if( denomexpr != NULL && numexpr != NULL )
287 {
288 /* transform numerator and denominator to detect structures like (a * f(x) + b) / (c * f(x) + d) */
289 transformExpr(scip, numexpr, &xexpr, &a, &b);
290 transformExpr(scip, denomexpr, &yexpr, &c, &d);
291
292 SCIPdebugMsg(scip, "detected numerator (%g * %p + %g) and denominator (%g * %p + %g)\n", a, (void*)xexpr, b,
293 c, (void*)yexpr, d);
294
295 /* detection is only be successful if the expression of the numerator an denominator are the same
296 * (so boundtightening can be stronger than default) or we are going to provide estimators (there will be an auxvar)
297 */
298 *success = (xexpr == yexpr) || (SCIPgetExprNAuxvarUsesNonlinear(expr) > 0);
299
300#ifdef SCIP_DEBUG
301 SCIPinfoMessage(scip, NULL, "Expression for numerator: ");
302 SCIP_CALL( SCIPprintExpr(scip, xexpr, NULL) );
303 SCIPinfoMessage(scip, NULL, "\nExpression for denominator: ");
304 SCIP_CALL( SCIPprintExpr(scip, yexpr, NULL) );
305 SCIPinfoMessage(scip, NULL, "\n");
306#endif
307 }
308
309 /* register usage of xexpr and yexpr
310 * create nonlinear handler expression data
311 */
312 if( *success )
313 {
314 assert(xexpr != NULL);
315 assert(xexpr != NULL);
316 assert(a != 0.0);
317 assert(c != 0.0);
318
319 assert(SCIPgetExprNAuxvarUsesNonlinear(expr) > 0 || xexpr == yexpr);
320
321 /* request auxiliary variables for xexpr and yexpr if we will estimate
322 * mark that the bounds of the expression are important to construct the estimators
323 * (TODO check the curvature of the univariate quotient, as bounds may actually not be used)
324 * if univariate, then we also do inteval and reverseprop, so mark that the activities will be used for inteval
325 */
328 xexpr == yexpr,
331
332 if( xexpr != yexpr && SCIPgetExprNAuxvarUsesNonlinear(expr) > 0 )
333 {
335 }
336
337 a = nomfac * a;
338 b = nomfac * b + numconst;
339
342 SCIPdebugMsg(scip, "detected quotient expression (%g * %p + %g) / (%g * %p + %g) + %g\n", a, (void*)xexpr,
343 b, c, (void*)yexpr, d, e);
344 SCIP_CALL( exprdataCreate(scip, nlhdlrexprdata, xexpr, a, b, yexpr, c, d, e) );
345 }
346
347 return SCIP_OKAY;
348}
349
350/** helper method to compute interval for (a x + b) / (c x + d) + e */
351static
353 SCIP* scip, /**< SCIP data structure */
354 SCIP_INTERVAL bnds, /**< bounds on x */
355 SCIP_Real a, /**< coefficient in numerator */
356 SCIP_Real b, /**< constant in numerator */
357 SCIP_Real c, /**< coefficient in denominator */
358 SCIP_Real d, /**< constant in denominator */
359 SCIP_Real e /**< constant */
360 )
361{
363 SCIP_INTERVAL denominterval;
364 SCIP_INTERVAL numinterval;
365 int i;
366
367 assert(scip != NULL);
368
369 /* return empty interval if the domain of x is empty */
371 {
373 return result;
374 }
375
376 /* compute bounds for denominator */
377 SCIPintervalMulScalar(SCIP_INTERVAL_INFINITY, &denominterval, bnds, c);
378 SCIPintervalAddScalar(SCIP_INTERVAL_INFINITY, &denominterval, denominterval, d);
379
380 /* there is no useful interval if 0 is in the interior of the interval of the denominator */
381 if( SCIPintervalGetInf(denominterval) < 0.0 && SCIPintervalGetSup(denominterval) > 0.0 )
382 {
384 return result;
385 }
386
387 /* a d = b c implies that f(x) = b / d + e, i.e., f is constant */
388 if( a*d - b*c == 0.0 )
389 {
390 SCIPintervalSet(&result, b / d + e);
391 return result;
392 }
393
394 /*
395 * evaluate for [x.inf,x.inf] and [x.sup,x.sup] independently
396 */
398
399 for( i = 0; i < 2; ++i )
400 {
401 SCIP_INTERVAL quotinterval;
402 SCIP_Real val = (i == 0) ? bnds.inf : bnds.sup;
403
404 /* set the resulting interval to a / c if the bounds is infinite */
405 if( SCIPisInfinity(scip, REALABS(val)) )
406 {
407 SCIPintervalSet(&quotinterval, a);
408 SCIPintervalDivScalar(SCIP_INTERVAL_INFINITY, &quotinterval, quotinterval, c);
409 }
410 else
411 {
412 /* a x' + b */
413 SCIPintervalSet(&numinterval, val);
414 SCIPintervalMulScalar(SCIP_INTERVAL_INFINITY, &numinterval, numinterval, a);
415 SCIPintervalAddScalar(SCIP_INTERVAL_INFINITY, &numinterval, numinterval, b);
416
417 /* c x' + d */
418 SCIPintervalSet(&denominterval, val);
419 SCIPintervalMulScalar(SCIP_INTERVAL_INFINITY, &denominterval, denominterval, c);
420 SCIPintervalAddScalar(SCIP_INTERVAL_INFINITY, &denominterval, denominterval, d);
421
422 /* (a x' + b) / (c x' + d) + e */
423 SCIPintervalDiv(SCIP_INTERVAL_INFINITY, &quotinterval, numinterval, denominterval);
424 SCIPintervalAddScalar(SCIP_INTERVAL_INFINITY, &quotinterval, quotinterval, e);
425 }
426
427 /* unify with the resulting interval */
428 SCIPintervalUnify(&result, result, quotinterval);
429 }
430
431 return result;
432}
433
434/** helper method to compute reverse propagation for (a x + b) / (c x + d) + e */
435static
437 SCIP_INTERVAL bnds, /**< bounds on (a x + b) / (c x + d) + e */
438 SCIP_Real a, /**< coefficient in numerator */
439 SCIP_Real b, /**< constant in numerator */
440 SCIP_Real c, /**< coefficient in denominator */
441 SCIP_Real d, /**< constant in denominator */
442 SCIP_Real e /**< constant */
443 )
444{
446 int i;
447
449
450 /* return empty interval if the domain of the expression is empty */
452 return result;
453
454 /* substract constant from bounds of the expression */
456
457 /* if the expression is constant or the limit lies inside the domain, nothing can be propagated */
458 if( a*d - b*c == 0.0 || (bnds.inf < a / c && bnds.sup > a / c) )
459 {
461 return result;
462 }
463
464 /* compute bounds for [x.inf,x.inf] and [x.sup,x.sup] independently */
465 for( i = 0; i < 2; ++i )
466 {
467 SCIP_INTERVAL denominator;
468 SCIP_INTERVAL numerator;
469 SCIP_INTERVAL quotient;
470 SCIP_Real val = (i == 0) ? bnds.inf : bnds.sup;
471
472 /* (d * x' - b) */
473 SCIPintervalSet(&numerator, d);
474 SCIPintervalMulScalar(SCIP_INTERVAL_INFINITY, &numerator, numerator, val);
475 SCIPintervalAddScalar(SCIP_INTERVAL_INFINITY, &numerator, numerator, -b);
476
477 /* (a - c * x') */
478 SCIPintervalSet(&denominator, -c);
479 SCIPintervalMulScalar(SCIP_INTERVAL_INFINITY, &denominator, denominator, val);
480 SCIPintervalAddScalar(SCIP_INTERVAL_INFINITY, &denominator, denominator, a);
481
482 /* (d * x' - b) / (a - c * x') */
483 SCIPintervalDiv(SCIP_INTERVAL_INFINITY, &quotient, numerator, denominator);
484
485 /* unify with the resulting interval */
486 SCIPintervalUnify(&result, result, quotient);
487 }
488
489 return result;
490}
491
492/** adds data to given rowprep; the generated estimator is always locally valid
493 *
494 * @note the constant is moved to the left- or right-hand side
495 * @note other than the name of this function may indicate, it does not create a rowprep
496 */
497static
499 SCIP* scip, /**< SCIP data structure */
500 SCIP_ROWPREP* rowprep, /**< a rowprep where to store the estimator */
501 SCIP_VAR** vars, /**< variables */
502 SCIP_Real* coefs, /**< coefficients */
503 SCIP_Real constant, /**< constant */
504 int nlinvars /**< total number of variables */
505 )
506{
507 assert(scip != NULL);
508 assert(rowprep != NULL);
509 assert(coefs != NULL);
510 assert(vars != NULL);
511
512 /* create rowprep */
513 SCIProwprepAddSide(rowprep, -constant);
514 SCIP_CALL( SCIPensureRowprepSize(scip, rowprep, nlinvars + 1) );
515
516 /* add coefficients */
517 SCIP_CALL( SCIPaddRowprepTerms(scip, rowprep, nlinvars, vars, coefs) );
518
519 return SCIP_OKAY;
520}
521
522/** computes an estimator at a given point for the univariate case (ax + b) / (cx + d) + e
523 *
524 * Depending on the reference point, the estimator is a tangent or a secant on the graph.
525 * It depends on whether we are under- or overestimating, whether we are on the left or
526 * on the right side of the singularity at -d/c, and whether it is the monotone increasing
527 * (ad - bc > 0) or decreasing part (ad - bc < 0). Together, there are 8 cases:
528 *
529 * - mon. incr. + overestimate + left hand side --> secant
530 * - mon. incr. + overestimate + right hand side --> tangent
531 * - mon. incr. + understimate + left hand side --> tangent
532 * - mon. incr. + understimate + right hand side --> secant
533 * - mon. decr. + overestimate + left hand side --> tangent
534 * - mon. decr. + overestimate + right hand side --> secant
535 * - mon. decr. + understimate + left hand side --> secant
536 * - mon. decr. + understimate + right hand side --> tangent
537 */
538static
540 SCIP* scip, /**< SCIP data structure */
541 SCIP_Real lbx, /**< local lower bound of x */
542 SCIP_Real ubx, /**< local upper bound of x */
543 SCIP_Real gllbx, /**< global lower bound of x */
544 SCIP_Real glubx, /**< global upper bound of x */
545 SCIP_Real solx, /**< solution value of x */
546 SCIP_Real a, /**< coefficient in numerator */
547 SCIP_Real b, /**< constant in numerator */
548 SCIP_Real c, /**< coefficient in denominator */
549 SCIP_Real d, /**< constant in denominator */
550 SCIP_Real e, /**< constant */
551 SCIP_Real* coef, /**< pointer to store the coefficient */
552 SCIP_Real* constant, /**< pointer to store the constant */
553 SCIP_Bool overestimate, /**< whether the expression should be overestimated */
554 SCIP_Bool* local, /**< pointer to store whether the estimate is locally valid */
555 SCIP_Bool* branchinguseful, /**< pointer to store whether branching on the expression would improve the estimator */
556 SCIP_Bool* success /**< buffer to store whether separation was successful */
557 )
558{
559 SCIP_Real singularity;
560 SCIP_Bool isinleftpart;
561 SCIP_Bool monincreasing;
562
563 assert(lbx <= solx && solx <= ubx);
564 assert(coef != NULL);
565 assert(constant != NULL);
566 assert(local != NULL);
567 assert(branchinguseful != NULL);
568 assert(success != NULL);
569
570 *branchinguseful = TRUE;
571 *success = FALSE;
572 *coef = 0.0;
573 *constant = 0.0;
574 singularity = -d / c;
575
576 /* estimate is globally valid if local and global bounds are equal */
577 *local = gllbx != lbx || glubx != ubx; /*lint !e777*/
578
579 /* if 0 is in the denom interval, estimation is not possible */
580 if( SCIPisLE(scip, lbx, singularity) && SCIPisGE(scip, ubx, singularity) )
581 return SCIP_OKAY;
582
583 isinleftpart = (ubx < singularity);
584 monincreasing = (a * d - b * c > 0.0);
585
586 /* this encodes the 8 cases explained above */
587 if( monincreasing == (overestimate == isinleftpart) )
588 {
589 SCIP_Real lbeval;
590 SCIP_Real ubeval;
591
592 /* if one of the bounds is infinite, secant cannot be computed */
593 if( SCIPisInfinity(scip, -lbx) || SCIPisInfinity(scip, ubx) )
594 return SCIP_OKAY;
595
596 lbeval = (a * lbx + b) / (c * lbx + d) + e;
597 ubeval = (a * ubx + b) / (c * ubx + d) + e;
598
599 /* compute coefficient and constant of linear estimator */
600 *coef = (ubeval - lbeval) / (ubx - lbx);
601 *constant = ubeval - (*coef) * ubx;
602 }
603 else
604 {
605 SCIP_Real soleval;
606
607 soleval = (a * solx + b) / (c * solx + d) + e;
608
609 /* compute coefficient and constant of linear estimator */
610 *coef = (a * d - b * c) / SQR(d + c * solx);
611 *constant = soleval - (*coef) * solx;
612
613 /* gradient cuts are globally valid if the singularity is not in [gllbx,glubx] */
614 *local = SCIPisLE(scip, gllbx, singularity) && SCIPisGE(scip, glubx, singularity);
615
616 /* branching will not improve the convexification via tangent cuts */
617 *branchinguseful = FALSE;
618 }
619
620 /* avoid huge values in the cut */
621 if( SCIPisHugeValue(scip, REALABS(*coef)) || SCIPisHugeValue(scip, REALABS(*constant)) )
622 return SCIP_OKAY;
623
624 *success = TRUE;
625
626 return SCIP_OKAY;
627}
628
629/** helper method to compute estimator for the univariate case; the estimator is stored in a given rowprep */
630static
632 SCIP* scip, /**< SCIP data structure */
633 SCIP_SOL* sol, /**< solution point (or NULL for the LP solution) */
634 SCIP_EXPR* xexpr, /**< argument expression */
635 SCIP_Real a, /**< coefficient in numerator */
636 SCIP_Real b, /**< constant in numerator */
637 SCIP_Real c, /**< coefficient in denominator */
638 SCIP_Real d, /**< constant in denominator */
639 SCIP_Real e, /**< constant */
640 SCIP_Bool overestimate, /**< whether the expression should be overestimated */
641 SCIP_ROWPREP* rowprep, /**< a rowprep where to store the estimator */
642 SCIP_Bool* branchinguseful, /**< pointer to store whether branching on the expression would improve the estimator */
643 SCIP_Bool* success /**< buffer to store whether separation was successful */
644 )
645{
646 SCIP_VAR* x;
647 SCIP_Real constant;
648 SCIP_Real coef;
649 SCIP_Real gllbx;
650 SCIP_Real glubx;
651 SCIP_Real lbx;
652 SCIP_Real ubx;
653 SCIP_Real solx;
654 SCIP_Bool local;
655 SCIP_INTERVAL bnd;
656
657 assert(rowprep != NULL);
658 assert(branchinguseful != NULL);
659 assert(success != NULL);
660
662
663 /* get local bounds on xexpr */
669 lbx = bnd.inf;
670 ubx = bnd.sup;
671
672 /* check whether variable has been fixed or has empty interval */
673 if( SCIPisEQ(scip, lbx, ubx) || ubx < lbx )
674 {
675 *success = FALSE;
676 return SCIP_OKAY;
677 }
678
679 /* get global variable bounds */
680 gllbx = SCIPvarGetLbGlobal(x);
681 glubx = SCIPvarGetUbGlobal(x);
682
683 /* get and adjust solution value */
684 solx = SCIPgetSolVal(scip, sol, x);
685 solx = MIN(MAX(solx, lbx), ubx);
686
687 /* compute an estimator */
688 SCIP_CALL( estimateUnivariate(scip, lbx, ubx, gllbx, glubx, solx, a, b, c, d, e, &coef, &constant, overestimate, &local, branchinguseful, success) );
689
690 /* add estimator to rowprep, if successful */
691 if( *success )
692 {
693 (void) SCIPsnprintf(SCIProwprepGetName(rowprep), SCIP_MAXSTRLEN, "quot_%s_%lld", SCIPvarGetName(x), SCIPgetNLPs(scip));
694 SCIP_CALL( createRowprep(scip, rowprep, &x, &coef, constant, 1) );
695 SCIProwprepSetLocal(rowprep, local);
696 }
697
698 return SCIP_OKAY;
699}
700
701/** helper method to compute a gradient cut for
702 * \f[
703 * h^c(x,y) := \frac{1}{y} \left(\frac{x + \sqrt{\text{lbx}\cdot\text{ubx}}}{\sqrt{\text{lbx}} + \sqrt{\text{ubx}}}\right)^2
704 * \f]
705 * at a given reference point
706 *
707 * See Zamora and Grossmann (1988) for more details.
708 */
709static
711 SCIP_Real lbx, /**< lower bound of x */
712 SCIP_Real ubx, /**< upper bound of x */
713 SCIP_Real solx, /**< solution value of x */
714 SCIP_Real soly, /**< solution value of y */
715 SCIP_Real* coefx, /**< pointer to store the coefficient of x */
716 SCIP_Real* coefy, /**< pointer to store the coefficient of y */
717 SCIP_Real* constant /**< pointer to store the constant */
718 )
719{
720 SCIP_Real tmp1;
721 SCIP_Real tmp2;
722
723 assert(lbx >= 0.0);
724 assert(lbx <= ubx);
725 assert(soly > 0.0);
726 assert(coefx != NULL);
727 assert(coefy != NULL);
728 assert(constant != NULL);
729
730 tmp1 = sqrt(lbx * ubx) + solx;
731 tmp2 = SQR(sqrt(lbx) + sqrt(ubx)) * soly; /*lint !e666*/
732 assert(tmp2 > 0.0);
733
734 *coefx = 2.0 * tmp1 / tmp2;
735 *coefy = -SQR(tmp1) / (tmp2 * soly);
736 *constant = 2.0 * sqrt(lbx * ubx) * tmp1 / tmp2;
737}
738
739/** computes an over- or underestimator at a given point for the bivariate case x/y &le;/&ge; z
740 *
741 * There are the following cases for y > 0:
742 *
743 * 1. lbx < 0 < ubx:
744 * Rewrite x / y = z as x = y * z and use McCormick to compute a valid inequality of the form
745 * x = y * z &le; a * y + b * z + c. Note that b > 0 because of y > 0. The inequality is then transformed
746 * to x / b - a/b * y - c/b &le; z, which results in a valid underestimator for x / y over the set
747 * {(x,y) | lbz &le; x / y &le; ubz}. Note that overestimating/underestimating the bilinear term with McCormick
748 * results in an underestimator/overestimator for x / y.
749 *
750 * 2. lbx &ge; 0 or ubx &le; 0:
751 * - overestimation: use \f$z \leq \frac{1}{\text{lby}\cdot\text{uby}} \min(\text{uby}\cdot x - \text{lbx}\cdot y + \text{lbx}\cdot\text{lby}, \text{lby}\cdot x - \text{ubx}\cdot y + \text{ubx}\cdot\text{uby})\f$
752 * - underestimation: use \f$z \geq x/y \geq \frac{1}{y} \frac{x + \sqrt{\text{lbx}\cdot\text{ubx}}}{\sqrt{\text{lbx} + \sqrt{\text{ubx}}}}\f$ and build gradient cut
753 *
754 * If y < 0, swap and negate its bounds and compute the respective opposite estimator (and negate it).
755 *
756 * If 0 is in the interval of y, nothing is possible.
757 */
758static
760 SCIP* scip, /**< SCIP data structure */
761 SCIP_Real lbx, /**< lower bound of x */
762 SCIP_Real ubx, /**< upper bound of x */
763 SCIP_Real lby, /**< lower bound of y */
764 SCIP_Real uby, /**< upper bound of y */
765 SCIP_Real lbz, /**< lower bound of z */
766 SCIP_Real ubz, /**< lower bound of z */
767 SCIP_Real solx, /**< reference point for x */
768 SCIP_Real soly, /**< reference point for y */
769 SCIP_Real solz, /**< reference point for z */
770 SCIP_Bool overestimate, /**< whether the expression should be overestimated */
771 SCIP_Real* coefx, /**< pointer to store the x coefficient */
772 SCIP_Real* coefy, /**< pointer to store the y coefficient */
773 SCIP_Real* constant, /**< pointer to store the constant */
774 SCIP_Bool* branchingusefulx, /**< pointer to store whether branching on x would improve the estimator */
775 SCIP_Bool* branchingusefuly, /**< pointer to store whether branching on y would improve the estimator */
776 SCIP_Bool* success /**< buffer to store whether computing the estimator was successful */
777 )
778{
779 SCIP_Bool negatedx = FALSE;
780 SCIP_Bool negatedy = FALSE;
781
782 assert(lbx <= solx && solx <= ubx);
783 assert(lby <= soly && soly <= uby);
784 assert(lbz <= solz && solz <= ubz);
785 assert(coefx != NULL);
786 assert(coefy != NULL);
787 assert(constant != NULL);
788 assert(branchingusefulx != NULL);
789 assert(branchingusefuly != NULL);
790 assert(success != NULL);
791
792 *branchingusefulx = TRUE;
793 *branchingusefuly = TRUE;
794 *success = TRUE;
795 *coefx = 0.0;
796 *coefy = 0.0;
797 *constant = 0.0;
798
799 /* if 0 is in [lby,uby], then it is not possible to compute an estimator */
800 if( SCIPisLE(scip, lby, 0.0) && SCIPisGE(scip, uby, 0.0) )
801 {
802 *success = FALSE;
803 return SCIP_OKAY;
804 }
805
806 /* negate bounds of y if it is not positive */
807 if( uby < 0.0 )
808 {
809 SCIP_Real tmp = uby;
810
811 uby = -lby;
812 lby = -tmp;
813 soly = -soly;
814 negatedy = TRUE;
815 overestimate = !overestimate;
816 }
817
818 /* case 1: 0 is in the interior of [lbx,ubx] */
819 if( lbx < 0.0 && 0.0 < ubx )
820 {
821 SCIP_Real mccoefy = 0.0;
822 SCIP_Real mccoefaux = 0.0;
823 SCIP_Real mcconst = 0.0;
824
825 /* as explained in the description of this method, overestimating/underestimating the bilinear term results in an
826 * underestimator/overestimator for x / y
827 */
828 SCIPaddBilinMcCormick(scip, 1.0, lbz, ubz, solz, lby, uby, soly, !overestimate, &mccoefaux, &mccoefy, &mcconst,
829 success);
830 assert(mccoefaux >= 0.0);
831
832 if( !(*success) )
833 return SCIP_OKAY;
834
835 /* resulting estimator is x/b - a/b * y - c/b, where a*y + b*z + c is the estimator for y*z */
836 *coefx = 1.0 / mccoefaux;
837 *coefy = -mccoefy / mccoefaux;
838 *constant = -mcconst / mccoefaux;
839 }
840 /* case 2: 0 is not in the interior of [lbx,ubx] */
841 else
842 {
843 /* negate bounds of x if it is negative */
844 if( ubx <= 0.0 )
845 {
846 SCIP_Real tmp = ubx;
847
848 ubx = -lbx;
849 lbx = -tmp;
850 solx = -solx;
851 negatedx = TRUE;
852 overestimate = !overestimate;
853 }
854
855 /* case 2a */
856 if( overestimate )
857 {
858 /* check where the minimum is attained */
859 if( uby * solx - lbx * soly + lbx * lby <= lby * solx - ubx * soly + ubx * uby )
860 {
861 *coefx = 1.0 / lby;
862 *coefy = -lbx / (lby * uby);
863 *constant = lbx / uby;
864 }
865 else
866 {
867 *coefx = 1.0 / uby;
868 *coefy = -ubx / (lby * uby);
869 *constant = ubx / lby;
870 }
871 }
872 /* case 2b */
873 else
874 {
875 /* compute gradient cut for h^c(x,y) at (solx,soly) */
876 hcGradCut(lbx, ubx, solx, soly, coefx, coefy, constant);
877
878 /* estimator is independent of the bounds of y */
879 *branchingusefuly = FALSE;
880 }
881 }
882
883 /* reverse negations of x and y in the resulting estimator */
884 if( negatedx )
885 *coefx = -(*coefx);
886 if( negatedy )
887 *coefy = -(*coefy);
888
889 /* if exactly one variable has been negated, then we have computed an underestimate/overestimate for the negated
890 * expression, which results in an overestimate/underestimate for the original expression
891 */
892 if( negatedx != negatedy )
893 {
894 *coefx = -(*coefx);
895 *coefy = -(*coefy);
896 *constant = -(*constant);
897 }
898
899 /* avoid huge values in the estimator */
900 if( SCIPisHugeValue(scip, REALABS(*coefx)) || SCIPisHugeValue(scip, REALABS(*coefy))
901 || SCIPisHugeValue(scip, REALABS(*constant)) )
902 {
903 *success = FALSE;
904 return SCIP_OKAY;
905 }
906
907 return SCIP_OKAY;
908}
909
910/** construct an estimator for a quotient expression of the form (ax + b) / (cy + d) + e
911 *
912 * The resulting estimator is stored in a rowprep.
913 *
914 * The method first computes an estimator for x' / y' with x := ax + b and y := cy + d
915 * and then transforms this estimator to one for the quotient (ax + b) / (cy + d) + e.
916 */
917static
919 SCIP* scip, /**< SCIP data structure */
920 SCIP_EXPR* xexpr, /**< numerator expression */
921 SCIP_EXPR* yexpr, /**< denominator expression */
922 SCIP_VAR* auxvar, /**< auxiliary variable */
923 SCIP_SOL* sol, /**< solution point (or NULL for the LP solution) */
924 SCIP_Real a, /**< coefficient of numerator */
925 SCIP_Real b, /**< constant of numerator */
926 SCIP_Real c, /**< coefficient of denominator */
927 SCIP_Real d, /**< constant of denominator */
928 SCIP_Real e, /**< constant term */
929 SCIP_Bool overestimate, /**< whether the expression should be overestimated */
930 SCIP_ROWPREP* rowprep, /**< a rowprep where to store the estimator */
931 SCIP_Bool* branchingusefulx, /**< pointer to store whether branching on x would improve the estimator */
932 SCIP_Bool* branchingusefuly, /**< pointer to store whether branching on y would improve the estimator */
933 SCIP_Bool* success /**< buffer to store whether separation was successful */
934 )
935{
936 SCIP_VAR* vars[2];
937 SCIP_Real coefs[2] = {0.0, 0.0};
938 SCIP_Real constant = 0.0;
939 SCIP_Real solx;
940 SCIP_Real soly;
941 SCIP_Real solz;
942 SCIP_Real lbx;
943 SCIP_Real ubx;
944 SCIP_Real lby;
945 SCIP_Real uby;
946 SCIP_Real lbz;
947 SCIP_Real ubz;
948 SCIP_INTERVAL bnd;
949
950 assert(xexpr != NULL);
951 assert(yexpr != NULL);
952 assert(xexpr != yexpr);
953 assert(auxvar != NULL);
954 assert(rowprep != NULL);
955 assert(branchingusefulx != NULL);
956 assert(branchingusefuly != NULL);
957 assert(success != NULL);
958
961
962 /* get bounds for x, y, and z */
968 lbx = bnd.inf;
969 ubx = bnd.sup;
970
976 lby = bnd.inf;
977 uby = bnd.sup;
978
979 lbz = SCIPvarGetLbLocal(auxvar);
980 ubz = SCIPvarGetUbLocal(auxvar);
981
982 /* check whether one of the variables has been fixed or has empty domain */
983 if( SCIPisEQ(scip, lbx, ubx) || SCIPisEQ(scip, lby, uby) || ubx < lbx || uby < lby )
984 {
985 *success = FALSE;
986 return SCIP_OKAY;
987 }
988
989 /* get and adjust solution values */
990 solx = SCIPgetSolVal(scip, sol, vars[0]);
991 soly = SCIPgetSolVal(scip, sol, vars[1]);
992 solz = SCIPgetSolVal(scip, sol, auxvar);
993 solx = MIN(MAX(solx, lbx), ubx);
994 soly = MIN(MAX(soly, lby), uby);
995 solz = MIN(MAX(solz, lbz), ubz);
996
997 /* compute an estimator */
999 MIN(a * lbx, a * ubx) + b, MAX(a * lbx, a * ubx) + b, /* bounds of x' */
1000 MIN(c * lby, c * uby) + d, MAX(c * lby, c * uby) + d, /* bounds of y' */
1001 lbz, ubz, a * solx + b, c * soly + d, solz, overestimate, &coefs[0], &coefs[1], &constant,
1002 branchingusefulx, branchingusefuly, success) );
1003
1004 /* add estimator to rowprep, if successful */
1005 if( *success )
1006 {
1007 /* transform estimator Ax' + By'+ C = A(ax + b) + B (cy + d) + C = (Aa) x + (Bc) y + (C + Ab + Bd);
1008 * add the constant e separately
1009 */
1010 constant += coefs[0] * b + coefs[1] * d + e;
1011 coefs[0] *= a;
1012 coefs[1] *= c;
1013
1014 /* prepare rowprep */
1015 (void) SCIPsnprintf(SCIProwprepGetName(rowprep), SCIP_MAXSTRLEN, "quot_%s_%s_%lld", SCIPvarGetName(vars[0]), SCIPvarGetName(vars[1]),
1016 SCIPgetNLPs(scip));
1017 SCIP_CALL( createRowprep(scip, rowprep, vars, coefs, constant, 2) );
1018 }
1019
1020 return SCIP_OKAY;
1021}
1022
1023/*
1024 * Callback methods of nonlinear handler
1025 */
1026
1027/** nonlinear handler copy callback */
1028static
1029SCIP_DECL_NLHDLRCOPYHDLR(nlhdlrCopyhdlrQuotient)
1030{ /*lint --e{715}*/
1031 assert(targetscip != NULL);
1032 assert(sourcenlhdlr != NULL);
1033
1035
1036 SCIP_CALL( SCIPincludeNlhdlrQuotient(targetscip) );
1037
1038 return SCIP_OKAY;
1039}
1040
1041
1042/** callback to free expression specific data */
1043static
1044SCIP_DECL_NLHDLRFREEEXPRDATA(nlhdlrFreeExprDataQuotient)
1045{ /*lint --e{715}*/
1046 assert(nlhdlrexprdata != NULL);
1047 assert(*nlhdlrexprdata != NULL);
1048
1049 /* free expression data of nonlinear handler */
1050 SCIP_CALL( exprdataFree(scip, nlhdlrexprdata) );
1051
1052 return SCIP_OKAY;
1053}
1054
1055
1056/** callback to detect structure in expression tree */
1057static
1058SCIP_DECL_NLHDLRDETECT(nlhdlrDetectQuotient)
1059{ /*lint --e{715}*/
1060 SCIP_Bool success;
1061
1062 assert(nlhdlrexprdata != NULL);
1063
1064 /* call detection routine */
1065 SCIP_CALL( detectExpr(scip, expr, nlhdlrexprdata, &success) );
1066
1067 if( success )
1068 {
1069 if( SCIPgetExprNAuxvarUsesNonlinear(expr) > 0 )
1070 *participating = SCIP_NLHDLR_METHOD_SEPABOTH;
1071
1072 if( (*nlhdlrexprdata)->numexpr == (*nlhdlrexprdata)->denomexpr )
1073 {
1074 /* if univariate, then we also do inteval and reverseprop */
1075 *participating |= SCIP_NLHDLR_METHOD_ACTIVITY;
1076
1077 /* if univariate, then all our methods are enforcing */
1078 *enforcing |= *participating;
1079 }
1080 }
1081
1082 return SCIP_OKAY;
1083}
1084
1085
1086/** auxiliary evaluation callback of nonlinear handler */
1087static
1088SCIP_DECL_NLHDLREVALAUX(nlhdlrEvalauxQuotient)
1089{ /*lint --e{715}*/
1090 SCIP_VAR* auxvarx;
1091 SCIP_VAR* auxvary;
1092 SCIP_Real solvalx;
1093 SCIP_Real solvaly;
1094 SCIP_Real nomval;
1095 SCIP_Real denomval;
1096
1097 assert(expr != NULL);
1098 assert(auxvalue != NULL);
1099
1100 /**! [SnippetNlhdlrEvalauxQuotient] */
1101 /* get auxiliary variables */
1102 auxvarx = SCIPgetExprAuxVarNonlinear(nlhdlrexprdata->numexpr);
1103 auxvary = SCIPgetExprAuxVarNonlinear(nlhdlrexprdata->denomexpr);
1104 assert(auxvarx != NULL);
1105 assert(auxvary != NULL);
1106
1107 /* get solution values of the auxiliary variables */
1108 solvalx = SCIPgetSolVal(scip, sol, auxvarx);
1109 solvaly = SCIPgetSolVal(scip, sol, auxvary);
1110
1111 /* evaluate expression w.r.t. the values of the auxiliary variables */
1112 nomval = nlhdlrexprdata->numcoef * solvalx + nlhdlrexprdata->numconst;
1113 denomval = nlhdlrexprdata->denomcoef * solvaly + nlhdlrexprdata->denomconst;
1114
1115 /* return SCIP_INVALID if the denominator evaluates to zero */
1116 *auxvalue = (denomval != 0.0) ? nlhdlrexprdata->constant + nomval / denomval : SCIP_INVALID;
1117 /**! [SnippetNlhdlrEvalauxQuotient] */
1118
1119 return SCIP_OKAY;
1120}
1121
1122
1123/** nonlinear handler under/overestimation callback
1124 *
1125 * @todo which of the paramters did I not use, but have to be taken into consideration?
1126*/
1127static
1128SCIP_DECL_NLHDLRESTIMATE(nlhdlrEstimateQuotient)
1129{ /*lint --e{715}*/
1130 SCIP_Bool branchingusefulx = FALSE;
1131 SCIP_Bool branchingusefuly = FALSE;
1132 SCIP_ROWPREP* rowprep;
1133
1134 assert(nlhdlr != NULL);
1135 assert(expr != NULL);
1136 assert(nlhdlrexprdata != NULL);
1137 assert(rowpreps != NULL);
1138
1139 /** ![SnippetNlhdlrEstimateQuotient] */
1140 *addedbranchscores = FALSE;
1141 *success = FALSE;
1142
1144
1145 if( nlhdlrexprdata->numexpr == nlhdlrexprdata->denomexpr )
1146 {
1147 /* univariate case */
1148 SCIP_CALL( estimateUnivariateQuotient(scip, sol, nlhdlrexprdata->numexpr, nlhdlrexprdata->numcoef, nlhdlrexprdata->numconst,
1149 nlhdlrexprdata->denomcoef, nlhdlrexprdata->denomconst, nlhdlrexprdata->constant, overestimate, rowprep,
1150 &branchingusefulx, success) );
1151 }
1152 else
1153 {
1154 /* bivariate case */
1155 SCIP_CALL( estimateBivariateQuotient(scip, nlhdlrexprdata->numexpr, nlhdlrexprdata->denomexpr, SCIPgetExprAuxVarNonlinear(expr), sol,
1156 nlhdlrexprdata->numcoef, nlhdlrexprdata->numconst, nlhdlrexprdata->denomcoef, nlhdlrexprdata->denomconst,
1157 nlhdlrexprdata->constant, overestimate, rowprep,
1158 &branchingusefulx, &branchingusefuly, success) );
1159 }
1160
1161 if( *success )
1162 {
1163 SCIP_CALL( SCIPsetPtrarrayVal(scip, rowpreps, 0, rowprep) );
1164 }
1165 else
1166 {
1167 SCIPfreeRowprep(scip, &rowprep);
1168 }
1169
1170 /* add branching scores if requested */
1171 if( addbranchscores )
1172 {
1173 SCIP_EXPR* exprs[2];
1174 SCIP_Real violation;
1175 int nexprs = 0;
1176
1177 if( branchingusefulx )
1178 exprs[nexprs++] = nlhdlrexprdata->numexpr;
1179 if( branchingusefuly )
1180 exprs[nexprs++] = nlhdlrexprdata->denomexpr;
1181
1182 /* compute violation w.r.t. the auxiliary variable(s) */
1183#ifndef BRSCORE_ABSVIOL
1184 SCIP_CALL( SCIPgetExprRelAuxViolationNonlinear(scip, expr, auxvalue, sol, &violation, NULL, NULL) );
1185#else
1186 SCIP_CALL( SCIPgetExprAbsAuxViolationNonlinear(scip, expr, auxvalue, sol, &violation, NULL, NULL) );
1187#endif
1188 assert(violation > 0.0); /* there should be a violation if we were called to enforce */
1189
1190 SCIP_CALL( SCIPaddExprsViolScoreNonlinear(scip, exprs, nexprs, violation, sol, addedbranchscores) );
1191 }
1192 /** ![SnippetNlhdlrEstimateQuotient] */
1193
1194 return SCIP_OKAY;
1195}
1196
1197/** nonlinear handler solution linearization callback */
1198static
1199SCIP_DECL_NLHDLRSOLLINEARIZE(nlhdlrSollinearizeQuotient)
1200{ /*lint --e{715}*/
1201 SCIP_VAR* x;
1202 SCIP_Real lbx;
1203 SCIP_Real ubx;
1204 SCIP_Real solx;
1205 int c;
1206
1207 assert(nlhdlr != NULL);
1208 assert(expr != NULL);
1209 assert(nlhdlrexprdata != NULL);
1210
1211 /* in the bivariate case, we do not get globally valid estimators */
1212 if( nlhdlrexprdata->numexpr != nlhdlrexprdata->denomexpr )
1213 return SCIP_OKAY;
1214
1215 x = SCIPgetExprAuxVarNonlinear(nlhdlrexprdata->numexpr);
1216 lbx = SCIPvarGetLbGlobal(x);
1217 ubx = SCIPvarGetUbGlobal(x);
1218 solx = SCIPgetSolVal(scip, sol, x);
1219 solx = MAX(MIN(solx, ubx), lbx);
1220
1221 for( c = (overestimate ? 0 : 1); c < (underestimate ? 2 : 1); ++c ) /* c == 0: overestimate, c == 1: underestimate */
1222 {
1223 SCIP_ROWPREP* rowprep;
1224 SCIP_Bool success = FALSE;
1225 SCIP_Bool local = TRUE;
1226 SCIP_Bool branchinguseful;
1227 SCIP_Real coef;
1228 SCIP_Real constant;
1229
1230 /* compute estimator, will be secant or gradient */
1231 SCIP_CALL( estimateUnivariate(scip, lbx, ubx, lbx, ubx, solx,
1232 nlhdlrexprdata->numcoef, nlhdlrexprdata->numconst, nlhdlrexprdata->denomcoef, nlhdlrexprdata->denomconst, nlhdlrexprdata->constant,
1233 &coef, &constant, c == 0, &local, &branchinguseful, &success) );
1234
1235 /* skip if not successful, only locally valid, or just a secant (branchinguseful is TRUE) */
1236 if( !success || local || branchinguseful )
1237 continue;
1238
1240 SCIP_CALL( SCIPaddRowprepTerm(scip, rowprep, x, coef) );
1242 SCIProwprepAddConstant(rowprep, constant);
1243 (void) SCIPsnprintf(SCIProwprepGetName(rowprep), SCIP_MAXSTRLEN, "quot_%s_sol%d", SCIPvarGetName(x), SCIPsolGetIndex(sol));
1244
1245 SCIP_CALL( SCIPcleanupRowprep2(scip, rowprep, sol, SCIPgetHugeValue(scip), &success) );
1246
1247 /* if cleanup succeeded and rowprep is still global, add to cutpool */
1248 if( success && !SCIProwprepIsLocal(rowprep) )
1249 {
1250 SCIP_ROW* row;
1251
1252 SCIP_CALL( SCIPgetRowprepRowCons(scip, &row, rowprep, cons) );
1253 SCIP_CALL( SCIPaddPoolCut(scip, row) );
1254 SCIP_CALL( SCIPreleaseRow(scip, &row) );
1255 }
1256
1257 SCIPfreeRowprep(scip, &rowprep);
1258 }
1259
1260 return SCIP_OKAY;
1261}
1262
1263/** nonlinear handler interval evaluation callback */
1264static
1265SCIP_DECL_NLHDLRINTEVAL(nlhdlrIntevalQuotient)
1266{ /*lint --e{715}*/
1267 SCIP_INTERVAL bnds;
1268
1269 assert(nlhdlrexprdata != NULL);
1270 assert(nlhdlrexprdata->numexpr != NULL);
1271 assert(nlhdlrexprdata->denomexpr != NULL);
1272
1273 /* it is not possible to compute tighter intervals if both expressions are different
1274 * we should not be called in this case, as we haven't said we would participate in this activity in detect
1275 */
1276 assert(nlhdlrexprdata->numexpr == nlhdlrexprdata->denomexpr);
1277
1278 /**! [SnippetNlhdlrIntevalQuotient] */
1279 /* get activity of the numerator (= denominator) expression */
1280 bnds = SCIPexprGetActivity(nlhdlrexprdata->numexpr);
1281
1282 /* call interval evaluation for the univariate quotient expression */
1283 *interval = intEvalQuotient(scip, bnds, nlhdlrexprdata->numcoef, nlhdlrexprdata->numconst,
1284 nlhdlrexprdata->denomcoef, nlhdlrexprdata->denomconst, nlhdlrexprdata->constant);
1285 /**! [SnippetNlhdlrIntevalQuotient] */
1286
1287 return SCIP_OKAY;
1288}
1289
1290
1291/** nonlinear handler callback for reverse propagation */
1292static
1293SCIP_DECL_NLHDLRREVERSEPROP(nlhdlrReversepropQuotient)
1294{ /*lint --e{715}*/
1296
1297 assert(nlhdlrexprdata != NULL);
1298 assert(nlhdlrexprdata->numexpr != NULL);
1299 assert(nlhdlrexprdata->denomexpr != NULL);
1300
1301 /* it is not possible to compute tighter intervals if both expressions are different
1302 * we should not be called in this case, as we haven't said we would participate in this activity in detect
1303 */
1304 assert(nlhdlrexprdata->numexpr == nlhdlrexprdata->denomexpr);
1305
1306 SCIPdebugMsg(scip, "call reverse propagation for expression (%g %p + %g) / (%g %p + %g) + %g bounds [%g,%g]\n",
1307 nlhdlrexprdata->numcoef, (void*)nlhdlrexprdata->numexpr, nlhdlrexprdata->numconst,
1308 nlhdlrexprdata->denomcoef, (void*)nlhdlrexprdata->denomexpr, nlhdlrexprdata->denomconst,
1309 nlhdlrexprdata->constant, bounds.inf, bounds.sup);
1310
1311 /* call reverse propagation */
1312 /**! [SnippetNlhdlrReversepropQuotient] */
1313 result = reversepropQuotient(bounds, nlhdlrexprdata->numcoef, nlhdlrexprdata->numconst,
1314 nlhdlrexprdata->denomcoef, nlhdlrexprdata->denomconst, nlhdlrexprdata->constant);
1315
1316 SCIPdebugMsg(scip, "try to tighten bounds of %p: [%g,%g] -> [%g,%g]\n",
1317 (void*)nlhdlrexprdata->numexpr, SCIPgetExprBoundsNonlinear(scip, nlhdlrexprdata->numexpr).inf,
1318 SCIPgetExprBoundsNonlinear(scip, nlhdlrexprdata->numexpr).sup, result.inf, result.sup);
1319
1320 /* tighten bounds of the expression */
1321 SCIP_CALL( SCIPtightenExprIntervalNonlinear(scip, nlhdlrexprdata->numexpr, result, infeasible, nreductions) );
1322 /**! [SnippetNlhdlrReversepropQuotient] */
1323
1324 return SCIP_OKAY;
1325}
1326
1327
1328/*
1329 * nonlinear handler specific interface methods
1330 */
1331
1332/** includes quotient nonlinear handler in nonlinear constraint handler */
1334 SCIP* scip /**< SCIP data structure */
1335 )
1336{
1337 SCIP_NLHDLRDATA* nlhdlrdata;
1338 SCIP_NLHDLR* nlhdlr;
1339
1340 assert(scip != NULL);
1341
1342 /* create nonlinear handler data */
1343 nlhdlrdata = NULL;
1344
1346 NLHDLR_ENFOPRIORITY, nlhdlrDetectQuotient, nlhdlrEvalauxQuotient, nlhdlrdata) );
1347 assert(nlhdlr != NULL);
1348
1349 SCIPnlhdlrSetCopyHdlr(nlhdlr, nlhdlrCopyhdlrQuotient);
1350 SCIPnlhdlrSetFreeExprData(nlhdlr, nlhdlrFreeExprDataQuotient);
1351 SCIPnlhdlrSetSepa(nlhdlr, NULL, NULL, nlhdlrEstimateQuotient, NULL);
1352 SCIPnlhdlrSetSollinearize(nlhdlr, nlhdlrSollinearizeQuotient);
1353 SCIPnlhdlrSetProp(nlhdlr, nlhdlrIntevalQuotient, nlhdlrReversepropQuotient);
1354
1355 return SCIP_OKAY;
1356}
SCIP_VAR * a
SCIP_VAR ** b
SCIP_VAR ** x
constraint handler for nonlinear constraints specified by algebraic expressions
#define NULL
Definition def.h:257
#define SCIP_MAXSTRLEN
Definition def.h:278
#define SCIP_INVALID
Definition def.h:187
#define SCIP_INTERVAL_INFINITY
Definition def.h:189
#define SCIP_Bool
Definition def.h:100
#define MIN(x, y)
Definition def.h:233
#define SCIP_STRINGEQ(name, reference, retcode)
Definition def.h:454
#define SCIP_Real
Definition def.h:165
#define SQR(x)
Definition def.h:208
#define TRUE
Definition def.h:102
#define FALSE
Definition def.h:103
#define MAX(x, y)
Definition def.h:229
#define REALABS(x)
Definition def.h:191
#define SCIP_CALL(x)
Definition def.h:364
unsigned int SCIPgetExprNAuxvarUsesNonlinear(SCIP_EXPR *expr)
SCIP_RETCODE SCIPgetExprRelAuxViolationNonlinear(SCIP *scip, SCIP_EXPR *expr, SCIP_Real auxvalue, SCIP_SOL *sol, SCIP_Real *viol, SCIP_Bool *violunder, SCIP_Bool *violover)
SCIP_VAR * SCIPgetExprAuxVarNonlinear(SCIP_EXPR *expr)
SCIP_RETCODE SCIPtightenExprIntervalNonlinear(SCIP *scip, SCIP_EXPR *expr, SCIP_INTERVAL newbounds, SCIP_Bool *cutoff, int *ntightenings)
SCIP_RETCODE SCIPaddExprsViolScoreNonlinear(SCIP *scip, SCIP_EXPR **exprs, int nexprs, SCIP_Real violscore, SCIP_SOL *sol, SCIP_Bool *success)
SCIP_RETCODE SCIPregisterExprUsageNonlinear(SCIP *scip, SCIP_EXPR *expr, SCIP_Bool useauxvar, SCIP_Bool useactivityforprop, SCIP_Bool useactivityforsepabelow, SCIP_Bool useactivityforsepaabove)
SCIP_INTERVAL SCIPgetExprBoundsNonlinear(SCIP *scip, SCIP_EXPR *expr)
SCIP_RETCODE SCIPgetExprAbsAuxViolationNonlinear(SCIP *scip, SCIP_EXPR *expr, SCIP_Real auxvalue, SCIP_SOL *sol, SCIP_Real *viol, SCIP_Bool *violunder, SCIP_Bool *violover)
void SCIPinfoMessage(SCIP *scip, FILE *file, const char *formatstr,...)
#define SCIPdebugMsg
void SCIPaddBilinMcCormick(SCIP *scip, SCIP_Real bilincoef, SCIP_Real lbx, SCIP_Real ubx, SCIP_Real refpointx, SCIP_Real lby, SCIP_Real uby, SCIP_Real refpointy, SCIP_Bool overestimate, SCIP_Real *lincoefx, SCIP_Real *lincoefy, SCIP_Real *linconstant, SCIP_Bool *success)
SCIP_RETCODE SCIPincludeNlhdlrQuotient(SCIP *scip)
SCIP_RETCODE SCIPaddPoolCut(SCIP *scip, SCIP_ROW *row)
Definition scip_cut.c:336
SCIP_RETCODE SCIPsetPtrarrayVal(SCIP *scip, SCIP_PTRARRAY *ptrarray, int idx, void *val)
int SCIPexprGetNChildren(SCIP_EXPR *expr)
Definition expr.c:3872
SCIP_Real SCIPgetExponentExprPow(SCIP_EXPR *expr)
Definition expr_pow.c:3449
SCIP_Bool SCIPisExprProduct(SCIP *scip, SCIP_EXPR *expr)
Definition scip_expr.c:1490
SCIP_Bool SCIPisExprSum(SCIP *scip, SCIP_EXPR *expr)
Definition scip_expr.c:1479
SCIP_Real * SCIPgetCoefsExprSum(SCIP_EXPR *expr)
Definition expr_sum.c:1554
SCIP_Real SCIPgetCoefExprProduct(SCIP_EXPR *expr)
SCIP_RETCODE SCIPreleaseExpr(SCIP *scip, SCIP_EXPR **expr)
Definition scip_expr.c:1443
SCIP_RETCODE SCIPprintExpr(SCIP *scip, SCIP_EXPR *expr, FILE *file)
Definition scip_expr.c:1512
SCIP_Bool SCIPisExprPower(SCIP *scip, SCIP_EXPR *expr)
Definition scip_expr.c:1501
SCIP_EXPR ** SCIPexprGetChildren(SCIP_EXPR *expr)
Definition expr.c:3882
SCIP_Real SCIPgetConstantExprSum(SCIP_EXPR *expr)
Definition expr_sum.c:1569
SCIP_INTERVAL SCIPexprGetActivity(SCIP_EXPR *expr)
Definition expr.c:4028
void SCIPcaptureExpr(SCIP_EXPR *expr)
Definition scip_expr.c:1435
SCIP_RETCODE SCIPevalExprActivity(SCIP *scip, SCIP_EXPR *expr)
Definition scip_expr.c:1742
void SCIPintervalIntersectEps(SCIP_INTERVAL *resultant, SCIP_Real eps, SCIP_INTERVAL operand1, SCIP_INTERVAL operand2)
SCIP_Real SCIPintervalGetInf(SCIP_INTERVAL interval)
void SCIPintervalSetEntire(SCIP_Real infinity, SCIP_INTERVAL *resultant)
void SCIPintervalUnify(SCIP_INTERVAL *resultant, SCIP_INTERVAL operand1, SCIP_INTERVAL operand2)
void SCIPintervalSubScalar(SCIP_Real infinity, SCIP_INTERVAL *resultant, SCIP_INTERVAL operand1, SCIP_Real operand2)
void SCIPintervalSet(SCIP_INTERVAL *resultant, SCIP_Real value)
SCIP_Bool SCIPintervalIsEmpty(SCIP_Real infinity, SCIP_INTERVAL operand)
void SCIPintervalSetBounds(SCIP_INTERVAL *resultant, SCIP_Real inf, SCIP_Real sup)
struct SCIP_Interval SCIP_INTERVAL
void SCIPintervalMulScalar(SCIP_Real infinity, SCIP_INTERVAL *resultant, SCIP_INTERVAL operand1, SCIP_Real operand2)
void SCIPintervalDiv(SCIP_Real infinity, SCIP_INTERVAL *resultant, SCIP_INTERVAL operand1, SCIP_INTERVAL operand2)
void SCIPintervalAddScalar(SCIP_Real infinity, SCIP_INTERVAL *resultant, SCIP_INTERVAL operand1, SCIP_Real operand2)
void SCIPintervalDivScalar(SCIP_Real infinity, SCIP_INTERVAL *resultant, SCIP_INTERVAL operand1, SCIP_Real operand2)
SCIP_Real SCIPintervalGetSup(SCIP_INTERVAL interval)
void SCIPintervalSetEmpty(SCIP_INTERVAL *resultant)
#define SCIPfreeBlockMemory(scip, ptr)
Definition scip_mem.h:108
#define SCIPallocBlockMemory(scip, ptr)
Definition scip_mem.h:89
void SCIPnlhdlrSetFreeExprData(SCIP_NLHDLR *nlhdlr,)
Definition nlhdlr.c:99
const char * SCIPnlhdlrGetName(SCIP_NLHDLR *nlhdlr)
Definition nlhdlr.c:167
void SCIPnlhdlrSetSollinearize(SCIP_NLHDLR *nlhdlr,)
Definition nlhdlr.c:155
void SCIPnlhdlrSetSepa(SCIP_NLHDLR *nlhdlr, SCIP_DECL_NLHDLRINITSEPA((*initsepa)), SCIP_DECL_NLHDLRENFO((*enfo)), SCIP_DECL_NLHDLRESTIMATE((*estimate)),)
Definition nlhdlr.c:137
void SCIPnlhdlrSetCopyHdlr(SCIP_NLHDLR *nlhdlr,)
Definition nlhdlr.c:77
SCIP_RETCODE SCIPincludeNlhdlrNonlinear(SCIP *scip, SCIP_NLHDLR **nlhdlr, const char *name, const char *desc, int detectpriority, int enfopriority, SCIP_DECL_NLHDLRDETECT((*detect)), SCIP_DECL_NLHDLREVALAUX((*evalaux)), SCIP_NLHDLRDATA *nlhdlrdata)
void SCIPnlhdlrSetProp(SCIP_NLHDLR *nlhdlr, SCIP_DECL_NLHDLRINTEVAL((*inteval)),)
Definition nlhdlr.c:124
SCIP_RETCODE SCIPreleaseRow(SCIP *scip, SCIP_ROW **row)
Definition scip_lp.c:1508
int SCIPsolGetIndex(SCIP_SOL *sol)
Definition sol.c:4305
SCIP_Real SCIPgetSolVal(SCIP *scip, SCIP_SOL *sol, SCIP_VAR *var)
Definition scip_sol.c:1763
SCIP_Longint SCIPgetNLPs(SCIP *scip)
SCIP_Real SCIPinfinity(SCIP *scip)
SCIP_Bool SCIPisGE(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
SCIP_Bool SCIPisLE(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
SCIP_Bool SCIPisHugeValue(SCIP *scip, SCIP_Real val)
SCIP_Bool SCIPisInfinity(SCIP *scip, SCIP_Real val)
SCIP_Real SCIPgetHugeValue(SCIP *scip)
SCIP_Bool SCIPisEQ(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
SCIP_Bool SCIPisZero(SCIP *scip, SCIP_Real val)
SCIP_Real SCIPepsilon(SCIP *scip)
SCIP_Real SCIPvarGetUbLocal(SCIP_VAR *var)
Definition var.c:24300
SCIP_Real SCIPvarGetUbGlobal(SCIP_VAR *var)
Definition var.c:24174
const char * SCIPvarGetName(SCIP_VAR *var)
Definition var.c:23299
SCIP_Real SCIPvarGetLbLocal(SCIP_VAR *var)
Definition var.c:24266
SCIP_Real SCIPvarGetLbGlobal(SCIP_VAR *var)
Definition var.c:24152
SCIP_RETCODE SCIPcleanupRowprep2(SCIP *scip, SCIP_ROWPREP *rowprep, SCIP_SOL *sol, SCIP_Real maxcoefbound, SCIP_Bool *success)
SCIP_RETCODE SCIPensureRowprepSize(SCIP *scip, SCIP_ROWPREP *rowprep, int size)
char * SCIProwprepGetName(SCIP_ROWPREP *rowprep)
SCIP_Bool SCIProwprepIsLocal(SCIP_ROWPREP *rowprep)
void SCIProwprepAddConstant(SCIP_ROWPREP *rowprep, SCIP_Real constant)
SCIP_RETCODE SCIPaddRowprepTerm(SCIP *scip, SCIP_ROWPREP *rowprep, SCIP_VAR *var, SCIP_Real coef)
SCIP_RETCODE SCIPgetRowprepRowCons(SCIP *scip, SCIP_ROW **row, SCIP_ROWPREP *rowprep, SCIP_CONS *cons)
SCIP_RETCODE SCIPcreateRowprep(SCIP *scip, SCIP_ROWPREP **rowprep, SCIP_SIDETYPE sidetype, SCIP_Bool local)
SCIP_RETCODE SCIPaddRowprepTerms(SCIP *scip, SCIP_ROWPREP *rowprep, int nvars, SCIP_VAR **vars, SCIP_Real *coefs)
void SCIProwprepSetLocal(SCIP_ROWPREP *rowprep, SCIP_Bool islocal)
void SCIProwprepAddSide(SCIP_ROWPREP *rowprep, SCIP_Real side)
void SCIPfreeRowprep(SCIP *scip, SCIP_ROWPREP **rowprep)
int SCIPsnprintf(char *t, int len, const char *s,...)
Definition misc.c:10827
return SCIP_OKAY
int c
static SCIP_SOL * sol
assert(minobj< SCIPgetCutoffbound(scip))
static SCIP_VAR ** vars
private functions of nonlinear handlers of nonlinear constraints
#define NLHDLR_DETECTPRIORITY
#define NLHDLR_ENFOPRIORITY
#define NLHDLR_DESC
#define NLHDLR_NAME
bilinear nonlinear handler
static void hcGradCut(SCIP_Real lbx, SCIP_Real ubx, SCIP_Real solx, SCIP_Real soly, SCIP_Real *coefx, SCIP_Real *coefy, SCIP_Real *constant)
static void transformExpr(SCIP *scip, SCIP_EXPR *expr, SCIP_EXPR **target, SCIP_Real *coef, SCIP_Real *constant)
static SCIP_RETCODE exprdataCreate(SCIP *scip, SCIP_NLHDLREXPRDATA **nlhdlrexprdata, SCIP_EXPR *numexpr, SCIP_Real numcoef, SCIP_Real numconst, SCIP_EXPR *denomexpr, SCIP_Real denomcoef, SCIP_Real denomconst, SCIP_Real constant)
static SCIP_RETCODE exprdataFree(SCIP *scip, SCIP_NLHDLREXPRDATA **nlhdlrexprdata)
static SCIP_RETCODE estimateUnivariate(SCIP *scip, SCIP_Real lbx, SCIP_Real ubx, SCIP_Real gllbx, SCIP_Real glubx, SCIP_Real solx, SCIP_Real a, SCIP_Real b, SCIP_Real c, SCIP_Real d, SCIP_Real e, SCIP_Real *coef, SCIP_Real *constant, SCIP_Bool overestimate, SCIP_Bool *local, SCIP_Bool *branchinguseful, SCIP_Bool *success)
static SCIP_INTERVAL reversepropQuotient(SCIP_INTERVAL bnds, SCIP_Real a, SCIP_Real b, SCIP_Real c, SCIP_Real d, SCIP_Real e)
static SCIP_RETCODE estimateBivariateQuotient(SCIP *scip, SCIP_EXPR *xexpr, SCIP_EXPR *yexpr, SCIP_VAR *auxvar, SCIP_SOL *sol, SCIP_Real a, SCIP_Real b, SCIP_Real c, SCIP_Real d, SCIP_Real e, SCIP_Bool overestimate, SCIP_ROWPREP *rowprep, SCIP_Bool *branchingusefulx, SCIP_Bool *branchingusefuly, SCIP_Bool *success)
static SCIP_INTERVAL intEvalQuotient(SCIP *scip, SCIP_INTERVAL bnds, SCIP_Real a, SCIP_Real b, SCIP_Real c, SCIP_Real d, SCIP_Real e)
#define infty2infty(infty1, infty2, val)
static SCIP_RETCODE estimateBivariate(SCIP *scip, SCIP_Real lbx, SCIP_Real ubx, SCIP_Real lby, SCIP_Real uby, SCIP_Real lbz, SCIP_Real ubz, SCIP_Real solx, SCIP_Real soly, SCIP_Real solz, SCIP_Bool overestimate, SCIP_Real *coefx, SCIP_Real *coefy, SCIP_Real *constant, SCIP_Bool *branchingusefulx, SCIP_Bool *branchingusefuly, SCIP_Bool *success)
static SCIP_RETCODE createRowprep(SCIP *scip, SCIP_ROWPREP *rowprep, SCIP_VAR **vars, SCIP_Real *coefs, SCIP_Real constant, int nlinvars)
static SCIP_RETCODE estimateUnivariateQuotient(SCIP *scip, SCIP_SOL *sol, SCIP_EXPR *xexpr, SCIP_Real a, SCIP_Real b, SCIP_Real c, SCIP_Real d, SCIP_Real e, SCIP_Bool overestimate, SCIP_ROWPREP *rowprep, SCIP_Bool *branchinguseful, SCIP_Bool *success)
static SCIP_RETCODE detectExpr(SCIP *scip, SCIP_EXPR *expr, SCIP_NLHDLREXPRDATA **nlhdlrexprdata, SCIP_Bool *success)
quotient nonlinear handler
#define SCIPdebug(x)
Definition pub_message.h:93
preparation of a linear inequality to become a SCIP_ROW
SCIP_Real sup
SCIP_Real inf
struct SCIP_Expr SCIP_EXPR
Definition type_expr.h:55
struct SCIP_Row SCIP_ROW
Definition type_lp.h:105
@ SCIP_SIDETYPE_RIGHT
Definition type_lp.h:66
@ SCIP_SIDETYPE_LEFT
Definition type_lp.h:65
struct SCIP_RowPrep SCIP_ROWPREP
Definition type_misc.h:173
#define SCIP_DECL_NLHDLREVALAUX(x)
#define SCIP_DECL_NLHDLRESTIMATE(x)
struct SCIP_NlhdlrData SCIP_NLHDLRDATA
#define SCIP_NLHDLR_METHOD_SEPABOTH
Definition type_nlhdlr.h:53
#define SCIP_DECL_NLHDLRCOPYHDLR(x)
Definition type_nlhdlr.h:70
#define SCIP_NLHDLR_METHOD_ACTIVITY
Definition type_nlhdlr.h:54
#define SCIP_DECL_NLHDLRSOLLINEARIZE(x)
#define SCIP_DECL_NLHDLRFREEEXPRDATA(x)
Definition type_nlhdlr.h:94
#define SCIP_DECL_NLHDLRDETECT(x)
struct SCIP_Nlhdlr SCIP_NLHDLR
struct SCIP_NlhdlrExprData SCIP_NLHDLREXPRDATA
#define SCIP_DECL_NLHDLRREVERSEPROP(x)
#define SCIP_DECL_NLHDLRINTEVAL(x)
@ SCIP_INVALIDCALL
enum SCIP_Retcode SCIP_RETCODE
struct Scip SCIP
Definition type_scip.h:39
struct SCIP_Sol SCIP_SOL
Definition type_sol.h:57
struct SCIP_Var SCIP_VAR
Definition type_var.h:166