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_smps.c
26   	 * @ingroup DEFPLUGINS_READER
27   	 * @brief  SMPS file reader - smps files list the cor, tim and sto files for a single instance
28   	 * @author Stephen J. Maher
29   	 */
30   	
31   	
32   	/*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
33   	
34   	#include "blockmemshell/memory.h"
35   	#include "scip/pub_fileio.h"
36   	#include "scip/pub_message.h"
37   	#include "scip/pub_misc.h"
38   	#include "scip/pub_reader.h"
39   	#include "scip/reader_cor.h"
40   	#include "scip/reader_smps.h"
41   	#include "scip/reader_sto.h"
42   	#include "scip/reader_tim.h"
43   	#include "scip/scip_mem.h"
44   	#include "scip/scip_message.h"
45   	#include "scip/scip_prob.h"
46   	#include "scip/scip_reader.h"
47   	#include <string.h>
48   	
49   	#if !defined(_WIN32) && !defined(_WIN64)
50   	#include <strings.h> /*lint --e{766}*/ /* needed for strncasecmp() */
51   	#endif
52   	
53   	/*
54   	 * The SMPS reader coordinates the reading of the cor, tim and sto files. The public reading methods from the cor, tim
55   	 * and sto readers are called from the SMPS reader. So, the header files for the cor, tim and sto readers are required.
56   	 */
57   	
58   	#define READER_NAME             "smpsreader"
59   	#define READER_DESC             "file reader for core problem of stochastic programs in the SMPS file format"
60   	#define READER_EXTENSION        "smps"
61   	
62   	#define SMPS_MAX_LINELEN  1024
63   	#define BLANK              ' '
64   	#define LINEWIDTH           80
65   	
66   	#define COR_FILEEXTENSION        "cor"
67   	#define TIM_FILEEXTENSION        "tim"
68   	#define STO_FILEEXTENSION        "sto"
69   	
70   	/** enum for the file types that are read by the SMPS reader */
71   	enum SCIP_SmpsFileType
72   	{
73   	   SCIP_SMPSFILETYPE_COR = 0,
74   	   SCIP_SMPSFILETYPE_TIM = 1,
75   	   SCIP_SMPSFILETYPE_STO = 2
76   	};
77   	typedef enum SCIP_SmpsFileType SCIP_SMPSFILETYPE;
78   	
79   	
80   	/** smps input structure */
81   	struct SmpsInput
82   	{
83   	   SCIP_FILE*            fp;
84   	   int                   lineno;
85   	   SCIP_Bool             haserror;
86   	   char                  buf[SMPS_MAX_LINELEN];
87   	   const char*           f0;
88   	   const char*           f1;
89   	};
90   	typedef struct SmpsInput SMPSINPUT;
91   	
92   	
93   	/** creates the smps input structure */
94   	static
95   	SCIP_RETCODE smpsinputCreate(
96   	   SCIP*                 scip,               /**< SCIP data structure */
97   	   SMPSINPUT**           smpsi,               /**< smps input structure */
98   	   SCIP_FILE*            fp                  /**< file object for the input file */
99   	   )
100  	{
101  	   assert(smpsi != NULL);
102  	   assert(fp != NULL);
103  	
104  	   SCIP_CALL( SCIPallocBlockMemory(scip, smpsi) );
105  	
106  	   (*smpsi)->fp          = fp;
107  	   (*smpsi)->lineno      = 0;
108  	   (*smpsi)->haserror    = FALSE;
109  	   (*smpsi)->buf     [0] = '\0';
110  	   (*smpsi)->f0          = NULL;
111  	   (*smpsi)->f1          = NULL;
112  	
113  	   return SCIP_OKAY;
114  	}
115  	
116  	/** free the smps input structure */
117  	static
118  	void smpsinputFree(
119  	   SCIP*                 scip,               /**< SCIP data structure */
120  	   SMPSINPUT**           smpsi               /**< smps input structure */
121  	   )
122  	{
123  	   SCIPfreeBlockMemory(scip, smpsi);
124  	}
125  	
126  	/** return the current value of field 0 */
127  	static
128  	const char* smpsinputField0(
129  	   const SMPSINPUT*      smpsi               /**< smps input structure */
130  	   )
131  	{
132  	   assert(smpsi != NULL);
133  	
134  	   return smpsi->f0;
135  	}
136  	
137  	/** fill the line from \p pos up to column LINEWIDTH with blanks. */
138  	static
139  	void clearFrom(
140  	   char*                 buf,                /**< buffer to clear */
141  	   unsigned int          pos                 /**< position to start the clearing process */
142  	   )
143  	{
144  	   unsigned int i;
145  	
146  	   for(i = pos; i < LINEWIDTH; i++)
147  	      buf[i] = BLANK;
148  	   buf[LINEWIDTH] = '\0';
149  	}
150  	
151  	/** read a smps format data line and parse the fields. */
152  	static
153  	SCIP_Bool smpsinputReadLine(
154  	   SMPSINPUT*            smpsi               /**< smps input structure */
155  	   )
156  	{
157  	   unsigned int len;
158  	   unsigned int i;
159  	   SCIP_Bool is_marker;
160  	   SCIP_Bool is_empty;
161  	   char* nexttok;
162  	
163  	   do
164  	   {
165  	      smpsi->f0 = smpsi->f1 = 0;
166  	      is_marker = FALSE;
167  	
168  	      /* Read until we have not a comment line. */
169  	      do
170  	      {
171  	         smpsi->buf[SMPS_MAX_LINELEN-1] = '\0';
172  	         if( NULL == SCIPfgets(smpsi->buf, (int) sizeof(smpsi->buf), smpsi->fp) )
173  	            return FALSE;
174  	         smpsi->lineno++;
175  	      }
176  	      while( *smpsi->buf == '*' );
177  	
178  	      /* Normalize line */
179  	      len = (unsigned int) strlen(smpsi->buf);
180  	
181  	      /* replace tabs and new lines by blanks */
182  	      for( i = 0; i < len; i++ )
183  	      {
184  	         if( (smpsi->buf[i] == '\t') || (smpsi->buf[i] == '\n') || (smpsi->buf[i] == '\r') )
185  	            smpsi->buf[i] = BLANK;
186  	      }
187  	
188  	      if( len < LINEWIDTH )
189  	         clearFrom(smpsi->buf, len);
190  	
191  	      SCIPdebugMessage("line %d: <%s>\n", smpsi->lineno, smpsi->buf);
192  	
193  	      assert(strlen(smpsi->buf) >= LINEWIDTH);
194  	
195  	      /* Look for new section */
196  	      if( *smpsi->buf != BLANK )
197  	      {
198  	         smpsi->f0 = SCIPstrtok(&smpsi->buf[0], " ", &nexttok);
199  	
200  	         assert(smpsi->f0 != 0);
201  	
202  	         smpsi->f1 = SCIPstrtok(NULL, " ", &nexttok);
203  	
204  	         return TRUE;
205  	      }
206  	
207  	      /* check for empty lines */
208  	      is_empty = (smpsi->f0 == NULL && smpsi->f1 == NULL);
209  	   }
210  	   while( is_marker || is_empty );
211  	
212  	   return TRUE;
213  	}
214  	
215  	/*
216  	 * Callback methods of reader
217  	 */
218  	
219  	/** copy method for reader plugins (called when SCIP copies plugins) */
220  	static
221  	SCIP_DECL_READERCOPY(readerCopySmps)
222  	{  /*lint --e{715}*/
223  	   assert(scip != NULL);
224  	   assert(reader != NULL);
225  	   assert(strcmp(SCIPreaderGetName(reader), READER_NAME) == 0);
226  	
227  	   /* call inclusion method of reader */
228  	   SCIP_CALL( SCIPincludeReaderSmps(scip) );
229  	
230  	   return SCIP_OKAY;
231  	}
232  	
233  	
234  	/** problem reading method of reader */
235  	static
236  	SCIP_DECL_READERREAD(readerReadSmps)
237  	{  /*lint --e{715}*/
238  	   SCIP_FILE* fp;
239  	   SMPSINPUT* smpsi;
240  	   SCIP_RETCODE retcode = SCIP_OKAY;
241  	
242  	   char corfilename[SCIP_MAXSTRLEN];
243  	   char timfilename[SCIP_MAXSTRLEN];
244  	   char stofilename[SCIP_MAXSTRLEN];
245  	   char* tmpfilename;
246  	   char* probname;
247  	   char* fileextension;
248  	   char* fromlastslash;
249  	   char parent[SCIP_MAXSTRLEN];
250  	   size_t parentlen;
251  	
252  	   SCIP_Bool hascorfile;
253  	   SCIP_Bool hastimfile;
254  	   SCIP_Bool hasstofile;
255  	
256  	   int i;
257  	
258  	   assert(scip != NULL);
259  	   assert(filename != NULL);
260  	
261  	   /* copy filename */
262  	   SCIP_CALL( SCIPduplicateBufferArray(scip, &tmpfilename, filename, (int)strlen(filename)+1) );
263  	
264  	   /* getting the problem name from the SMPS file name */
265  	   SCIPsplitFilename(tmpfilename, NULL, &probname, NULL, NULL);
266  	
267  	   fromlastslash = (char*) strrchr(filename, '/');
268  	
269  	   if( fromlastslash == NULL )
270  	      parentlen = 0;
271  	   else
272  	      parentlen = strlen(filename) - (strlen(fromlastslash) - 1);
273  	
274  	   (void)SCIPstrncpy(parent, filename, (int)parentlen + 1);
275  	
276  	   fp = SCIPfopen(filename, "r");
277  	   if( fp == NULL )
278  	   {
279  	      SCIPerrorMessage("cannot open file <%s> for reading\n", filename);
280  	      SCIPprintSysError(filename);
281  	
282  	      return SCIP_NOFILE;
283  	   }
284  	
285  	   SCIP_CALL( smpsinputCreate(scip, &smpsi, fp) );
286  	
287  	   hascorfile = FALSE;
288  	   hastimfile = FALSE;
289  	   hasstofile = FALSE;
290  	   while( smpsinputReadLine(smpsi) )
291  	   {
292  	      char* tmpinput;
293  	
294  	      /* copy the input */
295  	      SCIP_CALL( SCIPduplicateBufferArray(scip, &tmpinput, smpsinputField0(smpsi),
296  	            (int)strlen(smpsinputField0(smpsi))+1) ); /*lint !e666*/
297  	
298  	      /* get extension from filename */
299  	      SCIPsplitFilename(tmpinput, NULL, NULL, &fileextension, NULL);
300  	
301  	      if( strcasecmp(fileextension, COR_FILEEXTENSION) == 0 )
302  	      {
303  	         (void) SCIPsnprintf(corfilename, SCIP_MAXSTRLEN, "%s%s", parent, smpsinputField0(smpsi));
304  	         hascorfile = TRUE;
305  	      }
306  	      else if( strcasecmp(fileextension, TIM_FILEEXTENSION) == 0 )
307  	      {
308  	         (void) SCIPsnprintf(timfilename, SCIP_MAXSTRLEN, "%s%s", parent, smpsinputField0(smpsi));
309  	         hastimfile = TRUE;
310  	      }
311  	      else if( strcasecmp(fileextension, STO_FILEEXTENSION) == 0 )
312  	      {
313  	         (void) SCIPsnprintf(stofilename, SCIP_MAXSTRLEN, "%s%s", parent, smpsinputField0(smpsi));
314  	         hasstofile = TRUE;
315  	      }
316  	
317  	      SCIPfreeBufferArray(scip, &tmpinput);
318  	   }
319  	
320  	   /* printing errors if the correct files have not been provided */
321  	   if( !hascorfile )
322  	   {
323  	      SCIPerrorMessage("The core file has not been listed in <%s>\n", filename);
324  	   }
325  	
326  	   if( !hastimfile )
327  	   {
328  	      SCIPerrorMessage("The tim file has not been listed in <%s>\n", filename);
329  	   }
330  	
331  	   if( !hasstofile )
332  	   {
333  	      SCIPerrorMessage("The sto file has not been listed in <%s>\n", filename);
334  	   }
335  	
336  	   /* if one of the necessary file has not been provided, then an error will be returned */
337  	   if( !hascorfile || !hastimfile || !hasstofile )
338  	   {
339  	      retcode = SCIP_READERROR;
340  	      goto TERMINATE;
341  	   }
342  	
343  	   for( i = 0; i < 3; i++ )
344  	   {
345  	      int nvars;
346  	      int nbinvars;
347  	      int nintvars;
348  	      int nimplintvars;
349  	      int ncontvars;
350  	      SCIP_SMPSFILETYPE type;
351  	
352  	      type = (SCIP_SMPSFILETYPE) i;
353  	      switch( type )
354  	      {
355  	         case SCIP_SMPSFILETYPE_COR:
356  	            SCIPinfoMessage(scip, NULL, "reading core file <%s> for problem %s\n", corfilename, probname);
357  	            SCIPinfoMessage(scip, NULL, "============\n");
358  	
359  	            /* reading the CORE file */
360  	            SCIP_CALL_TERMINATE( retcode, SCIPreadCor(scip, corfilename, result), TERMINATE );
361  	
362  	            /* getting the variable information */
363  	            SCIP_CALL( SCIPgetOrigVarsData(scip, NULL, &nvars, &nbinvars, &nintvars, &nimplintvars, &ncontvars) );
364  	            SCIPinfoMessage(scip, NULL,
365  	               "core problem has %d variables (%d bin, %d int, %d impl, %d cont) and %d constraints\n",
366  	               nvars, nbinvars, nintvars, nimplintvars, ncontvars, SCIPgetNOrigConss(scip));
367  	            break;
368  	         case SCIP_SMPSFILETYPE_TIM:
369  	            SCIPinfoMessage(scip, NULL, "reading the time file <%s> for problem %s\n", timfilename, probname);
370  	            SCIPinfoMessage(scip, NULL, "============\n");
371  	
372  	            /* reading the TIME file */
373  	            SCIP_CALL_TERMINATE( retcode, SCIPreadTim(scip, timfilename, result), TERMINATE );
374  	
375  	            SCIPinfoMessage(scip, NULL, "problem %s has %d stages\n", probname, SCIPtimGetNStages(scip));
376  	            break;
377  	         case SCIP_SMPSFILETYPE_STO:
378  	#ifdef BENDERSBRANCH
379  	            SCIP_Bool usebenders;
380  	#endif
381  	
382  	            SCIPinfoMessage(scip, NULL, "read problem <%s>\n", stofilename);
383  	            SCIPinfoMessage(scip, NULL, "============\n");
384  	
385  	            /* reading the STO file */
386  	            SCIP_CALL_TERMINATE( retcode, SCIPreadSto(scip, stofilename, result), TERMINATE );
387  	
388  	            SCIPinfoMessage(scip, NULL, "problem %s has extended with a total of %d scenarios\n", probname,
389  	               SCIPstoGetNScenarios(scip));
390  	
391  	            /* getting the variable information */
392  	            SCIP_CALL( SCIPgetOrigVarsData(scip, NULL, &nvars, &nbinvars, &nintvars, &nimplintvars, &ncontvars) );
393  	
394  	            /* if Benders' decomposition is used, the variable will be distributed to a number of subproblems */
395  	#ifdef BENDERSBRANCH
396  	            SCIP_CALL( SCIPgetBoolParam(scip, "reading/sto/usebenders", &usebenders) );
397  	            if( usebenders )
398  	            {
399  	               SCIPinfoMessage(scip, NULL, "Benders' decomposition master problem ");
400  	            }
401  	            else
402  	#endif
403  	            {
404  	               SCIPinfoMessage(scip, NULL, "deterministic equivalent problem ");
405  	            }
406  	
407  	            SCIPinfoMessage(scip, NULL,
408  	               "has %d variables (%d bin, %d int, %d impl, %d cont) and %d constraints\n",
409  	               nvars, nbinvars, nintvars, nimplintvars, ncontvars, SCIPgetNOrigConss(scip));
410  	            break;
411  	         /* coverity[dead_error_begin] */
412  	         default:
413  	            SCIPerrorMessage("This should not happen. Aborting.\n");
414  	            SCIPABORT();
415  	            retcode = SCIP_READERROR;
416  	            goto TERMINATE;
417  	      }
418  	
419  	      SCIPinfoMessage(scip, NULL, "\n\n");
420  	   }
421  	
422  	   SCIPfclose(fp);
423  	
424  	 /* cppcheck-suppress unusedLabel */
425  	TERMINATE:
426  	   smpsinputFree(scip, &smpsi);
427  	
428  	   /* freeing buffer array */
429  	   SCIPfreeBufferArray(scip, &tmpfilename);
430  	
431  	   if( retcode == SCIP_PLUGINNOTFOUND )
432  	      retcode = SCIP_READERROR;
433  	
434  	   if( retcode == SCIP_NOFILE || retcode == SCIP_READERROR )
435  	      return retcode;
436  	
437  	   SCIP_CALL( retcode );
438  	
439  	   *result = SCIP_SUCCESS;
440  	
441  	   return SCIP_OKAY;
442  	}
443  	
444  	
445  	/*
446  	 * reader specific interface methods
447  	 */
448  	
449  	/** includes the smps file reader in SCIP */
450  	SCIP_RETCODE SCIPincludeReaderSmps(
451  	   SCIP*                 scip                /**< SCIP data structure */
452  	   )
453  	{
454  	   SCIP_READER* reader;
455  	
456  	   /* include reader */
457  	   SCIP_CALL( SCIPincludeReaderBasic(scip, &reader, READER_NAME, READER_DESC, READER_EXTENSION, NULL) );
458  	
459  	   assert(reader != NULL);
460  	
461  	   /* set non fundamental callbacks via setter functions */
462  	   SCIP_CALL( SCIPsetReaderCopy(scip, reader, readerCopySmps) );
463  	   SCIP_CALL( SCIPsetReaderRead(scip, reader, readerReadSmps) );
464  	
465  	   return SCIP_OKAY;
466  	}
467