1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the program and library */
4 /* SCIP --- Solving Constraint Integer Programs */
5 /* */
6 /* Copyright (C) 2002-2021 Konrad-Zuse-Zentrum */
7 /* fuer Informationstechnik Berlin */
8 /* */
9 /* SCIP is distributed under the terms of the ZIB Academic License. */
10 /* */
11 /* You should have received a copy of the ZIB Academic License */
12 /* along with SCIP; see the file COPYING. If not visit scipopt.org. */
13 /* */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15
16 /**@file nlpi/expr.c
17 * @ingroup OTHER_CFILES
18 * @brief methods for expressions, expression trees, expression graphs, and related
19 * @author Stefan Vigerske
20 * @author Thorsten Gellermann
21 * @author Ingmar Vierhaus (exprparse)
22 */
23
24 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
25
26 #include <stdarg.h>
27 #include <string.h>
28 #include <math.h>
29 #include <ctype.h>
30
31 #include "nlpi/pub_expr.h"
32 #include "nlpi/struct_expr.h"
33 #include "nlpi/exprinterpret.h"
34
35 #include "scip/intervalarith.h"
36 #include "scip/pub_misc.h"
37 #include "scip/misc.h"
38 #include "scip/pub_message.h"
39
40
41 #define SCIP_EXPRESSION_MAXCHILDEST 16 /**< estimate on maximal number of children */
42
43 /** sign of a value (-1 or +1)
44 *
45 * 0.0 has sign +1
46 */
47 #define SIGN(x) ((x) >= 0.0 ? 1.0 : -1.0)
48
49 /** ensures that a block memory array has at least a given size
50 *
51 * if cursize is 0, then *array1 can be NULL
52 */
53 #define ensureBlockMemoryArraySize(blkmem, array1, cursize, minsize) \
54 do { \
55 int __newsize; \
56 assert((blkmem) != NULL); \
57 if( *(cursize) >= (minsize) ) \
58 break; \
59 __newsize = calcGrowSize(minsize); \
60 assert(__newsize >= (minsize)); \
61 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, array1, *(cursize), __newsize) ); \
62 *(cursize) = __newsize; \
63 } while( FALSE )
64
65 #ifdef SCIP_DISABLED_CODE /* this macro is currently not used, which offends lint, so disable it */
66 /** ensures that two block memory arrays have at least a given size
67 *
68 * if cursize is 0, then arrays can be NULL
69 */
70 #define ensureBlockMemoryArraySize2(blkmem, array1, array2, cursize, minsize) \
71 do { \
72 int __newsize; \
73 assert((blkmem) != NULL); \
74 if( *(cursize) >= (minsize) ) \
75 break; \
76 __newsize = calcGrowSize(minsize); \
77 assert(__newsize >= (minsize)); \
78 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, array1, *(cursize), __newsize) ); \
79 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, array2, *(cursize), __newsize) ); \
80 *(cursize) = __newsize; \
81 } while( FALSE )
82 #endif
83
84 /** ensures that three block memory arrays have at least a given size
85 *
86 * if cursize is 0, then arrays can be NULL
87 */
88 #define ensureBlockMemoryArraySize3(blkmem, array1, array2, array3, cursize, minsize) \
89 do { \
90 int __newsize; \
91 assert((blkmem) != NULL); \
92 if( *(cursize) >= (minsize) ) \
93 break; \
94 __newsize = calcGrowSize(minsize); \
95 assert(__newsize >= (minsize)); \
96 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, array1, *(cursize), __newsize) ); \
97 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, array2, *(cursize), __newsize) ); \
98 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, array3, *(cursize), __newsize) ); \
99 *(cursize) = __newsize; \
100 } while( FALSE )
101
102 /**@name Miscellaneous private methods */
103 /**@{ */
104
105 /** calculate memory size for dynamically allocated arrays (copied from scip/set.c) */
106 static
calcGrowSize(int num)107 int calcGrowSize(
108 int num /**< minimum number of entries to store */
109 )
110 {
111 int size;
112
113 /* calculate the size with this loop, such that the resulting numbers are always the same (-> block memory) */
114 size = 4;
115 while( size < num )
116 size = (int)(1.2 * size + 4);
117
118 return size;
119 }
120
121 /** expression graph nodes comparison to use in sorting methods
122 *
123 * The nodes need to have been added to the expression graph (depth,pos >= 0).
124 * The better node is the one with the lower depth and lower position, if depth is equal.
125 */
126 static
SCIP_DECL_SORTPTRCOMP(exprgraphnodecomp)127 SCIP_DECL_SORTPTRCOMP(exprgraphnodecomp)
128 {
129 SCIP_EXPRGRAPHNODE* node1 = (SCIP_EXPRGRAPHNODE*)elem1;
130 SCIP_EXPRGRAPHNODE* node2 = (SCIP_EXPRGRAPHNODE*)elem2;
131
132 assert(node1 != NULL);
133 assert(node2 != NULL);
134 assert(node1->depth >= 0);
135 assert(node1->pos >= 0);
136 assert(node2->depth >= 0);
137 assert(node2->pos >= 0);
138
139 if( node1->depth != node2->depth )
140 return node1->depth - node2->depth;
141
142 /* there should be no two nodes on the same position */
143 assert((node1->pos != node2->pos) || (node1 == node2));
144
145 return node1->pos - node2->pos;
146 }
147
148 /** checks if a given new lower bound is tighter (w.r.t. given bound strengthening epsilon) than the old one (copied from scip/set.c) */
149 static
isLbBetter(SCIP_Real minstrength,SCIP_Real newlb,SCIP_Real oldlb,SCIP_Real oldub)150 SCIP_Bool isLbBetter(
151 SCIP_Real minstrength, /**< minimal relative improvement required to be a better bound */
152 SCIP_Real newlb, /**< new lower bound */
153 SCIP_Real oldlb, /**< old lower bound */
154 SCIP_Real oldub /**< old upper bound */
155 )
156 {
157 SCIP_Real eps;
158
159 /* nothing can be tighter than an empty interval */
160 if( oldlb > oldub )
161 return FALSE;
162
163 eps = REALABS(oldlb);
164 eps = MIN(oldub - oldlb, eps);
165 return EPSGT(newlb, oldlb, minstrength * MAX(eps, 1e-3));
166 }
167
168 /** checks if a given new upper bound is tighter (w.r.t. given bound strengthening epsilon) than the old one (copied from scip/set.c) */
169 static
isUbBetter(SCIP_Real minstrength,SCIP_Real newub,SCIP_Real oldlb,SCIP_Real oldub)170 SCIP_Bool isUbBetter(
171 SCIP_Real minstrength, /**< minimal relative improvement required to be a better bound */
172 SCIP_Real newub, /**< new upper bound */
173 SCIP_Real oldlb, /**< old lower bound */
174 SCIP_Real oldub /**< old upper bound */
175 )
176 {
177 SCIP_Real eps;
178
179 /* nothing can be tighter than an empty interval */
180 if( oldlb > oldub )
181 return FALSE;
182
183 eps = REALABS(oldub);
184 eps = MIN(oldub - oldlb, eps);
185 return EPSLT(newub, oldub, minstrength * MAX(eps, 1e-3));
186 }
187
188 /**@} */
189
190 /**@name Expression curvature methods */
191 /**@{ */
192
193 /** curvature names as strings */
194 static
195 const char* curvnames[4] =
196 {
197 "unknown",
198 "convex",
199 "concave",
200 "linear"
201 };
202
203 #undef SCIPexprcurvAdd
204
205 /** gives curvature for a sum of two functions with given curvature */
SCIPexprcurvAdd(SCIP_EXPRCURV curv1,SCIP_EXPRCURV curv2)206 SCIP_EXPRCURV SCIPexprcurvAdd(
207 SCIP_EXPRCURV curv1, /**< curvature of first summand */
208 SCIP_EXPRCURV curv2 /**< curvature of second summand */
209 )
210 {
211 return (SCIP_EXPRCURV) (curv1 & curv2);
212 }
213
214 /** gives the curvature for the negation of a function with given curvature */
SCIPexprcurvNegate(SCIP_EXPRCURV curvature)215 SCIP_EXPRCURV SCIPexprcurvNegate(
216 SCIP_EXPRCURV curvature /**< curvature of function */
217 )
218 {
219 switch( curvature )
220 {
221 case SCIP_EXPRCURV_CONCAVE:
222 return SCIP_EXPRCURV_CONVEX;
223
224 case SCIP_EXPRCURV_CONVEX:
225 return SCIP_EXPRCURV_CONCAVE;
226
227 case SCIP_EXPRCURV_LINEAR:
228 case SCIP_EXPRCURV_UNKNOWN:
229 /* can return curvature, do this below */
230 break;
231
232 default:
233 SCIPerrorMessage("unknown curvature status.\n");
234 SCIPABORT();
235 }
236
237 return curvature;
238 }
239
240 /** gives curvature for a functions with given curvature multiplied by a constant factor */
SCIPexprcurvMultiply(SCIP_Real factor,SCIP_EXPRCURV curvature)241 SCIP_EXPRCURV SCIPexprcurvMultiply(
242 SCIP_Real factor, /**< constant factor */
243 SCIP_EXPRCURV curvature /**< curvature of other factor */
244 )
245 {
246 if( factor == 0.0 )
247 return SCIP_EXPRCURV_LINEAR;
248 if( factor > 0.0 )
249 return curvature;
250 return SCIPexprcurvNegate(curvature);
251 }
252
253 /** gives curvature for base^exponent for given bounds and curvature of base-function and constant exponent */
SCIPexprcurvPower(SCIP_INTERVAL basebounds,SCIP_EXPRCURV basecurv,SCIP_Real exponent)254 SCIP_EXPRCURV SCIPexprcurvPower(
255 SCIP_INTERVAL basebounds, /**< bounds on base function */
256 SCIP_EXPRCURV basecurv, /**< curvature of base function */
257 SCIP_Real exponent /**< exponent */
258 )
259 {
260 SCIP_Bool expisint;
261
262 assert(basebounds.inf <= basebounds.sup);
263
264 if( exponent == 0.0 )
265 return SCIP_EXPRCURV_LINEAR;
266
267 if( exponent == 1.0 )
268 return basecurv;
269
270 expisint = EPSISINT(exponent, 0.0); /*lint !e835*/
271
272 /* if exponent is fractional, then power is not defined for a negative base
273 * thus, consider only positive part of basebounds
274 */
275 if( !expisint && basebounds.inf < 0.0 )
276 {
277 basebounds.inf = 0.0;
278 if( basebounds.sup < 0.0 )
279 return SCIP_EXPRCURV_LINEAR;
280 }
281
282 /* if basebounds contains 0.0, consider negative and positive interval separately, if possible */
283 if( basebounds.inf < 0.0 && basebounds.sup > 0.0 )
284 {
285 SCIP_INTERVAL leftbounds;
286 SCIP_INTERVAL rightbounds;
287
288 /* something like x^(-2) may look convex on each side of zero, but is not convex on the whole interval due to the singularity at 0.0 */
289 if( exponent < 0.0 )
290 return SCIP_EXPRCURV_UNKNOWN;
291
292 SCIPintervalSetBounds(&leftbounds, basebounds.inf, 0.0);
293 SCIPintervalSetBounds(&rightbounds, 0.0, basebounds.sup);
294
295 return (SCIP_EXPRCURV) (SCIPexprcurvPower(leftbounds, basecurv, exponent) & SCIPexprcurvPower(rightbounds, basecurv, exponent));
296 }
297 assert(basebounds.inf >= 0.0 || basebounds.sup <= 0.0);
298
299 /* (base^exponent)'' = exponent * ( (exponent-1) base^(exponent-2) (base')^2 + base^(exponent-1) base'' )
300 *
301 * if base'' is positive, i.e., base is convex, then
302 * - for base > 0.0 and exponent > 1.0, the second deriv. is positive -> convex
303 * - for base < 0.0 and exponent > 1.0, we can't say (first and second summand opposite signs)
304 * - for base > 0.0 and 0.0 < exponent < 1.0, we can't say (first sommand negative, second summand positive)
305 * - for base > 0.0 and exponent < 0.0, we can't say (first and second summand opposite signs)
306 * - for base < 0.0 and exponent < 0.0 and even, the second deriv. is positive -> convex
307 * - for base < 0.0 and exponent < 0.0 and odd, the second deriv. is negative -> concave
308 *
309 * if base'' is negative, i.e., base is concave, then
310 * - for base > 0.0 and exponent > 1.0, we can't say (first summand positive, second summand negative)
311 * - for base < 0.0 and exponent > 1.0 and even, the second deriv. is positive -> convex
312 * - for base < 0.0 and exponent > 1.0 and odd, the second deriv. is negative -> concave
313 * - for base > 0.0 and 0.0 < exponent < 1.0, the second deriv. is negative -> concave
314 * - for base > 0.0 and exponent < 0.0, the second deriv. is positive -> convex
315 * - for base < 0.0 and exponent < 0.0, we can't say (first and second summand opposite signs)
316 *
317 * if base'' is zero, i.e., base is linear, then
318 * (base^exponent)'' = exponent * (exponent-1) base^(exponent-2) (base')^2
319 * - just multiply signs
320 */
321
322 if( basecurv == SCIP_EXPRCURV_LINEAR )
323 {
324 SCIP_Real sign;
325
326 /* base^(exponent-2) is negative, if base < 0.0 and exponent is odd */
327 sign = exponent * (exponent - 1.0);
328 assert(basebounds.inf >= 0.0 || expisint);
329 if( basebounds.inf < 0.0 && ((int)exponent)%2 != 0 )
330 sign *= -1.0;
331 assert(sign != 0.0);
332
333 return sign > 0.0 ? SCIP_EXPRCURV_CONVEX : SCIP_EXPRCURV_CONCAVE;
334 }
335
336 if( basecurv == SCIP_EXPRCURV_CONVEX )
337 {
338 if( basebounds.sup <= 0.0 && exponent < 0.0 && expisint )
339 return ((int)exponent)%2 == 0 ? SCIP_EXPRCURV_CONVEX : SCIP_EXPRCURV_CONCAVE;
340 if( basebounds.inf >= 0.0 && exponent > 1.0 )
341 return SCIP_EXPRCURV_CONVEX ;
342 return SCIP_EXPRCURV_UNKNOWN;
343 }
344
345 if( basecurv == SCIP_EXPRCURV_CONCAVE )
346 {
347 if( basebounds.sup <= 0.0 && exponent > 1.0 && expisint )
348 return ((int)exponent)%2 == 0 ? SCIP_EXPRCURV_CONVEX : SCIP_EXPRCURV_CONCAVE;
349 if( basebounds.inf >= 0.0 && exponent < 1.0 )
350 return exponent < 0.0 ? SCIP_EXPRCURV_CONVEX : SCIP_EXPRCURV_CONCAVE;
351 return SCIP_EXPRCURV_UNKNOWN;
352 }
353
354 return SCIP_EXPRCURV_UNKNOWN;
355 }
356
357 /** gives curvature for a monomial with given curvatures and bounds for each factor
358 *
359 * See Maranas and Floudas, Finding All Solutions of Nonlinearly Constrained Systems of Equations, JOGO 7, 1995
360 * for the categorization in the case that all factors are linear.
361 */
SCIPexprcurvMonomial(int nfactors,SCIP_Real * exponents,int * factoridxs,SCIP_EXPRCURV * factorcurv,SCIP_INTERVAL * factorbounds)362 SCIP_EXPRCURV SCIPexprcurvMonomial(
363 int nfactors, /**< number of factors in monomial */
364 SCIP_Real* exponents, /**< exponents in monomial, or NULL if all 1.0 */
365 int* factoridxs, /**< indices of factors (but not exponents), or NULL if identity mapping */
366 SCIP_EXPRCURV* factorcurv, /**< curvature of each factor */
367 SCIP_INTERVAL* factorbounds /**< bounds of each factor */
368 )
369 {
370 SCIP_Real mult;
371 SCIP_Real e;
372 SCIP_EXPRCURV curv;
373 SCIP_EXPRCURV fcurv;
374 int nnegative;
375 int npositive;
376 SCIP_Real sum;
377 SCIP_Bool expcurvpos;
378 SCIP_Bool expcurvneg;
379 int j;
380 int f;
381
382 assert(nfactors >= 0);
383 assert(factorcurv != NULL || nfactors == 0);
384 assert(factorbounds != NULL || nfactors == 0);
385
386 if( nfactors == 0 )
387 return SCIP_EXPRCURV_LINEAR;
388
389 if( nfactors == 1 )
390 {
391 f = factoridxs != NULL ? factoridxs[0] : 0;
392 e = exponents != NULL ? exponents[0] : 1.0;
393 /* SCIPdebugMessage("monomial [%g,%g]^%g is %s\n",
394 factorbounds[f].inf, factorbounds[f].sup, e,
395 SCIPexprcurvGetName(SCIPexprcurvPower(factorbounds[f], factorcurv[f], e))); */
396 return SCIPexprcurvPower(factorbounds[f], factorcurv[f], e); /*lint !e613*/
397 }
398
399 mult = 1.0;
400
401 nnegative = 0; /* number of negative exponents */
402 npositive = 0; /* number of positive exponents */
403 sum = 0.0; /* sum of exponents */
404 expcurvpos = TRUE; /* whether exp_j * f_j''(x) >= 0 for all factors (assuming f_j >= 0) */
405 expcurvneg = TRUE; /* whether exp_j * f_j''(x) <= 0 for all factors (assuming f_j >= 0) */
406
407 for( j = 0; j < nfactors; ++j )
408 {
409 f = factoridxs != NULL ? factoridxs[j] : j;
410 if( factorcurv[f] == SCIP_EXPRCURV_UNKNOWN ) /*lint !e613*/
411 return SCIP_EXPRCURV_UNKNOWN;
412 if( factorbounds[f].inf < 0.0 && factorbounds[f].sup > 0.0 ) /*lint !e613*/
413 return SCIP_EXPRCURV_UNKNOWN;
414
415 e = exponents != NULL ? exponents[j] : 1.0;
416 if( e < 0.0 )
417 ++nnegative;
418 else
419 ++npositive;
420 sum += e;
421
422 if( factorbounds[f].inf < 0.0 ) /*lint !e613*/
423 {
424 /* if argument is negative, then exponent should be integer */
425 assert(EPSISINT(e, 0.0)); /*lint !e835*/
426
427 /* flip j'th argument: (f_j)^(exp_j) = (-1)^(exp_j) (-f_j)^(exp_j) */
428
429 /* -f_j has negated curvature of f_j */
430 fcurv = SCIPexprcurvNegate(factorcurv[f]); /*lint !e613*/
431
432 /* negate monomial, if exponent is odd, i.e., (-1)^(exp_j) = -1 */
433 if( (int)e % 2 != 0 )
434 mult *= -1.0;
435 }
436 else
437 {
438 fcurv = factorcurv[f]; /*lint !e613*/
439 }
440
441 /* check if exp_j * fcurv is convex (>= 0) and/or concave */
442 fcurv = SCIPexprcurvMultiply(e, fcurv);
443 if( !(fcurv & SCIP_EXPRCURV_CONVEX) )
444 expcurvpos = FALSE;
445 if( !(fcurv & SCIP_EXPRCURV_CONCAVE) )
446 expcurvneg = FALSE;
447 }
448
449 /* if all factors are linear, then a product f_j^exp_j with f_j >= 0 is convex if
450 * - all exponents are negative, or
451 * - all except one exponent j* are negative and exp_j* >= 1 - sum_{j!=j*}exp_j, but the latter is equivalent to sum_j exp_j >= 1
452 * further, the product is concave if
453 * - all exponents are positive and the sum of exponents is <= 1.0
454 *
455 * if factors are nonlinear, then we require additionally, that for convexity
456 * - each factor is convex if exp_j >= 0, or concave if exp_j <= 0, i.e., exp_j*f_j'' >= 0
457 * and for concavity, we require that
458 * - all factors are concave, i.e., exp_j*f_j'' <= 0
459 */
460
461 if( nnegative == nfactors && expcurvpos )
462 curv = SCIP_EXPRCURV_CONVEX;
463 else if( nnegative == nfactors-1 && EPSGE(sum, 1.0, 1e-9) && expcurvpos )
464 curv = SCIP_EXPRCURV_CONVEX;
465 else if( npositive == nfactors && EPSLE(sum, 1.0, 1e-9) && expcurvneg )
466 curv = SCIP_EXPRCURV_CONCAVE;
467 else
468 curv = SCIP_EXPRCURV_UNKNOWN;
469 curv = SCIPexprcurvMultiply(mult, curv);
470
471 return curv;
472 }
473
474 /** gives name as string for a curvature */
SCIPexprcurvGetName(SCIP_EXPRCURV curv)475 const char* SCIPexprcurvGetName(
476 SCIP_EXPRCURV curv /**< curvature */
477 )
478 {
479 assert(curv <= SCIP_EXPRCURV_LINEAR); /*lint !e685*/
480
481 return curvnames[curv];
482 }
483
484 /**@} */
485
486 /**@name Quadratic expression data private methods */
487 /**@{ */
488
489 /** creates SCIP_EXPRDATA_QUADRATIC data structure from given quadratic elements */
490 static
quadraticdataCreate(BMS_BLKMEM * blkmem,SCIP_EXPRDATA_QUADRATIC ** quadraticdata,SCIP_Real constant,int nchildren,SCIP_Real * lincoefs,int nquadelems,SCIP_QUADELEM * quadelems)491 SCIP_RETCODE quadraticdataCreate(
492 BMS_BLKMEM* blkmem, /**< block memory data structure */
493 SCIP_EXPRDATA_QUADRATIC** quadraticdata, /**< buffer to store pointer to quadratic data */
494 SCIP_Real constant, /**< constant */
495 int nchildren, /**< number of children */
496 SCIP_Real* lincoefs, /**< linear coefficients of children, or NULL if all 0.0 */
497 int nquadelems, /**< number of quadratic elements */
498 SCIP_QUADELEM* quadelems /**< quadratic elements */
499 )
500 {
501 assert(blkmem != NULL);
502 assert(quadraticdata != NULL);
503 assert(quadelems != NULL || nquadelems == 0);
504 assert(nchildren >= 0);
505
506 SCIP_ALLOC( BMSallocBlockMemory(blkmem, quadraticdata) );
507
508 (*quadraticdata)->constant = constant;
509 (*quadraticdata)->lincoefs = NULL;
510 (*quadraticdata)->nquadelems = nquadelems;
511 (*quadraticdata)->quadelems = NULL;
512 (*quadraticdata)->sorted = (nquadelems <= 1);
513
514 if( lincoefs != NULL )
515 {
516 SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &(*quadraticdata)->lincoefs, lincoefs, nchildren) );
517 }
518
519 if( nquadelems > 0 )
520 {
521 SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &(*quadraticdata)->quadelems, quadelems, nquadelems) );
522 }
523
524 return SCIP_OKAY;
525 }
526
527 /** sorts quadratic elements in a SCIP_EXPRDATA_QUADRATIC data structure */
528 static
quadraticdataSort(SCIP_EXPRDATA_QUADRATIC * quadraticdata)529 void quadraticdataSort(
530 SCIP_EXPRDATA_QUADRATIC* quadraticdata /**< quadratic data */
531 )
532 {
533 assert(quadraticdata != NULL);
534
535 if( quadraticdata->sorted )
536 {
537 #ifndef NDEBUG
538 int i;
539 for( i = 1; i < quadraticdata->nquadelems; ++i )
540 {
541 assert(quadraticdata->quadelems[i].idx1 <= quadraticdata->quadelems[i].idx2);
542 assert(quadraticdata->quadelems[i-1].idx1 <= quadraticdata->quadelems[i].idx1);
543 assert(quadraticdata->quadelems[i-1].idx1 < quadraticdata->quadelems[i].idx1 ||
544 quadraticdata->quadelems[i-1].idx2 <= quadraticdata->quadelems[i].idx2);
545 }
546 #endif
547 return;
548 }
549
550 if( quadraticdata->nquadelems > 0 )
551 SCIPquadelemSort(quadraticdata->quadelems, quadraticdata->nquadelems);
552
553 quadraticdata->sorted = TRUE;
554 }
555
556 /**@} */
557
558 /**@name Polynomial expression data private methods */
559 /**@{ */
560
561 /** compares two monomials
562 *
563 * gives 0 if monomials are equal */
564 static
SCIP_DECL_SORTPTRCOMP(monomialdataCompare)565 SCIP_DECL_SORTPTRCOMP(monomialdataCompare)
566 {
567 SCIP_EXPRDATA_MONOMIAL* monomial1;
568 SCIP_EXPRDATA_MONOMIAL* monomial2;
569
570 int i;
571
572 assert(elem1 != NULL);
573 assert(elem2 != NULL);
574
575 monomial1 = (SCIP_EXPRDATA_MONOMIAL*)elem1;
576 monomial2 = (SCIP_EXPRDATA_MONOMIAL*)elem2;
577
578 /* make sure, both monomials are equal */
579 SCIPexprSortMonomialFactors(monomial1);
580 SCIPexprSortMonomialFactors(monomial2);
581
582 /* for the first factor where both monomials differ,
583 * we return either the difference in the child indices, if children are different
584 * or the sign of the difference in the exponents
585 */
586 for( i = 0; i < monomial1->nfactors && i < monomial2->nfactors; ++i )
587 {
588 if( monomial1->childidxs[i] != monomial2->childidxs[i] )
589 return monomial1->childidxs[i] - monomial2->childidxs[i];
590 if( monomial1->exponents[i] > monomial2->exponents[i] )
591 return 1;
592 else if( monomial1->exponents[i] < monomial2->exponents[i] )
593 return -1;
594 }
595
596 /* if the factors of one monomial are a proper subset of the factors of the other monomial,
597 * we return the difference in the number of monomials
598 */
599 return monomial1->nfactors - monomial2->nfactors;
600 }
601
602 /** ensures that the factors arrays of a monomial have at least a given size */
603 static
monomialdataEnsureFactorsSize(BMS_BLKMEM * blkmem,SCIP_EXPRDATA_MONOMIAL * monomialdata,int minsize)604 SCIP_RETCODE monomialdataEnsureFactorsSize(
605 BMS_BLKMEM* blkmem, /**< block memory data structure */
606 SCIP_EXPRDATA_MONOMIAL* monomialdata, /**< monomial data */
607 int minsize /**< minimal size of factors arrays */
608 )
609 {
610 assert(blkmem != NULL);
611 assert(monomialdata != NULL);
612
613 if( minsize > monomialdata->factorssize )
614 {
615 int newsize;
616
617 newsize = calcGrowSize(minsize);
618 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &monomialdata->childidxs, monomialdata->factorssize, newsize) );
619 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &monomialdata->exponents, monomialdata->factorssize, newsize) );
620 monomialdata->factorssize = newsize;
621 }
622 assert(minsize <= monomialdata->factorssize);
623
624 return SCIP_OKAY;
625 }
626
627 /** creates SCIP_EXPRDATA_POLYNOMIAL data structure from given monomials */
628 static
polynomialdataCreate(BMS_BLKMEM * blkmem,SCIP_EXPRDATA_POLYNOMIAL ** polynomialdata,int nmonomials,SCIP_EXPRDATA_MONOMIAL ** monomials,SCIP_Real constant,SCIP_Bool copymonomials)629 SCIP_RETCODE polynomialdataCreate(
630 BMS_BLKMEM* blkmem, /**< block memory data structure */
631 SCIP_EXPRDATA_POLYNOMIAL** polynomialdata,/**< buffer to store pointer to polynomial data */
632 int nmonomials, /**< number of monomials */
633 SCIP_EXPRDATA_MONOMIAL** monomials, /**< monomials */
634 SCIP_Real constant, /**< constant part */
635 SCIP_Bool copymonomials /**< whether to copy monomials, or copy only given pointers, in which case polynomialdata assumes ownership of monomial structure */
636 )
637 {
638 assert(blkmem != NULL);
639 assert(polynomialdata != NULL);
640 assert(monomials != NULL || nmonomials == 0);
641
642 SCIP_ALLOC( BMSallocBlockMemory(blkmem, polynomialdata) );
643
644 (*polynomialdata)->constant = constant;
645 (*polynomialdata)->nmonomials = nmonomials;
646 (*polynomialdata)->monomialssize = nmonomials;
647 (*polynomialdata)->monomials = NULL;
648 (*polynomialdata)->sorted = (nmonomials <= 1);
649
650 if( nmonomials > 0 )
651 {
652 int i;
653
654 if( copymonomials )
655 {
656 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &(*polynomialdata)->monomials, nmonomials) );
657
658 for( i = 0; i < nmonomials; ++i )
659 {
660 assert(monomials[i] != NULL); /*lint !e613*/
661 SCIP_CALL( SCIPexprCreateMonomial(blkmem, &(*polynomialdata)->monomials[i],
662 monomials[i]->coef, monomials[i]->nfactors, monomials[i]->childidxs, monomials[i]->exponents) ); /*lint !e613*/
663 }
664 }
665 else
666 {
667 SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &(*polynomialdata)->monomials, monomials, nmonomials) );
668 }
669 }
670
671 return SCIP_OKAY;
672 }
673
674 /** creates a copy of a SCIP_EXPRDATA_POLYNOMIAL data structure */
675 static
polynomialdataCopy(BMS_BLKMEM * blkmem,SCIP_EXPRDATA_POLYNOMIAL ** polynomialdata,SCIP_EXPRDATA_POLYNOMIAL * sourcepolynomialdata)676 SCIP_RETCODE polynomialdataCopy(
677 BMS_BLKMEM* blkmem, /**< block memory data structure */
678 SCIP_EXPRDATA_POLYNOMIAL** polynomialdata,/**< buffer to store pointer to polynomial data */
679 SCIP_EXPRDATA_POLYNOMIAL* sourcepolynomialdata /**< polynomial data to copy */
680 )
681 {
682 assert(blkmem != NULL);
683 assert(polynomialdata != NULL);
684 assert(sourcepolynomialdata != NULL);
685
686 SCIP_ALLOC( BMSduplicateBlockMemory(blkmem, polynomialdata, sourcepolynomialdata) );
687
688 (*polynomialdata)->monomialssize = sourcepolynomialdata->nmonomials;
689 if( sourcepolynomialdata->nmonomials > 0 )
690 {
691 int i;
692
693 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &(*polynomialdata)->monomials, (*polynomialdata)->monomialssize) );
694
695 for( i = 0; i < sourcepolynomialdata->nmonomials; ++i )
696 {
697 assert(sourcepolynomialdata->monomials[i] != NULL); /*lint !e613*/
698 SCIP_CALL( SCIPexprCreateMonomial(blkmem, &(*polynomialdata)->monomials[i], sourcepolynomialdata->monomials[i]->coef,
699 sourcepolynomialdata->monomials[i]->nfactors, sourcepolynomialdata->monomials[i]->childidxs, sourcepolynomialdata->monomials[i]->exponents) );
700 (*polynomialdata)->monomials[i]->sorted = sourcepolynomialdata->monomials[i]->sorted;
701 }
702 }
703 else
704 {
705 (*polynomialdata)->monomials = NULL;
706 }
707
708 return SCIP_OKAY;
709 }
710
711 /** frees a SCIP_EXPRDATA_POLYNOMIAL data structure */
712 static
polynomialdataFree(BMS_BLKMEM * blkmem,SCIP_EXPRDATA_POLYNOMIAL ** polynomialdata)713 void polynomialdataFree(
714 BMS_BLKMEM* blkmem, /**< block memory data structure */
715 SCIP_EXPRDATA_POLYNOMIAL** polynomialdata /**< pointer to polynomial data to free */
716 )
717 {
718 assert(blkmem != NULL);
719 assert(polynomialdata != NULL);
720 assert(*polynomialdata != NULL);
721
722 if( (*polynomialdata)->monomialssize > 0 )
723 {
724 int i;
725
726 for( i = 0; i < (*polynomialdata)->nmonomials; ++i )
727 {
728 assert((*polynomialdata)->monomials[i] != NULL);
729 SCIPexprFreeMonomial(blkmem, &(*polynomialdata)->monomials[i]);
730 assert((*polynomialdata)->monomials[i] == NULL);
731 }
732
733 BMSfreeBlockMemoryArray(blkmem, &(*polynomialdata)->monomials, (*polynomialdata)->monomialssize);
734 }
735 assert((*polynomialdata)->monomials == NULL);
736
737 BMSfreeBlockMemory(blkmem, polynomialdata);
738 }
739
740 /** ensures that the monomials array of a polynomial has at least a given size */
741 static
polynomialdataEnsureMonomialsSize(BMS_BLKMEM * blkmem,SCIP_EXPRDATA_POLYNOMIAL * polynomialdata,int minsize)742 SCIP_RETCODE polynomialdataEnsureMonomialsSize(
743 BMS_BLKMEM* blkmem, /**< block memory data structure */
744 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata, /**< polynomial data */
745 int minsize /**< minimal size of monomials array */
746 )
747 {
748 assert(blkmem != NULL);
749 assert(polynomialdata != NULL);
750
751 ensureBlockMemoryArraySize(blkmem, &polynomialdata->monomials, &polynomialdata->monomialssize, minsize);
752 assert(minsize <= polynomialdata->monomialssize);
753
754 return SCIP_OKAY;
755 }
756
757 /** adds an array of monomials to a polynomial */
758 static
polynomialdataAddMonomials(BMS_BLKMEM * blkmem,SCIP_EXPRDATA_POLYNOMIAL * polynomialdata,int nmonomials,SCIP_EXPRDATA_MONOMIAL ** monomials,SCIP_Bool copymonomials)759 SCIP_RETCODE polynomialdataAddMonomials(
760 BMS_BLKMEM* blkmem, /**< block memory of expression */
761 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata, /**< polynomial data */
762 int nmonomials, /**< number of monomials to add */
763 SCIP_EXPRDATA_MONOMIAL** monomials, /**< the monomials to add */
764 SCIP_Bool copymonomials /**< whether to copy monomials or to assume ownership */
765 )
766 {
767 int i;
768
769 assert(blkmem != NULL);
770 assert(polynomialdata != NULL);
771 assert(monomials != NULL || nmonomials == 0);
772
773 if( nmonomials == 0 )
774 return SCIP_OKAY;
775
776 SCIP_CALL( polynomialdataEnsureMonomialsSize(blkmem, polynomialdata, polynomialdata->nmonomials + nmonomials) );
777 assert(polynomialdata->monomialssize >= polynomialdata->nmonomials + nmonomials);
778
779 if( copymonomials )
780 {
781 for( i = 0; i < nmonomials; ++i )
782 {
783 assert(monomials[i] != NULL); /*lint !e613*/
784 SCIP_CALL( SCIPexprCreateMonomial(blkmem, &polynomialdata->monomials[polynomialdata->nmonomials + i],
785 monomials[i]->coef, monomials[i]->nfactors, monomials[i]->childidxs, monomials[i]->exponents) ); /*lint !e613*/
786 }
787 }
788 else
789 {
790 BMScopyMemoryArray(&polynomialdata->monomials[polynomialdata->nmonomials], monomials, nmonomials); /*lint !e866*/
791 }
792 polynomialdata->nmonomials += nmonomials;
793
794 polynomialdata->sorted = (polynomialdata->nmonomials <= 1);
795
796 return SCIP_OKAY;
797 }
798
799 /** ensures that monomials of a polynomial are sorted */
800 static
polynomialdataSortMonomials(SCIP_EXPRDATA_POLYNOMIAL * polynomialdata)801 void polynomialdataSortMonomials(
802 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata /**< polynomial expression */
803 )
804 {
805 assert(polynomialdata != NULL);
806
807 if( polynomialdata->sorted )
808 {
809 #ifndef NDEBUG
810 int i;
811
812 /* a polynom with more than one monoms can only be sorted if its monoms are sorted */
813 for( i = 1; i < polynomialdata->nmonomials; ++i )
814 {
815 assert(polynomialdata->monomials[i-1]->sorted);
816 assert(polynomialdata->monomials[i]->sorted);
817 assert(monomialdataCompare(polynomialdata->monomials[i-1], polynomialdata->monomials[i]) <= 0);
818 }
819 #endif
820 return;
821 }
822
823 if( polynomialdata->nmonomials > 0 )
824 SCIPsortPtr((void**)polynomialdata->monomials, monomialdataCompare, polynomialdata->nmonomials);
825
826 polynomialdata->sorted = TRUE;
827 }
828
829 /** merges monomials that differ only in coefficient into a single monomial
830 *
831 * Eliminates monomials with coefficient between -eps and eps.
832 */
833 static
polynomialdataMergeMonomials(BMS_BLKMEM * blkmem,SCIP_EXPRDATA_POLYNOMIAL * polynomialdata,SCIP_Real eps,SCIP_Bool mergefactors)834 void polynomialdataMergeMonomials(
835 BMS_BLKMEM* blkmem, /**< block memory */
836 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata, /**< polynomial data */
837 SCIP_Real eps, /**< threshold under which numbers are treat as zero */
838 SCIP_Bool mergefactors /**< whether to merge factors in monomials too */
839 )
840 {
841 int i;
842 int offset;
843 int oldnfactors;
844
845 assert(polynomialdata != NULL);
846 assert(eps >= 0.0);
847
848 polynomialdataSortMonomials(polynomialdata);
849
850 /* merge monomials by adding their coefficients, eliminate monomials with no factors or zero coefficient*/
851 offset = 0;
852 i = 0;
853 while( i + offset < polynomialdata->nmonomials )
854 {
855 if( offset > 0 )
856 {
857 assert(polynomialdata->monomials[i] == NULL);
858 assert(polynomialdata->monomials[i+offset] != NULL);
859 polynomialdata->monomials[i] = polynomialdata->monomials[i+offset];
860 #ifndef NDEBUG
861 polynomialdata->monomials[i+offset] = NULL;
862 #endif
863 }
864
865 if( mergefactors )
866 {
867 oldnfactors = polynomialdata->monomials[i]->nfactors;
868 SCIPexprMergeMonomialFactors(polynomialdata->monomials[i], eps);
869
870 /* if monomial has changed, then we cannot assume anymore that polynomial is sorted */
871 if( oldnfactors != polynomialdata->monomials[i]->nfactors )
872 polynomialdata->sorted = FALSE;
873 }
874
875 while( i+offset+1 < polynomialdata->nmonomials )
876 {
877 assert(polynomialdata->monomials[i+offset+1] != NULL);
878 if( mergefactors )
879 {
880 oldnfactors = polynomialdata->monomials[i+offset+1]->nfactors;
881 SCIPexprMergeMonomialFactors(polynomialdata->monomials[i+offset+1], eps);
882
883 /* if monomial has changed, then we cannot assume anymore that polynomial is sorted */
884 if( oldnfactors != polynomialdata->monomials[i+offset+1]->nfactors )
885 polynomialdata->sorted = FALSE;
886 }
887 if( monomialdataCompare((void*)polynomialdata->monomials[i], (void*)polynomialdata->monomials[i+offset+1]) != 0 )
888 break;
889 polynomialdata->monomials[i]->coef += polynomialdata->monomials[i+offset+1]->coef;
890 SCIPexprFreeMonomial(blkmem, &polynomialdata->monomials[i+offset+1]);
891 ++offset;
892 }
893
894 if( polynomialdata->monomials[i]->nfactors == 0 )
895 {
896 /* constant monomial */
897 polynomialdata->constant += polynomialdata->monomials[i]->coef;
898 SCIPexprFreeMonomial(blkmem, &polynomialdata->monomials[i]);
899 ++offset;
900 continue;
901 }
902
903 if( EPSZ(polynomialdata->monomials[i]->coef, eps) )
904 {
905 SCIPexprFreeMonomial(blkmem, &polynomialdata->monomials[i]);
906 ++offset;
907 continue;
908 }
909
910 ++i;
911 }
912
913 #ifndef NDEBUG
914 for( ; i < polynomialdata->nmonomials; ++i )
915 assert(polynomialdata->monomials[i] == NULL);
916 #endif
917
918 polynomialdata->nmonomials -= offset;
919
920 if( EPSZ(polynomialdata->constant, eps) )
921 polynomialdata->constant = 0.0;
922 }
923
924 /** multiplies each summand of a polynomial by a given constant */
925 static
polynomialdataMultiplyByConstant(BMS_BLKMEM * blkmem,SCIP_EXPRDATA_POLYNOMIAL * polynomialdata,SCIP_Real factor)926 void polynomialdataMultiplyByConstant(
927 BMS_BLKMEM* blkmem, /**< block memory */
928 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata, /**< polynomial data */
929 SCIP_Real factor /**< constant factor */
930 )
931 {
932 int i;
933
934 assert(polynomialdata != NULL);
935
936 if( factor == 1.0 )
937 return;
938
939 if( factor == 0.0 )
940 {
941 for( i = 0; i < polynomialdata->nmonomials; ++i )
942 SCIPexprFreeMonomial(blkmem, &polynomialdata->monomials[i]);
943 polynomialdata->nmonomials = 0;
944 }
945 else
946 {
947 for( i = 0; i < polynomialdata->nmonomials; ++i )
948 SCIPexprChgMonomialCoef(polynomialdata->monomials[i], polynomialdata->monomials[i]->coef * factor);
949 }
950
951 polynomialdata->constant *= factor;
952 }
953
954 /** multiplies each summand of a polynomial by a given monomial */
955 static
polynomialdataMultiplyByMonomial(BMS_BLKMEM * blkmem,SCIP_EXPRDATA_POLYNOMIAL * polynomialdata,SCIP_EXPRDATA_MONOMIAL * factor,int * childmap)956 SCIP_RETCODE polynomialdataMultiplyByMonomial(
957 BMS_BLKMEM* blkmem, /**< block memory */
958 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata, /**< polynomial data */
959 SCIP_EXPRDATA_MONOMIAL* factor, /**< monomial factor */
960 int* childmap /**< map children in factor to children in expr, or NULL for 1:1 */
961 )
962 {
963 int i;
964
965 assert(blkmem != NULL);
966 assert(factor != NULL);
967 assert(polynomialdata != NULL);
968
969 if( factor->nfactors == 0 )
970 {
971 polynomialdataMultiplyByConstant(blkmem, polynomialdata, factor->coef);
972 return SCIP_OKAY;
973 }
974
975 /* multiply each monomial by factor */
976 for( i = 0; i < polynomialdata->nmonomials; ++i )
977 {
978 SCIP_CALL( SCIPexprMultiplyMonomialByMonomial(blkmem, polynomialdata->monomials[i], factor, childmap) );
979 }
980
981 /* add new monomial for constant multiplied by factor */
982 if( polynomialdata->constant != 0.0 )
983 {
984 SCIP_CALL( polynomialdataEnsureMonomialsSize(blkmem, polynomialdata, polynomialdata->nmonomials+1) );
985 SCIP_CALL( SCIPexprCreateMonomial(blkmem, &polynomialdata->monomials[polynomialdata->nmonomials], polynomialdata->constant, 0, NULL, NULL) );
986 SCIP_CALL( SCIPexprMultiplyMonomialByMonomial(blkmem, polynomialdata->monomials[polynomialdata->nmonomials], factor, childmap) );
987 ++polynomialdata->nmonomials;
988 polynomialdata->sorted = FALSE;
989 polynomialdata->constant = 0.0;
990 }
991
992 return SCIP_OKAY;
993 }
994
995 /** multiplies a polynomial by a polynomial
996 *
997 * Factors need to be different.
998 */
999 static
polynomialdataMultiplyByPolynomial(BMS_BLKMEM * blkmem,SCIP_EXPRDATA_POLYNOMIAL * polynomialdata,SCIP_EXPRDATA_POLYNOMIAL * factordata,int * childmap)1000 SCIP_RETCODE polynomialdataMultiplyByPolynomial(
1001 BMS_BLKMEM* blkmem, /**< block memory */
1002 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata, /**< polynomial data */
1003 SCIP_EXPRDATA_POLYNOMIAL* factordata, /**< polynomial factor data */
1004 int* childmap /**< map children in factor to children in polynomialdata, or NULL for 1:1 */
1005 )
1006 {
1007 int i1;
1008 int i2;
1009 int orignmonomials;
1010
1011 assert(blkmem != NULL);
1012 assert(polynomialdata != NULL);
1013 assert(factordata != NULL);
1014 assert(polynomialdata != factordata);
1015
1016 if( factordata->nmonomials == 0 )
1017 {
1018 polynomialdataMultiplyByConstant(blkmem, polynomialdata, factordata->constant);
1019 return SCIP_OKAY;
1020 }
1021 assert(factordata->monomials != NULL);
1022
1023 if( factordata->nmonomials == 1 && factordata->constant == 0.0 )
1024 {
1025 SCIP_CALL( polynomialdataMultiplyByMonomial(blkmem, polynomialdata, factordata->monomials[0], childmap) );
1026 return SCIP_OKAY;
1027 }
1028
1029 /* turn constant into a monomial, so we can assume below that constant is 0.0 */
1030 if( polynomialdata->constant != 0.0 )
1031 {
1032 SCIP_CALL( polynomialdataEnsureMonomialsSize(blkmem, polynomialdata, polynomialdata->nmonomials+1) );
1033 SCIP_CALL( SCIPexprCreateMonomial(blkmem, &polynomialdata->monomials[polynomialdata->nmonomials], polynomialdata->constant, 0, NULL, NULL) );
1034 ++polynomialdata->nmonomials;
1035 polynomialdata->sorted = FALSE;
1036 polynomialdata->constant = 0.0;
1037 }
1038
1039 SCIP_CALL( polynomialdataEnsureMonomialsSize(blkmem, polynomialdata, polynomialdata->nmonomials * (factordata->nmonomials + (factordata->constant == 0.0 ? 0 : 1))) );
1040
1041 /* for each monomial in factordata (except the last, if factordata->constant is 0),
1042 * duplicate monomials from polynomialdata and multiply them by the monomial for factordata */
1043 orignmonomials = polynomialdata->nmonomials;
1044 for( i2 = 0; i2 < factordata->nmonomials; ++i2 )
1045 {
1046 /* add a copy of original monomials to end of polynomialdata's monomials array */
1047 assert(polynomialdata->nmonomials + orignmonomials <= polynomialdata->monomialssize); /* reallocating in polynomialdataAddMonomials would make the polynomialdata->monomials invalid, so assert that above the monomials array was made large enough */
1048 SCIP_CALL( polynomialdataAddMonomials(blkmem, polynomialdata, orignmonomials, polynomialdata->monomials, TRUE) );
1049 assert(polynomialdata->nmonomials == (i2+2) * orignmonomials);
1050
1051 /* multiply each copied monomial by current monomial from factordata */
1052 for( i1 = (i2+1) * orignmonomials; i1 < (i2+2) * orignmonomials; ++i1 )
1053 {
1054 SCIP_CALL( SCIPexprMultiplyMonomialByMonomial(blkmem, polynomialdata->monomials[i1], factordata->monomials[i2], childmap) );
1055 }
1056
1057 if( factordata->constant == 0.0 && i2 == factordata->nmonomials - 2 )
1058 {
1059 ++i2;
1060 break;
1061 }
1062 }
1063
1064 if( factordata->constant != 0.0 )
1065 {
1066 assert(i2 == factordata->nmonomials);
1067 /* multiply original monomials in polynomialdata by constant in factordata */
1068 for( i1 = 0; i1 < orignmonomials; ++i1 )
1069 SCIPexprChgMonomialCoef(polynomialdata->monomials[i1], polynomialdata->monomials[i1]->coef * factordata->constant);
1070 }
1071 else
1072 {
1073 assert(i2 == factordata->nmonomials - 1);
1074 /* multiply original monomials in polynomialdata by last monomial in factordata */
1075 for( i1 = 0; i1 < orignmonomials; ++i1 )
1076 {
1077 SCIP_CALL( SCIPexprMultiplyMonomialByMonomial(blkmem, polynomialdata->monomials[i1], factordata->monomials[i2], childmap) );
1078 }
1079 }
1080
1081 return SCIP_OKAY;
1082 }
1083
1084 /** takes a power of a polynomial
1085 *
1086 * Exponent needs to be an integer,
1087 * polynomial needs to be a monomial, if exponent is negative.
1088 */
1089 static
polynomialdataPower(BMS_BLKMEM * blkmem,SCIP_EXPRDATA_POLYNOMIAL * polynomialdata,int exponent)1090 SCIP_RETCODE polynomialdataPower(
1091 BMS_BLKMEM* blkmem, /**< block memory */
1092 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata, /**< polynomial data */
1093 int exponent /**< exponent of power operation */
1094 )
1095 {
1096 SCIP_EXPRDATA_POLYNOMIAL* factor;
1097 int i;
1098
1099 assert(blkmem != NULL);
1100 assert(polynomialdata != NULL);
1101
1102 if( exponent == 0 )
1103 {
1104 /* x^0 = 1, except if x = 0 */
1105 if( polynomialdata->nmonomials == 0 && polynomialdata->constant == 0.0 )
1106 {
1107 polynomialdata->constant = 0.0;
1108 }
1109 else
1110 {
1111 polynomialdata->constant = 1.0;
1112
1113 for( i = 0; i < polynomialdata->nmonomials; ++i )
1114 SCIPexprFreeMonomial(blkmem, &polynomialdata->monomials[i]);
1115 polynomialdata->nmonomials = 0;
1116 }
1117
1118 return SCIP_OKAY;
1119 }
1120
1121 if( exponent == 1 )
1122 return SCIP_OKAY;
1123
1124 if( polynomialdata->nmonomials == 1 && polynomialdata->constant == 0.0 )
1125 {
1126 /* polynomial is a single monomial */
1127 SCIPexprMonomialPower(polynomialdata->monomials[0], exponent);
1128 return SCIP_OKAY;
1129 }
1130
1131 if( polynomialdata->nmonomials == 0 )
1132 {
1133 /* polynomial is a constant */
1134 polynomialdata->constant = pow(polynomialdata->constant, (SCIP_Real)exponent);
1135 return SCIP_OKAY;
1136 }
1137
1138 assert(exponent >= 2); /* negative exponents not allowed if more than one monom */
1139
1140 /* todo improve, look how SCIPintervalPowerScalar in intervalarith.c does it */
1141
1142 /* get copy of our polynomial */
1143 SCIP_CALL( polynomialdataCopy(blkmem, &factor, polynomialdata) );
1144
1145 /* do repeated multiplication */
1146 for( i = 2; i <= exponent; ++i )
1147 {
1148 SCIP_CALL( polynomialdataMultiplyByPolynomial(blkmem, polynomialdata, factor, NULL) );
1149 polynomialdataMergeMonomials(blkmem, polynomialdata, 0.0, TRUE);
1150 }
1151
1152 /* free copy again */
1153 polynomialdataFree(blkmem, &factor);
1154
1155 return SCIP_OKAY;
1156 }
1157
1158 /** applies a mapping of child indices to the indices used in polynomial monomials */
1159 static
polynomialdataApplyChildmap(SCIP_EXPRDATA_POLYNOMIAL * polynomialdata,int * childmap)1160 void polynomialdataApplyChildmap(
1161 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata, /**< polynomial data */
1162 int* childmap /**< mapping of child indices */
1163 )
1164 {
1165 SCIP_EXPRDATA_MONOMIAL* monomial;
1166 int i;
1167 int j;
1168
1169 assert(polynomialdata != NULL);
1170
1171 for( i = 0; i < polynomialdata->nmonomials; ++i )
1172 {
1173 monomial = polynomialdata->monomials[i];
1174 assert(monomial != NULL);
1175
1176 for( j = 0; j < monomial->nfactors; ++j )
1177 {
1178 monomial->childidxs[j] = childmap[monomial->childidxs[j]];
1179 assert(monomial->childidxs[j] >= 0);
1180 }
1181 monomial->sorted = FALSE;
1182 }
1183
1184 polynomialdata->sorted = FALSE;
1185 }
1186
1187 /** replaces a factor in a monomial by a polynomial and expands the result */
1188 static
polynomialdataExpandMonomialFactor(BMS_BLKMEM * blkmem,SCIP_MESSAGEHDLR * messagehdlr,SCIP_EXPRDATA_POLYNOMIAL * polynomialdata,int monomialpos,int factorpos,SCIP_EXPRDATA_POLYNOMIAL * factorpolynomial,int * childmap,int maxexpansionexponent,SCIP_Bool * success)1189 SCIP_RETCODE polynomialdataExpandMonomialFactor(
1190 BMS_BLKMEM* blkmem, /**< block memory data structure */
1191 SCIP_MESSAGEHDLR* messagehdlr, /**< message handler */
1192 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata, /**< polynomial data where to expand a monomial */
1193 int monomialpos, /**< position of monomial which factor to expand */
1194 int factorpos, /**< position of factor in monomial to expand */
1195 SCIP_EXPRDATA_POLYNOMIAL* factorpolynomial,/**< polynomial that should replace factor */
1196 int* childmap, /**< map of child indices in factorpolynomial to children of polynomial */
1197 int maxexpansionexponent,/**< maximal exponent for which polynomials (with > 1 summands) are expanded */
1198 SCIP_Bool* success /**< buffer to store whether expansion has been done */
1199 )
1200 {
1201 SCIP_EXPRDATA_POLYNOMIAL* factorpolynomialcopy;
1202 SCIP_EXPRDATA_MONOMIAL* monomial;
1203 int i;
1204
1205 assert(blkmem != NULL);
1206 assert(polynomialdata != NULL);
1207 assert(factorpolynomial != NULL);
1208 assert(childmap != NULL || factorpolynomial->nmonomials == 0);
1209 assert(success != NULL);
1210 assert(monomialpos >= 0);
1211 assert(monomialpos < polynomialdata->nmonomials);
1212 assert(factorpos >= 0);
1213
1214 monomial = polynomialdata->monomials[monomialpos];
1215 assert(monomial != NULL);
1216 assert(factorpos < monomial->nfactors);
1217
1218 *success = TRUE;
1219
1220 if( factorpolynomial->nmonomials == 0 )
1221 {
1222 /* factorpolynomial is a constant */
1223
1224 if( !EPSISINT(monomial->exponents[factorpos], 0.0) && factorpolynomial->constant < 0.0 ) /*lint !e835*/
1225 {
1226 /* if polynomial is a negative constant and our exponent is not integer, then cannot do expansion */
1227 SCIPmessagePrintWarning(messagehdlr, "got negative constant %g to the power of a noninteger exponent %g\n", factorpolynomial->constant, monomial->exponents[factorpos]);
1228 *success = FALSE;
1229 return SCIP_OKAY;
1230 }
1231 monomial->coef *= pow(factorpolynomial->constant, monomial->exponents[factorpos]);
1232
1233 /* move last factor to position factorpos */
1234 if( factorpos < monomial->nfactors-1 )
1235 {
1236 monomial->exponents[factorpos] = monomial->exponents[monomial->nfactors-1];
1237 monomial->childidxs[factorpos] = monomial->childidxs[monomial->nfactors-1];
1238 }
1239 --monomial->nfactors;
1240 monomial->sorted = FALSE;
1241 polynomialdata->sorted = FALSE;
1242
1243 return SCIP_OKAY;
1244 }
1245
1246 if( factorpolynomial->constant == 0.0 && factorpolynomial->nmonomials == 1 )
1247 {
1248 /* factorpolynomial is a single monomial */
1249 SCIP_EXPRDATA_MONOMIAL* factormonomial;
1250 int childidx;
1251 SCIP_Real exponent;
1252
1253 factormonomial = factorpolynomial->monomials[0];
1254 assert(factormonomial != NULL);
1255
1256 if( !EPSISINT(monomial->exponents[factorpos], 0.0) ) /*lint !e835*/
1257 {
1258 if( factormonomial->coef < 0.0 )
1259 {
1260 /* if coefficient of monomial is negative and our exponent is not integer, then do not do expansion
1261 * @todo the only case where this could make sense is if the factors can be negative, i.e., when we have negative arguments with an odd exponent: (-x^a)^b = (-x)^(ab) for a odd
1262 */
1263 *success = FALSE;
1264 return SCIP_OKAY;
1265 }
1266 if( factormonomial->nfactors > 1 )
1267 {
1268 /* @todo if there is an even number of factors in factormonomial that are negative, then they always multiply to something positive
1269 * however, we cannot expand them as below, since we cannot compute the single powers
1270 * since we do not have the bounds on the factors here, we skip expansion in this case
1271 * MINLPLib instances tls2,4,6 are examples where we are loosing here (do not recognize convexity)
1272 */
1273 *success = FALSE;
1274 return SCIP_OKAY;
1275 }
1276 }
1277
1278 SCIP_CALL( monomialdataEnsureFactorsSize(blkmem, monomial, monomial->nfactors + factormonomial->nfactors) );
1279
1280 for( i = 0; i < factormonomial->nfactors; ++i )
1281 {
1282 childidx = childmap[factormonomial->childidxs[i]]; /*lint !e613*/
1283 /* can do this because monomial->exponents[factorpos] is assumed to be integer or factormonomial has positive coefficient and only one factor
1284 * thus, if factormonomial->exponents[i] is fractional, then we can assume that it's argument is positive
1285 */
1286 exponent = factormonomial->exponents[i] * monomial->exponents[factorpos];
1287 SCIP_CALL( SCIPexprAddMonomialFactors(blkmem, monomial, 1, &childidx, &exponent) );
1288 }
1289
1290 monomial->coef *= pow(factormonomial->coef, monomial->exponents[factorpos]);
1291
1292 /* move last factor to position factorpos */
1293 if( factorpos < monomial->nfactors-1 )
1294 {
1295 monomial->exponents[factorpos] = monomial->exponents[monomial->nfactors-1];
1296 monomial->childidxs[factorpos] = monomial->childidxs[monomial->nfactors-1];
1297 }
1298 --monomial->nfactors;
1299 monomial->sorted = FALSE;
1300 polynomialdata->sorted = FALSE;
1301
1302 return SCIP_OKAY;
1303 }
1304
1305 /* if exponent is negative or fractional and the polynomial is not just a monomial, then we cannot do expansion */
1306 if( !EPSISINT(monomial->exponents[factorpos], 0.0) || monomial->exponents[factorpos] < 0.0 ) /*lint !e835*/
1307 {
1308 *success = FALSE;
1309 return SCIP_OKAY;
1310 }
1311
1312 /* if exponent is too large, skip expansion */
1313 if( monomial->exponents[factorpos] > maxexpansionexponent )
1314 {
1315 *success = FALSE;
1316 return SCIP_OKAY;
1317 }
1318
1319 /* check whether maximal degree of expansion would exceed maxexpansionexponent
1320 * that is, assume monomial is f1^a1 f2^a2 ... and we want to expand f1 = (g11^beta11 g12^beta12... + g21^beta21 g22^beta22 ... + ...)
1321 * then we do this only if all ai and all beta are > 0.0 and a1 max(beta11+beta12+..., beta21+beta22+..., ...) + a2 + ... < maxexpansionexponent
1322 * exception (there need to be one) is if monomial is just f1
1323 */
1324 if( maxexpansionexponent < INT_MAX && (monomial->nfactors > 1 || monomial->exponents[factorpos] != 1.0) )
1325 {
1326 SCIP_Real restdegree;
1327 SCIP_Real degree;
1328 int j;
1329
1330 restdegree = -monomial->exponents[factorpos];
1331 for( i = 0; i < monomial->nfactors; ++i )
1332 {
1333 if( monomial->exponents[i] < 0.0 )
1334 {
1335 /* ai < 0.0 */
1336 SCIPdebugMessage("skip expansion because factor %d in monomial has negative exponent\n", i);
1337 *success = FALSE;
1338 return SCIP_OKAY;
1339 }
1340 restdegree += monomial->exponents[i];
1341 }
1342
1343 for( i = 0; i < factorpolynomial->nmonomials; ++i )
1344 {
1345 degree = 0.0;
1346 for( j = 0; j < factorpolynomial->monomials[i]->nfactors; ++j )
1347 {
1348 if( factorpolynomial->monomials[i]->exponents[j] < 0.0 )
1349 {
1350 /* beta_ij < 0.0 */
1351 SCIPdebugMessage("skip expansion because %d'th factor in %d'th monomial of factorpolynomial is negative\n", i, j);
1352 *success = FALSE;
1353 return SCIP_OKAY;
1354 }
1355 degree += factorpolynomial->monomials[i]->exponents[j];
1356 }
1357 if( degree * monomial->exponents[factorpos] + restdegree > maxexpansionexponent )
1358 {
1359 /* (beta_i1+beta_i2+...)*monomial->exponents[factorpos] + rest > maxexpansion */
1360 SCIPdebugMessage("skip expansion because degree of %d'th monomial would yield degree %g > max = %d in expansion\n",
1361 i, degree * monomial->exponents[factorpos] + restdegree, maxexpansionexponent);
1362 *success = FALSE;
1363 return SCIP_OKAY;
1364 }
1365 }
1366 }
1367
1368 /* create a copy of factor */
1369 SCIP_CALL( polynomialdataCopy(blkmem, &factorpolynomialcopy, factorpolynomial) );
1370 /* apply childmap to copy */
1371 polynomialdataApplyChildmap(factorpolynomialcopy, childmap);
1372 /* create power of factor */
1373 SCIP_CALL( polynomialdataPower(blkmem, factorpolynomialcopy, (int)EPSFLOOR(monomial->exponents[factorpos], 0.0)) ); /*lint !e835*/
1374
1375 /* remove factor from monomial by moving last factor to position factorpos */
1376 if( factorpos < monomial->nfactors-1 )
1377 {
1378 monomial->exponents[factorpos] = monomial->exponents[monomial->nfactors-1];
1379 monomial->childidxs[factorpos] = monomial->childidxs[monomial->nfactors-1];
1380 }
1381 --monomial->nfactors;
1382 monomial->sorted = FALSE;
1383
1384 /* multiply factor with this reduced monomial */
1385 SCIP_CALL( polynomialdataMultiplyByMonomial(blkmem, factorpolynomialcopy, monomial, NULL) );
1386
1387 /* remove monomial from polynomial and move last monomial to monomialpos */
1388 SCIPexprFreeMonomial(blkmem, &polynomialdata->monomials[monomialpos]);
1389 if( monomialpos < polynomialdata->nmonomials-1 )
1390 polynomialdata->monomials[monomialpos] = polynomialdata->monomials[polynomialdata->nmonomials-1];
1391 --polynomialdata->nmonomials;
1392 polynomialdata->sorted = FALSE;
1393
1394 /* add factorpolynomialcopy to polynomial */
1395 SCIP_CALL( polynomialdataAddMonomials(blkmem, polynomialdata, factorpolynomialcopy->nmonomials, factorpolynomialcopy->monomials, FALSE) );
1396 polynomialdata->constant += factorpolynomialcopy->constant;
1397
1398 factorpolynomialcopy->nmonomials = 0;
1399 polynomialdataFree(blkmem, &factorpolynomialcopy);
1400
1401 return SCIP_OKAY;
1402 }
1403
1404 /**@} */
1405
1406 /**@name Expression operand private methods */
1407 /**@{ */
1408
1409 /** a default implementation of expression interval evaluation that always gives a correct result */
1410 static
SCIP_DECL_EXPRINTEVAL(exprevalIntDefault)1411 SCIP_DECL_EXPRINTEVAL( exprevalIntDefault )
1412 { /*lint --e{715}*/
1413 SCIPintervalSetEntire(infinity, result);
1414
1415 return SCIP_OKAY;
1416 }
1417
1418 /** a default implementation of expression curvature check that always gives a correct result */
1419 static
SCIP_DECL_EXPRCURV(exprcurvDefault)1420 SCIP_DECL_EXPRCURV( exprcurvDefault )
1421 { /*lint --e{715}*/
1422 *result = SCIP_EXPRCURV_UNKNOWN;
1423
1424 return SCIP_OKAY;
1425 }
1426
1427 /** point evaluation for EXPR_VAR */
1428 static
SCIP_DECL_EXPREVAL(exprevalVar)1429 SCIP_DECL_EXPREVAL( exprevalVar )
1430 { /*lint --e{715}*/
1431 assert(result != NULL);
1432 assert(varvals != NULL);
1433
1434 *result = varvals[opdata.intval];
1435
1436 return SCIP_OKAY;
1437 }
1438
1439 /** interval evaluation for EXPR_VAR */
1440 static
SCIP_DECL_EXPRINTEVAL(exprevalIntVar)1441 SCIP_DECL_EXPRINTEVAL( exprevalIntVar )
1442 { /*lint --e{715}*/
1443 assert(result != NULL);
1444 assert(varvals != NULL);
1445
1446 *result = varvals[opdata.intval];
1447
1448 return SCIP_OKAY;
1449 }
1450
1451 /** curvature for EXPR_VAR */
1452 static
SCIP_DECL_EXPRCURV(exprcurvVar)1453 SCIP_DECL_EXPRCURV( exprcurvVar )
1454 { /*lint --e{715}*/
1455 assert(result != NULL);
1456
1457 *result = SCIP_EXPRCURV_LINEAR;
1458
1459 return SCIP_OKAY;
1460 }
1461
1462 /** point evaluation for EXPR_CONST */
1463 static
SCIP_DECL_EXPREVAL(exprevalConst)1464 SCIP_DECL_EXPREVAL( exprevalConst )
1465 { /*lint --e{715}*/
1466 assert(result != NULL);
1467
1468 *result = opdata.dbl;
1469
1470 return SCIP_OKAY;
1471 }
1472
1473 /** interval evaluation for EXPR_CONST */
1474 static
SCIP_DECL_EXPRINTEVAL(exprevalIntConst)1475 SCIP_DECL_EXPRINTEVAL( exprevalIntConst )
1476 { /*lint --e{715}*/
1477 assert(result != NULL);
1478
1479 SCIPintervalSet(result, opdata.dbl);
1480
1481 return SCIP_OKAY;
1482 }
1483
1484 /** curvature for EXPR_CONST */
1485 static
SCIP_DECL_EXPRCURV(exprcurvConst)1486 SCIP_DECL_EXPRCURV( exprcurvConst )
1487 { /*lint --e{715}*/
1488 assert(result != NULL);
1489
1490 *result = SCIP_EXPRCURV_LINEAR;
1491
1492 return SCIP_OKAY;
1493 }
1494
1495 /** point evaluation for EXPR_PARAM */
1496 static
SCIP_DECL_EXPREVAL(exprevalParam)1497 SCIP_DECL_EXPREVAL( exprevalParam )
1498 { /*lint --e{715}*/
1499 assert(result != NULL);
1500 assert(paramvals != NULL );
1501
1502 *result = paramvals[opdata.intval];
1503
1504 return SCIP_OKAY;
1505 }
1506
1507 /** interval evaluation for EXPR_PARAM */
1508 static
SCIP_DECL_EXPRINTEVAL(exprevalIntParam)1509 SCIP_DECL_EXPRINTEVAL( exprevalIntParam )
1510 { /*lint --e{715}*/
1511 assert(result != NULL);
1512 assert(paramvals != NULL );
1513
1514 SCIPintervalSet(result, paramvals[opdata.intval]);
1515
1516 return SCIP_OKAY;
1517 }
1518
1519 /** curvature for EXPR_PARAM */
1520 static
SCIP_DECL_EXPRCURV(exprcurvParam)1521 SCIP_DECL_EXPRCURV( exprcurvParam )
1522 { /*lint --e{715}*/
1523 assert(result != NULL);
1524
1525 *result = SCIP_EXPRCURV_LINEAR;
1526
1527 return SCIP_OKAY;
1528 }
1529
1530 /** point evaluation for EXPR_PLUS */
1531 static
SCIP_DECL_EXPREVAL(exprevalPlus)1532 SCIP_DECL_EXPREVAL( exprevalPlus )
1533 { /*lint --e{715}*/
1534 assert(result != NULL);
1535 assert(argvals != NULL);
1536
1537 *result = argvals[0] + argvals[1];
1538
1539 return SCIP_OKAY;
1540 }
1541
1542 /** interval evaluation for EXPR_PLUS */
1543 static
SCIP_DECL_EXPRINTEVAL(exprevalIntPlus)1544 SCIP_DECL_EXPRINTEVAL( exprevalIntPlus )
1545 { /*lint --e{715}*/
1546 assert(result != NULL);
1547 assert(argvals != NULL);
1548
1549 SCIPintervalAdd(infinity, result, argvals[0], argvals[1]);
1550
1551 return SCIP_OKAY;
1552 }
1553
1554 /** curvature for EXPR_PLUS */
1555 static
SCIP_DECL_EXPRCURV(exprcurvPlus)1556 SCIP_DECL_EXPRCURV( exprcurvPlus )
1557 { /*lint --e{715}*/
1558 assert(result != NULL);
1559 assert(argcurv != NULL);
1560
1561 *result = SCIPexprcurvAdd(argcurv[0], argcurv[1]);
1562
1563 return SCIP_OKAY;
1564 }
1565
1566 /** point evaluation for EXPR_MINUS */
1567 static
SCIP_DECL_EXPREVAL(exprevalMinus)1568 SCIP_DECL_EXPREVAL( exprevalMinus )
1569 { /*lint --e{715}*/
1570 assert(result != NULL);
1571 assert(argvals != NULL);
1572
1573 *result = argvals[0] - argvals[1];
1574
1575 return SCIP_OKAY;
1576 }
1577
1578 /** interval evaluation for EXPR_MINUS */
1579 static
SCIP_DECL_EXPRINTEVAL(exprevalIntMinus)1580 SCIP_DECL_EXPRINTEVAL( exprevalIntMinus )
1581 { /*lint --e{715}*/
1582 assert(result != NULL);
1583 assert(argvals != NULL);
1584
1585 SCIPintervalSub(infinity, result, argvals[0], argvals[1]);
1586
1587 return SCIP_OKAY;
1588 }
1589
1590 /** curvature for EXPR_MINUS */
1591 static
SCIP_DECL_EXPRCURV(exprcurvMinus)1592 SCIP_DECL_EXPRCURV( exprcurvMinus )
1593 { /*lint --e{715}*/
1594 assert(result != NULL);
1595 assert(argcurv != NULL);
1596
1597 *result = SCIPexprcurvAdd(argcurv[0], SCIPexprcurvNegate(argcurv[1]));
1598
1599 return SCIP_OKAY;
1600 }
1601
1602 /** point evaluation for EXPR_MUL */
1603 static
SCIP_DECL_EXPREVAL(exprevalMult)1604 SCIP_DECL_EXPREVAL( exprevalMult )
1605 { /*lint --e{715}*/
1606 assert(result != NULL);
1607 assert(argvals != NULL);
1608
1609 *result = argvals[0] * argvals[1];
1610
1611 return SCIP_OKAY;
1612 }
1613
1614 /** interval evaluation for EXPR_MUL */
1615 static
SCIP_DECL_EXPRINTEVAL(exprevalIntMult)1616 SCIP_DECL_EXPRINTEVAL( exprevalIntMult )
1617 { /*lint --e{715}*/
1618 assert(result != NULL);
1619 assert(argvals != NULL);
1620
1621 SCIPintervalMul(infinity, result, argvals[0], argvals[1]);
1622
1623 return SCIP_OKAY;
1624 }
1625
1626 /** curvature for EXPR_MUL */
1627 static
SCIP_DECL_EXPRCURV(exprcurvMult)1628 SCIP_DECL_EXPRCURV( exprcurvMult )
1629 { /*lint --e{715}*/
1630 assert(result != NULL);
1631 assert(argcurv != NULL);
1632 assert(argbounds != NULL);
1633
1634 /* if one factor is constant, then product is
1635 * - linear, if constant is 0.0
1636 * - same curvature as other factor, if constant is positive
1637 * - negated curvature of other factor, if constant is negative
1638 *
1639 * if both factors are not constant, then product may not be convex nor concave
1640 */
1641 if( argbounds[1].inf == argbounds[1].sup ) /*lint !e777*/
1642 *result = SCIPexprcurvMultiply(argbounds[1].inf, argcurv[0]);
1643 else if( argbounds[0].inf == argbounds[0].sup ) /*lint !e777*/
1644 *result = SCIPexprcurvMultiply(argbounds[0].inf, argcurv[1]);
1645 else
1646 *result = SCIP_EXPRCURV_UNKNOWN;
1647
1648 return SCIP_OKAY;
1649 }
1650
1651 /** point evaluation for EXPR_DIV */
1652 static
1653 #if defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ * 10 >= 490 && !defined(__INTEL_COMPILER)
1654 __attribute__((no_sanitize_undefined))
1655 #endif
SCIP_DECL_EXPREVAL(exprevalDiv)1656 SCIP_DECL_EXPREVAL( exprevalDiv )
1657 { /*lint --e{715}*/
1658 assert(result != NULL);
1659 assert(argvals != NULL);
1660
1661 *result = argvals[0] / argvals[1];
1662
1663 return SCIP_OKAY;
1664 }
1665
1666 /** interval evaluation for EXPR_DIV */
1667 static
SCIP_DECL_EXPRINTEVAL(exprevalIntDiv)1668 SCIP_DECL_EXPRINTEVAL( exprevalIntDiv )
1669 { /*lint --e{715}*/
1670 assert(result != NULL);
1671 assert(argvals != NULL);
1672
1673 SCIPintervalDiv(infinity, result, argvals[0], argvals[1]);
1674
1675 return SCIP_OKAY;
1676 }
1677
1678 /** curvature for EXPR_DIV */
1679 static
SCIP_DECL_EXPRCURV(exprcurvDiv)1680 SCIP_DECL_EXPRCURV( exprcurvDiv )
1681 { /*lint --e{715}*/
1682 assert(result != NULL);
1683 assert(argcurv != NULL);
1684 assert(argbounds != NULL);
1685
1686 /* if denominator is constant, then quotient has curvature sign(denominator) * curv(nominator)
1687 *
1688 * if nominator is a constant, then quotient is
1689 * - sign(nominator) * convex, if denominator is concave and positive
1690 * - sign(nominator) * concave, if denominator is convex and negative
1691 *
1692 * if denominator is positive but convex, then we don't know, e.g.,
1693 * - 1/x^2 is convex for x>=0
1694 * - 1/(1+(x-1)^2) is neither convex nor concave for x >= 0
1695 *
1696 * if both nominator and denominator are not constant, then quotient may not be convex nor concave
1697 */
1698 if( argbounds[1].inf == argbounds[1].sup ) /*lint !e777*/
1699 {
1700 /* denominator is constant */
1701 *result = SCIPexprcurvMultiply(argbounds[1].inf, argcurv[0]);
1702 }
1703 else if( argbounds[0].inf == argbounds[0].sup ) /*lint !e777*/
1704 {
1705 /* nominator is constant */
1706 if( argbounds[1].inf >= 0.0 && (argcurv[1] & SCIP_EXPRCURV_CONCAVE) )
1707 *result = SCIPexprcurvMultiply(argbounds[0].inf, SCIP_EXPRCURV_CONVEX);
1708 else if( argbounds[1].sup <= 0.0 && (argcurv[1] & SCIP_EXPRCURV_CONVEX) )
1709 *result = SCIPexprcurvMultiply(argbounds[0].inf, SCIP_EXPRCURV_CONCAVE);
1710 else
1711 *result = SCIP_EXPRCURV_UNKNOWN;
1712 }
1713 else
1714 {
1715 /* denominator and nominator not constant */
1716 *result = SCIP_EXPRCURV_UNKNOWN;
1717 }
1718
1719 return SCIP_OKAY;
1720 }
1721
1722 /** point evaluation for EXPR_SQUARE */
1723 static
SCIP_DECL_EXPREVAL(exprevalSquare)1724 SCIP_DECL_EXPREVAL( exprevalSquare )
1725 { /*lint --e{715}*/
1726 assert(result != NULL);
1727 assert(argvals != NULL);
1728
1729 *result = argvals[0] * argvals[0];
1730
1731 return SCIP_OKAY;
1732 }
1733
1734 /** interval evaluation for EXPR_SQUARE */
1735 static
SCIP_DECL_EXPRINTEVAL(exprevalIntSquare)1736 SCIP_DECL_EXPRINTEVAL( exprevalIntSquare )
1737 { /*lint --e{715}*/
1738 assert(result != NULL);
1739 assert(argvals != NULL);
1740
1741 SCIPintervalSquare(infinity, result, argvals[0]);
1742
1743 return SCIP_OKAY;
1744 }
1745
1746 /** curvature for EXPR_SQUARE */
1747 static
SCIP_DECL_EXPRCURV(exprcurvSquare)1748 SCIP_DECL_EXPRCURV( exprcurvSquare )
1749 { /*lint --e{715}*/
1750 assert(result != NULL);
1751 assert(argcurv != NULL);
1752 assert(argbounds != NULL);
1753
1754 *result = SCIPexprcurvPower(argbounds[0], argcurv[0], 2.0);
1755
1756 return SCIP_OKAY;
1757 }
1758
1759 /** point evaluation for EXPR_SQRT */
1760 static
SCIP_DECL_EXPREVAL(exprevalSquareRoot)1761 SCIP_DECL_EXPREVAL( exprevalSquareRoot )
1762 { /*lint --e{715}*/
1763 assert(result != NULL);
1764 assert(argvals != NULL);
1765
1766 *result = sqrt(argvals[0]);
1767
1768 return SCIP_OKAY;
1769 }
1770
1771 /** interval evaluation for EXPR_SQRT */
1772 static
SCIP_DECL_EXPRINTEVAL(exprevalIntSquareRoot)1773 SCIP_DECL_EXPRINTEVAL( exprevalIntSquareRoot )
1774 { /*lint --e{715}*/
1775 assert(result != NULL);
1776 assert(argvals != NULL);
1777
1778 SCIPintervalSquareRoot(infinity, result, argvals[0]);
1779
1780 return SCIP_OKAY;
1781 }
1782
1783 /** curvature for EXPR_SQRT */
1784 static
SCIP_DECL_EXPRCURV(exprcurvSquareRoot)1785 SCIP_DECL_EXPRCURV( exprcurvSquareRoot )
1786 { /*lint --e{715}*/
1787 assert(result != NULL);
1788 assert(argcurv != NULL);
1789
1790 /* square-root is concave, if child is concave
1791 * otherwise, we don't know
1792 */
1793
1794 if( argcurv[0] & SCIP_EXPRCURV_CONCAVE )
1795 *result = SCIP_EXPRCURV_CONCAVE;
1796 else
1797 *result = SCIP_EXPRCURV_UNKNOWN;
1798
1799 return SCIP_OKAY;
1800 }
1801
1802 /** point evaluation for EXPR_REALPOWER */
1803 static
SCIP_DECL_EXPREVAL(exprevalRealPower)1804 SCIP_DECL_EXPREVAL( exprevalRealPower )
1805 { /*lint --e{715}*/
1806 assert(result != NULL);
1807 assert(argvals != NULL);
1808
1809 *result = pow(argvals[0], opdata.dbl);
1810
1811 return SCIP_OKAY;
1812 }
1813
1814 /** interval evaluation for EXPR_REALPOWER */
1815 static
SCIP_DECL_EXPRINTEVAL(exprevalIntRealPower)1816 SCIP_DECL_EXPRINTEVAL( exprevalIntRealPower )
1817 { /*lint --e{715}*/
1818 assert(result != NULL);
1819 assert(argvals != NULL);
1820
1821 SCIPintervalPowerScalar(infinity, result, argvals[0], opdata.dbl);
1822
1823 return SCIP_OKAY;
1824 }
1825
1826 /** curvature for EXPR_REALPOWER */
1827 static
SCIP_DECL_EXPRCURV(exprcurvRealPower)1828 SCIP_DECL_EXPRCURV( exprcurvRealPower )
1829 { /*lint --e{715}*/
1830 assert(result != NULL);
1831 assert(argcurv != NULL);
1832 assert(argbounds != NULL);
1833
1834 *result = SCIPexprcurvPower(argbounds[0], argcurv[0], opdata.dbl);
1835
1836 return SCIP_OKAY;
1837 }
1838
1839 /** point evaluation for EXPR_INTPOWER */
1840 static
1841 #if defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ * 10 >= 490 && !defined(__INTEL_COMPILER)
1842 __attribute__((no_sanitize_undefined))
1843 #endif
SCIP_DECL_EXPREVAL(exprevalIntPower)1844 SCIP_DECL_EXPREVAL( exprevalIntPower )
1845 { /*lint --e{715}*/
1846 assert(result != NULL);
1847 assert(argvals != NULL);
1848
1849 switch( opdata.intval )
1850 {
1851 case -1:
1852 *result = 1.0 / argvals[0];
1853 return SCIP_OKAY;
1854
1855 case 0:
1856 *result = 1.0;
1857 return SCIP_OKAY;
1858
1859 case 1:
1860 *result = argvals[0];
1861 return SCIP_OKAY;
1862
1863 case 2:
1864 *result = argvals[0] * argvals[0];
1865 return SCIP_OKAY;
1866
1867 default:
1868 *result = pow(argvals[0], (SCIP_Real)opdata.intval);
1869 }
1870
1871 return SCIP_OKAY;
1872 }
1873
1874 /** interval evaluation for EXPR_INTPOWER */
1875 static
SCIP_DECL_EXPRINTEVAL(exprevalIntIntPower)1876 SCIP_DECL_EXPRINTEVAL( exprevalIntIntPower )
1877 { /*lint --e{715}*/
1878 assert(result != NULL);
1879 assert(argvals != NULL);
1880
1881 SCIPintervalPowerScalar(infinity, result, argvals[0], (SCIP_Real)opdata.intval);
1882
1883 return SCIP_OKAY;
1884 }
1885
1886 /** curvature for EXPR_INTPOWER */
1887 static
SCIP_DECL_EXPRCURV(exprcurvIntPower)1888 SCIP_DECL_EXPRCURV( exprcurvIntPower )
1889 { /*lint --e{715}*/
1890 assert(result != NULL);
1891 assert(argcurv != NULL);
1892 assert(argbounds != NULL);
1893
1894 *result = SCIPexprcurvPower(argbounds[0], argcurv[0], (SCIP_Real)opdata.intval);
1895
1896 return SCIP_OKAY;
1897 }
1898
1899 /** point evaluation for EXPR_SIGNPOWER */
1900 static
SCIP_DECL_EXPREVAL(exprevalSignPower)1901 SCIP_DECL_EXPREVAL( exprevalSignPower )
1902 { /*lint --e{715}*/
1903 assert(result != NULL);
1904 assert(argvals != NULL);
1905
1906 if( argvals[0] > 0 )
1907 *result = pow( argvals[0], opdata.dbl);
1908 else
1909 *result = -pow(-argvals[0], opdata.dbl);
1910
1911 return SCIP_OKAY;
1912 }
1913
1914 /** interval evaluation for EXPR_SIGNPOWER */
1915 static
SCIP_DECL_EXPRINTEVAL(exprevalIntSignPower)1916 SCIP_DECL_EXPRINTEVAL( exprevalIntSignPower )
1917 { /*lint --e{715}*/
1918 assert(result != NULL);
1919 assert(argvals != NULL);
1920
1921 SCIPintervalSignPowerScalar(infinity, result, argvals[0], opdata.dbl);
1922
1923 return SCIP_OKAY;
1924 }
1925
1926 /** curvature for EXPR_SIGNPOWER */
1927 static
SCIP_DECL_EXPRCURV(exprcurvSignPower)1928 SCIP_DECL_EXPRCURV( exprcurvSignPower )
1929 { /*lint --e{715}*/
1930 SCIP_INTERVAL tmp;
1931 SCIP_EXPRCURV left;
1932 SCIP_EXPRCURV right;
1933
1934 assert(result != NULL);
1935 assert(argcurv != NULL);
1936 assert(argbounds != NULL);
1937
1938 /* for x <= 0, signpower(x,c) = -(-x)^c
1939 * for x >= 0, signpower(x,c) = ( x)^c
1940 *
1941 * thus, get curvatures for both parts and "intersect" them
1942 */
1943
1944 if( argbounds[0].inf < 0 )
1945 {
1946 SCIPintervalSetBounds(&tmp, 0.0, -argbounds[0].inf);
1947 left = SCIPexprcurvNegate(SCIPexprcurvPower(tmp, SCIPexprcurvNegate(argcurv[0]), opdata.dbl));
1948 }
1949 else
1950 {
1951 left = SCIP_EXPRCURV_LINEAR;
1952 }
1953
1954 if( argbounds[0].sup > 0 )
1955 {
1956 SCIPintervalSetBounds(&tmp, 0.0, argbounds[0].sup);
1957 right = SCIPexprcurvPower(tmp, argcurv[0], opdata.dbl);
1958 }
1959 else
1960 {
1961 right = SCIP_EXPRCURV_LINEAR;
1962 }
1963
1964 *result = (SCIP_EXPRCURV) (left & right);
1965
1966 return SCIP_OKAY;
1967 }
1968
1969 /** point evaluation for EXPR_EXP */
1970 static
SCIP_DECL_EXPREVAL(exprevalExp)1971 SCIP_DECL_EXPREVAL( exprevalExp )
1972 { /*lint --e{715}*/
1973 assert(result != NULL);
1974 assert(argvals != NULL);
1975
1976 *result = exp(argvals[0]);
1977
1978 return SCIP_OKAY;
1979 }
1980
1981 /** interval evaluation for EXPR_EXP */
1982 static
SCIP_DECL_EXPRINTEVAL(exprevalIntExp)1983 SCIP_DECL_EXPRINTEVAL( exprevalIntExp )
1984 { /*lint --e{715}*/
1985 assert(result != NULL);
1986 assert(argvals != NULL);
1987
1988 SCIPintervalExp(infinity, result, argvals[0]);
1989
1990 return SCIP_OKAY;
1991 }
1992
1993 /** curvature for EXPR_EXP */
1994 static
SCIP_DECL_EXPRCURV(exprcurvExp)1995 SCIP_DECL_EXPRCURV( exprcurvExp )
1996 { /*lint --e{715}*/
1997 assert(result != NULL);
1998 assert(argcurv != NULL);
1999
2000 /* expression is convex if child is convex
2001 * otherwise, we don't know
2002 */
2003 if( argcurv[0] & SCIP_EXPRCURV_CONVEX )
2004 *result = SCIP_EXPRCURV_CONVEX;
2005 else
2006 *result = SCIP_EXPRCURV_UNKNOWN;
2007
2008 return SCIP_OKAY;
2009 }
2010
2011 /** point evaluation for EXPR_LOG */
2012 static
SCIP_DECL_EXPREVAL(exprevalLog)2013 SCIP_DECL_EXPREVAL( exprevalLog )
2014 { /*lint --e{715}*/
2015 assert(result != NULL);
2016 assert(argvals != NULL);
2017
2018 *result = log(argvals[0]);
2019
2020 return SCIP_OKAY;
2021 }
2022
2023 /** interval evaluation for EXPR_LOG */
2024 static
SCIP_DECL_EXPRINTEVAL(exprevalIntLog)2025 SCIP_DECL_EXPRINTEVAL( exprevalIntLog )
2026 { /*lint --e{715}*/
2027 assert(result != NULL);
2028 assert(argvals != NULL);
2029
2030 SCIPintervalLog(infinity, result, argvals[0]);
2031
2032 return SCIP_OKAY;
2033 }
2034
2035 /** curvature for EXPR_LOG */
2036 static
SCIP_DECL_EXPRCURV(exprcurvLog)2037 SCIP_DECL_EXPRCURV( exprcurvLog )
2038 { /*lint --e{715}*/
2039 assert(result != NULL);
2040 assert(argcurv != NULL);
2041
2042 /* expression is concave if child is concave
2043 * otherwise, we don't know
2044 */
2045 if( argcurv[0] & SCIP_EXPRCURV_CONCAVE )
2046 *result = SCIP_EXPRCURV_CONCAVE;
2047 else
2048 *result = SCIP_EXPRCURV_UNKNOWN;
2049
2050 return SCIP_OKAY;
2051 }
2052
2053 /** point evaluation for EXPR_SIN */
2054 static
SCIP_DECL_EXPREVAL(exprevalSin)2055 SCIP_DECL_EXPREVAL( exprevalSin )
2056 { /*lint --e{715}*/
2057 assert(result != NULL);
2058 assert(argvals != NULL);
2059
2060 *result = sin(argvals[0]);
2061
2062 return SCIP_OKAY;
2063 }
2064
2065 /** interval evaluation for EXPR_SIN */
2066 static
SCIP_DECL_EXPRINTEVAL(exprevalIntSin)2067 SCIP_DECL_EXPRINTEVAL( exprevalIntSin )
2068 { /*lint --e{715}*/
2069 assert(result != NULL);
2070 assert(argvals != NULL);
2071 assert(nargs == 1);
2072
2073 SCIPintervalSin(infinity, result, *argvals);
2074
2075 return SCIP_OKAY;
2076 }
2077
2078 /* @todo implement exprcurvSin */
2079 #define exprcurvSin exprcurvDefault
2080
2081 /** point evaluation for EXPR_COS */
2082 static
SCIP_DECL_EXPREVAL(exprevalCos)2083 SCIP_DECL_EXPREVAL( exprevalCos )
2084 { /*lint --e{715}*/
2085 assert(result != NULL);
2086 assert(argvals != NULL);
2087
2088 *result = cos(argvals[0]);
2089
2090 return SCIP_OKAY;
2091 }
2092
2093 /** interval evaluation for EXPR_COS */
2094 static
SCIP_DECL_EXPRINTEVAL(exprevalIntCos)2095 SCIP_DECL_EXPRINTEVAL( exprevalIntCos )
2096 { /*lint --e{715}*/
2097 assert(result != NULL);
2098 assert(argvals != NULL);
2099 assert(nargs == 1);
2100
2101 SCIPintervalCos(infinity, result, *argvals);
2102
2103 return SCIP_OKAY;
2104 }
2105
2106 /* @todo implement exprcurvCos */
2107 #define exprcurvCos exprcurvDefault
2108
2109 /** point evaluation for EXPR_TAN */
2110 static
SCIP_DECL_EXPREVAL(exprevalTan)2111 SCIP_DECL_EXPREVAL( exprevalTan )
2112 { /*lint --e{715}*/
2113 assert(result != NULL);
2114 assert(argvals != NULL);
2115
2116 *result = tan(argvals[0]);
2117
2118 return SCIP_OKAY;
2119 }
2120
2121 /* @todo implement SCIPintervalTan */
2122 #define exprevalIntTan exprevalIntDefault
2123
2124 /* @todo implement exprcurvTan */
2125 #define exprcurvTan exprcurvDefault
2126
2127 /* erf and erfi do not seem to exist on every system, and we cannot really handle them anyway, so they are currently disabled */
2128 #ifdef SCIP_DISABLED_CODE
2129 static
SCIP_DECL_EXPREVAL(exprevalErf)2130 SCIP_DECL_EXPREVAL( exprevalErf )
2131 { /*lint --e{715}*/
2132 assert(result != NULL);
2133 assert(argvals != NULL);
2134
2135 *result = erf(argvals[0]);
2136
2137 return SCIP_OKAY;
2138 }
2139
2140 /* @todo implement SCIPintervalErf */
2141 #define exprevalIntErf exprevalIntDefault
2142
2143 /* @todo implement SCIPintervalErf */
2144 #define exprcurvErf exprcurvDefault
2145
2146 static
SCIP_DECL_EXPREVAL(exprevalErfi)2147 SCIP_DECL_EXPREVAL( exprevalErfi )
2148 { /*lint --e{715}*/
2149 assert(result != NULL);
2150 assert(argvals != NULL);
2151
2152 /* @TODO implement erfi evaluation */
2153 SCIPerrorMessage("erfi not implemented");
2154
2155 return SCIP_ERROR;
2156 }
2157
2158 /* @todo implement SCIPintervalErfi */
2159 #define exprevalIntErfi NULL
2160
2161 #define exprcurvErfi exprcurvDefault
2162 #endif
2163
2164 /** point evaluation for EXPR_MIN */
2165 static
SCIP_DECL_EXPREVAL(exprevalMin)2166 SCIP_DECL_EXPREVAL( exprevalMin )
2167 { /*lint --e{715}*/
2168 assert(result != NULL);
2169 assert(argvals != NULL);
2170
2171 *result = MIN(argvals[0], argvals[1]);
2172
2173 return SCIP_OKAY;
2174 }
2175
2176 /** interval evaluation for EXPR_MIN */
2177 static
SCIP_DECL_EXPRINTEVAL(exprevalIntMin)2178 SCIP_DECL_EXPRINTEVAL( exprevalIntMin )
2179 { /*lint --e{715}*/
2180 assert(result != NULL);
2181 assert(argvals != NULL);
2182
2183 SCIPintervalMin(infinity, result, argvals[0], argvals[1]);
2184
2185 return SCIP_OKAY;
2186 }
2187
2188 /** curvature for EXPR_MIN */
2189 static
SCIP_DECL_EXPRCURV(exprcurvMin)2190 SCIP_DECL_EXPRCURV( exprcurvMin )
2191 { /*lint --e{715}*/
2192 assert(result != NULL);
2193 assert(argcurv != NULL);
2194
2195 /* the minimum of two concave functions is concave
2196 * otherwise, we don't know
2197 */
2198
2199 if( (argcurv[0] & SCIP_EXPRCURV_CONCAVE) && (argcurv[1] & SCIP_EXPRCURV_CONCAVE) )
2200 *result = SCIP_EXPRCURV_CONCAVE;
2201 else
2202 *result = SCIP_EXPRCURV_UNKNOWN;
2203
2204 return SCIP_OKAY;
2205 }
2206
2207 /** point evaluation for EXPR_MAX */
2208 static
SCIP_DECL_EXPREVAL(exprevalMax)2209 SCIP_DECL_EXPREVAL( exprevalMax )
2210 { /*lint --e{715}*/
2211 assert(result != NULL);
2212 assert(argvals != NULL);
2213
2214 *result = MAX(argvals[0], argvals[1]);
2215
2216 return SCIP_OKAY;
2217 }
2218
2219 /** interval evaluation for EXPR_MAX */
2220 static
SCIP_DECL_EXPRINTEVAL(exprevalIntMax)2221 SCIP_DECL_EXPRINTEVAL( exprevalIntMax )
2222 { /*lint --e{715}*/
2223 assert(result != NULL);
2224 assert(argvals != NULL);
2225
2226 SCIPintervalMax(infinity, result, argvals[0], argvals[1]);
2227
2228 return SCIP_OKAY;
2229 }
2230
2231 /** curvature for EXPR_MAX */
2232 static
SCIP_DECL_EXPRCURV(exprcurvMax)2233 SCIP_DECL_EXPRCURV( exprcurvMax )
2234 { /*lint --e{715}*/
2235 assert(result != NULL);
2236 assert(argcurv != NULL);
2237
2238 /* the maximum of two convex functions is convex
2239 * otherwise, we don't know
2240 */
2241 if( (argcurv[0] & SCIP_EXPRCURV_CONVEX) && (argcurv[1] & SCIP_EXPRCURV_CONVEX) )
2242 *result = SCIP_EXPRCURV_CONVEX;
2243 else
2244 *result = SCIP_EXPRCURV_UNKNOWN;
2245
2246 return SCIP_OKAY;
2247 }
2248
2249 /** point evaluation for EXPR_ABS */
2250 static
SCIP_DECL_EXPREVAL(exprevalAbs)2251 SCIP_DECL_EXPREVAL( exprevalAbs )
2252 { /*lint --e{715}*/
2253 assert(result != NULL);
2254 assert(argvals != NULL);
2255
2256 *result = ABS(argvals[0]);
2257
2258 return SCIP_OKAY;
2259 }
2260
2261 /** interval evaluation for EXPR_ABS */
2262 static
SCIP_DECL_EXPRINTEVAL(exprevalIntAbs)2263 SCIP_DECL_EXPRINTEVAL( exprevalIntAbs )
2264 { /*lint --e{715}*/
2265 assert(result != NULL);
2266 assert(argvals != NULL);
2267
2268 SCIPintervalAbs(infinity, result, argvals[0]);
2269
2270 return SCIP_OKAY;
2271 }
2272
2273 /** curvature for EXPR_ABS */
2274 static
SCIP_DECL_EXPRCURV(exprcurvAbs)2275 SCIP_DECL_EXPRCURV( exprcurvAbs )
2276 { /*lint --e{715}*/
2277 assert(result != NULL);
2278 assert(argcurv != NULL);
2279 assert(argbounds != NULL);
2280
2281 /* if child is only negative, then abs(child) = -child
2282 * if child is only positive, then abs(child) = child
2283 * if child is both positive and negative, but also linear, then abs(child) is convex
2284 * otherwise, we don't know
2285 */
2286 if( argbounds[0].sup <= 0.0 )
2287 *result = SCIPexprcurvMultiply(-1.0, argcurv[0]);
2288 else if( argbounds[0].inf >= 0.0 )
2289 *result = argcurv[0];
2290 else if( argcurv[0] == SCIP_EXPRCURV_LINEAR )
2291 *result = SCIP_EXPRCURV_CONVEX;
2292 else
2293 *result = SCIP_EXPRCURV_UNKNOWN;
2294
2295 return SCIP_OKAY;
2296 }
2297
2298 /** point evaluation for EXPR_SIGN */
2299 static
SCIP_DECL_EXPREVAL(exprevalSign)2300 SCIP_DECL_EXPREVAL( exprevalSign )
2301 { /*lint --e{715}*/
2302 assert(result != NULL);
2303 assert(argvals != NULL);
2304
2305 *result = SIGN(argvals[0]);
2306
2307 return SCIP_OKAY;
2308 }
2309
2310 /** interval evaluation for EXPR_SIGN */
2311 static
SCIP_DECL_EXPRINTEVAL(exprevalIntSign)2312 SCIP_DECL_EXPRINTEVAL( exprevalIntSign )
2313 { /*lint --e{715}*/
2314 assert(result != NULL);
2315 assert(argvals != NULL);
2316
2317 SCIPintervalSign(infinity, result, argvals[0]);
2318
2319 return SCIP_OKAY;
2320 }
2321
2322 /** curvature for EXPR_SIGN */
2323 static
SCIP_DECL_EXPRCURV(exprcurvSign)2324 SCIP_DECL_EXPRCURV( exprcurvSign )
2325 { /*lint --e{715}*/
2326 assert(result != NULL);
2327 assert(argbounds != NULL);
2328
2329 /* if sign of child is clear, then sign is linear otherwise, we don't know */
2330 if( argbounds[0].sup <= 0.0 || argbounds[0].inf >= 0.0 )
2331 *result = SCIP_EXPRCURV_LINEAR;
2332 else
2333 *result = SCIP_EXPRCURV_UNKNOWN;
2334
2335 return SCIP_OKAY;
2336 }
2337
2338 /** point evaluation for EXPR_SUM */
2339 static
SCIP_DECL_EXPREVAL(exprevalSum)2340 SCIP_DECL_EXPREVAL( exprevalSum )
2341 { /*lint --e{715}*/
2342 int i;
2343
2344 assert(result != NULL);
2345 assert(argvals != NULL);
2346
2347 *result = 0.0;
2348 for( i = 0; i < nargs; ++i )
2349 *result += argvals[i];
2350
2351 return SCIP_OKAY;
2352 }
2353
2354 /** interval evaluation for EXPR_SUM */
2355 static
SCIP_DECL_EXPRINTEVAL(exprevalIntSum)2356 SCIP_DECL_EXPRINTEVAL( exprevalIntSum )
2357 { /*lint --e{715}*/
2358 int i;
2359
2360 assert(result != NULL);
2361 assert(argvals != NULL);
2362
2363 SCIPintervalSet(result, 0.0);
2364
2365 for( i = 0; i < nargs; ++i )
2366 SCIPintervalAdd(infinity, result, *result, argvals[i]);
2367
2368 return SCIP_OKAY;
2369 }
2370
2371 /** curvature for EXPR_SUM */
2372 static
SCIP_DECL_EXPRCURV(exprcurvSum)2373 SCIP_DECL_EXPRCURV( exprcurvSum )
2374 { /*lint --e{715}*/
2375 int i;
2376
2377 assert(result != NULL);
2378 assert(argcurv != NULL);
2379
2380 *result = SCIP_EXPRCURV_LINEAR;
2381
2382 for( i = 0; i < nargs; ++i )
2383 *result = SCIPexprcurvAdd(*result, argcurv[i]);
2384
2385 return SCIP_OKAY;
2386 }
2387
2388 /** point evaluation for EXPR_PRODUCT */
2389 static
SCIP_DECL_EXPREVAL(exprevalProduct)2390 SCIP_DECL_EXPREVAL( exprevalProduct )
2391 { /*lint --e{715}*/
2392 int i;
2393
2394 assert(result != NULL);
2395 assert(argvals != NULL);
2396
2397 *result = 1.0;
2398 for( i = 0; i < nargs; ++i )
2399 *result *= argvals[i];
2400
2401 return SCIP_OKAY;
2402 }
2403
2404 /** interval evaluation for EXPR_PRODUCT */
2405 static
SCIP_DECL_EXPRINTEVAL(exprevalIntProduct)2406 SCIP_DECL_EXPRINTEVAL( exprevalIntProduct )
2407 { /*lint --e{715}*/
2408 int i;
2409
2410 assert(result != NULL);
2411 assert(argvals != NULL);
2412
2413 SCIPintervalSet(result, 1.0);
2414
2415 for( i = 0; i < nargs; ++i )
2416 SCIPintervalMul(infinity, result, *result, argvals[i]);
2417
2418 return SCIP_OKAY;
2419 }
2420
2421 /** curvature for EXPR_PRODUCT */
2422 static
SCIP_DECL_EXPRCURV(exprcurvProduct)2423 SCIP_DECL_EXPRCURV( exprcurvProduct )
2424 { /*lint --e{715}*/
2425 SCIP_Bool hadnonconst;
2426 SCIP_Real constants;
2427 int i;
2428
2429 assert(result != NULL);
2430 assert(argcurv != NULL);
2431 assert(argbounds != NULL);
2432
2433 /* if all factors are constant, then product is linear (even constant)
2434 * if only one factor is not constant, then product is curvature of this factor, multiplied by sign of product of remaining factors
2435 */
2436 *result = SCIP_EXPRCURV_LINEAR;
2437 hadnonconst = FALSE;
2438 constants = 1.0;
2439
2440 for( i = 0; i < nargs; ++i )
2441 {
2442 if( argbounds[i].inf == argbounds[i].sup ) /*lint !e777*/
2443 {
2444 constants *= argbounds[i].inf;
2445 }
2446 else if( !hadnonconst )
2447 {
2448 /* first non-constant child */
2449 *result = argcurv[i];
2450 hadnonconst = TRUE;
2451 }
2452 else
2453 {
2454 /* more than one non-constant child, thus don't know curvature */
2455 *result = SCIP_EXPRCURV_UNKNOWN;
2456 break;
2457 }
2458 }
2459
2460 *result = SCIPexprcurvMultiply(constants, *result);
2461
2462 return SCIP_OKAY;
2463 }
2464
2465 /** point evaluation for EXPR_LINEAR */
2466 static
SCIP_DECL_EXPREVAL(exprevalLinear)2467 SCIP_DECL_EXPREVAL( exprevalLinear )
2468 { /*lint --e{715}*/
2469 SCIP_Real* coef;
2470 int i;
2471
2472 assert(result != NULL);
2473 assert(argvals != NULL || nargs == 0);
2474 assert(opdata.data != NULL);
2475
2476 coef = &((SCIP_Real*)opdata.data)[nargs];
2477
2478 *result = *coef;
2479 for( i = nargs-1, --coef; i >= 0; --i, --coef )
2480 *result += *coef * argvals[i]; /*lint !e613*/
2481
2482 assert(++coef == (SCIP_Real*)opdata.data);
2483
2484 return SCIP_OKAY;
2485 }
2486
2487 /** interval evaluation for EXPR_LINEAR */
2488 static
SCIP_DECL_EXPRINTEVAL(exprevalIntLinear)2489 SCIP_DECL_EXPRINTEVAL( exprevalIntLinear )
2490 { /*lint --e{715}*/
2491 assert(result != NULL);
2492 assert(argvals != NULL || nargs == 0);
2493 assert(opdata.data != NULL);
2494
2495 SCIPintervalScalprodScalars(infinity, result, nargs, argvals, (SCIP_Real*)opdata.data);
2496 SCIPintervalAddScalar(infinity, result, *result, ((SCIP_Real*)opdata.data)[nargs]);
2497
2498 return SCIP_OKAY;
2499 }
2500
2501 /** curvature for EXPR_LINEAR */
2502 static
SCIP_DECL_EXPRCURV(exprcurvLinear)2503 SCIP_DECL_EXPRCURV( exprcurvLinear )
2504 { /*lint --e{715}*/
2505 SCIP_Real* data;
2506 int i;
2507
2508 assert(result != NULL);
2509 assert(argcurv != NULL);
2510
2511 data = (SCIP_Real*)opdata.data;
2512 assert(data != NULL);
2513
2514 *result = SCIP_EXPRCURV_LINEAR;
2515
2516 for( i = 0; i < nargs; ++i )
2517 *result = SCIPexprcurvAdd(*result, SCIPexprcurvMultiply(data[i], argcurv[i]));
2518
2519 return SCIP_OKAY;
2520 }
2521
2522 /** expression data copy for EXPR_LINEAR */
2523 static
SCIP_DECL_EXPRCOPYDATA(exprCopyDataLinear)2524 SCIP_DECL_EXPRCOPYDATA( exprCopyDataLinear )
2525 { /*lint --e{715}*/
2526 SCIP_Real* targetdata;
2527
2528 assert(blkmem != NULL);
2529 assert(nchildren >= 0);
2530 assert(opdatatarget != NULL);
2531
2532 /* for a linear expression, we need to copy the array that holds the coefficients and constant term */
2533 assert(opdatasource.data != NULL);
2534 SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &targetdata, (SCIP_Real*)opdatasource.data, nchildren + 1) ); /*lint !e866*/
2535 opdatatarget->data = targetdata;
2536
2537 return SCIP_OKAY;
2538 }
2539
2540 /** expression data free for EXPR_LINEAR */
2541 static
SCIP_DECL_EXPRFREEDATA(exprFreeDataLinear)2542 SCIP_DECL_EXPRFREEDATA( exprFreeDataLinear )
2543 { /*lint --e{715}*/
2544 SCIP_Real* freedata;
2545
2546 assert(blkmem != NULL);
2547 assert(nchildren >= 0);
2548
2549 freedata = (SCIP_Real*)opdata.data;
2550 assert(freedata != NULL);
2551
2552 BMSfreeBlockMemoryArray(blkmem, &freedata, nchildren + 1); /*lint !e866*/
2553 }
2554
2555 /** point evaluation for EXPR_QUADRATIC */
2556 static
SCIP_DECL_EXPREVAL(exprevalQuadratic)2557 SCIP_DECL_EXPREVAL( exprevalQuadratic )
2558 { /*lint --e{715}*/
2559 SCIP_EXPRDATA_QUADRATIC* quaddata;
2560 SCIP_Real* lincoefs;
2561 SCIP_QUADELEM* quadelems;
2562 int nquadelems;
2563 int i;
2564
2565 assert(result != NULL);
2566 assert(argvals != NULL || nargs == 0);
2567
2568 quaddata = (SCIP_EXPRDATA_QUADRATIC*)opdata.data;
2569 assert(quaddata != NULL);
2570
2571 lincoefs = quaddata->lincoefs;
2572 nquadelems = quaddata->nquadelems;
2573 quadelems = quaddata->quadelems;
2574
2575 assert(quadelems != NULL || nquadelems == 0);
2576 assert(argvals != NULL || nquadelems == 0);
2577
2578 *result = quaddata->constant;
2579
2580 if( lincoefs != NULL )
2581 {
2582 for( i = nargs-1; i >= 0; --i )
2583 *result += lincoefs[i] * argvals[i]; /*lint !e613*/
2584 }
2585
2586 for( i = 0; i < nquadelems; ++i, ++quadelems ) /*lint !e613*/
2587 *result += quadelems->coef * argvals[quadelems->idx1] * argvals[quadelems->idx2]; /*lint !e613*/
2588
2589 return SCIP_OKAY;
2590 }
2591
2592 /** interval evaluation for EXPR_QUADRATIC */
2593 static
SCIP_DECL_EXPRINTEVAL(exprevalIntQuadratic)2594 SCIP_DECL_EXPRINTEVAL( exprevalIntQuadratic )
2595 { /*lint --e{715}*/
2596 SCIP_EXPRDATA_QUADRATIC* quaddata;
2597 SCIP_Real* lincoefs;
2598 SCIP_QUADELEM* quadelems;
2599 int nquadelems;
2600 int i;
2601 int argidx;
2602 SCIP_Real sqrcoef;
2603 SCIP_INTERVAL lincoef;
2604 SCIP_INTERVAL tmp;
2605
2606 assert(result != NULL);
2607 assert(argvals != NULL || nargs == 0);
2608
2609 quaddata = (SCIP_EXPRDATA_QUADRATIC*)opdata.data;
2610 assert(quaddata != NULL);
2611
2612 lincoefs = quaddata->lincoefs;
2613 nquadelems = quaddata->nquadelems;
2614 quadelems = quaddata->quadelems;
2615
2616 assert(quadelems != NULL || nquadelems == 0);
2617 assert(argvals != NULL || nargs == 0);
2618
2619 /* something fast for case of only one child */
2620 if( nargs == 1 )
2621 {
2622 SCIPintervalSet(&lincoef, lincoefs != NULL ? lincoefs[0] : 0.0);
2623
2624 sqrcoef = 0.0;
2625 for( i = 0; i < nquadelems; ++i )
2626 {
2627 assert(quadelems[i].idx1 == 0); /*lint !e613*/
2628 assert(quadelems[i].idx2 == 0); /*lint !e613*/
2629 sqrcoef += quadelems[i].coef; /*lint !e613*/
2630 }
2631
2632 SCIPintervalQuad(infinity, result, sqrcoef, lincoef, argvals[0]); /*lint !e613*/
2633 SCIPintervalAddScalar(infinity, result, *result, quaddata->constant);
2634
2635 return SCIP_OKAY;
2636 }
2637
2638 if( nargs == 2 && nquadelems > 0 )
2639 {
2640 /* if it's a bivariate quadratic expression with bilinear term, do something special */
2641 SCIP_Real ax; /* square coefficient of first child */
2642 SCIP_Real ay; /* square coefficient of second child */
2643 SCIP_Real axy; /* bilinear coefficient */
2644
2645 ax = 0.0;
2646 ay = 0.0;
2647 axy = 0.0;
2648 for( i = 0; i < nquadelems; ++i )
2649 if( quadelems[i].idx1 == 0 && quadelems[i].idx2 == 0 ) /*lint !e613*/
2650 ax += quadelems[i].coef; /*lint !e613*/
2651 else if( quadelems[i].idx1 == 1 && quadelems[i].idx2 == 1 ) /*lint !e613*/
2652 ay += quadelems[i].coef; /*lint !e613*/
2653 else
2654 axy += quadelems[i].coef; /*lint !e613*/
2655
2656 SCIPintervalQuadBivar(infinity, result, ax, ay, axy,
2657 lincoefs != NULL ? lincoefs[0] : 0.0, lincoefs != NULL ? lincoefs[1] : 0.0,
2658 argvals[0], argvals[1]); /*lint !e613*/
2659 SCIPdebugMessage("%g x^2 + %g y^2 + %g x y + %g x + %g y = [%g,%g] for x = [%g,%g], y = [%g,%g]\n",
2660 ax, ay, axy, lincoefs != NULL ? lincoefs[0] : 0.0, lincoefs != NULL ? lincoefs[1] : 0.0,
2661 result->inf, result->sup, argvals[0].inf, argvals[0].sup, argvals[1].inf, argvals[1].sup); /*lint !e613*/
2662
2663 SCIPintervalAddScalar(infinity, result, *result, quaddata->constant);
2664
2665 return SCIP_OKAY;
2666 }
2667
2668 /* make sure coefficients are sorted */
2669 quadraticdataSort(quaddata);
2670
2671 SCIPintervalSet(result, quaddata->constant);
2672
2673 /* for each argument, we collect it's linear index from lincoefs, it's square coefficients and all factors from bilinear terms
2674 * then we compute the interval sqrcoef*x^2 + lincoef*x and add it to result
2675 * @todo split quadratic expression into bivariate quadratic terms and apply the above method
2676 */
2677 i = 0;
2678 for( argidx = 0; argidx < nargs; ++argidx )
2679 {
2680 if( i == nquadelems || quadelems[i].idx1 > argidx ) /*lint !e613*/
2681 {
2682 /* there are no quadratic terms with argidx in its first argument, that should be easy to handle */
2683 if( lincoefs != NULL )
2684 {
2685 SCIPintervalMulScalar(infinity, &tmp, argvals[argidx], lincoefs[argidx]); /*lint !e613*/
2686 SCIPintervalAdd(infinity, result, *result, tmp);
2687 }
2688 continue;
2689 }
2690
2691 sqrcoef = 0.0;
2692 SCIPintervalSet(&lincoef, lincoefs != NULL ? lincoefs[argidx] : 0.0);
2693
2694 assert(i < nquadelems && quadelems[i].idx1 == argidx); /*lint !e613*/
2695 do
2696 {
2697 if( quadelems[i].idx2 == argidx ) /*lint !e613*/
2698 {
2699 sqrcoef += quadelems[i].coef; /*lint !e613*/
2700 }
2701 else
2702 {
2703 SCIPintervalMulScalar(infinity, &tmp, argvals[quadelems[i].idx2], quadelems[i].coef); /*lint !e613*/
2704 SCIPintervalAdd(infinity, &lincoef, lincoef, tmp);
2705 }
2706 ++i;
2707 }
2708 while( i < nquadelems && quadelems[i].idx1 == argidx ); /*lint !e613*/
2709 assert(i == nquadelems || quadelems[i].idx1 > argidx); /*lint !e613*/
2710
2711 SCIPintervalQuad(infinity, &tmp, sqrcoef, lincoef, argvals[argidx]); /*lint !e613*/
2712 SCIPintervalAdd(infinity, result, *result, tmp);
2713 }
2714 assert(i == nquadelems);
2715
2716 return SCIP_OKAY;
2717 }
2718
2719 /** curvature for EXPR_QUADRATIC */
2720 static
SCIP_DECL_EXPRCURV(exprcurvQuadratic)2721 SCIP_DECL_EXPRCURV( exprcurvQuadratic )
2722 { /*lint --e{715}*/
2723 SCIP_EXPRDATA_QUADRATIC* data;
2724 SCIP_QUADELEM* quadelems;
2725 int nquadelems;
2726 SCIP_Real* lincoefs;
2727 int i;
2728
2729 assert(result != NULL);
2730 assert(argcurv != NULL);
2731 assert(argbounds != NULL);
2732
2733 data = (SCIP_EXPRDATA_QUADRATIC*)opdata.data;
2734 assert(data != NULL);
2735
2736 lincoefs = data->lincoefs;
2737 quadelems = data->quadelems;
2738 nquadelems = data->nquadelems;
2739
2740 *result = SCIP_EXPRCURV_LINEAR;
2741
2742 if( lincoefs != NULL )
2743 for( i = 0; i < nargs; ++i )
2744 *result = SCIPexprcurvAdd(*result, SCIPexprcurvMultiply(lincoefs[i], argcurv[i]));
2745
2746 /* @todo could try cholesky factorization if all children linear...
2747 * @todo should then cache the result
2748 */
2749 for( i = 0; i < nquadelems && *result != SCIP_EXPRCURV_UNKNOWN; ++i )
2750 {
2751 if( quadelems[i].coef == 0.0 )
2752 continue;
2753
2754 if( argbounds[quadelems[i].idx1].inf == argbounds[quadelems[i].idx1].sup && /*lint !e777*/
2755 +argbounds[quadelems[i].idx2].inf == argbounds[quadelems[i].idx2].sup
2756 ) /*lint !e777*/
2757 {
2758 /* both factors are constants -> curvature does not change */
2759 continue;
2760 }
2761
2762 if( argbounds[quadelems[i].idx1].inf == argbounds[quadelems[i].idx1].sup ) /*lint !e777*/
2763 {
2764 /* first factor is constant, second is not -> add curvature of second */
2765 *result = SCIPexprcurvAdd(*result, SCIPexprcurvMultiply(quadelems[i].coef * argbounds[quadelems[i].idx1].inf, argcurv[quadelems[i].idx2]));
2766 }
2767 else if( argbounds[quadelems[i].idx2].inf == argbounds[quadelems[i].idx2].sup ) /*lint !e777*/
2768 {
2769 /* first factor is not constant, second is -> add curvature of first */
2770 *result = SCIPexprcurvAdd(*result, SCIPexprcurvMultiply(quadelems[i].coef * argbounds[quadelems[i].idx2].inf, argcurv[quadelems[i].idx1]));
2771 }
2772 else if( quadelems[i].idx1 == quadelems[i].idx2 )
2773 {
2774 /* both factors not constant, but the same (square term) */
2775 *result = SCIPexprcurvAdd(*result, SCIPexprcurvMultiply(quadelems[i].coef, SCIPexprcurvPower(argbounds[quadelems[i].idx1], argcurv[quadelems[i].idx1], 2.0)));
2776 }
2777 else
2778 {
2779 /* two different non-constant factors -> can't tell about curvature */
2780 *result = SCIP_EXPRCURV_UNKNOWN;
2781 }
2782 }
2783
2784 return SCIP_OKAY;
2785 }
2786
2787 /** expression data copy for EXPR_QUADRATIC */
2788 static
SCIP_DECL_EXPRCOPYDATA(exprCopyDataQuadratic)2789 SCIP_DECL_EXPRCOPYDATA( exprCopyDataQuadratic )
2790 { /*lint --e{715}*/
2791 SCIP_EXPRDATA_QUADRATIC* sourcedata;
2792
2793 assert(blkmem != NULL);
2794 assert(opdatatarget != NULL);
2795
2796 sourcedata = (SCIP_EXPRDATA_QUADRATIC*)opdatasource.data;
2797 assert(sourcedata != NULL);
2798
2799 SCIP_CALL( quadraticdataCreate(blkmem, (SCIP_EXPRDATA_QUADRATIC**)&opdatatarget->data,
2800 sourcedata->constant, nchildren, sourcedata->lincoefs, sourcedata->nquadelems, sourcedata->quadelems) );
2801
2802 return SCIP_OKAY;
2803 }
2804
2805 /** expression data free for EXPR_QUADRATIC */
2806 static
SCIP_DECL_EXPRFREEDATA(exprFreeDataQuadratic)2807 SCIP_DECL_EXPRFREEDATA( exprFreeDataQuadratic )
2808 { /*lint --e{715}*/
2809 SCIP_EXPRDATA_QUADRATIC* quadraticdata;
2810
2811 assert(blkmem != NULL);
2812 assert(nchildren >= 0);
2813
2814 quadraticdata = (SCIP_EXPRDATA_QUADRATIC*)opdata.data;
2815 assert(quadraticdata != NULL);
2816
2817 if( quadraticdata->lincoefs != NULL )
2818 {
2819 BMSfreeBlockMemoryArray(blkmem, &quadraticdata->lincoefs, nchildren);
2820 }
2821
2822 if( quadraticdata->nquadelems > 0 )
2823 {
2824 assert(quadraticdata->quadelems != NULL);
2825 BMSfreeBlockMemoryArray(blkmem, &quadraticdata->quadelems, quadraticdata->nquadelems);
2826 }
2827
2828 BMSfreeBlockMemory(blkmem, &quadraticdata);
2829 }
2830
2831 /** point evaluation for EXPR_POLYNOMIAL */
2832 static
SCIP_DECL_EXPREVAL(exprevalPolynomial)2833 SCIP_DECL_EXPREVAL( exprevalPolynomial )
2834 { /*lint --e{715}*/
2835 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata;
2836 SCIP_EXPRDATA_MONOMIAL* monomialdata;
2837 SCIP_Real childval;
2838 SCIP_Real exponent;
2839 SCIP_Real monomialval;
2840 int i;
2841 int j;
2842
2843 assert(result != NULL);
2844 assert(argvals != NULL || nargs == 0);
2845 assert(opdata.data != NULL);
2846
2847 polynomialdata = (SCIP_EXPRDATA_POLYNOMIAL*)opdata.data;
2848 assert(polynomialdata != NULL);
2849
2850 *result = polynomialdata->constant;
2851
2852 for( i = 0; i < polynomialdata->nmonomials; ++i )
2853 {
2854 monomialdata = polynomialdata->monomials[i];
2855 assert(monomialdata != NULL);
2856
2857 monomialval = monomialdata->coef;
2858 for( j = 0; j < monomialdata->nfactors; ++j )
2859 {
2860 assert(monomialdata->childidxs[j] >= 0);
2861 assert(monomialdata->childidxs[j] < nargs);
2862
2863 childval = argvals[monomialdata->childidxs[j]]; /*lint !e613*/
2864 if( childval == 1.0 ) /* 1^anything == 1 */
2865 continue;
2866
2867 exponent = monomialdata->exponents[j];
2868
2869 if( childval == 0.0 )
2870 {
2871 if( exponent > 0.0 )
2872 {
2873 /* 0^positive == 0 */
2874 monomialval = 0.0;
2875 break;
2876 }
2877 else if( exponent < 0.0 )
2878 {
2879 /* 0^negative = nan (or should it be +inf?, doesn't really matter) */
2880 #ifdef NAN
2881 *result = NAN;
2882 #else
2883 /* cppcheck-suppress wrongmathcall */
2884 *result = pow(0.0, -1.0);
2885 #endif
2886 return SCIP_OKAY;
2887 }
2888 /* 0^0 == 1 */
2889 continue;
2890 }
2891
2892 /* cover some special exponents separately to avoid calling expensive pow function */
2893 if( exponent == 0.0 )
2894 continue;
2895 if( exponent == 1.0 )
2896 {
2897 monomialval *= childval;
2898 continue;
2899 }
2900 if( exponent == 2.0 )
2901 {
2902 monomialval *= childval * childval;
2903 continue;
2904 }
2905 if( exponent == 0.5 )
2906 {
2907 monomialval *= sqrt(childval);
2908 continue;
2909 }
2910 if( exponent == -1.0 )
2911 {
2912 monomialval /= childval;
2913 continue;
2914 }
2915 if( exponent == -2.0 )
2916 {
2917 monomialval /= childval * childval;
2918 continue;
2919 }
2920 monomialval *= pow(childval, exponent);
2921 }
2922
2923 *result += monomialval;
2924 }
2925
2926 return SCIP_OKAY;
2927 }
2928
2929 /** interval evaluation for EXPR_POLYNOMIAL */
2930 static
SCIP_DECL_EXPRINTEVAL(exprevalIntPolynomial)2931 SCIP_DECL_EXPRINTEVAL( exprevalIntPolynomial )
2932 { /*lint --e{715}*/
2933 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata;
2934 SCIP_EXPRDATA_MONOMIAL* monomialdata;
2935 SCIP_INTERVAL childval;
2936 SCIP_INTERVAL monomialval;
2937 SCIP_Real exponent;
2938 int i;
2939 int j;
2940
2941 assert(result != NULL);
2942 assert(argvals != NULL || nargs == 0);
2943 assert(opdata.data != NULL);
2944
2945 polynomialdata = (SCIP_EXPRDATA_POLYNOMIAL*)opdata.data;
2946 assert(polynomialdata != NULL);
2947
2948 SCIPintervalSet(result, polynomialdata->constant);
2949
2950 for( i = 0; i < polynomialdata->nmonomials; ++i )
2951 {
2952 monomialdata = polynomialdata->monomials[i];
2953 assert(monomialdata != NULL);
2954
2955 SCIPintervalSet(&monomialval, monomialdata->coef);
2956 for( j = 0; j < monomialdata->nfactors && !SCIPintervalIsEntire(infinity, monomialval); ++j )
2957 {
2958 assert(monomialdata->childidxs[j] >= 0);
2959 assert(monomialdata->childidxs[j] < nargs);
2960
2961 childval = argvals[monomialdata->childidxs[j]]; /*lint !e613*/
2962
2963 exponent = monomialdata->exponents[j];
2964
2965 /* cover some special exponents separately to avoid calling expensive pow function */
2966 if( exponent == 0.0 )
2967 continue;
2968
2969 if( exponent == 1.0 )
2970 {
2971 SCIPintervalMul(infinity, &monomialval, monomialval, childval);
2972 continue;
2973 }
2974
2975 if( exponent == 2.0 )
2976 {
2977 SCIPintervalSquare(infinity, &childval, childval);
2978 SCIPintervalMul(infinity, &monomialval, monomialval, childval);
2979 continue;
2980 }
2981
2982 if( exponent == 0.5 )
2983 {
2984 SCIPintervalSquareRoot(infinity, &childval, childval);
2985 if( SCIPintervalIsEmpty(infinity, childval) )
2986 {
2987 SCIPintervalSetEmpty(result);
2988 return SCIP_OKAY;
2989 }
2990 SCIPintervalMul(infinity, &monomialval, monomialval, childval);
2991 continue;
2992 }
2993 else if( exponent == -1.0 )
2994 {
2995 SCIPintervalDiv(infinity, &monomialval, monomialval, childval);
2996 }
2997 else if( exponent == -2.0 )
2998 {
2999 SCIPintervalSquare(infinity, &childval, childval);
3000 SCIPintervalDiv(infinity, &monomialval, monomialval, childval);
3001 }
3002 else
3003 {
3004 SCIPintervalPowerScalar(infinity, &childval, childval, exponent);
3005 if( SCIPintervalIsEmpty(infinity, childval) )
3006 {
3007 SCIPintervalSetEmpty(result);
3008 return SCIP_OKAY;
3009 }
3010 SCIPintervalMul(infinity, &monomialval, monomialval, childval);
3011 }
3012
3013 /* the cases in which monomialval gets empty should have been catched */
3014 assert(!SCIPintervalIsEmpty(infinity, monomialval));
3015 }
3016
3017 SCIPintervalAdd(infinity, result, *result, monomialval);
3018 }
3019
3020 return SCIP_OKAY;
3021 }
3022
3023 /** curvature for EXPR_POLYNOMIAL */
3024 static
SCIP_DECL_EXPRCURV(exprcurvPolynomial)3025 SCIP_DECL_EXPRCURV( exprcurvPolynomial )
3026 { /*lint --e{715}*/
3027 SCIP_EXPRDATA_POLYNOMIAL* data;
3028 SCIP_EXPRDATA_MONOMIAL** monomials;
3029 SCIP_EXPRDATA_MONOMIAL* monomial;
3030 int nmonomials;
3031 int i;
3032
3033 assert(result != NULL);
3034 assert(argcurv != NULL);
3035 assert(argbounds != NULL);
3036
3037 data = (SCIP_EXPRDATA_POLYNOMIAL*)opdata.data;
3038 assert(data != NULL);
3039
3040 monomials = data->monomials;
3041 nmonomials = data->nmonomials;
3042
3043 *result = SCIP_EXPRCURV_LINEAR;
3044
3045 for( i = 0; i < nmonomials && *result != SCIP_EXPRCURV_UNKNOWN; ++i )
3046 {
3047 /* we assume that some simplifier was running, so that monomials do not have constants in their factors and such that all factors are different
3048 * (result would still be correct)
3049 */
3050 monomial = monomials[i];
3051 *result = SCIPexprcurvAdd(*result, SCIPexprcurvMultiply(monomial->coef, SCIPexprcurvMonomial(monomial->nfactors, monomial->exponents, monomial->childidxs, argcurv, argbounds)));
3052 }
3053
3054 return SCIP_OKAY;
3055 }
3056
3057 /** expression data copy for EXPR_POLYNOMIAL */
3058 static
SCIP_DECL_EXPRCOPYDATA(exprCopyDataPolynomial)3059 SCIP_DECL_EXPRCOPYDATA( exprCopyDataPolynomial )
3060 { /*lint --e{715}*/
3061 SCIP_EXPRDATA_POLYNOMIAL* sourcepolynomialdata;
3062 SCIP_EXPRDATA_POLYNOMIAL* targetpolynomialdata;
3063
3064 assert(blkmem != NULL);
3065 assert(opdatatarget != NULL);
3066
3067 sourcepolynomialdata = (SCIP_EXPRDATA_POLYNOMIAL*)opdatasource.data;
3068 assert(sourcepolynomialdata != NULL);
3069
3070 SCIP_CALL( polynomialdataCopy(blkmem, &targetpolynomialdata, sourcepolynomialdata) );
3071
3072 opdatatarget->data = (void*)targetpolynomialdata;
3073
3074 return SCIP_OKAY;
3075 }
3076
3077 /** expression data free for EXPR_POLYNOMIAL */
3078 static
SCIP_DECL_EXPRFREEDATA(exprFreeDataPolynomial)3079 SCIP_DECL_EXPRFREEDATA( exprFreeDataPolynomial )
3080 { /*lint --e{715}*/
3081 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata;
3082
3083 assert(blkmem != NULL);
3084
3085 polynomialdata = (SCIP_EXPRDATA_POLYNOMIAL*)opdata.data;
3086 assert(polynomialdata != NULL);
3087
3088 polynomialdataFree(blkmem, &polynomialdata);
3089 }
3090
3091 /** point evaluation for user expression */
3092 static
SCIP_DECL_EXPREVAL(exprevalUser)3093 SCIP_DECL_EXPREVAL( exprevalUser )
3094 { /*lint --e{715}*/
3095 SCIP_EXPRDATA_USER* exprdata;
3096
3097 exprdata = (SCIP_EXPRDATA_USER*) opdata.data;
3098
3099 SCIP_CALL( exprdata->eval(exprdata->userdata, nargs, argvals, result, NULL, NULL) );
3100
3101 return SCIP_OKAY;
3102 }
3103
3104 /** interval evaluation for user expression */
3105 static
SCIP_DECL_EXPRINTEVAL(exprevalIntUser)3106 SCIP_DECL_EXPRINTEVAL( exprevalIntUser )
3107 { /*lint --e{715}*/
3108 SCIP_EXPRDATA_USER* exprdata;
3109
3110 exprdata = (SCIP_EXPRDATA_USER*) opdata.data;
3111
3112 if( exprdata->inteval != NULL )
3113 {
3114 SCIP_CALL( exprdata->inteval(infinity, exprdata->userdata, nargs, argvals, result, NULL, NULL) );
3115 }
3116 else
3117 {
3118 /* if user does not provide interval evaluation, then return a result that is always correct */
3119 SCIPintervalSetEntire(infinity, result);
3120 }
3121
3122 return SCIP_OKAY;
3123 }
3124
3125 /** curvature check for user expression */
3126 static
SCIP_DECL_EXPRCURV(exprcurvUser)3127 SCIP_DECL_EXPRCURV( exprcurvUser )
3128 {
3129 SCIP_EXPRDATA_USER* exprdata;
3130
3131 exprdata = (SCIP_EXPRDATA_USER*) opdata.data;
3132
3133 if( exprdata->curv != NULL )
3134 {
3135 SCIP_CALL( exprdata->curv(infinity, exprdata->userdata, nargs, argbounds, argcurv, result) );
3136 }
3137 else
3138 {
3139 /* if user does not provide curvature check, then return unknown (which is handled like indefinite) */
3140 *result = SCIP_EXPRCURV_UNKNOWN;
3141 }
3142
3143 return SCIP_OKAY;
3144 }
3145
3146 /** data copy for user expression */
3147 static
SCIP_DECL_EXPRCOPYDATA(exprCopyDataUser)3148 SCIP_DECL_EXPRCOPYDATA( exprCopyDataUser )
3149 {
3150 SCIP_EXPRDATA_USER* exprdatasource;
3151 SCIP_EXPRDATA_USER* exprdatatarget;
3152
3153 assert(blkmem != NULL);
3154 assert(opdatatarget != NULL);
3155
3156 exprdatasource = (SCIP_EXPRDATA_USER*)opdatasource.data;
3157 assert(exprdatasource != NULL);
3158
3159 /* duplicate expression data */
3160 SCIP_ALLOC( BMSduplicateBlockMemory(blkmem, &exprdatatarget, exprdatasource) );
3161
3162 /* duplicate user expression data, if any */
3163 if( exprdatasource->copydata != NULL )
3164 {
3165 SCIP_CALL( exprdatasource->copydata(blkmem, nchildren, exprdatasource->userdata, &exprdatatarget->userdata) );
3166 }
3167 else
3168 {
3169 /* if no copy function for data, then there has to be no data */
3170 assert(exprdatatarget->userdata == NULL);
3171 }
3172
3173 opdatatarget->data = (void*)exprdatatarget;
3174
3175 return SCIP_OKAY;
3176 }
3177
3178 /** data free for user expression */
3179 static
SCIP_DECL_EXPRFREEDATA(exprFreeDataUser)3180 SCIP_DECL_EXPRFREEDATA( exprFreeDataUser )
3181 {
3182 SCIP_EXPRDATA_USER* exprdata;
3183
3184 assert(blkmem != NULL);
3185
3186 exprdata = (SCIP_EXPRDATA_USER*)opdata.data;
3187
3188 /* free user expression data, if any */
3189 if( exprdata->freedata != NULL )
3190 {
3191 exprdata->freedata(blkmem, nchildren, exprdata->userdata);
3192 }
3193 else
3194 {
3195 assert(exprdata->userdata == NULL);
3196 }
3197
3198 /* free expression data */
3199 BMSfreeBlockMemory(blkmem, &exprdata);
3200 }
3201
3202 /** element in table of expression operands */
3203 struct exprOpTableElement
3204 {
3205 const char* name; /**< name of operand (used for printing) */
3206 int nargs; /**< number of arguments (negative if not fixed) */
3207 SCIP_DECL_EXPREVAL ((*eval)); /**< evaluation function */
3208 SCIP_DECL_EXPRINTEVAL ((*inteval)); /**< interval evaluation function */
3209 SCIP_DECL_EXPRCURV ((*curv)); /**< curvature check function */
3210 SCIP_DECL_EXPRCOPYDATA ((*copydata)); /**< expression data copy function, or NULL to only opdata union */
3211 SCIP_DECL_EXPRFREEDATA ((*freedata)); /**< expression data free function, or NULL if nothing to free */
3212 };
3213
3214 #define EXPROPEMPTY {NULL, -1, NULL, NULL, NULL, NULL, NULL}
3215
3216 /** table containing for each operand the name, the number of children, and some evaluation functions */
3217 static
3218 struct exprOpTableElement exprOpTable[] =
3219 {
3220 EXPROPEMPTY,
3221 { "variable", 0, exprevalVar, exprevalIntVar, exprcurvVar, NULL, NULL },
3222 { "constant", 0, exprevalConst, exprevalIntConst, exprcurvConst, NULL, NULL },
3223 { "parameter", 0, exprevalParam, exprevalIntParam, exprcurvParam, NULL, NULL },
3224 EXPROPEMPTY, EXPROPEMPTY, EXPROPEMPTY, EXPROPEMPTY,
3225 { "plus", 2, exprevalPlus, exprevalIntPlus, exprcurvPlus, NULL, NULL },
3226 { "minus", 2, exprevalMinus, exprevalIntMinus, exprcurvMinus, NULL, NULL },
3227 { "mul", 2, exprevalMult, exprevalIntMult, exprcurvMult, NULL, NULL },
3228 { "div", 2, exprevalDiv, exprevalIntDiv, exprcurvDiv, NULL, NULL },
3229 { "sqr", 1, exprevalSquare, exprevalIntSquare, exprcurvSquare, NULL, NULL },
3230 { "sqrt", 1, exprevalSquareRoot, exprevalIntSquareRoot, exprcurvSquareRoot, NULL, NULL },
3231 { "realpower", 1, exprevalRealPower, exprevalIntRealPower, exprcurvRealPower, NULL, NULL },
3232 { "intpower", 1, exprevalIntPower, exprevalIntIntPower, exprcurvIntPower, NULL, NULL },
3233 { "signpower", 1, exprevalSignPower, exprevalIntSignPower, exprcurvSignPower, NULL, NULL },
3234 { "exp", 1, exprevalExp, exprevalIntExp, exprcurvExp, NULL, NULL },
3235 { "log", 1, exprevalLog, exprevalIntLog, exprcurvLog, NULL, NULL },
3236 { "sin", 1, exprevalSin, exprevalIntSin, exprcurvSin, NULL, NULL },
3237 { "cos", 1, exprevalCos, exprevalIntCos, exprcurvCos, NULL, NULL },
3238 { "tan", 1, exprevalTan, exprevalIntTan, exprcurvTan, NULL, NULL },
3239 /* { "erf", 1, exprevalErf, exprevalIntErf, exprcurvErf, NULL, NULL }, */
3240 /* { "erfi", 1, exprevalErfi, exprevalIntErfi exprcurvErfi, NULL, NULL }, */
3241 EXPROPEMPTY, EXPROPEMPTY,
3242 { "min", 2, exprevalMin, exprevalIntMin, exprcurvMin, NULL, NULL },
3243 { "max", 2, exprevalMax, exprevalIntMax, exprcurvMax, NULL, NULL },
3244 { "abs", 1, exprevalAbs, exprevalIntAbs, exprcurvAbs, NULL, NULL },
3245 { "sign", 1, exprevalSign, exprevalIntSign, exprcurvSign, NULL, NULL },
3246 EXPROPEMPTY, EXPROPEMPTY, EXPROPEMPTY,
3247 EXPROPEMPTY, EXPROPEMPTY, EXPROPEMPTY, EXPROPEMPTY, EXPROPEMPTY, EXPROPEMPTY, EXPROPEMPTY, EXPROPEMPTY, EXPROPEMPTY, EXPROPEMPTY,
3248 EXPROPEMPTY, EXPROPEMPTY, EXPROPEMPTY, EXPROPEMPTY, EXPROPEMPTY, EXPROPEMPTY, EXPROPEMPTY, EXPROPEMPTY, EXPROPEMPTY, EXPROPEMPTY,
3249 EXPROPEMPTY, EXPROPEMPTY, EXPROPEMPTY, EXPROPEMPTY, EXPROPEMPTY, EXPROPEMPTY, EXPROPEMPTY, EXPROPEMPTY, EXPROPEMPTY, EXPROPEMPTY,
3250 EXPROPEMPTY, EXPROPEMPTY, EXPROPEMPTY,
3251 { "sum", -2, exprevalSum, exprevalIntSum, exprcurvSum, NULL, NULL },
3252 { "prod", -2, exprevalProduct, exprevalIntProduct, exprcurvProduct, NULL, NULL },
3253 { "linear", -2, exprevalLinear, exprevalIntLinear, exprcurvLinear, exprCopyDataLinear, exprFreeDataLinear },
3254 { "quadratic", -2, exprevalQuadratic, exprevalIntQuadratic, exprcurvQuadratic, exprCopyDataQuadratic, exprFreeDataQuadratic },
3255 { "polynomial", -2, exprevalPolynomial, exprevalIntPolynomial, exprcurvPolynomial, exprCopyDataPolynomial, exprFreeDataPolynomial },
3256 { "user", -2, exprevalUser, exprevalIntUser, exprcurvUser, exprCopyDataUser, exprFreeDataUser }
3257 };
3258
3259 /**@} */
3260
3261 /**@name Expression operand methods */
3262 /**@{ */
3263
3264 /** gives the name of an operand as string */
SCIPexpropGetName(SCIP_EXPROP op)3265 const char* SCIPexpropGetName(
3266 SCIP_EXPROP op /**< expression operand */
3267 )
3268 {
3269 assert(op < SCIP_EXPR_LAST);
3270
3271 return exprOpTable[op].name;
3272 }
3273
3274 /** gives the number of children of a simple operand */
SCIPexpropGetNChildren(SCIP_EXPROP op)3275 int SCIPexpropGetNChildren(
3276 SCIP_EXPROP op /**< expression operand */
3277 )
3278 {
3279 assert(op < SCIP_EXPR_LAST);
3280
3281 return exprOpTable[op].nargs;
3282 }
3283
3284 /**@} */
3285
3286 /**@name Expressions private methods */
3287 /**@{ */
3288
3289 /** creates an expression
3290 *
3291 * Note, that the expression is allocated but for the children only the pointer is copied.
3292 */
3293 static
exprCreate(BMS_BLKMEM * blkmem,SCIP_EXPR ** expr,SCIP_EXPROP op,int nchildren,SCIP_EXPR ** children,SCIP_EXPROPDATA opdata)3294 SCIP_RETCODE exprCreate(
3295 BMS_BLKMEM* blkmem, /**< block memory data structure */
3296 SCIP_EXPR** expr, /**< pointer to buffer for expression address */
3297 SCIP_EXPROP op, /**< operand of expression */
3298 int nchildren, /**< number of children */
3299 SCIP_EXPR** children, /**< children */
3300 SCIP_EXPROPDATA opdata /**< operand data */
3301 )
3302 {
3303 assert(blkmem != NULL);
3304 assert(expr != NULL);
3305 assert(children != NULL || nchildren == 0);
3306 assert(children == NULL || nchildren > 0);
3307
3308 SCIP_ALLOC( BMSallocBlockMemory(blkmem, expr) );
3309
3310 (*expr)->op = op;
3311 (*expr)->nchildren = nchildren;
3312 (*expr)->children = children;
3313 (*expr)->data = opdata;
3314
3315 return SCIP_OKAY;
3316 }
3317
3318 /** tries to convert a given (operator,operatordata) pair into a polynomial operator with corresponding data
3319 *
3320 * Does not do this for constants.
3321 * If conversion is not possible or operator is already polynomial, *op and *data are
3322 * left untouched.
3323 */
3324 static
exprConvertToPolynomial(BMS_BLKMEM * blkmem,SCIP_EXPROP * op,SCIP_EXPROPDATA * data,int nchildren)3325 SCIP_RETCODE exprConvertToPolynomial(
3326 BMS_BLKMEM* blkmem, /**< block memory */
3327 SCIP_EXPROP* op, /**< pointer to expression operator */
3328 SCIP_EXPROPDATA* data, /**< pointer to expression data */
3329 int nchildren /**< number of children of operator */
3330 )
3331 {
3332 assert(blkmem != NULL);
3333 assert(op != NULL);
3334 assert(data != NULL);
3335
3336 switch( *op )
3337 {
3338 case SCIP_EXPR_VARIDX:
3339 case SCIP_EXPR_PARAM:
3340 case SCIP_EXPR_CONST:
3341 break;
3342
3343 case SCIP_EXPR_PLUS:
3344 {
3345 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata;
3346 SCIP_EXPRDATA_MONOMIAL* monomials[2];
3347 int childidx;
3348 SCIP_Real exponent;
3349
3350 assert(nchildren == 2);
3351
3352 /* create monomial for first child */
3353 childidx = 0;
3354 exponent = 1.0;
3355 SCIP_CALL( SCIPexprCreateMonomial(blkmem, &monomials[0], 1.0, 1, &childidx, &exponent) );
3356
3357 /* create monomial for second child */
3358 childidx = 1;
3359 exponent = 1.0;
3360 SCIP_CALL( SCIPexprCreateMonomial(blkmem, &monomials[1], 1.0, 1, &childidx, &exponent) );
3361
3362 /* create polynomial for sum of children */
3363 SCIP_CALL( polynomialdataCreate(blkmem, &polynomialdata, 2, monomials, 0.0, FALSE) );
3364
3365 *op = SCIP_EXPR_POLYNOMIAL;
3366 data->data = (void*)polynomialdata;
3367
3368 break;
3369 }
3370
3371 case SCIP_EXPR_MINUS:
3372 {
3373 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata;
3374 SCIP_EXPRDATA_MONOMIAL* monomials[2];
3375 int childidx;
3376 SCIP_Real exponent;
3377
3378 assert(nchildren == 2);
3379
3380 /* create monomial for first child */
3381 childidx = 0;
3382 exponent = 1.0;
3383 SCIP_CALL( SCIPexprCreateMonomial(blkmem, &monomials[0], 1.0, 1, &childidx, &exponent) );
3384
3385 /* create monomial for second child */
3386 childidx = 1;
3387 exponent = 1.0;
3388 SCIP_CALL( SCIPexprCreateMonomial(blkmem, &monomials[1], -1.0, 1, &childidx, &exponent) );
3389
3390 /* create polynomial for difference of children */
3391 SCIP_CALL( polynomialdataCreate(blkmem, &polynomialdata, 2, monomials, 0.0, FALSE) );
3392
3393 *op = SCIP_EXPR_POLYNOMIAL;
3394 data->data = (void*)polynomialdata;
3395
3396 break;
3397 }
3398
3399 case SCIP_EXPR_MUL:
3400 {
3401 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata;
3402 SCIP_EXPRDATA_MONOMIAL* monomial;
3403 int childidx[2];
3404 SCIP_Real exponent[2];
3405
3406 assert(nchildren == 2);
3407
3408 /* create monomial for product of children */
3409 childidx[0] = 0;
3410 childidx[1] = 1;
3411 exponent[0] = 1.0;
3412 exponent[1] = 1.0;
3413 SCIP_CALL( SCIPexprCreateMonomial(blkmem, &monomial, 1.0, 2, childidx, exponent) );
3414
3415 /* create polynomial */
3416 SCIP_CALL( polynomialdataCreate(blkmem, &polynomialdata, 1, &monomial, 0.0, FALSE) );
3417
3418 *op = SCIP_EXPR_POLYNOMIAL;
3419 data->data = (void*)polynomialdata;
3420
3421 break;
3422 }
3423
3424 case SCIP_EXPR_DIV:
3425 {
3426 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata;
3427 SCIP_EXPRDATA_MONOMIAL* monomial;
3428 int childidx[2];
3429 SCIP_Real exponent[2];
3430
3431 assert(nchildren == 2);
3432
3433 /* create monomial for division of children */
3434 childidx[0] = 0;
3435 childidx[1] = 1;
3436 exponent[0] = 1.0;
3437 exponent[1] = -1.0;
3438 SCIP_CALL( SCIPexprCreateMonomial(blkmem, &monomial, 1.0, 2, childidx, exponent) );
3439
3440 /* create polynomial */
3441 SCIP_CALL( polynomialdataCreate(blkmem, &polynomialdata, 1, &monomial, 0.0, FALSE) );
3442
3443 *op = SCIP_EXPR_POLYNOMIAL;
3444 data->data = (void*)polynomialdata;
3445
3446 break;
3447 }
3448
3449 case SCIP_EXPR_SQUARE:
3450 {
3451 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata;
3452 SCIP_EXPRDATA_MONOMIAL* monomial;
3453 int childidx;
3454 SCIP_Real exponent;
3455
3456 assert(nchildren == 1);
3457
3458 /* create monomial for square of child */
3459 childidx = 0;
3460 exponent = 2.0;
3461 SCIP_CALL( SCIPexprCreateMonomial(blkmem, &monomial, 1.0, 1, &childidx, &exponent) );
3462
3463 /* create polynomial */
3464 SCIP_CALL( polynomialdataCreate(blkmem, &polynomialdata, 1, &monomial, 0.0, FALSE) );
3465
3466 *op = SCIP_EXPR_POLYNOMIAL;
3467 data->data = (void*)polynomialdata;
3468
3469 break;
3470 }
3471
3472 case SCIP_EXPR_SQRT:
3473 {
3474 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata;
3475 SCIP_EXPRDATA_MONOMIAL* monomial;
3476 int childidx;
3477 SCIP_Real exponent;
3478
3479 assert(nchildren == 1);
3480
3481 /* create monomial for square root of child */
3482 childidx = 0;
3483 exponent = 0.5;
3484 SCIP_CALL( SCIPexprCreateMonomial(blkmem, &monomial, 1.0, 1, &childidx, &exponent) );
3485
3486 /* create polynomial */
3487 SCIP_CALL( polynomialdataCreate(blkmem, &polynomialdata, 1, &monomial, 0.0, FALSE) );
3488
3489 *op = SCIP_EXPR_POLYNOMIAL;
3490 data->data = (void*)polynomialdata;
3491
3492 break;
3493 }
3494
3495 case SCIP_EXPR_REALPOWER:
3496 {
3497 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata;
3498 SCIP_EXPRDATA_MONOMIAL* monomial;
3499 int childidx;
3500
3501 assert(nchildren == 1);
3502
3503 /* convert to child0 to the power of exponent */
3504
3505 /* create monomial for power of first child */
3506 childidx = 0;
3507 SCIP_CALL( SCIPexprCreateMonomial(blkmem, &monomial, 1.0, 1, &childidx, &data->dbl) );
3508
3509 /* create polynomial */
3510 SCIP_CALL( polynomialdataCreate(blkmem, &polynomialdata, 1, &monomial, 0.0, FALSE) );
3511
3512 *op = SCIP_EXPR_POLYNOMIAL;
3513 data->data = (void*)polynomialdata;
3514
3515 break;
3516 }
3517
3518 case SCIP_EXPR_SIGNPOWER:
3519 {
3520 SCIP_Real exponent;
3521
3522 assert(nchildren == 1);
3523
3524 /* check if exponent is an odd integer */
3525 exponent = data->dbl;
3526 if( EPSISINT(exponent, 0.0) && (int)exponent % 2 != 0 ) /*lint !e835*/
3527 {
3528 /* convert to child0 to the power of exponent, since sign is kept by taking power */
3529 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata;
3530 SCIP_EXPRDATA_MONOMIAL* monomial;
3531 int childidx;
3532
3533 /* create monomial for power of first child */
3534 childidx = 0;
3535 SCIP_CALL( SCIPexprCreateMonomial(blkmem, &monomial, 1.0, 1, &childidx, &exponent) );
3536
3537 /* create polynomial */
3538 SCIP_CALL( polynomialdataCreate(blkmem, &polynomialdata, 1, &monomial, 0.0, FALSE) );
3539
3540 *op = SCIP_EXPR_POLYNOMIAL;
3541 data->data = (void*)polynomialdata;
3542 }
3543 /* if exponent is not an odd integer constant, then keep it as signpower expression */
3544 break;
3545 }
3546
3547 case SCIP_EXPR_INTPOWER:
3548 {
3549 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata;
3550 SCIP_EXPRDATA_MONOMIAL* monomial;
3551 int childidx;
3552 SCIP_Real exponent;
3553
3554 assert(nchildren == 1);
3555
3556 /* create monomial for power of child */
3557 childidx = 0;
3558 exponent = data->intval;
3559 SCIP_CALL( SCIPexprCreateMonomial(blkmem, &monomial, 1.0, 1, &childidx, &exponent) );
3560
3561 /* create polynomial */
3562 SCIP_CALL( polynomialdataCreate(blkmem, &polynomialdata, 1, &monomial, 0.0, FALSE) );
3563
3564 *op = SCIP_EXPR_POLYNOMIAL;
3565 data->data = (void*)polynomialdata;
3566
3567 break;
3568 }
3569
3570 case SCIP_EXPR_EXP:
3571 case SCIP_EXPR_LOG:
3572 case SCIP_EXPR_SIN:
3573 case SCIP_EXPR_COS:
3574 case SCIP_EXPR_TAN:
3575 /* case SCIP_EXPR_ERF: */
3576 /* case SCIP_EXPR_ERFI: */
3577 case SCIP_EXPR_MIN:
3578 case SCIP_EXPR_MAX:
3579 case SCIP_EXPR_ABS:
3580 case SCIP_EXPR_SIGN:
3581 case SCIP_EXPR_USER:
3582 break;
3583
3584 case SCIP_EXPR_SUM:
3585 {
3586 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata;
3587 SCIP_EXPRDATA_MONOMIAL* monomial;
3588 int childidx;
3589 int i;
3590 SCIP_Real exponent;
3591
3592 /* create empty polynomial */
3593 SCIP_CALL( polynomialdataCreate(blkmem, &polynomialdata, 0, NULL, 0.0, FALSE) );
3594 SCIP_CALL( polynomialdataEnsureMonomialsSize(blkmem, polynomialdata, nchildren) );
3595 assert(polynomialdata->monomialssize >= nchildren);
3596
3597 /* add summands as monomials */
3598 childidx = 0;
3599 exponent = 1.0;
3600 SCIP_CALL( SCIPexprCreateMonomial(blkmem, &monomial, 1.0, 1, &childidx, &exponent) );
3601 for( i = 0; i < nchildren; ++i )
3602 {
3603 monomial->childidxs[0] = i;
3604 SCIP_CALL( polynomialdataAddMonomials(blkmem, polynomialdata, 1, &monomial, TRUE) );
3605 }
3606 SCIPexprFreeMonomial(blkmem, &monomial);
3607
3608 *op = SCIP_EXPR_POLYNOMIAL;
3609 data->data = (void*)polynomialdata;
3610
3611 break;
3612 }
3613
3614 case SCIP_EXPR_PRODUCT:
3615 {
3616 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata;
3617 SCIP_EXPRDATA_MONOMIAL* monomial;
3618 int childidx;
3619 int i;
3620 SCIP_Real exponent;
3621
3622 /* create monomial */
3623 SCIP_CALL( SCIPexprCreateMonomial(blkmem, &monomial, 1.0, 0, NULL, NULL) );
3624 SCIP_CALL( monomialdataEnsureFactorsSize(blkmem, monomial, nchildren) );
3625 exponent = 1.0;
3626 for( i = 0; i < nchildren; ++i )
3627 {
3628 childidx = i;
3629 SCIP_CALL( SCIPexprAddMonomialFactors(blkmem, monomial, 1, &childidx, &exponent) );
3630 }
3631
3632 /* create polynomial */
3633 SCIP_CALL( polynomialdataCreate(blkmem, &polynomialdata, 1, &monomial, 0.0, FALSE) );
3634
3635 *op = SCIP_EXPR_POLYNOMIAL;
3636 data->data = (void*)polynomialdata;
3637
3638 break;
3639 }
3640
3641 case SCIP_EXPR_LINEAR:
3642 {
3643 SCIP_Real* lineardata;
3644 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata;
3645 SCIP_EXPRDATA_MONOMIAL* monomial;
3646 int childidx;
3647 int i;
3648 SCIP_Real exponent;
3649
3650 /* get coefficients of linear term */
3651 lineardata = (SCIP_Real*)data->data;
3652 assert(lineardata != NULL);
3653
3654 /* create polynomial consisting of constant from linear term */
3655 SCIP_CALL( polynomialdataCreate(blkmem, &polynomialdata, 0, NULL, lineardata[nchildren], FALSE) );
3656 /* ensure space for linear coefficients */
3657 SCIP_CALL( polynomialdataEnsureMonomialsSize(blkmem, polynomialdata, nchildren) );
3658 assert(polynomialdata->monomialssize >= nchildren);
3659
3660 /* add summands as monomials */
3661 childidx = 0;
3662 exponent = 1.0;
3663 SCIP_CALL( SCIPexprCreateMonomial(blkmem, &monomial, 1.0, 1, &childidx, &exponent) );
3664 for( i = 0; i < nchildren; ++i )
3665 {
3666 monomial->coef = lineardata[i];
3667 monomial->childidxs[0] = i;
3668 SCIP_CALL( polynomialdataAddMonomials(blkmem, polynomialdata, 1, &monomial, TRUE) );
3669 }
3670 SCIPexprFreeMonomial(blkmem, &monomial);
3671
3672 /* free linear expression data */
3673 exprFreeDataLinear(blkmem, nchildren, *data);
3674
3675 *op = SCIP_EXPR_POLYNOMIAL;
3676 data->data = (void*)polynomialdata;
3677
3678 break;
3679 }
3680
3681 case SCIP_EXPR_QUADRATIC:
3682 {
3683 SCIP_EXPRDATA_QUADRATIC* quaddata;
3684 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata;
3685 SCIP_EXPRDATA_MONOMIAL* squaremonomial;
3686 SCIP_EXPRDATA_MONOMIAL* bilinmonomial;
3687 SCIP_EXPRDATA_MONOMIAL* linmonomial;
3688 int childidx[2];
3689 SCIP_Real exponent[2];
3690 int i;
3691
3692 /* get data of quadratic expression */
3693 quaddata = (SCIP_EXPRDATA_QUADRATIC*)data->data;
3694 assert(quaddata != NULL);
3695
3696 /* create empty polynomial */
3697 SCIP_CALL( polynomialdataCreate(blkmem, &polynomialdata, 0, NULL, quaddata->constant, FALSE) );
3698 /* ensure space for linear and quadratic terms */
3699 SCIP_CALL( polynomialdataEnsureMonomialsSize(blkmem, polynomialdata, (quaddata->lincoefs != NULL ? nchildren : 0) + quaddata->nquadelems) );
3700 assert(polynomialdata->monomialssize >= quaddata->nquadelems);
3701
3702 childidx[0] = 0;
3703 childidx[1] = 0;
3704
3705 /* create monomial templates */
3706 exponent[0] = 2.0;
3707 SCIP_CALL( SCIPexprCreateMonomial(blkmem, &squaremonomial, 1.0, 1, childidx, exponent) );
3708 exponent[0] = 1.0;
3709 exponent[1] = 1.0;
3710 SCIP_CALL( SCIPexprCreateMonomial(blkmem, &bilinmonomial, 1.0, 2, childidx, exponent) );
3711 SCIP_CALL( SCIPexprCreateMonomial(blkmem, &linmonomial, 1.0, 1, childidx, exponent) );
3712
3713 /* add linear terms as monomials */
3714 if( quaddata->lincoefs != NULL )
3715 for( i = 0; i < nchildren; ++i )
3716 if( quaddata->lincoefs[i] != 0.0 )
3717 {
3718 linmonomial->childidxs[0] = i;
3719 linmonomial->coef = quaddata->lincoefs[i];
3720 SCIP_CALL( polynomialdataAddMonomials(blkmem, polynomialdata, 1, &linmonomial, TRUE) );
3721 }
3722
3723 /* add quadratic terms as monomials */
3724 for( i = 0; i < quaddata->nquadelems; ++i )
3725 {
3726 if( quaddata->quadelems[i].idx1 == quaddata->quadelems[i].idx2 )
3727 {
3728 squaremonomial->childidxs[0] = quaddata->quadelems[i].idx1;
3729 squaremonomial->coef = quaddata->quadelems[i].coef;
3730 SCIP_CALL( polynomialdataAddMonomials(blkmem, polynomialdata, 1, &squaremonomial, TRUE) );
3731 }
3732 else
3733 {
3734 bilinmonomial->childidxs[0] = quaddata->quadelems[i].idx1;
3735 bilinmonomial->childidxs[1] = quaddata->quadelems[i].idx2;
3736 bilinmonomial->coef = quaddata->quadelems[i].coef;
3737 SCIP_CALL( polynomialdataAddMonomials(blkmem, polynomialdata, 1, &bilinmonomial, TRUE) );
3738 }
3739 }
3740 SCIPexprFreeMonomial(blkmem, &squaremonomial);
3741 SCIPexprFreeMonomial(blkmem, &bilinmonomial);
3742 SCIPexprFreeMonomial(blkmem, &linmonomial);
3743
3744 /* free quadratic expression data */
3745 exprFreeDataQuadratic(blkmem, nchildren, *data);
3746
3747 *op = SCIP_EXPR_POLYNOMIAL;
3748 data->data = (void*)polynomialdata;
3749
3750 break;
3751 }
3752
3753 case SCIP_EXPR_POLYNOMIAL:
3754 case SCIP_EXPR_LAST:
3755 break;
3756 } /*lint !e788*/
3757
3758 return SCIP_OKAY;
3759 }
3760
3761 /** converts polynomial expression back into simpler expression, if possible */
3762 static
exprUnconvertPolynomial(BMS_BLKMEM * blkmem,SCIP_EXPROP * op,SCIP_EXPROPDATA * data,int nchildren,void ** children)3763 SCIP_RETCODE exprUnconvertPolynomial(
3764 BMS_BLKMEM* blkmem, /**< block memory data structure */
3765 SCIP_EXPROP* op, /**< pointer to expression operator */
3766 SCIP_EXPROPDATA* data, /**< pointer to expression data holding polynomial data */
3767 int nchildren, /**< number of children of operator */
3768 void** children /**< children array */
3769 )
3770 {
3771 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata;
3772 SCIP_EXPRDATA_MONOMIAL* monomial;
3773 int maxdegree;
3774 int nlinmonomials;
3775 int i;
3776 int j;
3777
3778 assert(blkmem != NULL);
3779 assert(op != NULL);
3780 assert(*op == SCIP_EXPR_POLYNOMIAL);
3781 assert(data != NULL);
3782 assert(children != NULL || nchildren == 0);
3783
3784 polynomialdata = (SCIP_EXPRDATA_POLYNOMIAL*)data->data;
3785 assert(polynomialdata != NULL);
3786
3787 /* make sure monomials are sorted and merged */
3788 polynomialdataMergeMonomials(blkmem, polynomialdata, 0.0, TRUE);
3789
3790 /* if no monomials, then leave as it is */
3791 if( polynomialdata->nmonomials == 0 )
3792 return SCIP_OKAY;
3793
3794 /* check maximal degree of polynomial only - not considering children expressions
3795 * check number of linear monomials */
3796 maxdegree = 0;
3797 nlinmonomials = 0;
3798 for( i = 0; i < polynomialdata->nmonomials; ++i )
3799 {
3800 int monomialdegree;
3801
3802 monomial = polynomialdata->monomials[i];
3803 assert(monomial != NULL);
3804
3805 monomialdegree = 0;
3806 for(j = 0; j < monomial->nfactors; ++j )
3807 {
3808 if( !EPSISINT(monomial->exponents[j], 0.0) || monomial->exponents[j] < 0.0 ) /*lint !e835*/
3809 {
3810 monomialdegree = SCIP_EXPR_DEGREEINFINITY;
3811 break;
3812 }
3813
3814 monomialdegree += (int)EPSROUND(monomial->exponents[j], 0.0); /*lint !e835*/
3815 }
3816
3817 if( monomialdegree == SCIP_EXPR_DEGREEINFINITY )
3818 {
3819 maxdegree = SCIP_EXPR_DEGREEINFINITY;
3820 break;
3821 }
3822
3823 if( monomialdegree == 1 )
3824 ++nlinmonomials;
3825
3826 if( monomialdegree > maxdegree )
3827 maxdegree = monomialdegree;
3828 }
3829 assert(maxdegree > 0 );
3830
3831 if( maxdegree == 1 )
3832 {
3833 /* polynomial is a linear expression in children */
3834
3835 /* polynomial simplification and monomial merging should ensure that monomial i corresponds to child i and that there are not unused children */
3836 assert(polynomialdata->nmonomials == nchildren);
3837 assert(polynomialdata->nmonomials == nlinmonomials);
3838
3839 if( polynomialdata->constant == 0.0 && polynomialdata->nmonomials == 2 && polynomialdata->monomials[0]->coef == 1.0 && polynomialdata->monomials[1]->coef == 1.0 )
3840 {
3841 /* polynomial is addition of two expressions, so turn into SCIP_EXPR_PLUS */
3842 assert(polynomialdata->monomials[0]->nfactors == 1);
3843 assert(polynomialdata->monomials[0]->exponents[0] == 1.0);
3844 assert(polynomialdata->monomials[1]->nfactors == 1);
3845 assert(polynomialdata->monomials[1]->exponents[0] == 1.0);
3846
3847 polynomialdataFree(blkmem, &polynomialdata);
3848 data->data = NULL;
3849
3850 /* change operator type to PLUS */
3851 *op = SCIP_EXPR_PLUS;
3852
3853 return SCIP_OKAY;
3854 }
3855
3856 if( polynomialdata->constant == 0.0 && polynomialdata->nmonomials == 2 && polynomialdata->monomials[0]->coef == 1.0 && polynomialdata->monomials[1]->coef == -1.0 )
3857 {
3858 /* polynomial is substraction of two expressions, so turn into SCIP_EXPR_MINUS */
3859 assert(polynomialdata->monomials[0]->nfactors == 1);
3860 assert(polynomialdata->monomials[0]->exponents[0] == 1.0);
3861 assert(polynomialdata->monomials[1]->nfactors == 1);
3862 assert(polynomialdata->monomials[1]->exponents[0] == 1.0);
3863
3864 polynomialdataFree(blkmem, &polynomialdata);
3865 data->data = NULL;
3866
3867 /* change operator type to MINUS */
3868 *op = SCIP_EXPR_MINUS;
3869
3870 return SCIP_OKAY;
3871 }
3872
3873 if( polynomialdata->constant == 0.0 && polynomialdata->nmonomials == 2 && polynomialdata->monomials[0]->coef == -1.0 && polynomialdata->monomials[1]->coef == 1.0 )
3874 {
3875 /* polynomial is substraction of two expressions, so turn into SCIP_EXPR_MINUS */
3876 void* tmp;
3877
3878 assert(polynomialdata->monomials[0]->nfactors == 1);
3879 assert(polynomialdata->monomials[0]->exponents[0] == 1.0);
3880 assert(polynomialdata->monomials[1]->nfactors == 1);
3881 assert(polynomialdata->monomials[1]->exponents[0] == 1.0);
3882
3883 polynomialdataFree(blkmem, &polynomialdata);
3884 data->data = NULL;
3885
3886 /* swap children */
3887 tmp = children[1]; /*lint !e613*/
3888 children[1] = children[0]; /*lint !e613*/
3889 children[0] = tmp; /*lint !e613*/
3890
3891 /* change operator type to MINUS */
3892 *op = SCIP_EXPR_MINUS;
3893
3894 return SCIP_OKAY;
3895 }
3896
3897 if( polynomialdata->constant == 0.0 )
3898 {
3899 /* check if all monomials have coefficient 1.0 */
3900 for( i = 0; i < polynomialdata->nmonomials; ++i )
3901 if( polynomialdata->monomials[i]->coef != 1.0 )
3902 break;
3903
3904 if( i == polynomialdata->nmonomials )
3905 {
3906 /* polynomial is sum of children, so turn into SCIP_EXPR_SUM */
3907
3908 polynomialdataFree(blkmem, &polynomialdata);
3909 data->data = NULL;
3910
3911 /* change operator type to MINUS */
3912 *op = SCIP_EXPR_SUM;
3913
3914 return SCIP_OKAY;
3915 }
3916 }
3917
3918 /* turn polynomial into linear expression */
3919 {
3920 SCIP_Real* lindata;
3921
3922 /* monomial merging should ensure that each child appears in at most one monomial,
3923 * that monomials are ordered according to the child index, and that constant monomials have been removed
3924 */
3925
3926 /* setup data of linear expression */
3927 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &lindata, polynomialdata->nmonomials + 1) );
3928
3929 for( i = 0; i < polynomialdata->nmonomials; ++i )
3930 {
3931 assert(polynomialdata->monomials[i]->childidxs[0] == i);
3932 assert(polynomialdata->monomials[i]->exponents[0] == 1.0);
3933 lindata[i] = polynomialdata->monomials[i]->coef; /*lint !e644*/
3934 }
3935 lindata[i] = polynomialdata->constant;
3936
3937 polynomialdataFree(blkmem, &polynomialdata);
3938 *op = SCIP_EXPR_LINEAR;
3939 data->data = (void*)lindata;
3940
3941 return SCIP_OKAY;
3942 }
3943 }
3944
3945 if( maxdegree == 2 && (polynomialdata->nmonomials > 1 || polynomialdata->constant != 0.0 || polynomialdata->monomials[0]->coef != 1.0) )
3946 {
3947 /* polynomial is quadratic expression with more than one summand or with a constant or a square or bilinear term with coefficient != 1.0, so turn into SCIP_EXPR_QUADRATIC */
3948 SCIP_EXPRDATA_QUADRATIC* quaddata;
3949 int quadelemidx;
3950
3951 SCIP_ALLOC( BMSallocBlockMemory(blkmem, &quaddata) );
3952 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &quaddata->quadelems, polynomialdata->nmonomials - nlinmonomials) );
3953 quaddata->nquadelems = polynomialdata->nmonomials - nlinmonomials;
3954 quaddata->constant = polynomialdata->constant;
3955 quaddata->sorted = FALSE; /* quadratic data is sorted different than polynomials */
3956
3957 if( nlinmonomials > 0 )
3958 {
3959 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &quaddata->lincoefs, nchildren) );
3960 BMSclearMemoryArray(quaddata->lincoefs, nchildren);
3961 }
3962 else
3963 quaddata->lincoefs = NULL;
3964
3965 quadelemidx = 0;
3966 for( i = 0; i < polynomialdata->nmonomials; ++i )
3967 {
3968 assert(polynomialdata->monomials[i]->nfactors == 1 || polynomialdata->monomials[i]->nfactors == 2);
3969 if( polynomialdata->monomials[i]->nfactors == 1 )
3970 {
3971 if( polynomialdata->monomials[i]->exponents[0] == 1.0 )
3972 {
3973 /* monomial is a linear term */
3974 assert(quaddata->lincoefs != NULL);
3975 /* coverity[var_deref_op] */
3976 quaddata->lincoefs[polynomialdata->monomials[i]->childidxs[0]] += polynomialdata->monomials[i]->coef;
3977 }
3978 else
3979 {
3980 /* monomial should be a square term */
3981 assert(polynomialdata->monomials[i]->exponents[0] == 2.0);
3982 assert(quadelemidx < quaddata->nquadelems);
3983 quaddata->quadelems[quadelemidx].idx1 = polynomialdata->monomials[i]->childidxs[0];
3984 quaddata->quadelems[quadelemidx].idx2 = polynomialdata->monomials[i]->childidxs[0];
3985 quaddata->quadelems[quadelemidx].coef = polynomialdata->monomials[i]->coef;
3986 ++quadelemidx;
3987 }
3988 }
3989 else
3990 {
3991 /* monomial should be a bilinear term */
3992 assert(polynomialdata->monomials[i]->exponents[0] == 1.0);
3993 assert(polynomialdata->monomials[i]->exponents[1] == 1.0);
3994 assert(quadelemidx < quaddata->nquadelems);
3995 quaddata->quadelems[quadelemidx].idx1 = MIN(polynomialdata->monomials[i]->childidxs[0], polynomialdata->monomials[i]->childidxs[1]);
3996 quaddata->quadelems[quadelemidx].idx2 = MAX(polynomialdata->monomials[i]->childidxs[0], polynomialdata->monomials[i]->childidxs[1]);
3997 quaddata->quadelems[quadelemidx].coef = polynomialdata->monomials[i]->coef;
3998 ++quadelemidx;
3999 }
4000 }
4001 assert(quadelemidx == quaddata->nquadelems);
4002
4003 polynomialdataFree(blkmem, &polynomialdata);
4004
4005 *op = SCIP_EXPR_QUADRATIC;
4006 data->data = (void*)quaddata;
4007
4008 return SCIP_OKAY;
4009 }
4010
4011 if( polynomialdata->constant == 0.0 && polynomialdata->nmonomials == 1 && polynomialdata->monomials[0]->coef == 1.0 )
4012 {
4013 /* polynomial is product of children */
4014 monomial = polynomialdata->monomials[0];
4015 assert(monomial->nfactors == nchildren);
4016
4017 if( monomial->nfactors == 1 )
4018 {
4019 /* polynomial is x^k for some k */
4020 assert(monomial->exponents[0] != 1.0); /* should have been handled before */
4021 assert(monomial->childidxs[0] == 0);
4022
4023 if( monomial->exponents[0] == 2.0 )
4024 {
4025 /* polynomial is x^2, so turn into SCIP_EXPR_SQUARE */
4026
4027 polynomialdataFree(blkmem, &polynomialdata);
4028 data->data = NULL;
4029
4030 *op = SCIP_EXPR_SQUARE;
4031
4032 return SCIP_OKAY;
4033 }
4034
4035 if( EPSISINT(monomial->exponents[0], 0.0) ) /*lint !e835*/
4036 {
4037 /* k is an integer, so turn into SCIP_EXPR_INTPOWER */
4038 int exponent;
4039
4040 exponent = (int)EPSROUND(monomial->exponents[0], 0.0); /*lint !e835*/
4041
4042 polynomialdataFree(blkmem, &polynomialdata);
4043
4044 *op = SCIP_EXPR_INTPOWER;
4045 data->intval = exponent;
4046
4047 return SCIP_OKAY;
4048 }
4049
4050 if( monomial->exponents[0] == 0.5 )
4051 {
4052 /* polynomial is sqrt(x), so turn into SCIP_EXPR_SQRT */
4053
4054 polynomialdataFree(blkmem, &polynomialdata);
4055 data->data = NULL;
4056
4057 *op = SCIP_EXPR_SQRT;
4058
4059 return SCIP_OKAY;
4060 }
4061
4062 {
4063 /* polynomial is x^a with a some real number, so turn into SCIP_EXPR_REALPOWER */
4064 SCIP_Real exponent;
4065
4066 exponent = monomial->exponents[0];
4067
4068 polynomialdataFree(blkmem, &polynomialdata);
4069
4070 *op = SCIP_EXPR_REALPOWER;
4071 data->dbl = exponent;
4072
4073 return SCIP_OKAY;
4074 }
4075 }
4076
4077 if( maxdegree == 2 && monomial->nfactors == 2 )
4078 {
4079 /* polynomial is product of two children, so turn into SCIP_EXPR_MUL */
4080 assert(monomial->exponents[0] == 1.0);
4081 assert(monomial->exponents[1] == 1.0);
4082
4083 polynomialdataFree(blkmem, &polynomialdata);
4084 data->data = NULL;
4085
4086 *op = SCIP_EXPR_MUL;
4087
4088 return SCIP_OKAY;
4089 }
4090
4091 if( maxdegree == monomial->nfactors )
4092 {
4093 /* polynomial is a product of n children, so turn into SCIP_EXPR_PRODUCT */
4094
4095 polynomialdataFree(blkmem, &polynomialdata);
4096 data->data = NULL;
4097
4098 *op = SCIP_EXPR_PRODUCT;
4099
4100 return SCIP_OKAY;
4101 }
4102
4103 if( monomial->nfactors == 2 && monomial->exponents[0] == 1.0 && monomial->exponents[1] == -1.0 )
4104 {
4105 /* polynomial is x/y, so turn into SCIP_EXPR_DIV */
4106 assert(monomial->childidxs[0] == 0);
4107 assert(monomial->childidxs[1] == 1);
4108
4109 polynomialdataFree(blkmem, &polynomialdata);
4110 data->data = NULL;
4111
4112 *op = SCIP_EXPR_DIV;
4113
4114 return SCIP_OKAY;
4115 }
4116
4117 if( monomial->nfactors == 2 && monomial->exponents[0] == -1.0 && monomial->exponents[1] == 1.0 )
4118 {
4119 /* polynomial is y/x, so turn into SCIP_EXPR_DIV */
4120 void* tmp;
4121
4122 assert(monomial->childidxs[0] == 0);
4123 assert(monomial->childidxs[1] == 1);
4124
4125 polynomialdataFree(blkmem, &polynomialdata);
4126 data->data = NULL;
4127
4128 /* swap children */
4129 tmp = children[1]; /*lint !e613*/
4130 children[1] = children[0]; /*lint !e613*/
4131 children[0] = tmp; /*lint !e613*/
4132
4133 *op = SCIP_EXPR_DIV;
4134
4135 return SCIP_OKAY;
4136 }
4137 }
4138
4139 return SCIP_OKAY;
4140 }
4141
4142 /** adds copies of expressions to the array of children of a sum, product, linear, quadratic, or polynomial expression
4143 *
4144 * For a sum or product expression, this corresponds to add additional summands and factors, resp.
4145 * For a linear expression, this corresponds to add each expression with coefficient 1.0.
4146 * For a quadratic or polynomial expression, only the children array may be enlarged, the expression itself remains the same.
4147 */
4148 static
exprsimplifyAddChildren(BMS_BLKMEM * blkmem,SCIP_EXPR * expr,int nexprs,SCIP_EXPR ** exprs,SCIP_Bool comparechildren,SCIP_Real eps,int * childmap)4149 SCIP_RETCODE exprsimplifyAddChildren(
4150 BMS_BLKMEM* blkmem, /**< block memory */
4151 SCIP_EXPR* expr, /**< quadratic or polynomial expression */
4152 int nexprs, /**< number of expressions to add */
4153 SCIP_EXPR** exprs, /**< expressions to add */
4154 SCIP_Bool comparechildren, /**< whether to compare expressions with already existing children (no effect for sum and product) */
4155 SCIP_Real eps, /**< which epsilon to use when comparing expressions */
4156 int* childmap /**< array where to store mapping of indices from exprs to children array in expr, or NULL if not of interest */
4157 )
4158 {
4159 int i;
4160
4161 assert(blkmem != NULL);
4162 assert(expr != NULL);
4163 assert(expr->op == SCIP_EXPR_SUM || expr->op == SCIP_EXPR_PRODUCT || expr->op == SCIP_EXPR_LINEAR || expr->op == SCIP_EXPR_QUADRATIC || expr->op == SCIP_EXPR_POLYNOMIAL);
4164 assert(exprs != NULL || nexprs == 0);
4165
4166 if( nexprs == 0 )
4167 return SCIP_OKAY;
4168
4169 switch( expr->op )
4170 {
4171 case SCIP_EXPR_SUM:
4172 case SCIP_EXPR_PRODUCT:
4173 {
4174 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &expr->children, expr->nchildren, expr->nchildren + nexprs) );
4175 for( i = 0; i < nexprs; ++i )
4176 {
4177 SCIP_CALL( SCIPexprCopyDeep(blkmem, &expr->children[expr->nchildren + i], exprs[i]) ); /*lint !e613*/
4178 if( childmap != NULL )
4179 childmap[i] = expr->nchildren + i;
4180 }
4181 expr->nchildren += nexprs;
4182
4183 break;
4184 }
4185
4186 case SCIP_EXPR_LINEAR:
4187 case SCIP_EXPR_QUADRATIC:
4188 case SCIP_EXPR_POLYNOMIAL:
4189 {
4190 int j;
4191 int orignchildren;
4192 SCIP_Bool existsalready;
4193
4194 orignchildren = expr->nchildren;
4195 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &expr->children, expr->nchildren, expr->nchildren + nexprs) );
4196
4197 for( i = 0; i < nexprs; ++i )
4198 {
4199 existsalready = FALSE;
4200 if( comparechildren )
4201 for( j = 0; j < orignchildren; ++j )
4202 /* during simplification of polynomials, their may be NULL's in children array */
4203 if( expr->children[j] != NULL && SCIPexprAreEqual(expr->children[j], exprs[i], eps) ) /*lint !e613*/
4204 {
4205 existsalready = TRUE;
4206 break;
4207 }
4208
4209 if( !existsalready )
4210 {
4211 /* add copy of exprs[j] to children array */
4212 SCIP_CALL( SCIPexprCopyDeep(blkmem, &expr->children[expr->nchildren], exprs[i]) ); /*lint !e613*/
4213 if( childmap != NULL )
4214 childmap[i] = expr->nchildren;
4215 ++expr->nchildren;
4216 }
4217 else
4218 {
4219 if( childmap != NULL )
4220 childmap[i] = j; /*lint !e644*/
4221 if( expr->op == SCIP_EXPR_LINEAR )
4222 {
4223 /* if linear expression, increase coefficient by 1.0 */
4224 ((SCIP_Real*)expr->data.data)[j] += 1.0;
4225 }
4226 }
4227 }
4228
4229 /* shrink children array to actually used size */
4230 assert(comparechildren || expr->nchildren == orignchildren + nexprs);
4231 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &expr->children, orignchildren + nexprs, expr->nchildren) );
4232
4233 if( expr->op == SCIP_EXPR_LINEAR && expr->nchildren > orignchildren )
4234 {
4235 /* if linear expression, then add 1.0 coefficients for new expressions */
4236 SCIP_Real* data;
4237
4238 data = (SCIP_Real*)expr->data.data;
4239 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &data, orignchildren + 1, expr->nchildren + 1) );
4240 data[expr->nchildren] = data[orignchildren]; /* move constant from old end to new end */
4241 for( i = orignchildren; i < expr->nchildren; ++i )
4242 data[i] = 1.0;
4243 expr->data.data = (void*)data;
4244 }
4245 else if( expr->op == SCIP_EXPR_QUADRATIC && expr->nchildren > orignchildren )
4246 {
4247 /* if quadratic expression, then add 0.0 linear coefficients for new expressions */
4248 SCIP_EXPRDATA_QUADRATIC* data;
4249
4250 data = (SCIP_EXPRDATA_QUADRATIC*)expr->data.data;
4251 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &data->lincoefs, orignchildren, expr->nchildren) );
4252 BMSclearMemoryArray(&data->lincoefs[orignchildren], expr->nchildren - orignchildren); /*lint !e866*/
4253 }
4254
4255 break;
4256 }
4257
4258 default:
4259 SCIPerrorMessage("exprsimplifyAddChildren cannot be called for operand %d\n", expr->op);
4260 return SCIP_INVALIDDATA;
4261 } /*lint !e788*/
4262
4263 return SCIP_OKAY;
4264 }
4265
4266 /** converts expressions into polynomials, where possible and obvious */
4267 static
exprsimplifyConvertToPolynomials(BMS_BLKMEM * blkmem,SCIP_EXPR * expr)4268 SCIP_RETCODE exprsimplifyConvertToPolynomials(
4269 BMS_BLKMEM* blkmem, /**< block memory data structure */
4270 SCIP_EXPR* expr /**< expression to convert */
4271 )
4272 {
4273 int i;
4274
4275 assert(expr != NULL);
4276
4277 for( i = 0; i < expr->nchildren; ++i )
4278 {
4279 SCIP_CALL( exprsimplifyConvertToPolynomials(blkmem, expr->children[i]) );
4280 }
4281
4282 SCIP_CALL( exprConvertToPolynomial(blkmem, &expr->op, &expr->data, expr->nchildren) );
4283
4284 return SCIP_OKAY;
4285 }
4286
4287 /** removes duplicate children in a polynomial expression
4288 *
4289 * Leaves NULL's in children array.
4290 */
4291 static
exprsimplifyRemoveDuplicatePolynomialChildren(BMS_BLKMEM * blkmem,SCIP_EXPR * expr,SCIP_Real eps)4292 SCIP_RETCODE exprsimplifyRemoveDuplicatePolynomialChildren(
4293 BMS_BLKMEM* blkmem, /**< block memory data structure */
4294 SCIP_EXPR* expr, /**< expression */
4295 SCIP_Real eps /**< threshold for zero */
4296 )
4297 {
4298 SCIP_Bool foundduplicates;
4299 int* childmap;
4300 int i;
4301 int j;
4302
4303 assert(blkmem != NULL);
4304 assert(expr != NULL);
4305 assert(SCIPexprGetOperator(expr) == SCIP_EXPR_POLYNOMIAL);
4306
4307 if( expr->nchildren == 0 )
4308 return SCIP_OKAY;
4309
4310 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &childmap, expr->nchildren) );
4311
4312 foundduplicates = FALSE;
4313 for( i = 0; i < expr->nchildren; ++i )
4314 {
4315 if( expr->children[i] == NULL )
4316 continue;
4317 childmap[i] = i; /*lint !e644*/
4318
4319 for( j = i+1; j < expr->nchildren; ++j )
4320 {
4321 if( expr->children[j] == NULL )
4322 continue;
4323
4324 if( SCIPexprAreEqual(expr->children[i], expr->children[j], eps) )
4325 {
4326 /* forget about expr j and remember that is to be replaced by i */
4327 SCIPexprFreeDeep(blkmem, &expr->children[j]);
4328 childmap[j] = i;
4329 foundduplicates = TRUE;
4330 }
4331 }
4332 }
4333
4334 /* apply childmap to monomials */
4335 if( foundduplicates )
4336 polynomialdataApplyChildmap((SCIP_EXPRDATA_POLYNOMIAL*)expr->data.data, childmap);
4337
4338 /* free childmap */
4339 BMSfreeBlockMemoryArray(blkmem, &childmap, expr->nchildren);
4340
4341 return SCIP_OKAY;
4342 }
4343
4344 /** eliminates NULL's in children array and shrinks it to actual size */
4345 static
exprsimplifyRemovePolynomialNullChildren(BMS_BLKMEM * blkmem,SCIP_EXPR * expr)4346 SCIP_RETCODE exprsimplifyRemovePolynomialNullChildren(
4347 BMS_BLKMEM* blkmem, /**< block memory data structure */
4348 SCIP_EXPR* expr /**< expression */
4349 )
4350 {
4351 int* childmap;
4352 int lastnonnull;
4353 int i;
4354
4355 assert(blkmem != NULL);
4356 assert(expr != NULL);
4357 assert(SCIPexprGetOperator(expr) == SCIP_EXPR_POLYNOMIAL);
4358
4359 if( expr->nchildren == 0 )
4360 return SCIP_OKAY;
4361
4362 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &childmap, expr->nchildren) );
4363
4364 /* close gaps in children array */
4365 lastnonnull = expr->nchildren-1;
4366 while( lastnonnull >= 0 && expr->children[lastnonnull] == NULL )
4367 --lastnonnull;
4368 for( i = 0; i <= lastnonnull; ++i )
4369 {
4370 if( expr->children[i] != NULL )
4371 {
4372 childmap[i] = i; /* child at index i is not moved */ /*lint !e644*/
4373 continue;
4374 }
4375 assert(expr->children[lastnonnull] != NULL);
4376
4377 /* move child at lastnonnull to position i */
4378 expr->children[i] = expr->children[lastnonnull];
4379 expr->children[lastnonnull] = NULL;
4380 childmap[lastnonnull] = i;
4381
4382 /* update lastnonnull */
4383 --lastnonnull;
4384 while( lastnonnull >= 0 && expr->children[lastnonnull] == NULL )
4385 --lastnonnull;
4386 }
4387 assert(i > lastnonnull);
4388
4389 /* apply childmap to monomials */
4390 if( lastnonnull < expr->nchildren-1 )
4391 polynomialdataApplyChildmap((SCIP_EXPRDATA_POLYNOMIAL*)expr->data.data, childmap);
4392
4393 BMSfreeBlockMemoryArray(blkmem, &childmap, expr->nchildren);
4394
4395 /* shrink children array */
4396 if( lastnonnull >= 0 )
4397 {
4398 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &expr->children, expr->nchildren, lastnonnull+1) );
4399 expr->nchildren = lastnonnull+1;
4400 }
4401 else
4402 {
4403 BMSfreeBlockMemoryArray(blkmem, &expr->children, expr->nchildren);
4404 expr->nchildren = 0;
4405 }
4406
4407 return SCIP_OKAY;
4408 }
4409
4410 /** checks which children are still in use and frees those which are not */
4411 static
exprsimplifyRemovePolynomialUnusedChildren(BMS_BLKMEM * blkmem,SCIP_EXPR * expr)4412 SCIP_RETCODE exprsimplifyRemovePolynomialUnusedChildren(
4413 BMS_BLKMEM* blkmem, /**< block memory data structure */
4414 SCIP_EXPR* expr /**< polynomial expression */
4415 )
4416 {
4417 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata;
4418 SCIP_EXPRDATA_MONOMIAL* monomial;
4419 SCIP_Bool* childinuse;
4420 int i;
4421 int j;
4422
4423 assert(blkmem != NULL);
4424 assert(expr != NULL);
4425
4426 if( expr->nchildren == 0 )
4427 return SCIP_OKAY;
4428
4429 polynomialdata = (SCIP_EXPRDATA_POLYNOMIAL*)expr->data.data;
4430 assert(polynomialdata != NULL);
4431
4432 /* check which children are still in use */
4433 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &childinuse, expr->nchildren) );
4434 BMSclearMemoryArray(childinuse, expr->nchildren); /*lint !e644*/
4435 for( i = 0; i < polynomialdata->nmonomials; ++i )
4436 {
4437 monomial = polynomialdata->monomials[i];
4438 assert(monomial != NULL);
4439
4440 for( j = 0; j < monomial->nfactors; ++j )
4441 {
4442 assert(monomial->childidxs[j] >= 0);
4443 assert(monomial->childidxs[j] < expr->nchildren);
4444 childinuse[monomial->childidxs[j]] = TRUE;
4445 }
4446 }
4447
4448 /* free children that are not used in any monomial */
4449 for( i = 0; i < expr->nchildren; ++i )
4450 if( expr->children[i] != NULL && !childinuse[i] )
4451 SCIPexprFreeDeep(blkmem, &expr->children[i]);
4452
4453 BMSfreeBlockMemoryArray(blkmem, &childinuse, expr->nchildren);
4454
4455 return SCIP_OKAY;
4456 }
4457
4458 /** flattens polynomials in polynomials, check for constants in non-polynomials expressions
4459 *
4460 * exprsimplifyConvertToPolynomials should have been called before to eliminate simple polynomial operands.
4461 */
4462 static
exprsimplifyFlattenPolynomials(BMS_BLKMEM * blkmem,SCIP_MESSAGEHDLR * messagehdlr,SCIP_EXPR * expr,SCIP_Real eps,int maxexpansionexponent)4463 SCIP_RETCODE exprsimplifyFlattenPolynomials(
4464 BMS_BLKMEM* blkmem, /**< block memory data structure */
4465 SCIP_MESSAGEHDLR* messagehdlr, /**< message handler */
4466 SCIP_EXPR* expr, /**< expression */
4467 SCIP_Real eps, /**< threshold, under which values are treat as 0 */
4468 int maxexpansionexponent/**< maximal exponent for which we still expand non-monomial polynomials */
4469 )
4470 {
4471 int i;
4472
4473 assert(expr != NULL);
4474
4475 for( i = 0; i < expr->nchildren; ++i )
4476 {
4477 SCIP_CALL( exprsimplifyFlattenPolynomials(blkmem, messagehdlr, expr->children[i], eps, maxexpansionexponent) );
4478 }
4479
4480 switch( SCIPexprGetOperator(expr) )
4481 {
4482 case SCIP_EXPR_VARIDX:
4483 case SCIP_EXPR_CONST:
4484 case SCIP_EXPR_PARAM:
4485 case SCIP_EXPR_PLUS:
4486 case SCIP_EXPR_MINUS:
4487 case SCIP_EXPR_MUL:
4488 case SCIP_EXPR_DIV:
4489 case SCIP_EXPR_SQUARE:
4490 case SCIP_EXPR_SQRT:
4491 case SCIP_EXPR_INTPOWER:
4492 case SCIP_EXPR_REALPOWER:
4493 case SCIP_EXPR_SIGNPOWER:
4494 break;
4495
4496 case SCIP_EXPR_EXP:
4497 case SCIP_EXPR_LOG:
4498 case SCIP_EXPR_SIN:
4499 case SCIP_EXPR_COS:
4500 case SCIP_EXPR_TAN:
4501 /* case SCIP_EXPR_ERF: */
4502 /* case SCIP_EXPR_ERFI: */
4503 case SCIP_EXPR_ABS:
4504 case SCIP_EXPR_SIGN:
4505 {
4506 /* check if argument is a constant */
4507 if( (expr->children[0]->op == SCIP_EXPR_POLYNOMIAL && SCIPexprGetNChildren(expr->children[0]) == 0) ||
4508 expr->children[0]->op == SCIP_EXPR_CONST )
4509 {
4510 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata;
4511 SCIP_Real exprval;
4512
4513 /* since child0 has no children and it's polynomial was flattened, it should have no monomials */
4514 assert(expr->children[0]->op != SCIP_EXPR_POLYNOMIAL || SCIPexprGetNMonomials(expr->children[0]) == 0);
4515
4516 /* evaluate expression in constant polynomial */
4517 SCIP_CALL( SCIPexprEval(expr, NULL, NULL, &exprval) );
4518
4519 /* create polynomial */
4520 SCIP_CALL( polynomialdataCreate(blkmem, &polynomialdata, 0, NULL, exprval, FALSE) );
4521
4522 expr->op = SCIP_EXPR_POLYNOMIAL;
4523 expr->data.data = (void*)polynomialdata;
4524
4525 /* forget child */
4526 SCIPexprFreeDeep(blkmem, &expr->children[0]);
4527 BMSfreeBlockMemoryArray(blkmem, &expr->children, 1);
4528 expr->nchildren = 0;
4529 }
4530
4531 break;
4532 }
4533
4534 case SCIP_EXPR_MIN:
4535 case SCIP_EXPR_MAX:
4536 {
4537 /* check if both arguments are constants */
4538 if( ((expr->children[0]->op == SCIP_EXPR_POLYNOMIAL && SCIPexprGetNChildren(expr->children[0]) == 0) || expr->children[0]->op == SCIP_EXPR_CONST) &&
4539 ((expr->children[1]->op == SCIP_EXPR_POLYNOMIAL && SCIPexprGetNChildren(expr->children[1]) == 0) || expr->children[1]->op == SCIP_EXPR_CONST) )
4540 {
4541 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata;
4542 SCIP_Real exprval;
4543
4544 /* since children have no children and it's polynomial was flattened, it should have no monomials */
4545 assert(expr->children[0]->op != SCIP_EXPR_POLYNOMIAL || SCIPexprGetNMonomials(expr->children[0]) == 0);
4546 assert(expr->children[1]->op != SCIP_EXPR_POLYNOMIAL || SCIPexprGetNMonomials(expr->children[1]) == 0);
4547
4548 /* evaluate expression in constants */
4549 SCIP_CALL( SCIPexprEval(expr, NULL, NULL, &exprval) );
4550
4551 /* create polynomial */
4552 SCIP_CALL( polynomialdataCreate(blkmem, &polynomialdata, 0, NULL, exprval, FALSE) );
4553
4554 expr->op = SCIP_EXPR_POLYNOMIAL;
4555 expr->data.data = (void*)polynomialdata;
4556
4557 /* forget children */
4558 SCIPexprFreeDeep(blkmem, &expr->children[0]);
4559 SCIPexprFreeDeep(blkmem, &expr->children[1]);
4560 BMSfreeBlockMemoryArray(blkmem, &expr->children, 2);
4561 expr->nchildren = 0;
4562 }
4563
4564 break;
4565 }
4566
4567 case SCIP_EXPR_SUM:
4568 case SCIP_EXPR_PRODUCT:
4569 case SCIP_EXPR_LINEAR:
4570 case SCIP_EXPR_QUADRATIC:
4571 case SCIP_EXPR_USER:
4572 break;
4573
4574 case SCIP_EXPR_POLYNOMIAL:
4575 {
4576 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata;
4577 SCIP_EXPRDATA_MONOMIAL* monomial;
4578 SCIP_Bool removechild;
4579 int* childmap;
4580 int childmapsize;
4581 int j;
4582
4583 /* simplify current polynomial */
4584 SCIP_CALL( exprsimplifyRemoveDuplicatePolynomialChildren(blkmem, expr, eps) );
4585 SCIPexprMergeMonomials(blkmem, expr, eps, TRUE);
4586
4587 polynomialdata = (SCIP_EXPRDATA_POLYNOMIAL*)expr->data.data;
4588 assert(polynomialdata != NULL);
4589
4590 SCIPdebugMessage("expand factors in expression ");
4591 SCIPdebug( SCIPexprPrint(expr, messagehdlr, NULL, NULL, NULL, NULL) );
4592 SCIPdebugPrintf("\n");
4593
4594 childmap = NULL;
4595 childmapsize = 0;
4596
4597 /* resolve children that are constants
4598 * we do this first, because it reduces the degree and number of factors in the monomials,
4599 * thereby allowing some expansions of polynomials that may not be possible otherwise, e.g., turning c0*c1 with c0=quadratic and c1=constant into a single monomial
4600 */
4601 for( i = 0; i < expr->nchildren; ++i )
4602 {
4603 if( expr->children[i] == NULL )
4604 continue;
4605
4606 if( SCIPexprGetOperator(expr->children[i]) != SCIP_EXPR_CONST )
4607 continue;
4608
4609 removechild = TRUE; /* we intend to delete children[i] */
4610
4611 if( childmapsize < expr->children[i]->nchildren )
4612 {
4613 int newsize;
4614
4615 newsize = calcGrowSize(expr->children[i]->nchildren);
4616 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &childmap, childmapsize, newsize) );
4617 childmapsize = newsize;
4618 }
4619
4620 /* put constant of child i into every monomial where child i is used */
4621 for( j = 0; j < polynomialdata->nmonomials; ++j )
4622 {
4623 int factorpos;
4624 SCIP_Bool success;
4625
4626 monomial = polynomialdata->monomials[j];
4627 /* if monomial is not sorted, then polynomial should not be sorted either, or have only one monomial */
4628 assert(monomial->sorted || !polynomialdata->sorted || polynomialdata->nmonomials <= 1);
4629
4630 if( SCIPexprFindMonomialFactor(monomial, i, &factorpos) )
4631 {
4632 assert(factorpos >= 0);
4633 assert(factorpos < monomial->nfactors);
4634 /* assert that factors have been merged */
4635 assert(factorpos == 0 || monomial->childidxs[factorpos-1] != i);
4636 assert(factorpos == monomial->nfactors-1 || monomial->childidxs[factorpos+1] != i);
4637
4638 /* SCIPdebugMessage("attempt expanding child %d at monomial %d factor %d\n", i, j, factorpos);
4639 SCIPdebug( SCIPexprPrint(expr, NULL, NULL, NULL) ); SCIPdebugPrintf("\n");
4640 SCIPdebug( SCIPexprPrint(expr->children[i], NULL, NULL, NULL) ); SCIPdebugPrintf("\n"); */
4641
4642 if( !EPSISINT(monomial->exponents[factorpos], 0.0) && SCIPexprGetOpReal(expr->children[i]) < 0.0 ) /*lint !e835*/
4643 {
4644 /* if constant is negative and our exponent is not integer, then cannot do expansion */
4645 SCIPmessagePrintWarning(messagehdlr, "got negative constant %g to the power of a noninteger exponent %g\n",
4646 SCIPexprGetOpReal(expr->children[i]), monomial->exponents[factorpos]);
4647 success = FALSE;
4648 }
4649 else
4650 {
4651 monomial->coef *= pow(SCIPexprGetOpReal(expr->children[i]), monomial->exponents[factorpos]);
4652
4653 /* move last factor to position factorpos */
4654 if( factorpos < monomial->nfactors-1 )
4655 {
4656 monomial->exponents[factorpos] = monomial->exponents[monomial->nfactors-1];
4657 monomial->childidxs[factorpos] = monomial->childidxs[monomial->nfactors-1];
4658 }
4659 --monomial->nfactors;
4660 monomial->sorted = FALSE;
4661 polynomialdata->sorted = FALSE;
4662
4663 success = TRUE;
4664 }
4665
4666 if( !success )
4667 removechild = FALSE;
4668 }
4669 }
4670
4671 /* forget about child i, if it is not used anymore */
4672 if( removechild )
4673 SCIPexprFreeDeep(blkmem, &expr->children[i]);
4674
4675 /* simplify current polynomial again */
4676 SCIPexprMergeMonomials(blkmem, expr, eps, TRUE);
4677 }
4678
4679 /* try to resolve children that are polynomials itself */
4680 for( i = 0; i < expr->nchildren; ++i )
4681 {
4682 if( expr->children[i] == NULL )
4683 continue;
4684
4685 if( SCIPexprGetOperator(expr->children[i]) != SCIP_EXPR_POLYNOMIAL )
4686 continue;
4687
4688 removechild = TRUE; /* we intend to delete children[i] */
4689
4690 if( childmapsize < expr->children[i]->nchildren )
4691 {
4692 int newsize;
4693
4694 newsize = calcGrowSize(expr->children[i]->nchildren);
4695 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &childmap, childmapsize, newsize) );
4696 childmapsize = newsize;
4697 }
4698
4699 /* add children of child i */
4700 SCIP_CALL( exprsimplifyAddChildren(blkmem, expr, expr->children[i]->nchildren, expr->children[i]->children, TRUE, eps, childmap) );
4701
4702 /* put polynomial of child i into every monomial where child i is used */
4703 j = 0;
4704 while( j < polynomialdata->nmonomials )
4705 {
4706 int factorpos;
4707 SCIP_Bool success;
4708
4709 monomial = polynomialdata->monomials[j];
4710 /* if monomial is not sorted, then polynomial should not be sorted either, or have only one monomial */
4711 assert(monomial->sorted || !polynomialdata->sorted || polynomialdata->nmonomials <= 1);
4712
4713 if( SCIPexprFindMonomialFactor(monomial, i, &factorpos) )
4714 {
4715 assert(factorpos >= 0);
4716 assert(factorpos < monomial->nfactors);
4717 /* assert that factors have been merged */
4718 assert(factorpos == 0 || monomial->childidxs[factorpos-1] != i);
4719 assert(factorpos == monomial->nfactors-1 || monomial->childidxs[factorpos+1] != i);
4720
4721 /* SCIPdebugMessage("attempt expanding child %d at monomial %d factor %d\n", i, j, factorpos);
4722 SCIPdebug( SCIPexprPrint(expr, NULL, NULL, NULL) ); SCIPdebugPrintf("\n");
4723 SCIPdebug( SCIPexprPrint(expr->children[i], NULL, NULL, NULL) ); SCIPdebugPrintf("\n"); */
4724
4725 SCIP_CALL( polynomialdataExpandMonomialFactor(blkmem, messagehdlr, polynomialdata, j, factorpos,
4726 (SCIP_EXPRDATA_POLYNOMIAL*)expr->children[i]->data.data, childmap, maxexpansionexponent, &success) );
4727
4728 if( !success )
4729 {
4730 removechild = FALSE;
4731 ++j;
4732 }
4733 }
4734 else
4735 ++j;
4736
4737 /* expansion may remove monomials[j], move a monomial from the end to position j, or add new monomials to the end of polynomialdata
4738 * we thus repeat with index j, if a factor was successfully expanded
4739 */
4740 }
4741
4742 /* forget about child i, if it is not used anymore */
4743 if( removechild )
4744 SCIPexprFreeDeep(blkmem, &expr->children[i]);
4745
4746 /* simplify current polynomial again */
4747 SCIPexprMergeMonomials(blkmem, expr, eps, TRUE);
4748 }
4749
4750 BMSfreeBlockMemoryArrayNull(blkmem, &childmap, childmapsize);
4751
4752 /* free children that are not in use anymore */
4753 SCIP_CALL( exprsimplifyRemovePolynomialUnusedChildren(blkmem, expr) );
4754
4755 /* remove NULLs from children array */
4756 SCIP_CALL( exprsimplifyRemovePolynomialNullChildren(blkmem, expr) );
4757
4758 /* if no children left, then it's a constant polynomial -> change into EXPR_CONST */
4759 if( expr->nchildren == 0 )
4760 {
4761 SCIP_Real val;
4762
4763 /* if no children, then it should also have no monomials */
4764 assert(polynomialdata->nmonomials == 0);
4765
4766 val = polynomialdata->constant;
4767 polynomialdataFree(blkmem, &polynomialdata);
4768
4769 expr->op = SCIP_EXPR_CONST;
4770 expr->data.dbl = val;
4771 }
4772
4773 SCIPdebugMessage("-> ");
4774 SCIPdebug( SCIPexprPrint(expr, messagehdlr, NULL, NULL, NULL, NULL) );
4775 SCIPdebugPrintf("\n");
4776
4777 break;
4778 }
4779
4780 case SCIP_EXPR_LAST:
4781 break;
4782 } /*lint !e788*/
4783
4784 return SCIP_OKAY;
4785 }
4786
4787 /** separates linear monomials from an expression, if it is a polynomial expression
4788 *
4789 * Separates only those linear terms whose variable is not used otherwise in the expression.
4790 */
4791 static
exprsimplifySeparateLinearFromPolynomial(BMS_BLKMEM * blkmem,SCIP_EXPR * expr,SCIP_Real eps,int nvars,int * nlinvars,int * linidxs,SCIP_Real * lincoefs)4792 SCIP_RETCODE exprsimplifySeparateLinearFromPolynomial(
4793 BMS_BLKMEM* blkmem, /**< block memory data structure */
4794 SCIP_EXPR* expr, /**< expression */
4795 SCIP_Real eps, /**< threshold, under which positive values are treat as 0 */
4796 int nvars, /**< number of variables in expression */
4797 int* nlinvars, /**< buffer to store number of linear variables in linear part */
4798 int* linidxs, /**< array to store indices of variables in expression tree which belong to linear part */
4799 SCIP_Real* lincoefs /**< array to store coefficients of linear part */
4800 )
4801 {
4802 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata;
4803 SCIP_EXPRDATA_MONOMIAL* monomial;
4804 int* varsusage;
4805 int* childusage;
4806 int childidx;
4807 int i;
4808 int j;
4809
4810 assert(blkmem != NULL);
4811 assert(expr != NULL);
4812 assert(nlinvars != NULL);
4813 assert(linidxs != NULL);
4814 assert(lincoefs != NULL);
4815
4816 *nlinvars = 0;
4817
4818 if( SCIPexprGetOperator(expr) != SCIP_EXPR_POLYNOMIAL )
4819 return SCIP_OKAY;
4820
4821 if( SCIPexprGetNChildren(expr) == 0 )
4822 return SCIP_OKAY;
4823
4824 polynomialdata = (SCIP_EXPRDATA_POLYNOMIAL*)expr->data.data;
4825 assert(polynomialdata != NULL);
4826
4827 /* get variable usage */
4828 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &varsusage, nvars) );
4829 BMSclearMemoryArray(varsusage, nvars); /*lint !e644*/
4830 SCIPexprGetVarsUsage(expr, varsusage);
4831
4832 /* get child usage: how often each child is used in the polynomial */
4833 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &childusage, expr->nchildren) );
4834 BMSclearMemoryArray(childusage, expr->nchildren); /*lint !e644*/
4835 for( i = 0; i < polynomialdata->nmonomials; ++i )
4836 {
4837 monomial = polynomialdata->monomials[i];
4838 assert(monomial != NULL);
4839 for( j = 0; j < monomial->nfactors; ++j )
4840 {
4841 assert(monomial->childidxs[j] >= 0);
4842 assert(monomial->childidxs[j] < expr->nchildren);
4843 ++childusage[monomial->childidxs[j]];
4844 }
4845 }
4846
4847 /* move linear monomials out of polynomial */
4848 for( i = 0; i < polynomialdata->nmonomials; ++i )
4849 {
4850 monomial = polynomialdata->monomials[i];
4851 assert(monomial != NULL);
4852 if( monomial->nfactors != 1 )
4853 continue;
4854 if( monomial->exponents[0] != 1.0 )
4855 continue;
4856 childidx = monomial->childidxs[0];
4857 if( SCIPexprGetOperator(expr->children[childidx]) != SCIP_EXPR_VARIDX )
4858 continue;
4859
4860 /* we are at a linear monomial in a variable */
4861 assert(SCIPexprGetOpIndex(expr->children[childidx]) < nvars);
4862 if( childusage[childidx] == 1 && varsusage[SCIPexprGetOpIndex(expr->children[childidx])] == 1 )
4863 {
4864 /* if the child expression is not used in another monomial (which would due to merging be not linear)
4865 * and if the variable is not used somewhere else in the tree,
4866 * then move this monomial into linear part and free child
4867 */
4868 linidxs[*nlinvars] = SCIPexprGetOpIndex(expr->children[childidx]);
4869 lincoefs[*nlinvars] = monomial->coef;
4870 ++*nlinvars;
4871
4872 SCIPexprFreeDeep(blkmem, &expr->children[childidx]);
4873 monomial->coef = 0.0;
4874 monomial->nfactors = 0;
4875 }
4876 }
4877
4878 BMSfreeBlockMemoryArray(blkmem, &varsusage, nvars);
4879 BMSfreeBlockMemoryArray(blkmem, &childusage, expr->nchildren);
4880
4881 if( *nlinvars > 0 )
4882 {
4883 /* if we did something, cleanup polynomial (e.g., remove monomials with coefficient 0.0) */
4884 polynomialdataMergeMonomials(blkmem, polynomialdata, eps, FALSE);
4885 SCIP_CALL( exprsimplifyRemovePolynomialNullChildren(blkmem, expr) );
4886 }
4887
4888 return SCIP_OKAY;
4889 }
4890
4891 /** converts polynomial expressions back into simpler expressions, where possible */
4892 static
exprsimplifyUnconvertPolynomials(BMS_BLKMEM * blkmem,SCIP_EXPR * expr)4893 SCIP_RETCODE exprsimplifyUnconvertPolynomials(
4894 BMS_BLKMEM* blkmem, /**< block memory data structure */
4895 SCIP_EXPR* expr /**< expression to convert back */
4896 )
4897 {
4898 int i;
4899
4900 assert(blkmem != NULL);
4901 assert(expr != NULL);
4902
4903 for( i = 0; i < expr->nchildren; ++i )
4904 {
4905 SCIP_CALL( exprsimplifyUnconvertPolynomials(blkmem, expr->children[i]) );
4906 }
4907
4908 if( expr->op != SCIP_EXPR_POLYNOMIAL )
4909 return SCIP_OKAY;
4910
4911 SCIP_CALL( exprUnconvertPolynomial(blkmem, &expr->op, &expr->data, expr->nchildren, (void**)expr->children) );
4912
4913 return SCIP_OKAY;
4914 }
4915
4916 static
SCIP_DECL_HASHGETKEY(exprparseVarTableGetKey)4917 SCIP_DECL_HASHGETKEY( exprparseVarTableGetKey )
4918 { /*lint --e{715}*/
4919 return (void*)((char*)elem + sizeof(int));
4920 }
4921
4922 /** parses a variable name from a string and creates corresponding expression
4923 *
4924 * Creates a new variable index if variable not seen before, updates varnames and vartable structures.
4925 */
4926 static
exprparseReadVariable(BMS_BLKMEM * blkmem,const char ** str,SCIP_EXPR ** expr,int * nvars,int ** varnames,int * varnameslength,SCIP_HASHTABLE * vartable,SCIP_Real coefficient,const char * varnameendptr)4927 SCIP_RETCODE exprparseReadVariable(
4928 BMS_BLKMEM* blkmem, /**< block memory data structure */
4929 const char** str, /**< pointer to the string to be parsed */
4930 SCIP_EXPR** expr, /**< buffer to store pointer to created expression */
4931 int* nvars, /**< running number of encountered variables so far */
4932 int** varnames, /**< pointer to buffer to store new variable names */
4933 int* varnameslength, /**< pointer to length of the varnames buffer array */
4934 SCIP_HASHTABLE* vartable, /**< hash table for variable names and corresponding expression index */
4935 SCIP_Real coefficient, /**< coefficient to be used when creating the expression */
4936 const char* varnameendptr /**< if a \<varname\> should be parsed, set this to NULL. Then, str points to the '<'
4937 else, str should point to the first letter of the varname, and varnameendptr should
4938 point one char behind the last char of the variable name */
4939 )
4940 {
4941 int namelength;
4942 int varidx;
4943 char varname[SCIP_MAXSTRLEN];
4944 void* element;
4945
4946 assert(blkmem != NULL);
4947 assert(str != NULL);
4948 assert(expr != NULL);
4949 assert(nvars != NULL);
4950 assert(varnames != NULL);
4951 assert(vartable != NULL);
4952
4953 if( varnameendptr == NULL )
4954 {
4955 ++*str;
4956 varnameendptr = *str;
4957 while( varnameendptr[0] != '>' )
4958 ++varnameendptr;
4959 }
4960
4961 namelength = varnameendptr - *str; /*lint !e712*/
4962 if( namelength >= SCIP_MAXSTRLEN )
4963 {
4964 SCIPerrorMessage("Variable name %.*s is too long for buffer in exprparseReadVariable.\n", namelength, *str);
4965 return SCIP_READERROR;
4966 }
4967
4968 memcpy(varname, *str, namelength * sizeof(char));
4969 varname[namelength] = '\0';
4970
4971 element = SCIPhashtableRetrieve(vartable, varname);
4972 if( element != NULL )
4973 {
4974 /* variable is old friend */
4975 assert(strcmp((char*)element + sizeof(int), varname) == 0);
4976
4977 varidx = *(int*)element;
4978 }
4979 else
4980 {
4981 /* variable is new */
4982 varidx = *nvars;
4983
4984 (*varnameslength) -= (int)(1 + (strlen(varname) + 1) / sizeof(int) + 1);
4985 if( *varnameslength < 0 )
4986 {
4987 SCIPerrorMessage("Buffer in exprparseReadVariable is too short for varaible name %.*s.\n", namelength, *str);
4988 return SCIP_READERROR;
4989 }
4990
4991 /* store index of variable and variable name in varnames buffer */
4992 **varnames = varidx;
4993 (void) SCIPstrncpy((char*)(*varnames + 1), varname, (int)strlen(varname)+1);
4994
4995 /* insert variable into hashtable */
4996 SCIP_CALL( SCIPhashtableInsert(vartable, (void*)*varnames) );
4997
4998 ++*nvars;
4999 *varnames += 1 + (strlen(varname) + 1) / sizeof(int) + 1;
5000 }
5001
5002 /* create VARIDX expression, put into LINEAR expression if we have coefficient != 1 */
5003 SCIP_CALL( SCIPexprCreate(blkmem, expr, SCIP_EXPR_VARIDX, varidx) ); /*lint !e613*/
5004 if( coefficient != 1.0 )
5005 {
5006 SCIP_CALL( SCIPexprCreateLinear(blkmem, expr, 1, expr, &coefficient, 0.0) );
5007 }
5008
5009 /* Move pointer to char behind end of variable */
5010 *str = varnameendptr + 1;
5011
5012 /* consprint sometimes prints a variable type identifier which we don't need */
5013 if( (*str)[0] == '[' && (*str)[2] == ']' &&
5014 ((*str)[1] == SCIP_VARTYPE_BINARY_CHAR ||
5015 (*str)[1] == SCIP_VARTYPE_INTEGER_CHAR ||
5016 (*str)[1] == SCIP_VARTYPE_IMPLINT_CHAR ||
5017 (*str)[1] == SCIP_VARTYPE_CONTINUOUS_CHAR ) )
5018 *str += 3;
5019
5020 return SCIP_OKAY;
5021 }
5022
5023 /** if str[0] points to an opening parenthesis, this function sets endptr to point to the matching closing bracket in str
5024 *
5025 * Searches for at most length characters.
5026 */
5027 static
exprparseFindClosingParenthesis(const char * str,const char ** endptr,int length)5028 SCIP_RETCODE exprparseFindClosingParenthesis(
5029 const char* str, /**< pointer to the string to be parsed */
5030 const char** endptr, /**< pointer to point to the closing parenthesis */
5031 int length /**< length of the string to be parsed */
5032 )
5033 {
5034 int nopenbrackets;
5035
5036 assert(str[0] == '(');
5037
5038 *endptr = str;
5039
5040 /* find the end of this expression */
5041 nopenbrackets = 0;
5042 while( (*endptr - str ) < length && !(nopenbrackets == 1 && *endptr[0] == ')') )
5043 {
5044 if( *endptr[0] == '(')
5045 ++nopenbrackets;
5046 if( *endptr[0] == ')')
5047 --nopenbrackets;
5048 ++*endptr;
5049 }
5050
5051 if( *endptr[0] != ')' )
5052 {
5053 SCIPerrorMessage("unable to find closing parenthesis in unbalanced expression %.*s\n", length, str);
5054 return SCIP_READERROR;
5055 }
5056
5057 return SCIP_OKAY;
5058 }
5059
5060 /** this function sets endptr to point to the next separating comma in str
5061 *
5062 * That is, for a given string like "x+f(x,y),z", endptr will point to the comma before "z"
5063 *
5064 * Searches for at most length characters.
5065 */
5066 static
exprparseFindSeparatingComma(const char * str,const char ** endptr,int length)5067 SCIP_RETCODE exprparseFindSeparatingComma(
5068 const char* str, /**< pointer to the string to be parsed */
5069 const char** endptr, /**< pointer to point to the comma */
5070 int length /**< length of the string to be parsed */
5071 )
5072 {
5073 int nopenbrackets;
5074
5075 *endptr = str;
5076
5077 /* find a comma without open brackets */
5078 nopenbrackets = 0;
5079 while( (*endptr - str ) < length && !(nopenbrackets == 0 && *endptr[0] == ',') )
5080 {
5081 if( *endptr[0] == '(')
5082 ++nopenbrackets;
5083 if( *endptr[0] == ')')
5084 --nopenbrackets;
5085 ++*endptr;
5086 }
5087
5088 if( *endptr[0] != ',' )
5089 {
5090 SCIPerrorMessage("unable to find separating comma in unbalanced expression %.*s\n", length, str);
5091 return SCIP_READERROR;
5092 }
5093
5094 return SCIP_OKAY;
5095 }
5096
5097 /** parses an expression from a string */
5098 static
exprParse(BMS_BLKMEM * blkmem,SCIP_MESSAGEHDLR * messagehdlr,SCIP_EXPR ** expr,const char * str,int length,const char * lastchar,int * nvars,int ** varnames,int * varnameslength,SCIP_HASHTABLE * vartable,int recursiondepth)5099 SCIP_RETCODE exprParse(
5100 BMS_BLKMEM* blkmem, /**< block memory data structure */
5101 SCIP_MESSAGEHDLR* messagehdlr, /**< message handler */
5102 SCIP_EXPR** expr, /**< buffer to store pointer to created expression */
5103 const char* str, /**< pointer to the string to be parsed */
5104 int length, /**< length of the string to be parsed */
5105 const char* lastchar, /**< pointer to the last char of str that should be parsed */
5106 int* nvars, /**< running number of encountered variables so far */
5107 int** varnames, /**< pointer to buffer to store new variable names */
5108 int* varnameslength, /**< pointer to length of the varnames buffer array */
5109 SCIP_HASHTABLE* vartable, /**< hash table for variable names and corresponding expression index */
5110 int recursiondepth /**< current recursion depth */
5111 )
5112 { /*lint --e{712,747}*/
5113 SCIP_EXPR* arg1;
5114 SCIP_EXPR* arg2;
5115 const char* subexpptr;
5116 const char* subexpendptr;
5117 const char* strstart;
5118 const char* endptr;
5119 char* nonconstendptr;
5120 SCIP_Real number;
5121 int subexplength;
5122 int nopenbrackets;
5123
5124 assert(blkmem != NULL);
5125 assert(expr != NULL);
5126 assert(str != NULL);
5127 assert(lastchar >= str);
5128 assert(nvars != NULL);
5129 assert(varnames != NULL);
5130 assert(vartable != NULL);
5131
5132 assert(recursiondepth < 100);
5133
5134 strstart = str; /* might be needed for error message... */
5135
5136 SCIPdebugMessage("exprParse (%i): parsing %.*s\n", recursiondepth, (int) (lastchar-str + 1), str);
5137
5138 /* ignore whitespace */
5139 while( isspace((unsigned char)*str) )
5140 ++str;
5141
5142 /* look for a sum or difference not contained in brackets */
5143 subexpptr = str;
5144 nopenbrackets = 0;
5145
5146 /* find the end of this expression
5147 * a '+' right at the beginning indicates a coefficient, not treated here, or a summation
5148 * a '+' or '-' that follows an 'e' or 'E' indicates that we are in the middle of a number, so it doesn't separate terms
5149 */
5150 while( subexpptr != lastchar && !(nopenbrackets == 0 && (subexpptr[0] == '+' || subexpptr[0] == '-') && subexpptr != str && subexpptr[-1] != 'e' && subexpptr[-1] != 'E') )
5151 {
5152 if( subexpptr[0] == '(')
5153 ++nopenbrackets;
5154 if( subexpptr[0] == ')')
5155 --nopenbrackets;
5156 ++subexpptr;
5157 }
5158
5159 if( subexpptr != lastchar )
5160 {
5161 SCIP_CALL( exprParse(blkmem, messagehdlr, &arg1, str, (int) ((subexpptr - 1) - str + 1), subexpptr - 1, nvars,
5162 varnames, varnameslength, vartable, recursiondepth + 1) );
5163
5164 if( subexpptr[0] == '+' )
5165 ++subexpptr;
5166 SCIP_CALL( exprParse(blkmem, messagehdlr, &arg2, subexpptr , (int) (lastchar - (subexpptr ) + 1), lastchar, nvars,
5167 varnames, varnameslength, vartable, recursiondepth + 1) );
5168
5169 /* make new expression from two arguments
5170 * we always use add, because we leave the operator between the found expressions in the second argument
5171 * this way, we do not have to worry about ''minus brackets'' in the case of more then two summands:
5172 * a - b - c = a + (-b -c)
5173 */
5174 SCIP_CALL( SCIPexprAdd(blkmem, expr, 1.0, arg1, 1.0, arg2, 0.0) );
5175
5176 SCIPdebugMessage("exprParse (%i): returns expression ", recursiondepth);
5177 SCIPdebug( SCIPexprPrint(*expr, messagehdlr, NULL, NULL, NULL, NULL) );
5178 SCIPdebug( SCIPmessagePrintInfo(messagehdlr, "\n") );
5179
5180 return SCIP_OKAY;
5181 }
5182
5183 /* check for a bracketed subexpression */
5184 if( str[0] == '(' )
5185 {
5186 nopenbrackets = 0;
5187
5188 subexplength = -1; /* we do not want the closing bracket in the string */
5189 subexpptr = str + 1; /* leave out opening bracket */
5190
5191 /* find the end of this expression */
5192 while( subexplength < length && !(nopenbrackets == 1 && str[0] == ')') )
5193 {
5194 if( str[0] == '(' )
5195 ++nopenbrackets;
5196 if( str[0] == ')' )
5197 --nopenbrackets;
5198 ++str;
5199 ++subexplength;
5200 }
5201 subexpendptr = str - 1; /* leave out closing bracket */
5202
5203 SCIP_CALL( exprParse(blkmem, messagehdlr, expr, subexpptr, subexplength, subexpendptr, nvars, varnames,
5204 varnameslength, vartable, recursiondepth + 1) );
5205 ++str;
5206 }
5207 else if( isdigit((unsigned char)str[0]) || ((str[0] == '-' || str[0] == '+')
5208 && (isdigit((unsigned char)str[1]) || str[1] == ' ')) )
5209 {
5210 /* check if there is a lonely minus coming, indicating a -1.0 */
5211 if( str[0] == '-' && str[1] == ' ' )
5212 {
5213 number = -1.0;
5214 nonconstendptr = (char*) str + 1;
5215 }
5216 /* check if there is a number coming */
5217 else if( !SCIPstrToRealValue(str, &number, &nonconstendptr) )
5218 {
5219 SCIPerrorMessage("error parsing number from <%s>\n", str);
5220 return SCIP_READERROR;
5221 }
5222 str = nonconstendptr;
5223
5224 /* ignore whitespace */
5225 while( isspace((unsigned char)*str) && str != lastchar )
5226 ++str;
5227
5228 if( str[0] != '*' && str[0] != '/' && str[0] != '+' && str[0] != '-' && str[0] != '^' )
5229 {
5230 if( str < lastchar )
5231 {
5232 SCIP_CALL( exprParse(blkmem, messagehdlr, expr, str, (int)(lastchar - str) + 1, lastchar, nvars, varnames,
5233 varnameslength, vartable, recursiondepth + 1) );
5234 SCIP_CALL( SCIPexprMulConstant(blkmem, expr, *expr, number) );
5235 }
5236 else
5237 {
5238 SCIP_CALL( SCIPexprCreate(blkmem, expr, SCIP_EXPR_CONST, number) );
5239 }
5240 str = lastchar + 1;
5241 }
5242 else
5243 {
5244 SCIP_CALL( SCIPexprCreate(blkmem, expr, SCIP_EXPR_CONST, number) );
5245 }
5246 }
5247 else if( str[0] == '<' )
5248 {
5249 /* check if expressions begins with a variable */
5250 SCIP_CALL( exprparseReadVariable(blkmem, &str, expr, nvars, varnames, varnameslength, vartable, 1.0, NULL) );
5251 }
5252 /* four character operators */
5253 else if( strncmp(str, "sqrt", 4) == 0 )
5254 {
5255 str += 4;
5256 SCIP_CALL( exprparseFindClosingParenthesis(str, &endptr, length) );
5257 SCIP_CALL( exprParse(blkmem, messagehdlr, &arg1, str + 1, endptr - str - 1, endptr -1, nvars, varnames,
5258 varnameslength, vartable, recursiondepth + 1) );
5259 str = endptr + 1;
5260
5261 SCIP_CALL( SCIPexprCreate(blkmem, expr, SCIP_EXPR_SQRT, arg1) );
5262 }
5263 /* three character operators with 1 argument */
5264 else if(
5265 strncmp(str, "abs", 3) == 0 ||
5266 strncmp(str, "cos", 3) == 0 ||
5267 strncmp(str, "exp", 3) == 0 ||
5268 strncmp(str, "log", 3) == 0 ||
5269 strncmp(str, "sin", 3) == 0 ||
5270 strncmp(str, "sqr", 3) == 0 ||
5271 strncmp(str, "tan", 3) == 0 )
5272 {
5273 const char* opname = str;
5274
5275 str += 3;
5276 SCIP_CALL( exprparseFindClosingParenthesis(str, &endptr, length) );
5277 SCIP_CALL( exprParse(blkmem, messagehdlr, &arg1, str + 1, endptr - str - 1, endptr -1, nvars, varnames,
5278 varnameslength, vartable, recursiondepth + 1) );
5279 str = endptr + 1;
5280
5281 if( strncmp(opname, "abs", 3) == 0 )
5282 {
5283 SCIP_CALL( SCIPexprCreate(blkmem, expr, SCIP_EXPR_ABS, arg1) );
5284 }
5285 else if( strncmp(opname, "cos", 3) == 0 )
5286 {
5287 SCIP_CALL( SCIPexprCreate(blkmem, expr, SCIP_EXPR_COS, arg1) );
5288 }
5289 else if( strncmp(opname, "exp", 3) == 0 )
5290 {
5291 SCIP_CALL( SCIPexprCreate(blkmem, expr, SCIP_EXPR_EXP, arg1) );
5292 }
5293 else if( strncmp(opname, "log", 3) == 0 )
5294 {
5295 SCIP_CALL( SCIPexprCreate(blkmem, expr, SCIP_EXPR_LOG, arg1) );
5296 }
5297 else if( strncmp(opname, "sin", 3) == 0 )
5298 {
5299 SCIP_CALL( SCIPexprCreate(blkmem, expr, SCIP_EXPR_SIN, arg1) );
5300 }
5301 else if( strncmp(opname, "sqr", 3) == 0 )
5302 {
5303 SCIP_CALL( SCIPexprCreate(blkmem, expr, SCIP_EXPR_SQUARE, arg1) );
5304 }
5305 else
5306 {
5307 assert(strncmp(opname, "tan", 3) == 0);
5308 SCIP_CALL( SCIPexprCreate(blkmem, expr, SCIP_EXPR_TAN, arg1) );
5309 }
5310 }
5311 /* three character operators with 2 arguments */
5312 else if(
5313 strncmp(str, "max", 3) == 0 ||
5314 strncmp(str, "min", 3) == 0 )
5315 {
5316 /* we have a string of the form "min(...,...)" or "max(...,...)"
5317 * first find the closing parenthesis, then the comma
5318 */
5319 const char* comma;
5320 SCIP_EXPROP op;
5321
5322 op = (str[1] == 'a' ? SCIP_EXPR_MAX : SCIP_EXPR_MIN);
5323
5324 str += 3;
5325 SCIP_CALL( exprparseFindClosingParenthesis(str, &endptr, length) );
5326
5327 SCIP_CALL( exprparseFindSeparatingComma(str+1, &comma, endptr - str - 1) );
5328
5329 /* parse first argument [str+1..comma-1] */
5330 SCIP_CALL( exprParse(blkmem, messagehdlr, &arg1, str + 1, comma - str - 1, comma - 1, nvars, varnames,
5331 varnameslength, vartable, recursiondepth + 1) );
5332
5333 /* parse second argument [comma+1..endptr] */
5334 ++comma;
5335 while( comma < endptr && *comma == ' ' )
5336 ++comma;
5337
5338 SCIP_CALL( exprParse(blkmem, messagehdlr, &arg2, comma, endptr - comma, endptr - 1, nvars, varnames,
5339 varnameslength, vartable, recursiondepth + 1) );
5340
5341 SCIP_CALL( SCIPexprCreate(blkmem, expr, op, arg1, arg2) );
5342
5343 str = endptr + 1;
5344 }
5345 else if( strncmp(str, "power", 5) == 0 )
5346 {
5347 /* we have a string of the form "power(...,integer)" (thus, intpower)
5348 * first find the closing parenthesis, then the comma
5349 */
5350 const char* comma;
5351 int exponent;
5352
5353 str += 5;
5354 SCIP_CALL( exprparseFindClosingParenthesis(str, &endptr, length) );
5355
5356 SCIP_CALL( exprparseFindSeparatingComma(str+1, &comma, endptr - str - 1) );
5357
5358 /* parse first argument [str+1..comma-1] */
5359 SCIP_CALL( exprParse(blkmem, messagehdlr, &arg1, str + 1, comma - str - 1, comma - 1, nvars, varnames,
5360 varnameslength, vartable, recursiondepth + 1) );
5361
5362 ++comma;
5363 /* parse second argument [comma, endptr-1]: it needs to be an integer */
5364 while( comma < endptr && *comma == ' ' )
5365 ++comma;
5366 if( !isdigit((unsigned char)comma[0]) && !((comma[0] == '-' || comma[0] == '+') && isdigit((unsigned char)comma[1])) )
5367 {
5368 SCIPerrorMessage("error parsing integer exponent from <%s>\n", comma);
5369 }
5370 if( !SCIPstrToIntValue(comma, &exponent, &nonconstendptr) )
5371 {
5372 SCIPerrorMessage("error parsing integer from <%s>\n", comma);
5373 return SCIP_READERROR;
5374 }
5375
5376 SCIP_CALL( SCIPexprCreate(blkmem, expr, SCIP_EXPR_INTPOWER, arg1, exponent) );
5377
5378 str = endptr + 1;
5379 }
5380 else if( strncmp(str, "realpower", 9) == 0 || strncmp(str, "signpower", 9) == 0 )
5381 {
5382 /* we have a string of the form "realpower(...,double)" or "signpower(...,double)"
5383 * first find the closing parenthesis, then the comma
5384 */
5385 const char* opname = str;
5386 const char* comma;
5387
5388 str += 9;
5389 SCIP_CALL( exprparseFindClosingParenthesis(str, &endptr, length) );
5390
5391 SCIP_CALL( exprparseFindSeparatingComma(str+1, &comma, endptr - str - 1) );
5392
5393 /* parse first argument [str+1..comma-1] */
5394 SCIP_CALL( exprParse(blkmem, messagehdlr, &arg1, str + 1, comma - str - 1, comma - 1, nvars, varnames,
5395 varnameslength, vartable, recursiondepth + 1) );
5396
5397 ++comma;
5398 /* parse second argument [comma, endptr-1]: it needs to be an number */
5399 while( comma < endptr && *comma == ' ' )
5400 ++comma;
5401 if( !isdigit((unsigned char)comma[0]) && !((comma[0] == '-' || comma[0] == '+') && isdigit((unsigned char)comma[1])) )
5402 {
5403 SCIPerrorMessage("error parsing number exponent from <%s>\n", comma);
5404 }
5405 if( !SCIPstrToRealValue(comma, &number, &nonconstendptr) )
5406 {
5407 SCIPerrorMessage("error parsing number from <%s>\n", comma);
5408 return SCIP_READERROR;
5409 }
5410
5411 if( strncmp(opname, "realpower", 9) == 0 )
5412 {
5413 SCIP_CALL( SCIPexprCreate(blkmem, expr, SCIP_EXPR_REALPOWER, arg1, number) );
5414 }
5415 else
5416 {
5417 assert(strncmp(opname, "signpower", 9) == 0);
5418 SCIP_CALL( SCIPexprCreate(blkmem, expr, SCIP_EXPR_SIGNPOWER, arg1, number) );
5419 }
5420
5421 str = endptr + 1;
5422 }
5423 else if( isalpha(*str) || *str == '_' || *str == '#' )
5424 {
5425 /* check for a variable, that was not recognized earlier because somebody omitted the '<' and '>' we need for
5426 * SCIPparseVarName, making everyones life harder;
5427 * we allow only variable names starting with a character or underscore here
5428 */
5429 const char* varnamestartptr = str;
5430
5431 /* allow only variable names containing characters, digits, hash marks, and underscores here */
5432 while( isalnum(str[0]) || str[0] == '_' || str[0] == '#' )
5433 ++str;
5434
5435 SCIP_CALL( exprparseReadVariable(blkmem, &varnamestartptr, expr, nvars, varnames, varnameslength,
5436 vartable, 1.0, str) );
5437 }
5438 else
5439 {
5440 SCIPerrorMessage("parsing of invalid expression %.*s.\n", (int) (lastchar - str + 1), str);
5441 return SCIP_READERROR;
5442 }
5443
5444 /* if we are one char behind lastchar, we are done */
5445 if( str == lastchar + 1)
5446 {
5447 SCIPdebugMessage("exprParse (%i): returns expression ", recursiondepth);
5448 SCIPdebug( SCIPexprPrint(*expr, messagehdlr, NULL, NULL, NULL, NULL) );
5449 SCIPdebug( SCIPmessagePrintInfo(messagehdlr, "\n") );
5450
5451 return SCIP_OKAY;
5452 }
5453
5454 /* check if we are still in bounds */
5455 if( str > lastchar + 1)
5456 {
5457 SCIPerrorMessage("error finding first expression in \"%.*s\" took us outside of given subexpression length\n", length, strstart);
5458 return SCIP_READERROR;
5459 }
5460
5461 /* ignore whitespace */
5462 while( isspace((unsigned char)*str) && str != lastchar + 1 )
5463 ++str;
5464
5465 /* maybe now we're done? */
5466 if( str >= lastchar + 1)
5467 {
5468 SCIPdebugMessage("exprParse (%i): returns expression ", recursiondepth);
5469 SCIPdebug( SCIPexprPrint(*expr, messagehdlr, NULL, NULL, NULL, NULL) );
5470 SCIPdebug( SCIPmessagePrintInfo(messagehdlr, "\n") );
5471
5472 return SCIP_OKAY;
5473 }
5474
5475 if( str[0] == '^' )
5476 {
5477 /* a '^' behind the found expression indicates a power */
5478 SCIP_Real constant;
5479
5480 arg1 = *expr;
5481 ++str;
5482 while( isspace((unsigned char)*str) && str != lastchar + 1 )
5483 ++str;
5484
5485 if( isdigit((unsigned char)str[0]) || ((str[0] == '-' || str[0] == '+') && isdigit((unsigned char)str[1])) )
5486 {
5487 /* there is a number coming */
5488 if( !SCIPstrToRealValue(str, &number, &nonconstendptr) )
5489 {
5490 SCIPerrorMessage("error parsing number from <%s>\n", str);
5491 return SCIP_READERROR;
5492 }
5493
5494 SCIP_CALL( SCIPexprCreate(blkmem, &arg2, SCIP_EXPR_CONST, number) );
5495 str = nonconstendptr;
5496
5497 constant = SCIPexprGetOpReal(arg2);
5498 SCIPexprFreeDeep(blkmem, &arg2);
5499
5500 /* expr^number is intpower or realpower */
5501 if( EPSISINT(constant, 0.0) ) /*lint !e835*/
5502 {
5503 SCIP_CALL( SCIPexprCreate(blkmem, expr, SCIP_EXPR_INTPOWER, arg1, (int)constant) );
5504 }
5505 else
5506 {
5507 SCIP_CALL( SCIPexprCreate(blkmem, expr, SCIP_EXPR_REALPOWER, arg1, constant) );
5508 }
5509 }
5510 else if( str[0] == '(' )
5511 {
5512 /* we use exprParse to evaluate the exponent */
5513
5514 SCIP_CALL( exprparseFindClosingParenthesis(str, &endptr, length) );
5515 SCIP_CALL( exprParse(blkmem, messagehdlr, &arg2, str + 1, endptr - str - 1, endptr -1, nvars, varnames,
5516 varnameslength, vartable, recursiondepth + 1) );
5517
5518 if( SCIPexprGetOperator(arg2) != SCIP_EXPR_CONST )
5519 {
5520 /* reformulate arg1^arg2 as exp(arg2 * log(arg1)) */
5521 SCIP_CALL( SCIPexprCreate(blkmem, expr, SCIP_EXPR_LOG, arg1) );
5522 SCIP_CALL( SCIPexprCreate(blkmem, expr, SCIP_EXPR_MUL, *expr, arg2) );
5523 SCIP_CALL( SCIPexprCreate(blkmem, expr, SCIP_EXPR_EXP, *expr) );
5524 }
5525 else
5526 {
5527 /* expr^number is intpower or realpower */
5528 constant = SCIPexprGetOpReal(arg2);
5529 SCIPexprFreeDeep(blkmem, &arg2);
5530 if( EPSISINT(constant, 0.0) ) /*lint !e835*/
5531 {
5532 SCIP_CALL( SCIPexprCreate(blkmem, expr, SCIP_EXPR_INTPOWER, arg1, (int)constant) );
5533 }
5534 else
5535 {
5536 SCIP_CALL( SCIPexprCreate(blkmem, expr, SCIP_EXPR_REALPOWER, arg1, constant) );
5537 }
5538 }
5539 str = endptr + 1;
5540 }
5541 else
5542 {
5543 SCIPerrorMessage("unexpected string following ^ in %.*s\n", length, str);
5544 return SCIP_READERROR;
5545 }
5546
5547 /* ignore whitespace */
5548 while( isspace((unsigned char)*str) && str != lastchar + 1 )
5549 ++str;
5550 }
5551
5552 /* check for a two argument operator that is not a multiplication */
5553 if( str <= lastchar && (str[0] == '+' || str[0] == '-' || str[0] == '/') )
5554 {
5555 char op;
5556
5557 op = str[0];
5558 arg1 = *expr;
5559
5560 /* step forward over the operator to go to the beginning of the second argument */
5561 ++str;
5562
5563 SCIP_CALL( exprParse(blkmem, messagehdlr, &arg2, str, (int) (lastchar - str + 1), lastchar, nvars, varnames,
5564 varnameslength, vartable, recursiondepth + 1) );
5565 str = lastchar + 1;
5566
5567 /* make new expression from two arguments */
5568 if( op == '+')
5569 {
5570 SCIP_CALL( SCIPexprAdd(blkmem, expr, 1.0, arg1, 1.0, arg2, 0.0) );
5571 }
5572 else if( op == '-')
5573 {
5574 SCIP_CALL( SCIPexprAdd(blkmem, expr, 1.0, arg1, -1.0, arg2, 0.0) );
5575 }
5576 else if( op == '*' )
5577 {
5578 if( SCIPexprGetOperator(arg1) == SCIP_EXPR_CONST )
5579 {
5580 SCIP_CALL( SCIPexprMulConstant(blkmem, expr, arg2, SCIPexprGetOpReal(arg1)) );
5581 }
5582 else if( SCIPexprGetOperator(arg2) == SCIP_EXPR_CONST )
5583 {
5584 SCIP_CALL( SCIPexprMulConstant(blkmem, expr, arg1, SCIPexprGetOpReal(arg2)) );
5585 }
5586 else
5587 {
5588 SCIP_CALL( SCIPexprCreate(blkmem, expr, SCIP_EXPR_MUL, arg1, arg2) );
5589 }
5590 }
5591 else
5592 {
5593 assert(op == '/');
5594
5595 if( SCIPexprGetOperator(arg2) == SCIP_EXPR_CONST )
5596 {
5597 SCIP_CALL( SCIPexprMulConstant(blkmem, expr, arg1, 1.0 / SCIPexprGetOpReal(arg2)) );
5598 SCIPexprFreeShallow(blkmem, &arg2);
5599 }
5600 else
5601 {
5602 SCIP_CALL( SCIPexprCreate(blkmem, expr, SCIP_EXPR_DIV, arg1, arg2) );
5603 }
5604 }
5605 }
5606
5607 /* ignore whitespace */
5608 while( isspace((unsigned char)*str) )
5609 ++str;
5610
5611 /* we are either done or we have a multiplication? */
5612 if( str >= lastchar + 1 )
5613 {
5614 SCIPdebugMessage("exprParse (%i): returns expression ", recursiondepth);
5615 SCIPdebug( SCIPexprPrint(*expr, messagehdlr, NULL, NULL, NULL, NULL) );
5616 SCIPdebug( SCIPmessagePrintInfo(messagehdlr, "\n") );
5617
5618 return SCIP_OKAY;
5619 }
5620
5621 /* if there is a part of the string left to be parsed, we assume that this as a multiplication */
5622 arg1 = *expr;
5623
5624 /* stepping over multiplication operator if needed */
5625 if( str[0] == '*' )
5626 {
5627 ++str;
5628 }
5629 else if( str[0] != '(' )
5630 {
5631 SCIPdebugMessage("No operator found, assuming a multiplication before %.*s\n", (int) (lastchar - str + 1), str);
5632 }
5633
5634 SCIP_CALL( exprParse(blkmem, messagehdlr, &arg2, str, (int) (lastchar - str + 1), lastchar, nvars, varnames,
5635 varnameslength, vartable, recursiondepth + 1) );
5636
5637 if( SCIPexprGetOperator(arg1) == SCIP_EXPR_CONST )
5638 {
5639 SCIP_CALL( SCIPexprMulConstant(blkmem, expr, arg2, SCIPexprGetOpReal(arg1)) );
5640 SCIPexprFreeDeep(blkmem, &arg1);
5641 }
5642 else if( SCIPexprGetOperator(arg2) == SCIP_EXPR_CONST )
5643 {
5644 SCIP_CALL( SCIPexprMulConstant(blkmem, expr, arg1, SCIPexprGetOpReal(arg2)) );
5645 SCIPexprFreeDeep(blkmem, &arg2);
5646 }
5647 else
5648 {
5649 SCIP_CALL( SCIPexprCreate(blkmem, expr, SCIP_EXPR_MUL, arg1, arg2) );
5650 }
5651
5652 SCIPdebugMessage("exprParse (%i): returns expression ", recursiondepth);
5653 SCIPdebug( SCIPexprPrint(*expr, messagehdlr, NULL, NULL, NULL, NULL) );
5654 SCIPdebug( SCIPmessagePrintInfo(messagehdlr, "\n") );
5655
5656 return SCIP_OKAY;
5657 }
5658
5659 /**@} */
5660
5661 /**@name Expression methods */
5662 /**@{ */
5663
5664 /* In debug mode, the following methods are implemented as function calls to ensure
5665 * type validity.
5666 * In optimized mode, the methods are implemented as defines to improve performance.
5667 * However, we want to have them in the library anyways, so we have to undef the defines.
5668 */
5669
5670 #undef SCIPexprGetOperator
5671 #undef SCIPexprGetNChildren
5672 #undef SCIPexprGetChildren
5673 #undef SCIPexprGetOpIndex
5674 #undef SCIPexprGetOpReal
5675 #undef SCIPexprGetOpData
5676 #undef SCIPexprGetRealPowerExponent
5677 #undef SCIPexprGetIntPowerExponent
5678 #undef SCIPexprGetSignPowerExponent
5679 #undef SCIPexprGetLinearCoefs
5680 #undef SCIPexprGetLinearConstant
5681 #undef SCIPexprGetQuadElements
5682 #undef SCIPexprGetQuadConstant
5683 #undef SCIPexprGetQuadLinearCoefs
5684 #undef SCIPexprGetNQuadElements
5685 #undef SCIPexprGetMonomials
5686 #undef SCIPexprGetNMonomials
5687 #undef SCIPexprGetPolynomialConstant
5688 #undef SCIPexprGetMonomialCoef
5689 #undef SCIPexprGetMonomialNFactors
5690 #undef SCIPexprGetMonomialChildIndices
5691 #undef SCIPexprGetMonomialExponents
5692 #undef SCIPexprGetUserData
5693 #undef SCIPexprHasUserEstimator
5694 #undef SCIPexprGetUserEvalCapability
5695
5696 /** gives operator of expression */
SCIPexprGetOperator(SCIP_EXPR * expr)5697 SCIP_EXPROP SCIPexprGetOperator(
5698 SCIP_EXPR* expr /**< expression */
5699 )
5700 {
5701 assert(expr != NULL);
5702
5703 return expr->op;
5704 }
5705
5706 /** gives number of children of an expression */
SCIPexprGetNChildren(SCIP_EXPR * expr)5707 int SCIPexprGetNChildren(
5708 SCIP_EXPR* expr /**< expression */
5709 )
5710 {
5711 assert(expr != NULL);
5712
5713 return expr->nchildren;
5714 }
5715
5716 /** gives pointer to array with children of an expression */
SCIPexprGetChildren(SCIP_EXPR * expr)5717 SCIP_EXPR** SCIPexprGetChildren(
5718 SCIP_EXPR* expr /**< expression */
5719 )
5720 {
5721 assert(expr != NULL);
5722
5723 return expr->children;
5724 }
5725
5726 /** gives index belonging to a SCIP_EXPR_VARIDX or SCIP_EXPR_PARAM operand */
SCIPexprGetOpIndex(SCIP_EXPR * expr)5727 int SCIPexprGetOpIndex(
5728 SCIP_EXPR* expr /**< expression */
5729 )
5730 {
5731 assert(expr != NULL);
5732 assert(expr->op == SCIP_EXPR_VARIDX || expr->op == SCIP_EXPR_PARAM);
5733
5734 return expr->data.intval;
5735 }
5736
5737 /** gives real belonging to a SCIP_EXPR_CONST operand */
SCIPexprGetOpReal(SCIP_EXPR * expr)5738 SCIP_Real SCIPexprGetOpReal(
5739 SCIP_EXPR* expr /**< expression */
5740 )
5741 {
5742 assert(expr != NULL);
5743 assert(expr->op == SCIP_EXPR_CONST);
5744
5745 return expr->data.dbl;
5746 }
5747
5748 /** gives void* belonging to a complex operand */
SCIPexprGetOpData(SCIP_EXPR * expr)5749 void* SCIPexprGetOpData(
5750 SCIP_EXPR* expr /**< expression */
5751 )
5752 {
5753 assert(expr != NULL);
5754 assert(expr->op >= SCIP_EXPR_SUM); /* only complex operands store their data as void* */
5755
5756 return expr->data.data;
5757 }
5758
5759 /** gives exponent belonging to a SCIP_EXPR_REALPOWER expression */
SCIPexprGetRealPowerExponent(SCIP_EXPR * expr)5760 SCIP_Real SCIPexprGetRealPowerExponent(
5761 SCIP_EXPR* expr /**< expression */
5762 )
5763 {
5764 assert(expr != NULL);
5765 assert(expr->op == SCIP_EXPR_REALPOWER);
5766
5767 return expr->data.dbl;
5768 }
5769
5770 /** gives exponent belonging to a SCIP_EXPR_INTPOWER expression */
SCIPexprGetIntPowerExponent(SCIP_EXPR * expr)5771 int SCIPexprGetIntPowerExponent(
5772 SCIP_EXPR* expr /**< expression */
5773 )
5774 {
5775 assert(expr != NULL);
5776 assert(expr->op == SCIP_EXPR_INTPOWER);
5777
5778 return expr->data.intval;
5779 }
5780
5781 /** gives exponent belonging to a SCIP_EXPR_SIGNPOWER expression */
SCIPexprGetSignPowerExponent(SCIP_EXPR * expr)5782 SCIP_Real SCIPexprGetSignPowerExponent(
5783 SCIP_EXPR* expr /**< expression */
5784 )
5785 {
5786 assert(expr != NULL);
5787 assert(expr->op == SCIP_EXPR_SIGNPOWER);
5788
5789 return expr->data.dbl;
5790 }
5791
5792 /** gives linear coefficients belonging to a SCIP_EXPR_LINEAR expression */
SCIPexprGetLinearCoefs(SCIP_EXPR * expr)5793 SCIP_Real* SCIPexprGetLinearCoefs(
5794 SCIP_EXPR* expr /**< expression */
5795 )
5796 {
5797 assert(expr != NULL);
5798 assert(expr->op == SCIP_EXPR_LINEAR);
5799 assert(expr->data.data != NULL);
5800
5801 /* the coefficients are stored in the first nchildren elements of the array stored as expression data */
5802 return (SCIP_Real*)expr->data.data;
5803 }
5804
5805 /** gives constant belonging to a SCIP_EXPR_LINEAR expression */
SCIPexprGetLinearConstant(SCIP_EXPR * expr)5806 SCIP_Real SCIPexprGetLinearConstant(
5807 SCIP_EXPR* expr /**< expression */
5808 )
5809 {
5810 assert(expr != NULL);
5811 assert(expr->op == SCIP_EXPR_LINEAR);
5812 assert(expr->data.data != NULL);
5813
5814 /* the constant is stored in the nchildren's element of the array stored as expression data */
5815 return ((SCIP_Real*)expr->data.data)[expr->nchildren];
5816 }
5817
5818 /** gives quadratic elements belonging to a SCIP_EXPR_QUADRATIC expression */
SCIPexprGetQuadElements(SCIP_EXPR * expr)5819 SCIP_QUADELEM* SCIPexprGetQuadElements(
5820 SCIP_EXPR* expr /**< quadratic expression */
5821 )
5822 {
5823 assert(expr != NULL);
5824 assert(expr->op == SCIP_EXPR_QUADRATIC);
5825 assert(expr->data.data != NULL);
5826
5827 return ((SCIP_EXPRDATA_QUADRATIC*)expr->data.data)->quadelems;
5828 }
5829
5830 /** gives constant belonging to a SCIP_EXPR_QUADRATIC expression */
SCIPexprGetQuadConstant(SCIP_EXPR * expr)5831 SCIP_Real SCIPexprGetQuadConstant(
5832 SCIP_EXPR* expr /**< quadratic expression */
5833 )
5834 {
5835 assert(expr != NULL);
5836 assert(expr->op == SCIP_EXPR_QUADRATIC);
5837 assert(expr->data.data != NULL);
5838
5839 return ((SCIP_EXPRDATA_QUADRATIC*)expr->data.data)->constant;
5840 }
5841
5842 /** gives linear coefficients belonging to a SCIP_EXPR_QUADRATIC expression
5843 * can be NULL if all coefficients are 0.0 */
SCIPexprGetQuadLinearCoefs(SCIP_EXPR * expr)5844 SCIP_Real* SCIPexprGetQuadLinearCoefs(
5845 SCIP_EXPR* expr /**< quadratic expression */
5846 )
5847 {
5848 assert(expr != NULL);
5849 assert(expr->op == SCIP_EXPR_QUADRATIC);
5850 assert(expr->data.data != NULL);
5851
5852 return ((SCIP_EXPRDATA_QUADRATIC*)expr->data.data)->lincoefs;
5853 }
5854
5855 /** gives number of quadratic elements belonging to a SCIP_EXPR_QUADRATIC expression */
SCIPexprGetNQuadElements(SCIP_EXPR * expr)5856 int SCIPexprGetNQuadElements(
5857 SCIP_EXPR* expr /**< quadratic expression */
5858 )
5859 {
5860 assert(expr != NULL);
5861 assert(expr->op == SCIP_EXPR_QUADRATIC);
5862 assert(expr->data.data != NULL);
5863
5864 return ((SCIP_EXPRDATA_QUADRATIC*)expr->data.data)->nquadelems;
5865 }
5866
5867 /** gives the monomials belonging to a SCIP_EXPR_POLYNOMIAL expression */
SCIPexprGetMonomials(SCIP_EXPR * expr)5868 SCIP_EXPRDATA_MONOMIAL** SCIPexprGetMonomials(
5869 SCIP_EXPR* expr /**< expression */
5870 )
5871 {
5872 assert(expr != NULL);
5873 assert(expr->op == SCIP_EXPR_POLYNOMIAL);
5874 assert(expr->data.data != NULL);
5875
5876 return ((SCIP_EXPRDATA_POLYNOMIAL*)expr->data.data)->monomials;
5877 }
5878
5879 /** gives the number of monomials belonging to a SCIP_EXPR_POLYNOMIAL expression */
SCIPexprGetNMonomials(SCIP_EXPR * expr)5880 int SCIPexprGetNMonomials(
5881 SCIP_EXPR* expr /**< expression */
5882 )
5883 {
5884 assert(expr != NULL);
5885 assert(expr->op == SCIP_EXPR_POLYNOMIAL);
5886 assert(expr->data.data != NULL);
5887
5888 return ((SCIP_EXPRDATA_POLYNOMIAL*)expr->data.data)->nmonomials;
5889 }
5890
5891 /** gives the constant belonging to a SCIP_EXPR_POLYNOMIAL expression */
SCIPexprGetPolynomialConstant(SCIP_EXPR * expr)5892 SCIP_Real SCIPexprGetPolynomialConstant(
5893 SCIP_EXPR* expr /**< expression */
5894 )
5895 {
5896 assert(expr != NULL);
5897 assert(expr->op == SCIP_EXPR_POLYNOMIAL);
5898 assert(expr->data.data != NULL);
5899
5900 return ((SCIP_EXPRDATA_POLYNOMIAL*)expr->data.data)->constant;
5901 }
5902
5903 /** gets coefficient of a monomial */
SCIPexprGetMonomialCoef(SCIP_EXPRDATA_MONOMIAL * monomial)5904 SCIP_Real SCIPexprGetMonomialCoef(
5905 SCIP_EXPRDATA_MONOMIAL* monomial /**< monomial */
5906 )
5907 {
5908 assert(monomial != NULL);
5909
5910 return monomial->coef;
5911 }
5912
5913 /** gets number of factors of a monomial */
SCIPexprGetMonomialNFactors(SCIP_EXPRDATA_MONOMIAL * monomial)5914 int SCIPexprGetMonomialNFactors(
5915 SCIP_EXPRDATA_MONOMIAL* monomial /**< monomial */
5916 )
5917 {
5918 assert(monomial != NULL);
5919
5920 return monomial->nfactors;
5921 }
5922
5923 /** gets indices of children corresponding to factors of a monomial */
SCIPexprGetMonomialChildIndices(SCIP_EXPRDATA_MONOMIAL * monomial)5924 int* SCIPexprGetMonomialChildIndices(
5925 SCIP_EXPRDATA_MONOMIAL* monomial /**< monomial */
5926 )
5927 {
5928 assert(monomial != NULL);
5929
5930 return monomial->childidxs;
5931 }
5932
5933 /** gets exponents in factors of a monomial */
SCIPexprGetMonomialExponents(SCIP_EXPRDATA_MONOMIAL * monomial)5934 SCIP_Real* SCIPexprGetMonomialExponents(
5935 SCIP_EXPRDATA_MONOMIAL* monomial /**< monomial */
5936 )
5937 {
5938 assert(monomial != NULL);
5939
5940 return monomial->exponents;
5941 }
5942
5943 /** gets user data of a user expression */
SCIPexprGetUserData(SCIP_EXPR * expr)5944 SCIP_USEREXPRDATA* SCIPexprGetUserData(
5945 SCIP_EXPR* expr
5946 )
5947 {
5948 assert(expr != NULL);
5949 assert(expr->data.data != NULL);
5950
5951 return ((SCIP_EXPRDATA_USER*)expr->data.data)->userdata;
5952 }
5953
5954 /** indicates whether a user expression has the estimator callback defined */
SCIPexprHasUserEstimator(SCIP_EXPR * expr)5955 SCIP_Bool SCIPexprHasUserEstimator(
5956 SCIP_EXPR* expr
5957 )
5958 {
5959 assert(expr != NULL);
5960 assert(expr->data.data != NULL);
5961
5962 return ((SCIP_EXPRDATA_USER*)expr->data.data)->estimate != NULL;
5963 }
5964
5965 /** gives the evaluation capability of a user expression */
SCIPexprGetUserEvalCapability(SCIP_EXPR * expr)5966 SCIP_EXPRINTCAPABILITY SCIPexprGetUserEvalCapability(
5967 SCIP_EXPR* expr
5968 )
5969 {
5970 assert(expr != NULL);
5971 assert(expr->data.data != NULL);
5972
5973 return ((SCIP_EXPRDATA_USER*)expr->data.data)->evalcapability;
5974 }
5975
5976 /** creates a simple expression */
SCIPexprCreate(BMS_BLKMEM * blkmem,SCIP_EXPR ** expr,SCIP_EXPROP op,...)5977 SCIP_RETCODE SCIPexprCreate(
5978 BMS_BLKMEM* blkmem, /**< block memory data structure */
5979 SCIP_EXPR** expr, /**< pointer to buffer for expression address */
5980 SCIP_EXPROP op, /**< operand of expression */
5981 ... /**< arguments of operand */
5982 )
5983 {
5984 va_list ap;
5985 SCIP_EXPR** children;
5986 SCIP_EXPROPDATA opdata;
5987
5988 assert(blkmem != NULL);
5989 assert(expr != NULL);
5990
5991 switch( op )
5992 {
5993 case SCIP_EXPR_VARIDX:
5994 case SCIP_EXPR_PARAM:
5995 {
5996 va_start( ap, op ); /*lint !e838*/
5997 opdata.intval = va_arg( ap, int ); /*lint !e416 !e826*/
5998 va_end( ap ); /*lint !e826*/
5999
6000 assert( opdata.intval >= 0 );
6001
6002 SCIP_CALL( exprCreate( blkmem, expr, op, 0, NULL, opdata ) );
6003 break;
6004 }
6005
6006 case SCIP_EXPR_CONST:
6007 {
6008 va_start(ap, op ); /*lint !e838*/
6009 opdata.dbl = va_arg( ap, SCIP_Real ); /*lint !e416 !e826*/
6010 va_end( ap ); /*lint !e826*/
6011
6012 SCIP_CALL( exprCreate( blkmem, expr, op, 0, NULL, opdata ) );
6013 break;
6014 }
6015
6016 /* operands with two children */
6017 case SCIP_EXPR_PLUS :
6018 case SCIP_EXPR_MINUS :
6019 case SCIP_EXPR_MUL :
6020 case SCIP_EXPR_DIV :
6021 case SCIP_EXPR_MIN :
6022 case SCIP_EXPR_MAX :
6023 {
6024 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &children, 2) ); /*lint !e506*/
6025
6026 va_start(ap, op ); /*lint !e838*/
6027 children[0] = va_arg( ap, SCIP_EXPR* ); /*lint !e416 !e826*/
6028 children[1] = va_arg( ap, SCIP_EXPR* ); /*lint !e416 !e826*/
6029 assert(children[0] != NULL);
6030 assert(children[1] != NULL);
6031 va_end( ap ); /*lint !e826*/
6032 opdata.data = NULL; /* to avoid compiler warning about use of uninitialised value */
6033
6034 SCIP_CALL( exprCreate( blkmem, expr, op, 2, children, opdata ) );
6035 break;
6036 }
6037
6038 /* operands with one child */
6039 case SCIP_EXPR_SQUARE:
6040 case SCIP_EXPR_SQRT :
6041 case SCIP_EXPR_EXP :
6042 case SCIP_EXPR_LOG :
6043 case SCIP_EXPR_SIN :
6044 case SCIP_EXPR_COS :
6045 case SCIP_EXPR_TAN :
6046 /* case SCIP_EXPR_ERF : */
6047 /* case SCIP_EXPR_ERFI : */
6048 case SCIP_EXPR_ABS :
6049 case SCIP_EXPR_SIGN :
6050 {
6051 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &children, 1) ); /*lint !e506*/
6052
6053 va_start(ap, op ); /*lint !e838*/
6054 children[0] = va_arg( ap, SCIP_EXPR* ); /*lint !e416 !e826*/
6055 assert(children[0] != NULL);
6056 va_end( ap ); /*lint !e826*/
6057 opdata.data = NULL; /* to avoid compiler warning about use of uninitialised value */
6058
6059 SCIP_CALL( exprCreate( blkmem, expr, op, 1, children, opdata ) );
6060 break;
6061 }
6062
6063 case SCIP_EXPR_REALPOWER:
6064 case SCIP_EXPR_SIGNPOWER:
6065 {
6066 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &children, 1) ); /*lint !e506*/
6067
6068 va_start(ap, op ); /*lint !e838*/
6069 children[0] = va_arg( ap, SCIP_EXPR* ); /*lint !e416 !e826*/
6070 assert(children[0] != NULL);
6071 opdata.dbl = va_arg( ap, SCIP_Real); /*lint !e416 !e826*/
6072 va_end( ap ); /*lint !e826*/
6073
6074 SCIP_CALL( exprCreate( blkmem, expr, op, 1, children, opdata ) );
6075 break;
6076 }
6077
6078 case SCIP_EXPR_INTPOWER:
6079 {
6080 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &children, 1) ); /*lint !e506*/
6081
6082 va_start(ap, op ); /*lint !e838*/
6083 children[0] = va_arg( ap, SCIP_EXPR* ); /*lint !e416 !e826*/
6084 assert(children[0] != NULL);
6085 opdata.intval = va_arg( ap, int); /*lint !e416 !e826*/
6086 va_end( ap ); /*lint !e826*/
6087
6088 SCIP_CALL( exprCreate( blkmem, expr, op, 1, children, opdata ) );
6089 break;
6090 }
6091
6092 /* complex operands */
6093 case SCIP_EXPR_SUM :
6094 case SCIP_EXPR_PRODUCT:
6095 {
6096 int nchildren;
6097 SCIP_EXPR** childrenarg;
6098
6099 opdata.data = NULL; /* to avoid compiler warning about use of uninitialised value */
6100
6101 va_start(ap, op ); /*lint !e838*/
6102 /* first argument should be number of children */
6103 nchildren = va_arg( ap, int ); /*lint !e416 !e826*/
6104 assert(nchildren >= 0);
6105
6106 /* for a sum or product of 0 terms we can finish here */
6107 if( nchildren == 0 )
6108 {
6109 SCIP_RETCODE retcode;
6110 retcode = exprCreate( blkmem, expr, op, 0, NULL, opdata);
6111 va_end( ap ); /*lint !e826*/
6112 SCIP_CALL( retcode );
6113 break;
6114 }
6115
6116 /* next argument should be array of children expressions */
6117 childrenarg = va_arg( ap, SCIP_EXPR** ); /*lint !e416 !e826*/
6118 assert(childrenarg != NULL);
6119 va_end( ap ); /*lint !e826*/
6120
6121 SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &children, childrenarg, nchildren) );
6122
6123 SCIP_CALL( exprCreate( blkmem, expr, op, nchildren, children, opdata) );
6124 break;
6125 }
6126
6127 case SCIP_EXPR_LINEAR :
6128 case SCIP_EXPR_QUADRATIC:
6129 case SCIP_EXPR_POLYNOMIAL:
6130 case SCIP_EXPR_USER:
6131 {
6132 SCIPerrorMessage("cannot create complex expression linear, quadratic, polynomial, or user with SCIPexprCreate\n");
6133 return SCIP_INVALIDDATA;
6134 }
6135
6136 case SCIP_EXPR_LAST:
6137 SCIPABORT();
6138 break;
6139 }
6140
6141 return SCIP_OKAY;
6142 }
6143
6144 /** copies an expression including its children */
SCIPexprCopyDeep(BMS_BLKMEM * blkmem,SCIP_EXPR ** targetexpr,SCIP_EXPR * sourceexpr)6145 SCIP_RETCODE SCIPexprCopyDeep(
6146 BMS_BLKMEM* blkmem, /**< block memory data structure */
6147 SCIP_EXPR** targetexpr, /**< buffer to store pointer to copied expression */
6148 SCIP_EXPR* sourceexpr /**< expression to copy */
6149 )
6150 {
6151 assert(blkmem != NULL);
6152 assert(targetexpr != NULL);
6153 assert(sourceexpr != NULL);
6154
6155 SCIP_ALLOC( BMSduplicateBlockMemory(blkmem, targetexpr, sourceexpr) );
6156
6157 if( sourceexpr->nchildren )
6158 {
6159 int i;
6160
6161 /* alloc memory for children expressions */
6162 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &(*targetexpr)->children, sourceexpr->nchildren) );
6163
6164 /* copy children expressions */
6165 for( i = 0; i < sourceexpr->nchildren; ++i )
6166 {
6167 SCIP_CALL( SCIPexprCopyDeep(blkmem, &(*targetexpr)->children[i], sourceexpr->children[i]) );
6168 }
6169 }
6170 else
6171 {
6172 assert((*targetexpr)->children == NULL); /* otherwise, sourceexpr->children was not NULL, which is wrong */
6173 }
6174
6175 /* call operands data copy callback for complex operands
6176 * for simple operands BMSduplicate above should have done the job
6177 */
6178 if( exprOpTable[sourceexpr->op].copydata != NULL )
6179 {
6180 SCIP_CALL( exprOpTable[sourceexpr->op].copydata(blkmem, sourceexpr->nchildren, sourceexpr->data, &(*targetexpr)->data) );
6181 }
6182
6183 return SCIP_OKAY;
6184 }
6185
6186 /** frees an expression including its children */
SCIPexprFreeDeep(BMS_BLKMEM * blkmem,SCIP_EXPR ** expr)6187 void SCIPexprFreeDeep(
6188 BMS_BLKMEM* blkmem, /**< block memory data structure */
6189 SCIP_EXPR** expr /**< pointer to expression to free */
6190 )
6191 {
6192 assert(blkmem != NULL);
6193 assert(expr != NULL);
6194 assert(*expr != NULL);
6195
6196 /* call operands data free callback, if given */
6197 if( exprOpTable[(*expr)->op].freedata != NULL )
6198 {
6199 exprOpTable[(*expr)->op].freedata(blkmem, (*expr)->nchildren, (*expr)->data);
6200 }
6201
6202 if( (*expr)->nchildren )
6203 {
6204 int i;
6205
6206 assert( (*expr)->children != NULL );
6207
6208 for( i = 0; i < (*expr)->nchildren; ++i )
6209 {
6210 SCIPexprFreeDeep(blkmem, &(*expr)->children[i]);
6211 assert((*expr)->children[i] == NULL);
6212 }
6213
6214 BMSfreeBlockMemoryArray(blkmem, &(*expr)->children, (*expr)->nchildren);
6215 }
6216 else
6217 {
6218 assert( (*expr)->children == NULL );
6219 }
6220
6221 BMSfreeBlockMemory(blkmem, expr);
6222 }
6223
6224 /** frees an expression but not its children */
SCIPexprFreeShallow(BMS_BLKMEM * blkmem,SCIP_EXPR ** expr)6225 void SCIPexprFreeShallow(
6226 BMS_BLKMEM* blkmem, /**< block memory data structure */
6227 SCIP_EXPR** expr /**< pointer to expression to free */
6228 )
6229 {
6230 assert(blkmem != NULL);
6231 assert(expr != NULL);
6232 assert(*expr != NULL);
6233
6234 /* call operands data free callback, if given */
6235 if( exprOpTable[(*expr)->op].freedata != NULL )
6236 {
6237 exprOpTable[(*expr)->op].freedata(blkmem, (*expr)->nchildren, (*expr)->data);
6238 }
6239
6240 BMSfreeBlockMemoryArrayNull(blkmem, &(*expr)->children, (*expr)->nchildren);
6241
6242 BMSfreeBlockMemory(blkmem, expr);
6243 }
6244
6245 /** creates an expression from the addition of two given expression, with coefficients, and a constant
6246 *
6247 * The given expressions may be modified or freed, otherwise it will be used a child expression.
6248 * Favors creation and maintaining of SCIP_EXPR_LINEAR over SCIP_EXPR_PLUS or SCIP_EXPR_SUM.
6249 */
SCIPexprAdd(BMS_BLKMEM * blkmem,SCIP_EXPR ** expr,SCIP_Real coef1,SCIP_EXPR * term1,SCIP_Real coef2,SCIP_EXPR * term2,SCIP_Real constant)6250 SCIP_RETCODE SCIPexprAdd(
6251 BMS_BLKMEM* blkmem, /**< block memory data structure */
6252 SCIP_EXPR** expr, /**< pointer to store pointer to created expression */
6253 SCIP_Real coef1, /**< coefficient of first term */
6254 SCIP_EXPR* term1, /**< expression of first term, or NULL */
6255 SCIP_Real coef2, /**< coefficient of second term */
6256 SCIP_EXPR* term2, /**< expression of second term, or NULL */
6257 SCIP_Real constant /**< constant term to add */
6258 )
6259 {
6260 assert(blkmem != NULL);
6261 assert(expr != NULL);
6262
6263 /* @todo could do something special with quadratic and polynomial expressions */
6264
6265 if( term1 != NULL && SCIPexprGetOperator(term1) == SCIP_EXPR_CONST )
6266 {
6267 constant += coef1 * SCIPexprGetOpReal(term1);
6268 SCIPexprFreeDeep(blkmem, &term1);
6269 }
6270
6271 if( term2 != NULL && SCIPexprGetOperator(term2) == SCIP_EXPR_CONST )
6272 {
6273 constant += coef2 * SCIPexprGetOpReal(term2);
6274 SCIPexprFreeDeep(blkmem, &term2);
6275 }
6276
6277 if( term1 == NULL && term2 == NULL )
6278 {
6279 SCIP_CALL( SCIPexprCreate(blkmem, expr, SCIP_EXPR_CONST, constant) );
6280 return SCIP_OKAY;
6281 }
6282
6283 if( term1 != NULL && SCIPexprGetOperator(term1) == SCIP_EXPR_LINEAR && coef1 != 1.0 )
6284 {
6285 /* multiply coefficients and constant of linear expression term1 by coef1 */
6286 SCIP_Real* data;
6287 int i;
6288
6289 data = (SCIP_Real*)term1->data.data;
6290 assert(data != NULL);
6291
6292 /* loop one more index to multiply also constant of linear expression */
6293 for( i = 0; i <= term1->nchildren; ++i )
6294 data[i] *= coef1;
6295
6296 coef1 = 1.0;
6297 }
6298
6299 if( term2 != NULL && SCIPexprGetOperator(term2) == SCIP_EXPR_LINEAR && coef2 != 1.0 )
6300 {
6301 /* multiply coefficients and constant of linear expression term2 by coef2 */
6302 SCIP_Real* data;
6303 int i;
6304
6305 data = (SCIP_Real*)term2->data.data;
6306 assert(data != NULL);
6307
6308 /* loop one more index to multiply also constant of linear expression */
6309 for( i = 0; i <= term2->nchildren; ++i )
6310 data[i] *= coef2;
6311
6312 coef2 = 1.0;
6313 }
6314
6315 if( term1 == NULL || term2 == NULL )
6316 {
6317 if( term1 == NULL )
6318 {
6319 term1 = term2;
6320 coef1 = coef2;
6321 }
6322 if( constant != 0.0 || coef1 != 1.0 )
6323 {
6324 if( SCIPexprGetOperator(term1) == SCIP_EXPR_LINEAR )
6325 {
6326 assert(coef1 == 1.0);
6327
6328 /* add constant to existing linear expression */
6329 SCIP_CALL( SCIPexprAddToLinear(blkmem, term1, 0, NULL, NULL, constant) );
6330 *expr = term1;
6331 }
6332 else
6333 {
6334 /* create new linear expression for coef1 * term1 + constant */
6335 SCIP_CALL( SCIPexprCreateLinear(blkmem, expr, 1, &term1, &coef1, constant) );
6336 }
6337 }
6338 else
6339 {
6340 assert(constant == 0.0);
6341 assert(coef1 == 1.0);
6342 *expr = term1;
6343 }
6344
6345 return SCIP_OKAY;
6346 }
6347
6348 if( SCIPexprGetOperator(term1) == SCIP_EXPR_LINEAR && SCIPexprGetOperator(term2) == SCIP_EXPR_LINEAR )
6349 {
6350 /* add 2nd linear expression to first one */
6351 assert(coef1 == 1.0);
6352 assert(coef2 == 1.0);
6353
6354 SCIP_CALL( SCIPexprAddToLinear(blkmem, term1, SCIPexprGetNChildren(term2), SCIPexprGetLinearCoefs(term2), SCIPexprGetChildren(term2), SCIPexprGetLinearConstant(term2) + constant) );
6355 SCIPexprFreeShallow(blkmem, &term2);
6356
6357 *expr = term1;
6358
6359 return SCIP_OKAY;
6360 }
6361
6362 if( SCIPexprGetOperator(term2) == SCIP_EXPR_LINEAR )
6363 {
6364 /* if only term2 is linear, then swap */
6365 SCIP_EXPR* tmp;
6366
6367 tmp = term2;
6368 assert(coef2 == 1.0);
6369
6370 term2 = term1;
6371 coef2 = coef1;
6372 term1 = tmp;
6373 coef1 = 1.0;
6374 }
6375
6376 if( SCIPexprGetOperator(term1) == SCIP_EXPR_LINEAR )
6377 {
6378 /* add coef2*term2 as extra child to linear expression term1 */
6379 assert(coef1 == 1.0);
6380
6381 SCIP_CALL( SCIPexprAddToLinear(blkmem, term1, 1, &coef2, &term2, constant) );
6382 *expr = term1;
6383
6384 return SCIP_OKAY;
6385 }
6386
6387 /* both terms are not linear, then create new linear term for sum */
6388 {
6389 SCIP_Real coefs[2];
6390 SCIP_EXPR* children[2];
6391
6392 coefs[0] = coef1;
6393 coefs[1] = coef2;
6394 children[0] = term1;
6395 children[1] = term2;
6396
6397 SCIP_CALL( SCIPexprCreateLinear(blkmem, expr, 2, children, coefs, constant) );
6398 }
6399
6400 return SCIP_OKAY;
6401 }
6402
6403 /** creates an expression from the multiplication of an expression with a constant
6404 *
6405 * The given expressions may be modified or freed, otherwise it will be used a child expression.
6406 * Favors creation and maintaining SCIP_EXPR_LINEAR over SCIP_EXPR_PLUS or SCIP_EXPR_SUM.
6407 */
SCIPexprMulConstant(BMS_BLKMEM * blkmem,SCIP_EXPR ** expr,SCIP_EXPR * term,SCIP_Real factor)6408 SCIP_RETCODE SCIPexprMulConstant(
6409 BMS_BLKMEM* blkmem, /**< block memory data structure */
6410 SCIP_EXPR** expr, /**< buffer to store pointer to created expression */
6411 SCIP_EXPR* term, /**< term to multiply by factor */
6412 SCIP_Real factor /**< factor */
6413 )
6414 {
6415 assert(blkmem != NULL);
6416 assert(expr != NULL);
6417 assert(term != NULL);
6418
6419 if( factor == 0.0 )
6420 {
6421 SCIP_CALL( SCIPexprCreate(blkmem, expr, SCIP_EXPR_CONST, 0.0) );
6422
6423 SCIPexprFreeDeep(blkmem, &term);
6424
6425 return SCIP_OKAY;
6426 }
6427 if( factor == 1.0 )
6428 {
6429 *expr = term;
6430 return SCIP_OKAY;
6431 }
6432
6433 switch( SCIPexprGetOperator(term) )
6434 {
6435 case SCIP_EXPR_CONST :
6436 {
6437 SCIP_CALL( SCIPexprCreate(blkmem, expr, SCIP_EXPR_CONST, factor * SCIPexprGetOpReal(term)) );
6438 SCIPexprFreeDeep(blkmem, &term);
6439 break;
6440 }
6441
6442 case SCIP_EXPR_LINEAR :
6443 {
6444 SCIP_Real* data;
6445 int i;
6446
6447 data = (SCIP_Real*)term->data.data;
6448 assert(data != NULL);
6449
6450 /* loop one more index to multiply also constant of linear expression */
6451 for( i = 0; i <= SCIPexprGetNChildren(term); ++i )
6452 data[i] *= factor;
6453
6454 *expr = term;
6455 break;
6456 }
6457
6458 case SCIP_EXPR_QUADRATIC :
6459 {
6460 SCIP_EXPRDATA_QUADRATIC* data;
6461 int i;
6462
6463 data = (SCIP_EXPRDATA_QUADRATIC*)term->data.data;
6464
6465 data->constant *= factor;
6466
6467 if( data->lincoefs != NULL )
6468 for( i = 0; i < term->nchildren; ++i )
6469 data->lincoefs[i] *= factor;
6470
6471 for( i = 0; i < data->nquadelems; ++i )
6472 data->quadelems[i].coef *= factor;
6473
6474 *expr = term;
6475 break;
6476 }
6477
6478 case SCIP_EXPR_POLYNOMIAL :
6479 {
6480 SCIP_EXPRDATA_POLYNOMIAL* data;
6481 int i;
6482
6483 data = (SCIP_EXPRDATA_POLYNOMIAL*)term->data.data;
6484
6485 data->constant *= factor;
6486
6487 for( i = 0; i < data->nmonomials; ++i )
6488 data->monomials[i]->coef *= factor;
6489
6490 *expr = term;
6491 break;
6492 }
6493
6494 default:
6495 {
6496 SCIP_CALL( SCIPexprCreateLinear(blkmem, expr, 1, &term, &factor, 0.0) );
6497 break;
6498 }
6499
6500 } /*lint !e788 */
6501
6502 return SCIP_OKAY;
6503 }
6504
6505 /** creates a SCIP_EXPR_LINEAR expression that is (affine) linear in its children: constant + sum_i coef_i child_i */
SCIPexprCreateLinear(BMS_BLKMEM * blkmem,SCIP_EXPR ** expr,int nchildren,SCIP_EXPR ** children,SCIP_Real * coefs,SCIP_Real constant)6506 SCIP_RETCODE SCIPexprCreateLinear(
6507 BMS_BLKMEM* blkmem, /**< block memory data structure */
6508 SCIP_EXPR** expr, /**< pointer to buffer for expression address */
6509 int nchildren, /**< number of children */
6510 SCIP_EXPR** children, /**< children of expression */
6511 SCIP_Real* coefs, /**< coefficients of children */
6512 SCIP_Real constant /**< constant part */
6513 )
6514 {
6515 SCIP_EXPROPDATA opdata;
6516 SCIP_EXPR** childrencopy;
6517 SCIP_Real* data;
6518
6519 assert(nchildren >= 0);
6520 assert(children != NULL || nchildren == 0);
6521 assert(coefs != NULL || nchildren == 0);
6522
6523 if( nchildren > 0 )
6524 {
6525 SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &childrencopy, children, nchildren) );
6526 }
6527 else
6528 childrencopy = NULL;
6529
6530 /* we store the coefficients and the constant in a single array and make this our operand data */
6531 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &data, nchildren + 1) );
6532 BMScopyMemoryArray(data, coefs, nchildren); /*lint !e644*/
6533 data[nchildren] = constant;
6534
6535 opdata.data = (void*)data;
6536
6537 SCIP_CALL( exprCreate( blkmem, expr, SCIP_EXPR_LINEAR, nchildren, childrencopy, opdata) );
6538
6539 return SCIP_OKAY;
6540 }
6541
6542 /** adds new terms to a linear expression */
SCIPexprAddToLinear(BMS_BLKMEM * blkmem,SCIP_EXPR * expr,int nchildren,SCIP_Real * coefs,SCIP_EXPR ** children,SCIP_Real constant)6543 SCIP_RETCODE SCIPexprAddToLinear(
6544 BMS_BLKMEM* blkmem, /**< block memory */
6545 SCIP_EXPR* expr, /**< linear expression */
6546 int nchildren, /**< number of children to add */
6547 SCIP_Real* coefs, /**< coefficients of additional children */
6548 SCIP_EXPR** children, /**< additional children expressions */
6549 SCIP_Real constant /**< constant to add */
6550 )
6551 {
6552 SCIP_Real* data;
6553
6554 assert(blkmem != NULL);
6555 assert(expr != NULL);
6556 assert(expr->op == SCIP_EXPR_LINEAR);
6557 assert(nchildren >= 0);
6558 assert(coefs != NULL || nchildren == 0);
6559 assert(children != NULL || nchildren == 0);
6560
6561 data = (SCIP_Real*)expr->data.data;
6562 assert(data != NULL);
6563
6564 /* handle simple case of adding a constant */
6565 if( nchildren == 0 )
6566 {
6567 data[expr->nchildren] += constant;
6568
6569 return SCIP_OKAY;
6570 }
6571
6572 /* add new children to expr's children array */
6573 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &expr->children, expr->nchildren, expr->nchildren + nchildren) );
6574 BMScopyMemoryArray(&expr->children[expr->nchildren], children, nchildren); /*lint !e866*/
6575
6576 /* add constant and new coefs to expr's data array */
6577 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &data, expr->nchildren + 1, expr->nchildren + nchildren + 1) );
6578 data[expr->nchildren + nchildren] = data[expr->nchildren] + constant;
6579 BMScopyMemoryArray(&data[expr->nchildren], coefs, nchildren); /*lint !e866*/
6580 expr->data.data = (void*)data;
6581
6582 expr->nchildren += nchildren;
6583
6584 return SCIP_OKAY;
6585 }
6586
6587 /** creates a SCIP_EXPR_QUADRATIC expression: constant + sum_i coef_i child_i + sum_i coef_i child1_i child2_i */
SCIPexprCreateQuadratic(BMS_BLKMEM * blkmem,SCIP_EXPR ** expr,int nchildren,SCIP_EXPR ** children,SCIP_Real constant,SCIP_Real * lincoefs,int nquadelems,SCIP_QUADELEM * quadelems)6588 SCIP_RETCODE SCIPexprCreateQuadratic(
6589 BMS_BLKMEM* blkmem, /**< block memory data structure */
6590 SCIP_EXPR** expr, /**< pointer to buffer for expression address */
6591 int nchildren, /**< number of children */
6592 SCIP_EXPR** children, /**< children of expression */
6593 SCIP_Real constant, /**< constant */
6594 SCIP_Real* lincoefs, /**< linear coefficients of children, or NULL if all 0.0 */
6595 int nquadelems, /**< number of quadratic elements */
6596 SCIP_QUADELEM* quadelems /**< quadratic elements specifying coefficients and child indices */
6597 )
6598 {
6599 SCIP_EXPROPDATA opdata;
6600 SCIP_EXPR** childrencopy;
6601 SCIP_EXPRDATA_QUADRATIC* data;
6602
6603 assert(nchildren >= 0);
6604 assert(children != NULL || nchildren == 0);
6605 assert(quadelems != NULL || nquadelems == 0);
6606
6607 if( nchildren > 0 )
6608 {
6609 SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &childrencopy, children, nchildren) );
6610 }
6611 else
6612 childrencopy = NULL;
6613
6614 SCIP_CALL( quadraticdataCreate(blkmem, &data, constant, nchildren, lincoefs, nquadelems, quadelems) );
6615
6616 opdata.data = (void*)data;
6617
6618 SCIP_CALL( exprCreate( blkmem, expr, SCIP_EXPR_QUADRATIC, nchildren, childrencopy, opdata) );
6619
6620 return SCIP_OKAY;
6621 }
6622
6623 /** ensures that quadratic elements of a quadratic expression are sorted */
SCIPexprSortQuadElems(SCIP_EXPR * expr)6624 void SCIPexprSortQuadElems(
6625 SCIP_EXPR* expr /**< quadratic expression */
6626 )
6627 {
6628 assert(expr != NULL);
6629 assert(expr->op == SCIP_EXPR_QUADRATIC);
6630 assert(expr->data.data != NULL);
6631
6632 quadraticdataSort((SCIP_EXPRDATA_QUADRATIC*)expr->data.data);
6633 }
6634
6635 /** creates a SCIP_EXPR_POLYNOMIAL expression from an array of monomials: constant + sum_i monomial_i */
SCIPexprCreatePolynomial(BMS_BLKMEM * blkmem,SCIP_EXPR ** expr,int nchildren,SCIP_EXPR ** children,int nmonomials,SCIP_EXPRDATA_MONOMIAL ** monomials,SCIP_Real constant,SCIP_Bool copymonomials)6636 SCIP_RETCODE SCIPexprCreatePolynomial(
6637 BMS_BLKMEM* blkmem, /**< block memory data structure */
6638 SCIP_EXPR** expr, /**< pointer to buffer for expression address */
6639 int nchildren, /**< number of children */
6640 SCIP_EXPR** children, /**< children of expression */
6641 int nmonomials, /**< number of monomials */
6642 SCIP_EXPRDATA_MONOMIAL** monomials, /**< monomials */
6643 SCIP_Real constant, /**< constant part */
6644 SCIP_Bool copymonomials /**< should monomials by copied or ownership be assumed? */
6645 )
6646 {
6647 SCIP_EXPROPDATA opdata;
6648 SCIP_EXPR** childrencopy;
6649 SCIP_EXPRDATA_POLYNOMIAL* data;
6650
6651 assert(nchildren >= 0);
6652 assert(children != NULL || nchildren == 0);
6653 assert(monomials != NULL || nmonomials == 0);
6654
6655 if( nchildren > 0 )
6656 {
6657 SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &childrencopy, children, nchildren) );
6658 }
6659 else
6660 childrencopy = NULL;
6661
6662 SCIP_CALL( polynomialdataCreate(blkmem, &data, nmonomials, monomials, constant, copymonomials) );
6663 opdata.data = (void*)data;
6664
6665 SCIP_CALL( exprCreate( blkmem, expr, SCIP_EXPR_POLYNOMIAL, nchildren, childrencopy, opdata) );
6666
6667 return SCIP_OKAY;
6668 }
6669
6670 /** adds an array of monomials to a SCIP_EXPR_POLYNOMIAL expression */
SCIPexprAddMonomials(BMS_BLKMEM * blkmem,SCIP_EXPR * expr,int nmonomials,SCIP_EXPRDATA_MONOMIAL ** monomials,SCIP_Bool copymonomials)6671 SCIP_RETCODE SCIPexprAddMonomials(
6672 BMS_BLKMEM* blkmem, /**< block memory of expression */
6673 SCIP_EXPR* expr, /**< expression */
6674 int nmonomials, /**< number of monomials to add */
6675 SCIP_EXPRDATA_MONOMIAL** monomials, /**< the monomials to add */
6676 SCIP_Bool copymonomials /**< should monomials by copied or ownership be assumed? */
6677 )
6678 {
6679 assert(blkmem != NULL);
6680 assert(expr != NULL);
6681 assert(expr->op == SCIP_EXPR_POLYNOMIAL);
6682 assert(monomials != NULL || nmonomials == 0);
6683
6684 if( nmonomials == 0 )
6685 return SCIP_OKAY;
6686
6687 SCIP_CALL( polynomialdataAddMonomials(blkmem, (SCIP_EXPRDATA_POLYNOMIAL*)expr->data.data, nmonomials, monomials, copymonomials) );
6688
6689 return SCIP_OKAY;
6690 }
6691
6692 /** changes the constant in a SCIP_EXPR_POLYNOMIAL expression */
SCIPexprChgPolynomialConstant(SCIP_EXPR * expr,SCIP_Real constant)6693 void SCIPexprChgPolynomialConstant(
6694 SCIP_EXPR* expr, /**< expression */
6695 SCIP_Real constant /**< new value for constant */
6696 )
6697 {
6698 assert(expr != NULL);
6699 assert(expr->op == SCIP_EXPR_POLYNOMIAL);
6700 assert(expr->data.data != NULL);
6701
6702 ((SCIP_EXPRDATA_POLYNOMIAL*)expr->data.data)->constant = constant;
6703 }
6704
6705 /** multiplies each summand of a polynomial by a given constant */
SCIPexprMultiplyPolynomialByConstant(BMS_BLKMEM * blkmem,SCIP_EXPR * expr,SCIP_Real factor)6706 void SCIPexprMultiplyPolynomialByConstant(
6707 BMS_BLKMEM* blkmem, /**< block memory */
6708 SCIP_EXPR* expr, /**< polynomial expression */
6709 SCIP_Real factor /**< constant factor */
6710 )
6711 {
6712 assert(expr != NULL);
6713 assert(expr->op == SCIP_EXPR_POLYNOMIAL);
6714 assert(expr->data.data != NULL);
6715
6716 polynomialdataMultiplyByConstant(blkmem, (SCIP_EXPRDATA_POLYNOMIAL*)expr->data.data, factor);
6717 }
6718
6719 /** multiplies each summand of a polynomial by a given monomial */
SCIPexprMultiplyPolynomialByMonomial(BMS_BLKMEM * blkmem,SCIP_EXPR * expr,SCIP_EXPRDATA_MONOMIAL * factor,int * childmap)6720 SCIP_RETCODE SCIPexprMultiplyPolynomialByMonomial(
6721 BMS_BLKMEM* blkmem, /**< block memory */
6722 SCIP_EXPR* expr, /**< polynomial expression */
6723 SCIP_EXPRDATA_MONOMIAL* factor, /**< monomial factor */
6724 int* childmap /**< map children in factor to children in expr, or NULL for 1:1 */
6725 )
6726 {
6727 assert(blkmem != NULL);
6728 assert(factor != NULL);
6729 assert(expr != NULL);
6730 assert(expr->op == SCIP_EXPR_POLYNOMIAL);
6731 assert(expr->data.data != NULL);
6732
6733 SCIP_CALL( polynomialdataMultiplyByMonomial(blkmem, (SCIP_EXPRDATA_POLYNOMIAL*)expr->data.data, factor, childmap) );
6734
6735 return SCIP_OKAY;
6736 }
6737
6738 /** multiplies this polynomial by a polynomial
6739 *
6740 * Factor needs to be different from expr.
6741 * Children of factor need to be children of expr already, w.r.t. an optional mapping of child indices.
6742 */
SCIPexprMultiplyPolynomialByPolynomial(BMS_BLKMEM * blkmem,SCIP_EXPR * expr,SCIP_EXPR * factor,int * childmap)6743 SCIP_RETCODE SCIPexprMultiplyPolynomialByPolynomial(
6744 BMS_BLKMEM* blkmem, /**< block memory */
6745 SCIP_EXPR* expr, /**< polynomial expression */
6746 SCIP_EXPR* factor, /**< polynomial factor */
6747 int* childmap /**< map children in factor to children in expr, or NULL for 1:1 */
6748 )
6749 {
6750 assert(blkmem != NULL);
6751 assert(expr != NULL);
6752 assert(expr->op == SCIP_EXPR_POLYNOMIAL);
6753 assert(expr->data.data != NULL);
6754 assert(factor != NULL);
6755 assert(factor->op == SCIP_EXPR_POLYNOMIAL);
6756 assert(factor->data.data != NULL);
6757 assert(expr != factor);
6758
6759 #ifndef NDEBUG
6760 if( childmap == NULL )
6761 {
6762 int i;
6763 assert(factor->nchildren == expr->nchildren);
6764 for( i = 0; i < factor->nchildren; ++i )
6765 assert(SCIPexprAreEqual(expr->children[i], factor->children[i], 0.0));
6766 }
6767 else
6768 {
6769 int i;
6770 for( i = 0; i < factor->nchildren; ++i )
6771 {
6772 assert(childmap[i] >= 0);
6773 assert(childmap[i] < expr->nchildren);
6774 assert(SCIPexprAreEqual(expr->children[childmap[i]], factor->children[i], 0.0));
6775 }
6776 }
6777 #endif
6778
6779 SCIP_CALL( polynomialdataMultiplyByPolynomial(blkmem, (SCIP_EXPRDATA_POLYNOMIAL*)expr->data.data, (SCIP_EXPRDATA_POLYNOMIAL*)factor->data.data, childmap) );
6780
6781 return SCIP_OKAY;
6782 }
6783
6784 /** takes a power of the polynomial
6785 *
6786 * Exponent need to be an integer.
6787 * Polynomial needs to be a monomial, if exponent is negative.
6788 */
SCIPexprPolynomialPower(BMS_BLKMEM * blkmem,SCIP_EXPR * expr,int exponent)6789 SCIP_RETCODE SCIPexprPolynomialPower(
6790 BMS_BLKMEM* blkmem, /**< block memory */
6791 SCIP_EXPR* expr, /**< polynomial expression */
6792 int exponent /**< exponent of power operation */
6793 )
6794 {
6795 assert(blkmem != NULL);
6796 assert(expr != NULL);
6797 assert(expr->op == SCIP_EXPR_POLYNOMIAL);
6798 assert(expr->data.data != NULL);
6799
6800 SCIP_CALL( polynomialdataPower(blkmem, (SCIP_EXPRDATA_POLYNOMIAL*)expr->data.data, exponent) );
6801
6802 return SCIP_OKAY;
6803 }
6804
6805 /** merges monomials in a polynomial expression that differ only in coefficient into a single monomial
6806 *
6807 * Eliminates monomials with coefficient between -eps and eps.
6808 */
SCIPexprMergeMonomials(BMS_BLKMEM * blkmem,SCIP_EXPR * expr,SCIP_Real eps,SCIP_Bool mergefactors)6809 void SCIPexprMergeMonomials(
6810 BMS_BLKMEM* blkmem, /**< block memory */
6811 SCIP_EXPR* expr, /**< polynomial expression */
6812 SCIP_Real eps, /**< threshold under which numbers are treat as zero */
6813 SCIP_Bool mergefactors /**< whether to merge factors in monomials too */
6814 )
6815 {
6816 assert(expr != NULL);
6817 assert(expr->op == SCIP_EXPR_POLYNOMIAL);
6818 assert(expr->data.data != NULL);
6819
6820 polynomialdataMergeMonomials(blkmem, (SCIP_EXPRDATA_POLYNOMIAL*)expr->data.data, eps, mergefactors);
6821 }
6822
6823 /** checks if two monomials are equal */
SCIPexprAreMonomialsEqual(SCIP_EXPRDATA_MONOMIAL * monomial1,SCIP_EXPRDATA_MONOMIAL * monomial2,SCIP_Real eps)6824 SCIP_Bool SCIPexprAreMonomialsEqual(
6825 SCIP_EXPRDATA_MONOMIAL* monomial1, /**< first monomial */
6826 SCIP_EXPRDATA_MONOMIAL* monomial2, /**< second monomial */
6827 SCIP_Real eps /**< threshold under which numbers are treated as 0.0 */
6828 )
6829 {
6830 int i;
6831
6832 assert(monomial1 != NULL);
6833 assert(monomial2 != NULL);
6834
6835 if( monomial1->nfactors != monomial2->nfactors )
6836 return FALSE;
6837
6838 if( !EPSEQ(monomial1->coef, monomial2->coef, eps) )
6839 return FALSE;
6840
6841 SCIPexprSortMonomialFactors(monomial1);
6842 SCIPexprSortMonomialFactors(monomial2);
6843
6844 for( i = 0; i < monomial1->nfactors; ++i )
6845 {
6846 if( monomial1->childidxs[i] != monomial2->childidxs[i] ||
6847 !EPSEQ(monomial1->exponents[i], monomial2->exponents[i], eps) )
6848 return FALSE;
6849 }
6850
6851 return TRUE;
6852 }
6853
6854 /** changes coefficient of monomial */
SCIPexprChgMonomialCoef(SCIP_EXPRDATA_MONOMIAL * monomial,SCIP_Real newcoef)6855 void SCIPexprChgMonomialCoef(
6856 SCIP_EXPRDATA_MONOMIAL* monomial, /**< monomial */
6857 SCIP_Real newcoef /**< new coefficient */
6858 )
6859 {
6860 assert(monomial != NULL);
6861
6862 monomial->coef = newcoef;
6863 }
6864
6865 /** adds factors to a monomial */
SCIPexprAddMonomialFactors(BMS_BLKMEM * blkmem,SCIP_EXPRDATA_MONOMIAL * monomial,int nfactors,int * childidxs,SCIP_Real * exponents)6866 SCIP_RETCODE SCIPexprAddMonomialFactors(
6867 BMS_BLKMEM* blkmem, /**< block memory */
6868 SCIP_EXPRDATA_MONOMIAL* monomial, /**< monomial */
6869 int nfactors, /**< number of factors to add */
6870 int* childidxs, /**< indices of children corresponding to factors */
6871 SCIP_Real* exponents /**< exponent in each factor */
6872 )
6873 {
6874 assert(monomial != NULL);
6875 assert(nfactors >= 0);
6876 assert(childidxs != NULL || nfactors == 0);
6877 assert(exponents != NULL || nfactors == 0);
6878
6879 if( nfactors == 0 )
6880 return SCIP_OKAY;
6881
6882 SCIP_CALL( monomialdataEnsureFactorsSize(blkmem, monomial, monomial->nfactors + nfactors) );
6883 assert(monomial->nfactors + nfactors <= monomial->factorssize);
6884
6885 BMScopyMemoryArray(&monomial->childidxs[monomial->nfactors], childidxs, nfactors); /*lint !e866*/
6886 BMScopyMemoryArray(&monomial->exponents[monomial->nfactors], exponents, nfactors); /*lint !e866*/
6887
6888 monomial->nfactors += nfactors;
6889 monomial->sorted = (monomial->nfactors <= 1);
6890
6891 return SCIP_OKAY;
6892 }
6893
6894 /** multiplies a monomial with a monomial */
SCIPexprMultiplyMonomialByMonomial(BMS_BLKMEM * blkmem,SCIP_EXPRDATA_MONOMIAL * monomial,SCIP_EXPRDATA_MONOMIAL * factor,int * childmap)6895 SCIP_RETCODE SCIPexprMultiplyMonomialByMonomial(
6896 BMS_BLKMEM* blkmem, /**< block memory */
6897 SCIP_EXPRDATA_MONOMIAL* monomial, /**< monomial */
6898 SCIP_EXPRDATA_MONOMIAL* factor, /**< factor monomial */
6899 int* childmap /**< map to apply to children in factor, or NULL for 1:1 */
6900 )
6901 {
6902 assert(monomial != NULL);
6903 assert(factor != NULL);
6904
6905 if( factor->coef == 0.0 )
6906 {
6907 monomial->nfactors = 0;
6908 monomial->coef = 0.0;
6909 return SCIP_OKAY;
6910 }
6911
6912 SCIP_CALL( SCIPexprAddMonomialFactors(blkmem, monomial, factor->nfactors, factor->childidxs, factor->exponents) );
6913
6914 if( childmap != NULL )
6915 {
6916 int i;
6917 for( i = monomial->nfactors - factor->nfactors; i < monomial->nfactors; ++i )
6918 monomial->childidxs[i] = childmap[monomial->childidxs[i]];
6919 }
6920
6921 monomial->coef *= factor->coef;
6922
6923 return SCIP_OKAY;
6924 }
6925
6926 /** replaces the monomial by a power of the monomial
6927 *
6928 * Allows only integers as exponent.
6929 */
SCIPexprMonomialPower(SCIP_EXPRDATA_MONOMIAL * monomial,int exponent)6930 void SCIPexprMonomialPower(
6931 SCIP_EXPRDATA_MONOMIAL* monomial, /**< monomial */
6932 int exponent /**< integer exponent of power operation */
6933 )
6934 {
6935 int i;
6936
6937 assert(monomial != NULL);
6938
6939 if( exponent == 1 )
6940 return;
6941
6942 if( exponent == 0 )
6943 {
6944 /* x^0 = 1, unless x = 0; 0^0 = 0 */
6945 if( monomial->coef != 0.0 )
6946 monomial->coef = 1.0;
6947 monomial->nfactors = 0;
6948 return;
6949 }
6950
6951 monomial->coef = pow(monomial->coef, (SCIP_Real)exponent);
6952 for( i = 0; i < monomial->nfactors; ++i )
6953 monomial->exponents[i] *= exponent;
6954 }
6955
6956 /** merges factors that correspond to the same child by adding exponents
6957 *
6958 * Eliminates factors with exponent between -eps and eps.
6959 */
SCIPexprMergeMonomialFactors(SCIP_EXPRDATA_MONOMIAL * monomial,SCIP_Real eps)6960 void SCIPexprMergeMonomialFactors(
6961 SCIP_EXPRDATA_MONOMIAL* monomial, /**< monomial */
6962 SCIP_Real eps /**< threshold under which numbers are treated as 0.0 */
6963 )
6964 {
6965 int i;
6966 int offset;
6967
6968 assert(monomial != NULL);
6969 assert(eps >= 0.0);
6970
6971 SCIPexprSortMonomialFactors(monomial);
6972
6973 /* merge factors with same child index by adding up their exponents
6974 * delete factors with exponent 0.0 */
6975 offset = 0;
6976 i = 0;
6977 while( i + offset < monomial->nfactors )
6978 {
6979 if( offset > 0 )
6980 {
6981 assert(monomial->childidxs[i] == -1);
6982 assert(monomial->childidxs[i+offset] >= 0);
6983 monomial->childidxs[i] = monomial->childidxs[i+offset];
6984 monomial->exponents[i] = monomial->exponents[i+offset];
6985 #ifndef NDEBUG
6986 monomial->childidxs[i+offset] = -1;
6987 #endif
6988 }
6989
6990 while( i+offset+1 < monomial->nfactors && monomial->childidxs[i] == monomial->childidxs[i+offset+1] )
6991 {
6992 monomial->exponents[i] += monomial->exponents[i+offset+1];
6993 #ifndef NDEBUG
6994 monomial->childidxs[i+offset+1] = -1;
6995 #endif
6996 ++offset;
6997 }
6998
6999 if( EPSZ(monomial->exponents[i], eps) )
7000 {
7001 #ifndef NDEBUG
7002 monomial->childidxs[i] = -1;
7003 #endif
7004 ++offset;
7005 continue;
7006 }
7007 else if( EPSISINT(monomial->exponents[i], eps) )
7008 monomial->exponents[i] = EPSROUND(monomial->exponents[i], eps);
7009
7010 ++i;
7011 }
7012
7013 #ifndef NDEBUG
7014 for( ; i < monomial->nfactors; ++i )
7015 assert(monomial->childidxs[i] == -1);
7016 #endif
7017
7018 monomial->nfactors -= offset;
7019
7020 if( EPSEQ(monomial->coef, 1.0, eps) )
7021 monomial->coef = 1.0;
7022 else if( EPSEQ(monomial->coef, -1.0, eps) )
7023 monomial->coef = -1.0;
7024 }
7025
7026 /** ensures that monomials of a polynomial are sorted */
SCIPexprSortMonomials(SCIP_EXPR * expr)7027 void SCIPexprSortMonomials(
7028 SCIP_EXPR* expr /**< polynomial expression */
7029 )
7030 {
7031 assert(expr != NULL);
7032 assert(expr->op == SCIP_EXPR_POLYNOMIAL);
7033 assert(expr->data.data != NULL);
7034
7035 polynomialdataSortMonomials((SCIP_EXPRDATA_POLYNOMIAL*)expr->data.data);
7036 }
7037
7038 /** creates a monomial */
SCIPexprCreateMonomial(BMS_BLKMEM * blkmem,SCIP_EXPRDATA_MONOMIAL ** monomial,SCIP_Real coef,int nfactors,int * childidxs,SCIP_Real * exponents)7039 SCIP_RETCODE SCIPexprCreateMonomial(
7040 BMS_BLKMEM* blkmem, /**< block memory */
7041 SCIP_EXPRDATA_MONOMIAL** monomial, /**< buffer where to store pointer to new monomial */
7042 SCIP_Real coef, /**< coefficient of monomial */
7043 int nfactors, /**< number of factors in monomial */
7044 int* childidxs, /**< indices of children corresponding to factors, or NULL if identity */
7045 SCIP_Real* exponents /**< exponent in each factor, or NULL if all 1.0 */
7046 )
7047 {
7048 assert(blkmem != NULL);
7049 assert(monomial != NULL);
7050
7051 SCIP_ALLOC( BMSallocBlockMemory(blkmem, monomial) );
7052
7053 (*monomial)->coef = coef;
7054 (*monomial)->nfactors = nfactors;
7055 (*monomial)->factorssize = nfactors;
7056 (*monomial)->sorted = (nfactors <= 1);
7057
7058 if( nfactors > 0 )
7059 {
7060 if( childidxs != NULL )
7061 {
7062 SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &(*monomial)->childidxs, childidxs, nfactors) );
7063 }
7064 else
7065 {
7066 int i;
7067
7068 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &(*monomial)->childidxs, nfactors) );
7069 for( i = 0; i < nfactors; ++i )
7070 (*monomial)->childidxs[i] = i;
7071 }
7072
7073 if( exponents != NULL )
7074 {
7075 SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &(*monomial)->exponents, exponents, nfactors) );
7076 }
7077 else
7078 {
7079 int i;
7080
7081 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &(*monomial)->exponents, nfactors) );
7082 for( i = 0; i < nfactors; ++i )
7083 (*monomial)->exponents[i] = 1.0;
7084 }
7085 }
7086 else
7087 {
7088 (*monomial)->childidxs = NULL;
7089 (*monomial)->exponents = NULL;
7090 }
7091
7092 return SCIP_OKAY;
7093 }
7094
7095 /** frees a monomial */
SCIPexprFreeMonomial(BMS_BLKMEM * blkmem,SCIP_EXPRDATA_MONOMIAL ** monomial)7096 void SCIPexprFreeMonomial(
7097 BMS_BLKMEM* blkmem, /**< block memory */
7098 SCIP_EXPRDATA_MONOMIAL** monomial /**< pointer to monomial that should be freed */
7099 )
7100 {
7101 assert(blkmem != NULL);
7102 assert( monomial != NULL);
7103 assert(*monomial != NULL);
7104
7105 if( (*monomial)->factorssize > 0 )
7106 {
7107 assert((*monomial)->childidxs != NULL);
7108 assert((*monomial)->exponents != NULL);
7109
7110 BMSfreeBlockMemoryArray(blkmem, &(*monomial)->childidxs, (*monomial)->factorssize);
7111 BMSfreeBlockMemoryArray(blkmem, &(*monomial)->exponents, (*monomial)->factorssize);
7112 }
7113 assert((*monomial)->childidxs == NULL);
7114 assert((*monomial)->exponents == NULL);
7115
7116 BMSfreeBlockMemory(blkmem, monomial);
7117 }
7118
7119 /** ensures that factors in a monomial are sorted */
SCIPexprSortMonomialFactors(SCIP_EXPRDATA_MONOMIAL * monomial)7120 void SCIPexprSortMonomialFactors(
7121 SCIP_EXPRDATA_MONOMIAL* monomial /**< monomial */
7122 )
7123 {
7124 assert(monomial != NULL);
7125
7126 if( monomial->sorted )
7127 return;
7128
7129 if( monomial->nfactors > 0 )
7130 SCIPsortIntReal(monomial->childidxs, monomial->exponents, monomial->nfactors);
7131
7132 monomial->sorted = TRUE;
7133 }
7134
7135 /** finds a factor corresponding to a given child index in a monomial
7136 *
7137 * Note that if the factors have not been merged, the position of some factor corresponding to a given child is given.
7138 * Returns TRUE if a factor is found, FALSE if not.
7139 */
SCIPexprFindMonomialFactor(SCIP_EXPRDATA_MONOMIAL * monomial,int childidx,int * pos)7140 SCIP_Bool SCIPexprFindMonomialFactor(
7141 SCIP_EXPRDATA_MONOMIAL* monomial, /**< monomial */
7142 int childidx, /**< index of the child which factor to search for */
7143 int* pos /**< buffer to store position of factor */
7144 )
7145 {
7146 assert(monomial != NULL);
7147
7148 if( monomial->nfactors == 0 )
7149 return FALSE;
7150
7151 SCIPexprSortMonomialFactors(monomial);
7152
7153 return SCIPsortedvecFindInt(monomial->childidxs, childidx, monomial->nfactors, pos);
7154 }
7155
7156 /** creates a user expression */
SCIPexprCreateUser(BMS_BLKMEM * blkmem,SCIP_EXPR ** expr,int nchildren,SCIP_EXPR ** children,SCIP_USEREXPRDATA * data,SCIP_EXPRINTCAPABILITY evalcapability,SCIP_DECL_USEREXPREVAL ((* eval)),SCIP_DECL_USEREXPRINTEVAL ((* inteval)),SCIP_DECL_USEREXPRCURV ((* curv)),SCIP_DECL_USEREXPRPROP ((* prop)),SCIP_DECL_USEREXPRESTIMATE ((* estimate)),SCIP_DECL_USEREXPRCOPYDATA ((* copydata)),SCIP_DECL_USEREXPRFREEDATA ((* freedata)),SCIP_DECL_USEREXPRPRINT ((* print)))7157 SCIP_RETCODE SCIPexprCreateUser(
7158 BMS_BLKMEM* blkmem, /**< block memory data structure */
7159 SCIP_EXPR** expr, /**< pointer to buffer for expression address */
7160 int nchildren, /**< number of children */
7161 SCIP_EXPR** children, /**< children of expression */
7162 SCIP_USEREXPRDATA* data, /**< user data for expression, expression assumes ownership */
7163 SCIP_EXPRINTCAPABILITY evalcapability, /**< capability of evaluation functions (partially redundant, currently) */
7164 SCIP_DECL_USEREXPREVAL ((*eval)), /**< evaluation function */
7165 SCIP_DECL_USEREXPRINTEVAL ((*inteval)), /**< interval evaluation function, or NULL if not implemented */
7166 SCIP_DECL_USEREXPRCURV ((*curv)), /**< curvature check function */
7167 SCIP_DECL_USEREXPRPROP ((*prop)), /**< interval propagation function, or NULL if not implemented */
7168 SCIP_DECL_USEREXPRESTIMATE ((*estimate)), /**< estimation function, or NULL if convex, concave, or not implemented */
7169 SCIP_DECL_USEREXPRCOPYDATA ((*copydata)), /**< expression data copy function, or NULL if nothing to copy */
7170 SCIP_DECL_USEREXPRFREEDATA ((*freedata)), /**< expression data free function, or NULL if nothing to free */
7171 SCIP_DECL_USEREXPRPRINT ((*print)) /**< expression print function, or NULL for default string "user" */
7172 )
7173 {
7174 SCIP_EXPROPDATA opdata;
7175 SCIP_EXPRDATA_USER* userexprdata;
7176 SCIP_EXPR** childrencopy;
7177
7178 assert(blkmem != NULL);
7179 assert(expr != NULL);
7180 assert(eval != NULL);
7181 assert((evalcapability & SCIP_EXPRINTCAPABILITY_FUNCVALUE) != 0); /* the function evaluation is not optional */
7182 assert(((evalcapability & SCIP_EXPRINTCAPABILITY_INTFUNCVALUE) == 0) || inteval != NULL); /* if capability says it can do interval evaluation, then the corresponding callback needs to be provided */
7183 assert(curv != NULL);
7184 assert(copydata != NULL || data == NULL);
7185 assert(freedata != NULL || data == NULL);
7186
7187 SCIP_ALLOC( BMSallocBlockMemory(blkmem, &userexprdata) );
7188
7189 userexprdata->userdata = data;
7190 userexprdata->evalcapability = evalcapability;
7191 userexprdata->eval = eval;
7192 userexprdata->inteval = inteval;
7193 userexprdata->curv = curv;
7194 userexprdata->prop = prop;
7195 userexprdata->estimate = estimate;
7196 userexprdata->copydata = copydata;
7197 userexprdata->freedata = freedata;
7198 userexprdata->print = print;
7199
7200 opdata.data = (void*) userexprdata;
7201
7202 if( nchildren == 0 )
7203 {
7204 SCIP_CALL( exprCreate(blkmem, expr, SCIP_EXPR_USER, 0, NULL, opdata) );
7205 return SCIP_OKAY;
7206 }
7207 assert(children != NULL);
7208
7209 SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &childrencopy, children, nchildren) );
7210
7211 SCIP_CALL( exprCreate( blkmem, expr, SCIP_EXPR_USER, nchildren, childrencopy, opdata) );
7212
7213 return SCIP_OKAY;
7214 }
7215
7216 /** indicates whether the expression contains a SCIP_EXPR_PARAM */
SCIPexprHasParam(SCIP_EXPR * expr)7217 SCIP_Bool SCIPexprHasParam(
7218 SCIP_EXPR* expr /**< expression */
7219 )
7220 {
7221 int i;
7222
7223 assert(expr != NULL);
7224
7225 if( expr->op == SCIP_EXPR_PARAM )
7226 return TRUE;
7227
7228 for( i = 0; i < expr->nchildren; ++i )
7229 if( SCIPexprHasParam(expr->children[i]) )
7230 return TRUE;
7231
7232 return FALSE;
7233 }
7234
7235 /** gets maximal degree of expression, or SCIP_EXPR_DEGREEINFINITY if not a polynomial */
SCIPexprGetMaxDegree(SCIP_EXPR * expr,int * maxdegree)7236 SCIP_RETCODE SCIPexprGetMaxDegree(
7237 SCIP_EXPR* expr, /**< expression */
7238 int* maxdegree /**< buffer to store maximal degree */
7239 )
7240 {
7241 int child1;
7242 int child2;
7243
7244 assert(expr != NULL);
7245 assert(maxdegree != NULL);
7246
7247 switch( expr->op )
7248 {
7249 case SCIP_EXPR_VARIDX:
7250 *maxdegree = 1;
7251 break;
7252
7253 case SCIP_EXPR_CONST:
7254 case SCIP_EXPR_PARAM:
7255 *maxdegree = 0;
7256 break;
7257
7258 case SCIP_EXPR_PLUS:
7259 case SCIP_EXPR_MINUS:
7260 {
7261 assert(expr->children[0] != NULL);
7262 assert(expr->children[1] != NULL);
7263
7264 SCIP_CALL( SCIPexprGetMaxDegree(expr->children[0], &child1) );
7265 SCIP_CALL( SCIPexprGetMaxDegree(expr->children[1], &child2) );
7266
7267 *maxdegree = MAX(child1, child2);
7268 break;
7269 }
7270
7271 case SCIP_EXPR_MUL:
7272 {
7273 assert(expr->children[0] != NULL);
7274 assert(expr->children[1] != NULL);
7275
7276 SCIP_CALL( SCIPexprGetMaxDegree(expr->children[0], &child1) );
7277 SCIP_CALL( SCIPexprGetMaxDegree(expr->children[1], &child2) );
7278
7279 *maxdegree = child1 + child2;
7280 break;
7281 }
7282
7283 case SCIP_EXPR_DIV:
7284 {
7285 assert(expr->children[0] != NULL);
7286 assert(expr->children[1] != NULL);
7287
7288 SCIP_CALL( SCIPexprGetMaxDegree(expr->children[0], &child1) );
7289 SCIP_CALL( SCIPexprGetMaxDegree(expr->children[1], &child2) );
7290
7291 /* if not division by constant, then it is not a polynomial */
7292 *maxdegree = (child2 != 0) ? SCIP_EXPR_DEGREEINFINITY : child1;
7293 break;
7294 }
7295
7296 case SCIP_EXPR_SQUARE:
7297 {
7298 assert(expr->children[0] != NULL);
7299
7300 SCIP_CALL( SCIPexprGetMaxDegree(expr->children[0], &child1) );
7301
7302 *maxdegree = 2 * child1;
7303 break;
7304 }
7305
7306 case SCIP_EXPR_SQRT:
7307 {
7308 assert(expr->children[0] != NULL);
7309
7310 SCIP_CALL( SCIPexprGetMaxDegree(expr->children[0], &child1) );
7311
7312 /* if not squareroot of constant, then no polynomial */
7313 *maxdegree = (child1 != 0) ? SCIP_EXPR_DEGREEINFINITY : 0;
7314 break;
7315 }
7316
7317 case SCIP_EXPR_REALPOWER:
7318 {
7319 assert(expr->children[0] != NULL);
7320
7321 SCIP_CALL( SCIPexprGetMaxDegree(expr->children[0], &child1) );
7322
7323 /* constant ^ constant has degree 0 */
7324 if( child1 == 0 )
7325 {
7326 *maxdegree = 0;
7327 break;
7328 }
7329
7330 /* non-polynomial ^ constant is not a polynomial */
7331 if( child1 >= SCIP_EXPR_DEGREEINFINITY )
7332 {
7333 *maxdegree = SCIP_EXPR_DEGREEINFINITY;
7334 break;
7335 }
7336
7337 /* so it is polynomial ^ constant
7338 * let's see whether the constant is integral */
7339
7340 if( expr->data.dbl == 0.0 ) /* polynomial ^ 0 == 0 */
7341 *maxdegree = 0;
7342 else if( expr->data.dbl > 0.0 && (int)expr->data.dbl == expr->data.dbl ) /* natural exponent gives polynomial again */ /*lint !e777*/
7343 *maxdegree = child1 * (int)expr->data.dbl;
7344 else /* negative or nonintegral exponent does not give polynomial */
7345 *maxdegree = SCIP_EXPR_DEGREEINFINITY;
7346
7347 break;
7348 }
7349
7350 case SCIP_EXPR_INTPOWER:
7351 {
7352 assert(expr->children[0] != NULL);
7353
7354 SCIP_CALL( SCIPexprGetMaxDegree(expr->children[0], &child1) );
7355
7356 /* constant ^ integer or something ^ 0 has degree 0 */
7357 if( child1 == 0 || expr->data.intval == 0 )
7358 {
7359 *maxdegree = 0;
7360 break;
7361 }
7362
7363 /* non-polynomial ^ integer or something ^ negative is not a polynomial */
7364 if( child1 >= SCIP_EXPR_DEGREEINFINITY || expr->data.intval < 0 )
7365 {
7366 *maxdegree = SCIP_EXPR_DEGREEINFINITY;
7367 break;
7368 }
7369
7370 /* so it is polynomial ^ natural, which gives a polynomial again */
7371 *maxdegree = child1 * expr->data.intval;
7372
7373 break;
7374 }
7375
7376 case SCIP_EXPR_SIGNPOWER:
7377 {
7378 assert(expr->children[0] != NULL);
7379
7380 SCIP_CALL( SCIPexprGetMaxDegree(expr->children[0], &child1) );
7381
7382 /* if child is not constant, then it is no polynomial */
7383 *maxdegree = child1 != 0 ? SCIP_EXPR_DEGREEINFINITY : 0;
7384 break;
7385 }
7386
7387 case SCIP_EXPR_EXP:
7388 case SCIP_EXPR_LOG:
7389 case SCIP_EXPR_SIN:
7390 case SCIP_EXPR_COS:
7391 case SCIP_EXPR_TAN:
7392 /* case SCIP_EXPR_ERF: */
7393 /* case SCIP_EXPR_ERFI: */
7394 case SCIP_EXPR_ABS:
7395 case SCIP_EXPR_SIGN:
7396 case SCIP_EXPR_USER:
7397 {
7398 assert(expr->children[0] != NULL);
7399
7400 SCIP_CALL( SCIPexprGetMaxDegree(expr->children[0], &child1) );
7401
7402 /* if argument is not a constant, then no polynomial, otherwise it is a constant */
7403 *maxdegree = (child1 != 0) ? SCIP_EXPR_DEGREEINFINITY : 0;
7404 break;
7405 }
7406
7407 case SCIP_EXPR_MIN:
7408 case SCIP_EXPR_MAX:
7409 {
7410 assert(expr->children[0] != NULL);
7411 assert(expr->children[1] != NULL);
7412
7413 SCIP_CALL( SCIPexprGetMaxDegree(expr->children[0], &child1) );
7414 SCIP_CALL( SCIPexprGetMaxDegree(expr->children[1], &child2) );
7415
7416 /* if any of the operands is not constant, then it is no polynomial */
7417 *maxdegree = (child1 != 0 || child2 != 0) ? SCIP_EXPR_DEGREEINFINITY : 0;
7418 break;
7419 }
7420
7421 case SCIP_EXPR_SUM:
7422 case SCIP_EXPR_LINEAR:
7423 {
7424 int i;
7425
7426 *maxdegree = 0;
7427 for( i = 0; i < expr->nchildren && *maxdegree < SCIP_EXPR_DEGREEINFINITY; ++i )
7428 {
7429 SCIP_CALL( SCIPexprGetMaxDegree(expr->children[i], &child1) );
7430 if( child1 > *maxdegree )
7431 *maxdegree = child1;
7432 }
7433
7434 break;
7435 }
7436
7437 case SCIP_EXPR_PRODUCT:
7438 {
7439 int i;
7440
7441 *maxdegree = 0;
7442 for( i = 0; i < expr->nchildren; ++i )
7443 {
7444 SCIP_CALL( SCIPexprGetMaxDegree(expr->children[i], &child1) );
7445 if( child1 >= SCIP_EXPR_DEGREEINFINITY )
7446 {
7447 *maxdegree = SCIP_EXPR_DEGREEINFINITY;
7448 break;
7449 }
7450 *maxdegree += child1;
7451 }
7452
7453 break;
7454 }
7455
7456 case SCIP_EXPR_QUADRATIC:
7457 {
7458 SCIP_EXPRDATA_QUADRATIC* quadraticdata;
7459 int childidx;
7460 int quadidx;
7461
7462 quadraticdata = (SCIP_EXPRDATA_QUADRATIC*)expr->data.data;
7463
7464 /* make sure quadratic elements are sorted */
7465 quadraticdataSort(quadraticdata);
7466
7467 *maxdegree = 0;
7468 quadidx = 0;
7469 for( childidx = 0; childidx < expr->nchildren; ++childidx )
7470 {
7471 /* if no linear or no quadratic coefficient with current child on first position, then nothing to do */
7472 if( (quadraticdata->lincoefs == NULL || quadraticdata->lincoefs[childidx] == 0.0) &&
7473 (quadidx < quadraticdata->nquadelems && quadraticdata->quadelems[quadidx].idx1 > childidx) )
7474 continue;
7475
7476 SCIP_CALL( SCIPexprGetMaxDegree(expr->children[childidx], &child1) );
7477 if( child1 == SCIP_EXPR_DEGREEINFINITY )
7478 {
7479 *maxdegree = SCIP_EXPR_DEGREEINFINITY;
7480 break;
7481 }
7482
7483 while( quadidx < quadraticdata->nquadelems && quadraticdata->quadelems[quadidx].idx1 == childidx )
7484 {
7485 if( quadraticdata->quadelems[quadidx].idx2 == childidx )
7486 {
7487 /* square term */
7488 if( 2*child1 > *maxdegree )
7489 *maxdegree = 2*child1;
7490 }
7491 else
7492 {
7493 /* bilinear term */
7494 SCIP_CALL( SCIPexprGetMaxDegree(expr->children[quadraticdata->quadelems[quadidx].idx2], &child2) );
7495 if( child2 == SCIP_EXPR_DEGREEINFINITY )
7496 {
7497 *maxdegree = SCIP_EXPR_DEGREEINFINITY;
7498 break;
7499 }
7500 if( child1 + child2 > *maxdegree )
7501 *maxdegree = child1 + child2;
7502 }
7503 ++quadidx;
7504 }
7505 if( *maxdegree == SCIP_EXPR_DEGREEINFINITY )
7506 break;
7507 }
7508
7509 break;
7510 }
7511
7512 case SCIP_EXPR_POLYNOMIAL:
7513 {
7514 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata;
7515 SCIP_EXPRDATA_MONOMIAL* monomialdata;
7516 int monomialdegree;
7517 int i;
7518 int j;
7519
7520 polynomialdata = (SCIP_EXPRDATA_POLYNOMIAL*)expr->data.data;
7521
7522 *maxdegree = 0;
7523 for( i = 0; i < polynomialdata->nmonomials && *maxdegree < SCIP_EXPR_DEGREEINFINITY; ++i )
7524 {
7525 monomialdata = polynomialdata->monomials[i];
7526 assert(monomialdata != NULL);
7527
7528 /* compute degree of monomial = sum of degree of factors */
7529 monomialdegree = 0;
7530 for( j = 0; j < monomialdata->nfactors; ++j )
7531 {
7532 SCIP_CALL( SCIPexprGetMaxDegree(expr->children[monomialdata->childidxs[j]], &child1) );
7533
7534 /* if the exponent of the factor is not a natural number and the child is not constant (degree 0),
7535 * then we report that we are not really a polynomial */
7536 if( child1 != 0 && (monomialdata->exponents[j] < 0.0 || (int)monomialdata->exponents[j] != monomialdata->exponents[j]) )
7537 {
7538 *maxdegree = SCIP_EXPR_DEGREEINFINITY;
7539 break;
7540 }
7541
7542 monomialdegree += child1 * (int)monomialdata->exponents[j];
7543 }
7544
7545 if( monomialdegree > *maxdegree )
7546 *maxdegree = monomialdegree;
7547 }
7548
7549 break;
7550 }
7551
7552 case SCIP_EXPR_LAST:
7553 SCIPABORT();
7554 break;
7555 }
7556
7557 return SCIP_OKAY;
7558 }
7559
7560 /** counts usage of variables in expression */
SCIPexprGetVarsUsage(SCIP_EXPR * expr,int * varsusage)7561 void SCIPexprGetVarsUsage(
7562 SCIP_EXPR* expr, /**< expression to update */
7563 int* varsusage /**< array with counters of variable usage */
7564 )
7565 {
7566 int i;
7567
7568 assert(expr != NULL);
7569 assert(varsusage != NULL);
7570
7571 if( expr->op == SCIP_EXPR_VARIDX )
7572 {
7573 ++varsusage[expr->data.intval];
7574 }
7575
7576 for( i = 0; i < expr->nchildren; ++i )
7577 SCIPexprGetVarsUsage(expr->children[i], varsusage);
7578 }
7579
7580 /** compares whether two expressions are the same
7581 *
7582 * Inconclusive, i.e., may give FALSE even if expressions are equivalent (x*y != y*x).
7583 */
SCIPexprAreEqual(SCIP_EXPR * expr1,SCIP_EXPR * expr2,SCIP_Real eps)7584 SCIP_Bool SCIPexprAreEqual(
7585 SCIP_EXPR* expr1, /**< first expression */
7586 SCIP_EXPR* expr2, /**< second expression */
7587 SCIP_Real eps /**< threshold under which numbers are assumed to be zero */
7588 )
7589 {
7590 assert(expr1 != NULL);
7591 assert(expr2 != NULL);
7592
7593 if( expr1 == expr2 )
7594 return TRUE;
7595
7596 if( expr1->op != expr2->op )
7597 return FALSE;
7598
7599 switch( expr1->op )
7600 {
7601 case SCIP_EXPR_VARIDX:
7602 case SCIP_EXPR_PARAM:
7603 return expr1->data.intval == expr2->data.intval;
7604
7605 case SCIP_EXPR_CONST:
7606 return EPSEQ(expr1->data.dbl, expr2->data.dbl, eps);
7607
7608 /* operands with two children */
7609 case SCIP_EXPR_PLUS :
7610 case SCIP_EXPR_MINUS :
7611 case SCIP_EXPR_MUL :
7612 case SCIP_EXPR_DIV :
7613 case SCIP_EXPR_MIN :
7614 case SCIP_EXPR_MAX :
7615 return SCIPexprAreEqual(expr1->children[0], expr2->children[0], eps) && SCIPexprAreEqual(expr1->children[1], expr2->children[1], eps);
7616
7617 /* operands with one child */
7618 case SCIP_EXPR_SQUARE:
7619 case SCIP_EXPR_SQRT :
7620 case SCIP_EXPR_EXP :
7621 case SCIP_EXPR_LOG :
7622 case SCIP_EXPR_SIN :
7623 case SCIP_EXPR_COS :
7624 case SCIP_EXPR_TAN :
7625 /* case SCIP_EXPR_ERF : */
7626 /* case SCIP_EXPR_ERFI : */
7627 case SCIP_EXPR_ABS :
7628 case SCIP_EXPR_SIGN :
7629 return SCIPexprAreEqual(expr1->children[0], expr2->children[0], eps);
7630
7631 case SCIP_EXPR_REALPOWER:
7632 case SCIP_EXPR_SIGNPOWER:
7633 return EPSEQ(expr1->data.dbl, expr2->data.dbl, eps) && SCIPexprAreEqual(expr1->children[0], expr2->children[0], eps);
7634
7635 case SCIP_EXPR_INTPOWER:
7636 return expr1->data.intval == expr2->data.intval && SCIPexprAreEqual(expr1->children[0], expr2->children[0], eps);
7637
7638 /* complex operands */
7639 case SCIP_EXPR_SUM :
7640 case SCIP_EXPR_PRODUCT:
7641 {
7642 int i;
7643
7644 /* @todo sort children and have sorted flag in data? */
7645
7646 if( expr1->nchildren != expr2->nchildren )
7647 return FALSE;
7648
7649 for( i = 0; i < expr1->nchildren; ++i )
7650 {
7651 if( !SCIPexprAreEqual(expr1->children[i], expr2->children[i], eps) )
7652 return FALSE;
7653 }
7654
7655 return TRUE;
7656 }
7657
7658 case SCIP_EXPR_LINEAR :
7659 {
7660 SCIP_Real* data1;
7661 SCIP_Real* data2;
7662 int i;
7663
7664 /* @todo sort children and have sorted flag in data? */
7665
7666 if( expr1->nchildren != expr2->nchildren )
7667 return FALSE;
7668
7669 data1 = (SCIP_Real*)expr1->data.data;
7670 data2 = (SCIP_Real*)expr2->data.data;
7671
7672 /* check if constant and coefficients are equal */
7673 for( i = 0; i < expr1->nchildren + 1; ++i )
7674 if( !EPSEQ(data1[i], data2[i], eps) )
7675 return FALSE;
7676
7677 /* check if children are equal */
7678 for( i = 0; i < expr1->nchildren; ++i )
7679 {
7680 if( !SCIPexprAreEqual(expr1->children[i], expr2->children[i], eps) )
7681 return FALSE;
7682 }
7683
7684 return TRUE;
7685 }
7686
7687 case SCIP_EXPR_QUADRATIC:
7688 {
7689 SCIP_EXPRDATA_QUADRATIC* data1;
7690 SCIP_EXPRDATA_QUADRATIC* data2;
7691 int i;
7692
7693 if( expr1->nchildren != expr2->nchildren )
7694 return FALSE;
7695
7696 data1 = (SCIP_EXPRDATA_QUADRATIC*)expr1->data.data;
7697 data2 = (SCIP_EXPRDATA_QUADRATIC*)expr2->data.data;
7698
7699 if( data1->nquadelems != data2->nquadelems )
7700 return FALSE;
7701
7702 if( !EPSEQ(data1->constant, data2->constant, eps) )
7703 return FALSE;
7704
7705 /* check if linear part is equal */
7706 if( data1->lincoefs != NULL || data2->lincoefs != NULL )
7707 for( i = 0; i < expr1->nchildren; ++i )
7708 {
7709 if( data1->lincoefs == NULL )
7710 {
7711 if( !EPSZ(data2->lincoefs[i], eps) )
7712 return FALSE;
7713 }
7714 else if( data2->lincoefs == NULL )
7715 {
7716 if( !EPSZ(data1->lincoefs[i], eps) )
7717 return FALSE;
7718 }
7719 else if( !EPSEQ(data1->lincoefs[i], data2->lincoefs[i], eps) )
7720 return FALSE;
7721 }
7722
7723 SCIPexprSortQuadElems(expr1);
7724 SCIPexprSortQuadElems(expr2);
7725
7726 /* check if quadratic elements are equal */
7727 for( i = 0; i < data1->nquadelems; ++i )
7728 if( data1->quadelems[i].idx1 != data2->quadelems[i].idx1 ||
7729 data1->quadelems[i].idx2 != data2->quadelems[i].idx2 ||
7730 !EPSEQ(data1->quadelems[i].coef, data2->quadelems[i].coef, eps) )
7731 return FALSE;
7732
7733 /* check if children are equal */
7734 for( i = 0; i < expr1->nchildren; ++i )
7735 if( !SCIPexprAreEqual(expr1->children[i], expr2->children[i], eps) )
7736 return FALSE;
7737
7738 return TRUE;
7739 }
7740
7741 case SCIP_EXPR_POLYNOMIAL:
7742 {
7743 SCIP_EXPRDATA_POLYNOMIAL* data1;
7744 SCIP_EXPRDATA_POLYNOMIAL* data2;
7745 int i;
7746
7747 if( expr1->nchildren != expr2->nchildren )
7748 return FALSE;
7749
7750 data1 = (SCIP_EXPRDATA_POLYNOMIAL*)expr1->data.data;
7751 data2 = (SCIP_EXPRDATA_POLYNOMIAL*)expr2->data.data;
7752
7753 if( data1->nmonomials != data2->nmonomials )
7754 return FALSE;
7755
7756 if( !EPSEQ(data1->constant, data2->constant, eps) )
7757 return FALSE;
7758
7759 /* make sure polynomials are sorted */
7760 SCIPexprSortMonomials(expr1);
7761 SCIPexprSortMonomials(expr2);
7762
7763 /* check if monomials are equal */
7764 for( i = 0; i < data1->nmonomials; ++i )
7765 {
7766 if( !SCIPexprAreMonomialsEqual(data1->monomials[i], data2->monomials[i], eps) )
7767 return FALSE;
7768 }
7769
7770 /* check if children are equal */
7771 for( i = 0; i < expr1->nchildren; ++i )
7772 {
7773 if( !SCIPexprAreEqual(expr1->children[i], expr2->children[i], eps) )
7774 return FALSE;
7775 }
7776
7777 return TRUE;
7778 }
7779
7780 case SCIP_EXPR_USER:
7781 {
7782 /* @todo could implement this via another user callback */
7783 return FALSE;
7784 }
7785
7786 case SCIP_EXPR_LAST:
7787 break;
7788 }
7789
7790 SCIPerrorMessage("this should never happen\n");
7791 SCIPABORT();
7792 return FALSE; /*lint !e527*/
7793 }
7794
7795 /** aims at simplifying an expression and splitting of a linear expression
7796 *
7797 * If linear variables are split off, expression interpreter data, if stored in the tree, is freed.
7798 */
SCIPexprSimplify(BMS_BLKMEM * blkmem,SCIP_MESSAGEHDLR * messagehdlr,SCIP_EXPR * expr,SCIP_Real eps,int maxexpansionexponent,int nvars,int * nlinvars,int * linidxs,SCIP_Real * lincoefs)7799 SCIP_RETCODE SCIPexprSimplify(
7800 BMS_BLKMEM* blkmem, /**< block memory data structure */
7801 SCIP_MESSAGEHDLR* messagehdlr, /**< message handler */
7802 SCIP_EXPR* expr, /**< expression */
7803 SCIP_Real eps, /**< threshold, under which positive values are treat as 0 */
7804 int maxexpansionexponent,/**< maximal exponent for which we still expand non-monomial polynomials */
7805 int nvars, /**< number of variables in expression */
7806 int* nlinvars, /**< buffer to store number of linear variables in linear part, or NULL if linear part should not be separated */
7807 int* linidxs, /**< array to store indices of variables in expression tree which belong to linear part, or NULL */
7808 SCIP_Real* lincoefs /**< array to store coefficients of linear part, or NULL */
7809 )
7810 {
7811 assert(blkmem != NULL);
7812 assert(expr != NULL);
7813 assert(eps >= 0.0);
7814
7815 SCIPdebugMessage("simplify expression: ");
7816 SCIPdebug( SCIPexprPrint(expr, messagehdlr, NULL, NULL, NULL, NULL) );
7817 SCIPdebugPrintf("\n");
7818
7819 SCIP_CALL( exprsimplifyConvertToPolynomials(blkmem, expr) );
7820
7821 SCIPdebugMessage("converted to polynomials: ");
7822 SCIPdebug( SCIPexprPrint(expr, messagehdlr, NULL, NULL, NULL, NULL) );
7823 SCIPdebugPrintf("\n");
7824
7825 SCIP_CALL( exprsimplifyFlattenPolynomials(blkmem, messagehdlr, expr, eps, maxexpansionexponent) );
7826
7827 SCIPdebugMessage("polynomials flattened: ");
7828 SCIPdebug( SCIPexprPrint(expr, messagehdlr, NULL, NULL, NULL, NULL) );
7829 SCIPdebugPrintf("\n");
7830
7831 if( nlinvars != NULL )
7832 {
7833 /* separate linear part from root polynomial */
7834 SCIP_CALL( exprsimplifySeparateLinearFromPolynomial(blkmem, expr, eps, nvars, nlinvars, linidxs, lincoefs) );
7835
7836 SCIPdebugMessage("separated linear part: ");
7837 SCIPdebug( SCIPexprPrint(expr, messagehdlr, NULL, NULL, NULL, NULL) );
7838 SCIPdebugPrintf("\n");
7839 }
7840
7841 SCIP_CALL( exprsimplifyUnconvertPolynomials(blkmem, expr) );
7842
7843 SCIPdebugMessage("converted back from polynomials: ");
7844 SCIPdebug( SCIPexprPrint(expr, messagehdlr, NULL, NULL, NULL, NULL) );
7845 SCIPdebugPrintf("\n");
7846
7847 return SCIP_OKAY;
7848 }
7849
7850 /** evaluates an expression w.r.t. given values for children expressions */
SCIPexprEvalShallow(SCIP_EXPR * expr,SCIP_Real * argvals,SCIP_Real * varvals,SCIP_Real * param,SCIP_Real * val)7851 SCIP_RETCODE SCIPexprEvalShallow(
7852 SCIP_EXPR* expr, /**< expression */
7853 SCIP_Real* argvals, /**< values for children, can be NULL if the expression has no children */
7854 SCIP_Real* varvals, /**< values for variables, can be NULL if the expression operand is not a variable */
7855 SCIP_Real* param, /**< values for parameters, can be NULL if the expression operand is not a parameter */
7856 SCIP_Real* val /**< buffer to store value */
7857 )
7858 {
7859 assert(expr != NULL);
7860 assert(argvals != NULL || expr->nchildren == 0);
7861
7862 /* evaluate this expression */
7863 assert( exprOpTable[expr->op].eval != NULL );
7864 SCIP_CALL( exprOpTable[expr->op].eval(expr->data, expr->nchildren, argvals, varvals, param, val) );
7865
7866 return SCIP_OKAY;
7867 }
7868
7869 /** evaluates an expression w.r.t. a point */
SCIPexprEval(SCIP_EXPR * expr,SCIP_Real * varvals,SCIP_Real * param,SCIP_Real * val)7870 SCIP_RETCODE SCIPexprEval(
7871 SCIP_EXPR* expr, /**< expression */
7872 SCIP_Real* varvals, /**< values for variables, can be NULL if the expression is constant */
7873 SCIP_Real* param, /**< values for parameters, can be NULL if the expression is not parameterized */
7874 SCIP_Real* val /**< buffer to store value */
7875 )
7876 {
7877 int i;
7878 SCIP_Real staticbuf[SCIP_EXPRESSION_MAXCHILDEST];
7879 SCIP_Real* buf;
7880
7881 /* if many children, get large enough memory to store argument values */
7882 if( expr->nchildren > SCIP_EXPRESSION_MAXCHILDEST )
7883 {
7884 SCIP_ALLOC( BMSallocMemoryArray(&buf, expr->nchildren) );
7885 }
7886 else
7887 {
7888 buf = staticbuf;
7889 }
7890
7891 /* evaluate children */
7892 for( i = 0; i < expr->nchildren; ++i )
7893 {
7894 SCIP_CALL( SCIPexprEval(expr->children[i], varvals, param, &buf[i]) ); /*lint !e644*/
7895 }
7896
7897 /* evaluate this expression */
7898 assert( exprOpTable[expr->op].eval != NULL );
7899 SCIP_CALL( exprOpTable[expr->op].eval(expr->data, expr->nchildren, buf, varvals, param, val) );
7900
7901 /* free memory, if allocated before */
7902 if( staticbuf != buf )
7903 {
7904 BMSfreeMemoryArray(&buf);
7905 }
7906
7907 return SCIP_OKAY;
7908 }
7909
7910 /** evaluates an expression w.r.t. given interval values for children expressions */
SCIPexprEvalIntShallow(SCIP_EXPR * expr,SCIP_Real infinity,SCIP_INTERVAL * argvals,SCIP_INTERVAL * varvals,SCIP_Real * param,SCIP_INTERVAL * val)7911 SCIP_RETCODE SCIPexprEvalIntShallow(
7912 SCIP_EXPR* expr, /**< expression */
7913 SCIP_Real infinity, /**< value to use for infinity */
7914 SCIP_INTERVAL* argvals, /**< interval values for children, can be NULL if the expression has no children */
7915 SCIP_INTERVAL* varvals, /**< interval values for variables, can be NULL if the expression is constant */
7916 SCIP_Real* param, /**< values for parameters, can be NULL if the expression is not parameterized */
7917 SCIP_INTERVAL* val /**< buffer to store value */
7918 )
7919 {
7920 assert(expr != NULL);
7921 assert(argvals != NULL || expr->nchildren == 0);
7922
7923 /* evaluate this expression */
7924 assert( exprOpTable[expr->op].inteval != NULL );
7925 SCIP_CALL( exprOpTable[expr->op].inteval(infinity, expr->data, expr->nchildren, argvals, varvals, param, val) );
7926
7927 return SCIP_OKAY;
7928 }
7929
7930 /** evaluates an expression w.r.t. an interval */
SCIPexprEvalInt(SCIP_EXPR * expr,SCIP_Real infinity,SCIP_INTERVAL * varvals,SCIP_Real * param,SCIP_INTERVAL * val)7931 SCIP_RETCODE SCIPexprEvalInt(
7932 SCIP_EXPR* expr, /**< expression */
7933 SCIP_Real infinity, /**< value to use for infinity */
7934 SCIP_INTERVAL* varvals, /**< interval values for variables, can be NULL if the expression is constant */
7935 SCIP_Real* param, /**< values for parameters, can be NULL if the expression is not parameterized */
7936 SCIP_INTERVAL* val /**< buffer to store value */
7937 )
7938 {
7939 int i;
7940 SCIP_INTERVAL staticbuf[SCIP_EXPRESSION_MAXCHILDEST];
7941 SCIP_INTERVAL* buf;
7942
7943 /* if many children, get large enough memory to store argument values */
7944 if( expr->nchildren > SCIP_EXPRESSION_MAXCHILDEST )
7945 {
7946 SCIP_ALLOC( BMSallocMemoryArray(&buf, expr->nchildren) );
7947 }
7948 else
7949 {
7950 buf = staticbuf;
7951 }
7952
7953 /* evaluate children */
7954 for( i = 0; i < expr->nchildren; ++i )
7955 {
7956 SCIP_CALL( SCIPexprEvalInt(expr->children[i], infinity, varvals, param, &buf[i]) ); /*lint !e644*/
7957 }
7958
7959 /* evaluate this expression */
7960 assert( exprOpTable[expr->op].inteval != NULL );
7961 SCIP_CALL( exprOpTable[expr->op].inteval(infinity, expr->data, expr->nchildren, buf, varvals, param, val) );
7962
7963 /* free memory, if allocated before */
7964 if( staticbuf != buf )
7965 {
7966 BMSfreeMemoryArray(&buf);
7967 }
7968
7969 return SCIP_OKAY;
7970 }
7971
7972 /** evaluates a user expression w.r.t. given values for children expressions */
SCIPexprEvalUser(SCIP_EXPR * expr,SCIP_Real * argvals,SCIP_Real * val,SCIP_Real * gradient,SCIP_Real * hessian)7973 SCIP_RETCODE SCIPexprEvalUser(
7974 SCIP_EXPR* expr, /**< expression */
7975 SCIP_Real* argvals, /**< values for children */
7976 SCIP_Real* val, /**< buffer to store function value */
7977 SCIP_Real* gradient, /**< buffer to store gradient values, or NULL if not requested */
7978 SCIP_Real* hessian /**< buffer to store values of full Hessian, or NULL if not requested */
7979 )
7980 {
7981 SCIP_EXPRDATA_USER* exprdata;
7982
7983 assert(expr != NULL);
7984 assert(expr->op == SCIP_EXPR_USER);
7985 assert(argvals != NULL || expr->nchildren == 0);
7986
7987 exprdata = (SCIP_EXPRDATA_USER*) expr->data.data;
7988 assert(exprdata->eval != NULL);
7989
7990 SCIP_CALL( exprdata->eval(exprdata->userdata, expr->nchildren, argvals, val, gradient, hessian) );
7991
7992 return SCIP_OKAY;
7993 }
7994
7995 /** evaluates a user expression w.r.t. an interval */
SCIPexprEvalIntUser(SCIP_EXPR * expr,SCIP_Real infinity,SCIP_INTERVAL * argvals,SCIP_INTERVAL * val,SCIP_INTERVAL * gradient,SCIP_INTERVAL * hessian)7996 SCIP_RETCODE SCIPexprEvalIntUser(
7997 SCIP_EXPR* expr, /**< expression */
7998 SCIP_Real infinity, /**< value to use for infinity */
7999 SCIP_INTERVAL* argvals, /**< values for children */
8000 SCIP_INTERVAL* val, /**< buffer to store value */
8001 SCIP_INTERVAL* gradient, /**< buffer to store gradient values, or NULL if not requested */
8002 SCIP_INTERVAL* hessian /**< buffer to store values of full Hessian, or NULL if not requested */
8003 )
8004 {
8005 SCIP_EXPRDATA_USER* exprdata;
8006
8007 assert(expr != NULL);
8008 assert(expr->op == SCIP_EXPR_USER);
8009 assert(argvals != NULL || expr->nchildren == 0);
8010
8011 exprdata = (SCIP_EXPRDATA_USER*) expr->data.data;
8012
8013 if( exprdata->inteval == NULL )
8014 {
8015 int i;
8016
8017 for( i = 0; i < expr->nchildren; ++i )
8018 SCIPintervalSetEntire(infinity, &argvals[i]); /*lint !e613*/
8019 }
8020 else
8021 {
8022 SCIP_CALL( exprdata->inteval(infinity, exprdata->userdata, expr->nchildren, argvals, val, gradient, hessian) );
8023 }
8024
8025 return SCIP_OKAY;
8026 }
8027
8028 /** internal curvature check method */
8029 static
doCheckCurvature(SCIP_EXPR * expr,SCIP_Real infinity,SCIP_INTERVAL * varbounds,SCIP_INTERVAL * childbounds,SCIP_Real * param,SCIP_EXPRCURV * curv,SCIP_EXPRCURV * childcurv,SCIP_INTERVAL * bounds)8030 SCIP_RETCODE doCheckCurvature(
8031 SCIP_EXPR* expr, /**< expression to check */
8032 SCIP_Real infinity, /**< value to use for infinity */
8033 SCIP_INTERVAL* varbounds, /**< domains of variables */
8034 SCIP_INTERVAL* childbounds, /**< child bounds buffer array */
8035 SCIP_Real* param, /**< values for parameters, can be NULL if the expression is not parameterized */
8036 SCIP_EXPRCURV* curv, /**< buffer to store curvature of expression */
8037 SCIP_EXPRCURV* childcurv, /**< buffer array for curvature of children */
8038 SCIP_INTERVAL* bounds /**< buffer to store bounds on expression */
8039 )
8040 {
8041 int i;
8042
8043 assert(childbounds != NULL);
8044 assert(childcurv != NULL);
8045
8046 /* check curvature and compute bounds of children
8047 * constant children can be considered as always linear */
8048 for( i = 0; i < expr->nchildren; ++i )
8049 {
8050 SCIP_CALL( SCIPexprCheckCurvature(expr->children[i], infinity, varbounds, param, &childcurv[i], &childbounds[i]) ); /*lint !e644*/
8051 if( childbounds[i].inf == childbounds[i].sup ) /*lint !e777*/
8052 childcurv[i] = SCIP_EXPRCURV_LINEAR;
8053 }
8054
8055 /* get curvature and bounds of expr */
8056 assert(exprOpTable[expr->op].curv != NULL);
8057 assert(exprOpTable[expr->op].inteval != NULL);
8058
8059 SCIP_CALL( exprOpTable[expr->op].curv(infinity, expr->data, expr->nchildren, childbounds, childcurv, curv) );
8060 SCIP_CALL( exprOpTable[expr->op].inteval(infinity, expr->data, expr->nchildren, childbounds, varbounds, param, bounds) );
8061
8062 return SCIP_OKAY;
8063 }
8064
8065 /** tries to determine the curvature type of an expression w.r.t. given variable domains */
SCIPexprCheckCurvature(SCIP_EXPR * expr,SCIP_Real infinity,SCIP_INTERVAL * varbounds,SCIP_Real * param,SCIP_EXPRCURV * curv,SCIP_INTERVAL * bounds)8066 SCIP_RETCODE SCIPexprCheckCurvature(
8067 SCIP_EXPR* expr, /**< expression to check */
8068 SCIP_Real infinity, /**< value to use for infinity */
8069 SCIP_INTERVAL* varbounds, /**< domains of variables */
8070 SCIP_Real* param, /**< values for parameters, can be NULL if the expression is not parameterized */
8071 SCIP_EXPRCURV* curv, /**< buffer to store curvature of expression */
8072 SCIP_INTERVAL* bounds /**< buffer to store bounds on expression */
8073 )
8074 {
8075 SCIP_INTERVAL childboundsbuf[SCIP_EXPRESSION_MAXCHILDEST];
8076 SCIP_INTERVAL* childbounds = NULL;
8077 SCIP_EXPRCURV childcurvbuf[SCIP_EXPRESSION_MAXCHILDEST];
8078 SCIP_EXPRCURV* childcurv = NULL;
8079 SCIP_RETCODE retcode = SCIP_OKAY;
8080
8081 assert(expr != NULL);
8082 assert(curv != NULL);
8083 assert(bounds != NULL);
8084
8085 /* if many children, get large enough memory to store argument values */
8086 if( expr->nchildren > SCIP_EXPRESSION_MAXCHILDEST )
8087 {
8088 SCIP_ALLOC( BMSallocMemoryArray(&childbounds, expr->nchildren) );
8089 SCIP_ALLOC_TERMINATE( retcode, BMSallocMemoryArray(&childcurv, expr->nchildren), TERMINATE );
8090 }
8091 else
8092 {
8093 childbounds = childboundsbuf;
8094 childcurv = childcurvbuf;
8095 }
8096
8097 retcode = doCheckCurvature(expr, infinity, varbounds, childbounds, param, curv, childcurv, bounds);
8098
8099 TERMINATE:
8100 /* free memory, if allocated before */
8101 if( childboundsbuf != childbounds )
8102 {
8103 BMSfreeMemoryArrayNull(&childcurv);
8104 BMSfreeMemoryArrayNull(&childbounds);
8105 }
8106
8107 return retcode;
8108 }
8109
8110 /** under-/overestimates a user expression w.r.t. to given values and bounds for children expressions */
SCIPexprEstimateUser(SCIP_EXPR * expr,SCIP_Real infinity,SCIP_Real * argvals,SCIP_INTERVAL * argbounds,SCIP_Bool overestimate,SCIP_Real * coeffs,SCIP_Real * constant,SCIP_Bool * success)8111 SCIP_RETCODE SCIPexprEstimateUser(
8112 SCIP_EXPR* expr, /**< expression */
8113 SCIP_Real infinity, /**< value to use for infinity */
8114 SCIP_Real* argvals, /**< values for children */
8115 SCIP_INTERVAL* argbounds, /**< bounds for children */
8116 SCIP_Bool overestimate, /**< whether to overestimate the expression */
8117 SCIP_Real* coeffs, /**< buffer to store the linear coefficients for each child expression that gives a valid under-/overestimator */
8118 SCIP_Real* constant, /**< buffer to store the constant value of the linear under-/overestimator */
8119 SCIP_Bool* success /**< buffer to store whether an estimator was successfully computed */
8120 )
8121 {
8122 SCIP_EXPRDATA_USER* exprdata;
8123
8124 assert(expr != NULL);
8125 assert(expr->op == SCIP_EXPR_USER);
8126 assert(argvals != NULL || expr->nchildren == 0);
8127 assert(argbounds != NULL || expr->nchildren == 0);
8128
8129 exprdata = (SCIP_EXPRDATA_USER*) expr->data.data;
8130
8131 if( exprdata->estimate != NULL )
8132 {
8133 SCIP_CALL( exprdata->estimate(infinity, exprdata->userdata, expr->nchildren, argvals, argbounds, overestimate, coeffs, constant, success ) );
8134 }
8135 else
8136 {
8137 *success = FALSE;
8138 }
8139
8140 return SCIP_OKAY;
8141 }
8142
8143 /** substitutes variables (SCIP_EXPR_VARIDX) by expressions
8144 *
8145 * Note that only the children of the given expr are checked!
8146 * A variable with index i is replaced by a copy of substexprs[i], if the latter is not NULL.
8147 * If substexprs[i] == NULL, then the variable expression i is not touched.
8148 */
SCIPexprSubstituteVars(BMS_BLKMEM * blkmem,SCIP_EXPR * expr,SCIP_EXPR ** substexprs)8149 SCIP_RETCODE SCIPexprSubstituteVars(
8150 BMS_BLKMEM* blkmem, /**< block memory data structure */
8151 SCIP_EXPR* expr, /**< expression, which of the children may be replaced */
8152 SCIP_EXPR** substexprs /**< array of substitute expressions; single entries can be NULL */
8153 )
8154 {
8155 int i;
8156
8157 assert(blkmem != NULL);
8158 assert(expr != NULL);
8159 assert(substexprs != NULL);
8160
8161 for( i = 0; i < expr->nchildren; ++i )
8162 {
8163 if( expr->children[i]->op == SCIP_EXPR_VARIDX )
8164 {
8165 int varidx;
8166 varidx = expr->children[i]->data.intval;
8167
8168 assert(varidx >= 0);
8169 if( substexprs[varidx] != NULL )
8170 {
8171 /* replace child i by copy of substexprs[expr->children[i]->opdata.intval] */
8172 SCIPexprFreeDeep(blkmem, &expr->children[i]);
8173 SCIP_CALL( SCIPexprCopyDeep(blkmem, &expr->children[i], substexprs[varidx]) );
8174 }
8175 }
8176 else
8177 {
8178 /* call recursively */
8179 SCIP_CALL( SCIPexprSubstituteVars(blkmem, expr->children[i], substexprs) );
8180 }
8181 }
8182
8183 return SCIP_OKAY;
8184 }
8185
8186 /** updates variable indices in expression tree */
SCIPexprReindexVars(SCIP_EXPR * expr,int * newindices)8187 void SCIPexprReindexVars(
8188 SCIP_EXPR* expr, /**< expression to update */
8189 int* newindices /**< new indices of variables */
8190 )
8191 {
8192 int i;
8193
8194 assert(expr != NULL);
8195 assert(newindices != NULL);
8196
8197 if( expr->op == SCIP_EXPR_VARIDX )
8198 {
8199 expr->data.intval = newindices[expr->data.intval];
8200 assert(expr->data.intval >= 0);
8201 }
8202
8203 for( i = 0; i < expr->nchildren; ++i )
8204 SCIPexprReindexVars(expr->children[i], newindices);
8205 }
8206
8207 /** updates parameter indices in expression tree */
SCIPexprReindexParams(SCIP_EXPR * expr,int * newindices)8208 void SCIPexprReindexParams(
8209 SCIP_EXPR* expr, /**< expression to update */
8210 int* newindices /**< new indices of variables */
8211 )
8212 {
8213 int i;
8214
8215 assert(expr != NULL);
8216 assert(newindices != NULL);
8217
8218 if( expr->op == SCIP_EXPR_PARAM )
8219 {
8220 expr->data.intval = newindices[expr->data.intval];
8221 assert(expr->data.intval >= 0);
8222 }
8223
8224 for( i = 0; i < expr->nchildren; ++i )
8225 SCIPexprReindexParams(expr->children[i], newindices);
8226 }
8227
8228 /** prints an expression */
SCIPexprPrint(SCIP_EXPR * expr,SCIP_MESSAGEHDLR * messagehdlr,FILE * file,const char ** varnames,const char ** paramnames,SCIP_Real * paramvals)8229 void SCIPexprPrint(
8230 SCIP_EXPR* expr, /**< expression */
8231 SCIP_MESSAGEHDLR* messagehdlr, /**< message handler */
8232 FILE* file, /**< file for printing, or NULL for stdout */
8233 const char** varnames, /**< names of variables, or NULL for default names */
8234 const char** paramnames, /**< names of parameters, or NULL for default names */
8235 SCIP_Real* paramvals /**< values of parameters, or NULL for not printing */
8236 )
8237 {
8238 assert( expr != NULL );
8239
8240 switch( expr->op )
8241 {
8242 /* @Note: 'expr->data.intval' is either between 0 and number of variables-1, if it uses the varnames array, or
8243 * between 0 and number of params in the expression tree, if it uses the paramnames array
8244 * because, here, we cannot get the values above we cannot assert them
8245 */
8246 case SCIP_EXPR_VARIDX:
8247 if( varnames != NULL )
8248 {
8249 assert(varnames[expr->data.intval] != NULL);
8250 SCIPmessageFPrintInfo(messagehdlr, file, "<%s>", varnames[expr->data.intval]);
8251 }
8252 else
8253 {
8254 SCIPmessageFPrintInfo(messagehdlr, file, "var%d", expr->data.intval);
8255 }
8256 break;
8257
8258 case SCIP_EXPR_PARAM:
8259 if( paramnames != NULL )
8260 {
8261 assert(paramnames[expr->data.intval] != NULL);
8262 SCIPmessageFPrintInfo(messagehdlr, file, "%s", paramnames[expr->data.intval]);
8263 }
8264 else
8265 {
8266 SCIPmessageFPrintInfo(messagehdlr, file, "param%d", expr->data.intval );
8267 }
8268 if( paramvals != NULL )
8269 {
8270 SCIPmessageFPrintInfo(messagehdlr, file, "[%g]", paramvals[expr->data.intval] );
8271 }
8272 break;
8273
8274 case SCIP_EXPR_CONST:
8275 if (expr->data.dbl < 0.0 )
8276 SCIPmessageFPrintInfo(messagehdlr, file, "(%g)", expr->data.dbl );
8277 else
8278 SCIPmessageFPrintInfo(messagehdlr, file, "%g", expr->data.dbl );
8279 break;
8280
8281 case SCIP_EXPR_PLUS:
8282 SCIPmessageFPrintInfo(messagehdlr, file, "(");
8283 SCIPexprPrint(expr->children[0], messagehdlr, file, varnames, paramnames, paramvals);
8284 SCIPmessageFPrintInfo(messagehdlr, file, " + ");
8285 SCIPexprPrint(expr->children[1], messagehdlr, file, varnames, paramnames, paramvals);
8286 SCIPmessageFPrintInfo(messagehdlr, file, ")");
8287 break;
8288
8289 case SCIP_EXPR_MINUS:
8290 SCIPmessageFPrintInfo(messagehdlr, file, "(");
8291 SCIPexprPrint(expr->children[0], messagehdlr, file, varnames, paramnames, paramvals);
8292 SCIPmessageFPrintInfo(messagehdlr, file, " - ");
8293 SCIPexprPrint(expr->children[1], messagehdlr, file, varnames, paramnames, paramvals);
8294 SCIPmessageFPrintInfo(messagehdlr, file, ")");
8295 break;
8296
8297 case SCIP_EXPR_MUL:
8298 SCIPmessageFPrintInfo(messagehdlr, file, "(");
8299 SCIPexprPrint(expr->children[0], messagehdlr, file, varnames, paramnames, paramvals);
8300 SCIPmessageFPrintInfo(messagehdlr, file, " * ");
8301 SCIPexprPrint(expr->children[1], messagehdlr, file, varnames, paramnames, paramvals);
8302 SCIPmessageFPrintInfo(messagehdlr, file, ")");
8303 break;
8304
8305 case SCIP_EXPR_DIV:
8306 SCIPmessageFPrintInfo(messagehdlr, file, "(");
8307 SCIPexprPrint(expr->children[0], messagehdlr, file, varnames, paramnames, paramvals);
8308 SCIPmessageFPrintInfo(messagehdlr, file, " / ");
8309 SCIPexprPrint(expr->children[1], messagehdlr, file, varnames, paramnames, paramvals);
8310 SCIPmessageFPrintInfo(messagehdlr, file, ")");
8311 break;
8312
8313 case SCIP_EXPR_REALPOWER:
8314 case SCIP_EXPR_SIGNPOWER:
8315 SCIPmessageFPrintInfo(messagehdlr, file, "%s(", exprOpTable[expr->op].name);
8316 SCIPexprPrint(expr->children[0], messagehdlr, file, varnames, paramnames, paramvals);
8317 SCIPmessageFPrintInfo(messagehdlr, file, ", %g)", expr->data.dbl);
8318 break;
8319
8320 case SCIP_EXPR_INTPOWER:
8321 SCIPmessageFPrintInfo(messagehdlr, file, "power(");
8322 SCIPexprPrint(expr->children[0], messagehdlr, file, varnames, paramnames, paramvals);
8323 SCIPmessageFPrintInfo(messagehdlr, file, ", %d)", expr->data.intval);
8324 break;
8325
8326 case SCIP_EXPR_SQUARE:
8327 case SCIP_EXPR_SQRT:
8328 case SCIP_EXPR_EXP:
8329 case SCIP_EXPR_LOG:
8330 case SCIP_EXPR_SIN:
8331 case SCIP_EXPR_COS:
8332 case SCIP_EXPR_TAN:
8333 /* case SCIP_EXPR_ERF: */
8334 /* case SCIP_EXPR_ERFI: */
8335 case SCIP_EXPR_MIN:
8336 case SCIP_EXPR_MAX:
8337 case SCIP_EXPR_ABS:
8338 case SCIP_EXPR_SIGN:
8339 {
8340 int i;
8341
8342 SCIPmessageFPrintInfo(messagehdlr, file, "%s(", exprOpTable[expr->op].name);
8343
8344 for( i = 0; i < expr->nchildren; ++i )
8345 {
8346 SCIPexprPrint(expr->children[i], messagehdlr, file, varnames, paramnames, paramvals);
8347 if( i + 1 < expr->nchildren )
8348 {
8349 SCIPmessageFPrintInfo(messagehdlr, file, ", ");
8350 }
8351 }
8352
8353 SCIPmessageFPrintInfo(messagehdlr, file, ")");
8354 break;
8355 }
8356
8357 case SCIP_EXPR_SUM:
8358 case SCIP_EXPR_PRODUCT:
8359 {
8360 switch( expr->nchildren )
8361 {
8362 case 0:
8363 SCIPmessageFPrintInfo(messagehdlr, file, expr->op == SCIP_EXPR_SUM ? "0" : "1");
8364 break;
8365 case 1:
8366 SCIPexprPrint(expr->children[0], messagehdlr, file, varnames, paramnames, paramvals);
8367 break;
8368 default:
8369 {
8370 int i;
8371
8372 SCIPmessageFPrintInfo(messagehdlr, file, "(");
8373 for( i = 0; i < expr->nchildren; ++i )
8374 {
8375 if( i > 0 )
8376 {
8377 SCIPmessageFPrintInfo(messagehdlr, file, "%s", expr->op == SCIP_EXPR_SUM ? " + " : " * ");
8378 }
8379 SCIPexprPrint(expr->children[i], messagehdlr, file, varnames, paramnames, paramvals);
8380 }
8381 SCIPmessageFPrintInfo(messagehdlr, file, ")");
8382 }
8383 }
8384 break;
8385 }
8386
8387 case SCIP_EXPR_LINEAR:
8388 {
8389 SCIP_Real constant;
8390 int i;
8391
8392 constant = ((SCIP_Real*)expr->data.data)[expr->nchildren];
8393
8394 if( expr->nchildren == 0 )
8395 {
8396 SCIPmessageFPrintInfo(messagehdlr, file, "%.15g", constant);
8397 break;
8398 }
8399
8400 SCIPmessageFPrintInfo(messagehdlr, file, "(");
8401
8402 if( constant != 0.0 )
8403 {
8404 SCIPmessageFPrintInfo(messagehdlr, file, "%.15g", constant);
8405 }
8406
8407 for( i = 0; i < expr->nchildren; ++i )
8408 {
8409 SCIPmessageFPrintInfo(messagehdlr, file, " %+.15g ", ((SCIP_Real*)expr->data.data)[i]);
8410 SCIPexprPrint(expr->children[i], messagehdlr, file, varnames, paramnames, paramvals);
8411 }
8412
8413 SCIPmessageFPrintInfo(messagehdlr, file, ")");
8414 break;
8415 }
8416
8417 case SCIP_EXPR_QUADRATIC:
8418 {
8419 SCIP_EXPRDATA_QUADRATIC* quadraticdata;
8420 int i;
8421
8422 quadraticdata = (SCIP_EXPRDATA_QUADRATIC*)expr->data.data;
8423 assert(quadraticdata != NULL);
8424
8425 SCIPmessageFPrintInfo(messagehdlr, file, "(");
8426
8427 if( quadraticdata->constant != 0.0 )
8428 SCIPmessageFPrintInfo(messagehdlr, file, " %+.15g ", quadraticdata->constant);
8429
8430 if( quadraticdata->lincoefs != NULL )
8431 for( i = 0; i < expr->nchildren; ++i )
8432 {
8433 if( quadraticdata->lincoefs[i] == 0.0 )
8434 continue;
8435 SCIPmessageFPrintInfo(messagehdlr, file, " %+.15g ", quadraticdata->lincoefs[i]);
8436 SCIPexprPrint(expr->children[i], messagehdlr, file, varnames, paramnames, paramvals);
8437 }
8438
8439 for( i = 0; i < quadraticdata->nquadelems; ++i )
8440 {
8441 SCIPmessageFPrintInfo(messagehdlr, file, " %+.15g ", quadraticdata->quadelems[i].coef);
8442 SCIPexprPrint(expr->children[quadraticdata->quadelems[i].idx1], messagehdlr, file, varnames, paramnames, paramvals);
8443 if( quadraticdata->quadelems[i].idx1 == quadraticdata->quadelems[i].idx2 )
8444 {
8445 SCIPmessageFPrintInfo(messagehdlr, file, "^2");
8446 }
8447 else
8448 {
8449 SCIPmessageFPrintInfo(messagehdlr, file, " * ");
8450 SCIPexprPrint(expr->children[quadraticdata->quadelems[i].idx2], messagehdlr, file, varnames, paramnames, paramvals);
8451 }
8452 }
8453
8454 SCIPmessageFPrintInfo(messagehdlr, file, ")");
8455 break;
8456 }
8457
8458 case SCIP_EXPR_POLYNOMIAL:
8459 {
8460 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata;
8461 SCIP_EXPRDATA_MONOMIAL* monomialdata;
8462 int i;
8463 int j;
8464
8465 SCIPmessageFPrintInfo(messagehdlr, file, "(");
8466
8467 polynomialdata = (SCIP_EXPRDATA_POLYNOMIAL*)expr->data.data;
8468 assert(polynomialdata != NULL);
8469
8470 if( polynomialdata->constant != 0.0 || polynomialdata->nmonomials == 0 )
8471 {
8472 SCIPmessageFPrintInfo(messagehdlr, file, "%.15g", polynomialdata->constant);
8473 }
8474
8475 for( i = 0; i < polynomialdata->nmonomials; ++i )
8476 {
8477 monomialdata = polynomialdata->monomials[i];
8478 SCIPmessageFPrintInfo(messagehdlr, file, " %+.15g", monomialdata->coef);
8479
8480 for( j = 0; j < monomialdata->nfactors; ++j )
8481 {
8482 SCIPmessageFPrintInfo(messagehdlr, file, " * ");
8483
8484 SCIPexprPrint(expr->children[monomialdata->childidxs[j]], messagehdlr, file, varnames, paramnames, paramvals);
8485 if( monomialdata->exponents[j] < 0.0 )
8486 {
8487 SCIPmessageFPrintInfo(messagehdlr, file, "^(%.15g)", monomialdata->exponents[j]);
8488 }
8489 else if( monomialdata->exponents[j] != 1.0 )
8490 {
8491 SCIPmessageFPrintInfo(messagehdlr, file, "^%.15g", monomialdata->exponents[j]);
8492 }
8493 }
8494 }
8495
8496 SCIPmessageFPrintInfo(messagehdlr, file, ")");
8497 break;
8498 }
8499
8500 case SCIP_EXPR_USER:
8501 {
8502 SCIP_EXPRDATA_USER* exprdata;
8503 int i;
8504
8505 exprdata = (SCIP_EXPRDATA_USER*)expr->data.data;
8506 assert(exprdata != NULL);
8507
8508 if( exprdata->print != NULL )
8509 {
8510 exprdata->print(exprdata->userdata, messagehdlr, file);
8511 }
8512 else
8513 {
8514 SCIPmessageFPrintInfo(messagehdlr, file, "user");
8515 }
8516
8517 SCIPmessageFPrintInfo(messagehdlr, file, "(");
8518 for( i = 0; i < expr->nchildren; ++i )
8519 {
8520 if( i > 0 )
8521 {
8522 SCIPmessageFPrintInfo(messagehdlr, file, ",");
8523 }
8524 SCIPexprPrint(expr->children[i], messagehdlr, file, varnames, paramnames, paramvals);
8525 }
8526 SCIPmessageFPrintInfo(messagehdlr, file, ")");
8527
8528 break;
8529 }
8530
8531 case SCIP_EXPR_LAST:
8532 {
8533 SCIPerrorMessage("invalid expression\n");
8534 SCIPABORT();
8535 }
8536 }
8537 }
8538
8539 /** parses an expression from a string */
SCIPexprParse(BMS_BLKMEM * blkmem,SCIP_MESSAGEHDLR * messagehdlr,SCIP_EXPR ** expr,const char * str,const char * lastchar,int * nvars,int * varnames,int varnameslength)8540 SCIP_RETCODE SCIPexprParse(
8541 BMS_BLKMEM* blkmem, /**< block memory data structure */
8542 SCIP_MESSAGEHDLR* messagehdlr, /**< message handler */
8543 SCIP_EXPR** expr, /**< buffer to store pointer to created expression */
8544 const char* str, /**< pointer to the string to be parsed */
8545 const char* lastchar, /**< pointer to the last char of str that should be parsed */
8546 int* nvars, /**< buffer to store number of variables */
8547 int* varnames, /**< buffer to store variable names, prefixed by index (as int) */
8548 int varnameslength /**< length of the varnames buffer array */
8549 )
8550 {
8551 SCIP_HASHTABLE* vartable;
8552 SCIP_RETCODE retcode;
8553
8554 assert(blkmem != NULL);
8555 assert(expr != NULL);
8556 assert(str != NULL);
8557 assert(lastchar != NULL);
8558 assert(nvars != NULL);
8559 assert(varnames != NULL);
8560
8561 *nvars = 0;
8562
8563 /* create a hash table for variable names and corresponding expression index
8564 * for each variable, we store its name, prefixed with the assigned index in the first sizeof(int) bytes
8565 */
8566 SCIP_CALL( SCIPhashtableCreate(&vartable, blkmem, 10, exprparseVarTableGetKey, SCIPhashKeyEqString,
8567 SCIPhashKeyValString, NULL) );
8568
8569 retcode = exprParse(blkmem, messagehdlr, expr, str, (int) (lastchar - str + 1), lastchar, nvars, &varnames,
8570 &varnameslength, vartable, 0);
8571
8572 SCIPhashtableFree(&vartable);
8573
8574 return retcode;
8575 }
8576
8577
8578 /**@} */
8579
8580 /**@name Expression tree methods */
8581 /**@{ */
8582
8583 /* In debug mode, the following methods are implemented as function calls to ensure
8584 * type validity.
8585 * In optimized mode, the methods are implemented as defines to improve performance.
8586 * However, we want to have them in the library anyways, so we have to undef the defines.
8587 */
8588
8589 #undef SCIPexprtreeGetRoot
8590 #undef SCIPexprtreeGetNVars
8591 #undef SCIPexprtreeGetNParams
8592 #undef SCIPexprtreeGetParamVals
8593 #undef SCIPexprtreeSetParamVal
8594 #undef SCIPexprtreeGetInterpreterData
8595 #undef SCIPexprtreeSetInterpreterData
8596 #undef SCIPexprtreeFreeInterpreterData
8597 #undef SCIPexprtreeHasParam
8598 #undef SCIPexprtreeGetMaxDegree
8599 #undef SCIPexprtreeEval
8600 #undef SCIPexprtreeEvalInt
8601 #undef SCIPexprtreePrint
8602
8603 /** returns root expression of an expression tree */
SCIPexprtreeGetRoot(SCIP_EXPRTREE * tree)8604 SCIP_EXPR* SCIPexprtreeGetRoot(
8605 SCIP_EXPRTREE* tree /**< expression tree */
8606 )
8607 {
8608 assert(tree != NULL);
8609
8610 return tree->root;
8611 }
8612
8613 /** returns number of variables in expression tree */
SCIPexprtreeGetNVars(SCIP_EXPRTREE * tree)8614 int SCIPexprtreeGetNVars(
8615 SCIP_EXPRTREE* tree /**< expression tree */
8616 )
8617 {
8618 assert(tree != NULL);
8619
8620 return tree->nvars;
8621 }
8622
8623 /** returns number of parameters in expression tree */
SCIPexprtreeGetNParams(SCIP_EXPRTREE * tree)8624 int SCIPexprtreeGetNParams(
8625 SCIP_EXPRTREE* tree /**< expression tree */
8626 )
8627 {
8628 assert(tree != NULL);
8629
8630 return tree->nparams;
8631 }
8632
8633 /** returns values of parameters or NULL if none */
SCIPexprtreeGetParamVals(SCIP_EXPRTREE * tree)8634 SCIP_Real* SCIPexprtreeGetParamVals(
8635 SCIP_EXPRTREE* tree /**< expression tree */
8636 )
8637 {
8638 assert(tree != NULL);
8639
8640 return tree->params;
8641 }
8642
8643 /** sets value of a single parameter in expression tree */
SCIPexprtreeSetParamVal(SCIP_EXPRTREE * tree,int paramidx,SCIP_Real paramval)8644 void SCIPexprtreeSetParamVal(
8645 SCIP_EXPRTREE* tree, /**< expression tree */
8646 int paramidx, /**< index of parameter */
8647 SCIP_Real paramval /**< new value of parameter */
8648 )
8649 {
8650 assert(tree != NULL);
8651 assert(paramidx >= 0);
8652 assert(paramidx < tree->nparams);
8653 assert(tree->params != NULL);
8654
8655 tree->params[paramidx] = paramval;
8656 }
8657
8658 /** gets data of expression tree interpreter, or NULL if not set */
SCIPexprtreeGetInterpreterData(SCIP_EXPRTREE * tree)8659 SCIP_EXPRINTDATA* SCIPexprtreeGetInterpreterData(
8660 SCIP_EXPRTREE* tree /**< expression tree */
8661 )
8662 {
8663 assert(tree != NULL);
8664
8665 return tree->interpreterdata;
8666 }
8667
8668 /** sets data of expression tree interpreter */
SCIPexprtreeSetInterpreterData(SCIP_EXPRTREE * tree,SCIP_EXPRINTDATA * interpreterdata)8669 void SCIPexprtreeSetInterpreterData(
8670 SCIP_EXPRTREE* tree, /**< expression tree */
8671 SCIP_EXPRINTDATA* interpreterdata /**< expression interpreter data */
8672 )
8673 {
8674 assert(tree != NULL);
8675 assert(interpreterdata != NULL);
8676 assert(tree->interpreterdata == NULL);
8677
8678 tree->interpreterdata = interpreterdata;
8679 }
8680
8681 /** frees data of expression tree interpreter, if any */
SCIPexprtreeFreeInterpreterData(SCIP_EXPRTREE * tree)8682 SCIP_RETCODE SCIPexprtreeFreeInterpreterData(
8683 SCIP_EXPRTREE* tree /**< expression tree */
8684 )
8685 {
8686 if( tree->interpreterdata != NULL )
8687 {
8688 SCIP_CALL( SCIPexprintFreeData(&tree->interpreterdata) );
8689 assert(tree->interpreterdata == NULL);
8690 }
8691
8692 return SCIP_OKAY;
8693 }
8694
8695 /** indicates whether there are parameterized constants (SCIP_EXPR_PARAM) in expression tree */
SCIPexprtreeHasParam(SCIP_EXPRTREE * tree)8696 SCIP_Bool SCIPexprtreeHasParam(
8697 SCIP_EXPRTREE* tree /**< expression tree */
8698 )
8699 {
8700 assert(tree != NULL);
8701
8702 return SCIPexprHasParam(tree->root);
8703 }
8704
8705 /** Gives maximal degree of expression in expression tree.
8706 *
8707 * If constant expression, gives 0,
8708 * if linear expression, gives 1,
8709 * if polynomial expression, gives its maximal degree,
8710 * otherwise (nonpolynomial nonconstant expressions) gives at least SCIP_EXPR_DEGREEINFINITY.
8711 */
SCIPexprtreeGetMaxDegree(SCIP_EXPRTREE * tree,int * maxdegree)8712 SCIP_RETCODE SCIPexprtreeGetMaxDegree(
8713 SCIP_EXPRTREE* tree, /**< expression tree */
8714 int* maxdegree /**< buffer to store maximal degree */
8715 )
8716 {
8717 assert(tree != NULL);
8718
8719 SCIP_CALL( SCIPexprGetMaxDegree(tree->root, maxdegree) );
8720
8721 return SCIP_OKAY;
8722 }
8723
8724 /** evaluates an expression tree w.r.t. a point */
SCIPexprtreeEval(SCIP_EXPRTREE * tree,SCIP_Real * varvals,SCIP_Real * val)8725 SCIP_RETCODE SCIPexprtreeEval(
8726 SCIP_EXPRTREE* tree, /**< expression tree */
8727 SCIP_Real* varvals, /**< values for variables */
8728 SCIP_Real* val /**< buffer to store expression tree value */
8729 )
8730 {
8731 assert(tree != NULL);
8732 assert(varvals != NULL || tree->nvars == 0);
8733 assert(val != NULL);
8734
8735 SCIP_CALL( SCIPexprEval(tree->root, varvals, tree->params, val) );
8736
8737 return SCIP_OKAY;
8738 }
8739
8740 /** evaluates an expression tree w.r.t. an interval */
SCIPexprtreeEvalInt(SCIP_EXPRTREE * tree,SCIP_Real infinity,SCIP_INTERVAL * varvals,SCIP_INTERVAL * val)8741 SCIP_RETCODE SCIPexprtreeEvalInt(
8742 SCIP_EXPRTREE* tree, /**< expression tree */
8743 SCIP_Real infinity, /**< value for infinity */
8744 SCIP_INTERVAL* varvals, /**< intervals for variables */
8745 SCIP_INTERVAL* val /**< buffer to store expression tree value */
8746 )
8747 {
8748 assert(tree != NULL);
8749 assert(varvals != NULL || tree->nvars == 0);
8750 assert(val != NULL);
8751
8752 SCIP_CALL( SCIPexprEvalInt(tree->root, infinity, varvals, tree->params, val) );
8753
8754 return SCIP_OKAY;
8755 }
8756
8757 /** prints an expression tree */
SCIPexprtreePrint(SCIP_EXPRTREE * tree,SCIP_MESSAGEHDLR * messagehdlr,FILE * file,const char ** varnames,const char ** paramnames)8758 void SCIPexprtreePrint(
8759 SCIP_EXPRTREE* tree, /**< expression tree */
8760 SCIP_MESSAGEHDLR* messagehdlr, /**< message handler */
8761 FILE* file, /**< file for printing, or NULL for stdout */
8762 const char** varnames, /**< names of variables, or NULL for default names */
8763 const char** paramnames /**< names of parameters, or NULL for default names */
8764 )
8765 {
8766 assert(tree != NULL);
8767
8768 SCIPexprPrint(tree->root, messagehdlr, file, varnames, paramnames, tree->params);
8769 }
8770
8771
8772 /** creates an expression tree */
SCIPexprtreeCreate(BMS_BLKMEM * blkmem,SCIP_EXPRTREE ** tree,SCIP_EXPR * root,int nvars,int nparams,SCIP_Real * params)8773 SCIP_RETCODE SCIPexprtreeCreate(
8774 BMS_BLKMEM* blkmem, /**< block memory data structure */
8775 SCIP_EXPRTREE** tree, /**< buffer to store address of created expression tree */
8776 SCIP_EXPR* root, /**< pointer to root expression, not copied deep !, can be NULL */
8777 int nvars, /**< number of variables in variable mapping */
8778 int nparams, /**< number of parameters in expression */
8779 SCIP_Real* params /**< values for parameters, or NULL (if NULL but nparams > 0, then params is initialized with zeros) */
8780 )
8781 {
8782 assert(blkmem != NULL);
8783 assert(tree != NULL);
8784
8785 SCIP_ALLOC( BMSallocBlockMemory(blkmem, tree) );
8786
8787 (*tree)->blkmem = blkmem;
8788 (*tree)->root = root;
8789 (*tree)->nvars = nvars;
8790 (*tree)->vars = NULL;
8791 (*tree)->nparams = nparams;
8792 (*tree)->interpreterdata = NULL;
8793
8794 if( params != NULL )
8795 {
8796 assert(nparams > 0);
8797 SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &(*tree)->params, params, nparams) );
8798 }
8799 else if( nparams > 0 )
8800 {
8801 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &(*tree)->params, nparams) );
8802 BMSclearMemoryArray((*tree)->params, nparams);
8803 }
8804 else
8805 {
8806 assert(nparams == 0);
8807 (*tree)->params = NULL;
8808 }
8809
8810 return SCIP_OKAY;
8811 }
8812
8813 /** copies an expression tree */
SCIPexprtreeCopy(BMS_BLKMEM * blkmem,SCIP_EXPRTREE ** targettree,SCIP_EXPRTREE * sourcetree)8814 SCIP_RETCODE SCIPexprtreeCopy(
8815 BMS_BLKMEM* blkmem, /**< block memory that should be used in new expression tree */
8816 SCIP_EXPRTREE** targettree, /**< buffer to store address of copied expression tree */
8817 SCIP_EXPRTREE* sourcetree /**< expression tree to copy */
8818 )
8819 {
8820 assert(blkmem != NULL);
8821 assert(targettree != NULL);
8822 assert(sourcetree != NULL);
8823
8824 /* copy expression tree "header" */
8825 SCIP_ALLOC( BMSduplicateBlockMemory(blkmem, targettree, sourcetree) );
8826
8827 /* we may have a new block memory; and we do not want to keep the others interpreter data */
8828 (*targettree)->blkmem = blkmem;
8829 (*targettree)->interpreterdata = NULL;
8830
8831 /* copy variables, if any */
8832 if( sourcetree->vars != NULL )
8833 {
8834 assert(sourcetree->nvars > 0);
8835
8836 SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &(*targettree)->vars, sourcetree->vars, sourcetree->nvars) );
8837 }
8838
8839 /* copy parameters, if any */
8840 if( sourcetree->params != NULL )
8841 {
8842 assert(sourcetree->nparams > 0);
8843
8844 SCIP_ALLOC( BMSduplicateBlockMemoryArray(blkmem, &(*targettree)->params, sourcetree->params, sourcetree->nparams) );
8845 }
8846
8847 /* copy expression */
8848 SCIP_CALL( SCIPexprCopyDeep(blkmem, &(*targettree)->root, sourcetree->root) );
8849
8850 return SCIP_OKAY;
8851 }
8852
8853 /** frees an expression tree */
SCIPexprtreeFree(SCIP_EXPRTREE ** tree)8854 SCIP_RETCODE SCIPexprtreeFree(
8855 SCIP_EXPRTREE** tree /**< pointer to expression tree that is freed */
8856 )
8857 {
8858 assert( tree != NULL);
8859 assert(*tree != NULL);
8860
8861 SCIP_CALL( SCIPexprtreeFreeInterpreterData(*tree) );
8862
8863 if( (*tree)->root != NULL )
8864 {
8865 SCIPexprFreeDeep((*tree)->blkmem, &(*tree)->root);
8866 assert((*tree)->root == NULL);
8867 }
8868
8869 BMSfreeBlockMemoryArrayNull((*tree)->blkmem, &(*tree)->vars, (*tree)->nvars );
8870 BMSfreeBlockMemoryArrayNull((*tree)->blkmem, &(*tree)->params, (*tree)->nparams);
8871
8872 BMSfreeBlockMemory((*tree)->blkmem, tree);
8873
8874 return SCIP_OKAY;
8875 }
8876
8877 /** sets number and values of all parameters in expression tree */
SCIPexprtreeSetParams(SCIP_EXPRTREE * tree,int nparams,SCIP_Real * paramvals)8878 SCIP_RETCODE SCIPexprtreeSetParams(
8879 SCIP_EXPRTREE* tree, /**< expression tree */
8880 int nparams, /**< number of parameters */
8881 SCIP_Real* paramvals /**< values of parameters, can be NULL if nparams == 0 */
8882 )
8883 {
8884 assert(tree != NULL);
8885 assert(paramvals != NULL || nparams == 0);
8886
8887 if( nparams == 0 )
8888 {
8889 BMSfreeBlockMemoryArrayNull(tree->blkmem, &tree->params, tree->nparams);
8890 }
8891 else if( tree->params != NULL )
8892 {
8893 SCIP_ALLOC( BMSreallocBlockMemoryArray(tree->blkmem, &tree->params, tree->nparams, nparams) );
8894 BMScopyMemoryArray(tree->params, paramvals, nparams);
8895 }
8896 else
8897 {
8898 SCIP_ALLOC( BMSduplicateBlockMemoryArray(tree->blkmem, &tree->params, paramvals, nparams) );
8899 }
8900
8901 tree->nparams = nparams;
8902 assert(tree->params != NULL || tree->nparams == 0);
8903
8904 return SCIP_OKAY;
8905 }
8906
8907
8908 /** gives the number of usages for each variable in the expression tree */
SCIPexprtreeGetVarsUsage(SCIP_EXPRTREE * tree,int * varsusage)8909 void SCIPexprtreeGetVarsUsage(
8910 SCIP_EXPRTREE* tree, /**< expression tree */
8911 int* varsusage /**< array where to store for each variable how often it is used in the tree */
8912 )
8913 {
8914 assert(tree != NULL);
8915 assert(varsusage != NULL);
8916
8917 if( tree->nvars == 0 )
8918 return;
8919
8920 BMSclearMemoryArray(varsusage, tree->nvars);
8921 SCIPexprGetVarsUsage(tree->root, varsusage);
8922 }
8923
8924 /** aims at simplifying an expression and splitting of a linear expression
8925 *
8926 * If linear variables are split off, expression interpreter data, if stored in the tree, is freed.
8927 */
SCIPexprtreeSimplify(SCIP_EXPRTREE * tree,SCIP_MESSAGEHDLR * messagehdlr,SCIP_Real eps,int maxexpansionexponent,int * nlinvars,int * linidxs,SCIP_Real * lincoefs)8928 SCIP_RETCODE SCIPexprtreeSimplify(
8929 SCIP_EXPRTREE* tree, /**< expression tree */
8930 SCIP_MESSAGEHDLR* messagehdlr, /**< message handler */
8931 SCIP_Real eps, /**< threshold, under which positive values are treat as 0 */
8932 int maxexpansionexponent,/**< maximal exponent for which we still expand non-monomial polynomials */
8933 int* nlinvars, /**< buffer to store number of linear variables in linear part, or NULL if linear part should not be separated */
8934 int* linidxs, /**< array to store indices of variables in expression tree which belong to linear part, or NULL */
8935 SCIP_Real* lincoefs /**< array to store coefficients of linear part, or NULL */
8936 )
8937 {
8938 #ifndef NDEBUG
8939 SCIP_RANDNUMGEN* randnumgen;
8940 SCIP_Real* testx;
8941 SCIP_Real testval_before;
8942 SCIP_Real testval_after;
8943 int i;
8944 #endif
8945
8946 assert(tree != NULL);
8947
8948 #ifndef NDEBUG
8949 SCIP_CALL( SCIPrandomCreate(&randnumgen, tree->blkmem, 42) );
8950
8951 SCIP_ALLOC( BMSallocMemoryArray(&testx, SCIPexprtreeGetNVars(tree)) ); /*lint !e666*/
8952 for( i = 0; i < SCIPexprtreeGetNVars(tree); ++i )
8953 testx[i] = SCIPrandomGetReal(randnumgen, -100.0, 100.0); /*lint !e644*/
8954 SCIP_CALL( SCIPexprtreeEval(tree, testx, &testval_before) );
8955
8956 SCIPrandomFree(&randnumgen, tree->blkmem);
8957 #endif
8958
8959 /* we should be careful about declaring numbers close to zero as zero, so take eps^2 as tolerance */
8960 SCIP_CALL( SCIPexprSimplify(tree->blkmem, messagehdlr, tree->root, eps*eps, maxexpansionexponent, tree->nvars, nlinvars, linidxs, lincoefs) );
8961
8962 #ifndef NDEBUG
8963 SCIP_CALL( SCIPexprtreeEval(tree, testx, &testval_after) );
8964 if( nlinvars != NULL && testval_before == testval_before ) /*lint !e777*/
8965 for( i = 0; i < *nlinvars; ++i )
8966 testval_after += lincoefs[i] * testx[linidxs[i]];
8967 assert(testval_before != testval_before || testval_before == testval_after || EPSZ(SCIPrelDiff(testval_before, testval_after), eps)); /*lint !e777*/
8968 BMSfreeMemoryArray(&testx);
8969 #endif
8970
8971 /* removing something from the the tree may invalidate the interpreter data */
8972 if( nlinvars != NULL && *nlinvars > 0 )
8973 SCIP_CALL( SCIPexprtreeFreeInterpreterData(tree) );
8974
8975 return SCIP_OKAY;
8976 }
8977
8978 /** adds an expression to the root expression of the tree
8979 *
8980 * The root is replaced with an SCIP_EXPR_PLUS expression which has the previous root and the given expression (or a copy of it) as children.
8981 * If no root existed yet, then the root is set to the given expression (or a copy of it).
8982 */
SCIPexprtreeAddExpr(SCIP_EXPRTREE * tree,SCIP_EXPR * expr,SCIP_Bool copyexpr)8983 SCIP_RETCODE SCIPexprtreeAddExpr(
8984 SCIP_EXPRTREE* tree, /**< expression tree */
8985 SCIP_EXPR* expr, /**< expression to add to tree */
8986 SCIP_Bool copyexpr /**< whether expression should be copied */
8987 )
8988 {
8989 assert(tree != NULL);
8990
8991 /* adding something to the tree may invalidate the interpreter data */
8992 SCIP_CALL( SCIPexprtreeFreeInterpreterData(tree) );
8993
8994 if( copyexpr )
8995 {
8996 SCIP_CALL( SCIPexprCopyDeep(tree->blkmem, &expr, expr) );
8997 }
8998
8999 if( tree->root == NULL )
9000 {
9001 tree->root = expr;
9002 }
9003 else
9004 {
9005 SCIP_CALL( SCIPexprCreate(tree->blkmem, &tree->root, SCIP_EXPR_PLUS, tree->root, expr) );
9006 }
9007
9008 return SCIP_OKAY;
9009 }
9010
9011 /** tries to determine the curvature type of an expression tree w.r.t. given variable domains */
SCIPexprtreeCheckCurvature(SCIP_EXPRTREE * tree,SCIP_Real infinity,SCIP_INTERVAL * varbounds,SCIP_EXPRCURV * curv,SCIP_INTERVAL * bounds)9012 SCIP_RETCODE SCIPexprtreeCheckCurvature(
9013 SCIP_EXPRTREE* tree, /**< expression tree */
9014 SCIP_Real infinity, /**< value for infinity */
9015 SCIP_INTERVAL* varbounds, /**< domains of variables */
9016 SCIP_EXPRCURV* curv, /**< buffer to store curvature of expression */
9017 SCIP_INTERVAL* bounds /**< buffer to store bounds on expression, or NULL if not needed */
9018 )
9019 {
9020 SCIP_INTERVAL exprbounds;
9021
9022 assert(tree != NULL);
9023
9024 if( tree->root == NULL )
9025 {
9026 *curv = SCIP_EXPRCURV_LINEAR;
9027
9028 if( bounds != NULL )
9029 SCIPintervalSet(bounds, 0.0);
9030
9031 return SCIP_OKAY;
9032 }
9033
9034 SCIP_CALL( SCIPexprCheckCurvature(tree->root, infinity, varbounds, tree->params, curv, &exprbounds) );
9035
9036 if( bounds != NULL )
9037 *bounds = exprbounds;
9038
9039 return SCIP_OKAY;
9040 }
9041
9042 /** substitutes variables (SCIP_EXPR_VARIDX) in an expression tree by expressions
9043 *
9044 * A variable with index i is replaced by a copy of substexprs[i], if that latter is not NULL.
9045 * If substexprs[i] == NULL, then the variable expression i is not touched.
9046 */
SCIPexprtreeSubstituteVars(SCIP_EXPRTREE * tree,SCIP_EXPR ** substexprs)9047 SCIP_RETCODE SCIPexprtreeSubstituteVars(
9048 SCIP_EXPRTREE* tree, /**< expression tree */
9049 SCIP_EXPR** substexprs /**< array of substitute expressions; single entries can be NULL */
9050 )
9051 {
9052 assert(tree != NULL);
9053
9054 if( tree->root == NULL )
9055 return SCIP_OKAY;
9056
9057 if( tree->root->op == SCIP_EXPR_VARIDX )
9058 {
9059 int varidx;
9060
9061 varidx = tree->root->data.intval;
9062 assert(varidx >= 0);
9063 if( substexprs[varidx] != NULL )
9064 {
9065 /* substitute root expression */
9066 SCIPexprFreeDeep(tree->blkmem, &tree->root);
9067 SCIP_CALL( SCIPexprCopyDeep(tree->blkmem, &tree->root, substexprs[varidx]) );
9068 }
9069 }
9070 else
9071 {
9072 /* check children (and grandchildren and so on...) of root expression */
9073 SCIP_CALL( SCIPexprSubstituteVars(tree->blkmem, tree->root, substexprs) );
9074 }
9075
9076 /* substitution of variables should invalidate interpreter data */
9077 SCIP_CALL( SCIPexprtreeFreeInterpreterData(tree) );
9078
9079 return SCIP_OKAY;
9080 }
9081
9082 /**@} */
9083
9084 /**@name Quadratic element methods */
9085 /**@{ */
9086
9087 /** comparing two quadratic elements
9088 *
9089 * a is better than b if index1 of a is smaller than index1 of b or index1 of both is equal but index2 of a is smaller than index2 of b
9090 */
9091 #define QUADELEMS_ISBETTER(a, b) ( ((a).idx1 < (b).idx1) || ((a).idx1 == (b).idx1 && (a).idx2 < (b).idx2) )
9092
9093 /** swaps two quadratic elements */
9094 #define QUADELEMS_SWAP(x,y) \
9095 { \
9096 SCIP_QUADELEM temp = x; \
9097 x = y; \
9098 y = temp; \
9099 }
9100
9101 /** quicksort an array of quadratic elements; pivot is the medial element (taken from scip/sorttpl.c) */
9102 static
quadelemsQuickSort(SCIP_QUADELEM * elems,int start,int end)9103 void quadelemsQuickSort(
9104 SCIP_QUADELEM* elems, /**< array to be sorted */
9105 int start, /**< starting index */
9106 int end /**< ending index */
9107 )
9108 {
9109 assert(start <= end);
9110
9111 /* use quick sort for long lists */
9112 while( end - start >= 25 ) /* 25 was SORTTPL_SHELLSORTMAX in sorttpl.c */
9113 {
9114 SCIP_QUADELEM pivotkey;
9115 int lo;
9116 int hi;
9117 int mid;
9118
9119 /* select pivot element */
9120 mid = (start+end)/2;
9121 pivotkey = elems[mid];
9122
9123 /* partition the array into elements < pivot [start,hi] and elements >= pivot [lo,end] */
9124 lo = start;
9125 hi = end;
9126 for( ;; )
9127 {
9128 while( lo < end && QUADELEMS_ISBETTER(elems[lo], pivotkey) )
9129 lo++;
9130 while( hi > start && !QUADELEMS_ISBETTER(elems[hi], pivotkey) )
9131 hi--;
9132
9133 if( lo >= hi )
9134 break;
9135
9136 QUADELEMS_SWAP(elems[lo], elems[hi]);
9137
9138 lo++;
9139 hi--;
9140 }
9141 assert(hi == lo-1 || hi == start);
9142
9143 /* skip entries which are equal to the pivot element (three partitions, <, =, > than pivot)*/
9144 while( lo < end && !QUADELEMS_ISBETTER(pivotkey, elems[lo]) )
9145 lo++;
9146
9147 /* make sure that we have at least one element in the smaller partition */
9148 if( lo == start )
9149 {
9150 /* everything is greater or equal than the pivot element: move pivot to the left (degenerate case) */
9151 assert(!QUADELEMS_ISBETTER(elems[mid], pivotkey)); /* the pivot element did not change its position */
9152 assert(!QUADELEMS_ISBETTER(pivotkey, elems[mid]));
9153 QUADELEMS_SWAP(elems[lo], elems[mid]);
9154 lo++;
9155 }
9156
9157 /* sort the smaller partition by a recursive call, sort the larger part without recursion */
9158 if( hi - start <= end - lo )
9159 {
9160 /* sort [start,hi] with a recursive call */
9161 if( start < hi )
9162 quadelemsQuickSort(elems, start, hi);
9163
9164 /* now focus on the larger part [lo,end] */
9165 start = lo;
9166 }
9167 else
9168 {
9169 /* sort [lo,end] with a recursive call */
9170 if( lo < end )
9171 quadelemsQuickSort(elems, lo, end);
9172
9173 /* now focus on the larger part [start,hi] */
9174 end = hi;
9175 }
9176 }
9177
9178 /* use shell sort on the remaining small list */
9179 if( end - start >= 1 )
9180 {
9181 static const int incs[3] = {1, 5, 19}; /* sequence of increments */
9182 int k;
9183
9184 for( k = 2; k >= 0; --k )
9185 {
9186 int h;
9187 int i;
9188
9189 for( h = incs[k], i = h + start; i <= end; ++i )
9190 {
9191 int j;
9192 SCIP_QUADELEM tempkey = elems[i];
9193
9194 j = i;
9195 while( j >= h && QUADELEMS_ISBETTER(tempkey, elems[j-h]) )
9196 {
9197 elems[j] = elems[j-h];
9198 j -= h;
9199 }
9200
9201 elems[j] = tempkey;
9202 }
9203 }
9204 }
9205 }
9206
9207 /** sorts an array of quadratic elements
9208 *
9209 * The elements are sorted such that the first index is increasing and
9210 * such that among elements with the same first index, the second index is increasing.
9211 * For elements with same first and second index, the order is not defined.
9212 */
SCIPquadelemSort(SCIP_QUADELEM * quadelems,int nquadelems)9213 void SCIPquadelemSort(
9214 SCIP_QUADELEM* quadelems, /**< array of quadratic elements */
9215 int nquadelems /**< number of quadratic elements */
9216 )
9217 {
9218 if( nquadelems == 0 )
9219 return;
9220
9221 #ifndef NDEBUG
9222 {
9223 int i;
9224 for( i = 0; i < nquadelems; ++i )
9225 assert(quadelems[i].idx1 <= quadelems[i].idx2);
9226 }
9227 #endif
9228
9229 quadelemsQuickSort(quadelems, 0, nquadelems-1);
9230 }
9231
9232 /** Finds an index pair in a sorted array of quadratic elements.
9233 *
9234 * If (idx1,idx2) is found in quadelems, then returns TRUE and stores position of quadratic element in *pos.
9235 * If (idx1,idx2) is not found in quadelems, then returns FALSE and stores position where a quadratic element with these indices would be inserted in *pos.
9236 * Assumes that idx1 <= idx2.
9237 */
SCIPquadelemSortedFind(SCIP_QUADELEM * quadelems,int idx1,int idx2,int nquadelems,int * pos)9238 SCIP_Bool SCIPquadelemSortedFind(
9239 SCIP_QUADELEM* quadelems, /**< array of quadratic elements */
9240 int idx1, /**< index of first variable in element to search for */
9241 int idx2, /**< index of second variable in element to search for */
9242 int nquadelems, /**< number of quadratic elements in array */
9243 int* pos /**< buffer to store position of found quadratic element or position where it would be inserted, or NULL */
9244 )
9245 {
9246 int left;
9247 int right;
9248
9249 assert(quadelems != NULL || nquadelems == 0);
9250 assert(idx1 <= idx2);
9251
9252 if( nquadelems == 0 )
9253 {
9254 if( pos != NULL )
9255 *pos = 0;
9256 return FALSE;
9257 }
9258
9259 left = 0;
9260 right = nquadelems - 1;
9261 while( left <= right )
9262 {
9263 int middle;
9264
9265 middle = (left+right)/2;
9266 assert(0 <= middle && middle < nquadelems);
9267
9268 if( idx1 < quadelems[middle].idx1 || (idx1 == quadelems[middle].idx1 && idx2 < quadelems[middle].idx2) ) /*lint !e613*/
9269 right = middle - 1;
9270 else if( quadelems[middle].idx1 < idx1 || (quadelems[middle].idx1 == idx1 && quadelems[middle].idx2 < idx2) ) /*lint !e613*/
9271 left = middle + 1;
9272 else
9273 {
9274 if( pos != NULL )
9275 *pos = middle;
9276 return TRUE;
9277 }
9278 }
9279 assert(left == right+1);
9280
9281 if( pos != NULL )
9282 *pos = left;
9283 return FALSE;
9284 }
9285
9286 /** Adds quadratic elements with same index and removes elements with coefficient 0.0.
9287 *
9288 * Assumes that elements have been sorted before.
9289 */
SCIPquadelemSqueeze(SCIP_QUADELEM * quadelems,int nquadelems,int * nquadelemsnew)9290 void SCIPquadelemSqueeze(
9291 SCIP_QUADELEM* quadelems, /**< array of quadratic elements */
9292 int nquadelems, /**< number of quadratic elements */
9293 int* nquadelemsnew /**< pointer to store new (reduced) number of quadratic elements */
9294 )
9295 {
9296 int i;
9297 int next;
9298
9299 assert(quadelems != NULL);
9300 assert(nquadelemsnew != NULL);
9301 assert(nquadelems >= 0);
9302
9303 i = 0;
9304 next = 0;
9305 while( next < nquadelems )
9306 {
9307 /* assert that array is sorted */
9308 assert(QUADELEMS_ISBETTER(quadelems[i], quadelems[next]) ||
9309 (quadelems[i].idx1 == quadelems[next].idx1 && quadelems[i].idx2 == quadelems[next].idx2));
9310
9311 /* skip elements with coefficient 0.0 */
9312 if( quadelems[next].coef == 0.0 )
9313 {
9314 ++next;
9315 continue;
9316 }
9317
9318 /* if next element has same index as previous one, add it to the previous one */
9319 if( i >= 1 &&
9320 quadelems[i-1].idx1 == quadelems[next].idx1 &&
9321 quadelems[i-1].idx2 == quadelems[next].idx2 )
9322 {
9323 quadelems[i-1].coef += quadelems[next].coef;
9324 ++next;
9325 continue;
9326 }
9327
9328 /* otherwise, move next element to current position */
9329 quadelems[i] = quadelems[next];
9330 ++i;
9331 ++next;
9332 }
9333 assert(next == nquadelems);
9334
9335 /* now i should point to the position after the last valid element, i.e., it is the remaining number of elements */
9336 *nquadelemsnew = i;
9337 }
9338
9339 /**@} */
9340
9341 /**@name Expression graph node private methods */
9342 /**@{ */
9343
9344 /** adds a parent to an expression graph node */
9345 static
exprgraphNodeAddParent(BMS_BLKMEM * blkmem,SCIP_EXPRGRAPHNODE * node,SCIP_EXPRGRAPHNODE * parent)9346 SCIP_RETCODE exprgraphNodeAddParent(
9347 BMS_BLKMEM* blkmem, /**< block memory */
9348 SCIP_EXPRGRAPHNODE* node, /**< expression graph node where to add a parent */
9349 SCIP_EXPRGRAPHNODE* parent /**< parent node */
9350 )
9351 {
9352 assert(blkmem != NULL);
9353 assert(node != NULL);
9354 assert(node->depth >= 0);
9355 assert(node->pos >= 0);
9356 assert(parent != NULL);
9357 assert(parent->depth >= 0);
9358 assert(parent->pos >= 0);
9359 assert(parent->depth > node->depth); /* a parent node need to have larger depth */
9360
9361 ensureBlockMemoryArraySize(blkmem, &node->parents, &node->parentssize, node->nparents + 1);
9362 assert(node->nparents < node->parentssize);
9363
9364 node->parents[node->nparents] = parent;
9365 ++node->nparents;
9366
9367 /* update sorted flag */
9368 node->parentssorted = (node->nparents <= 1) || (node->parentssorted && (exprgraphnodecomp((void*)node->parents[node->nparents-2], (void*)parent) <= 0));
9369
9370 return SCIP_OKAY;
9371 }
9372
9373 /** ensures that array of parents in a node is sorted */
9374 static
exprgraphNodeSortParents(SCIP_EXPRGRAPHNODE * node)9375 void exprgraphNodeSortParents(
9376 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
9377 )
9378 {
9379 assert(node != NULL);
9380
9381 if( node->parentssorted )
9382 {
9383 #ifndef NDEBUG
9384 int i;
9385 for( i = 1; i < node->nparents; ++i )
9386 assert(exprgraphnodecomp((void*)node->parents[i-1], (void*)node->parents[i]) <= 0);
9387 #endif
9388 return;
9389 }
9390
9391 SCIPsortPtr((void**)node->parents, exprgraphnodecomp, node->nparents);
9392
9393 node->parentssorted = TRUE;
9394 }
9395
9396 /** removes a parent from an expression graph node
9397 *
9398 * If the node is not used and has no other parents, then it is freed.
9399 */
9400 static
exprgraphNodeRemoveParent(SCIP_EXPRGRAPH * exprgraph,SCIP_EXPRGRAPHNODE ** node,SCIP_EXPRGRAPHNODE * parent)9401 SCIP_RETCODE exprgraphNodeRemoveParent(
9402 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
9403 SCIP_EXPRGRAPHNODE** node, /**< expression graph node where to remove a parent, *node will be set to NULL */
9404 SCIP_EXPRGRAPHNODE* parent /**< parent node to remove */
9405 )
9406 {
9407 SCIP_EXPRGRAPHNODE* node_;
9408 int pos;
9409 int i;
9410
9411 assert(exprgraph != NULL);
9412 assert(node != NULL);
9413 assert(*node != NULL);
9414 assert((*node)->depth >= 0);
9415 assert((*node)->pos >= 0);
9416 assert((*node)->nparents > 0);
9417 assert(parent != NULL);
9418 assert(parent->depth >= 0);
9419 assert(parent->pos >= 0);
9420 assert(parent->depth > (*node)->depth); /* a parent node need to have larger depth */
9421
9422 /* find parent */
9423 exprgraphNodeSortParents(*node);
9424 (void) SCIPsortedvecFindPtr((void**)(*node)->parents, exprgraphnodecomp, (void*)parent, (*node)->nparents, &pos);
9425 assert(pos >= 0);
9426 assert(pos < (*node)->nparents);
9427 assert((*node)->parents[pos] == parent);
9428
9429 #ifdef SCIP_DISABLED_CODE
9430 /* move last parent to pos, if pos is before last
9431 * update sorted flag */
9432 if( pos < (*node)->nparents-1 )
9433 {
9434 (*node)->parents[pos] = (*node)->parents[(*node)->nparents-1];
9435 (*node)->parentssorted = ((*node)->nparents <= 2);
9436 }
9437 #else
9438 /* move all parents behind pos one position up
9439 * this is faster than moving the last parent to position pos if there are many repeated calls to this function as the parents array remains sorted
9440 */
9441 for( i = pos+1; i < (*node)->nparents; ++i )
9442 (*node)->parents[i-1] = (*node)->parents[i];
9443 #endif
9444 --(*node)->nparents;
9445
9446 /* keep pointer to *node in case it is still used */
9447 node_ = (*node)->nuses > 0 ? *node : NULL;
9448
9449 /* capture and release node so it is freed if possible */
9450 SCIPexprgraphCaptureNode(*node);
9451 SCIP_CALL( SCIPexprgraphReleaseNode(exprgraph, node) );
9452
9453 /* restore pointer, if node still exists */
9454 *node = node_;
9455
9456 return SCIP_OKAY;
9457 }
9458
9459 /** checks if a node is parent of a node */
9460 static
exprgraphNodeIsParent(SCIP_EXPRGRAPHNODE * node,SCIP_EXPRGRAPHNODE * parent)9461 SCIP_Bool exprgraphNodeIsParent(
9462 SCIP_EXPRGRAPHNODE* node, /**< expression graph node */
9463 SCIP_EXPRGRAPHNODE* parent /**< parent to look for */
9464 )
9465 {
9466 int pos;
9467
9468 assert(node != NULL);
9469 assert(parent != NULL);
9470
9471 /* if depth of node is at least as high as depth of parent, parent cannot be parent of node */
9472 if( node->depth >= parent->depth || node->nparents == 0 )
9473 return FALSE;
9474 assert(node->parents != NULL);
9475
9476 /* ensure parents array is sorted */
9477 exprgraphNodeSortParents(node);
9478
9479 return SCIPsortedvecFindPtr((void**)node->parents, exprgraphnodecomp, (void*)parent, node->nparents, &pos);
9480 }
9481
9482 /** adds expression graph nodes to the array of children of a sum, product, linear, quadratic, or polynomial expression
9483 *
9484 * For a sum or product expression, this corresponds to add additional summands and factors, resp.
9485 * For a linear expression, this corresponds to add each expression with coefficient 1.0.
9486 * For a quadratic or polynomial expression, only the children array may be enlarged, the expression itself remains the same.
9487 *
9488 * It is assumed that node and all exprs are in the expression graph already.
9489 * It is assumed that all expressions that are added have lower depth than node.
9490 */
9491 static
exprgraphNodeAddChildren(BMS_BLKMEM * blkmem,SCIP_EXPRGRAPHNODE * node,int nexprs,SCIP_EXPRGRAPHNODE ** exprs,int * childmap)9492 SCIP_RETCODE exprgraphNodeAddChildren(
9493 BMS_BLKMEM* blkmem, /**< block memory */
9494 SCIP_EXPRGRAPHNODE* node, /**< expression graph node */
9495 int nexprs, /**< number of children to add */
9496 SCIP_EXPRGRAPHNODE** exprs, /**< children nodes to add */
9497 int* childmap /**< array where to store mapping of indices from exprs to children array in node, or NULL if not of interest */
9498 )
9499 {
9500 int i;
9501 int j;
9502 int orignchildren;
9503 SCIP_Bool existsalready;
9504
9505 assert(blkmem != NULL);
9506 assert(node != NULL);
9507 assert(node->depth > 0);
9508 assert(node->pos >= 0);
9509 assert(node->op == SCIP_EXPR_SUM || node->op == SCIP_EXPR_PRODUCT || node->op == SCIP_EXPR_LINEAR || node->op == SCIP_EXPR_QUADRATIC || node->op == SCIP_EXPR_POLYNOMIAL);
9510 assert(exprs != NULL || nexprs == 0);
9511
9512 if( nexprs == 0 )
9513 return SCIP_OKAY;
9514
9515 orignchildren = node->nchildren;
9516 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &node->children, node->nchildren, node->nchildren + nexprs) );
9517
9518 for( i = 0; i < nexprs; ++i )
9519 {
9520 assert(exprs[i]->depth >= 0); /*lint !e613*/
9521 assert(exprs[i]->pos >= 0); /*lint !e613*/
9522 assert(exprs[i]->depth < node->depth); /*lint !e613*/
9523
9524 /* check if exprs[i] is a child already, if not SUM or PRODUCT */
9525 existsalready = FALSE;
9526 if( node->op != SCIP_EXPR_SUM && node->op != SCIP_EXPR_PRODUCT )
9527 for( j = 0; j < orignchildren; ++j )
9528 /* during simplification of polynomials, their may be NULL's in children array */
9529 if( node->children[j] != NULL && node->children[j] == exprs[i] ) /*lint !e613*/
9530 {
9531 existsalready = TRUE;
9532 break;
9533 }
9534
9535 if( !existsalready )
9536 {
9537 /* add exprs[i] to children array */
9538 node->children[node->nchildren] = exprs[i]; /*lint !e613*/
9539 SCIP_CALL( exprgraphNodeAddParent(blkmem, exprs[i], node) ); /*lint !e613*/
9540 if( childmap != NULL )
9541 childmap[i] = node->nchildren;
9542 ++node->nchildren;
9543 }
9544 else
9545 {
9546 if( childmap != NULL )
9547 childmap[i] = j; /*lint !e644*/
9548 if( node->op == SCIP_EXPR_LINEAR )
9549 {
9550 /* if linear expression, increase coefficient by 1.0 */
9551 ((SCIP_Real*)node->data.data)[j] += 1.0;
9552 }
9553 }
9554 }
9555
9556 /* shrink children array to actually used size */
9557 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &node->children, orignchildren + nexprs, node->nchildren) );
9558
9559 if( node->op == SCIP_EXPR_LINEAR && node->nchildren > orignchildren )
9560 {
9561 /* if linear expression, then add 1.0 coefficients for new expressions */
9562 SCIP_Real* data;
9563
9564 data = (SCIP_Real*)node->data.data;
9565 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &data, orignchildren + 1, node->nchildren + 1) );
9566 data[node->nchildren] = data[orignchildren]; /* move constant from old end to new end */
9567 for( i = orignchildren; i < node->nchildren; ++i )
9568 data[i] = 1.0;
9569 node->data.data = (void*)data;
9570 }
9571 else if( node->op == SCIP_EXPR_QUADRATIC && node->nchildren > orignchildren )
9572 {
9573 /* if quadratic expression, then add 0.0 linear coefficients for new expressions */
9574 SCIP_EXPRDATA_QUADRATIC* data;
9575
9576 data = (SCIP_EXPRDATA_QUADRATIC*)node->data.data;
9577 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &data->lincoefs, orignchildren, node->nchildren) );
9578 BMSclearMemoryArray(&data->lincoefs[orignchildren], node->nchildren - orignchildren); /*lint !e866*/
9579 }
9580
9581 node->simplified = FALSE;
9582
9583 return SCIP_OKAY;
9584 }
9585
9586 /** replaces a child node by another node
9587 *
9588 * Assumes that both nodes represent the same expression.
9589 * If this node was the last parent of oldchild and oldchild is not in use, then it is freed.
9590 * newchild must have deeper depth than node.
9591 */
9592 static
exprgraphNodeReplaceChild(SCIP_EXPRGRAPH * exprgraph,SCIP_EXPRGRAPHNODE * node,SCIP_EXPRGRAPHNODE ** oldchild,SCIP_EXPRGRAPHNODE * newchild)9593 SCIP_RETCODE exprgraphNodeReplaceChild(
9594 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
9595 SCIP_EXPRGRAPHNODE* node, /**< pointer to expression graph node */
9596 SCIP_EXPRGRAPHNODE** oldchild, /**< child node that should be replaced, it may be freed */
9597 SCIP_EXPRGRAPHNODE* newchild /**< node that should take position of oldchild */
9598 )
9599 {
9600 int childpos = -1;
9601
9602 assert(exprgraph != NULL);
9603 assert(node != NULL);
9604 assert(oldchild != NULL);
9605 assert(*oldchild != NULL);
9606 assert(newchild != NULL);
9607
9608 if( *oldchild == newchild )
9609 return SCIP_OKAY;
9610
9611 SCIPdebugMessage("replace child %p in node %p by %p\n", (void*)*oldchild, (void*)node, (void*)newchild);
9612
9613 /* let's see if child is just next to the place where we looked in a previous call to this function */
9614 if( exprgraph->lastreplacechildpos >= 0 && exprgraph->lastreplacechildpos+1 < node->nchildren && node->children[exprgraph->lastreplacechildpos+1] == *oldchild )
9615 {
9616 childpos = exprgraph->lastreplacechildpos+1;
9617 }
9618 else for( childpos = 0; childpos < node->nchildren; ++childpos )
9619 {
9620 /* search for oldchild in children array */
9621 if( node->children[childpos] == *oldchild )
9622 break;
9623 }
9624 assert(childpos >= 0);
9625 assert(childpos < node->nchildren);
9626 assert(node->children[childpos] == *oldchild);
9627
9628 /* add as parent to newchild */
9629 SCIP_CALL( exprgraphNodeAddParent(exprgraph->blkmem, newchild, node) );
9630
9631 /* remove as parent from oldchild */
9632 SCIP_CALL( exprgraphNodeRemoveParent(exprgraph, oldchild, node) );
9633
9634 /* set newchild as child i */
9635 node->children[childpos] = newchild;
9636
9637 node->simplified = FALSE;
9638
9639 /* remember to look next to childpos first next time */
9640 exprgraph->lastreplacechildpos = childpos;
9641
9642 return SCIP_OKAY;
9643 }
9644
9645 /** comparison of SCIP_EXPRGRAPHNODE's that are of type SCIP_EXPR_CONST
9646 *
9647 * A node is larger than another node, if their corresponding constants are related that way.
9648 */
9649 static
SCIP_DECL_SORTPTRCOMP(exprgraphConstNodeComp)9650 SCIP_DECL_SORTPTRCOMP(exprgraphConstNodeComp)
9651 {
9652 assert(elem1 != NULL);
9653 assert(elem2 != NULL);
9654 assert(((SCIP_EXPRGRAPHNODE*)elem1)->op == SCIP_EXPR_CONST);
9655 assert(((SCIP_EXPRGRAPHNODE*)elem2)->op == SCIP_EXPR_CONST);
9656 assert(((SCIP_EXPRGRAPHNODE*)elem1)->data.dbl == ((SCIP_EXPRGRAPHNODE*)elem1)->data.dbl); /* assert that const value is not nan */ /*lint !e777*/
9657 assert(((SCIP_EXPRGRAPHNODE*)elem2)->data.dbl == ((SCIP_EXPRGRAPHNODE*)elem2)->data.dbl); /* assert that const value is not nan */ /*lint !e777*/
9658
9659 if( ((SCIP_EXPRGRAPHNODE*)elem1)->data.dbl > ((SCIP_EXPRGRAPHNODE*)elem2)->data.dbl )
9660 return 1;
9661 else if( ((SCIP_EXPRGRAPHNODE*)elem1)->data.dbl < ((SCIP_EXPRGRAPHNODE*)elem2)->data.dbl )
9662 return -1;
9663 else
9664 return 0;
9665 }
9666
9667 /** sort array of nodes that holds constants */
9668 static
exprgraphSortConstNodes(SCIP_EXPRGRAPH * exprgraph)9669 void exprgraphSortConstNodes(
9670 SCIP_EXPRGRAPH* exprgraph /**< expression graph */
9671 )
9672 {
9673 assert(exprgraph != NULL);
9674
9675 if( exprgraph->constssorted )
9676 return;
9677
9678 SCIPsortPtr((void**)exprgraph->constnodes, exprgraphConstNodeComp, exprgraph->nconsts);
9679
9680 exprgraph->constssorted = TRUE;
9681 }
9682
9683 /** finds position of expression graph node corresponding to a constant in constnodes array */
9684 static
exprgraphFindConstNodePos(SCIP_EXPRGRAPH * exprgraph,SCIP_EXPRGRAPHNODE * node,int * pos)9685 SCIP_Bool exprgraphFindConstNodePos(
9686 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
9687 SCIP_EXPRGRAPHNODE* node, /**< node to search for */
9688 int* pos /**< buffer to store position of node, if found */
9689 )
9690 {
9691 int left;
9692 int right;
9693 int middle;
9694
9695 assert(exprgraph != NULL);
9696 assert(node != NULL);
9697 assert(node->op == SCIP_EXPR_CONST);
9698 assert(node->depth == 0);
9699 assert(node->pos >= 0);
9700 assert(pos != NULL);
9701
9702 exprgraphSortConstNodes(exprgraph);
9703 assert(exprgraph->constssorted);
9704
9705 /* find a node with constant node->data.dbl using binary search */
9706 left = 0;
9707 right = exprgraph->nconsts-1;
9708 *pos = -1;
9709 while( left <= right )
9710 {
9711 middle = (left+right)/2;
9712 assert(0 <= middle && middle < exprgraph->nconsts);
9713
9714 if( node->data.dbl < exprgraph->constnodes[middle]->data.dbl )
9715 right = middle - 1;
9716 else if( node->data.dbl > exprgraph->constnodes[middle]->data.dbl )
9717 left = middle + 1;
9718 else
9719 {
9720 *pos = middle;
9721 break;
9722 }
9723 }
9724 assert(left == right+1 || *pos >= 0);
9725 if( left == right+1 )
9726 return FALSE;
9727
9728 /* search left of *pos to find node */
9729 while( exprgraph->constnodes[*pos] != node && *pos > 0 && exprgraph->constnodes[*pos-1]->data.dbl == node->data.dbl ) /*lint !e777*/
9730 --*pos;
9731 /* search right of *pos to find node */
9732 while( exprgraph->constnodes[*pos] != node && *pos < exprgraph->nconsts-1 && exprgraph->constnodes[*pos+1]->data.dbl == node->data.dbl ) /*lint !e777*/
9733 ++*pos;
9734
9735 return exprgraph->constnodes[*pos] == node;
9736 }
9737
9738 /** creates an expression graph node */
9739 static
exprgraphCreateNode(BMS_BLKMEM * blkmem,SCIP_EXPRGRAPHNODE ** node,SCIP_EXPROP op,SCIP_EXPROPDATA opdata)9740 SCIP_RETCODE exprgraphCreateNode(
9741 BMS_BLKMEM* blkmem, /**< block memory */
9742 SCIP_EXPRGRAPHNODE** node, /**< buffer to store expression graph node */
9743 SCIP_EXPROP op, /**< operator type of expression */
9744 SCIP_EXPROPDATA opdata /**< operator data of expression */
9745 )
9746 {
9747 assert(blkmem != NULL);
9748 assert(node != NULL);
9749
9750 SCIP_ALLOC( BMSallocBlockMemory(blkmem, node) );
9751 BMSclearMemory(*node);
9752
9753 (*node)->op = op;
9754 (*node)->data = opdata;
9755
9756 /* mark graph position as not in graph yet */
9757 (*node)->depth = -1;
9758 (*node)->pos = -1;
9759
9760 /* arrays of length 0 are trivially sorted */
9761 (*node)->parentssorted = TRUE;
9762
9763 /* set bounds interval to entire */
9764 (*node)->boundstatus = SCIP_EXPRBOUNDSTATUS_VALID;
9765 SCIPintervalSetEntire(SCIP_REAL_MAX, &(*node)->bounds);
9766
9767 /* set initial value to invalid */
9768 (*node)->value = SCIP_INVALID;
9769
9770 /* set initial curvature to linear for variables, parameters, and constants and unknown otherwise */
9771 if( op == SCIP_EXPR_VARIDX || op == SCIP_EXPR_CONST || op == SCIP_EXPR_PARAM )
9772 (*node)->curv = SCIP_EXPRCURV_LINEAR;
9773 else
9774 (*node)->curv = SCIP_EXPRCURV_UNKNOWN;
9775
9776 /* per default, a node is enabled */
9777 (*node)->enabled = TRUE;
9778
9779 return SCIP_OKAY;
9780 }
9781
9782 /** prints the expression corresponding to a node (not recursively) */
9783 static
exprgraphPrintNodeExpression(SCIP_EXPRGRAPHNODE * node,SCIP_MESSAGEHDLR * messagehdlr,FILE * file,const char ** varnames,SCIP_Bool printchildrenbounds)9784 void exprgraphPrintNodeExpression(
9785 SCIP_EXPRGRAPHNODE* node, /**< node of expression graph */
9786 SCIP_MESSAGEHDLR* messagehdlr, /**< message handler */
9787 FILE* file, /**< file to print to, or NULL for stdout */
9788 const char** varnames, /**< variable names, or NULL for generic names */
9789 SCIP_Bool printchildrenbounds /**< whether to print bounds of children */
9790 )
9791 {
9792 int i;
9793
9794 assert(node != NULL);
9795
9796 switch( node->op )
9797 {
9798 case SCIP_EXPR_VARIDX:
9799 if( varnames != NULL )
9800 {
9801 SCIPmessageFPrintInfo(messagehdlr, file, "<%s>", (const char*)varnames[node->data.intval]);
9802 }
9803 else
9804 SCIPmessageFPrintInfo(messagehdlr, file, "x%d", node->data.intval);
9805 break;
9806
9807 case SCIP_EXPR_CONST:
9808 SCIPmessageFPrintInfo(messagehdlr, file, "%g", node->data.dbl);
9809 break;
9810
9811 case SCIP_EXPR_PARAM:
9812 SCIPmessageFPrintInfo(messagehdlr, file, "param%d", node->data.intval);
9813 break;
9814
9815 case SCIP_EXPR_PLUS:
9816 if( printchildrenbounds )
9817 SCIPmessageFPrintInfo(messagehdlr, file, "c0[%10g,%10g]", node->children[0]->bounds.inf, node->children[0]->bounds.sup);
9818 SCIPmessageFPrintInfo(messagehdlr, file, "+");
9819 if( printchildrenbounds )
9820 SCIPmessageFPrintInfo(messagehdlr, file, "c1[%10g,%10g]", node->children[1]->bounds.inf, node->children[1]->bounds.sup);
9821 break;
9822
9823 case SCIP_EXPR_MINUS:
9824 if( printchildrenbounds )
9825 SCIPmessageFPrintInfo(messagehdlr, file, "c0[%10g,%10g]", node->children[0]->bounds.inf, node->children[0]->bounds.sup);
9826 SCIPmessageFPrintInfo(messagehdlr, file, "-");
9827 if( printchildrenbounds )
9828 SCIPmessageFPrintInfo(messagehdlr, file, "c1[%10g,%10g]", node->children[1]->bounds.inf, node->children[1]->bounds.sup);
9829 break;
9830
9831 case SCIP_EXPR_MUL:
9832 if( printchildrenbounds )
9833 SCIPmessageFPrintInfo(messagehdlr, file, "c0[%10g,%10g]", node->children[0]->bounds.inf, node->children[0]->bounds.sup);
9834 SCIPmessageFPrintInfo(messagehdlr, file, "*");
9835 if( printchildrenbounds )
9836 SCIPmessageFPrintInfo(messagehdlr, file, "c1[%10g,%10g]", node->children[1]->bounds.inf, node->children[1]->bounds.sup);
9837 break;
9838
9839 case SCIP_EXPR_DIV:
9840 if( printchildrenbounds )
9841 SCIPmessageFPrintInfo(messagehdlr, file, "c0[%10g,%10g]", node->children[0]->bounds.inf, node->children[0]->bounds.sup);
9842 SCIPmessageFPrintInfo(messagehdlr, file, "/");
9843 if( printchildrenbounds )
9844 SCIPmessageFPrintInfo(messagehdlr, file, "c1[%10g,%10g]", node->children[1]->bounds.inf, node->children[1]->bounds.sup);
9845 break;
9846
9847 case SCIP_EXPR_SQUARE:
9848 if( printchildrenbounds )
9849 SCIPmessageFPrintInfo(messagehdlr, file, "c0[%10g,%10g]", node->children[0]->bounds.inf, node->children[0]->bounds.sup);
9850 SCIPmessageFPrintInfo(messagehdlr, file, "^2");
9851 break;
9852
9853 case SCIP_EXPR_REALPOWER:
9854 if( printchildrenbounds )
9855 SCIPmessageFPrintInfo(messagehdlr, file, "c0[%10g,%10g]", node->children[0]->bounds.inf, node->children[0]->bounds.sup);
9856 SCIPmessageFPrintInfo(messagehdlr, file, "^%g", node->data.dbl);
9857 break;
9858
9859 case SCIP_EXPR_SIGNPOWER:
9860 if( printchildrenbounds )
9861 SCIPmessageFPrintInfo(messagehdlr, file, "sign(c0)|c0[%10g,%10g]|^%g",
9862 node->children[0]->bounds.inf, node->children[0]->bounds.sup, node->data.dbl);
9863 else
9864 SCIPmessageFPrintInfo(messagehdlr, file, "sign(c0)|c0|^%g", node->data.dbl);
9865 break;
9866
9867 case SCIP_EXPR_INTPOWER:
9868 SCIPmessageFPrintInfo(messagehdlr, file, "c0");
9869 if( printchildrenbounds )
9870 SCIPmessageFPrintInfo(messagehdlr, file, "[%10g,%10g]", node->children[0]->bounds.inf, node->children[0]->bounds.sup);
9871 SCIPmessageFPrintInfo(messagehdlr, file, "^%d", node->data.intval);
9872 break;
9873
9874 case SCIP_EXPR_SQRT:
9875 case SCIP_EXPR_EXP:
9876 case SCIP_EXPR_LOG:
9877 case SCIP_EXPR_SIN:
9878 case SCIP_EXPR_COS:
9879 case SCIP_EXPR_TAN:
9880 /* SCIP_EXPR_ERF = 20, */ /**< gaussian error function (1 operand) */
9881 /* SCIP_EXPR_ERFI = 21, */ /**< imaginary part of gaussian error function (1 operand) */
9882 case SCIP_EXPR_MIN:
9883 case SCIP_EXPR_MAX:
9884 case SCIP_EXPR_ABS:
9885 case SCIP_EXPR_SIGN:
9886 SCIPmessageFPrintInfo(messagehdlr, file, "%s", (const char*)SCIPexpropGetName(node->op));
9887 if( printchildrenbounds )
9888 {
9889 SCIPmessageFPrintInfo(messagehdlr, file, "(c0[%10g,%10g]", node->children[0]->bounds.inf, node->children[0]->bounds.sup);
9890 if( node->nchildren == 2 )
9891 SCIPmessageFPrintInfo(messagehdlr, file, ",c1[%10g,%10g]", node->children[1]->bounds.inf, node->children[1]->bounds.sup);
9892 SCIPmessageFPrintInfo(messagehdlr, file, ")");
9893 }
9894 break;
9895
9896 case SCIP_EXPR_SUM:
9897 if( printchildrenbounds )
9898 for( i = 0; i < node->nchildren; ++i )
9899 {
9900 if( i > 0 )
9901 SCIPmessageFPrintInfo(messagehdlr, file, "+");
9902 SCIPmessageFPrintInfo(messagehdlr, file, "c%d[%10g,%10g]", i, node->children[i]->bounds.inf, node->children[i]->bounds.sup);
9903 }
9904 else
9905 SCIPmessageFPrintInfo(messagehdlr, file, "+");
9906 break;
9907
9908 case SCIP_EXPR_PRODUCT:
9909 if( printchildrenbounds )
9910 for( i = 0; i < node->nchildren; ++i )
9911 {
9912 if( i > 0 )
9913 SCIPmessageFPrintInfo(messagehdlr, file, "*");
9914 SCIPmessageFPrintInfo(messagehdlr, file, "c%d[%10g,%10g]", i, node->children[i]->bounds.inf, node->children[i]->bounds.sup);
9915 }
9916 else
9917 SCIPmessageFPrintInfo(messagehdlr, file, "*");
9918 break;
9919
9920 case SCIP_EXPR_LINEAR:
9921 {
9922 SCIP_Real constant;
9923
9924 constant = ((SCIP_Real*)node->data.data)[node->nchildren];
9925
9926 if( constant != 0.0 || node->nchildren == 0 )
9927 SCIPmessageFPrintInfo(messagehdlr, file, "%g", constant);
9928
9929 for( i = 0; i < node->nchildren; ++i )
9930 {
9931 if( ((SCIP_Real*)node->data.data)[i] == 1.0 )
9932 SCIPmessageFPrintInfo(messagehdlr, file, "+");
9933 else if( ((SCIP_Real*)node->data.data)[i] == -1.0 )
9934 SCIPmessageFPrintInfo(messagehdlr, file, "-");
9935 else
9936 SCIPmessageFPrintInfo(messagehdlr, file, "%+g*", ((SCIP_Real*)node->data.data)[i]);
9937 SCIPmessageFPrintInfo(messagehdlr, file, "c%d", i);
9938 if( printchildrenbounds )
9939 SCIPmessageFPrintInfo(messagehdlr, file, "[%10g,%10g]", node->children[i]->bounds.inf, node->children[i]->bounds.sup);
9940 }
9941
9942 break;
9943 }
9944
9945 case SCIP_EXPR_QUADRATIC:
9946 {
9947 SCIP_EXPRDATA_QUADRATIC* quadraticdata;
9948
9949 quadraticdata = (SCIP_EXPRDATA_QUADRATIC*)node->data.data;
9950 assert(quadraticdata != NULL);
9951
9952 if( quadraticdata->constant != 0.0 )
9953 SCIPmessageFPrintInfo(messagehdlr, file, "%g", quadraticdata->constant);
9954
9955 if( quadraticdata->lincoefs != NULL )
9956 for( i = 0; i < node->nchildren; ++i )
9957 {
9958 if( quadraticdata->lincoefs[i] == 0.0 )
9959 continue;
9960 SCIPmessageFPrintInfo(messagehdlr, file, "%+g*c%d", quadraticdata->lincoefs[i], i);
9961 if( printchildrenbounds )
9962 SCIPmessageFPrintInfo(messagehdlr, file, "[%10g,%10g]", node->children[i]->bounds.inf, node->children[i]->bounds.sup);
9963 }
9964
9965 for( i = 0; i < quadraticdata->nquadelems; ++i )
9966 {
9967 if( quadraticdata->quadelems[i].coef == 1.0 )
9968 SCIPmessageFPrintInfo(messagehdlr, file, "+");
9969 else if( quadraticdata->quadelems[i].coef == -1.0 )
9970 SCIPmessageFPrintInfo(messagehdlr, file, "-");
9971 else
9972 SCIPmessageFPrintInfo(messagehdlr, file, "%+g*", quadraticdata->quadelems[i].coef);
9973 SCIPmessageFPrintInfo(messagehdlr, file, "c%d", quadraticdata->quadelems[i].idx1);
9974 if( printchildrenbounds )
9975 SCIPmessageFPrintInfo(messagehdlr, file, "[%10g,%10g]", node->children[quadraticdata->quadelems[i].idx1]->bounds.inf, node->children[quadraticdata->quadelems[i].idx1]->bounds.sup);
9976 if( quadraticdata->quadelems[i].idx1 == quadraticdata->quadelems[i].idx2 )
9977 SCIPmessageFPrintInfo(messagehdlr, file, "^2");
9978 else
9979 {
9980 SCIPmessageFPrintInfo(messagehdlr, file, "*c%d", quadraticdata->quadelems[i].idx2);
9981 if( printchildrenbounds )
9982 SCIPmessageFPrintInfo(messagehdlr, file, "[%10g,%10g]", node->children[quadraticdata->quadelems[i].idx2]->bounds.inf, node->children[quadraticdata->quadelems[i].idx2]->bounds.sup);
9983 }
9984 }
9985
9986 break;
9987 }
9988
9989 case SCIP_EXPR_POLYNOMIAL:
9990 {
9991 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata;
9992 SCIP_EXPRDATA_MONOMIAL* monomialdata;
9993 int j;
9994
9995 polynomialdata = (SCIP_EXPRDATA_POLYNOMIAL*)node->data.data;
9996 assert(polynomialdata != NULL);
9997
9998 if( polynomialdata->constant != 0.0 || polynomialdata->nmonomials == 0 )
9999 {
10000 SCIPmessageFPrintInfo(messagehdlr, file, "%g", polynomialdata->constant);
10001 }
10002
10003 for( i = 0; i < polynomialdata->nmonomials; ++i )
10004 {
10005 monomialdata = polynomialdata->monomials[i];
10006 if( monomialdata->coef == 1.0 )
10007 SCIPmessageFPrintInfo(messagehdlr, file, "+");
10008 else if( monomialdata->coef == -1.0 )
10009 SCIPmessageFPrintInfo(messagehdlr, file, "-");
10010 else
10011 SCIPmessageFPrintInfo(messagehdlr, file, "%+g", monomialdata->coef);
10012
10013 for( j = 0; j < monomialdata->nfactors; ++j )
10014 {
10015 SCIPmessageFPrintInfo(messagehdlr, file, "c%d", monomialdata->childidxs[j]);
10016 if( printchildrenbounds )
10017 SCIPmessageFPrintInfo(messagehdlr, file, "[%10g,%10g]", node->children[monomialdata->childidxs[j]]->bounds.inf, node->children[monomialdata->childidxs[j]]->bounds.sup);
10018 if( monomialdata->exponents[j] < 0.0 )
10019 SCIPmessageFPrintInfo(messagehdlr, file, "^(%g)", monomialdata->exponents[j]);
10020 else if( monomialdata->exponents[j] != 1.0 )
10021 SCIPmessageFPrintInfo(messagehdlr, file, "^%g", monomialdata->exponents[j]);
10022 }
10023 }
10024
10025 break;
10026 }
10027
10028 case SCIP_EXPR_LAST:
10029 SCIPABORT();
10030 break;
10031
10032 default:
10033 SCIPmessageFPrintInfo(messagehdlr, file, "%s", SCIPexpropGetName(node->op));
10034 break;
10035 } /*lint !e788*/
10036 }
10037
10038 /** prints a node of an expression graph */
10039 static
exprgraphPrintNodeDot(SCIP_EXPRGRAPH * exprgraph,SCIP_EXPRGRAPHNODE * node,SCIP_MESSAGEHDLR * messagehdlr,FILE * file,const char ** varnames)10040 void exprgraphPrintNodeDot(
10041 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
10042 SCIP_EXPRGRAPHNODE* node, /**< node of expression graph */
10043 SCIP_MESSAGEHDLR* messagehdlr, /**< message handler */
10044 FILE* file, /**< file to print to, or NULL for stdout */
10045 const char** varnames /**< variable names, or NULL for generic names */
10046 )
10047 {
10048 SCIP_Real color;
10049 int i;
10050
10051 assert(exprgraph != NULL);
10052 assert(node != NULL);
10053 assert(file != NULL);
10054
10055 color = (SCIP_Real)node->op / (SCIP_Real)SCIP_EXPR_LAST;
10056 SCIPmessageFPrintInfo(messagehdlr, file, "n%d_%d [fillcolor=\"%g,%g,%g\", label=\"", node->depth, node->pos, color, color, color);
10057
10058 exprgraphPrintNodeExpression(node, messagehdlr, file, varnames, FALSE);
10059
10060 SCIPmessageFPrintInfo(messagehdlr, file, "\\n[%g,%g]", node->bounds.inf, node->bounds.sup);
10061 if( node->boundstatus & SCIP_EXPRBOUNDSTATUS_CHILDRELAXED )
10062 SCIPmessageFPrintInfo(messagehdlr, file, "!");
10063 if( node->boundstatus & SCIP_EXPRBOUNDSTATUS_CHILDTIGHTENED )
10064 SCIPmessageFPrintInfo(messagehdlr, file, "*");
10065 if( node->boundstatus & SCIP_EXPRBOUNDSTATUS_TIGHTENEDBYPARENT )
10066 SCIPmessageFPrintInfo(messagehdlr, file, "+");
10067
10068 SCIPmessageFPrintInfo(messagehdlr, file, "\"");
10069
10070 if( !node->enabled )
10071 SCIPmessageFPrintInfo(messagehdlr, file, ", style=dotted");
10072
10073 SCIPmessageFPrintInfo(messagehdlr, file, "]\n");
10074
10075 /* add edges from node to children */
10076 for( i = 0; i < node->nchildren; ++i )
10077 SCIPmessageFPrintInfo(messagehdlr, file, "n%d_%d -> n%d_%d [label=\"c%d\"]\n", node->depth, node->pos, node->children[i]->depth, node->children[i]->pos, i);
10078 }
10079
10080 /** evaluate node of expression graph w.r.t. values stored in children */
10081 static
exprgraphNodeEval(SCIP_EXPRGRAPHNODE * node,SCIP_Real * varvals)10082 SCIP_RETCODE exprgraphNodeEval(
10083 SCIP_EXPRGRAPHNODE* node, /**< expression graph node */
10084 SCIP_Real* varvals /**< values for variables */
10085 )
10086 {
10087 int i;
10088 SCIP_Real staticbuf[SCIP_EXPRESSION_MAXCHILDEST];
10089 SCIP_Real* buf;
10090
10091 assert(node != NULL);
10092
10093 /* if many children, get large enough memory to store argument values */
10094 if( node->nchildren > SCIP_EXPRESSION_MAXCHILDEST )
10095 {
10096 SCIP_ALLOC( BMSallocMemoryArray(&buf, node->nchildren) );
10097 }
10098 else
10099 {
10100 buf = staticbuf;
10101 }
10102
10103 /* get values of children */
10104 for( i = 0; i < node->nchildren; ++i )
10105 {
10106 assert(node->children[i]->value != SCIP_INVALID); /*lint !e777*/
10107 buf[i] = node->children[i]->value; /*lint !e644*/
10108 }
10109
10110 /* evaluate this expression */
10111 assert(exprOpTable[node->op].eval != NULL);
10112 SCIP_CALL( exprOpTable[node->op].eval(node->data, node->nchildren, buf, varvals, NULL, &node->value) );
10113 assert(node->value != SCIP_INVALID); /*lint !e777*/
10114
10115 /* free memory, if allocated before */
10116 if( staticbuf != buf )
10117 {
10118 BMSfreeMemoryArray(&buf);
10119 }
10120
10121 return SCIP_OKAY;
10122 }
10123
10124 /** evaluates node including subtree */
10125 static
exprgraphNodeEvalWithChildren(SCIP_EXPRGRAPHNODE * node,SCIP_Real * varvals)10126 SCIP_RETCODE exprgraphNodeEvalWithChildren(
10127 SCIP_EXPRGRAPHNODE* node, /**< expression graph node */
10128 SCIP_Real* varvals /**< values for variables */
10129 )
10130 {
10131 int i;
10132
10133 assert(node != NULL);
10134
10135 for( i = 0; i < node->nchildren; ++i )
10136 {
10137 SCIP_CALL( exprgraphNodeEvalWithChildren(node->children[i], varvals) );
10138 }
10139
10140 SCIP_CALL( exprgraphNodeEval(node, varvals) );
10141
10142 return SCIP_OKAY;
10143 }
10144
10145 /** updates bounds of a node if a children has changed its bounds */
10146 static
exprgraphNodeUpdateBounds(SCIP_EXPRGRAPHNODE * node,SCIP_Real infinity,SCIP_Real minstrength,SCIP_Bool parenttightenisinvalid)10147 SCIP_RETCODE exprgraphNodeUpdateBounds(
10148 SCIP_EXPRGRAPHNODE* node, /**< node of expression graph */
10149 SCIP_Real infinity, /**< value for infinity in interval arithmetics */
10150 SCIP_Real minstrength, /**< minimal required relative bound strengthening in a node to trigger a bound recalculation in parent nodes */
10151 SCIP_Bool parenttightenisinvalid /**< whether to consider bounds that have been tightened by parents as invalid */
10152 )
10153 {
10154 SCIP_INTERVAL childboundsstatic[SCIP_EXPRESSION_MAXCHILDEST];
10155 SCIP_INTERVAL* childbounds;
10156 SCIP_INTERVAL newbounds;
10157 int i;
10158
10159 assert(node != NULL);
10160 assert(node->depth >= 1); /* node should be in graph and not be at depth 0 (i.e., no variable, constant, or parameter) */
10161 assert(node->pos >= 0); /* node should be in graph */
10162 assert(node->op != SCIP_EXPR_VARIDX);
10163 assert(node->op != SCIP_EXPR_PARAM);
10164
10165 /* if we still have valid bounds and also no child got a bound tightening, then nothing to do
10166 * if node is disabled, then also do nothing */
10167 if( node->boundstatus == SCIP_EXPRBOUNDSTATUS_VALID || !node->enabled )
10168 return SCIP_OKAY;
10169
10170 /* if many children, get large enough memory to store children bounds */
10171 if( node->nchildren > SCIP_EXPRESSION_MAXCHILDEST )
10172 {
10173 SCIP_ALLOC( BMSallocMemoryArray(&childbounds, node->nchildren) );
10174 }
10175 else
10176 {
10177 childbounds = childboundsstatic;
10178 }
10179
10180 /* assemble bounds of children */
10181 for( i = 0; i < node->nchildren; ++i )
10182 {
10183 /* child should have valid and non-empty bounds */
10184 assert(!(node->children[i]->boundstatus & SCIP_EXPRBOUNDSTATUS_CHILDRELAXED));
10185 assert(!SCIPintervalIsEmpty(infinity, node->children[i]->bounds));
10186
10187 childbounds[i] = node->children[i]->bounds; /*lint !e644*/
10188 }
10189
10190 /* call interval evaluation function for this operand */
10191 assert( exprOpTable[node->op].inteval != NULL );
10192 SCIPintervalSet(&newbounds, 0.0);
10193 SCIP_CALL( exprOpTable[node->op].inteval(infinity, node->data, node->nchildren, childbounds, NULL, NULL, &newbounds) );
10194
10195 /* free memory, if allocated before */
10196 if( childbounds != childboundsstatic )
10197 {
10198 BMSfreeMemoryArray(&childbounds);
10199 }
10200
10201 /* NOTE: if you change code below, please make analog changes also in SCIPexprgraphUpdateNodeBoundsCurvature */
10202
10203 /* if bounds of a children were relaxed or our bounds were tightened by a (now possibly invalid) reverse propagation from a parent
10204 * and now our bounds are relaxed, then we have to propagate this upwards to ensure valid bounds
10205 *
10206 * if bounds were tightened (considerably), then tell this to those parents which think that they have valid bounds
10207 *
10208 * finally, if there was only a little tightening, then keep this updated bounds, but don't notify parents
10209 */
10210 if( (newbounds.inf < node->bounds.inf || newbounds.sup > node->bounds.sup) &&
10211 ((node->boundstatus & SCIP_EXPRBOUNDSTATUS_CHILDRELAXED) || ((node->boundstatus & SCIP_EXPRBOUNDSTATUS_TIGHTENEDBYPARENT) && parenttightenisinvalid)) )
10212 {
10213 for( i = 0; i < node->nparents; ++i )
10214 node->parents[i]->boundstatus = SCIP_EXPRBOUNDSTATUS_CHILDRELAXED;
10215
10216 node->bounds = newbounds;
10217 }
10218 else if( isLbBetter(minstrength, newbounds.inf, node->bounds.inf, node->bounds.sup) ||
10219 ( isUbBetter(minstrength, newbounds.sup, node->bounds.inf, node->bounds.sup)) )
10220 {
10221 for( i = 0; i < node->nparents; ++i )
10222 node->parents[i]->boundstatus |= SCIP_EXPRBOUNDSTATUS_CHILDTIGHTENED;
10223
10224 node->bounds = newbounds;
10225 }
10226 else
10227 {
10228 SCIPintervalIntersect(&node->bounds, node->bounds, newbounds);
10229 }
10230
10231 SCIPdebugMessage("updated bounds of node %p (%d,%d) op %s to [%g,%g]\n", (void*)node, node->depth, node->pos, SCIPexpropGetName(node->op), node->bounds.inf, node->bounds.sup);
10232
10233 /* node now has valid bounds */
10234 node->boundstatus = SCIP_EXPRBOUNDSTATUS_VALID;
10235
10236 return SCIP_OKAY;
10237 }
10238
10239 /** propagate bounds of a node into children by reverting the nodes expression */
10240 static
exprgraphNodePropagateBounds(SCIP_EXPRGRAPH * exprgraph,SCIP_EXPRGRAPHNODE * node,SCIP_Real infinity,SCIP_Real minstrength,SCIP_Bool * cutoff)10241 void exprgraphNodePropagateBounds(
10242 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
10243 SCIP_EXPRGRAPHNODE* node, /**< node in expression graph with no parents */
10244 SCIP_Real infinity, /**< value for infinity in interval arithmetics */
10245 SCIP_Real minstrength, /**< minimal required relative bound strengthening in a node to trigger a propagation into children nodes */
10246 SCIP_Bool* cutoff /**< buffer to store whether a node's bounds were propagated to an empty interval */
10247 )
10248 {
10249 SCIP_INTERVAL childbounds;
10250 int i;
10251
10252 assert(exprgraph != NULL);
10253 assert(node != NULL);
10254 assert(node->depth >= 0); /* node should be in graph */
10255 assert(node->pos >= 0); /* node should be in graph */
10256 assert(minstrength >= 0.0);
10257 assert(cutoff != NULL);
10258 assert(!SCIPintervalIsEmpty(infinity, node->bounds)); /* should not call backward prop. for a node that yield a cutoff already */
10259 assert(!node->enabled || !(node->boundstatus & SCIP_EXPRBOUNDSTATUS_CHILDRELAXED)); /* there should be no unprocessed relaxations of children bounds, if node is enabled */
10260
10261 /* if we have no recent bound tightening from a parent, then no use in reverse-propagating our bounds */
10262 if( (node->boundstatus & SCIP_EXPRBOUNDSTATUS_TIGHTENEDBYPARENTRECENT) == 0 )
10263 return;
10264
10265 /* if node is not enabled, then do nothing */
10266 if( !node->enabled )
10267 return;
10268
10269 /* tell children that they should propagate their bounds even if not tightened */
10270 if( (node->boundstatus & SCIP_EXPRBOUNDSTATUS_TIGHTENEDBYPARENTFORCE) == SCIP_EXPRBOUNDSTATUS_TIGHTENEDBYPARENTFORCE )
10271 minstrength = -1.0;
10272
10273 /* we will do something, so reset boundstatus to "tightened-by-parent, but not recently" */
10274 node->boundstatus = SCIP_EXPRBOUNDSTATUS_TIGHTENEDBYPARENT;
10275
10276 /* SCIPdebugMessage("propagating node %p (%d,%d) op %s: [%10g,%10g] = ", (void*)node, node->depth, node->pos, SCIPexpropGetName(node->op), node->bounds.inf, node->bounds.sup);
10277 * SCIPdebug( exprgraphPrintNodeExpression(node, messagehdlr, NULL, NULL, TRUE) );
10278 * SCIPdebugPrintf("\n");
10279 */
10280
10281 /* @todo add callback to exprOpTable for this */
10282
10283 switch( node->op )
10284 {
10285 case SCIP_EXPR_VARIDX:
10286 case SCIP_EXPR_CONST:
10287 case SCIP_EXPR_PARAM:
10288 /* cannot propagate bound changes further */
10289 break;
10290
10291 case SCIP_EXPR_PLUS:
10292 {
10293 assert(node->nchildren == 2);
10294 /* f = c0 + c1 -> c0 = f - c1, c1 = f - c0 */
10295
10296 SCIPintervalSub(infinity, &childbounds, node->bounds, node->children[1]->bounds);
10297 SCIPexprgraphTightenNodeBounds(exprgraph, node->children[0], childbounds, minstrength, infinity, cutoff);
10298
10299 if( *cutoff )
10300 break;
10301
10302 SCIPintervalSub(infinity, &childbounds, node->bounds, node->children[0]->bounds);
10303 SCIPexprgraphTightenNodeBounds(exprgraph, node->children[1], childbounds, minstrength, infinity, cutoff);
10304
10305 break;
10306 }
10307
10308 case SCIP_EXPR_MINUS:
10309 {
10310 assert(node->nchildren == 2);
10311 /* f = c0 - c1 -> c0 = f + c1, c1 = c0 - f */
10312
10313 SCIPintervalAdd(infinity, &childbounds, node->bounds, node->children[1]->bounds);
10314 SCIPexprgraphTightenNodeBounds(exprgraph, node->children[0], childbounds, minstrength, infinity, cutoff);
10315
10316 if( *cutoff )
10317 break;
10318
10319 SCIPintervalSub(infinity, &childbounds, node->children[0]->bounds, node->bounds);
10320 SCIPexprgraphTightenNodeBounds(exprgraph, node->children[1], childbounds, minstrength, infinity, cutoff);
10321
10322 break;
10323 }
10324
10325 case SCIP_EXPR_MUL:
10326 {
10327 assert(node->nchildren == 2);
10328 /* f = c0 * c1 -> c0 = f / c1, c1 = f / c0 */
10329
10330 SCIPintervalDiv(infinity, &childbounds, node->bounds, node->children[1]->bounds);
10331 SCIPexprgraphTightenNodeBounds(exprgraph, node->children[0], childbounds, minstrength, infinity, cutoff);
10332
10333 if( *cutoff )
10334 break;
10335
10336 SCIPintervalDiv(infinity, &childbounds, node->bounds, node->children[0]->bounds);
10337 SCIPexprgraphTightenNodeBounds(exprgraph, node->children[1], childbounds, minstrength, infinity, cutoff);
10338
10339 break;
10340 }
10341
10342 case SCIP_EXPR_DIV:
10343 {
10344 assert(node->nchildren == 2);
10345 /* f = c0 / c1 -> c0 = f * c1, c1 = c0 / f */
10346
10347 SCIPintervalMul(infinity, &childbounds, node->bounds, node->children[1]->bounds);
10348 SCIPexprgraphTightenNodeBounds(exprgraph, node->children[0], childbounds, minstrength, infinity, cutoff);
10349
10350 if( *cutoff )
10351 break;
10352
10353 SCIPintervalDiv(infinity, &childbounds, node->children[0]->bounds, node->bounds);
10354 SCIPexprgraphTightenNodeBounds(exprgraph, node->children[1], childbounds, minstrength, infinity, cutoff);
10355
10356 break;
10357 }
10358
10359 case SCIP_EXPR_SQUARE:
10360 {
10361 assert(node->nchildren == 1);
10362 /* f = c0^2 -> c0 = sqrt(f) union -sqrt(f) */
10363
10364 if( node->bounds.sup < 0.0 )
10365 {
10366 *cutoff = TRUE;
10367 break;
10368 }
10369
10370 SCIPintervalSquareRoot(infinity, &childbounds, node->bounds);
10371 if( node->children[0]->bounds.inf <= -childbounds.inf )
10372 SCIPintervalSetBounds(&childbounds, -childbounds.sup, childbounds.sup);
10373 SCIPexprgraphTightenNodeBounds(exprgraph, node->children[0], childbounds, minstrength, infinity, cutoff);
10374
10375 break;
10376 }
10377
10378 case SCIP_EXPR_SQRT:
10379 {
10380 assert(node->nchildren == 1);
10381 /* f = sqrt(c0) -> c0 = f^2 */
10382
10383 SCIPintervalSquare(infinity, &childbounds, node->bounds);
10384 SCIPexprgraphTightenNodeBounds(exprgraph, node->children[0], childbounds, minstrength, infinity, cutoff);
10385
10386 break;
10387 }
10388
10389 case SCIP_EXPR_REALPOWER:
10390 {
10391 assert(node->nchildren == 1);
10392
10393 SCIPintervalPowerScalarInverse(infinity, &childbounds, node->children[0]->bounds, node->data.dbl, node->bounds);
10394
10395 if( SCIPintervalIsEmpty(infinity, childbounds) )
10396 {
10397 *cutoff = TRUE;
10398 break;
10399 }
10400 SCIPexprgraphTightenNodeBounds(exprgraph, node->children[0], childbounds, minstrength, infinity, cutoff);
10401
10402 break;
10403 }
10404
10405 case SCIP_EXPR_SIGNPOWER:
10406 {
10407 assert(node->nchildren == 1);
10408
10409 if( node->data.dbl != 0.0 )
10410 {
10411 SCIPintervalSignPowerScalar(infinity, &childbounds, node->bounds, 1.0/node->data.dbl);
10412 }
10413 else
10414 {
10415 /* behaves like SCIP_EXPR_SIGN */
10416 SCIPintervalSetBounds(&childbounds,
10417 (node->bounds.inf <= -1.0 && node->bounds.sup >= -1.0) ? -infinity : 0.0,
10418 (node->bounds.inf <= 1.0 && node->bounds.sup >= 1.0) ? infinity : 0.0);
10419 }
10420
10421 SCIPexprgraphTightenNodeBounds(exprgraph, node->children[0], childbounds, minstrength, infinity, cutoff);
10422
10423 break;
10424 }
10425
10426 case SCIP_EXPR_INTPOWER:
10427 {
10428 assert(node->nchildren == 1);
10429
10430 SCIPintervalPowerScalarInverse(infinity, &childbounds, node->children[0]->bounds, (SCIP_Real)node->data.intval, node->bounds);
10431
10432 if( SCIPintervalIsEmpty(infinity, childbounds) )
10433 {
10434 *cutoff = TRUE;
10435 break;
10436 }
10437 SCIPexprgraphTightenNodeBounds(exprgraph, node->children[0], childbounds, minstrength, infinity, cutoff);
10438
10439 break;
10440 }
10441
10442 case SCIP_EXPR_EXP:
10443 {
10444 assert(node->nchildren == 1);
10445 /* f = exp(c0) -> c0 = log(f) */
10446
10447 if( node->bounds.sup < 0.0 )
10448 {
10449 *cutoff = TRUE;
10450 break;
10451 }
10452
10453 SCIPintervalLog(infinity, &childbounds, node->bounds);
10454 SCIPexprgraphTightenNodeBounds(exprgraph, node->children[0], childbounds, minstrength, infinity, cutoff);
10455
10456 break;
10457 }
10458
10459 case SCIP_EXPR_LOG:
10460 {
10461 assert(node->nchildren == 1);
10462 /* f = log(c0) -> c0 = exp(f) */
10463
10464 SCIPintervalExp(infinity, &childbounds, node->bounds);
10465 SCIPexprgraphTightenNodeBounds(exprgraph, node->children[0], childbounds, minstrength, infinity, cutoff);
10466
10467 break;
10468 }
10469
10470 case SCIP_EXPR_SIN:
10471 case SCIP_EXPR_COS:
10472 case SCIP_EXPR_TAN:
10473 /* case SCIP_EXPR_ERF: */
10474 /* case SCIP_EXPR_ERFI: */
10475 {
10476 assert(node->nchildren == 1);
10477
10478 /* @todo implement */
10479
10480 break;
10481 }
10482
10483 case SCIP_EXPR_ABS:
10484 {
10485 assert(node->nchildren == 1);
10486
10487 /* use identity if child bounds are non-negative */
10488 if( node->children[0]->bounds.inf >= 0 )
10489 {
10490 SCIPintervalSetBounds(&childbounds, node->bounds.inf, node->bounds.sup);
10491 }
10492 /* use -identity if child bounds are non-positive */
10493 else if( node->children[0]->bounds.sup <= 0 )
10494 {
10495 assert(node->bounds.inf <= node->bounds.sup);
10496 SCIPintervalSetBounds(&childbounds, -node->bounds.sup, -node->bounds.inf);
10497 }
10498 /* f = |c0| -> c0 = -f union f = [-f.sup, f.sup] */
10499 else
10500 {
10501 SCIPintervalSetBounds(&childbounds, -node->bounds.sup, node->bounds.sup);
10502 }
10503
10504 SCIPexprgraphTightenNodeBounds(exprgraph, node->children[0], childbounds, minstrength, infinity, cutoff);
10505
10506 break;
10507 }
10508
10509 case SCIP_EXPR_SIGN:
10510 {
10511 assert(node->nchildren == 1);
10512 /* f = sign(c0) -> c0 = ([-infty,0] if -1 in f) union ([0,infty] if 1 in f) */
10513
10514 SCIPintervalSetBounds(&childbounds,
10515 (node->bounds.inf <= -1.0 && node->bounds.sup >= -1.0) ? -infinity : 0.0,
10516 (node->bounds.inf <= 1.0 && node->bounds.sup >= 1.0) ? infinity : 0.0);
10517 SCIPexprgraphTightenNodeBounds(exprgraph, node->children[0], childbounds, minstrength, infinity, cutoff);
10518
10519 break;
10520 }
10521
10522 case SCIP_EXPR_MIN:
10523 {
10524 assert(node->nchildren == 2);
10525 /* f = min(c0,c1) -> f <= c0, f <= c1
10526 * if c1 > f -> c0 = f
10527 * if c0 > f -> c1 = f
10528 */
10529
10530 SCIPintervalSetBounds(&childbounds, node->bounds.inf,
10531 node->children[1]->bounds.inf > node->bounds.sup ? node->bounds.sup : infinity);
10532 SCIPexprgraphTightenNodeBounds(exprgraph, node->children[0], childbounds, minstrength, infinity, cutoff);
10533
10534 if( *cutoff )
10535 break;
10536
10537 SCIPintervalSetBounds(&childbounds, node->bounds.inf,
10538 node->children[0]->bounds.inf > node->bounds.sup ? node->bounds.sup : infinity);
10539 SCIPexprgraphTightenNodeBounds(exprgraph, node->children[1], childbounds, minstrength, infinity, cutoff);
10540
10541 break;
10542 }
10543
10544 case SCIP_EXPR_MAX:
10545 {
10546 assert(node->nchildren == 2);
10547 /* f = max(c0, c1) -> f >= c0, f >= c1
10548 * if c1 < f -> c0 = f
10549 * if c0 < f -> c1 = f
10550 */
10551
10552 SCIPintervalSetBounds(&childbounds,
10553 node->children[1]->bounds.sup < node->bounds.inf ? node->bounds.inf : -infinity,
10554 node->bounds.sup);
10555 SCIPexprgraphTightenNodeBounds(exprgraph, node->children[0], childbounds, minstrength, infinity, cutoff);
10556
10557 SCIPintervalSetBounds(&childbounds,
10558 node->children[0]->bounds.sup < node->bounds.inf ? node->bounds.inf : -infinity,
10559 node->bounds.sup);
10560 SCIPexprgraphTightenNodeBounds(exprgraph, node->children[1], childbounds, minstrength, infinity, cutoff);
10561
10562 break;
10563 }
10564
10565 case SCIP_EXPR_SUM:
10566 {
10567 SCIP_ROUNDMODE prevroundmode;
10568
10569 /* f = sum_i c_i -> c_i = f - sum_{j,j!=i} c_j */
10570
10571 SCIP_Real minlinactivity;
10572 SCIP_Real maxlinactivity;
10573 int minlinactivityinf;
10574 int maxlinactivityinf;
10575
10576 if( node->nchildren == 0 )
10577 break;
10578
10579 if( SCIPintervalIsEntire(infinity, node->bounds) )
10580 break;
10581
10582 minlinactivity = 0.0;
10583 maxlinactivity = 0.0;
10584 minlinactivityinf = 0;
10585 maxlinactivityinf = 0;
10586
10587 prevroundmode = SCIPintervalGetRoundingMode();
10588 SCIPintervalSetRoundingModeDownwards();
10589
10590 for( i = 0; i < node->nchildren; ++i )
10591 {
10592 assert(!SCIPintervalIsEmpty(infinity, node->children[i]->bounds));
10593
10594 /* minimal activity is only useful if node has a finite upper bound */
10595 if( node->bounds.sup < infinity )
10596 {
10597 if( node->children[i]->bounds.inf <= -infinity )
10598 {
10599 ++minlinactivityinf;
10600 }
10601 else
10602 {
10603 assert(node->children[i]->bounds.inf < infinity);
10604 minlinactivity += node->children[i]->bounds.inf;
10605 }
10606 }
10607
10608 /* maximal activity is only useful if node has a finite lower bound
10609 * we compute negated maximal activity here so we can keep downward rounding
10610 */
10611 if( node->bounds.inf > -infinity )
10612 {
10613 if( node->children[i]->bounds.sup >= infinity )
10614 {
10615 ++maxlinactivityinf;
10616 }
10617 else
10618 {
10619 assert(node->children[i]->bounds.sup > -infinity);
10620 maxlinactivity -= node->children[i]->bounds.sup;
10621 }
10622 }
10623 }
10624 maxlinactivity = -maxlinactivity; /* correct sign */
10625
10626 /* if there are too many unbounded bounds, then could only compute infinite bounds for children, so give up */
10627 if( (minlinactivityinf >= 2 || node->bounds.sup >= infinity) &&
10628 ( maxlinactivityinf >= 2 || node->bounds.inf <= -infinity)
10629 )
10630 {
10631 SCIPintervalSetRoundingMode(prevroundmode);
10632 break;
10633 }
10634
10635 for( i = 0; i < node->nchildren && !*cutoff; ++i )
10636 {
10637 /* upper bounds of c_i is
10638 * node->bounds.sup - (minlinactivity - c_i.inf), if c_i.inf > -infinity and minlinactivityinf == 0
10639 * node->bounds.sup - minlinactivity, if c_i.inf == -infinity and minlinactivityinf == 1
10640 */
10641 SCIPintervalSetEntire(infinity, &childbounds);
10642 if( node->bounds.sup < infinity )
10643 {
10644 /* we are still in downward rounding mode, so negate and negate to get upward rounding */
10645 if( node->children[i]->bounds.inf <= -infinity && minlinactivityinf <= 1 )
10646 {
10647 assert(minlinactivityinf == 1);
10648 childbounds.sup = SCIPintervalNegateReal(minlinactivity - node->bounds.sup);
10649 }
10650 else if( minlinactivityinf == 0 )
10651 {
10652 childbounds.sup = SCIPintervalNegateReal(minlinactivity - node->bounds.sup - node->children[i]->bounds.inf);
10653 }
10654 }
10655
10656 /* lower bounds of c_i is
10657 * node->bounds.inf - (maxlinactivity - c_i.sup), if c_i.sup < infinity and maxlinactivityinf == 0
10658 * node->bounds.inf - maxlinactivity, if c_i.sup == infinity and maxlinactivityinf == 1
10659 */
10660 if( node->bounds.inf > -infinity )
10661 {
10662 if( node->children[i]->bounds.sup >= infinity && maxlinactivityinf <= 1 )
10663 {
10664 assert(maxlinactivityinf == 1);
10665 childbounds.inf = node->bounds.inf - maxlinactivity;
10666 }
10667 else if( maxlinactivityinf == 0 )
10668 {
10669 childbounds.inf = node->bounds.inf - maxlinactivity + node->children[i]->bounds.sup;
10670 }
10671 }
10672
10673 SCIPexprgraphTightenNodeBounds(exprgraph, node->children[i], childbounds, minstrength, infinity, cutoff);
10674 }
10675
10676 SCIPintervalSetRoundingMode(prevroundmode);
10677
10678 break;
10679 }
10680
10681 case SCIP_EXPR_PRODUCT:
10682 {
10683 int j;
10684 /* f = prod_i c_i -> c_i = f / prod_{j:j!=i} c_j */
10685
10686 /* too expensive (runtime here is quadratic in number of children) */
10687 if( node->nchildren > 10 )
10688 break;
10689
10690 /* useless */
10691 if( SCIPintervalIsEntire(infinity, node->bounds) )
10692 break;
10693
10694 for( i = 0; i < node->nchildren && !*cutoff; ++i )
10695 {
10696 /* compute prod_{j:j!=i} c_j */
10697 SCIPintervalSet(&childbounds, 1.0);
10698 for( j = 0; j < node->nchildren; ++j )
10699 {
10700 if( i == j )
10701 continue;
10702 SCIPintervalMul(infinity, &childbounds, childbounds, node->children[j]->bounds);
10703
10704 /* if there is 0.0 in the product, then later division will hardly give useful bounds, so giveup for this i */
10705 if( childbounds.inf <= 0.0 && childbounds.sup >= 0.0 )
10706 break;
10707 }
10708
10709 if( j == node->nchildren )
10710 {
10711 SCIPintervalDiv(infinity, &childbounds, node->bounds, childbounds); /* f / prod_{j:j!=i} c_j */
10712 SCIPexprgraphTightenNodeBounds(exprgraph, node->children[i], childbounds, minstrength, infinity, cutoff);
10713 }
10714 }
10715
10716 break;
10717 }
10718
10719 case SCIP_EXPR_LINEAR:
10720 {
10721 SCIP_ROUNDMODE prevroundmode;
10722 SCIP_Real* coefs;
10723
10724 /* f = constant + sum_i a_ic_i -> c_i = (f - constant - sum_{j,j!=i} a_jc_j) / c_i */
10725
10726 SCIP_Real minlinactivity;
10727 SCIP_Real maxlinactivity;
10728 int minlinactivityinf;
10729 int maxlinactivityinf;
10730
10731 if( node->nchildren == 0 )
10732 break;
10733
10734 if( SCIPintervalIsEntire(infinity, node->bounds) )
10735 break;
10736
10737 coefs = (SCIP_Real*)node->data.data;
10738
10739 minlinactivity = coefs[node->nchildren];
10740 maxlinactivity = -coefs[node->nchildren];
10741 minlinactivityinf = 0;
10742 maxlinactivityinf = 0;
10743
10744 prevroundmode = SCIPintervalGetRoundingMode();
10745 SCIPintervalSetRoundingModeDownwards();
10746
10747 for( i = 0; i < node->nchildren; ++i )
10748 {
10749 assert(!SCIPintervalIsEmpty(infinity, node->children[i]->bounds));
10750
10751 /* minimal activity is only useful if node has a finite upper bound */
10752 if( node->bounds.sup < infinity )
10753 {
10754 if( coefs[i] >= 0.0 )
10755 {
10756 if( node->children[i]->bounds.inf <= -infinity )
10757 {
10758 ++minlinactivityinf;
10759 }
10760 else
10761 {
10762 assert(node->children[i]->bounds.inf < infinity);
10763 minlinactivity += coefs[i] * node->children[i]->bounds.inf;
10764 }
10765 }
10766 else
10767 {
10768 if( node->children[i]->bounds.sup >= infinity )
10769 {
10770 ++minlinactivityinf;
10771 }
10772 else
10773 {
10774 assert(node->children[i]->bounds.sup > -infinity);
10775 minlinactivity += coefs[i] * node->children[i]->bounds.sup;
10776 }
10777 }
10778 }
10779
10780 /* maximal activity is only useful if node has a finite lower bound
10781 * we compute negated maximal activity here so we can keep downward rounding
10782 */
10783 if( node->bounds.inf > -infinity )
10784 {
10785 if( coefs[i] >= 0.0 )
10786 {
10787 if( node->children[i]->bounds.sup >= infinity )
10788 {
10789 ++maxlinactivityinf;
10790 }
10791 else
10792 {
10793 assert(node->children[i]->bounds.sup > -infinity);
10794 maxlinactivity += SCIPintervalNegateReal(coefs[i]) * node->children[i]->bounds.sup;
10795 }
10796 }
10797 else
10798 {
10799 if( node->children[i]->bounds.inf <= -infinity )
10800 {
10801 ++maxlinactivityinf;
10802 }
10803 else
10804 {
10805 assert(node->children[i]->bounds.inf < infinity);
10806 maxlinactivity += SCIPintervalNegateReal(coefs[i]) * node->children[i]->bounds.inf;
10807 }
10808 }
10809 }
10810 }
10811 maxlinactivity = SCIPintervalNegateReal(maxlinactivity); /* correct sign */
10812
10813 /* SCIPdebugMessage("activity = [%10g,%10g] ninf = [%d,%d]; bounds = [%10g,%10g]\n", minlinactivity, maxlinactivity, minlinactivityinf, maxlinactivityinf, node->bounds.inf, node->bounds.sup); */
10814
10815 /* if there are too many unbounded bounds, then could only compute infinite bounds for children, so give up */
10816 if( (minlinactivityinf >= 2 || node->bounds.sup >= infinity) &&
10817 (maxlinactivityinf >= 2 || node->bounds.inf <= -infinity)
10818 )
10819 {
10820 SCIPintervalSetRoundingMode(prevroundmode);
10821 break;
10822 }
10823
10824 for( i = 0; i < node->nchildren && !*cutoff; ++i )
10825 {
10826 SCIP_INTERVAL ac;
10827
10828 if( coefs[i] == 0.0 )
10829 continue;
10830
10831 /* contribution of child i to activity (coefs[i] * node->children[i]->bounds) */
10832 SCIPintervalSet(&ac, 0.0);
10833 if( coefs[i] >= 0.0 )
10834 {
10835 if( node->children[i]->bounds.inf > -infinity )
10836 ac.inf = coefs[i] * node->children[i]->bounds.inf;
10837 if( node->children[i]->bounds.sup < infinity )
10838 ac.sup = SCIPintervalNegateReal(SCIPintervalNegateReal(coefs[i]) * node->children[i]->bounds.sup);
10839 }
10840 else
10841 {
10842 if( node->children[i]->bounds.sup < infinity )
10843 ac.inf = coefs[i] * node->children[i]->bounds.sup;
10844 if( node->children[i]->bounds.inf > -infinity )
10845 ac.sup = -SCIPintervalNegateReal(coefs[i] * node->children[i]->bounds.inf);
10846 }
10847
10848 SCIPintervalSetEntire(infinity, &childbounds);
10849 if( coefs[i] > 0.0 )
10850 {
10851 /* upper bounds of c_i is
10852 * (node->bounds.sup - minlinactivity)/coefs[i] + c_i.inf, if c_i.inf > -infinity and minlinactivityinf == 0
10853 * (node->bounds.sup - minlinactivity)/coefs[i], if c_i.inf == -infinity and minlinactivityinf == 1
10854 */
10855 if( node->bounds.sup < infinity )
10856 {
10857 /* we are still in downward rounding mode, so negate to get upward rounding */
10858 if( node->children[i]->bounds.inf <= -infinity && minlinactivityinf <= 1 )
10859 {
10860 assert(minlinactivityinf == 1);
10861 childbounds.sup = SCIPintervalNegateReal((minlinactivity - node->bounds.sup)/coefs[i]);
10862 }
10863 else if( minlinactivityinf == 0 )
10864 {
10865 childbounds.sup = SCIPintervalNegateReal((minlinactivity - ac.inf - node->bounds.sup)/coefs[i]);
10866 }
10867 }
10868
10869 /* lower bounds of c_i is
10870 * (node->bounds.inf - maxlinactivity)/coefs[i] + c_i.sup, if c_i.sup < infinity and maxlinactivityinf == 0
10871 * (node->bounds.inf - maxlinactivity)/coefs[i], if c_i.sup == infinity and maxlinactivityinf == 1
10872 */
10873 if( node->bounds.inf > -infinity )
10874 {
10875 if( node->children[i]->bounds.sup >= infinity && maxlinactivityinf <= 1 )
10876 {
10877 assert(maxlinactivityinf == 1);
10878 childbounds.inf = (node->bounds.inf - maxlinactivity)/coefs[i];
10879 }
10880 else if( maxlinactivityinf == 0 )
10881 {
10882 childbounds.inf = (node->bounds.inf - maxlinactivity + ac.sup)/coefs[i];
10883 }
10884 }
10885 }
10886 else
10887 {
10888 /* (a-b)/c in downward rounding may not result in a lower bound on (a-b)/c if c is negative
10889 * thus, we do (b-a)/(-c) in downward rounding
10890 */
10891 /* lower bounds of c_i is
10892 * (node->bounds.sup - minlinactivity)/coefs[i] + c_i.sup, if c_i.sup < infinity and minlinactivityinf == 0
10893 * (node->bounds.sup - minlinactivity)/coefs[i], if c_i.sup == infinity and minlinactivityinf == 1
10894 */
10895 if( node->bounds.sup < infinity )
10896 {
10897 if( node->children[i]->bounds.sup >= infinity && minlinactivityinf <= 1 )
10898 {
10899 assert(minlinactivityinf == 1);
10900 childbounds.inf = (minlinactivity - node->bounds.sup)/SCIPintervalNegateReal(coefs[i]);
10901 }
10902 else if( minlinactivityinf == 0 )
10903 {
10904 childbounds.inf = (minlinactivity - ac.inf - node->bounds.sup)/SCIPintervalNegateReal(coefs[i]);
10905 }
10906 }
10907
10908 /* upper bounds of c_i is
10909 * (node->bounds.inf - maxlinactivity)/coefs[i] + c_i.inf, if c_i.inf > -infinity and maxlinactivityinf == 0
10910 * (node->bounds.inf - maxlinactivity)/coefs[i], if c_i.inf == -infinity and maxlinactivityinf == 1
10911 */
10912 if( node->bounds.inf > -infinity )
10913 {
10914 /* we are still in downward rounding mode, so negate to get upward rounding */
10915 if( node->children[i]->bounds.inf <= -infinity && maxlinactivityinf <= 1 )
10916 {
10917 assert(maxlinactivityinf == 1);
10918 childbounds.sup = SCIPintervalNegateReal((node->bounds.inf - maxlinactivity)/SCIPintervalNegateReal(coefs[i]));
10919 }
10920 else if( maxlinactivityinf == 0 )
10921 {
10922 childbounds.sup = SCIPintervalNegateReal((node->bounds.inf - maxlinactivity + ac.sup)/SCIPintervalNegateReal(coefs[i]));
10923 }
10924 }
10925 }
10926
10927 SCIPexprgraphTightenNodeBounds(exprgraph, node->children[i], childbounds, minstrength, infinity, cutoff);
10928 }
10929
10930 SCIPintervalSetRoundingMode(prevroundmode);
10931
10932 break;
10933 }
10934
10935 case SCIP_EXPR_QUADRATIC:
10936 {
10937 SCIP_EXPRDATA_QUADRATIC* quaddata;
10938 SCIP_INTERVAL tmp;
10939 SCIP_INTERVAL a;
10940 SCIP_INTERVAL b;
10941 SCIP_INTERVAL c;
10942 SCIP_QUADELEM* quadelems;
10943 int nquadelems;
10944 SCIP_Real* lincoefs;
10945 int k;
10946
10947 /* f = constant + sum_i c_i + sum_k a_k c_(i_k) c_(j_k)
10948 * turn into quadratic univariate equation a*c_i^2 + b*c_i = c for each child
10949 */
10950
10951 quaddata = (SCIP_EXPRDATA_QUADRATIC*)node->data.data;
10952 quadelems = quaddata->quadelems;
10953 nquadelems = quaddata->nquadelems;
10954 lincoefs = quaddata->lincoefs;
10955
10956 /* too expensive, runtime here is O(nchildren * nquadelems) = O(nquadelems^2) since nchildren <= 2*nquadelems usually */
10957 if( nquadelems > 10 )
10958 break;
10959
10960 if( SCIPintervalIsEntire(infinity, node->bounds) )
10961 break;
10962
10963 if( node->nchildren == 2 && nquadelems > 0 )
10964 {
10965 /* if it's a bivariate quadratic expression with bilinear term, do something special */
10966 SCIP_Real ax; /* square coefficient of first child */
10967 SCIP_Real ay; /* square coefficient of second child */
10968 SCIP_Real axy; /* bilinear coefficient */
10969
10970 ax = 0.0;
10971 ay = 0.0;
10972 axy = 0.0;
10973 for( i = 0; i < nquadelems; ++i )
10974 if( quadelems[i].idx1 == 0 && quadelems[i].idx2 == 0 )
10975 ax += quadelems[i].coef;
10976 else if( quadelems[i].idx1 == 1 && quadelems[i].idx2 == 1 )
10977 ay += quadelems[i].coef;
10978 else
10979 axy += quadelems[i].coef;
10980
10981 c = node->bounds;
10982 SCIPintervalSubScalar(infinity, &c, c, quaddata->constant);
10983
10984 /* compute bounds for x */
10985 SCIPintervalSolveBivariateQuadExpressionAllScalar(
10986 infinity, &childbounds, ax, ay, axy,
10987 lincoefs != NULL ? lincoefs[0] : 0.0, lincoefs != NULL ? lincoefs[1] : 0.0,
10988 c, node->children[0]->bounds, node->children[1]->bounds
10989 );
10990 if( (childbounds.inf > node->children[0]->bounds.inf + 1e-9 || childbounds.sup + 1e-9 < node->children[0]->bounds.sup) )
10991 {
10992 SCIPdebugMessage("%g x^2 + %g y^2 + %g xy + %g x + %g y in [%g,%g], x = [%g,%g], y = [%g,%g] -> x in [%g,%g], cutoff = %d\n",
10993 ax, ay, axy, lincoefs != NULL ? lincoefs[0] : 0.0, lincoefs != NULL ? lincoefs[1] : 0.0,
10994 c.inf, c.sup, node->children[0]->bounds.inf, node->children[0]->bounds.sup,
10995 node->children[1]->bounds.inf, node->children[1]->bounds.sup, childbounds.inf, childbounds.sup, (int)SCIPintervalIsEmpty(infinity, childbounds)
10996 );
10997 }
10998
10999 if( SCIPintervalIsEmpty(infinity, childbounds) )
11000 *cutoff = TRUE;
11001 else
11002 SCIPexprgraphTightenNodeBounds(exprgraph, node->children[0], childbounds, minstrength, infinity, cutoff);
11003 if( *cutoff )
11004 break;
11005
11006 /* compute bounds for y */
11007 SCIPintervalSolveBivariateQuadExpressionAllScalar(
11008 infinity, &childbounds, ay, ax, axy,
11009 lincoefs != NULL ? lincoefs[1] : 0.0, lincoefs != NULL ? lincoefs[0] : 0.0,
11010 c, node->children[1]->bounds, node->children[0]->bounds
11011 );
11012
11013 if( (childbounds.inf > node->children[1]->bounds.inf + 1e-9 || childbounds.sup + 1e-9 < node->children[1]->bounds.sup) )
11014 {
11015 SCIPdebugMessage("%g x^2 + %g y^2 + %g xy + %g x + %g y in [%g,%g], x = [%g,%g], y = [%g,%g] -> y in [%g,%g], cutoff = %d\n",
11016 ax, ay, axy, lincoefs != NULL ? lincoefs[0] : 0.0, lincoefs != NULL ? lincoefs[1] : 0.0,
11017 c.inf, c.sup, node->children[0]->bounds.inf, node->children[0]->bounds.sup,
11018 node->children[1]->bounds.inf, node->children[1]->bounds.sup, childbounds.inf, childbounds.sup, (int)SCIPintervalIsEmpty(infinity, childbounds)
11019 );
11020 }
11021
11022 if( SCIPintervalIsEmpty(infinity, childbounds) )
11023 *cutoff = TRUE;
11024 else
11025 SCIPexprgraphTightenNodeBounds(exprgraph, node->children[1], childbounds, minstrength, infinity, cutoff);
11026 if( *cutoff )
11027 break;
11028
11029 break;
11030 }
11031
11032 for( i = 0; i < node->nchildren && !*cutoff; ++i )
11033 {
11034 SCIPintervalSet(&a, 0.0);
11035 SCIPintervalSet(&b, lincoefs != NULL ? lincoefs[i] : 0.0);
11036 c = node->bounds;
11037 SCIPintervalSubScalar(infinity, &c, c, quaddata->constant);
11038
11039 /* move linear terms not corresponding to i into c
11040 * @todo do this faster, see EXPR_LINEAR
11041 */
11042 if( lincoefs != NULL )
11043 for( k = 0; k < node->nchildren; ++k )
11044 if( i != k && lincoefs[k] != 0.0 )
11045 {
11046 SCIPintervalMulScalar(infinity, &tmp, node->children[k]->bounds, lincoefs[k]);
11047 SCIPintervalSub(infinity, &c, c, tmp);
11048 }
11049
11050 for( k = 0; k < nquadelems; ++k )
11051 {
11052 if( quadelems[k].idx1 == i && quadelems[k].idx2 == i )
11053 {
11054 SCIPintervalAddScalar(infinity, &a, a, quadelems[k].coef);
11055 }
11056 else if( quadelems[k].idx1 == i )
11057 {
11058 SCIPintervalMulScalar(infinity, &tmp, node->children[quadelems[k].idx2]->bounds, quadelems[k].coef);
11059 SCIPintervalAdd(infinity, &b, b, tmp);
11060 }
11061 else if( quadelems[k].idx2 == i )
11062 {
11063 SCIPintervalMulScalar(infinity, &tmp, node->children[quadelems[k].idx1]->bounds, quadelems[k].coef);
11064 SCIPintervalAdd(infinity, &b, b, tmp);
11065 }
11066 else if( quadelems[k].idx1 == quadelems[k].idx2 )
11067 {
11068 SCIPintervalSquare(infinity, &tmp, node->children[quadelems[k].idx1]->bounds);
11069 SCIPintervalMulScalar(infinity, &tmp, tmp, quadelems[k].coef);
11070 SCIPintervalSub(infinity, &c, c, tmp);
11071 }
11072 else
11073 {
11074 SCIPintervalMul(infinity, &tmp, node->children[quadelems[k].idx1]->bounds, node->children[quadelems[k].idx2]->bounds);
11075 SCIPintervalMulScalar(infinity, &tmp, tmp, quadelems[k].coef);
11076 SCIPintervalSub(infinity, &c, c, tmp);
11077 }
11078 }
11079
11080 SCIPdebugMessage("solve %gc%d^2 + [%10g,%10g]c%d = [%10g,%10g]\n",
11081 a.inf, i, b.inf, b.sup, i, c.inf, c.sup);
11082 SCIPintervalSolveUnivariateQuadExpression(infinity, &childbounds, a, b, c, node->children[i]->bounds);
11083 if( SCIPintervalIsEmpty(infinity, childbounds) )
11084 *cutoff = TRUE;
11085 else
11086 SCIPexprgraphTightenNodeBounds(exprgraph, node->children[i], childbounds, minstrength, infinity, cutoff);
11087 }
11088
11089 break;
11090 }
11091
11092 case SCIP_EXPR_POLYNOMIAL:
11093 {
11094 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata;
11095 SCIP_EXPRDATA_MONOMIAL** monomials;
11096 SCIP_EXPRDATA_MONOMIAL* monomial;
11097 int nmonomials;
11098 int j;
11099 int k;
11100 SCIP_Real n;
11101 int nexpisdoublen;
11102 int nexpishalfn;
11103 char abc_flag;
11104
11105 SCIP_INTERVAL monomialcoef;
11106 SCIP_INTERVAL tmp;
11107 SCIP_INTERVAL a;
11108 SCIP_INTERVAL b;
11109 SCIP_INTERVAL c;
11110 SCIP_INTERVAL childpowbounds;
11111
11112 /* f = constant + sum_i coef_i prod_j c_{i_j}^e_{i_j}
11113 * for each child x, write as a*x^(2n) + b*x^n = c for some n!=0
11114 *
11115 * we determine n by setting n to the first exponent of x that we see
11116 * then we count how often we see x^(2n) and x^(n/2)
11117 * if the number of x^(n/2) exceeds the number of x^(2n), then we half n
11118 */
11119
11120 polynomialdata = (SCIP_EXPRDATA_POLYNOMIAL*)node->data.data;
11121 monomials = polynomialdata->monomials;
11122 nmonomials = polynomialdata->nmonomials;
11123
11124 if( SCIPintervalIsEntire(infinity, node->bounds) )
11125 break;
11126
11127 for( i = 0; i < node->nchildren && !*cutoff; ++i )
11128 {
11129 n = 0.0;
11130 nexpisdoublen = 0;
11131 nexpishalfn = 0;
11132 for( j = 0; j < nmonomials; ++j )
11133 {
11134 monomial = monomials[j];
11135 for( k = 0; k < monomial->nfactors; ++k )
11136 {
11137 if( monomial->childidxs[k] == i )
11138 {
11139 if( n == 0.0 )
11140 n = monomial->exponents[k];
11141 else if( n == 2*monomial->exponents[k] ) /*lint !e777*/
11142 ++nexpishalfn;
11143 else if( 2*n == monomial->exponents[k] ) /*lint !e777*/
11144 ++nexpisdoublen;
11145 }
11146 }
11147 }
11148
11149 if( n == 0.0 )
11150 {
11151 /* child does not appear in polynomial -> cannot deduce bound */
11152 continue;
11153 }
11154
11155 /* half n if there are more monomials with x^(n/2) than monomials with x^(2n) */
11156 if( nexpishalfn > nexpisdoublen )
11157 n /= 2.0;
11158
11159 SCIPintervalSet(&a, 0.0);
11160 SCIPintervalSet(&b, 0.0);
11161 SCIPintervalSubScalar(infinity, &c, node->bounds, polynomialdata->constant);
11162
11163 for( j = 0; j < nmonomials; ++j )
11164 {
11165 monomial = monomials[j];
11166 SCIPintervalSet(&monomialcoef, monomial->coef);
11167 abc_flag = 'c';
11168 for( k = 0; k < monomial->nfactors; ++k )
11169 {
11170 if( monomial->childidxs[k] == i )
11171 {
11172 assert(abc_flag == 'c'); /* child should appear only once per monom */
11173 if( n > 0.0 )
11174 {
11175 if( monomial->exponents[k] > 2.0*n )
11176 {
11177 abc_flag = 'a';
11178 SCIPintervalPowerScalar(infinity, &tmp, node->children[i]->bounds, monomial->exponents[k] - 2.0*n);
11179 SCIPintervalMul(infinity, &monomialcoef, monomialcoef, tmp);
11180 }
11181 else if( monomial->exponents[k] == 2*n ) /*lint !e777*/
11182 {
11183 abc_flag = 'a';
11184 }
11185 else if( monomial->exponents[k] > n )
11186 {
11187 abc_flag = 'b';
11188 SCIPintervalPowerScalar(infinity, &tmp, node->children[i]->bounds, monomial->exponents[k] - n);
11189 SCIPintervalMul(infinity, &monomialcoef, monomialcoef, tmp);
11190 }
11191 else if( monomial->exponents[k] == n ) /*lint !e777*/
11192 {
11193 abc_flag = 'b';
11194 }
11195 else
11196 {
11197 SCIPintervalPowerScalar(infinity, &tmp, node->children[i]->bounds, monomial->exponents[k]);
11198 SCIPintervalMul(infinity, &monomialcoef, monomialcoef, tmp);
11199 }
11200 }
11201 else
11202 {
11203 assert(n < 0.0);
11204 if( monomial->exponents[k] < 2.0*n )
11205 {
11206 abc_flag = 'a';
11207 SCIPintervalPowerScalar(infinity, &tmp, node->children[i]->bounds, monomial->exponents[k] - 2.0*n);
11208 SCIPintervalMul(infinity, &monomialcoef, monomialcoef, tmp);
11209 }
11210 else if( monomial->exponents[k] == 2*n ) /*lint !e777*/
11211 {
11212 abc_flag = 'a';
11213 }
11214 else if( monomial->exponents[k] < n )
11215 {
11216 abc_flag = 'b';
11217 SCIPintervalPowerScalar(infinity, &tmp, node->children[i]->bounds, monomial->exponents[k] - n);
11218 SCIPintervalMul(infinity, &monomialcoef, monomialcoef, tmp);
11219 }
11220 else if( monomial->exponents[k] == n ) /*lint !e777*/
11221 {
11222 abc_flag = 'b';
11223 }
11224 else
11225 {
11226 SCIPintervalPowerScalar(infinity, &tmp, node->children[i]->bounds, monomial->exponents[k]);
11227 SCIPintervalMul(infinity, &monomialcoef, monomialcoef, tmp);
11228 }
11229 }
11230 }
11231 else
11232 {
11233 SCIPintervalPowerScalar(infinity, &tmp, node->children[monomial->childidxs[k]]->bounds, monomial->exponents[k]);
11234 SCIPintervalMul(infinity, &monomialcoef, monomialcoef, tmp);
11235 }
11236 }
11237
11238 if( abc_flag == 'a' )
11239 {
11240 SCIPintervalAdd(infinity, &a, a, monomialcoef);
11241 /* if monomialcoef is such that a exceeds value for infinity, then stop */
11242 if( a.inf >= infinity || a.sup <= -infinity )
11243 break;
11244 }
11245 else if( abc_flag == 'b' )
11246 {
11247 SCIPintervalAdd(infinity, &b, b, monomialcoef);
11248 /* if monomialcoef is such that b exceeds value for infinity, then stop */
11249 if( b.inf >= infinity || b.sup <= -infinity )
11250 break;
11251 }
11252 else
11253 {
11254 SCIPintervalSub(infinity, &c, c, monomialcoef);
11255 /* if monomialcoef is such that c exceeds value for infinity, then stop */
11256 if( c.inf >= infinity || c.sup <= -infinity )
11257 break;
11258 }
11259 }
11260
11261 /* if we run out of numbers (within -infinity,infinity) above, then stop */
11262 if( j < nmonomials )
11263 continue;
11264
11265 /* now have equation a*child^(2n) + b*child^n = c
11266 * solve a*y^2 + b*y = c (for y in childbounds^n), then child^n = y
11267 */
11268 SCIPintervalPowerScalar(infinity, &childpowbounds, node->children[i]->bounds, n);
11269 SCIPdebugMessage("solve [%10g,%10g]c%d^%g + [%10g,%10g]c%d^%g = [%10g,%10g] for c%d^%g in [%10g,%10g]",
11270 a.inf, a.sup, i, 2*n, b.inf, b.sup, i, n, c.inf, c.sup, i, n, childpowbounds.inf, childpowbounds.sup);
11271 SCIPintervalSolveUnivariateQuadExpression(infinity, &tmp, a, b, c, childpowbounds);
11272 SCIPdebugPrintf(" -> c%d^%g = [%10g, %10g]", i, n, tmp.inf, tmp.sup);
11273
11274 if( SCIPintervalIsEmpty(infinity, tmp) )
11275 {
11276 *cutoff = TRUE;
11277 break;
11278 }
11279
11280 SCIPintervalPowerScalarInverse(infinity, &childbounds, node->children[i]->bounds, n, tmp);
11281 SCIPdebugPrintf(" with c%d = [%10g, %10g] -> c%d = [%10g, %10g]\n", i, node->children[i]->bounds.inf, node->children[i]->bounds.sup, i, childbounds.inf, childbounds.sup);
11282 if( SCIPintervalIsEmpty(infinity, childbounds) )
11283 {
11284 SCIPdebugMessage(" -> cutoff\n");
11285 *cutoff = TRUE;
11286 break;
11287 }
11288
11289 SCIPexprgraphTightenNodeBounds(exprgraph, node->children[i], childbounds, minstrength, infinity, cutoff);
11290
11291 /* SCIPdebugMessage("-> node %p (%d,%d): [%10g,%10g] = ", (void*)node, node->depth, node->pos, node->bounds.inf, node->bounds.sup);
11292 SCIPdebug( exprgraphPrintNodeExpression(node, messagehdlr, NULL, NULL, TRUE) );
11293 SCIPdebugPrintf("\n"); */
11294 }
11295
11296 break;
11297 }
11298
11299 case SCIP_EXPR_USER:
11300 {
11301 SCIP_INTERVAL* childrenbounds;
11302 SCIP_EXPRDATA_USER* exprdata;
11303 int c;
11304
11305 exprdata = (SCIP_EXPRDATA_USER*)node->data.data;
11306
11307 /* do nothing if callback not implemented */
11308 if( exprdata->prop == NULL )
11309 break;
11310
11311 /* if only one child, do faster */
11312 if( node->nchildren == 1 )
11313 {
11314 childbounds = node->children[0]->bounds;
11315 SCIP_CALL_ABORT( exprdata->prop(infinity, exprdata->userdata, 1, &childbounds, node->bounds, cutoff) );
11316
11317 if( !*cutoff )
11318 SCIPexprgraphTightenNodeBounds(exprgraph, node->children[0], childbounds, minstrength, infinity, cutoff);
11319
11320 break;
11321 }
11322
11323 SCIP_ALLOC_ABORT( BMSallocBlockMemoryArray(exprgraph->blkmem, &childrenbounds, node->nchildren) );
11324 for( c = 0; c < node->nchildren; ++c )
11325 childrenbounds[c] = node->children[c]->bounds;
11326
11327 SCIP_CALL_ABORT( exprdata->prop(infinity, exprdata->userdata, node->nchildren, childrenbounds, node->bounds, cutoff) );
11328
11329 for( c = 0; !*cutoff && c < node->nchildren; ++c )
11330 {
11331 SCIPexprgraphTightenNodeBounds(exprgraph, node->children[c], childrenbounds[c], minstrength, infinity, cutoff);
11332 }
11333
11334 BMSfreeBlockMemoryArray(exprgraph->blkmem, &childrenbounds, node->nchildren);
11335
11336 break;
11337 }
11338
11339 case SCIP_EXPR_LAST:
11340 SCIPABORT();
11341 break;
11342 }
11343 }
11344
11345 /** removes duplicate children in a polynomial expression node
11346 *
11347 * Leaves NULL's in children array.
11348 */
11349 static
exprgraphNodeRemovePolynomialDuplicateChildren(SCIP_EXPRGRAPH * exprgraph,SCIP_EXPRGRAPHNODE * node)11350 SCIP_RETCODE exprgraphNodeRemovePolynomialDuplicateChildren(
11351 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
11352 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
11353 )
11354 {
11355 SCIP_Bool foundduplicates;
11356 int* childmap;
11357 int i;
11358 int j;
11359
11360 assert(exprgraph != NULL);
11361 assert(node != NULL);
11362 assert(node->op == SCIP_EXPR_POLYNOMIAL);
11363
11364 if( node->nchildren == 0 )
11365 return SCIP_OKAY;
11366
11367 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &childmap, node->nchildren) );
11368
11369 foundduplicates = FALSE;
11370 for( i = 0; i < node->nchildren; ++i )
11371 {
11372 if( node->children[i] == NULL )
11373 continue;
11374 childmap[i] = i; /*lint !e644*/
11375
11376 for( j = i+1; j < node->nchildren; ++j )
11377 {
11378 if( node->children[j] == NULL )
11379 continue;
11380
11381 if( node->children[i] == node->children[j] )
11382 {
11383 /* node should be parent of children[j] at least twice,
11384 * so we remove it once
11385 */
11386 SCIP_CALL( exprgraphNodeRemoveParent(exprgraph, &node->children[j], node) );
11387 node->children[j] = NULL;
11388 assert(exprgraphNodeIsParent(node->children[i], node));
11389
11390 childmap[j] = i;
11391 foundduplicates = TRUE;
11392 }
11393 }
11394 }
11395
11396 /* apply childmap to monomials */
11397 if( foundduplicates )
11398 polynomialdataApplyChildmap((SCIP_EXPRDATA_POLYNOMIAL*)node->data.data, childmap);
11399
11400 /* free childmap */
11401 BMSfreeBlockMemoryArray(exprgraph->blkmem, &childmap, node->nchildren);
11402
11403 return SCIP_OKAY;
11404 }
11405
11406 /** eliminates NULL's in children array and shrinks it to actual size */
11407 static
exprgraphNodeRemovePolynomialNullChildren(BMS_BLKMEM * blkmem,SCIP_EXPRGRAPHNODE * node)11408 SCIP_RETCODE exprgraphNodeRemovePolynomialNullChildren(
11409 BMS_BLKMEM* blkmem, /**< block memory */
11410 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
11411 )
11412 {
11413 int* childmap;
11414 int lastnonnull;
11415 int i;
11416
11417 assert(blkmem != NULL);
11418 assert(node != NULL);
11419 assert(node->op == SCIP_EXPR_POLYNOMIAL);
11420
11421 if( node->nchildren == 0 )
11422 return SCIP_OKAY;
11423
11424 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &childmap, node->nchildren) );
11425
11426 /* close gaps in children array */
11427 lastnonnull = node->nchildren-1;
11428 while( lastnonnull >= 0 && node->children[lastnonnull] == NULL )
11429 --lastnonnull;
11430 for( i = 0; i <= lastnonnull; ++i )
11431 {
11432 if( node->children[i] != NULL )
11433 {
11434 childmap[i] = i; /* child at index i is not moved */ /*lint !e644*/
11435 continue;
11436 }
11437 assert(node->children[lastnonnull] != NULL);
11438
11439 /* move child at lastnonnull to position i */
11440 node->children[i] = node->children[lastnonnull];
11441 node->children[lastnonnull] = NULL;
11442 childmap[lastnonnull] = i;
11443
11444 /* update lastnonnull */
11445 --lastnonnull;
11446 while( lastnonnull >= 0 && node->children[lastnonnull] == NULL )
11447 --lastnonnull;
11448 }
11449 assert(i > lastnonnull);
11450
11451 /* apply childmap to monomials */
11452 if( lastnonnull < node->nchildren-1 )
11453 polynomialdataApplyChildmap((SCIP_EXPRDATA_POLYNOMIAL*)node->data.data, childmap);
11454
11455 BMSfreeBlockMemoryArray(blkmem, &childmap, node->nchildren);
11456
11457 /* shrink children array */
11458 if( lastnonnull >= 0 )
11459 {
11460 SCIP_ALLOC( BMSreallocBlockMemoryArray(blkmem, &node->children, node->nchildren, lastnonnull+1) );
11461 node->nchildren = lastnonnull+1;
11462 }
11463 else
11464 {
11465 BMSfreeBlockMemoryArray(blkmem, &node->children, node->nchildren);
11466 node->nchildren = 0;
11467 }
11468
11469 return SCIP_OKAY;
11470 }
11471
11472 /** aims at simplifying a node in an expression graph, assuming all children have been simplified
11473 *
11474 * Converts node into polynomial, if possible and not constant.
11475 */
11476 static
exprgraphNodeSimplify(SCIP_EXPRGRAPH * exprgraph,SCIP_EXPRGRAPHNODE * node,SCIP_MESSAGEHDLR * messagehdlr,SCIP_Real eps,int maxexpansionexponent,SCIP_Bool * havechange)11477 SCIP_RETCODE exprgraphNodeSimplify(
11478 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
11479 SCIP_EXPRGRAPHNODE* node, /**< expression graph node */
11480 SCIP_MESSAGEHDLR* messagehdlr, /**< message handler */
11481 SCIP_Real eps, /**< threshold, under which positive values are treat as 0 */
11482 int maxexpansionexponent,/**< maximal exponent for which we still expand non-monomial polynomials */
11483 SCIP_Bool* havechange /**< flag to set if the node has been changed */
11484 )
11485 {
11486 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata;
11487 SCIP_EXPRDATA_MONOMIAL* monomial;
11488 BMS_BLKMEM* blkmem;
11489 SCIP_Bool removechild;
11490 SCIP_Bool* childinuse;
11491 int* childmap;
11492 int childmapsize;
11493 int i;
11494 int j;
11495 int orignchildren;
11496
11497 assert(exprgraph != NULL);
11498 assert(node != NULL);
11499 assert(node->depth > 0); /* simplifier is not thought for nodes at depth 0 */
11500 assert(havechange != NULL);
11501
11502 blkmem = exprgraph->blkmem;
11503 assert(blkmem != NULL);
11504
11505 SCIPdebugMessage("attempt simplification of node %p (%d,%d)\n", (void*)node, node->depth, node->pos);
11506
11507 /* if all children are constants, then turn this node into constant */
11508 for( i = 0; i < node->nchildren; ++i )
11509 if( node->children[i]->op != SCIP_EXPR_CONST )
11510 break;
11511 if( node->nchildren > 0 && i == node->nchildren )
11512 {
11513 /* get value of node */
11514 SCIP_CALL( exprgraphNodeEvalWithChildren(node, NULL) );
11515 assert(node->value != SCIP_INVALID); /*lint !e777*/
11516
11517 SCIPdebugMessage("turn node %p (%d,%d) into constant %g\n", (void*)node, node->depth, node->pos, node->value);
11518 SCIPdebug( exprgraphPrintNodeExpression(node, messagehdlr, NULL, NULL, TRUE) );
11519 SCIPdebugPrintf("\n");
11520
11521 /* free expression data */
11522 if( exprOpTable[node->op].freedata != NULL )
11523 exprOpTable[node->op].freedata(blkmem, node->nchildren, node->data);
11524
11525 /* disconnect from children */
11526 for( i = 0; i < node->nchildren; ++i )
11527 {
11528 SCIP_CALL( exprgraphNodeRemoveParent(exprgraph, &node->children[i], node) );
11529 }
11530 BMSfreeBlockMemoryArray(blkmem, &node->children, node->nchildren);
11531 node->nchildren = 0;
11532
11533 /* turn into constant expression */
11534 node->op = SCIP_EXPR_CONST;
11535 node->data.dbl = node->value;
11536
11537 *havechange = TRUE;
11538 node->simplified = TRUE;
11539
11540 return SCIP_OKAY;
11541 }
11542
11543 /* @todo for sign, min, max, abs, knowing bounds on children may allow simplification
11544 * @todo log(product) -> sum(log)
11545 * @todo product(exp) -> exp(sum)
11546 * @todo exp(x)^p -> exp(p*x)
11547 * @todo exp(const*log(x)) -> x^const
11548 */
11549
11550 SCIP_CALL( exprConvertToPolynomial(blkmem, &node->op, &node->data, node->nchildren) );
11551
11552 if( node->op != SCIP_EXPR_POLYNOMIAL )
11553 {
11554 node->simplified = TRUE;
11555 return SCIP_OKAY;
11556 }
11557
11558 polynomialdata = (SCIP_EXPRDATA_POLYNOMIAL*)node->data.data;
11559 assert(polynomialdata != NULL);
11560
11561 orignchildren = node->nchildren;
11562
11563 /* check if we have duplicate children and merge */
11564 SCIP_CALL( exprgraphNodeRemovePolynomialDuplicateChildren(exprgraph, node) );
11565 polynomialdataMergeMonomials(blkmem, polynomialdata, eps, TRUE);
11566
11567 SCIPdebugMessage("expand factors in expression node ");
11568 SCIPdebug( exprgraphPrintNodeExpression(node, messagehdlr, NULL, NULL, FALSE) );
11569 SCIPdebugPrintf("\n");
11570
11571 childmap = NULL;
11572 childmapsize = 0;
11573
11574 /* resolve children that are constants
11575 * we do this first, because it reduces the degree and number of factors in the monomials,
11576 * thereby allowing some expansions of polynomials that may not be possible otherwise, e.g., turning c0*c1 with c0=quadratic and c1=constant into a single monomial
11577 */
11578 for( i = 0; i < node->nchildren; ++i )
11579 {
11580 if( node->children[i] == NULL )
11581 continue;
11582
11583 /* convert children to polynomial, if not constant or polynomial
11584 * if child was simplified in this round, it may have already been converted, and then nothing happens
11585 * but if child was already simplified, then it was not converted, and thus we try it here
11586 */
11587 if( node->children[i]->op != SCIP_EXPR_CONST )
11588 continue;
11589
11590 SCIPdebugMessage("expand child %d in expression node ", i);
11591 SCIPdebug( exprgraphPrintNodeExpression(node, messagehdlr, NULL, NULL, FALSE) );
11592 SCIPdebugPrintf("\n\tchild = ");
11593 SCIPdebug( exprgraphPrintNodeExpression(node->children[i], messagehdlr, NULL, NULL, FALSE) );
11594 SCIPdebugPrintf("\n");
11595
11596 removechild = TRUE; /* we intend to release children[i] */
11597
11598 ensureBlockMemoryArraySize(blkmem, &childmap, &childmapsize, node->children[i]->nchildren);
11599
11600 /* put constant of child i into every monomial where child i is used */
11601 for( j = 0; j < polynomialdata->nmonomials; ++j )
11602 {
11603 int factorpos;
11604
11605 monomial = polynomialdata->monomials[j];
11606 /* if monomial is not sorted, then polynomial should not be sorted either, or have only one monomial */
11607 assert(monomial->sorted || !polynomialdata->sorted || polynomialdata->nmonomials <= 1);
11608
11609 if( SCIPexprFindMonomialFactor(monomial, i, &factorpos) )
11610 {
11611 assert(factorpos >= 0);
11612 assert(factorpos < monomial->nfactors);
11613 /* assert that factors have been merged */
11614 assert(factorpos == 0 || monomial->childidxs[factorpos-1] != i);
11615 assert(factorpos == monomial->nfactors-1 || monomial->childidxs[factorpos+1] != i);
11616
11617 SCIPdebugMessage("attempt expanding child %d at monomial %d factor %d\n", i, j, factorpos);
11618
11619 if( !EPSISINT(monomial->exponents[factorpos], 0.0) && node->children[i]->data.dbl < 0.0 ) /*lint !e835*/
11620 {
11621 /* if constant is negative and our exponent is not integer, then cannot do expansion */
11622 SCIPmessagePrintWarning(messagehdlr, "got negative constant %g to the power of a noninteger exponent %g\n", node->children[i]->data.dbl, monomial->exponents[factorpos]);
11623 removechild = FALSE;
11624 }
11625 else
11626 {
11627 monomial->coef *= pow(node->children[i]->data.dbl, monomial->exponents[factorpos]);
11628
11629 /* move last factor to position factorpos */
11630 if( factorpos < monomial->nfactors-1 )
11631 {
11632 monomial->exponents[factorpos] = monomial->exponents[monomial->nfactors-1];
11633 monomial->childidxs[factorpos] = monomial->childidxs[monomial->nfactors-1];
11634 }
11635 --monomial->nfactors;
11636 monomial->sorted = FALSE;
11637 polynomialdata->sorted = FALSE;
11638
11639 *havechange = TRUE;
11640 }
11641 }
11642 }
11643
11644 /* forget about child i, if it is not used anymore */
11645 if( removechild )
11646 {
11647 /* remove node from list of parents of child i */
11648 SCIP_CALL( exprgraphNodeRemoveParent(exprgraph, &node->children[i], node) );
11649 node->children[i] = NULL;
11650 }
11651
11652 /* simplify current polynomial again */
11653 polynomialdataMergeMonomials(blkmem, polynomialdata, eps, TRUE);
11654 }
11655
11656 /* resolve children that are polynomials itself */
11657 for( i = 0; i < node->nchildren; ++i )
11658 {
11659 if( node->children[i] == NULL )
11660 continue;
11661
11662 /* convert children to polynomial, if not constant or polynomial
11663 * if child was simplified in this round, it may have already been converted, and then nothing happens
11664 * but if child was already simplified, then it was not converted, and thus we try it here
11665 */
11666 SCIP_CALL( exprConvertToPolynomial(blkmem, &node->children[i]->op, &node->children[i]->data, node->children[i]->nchildren) );
11667
11668 if( node->children[i]->op != SCIP_EXPR_POLYNOMIAL )
11669 continue;
11670
11671 SCIPdebugMessage("expand child %d in expression node %p = ", i, (void*)node);
11672 SCIPdebug( exprgraphPrintNodeExpression(node, messagehdlr, NULL, NULL, FALSE) );
11673 SCIPdebug( SCIPmessagePrintInfo(messagehdlr, "\n\tchild = ") );
11674 SCIPdebug( exprgraphPrintNodeExpression(node->children[i], messagehdlr, NULL, NULL, FALSE) );
11675 SCIPdebug( SCIPmessagePrintInfo(messagehdlr, "\n") );
11676
11677 removechild = TRUE; /* we intend to release children[i] */
11678
11679 ensureBlockMemoryArraySize(blkmem, &childmap, &childmapsize, node->children[i]->nchildren);
11680
11681 /* add children of child i to node */
11682 SCIP_CALL( exprgraphNodeAddChildren(blkmem, node, node->children[i]->nchildren, node->children[i]->children, childmap) );
11683
11684 /* put polynomial of child i into every monomial where child i is used */
11685 j = 0;
11686 while( j < polynomialdata->nmonomials )
11687 {
11688 int factorpos;
11689 SCIP_Bool success;
11690
11691 monomial = polynomialdata->monomials[j];
11692 /* if monomial is not sorted, then polynomial should not be sorted either, or have only one monomial */
11693 assert(monomial->sorted || !polynomialdata->sorted || polynomialdata->nmonomials <= 1);
11694
11695 /* make sure factors are merged, should only be potentially necessary if not sorted, see also #1848 */
11696 if( !monomial->sorted )
11697 SCIPexprMergeMonomialFactors(monomial, eps);
11698
11699 if( !SCIPexprFindMonomialFactor(monomial, i, &factorpos) )
11700 {
11701 ++j;
11702 continue;
11703 }
11704
11705 assert(factorpos >= 0);
11706 assert(factorpos < monomial->nfactors);
11707 /* assert that factors have been merged */
11708 assert(factorpos == 0 || monomial->childidxs[factorpos-1] != i);
11709 assert(factorpos == monomial->nfactors-1 || monomial->childidxs[factorpos+1] != i);
11710
11711 SCIPdebugMessage("attempt expanding child %d at monomial %d factor %d\n", i, j, factorpos);
11712
11713 SCIP_CALL( polynomialdataExpandMonomialFactor(blkmem, messagehdlr, polynomialdata, j, factorpos,
11714 (SCIP_EXPRDATA_POLYNOMIAL*)node->children[i]->data.data, childmap, maxexpansionexponent, &success) );
11715
11716 if( !success )
11717 {
11718 removechild = FALSE;
11719 ++j;
11720 }
11721 else
11722 *havechange = TRUE;
11723
11724 /* expansion may remove monomials[j], move a monomial from the end to position j, or add new monomials to the end of polynomialdata
11725 * we thus repeat with index j, if a factor was successfully expanded
11726 */
11727 }
11728
11729 /* forget about child i, if it is not used anymore */
11730 if( removechild )
11731 {
11732 /* remove node from list of parents of child i */
11733 SCIP_CALL( exprgraphNodeRemoveParent(exprgraph, &node->children[i], node) );
11734 node->children[i] = NULL;
11735 }
11736 }
11737
11738 /* simplify current polynomial again */
11739 polynomialdataMergeMonomials(blkmem, polynomialdata, eps, TRUE);
11740
11741 BMSfreeBlockMemoryArrayNull(blkmem, &childmap, childmapsize);
11742
11743 /* check which children are still in use */
11744 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &childinuse, node->nchildren) );
11745 BMSclearMemoryArray(childinuse, node->nchildren); /*lint !e644*/
11746 for( i = 0; i < polynomialdata->nmonomials; ++i )
11747 {
11748 monomial = polynomialdata->monomials[i];
11749 assert(monomial != NULL);
11750
11751 for( j = 0; j < monomial->nfactors; ++j )
11752 {
11753 assert(monomial->childidxs[j] >= 0);
11754 assert(monomial->childidxs[j] < node->nchildren);
11755 childinuse[monomial->childidxs[j]] = TRUE;
11756 }
11757 }
11758
11759 /* free children that are not used in any monomial */
11760 for( i = 0; i < node->nchildren; ++i )
11761 if( node->children[i] != NULL && !childinuse[i] )
11762 {
11763 SCIP_CALL( exprgraphNodeRemoveParent(exprgraph, &node->children[i], node) );
11764 node->children[i] = NULL;
11765 }
11766
11767 BMSfreeBlockMemoryArray(blkmem, &childinuse, node->nchildren);
11768
11769 /* remove NULLs from children array */
11770 SCIP_CALL( exprgraphNodeRemovePolynomialNullChildren(blkmem, node) );
11771
11772 /* if no children, then it's a constant polynomial -> change into EXPR_CONST */
11773 if( node->nchildren == 0 )
11774 {
11775 SCIP_Real val;
11776
11777 /* if no children, then it should also have no monomials */
11778 assert(polynomialdata->nmonomials == 0);
11779
11780 val = polynomialdata->constant;
11781 polynomialdataFree(blkmem, &polynomialdata);
11782
11783 node->op = SCIP_EXPR_CONST;
11784 node->data.dbl = val;
11785 node->value = val;
11786 }
11787
11788 /* if no factor in a monomial was replaced, the number of children should not have changed
11789 * but if we found duplicates in the children array, then it should be reduced, and we want to count this as a change too
11790 */
11791 *havechange |= (node->nchildren < orignchildren); /*lint !e514*/
11792
11793 node->simplified = TRUE;
11794
11795 SCIPdebugMessage("-> %p = ", (void*)node);
11796 SCIPdebug( exprgraphPrintNodeExpression(node, messagehdlr, NULL, NULL, FALSE) );
11797 SCIPdebugPrintf("\n");
11798
11799 return SCIP_OKAY;
11800 }
11801
11802 /** creates an expression from a given node in an expression graph
11803 *
11804 * Assembles mapping of variables from graph to tree.
11805 */
11806 static
exprgraphNodeCreateExpr(SCIP_EXPRGRAPH * exprgraph,SCIP_EXPRGRAPHNODE * node,SCIP_EXPR ** expr,int * nexprvars,int * varidx)11807 SCIP_RETCODE exprgraphNodeCreateExpr(
11808 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
11809 SCIP_EXPRGRAPHNODE* node, /**< expression graph node which expression should be created */
11810 SCIP_EXPR** expr, /**< buffer to store pointer to created expression */
11811 int* nexprvars, /**< current number of variables in expression */
11812 int* varidx /**< current mapping of variable indices from graph to expression */
11813 )
11814 {
11815 SCIP_EXPR** childexprs;
11816 int i;
11817
11818 assert(exprgraph != NULL);
11819 assert(node != NULL);
11820 assert(expr != NULL);
11821 assert(nexprvars != NULL);
11822 assert(*nexprvars >= 0);
11823 assert(varidx != NULL);
11824
11825 childexprs = NULL;
11826 if( node->nchildren > 0 )
11827 {
11828 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &childexprs, node->nchildren) );
11829 for( i = 0; i < node->nchildren; ++i )
11830 {
11831 SCIP_CALL( exprgraphNodeCreateExpr(exprgraph, node->children[i], &childexprs[i], nexprvars, varidx) ); /*lint !e613*/
11832 }
11833 }
11834
11835 switch( node->op )
11836 {
11837 case SCIP_EXPR_VARIDX:
11838 {
11839 /* check if the variable already has an index assigned in the expression tree
11840 * if not, create one and increase nexprvars
11841 */
11842 assert(node->data.intval >= 0);
11843 assert(node->data.intval < exprgraph->nvars);
11844 assert(varidx[node->data.intval] >= -1);
11845 assert(varidx[node->data.intval] < *nexprvars);
11846 if( varidx[node->data.intval] == -1 )
11847 {
11848 varidx[node->data.intval] = *nexprvars;
11849 ++*nexprvars;
11850 }
11851
11852 SCIP_CALL( SCIPexprCreate(exprgraph->blkmem, expr, SCIP_EXPR_VARIDX, varidx[node->data.intval]) );
11853 break;
11854 }
11855
11856 case SCIP_EXPR_CONST:
11857 {
11858 SCIP_CALL( SCIPexprCreate(exprgraph->blkmem, expr, node->op, node->data.dbl) );
11859 break;
11860 }
11861
11862 case SCIP_EXPR_REALPOWER:
11863 case SCIP_EXPR_SIGNPOWER:
11864 {
11865 assert(node->nchildren == 1);
11866 assert(childexprs != NULL);
11867 /* coverity[var_deref_op] */
11868 SCIP_CALL( SCIPexprCreate(exprgraph->blkmem, expr, node->op, childexprs[0], node->data.dbl) ); /*lint !e613*/
11869 break;
11870 }
11871
11872 case SCIP_EXPR_INTPOWER:
11873 {
11874 assert(node->nchildren == 1);
11875 assert(childexprs != NULL);
11876 /* coverity[var_deref_op] */
11877 SCIP_CALL( SCIPexprCreate(exprgraph->blkmem, expr, node->op, childexprs[0], node->data.intval) ); /*lint !e613*/
11878 break;
11879 }
11880
11881 case SCIP_EXPR_PLUS:
11882 case SCIP_EXPR_MINUS:
11883 case SCIP_EXPR_MUL:
11884 case SCIP_EXPR_DIV:
11885 case SCIP_EXPR_MIN:
11886 case SCIP_EXPR_MAX:
11887 {
11888 assert(node->nchildren == 2);
11889 assert(childexprs != NULL);
11890 /* coverity[var_deref_op] */
11891 SCIP_CALL( SCIPexprCreate(exprgraph->blkmem, expr, node->op, childexprs[0], childexprs[1]) ); /*lint !e613*/
11892 break;
11893 }
11894
11895 case SCIP_EXPR_SQUARE:
11896 case SCIP_EXPR_SQRT:
11897 case SCIP_EXPR_EXP:
11898 case SCIP_EXPR_LOG:
11899 case SCIP_EXPR_SIN:
11900 case SCIP_EXPR_COS:
11901 case SCIP_EXPR_TAN:
11902 /* case SCIP_EXPR_ERF: */
11903 /* case SCIP_EXPR_ERFI: */
11904 case SCIP_EXPR_ABS:
11905 case SCIP_EXPR_SIGN:
11906 {
11907 assert(node->nchildren == 1);
11908 assert(childexprs != NULL);
11909 /* coverity[var_deref_op] */
11910 SCIP_CALL( SCIPexprCreate(exprgraph->blkmem, expr, node->op, childexprs[0]) ); /*lint !e613*/
11911 break;
11912 }
11913
11914 case SCIP_EXPR_SUM:
11915 case SCIP_EXPR_PRODUCT:
11916 {
11917 SCIP_CALL( SCIPexprCreate(exprgraph->blkmem, expr, node->op, node->nchildren, childexprs) );
11918 break;
11919 }
11920
11921 case SCIP_EXPR_LINEAR:
11922 {
11923 assert(node->data.data != NULL);
11924
11925 SCIP_CALL( SCIPexprCreateLinear(exprgraph->blkmem, expr, node->nchildren, childexprs, (SCIP_Real*)node->data.data, ((SCIP_Real*)node->data.data)[node->nchildren]) );
11926 break;
11927 }
11928
11929 case SCIP_EXPR_QUADRATIC:
11930 {
11931 SCIP_EXPRDATA_QUADRATIC* quaddata;
11932
11933 quaddata = (SCIP_EXPRDATA_QUADRATIC*)node->data.data;
11934 assert(quaddata != NULL);
11935
11936 SCIP_CALL( SCIPexprCreateQuadratic(exprgraph->blkmem, expr, node->nchildren, childexprs,
11937 quaddata->constant, quaddata->lincoefs, quaddata->nquadelems, quaddata->quadelems) );
11938 break;
11939 }
11940
11941 case SCIP_EXPR_POLYNOMIAL:
11942 {
11943 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata;
11944
11945 polynomialdata = (SCIP_EXPRDATA_POLYNOMIAL*)node->data.data;
11946 assert(polynomialdata != NULL);
11947
11948 SCIP_CALL( SCIPexprCreatePolynomial(exprgraph->blkmem, expr, node->nchildren, childexprs,
11949 polynomialdata->nmonomials, polynomialdata->monomials, polynomialdata->constant, TRUE) );
11950
11951 break;
11952 }
11953
11954 case SCIP_EXPR_USER:
11955 {
11956 SCIP_EXPRDATA_USER* exprdata;
11957 SCIP_USEREXPRDATA* userdata;
11958
11959 exprdata = (SCIP_EXPRDATA_USER*)node->data.data;
11960 assert(exprdata != NULL);
11961
11962 if( exprdata->copydata != NULL )
11963 {
11964 SCIP_CALL( exprdata->copydata(exprgraph->blkmem, node->nchildren, exprdata->userdata, &userdata) );
11965 }
11966 else
11967 userdata = exprdata->userdata;
11968
11969 /* coverity[var_deref_op] */
11970 /* coverity[var_deref_model] */
11971 SCIP_CALL( SCIPexprCreateUser(exprgraph->blkmem, expr, node->nchildren, childexprs,
11972 userdata, exprdata->evalcapability, exprdata->eval, exprdata->inteval, exprdata->curv, exprdata->prop, exprdata->estimate, exprdata->copydata, exprdata->freedata, exprdata->print) );
11973
11974 break;
11975 }
11976
11977 case SCIP_EXPR_LAST:
11978 case SCIP_EXPR_PARAM:
11979 {
11980 SCIPerrorMessage("expression operand %d not supported here\n", node->op);
11981 return SCIP_ERROR;
11982 }
11983 }
11984
11985 BMSfreeBlockMemoryArrayNull(exprgraph->blkmem, &childexprs, node->nchildren);
11986
11987 return SCIP_OKAY;
11988 }
11989
11990 /** counts how often expression graph variables are used in a subtree of the expression graph
11991 *
11992 * @note The function does not clear the array first, but only increases already existing counts.
11993 */
11994 static
exprgraphNodeGetVarsUsage(SCIP_EXPRGRAPHNODE * node,int * varsusage)11995 void exprgraphNodeGetVarsUsage(
11996 SCIP_EXPRGRAPHNODE* node, /**< root node of expression graph subtree */
11997 int* varsusage /**< array where to count usage of variables, length must be at least the number of variables in the graph */
11998 )
11999 {
12000 int i;
12001
12002 assert(node != NULL);
12003 assert(varsusage != NULL);
12004
12005 if( node->op == SCIP_EXPR_VARIDX )
12006 {
12007 ++varsusage[node->data.intval];
12008 return;
12009 }
12010
12011 for( i = 0; i < node->nchildren; ++i )
12012 exprgraphNodeGetVarsUsage(node->children[i], varsusage);
12013 }
12014
12015 /** checks whether a node can be put into a component when checking block separability of an expression
12016 *
12017 * If a variable used by node is already in another component, components are merged and component number is updated.
12018 */
12019 static
exprgraphNodeCheckSeparabilityComponent(SCIP_EXPRGRAPHNODE * node,int * compnr,int nchildcomps,int * childcomps,int nvars,int * varcomps)12020 void exprgraphNodeCheckSeparabilityComponent(
12021 SCIP_EXPRGRAPHNODE* node, /**< node to which we assign a component */
12022 int* compnr, /**< component number to assign, may be reduced if variables overlap */
12023 int nchildcomps, /**< number of entries for which childcomps have been set already */
12024 int* childcomps, /**< component numbers of children */
12025 int nvars, /**< number of variables */
12026 int* varcomps /**< component numbers of variables */
12027 )
12028 {
12029 int varidx;
12030 int i;
12031
12032 assert(node != NULL);
12033 assert(compnr != NULL);
12034 assert(*compnr >= 0);
12035 assert(childcomps != NULL);
12036 assert(varcomps != NULL);
12037
12038 if( node->op != SCIP_EXPR_VARIDX )
12039 {
12040 for( i = 0; i < node->nchildren; ++i )
12041 exprgraphNodeCheckSeparabilityComponent(node->children[i], compnr, nchildcomps, childcomps, nvars, varcomps);
12042 return;
12043 }
12044
12045 varidx = node->data.intval;
12046 assert(varidx >= 0);
12047 assert(varidx < nvars);
12048
12049 if( varcomps[varidx] == -1 )
12050 {
12051 /* first time we get to this variable, so set it's component to compnr and we are done */
12052 varcomps[varidx] = *compnr;
12053 return;
12054 }
12055
12056 if( varcomps[varidx] == *compnr )
12057 {
12058 /* variable is already in current component, that's also good and we are done */
12059 return;
12060 }
12061
12062 /* variable is already in another component, so have to merge component compnr into that component
12063 * do this by updating varcomps and childcomps */
12064 for( i = 0; i < nvars; ++i )
12065 if( varcomps[i] == *compnr )
12066 varcomps[i] = varcomps[varidx];
12067 for( i = 0; i < nchildcomps; ++i )
12068 if( childcomps[i] == *compnr )
12069 /* coverity[copy_paste_error] */
12070 childcomps[i] = varcomps[varidx];
12071 *compnr = varcomps[varidx];
12072 }
12073
12074 /**@} */
12075
12076 /**@name Expression graph private methods */
12077 /**@{ */
12078
12079 /** assert that expression graph has at least a given depth */
12080 static
exprgraphEnsureDepth(SCIP_EXPRGRAPH * exprgraph,int mindepth)12081 SCIP_RETCODE exprgraphEnsureDepth(
12082 SCIP_EXPRGRAPH* exprgraph, /**< buffer to store pointer to expression graph */
12083 int mindepth /**< minimal depth that should be ensured */
12084 )
12085 {
12086 int olddepth;
12087
12088 assert(exprgraph != NULL);
12089 assert(exprgraph->blkmem != NULL);
12090
12091 if( mindepth <= exprgraph->depth )
12092 return SCIP_OKAY;
12093
12094 olddepth = exprgraph->depth;
12095 ensureBlockMemoryArraySize3(exprgraph->blkmem, &exprgraph->nodessize, &exprgraph->nnodes, &exprgraph->nodes, &exprgraph->depth, mindepth);
12096 assert(exprgraph->depth >= mindepth);
12097
12098 /* initialize new array entries to 0 and NULL, resp. */
12099 BMSclearMemoryArray(&exprgraph->nodessize[olddepth], exprgraph->depth - olddepth); /*lint !e866*/
12100 BMSclearMemoryArray(&exprgraph->nnodes[olddepth], exprgraph->depth - olddepth); /*lint !e866*/
12101 BMSclearMemoryArray(&exprgraph->nodes[olddepth], exprgraph->depth - olddepth); /*lint !e866*/
12102
12103 return SCIP_OKAY;
12104 }
12105
12106 /** remove a variable from the variables arrays, assuming that its node will be removed or converted next */
12107 static
exprgraphRemoveVar(SCIP_EXPRGRAPH * exprgraph,int varidx)12108 SCIP_RETCODE exprgraphRemoveVar(
12109 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
12110 int varidx /**< variable index */
12111 )
12112 {
12113 SCIP_EXPRGRAPHNODE* varnode;
12114 void* var;
12115
12116 assert(exprgraph != NULL);
12117 assert(varidx >= 0);
12118 assert(varidx < exprgraph->nvars);
12119
12120 varnode = exprgraph->varnodes[varidx];
12121 assert(varnode->data.intval == varidx);
12122
12123 var = exprgraph->vars[varidx];
12124
12125 /* call varremove callback method, if set */
12126 if( exprgraph->exprgraphvarremove != NULL )
12127 {
12128 SCIP_CALL( exprgraph->exprgraphvarremove(exprgraph, exprgraph->userdata, var, varnode) );
12129 }
12130
12131 /* remove variable from hashmap */
12132 SCIP_CALL( SCIPhashmapRemove(exprgraph->varidxs, var) );
12133
12134 /* move last variable to position varidx and give it the new index */
12135 if( varidx < exprgraph->nvars-1 )
12136 {
12137 /* call callback method, if set */
12138 if( exprgraph->exprgraphvarchgidx != NULL )
12139 {
12140 SCIP_CALL( exprgraph->exprgraphvarchgidx(exprgraph, exprgraph->userdata, exprgraph->vars[exprgraph->nvars-1], exprgraph->varnodes[exprgraph->nvars-1], exprgraph->nvars-1, varidx) );
12141 }
12142
12143 exprgraph->vars[varidx] = exprgraph->vars[exprgraph->nvars-1];
12144 exprgraph->varbounds[varidx] = exprgraph->varbounds[exprgraph->nvars-1];
12145 exprgraph->varnodes[varidx] = exprgraph->varnodes[exprgraph->nvars-1];
12146 exprgraph->varnodes[varidx]->data.intval = varidx;
12147 SCIP_CALL( SCIPhashmapSetImageInt(exprgraph->varidxs, exprgraph->vars[varidx], varidx) );
12148 }
12149 --exprgraph->nvars;
12150
12151 return SCIP_OKAY;
12152 }
12153
12154 /** moves a node in an expression graph to a different depth
12155 *
12156 * New depth must be larger than children depth.
12157 * Moves parent nodes to higher depth, if needed.
12158 * Variable nodes cannot be moved.
12159 */
12160 static
exprgraphMoveNode(SCIP_EXPRGRAPH * exprgraph,SCIP_EXPRGRAPHNODE * node,int newdepth)12161 SCIP_RETCODE exprgraphMoveNode(
12162 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
12163 SCIP_EXPRGRAPHNODE* node, /**< node that shall be moved */
12164 int newdepth /**< new depth to which to move node */
12165 )
12166 {
12167 int olddepth;
12168 int oldpos;
12169 int i;
12170
12171 assert(exprgraph != NULL);
12172 assert(node != NULL);
12173 assert(node->depth >= 0); /* node should be in graph */
12174 assert(newdepth >= 0);
12175
12176 /* if already on aimed depth, then don't need to move */
12177 if( node->depth == newdepth )
12178 return SCIP_OKAY;
12179
12180 SCIPdebugMessage("move node %p (%d,%d) to depth %d\n", (void*)node, node->depth, node->pos, newdepth);
12181
12182 #ifndef NDEBUG
12183 /* assert that children are at lower depth than new depth */
12184 for( i = 0; i < node->nchildren; ++i )
12185 assert(node->children[i]->depth < newdepth);
12186 #endif
12187
12188 /* move parents to higher depth, if needed */
12189 for( i = 0; i < node->nparents; ++i )
12190 {
12191 if( node->parents[i]->depth <= newdepth )
12192 {
12193 /* move parent to depth+1 */
12194 SCIP_CALL( exprgraphMoveNode(exprgraph, node->parents[i], newdepth+1) );
12195 assert(node->parents[i]->depth > newdepth);
12196 }
12197 }
12198
12199 /* ensure that graph is deep enough */
12200 SCIP_CALL( exprgraphEnsureDepth(exprgraph, newdepth+1) );
12201 assert(exprgraph->depth > newdepth);
12202
12203 olddepth = node->depth;
12204 oldpos = node->pos;
12205
12206 /* add node to new depth */
12207 ensureBlockMemoryArraySize(exprgraph->blkmem, &exprgraph->nodes[newdepth], &exprgraph->nodessize[newdepth], exprgraph->nnodes[newdepth]+1); /*lint !e866*/
12208 node->depth = newdepth;
12209 node->pos = exprgraph->nnodes[newdepth];
12210 exprgraph->nodes[newdepth][node->pos] = node;
12211 ++exprgraph->nnodes[newdepth];
12212
12213 /* by moving the node to a new depth, the parents array in all its childrens may not be sorted anymore (parents order depends on depth) */
12214 for( i = 0; i < node->nchildren; ++i )
12215 node->children[i]->parentssorted = FALSE;
12216
12217 /* move last node at previous depth to previous position, if it wasn't last */
12218 if( oldpos < exprgraph->nnodes[olddepth]-1 )
12219 {
12220 exprgraph->nodes[olddepth][oldpos] = exprgraph->nodes[olddepth][exprgraph->nnodes[olddepth]-1];
12221 exprgraph->nodes[olddepth][oldpos]->pos = oldpos;
12222
12223 /* by moving the node to a new position, the parents array in all its children may not be sorted anymore (parents order depends on depth) */
12224 for( i = 0; i < exprgraph->nodes[olddepth][oldpos]->nchildren; ++i )
12225 exprgraph->nodes[olddepth][oldpos]->children[i]->parentssorted = FALSE;
12226 }
12227 --exprgraph->nnodes[olddepth];
12228
12229 if( node->depth == 0 )
12230 {
12231 /* if at depth 0, then it need to be a node for either a constant or a variable */
12232 assert(node->op == SCIP_EXPR_CONST || node->op == SCIP_EXPR_VARIDX);
12233 if( node->op == SCIP_EXPR_CONST )
12234 {
12235 /* add node to constnodes array of exprgraph @todo should use SCIPsortedvecInsertPtr? */
12236 ensureBlockMemoryArraySize(exprgraph->blkmem, &exprgraph->constnodes, &exprgraph->constssize, exprgraph->nconsts + 1);
12237 exprgraph->constnodes[exprgraph->nconsts] = node;
12238 ++exprgraph->nconsts;
12239 exprgraph->constssorted = exprgraph->nconsts <= 1 || (exprgraph->constssorted && exprgraphConstNodeComp(exprgraph->constnodes[exprgraph->nconsts-2], node) < 0);
12240 }
12241 else
12242 {
12243 /* adding a variable by moving it from a higher depth seems awkward, how did the variable get there in the first place? */
12244 SCIPerrorMessage("cannot move variable nodes to depth 0\n");
12245 return SCIP_ERROR;
12246 }
12247
12248 /* nodes at depth 0 always have curvature linear, even before any curvature check was running */
12249 node->curv = SCIP_EXPRCURV_LINEAR;
12250 }
12251
12252 return SCIP_OKAY;
12253 }
12254
12255 /** given a list of children, tries to find a common parent that represents a given operator with the same given data */
12256 static
exprgraphFindParentByOperator(SCIP_EXPRGRAPH * exprgraph,int nchildren,SCIP_EXPRGRAPHNODE ** children,SCIP_EXPROP op,SCIP_EXPROPDATA opdata,SCIP_EXPR ** exprchildren,SCIP_EXPRGRAPHNODE ** parent)12257 SCIP_RETCODE exprgraphFindParentByOperator(
12258 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
12259 int nchildren, /**< number of children */
12260 SCIP_EXPRGRAPHNODE** children, /**< children which parents to inspect */
12261 SCIP_EXPROP op, /**< operator */
12262 SCIP_EXPROPDATA opdata, /**< operator data */
12263 SCIP_EXPR** exprchildren, /**< children of expression to consider when modifying (reordering) operator data, or NULL */
12264 SCIP_EXPRGRAPHNODE** parent /**< buffer to store parent node if any is found, or NULL if none found */
12265 )
12266 {
12267 SCIP_EXPRGRAPHNODE** parentcands;
12268 int nparentcands;
12269 int parentcandssize;
12270 int i;
12271 int p;
12272
12273 assert(exprgraph != NULL);
12274 assert(nchildren > 0);
12275 assert(children != NULL);
12276 assert(parent != NULL);
12277
12278 *parent = NULL;
12279
12280 /* create initial set of parent candidates as
12281 * all parents of first child that have the same operator type and the same number of children
12282 * additionally, some easy conditions for complex expression types:
12283 * if expression type is int/real/signpower, then compare also exponent,
12284 * if expression type is linear, then compare also constant part,
12285 * if expression type is quadratic, then compare also number of quadratic elements,
12286 * if expression type is polynomial, then compare also number of monmials and constant part
12287 */
12288 parentcandssize = children[0]->nparents;
12289 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &parentcands, parentcandssize) );
12290 nparentcands = 0;
12291 for( p = 0; p < children[0]->nparents; ++p )
12292 if( children[0]->parents[p]->op == op &&
12293 children[0]->parents[p]->nchildren == nchildren &&
12294 (op != SCIP_EXPR_INTPOWER || opdata.intval == children[0]->parents[p]->data.intval) &&
12295 (op != SCIP_EXPR_REALPOWER || opdata.dbl == children[0]->parents[p]->data.dbl) && /*lint !e777*/
12296 (op != SCIP_EXPR_SIGNPOWER || opdata.dbl == children[0]->parents[p]->data.dbl) && /*lint !e777*/
12297 (op != SCIP_EXPR_LINEAR || ((SCIP_Real*)opdata.data)[nchildren] == ((SCIP_Real*)children[0]->parents[p]->data.data)[nchildren]) && /*lint !e777*/
12298 (op != SCIP_EXPR_QUADRATIC || ((SCIP_EXPRDATA_QUADRATIC*)opdata.data)->nquadelems == ((SCIP_EXPRDATA_QUADRATIC*)children[0]->parents[p]->data.data)->nquadelems) &&
12299 (op != SCIP_EXPR_QUADRATIC || ((SCIP_EXPRDATA_QUADRATIC*)opdata.data)->constant == ((SCIP_EXPRDATA_QUADRATIC*)children[0]->parents[p]->data.data)->constant) && /*lint !e777*/
12300 (op != SCIP_EXPR_POLYNOMIAL || ((SCIP_EXPRDATA_POLYNOMIAL*)opdata.data)->nmonomials == ((SCIP_EXPRDATA_POLYNOMIAL*)children[0]->parents[p]->data.data)->nmonomials) &&
12301 (op != SCIP_EXPR_POLYNOMIAL || ((SCIP_EXPRDATA_POLYNOMIAL*)opdata.data)->constant == ((SCIP_EXPRDATA_POLYNOMIAL*)children[0]->parents[p]->data.data)->constant) /*lint !e777*/
12302 )
12303 {
12304 parentcands[nparentcands++] = children[0]->parents[p]; /*lint !e644*/
12305 }
12306
12307 /* for all remaining children, remove parent candidates, that are not in their list of parents */
12308 for( i = 1; i < nchildren && nparentcands > 0; ++i )
12309 {
12310 p = 0;
12311 while( p < nparentcands )
12312 {
12313 /* if parentcands[p] is a parent of childnodes[i], then move last parent candidate to position p,
12314 * otherwise keep candidate and check next one
12315 */
12316 if( !exprgraphNodeIsParent(children[i], parentcands[p]) )
12317 {
12318 parentcands[p] = parentcands[nparentcands-1];
12319 --nparentcands;
12320 }
12321 else
12322 ++p;
12323 }
12324 }
12325
12326 SCIPdebugMessage("check %d parent candidates for expr with operator %d and %d children\n", nparentcands, op, nchildren);
12327
12328 if( nparentcands == 0 )
12329 {
12330 BMSfreeBlockMemoryArray(exprgraph->blkmem, &parentcands, children[0]->nparents);
12331 return SCIP_OKAY;
12332 }
12333
12334 /* at this point, all parents in parentcands have the nodes in children as children and are of the same operator type
12335 * check if there is also one which corresponds to same expression and store that one in *parent
12336 */
12337 switch( op )
12338 {
12339 /* commutative operands with no data */
12340 case SCIP_EXPR_PLUS :
12341 case SCIP_EXPR_MUL :
12342 case SCIP_EXPR_MIN :
12343 case SCIP_EXPR_MAX :
12344 case SCIP_EXPR_SUM :
12345 case SCIP_EXPR_PRODUCT:
12346 case SCIP_EXPR_SQUARE :
12347 case SCIP_EXPR_SQRT :
12348 case SCIP_EXPR_EXP :
12349 case SCIP_EXPR_LOG :
12350 case SCIP_EXPR_SIN :
12351 case SCIP_EXPR_COS :
12352 case SCIP_EXPR_TAN :
12353 /* case SCIP_EXPR_ERF : */
12354 /* case SCIP_EXPR_ERFI : */
12355 case SCIP_EXPR_ABS :
12356 case SCIP_EXPR_SIGN :
12357 {
12358 /* sort childnodes, if needed for later */
12359 if( nchildren > 2 )
12360 SCIPsortPtr((void**)children, exprgraphnodecomp, nchildren);
12361 for( p = 0; p < nparentcands; ++p )
12362 {
12363 assert(parentcands[p]->op == op); /* that was the first criterium for adding a node to parentcands */
12364 assert(parentcands[p]->nchildren == nchildren); /* that was the second criterium for adding a node to parentcands */
12365
12366 if( nchildren == 1 )
12367 {
12368 assert(parentcands[p]->children[0] == children[0]);
12369 /* same operand, same child, so same expression */
12370 *parent = parentcands[p];
12371 break;
12372 }
12373 else if( nchildren == 2 )
12374 {
12375 /* We know that every node in children is also a child of parentcands[p].
12376 * However, if there are duplicates in children, then it can happen that not every child of parentcands[p] is also one of the children.
12377 * So only if children equals parentcands[p]->children in some permutation, the expressions are the same.
12378 */
12379 if( (parentcands[p]->children[0] == children[0] && parentcands[p]->children[1] == children[1]) ||
12380 ( parentcands[p]->children[0] == children[1] && parentcands[p]->children[1] == children[0]) )
12381 {
12382 *parent = parentcands[p];
12383 break;
12384 }
12385 }
12386 else
12387 {
12388 /* as in the case for two nodes, we need to check whether parentcands[p]->children and children are equal up to permutation */
12389
12390 /* sort children of parent candidate */
12391 SCIPsortPtr((void**)parentcands[p]->children, exprgraphnodecomp, nchildren);
12392
12393 /* check if childnodes and parentcands[p]->children are the same */
12394 for( i = 0; i < nchildren; ++i )
12395 if( children[i] != parentcands[p]->children[i] )
12396 break;
12397 if( i == nchildren )
12398 {
12399 /* yeah, found an exact match */
12400 *parent = parentcands[p];
12401 break;
12402 }
12403 }
12404 }
12405
12406 break;
12407 }
12408
12409 /* non-commutative operands with two children */
12410 case SCIP_EXPR_MINUS :
12411 case SCIP_EXPR_DIV :
12412 {
12413 for( p = 0; p < nparentcands; ++p )
12414 {
12415 assert(parentcands[p]->op == op); /* that was the first criterium for adding a node to parentcands */
12416 assert(parentcands[p]->nchildren == 2); /* that was the second criterium for adding a node to parentcands */
12417 /* order of operands matters, so check if childnodes have same order as children of parent candidate (and are the same nodes too) */
12418 if( parentcands[p]->children[0] == children[0] && parentcands[p]->children[1] == children[1] )
12419 {
12420 /* yeah, found one */
12421 *parent = parentcands[p];
12422 break;
12423 }
12424 }
12425
12426 break;
12427 }
12428
12429 /* operands with one child and data */
12430 case SCIP_EXPR_INTPOWER:
12431 {
12432 assert(parentcands[0]->op == op); /* that was the first criterium for adding a node to parentcands */
12433 assert(parentcands[0]->nchildren == 1); /* that was the second criterium for adding a node to parentcands */
12434 assert(parentcands[0]->children[0] == children[0]); /* that's what exprgraphNodeIsParent should have ensured */
12435 assert(parentcands[0]->data.intval == opdata.intval); /* that was another criterium for adding a node to parentcands */
12436
12437 /* yeah, have one with same exponent */
12438 *parent = parentcands[0];
12439
12440 break;
12441 }
12442
12443 case SCIP_EXPR_REALPOWER:
12444 case SCIP_EXPR_SIGNPOWER:
12445 {
12446 assert(parentcands[0]->op == op); /* that was the first criterium for adding a node to parentcands */
12447 assert(parentcands[0]->nchildren == 1); /* that was the second criterium for adding a node to parentcands */
12448 assert(parentcands[0]->children[0] == children[0]); /* that's what exprgraphNodeIsParent should have ensured */
12449 assert(parentcands[0]->data.dbl == opdata.dbl); /* that was another criterium for adding a node to parentcands */ /*lint !e777*/
12450
12451 /* yeah, have one with same exponent */
12452 *parent = parentcands[0];
12453
12454 break;
12455 }
12456
12457 /* commutative operands with n children and data */
12458 case SCIP_EXPR_LINEAR:
12459 {
12460 SCIP_Real* exprcoef;
12461 SCIP_Real* candcoef;
12462
12463 exprcoef = (SCIP_Real*)opdata.data;
12464 /* sort childnodes, take care that children in expression are sorted the same way if given (so we don't mess up assignment of coefficients) */
12465 if( exprchildren != NULL )
12466 SCIPsortPtrPtrReal((void**)children, (void**)exprchildren, exprcoef, exprgraphnodecomp, nchildren);
12467 else
12468 SCIPsortPtrReal((void**)children, exprcoef, exprgraphnodecomp, nchildren);
12469 for( p = 0; p < nparentcands; ++p )
12470 {
12471 assert(parentcands[p]->op == op); /* that was the first criterium for adding a node to parentcands */
12472 assert(parentcands[p]->nchildren == nchildren); /* that was the second criterium for adding a node to parentcands */
12473
12474 candcoef = (SCIP_Real*)parentcands[p]->data.data;
12475 assert(exprcoef[nchildren] == candcoef[nchildren]); /* that was a criterium for adding a node to parentcands */ /*lint !e777*/
12476
12477 /* sort children of parent candidate */
12478 SCIPsortPtrReal((void**)parentcands[p]->children, candcoef, exprgraphnodecomp, nchildren);
12479
12480 /* check if children and coefficients in parent candidate and expression are the same */
12481 for( i = 0; i < nchildren; ++i )
12482 {
12483 if( children[i] != parentcands[p]->children[i] )
12484 break;
12485 if( exprcoef[i] != candcoef[i] ) /*lint !e777*/
12486 break;
12487 }
12488 if( i < nchildren )
12489 continue;
12490
12491 /* yeah, found an exact match */
12492 *parent = parentcands[p];
12493 break;
12494 }
12495
12496 break;
12497 }
12498
12499 case SCIP_EXPR_QUADRATIC:
12500 {
12501 SCIP_EXPRDATA_QUADRATIC* exprdata;
12502 SCIP_Real* exprlincoef;
12503 SCIP_Real* candlincoef;
12504 SCIP_EXPRDATA_QUADRATIC* canddata;
12505 int* perm;
12506 int* invperm;
12507
12508 exprdata = (SCIP_EXPRDATA_QUADRATIC*)opdata.data;
12509 exprlincoef = exprdata->lincoefs;
12510
12511 /* sort children in expr and parentcands and update indices in quadelems accordingly, then sort quadelems again and compare */
12512
12513 /* sort expr->children and childnodes and store inverse permutation in invperm */
12514 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &invperm, nchildren) );
12515 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &perm, nchildren) );
12516 for( i = 0; i < nchildren; ++i )
12517 invperm[i] = i; /*lint !e644*/
12518
12519 if( exprlincoef != NULL )
12520 if( exprchildren != NULL )
12521 SCIPsortPtrPtrRealInt((void**)children, (void**)exprchildren, exprlincoef, invperm, exprgraphnodecomp, nchildren);
12522 else
12523 SCIPsortPtrRealInt((void**)children, exprlincoef, invperm, exprgraphnodecomp, nchildren);
12524 else
12525 if( exprchildren != NULL )
12526 SCIPsortPtrPtrInt((void**)children, (void**)exprchildren, invperm, exprgraphnodecomp, nchildren);
12527 else
12528 SCIPsortPtrInt((void**)children, invperm, exprgraphnodecomp, nchildren);
12529
12530 /* compute permutation from its inverse */
12531 for( i = 0; i < nchildren; ++i )
12532 perm[invperm[i]] = i; /*lint !e644*/
12533
12534 /* apply permuation to exprdata->quadelems and sort again */
12535 for( i = 0; i < exprdata->nquadelems; ++i )
12536 {
12537 exprdata->quadelems[i].idx1 = perm[exprdata->quadelems[i].idx1];
12538 exprdata->quadelems[i].idx2 = perm[exprdata->quadelems[i].idx2];
12539 if( exprdata->quadelems[i].idx1 > exprdata->quadelems[i].idx2 )
12540 {
12541 int tmp;
12542 tmp = exprdata->quadelems[i].idx1;
12543 exprdata->quadelems[i].idx1 = exprdata->quadelems[i].idx2;
12544 exprdata->quadelems[i].idx2 = tmp;
12545 }
12546 }
12547 SCIPquadelemSort(exprdata->quadelems, exprdata->nquadelems);
12548 exprdata->sorted = TRUE;
12549
12550 for( p = 0; p < nparentcands; ++p )
12551 {
12552 assert(parentcands[p]->op == op); /* that was the first criterium for adding a node to parentcands */
12553 assert(parentcands[p]->nchildren == nchildren); /* that was the second criterium for adding a node to parentcands */
12554
12555 canddata = (SCIP_EXPRDATA_QUADRATIC*)parentcands[p]->data.data;
12556 candlincoef = canddata->lincoefs;
12557 assert(canddata->nquadelems == exprdata->nquadelems); /* that was a criterium for adding a node to parentcands */
12558 assert(canddata->constant == exprdata->constant); /* that was a criterium for adding a node to parentcands */ /*lint !e777*/
12559
12560 /* sort parentcands[p]->children and store inverse permutation in invperm */
12561 for( i = 0; i < nchildren; ++i )
12562 invperm[i] = i;
12563
12564 if( candlincoef != NULL )
12565 SCIPsortPtrRealInt((void**)parentcands[p]->children, candlincoef, invperm, exprgraphnodecomp, parentcands[p]->nchildren);
12566 else
12567 SCIPsortPtrInt((void**)parentcands[p]->children, invperm, exprgraphnodecomp, nchildren);
12568
12569 /* compute permutation from its inverse */
12570 for( i = 0; i < nchildren; ++i )
12571 perm[invperm[i]] = i;
12572
12573 /* apply permutation to canddata->quadelems */
12574 for( i = 0; i < canddata->nquadelems; ++i )
12575 {
12576 canddata->quadelems[i].idx1 = perm[canddata->quadelems[i].idx1];
12577 canddata->quadelems[i].idx2 = perm[canddata->quadelems[i].idx2];
12578 if( canddata->quadelems[i].idx1 > canddata->quadelems[i].idx2 )
12579 {
12580 int tmp;
12581 tmp = canddata->quadelems[i].idx1;
12582 canddata->quadelems[i].idx1 = canddata->quadelems[i].idx2;
12583 canddata->quadelems[i].idx2 = tmp;
12584 }
12585 }
12586 SCIPquadelemSort(canddata->quadelems, canddata->nquadelems);
12587 canddata->sorted = TRUE;
12588
12589 /* check if children and linear coefficients in parent candidate and expression are the same */
12590 for( i = 0; i < nchildren; ++i )
12591 {
12592 if( children[i] != parentcands[p]->children[i] )
12593 break;
12594 if( (exprlincoef == NULL ? 0.0 : exprlincoef[i]) != (candlincoef == NULL ? 0.0 : candlincoef[i]) ) /*lint !e777*/
12595 break;
12596 }
12597 if( i < nchildren )
12598 continue;
12599
12600 assert(exprdata->nquadelems == canddata->nquadelems);
12601 for( i = 0; i < exprdata->nquadelems; ++i )
12602 {
12603 if( exprdata->quadelems[i].idx1 != canddata->quadelems[i].idx1 ||
12604 exprdata->quadelems[i].idx2 != canddata->quadelems[i].idx2 ||
12605 exprdata->quadelems[i].coef != canddata->quadelems[i].coef ) /*lint !e777*/
12606 break;
12607 }
12608 if( i == exprdata->nquadelems )
12609 {
12610 /* yeah, parentcands[p] is same quadratic expression as expr */
12611 *parent = parentcands[p];
12612 break;
12613 }
12614 }
12615
12616 BMSfreeBlockMemoryArray(exprgraph->blkmem, &perm, nchildren);
12617 BMSfreeBlockMemoryArray(exprgraph->blkmem, &invperm, nchildren);
12618
12619 break;
12620 }
12621
12622 /* @todo in one GlobalLib instance, two polynoms differ only in the sign of all coefficients, it would be nice to recognize this somehow */
12623 case SCIP_EXPR_POLYNOMIAL:
12624 {
12625 SCIP_EXPRDATA_POLYNOMIAL* exprdata;
12626 SCIP_EXPRDATA_POLYNOMIAL* canddata;
12627 int* perm;
12628 int* invperm;
12629
12630 exprdata = (SCIP_EXPRDATA_POLYNOMIAL*)opdata.data;
12631
12632 /* sort children in expr and parentcands and update child indices in polynomialdata, then sort monomials again and compare */
12633
12634 /* sort exprchildren and childnodes and store inverse permutation in invperm */
12635 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &invperm, nchildren) );
12636 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &perm, nchildren) );
12637 for( i = 0; i < nchildren; ++i )
12638 invperm[i] = i; /*lint !e644*/
12639
12640 if( exprchildren != NULL )
12641 SCIPsortPtrPtrInt((void**)children, (void**)exprchildren, invperm, exprgraphnodecomp, nchildren);
12642 else
12643 SCIPsortPtrInt((void**)children, invperm, exprgraphnodecomp, nchildren);
12644
12645 /* compute permutation from its inverse */
12646 for( i = 0; i < nchildren; ++i )
12647 perm[invperm[i]] = i; /*lint !e644*/
12648
12649 /* apply permutation to exprdata and sort again */
12650 polynomialdataApplyChildmap(exprdata, perm);
12651 polynomialdataSortMonomials(exprdata);
12652
12653 for( p = 0; p < nparentcands; ++p )
12654 {
12655 assert(parentcands[p]->op == op); /* that was the first criterium for adding a node to parentcands */
12656 assert(parentcands[p]->nchildren == nchildren); /* that was the second criterium for adding a node to parentcands */
12657
12658 canddata = (SCIP_EXPRDATA_POLYNOMIAL*)parentcands[p]->data.data;
12659 assert(canddata->nmonomials == exprdata->nmonomials); /* that was a criterium for adding a node to parentcands */
12660 assert(canddata->constant == exprdata->constant); /* that was a criterium for adding a node to parentcands */ /*lint !e777*/
12661
12662 /* sort parentcands[p]->children and store inverse permutation in invperm */
12663 for( i = 0; i < nchildren; ++i )
12664 invperm[i] = i;
12665
12666 SCIPsortPtrInt((void**)parentcands[p]->children, invperm, exprgraphnodecomp, nchildren);
12667
12668 /* compute permutation from its inverse */
12669 for( i = 0; i < nchildren; ++i )
12670 perm[invperm[i]] = i;
12671
12672 /* apply permutation to canddata and sort again */
12673 polynomialdataApplyChildmap(canddata, perm);
12674 polynomialdataSortMonomials(canddata);
12675
12676 /* check if children are equal */
12677 for( i = 0; i < nchildren; ++i )
12678 if( children[i] != parentcands[p]->children[i] )
12679 break;
12680 if( i < nchildren )
12681 continue;
12682
12683 /* check if monomials are equal */
12684 for( i = 0; i < exprdata->nmonomials; ++i )
12685 if( !SCIPexprAreMonomialsEqual(exprdata->monomials[i], canddata->monomials[i], 0.0) )
12686 break;
12687 if( i == exprdata->nmonomials )
12688 {
12689 /* yeah, parentcands[p] is same polynomial expression as expr */
12690 *parent = parentcands[p];
12691 break;
12692 }
12693 }
12694
12695 BMSfreeBlockMemoryArray(exprgraph->blkmem, &perm, nchildren);
12696 BMSfreeBlockMemoryArray(exprgraph->blkmem, &invperm, nchildren);
12697
12698 break;
12699 }
12700
12701 case SCIP_EXPR_USER:
12702 {
12703 /* @todo need comparison function on user data to decide whether a parent candidate fits */
12704 break;
12705 }
12706
12707 case SCIP_EXPR_VARIDX:
12708 case SCIP_EXPR_PARAM:
12709 case SCIP_EXPR_CONST:
12710 case SCIP_EXPR_LAST:
12711 SCIPerrorMessage("expression operand %d unexpected here\n", op);
12712 return SCIP_ERROR;
12713 }
12714
12715 BMSfreeBlockMemoryArray(exprgraph->blkmem, &parentcands, parentcandssize);
12716
12717 return SCIP_OKAY;
12718 }
12719
12720 /** adds an expression into an expression graph
12721 *
12722 * Enables corresponding nodes.
12723 */
12724 static
exprgraphAddExpr(SCIP_EXPRGRAPH * exprgraph,SCIP_EXPR * expr,void ** vars,SCIP_Real * params,SCIP_EXPRGRAPHNODE ** exprnode,SCIP_Bool * exprnodeisnew)12725 SCIP_RETCODE exprgraphAddExpr(
12726 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
12727 SCIP_EXPR* expr, /**< expression to add */
12728 void** vars, /**< variables corresponding to VARIDX expressions */
12729 SCIP_Real* params, /**< parameter values */
12730 SCIP_EXPRGRAPHNODE** exprnode, /**< buffer to store expression graph node corresponding to root of this expression */
12731 SCIP_Bool* exprnodeisnew /**< buffer to indicate whether the node in *exprnode has been newly created for this expression (otherwise, expression was already in graph) */
12732 )
12733 {
12734 SCIP_EXPRGRAPHNODE** childnodes;
12735 SCIP_Bool childisnew;
12736 SCIP_Bool nochildisnew;
12737 SCIP_EXPROPDATA opdata;
12738 int i;
12739
12740 assert(exprgraph != NULL);
12741 assert(expr != NULL);
12742 assert(exprnode != NULL);
12743 assert(exprnodeisnew != NULL);
12744
12745 if( expr->op == SCIP_EXPR_VARIDX )
12746 {
12747 /* find node corresponding to variable and add if not existing yet */
12748 assert(expr->nchildren == 0);
12749
12750 SCIP_CALL( SCIPexprgraphAddVars(exprgraph, 1, &vars[expr->data.intval], exprnode) );
12751 assert(*exprnode != NULL);
12752 assert((*exprnode)->op == SCIP_EXPR_VARIDX);
12753 assert((*exprnode)->data.intval >= 0);
12754 assert((*exprnode)->data.intval < exprgraph->nvars);
12755 assert(exprgraph->vars[(*exprnode)->data.intval] == vars[expr->data.intval]);
12756
12757 *exprnodeisnew = (*exprnode)->nuses == 0 && (*exprnode)->nparents == 0;
12758
12759 return SCIP_OKAY;
12760 }
12761
12762 if( expr->op == SCIP_EXPR_CONST )
12763 {
12764 /* find node corresponding to constant and add if not existing yet */
12765 assert(expr->nchildren == 0);
12766
12767 SCIP_CALL( SCIPexprgraphAddConst(exprgraph, expr->data.dbl, exprnode) );
12768 assert(*exprnode != NULL);
12769 assert((*exprnode)->op == SCIP_EXPR_CONST);
12770 assert((*exprnode)->data.dbl == expr->data.dbl); /*lint !e777*/
12771
12772 *exprnodeisnew = (*exprnode)->nuses == 0 && (*exprnode)->nparents == 0;
12773
12774 return SCIP_OKAY;
12775 }
12776
12777 if( expr->op == SCIP_EXPR_PARAM )
12778 {
12779 /* find node corresponding to constant corresponding to parameter and add if not existing yet */
12780 assert(expr->nchildren == 0);
12781 assert(params != NULL);
12782
12783 SCIP_CALL( SCIPexprgraphAddConst(exprgraph, params[expr->data.intval], exprnode) );
12784 assert(*exprnode != NULL);
12785 assert((*exprnode)->op == SCIP_EXPR_CONST);
12786 assert((*exprnode)->data.dbl == params[expr->data.intval]); /*lint !e777*/
12787
12788 *exprnodeisnew = (*exprnode)->nuses == 0 && (*exprnode)->nparents == 0;
12789
12790 return SCIP_OKAY;
12791 }
12792
12793 /* expression should be variable or constant or have children */
12794 assert(expr->nchildren > 0);
12795
12796 /* add children expressions into expression graph
12797 * check if we can find a common parent
12798 */
12799 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &childnodes, expr->nchildren) );
12800 nochildisnew = TRUE;
12801 for( i = 0; i < expr->nchildren; ++i )
12802 {
12803 SCIP_CALL( exprgraphAddExpr(exprgraph, expr->children[i], vars, params, &childnodes[i], &childisnew) ); /*lint !e644*/
12804 assert(childnodes[i] != NULL);
12805 nochildisnew &= !childisnew; /*lint !e514*/
12806 }
12807
12808 /* if all children were known already, check if there is also already a node for the expression that we aim to add */
12809 if( nochildisnew )
12810 {
12811 SCIP_CALL( exprgraphFindParentByOperator(exprgraph, expr->nchildren, childnodes, expr->op, expr->data, expr->children, exprnode) );
12812
12813 if( *exprnode != NULL )
12814 {
12815 /* node already existing, make sure it is enabled */
12816 (*exprnode)->enabled = TRUE;
12817 *exprnodeisnew = FALSE;
12818
12819 /* SCIPdebugMessage("reused node %p (%d,%d) for expr ", (void*)*exprnode, (*exprnode)->depth, (*exprnode)->pos);
12820 * SCIPdebug( SCIPexprPrint(expr, messagehdlr, NULL, NULL, NULL, NULL) );
12821 * SCIPdebugPrintf("\n");
12822 */
12823
12824 BMSfreeBlockMemoryArray(exprgraph->blkmem, &childnodes, expr->nchildren);
12825 return SCIP_OKAY;
12826 }
12827 }
12828
12829 SCIPdebugMessage("add expr with operator %d and %d children\n", expr->op, expr->nchildren);
12830
12831 /* copy expression data */
12832 if( exprOpTable[expr->op].copydata != NULL )
12833 {
12834 SCIP_CALL( exprOpTable[expr->op].copydata(exprgraph->blkmem, expr->nchildren, expr->data, &opdata) );
12835 }
12836 else
12837 {
12838 opdata = expr->data;
12839 }
12840
12841 SCIP_CALL( exprgraphCreateNode(exprgraph->blkmem, exprnode, expr->op, opdata) );
12842 SCIP_CALL( SCIPexprgraphAddNode(exprgraph, *exprnode, -1, expr->nchildren, childnodes) );
12843 *exprnodeisnew = TRUE;
12844
12845 BMSfreeBlockMemoryArray(exprgraph->blkmem, &childnodes, expr->nchildren);
12846
12847 /* SCIPdebugMessage("created new node %p (%d,%d) for expr ", (void*)*exprnode, (*exprnode)->depth, (*exprnode)->pos);
12848 * SCIPdebug( SCIPexprPrint(expr, messagehdlr, NULL, NULL, NULL, NULL) );
12849 * SCIPdebugPrintf("\n");
12850 */
12851
12852 return SCIP_OKAY;
12853 }
12854
12855 /** sets bounds in variable nodes to those stored in exprgraph's varbounds array */
12856 static
exprgraphUpdateVarNodeBounds(SCIP_EXPRGRAPH * exprgraph,SCIP_Bool * clearreverseprop,SCIP_Bool * boundchanged)12857 void exprgraphUpdateVarNodeBounds(
12858 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
12859 SCIP_Bool* clearreverseprop, /**< flag to set if we had reset bound tightenings from reverse propagation */
12860 SCIP_Bool* boundchanged /**< buffer to store whether a variables bound has changes, compared to those stored in nodes */
12861 )
12862 {
12863 SCIP_EXPRGRAPHNODE* node;
12864 int i;
12865 int p;
12866
12867 assert(exprgraph != NULL);
12868 assert(clearreverseprop != NULL);
12869 assert(boundchanged != NULL);
12870
12871 *boundchanged = FALSE;
12872 for( i = 0; i < exprgraph->nvars; ++i )
12873 {
12874 node = exprgraph->varnodes[i];
12875
12876 if( node->bounds.inf == exprgraph->varbounds[i].inf && /*lint !e777*/
12877 +node->bounds.sup == exprgraph->varbounds[i].sup ) /*lint !e777*/
12878 {
12879 node->boundstatus = SCIP_EXPRBOUNDSTATUS_VALID;
12880 continue;
12881 }
12882
12883 if( exprgraph->varbounds[i].inf > exprgraph->varbounds[i].sup )
12884 {
12885 /* hmm, may happen due to numerics, let's be conservative and relax bounds to something that seems reasonable */
12886 SCIP_Real tmp;
12887
12888 tmp = exprgraph->varbounds[i].inf;
12889 exprgraph->varbounds[i].inf = MIN(tmp, exprgraph->varbounds[i].sup);
12890 exprgraph->varbounds[i].sup = MAX(tmp, exprgraph->varbounds[i].sup);
12891 }
12892
12893 if( exprgraph->varbounds[i].inf < node->bounds.inf ||
12894 +exprgraph->varbounds[i].sup > node->bounds.sup )
12895 {
12896 for( p = 0; p < node->nparents; ++p )
12897 node->parents[p]->boundstatus = SCIP_EXPRBOUNDSTATUS_CHILDRELAXED;
12898
12899 node->bounds = exprgraph->varbounds[i];
12900 SCIPdebugMessage("registered relaxed bound [%g,%g] of var %d for propagation\n", node->bounds.inf, node->bounds.sup, i);
12901
12902 *boundchanged = TRUE;
12903
12904 /* if a childs bounds are relaxed, then a previous reverse propagation may be invalid, so we have to clear its remainings */
12905 *clearreverseprop = TRUE;
12906 }
12907 else if( isLbBetter(1e-9, exprgraph->varbounds[i].inf, node->bounds.inf, node->bounds.sup) ||
12908 ( isUbBetter(1e-9, exprgraph->varbounds[i].sup, node->bounds.inf, node->bounds.sup)) )
12909 {
12910 for( p = 0; p < node->nparents; ++p )
12911 node->parents[p]->boundstatus |= SCIP_EXPRBOUNDSTATUS_CHILDTIGHTENED;
12912
12913 node->bounds = exprgraph->varbounds[i];
12914 SCIPdebugMessage("registered tightened bound [%g,%g] of var %d for propagation\n", node->bounds.inf, node->bounds.sup, i);
12915
12916 *boundchanged = TRUE;
12917 }
12918 else
12919 {
12920 node->bounds = exprgraph->varbounds[i];
12921 SCIPdebugMessage("registered slightly tightened bound [%g,%g] of var %d for propagation\n", node->bounds.inf, node->bounds.sup, i);
12922 }
12923
12924 node->boundstatus = SCIP_EXPRBOUNDSTATUS_VALID;
12925 }
12926 }
12927
12928 /**@} */
12929
12930 /**@name Expression graph node methods */
12931 /**@{ */
12932
12933 /* In debug mode, the following methods are implemented as function calls to ensure
12934 * type validity.
12935 * In optimized mode, the methods are implemented as defines to improve performance.
12936 * However, we want to have them in the library anyways, so we have to undef the defines.
12937 */
12938
12939 #undef SCIPexprgraphCaptureNode
12940 #undef SCIPexprgraphIsNodeEnabled
12941 #undef SCIPexprgraphGetNodeNChildren
12942 #undef SCIPexprgraphGetNodeChildren
12943 #undef SCIPexprgraphGetNodeNParents
12944 #undef SCIPexprgraphGetNodeParents
12945 #undef SCIPexprgraphGetNodeDepth
12946 #undef SCIPexprgraphGetNodePosition
12947 #undef SCIPexprgraphGetNodeOperator
12948 #undef SCIPexprgraphGetNodeOperatorIndex
12949 #undef SCIPexprgraphGetNodeOperatorReal
12950 #undef SCIPexprgraphGetNodeVar
12951 #undef SCIPexprgraphGetNodeRealPowerExponent
12952 #undef SCIPexprgraphGetNodeIntPowerExponent
12953 #undef SCIPexprgraphGetNodeSignPowerExponent
12954 #undef SCIPexprgraphGetNodeLinearCoefs
12955 #undef SCIPexprgraphGetNodeLinearConstant
12956 #undef SCIPexprgraphGetNodeQuadraticConstant
12957 #undef SCIPexprgraphGetNodeQuadraticLinearCoefs
12958 #undef SCIPexprgraphGetNodeQuadraticQuadElements
12959 #undef SCIPexprgraphGetNodeQuadraticNQuadElements
12960 #undef SCIPexprgraphGetNodePolynomialMonomials
12961 #undef SCIPexprgraphGetNodePolynomialNMonomials
12962 #undef SCIPexprgraphGetNodePolynomialConstant
12963 #undef SCIPexprgraphGetNodeUserData
12964 #undef SCIPexprgraphHasNodeUserEstimator
12965 #undef SCIPexprgraphGetNodeBounds
12966 #undef SCIPexprgraphGetNodeVal
12967 #undef SCIPexprgraphGetNodeCurvature
12968
12969 /** captures node, i.e., increases number of uses */
SCIPexprgraphCaptureNode(SCIP_EXPRGRAPHNODE * node)12970 void SCIPexprgraphCaptureNode(
12971 SCIP_EXPRGRAPHNODE* node /**< expression graph node to capture */
12972 )
12973 {
12974 assert(node->nuses >= 0);
12975
12976 SCIPdebugMessage("capture node %p\n", (void*)node);
12977
12978 ++node->nuses;
12979 }
12980
12981 /** returns whether a node is currently enabled */
SCIPexprgraphIsNodeEnabled(SCIP_EXPRGRAPHNODE * node)12982 SCIP_Bool SCIPexprgraphIsNodeEnabled(
12983 SCIP_EXPRGRAPHNODE* node /**< expression graph node to enable */
12984 )
12985 {
12986 assert(node != NULL);
12987
12988 return node->enabled;
12989 }
12990
12991 /** gets number of children of a node in an expression graph */
SCIPexprgraphGetNodeNChildren(SCIP_EXPRGRAPHNODE * node)12992 int SCIPexprgraphGetNodeNChildren(
12993 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
12994 )
12995 {
12996 assert(node != NULL);
12997
12998 return node->nchildren;
12999 }
13000
13001 /** gets children of a node in an expression graph */
SCIPexprgraphGetNodeChildren(SCIP_EXPRGRAPHNODE * node)13002 SCIP_EXPRGRAPHNODE** SCIPexprgraphGetNodeChildren(
13003 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
13004 )
13005 {
13006 assert(node != NULL);
13007
13008 return node->children;
13009 }
13010
13011 /** gets number of parents of a node in an expression graph */
SCIPexprgraphGetNodeNParents(SCIP_EXPRGRAPHNODE * node)13012 int SCIPexprgraphGetNodeNParents(
13013 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
13014 )
13015 {
13016 assert(node != NULL);
13017
13018 return node->nparents;
13019 }
13020
13021 /** gets parents of a node in an expression graph */
SCIPexprgraphGetNodeParents(SCIP_EXPRGRAPHNODE * node)13022 SCIP_EXPRGRAPHNODE** SCIPexprgraphGetNodeParents(
13023 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
13024 )
13025 {
13026 assert(node != NULL);
13027
13028 return node->parents;
13029 }
13030
13031 /** gets depth of node in expression graph */
SCIPexprgraphGetNodeDepth(SCIP_EXPRGRAPHNODE * node)13032 int SCIPexprgraphGetNodeDepth(
13033 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
13034 )
13035 {
13036 assert(node != NULL);
13037
13038 return node->depth;
13039 }
13040
13041 /** gets position of node in expression graph at its depth level */
SCIPexprgraphGetNodePosition(SCIP_EXPRGRAPHNODE * node)13042 int SCIPexprgraphGetNodePosition(
13043 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
13044 )
13045 {
13046 assert(node != NULL);
13047
13048 return node->pos;
13049 }
13050
13051 /** gets operator of a node in an expression graph */
SCIPexprgraphGetNodeOperator(SCIP_EXPRGRAPHNODE * node)13052 SCIP_EXPROP SCIPexprgraphGetNodeOperator(
13053 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
13054 )
13055 {
13056 assert(node != NULL);
13057
13058 return node->op;
13059 }
13060
13061 /** gives index belonging to a SCIP_EXPR_VARIDX or SCIP_EXPR_PARAM operand */
SCIPexprgraphGetNodeOperatorIndex(SCIP_EXPRGRAPHNODE * node)13062 int SCIPexprgraphGetNodeOperatorIndex(
13063 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
13064 )
13065 {
13066 assert(node != NULL);
13067 assert(node->op == SCIP_EXPR_VARIDX || node->op == SCIP_EXPR_PARAM);
13068
13069 return node->data.intval;
13070 }
13071
13072 /** gives real belonging to a SCIP_EXPR_CONST operand */
SCIPexprgraphGetNodeOperatorReal(SCIP_EXPRGRAPHNODE * node)13073 SCIP_Real SCIPexprgraphGetNodeOperatorReal(
13074 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
13075 )
13076 {
13077 assert(node != NULL);
13078 assert(node->op == SCIP_EXPR_CONST);
13079
13080 return node->data.dbl;
13081 }
13082
13083 /** gives variable belonging to a SCIP_EXPR_VARIDX expression */
SCIPexprgraphGetNodeVar(SCIP_EXPRGRAPH * exprgraph,SCIP_EXPRGRAPHNODE * node)13084 void* SCIPexprgraphGetNodeVar(
13085 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
13086 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
13087 )
13088 {
13089 assert(exprgraph != NULL);
13090 assert(node != NULL);
13091 assert(node->op == SCIP_EXPR_VARIDX);
13092 assert(node->data.intval >= 0);
13093 assert(node->data.intval < exprgraph->nvars);
13094
13095 return exprgraph->vars[node->data.intval];
13096 }
13097
13098 /** gives exponent belonging to a SCIP_EXPR_REALPOWER expression */
SCIPexprgraphGetNodeRealPowerExponent(SCIP_EXPRGRAPHNODE * node)13099 SCIP_Real SCIPexprgraphGetNodeRealPowerExponent(
13100 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
13101 )
13102 {
13103 assert(node != NULL);
13104 assert(node->op == SCIP_EXPR_REALPOWER);
13105
13106 return node->data.dbl;
13107 }
13108
13109 /** gives exponent belonging to a SCIP_EXPR_INTPOWER expression */
SCIPexprgraphGetNodeIntPowerExponent(SCIP_EXPRGRAPHNODE * node)13110 int SCIPexprgraphGetNodeIntPowerExponent(
13111 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
13112 )
13113 {
13114 assert(node != NULL);
13115 assert(node->op == SCIP_EXPR_INTPOWER);
13116
13117 return node->data.intval;
13118 }
13119
13120 /** gives exponent belonging to a SCIP_EXPR_SIGNPOWER expression */
SCIPexprgraphGetNodeSignPowerExponent(SCIP_EXPRGRAPHNODE * node)13121 SCIP_Real SCIPexprgraphGetNodeSignPowerExponent(
13122 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
13123 )
13124 {
13125 assert(node != NULL);
13126 assert(node->op == SCIP_EXPR_SIGNPOWER);
13127
13128 return node->data.dbl;
13129 }
13130
13131 /** gives linear coefficients belonging to a SCIP_EXPR_LINEAR expression */
SCIPexprgraphGetNodeLinearCoefs(SCIP_EXPRGRAPHNODE * node)13132 SCIP_Real* SCIPexprgraphGetNodeLinearCoefs(
13133 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
13134 )
13135 {
13136 assert(node != NULL);
13137 assert(node->op == SCIP_EXPR_LINEAR);
13138
13139 return (SCIP_Real*)node->data.data;
13140 }
13141
13142 /** gives constant belonging to a SCIP_EXPR_LINEAR expression */
SCIPexprgraphGetNodeLinearConstant(SCIP_EXPRGRAPHNODE * node)13143 SCIP_Real SCIPexprgraphGetNodeLinearConstant(
13144 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
13145 )
13146 {
13147 assert(node != NULL);
13148 assert(node->op == SCIP_EXPR_LINEAR);
13149 assert(node->data.data != NULL);
13150
13151 return ((SCIP_Real*)node->data.data)[node->nchildren];
13152 }
13153
13154 /** gives constant belonging to a SCIP_EXPR_QUADRATIC expression */
SCIPexprgraphGetNodeQuadraticConstant(SCIP_EXPRGRAPHNODE * node)13155 SCIP_Real SCIPexprgraphGetNodeQuadraticConstant(
13156 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
13157 )
13158 {
13159 assert(node != NULL);
13160 assert(node->op == SCIP_EXPR_QUADRATIC);
13161 assert(node->data.data != NULL);
13162
13163 return ((SCIP_EXPRDATA_QUADRATIC*)node->data.data)->constant;
13164 }
13165
13166 /** gives linear coefficients belonging to a SCIP_EXPR_QUADRATIC expression, or NULL if all coefficients are 0.0 */
SCIPexprgraphGetNodeQuadraticLinearCoefs(SCIP_EXPRGRAPHNODE * node)13167 SCIP_Real* SCIPexprgraphGetNodeQuadraticLinearCoefs(
13168 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
13169 )
13170 {
13171 assert(node != NULL);
13172 assert(node->op == SCIP_EXPR_QUADRATIC);
13173 assert(node->data.data != NULL);
13174
13175 return ((SCIP_EXPRDATA_QUADRATIC*)node->data.data)->lincoefs;
13176 }
13177
13178 /** gives quadratic elements belonging to a SCIP_EXPR_QUADRATIC expression */
SCIPexprgraphGetNodeQuadraticQuadElements(SCIP_EXPRGRAPHNODE * node)13179 SCIP_QUADELEM* SCIPexprgraphGetNodeQuadraticQuadElements(
13180 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
13181 )
13182 {
13183 assert(node != NULL);
13184 assert(node->op == SCIP_EXPR_QUADRATIC);
13185 assert(node->data.data != NULL);
13186
13187 return ((SCIP_EXPRDATA_QUADRATIC*)node->data.data)->quadelems;
13188 }
13189
13190 /** gives number of quadratic elements belonging to a SCIP_EXPR_QUADRATIC expression */
SCIPexprgraphGetNodeQuadraticNQuadElements(SCIP_EXPRGRAPHNODE * node)13191 int SCIPexprgraphGetNodeQuadraticNQuadElements(
13192 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
13193 )
13194 {
13195 assert(node != NULL);
13196 assert(node->op == SCIP_EXPR_QUADRATIC);
13197 assert(node->data.data != NULL);
13198
13199 return ((SCIP_EXPRDATA_QUADRATIC*)node->data.data)->nquadelems;
13200 }
13201
13202 /** gives the monomials belonging to a SCIP_EXPR_POLYNOMIAL expression */
SCIPexprgraphGetNodePolynomialMonomials(SCIP_EXPRGRAPHNODE * node)13203 SCIP_EXPRDATA_MONOMIAL** SCIPexprgraphGetNodePolynomialMonomials(
13204 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
13205 )
13206 {
13207 assert(node != NULL);
13208 assert(node->op == SCIP_EXPR_POLYNOMIAL);
13209 assert(node->data.data != NULL);
13210
13211 return ((SCIP_EXPRDATA_POLYNOMIAL*)node->data.data)->monomials;
13212 }
13213
13214 /** gives the number of monomials belonging to a SCIP_EXPR_POLYNOMIAL expression */
SCIPexprgraphGetNodePolynomialNMonomials(SCIP_EXPRGRAPHNODE * node)13215 int SCIPexprgraphGetNodePolynomialNMonomials(
13216 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
13217 )
13218 {
13219 assert(node != NULL);
13220 assert(node->op == SCIP_EXPR_POLYNOMIAL);
13221 assert(node->data.data != NULL);
13222
13223 return ((SCIP_EXPRDATA_POLYNOMIAL*)node->data.data)->nmonomials;
13224 }
13225
13226 /** gives the constant belonging to a SCIP_EXPR_POLYNOMIAL expression */
SCIPexprgraphGetNodePolynomialConstant(SCIP_EXPRGRAPHNODE * node)13227 SCIP_Real SCIPexprgraphGetNodePolynomialConstant(
13228 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
13229 )
13230 {
13231 assert(node != NULL);
13232 assert(node->op == SCIP_EXPR_POLYNOMIAL);
13233 assert(node->data.data != NULL);
13234
13235 return ((SCIP_EXPRDATA_POLYNOMIAL*)node->data.data)->constant;
13236 }
13237
13238 /** gives the curvature of a single monomial belonging to a SCIP_EXPR_POLYNOMIAL expression
13239 *
13240 * Assumes that curvature of children and bounds of children and node itself are valid.
13241 */
SCIPexprgraphGetNodePolynomialMonomialCurvature(SCIP_EXPRGRAPHNODE * node,int monomialidx,SCIP_Real infinity,SCIP_EXPRCURV * curv)13242 SCIP_RETCODE SCIPexprgraphGetNodePolynomialMonomialCurvature(
13243 SCIP_EXPRGRAPHNODE* node, /**< expression graph node */
13244 int monomialidx, /**< index of monomial */
13245 SCIP_Real infinity, /**< value for infinity in interval arithmetics */
13246 SCIP_EXPRCURV* curv /**< buffer to store monomial curvature */
13247 )
13248 {
13249 SCIP_EXPRDATA_MONOMIAL* monomial;
13250 SCIP_INTERVAL childboundsstatic[SCIP_EXPRESSION_MAXCHILDEST];
13251 SCIP_EXPRCURV childcurvstatic[SCIP_EXPRESSION_MAXCHILDEST];
13252 SCIP_INTERVAL* childbounds = NULL;
13253 SCIP_EXPRCURV* childcurv = NULL;
13254 SCIP_EXPRGRAPHNODE* child;
13255 SCIP_RETCODE retcode = SCIP_OKAY;
13256 int i;
13257
13258 assert(node != NULL);
13259 assert(node->depth >= 0); /* node should be in graph */
13260 assert(node->pos >= 0); /* node should be in graph */
13261 assert(node->enabled); /* node should be enabled, otherwise we may not have uptodate bounds and curvatures in children */
13262 assert(node->boundstatus == SCIP_EXPRBOUNDSTATUS_VALID); /* we assume node bounds to be valid */
13263 assert(node->op == SCIP_EXPR_POLYNOMIAL);
13264 assert(node->data.data != NULL);
13265 assert(monomialidx >= 0);
13266 assert(monomialidx < ((SCIP_EXPRDATA_POLYNOMIAL*)node->data.data)->nmonomials);
13267 assert(curv != NULL);
13268
13269 if( SCIPintervalIsEmpty(infinity, node->bounds) )
13270 {
13271 *curv = SCIP_EXPRCURV_LINEAR;
13272 return SCIP_OKAY;
13273 }
13274
13275 monomial = ((SCIP_EXPRDATA_POLYNOMIAL*)node->data.data)->monomials[monomialidx];
13276 assert(monomial != NULL);
13277
13278 /* if many children, get large enough memory to store children bounds */
13279 if( monomial->nfactors > SCIP_EXPRESSION_MAXCHILDEST )
13280 {
13281 SCIP_ALLOC( BMSallocMemoryArray(&childbounds, monomial->nfactors) );
13282 SCIP_ALLOC_TERMINATE( retcode, BMSallocMemoryArray(&childcurv, monomial->nfactors), TERMINATE );
13283 }
13284 else
13285 {
13286 childbounds = childboundsstatic;
13287 childcurv = childcurvstatic;
13288 }
13289
13290 /* assemble bounds and curvature of children */
13291 for( i = 0; i < monomial->nfactors; ++i )
13292 {
13293 child = node->children[monomial->childidxs[i]];
13294 assert(child != NULL);
13295
13296 /* child should have valid and non-empty bounds */
13297 assert(!(child->boundstatus & SCIP_EXPRBOUNDSTATUS_CHILDRELAXED));
13298 assert(!SCIPintervalIsEmpty(infinity, child->bounds));
13299 /* nodes at depth 0 are always linear */
13300 assert(child->depth > 0 || child->curv == SCIP_EXPRCURV_LINEAR);
13301
13302 childbounds[i] = child->bounds; /*lint !e644*/
13303 childcurv[i] = child->curv; /*lint !e644*/
13304 }
13305
13306 /* check curvature */
13307 *curv = SCIPexprcurvMonomial(monomial->nfactors, monomial->exponents, NULL, childcurv, childbounds);
13308 *curv = SCIPexprcurvMultiply(monomial->coef, *curv);
13309
13310 /* free memory, if allocated before */
13311 TERMINATE:
13312 if( childbounds != childboundsstatic )
13313 {
13314 BMSfreeMemoryArrayNull(&childbounds);
13315 BMSfreeMemoryArrayNull(&childcurv);
13316 }
13317
13318 return retcode;
13319 }
13320
13321 /** gives the user data belonging to a SCIP_EXPR_USER expression */
SCIPexprgraphGetNodeUserData(SCIP_EXPRGRAPHNODE * node)13322 SCIP_USEREXPRDATA* SCIPexprgraphGetNodeUserData(
13323 SCIP_EXPRGRAPHNODE* node
13324 )
13325 {
13326 assert(node != NULL);
13327 assert(node->op == SCIP_EXPR_USER);
13328 assert(node->data.data != NULL);
13329
13330 return ((SCIP_EXPRDATA_USER*)node->data.data)->userdata;
13331 }
13332
13333 /** indicates whether a user expression has the estimator callback defined */
SCIPexprgraphHasNodeUserEstimator(SCIP_EXPRGRAPHNODE * node)13334 SCIP_Bool SCIPexprgraphHasNodeUserEstimator(
13335 SCIP_EXPRGRAPHNODE* node
13336 )
13337 {
13338 assert(node != NULL);
13339 assert(node->op == SCIP_EXPR_USER);
13340 assert(node->data.data != NULL);
13341
13342 return ((SCIP_EXPRDATA_USER*)node->data.data)->estimate != NULL;
13343 }
13344
13345 /** gets bounds of a node in an expression graph */
SCIPexprgraphGetNodeBounds(SCIP_EXPRGRAPHNODE * node)13346 SCIP_INTERVAL SCIPexprgraphGetNodeBounds(
13347 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
13348 )
13349 {
13350 assert(node != NULL);
13351
13352 return node->bounds;
13353 }
13354
13355 /** gets value of expression associated to node from last evaluation call */
SCIPexprgraphGetNodeVal(SCIP_EXPRGRAPHNODE * node)13356 SCIP_Real SCIPexprgraphGetNodeVal(
13357 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
13358 )
13359 {
13360 assert(node != NULL);
13361
13362 return node->value;
13363 }
13364
13365 /** gets curvature of expression associated to node from last curvature check call */
SCIPexprgraphGetNodeCurvature(SCIP_EXPRGRAPHNODE * node)13366 SCIP_EXPRCURV SCIPexprgraphGetNodeCurvature(
13367 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
13368 )
13369 {
13370 assert(node != NULL);
13371
13372 return node->curv;
13373 }
13374
13375 /** creates an expression graph node */
SCIPexprgraphCreateNode(BMS_BLKMEM * blkmem,SCIP_EXPRGRAPHNODE ** node,SCIP_EXPROP op,...)13376 SCIP_RETCODE SCIPexprgraphCreateNode(
13377 BMS_BLKMEM* blkmem, /**< block memory */
13378 SCIP_EXPRGRAPHNODE** node, /**< buffer to store expression graph node */
13379 SCIP_EXPROP op, /**< operator type of expression */
13380 ...
13381 )
13382 {
13383 va_list ap;
13384 SCIP_EXPROPDATA opdata;
13385
13386 assert(blkmem != NULL);
13387 assert(node != NULL);
13388
13389 *node = NULL;
13390
13391 switch( op )
13392 {
13393 case SCIP_EXPR_VARIDX :
13394 case SCIP_EXPR_PARAM :
13395 case SCIP_EXPR_CONST :
13396 case SCIP_EXPR_LINEAR :
13397 case SCIP_EXPR_QUADRATIC :
13398 case SCIP_EXPR_POLYNOMIAL:
13399 case SCIP_EXPR_USER :
13400 {
13401 SCIPerrorMessage("cannot create node with operand %d via SCIPexprgraphCreateNode\n", op);
13402 SCIPABORT();
13403 return SCIP_ERROR; /*lint !e527*/
13404 }
13405
13406 /* operands without data */
13407 case SCIP_EXPR_PLUS :
13408 case SCIP_EXPR_MINUS :
13409 case SCIP_EXPR_MUL :
13410 case SCIP_EXPR_DIV :
13411 case SCIP_EXPR_MIN :
13412 case SCIP_EXPR_MAX :
13413 case SCIP_EXPR_SQUARE :
13414 case SCIP_EXPR_SQRT :
13415 case SCIP_EXPR_EXP :
13416 case SCIP_EXPR_LOG :
13417 case SCIP_EXPR_SIN :
13418 case SCIP_EXPR_COS :
13419 case SCIP_EXPR_TAN :
13420 /* case SCIP_EXPR_ERF : */
13421 /* case SCIP_EXPR_ERFI: */
13422 case SCIP_EXPR_ABS :
13423 case SCIP_EXPR_SIGN :
13424 case SCIP_EXPR_SUM :
13425 case SCIP_EXPR_PRODUCT:
13426 opdata.data = NULL;
13427 break;
13428
13429 case SCIP_EXPR_REALPOWER:
13430 case SCIP_EXPR_SIGNPOWER:
13431 {
13432 va_start(ap, op ); /*lint !e838*/
13433 opdata.dbl = va_arg( ap, SCIP_Real); /*lint !e416 !e826*/
13434 va_end( ap ); /*lint !e826*/
13435
13436 break;
13437 }
13438
13439 case SCIP_EXPR_INTPOWER:
13440 {
13441 va_start(ap, op ); /*lint !e838*/
13442 opdata.intval = va_arg( ap, int); /*lint !e416 !e826*/
13443 va_end( ap ); /*lint !e826*/
13444
13445 break;
13446 }
13447
13448 case SCIP_EXPR_LAST:
13449 SCIPABORT();
13450 return SCIP_INVALIDDATA; /*lint !e527*/
13451 }
13452
13453 SCIP_CALL( exprgraphCreateNode(blkmem, node, op, opdata) ); /*lint !e644*/
13454
13455 return SCIP_OKAY;
13456 }
13457
13458 /** creates an expression graph node for a linear expression */
SCIPexprgraphCreateNodeLinear(BMS_BLKMEM * blkmem,SCIP_EXPRGRAPHNODE ** node,int ncoefs,SCIP_Real * coefs,SCIP_Real constant)13459 SCIP_RETCODE SCIPexprgraphCreateNodeLinear(
13460 BMS_BLKMEM* blkmem, /**< block memory */
13461 SCIP_EXPRGRAPHNODE** node, /**< buffer to store expression graph node */
13462 int ncoefs, /**< number of coefficients */
13463 SCIP_Real* coefs, /**< coefficients of linear expression */
13464 SCIP_Real constant /**< constant of linear expression */
13465 )
13466 {
13467 SCIP_EXPROPDATA opdata;
13468 SCIP_Real* data;
13469
13470 assert(blkmem != NULL);
13471 assert(node != NULL);
13472
13473 /* we store the coefficients and the constant in a single array and make this our operand data */
13474 SCIP_ALLOC( BMSallocBlockMemoryArray(blkmem, &data, ncoefs + 1) );
13475 BMScopyMemoryArray(data, coefs, ncoefs); /*lint !e644*/
13476 data[ncoefs] = constant;
13477
13478 opdata.data = data;
13479 SCIP_CALL( exprgraphCreateNode(blkmem, node, SCIP_EXPR_LINEAR, opdata) );
13480
13481 return SCIP_OKAY;
13482 }
13483
13484 /** creates an expression graph node for a quadratic expression */
SCIPexprgraphCreateNodeQuadratic(BMS_BLKMEM * blkmem,SCIP_EXPRGRAPHNODE ** node,int nchildren,SCIP_Real * lincoefs,int nquadelems,SCIP_QUADELEM * quadelems,SCIP_Real constant)13485 SCIP_RETCODE SCIPexprgraphCreateNodeQuadratic(
13486 BMS_BLKMEM* blkmem, /**< block memory */
13487 SCIP_EXPRGRAPHNODE** node, /**< buffer to store expression graph node */
13488 int nchildren, /**< number of children */
13489 SCIP_Real* lincoefs, /**< linear coefficients for children, or NULL */
13490 int nquadelems, /**< number of quadratic elements */
13491 SCIP_QUADELEM* quadelems, /**< quadratic elements, or NULL if nquadelems == 0 */
13492 SCIP_Real constant /**< constant */
13493 )
13494 {
13495 SCIP_EXPROPDATA opdata;
13496 SCIP_EXPRDATA_QUADRATIC* data;
13497
13498 assert(blkmem != NULL);
13499 assert(node != NULL);
13500 assert(quadelems != NULL || nquadelems == 0);
13501
13502 SCIP_CALL( quadraticdataCreate(blkmem, &data, constant, nchildren, lincoefs, nquadelems, quadelems) );
13503
13504 opdata.data = data;
13505 SCIP_CALL( exprgraphCreateNode(blkmem, node, SCIP_EXPR_QUADRATIC, opdata) );
13506
13507 return SCIP_OKAY;
13508 }
13509
13510 /** creates an expression graph node for a polynomial expression */
SCIPexprgraphCreateNodePolynomial(BMS_BLKMEM * blkmem,SCIP_EXPRGRAPHNODE ** node,int nmonomials,SCIP_EXPRDATA_MONOMIAL ** monomials,SCIP_Real constant,SCIP_Bool copymonomials)13511 SCIP_RETCODE SCIPexprgraphCreateNodePolynomial(
13512 BMS_BLKMEM* blkmem, /**< block memory */
13513 SCIP_EXPRGRAPHNODE** node, /**< buffer to store expression graph node */
13514 int nmonomials, /**< number of monomials */
13515 SCIP_EXPRDATA_MONOMIAL** monomials, /**< monomials */
13516 SCIP_Real constant, /**< constant of polynomial */
13517 SCIP_Bool copymonomials /**< whether to copy monomials or to assume ownership */
13518 )
13519 {
13520 SCIP_EXPROPDATA opdata;
13521 SCIP_EXPRDATA_POLYNOMIAL* data;
13522
13523 assert(blkmem != NULL);
13524 assert(node != NULL);
13525 assert(monomials != NULL || nmonomials == 0);
13526
13527 SCIP_CALL( polynomialdataCreate(blkmem, &data, nmonomials, monomials, constant, copymonomials) );
13528
13529 opdata.data = data;
13530 SCIP_CALL( exprgraphCreateNode(blkmem, node, SCIP_EXPR_POLYNOMIAL, opdata) );
13531
13532 return SCIP_OKAY;
13533 }
13534
13535 /** adds monomials to an expression graph node that is a polynomial expression */
SCIPexprgraphNodePolynomialAddMonomials(BMS_BLKMEM * blkmem,SCIP_EXPRGRAPHNODE * node,int nmonomials,SCIP_EXPRDATA_MONOMIAL ** monomials,SCIP_Bool copymonomials)13536 SCIP_RETCODE SCIPexprgraphNodePolynomialAddMonomials(
13537 BMS_BLKMEM* blkmem, /**< block memory */
13538 SCIP_EXPRGRAPHNODE* node, /**< store expression graph node with polynomial operator */
13539 int nmonomials, /**< number of monomials */
13540 SCIP_EXPRDATA_MONOMIAL** monomials, /**< monomials */
13541 SCIP_Bool copymonomials /**< whether to copy monomials or to assume ownership */
13542 )
13543 {
13544 assert(blkmem != NULL);
13545 assert(node != NULL);
13546 assert(SCIPexprgraphGetNodeOperator(node) == SCIP_EXPR_POLYNOMIAL);
13547 assert(monomials != NULL || nmonomials == 0);
13548
13549 SCIP_CALL( polynomialdataAddMonomials(blkmem, (SCIP_EXPRDATA_POLYNOMIAL*)node->data.data, nmonomials, monomials, copymonomials) );
13550
13551 return SCIP_OKAY;
13552 }
13553
13554 /** creates an expression graph node for a user expression */
SCIPexprgraphCreateNodeUser(BMS_BLKMEM * blkmem,SCIP_EXPRGRAPHNODE ** node,SCIP_USEREXPRDATA * data,SCIP_EXPRINTCAPABILITY evalcapability,SCIP_DECL_USEREXPREVAL ((* eval)),SCIP_DECL_USEREXPRINTEVAL ((* inteval)),SCIP_DECL_USEREXPRCURV ((* curv)),SCIP_DECL_USEREXPRPROP ((* prop)),SCIP_DECL_USEREXPRESTIMATE ((* estimate)),SCIP_DECL_USEREXPRCOPYDATA ((* copydata)),SCIP_DECL_USEREXPRFREEDATA ((* freedata)),SCIP_DECL_USEREXPRPRINT ((* print)))13555 SCIP_RETCODE SCIPexprgraphCreateNodeUser(
13556 BMS_BLKMEM* blkmem, /**< block memory */
13557 SCIP_EXPRGRAPHNODE** node, /**< buffer to store expression graph node */
13558 SCIP_USEREXPRDATA* data, /**< user data for expression, node assumes ownership */
13559 SCIP_EXPRINTCAPABILITY evalcapability, /**< evaluation capability */
13560 SCIP_DECL_USEREXPREVAL ((*eval)), /**< evaluation function */
13561 SCIP_DECL_USEREXPRINTEVAL ((*inteval)), /**< interval evaluation function */
13562 SCIP_DECL_USEREXPRCURV ((*curv)), /**< curvature check function */
13563 SCIP_DECL_USEREXPRPROP ((*prop)), /**< interval propagation function */
13564 SCIP_DECL_USEREXPRESTIMATE ((*estimate)), /**< estimation function, or NULL if convex, concave, or not implemented */
13565 SCIP_DECL_USEREXPRCOPYDATA ((*copydata)), /**< expression data copy function, or NULL if nothing to copy */
13566 SCIP_DECL_USEREXPRFREEDATA ((*freedata)), /**< expression data free function, or NULL if nothing to free */
13567 SCIP_DECL_USEREXPRPRINT ((*print)) /**< expression print function, or NULL for default string "user" */
13568 )
13569 {
13570 SCIP_EXPROPDATA opdata;
13571 SCIP_EXPRDATA_USER* exprdata;
13572
13573 assert(blkmem != NULL);
13574 assert(node != NULL);
13575 assert(eval != NULL);
13576 assert((evalcapability & SCIP_EXPRINTCAPABILITY_FUNCVALUE) != 0); /* the function evaluation is not optional */
13577 assert(((evalcapability & SCIP_EXPRINTCAPABILITY_INTFUNCVALUE) == 0) || inteval != NULL); /* if capability says it can do interval evaluation, then the corresponding callback needs to be provided */
13578 assert(copydata != NULL || data == NULL);
13579 assert(freedata != NULL || data == NULL);
13580
13581 SCIP_ALLOC( BMSallocBlockMemory(blkmem, &exprdata) );
13582
13583 exprdata->userdata = data;
13584 exprdata->evalcapability = evalcapability;
13585 exprdata->eval = eval;
13586 exprdata->estimate = estimate;
13587 exprdata->inteval = inteval;
13588 exprdata->curv = curv;
13589 exprdata->prop = prop;
13590 exprdata->copydata = copydata;
13591 exprdata->freedata = freedata;
13592 exprdata->print = print;
13593
13594 opdata.data = (void*) exprdata;
13595
13596 SCIP_CALL( exprgraphCreateNode(blkmem, node, SCIP_EXPR_USER, opdata) );
13597
13598 return SCIP_OKAY;
13599 }
13600
13601 /** given a node of an expression graph, splitup a linear part which variables are not used somewhere else in the same expression
13602 *
13603 * E.g., if the expression is 1 + x + y + y^2, one gets 1 + x and the node remains at y + y^2.
13604 * If the node is a linear expression, it may be freed.
13605 * If it is not linear, the node may change, i.e., the remaining nonlinear part may be stored in a new node.
13606 * It is assumed that the user had captured the node.
13607 * It is assumed that the expression graph has been simplified before.
13608 */
SCIPexprgraphNodeSplitOffLinear(SCIP_EXPRGRAPH * exprgraph,SCIP_EXPRGRAPHNODE ** node,int linvarssize,int * nlinvars,void ** linvars,SCIP_Real * lincoefs,SCIP_Real * constant)13609 SCIP_RETCODE SCIPexprgraphNodeSplitOffLinear(
13610 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
13611 SCIP_EXPRGRAPHNODE** node, /**< expression graph node where to splitup linear part */
13612 int linvarssize, /**< length of linvars and lincoefs arrays */
13613 int* nlinvars, /**< buffer to store length of linear term that have been splitup */
13614 void** linvars, /**< buffer to store variables of linear part */
13615 SCIP_Real* lincoefs, /**< buffer to store coefficients of linear part */
13616 SCIP_Real* constant /**< buffer to store constant part */
13617 )
13618 {
13619 int orignvars;
13620 int* varsusage;
13621 SCIP_EXPRGRAPHNODE* orignode;
13622 SCIP_Bool havechange;
13623 int i;
13624
13625 assert(exprgraph != NULL);
13626 assert(node != NULL);
13627 assert(*node != NULL);
13628 assert((*node)->nuses > 0);
13629 assert(nlinvars != NULL);
13630 assert(linvars != NULL || linvarssize == 0);
13631 assert(lincoefs != NULL || linvarssize == 0);
13632 assert(constant != NULL);
13633
13634 *constant = 0.0;
13635 *nlinvars = 0;
13636
13637 SCIPdebugMessage("split off linear part for %s node %p (%d,%d)\n", SCIPexpropGetName((*node)->op), (void*)*node, (*node)->depth, (*node)->pos);
13638
13639 /* do some obvious and easy cases */
13640 switch( (*node)->op )
13641 {
13642 case SCIP_EXPR_VARIDX:
13643 {
13644 if( linvarssize >= 1 )
13645 {
13646 *nlinvars = 1;
13647 linvars[0] = exprgraph->vars[(*node)->data.intval]; /*lint !e613*/
13648 lincoefs[0] = 1.0; /*lint !e613*/
13649
13650 SCIP_CALL( SCIPexprgraphReleaseNode(exprgraph, node) );
13651 }
13652 return SCIP_OKAY;
13653 }
13654
13655 case SCIP_EXPR_CONST:
13656 {
13657 *constant = (*node)->data.dbl;
13658 SCIP_CALL( SCIPexprgraphReleaseNode(exprgraph, node) );
13659
13660 return SCIP_OKAY;
13661 }
13662
13663 case SCIP_EXPR_REALPOWER:
13664 case SCIP_EXPR_SIGNPOWER:
13665 {
13666 if( (*node)->data.dbl == 1.0 && (*node)->children[0]->op == SCIP_EXPR_VARIDX && linvarssize >= 1 )
13667 {
13668 *nlinvars = 1;
13669 linvars[0] = exprgraph->vars[(*node)->children[0]->data.intval]; /*lint !e613*/
13670 lincoefs[0] = 1.0; /*lint !e613*/
13671
13672 SCIP_CALL( SCIPexprgraphReleaseNode(exprgraph, node) );
13673 }
13674 return SCIP_OKAY;
13675 }
13676
13677 case SCIP_EXPR_INTPOWER:
13678 {
13679 if( (*node)->data.intval == 1 && (*node)->children[0]->op == SCIP_EXPR_VARIDX && linvarssize >= 1 )
13680 {
13681 *nlinvars = 1;
13682 linvars[0] = exprgraph->vars[(*node)->children[0]->data.intval]; /*lint !e613*/
13683 lincoefs[0] = 1.0; /*lint !e613*/
13684
13685 SCIP_CALL( SCIPexprgraphReleaseNode(exprgraph, node) );
13686 }
13687 return SCIP_OKAY;
13688 }
13689
13690 case SCIP_EXPR_PLUS:
13691 {
13692 if( (*node)->children[0]->op == SCIP_EXPR_CONST && (*node)->children[1]->op == SCIP_EXPR_VARIDX && linvarssize >= 1 )
13693 {
13694 *constant = (*node)->children[0]->data.dbl;
13695 *nlinvars = 1;
13696 linvars[0] = exprgraph->vars[(*node)->children[1]->data.intval]; /*lint !e613*/
13697 lincoefs[0] = 1.0; /*lint !e613*/
13698
13699 SCIP_CALL( SCIPexprgraphReleaseNode(exprgraph, node) );
13700
13701 return SCIP_OKAY;
13702 }
13703 else if( (*node)->children[1]->op == SCIP_EXPR_CONST && (*node)->children[0]->op == SCIP_EXPR_VARIDX && linvarssize >= 1 )
13704 {
13705 *constant = (*node)->children[1]->data.dbl;
13706 *nlinvars = 1;
13707 linvars[0] = exprgraph->vars[(*node)->children[0]->data.intval]; /*lint !e613*/
13708 lincoefs[0] = 1.0; /*lint !e613*/
13709
13710 SCIP_CALL( SCIPexprgraphReleaseNode(exprgraph, node) );
13711
13712 return SCIP_OKAY;
13713 }
13714 else if( (*node)->children[0]->op == SCIP_EXPR_VARIDX && (*node)->children[1]->op == SCIP_EXPR_VARIDX && linvarssize >= 2 )
13715 {
13716 *nlinvars = 2;
13717 linvars[0] = exprgraph->vars[(*node)->children[0]->data.intval]; /*lint !e613*/
13718 lincoefs[0] = 1.0; /*lint !e613*/
13719 linvars[1] = exprgraph->vars[(*node)->children[1]->data.intval]; /*lint !e613*/
13720 lincoefs[1] = 1.0; /*lint !e613*/
13721
13722 SCIP_CALL( SCIPexprgraphReleaseNode(exprgraph, node) );
13723
13724 return SCIP_OKAY;
13725 }
13726 else if( ((*node)->children[0]->op == SCIP_EXPR_VARIDX || (*node)->children[1]->op == SCIP_EXPR_VARIDX) && linvarssize >= 1 )
13727 {
13728 /* handle this one later */
13729 break;
13730 }
13731 return SCIP_OKAY;
13732 }
13733
13734 case SCIP_EXPR_MINUS:
13735 {
13736 if( (*node)->children[0]->op == SCIP_EXPR_CONST && (*node)->children[1]->op == SCIP_EXPR_VARIDX && linvarssize >= 1 )
13737 {
13738 *constant = (*node)->children[0]->data.dbl;
13739 *nlinvars = 1;
13740 linvars[0] = exprgraph->vars[(*node)->children[1]->data.intval]; /*lint !e613*/
13741 lincoefs[0] = -1.0; /*lint !e613*/
13742
13743 SCIP_CALL( SCIPexprgraphReleaseNode(exprgraph, node) );
13744
13745 return SCIP_OKAY;
13746 }
13747 else if( (*node)->children[1]->op == SCIP_EXPR_CONST && (*node)->children[0]->op == SCIP_EXPR_VARIDX && linvarssize >= 1 )
13748 {
13749 *constant = -(*node)->children[1]->data.dbl;
13750 *nlinvars = 1;
13751 linvars[0] = exprgraph->vars[(*node)->children[0]->data.intval]; /*lint !e613*/
13752 lincoefs[0] = 1.0; /*lint !e613*/
13753
13754 SCIP_CALL( SCIPexprgraphReleaseNode(exprgraph, node) );
13755
13756 return SCIP_OKAY;
13757 }
13758 else if( (*node)->children[0]->op == SCIP_EXPR_VARIDX && (*node)->children[1]->op == SCIP_EXPR_VARIDX && linvarssize >= 2 )
13759 {
13760 *nlinvars = 2;
13761 linvars[0] = exprgraph->vars[(*node)->children[0]->data.intval]; /*lint !e613*/
13762 lincoefs[0] = 1.0; /*lint !e613*/
13763 linvars[1] = exprgraph->vars[(*node)->children[1]->data.intval]; /*lint !e613*/
13764 lincoefs[1] = -1.0; /*lint !e613*/
13765
13766 SCIP_CALL( SCIPexprgraphReleaseNode(exprgraph, node) );
13767
13768 return SCIP_OKAY;
13769 }
13770 else if( ((*node)->children[0]->op == SCIP_EXPR_VARIDX || (*node)->children[1]->op == SCIP_EXPR_VARIDX) && linvarssize >= 1 )
13771 {
13772 /* handle this one later */
13773 break;
13774 }
13775 return SCIP_OKAY;
13776 }
13777
13778 case SCIP_EXPR_MUL:
13779 {
13780 if( (*node)->children[0]->op == SCIP_EXPR_CONST && (*node)->children[1]->op == SCIP_EXPR_VARIDX && linvarssize >= 1 )
13781 {
13782 *nlinvars = 1;
13783 linvars[0] = exprgraph->vars[(*node)->children[1]->data.intval]; /*lint !e613*/
13784 lincoefs[0] = (*node)->children[0]->data.dbl; /*lint !e613*/
13785
13786 SCIP_CALL( SCIPexprgraphReleaseNode(exprgraph, node) );
13787 }
13788 else if( (*node)->children[1]->op == SCIP_EXPR_CONST && (*node)->children[0]->op == SCIP_EXPR_VARIDX && linvarssize >= 1 )
13789 {
13790 *nlinvars = 1;
13791 linvars[0] = exprgraph->vars[(*node)->children[0]->data.intval]; /*lint !e613*/
13792 lincoefs[0] = (*node)->children[1]->data.dbl; /*lint !e613*/
13793
13794 SCIP_CALL( SCIPexprgraphReleaseNode(exprgraph, node) );
13795 }
13796 return SCIP_OKAY;
13797 }
13798
13799 case SCIP_EXPR_DIV:
13800 {
13801 if( (*node)->children[1]->op != SCIP_EXPR_CONST )
13802 return SCIP_OKAY;
13803
13804 if( (*node)->children[0]->op == SCIP_EXPR_VARIDX && linvarssize >= 1 )
13805 {
13806 *nlinvars = 1;
13807 linvars[0] = exprgraph->vars[(*node)->children[0]->data.intval]; /*lint !e613*/
13808 lincoefs[0] = 1.0/(*node)->children[1]->data.dbl; /*lint !e613*/
13809
13810 SCIP_CALL( SCIPexprgraphReleaseNode(exprgraph, node) );
13811 }
13812 return SCIP_OKAY;
13813 }
13814
13815 case SCIP_EXPR_SQUARE:
13816 case SCIP_EXPR_SQRT:
13817 case SCIP_EXPR_EXP:
13818 case SCIP_EXPR_LOG:
13819 case SCIP_EXPR_SIN:
13820 case SCIP_EXPR_COS:
13821 case SCIP_EXPR_TAN:
13822 /* case SCIP_EXPR_ERF: */
13823 /* case SCIP_EXPR_ERFI: */
13824 case SCIP_EXPR_ABS:
13825 case SCIP_EXPR_SIGN:
13826 case SCIP_EXPR_MIN:
13827 case SCIP_EXPR_MAX:
13828 return SCIP_OKAY;
13829
13830 case SCIP_EXPR_PRODUCT:
13831 case SCIP_EXPR_USER:
13832 return SCIP_OKAY;
13833
13834 case SCIP_EXPR_SUM:
13835 case SCIP_EXPR_LINEAR:
13836 case SCIP_EXPR_QUADRATIC:
13837 case SCIP_EXPR_POLYNOMIAL:
13838 default:
13839 {
13840 /* check if there is a child that is a variable */
13841 for( i = 0; i < (*node)->nchildren; ++i )
13842 {
13843 if( (*node)->children[i]->op == SCIP_EXPR_VARIDX )
13844 break;
13845 }
13846
13847 if( i == (*node)->nchildren )
13848 return SCIP_OKAY;
13849
13850 break;
13851 }
13852 } /*lint !e788*/
13853
13854 /* count how often variables are used in this expression */
13855 assert(exprgraph->nvars > 0); /* in a simplified expr graph with no variables, there can only be const nodes, but these were handled above */
13856 orignvars = exprgraph->nvars;
13857 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &varsusage, exprgraph->nvars) );
13858 BMSclearMemoryArray(varsusage, exprgraph->nvars); /*lint !e644*/
13859
13860 exprgraphNodeGetVarsUsage(*node, varsusage);
13861
13862 /* duplicate node if it has parents or more than one user */
13863 orignode = NULL;
13864 if( (*node)->nparents > 0 || (*node)->nuses > 1 )
13865 {
13866 SCIP_EXPROPDATA data;
13867
13868 orignode = *node;
13869
13870 if( exprOpTable[orignode->op].copydata != NULL )
13871 {
13872 SCIP_CALL( exprOpTable[orignode->op].copydata(exprgraph->blkmem, orignode->nchildren, orignode->data, &data) );
13873 }
13874 else
13875 data = orignode->data;
13876
13877 SCIP_CALL( exprgraphCreateNode(exprgraph->blkmem, node, orignode->op, data) );
13878 SCIP_CALL( SCIPexprgraphAddNode(exprgraph, *node, -1, orignode->nchildren, orignode->children) );
13879 SCIPexprgraphCaptureNode(*node);
13880 }
13881
13882 havechange = FALSE;
13883 /* split up constant and linear part */
13884 switch( (*node)->op )
13885 {
13886 case SCIP_EXPR_PLUS:
13887 case SCIP_EXPR_MINUS:
13888 {
13889 SCIP_EXPRGRAPHNODE* varchild;
13890 SCIP_EXPRGRAPHNODE* otherchild;
13891 int varidx;
13892
13893 /* we had looked at this above already and only continued if exactly one node is still a child and linvarssize is >= 1 */
13894 assert((*node)->children[0]->op == SCIP_EXPR_VARIDX || (*node)->children[1]->op == SCIP_EXPR_VARIDX);
13895 assert((*node)->children[0]->op != SCIP_EXPR_VARIDX || (*node)->children[1]->op != SCIP_EXPR_VARIDX);
13896 assert(linvarssize >= 1);
13897
13898 varchild = (*node)->children[0]->op == SCIP_EXPR_VARIDX ? (*node)->children[0] : (*node)->children[1];
13899 otherchild = (*node)->children[0]->op == SCIP_EXPR_VARIDX ? (*node)->children[1] : (*node)->children[0];
13900 varidx = varchild->data.intval;
13901 /* if variable is used in other child (which should be nonlinear), we don't take it */
13902 if( varsusage[varidx] > 1 )
13903 break;
13904
13905 /* add to linear variables */
13906 *nlinvars = 1;
13907 linvars[0] = exprgraph->vars[varidx]; /*lint !e613*/
13908 if( (*node)->op == SCIP_EXPR_MINUS && varchild == (*node)->children[1] )
13909 lincoefs[0] = -1.0; /*lint !e613*/
13910 else
13911 lincoefs[0] = 1.0; /*lint !e613*/
13912
13913 if( (*node)->op == SCIP_EXPR_PLUS || (*node)->children[0] == otherchild )
13914 {
13915 /* replace *node by otherchild */
13916 SCIPexprgraphCaptureNode(otherchild);
13917 SCIP_CALL( SCIPexprgraphReleaseNode(exprgraph, node) );
13918 *node = otherchild;
13919 }
13920 else
13921 {
13922 SCIP_Real* lindata;
13923
13924 /* turn *node into linear expression -1.0 * otherchild */
13925
13926 /* reduce to one child */
13927 SCIP_ALLOC( BMSreallocBlockMemoryArray(exprgraph->blkmem, &(*node)->children, 2, 1) ); /*lint !e506*/
13928 (*node)->children[0] = otherchild;
13929 (*node)->nchildren = 1;
13930 (*node)->op = SCIP_EXPR_LINEAR;
13931
13932 /* setup linear data -1.0 * child0 + 0.0 */
13933 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &lindata, 2) ); /*lint !e506*/
13934 lindata[0] = -1.0;
13935 lindata[1] = 0.0;
13936 (*node)->data.data = (void*)lindata;
13937
13938 /* remove *node as parent of varchild */
13939 SCIP_CALL( exprgraphNodeRemoveParent(exprgraph, &varchild, *node) );
13940 }
13941
13942 havechange = TRUE;
13943
13944 break;
13945 }
13946
13947 case SCIP_EXPR_SUM:
13948 {
13949 int nchildren;
13950
13951 i = 0;
13952 nchildren = (*node)->nchildren;
13953 while( i < nchildren )
13954 {
13955 /* sort out constants */
13956 if( (*node)->children[i]->op == SCIP_EXPR_CONST )
13957 {
13958 *constant += (*node)->children[i]->data.dbl;
13959 SCIP_CALL( exprgraphNodeRemoveParent(exprgraph, &(*node)->children[i], *node) );
13960
13961 if( i < nchildren-1 )
13962 {
13963 (*node)->children[i] = (*node)->children[nchildren-1];
13964 (*node)->children[nchildren-1] = NULL;
13965 }
13966 --nchildren;
13967
13968 continue;
13969 }
13970
13971 /* keep every child that is not a constant or variable */
13972 if( (*node)->children[i]->op != SCIP_EXPR_VARIDX )
13973 {
13974 ++i;
13975 continue;
13976 }
13977
13978 /* skip variables that are used in other parts of the expression */
13979 if( varsusage[(*node)->children[i]->data.intval] > 1 )
13980 {
13981 ++i;
13982 continue;
13983 }
13984
13985 /* move variable into linear part, if still space */
13986 if( *nlinvars < linvarssize )
13987 {
13988 linvars[*nlinvars] = exprgraph->vars[(*node)->children[i]->data.intval]; /*lint !e613*/
13989 lincoefs[*nlinvars] = 1.0; /*lint !e613*/
13990 ++*nlinvars;
13991
13992 SCIP_CALL( exprgraphNodeRemoveParent(exprgraph, &(*node)->children[i], *node) );
13993 if( i < nchildren-1 )
13994 {
13995 (*node)->children[i] = (*node)->children[nchildren-1];
13996 (*node)->children[nchildren-1] = NULL;
13997 }
13998 --nchildren;
13999
14000 continue;
14001 }
14002 }
14003 assert(i == nchildren);
14004
14005 if( nchildren == 0 )
14006 {
14007 /* all children were removed */
14008 havechange = TRUE;
14009 BMSfreeBlockMemoryArray(exprgraph->blkmem, &(*node)->children, (*node)->nchildren);
14010 (*node)->nchildren = 0;
14011 SCIP_CALL( SCIPexprgraphReleaseNode(exprgraph, node) );
14012 break;
14013 }
14014
14015 if( nchildren < (*node)->nchildren )
14016 {
14017 /* some children were removed */
14018 havechange = TRUE;
14019 SCIP_ALLOC( BMSreallocBlockMemoryArray(exprgraph->blkmem, &(*node)->children, (*node)->nchildren, nchildren) );
14020 (*node)->nchildren = nchildren;
14021 }
14022
14023 if( havechange && (*node)->nchildren == 1 )
14024 {
14025 /* replace node by its child */
14026 SCIP_EXPRGRAPHNODE* child;
14027
14028 child = (*node)->children[0];
14029 SCIPexprgraphCaptureNode(child);
14030 SCIP_CALL( SCIPexprgraphReleaseNode(exprgraph, node) );
14031 *node = child;
14032
14033 break;
14034 }
14035
14036 break;
14037 }
14038
14039 case SCIP_EXPR_LINEAR:
14040 {
14041 int nchildren;
14042 SCIP_Real* coefs;
14043
14044 coefs = (SCIP_Real*)(*node)->data.data;
14045 assert(coefs != NULL);
14046
14047 /* remove constant, if nonzero */
14048 if( coefs[(*node)->nchildren] != 0.0 )
14049 {
14050 *constant = coefs[(*node)->nchildren];
14051 coefs[(*node)->nchildren] = 0.0;
14052 havechange = TRUE;
14053 }
14054
14055 i = 0;
14056 nchildren = (*node)->nchildren;
14057 while( i < nchildren )
14058 {
14059 /* sort out constants */
14060 if( (*node)->children[i]->op == SCIP_EXPR_CONST )
14061 {
14062 *constant += coefs[i] * (*node)->children[i]->data.dbl;
14063 SCIP_CALL( exprgraphNodeRemoveParent(exprgraph, &(*node)->children[i], *node) );
14064
14065 if( i < nchildren-1 )
14066 {
14067 (*node)->children[i] = (*node)->children[nchildren-1];
14068 (*node)->children[nchildren-1] = NULL;
14069 coefs[i] = coefs[nchildren-1];
14070 coefs[nchildren-1] = 0.0;
14071 }
14072 --nchildren;
14073
14074 continue;
14075 }
14076
14077 /* keep everything that is not a constant or variable */
14078 if( (*node)->children[i]->op != SCIP_EXPR_VARIDX )
14079 {
14080 ++i;
14081 continue;
14082 }
14083
14084 /* skip variables that are used in other parts of the expression */
14085 if( varsusage[(*node)->children[i]->data.intval] > 1 )
14086 {
14087 ++i;
14088 continue;
14089 }
14090
14091 /* move variable into linear part, if still space */
14092 if( *nlinvars < linvarssize )
14093 {
14094 linvars[*nlinvars] = exprgraph->vars[(*node)->children[i]->data.intval]; /*lint !e613*/
14095 lincoefs[*nlinvars] = coefs[i]; /*lint !e613*/
14096 ++*nlinvars;
14097
14098 SCIP_CALL( exprgraphNodeRemoveParent(exprgraph, &(*node)->children[i], *node) );
14099 if( i < nchildren-1 )
14100 {
14101 (*node)->children[i] = (*node)->children[nchildren-1];
14102 (*node)->children[nchildren-1] = NULL;
14103 coefs[i] = coefs[nchildren-1];
14104 coefs[nchildren-1] = 0.0;
14105 }
14106 --nchildren;
14107
14108 continue;
14109 }
14110 }
14111 assert(i == nchildren);
14112
14113 if( nchildren == 0 )
14114 {
14115 /* all children were removed */
14116 havechange = TRUE;
14117 BMSfreeBlockMemoryArray(exprgraph->blkmem, &(*node)->children, (*node)->nchildren);
14118 BMSfreeBlockMemoryArray(exprgraph->blkmem, &coefs, (*node)->nchildren+1);
14119 (*node)->data.data = NULL;
14120 (*node)->nchildren = 0;
14121 (*node)->op = SCIP_EXPR_SUM; /* because we freed the constraint data already */
14122 SCIP_CALL( SCIPexprgraphReleaseNode(exprgraph, node) );
14123 break;
14124 }
14125
14126 if( nchildren < (*node)->nchildren )
14127 {
14128 /* some children were removed */
14129 havechange = TRUE;
14130 SCIP_ALLOC( BMSreallocBlockMemoryArray(exprgraph->blkmem, &(*node)->children, (*node)->nchildren, nchildren) );
14131 SCIP_ALLOC( BMSreallocBlockMemoryArray(exprgraph->blkmem, &coefs, (*node)->nchildren+1, nchildren+1) );
14132 coefs[nchildren] = 0.0;
14133 (*node)->data.data = (void*)coefs;
14134 (*node)->nchildren = nchildren;
14135 }
14136
14137 if( havechange && (*node)->nchildren == 1 && coefs[0] == 1.0 )
14138 {
14139 /* replace node by its child */
14140 SCIP_EXPRGRAPHNODE* child;
14141
14142 child = (*node)->children[0];
14143 SCIPexprgraphCaptureNode(child);
14144 SCIP_CALL( SCIPexprgraphReleaseNode(exprgraph, node) );
14145 *node = child;
14146
14147 break;
14148 }
14149
14150 break;
14151 }
14152
14153 case SCIP_EXPR_QUADRATIC:
14154 {
14155 SCIP_EXPRDATA_QUADRATIC* quaddata;
14156 SCIP_Bool* childused;
14157 int* childmap;
14158 int nchildren;
14159
14160 quaddata = (SCIP_EXPRDATA_QUADRATIC*)(*node)->data.data;
14161 assert(quaddata != NULL);
14162
14163 /* remove constant, if nonzero */
14164 if( quaddata->constant != 0.0 )
14165 {
14166 *constant = quaddata->constant;
14167 quaddata->constant = 0.0;
14168 havechange = TRUE;
14169 }
14170
14171 /* if there is no linear part or no space left for linear variables, then stop */
14172 if( quaddata->lincoefs == NULL || linvarssize == 0 )
14173 break;
14174
14175 /* check which childs are used in quadratic terms */
14176 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &childused, (*node)->nchildren) );
14177 BMSclearMemoryArray(childused, (*node)->nchildren); /*lint !e644*/
14178
14179 for( i = 0; i < quaddata->nquadelems; ++i )
14180 {
14181 childused[quaddata->quadelems[i].idx1] = TRUE;
14182 childused[quaddata->quadelems[i].idx2] = TRUE;
14183 }
14184
14185 /* alloc space for mapping of children indices */
14186 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &childmap, (*node)->nchildren) );
14187
14188 nchildren = (*node)->nchildren;
14189 for( i = 0; i < nchildren; ++i )
14190 {
14191 childmap[i] = i; /*lint !e644*/
14192 if( *nlinvars >= linvarssize )
14193 continue;
14194 /* skip child if not variable or also used in quadratic part or other parts of expression */
14195 if( (*node)->children[i]->op != SCIP_EXPR_VARIDX )
14196 continue;
14197 if( childused[i] )
14198 continue;
14199 if( varsusage[(*node)->children[i]->data.intval] > 1 )
14200 continue;
14201
14202 /* put variable into linear part */
14203 linvars[*nlinvars] = exprgraph->vars[(*node)->children[i]->data.intval]; /*lint !e613*/
14204 lincoefs[*nlinvars] = quaddata->lincoefs[i]; /*lint !e613*/
14205 quaddata->lincoefs[i] = 0.0;
14206 ++*nlinvars;
14207
14208 SCIP_CALL( exprgraphNodeRemoveParent(exprgraph, &(*node)->children[i], *node) );
14209
14210 /* move last child to position i */
14211 if( i < nchildren-1 )
14212 {
14213 (*node)->children[i] = (*node)->children[nchildren-1];
14214 quaddata->lincoefs[i] = quaddata->lincoefs[nchildren-1];
14215 childused[i] = childused[nchildren-1];
14216 childmap[nchildren-1] = i;
14217 }
14218 --nchildren;
14219 childmap[i] = -1;
14220
14221 havechange = TRUE;
14222 --i; /* look at i again */
14223 }
14224
14225 BMSfreeBlockMemoryArray(exprgraph->blkmem, &childused, (*node)->nchildren); /*lint !e850*/
14226
14227 if( nchildren < (*node)->nchildren )
14228 {
14229 /* apply childmap to quadratic term */
14230 for( i = 0; i < quaddata->nquadelems; ++i )
14231 {
14232 quaddata->quadelems[i].idx1 = childmap[quaddata->quadelems[i].idx1];
14233 quaddata->quadelems[i].idx2 = childmap[quaddata->quadelems[i].idx2];
14234 if( quaddata->quadelems[i].idx1 > quaddata->quadelems[i].idx2 )
14235 {
14236 int tmp;
14237 tmp = quaddata->quadelems[i].idx1;
14238 quaddata->quadelems[i].idx1 = quaddata->quadelems[i].idx2;
14239 quaddata->quadelems[i].idx2 = tmp;
14240 }
14241 }
14242 quaddata->sorted = FALSE;
14243 }
14244 BMSfreeBlockMemoryArray(exprgraph->blkmem, &childmap, (*node)->nchildren);
14245
14246 if( nchildren == 0 )
14247 {
14248 /* all children were removed (so it was actually a linear expression) */
14249 havechange = TRUE;
14250 BMSfreeBlockMemoryArray(exprgraph->blkmem, &(*node)->children, (*node)->nchildren);
14251 exprFreeDataQuadratic(exprgraph->blkmem, (*node)->nchildren, (*node)->data);
14252 (*node)->data.data = NULL;
14253 (*node)->nchildren = 0;
14254 (*node)->op = SCIP_EXPR_SUM;
14255 SCIP_CALL( SCIPexprgraphReleaseNode(exprgraph, node) );
14256 break;
14257 }
14258
14259 if( nchildren < (*node)->nchildren )
14260 {
14261 /* reduce number of children */
14262 SCIP_ALLOC( BMSreallocBlockMemoryArray(exprgraph->blkmem, &(*node)->children, (*node)->nchildren, nchildren) );
14263 SCIP_ALLOC( BMSreallocBlockMemoryArray(exprgraph->blkmem, &quaddata->lincoefs, (*node)->nchildren, nchildren) );
14264 (*node)->nchildren = nchildren;
14265 }
14266
14267 break;
14268 }
14269
14270 case SCIP_EXPR_POLYNOMIAL:
14271 {
14272 SCIP_EXPRDATA_POLYNOMIAL* polynomialdata;
14273 SCIP_EXPRDATA_MONOMIAL* monomial;
14274 SCIP_Bool* childused;
14275 int childidx;
14276 int j;
14277
14278 polynomialdata = (SCIP_EXPRDATA_POLYNOMIAL*)(*node)->data.data;
14279 assert(polynomialdata != NULL);
14280
14281 /* make sure linear monomials are merged */
14282 polynomialdataMergeMonomials(exprgraph->blkmem, polynomialdata, 0.0, FALSE);
14283
14284 /* remove constant, if nonzero */
14285 if( polynomialdata->constant != 0.0 )
14286 {
14287 *constant = polynomialdata->constant;
14288 polynomialdata->constant = 0.0;
14289 havechange = TRUE;
14290 }
14291
14292 /* if there is no space for linear variables, then stop */
14293 if( linvarssize == 0 )
14294 break;
14295
14296 /* get nonlinear child usage: how often each child is used in the polynomial in a nonlinear monomial */
14297 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &childused, (*node)->nchildren) );
14298 BMSclearMemoryArray(childused, (*node)->nchildren); /*lint !e644*/
14299 for( i = 0; i < polynomialdata->nmonomials; ++i )
14300 {
14301 monomial = polynomialdata->monomials[i];
14302 assert(monomial != NULL);
14303 if( monomial->nfactors == 0 || (monomial->nfactors == 1 && monomial->exponents[0] == 1.0) )
14304 continue;
14305 for( j = 0; j < monomial->nfactors; ++j )
14306 {
14307 assert(monomial->childidxs[j] >= 0);
14308 assert(monomial->childidxs[j] < (*node)->nchildren);
14309 childused[monomial->childidxs[j]] = TRUE;
14310 }
14311 }
14312
14313 /* move linear monomials out of polynomial */
14314 for( i = 0; i < polynomialdata->nmonomials && *nlinvars < linvarssize; ++i )
14315 {
14316 monomial = polynomialdata->monomials[i];
14317 assert(monomial != NULL);
14318
14319 /* sort out constants */
14320 if( monomial->nfactors == 0 )
14321 {
14322 if( monomial->coef != 0.0 )
14323 {
14324 *constant += monomial->coef;
14325 havechange = TRUE;
14326 }
14327 continue;
14328 }
14329
14330 if( monomial->nfactors != 1 )
14331 continue;
14332 if( monomial->exponents[0] != 1.0 )
14333 continue;
14334 childidx = monomial->childidxs[0];
14335 assert((*node)->children[childidx] != NULL); /* should be due to merge in the beginning */
14336 if( (*node)->children[childidx]->op != SCIP_EXPR_VARIDX )
14337 continue;
14338 if( childused[childidx] || varsusage[(*node)->children[childidx]->data.intval] > 1 )
14339 continue;
14340
14341 /* we are at a linear monomial in a variable that is not used somewhere else in nonlinear form */
14342
14343 /* put variable into linear part */
14344 linvars[*nlinvars] = exprgraph->vars[(*node)->children[childidx]->data.intval]; /*lint !e613*/
14345 lincoefs[*nlinvars] = monomial->coef; /*lint !e613*/
14346 ++*nlinvars;
14347
14348 monomial->coef = 0.0;
14349 monomial->nfactors = 0;
14350 polynomialdata->sorted = FALSE;
14351
14352 SCIP_CALL( exprgraphNodeRemoveParent(exprgraph, &(*node)->children[childidx], *node) );
14353 (*node)->children[childidx] = NULL;
14354
14355 havechange = TRUE;
14356 }
14357
14358 BMSfreeBlockMemoryArray(exprgraph->blkmem, &childused, (*node)->nchildren);
14359
14360 if( *nlinvars > 0 )
14361 {
14362 /* if we did something, cleanup polynomial (e.g., remove monomials with coefficient 0.0) */
14363 polynomialdataMergeMonomials(exprgraph->blkmem, polynomialdata, 0.0, FALSE);
14364 SCIP_CALL( exprgraphNodeRemovePolynomialNullChildren(exprgraph->blkmem, *node) );
14365 }
14366
14367 if( (*node)->nchildren == 0 )
14368 {
14369 assert(polynomialdata->nmonomials == 0);
14370 assert(polynomialdata->constant == 0.0);
14371 SCIP_CALL( SCIPexprgraphReleaseNode(exprgraph, node) );
14372 havechange = TRUE;
14373 break;
14374 }
14375
14376 break;
14377 }
14378
14379 default: ;
14380 } /*lint !e788*/
14381
14382 BMSfreeBlockMemoryArray(exprgraph->blkmem, &varsusage, orignvars);
14383
14384 if( orignode != NULL )
14385 {
14386 /* if node was duplicated, we need to forget about original or duplicate */
14387 if( !havechange )
14388 {
14389 /* if nothing has changed, then forget about duplicate */
14390 assert(*constant == 0.0);
14391 assert(*nlinvars == 0);
14392 assert(*node != NULL);
14393 SCIP_CALL( SCIPexprgraphReleaseNode(exprgraph, node) );
14394 *node = orignode;
14395 }
14396 else
14397 {
14398 /* if something changed, then release original node */
14399 SCIP_CALL( SCIPexprgraphReleaseNode(exprgraph, &orignode) );
14400 }
14401 }
14402 else if( havechange && *node != NULL )
14403 {
14404 /* if node was not duplicated and not removed but changed, then invalidate value, bounds, and simplified status */
14405 (*node)->value = SCIP_INVALID;
14406 (*node)->simplified = FALSE;
14407 (*node)->boundstatus = SCIP_EXPRBOUNDSTATUS_CHILDTIGHTENED;
14408 SCIPintervalSetEntire(SCIP_REAL_MAX, &(*node)->bounds);
14409 exprgraph->needvarboundprop = TRUE;
14410 }
14411
14412 return SCIP_OKAY;
14413 }
14414
14415 /** moves parents from a one node to another node
14416 *
14417 * In other words, replaces the child srcnode by targetnode in all parents of srcnode.
14418 * srcnode may be freed, if not captured.
14419 * It is assumed that targetnode represents the same expression as srcnode.
14420 */
SCIPexprgraphMoveNodeParents(SCIP_EXPRGRAPH * exprgraph,SCIP_EXPRGRAPHNODE ** srcnode,SCIP_EXPRGRAPHNODE * targetnode)14421 SCIP_RETCODE SCIPexprgraphMoveNodeParents(
14422 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
14423 SCIP_EXPRGRAPHNODE** srcnode, /**< node which parents to move */
14424 SCIP_EXPRGRAPHNODE* targetnode /**< node where to move parents to */
14425 )
14426 {
14427 assert(exprgraph != NULL);
14428 assert(srcnode != NULL);
14429 assert(*srcnode != NULL);
14430 assert(targetnode != NULL);
14431
14432 while( *srcnode != NULL && (*srcnode)->nparents > 0 )
14433 {
14434 if( (*srcnode)->parents[0]->depth <= targetnode->depth )
14435 {
14436 SCIP_CALL( exprgraphMoveNode(exprgraph, (*srcnode)->parents[0], targetnode->depth+1) );
14437 }
14438 SCIP_CALL( exprgraphNodeReplaceChild(exprgraph, (*srcnode)->parents[0], srcnode, targetnode) );
14439 }
14440 assert(*srcnode == NULL || (*srcnode)->nuses > 0);
14441
14442 return SCIP_OKAY;
14443 }
14444
14445 /** releases node, i.e., decreases number of uses
14446 *
14447 * node is freed if no parents and no other uses.
14448 * Children are recursively released if they have no other parents.
14449 * Nodes that are removed are also freed.
14450 * If node correspond to a variable, then the variable is removed from the expression graph;
14451 * similarly for constants.
14452 */
SCIPexprgraphReleaseNode(SCIP_EXPRGRAPH * exprgraph,SCIP_EXPRGRAPHNODE ** node)14453 SCIP_RETCODE SCIPexprgraphReleaseNode(
14454 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
14455 SCIP_EXPRGRAPHNODE** node /**< expression graph node to release */
14456 )
14457 {
14458 int i;
14459
14460 assert(exprgraph != NULL);
14461 assert(node != NULL);
14462 assert(*node != NULL);
14463 assert((*node)->depth >= 0); /* node should be in graph */
14464 assert((*node)->pos >= 0); /* node should be in graph */
14465 assert((*node)->depth < exprgraph->depth);
14466 assert((*node)->pos < exprgraph->nnodes[(*node)->depth]);
14467 assert((*node)->nuses >= 1);
14468 assert(exprgraph->nodes[(*node)->depth][(*node)->pos] == *node);
14469
14470 SCIPdebugMessage("release node %p\n", (void*)*node);
14471
14472 --(*node)->nuses;
14473
14474 /* do nothing if node still has parents or is still in use */
14475 if( (*node)->nparents > 0 || (*node)->nuses > 0 )
14476 {
14477 SCIPdebugMessage("skip removing node %p (%d, %d) with %d parents and %d uses from expression graph\n", (void*)*node, (*node)->depth, (*node)->pos, (*node)->nparents, (*node)->nuses);
14478 *node = NULL;
14479 return SCIP_OKAY;
14480 }
14481
14482 SCIPdebugMessage("remove node %p (%d, %d) with op %s from expression graph\n", (void*)*node, (*node)->depth, (*node)->pos, SCIPexpropGetName((*node)->op));
14483
14484 /* notify children about removal of its parent
14485 * they are also freed, if possible */
14486 for( i = 0; i < (*node)->nchildren; ++i )
14487 {
14488 SCIP_CALL( exprgraphNodeRemoveParent(exprgraph, &(*node)->children[i], *node) );
14489 (*node)->children[i] = NULL;
14490 }
14491
14492 if( (*node)->op == SCIP_EXPR_VARIDX )
14493 {
14494 assert((*node)->depth == 0);
14495 SCIP_CALL( exprgraphRemoveVar(exprgraph, (*node)->data.intval) );
14496 }
14497 else if( (*node)->op == SCIP_EXPR_CONST && (*node)->depth == 0 )
14498 {
14499 int constidx;
14500
14501 (void) exprgraphFindConstNodePos(exprgraph, *node, &constidx);
14502 assert(constidx >= 0);
14503 assert(constidx < exprgraph->nconsts);
14504 assert(exprgraph->constnodes[constidx] == *node);
14505
14506 /* move last constant to position constidx */
14507 if( constidx < exprgraph->nconsts-1 )
14508 {
14509 exprgraph->constnodes[constidx] = exprgraph->constnodes[exprgraph->nconsts-1];
14510 exprgraph->constssorted = (exprgraph->nconsts <= 2);
14511 }
14512 --exprgraph->nconsts;
14513 }
14514 else
14515 {
14516 /* only variables and constants are allowed at depth 0 */
14517 assert((*node)->depth > 0);
14518 }
14519
14520 /* remove node from nodes array in expression graph */
14521 if( (*node)->pos < exprgraph->nnodes[(*node)->depth]-1 )
14522 {
14523 /* move last node at depth of *node to position of *node */
14524 exprgraph->nodes[(*node)->depth][(*node)->pos] = exprgraph->nodes[(*node)->depth][exprgraph->nnodes[(*node)->depth]-1];
14525 exprgraph->nodes[(*node)->depth][(*node)->pos]->pos = (*node)->pos;
14526
14527 /* moving the node may change the order in the parents array of each child */
14528 for( i = 0; i < exprgraph->nodes[(*node)->depth][(*node)->pos]->nchildren; ++i )
14529 exprgraph->nodes[(*node)->depth][(*node)->pos]->children[i]->parentssorted = FALSE;
14530 }
14531 --exprgraph->nnodes[(*node)->depth];
14532
14533 /* node is now not in graph anymore */
14534 (*node)->depth = -1;
14535 (*node)->pos = -1;
14536
14537 /* free node */
14538 SCIPexprgraphFreeNode(exprgraph->blkmem, node);
14539
14540 *node = NULL;
14541
14542 return SCIP_OKAY;
14543 }
14544
14545 /* @todo should be a private method and node creation should already capture a node instead of waiting that it's added to the graph */
14546 /** frees a node of an expression graph */
SCIPexprgraphFreeNode(BMS_BLKMEM * blkmem,SCIP_EXPRGRAPHNODE ** node)14547 void SCIPexprgraphFreeNode(
14548 BMS_BLKMEM* blkmem, /**< block memory */
14549 SCIP_EXPRGRAPHNODE** node /**< pointer to expression graph node that should be freed */
14550 )
14551 {
14552 assert(blkmem != NULL);
14553 assert( node != NULL);
14554 assert(*node != NULL);
14555 assert((*node)->depth == -1); /* node should not be in graph anymore */
14556 assert((*node)->pos == -1); /* node should not be in graph anymore */
14557 assert((*node)->nuses == 0); /* node should not be in use */
14558
14559 /* free operator data, if needed */
14560 if( exprOpTable[(*node)->op].freedata != NULL )
14561 exprOpTable[(*node)->op].freedata(blkmem, (*node)->nchildren, (*node)->data);
14562
14563 /* free arrays of children and parent nodes */
14564 BMSfreeBlockMemoryArrayNull(blkmem, &(*node)->children, (*node)->nchildren);
14565 BMSfreeBlockMemoryArrayNull(blkmem, &(*node)->parents, (*node)->parentssize);
14566
14567 /* free node struct */
14568 BMSfreeBlockMemory(blkmem, node);
14569 }
14570
14571 /** enables a node and recursively all its children in an expression graph */
SCIPexprgraphEnableNode(SCIP_EXPRGRAPH * exprgraph,SCIP_EXPRGRAPHNODE * node)14572 void SCIPexprgraphEnableNode(
14573 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
14574 SCIP_EXPRGRAPHNODE* node /**< expression graph node to enable */
14575 )
14576 {
14577 int i;
14578
14579 assert(exprgraph != NULL);
14580 assert(node != NULL);
14581 assert(node->depth >= 0);
14582 assert(node->pos >= 0);
14583
14584 if( node->enabled )
14585 return;
14586
14587 SCIPdebugMessage("enable node %p (%d,%d)\n", (void*)node, node->depth, node->pos);
14588
14589 node->enabled = TRUE;
14590 for( i = 0; i < node->nchildren; ++i )
14591 SCIPexprgraphEnableNode(exprgraph, node->children[i]);
14592
14593 /* make sure bounds are updated in next bound propagation round */
14594 SCIPintervalSetEntire(SCIP_REAL_MAX, &node->bounds);
14595 exprgraph->needvarboundprop = TRUE;
14596 }
14597
14598 /** disables a node and recursively all children which have no enabled parents in an expression graph */
SCIPexprgraphDisableNode(SCIP_EXPRGRAPH * exprgraph,SCIP_EXPRGRAPHNODE * node)14599 void SCIPexprgraphDisableNode(
14600 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
14601 SCIP_EXPRGRAPHNODE* node /**< expression graph node to enable */
14602 )
14603 {
14604 int i;
14605
14606 assert(exprgraph != NULL);
14607 assert(node != NULL);
14608 assert(node->depth >= 0);
14609 assert(node->pos >= 0);
14610
14611 if( !node->enabled )
14612 return;
14613
14614 /* workaround: don't disable nodes if there could be more users than the one who is disabling the node
14615 * otherwise, if there are several nonlinear constraints using the same expression graph node as root node,
14616 * we might get enabled constraints with disabled node
14617 */
14618 if( node->nuses > 1 )
14619 return;
14620
14621 /* if all parents of node are disabled, then also node can be disabled */
14622 node->enabled = FALSE;
14623 for( i = 0; i < node->nparents; ++i )
14624 if( node->parents[i]->enabled )
14625 {
14626 node->enabled = TRUE;
14627 return;
14628 }
14629
14630 SCIPdebugMessage("disabled node %p (%d,%d), nuses = %d\n", (void*)node, node->depth, node->pos, node->nuses);
14631
14632 for( i = 0; i < node->nchildren; ++i )
14633 SCIPexprgraphDisableNode(exprgraph, node->children[i]);
14634 }
14635
14636 /** returns whether the node has siblings in the expression graph */
SCIPexprgraphHasNodeSibling(SCIP_EXPRGRAPHNODE * node)14637 SCIP_Bool SCIPexprgraphHasNodeSibling(
14638 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
14639 )
14640 {
14641 int p;
14642
14643 assert(node != NULL);
14644
14645 for( p = 0; p < node->nparents; ++p )
14646 if( node->parents[p]->nchildren > 1 )
14647 return TRUE;
14648
14649 return FALSE;
14650 }
14651
14652 /** returns whether all children of an expression graph node are variable nodes
14653 *
14654 * Returns TRUE for nodes without children.
14655 */
SCIPexprgraphAreAllNodeChildrenVars(SCIP_EXPRGRAPHNODE * node)14656 SCIP_Bool SCIPexprgraphAreAllNodeChildrenVars(
14657 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
14658 )
14659 {
14660 int i;
14661
14662 assert(node != NULL);
14663
14664 for( i = 0; i < node->nchildren; ++i )
14665 if( node->children[i]->op != SCIP_EXPR_VARIDX )
14666 return FALSE;
14667
14668 return TRUE;
14669 }
14670
14671 /** returns whether the node has an ancestor which has a nonlinear expression operand */
SCIPexprgraphHasNodeNonlinearAncestor(SCIP_EXPRGRAPHNODE * node)14672 SCIP_Bool SCIPexprgraphHasNodeNonlinearAncestor(
14673 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
14674 )
14675 {
14676 int p;
14677
14678 for( p = 0; p < node->nparents; ++p )
14679 {
14680 assert(node->parents[p]->depth > node->depth);
14681 switch( node->parents[p]->op )
14682 {
14683 case SCIP_EXPR_PLUS:
14684 case SCIP_EXPR_MINUS:
14685 case SCIP_EXPR_SUM:
14686 case SCIP_EXPR_LINEAR:
14687 if( SCIPexprgraphHasNodeNonlinearAncestor(node->parents[p]) )
14688 return TRUE;
14689 break;
14690
14691 #ifndef NDEBUG
14692 case SCIP_EXPR_VARIDX:
14693 case SCIP_EXPR_CONST:
14694 case SCIP_EXPR_PARAM:
14695 assert(0); /* these expressions cannot have children */
14696 break;
14697 #endif
14698
14699 default:
14700 /* parent has nonlinear expression operand */
14701 return TRUE;
14702 }/*lint !e788*/
14703 }
14704
14705 return FALSE;
14706 }
14707
14708 /** prints an expression graph node */
SCIPexprgraphPrintNode(SCIP_EXPRGRAPHNODE * node,SCIP_MESSAGEHDLR * messagehdlr,FILE * file)14709 void SCIPexprgraphPrintNode(
14710 SCIP_EXPRGRAPHNODE* node, /**< expression graph node */
14711 SCIP_MESSAGEHDLR* messagehdlr, /**< message handler */
14712 FILE* file /**< file to print to, or NULL for stdout */
14713 )
14714 {
14715 assert(node != NULL);
14716
14717 exprgraphPrintNodeExpression(node, messagehdlr, file, NULL, FALSE);
14718 }
14719
14720 /** tightens the bounds in a node of the graph
14721 *
14722 * Preparation for reverse propagation.
14723 * Sets bound status to SCIP_EXPRBOUNDSTATUS_TIGHTENEDBYPARENTRECENT if tightening is strong enough and not cutoff.
14724 */
SCIPexprgraphTightenNodeBounds(SCIP_EXPRGRAPH * exprgraph,SCIP_EXPRGRAPHNODE * node,SCIP_INTERVAL nodebounds,SCIP_Real minstrength,SCIP_Real infinity,SCIP_Bool * cutoff)14725 void SCIPexprgraphTightenNodeBounds(
14726 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
14727 SCIP_EXPRGRAPHNODE* node, /**< node in expression graph with no parents */
14728 SCIP_INTERVAL nodebounds, /**< new bounds for node */
14729 SCIP_Real minstrength, /**< minimal required relative bound strengthening in a node to trigger a propagation into children nodes (set to negative value if propagation should always be triggered) */
14730 SCIP_Real infinity, /**< value for infinity in interval arithmetics */
14731 SCIP_Bool* cutoff /**< buffer to store whether a node's bounds were propagated to an empty interval */
14732 )
14733 {
14734 assert(exprgraph != NULL);
14735 assert(node != NULL);
14736 assert(node->depth >= 0);
14737 assert(node->pos >= 0);
14738 assert(!SCIPintervalIsEmpty(infinity, nodebounds));
14739 assert(cutoff != NULL);
14740
14741 *cutoff = FALSE;
14742
14743 /* if node is disabled, then ignore new bounds */
14744 if( !node->enabled )
14745 {
14746 SCIPdebugMessage("ignore bound tightening for node %p (%d,%d)\n", (void*)node, node->depth, node->pos);
14747 return;
14748 }
14749
14750 SCIPdebugMessage("tighten bounds of node %p (%d,%d) from [%10g, %10g] by [%10g, %10g]",
14751 (void*)node, node->depth, node->pos,
14752 node->bounds.inf, node->bounds.sup, nodebounds.inf, nodebounds.sup);
14753
14754 /* bounds in node should be valid */
14755 assert(!(node->boundstatus & SCIP_EXPRBOUNDSTATUS_CHILDRELAXED));
14756
14757 if( nodebounds.inf > node->bounds.sup || nodebounds.sup < node->bounds.inf )
14758 {
14759 *cutoff = TRUE;
14760 SCIPdebugPrintf(" -> cutoff\n");
14761 return;
14762 }
14763
14764 /* if minstrength is negative, always mark that node has recently tightened bounds,
14765 * if bounds are considerably improved or tightening leads to an empty interval,
14766 * mark that node has recently tightened bounds
14767 * if bounds are only slightly improved, set the status to tightened by parent,
14768 * so next propagateVarBound round will reset the bounds
14769 */
14770 if( minstrength < 0.0 )
14771 node->boundstatus |= SCIP_EXPRBOUNDSTATUS_TIGHTENEDBYPARENTFORCE;
14772 else if(
14773 isLbBetter(minstrength, nodebounds.inf, node->bounds.inf, node->bounds.sup) ||
14774 isUbBetter(minstrength, nodebounds.sup, node->bounds.inf, node->bounds.sup) )
14775 node->boundstatus |= SCIP_EXPRBOUNDSTATUS_TIGHTENEDBYPARENTRECENT;
14776 else if( nodebounds.inf > node->bounds.inf || nodebounds.sup < node->bounds.sup )
14777 node->boundstatus |= SCIP_EXPRBOUNDSTATUS_TIGHTENEDBYPARENT;
14778
14779 SCIPintervalIntersect(&node->bounds, node->bounds, nodebounds);
14780 SCIPdebugPrintf(" -> [%10g, %10g] status %d\n", node->bounds.inf, node->bounds.sup, node->boundstatus);
14781 }
14782
14783 /** ensures that bounds and curvature information in a node is uptodate
14784 *
14785 * Assumes that bounds and curvature in children are uptodate.
14786 */
SCIPexprgraphUpdateNodeBoundsCurvature(SCIP_EXPRGRAPHNODE * node,SCIP_Real infinity,SCIP_Real minstrength,SCIP_Bool clearreverseprop)14787 SCIP_RETCODE SCIPexprgraphUpdateNodeBoundsCurvature(
14788 SCIP_EXPRGRAPHNODE* node, /**< expression graph node */
14789 SCIP_Real infinity, /**< value for infinity in interval arithmetics */
14790 SCIP_Real minstrength, /**< minimal required relative bound strengthening to trigger a bound recalculation in parent nodes */
14791 SCIP_Bool clearreverseprop /**< whether to reset bound tightenings from reverse propagation */
14792 )
14793 {
14794 SCIP_INTERVAL childboundsstatic[SCIP_EXPRESSION_MAXCHILDEST];
14795 SCIP_EXPRCURV childcurvstatic[SCIP_EXPRESSION_MAXCHILDEST];
14796 SCIP_INTERVAL* childbounds = NULL;
14797 SCIP_EXPRCURV* childcurv = NULL;
14798 SCIP_RETCODE retcode = SCIP_OKAY;
14799 int i;
14800
14801 assert(node != NULL);
14802 assert(node->depth >= 0); /* node should be in graph */
14803 assert(node->pos >= 0); /* node should be in graph */
14804 assert(node->enabled); /* node should be enabled, otherwise we may not have uptodate bounds and curvatures in children */
14805
14806 if( node->depth == 0 )
14807 {
14808 /* we cannot update bound tightenings in variable nodes here */
14809 assert(!clearreverseprop || !(node->boundstatus & SCIP_EXPRBOUNDSTATUS_TIGHTENEDBYPARENT));
14810 return SCIP_OKAY;
14811 }
14812
14813 assert(node->op != SCIP_EXPR_VARIDX);
14814 assert(node->op != SCIP_EXPR_PARAM);
14815
14816 /* if many children, get large enough memory to store children bounds */
14817 if( node->nchildren > SCIP_EXPRESSION_MAXCHILDEST )
14818 {
14819 SCIP_ALLOC( BMSallocMemoryArray(&childbounds, node->nchildren) );
14820 SCIP_ALLOC_TERMINATE(retcode, BMSallocMemoryArray(&childcurv, node->nchildren), TERMINATE);
14821 }
14822 else
14823 {
14824 childbounds = childboundsstatic;
14825 childcurv = childcurvstatic;
14826 }
14827
14828 /* assemble bounds and curvature of children */
14829 for( i = 0; i < node->nchildren; ++i )
14830 {
14831 /* child should have valid and non-empty bounds */
14832 assert(!(node->children[i]->boundstatus & SCIP_EXPRBOUNDSTATUS_CHILDRELAXED));
14833 assert(!SCIPintervalIsEmpty(infinity, node->children[i]->bounds));
14834 /* nodes at depth 0 are always linear */
14835 assert(node->children[i]->depth > 0 || node->children[i]->curv == SCIP_EXPRCURV_LINEAR);
14836
14837 childbounds[i] = node->children[i]->bounds; /*lint !e644*/
14838 childcurv[i] = node->children[i]->curv; /*lint !e644*/
14839 }
14840
14841 /* if we do not have valid bounds, then update
14842 * code below is copied from exprgraphNodeUpdateBounds */
14843 if( node->boundstatus != SCIP_EXPRBOUNDSTATUS_VALID )
14844 {
14845 SCIP_INTERVAL newbounds;
14846
14847 /* calling interval evaluation function for this operand */
14848 assert( exprOpTable[node->op].inteval != NULL );
14849 SCIP_CALL_TERMINATE( retcode, exprOpTable[node->op].inteval(infinity, node->data, node->nchildren, childbounds, NULL, NULL, &newbounds), TERMINATE );
14850
14851 /* if bounds of a children were relaxed or our bounds were tightened by a (now possibly invalid) reverse propagation from a parent
14852 * and now our bounds are relaxed, then we have to propagate this upwards to ensure valid bounds
14853 *
14854 * if bounds were tightened (considerably), then tell this to those parents which think that they have valid bounds
14855 *
14856 * finally, if there was only a little tightening, then keep this updated bounds, but don't notify parents
14857 */
14858 if( (newbounds.inf < node->bounds.inf || newbounds.sup > node->bounds.sup) &&
14859 ((node->boundstatus & SCIP_EXPRBOUNDSTATUS_CHILDRELAXED) || ((node->boundstatus & SCIP_EXPRBOUNDSTATUS_TIGHTENEDBYPARENT) && clearreverseprop)) )
14860 {
14861 for( i = 0; i < node->nparents; ++i )
14862 node->parents[i]->boundstatus = SCIP_EXPRBOUNDSTATUS_CHILDRELAXED;
14863
14864 node->bounds = newbounds;
14865 }
14866 else if( isLbBetter(minstrength, newbounds.inf, node->bounds.inf, node->bounds.sup) ||
14867 ( isUbBetter(minstrength, newbounds.sup, node->bounds.inf, node->bounds.sup)) )
14868 {
14869 for( i = 0; i < node->nparents; ++i )
14870 node->parents[i]->boundstatus |= SCIP_EXPRBOUNDSTATUS_CHILDTIGHTENED;
14871
14872 node->bounds = newbounds;
14873 }
14874 else
14875 {
14876 SCIPintervalIntersect(&node->bounds, node->bounds, newbounds);
14877 }
14878
14879 SCIPdebugMessage("updated bounds of node %p (%d,%d) op %s to [%g,%g]\n", (void*)node, node->depth, node->pos, SCIPexpropGetName(node->op), node->bounds.inf, node->bounds.sup);
14880
14881 /* node now has valid bounds */
14882 node->boundstatus = SCIP_EXPRBOUNDSTATUS_VALID;
14883 }
14884
14885 /* update curvature */
14886 if( SCIPintervalIsEmpty(infinity, node->bounds) )
14887 {
14888 node->curv = SCIP_EXPRCURV_LINEAR;
14889
14890 SCIPdebugMessage("node %p(%d,%d) has empty domain in SCIPexprgraphUpdateNodeBoundsCurvature\n", (void*)node, node->depth, node->pos);
14891 }
14892 else
14893 {
14894 SCIP_CALL_TERMINATE( retcode, exprOpTable[node->op].curv(infinity, node->data, node->nchildren, childbounds, childcurv, &node->curv), TERMINATE );
14895
14896 /* SCIPdebugMessage("curvature %s for %s = ", SCIPexprcurvGetName(node->curv), SCIPexpropGetName(node->op));
14897 * SCIPdebug( exprgraphPrintNodeExpression(node, NULL, NULL, TRUE) );
14898 * SCIPdebugPrintf("\n");
14899 */
14900 }
14901 TERMINATE:
14902 /* free memory, if allocated before */
14903 if( childbounds != childboundsstatic )
14904 {
14905 BMSfreeMemoryArrayNull(&childbounds);
14906 BMSfreeMemoryArrayNull(&childcurv);
14907 }
14908
14909 return retcode;
14910 }
14911
14912 /**@} */
14913
14914 /**@name Expression graph methods */
14915 /**@{ */
14916
14917 /* In debug mode, the following methods are implemented as function calls to ensure
14918 * type validity.
14919 * In optimized mode, the methods are implemented as defines to improve performance.
14920 * However, we want to have them in the library anyways, so we have to undef the defines.
14921 */
14922
14923 #undef SCIPexprgraphGetDepth
14924 #undef SCIPexprgraphGetNNodes
14925 #undef SCIPexprgraphGetNodes
14926 #undef SCIPexprgraphGetNVars
14927 #undef SCIPexprgraphGetVars
14928 #undef SCIPexprgraphGetVarNodes
14929 #undef SCIPexprgraphSetVarNodeValue
14930 #undef SCIPexprgraphSetVarsBounds
14931 #undef SCIPexprgraphSetVarBounds
14932 #undef SCIPexprgraphSetVarNodeBounds
14933 #undef SCIPexprgraphSetVarNodeLb
14934 #undef SCIPexprgraphSetVarNodeUb
14935 #undef SCIPexprgraphGetVarsBounds
14936
14937 /** get current maximal depth of expression graph */
SCIPexprgraphGetDepth(SCIP_EXPRGRAPH * exprgraph)14938 int SCIPexprgraphGetDepth(
14939 SCIP_EXPRGRAPH* exprgraph /**< expression graph */
14940 )
14941 {
14942 assert(exprgraph != NULL);
14943
14944 return exprgraph->depth;
14945 }
14946
14947 /** gets array with number of nodes at each depth of expression graph */
SCIPexprgraphGetNNodes(SCIP_EXPRGRAPH * exprgraph)14948 int* SCIPexprgraphGetNNodes(
14949 SCIP_EXPRGRAPH* exprgraph /**< expression graph */
14950 )
14951 {
14952 assert(exprgraph != NULL);
14953
14954 return exprgraph->nnodes;
14955 }
14956
14957 /** gets nodes of expression graph, one array per depth */
SCIPexprgraphGetNodes(SCIP_EXPRGRAPH * exprgraph)14958 SCIP_EXPRGRAPHNODE*** SCIPexprgraphGetNodes(
14959 SCIP_EXPRGRAPH* exprgraph /**< expression graph */
14960 )
14961 {
14962 assert(exprgraph != NULL);
14963
14964 return exprgraph->nodes;
14965 }
14966
14967 /** gets number of variables in expression graph */
SCIPexprgraphGetNVars(SCIP_EXPRGRAPH * exprgraph)14968 int SCIPexprgraphGetNVars(
14969 SCIP_EXPRGRAPH* exprgraph /**< pointer to expression graph that should be freed */
14970 )
14971 {
14972 assert(exprgraph != NULL);
14973
14974 return exprgraph->nvars;
14975 }
14976
14977 /** gets array of variables in expression graph */
SCIPexprgraphGetVars(SCIP_EXPRGRAPH * exprgraph)14978 void** SCIPexprgraphGetVars(
14979 SCIP_EXPRGRAPH* exprgraph /**< pointer to expression graph that should be freed */
14980 )
14981 {
14982 assert(exprgraph != NULL);
14983
14984 return exprgraph->vars;
14985 }
14986
14987 /** gets array of expression graph nodes corresponding to variables */
SCIPexprgraphGetVarNodes(SCIP_EXPRGRAPH * exprgraph)14988 SCIP_EXPRGRAPHNODE** SCIPexprgraphGetVarNodes(
14989 SCIP_EXPRGRAPH* exprgraph /**< pointer to expression graph that should be freed */
14990 )
14991 {
14992 assert(exprgraph != NULL);
14993
14994 return exprgraph->varnodes;
14995 }
14996
14997 /** sets value for a single variable given as expression graph node */
SCIPexprgraphSetVarNodeValue(SCIP_EXPRGRAPHNODE * varnode,SCIP_Real value)14998 void SCIPexprgraphSetVarNodeValue(
14999 SCIP_EXPRGRAPHNODE* varnode, /**< expression graph node corresponding to variable */
15000 SCIP_Real value /**< new value for variable */
15001 )
15002 {
15003 assert(varnode != NULL);
15004 assert(varnode->op == SCIP_EXPR_VARIDX);
15005
15006 varnode->value = value;
15007 }
15008
15009 /** sets bounds for variables */
SCIPexprgraphSetVarsBounds(SCIP_EXPRGRAPH * exprgraph,SCIP_INTERVAL * varbounds)15010 void SCIPexprgraphSetVarsBounds(
15011 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
15012 SCIP_INTERVAL* varbounds /**< new bounds for variables */
15013 )
15014 {
15015 assert(exprgraph != NULL);
15016 assert(varbounds != NULL || exprgraph->nvars == 0);
15017
15018 BMScopyMemoryArray(exprgraph->varbounds, varbounds, exprgraph->nvars);
15019 }
15020
15021 /** sets bounds for a single variable */
SCIPexprgraphSetVarBounds(SCIP_EXPRGRAPH * exprgraph,void * var,SCIP_INTERVAL varbounds)15022 void SCIPexprgraphSetVarBounds(
15023 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
15024 void* var, /**< variable */
15025 SCIP_INTERVAL varbounds /**< new bounds of variable */
15026 )
15027 {
15028 int pos;
15029
15030 assert(exprgraph != NULL);
15031 assert(var != NULL);
15032 assert(SCIPhashmapExists(exprgraph->varidxs, var));
15033
15034 pos = SCIPhashmapGetImageInt(exprgraph->varidxs, var);
15035 assert(pos < exprgraph->nvars);
15036 assert(exprgraph->vars[pos] == var);
15037
15038 exprgraph->varbounds[pos] = varbounds;
15039 }
15040
15041 /** sets bounds for a single variable given as expression graph node */
SCIPexprgraphSetVarNodeBounds(SCIP_EXPRGRAPH * exprgraph,SCIP_EXPRGRAPHNODE * varnode,SCIP_INTERVAL varbounds)15042 void SCIPexprgraphSetVarNodeBounds(
15043 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
15044 SCIP_EXPRGRAPHNODE* varnode, /**< expression graph node corresponding to variable */
15045 SCIP_INTERVAL varbounds /**< new bounds of variable */
15046 )
15047 {
15048 int pos;
15049
15050 assert(exprgraph != NULL);
15051 assert(varnode != NULL);
15052
15053 pos = varnode->data.intval;
15054 assert(pos >= 0);
15055 assert(pos < exprgraph->nvars);
15056 assert(exprgraph->varnodes[pos] == varnode);
15057
15058 exprgraph->varbounds[pos] = varbounds;
15059 }
15060
15061 /** sets lower bound for a single variable given as expression graph node */
SCIPexprgraphSetVarNodeLb(SCIP_EXPRGRAPH * exprgraph,SCIP_EXPRGRAPHNODE * varnode,SCIP_Real lb)15062 void SCIPexprgraphSetVarNodeLb(
15063 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
15064 SCIP_EXPRGRAPHNODE* varnode, /**< expression graph node corresponding to variable */
15065 SCIP_Real lb /**< new lower bound for variable */
15066 )
15067 {
15068 int pos;
15069
15070 assert(exprgraph != NULL);
15071 assert(varnode != NULL);
15072
15073 pos = varnode->data.intval;
15074 assert(pos >= 0);
15075 assert(pos < exprgraph->nvars);
15076 assert(exprgraph->varnodes[pos] == varnode);
15077
15078 exprgraph->varbounds[pos].inf = lb;
15079 }
15080
15081 /** sets upper bound for a single variable given as expression graph node */
SCIPexprgraphSetVarNodeUb(SCIP_EXPRGRAPH * exprgraph,SCIP_EXPRGRAPHNODE * varnode,SCIP_Real ub)15082 void SCIPexprgraphSetVarNodeUb(
15083 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
15084 SCIP_EXPRGRAPHNODE* varnode, /**< expression graph node corresponding to variable */
15085 SCIP_Real ub /**< new upper bound for variable */
15086 )
15087 {
15088 int pos;
15089
15090 assert(exprgraph != NULL);
15091 assert(varnode != NULL);
15092
15093 pos = varnode->data.intval;
15094 assert(pos >= 0);
15095 assert(pos < exprgraph->nvars);
15096 assert(exprgraph->varnodes[pos] == varnode);
15097
15098 exprgraph->varbounds[pos].sup = ub;
15099 }
15100
15101 /** gets bounds that are stored for all variables */
SCIPexprgraphGetVarsBounds(SCIP_EXPRGRAPH * exprgraph)15102 SCIP_INTERVAL* SCIPexprgraphGetVarsBounds(
15103 SCIP_EXPRGRAPH* exprgraph /**< expression graph */
15104 )
15105 {
15106 return exprgraph->varbounds;
15107 }
15108
15109 /** creates an empty expression graph */
SCIPexprgraphCreate(BMS_BLKMEM * blkmem,SCIP_EXPRGRAPH ** exprgraph,int varssizeinit,int depthinit,SCIP_DECL_EXPRGRAPHVARADDED ((* exprgraphvaradded)),SCIP_DECL_EXPRGRAPHVARREMOVE ((* exprgraphvarremove)),SCIP_DECL_EXPRGRAPHVARCHGIDX ((* exprgraphvarchgidx)),void * userdata)15110 SCIP_RETCODE SCIPexprgraphCreate(
15111 BMS_BLKMEM* blkmem, /**< block memory */
15112 SCIP_EXPRGRAPH** exprgraph, /**< buffer to store pointer to expression graph */
15113 int varssizeinit, /**< minimal initial size for variables array, or -1 to choose automatically */
15114 int depthinit, /**< minimal initial depth of expression graph, or -1 to choose automatically */
15115 SCIP_DECL_EXPRGRAPHVARADDED((*exprgraphvaradded)), /**< callback method to invoke when a variable has been added to the expression graph, or NULL if not needed */
15116 SCIP_DECL_EXPRGRAPHVARREMOVE((*exprgraphvarremove)), /**< callback method to invoke when a variable will be removed from the expression graph, or NULL if not needed */
15117 SCIP_DECL_EXPRGRAPHVARCHGIDX((*exprgraphvarchgidx)), /**< callback method to invoke when a variable changes its index in the expression graph, or NULL if not needed */
15118 void* userdata /**< user data to pass to callback functions */
15119 )
15120 {
15121 assert(blkmem != NULL);
15122 assert(exprgraph != NULL);
15123
15124 SCIP_ALLOC( BMSallocBlockMemory(blkmem, exprgraph) );
15125 BMSclearMemory(*exprgraph);
15126 (*exprgraph)->blkmem = blkmem;
15127
15128 /* create nodes's arrays */
15129 SCIP_CALL( exprgraphEnsureDepth(*exprgraph, MAX(1, depthinit)) );
15130 assert((*exprgraph)->depth >= 1);
15131
15132 /* create var's arrays and hashmap */
15133 ensureBlockMemoryArraySize3((*exprgraph)->blkmem, &(*exprgraph)->varnodes, &(*exprgraph)->vars, &(*exprgraph)->varbounds, &(*exprgraph)->varssize, varssizeinit);
15134 SCIP_CALL( SCIPhashmapCreate(&(*exprgraph)->varidxs, (*exprgraph)->blkmem, (*exprgraph)->varssize) );
15135
15136 /* empty array of constants is sorted */
15137 (*exprgraph)->constssorted = TRUE;
15138
15139 /* store callback functions and user data */
15140 (*exprgraph)->exprgraphvaradded = exprgraphvaradded;
15141 (*exprgraph)->exprgraphvarremove = exprgraphvarremove;
15142 (*exprgraph)->exprgraphvarchgidx = exprgraphvarchgidx;
15143 (*exprgraph)->userdata = userdata;
15144
15145 return SCIP_OKAY;
15146 }
15147
15148 /** frees an expression graph */
SCIPexprgraphFree(SCIP_EXPRGRAPH ** exprgraph)15149 SCIP_RETCODE SCIPexprgraphFree(
15150 SCIP_EXPRGRAPH** exprgraph /**< pointer to expression graph that should be freed */
15151 )
15152 {
15153 BMS_BLKMEM* blkmem;
15154 int d;
15155
15156 assert( exprgraph != NULL);
15157 assert(*exprgraph != NULL);
15158 assert((*exprgraph)->nvars == 0);
15159 assert((*exprgraph)->nconsts == 0);
15160
15161 blkmem = (*exprgraph)->blkmem;
15162 assert(blkmem != NULL);
15163
15164 /* free nodes arrays */
15165 for( d = 0; d < (*exprgraph)->depth; ++d )
15166 {
15167 assert((*exprgraph)->nnodes[d] == 0);
15168 BMSfreeBlockMemoryArrayNull(blkmem, &(*exprgraph)->nodes[d], (*exprgraph)->nodessize[d]); /*lint !e866*/
15169 }
15170 assert((*exprgraph)->nodes != NULL);
15171 assert((*exprgraph)->nnodes != NULL);
15172 assert((*exprgraph)->nodessize != NULL);
15173 BMSfreeBlockMemoryArray(blkmem, &(*exprgraph)->nodes, (*exprgraph)->depth);
15174 BMSfreeBlockMemoryArray(blkmem, &(*exprgraph)->nnodes, (*exprgraph)->depth);
15175 BMSfreeBlockMemoryArray(blkmem, &(*exprgraph)->nodessize, (*exprgraph)->depth);
15176
15177 /* free variables arrays and hashmap */
15178 BMSfreeBlockMemoryArrayNull(blkmem, &(*exprgraph)->vars, (*exprgraph)->varssize);
15179 BMSfreeBlockMemoryArrayNull(blkmem, &(*exprgraph)->varnodes, (*exprgraph)->varssize);
15180 BMSfreeBlockMemoryArrayNull(blkmem, &(*exprgraph)->varbounds, (*exprgraph)->varssize);
15181 SCIPhashmapFree(&(*exprgraph)->varidxs);
15182
15183 /* free constants array */
15184 BMSfreeBlockMemoryArrayNull(blkmem, &(*exprgraph)->constnodes, (*exprgraph)->constssize);
15185
15186 /* free graph struct */
15187 BMSfreeBlockMemory(blkmem, exprgraph);
15188
15189 return SCIP_OKAY;
15190 }
15191
15192 /** adds an expression graph node to an expression graph
15193 *
15194 * Expression graph assumes ownership of node.
15195 * Children are notified about new parent.
15196 * Depth will be chosen to be the maximum of mindepth and the depth of all children plus one.
15197 */
SCIPexprgraphAddNode(SCIP_EXPRGRAPH * exprgraph,SCIP_EXPRGRAPHNODE * node,int mindepth,int nchildren,SCIP_EXPRGRAPHNODE ** children)15198 SCIP_RETCODE SCIPexprgraphAddNode(
15199 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
15200 SCIP_EXPRGRAPHNODE* node, /**< expression graph node to add */
15201 int mindepth, /**< minimal depth in expression graph where to add node, e.g., 0 or smaller to choose automatically */
15202 int nchildren, /**< number of children */
15203 SCIP_EXPRGRAPHNODE** children /**< children nodes, or NULL if no children */
15204 )
15205 {
15206 SCIP_Bool childvalsvalid;
15207 int depth;
15208 int i;
15209
15210 assert(exprgraph != NULL);
15211 assert(node != NULL);
15212 assert(node->pos < 0); /* node should have no position in graph yet */
15213 assert(node->depth < 0); /* node should have no position in graph yet */
15214 assert(node->nchildren == 0); /* node should not have stored children yet */
15215 assert(node->children == NULL); /* node should not have stored children yet */
15216 assert(node->nparents == 0); /* node should not have parents stored yet */
15217 assert(children != NULL || nchildren == 0);
15218
15219 /* choose depth as maximal depth of children + 1, and at least mindepth */
15220 depth = MAX(0, mindepth);
15221 for( i = 0; i < nchildren; ++i )
15222 {
15223 if( children[i]->depth >= depth ) /*lint !e613*/
15224 depth = children[i]->depth + 1; /*lint !e613*/
15225 }
15226
15227 /* ensure that expression graph is deep enough */
15228 SCIP_CALL( exprgraphEnsureDepth(exprgraph, depth+1) );
15229 assert(exprgraph->depth > depth);
15230
15231 /* ensure enough space for nodes at depth depth */
15232 ensureBlockMemoryArraySize(exprgraph->blkmem, &exprgraph->nodes[depth], &exprgraph->nodessize[depth], exprgraph->nnodes[depth]+1); /*lint !e866*/
15233
15234 /* add node to graph */
15235 node->depth = depth;
15236 node->pos = exprgraph->nnodes[depth];
15237 exprgraph->nodes[depth][node->pos] = node;
15238 ++exprgraph->nnodes[depth];
15239
15240 /* add as parent to children
15241 * and check if children has valid values */
15242 childvalsvalid = TRUE;
15243 for( i = 0; i < nchildren; ++i )
15244 {
15245 SCIP_CALL( exprgraphNodeAddParent(exprgraph->blkmem, children[i], node) ); /*lint !e613*/
15246 childvalsvalid &= (children[i]->value != SCIP_INVALID); /*lint !e777 !e514 !e613*/
15247 }
15248 /* store children */
15249 if( nchildren > 0 )
15250 {
15251 SCIP_ALLOC( BMSduplicateBlockMemoryArray(exprgraph->blkmem, &node->children, children, nchildren) );
15252 node->nchildren = nchildren;
15253 }
15254
15255 if( node->op == SCIP_EXPR_CONST )
15256 {
15257 /* set bounds to constant value of node */
15258 node->boundstatus = SCIP_EXPRBOUNDSTATUS_VALID;
15259 SCIPintervalSet(&node->bounds, node->data.dbl);
15260 }
15261 else
15262 {
15263 /* set bounds to entire, set status to "should recompute", and note that we have to update something */
15264 node->boundstatus = SCIP_EXPRBOUNDSTATUS_CHILDTIGHTENED;
15265 SCIPintervalSetEntire(SCIP_REAL_MAX, &node->bounds);
15266 exprgraph->needvarboundprop = TRUE;
15267 }
15268
15269 /* if not a variable, set value of node according to values of children (if all have valid values) */
15270 if( node->op != SCIP_EXPR_VARIDX && childvalsvalid )
15271 {
15272 SCIP_CALL( exprgraphNodeEval(node, NULL) );
15273 }
15274
15275 return SCIP_OKAY;
15276 }
15277
15278 /** adds variables to an expression graph, if not existing yet
15279 *
15280 * Also already existing nodes are enabled.
15281 */
SCIPexprgraphAddVars(SCIP_EXPRGRAPH * exprgraph,int nvars,void ** vars,SCIP_EXPRGRAPHNODE ** varnodes)15282 SCIP_RETCODE SCIPexprgraphAddVars(
15283 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
15284 int nvars, /**< number of variables to add */
15285 void** vars, /**< variables to add */
15286 SCIP_EXPRGRAPHNODE** varnodes /**< array to store nodes corresponding to variables, or NULL if not of interest */
15287 )
15288 {
15289 SCIP_EXPRGRAPHNODE* node;
15290 SCIP_EXPROPDATA opdata;
15291 int i;
15292
15293 assert(exprgraph != NULL);
15294 assert(exprgraph->depth >= 1);
15295 assert(vars != NULL || nvars == 0);
15296
15297 /* if there are no variables yet, then it's quite likely that we will create new nodes for all vars, so can easily estimate how much space we will need in variables array and nodes at depth 0 arrays */
15298 if( exprgraph->nvars == 0 )
15299 {
15300 ensureBlockMemoryArraySize3(exprgraph->blkmem, &exprgraph->vars, &exprgraph->varnodes, &exprgraph->varbounds, &exprgraph->varssize, exprgraph->nvars + nvars);
15301 ensureBlockMemoryArraySize(exprgraph->blkmem, &exprgraph->nodes[0], &exprgraph->nodessize[0], exprgraph->nnodes[0] + nvars);
15302 }
15303
15304 for( i = 0; i < nvars; ++i )
15305 {
15306 /* skip variables that exist already */
15307 if( SCIPhashmapExists(exprgraph->varidxs, vars[i]) )/*lint !e613*/
15308 {
15309 (void) SCIPexprgraphFindVarNode(exprgraph, vars[i], &node); /*lint !e613*/
15310 assert(node != NULL);
15311
15312 /* enable node */
15313 node->enabled = TRUE;
15314
15315 if( varnodes != NULL )
15316 varnodes[i] = node;
15317
15318 continue;
15319 }
15320
15321 /* create new variable expression */
15322 opdata.intval = exprgraph->nvars;
15323 SCIP_CALL( exprgraphCreateNode(exprgraph->blkmem, &node, SCIP_EXPR_VARIDX, opdata) );
15324
15325 /* add expression node to expression graph at depth 0 */
15326 SCIP_CALL( SCIPexprgraphAddNode(exprgraph, node, 0, 0, NULL) );
15327
15328 /* add variable node to vars arrays and hashmap */
15329 ensureBlockMemoryArraySize3(exprgraph->blkmem, &exprgraph->vars, &exprgraph->varnodes, &exprgraph->varbounds, &exprgraph->varssize, exprgraph->nvars + 1);
15330 exprgraph->vars[exprgraph->nvars] = vars[i]; /*lint !e613*/
15331 exprgraph->varnodes[exprgraph->nvars] = node;
15332 SCIPintervalSetEntire(SCIP_REAL_MAX, &exprgraph->varbounds[exprgraph->nvars]);
15333 SCIP_CALL( SCIPhashmapInsertInt(exprgraph->varidxs, vars[i], exprgraph->nvars) ); /*lint !e613*/
15334 ++exprgraph->nvars;
15335
15336 if( varnodes != NULL )
15337 varnodes[i] = node;
15338
15339 SCIPdebugMessage("added node %p (%d, %d) for new variable %d\n", (void*)node, node->depth, node->pos, node->data.intval);
15340
15341 /* call callback method, if set */
15342 if( exprgraph->exprgraphvaradded != NULL )
15343 {
15344 SCIP_CALL( exprgraph->exprgraphvaradded(exprgraph, exprgraph->userdata, vars[i], node) ); /*lint !e613*/
15345 }
15346 }
15347
15348 return SCIP_OKAY;
15349 }
15350
15351 /** adds a constant to an expression graph, if not existing yet
15352 *
15353 * Also already existing nodes are enabled.
15354 */
SCIPexprgraphAddConst(SCIP_EXPRGRAPH * exprgraph,SCIP_Real constant,SCIP_EXPRGRAPHNODE ** constnode)15355 SCIP_RETCODE SCIPexprgraphAddConst(
15356 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
15357 SCIP_Real constant, /**< constant to add */
15358 SCIP_EXPRGRAPHNODE** constnode /**< buffer to store pointer to expression graph node corresponding to constant */
15359 )
15360 {
15361 SCIP_EXPROPDATA opdata;
15362
15363 assert(exprgraph != NULL);
15364 assert(constnode != NULL);
15365
15366 /* check if there is already an expression for this constant */
15367 if( SCIPexprgraphFindConstNode(exprgraph, constant, constnode) )
15368 {
15369 assert(*constnode != NULL);
15370 assert((*constnode)->op == SCIP_EXPR_CONST);
15371 assert((*constnode)->data.dbl == constant); /*lint !e777*/
15372 (*constnode)->enabled = TRUE;
15373 return SCIP_OKAY;
15374 }
15375
15376 /* create new node for constant */
15377 opdata.dbl = constant;
15378 SCIP_CALL( exprgraphCreateNode(exprgraph->blkmem, constnode, SCIP_EXPR_CONST, opdata) );
15379
15380 /* add node to expression graph at depth 0 */
15381 SCIP_CALL( SCIPexprgraphAddNode(exprgraph, *constnode, 0, 0, NULL) );
15382 assert((*constnode)->depth == 0);
15383 assert((*constnode)->pos == exprgraph->nnodes[0]-1);
15384
15385 /* add node to constnodes arrays; @todo should use SCIPsortedvecInsertPtr? */
15386 ensureBlockMemoryArraySize(exprgraph->blkmem, &exprgraph->constnodes, &exprgraph->constssize, exprgraph->nconsts + 1);
15387 exprgraph->constnodes[exprgraph->nconsts] = *constnode;
15388 ++exprgraph->nconsts;
15389 exprgraph->constssorted = exprgraph->nconsts <= 1 || (exprgraph->constssorted && exprgraphConstNodeComp(exprgraph->constnodes[exprgraph->nconsts-2], *constnode) < 0);
15390
15391 SCIPdebugMessage("added node %p (%d, %d) for new constant %g\n", (void*)constnode, (*constnode)->depth, (*constnode)->pos, (*constnode)->data.dbl);
15392
15393 return SCIP_OKAY;
15394 }
15395
15396 /** adds sum of expression trees into expression graph
15397 *
15398 * node will also be captured.
15399 *
15400 * @note Parameters will be converted into constants
15401 */
SCIPexprgraphAddExprtreeSum(SCIP_EXPRGRAPH * exprgraph,int nexprtrees,SCIP_EXPRTREE ** exprtrees,SCIP_Real * coefs,SCIP_EXPRGRAPHNODE ** rootnode,SCIP_Bool * rootnodeisnew)15402 SCIP_RETCODE SCIPexprgraphAddExprtreeSum(
15403 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
15404 int nexprtrees, /**< number of expression trees to add */
15405 SCIP_EXPRTREE** exprtrees, /**< expression trees that should be added */
15406 SCIP_Real* coefs, /**< coefficients of expression trees, or NULL if all 1.0 */
15407 SCIP_EXPRGRAPHNODE** rootnode, /**< buffer to store expression graph node corresponding to root of expression tree */
15408 SCIP_Bool* rootnodeisnew /**< buffer to indicate whether the node in *rootnode has been newly created for this expression tree (otherwise, expression tree was already in graph) */
15409 )
15410 {
15411 SCIP_Bool allone;
15412
15413 assert(exprgraph != NULL);
15414 assert(nexprtrees > 0);
15415 assert(exprtrees != NULL);
15416 assert(rootnode != NULL);
15417 assert(rootnodeisnew != NULL);
15418
15419 *rootnode = NULL;
15420
15421 if( nexprtrees == 1 && (coefs == NULL || coefs[0] == 1.0) )
15422 {
15423 assert(exprtrees[0] != NULL);
15424 assert(exprtrees[0]->vars != NULL || exprtrees[0]->nvars == 0);
15425
15426 /* coverity[var_deref_model] */
15427 SCIP_CALL( exprgraphAddExpr(exprgraph, exprtrees[0]->root, exprtrees[0]->vars, exprtrees[0]->params, rootnode, rootnodeisnew) );
15428 }
15429 else
15430 {
15431 SCIP_EXPROP op;
15432 SCIP_EXPRGRAPHNODE** rootnodes;
15433 SCIP_Bool rootnodeisnew_;
15434 int i;
15435
15436 *rootnodeisnew = TRUE;
15437 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &rootnodes, nexprtrees) );
15438
15439 allone = TRUE;
15440 for( i = 0; i < nexprtrees; ++i )
15441 {
15442 assert(exprtrees[i] != NULL);
15443 assert(exprtrees[i]->vars != NULL || exprtrees[i]->nvars == 0);
15444
15445 SCIP_CALL( exprgraphAddExpr(exprgraph, exprtrees[i]->root, exprtrees[i]->vars, exprtrees[i]->params, &rootnodes[i], &rootnodeisnew_) ); /*lint !e644*/
15446 assert(rootnodes[i] != NULL);
15447 *rootnodeisnew &= rootnodeisnew_;
15448
15449 allone &= (coefs == NULL || coefs[i] == 1.0); /*lint !e514*/
15450 }
15451
15452 /* decide which operand we want to use for the root node */
15453 if( coefs == NULL || allone )
15454 op = nexprtrees == 2 ? SCIP_EXPR_PLUS : SCIP_EXPR_SUM;
15455 else if( nexprtrees == 2 && coefs[0] == 1.0 && coefs[1] == -1.0 )
15456 op = SCIP_EXPR_MINUS;
15457 else if( nexprtrees == 2 && coefs[1] == 1.0 && coefs[0] == -1.0 )
15458 {
15459 SCIP_EXPRGRAPHNODE* tmp;
15460
15461 tmp = rootnodes[0];
15462 rootnodes[0] = rootnodes[1];
15463 rootnodes[1] = tmp;
15464 op = SCIP_EXPR_MINUS;
15465 }
15466 else
15467 op = SCIP_EXPR_LINEAR;
15468
15469 if( op != SCIP_EXPR_LINEAR )
15470 {
15471 SCIP_EXPROPDATA data;
15472 data.data = NULL;
15473
15474 if( !*rootnodeisnew )
15475 {
15476 /* all exprtrees were in the graph already, check if we also already have a node for the sum of rootnodes */
15477 SCIP_CALL( exprgraphFindParentByOperator(exprgraph, nexprtrees, rootnodes, op, data, NULL, rootnode) );
15478 }
15479
15480 if( *rootnode == NULL )
15481 {
15482 /* create new node for sum of rootnodes and add to exprgraph */
15483 SCIP_CALL( exprgraphCreateNode(exprgraph->blkmem, rootnode, op, data) );
15484 SCIP_CALL( SCIPexprgraphAddNode(exprgraph, *rootnode, -1, nexprtrees, rootnodes) );
15485 *rootnodeisnew = TRUE;
15486 }
15487 else
15488 {
15489 /* apparently we already had a node for the sum of rootnodes, so signal this to the caller */
15490 *rootnodeisnew = FALSE;
15491 }
15492 }
15493 else
15494 {
15495 SCIP_EXPROPDATA data;
15496 SCIP_Real* lindata;
15497
15498 assert(op == SCIP_EXPR_LINEAR);
15499
15500 /* setup data for linear expression: copy of coefficients and 0.0 as constant term */
15501 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &lindata, nexprtrees+1) );
15502 BMScopyMemoryArray(lindata, coefs, nexprtrees); /*lint !e644*/
15503 lindata[nexprtrees] = 0.0;
15504 data.data = lindata;
15505
15506 if( !*rootnodeisnew )
15507 {
15508 /* all exprtrees were in the graph already, check if we also already have a node for the linear combination of rootnodes */
15509 SCIP_CALL( exprgraphFindParentByOperator(exprgraph, nexprtrees, rootnodes, SCIP_EXPR_LINEAR, data, NULL, rootnode) );
15510 }
15511
15512 if( *rootnode == NULL )
15513 {
15514 /* create new node for linear combination of rootnodes and add to exprgraph */
15515 SCIP_CALL( exprgraphCreateNode(exprgraph->blkmem, rootnode, SCIP_EXPR_LINEAR, data) );
15516 SCIP_CALL( SCIPexprgraphAddNode(exprgraph, *rootnode, -1, nexprtrees, rootnodes) );
15517 *rootnodeisnew = TRUE;
15518 }
15519 else
15520 {
15521 /* apparently we already had a node for the sum of rootnodes, so signal this to the caller */
15522 *rootnodeisnew = FALSE;
15523 BMSfreeBlockMemoryArray(exprgraph->blkmem, &lindata, nexprtrees+1);
15524 }
15525 }
15526
15527 BMSfreeBlockMemoryArray(exprgraph->blkmem, &rootnodes, nexprtrees);
15528 }
15529 assert(*rootnode != NULL);
15530
15531 SCIPexprgraphCaptureNode(*rootnode);
15532
15533 SCIPdebugMessage("%s node %p (%d,%d) is root for %d expression trees\n",
15534 rootnodeisnew ? "new" : "old", (void*)*rootnode, (*rootnode)->depth, (*rootnode)->pos, nexprtrees);
15535
15536 return SCIP_OKAY;
15537 }
15538
15539 /** replaces variable in expression graph by a linear sum of variables
15540 *
15541 * Variables will be added if not in the graph yet.
15542 */
SCIPexprgraphReplaceVarByLinearSum(SCIP_EXPRGRAPH * exprgraph,void * var,int ncoefs,SCIP_Real * coefs,void ** vars,SCIP_Real constant)15543 SCIP_RETCODE SCIPexprgraphReplaceVarByLinearSum(
15544 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
15545 void* var, /**< variable to replace */
15546 int ncoefs, /**< number of coefficients in linear term */
15547 SCIP_Real* coefs, /**< coefficients in linear term, or NULL if ncoefs == 0 */
15548 void** vars, /**< variables in linear term */
15549 SCIP_Real constant /**< constant offset */
15550 )
15551 {
15552 SCIP_EXPRGRAPHNODE* varnode;
15553 SCIP_Real* lindata;
15554 int varidx;
15555 int i;
15556
15557 assert(exprgraph != NULL);
15558 assert(var != NULL);
15559 assert(SCIPhashmapExists(exprgraph->varidxs, var));
15560 assert(coefs != NULL || ncoefs == 0);
15561 assert(vars != NULL || ncoefs == 0);
15562
15563 varidx = SCIPhashmapGetImageInt(exprgraph->varidxs, var);
15564 assert(varidx < exprgraph->nvars);
15565 assert(exprgraph->vars[varidx] == var);
15566 varnode = exprgraph->varnodes[varidx];
15567 assert(varnode != NULL);
15568 assert(varnode->data.intval == varidx);
15569
15570 if( ncoefs == 0 || (ncoefs == 1 && constant == 0.0 && coefs[0] == 1.0) ) /*lint !e613*/
15571 {
15572 /* variable is replaced by constant or variable */
15573 SCIP_EXPRGRAPHNODE* node;
15574
15575 /* check if there is already a node for this constant or variable */
15576 node = NULL;
15577 if( ncoefs == 0 )
15578 {
15579 (void)SCIPexprgraphFindConstNode(exprgraph, constant, &node);
15580 assert(node == NULL || node->data.dbl == constant); /*lint !e777*/
15581 }
15582 else
15583 {
15584 (void)SCIPexprgraphFindVarNode(exprgraph, vars[0], &node); /*lint !e613*/
15585 assert(node == NULL || exprgraph->vars[node->data.intval] == vars[0]); /*lint !e613*/
15586 }
15587
15588 if( node != NULL )
15589 {
15590 SCIPdebugMessage("try to replace varnode %p (%d uses, %d parents) by %p\n", (void*)varnode, varnode->nuses, varnode->nparents, (void*)node);
15591
15592 /* tell parents of varnode to replace child varnode by node, this may free varnode */
15593 SCIP_CALL( SCIPexprgraphMoveNodeParents(exprgraph, &varnode, node) );
15594
15595 /* if varnode is in use, turn it into SCIP_EXPR_SUM with node as only child */
15596 if( varnode != NULL )
15597 {
15598 assert(varnode->nuses > 0);
15599 assert(varnode->nparents == 0);
15600
15601 /* remove variable (but don't free it's node) from graph */
15602 SCIP_CALL( exprgraphRemoveVar(exprgraph, varidx) );
15603
15604 /* move varnode up to depth 1 */
15605 SCIP_CALL( exprgraphMoveNode(exprgraph, varnode, 1) );
15606
15607 /* turn into EXPR_SUM expression */
15608 varnode->op = SCIP_EXPR_SUM;
15609 varnode->data.data = NULL;
15610 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &varnode->children, 1) ); /*lint !e506*/
15611 varnode->children[0] = node;
15612 varnode->nchildren = 1;
15613 SCIP_CALL( exprgraphNodeAddParent(exprgraph->blkmem, node, varnode) );
15614
15615 varnode->value = node->value;
15616 varnode->bounds = node->bounds;
15617 varnode->boundstatus = (node->boundstatus == SCIP_EXPRBOUNDSTATUS_VALID) ? SCIP_EXPRBOUNDSTATUS_VALID : SCIP_EXPRBOUNDSTATUS_CHILDRELAXED;
15618 }
15619 }
15620 else if( ncoefs == 0 )
15621 {
15622 /* turn node into EXPR_CONST node */
15623
15624 /* remove variable (but don't free it's node) from graph */
15625 SCIP_CALL( exprgraphRemoveVar(exprgraph, varidx) );
15626
15627 /* convert into EXPR_CONST node */
15628 varnode->op = SCIP_EXPR_CONST;
15629 varnode->data.dbl = constant;
15630
15631 varnode->value = constant;
15632 SCIPintervalSet(&varnode->bounds, constant);
15633 varnode->boundstatus = SCIP_EXPRBOUNDSTATUS_VALID;
15634
15635 /* add to constnodes arrays; @todo should use SCIPsortedvecInsertPtr? */
15636 ensureBlockMemoryArraySize(exprgraph->blkmem, &exprgraph->constnodes, &exprgraph->constssize, exprgraph->nconsts + 1);
15637 exprgraph->constnodes[exprgraph->nconsts] = varnode;
15638 ++exprgraph->nconsts;
15639 exprgraph->constssorted = exprgraph->nconsts <= 1 || (exprgraph->constssorted && exprgraphConstNodeComp(exprgraph->constnodes[exprgraph->nconsts-2], varnode) < 0);
15640 }
15641 else
15642 {
15643 /* turn node into EXPR_VARIDX node for new variable */
15644
15645 /* remove variable (but don't free it's node) from graph */
15646 SCIP_CALL( exprgraphRemoveVar(exprgraph, varidx) );
15647
15648 varnode->data.intval = exprgraph->nvars;
15649
15650 /* add variable node to vars arrays and hashmap */
15651 ensureBlockMemoryArraySize3(exprgraph->blkmem, &exprgraph->vars, &exprgraph->varnodes, &exprgraph->varbounds, &exprgraph->varssize, exprgraph->nvars + 1);
15652 exprgraph->vars[exprgraph->nvars] = vars[0]; /*lint !e613*/
15653 exprgraph->varnodes[exprgraph->nvars] = varnode;
15654 SCIPintervalSetEntire(SCIP_REAL_MAX, &exprgraph->varbounds[exprgraph->nvars]);
15655 SCIP_CALL( SCIPhashmapInsertInt(exprgraph->varidxs, vars[0], exprgraph->nvars) ); /*lint !e613*/
15656 ++exprgraph->nvars;
15657
15658 /* call callback method, if set */
15659 if( exprgraph->exprgraphvaradded != NULL )
15660 {
15661 SCIP_CALL( exprgraph->exprgraphvaradded(exprgraph, exprgraph->userdata, vars[0], varnode) ); /*lint !e613*/
15662 }
15663 }
15664
15665 /* mark varnode and its parents as not simplified */
15666 if( varnode != NULL )
15667 {
15668 varnode->simplified = FALSE;
15669 for( i = 0; i < varnode->nparents; ++i )
15670 varnode->parents[i]->simplified = FALSE;
15671 }
15672
15673 return SCIP_OKAY;
15674 }
15675
15676 /* turn varnode into EXPR_LINEAR */
15677
15678 /* remove variable (but don't free it's node) from graph */
15679 SCIP_CALL( exprgraphRemoveVar(exprgraph, varidx) );
15680
15681 /* move varnode up to depth 1 */
15682 SCIP_CALL( exprgraphMoveNode(exprgraph, varnode, 1) );
15683
15684 /* convert into EXPR_LINEAR node */
15685 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &lindata, ncoefs + 1) );
15686 BMScopyMemoryArray(lindata, coefs, ncoefs); /*lint !e644*/
15687 lindata[ncoefs] = constant;
15688 varnode->data.data = (void*)lindata;
15689 varnode->op = SCIP_EXPR_LINEAR;
15690
15691 /* add nodes corresponding to vars to expression graph, if not existing yet */
15692 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &varnode->children, ncoefs) );
15693 SCIP_CALL( SCIPexprgraphAddVars(exprgraph, ncoefs, vars, varnode->children) );
15694 varnode->nchildren = ncoefs;
15695
15696 /* notify vars about new parent varnode */
15697 for( i = 0; i < ncoefs; ++i )
15698 {
15699 SCIP_CALL( exprgraphNodeAddParent(exprgraph->blkmem, varnode->children[i], varnode) );
15700 }
15701
15702 /* set value and bounds to invalid, curvature can remain (still linear) */
15703 varnode->value = SCIP_INVALID;
15704 varnode->boundstatus = SCIP_EXPRBOUNDSTATUS_CHILDRELAXED;
15705
15706 /* mark varnode and its parents as not simplified */
15707 varnode->simplified = FALSE;
15708 for( i = 0; i < varnode->nparents; ++i )
15709 varnode->parents[i]->simplified = FALSE;
15710
15711 return SCIP_OKAY;
15712 }
15713
15714 /** finds expression graph node corresponding to a variable */
SCIPexprgraphFindVarNode(SCIP_EXPRGRAPH * exprgraph,void * var,SCIP_EXPRGRAPHNODE ** varnode)15715 SCIP_Bool SCIPexprgraphFindVarNode(
15716 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
15717 void* var, /**< variable to search for */
15718 SCIP_EXPRGRAPHNODE** varnode /**< buffer to store node corresponding to variable, if found, or NULL if not found */
15719 )
15720 {
15721 int pos;
15722
15723 assert(exprgraph != NULL);
15724 assert(var != NULL);
15725 assert(varnode != NULL);
15726
15727 if( !SCIPhashmapExists(exprgraph->varidxs, var) )
15728 {
15729 *varnode = NULL;
15730 return FALSE;
15731 }
15732
15733 pos = SCIPhashmapGetImageInt(exprgraph->varidxs, var);
15734 assert(pos < exprgraph->nvars);
15735
15736 *varnode = exprgraph->varnodes[pos];
15737 assert(*varnode != NULL);
15738 assert((*varnode)->op == SCIP_EXPR_VARIDX);
15739
15740 return TRUE;
15741 }
15742
15743 /** finds expression graph node corresponding to a constant */
SCIPexprgraphFindConstNode(SCIP_EXPRGRAPH * exprgraph,SCIP_Real constant,SCIP_EXPRGRAPHNODE ** constnode)15744 SCIP_Bool SCIPexprgraphFindConstNode(
15745 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
15746 SCIP_Real constant, /**< constant to search for */
15747 SCIP_EXPRGRAPHNODE** constnode /**< buffer to store node corresponding to constant, if found, or NULL if not found */
15748 )
15749 {
15750 int left;
15751 int right;
15752 int middle;
15753
15754 assert(exprgraph != NULL);
15755 assert(constnode != NULL);
15756 assert(constant == constant); /* cannot search for nan */ /*lint !e777*/
15757
15758 exprgraphSortConstNodes(exprgraph);
15759 assert(exprgraph->constssorted);
15760
15761 /* find node using binary search */
15762 left = 0;
15763 right = exprgraph->nconsts-1;
15764 *constnode = NULL;
15765
15766 while( left <= right )
15767 {
15768 middle = (left+right)/2;
15769 assert(0 <= middle && middle < exprgraph->nconsts);
15770
15771 if( constant < exprgraph->constnodes[middle]->data.dbl )
15772 right = middle - 1;
15773 else if( constant > exprgraph->constnodes[middle]->data.dbl )
15774 left = middle + 1;
15775 else
15776 {
15777 *constnode = exprgraph->constnodes[middle];
15778 break;
15779 }
15780 }
15781 if( left == right+1 )
15782 return FALSE;
15783
15784 assert(*constnode != NULL);
15785 assert((*constnode)->op == SCIP_EXPR_CONST);
15786 assert((*constnode)->data.dbl == constant); /*lint !e777*/
15787
15788 return TRUE;
15789 }
15790
15791 /** prints an expression graph in dot format */
SCIPexprgraphPrintDot(SCIP_EXPRGRAPH * exprgraph,SCIP_MESSAGEHDLR * messagehdlr,FILE * file,const char ** varnames)15792 SCIP_RETCODE SCIPexprgraphPrintDot(
15793 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
15794 SCIP_MESSAGEHDLR* messagehdlr, /**< message handler */
15795 FILE* file, /**< file to print to, or NULL for stdout */
15796 const char** varnames /**< variable names, or NULL for generic names */
15797 )
15798 {
15799 int d;
15800 int i;
15801
15802 assert(exprgraph != NULL);
15803
15804 if( file == NULL )
15805 file = stdout;
15806
15807 SCIPmessageFPrintInfo(messagehdlr, file, "strict digraph exprgraph {\n");
15808 SCIPmessageFPrintInfo(messagehdlr, file, "node [fontcolor=white, style=filled, rankdir=LR]\n");
15809
15810 for( d = 0; d < exprgraph->depth; ++d )
15811 {
15812 if( exprgraph->nnodes[d] == 0 )
15813 continue;
15814
15815 for( i = 0; i < exprgraph->nnodes[d]; ++i )
15816 {
15817 exprgraphPrintNodeDot(exprgraph, exprgraph->nodes[d][i], messagehdlr, file, varnames);
15818 }
15819 }
15820
15821 /* tell dot that all nodes of depth 0 have the same rank */
15822 SCIPmessageFPrintInfo(messagehdlr, file, "{rank=same;");
15823 for( i = 0; i < exprgraph->nnodes[0]; ++i )
15824 SCIPmessageFPrintInfo(messagehdlr, file, " n0_%d", i);
15825 SCIPmessageFPrintInfo(messagehdlr, file, "}\n");
15826
15827 /* tell dot that all nodes without parent have the same rank */
15828 SCIPmessageFPrintInfo(messagehdlr, file, "{rank=same;");
15829 for( d = 0; d < exprgraph->depth; ++d )
15830 for( i = 0; i < exprgraph->nnodes[d]; ++i )
15831 if( exprgraph->nodes[d][i]->nparents == 0 )
15832 SCIPmessageFPrintInfo(messagehdlr, file, " n%d_%d", d, i);
15833 SCIPmessageFPrintInfo(messagehdlr, file, "}\n");
15834
15835 SCIPmessageFPrintInfo(messagehdlr, file, "}\n");
15836
15837 return SCIP_OKAY;
15838 }
15839
15840 /** evaluates nodes of expression graph for given values of variables */
SCIPexprgraphEval(SCIP_EXPRGRAPH * exprgraph,SCIP_Real * varvals)15841 SCIP_RETCODE SCIPexprgraphEval(
15842 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
15843 SCIP_Real* varvals /**< values for variables */
15844 )
15845 {
15846 int d;
15847 int i;
15848
15849 assert(exprgraph != NULL);
15850 assert(varvals != NULL || exprgraph->nvars == 0);
15851
15852 for( d = 0; d < exprgraph->depth; ++d )
15853 for( i = 0; i < exprgraph->nnodes[d]; ++i )
15854 {
15855 SCIP_CALL( exprgraphNodeEval(exprgraph->nodes[d][i], varvals) );
15856 }
15857
15858 return SCIP_OKAY;
15859 }
15860
15861 /** propagates bound changes in variables forward through the expression graph */
SCIPexprgraphPropagateVarBounds(SCIP_EXPRGRAPH * exprgraph,SCIP_Real infinity,SCIP_Bool clearreverseprop,SCIP_Bool * domainerror)15862 SCIP_RETCODE SCIPexprgraphPropagateVarBounds(
15863 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
15864 SCIP_Real infinity, /**< value for infinity in interval arithmetics */
15865 SCIP_Bool clearreverseprop, /**< whether to reset bound tightenings from reverse propagation */
15866 SCIP_Bool* domainerror /**< buffer to store whether a node with empty bounds has been found, propagation is interrupted in this case */
15867 )
15868 {
15869 SCIP_EXPRGRAPHNODE* node;
15870 SCIP_Bool boundchanged;
15871 int d;
15872 int i;
15873
15874 assert(exprgraph != NULL);
15875 assert(domainerror != NULL);
15876
15877 *domainerror = FALSE;
15878
15879 /* update bounds in varnodes of expression graph */
15880 exprgraphUpdateVarNodeBounds(exprgraph, &clearreverseprop, &boundchanged);
15881
15882 /* if variable bounds have not changed and we do not have to clear a previous backward propagation, we can just return */
15883 if( !boundchanged && !clearreverseprop && !exprgraph->needvarboundprop )
15884 {
15885 SCIPdebugMessage("no bounds changed and clearreverseprop is FALSE -> skip propagation of variable bounds\n");
15886 return SCIP_OKAY;
15887 }
15888
15889 /* propagate bound changes, interrupt if we get to a node with empty bounds */
15890 for( d = 1; d < exprgraph->depth; ++d )
15891 {
15892 for( i = 0; i < exprgraph->nnodes[d]; ++i )
15893 {
15894 node = exprgraph->nodes[d][i];
15895 SCIP_CALL( exprgraphNodeUpdateBounds(node, infinity, 1e-9, clearreverseprop) );
15896 if( SCIPintervalIsEmpty(infinity, node->bounds) )
15897 {
15898 SCIPdebugMessage("bounds of node %p(%d,%d) empty, stop bounds propagation\n", (void*)node, node->depth, node->pos);
15899 /* we keep exprgraph->needvarboundprop at TRUE, since we interrupt propagation */
15900 *domainerror = TRUE;
15901 return SCIP_OKAY;
15902 }
15903 }
15904 }
15905
15906 exprgraph->needvarboundprop = FALSE;
15907
15908 return SCIP_OKAY;
15909 }
15910
15911 /** propagates bound changes in nodes backward through the graph
15912 *
15913 * New bounds are not stored in varbounds, but only in nodes corresponding to variables.
15914 * NOTE: it is assumed that SCIPexprgraphPropagateVarBounds was called before if variable bounds were relaxed.
15915 */
SCIPexprgraphPropagateNodeBounds(SCIP_EXPRGRAPH * exprgraph,SCIP_Real infinity,SCIP_Real minstrength,SCIP_Bool * cutoff)15916 void SCIPexprgraphPropagateNodeBounds(
15917 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
15918 SCIP_Real infinity, /**< value for infinity in interval arithmetics */
15919 SCIP_Real minstrength, /**< minimal required relative bound strengthening in a node to trigger a propagation into children nodes */
15920 SCIP_Bool* cutoff /**< buffer to store whether a node's bounds were propagated to an empty interval */
15921 )
15922 {
15923 SCIP_EXPRGRAPHNODE* node;
15924 int d;
15925 int i;
15926
15927 assert(exprgraph != NULL);
15928 assert(cutoff != NULL);
15929
15930 *cutoff = FALSE;
15931
15932 for( d = exprgraph->depth-1; d >= 0 && !*cutoff; --d )
15933 {
15934 for( i = 0; i < exprgraph->nnodes[d] && !*cutoff; ++i )
15935 {
15936 node = exprgraph->nodes[d][i];
15937 exprgraphNodePropagateBounds(exprgraph, node, infinity, minstrength, cutoff);
15938 }
15939 }
15940 if( *cutoff )
15941 return;
15942 }
15943
15944 /** updates curvature information in expression graph nodes w.r.t. currently stored variable bounds
15945 *
15946 * Implies update of bounds in expression graph.
15947 */
SCIPexprgraphCheckCurvature(SCIP_EXPRGRAPH * exprgraph,SCIP_Real infinity,SCIP_Bool clearreverseprop)15948 SCIP_RETCODE SCIPexprgraphCheckCurvature(
15949 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
15950 SCIP_Real infinity, /**< value for infinity in interval arithmetics */
15951 SCIP_Bool clearreverseprop /**< whether to reset bound tightenings from reverse propagation */
15952 )
15953 {
15954 SCIP_EXPRGRAPHNODE* node;
15955 SCIP_Bool boundchanged;
15956 int d;
15957 int i;
15958
15959 assert(exprgraph != NULL);
15960
15961 /* update bounds in varnodes of expression graph */
15962 exprgraphUpdateVarNodeBounds(exprgraph, &clearreverseprop, &boundchanged);
15963
15964 #ifndef NDEBUG
15965 for( i = 0; i < exprgraph->nnodes[0]; ++i )
15966 assert(exprgraph->nodes[0][i]->curv == SCIP_EXPRCURV_LINEAR);
15967 #endif
15968
15969 for( d = 1; d < exprgraph->depth; ++d )
15970 for( i = 0; i < exprgraph->nnodes[d]; ++i )
15971 {
15972 node = exprgraph->nodes[d][i];
15973 assert(node != NULL);
15974
15975 SCIP_CALL( SCIPexprgraphUpdateNodeBoundsCurvature(node, infinity, 1e-9, clearreverseprop) );
15976
15977 if( SCIPintervalIsEmpty(infinity, node->bounds) )
15978 {
15979 SCIPerrorMessage("SCIPexprgraphCheckCurvature gets domain error while propagating variables bounds, ignoring...\n");
15980 return SCIP_OKAY;
15981 }
15982 }
15983
15984 return SCIP_OKAY;
15985 }
15986
15987 /** aims at simplifying an expression graph
15988 *
15989 * A domain error can occur when variables were fixed to values for which a parent expression is not defined (e.g., 0^(-1) or log(-1)).
15990 */
SCIPexprgraphSimplify(SCIP_EXPRGRAPH * exprgraph,SCIP_MESSAGEHDLR * messagehdlr,SCIP_Real eps,int maxexpansionexponent,SCIP_Bool * havechange,SCIP_Bool * domainerror)15991 SCIP_RETCODE SCIPexprgraphSimplify(
15992 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
15993 SCIP_MESSAGEHDLR* messagehdlr, /**< message handler */
15994 SCIP_Real eps, /**< threshold, under which positive values are treat as 0 */
15995 int maxexpansionexponent,/**< maximal exponent for which we still expand non-monomial polynomials */
15996 SCIP_Bool* havechange, /**< buffer to indicate whether the graph has been modified */
15997 SCIP_Bool* domainerror /**< buffer to indicate whether a domain error has been encountered, i.e., some expressions turned into NaN */
15998 )
15999 {
16000 SCIP_EXPRGRAPHNODE* node;
16001 SCIP_Bool havechangenode;
16002 SCIP_Bool allsimplified;
16003 int d;
16004 int i;
16005 int j;
16006
16007 #ifndef NDEBUG
16008 SCIP_Real* testx;
16009 SCIP_HASHMAP* testvalidx;
16010 SCIP_Real* testvals;
16011 SCIP_RANDNUMGEN* randnumgen;
16012 int testvalssize;
16013 int ntestvals;
16014 #endif
16015
16016 assert(exprgraph != NULL);
16017 assert(eps >= 0.0);
16018 assert(havechange != NULL);
16019 assert(domainerror != NULL);
16020
16021 #ifndef NDEBUG
16022 SCIP_CALL( SCIPrandomCreate(&randnumgen, exprgraph->blkmem, 862) ); /* see also #1848 */
16023 SCIP_CALL( SCIPhashmapCreate(&testvalidx, exprgraph->blkmem, 1000) );
16024 testvals = NULL;
16025 ntestvals = 0;
16026 testvalssize = 0;
16027
16028 SCIP_ALLOC( BMSallocMemoryArray(&testx, exprgraph->nvars) );
16029 for( i = 0; i < exprgraph->nvars; ++i )
16030 testx[i] = SCIPrandomGetReal(randnumgen,
16031 exprgraph->varbounds[i].inf < -100.0 ? MIN(-100.0, exprgraph->varbounds[i].sup) : exprgraph->varbounds[i].inf,
16032 exprgraph->varbounds[i].sup > 100.0 ? MAX( 100.0, exprgraph->varbounds[i].inf) : exprgraph->varbounds[i].sup); /*lint !e644*/
16033 SCIP_CALL( SCIPexprgraphEval(exprgraph, testx) );
16034 for( d = 1; d < exprgraph->depth; ++d )
16035 for( i = 0; i < exprgraph->nnodes[d]; ++i )
16036 {
16037 node = exprgraph->nodes[d][i];
16038 assert(node != NULL);
16039
16040 /* nodes that are in use should not be removed by simplifier, so for those we store their value and check if it remains the same after simplifier was run */
16041 if( node->nuses > 0 )
16042 {
16043 ensureBlockMemoryArraySize(exprgraph->blkmem, &testvals, &testvalssize, ntestvals+1);
16044 SCIP_CALL( SCIPhashmapInsertInt(testvalidx, (void*)node, ntestvals) );
16045 testvals[ntestvals] = SCIPexprgraphGetNodeVal(node); /*lint !e613 !e794*/
16046 ++ntestvals;
16047 }
16048 }
16049
16050 SCIPrandomFree(&randnumgen, exprgraph->blkmem);
16051 #endif
16052
16053 #ifdef SCIP_OUTPUT
16054 {
16055 FILE* file;
16056 file = fopen("exprgraph_beforesimplify.dot", "w");
16057 if( file != NULL )
16058 {
16059 SCIP_CALL( SCIPexprgraphPrintDot(exprgraph, messagehdlr, file, NULL) );
16060 fclose(file);
16061 }
16062 }
16063 #endif
16064
16065 *havechange = FALSE; /* we have not changed any node yet */
16066 *domainerror = FALSE; /* no domain errors encountered so far */
16067 allsimplified = TRUE; /* all nodes we looked at are simplified */
16068
16069 /* call node simplifier from bottom up
16070 * for each node, convert to polynomials and merge in child nodes that are not in use otherwise, if possible
16071 */
16072 for( d = 1; d < exprgraph->depth && !*domainerror; ++d )
16073 {
16074 for( i = 0; i < exprgraph->nnodes[d]; ++i )
16075 {
16076 node = exprgraph->nodes[d][i];
16077 assert(node != NULL);
16078
16079 havechangenode = FALSE; /* node did not change yet */
16080
16081 if( node->op != SCIP_EXPR_CONST )
16082 {
16083 /* skip nodes that are already simplified */
16084 if( node->simplified )
16085 continue;
16086
16087 allsimplified = FALSE; /* looks like we found a node that has not been simplified */
16088
16089 /* we should be careful about declaring numbers close to zero as zero, so take eps^2 as tolerance */
16090 SCIP_CALL( exprgraphNodeSimplify(exprgraph, node, messagehdlr, eps*eps, maxexpansionexponent, &havechangenode) );
16091 assert(node->simplified == TRUE);
16092 *havechange |= havechangenode;
16093 }
16094
16095 /* if node was or has been converted into constant, may move to depth 0 */
16096 if( node->op == SCIP_EXPR_CONST )
16097 {
16098 SCIP_EXPRGRAPHNODE* constnode;
16099
16100 if( !SCIPisFinite(node->value) ) /*lint !e777*/
16101 {
16102 SCIPdebugMessage("Expression graph simplify turned node into NaN or inf.\n");
16103 *domainerror = TRUE;
16104 break;
16105 }
16106
16107 /* check if there is already a node for this constant */
16108 if( SCIPexprgraphFindConstNode(exprgraph, node->value, &constnode) )
16109 {
16110 assert(constnode->op == SCIP_EXPR_CONST);
16111 assert(constnode->data.dbl == node->value); /*lint !e777*/
16112
16113 if( node->nparents > 0 )
16114 {
16115 /* move parents of this node to constnode, node may be freed if not in use */
16116 SCIP_CALL( SCIPexprgraphMoveNodeParents(exprgraph, &node, constnode) );
16117 /* node should have no parents anymore, so it should have been freed if not in use */
16118 assert(node == NULL || node->nuses > 0);
16119 havechangenode = TRUE;
16120
16121 /* if node was freed, exprgraph->nodes[i] points to the next node that need to be simplified */
16122 if( node == NULL )
16123 {
16124 --i;
16125 continue;
16126 }
16127 }
16128 assert(node != NULL);
16129 assert(node->nuses > 0);
16130
16131 if( constnode->nuses == 0 )
16132 {
16133 /* move node to depth 0, adding it to constnodes */
16134 SCIP_CALL( exprgraphMoveNode(exprgraph, node, 0) );
16135
16136 /* move parents of constnode to node, so constnode is freed */
16137 SCIP_CALL( SCIPexprgraphMoveNodeParents(exprgraph, &constnode, node) );
16138 assert(constnode == NULL);
16139 havechangenode = TRUE;
16140
16141 /* node moved to depth 0, so exprgraph->nodes[i] points to the next node that need to be simplified */
16142 --i;
16143 continue;
16144 }
16145 }
16146 else
16147 {
16148 /* move to depth 0, adding it to constnodes */
16149 SCIP_CALL( exprgraphMoveNode(exprgraph, node, 0) );
16150
16151 /* node moved to depth 0, so exprgraph->nodes[i] points to the next node that need to be simplified */
16152 --i;
16153 }
16154 }
16155
16156 /* if there was a change, mark parents as not simplified */
16157 if( havechangenode )
16158 for( j = 0; j < node->nparents; ++j )
16159 node->parents[j]->simplified = FALSE;
16160 }
16161 } /*lint !e850*/
16162
16163 /* if we did nothing, clean up and escape from here */
16164 if( allsimplified || *domainerror )
16165 goto EXPRGRAPHSIMPLIFY_CLEANUP;
16166
16167 /* @todo find duplicate subexpressions in expression graph */
16168
16169 /* unconvert polynomials into simpler expressions, where possible */
16170 for( d = 1; d < exprgraph->depth; ++d )
16171 {
16172 for( i = 0; i < exprgraph->nnodes[d]; ++i )
16173 {
16174 node = exprgraph->nodes[d][i];
16175 assert(node != NULL);
16176
16177 if( node->op != SCIP_EXPR_POLYNOMIAL )
16178 continue;
16179
16180 SCIP_CALL( exprUnconvertPolynomial(exprgraph->blkmem, &node->op, &node->data, node->nchildren, (void**)node->children) );
16181
16182 if( node->op == SCIP_EXPR_SUM && node->nchildren == 1 )
16183 {
16184 /* node is identity w.r.t only child
16185 * replace node as child of parents by child of node
16186 */
16187
16188 for( j = 0; node != NULL && j < node->nparents; ++j )
16189 {
16190 SCIP_CALL( exprgraphNodeReplaceChild(exprgraph, node->parents[j], &node, node->children[0]) );
16191 }
16192 /* node should have no parents anymore, so it should have been freed if not in use */
16193 assert(node == NULL || node->nuses > 0);
16194
16195 /* if node was freed, exprgraph->nodes[i] points to the next node that need to be unconverted */
16196 if( node == NULL )
16197 --i;
16198 }
16199 }
16200 } /*lint !e850*/
16201
16202 #ifdef SCIP_OUTPUT
16203 {
16204 FILE* file;
16205 file = fopen("exprgraph_aftersimplify.dot", "w");
16206 if( file != NULL )
16207 {
16208 SCIP_CALL( SCIPexprgraphPrintDot(exprgraph, messagehdlr, file, NULL) );
16209 fclose(file);
16210 }
16211 }
16212 #endif
16213
16214 #ifndef NDEBUG
16215 for( d = 1; d < exprgraph->depth; ++d )
16216 for( i = 0; i < exprgraph->nnodes[d]; ++i )
16217 {
16218 int idx;
16219 SCIP_Real testval_before;
16220 SCIP_Real testval_after;
16221
16222 node = exprgraph->nodes[d][i];
16223 assert(node != NULL);
16224
16225 SCIP_CALL( exprgraphNodeEval(node, NULL) );
16226
16227 /* nodes that are in use should not have been removed by simplifier, check if they still have the same value in our testpoint */
16228 if( node->nuses > 0 )
16229 {
16230 assert(SCIPhashmapExists(testvalidx, (void*)node));
16231
16232 idx = SCIPhashmapGetImageInt(testvalidx, (void*)node);
16233 assert(idx < ntestvals);
16234 assert(testvals != NULL);
16235
16236 testval_before = testvals[idx]; /*lint !e613*/
16237 testval_after = SCIPexprgraphGetNodeVal(node);
16238
16239 assert(!SCIPisFinite(testval_before) || EPSZ(SCIPrelDiff(testval_before, testval_after), 10*eps)); /*lint !e777*/
16240 }
16241 }
16242 #endif
16243
16244 EXPRGRAPHSIMPLIFY_CLEANUP:
16245 #ifndef NDEBUG
16246 BMSfreeMemoryArray(&testx);
16247 BMSfreeBlockMemoryArrayNull(exprgraph->blkmem, &testvals, testvalssize);
16248 SCIPhashmapFree(&testvalidx);
16249 #endif
16250
16251 return SCIP_OKAY;
16252 }
16253
16254 /** creates an expression tree from a given node in an expression graph */
SCIPexprgraphGetTree(SCIP_EXPRGRAPH * exprgraph,SCIP_EXPRGRAPHNODE * rootnode,SCIP_EXPRTREE ** exprtree)16255 SCIP_RETCODE SCIPexprgraphGetTree(
16256 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
16257 SCIP_EXPRGRAPHNODE* rootnode, /**< expression graph node that should represent root of expression tree */
16258 SCIP_EXPRTREE** exprtree /**< buffer to store pointer to created expression tree */
16259 )
16260 {
16261 SCIP_EXPR* root;
16262 int nexprvars;
16263 int* varidx;
16264 int i;
16265
16266 assert(exprgraph != NULL);
16267 assert(rootnode != NULL);
16268 assert(rootnode->depth >= 0);
16269 assert(rootnode->pos >= 0);
16270 assert(exprtree != NULL);
16271
16272 /* buffer where to store mapping of expression graph variable indices to expression tree variable indices */
16273 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &varidx, exprgraph->nvars) );
16274
16275 /* initially, no variable appears in the expression tree */
16276 for( i = 0; i < exprgraph->nvars; ++i )
16277 varidx[i] = -1; /*lint !e644*/
16278 nexprvars = 0;
16279
16280 /* create expression from the subgraph that has rootnode as root */
16281 SCIP_CALL( exprgraphNodeCreateExpr(exprgraph, rootnode, &root, &nexprvars, varidx) );
16282
16283 /* create expression tree for this expression */
16284 SCIP_CALL( SCIPexprtreeCreate(exprgraph->blkmem, exprtree, root, nexprvars, 0, NULL) );
16285
16286 /* copy variables into expression tree */
16287 if( nexprvars > 0 )
16288 {
16289 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &(*exprtree)->vars, nexprvars) );
16290 for( i = 0; i < exprgraph->nvars; ++i )
16291 {
16292 assert(varidx[i] >= -1);
16293 assert(varidx[i] < nexprvars);
16294 if( varidx[i] >= 0 )
16295 (*exprtree)->vars[varidx[i]] = exprgraph->vars[i];
16296 }
16297 }
16298
16299 BMSfreeBlockMemoryArray(exprgraph->blkmem, &varidx, exprgraph->nvars);
16300
16301 return SCIP_OKAY;
16302 }
16303
16304 /** creates a sum of expression trees with pairwise disjoint variables from a given node in an expression graph
16305 *
16306 * Giving SCIPexprgraphGetNodeNChildren() for exprtreesize is always sufficient.
16307 */
SCIPexprgraphGetSeparableTrees(SCIP_EXPRGRAPH * exprgraph,SCIP_EXPRGRAPHNODE * node,int exprtreessize,int * nexprtrees,SCIP_EXPRTREE ** exprtrees,SCIP_Real * exprtreecoefs)16308 SCIP_RETCODE SCIPexprgraphGetSeparableTrees(
16309 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
16310 SCIP_EXPRGRAPHNODE* node, /**< expression graph node which represents expression to get */
16311 int exprtreessize, /**< length of exprtrees and exprtreecoefs arrays, need to be at least one */
16312 int* nexprtrees, /**< buffer to store number of expression trees */
16313 SCIP_EXPRTREE** exprtrees, /**< array where to store expression trees */
16314 SCIP_Real* exprtreecoefs /**< array where to store coefficients of expression trees */
16315 )
16316 {
16317 int ncomponents;
16318 int* childcomp;
16319 int* varcomp;
16320 int compnr;
16321 SCIP_Bool haveoverlap;
16322 int i;
16323 int j;
16324 int k;
16325
16326 SCIP_EXPR** exprs;
16327 int nexprs;
16328 int* childmap;
16329 int* childmapinv;
16330 int* varidx;
16331 int nexprvars;
16332
16333 assert(exprgraph != NULL);
16334 assert(node != NULL);
16335 assert(node->depth >= 0);
16336 assert(node->pos >= 0);
16337 assert(exprtreessize > 0);
16338 assert(nexprtrees != NULL);
16339 assert(exprtrees != NULL);
16340 assert(exprtreecoefs != NULL);
16341
16342 /* easy cases: if we have space for only one tree or there is only one child or only one variable in the graph,
16343 * or the node operator is not separable, fallback to SCIPexprgraphGetTree */
16344 if( exprtreessize == 1 || node->nchildren <= 1 || exprgraph->nvars <= 1 ||
16345 ( node->op != SCIP_EXPR_PLUS &&
16346 node->op != SCIP_EXPR_MINUS &&
16347 node->op != SCIP_EXPR_SUM &&
16348 node->op != SCIP_EXPR_LINEAR &&
16349 (node->op != SCIP_EXPR_QUADRATIC || ((SCIP_EXPRDATA_QUADRATIC*)node->data.data)->nquadelems <= 1) &&
16350 (node->op != SCIP_EXPR_POLYNOMIAL || ((SCIP_EXPRDATA_POLYNOMIAL*)node->data.data)->nmonomials <= 1)) )
16351 {
16352 *nexprtrees = 1;
16353 exprtreecoefs[0] = 1.0;
16354 SCIP_CALL( SCIPexprgraphGetTree(exprgraph, node, exprtrees) );
16355
16356 return SCIP_OKAY;
16357 }
16358
16359 /* find components in node->children <-> variables graph */
16360 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &childcomp, node->nchildren) );
16361 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &varcomp, exprgraph->nvars) );
16362 for( i = 0; i < exprgraph->nvars; ++i )
16363 varcomp[i] = -1; /*lint !e644*/
16364
16365 haveoverlap = FALSE;
16366 for( i = 0; i < node->nchildren; ++i )
16367 {
16368 compnr = i;
16369 exprgraphNodeCheckSeparabilityComponent(node->children[i], &compnr, i-1, childcomp, exprgraph->nvars, varcomp); /*lint !e644*/
16370 assert(compnr >= 0);
16371 assert(compnr < node->nchildren);
16372 childcomp[i] = compnr;
16373
16374 /* remember if component number was changed by CheckComponent */
16375 if( compnr != i )
16376 haveoverlap = TRUE;
16377 }
16378
16379 BMSfreeBlockMemoryArray(exprgraph->blkmem, &varcomp, exprgraph->nvars);
16380
16381 if( node->op == SCIP_EXPR_QUADRATIC )
16382 {
16383 /* merge components for products of children from different components */
16384 SCIP_EXPRDATA_QUADRATIC* data;
16385
16386 data = (SCIP_EXPRDATA_QUADRATIC*)node->data.data;
16387 assert(data != NULL);
16388
16389 for( i = 0; i < data->nquadelems; ++i )
16390 if( childcomp[data->quadelems[i].idx1] != childcomp[data->quadelems[i].idx2] )
16391 {
16392 /* reassign all children in component childcomp[data->quadelems[i].idx2] to component childcomp[data->quadelems[i].idx1] */
16393 compnr = childcomp[data->quadelems[i].idx2];
16394 for( j = 0; j < node->nchildren; ++j )
16395 if( childcomp[j] == compnr )
16396 childcomp[j] = childcomp[data->quadelems[i].idx1];
16397 assert(childcomp[data->quadelems[i].idx1] == childcomp[data->quadelems[i].idx2]);
16398 haveoverlap = TRUE;
16399 }
16400 }
16401 else if( node->op == SCIP_EXPR_POLYNOMIAL )
16402 {
16403 /* merge components for monomials of children from different components */
16404 SCIP_EXPRDATA_POLYNOMIAL* data;
16405
16406 data = (SCIP_EXPRDATA_POLYNOMIAL*)node->data.data;
16407 assert(data != NULL);
16408
16409 for( i = 0; i < data->nmonomials; ++i )
16410 for( j = 1; j < data->monomials[i]->nfactors; ++j )
16411 if( childcomp[data->monomials[i]->childidxs[j]] != childcomp[data->monomials[i]->childidxs[0]] )
16412 {
16413 /* reassign all children in component childcomp[data->monomials[i]->childidxs[j]] to component childcomp[data->monomials[i]->childidxs[0]] */
16414 compnr = childcomp[data->monomials[i]->childidxs[j]];
16415 for( k = 0; k < node->nchildren; ++k )
16416 if( childcomp[k] == compnr )
16417 childcomp[k] = childcomp[data->monomials[i]->childidxs[0]];
16418 assert(childcomp[data->monomials[i]->childidxs[j]] == childcomp[data->monomials[i]->childidxs[0]]);
16419 haveoverlap = TRUE;
16420 }
16421 }
16422
16423 if( haveoverlap )
16424 {
16425 /* some component numbers are unused, thus relabel and count final number of components */
16426 int* compmap;
16427
16428 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &compmap, node->nchildren) );
16429 for( i = 0; i < node->nchildren; ++i )
16430 compmap[i] = -1; /*lint !e644*/
16431
16432 ncomponents = 0;
16433 for( i = 0; i < node->nchildren; ++i )
16434 {
16435 if( compmap[childcomp[i]] == -1 )
16436 compmap[childcomp[i]] = ncomponents++;
16437 childcomp[i] = compmap[childcomp[i]];
16438 }
16439
16440 BMSfreeBlockMemoryArray(exprgraph->blkmem, &compmap, node->nchildren);
16441 }
16442 else
16443 {
16444 ncomponents = node->nchildren;
16445 }
16446
16447 if( ncomponents == 1 )
16448 {
16449 /* it turned out that expression is not block separable, so fallback to SCIPexprgraphGetTree */
16450 BMSfreeBlockMemoryArray(exprgraph->blkmem, &childcomp, node->nchildren);
16451
16452 *nexprtrees = 1;
16453 exprtreecoefs[0] = 1.0;
16454 SCIP_CALL( SCIPexprgraphGetTree(exprgraph, node, exprtrees) );
16455
16456 return SCIP_OKAY;
16457 }
16458
16459 if( ncomponents > exprtreessize )
16460 {
16461 /* if we have not enough space for all expressions, merge components with number > exprtreessize into component exprtreessize */
16462 for( i = 0; i < node->nchildren; ++i )
16463 if( childcomp[i] >= exprtreessize )
16464 childcomp[i] = exprtreessize-1;
16465 ncomponents = exprtreessize;
16466 }
16467
16468 assert(ncomponents >= 2);
16469
16470 /* setup expression trees for each component */
16471 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &exprs, node->nchildren) );
16472 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &varidx, exprgraph->nvars) ); /* mapping of expression graph variable indices to expression tree variable indices */
16473 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &childmap, node->nchildren) ); /* mapping of child indices from node to expressions belonging to a single component */
16474 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &childmapinv, node->nchildren) ); /* mapping of child indices from expressions belonging to a single component to node */
16475 for( i = 0; i < ncomponents; ++i )
16476 {
16477 /* initially, no variable appears in the expression tree */
16478 for( j = 0; j < exprgraph->nvars; ++j )
16479 varidx[j] = -1; /*lint !e644*/
16480 nexprvars = 0;
16481
16482 /* collect expressions from children belonging to component i */
16483 nexprs = 0;
16484 for( j = 0; j < node->nchildren; ++j )
16485 {
16486 assert(childcomp[j] >= 0);
16487 assert(childcomp[j] < ncomponents);
16488 if( childcomp[j] != i )
16489 continue;
16490
16491 /* create expression from the subgraph that has child j as root */
16492 SCIP_CALL( exprgraphNodeCreateExpr(exprgraph, node->children[j], &exprs[nexprs], &nexprvars, varidx) ); /*lint !e644*/
16493 childmap[j] = nexprs; /*lint !e644*/
16494 childmapinv[nexprs] = j; /*lint !e644*/
16495 ++nexprs;
16496 }
16497
16498 /* setup expression tree for component i */
16499 switch( node->op )
16500 {
16501 case SCIP_EXPR_PLUS:
16502 {
16503 assert(ncomponents == 2);
16504 assert(nexprs == 1);
16505
16506 SCIP_CALL( SCIPexprtreeCreate(exprgraph->blkmem, &exprtrees[i], exprs[0], nexprvars, 0, NULL) );
16507 exprtreecoefs[i] = 1.0;
16508
16509 break;
16510 }
16511
16512 case SCIP_EXPR_MINUS:
16513 {
16514 assert(ncomponents == 2);
16515 assert(nexprs == 1);
16516
16517 SCIP_CALL( SCIPexprtreeCreate(exprgraph->blkmem, &exprtrees[i], exprs[0], nexprvars, 0, NULL) );
16518 /* if component i consists of first child, then it has coefficient 1.0, otherwise it has coefficient -1 */
16519 assert(childmapinv[0] == 0 || childmapinv[0] == 1);
16520 exprtreecoefs[i] = (childmapinv[0] == 0 ? 1.0 : -1.0);
16521
16522 break;
16523 }
16524
16525 case SCIP_EXPR_SUM:
16526 {
16527 if( nexprs == 1 )
16528 {
16529 /* component corresponds to exactly one child of node */
16530 SCIP_CALL( SCIPexprtreeCreate(exprgraph->blkmem, &exprtrees[i], exprs[0], nexprvars, 0, NULL) );
16531 }
16532 else
16533 {
16534 /* component corresponds to a sum of children of node */
16535 SCIP_EXPR* sumexpr;
16536
16537 SCIP_CALL( SCIPexprCreate(exprgraph->blkmem, &sumexpr, SCIP_EXPR_SUM, nexprs, exprs) );
16538 SCIP_CALL( SCIPexprtreeCreate(exprgraph->blkmem, &exprtrees[i], sumexpr, nexprvars, 0, NULL) );
16539 }
16540 exprtreecoefs[i] = 1.0;
16541
16542 break;
16543 }
16544
16545 case SCIP_EXPR_LINEAR:
16546 {
16547 SCIP_Real* nodecoefs;
16548 SCIP_EXPR* sumexpr;
16549
16550 nodecoefs = (SCIP_Real*)node->data.data;
16551
16552 /* if there is a constant, then we put it into the expression of the first component */
16553 if( nexprs == 1 && (i > 0 || nodecoefs[node->nchildren] == 0.0) )
16554 {
16555 /* component corresponds to exactly one child of node */
16556 SCIP_CALL( SCIPexprtreeCreate(exprgraph->blkmem, &exprtrees[i], exprs[0], nexprvars, 0, NULL) );
16557 exprtreecoefs[i] = nodecoefs[childmapinv[0]];
16558 }
16559 else if( nexprs == 1 )
16560 {
16561 /* component corresponds to a sum of one child and a constant */
16562 assert(i == 0);
16563 assert(nodecoefs[node->nchildren] != 0.0);
16564 assert(nodecoefs[childmapinv[0]] != 0.0);
16565 SCIP_CALL( SCIPexprCreate(exprgraph->blkmem, &sumexpr, SCIP_EXPR_CONST, nodecoefs[node->nchildren]/nodecoefs[childmapinv[0]]) );
16566 SCIP_CALL( SCIPexprCreate(exprgraph->blkmem, &sumexpr, SCIP_EXPR_PLUS, sumexpr, exprs[0]) );
16567 SCIP_CALL( SCIPexprtreeCreate(exprgraph->blkmem, &exprtrees[i], sumexpr, nexprvars, 0, NULL) );
16568 exprtreecoefs[i] = nodecoefs[childmapinv[0]];
16569 }
16570 else
16571 {
16572 /* component corresponds to a linear combination of children of node */
16573
16574 if( nexprs == 2 && nodecoefs[childmapinv[0]] == nodecoefs[childmapinv[1]] && (i > 0 || nodecoefs[node->nchildren] == 0.0) ) /*lint !e777*/
16575 {
16576 /* if two expressions with equal sign, then create PLUS expression */
16577 SCIP_CALL( SCIPexprCreate(exprgraph->blkmem, &sumexpr, SCIP_EXPR_PLUS, exprs[0], exprs[1]) );
16578 exprtreecoefs[i] = nodecoefs[childmapinv[0]];
16579 }
16580 else if( nexprs == 2 && nodecoefs[childmapinv[0]] == -nodecoefs[childmapinv[1]] && (i > 0 || nodecoefs[node->nchildren] == 0.0) ) /*lint !e777*/
16581 {
16582 /* if two expressions with opposite sign, then create MINUS expression */
16583 SCIP_CALL( SCIPexprCreate(exprgraph->blkmem, &sumexpr, SCIP_EXPR_MINUS, exprs[0], exprs[1]) );
16584 exprtreecoefs[i] = nodecoefs[childmapinv[0]];
16585 }
16586 else
16587 {
16588 /* assemble coefficents and create SUM or LINEAR expression */
16589 SCIP_Real* coefs;
16590 SCIP_Bool allcoefsequal;
16591
16592 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &coefs, nexprs) );
16593 allcoefsequal = TRUE;
16594 coefs[0] = nodecoefs[childmapinv[0]]; /*lint !e644*/
16595 for( j = 0; j < nexprs; ++j )
16596 {
16597 coefs[j] = nodecoefs[childmapinv[j]];
16598 allcoefsequal &= (coefs[j] == coefs[0]); /*lint !e777 !e514*/
16599 }
16600
16601 /* if all coefficients are equal and no constant, create SUM expression, otherwise LINEAR expression */
16602 if( allcoefsequal && (i > 0 || nodecoefs[node->nchildren] == 0.0) )
16603 {
16604 SCIP_CALL( SCIPexprCreate(exprgraph->blkmem, &sumexpr, SCIP_EXPR_SUM, nexprs, exprs) );
16605 exprtreecoefs[i] = coefs[0];
16606 }
16607 else
16608 {
16609 SCIP_CALL( SCIPexprCreateLinear(exprgraph->blkmem, &sumexpr, nexprs, exprs, coefs, i == 0 ? nodecoefs[node->nchildren] : 0.0) );
16610 exprtreecoefs[i] = 1.0;
16611 }
16612
16613 BMSfreeBlockMemoryArray(exprgraph->blkmem, &coefs, nexprs);
16614 }
16615
16616 SCIP_CALL( SCIPexprtreeCreate(exprgraph->blkmem, &exprtrees[i], sumexpr, nexprvars, 0, NULL) );
16617 }
16618
16619 break;
16620 }
16621
16622 case SCIP_EXPR_QUADRATIC:
16623 {
16624 SCIP_EXPR* quadexpr;
16625 SCIP_EXPRDATA_QUADRATIC* nodedata;
16626 SCIP_Real* lincoefs;
16627 SCIP_QUADELEM* quadelems;
16628 int nquadelems;
16629
16630 nodedata = (SCIP_EXPRDATA_QUADRATIC*)node->data.data;
16631
16632 exprtreecoefs[i] = 1.0;
16633
16634 /* assemble coefficients corresponding to component i */
16635 if( nodedata->lincoefs != NULL )
16636 {
16637 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &lincoefs, nexprs) );
16638 for( j = 0; j < nexprs; ++j )
16639 lincoefs[j] = nodedata->lincoefs[childmapinv[j]]; /*lint !e771*/
16640 }
16641 else
16642 lincoefs = NULL;
16643
16644 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &quadelems, nodedata->nquadelems) );
16645 nquadelems = 0;
16646 for( j = 0; j < nodedata->nquadelems; ++j )
16647 {
16648 assert(childcomp[nodedata->quadelems[j].idx1] == childcomp[nodedata->quadelems[j].idx2]);
16649 if( childcomp[nodedata->quadelems[j].idx1] != i )
16650 continue;
16651 quadelems[nquadelems].idx1 = MIN(childmap[nodedata->quadelems[j].idx1], childmap[nodedata->quadelems[j].idx2]); /*lint !e644*/
16652 quadelems[nquadelems].idx2 = MAX(childmap[nodedata->quadelems[j].idx1], childmap[nodedata->quadelems[j].idx2]);
16653 quadelems[nquadelems].coef = nodedata->quadelems[j].coef;
16654 ++nquadelems;
16655 }
16656
16657 /* put constant into first component */
16658 SCIP_CALL( SCIPexprCreateQuadratic(exprgraph->blkmem, &quadexpr, nexprs, exprs, i == 0 ? nodedata->constant : 0.0, lincoefs, nquadelems, quadelems) );
16659 SCIP_CALL( SCIPexprtreeCreate(exprgraph->blkmem, &exprtrees[i], quadexpr, nexprvars, 0, NULL) );
16660
16661 BMSfreeBlockMemoryArray(exprgraph->blkmem, &quadelems, nodedata->nquadelems);
16662 BMSfreeBlockMemoryArrayNull(exprgraph->blkmem, &lincoefs, nexprs);
16663
16664 break;
16665 }
16666
16667 case SCIP_EXPR_POLYNOMIAL:
16668 {
16669 SCIP_EXPR* polyexpr;
16670 SCIP_EXPRDATA_POLYNOMIAL* nodedata;
16671 SCIP_EXPRDATA_MONOMIAL** monomials;
16672 SCIP_Real constant;
16673 int nmonomials;
16674
16675 nodedata = (SCIP_EXPRDATA_POLYNOMIAL*)node->data.data;
16676
16677 constant = nodedata->constant;
16678 exprtreecoefs[i] = 1.0;
16679
16680 /* collect monomials belonging to component i */
16681 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &monomials, nodedata->nmonomials) );
16682 nmonomials = 0;
16683 for( j = 0; j < nodedata->nmonomials; ++j )
16684 {
16685 if( nodedata->monomials[j]->nfactors == 0 )
16686 {
16687 constant += nodedata->monomials[j]->coef;
16688 continue;
16689 }
16690 if( childcomp[nodedata->monomials[j]->childidxs[0]] != i )
16691 continue;
16692
16693 SCIP_CALL( SCIPexprCreateMonomial(exprgraph->blkmem, &monomials[nmonomials], nodedata->monomials[j]->coef, nodedata->monomials[j]->nfactors,
16694 nodedata->monomials[j]->childidxs, nodedata->monomials[j]->exponents) ); /*lint !e644*/
16695 for( k = 0; k < monomials[nmonomials]->nfactors; ++k )
16696 {
16697 assert(childcomp[nodedata->monomials[j]->childidxs[k]] == i);
16698 monomials[nmonomials]->childidxs[k] = childmap[monomials[nmonomials]->childidxs[k]];
16699 }
16700 ++nmonomials;
16701 }
16702
16703 SCIP_CALL( SCIPexprCreatePolynomial(exprgraph->blkmem, &polyexpr, nexprs, exprs, nmonomials, monomials, i == 0 ? constant : 0.0, FALSE) );
16704 SCIP_CALL( SCIPexprtreeCreate(exprgraph->blkmem, &exprtrees[i], polyexpr, nexprvars, 0, NULL) );
16705
16706 BMSfreeBlockMemoryArray(exprgraph->blkmem, &monomials, nodedata->nmonomials);
16707
16708 break;
16709 }
16710
16711 default:
16712 SCIPerrorMessage("unexpected operator type %d\n", node->op);
16713 return SCIP_ERROR;
16714 } /*lint !e788*/
16715
16716 /* copy variables into expression tree */
16717 if( nexprvars > 0 )
16718 {
16719 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &exprtrees[i]->vars, nexprvars) );
16720 for( j = 0; j < exprgraph->nvars; ++j )
16721 {
16722 assert(varidx[j] >= -1);
16723 assert(varidx[j] < nexprvars);
16724 if( varidx[j] >= 0 )
16725 exprtrees[i]->vars[varidx[j]] = exprgraph->vars[j];
16726 }
16727 }
16728 }
16729
16730 BMSfreeBlockMemoryArray(exprgraph->blkmem, &exprs, node->nchildren);
16731 BMSfreeBlockMemoryArray(exprgraph->blkmem, &varidx, exprgraph->nvars);
16732 BMSfreeBlockMemoryArray(exprgraph->blkmem, &childmap, node->nchildren);
16733 BMSfreeBlockMemoryArray(exprgraph->blkmem, &childmapinv, node->nchildren);
16734 BMSfreeBlockMemoryArray(exprgraph->blkmem, &childcomp, node->nchildren);
16735
16736 *nexprtrees = ncomponents;
16737
16738 return SCIP_OKAY;
16739 }
16740
16741 /** returns how often expression graph variables are used in a subtree of the expression graph */
SCIPexprgraphGetSubtreeVarsUsage(SCIP_EXPRGRAPH * exprgraph,SCIP_EXPRGRAPHNODE * node,int * varsusage)16742 void SCIPexprgraphGetSubtreeVarsUsage(
16743 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
16744 SCIP_EXPRGRAPHNODE* node, /**< root node of expression graph subtree */
16745 int* varsusage /**< array where to count usage of variables, length must be at least the number of variables in the graph */
16746 )
16747 {
16748 assert(exprgraph != NULL);
16749 assert(node != NULL);
16750 assert(varsusage != NULL);
16751
16752 BMSclearMemoryArray(varsusage, exprgraph->nvars);
16753
16754 exprgraphNodeGetVarsUsage(node, varsusage);
16755 }
16756
16757 /** gives the number of summands which the expression of an expression graph node consists of */
SCIPexprgraphGetSumTreesNSummands(SCIP_EXPRGRAPHNODE * node)16758 int SCIPexprgraphGetSumTreesNSummands(
16759 SCIP_EXPRGRAPHNODE* node /**< expression graph node */
16760 )
16761 {
16762 switch( node->op )
16763 {
16764 case SCIP_EXPR_PLUS:
16765 case SCIP_EXPR_MINUS:
16766 return 2;
16767
16768 case SCIP_EXPR_SUM:
16769 case SCIP_EXPR_LINEAR:
16770 return node->nchildren;
16771
16772 case SCIP_EXPR_QUADRATIC:
16773 {
16774 SCIP_EXPRDATA_QUADRATIC* nodedata;
16775
16776 nodedata = (SCIP_EXPRDATA_QUADRATIC*)node->data.data;
16777 return (nodedata->lincoefs != NULL ? node->nchildren : 0) + nodedata->nquadelems;
16778 }
16779
16780 case SCIP_EXPR_POLYNOMIAL:
16781 {
16782 SCIP_EXPRDATA_POLYNOMIAL* nodedata;
16783
16784 nodedata = (SCIP_EXPRDATA_POLYNOMIAL*)node->data.data;
16785 return nodedata->nmonomials;
16786 }
16787
16788 default:
16789 return 1;
16790 } /*lint !e788*/
16791 }
16792
16793 /** creates a sum of expression trees, possibly sharing variables, from a given node in an expression graph */
SCIPexprgraphGetSumTrees(SCIP_EXPRGRAPH * exprgraph,SCIP_EXPRGRAPHNODE * node,int exprtreessize,int * nexprtrees,SCIP_EXPRTREE ** exprtrees,SCIP_Real * exprtreecoefs)16794 SCIP_RETCODE SCIPexprgraphGetSumTrees(
16795 SCIP_EXPRGRAPH* exprgraph, /**< expression graph */
16796 SCIP_EXPRGRAPHNODE* node, /**< expression graph node which represents expression to get */
16797 int exprtreessize, /**< length of exprtrees and exptreecoefs arrays, should be at least SCIPexprgraphGetSumTreesNSummands() */
16798 int* nexprtrees, /**< buffer to store number of expression trees */
16799 SCIP_EXPRTREE** exprtrees, /**< array where to store expression trees */
16800 SCIP_Real* exprtreecoefs /**< array where to store coefficients of expression trees */
16801 )
16802 {
16803 int* varidx;
16804 int nexprvars;
16805 int i;
16806
16807 assert(exprgraph != NULL);
16808 assert(node != NULL);
16809 assert(node->depth >= 0);
16810 assert(node->pos >= 0);
16811 assert(exprtreessize > 0);
16812 assert(nexprtrees != NULL);
16813 assert(exprtrees != NULL);
16814 assert(exprtreecoefs != NULL);
16815
16816 /* if node is not separable, fallback to SCIPexprgraphGetTree */
16817 if( node->op != SCIP_EXPR_PLUS &&
16818 node->op != SCIP_EXPR_MINUS &&
16819 node->op != SCIP_EXPR_SUM &&
16820 (node->op != SCIP_EXPR_LINEAR || node->nchildren <= 1) &&
16821 (node->op != SCIP_EXPR_QUADRATIC || (((SCIP_EXPRDATA_QUADRATIC*)node->data.data)->lincoefs == NULL && ((SCIP_EXPRDATA_QUADRATIC*)node->data.data)->nquadelems <= 1)) &&
16822 (node->op != SCIP_EXPR_POLYNOMIAL || ((SCIP_EXPRDATA_POLYNOMIAL*)node->data.data)->nmonomials <= 1) )
16823 {
16824 *nexprtrees = 1;
16825 exprtreecoefs[0] = 1.0;
16826 SCIP_CALL( SCIPexprgraphGetTree(exprgraph, node, exprtrees) );
16827
16828 return SCIP_OKAY;
16829 }
16830
16831 switch( node->op )
16832 {
16833 case SCIP_EXPR_PLUS:
16834 {
16835 assert(exprtreessize >= 2);
16836
16837 SCIP_CALL( SCIPexprgraphGetTree(exprgraph, node->children[0], &exprtrees[0]) );
16838 SCIP_CALL( SCIPexprgraphGetTree(exprgraph, node->children[1], &exprtrees[1]) );
16839
16840 exprtreecoefs[0] = 1.0;
16841 exprtreecoefs[1] = 1.0;
16842
16843 *nexprtrees = 2;
16844 break;
16845 }
16846
16847 case SCIP_EXPR_MINUS:
16848 {
16849 assert(exprtreessize >= 2);
16850
16851 SCIP_CALL( SCIPexprgraphGetTree(exprgraph, node->children[0], &exprtrees[0]) );
16852 SCIP_CALL( SCIPexprgraphGetTree(exprgraph, node->children[1], &exprtrees[1]) );
16853
16854 exprtreecoefs[0] = 1.0;
16855 exprtreecoefs[1] = -1.0;
16856
16857 *nexprtrees = 2;
16858 break;
16859 }
16860
16861 case SCIP_EXPR_SUM:
16862 {
16863 assert(exprtreessize >= node->nchildren);
16864
16865 for( i = 0; i < node->nchildren; ++i )
16866 {
16867 SCIP_CALL( SCIPexprgraphGetTree(exprgraph, node->children[i], &exprtrees[i]) );
16868 exprtreecoefs[i] = 1.0;
16869 }
16870
16871 *nexprtrees = node->nchildren;
16872 break;
16873 }
16874
16875 case SCIP_EXPR_LINEAR:
16876 {
16877 SCIP_Real* nodecoefs;
16878
16879 assert(exprtreessize >= node->nchildren);
16880 assert(node->nchildren > 0);
16881
16882 nodecoefs = (SCIP_Real*)node->data.data;
16883 assert(nodecoefs != NULL);
16884
16885 for( i = 0; i < node->nchildren; ++i )
16886 {
16887 SCIP_CALL( SCIPexprgraphGetTree(exprgraph, node->children[i], &exprtrees[i]) );
16888 exprtreecoefs[i] = nodecoefs[i];
16889 }
16890
16891 /* add constant to first summand, if nonzero; need to divide by coef of this exprtree */
16892 if( nodecoefs[node->nchildren] != 0.0 )
16893 {
16894 SCIP_EXPR* constexpr_;
16895
16896 SCIP_CALL( SCIPexprCreate(exprgraph->blkmem, &constexpr_, SCIP_EXPR_CONST, nodecoefs[node->nchildren] / exprtreecoefs[0]) );
16897 SCIP_CALL( SCIPexprtreeAddExpr(exprtrees[0], constexpr_, FALSE) );
16898 }
16899
16900 *nexprtrees = node->nchildren;
16901 break;
16902 }
16903
16904 case SCIP_EXPR_QUADRATIC:
16905 {
16906 SCIP_EXPRDATA_QUADRATIC* nodedata;
16907 SCIP_Real* lincoefs;
16908 SCIP_QUADELEM* quadelems;
16909 int nquadelems;
16910 SCIP_EXPR* expr;
16911 int j;
16912
16913 nodedata = (SCIP_EXPRDATA_QUADRATIC*)node->data.data;
16914 lincoefs = nodedata->lincoefs;
16915 quadelems = nodedata->quadelems;
16916 nquadelems = nodedata->nquadelems;
16917
16918 assert(exprtreessize >= (lincoefs != NULL ? node->nchildren : 0) + nquadelems);
16919 assert(node->nchildren > 0);
16920
16921 *nexprtrees = 0;
16922 if( lincoefs != NULL )
16923 {
16924 for( i = 0; i < node->nchildren; ++i )
16925 {
16926 if( lincoefs[i] == 0.0 )
16927 continue;
16928 SCIP_CALL( SCIPexprgraphGetTree(exprgraph, node->children[i], &exprtrees[*nexprtrees]) );
16929 exprtreecoefs[*nexprtrees] = lincoefs[i];
16930 ++*nexprtrees;
16931 }
16932 }
16933
16934 /* buffer where to store mapping of expression graph variable indices to expression tree variable indices */
16935 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &varidx, exprgraph->nvars) );
16936
16937 for( i = 0; i < nquadelems; ++i )
16938 {
16939 /* initially, no variable appears in the expression tree */
16940 for( j = 0; j < exprgraph->nvars; ++j )
16941 varidx[j] = -1; /*lint !e644*/
16942 nexprvars = 0;
16943
16944 /* create expression from the subgraph at quadelems[i].idx1 */
16945 SCIP_CALL( exprgraphNodeCreateExpr(exprgraph, node->children[quadelems[i].idx1], &expr, &nexprvars, varidx) );
16946
16947 if( quadelems[i].idx1 == quadelems[i].idx2 )
16948 {
16949 /* create expression for square of expr */
16950 SCIP_CALL( SCIPexprCreate(exprgraph->blkmem, &expr, SCIP_EXPR_SQUARE, expr) );
16951 }
16952 else
16953 {
16954 SCIP_EXPR* expr2;
16955
16956 /* create expression from the subgraph at quadelems[i].idx2, may add more variables into varidx */
16957 SCIP_CALL( exprgraphNodeCreateExpr(exprgraph, node->children[quadelems[i].idx2], &expr2, &nexprvars, varidx) );
16958 /* create expression for product */
16959 SCIP_CALL( SCIPexprCreate(exprgraph->blkmem, &expr, SCIP_EXPR_MUL, expr, expr2) );
16960 }
16961
16962 /* create expression tree for expr */
16963 SCIP_CALL( SCIPexprtreeCreate(exprgraph->blkmem, &exprtrees[*nexprtrees], expr, nexprvars, 0, NULL) );
16964
16965 /* copy variables into expression tree */
16966 if( nexprvars > 0 )
16967 {
16968 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &exprtrees[*nexprtrees]->vars, nexprvars) );
16969 for( j = 0; j < exprgraph->nvars; ++j )
16970 {
16971 assert(varidx[j] >= -1);
16972 assert(varidx[j] < nexprvars);
16973 if( varidx[j] >= 0 )
16974 exprtrees[*nexprtrees]->vars[varidx[j]] = exprgraph->vars[j];
16975 }
16976 }
16977
16978 exprtreecoefs[*nexprtrees] = quadelems[i].coef;
16979
16980 ++*nexprtrees;
16981 }
16982
16983 /* add constant to first summand, if nonzero; need to divide by coef of this exprtree */
16984 if( nodedata->constant != 0.0 )
16985 {
16986 SCIP_EXPR* constexpr_;
16987
16988 assert(*nexprtrees > 0);
16989 SCIP_CALL( SCIPexprCreate(exprgraph->blkmem, &constexpr_, SCIP_EXPR_CONST, nodedata->constant / exprtreecoefs[0]) );
16990 SCIP_CALL( SCIPexprtreeAddExpr(exprtrees[0], constexpr_, FALSE) );
16991 }
16992
16993 BMSfreeBlockMemoryArray(exprgraph->blkmem, &varidx, exprgraph->nvars);
16994
16995 break;
16996 }
16997
16998 case SCIP_EXPR_POLYNOMIAL:
16999 {
17000 SCIP_EXPRDATA_POLYNOMIAL* nodedata;
17001 SCIP_EXPRDATA_MONOMIAL** monomials;
17002 SCIP_Real constant;
17003 int nmonomials;
17004 SCIP_EXPR* expr;
17005 int* childidxs;
17006 int j;
17007
17008 nodedata = (SCIP_EXPRDATA_POLYNOMIAL*)node->data.data;
17009 monomials = nodedata->monomials;
17010 nmonomials = nodedata->nmonomials;
17011 constant = nodedata->constant;
17012
17013 assert(exprtreessize >= nmonomials);
17014 assert(node->nchildren > 0);
17015
17016 *nexprtrees = 0;
17017
17018 /* buffer where to store mapping of expression graph variable indices to expression tree variable indices */
17019 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &varidx, exprgraph->nvars) );
17020
17021 for( i = 0; i < nmonomials; ++i )
17022 {
17023 /* initially, no variable appears in the expression tree */
17024 for( j = 0; j < exprgraph->nvars; ++j )
17025 varidx[j] = -1;
17026 nexprvars = 0;
17027
17028 if( monomials[i]->nfactors == 1 )
17029 {
17030 /* create expression from the subgraph at only factor */
17031 SCIP_CALL( exprgraphNodeCreateExpr(exprgraph, node->children[monomials[i]->childidxs[0]], &expr, &nexprvars, varidx) );
17032
17033 /* put exponent in, if not 1.0 */
17034 if( monomials[i]->exponents[0] == 1.0 )
17035 ;
17036 else if( monomials[i]->exponents[0] == 2.0 )
17037 {
17038 SCIP_CALL( SCIPexprCreate(exprgraph->blkmem, &expr, SCIP_EXPR_SQUARE, expr) );
17039 }
17040 else if( EPSISINT(monomials[i]->exponents[0], 0.0) ) /*lint !e835*/
17041 {
17042 SCIP_CALL( SCIPexprCreate(exprgraph->blkmem, &expr, SCIP_EXPR_INTPOWER, expr, (int)monomials[i]->exponents[0]) );
17043 }
17044 else
17045 {
17046 SCIP_CALL( SCIPexprCreate(exprgraph->blkmem, &expr, SCIP_EXPR_REALPOWER, expr, monomials[i]->exponents[0]) );
17047 }
17048 }
17049 else if( monomials[i]->nfactors == 2 && monomials[i]->exponents[0] == 1.0 && monomials[i]->exponents[1] == 1.0 )
17050 {
17051 SCIP_EXPR* expr2;
17052
17053 /* create expressions for both factors */
17054 SCIP_CALL( exprgraphNodeCreateExpr(exprgraph, node->children[monomials[i]->childidxs[0]], &expr, &nexprvars, varidx) );
17055 SCIP_CALL( exprgraphNodeCreateExpr(exprgraph, node->children[monomials[i]->childidxs[1]], &expr2, &nexprvars, varidx) );
17056
17057 /* create expression for product of factors */
17058 SCIP_CALL( SCIPexprCreate(exprgraph->blkmem, &expr, SCIP_EXPR_MUL, expr, expr2) );
17059 }
17060 else
17061 {
17062 SCIP_EXPRDATA_MONOMIAL* monomial;
17063 SCIP_EXPR** exprs;
17064 int f;
17065
17066 /* create expression for each factor, assemble varidx and nexprvars
17067 * create child indices (= identity) */
17068 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &exprs, monomials[i]->nfactors) );
17069 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &childidxs, monomials[i]->nfactors) );
17070 for( f = 0; f < monomials[i]->nfactors; ++f )
17071 {
17072 SCIP_CALL( exprgraphNodeCreateExpr(exprgraph, node->children[monomials[i]->childidxs[f]], &exprs[f], &nexprvars, varidx) ); /*lint !e644*/
17073 childidxs[f] = f; /*lint !e644*/
17074 }
17075
17076 /* create monomial and polynomial expression for this monomial
17077 * add also constant here, but need to divide by monomial coefficient, since we set the exprtreecoefs to monomial coef
17078 */
17079 SCIP_CALL( SCIPexprCreateMonomial(exprgraph->blkmem, &monomial, 1.0, monomials[i]->nfactors, childidxs, monomials[i]->exponents) );
17080 SCIP_CALL( SCIPexprCreatePolynomial(exprgraph->blkmem, &expr, monomials[i]->nfactors, exprs, 1, &monomial, constant / monomials[i]->coef, FALSE) );
17081 constant = 0.0;
17082
17083 BMSfreeBlockMemoryArray(exprgraph->blkmem, &exprs, monomials[i]->nfactors);
17084 BMSfreeBlockMemoryArray(exprgraph->blkmem, &childidxs, monomials[i]->nfactors);
17085 }
17086
17087 /* create expression tree for expr */
17088 SCIP_CALL( SCIPexprtreeCreate(exprgraph->blkmem, &exprtrees[*nexprtrees], expr, nexprvars, 0, NULL) );
17089
17090 /* copy variables into expression tree */
17091 if( nexprvars > 0 )
17092 {
17093 SCIP_ALLOC( BMSallocBlockMemoryArray(exprgraph->blkmem, &exprtrees[*nexprtrees]->vars, nexprvars) );
17094 for( j = 0; j < exprgraph->nvars; ++j )
17095 {
17096 assert(varidx[j] >= -1);
17097 assert(varidx[j] < nexprvars);
17098 if( varidx[j] >= 0 )
17099 exprtrees[*nexprtrees]->vars[varidx[j]] = exprgraph->vars[j];
17100 }
17101 }
17102
17103 exprtreecoefs[*nexprtrees] = monomials[i]->coef;
17104
17105 ++*nexprtrees;
17106 }
17107
17108 /* add constant to first summand, if still nonzero; need to divide by coefficient of the this exprtree */
17109 if( constant != 0.0 )
17110 {
17111 SCIP_EXPR* constexpr_;
17112
17113 assert(*nexprtrees > 0);
17114 SCIP_CALL( SCIPexprCreate(exprgraph->blkmem, &constexpr_, SCIP_EXPR_CONST, constant / exprtreecoefs[0]) );
17115 SCIP_CALL( SCIPexprtreeAddExpr(exprtrees[0], constexpr_, FALSE) );
17116 }
17117
17118 BMSfreeBlockMemoryArray(exprgraph->blkmem, &varidx, exprgraph->nvars);
17119
17120 break;
17121 }
17122
17123 default:
17124 SCIPerrorMessage("unexpected operator type %d\n", node->op);
17125 return SCIP_ERROR;
17126 } /*lint !e788*/
17127
17128 return SCIP_OKAY;
17129 }
17130
17131 /**@} */
17132