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

1235 lines
44 KiB

  1. /* glpapi11.c (utility routines) */
  2. /***********************************************************************
  3. * This code is part of GLPK (GNU Linear Programming Kit).
  4. *
  5. * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
  6. * 2009, 2010, 2011, 2013 Andrew Makhorin, Department for Applied
  7. * Informatics, Moscow Aviation Institute, Moscow, Russia. All rights
  8. * reserved. E-mail: <mao@gnu.org>.
  9. *
  10. * GLPK is free software: you can redistribute it and/or modify it
  11. * under the terms of the GNU General Public License as published by
  12. * the Free Software Foundation, either version 3 of the License, or
  13. * (at your option) any later version.
  14. *
  15. * GLPK is distributed in the hope that it will be useful, but WITHOUT
  16. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  17. * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
  18. * License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with GLPK. If not, see <http://www.gnu.org/licenses/>.
  22. ***********************************************************************/
  23. #include "env.h"
  24. #include "glpsdf.h"
  25. #include "prob.h"
  26. #define xfprintf glp_format
  27. int glp_print_sol(glp_prob *P, const char *fname)
  28. { /* write basic solution in printable format */
  29. glp_file *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 = glp_open(fname, "w");
  36. if (fp == NULL)
  37. { xprintf("Unable to create `%s' - %s\n", fname, get_err_msg());
  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. #if 0 /* FIXME */
  188. xfflush(fp);
  189. #endif
  190. if (glp_ioerr(fp))
  191. { xprintf("Write error on `%s' - %s\n", fname, get_err_msg());
  192. ret = 1;
  193. goto done;
  194. }
  195. ret = 0;
  196. done: if (fp != NULL) glp_close(fp);
  197. return ret;
  198. }
  199. /***********************************************************************
  200. * NAME
  201. *
  202. * glp_read_sol - read basic solution from text file
  203. *
  204. * SYNOPSIS
  205. *
  206. * int glp_read_sol(glp_prob *lp, const char *fname);
  207. *
  208. * DESCRIPTION
  209. *
  210. * The routine glp_read_sol reads basic solution from a text file whose
  211. * name is specified by the parameter fname into the problem object.
  212. *
  213. * For the file format see description of the routine glp_write_sol.
  214. *
  215. * RETURNS
  216. *
  217. * On success the routine returns zero, otherwise non-zero. */
  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. * NAME
  283. *
  284. * glp_write_sol - write basic solution to text file
  285. *
  286. * SYNOPSIS
  287. *
  288. * int glp_write_sol(glp_prob *lp, const char *fname);
  289. *
  290. * DESCRIPTION
  291. *
  292. * The routine glp_write_sol writes the current basic solution to a
  293. * text file whose name is specified by the parameter fname. This file
  294. * can be read back with the routine glp_read_sol.
  295. *
  296. * RETURNS
  297. *
  298. * On success the routine returns zero, otherwise non-zero.
  299. *
  300. * FILE FORMAT
  301. *
  302. * The file created by the routine glp_write_sol is a plain text file,
  303. * which contains the following information:
  304. *
  305. * m n
  306. * p_stat d_stat obj_val
  307. * r_stat[1] r_prim[1] r_dual[1]
  308. * . . .
  309. * r_stat[m] r_prim[m] r_dual[m]
  310. * c_stat[1] c_prim[1] c_dual[1]
  311. * . . .
  312. * c_stat[n] c_prim[n] c_dual[n]
  313. *
  314. * where:
  315. * m is the number of rows (auxiliary variables);
  316. * n is the number of columns (structural variables);
  317. * p_stat is the primal status of the basic solution (GLP_UNDEF = 1,
  318. * GLP_FEAS = 2, GLP_INFEAS = 3, or GLP_NOFEAS = 4);
  319. * d_stat is the dual status of the basic solution (GLP_UNDEF = 1,
  320. * GLP_FEAS = 2, GLP_INFEAS = 3, or GLP_NOFEAS = 4);
  321. * obj_val is the objective value;
  322. * r_stat[i], i = 1,...,m, is the status of i-th row (GLP_BS = 1,
  323. * GLP_NL = 2, GLP_NU = 3, GLP_NF = 4, or GLP_NS = 5);
  324. * r_prim[i], i = 1,...,m, is the primal value of i-th row;
  325. * r_dual[i], i = 1,...,m, is the dual value of i-th row;
  326. * c_stat[j], j = 1,...,n, is the status of j-th column (GLP_BS = 1,
  327. * GLP_NL = 2, GLP_NU = 3, GLP_NF = 4, or GLP_NS = 5);
  328. * c_prim[j], j = 1,...,n, is the primal value of j-th column;
  329. * c_dual[j], j = 1,...,n, is the dual value of j-th column. */
  330. int glp_write_sol(glp_prob *lp, const char *fname)
  331. { glp_file *fp;
  332. int i, j, ret = 0;
  333. xprintf("Writing basic solution to `%s'...\n", fname);
  334. fp = glp_open(fname, "w");
  335. if (fp == NULL)
  336. { xprintf("Unable to create `%s' - %s\n", fname, get_err_msg());
  337. ret = 1;
  338. goto done;
  339. }
  340. /* number of rows, number of columns */
  341. xfprintf(fp, "%d %d\n", lp->m, lp->n);
  342. /* primal status, dual status, objective value */
  343. xfprintf(fp, "%d %d %.*g\n", lp->pbs_stat, lp->dbs_stat, DBL_DIG,
  344. lp->obj_val);
  345. /* rows (auxiliary variables) */
  346. for (i = 1; i <= lp->m; i++)
  347. { GLPROW *row = lp->row[i];
  348. /* status, primal value, dual value */
  349. xfprintf(fp, "%d %.*g %.*g\n", row->stat, DBL_DIG, row->prim,
  350. DBL_DIG, row->dual);
  351. }
  352. /* columns (structural variables) */
  353. for (j = 1; j <= lp->n; j++)
  354. { GLPCOL *col = lp->col[j];
  355. /* status, primal value, dual value */
  356. xfprintf(fp, "%d %.*g %.*g\n", col->stat, DBL_DIG, col->prim,
  357. DBL_DIG, col->dual);
  358. }
  359. #if 0 /* FIXME */
  360. xfflush(fp);
  361. #endif
  362. if (glp_ioerr(fp))
  363. { xprintf("Write error on `%s' - %s\n", fname, get_err_msg());
  364. ret = 1;
  365. goto done;
  366. }
  367. xprintf("%d lines were written\n", 2 + lp->m + lp->n);
  368. done: if (fp != NULL) glp_close(fp);
  369. return ret;
  370. }
  371. /**********************************************************************/
  372. static char *format(char buf[13+1], double x)
  373. { /* format floating-point number in MPS/360-like style */
  374. if (x == -DBL_MAX)
  375. strcpy(buf, " -Inf");
  376. else if (x == +DBL_MAX)
  377. strcpy(buf, " +Inf");
  378. else if (fabs(x) <= 999999.99998)
  379. { sprintf(buf, "%13.5f", x);
  380. #if 1
  381. if (strcmp(buf, " 0.00000") == 0 ||
  382. strcmp(buf, " -0.00000") == 0)
  383. strcpy(buf, " . ");
  384. else if (memcmp(buf, " 0.", 8) == 0)
  385. memcpy(buf, " .", 8);
  386. else if (memcmp(buf, " -0.", 8) == 0)
  387. memcpy(buf, " -.", 8);
  388. #endif
  389. }
  390. else
  391. sprintf(buf, "%13.6g", x);
  392. return buf;
  393. }
  394. int glp_print_ranges(glp_prob *P, int len, const int list[],
  395. int flags, const char *fname)
  396. { /* print sensitivity analysis report */
  397. glp_file *fp = NULL;
  398. GLPROW *row;
  399. GLPCOL *col;
  400. int m, n, pass, k, t, numb, type, stat, var1, var2, count, page,
  401. ret;
  402. double lb, ub, slack, coef, prim, dual, value1, value2, coef1,
  403. coef2, obj1, obj2;
  404. const char *name, *limit;
  405. char buf[13+1];
  406. /* sanity checks */
  407. if (P == NULL || P->magic != GLP_PROB_MAGIC)
  408. xerror("glp_print_ranges: P = %p; invalid problem object\n",
  409. P);
  410. m = P->m, n = P->n;
  411. if (len < 0)
  412. xerror("glp_print_ranges: len = %d; invalid list length\n",
  413. len);
  414. if (len > 0)
  415. { if (list == NULL)
  416. xerror("glp_print_ranges: list = %p: invalid parameter\n",
  417. list);
  418. for (t = 1; t <= len; t++)
  419. { k = list[t];
  420. if (!(1 <= k && k <= m+n))
  421. xerror("glp_print_ranges: list[%d] = %d; row/column numb"
  422. "er out of range\n", t, k);
  423. }
  424. }
  425. if (flags != 0)
  426. xerror("glp_print_ranges: flags = %d; invalid parameter\n",
  427. flags);
  428. if (fname == NULL)
  429. xerror("glp_print_ranges: fname = %p; invalid parameter\n",
  430. fname);
  431. if (glp_get_status(P) != GLP_OPT)
  432. { xprintf("glp_print_ranges: optimal basic solution required\n");
  433. ret = 1;
  434. goto done;
  435. }
  436. if (!glp_bf_exists(P))
  437. { xprintf("glp_print_ranges: basis factorization required\n");
  438. ret = 2;
  439. goto done;
  440. }
  441. /* start reporting */
  442. xprintf("Write sensitivity analysis report to `%s'...\n", fname);
  443. fp = glp_open(fname, "w");
  444. if (fp == NULL)
  445. { xprintf("Unable to create `%s' - %s\n", fname, get_err_msg());
  446. ret = 3;
  447. goto done;
  448. }
  449. page = count = 0;
  450. for (pass = 1; pass <= 2; pass++)
  451. for (t = 1; t <= (len == 0 ? m+n : len); t++)
  452. { if (t == 1) count = 0;
  453. k = (len == 0 ? t : list[t]);
  454. if (pass == 1 && k > m || pass == 2 && k <= m)
  455. continue;
  456. if (count == 0)
  457. { xfprintf(fp, "GLPK %-4s - SENSITIVITY ANALYSIS REPORT%73sPa"
  458. "ge%4d\n", glp_version(), "", ++page);
  459. xfprintf(fp, "\n");
  460. xfprintf(fp, "%-12s%s\n", "Problem:",
  461. P->name == NULL ? "" : P->name);
  462. xfprintf(fp, "%-12s%s%s%.10g (%s)\n", "Objective:",
  463. P->obj == NULL ? "" : P->obj,
  464. P->obj == NULL ? "" : " = ", P->obj_val,
  465. P->dir == GLP_MIN ? "MINimum" :
  466. P->dir == GLP_MAX ? "MAXimum" : "???");
  467. xfprintf(fp, "\n");
  468. xfprintf(fp, "%6s %-12s %2s %13s %13s %13s %13s %13s %13s "
  469. "%s\n", "No.", pass == 1 ? "Row name" : "Column name",
  470. "St", "Activity", pass == 1 ? "Slack" : "Obj coef",
  471. "Lower bound", "Activity", "Obj coef", "Obj value at",
  472. "Limiting");
  473. xfprintf(fp, "%6s %-12s %2s %13s %13s %13s %13s %13s %13s "
  474. "%s\n", "", "", "", "", "Marginal", "Upper bound",
  475. "range", "range", "break point", "variable");
  476. xfprintf(fp, "------ ------------ -- ------------- --------"
  477. "----- ------------- ------------- ------------- ------"
  478. "------- ------------\n");
  479. }
  480. if (pass == 1)
  481. { numb = k;
  482. xassert(1 <= numb && numb <= m);
  483. row = P->row[numb];
  484. name = row->name;
  485. type = row->type;
  486. lb = glp_get_row_lb(P, numb);
  487. ub = glp_get_row_ub(P, numb);
  488. coef = 0.0;
  489. stat = row->stat;
  490. prim = row->prim;
  491. if (type == GLP_FR)
  492. slack = - prim;
  493. else if (type == GLP_LO)
  494. slack = lb - prim;
  495. else if (type == GLP_UP || type == GLP_DB || type == GLP_FX)
  496. slack = ub - prim;
  497. dual = row->dual;
  498. }
  499. else
  500. { numb = k - m;
  501. xassert(1 <= numb && numb <= n);
  502. col = P->col[numb];
  503. name = col->name;
  504. lb = glp_get_col_lb(P, numb);
  505. ub = glp_get_col_ub(P, numb);
  506. coef = col->coef;
  507. stat = col->stat;
  508. prim = col->prim;
  509. slack = 0.0;
  510. dual = col->dual;
  511. }
  512. if (stat != GLP_BS)
  513. { glp_analyze_bound(P, k, &value1, &var1, &value2, &var2);
  514. if (stat == GLP_NF)
  515. coef1 = coef2 = coef;
  516. else if (stat == GLP_NS)
  517. coef1 = -DBL_MAX, coef2 = +DBL_MAX;
  518. else if (stat == GLP_NL && P->dir == GLP_MIN ||
  519. stat == GLP_NU && P->dir == GLP_MAX)
  520. coef1 = coef - dual, coef2 = +DBL_MAX;
  521. else
  522. coef1 = -DBL_MAX, coef2 = coef - dual;
  523. if (value1 == -DBL_MAX)
  524. { if (dual < -1e-9)
  525. obj1 = +DBL_MAX;
  526. else if (dual > +1e-9)
  527. obj1 = -DBL_MAX;
  528. else
  529. obj1 = P->obj_val;
  530. }
  531. else
  532. obj1 = P->obj_val + dual * (value1 - prim);
  533. if (value2 == +DBL_MAX)
  534. { if (dual < -1e-9)
  535. obj2 = -DBL_MAX;
  536. else if (dual > +1e-9)
  537. obj2 = +DBL_MAX;
  538. else
  539. obj2 = P->obj_val;
  540. }
  541. else
  542. obj2 = P->obj_val + dual * (value2 - prim);
  543. }
  544. else
  545. { glp_analyze_coef(P, k, &coef1, &var1, &value1, &coef2,
  546. &var2, &value2);
  547. if (coef1 == -DBL_MAX)
  548. { if (prim < -1e-9)
  549. obj1 = +DBL_MAX;
  550. else if (prim > +1e-9)
  551. obj1 = -DBL_MAX;
  552. else
  553. obj1 = P->obj_val;
  554. }
  555. else
  556. obj1 = P->obj_val + (coef1 - coef) * prim;
  557. if (coef2 == +DBL_MAX)
  558. { if (prim < -1e-9)
  559. obj2 = -DBL_MAX;
  560. else if (prim > +1e-9)
  561. obj2 = +DBL_MAX;
  562. else
  563. obj2 = P->obj_val;
  564. }
  565. else
  566. obj2 = P->obj_val + (coef2 - coef) * prim;
  567. }
  568. /*** first line ***/
  569. /* row/column number */
  570. xfprintf(fp, "%6d", numb);
  571. /* row/column name */
  572. xfprintf(fp, " %-12.12s", name == NULL ? "" : name);
  573. if (name != NULL && strlen(name) > 12)
  574. xfprintf(fp, "%s\n%6s %12s", name+12, "", "");
  575. /* row/column status */
  576. xfprintf(fp, " %2s",
  577. stat == GLP_BS ? "BS" : stat == GLP_NL ? "NL" :
  578. stat == GLP_NU ? "NU" : stat == GLP_NF ? "NF" :
  579. stat == GLP_NS ? "NS" : "??");
  580. /* row/column activity */
  581. xfprintf(fp, " %s", format(buf, prim));
  582. /* row slack, column objective coefficient */
  583. xfprintf(fp, " %s", format(buf, k <= m ? slack : coef));
  584. /* row/column lower bound */
  585. xfprintf(fp, " %s", format(buf, lb));
  586. /* row/column activity range */
  587. xfprintf(fp, " %s", format(buf, value1));
  588. /* row/column objective coefficient range */
  589. xfprintf(fp, " %s", format(buf, coef1));
  590. /* objective value at break point */
  591. xfprintf(fp, " %s", format(buf, obj1));
  592. /* limiting variable name */
  593. if (var1 != 0)
  594. { if (var1 <= m)
  595. limit = glp_get_row_name(P, var1);
  596. else
  597. limit = glp_get_col_name(P, var1 - m);
  598. if (limit != NULL)
  599. xfprintf(fp, " %s", limit);
  600. }
  601. xfprintf(fp, "\n");
  602. /*** second line ***/
  603. xfprintf(fp, "%6s %-12s %2s %13s", "", "", "", "");
  604. /* row/column reduced cost */
  605. xfprintf(fp, " %s", format(buf, dual));
  606. /* row/column upper bound */
  607. xfprintf(fp, " %s", format(buf, ub));
  608. /* row/column activity range */
  609. xfprintf(fp, " %s", format(buf, value2));
  610. /* row/column objective coefficient range */
  611. xfprintf(fp, " %s", format(buf, coef2));
  612. /* objective value at break point */
  613. xfprintf(fp, " %s", format(buf, obj2));
  614. /* limiting variable name */
  615. if (var2 != 0)
  616. { if (var2 <= m)
  617. limit = glp_get_row_name(P, var2);
  618. else
  619. limit = glp_get_col_name(P, var2 - m);
  620. if (limit != NULL)
  621. xfprintf(fp, " %s", limit);
  622. }
  623. xfprintf(fp, "\n");
  624. xfprintf(fp, "\n");
  625. /* print 10 items per page */
  626. count = (count + 1) % 10;
  627. }
  628. xfprintf(fp, "End of report\n");
  629. #if 0 /* FIXME */
  630. xfflush(fp);
  631. #endif
  632. if (glp_ioerr(fp))
  633. { xprintf("Write error on `%s' - %s\n", fname, get_err_msg());
  634. ret = 4;
  635. goto done;
  636. }
  637. ret = 0;
  638. done: if (fp != NULL) glp_close(fp);
  639. return ret;
  640. }
  641. /**********************************************************************/
  642. int glp_print_ipt(glp_prob *P, const char *fname)
  643. { /* write interior-point solution in printable format */
  644. glp_file *fp;
  645. GLPROW *row;
  646. GLPCOL *col;
  647. int i, j, t, ae_ind, re_ind, ret;
  648. double ae_max, re_max;
  649. xprintf("Writing interior-point solution to `%s'...\n", fname);
  650. fp = glp_open(fname, "w");
  651. if (fp == NULL)
  652. { xprintf("Unable to create `%s' - %s\n", fname, get_err_msg());
  653. ret = 1;
  654. goto done;
  655. }
  656. xfprintf(fp, "%-12s%s\n", "Problem:",
  657. P->name == NULL ? "" : P->name);
  658. xfprintf(fp, "%-12s%d\n", "Rows:", P->m);
  659. xfprintf(fp, "%-12s%d\n", "Columns:", P->n);
  660. xfprintf(fp, "%-12s%d\n", "Non-zeros:", P->nnz);
  661. t = glp_ipt_status(P);
  662. xfprintf(fp, "%-12s%s\n", "Status:",
  663. t == GLP_OPT ? "OPTIMAL" :
  664. t == GLP_UNDEF ? "UNDEFINED" :
  665. t == GLP_INFEAS ? "INFEASIBLE (INTERMEDIATE)" :
  666. t == GLP_NOFEAS ? "INFEASIBLE (FINAL)" : "???");
  667. xfprintf(fp, "%-12s%s%s%.10g (%s)\n", "Objective:",
  668. P->obj == NULL ? "" : P->obj,
  669. P->obj == NULL ? "" : " = ", P->ipt_obj,
  670. P->dir == GLP_MIN ? "MINimum" :
  671. P->dir == GLP_MAX ? "MAXimum" : "???");
  672. xfprintf(fp, "\n");
  673. xfprintf(fp, " No. Row name Activity Lower bound "
  674. " Upper bound Marginal\n");
  675. xfprintf(fp, "------ ------------ ------------- ------------- "
  676. "------------- -------------\n");
  677. for (i = 1; i <= P->m; i++)
  678. { row = P->row[i];
  679. xfprintf(fp, "%6d ", i);
  680. if (row->name == NULL || strlen(row->name) <= 12)
  681. xfprintf(fp, "%-12s ", row->name == NULL ? "" : row->name);
  682. else
  683. xfprintf(fp, "%s\n%20s", row->name, "");
  684. xfprintf(fp, "%3s", "");
  685. xfprintf(fp, "%13.6g ",
  686. fabs(row->pval) <= 1e-9 ? 0.0 : row->pval);
  687. if (row->type == GLP_LO || row->type == GLP_DB ||
  688. row->type == GLP_FX)
  689. xfprintf(fp, "%13.6g ", row->lb);
  690. else
  691. xfprintf(fp, "%13s ", "");
  692. if (row->type == GLP_UP || row->type == GLP_DB)
  693. xfprintf(fp, "%13.6g ", row->ub);
  694. else
  695. xfprintf(fp, "%13s ", row->type == GLP_FX ? "=" : "");
  696. if (fabs(row->dval) <= 1e-9)
  697. xfprintf(fp, "%13s", "< eps");
  698. else
  699. xfprintf(fp, "%13.6g ", row->dval);
  700. xfprintf(fp, "\n");
  701. }
  702. xfprintf(fp, "\n");
  703. xfprintf(fp, " No. Column name Activity Lower bound "
  704. " Upper bound Marginal\n");
  705. xfprintf(fp, "------ ------------ ------------- ------------- "
  706. "------------- -------------\n");
  707. for (j = 1; j <= P->n; j++)
  708. { col = P->col[j];
  709. xfprintf(fp, "%6d ", j);
  710. if (col->name == NULL || strlen(col->name) <= 12)
  711. xfprintf(fp, "%-12s ", col->name == NULL ? "" : col->name);
  712. else
  713. xfprintf(fp, "%s\n%20s", col->name, "");
  714. xfprintf(fp, "%3s", "");
  715. xfprintf(fp, "%13.6g ",
  716. fabs(col->pval) <= 1e-9 ? 0.0 : col->pval);
  717. if (col->type == GLP_LO || col->type == GLP_DB ||
  718. col->type == GLP_FX)
  719. xfprintf(fp, "%13.6g ", col->lb);
  720. else
  721. xfprintf(fp, "%13s ", "");
  722. if (col->type == GLP_UP || col->type == GLP_DB)
  723. xfprintf(fp, "%13.6g ", col->ub);
  724. else
  725. xfprintf(fp, "%13s ", col->type == GLP_FX ? "=" : "");
  726. if (fabs(col->dval) <= 1e-9)
  727. xfprintf(fp, "%13s", "< eps");
  728. else
  729. xfprintf(fp, "%13.6g ", col->dval);
  730. xfprintf(fp, "\n");
  731. }
  732. xfprintf(fp, "\n");
  733. xfprintf(fp, "Karush-Kuhn-Tucker optimality conditions:\n");
  734. xfprintf(fp, "\n");
  735. glp_check_kkt(P, GLP_IPT, GLP_KKT_PE, &ae_max, &ae_ind, &re_max,
  736. &re_ind);
  737. xfprintf(fp, "KKT.PE: max.abs.err = %.2e on row %d\n",
  738. ae_max, ae_ind);
  739. xfprintf(fp, " max.rel.err = %.2e on row %d\n",
  740. re_max, re_ind);
  741. xfprintf(fp, "%8s%s\n", "",
  742. re_max <= 1e-9 ? "High quality" :
  743. re_max <= 1e-6 ? "Medium quality" :
  744. re_max <= 1e-3 ? "Low quality" : "PRIMAL SOLUTION IS WRONG");
  745. xfprintf(fp, "\n");
  746. glp_check_kkt(P, GLP_IPT, GLP_KKT_PB, &ae_max, &ae_ind, &re_max,
  747. &re_ind);
  748. xfprintf(fp, "KKT.PB: max.abs.err = %.2e on %s %d\n",
  749. ae_max, ae_ind <= P->m ? "row" : "column",
  750. ae_ind <= P->m ? ae_ind : ae_ind - P->m);
  751. xfprintf(fp, " max.rel.err = %.2e on %s %d\n",
  752. re_max, re_ind <= P->m ? "row" : "column",
  753. re_ind <= P->m ? re_ind : re_ind - P->m);
  754. xfprintf(fp, "%8s%s\n", "",
  755. re_max <= 1e-9 ? "High quality" :
  756. re_max <= 1e-6 ? "Medium quality" :
  757. re_max <= 1e-3 ? "Low quality" : "PRIMAL SOLUTION IS INFEASIBL"
  758. "E");
  759. xfprintf(fp, "\n");
  760. glp_check_kkt(P, GLP_IPT, GLP_KKT_DE, &ae_max, &ae_ind, &re_max,
  761. &re_ind);
  762. xfprintf(fp, "KKT.DE: max.abs.err = %.2e on column %d\n",
  763. ae_max, ae_ind == 0 ? 0 : ae_ind - P->m);
  764. xfprintf(fp, " max.rel.err = %.2e on column %d\n",
  765. re_max, re_ind == 0 ? 0 : re_ind - P->m);
  766. xfprintf(fp, "%8s%s\n", "",
  767. re_max <= 1e-9 ? "High quality" :
  768. re_max <= 1e-6 ? "Medium quality" :
  769. re_max <= 1e-3 ? "Low quality" : "DUAL SOLUTION IS WRONG");
  770. xfprintf(fp, "\n");
  771. glp_check_kkt(P, GLP_IPT, GLP_KKT_DB, &ae_max, &ae_ind, &re_max,
  772. &re_ind);
  773. xfprintf(fp, "KKT.DB: max.abs.err = %.2e on %s %d\n",
  774. ae_max, ae_ind <= P->m ? "row" : "column",
  775. ae_ind <= P->m ? ae_ind : ae_ind - P->m);
  776. xfprintf(fp, " max.rel.err = %.2e on %s %d\n",
  777. re_max, re_ind <= P->m ? "row" : "column",
  778. re_ind <= P->m ? re_ind : re_ind - P->m);
  779. xfprintf(fp, "%8s%s\n", "",
  780. re_max <= 1e-9 ? "High quality" :
  781. re_max <= 1e-6 ? "Medium quality" :
  782. re_max <= 1e-3 ? "Low quality" : "DUAL SOLUTION IS INFEASIBLE")
  783. ;
  784. xfprintf(fp, "\n");
  785. xfprintf(fp, "End of output\n");
  786. #if 0 /* FIXME */
  787. xfflush(fp);
  788. #endif
  789. if (glp_ioerr(fp))
  790. { xprintf("Write error on `%s' - %s\n", fname, get_err_msg());
  791. ret = 1;
  792. goto done;
  793. }
  794. ret = 0;
  795. done: if (fp != NULL) glp_close(fp);
  796. return ret;
  797. }
  798. /***********************************************************************
  799. * NAME
  800. *
  801. * glp_read_ipt - read interior-point solution from text file
  802. *
  803. * SYNOPSIS
  804. *
  805. * int glp_read_ipt(glp_prob *lp, const char *fname);
  806. *
  807. * DESCRIPTION
  808. *
  809. * The routine glp_read_ipt reads interior-point solution from a text
  810. * file whose name is specified by the parameter fname into the problem
  811. * object.
  812. *
  813. * For the file format see description of the routine glp_write_ipt.
  814. *
  815. * RETURNS
  816. *
  817. * On success the routine returns zero, otherwise non-zero. */
  818. int glp_read_ipt(glp_prob *lp, const char *fname)
  819. { glp_data *data;
  820. jmp_buf jump;
  821. int i, j, k, ret = 0;
  822. xprintf("Reading interior-point solution from `%s'...\n", fname);
  823. data = glp_sdf_open_file(fname);
  824. if (data == NULL)
  825. { ret = 1;
  826. goto done;
  827. }
  828. if (setjmp(jump))
  829. { ret = 1;
  830. goto done;
  831. }
  832. glp_sdf_set_jump(data, jump);
  833. /* number of rows, number of columns */
  834. k = glp_sdf_read_int(data);
  835. if (k != lp->m)
  836. glp_sdf_error(data, "wrong number of rows\n");
  837. k = glp_sdf_read_int(data);
  838. if (k != lp->n)
  839. glp_sdf_error(data, "wrong number of columns\n");
  840. /* solution status, objective value */
  841. k = glp_sdf_read_int(data);
  842. if (!(k == GLP_UNDEF || k == GLP_OPT))
  843. glp_sdf_error(data, "invalid solution status\n");
  844. lp->ipt_stat = k;
  845. lp->ipt_obj = glp_sdf_read_num(data);
  846. /* rows (auxiliary variables) */
  847. for (i = 1; i <= lp->m; i++)
  848. { GLPROW *row = lp->row[i];
  849. /* primal value, dual value */
  850. row->pval = glp_sdf_read_num(data);
  851. row->dval = glp_sdf_read_num(data);
  852. }
  853. /* columns (structural variables) */
  854. for (j = 1; j <= lp->n; j++)
  855. { GLPCOL *col = lp->col[j];
  856. /* primal value, dual value */
  857. col->pval = glp_sdf_read_num(data);
  858. col->dval = glp_sdf_read_num(data);
  859. }
  860. xprintf("%d lines were read\n", glp_sdf_line(data));
  861. done: if (ret) lp->ipt_stat = GLP_UNDEF;
  862. if (data != NULL) glp_sdf_close_file(data);
  863. return ret;
  864. }
  865. /***********************************************************************
  866. * NAME
  867. *
  868. * glp_write_ipt - write interior-point solution to text file
  869. *
  870. * SYNOPSIS
  871. *
  872. * int glp_write_ipt(glp_prob *lp, const char *fname);
  873. *
  874. * DESCRIPTION
  875. *
  876. * The routine glp_write_ipt writes the current interior-point solution
  877. * to a text file whose name is specified by the parameter fname. This
  878. * file can be read back with the routine glp_read_ipt.
  879. *
  880. * RETURNS
  881. *
  882. * On success the routine returns zero, otherwise non-zero.
  883. *
  884. * FILE FORMAT
  885. *
  886. * The file created by the routine glp_write_ipt is a plain text file,
  887. * which contains the following information:
  888. *
  889. * m n
  890. * stat obj_val
  891. * r_prim[1] r_dual[1]
  892. * . . .
  893. * r_prim[m] r_dual[m]
  894. * c_prim[1] c_dual[1]
  895. * . . .
  896. * c_prim[n] c_dual[n]
  897. *
  898. * where:
  899. * m is the number of rows (auxiliary variables);
  900. * n is the number of columns (structural variables);
  901. * stat is the solution status (GLP_UNDEF = 1 or GLP_OPT = 5);
  902. * obj_val is the objective value;
  903. * r_prim[i], i = 1,...,m, is the primal value of i-th row;
  904. * r_dual[i], i = 1,...,m, is the dual value of i-th row;
  905. * c_prim[j], j = 1,...,n, is the primal value of j-th column;
  906. * c_dual[j], j = 1,...,n, is the dual value of j-th column. */
  907. int glp_write_ipt(glp_prob *lp, const char *fname)
  908. { glp_file *fp;
  909. int i, j, ret = 0;
  910. xprintf("Writing interior-point solution to `%s'...\n", fname);
  911. fp = glp_open(fname, "w");
  912. if (fp == NULL)
  913. { xprintf("Unable to create `%s' - %s\n", fname, get_err_msg());
  914. ret = 1;
  915. goto done;
  916. }
  917. /* number of rows, number of columns */
  918. xfprintf(fp, "%d %d\n", lp->m, lp->n);
  919. /* solution status, objective value */
  920. xfprintf(fp, "%d %.*g\n", lp->ipt_stat, DBL_DIG, lp->ipt_obj);
  921. /* rows (auxiliary variables) */
  922. for (i = 1; i <= lp->m; i++)
  923. { GLPROW *row = lp->row[i];
  924. /* primal value, dual value */
  925. xfprintf(fp, "%.*g %.*g\n", DBL_DIG, row->pval, DBL_DIG,
  926. row->dval);
  927. }
  928. /* columns (structural variables) */
  929. for (j = 1; j <= lp->n; j++)
  930. { GLPCOL *col = lp->col[j];
  931. /* primal value, dual value */
  932. xfprintf(fp, "%.*g %.*g\n", DBL_DIG, col->pval, DBL_DIG,
  933. col->dval);
  934. }
  935. #if 0 /* FIXME */
  936. xfflush(fp);
  937. #endif
  938. if (glp_ioerr(fp))
  939. { xprintf("Write error on `%s' - %s\n", fname, get_err_msg());
  940. ret = 1;
  941. goto done;
  942. }
  943. xprintf("%d lines were written\n", 2 + lp->m + lp->n);
  944. done: if (fp != NULL) glp_close(fp);
  945. return ret;
  946. }
  947. /**********************************************************************/
  948. int glp_print_mip(glp_prob *P, const char *fname)
  949. { /* write MIP solution in printable format */
  950. glp_file *fp;
  951. GLPROW *row;
  952. GLPCOL *col;
  953. int i, j, t, ae_ind, re_ind, ret;
  954. double ae_max, re_max;
  955. xprintf("Writing MIP solution to `%s'...\n", fname);
  956. fp = glp_open(fname, "w");
  957. if (fp == NULL)
  958. { xprintf("Unable to create `%s' - %s\n", fname, get_err_msg());
  959. ret = 1;
  960. goto done;
  961. }
  962. xfprintf(fp, "%-12s%s\n", "Problem:",
  963. P->name == NULL ? "" : P->name);
  964. xfprintf(fp, "%-12s%d\n", "Rows:", P->m);
  965. xfprintf(fp, "%-12s%d (%d integer, %d binary)\n", "Columns:",
  966. P->n, glp_get_num_int(P), glp_get_num_bin(P));
  967. xfprintf(fp, "%-12s%d\n", "Non-zeros:", P->nnz);
  968. t = glp_mip_status(P);
  969. xfprintf(fp, "%-12s%s\n", "Status:",
  970. t == GLP_OPT ? "INTEGER OPTIMAL" :
  971. t == GLP_FEAS ? "INTEGER NON-OPTIMAL" :
  972. t == GLP_NOFEAS ? "INTEGER EMPTY" :
  973. t == GLP_UNDEF ? "INTEGER UNDEFINED" : "???");
  974. xfprintf(fp, "%-12s%s%s%.10g (%s)\n", "Objective:",
  975. P->obj == NULL ? "" : P->obj,
  976. P->obj == NULL ? "" : " = ", P->mip_obj,
  977. P->dir == GLP_MIN ? "MINimum" :
  978. P->dir == GLP_MAX ? "MAXimum" : "???");
  979. xfprintf(fp, "\n");
  980. xfprintf(fp, " No. Row name Activity Lower bound "
  981. " Upper bound\n");
  982. xfprintf(fp, "------ ------------ ------------- ------------- "
  983. "-------------\n");
  984. for (i = 1; i <= P->m; i++)
  985. { row = P->row[i];
  986. xfprintf(fp, "%6d ", i);
  987. if (row->name == NULL || strlen(row->name) <= 12)
  988. xfprintf(fp, "%-12s ", row->name == NULL ? "" : row->name);
  989. else
  990. xfprintf(fp, "%s\n%20s", row->name, "");
  991. xfprintf(fp, "%3s", "");
  992. xfprintf(fp, "%13.6g ",
  993. fabs(row->mipx) <= 1e-9 ? 0.0 : row->mipx);
  994. if (row->type == GLP_LO || row->type == GLP_DB ||
  995. row->type == GLP_FX)
  996. xfprintf(fp, "%13.6g ", row->lb);
  997. else
  998. xfprintf(fp, "%13s ", "");
  999. if (row->type == GLP_UP || row->type == GLP_DB)
  1000. xfprintf(fp, "%13.6g ", row->ub);
  1001. else
  1002. xfprintf(fp, "%13s ", row->type == GLP_FX ? "=" : "");
  1003. xfprintf(fp, "\n");
  1004. }
  1005. xfprintf(fp, "\n");
  1006. xfprintf(fp, " No. Column name Activity Lower bound "
  1007. " Upper bound\n");
  1008. xfprintf(fp, "------ ------------ ------------- ------------- "
  1009. "-------------\n");
  1010. for (j = 1; j <= P->n; j++)
  1011. { col = P->col[j];
  1012. xfprintf(fp, "%6d ", j);
  1013. if (col->name == NULL || strlen(col->name) <= 12)
  1014. xfprintf(fp, "%-12s ", col->name == NULL ? "" : col->name);
  1015. else
  1016. xfprintf(fp, "%s\n%20s", col->name, "");
  1017. xfprintf(fp, "%s ",
  1018. col->kind == GLP_CV ? " " :
  1019. col->kind == GLP_IV ? "*" : "?");
  1020. xfprintf(fp, "%13.6g ",
  1021. fabs(col->mipx) <= 1e-9 ? 0.0 : col->mipx);
  1022. if (col->type == GLP_LO || col->type == GLP_DB ||
  1023. col->type == GLP_FX)
  1024. xfprintf(fp, "%13.6g ", col->lb);
  1025. else
  1026. xfprintf(fp, "%13s ", "");
  1027. if (col->type == GLP_UP || col->type == GLP_DB)
  1028. xfprintf(fp, "%13.6g ", col->ub);
  1029. else
  1030. xfprintf(fp, "%13s ", col->type == GLP_FX ? "=" : "");
  1031. xfprintf(fp, "\n");
  1032. }
  1033. xfprintf(fp, "\n");
  1034. xfprintf(fp, "Integer feasibility conditions:\n");
  1035. xfprintf(fp, "\n");
  1036. glp_check_kkt(P, GLP_MIP, GLP_KKT_PE, &ae_max, &ae_ind, &re_max,
  1037. &re_ind);
  1038. xfprintf(fp, "KKT.PE: max.abs.err = %.2e on row %d\n",
  1039. ae_max, ae_ind);
  1040. xfprintf(fp, " max.rel.err = %.2e on row %d\n",
  1041. re_max, re_ind);
  1042. xfprintf(fp, "%8s%s\n", "",
  1043. re_max <= 1e-9 ? "High quality" :
  1044. re_max <= 1e-6 ? "Medium quality" :
  1045. re_max <= 1e-3 ? "Low quality" : "SOLUTION IS WRONG");
  1046. xfprintf(fp, "\n");
  1047. glp_check_kkt(P, GLP_MIP, GLP_KKT_PB, &ae_max, &ae_ind, &re_max,
  1048. &re_ind);
  1049. xfprintf(fp, "KKT.PB: max.abs.err = %.2e on %s %d\n",
  1050. ae_max, ae_ind <= P->m ? "row" : "column",
  1051. ae_ind <= P->m ? ae_ind : ae_ind - P->m);
  1052. xfprintf(fp, " max.rel.err = %.2e on %s %d\n",
  1053. re_max, re_ind <= P->m ? "row" : "column",
  1054. re_ind <= P->m ? re_ind : re_ind - P->m);
  1055. xfprintf(fp, "%8s%s\n", "",
  1056. re_max <= 1e-9 ? "High quality" :
  1057. re_max <= 1e-6 ? "Medium quality" :
  1058. re_max <= 1e-3 ? "Low quality" : "SOLUTION IS INFEASIBLE");
  1059. xfprintf(fp, "\n");
  1060. xfprintf(fp, "End of output\n");
  1061. #if 0 /* FIXME */
  1062. xfflush(fp);
  1063. #endif
  1064. if (glp_ioerr(fp))
  1065. { xprintf("Write error on `%s' - %s\n", fname, get_err_msg());
  1066. ret = 1;
  1067. goto done;
  1068. }
  1069. ret = 0;
  1070. done: if (fp != NULL) glp_close(fp);
  1071. return ret;
  1072. }
  1073. /***********************************************************************
  1074. * NAME
  1075. *
  1076. * glp_read_mip - read MIP solution from text file
  1077. *
  1078. * SYNOPSIS
  1079. *
  1080. * int glp_read_mip(glp_prob *mip, const char *fname);
  1081. *
  1082. * DESCRIPTION
  1083. *
  1084. * The routine glp_read_mip reads MIP solution from a text file whose
  1085. * name is specified by the parameter fname into the problem object.
  1086. *
  1087. * For the file format see description of the routine glp_write_mip.
  1088. *
  1089. * RETURNS
  1090. *
  1091. * On success the routine returns zero, otherwise non-zero. */
  1092. int glp_read_mip(glp_prob *mip, const char *fname)
  1093. { glp_data *data;
  1094. jmp_buf jump;
  1095. int i, j, k, ret = 0;
  1096. xprintf("Reading MIP solution from `%s'...\n", fname);
  1097. data = glp_sdf_open_file(fname);
  1098. if (data == NULL)
  1099. { ret = 1;
  1100. goto done;
  1101. }
  1102. if (setjmp(jump))
  1103. { ret = 1;
  1104. goto done;
  1105. }
  1106. glp_sdf_set_jump(data, jump);
  1107. /* number of rows, number of columns */
  1108. k = glp_sdf_read_int(data);
  1109. if (k != mip->m)
  1110. glp_sdf_error(data, "wrong number of rows\n");
  1111. k = glp_sdf_read_int(data);
  1112. if (k != mip->n)
  1113. glp_sdf_error(data, "wrong number of columns\n");
  1114. /* solution status, objective value */
  1115. k = glp_sdf_read_int(data);
  1116. if (!(k == GLP_UNDEF || k == GLP_OPT || k == GLP_FEAS ||
  1117. k == GLP_NOFEAS))
  1118. glp_sdf_error(data, "invalid solution status\n");
  1119. mip->mip_stat = k;
  1120. mip->mip_obj = glp_sdf_read_num(data);
  1121. /* rows (auxiliary variables) */
  1122. for (i = 1; i <= mip->m; i++)
  1123. { GLPROW *row = mip->row[i];
  1124. row->mipx = glp_sdf_read_num(data);
  1125. }
  1126. /* columns (structural variables) */
  1127. for (j = 1; j <= mip->n; j++)
  1128. { GLPCOL *col = mip->col[j];
  1129. col->mipx = glp_sdf_read_num(data);
  1130. if (col->kind == GLP_IV && col->mipx != floor(col->mipx))
  1131. glp_sdf_error(data, "non-integer column value");
  1132. }
  1133. xprintf("%d lines were read\n", glp_sdf_line(data));
  1134. done: if (ret) mip->mip_stat = GLP_UNDEF;
  1135. if (data != NULL) glp_sdf_close_file(data);
  1136. return ret;
  1137. }
  1138. /***********************************************************************
  1139. * NAME
  1140. *
  1141. * glp_write_mip - write MIP solution to text file
  1142. *
  1143. * SYNOPSIS
  1144. *
  1145. * int glp_write_mip(glp_prob *mip, const char *fname);
  1146. *
  1147. * DESCRIPTION
  1148. *
  1149. * The routine glp_write_mip writes the current MIP solution to a text
  1150. * file whose name is specified by the parameter fname. This file can
  1151. * be read back with the routine glp_read_mip.
  1152. *
  1153. * RETURNS
  1154. *
  1155. * On success the routine returns zero, otherwise non-zero.
  1156. *
  1157. * FILE FORMAT
  1158. *
  1159. * The file created by the routine glp_write_sol is a plain text file,
  1160. * which contains the following information:
  1161. *
  1162. * m n
  1163. * stat obj_val
  1164. * r_val[1]
  1165. * . . .
  1166. * r_val[m]
  1167. * c_val[1]
  1168. * . . .
  1169. * c_val[n]
  1170. *
  1171. * where:
  1172. * m is the number of rows (auxiliary variables);
  1173. * n is the number of columns (structural variables);
  1174. * stat is the solution status (GLP_UNDEF = 1, GLP_FEAS = 2,
  1175. * GLP_NOFEAS = 4, or GLP_OPT = 5);
  1176. * obj_val is the objective value;
  1177. * r_val[i], i = 1,...,m, is the value of i-th row;
  1178. * c_val[j], j = 1,...,n, is the value of j-th column. */
  1179. int glp_write_mip(glp_prob *mip, const char *fname)
  1180. { glp_file *fp;
  1181. int i, j, ret = 0;
  1182. xprintf("Writing MIP solution to `%s'...\n", fname);
  1183. fp = glp_open(fname, "w");
  1184. if (fp == NULL)
  1185. { xprintf("Unable to create `%s' - %s\n", fname, get_err_msg());
  1186. ret = 1;
  1187. goto done;
  1188. }
  1189. /* number of rows, number of columns */
  1190. xfprintf(fp, "%d %d\n", mip->m, mip->n);
  1191. /* solution status, objective value */
  1192. xfprintf(fp, "%d %.*g\n", mip->mip_stat, DBL_DIG, mip->mip_obj);
  1193. /* rows (auxiliary variables) */
  1194. for (i = 1; i <= mip->m; i++)
  1195. xfprintf(fp, "%.*g\n", DBL_DIG, mip->row[i]->mipx);
  1196. /* columns (structural variables) */
  1197. for (j = 1; j <= mip->n; j++)
  1198. xfprintf(fp, "%.*g\n", DBL_DIG, mip->col[j]->mipx);
  1199. #if 0 /* FIXME */
  1200. xfflush(fp);
  1201. #endif
  1202. if (glp_ioerr(fp))
  1203. { xprintf("Write error on `%s' - %s\n", fname, get_err_msg());
  1204. ret = 1;
  1205. goto done;
  1206. }
  1207. xprintf("%d lines were written\n", 2 + mip->m + mip->n);
  1208. done: if (fp != NULL) glp_close(fp);
  1209. return ret;
  1210. }
  1211. /* eof */