src/glpapi11.c
changeset 2 4c8956a7bdf4
equal deleted inserted replaced
-1:000000000000 0:9a3de5d0021a
       
     1 /* glpapi11.c (utility 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 "glpapi.h"
       
    26 
       
    27 int glp_print_sol(glp_prob *P, const char *fname)
       
    28 {     /* write basic solution in printable format */
       
    29       XFILE *fp;
       
    30       GLPROW *row;
       
    31       GLPCOL *col;
       
    32       int i, j, t, ae_ind, re_ind, ret;
       
    33       double ae_max, re_max;
       
    34       xprintf("Writing basic solution to `%s'...\n", fname);
       
    35       fp = xfopen(fname, "w");
       
    36       if (fp == NULL)
       
    37       {  xprintf("Unable to create `%s' - %s\n", fname, xerrmsg());
       
    38          ret = 1;
       
    39          goto done;
       
    40       }
       
    41       xfprintf(fp, "%-12s%s\n", "Problem:",
       
    42          P->name == NULL ? "" : P->name);
       
    43       xfprintf(fp, "%-12s%d\n", "Rows:", P->m);
       
    44       xfprintf(fp, "%-12s%d\n", "Columns:", P->n);
       
    45       xfprintf(fp, "%-12s%d\n", "Non-zeros:", P->nnz);
       
    46       t = glp_get_status(P);
       
    47       xfprintf(fp, "%-12s%s\n", "Status:",
       
    48          t == GLP_OPT    ? "OPTIMAL" :
       
    49          t == GLP_FEAS   ? "FEASIBLE" :
       
    50          t == GLP_INFEAS ? "INFEASIBLE (INTERMEDIATE)" :
       
    51          t == GLP_NOFEAS ? "INFEASIBLE (FINAL)" :
       
    52          t == GLP_UNBND  ? "UNBOUNDED" :
       
    53          t == GLP_UNDEF  ? "UNDEFINED" : "???");
       
    54       xfprintf(fp, "%-12s%s%s%.10g (%s)\n", "Objective:",
       
    55          P->obj == NULL ? "" : P->obj,
       
    56          P->obj == NULL ? "" : " = ", P->obj_val,
       
    57          P->dir == GLP_MIN ? "MINimum" :
       
    58          P->dir == GLP_MAX ? "MAXimum" : "???");
       
    59       xfprintf(fp, "\n");
       
    60       xfprintf(fp, "   No.   Row name   St   Activity     Lower bound  "
       
    61          " Upper bound    Marginal\n");
       
    62       xfprintf(fp, "------ ------------ -- ------------- ------------- "
       
    63          "------------- -------------\n");
       
    64       for (i = 1; i <= P->m; i++)
       
    65       {  row = P->row[i];
       
    66          xfprintf(fp, "%6d ", i);
       
    67          if (row->name == NULL || strlen(row->name) <= 12)
       
    68             xfprintf(fp, "%-12s ", row->name == NULL ? "" : row->name);
       
    69          else
       
    70             xfprintf(fp, "%s\n%20s", row->name, "");
       
    71          xfprintf(fp, "%s ",
       
    72             row->stat == GLP_BS ? "B " :
       
    73             row->stat == GLP_NL ? "NL" :
       
    74             row->stat == GLP_NU ? "NU" :
       
    75             row->stat == GLP_NF ? "NF" :
       
    76             row->stat == GLP_NS ? "NS" : "??");
       
    77          xfprintf(fp, "%13.6g ",
       
    78             fabs(row->prim) <= 1e-9 ? 0.0 : row->prim);
       
    79          if (row->type == GLP_LO || row->type == GLP_DB ||
       
    80              row->type == GLP_FX)
       
    81             xfprintf(fp, "%13.6g ", row->lb);
       
    82          else
       
    83             xfprintf(fp, "%13s ", "");
       
    84          if (row->type == GLP_UP || row->type == GLP_DB)
       
    85             xfprintf(fp, "%13.6g ", row->ub);
       
    86          else
       
    87             xfprintf(fp, "%13s ", row->type == GLP_FX ? "=" : "");
       
    88          if (row->stat != GLP_BS)
       
    89          {  if (fabs(row->dual) <= 1e-9)
       
    90                xfprintf(fp, "%13s", "< eps");
       
    91             else
       
    92                xfprintf(fp, "%13.6g ", row->dual);
       
    93          }
       
    94          xfprintf(fp, "\n");
       
    95       }
       
    96       xfprintf(fp, "\n");
       
    97       xfprintf(fp, "   No. Column name  St   Activity     Lower bound  "
       
    98          " Upper bound    Marginal\n");
       
    99       xfprintf(fp, "------ ------------ -- ------------- ------------- "
       
   100          "------------- -------------\n");
       
   101       for (j = 1; j <= P->n; j++)
       
   102       {  col = P->col[j];
       
   103          xfprintf(fp, "%6d ", j);
       
   104          if (col->name == NULL || strlen(col->name) <= 12)
       
   105             xfprintf(fp, "%-12s ", col->name == NULL ? "" : col->name);
       
   106          else
       
   107             xfprintf(fp, "%s\n%20s", col->name, "");
       
   108          xfprintf(fp, "%s ",
       
   109             col->stat == GLP_BS ? "B " :
       
   110             col->stat == GLP_NL ? "NL" :
       
   111             col->stat == GLP_NU ? "NU" :
       
   112             col->stat == GLP_NF ? "NF" :
       
   113             col->stat == GLP_NS ? "NS" : "??");
       
   114          xfprintf(fp, "%13.6g ",
       
   115             fabs(col->prim) <= 1e-9 ? 0.0 : col->prim);
       
   116          if (col->type == GLP_LO || col->type == GLP_DB ||
       
   117              col->type == GLP_FX)
       
   118             xfprintf(fp, "%13.6g ", col->lb);
       
   119          else
       
   120             xfprintf(fp, "%13s ", "");
       
   121          if (col->type == GLP_UP || col->type == GLP_DB)
       
   122             xfprintf(fp, "%13.6g ", col->ub);
       
   123          else
       
   124             xfprintf(fp, "%13s ", col->type == GLP_FX ? "=" : "");
       
   125          if (col->stat != GLP_BS)
       
   126          {  if (fabs(col->dual) <= 1e-9)
       
   127                xfprintf(fp, "%13s", "< eps");
       
   128             else
       
   129                xfprintf(fp, "%13.6g ", col->dual);
       
   130          }
       
   131          xfprintf(fp, "\n");
       
   132       }
       
   133       xfprintf(fp, "\n");
       
   134       xfprintf(fp, "Karush-Kuhn-Tucker optimality conditions:\n");
       
   135       xfprintf(fp, "\n");
       
   136       _glp_check_kkt(P, GLP_SOL, GLP_KKT_PE, &ae_max, &ae_ind, &re_max,
       
   137          &re_ind);
       
   138       xfprintf(fp, "KKT.PE: max.abs.err = %.2e on row %d\n",
       
   139          ae_max, ae_ind);
       
   140       xfprintf(fp, "        max.rel.err = %.2e on row %d\n",
       
   141          re_max, re_ind);
       
   142       xfprintf(fp, "%8s%s\n", "",
       
   143          re_max <= 1e-9 ? "High quality" :
       
   144          re_max <= 1e-6 ? "Medium quality" :
       
   145          re_max <= 1e-3 ? "Low quality" : "PRIMAL SOLUTION IS WRONG");
       
   146       xfprintf(fp, "\n");
       
   147       _glp_check_kkt(P, GLP_SOL, GLP_KKT_PB, &ae_max, &ae_ind, &re_max,
       
   148          &re_ind);
       
   149       xfprintf(fp, "KKT.PB: max.abs.err = %.2e on %s %d\n",
       
   150             ae_max, ae_ind <= P->m ? "row" : "column",
       
   151             ae_ind <= P->m ? ae_ind : ae_ind - P->m);
       
   152       xfprintf(fp, "        max.rel.err = %.2e on %s %d\n",
       
   153             re_max, re_ind <= P->m ? "row" : "column",
       
   154             re_ind <= P->m ? re_ind : re_ind - P->m);
       
   155       xfprintf(fp, "%8s%s\n", "",
       
   156          re_max <= 1e-9 ? "High quality" :
       
   157          re_max <= 1e-6 ? "Medium quality" :
       
   158          re_max <= 1e-3 ? "Low quality" : "PRIMAL SOLUTION IS INFEASIBL"
       
   159             "E");
       
   160       xfprintf(fp, "\n");
       
   161       _glp_check_kkt(P, GLP_SOL, GLP_KKT_DE, &ae_max, &ae_ind, &re_max,
       
   162          &re_ind);
       
   163       xfprintf(fp, "KKT.DE: max.abs.err = %.2e on column %d\n",
       
   164          ae_max, ae_ind == 0 ? 0 : ae_ind - P->m);
       
   165       xfprintf(fp, "        max.rel.err = %.2e on column %d\n",
       
   166          re_max, re_ind == 0 ? 0 : re_ind - P->m);
       
   167       xfprintf(fp, "%8s%s\n", "",
       
   168          re_max <= 1e-9 ? "High quality" :
       
   169          re_max <= 1e-6 ? "Medium quality" :
       
   170          re_max <= 1e-3 ? "Low quality" : "DUAL SOLUTION IS WRONG");
       
   171       xfprintf(fp, "\n");
       
   172       _glp_check_kkt(P, GLP_SOL, GLP_KKT_DB, &ae_max, &ae_ind, &re_max,
       
   173          &re_ind);
       
   174       xfprintf(fp, "KKT.DB: max.abs.err = %.2e on %s %d\n",
       
   175             ae_max, ae_ind <= P->m ? "row" : "column",
       
   176             ae_ind <= P->m ? ae_ind : ae_ind - P->m);
       
   177       xfprintf(fp, "        max.rel.err = %.2e on %s %d\n",
       
   178             re_max, re_ind <= P->m ? "row" : "column",
       
   179             re_ind <= P->m ? re_ind : re_ind - P->m);
       
   180       xfprintf(fp, "%8s%s\n", "",
       
   181          re_max <= 1e-9 ? "High quality" :
       
   182          re_max <= 1e-6 ? "Medium quality" :
       
   183          re_max <= 1e-3 ? "Low quality" : "DUAL SOLUTION IS INFEASIBLE")
       
   184             ;
       
   185       xfprintf(fp, "\n");
       
   186       xfprintf(fp, "End of output\n");
       
   187       xfflush(fp);
       
   188       if (xferror(fp))
       
   189       {  xprintf("Write error on `%s' - %s\n", fname, xerrmsg());
       
   190          ret = 1;
       
   191          goto done;
       
   192       }
       
   193       ret = 0;
       
   194 done: if (fp != NULL) xfclose(fp);
       
   195       return ret;
       
   196 }
       
   197 
       
   198 /***********************************************************************
       
   199 *  NAME
       
   200 *
       
   201 *  glp_read_sol - read basic solution from text file
       
   202 *
       
   203 *  SYNOPSIS
       
   204 *
       
   205 *  int glp_read_sol(glp_prob *lp, const char *fname);
       
   206 *
       
   207 *  DESCRIPTION
       
   208 *
       
   209 *  The routine glp_read_sol reads basic solution from a text file whose
       
   210 *  name is specified by the parameter fname into the problem object.
       
   211 *
       
   212 *  For the file format see description of the routine glp_write_sol.
       
   213 *
       
   214 *  RETURNS
       
   215 *
       
   216 *  On success the routine returns zero, otherwise non-zero. */
       
   217 
       
   218 int glp_read_sol(glp_prob *lp, const char *fname)
       
   219 {     glp_data *data;
       
   220       jmp_buf jump;
       
   221       int i, j, k, ret = 0;
       
   222       xprintf("Reading basic solution from `%s'...\n", fname);
       
   223       data = glp_sdf_open_file(fname);
       
   224       if (data == NULL)
       
   225       {  ret = 1;
       
   226          goto done;
       
   227       }
       
   228       if (setjmp(jump))
       
   229       {  ret = 1;
       
   230          goto done;
       
   231       }
       
   232       glp_sdf_set_jump(data, jump);
       
   233       /* number of rows, number of columns */
       
   234       k = glp_sdf_read_int(data);
       
   235       if (k != lp->m)
       
   236          glp_sdf_error(data, "wrong number of rows\n");
       
   237       k = glp_sdf_read_int(data);
       
   238       if (k != lp->n)
       
   239          glp_sdf_error(data, "wrong number of columns\n");
       
   240       /* primal status, dual status, objective value */
       
   241       k = glp_sdf_read_int(data);
       
   242       if (!(k == GLP_UNDEF || k == GLP_FEAS || k == GLP_INFEAS ||
       
   243             k == GLP_NOFEAS))
       
   244          glp_sdf_error(data, "invalid primal status\n");
       
   245       lp->pbs_stat = k;
       
   246       k = glp_sdf_read_int(data);
       
   247       if (!(k == GLP_UNDEF || k == GLP_FEAS || k == GLP_INFEAS ||
       
   248             k == GLP_NOFEAS))
       
   249          glp_sdf_error(data, "invalid dual status\n");
       
   250       lp->dbs_stat = k;
       
   251       lp->obj_val = glp_sdf_read_num(data);
       
   252       /* rows (auxiliary variables) */
       
   253       for (i = 1; i <= lp->m; i++)
       
   254       {  GLPROW *row = lp->row[i];
       
   255          /* status, primal value, dual value */
       
   256          k = glp_sdf_read_int(data);
       
   257          if (!(k == GLP_BS || k == GLP_NL || k == GLP_NU ||
       
   258                k == GLP_NF || k == GLP_NS))
       
   259             glp_sdf_error(data, "invalid row status\n");
       
   260          glp_set_row_stat(lp, i, k);
       
   261          row->prim = glp_sdf_read_num(data);
       
   262          row->dual = glp_sdf_read_num(data);
       
   263       }
       
   264       /* columns (structural variables) */
       
   265       for (j = 1; j <= lp->n; j++)
       
   266       {  GLPCOL *col = lp->col[j];
       
   267          /* status, primal value, dual value */
       
   268          k = glp_sdf_read_int(data);
       
   269          if (!(k == GLP_BS || k == GLP_NL || k == GLP_NU ||
       
   270                k == GLP_NF || k == GLP_NS))
       
   271             glp_sdf_error(data, "invalid column status\n");
       
   272          glp_set_col_stat(lp, j, k);
       
   273          col->prim = glp_sdf_read_num(data);
       
   274          col->dual = glp_sdf_read_num(data);
       
   275       }
       
   276       xprintf("%d lines were read\n", glp_sdf_line(data));
       
   277 done: if (ret) lp->pbs_stat = lp->dbs_stat = GLP_UNDEF;
       
   278       if (data != NULL) glp_sdf_close_file(data);
       
   279       return ret;
       
   280 }
       
   281 
       
   282 /***********************************************************************
       
   283 *  NAME
       
   284 *
       
   285 *  glp_write_sol - write basic solution to text file
       
   286 *
       
   287 *  SYNOPSIS
       
   288 *
       
   289 *  int glp_write_sol(glp_prob *lp, const char *fname);
       
   290 *
       
   291 *  DESCRIPTION
       
   292 *
       
   293 *  The routine glp_write_sol writes the current basic solution to a
       
   294 *  text file whose name is specified by the parameter fname. This file
       
   295 *  can be read back with the routine glp_read_sol.
       
   296 *
       
   297 *  RETURNS
       
   298 *
       
   299 *  On success the routine returns zero, otherwise non-zero.
       
   300 *
       
   301 *  FILE FORMAT
       
   302 *
       
   303 *  The file created by the routine glp_write_sol is a plain text file,
       
   304 *  which contains the following information:
       
   305 *
       
   306 *     m n
       
   307 *     p_stat d_stat obj_val
       
   308 *     r_stat[1] r_prim[1] r_dual[1]
       
   309 *     . . .
       
   310 *     r_stat[m] r_prim[m] r_dual[m]
       
   311 *     c_stat[1] c_prim[1] c_dual[1]
       
   312 *     . . .
       
   313 *     c_stat[n] c_prim[n] c_dual[n]
       
   314 *
       
   315 *  where:
       
   316 *  m is the number of rows (auxiliary variables);
       
   317 *  n is the number of columns (structural variables);
       
   318 *  p_stat is the primal status of the basic solution (GLP_UNDEF = 1,
       
   319 *     GLP_FEAS = 2, GLP_INFEAS = 3, or GLP_NOFEAS = 4);
       
   320 *  d_stat is the dual status of the basic solution (GLP_UNDEF = 1,
       
   321 *     GLP_FEAS = 2, GLP_INFEAS = 3, or GLP_NOFEAS = 4);
       
   322 *  obj_val is the objective value;
       
   323 *  r_stat[i], i = 1,...,m, is the status of i-th row (GLP_BS = 1,
       
   324 *     GLP_NL = 2, GLP_NU = 3, GLP_NF = 4, or GLP_NS = 5);
       
   325 *  r_prim[i], i = 1,...,m, is the primal value of i-th row;
       
   326 *  r_dual[i], i = 1,...,m, is the dual value of i-th row;
       
   327 *  c_stat[j], j = 1,...,n, is the status of j-th column (GLP_BS = 1,
       
   328 *     GLP_NL = 2, GLP_NU = 3, GLP_NF = 4, or GLP_NS = 5);
       
   329 *  c_prim[j], j = 1,...,n, is the primal value of j-th column;
       
   330 *  c_dual[j], j = 1,...,n, is the dual value of j-th column. */
       
   331 
       
   332 int glp_write_sol(glp_prob *lp, const char *fname)
       
   333 {     XFILE *fp;
       
   334       int i, j, ret = 0;
       
   335       xprintf("Writing basic solution to `%s'...\n", fname);
       
   336       fp = xfopen(fname, "w");
       
   337       if (fp == NULL)
       
   338       {  xprintf("Unable to create `%s' - %s\n", fname, xerrmsg());
       
   339          ret = 1;
       
   340          goto done;
       
   341       }
       
   342       /* number of rows, number of columns */
       
   343       xfprintf(fp, "%d %d\n", lp->m, lp->n);
       
   344       /* primal status, dual status, objective value */
       
   345       xfprintf(fp, "%d %d %.*g\n", lp->pbs_stat, lp->dbs_stat, DBL_DIG,
       
   346          lp->obj_val);
       
   347       /* rows (auxiliary variables) */
       
   348       for (i = 1; i <= lp->m; i++)
       
   349       {  GLPROW *row = lp->row[i];
       
   350          /* status, primal value, dual value */
       
   351          xfprintf(fp, "%d %.*g %.*g\n", row->stat, DBL_DIG, row->prim,
       
   352             DBL_DIG, row->dual);
       
   353       }
       
   354       /* columns (structural variables) */
       
   355       for (j = 1; j <= lp->n; j++)
       
   356       {  GLPCOL *col = lp->col[j];
       
   357          /* status, primal value, dual value */
       
   358          xfprintf(fp, "%d %.*g %.*g\n", col->stat, DBL_DIG, col->prim,
       
   359             DBL_DIG, col->dual);
       
   360       }
       
   361       xfflush(fp);
       
   362       if (xferror(fp))
       
   363       {  xprintf("Write error on `%s' - %s\n", fname, xerrmsg());
       
   364          ret = 1;
       
   365          goto done;
       
   366       }
       
   367       xprintf("%d lines were written\n", 2 + lp->m + lp->n);
       
   368 done: if (fp != NULL) xfclose(fp);
       
   369       return ret;
       
   370 }
       
   371 
       
   372 /**********************************************************************/
       
   373 
       
   374 static char *format(char buf[13+1], double x)
       
   375 {     /* format floating-point number in MPS/360-like style */
       
   376       if (x == -DBL_MAX)
       
   377          strcpy(buf, "         -Inf");
       
   378       else if (x == +DBL_MAX)
       
   379          strcpy(buf, "         +Inf");
       
   380       else if (fabs(x) <= 999999.99998)
       
   381       {  sprintf(buf, "%13.5f", x);
       
   382 #if 1
       
   383          if (strcmp(buf, "      0.00000") == 0 ||
       
   384              strcmp(buf, "     -0.00000") == 0)
       
   385             strcpy(buf, "       .     ");
       
   386          else if (memcmp(buf, "      0.", 8) == 0)
       
   387             memcpy(buf, "       .", 8);
       
   388          else if (memcmp(buf, "     -0.", 8) == 0)
       
   389             memcpy(buf, "      -.", 8);
       
   390 #endif
       
   391       }
       
   392       else
       
   393          sprintf(buf, "%13.6g", x);
       
   394       return buf;
       
   395 }
       
   396 
       
   397 int glp_print_ranges(glp_prob *P, int len, const int list[],
       
   398       int flags, const char *fname)
       
   399 {     /* print sensitivity analysis report */
       
   400       XFILE *fp = NULL;
       
   401       GLPROW *row;
       
   402       GLPCOL *col;
       
   403       int m, n, pass, k, t, numb, type, stat, var1, var2, count, page,
       
   404          ret;
       
   405       double lb, ub, slack, coef, prim, dual, value1, value2, coef1,
       
   406          coef2, obj1, obj2;
       
   407       const char *name, *limit;
       
   408       char buf[13+1];
       
   409       /* sanity checks */
       
   410       if (P == NULL || P->magic != GLP_PROB_MAGIC)
       
   411          xerror("glp_print_ranges: P = %p; invalid problem object\n",
       
   412             P);
       
   413       m = P->m, n = P->n;
       
   414       if (len < 0)
       
   415          xerror("glp_print_ranges: len = %d; invalid list length\n",
       
   416             len);
       
   417       if (len > 0)
       
   418       {  if (list == NULL)
       
   419             xerror("glp_print_ranges: list = %p: invalid parameter\n",
       
   420                list);
       
   421          for (t = 1; t <= len; t++)
       
   422          {  k = list[t];
       
   423             if (!(1 <= k && k <= m+n))
       
   424                xerror("glp_print_ranges: list[%d] = %d; row/column numb"
       
   425                   "er out of range\n", t, k);
       
   426          }
       
   427       }
       
   428       if (flags != 0)
       
   429          xerror("glp_print_ranges: flags = %d; invalid parameter\n",
       
   430             flags);
       
   431       if (fname == NULL)
       
   432          xerror("glp_print_ranges: fname = %p; invalid parameter\n",
       
   433             fname);
       
   434       if (glp_get_status(P) != GLP_OPT)
       
   435       {  xprintf("glp_print_ranges: optimal basic solution required\n");
       
   436          ret = 1;
       
   437          goto done;
       
   438       }
       
   439       if (!glp_bf_exists(P))
       
   440       {  xprintf("glp_print_ranges: basis factorization required\n");
       
   441          ret = 2;
       
   442          goto done;
       
   443       }
       
   444       /* start reporting */
       
   445       xprintf("Write sensitivity analysis report to `%s'...\n", fname);
       
   446       fp = xfopen(fname, "w");
       
   447       if (fp == NULL)
       
   448       {  xprintf("Unable to create `%s' - %s\n", fname, xerrmsg());
       
   449          ret = 3;
       
   450          goto done;
       
   451       }
       
   452       page = count = 0;
       
   453       for (pass = 1; pass <= 2; pass++)
       
   454       for (t = 1; t <= (len == 0 ? m+n : len); t++)
       
   455       {  if (t == 1) count = 0;
       
   456          k = (len == 0 ? t : list[t]);
       
   457          if (pass == 1 && k > m || pass == 2 && k <= m)
       
   458             continue;
       
   459          if (count == 0)
       
   460          {  xfprintf(fp, "GLPK %-4s - SENSITIVITY ANALYSIS REPORT%73sPa"
       
   461                "ge%4d\n", glp_version(), "", ++page);
       
   462             xfprintf(fp, "\n");
       
   463             xfprintf(fp, "%-12s%s\n", "Problem:",
       
   464                P->name == NULL ? "" : P->name);
       
   465             xfprintf(fp, "%-12s%s%s%.10g (%s)\n", "Objective:",
       
   466                P->obj == NULL ? "" : P->obj,
       
   467                P->obj == NULL ? "" : " = ", P->obj_val,
       
   468                P->dir == GLP_MIN ? "MINimum" :
       
   469                P->dir == GLP_MAX ? "MAXimum" : "???");
       
   470             xfprintf(fp, "\n");
       
   471             xfprintf(fp, "%6s %-12s %2s %13s %13s %13s  %13s %13s %13s "
       
   472                "%s\n", "No.", pass == 1 ? "Row name" : "Column name",
       
   473                "St", "Activity", pass == 1 ? "Slack" : "Obj coef",
       
   474                "Lower bound", "Activity", "Obj coef", "Obj value at",
       
   475                "Limiting");
       
   476             xfprintf(fp, "%6s %-12s %2s %13s %13s %13s  %13s %13s %13s "
       
   477                "%s\n", "", "", "", "", "Marginal", "Upper bound",
       
   478                "range", "range", "break point", "variable");
       
   479             xfprintf(fp, "------ ------------ -- ------------- --------"
       
   480                "----- -------------  ------------- ------------- ------"
       
   481                "------- ------------\n");
       
   482          }
       
   483          if (pass == 1)
       
   484          {  numb = k;
       
   485             xassert(1 <= numb && numb <= m);
       
   486             row = P->row[numb];
       
   487             name = row->name;
       
   488             type = row->type;
       
   489             lb = glp_get_row_lb(P, numb);
       
   490             ub = glp_get_row_ub(P, numb);
       
   491             coef = 0.0;
       
   492             stat = row->stat;
       
   493             prim = row->prim;
       
   494             if (type == GLP_FR)
       
   495                slack = - prim;
       
   496             else if (type == GLP_LO)
       
   497                slack = lb - prim;
       
   498             else if (type == GLP_UP || type == GLP_DB || type == GLP_FX)
       
   499                slack = ub - prim;
       
   500             dual = row->dual;
       
   501          }
       
   502          else
       
   503          {  numb = k - m;
       
   504             xassert(1 <= numb && numb <= n);
       
   505             col = P->col[numb];
       
   506             name = col->name;
       
   507             lb = glp_get_col_lb(P, numb);
       
   508             ub = glp_get_col_ub(P, numb);
       
   509             coef = col->coef;
       
   510             stat = col->stat;
       
   511             prim = col->prim;
       
   512             slack = 0.0;
       
   513             dual = col->dual;
       
   514          }
       
   515          if (stat != GLP_BS)
       
   516          {  glp_analyze_bound(P, k, &value1, &var1, &value2, &var2);
       
   517             if (stat == GLP_NF)
       
   518                coef1 = coef2 = coef;
       
   519             else if (stat == GLP_NS)
       
   520                coef1 = -DBL_MAX, coef2 = +DBL_MAX;
       
   521             else if (stat == GLP_NL && P->dir == GLP_MIN ||
       
   522                      stat == GLP_NU && P->dir == GLP_MAX)
       
   523                coef1 = coef - dual, coef2 = +DBL_MAX;
       
   524             else
       
   525                coef1 = -DBL_MAX, coef2 = coef - dual;
       
   526             if (value1 == -DBL_MAX)
       
   527             {  if (dual < -1e-9)
       
   528                   obj1 = +DBL_MAX;
       
   529                else if (dual > +1e-9)
       
   530                   obj1 = -DBL_MAX;
       
   531                else
       
   532                   obj1 = P->obj_val;
       
   533             }
       
   534             else
       
   535                obj1 = P->obj_val + dual * (value1 - prim);
       
   536             if (value2 == +DBL_MAX)
       
   537             {  if (dual < -1e-9)
       
   538                   obj2 = -DBL_MAX;
       
   539                else if (dual > +1e-9)
       
   540                   obj2 = +DBL_MAX;
       
   541                else
       
   542                   obj2 = P->obj_val;
       
   543             }
       
   544             else
       
   545                obj2 = P->obj_val + dual * (value2 - prim);
       
   546          }
       
   547          else
       
   548          {  glp_analyze_coef(P, k, &coef1, &var1, &value1, &coef2,
       
   549                &var2, &value2);
       
   550             if (coef1 == -DBL_MAX)
       
   551             {  if (prim < -1e-9)
       
   552                   obj1 = +DBL_MAX;
       
   553                else if (prim > +1e-9)
       
   554                   obj1 = -DBL_MAX;
       
   555                else
       
   556                   obj1 = P->obj_val;
       
   557             }
       
   558             else
       
   559                obj1 = P->obj_val + (coef1 - coef) * prim;
       
   560             if (coef2 == +DBL_MAX)
       
   561             {  if (prim < -1e-9)
       
   562                   obj2 = -DBL_MAX;
       
   563                else if (prim > +1e-9)
       
   564                   obj2 = +DBL_MAX;
       
   565                else
       
   566                   obj2 = P->obj_val;
       
   567             }
       
   568             else
       
   569                obj2 = P->obj_val + (coef2 - coef) * prim;
       
   570          }
       
   571          /*** first line ***/
       
   572          /* row/column number */
       
   573          xfprintf(fp, "%6d", numb);
       
   574          /* row/column name */
       
   575          xfprintf(fp, " %-12.12s", name == NULL ? "" : name);
       
   576          if (name != NULL && strlen(name) > 12)
       
   577             xfprintf(fp, "%s\n%6s %12s", name+12, "", "");
       
   578          /* row/column status */
       
   579          xfprintf(fp, " %2s",
       
   580             stat == GLP_BS ? "BS" : stat == GLP_NL ? "NL" :
       
   581             stat == GLP_NU ? "NU" : stat == GLP_NF ? "NF" :
       
   582             stat == GLP_NS ? "NS" : "??");
       
   583          /* row/column activity */
       
   584          xfprintf(fp, " %s", format(buf, prim));
       
   585          /* row slack, column objective coefficient */
       
   586          xfprintf(fp, " %s", format(buf, k <= m ? slack : coef));
       
   587          /* row/column lower bound */
       
   588          xfprintf(fp, " %s", format(buf, lb));
       
   589          /* row/column activity range */
       
   590          xfprintf(fp, "  %s", format(buf, value1));
       
   591          /* row/column objective coefficient range */
       
   592          xfprintf(fp, " %s", format(buf, coef1));
       
   593          /* objective value at break point */
       
   594          xfprintf(fp, " %s", format(buf, obj1));
       
   595          /* limiting variable name */
       
   596          if (var1 != 0)
       
   597          {  if (var1 <= m)
       
   598                limit = glp_get_row_name(P, var1);
       
   599             else
       
   600                limit = glp_get_col_name(P, var1 - m);
       
   601             if (limit != NULL)
       
   602                xfprintf(fp, " %s", limit);
       
   603          }
       
   604          xfprintf(fp, "\n");
       
   605          /*** second line ***/
       
   606          xfprintf(fp, "%6s %-12s %2s %13s", "", "", "", "");
       
   607          /* row/column reduced cost */
       
   608          xfprintf(fp, " %s", format(buf, dual));
       
   609          /* row/column upper bound */
       
   610          xfprintf(fp, " %s", format(buf, ub));
       
   611          /* row/column activity range */
       
   612          xfprintf(fp, "  %s", format(buf, value2));
       
   613          /* row/column objective coefficient range */
       
   614          xfprintf(fp, " %s", format(buf, coef2));
       
   615          /* objective value at break point */
       
   616          xfprintf(fp, " %s", format(buf, obj2));
       
   617          /* limiting variable name */
       
   618          if (var2 != 0)
       
   619          {  if (var2 <= m)
       
   620                limit = glp_get_row_name(P, var2);
       
   621             else
       
   622                limit = glp_get_col_name(P, var2 - m);
       
   623             if (limit != NULL)
       
   624                xfprintf(fp, " %s", limit);
       
   625          }
       
   626          xfprintf(fp, "\n");
       
   627          xfprintf(fp, "\n");
       
   628          /* print 10 items per page */
       
   629          count = (count + 1) % 10;
       
   630       }
       
   631       xfprintf(fp, "End of report\n");
       
   632       xfflush(fp);
       
   633       if (xferror(fp))
       
   634       {  xprintf("Write error on `%s' - %s\n", fname, xerrmsg());
       
   635          ret = 4;
       
   636          goto done;
       
   637       }
       
   638       ret = 0;
       
   639 done: if (fp != NULL) xfclose(fp);
       
   640       return ret;
       
   641 }
       
   642 
       
   643 /**********************************************************************/
       
   644 
       
   645 int glp_print_ipt(glp_prob *P, const char *fname)
       
   646 {     /* write interior-point solution in printable format */
       
   647       XFILE *fp;
       
   648       GLPROW *row;
       
   649       GLPCOL *col;
       
   650       int i, j, t, ae_ind, re_ind, ret;
       
   651       double ae_max, re_max;
       
   652       xprintf("Writing interior-point solution to `%s'...\n", fname);
       
   653       fp = xfopen(fname, "w");
       
   654       if (fp == NULL)
       
   655       {  xprintf("Unable to create `%s' - %s\n", fname, xerrmsg());
       
   656          ret = 1;
       
   657          goto done;
       
   658       }
       
   659       xfprintf(fp, "%-12s%s\n", "Problem:",
       
   660          P->name == NULL ? "" : P->name);
       
   661       xfprintf(fp, "%-12s%d\n", "Rows:", P->m);
       
   662       xfprintf(fp, "%-12s%d\n", "Columns:", P->n);
       
   663       xfprintf(fp, "%-12s%d\n", "Non-zeros:", P->nnz);
       
   664       t = glp_ipt_status(P);
       
   665       xfprintf(fp, "%-12s%s\n", "Status:",
       
   666          t == GLP_OPT    ? "OPTIMAL" :
       
   667          t == GLP_UNDEF  ? "UNDEFINED" :
       
   668          t == GLP_INFEAS ? "INFEASIBLE (INTERMEDIATE)" :
       
   669          t == GLP_NOFEAS ? "INFEASIBLE (FINAL)" : "???");
       
   670       xfprintf(fp, "%-12s%s%s%.10g (%s)\n", "Objective:",
       
   671          P->obj == NULL ? "" : P->obj,
       
   672          P->obj == NULL ? "" : " = ", P->ipt_obj,
       
   673          P->dir == GLP_MIN ? "MINimum" :
       
   674          P->dir == GLP_MAX ? "MAXimum" : "???");
       
   675       xfprintf(fp, "\n");
       
   676       xfprintf(fp, "   No.   Row name        Activity     Lower bound  "
       
   677          " Upper bound    Marginal\n");
       
   678       xfprintf(fp, "------ ------------    ------------- ------------- "
       
   679          "------------- -------------\n");
       
   680       for (i = 1; i <= P->m; i++)
       
   681       {  row = P->row[i];
       
   682          xfprintf(fp, "%6d ", i);
       
   683          if (row->name == NULL || strlen(row->name) <= 12)
       
   684             xfprintf(fp, "%-12s ", row->name == NULL ? "" : row->name);
       
   685          else
       
   686             xfprintf(fp, "%s\n%20s", row->name, "");
       
   687          xfprintf(fp, "%3s", "");
       
   688          xfprintf(fp, "%13.6g ",
       
   689             fabs(row->pval) <= 1e-9 ? 0.0 : row->pval);
       
   690          if (row->type == GLP_LO || row->type == GLP_DB ||
       
   691              row->type == GLP_FX)
       
   692             xfprintf(fp, "%13.6g ", row->lb);
       
   693          else
       
   694             xfprintf(fp, "%13s ", "");
       
   695          if (row->type == GLP_UP || row->type == GLP_DB)
       
   696             xfprintf(fp, "%13.6g ", row->ub);
       
   697          else
       
   698             xfprintf(fp, "%13s ", row->type == GLP_FX ? "=" : "");
       
   699          if (fabs(row->dval) <= 1e-9)
       
   700             xfprintf(fp, "%13s", "< eps");
       
   701          else
       
   702             xfprintf(fp, "%13.6g ", row->dval);
       
   703          xfprintf(fp, "\n");
       
   704       }
       
   705       xfprintf(fp, "\n");
       
   706       xfprintf(fp, "   No. Column name       Activity     Lower bound  "
       
   707          " Upper bound    Marginal\n");
       
   708       xfprintf(fp, "------ ------------    ------------- ------------- "
       
   709          "------------- -------------\n");
       
   710       for (j = 1; j <= P->n; j++)
       
   711       {  col = P->col[j];
       
   712          xfprintf(fp, "%6d ", j);
       
   713          if (col->name == NULL || strlen(col->name) <= 12)
       
   714             xfprintf(fp, "%-12s ", col->name == NULL ? "" : col->name);
       
   715          else
       
   716             xfprintf(fp, "%s\n%20s", col->name, "");
       
   717          xfprintf(fp, "%3s", "");
       
   718          xfprintf(fp, "%13.6g ",
       
   719             fabs(col->pval) <= 1e-9 ? 0.0 : col->pval);
       
   720          if (col->type == GLP_LO || col->type == GLP_DB ||
       
   721              col->type == GLP_FX)
       
   722             xfprintf(fp, "%13.6g ", col->lb);
       
   723          else
       
   724             xfprintf(fp, "%13s ", "");
       
   725          if (col->type == GLP_UP || col->type == GLP_DB)
       
   726             xfprintf(fp, "%13.6g ", col->ub);
       
   727          else
       
   728             xfprintf(fp, "%13s ", col->type == GLP_FX ? "=" : "");
       
   729          if (fabs(col->dval) <= 1e-9)
       
   730             xfprintf(fp, "%13s", "< eps");
       
   731          else
       
   732             xfprintf(fp, "%13.6g ", col->dval);
       
   733          xfprintf(fp, "\n");
       
   734       }
       
   735       xfprintf(fp, "\n");
       
   736       xfprintf(fp, "Karush-Kuhn-Tucker optimality conditions:\n");
       
   737       xfprintf(fp, "\n");
       
   738       _glp_check_kkt(P, GLP_IPT, GLP_KKT_PE, &ae_max, &ae_ind, &re_max,
       
   739          &re_ind);
       
   740       xfprintf(fp, "KKT.PE: max.abs.err = %.2e on row %d\n",
       
   741          ae_max, ae_ind);
       
   742       xfprintf(fp, "        max.rel.err = %.2e on row %d\n",
       
   743          re_max, re_ind);
       
   744       xfprintf(fp, "%8s%s\n", "",
       
   745          re_max <= 1e-9 ? "High quality" :
       
   746          re_max <= 1e-6 ? "Medium quality" :
       
   747          re_max <= 1e-3 ? "Low quality" : "PRIMAL SOLUTION IS WRONG");
       
   748       xfprintf(fp, "\n");
       
   749       _glp_check_kkt(P, GLP_IPT, GLP_KKT_PB, &ae_max, &ae_ind, &re_max,
       
   750          &re_ind);
       
   751       xfprintf(fp, "KKT.PB: max.abs.err = %.2e on %s %d\n",
       
   752             ae_max, ae_ind <= P->m ? "row" : "column",
       
   753             ae_ind <= P->m ? ae_ind : ae_ind - P->m);
       
   754       xfprintf(fp, "        max.rel.err = %.2e on %s %d\n",
       
   755             re_max, re_ind <= P->m ? "row" : "column",
       
   756             re_ind <= P->m ? re_ind : re_ind - P->m);
       
   757       xfprintf(fp, "%8s%s\n", "",
       
   758          re_max <= 1e-9 ? "High quality" :
       
   759          re_max <= 1e-6 ? "Medium quality" :
       
   760          re_max <= 1e-3 ? "Low quality" : "PRIMAL SOLUTION IS INFEASIBL"
       
   761             "E");
       
   762       xfprintf(fp, "\n");
       
   763       _glp_check_kkt(P, GLP_IPT, GLP_KKT_DE, &ae_max, &ae_ind, &re_max,
       
   764          &re_ind);
       
   765       xfprintf(fp, "KKT.DE: max.abs.err = %.2e on column %d\n",
       
   766          ae_max, ae_ind == 0 ? 0 : ae_ind - P->m);
       
   767       xfprintf(fp, "        max.rel.err = %.2e on column %d\n",
       
   768          re_max, re_ind == 0 ? 0 : re_ind - P->m);
       
   769       xfprintf(fp, "%8s%s\n", "",
       
   770          re_max <= 1e-9 ? "High quality" :
       
   771          re_max <= 1e-6 ? "Medium quality" :
       
   772          re_max <= 1e-3 ? "Low quality" : "DUAL SOLUTION IS WRONG");
       
   773       xfprintf(fp, "\n");
       
   774       _glp_check_kkt(P, GLP_IPT, GLP_KKT_DB, &ae_max, &ae_ind, &re_max,
       
   775          &re_ind);
       
   776       xfprintf(fp, "KKT.DB: max.abs.err = %.2e on %s %d\n",
       
   777             ae_max, ae_ind <= P->m ? "row" : "column",
       
   778             ae_ind <= P->m ? ae_ind : ae_ind - P->m);
       
   779       xfprintf(fp, "        max.rel.err = %.2e on %s %d\n",
       
   780             re_max, re_ind <= P->m ? "row" : "column",
       
   781             re_ind <= P->m ? re_ind : re_ind - P->m);
       
   782       xfprintf(fp, "%8s%s\n", "",
       
   783          re_max <= 1e-9 ? "High quality" :
       
   784          re_max <= 1e-6 ? "Medium quality" :
       
   785          re_max <= 1e-3 ? "Low quality" : "DUAL SOLUTION IS INFEASIBLE")
       
   786             ;
       
   787       xfprintf(fp, "\n");
       
   788       xfprintf(fp, "End of output\n");
       
   789       xfflush(fp);
       
   790       if (xferror(fp))
       
   791       {  xprintf("Write error on `%s' - %s\n", fname, xerrmsg());
       
   792          ret = 1;
       
   793          goto done;
       
   794       }
       
   795       ret = 0;
       
   796 done: if (fp != NULL) xfclose(fp);
       
   797       return ret;
       
   798 }
       
   799 
       
   800 /***********************************************************************
       
   801 *  NAME
       
   802 *
       
   803 *  glp_read_ipt - read interior-point solution from text file
       
   804 *
       
   805 *  SYNOPSIS
       
   806 *
       
   807 *  int glp_read_ipt(glp_prob *lp, const char *fname);
       
   808 *
       
   809 *  DESCRIPTION
       
   810 *
       
   811 *  The routine glp_read_ipt reads interior-point solution from a text
       
   812 *  file whose name is specified by the parameter fname into the problem
       
   813 *  object.
       
   814 *
       
   815 *  For the file format see description of the routine glp_write_ipt.
       
   816 *
       
   817 *  RETURNS
       
   818 *
       
   819 *  On success the routine returns zero, otherwise non-zero. */
       
   820 
       
   821 int glp_read_ipt(glp_prob *lp, const char *fname)
       
   822 {     glp_data *data;
       
   823       jmp_buf jump;
       
   824       int i, j, k, ret = 0;
       
   825       xprintf("Reading interior-point solution from `%s'...\n", fname);
       
   826       data = glp_sdf_open_file(fname);
       
   827       if (data == NULL)
       
   828       {  ret = 1;
       
   829          goto done;
       
   830       }
       
   831       if (setjmp(jump))
       
   832       {  ret = 1;
       
   833          goto done;
       
   834       }
       
   835       glp_sdf_set_jump(data, jump);
       
   836       /* number of rows, number of columns */
       
   837       k = glp_sdf_read_int(data);
       
   838       if (k != lp->m)
       
   839          glp_sdf_error(data, "wrong number of rows\n");
       
   840       k = glp_sdf_read_int(data);
       
   841       if (k != lp->n)
       
   842          glp_sdf_error(data, "wrong number of columns\n");
       
   843       /* solution status, objective value */
       
   844       k = glp_sdf_read_int(data);
       
   845       if (!(k == GLP_UNDEF || k == GLP_OPT))
       
   846          glp_sdf_error(data, "invalid solution status\n");
       
   847       lp->ipt_stat = k;
       
   848       lp->ipt_obj = glp_sdf_read_num(data);
       
   849       /* rows (auxiliary variables) */
       
   850       for (i = 1; i <= lp->m; i++)
       
   851       {  GLPROW *row = lp->row[i];
       
   852          /* primal value, dual value */
       
   853          row->pval = glp_sdf_read_num(data);
       
   854          row->dval = glp_sdf_read_num(data);
       
   855       }
       
   856       /* columns (structural variables) */
       
   857       for (j = 1; j <= lp->n; j++)
       
   858       {  GLPCOL *col = lp->col[j];
       
   859          /* primal value, dual value */
       
   860          col->pval = glp_sdf_read_num(data);
       
   861          col->dval = glp_sdf_read_num(data);
       
   862       }
       
   863       xprintf("%d lines were read\n", glp_sdf_line(data));
       
   864 done: if (ret) lp->ipt_stat = GLP_UNDEF;
       
   865       if (data != NULL) glp_sdf_close_file(data);
       
   866       return ret;
       
   867 }
       
   868 
       
   869 /***********************************************************************
       
   870 *  NAME
       
   871 *
       
   872 *  glp_write_ipt - write interior-point solution to text file
       
   873 *
       
   874 *  SYNOPSIS
       
   875 *
       
   876 *  int glp_write_ipt(glp_prob *lp, const char *fname);
       
   877 *
       
   878 *  DESCRIPTION
       
   879 *
       
   880 *  The routine glp_write_ipt writes the current interior-point solution
       
   881 *  to a text file whose name is specified by the parameter fname. This
       
   882 *  file can be read back with the routine glp_read_ipt.
       
   883 *
       
   884 *  RETURNS
       
   885 *
       
   886 *  On success the routine returns zero, otherwise non-zero.
       
   887 *
       
   888 *  FILE FORMAT
       
   889 *
       
   890 *  The file created by the routine glp_write_ipt is a plain text file,
       
   891 *  which contains the following information:
       
   892 *
       
   893 *     m n
       
   894 *     stat obj_val
       
   895 *     r_prim[1] r_dual[1]
       
   896 *     . . .
       
   897 *     r_prim[m] r_dual[m]
       
   898 *     c_prim[1] c_dual[1]
       
   899 *     . . .
       
   900 *     c_prim[n] c_dual[n]
       
   901 *
       
   902 *  where:
       
   903 *  m is the number of rows (auxiliary variables);
       
   904 *  n is the number of columns (structural variables);
       
   905 *  stat is the solution status (GLP_UNDEF = 1 or GLP_OPT = 5);
       
   906 *  obj_val is the objective value;
       
   907 *  r_prim[i], i = 1,...,m, is the primal value of i-th row;
       
   908 *  r_dual[i], i = 1,...,m, is the dual value of i-th row;
       
   909 *  c_prim[j], j = 1,...,n, is the primal value of j-th column;
       
   910 *  c_dual[j], j = 1,...,n, is the dual value of j-th column. */
       
   911 
       
   912 int glp_write_ipt(glp_prob *lp, const char *fname)
       
   913 {     XFILE *fp;
       
   914       int i, j, ret = 0;
       
   915       xprintf("Writing interior-point solution to `%s'...\n", fname);
       
   916       fp = xfopen(fname, "w");
       
   917       if (fp == NULL)
       
   918       {  xprintf("Unable to create `%s' - %s\n", fname, xerrmsg());
       
   919          ret = 1;
       
   920          goto done;
       
   921       }
       
   922       /* number of rows, number of columns */
       
   923       xfprintf(fp, "%d %d\n", lp->m, lp->n);
       
   924       /* solution status, objective value */
       
   925       xfprintf(fp, "%d %.*g\n", lp->ipt_stat, DBL_DIG, lp->ipt_obj);
       
   926       /* rows (auxiliary variables) */
       
   927       for (i = 1; i <= lp->m; i++)
       
   928       {  GLPROW *row = lp->row[i];
       
   929          /* primal value, dual value */
       
   930          xfprintf(fp, "%.*g %.*g\n", DBL_DIG, row->pval, DBL_DIG,
       
   931             row->dval);
       
   932       }
       
   933       /* columns (structural variables) */
       
   934       for (j = 1; j <= lp->n; j++)
       
   935       {  GLPCOL *col = lp->col[j];
       
   936          /* primal value, dual value */
       
   937          xfprintf(fp, "%.*g %.*g\n", DBL_DIG, col->pval, DBL_DIG,
       
   938             col->dval);
       
   939       }
       
   940       xfflush(fp);
       
   941       if (xferror(fp))
       
   942       {  xprintf("Write error on `%s' - %s\n", fname, xerrmsg());
       
   943          ret = 1;
       
   944          goto done;
       
   945       }
       
   946       xprintf("%d lines were written\n", 2 + lp->m + lp->n);
       
   947 done: if (fp != NULL) xfclose(fp);
       
   948       return ret;
       
   949 }
       
   950 
       
   951 /**********************************************************************/
       
   952 
       
   953 int glp_print_mip(glp_prob *P, const char *fname)
       
   954 {     /* write MIP solution in printable format */
       
   955       XFILE *fp;
       
   956       GLPROW *row;
       
   957       GLPCOL *col;
       
   958       int i, j, t, ae_ind, re_ind, ret;
       
   959       double ae_max, re_max;
       
   960       xprintf("Writing MIP solution to `%s'...\n", fname);
       
   961       fp = xfopen(fname, "w");
       
   962       if (fp == NULL)
       
   963       {  xprintf("Unable to create `%s' - %s\n", fname, xerrmsg());
       
   964          ret = 1;
       
   965          goto done;
       
   966       }
       
   967       xfprintf(fp, "%-12s%s\n", "Problem:",
       
   968          P->name == NULL ? "" : P->name);
       
   969       xfprintf(fp, "%-12s%d\n", "Rows:", P->m);
       
   970       xfprintf(fp, "%-12s%d (%d integer, %d binary)\n", "Columns:",
       
   971          P->n, glp_get_num_int(P), glp_get_num_bin(P));
       
   972       xfprintf(fp, "%-12s%d\n", "Non-zeros:", P->nnz);
       
   973       t = glp_mip_status(P);
       
   974       xfprintf(fp, "%-12s%s\n", "Status:",
       
   975          t == GLP_OPT    ? "INTEGER OPTIMAL" :
       
   976          t == GLP_FEAS   ? "INTEGER NON-OPTIMAL" :
       
   977          t == GLP_NOFEAS ? "INTEGER EMPTY" :
       
   978          t == GLP_UNDEF  ? "INTEGER UNDEFINED" : "???");
       
   979       xfprintf(fp, "%-12s%s%s%.10g (%s)\n", "Objective:",
       
   980          P->obj == NULL ? "" : P->obj,
       
   981          P->obj == NULL ? "" : " = ", P->mip_obj,
       
   982          P->dir == GLP_MIN ? "MINimum" :
       
   983          P->dir == GLP_MAX ? "MAXimum" : "???");
       
   984       xfprintf(fp, "\n");
       
   985       xfprintf(fp, "   No.   Row name        Activity     Lower bound  "
       
   986          " Upper bound\n");
       
   987       xfprintf(fp, "------ ------------    ------------- ------------- "
       
   988          "-------------\n");
       
   989       for (i = 1; i <= P->m; i++)
       
   990       {  row = P->row[i];
       
   991          xfprintf(fp, "%6d ", i);
       
   992          if (row->name == NULL || strlen(row->name) <= 12)
       
   993             xfprintf(fp, "%-12s ", row->name == NULL ? "" : row->name);
       
   994          else
       
   995             xfprintf(fp, "%s\n%20s", row->name, "");
       
   996          xfprintf(fp, "%3s", "");
       
   997          xfprintf(fp, "%13.6g ",
       
   998             fabs(row->mipx) <= 1e-9 ? 0.0 : row->mipx);
       
   999          if (row->type == GLP_LO || row->type == GLP_DB ||
       
  1000              row->type == GLP_FX)
       
  1001             xfprintf(fp, "%13.6g ", row->lb);
       
  1002          else
       
  1003             xfprintf(fp, "%13s ", "");
       
  1004          if (row->type == GLP_UP || row->type == GLP_DB)
       
  1005             xfprintf(fp, "%13.6g ", row->ub);
       
  1006          else
       
  1007             xfprintf(fp, "%13s ", row->type == GLP_FX ? "=" : "");
       
  1008          xfprintf(fp, "\n");
       
  1009       }
       
  1010       xfprintf(fp, "\n");
       
  1011       xfprintf(fp, "   No. Column name       Activity     Lower bound  "
       
  1012          " Upper bound\n");
       
  1013       xfprintf(fp, "------ ------------    ------------- ------------- "
       
  1014          "-------------\n");
       
  1015       for (j = 1; j <= P->n; j++)
       
  1016       {  col = P->col[j];
       
  1017          xfprintf(fp, "%6d ", j);
       
  1018          if (col->name == NULL || strlen(col->name) <= 12)
       
  1019             xfprintf(fp, "%-12s ", col->name == NULL ? "" : col->name);
       
  1020          else
       
  1021             xfprintf(fp, "%s\n%20s", col->name, "");
       
  1022          xfprintf(fp, "%s  ",
       
  1023             col->kind == GLP_CV ? " " :
       
  1024             col->kind == GLP_IV ? "*" : "?");
       
  1025          xfprintf(fp, "%13.6g ",
       
  1026             fabs(col->mipx) <= 1e-9 ? 0.0 : col->mipx);
       
  1027          if (col->type == GLP_LO || col->type == GLP_DB ||
       
  1028              col->type == GLP_FX)
       
  1029             xfprintf(fp, "%13.6g ", col->lb);
       
  1030          else
       
  1031             xfprintf(fp, "%13s ", "");
       
  1032          if (col->type == GLP_UP || col->type == GLP_DB)
       
  1033             xfprintf(fp, "%13.6g ", col->ub);
       
  1034          else
       
  1035             xfprintf(fp, "%13s ", col->type == GLP_FX ? "=" : "");
       
  1036          xfprintf(fp, "\n");
       
  1037       }
       
  1038       xfprintf(fp, "\n");
       
  1039       xfprintf(fp, "Integer feasibility conditions:\n");
       
  1040       xfprintf(fp, "\n");
       
  1041       _glp_check_kkt(P, GLP_MIP, GLP_KKT_PE, &ae_max, &ae_ind, &re_max,
       
  1042          &re_ind);
       
  1043       xfprintf(fp, "KKT.PE: max.abs.err = %.2e on row %d\n",
       
  1044          ae_max, ae_ind);
       
  1045       xfprintf(fp, "        max.rel.err = %.2e on row %d\n",
       
  1046          re_max, re_ind);
       
  1047       xfprintf(fp, "%8s%s\n", "",
       
  1048          re_max <= 1e-9 ? "High quality" :
       
  1049          re_max <= 1e-6 ? "Medium quality" :
       
  1050          re_max <= 1e-3 ? "Low quality" : "SOLUTION IS WRONG");
       
  1051       xfprintf(fp, "\n");
       
  1052       _glp_check_kkt(P, GLP_MIP, GLP_KKT_PB, &ae_max, &ae_ind, &re_max,
       
  1053          &re_ind);
       
  1054       xfprintf(fp, "KKT.PB: max.abs.err = %.2e on %s %d\n",
       
  1055             ae_max, ae_ind <= P->m ? "row" : "column",
       
  1056             ae_ind <= P->m ? ae_ind : ae_ind - P->m);
       
  1057       xfprintf(fp, "        max.rel.err = %.2e on %s %d\n",
       
  1058             re_max, re_ind <= P->m ? "row" : "column",
       
  1059             re_ind <= P->m ? re_ind : re_ind - P->m);
       
  1060       xfprintf(fp, "%8s%s\n", "",
       
  1061          re_max <= 1e-9 ? "High quality" :
       
  1062          re_max <= 1e-6 ? "Medium quality" :
       
  1063          re_max <= 1e-3 ? "Low quality" : "SOLUTION IS INFEASIBLE");
       
  1064       xfprintf(fp, "\n");
       
  1065       xfprintf(fp, "End of output\n");
       
  1066       xfflush(fp);
       
  1067       if (xferror(fp))
       
  1068       {  xprintf("Write error on `%s' - %s\n", fname, xerrmsg());
       
  1069          ret = 1;
       
  1070          goto done;
       
  1071       }
       
  1072       ret = 0;
       
  1073 done: if (fp != NULL) xfclose(fp);
       
  1074       return ret;
       
  1075 }
       
  1076 
       
  1077 /***********************************************************************
       
  1078 *  NAME
       
  1079 *
       
  1080 *  glp_read_mip - read MIP solution from text file
       
  1081 *
       
  1082 *  SYNOPSIS
       
  1083 *
       
  1084 *  int glp_read_mip(glp_prob *mip, const char *fname);
       
  1085 *
       
  1086 *  DESCRIPTION
       
  1087 *
       
  1088 *  The routine glp_read_mip reads MIP solution from a text file whose
       
  1089 *  name is specified by the parameter fname into the problem object.
       
  1090 *
       
  1091 *  For the file format see description of the routine glp_write_mip.
       
  1092 *
       
  1093 *  RETURNS
       
  1094 *
       
  1095 *  On success the routine returns zero, otherwise non-zero. */
       
  1096 
       
  1097 int glp_read_mip(glp_prob *mip, const char *fname)
       
  1098 {     glp_data *data;
       
  1099       jmp_buf jump;
       
  1100       int i, j, k, ret = 0;
       
  1101       xprintf("Reading MIP solution from `%s'...\n", fname);
       
  1102       data = glp_sdf_open_file(fname);
       
  1103       if (data == NULL)
       
  1104       {  ret = 1;
       
  1105          goto done;
       
  1106       }
       
  1107       if (setjmp(jump))
       
  1108       {  ret = 1;
       
  1109          goto done;
       
  1110       }
       
  1111       glp_sdf_set_jump(data, jump);
       
  1112       /* number of rows, number of columns */
       
  1113       k = glp_sdf_read_int(data);
       
  1114       if (k != mip->m)
       
  1115          glp_sdf_error(data, "wrong number of rows\n");
       
  1116       k = glp_sdf_read_int(data);
       
  1117       if (k != mip->n)
       
  1118          glp_sdf_error(data, "wrong number of columns\n");
       
  1119       /* solution status, objective value */
       
  1120       k = glp_sdf_read_int(data);
       
  1121       if (!(k == GLP_UNDEF || k == GLP_OPT || k == GLP_FEAS ||
       
  1122             k == GLP_NOFEAS))
       
  1123          glp_sdf_error(data, "invalid solution status\n");
       
  1124       mip->mip_stat = k;
       
  1125       mip->mip_obj = glp_sdf_read_num(data);
       
  1126       /* rows (auxiliary variables) */
       
  1127       for (i = 1; i <= mip->m; i++)
       
  1128       {  GLPROW *row = mip->row[i];
       
  1129          row->mipx = glp_sdf_read_num(data);
       
  1130       }
       
  1131       /* columns (structural variables) */
       
  1132       for (j = 1; j <= mip->n; j++)
       
  1133       {  GLPCOL *col = mip->col[j];
       
  1134          col->mipx = glp_sdf_read_num(data);
       
  1135          if (col->kind == GLP_IV && col->mipx != floor(col->mipx))
       
  1136             glp_sdf_error(data, "non-integer column value");
       
  1137       }
       
  1138       xprintf("%d lines were read\n", glp_sdf_line(data));
       
  1139 done: if (ret) mip->mip_stat = GLP_UNDEF;
       
  1140       if (data != NULL) glp_sdf_close_file(data);
       
  1141       return ret;
       
  1142 }
       
  1143 
       
  1144 /***********************************************************************
       
  1145 *  NAME
       
  1146 *
       
  1147 *  glp_write_mip - write MIP solution to text file
       
  1148 *
       
  1149 *  SYNOPSIS
       
  1150 *
       
  1151 *  int glp_write_mip(glp_prob *mip, const char *fname);
       
  1152 *
       
  1153 *  DESCRIPTION
       
  1154 *
       
  1155 *  The routine glp_write_mip writes the current MIP solution to a text
       
  1156 *  file whose name is specified by the parameter fname. This file can
       
  1157 *  be read back with the routine glp_read_mip.
       
  1158 *
       
  1159 *  RETURNS
       
  1160 *
       
  1161 *  On success the routine returns zero, otherwise non-zero.
       
  1162 *
       
  1163 *  FILE FORMAT
       
  1164 *
       
  1165 *  The file created by the routine glp_write_sol is a plain text file,
       
  1166 *  which contains the following information:
       
  1167 *
       
  1168 *     m n
       
  1169 *     stat obj_val
       
  1170 *     r_val[1]
       
  1171 *     . . .
       
  1172 *     r_val[m]
       
  1173 *     c_val[1]
       
  1174 *     . . .
       
  1175 *     c_val[n]
       
  1176 *
       
  1177 *  where:
       
  1178 *  m is the number of rows (auxiliary variables);
       
  1179 *  n is the number of columns (structural variables);
       
  1180 *  stat is the solution status (GLP_UNDEF = 1, GLP_FEAS = 2,
       
  1181 *     GLP_NOFEAS = 4, or GLP_OPT = 5);
       
  1182 *  obj_val is the objective value;
       
  1183 *  r_val[i], i = 1,...,m, is the value of i-th row;
       
  1184 *  c_val[j], j = 1,...,n, is the value of j-th column. */
       
  1185 
       
  1186 int glp_write_mip(glp_prob *mip, const char *fname)
       
  1187 {     XFILE *fp;
       
  1188       int i, j, ret = 0;
       
  1189       xprintf("Writing MIP solution to `%s'...\n", fname);
       
  1190       fp = xfopen(fname, "w");
       
  1191       if (fp == NULL)
       
  1192       {  xprintf("Unable to create `%s' - %s\n", fname, xerrmsg());
       
  1193          ret = 1;
       
  1194          goto done;
       
  1195       }
       
  1196       /* number of rows, number of columns */
       
  1197       xfprintf(fp, "%d %d\n", mip->m, mip->n);
       
  1198       /* solution status, objective value */
       
  1199       xfprintf(fp, "%d %.*g\n", mip->mip_stat, DBL_DIG, mip->mip_obj);
       
  1200       /* rows (auxiliary variables) */
       
  1201       for (i = 1; i <= mip->m; i++)
       
  1202          xfprintf(fp, "%.*g\n", DBL_DIG, mip->row[i]->mipx);
       
  1203       /* columns (structural variables) */
       
  1204       for (j = 1; j <= mip->n; j++)
       
  1205          xfprintf(fp, "%.*g\n", DBL_DIG, mip->col[j]->mipx);
       
  1206       xfflush(fp);
       
  1207       if (xferror(fp))
       
  1208       {  xprintf("Write error on `%s' - %s\n", fname, xerrmsg());
       
  1209          ret = 1;
       
  1210          goto done;
       
  1211       }
       
  1212       xprintf("%d lines were written\n", 2 + mip->m + mip->n);
       
  1213 done: if (fp != NULL) xfclose(fp);
       
  1214       return ret;
       
  1215 }
       
  1216 
       
  1217 /* eof */