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_entropy.c
17 * @ingroup DEFPLUGINS_EXPR
18 * @brief handler for -x*log(x) expressions
19 * @author Benjamin Mueller
20 * @author Fabian Wegscheider
21 * @author Ksenia Bestuzheva
22 *
23 * @todo replace exp(-1.0) by 1.0/M_E
24 */
25
26 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
27
28 #include "scip/expr_entropy.h"
29 #include "scip/expr_value.h"
30 #include "scip/expr.h"
31
32 #include <string.h>
33
34 /* fundamental expression handler properties */
35 #define EXPRHDLR_NAME "entropy"
36 #define EXPRHDLR_DESC "entropy expression (-x*log(x))"
37 #define EXPRHDLR_PRECEDENCE 81000
38 #define EXPRHDLR_HASHKEY SCIPcalcFibHash(7477.0)
39
40 /*
41 * Data structures
42 */
43
44 /*
45 * Local methods
46 */
47
48 /** helper function for reverseProp() which returns an x* in [xmin,xmax] s.t. the distance -x*log(x) and a given target
49 * value is minimized; the function assumes that -x*log(x) is monotone on [xmin,xmax];
50 */
51 static
52 SCIP_Real reversePropBinarySearch(
53 SCIP* scip, /**< SCIP data structure */
54 SCIP_Real xmin, /**< smallest possible x */
55 SCIP_Real xmax, /**< largest possible x */
56 SCIP_Bool increasing, /**< -x*log(x) is increasing or decreasing on [xmin,xmax] */
57 SCIP_Real targetval /**< target value */
58 )
59 {
60 SCIP_Real xminval = (xmin == 0.0) ? 0.0 : -xmin * log(xmin);
61 SCIP_Real xmaxval = (xmax == 0.0) ? 0.0 : -xmax * log(xmax);
62 int i;
63
64 assert(xmin <= xmax);
65 assert(increasing ? xminval <= xmaxval : xminval >= xmaxval);
66
67 /* function can not achieve -x*log(x) -> return xmin or xmax */
68 if( SCIPisGE(scip, xminval, targetval) && SCIPisGE(scip, xmaxval, targetval) )
69 return increasing ? xmin : xmax;
70 else if( SCIPisLE(scip, xminval, targetval) && SCIPisLE(scip, xmaxval, targetval) )
71 return increasing ? xmax : xmin;
72
73 /* binary search */
74 for( i = 0; i < 1000; ++i )
75 {
76 SCIP_Real x = (xmin + xmax) / 2.0;
77 SCIP_Real xval = (x == 0.0) ? 0.0 : -x * log(x);
78
79 /* found the corresponding point -> skip */
80 if( SCIPisEQ(scip, xval, targetval) )
81 return x;
82 else if( SCIPisLT(scip, xval, targetval) )
83 {
84 if( increasing )
85 xmin = x;
86 else
87 xmax = x;
88 }
89 else
90 {
91 if( increasing )
92 xmax = x;
93 else
94 xmin = x;
95 }
96 }
97
98 return SCIP_INVALID;
99 }
100
101 /** helper function for reverse propagation; needed for proper unittest */
102 static
103 SCIP_RETCODE reverseProp(
104 SCIP* scip, /**< SCIP data structure */
105 SCIP_INTERVAL exprinterval, /**< bounds on the expression */
106 SCIP_INTERVAL childinterval, /**< bounds on the interval of the child */
107 SCIP_INTERVAL* interval /**< resulting interval */
108 )
109 {
110 SCIP_INTERVAL childentropy;
111 SCIP_INTERVAL intersection;
112 SCIP_INTERVAL tmp;
113 SCIP_Real childinf;
114 SCIP_Real childsup;
115 SCIP_Real extremum;
116 SCIP_Real boundinf;
117 SCIP_Real boundsup;
118
119 assert(scip != NULL);
120 assert(interval != NULL);
121
122 /* check whether domain is empty, i.e., bounds on -x*log(x) > 1/e */
123 if( SCIPisGT(scip, SCIPintervalGetInf(exprinterval), exp(-1.0))
124 || SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, childinterval) )
125 {
126 SCIPintervalSetEmpty(interval);
127 return SCIP_OKAY;
128 }
129
130 /* compute the intersection between entropy([childinf,childsup]) and [expr.inf, expr.sup] */
131 SCIPintervalEntropy(SCIP_INTERVAL_INFINITY, &childentropy, childinterval);
132 SCIPintervalIntersect(&intersection, childentropy, exprinterval);
133
134 /* intersection empty -> infeasible */
135 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, intersection) )
136 {
137 SCIPintervalSetEmpty(interval);
138 return SCIP_OKAY;
139 }
140
141 /* intersection = childentropy -> nothing can be learned */
142 if( SCIPintervalIsSubsetEQ(SCIP_INTERVAL_INFINITY, childentropy, intersection) )
143 {
144 SCIPintervalSetBounds(interval, 0.0, SCIP_INTERVAL_INFINITY);
145 SCIPintervalIntersect(interval, *interval, childinterval);
146 return SCIP_OKAY;
147 }
148
149 childinf = MAX(0.0, SCIPintervalGetInf(childinterval)); /*lint !e666*/
150 childsup = SCIPintervalGetSup(childinterval);
151 extremum = exp(-1.0);
152 boundinf = SCIP_INVALID;
153 boundsup = SCIP_INVALID;
154
155 /*
156 * check whether lower bound of child can be improved
157 */
158 SCIPintervalSet(&tmp, childinf);
159 SCIPintervalEntropy(SCIP_INTERVAL_INFINITY, &tmp, tmp);
160
161 /* entropy(childinf) < intersection.inf -> consider [childinf, MIN(childsup, extremum)] */
162 if( SCIPintervalGetInf(intersection) > -SCIP_INTERVAL_INFINITY && SCIPintervalGetSup(tmp) - SCIPintervalGetInf(intersection) < -SCIPepsilon(scip) )
163 {
164 boundinf = reversePropBinarySearch(scip, childinf, MIN(extremum, childsup), TRUE,
165 SCIPintervalGetInf(intersection));
166 }
167 /* entropy(childinf) > intersection.sup -> consider [MAX(childinf,extremum), childsup] */
168 else if( SCIPintervalGetSup(intersection) < SCIP_INTERVAL_INFINITY && SCIPintervalGetInf(tmp) - SCIPintervalGetSup(intersection) > SCIPepsilon(scip) )
169 {
170 boundinf = reversePropBinarySearch(scip, MAX(childinf, extremum), childsup, FALSE,
171 SCIPintervalGetSup(intersection));
172 }
173 /* using a strict greater-than here because we expect a tightening because we saw an at-least-epsilon-potential above */
174 assert(boundinf == SCIP_INVALID || boundinf > childinf); /*lint !e777*/
175
176 /*
177 * check whether upper bound of child can be improved
178 */
179 if( childsup < SCIP_INTERVAL_INFINITY )
180 {
181 SCIPintervalSet(&tmp, childsup);
182 SCIPintervalEntropy(SCIP_INTERVAL_INFINITY, &tmp, tmp);
183 }
184 else
185 SCIPintervalSetBounds(&tmp, -SCIP_INTERVAL_INFINITY, -SCIP_INTERVAL_INFINITY); /* entropy(inf) = -inf */
186
187 /* entropy(childsup) < intersection.inf -> consider [MAX(childinf,extremum), childsup] */
188 if( SCIPintervalGetInf(intersection) > -SCIP_INTERVAL_INFINITY && SCIPintervalGetSup(tmp) - SCIPintervalGetInf(intersection) < -SCIPepsilon(scip) )
189 {
190 boundsup = reversePropBinarySearch(scip, MAX(childinf, extremum), childsup, FALSE,
191 SCIPintervalGetInf(intersection));
192 }
193 /* entropy(childsup) > intersection.sup -> consider [childinf, MIN(childsup,extremum)] */
194 else if( SCIPintervalGetSup(intersection) < SCIP_INTERVAL_INFINITY && SCIPintervalGetInf(tmp) - SCIPintervalGetSup(intersection) > SCIPepsilon(scip) )
195 {
196 boundsup = reversePropBinarySearch(scip, childinf, MIN(childsup, extremum), TRUE,
197 SCIPintervalGetSup(intersection));
198 }
199 /* using a strict smaller-than here because we expect a tightening because we saw an at-least-epsilon-potential above */
200 assert(boundsup == SCIP_INVALID || boundsup < childsup); /*lint !e777*/
201
202 if( boundinf != SCIP_INVALID ) /*lint !e777*/
203 {
204 childinf = MAX(childinf, boundinf);
205 }
206 if( boundsup != SCIP_INVALID ) /*lint !e777*/
207 {
208 childsup = boundsup;
209 }
210 assert(childinf <= childsup); /* infeasible case has been handled already */
211
212 /* set the resulting bounds */
213 SCIPintervalSetBounds(interval, childinf, childsup);
214
215 return SCIP_OKAY;
216 }
217
218 /*
219 * Callback methods of expression handler
220 */
221
222 /** expression handler copy callback */
223 static
224 SCIP_DECL_EXPRCOPYHDLR(copyhdlrEntropy)
225 { /*lint --e{715}*/
226 SCIP_CALL( SCIPincludeExprhdlrEntropy(scip) );
227
228 return SCIP_OKAY;
229 }
230
231 /** simplifies an entropy expression */
232 static
233 SCIP_DECL_EXPRSIMPLIFY(simplifyEntropy)
234 { /*lint --e{715}*/
235 SCIP_EXPR* child;
236
237 assert(scip != NULL);
238 assert(expr != NULL);
239 assert(simplifiedexpr != NULL);
240 assert(SCIPexprGetNChildren(expr) == 1);
241
242 child = SCIPexprGetChildren(expr)[0];
243 assert(child != NULL);
244
245 /* check for value expression */
246 if( SCIPisExprValue(scip, child) )
247 {
248 SCIP_Real childvalue = SCIPgetValueExprValue(child);
249
250 /* TODO how to handle a negative value? */
251 assert(childvalue >= 0.0);
252
253 if( childvalue == 0.0 || childvalue == 1.0 )
254 {
255 SCIP_CALL( SCIPcreateExprValue(scip, simplifiedexpr, 0.0, ownercreate, ownercreatedata) );
256 }
257 else
258 {
259 SCIP_CALL( SCIPcreateExprValue(scip, simplifiedexpr, -childvalue * log(childvalue), ownercreate,
260 ownercreatedata) );
261 }
262 }
263 else
264 {
265 *simplifiedexpr = expr;
266
267 /* we have to capture it, since it must simulate a "normal" simplified call in which a new expression is created */
268 SCIPcaptureExpr(*simplifiedexpr);
269 }
270
271 /* TODO handle -x*log(x) = 0 if x in {0,1} */
272
273 return SCIP_OKAY;
274 }
275
276 /** expression data copy callback */
277 static
278 SCIP_DECL_EXPRCOPYDATA(copydataEntropy)
279 { /*lint --e{715}*/
280 assert(targetexprdata != NULL);
281 assert(sourceexpr != NULL);
282 assert(SCIPexprGetData(sourceexpr) == NULL);
283
284 *targetexprdata = NULL;
285 return SCIP_OKAY;
286 }
287
288 /** expression data free callback */
289 static
290 SCIP_DECL_EXPRFREEDATA(freedataEntropy)
291 { /*lint --e{715}*/
292 assert(expr != NULL);
293
294 SCIPexprSetData(expr, NULL);
295 return SCIP_OKAY;
296 }
297
298 /** expression parse callback */
299 static
300 SCIP_DECL_EXPRPARSE(parseEntropy)
301 { /*lint --e{715}*/
302 SCIP_EXPR* childexpr;
303
304 assert(expr != NULL);
305
306 /* parse child expression from remaining string */
307 SCIP_CALL( SCIPparseExpr(scip, &childexpr, string, endstring, ownercreate, ownercreatedata) );
308 assert(childexpr != NULL);
309
310 /* create entropy expression */
311 SCIP_CALL( SCIPcreateExprEntropy(scip, expr, childexpr, ownercreate, ownercreatedata) );
312 assert(*expr != NULL);
313
314 /* release child expression since it has been captured by the entropy expression */
315 SCIP_CALL( SCIPreleaseExpr(scip, &childexpr) );
316
317 *success = TRUE;
318
319 return SCIP_OKAY;
320 }
321
322
323 /** expression (point-) evaluation callback */
324 static
325 SCIP_DECL_EXPREVAL(evalEntropy)
326 { /*lint --e{715}*/
327 SCIP_Real childvalue;
328
329 assert(expr != NULL);
330 assert(SCIPexprGetData(expr) == NULL);
331 assert(SCIPexprGetNChildren(expr) == 1);
332 assert(SCIPexprGetEvalValue(SCIPexprGetChildren(expr)[0]) != SCIP_INVALID); /*lint !e777*/
333
334 childvalue = SCIPexprGetEvalValue(SCIPexprGetChildren(expr)[0]);
335
336 if( childvalue < 0.0 )
337 {
338 SCIPdebugMsg(scip, "invalid evaluation of entropy expression\n");
339 *val = SCIP_INVALID;
340 }
341 else if( childvalue == 0.0 || childvalue == 1.0 )
342 {
343 /* -x*log(x) = 0 iff x in {0,1} */
344 *val = 0.0;
345 }
346 else
347 {
348 *val = -childvalue * log(childvalue);
349 }
350
351 return SCIP_OKAY;
352 }
353
354 /** expression derivative evaluation callback */
355 static
356 SCIP_DECL_EXPRBWDIFF(bwdiffEntropy)
357 { /*lint --e{715}*/
358 SCIP_EXPR* child;
359 SCIP_Real childvalue;
360
361 assert(expr != NULL);
362 assert(childidx == 0);
363 assert(SCIPexprGetNChildren(expr) == 1);
364 assert(SCIPexprGetEvalValue(expr) != SCIP_INVALID); /*lint !e777*/
365
366 child = SCIPexprGetChildren(expr)[0];
367 assert(child != NULL);
368 assert(!SCIPisExprValue(scip, child));
369
370 childvalue = SCIPexprGetEvalValue(child);
371
372 /* derivative is not defined for x = 0 */
373 if( childvalue <= 0.0 )
374 *val = SCIP_INVALID;
375 else
376 *val = -1.0 - log(childvalue);
377
378 return SCIP_OKAY;
379 }
380
381 /** expression interval evaluation callback */
382 static
383 SCIP_DECL_EXPRINTEVAL(intevalEntropy)
384 { /*lint --e{715}*/
385 SCIP_INTERVAL childinterval;
386
387 assert(expr != NULL);
388 assert(SCIPexprGetData(expr) == NULL);
389 assert(SCIPexprGetNChildren(expr) == 1);
390
391 childinterval = SCIPexprGetActivity(SCIPexprGetChildren(expr)[0]);
392
393 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, childinterval) )
394 SCIPintervalSetEmpty(interval);
395 else
396 SCIPintervalEntropy(SCIP_INTERVAL_INFINITY, interval, childinterval);
397
398 return SCIP_OKAY;
399 }
400
401 /** expression estimator callback */
402 static
403 SCIP_DECL_EXPRESTIMATE(estimateEntropy)
404 { /*lint --e{715}*/
405 assert(scip != NULL);
406 assert(expr != NULL);
407 assert(localbounds != NULL);
408 assert(globalbounds != NULL);
409 assert(strcmp(SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)), EXPRHDLR_NAME) == 0);
410 assert(refpoint != NULL);
411 assert(coefs != NULL);
412 assert(constant != NULL);
413 assert(islocal != NULL);
414 assert(branchcand != NULL);
415 assert(*branchcand == TRUE);
416 assert(success != NULL);
417
418 *success = FALSE;
419
420 /* use secant for underestimate (locally valid) */
421 if( !overestimate )
422 {
423 SCIP_Real lb;
424 SCIP_Real ub;
425 SCIP_Real vallb;
426 SCIP_Real valub;
427
428 lb = localbounds[0].inf;
429 ub = localbounds[0].sup;
430
431 if( lb < 0.0 || SCIPisInfinity(scip, ub) || SCIPisEQ(scip, lb, ub) )
432 return SCIP_OKAY;
433
434 assert(lb >= 0.0 && ub >= 0.0);
435 assert(ub - lb != 0.0);
436
437 vallb = (lb == 0.0) ? 0.0 : -lb * log(lb);
438 valub = (ub == 0.0) ? 0.0 : -ub * log(ub);
439
440 coefs[0] = (valub - vallb) / (ub - lb);
441 *constant = valub - coefs[0] * ub;
442 assert(SCIPisEQ(scip, *constant, vallb - coefs[0] * lb));
443
444 *islocal = TRUE;
445 }
446 /* use gradient cut for overestimate (globally valid) */
447 else
448 {
449 if( !SCIPisPositive(scip, refpoint[0]) )
450 {
451 /* if refpoint is 0 (then lb=0 probably) or negative, then slope is infinite (or not defined), then try to move away from 0 */
452 if( SCIPisZero(scip, localbounds[0].sup) )
453 return SCIP_OKAY;
454
455 refpoint[0] = SCIPepsilon(scip);
456 }
457
458 /* -x*(1+log(x*)) + x* <= -x*log(x) */
459 coefs[0] = -(1.0 + log(refpoint[0]));
460 *constant = refpoint[0];
461
462 *islocal = FALSE;
463 *branchcand = FALSE;
464 }
465
466 /* give up if the constant or coefficient is too large */
467 if( SCIPisInfinity(scip, REALABS(*constant)) || SCIPisInfinity(scip, REALABS(coefs[0])) )
468 return SCIP_OKAY;
469
470 *success = TRUE;
471
472 return SCIP_OKAY;
473 }
474
475 /** initial estimates callback */
476 static
477 SCIP_DECL_EXPRINITESTIMATES(initestimatesEntropy)
478 { /*lint --e{715}*/
479 SCIP_Real refpointsover[3] = {SCIP_INVALID, SCIP_INVALID, SCIP_INVALID};
480 SCIP_Bool overest[4] = {TRUE, TRUE, TRUE, FALSE};
481 SCIP_Real lb;
482 SCIP_Real ub;
483 int i;
484
485 assert(scip != NULL);
486 assert(expr != NULL);
487 assert(SCIPexprGetNChildren(expr) == 1);
488 assert(strcmp(SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)), EXPRHDLR_NAME) == 0);
489
490 lb = bounds[0].inf;
491 ub = bounds[0].sup;
492
(1) Event cond_false: |
Condition "fabs(lb - ub) <= scip->set->num_epsilon", taking false branch. |
493 if( SCIPisEQ(scip, lb, ub) )
(2) Event if_end: |
End of if statement. |
494 return SCIP_OKAY;
495
(3) Event cond_true: |
Condition "overestimate", taking true branch. |
496 if( overestimate )
497 {
498 /* adjust lb */
(4) Event cond_true: |
Condition "lb >= SCIPepsilon(scip)", taking true branch. |
499 lb = MAX(lb, SCIPepsilon(scip)); /*lint !e666*/
500
501 refpointsover[0] = lb;
(5) Event cond_true: |
Condition "ub >= scip->set->num_infinity", taking true branch. |
502 refpointsover[1] = SCIPisInfinity(scip, ub) ? lb + 2.0 : (lb + ub) / 2;
(6) Event cond_true: |
Condition "ub >= scip->set->num_infinity", taking true branch. |
503 refpointsover[2] = SCIPisInfinity(scip, ub) ? lb + 20.0 : ub;
504 }
505
506 *nreturned = 0;
507
(7) Event cond_true: |
Condition "i < 4", taking true branch. |
(16) Event loop_begin: |
Jumped back to beginning of loop. |
(17) Event cond_true: |
Condition "i < 4", taking true branch. |
(18) Event cond_at_most: |
Checking "i < 4" implies that "i" may be up to 3 on the true branch. |
Also see events: |
[overrun-local] |
508 for( i = 0; i < 4; ++i )
509 {
(8) Event cond_true: |
Condition "overest[i]", taking true branch. |
(9) Event cond_false: |
Condition "!overestimate", taking false branch. |
(10) Event cond_false: |
Condition "!overest[i]", taking false branch. |
(19) Event cond_true: |
Condition "overest[i]", taking true branch. |
(20) Event cond_false: |
Condition "!overestimate", taking false branch. |
(21) Event cond_false: |
Condition "!overest[i]", taking false branch. |
510 if( (overest[i] && !overestimate) || (!overest[i] && (overestimate || SCIPisInfinity(scip, ub))) )
(11) Event if_end: |
End of if statement. |
(22) Event if_end: |
End of if statement. |
511 continue;
512
513 assert(!overest[i] || (SCIPisLE(scip, refpointsover[i], ub) && SCIPisGE(scip, refpointsover[i], lb))); /*lint !e661*/
514
(12) Event cond_true: |
Condition "overest[i]", taking true branch. |
(23) Event cond_true: |
Condition "overest[i]", taking true branch. |
515 if( overest[i] )
516 { /*lint !e661*/
517 /* -x*(1+log(x*)) + x* <= -x*log(x) */
518 assert(i < 3);
519 coefs[*nreturned][0] = -(1.0 + log(refpointsover[i]));
(24) 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] |
520 constant[*nreturned] = refpointsover[i];
(13) Event if_fallthrough: |
Falling through to end of if statement. |
521 }
522 else
523 {
524 assert(lb > 0.0 && ub >= 0.0);
525 assert(ub - lb != 0.0);
526
527 coefs[*nreturned][0] = (-ub * log(ub) + lb * log(lb)) / (ub - lb);
528 constant[*nreturned] = -ub * log(ub) - coefs[*nreturned][0] * ub;
529 assert(SCIPisEQ(scip, constant[*nreturned], -lb * log(lb) - coefs[*nreturned][0] * lb));
(14) Event if_end: |
End of if statement. |
530 }
531
532 ++(*nreturned);
(15) Event loop: |
Jumping back to the beginning of the loop. |
533 }
534
535 return SCIP_OKAY;
536 }
537
538 /** expression reverse propagation callback */
539 static
540 SCIP_DECL_EXPRREVERSEPROP(reversepropEntropy)
541 { /*lint --e{715}*/
542 SCIP_INTERVAL newinterval;
543
544 assert(scip != NULL);
545 assert(expr != NULL);
546 assert(SCIPexprGetNChildren(expr) == 1);
547 assert(childrenbounds != NULL);
548 assert(infeasible != NULL);
549
550 /* compute resulting intervals (reverseProp handles childinterval being empty) */
551 SCIP_CALL( reverseProp(scip, bounds, childrenbounds[0], &newinterval) );
552 assert(SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, newinterval) || newinterval.inf >= 0.0);
553
554 childrenbounds[0] = newinterval;
555
556 return SCIP_OKAY;
557 }
558
559 /** entropy hash callback */
560 static
561 SCIP_DECL_EXPRHASH(hashEntropy)
562 { /*lint --e{715}*/
563 assert(expr != NULL);
564 assert(SCIPexprGetNChildren(expr) == 1);
565 assert(hashkey != NULL);
566 assert(childrenhashes != NULL);
567
568 *hashkey = EXPRHDLR_HASHKEY;
569 *hashkey ^= childrenhashes[0];
570
571 return SCIP_OKAY;
572 }
573
574 /** expression curvature detection callback */
575 static
576 SCIP_DECL_EXPRCURVATURE(curvatureEntropy)
577 { /*lint --e{715}*/
578 assert(scip != NULL);
579 assert(expr != NULL);
580 assert(childcurv != NULL);
581 assert(success != NULL);
582 assert(SCIPexprGetNChildren(expr) == 1);
583
584 /* to be concave, the child needs to be concave, too; we cannot be convex or linear */
585 if( exprcurvature == SCIP_EXPRCURV_CONCAVE )
586 {
587 *childcurv = SCIP_EXPRCURV_CONCAVE;
588 *success = TRUE;
589 }
590 else
591 *success = FALSE;
592
593 return SCIP_OKAY;
594 }
595
596 /** expression monotonicity detection callback */
597 static
598 SCIP_DECL_EXPRMONOTONICITY(monotonicityEntropy)
599 { /*lint --e{715}*/
600 SCIP_EXPR* child;
601 SCIP_INTERVAL childbounds;
602 SCIP_Real brpoint = exp(-1.0);
603
604 assert(scip != NULL);
605 assert(expr != NULL);
606 assert(result != NULL);
607 assert(childidx == 0);
608
609 child = SCIPexprGetChildren(expr)[0];
610 assert(child != NULL);
611
612 SCIP_CALL( SCIPevalExprActivity(scip, child) );
613 childbounds = SCIPexprGetActivity(child);
614
615 if( childbounds.sup <= brpoint )
616 *result = SCIP_MONOTONE_INC;
617 else if( childbounds.inf >= brpoint )
618 *result = SCIP_MONOTONE_DEC;
619 else
620 *result = SCIP_MONOTONE_UNKNOWN;
621
622 return SCIP_OKAY;
623 }
624
625 /** expression integrality detection callback */
626 static
627 SCIP_DECL_EXPRINTEGRALITY(integralityEntropy)
628 { /*lint --e{715}*/
629 assert(scip != NULL);
630 assert(expr != NULL);
631 assert(isintegral != NULL);
632
633 /* TODO it is possible to check for the special case that the child is integral and its bounds are [0,1]; in
634 * this case the entropy expression can only achieve 0 and is thus integral
635 */
636 *isintegral = FALSE;
637
638 return SCIP_OKAY;
639 }
640
641 /** creates the handler for entropy expressions and includes it into SCIP */
642 SCIP_RETCODE SCIPincludeExprhdlrEntropy(
643 SCIP* scip /**< SCIP data structure */
644 )
645 {
646 SCIP_EXPRHDLRDATA* exprhdlrdata;
647 SCIP_EXPRHDLR* exprhdlr;
648
649 /* create expression handler data */
650 exprhdlrdata = NULL;
651
652 /* include expression handler */
653 SCIP_CALL( SCIPincludeExprhdlr(scip, &exprhdlr, EXPRHDLR_NAME, EXPRHDLR_DESC, EXPRHDLR_PRECEDENCE,
654 evalEntropy, exprhdlrdata) );
655 assert(exprhdlr != NULL);
656
657 SCIPexprhdlrSetCopyFreeHdlr(exprhdlr, copyhdlrEntropy, NULL);
658 SCIPexprhdlrSetCopyFreeData(exprhdlr, copydataEntropy, freedataEntropy);
659 SCIPexprhdlrSetSimplify(exprhdlr, simplifyEntropy);
660 SCIPexprhdlrSetParse(exprhdlr, parseEntropy);
661 SCIPexprhdlrSetIntEval(exprhdlr, intevalEntropy);
662 SCIPexprhdlrSetEstimate(exprhdlr, initestimatesEntropy, estimateEntropy);
663 SCIPexprhdlrSetReverseProp(exprhdlr, reversepropEntropy);
664 SCIPexprhdlrSetHash(exprhdlr, hashEntropy);
665 SCIPexprhdlrSetDiff(exprhdlr, bwdiffEntropy, NULL ,NULL);
666 SCIPexprhdlrSetCurvature(exprhdlr, curvatureEntropy);
667 SCIPexprhdlrSetMonotonicity(exprhdlr, monotonicityEntropy);
668 SCIPexprhdlrSetIntegrality(exprhdlr, integralityEntropy);
669
670 return SCIP_OKAY;
671 }
672
673 /** creates an entropy expression */
674 SCIP_RETCODE SCIPcreateExprEntropy(
675 SCIP* scip, /**< SCIP data structure */
676 SCIP_EXPR** expr, /**< pointer where to store expression */
677 SCIP_EXPR* child, /**< child expression */
678 SCIP_DECL_EXPR_OWNERCREATE((*ownercreate)), /**< function to call to create ownerdata */
679 void* ownercreatedata /**< data to pass to ownercreate */
680 )
681 {
682 SCIP_EXPRHDLR* exprhdlr;
683 SCIP_EXPRDATA* exprdata;
684
685 assert(expr != NULL);
686 assert(child != NULL);
687
688 exprhdlr = SCIPfindExprhdlr(scip, EXPRHDLR_NAME);
689 assert(exprhdlr != NULL);
690
691 /* create expression data */
692 exprdata = NULL;
693
694 /* create expression */
695 SCIP_CALL( SCIPcreateExpr(scip, expr, exprhdlr, exprdata, 1, &child, ownercreate, ownercreatedata) );
696
697 return SCIP_OKAY;
698 }
699
700 /** indicates whether expression is of entropy-type */ /*lint -e{715}*/
701 SCIP_Bool SCIPisExprEntropy(
702 SCIP* scip, /**< SCIP data structure */
703 SCIP_EXPR* expr /**< expression */
704 )
705 { /*lint --e{715}*/
706 assert(expr != NULL);
707
708 return strcmp(SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)), EXPRHDLR_NAME) == 0;
709 }
710