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