1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the class library */
4 /* SoPlex --- the Sequential object-oriented simPlex. */
5 /* */
6 /* Copyright (C) 1996-2022 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 spxlpbase_rational.hpp
17 * @brief Saving LPs with Rational values in a form suitable for SoPlex.
18 */
19
20 #include <assert.h>
21 #include <stdio.h>
22 #include <ctype.h>
23 #include <iostream>
24
25 #include "soplex/spxdefines.h"
(1) Event include_recursion: |
#include file "/sapmnt/home1/d029903/my_work/SCIPSoPlex_coverity/scipoptsuite-8.0.1/soplex/src/soplex/spxlpbase.h" includes itself: spxlpbase.h -> spxlpbase_rational.hpp -> spxlpbase.h |
(2) Event caretline: |
^ |
26 #include "soplex/spxlpbase.h"
27 #include "soplex/spxout.h"
28 #include "soplex/mpsinput.h"
29 #include "soplex/exceptions.h"
30 #include "soplex/rational.h"
31
32 #define MAX_LINE_WRITE_LEN 65536 ///< maximum length allowed for writing lines
33
34 namespace soplex
35 {
36 template<> inline
37 void SPxLPBase<Rational>::computePrimalActivity(const VectorBase<Rational>& primal,
38 VectorBase<Rational>& activity, const bool unscaled) const
39 {
40 if(primal.dim() != nCols())
41 {
42 throw SPxInternalCodeException("XSPXLP01 Primal vector for computing row activity has wrong dimension");
43 }
44
45 if(activity.dim() != nRows())
46 {
47 throw SPxInternalCodeException("XSPXLP03 Activity vector computing row activity has wrong dimension");
48 }
49
50 int c;
51
52 for(c = 0; c < nCols() && primal[c] == 0; c++)
53 ;
54
55 if(c >= nCols())
56 {
57 activity.clear();
58 return;
59 }
60
61 activity = colVector(c);
62
63 activity *= primal[c];
64 c++;
65
66 for(; c < nCols(); c++)
67 {
68 if(primal[c] != 0)
69 {
70 activity.multAdd(primal[c], colVector(c));
71 }
72 }
73 }
74
75 template<> inline
76 void SPxLPBase<Rational>::computeDualActivity(const VectorBase<Rational>& dual,
77 VectorBase<Rational>& activity, const bool unscaled) const
78 {
79 if(dual.dim() != nRows())
80 {
81 throw SPxInternalCodeException("XSPXLP02 Dual vector for computing dual activity has wrong dimension");
82 }
83
84 if(activity.dim() != nCols())
85 {
86 throw SPxInternalCodeException("XSPXLP04 Activity vector computing dual activity has wrong dimension");
87 }
88
89 int r;
90
91 for(r = 0; r < nRows() && dual[r] == 0; r++)
92 ;
93
94 if(r >= nRows())
95 {
96 activity.clear();
97 return;
98 }
99
100 activity = rowVector(r);
101
102 activity *= dual[r];
103 r++;
104
105 for(; r < nRows(); r++)
106 {
107 if(dual[r] != 0)
108 {
109 activity.multAdd(dual[r], rowVector(r));
110 }
111 }
112 }
113
114 template<> inline
115 Rational SPxLPBase<Rational>::maxAbsNzo(bool /* unscaled */) const
116 {
117 Rational maxi = Rational(0);
118
119 for(int i = 0; i < nCols(); ++i)
120 {
121 Rational m = colVector(i).maxAbs();
122
123 if(m > maxi)
124 maxi = m;
125 }
126
127 assert(maxi >= Rational(0));
128
129 return maxi;
130 }
131
132 template<> inline
133 Rational SPxLPBase<Rational>::minAbsNzo(bool /* unscaled */) const
134 {
135 Rational mini = infinity;
136
137 for(int i = 0; i < nCols(); ++i)
138 {
139 Rational m = colVector(i).minAbs();
140
141 if(m < mini)
142 mini = m;
143 }
144
145 assert(mini >= Rational(0));
146
147 return mini;
148 }
149
150 // ---------------------------------------------------------------------------------------------------------------------
151 // Specialization for reading LP format
152 // ---------------------------------------------------------------------------------------------------------------------
153
154 #define LPF_MAX_LINE_LEN 8192 ///< maximum length of a line (8190 + \\n + \\0)
155
156 /// Read the next number and advance \p pos.
157 /** If only a sign is encountered, the number is assumed to be \c sign * 1. This routine will not catch malformatted
158 * numbers like .e10 !
159 */
160 static Rational LPFreadValue(char*& pos, SPxOut* spxout, const int lineno = -1)
161 {
162 assert(LPFisValue(pos));
163
164 char tmp[LPF_MAX_LINE_LEN];
165 const char* s = pos;
166 char* t;
167 Rational value = 1;
168 bool has_digits = false;
169 bool has_emptyexponent = false;
170 bool has_dot = false;
171 bool has_exponent = false;
172 bool has_emptydivisor = false;
173
174 // 1. sign
175 if((*s == '+') || (*s == '-'))
176 s++;
177
178 // 2. Digits before the decimal dot
179 while((*s >= '0') && (*s <= '9'))
180 {
181 has_digits = true;
182 s++;
183 }
184
185 // 3. Decimal dot
186 if(*s == '.')
187 {
188 has_dot = true;
189 s++;
190
191 // 4. If there was a dot, possible digit behind it
192 while((*s >= '0') && (*s <= '9'))
193 {
194 has_digits = true;
195 s++;
196 }
197 }
198
199 // 5. Exponent
200 if(tolower(*s) == 'e')
201 {
202 has_exponent = true;
203 has_emptyexponent = true;
204 s++;
205
206 // 6. Exponent sign
207 if((*s == '+') || (*s == '-'))
208 s++;
209
210 // 7. Exponent digits
211 while((*s >= '0') && (*s <= '9'))
212 {
213 has_emptyexponent = false;
214 s++;
215 }
216 }
217
218 // 8. Division
219 if(*s == '/')
220 {
221 s++;
222 has_emptydivisor = true;
223
224 while((*s >= '0') && (*s <= '9'))
225 {
226 has_emptydivisor = false;
227 s++;
228 }
229
230 if(has_dot || has_exponent || has_emptydivisor ||
231 (*s == '.') || (*s == '+') || (*s == '-') || (tolower(*s) == 'e'))
232 {
233 MSG_WARNING((*spxout), (*spxout) << "WLPFRD03 Warning: In line " << lineno <<
234 ": malformed rational value in LP file\n";)
235 }
236 }
237
238
239 assert(s != pos);
240
241 if(has_emptyexponent)
242 {
243 MSG_WARNING((*spxout), (*spxout) << "WLPFRD01 Warning: In line " << lineno <<
244 ": found empty exponent in LP file - check for forbidden variable names with initial 'e' or 'E'\n");
245 }
246
247 if(!has_digits)
248 value = (*pos == '-') ? -1 : 1;
249 else
250 {
251 for(t = tmp; pos != s; pos++)
252 *t++ = *pos;
253
254 *t = '\0';
255
256 try
257 {
258 value = ratFromString(tmp);
259 }
260 catch(const std::exception& e)
261 {
262 MSG_WARNING((*spxout), (*spxout) << "WLPFRD04 Warning: In line " << lineno <<
263 ": malformed rational value in LP file\n");
264 std::cerr << e.what() << '\n';
265 }
266 }
267
268 pos += s - pos;
269
270 assert(pos == s);
271
272 MSG_DEBUG(std::cout << "DLPFRD01 LPFreadValue = " << value << std::endl;)
273
274 if(LPFisSpace(*pos))
275 pos++;
276
277 return value;
278 }
279
280
281
282 /// Read the next column name from the input.
283 /** The name read is looked up and if not found \p emptycol
284 * is added to \p colset. \p pos is advanced behind the name.
285 * @return The Index of the named column.
286 */
287 static int LPFreadColName(char*& pos, NameSet* colnames, LPColSetBase<Rational>& colset,
288 const LPColBase<Rational>* emptycol, SPxOut* spxout)
289 {
290 assert(LPFisColName(pos));
291 assert(colnames != 0);
292
293 char name[LPF_MAX_LINE_LEN];
294 const char* s = pos;
295 int i;
296 int colidx;
297
298 // These are the characters that are not allowed in a column name.
299 while((strchr("+-.<>= ", *s) == 0) && (*s != '\0'))
300 s++;
301
302 for(i = 0; pos != s; i++, pos++)
303 name[i] = *pos;
304
305 name[i] = '\0';
306
307 if((colidx = colnames->number(name)) < 0)
308 {
309 // We only add the name if we got an empty column.
310 if(emptycol == 0)
311 MSG_WARNING((*spxout), (*spxout) << "WLPFRD02 Unknown variable \"" << name << "\" ";)
312 else
313 {
314 colidx = colnames->num();
315 colnames->add(name);
316 colset.add(*emptycol);
317 }
318 }
319
320 MSG_DEBUG(std::cout << "DLPFRD03 LPFreadColName [" << name << "] = " << colidx << std::endl;)
321
322 if(LPFisSpace(*pos))
323 pos++;
324
325 return colidx;
326 }
327
328 static Rational LPFreadInfinity(char*& pos)
329 {
330 assert(LPFisInfinity(pos));
331
332 Rational sense = (*pos == '-') ? -1 : 1;
333
334 (void) LPFhasKeyword(++pos, "inf[inity]");
335
336 sense *= Rational(infinity);
337 return sense;
338 }
339
340 /// Read LP in "CPLEX LP File Format".
341 /** The specification is taken from the ILOG CPLEX 7.0 Reference Manual, Appendix E, Page 527.
342 *
343 * This routine should read (most?) valid LP format files. What it will not do, is find all cases where a file is ill
344 * formed. If this happens it may complain and read nothing or read "something".
345 *
346 * Problem: A line ending in '+' or '-' followed by a line starting with a number, will be regarded as an error.
347 *
348 * The reader will accept the keyword INT[egers] as a synonym for GEN[erals] which is an undocumented feature in CPLEX.
349 *
350 * A difference to the CPLEX reader, is that no name for the objective row is required.
351 *
352 * The manual says the maximum allowed line length is 255 characters, but CPLEX does not complain if the lines are
353 * longer.
354 *
355 * @return true if the file was read correctly
356 */
357 template <> inline
358 bool SPxLPBase<Rational>::readLPF(
359 std::istream& p_input, ///< input stream.
360 NameSet* p_rnames, ///< row names.
361 NameSet* p_cnames, ///< column names.
362 DIdxSet* p_intvars) ///< integer variables.
363 {
364 enum
365 {
366 START, OBJECTIVE, CONSTRAINTS, BOUNDS, INTEGERS, BINARIES
367 } section = START;
368
369 NameSet* rnames; ///< row names.
370 NameSet* cnames; ///< column names.
371
372 LPColSetBase<Rational> cset; ///< the set of columns read.
373 LPRowSetBase<Rational> rset; ///< the set of rows read.
374 LPColBase<Rational> emptycol; ///< reusable empty column.
375 LPRowBase<Rational> row; ///< last assembled row.
376 DSVectorBase<Rational> vec; ///< last assembled vector (from row).
377
378 Rational val = 1;
379 int colidx;
380 int sense = 0;
381
382 char buf[LPF_MAX_LINE_LEN];
383 char tmp[LPF_MAX_LINE_LEN];
384 char line[LPF_MAX_LINE_LEN];
385 int lineno = 0;
386 bool unnamed = true;
387 bool finished = false;
388 bool other;
389 bool have_value = true;
390 int i;
391 int k;
392 char* s;
393 char* pos;
394 char* pos_old = 0;
395
396 if(p_cnames)
397 cnames = p_cnames;
398 else
399 {
400 cnames = 0;
401 spx_alloc(cnames);
402 cnames = new(cnames) NameSet();
403 }
404
405 cnames->clear();
406
407 if(p_rnames)
408 rnames = p_rnames;
409 else
410 {
411 try
412 {
413 rnames = 0;
414 spx_alloc(rnames);
415 rnames = new(rnames) NameSet();
416 }
417 catch(const SPxMemoryException& x)
418 {
419 if(!p_cnames)
420 {
421 cnames->~NameSet();
422 spx_free(cnames);
423 }
424
425 throw x;
426 }
427 }
428
429 rnames->clear();
430
431 SPxLPBase<Rational>::clear(); // clear the LP.
432
433 //--------------------------------------------------------------------------
434 //--- Main Loop
435 //--------------------------------------------------------------------------
436 for(;;)
437 {
438 // 0. Read a line from the file.
439 if(!p_input.getline(buf, sizeof(buf)))
440 {
441 if(strlen(buf) == LPF_MAX_LINE_LEN - 1)
442 {
443 MSG_ERROR(std::cerr << "ELPFRD06 Line exceeds " << LPF_MAX_LINE_LEN - 2
444 << " characters" << std::endl;)
445 }
446 else
447 {
448 MSG_ERROR(std::cerr << "ELPFRD07 No 'End' marker found" << std::endl;)
449 finished = true;
450 }
451
452 break;
453 }
454
455 lineno++;
456 i = 0;
457 pos = buf;
458
459 MSG_DEBUG(std::cout << "DLPFRD08 Reading line " << lineno
460 << " (pos=" << pos << ")" << std::endl;)
461
462 // 1. Remove comments.
463 if(0 != (s = strchr(buf, '\\')))
464 * s = '\0';
465
466 // 2. Look for keywords.
467 if(section == START)
468 {
469 if(LPFhasKeyword(pos, "max[imize]"))
470 {
471 changeSense(SPxLPBase<Rational>::MAXIMIZE);
472 section = OBJECTIVE;
473 }
474 else if(LPFhasKeyword(pos, "min[imize]"))
475 {
476 changeSense(SPxLPBase<Rational>::MINIMIZE);
477 section = OBJECTIVE;
478 }
479 }
480 else if(section == OBJECTIVE)
481 {
482 if(LPFhasKeyword(pos, "s[ubject][ ]t[o]")
483 || LPFhasKeyword(pos, "s[uch][ ]t[hat]")
484 || LPFhasKeyword(pos, "s[.][ ]t[.]")
485 || LPFhasKeyword(pos, "lazy con[straints]"))
486 {
487 // store objective vector
488 for(int j = vec.size() - 1; j >= 0; --j)
489 cset.maxObj_w(vec.index(j)) = vec.value(j);
490
491 // multiplication with -1 for minimization is done below
492 vec.clear();
493 have_value = true;
494 val = 1;
495 section = CONSTRAINTS;
496 }
497 }
498 else if(section == CONSTRAINTS &&
499 (LPFhasKeyword(pos, "s[ubject][ ]t[o]")
500 || LPFhasKeyword(pos, "s[uch][ ]t[hat]")
501 || LPFhasKeyword(pos, "s[.][ ]t[.]")))
502 {
503 have_value = true;
504 val = 1;
505 }
506 else
507 {
508 if(LPFhasKeyword(pos, "lazy con[straints]"))
509 ;
510 else if(LPFhasKeyword(pos, "bound[s]"))
511 section = BOUNDS;
512 else if(LPFhasKeyword(pos, "bin[ary]"))
513 section = BINARIES;
514 else if(LPFhasKeyword(pos, "bin[aries]"))
515 section = BINARIES;
516 else if(LPFhasKeyword(pos, "gen[erals]"))
517 section = INTEGERS;
518 else if(LPFhasKeyword(pos, "int[egers]")) // this is undocumented
519 section = INTEGERS;
520 else if(LPFhasKeyword(pos, "end"))
521 {
522 finished = true;
523 break;
524 }
525 else if(LPFhasKeyword(pos, "s[ubject][ ]t[o]") // second time
526 || LPFhasKeyword(pos, "s[uch][ ]t[hat]")
527 || LPFhasKeyword(pos, "s[.][ ]t[.]")
528 || LPFhasKeyword(pos, "lazy con[straints]"))
529 {
530 // In principle this has to checked for all keywords above,
531 // otherwise we just ignore any half finished constraint
532 if(have_value)
533 goto syntax_error;
534
535 have_value = true;
536 val = 1;
537 }
538 }
539
540 // 3a. Look for row names in objective and drop it.
541 if(section == OBJECTIVE)
542 LPFhasRowName(pos, 0);
543
544 // 3b. Look for row name in constraint and store it.
545 if(section == CONSTRAINTS)
546 if(LPFhasRowName(pos, rnames))
547 unnamed = false;
548
549 // 4a. Remove initial spaces.
550 while(LPFisSpace(pos[i]))
551 i++;
552
553 // 4b. remove spaces if they do not appear before the name of a vaiable.
554 for(k = 0; pos[i] != '\0'; i++)
555 if(!LPFisSpace(pos[i]) || LPFisColName(&pos[i + 1]))
556 tmp[k++] = pos[i];
557
558 tmp[k] = '\0';
559
560 // 5. Is this an empty line ?
561 if(tmp[0] == '\0')
562 continue;
563
564 // 6. Collapse sequences of '+' and '-'. e.g ++---+ => -
565 for(i = 0, k = 0; tmp[i] != '\0'; i++)
566 {
567 while(((tmp[i] == '+') || (tmp[i] == '-')) && ((tmp[i + 1] == '+') || (tmp[i + 1] == '-')))
568 {
569 if(tmp[i++] == '-')
570 tmp[i] = (tmp[i] == '-') ? '+' : '-';
571 }
572
573 line[k++] = tmp[i];
574 }
575
576 line[k] = '\0';
577
578 //-----------------------------------------------------------------------
579 //--- Line processing loop
580 //-----------------------------------------------------------------------
581 pos = line;
582
583 MSG_DEBUG(std::cout << "DLPFRD09 pos=" << pos << std::endl;)
584
585 // 7. We have something left to process.
586 while((pos != 0) && (*pos != '\0'))
587 {
588 // remember our position, so we are sure we make progress.
589 pos_old = pos;
590
591 // now process the sections
592 switch(section)
593 {
594 case OBJECTIVE:
595 if(LPFisValue(pos))
596 {
597 Rational pre_sign = 1;
598
599 /* Already having here a value could only result from being the first number in a constraint, or a sign
600 * '+' or '-' as last token on the previous line.
601 */
602 if(have_value)
603 {
604 if(spxAbs(val) != 1)
605 goto syntax_error;
606
607 if(val == -1)
608 pre_sign = val;
609 }
610
611 have_value = true;
612 val = LPFreadValue(pos, spxout, lineno);
613 val *= pre_sign;
614 }
615
616 if(*pos == '\0')
617 continue;
618
619 if(!have_value || !LPFisColName(pos))
620 goto syntax_error;
621
622 have_value = false;
623 colidx = LPFreadColName(pos, cnames, cset, &emptycol, spxout);
624 vec.add(colidx, val);
625 break;
626
627 case CONSTRAINTS:
628 if(LPFisValue(pos))
629 {
630 Rational pre_sign = 1;
631
632 /* Already having here a value could only result from being the first number in a constraint, or a sign
633 * '+' or '-' as last token on the previous line.
634 */
635 if(have_value)
636 {
637 if(spxAbs(val) != 1)
638 goto syntax_error;
639
640 if(val == -1)
641 pre_sign = val;
642 }
643
644 have_value = true;
645 val = LPFreadValue(pos, spxout, lineno);
646 val *= pre_sign;
647
648 if(sense != 0)
649 {
650 if(sense == '<')
651 {
652 row.setLhs(-infinity);
653 row.setRhs(val);
654 }
655 else if(sense == '>')
656 {
657 row.setLhs(val);
658 row.setRhs(infinity);
659 }
660 else
661 {
662 assert(sense == '=');
663
664 row.setLhs(val);
665 row.setRhs(val);
666 }
667
668 row.setRowVector(vec);
669 rset.add(row);
670 vec.clear();
671
672 if(!unnamed)
673 unnamed = true;
674 else
675 {
676 char name[16];
677 spxSnprintf(name, 16, "C%d", rset.num());
678 rnames->add(name);
679 }
680
681 have_value = true;
682 val = 1;
683 sense = 0;
684 pos = 0;
685 // next line
686 continue;
687 }
688 }
689
690 if(*pos == '\0')
691 continue;
692
693 if(have_value)
694 {
695 if(LPFisColName(pos))
696 {
697 colidx = LPFreadColName(pos, cnames, cset, &emptycol, spxout);
698
699 if(val != 0)
700 {
701 // Do we have this index already in the row?
702 int n = vec.pos(colidx);
703
704 // if not, add it
705 if(n < 0)
706 vec.add(colidx, val);
707 // if yes, add them up and remove the element if it amounts to zero
708 else
709 {
710 assert(vec.index(n) == colidx);
711
712 val += vec.value(n);
713
714 if(val == 0)
715 vec.remove(n);
716 else
717 vec.value(n) = val;
718
719 assert(cnames->has(colidx));
720
721 MSG_WARNING((*this->spxout), (*this->spxout) << "WLPFRD10 Duplicate index "
722 << (*cnames)[colidx]
723 << " in line " << lineno
724 << std::endl;)
725 }
726 }
727
728 have_value = false;
729 }
730 else
731 {
732 // We have a row like c1: <= 5 with no variables. We can not handle 10 <= 5; issue a syntax error.
733 if(val != 1)
734 goto syntax_error;
735
736 // If the next thing is not the sense we give up also.
737 if(!LPFisSense(pos))
738 goto syntax_error;
739
740 have_value = false;
741 }
742 }
743
744 assert(!have_value);
745
746 if(LPFisSense(pos))
747 sense = LPFreadSense(pos);
748
749 break;
750
751 case BOUNDS:
752 other = false;
753 sense = 0;
754
755 if(LPFisValue(pos))
756 {
757 val = LPFisInfinity(pos) ? LPFreadInfinity(pos) : LPFreadValue(pos, spxout, lineno);
758
759 if(!LPFisSense(pos))
760 goto syntax_error;
761
762 sense = LPFreadSense(pos);
763 other = true;
764 }
765
766 if(!LPFisColName(pos))
767 goto syntax_error;
768
769 if((colidx = LPFreadColName(pos, cnames, cset, 0, spxout)) < 0)
770 {
771 MSG_WARNING((*this->spxout), (*this->spxout) << "WLPFRD11 in Bounds section line "
772 << lineno << " ignored" << std::endl;)
773 *pos = '\0';
774 continue;
775 }
776
777 if(sense)
778 {
779 if(sense == '<')
780 cset.lower_w(colidx) = val;
781 else if(sense == '>')
782 cset.upper_w(colidx) = val;
783 else
784 {
785 assert(sense == '=');
786 cset.lower_w(colidx) = val;
787 cset.upper_w(colidx) = val;
788 }
789 }
790
791 if(LPFisFree(pos))
792 {
793 cset.lower_w(colidx) = -infinity;
794 cset.upper_w(colidx) = infinity;
795 other = true;
796 pos += 4; // set position after the word "free"
797 }
798 else if(LPFisSense(pos))
799 {
800 sense = LPFreadSense(pos);
801 other = true;
802
803 if(!LPFisValue(pos))
804 goto syntax_error;
805
806 val = LPFisInfinity(pos) ? LPFreadInfinity(pos) : LPFreadValue(pos, spxout, lineno);
807
808 if(sense == '<')
809 cset.upper_w(colidx) = val;
810 else if(sense == '>')
811 cset.lower_w(colidx) = val;
812 else
813 {
814 assert(sense == '=');
815 cset.lower_w(colidx) = val;
816 cset.upper_w(colidx) = val;
817 }
818 }
819
820 /* Do we have only a single column name in the input line? We could ignore this savely, but it is probably
821 * a sign of some other error.
822 */
823 if(!other)
824 goto syntax_error;
825
826 break;
827
828 case BINARIES:
829 case INTEGERS:
830 if((colidx = LPFreadColName(pos, cnames, cset, 0, spxout)) < 0)
831 {
832 MSG_WARNING((*this->spxout), (*this->spxout) << "WLPFRD12 in Binary/General section line " << lineno
833 << " ignored" << std::endl;)
834 }
835 else
836 {
837 if(section == BINARIES)
838 {
839 if(cset.lower(colidx) < 0)
840 {
841 cset.lower_w(colidx) = 0;
842 }
843
844 if(cset.upper(colidx) > 1)
845 {
846 cset.upper_w(colidx) = 1;
847 }
848 }
849
850 if(p_intvars != 0)
851 p_intvars->addIdx(colidx);
852 }
853
854 break;
855
856 case START:
857 MSG_ERROR(std::cerr << "ELPFRD13 This seems to be no LP format file" << std::endl;)
858 goto syntax_error;
859
860 default:
861 throw SPxInternalCodeException("XLPFRD01 This should never happen.");
862 }
863
864 if(pos == pos_old)
865 goto syntax_error;
866 }
867 }
868
869 assert(isConsistent());
870
871 addCols(cset);
872 assert(isConsistent());
873
874 addRows(rset);
875 assert(isConsistent());
876
877 syntax_error:
878
879 if(finished)
880 {
881 MSG_INFO2((*this->spxout), (*this->spxout) << "Finished reading " << lineno << " lines" <<
882 std::endl;)
883 }
884 else
885 MSG_ERROR(std::cerr << "ELPFRD15 Syntax error in line " << lineno << std::endl;)
886
887 if(p_cnames == 0)
888 spx_free(cnames);
889
890 if(p_rnames == 0)
891 spx_free(rnames);
892
893 return finished;
894 }
895
896 /// Process ROWS section.
897 static void MPSreadRows(MPSInput& mps, LPRowSetBase<Rational>& rset, NameSet& rnames,
898 SPxOut* spxout)
899 {
900 LPRowBase<Rational> row;
901
902 while(mps.readLine())
903 {
904 if(mps.field0() != 0)
905 {
906 MSG_INFO2((*spxout), (*spxout) << "IMPSRD02 Objective name : " << mps.objName() << std::endl;)
907
908 if(strcmp(mps.field0(), "COLUMNS"))
909 break;
910
911 mps.setSection(MPSInput::COLUMNS);
912
913 return;
914 }
915
916 if(*mps.field1() == 'N')
917 {
918 if(*mps.objName() == '\0')
919 mps.setObjName(mps.field2());
920 }
921 else
922 {
923 if(rnames.has(mps.field2()))
924 break;
925
926 rnames.add(mps.field2());
927
928 switch(*mps.field1())
929 {
930 case 'G':
931 row.setLhs(0);
932 row.setRhs(infinity);
933 break;
934
935 case 'E':
936 row.setLhs(0);
937 row.setRhs(0);
938 break;
939
940 case 'L':
941 row.setLhs(-infinity);
942 row.setRhs(0);
943 break;
944
945 default:
946 mps.syntaxError();
947 return;
948 }
949
950 rset.add(row);
951 }
952
953 assert((*mps.field1() == 'N') || (rnames.number(mps.field2()) == rset.num() - 1));
954 }
955
956 mps.syntaxError();
957 }
958
959
960
961 /// Process COLUMNS section.
962 static void MPSreadCols(MPSInput& mps, const LPRowSetBase<Rational>& rset, const NameSet& rnames,
963 LPColSetBase<Rational>& cset, NameSet& cnames, DIdxSet* intvars, SPxOut* spxout)
964 {
965 Rational val;
966 int idx;
967 char colname[MPSInput::MAX_LINE_LEN] = { '\0' };
968 LPColBase<Rational> col(rset.num());
969 DSVectorBase<Rational> vec;
970
971 col.setObj(0);
972 vec.clear();
973
974 while(mps.readLine())
975 {
976 if(mps.field0() != 0)
977 {
978 if(strcmp(mps.field0(), "RHS"))
979 break;
980
981 if(colname[0] != '\0')
982 {
983 col.setColVector(vec);
984 cset.add(col);
985 }
986
987 mps.setSection(MPSInput::RHS);
988
989 return;
990 }
991
992 if((mps.field1() == 0) || (mps.field2() == 0) || (mps.field3() == 0))
993 break;
994
995 // new column?
996 if(strcmp(colname, mps.field1()))
997 {
998 // first column?
999 if(colname[0] != '\0')
1000 {
1001 col.setColVector(vec);
1002 cset.add(col);
1003 }
1004
1005 // save copy of string (make sure string ends with \0)
1006 spxSnprintf(colname, MPSInput::MAX_LINE_LEN - 1, "%s", mps.field1());
1007 colname[MPSInput::MAX_LINE_LEN - 1] = '\0';
1008
1009 int ncnames = cnames.size();
1010 cnames.add(colname);
1011
1012 // check whether the new name is unique wrt previous column names
1013 if(cnames.size() <= ncnames)
1014 {
1015 MSG_ERROR(std::cerr << "ERROR in COLUMNS: duplicate column name or not column-wise ordering" <<
1016 std::endl;)
1017 break;
1018 }
1019
1020 vec.clear();
1021 col.setObj(0);
1022 col.setLower(0);
1023 col.setUpper(infinity);
1024
1025 if(mps.isInteger())
1026 {
1027 assert(cnames.number(colname) == cset.num());
1028
1029 if(intvars != 0)
1030 intvars->addIdx(cnames.number(colname));
1031
1032 // for Integer variable the default bounds are 0/1
1033 col.setUpper(1);
1034 }
1035 }
1036
1037 try
1038 {
1039 val = ratFromString(mps.field3());
1040 }
1041 catch(const std::exception& e)
1042 {
1043 MSG_WARNING((*spxout), (*spxout) << "WMPSRD01 Warning: malformed rational value in MPS file\n");
1044 std::cerr << e.what() << '\n';
1045 }
1046
1047 if(!strcmp(mps.field2(), mps.objName()))
1048 col.setObj(val);
1049 else
1050 {
1051 if((idx = rnames.number(mps.field2())) < 0)
1052 mps.entryIgnored("Column", mps.field1(), "row", mps.field2());
1053 else if(val != 0)
1054 vec.add(idx, val);
1055 }
1056
1057 if(mps.field5() != 0)
1058 {
1059 assert(mps.field4() != 0);
1060
1061 try
1062 {
1063 val = ratFromString(mps.field5());
1064 }
1065 catch(const std::exception& e)
1066 {
1067 MSG_WARNING((*spxout), (*spxout) << "WMPSRD02 Warning: malformed rational value in MPS file\n");
1068 std::cerr << e.what() << '\n';
1069 }
1070
1071 if(!strcmp(mps.field4(), mps.objName()))
1072 col.setObj(val);
1073 else
1074 {
1075 if((idx = rnames.number(mps.field4())) < 0)
1076 mps.entryIgnored("Column", mps.field1(), "row", mps.field4());
1077 else if(val != 0)
1078 vec.add(idx, val);
1079 }
1080 }
1081 }
1082
1083 mps.syntaxError();
1084 }
1085
1086
1087
1088 /// Process RHS section.
1089 static void MPSreadRhs(MPSInput& mps, LPRowSetBase<Rational>& rset, const NameSet& rnames,
1090 SPxOut* spxout)
1091 {
1092 char rhsname[MPSInput::MAX_LINE_LEN] = { '\0' };
1093 char addname[MPSInput::MAX_LINE_LEN] = { '\0' };
1094 int idx;
1095 Rational val;
1096
1097 while(mps.readLine())
1098 {
1099 if(mps.field0() != 0)
1100 {
1101 MSG_INFO2((*spxout), (*spxout) << "IMPSRD03 RHS name : " << rhsname << std::endl;);
1102
1103 if(!strcmp(mps.field0(), "RANGES"))
1104 mps.setSection(MPSInput::RANGES);
1105 else if(!strcmp(mps.field0(), "BOUNDS"))
1106 mps.setSection(MPSInput::BOUNDS);
1107 else if(!strcmp(mps.field0(), "ENDATA"))
1108 mps.setSection(MPSInput::ENDATA);
1109 else
1110 break;
1111
1112 return;
1113 }
1114
1115 if(((mps.field2() != 0) && (mps.field3() == 0)) || ((mps.field4() != 0) && (mps.field5() == 0)))
1116 mps.insertName("_RHS_");
1117
1118 if((mps.field1() == 0) || (mps.field2() == 0) || (mps.field3() == 0))
1119 break;
1120
1121 if(*rhsname == '\0')
1122 spxSnprintf(rhsname, MPSInput::MAX_LINE_LEN, "%s", mps.field1());
1123
1124 if(strcmp(rhsname, mps.field1()))
1125 {
1126 if(strcmp(addname, mps.field1()))
1127 {
1128 assert(strlen(mps.field1()) < MPSInput::MAX_LINE_LEN);
1129 spxSnprintf(addname, MPSInput::MAX_LINE_LEN, "%s", mps.field1());
1130 MSG_INFO3((*spxout), (*spxout) << "IMPSRD07 RHS ignored : " << addname << std::endl);
1131 }
1132 }
1133 else
1134 {
1135 if((idx = rnames.number(mps.field2())) < 0)
1136 mps.entryIgnored("RHS", mps.field1(), "row", mps.field2());
1137 else
1138 {
1139 try
1140 {
1141 val = ratFromString(mps.field3());
1142 }
1143 catch(const std::exception& e)
1144 {
1145 MSG_WARNING((*spxout), (*spxout) << "WMPSRD03 Warning: malformed rational value in MPS file\n");
1146 std::cerr << e.what() << '\n';
1147 }
1148
1149 // LE or EQ
1150 if(double(rset.rhs(idx)) < double(infinity))
1151 rset.rhs_w(idx) = val;
1152
1153 // GE or EQ
1154 if(double(rset.lhs(idx)) > double(-infinity))
1155 rset.lhs_w(idx) = val;
1156 }
1157
1158 if(mps.field5() != 0)
1159 {
1160 if((idx = rnames.number(mps.field4())) < 0)
1161 mps.entryIgnored("RHS", mps.field1(), "row", mps.field4());
1162 else
1163 {
1164 try
1165 {
1166 val = ratFromString(mps.field5());
1167 }
1168 catch(const std::exception& e)
1169 {
1170 MSG_WARNING((*spxout), (*spxout) << "WMPSRD04 Warning: malformed rational value in MPS file\n");
1171 std::cerr << e.what() << '\n';
1172 }
1173
1174 // LE or EQ
1175 if(double(rset.rhs(idx)) < double(infinity))
1176 rset.rhs_w(idx) = val;
1177
1178 // GE or EQ
1179 if(double(rset.lhs(idx)) > double(-infinity))
1180 rset.lhs_w(idx) = val;
1181 }
1182 }
1183 }
1184 }
1185
1186 mps.syntaxError();
1187 }
1188
1189
1190
1191 /// Process RANGES section.
1192 static void MPSreadRanges(MPSInput& mps, LPRowSetBase<Rational>& rset, const NameSet& rnames,
1193 SPxOut* spxout)
1194 {
1195 char rngname[MPSInput::MAX_LINE_LEN] = { '\0' };
1196 int idx;
1197 Rational val;
1198
1199 while(mps.readLine())
1200 {
1201 if(mps.field0() != 0)
1202 {
1203 MSG_INFO2((*spxout), (*spxout) << "IMPSRD04 Range name : " << rngname << std::endl;);
1204
1205 if(!strcmp(mps.field0(), "BOUNDS"))
1206 mps.setSection(MPSInput::BOUNDS);
1207 else if(!strcmp(mps.field0(), "ENDATA"))
1208 mps.setSection(MPSInput::ENDATA);
1209 else
1210 break;
1211
1212 return;
1213 }
1214
1215 if(((mps.field2() != 0) && (mps.field3() == 0)) || ((mps.field4() != 0) && (mps.field5() == 0)))
1216 mps.insertName("_RNG_");
1217
1218 if((mps.field1() == 0) || (mps.field2() == 0) || (mps.field3() == 0))
1219 break;
1220
1221 if(*rngname == '\0')
1222 {
1223 assert(strlen(mps.field1()) < MPSInput::MAX_LINE_LEN);
1224 spxSnprintf(rngname, MPSInput::MAX_LINE_LEN, "%s", mps.field1());
1225 }
1226
1227 /* The rules are:
1228 * Row Sign LHS RHS
1229 * ----------------------------------------
1230 * G +/- rhs rhs + |range|
1231 * L +/- rhs - |range| rhs
1232 * E + rhs rhs + range
1233 * E - rhs + range rhs
1234 * ----------------------------------------
1235 */
1236 if(!strcmp(rngname, mps.field1()))
1237 {
1238 if((idx = rnames.number(mps.field2())) < 0)
1239 mps.entryIgnored("Range", mps.field1(), "row", mps.field2());
1240 else
1241 {
1242 try
1243 {
1244 val = ratFromString(mps.field3());
1245 }
1246 catch(const std::exception& e)
1247 {
1248 MSG_WARNING((*spxout), (*spxout) << "WMPSRD05 Warning: malformed rational value in MPS file\n");
1249 std::cerr << e.what() << '\n';
1250 }
1251
1252 // EQ
1253 if((double(rset.lhs(idx)) > -double(infinity)) && (double(rset.rhs_w(idx)) < double(infinity)))
1254 {
1255 assert(rset.lhs(idx) == rset.rhs(idx));
1256
1257 if(double(val) >= 0)
1258 rset.rhs_w(idx) += val;
1259 else
1260 rset.lhs_w(idx) += val;
1261 }
1262 else
1263 {
1264 // GE
1265 if(double(rset.lhs(idx)) > -double(infinity))
1266 {
1267 rset.rhs_w(idx) = rset.lhs(idx);
1268 rset.rhs_w(idx) += spxAbs(val);
1269 }
1270 // LE
1271 else
1272 {
1273 rset.lhs_w(idx) = rset.rhs(idx);
1274 rset.lhs_w(idx) -= spxAbs(val);
1275 }
1276 }
1277 }
1278
1279 if(mps.field5() != 0)
1280 {
1281 if((idx = rnames.number(mps.field4())) < 0)
1282 mps.entryIgnored("Range", mps.field1(), "row", mps.field4());
1283 else
1284 {
1285 try
1286 {
1287 val = ratFromString(mps.field5());
1288 }
1289 catch(const std::exception& e)
1290 {
1291 MSG_WARNING((*spxout), (*spxout) << "WMPSRD06 Warning: malformed rational value in MPS file\n");
1292 std::cerr << e.what() << '\n';
1293 }
1294
1295 // EQ
1296 if((double(rset.lhs(idx)) > -double(infinity)) && (double(rset.rhs(idx)) < double(infinity)))
1297 {
1298 assert(rset.lhs(idx) == rset.rhs(idx));
1299
1300 if(double(val) >= 0)
1301 rset.rhs_w(idx) += val;
1302 else
1303 rset.lhs_w(idx) += val;
1304 }
1305 else
1306 {
1307 // GE
1308 if(double(rset.lhs(idx)) > -double(infinity))
1309 {
1310 rset.rhs_w(idx) = rset.lhs(idx);
1311 rset.rhs_w(idx) += spxAbs(val);
1312 }
1313 // LE
1314 else
1315 {
1316 rset.lhs_w(idx) = rset.rhs(idx);
1317 rset.lhs_w(idx) -= spxAbs(val);
1318 }
1319 }
1320 }
1321 }
1322 }
1323 }
1324
1325 mps.syntaxError();
1326 }
1327
1328
1329
1330 /// Process BOUNDS section.
1331 static void MPSreadBounds(MPSInput& mps, LPColSetBase<Rational>& cset, const NameSet& cnames,
1332 DIdxSet* intvars, SPxOut* spxout)
1333 {
1334 DIdxSet oldbinvars;
1335 char bndname[MPSInput::MAX_LINE_LEN] = { '\0' };
1336 int idx;
1337 Rational val;
1338
1339 while(mps.readLine())
1340 {
1341 if(mps.field0() != 0)
1342 {
1343 MSG_INFO2((*spxout), (*spxout) << "IMPSRD05 Bound name : " << bndname << std::endl;)
1344
1345 if(strcmp(mps.field0(), "ENDATA"))
1346 break;
1347
1348 mps.setSection(MPSInput::ENDATA);
1349
1350 return;
1351 }
1352
1353 // Is the value field used ?
1354 if((!strcmp(mps.field1(), "LO"))
1355 || (!strcmp(mps.field1(), "UP"))
1356 || (!strcmp(mps.field1(), "FX"))
1357 || (!strcmp(mps.field1(), "LI"))
1358 || (!strcmp(mps.field1(), "UI")))
1359 {
1360 if((mps.field3() != 0) && (mps.field4() == 0))
1361 mps.insertName("_BND_", true);
1362 }
1363 else
1364 {
1365 if((mps.field2() != 0) && (mps.field3() == 0))
1366 mps.insertName("_BND_", true);
1367 }
1368
1369 if((mps.field1() == 0) || (mps.field2() == 0) || (mps.field3() == 0))
1370 break;
1371
1372 if(*bndname == '\0')
1373 {
1374 assert(strlen(mps.field2()) < MPSInput::MAX_LINE_LEN);
1375 spxSnprintf(bndname, MPSInput::MAX_LINE_LEN, "%s", mps.field2());
1376 }
1377
1378 // Only read the first Bound in section
1379 if(!strcmp(bndname, mps.field2()))
1380 {
1381 if((idx = cnames.number(mps.field3())) < 0)
1382 mps.entryIgnored("column", mps.field3(), "bound", bndname);
1383 else
1384 {
1385 if(mps.field4() == 0)
1386 val = 0;
1387 else if(!strcmp(mps.field4(), "-Inf") || !strcmp(mps.field4(), "-inf"))
1388 val = -infinity;
1389 else if(!strcmp(mps.field4(), "Inf") || !strcmp(mps.field4(), "inf")
1390 || !strcmp(mps.field4(), "+Inf") || !strcmp(mps.field4(), "+inf"))
1391 val = infinity;
1392 else
1393 try
1394 {
1395 val = ratFromString(mps.field4());
1396 }
1397 catch(const std::exception& e)
1398 {
1399 MSG_WARNING((*spxout), (*spxout) << "WMPSRD07 Warning: malformed rational value in MPS file\n");
1400 std::cerr << e.what() << '\n';
1401 }
1402
1403 // ILOG extension (Integer Bound)
1404 if(mps.field1()[1] == 'I')
1405 {
1406 if(intvars != 0)
1407 intvars->addIdx(idx);
1408
1409 // if the variable has appeared in the MARKER section of the COLUMNS section then its default bounds were
1410 // set to 0,1; the first time it is declared integer we need to change to default bounds 0,infinity
1411 if(oldbinvars.pos(idx) < 0)
1412 {
1413 cset.upper_w(idx) = infinity;
1414 oldbinvars.addIdx(idx);
1415 }
1416 }
1417
1418 switch(*mps.field1())
1419 {
1420 case 'L':
1421 cset.lower_w(idx) = val;
1422 break;
1423
1424 case 'U':
1425 cset.upper_w(idx) = val;
1426 break;
1427
1428 case 'F':
1429 if(mps.field1()[1] == 'X')
1430 {
1431 cset.lower_w(idx) = val;
1432 cset.upper_w(idx) = val;
1433 }
1434 else
1435 {
1436 cset.lower_w(idx) = -infinity;
1437 cset.upper_w(idx) = infinity;
1438 }
1439
1440 break;
1441
1442 case 'M':
1443 cset.lower_w(idx) = -infinity;
1444 break;
1445
1446 case 'P':
1447 cset.upper_w(idx) = infinity;
1448 break;
1449
1450 // Ilog extension (Binary)
1451 case 'B':
1452 cset.lower_w(idx) = 0;
1453 cset.upper_w(idx) = 1;
1454
1455 if(intvars != 0)
1456 intvars->addIdx(idx);
1457
1458 break;
1459
1460 default:
1461 mps.syntaxError();
1462 return;
1463 }
1464 }
1465 }
1466 }
1467
1468 mps.syntaxError();
1469 }
1470
1471 /// Read LP in MPS File Format.
1472 /**
1473 * The specification is taken from the IBM Optimization Library Guide and Reference, online available at
1474 * http://www.software.ibm.com/sos/features/libuser.htm and from the ILOG CPLEX 7.0 Reference Manual, Appendix E, Page
1475 * 531.
1476 *
1477 * This routine should read all valid MPS format files. What it will not do, is find all cases where a file is ill
1478 * formed. If this happens it may complain and read nothing or read "something".
1479 *
1480 * @return true if the file was read correctly.
1481 */
1482 #define INIT_COLS 1000 ///< initialy allocated columns.
1483 #define INIT_NZOS 5000 ///< initialy allocated non zeros.
1484 template <> inline
1485 bool SPxLPBase<Rational>::readMPS(
1486 std::istream& p_input, ///< input stream.
1487 NameSet* p_rnames, ///< row names.
1488 NameSet* p_cnames, ///< column names.
1489 DIdxSet* p_intvars) ///< integer variables.
1490 {
1491 LPRowSetBase<Rational>& rset = *this;
1492 LPColSetBase<Rational>& cset = *this;
1493 NameSet* rnames;
1494 NameSet* cnames;
1495
1496 if(p_cnames)
1497 cnames = p_cnames;
1498 else
1499 {
1500 cnames = 0;
1501 spx_alloc(cnames);
1502 cnames = new(cnames) NameSet();
1503 }
1504
1505 cnames->clear();
1506
1507 if(p_rnames)
1508 rnames = p_rnames;
1509 else
1510 {
1511 try
1512 {
1513 rnames = 0;
1514 spx_alloc(rnames);
1515 rnames = new(rnames) NameSet();
1516 }
1517 catch(const SPxMemoryException& x)
1518 {
1519 if(!p_cnames)
1520 {
1521 cnames->~NameSet();
1522 spx_free(cnames);
1523 }
1524
1525 throw x;
1526 }
1527 }
1528
1529 rnames->clear();
1530
1531 SPxLPBase<Rational>::clear(); // clear the LP.
1532
1533 cset.memRemax(INIT_NZOS);
1534 cset.reMax(INIT_COLS);
1535
1536 MPSInput mps(p_input);
1537
1538 MPSreadName(mps, spxout);
1539
1540 if(mps.section() == MPSInput::OBJSEN)
1541 MPSreadObjsen(mps);
1542
1543 if(mps.section() == MPSInput::OBJNAME)
1544 MPSreadObjname(mps);
1545
1546 if(mps.section() == MPSInput::ROWS)
1547 MPSreadRows(mps, rset, *rnames, spxout);
1548
1549 addedRows(rset.num());
1550
1551 if(mps.section() == MPSInput::COLUMNS)
1552 MPSreadCols(mps, rset, *rnames, cset, *cnames, p_intvars, spxout);
1553
1554 if(mps.section() == MPSInput::RHS)
1555 MPSreadRhs(mps, rset, *rnames, spxout);
1556
1557 if(mps.section() == MPSInput::RANGES)
1558 MPSreadRanges(mps, rset, *rnames, spxout);
1559
1560 if(mps.section() == MPSInput::BOUNDS)
1561 MPSreadBounds(mps, cset, *cnames, p_intvars, spxout);
1562
1563 if(mps.section() != MPSInput::ENDATA)
1564 mps.syntaxError();
1565
1566 if(mps.hasError())
1567 clear();
1568 else
1569 {
1570 changeSense(mps.objSense() == MPSInput::MINIMIZE ? SPxLPBase<Rational>::MINIMIZE :
1571 SPxLPBase<Rational>::MAXIMIZE);
1572
1573 MSG_INFO2((*spxout), (*spxout) << "IMPSRD06 Objective sense: " << ((mps.objSense() ==
1574 MPSInput::MINIMIZE) ? "Minimize\n" : "Maximize\n"));
1575
1576 added2Set(
1577 *(reinterpret_cast<SVSetBase<Rational>*>(static_cast<LPRowSetBase<Rational>*>(this))),
1578 *(reinterpret_cast<SVSetBase<Rational>*>(static_cast<LPColSetBase<Rational>*>(this))),
1579 cset.num());
1580 addedCols(cset.num());
1581
1582 assert(isConsistent());
1583 }
1584
1585 if(p_cnames == 0)
1586 {
1587 cnames->~NameSet();
1588 spx_free(cnames);
1589 }
1590
1591 if(p_rnames == 0)
1592 {
1593 rnames->~NameSet();
1594 spx_free(rnames);
1595 }
1596
1597 return !mps.hasError();
1598 }
1599
1600
1601
1602 // ---------------------------------------------------------------------------------------------------------------------
1603 // Specialization for writing LP format
1604 // ---------------------------------------------------------------------------------------------------------------------
1605
1606 // get the name of a row or construct one
1607 static const char* LPFgetRowName(
1608 const SPxLPBase<Rational>& p_lp,
1609 int p_idx,
1610 const NameSet* p_rnames,
1611 char* p_buf,
1612 int p_num_written_rows
1613 )
1614 {
1615 assert(p_buf != 0);
1616 assert(p_idx >= 0);
1617 assert(p_idx < p_lp.nRows());
1618
1619 if(p_rnames != 0)
1620 {
1621 DataKey key = p_lp.rId(p_idx);
1622
1623 if(p_rnames->has(key))
1624 return (*p_rnames)[key];
1625 }
1626
1627 spxSnprintf(p_buf, 16, "C%d", p_num_written_rows);
1628
1629 return p_buf;
1630 }
1631
1632
1633
1634 // get the name of a column or construct one
1635 static const char* getColName(
1636 const SPxLPBase<Rational>& p_lp,
1637 int p_idx,
1638 const NameSet* p_cnames,
1639 char* p_buf
1640 )
1641 {
1642 assert(p_buf != 0);
1643 assert(p_idx >= 0);
1644 assert(p_idx < p_lp.nCols());
1645
1646 if(p_cnames != 0)
1647 {
1648 DataKey key = p_lp.cId(p_idx);
1649
1650 if(p_cnames->has(key))
1651 return (*p_cnames)[key];
1652 }
1653
1654 spxSnprintf(p_buf, 16, "x%d", p_idx);
1655
1656 return p_buf;
1657 }
1658
1659
1660
1661 // write an SVector
1662 #define NUM_ENTRIES_PER_LINE 5
1663 static void LPFwriteSVector(
1664 const SPxLPBase<Rational>& p_lp, ///< the LP
1665 std::ostream& p_output, ///< output stream
1666 const NameSet* p_cnames, ///< column names
1667 const SVectorBase<Rational>& p_svec, ///< vector to write
1668 SPxOut* spxout ///< out stream
1669 )
1670 {
1671
1672 char name[16];
1673 int num_coeffs = 0;
1674 long long pos;
1675
1676 pos = p_output.tellp();
1677
1678 for(int j = 0; j < p_lp.nCols(); ++j)
1679 {
1680 const Rational coeff = p_svec[j];
1681
1682 if(coeff == 0)
1683 continue;
1684
1685 if(num_coeffs == 0)
1686 p_output << coeff << " " << getColName(p_lp, j, p_cnames, name);
1687 else
1688 {
1689 // insert a line break every NUM_ENTRIES_PER_LINE columns or whenever max line length is nearly exceeded
1690 if(num_coeffs == NUM_ENTRIES_PER_LINE ||
1691 (long long)(p_output.tellp()) - pos + (long long)(coeff.str().length() + 100) > MAX_LINE_WRITE_LEN)
1692 {
1693 num_coeffs = 0;
1694 p_output << "\n\t";
1695
1696 if((long long)(p_output.tellp()) - pos > MAX_LINE_WRITE_LEN)
1697 {
1698 MSG_WARNING((*spxout), (*spxout) <<
1699 "XLPSWR01 Warning: MAX_LINE_WRITE_LEN possibly exceeded when writing LP file\n");
1700 }
1701
1702 pos = p_output.tellp();
1703 }
1704
1705 if(coeff < 0)
1706 p_output << " - " << -coeff;
1707 else
1708 p_output << " + " << coeff;
1709
1710 p_output << " " << getColName(p_lp, j, p_cnames, name);
1711 }
1712
1713 ++num_coeffs;
1714 }
1715 }
1716
1717
1718
1719 // write the objective
1720 static void LPFwriteObjective(
1721 const SPxLPBase<Rational>& p_lp, ///< the LP
1722 std::ostream& p_output, ///< output stream
1723 const NameSet* p_cnames, ///< column names
1724 SPxOut* spxout ///< out stream
1725 )
1726 {
1727
1728 const int sense = p_lp.spxSense();
1729
1730 p_output << ((sense == SPxLPBase<Rational>::MINIMIZE) ? "Minimize\n" : "Maximize\n");
1731 p_output << " obj: ";
1732
1733 const VectorBase<Rational>& obj = p_lp.maxObj();
1734 DSVectorBase<Rational> svec(obj.dim());
1735 svec.operator = (obj);
1736 svec *= Rational(sense);
1737 LPFwriteSVector(p_lp, p_output, p_cnames, svec, spxout);
1738 p_output << "\n";
1739 }
1740
1741
1742
1743 // write non-ranged rows
1744 static void LPFwriteRow(
1745 const SPxLPBase<Rational>& p_lp, ///< the LP
1746 std::ostream& p_output, ///< output stream
1747 const NameSet* p_cnames, ///< column names
1748 const SVectorBase<Rational>& p_svec, ///< vector of the row
1749 const Rational& p_lhs, ///< lhs of the row
1750 const Rational& p_rhs, ///< rhs of the row
1751 SPxOut* spxout ///< out stream
1752 )
1753 {
1754
1755 long long pos;
1756 pos = p_output.tellp();
1757
1758 LPFwriteSVector(p_lp, p_output, p_cnames, p_svec, spxout);
1759
1760 long long sidelen;
1761 sidelen = (p_lhs == p_rhs
1762 || double(p_lhs) <= double(-infinity)) ? (long long)p_rhs.str().length()
1763 : (long long)p_lhs.str().length();
1764
1765 // insert a line break if max line length is in danger of being exceeded
1766 if((long long)(p_output.tellp()) - pos + sidelen + (long long)100 > MAX_LINE_WRITE_LEN)
1767 {
1768 p_output << "\n\t";
1769
1770 if((long long)(p_output.tellp()) - pos > MAX_LINE_WRITE_LEN)
1771 {
1772 MSG_WARNING((*spxout), (*spxout) <<
1773 "XLPSWR02 Warning: MAX_LINE_WRITE_LEN possibly exceeded when writing LP file\n");
1774 }
1775
1776 pos = p_output.tellp();
1777 }
1778
1779 // write bound value
1780 if(p_lhs == p_rhs)
1781 p_output << " = " << p_rhs;
1782 else if(double(p_lhs) <= double(-infinity))
1783 p_output << " <= " << p_rhs;
1784 else
1785 {
1786 assert(double(p_rhs) >= double(infinity));
1787 p_output << " >= " << p_lhs;
1788 }
1789
1790 p_output << "\n";
1791
1792 if((long long)(p_output.tellp()) - pos > MAX_LINE_WRITE_LEN)
1793 {
1794 MSG_WARNING((*spxout), (*spxout) <<
1795 "XLPSWR03 Warning: MAX_LINE_WRITE_LEN possibly exceeded when writing LP file\n");
1796 }
1797 }
1798
1799
1800
1801 // write all rows
1802 static void LPFwriteRows(
1803 const SPxLPBase<Rational>& p_lp, ///< the LP
1804 std::ostream& p_output, ///< output stream
1805 const NameSet* p_rnames, ///< row names
1806 const NameSet* p_cnames, ///< column names
1807 SPxOut* spxout ///< out stream
1808 )
1809 {
1810
1811 char name[16];
1812
1813 p_output << "Subject To\n";
1814
1815 for(int i = 0; i < p_lp.nRows(); ++i)
1816 {
1817 const Rational lhs = p_lp.lhs(i);
1818 const Rational rhs = p_lp.rhs(i);
1819
1820 if(double(lhs) > -double(infinity) && double(rhs) < double(infinity) && lhs != rhs)
1821 {
1822 // ranged row -> write two non-ranged rows
1823 p_output << " " << LPFgetRowName(p_lp, i, p_rnames, name, i) << "_1 : ";
1824 LPFwriteRow(p_lp, p_output, p_cnames, p_lp.rowVector(i), lhs, infinity, spxout);
1825
1826 p_output << " " << LPFgetRowName(p_lp, i, p_rnames, name, i) << "_2 : ";
1827 LPFwriteRow(p_lp, p_output, p_cnames, p_lp.rowVector(i), -infinity, rhs, spxout);
1828 }
1829 else
1830 {
1831 p_output << " " << LPFgetRowName(p_lp, i, p_rnames, name, i) << " : ";
1832 LPFwriteRow(p_lp, p_output, p_cnames, p_lp.rowVector(i), lhs, rhs, spxout);
1833 }
1834 }
1835 }
1836
1837
1838
1839 // write the variable bounds
1840 // (the default bounds 0 <= x <= infinity are not written)
1841 static void LPFwriteBounds(
1842 const SPxLPBase<Rational>& p_lp, ///< the LP to write
1843 std::ostream& p_output, ///< output stream
1844 const NameSet* p_cnames, ///< column names
1845 SPxOut* spxout ///< out stream
1846 )
1847 {
1848
1849 char name[16];
1850 long long pos;
1851
1852 pos = p_output.tellp();
1853
1854 p_output << "Bounds\n";
1855
1856 for(int j = 0; j < p_lp.nCols(); ++j)
1857 {
1858 const Rational lower = p_lp.lower(j);
1859 const Rational upper = p_lp.upper(j);
1860
1861 if(lower == upper)
1862 {
1863 p_output << " " << getColName(p_lp, j, p_cnames, name) << " = " << upper << '\n';
1864 }
1865 else if(double(lower) > -double(infinity))
1866 {
1867 if(double(upper) < double(infinity))
1868 {
1869 // range bound
1870 if(lower != 0)
1871 p_output << " " << lower << " <= "
1872 << getColName(p_lp, j, p_cnames, name)
1873 << " <= " << upper << '\n';
1874 else
1875 p_output << " " << getColName(p_lp, j, p_cnames, name)
1876 << " <= " << upper << '\n';
1877 }
1878 else if(lower != 0)
1879 p_output << " " << lower << " <= "
1880 << getColName(p_lp, j, p_cnames, name)
1881 << '\n';
1882 }
1883 else if(double(upper) < double(infinity))
1884 p_output << " -Inf <= "
1885 << getColName(p_lp, j, p_cnames, name)
1886 << " <= " << upper << '\n';
1887 else
1888 p_output << " " << getColName(p_lp, j, p_cnames, name)
1889 << " free\n";
1890
1891 // check if max line length exceeded
1892 if((long long)(p_output.tellp()) - pos > MAX_LINE_WRITE_LEN)
1893 {
1894 MSG_WARNING((*spxout), (*spxout) <<
1895 "XLPSWR04 Warning: MAX_LINE_WRITE_LEN exceeded when writing LP file\n");
1896 }
1897
1898 pos = p_output.tellp();
1899 }
1900 }
1901
1902
1903
1904 // write the generals section
1905 static void LPFwriteGenerals(
1906 const SPxLPBase<Rational>& p_lp, ///< the LP to write
1907 std::ostream& p_output, ///< output stream
1908 const NameSet* p_cnames, ///< column names
1909 const DIdxSet* p_intvars ///< integer variables
1910 )
1911 {
1912
1913 char name[16];
1914
1915 if(p_intvars == NULL || p_intvars->size() <= 0)
1916 return; // no integer variables
1917
1918 p_output << "Generals\n";
1919
1920 for(int j = 0; j < p_lp.nCols(); ++j)
1921 if(p_intvars->pos(j) >= 0)
1922 p_output << " " << getColName(p_lp, j, p_cnames, name) << "\n";
1923 }
1924
1925
1926 /// Write LP in LP Format.
1927 template <> inline
1928 void SPxLPBase<Rational>::writeLPF(
1929 std::ostream& p_output, ///< output stream
1930 const NameSet* p_rnames, ///< row names
1931 const NameSet* p_cnames, ///< column names
1932 const DIdxSet* p_intvars ///< integer variables
1933 ) const
1934 {
1935 LPFwriteObjective(*this, p_output, p_cnames, spxout);
1936 LPFwriteRows(*this, p_output, p_rnames, p_cnames, spxout);
1937 LPFwriteBounds(*this, p_output, p_cnames, spxout);
1938 LPFwriteGenerals(*this, p_output, p_cnames, p_intvars);
1939
1940 p_output << "End" << std::endl;
1941 }
1942
1943
1944
1945 // ---------------------------------------------------------------------------------------------------------------------
1946 // Specialization for writing MPS format
1947 // ---------------------------------------------------------------------------------------------------------------------
1948
1949 // A problem here.
1950 static void MPSwriteRecord(
1951 std::ostream& os,
1952 const char* indicator,
1953 const char* name,
1954 SPxOut* spxout,
1955 const char* name1 = nullptr,
1956 const Rational value1 = 0,
1957 const char* name2 = nullptr,
1958 const Rational value2 = 0
1959 )
1960 {
1961 char buf[81];
1962 long long pos;
1963 pos = os.tellp();
1964
1965 spxSnprintf(buf, sizeof(buf), " %-2.2s %-8.8s", (indicator == 0) ? "" : indicator,
1966 (name == 0) ? "" : name);
1967 os << buf;
1968
1969 if(name1 != nullptr)
1970 {
1971 spxSnprintf(buf, sizeof(buf), " %-8.8s ", name1);
1972 os << buf << value1;
1973
1974 if(name2 != 0)
1975 {
1976 spxSnprintf(buf, sizeof(buf), " %-8.8s ", name2);
1977 os << buf << value2;
1978 }
1979 }
1980
1981 os << std::endl;
1982
1983 // Warning if line is too long
1984 if((long long)(os.tellp()) - pos > MAX_LINE_WRITE_LEN)
1985 {
1986 MSG_WARNING((*spxout), (*spxout) <<
1987 "XMPSWR04 Warning: MAX_LINE_WRITE_LEN exceeded when writing MPS file\n");
1988 }
1989 }
1990
1991
1992
1993 static Rational MPSgetRHS(Rational left, Rational right)
1994 {
1995 Rational rhsval;
1996
1997 if(double(left) > -double(infinity)) /// This includes ranges
1998 rhsval = left;
1999 else if(double(right) < double(infinity))
2000 rhsval = right;
2001 else
2002 throw SPxInternalCodeException("XMPSWR01 This should never happen.");
2003
2004 return rhsval;
2005 }
2006
2007
2008
2009 static const char* MPSgetRowName(
2010 const SPxLPBase<Rational>& lp,
2011 int idx,
2012 const NameSet* rnames,
2013 char* buf
2014 )
2015 {
2016 assert(buf != 0);
2017 assert(idx >= 0);
2018 assert(idx < lp.nRows());
2019
2020 if(rnames != 0)
2021 {
2022 DataKey key = lp.rId(idx);
2023
2024 if(rnames->has(key))
2025 return (*rnames)[key];
2026 }
2027
2028 spxSnprintf(buf, 16, "C%d", idx);
2029
2030 return buf;
2031 }
2032
2033
2034
2035 /// Write LP in MPS format.
2036 /** @note There will always be a BOUNDS section, even if there are no bounds.
2037 */
2038 template <> inline
2039 void SPxLPBase<Rational>::writeMPS(
2040 std::ostream& p_output, ///< output stream.
2041 const NameSet* p_rnames, ///< row names.
2042 const NameSet* p_cnames, ///< column names.
2043 const DIdxSet* p_intvars ///< integer variables.
2044 ) const
2045 {
2046
2047 const char* indicator;
2048 char name [16];
2049 char name1[16];
2050 char name2[16];
2051 bool has_ranges = false;
2052 int i;
2053 int k;
2054
2055 // --- NAME Section ---
2056 p_output << "NAME MPSDATA" << std::endl;
2057
2058 // --- ROWS Section ---
2059 p_output << "ROWS" << std::endl;
2060
2061 for(i = 0; i < nRows(); i++)
2062 {
2063 if(lhs(i) == rhs(i))
2064 indicator = "E";
2065 else if((double(lhs(i)) > -double(infinity)) && (double(rhs(i)) < double(infinity)))
2066 {
2067 indicator = "E";
2068 has_ranges = true;
2069 }
2070 else if(double(lhs(i)) > -double(infinity))
2071 indicator = "G";
2072 else if(double(rhs(i)) < double(infinity))
2073 indicator = "L";
2074 else
2075 throw SPxInternalCodeException("XMPSWR02 This should never happen.");
2076
2077 MPSwriteRecord(p_output, indicator, MPSgetRowName(*this, i, p_rnames, name), spxout);
2078 }
2079
2080 MPSwriteRecord(p_output, "N", "MINIMIZE", spxout);
2081
2082 // --- COLUMNS Section ---
2083 p_output << "COLUMNS" << std::endl;
2084
2085 bool has_intvars = (p_intvars != 0) && (p_intvars->size() > 0);
2086
2087 for(int j = 0; j < (has_intvars ? 2 : 1); j++)
2088 {
2089 bool is_intrun = has_intvars && (j == 1);
2090
2091 if(is_intrun)
2092 p_output << " MARK0001 'MARKER' 'INTORG'" << std::endl;
2093
2094 for(i = 0; i < nCols(); i++)
2095 {
2096 bool is_intvar = has_intvars && (p_intvars->pos(i) >= 0);
2097
2098 if((is_intrun && !is_intvar) || (!is_intrun && is_intvar))
2099 continue;
2100
2101 const SVectorBase<Rational>& col = colVector(i);
2102 int colsize2 = (col.size() / 2) * 2;
2103
2104 assert(colsize2 % 2 == 0);
2105
2106 for(k = 0; k < colsize2; k += 2)
2107 MPSwriteRecord(p_output, 0, getColName(*this, i, p_cnames, name), spxout,
2108 MPSgetRowName(*this, col.index(k), p_rnames, name1), col.value(k),
2109 MPSgetRowName(*this, col.index(k + 1), p_rnames, name2), col.value(k + 1));
2110
2111 if(colsize2 != col.size())
2112 MPSwriteRecord(p_output, 0, getColName(*this, i, p_cnames, name), spxout,
2113 MPSgetRowName(*this, col.index(k), p_rnames, name1), col.value(k));
2114
2115 if(maxObj(i) != 0)
2116 MPSwriteRecord(p_output, 0, getColName(*this, i, p_cnames, name), spxout, "MINIMIZE", -maxObj(i));
2117 }
2118
2119 if(is_intrun)
2120 p_output << " MARK0001 'MARKER' 'INTEND'" << std::endl;
2121 }
2122
2123 // --- RHS Section ---
2124 p_output << "RHS" << std::endl;
2125
2126 i = 0;
2127
2128 while(i < nRows())
2129 {
2130 Rational rhsval1 = 0;
2131 Rational rhsval2 = 0;
2132
2133 for(; i < nRows(); i++)
2134 if((rhsval1 = MPSgetRHS(lhs(i), rhs(i))) != 0)
2135 break;
2136
2137 if(i < nRows())
2138 {
2139 for(k = i + 1; k < nRows(); k++)
2140 {
2141 if((rhsval2 = MPSgetRHS(lhs(k), rhs(k))) != 0)
2142 break;
2143 }
2144
2145 if(k < nRows())
2146 {
2147 MPSwriteRecord(p_output, 0, "RHS", spxout, MPSgetRowName(*this, i, p_rnames, name1), rhsval1,
2148 MPSgetRowName(*this, k, p_rnames, name2), rhsval2);
2149 }
2150 else
2151 MPSwriteRecord(p_output, 0, "RHS", spxout, MPSgetRowName(*this, i, p_rnames, name1), rhsval1);
2152
2153 i = k + 1;
2154 }
2155 }
2156
2157 // --- RANGES Section ---
2158 if(has_ranges)
2159 {
2160 p_output << "RANGES" << std::endl;
2161
2162 for(i = 0; i < nRows(); i++)
2163 {
2164 if((double(lhs(i)) > -double(infinity)) && (double(rhs(i)) < double(infinity)))
2165 {
2166 Rational range = rhs(i);
2167 range -= lhs(i);
2168 MPSwriteRecord(p_output, "", "RANGE", spxout, MPSgetRowName(*this, i, p_rnames, name1), range);
2169 }
2170 }
2171 }
2172
2173 // --- BOUNDS Section ---
2174 p_output << "BOUNDS" << std::endl;
2175
2176 for(i = 0; i < nCols(); i++)
2177 {
2178 // skip variables that do not appear in the objective function or any constraint
2179 const SVectorBase<Rational>& col = colVector(i);
2180
2181 if(col.size() == 0 && maxObj(i) == 0)
2182 continue;
2183
2184 if(lower(i) == upper(i))
2185 {
2186 MPSwriteRecord(p_output, "FX", "BOUND", spxout, getColName(*this, i, p_cnames, name1), lower(i));
2187 continue;
2188 }
2189
2190 if((double(lower(i)) <= double(-infinity)) && (double(upper(i)) >= double(infinity)))
2191 {
2192 MPSwriteRecord(p_output, "FR", "BOUND", spxout, getColName(*this, i, p_cnames, name1));
2193 continue;
2194 }
2195
2196 if(lower(i) != 0)
2197 {
2198 if(double(lower(i)) > -double(infinity))
2199 MPSwriteRecord(p_output, "LO", "BOUND", spxout, getColName(*this, i, p_cnames, name1), lower(i));
2200 else
2201 MPSwriteRecord(p_output, "MI", "BOUND", spxout, getColName(*this, i, p_cnames, name1));
2202 }
2203
2204 if(has_intvars && (p_intvars->pos(i) >= 0))
2205 {
2206 // Integer variables have default upper bound 1, but we should write
2207 // it nevertheless since CPLEX seems to assume infinity otherwise.
2208 MPSwriteRecord(p_output, "UP", "BOUND", spxout, getColName(*this, i, p_cnames, name1), upper(i));
2209 }
2210 else
2211 {
2212 // Continous variables have default upper bound infinity
2213 if(double(upper(i)) < double(infinity))
2214 MPSwriteRecord(p_output, "UP", "BOUND", spxout, getColName(*this, i, p_cnames, name1), upper(i));
2215 }
2216 }
2217
2218 // --- ENDATA Section ---
2219 p_output << "ENDATA" << std::endl;
2220
2221 // Output warning when writing a maximisation problem
2222 if(spxSense() == SPxLPBase<Rational>::MAXIMIZE)
2223 {
2224 MSG_WARNING((*spxout), (*spxout) <<
2225 "XMPSWR03 Warning: objective function inverted when writing maximization problem in MPS file format\n");
2226 }
2227 }
2228
2229
2230
2231 /// Building the dual problem from a given LP
2232 /// @note primalRows must be as large as the number of unranged primal rows + 2 * the number of ranged primal rows.
2233 /// dualCols must have the identical size to the primal rows.
2234 template <> inline
2235 void SPxLPBase<Rational>::buildDualProblem(SPxLPBase<Rational>& dualLP, SPxRowId primalRowIds[],
2236 SPxColId primalColIds[],
2237 SPxRowId dualRowIds[], SPxColId dualColIds[], int* nprimalrows, int* nprimalcols, int* ndualrows,
2238 int* ndualcols)
2239 {
2240 assert(false);
2241 MSG_ERROR(std::cerr << "Method buildDualProblem() not implemented for Rational\n");
2242 }
2243
2244
2245 // ---------------------------------------------------------------------------------------------------------------------
2246 // Explicit instantiation
2247 // ---------------------------------------------------------------------------------------------------------------------
2248 template class SPxLPBase < Rational >;
2249 } // namespace soplex
2250