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