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