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