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   reader_cip.c
17  * @ingroup DEFPLUGINS_READER
18  * @brief  CIP file reader
19  * @author Stefan Heinz
20  * @author Marc Pfetsch
21  * @author Michael Winkler
22  *
23  */
24 
25 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
26 
27 #include "blockmemshell/memory.h"
28 #include <ctype.h>
29 #include "scip/cons_linear.h"
30 #include "scip/pub_fileio.h"
31 #include "scip/pub_message.h"
32 #include "scip/pub_misc.h"
33 #include "scip/pub_reader.h"
34 #include "scip/pub_var.h"
35 #include "scip/reader_cip.h"
36 #include "scip/scip_cons.h"
37 #include "scip/scip_mem.h"
38 #include "scip/scip_message.h"
39 #include "scip/scip_numerics.h"
40 #include "scip/scip_param.h"
41 #include "scip/scip_prob.h"
42 #include "scip/scip_reader.h"
43 #include "scip/scip_var.h"
44 #include <string.h>
45 
46 #if !defined(_WIN32) && !defined(_WIN64)
47 #include <strings.h> /*lint --e{766}*/ /* needed for strncasecmp() */
48 #endif
49 
50 #define READER_NAME             "cipreader"
51 #define READER_DESC             "file reader for CIP (Constraint Integer Program) format"
52 #define READER_EXTENSION        "cip"
53 
54 #define DEFAULT_CIP_WRITEFIXEDVARS  TRUE     /**< Should fixed and aggregated variables be written when writing? */
55 
56 
57 /** CIP reading data */
58 struct SCIP_ReaderData
59 {
60    SCIP_Bool             writefixedvars;     /**< Should fixed and aggregated variables be written when writing? */
61 };
62 
63 
64 /** Section of the in CIP files */
65 enum CipSection
66 {
67    CIP_START,            /**< start tag */
68    CIP_STATISTIC,        /**< statistics section */
69    CIP_OBJECTIVE,        /**< objective */
70    CIP_VARS,             /**< list of (free) variables */
71    CIP_FIXEDVARS,        /**< list of fixed variables */
72    CIP_CONSTRAINTS,      /**< constraints */
73    CIP_END               /**< end of file tag */
74 };
75 typedef enum CipSection CIPSECTION;          /**< Section of the in CIP files */
76 
77 
78 /*
79  * Data structures
80  */
81 
82 /** CIP reading data */
83 struct CipInput
84 {
85    SCIP_FILE*            file;               /**< input file */
86    char*                 strbuf;             /**< string buffer for input lines */
87    int                   len;                /**< length of strbuf */
88    int                   readingsize;        /**< size of block in which len is increased if necessary */
89    int                   linenumber;         /**< number of line in input file */
90    CIPSECTION            section;            /**< current section */
91    SCIP_Bool             haserror;           /**< some error occurred */
92    SCIP_Bool             endfile;            /**< we have reached the end of the file */
93 };
94 typedef struct CipInput CIPINPUT;            /**< CIP reading data */
95 
96 
97 /*
98  * Local methods for reading/parsing
99  */
100 
101 /** get next input line; this are all characters until the next semicolon */
102 static
getInputString(SCIP * scip,CIPINPUT * cipinput)103 SCIP_RETCODE getInputString(
104    SCIP*                 scip,               /**< SCIP data structure */
105    CIPINPUT*             cipinput            /**< CIP parsing data */
106    )
107 {
108    char* endline;
109    char* endcharacter;
110    char* windowsendlinechar;
111 
112    assert(cipinput != NULL);
113 
114    /* read next line */
115    cipinput->endfile = (SCIPfgets(cipinput->strbuf, cipinput->len, cipinput->file) == NULL);
116 
117    if( cipinput->endfile )
118    {
119       /* clear the line for safety reason */
120       BMSclearMemoryArray(cipinput->strbuf, cipinput->len);
121       return SCIP_OKAY;
122    }
123 
124    cipinput->linenumber++;
125    endline = strchr(cipinput->strbuf, '\n');
126 
127    endcharacter = strchr(cipinput->strbuf, ';');
128    while( endline == NULL || (endcharacter == NULL && cipinput->section == CIP_CONSTRAINTS && strncmp(cipinput->strbuf, "END", 3) != 0 ) )
129    {
130       int pos;
131 
132       /* we refill the buffer from the '\n' character */
133       if( endline == NULL )
134          pos = cipinput->len - 1;
135       else
136          pos = (int) (endline - cipinput->strbuf);
137 
138       /* don't erase the '\n' from all buffers for constraints */
139       if( endline != NULL && cipinput->section == CIP_CONSTRAINTS )
140          pos++;
141 
142       /* if necessary reallocate memory */
143       if( pos + cipinput->readingsize >= cipinput->len )
144       {
145          cipinput->len = SCIPcalcMemGrowSize(scip, pos + cipinput->readingsize);
146          SCIP_CALL( SCIPreallocBufferArray(scip, &(cipinput->strbuf), cipinput->len) );
147       }
148 
149       /* read next line */
150       cipinput->endfile = (SCIPfgets(&(cipinput->strbuf[pos]), cipinput->len - pos, cipinput->file) == NULL);
151 
152       if( cipinput->endfile )
153       {
154 	 /* clear the line for safety reason */
155 	 BMSclearMemoryArray(cipinput->strbuf, cipinput->len);
156          return SCIP_OKAY;
157       }
158 
159       cipinput->linenumber++;
160       endline = strrchr(cipinput->strbuf, '\n');
161       endcharacter = strchr(cipinput->strbuf, ';');
162    }
163    assert(endline != NULL);
164 
165    /*SCIPdebugMsg(scip, "read line: %s\n", cipinput->strbuf);*/
166 
167    /* check for windows "carriage return" endline character */
168    windowsendlinechar = strrchr(cipinput->strbuf, '\r');
169    if( windowsendlinechar != NULL && windowsendlinechar + 1 == endline )
170       --endline;
171    else
172       /* if the assert should not hold we found a windows "carriage return" which was not at the end of the line */
173       assert(windowsendlinechar == NULL);
174 
175    if( cipinput->section == CIP_CONSTRAINTS && endcharacter != NULL && endline - endcharacter != 1 )
176    {
177       SCIPerrorMessage("Constraint line has to end with ';\\n' (line: %d).\n", cipinput->linenumber);
178       cipinput->haserror = TRUE;
179       return SCIP_OKAY; /* return error at hightest level */
180    }
181 
182    *endline = '\0';
183 
184    return SCIP_OKAY;
185 }
186 
187 /** read the problem name out of the statistics */
188 static
getStart(SCIP * scip,CIPINPUT * cipinput)189 void getStart(
190    SCIP*                 scip,               /**< SCIP data structure */
191    CIPINPUT*             cipinput            /**< CIP parsing data */
192    )
193 {
194    char* buf;
195 
196    assert(scip != NULL);
197 
198    buf = cipinput->strbuf;
199 
200    if( strncmp(buf, "STATISTICS", 9) == 0 )
201    {
202       cipinput->section = CIP_STATISTIC;
203       return;
204    }
205 
206    if( strncmp(buf, "VARIABLES", 8) == 0 || strncmp(buf, "FIXED", 5) == 0 || strncmp(buf, "CONSTRAINTS", 11) == 0 || strncmp(buf, "OBJECTIVE", 9) == 0 )
207    {
208       SCIPerrorMessage("Syntax Error: File has to start with 'STATISTICS' section.\n");
209       cipinput->haserror = TRUE;
210    }
211 }
212 
213 
214 /** read the problem name out of the statistics */
215 static
getStatistics(SCIP * scip,CIPINPUT * cipinput)216 SCIP_RETCODE getStatistics(
217    SCIP*                 scip,               /**< SCIP data structure */
218    CIPINPUT*             cipinput            /**< CIP parsing data */
219    )
220 {
221    char* buf;
222 
223    buf = cipinput->strbuf;
224 
225    if( strncmp(buf, "OBJECTIVE", 9) == 0 )
226    {
227       cipinput->section = CIP_OBJECTIVE;
228       return SCIP_OKAY;
229    }
230 
231    SCIPdebugMsg(scip, "parse statistics\n");
232 
233    if( strncmp(buf, "  Problem name", 14) == 0 )
234    {
235       char* name;
236       char* s;
237 
238       name = strchr(buf, ':');
239 
240       if( name == NULL )
241       {
242          SCIPwarningMessage(scip, "did not find problem name (line: %d):\n%s\n", cipinput->linenumber, cipinput->strbuf);
243          return SCIP_OKAY;  /* no error, might work with empty problem name */
244       }
245 
246       /* skip ':' */
247       ++name;
248 
249       /* make sure that we terminate the string at comments ('#') or newline ('\r', '\n')*/
250       if( NULL != (s = strpbrk(name, "#\r\n")) )
251          *s = '\0';
252 
253       /* remove white space (tabs, ' ') in front of the name */
254       while( isspace((unsigned char)* name) )
255          ++name;
256 
257       /* set problem name */
258       SCIP_CALL( SCIPsetProbName(scip, name) );
259 
260       SCIPdebugMsg(scip, "problem name <%s>\n", name);
261    }
262 
263    return SCIP_OKAY;
264 }
265 
266 /** read objective sense, offset, and scale */
267 static
getObjective(SCIP * scip,CIPINPUT * cipinput,SCIP_Real * objscale,SCIP_Real * objoffset)268 SCIP_RETCODE getObjective(
269    SCIP*                 scip,               /**< SCIP data structure */
270    CIPINPUT*             cipinput,           /**< CIP parsing data */
271    SCIP_Real*            objscale,           /**< buffer where to multiply with objective scale */
272    SCIP_Real*            objoffset           /**< buffer where to add with objective offset */
273    )
274 {
275    char* buf;
276    char* name;
277 
278    assert(objscale != NULL);
279    assert(objoffset != NULL);
280 
281    buf = cipinput->strbuf;
282 
283    if( strncmp(buf, "VARIABLES", 8) == 0 )
284       cipinput->section = CIP_VARS;
285    else if( strncmp(buf, "FIXED", 5) == 0 )
286       cipinput->section = CIP_FIXEDVARS;
287    else if( strncmp(buf, "CONSTRAINTS", 11) == 0 )
288       cipinput->section = CIP_CONSTRAINTS;
289    else if( strncmp(buf, "END", 3) == 0 )
290       cipinput->section = CIP_END;
291 
292    if( cipinput->section != CIP_OBJECTIVE )
293       return SCIP_OKAY;
294 
295    SCIPdebugMsg(scip, "parse objective information\n");
296 
297    /* remove white space */
298    while ( isspace((unsigned char)* buf) )
299       ++buf;
300 
301    if( strncasecmp(buf, "Sense", 5) == 0 )
302    {
303       SCIP_OBJSENSE objsense;
304 
305       name = strchr(buf, ':');
306 
307       if( name == NULL )
308       {
309          SCIPwarningMessage(scip, "did not find objective sense (line: %d):\n%s\n", cipinput->linenumber, cipinput->strbuf);
310          return SCIP_OKAY; /* no error - might work with default */
311       }
312 
313       /* skip ':' */
314       ++name;
315 
316       /* remove white space in front of the name */
317       while( isspace((unsigned char)* name) )
318          ++name;
319 
320       if( strncasecmp(name, "minimize", 3) == 0 )
321          objsense = SCIP_OBJSENSE_MINIMIZE;
322       else if( strncasecmp(name, "maximize", 3) == 0 )
323          objsense = SCIP_OBJSENSE_MAXIMIZE;
324       else
325       {
326          SCIPwarningMessage(scip, "unknown objective sense '%s' (line: %d):\n%s\n", name, cipinput->linenumber, cipinput->strbuf);
327          return SCIP_OKAY; /* no error - might work with default */
328       }
329 
330       /* set problem name */
331       SCIP_CALL( SCIPsetObjsense(scip, objsense) );
332       SCIPdebugMsg(scip, "objective sense <%s>\n", objsense == SCIP_OBJSENSE_MINIMIZE ? "minimize" : "maximize");
333    }
334    else if( strncasecmp(buf, "Offset", 6) == 0 )
335    {
336       SCIP_Real off = 0;
337       char* endptr;
338 
339       name = strchr(buf, ':');
340 
341       if( name == NULL )
342       {
343          SCIPwarningMessage(scip, "did not find offset (line: %d)\n", cipinput->linenumber);
344          return SCIP_OKAY;
345       }
346 
347       /* skip ':' */
348       ++name;
349 
350       /* remove white space in front of the name */
351       while(isspace((unsigned char)*name))
352          ++name;
353 
354       if ( SCIPstrToRealValue(name, &off, &endptr) )
355       {
356          *objoffset += off;
357          SCIPdebugMsg(scip, "offset <%g> (total: %g)\n", off, *objoffset);
358       }
359       else
360       {
361          SCIPwarningMessage(scip, "could not parse offset (line: %d)\n%s\n", cipinput->linenumber, cipinput->strbuf);
362          return SCIP_OKAY;
363       }
364    }
365    else if( strncasecmp(buf, "Scale", 5) == 0 )
366    {
367       SCIP_Real scale = 1.0;
368       char* endptr;
369 
370       name = strchr(buf, ':');
371 
372       if( name == NULL )
373       {
374          SCIPwarningMessage(scip, "did not find scale (line: %d)\n", cipinput->linenumber);
375          return SCIP_OKAY;
376       }
377 
378       /* skip ':' */
379       ++name;
380 
381       /* remove white space in front of the name */
382       while(isspace((unsigned char)*name))
383          ++name;
384 
385       if ( SCIPstrToRealValue(name, &scale, &endptr) )
386       {
387          *objscale *= scale;
388          SCIPdebugMsg(scip, "objscale <%g> (total: %g)\n", scale, *objscale);
389       }
390       else
391       {
392          SCIPwarningMessage(scip, "could not parse objective scale (line: %d)\n%s\n", cipinput->linenumber, cipinput->strbuf);
393          return SCIP_OKAY;
394       }
395    }
396 
397    return SCIP_OKAY;
398 }
399 
400 /** read variable */
401 static
getVariable(SCIP * scip,CIPINPUT * cipinput,SCIP_Bool initial,SCIP_Bool removable,SCIP_Real objscale)402 SCIP_RETCODE getVariable(
403    SCIP*                 scip,               /**< SCIP data structure */
404    CIPINPUT*             cipinput,           /**< CIP parsing data */
405    SCIP_Bool             initial,            /**< should var's column be present in the initial root LP? */
406    SCIP_Bool             removable,          /**< is var's column removable from the LP (due to aging or cleanup)? */
407    SCIP_Real             objscale            /**< objective scale */
408    )
409 {
410    SCIP_Bool success;
411    SCIP_VAR* var;
412    char* buf;
413    char* endptr;
414 
415    buf = cipinput->strbuf;
416 
417    if( strncmp(buf, "FIXED", 5) == 0 )
418       cipinput->section = CIP_FIXEDVARS;
419    else if( strncmp(buf, "CONSTRAINTS", 4) == 0 )
420       cipinput->section = CIP_CONSTRAINTS;
421    else if( strncmp(buf, "END", 3) == 0 )
422       cipinput->section = CIP_END;
423 
424    if( cipinput->section != CIP_VARS )
425       return SCIP_OKAY;
426 
427    SCIPdebugMsg(scip, "parse variable\n");
428 
429    /* parse the variable */
430    SCIP_CALL( SCIPparseVar(scip, &var, buf, initial, removable, NULL, NULL, NULL, NULL, NULL, &endptr, &success) );
431 
432    if( !success )
433    {
434       SCIPerrorMessage("syntax error in variable information (line: %d):\n%s\n", cipinput->linenumber, cipinput->strbuf);
435       cipinput->haserror = TRUE;
436       return SCIP_OKAY;
437    }
438 
439    if( objscale != 1.0 )
440    {
441       SCIP_CALL( SCIPchgVarObj(scip, var, SCIPvarGetObj(var) * objscale) );
442    }
443 
444    SCIP_CALL( SCIPaddVar(scip, var) );
445 
446    SCIPdebug( SCIP_CALL( SCIPprintVar(scip, var, NULL) ) );
447 
448    SCIP_CALL( SCIPreleaseVar(scip, &var) );
449 
450    return SCIP_OKAY;
451 }
452 
453 /** read fixed variable */
454 static
getFixedVariable(SCIP * scip,CIPINPUT * cipinput)455 SCIP_RETCODE getFixedVariable(
456    SCIP*                 scip,               /**< SCIP data structure */
457    CIPINPUT*             cipinput            /**< CIP parsing data */
458    )
459 {
460    SCIP_Bool success;
461    SCIP_VAR* var;
462    char* buf;
463    char* endptr;
464    char name[SCIP_MAXSTRLEN];
465 
466    buf = cipinput->strbuf;
467 
468    if( strncmp(buf, "CONSTRAINTS", 11) == 0 )
469       cipinput->section = CIP_CONSTRAINTS;
470    else if( strncmp(buf, "END", 3) == 0 )
471       cipinput->section = CIP_END;
472 
473    if( cipinput->section != CIP_FIXEDVARS )
474       return SCIP_OKAY;
475 
476    SCIPdebugMsg(scip, "parse fixed variable\n");
477 
478    /* parse the variable */
479    SCIP_CALL( SCIPparseVar(scip, &var, buf, TRUE, FALSE, NULL, NULL, NULL, NULL, NULL, &endptr, &success) );
480 
481    if( !success )
482    {
483       SCIPerrorMessage("syntax error in variable information (line: %d):\n%s\n", cipinput->linenumber, cipinput->strbuf);
484       cipinput->haserror = TRUE;
485       return SCIP_OKAY;
486    }
487 
488    /* skip intermediate stuff */
489    buf = endptr;
490 
491    while ( *buf != '\0' && (*buf == ' ' || *buf == ',') )
492       ++buf;
493 
494    /* check whether variable is fixed */
495    if ( strncmp(buf, "fixed:", 6) == 0 )
496    {
497       SCIP_CALL( SCIPaddVar(scip, var) );
498       SCIPdebug( SCIP_CALL( SCIPprintVar(scip, var, NULL) ) );
499    }
500    else if ( strncmp(buf, "negated:", 8) == 0 )
501    {
502       SCIP_CONS* lincons;
503       SCIP_VAR* negvar;
504       SCIP_Real vals[2];
505       SCIP_VAR* vars[2];
506 
507       buf += 8;
508 
509       /* we can just parse the next variable (ignoring all other information in between) */
510       SCIP_CALL( SCIPparseVarName(scip, buf, &negvar, &endptr) );
511 
512       if ( negvar == NULL )
513       {
514          SCIPerrorMessage("could not parse negated variable (line: %d):\n%s\n", cipinput->linenumber, cipinput->strbuf);
515          cipinput->haserror = TRUE;
516          return SCIP_OKAY;
517       }
518       assert(SCIPvarIsBinary(var));
519       assert(SCIPvarIsBinary(negvar));
520 
521       SCIP_CALL( SCIPaddVar(scip, var) );
522 
523       SCIPdebugMsg(scip, "creating negated variable <%s> (of <%s>) ...\n", SCIPvarGetName(var), SCIPvarGetName(negvar) );
524       SCIPdebug( SCIP_CALL( SCIPprintVar(scip, var, NULL) ) );
525 
526       /* add linear constraint for negation */
527       (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "neg_%s", SCIPvarGetName(var) );
528       vars[0] = var;
529       vars[1] = negvar;
530       vals[0] = 1.0;
531       vals[1] = 1.0;
532       SCIPdebugMsg(scip, "coupling constraint:\n");
533       SCIP_CALL( SCIPcreateConsLinear(scip, &lincons, name, 2, vars, vals, 1.0, 1.0, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE) );
534       SCIPdebugPrintCons(scip, lincons, NULL);
535       SCIP_CALL( SCIPaddCons(scip, lincons) );
536       SCIP_CALL( SCIPreleaseCons(scip, &lincons) );
537    }
538    else if ( strncmp(buf, "aggregated:", 11) == 0 )
539    {
540       /* handle (multi-)aggregated variables */
541       SCIP_CONS* lincons;
542       SCIP_Real* vals;
543       SCIP_VAR** vars;
544       SCIP_Real rhs = 0.0;
545       const char* str;
546       int nvarssize = 20;
547       int requsize;
548       int nvars;
549 
550       buf += 11;
551 
552       SCIPdebugMsg(scip, "parsing aggregated variable <%s> ...\n", SCIPvarGetName(var));
553 
554       /* first parse constant */
555       if ( ! SCIPstrToRealValue(buf, &rhs, &endptr) )
556       {
557          SCIPerrorMessage("expected constant when aggregated variable information (line: %d):\n%s\n", cipinput->linenumber, buf);
558          cipinput->haserror = TRUE;
559          return SCIP_OKAY;
560       }
561 
562       /* check whether constant is 0.0 */
563       str = endptr;
564       while ( *str != '\0' && isspace(*str) )
565          ++str;
566       /* if next char is '<' we found a variable -> constant is 0 */
567       if ( *str != '<' )
568       {
569          SCIPdebugMsg(scip, "constant: %f\n", rhs);
570          buf = endptr;
571       }
572       else
573       {
574          /* otherwise keep buf */
575          rhs = 0.0;
576       }
577 
578       /* initialize buffers for storing the variables and values */
579       SCIP_CALL( SCIPallocBufferArray(scip, &vars, nvarssize) );
580       SCIP_CALL( SCIPallocBufferArray(scip, &vals, nvarssize) );
581 
582       vars[0] = var;
583       vals[0] = -1.0;
584       --nvarssize;
585 
586       /* parse linear sum to get variables and coefficients */
587       SCIP_CALL( SCIPparseVarsLinearsum(scip, buf, &(vars[1]), &(vals[1]), &nvars, nvarssize, &requsize, &endptr, &success) );
588       if ( success && requsize > nvarssize )
589       {
590          /* realloc buffers and try again */
591          nvarssize = requsize;
592          SCIP_CALL( SCIPreallocBufferArray(scip, &vars, nvarssize + 1) );
593          SCIP_CALL( SCIPreallocBufferArray(scip, &vals, nvarssize + 1) );
594 
595          SCIP_CALL( SCIPparseVarsLinearsum(scip, buf, &(vars[1]), &(vals[1]), &nvars, nvarssize, &requsize, &endptr, &success) );
596          assert( ! success || requsize <= nvarssize); /* if successful, then should have had enough space now */
597       }
598 
599       if( success )
600       {
601          /* add aggregated variable */
602          SCIP_CALL( SCIPaddVar(scip, var) );
603 
604          /* special handling of variables that seem to be slack variables of indicator constraints */
605          str = SCIPvarGetName(var);
606          if ( strncmp(str, "indslack", 8) == 0 )
607          {
608             (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "indlin");
609             (void) strncat(name, str+8, SCIP_MAXSTRLEN-7);
610          }
611          else if ( strncmp(str, "t_indslack", 10) == 0 )
612          {
613             (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "indlin");
614             (void) strncat(name, str+10, SCIP_MAXSTRLEN-7);
615          }
616          else
617             (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s", SCIPvarGetName(var) );
618 
619          /* add linear constraint for (multi-)aggregation */
620          SCIPdebugMsg(scip, "coupling constraint:\n");
621          SCIP_CALL( SCIPcreateConsLinear(scip, &lincons, name, nvars + 1, vars, vals, -rhs, -rhs, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE) );
622          SCIPdebugPrintCons(scip, lincons, NULL);
623          SCIP_CALL( SCIPaddCons(scip, lincons) );
624          SCIP_CALL( SCIPreleaseCons(scip, &lincons) );
625       }
626       else
627       {
628          SCIPwarningMessage(scip, "Could not read (multi-)aggregated variable <%s>: dependent variables unkown - consider changing the order (line: %d):\n%s\n",
629             SCIPvarGetName(var), cipinput->linenumber, buf);
630       }
631 
632       SCIPfreeBufferArray(scip, &vals);
633       SCIPfreeBufferArray(scip, &vars);
634    }
635    else
636    {
637       SCIPerrorMessage("unknown section when parsing variables (line: %d):\n%s\n", cipinput->linenumber, buf);
638       cipinput->haserror = TRUE;
639       return SCIP_OKAY;
640    }
641    SCIP_CALL( SCIPreleaseVar(scip, &var) );
642 
643    return SCIP_OKAY;
644 }
645 
646 /** read constraint */
647 static
getConstraint(SCIP * scip,CIPINPUT * cipinput,SCIP_Bool initial,SCIP_Bool dynamic,SCIP_Bool removable)648 SCIP_RETCODE getConstraint(
649    SCIP*                 scip,               /**< SCIP data structure */
650    CIPINPUT*             cipinput,           /**< CIP parsing data */
651    SCIP_Bool             initial,            /**< should the LP relaxation of constraint be in the initial LP?
652                                               *   Usually set to TRUE. Set to FALSE for 'lazy constraints'. */
653    SCIP_Bool             dynamic,            /**< Is constraint subject to aging?
654                                               *   Usually set to FALSE. Set to TRUE for own cuts which
655                                               *   are separated as constraints. */
656    SCIP_Bool             removable           /**< should the relaxation be removed from the LP due to aging or cleanup?
657                                               *   Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */
658    )
659 {
660    SCIP_CONS* cons;
661    char* buf;
662    char* copybuf;
663    SCIP_RETCODE retcode;
664    SCIP_Bool separate;
665    SCIP_Bool enforce;
666    SCIP_Bool check;
667    SCIP_Bool propagate;
668    SCIP_Bool local;
669    SCIP_Bool modifiable;
670    SCIP_Bool success;
671    int len;
672 
673    buf = cipinput->strbuf;
674 
675    if( strncmp(buf, "END", 3) == 0 )
676    {
677       cipinput->section = CIP_END;
678       return SCIP_OKAY;
679    }
680 
681    SCIPdebugMsg(scip, "parse constraints in line %d\n", cipinput->linenumber);
682 
683    separate = TRUE;
684    enforce = TRUE;
685    check = TRUE;
686    propagate = TRUE;
687    local = FALSE;
688    modifiable = FALSE;
689 
690    /* get length of line and check for correct ending of constraint line */
691    len = (int)strlen(buf);
692    if( len < 1 )
693    {
694       SCIPerrorMessage("syntax error: expected constraint in line %d.\n", cipinput->linenumber);
695       cipinput->haserror = TRUE;
696       return SCIP_OKAY;
697    }
698    if ( buf[len - 1] != ';' )
699    {
700       SCIPerrorMessage("syntax error: line has to end with ';' (line: %d)\n", cipinput->linenumber);
701       cipinput->haserror = TRUE;
702       return SCIP_OKAY;
703    }
704 
705    /* copy buffer for working purpose */
706    SCIP_CALL( SCIPduplicateBufferArray(scip, &copybuf, buf, len) );
707    copybuf[len - 1] = '\0';
708 
709    /* parse the constraint */
710    retcode = SCIPparseCons(scip, &cons, copybuf,
711       initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, FALSE, &success);
712 
713    /* free temporary buffer */
714    SCIPfreeBufferArray(scip, &copybuf);
715 
716    SCIP_CALL( retcode );
717 
718    if( !success )
719    {
720       SCIPerrorMessage("syntax error when reading constraint (line: %d):\n%s\n", cipinput->linenumber, cipinput->strbuf);
721       cipinput->haserror = TRUE;
722       return SCIP_OKAY;
723    }
724 
725    SCIP_CALL( SCIPaddCons(scip, cons) );
726    SCIPdebugPrintCons(scip, cons, NULL);
727    SCIP_CALL( SCIPreleaseCons(scip, &cons) );
728 
729    return SCIP_OKAY;
730 }
731 
732 /*
733  * Callback methods of reader
734  */
735 
736 /** copy method for reader plugins (called when SCIP copies plugins) */
737 static
SCIP_DECL_READERCOPY(readerCopyCip)738 SCIP_DECL_READERCOPY(readerCopyCip)
739 {  /*lint --e{715}*/
740    assert(scip != NULL);
741    assert(reader != NULL);
742    assert(strcmp(SCIPreaderGetName(reader), READER_NAME) == 0);
743 
744    /* call inclusion method of reader */
745    SCIP_CALL( SCIPincludeReaderCip(scip) );
746 
747    return SCIP_OKAY;
748 }
749 
750 /** destructor of reader to free user data (called when SCIP is exiting) */
751 static
SCIP_DECL_READERFREE(readerFreeCip)752 SCIP_DECL_READERFREE(readerFreeCip)
753 {
754    SCIP_READERDATA* readerdata;
755 
756    assert(strcmp(SCIPreaderGetName(reader), READER_NAME) == 0);
757    readerdata = SCIPreaderGetData(reader);
758    assert(readerdata != NULL);
759    SCIPfreeBlockMemory(scip, &readerdata);
760 
761    return SCIP_OKAY;
762 }
763 
764 
765 /** problem reading method of reader */
766 static
SCIP_DECL_READERREAD(readerReadCip)767 SCIP_DECL_READERREAD(readerReadCip)
768 {  /*lint --e{715}*/
769    CIPINPUT cipinput;
770    SCIP_Real objscale;
771    SCIP_Real objoffset;
772    SCIP_Bool initialconss;
773    SCIP_Bool dynamicconss;
774    SCIP_Bool dynamiccols;
775    SCIP_Bool dynamicrows;
776    SCIP_Bool initialvar;
777    SCIP_Bool removablevar;
778    SCIP_RETCODE retcode;
779 
780    if( NULL == (cipinput.file = SCIPfopen(filename, "r")) )
781    {
782       SCIPerrorMessage("cannot open file <%s> for reading\n", filename);
783       SCIPprintSysError(filename);
784       return SCIP_NOFILE;
785    }
786 
787    cipinput.len = 131071;
788    SCIP_CALL( SCIPallocBufferArray(scip, &(cipinput.strbuf), cipinput.len) );
789 
790    cipinput.linenumber = 0;
791    cipinput.section = CIP_START;
792    cipinput.haserror = FALSE;
793    cipinput.endfile = FALSE;
794    cipinput.readingsize = 65535;
795 
796    SCIP_CALL( SCIPcreateProb(scip, filename, NULL, NULL, NULL, NULL, NULL, NULL, NULL) );
797 
798    SCIP_CALL( SCIPgetBoolParam(scip, "reading/initialconss", &initialconss) );
799    SCIP_CALL( SCIPgetBoolParam(scip, "reading/dynamiccols", &dynamiccols) );
800    SCIP_CALL( SCIPgetBoolParam(scip, "reading/dynamicconss", &dynamicconss) );
801    SCIP_CALL( SCIPgetBoolParam(scip, "reading/dynamicrows", &dynamicrows) );
802 
803    initialvar = !dynamiccols;
804    removablevar = dynamiccols;
805 
806    objscale = 1.0;
807    objoffset = 0.0;
808 
809    while( cipinput.section != CIP_END && !cipinput.haserror )
810    {
811       /* get next input string */
812       SCIP_CALL( getInputString(scip, &cipinput) );
813 
814       if( cipinput.endfile )
815          break;
816 
817       switch( cipinput.section )
818       {
819       case CIP_START:
820          getStart(scip, &cipinput);
821          break;
822       case CIP_STATISTIC:
823          SCIP_CALL( getStatistics(scip, &cipinput) );
824          break;
825       case CIP_OBJECTIVE:
826          SCIP_CALL( getObjective(scip, &cipinput, &objscale, &objoffset) );
827          break;
828       case CIP_VARS:
829          retcode = getVariable(scip, &cipinput, initialvar, removablevar, objscale);
830 
831          if( retcode == SCIP_READERROR )
832          {
833             cipinput.haserror = TRUE;
834             goto TERMINATE;
835          }
836          SCIP_CALL(retcode);
837 
838          break;
839       case CIP_FIXEDVARS:
840          retcode = getFixedVariable(scip, &cipinput);
841 
842          if( retcode == SCIP_READERROR )
843          {
844             cipinput.haserror = TRUE;
845             goto TERMINATE;
846          }
847          SCIP_CALL(retcode);
848 
849          break;
850       case CIP_CONSTRAINTS:
851          retcode = getConstraint(scip, &cipinput, initialconss, dynamicconss, dynamicrows);
852 
853          if( retcode == SCIP_READERROR )
854          {
855             cipinput.haserror = TRUE;
856             goto TERMINATE;
857          }
858          SCIP_CALL(retcode);
859 
860          break;
861       default:
862          SCIPerrorMessage("invalid CIP state\n");
863          SCIPABORT();
864          return SCIP_INVALIDDATA;  /*lint !e527*/
865       } /*lint !e788*/
866    }
867 
868    if( !SCIPisZero(scip, objoffset) && !cipinput.haserror )
869    {
870       SCIP_VAR* objoffsetvar;
871 
872       objoffset *= objscale;
873       SCIP_CALL( SCIPcreateVar(scip, &objoffsetvar, "objoffset", objoffset, objoffset, 1.0, SCIP_VARTYPE_CONTINUOUS,
874          TRUE, TRUE, NULL, NULL, NULL, NULL, NULL) );
875       SCIP_CALL( SCIPaddVar(scip, objoffsetvar) );
876       SCIP_CALL( SCIPreleaseVar(scip, &objoffsetvar) );
877       SCIPdebugMsg(scip, "added variables <objoffset> for objective offset of <%g>\n", objoffset);
878    }
879 
880    if( cipinput.section != CIP_END && !cipinput.haserror )
881    {
882       SCIPerrorMessage("unexpected EOF\n");
883    }
884 
885  TERMINATE:
886    /* close file stream */
887    SCIPfclose(cipinput.file);
888 
889    SCIPfreeBufferArray(scip, &cipinput.strbuf);
890 
891    if( cipinput.haserror )
892       return SCIP_READERROR;
893 
894    /* successfully parsed cip format */
895    *result = SCIP_SUCCESS;
896    return SCIP_OKAY;
897 }
898 
899 /** hash key retrieval function for variables */
900 static
SCIP_DECL_HASHGETKEY(hashGetKeyVar)901 SCIP_DECL_HASHGETKEY(hashGetKeyVar)
902 {  /*lint --e{715}*/
903    return elem;
904 }
905 
906 /** returns TRUE iff the indices of both variables are equal */
907 static
SCIP_DECL_HASHKEYEQ(hashKeyEqVar)908 SCIP_DECL_HASHKEYEQ(hashKeyEqVar)
909 {  /*lint --e{715}*/
910    if( key1 == key2 )
911       return TRUE;
912    return FALSE;
913 }
914 
915 /** returns the hash value of the key */
916 static
SCIP_DECL_HASHKEYVAL(hashKeyValVar)917 SCIP_DECL_HASHKEYVAL(hashKeyValVar)
918 {  /*lint --e{715}*/
919    assert( SCIPvarGetIndex((SCIP_VAR*) key) >= 0 );
920    return (unsigned int) SCIPvarGetIndex((SCIP_VAR*) key);
921 }
922 
923 /** problem writing method of reader */
924 static
SCIP_DECL_READERWRITE(readerWriteCip)925 SCIP_DECL_READERWRITE(readerWriteCip)
926 {  /*lint --e{715}*/
927    SCIP_HASHTABLE* varhash = NULL;
928    SCIP_READERDATA* readerdata;
929    int i;
930 
931    assert(reader != NULL);
932    assert(strcmp(SCIPreaderGetName(reader), READER_NAME) == 0);
933 
934    SCIPinfoMessage(scip, file, "STATISTICS\n");
935    SCIPinfoMessage(scip, file, "  Problem name     : %s\n", name);
936    SCIPinfoMessage(scip, file, "  Variables        : %d (%d binary, %d integer, %d implicit integer, %d continuous)\n",
937       nvars, nbinvars, nintvars, nimplvars, ncontvars);
938    SCIPinfoMessage(scip, file, "  Constraints      : %d initial, %d maximal\n", startnconss, maxnconss);
939 
940    SCIPinfoMessage(scip, file, "OBJECTIVE\n");
941    SCIPinfoMessage(scip, file, "  Sense            : %s\n", objsense == SCIP_OBJSENSE_MINIMIZE ? "minimize" : "maximize");
942    if( !SCIPisZero(scip, objoffset) )
943       SCIPinfoMessage(scip, file, "  Offset           : %+.15g\n", objoffset);
944    if( !SCIPisEQ(scip, objscale, 1.0) )
945       SCIPinfoMessage(scip, file, "  Scale            : %.15g\n", objscale);
946 
947    if ( nfixedvars > 0 )
948    {
949       /* set up hash table for variables that have been written property (used for writing out fixed vars in the right order) */
950       SCIP_CALL( SCIPhashtableCreate(&varhash, SCIPblkmem(scip), nvars + nfixedvars, hashGetKeyVar, hashKeyEqVar, hashKeyValVar, NULL) );
951    }
952 
953    if ( nvars + nfixedvars > 0 )
954    {
955       SCIPinfoMessage(scip, file, "VARIABLES\n");
956    }
957 
958    if( nvars > 0 )
959    {
960       for( i = 0; i < nvars; ++i )
961       {
962          SCIP_VAR* var;
963 
964          var = vars[i];
965          assert( var != NULL );
966          SCIP_CALL( SCIPprintVar(scip, var, file) );
967          if ( varhash != NULL )
968          {
969             /* add free variable to hashtable */
970             if ( ! SCIPhashtableExists(varhash, (void*) var) )
971             {
972                SCIP_CALL( SCIPhashtableInsert(varhash, (void*) var) );
973             }
974          }
975       }
976    }
977 
978    readerdata = SCIPreaderGetData(reader);
979    assert(readerdata != NULL);
980 
981    if( readerdata->writefixedvars && nfixedvars > 0 )
982    {
983       int nwritten = 0;
984 
985       SCIPinfoMessage(scip, file, "FIXED\n");
986 
987       /* loop through variables until each has been written after the variables that it depends on have been written; this
988        * requires several runs over the variables, but the depth (= number of loops) is usually small. */
989       while ( nwritten < nfixedvars )
990       {
991          SCIPdebugMsg(scip, "written %d of %d fixed variables.\n", nwritten, nfixedvars);
992          for (i = 0; i < nfixedvars; ++i)
993          {
994             SCIP_VAR* var;
995             SCIP_VAR* tmpvar;
996 
997             var = fixedvars[i];
998             assert( var != NULL );
999 
1000             /* skip variables already written */
1001             if ( SCIPhashtableExists(varhash, (void*) var) )
1002                continue;
1003 
1004             switch ( SCIPvarGetStatus(var) )
1005             {
1006             case SCIP_VARSTATUS_FIXED:
1007 
1008                /* fixed variables can simply be output and added to the hashtable */
1009                SCIP_CALL( SCIPprintVar(scip, var, file) );
1010                assert( ! SCIPhashtableExists(varhash, (void*) var) );
1011                SCIP_CALL( SCIPhashtableInsert(varhash, (void*) var) );
1012                ++nwritten;
1013 
1014                break;
1015 
1016             case SCIP_VARSTATUS_NEGATED:
1017 
1018                tmpvar = SCIPvarGetNegationVar(var);
1019                assert( tmpvar != NULL );
1020                assert( var == SCIPvarGetNegatedVar(tmpvar) );
1021 
1022                /* if the negated variable has been written, we can write the current variable */
1023                if ( SCIPhashtableExists(varhash, (void*) tmpvar) )
1024                {
1025                   SCIP_CALL( SCIPprintVar(scip, var, file) );
1026                   assert( ! SCIPhashtableExists(varhash, (void*) var) );
1027                   SCIP_CALL( SCIPhashtableInsert(varhash, (void*) var) );
1028                   ++nwritten;
1029                }
1030                break;
1031 
1032             case SCIP_VARSTATUS_AGGREGATED:
1033 
1034                tmpvar = SCIPvarGetAggrVar(var);
1035                assert( tmpvar != NULL );
1036 
1037                /* if the aggregating variable has been written, we can write the current variable */
1038                if ( SCIPhashtableExists(varhash, (void*) tmpvar) )
1039                {
1040                   SCIP_CALL( SCIPprintVar(scip, var, file) );
1041                   assert( ! SCIPhashtableExists(varhash, (void*) var) );
1042                   SCIP_CALL( SCIPhashtableInsert(varhash, (void*) var) );
1043                   ++nwritten;
1044                }
1045                break;
1046 
1047             case SCIP_VARSTATUS_MULTAGGR:
1048             {
1049                SCIP_VAR** aggrvars;
1050                int naggrvars;
1051                int j;
1052 
1053                /* get the active representation */
1054                SCIP_CALL( SCIPflattenVarAggregationGraph(scip, var) );
1055 
1056                naggrvars = SCIPvarGetMultaggrNVars(var);
1057                aggrvars = SCIPvarGetMultaggrVars(var);
1058                assert(aggrvars != NULL || naggrvars == 0);
1059 
1060                for (j = 0; j < naggrvars; ++j)
1061                {
1062                   if( !SCIPhashtableExists(varhash, (void*) aggrvars[j]) ) /*lint !e613*/
1063                      break;
1064                }
1065 
1066                /* if all multi-aggregating variables have been written, we can write the current variable */
1067                if ( j >= naggrvars )
1068                {
1069                   SCIP_CALL( SCIPprintVar(scip, var, file) );
1070                   assert( ! SCIPhashtableExists(varhash, (void*) var) );
1071                   SCIP_CALL( SCIPhashtableInsert(varhash, (void*) var) );
1072                   ++nwritten;
1073                }
1074                break;
1075             }
1076 
1077             case SCIP_VARSTATUS_ORIGINAL:
1078             case SCIP_VARSTATUS_LOOSE:
1079             case SCIP_VARSTATUS_COLUMN:
1080                SCIPerrorMessage("Only fixed variables are allowed to be present in fixedvars list.\n");
1081                SCIPABORT();
1082                return SCIP_ERROR; /*lint !e527*/
1083             }
1084          }
1085       }
1086    }
1087 
1088    if( nconss > 0 )
1089    {
1090       SCIPinfoMessage(scip, file, "CONSTRAINTS\n");
1091 
1092       for( i = 0; i < nconss; ++i )
1093       {
1094          SCIP_CALL( SCIPprintCons(scip, conss[i], file) );
1095          SCIPinfoMessage(scip, file, ";\n");
1096       }
1097    }
1098    SCIPinfoMessage(scip, file, "END\n");
1099 
1100    *result = SCIP_SUCCESS;
1101 
1102    if( nfixedvars > 0 )
1103       SCIPhashtableFree(&varhash);
1104    else
1105       assert(varhash == NULL);
1106 
1107    return SCIP_OKAY;
1108 }
1109 
1110 
1111 /*
1112  * reader specific interface methods
1113  */
1114 
1115 /** includes the cip file reader in SCIP */
SCIPincludeReaderCip(SCIP * scip)1116 SCIP_RETCODE SCIPincludeReaderCip(
1117    SCIP*                 scip                /**< SCIP data structure */
1118    )
1119 {
1120    SCIP_READERDATA* readerdata;
1121    SCIP_READER* reader;
1122 
1123    /* create cip reader data */
1124    SCIP_CALL( SCIPallocBlockMemory(scip, &readerdata) );
1125 
1126    /* include reader */
1127    SCIP_CALL( SCIPincludeReaderBasic(scip, &reader, READER_NAME, READER_DESC, READER_EXTENSION, readerdata) );
1128 
1129    /* set non fundamental callbacks via setter functions */
1130    SCIP_CALL( SCIPsetReaderCopy(scip, reader, readerCopyCip) );
1131    SCIP_CALL( SCIPsetReaderFree(scip, reader, readerFreeCip) );
1132    SCIP_CALL( SCIPsetReaderRead(scip, reader, readerReadCip) );
1133    SCIP_CALL( SCIPsetReaderWrite(scip, reader, readerWriteCip) );
1134 
1135    /* add cip reader parameters */
1136    SCIP_CALL( SCIPaddBoolParam(scip,
1137          "reading/cipreader/writefixedvars", "should fixed and aggregated variables be printed (if not, re-parsing might fail)",
1138          &readerdata->writefixedvars, FALSE, DEFAULT_CIP_WRITEFIXEDVARS, NULL, NULL) );
1139 
1140    return SCIP_OKAY;
1141 }
1142