alpar@9: /* glpapi17.c (flow network problems) */ alpar@9: alpar@9: /*********************************************************************** alpar@9: * This code is part of GLPK (GNU Linear Programming Kit). alpar@9: * alpar@9: * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, alpar@9: * 2009, 2010, 2011 Andrew Makhorin, Department for Applied Informatics, alpar@9: * Moscow Aviation Institute, Moscow, Russia. All rights reserved. alpar@9: * E-mail: . alpar@9: * alpar@9: * GLPK is free software: you can redistribute it and/or modify it alpar@9: * under the terms of the GNU General Public License as published by alpar@9: * the Free Software Foundation, either version 3 of the License, or alpar@9: * (at your option) any later version. alpar@9: * alpar@9: * GLPK is distributed in the hope that it will be useful, but WITHOUT alpar@9: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY alpar@9: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public alpar@9: * License for more details. alpar@9: * alpar@9: * You should have received a copy of the GNU General Public License alpar@9: * along with GLPK. If not, see . alpar@9: ***********************************************************************/ alpar@9: alpar@9: #include "glpapi.h" alpar@9: #include "glpnet.h" alpar@9: alpar@9: /*********************************************************************** alpar@9: * NAME alpar@9: * alpar@9: * glp_mincost_lp - convert minimum cost flow problem to LP alpar@9: * alpar@9: * SYNOPSIS alpar@9: * alpar@9: * void glp_mincost_lp(glp_prob *lp, glp_graph *G, int names, alpar@9: * int v_rhs, int a_low, int a_cap, int a_cost); alpar@9: * alpar@9: * DESCRIPTION alpar@9: * alpar@9: * The routine glp_mincost_lp builds an LP problem, which corresponds alpar@9: * to the minimum cost flow problem on the specified network G. */ alpar@9: alpar@9: void glp_mincost_lp(glp_prob *lp, glp_graph *G, int names, int v_rhs, alpar@9: int a_low, int a_cap, int a_cost) alpar@9: { glp_vertex *v; alpar@9: glp_arc *a; alpar@9: int i, j, type, ind[1+2]; alpar@9: double rhs, low, cap, cost, val[1+2]; alpar@9: if (!(names == GLP_ON || names == GLP_OFF)) alpar@9: xerror("glp_mincost_lp: names = %d; invalid parameter\n", alpar@9: names); alpar@9: if (v_rhs >= 0 && v_rhs > G->v_size - (int)sizeof(double)) alpar@9: xerror("glp_mincost_lp: v_rhs = %d; invalid offset\n", v_rhs); alpar@9: if (a_low >= 0 && a_low > G->a_size - (int)sizeof(double)) alpar@9: xerror("glp_mincost_lp: a_low = %d; invalid offset\n", a_low); alpar@9: if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double)) alpar@9: xerror("glp_mincost_lp: a_cap = %d; invalid offset\n", a_cap); alpar@9: if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double)) alpar@9: xerror("glp_mincost_lp: a_cost = %d; invalid offset\n", a_cost) alpar@9: ; alpar@9: glp_erase_prob(lp); alpar@9: if (names) glp_set_prob_name(lp, G->name); alpar@9: if (G->nv > 0) glp_add_rows(lp, G->nv); alpar@9: for (i = 1; i <= G->nv; i++) alpar@9: { v = G->v[i]; alpar@9: if (names) glp_set_row_name(lp, i, v->name); alpar@9: if (v_rhs >= 0) alpar@9: memcpy(&rhs, (char *)v->data + v_rhs, sizeof(double)); alpar@9: else alpar@9: rhs = 0.0; alpar@9: glp_set_row_bnds(lp, i, GLP_FX, rhs, rhs); alpar@9: } alpar@9: if (G->na > 0) glp_add_cols(lp, G->na); alpar@9: for (i = 1, j = 0; i <= G->nv; i++) alpar@9: { v = G->v[i]; alpar@9: for (a = v->out; a != NULL; a = a->t_next) alpar@9: { j++; alpar@9: if (names) alpar@9: { char name[50+1]; alpar@9: sprintf(name, "x[%d,%d]", a->tail->i, a->head->i); alpar@9: xassert(strlen(name) < sizeof(name)); alpar@9: glp_set_col_name(lp, j, name); alpar@9: } alpar@9: if (a->tail->i != a->head->i) alpar@9: { ind[1] = a->tail->i, val[1] = +1.0; alpar@9: ind[2] = a->head->i, val[2] = -1.0; alpar@9: glp_set_mat_col(lp, j, 2, ind, val); alpar@9: } alpar@9: if (a_low >= 0) alpar@9: memcpy(&low, (char *)a->data + a_low, sizeof(double)); alpar@9: else alpar@9: low = 0.0; alpar@9: if (a_cap >= 0) alpar@9: memcpy(&cap, (char *)a->data + a_cap, sizeof(double)); alpar@9: else alpar@9: cap = 1.0; alpar@9: if (cap == DBL_MAX) alpar@9: type = GLP_LO; alpar@9: else if (low != cap) alpar@9: type = GLP_DB; alpar@9: else alpar@9: type = GLP_FX; alpar@9: glp_set_col_bnds(lp, j, type, low, cap); alpar@9: if (a_cost >= 0) alpar@9: memcpy(&cost, (char *)a->data + a_cost, sizeof(double)); alpar@9: else alpar@9: cost = 0.0; alpar@9: glp_set_obj_coef(lp, j, cost); alpar@9: } alpar@9: } alpar@9: xassert(j == G->na); alpar@9: return; alpar@9: } alpar@9: alpar@9: /**********************************************************************/ alpar@9: alpar@9: int glp_mincost_okalg(glp_graph *G, int v_rhs, int a_low, int a_cap, alpar@9: int a_cost, double *sol, int a_x, int v_pi) alpar@9: { /* find minimum-cost flow with out-of-kilter algorithm */ alpar@9: glp_vertex *v; alpar@9: glp_arc *a; alpar@9: int nv, na, i, k, s, t, *tail, *head, *low, *cap, *cost, *x, *pi, alpar@9: ret; alpar@9: double sum, temp; alpar@9: if (v_rhs >= 0 && v_rhs > G->v_size - (int)sizeof(double)) alpar@9: xerror("glp_mincost_okalg: v_rhs = %d; invalid offset\n", alpar@9: v_rhs); alpar@9: if (a_low >= 0 && a_low > G->a_size - (int)sizeof(double)) alpar@9: xerror("glp_mincost_okalg: a_low = %d; invalid offset\n", alpar@9: a_low); alpar@9: if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double)) alpar@9: xerror("glp_mincost_okalg: a_cap = %d; invalid offset\n", alpar@9: a_cap); alpar@9: if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double)) alpar@9: xerror("glp_mincost_okalg: a_cost = %d; invalid offset\n", alpar@9: a_cost); alpar@9: if (a_x >= 0 && a_x > G->a_size - (int)sizeof(double)) alpar@9: xerror("glp_mincost_okalg: a_x = %d; invalid offset\n", a_x); alpar@9: if (v_pi >= 0 && v_pi > G->v_size - (int)sizeof(double)) alpar@9: xerror("glp_mincost_okalg: v_pi = %d; invalid offset\n", v_pi); alpar@9: /* s is artificial source node */ alpar@9: s = G->nv + 1; alpar@9: /* t is artificial sink node */ alpar@9: t = s + 1; alpar@9: /* nv is the total number of nodes in the resulting network */ alpar@9: nv = t; alpar@9: /* na is the total number of arcs in the resulting network */ alpar@9: na = G->na + 1; alpar@9: for (i = 1; i <= G->nv; i++) alpar@9: { v = G->v[i]; alpar@9: if (v_rhs >= 0) alpar@9: memcpy(&temp, (char *)v->data + v_rhs, sizeof(double)); alpar@9: else alpar@9: temp = 0.0; alpar@9: if (temp != 0.0) na++; alpar@9: } alpar@9: /* allocate working arrays */ alpar@9: tail = xcalloc(1+na, sizeof(int)); alpar@9: head = xcalloc(1+na, sizeof(int)); alpar@9: low = xcalloc(1+na, sizeof(int)); alpar@9: cap = xcalloc(1+na, sizeof(int)); alpar@9: cost = xcalloc(1+na, sizeof(int)); alpar@9: x = xcalloc(1+na, sizeof(int)); alpar@9: pi = xcalloc(1+nv, sizeof(int)); alpar@9: /* construct the resulting network */ alpar@9: k = 0; alpar@9: /* (original arcs) */ alpar@9: for (i = 1; i <= G->nv; i++) alpar@9: { v = G->v[i]; alpar@9: for (a = v->out; a != NULL; a = a->t_next) alpar@9: { k++; alpar@9: tail[k] = a->tail->i; alpar@9: head[k] = a->head->i; alpar@9: if (tail[k] == head[k]) alpar@9: { ret = GLP_EDATA; alpar@9: goto done; alpar@9: } alpar@9: if (a_low >= 0) alpar@9: memcpy(&temp, (char *)a->data + a_low, sizeof(double)); alpar@9: else alpar@9: temp = 0.0; alpar@9: if (!(0.0 <= temp && temp <= (double)INT_MAX && alpar@9: temp == floor(temp))) alpar@9: { ret = GLP_EDATA; alpar@9: goto done; alpar@9: } alpar@9: low[k] = (int)temp; alpar@9: if (a_cap >= 0) alpar@9: memcpy(&temp, (char *)a->data + a_cap, sizeof(double)); alpar@9: else alpar@9: temp = 1.0; alpar@9: if (!((double)low[k] <= temp && temp <= (double)INT_MAX && alpar@9: temp == floor(temp))) alpar@9: { ret = GLP_EDATA; alpar@9: goto done; alpar@9: } alpar@9: cap[k] = (int)temp; alpar@9: if (a_cost >= 0) alpar@9: memcpy(&temp, (char *)a->data + a_cost, sizeof(double)); alpar@9: else alpar@9: temp = 0.0; alpar@9: if (!(fabs(temp) <= (double)INT_MAX && temp == floor(temp))) alpar@9: { ret = GLP_EDATA; alpar@9: goto done; alpar@9: } alpar@9: cost[k] = (int)temp; alpar@9: } alpar@9: } alpar@9: /* (artificial arcs) */ alpar@9: sum = 0.0; alpar@9: for (i = 1; i <= G->nv; i++) alpar@9: { v = G->v[i]; alpar@9: if (v_rhs >= 0) alpar@9: memcpy(&temp, (char *)v->data + v_rhs, sizeof(double)); alpar@9: else alpar@9: temp = 0.0; alpar@9: if (!(fabs(temp) <= (double)INT_MAX && temp == floor(temp))) alpar@9: { ret = GLP_EDATA; alpar@9: goto done; alpar@9: } alpar@9: if (temp > 0.0) alpar@9: { /* artificial arc from s to original source i */ alpar@9: k++; alpar@9: tail[k] = s; alpar@9: head[k] = i; alpar@9: low[k] = cap[k] = (int)(+temp); /* supply */ alpar@9: cost[k] = 0; alpar@9: sum += (double)temp; alpar@9: } alpar@9: else if (temp < 0.0) alpar@9: { /* artificial arc from original sink i to t */ alpar@9: k++; alpar@9: tail[k] = i; alpar@9: head[k] = t; alpar@9: low[k] = cap[k] = (int)(-temp); /* demand */ alpar@9: cost[k] = 0; alpar@9: } alpar@9: } alpar@9: /* (feedback arc from t to s) */ alpar@9: k++; alpar@9: xassert(k == na); alpar@9: tail[k] = t; alpar@9: head[k] = s; alpar@9: if (sum > (double)INT_MAX) alpar@9: { ret = GLP_EDATA; alpar@9: goto done; alpar@9: } alpar@9: low[k] = cap[k] = (int)sum; /* total supply/demand */ alpar@9: cost[k] = 0; alpar@9: /* find minimal-cost circulation in the resulting network */ alpar@9: ret = okalg(nv, na, tail, head, low, cap, cost, x, pi); alpar@9: switch (ret) alpar@9: { case 0: alpar@9: /* optimal circulation found */ alpar@9: ret = 0; alpar@9: break; alpar@9: case 1: alpar@9: /* no feasible circulation exists */ alpar@9: ret = GLP_ENOPFS; alpar@9: break; alpar@9: case 2: alpar@9: /* integer overflow occured */ alpar@9: ret = GLP_ERANGE; alpar@9: goto done; alpar@9: case 3: alpar@9: /* optimality test failed (logic error) */ alpar@9: ret = GLP_EFAIL; alpar@9: goto done; alpar@9: default: alpar@9: xassert(ret != ret); alpar@9: } alpar@9: /* store solution components */ alpar@9: /* (objective function = the total cost) */ alpar@9: if (sol != NULL) alpar@9: { temp = 0.0; alpar@9: for (k = 1; k <= na; k++) alpar@9: temp += (double)cost[k] * (double)x[k]; alpar@9: *sol = temp; alpar@9: } alpar@9: /* (arc flows) */ alpar@9: if (a_x >= 0) alpar@9: { k = 0; alpar@9: for (i = 1; i <= G->nv; i++) alpar@9: { v = G->v[i]; alpar@9: for (a = v->out; a != NULL; a = a->t_next) alpar@9: { temp = (double)x[++k]; alpar@9: memcpy((char *)a->data + a_x, &temp, sizeof(double)); alpar@9: } alpar@9: } alpar@9: } alpar@9: /* (node potentials = Lagrange multipliers) */ alpar@9: if (v_pi >= 0) alpar@9: { for (i = 1; i <= G->nv; i++) alpar@9: { v = G->v[i]; alpar@9: temp = - (double)pi[i]; alpar@9: memcpy((char *)v->data + v_pi, &temp, sizeof(double)); alpar@9: } alpar@9: } alpar@9: done: /* free working arrays */ alpar@9: xfree(tail); alpar@9: xfree(head); alpar@9: xfree(low); alpar@9: xfree(cap); alpar@9: xfree(cost); alpar@9: xfree(x); alpar@9: xfree(pi); alpar@9: return ret; alpar@9: } alpar@9: alpar@9: /*********************************************************************** alpar@9: * NAME alpar@9: * alpar@9: * glp_maxflow_lp - convert maximum flow problem to LP alpar@9: * alpar@9: * SYNOPSIS alpar@9: * alpar@9: * void glp_maxflow_lp(glp_prob *lp, glp_graph *G, int names, int s, alpar@9: * int t, int a_cap); alpar@9: * alpar@9: * DESCRIPTION alpar@9: * alpar@9: * The routine glp_maxflow_lp builds an LP problem, which corresponds alpar@9: * to the maximum flow problem on the specified network G. */ alpar@9: alpar@9: void glp_maxflow_lp(glp_prob *lp, glp_graph *G, int names, int s, alpar@9: int t, int a_cap) alpar@9: { glp_vertex *v; alpar@9: glp_arc *a; alpar@9: int i, j, type, ind[1+2]; alpar@9: double cap, val[1+2]; alpar@9: if (!(names == GLP_ON || names == GLP_OFF)) alpar@9: xerror("glp_maxflow_lp: names = %d; invalid parameter\n", alpar@9: names); alpar@9: if (!(1 <= s && s <= G->nv)) alpar@9: xerror("glp_maxflow_lp: s = %d; source node number out of rang" alpar@9: "e\n", s); alpar@9: if (!(1 <= t && t <= G->nv)) alpar@9: xerror("glp_maxflow_lp: t = %d: sink node number out of range " alpar@9: "\n", t); alpar@9: if (s == t) alpar@9: xerror("glp_maxflow_lp: s = t = %d; source and sink nodes must" alpar@9: " be distinct\n", s); alpar@9: if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double)) alpar@9: xerror("glp_maxflow_lp: a_cap = %d; invalid offset\n", a_cap); alpar@9: glp_erase_prob(lp); alpar@9: if (names) glp_set_prob_name(lp, G->name); alpar@9: glp_set_obj_dir(lp, GLP_MAX); alpar@9: glp_add_rows(lp, G->nv); alpar@9: for (i = 1; i <= G->nv; i++) alpar@9: { v = G->v[i]; alpar@9: if (names) glp_set_row_name(lp, i, v->name); alpar@9: if (i == s) alpar@9: type = GLP_LO; alpar@9: else if (i == t) alpar@9: type = GLP_UP; alpar@9: else alpar@9: type = GLP_FX; alpar@9: glp_set_row_bnds(lp, i, type, 0.0, 0.0); alpar@9: } alpar@9: if (G->na > 0) glp_add_cols(lp, G->na); alpar@9: for (i = 1, j = 0; i <= G->nv; i++) alpar@9: { v = G->v[i]; alpar@9: for (a = v->out; a != NULL; a = a->t_next) alpar@9: { j++; alpar@9: if (names) alpar@9: { char name[50+1]; alpar@9: sprintf(name, "x[%d,%d]", a->tail->i, a->head->i); alpar@9: xassert(strlen(name) < sizeof(name)); alpar@9: glp_set_col_name(lp, j, name); alpar@9: } alpar@9: if (a->tail->i != a->head->i) alpar@9: { ind[1] = a->tail->i, val[1] = +1.0; alpar@9: ind[2] = a->head->i, val[2] = -1.0; alpar@9: glp_set_mat_col(lp, j, 2, ind, val); alpar@9: } alpar@9: if (a_cap >= 0) alpar@9: memcpy(&cap, (char *)a->data + a_cap, sizeof(double)); alpar@9: else alpar@9: cap = 1.0; alpar@9: if (cap == DBL_MAX) alpar@9: type = GLP_LO; alpar@9: else if (cap != 0.0) alpar@9: type = GLP_DB; alpar@9: else alpar@9: type = GLP_FX; alpar@9: glp_set_col_bnds(lp, j, type, 0.0, cap); alpar@9: if (a->tail->i == s) alpar@9: glp_set_obj_coef(lp, j, +1.0); alpar@9: else if (a->head->i == s) alpar@9: glp_set_obj_coef(lp, j, -1.0); alpar@9: } alpar@9: } alpar@9: xassert(j == G->na); alpar@9: return; alpar@9: } alpar@9: alpar@9: int glp_maxflow_ffalg(glp_graph *G, int s, int t, int a_cap, alpar@9: double *sol, int a_x, int v_cut) alpar@9: { /* find maximal flow with Ford-Fulkerson algorithm */ alpar@9: glp_vertex *v; alpar@9: glp_arc *a; alpar@9: int nv, na, i, k, flag, *tail, *head, *cap, *x, ret; alpar@9: char *cut; alpar@9: double temp; alpar@9: if (!(1 <= s && s <= G->nv)) alpar@9: xerror("glp_maxflow_ffalg: s = %d; source node number out of r" alpar@9: "ange\n", s); alpar@9: if (!(1 <= t && t <= G->nv)) alpar@9: xerror("glp_maxflow_ffalg: t = %d: sink node number out of ran" alpar@9: "ge\n", t); alpar@9: if (s == t) alpar@9: xerror("glp_maxflow_ffalg: s = t = %d; source and sink nodes m" alpar@9: "ust be distinct\n", s); alpar@9: if (a_cap >= 0 && a_cap > G->a_size - (int)sizeof(double)) alpar@9: xerror("glp_maxflow_ffalg: a_cap = %d; invalid offset\n", alpar@9: a_cap); alpar@9: if (v_cut >= 0 && v_cut > G->v_size - (int)sizeof(int)) alpar@9: xerror("glp_maxflow_ffalg: v_cut = %d; invalid offset\n", alpar@9: v_cut); alpar@9: /* allocate working arrays */ alpar@9: nv = G->nv; alpar@9: na = G->na; alpar@9: tail = xcalloc(1+na, sizeof(int)); alpar@9: head = xcalloc(1+na, sizeof(int)); alpar@9: cap = xcalloc(1+na, sizeof(int)); alpar@9: x = xcalloc(1+na, sizeof(int)); alpar@9: if (v_cut < 0) alpar@9: cut = NULL; alpar@9: else alpar@9: cut = xcalloc(1+nv, sizeof(char)); alpar@9: /* copy the flow network */ alpar@9: k = 0; alpar@9: for (i = 1; i <= G->nv; i++) alpar@9: { v = G->v[i]; alpar@9: for (a = v->out; a != NULL; a = a->t_next) alpar@9: { k++; alpar@9: tail[k] = a->tail->i; alpar@9: head[k] = a->head->i; alpar@9: if (tail[k] == head[k]) alpar@9: { ret = GLP_EDATA; alpar@9: goto done; alpar@9: } alpar@9: if (a_cap >= 0) alpar@9: memcpy(&temp, (char *)a->data + a_cap, sizeof(double)); alpar@9: else alpar@9: temp = 1.0; alpar@9: if (!(0.0 <= temp && temp <= (double)INT_MAX && alpar@9: temp == floor(temp))) alpar@9: { ret = GLP_EDATA; alpar@9: goto done; alpar@9: } alpar@9: cap[k] = (int)temp; alpar@9: } alpar@9: } alpar@9: xassert(k == na); alpar@9: /* find maximal flow in the flow network */ alpar@9: ffalg(nv, na, tail, head, s, t, cap, x, cut); alpar@9: ret = 0; alpar@9: /* store solution components */ alpar@9: /* (objective function = total flow through the network) */ alpar@9: if (sol != NULL) alpar@9: { temp = 0.0; alpar@9: for (k = 1; k <= na; k++) alpar@9: { if (tail[k] == s) alpar@9: temp += (double)x[k]; alpar@9: else if (head[k] == s) alpar@9: temp -= (double)x[k]; alpar@9: } alpar@9: *sol = temp; alpar@9: } alpar@9: /* (arc flows) */ alpar@9: if (a_x >= 0) alpar@9: { k = 0; alpar@9: for (i = 1; i <= G->nv; i++) alpar@9: { v = G->v[i]; alpar@9: for (a = v->out; a != NULL; a = a->t_next) alpar@9: { temp = (double)x[++k]; alpar@9: memcpy((char *)a->data + a_x, &temp, sizeof(double)); alpar@9: } alpar@9: } alpar@9: } alpar@9: /* (node flags) */ alpar@9: if (v_cut >= 0) alpar@9: { for (i = 1; i <= G->nv; i++) alpar@9: { v = G->v[i]; alpar@9: flag = cut[i]; alpar@9: memcpy((char *)v->data + v_cut, &flag, sizeof(int)); alpar@9: } alpar@9: } alpar@9: done: /* free working arrays */ alpar@9: xfree(tail); alpar@9: xfree(head); alpar@9: xfree(cap); alpar@9: xfree(x); alpar@9: if (cut != NULL) xfree(cut); alpar@9: return ret; alpar@9: } alpar@9: alpar@9: /*********************************************************************** alpar@9: * NAME alpar@9: * alpar@9: * glp_check_asnprob - check correctness of assignment problem data alpar@9: * alpar@9: * SYNOPSIS alpar@9: * alpar@9: * int glp_check_asnprob(glp_graph *G, int v_set); alpar@9: * alpar@9: * RETURNS alpar@9: * alpar@9: * If the specified assignment problem data are correct, the routine alpar@9: * glp_check_asnprob returns zero, otherwise, non-zero. */ alpar@9: alpar@9: int glp_check_asnprob(glp_graph *G, int v_set) alpar@9: { glp_vertex *v; alpar@9: int i, k, ret = 0; alpar@9: if (v_set >= 0 && v_set > G->v_size - (int)sizeof(int)) alpar@9: xerror("glp_check_asnprob: v_set = %d; invalid offset\n", alpar@9: v_set); alpar@9: for (i = 1; i <= G->nv; i++) alpar@9: { v = G->v[i]; alpar@9: if (v_set >= 0) alpar@9: { memcpy(&k, (char *)v->data + v_set, sizeof(int)); alpar@9: if (k == 0) alpar@9: { if (v->in != NULL) alpar@9: { ret = 1; alpar@9: break; alpar@9: } alpar@9: } alpar@9: else if (k == 1) alpar@9: { if (v->out != NULL) alpar@9: { ret = 2; alpar@9: break; alpar@9: } alpar@9: } alpar@9: else alpar@9: { ret = 3; alpar@9: break; alpar@9: } alpar@9: } alpar@9: else alpar@9: { if (v->in != NULL && v->out != NULL) alpar@9: { ret = 4; alpar@9: break; alpar@9: } alpar@9: } alpar@9: } alpar@9: return ret; alpar@9: } alpar@9: alpar@9: /*********************************************************************** alpar@9: * NAME alpar@9: * alpar@9: * glp_asnprob_lp - convert assignment problem to LP alpar@9: * alpar@9: * SYNOPSIS alpar@9: * alpar@9: * int glp_asnprob_lp(glp_prob *P, int form, glp_graph *G, int names, alpar@9: * int v_set, int a_cost); alpar@9: * alpar@9: * DESCRIPTION alpar@9: * alpar@9: * The routine glp_asnprob_lp builds an LP problem, which corresponds alpar@9: * to the assignment problem on the specified graph G. alpar@9: * alpar@9: * RETURNS alpar@9: * alpar@9: * If the LP problem has been successfully built, the routine returns alpar@9: * zero, otherwise, non-zero. */ alpar@9: alpar@9: int glp_asnprob_lp(glp_prob *P, int form, glp_graph *G, int names, alpar@9: int v_set, int a_cost) alpar@9: { glp_vertex *v; alpar@9: glp_arc *a; alpar@9: int i, j, ret, ind[1+2]; alpar@9: double cost, val[1+2]; alpar@9: if (!(form == GLP_ASN_MIN || form == GLP_ASN_MAX || alpar@9: form == GLP_ASN_MMP)) alpar@9: xerror("glp_asnprob_lp: form = %d; invalid parameter\n", alpar@9: form); alpar@9: if (!(names == GLP_ON || names == GLP_OFF)) alpar@9: xerror("glp_asnprob_lp: names = %d; invalid parameter\n", alpar@9: names); alpar@9: if (v_set >= 0 && v_set > G->v_size - (int)sizeof(int)) alpar@9: xerror("glp_asnprob_lp: v_set = %d; invalid offset\n", alpar@9: v_set); alpar@9: if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double)) alpar@9: xerror("glp_asnprob_lp: a_cost = %d; invalid offset\n", alpar@9: a_cost); alpar@9: ret = glp_check_asnprob(G, v_set); alpar@9: if (ret != 0) goto done; alpar@9: glp_erase_prob(P); alpar@9: if (names) glp_set_prob_name(P, G->name); alpar@9: glp_set_obj_dir(P, form == GLP_ASN_MIN ? GLP_MIN : GLP_MAX); alpar@9: if (G->nv > 0) glp_add_rows(P, G->nv); alpar@9: for (i = 1; i <= G->nv; i++) alpar@9: { v = G->v[i]; alpar@9: if (names) glp_set_row_name(P, i, v->name); alpar@9: glp_set_row_bnds(P, i, form == GLP_ASN_MMP ? GLP_UP : GLP_FX, alpar@9: 1.0, 1.0); alpar@9: } alpar@9: if (G->na > 0) glp_add_cols(P, G->na); alpar@9: for (i = 1, j = 0; i <= G->nv; i++) alpar@9: { v = G->v[i]; alpar@9: for (a = v->out; a != NULL; a = a->t_next) alpar@9: { j++; alpar@9: if (names) alpar@9: { char name[50+1]; alpar@9: sprintf(name, "x[%d,%d]", a->tail->i, a->head->i); alpar@9: xassert(strlen(name) < sizeof(name)); alpar@9: glp_set_col_name(P, j, name); alpar@9: } alpar@9: ind[1] = a->tail->i, val[1] = +1.0; alpar@9: ind[2] = a->head->i, val[2] = +1.0; alpar@9: glp_set_mat_col(P, j, 2, ind, val); alpar@9: glp_set_col_bnds(P, j, GLP_DB, 0.0, 1.0); alpar@9: if (a_cost >= 0) alpar@9: memcpy(&cost, (char *)a->data + a_cost, sizeof(double)); alpar@9: else alpar@9: cost = 1.0; alpar@9: glp_set_obj_coef(P, j, cost); alpar@9: } alpar@9: } alpar@9: xassert(j == G->na); alpar@9: done: return ret; alpar@9: } alpar@9: alpar@9: /**********************************************************************/ alpar@9: alpar@9: int glp_asnprob_okalg(int form, glp_graph *G, int v_set, int a_cost, alpar@9: double *sol, int a_x) alpar@9: { /* solve assignment problem with out-of-kilter algorithm */ alpar@9: glp_vertex *v; alpar@9: glp_arc *a; alpar@9: int nv, na, i, k, *tail, *head, *low, *cap, *cost, *x, *pi, ret; alpar@9: double temp; alpar@9: if (!(form == GLP_ASN_MIN || form == GLP_ASN_MAX || alpar@9: form == GLP_ASN_MMP)) alpar@9: xerror("glp_asnprob_okalg: form = %d; invalid parameter\n", alpar@9: form); alpar@9: if (v_set >= 0 && v_set > G->v_size - (int)sizeof(int)) alpar@9: xerror("glp_asnprob_okalg: v_set = %d; invalid offset\n", alpar@9: v_set); alpar@9: if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double)) alpar@9: xerror("glp_asnprob_okalg: a_cost = %d; invalid offset\n", alpar@9: a_cost); alpar@9: if (a_x >= 0 && a_x > G->a_size - (int)sizeof(int)) alpar@9: xerror("glp_asnprob_okalg: a_x = %d; invalid offset\n", a_x); alpar@9: if (glp_check_asnprob(G, v_set)) alpar@9: return GLP_EDATA; alpar@9: /* nv is the total number of nodes in the resulting network */ alpar@9: nv = G->nv + 1; alpar@9: /* na is the total number of arcs in the resulting network */ alpar@9: na = G->na + G->nv; alpar@9: /* allocate working arrays */ alpar@9: tail = xcalloc(1+na, sizeof(int)); alpar@9: head = xcalloc(1+na, sizeof(int)); alpar@9: low = xcalloc(1+na, sizeof(int)); alpar@9: cap = xcalloc(1+na, sizeof(int)); alpar@9: cost = xcalloc(1+na, sizeof(int)); alpar@9: x = xcalloc(1+na, sizeof(int)); alpar@9: pi = xcalloc(1+nv, sizeof(int)); alpar@9: /* construct the resulting network */ alpar@9: k = 0; alpar@9: /* (original arcs) */ alpar@9: for (i = 1; i <= G->nv; i++) alpar@9: { v = G->v[i]; alpar@9: for (a = v->out; a != NULL; a = a->t_next) alpar@9: { k++; alpar@9: tail[k] = a->tail->i; alpar@9: head[k] = a->head->i; alpar@9: low[k] = 0; alpar@9: cap[k] = 1; alpar@9: if (a_cost >= 0) alpar@9: memcpy(&temp, (char *)a->data + a_cost, sizeof(double)); alpar@9: else alpar@9: temp = 1.0; alpar@9: if (!(fabs(temp) <= (double)INT_MAX && temp == floor(temp))) alpar@9: { ret = GLP_EDATA; alpar@9: goto done; alpar@9: } alpar@9: cost[k] = (int)temp; alpar@9: if (form != GLP_ASN_MIN) cost[k] = - cost[k]; alpar@9: } alpar@9: } alpar@9: /* (artificial arcs) */ alpar@9: for (i = 1; i <= G->nv; i++) alpar@9: { v = G->v[i]; alpar@9: k++; alpar@9: if (v->out == NULL) alpar@9: tail[k] = i, head[k] = nv; alpar@9: else if (v->in == NULL) alpar@9: tail[k] = nv, head[k] = i; alpar@9: else alpar@9: xassert(v != v); alpar@9: low[k] = (form == GLP_ASN_MMP ? 0 : 1); alpar@9: cap[k] = 1; alpar@9: cost[k] = 0; alpar@9: } alpar@9: xassert(k == na); alpar@9: /* find minimal-cost circulation in the resulting network */ alpar@9: ret = okalg(nv, na, tail, head, low, cap, cost, x, pi); alpar@9: switch (ret) alpar@9: { case 0: alpar@9: /* optimal circulation found */ alpar@9: ret = 0; alpar@9: break; alpar@9: case 1: alpar@9: /* no feasible circulation exists */ alpar@9: ret = GLP_ENOPFS; alpar@9: break; alpar@9: case 2: alpar@9: /* integer overflow occured */ alpar@9: ret = GLP_ERANGE; alpar@9: goto done; alpar@9: case 3: alpar@9: /* optimality test failed (logic error) */ alpar@9: ret = GLP_EFAIL; alpar@9: goto done; alpar@9: default: alpar@9: xassert(ret != ret); alpar@9: } alpar@9: /* store solution components */ alpar@9: /* (objective function = the total cost) */ alpar@9: if (sol != NULL) alpar@9: { temp = 0.0; alpar@9: for (k = 1; k <= na; k++) alpar@9: temp += (double)cost[k] * (double)x[k]; alpar@9: if (form != GLP_ASN_MIN) temp = - temp; alpar@9: *sol = temp; alpar@9: } alpar@9: /* (arc flows) */ alpar@9: if (a_x >= 0) alpar@9: { k = 0; alpar@9: for (i = 1; i <= G->nv; i++) alpar@9: { v = G->v[i]; alpar@9: for (a = v->out; a != NULL; a = a->t_next) alpar@9: { k++; alpar@9: if (ret == 0) alpar@9: xassert(x[k] == 0 || x[k] == 1); alpar@9: memcpy((char *)a->data + a_x, &x[k], sizeof(int)); alpar@9: } alpar@9: } alpar@9: } alpar@9: done: /* free working arrays */ alpar@9: xfree(tail); alpar@9: xfree(head); alpar@9: xfree(low); alpar@9: xfree(cap); alpar@9: xfree(cost); alpar@9: xfree(x); alpar@9: xfree(pi); alpar@9: return ret; alpar@9: } alpar@9: alpar@9: /*********************************************************************** alpar@9: * NAME alpar@9: * alpar@9: * glp_asnprob_hall - find bipartite matching of maximum cardinality alpar@9: * alpar@9: * SYNOPSIS alpar@9: * alpar@9: * int glp_asnprob_hall(glp_graph *G, int v_set, int a_x); alpar@9: * alpar@9: * DESCRIPTION alpar@9: * alpar@9: * The routine glp_asnprob_hall finds a matching of maximal cardinality alpar@9: * in the specified bipartite graph G. It uses a version of the Fortran alpar@9: * routine MC21A developed by I.S.Duff [1], which implements Hall's alpar@9: * algorithm [2]. alpar@9: * alpar@9: * RETURNS alpar@9: * alpar@9: * The routine glp_asnprob_hall returns the cardinality of the matching alpar@9: * found. However, if the specified graph is incorrect (as detected by alpar@9: * the routine glp_check_asnprob), the routine returns negative value. alpar@9: * alpar@9: * REFERENCES alpar@9: * alpar@9: * 1. I.S.Duff, Algorithm 575: Permutations for zero-free diagonal, ACM alpar@9: * Trans. on Math. Softw. 7 (1981), 387-390. alpar@9: * alpar@9: * 2. M.Hall, "An Algorithm for distinct representatives," Amer. Math. alpar@9: * Monthly 63 (1956), 716-717. */ alpar@9: alpar@9: int glp_asnprob_hall(glp_graph *G, int v_set, int a_x) alpar@9: { glp_vertex *v; alpar@9: glp_arc *a; alpar@9: int card, i, k, loc, n, n1, n2, xij; alpar@9: int *num, *icn, *ip, *lenr, *iperm, *pr, *arp, *cv, *out; alpar@9: if (v_set >= 0 && v_set > G->v_size - (int)sizeof(int)) alpar@9: xerror("glp_asnprob_hall: v_set = %d; invalid offset\n", alpar@9: v_set); alpar@9: if (a_x >= 0 && a_x > G->a_size - (int)sizeof(int)) alpar@9: xerror("glp_asnprob_hall: a_x = %d; invalid offset\n", a_x); alpar@9: if (glp_check_asnprob(G, v_set)) alpar@9: return -1; alpar@9: /* determine the number of vertices in sets R and S and renumber alpar@9: vertices in S which correspond to columns of the matrix; skip alpar@9: all isolated vertices */ alpar@9: num = xcalloc(1+G->nv, sizeof(int)); alpar@9: n1 = n2 = 0; alpar@9: for (i = 1; i <= G->nv; i++) alpar@9: { v = G->v[i]; alpar@9: if (v->in == NULL && v->out != NULL) alpar@9: n1++, num[i] = 0; /* vertex in R */ alpar@9: else if (v->in != NULL && v->out == NULL) alpar@9: n2++, num[i] = n2; /* vertex in S */ alpar@9: else alpar@9: { xassert(v->in == NULL && v->out == NULL); alpar@9: num[i] = -1; /* isolated vertex */ alpar@9: } alpar@9: } alpar@9: /* the matrix must be square, thus, if it has more columns than alpar@9: rows, extra rows will be just empty, and vice versa */ alpar@9: n = (n1 >= n2 ? n1 : n2); alpar@9: /* allocate working arrays */ alpar@9: icn = xcalloc(1+G->na, sizeof(int)); alpar@9: ip = xcalloc(1+n, sizeof(int)); alpar@9: lenr = xcalloc(1+n, sizeof(int)); alpar@9: iperm = xcalloc(1+n, sizeof(int)); alpar@9: pr = xcalloc(1+n, sizeof(int)); alpar@9: arp = xcalloc(1+n, sizeof(int)); alpar@9: cv = xcalloc(1+n, sizeof(int)); alpar@9: out = xcalloc(1+n, sizeof(int)); alpar@9: /* build the adjacency matrix of the bipartite graph in row-wise alpar@9: format (rows are vertices in R, columns are vertices in S) */ alpar@9: k = 0, loc = 1; alpar@9: for (i = 1; i <= G->nv; i++) alpar@9: { if (num[i] != 0) continue; alpar@9: /* vertex i in R */ alpar@9: ip[++k] = loc; alpar@9: v = G->v[i]; alpar@9: for (a = v->out; a != NULL; a = a->t_next) alpar@9: { xassert(num[a->head->i] != 0); alpar@9: icn[loc++] = num[a->head->i]; alpar@9: } alpar@9: lenr[k] = loc - ip[k]; alpar@9: } alpar@9: xassert(loc-1 == G->na); alpar@9: /* make all extra rows empty (all extra columns are empty due to alpar@9: the row-wise format used) */ alpar@9: for (k++; k <= n; k++) alpar@9: ip[k] = loc, lenr[k] = 0; alpar@9: /* find a row permutation that maximizes the number of non-zeros alpar@9: on the main diagonal */ alpar@9: card = mc21a(n, icn, ip, lenr, iperm, pr, arp, cv, out); alpar@9: #if 1 /* 18/II-2010 */ alpar@9: /* FIXED: if card = n, arp remains clobbered on exit */ alpar@9: for (i = 1; i <= n; i++) alpar@9: arp[i] = 0; alpar@9: for (i = 1; i <= card; i++) alpar@9: { k = iperm[i]; alpar@9: xassert(1 <= k && k <= n); alpar@9: xassert(arp[k] == 0); alpar@9: arp[k] = i; alpar@9: } alpar@9: #endif alpar@9: /* store solution, if necessary */ alpar@9: if (a_x < 0) goto skip; alpar@9: k = 0; alpar@9: for (i = 1; i <= G->nv; i++) alpar@9: { if (num[i] != 0) continue; alpar@9: /* vertex i in R */ alpar@9: k++; alpar@9: v = G->v[i]; alpar@9: for (a = v->out; a != NULL; a = a->t_next) alpar@9: { /* arp[k] is the number of matched column or zero */ alpar@9: if (arp[k] == num[a->head->i]) alpar@9: { xassert(arp[k] != 0); alpar@9: xij = 1; alpar@9: } alpar@9: else alpar@9: xij = 0; alpar@9: memcpy((char *)a->data + a_x, &xij, sizeof(int)); alpar@9: } alpar@9: } alpar@9: skip: /* free working arrays */ alpar@9: xfree(num); alpar@9: xfree(icn); alpar@9: xfree(ip); alpar@9: xfree(lenr); alpar@9: xfree(iperm); alpar@9: xfree(pr); alpar@9: xfree(arp); alpar@9: xfree(cv); alpar@9: xfree(out); alpar@9: return card; alpar@9: } alpar@9: alpar@9: /*********************************************************************** alpar@9: * NAME alpar@9: * alpar@9: * glp_cpp - solve critical path problem alpar@9: * alpar@9: * SYNOPSIS alpar@9: * alpar@9: * double glp_cpp(glp_graph *G, int v_t, int v_es, int v_ls); alpar@9: * alpar@9: * DESCRIPTION alpar@9: * alpar@9: * The routine glp_cpp solves the critical path problem represented in alpar@9: * the form of the project network. alpar@9: * alpar@9: * The parameter G is a pointer to the graph object, which specifies alpar@9: * the project network. This graph must be acyclic. Multiple arcs are alpar@9: * allowed being considered as single arcs. alpar@9: * alpar@9: * The parameter v_t specifies an offset of the field of type double alpar@9: * in the vertex data block, which contains time t[i] >= 0 needed to alpar@9: * perform corresponding job j. If v_t < 0, it is assumed that t[i] = 1 alpar@9: * for all jobs. alpar@9: * alpar@9: * The parameter v_es specifies an offset of the field of type double alpar@9: * in the vertex data block, to which the routine stores earliest start alpar@9: * time for corresponding job. If v_es < 0, this time is not stored. alpar@9: * alpar@9: * The parameter v_ls specifies an offset of the field of type double alpar@9: * in the vertex data block, to which the routine stores latest start alpar@9: * time for corresponding job. If v_ls < 0, this time is not stored. alpar@9: * alpar@9: * RETURNS alpar@9: * alpar@9: * The routine glp_cpp returns the minimal project duration, that is, alpar@9: * minimal time needed to perform all jobs in the project. */ alpar@9: alpar@9: static void sorting(glp_graph *G, int list[]); alpar@9: alpar@9: double glp_cpp(glp_graph *G, int v_t, int v_es, int v_ls) alpar@9: { glp_vertex *v; alpar@9: glp_arc *a; alpar@9: int i, j, k, nv, *list; alpar@9: double temp, total, *t, *es, *ls; alpar@9: if (v_t >= 0 && v_t > G->v_size - (int)sizeof(double)) alpar@9: xerror("glp_cpp: v_t = %d; invalid offset\n", v_t); alpar@9: if (v_es >= 0 && v_es > G->v_size - (int)sizeof(double)) alpar@9: xerror("glp_cpp: v_es = %d; invalid offset\n", v_es); alpar@9: if (v_ls >= 0 && v_ls > G->v_size - (int)sizeof(double)) alpar@9: xerror("glp_cpp: v_ls = %d; invalid offset\n", v_ls); alpar@9: nv = G->nv; alpar@9: if (nv == 0) alpar@9: { total = 0.0; alpar@9: goto done; alpar@9: } alpar@9: /* allocate working arrays */ alpar@9: t = xcalloc(1+nv, sizeof(double)); alpar@9: es = xcalloc(1+nv, sizeof(double)); alpar@9: ls = xcalloc(1+nv, sizeof(double)); alpar@9: list = xcalloc(1+nv, sizeof(int)); alpar@9: /* retrieve job times */ alpar@9: for (i = 1; i <= nv; i++) alpar@9: { v = G->v[i]; alpar@9: if (v_t >= 0) alpar@9: { memcpy(&t[i], (char *)v->data + v_t, sizeof(double)); alpar@9: if (t[i] < 0.0) alpar@9: xerror("glp_cpp: t[%d] = %g; invalid time\n", i, t[i]); alpar@9: } alpar@9: else alpar@9: t[i] = 1.0; alpar@9: } alpar@9: /* perform topological sorting to determine the list of nodes alpar@9: (jobs) such that if list[k] = i and list[kk] = j and there alpar@9: exists arc (i->j), then k < kk */ alpar@9: sorting(G, list); alpar@9: /* FORWARD PASS */ alpar@9: /* determine earliest start times */ alpar@9: for (k = 1; k <= nv; k++) alpar@9: { j = list[k]; alpar@9: es[j] = 0.0; alpar@9: for (a = G->v[j]->in; a != NULL; a = a->h_next) alpar@9: { i = a->tail->i; alpar@9: /* there exists arc (i->j) in the project network */ alpar@9: temp = es[i] + t[i]; alpar@9: if (es[j] < temp) es[j] = temp; alpar@9: } alpar@9: } alpar@9: /* determine the minimal project duration */ alpar@9: total = 0.0; alpar@9: for (i = 1; i <= nv; i++) alpar@9: { temp = es[i] + t[i]; alpar@9: if (total < temp) total = temp; alpar@9: } alpar@9: /* BACKWARD PASS */ alpar@9: /* determine latest start times */ alpar@9: for (k = nv; k >= 1; k--) alpar@9: { i = list[k]; alpar@9: ls[i] = total - t[i]; alpar@9: for (a = G->v[i]->out; a != NULL; a = a->t_next) alpar@9: { j = a->head->i; alpar@9: /* there exists arc (i->j) in the project network */ alpar@9: temp = ls[j] - t[i]; alpar@9: if (ls[i] > temp) ls[i] = temp; alpar@9: } alpar@9: /* avoid possible round-off errors */ alpar@9: if (ls[i] < es[i]) ls[i] = es[i]; alpar@9: } alpar@9: /* store results, if necessary */ alpar@9: if (v_es >= 0) alpar@9: { for (i = 1; i <= nv; i++) alpar@9: { v = G->v[i]; alpar@9: memcpy((char *)v->data + v_es, &es[i], sizeof(double)); alpar@9: } alpar@9: } alpar@9: if (v_ls >= 0) alpar@9: { for (i = 1; i <= nv; i++) alpar@9: { v = G->v[i]; alpar@9: memcpy((char *)v->data + v_ls, &ls[i], sizeof(double)); alpar@9: } alpar@9: } alpar@9: /* free working arrays */ alpar@9: xfree(t); alpar@9: xfree(es); alpar@9: xfree(ls); alpar@9: xfree(list); alpar@9: done: return total; alpar@9: } alpar@9: alpar@9: static void sorting(glp_graph *G, int list[]) alpar@9: { /* perform topological sorting to determine the list of nodes alpar@9: (jobs) such that if list[k] = i and list[kk] = j and there alpar@9: exists arc (i->j), then k < kk */ alpar@9: int i, k, nv, v_size, *num; alpar@9: void **save; alpar@9: nv = G->nv; alpar@9: v_size = G->v_size; alpar@9: save = xcalloc(1+nv, sizeof(void *)); alpar@9: num = xcalloc(1+nv, sizeof(int)); alpar@9: G->v_size = sizeof(int); alpar@9: for (i = 1; i <= nv; i++) alpar@9: { save[i] = G->v[i]->data; alpar@9: G->v[i]->data = &num[i]; alpar@9: list[i] = 0; alpar@9: } alpar@9: if (glp_top_sort(G, 0) != 0) alpar@9: xerror("glp_cpp: project network is not acyclic\n"); alpar@9: G->v_size = v_size; alpar@9: for (i = 1; i <= nv; i++) alpar@9: { G->v[i]->data = save[i]; alpar@9: k = num[i]; alpar@9: xassert(1 <= k && k <= nv); alpar@9: xassert(list[k] == 0); alpar@9: list[k] = i; alpar@9: } alpar@9: xfree(save); alpar@9: xfree(num); alpar@9: return; alpar@9: } alpar@9: alpar@9: /* eof */