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