#include #include #include "commonlib.h" #include "lp_lib.h" #include "lp_MPS.h" #ifdef FORTIFY # include "lp_fortify.h" #endif /* MPS file input and output routines for lp_solve */ /* ------------------------------------------------------------------------- */ /* A: MPS format was named after an early IBM LP product and has emerged as a de facto standard ASCII medium among most of the commercial LP codes. Essentially all commercial LP codes accept this format, but if you are using public domain software and have MPS files, you may need to write your own reader routine for this. It's not too hard. The main things to know about MPS format are that it is column oriented (as opposed to entering the model as equations), and everything (variables, rows, etc.) gets a name. MPS format is described in more detail in Murtagh's book, referenced in another section. Also, ftp://softlib.cs.rice.edu/pub/miplib/mps_format is a nice short introduction. exports MPS is an old format, so it is set up as though you were using punch cards, and is not free format. Fields start in column 1, 5, 15, 25, 40 and 50. Sections of an MPS file are marked by so-called header cards, which are distinguished by their starting in column 1. Although it is typical to use upper-case throughout the file (like I said, MPS has long historical roots), many MPS-readers will accept mixed-case for anything except the header cards, and some allow mixed-case anywhere. The names that you choose for the individual entities (constraints or variables) are not important to the solver; you should pick names that are meaningful to you, or will be easy for a post-processing code to read. Here is a little sample model written in MPS format (explained in more detail below): NAME TESTPROB ROWS N COST L LIM1 G LIM2 E MYEQN COLUMNS XONE COST 1 LIM1 1 XONE LIM2 1 YTWO COST 4 LIM1 1 YTWO MYEQN -1 ZTHREE COST 9 LIM2 1 ZTHREE MYEQN 1 RHS RHS1 LIM1 5 LIM2 10 RHS1 MYEQN 7 BOUNDS UP BND1 XONE 4 LO BND1 YTWO -1 UP BND1 YTWO 1 ENDATA means: Optimize COST: XONE + 4 YTWO + 9 ZTHREE Subject To LIM1: XONE + YTWO <= 5 LIM2: XONE + ZTHREE >= 10 MYEQN: - YTWO + ZTHREE = 7 Bounds 0 <= XONE <= 4 -1 <= YTWO <= 1 End */ /* copy a MPS name, only trailing spaces are removed. In MPS, names can have embedded spaces! */ STATIC void namecpy(char *into, char *from) { int i; /* copy at most 8 characters of from, stop at end of string or newline */ for(i = 0; (from[i] != '\0') && (from[i] != '\n') && (from[i] != '\r') && (i < 8); i++) into[i] = from[i]; /* end with end of string */ into[i] = '\0'; /* remove trailing spaces, if any */ for(i--; (i >= 0) && (into[i] == ' '); i--) into[i] = '\0'; } /* scan an MPS line, and pick up the information in the fields that are present */ /* scan_line for fixed MPS format */ STATIC int scan_lineFIXED(int section, char* line, char *field1, char *field2, char *field3, double *field4, char *field5, double *field6) { int items = 0, line_len; char buf[16], *ptr1, *ptr2; line_len = (int) strlen(line); while ((line_len) && ((line[line_len-1] == '\n') || (line[line_len-1] == '\r') || (line[line_len-1] == ' '))) line_len--; if(line_len >= 1) { /* spaces or N/L/G/E or UP/LO */ strncpy(buf, line, 4); buf[4] = '\0'; sscanf(buf, "%s", field1); items++; } else field1[0] = '\0'; line += 4; if(line_len >= 5) { /* name */ namecpy(field2, line); items++; } else field2[0] = '\0'; line += 10; if(line_len >= 14) { /* name */ namecpy(field3, line); items++; } else field3[0] = '\0'; line += 10; if(line_len >= 25) { /* number */ strncpy(buf, line, 15); buf[15] = '\0'; for(ptr1 = ptr2 = buf; ; ptr1++) if(!isspace((unsigned char) *ptr1)) if((*(ptr2++) = *ptr1) == 0) break; /* *field4 = atof(buf); */ *field4 = strtod(buf, &ptr1); if(*ptr1) return(-1); items++; } else *field4 = 0; line += 15; if(line_len >= 40) { /* name */ namecpy(field5, line); items++; } else field5[0] = '\0'; line += 10; if(line_len >= 50) { /* number */ strncpy(buf, line, 15); buf[15] = '\0'; for(ptr1 = ptr2 = buf; ; ptr1++) if(!isspace((unsigned char) *ptr1)) if((*(ptr2++) = *ptr1) == 0) break; /* *field6 = atof(buf); */ *field6 = strtod(buf, &ptr1); if(*ptr1) return(-1); items++; } else *field6 = 0; return(items); } STATIC int spaces(char *line, int line_len) { int l; char *line1 = line; while (*line1 == ' ') line1++; l = (int) (line1 - line); if (line_len < l) l = line_len; return(l); } STATIC int lenfield(char *line, int line_len) { int l; char *line1 = line; while ((*line1) && (*line1 != ' ')) line1++; l = (int) (line1 - line); if (line_len < l) l = line_len; return(l); } /* scan_line for fixed MPS format */ STATIC int scan_lineFREE(int section, char* line, char *field1, char *field2, char *field3, double *field4, char *field5, double *field6) { int items = 0, line_len, len; char buf[256], *ptr1, *ptr2; line_len = (int) strlen(line); while ((line_len) && ((line[line_len-1] == '\n') || (line[line_len-1] == '\r') || (line[line_len-1] == ' '))) line_len--; len = spaces(line, line_len); line += len; line_len -= len; if ((section == MPSCOLUMNS) || (section == MPSRHS) || (section == MPSRANGES)) { field1[0] = '\0'; items++; } else { len = lenfield(line, line_len); if(line_len >= 1) { /* spaces or N/L/G/E or UP/LO */ strncpy(buf, line, len); buf[len] = '\0'; sscanf(buf, "%s", field1); items++; } else field1[0] = '\0'; line += len; line_len -= len; len = spaces(line, line_len); line += len; line_len -= len; } len = lenfield(line, line_len); if(line_len >= 1) { /* name */ strncpy(field2, line, len); field2[len] = '\0'; items++; } else field2[0] = '\0'; line += len; line_len -= len; len = spaces(line, line_len); line += len; line_len -= len; len = lenfield(line, line_len); if(line_len >= 1) { /* name */ strncpy(field3, line, len); field3[len] = '\0'; items++; } else field3[0] = '\0'; line += len; line_len -= len; len = spaces(line, line_len); line += len; line_len -= len; if (*field3) { if((section == MPSCOLUMNS) && (strcmp(field3, "'MARKER'") == 0)) { *field4 = 0; items++; ptr1 = field3; } else { *field4 = strtod(field3, &ptr1); if(*ptr1 == 0) { strcpy(field3, field2); if ((section == MPSROWS) || (section == MPSBOUNDS) /* || (section == MPSSOS) */) *field2 = 0; else { strcpy(field2, field1); *field1 = 0; } items++; } else ptr1 = NULL; } } else ptr1 = NULL; if(ptr1 == NULL) { len = lenfield(line, line_len); if(line_len >= 1) { /* number */ strncpy(buf, line, len); buf[len] = '\0'; for(ptr1 = ptr2 = buf; ; ptr1++) if(!isspace((unsigned char) *ptr1)) if((*(ptr2++) = *ptr1) == 0) break; /* *field4 = atof(buf); */ *field4 = strtod(buf, &ptr1); if(*ptr1) return(-1); items++; } else *field4 = 0; line += len; line_len -= len; len = spaces(line, line_len); line += len; line_len -= len; } len = lenfield(line, line_len); if(line_len >= 1) { /* name */ strncpy(field5, line, len); field5[len] = '\0'; items++; } else field5[0] = '\0'; line += len; line_len -= len; len = spaces(line, line_len); line += len; line_len -= len; len = lenfield(line, line_len); if(line_len >= 1) { /* number */ strncpy(buf, line, len); buf[len] = '\0'; for(ptr1 = ptr2 = buf; ; ptr1++) if(!isspace((unsigned char) *ptr1)) if((*(ptr2++) = *ptr1) == 0) break; /* *field6 = atof(buf); */ *field6 = strtod(buf, &ptr1); if(*ptr1) return(-1); items++; } else *field6 = 0; if((section == MPSSOS) && (items == 2)) { strcpy(field3, field2); strcpy(field2, field1); *field1 = 0; } for(ptr1 = field1; *ptr1; ptr1++) *ptr1=toupper(*ptr1); return(items); } STATIC int addmpscolumn(lprec *lp, MYBOOL Int_section, MYBOOL *Column_ready, int *count, REAL *Last_column, int *Last_columnno, char *Last_col_name) { int ok = TRUE; if (*Column_ready) { ok = add_columnex(lp, *count, Last_column, Last_columnno); if (ok) { ok = set_col_name(lp, lp->columns, Last_col_name); } if (ok) lp_solve_set_int(lp, lp->columns, Int_section); } *Column_ready = FALSE; *count = 0; return(ok); } STATIC MYBOOL appendmpsitem(int *count, int rowIndex[], REAL rowValue[]) { int i = *count; if(rowValue[i] == 0) return( FALSE ); while((i > 0) && (rowIndex[i] < rowIndex[i-1])) { swapINT (rowIndex+i, rowIndex+i-1); swapREAL(rowValue+i, rowValue+i-1); i--; } (*count)++; return( TRUE ); } lprec *MPS_readfile(char *input, int typeMPS, int verbose) { lprec *lp = NULL; FILE *fpin; fpin = fopen(input, "r"); if(fpin != NULL) { lp = MPS_readhandle(fpin, typeMPS, verbose); fclose(fpin); } return(lp); } lprec *MPS_readhandle(FILE *input, int typeMPS, int verbose) { char field1[BUFSIZ], field2[BUFSIZ], field3[BUFSIZ], field5[BUFSIZ], line[BUFSIZ], tmp[BUFSIZ], Last_col_name[BUFSIZ], probname[BUFSIZ], *ptr; int items, row, Lineno, var, section = MPSUNDEF, variant = 0, NZ = 0, SOS = 0; MYBOOL Int_section, Column_ready, Column_ready1, Unconstrained_rows_found = FALSE, OF_found = FALSE, CompleteStatus = FALSE; double field4, field6; REAL *Last_column = NULL; int count = 0, *Last_columnno = NULL; lprec *lp; int (*scan_line)(int section, char* line, char *field1, char *field2, char *field3, double *field4, char *field5, double *field6); lp = lp_solve_make_lp(0, 0); switch(typeMPS) { case MPSFIXED: scan_line = scan_lineFIXED; break; case MPSFREE: scan_line = scan_lineFREE; break; default: report(lp, IMPORTANT, "MPS_readfile: unrecognized MPS file type.\n"); lp_solve_delete_lp(lp); return(NULL); } if (lp != NULL) { lp->source_is_file = TRUE; lp->verbose = verbose; strcpy(Last_col_name, ""); Int_section = FALSE; Column_ready = FALSE; Lineno = 0; /* let's initialize line to all zero's */ MEMCLEAR(line, BUFSIZ); while(fgets(line, BUFSIZ - 1, input)) { Lineno++; for(ptr = line; (*ptr) && (isspace((unsigned char) *ptr)); ptr++); /* skip lines which start with "*", they are comment */ if((line[0] == '*') || (*ptr == 0) || (*ptr == '\n') || (*ptr == '\r')) { report(lp, FULL, "Comment on line %d: %s", Lineno, line); continue; } report(lp, FULL, "Line %6d: %s", Lineno, line); /* first check for "special" lines: NAME, ROWS, BOUNDS .... */ /* this must start in the first position of line */ if(line[0] != ' ') { sscanf(line, "%s", tmp); if(strcmp(tmp, "NAME") == 0) { section = MPSNAME; sscanf(line, "NAME %s", probname); if (!set_lp_name(lp, probname)) break; } else if(strcmp(tmp, "ROWS") == 0) { section = MPSROWS; report(lp, FULL, "Switching to ROWS section\n"); } else if(strcmp(tmp, "COLUMNS") == 0) { allocREAL(lp, &Last_column, lp->rows + 1, TRUE); allocINT(lp, &Last_columnno, lp->rows + 1, TRUE); count = 0; if ((Last_column == NULL) || (Last_columnno == NULL)) break; section = MPSCOLUMNS; report(lp, FULL, "Switching to COLUMNS section\n"); } else if(strcmp(tmp, "RHS") == 0) { if (!addmpscolumn(lp, Int_section, &Column_ready, &count, Last_column, Last_columnno, Last_col_name)) break; section = MPSRHS; report(lp, FULL, "Switching to RHS section\n"); } else if(strcmp(tmp, "BOUNDS") == 0) { section = MPSBOUNDS; report(lp, FULL, "Switching to BOUNDS section\n"); } else if(strcmp(tmp, "RANGES") == 0) { section = MPSRANGES; report(lp, FULL, "Switching to RANGES section\n"); } else if((strcmp(tmp, "SOS") == 0) || (strcmp(tmp, "SETS") == 0)) { section = MPSSOS; if(strcmp(tmp, "SOS") == 0) variant = 0; else variant = 1; report(lp, FULL, "Switching to %s section\n", tmp); } else if(strcmp(tmp, "ENDATA") == 0) { report(lp, FULL, "Finished reading MPS file\n"); CompleteStatus = TRUE; break; } else { /* line does not start with space and does not match above */ report(lp, IMPORTANT, "Unrecognized line %d: %s\n", Lineno, line); break; } } else { /* normal line, process */ items = scan_line(section, line, field1, field2, field3, &field4, field5, &field6); if(items < 0){ report(lp, IMPORTANT, "Syntax error on line %d: %s\n", Lineno, line); break; } switch(section) { case MPSNAME: report(lp, IMPORTANT, "Error, extra line under NAME line\n"); break; /* Process entries in the ROWS section */ case MPSROWS: /* field1: rel. operator; field2: name of constraint */ report(lp, FULL, "Row %5d: %s %s\n", lp->rows + 1, field1, field2); if(strcmp(field1, "N") == 0) { if(!OF_found) { /* take the first N row as OF, ignore others */ if (!set_row_name(lp, 0, field2)) break; OF_found = TRUE; } else if(!Unconstrained_rows_found) { report(lp, IMPORTANT, "Unconstrained row %s ignored\n", field2); report(lp, IMPORTANT, "Further messages of this kind will be suppressed\n"); Unconstrained_rows_found = TRUE; } } else if(strcmp(field1, "L") == 0) { if ((!str_add_constraint(lp, "" ,LE ,0)) || (!set_row_name(lp, lp->rows, field2))) break; } else if(strcmp(field1, "G") == 0) { if ((!str_add_constraint(lp, "" ,GE ,0)) || (!set_row_name(lp, lp->rows, field2))) break; } else if(strcmp(field1, "E") == 0) { if ((!str_add_constraint(lp, "",EQ ,0)) || (!set_row_name(lp, lp->rows, field2))) break; } else { report(lp, SEVERE, "Unknown relation code '%s' on line %d\n", field1, Lineno); break; } continue; /* Process entries in the COLUMNS section */ case MPSCOLUMNS: /* field2: variable; field3: constraint; field4: coef */ /* optional: field5: constraint; field6: coef */ report(lp, FULL, "Column %4d: %s %s %g %s %g\n", lp->columns + 1, field2, field3, field4, field5, field6); if((items == 4) || (items == 5) || (items == 6)) { if (NZ == 0) strcpy(Last_col_name, field2); else if(*field2) { Column_ready1 = (MYBOOL) (strcmp(field2, Last_col_name) != 0); if(Column_ready1) { if (find_var(lp, field2, FALSE) >= 0) { report(lp, SEVERE, "Variable name (%s) is already used!\n", field2); break; } if(Column_ready) { /* Added ability to handle non-standard "same as above" column name */ if (addmpscolumn(lp, Int_section, &Column_ready, &count, Last_column, Last_columnno, Last_col_name)) { strcpy(Last_col_name, field2); NZ = 0; } else break; } } } if(items == 5) { /* there might be an INTEND or INTORG marker */ /* look for " 'MARKER' 'INTORG'" or " 'MARKER' 'INTEND'" */ if(strcmp(field3, "'MARKER'") != 0) break; if(strcmp(field5, "'INTORG'") == 0) { Int_section = TRUE; report(lp, FULL, "Switching to integer section\n"); } else if(strcmp(field5, "'INTEND'") == 0) { Int_section = FALSE; report(lp, FULL, "Switching to non-integer section\n"); } else report(lp, IMPORTANT, "Unknown marker (ignored) at line %d: %s\n", Lineno, field5); } else if((row = find_row(lp, field3, Unconstrained_rows_found)) >= 0) { if(row > lp->rows) report(lp, CRITICAL, "Invalid row %s encountered in the MPS file\n", field3); Last_columnno[count] = row; Last_column[count] = (REAL)field4; if(appendmpsitem(&count, Last_columnno, Last_column)) { NZ++; Column_ready = TRUE; } } } if(items == 6) { if((row = find_row(lp, field5, Unconstrained_rows_found)) >= 0) { if(row > lp->rows) report(lp, CRITICAL, "Invalid row %s encountered in the MPS file\n", field6); Last_columnno[count] = row; Last_column[count] = (REAL)field6; if(appendmpsitem(&count, Last_columnno, Last_column)) { NZ++; Column_ready = TRUE; } } } if((items < 4) || (items > 6)) { /* Wrong! */ report(lp, CRITICAL, "Wrong number of items (%d) in COLUMNS section (line %d)\n", items, Lineno); break; } continue; /* Process entries in the RHS section */ /* field2: uninteresting name; field3: constraint name */ /* field4: value */ /* optional: field5: constraint name; field6: value */ case MPSRHS: report(lp, FULL, "RHS line: %s %s %g %s %g\n", field2, field3, field4, field5, field6); if((items != 4) && (items != 6)) { report(lp, CRITICAL, "Wrong number of items (%d) in RHS section line %d\n", items, Lineno); break; } if((row = find_row(lp, field3, Unconstrained_rows_found)) >= 0) { lp_solve_set_rh(lp, row, (REAL)field4); } if(items == 6) { if((row = find_row(lp, field5, Unconstrained_rows_found)) >= 0) { lp_solve_set_rh(lp, row, (REAL)field6); } } continue; /* Process entries in the BOUNDS section */ /* field1: bound type; field2: uninteresting name; */ /* field3: variable name; field4: value */ case MPSBOUNDS: report(lp, FULL, "BOUNDS line: %s %s %s %g\n", field1, field2, field3, field4); var = find_var(lp, field3, FALSE); if(var < 0){ /* bound on undefined var in COLUMNS section ... */ Column_ready = TRUE; if (!addmpscolumn(lp, FALSE, &Column_ready, &count, Last_column, Last_columnno, field3)) break; Column_ready = TRUE; var = find_var(lp, field3, TRUE); } if(var < 0) /* undefined var and could add ... */; else if(strcmp(field1, "UP") == 0) { /* upper bound */ set_bounds(lp, var, get_lowbo(lp, var), field4); } else if(strcmp(field1, "SC") == 0) { /* upper bound */ if(field4 == 0) field4 = lp->infinite; set_bounds(lp, var, get_lowbo(lp, var), field4); set_semicont(lp, var, TRUE); } else if(strcmp(field1, "SI") == 0) { /* upper bound */ if(field4 == 0) field4 = lp->infinite; set_bounds(lp, var, get_lowbo(lp, var), field4); lp_solve_set_int(lp, var, TRUE); set_semicont(lp, var, TRUE); } else if(strcmp(field1, "LO") == 0) { /* lower bound */ if((field4 < lp->negrange) && (lp->splitnegvars == AUTOMATIC)) field4 = -lp->infinite; set_bounds(lp, var, field4, get_upbo(lp, var)); } else if(strcmp(field1, "PL") == 0) /* plus-ranged variable */ set_bounds(lp, var, get_lowbo(lp, var), lp->infinite); else if(strcmp(field1, "MI") == 0) { /* minus-ranged variable */ set_bounds(lp, var, -lp->infinite, get_upbo(lp, var)); } else if(strcmp(field1, "FR") == 0) { /* free variable */ set_free(lp, var); } else if(strcmp(field1, "FX") == 0) { /* fixed, upper _and_ lower */ set_bounds(lp, var, field4, field4); } else if(strcmp(field1, "BV") == 0) { /* binary variable */ set_binary(lp, var, TRUE); } /* AMPL bounds type UI and LI added by E.Imamura (CRIEPI) */ else if(strcmp(field1, "UI") == 0) { /* upper bound for integer variable */ set_bounds(lp, var, get_lowbo(lp, var), field4); lp_solve_set_int(lp, var, TRUE); } else if(strcmp(field1, "LI") == 0) { /* lower bound for integer variable - corrected by KE */ set_bounds(lp, var, field4, get_upbo(lp, var)); lp_solve_set_int(lp, var, TRUE); } else { report(lp, CRITICAL, "BOUND type %s on line %d is not supported", field1, Lineno); break; } continue; /* Process entries in the BOUNDS section */ /* We have to implement the following semantics: D. The RANGES section is for constraints of the form: h <= constraint <= u . The range of the constraint is r = u - h . The value of r is specified in the RANGES section, and the value of u or h is specified in the RHS section. If b is the value entered in the RHS section, and r is the value entered in the RANGES section, then u and h are thus defined: row type sign of r h u ---------------------------------------------- G + or - b b + |r| L + or - b - |r| b E + b b + |r| E - b - |r| b */ /* field2: uninteresting name; field3: constraint name */ /* field4: value */ /* optional: field5: constraint name; field6: value */ case MPSRANGES: report(lp, FULL, "RANGES line: %s %s %g %s %g", field2, field3, field4, field5, field6); if((items != 4) && (items != 6)) { report(lp, CRITICAL, "Wrong number of items (%d) in RANGES section line %d", items, Lineno); break; } if((row = find_row(lp, field3, Unconstrained_rows_found)) >= 0) { /* Determine constraint type */ if(fabs(field4) >= lp->infinite) { report(lp, IMPORTANT, "Warning, Range for row %s >= infinity (value %g) on line %d, ignored", field3, field4, Lineno); } else if(field4 == 0) { /* Change of a GE or LE to EQ */ if(lp->orig_upbo[row] != 0) lp_solve_set_constr_type(lp, row, EQ); } else if(is_chsign(lp, row)) { /* GE */ lp->orig_upbo[row] = fabs(field4); } else if((lp->orig_upbo[row] == 0) && (field4 >= 0)) { /* EQ with positive sign of r value */ lp_solve_set_constr_type(lp, row, GE); lp->orig_upbo[row] = field4; } else if(lp->orig_upbo[row] == lp->infinite) { /* LE */ lp->orig_upbo[row] = fabs(field4); } else if((lp->orig_upbo[row] == 0) && (field4 < 0)) { /* EQ with negative sign of r value */ lp_solve_set_constr_type(lp, row, LE); lp->orig_upbo[row] = my_flipsign(field4); } else { /* let's be paranoid */ report(lp, IMPORTANT, "Cannot figure out row type, row = %d, is_chsign = %d, upbo = %g on line %d", row, is_chsign(lp, row), (double)lp->orig_upbo[row], Lineno); } } if(items == 6) { if((row = find_row(lp, field5, Unconstrained_rows_found)) >= 0) { /* Determine constraint type */ if(fabs(field6) >= lp->infinite) { report(lp, IMPORTANT, "Warning, Range for row %s >= infinity (value %g) on line %d, ignored", field5, field6, Lineno); } else if(field6 == 0) { /* Change of a GE or LE to EQ */ if(lp->orig_upbo[row] != 0) lp_solve_set_constr_type(lp, row, EQ); } else if(is_chsign(lp, row)) { /* GE */ lp->orig_upbo[row] = fabs(field6); } else if(lp->orig_upbo[row] == 0 && field6 >= 0) { /* EQ with positive sign of r value */ lp_solve_set_constr_type(lp, row, GE); lp->orig_upbo[row] = field6; } else if(lp->orig_upbo[row] == lp->infinite) { /* LE */ lp->orig_upbo[row] = fabs(field6); } else if((lp->orig_upbo[row] == 0) && (field6 < 0)) { /* EQ with negative sign of r value */ lp_solve_set_constr_type(lp, row, LE); lp->orig_upbo[row] = my_flipsign(field6); } else { /* let's be paranoid */ report(lp, IMPORTANT, "Cannot figure out row type, row = %d, is_chsign = %d, upbo = %g on line", row, is_chsign(lp,row), (REAL) lp->orig_upbo[row], Lineno); } } } continue; /* Process entries in the SOS section */ /* We have to implement the following semantics: E. The SOS section is for ordered variable sets of the form: x1, x2, x3 ... xn where only a given number of consequtive variables may be non-zero. Each set definition is prefaced by type, name and priority data. Each set member has an optional weight that determines its order. There are two forms supported; a full format and a reduced CPLEX-like format. */ case MPSSOS: report(lp, FULL, "SOS line: %s %s %g %s %g", field2, field3, field4, field5, field6); if((items == 0) || (items > 4)) { report(lp, IMPORTANT, "Invalid number of items (%d) in SOS section line %d\n", items, Lineno); break; } if(strlen(field1) == 0) items--; /* fix scanline anomoly! */ /* Check if this is the start of a new SOS */ if(items == 1 || items == 4) { row = (int) (field1[1] - '0'); if((row <= 0) || (row > 9)) { report(lp, IMPORTANT, "Error: Invalid SOS type %s line %d\n", field1, Lineno); break; } field1[0] = '\0'; /* fix scanline anomoly! */ /* lp_solve needs a name for the SOS */ if(variant == 0) { if(strlen(field3) == 0) /* CPLEX format does not provide a SOS name; create one */ sprintf(field3, "SOS_%d", SOS_count(lp) + 1); } else { /* Remap XPRESS format name */ strcpy(field3, field1); } /* Obtain the SOS priority */ if(items == 4) SOS = (int) field4; else SOS = 1; /* Define a new SOS instance */ SOS = add_SOS(lp, field3, (int) row, SOS, 0, NULL, NULL); } /* Otherwise, add set members to the active SOS */ else { char *field = (items == 3) ? field3 /* Native lp_solve and XPRESS formats */ : field2 /* CPLEX format */; var = find_var(lp, field, FALSE); /* Native lp_solve and XPRESS formats */ if(var < 0){ /* SOS on undefined var in COLUMNS section ... */ Column_ready = TRUE; if (!addmpscolumn(lp, FALSE, &Column_ready, &count, Last_column, Last_columnno, field)) break; Column_ready = TRUE; var = find_var(lp, field, TRUE); } if((var < 0) || (SOS < 1)) /* undefined var and could add ... */; else append_SOSrec(lp->SOS->sos_list[SOS-1], 1, &var, &field4); } continue; } /* If we got here there was an error "upstream" */ report(lp, IMPORTANT, "Error: Cannot handle line %d\n", Lineno); break; } } if(CompleteStatus == FALSE) { lp_solve_delete_lp(lp); lp = NULL; } if(Last_column != NULL) FREE(Last_column); if(Last_columnno != NULL) FREE(Last_columnno); } return(lp); } static void number(char *str,REAL value) { char __str[80], *_str; int i; /* sprintf(_str,"%12.6G",value); */ _str=__str+2; if (value>=0.0) if ((value!=0.0) && ((value>0.99999999e12) || (value<0.0001))) { int n=15; do { n--; i=sprintf(_str,"%*.*E",n,n-6,(double) value); if (i>12) { char *ptr=strchr(_str,'E'); if (ptr!=NULL) { if (*(++ptr)=='-') ptr++; while ((i>12) && ((*ptr=='+') || (*ptr=='0'))) { strcpy(ptr,ptr+1); i--; } } } } while (i>12); } else if (value>=1.0e10) { int n=13; do { i=sprintf(_str,"%*.0f",--n,(double) value); } while (i>12); } else { if (((i=sprintf(_str,"%12.10f",(double) value))>12) && (_str[12]>='5')) { for (i=11;i>=0;i--) if (_str[i]!='.') { if (++_str[i]>'9') _str[i]='0'; else break; } if (i<0) { *(--_str)='1'; *(--_str)=' '; } } } else if ((value<-0.99999999e11) || (value>-0.0001)) { int n=15; do { n--; i=sprintf(_str,"%*.*E",n,n-7,(double) value); if (i>12) { char *ptr=strchr(_str,'E'); if (ptr!=NULL) { if (*(++ptr)=='-') ptr++; while ((i>12) && ((*ptr=='+') || (*ptr=='0'))) { strcpy(ptr,ptr+1); i--; } } } } while (i>12); } else if (value<=-1.0e9) { int n=13; do { i=sprintf(_str,"%*.0f",--n,(double) value); } while (i>12); } else if (((i=sprintf(_str,"%12.9f",(double) value))>12) && (_str[12]>='5')) { for (i=11;i>=1;i--) if (_str[i]!='.') { if (++_str[i]>'9') _str[i]='0'; else break; } if (i<1) { *_str='1'; *(--_str)='-'; *(--_str)=' '; } } strncpy(str,_str,12); } static char numberbuffer[15]; static char *formatnumber12(double a) { #if 0 return(sprintf(numberbuffer, "%12g", a)); #else number(numberbuffer, a); return(numberbuffer); #endif } STATIC char *MPSnameFIXED(char *name) { static char name0[9]; sprintf(name0, "%-8.8s", name); return(name0); } STATIC char *MPSnameFREE(char *name) { if(strlen(name) < 8) return(MPSnameFIXED(name)); else return(name); } MYBOOL MPS_writefile(lprec *lp, int typeMPS, char *filename) { int i, j, jj, je, k, marker, putheader; MYBOOL ok, names_used; REAL a; FILE *output = stdout; char * (*MPSname)(char *name); #ifdef Paranoia if(lp->matA->is_roworder) { report(lp, IMPORTANT, "MPS_writefile: Cannot write to MPS file while in row entry mode.\n"); return(FALSE); } #endif switch(typeMPS) { case MPSFIXED: MPSname = MPSnameFIXED; break; case MPSFREE: MPSname = MPSnameFREE; break; default: report(lp, IMPORTANT, "MPS_writefile: unrecognized MPS file type.\n"); return(FALSE); } ok = (MYBOOL) ((filename == NULL) || ((output = fopen(filename,"w")) != NULL)); if(!ok) return(ok); if(filename == NULL && lp->outstream != NULL) output = lp->outstream; names_used = lp->names_used; if(typeMPS == MPSFIXED) { /* Check if there is no variable name where the first 8 charachters are equal to the first 8 characters of anothe variable */ if(names_used) for(i = 1; (i <= lp->columns) && (ok); i++) if((lp->col_name[i] != NULL) && (lp->col_name[i]->name != NULL) && (!is_splitvar(lp, i)) && (strlen(lp->col_name[i]->name) > 8)) for(j = 1; (j < i) && (ok); j++) if((lp->col_name[j] != NULL) && (lp->col_name[j]->name != NULL) && (!is_splitvar(lp, j))) if(strncmp(lp->col_name[i]->name, lp->col_name[j]->name, 8) == 0) ok = FALSE; } if(!ok) { lp->names_used = FALSE; ok = TRUE; } marker = 0; fprintf(output, "* MPS file created by lp_solve v%d.%d!\n", (int) MAJORVERSION, (int) MINORVERSION); fprintf(output, "* Model constraints = %d (of which %d equalities)\n", lp->rows, lp->equalities); fprintf(output, "* variables = %d (of which %d integer-valued and %d semi-continuous)\n", lp->columns, lp->int_count, lp->sc_count); if(SOS_count(lp) > 0) fprintf(output, "* SOS = %d (involving %d variables)\n", SOS_count(lp), lp->sos_vars); fprintf(output, "*\n"); fprintf(output, "NAME %s\n", MPSname(lp->lp_name)); fprintf(output, "ROWS\n"); for(i = 0; i <= lp->rows; i++) { if(i == 0) fprintf(output, " N "); else if(lp->orig_upbo[i] != 0) { if(is_chsign(lp,i)) fprintf(output, " G "); else fprintf(output, " L "); } else fprintf(output, " E "); fprintf(output, "%s\n", MPSname(get_row_name(lp, i))); } fprintf(output, "COLUMNS\n"); for(i = 1; i <= lp->columns; i++) { if(!is_splitvar(lp, i)) { if(is_int(lp,i) && (marker % 2) == 0) { fprintf(output, " MARK%04d 'MARKER' 'INTORG'\n", marker); marker++; } if(!is_int(lp,i) && (marker % 2) == 1) { fprintf(output, " MARK%04d 'MARKER' 'INTEND'\n", marker); marker++; } /* Loop over non-zero column entries */ je = lp->matA->col_end[i]; for(k = 1, jj = lp->matA->col_end[i-1]; jj < je; jj++) { k = 1 - k; j = lp->matA->col_mat[jj].row_nr; a = get_mat_byindex(lp, jj, FALSE); a = my_chsign(is_chsign(lp, j), a); if (k == 0) { fprintf(output, " %s", MPSname(get_col_name(lp, i))); fprintf(output, " %s %s", MPSname(get_row_name(lp, j)), /* formatnumber12((double) a)); */ formatnumber12((double) (a * (j == 0 && is_maxim(lp) ? -1 : 1)))); } else fprintf(output, " %s %s\n", MPSname(get_row_name(lp, j)), formatnumber12((double) (a * (j == 0 && is_maxim(lp) ? -1 : 1)))); /* formatnumber12((double) a)); */ } if(k == 0) fprintf(output, "\n"); } } if((marker % 2) == 1) { fprintf(output, " MARK%04d 'MARKER' 'INTEND'\n", marker); /* marker++; */ /* marker not used after this */ } fprintf(output, "RHS\n"); for(k = 1, i = 0; i <= lp->rows; i++) { a = lp->orig_rh[i]; if(a) { a = unscaled_value(lp, a, i); if((i == 0) || is_chsign(lp, i)) a = my_flipsign(a); k = 1 - k; if(k == 0) fprintf(output, " RHS %s %s", MPSname(get_row_name(lp, i)), formatnumber12((double)a)); else fprintf(output, " %s %s\n", MPSname(get_row_name(lp, i)), formatnumber12((double)a)); } } if(k == 0) fprintf(output, "\n"); putheader = TRUE; for(k = 1, i = 1; i <= lp->rows; i++){ a = 0; if((lp->orig_upbo[i] < lp->infinite) && (lp->orig_upbo[i] != 0.0)) a = lp->orig_upbo[i]; #if 0 else if((lp->orig_lowbo[i] > -lp->infinite) && (lp->orig_lowbo[i] != 0.0)) a = my_flipsign(lp->orig_lowbo[i]); #endif if(a) { if(putheader) { fprintf(output, "RANGES\n"); putheader = FALSE; } a = unscaled_value(lp, a, i); k = 1 - k; if(k == 0) fprintf(output, " RGS %s %s", MPSname(get_row_name(lp, i)), formatnumber12((double)a)); else fprintf(output, " %s %s\n", MPSname(get_row_name(lp, i)), formatnumber12((double)a)); } } if(k == 0) fprintf(output, "\n"); putheader = TRUE; for(i = lp->rows + 1; i <= lp->sum; i++) if(!is_splitvar(lp, i - lp->rows)) { j = i - lp->rows; if((lp->orig_lowbo[i] != 0) && (lp->orig_upbo[i] < lp->infinite) && (lp->orig_lowbo[i] == lp->orig_upbo[i])) { a = lp->orig_upbo[i]; a = unscaled_value(lp, a, i); if(putheader) { fprintf(output, "BOUNDS\n"); putheader = FALSE; } fprintf(output, " FX BND %s %s\n", MPSname(get_col_name(lp, j)), formatnumber12((double)a)); } else if(is_binary(lp, j)) { if(putheader) { fprintf(output, "BOUNDS\n"); putheader = FALSE; } fprintf(output, " BV BND %s\n", MPSname(get_col_name(lp, j))); } else if(is_free(lp, j)) { if(putheader) { fprintf(output, "BOUNDS\n"); putheader = FALSE; } fprintf(output, " FR BND %s\n", MPSname(get_col_name(lp, j))); } else { if(lp->orig_upbo[i] < lp->infinite) { a = lp->orig_upbo[i]; a = unscaled_value(lp, a, i); if(putheader) { fprintf(output, "BOUNDS\n"); putheader = FALSE; } if(is_semicont(lp, j)) { if(is_int(lp, j)) fprintf(output, " SI BND %s %s\n", MPSname(get_col_name(lp, j)), formatnumber12((double)a)); else fprintf(output, " SC BND %s %s\n", MPSname(get_col_name(lp, j)), formatnumber12((double)a)); } else fprintf(output, " UP BND %s %s\n", MPSname(get_col_name(lp, j)), formatnumber12((double)a)); } if(lp->orig_lowbo[i] != 0) { a = lp->orig_lowbo[i]; a = unscaled_value(lp, a, i); if(putheader) { fprintf(output, "BOUNDS\n"); putheader = FALSE; } if(lp->orig_lowbo[i] != -lp->infinite) fprintf(output, " LO BND %s %s\n", MPSname(get_col_name(lp, j)), formatnumber12((double)a)); else fprintf(output, " MI BND %s\n", MPSname(get_col_name(lp, j))); } } } /* Write optional SOS section */ putheader = TRUE; for(i = 0; i < SOS_count(lp); i++) { SOSgroup *SOS = lp->SOS; if(putheader) { fprintf(output, "SOS\n"); putheader = FALSE; } fprintf(output, " S%1d SOS %s %s\n", SOS->sos_list[i]->type, MPSname(SOS->sos_list[i]->name), formatnumber12((double) SOS->sos_list[i]->priority)); for(j = 1; j <= SOS->sos_list[i]->size; j++) { fprintf(output, " SOS %s %s\n", MPSname(get_col_name(lp, SOS->sos_list[i]->members[j])), formatnumber12((double) SOS->sos_list[i]->weights[j])); } } fprintf(output, "ENDATA\n"); lp->names_used = names_used; if(filename != NULL) fclose(output); return(ok); } MYBOOL MPS_writehandle(lprec *lp, int typeMPS, FILE *output) { set_outputstream(lp, output); return(MPS_writefile(lp, typeMPS, NULL)); }