SCIP Doxygen Documentation
Loading...
Searching...
No Matches
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
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)
void SCIPnodeselSetData(SCIP_NODESEL *nodesel, SCIP_NODESELDATA *nodeseldata)
Definition nodesel.c:1273
SCIP_RETCODE SCIPsetNodeselFree(SCIP *scip, SCIP_NODESEL *nodesel,)
SCIP_NODESELDATA * SCIPnodeselGetData(SCIP_NODESEL *nodesel)
Definition nodesel.c:1263
SCIP_RETCODE SCIPsetNodeselCopy(SCIP *scip, SCIP_NODESEL *nodesel,)
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
return SCIP_OKAY
assert(minobj< SCIPgetCutoffbound(scip))
#define NODESEL_NAME
Definition nodesel_bfs.c:47
#define MAXPLUNGEQUOT
Definition nodesel_bfs.c:59
#define NODESEL_MEMSAVEPRIORITY
Definition nodesel_bfs.c:50
#define NODESEL_STDPRIORITY
Definition nodesel_bfs.c:49
#define NODESEL_DESC
Definition nodesel_bfs.c:48
#define MINPLUNGEDEPTH
Definition nodesel_bfs.c:57
#define MAXPLUNGEDEPTH
Definition nodesel_bfs.c:58
#define BESTNODEFREQ
static SCIP_Real getNodeselScore(SCIP_NODE *node, SCIP_Real estimweight)
#define ESTIMWEIGHT
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
#define SCIP_DECL_NODESELCOMP(x)
struct SCIP_Nodesel SCIP_NODESEL
#define SCIP_DECL_NODESELCOPY(x)
#define SCIP_DECL_NODESELSELECT(x)
#define SCIP_DECL_NODESELFREE(x)
struct SCIP_NodeselData SCIP_NODESELDATA
@ SCIP_INVALIDCALL
enum SCIP_Retcode SCIP_RETCODE
struct Scip SCIP
Definition type_scip.h:39
struct SCIP_Node SCIP_NODE
Definition type_tree.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