1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the program and library */
4 /* SCIP --- Solving Constraint Integer Programs */
5 /* */
6 /* Copyright (C) 2002-2021 Konrad-Zuse-Zentrum */
7 /* fuer Informationstechnik Berlin */
8 /* */
9 /* SCIP is distributed under the terms of the ZIB Academic License. */
10 /* */
11 /* You should have received a copy of the ZIB Academic License. */
12 /* along with SCIP; see the file COPYING. If not visit scipopt.org. */
13 /* */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15
16 /**@file Heur2opt.cpp
17 * @brief 2-Optimum - combinatorial improvement heuristic for TSP
18 * @author Timo Berthold
19 */
20
21 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
22
23 #include <cassert>
24 #include <iostream>
25
26 #include "objscip/objscip.h"
27 #include "GomoryHuTree.h"
28 #include "Heur2opt.h"
29 #include "ProbDataTSP.h"
30
31 using namespace tsp;
32 using namespace std;
33
34
35 /** method finding the edge going from the node with id index1 to the node with id index2 */
36 static
findEdge(GRAPHNODE * nodes,GRAPHNODE * node1,GRAPHNODE * node2)37 GRAPHEDGE* findEdge(
38 GRAPHNODE* nodes, /**< all nodes of the graph */
39 GRAPHNODE* node1, /**< id of the node where the searched edge starts */
40 GRAPHNODE* node2 /**< id of the node where the searched edge ends */
41 )
42 { /*lint --e{715}*/
43 GRAPHEDGE* edge = node1->first_edge;
44
45 // regard every outgoing edge of node index1 and stop if adjacent to node index2
46 while( edge != NULL )
47 {
48 if( edge->adjac == node2 )
49 break;
50 edge = edge->next;
51 }
52 return edge;
53 }
54
55
56 /** destructor of primal heuristic to free user data (called when SCIP is exiting) */
SCIP_DECL_HEURFREE(Heur2opt::scip_free)57 SCIP_DECL_HEURFREE(Heur2opt::scip_free)
58 { /*lint --e{715}*/
59 return SCIP_OKAY;
60 }
61
62
63 /** initialization method of primal heuristic (called after problem was transformed) */
SCIP_DECL_HEURINIT(Heur2opt::scip_init)64 SCIP_DECL_HEURINIT(Heur2opt::scip_init)
65 { /*lint --e{715}*/
66 return SCIP_OKAY;
67 }
68
69
70 /** deinitialization method of primal heuristic (called before transformed problem is freed) */
SCIP_DECL_HEUREXIT(Heur2opt::scip_exit)71 SCIP_DECL_HEUREXIT(Heur2opt::scip_exit)
72 { /*lint --e{715}*/
73 return SCIP_OKAY;
74 }
75
76
77 /** solving process initialization method of primal heuristic (called when branch and bound process is about to begin)
78 *
79 * This method is called when the presolving was finished and the branch and bound process is about to begin.
80 * The primal heuristic may use this call to initialize its branch and bound specific data.
81 *
82 */
SCIP_DECL_HEURINITSOL(Heur2opt::scip_initsol)83 SCIP_DECL_HEURINITSOL(Heur2opt::scip_initsol)
84 { /*lint --e{715}*/
85 ProbDataTSP* probdata = dynamic_cast<ProbDataTSP*>(SCIPgetObjProbData(scip));
86 graph_ = probdata->getGraph(); /*lint !e613*/
87 capture_graph(graph_);
88
89 ncalls_ = 0;
90 sol_ = NULL;
91 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &tour_, graph_->nnodes) );
92
93 return SCIP_OKAY;
94 }
95
96
97 /** solving process deinitialization method of primal heuristic (called before branch and bound process data is freed)
98 *
99 * This method is called before the branch and bound process is freed.
100 * The primal heuristic should use this call to clean up its branch and bound data.
101 */
SCIP_DECL_HEUREXITSOL(Heur2opt::scip_exitsol)102 SCIP_DECL_HEUREXITSOL(Heur2opt::scip_exitsol)
103 { /*lint --e{715}*/
104 assert( graph_ != 0 );
105 assert( tour_ != 0 );
106
107 SCIPfreeBlockMemoryArray(scip, &tour_, graph_->nnodes);
108 release_graph(&graph_);
109
110 return SCIP_OKAY;
111 }
112
113
114 /** execution method of primal heuristic 2-Opt */
SCIP_DECL_HEUREXEC(Heur2opt::scip_exec)115 SCIP_DECL_HEUREXEC(Heur2opt::scip_exec)
116 { /*lint --e{715}*/
117 assert( scip != NULL );
118 assert( result != NULL );
119 assert( heur != NULL );
120
121 SCIP_SOL* sol = SCIPgetBestSol(scip);
122 bool newsol;
123
124 // check whether a new solution was found meanwhile
125 if( sol != sol_ )
126 {
127 sol_ = sol;
128 ncalls_ = 0;
129 newsol = true;
130 }
131 else
132 newsol = false;
133
134 ++ncalls_;
135
136 int nnodes = graph_->nnodes; /*lint !e613*/
137
138 // some cases need not to be handled
139 if( nnodes < 4 || sol == NULL || ncalls_ >= nnodes )
140 {
141 *result = SCIP_DIDNOTRUN;
142 return SCIP_OKAY;
143 }
144
145 *result= SCIP_DIDNOTFIND;
146
147 GRAPHNODE* nodes = graph_->nodes; /*lint !e613*/
148
149 // get tour from sol and sort edges by length, if new solution was found
150 if( newsol )
151 {
152 GRAPHEDGE* edge;
153 GRAPHEDGE* lastedge = NULL;
154 GRAPHNODE* node = &nodes[0];
155 int i = 0;
156
157 do
158 {
159 edge = node->first_edge;
160 while( edge != NULL )
161 {
162 // find the next edge of the tour
163 if( edge->back != lastedge && SCIPgetSolVal(scip, sol, edge->var) > 0.5 )
164 {
165 node = edge->adjac;
166 lastedge = edge;
167
168 int j;
169 // shift edge through the (sorted) array
170 for(j = i; j > 0 && tour_[j-1]->length < edge->length; j-- ) /*lint !e613*/
171 tour_[j] = tour_[j-1]; /*lint !e613*/
172
173 // and insert the edge at the right position
174 tour_[j] = edge; /*lint !e613*/
175
176 i++;
177 break;
178 }
179 edge = edge->next;
180 }
181 }
182 while ( node != &nodes[0] );
183 assert( i == nnodes );
184 }
185
186 GRAPHEDGE** edges2test = NULL;
187 SCIP_CALL( SCIPallocBufferArray(scip, &edges2test, 4) );
188
189 /* test current edge with all 'longer' edges for improvement if swapping with crossing edges (though do 2Opt for one edge) */
190 for( int i = 0; i < ncalls_ && *result != SCIP_FOUNDSOL; i++ )
191 {
192 edges2test[0] = tour_[ncalls_]; /*lint !e613*/
193 edges2test[1] = tour_[i]; /*lint !e613*/
194 edges2test[2] = findEdge( nodes, edges2test[0]->back->adjac, edges2test[1]->back->adjac );
195 edges2test[3] = findEdge( nodes, edges2test[0]->adjac, edges2test[1]->adjac );
196 assert( edges2test[2] != NULL );
197 assert( edges2test[3] != NULL );
198
199 // if the new solution is better and variables are not fixed, update and end
200 if( edges2test[0]->length + edges2test[1]->length > edges2test[2]->length + edges2test[3]->length
201 && SCIPvarGetLbGlobal(edges2test[0]->var) < 0.5
202 && SCIPvarGetLbGlobal(edges2test[1]->var) < 0.5
203 && SCIPvarGetUbGlobal(edges2test[2]->var) > 0.5
204 && SCIPvarGetUbGlobal(edges2test[3]->var) > 0.5 )
205 {
206 SCIP_Bool success;
207 SCIP_SOL* swapsol; // copy of sol with 4 edges swapped
208
209 SCIP_CALL( SCIPcreateSol(scip, &swapsol, heur) );
210
211 // copy the old solution
212 for( int j = 0; j < nnodes; j++)
213 {
214 SCIP_CALL( SCIPsetSolVal(scip, swapsol, tour_[j]->var, 1.0) ); /*lint !e613*/
215 }
216
217 // and replace two edges
218 SCIP_CALL( SCIPsetSolVal(scip, swapsol, edges2test[0]->var, 0.0) );
219 SCIP_CALL( SCIPsetSolVal(scip, swapsol, edges2test[1]->var, 0.0) );
220 SCIP_CALL( SCIPsetSolVal(scip, swapsol, edges2test[2]->var, 1.0) );
221 SCIP_CALL( SCIPsetSolVal(scip, swapsol, edges2test[3]->var, 1.0) );
222 SCIP_CALL( SCIPaddSolFree(scip, &swapsol, &success) );
223
224 assert(success);
225 *result = SCIP_FOUNDSOL;
226 ncalls_ = 0;
227 }
228 }
229 SCIPfreeBufferArray(scip, &edges2test);
230
231 return SCIP_OKAY;
232 }
233
234
235 /** clone method which will be used to copy a objective plugin */
SCIP_DECL_HEURCLONE(scip::ObjCloneable * Heur2opt::clone)236 SCIP_DECL_HEURCLONE(scip::ObjCloneable* Heur2opt::clone) /*lint !e665*/
237 {
238 return new Heur2opt(scip);
239 }
240