1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the program and library */
4 /* SCIP --- Solving Constraint Integer Programs */
5 /* */
6 /* Copyright (C) 2002-2022 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 scip.zib.de. */
13 /* */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15
16 /**@file expr_exp.c
17 * @ingroup DEFPLUGINS_EXPR
18 * @brief exponential expression handler
19 * @author Stefan Vigerske
20 * @author Benjamin Mueller
21 * @author Ksenia Bestuzheva
22 */
23
24 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
25
26 #define _USE_MATH_DEFINES /* to get M_E on Windows */ /*lint !750 */
27
28 #include <string.h>
29 #include <math.h>
30
31 #include "scip/expr_exp.h"
32 #include "scip/expr_value.h"
33
34 #define EXPRHDLR_NAME "exp"
35 #define EXPRHDLR_DESC "exponential expression"
36 #define EXPRHDLR_PRECEDENCE 85000
37 #define EXPRHDLR_HASHKEY SCIPcalcFibHash(10181.0)
38
39 /*
40 * Data structures
41 */
42
43 /*
44 * Local methods
45 */
46
47 /** computes coefficients of secant of an exponential term */
48 static
49 void addExpSecant(
50 SCIP* scip, /**< SCIP data structure */
51 SCIP_Real lb, /**< lower bound on variable */
52 SCIP_Real ub, /**< upper bound on variable */
53 SCIP_Real* lincoef, /**< buffer to add coefficient of secant */
54 SCIP_Real* linconstant, /**< buffer to add constant of secant */
55 SCIP_Bool* success /**< buffer to set to FALSE if secant has failed due to large numbers or unboundedness */
56 )
57 {
58 SCIP_Real coef;
59 SCIP_Real constant;
60
61 assert(scip != NULL);
62 assert(!SCIPisInfinity(scip, lb));
63 assert(!SCIPisInfinity(scip, -ub));
64 assert(SCIPisLE(scip, lb, ub));
65 assert(lincoef != NULL);
66 assert(linconstant != NULL);
67 assert(success != NULL);
68
69 if( SCIPisInfinity(scip, -lb) || SCIPisInfinity(scip, ub) )
70 {
71 /* unboundedness */
72 *success = FALSE;
73 return;
74 }
75
76 /* if lb and ub are too close use a safe secant */
77 if( SCIPisEQ(scip, lb, ub) )
78 {
79 coef = 0.0;
80 constant = exp(ub);
81 }
82 else
83 {
84 coef = (exp(ub) - exp(lb)) / (ub - lb);
85 constant = exp(ub) - coef * ub;
86 }
87
88 if( SCIPisInfinity(scip, REALABS(coef)) || SCIPisInfinity(scip, REALABS(constant)) )
89 {
90 *success = FALSE;
91 return;
92 }
93
94 *lincoef += coef;
95 *linconstant += constant;
96 }
97
98 /** computes coefficients of linearization of an exponential term in a reference point */
99 static
100 void addExpLinearization(
101 SCIP* scip, /**< SCIP data structure */
102 SCIP_Real refpoint, /**< point for which to compute value of linearization */
103 SCIP_Bool isint, /**< whether corresponding variable is a discrete variable, and thus linearization could be moved */
104 SCIP_Real* lincoef, /**< buffer to add coefficient of secant */
105 SCIP_Real* linconstant, /**< buffer to add constant of secant */
106 SCIP_Bool* success /**< buffer to set to FALSE if secant has failed due to large numbers or unboundedness */
107 )
108 {
109 SCIP_Real constant;
110 SCIP_Real coef;
111
112 assert(scip != NULL);
113 assert(lincoef != NULL);
114 assert(linconstant != NULL);
115 assert(success != NULL);
116
117 if( SCIPisInfinity(scip, REALABS(refpoint)) )
118 {
119 *success = FALSE;
120 return;
121 }
122
123 if( !isint || SCIPisIntegral(scip, refpoint) )
124 {
125 coef = exp(refpoint);
126 constant = exp(refpoint) * (1.0 - refpoint);
127 }
128 else
129 {
130 /* exp(x) -> secant between f=floor(refpoint) and f+1 = ((e-1)*e^f) * x + e^f - f * ((e-1)*e^f) */
131 SCIP_Real f;
132
133 f = SCIPfloor(scip, refpoint);
134
135 coef = (M_E - 1.0) * exp(f);
136 constant = exp(f) - f * coef;
137 }
138
139 if( SCIPisInfinity(scip, REALABS(coef)) || SCIPisInfinity(scip, REALABS(constant)) )
140 {
141 *success = FALSE;
142 return;
143 }
144
145 *lincoef += coef;
146 *linconstant += constant;
147 }
148
149 /*
150 * Callback methods of expression handler
151 */
152
153 /** simplifies an exp expression
154 *
155 * Evaluates the exponential function when its child is a value expression.
156 *
157 * TODO: exp(log(*)) = *
158 */
159 static
160 SCIP_DECL_EXPRSIMPLIFY(simplifyExp)
161 {
162 SCIP_EXPR* child;
163
164 assert(scip != NULL);
165 assert(expr != NULL);
166 assert(simplifiedexpr != NULL);
167 assert(SCIPexprGetNChildren(expr) == 1);
168
169 child = SCIPexprGetChildren(expr)[0];
170 assert(child != NULL);
171
172 /**! [SnippetExprSimplifyExp] */
173 /* check for value expression */
174 if( SCIPisExprValue(scip, child) )
175 {
176 SCIP_CALL( SCIPcreateExprValue(scip, simplifiedexpr, exp(SCIPgetValueExprValue(child)), ownercreate, ownercreatedata) );
177 }
178 else
179 {
180 *simplifiedexpr = expr;
181
182 /* we have to capture it, since it must simulate a "normal" simplified call in which a new expression is created */
183 SCIPcaptureExpr(*simplifiedexpr);
184 }
185 /**! [SnippetExprSimplifyExp] */
186
187 return SCIP_OKAY;
188 }
189
190 /** expression handler copy callback */
191 static
192 SCIP_DECL_EXPRCOPYHDLR(copyhdlrExp)
193 { /*lint --e{715}*/
194 SCIP_CALL( SCIPincludeExprhdlrExp(scip) );
195
196 return SCIP_OKAY;
197 }
198
199 /** expression data copy callback */
200 static
201 SCIP_DECL_EXPRCOPYDATA(copydataExp)
202 { /*lint --e{715}*/
203 assert(targetexprdata != NULL);
204 assert(sourceexpr != NULL);
205 assert(SCIPexprGetData(sourceexpr) == NULL);
206
207 *targetexprdata = NULL;
208
209 return SCIP_OKAY;
210 }
211
212 /** expression data free callback */
213 static
214 SCIP_DECL_EXPRFREEDATA(freedataExp)
215 { /*lint --e{715}*/
216 assert(expr != NULL);
217
218 SCIPexprSetData(expr, NULL);
219
220 return SCIP_OKAY;
221 }
222
223 /** expression parse callback */
224 static
225 SCIP_DECL_EXPRPARSE(parseExp)
226 { /*lint --e{715}*/
227 SCIP_EXPR* childexpr;
228
229 assert(expr != NULL);
230
231 /**! [SnippetExprParseExp] */
232 /* parse child expression from remaining string */
233 SCIP_CALL( SCIPparseExpr(scip, &childexpr, string, endstring, ownercreate, ownercreatedata) );
234 assert(childexpr != NULL);
235
236 /* create exponential expression */
237 SCIP_CALL( SCIPcreateExprExp(scip, expr, childexpr, ownercreate, ownercreatedata) );
238 assert(*expr != NULL);
239
240 /* release child expression since it has been captured by the exponential expression */
241 SCIP_CALL( SCIPreleaseExpr(scip, &childexpr) );
242
243 *success = TRUE;
244 /**! [SnippetExprParseExp] */
245
246 return SCIP_OKAY;
247 }
248
249 /** expression point evaluation callback */
250 static
251 SCIP_DECL_EXPREVAL(evalExp)
252 { /*lint --e{715}*/
253 assert(expr != NULL);
254 assert(SCIPexprGetData(expr) == NULL);
255 assert(SCIPexprGetNChildren(expr) == 1);
256 assert(SCIPexprGetEvalValue(SCIPexprGetChildren(expr)[0]) != SCIP_INVALID); /*lint !e777*/
257
258 *val = exp(SCIPexprGetEvalValue(SCIPexprGetChildren(expr)[0]));
259
260 return SCIP_OKAY;
261 }
262
263 /** expression derivative evaluation callback */
264 static
265 SCIP_DECL_EXPRBWDIFF(bwdiffExp)
266 { /*lint --e{715}*/
267 assert(expr != NULL);
268 assert(childidx == 0);
269 assert(!SCIPisExprValue(scip, SCIPexprGetChildren(expr)[0]));
270 assert(SCIPexprGetEvalValue(expr) != SCIP_INVALID); /*lint !e777*/
271
272 *val = SCIPexprGetEvalValue(expr);
273
274 return SCIP_OKAY;
275 }
276
277 /** expression interval evaluation callback */
278 static
279 SCIP_DECL_EXPRINTEVAL(intevalExp)
280 { /*lint --e{715}*/
281 SCIP_INTERVAL childinterval;
282
283 assert(expr != NULL);
284 assert(SCIPexprGetData(expr) == NULL);
285 assert(SCIPexprGetNChildren(expr) == 1);
286
287 childinterval = SCIPexprGetActivity(SCIPexprGetChildren(expr)[0]);
288
289 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, childinterval) )
290 SCIPintervalSetEmpty(interval);
291 else
292 SCIPintervalExp(SCIP_INTERVAL_INFINITY, interval, childinterval);
293
294 return SCIP_OKAY;
295 }
296
297 /** expression estimator callback */
298 static
299 SCIP_DECL_EXPRESTIMATE(estimateExp)
300 { /*lint --e{715}*/
301 assert(scip != NULL);
302 assert(expr != NULL);
303 assert(SCIPexprGetNChildren(expr) == 1);
304 assert(strcmp(SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)), EXPRHDLR_NAME) == 0);
305 assert(coefs != NULL);
306 assert(constant != NULL);
307 assert(islocal != NULL);
308 assert(branchcand != NULL);
309 assert(*branchcand == TRUE);
310 assert(success != NULL);
311
312 *success = TRUE;
313 *coefs = 0.0;
314 *constant = 0.0;
315
316 if( overestimate )
317 {
318 addExpSecant(scip, localbounds[0].inf, localbounds[0].sup, coefs, constant, success);
319 *islocal = TRUE; /* secants are only valid locally */
320 }
321 else
322 {
323 addExpLinearization(scip, refpoint[0], SCIPexprIsIntegral(SCIPexprGetChildren(expr)[0]), coefs, constant, success);
324 *islocal = FALSE; /* linearization are globally valid */
325 *branchcand = FALSE;
326 }
327
328 return SCIP_OKAY;
329 }
330
331 /** initital estimates callback for an exponential expression */
332 static
333 SCIP_DECL_EXPRINITESTIMATES(initestimatesExp)
334 {
335 SCIP_Real refpointsunder[3] = {SCIP_INVALID, SCIP_INVALID, SCIP_INVALID};
336 SCIP_Bool overest[4] = {FALSE, FALSE, FALSE, TRUE};
337 SCIP_EXPR* child;
338 SCIP_Real lb;
339 SCIP_Real ub;
340 SCIP_Bool success;
341 int i;
342
343 assert(scip != NULL);
344 assert(expr != NULL);
345 assert(SCIPexprGetNChildren(expr) == 1);
346 assert(strcmp(SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)), EXPRHDLR_NAME) == 0);
347
348 /* get expression data */
349 child = SCIPexprGetChildren(expr)[0];
350 assert(child != NULL);
351
352 lb = bounds[0].inf;
353 ub = bounds[0].sup;
354
(1) Event cond_true: |
Condition "!overestimate", taking true branch. |
355 if( !overestimate )
356 {
357 SCIP_Real lbfinite;
358 SCIP_Real ubfinite;
359
360 /* make bounds finite */
(2) Event cond_true: |
Condition "-lb >= scip->set->num_infinity", taking true branch. |
(3) Event cond_true: |
Condition "-5. <= ub - 0.1 * fabs(ub)", taking true branch. |
361 lbfinite = SCIPisInfinity(scip, -lb) ? MIN(-5.0, ub - 0.1 * REALABS(ub)) : lb; /*lint !e666*/
(4) Event cond_true: |
Condition "ub >= scip->set->num_infinity", taking true branch. |
(5) Event cond_true: |
Condition "3. >= lb + 0.1 * fabs(lb)", taking true branch. |
362 ubfinite = SCIPisInfinity(scip, ub) ? MAX( 3.0, lb + 0.1 * REALABS(lb)) : ub; /*lint !e666*/
363
364 refpointsunder[0] = (7.0 * lbfinite + ubfinite) / 8.0;
365 refpointsunder[1] = (lbfinite + ubfinite) / 2.0;
366 refpointsunder[2] = (lbfinite + 7.0 * ubfinite) / 8.0;
367 }
368
369 *nreturned = 0;
(6) Event cond_true: |
Condition "i < 4", taking true branch. |
(17) Event loop_begin: |
Jumped back to beginning of loop. |
(18) Event cond_true: |
Condition "i < 4", taking true branch. |
(19) Event cond_at_most: |
Checking "i < 4" implies that "i" may be up to 3 on the true branch. |
Also see events: |
[overrun-local] |
370 for( i = 0; i < 4; ++i )
371 {
(7) Event cond_true: |
Condition "!overest[i]", taking true branch. |
(8) Event cond_false: |
Condition "overestimate", taking false branch. |
(20) Event cond_true: |
Condition "!overest[i]", taking true branch. |
(21) Event cond_false: |
Condition "overestimate", taking false branch. |
372 if( !overest[i] && overestimate )
(9) Event if_end: |
End of if statement. |
(22) Event if_end: |
End of if statement. |
373 continue;
374
(10) Event cond_false: |
Condition "overest[i]", taking false branch. |
(23) Event cond_false: |
Condition "overest[i]", taking false branch. |
375 if( overest[i] && (!overestimate || SCIPisInfinity(scip, ub) || SCIPisInfinity(scip, -lb)) )
(11) Event if_end: |
End of if statement. |
(24) Event if_end: |
End of if statement. |
376 continue;
377
378 assert(overest[i] || (SCIPisLE(scip, refpointsunder[i], ub) && SCIPisGE(scip, refpointsunder[i], lb))); /*lint !e661*/
379
380 coefs[*nreturned][0] = 0.0;
381 constant[*nreturned] = 0.0;
382
383 success = TRUE;
384
(12) Event cond_true: |
Condition "!overest[i]", taking true branch. |
(25) Event cond_true: |
Condition "!overest[i]", taking true branch. |
385 if( !overest[i] )
386 {
387 assert(i < 3);
(26) Event overrun-local: |
Overrunning array "refpointsunder" of 3 8-byte elements at element index 3 (byte offset 31) using index "i" (which evaluates to 3). |
Also see events: |
[cond_at_most] |
388 addExpLinearization(scip, refpointsunder[i], SCIPexprIsIntegral(child), coefs[*nreturned], &constant[*nreturned], &success); /*lint !e661*/
(13) Event if_fallthrough: |
Falling through to end of if statement. |
389 }
390 else
(14) Event if_end: |
End of if statement. |
391 addExpSecant(scip, lb, ub, coefs[*nreturned], &constant[*nreturned], &success);
392
(15) Event cond_true: |
Condition "success", taking true branch. |
393 if( success )
394 ++*nreturned;
(16) Event loop: |
Jumping back to the beginning of the loop. |
395 }
396
397 return SCIP_OKAY;
398 }
399
400 /** expression reverse propagation callback */
401 static
402 SCIP_DECL_EXPRREVERSEPROP(reversepropExp)
403 { /*lint --e{715}*/
404 assert(scip != NULL);
405 assert(expr != NULL);
406 assert(SCIPexprGetNChildren(expr) == 1);
407 assert(SCIPintervalGetInf(bounds) >= 0.0);
408
409 if( SCIPintervalGetSup(bounds) <= 0.0 )
410 {
411 *infeasible = TRUE;
412 return SCIP_OKAY;
413 }
414
415 /* f = exp(c0) -> c0 = log(f) */
416 SCIPintervalLog(SCIP_INTERVAL_INFINITY, childrenbounds, bounds);
417
418 return SCIP_OKAY;
419 }
420
421 /** expression hash callback */
422 static
423 SCIP_DECL_EXPRHASH(hashExp)
424 { /*lint --e{715}*/
425 assert(scip != NULL);
426 assert(expr != NULL);
427 assert(SCIPexprGetNChildren(expr) == 1);
428 assert(hashkey != NULL);
429 assert(childrenhashes != NULL);
430
431 *hashkey = EXPRHDLR_HASHKEY;
432 *hashkey ^= childrenhashes[0];
433
434 return SCIP_OKAY;
435 }
436
437 /** expression curvature detection callback */
438 static
439 SCIP_DECL_EXPRCURVATURE(curvatureExp)
440 { /*lint --e{715}*/
441 assert(scip != NULL);
442 assert(expr != NULL);
443 assert(childcurv != NULL);
444 assert(success != NULL);
445 assert(SCIPexprGetNChildren(expr) == 1);
446
447 /* expression is convex if child is convex; expression cannot be concave or linear */
448 if( exprcurvature == SCIP_EXPRCURV_CONVEX )
449 {
450 *success = TRUE;
451 *childcurv = SCIP_EXPRCURV_CONVEX;
452 }
453 else
454 *success = FALSE;
455
456 return SCIP_OKAY;
457 }
458
459 /** expression monotonicity detection callback */
460 static
461 SCIP_DECL_EXPRMONOTONICITY(monotonicityExp)
462 { /*lint --e{715}*/
463 assert(scip != NULL);
464 assert(expr != NULL);
465 assert(result != NULL);
466 assert(childidx == 0);
467
468 *result = SCIP_MONOTONE_INC;
469
470 return SCIP_OKAY;
471 }
472
473 /** creates the handler for exponential expressions and includes it into SCIP */
474 SCIP_RETCODE SCIPincludeExprhdlrExp(
475 SCIP* scip /**< SCIP data structure */
476 )
477 {
478 SCIP_EXPRHDLR* exprhdlr;
479
480 SCIP_CALL( SCIPincludeExprhdlr(scip, &exprhdlr, EXPRHDLR_NAME, EXPRHDLR_DESC,
481 EXPRHDLR_PRECEDENCE, evalExp, NULL) );
482 assert(exprhdlr != NULL);
483
484 SCIPexprhdlrSetCopyFreeHdlr(exprhdlr, copyhdlrExp, NULL);
485 SCIPexprhdlrSetCopyFreeData(exprhdlr, copydataExp, freedataExp);
486 SCIPexprhdlrSetSimplify(exprhdlr, simplifyExp);
487 SCIPexprhdlrSetParse(exprhdlr, parseExp);
488 SCIPexprhdlrSetIntEval(exprhdlr, intevalExp);
489 SCIPexprhdlrSetEstimate(exprhdlr, initestimatesExp, estimateExp);
490 SCIPexprhdlrSetReverseProp(exprhdlr, reversepropExp);
491 SCIPexprhdlrSetHash(exprhdlr, hashExp);
492 SCIPexprhdlrSetDiff(exprhdlr, bwdiffExp, NULL, NULL);
493 SCIPexprhdlrSetCurvature(exprhdlr, curvatureExp);
494 SCIPexprhdlrSetMonotonicity(exprhdlr, monotonicityExp);
495
496 return SCIP_OKAY;
497 }
498
499 /** creates an exponential expression */
500 SCIP_RETCODE SCIPcreateExprExp(
501 SCIP* scip, /**< SCIP data structure */
502 SCIP_EXPR** expr, /**< pointer where to store expression */
503 SCIP_EXPR* child, /**< single child */
504 SCIP_DECL_EXPR_OWNERCREATE((*ownercreate)), /**< function to call to create ownerdata */
505 void* ownercreatedata /**< data to pass to ownercreate */
506 )
507 {
508 assert(expr != NULL);
509 assert(child != NULL);
510 assert(SCIPfindExprhdlr(scip, EXPRHDLR_NAME) != NULL);
511
512 SCIP_CALL( SCIPcreateExpr(scip, expr, SCIPfindExprhdlr(scip, EXPRHDLR_NAME), NULL, 1, &child, ownercreate, ownercreatedata) );
513
514 return SCIP_OKAY;
515 }
516
517 /** indicates whether expression is of exp-type */ /*lint -e{715}*/
518 SCIP_Bool SCIPisExprExp(
519 SCIP* scip, /**< SCIP data structure */
520 SCIP_EXPR* expr /**< expression */
521 )
522 { /*lint --e{715}*/
523 assert(expr != NULL);
524
525 return strcmp(SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)), EXPRHDLR_NAME) == 0;
526 }
527