1
2 #include <string.h>
3 #include <stdarg.h>
4 #include <ctype.h>
5 #include "commonlib.h"
6 #include "lp_lib.h"
7 #include "lp_scale.h"
8 #include "lp_report.h"
9 #include "lp_MPS.h"
10
11 #ifdef FORTIFY
12 # include "lp_fortify.h"
13 #endif
14
15 /* Define buffer-size controled function mapping */
16 # if defined _MSC_VER
17 # define vsnprintf _vsnprintf
18 # endif
19
20 /* MPS file input and output routines for lp_solve */
21 /* ------------------------------------------------------------------------- */
22
23 /*
24 A: MPS format was named after an early IBM LP product and has emerged
25 as a de facto standard ASCII medium among most of the commercial LP
26 codes. Essentially all commercial LP codes accept this format, but if
27 you are using public domain software and have MPS files, you may need
28 to write your own reader routine for this. It's not too hard. The
29 main things to know about MPS format are that it is column oriented (as
30 opposed to entering the model as equations), and everything (variables,
31 rows, etc.) gets a name. MPS format is described in more detail in
32 Murtagh's book, referenced in another section. Also,
33
34 ftp://softlib.cs.rice.edu/pub/miplib/mps_format
35
36 is a nice short introduction. exports
37
38 MPS is an old format, so it is set up as though you were using punch
39 cards, and is not free format. Fields start in column 1, 5, 15, 25, 40
40 and 50. Sections of an MPS file are marked by so-called header cards,
41 which are distinguished by their starting in column 1. Although it is
42 typical to use upper-case throughout the file (like I said, MPS has
43 long historical roots), many MPS-readers will accept mixed-case for
44 anything except the header cards, and some allow mixed-case anywhere.
45 The names that you choose for the individual entities (constraints or
46 variables) are not important to the solver; you should pick names that
47 are meaningful to you, or will be easy for a post-processing code to
48 read.
49
50 Here is a little sample model written in MPS format (explained in more
51 detail below):
52
53 NAME TESTPROB
54 ROWS
55 N COST
56 L LIM1
57 G LIM2
58 E MYEQN
59 COLUMNS
60 XONE COST 1 LIM1 1
61 XONE LIM2 1
62 YTWO COST 4 LIM1 1
63 YTWO MYEQN -1
64 ZTHREE COST 9 LIM2 1
65 ZTHREE MYEQN 1
66 RHS
67 RHS1 LIM1 5 LIM2 10
68 RHS1 MYEQN 7
69 BOUNDS
70 UP BND1 XONE 4
71 LO BND1 YTWO -1
72 UP BND1 YTWO 1
73 ENDATA
74
75 means:
76
77 Optimize
78 COST: XONE + 4 YTWO + 9 ZTHREE
79 Subject To
80 LIM1: XONE + YTWO <= 5
81 LIM2: XONE + ZTHREE >= 10
82 MYEQN: - YTWO + ZTHREE = 7
83 Bounds
84 0 <= XONE <= 4
85 -1 <= YTWO <= 1
86 End
87
88 */
89
90 /* copy a MPS name, only trailing spaces are removed. In MPS, names can have
91 embedded spaces! */
namecpy(char * into,char * from)92 STATIC void namecpy(char *into, char *from)
93 {
94 int i;
95
96 /* copy at most 8 characters of from, stop at end of string or newline */
97 for(i = 0; (from[i] != '\0') && (from[i] != '\n') && (from[i] != '\r') && (i < 8); i++)
98 into[i] = from[i];
99
100 /* end with end of string */
101 into[i] = '\0';
102
103 /* remove trailing spaces, if any */
104 for(i--; (i >= 0) && (into[i] == ' '); i--)
105 into[i] = '\0';
106 }
107
108 /* scan an MPS line, and pick up the information in the fields that are
109 present */
110
111 /* scan_line for fixed MPS format */
scan_lineFIXED(int section,char * line,char * field1,char * field2,char * field3,double * field4,char * field5,double * field6)112 STATIC int scan_lineFIXED(int section, char* line, char *field1, char *field2, char *field3,
113 double *field4, char *field5, double *field6)
114 {
115 int items = 0, line_len;
116 char buf[16], *ptr1, *ptr2;
117
118 line_len = (int) strlen(line);
119 while ((line_len) && ((line[line_len-1] == '\n') || (line[line_len-1] == '\r') || (line[line_len-1] == ' ')))
120 line_len--;
121
122 if(line_len >= 1) { /* spaces or N/L/G/E or UP/LO */
123 strncpy(buf, line, 4);
124 buf[4] = '\0';
125 sscanf(buf, "%s", field1);
126 items++;
127 }
128 else
129 field1[0] = '\0';
130
131 line += 4;
132
133 if(line_len >= 5) { /* name */
134 namecpy(field2, line);
135 items++;
136 }
137 else
138 field2[0] = '\0';
139
140 line += 10;
141
142 if(line_len >= 14) { /* name */
143 namecpy(field3, line);
144 items++;
145 }
146 else
147 field3[0] = '\0';
148
149 line += 10;
150
151 if(line_len >= 25) { /* number */
152 strncpy(buf, line, 15);
153 buf[15] = '\0';
154 for(ptr1 = ptr2 = buf; ; ptr1++)
155 if(!isspace((unsigned char) *ptr1))
156 if((*(ptr2++) = *ptr1) == 0)
157 break;
158 /* *field4 = atof(buf); */
159 *field4 = strtod(buf, &ptr1);
160 if(*ptr1)
161 return(-1);
162 items++;
163 }
164 else
165 *field4 = 0;
166
167 line += 15;
168
169 if(line_len >= 40) { /* name */
170 namecpy(field5, line);
171 items++;
172 }
173 else
174 field5[0] = '\0';
175 line += 10;
176
177 if(line_len >= 50) { /* number */
178 strncpy(buf, line, 15);
179 buf[15] = '\0';
180 for(ptr1 = ptr2 = buf; ; ptr1++)
181 if(!isspace((unsigned char) *ptr1))
182 if((*(ptr2++) = *ptr1) == 0)
183 break;
184 /* *field6 = atof(buf); */
185 *field6 = strtod(buf, &ptr1);
186 if(*ptr1)
187 return(-1);
188 items++;
189 }
190 else
191 *field6 = 0;
192
193 return(items);
194 }
195
spaces(char * line,int line_len)196 STATIC int spaces(char *line, int line_len)
197 {
198 int l;
199 char *line1 = line;
200
201 while (*line1 == ' ')
202 line1++;
203 l = (int) (line1 - line);
204 if (line_len < l)
205 l = line_len;
206 return(l);
207 }
208
lenfield(char * line,int line_len)209 STATIC int lenfield(char *line, int line_len)
210 {
211 int l;
212 char *line1 = line;
213
214 while ((*line1) && (*line1 != ' '))
215 line1++;
216 l = (int) (line1 - line);
217 if (line_len < l)
218 l = line_len;
219 return(l);
220 }
221
222 /* scan_line for fixed MPS format */
scan_lineFREE(int section,char * line,char * field1,char * field2,char * field3,double * field4,char * field5,double * field6)223 STATIC int scan_lineFREE(int section, char* line, char *field1, char *field2, char *field3,
224 double *field4, char *field5, double *field6)
225 {
226 int items = 0, line_len, len;
227 char buf[256], *ptr1, *ptr2;
228
229 line_len = (int) strlen(line);
230 while ((line_len) && ((line[line_len-1] == '\n') || (line[line_len-1] == '\r') || (line[line_len-1] == ' ')))
231 line_len--;
232
233 len = spaces(line, line_len);
234 line += len;
235 line_len -= len;
236
237 if ((section == MPSCOLUMNS) || (section == MPSRHS) || (section == MPSRANGES)) {
238 field1[0] = '\0';
239 items++;
240 }
241 else {
242 len = lenfield(line, line_len);
243 if(line_len >= 1) { /* spaces or N/L/G/E or UP/LO */
244 strncpy(buf, line, len);
245 buf[len] = '\0';
246 sscanf(buf, "%s", field1);
247 items++;
248 }
249 else
250 field1[0] = '\0';
251
252 line += len;
253 line_len -= len;
254
255 len = spaces(line, line_len);
256 line += len;
257 line_len -= len;
258 }
259
260 len = lenfield(line, line_len);
261 if(line_len >= 1) { /* name */
262 strncpy(field2, line, len);
263 field2[len] = '\0';
264 items++;
265 }
266 else
267 field2[0] = '\0';
268
269 line += len;
270 line_len -= len;
271
272 len = spaces(line, line_len);
273 line += len;
274 line_len -= len;
275
276 len = lenfield(line, line_len);
277 if(line_len >= 1) { /* name */
278 strncpy(field3, line, len);
279 field3[len] = '\0';
280 items++;
281 }
282 else
283 field3[0] = '\0';
284
285 line += len;
286 line_len -= len;
287
288 len = spaces(line, line_len);
289 line += len;
290 line_len -= len;
291
292 if (*field3) {
293 if((section == MPSCOLUMNS) && (strcmp(field3, "'MARKER'") == 0)) {
294 *field4 = 0;
295 items++;
296 ptr1 = field3;
297 }
298 else {
299 *field4 = strtod(field3, &ptr1);
300 if(*ptr1 == 0) {
301 strcpy(field3, field2);
302 if ((section == MPSROWS) || (section == MPSBOUNDS) /* || (section == MPSSOS) */)
303 *field2 = 0;
304 else {
305 strcpy(field2, field1);
306 *field1 = 0;
307 }
308 items++;
309 }
310 else
311 ptr1 = NULL;
312 }
313 }
314 else ptr1 = NULL;
315
316 if(ptr1 == NULL) {
317 len = lenfield(line, line_len);
318 if(line_len >= 1) { /* number */
319 strncpy(buf, line, len);
320 buf[len] = '\0';
321 for(ptr1 = ptr2 = buf; ; ptr1++)
322 if(!isspace((unsigned char) *ptr1))
323 if((*(ptr2++) = *ptr1) == 0)
324 break;
325 /* *field4 = atof(buf); */
326 *field4 = strtod(buf, &ptr1);
327 if(*ptr1)
328 return(-1);
329 items++;
330 }
331 else
332 *field4 = 0;
333
334 line += len;
335 line_len -= len;
336
337 len = spaces(line, line_len);
338 line += len;
339 line_len -= len;
340 }
341
342 len = lenfield(line, line_len);
343 if(line_len >= 1) { /* name */
344 strncpy(field5, line, len);
345 field5[len] = '\0';
346 items++;
347 }
348 else
349 field5[0] = '\0';
350 line += len;
351 line_len -= len;
352
353 len = spaces(line, line_len);
354 line += len;
355 line_len -= len;
356
357 len = lenfield(line, line_len);
358 if(line_len >= 1) { /* number */
359 strncpy(buf, line, len);
360 buf[len] = '\0';
361 for(ptr1 = ptr2 = buf; ; ptr1++)
362 if(!isspace((unsigned char) *ptr1))
363 if((*(ptr2++) = *ptr1) == 0)
364 break;
365 /* *field6 = atof(buf); */
366 *field6 = strtod(buf, &ptr1);
367 if(*ptr1)
368 return(-1);
369 items++;
370 }
371 else
372 *field6 = 0;
373
374 if((section == MPSSOS) && (items == 2)) {
375 strcpy(field3, field2);
376 strcpy(field2, field1);
377 *field1 = 0;
378 }
379
380 if(section != MPSOBJNAME) {
381 for(ptr1 = field1; *ptr1; ptr1++)
382 *ptr1=(char)toupper(*ptr1);
383 }
384
385 return(items);
386 }
387
addmpscolumn(lprec * lp,MYBOOL Int_section,MYBOOL * Column_ready,int * count,REAL * Last_column,int * Last_columnno,char * Last_col_name)388 STATIC int addmpscolumn(lprec *lp, MYBOOL Int_section, MYBOOL *Column_ready,
389 int *count, REAL *Last_column, int *Last_columnno, char *Last_col_name)
390 {
391 int ok = TRUE;
392
393 if (*Column_ready) {
394 ok = add_columnex(lp, *count, Last_column, Last_columnno);
395 if (ok) {
396 ok = set_col_name(lp, lp->columns, Last_col_name);
397 }
398 if (ok)
399 set_int(lp, lp->columns, Int_section);
400 }
401 *Column_ready = FALSE;
402 *count = 0;
403 return(ok);
404 }
405
appendmpsitem(int * count,int rowIndex[],REAL rowValue[])406 STATIC MYBOOL appendmpsitem(int *count, int rowIndex[], REAL rowValue[])
407 {
408 int i = *count;
409
410 if(rowValue[i] == 0)
411 return( FALSE );
412
413 while((i > 0) && (rowIndex[i] < rowIndex[i-1])) {
414 swapINT (rowIndex+i, rowIndex+i-1);
415 swapREAL(rowValue+i, rowValue+i-1);
416 i--;
417 }
418 (*count)++;
419 return( TRUE );
420 }
421
MPS_readfile(lprec ** newlp,char * filename,int typeMPS,int verbose)422 MYBOOL MPS_readfile(lprec **newlp, char *filename, int typeMPS, int verbose)
423 {
424 MYBOOL status = FALSE;
425 FILE *fpin;
426
427 fpin = fopen(filename, "r");
428 if(fpin != NULL) {
429 status = MPS_readhandle(newlp, fpin, typeMPS, verbose);
430 fclose(fpin);
431 }
432 return( status );
433 }
434
MPS_input(void * fpin,char * buf,int max_size)435 static int __WINAPI MPS_input(void *fpin, char *buf, int max_size)
436 {
437 return(fgets(buf, max_size, (FILE *) fpin) != NULL);
438 }
439
MPS_readhandle(lprec ** newlp,FILE * filehandle,int typeMPS,int verbose)440 MYBOOL __WINAPI MPS_readhandle(lprec **newlp, FILE *filehandle, int typeMPS, int verbose)
441 {
442 return(MPS_readex(newlp, (void *) filehandle, MPS_input, typeMPS, verbose));
443 }
444
MPS_readex(lprec ** newlp,void * userhandle,read_modeldata_func read_modeldata,int typeMPS,int verbose)445 MYBOOL __WINAPI MPS_readex(lprec **newlp, void *userhandle, read_modeldata_func read_modeldata, int typeMPS, int verbose)
446 {
447 char field1[BUFSIZ], field2[BUFSIZ], field3[BUFSIZ], field5[BUFSIZ], line[BUFSIZ], tmp[BUFSIZ],
448 Last_col_name[BUFSIZ], probname[BUFSIZ], OBJNAME[BUFSIZ], *ptr;
449 int items, row, Lineno, var,
450 section = MPSUNDEF, variant = 0, NZ = 0, SOS = 0;
451 MYBOOL Int_section, Column_ready, Column_ready1,
452 Unconstrained_rows_found = FALSE, OF_found = FALSE, CompleteStatus = FALSE;
453 double field4, field6;
454 REAL *Last_column = NULL;
455 int count = 0, *Last_columnno = NULL;
456 int OBJSENSE = ROWTYPE_EMPTY;
457 lprec *lp;
458 int (*scan_line)(int section, char* line, char *field1, char *field2, char *field3,
459 double *field4, char *field5, double *field6);
460
461 if(newlp == NULL)
462 return( CompleteStatus );
463 else if(*newlp == NULL)
464 lp = make_lp(0, 0);
465 else
466 lp = *newlp;
467
468 switch(typeMPS) {
469 case MPSFIXED:
470 scan_line = scan_lineFIXED;
471 break;
472 case MPSFREE:
473 scan_line = scan_lineFREE;
474 break;
475 default:
476 report(lp, IMPORTANT, "MPS_readfile: Unrecognized MPS line type.\n");
477 delete_lp(lp);
478 return( CompleteStatus );
479 }
480
481 if (lp != NULL) {
482 lp->source_is_file = TRUE;
483 lp->verbose = verbose;
484 strcpy(Last_col_name, "");
485 strcpy(OBJNAME, "");
486 Int_section = FALSE;
487 Column_ready = FALSE;
488 Lineno = 0;
489
490 /* let's initialize line to all zero's */
491 MEMCLEAR(line, BUFSIZ);
492
493 while(read_modeldata(userhandle, line, BUFSIZ - 1)) {
494 Lineno++;
495
496 for(ptr = line; (*ptr) && (isspace((unsigned char) *ptr)); ptr++);
497
498 /* skip lines which start with "*", they are comment */
499 if((line[0] == '*') || (*ptr == 0) || (*ptr == '\n') || (*ptr == '\r')) {
500 report(lp, FULL, "Comment on line %d: %s", Lineno, line);
501 continue;
502 }
503
504 report(lp, FULL, "Line %6d: %s", Lineno, line);
505
506 /* first check for "special" lines: NAME, ROWS, BOUNDS .... */
507 /* this must start in the first position of line */
508 if(line[0] != ' ') {
509 sscanf(line, "%s", tmp);
510 if(strcmp(tmp, "NAME") == 0) {
511 section = MPSNAME;
512 *probname = 0;
513 sscanf(line, "NAME %s", probname);
514 if (!set_lp_name(lp, probname))
515 break;
516 }
517 else if((typeMPS == MPSFREE) && (strcmp(tmp, "OBJSENSE") == 0)) {
518 section = MPSOBJSENSE;
519 report(lp, FULL, "Switching to OBJSENSE section\n");
520 }
521 else if((typeMPS == MPSFREE) && (strcmp(tmp, "OBJNAME") == 0)) {
522 section = MPSOBJNAME;
523 report(lp, FULL, "Switching to OBJNAME section\n");
524 }
525 else if(strcmp(tmp, "ROWS") == 0) {
526 section = MPSROWS;
527 report(lp, FULL, "Switching to ROWS section\n");
528 }
529 else if(strcmp(tmp, "COLUMNS") == 0) {
530 allocREAL(lp, &Last_column, lp->rows + 1, TRUE);
531 allocINT(lp, &Last_columnno, lp->rows + 1, TRUE);
532 count = 0;
533 if ((Last_column == NULL) || (Last_columnno == NULL))
534 break;
535 section = MPSCOLUMNS;
536 report(lp, FULL, "Switching to COLUMNS section\n");
537 }
538 else if(strcmp(tmp, "RHS") == 0) {
539 if (!addmpscolumn(lp, Int_section, &Column_ready, &count, Last_column, Last_columnno, Last_col_name))
540 break;
541 section = MPSRHS;
542 report(lp, FULL, "Switching to RHS section\n");
543 }
544 else if(strcmp(tmp, "BOUNDS") == 0) {
545 section = MPSBOUNDS;
546 report(lp, FULL, "Switching to BOUNDS section\n");
547 }
548 else if(strcmp(tmp, "RANGES") == 0) {
549 section = MPSRANGES;
550 report(lp, FULL, "Switching to RANGES section\n");
551 }
552 else if((strcmp(tmp, "SOS") == 0) || (strcmp(tmp, "SETS") == 0)) {
553 section = MPSSOS;
554 if(strcmp(tmp, "SOS") == 0)
555 variant = 0;
556 else
557 variant = 1;
558 report(lp, FULL, "Switching to %s section\n", tmp);
559 }
560 else if(strcmp(tmp, "ENDATA") == 0) {
561 report(lp, FULL, "Finished reading MPS file\n");
562 CompleteStatus = TRUE;
563 break;
564 }
565 else { /* line does not start with space and does not match above */
566 report(lp, IMPORTANT, "Unrecognized MPS line %d: %s\n", Lineno, line);
567 break;
568 }
569 }
570 else { /* normal line, process */
571 items = scan_line(section, line, field1, field2, field3, &field4, field5, &field6);
572 if(items < 0){
573 report(lp, IMPORTANT, "Syntax error on line %d: %s\n", Lineno, line);
574 break;
575 }
576
577 switch(section) {
578
579 case MPSNAME:
580 report(lp, IMPORTANT, "Error, extra line under NAME line\n");
581 break;
582
583 case MPSOBJSENSE:
584 if(OBJSENSE != ROWTYPE_EMPTY) {
585 report(lp, IMPORTANT, "Error, extra line under OBJSENSE line\n");
586 break;
587 }
588 if((strcmp(field1, "MAXIMIZE") == 0) || (strcmp(field1, "MAX") == 0)) {
589 OBJSENSE = ROWTYPE_OFMAX;
590 set_maxim(lp);
591 }
592 else if((strcmp(field1, "MINIMIZE") == 0) || (strcmp(field1, "MIN") == 0)) {
593 OBJSENSE = ROWTYPE_OFMIN;
594 set_minim(lp);
595 }
596 else {
597 report(lp, SEVERE, "Unknown OBJSENSE direction '%s' on line %d\n", field1, Lineno);
598 break;
599 }
600 continue;
601
602 case MPSOBJNAME:
603 if(*OBJNAME) {
604 report(lp, IMPORTANT, "Error, extra line under OBJNAME line\n");
605 break;
606 }
607 strcpy(OBJNAME, field1);
608 continue;
609
610 /* Process entries in the ROWS section */
611 case MPSROWS:
612 /* field1: rel. operator; field2: name of constraint */
613
614 report(lp, FULL, "Row %5d: %s %s\n", lp->rows + 1, field1, field2);
615
616 if(strcmp(field1, "N") == 0) {
617 if((*OBJNAME) && (strcmp(field2, OBJNAME)))
618 /* Ignore this objective name since it is not equal to the OBJNAME name */;
619 else if(!OF_found) { /* take the first N row as OF, ignore others */
620 if (!set_row_name(lp, 0, field2))
621 break;
622 OF_found = TRUE;
623 }
624 else if(!Unconstrained_rows_found) {
625 report(lp, IMPORTANT, "Unconstrained row %s ignored\n", field2);
626 report(lp, IMPORTANT, "Further messages of this kind will be suppressed\n");
627 Unconstrained_rows_found = TRUE;
628 }
629 }
630 else if(strcmp(field1, "L") == 0) {
631 if ((!str_add_constraint(lp, "" ,LE ,0)) || (!set_row_name(lp, lp->rows, field2)))
632 break;
633 }
634 else if(strcmp(field1, "G") == 0) {
635 if ((!str_add_constraint(lp, "" ,GE ,0)) || (!set_row_name(lp, lp->rows, field2)))
636 break;
637 }
638 else if(strcmp(field1, "E") == 0) {
639 if ((!str_add_constraint(lp, "",EQ ,0)) || (!set_row_name(lp, lp->rows, field2)))
640 break;
641 }
642 else {
643 report(lp, SEVERE, "Unknown relation code '%s' on line %d\n", field1, Lineno);
644 break;
645 }
646
647 continue;
648
649 /* Process entries in the COLUMNS section */
650 case MPSCOLUMNS:
651 /* field2: variable; field3: constraint; field4: coef */
652 /* optional: field5: constraint; field6: coef */
653
654 report(lp, FULL, "Column %4d: %s %s %g %s %g\n",
655 lp->columns + 1, field2, field3, field4, field5, field6);
656
657 if((items == 4) || (items == 5) || (items == 6)) {
658 if (NZ == 0)
659 strcpy(Last_col_name, field2);
660 else if(*field2) {
661 Column_ready1 = (MYBOOL) (strcmp(field2, Last_col_name) != 0);
662 if(Column_ready1) {
663 if (find_var(lp, field2, FALSE) >= 0) {
664 report(lp, SEVERE, "Variable name (%s) is already used!\n", field2);
665 break;
666 }
667
668 if(Column_ready) { /* Added ability to handle non-standard "same as above" column name */
669 if (addmpscolumn(lp, Int_section, &Column_ready, &count, Last_column, Last_columnno, Last_col_name)) {
670 strcpy(Last_col_name, field2);
671 NZ = 0;
672 }
673 else
674 break;
675 }
676 }
677 }
678 if(items == 5) { /* there might be an INTEND or INTORG marker */
679 /* look for " <name> 'MARKER' 'INTORG'"
680 or " <name> 'MARKER' 'INTEND'" */
681 if(strcmp(field3, "'MARKER'") != 0)
682 break;
683 if(strcmp(field5, "'INTORG'") == 0) {
684 Int_section = TRUE;
685 report(lp, FULL, "Switching to integer section\n");
686 }
687 else if(strcmp(field5, "'INTEND'") == 0) {
688 Int_section = FALSE;
689 report(lp, FULL, "Switching to non-integer section\n");
690 }
691 else
692 report(lp, IMPORTANT, "Unknown marker (ignored) at line %d: %s\n",
693 Lineno, field5);
694 }
695 else if((row = find_row(lp, field3, Unconstrained_rows_found)) >= 0) {
696 if(row > lp->rows)
697 report(lp, CRITICAL, "Invalid row %s encountered in the MPS file\n", field3);
698 Last_columnno[count] = row;
699 Last_column[count] = (REAL)field4;
700 if(appendmpsitem(&count, Last_columnno, Last_column)) {
701 NZ++;
702 Column_ready = TRUE;
703 }
704 }
705 }
706 if(items == 6) {
707 if((row = find_row(lp, field5, Unconstrained_rows_found)) >= 0) {
708 if(row > lp->rows)
709 report(lp, CRITICAL, "Invalid row %s encountered in the MPS file\n", field5);
710 Last_columnno[count] = row;
711 Last_column[count] = (REAL)field6;
712 if(appendmpsitem(&count, Last_columnno, Last_column)) {
713 NZ++;
714 Column_ready = TRUE;
715 }
716 }
717 }
718
719 if((items < 4) || (items > 6)) { /* Wrong! */
720 report(lp, CRITICAL, "Wrong number of items (%d) in COLUMNS section (line %d)\n",
721 items, Lineno);
722 break;
723 }
724
725 continue;
726
727 /* Process entries in the RHS section */
728 /* field2: uninteresting name; field3: constraint name */
729 /* field4: value */
730 /* optional: field5: constraint name; field6: value */
731 case MPSRHS:
732
733 report(lp, FULL, "RHS line: %s %s %g %s %g\n",
734 field2, field3, field4, field5, field6);
735
736 if((items != 4) && (items != 6)) {
737 report(lp, CRITICAL, "Wrong number of items (%d) in RHS section line %d\n",
738 items, Lineno);
739 break;
740 }
741
742 if((row = find_row(lp, field3, Unconstrained_rows_found)) >= 0) {
743 set_rh(lp, row, (REAL)field4);
744 }
745
746 if(items == 6) {
747 if((row = find_row(lp, field5, Unconstrained_rows_found)) >= 0) {
748 set_rh(lp, row, (REAL)field6);
749 }
750 }
751
752 continue;
753
754 /* Process entries in the BOUNDS section */
755 /* field1: bound type; field2: uninteresting name; */
756 /* field3: variable name; field4: value */
757 case MPSBOUNDS:
758
759 report(lp, FULL, "BOUNDS line: %s %s %s %g\n",
760 field1, field2, field3, field4);
761
762 var = find_var(lp, field3, FALSE);
763 if(var < 0){ /* bound on undefined var in COLUMNS section ... */
764 Column_ready = TRUE;
765 if (!addmpscolumn(lp, FALSE, &Column_ready, &count, Last_column, Last_columnno, field3))
766 break;
767 Column_ready = TRUE;
768 var = find_var(lp, field3, TRUE);
769 }
770 if(var < 0) /* undefined var and could add ... */;
771 else if(strcmp(field1, "UP") == 0) {
772 /* upper bound */
773 if(!set_bounds(lp, var, get_lowbo(lp, var), field4))
774 break;
775 }
776 else if(strcmp(field1, "SC") == 0) {
777 /* upper bound */
778 if(field4 == 0)
779 field4 = lp->infinite;
780 if(!set_bounds(lp, var, get_lowbo(lp, var), field4))
781 break;
782 set_semicont(lp, var, TRUE);
783 }
784 else if(strcmp(field1, "SI") == 0) {
785 /* upper bound */
786 if(field4 == 0)
787 field4 = lp->infinite;
788 if(!set_bounds(lp, var, get_lowbo(lp, var), field4))
789 break;
790 set_int(lp, var, TRUE);
791 set_semicont(lp, var, TRUE);
792 }
793 else if(strcmp(field1, "LO") == 0) {
794 /* lower bound */
795 if(!set_bounds(lp, var, field4, get_upbo(lp, var)))
796 break;
797 }
798 else if(strcmp(field1, "PL") == 0) { /* plus-ranged variable */
799 if(!set_bounds(lp, var, get_lowbo(lp, var), lp->infinite))
800 break;
801 }
802 else if(strcmp(field1, "MI") == 0) { /* minus-ranged variable */
803 if(!set_bounds(lp, var, -lp->infinite, get_upbo(lp, var)))
804 break;
805 }
806 else if(strcmp(field1, "FR") == 0) { /* free variable */
807 set_unbounded(lp, var);
808 }
809 else if(strcmp(field1, "FX") == 0) {
810 /* fixed, upper _and_ lower */
811 if(!set_bounds(lp, var, field4, field4))
812 break;
813 }
814 else if(strcmp(field1, "BV") == 0) { /* binary variable */
815 set_binary(lp, var, TRUE);
816 }
817 /* AMPL bounds type UI and LI added by E.Imamura (CRIEPI) */
818 else if(strcmp(field1, "UI") == 0) { /* upper bound for integer variable */
819 if(!set_bounds(lp, var, get_lowbo(lp, var), field4))
820 break;
821 set_int(lp, var, TRUE);
822 }
823 else if(strcmp(field1, "LI") == 0) { /* lower bound for integer variable - corrected by KE */
824 if(!set_bounds(lp, var, field4, get_upbo(lp, var)))
825 break;
826 set_int(lp, var, TRUE);
827 }
828 else {
829 report(lp, CRITICAL, "BOUND type %s on line %d is not supported",
830 field1, Lineno);
831 break;
832 }
833
834 continue;
835
836 /* Process entries in the BOUNDS section */
837
838 /* We have to implement the following semantics:
839
840 D. The RANGES section is for constraints of the form: h <=
841 constraint <= u . The range of the constraint is r = u - h . The
842 value of r is specified in the RANGES section, and the value of u or
843 h is specified in the RHS section. If b is the value entered in the
844 RHS section, and r is the value entered in the RANGES section, then
845 u and h are thus defined:
846
847 row type sign of r h u
848 ----------------------------------------------
849 G + or - b b + |r|
850 L + or - b - |r| b
851 E + b b + |r|
852 E - b - |r| b */
853
854 /* field2: uninteresting name; field3: constraint name */
855 /* field4: value */
856 /* optional: field5: constraint name; field6: value */
857
858 case MPSRANGES:
859
860 report(lp, FULL, "RANGES line: %s %s %g %s %g",
861 field2, field3, field4, field5, field6);
862
863 if((items != 4) && (items != 6)) {
864 report(lp, CRITICAL, "Wrong number of items (%d) in RANGES section line %d",
865 items, Lineno);
866 break;
867 }
868
869 if((row = find_row(lp, field3, Unconstrained_rows_found)) >= 0) {
870 /* Determine constraint type */
871
872 if(fabs(field4) >= lp->infinite) {
873 report(lp, IMPORTANT,
874 "Warning, Range for row %s >= infinity (value %g) on line %d, ignored",
875 field3, field4, Lineno);
876 }
877 else if(field4 == 0) {
878 /* Change of a GE or LE to EQ */
879 if(lp->orig_upbo[row] != 0)
880 set_constr_type(lp, row, EQ);
881 }
882 else if(is_chsign(lp, row)) {
883 /* GE */
884 lp->orig_upbo[row] = fabs(field4);
885 }
886 else if((lp->orig_upbo[row] == 0) && (field4 >= 0)) {
887 /* EQ with positive sign of r value */
888 set_constr_type(lp, row, GE);
889 lp->orig_upbo[row] = field4;
890 }
891 else if(lp->orig_upbo[row] == lp->infinite) {
892 /* LE */
893 lp->orig_upbo[row] = fabs(field4);
894 }
895 else if((lp->orig_upbo[row] == 0) && (field4 < 0)) {
896 /* EQ with negative sign of r value */
897 set_constr_type(lp, row, LE);
898 lp->orig_upbo[row] = my_flipsign(field4);
899 }
900 else { /* let's be paranoid */
901 report(lp, IMPORTANT,
902 "Cannot figure out row type, row = %d, is_chsign = %d, upbo = %g on line %d",
903 row, is_chsign(lp, row), (double)lp->orig_upbo[row], Lineno);
904 }
905 }
906
907 if(items == 6) {
908 if((row = find_row(lp, field5, Unconstrained_rows_found)) >= 0) {
909 /* Determine constraint type */
910
911 if(fabs(field6) >= lp->infinite) {
912 report(lp, IMPORTANT,
913 "Warning, Range for row %s >= infinity (value %g) on line %d, ignored",
914 field5, field6, Lineno);
915 }
916 else if(field6 == 0) {
917 /* Change of a GE or LE to EQ */
918 if(lp->orig_upbo[row] != 0)
919 set_constr_type(lp, row, EQ);
920 }
921 else if(is_chsign(lp, row)) {
922 /* GE */
923 lp->orig_upbo[row] = fabs(field6);
924 }
925 else if(lp->orig_upbo[row] == 0 && field6 >= 0) {
926 /* EQ with positive sign of r value */
927 set_constr_type(lp, row, GE);
928 lp->orig_upbo[row] = field6;
929 }
930 else if(lp->orig_upbo[row] == lp->infinite) {
931 /* LE */
932 lp->orig_upbo[row] = fabs(field6);
933 }
934 else if((lp->orig_upbo[row] == 0) && (field6 < 0)) {
935 /* EQ with negative sign of r value */
936 set_constr_type(lp, row, LE);
937 lp->orig_upbo[row] = my_flipsign(field6);
938 }
939 else { /* let's be paranoid */
940 report(lp, IMPORTANT,
941 "Cannot figure out row type, row = %d, is_chsign = %d, upbo = %g on line %d",
942 row, is_chsign(lp,row), (double) lp->orig_upbo[row], Lineno);
943 }
944 }
945 }
946
947 continue;
948
949 /* Process entries in the SOS section */
950
951 /* We have to implement the following semantics:
952
953 E. The SOS section is for ordered variable sets of the form:
954 x1, x2, x3 ... xn where only a given number of consequtive variables
955 may be non-zero. Each set definition is prefaced by type, name
956 and priority data. Each set member has an optional weight that
957 determines its order. There are two forms supported; a full format
958 and a reduced CPLEX-like format. */
959
960 case MPSSOS:
961 report(lp, FULL, "SOS line: %s %s %g %s %g",
962 field2, field3, field4, field5, field6);
963
964 if((items == 0) || (items > 4)) {
965 report(lp, IMPORTANT,
966 "Invalid number of items (%d) in SOS section line %d\n",
967 items, Lineno);
968 break;
969 }
970
971 if(strlen(field1) == 0) items--; /* fix scanline anomoly! */
972
973 /* Check if this is the start of a new SOS */
974 if(items == 1 || items == 4) {
975 row = (int) (field1[1] - '0');
976 if((row <= 0) || (row > 9)) {
977 report(lp, IMPORTANT,
978 "Error: Invalid SOS type %s line %d\n", field1, Lineno);
979 break;
980 }
981 field1[0] = '\0'; /* fix scanline anomoly! */
982
983 /* lp_solve needs a name for the SOS */
984 if(variant == 0) {
985 if(strlen(field3) == 0) /* CPLEX format does not provide a SOS name; create one */
986 sprintf(field3, "SOS_%d", SOS_count(lp) + 1);
987 }
988 else { /* Remap XPRESS format name */
989 strcpy(field3, field1);
990 }
991 /* Obtain the SOS priority */
992 if(items == 4)
993 SOS = (int) field4;
994 else
995 SOS = 1;
996
997 /* Define a new SOS instance */
998
999 SOS = add_SOS(lp, field3, (int) row, SOS, 0, NULL, NULL);
1000 }
1001 /* Otherwise, add set members to the active SOS */
1002 else {
1003 char *field = (items == 3) ? field3 /* Native lp_solve and XPRESS formats */ : field2 /* CPLEX format */;
1004
1005 var = find_var(lp, field, FALSE); /* Native lp_solve and XPRESS formats */
1006 if(var < 0){ /* SOS on undefined var in COLUMNS section ... */
1007 Column_ready = TRUE;
1008 if (!addmpscolumn(lp, FALSE, &Column_ready, &count, Last_column, Last_columnno, field))
1009 break;
1010 Column_ready = TRUE;
1011 var = find_var(lp, field, TRUE);
1012 }
1013 if((var < 0) || (SOS < 1)) /* undefined var and could add ... */;
1014 else append_SOSrec(lp->SOS->sos_list[SOS-1], 1, &var, &field4);
1015 }
1016
1017 continue;
1018 }
1019
1020 /* If we got here there was an error "upstream" */
1021 report(lp, IMPORTANT,
1022 "Error: Cannot handle line %d\n", Lineno);
1023 break;
1024 }
1025 }
1026
1027 if((*OBJNAME) && (!OF_found)) {
1028 report(lp, IMPORTANT,
1029 "Error: Objective function specified by OBJNAME card not found\n");
1030 CompleteStatus = FALSE;
1031 }
1032
1033 if(CompleteStatus == FALSE)
1034 delete_lp(lp);
1035 else
1036 *newlp = lp;
1037 if(Last_column != NULL)
1038 FREE(Last_column);
1039 if(Last_columnno != NULL)
1040 FREE(Last_columnno);
1041 }
1042
1043 return( CompleteStatus );
1044 }
1045
number(char * str,REAL value)1046 static void number(char *str,REAL value)
1047 {
1048 char __str[80], *_str;
1049 int i;
1050
1051 /* sprintf(_str,"%12.6G",value); */
1052 _str=__str+2;
1053 if (value>=0.0)
1054 if ((value!=0.0) && ((value>0.99999999e12) || (value<0.0001))) {
1055 int n=15;
1056
1057 do {
1058 n--;
1059 i=sprintf(_str,"%*.*E",n,n-6,(double) value);
1060 if (i>12) {
1061 char *ptr=strchr(_str,'E');
1062
1063 if (ptr!=NULL) {
1064 if (*(++ptr)=='-') ptr++;
1065 while ((i>12) && ((*ptr=='+') || (*ptr=='0'))) {
1066 strcpy(ptr,ptr+1);
1067 i--;
1068 }
1069 }
1070 }
1071 } while (i>12);
1072 }
1073 else if (value>=1.0e10) {
1074 int n=13;
1075
1076 do {
1077 i=sprintf(_str,"%*.0f",--n,(double) value);
1078 } while (i>12);
1079 }
1080 else {
1081 if (((i=sprintf(_str,"%12.10f",(double) value))>12) && (_str[12]>='5')) {
1082 for (i=11;i>=0;i--)
1083 if (_str[i]!='.') {
1084 if (++_str[i]>'9') _str[i]='0';
1085 else break;
1086 }
1087 if (i<0) {
1088 *(--_str)='1';
1089 *(--_str)=' ';
1090 }
1091 }
1092 }
1093 else
1094 if ((value<-0.99999999e11) || (value>-0.0001)) {
1095 int n=15;
1096
1097 do {
1098 n--;
1099 i=sprintf(_str,"%*.*E",n,n-7,(double) value);
1100 if (i>12) {
1101 char *ptr=strchr(_str,'E');
1102
1103 if (ptr!=NULL) {
1104 if (*(++ptr)=='-') ptr++;
1105 while ((i>12) && ((*ptr=='+') || (*ptr=='0'))) {
1106 strcpy(ptr,ptr+1);
1107 i--;
1108 }
1109 }
1110 }
1111 } while (i>12);
1112 }
1113 else if (value<=-1.0e9) {
1114 int n=13;
1115
1116 do {
1117 i=sprintf(_str,"%*.0f",--n,(double) value);
1118 } while (i>12);
1119 }
1120 else
1121 if (((i=sprintf(_str,"%12.9f",(double) value))>12) && (_str[12]>='5')) {
1122 for (i=11;i>=1;i--)
1123 if (_str[i]!='.') {
1124 if (++_str[i]>'9') _str[i]='0';
1125 else break;
1126 }
1127 if (i<1) {
1128 *_str='1';
1129 *(--_str)='-';
1130 *(--_str)=' ';
1131 }
1132 }
1133 _str[12] ='\0'; strcpy(str, _str);
1134 // strncpy(str,_str,12); str[12] = '\0';
1135 }
1136
1137 static char numberbuffer[15];
1138
formatnumber12(double a)1139 static char *formatnumber12(double a)
1140 {
1141 #if 0
1142 return(sprintf(numberbuffer, "%12g", a));
1143 #else
1144 number(numberbuffer, a);
1145 return(numberbuffer);
1146 #endif
1147 }
1148
MPSnameFIXED(char * name)1149 STATIC char *MPSnameFIXED(char *name)
1150 {
1151 static char name0[9];
1152
1153 sprintf(name0, "%-8.8s", name);
1154 return(name0);
1155 }
1156
MPSnameFREE(char * name)1157 STATIC char *MPSnameFREE(char *name)
1158 {
1159 if(strlen(name) < 8)
1160 return(MPSnameFIXED(name));
1161 else
1162 return(name);
1163 }
1164
write_data(void * userhandle,write_modeldata_func write_modeldata,char * format,...)1165 static void write_data(void *userhandle, write_modeldata_func write_modeldata, char *format, ...)
1166 {
1167 char buff[DEF_STRBUFSIZE+1];
1168 va_list ap;
1169
1170 va_start(ap, format);
1171 vsnprintf(buff, DEF_STRBUFSIZE, format, ap);
1172 write_modeldata(userhandle, buff);
1173 va_end(ap);
1174 }
1175
MPS_writefileex(lprec * lp,int typeMPS,void * userhandle,write_modeldata_func write_modeldata)1176 MYBOOL MPS_writefileex(lprec *lp, int typeMPS, void *userhandle, write_modeldata_func write_modeldata)
1177 {
1178 int i, j, jj, je, k, marker, putheader, ChangeSignObj = FALSE, *idx, *idx1;
1179 MYBOOL ok = TRUE, names_used;
1180 REAL a, *val, *val1;
1181 /* "Output" not defined. Excluded April 19 2006 SEB. */
1182 /* FILE *output = stdout; */
1183 char * (*MPSname)(char *name);
1184
1185 if(lp->matA->is_roworder) {
1186 report(lp, IMPORTANT, "MPS_writefile: Cannot write to MPS file while in row entry mode.\n");
1187 return(FALSE);
1188 }
1189
1190 switch(typeMPS) {
1191 case MPSFIXED:
1192 MPSname = MPSnameFIXED;
1193 ChangeSignObj = is_maxim(lp);
1194 break;
1195 case MPSFREE:
1196 MPSname = MPSnameFREE;
1197 break;
1198 default:
1199 report(lp, IMPORTANT, "MPS_writefile: unrecognized MPS name type.\n");
1200 return(FALSE);
1201 }
1202
1203 names_used = lp->names_used;
1204
1205 if(typeMPS == MPSFIXED) {
1206 /* Check if there is no variable name where the first 8 charachters are equal to the first 8 characters of anothe variable */
1207 if(names_used)
1208 for(i = 1; (i <= lp->columns) && (ok); i++)
1209 if((lp->col_name[i] != NULL) && (lp->col_name[i]->name != NULL) && (!is_splitvar(lp, i)) && (strlen(lp->col_name[i]->name) > 8))
1210 for(j = 1; (j < i) && (ok); j++)
1211 if((lp->col_name[j] != NULL) && (lp->col_name[j]->name != NULL) && (!is_splitvar(lp, j)))
1212 if(strncmp(lp->col_name[i]->name, lp->col_name[j]->name, 8) == 0)
1213 ok = FALSE;
1214 }
1215
1216 if(!ok) {
1217 lp->names_used = FALSE;
1218 ok = TRUE;
1219 }
1220 marker = 0;
1221
1222 /* First write metadata in structured comment form (lp_solve style) */
1223 write_data(userhandle, write_modeldata, "*<meta creator='lp_solve v%d.%d'>\n",
1224 (int) MAJORVERSION, (int) MINORVERSION);
1225 write_data(userhandle, write_modeldata, "*<meta rows=%d>\n", lp->rows);
1226 write_data(userhandle, write_modeldata, "*<meta columns=%d>\n", lp->columns);
1227 write_data(userhandle, write_modeldata, "*<meta equalities=%d>\n", lp->equalities);
1228 if(SOS_count(lp) > 0)
1229 write_data(userhandle, write_modeldata, "*<meta SOS=%d>\n", SOS_count(lp));
1230 write_data(userhandle, write_modeldata, "*<meta integers=%d>\n", lp->int_vars);
1231 if(lp->sc_vars > 0)
1232 write_data(userhandle, write_modeldata, "*<meta scvars=%d>\n", lp->sc_vars);
1233 write_data(userhandle, write_modeldata, "*<meta origsense='%s'>\n", (is_maxim(lp) ? "MAX" : "MIN"));
1234 write_data(userhandle, write_modeldata, "*\n");
1235
1236 /* Write the MPS content */
1237 write_data(userhandle, write_modeldata, "NAME %s\n", MPSname(get_lp_name(lp)));
1238 if((typeMPS == MPSFREE) && (is_maxim(lp)))
1239 write_data(userhandle, write_modeldata, "OBJSENSE\n MAX\n");
1240 write_data(userhandle, write_modeldata, "ROWS\n");
1241 for(i = 0; i <= lp->rows; i++) {
1242 if(i == 0)
1243 write_data(userhandle, write_modeldata, " N ");
1244 else if(lp->orig_upbo[i] != 0) {
1245 if(is_chsign(lp,i))
1246 write_data(userhandle, write_modeldata, " G ");
1247 else
1248 write_data(userhandle, write_modeldata, " L ");
1249 }
1250 else
1251 write_data(userhandle, write_modeldata, " E ");
1252 write_data(userhandle, write_modeldata, "%s\n", MPSname(get_row_name(lp, i)));
1253 }
1254
1255 allocREAL(lp, &val, 1 + lp->rows, TRUE);
1256 allocINT(lp, &idx, 1 + lp->rows, TRUE);
1257 write_data(userhandle, write_modeldata, "COLUMNS\n");
1258 for(i = 1; i <= lp->columns; i++) {
1259 if(!is_splitvar(lp, i)) {
1260 if(is_int(lp,i) && (marker % 2) == 0) {
1261 write_data(userhandle, write_modeldata, " MARK%04d 'MARKER' 'INTORG'\n",
1262 marker);
1263 marker++;
1264 }
1265 if(!is_int(lp,i) && (marker % 2) == 1) {
1266 write_data(userhandle, write_modeldata, " MARK%04d 'MARKER' 'INTEND'\n",
1267 marker);
1268 marker++;
1269 }
1270
1271 /* Loop over non-zero column entries */
1272 je = get_columnex(lp, i, val, idx);
1273 for(k = 1, val1 = val, idx1 = idx, jj = 0; jj < je; jj++) {
1274 k = 1 - k;
1275 j = *(idx1++);
1276 a = *(val1++);
1277 if (k == 0) {
1278 write_data(userhandle, write_modeldata, " %s",
1279 MPSname(get_col_name(lp, i)));
1280 write_data(userhandle, write_modeldata, " %s %s",
1281 MPSname(get_row_name(lp, j)),
1282 /* formatnumber12((double) a)); */
1283 formatnumber12((double) (a * (j == 0 && ChangeSignObj ? -1 : 1))));
1284 }
1285 else
1286 write_data(userhandle, write_modeldata, " %s %s\n",
1287 MPSname(get_row_name(lp, j)),
1288 formatnumber12((double) (a * (j == 0 && ChangeSignObj ? -1 : 1))));
1289 /* formatnumber12((double) a)); */
1290 }
1291 if(k == 0)
1292 write_data(userhandle, write_modeldata, "\n");
1293 }
1294 }
1295 if((marker % 2) == 1) {
1296 write_data(userhandle, write_modeldata, " MARK%04d 'MARKER' 'INTEND'\n",
1297 marker);
1298 /* marker++; */ /* marker not used after this */
1299 }
1300 FREE(idx);
1301 FREE(val);
1302
1303 write_data(userhandle, write_modeldata, "RHS\n");
1304 for(k = 1, i = 0; i <= lp->rows; i++) {
1305 a = lp->orig_rhs[i];
1306 if(a) {
1307 a = unscaled_value(lp, a, i);
1308 if((i == 0) || is_chsign(lp, i))
1309 a = my_flipsign(a);
1310 k = 1 - k;
1311 if(k == 0)
1312 write_data(userhandle, write_modeldata, " RHS %s %s",
1313 MPSname(get_row_name(lp, i)),
1314 formatnumber12((double)a));
1315 else
1316 write_data(userhandle, write_modeldata, " %s %s\n",
1317 MPSname(get_row_name(lp, i)),
1318 formatnumber12((double)a));
1319 }
1320 }
1321 if(k == 0)
1322 write_data(userhandle, write_modeldata, "\n");
1323
1324 putheader = TRUE;
1325 for(k = 1, i = 1; i <= lp->rows; i++){
1326 a = 0;
1327 if((lp->orig_upbo[i] < lp->infinite) && (lp->orig_upbo[i] != 0.0))
1328 a = lp->orig_upbo[i];
1329 if(a) {
1330 if(putheader) {
1331 write_data(userhandle, write_modeldata, "RANGES\n");
1332 putheader = FALSE;
1333 }
1334 a = unscaled_value(lp, a, i);
1335 k = 1 - k;
1336 if(k == 0)
1337 write_data(userhandle, write_modeldata, " RGS %s %s",
1338 MPSname(get_row_name(lp, i)),
1339 formatnumber12((double)a));
1340 else
1341 write_data(userhandle, write_modeldata, " %s %s\n",
1342 MPSname(get_row_name(lp, i)),
1343 formatnumber12((double)a));
1344 }
1345 }
1346 if(k == 0)
1347 write_data(userhandle, write_modeldata, "\n");
1348
1349 putheader = TRUE;
1350 for(i = lp->rows + 1; i <= lp->sum; i++)
1351 if(!is_splitvar(lp, i - lp->rows)) {
1352 j = i - lp->rows;
1353 if((lp->orig_lowbo[i] != 0) && (lp->orig_upbo[i] < lp->infinite) &&
1354 (lp->orig_lowbo[i] == lp->orig_upbo[i])) {
1355 a = lp->orig_upbo[i];
1356 a = unscaled_value(lp, a, i);
1357 if(putheader) {
1358 write_data(userhandle, write_modeldata, "BOUNDS\n");
1359 putheader = FALSE;
1360 }
1361 write_data(userhandle, write_modeldata, " FX BND %s %s\n",
1362 MPSname(get_col_name(lp, j)),
1363 formatnumber12((double)a));
1364 }
1365 else if(is_binary(lp, j)) {
1366 if(putheader) {
1367 write_data(userhandle, write_modeldata, "BOUNDS\n");
1368 putheader = FALSE;
1369 }
1370 write_data(userhandle, write_modeldata, " BV BND %s\n",
1371 MPSname(get_col_name(lp, j)));
1372 }
1373 else if(is_unbounded(lp, j)) {
1374 if(putheader) {
1375 write_data(userhandle, write_modeldata, "BOUNDS\n");
1376 putheader = FALSE;
1377 }
1378 write_data(userhandle, write_modeldata, " FR BND %s\n",
1379 MPSname(get_col_name(lp, j)));
1380 }
1381 else {
1382 if(lp->orig_upbo[i] < lp->infinite) {
1383 a = lp->orig_upbo[i];
1384 a = unscaled_value(lp, a, i);
1385 if(putheader) {
1386 write_data(userhandle, write_modeldata, "BOUNDS\n");
1387 putheader = FALSE;
1388 }
1389 if(is_semicont(lp, j)) {
1390 if(is_int(lp, j))
1391 write_data(userhandle, write_modeldata, " SI BND %s %s\n",
1392 MPSname(get_col_name(lp, j)),
1393 formatnumber12((double)a));
1394 else
1395 write_data(userhandle, write_modeldata, " SC BND %s %s\n",
1396 MPSname(get_col_name(lp, j)),
1397 formatnumber12((double)a));
1398 }
1399 else
1400 write_data(userhandle, write_modeldata, " UP BND %s %s\n",
1401 MPSname(get_col_name(lp, j)),
1402 formatnumber12((double)a));
1403 }
1404 if(lp->orig_lowbo[i] != 0) {
1405 a = lp->orig_lowbo[i];
1406 a = unscaled_value(lp, a, i);
1407 if(putheader) {
1408 write_data(userhandle, write_modeldata, "BOUNDS\n");
1409 putheader = FALSE;
1410 }
1411 if(lp->orig_lowbo[i] != -lp->infinite)
1412 write_data(userhandle, write_modeldata, " LO BND %s %s\n",
1413 MPSname(get_col_name(lp, j)),
1414 formatnumber12((double)a));
1415 else
1416 write_data(userhandle, write_modeldata, " MI BND %s\n",
1417 MPSname(get_col_name(lp, j)));
1418 }
1419 }
1420 }
1421
1422 /* Write optional SOS section */
1423 putheader = TRUE;
1424 for(i = 0; i < SOS_count(lp); i++) {
1425 SOSgroup *SOS = lp->SOS;
1426
1427 if(putheader) {
1428 write_data(userhandle, write_modeldata, "SOS\n");
1429 putheader = FALSE;
1430 }
1431 write_data(userhandle, write_modeldata, " S%1d SOS %s %s\n",
1432 SOS->sos_list[i]->type,
1433 MPSname(SOS->sos_list[i]->name),
1434 formatnumber12((double) SOS->sos_list[i]->priority));
1435 for(j = 1; j <= SOS->sos_list[i]->size; j++) {
1436 write_data(userhandle, write_modeldata, " SOS %s %s\n",
1437 MPSname(get_col_name(lp, SOS->sos_list[i]->members[j])),
1438 formatnumber12((double) SOS->sos_list[i]->weights[j]));
1439 }
1440 }
1441
1442 write_data(userhandle, write_modeldata, "ENDATA\n");
1443
1444 lp->names_used = names_used;
1445
1446 return(ok);
1447 }
1448
write_lpdata(void * userhandle,char * buf)1449 static int __WINAPI write_lpdata(void *userhandle, char *buf)
1450 {
1451 fputs(buf, (FILE *) userhandle);
1452 return(TRUE);
1453 }
1454
MPS_writefile(lprec * lp,int typeMPS,char * filename)1455 MYBOOL MPS_writefile(lprec *lp, int typeMPS, char *filename)
1456 {
1457 FILE *output; /* = stdout; */
1458 MYBOOL ok;
1459
1460 ok = ((output = fopen(filename, "w")) != NULL);
1461 if(!ok)
1462 return(ok);
1463
1464 ok = MPS_writefileex(lp, typeMPS, (void *) output, write_lpdata);
1465
1466 fclose(output);
1467
1468 return(ok);
1469 }
1470
MPS_writehandle(lprec * lp,int typeMPS,FILE * output)1471 MYBOOL MPS_writehandle(lprec *lp, int typeMPS, FILE *output)
1472 {
1473 MYBOOL ok;
1474
1475 set_outputstream(lp, output);
1476
1477 output = lp->outstream;
1478
1479 ok = MPS_writefileex(lp, typeMPS, (void *) output, write_lpdata);
1480
1481 return(ok);
1482 }
1483
1484
1485 /* Read and write BAS files */
1486 /* #define OldNameMatch */
1487 #ifdef OldNameMatch
MPS_getnameidx(lprec * lp,char * varname,MYBOOL isrow)1488 static int MPS_getnameidx(lprec *lp, char *varname, MYBOOL isrow)
1489 {
1490 int in = -1;
1491
1492 in = get_nameindex(lp, varname, isrow);
1493 if((in < 0) && (strncmp(varname, (isrow ? ROWNAMEMASK : COLNAMEMASK), 1) == 0)) {
1494 if(sscanf(varname + 1, "%d", &in) != 1)
1495 in = -1;
1496 }
1497 return( in );
1498 }
1499 #else
MPS_getnameidx(lprec * lp,char * varname,MYBOOL tryrowfirst)1500 static int MPS_getnameidx(lprec *lp, char *varname, MYBOOL tryrowfirst)
1501 {
1502 int in;
1503
1504 /* Have we defined our own variable names? */
1505 if(lp->names_used) {
1506 /* First check the primary name list */
1507 in = get_nameindex(lp, varname, tryrowfirst);
1508 if((in > 0) && !tryrowfirst)
1509 in += lp->rows;
1510 /* If we were unsuccessful, try the secondary name list */
1511 else if(in < 0) {
1512 in = get_nameindex(lp, varname, (MYBOOL) !tryrowfirst);
1513 if((in > 0) && tryrowfirst)
1514 in += lp->rows;
1515 }
1516 }
1517 /* If not, see if we can match the standard name mask */
1518 else {
1519 if(strncmp(varname, (tryrowfirst ? ROWNAMEMASK : COLNAMEMASK), 1) == 0) {
1520 /* Fail if we did not successfully scan as a valid integer */
1521 if((sscanf(varname + 1, "%d", &in) != 1) ||
1522 (in < (tryrowfirst ? 0 : 1)) || (in > (tryrowfirst ? lp->rows : lp->columns)))
1523 in = -1;
1524 }
1525 else if(strncmp(varname, (!tryrowfirst ? ROWNAMEMASK : COLNAMEMASK), 1) == 0) {
1526 /* Fail if we did not successfully scan as a valid integer */
1527 if((sscanf(varname + 1, "%d", &in) != 1) ||
1528 (in < (tryrowfirst ? 0 : 1)) || (in > (tryrowfirst ? lp->rows : lp->columns)))
1529 in = -1;
1530 }
1531 }
1532 return( in );
1533 }
1534 #endif
1535
MPS_readBAS(lprec * lp,int typeMPS,char * filename,char * info)1536 MYBOOL MPS_readBAS(lprec *lp, int typeMPS, char *filename, char *info)
1537 {
1538 char field1[BUFSIZ], field2[BUFSIZ], field3[BUFSIZ], field5[BUFSIZ],
1539 line[BUFSIZ], tmp[BUFSIZ], *ptr;
1540 double field4, field6;
1541 int ib, in, items, Lineno = 0;
1542 MYBOOL ok;
1543 FILE *input = stdin;
1544 int (*scan_line)(int section, char* line, char *field1, char *field2, char *field3,
1545 double *field4, char *field5, double *field6);
1546
1547 switch(typeMPS) {
1548 case MPSFIXED:
1549 scan_line = scan_lineFIXED;
1550 break;
1551 case MPSFREE:
1552 scan_line = scan_lineFREE;
1553 break;
1554 default:
1555 report(lp, IMPORTANT, "MPS_readBAS: unrecognized MPS line type.\n");
1556 return(FALSE);
1557 }
1558
1559 ok = (MYBOOL) ((filename != NULL) && ((input = fopen(filename,"r")) != NULL));
1560 if(!ok)
1561 return(ok);
1562 default_basis(lp);
1563
1564 /* Let's initialize line to all zero's */
1565 MEMCLEAR(line, BUFSIZ);
1566 ok = FALSE;
1567 while(fgets(line, BUFSIZ - 1, input)) {
1568 Lineno++;
1569
1570 for(ptr = line; (*ptr) && (isspace((unsigned char) *ptr)); ptr++);
1571
1572 /* skip lines which start with "*", they are comment */
1573 if((line[0] == '*') || (*ptr == 0) || (*ptr == '\n') || (*ptr == '\r')) {
1574 report(lp, FULL, "Comment on line %d: %s", Lineno, line);
1575 continue;
1576 }
1577
1578 report(lp, FULL, "Line %6d: %s", Lineno, line);
1579
1580 /* first check for "special" lines: in our case only NAME and ENDATA,
1581 ...this must start in the first position of line */
1582 if(line[0] != ' ') {
1583 sscanf(line, "%s", tmp);
1584 if(strcmp(tmp, "NAME") == 0) {
1585 if(info != NULL) {
1586 *info = 0;
1587 for(ptr = line + 4; (*ptr) && (isspace((unsigned char) *ptr)); ptr++);
1588 in = (int) strlen(ptr);
1589 while ((in > 0) && ((ptr[in - 1] == '\r') || (ptr[in - 1] == '\n') || isspace(ptr[in - 1])))
1590 in--;
1591 ptr[in] = 0;
1592 strcpy(info, ptr);
1593 }
1594 }
1595 else if(strcmp(tmp, "ENDATA") == 0) {
1596 report(lp, FULL, "Finished reading BAS file\n");
1597 ok = TRUE;
1598 break;
1599 }
1600 else { /* line does not start with space and does not match above */
1601 report(lp, IMPORTANT, "Unrecognized BAS line %d: %s\n", Lineno, line);
1602 break;
1603 }
1604 }
1605 else { /* normal line, process */
1606 items = scan_line(MPSRHS, line, field1, field2, field3, &field4, field5, &field6);
1607 if(items < 0){
1608 report(lp, IMPORTANT, "Syntax error on line %d: %s\n", Lineno, line);
1609 break;
1610 }
1611 /* find first variable index value */
1612 in = MPS_getnameidx(lp, field2, FALSE);
1613 #ifdef OldNameMatch
1614 if(in < 0)
1615 in = MPS_getnameidx(lp, field2, TRUE);
1616 else
1617 in += lp->rows;
1618 #endif
1619 if(in < 0)
1620 break;
1621
1622 /* check if we have the basic/non-basic variable format */
1623 if(field1[0] == 'X') {
1624 /* find second variable index value */
1625 ib = in;
1626 in = MPS_getnameidx(lp, field3, FALSE);
1627 #ifdef OldNameMatch
1628 if(in < 0)
1629 in = MPS_getnameidx(lp, field3, TRUE);
1630 else
1631 in += lp->rows;
1632 #endif
1633 if(in < 0)
1634 break;
1635
1636 lp->is_lower[in] = (MYBOOL) (field1[1] == 'L');
1637 lp->is_basic[ib] = TRUE;
1638 }
1639 else
1640 lp->is_lower[in] = (MYBOOL) (field1[0] == 'L');
1641
1642 lp->is_basic[in] = FALSE;
1643
1644 }
1645 }
1646 /* Update the basis index-to-variable array */
1647 ib = 0;
1648 items = lp->sum;
1649 for(in = 1; in <= items; in++)
1650 if(lp->is_basic[in]) {
1651 ib++;
1652 lp->var_basic[ib] = in;
1653 }
1654
1655 fclose(input);
1656 return( ok );
1657 }
1658
MPS_writeBAS(lprec * lp,int typeMPS,char * filename)1659 MYBOOL MPS_writeBAS(lprec *lp, int typeMPS, char *filename)
1660 {
1661 int ib, in;
1662 MYBOOL ok;
1663 char name1[100], name2[100];
1664 FILE *output; /* = stdout; */
1665 char * (*MPSname)(char *name);
1666
1667 /* Set name formatter */
1668 switch(typeMPS) {
1669 case MPSFIXED:
1670 MPSname = MPSnameFIXED;
1671 break;
1672 case MPSFREE:
1673 MPSname = MPSnameFREE;
1674 break;
1675 default:
1676 report(lp, IMPORTANT, "MPS_writeBAS: unrecognized MPS name type.\n");
1677 return(FALSE);
1678 }
1679
1680 /* Open the file for writing */
1681 ok = (MYBOOL) ((filename == NULL) || ((output = fopen(filename,"w")) != NULL));
1682 if(!ok)
1683 return(ok);
1684 if(filename == NULL && lp->outstream != NULL)
1685 output = lp->outstream;
1686
1687 fprintf(output, "NAME %s Rows %d Cols %d Iters %.0f\n",
1688 get_lp_name(lp), lp->rows, lp->columns, (double) get_total_iter(lp));
1689
1690 ib = lp->rows;
1691 in = 0;
1692 while ((ib < lp->sum) || (in < lp->sum)) {
1693
1694 /* Find next basic variable (skip slacks) */
1695 ib++;
1696 while((ib <= lp->sum) && !lp->is_basic[ib])
1697 ib++;
1698
1699 /* Find next non-basic variable (skip lower-bounded structural variables) */
1700 in++;
1701 while((in <= lp->sum) && (lp->is_basic[in] ||
1702 ((in > lp->rows) && lp->is_lower[in])))
1703 in++;
1704
1705 /* Check if we have a basic/non-basic variable pair */
1706 if((ib <= lp->sum) && (in <= lp->sum)) {
1707 strcpy(name1, MPSname((ib <= lp->rows ? get_row_name(lp, ib) :
1708 get_col_name(lp, ib-lp->rows))));
1709 strcpy(name2, MPSname((in <= lp->rows ? get_row_name(lp, in) :
1710 get_col_name(lp, in-lp->rows))));
1711 fprintf(output, " %2s %s %s\n", (lp->is_lower[in] ? "XL" : "XU"), name1, name2);
1712 }
1713
1714 /* Otherwise just write the bound state of the non-basic variable */
1715 else if(in <= lp->sum) {
1716 strcpy(name1, MPSname((in <= lp->rows ? get_row_name(lp, in) :
1717 get_col_name(lp, in-lp->rows))));
1718 fprintf(output, " %2s %s\n", (lp->is_lower[in] ? "LL" : "UL"), name1);
1719 }
1720
1721 }
1722 fprintf(output, "ENDATA\n");
1723
1724 if(filename != NULL)
1725 fclose(output);
1726 return( ok );
1727 }
1728