SCIP

    Solving Constraint Integer Programs

    nodesel_hybridestim.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-2026 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 nodesel_hybridestim.c
    26 * @ingroup DEFPLUGINS_NODESEL
    27 * @brief node selector for hybrid best estimate / best bound search
    28 * @author Tobias Achterberg
    29 */
    30
    31/*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
    32
    34#include "scip/pub_message.h"
    35#include "scip/pub_nodesel.h"
    36#include "scip/pub_tree.h"
    37#include "scip/scip_mem.h"
    38#include "scip/scip_message.h"
    39#include "scip/scip_nodesel.h"
    40#include "scip/scip_numerics.h"
    41#include "scip/scip_param.h"
    43#include "scip/scip_tree.h"
    44
    45
    46#define NODESEL_NAME "hybridestim"
    47#define NODESEL_DESC "hybrid best estimate / best bound search"
    48#define NODESEL_STDPRIORITY 50000
    49#define NODESEL_MEMSAVEPRIORITY 50
    50
    51
    52/*
    53 * Default parameter settings
    54 */
    55
    56#define MINPLUNGEDEPTH -1 /**< minimal plunging depth, before new best node may be selected (-1 for dynamic setting) */
    57#define MAXPLUNGEDEPTH -1 /**< maximal plunging depth, before new best node is forced to be selected (-1 for dynamic setting) */
    58#define MAXPLUNGEQUOT 0.25 /**< maximal quotient (curlowerbound - lowerbound)/(cutoffbound - lowerbound)
    59 * where plunging is performed */
    60#define BESTNODEFREQ 1000 /**< frequency at which the best node instead of the hybrid best estimate / best bound is selected (0: never) */
    61#define ESTIMWEIGHT 0.10 /**< weight of estimate value in node selection score (0: pure best bound search,
    62 * 1: pure best estimate search) */
    63
    64
    65/** node selector data for hybrid best estimate / best bound search node selection */
    66struct SCIP_NodeselData
    67{
    68 SCIP_Real maxplungequot; /**< maximal quotient (curlowerbound - lowerbound)/(cutoffbound - lowerbound)
    69 * where plunging is performed */
    70 SCIP_Real estimweight; /**< weight of estimate value in node selection score (0: pure best bound search,
    71 * 1: pure best estimate search) */
    72 int minplungedepth; /**< minimal plunging depth, before new best node may be selected
    73 * (-1 for dynamic setting) */
    74 int maxplungedepth; /**< maximal plunging depth, before new best node is forced to be selected
    75 * (-1 for dynamic setting) */
    76 int bestnodefreq; /**< frequency at which the best node instead of the hybrid best estimate / best bound is selected
    77 * (0: never) */
    78};
    79
    80
    81/*
    82 * Local methods
    83 */
    84
    85/** returns a weighted sum of the node's lower bound and estimate value */
    86static
    88 SCIP_NODE* node, /**< branching node */
    89 SCIP_Real estimweight /**< weight of estimate in score */
    90 )
    91{
    92 return (1.0-estimweight) * SCIPnodeGetLowerbound(node) + estimweight * SCIPnodeGetEstimate(node);
    93}
    94
    95
    96/*
    97 * Callback methods
    98 */
    99
    100/** copy method for node selector plugins (called when SCIP copies plugins) */
    101static
    102SCIP_DECL_NODESELCOPY(nodeselCopyHybridestim)
    103{ /*lint --e{715}*/
    104 assert(scip != NULL);
    105 assert(nodesel != NULL);
    106
    108
    109 /* call inclusion method of node selector */
    111
    112 return SCIP_OKAY;
    113}
    114
    115/** destructor of node selector to free user data (called when SCIP is exiting) */
    116static
    117SCIP_DECL_NODESELFREE(nodeselFreeHybridestim)
    118{ /*lint --e{715}*/
    119 SCIP_NODESELDATA* nodeseldata;
    120
    121 assert(nodesel != NULL);
    122 assert(scip != NULL);
    123
    125
    126 /* free user data of node selector */
    127 nodeseldata = SCIPnodeselGetData(nodesel);
    128 assert(nodeseldata != NULL);
    129 SCIPfreeBlockMemory(scip, &nodeseldata);
    130 SCIPnodeselSetData(nodesel, nodeseldata);
    131
    132 return SCIP_OKAY;
    133}
    134
    135
    136/** node selection method of node selector */
    137static
    138SCIP_DECL_NODESELSELECT(nodeselSelectHybridestim)
    139{ /*lint --e{715}*/
    140 SCIP_NODESELDATA* nodeseldata;
    141 int minplungedepth;
    142 int maxplungedepth;
    143 int plungedepth;
    144 int bestnodefreq;
    145 SCIP_Real maxplungequot;
    146
    147 assert(nodesel != NULL);
    148 assert(scip != NULL);
    149 assert(selnode != NULL);
    150
    152
    153 *selnode = NULL;
    154
    155 /* get node selector user data */
    156 nodeseldata = SCIPnodeselGetData(nodesel);
    157 assert(nodeseldata != NULL);
    158
    159 /* calculate minimal and maximal plunging depth */
    160 minplungedepth = nodeseldata->minplungedepth;
    161 maxplungedepth = nodeseldata->maxplungedepth;
    162 maxplungequot = nodeseldata->maxplungequot;
    163 if( minplungedepth == -1 )
    164 {
    165 minplungedepth = SCIPgetMaxDepth(scip)/10;
    167 minplungedepth += 10;
    168 if( maxplungedepth >= 0 )
    169 minplungedepth = MIN(minplungedepth, maxplungedepth);
    170 }
    171 if( maxplungedepth == -1 )
    172 maxplungedepth = SCIPgetMaxDepth(scip)/2;
    173 maxplungedepth = MAX(maxplungedepth, minplungedepth);
    174 bestnodefreq = (nodeseldata->bestnodefreq == 0 ? INT_MAX : nodeseldata->bestnodefreq);
    175
    176 /* check, if we exceeded the maximal plunging depth */
    177 plungedepth = SCIPgetPlungeDepth(scip);
    178 if( plungedepth > maxplungedepth )
    179 {
    180 /* we don't want to plunge again: select best node from the tree */
    181 SCIPdebugMsg(scip, "plungedepth: [%d,%d], cur: %d -> abort plunging\n", minplungedepth, maxplungedepth, plungedepth);
    182 if( SCIPgetNNodes(scip) % bestnodefreq == 0 )
    183 *selnode = SCIPgetBestboundNode(scip);
    184 else
    185 *selnode = SCIPgetBestNode(scip);
    186 SCIPdebugMsg(scip, " -> best node : lower=%g\n",
    187 *selnode != NULL ? SCIPnodeGetLowerbound(*selnode) : SCIPinfinity(scip));
    188 }
    189 else
    190 {
    191 SCIP_NODE* node;
    192 SCIP_Real lowerbound;
    193 SCIP_Real cutoffbound;
    194 SCIP_Real maxbound;
    195
    196 /* get global lower and cutoff bound */
    197 lowerbound = SCIPgetLowerbound(scip);
    198 cutoffbound = SCIPgetCutoffbound(scip);
    199
    200 /* if we didn't find a solution yet, the cutoff bound is usually very bad:
    201 * use only 20% of the gap as cutoff bound
    202 */
    203 if( SCIPgetNSolsFound(scip) == 0 )
    204 cutoffbound = lowerbound + 0.2 * (cutoffbound - lowerbound);
    205
    206 /* check, if plunging is forced at the current depth */
    207 if( plungedepth < minplungedepth )
    208 maxbound = SCIPinfinity(scip);
    209 else
    210 {
    211 /* calculate maximal plunging bound */
    212 maxbound = lowerbound + maxplungequot * (cutoffbound - lowerbound);
    213 }
    214
    215 SCIPdebugMsg(scip, "plungedepth: [%d,%d], cur: %d, bounds: [%g,%g], maxbound: %g\n",
    216 minplungedepth, maxplungedepth, plungedepth, lowerbound, cutoffbound, maxbound);
    217
    218 /* we want to plunge again: prefer children over siblings, and siblings over leaves,
    219 * but only select a child or sibling, if its estimate is small enough;
    220 * prefer using nodes with higher node selection priority assigned by the branching rule
    221 */
    222 node = SCIPgetPrioChild(scip);
    223 if( node != NULL && SCIPnodeGetEstimate(node) < maxbound )
    224 {
    225 *selnode = node;
    226 SCIPdebugMsg(scip, " -> selected prio child: estimate=%g\n", SCIPnodeGetEstimate(*selnode));
    227 }
    228 else
    229 {
    230 node = SCIPgetBestChild(scip);
    231 if( node != NULL && SCIPnodeGetEstimate(node) < maxbound )
    232 {
    233 *selnode = node;
    234 SCIPdebugMsg(scip, " -> selected best child: estimate=%g\n", SCIPnodeGetEstimate(*selnode));
    235 }
    236 else
    237 {
    238 node = SCIPgetPrioSibling(scip);
    239 if( node != NULL && SCIPnodeGetEstimate(node) < maxbound )
    240 {
    241 *selnode = node;
    242 SCIPdebugMsg(scip, " -> selected prio sibling: estimate=%g\n", SCIPnodeGetEstimate(*selnode));
    243 }
    244 else
    245 {
    246 node = SCIPgetBestSibling(scip);
    247 if( node != NULL && SCIPnodeGetEstimate(node) < maxbound )
    248 {
    249 *selnode = node;
    250 SCIPdebugMsg(scip, " -> selected best sibling: estimate=%g\n", SCIPnodeGetEstimate(*selnode));
    251 }
    252 else
    253 {
    254 if( SCIPgetNNodes(scip) % bestnodefreq == 0 )
    255 *selnode = SCIPgetBestboundNode(scip);
    256 else
    257 *selnode = SCIPgetBestNode(scip);
    258 SCIPdebugMsg(scip, " -> selected best leaf: estimate=%g\n",
    259 *selnode != NULL ? SCIPnodeGetEstimate(*selnode) : SCIPinfinity(scip));
    260 }
    261 }
    262 }
    263 }
    264 }
    265
    266 return SCIP_OKAY;
    267}
    268
    269
    270/** node comparison method of node selector */
    271static
    272SCIP_DECL_NODESELCOMP(nodeselCompHybridestim)
    273{ /*lint --e{715}*/
    274 SCIP_NODESELDATA* nodeseldata;
    275 SCIP_Real score1;
    276 SCIP_Real score2;
    277
    278 assert(nodesel != NULL);
    279 assert(scip != NULL);
    280
    281 nodeseldata = SCIPnodeselGetData(nodesel);
    282 assert(nodeseldata != NULL);
    283
    284 score1 = getNodeselScore(node1, nodeseldata->estimweight);
    285 score2 = getNodeselScore(node2, nodeseldata->estimweight);
    286 if( (SCIPisInfinity(scip, score1) && SCIPisInfinity(scip, score2)) ||
    287 (SCIPisInfinity(scip, -score1) && SCIPisInfinity(scip, -score2)) ||
    288 SCIPisEQ(scip, score1, score2) )
    289 {
    290 SCIP_NODETYPE nodetype1;
    291 SCIP_NODETYPE nodetype2;
    292
    293 nodetype1 = SCIPnodeGetType(node1);
    294 nodetype2 = SCIPnodeGetType(node2);
    295 if( nodetype1 == SCIP_NODETYPE_CHILD && nodetype2 != SCIP_NODETYPE_CHILD )
    296 return -1;
    297 else if( nodetype1 != SCIP_NODETYPE_CHILD && nodetype2 == SCIP_NODETYPE_CHILD )
    298 return +1;
    299 else if( nodetype1 == SCIP_NODETYPE_SIBLING && nodetype2 != SCIP_NODETYPE_SIBLING )
    300 return -1;
    301 else if( nodetype1 != SCIP_NODETYPE_SIBLING && nodetype2 == SCIP_NODETYPE_SIBLING )
    302 return +1;
    303 else
    304 {
    305 int depth1;
    306 int depth2;
    307
    308 depth1 = SCIPnodeGetDepth(node1);
    309 depth2 = SCIPnodeGetDepth(node2);
    310 if( depth1 < depth2 )
    311 return -1;
    312 else if( depth1 > depth2 )
    313 return +1;
    314 else
    315 return 0;
    316 }
    317 }
    318
    319 if( SCIPisLT(scip, score1, score2) )
    320 return -1;
    321
    322 assert(SCIPisGT(scip, score1, score2));
    323 return +1;
    324}
    325
    326
    327/*
    328 * hybridestim specific interface methods
    329 */
    330
    331/** creates the node selector for hybrid best estimate / best bound search and includes it in SCIP */
    333 SCIP* scip /**< SCIP data structure */
    334 )
    335{
    336 SCIP_NODESELDATA* nodeseldata;
    337 SCIP_NODESEL* nodesel;
    338
    339 /* allocate and initialize node selector data; this has to be freed in the destructor */
    340 SCIP_CALL( SCIPallocBlockMemory(scip, &nodeseldata) );
    341
    342 /* include node selector */
    344 nodeselSelectHybridestim, nodeselCompHybridestim, nodeseldata) );
    345
    346 assert(nodesel != NULL);
    347
    348 SCIP_CALL( SCIPsetNodeselCopy(scip, nodesel, nodeselCopyHybridestim) );
    349 SCIP_CALL( SCIPsetNodeselFree(scip, nodesel, nodeselFreeHybridestim) );
    350
    351 /* add node selector parameters */
    353 "nodeselection/hybridestim/minplungedepth",
    354 "minimal plunging depth, before new best node may be selected (-1 for dynamic setting)",
    355 &nodeseldata->minplungedepth, TRUE, MINPLUNGEDEPTH, -1, INT_MAX, NULL, NULL) );
    357 "nodeselection/hybridestim/maxplungedepth",
    358 "maximal plunging depth, before new best node is forced to be selected (-1 for dynamic setting)",
    359 &nodeseldata->maxplungedepth, TRUE, MAXPLUNGEDEPTH, -1, INT_MAX, NULL, NULL) );
    361 "nodeselection/hybridestim/maxplungequot",
    362 "maximal quotient (estimate - lowerbound)/(cutoffbound - lowerbound) where plunging is performed",
    363 &nodeseldata->maxplungequot, TRUE, MAXPLUNGEQUOT, 0.0, SCIP_REAL_MAX, NULL, NULL) );
    365 "nodeselection/hybridestim/bestnodefreq",
    366 "frequency at which the best node instead of the hybrid best estimate / best bound is selected (0: never)",
    367 &nodeseldata->bestnodefreq, FALSE, BESTNODEFREQ, 0, INT_MAX, NULL, NULL) );
    369 "nodeselection/hybridestim/estimweight",
    370 "weight of estimate value in node selection score (0: pure best bound search, 1: pure best estimate search)",
    371 &nodeseldata->estimweight, TRUE, ESTIMWEIGHT, 0.0, 1.0, NULL, NULL) );
    372
    373 return SCIP_OKAY;
    374}
    375
    #define NULL
    Definition: def.h:257
    #define SCIP_REAL_MAX
    Definition: def.h:167
    #define MIN(x, y)
    Definition: def.h:233
    #define SCIP_STRINGEQ(name, reference, retcode)
    Definition: def.h:454
    #define SCIP_Real
    Definition: def.h:165
    #define TRUE
    Definition: def.h:102
    #define FALSE
    Definition: def.h:103
    #define MAX(x, y)
    Definition: def.h:229
    #define SCIP_CALL(x)
    Definition: def.h:364
    #define SCIPdebugMsg
    Definition: scip_message.h:78
    SCIP_RETCODE SCIPincludeNodeselHybridestim(SCIP *scip)
    SCIP_RETCODE SCIPaddIntParam(SCIP *scip, const char *name, const char *desc, int *valueptr, SCIP_Bool isadvanced, int defaultvalue, int minvalue, int maxvalue, SCIP_DECL_PARAMCHGD((*paramchgd)), SCIP_PARAMDATA *paramdata)
    Definition: scip_param.c:83
    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
    #define SCIPfreeBlockMemory(scip, ptr)
    Definition: scip_mem.h:108
    #define SCIPallocBlockMemory(scip, ptr)
    Definition: scip_mem.h:89
    SCIP_NODETYPE SCIPnodeGetType(SCIP_NODE *node)
    Definition: tree.c:8503
    SCIP_Real SCIPnodeGetLowerbound(SCIP_NODE *node)
    Definition: tree.c:8533
    SCIP_Real SCIPnodeGetEstimate(SCIP_NODE *node)
    Definition: tree.c:8553
    int SCIPnodeGetDepth(SCIP_NODE *node)
    Definition: tree.c:8523
    SCIP_RETCODE SCIPincludeNodeselBasic(SCIP *scip, SCIP_NODESEL **nodesel, const char *name, const char *desc, int stdpriority, int memsavepriority, SCIP_DECL_NODESELSELECT((*nodeselselect)), SCIP_DECL_NODESELCOMP((*nodeselcomp)), SCIP_NODESELDATA *nodeseldata)
    Definition: scip_nodesel.c:111
    void SCIPnodeselSetData(SCIP_NODESEL *nodesel, SCIP_NODESELDATA *nodeseldata)
    Definition: nodesel.c:1273
    SCIP_RETCODE SCIPsetNodeselFree(SCIP *scip, SCIP_NODESEL *nodesel, SCIP_DECL_NODESELFREE((*nodeselfree)))
    Definition: scip_nodesel.c:162
    SCIP_NODESELDATA * SCIPnodeselGetData(SCIP_NODESEL *nodesel)
    Definition: nodesel.c:1263
    SCIP_RETCODE SCIPsetNodeselCopy(SCIP *scip, SCIP_NODESEL *nodesel, SCIP_DECL_NODESELCOPY((*nodeselcopy)))
    Definition: scip_nodesel.c:146
    const char * SCIPnodeselGetName(SCIP_NODESEL *nodesel)
    Definition: nodesel.c:1195
    SCIP_Longint SCIPgetNSolsFound(SCIP *scip)
    int SCIPgetMaxDepth(SCIP *scip)
    SCIP_Longint SCIPgetNNodes(SCIP *scip)
    SCIP_Longint SCIPgetNStrongbranchLPIterations(SCIP *scip)
    SCIP_Real SCIPgetLowerbound(SCIP *scip)
    SCIP_Longint SCIPgetNNodeLPIterations(SCIP *scip)
    SCIP_Real SCIPgetCutoffbound(SCIP *scip)
    SCIP_Real SCIPinfinity(SCIP *scip)
    SCIP_Bool SCIPisInfinity(SCIP *scip, SCIP_Real val)
    SCIP_Bool SCIPisGT(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
    SCIP_Bool SCIPisEQ(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
    SCIP_Bool SCIPisLT(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
    SCIP_NODE * SCIPgetBestSibling(SCIP *scip)
    Definition: scip_tree.c:336
    SCIP_NODE * SCIPgetBestChild(SCIP *scip)
    Definition: scip_tree.c:320
    SCIP_NODE * SCIPgetPrioSibling(SCIP *scip)
    Definition: scip_tree.c:304
    SCIP_NODE * SCIPgetBestNode(SCIP *scip)
    Definition: scip_tree.c:368
    int SCIPgetPlungeDepth(SCIP *scip)
    Definition: scip_tree.c:715
    SCIP_NODE * SCIPgetBestboundNode(SCIP *scip)
    Definition: scip_tree.c:384
    SCIP_NODE * SCIPgetPrioChild(SCIP *scip)
    Definition: scip_tree.c:288
    #define BESTNODEFREQ
    static SCIP_Real getNodeselScore(SCIP_NODE *node, SCIP_Real estimweight)
    static SCIP_DECL_NODESELFREE(nodeselFreeHybridestim)
    static SCIP_DECL_NODESELSELECT(nodeselSelectHybridestim)
    #define NODESEL_NAME
    #define MAXPLUNGEQUOT
    #define NODESEL_MEMSAVEPRIORITY
    static SCIP_DECL_NODESELCOPY(nodeselCopyHybridestim)
    #define NODESEL_STDPRIORITY
    #define ESTIMWEIGHT
    #define NODESEL_DESC
    #define MINPLUNGEDEPTH
    static SCIP_DECL_NODESELCOMP(nodeselCompHybridestim)
    #define MAXPLUNGEDEPTH
    node selector for hybrid best estimate / best bound search
    public methods for message output
    public methods for node selectors
    public methods for branch and bound tree
    public methods for memory management
    public methods for message handling
    public methods for node selector plugins
    public methods for numerical tolerances
    public methods for SCIP parameter handling
    public methods for querying solving statistics
    public methods for the branch-and-bound tree
    struct SCIP_NodeselData SCIP_NODESELDATA
    Definition: type_nodesel.h:52
    @ SCIP_OKAY
    Definition: type_retcode.h:42
    @ SCIP_INVALIDCALL
    Definition: type_retcode.h:51
    enum SCIP_Retcode SCIP_RETCODE
    Definition: type_retcode.h:63
    enum SCIP_NodeType SCIP_NODETYPE
    Definition: type_tree.h:53
    @ SCIP_NODETYPE_CHILD
    Definition: type_tree.h:44
    @ SCIP_NODETYPE_SIBLING
    Definition: type_tree.h:43