src/glpapi11.c
author Alpar Juttner <alpar@cs.elte.hu>
Sun, 05 Dec 2010 17:35:23 +0100
changeset 2 4c8956a7bdf4
permissions -rw-r--r--
Set up CMAKE build environment
     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 */