1 /*===========================================================================*/
2 /*                                                                           */
3 /* This file is part of a demonstration application for use with the         */
4 /* SYMPHONY Branch, Cut, and Price Library. This application is a solver for */
5 /* Capacitated Network Routing Problems.                                     */
6 /*                                                                           */
7 /* (c) Copyright 2000-2007 Ted Ralphs. All Rights Reserved.                  */
8 /*                                                                           */
9 /* This application was developed by Ted Ralphs (ted@lehigh.edu)             */
10 /*                                                                           */
11 /* This software is licensed under the Eclipse Public License. Please see    */
12 /* accompanying file for terms.                                              */
13 /*                                                                           */
14 /*===========================================================================*/
15 
16 /* system include files */
17 
18 /* SYMPHONY include files */
19 #include "sym_macros.h"
20 #include "sym_constants.h"
21 #include "sym_dg.h"
22 
23 /* CNRP include files */
24 #include "cnrp_dg.h"
25 #include "cnrp_const.h"
26 #include "cnrp_macros.h"
27 
28 /*===========================================================================*/
29 
30 /*===========================================================================*\
31  * This file contains additional user functions for the draw graph process.
32 \*===========================================================================*/
33 
34 /*===========================================================================*\
35  * Create the solution graph using adjacency lists
36 \*===========================================================================*/
37 
dg_createnet(int vertnum,int length,int * xind,double * xval)38 dg_net_network *dg_createnet(int vertnum, int length, int *xind, double *xval)
39 {
40    dg_net_edge *nedge;
41    dg_net_network *n;
42    dg_net_vertex *verts, *nv;
43    int v[2], i, j;
44    dg_net_elist *adjlist;
45 
46    n = (dg_net_network *) calloc(1, sizeof(dg_net_network));
47    n->vertnum = vertnum;
48    n->edgenum = length;
49    n->verts = verts =
50       (dg_net_vertex *) calloc(n->vertnum, sizeof(dg_net_vertex));
51    n->adjlist = adjlist =
52       (dg_net_elist *) malloc(2 * n->edgenum*sizeof(dg_net_elist));
53    n->edges = nedge = (dg_net_edge *) calloc(n->edgenum, sizeof(dg_net_edge));
54 
55    /*---- set up orignodenum ------------------------------------------------*/
56    for (i=vertnum-1; i>=0; i--){
57       verts[i].orignodenum = i;
58    }
59 
60    /*---- set up the adjacency list -----------------------------------------*/
61    for (i=0; i<length; i++, xval++, xind++){
62       BOTH_ENDS(*xind, &v[1], &v[0]);
63       nedge->head = verts + v[0];
64       nedge->tail = verts + v[1];
65       nedge->weight = *xval;
66       for (j=1; j>=0; j--){
67 	 nv = verts + v[j];
68 	 if (!nv->first){
69 	    nv->first = nv->last = adjlist;
70 	 }else{
71 	    nv->last->next_edge = adjlist;
72 	    nv->last = adjlist;
73 	 }
74 	 adjlist->next_edge = NULL;
75 	 nv->degree++;
76 	 adjlist->data = nedge;
77 	 adjlist->other = verts + v[1-j];
78 	 adjlist++;
79       }
80       nedge++;
81    }
82    return(n);
83 }
84 
85 /*===========================================================================*/
86 
87 /*===========================================================================*\
88  * Free the network
89 \*===========================================================================*/
90 
dg_freenet(dg_net_network * n)91 void dg_freenet(dg_net_network *n)
92 {
93    if (n){
94       FREE(n->adjlist);
95       FREE(n->edges);
96       FREE(n->verts);
97       FREE(n);
98    }
99 }
100 
101 /*===========================================================================*/
102 
103 /*===========================================================================*\
104  * This function shrinks the chains in the graph
105 \*===========================================================================*/
106 
dg_net_shrink_chains(dg_net_network * n)107 void dg_net_shrink_chains(dg_net_network *n)
108 {
109    int vertnum = n->vertnum;
110    dg_net_vertex *verts = n->verts;
111 
112    dg_net_vertex *nw, *nv1, *nv, *nu;
113    dg_net_elist *ne;
114    dg_net_edge *dat;
115 
116    int i;
117 
118    /*------------------------------------------------------------------------*
119     * Here we contract all chains of 1-edges, called 1-paths. We simply
120     * look at all nodes of degree 2 and keep following the 1-path in each
121     * direction from that node until we reach a node that is not of degree 2.
122     *------------------------------------------------------------------------*/
123 
124    for (i=0; i<vertnum-1; i++){
125       nv = verts+i;
126       /*-----------check whether we have a degree 2 node-----------*/
127       if (nv->scanned != NOT_SCANNED || nv->degree != 2)
128 	 continue;
129 
130       nv->scanned = SCANNED_SHRUNK;
131 
132       /*---------------------------------------------------------------------*
133        * follow the 1-path from i until we hit a node that is not of degree 2
134        * If during this process we come back to nv (i) that means we have a
135        * subtour we can contract into one node.
136        * Also, as we go along we contract all the nodes on the 1-path into i.
137        *---------------------------------------------------------------------*/
138       for (ne = nv->first, nw = ne->other; nw->degree == 2; nw = ne->other){
139 	 ne->data->deleted = TRUE;
140 	 if (nv == nw)
141 	    break;
142 	 nw->scanned = SCANNED_SHRUNK;
143 	 nv->snode_size++;
144 	 nw->snode_next = nv->snode_next;
145 	 nv->snode_next = nw;
146 	 ne = (nw->first->data->deleted ? nw->last : nw->first);
147       }
148 
149       if (nv == nw){
150 	 /* in this case we had a subtour and so we don't need to go on*/
151 	 nv->first = nv->last = NULL;
152 	 nv->scanned = SCANNED_ALIVE;
153 	 continue;
154       }
155 
156       /* Now nw is not a degree 2 node, but nv->snode_next is the node
157        * in the chain adjacent to nw and so on. The number of nodes
158        * hanging off nv is exactly nv->snode_size (not including nv) */
159       if (nv->snode_size){
160 	 nv1 = nv->snode_next;
161 	 if (nv1->first->other != nw){
162 	    ne = nv1->first;
163 	    nv1->first = nv1->last;
164 	    nv1->first->next_edge = ne;
165 	    nv1->last = ne;
166 	    ne->next_edge = NULL;
167 	 }
168 	 /* Follow the chain back and hang nv to the end */
169 	 nv1->snode_size = nv->snode_size;
170 	 for (nu = nv1; nu->snode_next; nu = nu->snode_next);
171 	 nu->snode_next = nv;
172 	 nv->snode_next = NULL;
173 	 nv->snode_size = 0;
174       }else{
175 	 nv1 = nv;
176       }
177 
178       /* Now nw is next to nv1 and nv1->first->other = nw */
179 
180       /* Continue to the other end. */
181       for (ne=nv->last, nu=ne->other; nu->degree == 2; nv=nu, nu=ne->other){
182 	 ne->data->deleted = TRUE;
183 	 nu->scanned = SCANNED_SHRUNK;
184 	 nv1->snode_size++;
185 	 nv->snode_next = nu;
186 	 ne = (nu->first->data->deleted ? nu->last : nu->first);
187       }
188 
189       /* Now nv is the last node in the chain, nu the first not degree
190        * 2 node, (ne goes from nv to nu) the chain hangs off of nv1 in
191        * order, nv1->size is the number of degree 2 nodes in the chain
192        * -1 (nv1 itself).
193        *
194        * Now we update the adjacency lists appropriately. */
195       if (nv1->snode_size){
196 	 nv1->first->next_edge = nv1->last = ne;
197 	 ne->next_edge = NULL;
198 	 dat = ne->data;
199 	 if (dat->head == nu)
200 	    dat->tail = nv1;
201 	 else
202 	    dat->head = nv1;
203 	 for (ne=nu->first; ne->data != dat; ne=ne->next_edge);
204 	 ne->other = nv1;
205       }
206       nv1->scanned = SCANNED_ALIVE;
207       /* Now we are completely done. The whole chain is shrinked
208        * into nv1, nv1 is connected to appropriately to nw and nu. */
209    }
210 
211    /* Note that the 'scanned' field is SCANNED_SHRUNK for nodes shrinked into
212     * another node; SCANNED_ALIVE for degree 2 nodes still alive and
213     * NOT_SCANNED for nodes of degree >= 3 */
214 }
215 
216 /*===========================================================================*/
217 
218 /*===========================================================================*\
219  * This function copies a network into a graph which already contains the
220  * nodes of the graph. Therefore we have to get rid of the shrunk nodes
221  * in the process.
222 \*===========================================================================*/
223 
copy_network_into_graph(dg_net_network * n,dg_graph * g)224 void copy_network_into_graph(dg_net_network *n, dg_graph *g)
225 {
226    int i, k, vertnum = n->vertnum, edgenum = n->edgenum;
227    dg_net_vertex *verts = n->verts;
228    dg_net_edge *ne, *nedges = n->edges;
229    dg_node *nod, *gnod, *nodes, *gnodes = g->nodes;
230    dg_edge *ge, *gedges = g->edges;
231 
232    for (k = i = 0; i < vertnum; i++)
233       if (verts[i].scanned != SCANNED_SHRUNK)
234 	 k++;
235 
236    nodes = (dg_node *) malloc(k * sizeof(dg_node));
237    for (k = i = 0; i < vertnum; i++){
238       if (verts[i].scanned != SCANNED_SHRUNK){
239 	 nod = nodes + k++;
240 	 gnod = gnodes + i;
241 	 *nod = *gnod;
242 	 if (verts[i].snode_size){ /* if anything is shrunk into this node */
243 	    sprintf(nod->weight, "%i", verts[i].snode_size);
244 	 }else{
245 	    *nod->weight = 0;
246 	 }
247       }
248    }
249    FREE(gnodes);
250    g->nodenum = k;
251    g->nodes = nodes;
252 
253    for (k = i = 0; i < edgenum; i++)
254       if (! nedges[i].deleted)
255 	 k++;
256 
257    gedges = g->edges = (dg_edge *) malloc(k * sizeof(dg_edge));
258    for (k = i = 0; i < edgenum; i++){
259       if (!(ne=nedges+i)->deleted){
260 	 ge = gedges + k++;
261 	 ge->edge_id = INDEX( (ge->tail = ne->tail->orignodenum),
262 			      (ge->head = ne->head->orignodenum) );
263 	 ge->deleted = FALSE;
264 	 if (ne->weight > .99999){
265 	    strcpy(ge->weight, "1");
266 	    ge->dash[0] = 0;
267 	 }else{
268 	    sprintf(ge->weight, "%.3f", ne->weight);
269 	    strcpy(ge->dash, "4 3");
270 	 }
271       }
272    }
273    g->edgenum = k;
274 }
275