1 /**
2 * @file mpsinput.cpp
3 * @brief MPS reader class
4 *
5 * @author Domenico Salvagnin
6 * @author Tobias Achterberg
7 * @author Thorsten Koch
8 */
9
10 #include "mpsinput.h"
11 #include "model.h"
12 #include <stdlib.h>
13 #include <string.h>
14 #include <ctype.h>
15 #include <math.h>
16 #include <assert.h>
17 #include <sstream>
18 #include <iostream>
19
20 #define MPS_MIN_LINELEN 80
21 #define MPS_MAX_NAMELEN 256
22 #define PATCH_CHAR '_'
23 #define BLANK ' '
24 #define INFBOUND 1e20
25
26 /* fill the line from \p pos up to column MPS_MIN_LINELEN with blanks. */
27 static
clearFrom(char * buf,unsigned int pos)28 void clearFrom(
29 char* buf,
30 unsigned int pos
31 )
32 {
33 for(unsigned int i = pos; i < MPS_MIN_LINELEN; ++i)
34 buf[i] = BLANK;
35 buf[MPS_MIN_LINELEN] = '\0';
36 }
37
38 /* change all blanks inside a field to #PATCH_CHAR. */
39 static
patchField(char * buf,int beg,int end)40 void patchField(
41 char* buf,
42 int beg,
43 int end
44 )
45 {
46 while( (beg <= end) && (buf[end] == BLANK) )
47 end--;
48
49 while( (beg <= end) && (buf[beg] == BLANK) )
50 beg++;
51
52 for( int i = beg; i <= end; i++ )
53 if( buf[i] == BLANK )
54 buf[i] = PATCH_CHAR;
55 }
56
SyntaxError(const char * line,int lineNumber)57 SyntaxError::SyntaxError(const char* line, int lineNumber)
58 {
59 std::ostringstream out;
60 out << "Syntax error in line " << lineNumber << ": " << line;
61 msg = out.str();
62 }
63
what() const64 const char* SyntaxError::what() const throw()
65 {
66 return msg.c_str();
67 }
68
MpsInput()69 MpsInput::MpsInput()
70 {
71 section = MPS_NAME;
72 model = NULL;
73 fp = NULL;
74 gzfp = NULL;
75 isZipped = false;
76 lineno = 0;
77 isInteger = false;
78 isNewFormat = false;
79 semiContWarning = false;
80 buf[0] = '\0';
81 f0 = NULL;
82 f1 = NULL;
83 f2 = NULL;
84 f3 = NULL;
85 f4 = NULL;
86 f5 = NULL;
87 }
88
89 /* read a mps format data line and parse the fields. */
readLine()90 bool MpsInput::readLine()
91 {
92 bool isMarker;
93 bool isEmpty;
94 char* nexttok;
95
96 do
97 {
98 f0 = f1 = f2 = f3 = f4 = f5 = 0;
99 isMarker = false;
100
101 /* Read until we have a not comment line. */
102 if( isZipped )
103 {
104 do
105 {
106 memset((void*)buf, 0, MPS_MAX_LINELEN);
107 if (NULL == gzgets(gzfp, buf, sizeof(buf)))
108 return false;
109 lineno++;
110 }
111 while( buf[0] == '*' );
112 }
113 else
114 {
115 do
116 {
117 memset((void*)buf, 0, MPS_MAX_LINELEN);
118 if (NULL == fgets(buf, sizeof(buf), fp))
119 return false;
120 lineno++;
121 }
122 while( buf[0] == '*' );
123 }
124
125 /* Normalize line */
126 unsigned int len = strlen(buf);
127
128 for( unsigned int i = 0; i < len; i++ )
129 if( (buf[i] == '\t') || (buf[i] == '\n') || (buf[i] == '\r') )
130 buf[i] = BLANK;
131
132 if( len < MPS_MIN_LINELEN )
133 clearFrom(buf, len);
134
135 assert(strlen(buf) >= MPS_MIN_LINELEN);
136
137 /* Look for new section */
138 if( *buf != BLANK )
139 {
140 f0 = strtok_r(&buf[0], " ", &nexttok);
141 assert(f0 != 0);
142 f1 = strtok_r(NULL, " ", &nexttok);
143 return true;
144 }
145
146 /* If we decide to use the new format we never revert this decision */
147 if( !isNewFormat )
148 {
149 /* Test for fixed format comments */
150 if( (buf[14] == '$') && (buf[13] == ' ') )
151 clearFrom(buf, 14);
152 else if( (buf[39] == '$') && (buf[38] == ' ') )
153 clearFrom(buf, 39);
154
155 /* Test for fixed format */
156 int space = buf[12] | buf[13]
157 | buf[22] | buf[23]
158 | buf[36] | buf[37] | buf[38]
159 | buf[47] | buf[48]
160 | buf[61] | buf[62] | buf[63];
161
162 if (space == BLANK)
163 {
164 /* Now we have space at the right positions.
165 * But are there also the non space where they
166 * should be ?
167 */
168 int number = isdigit(buf[24]) || isdigit(buf[25])
169 || isdigit(buf[26]) || isdigit(buf[27])
170 || isdigit(buf[28]) || isdigit(buf[29])
171 || isdigit(buf[30]) || isdigit(buf[31])
172 || isdigit(buf[32]) || isdigit(buf[33])
173 || isdigit(buf[34]) || isdigit(buf[35]);
174
175 /* len < 13 is handle ROW lines with embedded spaces
176 * in the names correctly
177 */
178 if( number || len < 13 )
179 {
180 /* We assume fixed format, so we patch possible embedded spaces. */
181 patchField(buf, 4, 12);
182 patchField(buf, 14, 22);
183 patchField(buf, 39, 47);
184 }
185 else
186 {
187 if( section == MPS_COLUMNS || section == MPS_RHS
188 || section == MPS_RANGES || section == MPS_BOUNDS )
189 isNewFormat = true;
190 }
191 }
192 else
193 {
194 isNewFormat = true;
195 }
196 }
197 char* s = &buf[1];
198
199 /* At this point it is not clear if we have a indicator field.
200 * If there is none (e.g. empty) f1 will be the first name field.
201 * If there is one, f2 will be the first name field.
202 *
203 * Initially comment marks '$' are only allowed in the beginning
204 * of the 2nd and 3rd name field. We test all fields but the first.
205 * This makes no difference, since if the $ is at the start of a value
206 * field, the line will be errornous anyway.
207 */
208 do
209 {
210 if( NULL == (f1 = strtok_r(s, " ", &nexttok)) )
211 break;
212
213 if( (NULL == (f2 = strtok_r(NULL, " ", &nexttok))) || (*f2 == '$') )
214 {
215 f2 = 0;
216 break;
217 }
218 if( !strcmp(f2, "'MARKER'") )
219 isMarker = true;
220
221 if( (NULL == (f3 = strtok_r(NULL, " ", &nexttok))) || (*f3 == '$') )
222 {
223 f3 = 0;
224 break;
225 }
226 if( isMarker )
227 {
228 if( !strcmp(f3, "'INTORG'") )
229 isInteger = true;
230 else if( !strcmp(f3, "'INTEND'") )
231 isInteger = false;
232 else
233 break; /* unknown marker */
234 }
235 if( !strcmp(f3, "'MARKER'") )
236 isMarker = true;
237
238 if( (NULL == (f4 = strtok_r(NULL, " ", &nexttok))) || (*f4 == '$') )
239 {
240 f4 = 0;
241 break;
242 }
243 if( isMarker )
244 {
245 if( !strcmp(f4, "'INTORG'") )
246 isInteger = true;
247 else if( !strcmp(f4, "'INTEND'") )
248 isInteger = false;
249 else
250 break; /* unknown marker */
251 }
252 if( (NULL == (f5 = strtok_r(NULL, " ", &nexttok))) || (*f5 == '$') )
253 f5 = 0;
254 }
255 while( false );
256
257 /* check for empty lines */
258 isEmpty = (f0 == NULL && f1 == NULL);
259 }
260 while( isMarker || isEmpty );
261
262 return true;
263 }
264
265 /* Insert \p name as field 1 and shift all other fields up. */
insertName(const char * name,bool second)266 void MpsInput::insertName(
267 const char* name,
268 bool second
269 )
270 {
271 assert(name != NULL);
272
273 f5 = f4;
274 f4 = f3;
275 f3 = f2;
276
277 if( second )
278 f2 = name;
279 else
280 {
281 f2 = f1;
282 f1 = name;
283 }
284 }
285
286 /* Process NAME section. */
readName()287 void MpsInput::readName()
288 {
289 /* This has to be the Line with the NAME section. */
290 if( !readLine() || (f0 == NULL) || strcmp(f0, "NAME") )
291 throw SyntaxError(buf, lineno);
292
293 /* Sometimes the name is omitted. */
294 if( f1 == 0)
295 model->modelName = "_MPS_";
296 else
297 model->modelName = std::string(f1);
298
299 /* This has to be a new section */
300 if( !readLine() || (f0 == NULL) )
301 throw SyntaxError(buf, lineno);
302
303 if( !strncmp(f0, "ROWS", 4) )
304 section = MPS_ROWS;
305 else if( !strncmp(f0, "USERCUTS", 8) )
306 section = MPS_USERCUTS;
307 else if( !strncmp(f0, "LAZYCONS", 8) )
308 section = MPS_LAZYCONS;
309 else if( !strncmp(f0, "OBJSEN", 6) )
310 section = MPS_OBJSEN;
311 else if( !strncmp(f0, "OBJNAME", 7) )
312 section = MPS_OBJNAME;
313 else if( !strncmp(f0, "INDICATORS", 10) )
314 section = MPS_INDICATORS;
315 else
316 throw SyntaxError(buf, lineno);
317 }
318
319 /* Process OBJSEN section. This Section is an ILOG extension. */
readObjsen()320 void MpsInput::readObjsen()
321 {
322 /* This has to be the Line with MIN or MAX. */
323 if( !readLine() || (f1 == NULL) )
324 throw SyntaxError(buf, lineno);
325
326 if( !strncmp(f1, "MIN", 3) )
327 model->objSense = Model::MINIMIZE;
328 else if( !strncmp(f1, "MAX", 3) )
329 model->objSense = Model::MAXIMIZE;
330 else
331 throw SyntaxError(buf, lineno);
332
333 /* Look for ROWS, USERCUTS, LAZYCONS, or OBJNAME Section */
334 if( !readLine() || f0 == NULL )
335 throw SyntaxError(buf, lineno);
336
337 if( !strcmp(f0, "ROWS") )
338 section = MPS_ROWS;
339 else if( !strcmp(f0, "USERCUTS") )
340 section = MPS_USERCUTS;
341 else if( !strcmp(f0, "LAZYCONS") )
342 section = MPS_LAZYCONS;
343 else if( !strcmp(f0, "OBJNAME") )
344 section = MPS_OBJNAME;
345 else
346 throw SyntaxError(buf, lineno);
347 }
348
349 /* Process OBJNAME section. This Section is an ILOG extension. */
readObjname()350 void MpsInput::readObjname()
351 {
352 std::cout << "readObjName" << std::endl;
353 /* This has to be the Line with the name. */
354 if( !readLine() || f1 == NULL )
355 throw SyntaxError(buf, lineno);
356
357 model->objName = std::string(f1);
358
359 /* Look for ROWS, USERCUTS, or LAZYCONS Section */
360 if( !readLine() || f0 == NULL )
361 throw SyntaxError(buf, lineno);
362
363 if( !strcmp(f0, "ROWS") )
364 section = MPS_ROWS;
365 else if( !strcmp(f0, "USERCUTS") )
366 section = MPS_USERCUTS;
367 else if( !strcmp(f0, "LAZYCONS") )
368 section = MPS_LAZYCONS;
369 else
370 throw SyntaxError(buf, lineno);
371 }
372
373 /* Process ROWS, USERCUTS, or LAZYCONS section. */
readRows()374 void MpsInput::readRows()
375 {
376 while( readLine() )
377 {
378 if( f0 != NULL )
379 {
380 if( !strcmp(f0, "ROWS") )
381 section = MPS_ROWS;
382 else if( !strcmp(f0, "USERCUTS") )
383 section = MPS_USERCUTS;
384 else if( !strcmp(f0, "LAZYCONS") )
385 section = MPS_LAZYCONS;
386 else if( !strcmp(f0, "COLUMNS") )
387 section = MPS_COLUMNS;
388 else
389 throw SyntaxError(buf, lineno);
390 return;
391 }
392
393 if( *f1 == 'N' )
394 {
395 if( model->objName.empty() )
396 model->objName = std::string(f2);
397 else if( model->objName != std::string(f2) )
398 std::cerr << "Warning line " << lineno << ": row <" << f2 << "> for objective function N ignored" << std::endl;
399 }
400 else
401 {
402 bool redundant = (section == MPS_USERCUTS);
403 LinearConstraint::LinearType ctype;
404 Rational clb; // default to zero
405 Rational cub; // default to zero
406
407 switch(*f1)
408 {
409 case 'G' :
410 ctype = LinearConstraint::GREATER_THAN;
411 cub = INFBOUND;
412 break;
413 case 'E' :
414 ctype = LinearConstraint::EQUAL;
415 break;
416 case 'L' :
417 ctype = LinearConstraint::LESS_THAN;
418 clb = -INFBOUND;
419 break;
420 default :
421 throw SyntaxError(buf, lineno);
422 }
423
424 model->pushCons(new LinearConstraint(f2, ctype, clb, cub, redundant));
425 }
426 }
427 throw SyntaxError(buf, lineno);
428 }
429
430 /* Process COLUMNS section. */
readCols()431 void MpsInput::readCols()
432 {
433 char colname[MPS_MAX_NAMELEN] = { '\0' };
434
435 while( readLine() )
436 {
437 if( f0 != 0 )
438 {
439 if( strcmp(f0, "RHS") )
440 break;
441 section = MPS_RHS;
442 return;
443 }
444 if( f1 == NULL || f2 == NULL || f3 == NULL )
445 break;
446
447 /* new column? */
448 if( strcmp(colname, f1) )
449 {
450 (void)strncpy(colname, f1, MPS_MAX_NAMELEN - 1);
451
452 if( isInteger )
453 {
454 /* for integer variables, default bounds are 0 <= x < 1(not +infinity, like it is for continuous variables), and default cost is 0 */
455 model->pushVar(new Var(colname, Var::BINARY, 0.0, 1.0, 0.0));
456 }
457 else
458 {
459 /* for continuous variables, default bounds are 0 <= x, and default cost is 0 */
460 model->pushVar(new Var(colname, Var::CONTINUOUS, 0.0, INFBOUND, 0.0));
461 }
462 }
463
464 Var* var = model->getVar(colname);
465 assert( var != NULL );
466 Rational val;
467 val.fromString(f3);
468
469 if( std::string(f2) == model->objName )
470 var->objCoef = val;
471 else
472 {
473 LinearConstraint* cons = static_cast<LinearConstraint*>(model->getCons(f2));
474 /* we cannot assert cons != NULL, because we may have ignored free rows! */
475 if(cons != NULL)
476 cons->push(var, val);
477 }
478 if( f5 != NULL )
479 {
480 assert( f4 != NULL );
481
482 val.fromString(f5);
483
484 if( std::string(f4) == model->objName )
485 var->objCoef = val;
486 else
487 {
488 LinearConstraint* cons = static_cast<LinearConstraint*>(model->getCons(f4));
489 /* we cannot assert cons != NULL, because we may have ignored free rows! */
490 if(cons != NULL)
491 cons->push(var, val);
492 }
493 }
494 }
495 throw SyntaxError(buf, lineno);
496 }
497
498 /* Process RHS section. */
readRhs()499 void MpsInput::readRhs()
500 {
501 char rhsname[MPS_MAX_NAMELEN] = { '\0' };
502 Rational val;
503
504 while( readLine() )
505 {
506 if( f0 != NULL )
507 {
508 if( !strcmp(f0, "RANGES") )
509 section = MPS_RANGES;
510 else if( !strcmp(f0, "BOUNDS") )
511 section = MPS_BOUNDS;
512 else if( !strcmp(f0, "INDICATORS") )
513 section = MPS_INDICATORS;
514 else if( !strcmp(f0, "SOS") )
515 section = MPS_SOS;
516 else if( !strcmp(f0, "ENDATA") )
517 section = MPS_ENDATA;
518 else
519 break;
520 return;
521 }
522 if( (f2 != NULL && f3 == NULL)
523 || (f4 != NULL && f5 == NULL) )
524 insertName("_RHS_", false);
525
526 if( f1 == NULL || f2 == NULL || f3 == NULL )
527 break;
528
529 if( *rhsname == '\0' )
530 (void)strncpy(rhsname, f1, MPS_MAX_NAMELEN - 1);
531
532 if( !strcmp(rhsname, f1) )
533 {
534 val.fromString(f3);
535 LinearConstraint* cons = static_cast<LinearConstraint*>(model->getCons(f2));
536 assert( cons != NULL );
537 switch(cons->lintype)
538 {
539 case LinearConstraint::LESS_THAN:
540 cons->rhs = val;
541 break;
542 case LinearConstraint::GREATER_THAN:
543 cons->lhs = val;
544 break;
545 case LinearConstraint::EQUAL:
546 cons->lhs = val;
547 cons->rhs = val;
548 break;
549 default:
550 throw SyntaxError(buf, lineno);
551 }
552 if( f5 != NULL )
553 {
554 val.fromString(f5);
555 LinearConstraint* cons = static_cast<LinearConstraint*>(model->getCons(f4));
556 assert( cons != NULL );
557 switch(cons->lintype)
558 {
559 case LinearConstraint::LESS_THAN:
560 cons->rhs = val;
561 break;
562 case LinearConstraint::GREATER_THAN:
563 cons->lhs = val;
564 break;
565 case LinearConstraint::EQUAL:
566 cons->lhs = val;
567 cons->rhs = val;
568 break;
569 default:
570 throw SyntaxError(buf, lineno);
571 }
572 }
573 }
574 }
575 throw SyntaxError(buf, lineno);
576 }
577
578 /* Process RANGES section */
readRanges()579 void MpsInput::readRanges()
580 {
581 char rngname[MPS_MAX_NAMELEN] = { '\0' };
582 Rational val;
583
584 while( readLine() )
585 {
586 if( f0 != NULL )
587 {
588 if( !strcmp(f0, "BOUNDS") )
589 section = MPS_BOUNDS;
590 else if( !strcmp(f0, "INDICATORS") )
591 section = MPS_INDICATORS;
592 else if( !strcmp(f0, "SOS") )
593 section = MPS_SOS;
594 else if( !strcmp(f0, "ENDATA") )
595 section = MPS_ENDATA;
596 else
597 break;
598 return;
599 }
600 if( (f2 != NULL && f3 == NULL)
601 || (f4 != NULL && f5 == NULL) )
602 insertName("_RNG_", false);
603
604 if( f1 == NULL || f2 == NULL || f3 == NULL )
605 break;
606
607 if( *rngname == '\0' )
608 (void)strncpy(rngname, f1, MPS_MAX_NAMELEN - 1);
609
610 /* The rules are:
611 * Row Sign LHS RHS
612 * ----------------------------------------
613 * G +/- rhs rhs + |range|
614 * L +/- rhs - |range| rhs
615 * E + rhs rhs + range
616 * E - rhs + range rhs
617 * ----------------------------------------
618 */
619 if( !strcmp(rngname, f1) )
620 {
621 val.fromString(f3);
622 LinearConstraint* cons = static_cast<LinearConstraint*>(model->getCons(f2));
623 assert( cons != NULL );
624 switch(cons->lintype)
625 {
626 case LinearConstraint::LESS_THAN:
627 val.abs();
628 sub(cons->lhs, cons->rhs, val);
629 break;
630 case LinearConstraint::GREATER_THAN:
631 val.abs();
632 add(cons->rhs, cons->lhs, val);
633 break;
634 case LinearConstraint::EQUAL:
635 if (val.isPositive())
636 cons->rhs += val;
637 else
638 cons->lhs += val;
639 break;
640 default:
641 throw SyntaxError(buf, lineno);
642 }
643 cons->lintype = LinearConstraint::RANGED;
644 if( f5 != NULL )
645 {
646 val.fromString(f5);
647 LinearConstraint* cons = static_cast<LinearConstraint*>(model->getCons(f4));
648 assert( cons != NULL );
649 switch(cons->lintype)
650 {
651 case LinearConstraint::LESS_THAN:
652 val.abs();
653 sub(cons->lhs, cons->rhs, val);
654 break;
655 case LinearConstraint::GREATER_THAN:
656 val.abs();
657 add(cons->rhs, cons->lhs, val);
658 break;
659 case LinearConstraint::EQUAL:
660 if (val.isPositive())
661 cons->rhs += val;
662 else
663 cons->lhs += val;
664 break;
665 default:
666 throw SyntaxError(buf, lineno);
667 }
668 cons->lintype = LinearConstraint::RANGED;
669 }
670 }
671 }
672 throw SyntaxError(buf, lineno);
673 }
674
675 /* Process BOUNDS section. */
readBounds()676 void MpsInput::readBounds()
677 {
678 char bndname[MPS_MAX_NAMELEN] = { '\0' };
679 Rational val;
680 Rational zero;
681 Rational one(1);
682 bool shifted;
683
684 while( readLine() )
685 {
686 if( f0 != 0 )
687 {
688 if( !strcmp(f0, "INDICATORS") )
689 section = MPS_INDICATORS;
690 else if( !strcmp(f0, "SOS") )
691 section = MPS_SOS;
692 else if( !strcmp(f0, "ENDATA") )
693 section = MPS_ENDATA;
694 else
695 break;
696 return;
697 }
698
699 shifted = false;
700
701 /* Is the value field used ? */
702 if( !strcmp(f1, "LO") /* lower bound given in field 4 */
703 || !strcmp(f1, "UP") /* upper bound given in field 4 */
704 || !strcmp(f1, "FX") /* fixed value given in field 4 */
705 || !strcmp(f1, "LI") /* CPLEX extension: lower bound of integer variable given in field 4 */
706 || !strcmp(f1, "UI") /* CPLEX extension: upper bound of integer variable given in field 4 */
707 || !strcmp(f1, "SC") )/* CPLEX extension: semi continuous variable */
708 {
709 if( f3 != NULL && f4 == NULL )
710 {
711 insertName("_BND_", true);
712 shifted = true;
713 }
714 if( !semiContWarning && !strcmp(f1, "SC") )
715 {
716 std::cerr << "Warning line " << lineno << ": not supported semi continuous declaration " << f1 << " for variable <" << f3 << ">" << std::endl;
717 semiContWarning = true;
718 }
719 }
720 else if( !strcmp(f1, "FR") /* free variable */
721 || !strcmp(f1, "MI") /* lower bound is minus infinity */
722 || !strcmp(f1, "PL") /* upper bound is plus infinity */
723 || !strcmp(f1, "BV") ) /* CPLEX extension: binary variable */
724 {
725 if( f2 != NULL && f3 == NULL )
726 {
727 insertName("_BND_", true);
728 shifted = true;
729 }
730 }
731 else
732 throw SyntaxError(buf, lineno);
733
734 if( f1 == NULL || f2 == NULL || f3 == NULL )
735 break;
736
737 if( *bndname == '\0' )
738 (void)strncpy(bndname, f2, MPS_MAX_NAMELEN - 1);
739
740 /* Only read the first Bound in section */
741 if( !strcmp(bndname, f2) )
742 {
743 Var* var = model->getVar(f3);
744 assert( var != NULL );
745 if( f4 == NULL )
746 val = 0.0;
747 else
748 val.fromString(f4);
749
750 /* if a bound of a binary variable is given, the variable is converted into an integer variable
751 * with default bounds 0 <= x <= infinity
752 */
753 if( var->type == Var::BINARY )
754 {
755 if( (f1[1] == 'I') /* ILOG extension (Integer Bound) */
756 || (!(f1[0] == 'L' && (val == zero))
757 && !(f1[0] == 'U' && (val == one))) )
758 {
759 var->type = Var::INTEGER;
760 var->ub = INFBOUND;
761 }
762 }
763
764 switch( f1[0] )
765 {
766 case 'L':
767 if( f1[1] == 'I' ) /* ILOG extension (Integer Bound) */
768 var->type = Var::INTEGER;
769 var->lb = val;
770 break;
771 case 'U':
772 if( f1[1] == 'I' ) /* ILOG extension (Integer Bound) */
773 var->type = Var::INTEGER;
774 var->ub = val;
775 break;
776 case 'F':
777 if( f1[1] == 'X' )
778 {
779 var->lb = val;
780 var->ub = val;
781 }
782 else
783 {
784 var->lb = -INFBOUND;
785 var->ub = INFBOUND;
786 }
787 break;
788 case 'M':
789 var->lb = -INFBOUND;
790 break;
791 case 'P':
792 var->ub = INFBOUND;
793 break;
794 case 'B' : /* Ilog extension (Binary) */
795 var->type = Var::BINARY;
796 max(var->lb, zero, var->lb);
797 min(var->ub, one, var->ub);
798 break;
799 default:
800 throw SyntaxError(buf, lineno);
801 }
802 }
803 else
804 {
805 /* check for syntax error */
806 assert(*bndname != '\0');
807 if( strcmp(bndname, f3) == 0 && shifted )
808 throw SyntaxError(buf, lineno);
809
810 std::cerr << "Warning line " << lineno << ": bound " << f2 << " for variable <" << f3 << "> ignored" << std::endl;
811 }
812 }
813 throw SyntaxError(buf, lineno);
814 }
815
816 /* Process SOS section [non standard ILOG extension]. */
readSOS()817 void MpsInput::readSOS()
818 {
819 char sosname[MPS_MAX_NAMELEN] = { '\0' };
820 int cnt = 0;
821
822 while( readLine() )
823 {
824 if( f0 != NULL )
825 {
826 if( !strcmp(f0, "INDICATORS") )
827 section = MPS_INDICATORS;
828 else if( !strcmp(f0, "ENDATA") )
829 section = MPS_ENDATA;
830 else
831 break;
832 return;
833 }
834
835 if( f1 == NULL && f2 == NULL )
836 break;
837
838 /* check for new SOS set */
839 int type = -1;
840 if( !strcmp(f1, "S1") )
841 type = 1;
842 if( !strcmp(f1, "S2") )
843 type = 2;
844
845 if( type > 0 )
846 {
847 assert( type == 1 || type == 2 );
848 if( f2 != NULL )
849 (void)strncpy(sosname, f2, MPS_MAX_NAMELEN - 1);
850 else
851 (void)snprintf(sosname, MPS_MAX_NAMELEN, "SOS%d", ++cnt);
852 /* create SOS constraint */
853 if( type == 1 )
854 model->pushCons(new SOSConstraint(sosname, SOSConstraint::TYPE_1));
855 else if( type == 2 )
856 model->pushCons(new SOSConstraint(sosname, SOSConstraint::TYPE_2));
857 }
858 else
859 {
860 SOSConstraint* cons = static_cast<SOSConstraint*>(model->getCons(sosname));
861 assert( cons != NULL );
862 /* add vars to SOS constraint */
863 Var* var = model->getVar(f1);
864 assert( var != NULL );
865 cons->push(var);
866 }
867 }
868 throw SyntaxError(buf, lineno);
869 }
870
871 /* Process INDICATORS section. */
readIndicators()872 void MpsInput::readIndicators()
873 {
874 Rational ifval;
875 Rational zero;
876 Rational one(1);
877
878 while( readLine() )
879 {
880 if( f0 != 0 )
881 {
882 if( !strcmp(f0, "SOS") )
883 section = MPS_SOS;
884 else if( !strcmp(f0, "ENDATA") )
885 section = MPS_ENDATA;
886 else
887 break;
888 return;
889 }
890
891 /* All indicator rows start with 'IF' */
892 if (strcmp(f1, "IF")) throw SyntaxError(buf, lineno);
893
894 /* These fields are mandatory */
895 if( f2 == NULL || f3 == NULL || f4 == NULL ) throw SyntaxError(buf, lineno);
896
897 /* Get indicator data */
898 Var* ifvar = model->getVar(f3);
899 assert( ifvar != NULL );
900 ifval.fromString(f4);
901 Constraint* thencons = model->getCons(f2);
902 assert( thencons != NULL );
903 assert( ifval == one || ifval == zero );
904
905 /* Remove old constraint named 'f2' */
906 model->removeCons(f2);
907
908 /* Add indicator constraint */
909 model->pushCons(new IndicatorConstraint(f2, ifvar, ifval == one, thencons));
910 }
911 throw SyntaxError(buf, lineno);
912 }
913
914 /* Read LP in "MPS File Format".
915 *
916 * A specification of the MPS format can be found at
917 *
918 * http://plato.asu.edu/ftp/mps_format.txt,
919 * ftp://ftp.caam.rice.edu/pub/people/bixby/miplib/mps_format,
920 *
921 * and in the
922 *
923 * ILOG CPLEX Reference Manual
924 *
925 * This routine should read all valid MPS format files.
926 * What it will not do, is to find all cases where a file is ill formed.
927 * If this happens it may complain and read nothing or read "something".
928 */
929
readMps(const char * _filename,Model * _model)930 bool MpsInput::readMps(
931 const char* _filename, /**< name of the input file */
932 Model* _model
933 )
934 {
935 assert( _filename != NULL );
936 assert( _model != NULL );
937
938 int pathlen = strlen(_filename);
939 if( strcmp(_filename + pathlen - 3, ".gz") == 0 )
940 {
941 isZipped = true;
942 gzfp = gzopen(_filename, "r");
943 if( gzfp == NULL )
944 {
945 std::cout << "cannot open file <" << _filename << "> for reading" << std::endl;
946 return false;
947 }
948 }
949 else
950 {
951 fp = fopen(_filename, "r");
952 if( fp == NULL )
953 {
954 std::cout << "cannot open file <" << _filename << "> for reading" << std::endl;
955 return false;
956 }
957 }
958 assert( fp != NULL || gzfp != NULL );
959
960 model = _model;
961
962 bool hasError = false;
963
964 try
965 {
966 readName();
967
968 if( section == MPS_OBJSEN )
969 readObjsen();
970 if( section == MPS_OBJNAME )
971 readObjname();
972 while( section == MPS_ROWS
973 || section == MPS_USERCUTS
974 || section == MPS_LAZYCONS )
975 {
976 readRows();
977 }
978 if( section == MPS_COLUMNS )
979 readCols();
980 if( section == MPS_RHS )
981 readRhs();
982 if( section == MPS_RANGES )
983 readRanges();
984 if( section == MPS_BOUNDS )
985 readBounds();
986 if( section == MPS_SOS )
987 readSOS();
988 if( section == MPS_INDICATORS )
989 readIndicators();
990 if( section != MPS_ENDATA )
991 throw SyntaxError(buf, lineno);
992 }
993 catch(SyntaxError& err)
994 {
995 std::cerr << err.what() << std::endl;
996 hasError = true;
997 section = MPS_ENDATA;
998 }
999
1000 model = NULL;
1001
1002 if( isZipped )
1003 {
1004 gzclose(gzfp);
1005 gzfp = NULL;
1006 }
1007 else
1008 {
1009 fclose(fp);
1010 fp = NULL;
1011 }
1012 isZipped = false;
1013
1014 return (!hasError);
1015 }
1016