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.c
17  * @ingroup OTHER_CFILES
18  * @brief  interface for input file readers
19  * @author Tobias Achterberg
20  */
21 
22 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
23 
24 #include <assert.h>
25 #include <string.h>
26 #if defined(_WIN32) || defined(_WIN64)
27 #else
28 #include <strings.h> /*lint --e{766}*/
29 #endif
30 #include <math.h>
31 
32 #include "scip/def.h"
33 #include "blockmemshell/memory.h"
34 #include "scip/set.h"
35 #include "scip/clock.h"
36 #include "scip/pub_misc.h"
37 #include "scip/reader.h"
38 #include "scip/prob.h"
39 #include "scip/pub_var.h"
40 #include "scip/var.h"
41 #include "scip/pub_cons.h"
42 #include "scip/cons.h"
43 #include "scip/pub_message.h"
44 
45 #include "scip/struct_reader.h"
46 
47 
48 /** copies the given reader to a new scip */
SCIPreaderCopyInclude(SCIP_READER * reader,SCIP_SET * set)49 SCIP_RETCODE SCIPreaderCopyInclude(
50    SCIP_READER*          reader,             /**< reader */
51    SCIP_SET*             set                 /**< SCIP_SET of SCIP to copy to */
52    )
53 {
54    assert(reader != NULL);
55    assert(set != NULL);
56    assert(set->scip != NULL);
57 
58    if( reader->readercopy != NULL )
59    {
60       SCIPsetDebugMsg(set, "including reader %s in subscip %p\n", SCIPreaderGetName(reader), (void*)set->scip);
61       SCIP_CALL( reader->readercopy(set->scip, reader) );
62    }
63    return SCIP_OKAY;
64 }
65 
66 /** internal method to create a reader */
67 static
doReaderCreate(SCIP_READER ** reader,const char * name,const char * desc,const char * extension,SCIP_DECL_READERCOPY ((* readercopy)),SCIP_DECL_READERFREE ((* readerfree)),SCIP_DECL_READERREAD ((* readerread)),SCIP_DECL_READERWRITE ((* readerwrite)),SCIP_READERDATA * readerdata)68 SCIP_RETCODE doReaderCreate(
69    SCIP_READER**         reader,             /**< pointer to store reader */
70    const char*           name,               /**< name of reader */
71    const char*           desc,               /**< description of reader */
72    const char*           extension,          /**< file extension that reader processes */
73    SCIP_DECL_READERCOPY  ((*readercopy)),    /**< copy method of reader or NULL if you don't want to copy your plugin into sub-SCIPs */
74    SCIP_DECL_READERFREE  ((*readerfree)),    /**< destructor of reader */
75    SCIP_DECL_READERREAD  ((*readerread)),    /**< read method */
76    SCIP_DECL_READERWRITE ((*readerwrite)),   /**< write method */
77    SCIP_READERDATA*      readerdata          /**< reader data */
78    )
79 {
80    assert(reader != NULL);
81    assert(name != NULL);
82    assert(desc != NULL);
83    assert(extension != NULL);
84 
85    SCIP_ALLOC( BMSallocMemory(reader) );
86    BMSclearMemory(*reader);
87 
88    SCIP_ALLOC( BMSduplicateMemoryArray(&(*reader)->name, name, strlen(name)+1) );
89    SCIP_ALLOC( BMSduplicateMemoryArray(&(*reader)->desc, desc, strlen(desc)+1) );
90    SCIP_ALLOC( BMSduplicateMemoryArray(&(*reader)->extension, extension, strlen(extension)+1) );
91    (*reader)->readercopy = readercopy;
92    (*reader)->readerfree = readerfree;
93    (*reader)->readerread = readerread;
94    (*reader)->readerwrite = readerwrite;
95    (*reader)->readerdata = readerdata;
96 
97    /* create reading clock */
98    SCIP_CALL( SCIPclockCreate(&(*reader)->readingtime, SCIP_CLOCKTYPE_DEFAULT) );
99 
100    return SCIP_OKAY;
101 }
102 
103 /** creates a reader */
SCIPreaderCreate(SCIP_READER ** reader,SCIP_SET * set,const char * name,const char * desc,const char * extension,SCIP_DECL_READERCOPY ((* readercopy)),SCIP_DECL_READERFREE ((* readerfree)),SCIP_DECL_READERREAD ((* readerread)),SCIP_DECL_READERWRITE ((* readerwrite)),SCIP_READERDATA * readerdata)104 SCIP_RETCODE SCIPreaderCreate(
105    SCIP_READER**         reader,             /**< pointer to store reader */
106    SCIP_SET*             set,                /**< global SCIP settings */
107    const char*           name,               /**< name of reader */
108    const char*           desc,               /**< description of reader */
109    const char*           extension,          /**< file extension that reader processes */
110    SCIP_DECL_READERCOPY  ((*readercopy)),    /**< copy method of reader or NULL if you don't want to copy your plugin into sub-SCIPs */
111    SCIP_DECL_READERFREE  ((*readerfree)),    /**< destructor of reader */
112    SCIP_DECL_READERREAD  ((*readerread)),    /**< read method */
113    SCIP_DECL_READERWRITE ((*readerwrite)),   /**< write method */
114    SCIP_READERDATA*      readerdata          /**< reader data */
115    )
116 {
117    assert(reader != NULL);
118    assert(set != NULL);
119    assert(name != NULL);
120    assert(desc != NULL);
121    assert(extension != NULL);
122 
123    SCIP_CALL_FINALLY( doReaderCreate(reader, name, desc, extension, readercopy, readerfree, readerread, readerwrite,
124       readerdata), (void) SCIPreaderFree(reader, set) );
125 
126    return SCIP_OKAY;
127 }
128 
129 /** frees memory of reader */
SCIPreaderFree(SCIP_READER ** reader,SCIP_SET * set)130 SCIP_RETCODE SCIPreaderFree(
131    SCIP_READER**         reader,             /**< pointer to reader data structure */
132    SCIP_SET*             set                 /**< global SCIP settings */
133    )
134 {
135    assert(reader != NULL);
136    assert(set != NULL);
137 
138    if( *reader == NULL )
139       return SCIP_OKAY;
140 
141    /* call destructor of reader */
142    if( (*reader)->readerfree != NULL )
143    {
144       SCIP_CALL( (*reader)->readerfree(set->scip, *reader) );
145    }
146 
147    BMSfreeMemoryArrayNull(&(*reader)->name);
148    BMSfreeMemoryArrayNull(&(*reader)->desc);
149    BMSfreeMemoryArrayNull(&(*reader)->extension);
150 
151    /* free clock */
152    SCIPclockFree(&(*reader)->readingtime);
153 
154    BMSfreeMemory(reader);
155 
156    return SCIP_OKAY;
157 }
158 
159 /** returns TRUE, if reader is responsible for files with the given extension */
160 static
readerIsApplicable(SCIP_READER * reader,const char * extension)161 SCIP_Bool readerIsApplicable(
162    SCIP_READER*          reader,             /**< reader */
163    const char*           extension           /**< extension of the input file name */
164    )
165 {
166    assert(reader != NULL);
167    assert(reader->extension != NULL);
168 
169    return (extension != NULL && strcasecmp(reader->extension, extension) == 0)
170       || (extension == NULL && *(reader->extension) == '\0');
171 }
172 
173 /** reads problem data from file with given reader or returns SCIP_DIDNOTRUN */
SCIPreaderRead(SCIP_READER * reader,SCIP_SET * set,const char * filename,const char * extension,SCIP_RESULT * result)174 SCIP_RETCODE SCIPreaderRead(
175    SCIP_READER*          reader,             /**< reader */
176    SCIP_SET*             set,                /**< global SCIP settings */
177    const char*           filename,           /**< name of the input file */
178    const char*           extension,          /**< extension of the input file name */
179    SCIP_RESULT*          result              /**< pointer to store the result of the callback method */
180    )
181 {
182    SCIP_RETCODE retcode;
183 
184    assert(reader != NULL);
185    assert(set != NULL);
186    assert(filename != NULL);
187    assert(result != NULL);
188 
189    /* check, if reader is applicable on the given file */
190    if( readerIsApplicable(reader, extension) && reader->readerread != NULL )
191    {
192       SCIP_CLOCK* readingtime;
193 
194       /**@note we need temporary clock to measure the reading time correctly since in case of creating a new problem
195        *       within the reader all clocks are reset (including the reader clocks); this resetting is necessary for
196        *       example for those case we people solve several problems using the (same) interactive shell
197        */
198 
199       assert(!SCIPclockIsRunning(reader->readingtime));
200 
201       /* create a temporary clock for measuring the reading time */
202       SCIP_CALL( SCIPclockCreate(&readingtime, SCIP_CLOCKTYPE_DEFAULT) );
203 
204       /* start timing */
205       SCIPclockStart(readingtime, set);
206 
207       /* call reader to read problem */
208       retcode = reader->readerread(set->scip, reader, filename, result);
209 
210       /* stop timing */
211       SCIPclockStop(readingtime, set);
212 
213       /* add time to reader reading clock */
214       SCIPclockSetTime(reader->readingtime, SCIPclockGetTime(reader->readingtime) + SCIPclockGetTime(readingtime));
215 
216       /* free the temporary clock */
217       SCIPclockFree(&readingtime);
218    }
219    else
220    {
221       *result = SCIP_DIDNOTRUN;
222       retcode = SCIP_OKAY;
223    }
224 
225    /* check for reader errors */
226    if( retcode == SCIP_NOFILE || retcode == SCIP_READERROR )
227       return retcode;
228 
229    /* check if the result code is valid in case no reader error occurred */
230    assert( *result == SCIP_DIDNOTRUN || *result == SCIP_SUCCESS );
231 
232    SCIP_CALL( retcode );
233 
234    return SCIP_OKAY;
235 }
236 
237 
238 /* reset the variable name to the given one */
239 static
resetVarname(SCIP_VAR * var,SCIP_SET * set,const char * name)240 void resetVarname(
241    SCIP_VAR*             var,                /**< variable */
242    SCIP_SET*             set,                /**< global SCIP settings */
243    const char*           name                /**< variable name */
244    )
245 {
246    const char * oldname;
247 
248    assert( var != NULL );
249    assert( name != NULL );
250 
251    /* get pointer to temporary generic name and free the memory */
252    oldname = SCIPvarGetName(var);
253    SCIPsetFreeBufferArray(set, &oldname);
254 
255    /* reset name */
256    SCIPvarSetNamePointer(var, name);
257 }
258 
259 
260 /** writes problem data to file with given reader or returns SCIP_DIDNOTRUN */
SCIPreaderWrite(SCIP_READER * reader,SCIP_PROB * prob,SCIP_SET * set,FILE * file,const char * extension,SCIP_Bool genericnames,SCIP_RESULT * result)261 SCIP_RETCODE SCIPreaderWrite(
262    SCIP_READER*          reader,             /**< reader */
263    SCIP_PROB*            prob,               /**< problem data */
264    SCIP_SET*             set,                /**< global SCIP settings */
265    FILE*                 file,               /**< output file (or NULL for standard output) */
266    const char*           extension,          /**< file format */
267    SCIP_Bool             genericnames,       /**< using generic variable and constraint names? */
268    SCIP_RESULT*          result              /**< pointer to store the result of the callback method */
269    )
270 {
271    SCIP_RETCODE retcode;
272 
273    assert(reader != NULL);
274    assert(set != NULL);
275    assert(set->buffer != NULL);
276    assert(extension != NULL);
277    assert(result != NULL);
278 
279    /* check, if reader is applicable on the given file */
280    if( readerIsApplicable(reader, extension) && reader->readerwrite != NULL )
281    {
282       const char* consname;
283       const char** varnames = NULL;
284       const char** fixedvarnames = NULL;
285       const char** consnames = NULL;
286       SCIP_VAR** vars;
287       SCIP_VAR** fixedvars;
288       SCIP_CONS** conss;
289       SCIP_CONS* cons;
290       SCIP_Real objscale;
291       char* name;
292       int nfixedvars;
293       int nconss;
294       int nvars;
295       int i;
296 
297       vars = prob->vars;
298       nvars = prob->nvars;
299       fixedvars = prob->fixedvars;
300       nfixedvars = prob->nfixedvars;
301 
302       /* case of the transformed problem, we want to write currently valid problem */
303       if( prob->transformed )
304       {
305          SCIP_CONSHDLR** conshdlrs;
306          int nconshdlrs;
307 
308          conshdlrs = set->conshdlrs;
309          nconshdlrs = set->nconshdlrs;
310 
311          /* collect number of constraints which have to be enforced; these are the constraints which currency (locally)
312           * enabled; these also includes the local constraints
313           */
314          nconss = 0;
315          for( i = 0; i < nconshdlrs; ++i )
316          {
317             /* check if all constraints of the constraint handler should be written */
318             if( set->write_allconss )
319                nconss += SCIPconshdlrGetNConss(conshdlrs[i]);
320             else
321                nconss += SCIPconshdlrGetNEnfoConss(conshdlrs[i]);
322          }
323 
324          SCIPsetDebugMsg(set, "Writing %d constraints.\n", nconss);
325 
326          SCIP_CALL( SCIPsetAllocBufferArray(set, &conss, nconss) );
327 
328          /* copy the constraints */
329          nconss = 0;
330          for( i = 0; i < nconshdlrs; ++i )
331          {
332             SCIP_CONS** conshdlrconss;
333             int nconshdlrconss;
334             int c;
335 
336             /* check if all constraints of the constraint handler should be written */
337             if( set->write_allconss )
338             {
339                conshdlrconss = SCIPconshdlrGetConss(conshdlrs[i]);
340                nconshdlrconss = SCIPconshdlrGetNConss(conshdlrs[i]);
341             }
342             else
343             {
344                conshdlrconss = SCIPconshdlrGetEnfoConss(conshdlrs[i]);
345                nconshdlrconss = SCIPconshdlrGetNEnfoConss(conshdlrs[i]);
346             }
347 
348             SCIPsetDebugMsg(set, "Conshdlr <%s> has %d constraints to write from all in all %d constraints.\n", SCIPconshdlrGetName(conshdlrs[i]), nconshdlrconss, SCIPconshdlrGetNConss(conshdlrs[i]));
349 
350             for( c = 0; c < nconshdlrconss; ++c )
351             {
352                conss[nconss] = conshdlrconss[c];
353                nconss++;
354             }
355          }
356       }
357       else
358       {
359          conss = prob->conss;
360          nconss = prob->nconss;
361       }
362 
363       if( genericnames )
364       {
365          SCIP_VAR* var;
366          int size;
367 
368          /* save variable and constraint names and replace these names by generic names */
369 
370          /* allocate memory for saving the original variable and constraint names */
371          SCIP_CALL( SCIPsetAllocBufferArray(set, &varnames, nvars) );
372          SCIP_CALL( SCIPsetAllocBufferArray(set, &fixedvarnames, nfixedvars) );
373          SCIP_CALL( SCIPsetAllocBufferArray(set, &consnames, nconss) );
374 
375          /* compute length of the generic variable names:
376           * - nvars + 1 to avoid log of zero
377           * - +3 (zero at end + 'x' + 1 because we round down)
378           * Example: 10 -> need 4 chars ("x10\0")
379           */
380          size = (int) log10(nvars+1.0) + 3;
381 
382          for( i = 0; i < nvars; ++i )
383          {
384             var = vars[i];
385             varnames[i] = SCIPvarGetName(var);
386 
387             SCIP_CALL( SCIPsetAllocBufferArray(set, &name, size) );
388             (void) SCIPsnprintf(name, size, "x%d", i + set->write_genoffset);
389             SCIPvarSetNamePointer(var, name);
390          }
391 
392          /* compute length of the generic variable names */
393          size = (int) log10(nfixedvars+1.0) + 3;
394 
395          for( i = 0; i < nfixedvars; ++i )
396          {
397             var = fixedvars[i];
398             fixedvarnames[i] = SCIPvarGetName(var);
399 
400             SCIP_CALL( SCIPsetAllocBufferArray(set, &name, size) );
401             (void) SCIPsnprintf(name, size, "y%d", i);
402             SCIPvarSetNamePointer(var, name);
403          }
404 
405          /* compute length of the generic constraint names */
406          size = (int) log10(nconss+1.0) + 3;
407 
408          for( i = 0; i < nconss; ++i )
409          {
410             cons = conss[i];
411             consnames[i] = SCIPconsGetName(cons);
412 
413             SCIP_CALL( SCIPsetAllocBufferArray(set, &name, size) );
414             (void) SCIPsnprintf(name, size, "c%d", i);
415             SCIPconsSetNamePointer(cons, name);
416          }
417       }
418 
419       /* adapt objective scale for transformed problem (for the original no change is necessary) */
420       objscale = prob->objscale;
421       if( prob->transformed && prob->objsense == SCIP_OBJSENSE_MAXIMIZE )
422          objscale *= -1.0;
423 
424       /* call reader to write problem */
425       retcode = reader->readerwrite(set->scip, reader, file, prob->name, prob->probdata, prob->transformed,
426          prob->objsense, objscale, prob->objoffset,
427          vars, nvars, prob->nbinvars, prob->nintvars, prob->nimplvars, prob->ncontvars,
428          fixedvars, nfixedvars, prob->startnvars,
429          conss, nconss, prob->maxnconss, prob->startnconss, genericnames, result);
430 
431       /* reset variable and constraint names to original names */
432       if( genericnames )
433       {
434          assert(varnames != NULL);
435          assert(fixedvarnames != NULL);
436          assert(consnames != NULL);
437          for( i = nconss - 1; i >= 0; --i )
438          {
439             cons = conss[i];
440 
441             /* get pointer to temporary generic name and free the memory */
442             consname = SCIPconsGetName(cons);
443             SCIPsetFreeBufferArray(set, &consname);
444 
445             /* reset name */
446             SCIPconsSetNamePointer(cons, consnames[i]);
447          }
448 
449          for( i = nfixedvars - 1; i >= 0; --i )
450             resetVarname(fixedvars[i], set, fixedvarnames[i]);
451 
452          for( i = nvars - 1; i >= 0; --i )
453             resetVarname(vars[i], set, varnames[i]);
454 
455          /* free memory */
456          SCIPsetFreeBufferArray(set, &consnames);
457          SCIPsetFreeBufferArray(set, &fixedvarnames);
458          SCIPsetFreeBufferArray(set, &varnames);
459       }
460 
461       if( prob->transformed )
462       {
463          /* free memory */
464          SCIPsetFreeBufferArray(set, &conss);
465       }
466    }
467    else
468    {
469       *result = SCIP_DIDNOTRUN;
470       retcode = SCIP_OKAY;
471    }
472 
473    /* check for reader errors */
474    if( retcode == SCIP_WRITEERROR )
475       return retcode;
476 
477    SCIP_CALL( retcode );
478 
479    return SCIP_OKAY;
480 }
481 
482 /** gets user data of reader */
SCIPreaderGetData(SCIP_READER * reader)483 SCIP_READERDATA* SCIPreaderGetData(
484    SCIP_READER*          reader              /**< reader */
485    )
486 {
487    assert(reader != NULL);
488 
489    return reader->readerdata;
490 }
491 
492 /** sets user data of reader; user has to free old data in advance! */
SCIPreaderSetData(SCIP_READER * reader,SCIP_READERDATA * readerdata)493 void SCIPreaderSetData(
494    SCIP_READER*          reader,             /**< reader */
495    SCIP_READERDATA*      readerdata          /**< new reader user data */
496    )
497 {
498    assert(reader != NULL);
499 
500    reader->readerdata = readerdata;
501 }
502 
503 /** sets copy method of reader */
SCIPreaderSetCopy(SCIP_READER * reader,SCIP_DECL_READERCOPY ((* readercopy)))504 void SCIPreaderSetCopy(
505    SCIP_READER*          reader,             /**< reader */
506    SCIP_DECL_READERCOPY  ((*readercopy))     /**< copy method of reader or NULL if you don't want to copy your plugin into sub-SCIPs */
507    )
508 {
509    assert(reader != NULL);
510 
511    reader->readercopy = readercopy;
512 }
513 
514 /** sets destructor of reader */
SCIPreaderSetFree(SCIP_READER * reader,SCIP_DECL_READERFREE ((* readerfree)))515 void SCIPreaderSetFree(
516    SCIP_READER*          reader,             /**< reader */
517    SCIP_DECL_READERFREE  ((*readerfree))     /**< destructor of reader */
518    )
519 {
520    assert(reader != NULL);
521 
522    reader->readerfree = readerfree;
523 }
524 
525 /** sets read method of reader */
SCIPreaderSetRead(SCIP_READER * reader,SCIP_DECL_READERREAD ((* readerread)))526 void SCIPreaderSetRead(
527    SCIP_READER*          reader,             /**< reader */
528    SCIP_DECL_READERREAD  ((*readerread))     /**< read method */
529    )
530 {
531    assert(reader != NULL);
532 
533    reader->readerread = readerread;
534 }
535 
536 /** sets write method of reader */
SCIPreaderSetWrite(SCIP_READER * reader,SCIP_DECL_READERWRITE ((* readerwrite)))537 void SCIPreaderSetWrite(
538    SCIP_READER*          reader,             /**< reader */
539    SCIP_DECL_READERWRITE ((*readerwrite))    /**< write method */
540    )
541 {
542    assert(reader != NULL);
543 
544    reader->readerwrite = readerwrite;
545 }
546 
547 /** gets name of reader */
SCIPreaderGetName(SCIP_READER * reader)548 const char* SCIPreaderGetName(
549    SCIP_READER*          reader              /**< reader */
550    )
551 {
552    assert(reader != NULL);
553 
554    return reader->name;
555 }
556 
557 /** gets description of reader */
SCIPreaderGetDesc(SCIP_READER * reader)558 const char* SCIPreaderGetDesc(
559    SCIP_READER*          reader              /**< reader */
560    )
561 {
562    assert(reader != NULL);
563 
564    return reader->desc;
565 }
566 
567 /** gets file extension of reader */
SCIPreaderGetExtension(SCIP_READER * reader)568 const char* SCIPreaderGetExtension(
569    SCIP_READER*          reader              /**< reader */
570    )
571 {
572    assert(reader != NULL);
573 
574    return reader->extension;
575 }
576 
577 /** return whether the reader can read files */
SCIPreaderCanRead(SCIP_READER * reader)578 SCIP_Bool SCIPreaderCanRead(
579    SCIP_READER*          reader              /**< reader */
580    )
581 {
582    assert(reader != NULL);
583 
584    return (reader->readerread != NULL);
585 }
586 
587 /** return whether the reader can write files */
SCIPreaderCanWrite(SCIP_READER * reader)588 SCIP_Bool SCIPreaderCanWrite(
589    SCIP_READER*          reader              /**< reader */
590    )
591 {
592    assert(reader != NULL);
593 
594    return (reader->readerwrite != NULL);
595 }
596 
597 /** gets time in seconds used in this reader for reading */
SCIPreaderGetReadingTime(SCIP_READER * reader)598 SCIP_Real SCIPreaderGetReadingTime(
599    SCIP_READER*          reader              /**< reader */
600    )
601 {
602    assert(reader != NULL);
603 
604    return SCIPclockGetTime(reader->readingtime);
605 }
606 
607 /** enables or disables all clocks of \p reader, depending on the value of the flag */
SCIPreaderEnableOrDisableClocks(SCIP_READER * reader,SCIP_Bool enable)608 void SCIPreaderEnableOrDisableClocks(
609    SCIP_READER*          reader,             /**< the reader for which all clocks should be enabled or disabled */
610    SCIP_Bool             enable              /**< should the clocks be enabled? */
611    )
612 {
613    assert(reader != NULL);
614 
615    SCIPclockEnableOrDisable(reader->readingtime, enable);
616 }
617 
618 /** resets reading time of reader */
SCIPreaderResetReadingTime(SCIP_READER * reader)619 SCIP_RETCODE SCIPreaderResetReadingTime(
620    SCIP_READER*          reader              /**< reader */
621    )
622 {
623    assert(reader != NULL);
624 
625    /* reset reading time/clock */
626    SCIPclockReset(reader->readingtime);
627 
628    return SCIP_OKAY;
629 }
630 
631