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