1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the class library */
4 /* SoPlex --- the Sequential object-oriented simPlex. */
5 /* */
6 /* Copyright (C) 1996-2021 Konrad-Zuse-Zentrum */
7 /* fuer Informationstechnik Berlin */
8 /* */
9 /* SoPlex is distributed under the terms of the ZIB Academic Licence. */
10 /* */
11 /* You should have received a copy of the ZIB Academic License */
12 /* along with SoPlex; see the file COPYING. If not email to soplex@zib.de. */
13 /* */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15
16 /**@file soplexmain.cpp
17 * @brief Command line interface of SoPlex LP solver
18 */
19
20 #include <assert.h>
21 #include <math.h>
22 #include <string.h>
23
24 #include <iostream>
25 #include <iomanip>
26 #include <fstream>
27
28 #include "soplex.h"
29 #include "soplex/validation.h"
30
31 using namespace soplex;
32
33 // function prototype
34 int main(int argc, char* argv[]);
35
36 // prints usage and command line options
37 static
38 void printUsage(const char* const argv[], int idx)
39 {
40 const char* usage =
41 "general options:\n"
42 " --readbas=<basfile> read starting basis from file\n"
43 " --writebas=<basfile> write terminal basis to file\n"
44 " --writefile=<lpfile> write LP to file in LP or MPS format depending on extension\n"
45 " --writedual=<lpfile> write the dual LP to a file in LP or MPS formal depending on extension\n"
46 " --<type>:<name>=<val> change parameter value using syntax of settings file entries\n"
47 " --loadset=<setfile> load parameters from settings file (overruled by command line parameters)\n"
48 " --saveset=<setfile> save parameters to settings file\n"
49 " --diffset=<setfile> save modified parameters to settings file\n"
50 " --extsol=<value> external solution for soplex to use for validation\n"
51 "\n"
52 "limits and tolerances:\n"
53 " -t<s> set time limit to <s> seconds\n"
54 " -i<n> set iteration limit to <n>\n"
55 " -f<eps> set primal feasibility tolerance to <eps>\n"
56 " -o<eps> set dual feasibility (optimality) tolerance to <eps>\n"
57 " -l<eps> set validation tolerance to <eps>\n"
58 "\n"
59 "algorithmic settings (* indicates default):\n"
60 " --readmode=<value> choose reading mode for <lpfile> (0* - floating-point, 1 - rational)\n"
61 " --solvemode=<value> choose solving mode (0 - floating-point solve, 1* - auto, 2 - force iterative refinement)\n"
62 " --arithmetic=<value> choose base arithmetic type (0 - double, 1 - quadprecision, 2 - higher multiprecision)\n"
63 #ifdef SOPLEX_WITH_MPFR
64 " --precision=<value> choose precision for multiprecision solve (only active when arithmetic=2 minimal value = 50)\n"
65 #endif
66 #ifdef SOPLEX_WITH_CPPMPF
67 " --precision=<value> choose precision for multiprecision solve (only active when arithmetic=2, possible values 50,100,200, compile with mpfr for arbitrary precision)\n"
68 #endif
69 " -s<value> choose simplifier/presolver (0 - off, 1* - internal, 2*- PaPILO)\n"
70 " -g<value> choose scaling (0 - off, 1 - uni-equilibrium, 2* - bi-equilibrium, 3 - geometric, 4 - iterated geometric, 5 - least squares, 6 - geometric-equilibrium)\n"
71 " -p<value> choose pricing (0* - auto, 1 - dantzig, 2 - parmult, 3 - devex, 4 - quicksteep, 5 - steep)\n"
72 " -r<value> choose ratio tester (0 - textbook, 1 - harris, 2 - fast, 3* - boundflipping)\n"
73 "\n"
74 "display options:\n"
75 " -v<level> set verbosity to <level> (0 - error, 3 - normal, 5 - high)\n"
76 " -x print primal solution\n"
77 " -y print dual multipliers\n"
78 " -X print primal solution in rational numbers\n"
79 " -Y print dual multipliers in rational numbers\n"
80 " -q display detailed statistics\n"
81 " -c perform final check of optimal solution in original problem\n"
82 "\n";
83
84 if(idx <= 0)
85 std::cerr << "missing input file\n\n";
86 else
87 std::cerr << "invalid option \"" << argv[idx] << "\"\n\n";
88
89 std::cerr << "usage: " << argv[0] << " " << "[options] <lpfile>\n"
90 #ifdef SOPLEX_WITH_ZLIB
91 << " <lpfile> linear program as .mps[.gz] or .lp[.gz] file\n\n"
92 #else
93 << " <lpfile> linear program as .mps or .lp file\n\n"
94 #endif
95 << usage;
96 }
97
98 // cleans up C strings
99 static
100 void freeStrings(char*& s1, char*& s2, char*& s3, char*& s4, char*& s5)
101 {
102 if(s1 != 0)
103 {
104 delete [] s1;
105 s1 = 0;
106 }
107
108 if(s2 != 0)
109 {
110 delete [] s2;
111 s2 = 0;
112 }
113
114 if(s3 != 0)
115 {
116 delete [] s3;
117 s3 = 0;
118 }
119
120 if(s4 != 0)
121 {
122 delete [] s4;
123 s4 = 0;
124 }
125
126 if(s5 != 0)
127 {
128 delete [] s5;
129 s5 = 0;
130 }
131 }
132
133 /// performs external feasibility check with real type
134 ///@todo implement external check; currently we use the internal methods for convenience
135
136 template <class R>
137 static
138 void checkSolutionReal(SoPlexBase<R>& soplex)
139 {
140 if(soplex.hasPrimal())
141 {
142 R boundviol;
143 R rowviol;
144 R sumviol;
145
146 if(soplex.getBoundViolation(boundviol, sumviol) && soplex.getRowViolation(rowviol, sumviol))
147 {
148 MSG_INFO1(soplex.spxout,
149 R maxviol = boundviol > rowviol ? boundviol : rowviol;
150 bool feasible = (maxviol <= soplex.realParam(SoPlexBase<R>::FEASTOL));
151 soplex.spxout << "Primal solution " << (feasible ? "feasible" : "infeasible")
152 << " in original problem (max. violation = " << std::scientific << maxviol
153 << std::setprecision(8) << std::fixed << ").\n");
154 }
155 else
156 {
157 MSG_INFO1(soplex.spxout, soplex.spxout << "Could not check primal solution.\n");
158 }
159 }
160 else
161 {
162 MSG_INFO1(soplex.spxout, soplex.spxout << "No primal solution available.\n");
163 }
164
165 if(soplex.hasDual())
166 {
167 R redcostviol;
168 R dualviol;
169 R sumviol;
170
171 if(soplex.getRedCostViolation(redcostviol, sumviol) && soplex.getDualViolation(dualviol, sumviol))
172 {
173 MSG_INFO1(soplex.spxout,
174 R maxviol = redcostviol > dualviol ? redcostviol : dualviol;
175 bool feasible = (maxviol <= soplex.realParam(SoPlexBase<R>::OPTTOL));
176 soplex.spxout << "Dual solution " << (feasible ? "feasible" : "infeasible")
177 << " in original problem (max. violation = " << std::scientific << maxviol
178 << std::setprecision(8) << std::fixed << ").\n"
179 );
180 }
181 else
182 {
183 MSG_INFO1(soplex.spxout, soplex.spxout << "Could not check dual solution.\n");
184 }
185 }
186 else
187 {
188 MSG_INFO1(soplex.spxout, soplex.spxout << "No dual solution available.\n");
189 }
190 }
191
192 /// performs external feasibility check with rational type
193 ///@todo implement external check; currently we use the internal methods for convenience
194 template <class R>
195 static void checkSolutionRational(SoPlexBase<R>& soplex)
196 {
197 if(soplex.hasPrimal())
198 {
199 Rational boundviol;
200 Rational rowviol;
201 Rational sumviol;
202
203 if(soplex.getBoundViolationRational(boundviol, sumviol)
204 && soplex.getRowViolationRational(rowviol, sumviol))
205 {
206 MSG_INFO1(soplex.spxout,
207 Rational maxviol = boundviol > rowviol ? boundviol : rowviol;
208 bool feasible = (maxviol <= soplex.realParam(SoPlexBase<R>::FEASTOL));
209 soplex.spxout << "Primal solution " << (feasible ? "feasible" : "infeasible") <<
210 " in original problem (max. violation = " << maxviol << ").\n"
211 );
212 }
213 else
214 {
215 MSG_INFO1(soplex.spxout, soplex.spxout << "Could not check primal solution.\n");
216 }
217 }
218 else
219 {
220 MSG_INFO1(soplex.spxout, soplex.spxout << "No primal solution available.\n");
221 }
222
223 if(soplex.hasDual())
224 {
225 Rational redcostviol;
226 Rational dualviol;
227 Rational sumviol;
228
229 if(soplex.getRedCostViolationRational(redcostviol, sumviol)
230 && soplex.getDualViolationRational(dualviol, sumviol))
231 {
232 MSG_INFO1(soplex.spxout,
233 Rational maxviol = redcostviol > dualviol ? redcostviol : dualviol;
234 bool feasible = (maxviol <= soplex.realParam(SoPlexBase<R>::OPTTOL));
235 soplex.spxout << "Dual solution " << (feasible ? "feasible" : "infeasible") <<
236 " in original problem (max. violation = " << maxviol << ").\n"
237 );
238 }
239 else
240 {
241 MSG_INFO1(soplex.spxout, soplex.spxout << "Could not check dual solution.\n");
242 }
243 }
244 else
245 {
246 MSG_INFO1(soplex.spxout, soplex.spxout << "No dual solution available.\n");
247 }
248 }
249
250 /// performs external feasibility check according to check mode
251 template <class R>
252 void checkSolution(SoPlexBase<R>& soplex)
253 {
254 if(soplex.intParam(SoPlexBase<R>::CHECKMODE) == SoPlexBase<R>::CHECKMODE_RATIONAL
255 || (soplex.intParam(SoPlexBase<R>::CHECKMODE) == SoPlexBase<R>::CHECKMODE_AUTO
256 && soplex.intParam(SoPlexBase<R>::READMODE) == SoPlexBase<R>::READMODE_RATIONAL))
257 {
258 checkSolutionRational(soplex);
259 }
260 else
261 {
262 checkSolutionReal(soplex);
263 }
264
265 MSG_INFO1(soplex.spxout, soplex.spxout << "\n");
266 }
267
268 template <class R>
269 static
270 void printPrimalSolution(SoPlexBase<R>& soplex, NameSet& colnames, NameSet& rownames,
271 bool real = true, bool rational = false)
272 {
273 int printprec;
274 int printwidth;
275 printprec = (int) - log10(Real(Param::epsilon()));
276 printwidth = printprec + 10;
277
278 if(real)
279 {
280 VectorBase<R> primal(soplex.numCols());
281
282 if(soplex.getPrimalRay(primal))
283 {
284 MSG_INFO1(soplex.spxout, soplex.spxout << "\nPrimal ray (name, value):\n";)
285
286 for(int i = 0; i < soplex.numCols(); ++i)
287 {
288 if(isNotZero(primal[i]))
289 {
290 MSG_INFO1(soplex.spxout, soplex.spxout << colnames[i] << "\t"
291 << std::setw(printwidth) << std::setprecision(printprec)
292 << primal[i] << std::endl;)
293 }
294 }
295
296 MSG_INFO1(soplex.spxout, soplex.spxout << "All other entries are zero (within "
297 << std::setprecision(1) << std::scientific << Param::epsilon()
298 << std::setprecision(8) << std::fixed
299 << ")." << std::endl;)
300 }
301 else if(soplex.isPrimalFeasible() && soplex.getPrimal(primal))
302 {
303 int nNonzeros = 0;
304 MSG_INFO1(soplex.spxout, soplex.spxout << "\nPrimal solution (name, value):\n";)
305
306 for(int i = 0; i < soplex.numCols(); ++i)
307 {
308 if(isNotZero(primal[i]))
309 {
310 MSG_INFO1(soplex.spxout, soplex.spxout << colnames[i] << "\t"
311 << std::setw(printwidth) << std::setprecision(printprec)
312 << primal[i] << std::endl;)
313 ++nNonzeros;
314 }
315 }
316
317 MSG_INFO1(soplex.spxout, soplex.spxout << "All other variables are zero (within "
318 << std::setprecision(1) << std::scientific << Param::epsilon()
319 << std::setprecision(8) << std::fixed
320 << "). Solution has " << nNonzeros << " nonzero entries." << std::endl;)
321 }
322 else
323 MSG_INFO1(soplex.spxout, soplex.spxout << "No primal information available.\n")
324 }
325
326 if(rational)
327 {
328 VectorRational primal(soplex.numCols());
329
330 if(soplex.getPrimalRayRational(primal))
331 {
332 MSG_INFO1(soplex.spxout, soplex.spxout << "\nPrimal ray (name, value):\n";)
333
334 for(int i = 0; i < soplex.numCols(); ++i)
335 {
336 if(primal[i] != (Rational) 0)
337 {
338 MSG_INFO1(soplex.spxout, soplex.spxout << colnames[i] << "\t"
339 << std::setw(printwidth) << std::setprecision(printprec)
340 << primal[i] << std::endl;)
341 }
342 }
343
344 MSG_INFO1(soplex.spxout, soplex.spxout << "All other entries are zero." << std::endl;)
345 }
346
347 if(soplex.isPrimalFeasible() && soplex.getPrimalRational(primal))
348 {
349 int nNonzeros = 0;
350 MSG_INFO1(soplex.spxout, soplex.spxout << "\nPrimal solution (name, value):\n";)
351
352 for(int i = 0; i < soplex.numColsRational(); ++i)
353 {
354 if(primal[i] != (Rational) 0)
355 {
356 MSG_INFO1(soplex.spxout, soplex.spxout << colnames[i] << "\t" << primal[i] << std::endl;)
357 ++nNonzeros;
358 }
359 }
360
361 MSG_INFO1(soplex.spxout, soplex.spxout << "All other variables are zero. Solution has "
362 << nNonzeros << " nonzero entries." << std::endl;)
363 }
364 else
365 MSG_INFO1(soplex.spxout, soplex.spxout << "No primal (rational) solution available.\n")
366
367 }
368 }
369
370 template <class R>
371 static
372 void printDualSolution(SoPlexBase<R>& soplex, NameSet& colnames, NameSet& rownames,
373 bool real = true, bool rational = false)
374 {
375 int printprec;
376 int printwidth;
377 printprec = (int) - log10(Real(Param::epsilon()));
378 printwidth = printprec + 10;
379
380 if(real)
381 {
382 VectorBase<R> dual(soplex.numRows());
383
384 if(soplex.getDualFarkas(dual))
385 {
386 MSG_INFO1(soplex.spxout, soplex.spxout << "\nDual ray (name, value):\n";)
387
388 for(int i = 0; i < soplex.numRows(); ++i)
389 {
390 if(isNotZero(dual[i]))
391 {
392 MSG_INFO1(soplex.spxout, soplex.spxout << rownames[i] << "\t"
393 << std::setw(printwidth) << std::setprecision(printprec)
394 << dual[i] << std::endl;)
395 }
396 }
397
398 MSG_INFO1(soplex.spxout, soplex.spxout << "All other entries are zero (within "
399 << std::setprecision(1) << std::scientific << Param::epsilon()
400 << std::setprecision(8) << std::fixed << ")." << std::endl;)
401 }
402 else if(soplex.isDualFeasible() && soplex.getDual(dual))
403 {
404 MSG_INFO1(soplex.spxout, soplex.spxout << "\nDual solution (name, value):\n";)
405
406 for(int i = 0; i < soplex.numRows(); ++i)
407 {
408 if(isNotZero(dual[i]))
409 {
410 MSG_INFO1(soplex.spxout, soplex.spxout << rownames[i] << "\t"
411 << std::setw(printwidth) << std::setprecision(printprec)
412 << dual[i] << std::endl;)
413 }
414 }
415
416 MSG_INFO1(soplex.spxout, soplex.spxout << "All other dual values are zero (within "
417 << std::setprecision(1) << std::scientific << Param::epsilon()
418 << std::setprecision(8) << std::fixed << ")." << std::endl;)
419
420 VectorBase<R> redcost(soplex.numCols());
421
422 if(soplex.getRedCost(redcost))
423 {
424 MSG_INFO1(soplex.spxout, soplex.spxout << "\nReduced costs (name, value):\n";)
425
426 for(int i = 0; i < soplex.numCols(); ++i)
427 {
428 if(isNotZero(redcost[i]))
429 {
430 MSG_INFO1(soplex.spxout, soplex.spxout << colnames[i] << "\t"
431 << std::setw(printwidth) << std::setprecision(printprec)
432 << redcost[i] << std::endl;)
433 }
434 }
435
436 MSG_INFO1(soplex.spxout, soplex.spxout << "All other reduced costs are zero (within "
437 << std::setprecision(1) << std::scientific << Param::epsilon()
438 << std::setprecision(8) << std::fixed << ")." << std::endl;)
439 }
440 }
441 else
442 MSG_INFO1(soplex.spxout, soplex.spxout << "No dual information available.\n")
443 }
444
445 if(rational)
446 {
447 VectorRational dual(soplex.numRows());
448
449 if(soplex.getDualFarkasRational(dual))
450 {
451 MSG_INFO1(soplex.spxout, soplex.spxout << "\nDual ray (name, value):\n";)
452
453 for(int i = 0; i < soplex.numRows(); ++i)
454 {
455 if(dual[i] != (Rational) 0)
456 {
457 MSG_INFO1(soplex.spxout, soplex.spxout << rownames[i] << "\t"
458 << std::setw(printwidth)
459 << std::setprecision(printprec)
460 << dual[i] << std::endl;)
461 }
462 }
463
464 MSG_INFO1(soplex.spxout, soplex.spxout << "All other entries are zero." << std::endl;)
465 }
466
467 if(soplex.isDualFeasible() && soplex.getDualRational(dual))
468 {
469 MSG_INFO1(soplex.spxout, soplex.spxout << "\nDual solution (name, value):\n";)
470
471 for(int i = 0; i < soplex.numRowsRational(); ++i)
472 {
473 if(dual[i] != (Rational) 0)
474 MSG_INFO1(soplex.spxout, soplex.spxout << rownames[i] << "\t" << dual[i] << std::endl;)
475 }
476
477 MSG_INFO1(soplex.spxout, soplex.spxout << "All other dual values are zero." << std::endl;)
478
479 VectorRational redcost(soplex.numCols());
480
481 if(soplex.getRedCostRational(redcost))
482 {
483 MSG_INFO1(soplex.spxout, soplex.spxout << "\nReduced costs (name, value):\n";)
484
485 for(int i = 0; i < soplex.numCols(); ++i)
486 {
487 if(redcost[i] != (Rational) 0)
488 MSG_INFO1(soplex.spxout, soplex.spxout << colnames[i] << "\t" << redcost[i] << std::endl;)
489 }
490
491 MSG_INFO1(soplex.spxout, soplex.spxout << "All other reduced costs are zero." << std::endl;)
492 }
493 }
494 else
495 MSG_INFO1(soplex.spxout, soplex.spxout << "No dual (rational) solution available.\n")
496 }
497 }
498
499 // Runs SoPlex with the parsed boost variables map
500 template <class R>
501 int runSoPlex(int argc, char* argv[])
502 {
503 SoPlexBase<R>* soplex = nullptr;
504
505 Timer* readingTime = nullptr;
506 Validation<R>* validation = nullptr;
507 int optidx;
508
509 const char* lpfilename = nullptr;
510 char* readbasname = nullptr;
511 char* writebasname = nullptr;
512 char* writefilename = nullptr;
513 char* writedualfilename = nullptr;
514 char* loadsetname = nullptr;
515 char* savesetname = nullptr;
516 char* diffsetname = nullptr;
517 bool printPrimal = false;
518 bool printPrimalRational = false;
519 bool printDual = false;
520 bool printDualRational = false;
521 bool displayStatistics = false;
522 bool checkSol = false;
523
524 int returnValue = 0;
525
526 try
527 {
528 NameSet rownames;
529 NameSet colnames;
530
531 // create default timer (CPU time)
532 readingTime = TimerFactory::createTimer(Timer::USER_TIME);
533 soplex = nullptr;
534 spx_alloc(soplex);
535 new(soplex) SoPlexBase<R>();
536
537 soplex->printVersion();
538 MSG_INFO1(soplex->spxout, soplex->spxout << SOPLEX_COPYRIGHT << std::endl << std::endl);
539
540 validation = nullptr;
541 spx_alloc(validation);
542 new(validation) Validation<R>();
543
544 // no options were given
545 if(argc <= 1)
546 {
547 printUsage(argv, 0);
548 returnValue = 1;
549 goto TERMINATE;
550 }
551
552 // read arguments from command line
553 for(optidx = 1; optidx < argc; optidx++)
554 {
555 char* option = argv[optidx];
556
557 // we reached <lpfile>
558 if(option[0] != '-')
559 {
560 lpfilename = argv[optidx];
561 continue;
562 }
563
564 // option string must start with '-', must contain at least two characters, and exactly two characters if and
565 // only if it is -x, -y, -q, or -c
566 if(option[0] != '-' || option[1] == '\0'
567 || ((option[2] == '\0') != (option[1] == 'x' || option[1] == 'X' || option[1] == 'y'
568 || option[1] == 'Y' || option[1] == 'q' || option[1] == 'c')))
569 {
570 printUsage(argv, optidx);
571 returnValue = 1;
572 goto TERMINATE_FREESTRINGS;
573 }
574
575 switch(option[1])
576 {
577 case '-' :
578 {
579 option = &option[2];
580
581 // --readbas=<basfile> : read starting basis from file
582 if(strncmp(option, "readbas=", 8) == 0)
583 {
584 if(readbasname == nullptr)
585 {
586 char* filename = &option[8];
587 readbasname = new char[strlen(filename) + 1];
588 spxSnprintf(readbasname, strlen(filename) + 1, "%s", filename);
589 }
590 }
591 // --writebas=<basfile> : write terminal basis to file
592 else if(strncmp(option, "writebas=", 9) == 0)
593 {
594 if(writebasname == nullptr)
595 {
596 char* filename = &option[9];
597 writebasname = new char[strlen(filename) + 1];
598 spxSnprintf(writebasname, strlen(filename) + 1, "%s", filename);
599 }
600 }
601 // --writefile=<lpfile> : write LP to file
602 else if(strncmp(option, "writefile=", 10) == 0)
603 {
604 if(writefilename == nullptr)
605 {
606 char* filename = &option[10];
607 writefilename = new char[strlen(filename) + 1];
608 spxSnprintf(writefilename, strlen(filename) + 1, "%s", filename);
609 }
610 }
611 // --writedual=<lpfile> : write dual LP to a file
612 else if(strncmp(option, "writedual=", 10) == 0)
613 {
614 if(writedualfilename == nullptr)
615 {
616 char* dualfilename = &option[10];
617 writedualfilename = new char[strlen(dualfilename) + 1];
618 spxSnprintf(writedualfilename, strlen(dualfilename) + 1, "%s", dualfilename);
619 }
620 }
621 // --loadset=<setfile> : load parameters from settings file
622 else if(strncmp(option, "loadset=", 8) == 0)
623 {
624 if(loadsetname == nullptr)
625 {
626 char* filename = &option[8];
627 loadsetname = new char[strlen(filename) + 1];
628 spxSnprintf(loadsetname, strlen(filename) + 1, "%s", filename);
629
630 if(!soplex->loadSettingsFile(loadsetname))
631 {
632 printUsage(argv, optidx);
633 returnValue = 1;
634 goto TERMINATE_FREESTRINGS;
635 }
636 else
637 {
638 // we need to start parsing again because some command line parameters might have been overwritten
639 optidx = 0;
640 }
641 }
642 }
643 // --saveset=<setfile> : save parameters to settings file
644 else if(strncmp(option, "saveset=", 8) == 0)
645 {
646 if(savesetname == nullptr)
647 {
648 char* filename = &option[8];
649 savesetname = new char[strlen(filename) + 1];
650 spxSnprintf(savesetname, strlen(filename) + 1, "%s", filename);
651 }
652 }
653 // --diffset=<setfile> : save modified parameters to settings file
654 else if(strncmp(option, "diffset=", 8) == 0)
655 {
656 if(diffsetname == nullptr)
657 {
658 char* filename = &option[8];
659 diffsetname = new char[strlen(filename) + 1];
660 spxSnprintf(diffsetname, strlen(filename) + 1, "%s", filename);
661 }
662 }
663 // --readmode=<value> : choose reading mode for <lpfile> (0* - floating-point, 1 - rational)
664 else if(strncmp(option, "readmode=", 9) == 0)
665 {
666 if(!soplex->setIntParam(soplex->READMODE, option[9] - '0'))
667 {
668 printUsage(argv, optidx);
669 returnValue = 1;
670 goto TERMINATE_FREESTRINGS;
671 }
672 }
673 // --solvemode=<value> : choose solving mode (0* - floating-point solve, 1 - auto, 2 - force iterative refinement)
674 else if(strncmp(option, "solvemode=", 10) == 0)
675 {
676 if(!soplex->setIntParam(soplex->SOLVEMODE, option[10] - '0'))
677 {
678 printUsage(argv, optidx);
679 returnValue = 1;
680 goto TERMINATE_FREESTRINGS;
681 }
682 // if the LP is parsed rationally and might be solved rationally, we choose automatic syncmode such that
683 // the rational LP is kept after reading
684 else if(soplex->intParam(soplex->READMODE) == soplex->READMODE_RATIONAL
685 && soplex->intParam(soplex->SOLVEMODE) != soplex->SOLVEMODE_REAL)
686 {
687 soplex->setIntParam(soplex->SYNCMODE, soplex->SYNCMODE_AUTO);
688 }
689 }
690 // --extsol=<value> : external solution for soplex to use for validation
691 else if(strncmp(option, "extsol=", 7) == 0)
692 {
693 char* input = &option[7];
694
695 if(!validation->updateExternalSolution(input))
696 {
697 printUsage(argv, optidx);
698 returnValue = 1;
699 goto TERMINATE_FREESTRINGS;
700 }
701 }
702 // --arithmetic=<value> : base arithmetic type, directly handled in main()
703 else if(strncmp(option, "arithmetic=", 11) == 0)
704 {
705 continue;
706 }
707 // --precision=<value> : arithmetic precision, directly handled in main()
708 else if(strncmp(option, "precision=", 10) == 0)
709 {
710 continue;
711 }
712 // --<type>:<name>=<val> : change parameter value using syntax of settings file entries
713 else if(!soplex->parseSettingsString(option))
714 {
715 printUsage(argv, optidx);
716 returnValue = 1;
717 goto TERMINATE_FREESTRINGS;
718 }
719
720 break;
721 }
722
723 case 't' :
724
725 // -t<s> : set time limit to <s> seconds
726 if(!soplex->setRealParam(soplex->TIMELIMIT, atoi(&option[2])))
727 {
728 printUsage(argv, optidx);
729 returnValue = 1;
730 goto TERMINATE_FREESTRINGS;
731 }
732
733 break;
734
735 case 'i' :
736
737 // -i<n> : set iteration limit to <n>
738 if(!soplex->setIntParam(soplex->ITERLIMIT, atoi(&option[2])))
739 {
740 printUsage(argv, optidx);
741 returnValue = 1;
742 goto TERMINATE_FREESTRINGS;
743 }
744
745 break;
746
747 case 'f' :
748
749 // -f<eps> : set primal feasibility tolerance to <eps>
750 if(!soplex->setRealParam(soplex->FEASTOL, atof(&option[2])))
751 {
752 printUsage(argv, optidx);
753 returnValue = 1;
754 goto TERMINATE_FREESTRINGS;
755 }
756
757 break;
758
759 case 'o' :
760
761 // -o<eps> : set dual feasibility (optimality) tolerance to <eps>
762 if(!soplex->setRealParam(soplex->OPTTOL, atof(&option[2])))
763 {
764 printUsage(argv, optidx);
765 returnValue = 1;
766 goto TERMINATE_FREESTRINGS;
767 }
768
769 break;
770
771 case 'l' :
772
773 // l<eps> : set validation tolerance to <eps>
774 if(!validation->updateValidationTolerance(&option[2]))
775 {
776 printUsage(argv, optidx);
777 returnValue = 1;
778 goto TERMINATE_FREESTRINGS;
779 }
780
781 break;
782
783 case 's' :
784
785 // -s<value> : choose simplifier/presolver (0 - off, 1 - internal, 2* - PaPILO)
786 if(!soplex->setIntParam(soplex->SIMPLIFIER, option[2] - '0'))
787 {
788 printUsage(argv, optidx);
789 returnValue = 1;
790 goto TERMINATE_FREESTRINGS;
791 }
792
793 break;
794
795 case 'g' :
796
797 // -g<value> : choose scaling (0 - off, 1 - uni-equilibrium, 2* - bi-equilibrium, 3 - geometric, 4 - iterated geometric, 5 - least squares, 6 - geometric-equilibrium)
798 if(!soplex->setIntParam(soplex->SCALER, option[2] - '0'))
799 {
800 printUsage(argv, optidx);
801 returnValue = 1;
802 goto TERMINATE_FREESTRINGS;
803 }
804
805 break;
806
807 case 'p' :
808
809 // -p<value> : choose pricing (0* - auto, 1 - dantzig, 2 - parmult, 3 - devex, 4 - quicksteep, 5 - steep)
810 if(!soplex->setIntParam(soplex->PRICER, option[2] - '0'))
811 {
812 printUsage(argv, optidx);
813 returnValue = 1;
814 goto TERMINATE_FREESTRINGS;
815 }
816
817 break;
818
819 case 'r' :
820
821 // -r<value> : choose ratio tester (0 - textbook, 1 - harris, 2* - fast, 3 - boundflipping)
822 if(!soplex->setIntParam(soplex->RATIOTESTER, option[2] - '0'))
823 {
824 printUsage(argv, optidx);
825 returnValue = 1;
826 goto TERMINATE_FREESTRINGS;
827 }
828
829 break;
830
831 case 'v' :
832
833 // -v<level> : set verbosity to <level> (0 - error, 3 - normal, 5 - high)
834 if(!soplex->setIntParam(soplex->VERBOSITY, option[2] - '0'))
835 {
836 printUsage(argv, optidx);
837 returnValue = 1;
838 goto TERMINATE_FREESTRINGS;
839 }
840
841 break;
842
843 case 'x' :
844 // -x : print primal solution
845 printPrimal = true;
846 break;
847
848 case 'X' :
849 // -X : print primal solution with rationals
850 printPrimalRational = true;
851 break;
852
853 case 'y' :
854 // -y : print dual multipliers
855 printDual = true;
856 break;
857
858 case 'Y' :
859 // -Y : print dual multipliers with rationals
860 printDualRational = true;
861 break;
862
863 case 'q' :
864 // -q : display detailed statistics
865 displayStatistics = true;
866 break;
867
868 case 'c' :
869 // -c : perform final check of optimal solution in original problem
870 checkSol = true;
871 break;
872
873 case 'h' :
874
875 // -h : display all parameters
876 if(!soplex->saveSettingsFile(0, false))
877 {
878 MSG_ERROR(std::cerr << "Error printing parameters\n");
879 }
880
881 break;
882
883 //lint -fallthrough
884 default :
885 {
886 printUsage(argv, optidx);
887 returnValue = 1;
888 goto TERMINATE_FREESTRINGS;
889 }
890 }
891 }
892
893 MSG_INFO1(soplex->spxout, soplex->printUserSettings();)
894
895 // no LP file was given and no settings files are written
896 if(lpfilename == nullptr && savesetname == nullptr && diffsetname == nullptr)
897 {
898 printUsage(argv, 0);
899 returnValue = 1;
900 goto TERMINATE_FREESTRINGS;
901 }
902
903 // ensure that syncmode is not manual
904 if(soplex->intParam(soplex->SYNCMODE) == soplex->SYNCMODE_MANUAL)
905 {
906 MSG_ERROR(std::cerr <<
907 "Error: manual synchronization is invalid on command line. Change parameter int:syncmode.\n");
908 returnValue = 1;
909 goto TERMINATE_FREESTRINGS;
910 }
911
912 // save settings files
913 if(savesetname != nullptr)
914 {
915 MSG_INFO1(soplex->spxout, soplex->spxout << "Saving parameters to settings file <" << savesetname <<
916 "> . . .\n");
917
918 if(!soplex->saveSettingsFile(savesetname, false))
919 {
920 MSG_ERROR(std::cerr << "Error writing parameters to file <" << savesetname << ">\n");
921 }
922 }
923
924 if(diffsetname != nullptr)
925 {
926 MSG_INFO1(soplex->spxout, soplex->spxout << "Saving modified parameters to settings file <" <<
927 diffsetname << "> . . .\n");
928
929 if(!soplex->saveSettingsFile(diffsetname, true))
930 {
931 MSG_ERROR(std::cerr << "Error writing modified parameters to file <" << diffsetname << ">\n");
932 }
933 }
934
935 // no LP file given: exit after saving settings
936 if(lpfilename == nullptr)
937 {
938 if(loadsetname != nullptr || savesetname != nullptr || diffsetname != nullptr)
939 {
940 MSG_INFO1(soplex->spxout, soplex->spxout << "\n");
941 }
942
943 goto TERMINATE_FREESTRINGS;
944 }
945
946 // measure time for reading LP file and basis file
947 readingTime->start();
948
949 // if the LP is parsed rationally and might be solved rationally, we choose automatic syncmode such that
950 // the rational LP is kept after reading
951 if(soplex->intParam(soplex->READMODE) == soplex->READMODE_RATIONAL
952 && soplex->intParam(soplex->SOLVEMODE) != soplex->SOLVEMODE_REAL)
953 {
954 soplex->setIntParam(soplex->SYNCMODE, soplex->SYNCMODE_AUTO);
955 }
956
957 // read LP from input file
958 MSG_INFO1(soplex->spxout, soplex->spxout << "Reading "
959 << (soplex->intParam(soplex->READMODE) == soplex->READMODE_REAL ? "(real)" : "(rational)")
960 << " LP file <" << lpfilename << "> . . .\n");
961
962 if(!soplex->readFile(lpfilename, &rownames, &colnames))
963 {
964 MSG_ERROR(std::cerr << "Error while reading file <" << lpfilename << ">.\n");
965 returnValue = 1;
966 goto TERMINATE_FREESTRINGS;
967 }
968
969 // write LP if specified
970 if(writefilename != nullptr)
971 {
972 if(!soplex->writeFile(writefilename, &rownames, &colnames))
973 {
974 MSG_ERROR(std::cerr << "Error while writing file <" << writefilename << ">.\n\n");
975 returnValue = 1;
976 goto TERMINATE_FREESTRINGS;
977 }
978 else
979 {
980 MSG_INFO1(soplex->spxout, soplex->spxout << "Written LP to file <" << writefilename << ">.\n\n");
981 }
982 }
983
984 // write dual LP if specified
985 if(writedualfilename != nullptr)
986 {
987 if(!soplex->writeDualFileReal(writedualfilename, &rownames, &colnames))
988 {
989 MSG_ERROR(std::cerr << "Error while writing dual file <" << writedualfilename << ">.\n\n");
990 returnValue = 1;
991 goto TERMINATE_FREESTRINGS;
992 }
993 else
994 {
995 MSG_INFO1(soplex->spxout, soplex->spxout << "Written dual LP to file <" << writedualfilename <<
996 ">.\n\n");
997 }
998 }
999
1000 // read basis file if specified
1001 if(readbasname != nullptr)
1002 {
1003 MSG_INFO1(soplex->spxout, soplex->spxout << "Reading basis file <" << readbasname << "> . . . ");
1004
1005 if(!soplex->readBasisFile(readbasname, &rownames, &colnames))
1006 {
1007 MSG_ERROR(std::cerr << "Error while reading file <" << readbasname << ">.\n");
1008 returnValue = 1;
1009 goto TERMINATE_FREESTRINGS;
1010 }
1011 }
1012
1013 readingTime->stop();
1014
1015 MSG_INFO1(soplex->spxout,
1016 std::streamsize prec = soplex->spxout.precision();
1017 soplex->spxout << "Reading took "
1018 << std::fixed << std::setprecision(2) << readingTime->time()
1019 << std::scientific << std::setprecision(int(prec))
1020 << " seconds.\n\n");
1021
1022 MSG_INFO1(soplex->spxout, soplex->spxout << "LP has " << soplex->numRows() << " rows "
1023 << soplex->numCols() << " columns and " << soplex->numNonzeros() << " nonzeros.\n\n");
1024
1025 // solve the LP
1026 soplex->optimize();
1027
1028 // print solution, check solution, and display statistics
1029 printPrimalSolution(*soplex, colnames, rownames, printPrimal, printPrimalRational);
1030 printDualSolution(*soplex, colnames, rownames, printDual, printDualRational);
1031
1032 if(checkSol)
1033 checkSolution<R>(*soplex); // The type needs to get fixed here
1034
1035 if(displayStatistics)
1036 {
1037 MSG_INFO1(soplex->spxout, soplex->spxout << "Statistics\n==========\n\n");
1038 soplex->printStatistics(soplex->spxout.getStream(SPxOut::INFO1));
1039 }
1040
1041 if(validation->validate)
1042 validation->validateSolveReal(*soplex);
1043
1044 // write basis file if specified
1045 if(writebasname != nullptr)
1046 {
1047 if(!soplex->hasBasis())
1048 {
1049 MSG_WARNING(soplex->spxout, soplex->spxout <<
1050 "No basis information available. Could not write file <" << writebasname << ">\n\n");
1051 }
1052 else if(!soplex->writeBasisFile(writebasname, &rownames, &colnames))
1053 {
1054 MSG_ERROR(std::cerr << "Error while writing file <" << writebasname << ">.\n\n");
1055 returnValue = 1;
1056 goto TERMINATE_FREESTRINGS;
1057 }
1058 else
1059 {
1060 MSG_INFO1(soplex->spxout, soplex->spxout << "Written basis information to file <" << writebasname <<
1061 ">.\n\n");
1062 }
1063 }
1064 }
1065 catch(const SPxException& x)
1066 {
1067 MSG_ERROR(std::cerr << "Exception caught: " << x.what() << "\n");
1068 returnValue = 1;
1069 goto TERMINATE_FREESTRINGS;
1070 }
1071
1072 TERMINATE_FREESTRINGS:
1073 freeStrings(readbasname, writebasname, loadsetname, savesetname, diffsetname);
1074
1075 TERMINATE:
1076
1077 // because EGlpNumClear() calls mpq_clear() for all mpq_t variables, we need to destroy all objects of class Rational
1078 // beforehand; hence all Rational objects and all data that uses Rational objects must be allocated dynamically via
1079 // spx_alloc() and freed here; disabling the list memory is crucial
1080 if(nullptr != soplex)
1081 {
1082 soplex->~SoPlexBase();
1083 spx_free(soplex);
1084 }
1085
1086 if(nullptr != validation)
1087 {
1088 validation->~Validation();
1089 spx_free(validation);
1090 }
1091
1092 if(nullptr != readingTime)
1093 {
1094 readingTime->~Timer();
1095 spx_free(readingTime);
1096 }
1097
1098 return returnValue;
1099 }
1100
1101 /// runs SoPlexBase command line
1102 int main(int argc, char* argv[])
1103 {
1104 int arithmetic = 0;
1105 int precision = 0;
1106 int optidx;
1107
1108 // find out which precision/solvemode soplex should be run in. the rest happens in runSoPlex
1109 // no options were given
1110 if(argc <= 1)
1111 {
1112 printUsage(argv, 0);
1113 return 1;
1114 }
1115
1116 // read arguments from command line
1117 for(optidx = 1; optidx < argc; optidx++)
1118 {
1119 char* option = argv[optidx];
1120
1121 // we reached <lpfile>
1122 if(option[0] != '-')
1123 continue;
1124
1125 // option string must start with '-', must contain at least two characters, and exactly two characters if and
1126 // only if it is -x, -y, -q, or -c
1127 if(option[0] != '-' || option[1] == '\0'
1128 || ((option[2] == '\0') != (option[1] == 'x' || option[1] == 'X' || option[1] == 'y'
1129 || option[1] == 'Y' || option[1] == 'q' || option[1] == 'c')))
1130 {
1131 printUsage(argv, optidx);
1132 return 1;
1133 }
1134
1135 switch(option[1])
1136 {
1137 case '-' :
1138 option = &option[2];
1139
1140 // --arithmetic=<value> : choose base arithmetic type (0 - double, 1 - quadprecision, 2 - higher multiprecision)
1141 // only need to do something here if multi or quad, the rest is handled in runSoPlex
1142 if(strncmp(option, "arithmetic=", 11) == 0)
1143 {
1144 if(option[11] == '1')
1145 {
1146 #ifndef SOPLEX_WITH_FLOAT128
1147 MSG_ERROR(std::cerr <<
1148 "Cannot set arithmetic type to quadprecision - Soplex compiled without quadprecision support\n";)
1149 printUsage(argv, 0);
1150 return 1;
1151 #else
1152 arithmetic = 1;
1153 #endif
1154 }
1155 else if(option[11] == '2')
1156 {
1157 #ifndef SOPLEX_WITH_BOOST
1158 MSG_ERROR(std::cerr <<
1159 "Cannot set arithmetic type to multiprecision - Soplex compiled without boost\n";)
1160 printUsage(argv, 0);
1161 return 1;
1162 #else
1163 arithmetic = 2;
1164
1165 // default precision in multiprecision solve is 50
1166 if(precision == 0)
1167 precision = 50;
1168
1169 #endif
1170 }
1171 }
1172 // set precision
1173 else if(strncmp(option, "precision=", 10) == 0)
1174 {
1175 precision = atoi(option + 10);
1176 #ifndef SOPLEX_WITH_BOOST
1177 MSG_ERROR(std::cerr << "Setting precision to non-default value without Boost has no effect\n";)
1178 #endif
1179 }
1180
1181 break;
1182
1183 default:
1184 break;
1185 }
1186 }
1187
1188 if(precision != 0 && arithmetic != 2)
1189 {
1190 MSG_ERROR(std::cerr <<
1191 "Setting precision to non-default value without enabling multiprecision solve has no effect\n";)
1192 }
1193
(2) Event const: |
When switching on "arithmetic", the value of "arithmetic" must be equal to 0. |
(3) Event dead_error_condition: |
The "switch" governing value "arithmetic" cannot reach the "default" case. |
Also see events: |
[assignment][dead_error_begin] |
1194 switch(arithmetic)
1195 {
1196 case 0: // double
1197 runSoPlex<Real>(argc, argv);
1198 break;
1199
1200 #ifdef SOPLEX_WITH_BOOST
1201 #ifdef SOPLEX_WITH_FLOAT128
1202
1203 case 1: // quadprecision
1204 #if BOOST_VERSION < 107000
1205 std::cerr << "Error: Boost version too old." << std:: endl <<
1206 "In order to use the quadprecision feature of SoPlex," <<
1207 " Boost Version 1.70.0 or higher is required." << std::endl << \
1208 "Included Boost version is " << BOOST_VERSION / 100000 << "." // maj. version
1209 << BOOST_VERSION / 100 % 1000 << "." // min. version
1210 << BOOST_VERSION % 100 // patch version;
1211 << std::endl;
1212 #else
1213 using namespace boost::multiprecision;
1214 using Quad = boost::multiprecision::float128;
1215 runSoPlex<Quad>(argc, argv);
1216 #endif
1217 break;
1218 #endif
1219
1220 case 2: // soplex mpf
1221 using namespace boost::multiprecision;
1222
1223 #if BOOST_VERSION < 107000
1224 std::cerr << "Error: Boost version too old." << std:: endl <<
1225 "In order to use the multiprecision feature of SoPlex," <<
1226 " Boost Version 1.70.0 or higher is required." << std::endl << \
1227 "Included Boost version is " << BOOST_VERSION / 100000 << "." // maj. version
1228 << BOOST_VERSION / 100 % 1000 << "." // min. version
1229 << BOOST_VERSION % 100 // patch version;
1230 << std::endl;
1231 #else
1232 #ifdef SOPLEX_WITH_MPFR
1233
1234 // et_off means the expression templates options is turned off. TODO:
1235 // The documentation also mentions about static vs dynamic memory
1236 // allocation for the mpfr types. Is it relevant here? I probably also
1237 // need to have the mpfr_float_eto in the global soplex namespace
1238 using multiprecision = number<mpfr_float_backend<0>, et_off>;
1239 multiprecision::default_precision(precision);
1240 runSoPlex<multiprecision>(argc, argv);
1241 #endif // SOPLEX_WITH_MPFR
1242
1243 #ifdef SOPLEX_WITH_CPPMPF
1244 // It seems that precision cannot be set on run time for cpp_float
1245 // backend for boost::number. So a precision of 50 decimal points is
1246 // set.
1247 using multiprecision1 = number<cpp_dec_float<50>, et_off>;
1248 using multiprecision2 = number<cpp_dec_float<100>, et_off>;
1249 using multiprecision3 = number<cpp_dec_float<200>, et_off>;
1250
1251 if(precision <= 50)
1252 runSoPlex<multiprecision1>(argc, argv);
1253 else if(precision <= 100)
1254 runSoPlex<multiprecision2>(argc, argv);
1255 else
1256 runSoPlex<multiprecision3>(argc, argv);
1257
1258 #endif // SOPLEX_WITH_CPPMPF
1259 #endif
1260 break;
1261 #endif
1262
1263 default:
1264 std::cerr << "Wrong value for the arithmetic mode\n";
1265 return 0;
1266 }
1267 }
1268