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-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 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:248
    #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:355
    #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:516
    SCIP_RETCODE SCIPfreeSol(SCIP *scip, SCIP_SOL **sol)
    Definition: scip_sol.c:1252
    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:4012
    SCIP_RETCODE SCIPsetSolVal(SCIP *scip, SCIP_SOL *sol, SCIP_VAR *var, SCIP_Real val)
    Definition: scip_sol.c:1571
    SCIP_Real SCIPvarGetUbGlobal(SCIP_VAR *var)
    Definition: var.c:24142
    SCIP_Real SCIPvarGetLbGlobal(SCIP_VAR *var)
    Definition: var.c:24120
    #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