1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the program and library */
4 /* SCIP --- Solving Constraint Integer Programs */
5 /* */
6 /* Copyright (C) 2002-2021 Konrad-Zuse-Zentrum */
7 /* fuer Informationstechnik Berlin */
8 /* */
9 /* SCIP is distributed under the terms of the ZIB Academic License. */
10 /* */
11 /* You should have received a copy of the ZIB Academic License */
12 /* along with SCIP; see the file COPYING. If not visit scipopt.org. */
13 /* */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15
16 /**@file reader_bnd.c
17 * @ingroup DEFPLUGINS_READER
18 * @brief file reader for variable bounds
19 * @author Ambros Gleixner
20 * @author Ingmar Vierhaus
21 * @author Benjamin Mueller
22 *
23 * This reader allows to read a file containing new bounds for variables of the current problem. Each line of the file
24 * should have format
25 *
26 * \<variable name\> \<lower bound\> \<upper bound\>
27 *
28 * where infinite bounds can be written as inf, +inf or -inf. Note that only a subset of the variables may appear in
29 * the file. Lines with unknown variable names are ignored.
30 * The writing functionality can be used in problem and transformed stages. Note that in transformed stage,
31 * the leading "t_" in the name of a transformed variable will not appear in the output. This way, bounds written in transformed stage
32 * can be read again in problem stage.
33 */
34
35 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
36
37 #include "scip/pub_fileio.h"
38 #include "scip/pub_message.h"
39 #include "scip/pub_misc.h"
40 #include "scip/pub_reader.h"
41 #include "scip/pub_var.h"
42 #include "scip/reader_bnd.h"
43 #include "scip/scip_general.h"
44 #include "scip/scip_mem.h"
45 #include "scip/scip_message.h"
46 #include "scip/scip_numerics.h"
47 #include "scip/scip_param.h"
48 #include "scip/scip_reader.h"
49 #include "scip/scip_var.h"
50 #include <string.h>
51
52 #if !defined(_WIN32) && !defined(_WIN64)
53 #include <strings.h> /*lint --e{766}*/ /* needed for strncasecmp() */
54 #endif
55
56
57 #define READER_NAME "bndreader"
58 #define READER_DESC "file reader for variable bounds"
59 #define READER_EXTENSION "bnd"
60
61 #define DEFAULT_IMPROVEONLY FALSE /**< only use improving bounds */
62
63
64 /** BND reader data */
65 struct SCIP_ReaderData
66 {
67 SCIP_Bool improveonly; /**< only use improving bounds */
68 };
69
70
71 /*
72 * Local methods of reader
73 */
74
75 /** reads a given bound file, problem has to be in problem stage */
76 static
readBounds(SCIP * scip,const char * fname,SCIP_READERDATA * readerdata)77 SCIP_RETCODE readBounds(
78 SCIP* scip, /**< SCIP data structure */
79 const char* fname, /**< name of the input file */
80 SCIP_READERDATA* readerdata /**< pointer to the data of the reader */
81 )
82 {
83 SCIP_RETCODE retcode;
84 SCIP_FILE* file;
85 SCIP_Bool error;
86 SCIP_Bool unknownvariablemessage;
87 SCIP_Bool usevartable;
88 int lineno;
89
90 assert(scip != NULL);
91 assert(fname != NULL);
92
93 SCIP_CALL( SCIPgetBoolParam(scip, "misc/usevartable", &usevartable) );
94
95 if( !usevartable )
96 {
97 SCIPerrorMessage("Cannot read bounds file if vartable is disabled. Make sure parameter 'misc/usevartable' is set to TRUE.\n");
98 return SCIP_READERROR;
99 }
100
101 /* open input file */
102 file = SCIPfopen(fname, "r");
103 if( file == NULL )
104 {
105 SCIPerrorMessage("cannot open file <%s> for reading\n", fname);
106 SCIPprintSysError(fname);
107 return SCIP_NOFILE;
108 }
109
110 /* read the file */
111 error = FALSE;
112 unknownvariablemessage = FALSE;
113 lineno = 0;
114 while( !SCIPfeof(file) && !error )
115 {
116 char buffer[SCIP_MAXSTRLEN];
117 char varname[SCIP_MAXSTRLEN];
118 char lbstring[SCIP_MAXSTRLEN];
119 char ubstring[SCIP_MAXSTRLEN];
120 char format[SCIP_MAXSTRLEN];
121 SCIP_VAR* var;
122 SCIP_Real lb;
123 SCIP_Real ub;
124 int nread;
125 char* endptr;
126
127 /* get next line */
128 if( SCIPfgets(buffer, (int) sizeof(buffer), file) == NULL )
129 break;
130 lineno++;
131
132 /* parse the line */
133 (void) SCIPsnprintf(format, SCIP_MAXSTRLEN, "%%%ds %%%ds %%%ds\n", SCIP_MAXSTRLEN, SCIP_MAXSTRLEN, SCIP_MAXSTRLEN);
134 (void) sscanf(buffer, format, varname, lbstring, ubstring);
135
136 retcode = SCIPparseVarName(scip, buffer, &var, &endptr);
137 if( retcode != SCIP_OKAY )
138 {
139 SCIPerrorMessage("Error parsing variable name in line %d of bounds file <%s>\n", lineno, fname);
140 error = TRUE;
141 break;
142 }
143
144 (void) SCIPsnprintf(format, SCIP_MAXSTRLEN, "%%%ds %%%ds\n", SCIP_MAXSTRLEN, SCIP_MAXSTRLEN);
145 nread = sscanf(endptr, format, lbstring, ubstring);
146 if( nread < 1 )
147 {
148 SCIPerrorMessage("invalid input line %d in bounds file <%s>: <%s>\n", lineno, fname, buffer);
149 error = TRUE;
150 break;
151 }
152
153 if( var == NULL )
154 {
155 if( !unknownvariablemessage )
156 {
157 SCIPwarningMessage(scip, "unable to parse variable name in line %d of bounds file <%s>:\n", lineno, fname);
158 SCIPwarningMessage(scip, "line is: %s", buffer);
159 SCIPwarningMessage(scip, " (further unknown variables are ignored)\n");
160 unknownvariablemessage = TRUE;
161 }
162 continue;
163 }
164
165 /* cast the lower bound value */
166 if( strncasecmp(lbstring, "inv", 3) == 0 )
167 continue;
168 else if( strncasecmp(lbstring, "+inf", 4) == 0 || strncasecmp(lbstring, "inf", 3) == 0 )
169 lb = SCIPinfinity(scip);
170 else if( strncasecmp(lbstring, "-inf", 4) == 0 )
171 lb = -SCIPinfinity(scip);
172 else
173 {
174 nread = sscanf(lbstring, "%lf", &lb);
175 if( nread != 1 )
176 {
177 SCIPerrorMessage("invalid lower bound value <%s> for variable <%s> in line %d of bounds file <%s>\n",
178 lbstring, varname, lineno, fname);
179 error = TRUE;
180 break;
181 }
182 }
183
184 /* cast the upper bound value */
185 if( strncasecmp(ubstring, "inv", 3) == 0 )
186 continue;
187 else if( strncasecmp(ubstring, "+inf", 4) == 0 || strncasecmp(ubstring, "inf", 3) == 0 )
188 ub = SCIPinfinity(scip);
189 else if( strncasecmp(ubstring, "-inf", 4) == 0 )
190 ub = -SCIPinfinity(scip);
191 else
192 {
193 /* coverity[secure_coding] */
194 nread = sscanf(ubstring, "%lf", &ub);
195 if( nread != 1 )
196 {
197 SCIPerrorMessage("invalid lower bound value <%s> for variable <%s> in line %d of bounds file <%s>\n",
198 ubstring, varname, lineno, fname);
199 error = TRUE;
200 break;
201 }
202 }
203
204 if( readerdata->improveonly )
205 {
206 if( SCIPisLT(scip, lb, SCIPvarGetLbGlobal(var)) )
207 {
208 SCIPwarningMessage(scip, "not applying lower bound value %s for variable <%s> in line %d of bounds file %s,"
209 " because it does not improve existing bound of %f\n",
210 lbstring, SCIPvarGetName(var), lineno, fname, SCIPvarGetLbGlobal(var));
211 }
212 if( SCIPisGT(scip, ub, SCIPvarGetUbGlobal(var)) )
213 {
214 SCIPwarningMessage(scip, "not applying upper bound value %s for variable <%s> in line %d of bounds file %s, "
215 "because it does not improve existing bound of %f\n",
216 ubstring, SCIPvarGetName(var), lineno, fname, SCIPvarGetUbGlobal(var));
217 }
218
219 /* collect best variable bounds */
220 lb = MAX(lb, SCIPvarGetLbGlobal(var)); /*lint !e666*/
221 ub = MIN(ub, SCIPvarGetUbGlobal(var)); /*lint !e666*/
222 }
223
224 /* note that we don't need to check if lb > ub in SCIPchgVar{Lb,Ub} */
225 retcode = SCIPchgVarLb(scip, var, lb);
226 if( retcode != SCIP_OKAY )
227 {
228 SCIPerrorMessage("Error changing lower bound for variable <%s> in line %d of bounds file <%s>\n", varname, lineno, fname);
229 error = TRUE;
230 break;
231 }
232
233 retcode = SCIPchgVarUb(scip, var, ub);
234 if( retcode != SCIP_OKAY )
235 {
236 SCIPerrorMessage("Error changing upper bound for variable <%s> in line %d of bounds file <%s>\n", varname, lineno, fname);
237 error = TRUE;
238 break;
239 }
240 }
241
242 /* close input file */
243 SCIPfclose(file);
244
245 /* return error if necessary */
246 if ( error )
247 return SCIP_READERROR;
248
249 return SCIP_OKAY;
250 }
251
252
253 /*
254 * Callback methods of reader
255 */
256
257 /** copy method for reader plugins (called when SCIP copies plugins) */
258 static
SCIP_DECL_READERCOPY(readerCopyBnd)259 SCIP_DECL_READERCOPY(readerCopyBnd)
260 { /*lint --e{715}*/
261 assert(scip != NULL);
262 assert(reader != NULL);
263 assert(strcmp(SCIPreaderGetName(reader), READER_NAME) == 0);
264
265 /* call inclusion method of reader */
266 SCIP_CALL( SCIPincludeReaderBnd(scip) );
267
268 return SCIP_OKAY;
269 }
270
271
272 /** problem reading method of reader
273 *
274 * In order to determine the type of the file, we have to open it. Thus, it has to be opened
275 * twice. This might be removed, but is likely to not hurt the performance too much.
276 */
277 static
SCIP_DECL_READERREAD(readerReadBnd)278 SCIP_DECL_READERREAD(readerReadBnd)
279 { /*lint --e{715}*/
280 assert(reader != NULL);
281 assert(strcmp(SCIPreaderGetName(reader), READER_NAME) == 0);
282 assert(result != NULL);
283
284 *result = SCIP_DIDNOTRUN;
285
286 if( SCIPgetStage(scip) < SCIP_STAGE_PROBLEM )
287 {
288 SCIPerrorMessage("reading of bounds file is only possible after a problem was created\n");
289 return SCIP_READERROR;
290 }
291
292 if( SCIPgetStage(scip) > SCIP_STAGE_PROBLEM )
293 {
294 SCIPerrorMessage("reading of bounds file is only possible during problem creation stage\n");
295 return SCIP_READERROR;
296 }
297
298 /* read bounds file */
299 SCIP_CALL( readBounds(scip, filename, SCIPreaderGetData(reader)) );
300
301 *result = SCIP_SUCCESS;
302
303 return SCIP_OKAY;
304 }
305
306 /** outputs given bounds into a file stream */
307 static
printBounds(SCIP * scip,SCIP_MESSAGEHDLR * messagehdlr,FILE * file,SCIP_Real lb,SCIP_Real ub)308 void printBounds(
309 SCIP* scip, /**< SCIP data structure */
310 SCIP_MESSAGEHDLR* messagehdlr, /**< message handler */
311 FILE* file, /**< file stream to print into, or NULL for stdout */
312 SCIP_Real lb, /**< lower bound */
313 SCIP_Real ub /**< upper bound */
314 )
315 {
316 /* print lower bound */
317 if( SCIPisInfinity(scip, lb) )
318 SCIPmessageFPrintInfo(messagehdlr, file, "+inf ");
319 else if( SCIPisInfinity(scip, -lb) )
320 SCIPmessageFPrintInfo(messagehdlr, file, "-inf ");
321 else
322 SCIPmessageFPrintInfo(messagehdlr, file, "%.15" SCIP_REAL_FORMAT " ", lb);
323
324 /* print upper bound */
325 if( SCIPisInfinity(scip, ub) )
326 SCIPmessageFPrintInfo(messagehdlr, file, "+inf");
327 else if( SCIPisInfinity(scip, -ub) )
328 SCIPmessageFPrintInfo(messagehdlr, file, "-inf");
329 else
330 SCIPmessageFPrintInfo(messagehdlr, file, "%.15" SCIP_REAL_FORMAT, ub);
331 }
332
333 /** writes problem to file */
334 static
SCIPwriteBnd(SCIP * scip,FILE * file,SCIP_VAR ** vars,int nvars,SCIP_RESULT * result)335 SCIP_RETCODE SCIPwriteBnd(
336 SCIP* scip, /**< SCIP data structure */
337 FILE* file, /**< file stream to print into, or NULL for stdout */
338 SCIP_VAR** vars, /**< array with active variables ordered binary, integer, implicit, continuous */
339 int nvars, /**< number of active variables in the problem */
340 SCIP_RESULT* result /**< pointer to store the result of the file writing call */
341 )
342 {
343 SCIP_MESSAGEHDLR* messagehdlr;
344 SCIP_Real lb;
345 SCIP_Real ub;
346 int i;
347
348 assert(result != NULL);
349
350 messagehdlr = SCIPgetMessagehdlr(scip);
351 *result = SCIP_SUCCESS;
352
353 if( nvars == 0 )
354 {
355 SCIPwarningMessage(scip, "Problem has no variables, no bounds written.\n");
356 return SCIP_OKAY;
357 }
358
359 for( i = 0; i < nvars; ++i )
360 {
361 SCIP_VAR* var;
362 const char* varname;
363
364 var = vars[i];
365 assert( var != NULL );
366 varname = SCIPvarGetName(var);
367
368 /* strip 't_' from varname */
369 if( SCIPvarIsTransformedOrigvar(var) && strncmp(SCIPvarGetName(var), "t_", 2) == 0)
370 {
371 varname = varname + 2;
372 }
373
374 SCIPinfoMessage(scip, file, "<%s> ", varname);
375
376 /* print global bounds for transformed variables, original bounds for original variables */
377 if( !SCIPvarIsTransformed(var) )
378 {
379 lb = SCIPvarGetLbOriginal(var);
380 ub = SCIPvarGetUbOriginal(var);
381 }
382 else
383 {
384 lb = SCIPvarGetLbGlobal(var);
385 ub = SCIPvarGetUbGlobal(var);
386 }
387
388 /* print bounds into the file */
389 printBounds(scip, messagehdlr, file, lb, ub);
390 SCIPmessageFPrintInfo(messagehdlr, file, "\n");
391 }
392
393 return SCIP_OKAY;
394 }
395
396 /** problem writing method of reader */
397 static
SCIP_DECL_READERWRITE(readerWriteBnd)398 SCIP_DECL_READERWRITE(readerWriteBnd)
399 { /*lint --e{715}*/
400 assert(reader != NULL);
401 assert(strcmp(SCIPreaderGetName(reader), READER_NAME) == 0);
402
403 SCIP_CALL( SCIPwriteBnd(scip, file, vars, nvars, result) );
404
405 return SCIP_OKAY;
406 }
407
408 /** destructor of reader to free reader data (called when SCIP is exiting) */
409 static
SCIP_DECL_READERFREE(readerFreeBnd)410 SCIP_DECL_READERFREE(readerFreeBnd)
411 {
412 SCIP_READERDATA* readerdata;
413
414 assert(strcmp(SCIPreaderGetName(reader), READER_NAME) == 0);
415 readerdata = SCIPreaderGetData(reader);
416 assert(readerdata != NULL);
417 SCIPfreeBlockMemory(scip, &readerdata);
418
419 return SCIP_OKAY;
420 }
421
422 /*
423 * bnd file reader specific interface methods
424 */
425
426 /** includes the bnd file reader in SCIP */
SCIPincludeReaderBnd(SCIP * scip)427 SCIP_RETCODE SCIPincludeReaderBnd(
428 SCIP* scip /**< SCIP data structure */
429 )
430 {
431 SCIP_READERDATA* readerdata;
432 SCIP_READER* reader;
433
434 /* create reader data */
435 SCIP_CALL( SCIPallocBlockMemory(scip, &readerdata) );
436
437 /* include reader */
438 SCIP_CALL( SCIPincludeReaderBasic(scip, &reader, READER_NAME, READER_DESC, READER_EXTENSION, readerdata) );
439
440 /* set non fundamental callbacks via setter functions */
441 SCIP_CALL( SCIPsetReaderCopy(scip, reader, readerCopyBnd) );
442 SCIP_CALL( SCIPsetReaderRead(scip, reader, readerReadBnd) );
443 SCIP_CALL( SCIPsetReaderWrite(scip, reader, readerWriteBnd) );
444 SCIP_CALL( SCIPsetReaderFree(scip, reader, readerFreeBnd) );
445
446 /* add bnd reader parameters */
447 SCIP_CALL( SCIPaddBoolParam(scip,
448 "reading/bndreader/improveonly", "only use improving bounds",
449 &readerdata->improveonly, FALSE, DEFAULT_IMPROVEONLY, NULL, NULL) );
450
451 return SCIP_OKAY;
452 }
453