1 |
aw0a |
1 |
/* |
2 |
|
|
* Contents: MPS module |
3 |
|
|
* |
4 |
|
|
* Authors: Craig Schmidt |
5 |
|
|
* |
6 |
|
|
* Dates: 02/95 - Original version |
7 |
|
|
* |
8 |
|
|
* Description: This module will create an MPS file representation |
9 |
|
|
* of the current system, and a file mapping |
10 |
|
|
* variable names to MPS names. |
11 |
|
|
* |
12 |
|
|
* Version: $Revision: 1.13 $ |
13 |
|
|
* Version control file: $RCSfile: mps.c,v $ |
14 |
|
|
* Date last modified: $Date: 2000/01/25 02:27:03 $ |
15 |
|
|
* Last modified by: $Author: ballan $ |
16 |
|
|
* |
17 |
|
|
* This file is part of the SLV solver. |
18 |
|
|
* The write_MPS routine is passed a mps_data_t |
19 |
|
|
* data structure, the solver subparameters, and the |
20 |
|
|
* name of the file. |
21 |
|
|
* |
22 |
|
|
* The write_name_map routine creates a file linking the |
23 |
|
|
* ascend name of a variable and CXXXXXXX |
24 |
|
|
* |
25 |
|
|
* This file is part of the SLV solver. |
26 |
|
|
* |
27 |
|
|
* Copyright (C) 1990 Karl Michael Westerberg |
28 |
|
|
* Copyright (C) 1993 Joseph Zaher |
29 |
|
|
* Copyright (C) 1994 Joseph Zaher, Benjamin Andrew Allan |
30 |
|
|
* Copyright (C) 1995 Craig Schmidt |
31 |
|
|
* |
32 |
|
|
* The SLV solver is free software; you can redistribute |
33 |
|
|
* it and/or modify it under the terms of the GNU General Public License as |
34 |
|
|
* published by the Free Software Foundation; either version 2 of the |
35 |
|
|
* License, or (at your option) any later version. |
36 |
|
|
* |
37 |
|
|
* The SLV solver is distributed in hope that it will be |
38 |
|
|
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of |
39 |
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
40 |
|
|
* General Public License for more details. |
41 |
|
|
* |
42 |
|
|
* You should have received a copy of the GNU General Public License |
43 |
|
|
* along with the program; if not, write to the Free Software Foundation, |
44 |
|
|
* Inc., 675 Mass Ave, Cambridge, MA 02139 USA. Check the file named |
45 |
|
|
* COPYING. COPYING is found in ../compiler. |
46 |
|
|
*/ |
47 |
|
|
|
48 |
|
|
/*** MPS matrix strucutre |
49 |
|
|
*** v |
50 |
|
|
*** min/max cx: Ax<=b u |
51 |
|
|
*** s |
52 |
|
|
*** e |
53 |
|
|
*** 1 d |
54 |
|
|
*** |
55 |
|
|
*** | | |
56 |
|
|
*** | | |
57 |
|
|
*** \ / \ / |
58 |
|
|
*** |
59 |
|
|
*** +- -+ |
60 |
|
|
*** 1 -> | | |
61 |
|
|
*** | | |
62 |
|
|
*** | A | |
63 |
|
|
*** | | |
64 |
|
|
*** rused -> | | |
65 |
|
|
*** +- -+ |
66 |
|
|
*** |
67 |
|
|
*** crow -> [ c ] |
68 |
|
|
**/ |
69 |
|
|
|
70 |
|
|
#include <time.h> |
71 |
|
|
#include <errno.h> |
72 |
|
|
#include "utilities/ascConfig.h" |
73 |
|
|
#include "compiler/compiler.h" |
74 |
|
|
#include "utilities/ascMalloc.h" |
75 |
|
|
#include "general/list.h" |
76 |
|
|
#include "utilities/set.h" |
77 |
|
|
#include "general/tm_time.h" |
78 |
|
|
#include "utilities/mem.h" |
79 |
|
|
#include "solver/mtx.h" |
80 |
|
|
#include "solver/slv_types.h" |
81 |
|
|
#include "solver/var.h" |
82 |
|
|
#include "solver/rel.h" |
83 |
|
|
#include "solver/discrete.h" |
84 |
|
|
#include "solver/conditional.h" |
85 |
|
|
#include "solver/logrel.h" |
86 |
|
|
#include "solver/bnd.h" |
87 |
|
|
#include "solver/slv_common.h" |
88 |
|
|
#include "solver/linsol.h" |
89 |
|
|
#include "solver/linsolqr.h" |
90 |
|
|
#include "solver/slv_client.h" |
91 |
|
|
#include "solver/slv6.h" |
92 |
|
|
#include "solver/mps.h" |
93 |
|
|
|
94 |
|
|
#ifdef STATIC_MPS |
95 |
|
|
|
96 |
|
|
/* _________________________________________________________________________ */ |
97 |
|
|
|
98 |
|
|
/** |
99 |
|
|
*** Utility routines |
100 |
|
|
*** ---------------------------- |
101 |
|
|
*** stamp - stamp file header with a unique timestamp |
102 |
|
|
*** process_name - do tilde expansion, check for .mps or .map, etc. |
103 |
|
|
*** open_write - open the designated file for write access |
104 |
|
|
*** close_file - close the file |
105 |
|
|
*** print_col_element - print either the first or second column entry |
106 |
|
|
*** print_col - prints out a column of data from the Ac_mtx |
107 |
|
|
**/ |
108 |
|
|
|
109 |
|
|
static void stamp(FILE *outfile, boolean newstamp, boolean dostamp, boolean dostr) |
110 |
|
|
/** |
111 |
|
|
*** Write a unique stamp to file, create a new one |
112 |
|
|
*** if newstamp is true |
113 |
|
|
**/ |
114 |
|
|
{ |
115 |
|
|
static char stampstr[26]; |
116 |
|
|
static unsigned long stamptime; |
117 |
|
|
time_t now; |
118 |
|
|
|
119 |
|
|
now = time(NULL); |
120 |
|
|
|
121 |
|
|
if (newstamp) { /* generate new stamp */ |
122 |
|
|
stamptime = (unsigned long) clock(); |
123 |
|
|
sprintf(&stampstr[0], "%-26s", ctime(&now)); |
124 |
|
|
} |
125 |
|
|
|
126 |
|
|
if (dostamp) FPRINTF(outfile,"%-8x", stamptime); /* only 8 chars for stamp in MPS, so show hex */ |
127 |
|
|
if (dostr) FPRINTF(outfile," %s", &stampstr); /* print friendlier form */ |
128 |
|
|
} |
129 |
|
|
|
130 |
|
|
static FILE *open_write(const char *filename) |
131 |
|
|
/** |
132 |
|
|
*** Open file for write access, return NULL if error |
133 |
|
|
**/ |
134 |
|
|
{ |
135 |
|
|
FILE *f; |
136 |
|
|
errno = 0; |
137 |
|
|
if (filename == NULL) filename = "\0"; /* shouldn't pass null to fopen */ |
138 |
|
|
f = fopen(filename, "w"); |
139 |
|
|
if( f == NULL ) { |
140 |
|
|
FPRINTF(stderr,"ERROR: (MPS) open_write\n"); |
141 |
|
|
FPRINTF(stderr," Unable to open %s. Error:%s\n", |
142 |
|
|
filename, strerror(errno)); |
143 |
|
|
} |
144 |
|
|
|
145 |
|
|
return f; |
146 |
|
|
} |
147 |
|
|
|
148 |
|
|
|
149 |
|
|
static boolean close_file(FILE *f) |
150 |
|
|
/** |
151 |
|
|
*** Close file and handle errors |
152 |
|
|
**/ |
153 |
|
|
{ |
154 |
|
|
int s = 0; |
155 |
|
|
if(f == NULL) return TRUE; /* ignore this case */ |
156 |
|
|
|
157 |
|
|
errno = 0; |
158 |
|
|
s = fclose(f); |
159 |
|
|
if (s == EOF) |
160 |
|
|
{ |
161 |
|
|
FPRINTF(stderr,"ERROR: (MPS) open_write\n"); |
162 |
|
|
perror(" Unable to close file"); |
163 |
|
|
return FALSE; |
164 |
|
|
} |
165 |
|
|
else |
166 |
|
|
return TRUE; |
167 |
|
|
} |
168 |
|
|
|
169 |
|
|
|
170 |
|
|
/* define constants for whether this is the first or second value on the line */ |
171 |
|
|
#define ONE 0 |
172 |
|
|
#define TWO 1 |
173 |
|
|
|
174 |
|
|
static void print_col_element(FILE *out, |
175 |
|
|
int32 var, |
176 |
|
|
int32 rel, |
177 |
|
|
real64 value) /* value of mtx element */ |
178 |
|
|
/** |
179 |
|
|
*** Prints the appropriate variable and relation labels, and |
180 |
|
|
*** the value of the element. If the the variable is different |
181 |
|
|
*** from last call of this routine, the first column is used. |
182 |
|
|
*** Otherwise it alternates, putting 2 values per line. |
183 |
|
|
*** |
184 |
|
|
*** A value of -1 for var is a special case, meaning that you just |
185 |
|
|
*** want a newline if the last call was in the middle of a line |
186 |
|
|
**/ |
187 |
|
|
{ |
188 |
|
|
static int32 oldvar; |
189 |
|
|
static int onetwo; /* is it the first or second value on the line (ONE or TWO) */ |
190 |
|
|
|
191 |
|
|
/* set up state */ |
192 |
|
|
|
193 |
|
|
if (var == -1) { /* just write newline if last time were in middle of line */ |
194 |
|
|
if (onetwo == ONE) |
195 |
|
|
FPRINTF(out,"\n"); |
196 |
|
|
return; |
197 |
|
|
} |
198 |
|
|
|
199 |
|
|
if (oldvar != var) { /* are at a new variable */ |
200 |
|
|
onetwo = ONE; |
201 |
|
|
oldvar = var; |
202 |
|
|
} |
203 |
|
|
else |
204 |
|
|
if (onetwo == ONE) |
205 |
|
|
onetwo = TWO; |
206 |
|
|
else onetwo = ONE; |
207 |
|
|
|
208 |
|
|
/* print full names if in first col, else just the rel label */ |
209 |
|
|
|
210 |
|
|
/* Format: Field 2 Field 3 Field 4 Field 5 Field 6 |
211 |
|
|
(5-12) (15-22) (25-36) (40-47) (50-61) |
212 |
|
|
Column Row 1 Value 1 Row 2 Value 2 |
213 |
|
|
name name name name |
214 |
|
|
*/ |
215 |
|
|
|
216 |
|
|
if (onetwo == ONE) |
217 |
|
|
|
218 |
|
|
/* 1111 2222 3 */ |
219 |
|
|
/* 1234 2345 2345 6 */ |
220 |
|
|
FPRINTF(out," C%07d R%07d %12.6G", var, rel, value); |
221 |
|
|
else |
222 |
|
|
/* 3334 4445 */ |
223 |
|
|
/* 7890 7890 */ |
224 |
|
|
FPRINTF(out," R%07d %12.6G\n", rel, value); |
225 |
|
|
|
226 |
|
|
} |
227 |
|
|
|
228 |
|
|
|
229 |
|
|
void print_col(FILE *out, /* file */ |
230 |
|
|
mtx_matrix_t Ac_mtx, /* Matrix representation of problem */ |
231 |
|
|
char typerow[], /* array of variable type data */ |
232 |
|
|
int32 curcol, /* current column number */ |
233 |
|
|
int32 rused, /* number of relations */ |
234 |
|
|
int32 vused, /* number of vars */ |
235 |
|
|
int dointeger, /* supports integer vars */ |
236 |
|
|
int dobinary) /* supports binary vars */ |
237 |
|
|
/** |
238 |
|
|
*** Uses print_col_element to print an entire column of the Ac_mtx |
239 |
|
|
*** It uses the values of dointeger and dobinary to determine |
240 |
|
|
*** if any INTORG markers are needed |
241 |
|
|
**/ |
242 |
|
|
{ |
243 |
|
|
static boolean inBinInt = FALSE; /* are we currently in a binary or integer var region ? */ |
244 |
|
|
static int marknum = 0; /* number for marker label */ |
245 |
|
|
real64 value; /* value of mtx element */ |
246 |
|
|
mtx_coord_t nz; /* coordinate of row/column in A matrix */ |
247 |
|
|
mtx_range_t range; /* define range */ |
248 |
|
|
int32 orgcol; /* original column number */ |
249 |
|
|
char coltype; /* type of current column */ |
250 |
|
|
char nexttype; /* type of next column */ |
251 |
|
|
|
252 |
|
|
orgcol = mtx_col_to_org(Ac_mtx, curcol); |
253 |
|
|
coltype = typerow[orgcol]; /* get cur col type */ |
254 |
|
|
if (curcol != vused-1) /* don't go beyond array limits */ |
255 |
|
|
nexttype = typerow[mtx_col_to_org(Ac_mtx, curcol+1)]; |
256 |
|
|
else |
257 |
|
|
nexttype = 0; |
258 |
|
|
|
259 |
|
|
if (coltype != SOLVER_FIXED) { /* only bother with incident, nonfixed variables */ |
260 |
|
|
|
261 |
|
|
/* do an INTORG marker if start of binary/integer var */ |
262 |
|
|
if ((! inBinInt) && (((coltype == SOLVER_BINARY) && (dobinary == 0)) || |
263 |
|
|
((coltype == SOLVER_INT) && (dointeger == 0)))) { |
264 |
|
|
inBinInt = TRUE; /* turn on flag */ |
265 |
|
|
|
266 |
|
|
/* 1 2 3 4 * |
267 |
|
|
* 1234 234567890123456789012345678901234567 */ |
268 |
|
|
FPRINTF(out," M%07d 'MARKER' 'INTORG'\n", marknum); |
269 |
|
|
} |
270 |
|
|
|
271 |
|
|
nz.row = mtx_FIRST; /* first nonzero row */ |
272 |
|
|
nz.col = curcol; /* current col */ |
273 |
|
|
|
274 |
|
|
/* note: since mtx_FIRST = mtx_LAST, can't use a while loop */ |
275 |
|
|
value = mtx_next_in_col(Ac_mtx,&nz,mtx_range(&range,0,rused)); |
276 |
|
|
do { |
277 |
|
|
print_col_element(out, orgcol, mtx_row_to_org(Ac_mtx, nz.row), value); /* print out a nonzero element */ |
278 |
|
|
value = mtx_next_in_col(Ac_mtx,&nz,mtx_range(&range,0,rused)); |
279 |
|
|
} while (nz.row != mtx_LAST); |
280 |
|
|
|
281 |
|
|
print_col_element(out, -1 , 0, 0.0); /* clean up newline */ |
282 |
|
|
|
283 |
|
|
/* close marker if in last column or next type is not an int or bin */ |
284 |
|
|
if (((inBinInt) && (curcol == (vused-1))) || |
285 |
|
|
(inBinInt && ((nexttype != SOLVER_BINARY) && (nexttype != SOLVER_INT )))) { |
286 |
|
|
|
287 |
|
|
inBinInt = FALSE; /* turn on flag */ |
288 |
|
|
/* 1 2 3 4 * |
289 |
|
|
* 1234 234567890123456789012345678901234567 */ |
290 |
|
|
FPRINTF(out," E%07d 'MARKER' 'INTEND'\n", marknum++); /* advance number */ |
291 |
|
|
} |
292 |
|
|
|
293 |
|
|
} |
294 |
|
|
|
295 |
|
|
} |
296 |
|
|
|
297 |
|
|
/* _________________________________________________________________________ */ |
298 |
|
|
|
299 |
|
|
/** write_name_map |
300 |
|
|
*** |
301 |
|
|
*** writes out a file mapping the VXXXXXXX variable names |
302 |
|
|
*** with the actual ASCEND names |
303 |
|
|
**/ |
304 |
|
|
|
305 |
|
|
extern boolean write_name_map(const char *name, /* filename for output */ |
306 |
|
|
struct var_variable **vlist) /* Variable list (NULL terminated) */ |
307 |
|
|
{ |
308 |
|
|
FILE *out; |
309 |
|
|
int i; |
310 |
|
|
|
311 |
|
|
if ((vlist == NULL) || (name == NULL)) { /* got a bad pointer */ |
312 |
|
|
FPRINTF(stderr,"ERROR: (MPS) write_name_map\n"); |
313 |
|
|
FPRINTF(stderr," Routine was passed a NULL pointer!\n"); |
314 |
|
|
return FALSE; |
315 |
|
|
} |
316 |
|
|
|
317 |
|
|
out = open_write(name); |
318 |
|
|
if (out == NULL) |
319 |
|
|
return FALSE; |
320 |
|
|
|
321 |
|
|
FPRINTF(out,"ASCEND/MPS Variable Name Mapping\n\n"); |
322 |
|
|
FPRINTF(out,"Timestamp: "); |
323 |
|
|
stamp(out,FALSE,TRUE,TRUE); /* use same stamp as in write_MPS, which was already called */ |
324 |
|
|
FPRINTF(out,"\n"); |
325 |
|
|
FPRINTF(out,"MPS Name ASCEND Name\n"); |
326 |
|
|
FPRINTF(out,"-------- -----------\n"); |
327 |
|
|
|
328 |
|
|
for(; *vlist != NULL ; ++vlist ) |
329 |
|
|
if( free_inc_var_filter(*vlist) ) |
330 |
|
|
{ |
331 |
|
|
FPRINTF(out,"C%07d ",var_sindex(*vlist)); |
332 |
|
|
|
333 |
|
|
/* old way: just the unqualified names */ |
334 |
|
|
/* slv_print_var_name(out,*vlist); */ |
335 |
|
|
|
336 |
|
|
/* now, from instance_io.h, the full qualified name */ |
337 |
|
|
WriteInstanceName(out, var_instance(*vlist), NULL); |
338 |
|
|
FPRINTF(out,"\n"); |
339 |
|
|
} |
340 |
|
|
|
341 |
|
|
return close_file(out); |
342 |
|
|
|
343 |
|
|
} |
344 |
|
|
|
345 |
|
|
/* _________________________________________________________________________ */ |
346 |
|
|
|
347 |
|
|
/** routines used by write_MPS |
348 |
|
|
*** |
349 |
|
|
*** do_name - create header NAME section |
350 |
|
|
*** do_rows - name constraints: just needs matrix of info |
351 |
|
|
*** scan_SOS - identify special ordered sets |
352 |
|
|
*** do_columns - create A matrix |
353 |
|
|
*** do_rhs - create rhs |
354 |
|
|
*** do_bounds - create BOUNDS section |
355 |
|
|
**/ |
356 |
|
|
|
357 |
|
|
|
358 |
|
|
/* create header */ |
359 |
|
|
static void do_name(FILE *out, /* file */ |
360 |
|
|
int obj, /* how does it know to max/min */ |
361 |
|
|
int bo, /* QOMILP style cutoff */ |
362 |
|
|
int eps, /* QOMILP style termination criteria */ |
363 |
|
|
double boval, /* value of cutoff */ |
364 |
|
|
double epsval) /* value of termination criteria */ |
365 |
|
|
{ |
366 |
|
|
|
367 |
|
|
/** Relevant options: |
368 |
|
|
*** |
369 |
|
|
*** sp.ia[SP6_OBJ] 0->solver assumes minimization, do nothing special |
370 |
|
|
*** 1->solver assumes maximization, swap obj coeff for min problems |
371 |
|
|
*** 2->solver support SCICONIC style MINIMIZE |
372 |
|
|
*** 3->solver supports QOMILP style MAX/MIN in names section |
373 |
|
|
*** |
374 |
|
|
*** sp.ia[SP6_BO] 0->no support |
375 |
|
|
*** 1->solver supports QOMILP style BO cutoff bound in names section |
376 |
|
|
*** Note: value of bound is in sp.ra[SP6_BNDVAL] |
377 |
|
|
*** sp.ia[SP6_EPS] 0->no support |
378 |
|
|
*** 1->solver supports QOMILP style EPS termination criterion |
379 |
|
|
*** Note: value of bound is in sp.ra[SP6_EPSVAL] |
380 |
|
|
*** |
381 |
|
|
*** sp.ra[SP6_BOVAL] value of QOMILP style BO cutoff bound in names section |
382 |
|
|
*** Note: Ignored if sp.ia[SP6_BO]=0 |
383 |
|
|
*** sp.ra[SP6_EPSVAL] value of QOMILP style EPS termination criterion |
384 |
|
|
*** Note: Ignored if sp.ia[SP6_EPS]=0 |
385 |
|
|
**/ |
386 |
|
|
|
387 |
|
|
/* Note: ASCEND assumes we're _minimizing_ the objective */ |
388 |
|
|
/* Name can only be 8 characters, so display number in hex */ |
389 |
|
|
|
390 |
|
|
switch (obj) { |
391 |
|
|
case 0: |
392 |
|
|
case 1: /* general case: Ok for CPLEXl, OSL, lpsolve */ |
393 |
|
|
FPRINTF(out,"NAME "); |
394 |
|
|
stamp(out,TRUE,TRUE,FALSE); /* create new timestamp, name map comes later */ |
395 |
|
|
FPRINTF(out,"\n"); |
396 |
|
|
break; |
397 |
|
|
|
398 |
|
|
case 2: /* with SCIONIC use MINIMISE */ |
399 |
|
|
FPRINTF(out,"NAME "); |
400 |
|
|
stamp(out,TRUE,TRUE,FALSE); /* create new timestamp, name map comes later */ |
401 |
|
|
FPRINTF(out,"MINIMISE\n"); /* British spelling */ |
402 |
|
|
break; |
403 |
|
|
|
404 |
|
|
case 3: /* with QOMILP use MIN */ |
405 |
|
|
FPRINTF(out,"NAME "); |
406 |
|
|
stamp(out,TRUE,TRUE,TRUE); /* create new timestamp, name map comes later */ |
407 |
|
|
FPRINTF(out,"\n"); |
408 |
|
|
FPRINTF(out," MIN\n"); /* optimization direction */ |
409 |
|
|
break; |
410 |
|
|
|
411 |
|
|
default: FPRINTF(stderr,"ERROR: (MPS) do_name\n"); |
412 |
|
|
FPRINTF(stderr," Unknown option for objective!\n"); |
413 |
|
|
} |
414 |
|
|
|
415 |
|
|
if (bo == 1) |
416 |
|
|
FPRINTF(out," BO %12.8f\n", boval); /* Numbers in 25-36 for QOMILP */ |
417 |
|
|
|
418 |
|
|
if (eps == 1) |
419 |
|
|
FPRINTF(out," EPS %12.2f\n", epsval); |
420 |
|
|
|
421 |
|
|
} |
422 |
|
|
|
423 |
|
|
|
424 |
|
|
static void do_rows(FILE *out, /* file */ |
425 |
|
|
char relopcol[], /* array of data */ |
426 |
|
|
int32 rused) /* size of array */ |
427 |
|
|
{ |
428 |
|
|
int i; |
429 |
|
|
|
430 |
|
|
FPRINTF(out,"ROWS\n"); /* section header */ |
431 |
|
|
|
432 |
|
|
for (i = 0; i < rused; i++) |
433 |
|
|
switch (relopcol[i]) { |
434 |
|
|
case rel_TOK_less: FPRINTF(out," L R%07d\n", i); |
435 |
|
|
break; |
436 |
|
|
case rel_TOK_equal: FPRINTF(out," E R%07d\n", i); |
437 |
|
|
break; |
438 |
|
|
case rel_TOK_greater: FPRINTF(out," G R%07d\n", i); |
439 |
|
|
break; |
440 |
|
|
case rel_TOK_nonincident: break; |
441 |
|
|
|
442 |
|
|
default: FPRINTF(stderr,"ERROR: (MPS) do_rows\n"); |
443 |
|
|
FPRINTF(stderr," Unknown value for relational operators!\n"); |
444 |
|
|
} |
445 |
|
|
|
446 |
|
|
FPRINTF(out," N R%07d\n", rused); /* objective row */ |
447 |
|
|
|
448 |
|
|
} |
449 |
|
|
|
450 |
|
|
|
451 |
|
|
static void upgrade_vars(FILE *out, /* file */ |
452 |
|
|
char typerow[], /* array of variable type data */ |
453 |
|
|
real64 ubrow[], /* to change ub on int->bin */ |
454 |
|
|
int32 vused, /* number of vars */ |
455 |
|
|
int relaxed, /* should the relaxed problem be solved */ |
456 |
|
|
int dointeger, /* supports integer vars */ |
457 |
|
|
int dobinary, /* supports binary vars */ |
458 |
|
|
int dosemi) /* supports semi-continuous vars */ |
459 |
|
|
|
460 |
|
|
/** This very simple routine checks to see if a variables type is currently |
461 |
|
|
*** supported. If not, it converts it into a type which is, printing a |
462 |
|
|
*** warning about the conversion to stderr. |
463 |
|
|
*** |
464 |
|
|
*** It does bin -> integer |
465 |
|
|
*** integer -> bin |
466 |
|
|
*** semi -> solver_var |
467 |
|
|
*** bin -> solver_var |
468 |
|
|
*** integer -> solver_var |
469 |
|
|
*** conversions, as appropriate. |
470 |
|
|
**/ |
471 |
|
|
|
472 |
|
|
{ |
473 |
|
|
int orgcol; /* original column number */ |
474 |
|
|
|
475 |
|
|
for(orgcol = 0; orgcol < vused; orgcol++) /* loop over all columns, is _original_ column number */ |
476 |
|
|
if (typerow[orgcol] != SOLVER_FIXED) { /* only bother with incident variables */ |
477 |
|
|
|
478 |
|
|
if ((relaxed == 1) && ((typerow[orgcol] == SOLVER_BINARY) || /* if relaxed convert it to solver_var */ |
479 |
|
|
(typerow[orgcol] == SOLVER_INT) || |
480 |
|
|
(typerow[orgcol] == SOLVER_SEMI))) |
481 |
|
|
typerow[orgcol] = SOLVER_VAR; |
482 |
|
|
else |
483 |
|
|
|
484 |
|
|
/* upgrade types as appropriate here */ |
485 |
|
|
/* if defined a binary var, and solver only supports integer vars, "upgrade" to an int var, etc. */ |
486 |
|
|
if ((typerow[orgcol] == SOLVER_BINARY) && (dointeger != 2) && (dobinary == 2)) { |
487 |
|
|
typerow[orgcol] = SOLVER_INT; |
488 |
|
|
} |
489 |
|
|
else if ((typerow[orgcol] == SOLVER_INT) && (dointeger == 2) && (dobinary != 2)) { |
490 |
|
|
FPRINTF(stderr,"WARNING: Variable C%07d was treated as a %s instead of a %s.\n", orgcol, SOLVER_BINARY_STR, SOLVER_INT_STR); |
491 |
|
|
FPRINTF(stderr," The selected MILP solver does not support %s.\n", SOLVER_INT_STR); |
492 |
|
|
FPRINTF(stderr," Upper bound was set to 1.0.\n"); |
493 |
|
|
typerow[orgcol] = SOLVER_BINARY; |
494 |
|
|
ubrow[orgcol] = 1.0; /* note: changed bound */ |
495 |
|
|
} |
496 |
|
|
else if ((typerow[orgcol] == SOLVER_SEMI) && (dosemi == 0)) { /* semi not supported */ |
497 |
|
|
FPRINTF(stderr,"WARNING: Variable C%07d was converted from a %s to a %s.\n", orgcol, SOLVER_SEMI_STR, SOLVER_VAR_STR); |
498 |
|
|
FPRINTF(stderr," The selected MILP solver does not support %s.\n", orgcol, SOLVER_SEMI_STR); |
499 |
|
|
FPRINTF(stderr," The solution found may not be correct for your model.\n"); |
500 |
|
|
typerow[orgcol] = SOLVER_VAR; |
501 |
|
|
} |
502 |
|
|
else if ((typerow[orgcol] == SOLVER_BINARY) && (dointeger == 2) && (dobinary ==2)) { /* neither is supported */ |
503 |
|
|
FPRINTF(stderr,"WARNING: Variable C%07d was treated as a %s instead of a %s.\n", orgcol, SOLVER_VAR_STR, SOLVER_BINARY_STR); |
504 |
|
|
FPRINTF(stderr," The selected MILP solver only supports %s.\n", SOLVER_VAR_STR); |
505 |
|
|
typerow[orgcol] = SOLVER_VAR; |
506 |
|
|
} |
507 |
|
|
else if ((typerow[orgcol] == SOLVER_INT) && (dointeger == 2) && (dobinary == 2)) { /* neither is supported */ |
508 |
|
|
FPRINTF(stderr,"WARNING: Variable C%07d was treated as a %s instead of a %s.\n", orgcol, SOLVER_VAR_STR, SOLVER_INT_STR); |
509 |
|
|
FPRINTF(stderr," The selected MILP solver only supports %s.\n", SOLVER_VAR_STR); |
510 |
|
|
typerow[orgcol] = SOLVER_VAR; |
511 |
|
|
} |
512 |
|
|
} |
513 |
|
|
} |
514 |
|
|
|
515 |
|
|
|
516 |
|
|
|
517 |
|
|
void scan_SOS(mtx_matrix_t Ac_mtx, /* Matrix representation of problem */ |
518 |
|
|
char relopcol[], /* array of relational operator data */ |
519 |
|
|
real64 bcol[], /* array of RHS data */ |
520 |
|
|
char typerow[], /* array of variable type data */ |
521 |
|
|
int32 rused, /* size of arrays */ |
522 |
|
|
int32 vused, /* number of vars */ |
523 |
|
|
int32 *sosvarptr, /* output: number of variables used |
524 |
|
|
in SOS's */ |
525 |
|
|
int32 *sosrelptr) /* output: number of relations |
526 |
|
|
defining SOS's */ |
527 |
|
|
|
528 |
|
|
/** This routine identifies relations which are special ordered sets. |
529 |
|
|
*** It used the Ac_mtx matrix, and reorganizes it into the following |
530 |
|
|
*** format: |
531 |
|
|
*** 1 sosvar vused |
532 |
|
|
*** | | | |
533 |
|
|
*** + + + |
534 |
|
|
*** 1 -> | xxxxx | |
535 |
|
|
*** | xxx | |
536 |
|
|
*** | xxxx | |
537 |
|
|
*** sosrel -> | xx | |
538 |
|
|
*** | x x x x x | |
539 |
|
|
*** | x x x x | |
540 |
|
|
*** | x x x x x | |
541 |
|
|
*** | x x x x x | |
542 |
|
|
*** | x x x x | |
543 |
|
|
*** rused -> | x x x x x | |
544 |
|
|
*** crow |
545 |
|
|
*** |
546 |
|
|
*** sosvar = number of vars involved in SOS's |
547 |
|
|
*** sosrel = number of SOS equations |
548 |
|
|
*** |
549 |
|
|
*** The nomenclature of SOS's is really confused. The number used |
550 |
|
|
*** depends on the solver/modeling language in use. In this case we are |
551 |
|
|
*** looking for equations of the form sum(i, x[i]) = 1, which I'll call |
552 |
|
|
*** a SOS1, and sum(i, x[i]) <= 1, which I'll call a SOS3. |
553 |
|
|
*** |
554 |
|
|
*** First, this routine checks to see if a relation is >=. If so, it |
555 |
|
|
*** can't be SOS, so that row is skipped. Next, it checks to see |
556 |
|
|
*** if the RHS is 1. If not, that row is skipped. |
557 |
|
|
*** |
558 |
|
|
*** Note that overlapping SOS's are not allowed by the MPS format. |
559 |
|
|
*** That is, a variable can only be part of one SOS. |
560 |
|
|
*** |
561 |
|
|
*** This routine checks to be sure that all vars involved are binary |
562 |
|
|
*** or integer. If so, the row is a SOS. If not, it's just a regular |
563 |
|
|
*** constraint. (It doesn't explicitly check the upper bounds on |
564 |
|
|
*** integer vars, since if their not 1 the model will be infeasible.) |
565 |
|
|
*** |
566 |
|
|
*** Note that this routine uses the current row/col, not the original like most |
567 |
|
|
*** other routines. The columns are reorganized so that all vars of a SOS are |
568 |
|
|
*** together, as shown above. |
569 |
|
|
**/ |
570 |
|
|
|
571 |
|
|
{ |
572 |
|
|
real64 value; /* value of mtx element */ |
573 |
|
|
mtx_coord_t nz; /* coordinate of row/column for going down RHS column */ |
574 |
|
|
mtx_range_t range; /* storage for range of A matrix, run down a column */ |
575 |
|
|
int32 current_row; /* counter for row being examined */ |
576 |
|
|
int32 current_col; /* counter for column being examined */ |
577 |
|
|
int32 not_row; /* counter for row at end of matrix which is not an SOS */ |
578 |
|
|
boolean isSOS; /* is the current row a SOS ? */ |
579 |
|
|
|
580 |
|
|
current_row = 0; /* initialize stuff */ |
581 |
|
|
not_row = rused-1; /* rel 0 to rused-1 are actually used */ |
582 |
|
|
current_col = 0; |
583 |
|
|
|
584 |
|
|
while (current_row <= not_row) /* examin each row of the A matrix once */ |
585 |
|
|
{ |
586 |
|
|
if ((bcol[mtx_row_to_org(Ac_mtx, current_row)] == 1.0) && (relopcol[mtx_row_to_org(Ac_mtx, current_row)] != rel_TOK_greater)) /* see if row coefficients ok */ |
587 |
|
|
{ |
588 |
|
|
/* it is a SOS if all coefficients are 1, |
589 |
|
|
and all columns with nonzero coeff are >= current_col |
590 |
|
|
i.e. the equation doesn't contain any vars from prev SOS equations */ |
591 |
|
|
|
592 |
|
|
isSOS = TRUE; |
593 |
|
|
nz.col = mtx_FIRST; /* first nonzero col (1) */ |
594 |
|
|
nz.row = current_row; /* use current row */ |
595 |
|
|
|
596 |
|
|
do { /* loop until done or find a value != 1.0 */ |
597 |
|
|
|
598 |
|
|
value = mtx_next_in_row(Ac_mtx,&nz,mtx_range(&range,0,vused)); |
599 |
|
|
if ((nz.col != mtx_FIRST) && (nz.col != mtx_LAST)) { |
600 |
|
|
if ( nz.col < current_col) { |
601 |
|
|
isSOS = FALSE; /* overlaps prev SOS */ |
602 |
|
|
FPRINTF(stderr, "nz.col, current_col, mtx_FIRST: %d %d %d\n", nz.col, current_col, mtx_FIRST); |
603 |
|
|
} |
604 |
|
|
if ((typerow[mtx_col_to_org(Ac_mtx, nz.col)] != SOLVER_BINARY) && |
605 |
|
|
( typerow[mtx_col_to_org(Ac_mtx, nz.col)] != SOLVER_INT)) { |
606 |
|
|
isSOS = FALSE; /* var is wrong type */ |
607 |
|
|
FPRINTF(stderr, "typerow: %d\n", typerow[mtx_col_to_org(Ac_mtx, nz.col)]); |
608 |
|
|
} |
609 |
|
|
} |
610 |
|
|
|
611 |
|
|
} while ( (value == 1.0) && (nz.row != mtx_LAST) && isSOS ); |
612 |
|
|
|
613 |
|
|
if (nz.col != mtx_LAST) isSOS = FALSE; /* only true if terminated due to mxt_LAST */ |
614 |
|
|
FPRINTF(stderr, "isSOS,nz.col:%d, %d\n", isSOS,nz.col); |
615 |
|
|
|
616 |
|
|
} |
617 |
|
|
else |
618 |
|
|
isSOS = FALSE; |
619 |
|
|
|
620 |
|
|
if (isSOS) /* reorder columns so all line up in first cols */ |
621 |
|
|
{ |
622 |
|
|
FPRINTF(stderr, "current_row, not_row:%d, %d\n", current_row, not_row); |
623 |
|
|
/* Is a SOS, so rearrange columns so all the vars in the equation |
624 |
|
|
are from current_col on. Also advance current_row by one. */ |
625 |
|
|
|
626 |
|
|
nz.col = mtx_FIRST; /* first nonzero col */ |
627 |
|
|
nz.row = current_row; /* use current row */ |
628 |
|
|
|
629 |
|
|
mtx_next_in_row(Ac_mtx,&nz,mtx_range(&range,0,vused)); |
630 |
|
|
while( nz.col != mtx_LAST) { |
631 |
|
|
mtx_swap_cols(Ac_mtx, nz.col, current_col++); |
632 |
|
|
mtx_next_in_row(Ac_mtx,&nz,mtx_range(&range,0,vused)); |
633 |
|
|
} |
634 |
|
|
current_row++; /* now examine next row */ |
635 |
|
|
} |
636 |
|
|
else |
637 |
|
|
{ |
638 |
|
|
/* Isn't a SOS, so swap current_row with not_row, |
639 |
|
|
to get it out of the way. |
640 |
|
|
Also decrement not_row by one */ |
641 |
|
|
|
642 |
|
|
mtx_swap_rows(Ac_mtx, current_row, not_row--); |
643 |
|
|
|
644 |
|
|
} |
645 |
|
|
} |
646 |
|
|
|
647 |
|
|
*sosvarptr = current_col; |
648 |
|
|
*sosrelptr = current_row; |
649 |
|
|
} |
650 |
|
|
|
651 |
|
|
|
652 |
|
|
void do_columns(FILE *out, /* file */ |
653 |
|
|
mtx_matrix_t Ac_mtx, /* Matrix representation of problem */ |
654 |
|
|
char typerow[], /* array of variable type data */ |
655 |
|
|
int32 rused, /* number of relations */ |
656 |
|
|
int32 vused, /* number of vars */ |
657 |
|
|
int32 sosvar, /* number of variables used in SOS's */ |
658 |
|
|
int32 sosrel, /* number of relations that |
659 |
|
|
are SOS's */ |
660 |
|
|
int dointeger, /* supports integer vars */ |
661 |
|
|
int dobinary) /* supports binary vars */ |
662 |
|
|
/*** |
663 |
|
|
*** Creates the main A matrix of data. It handles SOS's, relying on |
664 |
|
|
*** the values of sosvar and sosrel (which should be 0 is there aren't |
665 |
|
|
*** any SOS's). It mostly just calls print_col, the appropriate number |
666 |
|
|
*** of times. |
667 |
|
|
***/ |
668 |
|
|
{ |
669 |
|
|
mtx_range_t range; /* storage for range of A matrix, run down a column */ |
670 |
|
|
int32 curcol; /* counter for current column */ |
671 |
|
|
int32 currow; /* current row number */ |
672 |
|
|
int sosnum; /* number used in marker labels */ |
673 |
|
|
int upcol; /* upper counter on column in SOS */ |
674 |
|
|
|
675 |
|
|
FPRINTF(out,"COLUMNS\n"); /* section header */ |
676 |
|
|
|
677 |
|
|
sosnum = 0; |
678 |
|
|
curcol = 0; |
679 |
|
|
|
680 |
|
|
/* loop over every SOS */ |
681 |
|
|
for (currow = 0; currow < sosrel ; currow++) { |
682 |
|
|
|
683 |
|
|
/* 0 1 2 3 4 * |
684 |
|
|
* 1234 234567890123456789012345678901234567 */ |
685 |
|
|
FPRINTF(out," M%07d 'MARKER' 'SETORG'\n", sosnum); |
686 |
|
|
|
687 |
|
|
/* figure out end point of next SOS */ |
688 |
|
|
upcol = mtx_nonzeros_in_row(Ac_mtx,currow,mtx_range(&range,0,vused-1)) + curcol; |
689 |
|
|
|
690 |
|
|
for ( ; curcol < upcol; curcol++) |
691 |
|
|
print_col(out, Ac_mtx, typerow, curcol, rused, vused, dointeger, dobinary); |
692 |
|
|
|
693 |
|
|
/* 0 1 2 3 4 * |
694 |
|
|
* 1234 234567890123456789012345678901234567 */ |
695 |
|
|
FPRINTF(out," E%07d 'MARKER' 'SETEND'\n", sosnum++); |
696 |
|
|
|
697 |
|
|
} |
698 |
|
|
|
699 |
|
|
/* loop rest of all columns, not in a SOS */ |
700 |
|
|
for( ; curcol < vused; curcol++) |
701 |
|
|
print_col(out, Ac_mtx, typerow, curcol, rused, vused, dointeger, dobinary); |
702 |
|
|
|
703 |
|
|
} |
704 |
|
|
|
705 |
|
|
|
706 |
|
|
|
707 |
|
|
void do_rhs(FILE *out, /* file */ |
708 |
|
|
real64 bcol[], /* array of data */ |
709 |
|
|
char relopcol[], /* is it incident? */ |
710 |
|
|
int32 rused, /* size of array */ |
711 |
|
|
int32 vused) /* number RHS column vused+1 */ |
712 |
|
|
|
713 |
|
|
/*** |
714 |
|
|
*** Prints out the right hand side data |
715 |
|
|
***/ |
716 |
|
|
{ |
717 |
|
|
int i; |
718 |
|
|
|
719 |
|
|
FPRINTF(out,"RHS\n"); /* section header */ |
720 |
|
|
|
721 |
|
|
for(i = 0; i < rused; i++) /* loop over all rows */ |
722 |
|
|
if (relopcol[i] != rel_TOK_nonincident) /* if it is incident, nonfixed */ |
723 |
|
|
print_col_element(out, vused , i, bcol[i]); /* then print out stuff */ |
724 |
|
|
|
725 |
|
|
print_col_element(out, -1 , 0, 0.0); /* clean up newline */ |
726 |
|
|
|
727 |
|
|
} |
728 |
|
|
|
729 |
|
|
void do_bounds(FILE *out, /* file */ |
730 |
|
|
real64 lbrow[], /* array of data */ |
731 |
|
|
real64 ubrow[], /* array of data */ |
732 |
|
|
char typerow[], /* array of data */ |
733 |
|
|
int32 rused, /* size of arrays */ |
734 |
|
|
int nonneg, /* allow nonneg vars (no FR or MI) ? */ |
735 |
|
|
int binary_flag, /* allow BV vars ? */ |
736 |
|
|
int integer_flag, /* allow UI vars ? */ |
737 |
|
|
int semi_flag, /* allow SC vars ? */ |
738 |
|
|
double pinf, /* any UB>=pinf is set to + infinity */ |
739 |
|
|
double minf) /* any LB<=minf is set to - infinity */ |
740 |
|
|
|
741 |
|
|
/*** This routine creates the BOUNDS section of the MPS file. |
742 |
|
|
*** It checks the following flags: |
743 |
|
|
*** |
744 |
|
|
*** iarray[SP6_NONNEG] 0->solver handles free vars |
745 |
|
|
*** 1->solver requires that all vars have LB=0, |
746 |
|
|
UB=infinity, no FR or MI |
747 |
|
|
*** iarray[SP6_BINARY] 0->solver supports binary variables using INTORG |
748 |
|
|
*** 1->solver supports binary variables with |
749 |
|
|
BV option in BOUNDS |
750 |
|
|
*** 2->no support |
751 |
|
|
*** iarray[SP6_INTEGER] 0->solver defines integer vars using INTORG |
752 |
|
|
*** 1->solver defines integer vars using UI in BOUNDS |
753 |
|
|
*** 2->no support for integer vars |
754 |
|
|
*** iarray[SP6_SEMI] 0->no support |
755 |
|
|
*** 1->solver supports SCICONIC style semi- |
756 |
|
|
continuous vars |
757 |
|
|
*** rarray[SP6_PINF] any UB >= pinf is set to + infinity |
758 |
|
|
*** rarray[SP6_MINF] any LB <= minf is set to - infinity |
759 |
|
|
** |
760 |
|
|
*** The following formats are defined: |
761 |
|
|
*** |
762 |
|
|
*** LO Lower bound bj <= xj ( <= pinfinity) |
763 |
|
|
*** UP Upper bound (0 <= ) xj < bj |
764 |
|
|
*** FX Fixed variable xj = bj |
765 |
|
|
*** FR Free variable minfinity < xj < pinfinity |
766 |
|
|
*** MI Lower bound minfinity minfinity < xj ( <= 0) |
767 |
|
|
*** PL Upper bound pinfinity (0 <= ) xj <= pinfinity |
768 |
|
|
*** |
769 |
|
|
*** Where default bounds of 0 or infinity are shown in () |
770 |
|
|
*** A default of PL is assumed for all variables, so it is not |
771 |
|
|
*** explicitly writen to the file |
772 |
|
|
***/ |
773 |
|
|
{ |
774 |
|
|
int i; |
775 |
|
|
|
776 |
|
|
FPRINTF(out,"BOUNDS\n"); /* section header */ |
777 |
|
|
|
778 |
|
|
for(i = 0; i < rused; i++) /* loop over all rows */ |
779 |
|
|
{ |
780 |
|
|
if ((typerow[i] == SOLVER_BINARY) && (binary_flag == 1)) /* do BV */ |
781 |
|
|
FPRINTF(out," BV B%07d C%07d\n",i,i); |
782 |
|
|
else if ((typerow[i] == SOLVER_INT) && (integer_flag == 1)) /* do UI */ |
783 |
|
|
{ |
784 |
|
|
FPRINTF(out," UI B%07d C%07d %12.6G\n",i,i,ubrow[i]); |
785 |
|
|
if (lbrow[i] != 0.0) /* LB of 0 is assumed, so don't need to add it */ |
786 |
|
|
FPRINTF(out," LO B%07d C%07d %12.6G\n",i,i,lbrow[i]); |
787 |
|
|
} |
788 |
|
|
else if ((typerow[i] == SOLVER_SEMI) && (semi_flag == 1)) /* do SC, upper bound is value */ |
789 |
|
|
|
790 |
|
|
FPRINTF(out," SC B%07d C%07d %12.6G\n",i,i,ubrow[i]); |
791 |
|
|
|
792 |
|
|
else if ((ubrow[i] >= pinf) && (lbrow[i] <= minf) && (nonneg == 0)) /* do FR */ |
793 |
|
|
|
794 |
|
|
FPRINTF(out," FR B%07d C%07d\n",i,i); |
795 |
|
|
|
796 |
|
|
else if ((ubrow[i] <= pinf) && (lbrow[i] <= minf) && (nonneg == 0)) /* do MI */ |
797 |
|
|
{ |
798 |
|
|
FPRINTF(out," MI B%07d C%07d\n",i,i,ubrow[i]); |
799 |
|
|
if (ubrow[i] != 0.0) /* UB of 0 is assumed, so don't need to add it */ |
800 |
|
|
FPRINTF(out," UP B%07d C%07d %12.6G\n",i,i,ubrow[i]); |
801 |
|
|
} |
802 |
|
|
else if (ubrow[i] == lbrow[i]) /* do FX */ |
803 |
|
|
|
804 |
|
|
FPRINTF(out," FX B%07d C%07d %12.6G\n",i,i,ubrow[i]); |
805 |
|
|
|
806 |
|
|
else if ((ubrow[i] >= pinf) && (lbrow[i] == 0.0)) /* do PL */ |
807 |
|
|
|
808 |
|
|
continue; /* are default limits, no bound necessary */ |
809 |
|
|
|
810 |
|
|
else /* do normal UB and LB */ |
811 |
|
|
{ |
812 |
|
|
if (lbrow[i] != 0.0) /* LB of 0 is assumed, so don't need to add it */ |
813 |
|
|
FPRINTF(out," LO B%07d C%07d %12.6G\n",i,i,lbrow[i]); |
814 |
|
|
|
815 |
|
|
if (ubrow[i] <= pinf) /* UB of + infinity is assumed, so don't need to add it */ |
816 |
|
|
FPRINTF(out," UP B%07d C%07d %12.6G\n",i,i,ubrow[i]); |
817 |
|
|
} |
818 |
|
|
|
819 |
|
|
} |
820 |
|
|
|
821 |
|
|
} |
822 |
|
|
|
823 |
|
|
|
824 |
|
|
/* _________________________________________________________________________ */ |
825 |
|
|
|
826 |
|
|
/* writes out an MPS file */ |
827 |
|
|
|
828 |
|
|
extern boolean write_MPS(const char *name, /* filename for output */ |
829 |
|
|
mps_data_t mps, /* the main chunk of data for the problem */ |
830 |
|
|
int iarray[slv6_IA_SIZE], /* Integer subparameters */ |
831 |
|
|
double rarray[slv6_RA_SIZE]) /* Real subparameters */ |
832 |
|
|
{ |
833 |
|
|
FILE *out; |
834 |
|
|
int32 sosvar; /* number of variables used in SOS's */ |
835 |
|
|
int32 sosrel; /* number of relations defining SOS's */ |
836 |
|
|
int i; /* temporary counter */ |
837 |
|
|
|
838 |
|
|
if (name == NULL) { /* got a bad pointer */ |
839 |
|
|
FPRINTF(stderr,"ERROR: (MPS) write_MPS\n"); |
840 |
|
|
FPRINTF(stderr," Routine was passed a NULL pointer!\n"); |
841 |
|
|
return FALSE; |
842 |
|
|
} |
843 |
|
|
|
844 |
|
|
out = open_write(name); |
845 |
|
|
if (out == NULL) return FALSE; |
846 |
|
|
|
847 |
|
|
/* create header */ |
848 |
|
|
do_name(out, /* file */ |
849 |
|
|
iarray[SP6_OBJ], /* how does it know to max/min */ |
850 |
|
|
iarray[SP6_BO], /* QOMILP style cutoff */ |
851 |
|
|
iarray[SP6_EPS], /* QOMILP style termination criteria */ |
852 |
|
|
rarray[SP6_BOVAL], /* value of cutoff */ |
853 |
|
|
rarray[SP6_EPSVAL]); /* value of termination criteria */ |
854 |
|
|
|
855 |
|
|
do_rows(out, /* file */ |
856 |
|
|
mps.relopcol, /* need type of constraint <=, >=, = */ |
857 |
|
|
mps.rinc); /* number of incident relations */ |
858 |
|
|
|
859 |
|
|
upgrade_vars(out, /* file */ |
860 |
|
|
mps.typerow, /* array of variable type data */ |
861 |
|
|
mps.ubrow, /* change ub on int -> bin conversion */ |
862 |
|
|
mps.vused, /* number of vars */ |
863 |
|
|
iarray[SP6_RELAXED], /* should the relaxed problem be solved */ |
864 |
|
|
iarray[SP6_INTEGER], /* supports integer vars */ |
865 |
|
|
iarray[SP6_BINARY], /* supports binary vars */ |
866 |
|
|
iarray[SP6_SEMI]); /* supports semi-continuous vars */ |
867 |
|
|
|
868 |
|
|
if ((iarray[SP6_SOS1] == 1) || (iarray[SP6_SOS3] == 1)) /* look for SOS's, reorder matrix */ |
869 |
|
|
scan_SOS(mps.Ac_mtx, /* Matrix representation of problem */ |
870 |
|
|
mps.relopcol, /* array of relational operator data */ |
871 |
|
|
mps.bcol, /* array of RHS data */ |
872 |
|
|
mps.typerow, /* array of variable type data */ |
873 |
|
|
mps.rinc, /* size of incident relations */ |
874 |
|
|
mps.vinc, /* number of vars */ |
875 |
|
|
&sosvar, /* output: number of variables used in SOS's */ |
876 |
|
|
&sosrel); /* output: number of relations defining SOS's */ |
877 |
|
|
else { |
878 |
|
|
sosvar = 0; /* set for use in do_columns */ |
879 |
|
|
sosrel = 0; |
880 |
|
|
} |
881 |
|
|
|
882 |
|
|
if (iarray[SP6_SOS2] == 1) { /* don't support SOS2 yet */ |
883 |
|
|
FPRINTF(stderr,"WARNING: (MPS) write_MPS\n"); |
884 |
|
|
FPRINTF(stderr," SOS2 are not currently supported in ASCEND!\n"); |
885 |
|
|
} |
886 |
|
|
|
887 |
|
|
do_columns(out, /* file */ |
888 |
|
|
mps.Ac_mtx, /* Matrix representation of problem */ |
889 |
|
|
mps.typerow, /* array of variable type data */ |
890 |
|
|
mps.rused, /* number of relations */ |
891 |
|
|
mps.vused, /* number of vars */ |
892 |
|
|
sosvar, /* number of variables used in SOS's */ |
893 |
|
|
sosrel, /* number of SOS's */ |
894 |
|
|
iarray[SP6_INTEGER], /* supports integer vars */ |
895 |
|
|
iarray[SP6_BINARY]); /* supports binary vars */ |
896 |
|
|
|
897 |
|
|
do_rhs(out, /* file */ |
898 |
|
|
mps.bcol, |
899 |
|
|
mps.relopcol, |
900 |
|
|
mps.rinc, |
901 |
|
|
mps.vinc); |
902 |
|
|
|
903 |
|
|
do_bounds(out, /* file */ |
904 |
|
|
mps.lbrow, /* array of data */ |
905 |
|
|
mps.ubrow, /* array of data */ |
906 |
|
|
mps.typerow, /* array of data */ |
907 |
|
|
mps.rused, /* size of arrays */ |
908 |
|
|
iarray[SP6_NONNEG], /* allow nonneg vars (no FR or MI) ? */ |
909 |
|
|
iarray[SP6_BINARY], /* allow BV vars ? */ |
910 |
|
|
iarray[SP6_INTEGER], /* allow UI vars ? */ |
911 |
|
|
iarray[SP6_SEMI], /* allow SC vars ? */ |
912 |
|
|
rarray[SP6_PINF], /* any UB>=pinf is set to + infinity */ |
913 |
|
|
rarray[SP6_MINF]); /* any LB<=minf is set to - infinity */ |
914 |
|
|
|
915 |
|
|
FPRINTF(out, "ENDATA\n"); /* finish up the file */ |
916 |
|
|
|
917 |
|
|
return close_file(out); |
918 |
|
|
} |
919 |
|
|
|
920 |
|
|
#endif |
921 |
|
|
|
922 |
|
|
|