1 /* glpapi17.c (flow network problems) */
3 /***********************************************************************
4 * This code is part of GLPK (GNU Linear Programming Kit).
6 * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
7 * 2009, 2010 Andrew Makhorin, Department for Applied Informatics,
8 * Moscow Aviation Institute, Moscow, Russia. All rights reserved.
9 * E-mail: <mao@gnu.org>.
11 * GLPK is free software: you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
16 * GLPK is distributed in the hope that it will be useful, but WITHOUT
17 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
19 * License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with GLPK. If not, see <http://www.gnu.org/licenses/>.
23 ***********************************************************************/
28 /***********************************************************************
31 * glp_mincost_lp - convert minimum cost flow problem to LP
35 * void glp_mincost_lp(glp_prob *lp, glp_graph *G, int names,
36 * int v_rhs, int a_low, int a_cap, int a_cost);
40 * The routine glp_mincost_lp builds an LP problem, which corresponds
41 * to the minimum cost flow problem on the specified network G. */
43 void glp_mincost_lp(glp_prob *lp, glp_graph *G, int names, int v_rhs,
44 int a_low, int a_cap, int a_cost)
47 int i, j, type, ind[1+2];
48 double rhs, low, cap, cost, val[1+2];
49 if (!(names == GLP_ON || names == GLP_OFF))
50 xerror("glp_mincost_lp: names = %d; invalid parameter\n",
52 if (v_rhs >= 0 && v_rhs > G->v_size - (int)sizeof(double))
53 xerror("glp_mincost_lp: v_rhs = %d; invalid offset\n", v_rhs);
54 if (a_low >= 0 && a_low > G->a_size - (int)sizeof(double))
55 xerror("glp_mincost_lp: a_low = %d; invalid offset\n", a_low);
56 if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double))
57 xerror("glp_mincost_lp: a_cap = %d; invalid offset\n", a_cap);
58 if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double))
59 xerror("glp_mincost_lp: a_cost = %d; invalid offset\n", a_cost)
62 if (names) glp_set_prob_name(lp, G->name);
63 if (G->nv > 0) glp_add_rows(lp, G->nv);
64 for (i = 1; i <= G->nv; i++)
66 if (names) glp_set_row_name(lp, i, v->name);
68 memcpy(&rhs, (char *)v->data + v_rhs, sizeof(double));
71 glp_set_row_bnds(lp, i, GLP_FX, rhs, rhs);
73 if (G->na > 0) glp_add_cols(lp, G->na);
74 for (i = 1, j = 0; i <= G->nv; i++)
76 for (a = v->out; a != NULL; a = a->t_next)
80 sprintf(name, "x[%d,%d]", a->tail->i, a->head->i);
81 xassert(strlen(name) < sizeof(name));
82 glp_set_col_name(lp, j, name);
84 if (a->tail->i != a->head->i)
85 { ind[1] = a->tail->i, val[1] = +1.0;
86 ind[2] = a->head->i, val[2] = -1.0;
87 glp_set_mat_col(lp, j, 2, ind, val);
90 memcpy(&low, (char *)a->data + a_low, sizeof(double));
94 memcpy(&cap, (char *)a->data + a_cap, sizeof(double));
103 glp_set_col_bnds(lp, j, type, low, cap);
105 memcpy(&cost, (char *)a->data + a_cost, sizeof(double));
108 glp_set_obj_coef(lp, j, cost);
115 /**********************************************************************/
117 int glp_mincost_okalg(glp_graph *G, int v_rhs, int a_low, int a_cap,
118 int a_cost, double *sol, int a_x, int v_pi)
119 { /* find minimum-cost flow with out-of-kilter algorithm */
122 int nv, na, i, k, s, t, *tail, *head, *low, *cap, *cost, *x, *pi,
125 if (v_rhs >= 0 && v_rhs > G->v_size - (int)sizeof(double))
126 xerror("glp_mincost_okalg: v_rhs = %d; invalid offset\n",
128 if (a_low >= 0 && a_low > G->a_size - (int)sizeof(double))
129 xerror("glp_mincost_okalg: a_low = %d; invalid offset\n",
131 if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double))
132 xerror("glp_mincost_okalg: a_cap = %d; invalid offset\n",
134 if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double))
135 xerror("glp_mincost_okalg: a_cost = %d; invalid offset\n",
137 if (a_x >= 0 && a_x > G->a_size - (int)sizeof(double))
138 xerror("glp_mincost_okalg: a_x = %d; invalid offset\n", a_x);
139 if (v_pi >= 0 && v_pi > G->v_size - (int)sizeof(double))
140 xerror("glp_mincost_okalg: v_pi = %d; invalid offset\n", v_pi);
141 /* s is artificial source node */
143 /* t is artificial sink node */
145 /* nv is the total number of nodes in the resulting network */
147 /* na is the total number of arcs in the resulting network */
149 for (i = 1; i <= G->nv; i++)
152 memcpy(&temp, (char *)v->data + v_rhs, sizeof(double));
155 if (temp != 0.0) na++;
157 /* allocate working arrays */
158 tail = xcalloc(1+na, sizeof(int));
159 head = xcalloc(1+na, sizeof(int));
160 low = xcalloc(1+na, sizeof(int));
161 cap = xcalloc(1+na, sizeof(int));
162 cost = xcalloc(1+na, sizeof(int));
163 x = xcalloc(1+na, sizeof(int));
164 pi = xcalloc(1+nv, sizeof(int));
165 /* construct the resulting network */
167 /* (original arcs) */
168 for (i = 1; i <= G->nv; i++)
170 for (a = v->out; a != NULL; a = a->t_next)
172 tail[k] = a->tail->i;
173 head[k] = a->head->i;
174 if (tail[k] == head[k])
179 memcpy(&temp, (char *)a->data + a_low, sizeof(double));
182 if (!(0.0 <= temp && temp <= (double)INT_MAX &&
183 temp == floor(temp)))
189 memcpy(&temp, (char *)a->data + a_cap, sizeof(double));
192 if (!((double)low[k] <= temp && temp <= (double)INT_MAX &&
193 temp == floor(temp)))
199 memcpy(&temp, (char *)a->data + a_cost, sizeof(double));
202 if (!(fabs(temp) <= (double)INT_MAX && temp == floor(temp)))
209 /* (artificial arcs) */
211 for (i = 1; i <= G->nv; i++)
214 memcpy(&temp, (char *)v->data + v_rhs, sizeof(double));
217 if (!(fabs(temp) <= (double)INT_MAX && temp == floor(temp)))
222 { /* artificial arc from s to original source i */
226 low[k] = cap[k] = (int)(+temp); /* supply */
231 { /* artificial arc from original sink i to t */
235 low[k] = cap[k] = (int)(-temp); /* demand */
239 /* (feedback arc from t to s) */
244 if (sum > (double)INT_MAX)
248 low[k] = cap[k] = (int)sum; /* total supply/demand */
250 /* find minimal-cost circulation in the resulting network */
251 ret = okalg(nv, na, tail, head, low, cap, cost, x, pi);
254 /* optimal circulation found */
258 /* no feasible circulation exists */
262 /* integer overflow occured */
266 /* optimality test failed (logic error) */
272 /* store solution components */
273 /* (objective function = the total cost) */
276 for (k = 1; k <= na; k++)
277 temp += (double)cost[k] * (double)x[k];
283 for (i = 1; i <= G->nv; i++)
285 for (a = v->out; a != NULL; a = a->t_next)
286 { temp = (double)x[++k];
287 memcpy((char *)a->data + a_x, &temp, sizeof(double));
291 /* (node potentials = Lagrange multipliers) */
293 { for (i = 1; i <= G->nv; i++)
295 temp = - (double)pi[i];
296 memcpy((char *)v->data + v_pi, &temp, sizeof(double));
299 done: /* free working arrays */
310 /***********************************************************************
313 * glp_maxflow_lp - convert maximum flow problem to LP
317 * void glp_maxflow_lp(glp_prob *lp, glp_graph *G, int names, int s,
322 * The routine glp_maxflow_lp builds an LP problem, which corresponds
323 * to the maximum flow problem on the specified network G. */
325 void glp_maxflow_lp(glp_prob *lp, glp_graph *G, int names, int s,
329 int i, j, type, ind[1+2];
330 double cap, val[1+2];
331 if (!(names == GLP_ON || names == GLP_OFF))
332 xerror("glp_maxflow_lp: names = %d; invalid parameter\n",
334 if (!(1 <= s && s <= G->nv))
335 xerror("glp_maxflow_lp: s = %d; source node number out of rang"
337 if (!(1 <= t && t <= G->nv))
338 xerror("glp_maxflow_lp: t = %d: sink node number out of range "
341 xerror("glp_maxflow_lp: s = t = %d; source and sink nodes must"
342 " be distinct\n", s);
343 if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double))
344 xerror("glp_maxflow_lp: a_cap = %d; invalid offset\n", a_cap);
346 if (names) glp_set_prob_name(lp, G->name);
347 glp_set_obj_dir(lp, GLP_MAX);
348 glp_add_rows(lp, G->nv);
349 for (i = 1; i <= G->nv; i++)
351 if (names) glp_set_row_name(lp, i, v->name);
358 glp_set_row_bnds(lp, i, type, 0.0, 0.0);
360 if (G->na > 0) glp_add_cols(lp, G->na);
361 for (i = 1, j = 0; i <= G->nv; i++)
363 for (a = v->out; a != NULL; a = a->t_next)
367 sprintf(name, "x[%d,%d]", a->tail->i, a->head->i);
368 xassert(strlen(name) < sizeof(name));
369 glp_set_col_name(lp, j, name);
371 if (a->tail->i != a->head->i)
372 { ind[1] = a->tail->i, val[1] = +1.0;
373 ind[2] = a->head->i, val[2] = -1.0;
374 glp_set_mat_col(lp, j, 2, ind, val);
377 memcpy(&cap, (char *)a->data + a_cap, sizeof(double));
386 glp_set_col_bnds(lp, j, type, 0.0, cap);
388 glp_set_obj_coef(lp, j, +1.0);
389 else if (a->head->i == s)
390 glp_set_obj_coef(lp, j, -1.0);
397 int glp_maxflow_ffalg(glp_graph *G, int s, int t, int a_cap,
398 double *sol, int a_x, int v_cut)
399 { /* find maximal flow with Ford-Fulkerson algorithm */
402 int nv, na, i, k, flag, *tail, *head, *cap, *x, ret;
405 if (!(1 <= s && s <= G->nv))
406 xerror("glp_maxflow_ffalg: s = %d; source node number out of r"
408 if (!(1 <= t && t <= G->nv))
409 xerror("glp_maxflow_ffalg: t = %d: sink node number out of ran"
412 xerror("glp_maxflow_ffalg: s = t = %d; source and sink nodes m"
413 "ust be distinct\n", s);
414 if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double))
415 xerror("glp_maxflow_ffalg: a_cap = %d; invalid offset\n",
417 if (v_cut >= 0 && v_cut > G->v_size - (int)sizeof(int))
418 xerror("glp_maxflow_ffalg: v_cut = %d; invalid offset\n",
420 /* allocate working arrays */
423 tail = xcalloc(1+na, sizeof(int));
424 head = xcalloc(1+na, sizeof(int));
425 cap = xcalloc(1+na, sizeof(int));
426 x = xcalloc(1+na, sizeof(int));
430 cut = xcalloc(1+nv, sizeof(char));
431 /* copy the flow network */
433 for (i = 1; i <= G->nv; i++)
435 for (a = v->out; a != NULL; a = a->t_next)
437 tail[k] = a->tail->i;
438 head[k] = a->head->i;
439 if (tail[k] == head[k])
444 memcpy(&temp, (char *)a->data + a_cap, sizeof(double));
447 if (!(0.0 <= temp && temp <= (double)INT_MAX &&
448 temp == floor(temp)))
456 /* find maximal flow in the flow network */
457 ffalg(nv, na, tail, head, s, t, cap, x, cut);
459 /* store solution components */
460 /* (objective function = total flow through the network) */
463 for (k = 1; k <= na; k++)
465 temp += (double)x[k];
466 else if (head[k] == s)
467 temp -= (double)x[k];
474 for (i = 1; i <= G->nv; i++)
476 for (a = v->out; a != NULL; a = a->t_next)
477 { temp = (double)x[++k];
478 memcpy((char *)a->data + a_x, &temp, sizeof(double));
484 { for (i = 1; i <= G->nv; i++)
487 memcpy((char *)v->data + v_cut, &flag, sizeof(int));
490 done: /* free working arrays */
495 if (cut != NULL) xfree(cut);
499 /***********************************************************************
502 * glp_check_asnprob - check correctness of assignment problem data
506 * int glp_check_asnprob(glp_graph *G, int v_set);
510 * If the specified assignment problem data are correct, the routine
511 * glp_check_asnprob returns zero, otherwise, non-zero. */
513 int glp_check_asnprob(glp_graph *G, int v_set)
516 if (v_set >= 0 && v_set > G->v_size - (int)sizeof(int))
517 xerror("glp_check_asnprob: v_set = %d; invalid offset\n",
519 for (i = 1; i <= G->nv; i++)
522 { memcpy(&k, (char *)v->data + v_set, sizeof(int));
530 { if (v->out != NULL)
541 { if (v->in != NULL && v->out != NULL)
550 /***********************************************************************
553 * glp_asnprob_lp - convert assignment problem to LP
557 * int glp_asnprob_lp(glp_prob *P, int form, glp_graph *G, int names,
558 * int v_set, int a_cost);
562 * The routine glp_asnprob_lp builds an LP problem, which corresponds
563 * to the assignment problem on the specified graph G.
567 * If the LP problem has been successfully built, the routine returns
568 * zero, otherwise, non-zero. */
570 int glp_asnprob_lp(glp_prob *P, int form, glp_graph *G, int names,
571 int v_set, int a_cost)
574 int i, j, ret, ind[1+2];
575 double cost, val[1+2];
576 if (!(form == GLP_ASN_MIN || form == GLP_ASN_MAX ||
577 form == GLP_ASN_MMP))
578 xerror("glp_asnprob_lp: form = %d; invalid parameter\n",
580 if (!(names == GLP_ON || names == GLP_OFF))
581 xerror("glp_asnprob_lp: names = %d; invalid parameter\n",
583 if (v_set >= 0 && v_set > G->v_size - (int)sizeof(int))
584 xerror("glp_asnprob_lp: v_set = %d; invalid offset\n",
586 if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double))
587 xerror("glp_asnprob_lp: a_cost = %d; invalid offset\n",
589 ret = glp_check_asnprob(G, v_set);
590 if (ret != 0) goto done;
592 if (names) glp_set_prob_name(P, G->name);
593 glp_set_obj_dir(P, form == GLP_ASN_MIN ? GLP_MIN : GLP_MAX);
594 if (G->nv > 0) glp_add_rows(P, G->nv);
595 for (i = 1; i <= G->nv; i++)
597 if (names) glp_set_row_name(P, i, v->name);
598 glp_set_row_bnds(P, i, form == GLP_ASN_MMP ? GLP_UP : GLP_FX,
601 if (G->na > 0) glp_add_cols(P, G->na);
602 for (i = 1, j = 0; i <= G->nv; i++)
604 for (a = v->out; a != NULL; a = a->t_next)
608 sprintf(name, "x[%d,%d]", a->tail->i, a->head->i);
609 xassert(strlen(name) < sizeof(name));
610 glp_set_col_name(P, j, name);
612 ind[1] = a->tail->i, val[1] = +1.0;
613 ind[2] = a->head->i, val[2] = +1.0;
614 glp_set_mat_col(P, j, 2, ind, val);
615 glp_set_col_bnds(P, j, GLP_DB, 0.0, 1.0);
617 memcpy(&cost, (char *)a->data + a_cost, sizeof(double));
620 glp_set_obj_coef(P, j, cost);
627 /**********************************************************************/
629 int glp_asnprob_okalg(int form, glp_graph *G, int v_set, int a_cost,
630 double *sol, int a_x)
631 { /* solve assignment problem with out-of-kilter algorithm */
634 int nv, na, i, k, *tail, *head, *low, *cap, *cost, *x, *pi, ret;
636 if (!(form == GLP_ASN_MIN || form == GLP_ASN_MAX ||
637 form == GLP_ASN_MMP))
638 xerror("glp_asnprob_okalg: form = %d; invalid parameter\n",
640 if (v_set >= 0 && v_set > G->v_size - (int)sizeof(int))
641 xerror("glp_asnprob_okalg: v_set = %d; invalid offset\n",
643 if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double))
644 xerror("glp_asnprob_okalg: a_cost = %d; invalid offset\n",
646 if (a_x >= 0 && a_x > G->a_size - (int)sizeof(int))
647 xerror("glp_asnprob_okalg: a_x = %d; invalid offset\n", a_x);
648 if (glp_check_asnprob(G, v_set))
650 /* nv is the total number of nodes in the resulting network */
652 /* na is the total number of arcs in the resulting network */
654 /* allocate working arrays */
655 tail = xcalloc(1+na, sizeof(int));
656 head = xcalloc(1+na, sizeof(int));
657 low = xcalloc(1+na, sizeof(int));
658 cap = xcalloc(1+na, sizeof(int));
659 cost = xcalloc(1+na, sizeof(int));
660 x = xcalloc(1+na, sizeof(int));
661 pi = xcalloc(1+nv, sizeof(int));
662 /* construct the resulting network */
664 /* (original arcs) */
665 for (i = 1; i <= G->nv; i++)
667 for (a = v->out; a != NULL; a = a->t_next)
669 tail[k] = a->tail->i;
670 head[k] = a->head->i;
674 memcpy(&temp, (char *)a->data + a_cost, sizeof(double));
677 if (!(fabs(temp) <= (double)INT_MAX && temp == floor(temp)))
682 if (form != GLP_ASN_MIN) cost[k] = - cost[k];
685 /* (artificial arcs) */
686 for (i = 1; i <= G->nv; i++)
690 tail[k] = i, head[k] = nv;
691 else if (v->in == NULL)
692 tail[k] = nv, head[k] = i;
695 low[k] = (form == GLP_ASN_MMP ? 0 : 1);
700 /* find minimal-cost circulation in the resulting network */
701 ret = okalg(nv, na, tail, head, low, cap, cost, x, pi);
704 /* optimal circulation found */
708 /* no feasible circulation exists */
712 /* integer overflow occured */
716 /* optimality test failed (logic error) */
722 /* store solution components */
723 /* (objective function = the total cost) */
726 for (k = 1; k <= na; k++)
727 temp += (double)cost[k] * (double)x[k];
728 if (form != GLP_ASN_MIN) temp = - temp;
734 for (i = 1; i <= G->nv; i++)
736 for (a = v->out; a != NULL; a = a->t_next)
739 xassert(x[k] == 0 || x[k] == 1);
740 memcpy((char *)a->data + a_x, &x[k], sizeof(int));
744 done: /* free working arrays */
755 /***********************************************************************
758 * glp_asnprob_hall - find bipartite matching of maximum cardinality
762 * int glp_asnprob_hall(glp_graph *G, int v_set, int a_x);
766 * The routine glp_asnprob_hall finds a matching of maximal cardinality
767 * in the specified bipartite graph G. It uses a version of the Fortran
768 * routine MC21A developed by I.S.Duff [1], which implements Hall's
773 * The routine glp_asnprob_hall returns the cardinality of the matching
774 * found. However, if the specified graph is incorrect (as detected by
775 * the routine glp_check_asnprob), the routine returns negative value.
779 * 1. I.S.Duff, Algorithm 575: Permutations for zero-free diagonal, ACM
780 * Trans. on Math. Softw. 7 (1981), 387-390.
782 * 2. M.Hall, "An Algorithm for distinct representatives," Amer. Math.
783 * Monthly 63 (1956), 716-717. */
785 int glp_asnprob_hall(glp_graph *G, int v_set, int a_x)
788 int card, i, k, loc, n, n1, n2, xij;
789 int *num, *icn, *ip, *lenr, *iperm, *pr, *arp, *cv, *out;
790 if (v_set >= 0 && v_set > G->v_size - (int)sizeof(int))
791 xerror("glp_asnprob_hall: v_set = %d; invalid offset\n",
793 if (a_x >= 0 && a_x > G->a_size - (int)sizeof(int))
794 xerror("glp_asnprob_hall: a_x = %d; invalid offset\n", a_x);
795 if (glp_check_asnprob(G, v_set))
797 /* determine the number of vertices in sets R and S and renumber
798 vertices in S which correspond to columns of the matrix; skip
799 all isolated vertices */
800 num = xcalloc(1+G->nv, sizeof(int));
802 for (i = 1; i <= G->nv; i++)
804 if (v->in == NULL && v->out != NULL)
805 n1++, num[i] = 0; /* vertex in R */
806 else if (v->in != NULL && v->out == NULL)
807 n2++, num[i] = n2; /* vertex in S */
809 { xassert(v->in == NULL && v->out == NULL);
810 num[i] = -1; /* isolated vertex */
813 /* the matrix must be square, thus, if it has more columns than
814 rows, extra rows will be just empty, and vice versa */
815 n = (n1 >= n2 ? n1 : n2);
816 /* allocate working arrays */
817 icn = xcalloc(1+G->na, sizeof(int));
818 ip = xcalloc(1+n, sizeof(int));
819 lenr = xcalloc(1+n, sizeof(int));
820 iperm = xcalloc(1+n, sizeof(int));
821 pr = xcalloc(1+n, sizeof(int));
822 arp = xcalloc(1+n, sizeof(int));
823 cv = xcalloc(1+n, sizeof(int));
824 out = xcalloc(1+n, sizeof(int));
825 /* build the adjacency matrix of the bipartite graph in row-wise
826 format (rows are vertices in R, columns are vertices in S) */
828 for (i = 1; i <= G->nv; i++)
829 { if (num[i] != 0) continue;
833 for (a = v->out; a != NULL; a = a->t_next)
834 { xassert(num[a->head->i] != 0);
835 icn[loc++] = num[a->head->i];
837 lenr[k] = loc - ip[k];
839 xassert(loc-1 == G->na);
840 /* make all extra rows empty (all extra columns are empty due to
841 the row-wise format used) */
842 for (k++; k <= n; k++)
843 ip[k] = loc, lenr[k] = 0;
844 /* find a row permutation that maximizes the number of non-zeros
845 on the main diagonal */
846 card = mc21a(n, icn, ip, lenr, iperm, pr, arp, cv, out);
847 #if 1 /* 18/II-2010 */
848 /* FIXED: if card = n, arp remains clobbered on exit */
849 for (i = 1; i <= n; i++)
851 for (i = 1; i <= card; i++)
853 xassert(1 <= k && k <= n);
854 xassert(arp[k] == 0);
858 /* store solution, if necessary */
859 if (a_x < 0) goto skip;
861 for (i = 1; i <= G->nv; i++)
862 { if (num[i] != 0) continue;
866 for (a = v->out; a != NULL; a = a->t_next)
867 { /* arp[k] is the number of matched column or zero */
868 if (arp[k] == num[a->head->i])
869 { xassert(arp[k] != 0);
874 memcpy((char *)a->data + a_x, &xij, sizeof(int));
877 skip: /* free working arrays */
890 /***********************************************************************
893 * glp_cpp - solve critical path problem
897 * double glp_cpp(glp_graph *G, int v_t, int v_es, int v_ls);
901 * The routine glp_cpp solves the critical path problem represented in
902 * the form of the project network.
904 * The parameter G is a pointer to the graph object, which specifies
905 * the project network. This graph must be acyclic. Multiple arcs are
906 * allowed being considered as single arcs.
908 * The parameter v_t specifies an offset of the field of type double
909 * in the vertex data block, which contains time t[i] >= 0 needed to
910 * perform corresponding job j. If v_t < 0, it is assumed that t[i] = 1
913 * The parameter v_es specifies an offset of the field of type double
914 * in the vertex data block, to which the routine stores earliest start
915 * time for corresponding job. If v_es < 0, this time is not stored.
917 * The parameter v_ls specifies an offset of the field of type double
918 * in the vertex data block, to which the routine stores latest start
919 * time for corresponding job. If v_ls < 0, this time is not stored.
923 * The routine glp_cpp returns the minimal project duration, that is,
924 * minimal time needed to perform all jobs in the project. */
926 static void sorting(glp_graph *G, int list[]);
928 double glp_cpp(glp_graph *G, int v_t, int v_es, int v_ls)
931 int i, j, k, nv, *list;
932 double temp, total, *t, *es, *ls;
933 if (v_t >= 0 && v_t > G->v_size - (int)sizeof(double))
934 xerror("glp_cpp: v_t = %d; invalid offset\n", v_t);
935 if (v_es >= 0 && v_es > G->v_size - (int)sizeof(double))
936 xerror("glp_cpp: v_es = %d; invalid offset\n", v_es);
937 if (v_ls >= 0 && v_ls > G->v_size - (int)sizeof(double))
938 xerror("glp_cpp: v_ls = %d; invalid offset\n", v_ls);
944 /* allocate working arrays */
945 t = xcalloc(1+nv, sizeof(double));
946 es = xcalloc(1+nv, sizeof(double));
947 ls = xcalloc(1+nv, sizeof(double));
948 list = xcalloc(1+nv, sizeof(int));
949 /* retrieve job times */
950 for (i = 1; i <= nv; i++)
953 { memcpy(&t[i], (char *)v->data + v_t, sizeof(double));
955 xerror("glp_cpp: t[%d] = %g; invalid time\n", i, t[i]);
960 /* perform topological sorting to determine the list of nodes
961 (jobs) such that if list[k] = i and list[kk] = j and there
962 exists arc (i->j), then k < kk */
965 /* determine earliest start times */
966 for (k = 1; k <= nv; k++)
969 for (a = G->v[j]->in; a != NULL; a = a->h_next)
971 /* there exists arc (i->j) in the project network */
973 if (es[j] < temp) es[j] = temp;
976 /* determine the minimal project duration */
978 for (i = 1; i <= nv; i++)
979 { temp = es[i] + t[i];
980 if (total < temp) total = temp;
983 /* determine latest start times */
984 for (k = nv; k >= 1; k--)
986 ls[i] = total - t[i];
987 for (a = G->v[i]->out; a != NULL; a = a->t_next)
989 /* there exists arc (i->j) in the project network */
991 if (ls[i] > temp) ls[i] = temp;
993 /* avoid possible round-off errors */
994 if (ls[i] < es[i]) ls[i] = es[i];
996 /* store results, if necessary */
998 { for (i = 1; i <= nv; i++)
1000 memcpy((char *)v->data + v_es, &es[i], sizeof(double));
1004 { for (i = 1; i <= nv; i++)
1006 memcpy((char *)v->data + v_ls, &ls[i], sizeof(double));
1009 /* free working arrays */
1017 static void sorting(glp_graph *G, int list[])
1018 { /* perform topological sorting to determine the list of nodes
1019 (jobs) such that if list[k] = i and list[kk] = j and there
1020 exists arc (i->j), then k < kk */
1021 int i, k, nv, v_size, *num;
1025 save = xcalloc(1+nv, sizeof(void *));
1026 num = xcalloc(1+nv, sizeof(int));
1027 G->v_size = sizeof(int);
1028 for (i = 1; i <= nv; i++)
1029 { save[i] = G->v[i]->data;
1030 G->v[i]->data = &num[i];
1033 if (glp_top_sort(G, 0) != 0)
1034 xerror("glp_cpp: project network is not acyclic\n");
1036 for (i = 1; i <= nv; i++)
1037 { G->v[i]->data = save[i];
1039 xassert(1 <= k && k <= nv);
1040 xassert(list[k] == 0);