src/glpapi06.c
author Alpar Juttner <alpar@cs.elte.hu>
Mon, 06 Dec 2010 13:09:21 +0100
changeset 1 c445c931472f
permissions -rw-r--r--
Import glpk-4.45

- Generated files and doc/notes are removed
     1 /* glpapi06.c (simplex method routines) */
     2 
     3 /***********************************************************************
     4 *  This code is part of GLPK (GNU Linear Programming Kit).
     5 *
     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>.
    10 *
    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.
    15 *
    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.
    20 *
    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 ***********************************************************************/
    24 
    25 #include "glpios.h"
    26 #include "glpnpp.h"
    27 #include "glpspx.h"
    28 
    29 /***********************************************************************
    30 *  NAME
    31 *
    32 *  glp_simplex - solve LP problem with the simplex method
    33 *
    34 *  SYNOPSIS
    35 *
    36 *  int glp_simplex(glp_prob *P, const glp_smcp *parm);
    37 *
    38 *  DESCRIPTION
    39 *
    40 *  The routine glp_simplex is a driver to the LP solver based on the
    41 *  simplex method. This routine retrieves problem data from the
    42 *  specified problem object, calls the solver to solve the problem
    43 *  instance, and stores results of computations back into the problem
    44 *  object.
    45 *
    46 *  The simplex solver has a set of control parameters. Values of the
    47 *  control parameters can be passed in a structure glp_smcp, which the
    48 *  parameter parm points to.
    49 *
    50 *  The parameter parm can be specified as NULL, in which case the LP
    51 *  solver uses default settings.
    52 *
    53 *  RETURNS
    54 *
    55 *  0  The LP problem instance has been successfully solved. This code
    56 *     does not necessarily mean that the solver has found optimal
    57 *     solution. It only means that the solution process was successful.
    58 *
    59 *  GLP_EBADB
    60 *     Unable to start the search, because the initial basis specified
    61 *     in the problem object is invalid--the number of basic (auxiliary
    62 *     and structural) variables is not the same as the number of rows in
    63 *     the problem object.
    64 *
    65 *  GLP_ESING
    66 *     Unable to start the search, because the basis matrix correspodning
    67 *     to the initial basis is singular within the working precision.
    68 *
    69 *  GLP_ECOND
    70 *     Unable to start the search, because the basis matrix correspodning
    71 *     to the initial basis is ill-conditioned, i.e. its condition number
    72 *     is too large.
    73 *
    74 *  GLP_EBOUND
    75 *     Unable to start the search, because some double-bounded variables
    76 *     have incorrect bounds.
    77 *
    78 *  GLP_EFAIL
    79 *     The search was prematurely terminated due to the solver failure.
    80 *
    81 *  GLP_EOBJLL
    82 *     The search was prematurely terminated, because the objective
    83 *     function being maximized has reached its lower limit and continues
    84 *     decreasing (dual simplex only).
    85 *
    86 *  GLP_EOBJUL
    87 *     The search was prematurely terminated, because the objective
    88 *     function being minimized has reached its upper limit and continues
    89 *     increasing (dual simplex only).
    90 *
    91 *  GLP_EITLIM
    92 *     The search was prematurely terminated, because the simplex
    93 *     iteration limit has been exceeded.
    94 *
    95 *  GLP_ETMLIM
    96 *     The search was prematurely terminated, because the time limit has
    97 *     been exceeded.
    98 *
    99 *  GLP_ENOPFS
   100 *     The LP problem instance has no primal feasible solution (only if
   101 *     the LP presolver is used).
   102 *
   103 *  GLP_ENODFS
   104 *     The LP problem instance has no dual feasible solution (only if the
   105 *     LP presolver is used). */
   106 
   107 static void trivial_lp(glp_prob *P, const glp_smcp *parm)
   108 {     /* solve trivial LP which has empty constraint matrix */
   109       GLPROW *row;
   110       GLPCOL *col;
   111       int i, j;
   112       double p_infeas, d_infeas, zeta;
   113       P->valid = 0;
   114       P->pbs_stat = P->dbs_stat = GLP_FEAS;
   115       P->obj_val = P->c0;
   116       P->some = 0;
   117       p_infeas = d_infeas = 0.0;
   118       /* make all auxiliary variables basic */
   119       for (i = 1; i <= P->m; i++)
   120       {  row = P->row[i];
   121          row->stat = GLP_BS;
   122          row->prim = row->dual = 0.0;
   123          /* check primal feasibility */
   124          if (row->type == GLP_LO || row->type == GLP_DB ||
   125              row->type == GLP_FX)
   126          {  /* row has lower bound */
   127             if (row->lb > + parm->tol_bnd)
   128             {  P->pbs_stat = GLP_NOFEAS;
   129                if (P->some == 0 && parm->meth != GLP_PRIMAL)
   130                   P->some = i;
   131             }
   132             if (p_infeas < + row->lb)
   133                p_infeas = + row->lb;
   134          }
   135          if (row->type == GLP_UP || row->type == GLP_DB ||
   136              row->type == GLP_FX)
   137          {  /* row has upper bound */
   138             if (row->ub < - parm->tol_bnd)
   139             {  P->pbs_stat = GLP_NOFEAS;
   140                if (P->some == 0 && parm->meth != GLP_PRIMAL)
   141                   P->some = i;
   142             }
   143             if (p_infeas < - row->ub)
   144                p_infeas = - row->ub;
   145          }
   146       }
   147       /* determine scale factor for the objective row */
   148       zeta = 1.0;
   149       for (j = 1; j <= P->n; j++)
   150       {  col = P->col[j];
   151          if (zeta < fabs(col->coef)) zeta = fabs(col->coef);
   152       }
   153       zeta = (P->dir == GLP_MIN ? +1.0 : -1.0) / zeta;
   154       /* make all structural variables non-basic */
   155       for (j = 1; j <= P->n; j++)
   156       {  col = P->col[j];
   157          if (col->type == GLP_FR)
   158             col->stat = GLP_NF, col->prim = 0.0;
   159          else if (col->type == GLP_LO)
   160 lo:         col->stat = GLP_NL, col->prim = col->lb;
   161          else if (col->type == GLP_UP)
   162 up:         col->stat = GLP_NU, col->prim = col->ub;
   163          else if (col->type == GLP_DB)
   164          {  if (zeta * col->coef > 0.0)
   165                goto lo;
   166             else if (zeta * col->coef < 0.0)
   167                goto up;
   168             else if (fabs(col->lb) <= fabs(col->ub))
   169                goto lo;
   170             else
   171                goto up;
   172          }
   173          else if (col->type == GLP_FX)
   174             col->stat = GLP_NS, col->prim = col->lb;
   175          col->dual = col->coef;
   176          P->obj_val += col->coef * col->prim;
   177          /* check dual feasibility */
   178          if (col->type == GLP_FR || col->type == GLP_LO)
   179          {  /* column has no upper bound */
   180             if (zeta * col->dual < - parm->tol_dj)
   181             {  P->dbs_stat = GLP_NOFEAS;
   182                if (P->some == 0 && parm->meth == GLP_PRIMAL)
   183                   P->some = P->m + j;
   184             }
   185             if (d_infeas < - zeta * col->dual)
   186                d_infeas = - zeta * col->dual;
   187          }
   188          if (col->type == GLP_FR || col->type == GLP_UP)
   189          {  /* column has no lower bound */
   190             if (zeta * col->dual > + parm->tol_dj)
   191             {  P->dbs_stat = GLP_NOFEAS;
   192                if (P->some == 0 && parm->meth == GLP_PRIMAL)
   193                   P->some = P->m + j;
   194             }
   195             if (d_infeas < + zeta * col->dual)
   196                d_infeas = + zeta * col->dual;
   197          }
   198       }
   199       /* simulate the simplex solver output */
   200       if (parm->msg_lev >= GLP_MSG_ON && parm->out_dly == 0)
   201       {  xprintf("~%6d: obj = %17.9e  infeas = %10.3e\n", P->it_cnt,
   202             P->obj_val, parm->meth == GLP_PRIMAL ? p_infeas : d_infeas);
   203       }
   204       if (parm->msg_lev >= GLP_MSG_ALL && parm->out_dly == 0)
   205       {  if (P->pbs_stat == GLP_FEAS && P->dbs_stat == GLP_FEAS)
   206             xprintf("OPTIMAL SOLUTION FOUND\n");
   207          else if (P->pbs_stat == GLP_NOFEAS)
   208             xprintf("PROBLEM HAS NO FEASIBLE SOLUTION\n");
   209          else if (parm->meth == GLP_PRIMAL)
   210             xprintf("PROBLEM HAS UNBOUNDED SOLUTION\n");
   211          else
   212             xprintf("PROBLEM HAS NO DUAL FEASIBLE SOLUTION\n");
   213       }
   214       return;
   215 }
   216 
   217 static int solve_lp(glp_prob *P, const glp_smcp *parm)
   218 {     /* solve LP directly without using the preprocessor */
   219       int ret;
   220       if (!glp_bf_exists(P))
   221       {  ret = glp_factorize(P);
   222          if (ret == 0)
   223             ;
   224          else if (ret == GLP_EBADB)
   225          {  if (parm->msg_lev >= GLP_MSG_ERR)
   226                xprintf("glp_simplex: initial basis is invalid\n");
   227          }
   228          else if (ret == GLP_ESING)
   229          {  if (parm->msg_lev >= GLP_MSG_ERR)
   230                xprintf("glp_simplex: initial basis is singular\n");
   231          }
   232          else if (ret == GLP_ECOND)
   233          {  if (parm->msg_lev >= GLP_MSG_ERR)
   234                xprintf(
   235                   "glp_simplex: initial basis is ill-conditioned\n");
   236          }
   237          else
   238             xassert(ret != ret);
   239          if (ret != 0) goto done;
   240       }
   241       if (parm->meth == GLP_PRIMAL)
   242          ret = spx_primal(P, parm);
   243       else if (parm->meth == GLP_DUALP)
   244       {  ret = spx_dual(P, parm);
   245          if (ret == GLP_EFAIL && P->valid)
   246             ret = spx_primal(P, parm);
   247       }
   248       else if (parm->meth == GLP_DUAL)
   249          ret = spx_dual(P, parm);
   250       else
   251          xassert(parm != parm);
   252 done: return ret;
   253 }
   254 
   255 static int preprocess_and_solve_lp(glp_prob *P, const glp_smcp *parm)
   256 {     /* solve LP using the preprocessor */
   257       NPP *npp;
   258       glp_prob *lp = NULL;
   259       glp_bfcp bfcp;
   260       int ret;
   261       if (parm->msg_lev >= GLP_MSG_ALL)
   262          xprintf("Preprocessing...\n");
   263       /* create preprocessor workspace */
   264       npp = npp_create_wksp();
   265       /* load original problem into the preprocessor workspace */
   266       npp_load_prob(npp, P, GLP_OFF, GLP_SOL, GLP_OFF);
   267       /* process LP prior to applying primal/dual simplex method */
   268       ret = npp_simplex(npp, parm);
   269       if (ret == 0)
   270          ;
   271       else if (ret == GLP_ENOPFS)
   272       {  if (parm->msg_lev >= GLP_MSG_ALL)
   273             xprintf("PROBLEM HAS NO PRIMAL FEASIBLE SOLUTION\n");
   274       }
   275       else if (ret == GLP_ENODFS)
   276       {  if (parm->msg_lev >= GLP_MSG_ALL)
   277             xprintf("PROBLEM HAS NO DUAL FEASIBLE SOLUTION\n");
   278       }
   279       else
   280          xassert(ret != ret);
   281       if (ret != 0) goto done;
   282       /* build transformed LP */
   283       lp = glp_create_prob();
   284       npp_build_prob(npp, lp);
   285       /* if the transformed LP is empty, it has empty solution, which
   286          is optimal */
   287       if (lp->m == 0 && lp->n == 0)
   288       {  lp->pbs_stat = lp->dbs_stat = GLP_FEAS;
   289          lp->obj_val = lp->c0;
   290          if (parm->msg_lev >= GLP_MSG_ON && parm->out_dly == 0)
   291          {  xprintf("~%6d: obj = %17.9e  infeas = %10.3e\n", P->it_cnt,
   292                lp->obj_val, 0.0);
   293          }
   294          if (parm->msg_lev >= GLP_MSG_ALL)
   295             xprintf("OPTIMAL SOLUTION FOUND BY LP PREPROCESSOR\n");
   296          goto post;
   297       }
   298       if (parm->msg_lev >= GLP_MSG_ALL)
   299       {  xprintf("%d row%s, %d column%s, %d non-zero%s\n",
   300             lp->m, lp->m == 1 ? "" : "s", lp->n, lp->n == 1 ? "" : "s",
   301             lp->nnz, lp->nnz == 1 ? "" : "s");
   302       }
   303       /* inherit basis factorization control parameters */
   304       glp_get_bfcp(P, &bfcp);
   305       glp_set_bfcp(lp, &bfcp);
   306       /* scale the transformed problem */
   307       {  ENV *env = get_env_ptr();
   308          int term_out = env->term_out;
   309          if (!term_out || parm->msg_lev < GLP_MSG_ALL)
   310             env->term_out = GLP_OFF;
   311          else
   312             env->term_out = GLP_ON;
   313          glp_scale_prob(lp, GLP_SF_AUTO);
   314          env->term_out = term_out;
   315       }
   316       /* build advanced initial basis */
   317       {  ENV *env = get_env_ptr();
   318          int term_out = env->term_out;
   319          if (!term_out || parm->msg_lev < GLP_MSG_ALL)
   320             env->term_out = GLP_OFF;
   321          else
   322             env->term_out = GLP_ON;
   323          glp_adv_basis(lp, 0);
   324          env->term_out = term_out;
   325       }
   326       /* solve the transformed LP */
   327       lp->it_cnt = P->it_cnt;
   328       ret = solve_lp(lp, parm);
   329       P->it_cnt = lp->it_cnt;
   330       /* only optimal solution can be postprocessed */
   331       if (!(ret == 0 && lp->pbs_stat == GLP_FEAS && lp->dbs_stat ==
   332             GLP_FEAS))
   333       {  if (parm->msg_lev >= GLP_MSG_ERR)
   334             xprintf("glp_simplex: unable to recover undefined or non-op"
   335                "timal solution\n");
   336          if (ret == 0)
   337          {  if (lp->pbs_stat == GLP_NOFEAS)
   338                ret = GLP_ENOPFS;
   339             else if (lp->dbs_stat == GLP_NOFEAS)
   340                ret = GLP_ENODFS;
   341             else
   342                xassert(lp != lp);
   343          }
   344          goto done;
   345       }
   346 post: /* postprocess solution from the transformed LP */
   347       npp_postprocess(npp, lp);
   348       /* the transformed LP is no longer needed */
   349       glp_delete_prob(lp), lp = NULL;
   350       /* store solution to the original problem */
   351       npp_unload_sol(npp, P);
   352       /* the original LP has been successfully solved */
   353       ret = 0;
   354 done: /* delete the transformed LP, if it exists */
   355       if (lp != NULL) glp_delete_prob(lp);
   356       /* delete preprocessor workspace */
   357       npp_delete_wksp(npp);
   358       return ret;
   359 }
   360 
   361 int glp_simplex(glp_prob *P, const glp_smcp *parm)
   362 {     /* solve LP problem with the simplex method */
   363       glp_smcp _parm;
   364       int i, j, ret;
   365       /* check problem object */
   366       if (P == NULL || P->magic != GLP_PROB_MAGIC)
   367          xerror("glp_simplex: P = %p; invalid problem object\n", P);
   368       if (P->tree != NULL && P->tree->reason != 0)
   369          xerror("glp_simplex: operation not allowed\n");
   370       /* check control parameters */
   371       if (parm == NULL)
   372          parm = &_parm, glp_init_smcp((glp_smcp *)parm);
   373       if (!(parm->msg_lev == GLP_MSG_OFF ||
   374             parm->msg_lev == GLP_MSG_ERR ||
   375             parm->msg_lev == GLP_MSG_ON  ||
   376             parm->msg_lev == GLP_MSG_ALL ||
   377             parm->msg_lev == GLP_MSG_DBG))
   378          xerror("glp_simplex: msg_lev = %d; invalid parameter\n",
   379             parm->msg_lev);
   380       if (!(parm->meth == GLP_PRIMAL ||
   381             parm->meth == GLP_DUALP  ||
   382             parm->meth == GLP_DUAL))
   383          xerror("glp_simplex: meth = %d; invalid parameter\n",
   384             parm->meth);
   385       if (!(parm->pricing == GLP_PT_STD ||
   386             parm->pricing == GLP_PT_PSE))
   387          xerror("glp_simplex: pricing = %d; invalid parameter\n",
   388             parm->pricing);
   389       if (!(parm->r_test == GLP_RT_STD ||
   390             parm->r_test == GLP_RT_HAR))
   391          xerror("glp_simplex: r_test = %d; invalid parameter\n",
   392             parm->r_test);
   393       if (!(0.0 < parm->tol_bnd && parm->tol_bnd < 1.0))
   394          xerror("glp_simplex: tol_bnd = %g; invalid parameter\n",
   395             parm->tol_bnd);
   396       if (!(0.0 < parm->tol_dj && parm->tol_dj < 1.0))
   397          xerror("glp_simplex: tol_dj = %g; invalid parameter\n",
   398             parm->tol_dj);
   399       if (!(0.0 < parm->tol_piv && parm->tol_piv < 1.0))
   400          xerror("glp_simplex: tol_piv = %g; invalid parameter\n",
   401             parm->tol_piv);
   402       if (parm->it_lim < 0)
   403          xerror("glp_simplex: it_lim = %d; invalid parameter\n",
   404             parm->it_lim);
   405       if (parm->tm_lim < 0)
   406          xerror("glp_simplex: tm_lim = %d; invalid parameter\n",
   407             parm->tm_lim);
   408       if (parm->out_frq < 1)
   409          xerror("glp_simplex: out_frq = %d; invalid parameter\n",
   410             parm->out_frq);
   411       if (parm->out_dly < 0)
   412          xerror("glp_simplex: out_dly = %d; invalid parameter\n",
   413             parm->out_dly);
   414       if (!(parm->presolve == GLP_ON || parm->presolve == GLP_OFF))
   415          xerror("glp_simplex: presolve = %d; invalid parameter\n",
   416             parm->presolve);
   417       /* basic solution is currently undefined */
   418       P->pbs_stat = P->dbs_stat = GLP_UNDEF;
   419       P->obj_val = 0.0;
   420       P->some = 0;
   421       /* check bounds of double-bounded variables */
   422       for (i = 1; i <= P->m; i++)
   423       {  GLPROW *row = P->row[i];
   424          if (row->type == GLP_DB && row->lb >= row->ub)
   425          {  if (parm->msg_lev >= GLP_MSG_ERR)
   426                xprintf("glp_simplex: row %d: lb = %g, ub = %g; incorrec"
   427                   "t bounds\n", i, row->lb, row->ub);
   428             ret = GLP_EBOUND;
   429             goto done;
   430          }
   431       }
   432       for (j = 1; j <= P->n; j++)
   433       {  GLPCOL *col = P->col[j];
   434          if (col->type == GLP_DB && col->lb >= col->ub)
   435          {  if (parm->msg_lev >= GLP_MSG_ERR)
   436                xprintf("glp_simplex: column %d: lb = %g, ub = %g; incor"
   437                   "rect bounds\n", j, col->lb, col->ub);
   438             ret = GLP_EBOUND;
   439             goto done;
   440          }
   441       }
   442       /* solve LP problem */
   443       if (parm->msg_lev >= GLP_MSG_ALL)
   444       {  xprintf("GLPK Simplex Optimizer, v%s\n", glp_version());
   445          xprintf("%d row%s, %d column%s, %d non-zero%s\n",
   446             P->m, P->m == 1 ? "" : "s", P->n, P->n == 1 ? "" : "s",
   447             P->nnz, P->nnz == 1 ? "" : "s");
   448       }
   449       if (P->nnz == 0)
   450          trivial_lp(P, parm), ret = 0;
   451       else if (!parm->presolve)
   452          ret = solve_lp(P, parm);
   453       else
   454          ret = preprocess_and_solve_lp(P, parm);
   455 done: /* return to the application program */
   456       return ret;
   457 }
   458 
   459 /***********************************************************************
   460 *  NAME
   461 *
   462 *  glp_init_smcp - initialize simplex method control parameters
   463 *
   464 *  SYNOPSIS
   465 *
   466 *  void glp_init_smcp(glp_smcp *parm);
   467 *
   468 *  DESCRIPTION
   469 *
   470 *  The routine glp_init_smcp initializes control parameters, which are
   471 *  used by the simplex solver, with default values.
   472 *
   473 *  Default values of the control parameters are stored in a glp_smcp
   474 *  structure, which the parameter parm points to. */
   475 
   476 void glp_init_smcp(glp_smcp *parm)
   477 {     parm->msg_lev = GLP_MSG_ALL;
   478       parm->meth = GLP_PRIMAL;
   479       parm->pricing = GLP_PT_PSE;
   480       parm->r_test = GLP_RT_HAR;
   481       parm->tol_bnd = 1e-7;
   482       parm->tol_dj = 1e-7;
   483       parm->tol_piv = 1e-10;
   484       parm->obj_ll = -DBL_MAX;
   485       parm->obj_ul = +DBL_MAX;
   486       parm->it_lim = INT_MAX;
   487       parm->tm_lim = INT_MAX;
   488       parm->out_frq = 500;
   489       parm->out_dly = 0;
   490       parm->presolve = GLP_OFF;
   491       return;
   492 }
   493 
   494 /***********************************************************************
   495 *  NAME
   496 *
   497 *  glp_get_status - retrieve generic status of basic solution
   498 *
   499 *  SYNOPSIS
   500 *
   501 *  int glp_get_status(glp_prob *lp);
   502 *
   503 *  RETURNS
   504 *
   505 *  The routine glp_get_status reports the generic status of the basic
   506 *  solution for the specified problem object as follows:
   507 *
   508 *  GLP_OPT    - solution is optimal;
   509 *  GLP_FEAS   - solution is feasible;
   510 *  GLP_INFEAS - solution is infeasible;
   511 *  GLP_NOFEAS - problem has no feasible solution;
   512 *  GLP_UNBND  - problem has unbounded solution;
   513 *  GLP_UNDEF  - solution is undefined. */
   514 
   515 int glp_get_status(glp_prob *lp)
   516 {     int status;
   517       status = glp_get_prim_stat(lp);
   518       switch (status)
   519       {  case GLP_FEAS:
   520             switch (glp_get_dual_stat(lp))
   521             {  case GLP_FEAS:
   522                   status = GLP_OPT;
   523                   break;
   524                case GLP_NOFEAS:
   525                   status = GLP_UNBND;
   526                   break;
   527                case GLP_UNDEF:
   528                case GLP_INFEAS:
   529                   status = status;
   530                   break;
   531                default:
   532                   xassert(lp != lp);
   533             }
   534             break;
   535          case GLP_UNDEF:
   536          case GLP_INFEAS:
   537          case GLP_NOFEAS:
   538             status = status;
   539             break;
   540          default:
   541             xassert(lp != lp);
   542       }
   543       return status;
   544 }
   545 
   546 /***********************************************************************
   547 *  NAME
   548 *
   549 *  glp_get_prim_stat - retrieve status of primal basic solution
   550 *
   551 *  SYNOPSIS
   552 *
   553 *  int glp_get_prim_stat(glp_prob *lp);
   554 *
   555 *  RETURNS
   556 *
   557 *  The routine glp_get_prim_stat reports the status of the primal basic
   558 *  solution for the specified problem object as follows:
   559 *
   560 *  GLP_UNDEF  - primal solution is undefined;
   561 *  GLP_FEAS   - primal solution is feasible;
   562 *  GLP_INFEAS - primal solution is infeasible;
   563 *  GLP_NOFEAS - no primal feasible solution exists. */
   564 
   565 int glp_get_prim_stat(glp_prob *lp)
   566 {     int pbs_stat = lp->pbs_stat;
   567       return pbs_stat;
   568 }
   569 
   570 /***********************************************************************
   571 *  NAME
   572 *
   573 *  glp_get_dual_stat - retrieve status of dual basic solution
   574 *
   575 *  SYNOPSIS
   576 *
   577 *  int glp_get_dual_stat(glp_prob *lp);
   578 *
   579 *  RETURNS
   580 *
   581 *  The routine glp_get_dual_stat reports the status of the dual basic
   582 *  solution for the specified problem object as follows:
   583 *
   584 *  GLP_UNDEF  - dual solution is undefined;
   585 *  GLP_FEAS   - dual solution is feasible;
   586 *  GLP_INFEAS - dual solution is infeasible;
   587 *  GLP_NOFEAS - no dual feasible solution exists. */
   588 
   589 int glp_get_dual_stat(glp_prob *lp)
   590 {     int dbs_stat = lp->dbs_stat;
   591       return dbs_stat;
   592 }
   593 
   594 /***********************************************************************
   595 *  NAME
   596 *
   597 *  glp_get_obj_val - retrieve objective value (basic solution)
   598 *
   599 *  SYNOPSIS
   600 *
   601 *  double glp_get_obj_val(glp_prob *lp);
   602 *
   603 *  RETURNS
   604 *
   605 *  The routine glp_get_obj_val returns value of the objective function
   606 *  for basic solution. */
   607 
   608 double glp_get_obj_val(glp_prob *lp)
   609 {     /*struct LPXCPS *cps = lp->cps;*/
   610       double z;
   611       z = lp->obj_val;
   612       /*if (cps->round && fabs(z) < 1e-9) z = 0.0;*/
   613       return z;
   614 }
   615 
   616 /***********************************************************************
   617 *  NAME
   618 *
   619 *  glp_get_row_stat - retrieve row status
   620 *
   621 *  SYNOPSIS
   622 *
   623 *  int glp_get_row_stat(glp_prob *lp, int i);
   624 *
   625 *  RETURNS
   626 *
   627 *  The routine glp_get_row_stat returns current status assigned to the
   628 *  auxiliary variable associated with i-th row as follows:
   629 *
   630 *  GLP_BS - basic variable;
   631 *  GLP_NL - non-basic variable on its lower bound;
   632 *  GLP_NU - non-basic variable on its upper bound;
   633 *  GLP_NF - non-basic free (unbounded) variable;
   634 *  GLP_NS - non-basic fixed variable. */
   635 
   636 int glp_get_row_stat(glp_prob *lp, int i)
   637 {     if (!(1 <= i && i <= lp->m))
   638          xerror("glp_get_row_stat: i = %d; row number out of range\n",
   639             i);
   640       return lp->row[i]->stat;
   641 }
   642 
   643 /***********************************************************************
   644 *  NAME
   645 *
   646 *  glp_get_row_prim - retrieve row primal value (basic solution)
   647 *
   648 *  SYNOPSIS
   649 *
   650 *  double glp_get_row_prim(glp_prob *lp, int i);
   651 *
   652 *  RETURNS
   653 *
   654 *  The routine glp_get_row_prim returns primal value of the auxiliary
   655 *  variable associated with i-th row. */
   656 
   657 double glp_get_row_prim(glp_prob *lp, int i)
   658 {     /*struct LPXCPS *cps = lp->cps;*/
   659       double prim;
   660       if (!(1 <= i && i <= lp->m))
   661          xerror("glp_get_row_prim: i = %d; row number out of range\n",
   662             i);
   663       prim = lp->row[i]->prim;
   664       /*if (cps->round && fabs(prim) < 1e-9) prim = 0.0;*/
   665       return prim;
   666 }
   667 
   668 /***********************************************************************
   669 *  NAME
   670 *
   671 *  glp_get_row_dual - retrieve row dual value (basic solution)
   672 *
   673 *  SYNOPSIS
   674 *
   675 *  double glp_get_row_dual(glp_prob *lp, int i);
   676 *
   677 *  RETURNS
   678 *
   679 *  The routine glp_get_row_dual returns dual value (i.e. reduced cost)
   680 *  of the auxiliary variable associated with i-th row. */
   681 
   682 double glp_get_row_dual(glp_prob *lp, int i)
   683 {     /*struct LPXCPS *cps = lp->cps;*/
   684       double dual;
   685       if (!(1 <= i && i <= lp->m))
   686          xerror("glp_get_row_dual: i = %d; row number out of range\n",
   687             i);
   688       dual = lp->row[i]->dual;
   689       /*if (cps->round && fabs(dual) < 1e-9) dual = 0.0;*/
   690       return dual;
   691 }
   692 
   693 /***********************************************************************
   694 *  NAME
   695 *
   696 *  glp_get_col_stat - retrieve column status
   697 *
   698 *  SYNOPSIS
   699 *
   700 *  int glp_get_col_stat(glp_prob *lp, int j);
   701 *
   702 *  RETURNS
   703 *
   704 *  The routine glp_get_col_stat returns current status assigned to the
   705 *  structural variable associated with j-th column as follows:
   706 *
   707 *  GLP_BS - basic variable;
   708 *  GLP_NL - non-basic variable on its lower bound;
   709 *  GLP_NU - non-basic variable on its upper bound;
   710 *  GLP_NF - non-basic free (unbounded) variable;
   711 *  GLP_NS - non-basic fixed variable. */
   712 
   713 int glp_get_col_stat(glp_prob *lp, int j)
   714 {     if (!(1 <= j && j <= lp->n))
   715          xerror("glp_get_col_stat: j = %d; column number out of range\n"
   716             , j);
   717       return lp->col[j]->stat;
   718 }
   719 
   720 /***********************************************************************
   721 *  NAME
   722 *
   723 *  glp_get_col_prim - retrieve column primal value (basic solution)
   724 *
   725 *  SYNOPSIS
   726 *
   727 *  double glp_get_col_prim(glp_prob *lp, int j);
   728 *
   729 *  RETURNS
   730 *
   731 *  The routine glp_get_col_prim returns primal value of the structural
   732 *  variable associated with j-th column. */
   733 
   734 double glp_get_col_prim(glp_prob *lp, int j)
   735 {     /*struct LPXCPS *cps = lp->cps;*/
   736       double prim;
   737       if (!(1 <= j && j <= lp->n))
   738          xerror("glp_get_col_prim: j = %d; column number out of range\n"
   739             , j);
   740       prim = lp->col[j]->prim;
   741       /*if (cps->round && fabs(prim) < 1e-9) prim = 0.0;*/
   742       return prim;
   743 }
   744 
   745 /***********************************************************************
   746 *  NAME
   747 *
   748 *  glp_get_col_dual - retrieve column dual value (basic solution)
   749 *
   750 *  SYNOPSIS
   751 *
   752 *  double glp_get_col_dual(glp_prob *lp, int j);
   753 *
   754 *  RETURNS
   755 *
   756 *  The routine glp_get_col_dual returns dual value (i.e. reduced cost)
   757 *  of the structural variable associated with j-th column. */
   758 
   759 double glp_get_col_dual(glp_prob *lp, int j)
   760 {     /*struct LPXCPS *cps = lp->cps;*/
   761       double dual;
   762       if (!(1 <= j && j <= lp->n))
   763          xerror("glp_get_col_dual: j = %d; column number out of range\n"
   764             , j);
   765       dual = lp->col[j]->dual;
   766       /*if (cps->round && fabs(dual) < 1e-9) dual = 0.0;*/
   767       return dual;
   768 }
   769 
   770 /***********************************************************************
   771 *  NAME
   772 *
   773 *  glp_get_unbnd_ray - determine variable causing unboundedness
   774 *
   775 *  SYNOPSIS
   776 *
   777 *  int glp_get_unbnd_ray(glp_prob *lp);
   778 *
   779 *  RETURNS
   780 *
   781 *  The routine glp_get_unbnd_ray returns the number k of a variable,
   782 *  which causes primal or dual unboundedness. If 1 <= k <= m, it is
   783 *  k-th auxiliary variable, and if m+1 <= k <= m+n, it is (k-m)-th
   784 *  structural variable, where m is the number of rows, n is the number
   785 *  of columns in the problem object. If such variable is not defined,
   786 *  the routine returns 0.
   787 *
   788 *  COMMENTS
   789 *
   790 *  If it is not exactly known which version of the simplex solver
   791 *  detected unboundedness, i.e. whether the unboundedness is primal or
   792 *  dual, it is sufficient to check the status of the variable reported
   793 *  with the routine glp_get_row_stat or glp_get_col_stat. If the
   794 *  variable is non-basic, the unboundedness is primal, otherwise, if
   795 *  the variable is basic, the unboundedness is dual (the latter case
   796 *  means that the problem has no primal feasible dolution). */
   797 
   798 int glp_get_unbnd_ray(glp_prob *lp)
   799 {     int k;
   800       k = lp->some;
   801       xassert(k >= 0);
   802       if (k > lp->m + lp->n) k = 0;
   803       return k;
   804 }
   805 
   806 /* eof */