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