Mainly, file readers are called to parse an input file and generate a constraint integer programming model. They create constraints and variables and activate variable pricers if necessary. However, they can also be called, for example, to parse an input file containing information about a primal solution or fixing of variables. Besides that it is possible to use some of them for writing (exporting) the problem in a specific format.
A complete list of all file readers contained in this release can be found here.
Since a file reader is also responsible for writing a file, the user may ask why the readers have not the name "filehandler". This name would represent this plugin much better than the used one.
The used name "readers" is historically grown. In the beginning of SCIP there was no need to write/export problems. Therefore, the the plugin name "readers" was best fitting for this plugin since only reading was essential. It turned out, however, that it is quite nice to write/export certain subproblem during the solving process mainly for debugging. Therefore, a writing callback was added to the "readers" plugin.
We now explain how users can add their own file readers. Take the file reader for MIPs in IBM's Mathematical Programming System format (src/scip/reader_mps.c) as an example. As all other default plugins, it is written in C. C++ users can easily adapt the code by using the scip::ObjReader wrapper base class and implement the scip_...() virtual methods instead of the SCIP_DECL_READER... callback methods.
Additional documentation for the callback methods of a file reader can be found in the file type_reader.h.
Here is what you have to do to implement a file reader named "myreader" in C:
- Copy the template files src/scip/reader_xyz.c and src/scip/reader_xyz.h into files named "reader_myreader.c" and "reader_myreader.h".
Make sure to adjust your Makefile such that these files are compiled and linked to your project.
- Use SCIPincludeReaderMyreader() in order to include the file reader into your SCIP instance, e.g., in the main file of your project (see, e.g., src/cmain.c in the Binpacking example).
- Open the new files with a text editor and replace all occurrences of "xyz" by "myreader".
- Adjust the properties of the file reader.
- Define the file reader data. This is optional.
- Implement the interface methods.
- Implement the fundamental callback methods.
- Implement the additional callback methods. This is optional.
At the top of the new file "reader_myreader.c" you can find the file reader properties. These are given as compiler defines. In the C++ wrapper class, you have to provide the file reader properties by calling the constructor of the abstract base class scip::ObjReader from within your constructor. The properties you have to set have the following meaning:
- READER_NAME: the name of the file reader.
- This name is used in the interactive shell to address the file reader. Additionally, if you are searching for a file reader with SCIPfindReader(), this name is looked up. Names have to be unique: no two file readers may have the same name.
- READER_DESC: the description of the file reader.
- This string is printed as a description of the file reader in the interactive shell.
- READER_EXTENSION: the file name extension of the file reader.
- Each file reader is hooked to a single file name extension. It is automatically called if the user wants to read in a file of corresponding name. The extensions of the different file readers have to be unique. Note that the additional extension '.gz', '.z', or '.Z' (indicating a gzip compressed file) are ignored for assigning an input file to a reader.
It is not possible to hook up a (single) file reader with more than one file extension. It is, however, not necessary to implement the same (parsing/writing) methods more than once, if you want to support several file extension with the same parser. To do so look at the files reader_lp.c and reader_rlp.c. Both support the LP format.
Below the header "Data structures" you can find a struct which is called "struct SCIP_ReaderData". In this data structure, you can store the data of your file reader. For example, you should store the adjustable parameters of the file reader in this data structure. If you are using C++, you can add file reader data as usual as object variables to your class.
Defining file reader data is optional. You can leave the struct empty.
At the bottom of "reader_myreader.c", you can find the interface method SCIPincludeReaderMyreader(), which also appears in "reader_myreader.h". SCIPincludeReaderMyreader() is called by the user, if (s)he wants to include the reader, i.e., if (s)he wants to use the reader in his/her application.
This method only has to be adjusted slightly. It is responsible for notifying SCIP of the presence of the reader. For this, you can either call SCIPincludeReader(), or SCIPincludeReaderBasic() since SCIP version 3.0. In the latter variant, additional callbacks must be added via setter functions as, e.g., SCIPsetReaderCopy(). We recommend this latter variant because it is more stable towards future SCIP versions which might have more callbacks, whereas source code using the first variant must be manually adjusted with every SCIP release containing new callbacks for readers in order to compile.
If you are using file reader data, you have to allocate the memory for the data at this point. You can do this by calling:
You also have to initialize the fields in struct SCIP_ReaderData afterwards.
File reader plugins have no fundamental callback methods. This is due to the fact that a file reader can be used for reading and/or writing a file. A file reader is only useful if the reader method READERREAD and/or the writing method READERWRITE is implemented. One of these methods should be implemented for every file reader; the other callback methods READERCOPY and READERFREE are optional. In the C++ wrapper class scip::ObjReader, the scip_read() and scip_write() methods (which corresponds to the READERREAD and READERWRITE callback) are virtual member functions. At least one of them should be implemented.
Additional documentation for the callback methods can be found in type_reader.h.
File reader plugins contain only additional callback methods, namely the methods READERREAD, READERWRITE, READERFREE, and READERCOPY. Therefore, these are not needed to be implemented. However, at least READERREAD and/or READERWRITE should be implemented (see notes above).
The READERREAD callback is called when the user invokes SCIP to read in a file with file name extension corresponding to the READER_EXTENSION property of the file reader. This is usually triggered by a call to the method SCIPreadProb() or by an interactive shell command. The READERREAD callback should parse the input file and perform the desired action, which usually means generating a constraint integer programming model, adding a primal solution, fixing variables in an existing model.
Typical methods called by a file reader that is used to read/generate constraint integer programming models are, for example,
- creating an empty problem: SCIPcreateProb()
- creating the variables: SCIPcreateVar(), SCIPchgVarType(), SCIPchgVarLb(), SCIPchgVarUb(), SCIPaddVar(), and SCIPreleaseVar()
- modifying the objective function: SCIPchgVarObj() and SCIPsetObjsense().
- creating the constraints: SCIPcreateConsLinear(), SCIPaddCoefLinear(), SCIPchgLhsLinear(), SCIPchgRhsLinear(), SCIPaddCons(), and SCIPreleaseCons()
Primal solutions can only be created for the transformed problem. Therefore, the user has to call SCIPtransformProb() before (s)he reads in the file containing the solution and adds it to the solution pool via the method SCIPreadSol().
The READERWRITE callback is called when the user invokes SCIP to write a problem (original or transformed) in the format the reader supports. This is only possible if this callback is implemented. To write the problem all necessary information is given through the parameters of this callback method (see type_reader.h). This information should be used to output the problem in the requested format. This callback method is usually triggered by the call of the methods SCIPwriteOrigProblem(), SCIPwriteTransProblem(), SCIPprintOrigProblem(), or SCIPprintTransProblem().
A typical method called by a file reader which is used to write/export a constraint integer programming model is SCIPinfoMessage(). This method outputs a given string into a file or into stdout.
For an example we refer to the writing method of the MPS reader (see reader_mps.c).
The READERCOPY callback is executed when a SCIP instance is copied, e.g. to solve a sub-SCIP. By defining this callback as
NULL the user disables the execution of the specified reader for all copied SCIP instances. The question might arise why to copy that plugin. In case of debugging it is nice to be able to write/display the copied instances. Since the reader is in charge of that, you might want to copy the plugin. Below you see a standard implementation.
If you are using file reader data, you have to implement this method in order to free the file reader data. This can be done by the following procedure:
If you have allocated memory for fields in your file reader data, remember to free this memory before freeing the file reader data itself. If you are using the C++ wrapper class, this method is not available. Instead, just use the destructor of your class to free the member variables of your class.