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 scipshell.c 26 * @ingroup OTHER_CFILES 27 * @brief SCIP command line interface 28 * @author Tobias Achterberg 29 */ 30 31 /*--+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/ 32 33 #include <stdio.h> 34 #include <string.h> 35 #include <ctype.h> 36 37 #include "scip/scip.h" 38 #include "scip/scipdefplugins.h" 39 #include "scip/scipshell.h" 40 #include "scip/message_default.h" 41 #include "scip/reader_nl.h" 42 43 /* 44 * Message Handler 45 */ 46 47 static 48 SCIP_RETCODE readParams( 49 SCIP* scip, /**< SCIP data structure */ 50 const char* filename /**< parameter file name */ 51 ) 52 { 53 if( SCIPfileExists(filename) ) 54 { 55 SCIPinfoMessage(scip, NULL, "reading user parameter file <%s>\n", filename); 56 SCIP_CALL( SCIPreadParams(scip, filename) ); 57 } 58 else 59 SCIPinfoMessage(scip, NULL, "user parameter file <%s> not found - using default parameters\n", filename); 60 61 return SCIP_OKAY; 62 } 63 64 static 65 SCIP_RETCODE fromCommandLine( 66 SCIP* scip, /**< SCIP data structure */ 67 const char* filename /**< input file name */ 68 ) 69 { 70 SCIP_RETCODE retcode; 71 SCIP_Bool outputorigsol = FALSE; 72 73 /******************** 74 * Problem Creation * 75 ********************/ 76 77 /** @note The message handler should be only fed line by line such the message has the chance to add string in front 78 * of each message 79 */ 80 SCIPinfoMessage(scip, NULL, "\n"); 81 SCIPinfoMessage(scip, NULL, "read problem <%s>\n", filename); 82 SCIPinfoMessage(scip, NULL, "============\n"); 83 SCIPinfoMessage(scip, NULL, "\n"); 84 85 retcode = SCIPreadProb(scip, filename, NULL); 86 87 switch( retcode ) 88 { 89 case SCIP_NOFILE: 90 SCIPinfoMessage(scip, NULL, "file <%s> not found\n", filename); 91 return SCIP_OKAY; 92 case SCIP_PLUGINNOTFOUND: 93 SCIPinfoMessage(scip, NULL, "no reader for input file <%s> available\n", filename); 94 return SCIP_OKAY; 95 case SCIP_READERROR: 96 SCIPinfoMessage(scip, NULL, "error reading file <%s>\n", filename); 97 return SCIP_OKAY; 98 default: 99 SCIP_CALL( retcode ); 100 } /*lint !e788*/ 101 102 /******************* 103 * Problem Solving * 104 *******************/ 105 106 /* solve problem */ 107 SCIPinfoMessage(scip, NULL, "\nsolve problem\n"); 108 SCIPinfoMessage(scip, NULL, "=============\n\n"); 109 110 SCIP_CALL( SCIPsolve(scip) ); 111 112 /******************* 113 * Solution Output * 114 *******************/ 115 116 SCIP_CALL( SCIPgetBoolParam(scip, "misc/outputorigsol", &outputorigsol) ); 117 if ( outputorigsol ) 118 { 119 SCIP_SOL* bestsol; 120 121 SCIPinfoMessage(scip, NULL, "\nprimal solution (original space):\n"); 122 SCIPinfoMessage(scip, NULL, "=================================\n\n"); 123 124 bestsol = SCIPgetBestSol(scip); 125 if ( bestsol == NULL ) 126 SCIPinfoMessage(scip, NULL, "no solution available\n"); 127 else 128 { 129 SCIP_SOL* origsol; 130 131 SCIP_CALL( SCIPcreateSolCopy(scip, &origsol, bestsol) ); 132 SCIP_CALL( SCIPretransformSol(scip, origsol) ); 133 SCIP_CALL( SCIPprintSol(scip, origsol, NULL, FALSE) ); 134 SCIP_CALL( SCIPfreeSol(scip, &origsol) ); 135 } 136 } 137 else 138 { 139 SCIPinfoMessage(scip, NULL, "\nprimal solution (transformed space):\n"); 140 SCIPinfoMessage(scip, NULL, "====================================\n\n"); 141 142 SCIP_CALL( SCIPprintBestSol(scip, NULL, FALSE) ); 143 } 144 145 /************** 146 * Statistics * 147 **************/ 148 149 SCIPinfoMessage(scip, NULL, "\nStatistics\n"); 150 SCIPinfoMessage(scip, NULL, "==========\n\n"); 151 152 SCIP_CALL( SCIPprintStatistics(scip, NULL) ); 153 154 return SCIP_OKAY; 155 } 156 157 /** runs SCIP as if it was called by AMPL */ 158 static 159 SCIP_RETCODE fromAmpl( 160 SCIP* scip, /**< SCIP data structure */ 161 char* nlfilename, /**< name of .nl file, without the .nl */ 162 SCIP_Bool interactive, /**< whether to start SCIP shell instead of solve only */ 163 const char* defaultsetname /**< name of default settings file */ 164 ) 165 { 166 #ifdef SCIP_WITH_AMPL 167 char fullnlfilename[SCIP_MAXSTRLEN]; 168 char* logfile; 169 SCIP_Bool printstat; 170 char* options; 171 size_t nlfilenamelen; 172 173 SCIP_CALL( SCIPaddBoolParam(scip, "display/statistics", 174 "whether to print statistics on a solve", 175 &printstat, FALSE, FALSE, NULL, NULL) ); 176 177 SCIP_CALL( SCIPaddStringParam(scip, "display/logfile", 178 "name of file to write SCIP log to (additionally to writing to stdout)", 179 NULL, FALSE, "", NULL, NULL) ); 180 181 SCIPprintVersion(scip, NULL); 182 SCIPinfoMessage(scip, NULL, "\n"); 183 184 SCIPprintExternalCodes(scip, NULL); 185 SCIPinfoMessage(scip, NULL, "\n"); 186 187 options = getenv("scip_options"); 188 if( options != NULL ) 189 { 190 /* parse and apply options from scip_options env variable */ 191 char* optname; 192 char* optval; 193 194 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "applying scip_options:\n"); 195 196 optname = strtok(options, " "); 197 optval = strtok(NULL, " "); 198 while( optname != NULL && optval != NULL ) 199 { 200 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " %s = %s\n", optname, optval); 201 SCIP_CALL( SCIPsetParam(scip, optname, optval) ); 202 203 optname = strtok(NULL, " "); 204 optval = strtok(NULL, " "); 205 } 206 } 207 208 if( defaultsetname != NULL ) 209 { 210 if( SCIPfileExists(defaultsetname) ) 211 { 212 SCIPinfoMessage(scip, NULL, "reading user parameter file <%s>\n", defaultsetname); 213 SCIPinfoMessage(scip, NULL, "===========================\n\n"); 214 SCIP_CALL( SCIPreadParams(scip, defaultsetname) ); 215 SCIP_CALL( SCIPwriteParams(scip, NULL, FALSE, TRUE) ); 216 SCIPinfoMessage(scip, NULL, "\n"); 217 } 218 else 219 { 220 SCIPinfoMessage(scip, NULL, "user parameter file <%s> not found - using default parameters\n", defaultsetname); 221 } 222 } 223 224 SCIP_CALL( SCIPgetStringParam(scip, "display/logfile", &logfile) ); 225 if( *logfile ) 226 SCIPsetMessagehdlrLogfile(scip, logfile); 227 228 /* AMPL calls solver with file without .nl extension, but others (Pyomo) may not 229 * so add .nl only if not already present 230 */ 231 nlfilenamelen = strlen(nlfilename); 232 if( nlfilenamelen > 3 && strcmp(nlfilename + (nlfilenamelen-3), ".nl") == 0 ) 233 (void) SCIPsnprintf(fullnlfilename, SCIP_MAXSTRLEN, "%s", nlfilename); 234 else 235 (void) SCIPsnprintf(fullnlfilename, SCIP_MAXSTRLEN, "%s.nl", nlfilename); 236 SCIPinfoMessage(scip, NULL, "read problem <%s>\n", fullnlfilename); 237 SCIPinfoMessage(scip, NULL, "============\n\n"); 238 239 SCIP_CALL( SCIPreadProb(scip, fullnlfilename, "nl") ); 240 241 if( interactive ) 242 { 243 SCIP_CALL( SCIPstartInteraction(scip) ); 244 } 245 else 246 { 247 SCIPinfoMessage(scip, NULL, "\nsolve problem\n"); 248 SCIPinfoMessage(scip, NULL, "=============\n\n"); 249 250 SCIP_CALL( SCIPsolve(scip) ); 251 } 252 253 SCIP_CALL( SCIPgetBoolParam(scip, "display/statistics", &printstat) ); 254 if( printstat ) 255 { 256 SCIPinfoMessage(scip, NULL, "\nStatistics\n"); 257 SCIPinfoMessage(scip, NULL, "==========\n\n"); 258 259 SCIP_CALL( SCIPprintStatistics(scip, NULL) ); 260 } 261 262 SCIP_CALL( SCIPwriteSolutionNl(scip) ); 263 264 return SCIP_OKAY; 265 266 #else /* SCIP_WITH_AMPL */ 267 SCIPerrorMessage("SCIP has been compiled without AMPL support.\n"); 268 return SCIP_PLUGINNOTFOUND; 269 #endif 270 } 271 272 /** evaluates command line parameters and runs SCIP appropriately in the given SCIP instance */ 273 SCIP_RETCODE SCIPprocessShellArguments( 274 SCIP* scip, /**< SCIP data structure */ 275 int argc, /**< number of shell parameters */ 276 char** argv, /**< array with shell parameters */ 277 const char* defaultsetname /**< name of default settings file */ 278 ) 279 { /*lint --e{850}*/ 280 char* probname = NULL; 281 char* settingsname = NULL; 282 char* logname = NULL; 283 int randomseed; 284 SCIP_Bool randomseedread; 285 SCIP_Bool quiet; 286 SCIP_Bool paramerror; 287 SCIP_Bool interactive; 288 SCIP_Bool onlyversion; 289 SCIP_Real primalreference = SCIP_UNKNOWN; 290 SCIP_Real dualreference = SCIP_UNKNOWN; 291 const char* dualrefstring; 292 const char* primalrefstring; 293 int i; 294 295 /******************** 296 * Parse parameters * 297 ********************/ 298 299 /* recognize and handle case where we were called from AMPL first */ 300 if( argc >= 3 && strcmp(argv[2], "-AMPL") == 0 ) 301 { 302 /* check for optional argument -i after -AMPL */ 303 interactive = argc >= 4 && strcmp(argv[3], "-i") == 0; 304 305 SCIP_CALL( fromAmpl(scip, argv[1], interactive, defaultsetname) ); 306 307 return SCIP_OKAY; 308 } 309 310 quiet = FALSE; 311 paramerror = FALSE; 312 interactive = FALSE; 313 onlyversion = FALSE; 314 randomseedread = FALSE; 315 randomseed = 0; 316 primalrefstring = NULL; 317 dualrefstring = NULL; 318 319 for( i = 1; i < argc; ++i ) 320 { 321 if( strcmp(argv[i], "-l") == 0 ) 322 { 323 i++; 324 if( i < argc ) 325 logname = argv[i]; 326 else 327 { 328 printf("missing log filename after parameter '-l'\n"); 329 paramerror = TRUE; 330 } 331 } 332 else if( strcmp(argv[i], "-q") == 0 ) 333 quiet = TRUE; 334 else if( strcmp(argv[i], "-v") == 0 ) 335 onlyversion = TRUE; 336 else if( strcmp(argv[i], "--version") == 0 ) 337 onlyversion = TRUE; 338 else if( strcmp(argv[i], "-s") == 0 ) 339 { 340 i++; 341 if( i < argc ) 342 settingsname = argv[i]; 343 else 344 { 345 printf("missing settings filename after parameter '-s'\n"); 346 paramerror = TRUE; 347 } 348 } 349 else if( strcmp(argv[i], "-f") == 0 ) 350 { 351 i++; 352 if( i < argc ) 353 probname = argv[i]; 354 else 355 { 356 printf("missing problem filename after parameter '-f'\n"); 357 paramerror = TRUE; 358 } 359 } 360 else if( strcmp(argv[i], "-c") == 0 ) 361 { 362 i++; 363 if( i < argc ) 364 { 365 SCIP_CALL( SCIPaddDialogInputLine(scip, argv[i]) ); 366 interactive = TRUE; 367 } 368 else 369 { 370 printf("missing command line after parameter '-c'\n"); 371 paramerror = TRUE; 372 } 373 } 374 else if( strcmp(argv[i], "-b") == 0 ) 375 { 376 i++; 377 if( i < argc ) 378 { 379 SCIP_FILE* file; 380 381 file = SCIPfopen(argv[i], "r"); 382 if( file == NULL ) 383 { 384 printf("cannot read command batch file <%s>\n", argv[i]); 385 SCIPprintSysError(argv[i]); 386 paramerror = TRUE; 387 } 388 else 389 { 390 while( !SCIPfeof(file) ) 391 { 392 char buffer[SCIP_MAXSTRLEN]; 393 394 (void)SCIPfgets(buffer, (int) sizeof(buffer), file); 395 if( buffer[0] != '\0' ) 396 { 397 SCIP_CALL_FINALLY( SCIPaddDialogInputLine(scip, buffer), SCIPfclose(file) ); 398 } 399 } 400 SCIPfclose(file); 401 interactive = TRUE; 402 } 403 } 404 else 405 { 406 printf("missing command batch filename after parameter '-b'\n"); 407 paramerror = TRUE; 408 } 409 } 410 else if( strcmp(argv[i], "-r") == 0 ) 411 { 412 /*read a random seed from the command line */ 413 i++; 414 if( i < argc && isdigit(argv[i][0]) ) 415 { 416 randomseed = atoi(argv[i]); 417 randomseedread = TRUE; 418 } 419 else 420 { 421 printf("Random seed parameter '-r' followed by something that is not an integer\n"); 422 paramerror = TRUE; 423 } 424 } 425 else if( strcmp(argv[i], "-o") == 0 ) 426 { 427 if( i >= argc - 2 ) 428 { 429 printf("wrong usage of reference objective parameter '-o': -o <primref> <dualref>\n"); 430 paramerror = TRUE; 431 } 432 else 433 { 434 /* do not parse the strings directly, the settings could still influence the value of +-infinity */ 435 primalrefstring = argv[i + 1]; 436 dualrefstring = argv[i+2]; 437 } 438 i += 2; 439 } 440 else 441 { 442 printf("invalid parameter <%s>\n", argv[i]); 443 paramerror = TRUE; 444 } 445 } 446 447 if( interactive && probname != NULL ) 448 { 449 printf("cannot mix batch mode '-c' and '-b' with file mode '-f'\n"); 450 paramerror = TRUE; 451 } 452 453 if( !paramerror ) 454 { 455 /*********************************** 456 * create log file message handler * 457 ***********************************/ 458 459 if( quiet ) 460 { 461 SCIPsetMessagehdlrQuiet(scip, quiet); 462 } 463 464 if( logname != NULL ) 465 { 466 SCIPsetMessagehdlrLogfile(scip, logname); 467 } 468 469 /*********************************** 470 * Version and library information * 471 ***********************************/ 472 473 SCIPprintVersion(scip, NULL); 474 SCIPinfoMessage(scip, NULL, "\n"); 475 476 SCIPprintExternalCodes(scip, NULL); 477 SCIPinfoMessage(scip, NULL, "\n"); 478 479 if( onlyversion ) 480 { 481 SCIPprintBuildOptions(scip, NULL); 482 SCIPinfoMessage(scip, NULL, "\n"); 483 return SCIP_OKAY; 484 } 485 486 /***************** 487 * Load settings * 488 *****************/ 489 490 if( settingsname != NULL ) 491 { 492 SCIP_CALL( readParams(scip, settingsname) ); 493 } 494 else if( defaultsetname != NULL ) 495 { 496 SCIP_CALL( readParams(scip, defaultsetname) ); 497 } 498 499 /************************************ 500 * Change random seed, if specified * 501 ***********************************/ 502 if( randomseedread ) 503 { 504 SCIP_CALL( SCIPsetIntParam(scip, "randomization/randomseedshift", randomseed) ); 505 } 506 507 /************** 508 * Start SCIP * 509 **************/ 510 511 if( probname != NULL ) 512 { 513 SCIP_Bool validatesolve = FALSE; 514 515 if( primalrefstring != NULL && dualrefstring != NULL ) 516 { 517 char *endptr; 518 if( ! SCIPparseReal(scip, primalrefstring, &primalreference, &endptr) || 519 ! SCIPparseReal(scip, dualrefstring, &dualreference, &endptr) ) 520 { 521 printf("error parsing primal and dual reference values for validation: %s %s\n", primalrefstring, dualrefstring); 522 return SCIP_ERROR; 523 } 524 else 525 validatesolve = TRUE; 526 } 527 SCIP_CALL( fromCommandLine(scip, probname) ); 528 529 /* validate the solve */ 530 if( validatesolve ) 531 { 532 SCIP_CALL( SCIPvalidateSolve(scip, primalreference, dualreference, SCIPfeastol(scip), FALSE, NULL, NULL, NULL) ); 533 } 534 } 535 else 536 { 537 SCIPinfoMessage(scip, NULL, "\n"); 538 SCIP_CALL( SCIPstartInteraction(scip) ); 539 } 540 } 541 else 542 { 543 printf("\nsyntax: %s [-l <logfile>] [-q] [-s <settings>] [-r <randseed>] [-f <problem>] [-b <batchfile>] [-c \"command\"]\n" 544 " -v, --version : print version and build options\n" 545 " -l <logfile> : copy output into log file\n" 546 " -q : suppress screen messages\n" 547 " -s <settings> : load parameter settings (.set) file\n" 548 " -f <problem> : load and solve problem file\n" 549 " -o <primref> <dualref> : pass primal and dual objective reference values for validation at the end of the solve\n" 550 " -b <batchfile>: load and execute dialog command batch file (can be used multiple times)\n" 551 " -r <randseed> : nonnegative integer to be used as random seed. " 552 "Has priority over random seed specified through parameter settings (.set) file\n" 553 " -c \"command\" : execute single line of dialog commands (can be used multiple times)\n", 554 argv[0]); 555 #ifdef SCIP_WITH_AMPL 556 printf("\nas AMPL solver: %s <.nl-file without the .nl> -AMPL [-i]\n" 557 " -i : start interactive SCIP shell after .nl file has been read\n", 558 argv[0]); 559 #endif 560 printf("\n"); 561 } 562 563 return SCIP_OKAY; 564 } 565 566 /** creates a SCIP instance with default plugins, evaluates command line parameters, runs SCIP appropriately, 567 * and frees the SCIP instance 568 */ 569 SCIP_RETCODE SCIPrunShell( 570 int argc, /**< number of shell parameters */ 571 char** argv, /**< array with shell parameters */ 572 const char* defaultsetname /**< name of default settings file */ 573 ) 574 { 575 SCIP* scip = NULL; 576 577 /********* 578 * Setup * 579 *********/ 580 581 /* initialize SCIP */ 582 SCIP_CALL( SCIPcreate(&scip) ); 583 584 /* we explicitly enable the use of a debug solution for this main SCIP instance */ 585 SCIPenableDebugSol(scip); 586 587 /* include default SCIP plugins */ 588 SCIP_CALL( SCIPincludeDefaultPlugins(scip) ); 589 590 /********************************** 591 * Process command line arguments * 592 **********************************/ 593 594 SCIP_CALL( SCIPprocessShellArguments(scip, argc, argv, defaultsetname) ); 595 596 /******************** 597 * Deinitialization * 598 ********************/ 599 SCIP_CALL( SCIPfree(&scip) ); 600 601 BMScheckEmptyMemory(); 602 603 return SCIP_OKAY; 604 } 605