You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

456 lines
14 KiB

/* glpapi07.c (exact simplex solver) */
/***********************************************************************
* This code is part of GLPK (GNU Linear Programming Kit).
*
* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
* 2009, 2010, 2011, 2013 Andrew Makhorin, Department for Applied
* Informatics, Moscow Aviation Institute, Moscow, Russia. All rights
* reserved. E-mail: <mao@gnu.org>.
*
* GLPK is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GLPK is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GLPK. If not, see <http://www.gnu.org/licenses/>.
***********************************************************************/
#include "draft.h"
#include "glpssx.h"
#include "misc.h"
#include "prob.h"
/***********************************************************************
* NAME
*
* glp_exact - solve LP problem in exact arithmetic
*
* SYNOPSIS
*
* int glp_exact(glp_prob *lp, const glp_smcp *parm);
*
* DESCRIPTION
*
* The routine glp_exact is a tentative implementation of the primal
* two-phase simplex method based on exact (rational) arithmetic. It is
* similar to the routine glp_simplex, however, for all internal
* computations it uses arithmetic of rational numbers, which is exact
* in mathematical sense, i.e. free of round-off errors unlike floating
* point arithmetic.
*
* Note that the routine glp_exact uses inly two control parameters
* passed in the structure glp_smcp, namely, it_lim and tm_lim.
*
* RETURNS
*
* 0 The LP problem instance has been successfully solved. This code
* does not necessarily mean that the solver has found optimal
* solution. It only means that the solution process was successful.
*
* GLP_EBADB
* Unable to start the search, because the initial basis specified
* in the problem object is invalid--the number of basic (auxiliary
* and structural) variables is not the same as the number of rows in
* the problem object.
*
* GLP_ESING
* Unable to start the search, because the basis matrix correspodning
* to the initial basis is exactly singular.
*
* GLP_EBOUND
* Unable to start the search, because some double-bounded variables
* have incorrect bounds.
*
* GLP_EFAIL
* The problem has no rows/columns.
*
* GLP_EITLIM
* The search was prematurely terminated, because the simplex
* iteration limit has been exceeded.
*
* GLP_ETMLIM
* The search was prematurely terminated, because the time limit has
* been exceeded. */
static void set_d_eps(mpq_t x, double val)
{ /* convert double val to rational x obtaining a more adequate
fraction than provided by mpq_set_d due to allowing a small
approximation error specified by a given relative tolerance;
for example, mpq_set_d would give the following
1/3 ~= 0.333333333333333314829616256247391... ->
-> 6004799503160661/18014398509481984
while this routine gives exactly 1/3 */
int s, n, j;
double f, p, q, eps = 1e-9;
mpq_t temp;
xassert(-DBL_MAX <= val && val <= +DBL_MAX);
#if 1 /* 30/VII-2008 */
if (val == floor(val))
{ /* if val is integral, do not approximate */
mpq_set_d(x, val);
goto done;
}
#endif
if (val > 0.0)
s = +1;
else if (val < 0.0)
s = -1;
else
{ mpq_set_si(x, 0, 1);
goto done;
}
f = frexp(fabs(val), &n);
/* |val| = f * 2^n, where 0.5 <= f < 1.0 */
fp2rat(f, 0.1 * eps, &p, &q);
/* f ~= p / q, where p and q are integers */
mpq_init(temp);
mpq_set_d(x, p);
mpq_set_d(temp, q);
mpq_div(x, x, temp);
mpq_set_si(temp, 1, 1);
for (j = 1; j <= abs(n); j++)
mpq_add(temp, temp, temp);
if (n > 0)
mpq_mul(x, x, temp);
else if (n < 0)
mpq_div(x, x, temp);
mpq_clear(temp);
if (s < 0) mpq_neg(x, x);
/* check that the desired tolerance has been attained */
xassert(fabs(val - mpq_get_d(x)) <= eps * (1.0 + fabs(val)));
done: return;
}
static void load_data(SSX *ssx, glp_prob *lp)
{ /* load LP problem data into simplex solver workspace */
int m = ssx->m;
int n = ssx->n;
int nnz = ssx->A_ptr[n+1]-1;
int j, k, type, loc, len, *ind;
double lb, ub, coef, *val;
xassert(lp->m == m);
xassert(lp->n == n);
xassert(lp->nnz == nnz);
/* types and bounds of rows and columns */
for (k = 1; k <= m+n; k++)
{ if (k <= m)
{ type = lp->row[k]->type;
lb = lp->row[k]->lb;
ub = lp->row[k]->ub;
}
else
{ type = lp->col[k-m]->type;
lb = lp->col[k-m]->lb;
ub = lp->col[k-m]->ub;
}
switch (type)
{ case GLP_FR: type = SSX_FR; break;
case GLP_LO: type = SSX_LO; break;
case GLP_UP: type = SSX_UP; break;
case GLP_DB: type = SSX_DB; break;
case GLP_FX: type = SSX_FX; break;
default: xassert(type != type);
}
ssx->type[k] = type;
set_d_eps(ssx->lb[k], lb);
set_d_eps(ssx->ub[k], ub);
}
/* optimization direction */
switch (lp->dir)
{ case GLP_MIN: ssx->dir = SSX_MIN; break;
case GLP_MAX: ssx->dir = SSX_MAX; break;
default: xassert(lp != lp);
}
/* objective coefficients */
for (k = 0; k <= m+n; k++)
{ if (k == 0)
coef = lp->c0;
else if (k <= m)
coef = 0.0;
else
coef = lp->col[k-m]->coef;
set_d_eps(ssx->coef[k], coef);
}
/* constraint coefficients */
ind = xcalloc(1+m, sizeof(int));
val = xcalloc(1+m, sizeof(double));
loc = 0;
for (j = 1; j <= n; j++)
{ ssx->A_ptr[j] = loc+1;
len = glp_get_mat_col(lp, j, ind, val);
for (k = 1; k <= len; k++)
{ loc++;
ssx->A_ind[loc] = ind[k];
set_d_eps(ssx->A_val[loc], val[k]);
}
}
xassert(loc == nnz);
xfree(ind);
xfree(val);
return;
}
static int load_basis(SSX *ssx, glp_prob *lp)
{ /* load current LP basis into simplex solver workspace */
int m = ssx->m;
int n = ssx->n;
int *type = ssx->type;
int *stat = ssx->stat;
int *Q_row = ssx->Q_row;
int *Q_col = ssx->Q_col;
int i, j, k;
xassert(lp->m == m);
xassert(lp->n == n);
/* statuses of rows and columns */
for (k = 1; k <= m+n; k++)
{ if (k <= m)
stat[k] = lp->row[k]->stat;
else
stat[k] = lp->col[k-m]->stat;
switch (stat[k])
{ case GLP_BS:
stat[k] = SSX_BS;
break;
case GLP_NL:
stat[k] = SSX_NL;
xassert(type[k] == SSX_LO || type[k] == SSX_DB);
break;
case GLP_NU:
stat[k] = SSX_NU;
xassert(type[k] == SSX_UP || type[k] == SSX_DB);
break;
case GLP_NF:
stat[k] = SSX_NF;
xassert(type[k] == SSX_FR);
break;
case GLP_NS:
stat[k] = SSX_NS;
xassert(type[k] == SSX_FX);
break;
default:
xassert(stat != stat);
}
}
/* build permutation matix Q */
i = j = 0;
for (k = 1; k <= m+n; k++)
{ if (stat[k] == SSX_BS)
{ i++;
if (i > m) return 1;
Q_row[k] = i, Q_col[i] = k;
}
else
{ j++;
if (j > n) return 1;
Q_row[k] = m+j, Q_col[m+j] = k;
}
}
xassert(i == m && j == n);
return 0;
}
int glp_exact(glp_prob *lp, const glp_smcp *parm)
{ glp_smcp _parm;
SSX *ssx;
int m = lp->m;
int n = lp->n;
int nnz = lp->nnz;
int i, j, k, type, pst, dst, ret, stat;
double lb, ub, prim, dual, sum;
if (parm == NULL)
parm = &_parm, glp_init_smcp((glp_smcp *)parm);
/* check control parameters */
if (parm->it_lim < 0)
xerror("glp_exact: it_lim = %d; invalid parameter\n",
parm->it_lim);
if (parm->tm_lim < 0)
xerror("glp_exact: tm_lim = %d; invalid parameter\n",
parm->tm_lim);
/* the problem must have at least one row and one column */
if (!(m > 0 && n > 0))
{ xprintf("glp_exact: problem has no rows/columns\n");
return GLP_EFAIL;
}
#if 1
/* basic solution is currently undefined */
lp->pbs_stat = lp->dbs_stat = GLP_UNDEF;
lp->obj_val = 0.0;
lp->some = 0;
#endif
/* check that all double-bounded variables have correct bounds */
for (k = 1; k <= m+n; k++)
{ if (k <= m)
{ type = lp->row[k]->type;
lb = lp->row[k]->lb;
ub = lp->row[k]->ub;
}
else
{ type = lp->col[k-m]->type;
lb = lp->col[k-m]->lb;
ub = lp->col[k-m]->ub;
}
if (type == GLP_DB && lb >= ub)
{ xprintf("glp_exact: %s %d has invalid bounds\n",
k <= m ? "row" : "column", k <= m ? k : k-m);
return GLP_EBOUND;
}
}
/* create the simplex solver workspace */
xprintf("glp_exact: %d rows, %d columns, %d non-zeros\n",
m, n, nnz);
#ifdef HAVE_GMP
xprintf("GNU MP bignum library is being used\n");
#else
xprintf("GLPK bignum module is being used\n");
xprintf("(Consider installing GNU MP to attain a much better perf"
"ormance.)\n");
#endif
ssx = ssx_create(m, n, nnz);
/* load LP problem data into the workspace */
load_data(ssx, lp);
/* load current LP basis into the workspace */
if (load_basis(ssx, lp))
{ xprintf("glp_exact: initial LP basis is invalid\n");
ret = GLP_EBADB;
goto done;
}
/* inherit some control parameters from the LP object */
#if 0
ssx->it_lim = lpx_get_int_parm(lp, LPX_K_ITLIM);
ssx->it_cnt = lpx_get_int_parm(lp, LPX_K_ITCNT);
ssx->tm_lim = lpx_get_real_parm(lp, LPX_K_TMLIM);
#else
ssx->it_lim = parm->it_lim;
ssx->it_cnt = lp->it_cnt;
ssx->tm_lim = (double)parm->tm_lim / 1000.0;
#endif
ssx->out_frq = 5.0;
ssx->tm_beg = xtime();
#if 0 /* 10/VI-2013 */
ssx->tm_lag = xlset(0);
#else
ssx->tm_lag = 0.0;
#endif
/* solve LP */
ret = ssx_driver(ssx);
/* copy back some statistics to the LP object */
#if 0
lpx_set_int_parm(lp, LPX_K_ITLIM, ssx->it_lim);
lpx_set_int_parm(lp, LPX_K_ITCNT, ssx->it_cnt);
lpx_set_real_parm(lp, LPX_K_TMLIM, ssx->tm_lim);
#else
lp->it_cnt = ssx->it_cnt;
#endif
/* analyze the return code */
switch (ret)
{ case 0:
/* optimal solution found */
ret = 0;
pst = dst = GLP_FEAS;
break;
case 1:
/* problem has no feasible solution */
ret = 0;
pst = GLP_NOFEAS, dst = GLP_INFEAS;
break;
case 2:
/* problem has unbounded solution */
ret = 0;
pst = GLP_FEAS, dst = GLP_NOFEAS;
#if 1
xassert(1 <= ssx->q && ssx->q <= n);
lp->some = ssx->Q_col[m + ssx->q];
xassert(1 <= lp->some && lp->some <= m+n);
#endif
break;
case 3:
/* iteration limit exceeded (phase I) */
ret = GLP_EITLIM;
pst = dst = GLP_INFEAS;
break;
case 4:
/* iteration limit exceeded (phase II) */
ret = GLP_EITLIM;
pst = GLP_FEAS, dst = GLP_INFEAS;
break;
case 5:
/* time limit exceeded (phase I) */
ret = GLP_ETMLIM;
pst = dst = GLP_INFEAS;
break;
case 6:
/* time limit exceeded (phase II) */
ret = GLP_ETMLIM;
pst = GLP_FEAS, dst = GLP_INFEAS;
break;
case 7:
/* initial basis matrix is singular */
ret = GLP_ESING;
goto done;
default:
xassert(ret != ret);
}
/* store final basic solution components into LP object */
lp->pbs_stat = pst;
lp->dbs_stat = dst;
sum = lp->c0;
for (k = 1; k <= m+n; k++)
{ if (ssx->stat[k] == SSX_BS)
{ i = ssx->Q_row[k]; /* x[k] = xB[i] */
xassert(1 <= i && i <= m);
stat = GLP_BS;
prim = mpq_get_d(ssx->bbar[i]);
dual = 0.0;
}
else
{ j = ssx->Q_row[k] - m; /* x[k] = xN[j] */
xassert(1 <= j && j <= n);
switch (ssx->stat[k])
{ case SSX_NF:
stat = GLP_NF;
prim = 0.0;
break;
case SSX_NL:
stat = GLP_NL;
prim = mpq_get_d(ssx->lb[k]);
break;
case SSX_NU:
stat = GLP_NU;
prim = mpq_get_d(ssx->ub[k]);
break;
case SSX_NS:
stat = GLP_NS;
prim = mpq_get_d(ssx->lb[k]);
break;
default:
xassert(ssx != ssx);
}
dual = mpq_get_d(ssx->cbar[j]);
}
if (k <= m)
{ glp_set_row_stat(lp, k, stat);
lp->row[k]->prim = prim;
lp->row[k]->dual = dual;
}
else
{ glp_set_col_stat(lp, k-m, stat);
lp->col[k-m]->prim = prim;
lp->col[k-m]->dual = dual;
sum += lp->col[k-m]->coef * prim;
}
}
lp->obj_val = sum;
done: /* delete the simplex solver workspace */
ssx_delete(ssx);
/* return to the application program */
return ret;
}
/* eof */