1 /* asnokalg.c (solve assignment problem with out-of-kilter alg.) */
2 
3 /***********************************************************************
4 *  This code is part of GLPK (GNU Linear Programming Kit).
5 *  Copyright (C) 2009-2016 Free Software Foundation, Inc.
6 *  Written by Andrew Makhorin <mao@gnu.org>.
7 *
8 *  GLPK is free software: you can redistribute it and/or modify it
9 *  under the terms of the GNU General Public License as published by
10 *  the Free Software Foundation, either version 3 of the License, or
11 *  (at your option) any later version.
12 *
13 *  GLPK is distributed in the hope that it will be useful, but WITHOUT
14 *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 *  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
16 *  License for more details.
17 *
18 *  You should have received a copy of the GNU General Public License
19 *  along with GLPK. If not, see <http://www.gnu.org/licenses/>.
20 ***********************************************************************/
21 
22 #include "env.h"
23 #include "glpk.h"
24 #include "okalg.h"
25 
glp_asnprob_okalg(int form,glp_graph * G,int v_set,int a_cost,double * sol,int a_x)26 int glp_asnprob_okalg(int form, glp_graph *G, int v_set, int a_cost,
27       double *sol, int a_x)
28 {     /* solve assignment problem with out-of-kilter algorithm */
29       glp_vertex *v;
30       glp_arc *a;
31       int nv, na, i, k, *tail, *head, *low, *cap, *cost, *x, *pi, ret;
32       double temp;
33       if (!(form == GLP_ASN_MIN || form == GLP_ASN_MAX ||
34             form == GLP_ASN_MMP))
35          xerror("glp_asnprob_okalg: form = %d; invalid parameter\n",
36             form);
37       if (v_set >= 0 && v_set > G->v_size - (int)sizeof(int))
38          xerror("glp_asnprob_okalg: v_set = %d; invalid offset\n",
39             v_set);
40       if (a_cost >= 0 && a_cost > G->a_size - (int)sizeof(double))
41          xerror("glp_asnprob_okalg: a_cost = %d; invalid offset\n",
42             a_cost);
43       if (a_x >= 0 && a_x > G->a_size - (int)sizeof(int))
44          xerror("glp_asnprob_okalg: a_x = %d; invalid offset\n", a_x);
45       if (glp_check_asnprob(G, v_set))
46          return GLP_EDATA;
47       /* nv is the total number of nodes in the resulting network */
48       nv = G->nv + 1;
49       /* na is the total number of arcs in the resulting network */
50       na = G->na + G->nv;
51       /* allocate working arrays */
52       tail = xcalloc(1+na, sizeof(int));
53       head = xcalloc(1+na, sizeof(int));
54       low = xcalloc(1+na, sizeof(int));
55       cap = xcalloc(1+na, sizeof(int));
56       cost = xcalloc(1+na, sizeof(int));
57       x = xcalloc(1+na, sizeof(int));
58       pi = xcalloc(1+nv, sizeof(int));
59       /* construct the resulting network */
60       k = 0;
61       /* (original arcs) */
62       for (i = 1; i <= G->nv; i++)
63       {  v = G->v[i];
64          for (a = v->out; a != NULL; a = a->t_next)
65          {  k++;
66             tail[k] = a->tail->i;
67             head[k] = a->head->i;
68             low[k] = 0;
69             cap[k] = 1;
70             if (a_cost >= 0)
71                memcpy(&temp, (char *)a->data + a_cost, sizeof(double));
72             else
73                temp = 1.0;
74             if (!(fabs(temp) <= (double)INT_MAX && temp == floor(temp)))
75             {  ret = GLP_EDATA;
76                goto done;
77             }
78             cost[k] = (int)temp;
79             if (form != GLP_ASN_MIN) cost[k] = - cost[k];
80          }
81       }
82       /* (artificial arcs) */
83       for (i = 1; i <= G->nv; i++)
84       {  v = G->v[i];
85          k++;
86          if (v->out == NULL)
87             tail[k] = i, head[k] = nv;
88          else if (v->in == NULL)
89             tail[k] = nv, head[k] = i;
90          else
91             xassert(v != v);
92          low[k] = (form == GLP_ASN_MMP ? 0 : 1);
93          cap[k] = 1;
94          cost[k] = 0;
95       }
96       xassert(k == na);
97       /* find minimal-cost circulation in the resulting network */
98       ret = okalg(nv, na, tail, head, low, cap, cost, x, pi);
99       switch (ret)
100       {  case 0:
101             /* optimal circulation found */
102             ret = 0;
103             break;
104          case 1:
105             /* no feasible circulation exists */
106             ret = GLP_ENOPFS;
107             break;
108          case 2:
109             /* integer overflow occured */
110             ret = GLP_ERANGE;
111             goto done;
112          case 3:
113             /* optimality test failed (logic error) */
114             ret = GLP_EFAIL;
115             goto done;
116          default:
117             xassert(ret != ret);
118       }
119       /* store solution components */
120       /* (objective function = the total cost) */
121       if (sol != NULL)
122       {  temp = 0.0;
123          for (k = 1; k <= na; k++)
124             temp += (double)cost[k] * (double)x[k];
125          if (form != GLP_ASN_MIN) temp = - temp;
126          *sol = temp;
127       }
128       /* (arc flows) */
129       if (a_x >= 0)
130       {  k = 0;
131          for (i = 1; i <= G->nv; i++)
132          {  v = G->v[i];
133             for (a = v->out; a != NULL; a = a->t_next)
134             {  k++;
135                if (ret == 0)
136                   xassert(x[k] == 0 || x[k] == 1);
137                memcpy((char *)a->data + a_x, &x[k], sizeof(int));
138             }
139          }
140       }
141 done: /* free working arrays */
142       xfree(tail);
143       xfree(head);
144       xfree(low);
145       xfree(cap);
146       xfree(cost);
147       xfree(x);
148       xfree(pi);
149       return ret;
150 }
151 
152 /* eof */
153