alpar@1: /* glpcpx.c (CPLEX LP format routines) */ alpar@1: alpar@1: /*********************************************************************** alpar@1: * This code is part of GLPK (GNU Linear Programming Kit). alpar@1: * alpar@1: * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, alpar@1: * 2009, 2010 Andrew Makhorin, Department for Applied Informatics, alpar@1: * Moscow Aviation Institute, Moscow, Russia. All rights reserved. alpar@1: * E-mail: . alpar@1: * alpar@1: * GLPK is free software: you can redistribute it and/or modify it alpar@1: * under the terms of the GNU General Public License as published by alpar@1: * the Free Software Foundation, either version 3 of the License, or alpar@1: * (at your option) any later version. alpar@1: * alpar@1: * GLPK is distributed in the hope that it will be useful, but WITHOUT alpar@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY alpar@1: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public alpar@1: * License for more details. alpar@1: * alpar@1: * You should have received a copy of the GNU General Public License alpar@1: * along with GLPK. If not, see . alpar@1: ***********************************************************************/ alpar@1: alpar@1: #include "glpapi.h" alpar@1: alpar@1: /*********************************************************************** alpar@1: * NAME alpar@1: * alpar@1: * glp_init_cpxcp - initialize CPLEX LP format control parameters alpar@1: * alpar@1: * SYNOPSIS alpar@1: * alpar@1: * void glp_init_cpxcp(glp_cpxcp *parm): alpar@1: * alpar@1: * The routine glp_init_cpxcp initializes control parameters used by alpar@1: * the CPLEX LP input/output routines glp_read_lp and glp_write_lp with alpar@1: * default values. alpar@1: * alpar@1: * Default values of the control parameters are stored in the glp_cpxcp alpar@1: * structure, which the parameter parm points to. */ alpar@1: alpar@1: void glp_init_cpxcp(glp_cpxcp *parm) alpar@1: { xassert(parm != NULL); alpar@1: return; alpar@1: } alpar@1: alpar@1: static void check_parm(const char *func, const glp_cpxcp *parm) alpar@1: { /* check control parameters */ alpar@1: xassert(func != NULL); alpar@1: xassert(parm != NULL); alpar@1: return; alpar@1: } alpar@1: alpar@1: /*********************************************************************** alpar@1: * NAME alpar@1: * alpar@1: * glp_read_lp - read problem data in CPLEX LP format alpar@1: * alpar@1: * SYNOPSIS alpar@1: * alpar@1: * int glp_read_lp(glp_prob *P, const glp_cpxcp *parm, const char alpar@1: * *fname); alpar@1: * alpar@1: * DESCRIPTION alpar@1: * alpar@1: * The routine glp_read_lp reads problem data in CPLEX LP format from alpar@1: * a text file. alpar@1: * alpar@1: * The parameter parm is a pointer to the structure glp_cpxcp, which alpar@1: * specifies control parameters used by the routine. If parm is NULL, alpar@1: * the routine uses default settings. alpar@1: * alpar@1: * The character string fname specifies a name of the text file to be alpar@1: * read. alpar@1: * alpar@1: * Note that before reading data the current content of the problem alpar@1: * object is completely erased with the routine glp_erase_prob. alpar@1: * alpar@1: * RETURNS alpar@1: * alpar@1: * If the operation was successful, the routine glp_read_lp returns alpar@1: * zero. Otherwise, it prints an error message and returns non-zero. */ alpar@1: alpar@1: struct csa alpar@1: { /* common storage area */ alpar@1: glp_prob *P; alpar@1: /* LP/MIP problem object */ alpar@1: const glp_cpxcp *parm; alpar@1: /* pointer to control parameters */ alpar@1: const char *fname; alpar@1: /* name of input CPLEX LP file */ alpar@1: XFILE *fp; alpar@1: /* stream assigned to input CPLEX LP file */ alpar@1: jmp_buf jump; alpar@1: /* label for go to in case of error */ alpar@1: int count; alpar@1: /* line count */ alpar@1: int c; alpar@1: /* current character or XEOF */ alpar@1: int token; alpar@1: /* current token: */ alpar@1: #define T_EOF 0x00 /* end of file */ alpar@1: #define T_MINIMIZE 0x01 /* keyword 'minimize' */ alpar@1: #define T_MAXIMIZE 0x02 /* keyword 'maximize' */ alpar@1: #define T_SUBJECT_TO 0x03 /* keyword 'subject to' */ alpar@1: #define T_BOUNDS 0x04 /* keyword 'bounds' */ alpar@1: #define T_GENERAL 0x05 /* keyword 'general' */ alpar@1: #define T_INTEGER 0x06 /* keyword 'integer' */ alpar@1: #define T_BINARY 0x07 /* keyword 'binary' */ alpar@1: #define T_END 0x08 /* keyword 'end' */ alpar@1: #define T_NAME 0x09 /* symbolic name */ alpar@1: #define T_NUMBER 0x0A /* numeric constant */ alpar@1: #define T_PLUS 0x0B /* delimiter '+' */ alpar@1: #define T_MINUS 0x0C /* delimiter '-' */ alpar@1: #define T_COLON 0x0D /* delimiter ':' */ alpar@1: #define T_LE 0x0E /* delimiter '<=' */ alpar@1: #define T_GE 0x0F /* delimiter '>=' */ alpar@1: #define T_EQ 0x10 /* delimiter '=' */ alpar@1: char image[255+1]; alpar@1: /* image of current token */ alpar@1: int imlen; alpar@1: /* length of token image */ alpar@1: double value; alpar@1: /* value of numeric constant */ alpar@1: int n_max; alpar@1: /* length of the following five arrays (enlarged automatically, alpar@1: if necessary) */ alpar@1: int *ind; /* int ind[1+n_max]; */ alpar@1: double *val; /* double val[1+n_max]; */ alpar@1: char *flag; /* char flag[1+n_max]; */ alpar@1: /* working arrays used to construct linear forms */ alpar@1: double *lb; /* double lb[1+n_max]; */ alpar@1: double *ub; /* double ub[1+n_max]; */ alpar@1: /* lower and upper bounds of variables (columns) */ alpar@1: }; alpar@1: alpar@1: #define CHAR_SET "!\"#$%&()/,.;?@_`'{}|~" alpar@1: /* characters, which may appear in symbolic names */ alpar@1: alpar@1: static void error(struct csa *csa, const char *fmt, ...) alpar@1: { /* print error message and terminate processing */ alpar@1: va_list arg; alpar@1: xprintf("%s:%d: ", csa->fname, csa->count); alpar@1: va_start(arg, fmt); alpar@1: xvprintf(fmt, arg); alpar@1: va_end(arg); alpar@1: longjmp(csa->jump, 1); alpar@1: /* no return */ alpar@1: } alpar@1: alpar@1: static void warning(struct csa *csa, const char *fmt, ...) alpar@1: { /* print warning message and continue processing */ alpar@1: va_list arg; alpar@1: xprintf("%s:%d: warning: ", csa->fname, csa->count); alpar@1: va_start(arg, fmt); alpar@1: xvprintf(fmt, arg); alpar@1: va_end(arg); alpar@1: return; alpar@1: } alpar@1: alpar@1: static void read_char(struct csa *csa) alpar@1: { /* read next character from input file */ alpar@1: int c; alpar@1: xassert(csa->c != XEOF); alpar@1: if (csa->c == '\n') csa->count++; alpar@1: c = xfgetc(csa->fp); alpar@1: if (c < 0) alpar@1: { if (xferror(csa->fp)) alpar@1: error(csa, "read error - %s\n", xerrmsg()); alpar@1: else if (csa->c == '\n') alpar@1: { csa->count--; alpar@1: c = XEOF; alpar@1: } alpar@1: else alpar@1: { warning(csa, "missing final end of line\n"); alpar@1: c = '\n'; alpar@1: } alpar@1: } alpar@1: else if (c == '\n') alpar@1: ; alpar@1: else if (isspace(c)) alpar@1: c = ' '; alpar@1: else if (iscntrl(c)) alpar@1: error(csa, "invalid control character 0x%02X\n", c); alpar@1: csa->c = c; alpar@1: return; alpar@1: } alpar@1: alpar@1: static void add_char(struct csa *csa) alpar@1: { /* append current character to current token */ alpar@1: if (csa->imlen == sizeof(csa->image)-1) alpar@1: error(csa, "token `%.15s...' too long\n", csa->image); alpar@1: csa->image[csa->imlen++] = (char)csa->c; alpar@1: csa->image[csa->imlen] = '\0'; alpar@1: read_char(csa); alpar@1: return; alpar@1: } alpar@1: alpar@1: static int the_same(char *s1, char *s2) alpar@1: { /* compare two character strings ignoring case sensitivity */ alpar@1: for (; *s1 != '\0'; s1++, s2++) alpar@1: { if (tolower((unsigned char)*s1) != tolower((unsigned char)*s2)) alpar@1: return 0; alpar@1: } alpar@1: return 1; alpar@1: } alpar@1: alpar@1: static void scan_token(struct csa *csa) alpar@1: { /* scan next token */ alpar@1: int flag; alpar@1: csa->token = -1; alpar@1: csa->image[0] = '\0'; alpar@1: csa->imlen = 0; alpar@1: csa->value = 0.0; alpar@1: loop: flag = 0; alpar@1: /* skip non-significant characters */ alpar@1: while (csa->c == ' ') read_char(csa); alpar@1: /* recognize and scan current token */ alpar@1: if (csa->c == XEOF) alpar@1: csa->token = T_EOF; alpar@1: else if (csa->c == '\n') alpar@1: { read_char(csa); alpar@1: /* if the next character is letter, it may begin a keyword */ alpar@1: if (isalpha(csa->c)) alpar@1: { flag = 1; alpar@1: goto name; alpar@1: } alpar@1: goto loop; alpar@1: } alpar@1: else if (csa->c == '\\') alpar@1: { /* comment; ignore everything until end-of-line */ alpar@1: while (csa->c != '\n') read_char(csa); alpar@1: goto loop; alpar@1: } alpar@1: else if (isalpha(csa->c) || csa->c != '.' && strchr(CHAR_SET, alpar@1: csa->c) != NULL) alpar@1: name: { /* symbolic name */ alpar@1: csa->token = T_NAME; alpar@1: while (isalnum(csa->c) || strchr(CHAR_SET, csa->c) != NULL) alpar@1: add_char(csa); alpar@1: if (flag) alpar@1: { /* check for keyword */ alpar@1: if (the_same(csa->image, "minimize")) alpar@1: csa->token = T_MINIMIZE; alpar@1: else if (the_same(csa->image, "minimum")) alpar@1: csa->token = T_MINIMIZE; alpar@1: else if (the_same(csa->image, "min")) alpar@1: csa->token = T_MINIMIZE; alpar@1: else if (the_same(csa->image, "maximize")) alpar@1: csa->token = T_MAXIMIZE; alpar@1: else if (the_same(csa->image, "maximum")) alpar@1: csa->token = T_MAXIMIZE; alpar@1: else if (the_same(csa->image, "max")) alpar@1: csa->token = T_MAXIMIZE; alpar@1: else if (the_same(csa->image, "subject")) alpar@1: { if (csa->c == ' ') alpar@1: { read_char(csa); alpar@1: if (tolower(csa->c) == 't') alpar@1: { csa->token = T_SUBJECT_TO; alpar@1: csa->image[csa->imlen++] = ' '; alpar@1: csa->image[csa->imlen] = '\0'; alpar@1: add_char(csa); alpar@1: if (tolower(csa->c) != 'o') alpar@1: error(csa, "keyword `subject to' incomplete\n"); alpar@1: add_char(csa); alpar@1: if (isalpha(csa->c)) alpar@1: error(csa, "keyword `%s%c...' not recognized\n", alpar@1: csa->image, csa->c); alpar@1: } alpar@1: } alpar@1: } alpar@1: else if (the_same(csa->image, "such")) alpar@1: { if (csa->c == ' ') alpar@1: { read_char(csa); alpar@1: if (tolower(csa->c) == 't') alpar@1: { csa->token = T_SUBJECT_TO; alpar@1: csa->image[csa->imlen++] = ' '; alpar@1: csa->image[csa->imlen] = '\0'; alpar@1: add_char(csa); alpar@1: if (tolower(csa->c) != 'h') alpar@1: err: error(csa, "keyword `such that' incomplete\n"); alpar@1: add_char(csa); alpar@1: if (tolower(csa->c) != 'a') goto err; alpar@1: add_char(csa); alpar@1: if (tolower(csa->c) != 't') goto err; alpar@1: add_char(csa); alpar@1: if (isalpha(csa->c)) alpar@1: error(csa, "keyword `%s%c...' not recognized\n", alpar@1: csa->image, csa->c); alpar@1: } alpar@1: } alpar@1: } alpar@1: else if (the_same(csa->image, "st")) alpar@1: csa->token = T_SUBJECT_TO; alpar@1: else if (the_same(csa->image, "s.t.")) alpar@1: csa->token = T_SUBJECT_TO; alpar@1: else if (the_same(csa->image, "st.")) alpar@1: csa->token = T_SUBJECT_TO; alpar@1: else if (the_same(csa->image, "bounds")) alpar@1: csa->token = T_BOUNDS; alpar@1: else if (the_same(csa->image, "bound")) alpar@1: csa->token = T_BOUNDS; alpar@1: else if (the_same(csa->image, "general")) alpar@1: csa->token = T_GENERAL; alpar@1: else if (the_same(csa->image, "generals")) alpar@1: csa->token = T_GENERAL; alpar@1: else if (the_same(csa->image, "gen")) alpar@1: csa->token = T_GENERAL; alpar@1: else if (the_same(csa->image, "integer")) alpar@1: csa->token = T_INTEGER; alpar@1: else if (the_same(csa->image, "integers")) alpar@1: csa->token = T_INTEGER; alpar@1: else if (the_same(csa->image, "int")) alpar@1: csa->token = T_INTEGER; alpar@1: else if (the_same(csa->image, "binary")) alpar@1: csa->token = T_BINARY; alpar@1: else if (the_same(csa->image, "binaries")) alpar@1: csa->token = T_BINARY; alpar@1: else if (the_same(csa->image, "bin")) alpar@1: csa->token = T_BINARY; alpar@1: else if (the_same(csa->image, "end")) alpar@1: csa->token = T_END; alpar@1: } alpar@1: } alpar@1: else if (isdigit(csa->c) || csa->c == '.') alpar@1: { /* numeric constant */ alpar@1: csa->token = T_NUMBER; alpar@1: /* scan integer part */ alpar@1: while (isdigit(csa->c)) add_char(csa); alpar@1: /* scan optional fractional part (it is mandatory, if there is alpar@1: no integer part) */ alpar@1: if (csa->c == '.') alpar@1: { add_char(csa); alpar@1: if (csa->imlen == 1 && !isdigit(csa->c)) alpar@1: error(csa, "invalid use of decimal point\n"); alpar@1: while (isdigit(csa->c)) add_char(csa); alpar@1: } alpar@1: /* scan optional decimal exponent */ alpar@1: if (csa->c == 'e' || csa->c == 'E') alpar@1: { add_char(csa); alpar@1: if (csa->c == '+' || csa->c == '-') add_char(csa); alpar@1: if (!isdigit(csa->c)) alpar@1: error(csa, "numeric constant `%s' incomplete\n", alpar@1: csa->image); alpar@1: while (isdigit(csa->c)) add_char(csa); alpar@1: } alpar@1: /* convert the numeric constant to floating-point */ alpar@1: if (str2num(csa->image, &csa->value)) alpar@1: error(csa, "numeric constant `%s' out of range\n", alpar@1: csa->image); alpar@1: } alpar@1: else if (csa->c == '+') alpar@1: csa->token = T_PLUS, add_char(csa); alpar@1: else if (csa->c == '-') alpar@1: csa->token = T_MINUS, add_char(csa); alpar@1: else if (csa->c == ':') alpar@1: csa->token = T_COLON, add_char(csa); alpar@1: else if (csa->c == '<') alpar@1: { csa->token = T_LE, add_char(csa); alpar@1: if (csa->c == '=') add_char(csa); alpar@1: } alpar@1: else if (csa->c == '>') alpar@1: { csa->token = T_GE, add_char(csa); alpar@1: if (csa->c == '=') add_char(csa); alpar@1: } alpar@1: else if (csa->c == '=') alpar@1: { csa->token = T_EQ, add_char(csa); alpar@1: if (csa->c == '<') alpar@1: csa->token = T_LE, add_char(csa); alpar@1: else if (csa->c == '>') alpar@1: csa->token = T_GE, add_char(csa); alpar@1: } alpar@1: else alpar@1: error(csa, "character `%c' not recognized\n", csa->c); alpar@1: /* skip non-significant characters */ alpar@1: while (csa->c == ' ') read_char(csa); alpar@1: return; alpar@1: } alpar@1: alpar@1: static int find_col(struct csa *csa, char *name) alpar@1: { /* find column by its symbolic name */ alpar@1: int j; alpar@1: j = glp_find_col(csa->P, name); alpar@1: if (j == 0) alpar@1: { /* not found; create new column */ alpar@1: j = glp_add_cols(csa->P, 1); alpar@1: glp_set_col_name(csa->P, j, name); alpar@1: /* enlarge working arrays, if necessary */ alpar@1: if (csa->n_max < j) alpar@1: { int n_max = csa->n_max; alpar@1: int *ind = csa->ind; alpar@1: double *val = csa->val; alpar@1: char *flag = csa->flag; alpar@1: double *lb = csa->lb; alpar@1: double *ub = csa->ub; alpar@1: csa->n_max += csa->n_max; alpar@1: csa->ind = xcalloc(1+csa->n_max, sizeof(int)); alpar@1: memcpy(&csa->ind[1], &ind[1], n_max * sizeof(int)); alpar@1: xfree(ind); alpar@1: csa->val = xcalloc(1+csa->n_max, sizeof(double)); alpar@1: memcpy(&csa->val[1], &val[1], n_max * sizeof(double)); alpar@1: xfree(val); alpar@1: csa->flag = xcalloc(1+csa->n_max, sizeof(char)); alpar@1: memset(&csa->flag[1], 0, csa->n_max * sizeof(char)); alpar@1: memcpy(&csa->flag[1], &flag[1], n_max * sizeof(char)); alpar@1: xfree(flag); alpar@1: csa->lb = xcalloc(1+csa->n_max, sizeof(double)); alpar@1: memcpy(&csa->lb[1], &lb[1], n_max * sizeof(double)); alpar@1: xfree(lb); alpar@1: csa->ub = xcalloc(1+csa->n_max, sizeof(double)); alpar@1: memcpy(&csa->ub[1], &ub[1], n_max * sizeof(double)); alpar@1: xfree(ub); alpar@1: } alpar@1: csa->lb[j] = +DBL_MAX, csa->ub[j] = -DBL_MAX; alpar@1: } alpar@1: return j; alpar@1: } alpar@1: alpar@1: /*********************************************************************** alpar@1: * parse_linear_form - parse linear form alpar@1: * alpar@1: * This routine parses the linear form using the following syntax: alpar@1: * alpar@1: * ::= alpar@1: * ::= alpar@1: * ::= | alpar@1: * ::= | + | - | alpar@1: * + | - alpar@1: * alpar@1: * The routine returns the number of terms in the linear form. */ alpar@1: alpar@1: static int parse_linear_form(struct csa *csa) alpar@1: { int j, k, len = 0, newlen; alpar@1: double s, coef; alpar@1: loop: /* parse an optional sign */ alpar@1: if (csa->token == T_PLUS) alpar@1: s = +1.0, scan_token(csa); alpar@1: else if (csa->token == T_MINUS) alpar@1: s = -1.0, scan_token(csa); alpar@1: else alpar@1: s = +1.0; alpar@1: /* parse an optional coefficient */ alpar@1: if (csa->token == T_NUMBER) alpar@1: coef = csa->value, scan_token(csa); alpar@1: else alpar@1: coef = 1.0; alpar@1: /* parse a variable name */ alpar@1: if (csa->token != T_NAME) alpar@1: error(csa, "missing variable name\n"); alpar@1: /* find the corresponding column */ alpar@1: j = find_col(csa, csa->image); alpar@1: /* check if the variable is already used in the linear form */ alpar@1: if (csa->flag[j]) alpar@1: error(csa, "multiple use of variable `%s' not allowed\n", alpar@1: csa->image); alpar@1: /* add new term to the linear form */ alpar@1: len++, csa->ind[len] = j, csa->val[len] = s * coef; alpar@1: /* and mark that the variable is used in the linear form */ alpar@1: csa->flag[j] = 1; alpar@1: scan_token(csa); alpar@1: /* if the next token is a sign, there is another term */ alpar@1: if (csa->token == T_PLUS || csa->token == T_MINUS) goto loop; alpar@1: /* clear marks of the variables used in the linear form */ alpar@1: for (k = 1; k <= len; k++) csa->flag[csa->ind[k]] = 0; alpar@1: /* remove zero coefficients */ alpar@1: newlen = 0; alpar@1: for (k = 1; k <= len; k++) alpar@1: { if (csa->val[k] != 0.0) alpar@1: { newlen++; alpar@1: csa->ind[newlen] = csa->ind[k]; alpar@1: csa->val[newlen] = csa->val[k]; alpar@1: } alpar@1: } alpar@1: return newlen; alpar@1: } alpar@1: alpar@1: /*********************************************************************** alpar@1: * parse_objective - parse objective function alpar@1: * alpar@1: * This routine parses definition of the objective function using the alpar@1: * following syntax: alpar@1: * alpar@1: * ::= minimize | minimum | min | maximize | maximum | max alpar@1: * ::= | : alpar@1: * ::= */ alpar@1: alpar@1: static void parse_objective(struct csa *csa) alpar@1: { /* parse objective sense */ alpar@1: int k, len; alpar@1: /* parse the keyword 'minimize' or 'maximize' */ alpar@1: if (csa->token == T_MINIMIZE) alpar@1: glp_set_obj_dir(csa->P, GLP_MIN); alpar@1: else if (csa->token == T_MAXIMIZE) alpar@1: glp_set_obj_dir(csa->P, GLP_MAX); alpar@1: else alpar@1: xassert(csa != csa); alpar@1: scan_token(csa); alpar@1: /* parse objective name */ alpar@1: if (csa->token == T_NAME && csa->c == ':') alpar@1: { /* objective name is followed by a colon */ alpar@1: glp_set_obj_name(csa->P, csa->image); alpar@1: scan_token(csa); alpar@1: xassert(csa->token == T_COLON); alpar@1: scan_token(csa); alpar@1: } alpar@1: else alpar@1: { /* objective name is not specified; use default */ alpar@1: glp_set_obj_name(csa->P, "obj"); alpar@1: } alpar@1: /* parse linear form */ alpar@1: len = parse_linear_form(csa); alpar@1: for (k = 1; k <= len; k++) alpar@1: glp_set_obj_coef(csa->P, csa->ind[k], csa->val[k]); alpar@1: return; alpar@1: } alpar@1: alpar@1: /*********************************************************************** alpar@1: * parse_constraints - parse constraints section alpar@1: * alpar@1: * This routine parses the constraints section using the following alpar@1: * syntax: alpar@1: * alpar@1: * ::= | : alpar@1: * ::= < | <= | =< | > | >= | => | = alpar@1: * ::= | + | alpar@1: * - alpar@1: * ::= alpar@1: * alpar@1: * ::= subject to | such that | st | s.t. | st. alpar@1: * ::= | alpar@1: * */ alpar@1: alpar@1: static void parse_constraints(struct csa *csa) alpar@1: { int i, len, type; alpar@1: double s; alpar@1: /* parse the keyword 'subject to' */ alpar@1: xassert(csa->token == T_SUBJECT_TO); alpar@1: scan_token(csa); alpar@1: loop: /* create new row (constraint) */ alpar@1: i = glp_add_rows(csa->P, 1); alpar@1: /* parse row name */ alpar@1: if (csa->token == T_NAME && csa->c == ':') alpar@1: { /* row name is followed by a colon */ alpar@1: if (glp_find_row(csa->P, csa->image) != 0) alpar@1: error(csa, "constraint `%s' multiply defined\n", alpar@1: csa->image); alpar@1: glp_set_row_name(csa->P, i, csa->image); alpar@1: scan_token(csa); alpar@1: xassert(csa->token == T_COLON); alpar@1: scan_token(csa); alpar@1: } alpar@1: else alpar@1: { /* row name is not specified; use default */ alpar@1: char name[50]; alpar@1: sprintf(name, "r.%d", csa->count); alpar@1: glp_set_row_name(csa->P, i, name); alpar@1: } alpar@1: /* parse linear form */ alpar@1: len = parse_linear_form(csa); alpar@1: glp_set_mat_row(csa->P, i, len, csa->ind, csa->val); alpar@1: /* parse constraint sense */ alpar@1: if (csa->token == T_LE) alpar@1: type = GLP_UP, scan_token(csa); alpar@1: else if (csa->token == T_GE) alpar@1: type = GLP_LO, scan_token(csa); alpar@1: else if (csa->token == T_EQ) alpar@1: type = GLP_FX, scan_token(csa); alpar@1: else alpar@1: error(csa, "missing constraint sense\n"); alpar@1: /* parse right-hand side */ alpar@1: if (csa->token == T_PLUS) alpar@1: s = +1.0, scan_token(csa); alpar@1: else if (csa->token == T_MINUS) alpar@1: s = -1.0, scan_token(csa); alpar@1: else alpar@1: s = +1.0; alpar@1: if (csa->token != T_NUMBER) alpar@1: error(csa, "missing right-hand side\n"); alpar@1: glp_set_row_bnds(csa->P, i, type, s * csa->value, s * csa->value); alpar@1: /* the rest of the current line must be empty */ alpar@1: if (!(csa->c == '\n' || csa->c == XEOF)) alpar@1: error(csa, "invalid symbol(s) beyond right-hand side\n"); alpar@1: scan_token(csa); alpar@1: /* if the next token is a sign, numeric constant, or a symbolic alpar@1: name, here is another constraint */ alpar@1: if (csa->token == T_PLUS || csa->token == T_MINUS || alpar@1: csa->token == T_NUMBER || csa->token == T_NAME) goto loop; alpar@1: return; alpar@1: } alpar@1: alpar@1: static void set_lower_bound(struct csa *csa, int j, double lb) alpar@1: { /* set lower bound of j-th variable */ alpar@1: if (csa->lb[j] != +DBL_MAX) alpar@1: { warning(csa, "lower bound of variable `%s' redefined\n", alpar@1: glp_get_col_name(csa->P, j)); alpar@1: } alpar@1: csa->lb[j] = lb; alpar@1: return; alpar@1: } alpar@1: alpar@1: static void set_upper_bound(struct csa *csa, int j, double ub) alpar@1: { /* set upper bound of j-th variable */ alpar@1: if (csa->ub[j] != -DBL_MAX) alpar@1: { warning(csa, "upper bound of variable `%s' redefined\n", alpar@1: glp_get_col_name(csa->P, j)); alpar@1: } alpar@1: csa->ub[j] = ub; alpar@1: return; alpar@1: } alpar@1: alpar@1: /*********************************************************************** alpar@1: * parse_bounds - parse bounds section alpar@1: * alpar@1: * This routine parses the bounds section using the following syntax: alpar@1: * alpar@1: * ::= alpar@1: * ::= infinity | inf alpar@1: * ::= | + | alpar@1: * - | + | - alpar@1: * ::= < | <= | =< alpar@1: * ::= > | >= | => alpar@1: * ::= | alpar@1: * | | alpar@1: * | = | free alpar@1: * ::= bounds | bound alpar@1: * ::= | alpar@1: * */ alpar@1: alpar@1: static void parse_bounds(struct csa *csa) alpar@1: { int j, lb_flag; alpar@1: double lb, s; alpar@1: /* parse the keyword 'bounds' */ alpar@1: xassert(csa->token == T_BOUNDS); alpar@1: scan_token(csa); alpar@1: loop: /* bound definition can start with a sign, numeric constant, or alpar@1: a symbolic name */ alpar@1: if (!(csa->token == T_PLUS || csa->token == T_MINUS || alpar@1: csa->token == T_NUMBER || csa->token == T_NAME)) goto done; alpar@1: /* parse bound definition */ alpar@1: if (csa->token == T_PLUS || csa->token == T_MINUS) alpar@1: { /* parse signed lower bound */ alpar@1: lb_flag = 1; alpar@1: s = (csa->token == T_PLUS ? +1.0 : -1.0); alpar@1: scan_token(csa); alpar@1: if (csa->token == T_NUMBER) alpar@1: lb = s * csa->value, scan_token(csa); alpar@1: else if (the_same(csa->image, "infinity") || alpar@1: the_same(csa->image, "inf")) alpar@1: { if (s > 0.0) alpar@1: error(csa, "invalid use of `+inf' as lower bound\n"); alpar@1: lb = -DBL_MAX, scan_token(csa); alpar@1: } alpar@1: else alpar@1: error(csa, "missing lower bound\n"); alpar@1: } alpar@1: else if (csa->token == T_NUMBER) alpar@1: { /* parse unsigned lower bound */ alpar@1: lb_flag = 1; alpar@1: lb = csa->value, scan_token(csa); alpar@1: } alpar@1: else alpar@1: { /* lower bound is not specified */ alpar@1: lb_flag = 0; alpar@1: } alpar@1: /* parse the token that should follow the lower bound */ alpar@1: if (lb_flag) alpar@1: { if (csa->token != T_LE) alpar@1: error(csa, "missing `<', `<=', or `=<' after lower bound\n") alpar@1: ; alpar@1: scan_token(csa); alpar@1: } alpar@1: /* parse variable name */ alpar@1: if (csa->token != T_NAME) alpar@1: error(csa, "missing variable name\n"); alpar@1: j = find_col(csa, csa->image); alpar@1: /* set lower bound */ alpar@1: if (lb_flag) set_lower_bound(csa, j, lb); alpar@1: scan_token(csa); alpar@1: /* parse the context that follows the variable name */ alpar@1: if (csa->token == T_LE) alpar@1: { /* parse upper bound */ alpar@1: scan_token(csa); alpar@1: if (csa->token == T_PLUS || csa->token == T_MINUS) alpar@1: { /* parse signed upper bound */ alpar@1: s = (csa->token == T_PLUS ? +1.0 : -1.0); alpar@1: scan_token(csa); alpar@1: if (csa->token == T_NUMBER) alpar@1: { set_upper_bound(csa, j, s * csa->value); alpar@1: scan_token(csa); alpar@1: } alpar@1: else if (the_same(csa->image, "infinity") || alpar@1: the_same(csa->image, "inf")) alpar@1: { if (s < 0.0) alpar@1: error(csa, "invalid use of `-inf' as upper bound\n"); alpar@1: set_upper_bound(csa, j, +DBL_MAX); alpar@1: scan_token(csa); alpar@1: } alpar@1: else alpar@1: error(csa, "missing upper bound\n"); alpar@1: } alpar@1: else if (csa->token == T_NUMBER) alpar@1: { /* parse unsigned upper bound */ alpar@1: set_upper_bound(csa, j, csa->value); alpar@1: scan_token(csa); alpar@1: } alpar@1: else alpar@1: error(csa, "missing upper bound\n"); alpar@1: } alpar@1: else if (csa->token == T_GE) alpar@1: { /* parse lower bound */ alpar@1: if (lb_flag) alpar@1: { /* the context '... <= x >= ...' is invalid */ alpar@1: error(csa, "invalid bound definition\n"); alpar@1: } alpar@1: scan_token(csa); alpar@1: if (csa->token == T_PLUS || csa->token == T_MINUS) alpar@1: { /* parse signed lower bound */ alpar@1: s = (csa->token == T_PLUS ? +1.0 : -1.0); alpar@1: scan_token(csa); alpar@1: if (csa->token == T_NUMBER) alpar@1: { set_lower_bound(csa, j, s * csa->value); alpar@1: scan_token(csa); alpar@1: } alpar@1: else if (the_same(csa->image, "infinity") || alpar@1: the_same(csa->image, "inf") == 0) alpar@1: { if (s > 0.0) alpar@1: error(csa, "invalid use of `+inf' as lower bound\n"); alpar@1: set_lower_bound(csa, j, -DBL_MAX); alpar@1: scan_token(csa); alpar@1: } alpar@1: else alpar@1: error(csa, "missing lower bound\n"); alpar@1: } alpar@1: else if (csa->token == T_NUMBER) alpar@1: { /* parse unsigned lower bound */ alpar@1: set_lower_bound(csa, j, csa->value); alpar@1: scan_token(csa); alpar@1: } alpar@1: else alpar@1: error(csa, "missing lower bound\n"); alpar@1: } alpar@1: else if (csa->token == T_EQ) alpar@1: { /* parse fixed value */ alpar@1: if (lb_flag) alpar@1: { /* the context '... <= x = ...' is invalid */ alpar@1: error(csa, "invalid bound definition\n"); alpar@1: } alpar@1: scan_token(csa); alpar@1: if (csa->token == T_PLUS || csa->token == T_MINUS) alpar@1: { /* parse signed fixed value */ alpar@1: s = (csa->token == T_PLUS ? +1.0 : -1.0); alpar@1: scan_token(csa); alpar@1: if (csa->token == T_NUMBER) alpar@1: { set_lower_bound(csa, j, s * csa->value); alpar@1: set_upper_bound(csa, j, s * csa->value); alpar@1: scan_token(csa); alpar@1: } alpar@1: else alpar@1: error(csa, "missing fixed value\n"); alpar@1: } alpar@1: else if (csa->token == T_NUMBER) alpar@1: { /* parse unsigned fixed value */ alpar@1: set_lower_bound(csa, j, csa->value); alpar@1: set_upper_bound(csa, j, csa->value); alpar@1: scan_token(csa); alpar@1: } alpar@1: else alpar@1: error(csa, "missing fixed value\n"); alpar@1: } alpar@1: else if (the_same(csa->image, "free")) alpar@1: { /* parse the keyword 'free' */ alpar@1: if (lb_flag) alpar@1: { /* the context '... <= x free ...' is invalid */ alpar@1: error(csa, "invalid bound definition\n"); alpar@1: } alpar@1: set_lower_bound(csa, j, -DBL_MAX); alpar@1: set_upper_bound(csa, j, +DBL_MAX); alpar@1: scan_token(csa); alpar@1: } alpar@1: else if (!lb_flag) alpar@1: { /* neither lower nor upper bounds are specified */ alpar@1: error(csa, "invalid bound definition\n"); alpar@1: } alpar@1: goto loop; alpar@1: done: return; alpar@1: } alpar@1: alpar@1: /*********************************************************************** alpar@1: * parse_integer - parse general, integer, or binary section alpar@1: * alpar@1: * ::= alpar@1: * ::= general | generals | gen alpar@1: * ::= integer | integers | int alpar@1: * ::= binary | binaries | bin alpar@1: *
::= alpar@1: * ::=
| alpar@1: * */ alpar@1: alpar@1: static void parse_integer(struct csa *csa) alpar@1: { int j, binary; alpar@1: /* parse the keyword 'general', 'integer', or 'binary' */ alpar@1: if (csa->token == T_GENERAL) alpar@1: binary = 0, scan_token(csa); alpar@1: else if (csa->token == T_INTEGER) alpar@1: binary = 0, scan_token(csa); alpar@1: else if (csa->token == T_BINARY) alpar@1: binary = 1, scan_token(csa); alpar@1: else alpar@1: xassert(csa != csa); alpar@1: /* parse list of variables (may be empty) */ alpar@1: while (csa->token == T_NAME) alpar@1: { /* find the corresponding column */ alpar@1: j = find_col(csa, csa->image); alpar@1: /* change kind of the variable */ alpar@1: glp_set_col_kind(csa->P, j, GLP_IV); alpar@1: /* set 0-1 bounds for the binary variable */ alpar@1: if (binary) alpar@1: { set_lower_bound(csa, j, 0.0); alpar@1: set_upper_bound(csa, j, 1.0); alpar@1: } alpar@1: scan_token(csa); alpar@1: } alpar@1: return; alpar@1: } alpar@1: alpar@1: int glp_read_lp(glp_prob *P, const glp_cpxcp *parm, const char *fname) alpar@1: { /* read problem data in CPLEX LP format */ alpar@1: glp_cpxcp _parm; alpar@1: struct csa _csa, *csa = &_csa; alpar@1: int ret; alpar@1: xprintf("Reading problem data from `%s'...\n", fname); alpar@1: if (parm == NULL) alpar@1: glp_init_cpxcp(&_parm), parm = &_parm; alpar@1: /* check control parameters */ alpar@1: check_parm("glp_read_lp", parm); alpar@1: /* initialize common storage area */ alpar@1: csa->P = P; alpar@1: csa->parm = parm; alpar@1: csa->fname = fname; alpar@1: csa->fp = NULL; alpar@1: if (setjmp(csa->jump)) alpar@1: { ret = 1; alpar@1: goto done; alpar@1: } alpar@1: csa->count = 0; alpar@1: csa->c = '\n'; alpar@1: csa->token = T_EOF; alpar@1: csa->image[0] = '\0'; alpar@1: csa->imlen = 0; alpar@1: csa->value = 0.0; alpar@1: csa->n_max = 100; alpar@1: csa->ind = xcalloc(1+csa->n_max, sizeof(int)); alpar@1: csa->val = xcalloc(1+csa->n_max, sizeof(double)); alpar@1: csa->flag = xcalloc(1+csa->n_max, sizeof(char)); alpar@1: memset(&csa->flag[1], 0, csa->n_max * sizeof(char)); alpar@1: csa->lb = xcalloc(1+csa->n_max, sizeof(double)); alpar@1: csa->ub = xcalloc(1+csa->n_max, sizeof(double)); alpar@1: /* erase problem object */ alpar@1: glp_erase_prob(P); alpar@1: glp_create_index(P); alpar@1: /* open input CPLEX LP file */ alpar@1: csa->fp = xfopen(fname, "r"); alpar@1: if (csa->fp == NULL) alpar@1: { xprintf("Unable to open `%s' - %s\n", fname, xerrmsg()); alpar@1: ret = 1; alpar@1: goto done; alpar@1: } alpar@1: /* scan very first token */ alpar@1: scan_token(csa); alpar@1: /* parse definition of the objective function */ alpar@1: if (!(csa->token == T_MINIMIZE || csa->token == T_MAXIMIZE)) alpar@1: error(csa, "`minimize' or `maximize' keyword missing\n"); alpar@1: parse_objective(csa); alpar@1: /* parse constraints section */ alpar@1: if (csa->token != T_SUBJECT_TO) alpar@1: error(csa, "constraints section missing\n"); alpar@1: parse_constraints(csa); alpar@1: /* parse optional bounds section */ alpar@1: if (csa->token == T_BOUNDS) parse_bounds(csa); alpar@1: /* parse optional general, integer, and binary sections */ alpar@1: while (csa->token == T_GENERAL || alpar@1: csa->token == T_INTEGER || alpar@1: csa->token == T_BINARY) parse_integer(csa); alpar@1: /* check for the keyword 'end' */ alpar@1: if (csa->token == T_END) alpar@1: scan_token(csa); alpar@1: else if (csa->token == T_EOF) alpar@1: warning(csa, "keyword `end' missing\n"); alpar@1: else alpar@1: error(csa, "symbol `%s' in wrong position\n", csa->image); alpar@1: /* nothing must follow the keyword 'end' (except comments) */ alpar@1: if (csa->token != T_EOF) alpar@1: error(csa, "extra symbol(s) detected beyond `end'\n"); alpar@1: /* set bounds of variables */ alpar@1: { int j, type; alpar@1: double lb, ub; alpar@1: for (j = 1; j <= P->n; j++) alpar@1: { lb = csa->lb[j]; alpar@1: ub = csa->ub[j]; alpar@1: if (lb == +DBL_MAX) lb = 0.0; /* default lb */ alpar@1: if (ub == -DBL_MAX) ub = +DBL_MAX; /* default ub */ alpar@1: if (lb == -DBL_MAX && ub == +DBL_MAX) alpar@1: type = GLP_FR; alpar@1: else if (ub == +DBL_MAX) alpar@1: type = GLP_LO; alpar@1: else if (lb == -DBL_MAX) alpar@1: type = GLP_UP; alpar@1: else if (lb != ub) alpar@1: type = GLP_DB; alpar@1: else alpar@1: type = GLP_FX; alpar@1: glp_set_col_bnds(csa->P, j, type, lb, ub); alpar@1: } alpar@1: } alpar@1: /* print some statistics */ alpar@1: xprintf("%d row%s, %d column%s, %d non-zero%s\n", alpar@1: P->m, P->m == 1 ? "" : "s", P->n, P->n == 1 ? "" : "s", alpar@1: P->nnz, P->nnz == 1 ? "" : "s"); alpar@1: if (glp_get_num_int(P) > 0) alpar@1: { int ni = glp_get_num_int(P); alpar@1: int nb = glp_get_num_bin(P); alpar@1: if (ni == 1) alpar@1: { if (nb == 0) alpar@1: xprintf("One variable is integer\n"); alpar@1: else alpar@1: xprintf("One variable is binary\n"); alpar@1: } alpar@1: else alpar@1: { xprintf("%d integer variables, ", ni); alpar@1: if (nb == 0) alpar@1: xprintf("none"); alpar@1: else if (nb == 1) alpar@1: xprintf("one"); alpar@1: else if (nb == ni) alpar@1: xprintf("all"); alpar@1: else alpar@1: xprintf("%d", nb); alpar@1: xprintf(" of which %s binary\n", nb == 1 ? "is" : "are"); alpar@1: } alpar@1: } alpar@1: xprintf("%d lines were read\n", csa->count); alpar@1: /* problem data has been successfully read */ alpar@1: glp_delete_index(P); alpar@1: glp_sort_matrix(P); alpar@1: ret = 0; alpar@1: done: if (csa->fp != NULL) xfclose(csa->fp); alpar@1: xfree(csa->ind); alpar@1: xfree(csa->val); alpar@1: xfree(csa->flag); alpar@1: xfree(csa->lb); alpar@1: xfree(csa->ub); alpar@1: if (ret != 0) glp_erase_prob(P); alpar@1: return ret; alpar@1: } alpar@1: alpar@1: /*********************************************************************** alpar@1: * NAME alpar@1: * alpar@1: * glp_write_lp - write problem data in CPLEX LP format alpar@1: * alpar@1: * SYNOPSIS alpar@1: * alpar@1: * int glp_write_lp(glp_prob *P, const glp_cpxcp *parm, const char alpar@1: * *fname); alpar@1: * alpar@1: * DESCRIPTION alpar@1: * alpar@1: * The routine glp_write_lp writes problem data in CPLEX LP format to alpar@1: * a text file. alpar@1: * alpar@1: * The parameter parm is a pointer to the structure glp_cpxcp, which alpar@1: * specifies control parameters used by the routine. If parm is NULL, alpar@1: * the routine uses default settings. alpar@1: * alpar@1: * The character string fname specifies a name of the text file to be alpar@1: * written. alpar@1: * alpar@1: * RETURNS alpar@1: * alpar@1: * If the operation was successful, the routine glp_write_lp returns alpar@1: * zero. Otherwise, it prints an error message and returns non-zero. */ alpar@1: alpar@1: #define csa csa1 alpar@1: alpar@1: struct csa alpar@1: { /* common storage area */ alpar@1: glp_prob *P; alpar@1: /* pointer to problem object */ alpar@1: const glp_cpxcp *parm; alpar@1: /* pointer to control parameters */ alpar@1: }; alpar@1: alpar@1: static int check_name(char *name) alpar@1: { /* check if specified name is valid for CPLEX LP format */ alpar@1: if (*name == '.') return 1; alpar@1: if (isdigit((unsigned char)*name)) return 1; alpar@1: for (; *name; name++) alpar@1: { if (!isalnum((unsigned char)*name) && alpar@1: strchr(CHAR_SET, (unsigned char)*name) == NULL) return 1; alpar@1: } alpar@1: return 0; /* name is ok */ alpar@1: } alpar@1: alpar@1: static void adjust_name(char *name) alpar@1: { /* attempt to adjust specified name to make it valid for CPLEX LP alpar@1: format */ alpar@1: for (; *name; name++) alpar@1: { if (*name == ' ') alpar@1: *name = '_'; alpar@1: else if (*name == '-') alpar@1: *name = '~'; alpar@1: else if (*name == '[') alpar@1: *name = '('; alpar@1: else if (*name == ']') alpar@1: *name = ')'; alpar@1: } alpar@1: return; alpar@1: } alpar@1: alpar@1: static char *row_name(struct csa *csa, int i, char rname[255+1]) alpar@1: { /* construct symbolic name of i-th row (constraint) */ alpar@1: const char *name; alpar@1: if (i == 0) alpar@1: name = glp_get_obj_name(csa->P); alpar@1: else alpar@1: name = glp_get_row_name(csa->P, i); alpar@1: if (name == NULL) goto fake; alpar@1: strcpy(rname, name); alpar@1: adjust_name(rname); alpar@1: if (check_name(rname)) goto fake; alpar@1: return rname; alpar@1: fake: if (i == 0) alpar@1: strcpy(rname, "obj"); alpar@1: else alpar@1: sprintf(rname, "r_%d", i); alpar@1: return rname; alpar@1: } alpar@1: alpar@1: static char *col_name(struct csa *csa, int j, char cname[255+1]) alpar@1: { /* construct symbolic name of j-th column (variable) */ alpar@1: const char *name; alpar@1: name = glp_get_col_name(csa->P, j); alpar@1: if (name == NULL) goto fake; alpar@1: strcpy(cname, name); alpar@1: adjust_name(cname); alpar@1: if (check_name(cname)) goto fake; alpar@1: return cname; alpar@1: fake: sprintf(cname, "x_%d", j); alpar@1: return cname; alpar@1: } alpar@1: alpar@1: int glp_write_lp(glp_prob *P, const glp_cpxcp *parm, const char *fname) alpar@1: { /* write problem data in CPLEX LP format */ alpar@1: glp_cpxcp _parm; alpar@1: struct csa _csa, *csa = &_csa; alpar@1: XFILE *fp; alpar@1: GLPROW *row; alpar@1: GLPCOL *col; alpar@1: GLPAIJ *aij; alpar@1: int i, j, len, flag, count, ret; alpar@1: char line[1000+1], term[500+1], name[255+1]; alpar@1: xprintf("Writing problem data to `%s'...\n", fname); alpar@1: if (parm == NULL) alpar@1: glp_init_cpxcp(&_parm), parm = &_parm; alpar@1: /* check control parameters */ alpar@1: check_parm("glp_write_lp", parm); alpar@1: /* initialize common storage area */ alpar@1: csa->P = P; alpar@1: csa->parm = parm; alpar@1: /* create output CPLEX LP file */ alpar@1: fp = xfopen(fname, "w"), count = 0; alpar@1: if (fp == NULL) alpar@1: { xprintf("Unable to create `%s' - %s\n", fname, xerrmsg()); alpar@1: ret = 1; alpar@1: goto done; alpar@1: } alpar@1: /* write problem name */ alpar@1: xfprintf(fp, "\\* Problem: %s *\\\n", alpar@1: P->name == NULL ? "Unknown" : P->name), count++; alpar@1: xfprintf(fp, "\n"), count++; alpar@1: /* the problem should contain at least one row and one column */ alpar@1: if (!(P->m > 0 && P->n > 0)) alpar@1: { xprintf("Warning: problem has no rows/columns\n"); alpar@1: xfprintf(fp, "\\* WARNING: PROBLEM HAS NO ROWS/COLUMNS *\\\n"), alpar@1: count++; alpar@1: xfprintf(fp, "\n"), count++; alpar@1: goto skip; alpar@1: } alpar@1: /* write the objective function definition */ alpar@1: if (P->dir == GLP_MIN) alpar@1: xfprintf(fp, "Minimize\n"), count++; alpar@1: else if (P->dir == GLP_MAX) alpar@1: xfprintf(fp, "Maximize\n"), count++; alpar@1: else alpar@1: xassert(P != P); alpar@1: row_name(csa, 0, name); alpar@1: sprintf(line, " %s:", name); alpar@1: len = 0; alpar@1: for (j = 1; j <= P->n; j++) alpar@1: { col = P->col[j]; alpar@1: if (col->coef != 0.0 || col->ptr == NULL) alpar@1: { len++; alpar@1: col_name(csa, j, name); alpar@1: if (col->coef == 0.0) alpar@1: sprintf(term, " + 0 %s", name); /* empty column */ alpar@1: else if (col->coef == +1.0) alpar@1: sprintf(term, " + %s", name); alpar@1: else if (col->coef == -1.0) alpar@1: sprintf(term, " - %s", name); alpar@1: else if (col->coef > 0.0) alpar@1: sprintf(term, " + %.*g %s", DBL_DIG, +col->coef, name); alpar@1: else alpar@1: sprintf(term, " - %.*g %s", DBL_DIG, -col->coef, name); alpar@1: if (strlen(line) + strlen(term) > 72) alpar@1: xfprintf(fp, "%s\n", line), line[0] = '\0', count++; alpar@1: strcat(line, term); alpar@1: } alpar@1: } alpar@1: if (len == 0) alpar@1: { /* empty objective */ alpar@1: sprintf(term, " 0 %s", col_name(csa, 1, name)); alpar@1: strcat(line, term); alpar@1: } alpar@1: xfprintf(fp, "%s\n", line), count++; alpar@1: if (P->c0 != 0.0) alpar@1: xfprintf(fp, "\\* constant term = %.*g *\\\n", DBL_DIG, P->c0), alpar@1: count++; alpar@1: xfprintf(fp, "\n"), count++; alpar@1: /* write the constraints section */ alpar@1: xfprintf(fp, "Subject To\n"), count++; alpar@1: for (i = 1; i <= P->m; i++) alpar@1: { row = P->row[i]; alpar@1: if (row->type == GLP_FR) continue; /* skip free row */ alpar@1: row_name(csa, i, name); alpar@1: sprintf(line, " %s:", name); alpar@1: /* linear form */ alpar@1: for (aij = row->ptr; aij != NULL; aij = aij->r_next) alpar@1: { col_name(csa, aij->col->j, name); alpar@1: if (aij->val == +1.0) alpar@1: sprintf(term, " + %s", name); alpar@1: else if (aij->val == -1.0) alpar@1: sprintf(term, " - %s", name); alpar@1: else if (aij->val > 0.0) alpar@1: sprintf(term, " + %.*g %s", DBL_DIG, +aij->val, name); alpar@1: else alpar@1: sprintf(term, " - %.*g %s", DBL_DIG, -aij->val, name); alpar@1: if (strlen(line) + strlen(term) > 72) alpar@1: xfprintf(fp, "%s\n", line), line[0] = '\0', count++; alpar@1: strcat(line, term); alpar@1: } alpar@1: if (row->type == GLP_DB) alpar@1: { /* double-bounded (ranged) constraint */ alpar@1: sprintf(term, " - ~r_%d", i); alpar@1: if (strlen(line) + strlen(term) > 72) alpar@1: xfprintf(fp, "%s\n", line), line[0] = '\0', count++; alpar@1: strcat(line, term); alpar@1: } alpar@1: else if (row->ptr == NULL) alpar@1: { /* empty constraint */ alpar@1: sprintf(term, " 0 %s", col_name(csa, 1, name)); alpar@1: strcat(line, term); alpar@1: } alpar@1: /* right hand-side */ alpar@1: if (row->type == GLP_LO) alpar@1: sprintf(term, " >= %.*g", DBL_DIG, row->lb); alpar@1: else if (row->type == GLP_UP) alpar@1: sprintf(term, " <= %.*g", DBL_DIG, row->ub); alpar@1: else if (row->type == GLP_DB || row->type == GLP_FX) alpar@1: sprintf(term, " = %.*g", DBL_DIG, row->lb); alpar@1: else alpar@1: xassert(row != row); alpar@1: if (strlen(line) + strlen(term) > 72) alpar@1: xfprintf(fp, "%s\n", line), line[0] = '\0', count++; alpar@1: strcat(line, term); alpar@1: xfprintf(fp, "%s\n", line), count++; alpar@1: } alpar@1: xfprintf(fp, "\n"), count++; alpar@1: /* write the bounds section */ alpar@1: flag = 0; alpar@1: for (i = 1; i <= P->m; i++) alpar@1: { row = P->row[i]; alpar@1: if (row->type != GLP_DB) continue; alpar@1: if (!flag) alpar@1: xfprintf(fp, "Bounds\n"), flag = 1, count++; alpar@1: xfprintf(fp, " 0 <= ~r_%d <= %.*g\n", alpar@1: i, DBL_DIG, row->ub - row->lb), count++; alpar@1: } alpar@1: for (j = 1; j <= P->n; j++) alpar@1: { col = P->col[j]; alpar@1: if (col->type == GLP_LO && col->lb == 0.0) continue; alpar@1: if (!flag) alpar@1: xfprintf(fp, "Bounds\n"), flag = 1, count++; alpar@1: col_name(csa, j, name); alpar@1: if (col->type == GLP_FR) alpar@1: xfprintf(fp, " %s free\n", name), count++; alpar@1: else if (col->type == GLP_LO) alpar@1: xfprintf(fp, " %s >= %.*g\n", alpar@1: name, DBL_DIG, col->lb), count++; alpar@1: else if (col->type == GLP_UP) alpar@1: xfprintf(fp, " -Inf <= %s <= %.*g\n", alpar@1: name, DBL_DIG, col->ub), count++; alpar@1: else if (col->type == GLP_DB) alpar@1: xfprintf(fp, " %.*g <= %s <= %.*g\n", alpar@1: DBL_DIG, col->lb, name, DBL_DIG, col->ub), count++; alpar@1: else if (col->type == GLP_FX) alpar@1: xfprintf(fp, " %s = %.*g\n", alpar@1: name, DBL_DIG, col->lb), count++; alpar@1: else alpar@1: xassert(col != col); alpar@1: } alpar@1: if (flag) xfprintf(fp, "\n"), count++; alpar@1: /* write the integer section */ alpar@1: flag = 0; alpar@1: for (j = 1; j <= P->n; j++) alpar@1: { col = P->col[j]; alpar@1: if (col->kind == GLP_CV) continue; alpar@1: xassert(col->kind == GLP_IV); alpar@1: if (!flag) alpar@1: xfprintf(fp, "Generals\n"), flag = 1, count++; alpar@1: xfprintf(fp, " %s\n", col_name(csa, j, name)), count++; alpar@1: } alpar@1: if (flag) xfprintf(fp, "\n"), count++; alpar@1: skip: /* write the end keyword */ alpar@1: xfprintf(fp, "End\n"), count++; alpar@1: xfflush(fp); alpar@1: if (xferror(fp)) alpar@1: { xprintf("Write error on `%s' - %s\n", fname, xerrmsg()); alpar@1: ret = 1; alpar@1: goto done; alpar@1: } alpar@1: /* problem data has been successfully written */ alpar@1: xprintf("%d lines were written\n", count); alpar@1: ret = 0; alpar@1: done: if (fp != NULL) xfclose(fp); alpar@1: return ret; alpar@1: } alpar@1: alpar@1: /* eof */