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