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_log.c
17 * @ingroup DEFPLUGINS_EXPR
18 * @brief logarithm expression handler
19 * @author Stefan Vigerske
20 * @author Benjamin Mueller
21 * @author Ksenia Bestuzheva
22 *
23 */
24
25 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
26
27 #include <string.h>
28
29 #include "scip/expr_value.h"
30 #include "scip/expr_log.h"
31
32 #define EXPRHDLR_NAME "log"
33 #define EXPRHDLR_DESC "natural logarithm expression"
34 #define EXPRHDLR_PRECEDENCE 80000
35 #define EXPRHDLR_HASHKEY SCIPcalcFibHash(16273.0)
36
37 /*
38 * Data structures
39 */
40
41 /** expression handler data */
42 struct SCIP_ExprhdlrData
43 {
44 SCIP_Real minzerodistance; /**< minimal distance from zero to enforce for child in bound tightening */
45 SCIP_Bool warnedonpole; /**< whether we warned on enforcing a minimal non-zero bound for child */
46 };
47
48 /*
49 * Local methods
50 */
51
52 /** computes coefficients of secant of a logarithmic term */
53 static
54 void addLogSecant(
55 SCIP* scip, /**< SCIP data structure */
56 SCIP_Real lb, /**< lower bound on variable */
57 SCIP_Real ub, /**< upper bound on variable */
58 SCIP_Real* lincoef, /**< buffer to add coefficient of secant */
59 SCIP_Real* linconstant, /**< buffer to add constant of secant */
60 SCIP_Bool* success /**< buffer to set to FALSE if secant has failed due to large numbers or unboundedness */
61 )
62 {
63 SCIP_Real coef;
64 SCIP_Real constant;
65
66 assert(scip != NULL);
67 assert(!SCIPisInfinity(scip, lb));
68 assert(!SCIPisInfinity(scip, -ub));
69 assert(SCIPisLE(scip, lb, ub));
70 assert(lincoef != NULL);
71 assert(linconstant != NULL);
72 assert(success != NULL);
73
74 if( SCIPisLE(scip, lb, 0.0) || SCIPisInfinity(scip, ub) )
75 {
76 /* unboundedness */
77 *success = FALSE;
78 return;
79 }
80
81 /* if lb and ub are too close use a safe secant */
82 if( SCIPisEQ(scip, lb, ub) )
83 {
84 coef = 0.0;
85 constant = log(ub);
86 }
87 else
88 {
89 coef = (log(ub) - log(lb)) / (ub - lb);
90 constant = log(ub) - coef * ub;
91 }
92
93 if( SCIPisInfinity(scip, REALABS(coef)) || SCIPisInfinity(scip, REALABS(constant)) )
94 {
95 *success = FALSE;
96 return;
97 }
98
99 *lincoef += coef;
100 *linconstant += constant;
101 }
102
103 /** computes coefficients of linearization of a logarithmic term in a reference point */
104 static
105 void addLogLinearization(
106 SCIP* scip, /**< SCIP data structure */
107 SCIP_Real refpoint, /**< point for which to compute value of linearization */
108 SCIP_Bool isint, /**< whether corresponding variable is a discrete variable, and thus linearization could be moved */
109 SCIP_Real* lincoef, /**< buffer to add coefficient of secant */
110 SCIP_Real* linconstant, /**< buffer to add constant of secant */
111 SCIP_Bool* success /**< buffer to set to FALSE if secant has failed due to large numbers or unboundedness */
112 )
113 {
114 SCIP_Real constant;
115 SCIP_Real coef;
116
117 assert(scip != NULL);
118 assert(lincoef != NULL);
119 assert(linconstant != NULL);
120 assert(success != NULL);
121
122 /* can not compute a valid cut if zero is contained in [lb,ub] */
123 if( SCIPisInfinity(scip, REALABS(refpoint)) || SCIPisLE(scip, refpoint, 0.0) )
124 {
125 *success = FALSE;
126 return;
127 }
128
129 if( !isint || SCIPisIntegral(scip, refpoint) )
130 {
131 assert(refpoint != 0.0);
132 coef = 1.0 / refpoint;
133 constant = log(refpoint) - 1.0;
134 }
135 else
136 {
137 /* log(x) -> secant between f=floor(refpoint) and f+1 = log((f+1.0)/f) * x + log(f) - log((f+1.0)/f) * f */
138 SCIP_Real f;
139
140 f = SCIPfloor(scip, refpoint);
141 assert(f > 0.0);
142
143 coef = log((f+1.0) / f);
144 constant = log(f) - coef * f;
145 }
146
147 if( SCIPisInfinity(scip, REALABS(coef)) || SCIPisInfinity(scip, REALABS(constant)) )
148 {
149 *success = FALSE;
150 return;
151 }
152
153 *lincoef += coef;
154 *linconstant += constant;
155 }
156
157 /*
158 * Callback methods of expression handler
159 */
160
161 /** simplifies a log expression
162 *
163 * Evaluates the logarithm function when its child is a value expression.
164 *
165 * TODO: split products ?
166 * TODO: log(exp(*)) = *
167 */
168 static
169 SCIP_DECL_EXPRSIMPLIFY(simplifyLog)
170 { /*lint --e{715}*/
171 SCIP_EXPR* child;
172
173 assert(scip != NULL);
174 assert(expr != NULL);
175 assert(simplifiedexpr != NULL);
176 assert(SCIPexprGetNChildren(expr) == 1);
177
178 child = SCIPexprGetChildren(expr)[0];
179 assert(child != NULL);
180
181 /* check for value expression */
182 if( SCIPisExprValue(scip, child) )
183 {
184 /* TODO how to handle a non-positive value? */
185 assert(SCIPgetValueExprValue(child) > 0.0);
186
187 SCIP_CALL( SCIPcreateExprValue(scip, simplifiedexpr, log(SCIPgetValueExprValue(child)), ownercreate,
188 ownercreatedata) );
189 }
190 else
191 {
192 *simplifiedexpr = expr;
193
194 /* we have to capture it, since it must simulate a "normal" simplified call in which a new expression is created */
195 SCIPcaptureExpr(*simplifiedexpr);
196 }
197
198 return SCIP_OKAY;
199 }
200
201 /** expression handler copy callback */
202 static
203 SCIP_DECL_EXPRCOPYHDLR(copyhdlrLog)
204 { /*lint --e{715}*/
205 SCIP_CALL( SCIPincludeExprhdlrLog(scip) );
206
207 return SCIP_OKAY;
208 }
209
210 /** expression handler free callback */
211 static
212 SCIP_DECL_EXPRFREEHDLR(freehdlrLog)
213 { /*lint --e{715}*/
214 assert(exprhdlrdata != NULL);
215 assert(*exprhdlrdata != NULL);
216
217 SCIPfreeBlockMemory(scip, exprhdlrdata);
218
219 return SCIP_OKAY;
220 }
221
222 /** expression data copy callback */
223 static
224 SCIP_DECL_EXPRCOPYDATA(copydataLog)
225 { /*lint --e{715}*/
226 assert(targetexprdata != NULL);
227 assert(sourceexpr != NULL);
228 assert(SCIPexprGetData(sourceexpr) == NULL);
229
230 *targetexprdata = NULL;
231
232 return SCIP_OKAY;
233 }
234
235 /** expression data free callback */
236 static
237 SCIP_DECL_EXPRFREEDATA(freedataLog)
238 { /*lint --e{715}*/
239 assert(expr != NULL);
240
241 SCIPexprSetData(expr, NULL);
242
243 return SCIP_OKAY;
244 }
245
246 /** expression parse callback */
247 static
248 SCIP_DECL_EXPRPARSE(parseLog)
249 { /*lint --e{715}*/
250 SCIP_EXPR* childexpr;
251
252 assert(expr != NULL);
253
254 /* parse child expression from remaining string */
255 SCIP_CALL( SCIPparseExpr(scip, &childexpr, string, endstring, ownercreate, ownercreatedata) );
256 assert(childexpr != NULL);
257
258 /* create logarithmic expression */
259 SCIP_CALL( SCIPcreateExprLog(scip, expr, childexpr, ownercreate, ownercreatedata) );
260 assert(*expr != NULL);
261
262 /* release child expression since it has been captured by the logarithmic expression */
263 SCIP_CALL( SCIPreleaseExpr(scip, &childexpr) );
264
265 *success = TRUE;
266
267 return SCIP_OKAY;
268 }
269
270 /** expression point evaluation callback */
271 static
272 SCIP_DECL_EXPREVAL(evalLog)
273 { /*lint --e{715}*/
274 assert(expr != NULL);
275 assert(SCIPexprGetData(expr) == NULL);
276 assert(SCIPexprGetNChildren(expr) == 1);
277 assert(SCIPexprGetEvalValue(SCIPexprGetChildren(expr)[0]) != SCIP_INVALID); /*lint !e777*/
278
279 /**! [SnippetExprEvalLog] */
280 if( SCIPexprGetEvalValue(SCIPexprGetChildren(expr)[0]) <= 0.0 )
281 {
282 SCIPdebugMsg(scip, "invalid evaluation of logarithmic expression\n");
283 *val = SCIP_INVALID;
284 }
285 else
286 {
287 *val = log(SCIPexprGetEvalValue(SCIPexprGetChildren(expr)[0]));
288 }
289 /**! [SnippetExprEvalLog] */
290
291 return SCIP_OKAY;
292 }
293
294 /** expression derivative evaluation callback */
295 static
296 SCIP_DECL_EXPRBWDIFF(bwdiffLog)
297 { /*lint --e{715}*/
298 SCIP_EXPR* child;
299
300 assert(expr != NULL);
301 assert(childidx == 0);
302 assert(SCIPexprGetEvalValue(expr) != SCIP_INVALID); /*lint !e777*/
303
304 child = SCIPexprGetChildren(expr)[0];
305 assert(child != NULL);
306 assert(strcmp(SCIPexprhdlrGetName(SCIPexprGetHdlr(child)), "val") != 0);
307 assert(SCIPexprGetEvalValue(child) > 0.0);
308
309 *val = 1.0 / SCIPexprGetEvalValue(child);
310
311 return SCIP_OKAY;
312 }
313
314 /** expression interval evaluation callback */
315 static
316 SCIP_DECL_EXPRINTEVAL(intevalLog)
317 { /*lint --e{715}*/
318 SCIP_EXPRHDLRDATA* exprhdlrdata;
319 SCIP_INTERVAL childinterval;
320
321 assert(expr != NULL);
322 assert(SCIPexprGetData(expr) == NULL);
323 assert(SCIPexprGetNChildren(expr) == 1);
324
325 exprhdlrdata = SCIPexprhdlrGetData(SCIPexprGetHdlr(expr));
326 assert(exprhdlrdata != NULL);
327
328 childinterval = SCIPexprGetActivity(SCIPexprGetChildren(expr)[0]);
329
330 /* pretend childinterval to be >= epsilon, see also reversepropLog */
331 if( childinterval.inf < exprhdlrdata->minzerodistance && exprhdlrdata->minzerodistance > 0.0 )
332 {
333 if( !exprhdlrdata->warnedonpole && SCIPgetVerbLevel(scip) > SCIP_VERBLEVEL_NONE )
334 {
335 SCIPinfoMessage(scip, NULL, "Changing lower bound for child of log() from %g to %g.\n"
336 "Check your model formulation or use option expr/" EXPRHDLR_NAME "/minzerodistance to avoid this warning.\n",
337 childinterval.inf, exprhdlrdata->minzerodistance);
338 SCIPinfoMessage(scip, NULL, "Expression: ");
339 SCIP_CALL( SCIPprintExpr(scip, expr, NULL) );
340 SCIPinfoMessage(scip, NULL, "\n");
341 exprhdlrdata->warnedonpole = TRUE;
342 }
343 childinterval.inf = exprhdlrdata->minzerodistance;
344 }
345
346 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, childinterval) )
347 {
348 SCIPintervalSetEmpty(interval);
349 return SCIP_OKAY;
350 }
351
352 SCIPintervalLog(SCIP_INTERVAL_INFINITY, interval, childinterval);
353
354 return SCIP_OKAY;
355 }
356
357 /** expression estimation callback */
358 static
359 SCIP_DECL_EXPRESTIMATE(estimateLog)
360 { /*lint --e{715}*/
361 SCIP_Real lb;
362 SCIP_Real ub;
363
364 assert(scip != NULL);
365 assert(expr != NULL);
366 assert(SCIPexprGetNChildren(expr) == 1);
367 assert(strcmp(SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)), EXPRHDLR_NAME) == 0);
368 assert(coefs != NULL);
369 assert(constant != NULL);
370 assert(islocal != NULL);
371 assert(branchcand != NULL);
372 assert(*branchcand == TRUE);
373 assert(success != NULL);
374 assert(refpoint != NULL);
375
376 lb = localbounds[0].inf;
377 ub = localbounds[0].sup;
378
379 *coefs = 0.0;
380 *constant = 0.0;
381 *success = TRUE;
382
383 if( overestimate )
384 {
385 if( !SCIPisPositive(scip, refpoint[0]) )
386 {
387 /* if refpoint is 0 (then lb=0 probably) or below, then slope is infinite, then try to move away from 0 */
388 if( SCIPisZero(scip, ub) )
389 {
390 *success = FALSE;
391 return SCIP_OKAY;
392 }
393
394 if( localbounds[0].sup < 0.2 )
395 refpoint[0] = 0.5 * lb + 0.5 * ub;
396 else
397 refpoint[0] = 0.1;
398 }
399
400 addLogLinearization(scip, refpoint[0], SCIPexprIsIntegral(SCIPexprGetChildren(expr)[0]), coefs, constant, success);
401 *islocal = FALSE; /* linearization is globally valid */
402 *branchcand = FALSE;
403 }
404 else
405 {
406 addLogSecant(scip, lb, ub, coefs, constant, success);
407 *islocal = TRUE; /* secants are only valid locally */
408 }
409
410 return SCIP_OKAY;
411 }
412
413 /** initial estimates callback that provides initial linear estimators for a logarithm expression */
414 static
415 SCIP_DECL_EXPRINITESTIMATES(initestimatesLog)
416 {
417 SCIP_Real refpointsover[3] = {SCIP_INVALID, SCIP_INVALID, SCIP_INVALID};
418 SCIP_Bool overest[4] = {TRUE, TRUE, TRUE, FALSE};
419 SCIP_EXPR* child;
420 SCIP_Real lb;
421 SCIP_Real ub;
422 SCIP_Bool success;
423 int i;
424
425 assert(scip != NULL);
426 assert(expr != NULL);
427 assert(SCIPexprGetNChildren(expr) == 1);
428 assert(strcmp(SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)), EXPRHDLR_NAME) == 0);
429
430 /* get expression data */
431 child = SCIPexprGetChildren(expr)[0];
432 assert(child != NULL);
433
434 lb = SCIPintervalGetInf(bounds[0]);
435 ub = SCIPintervalGetSup(bounds[0]);
436
(1) Event cond_false: |
Condition "fabs(lb - ub) <= scip->set->num_epsilon", taking false branch. |
437 if( SCIPisEQ(scip, lb, ub) )
(2) Event if_end: |
End of if statement. |
438 return SCIP_OKAY;
439
(3) Event cond_true: |
Condition "overestimate", taking true branch. |
440 if( overestimate )
441 {
442 /* adjust lb */
(4) Event cond_true: |
Condition "0.5 * lb + 0.5 * ub <= 0.1", taking true branch. |
(5) Event cond_true: |
Condition "lb >= ((0.5 * lb + 0.5 * ub <= 0.1) ? 0.5 * lb + 0.5 * ub : 0.1)", taking true branch. |
443 lb = MAX(lb, MIN(0.5 * lb + 0.5 * ub, 0.1));
444
445 refpointsover[0] = lb;
(6) Event cond_true: |
Condition "ub >= scip->set->num_infinity", taking true branch. |
446 refpointsover[1] = SCIPisInfinity(scip, ub) ? lb + 2.0 : (lb + ub) / 2;
(7) Event cond_true: |
Condition "ub >= scip->set->num_infinity", taking true branch. |
447 refpointsover[2] = SCIPisInfinity(scip, ub) ? lb + 20.0 : ub;
448 }
449
450 *nreturned = 0;
451
(8) Event cond_true: |
Condition "i < 4", taking true branch. |
(21) Event loop_begin: |
Jumped back to beginning of loop. |
(22) Event cond_true: |
Condition "i < 4", taking true branch. |
(23) Event cond_at_most: |
Checking "i < 4" implies that "i" may be up to 3 on the true branch. |
Also see events: |
[overrun-local] |
452 for( i = 0; i < 4; ++i )
453 {
(9) Event cond_true: |
Condition "overest[i]", taking true branch. |
(10) Event cond_false: |
Condition "!overestimate", taking false branch. |
(11) Event cond_false: |
Condition "!overest[i]", taking false branch. |
(24) Event cond_true: |
Condition "overest[i]", taking true branch. |
(25) Event cond_false: |
Condition "!overestimate", taking false branch. |
(26) Event cond_false: |
Condition "!overest[i]", taking false branch. |
454 if( (overest[i] && !overestimate) || (!overest[i] && (overestimate || SCIPisInfinity(scip, ub))) )
(12) Event if_end: |
End of if statement. |
(27) Event if_end: |
End of if statement. |
455 continue;
456
457 assert(!overest[i] || (SCIPisLE(scip, refpointsover[i], ub) && SCIPisGE(scip, refpointsover[i], lb))); /*lint !e661*/
458
459 success = TRUE;
460 coefs[*nreturned][0] = 0.0;
461 constant[*nreturned] = 0.0;
462
(13) Event cond_true: |
Condition "overest[i]", taking true branch. |
(28) Event cond_true: |
Condition "overest[i]", taking true branch. |
463 if( overest[i] )
464 {
465 assert(i < 3);
(29) Event overrun-local: |
Overrunning array "refpointsover" 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] |
466 addLogLinearization(scip, refpointsover[i], SCIPexprIsIntegral(child), coefs[*nreturned], &constant[*nreturned], &success); /*lint !e661*/
(14) Event cond_true: |
Condition "success", taking true branch. |
467 if( success )
468 {
(15) Event cond_false: |
Condition "0", taking false branch. |
(16) Event loop_end: |
Reached end of loop. |
469 SCIPdebugMsg(scip, "init overestimate log(x) at x=%g -> %g*x+%g\n", refpointsover[i], coefs[*nreturned][0], constant[*nreturned]);
470 }
(17) Event if_fallthrough: |
Falling through to end of if statement. |
471 }
472 else
473 {
474 addLogSecant(scip, lb, ub, coefs[*nreturned], &constant[*nreturned], &success);
475 if( success )
476 {
477 SCIPdebugMsg(scip, "init underestimate log(x) on x=[%g,%g] -> %g*x+%g\n", lb, ub, coefs[*nreturned][0], constant[*nreturned]);
478 }
(18) Event if_end: |
End of if statement. |
479 }
480
(19) Event cond_true: |
Condition "success", taking true branch. |
481 if( success )
482 {
483 ++(*nreturned);
484 }
(20) Event loop: |
Jumping back to the beginning of the loop. |
485 }
486
487 return SCIP_OKAY;
488 }
489
490 /** expression reverse propagation callback */
491 static
492 SCIP_DECL_EXPRREVERSEPROP(reversepropLog)
493 { /*lint --e{715}*/
494 SCIP_EXPRHDLRDATA* exprhdlrdata;
495
496 assert(scip != NULL);
497 assert(expr != NULL);
498 assert(SCIPexprGetNChildren(expr) == 1);
499
500 exprhdlrdata = SCIPexprhdlrGetData(SCIPexprGetHdlr(expr));
501 assert(exprhdlrdata != NULL);
502
503 /* f = log(c0) -> c0 = exp(f) */
504 SCIPintervalExp(SCIP_INTERVAL_INFINITY, &childrenbounds[0], bounds);
505
506 /* force child lower bound to be at least epsilon away from 0
507 * this can help a lot in enforcement (try ex8_5_3)
508 * child being equal 0 is already forbidden, so making it strictly greater-equal epsilon enforces
509 * and hopefully doesn't introduce much problems
510 * if childrenbounds[0].sup < epsilon, too, then this will result in a cutoff
511 */
512 if( childrenbounds[0].inf < exprhdlrdata->minzerodistance )
513 {
514 SCIPdebugMsg(scip, "Pushing child lower bound from %g to %g; upper bound remains at %g\n", childrenbounds[0].inf, SCIPepsilon(scip), childrenbounds[0].sup);
515
516 if( !exprhdlrdata->warnedonpole && SCIPgetVerbLevel(scip) > SCIP_VERBLEVEL_NONE )
517 {
518 SCIPinfoMessage(scip, NULL, "Changing lower bound for child of log() from %g to %g.\n"
519 "Check your model formulation or use option expr/" EXPRHDLR_NAME "/minzerodistance to avoid this warning.\n",
520 childrenbounds[0].inf, exprhdlrdata->minzerodistance);
521 SCIPinfoMessage(scip, NULL, "Expression: ");
522 SCIP_CALL( SCIPprintExpr(scip, expr, NULL) );
523 SCIPinfoMessage(scip, NULL, "\n");
524 exprhdlrdata->warnedonpole = TRUE;
525 }
526
527 childrenbounds[0].inf = exprhdlrdata->minzerodistance;
528 }
529
530 return SCIP_OKAY;
531 }
532
533 /** expression hash callback */
534 static
535 SCIP_DECL_EXPRHASH(hashLog)
536 { /*lint --e{715}*/
537 assert(scip != NULL);
538 assert(expr != NULL);
539 assert(SCIPexprGetNChildren(expr) == 1);
540 assert(hashkey != NULL);
541 assert(childrenhashes != NULL);
542
543 *hashkey = EXPRHDLR_HASHKEY;
544 *hashkey ^= childrenhashes[0];
545
546 return SCIP_OKAY;
547 }
548
549 /** expression curvature detection callback */
550 static
551 SCIP_DECL_EXPRCURVATURE(curvatureLog)
552 { /*lint --e{715}*/
553 assert(scip != NULL);
554 assert(expr != NULL);
555 assert(childcurv != NULL);
556 assert(SCIPexprGetNChildren(expr) == 1);
557
558 /* expression is concave if child is concave, expression cannot be linear or convex */
559 if( exprcurvature == SCIP_EXPRCURV_CONCAVE )
560 {
561 *childcurv = SCIP_EXPRCURV_CONCAVE;
562 *success = TRUE;
563 }
564 else
565 *success = FALSE;
566
567 return SCIP_OKAY;
568 }
569
570 /** expression monotonicity detection callback */
571 static
572 SCIP_DECL_EXPRMONOTONICITY(monotonicityLog)
573 { /*lint --e{715}*/
574 assert(scip != NULL);
575 assert(expr != NULL);
576 assert(result != NULL);
577 assert(childidx == 0);
578
579 *result = SCIP_MONOTONE_INC;
580
581 return SCIP_OKAY;
582 }
583
584 /** creates the handler for logarithmic expression and includes it into SCIP */
585 SCIP_RETCODE SCIPincludeExprhdlrLog(
586 SCIP* scip /**< SCIP data structure */
587 )
588 {
589 SCIP_EXPRHDLR* exprhdlr;
590 SCIP_EXPRHDLRDATA* exprhdlrdata;
591
592 /**! [SnippetIncludeExprhdlrLog] */
593 SCIP_CALL( SCIPallocClearBlockMemory(scip, &exprhdlrdata) );
594
595 SCIP_CALL( SCIPincludeExprhdlr(scip, &exprhdlr, EXPRHDLR_NAME, EXPRHDLR_DESC, EXPRHDLR_PRECEDENCE, evalLog,
596 exprhdlrdata) );
597 assert(exprhdlr != NULL);
598
599 SCIPexprhdlrSetCopyFreeHdlr(exprhdlr, copyhdlrLog, freehdlrLog);
600 SCIPexprhdlrSetCopyFreeData(exprhdlr, copydataLog, freedataLog);
601 SCIPexprhdlrSetSimplify(exprhdlr, simplifyLog);
602 SCIPexprhdlrSetParse(exprhdlr, parseLog);
603 SCIPexprhdlrSetIntEval(exprhdlr, intevalLog);
604 SCIPexprhdlrSetEstimate(exprhdlr, initestimatesLog, estimateLog);
605 SCIPexprhdlrSetReverseProp(exprhdlr, reversepropLog);
606 SCIPexprhdlrSetHash(exprhdlr, hashLog);
607 SCIPexprhdlrSetDiff(exprhdlr, bwdiffLog, NULL, NULL);
608 SCIPexprhdlrSetCurvature(exprhdlr, curvatureLog);
609 SCIPexprhdlrSetMonotonicity(exprhdlr, monotonicityLog);
610
611 SCIP_CALL( SCIPaddRealParam(scip, "expr/" EXPRHDLR_NAME "/minzerodistance",
612 "minimal distance from zero to enforce for child in bound tightening",
613 &exprhdlrdata->minzerodistance, FALSE, SCIPepsilon(scip), 0.0, 1.0, NULL, NULL) );
614 /**! [SnippetIncludeExprhdlrLog] */
615
616 return SCIP_OKAY;
617 }
618
619 /** creates a logarithmic expression */
620 SCIP_RETCODE SCIPcreateExprLog(
621 SCIP* scip, /**< SCIP data structure */
622 SCIP_EXPR** expr, /**< pointer where to store expression */
623 SCIP_EXPR* child, /**< single child */
624 SCIP_DECL_EXPR_OWNERCREATE((*ownercreate)), /**< function to call to create ownerdata */
625 void* ownercreatedata /**< data to pass to ownercreate */
626 )
627 {
628 assert(expr != NULL);
629 assert(child != NULL);
630
631 SCIP_CALL( SCIPcreateExpr(scip, expr, SCIPfindExprhdlr(scip, EXPRHDLR_NAME), NULL, 1, &child, ownercreate,
632 ownercreatedata) );
633
634 return SCIP_OKAY;
635 }
636
637 /** indicates whether expression is of log-type */ /*lint -e{715}*/
638 SCIP_Bool SCIPisExprLog(
639 SCIP* scip, /**< SCIP data structure */
640 SCIP_EXPR* expr /**< expression */
641 )
642 { /*lint --e{715}*/
643 assert(expr != NULL);
644
645 return strcmp(SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)), EXPRHDLR_NAME) == 0;
646 }
647