00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #ifdef HAVE_CONFIG_H
00024 #include <config.h>
00025 #endif
00026
00027 #include <cfloat>
00028 #include <csetjmp>
00029 #include <ctime>
00030
00031 #include "lo-ieee.h"
00032
00033 #include "defun-dld.h"
00034 #include "error.h"
00035 #include "gripes.h"
00036 #include "oct-map.h"
00037 #include "oct-obj.h"
00038 #include "pager.h"
00039
00040 #if defined (HAVE_GLPK)
00041
00042 extern "C"
00043 {
00044 #if defined (HAVE_GLPK_GLPK_H)
00045 #include <glpk/glpk.h>
00046 #else
00047 #include <glpk.h>
00048 #endif
00049
00050 #if 0
00051 #ifdef GLPK_PRE_4_14
00052
00053 #ifndef _GLPLIB_H
00054 #include <glplib.h>
00055 #endif
00056 #ifndef lib_set_fault_hook
00057 #define lib_set_fault_hook lib_fault_hook
00058 #endif
00059 #ifndef lib_set_print_hook
00060 #define lib_set_print_hook lib_print_hook
00061 #endif
00062
00063 #else
00064
00065 void _glp_lib_print_hook (int (*func)(void *info, char *buf), void *info);
00066 void _glp_lib_fault_hook (int (*func)(void *info, char *buf), void *info);
00067
00068 #endif
00069 #endif
00070 }
00071
00072 #define NIntP 17
00073 #define NRealP 10
00074
00075 int lpxIntParam[NIntP] = {
00076 0,
00077 1,
00078 0,
00079 1,
00080 0,
00081 -1,
00082 0,
00083 200,
00084 1,
00085 2,
00086 0,
00087 1,
00088 0,
00089 0,
00090 2,
00091 2,
00092 1
00093 };
00094
00095 int IParam[NIntP] = {
00096 LPX_K_MSGLEV,
00097 LPX_K_SCALE,
00098 LPX_K_DUAL,
00099 LPX_K_PRICE,
00100 LPX_K_ROUND,
00101 LPX_K_ITLIM,
00102 LPX_K_ITCNT,
00103 LPX_K_OUTFRQ,
00104 LPX_K_MPSINFO,
00105 LPX_K_MPSOBJ,
00106 LPX_K_MPSORIG,
00107 LPX_K_MPSWIDE,
00108 LPX_K_MPSFREE,
00109 LPX_K_MPSSKIP,
00110 LPX_K_BRANCH,
00111 LPX_K_BTRACK,
00112 LPX_K_PRESOL
00113 };
00114
00115
00116 double lpxRealParam[NRealP] = {
00117 0.07,
00118 1e-7,
00119 1e-7,
00120 1e-9,
00121 -DBL_MAX,
00122 DBL_MAX,
00123 -1.0,
00124 0.0,
00125 1e-6,
00126 1e-7
00127 };
00128
00129 int RParam[NRealP] = {
00130 LPX_K_RELAX,
00131 LPX_K_TOLBND,
00132 LPX_K_TOLDJ,
00133 LPX_K_TOLPIV,
00134 LPX_K_OBJLL,
00135 LPX_K_OBJUL,
00136 LPX_K_TMLIM,
00137 LPX_K_OUTDLY,
00138 LPX_K_TOLINT,
00139 LPX_K_TOLOBJ
00140 };
00141
00142 static jmp_buf mark;
00143
00144 #if 0
00145 int
00146 glpk_fault_hook (void * , char *msg)
00147 {
00148 error ("CRITICAL ERROR in GLPK: %s", msg);
00149 longjmp (mark, -1);
00150 }
00151
00152 int
00153 glpk_print_hook (void * , char *msg)
00154 {
00155 message (0, "%s", msg);
00156 return 1;
00157 }
00158 #endif
00159
00160 int
00161 glpk (int sense, int n, int m, double *c, int nz, int *rn, int *cn,
00162 double *a, double *b, char *ctype, int *freeLB, double *lb,
00163 int *freeUB, double *ub, int *vartype, int isMIP, int lpsolver,
00164 int save_pb, double *xmin, double *fmin, double *status,
00165 double *lambda, double *redcosts, double *time, double *mem)
00166 {
00167 int errnum;
00168 int typx = 0;
00169 int method;
00170
00171 clock_t t_start = clock();
00172
00173 #if 0
00174 #ifdef GLPK_PRE_4_14
00175 lib_set_fault_hook (0, glpk_fault_hook);
00176 #else
00177 _glp_lib_fault_hook (glpk_fault_hook, 0);
00178 #endif
00179
00180 if (lpxIntParam[0] > 1)
00181 #ifdef GLPK_PRE_4_14
00182 lib_set_print_hook (0, glpk_print_hook);
00183 #else
00184 _glp_lib_print_hook (glpk_print_hook, 0);
00185 #endif
00186 #endif
00187
00188 LPX *lp = lpx_create_prob ();
00189
00190
00191
00192 if (sense == 1)
00193 lpx_set_obj_dir (lp, LPX_MIN);
00194 else
00195 lpx_set_obj_dir (lp, LPX_MAX);
00196
00197
00198 if (isMIP)
00199 lpx_set_class (lp, LPX_MIP);
00200
00201 lpx_add_cols (lp, n);
00202 for (int i = 0; i < n; i++)
00203 {
00204
00205 if (! freeLB[i] && ! freeUB[i])
00206 {
00207 if (lb[i] != ub[i])
00208 lpx_set_col_bnds (lp, i+1, LPX_DB, lb[i], ub[i]);
00209 else
00210 lpx_set_col_bnds (lp, i+1, LPX_FX, lb[i], ub[i]);
00211 }
00212 else
00213 {
00214 if (! freeLB[i] && freeUB[i])
00215 lpx_set_col_bnds (lp, i+1, LPX_LO, lb[i], ub[i]);
00216 else
00217 {
00218 if (freeLB[i] && ! freeUB[i])
00219 lpx_set_col_bnds (lp, i+1, LPX_UP, lb[i], ub[i]);
00220 else
00221 lpx_set_col_bnds (lp, i+1, LPX_FR, lb[i], ub[i]);
00222 }
00223 }
00224
00225
00226
00227 lpx_set_obj_coef(lp,i+1,c[i]);
00228
00229 if (isMIP)
00230 lpx_set_col_kind (lp, i+1, vartype[i]);
00231 }
00232
00233 lpx_add_rows (lp, m);
00234
00235 for (int i = 0; i < m; i++)
00236 {
00237
00238
00239
00240
00241
00242
00243
00244
00245 switch (ctype[i])
00246 {
00247 case 'F':
00248 typx = LPX_FR;
00249 break;
00250
00251 case 'U':
00252 typx = LPX_UP;
00253 break;
00254
00255 case 'L':
00256 typx = LPX_LO;
00257 break;
00258
00259 case 'S':
00260 typx = LPX_FX;
00261 break;
00262
00263 case 'D':
00264 typx = LPX_DB;
00265 break;
00266 }
00267
00268 lpx_set_row_bnds (lp, i+1, typx, b[i], b[i]);
00269
00270 }
00271
00272 lpx_load_matrix (lp, nz, rn, cn, a);
00273
00274 if (save_pb)
00275 {
00276 static char tmp[] = "outpb.lp";
00277 if (lpx_write_cpxlp (lp, tmp) != 0)
00278 {
00279 error ("__glpk__: unable to write problem");
00280 longjmp (mark, -1);
00281 }
00282 }
00283
00284
00285
00286
00287 if (lpxIntParam[1] && (! lpxIntParam[16] || lpsolver != 1))
00288 lpx_scale_prob (lp);
00289
00290
00291 if (lpsolver == 1 && ! lpxIntParam[16])
00292 lpx_adv_basis (lp);
00293
00294 for(int i = 0; i < NIntP; i++)
00295 lpx_set_int_parm (lp, IParam[i], lpxIntParam[i]);
00296
00297 for (int i = 0; i < NRealP; i++)
00298 lpx_set_real_parm (lp, RParam[i], lpxRealParam[i]);
00299
00300 if (lpsolver == 1)
00301 method = 'S';
00302 else
00303 method = 'T';
00304
00305 switch (method)
00306 {
00307 case 'S':
00308 {
00309 if (isMIP)
00310 {
00311 method = 'I';
00312 errnum = lpx_simplex (lp);
00313 errnum = lpx_integer (lp);
00314 }
00315 else
00316 errnum = lpx_simplex(lp);
00317 }
00318 break;
00319
00320 case 'T':
00321 errnum = lpx_interior(lp);
00322 break;
00323
00324 default:
00325 break;
00326 #if 0
00327 #ifdef GLPK_PRE_4_14
00328 insist (method != method);
00329 #else
00330 static char tmp[] = "method != method";
00331 glpk_fault_hook (0, tmp);
00332 #endif
00333 #endif
00334 }
00335
00336
00337
00338
00339
00340
00341 if (errnum == LPX_E_OK)
00342 {
00343 if (isMIP)
00344 {
00345 *status = lpx_mip_status (lp);
00346 *fmin = lpx_mip_obj_val (lp);
00347 }
00348 else
00349 {
00350 if (lpsolver == 1)
00351 {
00352 *status = lpx_get_status (lp);
00353 *fmin = lpx_get_obj_val (lp);
00354 }
00355 else
00356 {
00357 *status = lpx_ipt_status (lp);
00358 *fmin = lpx_ipt_obj_val (lp);
00359 }
00360 }
00361
00362 if (isMIP)
00363 {
00364 for (int i = 0; i < n; i++)
00365 xmin[i] = lpx_mip_col_val (lp, i+1);
00366 }
00367 else
00368 {
00369
00370 for (int i = 0; i < n; i++)
00371 {
00372 if (lpsolver == 1)
00373 xmin[i] = lpx_get_col_prim (lp, i+1);
00374 else
00375 xmin[i] = lpx_ipt_col_prim (lp, i+1);
00376 }
00377
00378
00379 for (int i = 0; i < m; i++)
00380 {
00381 if (lpsolver == 1)
00382 lambda[i] = lpx_get_row_dual (lp, i+1);
00383 else
00384 lambda[i] = lpx_ipt_row_dual (lp, i+1);
00385 }
00386
00387
00388 for (int i = 0; i < lpx_get_num_cols (lp); i++)
00389 {
00390 if (lpsolver == 1)
00391 redcosts[i] = lpx_get_col_dual (lp, i+1);
00392 else
00393 redcosts[i] = lpx_ipt_col_dual (lp, i+1);
00394 }
00395 }
00396
00397 *time = (clock () - t_start) / CLOCKS_PER_SEC;
00398
00399 #ifdef GLPK_PRE_4_14
00400 *mem = (lib_env_ptr () -> mem_tpeak);
00401 #else
00402 *mem = 0;
00403 #endif
00404
00405 lpx_delete_prob (lp);
00406 return 0;
00407 }
00408
00409 lpx_delete_prob (lp);
00410
00411 *status = errnum;
00412
00413 return errnum;
00414 }
00415
00416 #endif
00417
00418 #define OCTAVE_GLPK_GET_REAL_PARAM(NAME, IDX) \
00419 do \
00420 { \
00421 octave_value tmp = PARAM.getfield (NAME); \
00422 \
00423 if (tmp.is_defined ()) \
00424 { \
00425 if (! tmp.is_empty ()) \
00426 { \
00427 lpxRealParam[IDX] = tmp.scalar_value (); \
00428 \
00429 if (error_state) \
00430 { \
00431 error ("glpk: invalid value in PARAM." NAME); \
00432 return retval; \
00433 } \
00434 } \
00435 else \
00436 { \
00437 error ("glpk: invalid value in PARAM." NAME); \
00438 return retval; \
00439 } \
00440 } \
00441 } \
00442 while (0)
00443
00444 #define OCTAVE_GLPK_GET_INT_PARAM(NAME, VAL) \
00445 do \
00446 { \
00447 octave_value tmp = PARAM.getfield (NAME); \
00448 \
00449 if (tmp.is_defined ()) \
00450 { \
00451 if (! tmp.is_empty ()) \
00452 { \
00453 VAL = tmp.int_value (); \
00454 \
00455 if (error_state) \
00456 { \
00457 error ("glpk: invalid value in PARAM." NAME); \
00458 return retval; \
00459 } \
00460 } \
00461 else \
00462 { \
00463 error ("glpk: invalid value in PARAM." NAME); \
00464 return retval; \
00465 } \
00466 } \
00467 } \
00468 while (0)
00469
00470 DEFUN_DLD (__glpk__, args, ,
00471 "-*- texinfo -*-\n\
00472 @deftypefn {Loadable Function} {[@var{values}] =} __glpk__ (@var{args})\n\
00473 Undocumented internal function.\n\
00474 @end deftypefn")
00475 {
00476
00477 octave_value_list retval;
00478
00479 #if defined (HAVE_GLPK)
00480
00481 int nrhs = args.length ();
00482
00483 if (nrhs != 9)
00484 {
00485 print_usage ();
00486 return retval;
00487 }
00488
00489
00490
00491 volatile int mrowsc = args(0).rows();
00492
00493 Matrix C (args(0).matrix_value ());
00494
00495 if (error_state)
00496 {
00497 error ("__glpk__: invalid value of C");
00498 return retval;
00499 }
00500
00501 double *c = C.fortran_vec ();
00502 Array<int> rn;
00503 Array<int> cn;
00504 ColumnVector a;
00505 volatile int mrowsA;
00506 volatile int nz = 0;
00507
00508
00509
00510 if (args(1).is_sparse_type ())
00511 {
00512 SparseMatrix A = args(1).sparse_matrix_value ();
00513
00514 if (error_state)
00515 {
00516 error ("__glpk__: invalid value of A");
00517 return retval;
00518 }
00519
00520 mrowsA = A.rows ();
00521 octave_idx_type Anc = A.cols ();
00522 octave_idx_type Anz = A.nnz ();
00523 rn.resize (dim_vector (Anz+1, 1));
00524 cn.resize (dim_vector (Anz+1, 1));
00525 a.resize (Anz+1, 0.0);
00526
00527 if (Anc != mrowsc)
00528 {
00529 error ("__glpk__: invalid value of A");
00530 return retval;
00531 }
00532
00533 for (octave_idx_type j = 0; j < Anc; j++)
00534 for (octave_idx_type i = A.cidx(j); i < A.cidx(j+1); i++)
00535 {
00536 nz++;
00537 rn(nz) = A.ridx(i) + 1;
00538 cn(nz) = j + 1;
00539 a(nz) = A.data(i);
00540 }
00541 }
00542 else
00543 {
00544 Matrix A (args(1).matrix_value ());
00545
00546 if (error_state)
00547 {
00548 error ("__glpk__: invalid value of A");
00549 return retval;
00550 }
00551
00552 mrowsA = A.rows ();
00553 rn.resize (dim_vector (mrowsA*mrowsc+1, 1));
00554 cn.resize (dim_vector (mrowsA*mrowsc+1, 1));
00555 a.resize (mrowsA*mrowsc+1, 0.0);
00556
00557 for (int i = 0; i < mrowsA; i++)
00558 {
00559 for (int j = 0; j < mrowsc; j++)
00560 {
00561 if (A(i,j) != 0)
00562 {
00563 nz++;
00564 rn(nz) = i + 1;
00565 cn(nz) = j + 1;
00566 a(nz) = A(i,j);
00567 }
00568 }
00569 }
00570
00571 }
00572
00573
00574
00575 Matrix B (args(2).matrix_value ());
00576
00577 if (error_state)
00578 {
00579 error ("__glpk__: invalid value of B");
00580 return retval;
00581 }
00582
00583 double *b = B.fortran_vec ();
00584
00585
00586
00587 Matrix LB (args(3).matrix_value ());
00588
00589 if (error_state || LB.length () < mrowsc)
00590 {
00591 error ("__glpk__: invalid value of LB");
00592 return retval;
00593 }
00594
00595 double *lb = LB.fortran_vec ();
00596
00597
00598 Array<int> freeLB (dim_vector (mrowsc, 1));
00599 for (int i = 0; i < mrowsc; i++)
00600 {
00601 if (xisinf (lb[i]))
00602 {
00603 freeLB(i) = 1;
00604 lb[i] = -octave_Inf;
00605 }
00606 else
00607 freeLB(i) = 0;
00608 }
00609
00610
00611
00612 Matrix UB (args(4).matrix_value ());
00613
00614 if (error_state || UB.length () < mrowsc)
00615 {
00616 error ("__glpk__: invalid value of UB");
00617 return retval;
00618 }
00619
00620 double *ub = UB.fortran_vec ();
00621
00622 Array<int> freeUB (dim_vector (mrowsc, 1));
00623 for (int i = 0; i < mrowsc; i++)
00624 {
00625 if (xisinf (ub[i]))
00626 {
00627 freeUB(i) = 1;
00628 ub[i] = octave_Inf;
00629 }
00630 else
00631 freeUB(i) = 0;
00632 }
00633
00634
00635
00636 charMatrix CTYPE (args(5).char_matrix_value ());
00637
00638 if (error_state)
00639 {
00640 error ("__glpk__: invalid value of CTYPE");
00641 return retval;
00642 }
00643
00644 char *ctype = CTYPE.fortran_vec ();
00645
00646
00647 charMatrix VTYPE (args(6).char_matrix_value ());
00648
00649 if (error_state)
00650 {
00651 error ("__glpk__: invalid value of VARTYPE");
00652 return retval;
00653 }
00654
00655 Array<int> vartype (dim_vector (mrowsc, 1));
00656 volatile int isMIP = 0;
00657 for (int i = 0; i < mrowsc ; i++)
00658 {
00659 if (VTYPE(i,0) == 'I')
00660 {
00661 isMIP = 1;
00662 vartype(i) = LPX_IV;
00663 }
00664 else
00665 vartype(i) = LPX_CV;
00666 }
00667
00668
00669 volatile int sense;
00670 double SENSE = args(7).scalar_value ();
00671
00672 if (error_state)
00673 {
00674 error ("__glpk__: invalid value of SENSE");
00675 return retval;
00676 }
00677
00678 if (SENSE >= 0)
00679 sense = 1;
00680 else
00681 sense = -1;
00682
00683
00684 octave_scalar_map PARAM = args(8).scalar_map_value ();
00685
00686 if (error_state)
00687 {
00688 error ("__glpk__: invalid value of PARAM");
00689 return retval;
00690 }
00691
00692
00693
00694
00695
00696
00697 OCTAVE_GLPK_GET_INT_PARAM ("msglev", lpxIntParam[0]);
00698 if (lpxIntParam[0] < 0 || lpxIntParam[0] > 3)
00699 {
00700 error ("__glpk__: PARAM.msglev must be 0 (no output [default]) or 1 (error messages only) or 2 (normal output) or 3 (full output)");
00701 return retval;
00702 }
00703
00704
00705 OCTAVE_GLPK_GET_INT_PARAM ("scale", lpxIntParam[1]);
00706 if (lpxIntParam[1] < 0 || lpxIntParam[1] > 2)
00707 {
00708 error ("__glpk__: PARAM.scale must be 0 (no scaling) or 1 (equilibration scaling [default]) or 2 (geometric mean scaling)");
00709 return retval;
00710 }
00711
00712
00713 OCTAVE_GLPK_GET_INT_PARAM ("dual", lpxIntParam[2]);
00714 if (lpxIntParam[2] < 0 || lpxIntParam[2] > 1)
00715 {
00716 error ("__glpk__: PARAM.dual must be 0 (do NOT use dual simplex [default]) or 1 (use dual simplex)");
00717 return retval;
00718 }
00719
00720
00721 OCTAVE_GLPK_GET_INT_PARAM ("price", lpxIntParam[3]);
00722 if (lpxIntParam[3] < 0 || lpxIntParam[3] > 1)
00723 {
00724 error ("__glpk__: PARAM.price must be 0 (textbook pricing) or 1 (steepest edge pricing [default])");
00725 return retval;
00726 }
00727
00728
00729 OCTAVE_GLPK_GET_INT_PARAM ("round", lpxIntParam[4]);
00730 if (lpxIntParam[4] < 0 || lpxIntParam[4] > 1)
00731 {
00732 error ("__glpk__: PARAM.round must be 0 (report all primal and dual values [default]) or 1 (replace tiny primal and dual values by exact zero)");
00733 return retval;
00734 }
00735
00736
00737 OCTAVE_GLPK_GET_INT_PARAM ("itlim", lpxIntParam[5]);
00738
00739
00740 OCTAVE_GLPK_GET_INT_PARAM ("itcnt", lpxIntParam[6]);
00741
00742
00743 OCTAVE_GLPK_GET_INT_PARAM ("outfrq", lpxIntParam[7]);
00744
00745
00746 OCTAVE_GLPK_GET_INT_PARAM ("branch", lpxIntParam[14]);
00747 if (lpxIntParam[14] < 0 || lpxIntParam[14] > 2)
00748 {
00749 error ("__glpk__: PARAM.branch must be (MIP only) 0 (branch on first variable) or 1 (branch on last variable) or 2 (branch using a heuristic by Driebeck and Tomlin [default]");
00750 return retval;
00751 }
00752
00753
00754 OCTAVE_GLPK_GET_INT_PARAM ("btrack", lpxIntParam[15]);
00755 if (lpxIntParam[15] < 0 || lpxIntParam[15] > 2)
00756 {
00757 error ("__glpk__: PARAM.btrack must be (MIP only) 0 (depth first search) or 1 (breadth first search) or 2 (backtrack using the best projection heuristic [default]");
00758 return retval;
00759 }
00760
00761
00762 OCTAVE_GLPK_GET_INT_PARAM ("presol", lpxIntParam[16]);
00763 if (lpxIntParam[16] < 0 || lpxIntParam[16] > 1)
00764 {
00765 error ("__glpk__: PARAM.presol must be 0 (do NOT use LP presolver) or 1 (use LP presolver [default])");
00766 return retval;
00767 }
00768
00769
00770 volatile int lpsolver = 1;
00771 OCTAVE_GLPK_GET_INT_PARAM ("lpsolver", lpsolver);
00772 if (lpsolver < 1 || lpsolver > 2)
00773 {
00774 error ("__glpk__: PARAM.lpsolver must be 1 (simplex method) or 2 (interior point method)");
00775 return retval;
00776 }
00777
00778
00779 volatile int save_pb = 0;
00780 OCTAVE_GLPK_GET_INT_PARAM ("save", save_pb);
00781 save_pb = save_pb != 0;
00782
00783
00784
00785
00786
00787
00788 OCTAVE_GLPK_GET_REAL_PARAM ("relax", 0);
00789
00790
00791
00792 OCTAVE_GLPK_GET_REAL_PARAM ("tolbnd", 1);
00793
00794
00795
00796 OCTAVE_GLPK_GET_REAL_PARAM ("toldj", 2);
00797
00798
00799
00800 OCTAVE_GLPK_GET_REAL_PARAM ("tolpiv", 3);
00801
00802 OCTAVE_GLPK_GET_REAL_PARAM ("objll", 4);
00803
00804 OCTAVE_GLPK_GET_REAL_PARAM ("objul", 5);
00805
00806 OCTAVE_GLPK_GET_REAL_PARAM ("tmlim", 6);
00807
00808 OCTAVE_GLPK_GET_REAL_PARAM ("outdly", 7);
00809
00810 OCTAVE_GLPK_GET_REAL_PARAM ("tolint", 8);
00811
00812 OCTAVE_GLPK_GET_REAL_PARAM ("tolobj", 9);
00813
00814
00815 ColumnVector xmin (mrowsc, octave_NA);
00816 double fmin = octave_NA;
00817 double status;
00818 ColumnVector lambda (mrowsA, octave_NA);
00819 ColumnVector redcosts (mrowsc, octave_NA);
00820 double time;
00821 double mem;
00822
00823 int jmpret = setjmp (mark);
00824
00825 if (jmpret == 0)
00826 glpk (sense, mrowsc, mrowsA, c, nz, rn.fortran_vec (),
00827 cn.fortran_vec (), a.fortran_vec (), b, ctype,
00828 freeLB.fortran_vec (), lb, freeUB.fortran_vec (), ub,
00829 vartype.fortran_vec (), isMIP, lpsolver, save_pb,
00830 xmin.fortran_vec (), &fmin, &status, lambda.fortran_vec (),
00831 redcosts.fortran_vec (), &time, &mem);
00832
00833 octave_scalar_map extra;
00834
00835 if (! isMIP)
00836 {
00837 extra.assign ("lambda", lambda);
00838 extra.assign ("redcosts", redcosts);
00839 }
00840
00841 extra.assign ("time", time);
00842 extra.assign ("mem", mem);
00843
00844 retval(3) = extra;
00845 retval(2) = status;
00846 retval(1) = fmin;
00847 retval(0) = xmin;
00848
00849 #else
00850
00851 gripe_not_supported ("glpk");
00852
00853 #endif
00854
00855 return retval;
00856 }
00857
00858
00859
00860
00861
00862
00863