Scippy

SCIP

Solving Constraint Integer Programs

HeurFarthestInsert.cpp
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-2024 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 HeurFarthestInsert.cpp
26 * @brief farthest insert - combinatorial heuristic for TSP
27 * @author Timo Berthold
28 */
29
30/*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
31
32
33#include <iostream>
34#include <cassert>
35
36#include "objscip/objscip.h"
37#include "GomoryHuTree.h"
38#include "HeurFarthestInsert.h"
39#include "ProbDataTSP.h"
40
41using namespace tsp;
42using namespace std;
43
44/** method finding the edge going from the node with id index1 to the node with id index2 */
45static
47 GRAPHNODE* nodes, /**< all nodes of the graph */
48 int index1, /**< id of the node where the searched edge starts */
49 int index2 /**< id of the node where the searched edge ends */
50 )
51{
52 GRAPHEDGE* edge = nodes[index1].first_edge;
53
54 // regard every outgoing edge of node index1 and stop if adjacent to node index2
55 while( edge != NULL )
56 {
57 if( edge->adjac->id == index2 )
58 break;
59 edge = edge->next;
60 }
61
62 return edge;
63}
64
65
66/** method updating the distances of the nodes to the tour after having inserted one node with id index */
67static
69 GRAPHNODE* nodes, /**< all nodes of the graph */
70 double* dist, /**< array with current distances of all nodes to the subtour */
71 int idx /**< id of the inserted node */
72 )
73{
74 GRAPHEDGE* edge = nodes[idx].first_edge;
75
76 /* Regard all outgoing edges of the node and update, if the length and therefore the distance of the adjacent is
77 * smaller and the edge is not fixed to 0. */
78 while( edge != NULL )
79 {
80 if( dist[edge->adjac->id] > edge->length && SCIPvarGetUbGlobal(edge->var) != 0.0 )
81 dist[edge->adjac->id] = edge->length;
82 edge = edge->next;
83 }
84}
85
86
87/** destructor of primal heuristic to free user data (called when SCIP is exiting) */
88SCIP_DECL_HEURFREE(HeurFarthestInsert::scip_free)
89{ /*lint --e{715}*/
90 return SCIP_OKAY;
91}
92
93/** initialization method of primal heuristic (called after problem was transformed) */
94SCIP_DECL_HEURINIT(HeurFarthestInsert::scip_init)
95{ /*lint --e{715}*/
96 ProbDataTSP* probdata = dynamic_cast<ProbDataTSP*>(SCIPgetObjProbData(scip));
97 graph_ = probdata->getGraph(); /*lint !e613*/
98 capture_graph(graph_);
99 return SCIP_OKAY;
100}
101
102/** deinitialization method of primal heuristic (called before transformed problem is freed) */
103SCIP_DECL_HEUREXIT(HeurFarthestInsert::scip_exit)
104{ /*lint --e{715}*/
105 release_graph(&graph_);
106
107 return SCIP_OKAY;
108}
109
110/** solving process initialization method of primal heuristic (called when branch and bound process is about to begin)
111 *
112 * This method is called when the presolving was finished and the branch and bound process is about to begin.
113 * The primal heuristic may use this call to initialize its branch and bound specific data.
114 *
115 */
116SCIP_DECL_HEURINITSOL(HeurFarthestInsert::scip_initsol)
117{ /*lint --e{715}*/
118 return SCIP_OKAY;
119}
120
121/** solving process deinitialization method of primal heuristic (called before branch and bound process data is freed)
122 *
123 * This method is called before the branch and bound process is freed.
124 * The primal heuristic should use this call to clean up its branch and bound data.
125 */
126SCIP_DECL_HEUREXITSOL(HeurFarthestInsert::scip_exitsol)
127{ /*lint --e{715}*/
128 return SCIP_OKAY;
129}
130
131/** execution method of primal heuristic
132 *
133 * Searches for feasible primal solutions. The method is called in the node processing loop.
134 *
135 * possible return values for *result:
136 * - SCIP_FOUNDSOL : at least one feasible primal solution was found
137 * - SCIP_DIDNOTFIND : the heuristic searched, but did not find a feasible solution
138 * - SCIP_DIDNOTRUN : the heuristic was skipped
139 * - SCIP_DELAYED : the heuristic was skipped, but should be called again as soon as possible, disregarding
140 * its frequency
141 */
142SCIP_DECL_HEUREXEC(HeurFarthestInsert::scip_exec)
143{ /*lint --e{715}*/
144 int nnodes = graph_->nnodes; /*lint !e613*/
145 int nedges = graph_->nedges; /*lint !e613*/
146
147 SCIP_Bool hasFixedEdges = FALSE;
148 for(int e = 0; e < nedges; ++e)
149 {
150 GRAPHEDGE* edge = &(graph_->edges[e]); /*lint !e613*/
151 if( SCIPvarGetLbGlobal(edge->var) == 1.0 )
152 {
153 hasFixedEdges = TRUE;
154 break;
155 }
156 }
157
158 // no longer need "SCIPgetNRuns(scip) > 1" since we now respect fixed variables after restart
159 if( nnodes < 3 || hasFixedEdges )
160 *result = SCIP_DIDNOTRUN;
161 else
162 {
163 bool* subtour;
164 int i;
165 double* dist;
166
167 GRAPHNODE* startnode;
168 GRAPHNODE* node;
169 GRAPHEDGE* edge;
170
171 GRAPHEDGE** bestedges; // will contain the best insertion of a given node into a subtour
172 GRAPHEDGE** edges; // will contain some insertion of a given node into a subtour
173 GRAPHEDGE** successor; // stores the successor of a node in the current subtour
174 GRAPHNODE* nodes = graph_->nodes; /*lint !e613*/
175
176 for( i = 0; i < nnodes; i++ )
177 assert( i == nodes[i].id );
178
179 // memory allocation
180 SCIP_CALL( SCIPallocBufferArray(scip, &subtour, nnodes) ); /*lint !e530*/
181 SCIP_CALL( SCIPallocBufferArray(scip, &dist, nnodes) ); /*lint !e530*/
182 SCIP_CALL( SCIPallocBufferArray(scip, &successor, nnodes) ); /*lint !e530*/
183 SCIP_CALL( SCIPallocBufferArray(scip, &edges, 3) ); /*lint !e530*/
184 SCIP_CALL( SCIPallocBufferArray(scip, &bestedges, 3) ); /*lint !e530*/
185
186 BMSclearMemoryArray(subtour, nnodes);
187 for( i = 0; i < nnodes; i++ )
188 dist[i] = DBL_MAX;
189
190 // building up a 3-circle, only using edges not fixed to 0
191 SCIP_Bool foundThreeCircle = FALSE;
192 for(int u = 0; u < nnodes - 2 && !foundThreeCircle; ++u)
193 {
194 for(int v = u + 1; v < nnodes - 1 && !foundThreeCircle; ++v)
195 {
196 GRAPHEDGE * uv = findEdge(nodes, u, v);
197 assert(uv != NULL);
198 if( SCIPvarGetUbGlobal(uv->var) == 0.0 )
199 continue;
200
201 for(int w = v + 1; (w < nnodes) && !foundThreeCircle; ++w) /*lint !e845*/
202 {
203 GRAPHEDGE * vw = findEdge(nodes, v, w);
204 assert(vw != NULL);
205 GRAPHEDGE * wu = findEdge(nodes, w, u);
206 assert(wu != NULL);
207
208 if( SCIPvarGetUbGlobal(vw->var) == 0.0 || SCIPvarGetUbGlobal(wu->var) == 0.0 )
209 continue;
210 else
211 {
212 foundThreeCircle = TRUE;
213
214 subtour[u] = true;
215 dist[u] = 0.0;
216 updateDistances(nodes, dist, u);
217 subtour[v] = true;
218 dist[v] = 0.0;
219 updateDistances(nodes, dist, v);
220 subtour[w] = true;
221 dist[w] = 0.0;
222 updateDistances(nodes, dist, w);
223 successor[u] = uv;
224 successor[v] = vw;
225 successor[w] = wu;
226 } // foundThreeCircle with no fixed variables
227 } // for w
228 } // for v
229 } // for u
230
231 if( !foundThreeCircle )
232 {
233 *result = SCIP_DIDNOTFIND;
234 }
235 else
236 {
237 double maxmin;
238 double minval;
239 int newnodeindex;
240
241 SCIP_Bool couldNotInsert = FALSE;
242
243 // widen the subtour by one node each step until you have a complete tour, actually the farthest insert heuritic
244 int subtourlength = 3;
245 for(; subtourlength < nnodes; subtourlength++ )
246 {
247 // find the node with the maximal distance to the tour
248 maxmin = 0.0;
249 newnodeindex = -1;
250 for( i = 0; i < nnodes; i++)
251 {
252 if( (maxmin < dist[i] && dist[i] != DBL_MAX) || (maxmin == dist[i] && !subtour[i]) ) /*lint !e777*/
253 {
254 maxmin = dist[i];
255 newnodeindex = i;
256 }
257 }
258 if(newnodeindex == -1)
259 {
260 couldNotInsert = TRUE;
261 break;
262 }
263
264 // find connection to one node in the tour
265 BMSclearMemoryArray(bestedges, 3);
266 edge = nodes[newnodeindex].first_edge;
267 startnode = NULL;
268
269 while( edge != NULL )
270 {
271 if( subtour[edge->adjac->id] && SCIPvarGetUbGlobal(edge->var) != 0.0 )
272 break;
273 edge = edge->next;
274 }
275
276 assert(edge != NULL);
277 assert(subtour[edge->adjac->id]);
278
279 /* find best insertion of the new node by trying to replace any edge connecting by the two edges connecting
280 * its end node with the new node */
281 minval = DBL_MAX;
282 edges[0] = edge;
283 startnode = edge->adjac;
284 node = startnode;
285
286 // succeed to the next edge in the subtour
287 do
288 {
289 edges[1] = successor[node->id];
290 edges[2] = findEdge(nodes, edges[1]->adjac->id, newnodeindex);
291 assert( edges[2] != NULL );
292
293 // check, whether you have found a better (feasible) insertion
294 if( edges[0]->back->length - edges[1]->length + edges[2]->back->length < minval
295 && SCIPvarGetUbGlobal(edges[0]->var) != 0.0
296 && SCIPvarGetUbGlobal(edges[2]->var) != 0.0 )
297 {
298 minval = edges[0]->back->length - edges[1]->length + edges[2]->back->length;
299 for( i = 0; i < 3; i++ )
300 bestedges[i] = edges[i];
301 }
302 node = edges[1]->adjac;
303 edges[0] = edges[2]->back;
304 }
305 while( node != startnode);
306
307 if( minval == DBL_MAX ) /*lint !e777*/
308 {
309 couldNotInsert = TRUE;
310 break;
311 }
312 else
313 {
314 // bestedges should contain a 3-cycle (modulo orientation) connecting new node with two incident ones of the tour
315 assert(bestedges[0]->adjac->id == bestedges[1]->back->adjac->id);
316 assert(bestedges[1]->adjac->id == bestedges[2]->back->adjac->id);
317 assert(bestedges[2]->adjac->id == bestedges[0]->back->adjac->id);
318 assert(subtour[bestedges[0]->adjac->id]);
319 assert(subtour[bestedges[1]->adjac->id]);
320 assert(bestedges[2]->adjac->id == newnodeindex);
321 assert(!subtour[newnodeindex]);
322
323 // now officially insert the new node into the tour
324 successor[bestedges[0]->adjac->id] = bestedges[0]->back;
325 successor[bestedges[2]->adjac->id] = bestedges[2]->back;
326 dist[newnodeindex] = 0.0;
327 subtour[newnodeindex] = true;
328 updateDistances(nodes, dist, newnodeindex);
329 } // minval < DBL_MAX
330 } // for subtourlength
331
332 if(couldNotInsert)
333 {
334 *result = SCIP_DIDNOTFIND;
335 }
336 else
337 {
338 SCIP_SOL* sol;
339 SCIP_Bool success;
340
341 // now create a solution out of the edges stored in successor and try to add it to SCIP
342 SCIP_CALL( SCIPcreateSol (scip, &sol, heur) );
343 for( i = 0; i < nnodes; i++ )
344 {
345 SCIP_CALL( SCIPsetSolVal(scip, sol, successor[i]->var, 1.0) );
346 }
347
348 SCIP_CALL( SCIPtrySol(scip, sol, FALSE, FALSE, FALSE, FALSE, FALSE, &success) );
349
350 if( success )
351 *result = SCIP_FOUNDSOL;
352 else
353 *result = SCIP_DIDNOTFIND;
354
355 SCIP_CALL( SCIPfreeSol(scip, &sol) );
356 } // couldNotInsert == FALSE
357 } // foundThreeCircle == TRUE
358
359 // free all local memory
360 SCIPfreeBufferArray(scip, &bestedges);
361 SCIPfreeBufferArray(scip, &edges);
362 SCIPfreeBufferArray(scip, &successor);
364 SCIPfreeBufferArray(scip, &subtour);
365 }
366
367 return SCIP_OKAY;
368}
369
370/** clone method which will be used to copy a objective plugin */
371SCIP_DECL_HEURCLONE(scip::ObjCloneable* HeurFarthestInsert::clone) /*lint !e665*/
372{
373 return new HeurFarthestInsert(scip);
374}
void capture_graph(GRAPH *gr)
void release_graph(GRAPH **gr)
generator for global cuts in undirected graphs
static GRAPHEDGE * findEdge(GRAPHNODE *nodes, int index1, int index2)
SCIP_DECL_HEURCLONE(scip::ObjCloneable *HeurFarthestInsert::clone)
SCIP_DECL_HEURFREE(HeurFarthestInsert::scip_free)
static void updateDistances(GRAPHNODE *nodes, double *dist, int idx)
SCIP_DECL_HEUREXIT(HeurFarthestInsert::scip_exit)
SCIP_DECL_HEURINITSOL(HeurFarthestInsert::scip_initsol)
SCIP_DECL_HEUREXITSOL(HeurFarthestInsert::scip_exitsol)
SCIP_DECL_HEURINIT(HeurFarthestInsert::scip_init)
SCIP_DECL_HEUREXEC(HeurFarthestInsert::scip_exec)
farthest insert - combinatorial heuristic for TSP
C++ problem data for TSP.
SCIP_VAR * w
Definition: circlepacking.c:67
GRAPH * getGraph()
Definition: ProbDataTSP.h:97
#define NULL
Definition: def.h:267
#define SCIP_Bool
Definition: def.h:91
#define TRUE
Definition: def.h:93
#define FALSE
Definition: def.h:94
#define SCIP_CALL(x)
Definition: def.h:374
#define nnodes
Definition: gastrans.c:74
#define SCIPallocBufferArray(scip, ptr, num)
Definition: scip_mem.h:124
#define SCIPfreeBufferArray(scip, ptr)
Definition: scip_mem.h:136
SCIP_RETCODE SCIPcreateSol(SCIP *scip, SCIP_SOL **sol, SCIP_HEUR *heur)
Definition: scip_sol.c:184
SCIP_RETCODE SCIPfreeSol(SCIP *scip, SCIP_SOL **sol)
Definition: scip_sol.c:841
SCIP_RETCODE SCIPtrySol(SCIP *scip, SCIP_SOL *sol, SCIP_Bool printreason, SCIP_Bool completely, SCIP_Bool checkbounds, SCIP_Bool checkintegrality, SCIP_Bool checklprows, SCIP_Bool *stored)
Definition: scip_sol.c:2954
SCIP_RETCODE SCIPsetSolVal(SCIP *scip, SCIP_SOL *sol, SCIP_VAR *var, SCIP_Real val)
Definition: scip_sol.c:1077
SCIP_Real SCIPvarGetUbGlobal(SCIP_VAR *var)
Definition: var.c:18088
SCIP_Real SCIPvarGetLbGlobal(SCIP_VAR *var)
Definition: var.c:18078
#define BMSclearMemoryArray(ptr, num)
Definition: memory.h:130
Definition: pqueue.h:38
scip::ObjProbData * SCIPgetObjProbData(SCIP *scip)
C++ wrapper classes for SCIP.
struct GraphEdge * back
Definition: GomoryHuTree.h:70
GRAPHNODE * adjac
Definition: GomoryHuTree.h:72
SCIP_VAR * var
Definition: GomoryHuTree.h:74
double length
Definition: GomoryHuTree.h:67
struct GraphEdge * next
Definition: GomoryHuTree.h:69
struct GraphEdge * first_edge
Definition: GomoryHuTree.h:53
Definition of base class for all clonable classes.
Definition: objcloneable.h:48
@ SCIP_DIDNOTRUN
Definition: type_result.h:42
@ SCIP_DIDNOTFIND
Definition: type_result.h:44
@ SCIP_FOUNDSOL
Definition: type_result.h:56
@ SCIP_OKAY
Definition: type_retcode.h:42