alpar@1
|
1 |
/* glpapi17.c (flow network problems) */
|
alpar@1
|
2 |
|
alpar@1
|
3 |
/***********************************************************************
|
alpar@1
|
4 |
* This code is part of GLPK (GNU Linear Programming Kit).
|
alpar@1
|
5 |
*
|
alpar@1
|
6 |
* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
|
alpar@1
|
7 |
* 2009, 2010 Andrew Makhorin, Department for Applied Informatics,
|
alpar@1
|
8 |
* Moscow Aviation Institute, Moscow, Russia. All rights reserved.
|
alpar@1
|
9 |
* E-mail: <mao@gnu.org>.
|
alpar@1
|
10 |
*
|
alpar@1
|
11 |
* GLPK is free software: you can redistribute it and/or modify it
|
alpar@1
|
12 |
* under the terms of the GNU General Public License as published by
|
alpar@1
|
13 |
* the Free Software Foundation, either version 3 of the License, or
|
alpar@1
|
14 |
* (at your option) any later version.
|
alpar@1
|
15 |
*
|
alpar@1
|
16 |
* GLPK is distributed in the hope that it will be useful, but WITHOUT
|
alpar@1
|
17 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
alpar@1
|
18 |
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
alpar@1
|
19 |
* License for more details.
|
alpar@1
|
20 |
*
|
alpar@1
|
21 |
* You should have received a copy of the GNU General Public License
|
alpar@1
|
22 |
* along with GLPK. If not, see <http://www.gnu.org/licenses/>.
|
alpar@1
|
23 |
***********************************************************************/
|
alpar@1
|
24 |
|
alpar@1
|
25 |
#include "glpapi.h"
|
alpar@1
|
26 |
#include "glpnet.h"
|
alpar@1
|
27 |
|
alpar@1
|
28 |
/***********************************************************************
|
alpar@1
|
29 |
* NAME
|
alpar@1
|
30 |
*
|
alpar@1
|
31 |
* glp_mincost_lp - convert minimum cost flow problem to LP
|
alpar@1
|
32 |
*
|
alpar@1
|
33 |
* SYNOPSIS
|
alpar@1
|
34 |
*
|
alpar@1
|
35 |
* void glp_mincost_lp(glp_prob *lp, glp_graph *G, int names,
|
alpar@1
|
36 |
* int v_rhs, int a_low, int a_cap, int a_cost);
|
alpar@1
|
37 |
*
|
alpar@1
|
38 |
* DESCRIPTION
|
alpar@1
|
39 |
*
|
alpar@1
|
40 |
* The routine glp_mincost_lp builds an LP problem, which corresponds
|
alpar@1
|
41 |
* to the minimum cost flow problem on the specified network G. */
|
alpar@1
|
42 |
|
alpar@1
|
43 |
void glp_mincost_lp(glp_prob *lp, glp_graph *G, int names, int v_rhs,
|
alpar@1
|
44 |
int a_low, int a_cap, int a_cost)
|
alpar@1
|
45 |
{ glp_vertex *v;
|
alpar@1
|
46 |
glp_arc *a;
|
alpar@1
|
47 |
int i, j, type, ind[1+2];
|
alpar@1
|
48 |
double rhs, low, cap, cost, val[1+2];
|
alpar@1
|
49 |
if (!(names == GLP_ON || names == GLP_OFF))
|
alpar@1
|
50 |
xerror("glp_mincost_lp: names = %d; invalid parameter\n",
|
alpar@1
|
51 |
names);
|
alpar@1
|
52 |
if (v_rhs >= 0 && v_rhs > G->v_size - (int)sizeof(double))
|
alpar@1
|
53 |
xerror("glp_mincost_lp: v_rhs = %d; invalid offset\n", v_rhs);
|
alpar@1
|
54 |
if (a_low >= 0 && a_low > G->a_size - (int)sizeof(double))
|
alpar@1
|
55 |
xerror("glp_mincost_lp: a_low = %d; invalid offset\n", a_low);
|
alpar@1
|
56 |
if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double))
|
alpar@1
|
57 |
xerror("glp_mincost_lp: a_cap = %d; invalid offset\n", a_cap);
|
alpar@1
|
58 |
if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double))
|
alpar@1
|
59 |
xerror("glp_mincost_lp: a_cost = %d; invalid offset\n", a_cost)
|
alpar@1
|
60 |
;
|
alpar@1
|
61 |
glp_erase_prob(lp);
|
alpar@1
|
62 |
if (names) glp_set_prob_name(lp, G->name);
|
alpar@1
|
63 |
if (G->nv > 0) glp_add_rows(lp, G->nv);
|
alpar@1
|
64 |
for (i = 1; i <= G->nv; i++)
|
alpar@1
|
65 |
{ v = G->v[i];
|
alpar@1
|
66 |
if (names) glp_set_row_name(lp, i, v->name);
|
alpar@1
|
67 |
if (v_rhs >= 0)
|
alpar@1
|
68 |
memcpy(&rhs, (char *)v->data + v_rhs, sizeof(double));
|
alpar@1
|
69 |
else
|
alpar@1
|
70 |
rhs = 0.0;
|
alpar@1
|
71 |
glp_set_row_bnds(lp, i, GLP_FX, rhs, rhs);
|
alpar@1
|
72 |
}
|
alpar@1
|
73 |
if (G->na > 0) glp_add_cols(lp, G->na);
|
alpar@1
|
74 |
for (i = 1, j = 0; i <= G->nv; i++)
|
alpar@1
|
75 |
{ v = G->v[i];
|
alpar@1
|
76 |
for (a = v->out; a != NULL; a = a->t_next)
|
alpar@1
|
77 |
{ j++;
|
alpar@1
|
78 |
if (names)
|
alpar@1
|
79 |
{ char name[50+1];
|
alpar@1
|
80 |
sprintf(name, "x[%d,%d]", a->tail->i, a->head->i);
|
alpar@1
|
81 |
xassert(strlen(name) < sizeof(name));
|
alpar@1
|
82 |
glp_set_col_name(lp, j, name);
|
alpar@1
|
83 |
}
|
alpar@1
|
84 |
if (a->tail->i != a->head->i)
|
alpar@1
|
85 |
{ ind[1] = a->tail->i, val[1] = +1.0;
|
alpar@1
|
86 |
ind[2] = a->head->i, val[2] = -1.0;
|
alpar@1
|
87 |
glp_set_mat_col(lp, j, 2, ind, val);
|
alpar@1
|
88 |
}
|
alpar@1
|
89 |
if (a_low >= 0)
|
alpar@1
|
90 |
memcpy(&low, (char *)a->data + a_low, sizeof(double));
|
alpar@1
|
91 |
else
|
alpar@1
|
92 |
low = 0.0;
|
alpar@1
|
93 |
if (a_cap >= 0)
|
alpar@1
|
94 |
memcpy(&cap, (char *)a->data + a_cap, sizeof(double));
|
alpar@1
|
95 |
else
|
alpar@1
|
96 |
cap = 1.0;
|
alpar@1
|
97 |
if (cap == DBL_MAX)
|
alpar@1
|
98 |
type = GLP_LO;
|
alpar@1
|
99 |
else if (low != cap)
|
alpar@1
|
100 |
type = GLP_DB;
|
alpar@1
|
101 |
else
|
alpar@1
|
102 |
type = GLP_FX;
|
alpar@1
|
103 |
glp_set_col_bnds(lp, j, type, low, cap);
|
alpar@1
|
104 |
if (a_cost >= 0)
|
alpar@1
|
105 |
memcpy(&cost, (char *)a->data + a_cost, sizeof(double));
|
alpar@1
|
106 |
else
|
alpar@1
|
107 |
cost = 0.0;
|
alpar@1
|
108 |
glp_set_obj_coef(lp, j, cost);
|
alpar@1
|
109 |
}
|
alpar@1
|
110 |
}
|
alpar@1
|
111 |
xassert(j == G->na);
|
alpar@1
|
112 |
return;
|
alpar@1
|
113 |
}
|
alpar@1
|
114 |
|
alpar@1
|
115 |
/**********************************************************************/
|
alpar@1
|
116 |
|
alpar@1
|
117 |
int glp_mincost_okalg(glp_graph *G, int v_rhs, int a_low, int a_cap,
|
alpar@1
|
118 |
int a_cost, double *sol, int a_x, int v_pi)
|
alpar@1
|
119 |
{ /* find minimum-cost flow with out-of-kilter algorithm */
|
alpar@1
|
120 |
glp_vertex *v;
|
alpar@1
|
121 |
glp_arc *a;
|
alpar@1
|
122 |
int nv, na, i, k, s, t, *tail, *head, *low, *cap, *cost, *x, *pi,
|
alpar@1
|
123 |
ret;
|
alpar@1
|
124 |
double sum, temp;
|
alpar@1
|
125 |
if (v_rhs >= 0 && v_rhs > G->v_size - (int)sizeof(double))
|
alpar@1
|
126 |
xerror("glp_mincost_okalg: v_rhs = %d; invalid offset\n",
|
alpar@1
|
127 |
v_rhs);
|
alpar@1
|
128 |
if (a_low >= 0 && a_low > G->a_size - (int)sizeof(double))
|
alpar@1
|
129 |
xerror("glp_mincost_okalg: a_low = %d; invalid offset\n",
|
alpar@1
|
130 |
a_low);
|
alpar@1
|
131 |
if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double))
|
alpar@1
|
132 |
xerror("glp_mincost_okalg: a_cap = %d; invalid offset\n",
|
alpar@1
|
133 |
a_cap);
|
alpar@1
|
134 |
if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double))
|
alpar@1
|
135 |
xerror("glp_mincost_okalg: a_cost = %d; invalid offset\n",
|
alpar@1
|
136 |
a_cost);
|
alpar@1
|
137 |
if (a_x >= 0 && a_x > G->a_size - (int)sizeof(double))
|
alpar@1
|
138 |
xerror("glp_mincost_okalg: a_x = %d; invalid offset\n", a_x);
|
alpar@1
|
139 |
if (v_pi >= 0 && v_pi > G->v_size - (int)sizeof(double))
|
alpar@1
|
140 |
xerror("glp_mincost_okalg: v_pi = %d; invalid offset\n", v_pi);
|
alpar@1
|
141 |
/* s is artificial source node */
|
alpar@1
|
142 |
s = G->nv + 1;
|
alpar@1
|
143 |
/* t is artificial sink node */
|
alpar@1
|
144 |
t = s + 1;
|
alpar@1
|
145 |
/* nv is the total number of nodes in the resulting network */
|
alpar@1
|
146 |
nv = t;
|
alpar@1
|
147 |
/* na is the total number of arcs in the resulting network */
|
alpar@1
|
148 |
na = G->na + 1;
|
alpar@1
|
149 |
for (i = 1; i <= G->nv; i++)
|
alpar@1
|
150 |
{ v = G->v[i];
|
alpar@1
|
151 |
if (v_rhs >= 0)
|
alpar@1
|
152 |
memcpy(&temp, (char *)v->data + v_rhs, sizeof(double));
|
alpar@1
|
153 |
else
|
alpar@1
|
154 |
temp = 0.0;
|
alpar@1
|
155 |
if (temp != 0.0) na++;
|
alpar@1
|
156 |
}
|
alpar@1
|
157 |
/* allocate working arrays */
|
alpar@1
|
158 |
tail = xcalloc(1+na, sizeof(int));
|
alpar@1
|
159 |
head = xcalloc(1+na, sizeof(int));
|
alpar@1
|
160 |
low = xcalloc(1+na, sizeof(int));
|
alpar@1
|
161 |
cap = xcalloc(1+na, sizeof(int));
|
alpar@1
|
162 |
cost = xcalloc(1+na, sizeof(int));
|
alpar@1
|
163 |
x = xcalloc(1+na, sizeof(int));
|
alpar@1
|
164 |
pi = xcalloc(1+nv, sizeof(int));
|
alpar@1
|
165 |
/* construct the resulting network */
|
alpar@1
|
166 |
k = 0;
|
alpar@1
|
167 |
/* (original arcs) */
|
alpar@1
|
168 |
for (i = 1; i <= G->nv; i++)
|
alpar@1
|
169 |
{ v = G->v[i];
|
alpar@1
|
170 |
for (a = v->out; a != NULL; a = a->t_next)
|
alpar@1
|
171 |
{ k++;
|
alpar@1
|
172 |
tail[k] = a->tail->i;
|
alpar@1
|
173 |
head[k] = a->head->i;
|
alpar@1
|
174 |
if (tail[k] == head[k])
|
alpar@1
|
175 |
{ ret = GLP_EDATA;
|
alpar@1
|
176 |
goto done;
|
alpar@1
|
177 |
}
|
alpar@1
|
178 |
if (a_low >= 0)
|
alpar@1
|
179 |
memcpy(&temp, (char *)a->data + a_low, sizeof(double));
|
alpar@1
|
180 |
else
|
alpar@1
|
181 |
temp = 0.0;
|
alpar@1
|
182 |
if (!(0.0 <= temp && temp <= (double)INT_MAX &&
|
alpar@1
|
183 |
temp == floor(temp)))
|
alpar@1
|
184 |
{ ret = GLP_EDATA;
|
alpar@1
|
185 |
goto done;
|
alpar@1
|
186 |
}
|
alpar@1
|
187 |
low[k] = (int)temp;
|
alpar@1
|
188 |
if (a_cap >= 0)
|
alpar@1
|
189 |
memcpy(&temp, (char *)a->data + a_cap, sizeof(double));
|
alpar@1
|
190 |
else
|
alpar@1
|
191 |
temp = 1.0;
|
alpar@1
|
192 |
if (!((double)low[k] <= temp && temp <= (double)INT_MAX &&
|
alpar@1
|
193 |
temp == floor(temp)))
|
alpar@1
|
194 |
{ ret = GLP_EDATA;
|
alpar@1
|
195 |
goto done;
|
alpar@1
|
196 |
}
|
alpar@1
|
197 |
cap[k] = (int)temp;
|
alpar@1
|
198 |
if (a_cost >= 0)
|
alpar@1
|
199 |
memcpy(&temp, (char *)a->data + a_cost, sizeof(double));
|
alpar@1
|
200 |
else
|
alpar@1
|
201 |
temp = 0.0;
|
alpar@1
|
202 |
if (!(fabs(temp) <= (double)INT_MAX && temp == floor(temp)))
|
alpar@1
|
203 |
{ ret = GLP_EDATA;
|
alpar@1
|
204 |
goto done;
|
alpar@1
|
205 |
}
|
alpar@1
|
206 |
cost[k] = (int)temp;
|
alpar@1
|
207 |
}
|
alpar@1
|
208 |
}
|
alpar@1
|
209 |
/* (artificial arcs) */
|
alpar@1
|
210 |
sum = 0.0;
|
alpar@1
|
211 |
for (i = 1; i <= G->nv; i++)
|
alpar@1
|
212 |
{ v = G->v[i];
|
alpar@1
|
213 |
if (v_rhs >= 0)
|
alpar@1
|
214 |
memcpy(&temp, (char *)v->data + v_rhs, sizeof(double));
|
alpar@1
|
215 |
else
|
alpar@1
|
216 |
temp = 0.0;
|
alpar@1
|
217 |
if (!(fabs(temp) <= (double)INT_MAX && temp == floor(temp)))
|
alpar@1
|
218 |
{ ret = GLP_EDATA;
|
alpar@1
|
219 |
goto done;
|
alpar@1
|
220 |
}
|
alpar@1
|
221 |
if (temp > 0.0)
|
alpar@1
|
222 |
{ /* artificial arc from s to original source i */
|
alpar@1
|
223 |
k++;
|
alpar@1
|
224 |
tail[k] = s;
|
alpar@1
|
225 |
head[k] = i;
|
alpar@1
|
226 |
low[k] = cap[k] = (int)(+temp); /* supply */
|
alpar@1
|
227 |
cost[k] = 0;
|
alpar@1
|
228 |
sum += (double)temp;
|
alpar@1
|
229 |
}
|
alpar@1
|
230 |
else if (temp < 0.0)
|
alpar@1
|
231 |
{ /* artificial arc from original sink i to t */
|
alpar@1
|
232 |
k++;
|
alpar@1
|
233 |
tail[k] = i;
|
alpar@1
|
234 |
head[k] = t;
|
alpar@1
|
235 |
low[k] = cap[k] = (int)(-temp); /* demand */
|
alpar@1
|
236 |
cost[k] = 0;
|
alpar@1
|
237 |
}
|
alpar@1
|
238 |
}
|
alpar@1
|
239 |
/* (feedback arc from t to s) */
|
alpar@1
|
240 |
k++;
|
alpar@1
|
241 |
xassert(k == na);
|
alpar@1
|
242 |
tail[k] = t;
|
alpar@1
|
243 |
head[k] = s;
|
alpar@1
|
244 |
if (sum > (double)INT_MAX)
|
alpar@1
|
245 |
{ ret = GLP_EDATA;
|
alpar@1
|
246 |
goto done;
|
alpar@1
|
247 |
}
|
alpar@1
|
248 |
low[k] = cap[k] = (int)sum; /* total supply/demand */
|
alpar@1
|
249 |
cost[k] = 0;
|
alpar@1
|
250 |
/* find minimal-cost circulation in the resulting network */
|
alpar@1
|
251 |
ret = okalg(nv, na, tail, head, low, cap, cost, x, pi);
|
alpar@1
|
252 |
switch (ret)
|
alpar@1
|
253 |
{ case 0:
|
alpar@1
|
254 |
/* optimal circulation found */
|
alpar@1
|
255 |
ret = 0;
|
alpar@1
|
256 |
break;
|
alpar@1
|
257 |
case 1:
|
alpar@1
|
258 |
/* no feasible circulation exists */
|
alpar@1
|
259 |
ret = GLP_ENOPFS;
|
alpar@1
|
260 |
break;
|
alpar@1
|
261 |
case 2:
|
alpar@1
|
262 |
/* integer overflow occured */
|
alpar@1
|
263 |
ret = GLP_ERANGE;
|
alpar@1
|
264 |
goto done;
|
alpar@1
|
265 |
case 3:
|
alpar@1
|
266 |
/* optimality test failed (logic error) */
|
alpar@1
|
267 |
ret = GLP_EFAIL;
|
alpar@1
|
268 |
goto done;
|
alpar@1
|
269 |
default:
|
alpar@1
|
270 |
xassert(ret != ret);
|
alpar@1
|
271 |
}
|
alpar@1
|
272 |
/* store solution components */
|
alpar@1
|
273 |
/* (objective function = the total cost) */
|
alpar@1
|
274 |
if (sol != NULL)
|
alpar@1
|
275 |
{ temp = 0.0;
|
alpar@1
|
276 |
for (k = 1; k <= na; k++)
|
alpar@1
|
277 |
temp += (double)cost[k] * (double)x[k];
|
alpar@1
|
278 |
*sol = temp;
|
alpar@1
|
279 |
}
|
alpar@1
|
280 |
/* (arc flows) */
|
alpar@1
|
281 |
if (a_x >= 0)
|
alpar@1
|
282 |
{ k = 0;
|
alpar@1
|
283 |
for (i = 1; i <= G->nv; i++)
|
alpar@1
|
284 |
{ v = G->v[i];
|
alpar@1
|
285 |
for (a = v->out; a != NULL; a = a->t_next)
|
alpar@1
|
286 |
{ temp = (double)x[++k];
|
alpar@1
|
287 |
memcpy((char *)a->data + a_x, &temp, sizeof(double));
|
alpar@1
|
288 |
}
|
alpar@1
|
289 |
}
|
alpar@1
|
290 |
}
|
alpar@1
|
291 |
/* (node potentials = Lagrange multipliers) */
|
alpar@1
|
292 |
if (v_pi >= 0)
|
alpar@1
|
293 |
{ for (i = 1; i <= G->nv; i++)
|
alpar@1
|
294 |
{ v = G->v[i];
|
alpar@1
|
295 |
temp = - (double)pi[i];
|
alpar@1
|
296 |
memcpy((char *)v->data + v_pi, &temp, sizeof(double));
|
alpar@1
|
297 |
}
|
alpar@1
|
298 |
}
|
alpar@1
|
299 |
done: /* free working arrays */
|
alpar@1
|
300 |
xfree(tail);
|
alpar@1
|
301 |
xfree(head);
|
alpar@1
|
302 |
xfree(low);
|
alpar@1
|
303 |
xfree(cap);
|
alpar@1
|
304 |
xfree(cost);
|
alpar@1
|
305 |
xfree(x);
|
alpar@1
|
306 |
xfree(pi);
|
alpar@1
|
307 |
return ret;
|
alpar@1
|
308 |
}
|
alpar@1
|
309 |
|
alpar@1
|
310 |
/***********************************************************************
|
alpar@1
|
311 |
* NAME
|
alpar@1
|
312 |
*
|
alpar@1
|
313 |
* glp_maxflow_lp - convert maximum flow problem to LP
|
alpar@1
|
314 |
*
|
alpar@1
|
315 |
* SYNOPSIS
|
alpar@1
|
316 |
*
|
alpar@1
|
317 |
* void glp_maxflow_lp(glp_prob *lp, glp_graph *G, int names, int s,
|
alpar@1
|
318 |
* int t, int a_cap);
|
alpar@1
|
319 |
*
|
alpar@1
|
320 |
* DESCRIPTION
|
alpar@1
|
321 |
*
|
alpar@1
|
322 |
* The routine glp_maxflow_lp builds an LP problem, which corresponds
|
alpar@1
|
323 |
* to the maximum flow problem on the specified network G. */
|
alpar@1
|
324 |
|
alpar@1
|
325 |
void glp_maxflow_lp(glp_prob *lp, glp_graph *G, int names, int s,
|
alpar@1
|
326 |
int t, int a_cap)
|
alpar@1
|
327 |
{ glp_vertex *v;
|
alpar@1
|
328 |
glp_arc *a;
|
alpar@1
|
329 |
int i, j, type, ind[1+2];
|
alpar@1
|
330 |
double cap, val[1+2];
|
alpar@1
|
331 |
if (!(names == GLP_ON || names == GLP_OFF))
|
alpar@1
|
332 |
xerror("glp_maxflow_lp: names = %d; invalid parameter\n",
|
alpar@1
|
333 |
names);
|
alpar@1
|
334 |
if (!(1 <= s && s <= G->nv))
|
alpar@1
|
335 |
xerror("glp_maxflow_lp: s = %d; source node number out of rang"
|
alpar@1
|
336 |
"e\n", s);
|
alpar@1
|
337 |
if (!(1 <= t && t <= G->nv))
|
alpar@1
|
338 |
xerror("glp_maxflow_lp: t = %d: sink node number out of range "
|
alpar@1
|
339 |
"\n", t);
|
alpar@1
|
340 |
if (s == t)
|
alpar@1
|
341 |
xerror("glp_maxflow_lp: s = t = %d; source and sink nodes must"
|
alpar@1
|
342 |
" be distinct\n", s);
|
alpar@1
|
343 |
if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double))
|
alpar@1
|
344 |
xerror("glp_maxflow_lp: a_cap = %d; invalid offset\n", a_cap);
|
alpar@1
|
345 |
glp_erase_prob(lp);
|
alpar@1
|
346 |
if (names) glp_set_prob_name(lp, G->name);
|
alpar@1
|
347 |
glp_set_obj_dir(lp, GLP_MAX);
|
alpar@1
|
348 |
glp_add_rows(lp, G->nv);
|
alpar@1
|
349 |
for (i = 1; i <= G->nv; i++)
|
alpar@1
|
350 |
{ v = G->v[i];
|
alpar@1
|
351 |
if (names) glp_set_row_name(lp, i, v->name);
|
alpar@1
|
352 |
if (i == s)
|
alpar@1
|
353 |
type = GLP_LO;
|
alpar@1
|
354 |
else if (i == t)
|
alpar@1
|
355 |
type = GLP_UP;
|
alpar@1
|
356 |
else
|
alpar@1
|
357 |
type = GLP_FX;
|
alpar@1
|
358 |
glp_set_row_bnds(lp, i, type, 0.0, 0.0);
|
alpar@1
|
359 |
}
|
alpar@1
|
360 |
if (G->na > 0) glp_add_cols(lp, G->na);
|
alpar@1
|
361 |
for (i = 1, j = 0; i <= G->nv; i++)
|
alpar@1
|
362 |
{ v = G->v[i];
|
alpar@1
|
363 |
for (a = v->out; a != NULL; a = a->t_next)
|
alpar@1
|
364 |
{ j++;
|
alpar@1
|
365 |
if (names)
|
alpar@1
|
366 |
{ char name[50+1];
|
alpar@1
|
367 |
sprintf(name, "x[%d,%d]", a->tail->i, a->head->i);
|
alpar@1
|
368 |
xassert(strlen(name) < sizeof(name));
|
alpar@1
|
369 |
glp_set_col_name(lp, j, name);
|
alpar@1
|
370 |
}
|
alpar@1
|
371 |
if (a->tail->i != a->head->i)
|
alpar@1
|
372 |
{ ind[1] = a->tail->i, val[1] = +1.0;
|
alpar@1
|
373 |
ind[2] = a->head->i, val[2] = -1.0;
|
alpar@1
|
374 |
glp_set_mat_col(lp, j, 2, ind, val);
|
alpar@1
|
375 |
}
|
alpar@1
|
376 |
if (a_cap >= 0)
|
alpar@1
|
377 |
memcpy(&cap, (char *)a->data + a_cap, sizeof(double));
|
alpar@1
|
378 |
else
|
alpar@1
|
379 |
cap = 1.0;
|
alpar@1
|
380 |
if (cap == DBL_MAX)
|
alpar@1
|
381 |
type = GLP_LO;
|
alpar@1
|
382 |
else if (cap != 0.0)
|
alpar@1
|
383 |
type = GLP_DB;
|
alpar@1
|
384 |
else
|
alpar@1
|
385 |
type = GLP_FX;
|
alpar@1
|
386 |
glp_set_col_bnds(lp, j, type, 0.0, cap);
|
alpar@1
|
387 |
if (a->tail->i == s)
|
alpar@1
|
388 |
glp_set_obj_coef(lp, j, +1.0);
|
alpar@1
|
389 |
else if (a->head->i == s)
|
alpar@1
|
390 |
glp_set_obj_coef(lp, j, -1.0);
|
alpar@1
|
391 |
}
|
alpar@1
|
392 |
}
|
alpar@1
|
393 |
xassert(j == G->na);
|
alpar@1
|
394 |
return;
|
alpar@1
|
395 |
}
|
alpar@1
|
396 |
|
alpar@1
|
397 |
int glp_maxflow_ffalg(glp_graph *G, int s, int t, int a_cap,
|
alpar@1
|
398 |
double *sol, int a_x, int v_cut)
|
alpar@1
|
399 |
{ /* find maximal flow with Ford-Fulkerson algorithm */
|
alpar@1
|
400 |
glp_vertex *v;
|
alpar@1
|
401 |
glp_arc *a;
|
alpar@1
|
402 |
int nv, na, i, k, flag, *tail, *head, *cap, *x, ret;
|
alpar@1
|
403 |
char *cut;
|
alpar@1
|
404 |
double temp;
|
alpar@1
|
405 |
if (!(1 <= s && s <= G->nv))
|
alpar@1
|
406 |
xerror("glp_maxflow_ffalg: s = %d; source node number out of r"
|
alpar@1
|
407 |
"ange\n", s);
|
alpar@1
|
408 |
if (!(1 <= t && t <= G->nv))
|
alpar@1
|
409 |
xerror("glp_maxflow_ffalg: t = %d: sink node number out of ran"
|
alpar@1
|
410 |
"ge\n", t);
|
alpar@1
|
411 |
if (s == t)
|
alpar@1
|
412 |
xerror("glp_maxflow_ffalg: s = t = %d; source and sink nodes m"
|
alpar@1
|
413 |
"ust be distinct\n", s);
|
alpar@1
|
414 |
if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double))
|
alpar@1
|
415 |
xerror("glp_maxflow_ffalg: a_cap = %d; invalid offset\n",
|
alpar@1
|
416 |
a_cap);
|
alpar@1
|
417 |
if (v_cut >= 0 && v_cut > G->v_size - (int)sizeof(int))
|
alpar@1
|
418 |
xerror("glp_maxflow_ffalg: v_cut = %d; invalid offset\n",
|
alpar@1
|
419 |
v_cut);
|
alpar@1
|
420 |
/* allocate working arrays */
|
alpar@1
|
421 |
nv = G->nv;
|
alpar@1
|
422 |
na = G->na;
|
alpar@1
|
423 |
tail = xcalloc(1+na, sizeof(int));
|
alpar@1
|
424 |
head = xcalloc(1+na, sizeof(int));
|
alpar@1
|
425 |
cap = xcalloc(1+na, sizeof(int));
|
alpar@1
|
426 |
x = xcalloc(1+na, sizeof(int));
|
alpar@1
|
427 |
if (v_cut < 0)
|
alpar@1
|
428 |
cut = NULL;
|
alpar@1
|
429 |
else
|
alpar@1
|
430 |
cut = xcalloc(1+nv, sizeof(char));
|
alpar@1
|
431 |
/* copy the flow network */
|
alpar@1
|
432 |
k = 0;
|
alpar@1
|
433 |
for (i = 1; i <= G->nv; i++)
|
alpar@1
|
434 |
{ v = G->v[i];
|
alpar@1
|
435 |
for (a = v->out; a != NULL; a = a->t_next)
|
alpar@1
|
436 |
{ k++;
|
alpar@1
|
437 |
tail[k] = a->tail->i;
|
alpar@1
|
438 |
head[k] = a->head->i;
|
alpar@1
|
439 |
if (tail[k] == head[k])
|
alpar@1
|
440 |
{ ret = GLP_EDATA;
|
alpar@1
|
441 |
goto done;
|
alpar@1
|
442 |
}
|
alpar@1
|
443 |
if (a_cap >= 0)
|
alpar@1
|
444 |
memcpy(&temp, (char *)a->data + a_cap, sizeof(double));
|
alpar@1
|
445 |
else
|
alpar@1
|
446 |
temp = 1.0;
|
alpar@1
|
447 |
if (!(0.0 <= temp && temp <= (double)INT_MAX &&
|
alpar@1
|
448 |
temp == floor(temp)))
|
alpar@1
|
449 |
{ ret = GLP_EDATA;
|
alpar@1
|
450 |
goto done;
|
alpar@1
|
451 |
}
|
alpar@1
|
452 |
cap[k] = (int)temp;
|
alpar@1
|
453 |
}
|
alpar@1
|
454 |
}
|
alpar@1
|
455 |
xassert(k == na);
|
alpar@1
|
456 |
/* find maximal flow in the flow network */
|
alpar@1
|
457 |
ffalg(nv, na, tail, head, s, t, cap, x, cut);
|
alpar@1
|
458 |
ret = 0;
|
alpar@1
|
459 |
/* store solution components */
|
alpar@1
|
460 |
/* (objective function = total flow through the network) */
|
alpar@1
|
461 |
if (sol != NULL)
|
alpar@1
|
462 |
{ temp = 0.0;
|
alpar@1
|
463 |
for (k = 1; k <= na; k++)
|
alpar@1
|
464 |
{ if (tail[k] == s)
|
alpar@1
|
465 |
temp += (double)x[k];
|
alpar@1
|
466 |
else if (head[k] == s)
|
alpar@1
|
467 |
temp -= (double)x[k];
|
alpar@1
|
468 |
}
|
alpar@1
|
469 |
*sol = temp;
|
alpar@1
|
470 |
}
|
alpar@1
|
471 |
/* (arc flows) */
|
alpar@1
|
472 |
if (a_x >= 0)
|
alpar@1
|
473 |
{ k = 0;
|
alpar@1
|
474 |
for (i = 1; i <= G->nv; i++)
|
alpar@1
|
475 |
{ v = G->v[i];
|
alpar@1
|
476 |
for (a = v->out; a != NULL; a = a->t_next)
|
alpar@1
|
477 |
{ temp = (double)x[++k];
|
alpar@1
|
478 |
memcpy((char *)a->data + a_x, &temp, sizeof(double));
|
alpar@1
|
479 |
}
|
alpar@1
|
480 |
}
|
alpar@1
|
481 |
}
|
alpar@1
|
482 |
/* (node flags) */
|
alpar@1
|
483 |
if (v_cut >= 0)
|
alpar@1
|
484 |
{ for (i = 1; i <= G->nv; i++)
|
alpar@1
|
485 |
{ v = G->v[i];
|
alpar@1
|
486 |
flag = cut[i];
|
alpar@1
|
487 |
memcpy((char *)v->data + v_cut, &flag, sizeof(int));
|
alpar@1
|
488 |
}
|
alpar@1
|
489 |
}
|
alpar@1
|
490 |
done: /* free working arrays */
|
alpar@1
|
491 |
xfree(tail);
|
alpar@1
|
492 |
xfree(head);
|
alpar@1
|
493 |
xfree(cap);
|
alpar@1
|
494 |
xfree(x);
|
alpar@1
|
495 |
if (cut != NULL) xfree(cut);
|
alpar@1
|
496 |
return ret;
|
alpar@1
|
497 |
}
|
alpar@1
|
498 |
|
alpar@1
|
499 |
/***********************************************************************
|
alpar@1
|
500 |
* NAME
|
alpar@1
|
501 |
*
|
alpar@1
|
502 |
* glp_check_asnprob - check correctness of assignment problem data
|
alpar@1
|
503 |
*
|
alpar@1
|
504 |
* SYNOPSIS
|
alpar@1
|
505 |
*
|
alpar@1
|
506 |
* int glp_check_asnprob(glp_graph *G, int v_set);
|
alpar@1
|
507 |
*
|
alpar@1
|
508 |
* RETURNS
|
alpar@1
|
509 |
*
|
alpar@1
|
510 |
* If the specified assignment problem data are correct, the routine
|
alpar@1
|
511 |
* glp_check_asnprob returns zero, otherwise, non-zero. */
|
alpar@1
|
512 |
|
alpar@1
|
513 |
int glp_check_asnprob(glp_graph *G, int v_set)
|
alpar@1
|
514 |
{ glp_vertex *v;
|
alpar@1
|
515 |
int i, k, ret = 0;
|
alpar@1
|
516 |
if (v_set >= 0 && v_set > G->v_size - (int)sizeof(int))
|
alpar@1
|
517 |
xerror("glp_check_asnprob: v_set = %d; invalid offset\n",
|
alpar@1
|
518 |
v_set);
|
alpar@1
|
519 |
for (i = 1; i <= G->nv; i++)
|
alpar@1
|
520 |
{ v = G->v[i];
|
alpar@1
|
521 |
if (v_set >= 0)
|
alpar@1
|
522 |
{ memcpy(&k, (char *)v->data + v_set, sizeof(int));
|
alpar@1
|
523 |
if (k == 0)
|
alpar@1
|
524 |
{ if (v->in != NULL)
|
alpar@1
|
525 |
{ ret = 1;
|
alpar@1
|
526 |
break;
|
alpar@1
|
527 |
}
|
alpar@1
|
528 |
}
|
alpar@1
|
529 |
else if (k == 1)
|
alpar@1
|
530 |
{ if (v->out != NULL)
|
alpar@1
|
531 |
{ ret = 2;
|
alpar@1
|
532 |
break;
|
alpar@1
|
533 |
}
|
alpar@1
|
534 |
}
|
alpar@1
|
535 |
else
|
alpar@1
|
536 |
{ ret = 3;
|
alpar@1
|
537 |
break;
|
alpar@1
|
538 |
}
|
alpar@1
|
539 |
}
|
alpar@1
|
540 |
else
|
alpar@1
|
541 |
{ if (v->in != NULL && v->out != NULL)
|
alpar@1
|
542 |
{ ret = 4;
|
alpar@1
|
543 |
break;
|
alpar@1
|
544 |
}
|
alpar@1
|
545 |
}
|
alpar@1
|
546 |
}
|
alpar@1
|
547 |
return ret;
|
alpar@1
|
548 |
}
|
alpar@1
|
549 |
|
alpar@1
|
550 |
/***********************************************************************
|
alpar@1
|
551 |
* NAME
|
alpar@1
|
552 |
*
|
alpar@1
|
553 |
* glp_asnprob_lp - convert assignment problem to LP
|
alpar@1
|
554 |
*
|
alpar@1
|
555 |
* SYNOPSIS
|
alpar@1
|
556 |
*
|
alpar@1
|
557 |
* int glp_asnprob_lp(glp_prob *P, int form, glp_graph *G, int names,
|
alpar@1
|
558 |
* int v_set, int a_cost);
|
alpar@1
|
559 |
*
|
alpar@1
|
560 |
* DESCRIPTION
|
alpar@1
|
561 |
*
|
alpar@1
|
562 |
* The routine glp_asnprob_lp builds an LP problem, which corresponds
|
alpar@1
|
563 |
* to the assignment problem on the specified graph G.
|
alpar@1
|
564 |
*
|
alpar@1
|
565 |
* RETURNS
|
alpar@1
|
566 |
*
|
alpar@1
|
567 |
* If the LP problem has been successfully built, the routine returns
|
alpar@1
|
568 |
* zero, otherwise, non-zero. */
|
alpar@1
|
569 |
|
alpar@1
|
570 |
int glp_asnprob_lp(glp_prob *P, int form, glp_graph *G, int names,
|
alpar@1
|
571 |
int v_set, int a_cost)
|
alpar@1
|
572 |
{ glp_vertex *v;
|
alpar@1
|
573 |
glp_arc *a;
|
alpar@1
|
574 |
int i, j, ret, ind[1+2];
|
alpar@1
|
575 |
double cost, val[1+2];
|
alpar@1
|
576 |
if (!(form == GLP_ASN_MIN || form == GLP_ASN_MAX ||
|
alpar@1
|
577 |
form == GLP_ASN_MMP))
|
alpar@1
|
578 |
xerror("glp_asnprob_lp: form = %d; invalid parameter\n",
|
alpar@1
|
579 |
form);
|
alpar@1
|
580 |
if (!(names == GLP_ON || names == GLP_OFF))
|
alpar@1
|
581 |
xerror("glp_asnprob_lp: names = %d; invalid parameter\n",
|
alpar@1
|
582 |
names);
|
alpar@1
|
583 |
if (v_set >= 0 && v_set > G->v_size - (int)sizeof(int))
|
alpar@1
|
584 |
xerror("glp_asnprob_lp: v_set = %d; invalid offset\n",
|
alpar@1
|
585 |
v_set);
|
alpar@1
|
586 |
if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double))
|
alpar@1
|
587 |
xerror("glp_asnprob_lp: a_cost = %d; invalid offset\n",
|
alpar@1
|
588 |
a_cost);
|
alpar@1
|
589 |
ret = glp_check_asnprob(G, v_set);
|
alpar@1
|
590 |
if (ret != 0) goto done;
|
alpar@1
|
591 |
glp_erase_prob(P);
|
alpar@1
|
592 |
if (names) glp_set_prob_name(P, G->name);
|
alpar@1
|
593 |
glp_set_obj_dir(P, form == GLP_ASN_MIN ? GLP_MIN : GLP_MAX);
|
alpar@1
|
594 |
if (G->nv > 0) glp_add_rows(P, G->nv);
|
alpar@1
|
595 |
for (i = 1; i <= G->nv; i++)
|
alpar@1
|
596 |
{ v = G->v[i];
|
alpar@1
|
597 |
if (names) glp_set_row_name(P, i, v->name);
|
alpar@1
|
598 |
glp_set_row_bnds(P, i, form == GLP_ASN_MMP ? GLP_UP : GLP_FX,
|
alpar@1
|
599 |
1.0, 1.0);
|
alpar@1
|
600 |
}
|
alpar@1
|
601 |
if (G->na > 0) glp_add_cols(P, G->na);
|
alpar@1
|
602 |
for (i = 1, j = 0; i <= G->nv; i++)
|
alpar@1
|
603 |
{ v = G->v[i];
|
alpar@1
|
604 |
for (a = v->out; a != NULL; a = a->t_next)
|
alpar@1
|
605 |
{ j++;
|
alpar@1
|
606 |
if (names)
|
alpar@1
|
607 |
{ char name[50+1];
|
alpar@1
|
608 |
sprintf(name, "x[%d,%d]", a->tail->i, a->head->i);
|
alpar@1
|
609 |
xassert(strlen(name) < sizeof(name));
|
alpar@1
|
610 |
glp_set_col_name(P, j, name);
|
alpar@1
|
611 |
}
|
alpar@1
|
612 |
ind[1] = a->tail->i, val[1] = +1.0;
|
alpar@1
|
613 |
ind[2] = a->head->i, val[2] = +1.0;
|
alpar@1
|
614 |
glp_set_mat_col(P, j, 2, ind, val);
|
alpar@1
|
615 |
glp_set_col_bnds(P, j, GLP_DB, 0.0, 1.0);
|
alpar@1
|
616 |
if (a_cost >= 0)
|
alpar@1
|
617 |
memcpy(&cost, (char *)a->data + a_cost, sizeof(double));
|
alpar@1
|
618 |
else
|
alpar@1
|
619 |
cost = 1.0;
|
alpar@1
|
620 |
glp_set_obj_coef(P, j, cost);
|
alpar@1
|
621 |
}
|
alpar@1
|
622 |
}
|
alpar@1
|
623 |
xassert(j == G->na);
|
alpar@1
|
624 |
done: return ret;
|
alpar@1
|
625 |
}
|
alpar@1
|
626 |
|
alpar@1
|
627 |
/**********************************************************************/
|
alpar@1
|
628 |
|
alpar@1
|
629 |
int glp_asnprob_okalg(int form, glp_graph *G, int v_set, int a_cost,
|
alpar@1
|
630 |
double *sol, int a_x)
|
alpar@1
|
631 |
{ /* solve assignment problem with out-of-kilter algorithm */
|
alpar@1
|
632 |
glp_vertex *v;
|
alpar@1
|
633 |
glp_arc *a;
|
alpar@1
|
634 |
int nv, na, i, k, *tail, *head, *low, *cap, *cost, *x, *pi, ret;
|
alpar@1
|
635 |
double temp;
|
alpar@1
|
636 |
if (!(form == GLP_ASN_MIN || form == GLP_ASN_MAX ||
|
alpar@1
|
637 |
form == GLP_ASN_MMP))
|
alpar@1
|
638 |
xerror("glp_asnprob_okalg: form = %d; invalid parameter\n",
|
alpar@1
|
639 |
form);
|
alpar@1
|
640 |
if (v_set >= 0 && v_set > G->v_size - (int)sizeof(int))
|
alpar@1
|
641 |
xerror("glp_asnprob_okalg: v_set = %d; invalid offset\n",
|
alpar@1
|
642 |
v_set);
|
alpar@1
|
643 |
if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double))
|
alpar@1
|
644 |
xerror("glp_asnprob_okalg: a_cost = %d; invalid offset\n",
|
alpar@1
|
645 |
a_cost);
|
alpar@1
|
646 |
if (a_x >= 0 && a_x > G->a_size - (int)sizeof(int))
|
alpar@1
|
647 |
xerror("glp_asnprob_okalg: a_x = %d; invalid offset\n", a_x);
|
alpar@1
|
648 |
if (glp_check_asnprob(G, v_set))
|
alpar@1
|
649 |
return GLP_EDATA;
|
alpar@1
|
650 |
/* nv is the total number of nodes in the resulting network */
|
alpar@1
|
651 |
nv = G->nv + 1;
|
alpar@1
|
652 |
/* na is the total number of arcs in the resulting network */
|
alpar@1
|
653 |
na = G->na + G->nv;
|
alpar@1
|
654 |
/* allocate working arrays */
|
alpar@1
|
655 |
tail = xcalloc(1+na, sizeof(int));
|
alpar@1
|
656 |
head = xcalloc(1+na, sizeof(int));
|
alpar@1
|
657 |
low = xcalloc(1+na, sizeof(int));
|
alpar@1
|
658 |
cap = xcalloc(1+na, sizeof(int));
|
alpar@1
|
659 |
cost = xcalloc(1+na, sizeof(int));
|
alpar@1
|
660 |
x = xcalloc(1+na, sizeof(int));
|
alpar@1
|
661 |
pi = xcalloc(1+nv, sizeof(int));
|
alpar@1
|
662 |
/* construct the resulting network */
|
alpar@1
|
663 |
k = 0;
|
alpar@1
|
664 |
/* (original arcs) */
|
alpar@1
|
665 |
for (i = 1; i <= G->nv; i++)
|
alpar@1
|
666 |
{ v = G->v[i];
|
alpar@1
|
667 |
for (a = v->out; a != NULL; a = a->t_next)
|
alpar@1
|
668 |
{ k++;
|
alpar@1
|
669 |
tail[k] = a->tail->i;
|
alpar@1
|
670 |
head[k] = a->head->i;
|
alpar@1
|
671 |
low[k] = 0;
|
alpar@1
|
672 |
cap[k] = 1;
|
alpar@1
|
673 |
if (a_cost >= 0)
|
alpar@1
|
674 |
memcpy(&temp, (char *)a->data + a_cost, sizeof(double));
|
alpar@1
|
675 |
else
|
alpar@1
|
676 |
temp = 1.0;
|
alpar@1
|
677 |
if (!(fabs(temp) <= (double)INT_MAX && temp == floor(temp)))
|
alpar@1
|
678 |
{ ret = GLP_EDATA;
|
alpar@1
|
679 |
goto done;
|
alpar@1
|
680 |
}
|
alpar@1
|
681 |
cost[k] = (int)temp;
|
alpar@1
|
682 |
if (form != GLP_ASN_MIN) cost[k] = - cost[k];
|
alpar@1
|
683 |
}
|
alpar@1
|
684 |
}
|
alpar@1
|
685 |
/* (artificial arcs) */
|
alpar@1
|
686 |
for (i = 1; i <= G->nv; i++)
|
alpar@1
|
687 |
{ v = G->v[i];
|
alpar@1
|
688 |
k++;
|
alpar@1
|
689 |
if (v->out == NULL)
|
alpar@1
|
690 |
tail[k] = i, head[k] = nv;
|
alpar@1
|
691 |
else if (v->in == NULL)
|
alpar@1
|
692 |
tail[k] = nv, head[k] = i;
|
alpar@1
|
693 |
else
|
alpar@1
|
694 |
xassert(v != v);
|
alpar@1
|
695 |
low[k] = (form == GLP_ASN_MMP ? 0 : 1);
|
alpar@1
|
696 |
cap[k] = 1;
|
alpar@1
|
697 |
cost[k] = 0;
|
alpar@1
|
698 |
}
|
alpar@1
|
699 |
xassert(k == na);
|
alpar@1
|
700 |
/* find minimal-cost circulation in the resulting network */
|
alpar@1
|
701 |
ret = okalg(nv, na, tail, head, low, cap, cost, x, pi);
|
alpar@1
|
702 |
switch (ret)
|
alpar@1
|
703 |
{ case 0:
|
alpar@1
|
704 |
/* optimal circulation found */
|
alpar@1
|
705 |
ret = 0;
|
alpar@1
|
706 |
break;
|
alpar@1
|
707 |
case 1:
|
alpar@1
|
708 |
/* no feasible circulation exists */
|
alpar@1
|
709 |
ret = GLP_ENOPFS;
|
alpar@1
|
710 |
break;
|
alpar@1
|
711 |
case 2:
|
alpar@1
|
712 |
/* integer overflow occured */
|
alpar@1
|
713 |
ret = GLP_ERANGE;
|
alpar@1
|
714 |
goto done;
|
alpar@1
|
715 |
case 3:
|
alpar@1
|
716 |
/* optimality test failed (logic error) */
|
alpar@1
|
717 |
ret = GLP_EFAIL;
|
alpar@1
|
718 |
goto done;
|
alpar@1
|
719 |
default:
|
alpar@1
|
720 |
xassert(ret != ret);
|
alpar@1
|
721 |
}
|
alpar@1
|
722 |
/* store solution components */
|
alpar@1
|
723 |
/* (objective function = the total cost) */
|
alpar@1
|
724 |
if (sol != NULL)
|
alpar@1
|
725 |
{ temp = 0.0;
|
alpar@1
|
726 |
for (k = 1; k <= na; k++)
|
alpar@1
|
727 |
temp += (double)cost[k] * (double)x[k];
|
alpar@1
|
728 |
if (form != GLP_ASN_MIN) temp = - temp;
|
alpar@1
|
729 |
*sol = temp;
|
alpar@1
|
730 |
}
|
alpar@1
|
731 |
/* (arc flows) */
|
alpar@1
|
732 |
if (a_x >= 0)
|
alpar@1
|
733 |
{ k = 0;
|
alpar@1
|
734 |
for (i = 1; i <= G->nv; i++)
|
alpar@1
|
735 |
{ v = G->v[i];
|
alpar@1
|
736 |
for (a = v->out; a != NULL; a = a->t_next)
|
alpar@1
|
737 |
{ k++;
|
alpar@1
|
738 |
if (ret == 0)
|
alpar@1
|
739 |
xassert(x[k] == 0 || x[k] == 1);
|
alpar@1
|
740 |
memcpy((char *)a->data + a_x, &x[k], sizeof(int));
|
alpar@1
|
741 |
}
|
alpar@1
|
742 |
}
|
alpar@1
|
743 |
}
|
alpar@1
|
744 |
done: /* free working arrays */
|
alpar@1
|
745 |
xfree(tail);
|
alpar@1
|
746 |
xfree(head);
|
alpar@1
|
747 |
xfree(low);
|
alpar@1
|
748 |
xfree(cap);
|
alpar@1
|
749 |
xfree(cost);
|
alpar@1
|
750 |
xfree(x);
|
alpar@1
|
751 |
xfree(pi);
|
alpar@1
|
752 |
return ret;
|
alpar@1
|
753 |
}
|
alpar@1
|
754 |
|
alpar@1
|
755 |
/***********************************************************************
|
alpar@1
|
756 |
* NAME
|
alpar@1
|
757 |
*
|
alpar@1
|
758 |
* glp_asnprob_hall - find bipartite matching of maximum cardinality
|
alpar@1
|
759 |
*
|
alpar@1
|
760 |
* SYNOPSIS
|
alpar@1
|
761 |
*
|
alpar@1
|
762 |
* int glp_asnprob_hall(glp_graph *G, int v_set, int a_x);
|
alpar@1
|
763 |
*
|
alpar@1
|
764 |
* DESCRIPTION
|
alpar@1
|
765 |
*
|
alpar@1
|
766 |
* The routine glp_asnprob_hall finds a matching of maximal cardinality
|
alpar@1
|
767 |
* in the specified bipartite graph G. It uses a version of the Fortran
|
alpar@1
|
768 |
* routine MC21A developed by I.S.Duff [1], which implements Hall's
|
alpar@1
|
769 |
* algorithm [2].
|
alpar@1
|
770 |
*
|
alpar@1
|
771 |
* RETURNS
|
alpar@1
|
772 |
*
|
alpar@1
|
773 |
* The routine glp_asnprob_hall returns the cardinality of the matching
|
alpar@1
|
774 |
* found. However, if the specified graph is incorrect (as detected by
|
alpar@1
|
775 |
* the routine glp_check_asnprob), the routine returns negative value.
|
alpar@1
|
776 |
*
|
alpar@1
|
777 |
* REFERENCES
|
alpar@1
|
778 |
*
|
alpar@1
|
779 |
* 1. I.S.Duff, Algorithm 575: Permutations for zero-free diagonal, ACM
|
alpar@1
|
780 |
* Trans. on Math. Softw. 7 (1981), 387-390.
|
alpar@1
|
781 |
*
|
alpar@1
|
782 |
* 2. M.Hall, "An Algorithm for distinct representatives," Amer. Math.
|
alpar@1
|
783 |
* Monthly 63 (1956), 716-717. */
|
alpar@1
|
784 |
|
alpar@1
|
785 |
int glp_asnprob_hall(glp_graph *G, int v_set, int a_x)
|
alpar@1
|
786 |
{ glp_vertex *v;
|
alpar@1
|
787 |
glp_arc *a;
|
alpar@1
|
788 |
int card, i, k, loc, n, n1, n2, xij;
|
alpar@1
|
789 |
int *num, *icn, *ip, *lenr, *iperm, *pr, *arp, *cv, *out;
|
alpar@1
|
790 |
if (v_set >= 0 && v_set > G->v_size - (int)sizeof(int))
|
alpar@1
|
791 |
xerror("glp_asnprob_hall: v_set = %d; invalid offset\n",
|
alpar@1
|
792 |
v_set);
|
alpar@1
|
793 |
if (a_x >= 0 && a_x > G->a_size - (int)sizeof(int))
|
alpar@1
|
794 |
xerror("glp_asnprob_hall: a_x = %d; invalid offset\n", a_x);
|
alpar@1
|
795 |
if (glp_check_asnprob(G, v_set))
|
alpar@1
|
796 |
return -1;
|
alpar@1
|
797 |
/* determine the number of vertices in sets R and S and renumber
|
alpar@1
|
798 |
vertices in S which correspond to columns of the matrix; skip
|
alpar@1
|
799 |
all isolated vertices */
|
alpar@1
|
800 |
num = xcalloc(1+G->nv, sizeof(int));
|
alpar@1
|
801 |
n1 = n2 = 0;
|
alpar@1
|
802 |
for (i = 1; i <= G->nv; i++)
|
alpar@1
|
803 |
{ v = G->v[i];
|
alpar@1
|
804 |
if (v->in == NULL && v->out != NULL)
|
alpar@1
|
805 |
n1++, num[i] = 0; /* vertex in R */
|
alpar@1
|
806 |
else if (v->in != NULL && v->out == NULL)
|
alpar@1
|
807 |
n2++, num[i] = n2; /* vertex in S */
|
alpar@1
|
808 |
else
|
alpar@1
|
809 |
{ xassert(v->in == NULL && v->out == NULL);
|
alpar@1
|
810 |
num[i] = -1; /* isolated vertex */
|
alpar@1
|
811 |
}
|
alpar@1
|
812 |
}
|
alpar@1
|
813 |
/* the matrix must be square, thus, if it has more columns than
|
alpar@1
|
814 |
rows, extra rows will be just empty, and vice versa */
|
alpar@1
|
815 |
n = (n1 >= n2 ? n1 : n2);
|
alpar@1
|
816 |
/* allocate working arrays */
|
alpar@1
|
817 |
icn = xcalloc(1+G->na, sizeof(int));
|
alpar@1
|
818 |
ip = xcalloc(1+n, sizeof(int));
|
alpar@1
|
819 |
lenr = xcalloc(1+n, sizeof(int));
|
alpar@1
|
820 |
iperm = xcalloc(1+n, sizeof(int));
|
alpar@1
|
821 |
pr = xcalloc(1+n, sizeof(int));
|
alpar@1
|
822 |
arp = xcalloc(1+n, sizeof(int));
|
alpar@1
|
823 |
cv = xcalloc(1+n, sizeof(int));
|
alpar@1
|
824 |
out = xcalloc(1+n, sizeof(int));
|
alpar@1
|
825 |
/* build the adjacency matrix of the bipartite graph in row-wise
|
alpar@1
|
826 |
format (rows are vertices in R, columns are vertices in S) */
|
alpar@1
|
827 |
k = 0, loc = 1;
|
alpar@1
|
828 |
for (i = 1; i <= G->nv; i++)
|
alpar@1
|
829 |
{ if (num[i] != 0) continue;
|
alpar@1
|
830 |
/* vertex i in R */
|
alpar@1
|
831 |
ip[++k] = loc;
|
alpar@1
|
832 |
v = G->v[i];
|
alpar@1
|
833 |
for (a = v->out; a != NULL; a = a->t_next)
|
alpar@1
|
834 |
{ xassert(num[a->head->i] != 0);
|
alpar@1
|
835 |
icn[loc++] = num[a->head->i];
|
alpar@1
|
836 |
}
|
alpar@1
|
837 |
lenr[k] = loc - ip[k];
|
alpar@1
|
838 |
}
|
alpar@1
|
839 |
xassert(loc-1 == G->na);
|
alpar@1
|
840 |
/* make all extra rows empty (all extra columns are empty due to
|
alpar@1
|
841 |
the row-wise format used) */
|
alpar@1
|
842 |
for (k++; k <= n; k++)
|
alpar@1
|
843 |
ip[k] = loc, lenr[k] = 0;
|
alpar@1
|
844 |
/* find a row permutation that maximizes the number of non-zeros
|
alpar@1
|
845 |
on the main diagonal */
|
alpar@1
|
846 |
card = mc21a(n, icn, ip, lenr, iperm, pr, arp, cv, out);
|
alpar@1
|
847 |
#if 1 /* 18/II-2010 */
|
alpar@1
|
848 |
/* FIXED: if card = n, arp remains clobbered on exit */
|
alpar@1
|
849 |
for (i = 1; i <= n; i++)
|
alpar@1
|
850 |
arp[i] = 0;
|
alpar@1
|
851 |
for (i = 1; i <= card; i++)
|
alpar@1
|
852 |
{ k = iperm[i];
|
alpar@1
|
853 |
xassert(1 <= k && k <= n);
|
alpar@1
|
854 |
xassert(arp[k] == 0);
|
alpar@1
|
855 |
arp[k] = i;
|
alpar@1
|
856 |
}
|
alpar@1
|
857 |
#endif
|
alpar@1
|
858 |
/* store solution, if necessary */
|
alpar@1
|
859 |
if (a_x < 0) goto skip;
|
alpar@1
|
860 |
k = 0;
|
alpar@1
|
861 |
for (i = 1; i <= G->nv; i++)
|
alpar@1
|
862 |
{ if (num[i] != 0) continue;
|
alpar@1
|
863 |
/* vertex i in R */
|
alpar@1
|
864 |
k++;
|
alpar@1
|
865 |
v = G->v[i];
|
alpar@1
|
866 |
for (a = v->out; a != NULL; a = a->t_next)
|
alpar@1
|
867 |
{ /* arp[k] is the number of matched column or zero */
|
alpar@1
|
868 |
if (arp[k] == num[a->head->i])
|
alpar@1
|
869 |
{ xassert(arp[k] != 0);
|
alpar@1
|
870 |
xij = 1;
|
alpar@1
|
871 |
}
|
alpar@1
|
872 |
else
|
alpar@1
|
873 |
xij = 0;
|
alpar@1
|
874 |
memcpy((char *)a->data + a_x, &xij, sizeof(int));
|
alpar@1
|
875 |
}
|
alpar@1
|
876 |
}
|
alpar@1
|
877 |
skip: /* free working arrays */
|
alpar@1
|
878 |
xfree(num);
|
alpar@1
|
879 |
xfree(icn);
|
alpar@1
|
880 |
xfree(ip);
|
alpar@1
|
881 |
xfree(lenr);
|
alpar@1
|
882 |
xfree(iperm);
|
alpar@1
|
883 |
xfree(pr);
|
alpar@1
|
884 |
xfree(arp);
|
alpar@1
|
885 |
xfree(cv);
|
alpar@1
|
886 |
xfree(out);
|
alpar@1
|
887 |
return card;
|
alpar@1
|
888 |
}
|
alpar@1
|
889 |
|
alpar@1
|
890 |
/***********************************************************************
|
alpar@1
|
891 |
* NAME
|
alpar@1
|
892 |
*
|
alpar@1
|
893 |
* glp_cpp - solve critical path problem
|
alpar@1
|
894 |
*
|
alpar@1
|
895 |
* SYNOPSIS
|
alpar@1
|
896 |
*
|
alpar@1
|
897 |
* double glp_cpp(glp_graph *G, int v_t, int v_es, int v_ls);
|
alpar@1
|
898 |
*
|
alpar@1
|
899 |
* DESCRIPTION
|
alpar@1
|
900 |
*
|
alpar@1
|
901 |
* The routine glp_cpp solves the critical path problem represented in
|
alpar@1
|
902 |
* the form of the project network.
|
alpar@1
|
903 |
*
|
alpar@1
|
904 |
* The parameter G is a pointer to the graph object, which specifies
|
alpar@1
|
905 |
* the project network. This graph must be acyclic. Multiple arcs are
|
alpar@1
|
906 |
* allowed being considered as single arcs.
|
alpar@1
|
907 |
*
|
alpar@1
|
908 |
* The parameter v_t specifies an offset of the field of type double
|
alpar@1
|
909 |
* in the vertex data block, which contains time t[i] >= 0 needed to
|
alpar@1
|
910 |
* perform corresponding job j. If v_t < 0, it is assumed that t[i] = 1
|
alpar@1
|
911 |
* for all jobs.
|
alpar@1
|
912 |
*
|
alpar@1
|
913 |
* The parameter v_es specifies an offset of the field of type double
|
alpar@1
|
914 |
* in the vertex data block, to which the routine stores earliest start
|
alpar@1
|
915 |
* time for corresponding job. If v_es < 0, this time is not stored.
|
alpar@1
|
916 |
*
|
alpar@1
|
917 |
* The parameter v_ls specifies an offset of the field of type double
|
alpar@1
|
918 |
* in the vertex data block, to which the routine stores latest start
|
alpar@1
|
919 |
* time for corresponding job. If v_ls < 0, this time is not stored.
|
alpar@1
|
920 |
*
|
alpar@1
|
921 |
* RETURNS
|
alpar@1
|
922 |
*
|
alpar@1
|
923 |
* The routine glp_cpp returns the minimal project duration, that is,
|
alpar@1
|
924 |
* minimal time needed to perform all jobs in the project. */
|
alpar@1
|
925 |
|
alpar@1
|
926 |
static void sorting(glp_graph *G, int list[]);
|
alpar@1
|
927 |
|
alpar@1
|
928 |
double glp_cpp(glp_graph *G, int v_t, int v_es, int v_ls)
|
alpar@1
|
929 |
{ glp_vertex *v;
|
alpar@1
|
930 |
glp_arc *a;
|
alpar@1
|
931 |
int i, j, k, nv, *list;
|
alpar@1
|
932 |
double temp, total, *t, *es, *ls;
|
alpar@1
|
933 |
if (v_t >= 0 && v_t > G->v_size - (int)sizeof(double))
|
alpar@1
|
934 |
xerror("glp_cpp: v_t = %d; invalid offset\n", v_t);
|
alpar@1
|
935 |
if (v_es >= 0 && v_es > G->v_size - (int)sizeof(double))
|
alpar@1
|
936 |
xerror("glp_cpp: v_es = %d; invalid offset\n", v_es);
|
alpar@1
|
937 |
if (v_ls >= 0 && v_ls > G->v_size - (int)sizeof(double))
|
alpar@1
|
938 |
xerror("glp_cpp: v_ls = %d; invalid offset\n", v_ls);
|
alpar@1
|
939 |
nv = G->nv;
|
alpar@1
|
940 |
if (nv == 0)
|
alpar@1
|
941 |
{ total = 0.0;
|
alpar@1
|
942 |
goto done;
|
alpar@1
|
943 |
}
|
alpar@1
|
944 |
/* allocate working arrays */
|
alpar@1
|
945 |
t = xcalloc(1+nv, sizeof(double));
|
alpar@1
|
946 |
es = xcalloc(1+nv, sizeof(double));
|
alpar@1
|
947 |
ls = xcalloc(1+nv, sizeof(double));
|
alpar@1
|
948 |
list = xcalloc(1+nv, sizeof(int));
|
alpar@1
|
949 |
/* retrieve job times */
|
alpar@1
|
950 |
for (i = 1; i <= nv; i++)
|
alpar@1
|
951 |
{ v = G->v[i];
|
alpar@1
|
952 |
if (v_t >= 0)
|
alpar@1
|
953 |
{ memcpy(&t[i], (char *)v->data + v_t, sizeof(double));
|
alpar@1
|
954 |
if (t[i] < 0.0)
|
alpar@1
|
955 |
xerror("glp_cpp: t[%d] = %g; invalid time\n", i, t[i]);
|
alpar@1
|
956 |
}
|
alpar@1
|
957 |
else
|
alpar@1
|
958 |
t[i] = 1.0;
|
alpar@1
|
959 |
}
|
alpar@1
|
960 |
/* perform topological sorting to determine the list of nodes
|
alpar@1
|
961 |
(jobs) such that if list[k] = i and list[kk] = j and there
|
alpar@1
|
962 |
exists arc (i->j), then k < kk */
|
alpar@1
|
963 |
sorting(G, list);
|
alpar@1
|
964 |
/* FORWARD PASS */
|
alpar@1
|
965 |
/* determine earliest start times */
|
alpar@1
|
966 |
for (k = 1; k <= nv; k++)
|
alpar@1
|
967 |
{ j = list[k];
|
alpar@1
|
968 |
es[j] = 0.0;
|
alpar@1
|
969 |
for (a = G->v[j]->in; a != NULL; a = a->h_next)
|
alpar@1
|
970 |
{ i = a->tail->i;
|
alpar@1
|
971 |
/* there exists arc (i->j) in the project network */
|
alpar@1
|
972 |
temp = es[i] + t[i];
|
alpar@1
|
973 |
if (es[j] < temp) es[j] = temp;
|
alpar@1
|
974 |
}
|
alpar@1
|
975 |
}
|
alpar@1
|
976 |
/* determine the minimal project duration */
|
alpar@1
|
977 |
total = 0.0;
|
alpar@1
|
978 |
for (i = 1; i <= nv; i++)
|
alpar@1
|
979 |
{ temp = es[i] + t[i];
|
alpar@1
|
980 |
if (total < temp) total = temp;
|
alpar@1
|
981 |
}
|
alpar@1
|
982 |
/* BACKWARD PASS */
|
alpar@1
|
983 |
/* determine latest start times */
|
alpar@1
|
984 |
for (k = nv; k >= 1; k--)
|
alpar@1
|
985 |
{ i = list[k];
|
alpar@1
|
986 |
ls[i] = total - t[i];
|
alpar@1
|
987 |
for (a = G->v[i]->out; a != NULL; a = a->t_next)
|
alpar@1
|
988 |
{ j = a->head->i;
|
alpar@1
|
989 |
/* there exists arc (i->j) in the project network */
|
alpar@1
|
990 |
temp = ls[j] - t[i];
|
alpar@1
|
991 |
if (ls[i] > temp) ls[i] = temp;
|
alpar@1
|
992 |
}
|
alpar@1
|
993 |
/* avoid possible round-off errors */
|
alpar@1
|
994 |
if (ls[i] < es[i]) ls[i] = es[i];
|
alpar@1
|
995 |
}
|
alpar@1
|
996 |
/* store results, if necessary */
|
alpar@1
|
997 |
if (v_es >= 0)
|
alpar@1
|
998 |
{ for (i = 1; i <= nv; i++)
|
alpar@1
|
999 |
{ v = G->v[i];
|
alpar@1
|
1000 |
memcpy((char *)v->data + v_es, &es[i], sizeof(double));
|
alpar@1
|
1001 |
}
|
alpar@1
|
1002 |
}
|
alpar@1
|
1003 |
if (v_ls >= 0)
|
alpar@1
|
1004 |
{ for (i = 1; i <= nv; i++)
|
alpar@1
|
1005 |
{ v = G->v[i];
|
alpar@1
|
1006 |
memcpy((char *)v->data + v_ls, &ls[i], sizeof(double));
|
alpar@1
|
1007 |
}
|
alpar@1
|
1008 |
}
|
alpar@1
|
1009 |
/* free working arrays */
|
alpar@1
|
1010 |
xfree(t);
|
alpar@1
|
1011 |
xfree(es);
|
alpar@1
|
1012 |
xfree(ls);
|
alpar@1
|
1013 |
xfree(list);
|
alpar@1
|
1014 |
done: return total;
|
alpar@1
|
1015 |
}
|
alpar@1
|
1016 |
|
alpar@1
|
1017 |
static void sorting(glp_graph *G, int list[])
|
alpar@1
|
1018 |
{ /* perform topological sorting to determine the list of nodes
|
alpar@1
|
1019 |
(jobs) such that if list[k] = i and list[kk] = j and there
|
alpar@1
|
1020 |
exists arc (i->j), then k < kk */
|
alpar@1
|
1021 |
int i, k, nv, v_size, *num;
|
alpar@1
|
1022 |
void **save;
|
alpar@1
|
1023 |
nv = G->nv;
|
alpar@1
|
1024 |
v_size = G->v_size;
|
alpar@1
|
1025 |
save = xcalloc(1+nv, sizeof(void *));
|
alpar@1
|
1026 |
num = xcalloc(1+nv, sizeof(int));
|
alpar@1
|
1027 |
G->v_size = sizeof(int);
|
alpar@1
|
1028 |
for (i = 1; i <= nv; i++)
|
alpar@1
|
1029 |
{ save[i] = G->v[i]->data;
|
alpar@1
|
1030 |
G->v[i]->data = &num[i];
|
alpar@1
|
1031 |
list[i] = 0;
|
alpar@1
|
1032 |
}
|
alpar@1
|
1033 |
if (glp_top_sort(G, 0) != 0)
|
alpar@1
|
1034 |
xerror("glp_cpp: project network is not acyclic\n");
|
alpar@1
|
1035 |
G->v_size = v_size;
|
alpar@1
|
1036 |
for (i = 1; i <= nv; i++)
|
alpar@1
|
1037 |
{ G->v[i]->data = save[i];
|
alpar@1
|
1038 |
k = num[i];
|
alpar@1
|
1039 |
xassert(1 <= k && k <= nv);
|
alpar@1
|
1040 |
xassert(list[k] == 0);
|
alpar@1
|
1041 |
list[k] = i;
|
alpar@1
|
1042 |
}
|
alpar@1
|
1043 |
xfree(save);
|
alpar@1
|
1044 |
xfree(num);
|
alpar@1
|
1045 |
return;
|
alpar@1
|
1046 |
}
|
alpar@1
|
1047 |
|
alpar@1
|
1048 |
/* eof */
|