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