1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the program and library */
4 /* SCIP --- Solving Constraint Integer Programs */
5 /* */
6 /* Copyright (C) 2002-2021 Konrad-Zuse-Zentrum */
7 /* fuer Informationstechnik Berlin */
8 /* */
9 /* SCIP is distributed under the terms of the ZIB Academic License. */
10 /* */
11 /* You should have received a copy of the ZIB Academic License */
12 /* along with SCIP; see the file COPYING. If not visit scipopt.org. */
13 /* */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15
16 /**@file scipshell.c
17 * @ingroup OTHER_CFILES
18 * @brief SCIP command line interface
19 * @author Tobias Achterberg
20 */
21
22 /*--+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
23
24 #include <stdio.h>
25 #include <string.h>
26 #include <ctype.h>
27
28 #include "scip/scip.h"
29 #include "scip/scipdefplugins.h"
30 #include "scip/scipshell.h"
31 #include "scip/message_default.h"
32
33 /*
34 * Message Handler
35 */
36
37 static
readParams(SCIP * scip,const char * filename)38 SCIP_RETCODE readParams(
39 SCIP* scip, /**< SCIP data structure */
40 const char* filename /**< parameter file name */
41 )
42 {
43 if( SCIPfileExists(filename) )
44 {
45 SCIPinfoMessage(scip, NULL, "reading user parameter file <%s>\n", filename);
46 SCIP_CALL( SCIPreadParams(scip, filename) );
47 }
48 else
49 SCIPinfoMessage(scip, NULL, "user parameter file <%s> not found - using default parameters\n", filename);
50
51 return SCIP_OKAY;
52 }
53
54 static
fromCommandLine(SCIP * scip,const char * filename)55 SCIP_RETCODE fromCommandLine(
56 SCIP* scip, /**< SCIP data structure */
57 const char* filename /**< input file name */
58 )
59 {
60 SCIP_RETCODE retcode;
61 SCIP_Bool outputorigsol = FALSE;
62
63 /********************
64 * Problem Creation *
65 ********************/
66
67 /** @note The message handler should be only fed line by line such the message has the chance to add string in front
68 * of each message
69 */
70 SCIPinfoMessage(scip, NULL, "\n");
71 SCIPinfoMessage(scip, NULL, "read problem <%s>\n", filename);
72 SCIPinfoMessage(scip, NULL, "============\n");
73 SCIPinfoMessage(scip, NULL, "\n");
74
75 retcode = SCIPreadProb(scip, filename, NULL);
76
77 switch( retcode )
78 {
79 case SCIP_NOFILE:
80 SCIPinfoMessage(scip, NULL, "file <%s> not found\n", filename);
81 return SCIP_OKAY;
82 case SCIP_PLUGINNOTFOUND:
83 SCIPinfoMessage(scip, NULL, "no reader for input file <%s> available\n", filename);
84 return SCIP_OKAY;
85 case SCIP_READERROR:
86 SCIPinfoMessage(scip, NULL, "error reading file <%s>\n", filename);
87 return SCIP_OKAY;
88 default:
89 SCIP_CALL( retcode );
90 } /*lint !e788*/
91
92 /*******************
93 * Problem Solving *
94 *******************/
95
96 /* solve problem */
97 SCIPinfoMessage(scip, NULL, "\nsolve problem\n");
98 SCIPinfoMessage(scip, NULL, "=============\n\n");
99
100 SCIP_CALL( SCIPsolve(scip) );
101
102 /*******************
103 * Solution Output *
104 *******************/
105
106 SCIP_CALL( SCIPgetBoolParam(scip, "misc/outputorigsol", &outputorigsol) );
107 if ( outputorigsol )
108 {
109 SCIP_SOL* bestsol;
110
111 SCIPinfoMessage(scip, NULL, "\nprimal solution (original space):\n");
112 SCIPinfoMessage(scip, NULL, "=================================\n\n");
113
114 bestsol = SCIPgetBestSol(scip);
115 if ( bestsol == NULL )
116 SCIPinfoMessage(scip, NULL, "no solution available\n");
117 else
118 {
119 SCIP_SOL* origsol;
120
121 SCIP_CALL( SCIPcreateSolCopy(scip, &origsol, bestsol) );
122 SCIP_CALL( SCIPretransformSol(scip, origsol) );
123 SCIP_CALL( SCIPprintSol(scip, origsol, NULL, FALSE) );
124 SCIP_CALL( SCIPfreeSol(scip, &origsol) );
125 }
126 }
127 else
128 {
129 SCIPinfoMessage(scip, NULL, "\nprimal solution (transformed space):\n");
130 SCIPinfoMessage(scip, NULL, "====================================\n\n");
131
132 SCIP_CALL( SCIPprintBestSol(scip, NULL, FALSE) );
133 }
134
135 /**************
136 * Statistics *
137 **************/
138
139 SCIPinfoMessage(scip, NULL, "\nStatistics\n");
140 SCIPinfoMessage(scip, NULL, "==========\n\n");
141
142 SCIP_CALL( SCIPprintStatistics(scip, NULL) );
143
144 return SCIP_OKAY;
145 }
146
147 /** evaluates command line parameters and runs SCIP appropriately in the given SCIP instance */
SCIPprocessShellArguments(SCIP * scip,int argc,char ** argv,const char * defaultsetname)148 SCIP_RETCODE SCIPprocessShellArguments(
149 SCIP* scip, /**< SCIP data structure */
150 int argc, /**< number of shell parameters */
151 char** argv, /**< array with shell parameters */
152 const char* defaultsetname /**< name of default settings file */
153 )
154 { /*lint --e{850}*/
155 char* probname = NULL;
156 char* settingsname = NULL;
157 char* logname = NULL;
158 int randomseed;
159 SCIP_Bool randomseedread;
160 SCIP_Bool quiet;
161 SCIP_Bool paramerror;
162 SCIP_Bool interactive;
163 SCIP_Bool onlyversion;
164 SCIP_Real primalreference = SCIP_UNKNOWN;
165 SCIP_Real dualreference = SCIP_UNKNOWN;
166 const char* dualrefstring;
167 const char* primalrefstring;
168 int i;
169
170 /********************
171 * Parse parameters *
172 ********************/
173
174 quiet = FALSE;
175 paramerror = FALSE;
176 interactive = FALSE;
177 onlyversion = FALSE;
178 randomseedread = FALSE;
179 randomseed = 0;
180 primalrefstring = NULL;
181 dualrefstring = NULL;
182
183 for( i = 1; i < argc; ++i )
184 {
185 if( strcmp(argv[i], "-l") == 0 )
186 {
187 i++;
188 if( i < argc )
189 logname = argv[i];
190 else
191 {
192 printf("missing log filename after parameter '-l'\n");
193 paramerror = TRUE;
194 }
195 }
196 else if( strcmp(argv[i], "-q") == 0 )
197 quiet = TRUE;
198 else if( strcmp(argv[i], "-v") == 0 )
199 onlyversion = TRUE;
200 else if( strcmp(argv[i], "--version") == 0 )
201 onlyversion = TRUE;
202 else if( strcmp(argv[i], "-s") == 0 )
203 {
204 i++;
205 if( i < argc )
206 settingsname = argv[i];
207 else
208 {
209 printf("missing settings filename after parameter '-s'\n");
210 paramerror = TRUE;
211 }
212 }
213 else if( strcmp(argv[i], "-f") == 0 )
214 {
215 i++;
216 if( i < argc )
217 probname = argv[i];
218 else
219 {
220 printf("missing problem filename after parameter '-f'\n");
221 paramerror = TRUE;
222 }
223 }
224 else if( strcmp(argv[i], "-c") == 0 )
225 {
226 i++;
227 if( i < argc )
228 {
229 SCIP_CALL( SCIPaddDialogInputLine(scip, argv[i]) );
230 interactive = TRUE;
231 }
232 else
233 {
234 printf("missing command line after parameter '-c'\n");
235 paramerror = TRUE;
236 }
237 }
238 else if( strcmp(argv[i], "-b") == 0 )
239 {
240 i++;
241 if( i < argc )
242 {
243 SCIP_FILE* file;
244
245 file = SCIPfopen(argv[i], "r");
246 if( file == NULL )
247 {
248 printf("cannot read command batch file <%s>\n", argv[i]);
249 SCIPprintSysError(argv[i]);
250 paramerror = TRUE;
251 }
252 else
253 {
254 while( !SCIPfeof(file) )
255 {
256 char buffer[SCIP_MAXSTRLEN];
257
258 (void)SCIPfgets(buffer, (int) sizeof(buffer), file);
259 if( buffer[0] != '\0' )
260 {
261 SCIP_CALL_FINALLY( SCIPaddDialogInputLine(scip, buffer), SCIPfclose(file) );
262 }
263 }
264 SCIPfclose(file);
265 interactive = TRUE;
266 }
267 }
268 else
269 {
270 printf("missing command batch filename after parameter '-b'\n");
271 paramerror = TRUE;
272 }
273 }
274 else if( strcmp(argv[i], "-r") == 0 )
275 {
276 /*read a random seed from the command line */
277 i++;
278 if( i < argc && isdigit(argv[i][0]) )
279 {
280 randomseed = atoi(argv[i]);
281 randomseedread = TRUE;
282 }
283 else
284 {
285 printf("Random seed parameter '-r' followed by something that is not an integer\n");
286 paramerror = TRUE;
287 }
288 }
289 else if( strcmp(argv[i], "-o") == 0 )
290 {
291 if( i >= argc - 2 )
292 {
293 printf("wrong usage of reference objective parameter '-o': -o <primref> <dualref>\n");
294 paramerror = TRUE;
295 }
296 else
297 {
298 /* do not parse the strings directly, the settings could still influence the value of +-infinity */
299 primalrefstring = argv[i + 1];
300 dualrefstring = argv[i+2];
301 }
302 i += 2;
303 }
304 else
305 {
306 printf("invalid parameter <%s>\n", argv[i]);
307 paramerror = TRUE;
308 }
309 }
310
311 if( interactive && probname != NULL )
312 {
313 printf("cannot mix batch mode '-c' and '-b' with file mode '-f'\n");
314 paramerror = TRUE;
315 }
316
317 if( !paramerror )
318 {
319 /***********************************
320 * create log file message handler *
321 ***********************************/
322
323 if( quiet )
324 {
325 SCIPsetMessagehdlrQuiet(scip, quiet);
326 }
327
328 if( logname != NULL )
329 {
330 SCIPsetMessagehdlrLogfile(scip, logname);
331 }
332
333 /***********************************
334 * Version and library information *
335 ***********************************/
336
337 SCIPprintVersion(scip, NULL);
338 SCIPinfoMessage(scip, NULL, "\n");
339
340 SCIPprintExternalCodes(scip, NULL);
341 SCIPinfoMessage(scip, NULL, "\n");
342
343 if( onlyversion )
344 {
345 SCIPprintBuildOptions(scip, NULL);
346 SCIPinfoMessage(scip, NULL, "\n");
347 return SCIP_OKAY;
348 }
349
350 /*****************
351 * Load settings *
352 *****************/
353
354 if( settingsname != NULL )
355 {
356 SCIP_CALL( readParams(scip, settingsname) );
357 }
358 else if( defaultsetname != NULL )
359 {
360 SCIP_CALL( readParams(scip, defaultsetname) );
361 }
362
363 /************************************
364 * Change random seed, if specified *
365 ***********************************/
366 if( randomseedread )
367 {
368 SCIP_CALL( SCIPsetIntParam(scip, "randomization/randomseedshift", randomseed) );
369 }
370
371 /**************
372 * Start SCIP *
373 **************/
374
375 if( probname != NULL )
376 {
377 SCIP_Bool validatesolve = FALSE;
378
379 if( primalrefstring != NULL && dualrefstring != NULL )
380 {
381 char *endptr;
382 if( ! SCIPparseReal(scip, primalrefstring, &primalreference, &endptr) ||
383 ! SCIPparseReal(scip, dualrefstring, &dualreference, &endptr) )
384 {
385 printf("error parsing primal and dual reference values for validation: %s %s\n", primalrefstring, dualrefstring);
386 return SCIP_ERROR;
387 }
388 else
389 validatesolve = TRUE;
390 }
391 SCIP_CALL( fromCommandLine(scip, probname) );
392
393 /* validate the solve */
394 if( validatesolve )
395 {
396 SCIP_CALL( SCIPvalidateSolve(scip, primalreference, dualreference, SCIPfeastol(scip), FALSE, NULL, NULL, NULL) );
397 }
398 }
399 else
400 {
401 SCIPinfoMessage(scip, NULL, "\n");
402 SCIP_CALL( SCIPstartInteraction(scip) );
403 }
404 }
405 else
406 {
407 printf("\nsyntax: %s [-l <logfile>] [-q] [-s <settings>] [-r <randseed>] [-f <problem>] [-b <batchfile>] [-c \"command\"]\n"
408 " -v, --version : print version and build options\n"
409 " -l <logfile> : copy output into log file\n"
410 " -q : suppress screen messages\n"
411 " -s <settings> : load parameter settings (.set) file\n"
412 " -f <problem> : load and solve problem file\n"
413 " -o <primref> <dualref> : pass primal and dual objective reference values for validation at the end of the solve\n"
414 " -b <batchfile>: load and execute dialog command batch file (can be used multiple times)\n"
415 " -r <randseed> : nonnegative integer to be used as random seed. "
416 "Has priority over random seed specified through parameter settings (.set) file\n"
417 " -c \"command\" : execute single line of dialog commands (can be used multiple times)\n\n",
418 argv[0]);
419 }
420
421 return SCIP_OKAY;
422 }
423
424 /** creates a SCIP instance with default plugins, evaluates command line parameters, runs SCIP appropriately,
425 * and frees the SCIP instance
426 */
SCIPrunShell(int argc,char ** argv,const char * defaultsetname)427 SCIP_RETCODE SCIPrunShell(
428 int argc, /**< number of shell parameters */
429 char** argv, /**< array with shell parameters */
430 const char* defaultsetname /**< name of default settings file */
431 )
432 {
433 SCIP* scip = NULL;
434
435 /*********
436 * Setup *
437 *********/
438
439 /* initialize SCIP */
440 SCIP_CALL( SCIPcreate(&scip) );
441
442 /* we explicitly enable the use of a debug solution for this main SCIP instance */
443 SCIPenableDebugSol(scip);
444
445 /* include default SCIP plugins */
446 SCIP_CALL( SCIPincludeDefaultPlugins(scip) );
447
448 /**********************************
449 * Process command line arguments *
450 **********************************/
451 SCIP_CALL( SCIPprocessShellArguments(scip, argc, argv, defaultsetname) );
452
453 /********************
454 * Deinitialization *
455 ********************/
456 SCIP_CALL( SCIPfree(&scip) );
457
458 BMScheckEmptyMemory();
459
460 return SCIP_OKAY;
461 }
462