Scippy

    SCIP

    Solving Constraint Integer Programs

    nlhdlr_convex.c
    Go to the documentation of this file.
    1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    2/* */
    3/* This file is part of the program and library */
    4/* SCIP --- Solving Constraint Integer Programs */
    5/* */
    6/* Copyright (c) 2002-2025 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 nlhdlr_convex.c
    26 * @ingroup DEFPLUGINS_NLHDLR
    27 * @brief nonlinear handlers for convex and concave expressions
    28 * @author Benjamin Mueller
    29 * @author Stefan Vigerske
    30 */
    31
    32#include <string.h>
    33
    34#include "scip/nlhdlr_convex.h"
    35#include "scip/pub_nlhdlr.h"
    36#include "scip/scip_expr.h"
    37#include "scip/cons_nonlinear.h"
    38#include "scip/expr_var.h"
    39#include "scip/expr_abs.h"
    41#include "scip/dbldblarith.h"
    42
    43/* fundamental nonlinear handler properties */
    44#define CONVEX_NLHDLR_NAME "convex"
    45#define CONVEX_NLHDLR_DESC "handler that identifies and estimates convex expressions"
    46#define CONVEX_NLHDLR_DETECTPRIORITY 50
    47#define CONVEX_NLHDLR_ENFOPRIORITY 50
    48
    49#define CONCAVE_NLHDLR_NAME "concave"
    50#define CONCAVE_NLHDLR_DESC "handler that identifies and estimates concave expressions"
    51#define CONCAVE_NLHDLR_DETECTPRIORITY 40
    52#define CONCAVE_NLHDLR_ENFOPRIORITY 40
    53
    54#define DEFAULT_DETECTSUM FALSE
    55#define DEFAULT_EXTENDEDFORM TRUE
    56#define DEFAULT_CVXQUADRATIC_CONVEX TRUE
    57#define DEFAULT_CVXQUADRATIC_CONCAVE FALSE
    58#define DEFAULT_CVXSIGNOMIAL TRUE
    59#define DEFAULT_CVXPRODCOMP TRUE
    60#define DEFAULT_HANDLETRIVIAL FALSE
    61#define DEFAULT_MAXPERTURB 0.01
    62
    63#define INITLPMAXVARVAL 1000.0 /**< maximal absolute value of variable for still generating a linearization cut at that point in initlp */
    64#define RANDNUMINITSEED 220802 /**< initial seed for random number generator for point perturbation */
    65
    66/*lint -e440*/
    67/*lint -e441*/
    68/*lint -e666*/
    69/*lint -e777*/
    70
    71/*
    72 * Data structures
    73 */
    74
    75/** nonlinear handler expression data */
    76struct SCIP_NlhdlrExprData
    77{
    78 SCIP_EXPR* nlexpr; /**< expression (copy) for which this nlhdlr estimates */
    79 SCIP_HASHMAP* nlexpr2origexpr; /**< mapping of our copied expression to original expression */
    80
    81 int nleafs; /**< number of distinct leafs of nlexpr, i.e., number of distinct (auxiliary) variables handled */
    82 SCIP_EXPR** leafexprs; /**< distinct leaf expressions (excluding value-expressions), thus variables */
    83};
    84
    85/** nonlinear handler data */
    86struct SCIP_NlhdlrData
    87{
    88 SCIP_Bool isnlhdlrconvex; /**< whether this data is used for the convex nlhdlr (TRUE) or the concave one (FALSE) */
    89 SCIP_SOL* evalsol; /**< solution used for evaluating expression in a different point,
    90 e.g., for facet computation of vertex-polyhedral function */
    91 SCIP_RANDNUMGEN* randnumgen; /**< random number generator used to perturb reference point in estimateGradient() */
    92
    93 /* parameters */
    94 SCIP_Bool detectsum; /**< whether to run detection when the root of an expression is a non-quadratic sum */
    95 SCIP_Bool extendedform; /**< whether to create extended formulations instead of looking for maximal possible subexpression */
    96 SCIP_Real maxperturb; /**< maximal perturbation of non-differentiable reference point */
    97
    98 /* advanced parameters (maybe remove some day) */
    99 SCIP_Bool cvxquadratic; /**< whether to use convexity check on quadratics */
    100 SCIP_Bool cvxsignomial; /**< whether to use convexity check on signomials */
    101 SCIP_Bool cvxprodcomp; /**< whether to use convexity check on product composition f(h)*h */
    102 SCIP_Bool handletrivial; /**< whether to handle trivial expressions, i.e., those where all children are variables */
    103};
    104
    105/** data struct to be be passed on to vertexpoly-evalfunction (see SCIPcomputeFacetVertexPolyhedralNonlinear) */
    106typedef struct
    107{
    112
    113/** stack used in constructExpr to store expressions that need to be investigated ("to do list") */
    114typedef struct
    115{
    116 SCIP_EXPR** stack; /**< stack elements */
    117 int stacksize; /**< allocated space (in number of pointers) */
    118 int stackpos; /**< position of top element of stack */
    119} EXPRSTACK;
    120
    121#define DECL_CURVCHECK(x) SCIP_RETCODE x( \
    122 SCIP* scip, /**< SCIP data structure */ \
    123 SCIP_EXPR* nlexpr, /**< nlhdlr-expr to check */ \
    124 SCIP_Bool isrootexpr, /**< whether nlexpr is the root from where detection has been started */ \
    125 EXPRSTACK* stack, /**< stack where to add generated leafs */ \
    126 SCIP_HASHMAP* nlexpr2origexpr, /**< mapping from our expression copy to original expression */ \
    127 SCIP_NLHDLRDATA* nlhdlrdata, /**< data of nlhdlr */ \
    128 SCIP_HASHMAP* assumevarfixed, /**< hashmap containing variables that should be assumed to be fixed, or NULL */ \
    129 SCIP_Bool* success /**< whether we found something */ \
    130 )
    131
    132/*
    133 * static methods
    134 */
    135
    136/** create nlhdlr-expression
    137 *
    138 * does not create children, i.e., assumes that this will be a leaf
    139 */
    140static
    142 SCIP* scip, /**< SCIP data structure */
    143 SCIP_HASHMAP* nlexpr2origexpr, /**< mapping from copied to original expression */
    144 SCIP_EXPR** nlhdlrexpr, /**< buffer to store created expr */
    145 SCIP_EXPR* origexpr, /**< original expression to be copied */
    146 SCIP_EXPRCURV curv /**< curvature to achieve */
    147 )
    148{
    149 assert(scip != NULL);
    150 assert(nlexpr2origexpr != NULL);
    151 assert(nlhdlrexpr != NULL);
    152 assert(origexpr != NULL);
    153
    154 if( SCIPexprGetNChildren(origexpr) == 0 )
    155 {
    156 /* for leaves, do not copy */
    157 *nlhdlrexpr = origexpr;
    158 SCIPcaptureExpr(*nlhdlrexpr);
    159 if( !SCIPhashmapExists(nlexpr2origexpr, (void*)*nlhdlrexpr) )
    160 {
    161 SCIP_CALL( SCIPhashmapInsert(nlexpr2origexpr, (void*)*nlhdlrexpr, (void*)origexpr) );
    162 }
    163 return SCIP_OKAY;
    164 }
    165
    166 /* create copy of expression, but without children */
    167 SCIP_CALL( SCIPduplicateExprShallow(scip, origexpr, nlhdlrexpr, NULL, NULL) );
    168 assert(*nlhdlrexpr != NULL); /* copies within the same SCIP must always work */
    169
    170 /* store the curvature we want to get in the curvature flag of the copied expression
    171 * it's a bit of a misuse, but once we are done with everything, this is actually correct
    172 */
    173 SCIPexprSetCurvature(*nlhdlrexpr, curv);
    174
    175 /* remember which the original expression was */
    176 SCIP_CALL( SCIPhashmapInsert(nlexpr2origexpr, (void*)*nlhdlrexpr, (void*)origexpr) );
    177
    178 return SCIP_OKAY;
    179}
    180
    181/** expand nlhdlr-expression by adding children according to original expression */
    182static
    184 SCIP* scip, /**< SCIP data structure */
    185 SCIP_HASHMAP* nlexpr2origexpr, /**< mapping from copied to original expression */
    186 SCIP_EXPR* nlhdlrexpr, /**< expression for which to create children */
    187 SCIP_EXPRCURV* childrencurv /**< curvature required for children, or NULL if to set to UNKNOWN */
    188 )
    189{
    190 SCIP_EXPR* origexpr;
    191 SCIP_EXPR* child;
    192 int nchildren;
    193 int i;
    194
    195 assert(scip != NULL);
    196 assert(nlhdlrexpr != NULL);
    197 assert(SCIPexprGetNChildren(nlhdlrexpr) == 0);
    198
    199 origexpr = (SCIP_EXPR*)SCIPhashmapGetImage(nlexpr2origexpr, (void*)nlhdlrexpr);
    200
    201 nchildren = SCIPexprGetNChildren(origexpr);
    202 if( nchildren == 0 )
    203 return SCIP_OKAY;
    204
    205 for( i = 0; i < nchildren; ++i )
    206 {
    207 SCIP_CALL( nlhdlrExprCreate(scip, nlexpr2origexpr, &child, SCIPexprGetChildren(origexpr)[i],
    208 childrencurv != NULL ? childrencurv[i] : SCIP_EXPRCURV_UNKNOWN) );
    209 SCIP_CALL( SCIPappendExprChild(scip, nlhdlrexpr, child) );
    210 /* append captures child, so we can release the capture from nlhdlrExprCreate */
    211 SCIP_CALL( SCIPreleaseExpr(scip, &child) );
    212 }
    213
    214 assert(SCIPexprGetNChildren(nlhdlrexpr) == SCIPexprGetNChildren(origexpr));
    215
    216 return SCIP_OKAY;
    217}
    218
    219/** evaluate expression at solution w.r.t. auxiliary variables */
    220static
    221SCIP_DECL_VERTEXPOLYFUN(nlhdlrExprEvalConcave)
    222{
    223 VERTEXPOLYFUN_EVALDATA* evaldata = (VERTEXPOLYFUN_EVALDATA*)funcdata;
    224 int i;
    225
    226 assert(args != NULL);
    227 assert(nargs == evaldata->nlhdlrexprdata->nleafs);
    228 assert(evaldata != NULL);
    229
    230#ifdef SCIP_MORE_DEBUG
    231 SCIPdebugMsg(evaldata->scip, "eval vertexpolyfun at\n");
    232#endif
    233 for( i = 0; i < nargs; ++i )
    234 {
    235#ifdef SCIP_MORE_DEBUG
    236 SCIPdebugMsg(evaldata->scip, " <%s> = %g\n",
    237 SCIPvarGetName(SCIPgetVarExprVar(evaldata->nlhdlrexprdata->leafexprs[i])), args[i]);
    238#endif
    239 SCIP_CALL_ABORT( SCIPsetSolVal(evaldata->scip, evaldata->evalsol,
    240 SCIPgetVarExprVar(evaldata->nlhdlrexprdata->leafexprs[i]), args[i]) );
    241 }
    242
    243 SCIP_CALL_ABORT( SCIPevalExpr(evaldata->scip, evaldata->nlhdlrexprdata->nlexpr, evaldata->evalsol, 0L) );
    244
    245 return SCIPexprGetEvalValue(evaldata->nlhdlrexprdata->nlexpr);
    246}
    247
    248/** initialize expression stack */
    249static
    251 SCIP* scip, /**< SCIP data structure */
    252 EXPRSTACK* exprstack, /**< stack to initialize */
    253 int initsize /**< initial size */
    254 )
    255{
    256 assert(scip != NULL);
    257 assert(exprstack != NULL);
    258 assert(initsize > 0);
    259
    260 SCIP_CALL( SCIPallocBufferArray(scip, &exprstack->stack, initsize) );
    261 exprstack->stacksize = initsize;
    262 exprstack->stackpos = -1;
    263
    264 return SCIP_OKAY;
    265}
    266
    267/** free expression stack */
    268static
    270 SCIP* scip, /**< SCIP data structure */
    271 EXPRSTACK* exprstack /**< free expression stack */
    272 )
    273{
    274 assert(scip != NULL);
    275 assert(exprstack != NULL);
    276
    277 SCIPfreeBufferArray(scip, &exprstack->stack);
    278}
    279
    280/** add expressions to expression stack */
    281static
    283 SCIP* scip, /**< SCIP data structure */
    284 EXPRSTACK* exprstack, /**< expression stack */
    285 int nexprs, /**< number of expressions to push */
    286 SCIP_EXPR** exprs /**< expressions to push */
    287 )
    288{
    289 assert(scip != NULL);
    290 assert(exprstack != NULL);
    291
    292 if( nexprs == 0 )
    293 return SCIP_OKAY;
    294
    295 assert(exprs != NULL);
    296
    297 if( exprstack->stackpos+1 + nexprs > exprstack->stacksize ) /*lint !e644*/
    298 {
    299 exprstack->stacksize = SCIPcalcMemGrowSize(scip, exprstack->stackpos+1 + nexprs); /*lint !e644*/
    300 SCIP_CALL( SCIPreallocBufferArray(scip, &exprstack->stack, exprstack->stacksize) );
    301 }
    302
    303 memcpy(exprstack->stack + (exprstack->stackpos+1), exprs, nexprs * sizeof(SCIP_EXPR*)); /*lint !e679*/ /*lint !e737*/ /*lint !e420*/
    304 exprstack->stackpos += nexprs;
    305
    306 return SCIP_OKAY;
    307}
    308
    309/** gives expression from top of expression stack and removes it from stack */
    310static
    312 EXPRSTACK* exprstack /**< expression stack */
    313 )
    314{
    315 assert(exprstack != NULL);
    316 assert(exprstack->stackpos >= 0);
    317
    318 return exprstack->stack[exprstack->stackpos--];
    319}
    320
    321/** indicate whether expression stack is empty */
    322static
    324 EXPRSTACK* exprstack /**< expression stack */
    325 )
    326{
    327 assert(exprstack != NULL);
    328
    329 return exprstack->stackpos < 0;
    330}
    331
    332/** looks whether given expression is (proper) quadratic and has a given curvature
    333 *
    334 * If having a given curvature, currently require all arguments of quadratic to be linear.
    335 * Hence, not using this for a simple square term, as curvCheckExprhdlr may provide a better condition on argument curvature then.
    336 * Also we wouldn't do anything useful for a single bilinear term.
    337 * Thus, run on sum's only.
    338 */
    339static
    340DECL_CURVCHECK(curvCheckQuadratic)
    341{ /*lint --e{715}*/
    342 SCIP_EXPR* expr;
    343 SCIP_EXPRCURV presentcurv;
    344 SCIP_EXPRCURV wantedcurv;
    345 SCIP_HASHSET* lonelysquares = NULL;
    346 SCIP_Bool isquadratic;
    347 int nbilinexprs;
    348 int nquadexprs;
    349 int i;
    350
    351 assert(nlexpr != NULL);
    352 assert(stack != NULL);
    353 assert(nlexpr2origexpr != NULL);
    354 assert(success != NULL);
    355
    356 *success = FALSE;
    357
    358 if( !nlhdlrdata->cvxquadratic )
    359 return SCIP_OKAY;
    360
    361 if( !SCIPisExprSum(scip, nlexpr) )
    362 return SCIP_OKAY;
    363
    364 wantedcurv = SCIPexprGetCurvature(nlexpr);
    365 if( wantedcurv == SCIP_EXPRCURV_LINEAR )
    366 return SCIP_OKAY;
    367 assert(wantedcurv == SCIP_EXPRCURV_CONVEX || wantedcurv == SCIP_EXPRCURV_CONCAVE);
    368
    369 expr = (SCIP_EXPR*)SCIPhashmapGetImage(nlexpr2origexpr, (void*)nlexpr);
    370 assert(expr != NULL);
    371
    372 /* check whether quadratic */
    373 SCIP_CALL( SCIPcheckExprQuadratic(scip, expr, &isquadratic) );
    374
    375 /* if not quadratic, then give up here */
    376 if( !isquadratic )
    377 return SCIP_OKAY;
    378
    379 SCIPexprGetQuadraticData(expr, NULL, NULL, NULL, NULL, &nquadexprs, &nbilinexprs, NULL, NULL);
    380
    381 /* if only single square term (+linear), then give up here (let curvCheckExprhdlr handle this) */
    382 if( nquadexprs <= 1 )
    383 return SCIP_OKAY;
    384
    385 /* if root expression is only sum of squares (+linear) and detectsum is disabled, then give up here, too */
    386 if( isrootexpr && !nlhdlrdata->detectsum && nbilinexprs == 0 )
    387 return SCIP_OKAY;
    388
    389 /* get curvature of quadratic
    390 * TODO as we know what curvature we want, we could first do some simple checks like computing xQx for a random x
    391 */
    392 SCIP_CALL( SCIPcomputeExprQuadraticCurvature(scip, expr, &presentcurv, assumevarfixed, FALSE) );
    393
    394 /* if not having desired curvature, return */
    395 if( presentcurv != wantedcurv )
    396 return SCIP_OKAY;
    397
    398 *success = TRUE;
    399
    400 if( !nlhdlrdata->detectsum )
    401 {
    402 /* first step towards block-decomposition of quadratic term:
    403 * collect all square-expressions (in original expr) which have no adjacent bilinear term
    404 * we will treat these x^2 as linear, i.e., add an auxvar for them, so x^2 maybe linearized
    405 * more efficiently (in particular if x is discrete)
    406 */
    407 SCIP_CALL( SCIPhashsetCreate(&lonelysquares, SCIPblkmem(scip), nquadexprs) );
    408 for( i = 0; i < nquadexprs; ++i )
    409 {
    410 int nadjbilin;
    411 SCIP_EXPR* sqrexpr;
    412
    413 SCIPexprGetQuadraticQuadTerm(expr, i, NULL, NULL, NULL, &nadjbilin, NULL, &sqrexpr);
    414 if( nadjbilin == 0 )
    415 {
    416 assert(sqrexpr != NULL);
    417 SCIP_CALL( SCIPhashsetInsert(lonelysquares, SCIPblkmem(scip), (void*)sqrexpr) );
    418 }
    419 }
    420 }
    421
    422 /* add immediate children to nlexpr */
    423 SCIP_CALL( nlhdlrExprGrowChildren(scip, nlexpr2origexpr, nlexpr, NULL) );
    424 assert(SCIPexprGetNChildren(nlexpr) == SCIPexprGetNChildren(expr));
    425
    426 /* put children that are not square or product on stack
    427 * grow child for children that are square or product and put this child on stack
    428 * require all children to be linear
    429 */
    430 for( i = 0; i < SCIPexprGetNChildren(nlexpr); ++i )
    431 {
    432 SCIP_EXPR* child;
    434
    435 child = SCIPexprGetChildren(nlexpr)[i];
    436 assert(child != NULL);
    437
    438 assert(SCIPhashmapGetImage(nlexpr2origexpr, (void*)child) == SCIPexprGetChildren(expr)[i]);
    439
    440 if( SCIPisExprPower(scip, child) && SCIPgetExponentExprPow(child) == 2.0 &&
    441 (lonelysquares == NULL || !SCIPhashsetExists(lonelysquares, SCIPexprGetChildren(expr)[i])) )
    442 {
    443 /* square term that isn't lonely, i.e., orig-version of child is a square-expr and nadjbilin>0 */
    444 SCIP_CALL( nlhdlrExprGrowChildren(scip, nlexpr2origexpr, child, curvlinear) );
    445 assert(SCIPexprGetNChildren(child) == 1);
    446 SCIP_CALL( exprstackPush(scip, stack, 1, SCIPexprGetChildren(child)) );
    447 }
    448 else if( SCIPisExprProduct(scip, child) && SCIPexprGetNChildren(SCIPexprGetChildren(expr)[i]) == 2 )
    449 /* using original version of child here as NChildren(child)==0 atm */
    450 {
    451 /* bilinear term */
    452 SCIP_CALL( nlhdlrExprGrowChildren(scip, nlexpr2origexpr, child, curvlinear) );
    453 assert(SCIPexprGetNChildren(child) == 2);
    454 SCIP_CALL( exprstackPush(scip, stack, 2, SCIPexprGetChildren(child)) );
    455 }
    456 else
    457 {
    458 /* linear term (or term to be considered as linear) or lonely square term
    459 * if we want extended formulations, then require linearity, so an auxvar will be introduced if it is nonlinear
    460 * if we do not want extended formulations, then the term needs to have curvature "wantedcurv"
    461 * thus, if the coef is negative, then the child needs to have the curvature opposite to "wantedcurv"
    462 */
    463 if( nlhdlrdata->extendedform )
    465 else
    466 SCIPexprSetCurvature(child, SCIPexprcurvMultiply(SCIPgetCoefsExprSum(nlexpr)[i], wantedcurv));
    467 SCIP_CALL( exprstackPush(scip, stack, 1, &child) );
    468 }
    469 }
    470
    471 if( lonelysquares != NULL )
    472 SCIPhashsetFree(&lonelysquares, SCIPblkmem(scip));
    473
    474 return SCIP_OKAY;
    475}
    476
    477/** looks whether top of given expression looks like a signomial that can have a given curvature
    478 *
    479 * e.g., sqrt(x)*sqrt(y) is convex if x,y >= 0 and x and y are convex
    480 *
    481 * unfortunately, doesn't work for tls, because i) it's originally sqrt(x*y), and ii) it is expanded into some sqrt(z*y+y);
    482 * but works for cvxnonsep_nsig
    483 */
    484static
    485DECL_CURVCHECK(curvCheckSignomial)
    486{ /*lint --e{715}*/
    487 SCIP_EXPR* expr;
    488 SCIP_EXPR* child;
    489 SCIP_Real* exponents;
    490 SCIP_INTERVAL* bounds;
    491 SCIP_EXPRCURV* curv;
    492 int nfactors;
    493 int i;
    494
    495 assert(nlexpr != NULL);
    496 assert(stack != NULL);
    497 assert(nlexpr2origexpr != NULL);
    498 assert(success != NULL);
    499
    500 *success = FALSE;
    501
    502 if( !nlhdlrdata->cvxsignomial )
    503 return SCIP_OKAY;
    504
    505 if( !SCIPisExprProduct(scip, nlexpr) )
    506 return SCIP_OKAY;
    507
    508 expr = (SCIP_EXPR*)SCIPhashmapGetImage(nlexpr2origexpr, (void*)nlexpr);
    509 assert(expr != NULL);
    510
    511 nfactors = SCIPexprGetNChildren(expr);
    512 if( nfactors <= 1 ) /* boooring */
    513 return SCIP_OKAY;
    514
    515 SCIP_CALL( SCIPallocBufferArray(scip, &exponents, nfactors) );
    516 SCIP_CALL( SCIPallocBufferArray(scip, &bounds, nfactors) );
    517 SCIP_CALL( SCIPallocBufferArray(scip, &curv, nfactors) );
    518
    519 for( i = 0; i < nfactors; ++i )
    520 {
    521 child = SCIPexprGetChildren(expr)[i];
    522 assert(child != NULL);
    523
    524 if( !SCIPisExprPower(scip, child) )
    525 {
    526 exponents[i] = 1.0;
    528 bounds[i] = SCIPexprGetActivity(child);
    529 }
    530 else
    531 {
    532 exponents[i] = SCIPgetExponentExprPow(child);
    534 bounds[i] = SCIPexprGetActivity(SCIPexprGetChildren(child)[0]);
    535 }
    536 }
    537
    539 nfactors, exponents, bounds, curv) )
    540 goto TERMINATE;
    541
    542 /* add immediate children to nlexpr
    543 * some entries in curv actually apply to arguments of pow's, will correct this next
    544 */
    545 SCIP_CALL( nlhdlrExprGrowChildren(scip, nlexpr2origexpr, nlexpr, curv) );
    546 assert(SCIPexprGetNChildren(nlexpr) == nfactors);
    547
    548 /* put children that are not power on stack
    549 * grow child for children that are power and put this child on stack
    550 * if extendedform, then require children to be linear
    551 * unless they are linear, an auxvar will be introduced for them and thus they will be handled as var here
    552 */
    553 for( i = 0; i < nfactors; ++i )
    554 {
    555 child = SCIPexprGetChildren(nlexpr)[i];
    556 assert(child != NULL);
    557
    558 if( SCIPisExprPower(scip, child) )
    559 {
    560 SCIP_CALL( nlhdlrExprGrowChildren(scip, nlexpr2origexpr, child, &curv[i]) );
    561 assert(SCIPexprGetNChildren(child) == 1);
    562 child = SCIPexprGetChildren(child)[0];
    563 }
    564 assert(SCIPexprGetNChildren(child) == 0);
    565
    566 if( nlhdlrdata->extendedform )
    567 {
    569#ifdef SCIP_DEBUG
    570 SCIPinfoMessage(scip, NULL, "Extendedform: Require linearity for ");
    571 SCIPprintExpr(scip, child, NULL);
    572 SCIPinfoMessage(scip, NULL, "\n");
    573#endif
    574 }
    575
    576 SCIP_CALL( exprstackPush(scip, stack, 1, &child) );
    577 }
    578
    579 *success = TRUE;
    580
    581TERMINATE:
    583 SCIPfreeBufferArray(scip, &bounds);
    584 SCIPfreeBufferArray(scip, &exponents);
    585
    586 return SCIP_OKAY;
    587}
    588
    589/** looks for \f$f(c h(x)+d) h(x) \cdot \text{constant}\f$ and tries to conclude conditions on curvature
    590 *
    591 * Assume \f$h\f$ is univariate:
    592 * - First derivative is \f$f'(c h + d) c h' h + f(c h + d) h'\f$.
    593 * - Second derivative is \f{align}{&f''(c h + d) c h' c h' h + f'(c h + d) (c h'' h + c h' h') + f'(c h + d) c h' h' + f(c h + d) h'' \\
    594 * =& f''(c h + d) c^2 h'^2 h + f'(c h + d) c h'' h + 2 f'(c h + d) c h'^2 + f(c h + d) h''.\f}
    595 * Remove always positive factors leaves \f[f''(c h + d) h,\quad f'(c h + d) c h'' h,\quad f'(c h + d) c,\quad f(c h + d) h''.\f]
    596 * For convexity we want all these terms to be nonnegative. For concavity we want all of them to be nonpositive.
    597 * Note, that in each term either both \f$f'(c h + d)\f$ and \f$c\f$ occur, or none of them.
    598 * - Thus, \f$f(c h(x) + d)h(x)\f$ is convex if \f$cf\f$ is monotonically increasing \f$(c f' \geq 0)\f$ and either
    599 * - \f$f\f$ is convex \f$(f'' \geq 0)\f$ and \f$h\f$ is nonnegative \f$(h \geq 0)\f$ and \f$h\f$ is convex \f$(h'' \geq 0)\f$ and [\f$f\f$ is nonnegative \f$(f \geq 0)\f$ or \f$h\f$ is linear \f$(h''=0)\f$], or
    600 * - \f$f\f$ is concave \f$(f'' \leq 0)\f$ and \f$h\f$ is nonpositive \f$(h \leq 0)\f$ and \f$h\f$ is concave \f$(h'' \leq 0)\f$ and [\f$f\f$ is nonpositive \f$(f \leq 0)\f$ or \f$h\f$ is linear \f$(h''=0)\f$].
    601 * - Further, \f$f(c h(x) + d)h(x)\f$ is concave if \f$cf\f$ is monotonically decreasing \f$(c f' \leq 0)\f$ and either
    602 * - f is convex \f$(f'' \geq 0)\f$ and \f$h\f$ is nonpositive \f$(h \leq 0)\f$ and \f$h\f$ is concave \f$(h'' \leq 0)\f$ and [\f$f\f$ is nonnegative \f$(f \geq 0)\f$ or \f$h\f$ is linear \f$(h''=0)\f$], or
    603 * - f is concave \f$(f'' \leq 0)\f$ and \f$h\f$ is nonnegative \f$(h >= 0)\f$ and \f$h\f$ is convex \f$(h'' \geq 0)\f$ and [\f$f\f$ is nonpositive \f$(f \leq 0)\f$ or \f$h\f$ is linear \f$(h''=0)\f$].
    604 *
    605 * This should hold also for multivariate and linear \f$h\f$, as things are invariant under linear transformations.
    606 * Similar to signomial, I'll assume that this will also hold for other multivariate \f$h\f$ (someone has a formal proof?).
    607 */
    608static
    609DECL_CURVCHECK(curvCheckProductComposite)
    610{ /*lint --e{715}*/
    611 SCIP_EXPR* expr;
    612 SCIP_EXPR* f;
    613 SCIP_EXPR* h = NULL;
    614 SCIP_Real c = 0.0;
    615 SCIP_EXPR* ch = NULL; /* c * h */
    616 SCIP_Real d;
    617 SCIP_INTERVAL fbounds;
    618 SCIP_INTERVAL hbounds;
    619 SCIP_MONOTONE fmonotonicity;
    620 SCIP_EXPRCURV desiredcurv;
    621 SCIP_EXPRCURV hcurv;
    622 SCIP_EXPRCURV dummy;
    623 int fidx;
    624
    625 assert(nlexpr != NULL);
    626 assert(stack != NULL);
    627 assert(nlexpr2origexpr != NULL);
    628 assert(success != NULL);
    629
    630 *success = FALSE;
    631
    632 if( !nlhdlrdata->cvxprodcomp )
    633 return SCIP_OKAY;
    634
    635 if( !SCIPisExprProduct(scip, nlexpr) )
    636 return SCIP_OKAY;
    637
    638 expr = (SCIP_EXPR*)SCIPhashmapGetImage(nlexpr2origexpr, (void*)nlexpr);
    639 assert(expr != NULL);
    640
    641 if( SCIPexprGetNChildren(expr) != 2 )
    642 return SCIP_OKAY;
    643
    644 /* check whether we have f(c * h(x)) * h(x) or h(x) * f(c * h(x)) */
    645 for( fidx = 0; fidx <= 1; ++fidx )
    646 {
    647 f = SCIPexprGetChildren(expr)[fidx];
    648
    649 if( SCIPexprGetNChildren(f) != 1 )
    650 continue;
    651
    652 ch = SCIPexprGetChildren(f)[0];
    653 c = 1.0;
    654 h = ch;
    655
    656 /* check whether ch is of the form c*h(x), then switch h to child ch */
    657 if( SCIPisExprSum(scip, ch) && SCIPexprGetNChildren(ch) == 1 )
    658 {
    659 c = SCIPgetCoefsExprSum(ch)[0];
    660 h = SCIPexprGetChildren(ch)[0];
    661 assert(c != 1.0 || SCIPgetConstantExprSum(ch) != 0.0); /* we could handle this, but it should have been simplified away */
    662 }
    663
    664#ifndef NLHDLR_CONVEX_UNITTEST
    665 /* can assume that duplicate subexpressions have been identified and comparing pointer is sufficient */
    666 if( SCIPexprGetChildren(expr)[1-fidx] == h )
    667#else
    668 /* called from unittest -> duplicate subexpressions were not identified -> compare more expensively */
    669 if( SCIPcompareExpr(scip, SCIPexprGetChildren(expr)[1-fidx], h) == 0 )
    670#endif
    671 break;
    672 }
    673 if( fidx == 2 )
    674 return SCIP_OKAY;
    675
    676 /* constant of c*h(x)+d */
    677 d = h != ch ? SCIPgetConstantExprSum(ch) : 0.0;
    678
    679#ifdef SCIP_MORE_DEBUG
    680 SCIPinfoMessage(scip, NULL, "f(c*h+d)*h with f = %s, c = %g, d = %g, h = ", SCIPexprhdlrGetName(SCIPexprGetHdlr(f)), c, d);
    682 SCIPinfoMessage(scip, NULL, "\n");
    683#endif
    684
    685 assert(c != 0.0);
    686
    689 fbounds = SCIPexprGetActivity(f);
    690 hbounds = SCIPexprGetActivity(h);
    691
    692 /* if h has mixed sign, then cannot conclude anything */
    693 if( hbounds.inf < 0.0 && hbounds.sup > 0.0 )
    694 return SCIP_OKAY;
    695
    696 /* If we have some convex or concave x*abs(c*x+d), then gradients at x=-d/c may be very wrong due to
    697 * rounding errors and non-differentiability of abs() at zero (#3411). Therefore, we skip handling
    698 * such expression in this nonlinear handler when one of the bounds of c*x+d is very close to zero.
    699 * (If zero is in between the bounds of c*x+d, then the composition wouldn't be regarded as convex/concave anyway.)
    700 */
    701 if( SCIPisExprAbs(scip, f) && (SCIPisZero(scip, c*hbounds.inf+d) || SCIPisZero(scip, c*hbounds.sup+d)) )
    702 return SCIP_OKAY;
    703
    704 SCIP_CALL( SCIPcallExprMonotonicity(scip, f, 0, &fmonotonicity) );
    705
    706 /* if f is not monotone, then cannot conclude anything */
    707 if( fmonotonicity == SCIP_MONOTONE_UNKNOWN )
    708 return SCIP_OKAY;
    709
    710 /* curvature we want to achieve (negate if product has negative coef) */
    712
    713 /* now check the conditions as stated above */
    714 if( desiredcurv == SCIP_EXPRCURV_CONVEX )
    715 {
    716 /* f(c h(x)+d)h(x) is convex if c*f is monotonically increasing (c f' >= 0) and either
    717 * - f is convex (f'' >= 0) and h is nonnegative (h >= 0) and h is convex (h'' >= 0) and [f is nonnegative (f >= 0) or h is linear (h''=0)], or
    718 * - f is concave (f'' <= 0) and h is nonpositive (h <= 0) and h is concave (h'' <= 0) and [f is nonpositive (f <= 0) or h is linear (h''=0)]
    719 * as the curvature requirements on f are on f only and not the composition f(h), we can ignore the requirements returned by SCIPcallExprCurvature (last arg)
    720 */
    721 if( (c > 0.0 && fmonotonicity != SCIP_MONOTONE_INC) || (c < 0.0 && fmonotonicity != SCIP_MONOTONE_DEC) )
    722 return SCIP_OKAY;
    723
    724 /* check whether f can be convex (h>=0) or concave (h<=0), resp., and derive requirements for h */
    725 if( hbounds.inf >= 0 )
    726 {
    727 SCIP_CALL( SCIPcallExprCurvature(scip, f, SCIP_EXPRCURV_CONVEX, success, &dummy) );
    728
    729 /* now h also needs to be convex; and if f < 0, then h actually needs to be linear */
    730 if( fbounds.inf < 0.0 )
    731 hcurv = SCIP_EXPRCURV_LINEAR;
    732 else
    733 hcurv = SCIP_EXPRCURV_CONVEX;
    734 }
    735 else
    736 {
    737 SCIP_CALL( SCIPcallExprCurvature(scip, f, SCIP_EXPRCURV_CONCAVE, success, &dummy) );
    738
    739 /* now h also needs to be concave; and if f > 0, then h actually needs to be linear */
    740 if( fbounds.sup > 0.0 )
    741 hcurv = SCIP_EXPRCURV_LINEAR;
    742 else
    743 hcurv = SCIP_EXPRCURV_CONCAVE;
    744 }
    745 }
    746 else
    747 {
    748 /* f(c h(x)+d)*h(x) is concave if c*f is monotonically decreasing (c f' <= 0) and either
    749 * - f is convex (f'' >= 0) and h is nonpositive (h <= 0) and h is concave (h'' <= 0) and [f is nonnegative (f >= 0) or h is linear (h''=0)], or
    750 * - f is concave (f'' <= 0) and h is nonnegative (h >= 0) and h is convex (h'' >= 0) and [f is nonpositive (f <= 0) or h is linear (h''=0)]
    751 * as the curvature requirements on f are on f only and not the composition f(h), we can ignore the requirements returned by SCIPcallExprCurvature (last arg)
    752 */
    753 if( (c > 0.0 && fmonotonicity != SCIP_MONOTONE_DEC) || (c < 0.0 && fmonotonicity != SCIP_MONOTONE_INC) )
    754 return SCIP_OKAY;
    755
    756 /* check whether f can be convex (h<=0) or concave (h>=0), resp., and derive requirements for h */
    757 if( hbounds.sup <= 0 )
    758 {
    759 SCIP_CALL( SCIPcallExprCurvature(scip, f, SCIP_EXPRCURV_CONVEX, success, &dummy) );
    760
    761 /* now h also needs to be concave; and if f < 0, then h actually needs to be linear */
    762 if( fbounds.inf < 0.0 )
    763 hcurv = SCIP_EXPRCURV_LINEAR;
    764 else
    765 hcurv = SCIP_EXPRCURV_CONCAVE;
    766 }
    767 else
    768 {
    769 SCIP_CALL( SCIPcallExprCurvature(scip, f, SCIP_EXPRCURV_CONCAVE, success, &dummy) );
    770
    771 /* now h also needs to be convex; and if f > 0, then h actually needs to be linear */
    772 if( fbounds.sup > 0.0 )
    773 hcurv = SCIP_EXPRCURV_LINEAR;
    774 else
    775 hcurv = SCIP_EXPRCURV_CONVEX;
    776 }
    777 }
    778
    779 if( !*success )
    780 return SCIP_OKAY;
    781
    782 /* add immediate children (f and ch) to nlexpr; we set required curvature for h further below */
    783 SCIP_CALL( nlhdlrExprGrowChildren(scip, nlexpr2origexpr, nlexpr, NULL) );
    784 assert(SCIPexprGetNChildren(nlexpr) == 2);
    785
    786 /* copy of f (and h) should have same child position in nlexpr as f (and h) has on expr (resp) */
    787 assert(SCIPhashmapGetImage(nlexpr2origexpr, (void*)SCIPexprGetChildren(nlexpr)[fidx]) == (void*)f);
    788#ifndef NLHDLR_CONVEX_UNITTEST
    789 assert(SCIPhashmapGetImage(nlexpr2origexpr, (void*)SCIPexprGetChildren(nlexpr)[1-fidx]) == (void*)h);
    790#endif
    791 /* push this h onto stack for further checking */
    792 SCIP_CALL( exprstackPush(scip, stack, 1, &(SCIPexprGetChildren(nlexpr)[1-fidx])) );
    793
    794 /* if we prefer extended formulations, then we always want h() to be linear */
    795 if( nlhdlrdata->extendedform )
    796 hcurv = SCIP_EXPRCURV_LINEAR;
    797
    798 /* h-child of product should have curvature hcurv */
    799 SCIPexprSetCurvature(SCIPexprGetChildren(nlexpr)[1-fidx], hcurv);
    800
    801 if( h != ch )
    802 {
    803 /* add copy of ch as child to copy of f */
    804 SCIP_CALL( nlhdlrExprGrowChildren(scip, nlexpr2origexpr, SCIPexprGetChildren(nlexpr)[fidx], NULL) );
    805 assert(SCIPexprGetNChildren(SCIPexprGetChildren(nlexpr)[fidx]) == 1);
    806 assert(SCIPhashmapGetImage(nlexpr2origexpr, (void*)SCIPexprGetChildren(SCIPexprGetChildren(nlexpr)[fidx])[0]) == (void*)ch);
    807
    808 /* add copy of h (created above as child of product) as child in copy of ch */
    810 SCIPexprGetChildren(SCIPexprGetChildren(nlexpr)[fidx])[0] /* copy of ch */,
    811 SCIPexprGetChildren(nlexpr)[1-fidx] /* copy of h */) );
    812 }
    813 else
    814 {
    815 /* add copy of h (created above as child of product) as child in copy of f */
    817 SCIPexprGetChildren(nlexpr)[fidx] /* copy of f */,
    818 SCIPexprGetChildren(nlexpr)[1-fidx] /* copy of h */) );
    819 }
    820
    821 return SCIP_OKAY;
    822}
    823
    824/** use expression handlers curvature callback to check whether given curvature can be achieved */
    825static
    826DECL_CURVCHECK(curvCheckExprhdlr)
    827{ /*lint --e{715}*/
    828 SCIP_EXPR* origexpr;
    829 int nchildren;
    830 SCIP_EXPRCURV* childcurv;
    831
    832 assert(nlexpr != NULL);
    833 assert(stack != NULL);
    834 assert(nlexpr2origexpr != NULL);
    835 assert(success != NULL);
    836
    837 origexpr = (SCIP_EXPR*)SCIPhashmapGetImage(nlexpr2origexpr, nlexpr);
    838 assert(origexpr != NULL);
    839 nchildren = SCIPexprGetNChildren(origexpr);
    840
    841 if( nchildren == 0 )
    842 {
    843 /* if originally no children, then should be var or value, which should have every curvature,
    844 * so should always be success
    845 */
    846 SCIP_CALL( SCIPcallExprCurvature(scip, origexpr, SCIPexprGetCurvature(nlexpr), success, NULL) );
    847 assert(*success);
    848
    849 return SCIP_OKAY;
    850 }
    851
    852 *success = FALSE;
    853
    854 /* ignore sums if > 1 children
    855 * NOTE: this means that for something like 1+f(x), even if f is a trivial convex expression, we would handle 1+f(x)
    856 * with this nlhdlr, instead of formulating this as 1+z and handling z=f(x) with the default nlhdlr, i.e., the exprhdlr
    857 * today, I prefer handling this here, as it avoids introducing an extra auxiliary variable
    858 */
    859 if( isrootexpr && !nlhdlrdata->detectsum && SCIPisExprSum(scip, nlexpr) && nchildren > 1 )
    860 return SCIP_OKAY;
    861
    862 SCIP_CALL( SCIPallocBufferArray(scip, &childcurv, nchildren) );
    863
    864 /* check whether and under which conditions origexpr can have desired curvature */
    865 SCIP_CALL( SCIPcallExprCurvature(scip, origexpr, SCIPexprGetCurvature(nlexpr), success, childcurv) );
    866#ifdef SCIP_MORE_DEBUG
    867 SCIPprintExpr(scip, origexpr, NULL);
    868 SCIPinfoMessage(scip, NULL, " is %s? %d\n", SCIPexprcurvGetName(SCIPexprGetCurvature(nlexpr)), *success);
    869#endif
    870 if( !*success )
    871 goto TERMINATE;
    872
    873 /* if origexpr can have curvature curv, then don't treat it as leaf, but include its children */
    874 SCIP_CALL( nlhdlrExprGrowChildren(scip, nlexpr2origexpr, nlexpr, childcurv) );
    875 assert(SCIPexprGetChildren(nlexpr) != NULL);
    876 assert(SCIPexprGetNChildren(nlexpr) == nchildren);
    877
    878 /* If we prefer extended formulations, then require all children to be linear.
    879 * Unless they are, auxvars will be introduced and they will be handles as variables, which can be an
    880 * advantage in the context of extended formulations.
    881 */
    882 if( nlhdlrdata->extendedform )
    883 {
    884 int i;
    885 for( i = 0; i < nchildren; ++i )
    887#ifdef SCIP_DEBUG
    888 SCIPinfoMessage(scip, NULL, "require linearity for children of ");
    889 SCIPprintExpr(scip, origexpr, NULL);
    890 SCIPinfoMessage(scip, NULL, "\n");
    891#endif
    892 }
    893
    894 /* add children expressions to to-do list (stack) */
    895 SCIP_CALL( exprstackPush(scip, stack, nchildren, SCIPexprGetChildren(nlexpr)) );
    896
    897TERMINATE:
    898 SCIPfreeBufferArray(scip, &childcurv);
    899
    900 return SCIP_OKAY;
    901}
    902
    903/** curvature check and expression-growing methods
    904 *
    905 * some day this could be plugins added by users at runtime, but for now we have a fixed list here
    906 * @note curvCheckExprhdlr should be last
    907 */
    908static DECL_CURVCHECK((*CURVCHECKS[])) = { curvCheckProductComposite, curvCheckSignomial, curvCheckQuadratic, curvCheckExprhdlr };
    909/** number of curvcheck methods */
    910static const int NCURVCHECKS = sizeof(CURVCHECKS) / sizeof(void*);
    911
    912/** checks whether expression is a sum with more than one child and each child being a variable or going to be a variable if `expr` is a nlhdlr-specific copy
    913 *
    914 * Within constructExpr(), we can have an expression of any type which is a copy of an original expression,
    915 * but without children. At the end of constructExpr() (after the loop with the stack), these expressions
    916 * will remain as leafs and will eventually be turned into variables in collectLeafs(). Thus, we treat
    917 * every child that has no children as if it were a variable. Theoretically, there is still the possibility
    918 * that it could be a constant (value-expression), but simplify should have removed these.
    919 */
    920static
    922 SCIP* scip, /**< SCIP data structure */
    923 SCIP_EXPR* expr /**< expression to check */
    924 )
    925{
    926 int nchildren;
    927 int c;
    928
    929 assert(expr != NULL);
    930
    931 if( !SCIPisExprSum(scip, expr) )
    932 return FALSE;
    933
    934 nchildren = SCIPexprGetNChildren(expr);
    935 if( nchildren <= 1 )
    936 return FALSE;
    937
    938 for( c = 0; c < nchildren; ++c )
    939 /*if( !SCIPisExprVar(scip, SCIPexprGetChildren(expr)[c]) ) */
    940 if( SCIPexprGetNChildren(SCIPexprGetChildren(expr)[c]) > 0 )
    941 return FALSE;
    942
    943 return TRUE;
    944}
    945
    946/** constructs a subexpression (as nlhdlr-expression) of maximal size that has a given curvature
    947 *
    948 * If the curvature cannot be achieved for an expression in the original expression graph,
    949 * then this expression becomes a leaf in the nlhdlr-expression.
    950 *
    951 * Sets `*rootnlexpr` to NULL if failed.
    952 */
    953static
    955 SCIP* scip, /**< SCIP data structure */
    956 SCIP_NLHDLRDATA* nlhdlrdata, /**< nonlinear handler data */
    957 SCIP_EXPR** rootnlexpr, /**< buffer to store created expression */
    958 SCIP_HASHMAP* nlexpr2origexpr, /**< mapping from our expression copy to original expression */
    959 int* nleafs, /**< number of leafs in constructed expression */
    960 SCIP_EXPR* rootexpr, /**< expression */
    961 SCIP_EXPRCURV curv, /**< curvature to achieve */
    962 SCIP_HASHMAP* assumevarfixed, /**< hashmap containing variables that should be assumed to be fixed, or NULL */
    963 SCIP_Bool assumecurvature, /**< whether to assume that desired curvature is given (skips curvature checks) */
    964 SCIP_Bool* curvsuccess /**< pointer to store whether the curvature could be achieved
    965 * w.r.t. the original variables (might be NULL) */
    966 )
    967{
    968 SCIP_EXPR* nlexpr;
    969 EXPRSTACK stack; /* to do list: expressions where to check whether they can have the desired curvature when taking their children into account */
    970 int oldstackpos;
    971 SCIP_Bool isrootexpr = TRUE;
    972
    973 assert(scip != NULL);
    974 assert(nlhdlrdata != NULL);
    975 assert(rootnlexpr != NULL);
    976 assert(nlexpr2origexpr != NULL);
    977 assert(nleafs != NULL);
    978 assert(rootexpr != NULL);
    979 assert(curv == SCIP_EXPRCURV_CONVEX || curv == SCIP_EXPRCURV_CONCAVE);
    980
    981 /* create root expression */
    982 SCIP_CALL( nlhdlrExprCreate(scip, nlexpr2origexpr, rootnlexpr, rootexpr, curv) );
    983
    984 *nleafs = 0;
    985 if( curvsuccess != NULL )
    986 *curvsuccess = TRUE;
    987
    988 SCIP_CALL( exprstackInit(scip, &stack, 20) );
    989 SCIP_CALL( exprstackPush(scip, &stack, 1, rootnlexpr) );
    990 while( !exprstackIsEmpty(&stack) )
    991 {
    992 /* take expression from stack */
    993 nlexpr = exprstackPop(&stack);
    994 assert(nlexpr != NULL);
    995 assert(SCIPexprGetNChildren(nlexpr) == 0);
    996
    997 oldstackpos = stack.stackpos;
    998 if( nlhdlrdata->isnlhdlrconvex && !SCIPexprhdlrHasBwdiff(SCIPexprGetHdlr(nlexpr)) )
    999 {
    1000 /* if bwdiff is not implemented, then we could not generate cuts in the convex nlhdlr, so "stop" (treat nlexpr as variable) */
    1001 }
    1002 else if( !nlhdlrdata->isnlhdlrconvex && exprIsMultivarLinear(scip, (SCIP_EXPR*)SCIPhashmapGetImage(nlexpr2origexpr, (void*)nlexpr)) )
    1003 {
    1004 /* if we are in the concave handler, we would like to treat linear multivariate subexpressions by a new auxvar always,
    1005 * e.g., handle log(x+y) as log(z), z=x+y, because the estimation problem will be smaller then without making the estimator worse
    1006 * (cons_nonlinear does this, too)
    1007 * this check takes care of this when x and y are original variables
    1008 * however, it isn't unlikely that we will have sums that become linear after we add auxvars for some children
    1009 * this will be handled in a postprocessing below
    1010 * for now, the check is performed on the original expression since there is not enough information in nlexpr yet
    1011 */
    1012#ifdef SCIP_MORE_DEBUG
    1013 SCIPprintExpr(scip, SCIPhashmapGetImage(nlexpr2origexpr, (void*)nlexpr), NULL);
    1014 SCIPinfoMessage(scip, NULL, "... is a multivariate linear sum that we'll treat as auxvar\n");
    1015#endif
    1016 }
    1017 else if( SCIPexprGetCurvature(nlexpr) != SCIP_EXPRCURV_UNKNOWN && !assumecurvature )
    1018 {
    1019 /* if we are here, either convexity or concavity is required; try to check for this curvature */
    1020 SCIP_Bool success = FALSE;
    1021 int method;
    1022
    1023 /* try through curvature check methods until one succeeds */
    1024 for( method = 0; method < NCURVCHECKS; ++method )
    1025 {
    1026 SCIP_CALL( CURVCHECKS[method](scip, nlexpr, isrootexpr, &stack, nlexpr2origexpr, nlhdlrdata, assumevarfixed, &success) );
    1027 if( success )
    1028 break;
    1029 }
    1030 }
    1031 else
    1032 {
    1033 /* if we don't care about curvature in this subtree anymore (very unlikely),
    1034 * or we are told to assume that the desired curvature is present (assumecurvature==TRUE),
    1035 * then only continue iterating this subtree to assemble leaf expressions
    1036 */
    1037 SCIP_CALL( nlhdlrExprGrowChildren(scip, nlexpr2origexpr, nlexpr, NULL) );
    1038
    1039 /* add children expressions, if any, to to-do list (stack) */
    1041 }
    1042 assert(stack.stackpos >= oldstackpos); /* none of the methods above should have removed something from the stack */
    1043
    1044 isrootexpr = FALSE;
    1045
    1046 /* if nothing was added, then none of the successors of nlexpr were added to the stack
    1047 * this is either because nlexpr was already a variable or value expressions, thus a leaf,
    1048 * or because the desired curvature could not be achieved, so it will be handled as variables, thus a leaf
    1049 */
    1050 if( stack.stackpos == oldstackpos )
    1051 {
    1052 ++*nleafs;
    1053
    1054 /* check whether the new leaf is not an original variable (or constant) */
    1055 if( curvsuccess != NULL && !SCIPisExprVar(scip, nlexpr) && !SCIPisExprValue(scip, nlexpr) )
    1056 *curvsuccess = FALSE;
    1057 }
    1058 }
    1059
    1060 exprstackFree(scip, &stack);
    1061
    1062 if( !nlhdlrdata->isnlhdlrconvex && *rootnlexpr != NULL )
    1063 {
    1064 /* remove multivariate linear subexpressions, that is, change some f(z1+z2) into f(z3) (z3=z1+z2 will be done by nlhdlr_default)
    1065 * this handles the case that was not covered by the above check, which could recognize f(x+y) for x, y original variables
    1066 */
    1067 SCIP_EXPRITER* it;
    1068
    1070 SCIP_CALL( SCIPexpriterInit(it, *rootnlexpr, SCIP_EXPRITER_DFS, FALSE) );
    1072
    1073 while( !SCIPexpriterIsEnd(it) )
    1074 {
    1075 SCIP_EXPR* child;
    1076
    1077 child = SCIPexpriterGetChildExprDFS(it);
    1078 assert(child != NULL);
    1079
    1080 /* We want to change some f(x+y+z) into just f(), where f is the expression the iterator points to
    1081 * and x+y+z is child. A child of a child, e.g., z, may not be a variable yet (these are added in collectLeafs later),
    1082 * but an expression of some nonlinear type without children.
    1083 */
    1084 if( exprIsMultivarLinear(scip, child) )
    1085 {
    1086 /* turn child (x+y+z) into a sum without children
    1087 * collectLeafs() should then replace this by an auxvar
    1088 */
    1089#ifdef SCIP_MORE_DEBUG
    1090 SCIPprintExpr(scip, child, NULL);
    1091 SCIPinfoMessage(scip, NULL, "... is a multivariate linear sum that we'll treat as auxvar instead (postprocess)\n");
    1092#endif
    1093
    1094 /* TODO remove children from nlexpr2origexpr ?
    1095 * should also do this if they are not used somewhere else; we could check nuses for this
    1096 * however, it shouldn't matter to have some stray entries in the hashmap either
    1097 */
    1099 assert(SCIPexprGetNChildren(child) == 0);
    1100
    1101 (void) SCIPexpriterSkipDFS(it);
    1102 }
    1103 else
    1104 {
    1105 (void) SCIPexpriterGetNext(it);
    1106 }
    1107 }
    1108
    1109 SCIPfreeExpriter(&it);
    1110 }
    1111
    1112 if( *rootnlexpr != NULL )
    1113 {
    1114 SCIP_Bool istrivial = TRUE;
    1115
    1116 /* if handletrivial is enabled, then only require that rootnlexpr itself has required curvature (so has children; see below) and
    1117 * that we are not a trivial sum (because the previous implementation of this nlhdlr didn't allow this, either)
    1118 */
    1119 if( !nlhdlrdata->handletrivial || SCIPisExprSum(scip, *rootnlexpr) )
    1120 {
    1121 /* if all children do not have children, i.e., are variables, or will be replaced by auxvars, then free
    1122 * also if rootnlexpr has no children, then free
    1123 */
    1124 int i;
    1125 for( i = 0; i < SCIPexprGetNChildren(*rootnlexpr); ++i )
    1126 {
    1127 if( SCIPexprGetNChildren(SCIPexprGetChildren(*rootnlexpr)[i]) > 0 )
    1128 {
    1129 istrivial = FALSE;
    1130 break;
    1131 }
    1132 }
    1133 }
    1134 else if( SCIPexprGetNChildren(*rootnlexpr) > 0 ) /* if handletrivial, then just require children */
    1135 istrivial = FALSE;
    1136
    1137 if( istrivial )
    1138 {
    1139 SCIP_CALL( SCIPreleaseExpr(scip, rootnlexpr) );
    1140 }
    1141 }
    1142
    1143 return SCIP_OKAY;
    1144}
    1145
    1146/** collects (non-value) leaf expressions and ensure that they correspond to a variable (original or auxiliary)
    1147 *
    1148 * For children where we could not achieve the desired curvature, get the auxvar and replace the child by a
    1149 * var-expression that points to this auxvar.
    1150 * Collect all leaf expressions (if not a value-expression) and index them.
    1151 */
    1152static
    1154 SCIP* scip, /**< SCIP data structure */
    1155 SCIP_NLHDLREXPRDATA* nlhdlrexprdata /**< nlhdlr expression data */
    1156 )
    1157{
    1158 SCIP_EXPRITER* it;
    1159 SCIP_EXPR* nlexpr;
    1160 SCIP_HASHMAP* leaf2index;
    1161 int i;
    1162
    1163 assert(nlhdlrexprdata != NULL);
    1164 assert(nlhdlrexprdata->nlexpr != NULL);
    1165 assert(nlhdlrexprdata->nlexpr2origexpr != NULL);
    1166 /* nleafs should be the upper bound on the number of variables given by constructExpr
    1167 * leafexprs should be NULL, as this is what we want to setup here
    1168 */
    1169 assert(nlhdlrexprdata->nleafs > 0);
    1170 assert(nlhdlrexprdata->leafexprs == NULL);
    1171
    1172 /* collect all auxvars and collect all variables */
    1173 SCIP_CALL( SCIPhashmapCreate(&leaf2index, SCIPblkmem(scip), nlhdlrexprdata->nleafs) );
    1174 nlhdlrexprdata->nleafs = 0; /* we start a new count, this time skipping value-expressions */
    1175
    1177 SCIP_CALL( SCIPexpriterInit(it, nlhdlrexprdata->nlexpr, SCIP_EXPRITER_DFS, FALSE) );
    1179
    1180 for( nlexpr = SCIPexpriterGetCurrent(it); !SCIPexpriterIsEnd(it); nlexpr = SCIPexpriterGetNext(it) )
    1181 {
    1182 SCIP_EXPR* child;
    1183 SCIP_EXPR* origexpr;
    1184
    1185 assert(nlexpr != NULL);
    1186
    1187 child = SCIPexpriterGetChildExprDFS(it);
    1188
    1189 /* if the to-be-visited child has children, then it doesn't need to be replaced by a new expression (representing the auxvar) */
    1190 if( SCIPexprGetNChildren(child) > 0 )
    1191 continue;
    1192
    1193 origexpr = (SCIP_EXPR*)SCIPhashmapGetImage(nlhdlrexprdata->nlexpr2origexpr, (void*)child);
    1194 assert(origexpr != NULL);
    1195
    1196 if( SCIPexprGetNChildren(origexpr) > 0 )
    1197 {
    1198 SCIP_EXPR* newchild;
    1199 int childidx;
    1200 SCIP_VAR* var;
    1201
    1202 /* having a child that had children in original but not in copy means that we could not achieve the desired curvature
    1203 * thus, replace by a new child that points to the auxvar of the original expression
    1204 * we registered in createNlhdlrExprData that we need an auxvar, so it should exist now
    1205 */
    1206 var = SCIPgetExprAuxVarNonlinear(origexpr);
    1207 assert(var != NULL);
    1208
    1209 SCIP_CALL( SCIPcreateExprVar(scip, &newchild, var, NULL, NULL) ); /* this captures newchild once */
    1210
    1211 childidx = SCIPexpriterGetChildIdxDFS(it);
    1212 SCIP_CALL( SCIPreplaceExprChild(scip, nlexpr, childidx, newchild) ); /* this captures newchild again */
    1213
    1214 /* do not remove child->origexpr from hashmap, as child may appear again due to common subexprs
    1215 * (created by curvCheckProductComposite, for example)
    1216 * if it doesn't reappear, though, but the memory address is reused, we need to make sure it
    1217 * points to the right origexpr
    1218 */
    1219 /* SCIP_CALL( SCIPhashmapRemove(nlexpr2origexpr, (void*)child) ); */
    1220 SCIP_CALL( SCIPhashmapSetImage(nlhdlrexprdata->nlexpr2origexpr, (void*)newchild, (void*)origexpr) );
    1221
    1222 if( !SCIPhashmapExists(leaf2index, (void*)newchild) )
    1223 {
    1224 /* new leaf -> new index and remember in hashmap */
    1225 SCIP_CALL( SCIPhashmapInsertInt(leaf2index, (void*)newchild, nlhdlrexprdata->nleafs++) );
    1226 }
    1227
    1228 child = newchild;
    1229 SCIP_CALL( SCIPreleaseExpr(scip, &newchild) ); /* because it was captured by both create and replace */
    1230 }
    1231 else if( SCIPisExprVar(scip, child) )
    1232 {
    1233 /* if variable, then add to hashmap, if not already there */
    1234 if( !SCIPhashmapExists(leaf2index, (void*)child) )
    1235 {
    1236 SCIP_CALL( SCIPhashmapInsertInt(leaf2index, (void*)child, nlhdlrexprdata->nleafs++) );
    1237 }
    1238 }
    1239 /* else: it's probably a value-expression, nothing to do */
    1240
    1241 /* update integrality flag for future leaf expressions: convex nlhdlr may use this information */
    1243 }
    1244 assert(nlhdlrexprdata->nleafs > 0);
    1245
    1246 SCIPfreeExpriter(&it);
    1247
    1248 /* assemble auxvars array */
    1249 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(nlhdlrexprdata->leafexprs), nlhdlrexprdata->nleafs) );
    1250 for( i = 0; i < SCIPhashmapGetNEntries(leaf2index); ++i )
    1251 {
    1252 SCIP_HASHMAPENTRY* entry;
    1253 SCIP_EXPR* leaf;
    1254 int idx;
    1255
    1256 entry = SCIPhashmapGetEntry(leaf2index, i);
    1257 if( entry == NULL )
    1258 continue;
    1259
    1260 leaf = (SCIP_EXPR*) SCIPhashmapEntryGetOrigin(entry);
    1261 assert(leaf != NULL);
    1262 assert(SCIPisExprVar(scip, leaf));
    1263
    1264 idx = SCIPhashmapEntryGetImageInt(entry);
    1265 assert(idx >= 0);
    1266 assert(idx < nlhdlrexprdata->nleafs);
    1267
    1268 nlhdlrexprdata->leafexprs[idx] = leaf;
    1269
    1270 SCIPdebugMsg(scip, "leaf %d: <%s>\n", idx, SCIPvarGetName(SCIPgetVarExprVar(leaf)));
    1271 }
    1272
    1273 SCIPhashmapFree(&leaf2index);
    1274
    1275 return SCIP_OKAY;
    1276}
    1277
    1278/** creates nonlinear handler expression data structure and registers expr usage */
    1279static
    1281 SCIP* scip, /**< SCIP data structure */
    1282 SCIP_NLHDLRDATA* nlhdlrdata, /**< nlhdlr data */
    1283 SCIP_NLHDLREXPRDATA** nlhdlrexprdata, /**< pointer to store nlhdlr expression data */
    1284 SCIP_EXPR* expr, /**< original expression */
    1285 SCIP_EXPR* nlexpr, /**< our copy of expression */
    1286 SCIP_HASHMAP* nlexpr2origexpr, /**< mapping of expression copy to original */
    1287 int nleafs, /**< number of leafs as counted by constructExpr */
    1288 SCIP_NLHDLR_METHOD participating /**< the enfo methods in which we plan to participate */
    1289 )
    1290{
    1291 SCIP_EXPRITER* it;
    1292 SCIP_Bool usingaux;
    1293
    1294 assert(scip != NULL);
    1295 assert(expr != NULL);
    1296 assert(nlhdlrexprdata != NULL);
    1297 assert(*nlhdlrexprdata == NULL);
    1298 assert(nlexpr != NULL);
    1299 assert(nlexpr2origexpr != NULL);
    1300
    1301 assert(SCIPexprGetNChildren(nlexpr) > 0);
    1302 assert(SCIPexprGetChildren(nlexpr) != NULL);
    1303
    1304 SCIP_CALL( SCIPallocClearBlockMemory(scip, nlhdlrexprdata) );
    1305 (*nlhdlrexprdata)->nlexpr = nlexpr;
    1306 (*nlhdlrexprdata)->nlexpr2origexpr = nlexpr2origexpr;
    1307 (*nlhdlrexprdata)->nleafs = nleafs;
    1308
    1309 usingaux = FALSE;
    1310
    1314
    1315 for( ; !SCIPexpriterIsEnd(it); (void) SCIPexpriterGetNext(it) )
    1316 {
    1317 SCIP_EXPR* child;
    1318 SCIP_EXPR* origexpr;
    1319
    1320 /* check whether to-be-visited child needs to be replaced by a new expression (representing the auxvar)
    1321 * if child has children, then that is not the case
    1322 * if child has no children, but also corresponding origexpr has no chilren, then this is also not the case
    1323 */
    1324 child = SCIPexpriterGetChildExprDFS(it);
    1325 if( SCIPexprGetNChildren(child) > 0 )
    1326 continue;
    1327
    1328 origexpr = (SCIP_EXPR*)SCIPhashmapGetImage(nlexpr2origexpr, (void*)child);
    1329 assert(origexpr != NULL);
    1330
    1331 /* if child had children in original but not in copy means that we could not achieve the desired curvature
    1332 * thus, we will later replace by a new child that points to the auxvar of the original expression
    1333 * as we do not have the auxvar now, we will only register that we will need the auxvar later (if origexpr isn't a variable or constant)
    1334 * if we are working for the concave nlhdlr, then we also indicate interest on the exprs activity for estimate (distinguish below or above)
    1335 */
    1337 SCIPexprGetNChildren(origexpr) > 0, FALSE,
    1338 !nlhdlrdata->isnlhdlrconvex && (participating & SCIP_NLHDLR_METHOD_SEPABELOW),
    1339 !nlhdlrdata->isnlhdlrconvex && (participating & SCIP_NLHDLR_METHOD_SEPAABOVE)) );
    1340
    1341 /* remember that we use an auxvar */
    1342 if( SCIPexprGetNChildren(origexpr) > 0 )
    1343 usingaux = TRUE;
    1344 }
    1345
    1346 SCIPfreeExpriter(&it);
    1347
    1348#ifdef SCIP_DEBUG
    1349 SCIPprintExpr(scip, nlexpr, NULL);
    1350 SCIPinfoMessage(scip, NULL, " (%p) is handled as %s\n", SCIPhashmapGetImage(nlexpr2origexpr, (void*)nlexpr),
    1352#endif
    1353
    1354 /* If we don't work on the extended formulation, then set curvature also in original expression
    1355 * (in case someone wants to pick this up; this might be removed again).
    1356 * This doesn't ensure that every convex or concave original expression is actually marked here.
    1357 * Not only because our tests are incomprehensive, but also because we may not detect on sums,
    1358 * prefer extended formulations (in nlhdlr_convex), or introduce auxvars for linear subexpressions
    1359 * on purpose (in nlhdlr_concave).
    1360 */
    1361 if( !usingaux )
    1363
    1364 return SCIP_OKAY;
    1365}
    1366
    1367/** adds an estimator for a vertex-polyhedral (e.g., concave) function to a given rowprep
    1368 *
    1369 * Calls \ref SCIPcomputeFacetVertexPolyhedralNonlinear() for given function and
    1370 * box set to local bounds of auxiliary variables.
    1371 */
    1372static
    1374 SCIP* scip, /**< SCIP data structure */
    1375 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */
    1376 SCIP_NLHDLR* nlhdlr, /**< nonlinear handler */
    1377 SCIP_NLHDLREXPRDATA* nlhdlrexprdata, /**< nonlinear handler expression data */
    1378 SCIP_SOL* sol, /**< solution to use, unless usemidpoint is TRUE */
    1379 SCIP_Bool usemidpoint, /**< whether to use the midpoint of the domain instead of sol */
    1380 SCIP_Bool overestimate, /**< whether over- or underestimating */
    1381 SCIP_Real targetvalue, /**< a target value to achieve; if not reachable, then can give up early */
    1382 SCIP_ROWPREP* rowprep, /**< rowprep where to store estimator */
    1383 SCIP_Bool* success /**< buffer to store whether successful */
    1384 )
    1385{
    1386 SCIP_NLHDLRDATA* nlhdlrdata;
    1387 VERTEXPOLYFUN_EVALDATA evaldata;
    1388 SCIP_Real* xstar;
    1389 SCIP_Real* box;
    1390 SCIP_Real facetconstant;
    1391 SCIP_VAR* var;
    1392 int i;
    1393 SCIP_Bool allfixed;
    1394
    1395 assert(scip != NULL);
    1396 assert(nlhdlr != NULL);
    1397 assert(nlhdlrexprdata != NULL);
    1398 assert(rowprep != NULL);
    1399 assert(success != NULL);
    1400
    1401 *success = FALSE;
    1402
    1403 /* caller is responsible to have checked whether we can estimate, i.e., expression curvature and overestimate flag match */
    1404 assert( overestimate || SCIPexprGetCurvature(nlhdlrexprdata->nlexpr) == SCIP_EXPRCURV_CONCAVE); /* if underestimate, then must be concave */
    1405 assert(!overestimate || SCIPexprGetCurvature(nlhdlrexprdata->nlexpr) == SCIP_EXPRCURV_CONVEX); /* if overestimate, then must be convex */
    1406
    1407#ifdef SCIP_DEBUG
    1408 SCIPinfoMessage(scip, NULL, "%sestimate expression ", overestimate ? "over" : "under");
    1409 SCIPprintExpr(scip, nlhdlrexprdata->nlexpr, NULL);
    1410 SCIPinfoMessage(scip, NULL, " at point\n");
    1411 for( i = 0; i < nlhdlrexprdata->nleafs; ++i )
    1412 {
    1413 var = SCIPgetVarExprVar(nlhdlrexprdata->leafexprs[i]);
    1414 assert(var != NULL);
    1415
    1416 SCIPinfoMessage(scip, NULL, " <%s> = %g [%g,%g]\n", SCIPvarGetName(var),
    1417 usemidpoint ? 0.5 * (SCIPvarGetLbLocal(var) + SCIPvarGetUbLocal(var)) : SCIPgetSolVal(scip, sol, var),
    1419 }
    1420#endif
    1421
    1422 nlhdlrdata = SCIPnlhdlrGetData(nlhdlr);
    1423 assert(nlhdlrdata != NULL);
    1424
    1425 if( nlhdlrdata->evalsol == NULL )
    1426 {
    1427 SCIP_CALL( SCIPcreateSol(scip, &nlhdlrdata->evalsol, NULL) );
    1428 }
    1429
    1430 evaldata.nlhdlrexprdata = nlhdlrexprdata;
    1431 evaldata.evalsol = nlhdlrdata->evalsol;
    1432 evaldata.scip = scip;
    1433
    1434 SCIP_CALL( SCIPallocBufferArray(scip, &xstar, nlhdlrexprdata->nleafs) );
    1435 SCIP_CALL( SCIPallocBufferArray(scip, &box, 2*nlhdlrexprdata->nleafs) );
    1436
    1437 allfixed = TRUE;
    1438 for( i = 0; i < nlhdlrexprdata->nleafs; ++i )
    1439 {
    1440 var = SCIPgetVarExprVar(nlhdlrexprdata->leafexprs[i]);
    1441 assert(var != NULL);
    1442
    1443 box[2*i] = SCIPvarGetLbLocal(var);
    1444 if( SCIPisInfinity(scip, -box[2*i]) )
    1445 {
    1446 SCIPdebugMsg(scip, "lower bound at -infinity, no estimate possible\n");
    1447 goto TERMINATE;
    1448 }
    1449
    1450 box[2*i+1] = SCIPvarGetUbLocal(var);
    1451 if( SCIPisInfinity(scip, box[2*i+1]) )
    1452 {
    1453 SCIPdebugMsg(scip, "upper bound at +infinity, no estimate possible\n");
    1454 goto TERMINATE;
    1455 }
    1456
    1457 if( !SCIPisRelEQ(scip, box[2*i], box[2*i+1]) )
    1458 allfixed = FALSE;
    1459
    1460 if( usemidpoint )
    1461 xstar[i] = 0.5 * (box[2*i] + box[2*i+1]);
    1462 else
    1463 xstar[i] = SCIPgetSolVal(scip, sol, var);
    1464 assert(xstar[i] != SCIP_INVALID);
    1465 }
    1466
    1467 if( allfixed )
    1468 {
    1469 /* SCIPcomputeFacetVertexPolyhedralNonlinear prints a warning and does not succeed if all is fixed */
    1470 SCIPdebugMsg(scip, "all variables fixed, skip estimate\n");
    1471 goto TERMINATE;
    1472 }
    1473
    1474 SCIP_CALL( SCIPensureRowprepSize(scip, rowprep, nlhdlrexprdata->nleafs + 1) );
    1475
    1476 SCIP_CALL( SCIPcomputeFacetVertexPolyhedralNonlinear(scip, conshdlr, overestimate, nlhdlrExprEvalConcave, (void*)&evaldata,
    1477 xstar, box, nlhdlrexprdata->nleafs, targetvalue, success, SCIProwprepGetCoefs(rowprep), &facetconstant) );
    1478
    1479 if( !*success )
    1480 {
    1481 SCIPdebugMsg(scip, "failed to compute facet of convex hull\n");
    1482 goto TERMINATE;
    1483 }
    1484
    1485 SCIProwprepSetLocal(rowprep, TRUE);
    1486 SCIProwprepAddConstant(rowprep, facetconstant);
    1487 for( i = 0; i < nlhdlrexprdata->nleafs; ++i )
    1488 {
    1489 SCIP_CALL( SCIPaddRowprepTerm(scip, rowprep, SCIPgetVarExprVar(nlhdlrexprdata->leafexprs[i]), SCIProwprepGetCoefs(rowprep)[i]) );
    1490 }
    1491
    1492#ifdef SCIP_DEBUG
    1493 SCIPinfoMessage(scip, NULL, "computed estimator: ");
    1494 SCIPprintRowprep(scip, rowprep, NULL);
    1495#endif
    1496
    1497 TERMINATE:
    1499 SCIPfreeBufferArray(scip, &xstar);
    1500
    1501 return SCIP_OKAY;
    1502}
    1503
    1504/** adds an estimator computed via a gradient to a given rowprep */
    1505static
    1507 SCIP* scip, /**< SCIP data structure */
    1508 SCIP_NLHDLREXPRDATA* nlhdlrexprdata, /**< nonlinear handler expression data */
    1509 SCIP_SOL* sol, /**< solution to use */
    1510 SCIP_ROWPREP* rowprep, /**< rowprep where to store estimator */
    1511 SCIP_Bool* success /**< buffer to store whether successful */
    1512 )
    1513{
    1514 SCIP_EXPR* nlexpr;
    1515 SCIP_Real QUAD(constant);
    1516 int i;
    1517
    1518 assert(scip != NULL);
    1519 assert(nlhdlrexprdata != NULL);
    1520 assert(rowprep != NULL);
    1521 assert(success != NULL);
    1522
    1523 nlexpr = nlhdlrexprdata->nlexpr;
    1524 assert(nlexpr != NULL);
    1525
    1526 /* compute gradient (TODO: this also re-evaluates (soltag=0), which shouldn't be necessary unless we tried ConvexSecant before or are called from Sollinearize callback) */
    1527 SCIP_CALL( SCIPevalExprGradient(scip, nlexpr, sol, 0L) );
    1528
    1529 /* if gradient evaluation error, then return */
    1530 if( SCIPexprGetDerivative(nlexpr) == SCIP_INVALID )
    1531 {
    1532 SCIPdebugMsg(scip, "gradient evaluation error for %p\n", (void*)nlexpr);
    1533 return SCIP_OKAY;
    1534 }
    1535
    1536 /* add gradient underestimator to rowprep: f(sol) + (x - sol) \nabla f(sol)
    1537 * constant will store f(sol) - sol * \nabla f(sol)
    1538 * to avoid some cancellation errors when linear variables take huge values (like 1e20),
    1539 * we use double-double arithmetic here
    1540 */
    1541 QUAD_ASSIGN(constant, SCIPexprGetEvalValue(nlexpr)); /* f(sol) */
    1542 for( i = 0; i < nlhdlrexprdata->nleafs; ++i )
    1543 {
    1544 SCIP_VAR* var;
    1545 SCIP_Real deriv;
    1546 SCIP_Real varval;
    1547
    1548 assert(SCIPexprGetDiffTag(nlhdlrexprdata->leafexprs[i]) == SCIPexprGetDiffTag(nlexpr));
    1549 deriv = SCIPexprGetDerivative(nlhdlrexprdata->leafexprs[i]);
    1550 if( deriv == SCIP_INVALID )
    1551 {
    1552 SCIPdebugMsg(scip, "gradient evaluation error for component %d of %p\n", i, (void*)nlexpr);
    1553 return SCIP_OKAY;
    1554 }
    1555
    1556 var = SCIPgetVarExprVar(nlhdlrexprdata->leafexprs[i]);
    1557 assert(var != NULL);
    1558
    1559 varval = SCIPgetSolVal(scip, sol, var);
    1560
    1561 SCIPdebugMsg(scip, "add %g * (<%s> - %g) to rowprep\n", deriv, SCIPvarGetName(var), varval);
    1562
    1563 /* add deriv * var to rowprep and deriv * (-varval) to constant */
    1564 SCIP_CALL( SCIPaddRowprepTerm(scip, rowprep, var, deriv) );
    1565 SCIPquadprecSumQD(constant, constant, -deriv * varval);
    1566 }
    1567
    1568 SCIProwprepAddConstant(rowprep, QUAD_TO_DBL(constant));
    1569 SCIProwprepSetLocal(rowprep, FALSE);
    1570
    1571 *success = TRUE;
    1572
    1573 return SCIP_OKAY;
    1574}
    1575
    1576/** adds an estimator computed via a gradient to a given rowprep, possibly perturbing solution */
    1577static
    1579 SCIP* scip, /**< SCIP data structure */
    1580 SCIP_NLHDLR* nlhdlr, /**< nonlinear handler */
    1581 SCIP_NLHDLREXPRDATA* nlhdlrexprdata, /**< nonlinear handler expression data */
    1582 SCIP_SOL* sol, /**< solution to use */
    1583 SCIP_ROWPREP* rowprep, /**< rowprep where to store estimator */
    1584 SCIP_Bool* success /**< buffer to store whether successful */
    1585 )
    1586{
    1587 SCIP_NLHDLRDATA* nlhdlrdata;
    1588 int i;
    1589
    1590 assert(nlhdlrexprdata != NULL);
    1591 assert(rowprep != NULL);
    1592 assert(success != NULL);
    1593
    1594#ifdef SCIP_DEBUG
    1595 SCIPinfoMessage(scip, NULL, "estimate expression ");
    1596 SCIPprintExpr(scip, nlhdlrexprdata->nlexpr, NULL);
    1597 SCIPinfoMessage(scip, NULL, " by gradient\n");
    1598#endif
    1599
    1600 *success = FALSE;
    1601
    1602 SCIP_CALL( estimateGradientInner(scip, nlhdlrexprdata, sol, rowprep, success) );
    1603
    1604 /* if succeeded, then there was no gradient evaluation error, so we are done */
    1605 if( *success )
    1606 return SCIP_OKAY;
    1607
    1608 nlhdlrdata = SCIPnlhdlrGetData(nlhdlr);
    1609 assert(nlhdlrdata != NULL);
    1610
    1611 /* we take maxperturb == 0 as signal to not try perturbation */
    1612 if( nlhdlrdata->maxperturb == 0.0 )
    1613 {
    1614 SCIPdebugMsg(scip, "gradient evaluation error, perturbation disabled\n");
    1615 return SCIP_OKAY;
    1616 }
    1617
    1618 SCIPdebugMsg(scip, "gradient evaluation error, try perturbed point\n");
    1619
    1620 if( nlhdlrdata->evalsol == NULL )
    1621 {
    1622 SCIP_CALL( SCIPcreateSol(scip, &nlhdlrdata->evalsol, NULL) );
    1623 }
    1624
    1625 if( nlhdlrdata->randnumgen == NULL )
    1626 {
    1627 SCIP_CALL( SCIPcreateRandom(scip, &nlhdlrdata->randnumgen, RANDNUMINITSEED, TRUE) );
    1628 }
    1629
    1630 for( i = 0; i < nlhdlrexprdata->nleafs; ++i )
    1631 {
    1632 SCIP_VAR* var;
    1633 SCIP_Real lb;
    1634 SCIP_Real ub;
    1635 SCIP_Real val;
    1636 SCIP_Real p;
    1637
    1638 var = SCIPgetVarExprVar(nlhdlrexprdata->leafexprs[i]);
    1639 assert(var != NULL);
    1640
    1641 lb = SCIPvarGetLbGlobal(var);
    1642 ub = SCIPvarGetUbGlobal(var);
    1643 val = SCIPgetSolVal(scip, sol, var);
    1644 val = MIN(ub, MAX(lb, val));
    1645
    1646 p = SCIPrandomGetReal(nlhdlrdata->randnumgen, -nlhdlrdata->maxperturb, nlhdlrdata->maxperturb);
    1647 if( !SCIPisZero(scip, val) )
    1648 p *= REALABS(val);
    1649
    1650 /* if perturbation to left underruns lower bound, then try perturb to right
    1651 * if perturbation to right exceeds lower bound, then try perturb to left
    1652 */
    1653 if( val + p <= lb )
    1654 val -= p;
    1655 else if( val + p >= ub )
    1656 val -= p;
    1657 else
    1658 val += p;
    1659
    1660 /* if still exceeding bound, then |ub-lb| < 2*maxperturb and we pick a random value between bounds
    1661 * (if ub-lb < 2eps, then SCIPrandomGetReal() still gives a reasonable number in [lb,ub])
    1662 */
    1663 if( val <= lb || val >= ub )
    1664 val = SCIPrandomGetReal(nlhdlrdata->randnumgen, lb + SCIPepsilon(scip), ub - SCIPepsilon(scip));
    1665
    1666 SCIP_CALL( SCIPsetSolVal(scip, nlhdlrdata->evalsol, var, val) );
    1667 }
    1668
    1669 /* try again with perturbed point */
    1670 SCIP_CALL( estimateGradientInner(scip, nlhdlrexprdata, nlhdlrdata->evalsol, rowprep, success) );
    1671
    1672 return SCIP_OKAY;
    1673}
    1674
    1675/** adds an estimator generated by putting a secant through the coordinates given by the two closest integer points */
    1676static
    1678 SCIP* scip, /**< SCIP data structure */
    1679 SCIP_NLHDLR* nlhdlr, /**< nonlinear handler */
    1680 SCIP_NLHDLREXPRDATA* nlhdlrexprdata, /**< nonlinear handler expression data */
    1681 SCIP_SOL* sol, /**< solution to use, unless usemidpoint is TRUE */
    1682 SCIP_ROWPREP* rowprep, /**< rowprep where to store estimator */
    1683 SCIP_Bool* success /**< buffer to store whether successful */
    1684 )
    1685{
    1686 SCIP_NLHDLRDATA* nlhdlrdata;
    1687 SCIP_EXPR* nlexpr;
    1688 SCIP_VAR* var;
    1689 SCIP_Real x;
    1690 SCIP_Real left, right;
    1691 SCIP_Real fleft, fright;
    1692
    1693 assert(nlhdlrexprdata != NULL);
    1694 assert(nlhdlrexprdata->nleafs == 1);
    1695 assert(rowprep != NULL);
    1696 assert(success != NULL);
    1697
    1698 nlexpr = nlhdlrexprdata->nlexpr;
    1699 assert(nlexpr != NULL);
    1700
    1701 *success = FALSE;
    1702
    1703 nlhdlrdata = SCIPnlhdlrGetData(nlhdlr);
    1704 assert(nlhdlrdata != NULL);
    1705
    1706 var = SCIPgetVarExprVar(nlhdlrexprdata->leafexprs[0]);
    1707 assert(var != NULL);
    1708
    1709 x = SCIPgetSolVal(scip, sol, var);
    1710
    1711#ifdef SCIP_DEBUG
    1712 SCIPinfoMessage(scip, NULL, "estimate expression ");
    1713 SCIPprintExpr(scip, nlexpr, NULL);
    1714 SCIPinfoMessage(scip, NULL, " by secant\n");
    1715 SCIPinfoMessage(scip, NULL, "integral variable <%s> = %g [%g,%g]\n", SCIPvarGetName(var),
    1717#endif
    1718
    1719 /* find out coordinates of var left and right to sol */
    1720 if( SCIPisIntegral(scip, x) )
    1721 {
    1722 x = SCIPround(scip, x);
    1723 if( SCIPisEQ(scip, x, SCIPvarGetLbGlobal(var)) )
    1724 {
    1725 left = x;
    1726 right = left + 1.0;
    1727 }
    1728 else
    1729 {
    1730 right = x;
    1731 left = right - 1.0;
    1732 }
    1733 }
    1734 else
    1735 {
    1736 left = SCIPfloor(scip, x);
    1737 right = SCIPceil(scip, x);
    1738 }
    1739 assert(left != right);
    1740
    1741 /* now evaluate at left and right */
    1742 if( nlhdlrdata->evalsol == NULL )
    1743 {
    1744 SCIP_CALL( SCIPcreateSol(scip, &nlhdlrdata->evalsol, NULL) );
    1745 }
    1746
    1747 SCIP_CALL( SCIPsetSolVal(scip, nlhdlrdata->evalsol, var, left) );
    1748 SCIP_CALL( SCIPevalExpr(scip, nlexpr, nlhdlrdata->evalsol, 0L) );
    1749
    1750 /* evaluation error or a too large constant -> skip */
    1751 fleft = SCIPexprGetEvalValue(nlexpr);
    1752 if( SCIPisInfinity(scip, REALABS(fleft)) )
    1753 {
    1754 SCIPdebugMsg(scip, "evaluation error / too large value (%g) for %p\n", SCIPexprGetEvalValue(nlexpr), (void*)nlexpr);
    1755 return SCIP_OKAY;
    1756 }
    1757
    1758 SCIP_CALL( SCIPsetSolVal(scip, nlhdlrdata->evalsol, var, right) );
    1759 SCIP_CALL( SCIPevalExpr(scip, nlexpr, nlhdlrdata->evalsol, 0L) );
    1760
    1761 /* evaluation error or a too large constant -> skip */
    1762 fright = SCIPexprGetEvalValue(nlexpr);
    1763 if( SCIPisInfinity(scip, REALABS(fright)) )
    1764 {
    1765 SCIPdebugMsg(scip, "evaluation error / too large value (%g) for %p\n", SCIPexprGetEvalValue(nlexpr), (void*)nlexpr);
    1766 return SCIP_OKAY;
    1767 }
    1768
    1769 SCIPdebugMsg(scip, "f(%g)=%g, f(%g)=%g\n", left, fleft, right, fright);
    1770
    1771 /* skip if too steep
    1772 * for clay0204h, this resulted in a wrong cut from f(0)=1e12 f(1)=0.99998,
    1773 * since due to limited precision, this was handled as if f(1)=1
    1774 */
    1775 if( (!SCIPisZero(scip, fleft) && REALABS(fright/fleft)*SCIPepsilon(scip) > 1.0) ||
    1776 (!SCIPisZero(scip, fright) && REALABS(fleft/fright)*SCIPepsilon(scip) > 1.0) )
    1777 {
    1778 SCIPdebugMsg(scip, "function is too steep, abandoning\n");
    1779 return SCIP_OKAY;
    1780 }
    1781
    1782 /* now add f(left) + (f(right) - f(left)) * (x - left) as estimator to rowprep */
    1783 SCIP_CALL( SCIPaddRowprepTerm(scip, rowprep, var, fright - fleft) );
    1784 SCIProwprepAddConstant(rowprep, fleft - (fright - fleft) * left);
    1785 SCIProwprepSetLocal(rowprep, FALSE);
    1786
    1787 *success = TRUE;
    1788
    1789 return SCIP_OKAY;
    1790}
    1791
    1792/*
    1793 * Callback methods of convex nonlinear handler
    1794 */
    1795
    1796/** free handler data of convex or concave nlhdlr */
    1797static
    1798SCIP_DECL_NLHDLRFREEHDLRDATA(nlhdlrfreeHdlrDataConvexConcave)
    1799{ /*lint --e{715}*/
    1800 assert(scip != NULL);
    1801 assert(nlhdlrdata != NULL);
    1802 assert(*nlhdlrdata != NULL);
    1803 assert((*nlhdlrdata)->evalsol == NULL);
    1804 assert((*nlhdlrdata)->randnumgen == NULL);
    1805
    1806 SCIPfreeBlockMemory(scip, nlhdlrdata);
    1807
    1808 return SCIP_OKAY;
    1809}
    1810
    1811/** callback to free expression specific data */
    1812static
    1813SCIP_DECL_NLHDLRFREEEXPRDATA(nlhdlrfreeExprDataConvexConcave)
    1814{ /*lint --e{715}*/
    1815 assert(scip != NULL);
    1816 assert(nlhdlrexprdata != NULL);
    1817 assert(*nlhdlrexprdata != NULL);
    1818
    1819 SCIPfreeBlockMemoryArrayNull(scip, &(*nlhdlrexprdata)->leafexprs, (*nlhdlrexprdata)->nleafs);
    1820 SCIP_CALL( SCIPreleaseExpr(scip, &(*nlhdlrexprdata)->nlexpr) );
    1821 SCIPhashmapFree(&(*nlhdlrexprdata)->nlexpr2origexpr);
    1822
    1823 SCIPfreeBlockMemory(scip, nlhdlrexprdata);
    1824
    1825 return SCIP_OKAY;
    1826}
    1827
    1828/** deinitialization of problem-specific data */
    1829static
    1830SCIP_DECL_NLHDLREXIT(nlhdlrExitConvex)
    1831{
    1832 SCIP_NLHDLRDATA* nlhdlrdata;
    1833
    1834 nlhdlrdata = SCIPnlhdlrGetData(nlhdlr);
    1835 assert(nlhdlrdata != NULL);
    1836
    1837 if( nlhdlrdata->evalsol != NULL )
    1838 {
    1839 SCIP_CALL( SCIPfreeSol(scip, &nlhdlrdata->evalsol) );
    1840 }
    1841
    1842 if( nlhdlrdata->randnumgen != NULL )
    1843 SCIPfreeRandom(scip, &nlhdlrdata->randnumgen);
    1844
    1845 return SCIP_OKAY;
    1846}
    1847
    1848/** checks whether expression (or -expression) is convex, possibly after introducing auxiliary variables */
    1849static
    1850SCIP_DECL_NLHDLRDETECT(nlhdlrDetectConvex)
    1851{ /*lint --e{715}*/
    1852 SCIP_NLHDLRDATA* nlhdlrdata;
    1853 SCIP_EXPR* nlexpr = NULL;
    1854 SCIP_HASHMAP* nlexpr2origexpr;
    1855 int nleafs = 0;
    1856
    1857 assert(scip != NULL);
    1858 assert(nlhdlr != NULL);
    1859 assert(expr != NULL);
    1860 assert(enforcing != NULL);
    1861 assert(participating != NULL);
    1862 assert(nlhdlrexprdata != NULL);
    1863
    1864 /* we currently do not participate if only activity computation is required */
    1866 return SCIP_OKAY;
    1867
    1868 /* ignore pure constants and variables */
    1869 if( SCIPexprGetNChildren(expr) == 0 )
    1870 return SCIP_OKAY;
    1871
    1872 nlhdlrdata = SCIPnlhdlrGetData(nlhdlr);
    1873 assert(nlhdlrdata != NULL);
    1874 assert(nlhdlrdata->isnlhdlrconvex);
    1875
    1876 SCIPdebugMsg(scip, "nlhdlr_convex detect for expr %p\n", (void*)expr);
    1877
    1878 /* initialize mapping from copied expression to original one
    1879 * 20 is not a bad estimate for the size of convex subexpressions that we can usually discover
    1880 * when expressions will be allowed to store "user"data, we could get rid of this hashmap (TODO)
    1881 */
    1882 SCIP_CALL( SCIPhashmapCreate(&nlexpr2origexpr, SCIPblkmem(scip), 20) );
    1883
    1884 if( (*enforcing & SCIP_NLHDLR_METHOD_SEPABELOW) == 0 ) /* if no separation below yet */
    1885 {
    1886 SCIP_CALL( constructExpr(scip, nlhdlrdata, &nlexpr, nlexpr2origexpr, &nleafs, expr,
    1888 if( nlexpr != NULL )
    1889 {
    1890 assert(SCIPexprGetNChildren(nlexpr) > 0); /* should not be trivial */
    1891
    1892 *participating |= SCIP_NLHDLR_METHOD_SEPABELOW;
    1893
    1894 SCIPdebugMsg(scip, "detected expr %p to be convex -> can enforce expr <= auxvar\n", (void*)expr);
    1895 }
    1896 else
    1897 {
    1898 SCIP_CALL( SCIPhashmapRemoveAll(nlexpr2origexpr) );
    1899 }
    1900 }
    1901
    1902 if( (*enforcing & SCIP_NLHDLR_METHOD_SEPAABOVE) == 0 && nlexpr == NULL ) /* if no separation above and not convex */
    1903 {
    1904 SCIP_CALL( constructExpr(scip, nlhdlrdata, &nlexpr, nlexpr2origexpr, &nleafs, expr,
    1906 if( nlexpr != NULL )
    1907 {
    1908 assert(SCIPexprGetNChildren(nlexpr) > 0); /* should not be trivial */
    1909
    1910 *participating |= SCIP_NLHDLR_METHOD_SEPAABOVE;
    1911
    1912 SCIPdebugMsg(scip, "detected expr %p to be concave -> can enforce expr >= auxvar\n", (void*)expr);
    1913 }
    1914 }
    1915
    1916 /* everything we participate in we also enforce */
    1917 *enforcing |= *participating;
    1918
    1919 assert(*participating || nlexpr == NULL);
    1920 if( !*participating )
    1921 {
    1922 SCIPhashmapFree(&nlexpr2origexpr);
    1923 return SCIP_OKAY;
    1924 }
    1925
    1926 /* create the expression data of the nonlinear handler
    1927 * notify conshdlr about expr for which we will require auxiliary variables
    1928 */
    1929 SCIP_CALL( createNlhdlrExprData(scip, nlhdlrdata, nlhdlrexprdata, expr, nlexpr, nlexpr2origexpr, nleafs, *participating) );
    1930
    1931 return SCIP_OKAY;
    1932}
    1933
    1934/** auxiliary evaluation callback */
    1935static
    1936SCIP_DECL_NLHDLREVALAUX(nlhdlrEvalAuxConvexConcave)
    1937{ /*lint --e{715}*/
    1938 assert(nlhdlrexprdata != NULL);
    1939 assert(nlhdlrexprdata->nlexpr != NULL);
    1940 assert(auxvalue != NULL);
    1941
    1942 SCIP_CALL( SCIPevalExpr(scip, nlhdlrexprdata->nlexpr, sol, 0L) );
    1943 *auxvalue = SCIPexprGetEvalValue(nlhdlrexprdata->nlexpr);
    1944
    1945 return SCIP_OKAY;
    1946}
    1947
    1948/** init sepa callback that initializes LP */
    1949static
    1950SCIP_DECL_NLHDLRINITSEPA(nlhdlrInitSepaConvex)
    1951{ /*lint --e{715}*/
    1952 SCIP_EXPR* nlexpr;
    1953 SCIP_EXPRCURV curvature;
    1954 SCIP_Bool success;
    1955 SCIP_ROWPREP* rowprep = NULL;
    1956 SCIP_ROW* row;
    1957 SCIP_Real lb;
    1958 SCIP_Real ub;
    1959 SCIP_Real lambda;
    1960 SCIP_SOL* sol;
    1961 int k;
    1962
    1963 assert(scip != NULL);
    1964 assert(expr != NULL);
    1965 assert(nlhdlrexprdata != NULL);
    1966
    1967 /* setup nlhdlrexprdata->leafexprs */
    1968 SCIP_CALL( collectLeafs(scip, nlhdlrexprdata) );
    1969
    1970 nlexpr = nlhdlrexprdata->nlexpr;
    1971 assert(nlexpr != NULL);
    1972 assert(SCIPhashmapGetImage(nlhdlrexprdata->nlexpr2origexpr, (void*)nlexpr) == expr);
    1973
    1974 curvature = SCIPexprGetCurvature(nlexpr);
    1975 assert(curvature == SCIP_EXPRCURV_CONVEX || curvature == SCIP_EXPRCURV_CONCAVE);
    1976
    1977 /* we can only be estimating on the convex side */
    1978 if( curvature == SCIP_EXPRCURV_CONVEX )
    1979 overestimate = FALSE;
    1980 else if( curvature == SCIP_EXPRCURV_CONCAVE )
    1981 underestimate = FALSE;
    1982 if( !overestimate && !underestimate )
    1983 return SCIP_OKAY;
    1984
    1985 /* linearizes at 5 different points obtained as convex combination of the lower and upper bound of the variables
    1986 * present in the convex expression; whether more weight is given to the lower or upper bound of a variable depends
    1987 * on whether the fixing of the variable to that value is better for the objective function
    1988 */
    1989 SCIP_CALL( SCIPcreateSol(scip, &sol, NULL) );
    1990
    1991 *infeasible = FALSE;
    1992
    1993 for( k = 0; k < 5; ++k )
    1994 {
    1995 int i;
    1996 lambda = 0.1 * (k+1); /* lambda = 0.1, 0.2, 0.3, 0.4, 0.5 */
    1997
    1998 for( i = 0; i < nlhdlrexprdata->nleafs; ++i )
    1999 {
    2000 SCIP_VAR* var;
    2001
    2002 var = SCIPgetVarExprVar(nlhdlrexprdata->leafexprs[i]);
    2003
    2004 lb = SCIPvarGetLbGlobal(var);
    2005 ub = SCIPvarGetUbGlobal(var);
    2006
    2007 /* make sure the absolute values of bounds are not too large */
    2008 if( ub > -INITLPMAXVARVAL )
    2009 lb = MAX(lb, -INITLPMAXVARVAL);
    2010 if( lb < INITLPMAXVARVAL )
    2011 ub = MIN(ub, INITLPMAXVARVAL);
    2012
    2013 /* in the case when ub < -maxabsbnd or lb > maxabsbnd, we still want to at least make bounds finite */
    2014 if( SCIPisInfinity(scip, -lb) )
    2015 lb = MIN(-10.0, ub - 0.1*REALABS(ub));
    2016 if( SCIPisInfinity(scip, ub) )
    2017 ub = MAX( 10.0, lb + 0.1*REALABS(lb));
    2018
    2020 SCIP_CALL( SCIPsetSolVal(scip, sol, var, lambda * ub + (1.0 - lambda) * lb) );
    2021 else
    2022 SCIP_CALL( SCIPsetSolVal(scip, sol, var, lambda * lb + (1.0 - lambda) * ub) );
    2023 }
    2024
    2026 SCIP_CALL( estimateGradient(scip, nlhdlr, nlhdlrexprdata, sol, rowprep, &success) );
    2027 if( !success )
    2028 {
    2029 SCIPdebugMsg(scip, "failed to linearize for k = %d\n", k);
    2030 SCIPfreeRowprep(scip, &rowprep);
    2031 continue;
    2032 }
    2033
    2034 /* add auxiliary variable */
    2036
    2037 /* straighten out numerics */
    2038 SCIP_CALL( SCIPcleanupRowprep2(scip, rowprep, NULL, SCIPgetHugeValue(scip), &success) );
    2039 if( !success )
    2040 {
    2041 SCIPdebugMsg(scip, "failed to cleanup rowprep numerics for k = %d\n", k);
    2042 SCIPfreeRowprep(scip, &rowprep);
    2043 continue;
    2044 }
    2045
    2046 (void) SCIPsnprintf(SCIProwprepGetName(rowprep), SCIP_MAXSTRLEN, "%sestimate_gradient%p_initsepa_%d",
    2047 overestimate ? "over" : "under", (void*)expr, k);
    2048 SCIP_CALL( SCIPgetRowprepRowCons(scip, &row, rowprep, cons) );
    2049 SCIPfreeRowprep(scip, &rowprep);
    2050
    2051#ifdef SCIP_DEBUG
    2052 SCIPinfoMessage(scip, NULL, "initsepa computed row: ");
    2053 SCIPprintRow(scip, row, NULL);
    2054#endif
    2055
    2056 SCIP_CALL( SCIPaddRow(scip, row, FALSE, infeasible) );
    2057 SCIP_CALL( SCIPreleaseRow(scip, &row) );
    2058
    2059 if( *infeasible )
    2060 break;
    2061 }
    2062
    2063 SCIP_CALL( SCIPfreeSol(scip, &sol) );
    2064
    2065 return SCIP_OKAY;
    2066}
    2067
    2068/** estimator callback */
    2069static
    2070SCIP_DECL_NLHDLRESTIMATE(nlhdlrEstimateConvex)
    2071{ /*lint --e{715}*/
    2072 SCIP_ROWPREP* rowprep;
    2073
    2074 assert(scip != NULL);
    2075 assert(expr != NULL);
    2076 assert(nlhdlrexprdata != NULL);
    2077 assert(nlhdlrexprdata->nlexpr != NULL);
    2078 assert(rowpreps != NULL);
    2079 assert(success != NULL);
    2080
    2081 assert(SCIPhashmapGetImage(nlhdlrexprdata->nlexpr2origexpr, (void*)nlhdlrexprdata->nlexpr) == expr);
    2082
    2083 /* we must be called only for the side that we indicated to participate in during DETECT */
    2084 assert(SCIPexprGetCurvature(nlhdlrexprdata->nlexpr) == SCIP_EXPRCURV_CONVEX
    2085 || SCIPexprGetCurvature(nlhdlrexprdata->nlexpr) == SCIP_EXPRCURV_CONCAVE);
    2086 assert(!overestimate || SCIPexprGetCurvature(nlhdlrexprdata->nlexpr) == SCIP_EXPRCURV_CONCAVE);
    2087 assert( overestimate || SCIPexprGetCurvature(nlhdlrexprdata->nlexpr) == SCIP_EXPRCURV_CONVEX);
    2088
    2089 *success = FALSE;
    2090 *addedbranchscores = FALSE;
    2091
    2092 /* we can skip eval as nlhdlrEvalAux should have been called for same solution before */
    2093 /* SCIP_CALL( nlhdlrExprEval(scip, nlexpr, sol) ); */
    2094 assert(auxvalue == SCIPexprGetEvalValue(nlhdlrexprdata->nlexpr)); /* given value (originally from
    2095 nlhdlrEvalAuxConvexConcave) should coincide with the one stored in nlexpr */
    2096
    2098
    2099 if( nlhdlrexprdata->nleafs == 1 && SCIPexprIsIntegral(nlhdlrexprdata->leafexprs[0]) )
    2100 {
    2101 SCIP_CALL( estimateConvexSecant(scip, nlhdlr, nlhdlrexprdata, sol, rowprep, success) );
    2102
    2103 (void) SCIPsnprintf(SCIProwprepGetName(rowprep), SCIP_MAXSTRLEN, "%sestimate_convexsecant%p_%s%" SCIP_LONGINT_FORMAT,
    2104 overestimate ? "over" : "under",
    2105 (void*)expr,
    2106 sol != NULL ? "sol" : "lp",
    2107 sol != NULL ? (SCIP_Longint) SCIPsolGetIndex(sol) : SCIPgetNLPs(scip));
    2108 }
    2109
    2110 /* if secant method was not used or failed, then try with gradient (unless we had an evaluation error in sol before) */
    2111 if( !*success && auxvalue != SCIP_INVALID )
    2112 {
    2113 SCIP_CALL( estimateGradient(scip, nlhdlr, nlhdlrexprdata, sol, rowprep, success) );
    2114
    2115 (void) SCIPsnprintf(SCIProwprepGetName(rowprep), SCIP_MAXSTRLEN, "%sestimate_convexgradient%p_%s%" SCIP_LONGINT_FORMAT,
    2116 overestimate ? "over" : "under",
    2117 (void*)expr,
    2118 sol != NULL ? "sol" : "lp",
    2119 sol != NULL ? (SCIP_Longint) SCIPsolGetIndex(sol) : SCIPgetNLPs(scip));
    2120 }
    2121
    2122 if( *success )
    2123 {
    2124 SCIP_CALL( SCIPsetPtrarrayVal(scip, rowpreps, 0, rowprep) );
    2125 }
    2126 else
    2127 {
    2128 SCIPfreeRowprep(scip, &rowprep);
    2129 }
    2130
    2131 return SCIP_OKAY;
    2132}
    2133
    2134/** solution notification callback */
    2135static
    2136SCIP_DECL_NLHDLRSOLLINEARIZE(nlhdlrSollinearizeConvex)
    2137{ /*lint --e{715}*/
    2138 SCIP_ROWPREP* rowprep;
    2139 SCIP_Bool success = FALSE;
    2140
    2141 assert(scip != NULL);
    2142 assert(expr != NULL);
    2143 assert(nlhdlrexprdata != NULL);
    2144 assert(nlhdlrexprdata->nlexpr != NULL);
    2145 assert(SCIPhashmapGetImage(nlhdlrexprdata->nlexpr2origexpr, (void*)nlhdlrexprdata->nlexpr) == expr);
    2146 assert(sol != NULL);
    2147
    2148 /* we must be called only for the side that we indicated to participate in during DETECT */
    2149 assert(SCIPexprGetCurvature(nlhdlrexprdata->nlexpr) == SCIP_EXPRCURV_CONVEX
    2150 || SCIPexprGetCurvature(nlhdlrexprdata->nlexpr) == SCIP_EXPRCURV_CONCAVE);
    2151 assert(!overestimate || SCIPexprGetCurvature(nlhdlrexprdata->nlexpr) == SCIP_EXPRCURV_CONCAVE);
    2152 assert(!underestimate || SCIPexprGetCurvature(nlhdlrexprdata->nlexpr) == SCIP_EXPRCURV_CONVEX);
    2153 assert(underestimate == !overestimate); /* should be exactly one of under- and overestimate */
    2154
    2155 /* evaluate nlexpr in solution */
    2156 SCIP_CALL( SCIPevalExpr(scip, nlhdlrexprdata->nlexpr, sol, 0L) );
    2157
    2159
    2160 if( nlhdlrexprdata->nleafs == 1 && SCIPexprIsIntegral(nlhdlrexprdata->leafexprs[0]) )
    2161 {
    2162 SCIP_CALL( estimateConvexSecant(scip, nlhdlr, nlhdlrexprdata, sol, rowprep, &success) );
    2163
    2164 (void) SCIPsnprintf(SCIProwprepGetName(rowprep), SCIP_MAXSTRLEN, "%sestimate_convexsecant%p_sol%dnotify",
    2165 overestimate ? "over" : "under", (void*)expr, SCIPsolGetIndex(sol));
    2166 }
    2167
    2168 /* if secant method was not used or failed, then try with gradient */
    2169 if( !success )
    2170 {
    2171 SCIP_CALL( estimateGradient(scip, nlhdlr, nlhdlrexprdata, sol, rowprep, &success) );
    2172
    2173 (void) SCIPsnprintf(SCIProwprepGetName(rowprep), SCIP_MAXSTRLEN, "%sestimate_convexgradient%p_sol%dnotify",
    2174 overestimate ? "over" : "under", (void*)expr, SCIPsolGetIndex(sol));
    2175 }
    2176
    2177 if( success )
    2178 {
    2179 /* complete estimator to cut and clean it up */
    2181 SCIP_CALL( SCIPcleanupRowprep2(scip, rowprep, sol, SCIPgetHugeValue(scip), &success) );
    2182 }
    2183
    2184 /* if cleanup succeeded and rowprep is still global, add to cutpool */
    2185 if( success && !SCIProwprepIsLocal(rowprep) )
    2186 {
    2187 SCIP_ROW* row;
    2188
    2189 SCIP_CALL( SCIPgetRowprepRowCons(scip, &row, rowprep, cons) );
    2190 SCIP_CALL( SCIPaddPoolCut(scip, row) );
    2191 SCIP_CALL( SCIPreleaseRow(scip, &row) );
    2192 }
    2193
    2194 SCIPfreeRowprep(scip, &rowprep);
    2195
    2196 return SCIP_OKAY;
    2197}
    2198
    2199/** include nlhdlr in another scip instance */
    2200static
    2201SCIP_DECL_NLHDLRCOPYHDLR(nlhdlrCopyhdlrConvex)
    2202{ /*lint --e{715}*/
    2203 assert(targetscip != NULL);
    2204 assert(sourcenlhdlr != NULL);
    2205 assert(strcmp(SCIPnlhdlrGetName(sourcenlhdlr), CONVEX_NLHDLR_NAME) == 0);
    2206
    2207 SCIP_CALL( SCIPincludeNlhdlrConvex(targetscip) );
    2208
    2209 return SCIP_OKAY;
    2210}
    2211
    2212/** includes convex nonlinear handler in nonlinear constraint handler */
    2214 SCIP* scip /**< SCIP data structure */
    2215 )
    2216{
    2217 SCIP_NLHDLR* nlhdlr;
    2218 SCIP_NLHDLRDATA* nlhdlrdata;
    2219
    2220 assert(scip != NULL);
    2221
    2222 SCIP_CALL( SCIPallocBlockMemory(scip, &nlhdlrdata) );
    2223 nlhdlrdata->isnlhdlrconvex = TRUE;
    2224 nlhdlrdata->evalsol = NULL;
    2225 nlhdlrdata->randnumgen = NULL;
    2226
    2228 CONVEX_NLHDLR_DETECTPRIORITY, CONVEX_NLHDLR_ENFOPRIORITY, nlhdlrDetectConvex, nlhdlrEvalAuxConvexConcave, nlhdlrdata) );
    2229 assert(nlhdlr != NULL);
    2230
    2231 SCIP_CALL( SCIPaddBoolParam(scip, "nlhdlr/" CONVEX_NLHDLR_NAME "/detectsum",
    2232 "whether to run convexity detection when the root of an expression is a non-quadratic sum",
    2233 &nlhdlrdata->detectsum, FALSE, DEFAULT_DETECTSUM, NULL, NULL) );
    2234
    2235 SCIP_CALL( SCIPaddBoolParam(scip, "nlhdlr/" CONVEX_NLHDLR_NAME "/extendedform",
    2236 "whether to create extended formulations instead of looking for maximal convex expressions",
    2237 &nlhdlrdata->extendedform, FALSE, DEFAULT_EXTENDEDFORM, NULL, NULL) );
    2238
    2239 SCIP_CALL( SCIPaddRealParam(scip, "nlhdlr/" CONVEX_NLHDLR_NAME "/maxperturb",
    2240 "maximal relative perturbation of non-differentiable reference point",
    2241 &nlhdlrdata->maxperturb, FALSE, DEFAULT_MAXPERTURB, 0.0, 1.0, NULL, NULL) );
    2242
    2243 SCIP_CALL( SCIPaddBoolParam(scip, "nlhdlr/" CONVEX_NLHDLR_NAME "/cvxquadratic",
    2244 "whether to use convexity check on quadratics",
    2245 &nlhdlrdata->cvxquadratic, TRUE, DEFAULT_CVXQUADRATIC_CONVEX, NULL, NULL) );
    2246
    2247 SCIP_CALL( SCIPaddBoolParam(scip, "nlhdlr/" CONVEX_NLHDLR_NAME "/cvxsignomial",
    2248 "whether to use convexity check on signomials",
    2249 &nlhdlrdata->cvxsignomial, TRUE, DEFAULT_CVXSIGNOMIAL, NULL, NULL) );
    2250
    2251 SCIP_CALL( SCIPaddBoolParam(scip, "nlhdlr/" CONVEX_NLHDLR_NAME "/cvxprodcomp",
    2252 "whether to use convexity check on product composition f(h)*h",
    2253 &nlhdlrdata->cvxprodcomp, TRUE, DEFAULT_CVXPRODCOMP, NULL, NULL) );
    2254
    2255 SCIP_CALL( SCIPaddBoolParam(scip, "nlhdlr/" CONVEX_NLHDLR_NAME "/handletrivial",
    2256 "whether to also handle trivial convex expressions",
    2257 &nlhdlrdata->handletrivial, TRUE, DEFAULT_HANDLETRIVIAL, NULL, NULL) );
    2258
    2259 SCIPnlhdlrSetFreeHdlrData(nlhdlr, nlhdlrfreeHdlrDataConvexConcave);
    2260 SCIPnlhdlrSetCopyHdlr(nlhdlr, nlhdlrCopyhdlrConvex);
    2261 SCIPnlhdlrSetFreeExprData(nlhdlr, nlhdlrfreeExprDataConvexConcave);
    2262 SCIPnlhdlrSetSepa(nlhdlr, nlhdlrInitSepaConvex, NULL, nlhdlrEstimateConvex, NULL);
    2263 SCIPnlhdlrSetSollinearize(nlhdlr, nlhdlrSollinearizeConvex);
    2264 SCIPnlhdlrSetInitExit(nlhdlr, NULL, nlhdlrExitConvex);
    2265
    2266 return SCIP_OKAY;
    2267}
    2268
    2269/*
    2270 * Callback methods of concave nonlinear handler
    2271 */
    2272
    2273/** deinitialization of problem-specific data */
    2274static
    2275SCIP_DECL_NLHDLREXIT(nlhdlrExitConcave)
    2276{
    2277 SCIP_NLHDLRDATA* nlhdlrdata;
    2278
    2279 nlhdlrdata = SCIPnlhdlrGetData(nlhdlr);
    2280 assert(nlhdlrdata != NULL);
    2281 assert(nlhdlrdata->randnumgen == NULL); /* not used for concave nlhdlr so far */
    2282
    2283 if( nlhdlrdata->evalsol != NULL )
    2284 {
    2285 SCIP_CALL( SCIPfreeSol(scip, &nlhdlrdata->evalsol) );
    2286 }
    2287
    2288 return SCIP_OKAY;
    2289}
    2290
    2291/** checks whether expression (or -expression) is concave, possibly after introducing auxiliary variables */
    2292static
    2293SCIP_DECL_NLHDLRDETECT(nlhdlrDetectConcave)
    2294{ /*lint --e{715}*/
    2295 SCIP_NLHDLRDATA* nlhdlrdata;
    2296 SCIP_EXPR* nlexpr = NULL;
    2297 SCIP_HASHMAP* nlexpr2origexpr;
    2298 int nleafs = 0;
    2299
    2300 assert(scip != NULL);
    2301 assert(nlhdlr != NULL);
    2302 assert(expr != NULL);
    2303 assert(enforcing != NULL);
    2304 assert(participating != NULL);
    2305 assert(nlhdlrexprdata != NULL);
    2306
    2307 /* we currently do not participate if only activity computation is required */
    2309 return SCIP_OKAY;
    2310
    2311 /* ignore pure constants and variables */
    2312 if( SCIPexprGetNChildren(expr) == 0 )
    2313 return SCIP_OKAY;
    2314
    2315 nlhdlrdata = SCIPnlhdlrGetData(nlhdlr);
    2316 assert(nlhdlrdata != NULL);
    2317 assert(!nlhdlrdata->isnlhdlrconvex);
    2318
    2319 SCIPdebugMsg(scip, "nlhdlr_concave detect for expr %p\n", (void*)expr);
    2320
    2321 /* initialize mapping from copied expression to original one
    2322 * 20 is not a bad estimate for the size of concave subexpressions that we can usually discover
    2323 * when expressions will be allowed to store "user"data, we could get rid of this hashmap (TODO)
    2324 */
    2325 SCIP_CALL( SCIPhashmapCreate(&nlexpr2origexpr, SCIPblkmem(scip), 20) );
    2326
    2327 if( (*enforcing & SCIP_NLHDLR_METHOD_SEPABELOW) == 0 ) /* if no separation below yet */
    2328 {
    2329 SCIP_CALL( constructExpr(scip, nlhdlrdata, &nlexpr, nlexpr2origexpr, &nleafs, expr,
    2331
    2332 if( nlexpr != NULL && nleafs > SCIP_MAXVERTEXPOLYDIM )
    2333 {
    2334 SCIPdebugMsg(scip, "Too many variables (%d) in constructed expression. Will not be able to estimate. Rejecting.\n", nleafs);
    2335 SCIP_CALL( SCIPreleaseExpr(scip, &nlexpr) );
    2336 }
    2337
    2338 if( nlexpr != NULL )
    2339 {
    2340 assert(SCIPexprGetNChildren(nlexpr) > 0); /* should not be trivial */
    2341
    2342 *participating |= SCIP_NLHDLR_METHOD_SEPABELOW;
    2343
    2344 SCIPdebugMsg(scip, "detected expr %p to be concave -> can enforce expr <= auxvar\n", (void*)expr);
    2345 }
    2346 else
    2347 {
    2348 SCIP_CALL( SCIPhashmapRemoveAll(nlexpr2origexpr) );
    2349 }
    2350 }
    2351
    2352 if( (*enforcing & SCIP_NLHDLR_METHOD_SEPAABOVE) == 0 && nlexpr == NULL ) /* if no separation above and not concave */
    2353 {
    2354 SCIP_CALL( constructExpr(scip, nlhdlrdata, &nlexpr, nlexpr2origexpr, &nleafs, expr,
    2356
    2357 if( nlexpr != NULL && nleafs > SCIP_MAXVERTEXPOLYDIM )
    2358 {
    2359 SCIPdebugMsg(scip, "Too many variables (%d) in constructed expression. Will not be able to estimate. Rejecting.\n", nleafs);
    2360 SCIP_CALL( SCIPreleaseExpr(scip, &nlexpr) );
    2361 }
    2362
    2363 if( nlexpr != NULL )
    2364 {
    2365 assert(SCIPexprGetNChildren(nlexpr) > 0); /* should not be trivial */
    2366
    2367 *participating |= SCIP_NLHDLR_METHOD_SEPAABOVE;
    2368
    2369 SCIPdebugMsg(scip, "detected expr %p to be convex -> can enforce expr >= auxvar\n", (void*)expr);
    2370 }
    2371 }
    2372
    2373 /* everything we participate in we also enforce (at the moment) */
    2374 *enforcing |= *participating;
    2375
    2376 assert(*participating || nlexpr == NULL);
    2377 if( !*participating )
    2378 {
    2379 SCIPhashmapFree(&nlexpr2origexpr);
    2380 return SCIP_OKAY;
    2381 }
    2382
    2383 /* create the expression data of the nonlinear handler
    2384 * notify conshdlr about expr for which we will require auxiliary variables and use activity
    2385 */
    2386 SCIP_CALL( createNlhdlrExprData(scip, nlhdlrdata, nlhdlrexprdata, expr, nlexpr, nlexpr2origexpr, nleafs, *participating) );
    2387
    2388 return SCIP_OKAY;
    2389}
    2390
    2391/** init sepa callback that initializes LP */
    2392static
    2393SCIP_DECL_NLHDLRINITSEPA(nlhdlrInitSepaConcave)
    2394{
    2395 SCIP_EXPR* nlexpr;
    2396 SCIP_EXPRCURV curvature;
    2397 SCIP_Bool success;
    2398 SCIP_ROWPREP* rowprep = NULL;
    2399 SCIP_ROW* row;
    2400
    2401 assert(scip != NULL);
    2402 assert(expr != NULL);
    2403 assert(nlhdlrexprdata != NULL);
    2404
    2405 nlexpr = nlhdlrexprdata->nlexpr;
    2406 assert(nlexpr != NULL);
    2407 assert(SCIPhashmapGetImage(nlhdlrexprdata->nlexpr2origexpr, (void*)nlexpr) == expr);
    2408
    2409 /* setup nlhdlrexprdata->leafexprs */
    2410 SCIP_CALL( collectLeafs(scip, nlhdlrexprdata) );
    2411
    2412 curvature = SCIPexprGetCurvature(nlexpr);
    2413 assert(curvature == SCIP_EXPRCURV_CONVEX || curvature == SCIP_EXPRCURV_CONCAVE);
    2414 /* we can only be estimating on non-convex side */
    2415 if( curvature == SCIP_EXPRCURV_CONCAVE )
    2416 overestimate = FALSE;
    2417 else if( curvature == SCIP_EXPRCURV_CONVEX )
    2418 underestimate = FALSE;
    2419 if( !overestimate && !underestimate )
    2420 return SCIP_OKAY;
    2421
    2422 /* compute estimator and store in rowprep */
    2424 SCIP_CALL( estimateVertexPolyhedral(scip, conshdlr, nlhdlr, nlhdlrexprdata, NULL, TRUE, overestimate,
    2425 overestimate ? SCIPinfinity(scip) : -SCIPinfinity(scip), rowprep, &success) );
    2426 if( !success )
    2427 {
    2428 SCIPdebugMsg(scip, "failed to compute facet of convex hull\n");
    2429 goto TERMINATE;
    2430 }
    2431
    2432 /* add auxiliary variable */
    2434
    2435 /* straighten out numerics */
    2436 SCIP_CALL( SCIPcleanupRowprep2(scip, rowprep, NULL, SCIPgetHugeValue(scip), &success) );
    2437 if( !success )
    2438 {
    2439 SCIPdebugMsg(scip, "failed to cleanup rowprep numerics\n");
    2440 goto TERMINATE;
    2441 }
    2442
    2443 (void) SCIPsnprintf(SCIProwprepGetName(rowprep), SCIP_MAXSTRLEN, "%sestimate_concave%p_initsepa",
    2444 overestimate ? "over" : "under", (void*)expr);
    2445 SCIP_CALL( SCIPgetRowprepRowCons(scip, &row, rowprep, cons) );
    2446
    2447#ifdef SCIP_DEBUG
    2448 SCIPinfoMessage(scip, NULL, "initsepa computed row: ");
    2449 SCIPprintRow(scip, row, NULL);
    2450#endif
    2451
    2452 SCIP_CALL( SCIPaddRow(scip, row, FALSE, infeasible) );
    2453 SCIP_CALL( SCIPreleaseRow(scip, &row) );
    2454
    2455 TERMINATE:
    2456 if( rowprep != NULL )
    2457 SCIPfreeRowprep(scip, &rowprep);
    2458
    2459 return SCIP_OKAY;
    2460}
    2461
    2462/** estimator callback */
    2463static
    2464SCIP_DECL_NLHDLRESTIMATE(nlhdlrEstimateConcave)
    2465{ /*lint --e{715}*/
    2466 SCIP_ROWPREP* rowprep;
    2467
    2468 assert(scip != NULL);
    2469 assert(expr != NULL);
    2470 assert(nlhdlrexprdata != NULL);
    2471 assert(nlhdlrexprdata->nlexpr != NULL);
    2472 assert(rowpreps != NULL);
    2473 assert(success != NULL);
    2474
    2475 assert(SCIPhashmapGetImage(nlhdlrexprdata->nlexpr2origexpr, (void*)nlhdlrexprdata->nlexpr) == expr);
    2476
    2477 /* we must be called only for the side that we indicated to participate in during DETECT */
    2478 assert(SCIPexprGetCurvature(nlhdlrexprdata->nlexpr) == SCIP_EXPRCURV_CONVEX
    2479 || SCIPexprGetCurvature(nlhdlrexprdata->nlexpr) == SCIP_EXPRCURV_CONCAVE);
    2480 assert(!overestimate || SCIPexprGetCurvature(nlhdlrexprdata->nlexpr) == SCIP_EXPRCURV_CONVEX);
    2481 assert( overestimate || SCIPexprGetCurvature(nlhdlrexprdata->nlexpr) == SCIP_EXPRCURV_CONCAVE);
    2482
    2483 *success = FALSE;
    2484 *addedbranchscores = FALSE;
    2485
    2487
    2488 SCIP_CALL( estimateVertexPolyhedral(scip, conshdlr, nlhdlr, nlhdlrexprdata, sol, FALSE, overestimate, targetvalue, rowprep, success) );
    2489
    2490 if( *success )
    2491 {
    2492 SCIP_CALL( SCIPsetPtrarrayVal(scip, rowpreps, 0, rowprep) );
    2493
    2494 (void) SCIPsnprintf(SCIProwprepGetName(rowprep), SCIP_MAXSTRLEN, "%sestimate_concave%p_%s%" SCIP_LONGINT_FORMAT,
    2495 overestimate ? "over" : "under",
    2496 (void*)expr,
    2497 sol != NULL ? "sol" : "lp",
    2498 sol != NULL ? (SCIP_Longint) SCIPsolGetIndex(sol) : SCIPgetNLPs(scip));
    2499 }
    2500 else
    2501 {
    2502 SCIPfreeRowprep(scip, &rowprep);
    2503 }
    2504
    2505 if( addbranchscores )
    2506 {
    2507 SCIP_Real violation;
    2508
    2509 /* check how much is the violation on the side that we estimate */
    2510 if( auxvalue == SCIP_INVALID )
    2511 {
    2512 /* if cannot evaluate, then always branch */
    2513 violation = SCIPinfinity(scip);
    2514 }
    2515 else
    2516 {
    2517 SCIP_Real auxval;
    2518
    2519 /* get value of auxiliary variable of this expression */
    2520 assert(SCIPgetExprAuxVarNonlinear(expr) != NULL);
    2521 auxval = SCIPgetSolVal(scip, sol, SCIPgetExprAuxVarNonlinear(expr));
    2522
    2523 /* compute the violation
    2524 * if we underestimate, then we enforce expr <= auxval, so violation is (positive part of) auxvalue - auxval
    2525 * if we overestimate, then we enforce expr >= auxval, so violation is (positive part of) auxval - auxvalue
    2526 */
    2527 if( !overestimate )
    2528 violation = MAX(0.0, auxvalue - auxval);
    2529 else
    2530 violation = MAX(0.0, auxval - auxvalue);
    2531 }
    2532 assert(violation >= 0.0);
    2533
    2534 /* add violation as branching-score to expressions; the core will take care distributing this onto variables */
    2535 if( nlhdlrexprdata->nleafs == 1 )
    2536 {
    2537 SCIP_EXPR* e;
    2538 e = (SCIP_EXPR*)SCIPhashmapGetImage(nlhdlrexprdata->nlexpr2origexpr, nlhdlrexprdata->leafexprs[0]);
    2539 SCIP_CALL( SCIPaddExprsViolScoreNonlinear(scip, &e, 1, violation, sol, addedbranchscores) );
    2540 }
    2541 else
    2542 {
    2543 SCIP_EXPR** exprs;
    2544 int c;
    2545
    2546 /* map leaf expressions back to original expressions
    2547 * TODO do this once at end of detect and store in nlhdlrexprdata
    2548 */
    2549 SCIP_CALL( SCIPallocBufferArray(scip, &exprs, nlhdlrexprdata->nleafs) );
    2550 for( c = 0; c < nlhdlrexprdata->nleafs; ++c )
    2551 exprs[c] = (SCIP_EXPR*)SCIPhashmapGetImage(nlhdlrexprdata->nlexpr2origexpr, nlhdlrexprdata->leafexprs[c]);
    2552
    2553 SCIP_CALL( SCIPaddExprsViolScoreNonlinear(scip, exprs, nlhdlrexprdata->nleafs, violation, sol, addedbranchscores) );
    2554
    2555 SCIPfreeBufferArray(scip, &exprs);
    2556 }
    2557 }
    2558
    2559 return SCIP_OKAY;
    2560}
    2561
    2562/** includes nonlinear handler in another scip instance */
    2563static
    2564SCIP_DECL_NLHDLRCOPYHDLR(nlhdlrCopyhdlrConcave)
    2565{ /*lint --e{715}*/
    2566 assert(targetscip != NULL);
    2567 assert(sourcenlhdlr != NULL);
    2568 assert(strcmp(SCIPnlhdlrGetName(sourcenlhdlr), CONCAVE_NLHDLR_NAME) == 0);
    2569
    2570 SCIP_CALL( SCIPincludeNlhdlrConcave(targetscip) );
    2571
    2572 return SCIP_OKAY;
    2573}
    2574
    2575/** includes concave nonlinear handler in nonlinear constraint handler */
    2577 SCIP* scip /**< SCIP data structure */
    2578 )
    2579{
    2580 SCIP_NLHDLR* nlhdlr;
    2581 SCIP_NLHDLRDATA* nlhdlrdata;
    2582
    2583 assert(scip != NULL);
    2584
    2585 SCIP_CALL( SCIPallocBlockMemory(scip, &nlhdlrdata) );
    2586 nlhdlrdata->isnlhdlrconvex = FALSE;
    2587 nlhdlrdata->evalsol = NULL;
    2588 nlhdlrdata->randnumgen = NULL;
    2589
    2591 CONCAVE_NLHDLR_DETECTPRIORITY, CONCAVE_NLHDLR_ENFOPRIORITY, nlhdlrDetectConcave, nlhdlrEvalAuxConvexConcave, nlhdlrdata) );
    2592 assert(nlhdlr != NULL);
    2593
    2594 SCIP_CALL( SCIPaddBoolParam(scip, "nlhdlr/" CONCAVE_NLHDLR_NAME "/detectsum",
    2595 "whether to run convexity detection when the root of an expression is a sum",
    2596 &nlhdlrdata->detectsum, FALSE, DEFAULT_DETECTSUM, NULL, NULL) );
    2597
    2598 /* "extended" formulations of a concave expressions can give worse estimators */
    2599 nlhdlrdata->extendedform = FALSE;
    2600
    2601 SCIP_CALL( SCIPaddBoolParam(scip, "nlhdlr/" CONCAVE_NLHDLR_NAME "/cvxquadratic",
    2602 "whether to use convexity check on quadratics",
    2603 &nlhdlrdata->cvxquadratic, TRUE, DEFAULT_CVXQUADRATIC_CONCAVE, NULL, NULL) );
    2604
    2605 SCIP_CALL( SCIPaddBoolParam(scip, "nlhdlr/" CONCAVE_NLHDLR_NAME "/cvxsignomial",
    2606 "whether to use convexity check on signomials",
    2607 &nlhdlrdata->cvxsignomial, TRUE, DEFAULT_CVXSIGNOMIAL, NULL, NULL) );
    2608
    2609 SCIP_CALL( SCIPaddBoolParam(scip, "nlhdlr/" CONCAVE_NLHDLR_NAME "/cvxprodcomp",
    2610 "whether to use convexity check on product composition f(h)*h",
    2611 &nlhdlrdata->cvxprodcomp, TRUE, DEFAULT_CVXPRODCOMP, NULL, NULL) );
    2612
    2613 SCIP_CALL( SCIPaddBoolParam(scip, "nlhdlr/" CONCAVE_NLHDLR_NAME "/handletrivial",
    2614 "whether to also handle trivial convex expressions",
    2615 &nlhdlrdata->handletrivial, TRUE, DEFAULT_HANDLETRIVIAL, NULL, NULL) );
    2616
    2617 SCIPnlhdlrSetFreeHdlrData(nlhdlr, nlhdlrfreeHdlrDataConvexConcave);
    2618 SCIPnlhdlrSetCopyHdlr(nlhdlr, nlhdlrCopyhdlrConcave);
    2619 SCIPnlhdlrSetFreeExprData(nlhdlr, nlhdlrfreeExprDataConvexConcave);
    2620 SCIPnlhdlrSetSepa(nlhdlr, nlhdlrInitSepaConcave, NULL, nlhdlrEstimateConcave, NULL);
    2621 SCIPnlhdlrSetInitExit(nlhdlr, NULL, nlhdlrExitConcave);
    2622
    2623 return SCIP_OKAY;
    2624}
    2625
    2626/** checks whether a given expression is convex or concave w.r.t. the original variables
    2627 *
    2628 * This function uses the methods that are used in the detection algorithm of the convex nonlinear handler.
    2629 */
    2631 SCIP* scip, /**< SCIP data structure */
    2632 SCIP_EXPR* expr, /**< expression */
    2633 SCIP_EXPRCURV curv, /**< curvature to check for */
    2634 SCIP_Bool* success, /**< buffer to store whether expression has curvature curv (w.r.t. original variables) */
    2635 SCIP_HASHMAP* assumevarfixed /**< hashmap containing variables that should be assumed to be fixed, or NULL */
    2636 )
    2637{
    2638 SCIP_NLHDLRDATA nlhdlrdata;
    2639 SCIP_EXPR* rootnlexpr;
    2640 SCIP_HASHMAP* nlexpr2origexpr;
    2641 int nleafs;
    2642
    2643 assert(expr != NULL);
    2644 assert(curv != SCIP_EXPRCURV_UNKNOWN);
    2645 assert(success != NULL);
    2646
    2647 /* create temporary hashmap */
    2648 SCIP_CALL( SCIPhashmapCreate(&nlexpr2origexpr, SCIPblkmem(scip), 20) );
    2649
    2650 /* prepare nonlinear handler data */
    2651 nlhdlrdata.isnlhdlrconvex = TRUE;
    2652 nlhdlrdata.evalsol = NULL;
    2653 nlhdlrdata.detectsum = TRUE;
    2654 nlhdlrdata.extendedform = FALSE;
    2655 nlhdlrdata.cvxquadratic = TRUE;
    2656 nlhdlrdata.cvxsignomial = TRUE;
    2657 nlhdlrdata.cvxprodcomp = TRUE;
    2658 nlhdlrdata.handletrivial = TRUE;
    2659
    2660 SCIP_CALL( constructExpr(scip, &nlhdlrdata, &rootnlexpr, nlexpr2origexpr, &nleafs, expr, curv, assumevarfixed, FALSE, success) );
    2661
    2662 /* free created expression */
    2663 if( rootnlexpr != NULL )
    2664 {
    2665 SCIP_CALL( SCIPreleaseExpr(scip, &rootnlexpr) );
    2666 }
    2667
    2668 /* free hashmap */
    2669 SCIPhashmapFree(&nlexpr2origexpr);
    2670
    2671 return SCIP_OKAY;
    2672}
    SCIP_VAR * h
    Definition: circlepacking.c:68
    SCIP_VAR ** x
    Definition: circlepacking.c:63
    constraint handler for nonlinear constraints specified by algebraic expressions
    defines macros for basic operations in double-double arithmetic giving roughly twice the precision of...
    #define SCIPquadprecSumQD(r, a, b)
    Definition: dbldblarith.h:62
    #define QUAD_ASSIGN(a, constant)
    Definition: dbldblarith.h:51
    #define QUAD(x)
    Definition: dbldblarith.h:47
    #define QUAD_TO_DBL(x)
    Definition: dbldblarith.h:49
    #define NULL
    Definition: def.h:248
    #define SCIP_MAXSTRLEN
    Definition: def.h:269
    #define SCIP_Longint
    Definition: def.h:141
    #define SCIP_INVALID
    Definition: def.h:178
    #define SCIP_Bool
    Definition: def.h:91
    #define MIN(x, y)
    Definition: def.h:224
    #define SCIP_Real
    Definition: def.h:156
    #define TRUE
    Definition: def.h:93
    #define FALSE
    Definition: def.h:94
    #define MAX(x, y)
    Definition: def.h:220
    #define SCIP_CALL_ABORT(x)
    Definition: def.h:334
    #define SCIP_LONGINT_FORMAT
    Definition: def.h:148
    #define REALABS(x)
    Definition: def.h:182
    #define SCIP_CALL(x)
    Definition: def.h:355
    absolute expression handler
    variable expression handler
    SCIP_Bool SCIPassumeConvexNonlinear(SCIP_CONSHDLR *conshdlr)
    SCIP_VAR * SCIPgetExprAuxVarNonlinear(SCIP_EXPR *expr)
    SCIP_RETCODE SCIPaddExprsViolScoreNonlinear(SCIP *scip, SCIP_EXPR **exprs, int nexprs, SCIP_Real violscore, SCIP_SOL *sol, SCIP_Bool *success)
    SCIP_RETCODE SCIPregisterExprUsageNonlinear(SCIP *scip, SCIP_EXPR *expr, SCIP_Bool useauxvar, SCIP_Bool useactivityforprop, SCIP_Bool useactivityforsepabelow, SCIP_Bool useactivityforsepaabove)
    SCIP_RETCODE SCIPcomputeFacetVertexPolyhedralNonlinear(SCIP *scip, SCIP_CONSHDLR *conshdlr, SCIP_Bool overestimate, SCIP_DECL_VERTEXPOLYFUN((*function)), void *fundata, SCIP_Real *xstar, SCIP_Real *box, int nallvars, SCIP_Real targetvalue, SCIP_Bool *success, SCIP_Real *facetcoefs, SCIP_Real *facetconstant)
    #define SCIP_MAXVERTEXPOLYDIM
    SCIP_RETCODE SCIPcreateExprVar(SCIP *scip, SCIP_EXPR **expr, SCIP_VAR *var, SCIP_DECL_EXPR_OWNERCREATE((*ownercreate)), void *ownercreatedata)
    Definition: expr_var.c:398
    SCIP_Bool SCIPisExprAbs(SCIP *scip, SCIP_EXPR *expr)
    Definition: expr_abs.c:546
    void SCIPhashmapFree(SCIP_HASHMAP **hashmap)
    Definition: misc.c:3095
    void * SCIPhashmapGetImage(SCIP_HASHMAP *hashmap, void *origin)
    Definition: misc.c:3284
    SCIP_RETCODE SCIPhashmapInsert(SCIP_HASHMAP *hashmap, void *origin, void *image)
    Definition: misc.c:3143
    SCIP_RETCODE SCIPhashmapSetImage(SCIP_HASHMAP *hashmap, void *origin, void *image)
    Definition: misc.c:3366
    int SCIPhashmapEntryGetImageInt(SCIP_HASHMAPENTRY *entry)
    Definition: misc.c:3623
    int SCIPhashmapGetNEntries(SCIP_HASHMAP *hashmap)
    Definition: misc.c:3584
    SCIP_HASHMAPENTRY * SCIPhashmapGetEntry(SCIP_HASHMAP *hashmap, int entryidx)
    Definition: misc.c:3592
    SCIP_RETCODE SCIPhashmapCreate(SCIP_HASHMAP **hashmap, BMS_BLKMEM *blkmem, int mapsize)
    Definition: misc.c:3061
    void * SCIPhashmapEntryGetOrigin(SCIP_HASHMAPENTRY *entry)
    Definition: misc.c:3603
    SCIP_Bool SCIPhashmapExists(SCIP_HASHMAP *hashmap, void *origin)
    Definition: misc.c:3466
    SCIP_RETCODE SCIPhashmapInsertInt(SCIP_HASHMAP *hashmap, void *origin, int image)
    Definition: misc.c:3179
    SCIP_RETCODE SCIPhashmapRemoveAll(SCIP_HASHMAP *hashmap)
    Definition: misc.c:3676
    void SCIPhashsetFree(SCIP_HASHSET **hashset, BMS_BLKMEM *blkmem)
    Definition: misc.c:3833
    SCIP_Bool SCIPhashsetExists(SCIP_HASHSET *hashset, void *element)
    Definition: misc.c:3860
    SCIP_RETCODE SCIPhashsetInsert(SCIP_HASHSET *hashset, BMS_BLKMEM *blkmem, void *element)
    Definition: misc.c:3843
    SCIP_RETCODE SCIPhashsetCreate(SCIP_HASHSET **hashset, BMS_BLKMEM *blkmem, int size)
    Definition: misc.c:3802
    void SCIPinfoMessage(SCIP *scip, FILE *file, const char *formatstr,...)
    Definition: scip_message.c:208
    #define SCIPdebugMsg
    Definition: scip_message.h:78
    SCIP_RETCODE SCIPhasExprCurvature(SCIP *scip, SCIP_EXPR *expr, SCIP_EXPRCURV curv, SCIP_Bool *success, SCIP_HASHMAP *assumevarfixed)
    SCIP_RETCODE SCIPincludeNlhdlrConvex(SCIP *scip)
    SCIP_RETCODE SCIPincludeNlhdlrConcave(SCIP *scip)
    SCIP_RETCODE SCIPaddRealParam(SCIP *scip, const char *name, const char *desc, SCIP_Real *valueptr, SCIP_Bool isadvanced, SCIP_Real defaultvalue, SCIP_Real minvalue, SCIP_Real maxvalue, SCIP_DECL_PARAMCHGD((*paramchgd)), SCIP_PARAMDATA *paramdata)
    Definition: scip_param.c:139
    SCIP_RETCODE SCIPaddBoolParam(SCIP *scip, const char *name, const char *desc, SCIP_Bool *valueptr, SCIP_Bool isadvanced, SCIP_Bool defaultvalue, SCIP_DECL_PARAMCHGD((*paramchgd)), SCIP_PARAMDATA *paramdata)
    Definition: scip_param.c:57
    SCIP_RETCODE SCIPaddPoolCut(SCIP *scip, SCIP_ROW *row)
    Definition: scip_cut.c:336
    SCIP_RETCODE SCIPaddRow(SCIP *scip, SCIP_ROW *row, SCIP_Bool forcecut, SCIP_Bool *infeasible)
    Definition: scip_cut.c:225
    SCIP_RETCODE SCIPsetPtrarrayVal(SCIP *scip, SCIP_PTRARRAY *ptrarray, int idx, void *val)
    const char * SCIPexprhdlrGetName(SCIP_EXPRHDLR *exprhdlr)
    Definition: expr.c:545
    SCIP_Bool SCIPexprhdlrHasBwdiff(SCIP_EXPRHDLR *exprhdlr)
    Definition: expr.c:595
    const char * SCIPexprcurvGetName(SCIP_EXPRCURV curv)
    Definition: exprcurv.c:586
    SCIP_RETCODE SCIPappendExprChild(SCIP *scip, SCIP_EXPR *expr, SCIP_EXPR *child)
    Definition: scip_expr.c:1256
    SCIP_RETCODE SCIPevalExpr(SCIP *scip, SCIP_EXPR *expr, SCIP_SOL *sol, SCIP_Longint soltag)
    Definition: scip_expr.c:1661
    int SCIPexprGetNChildren(SCIP_EXPR *expr)
    Definition: expr.c:3872
    SCIP_RETCODE SCIPcomputeExprIntegrality(SCIP *scip, SCIP_EXPR *expr)
    Definition: scip_expr.c:2040
    SCIP_Real SCIPgetExponentExprPow(SCIP_EXPR *expr)
    Definition: expr_pow.c:3448
    SCIP_Bool SCIPisExprProduct(SCIP *scip, SCIP_EXPR *expr)
    Definition: scip_expr.c:1490
    SCIP_RETCODE SCIPevalExprGradient(SCIP *scip, SCIP_EXPR *expr, SCIP_SOL *sol, SCIP_Longint soltag)
    Definition: scip_expr.c:1692
    SCIP_Bool SCIPexpriterIsEnd(SCIP_EXPRITER *iterator)
    Definition: expriter.c:969
    void SCIPexprSetCurvature(SCIP_EXPR *expr, SCIP_EXPRCURV curvature)
    Definition: expr.c:4080
    SCIP_EXPR * SCIPexpriterSkipDFS(SCIP_EXPRITER *iterator)
    Definition: expriter.c:930
    SCIP_Real SCIPexprGetDerivative(SCIP_EXPR *expr)
    Definition: expr.c:3972
    SCIP_Bool SCIPisExprSum(SCIP *scip, SCIP_EXPR *expr)
    Definition: scip_expr.c:1479
    SCIP_RETCODE SCIPduplicateExprShallow(SCIP *scip, SCIP_EXPR *expr, SCIP_EXPR **copyexpr, SCIP_DECL_EXPR_OWNERCREATE((*ownercreate)), void *ownercreatedata)
    Definition: scip_expr.c:1327
    SCIP_Bool SCIPexprIsIntegral(SCIP_EXPR *expr)
    Definition: expr.c:4101
    void SCIPexprGetQuadraticData(SCIP_EXPR *expr, SCIP_Real *constant, int *nlinexprs, SCIP_EXPR ***linexprs, SCIP_Real **lincoefs, int *nquadexprs, int *nbilinexprs, SCIP_Real **eigenvalues, SCIP_Real **eigenvectors)
    Definition: expr.c:4141
    SCIP_RETCODE SCIPreplaceExprChild(SCIP *scip, SCIP_EXPR *expr, int childidx, SCIP_EXPR *newchild)
    Definition: scip_expr.c:1274
    SCIP_Real * SCIPgetCoefsExprSum(SCIP_EXPR *expr)
    Definition: expr_sum.c:1554
    SCIP_Bool SCIPisExprValue(SCIP *scip, SCIP_EXPR *expr)
    Definition: scip_expr.c:1468
    SCIP_Real SCIPgetCoefExprProduct(SCIP_EXPR *expr)
    int SCIPcompareExpr(SCIP *scip, SCIP_EXPR *expr1, SCIP_EXPR *expr2)
    Definition: scip_expr.c:1759
    SCIP_RETCODE SCIPreleaseExpr(SCIP *scip, SCIP_EXPR **expr)
    Definition: scip_expr.c:1443
    SCIP_EXPR * SCIPexpriterGetCurrent(SCIP_EXPRITER *iterator)
    Definition: expriter.c:683
    SCIP_Bool SCIPexprcurvMonomialInv(SCIP_EXPRCURV monomialcurv, int nfactors, SCIP_Real *exponents, SCIP_INTERVAL *factorbounds, SCIP_EXPRCURV *factorcurv)
    Definition: exprcurv.c:457
    void SCIPexpriterSetStagesDFS(SCIP_EXPRITER *iterator, SCIP_EXPRITER_STAGE stopstages)
    Definition: expriter.c:664
    SCIP_Bool SCIPisExprVar(SCIP *scip, SCIP_EXPR *expr)
    Definition: scip_expr.c:1457
    SCIP_RETCODE SCIPcomputeExprQuadraticCurvature(SCIP *scip, SCIP_EXPR *expr, SCIP_EXPRCURV *curv, SCIP_HASHMAP *assumevarfixed, SCIP_Bool storeeigeninfo)
    Definition: scip_expr.c:2611
    SCIP_EXPRCURV SCIPexprcurvMultiply(SCIP_Real factor, SCIP_EXPRCURV curvature)
    Definition: exprcurv.c:88
    SCIP_RETCODE SCIPcreateExpriter(SCIP *scip, SCIP_EXPRITER **iterator)
    Definition: scip_expr.c:2362
    SCIP_RETCODE SCIPprintExpr(SCIP *scip, SCIP_EXPR *expr, FILE *file)
    Definition: scip_expr.c:1512
    SCIP_EXPRCURV SCIPexprGetCurvature(SCIP_EXPR *expr)
    Definition: expr.c:4070
    SCIP_Bool SCIPisExprPower(SCIP *scip, SCIP_EXPR *expr)
    Definition: scip_expr.c:1501
    SCIP_Real SCIPexprGetEvalValue(SCIP_EXPR *expr)
    Definition: expr.c:3946
    SCIP_EXPR * SCIPexpriterGetNext(SCIP_EXPRITER *iterator)
    Definition: expriter.c:858
    SCIP_RETCODE SCIPcheckExprQuadratic(SCIP *scip, SCIP_EXPR *expr, SCIP_Bool *isquadratic)
    Definition: scip_expr.c:2402
    SCIP_EXPR ** SCIPexprGetChildren(SCIP_EXPR *expr)
    Definition: expr.c:3882
    SCIP_Real SCIPgetConstantExprSum(SCIP_EXPR *expr)
    Definition: expr_sum.c:1569
    SCIP_VAR * SCIPgetVarExprVar(SCIP_EXPR *expr)
    Definition: expr_var.c:424
    SCIP_INTERVAL SCIPexprGetActivity(SCIP_EXPR *expr)
    Definition: expr.c:4028
    void SCIPexprGetQuadraticQuadTerm(SCIP_EXPR *quadexpr, int termidx, SCIP_EXPR **expr, SCIP_Real *lincoef, SCIP_Real *sqrcoef, int *nadjbilin, int **adjbilin, SCIP_EXPR **sqrexpr)
    Definition: expr.c:4186
    int SCIPexpriterGetChildIdxDFS(SCIP_EXPRITER *iterator)
    Definition: expriter.c:707
    void SCIPfreeExpriter(SCIP_EXPRITER **iterator)
    Definition: scip_expr.c:2376
    void SCIPcaptureExpr(SCIP_EXPR *expr)
    Definition: scip_expr.c:1435
    SCIP_RETCODE SCIPexpriterInit(SCIP_EXPRITER *iterator, SCIP_EXPR *expr, SCIP_EXPRITER_TYPE type, SCIP_Bool allowrevisit)
    Definition: expriter.c:501
    SCIP_Longint SCIPexprGetDiffTag(SCIP_EXPR *expr)
    Definition: expr.c:4015
    SCIP_RETCODE SCIPremoveExprChildren(SCIP *scip, SCIP_EXPR *expr)
    Definition: scip_expr.c:1293
    SCIP_RETCODE SCIPevalExprActivity(SCIP *scip, SCIP_EXPR *expr)
    Definition: scip_expr.c:1742
    SCIP_EXPRHDLR * SCIPexprGetHdlr(SCIP_EXPR *expr)
    Definition: expr.c:3895
    SCIP_EXPR * SCIPexpriterGetChildExprDFS(SCIP_EXPRITER *iterator)
    Definition: expriter.c:721
    #define SCIPallocClearBlockMemory(scip, ptr)
    Definition: scip_mem.h:91
    BMS_BLKMEM * SCIPblkmem(SCIP *scip)
    Definition: scip_mem.c:57
    int SCIPcalcMemGrowSize(SCIP *scip, int num)
    Definition: scip_mem.c:139
    #define SCIPallocBufferArray(scip, ptr, num)
    Definition: scip_mem.h:124
    #define SCIPreallocBufferArray(scip, ptr, num)
    Definition: scip_mem.h:128
    #define SCIPfreeBufferArray(scip, ptr)
    Definition: scip_mem.h:136
    #define SCIPallocBlockMemoryArray(scip, ptr, num)
    Definition: scip_mem.h:93
    #define SCIPfreeBlockMemory(scip, ptr)
    Definition: scip_mem.h:108
    #define SCIPfreeBlockMemoryArrayNull(scip, ptr, num)
    Definition: scip_mem.h:111
    #define SCIPallocBlockMemory(scip, ptr)
    Definition: scip_mem.h:89
    void SCIPnlhdlrSetCopyHdlr(SCIP_NLHDLR *nlhdlr, SCIP_DECL_NLHDLRCOPYHDLR((*copy)))
    Definition: nlhdlr.c:77
    void SCIPnlhdlrSetFreeExprData(SCIP_NLHDLR *nlhdlr, SCIP_DECL_NLHDLRFREEEXPRDATA((*freeexprdata)))
    Definition: nlhdlr.c:99
    void SCIPnlhdlrSetSollinearize(SCIP_NLHDLR *nlhdlr, SCIP_DECL_NLHDLRSOLLINEARIZE((*sollinearize)))
    Definition: nlhdlr.c:155
    SCIP_NLHDLRDATA * SCIPnlhdlrGetData(SCIP_NLHDLR *nlhdlr)
    Definition: nlhdlr.c:217
    void SCIPnlhdlrSetFreeHdlrData(SCIP_NLHDLR *nlhdlr, SCIP_DECL_NLHDLRFREEHDLRDATA((*freehdlrdata)))
    Definition: nlhdlr.c:88
    void SCIPnlhdlrSetSepa(SCIP_NLHDLR *nlhdlr, SCIP_DECL_NLHDLRINITSEPA((*initsepa)), SCIP_DECL_NLHDLRENFO((*enfo)), SCIP_DECL_NLHDLRESTIMATE((*estimate)), SCIP_DECL_NLHDLREXITSEPA((*exitsepa)))
    Definition: nlhdlr.c:137
    void SCIPnlhdlrSetInitExit(SCIP_NLHDLR *nlhdlr, SCIP_DECL_NLHDLRINIT((*init)), SCIP_DECL_NLHDLREXIT((*exit_)))
    Definition: nlhdlr.c:111
    const char * SCIPnlhdlrGetName(SCIP_NLHDLR *nlhdlr)
    Definition: nlhdlr.c:167
    SCIP_RETCODE SCIPincludeNlhdlrNonlinear(SCIP *scip, SCIP_NLHDLR **nlhdlr, const char *name, const char *desc, int detectpriority, int enfopriority, SCIP_DECL_NLHDLRDETECT((*detect)), SCIP_DECL_NLHDLREVALAUX((*evalaux)), SCIP_NLHDLRDATA *nlhdlrdata)
    SCIP_RETCODE SCIPprintRow(SCIP *scip, SCIP_ROW *row, FILE *file)
    Definition: scip_lp.c:2176
    SCIP_RETCODE SCIPreleaseRow(SCIP *scip, SCIP_ROW **row)
    Definition: scip_lp.c:1508
    SCIP_RETCODE SCIPcreateSol(SCIP *scip, SCIP_SOL **sol, SCIP_HEUR *heur)
    Definition: scip_sol.c:516
    SCIP_RETCODE SCIPfreeSol(SCIP *scip, SCIP_SOL **sol)
    Definition: scip_sol.c:1252
    int SCIPsolGetIndex(SCIP_SOL *sol)
    Definition: sol.c:4290
    SCIP_RETCODE SCIPsetSolVal(SCIP *scip, SCIP_SOL *sol, SCIP_VAR *var, SCIP_Real val)
    Definition: scip_sol.c:1571
    SCIP_Real SCIPgetSolVal(SCIP *scip, SCIP_SOL *sol, SCIP_VAR *var)
    Definition: scip_sol.c:1765
    SCIP_Longint SCIPgetNLPs(SCIP *scip)
    SCIP_Bool SCIPisRelEQ(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
    SCIP_Real SCIPinfinity(SCIP *scip)
    SCIP_Bool SCIPisIntegral(SCIP *scip, SCIP_Real val)
    SCIP_Real SCIPfloor(SCIP *scip, SCIP_Real val)
    SCIP_Bool SCIPisInfinity(SCIP *scip, SCIP_Real val)
    SCIP_Real SCIPround(SCIP *scip, SCIP_Real val)
    SCIP_Real SCIPgetHugeValue(SCIP *scip)
    SCIP_Real SCIPceil(SCIP *scip, SCIP_Real val)
    SCIP_Bool SCIPisEQ(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
    SCIP_Bool SCIPisZero(SCIP *scip, SCIP_Real val)
    SCIP_Real SCIPepsilon(SCIP *scip)
    SCIP_BOUNDTYPE SCIPvarGetBestBoundType(SCIP_VAR *var)
    Definition: var.c:24372
    SCIP_Real SCIPvarGetUbLocal(SCIP_VAR *var)
    Definition: var.c:24268
    SCIP_Real SCIPvarGetUbGlobal(SCIP_VAR *var)
    Definition: var.c:24142
    const char * SCIPvarGetName(SCIP_VAR *var)
    Definition: var.c:23267
    SCIP_Real SCIPvarGetLbLocal(SCIP_VAR *var)
    Definition: var.c:24234
    SCIP_Real SCIPvarGetLbGlobal(SCIP_VAR *var)
    Definition: var.c:24120
    void SCIPfreeRandom(SCIP *scip, SCIP_RANDNUMGEN **randnumgen)
    SCIP_Real SCIPrandomGetReal(SCIP_RANDNUMGEN *randnumgen, SCIP_Real minrandval, SCIP_Real maxrandval)
    Definition: misc.c:10245
    SCIP_RETCODE SCIPcreateRandom(SCIP *scip, SCIP_RANDNUMGEN **randnumgen, unsigned int initialseed, SCIP_Bool useglobalseed)
    SCIP_RETCODE SCIPcleanupRowprep2(SCIP *scip, SCIP_ROWPREP *rowprep, SCIP_SOL *sol, SCIP_Real maxcoefbound, SCIP_Bool *success)
    SCIP_RETCODE SCIPensureRowprepSize(SCIP *scip, SCIP_ROWPREP *rowprep, int size)
    Definition: misc_rowprep.c:887
    SCIP_Real * SCIProwprepGetCoefs(SCIP_ROWPREP *rowprep)
    Definition: misc_rowprep.c:649
    char * SCIProwprepGetName(SCIP_ROWPREP *rowprep)
    Definition: misc_rowprep.c:689
    SCIP_Bool SCIProwprepIsLocal(SCIP_ROWPREP *rowprep)
    Definition: misc_rowprep.c:679
    void SCIProwprepAddConstant(SCIP_ROWPREP *rowprep, SCIP_Real constant)
    Definition: misc_rowprep.c:760
    SCIP_RETCODE SCIPaddRowprepTerm(SCIP *scip, SCIP_ROWPREP *rowprep, SCIP_VAR *var, SCIP_Real coef)
    Definition: misc_rowprep.c:913
    SCIP_RETCODE SCIPgetRowprepRowCons(SCIP *scip, SCIP_ROW **row, SCIP_ROWPREP *rowprep, SCIP_CONS *cons)
    SCIP_RETCODE SCIPcreateRowprep(SCIP *scip, SCIP_ROWPREP **rowprep, SCIP_SIDETYPE sidetype, SCIP_Bool local)
    Definition: misc_rowprep.c:563
    void SCIProwprepSetLocal(SCIP_ROWPREP *rowprep, SCIP_Bool islocal)
    Definition: misc_rowprep.c:780
    void SCIPfreeRowprep(SCIP *scip, SCIP_ROWPREP **rowprep)
    Definition: misc_rowprep.c:583
    void SCIPprintRowprep(SCIP *scip, SCIP_ROWPREP *rowprep, FILE *file)
    Definition: misc_rowprep.c:801
    int SCIPsnprintf(char *t, int len, const char *s,...)
    Definition: misc.c:10827
    static SCIP_DECL_NLHDLREXIT(nlhdlrExitConvex)
    #define DEFAULT_HANDLETRIVIAL
    Definition: nlhdlr_convex.c:60
    static SCIP_RETCODE exprstackInit(SCIP *scip, EXPRSTACK *exprstack, int initsize)
    static SCIP_DECL_NLHDLREVALAUX(nlhdlrEvalAuxConvexConcave)
    static SCIP_DECL_VERTEXPOLYFUN(nlhdlrExprEvalConcave)
    static SCIP_RETCODE estimateGradientInner(SCIP *scip, SCIP_NLHDLREXPRDATA *nlhdlrexprdata, SCIP_SOL *sol, SCIP_ROWPREP *rowprep, SCIP_Bool *success)
    #define CONCAVE_NLHDLR_NAME
    Definition: nlhdlr_convex.c:49
    static SCIP_Bool exprIsMultivarLinear(SCIP *scip, SCIP_EXPR *expr)
    #define CONCAVE_NLHDLR_DESC
    Definition: nlhdlr_convex.c:50
    static SCIP_DECL_NLHDLRFREEHDLRDATA(nlhdlrfreeHdlrDataConvexConcave)
    static SCIP_RETCODE exprstackPush(SCIP *scip, EXPRSTACK *exprstack, int nexprs, SCIP_EXPR **exprs)
    static void exprstackFree(SCIP *scip, EXPRSTACK *exprstack)
    #define CONVEX_NLHDLR_DETECTPRIORITY
    Definition: nlhdlr_convex.c:46
    #define DEFAULT_EXTENDEDFORM
    Definition: nlhdlr_convex.c:55
    static SCIP_RETCODE estimateVertexPolyhedral(SCIP *scip, SCIP_CONSHDLR *conshdlr, SCIP_NLHDLR *nlhdlr, SCIP_NLHDLREXPRDATA *nlhdlrexprdata, SCIP_SOL *sol, SCIP_Bool usemidpoint, SCIP_Bool overestimate, SCIP_Real targetvalue, SCIP_ROWPREP *rowprep, SCIP_Bool *success)
    static SCIP_RETCODE createNlhdlrExprData(SCIP *scip, SCIP_NLHDLRDATA *nlhdlrdata, SCIP_NLHDLREXPRDATA **nlhdlrexprdata, SCIP_EXPR *expr, SCIP_EXPR *nlexpr, SCIP_HASHMAP *nlexpr2origexpr, int nleafs, SCIP_NLHDLR_METHOD participating)
    static SCIP_DECL_NLHDLRCOPYHDLR(nlhdlrCopyhdlrConvex)
    #define CONVEX_NLHDLR_DESC
    Definition: nlhdlr_convex.c:45
    #define CONVEX_NLHDLR_NAME
    Definition: nlhdlr_convex.c:44
    static SCIP_DECL_NLHDLRFREEEXPRDATA(nlhdlrfreeExprDataConvexConcave)
    #define DEFAULT_MAXPERTURB
    Definition: nlhdlr_convex.c:61
    #define CONCAVE_NLHDLR_DETECTPRIORITY
    Definition: nlhdlr_convex.c:51
    static SCIP_DECL_NLHDLRINITSEPA(nlhdlrInitSepaConvex)
    static const int NCURVCHECKS
    static SCIP_DECL_NLHDLRESTIMATE(nlhdlrEstimateConvex)
    #define RANDNUMINITSEED
    Definition: nlhdlr_convex.c:64
    #define CONVEX_NLHDLR_ENFOPRIORITY
    Definition: nlhdlr_convex.c:47
    #define DEFAULT_CVXSIGNOMIAL
    Definition: nlhdlr_convex.c:58
    static SCIP_RETCODE constructExpr(SCIP *scip, SCIP_NLHDLRDATA *nlhdlrdata, SCIP_EXPR **rootnlexpr, SCIP_HASHMAP *nlexpr2origexpr, int *nleafs, SCIP_EXPR *rootexpr, SCIP_EXPRCURV curv, SCIP_HASHMAP *assumevarfixed, SCIP_Bool assumecurvature, SCIP_Bool *curvsuccess)
    static SCIP_RETCODE collectLeafs(SCIP *scip, SCIP_NLHDLREXPRDATA *nlhdlrexprdata)
    #define DEFAULT_DETECTSUM
    Definition: nlhdlr_convex.c:54
    static SCIP_RETCODE nlhdlrExprCreate(SCIP *scip, SCIP_HASHMAP *nlexpr2origexpr, SCIP_EXPR **nlhdlrexpr, SCIP_EXPR *origexpr, SCIP_EXPRCURV curv)
    #define DEFAULT_CVXPRODCOMP
    Definition: nlhdlr_convex.c:59
    #define INITLPMAXVARVAL
    Definition: nlhdlr_convex.c:63
    #define DEFAULT_CVXQUADRATIC_CONCAVE
    Definition: nlhdlr_convex.c:57
    #define DEFAULT_CVXQUADRATIC_CONVEX
    Definition: nlhdlr_convex.c:56
    static SCIP_RETCODE estimateConvexSecant(SCIP *scip, SCIP_NLHDLR *nlhdlr, SCIP_NLHDLREXPRDATA *nlhdlrexprdata, SCIP_SOL *sol, SCIP_ROWPREP *rowprep, SCIP_Bool *success)
    static SCIP_DECL_NLHDLRSOLLINEARIZE(nlhdlrSollinearizeConvex)
    static SCIP_RETCODE nlhdlrExprGrowChildren(SCIP *scip, SCIP_HASHMAP *nlexpr2origexpr, SCIP_EXPR *nlhdlrexpr, SCIP_EXPRCURV *childrencurv)
    #define DECL_CURVCHECK(x)
    static SCIP_Bool exprstackIsEmpty(EXPRSTACK *exprstack)
    static SCIP_EXPR * exprstackPop(EXPRSTACK *exprstack)
    #define CONCAVE_NLHDLR_ENFOPRIORITY
    Definition: nlhdlr_convex.c:52
    static SCIP_DECL_NLHDLRDETECT(nlhdlrDetectConvex)
    static SCIP_RETCODE estimateGradient(SCIP *scip, SCIP_NLHDLR *nlhdlr, SCIP_NLHDLREXPRDATA *nlhdlrexprdata, SCIP_SOL *sol, SCIP_ROWPREP *rowprep, SCIP_Bool *success)
    nonlinear handlers for convex and concave expressions, respectively
    preparation of a linear inequality to become a SCIP_ROW
    public functions of nonlinear handlers of nonlinear constraints
    public functions to work with algebraic expressions
    SCIP_EXPR ** stack
    SCIP_Real sup
    Definition: intervalarith.h:57
    SCIP_Real inf
    Definition: intervalarith.h:56
    SCIP_NLHDLREXPRDATA * nlhdlrexprdata
    SCIP_EXPRCURV
    Definition: type_expr.h:61
    @ SCIP_EXPRCURV_CONVEX
    Definition: type_expr.h:63
    @ SCIP_EXPRCURV_LINEAR
    Definition: type_expr.h:65
    @ SCIP_EXPRCURV_UNKNOWN
    Definition: type_expr.h:62
    @ SCIP_EXPRCURV_CONCAVE
    Definition: type_expr.h:64
    #define SCIP_EXPRITER_VISITINGCHILD
    Definition: type_expr.h:695
    SCIP_MONOTONE
    Definition: type_expr.h:70
    @ SCIP_MONOTONE_UNKNOWN
    Definition: type_expr.h:71
    @ SCIP_MONOTONE_INC
    Definition: type_expr.h:72
    @ SCIP_MONOTONE_DEC
    Definition: type_expr.h:73
    @ SCIP_EXPRITER_DFS
    Definition: type_expr.h:718
    @ SCIP_BOUNDTYPE_LOWER
    Definition: type_lp.h:57
    @ SCIP_SIDETYPE_RIGHT
    Definition: type_lp.h:66
    @ SCIP_SIDETYPE_LEFT
    Definition: type_lp.h:65
    #define SCIP_NLHDLR_METHOD_SEPAABOVE
    Definition: type_nlhdlr.h:52
    struct SCIP_NlhdlrData SCIP_NLHDLRDATA
    Definition: type_nlhdlr.h:452
    #define SCIP_NLHDLR_METHOD_SEPABOTH
    Definition: type_nlhdlr.h:53
    unsigned int SCIP_NLHDLR_METHOD
    Definition: type_nlhdlr.h:57
    struct SCIP_NlhdlrExprData SCIP_NLHDLREXPRDATA
    Definition: type_nlhdlr.h:453
    #define SCIP_NLHDLR_METHOD_SEPABELOW
    Definition: type_nlhdlr.h:51
    @ SCIP_OKAY
    Definition: type_retcode.h:42
    enum SCIP_Retcode SCIP_RETCODE
    Definition: type_retcode.h:63