Separators are used to generate general purpose cutting planes. Constraint based cutting planes, the second type of cutting planes in SCIP, are separated in the CONSSEPALP and CONSSEPASOL callback methods of the constraint handlers, see CONSSEPALP and CONSSEPASOL. These cuts are valid inequalities or even facets of the polyhedron described by a single constraint or a subset of the constraints of a single constraint class. In contrast, general purpose cuts do not require or exploit any knowledge about the underlying problem structure but use only the current LP relaxation and the integrality conditions. See also "When should I implement a constraint handler, when should I implement a separator?" on Frequently Asked Questions (FAQ).
A complete list of all separators contained in this release can be found here.
We now explain how users can add their own separators. Take the separator for the class of Gomory mixed integer inequalities (src/scip/sepa_gomory.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::ObjSepa wrapper base class and implement the scip_...() virtual methods instead of the SCIP_DECL_SEPA... callback methods.
Additional documentation for the callback methods of a separator, in particular for the input parameters, can be found in the file type_sepa.h.
Here is what you have to do to implement a separator:
At the top of the new file "sepa_myseparator.c", you can find the separator properties. These are given as compiler defines. In the C++ wrapper class, you have to provide the separator properties by calling the constructor of the abstract base class scip::ObjSepa from within your constructor. The properties you have to set have the following meaning:
Below the header "Data structures" you can find a struct which is called "struct SCIP_SepaData". In this data structure, you can store the data of your separator. For example, you should store the adjustable parameters of the separator in this data structure. In a separator, user parameters for the maximal number of separation rounds per node and for the maximal number of cuts separated per separation round might be useful. If you are using C++, you can add separator data as usual as object variables to your class.
Defining separator data is optional. You can leave the struct empty.
At the bottom of "sepa_myseparator.c", you can find the interface method SCIPincludeSepaMyseparator(), which also appears in "sepa_myseparator.h" SCIPincludeSepaMyseparator() is called by the user, if (s)he wants to include the separator, i.e., if (s)he wants to use the separator in his/her application.
This method only has to be adjusted slightly. It is responsible for notifying SCIP of the presence of the separator. For this, you can either call SCIPincludeSepa(), or SCIPincludeSepaBasic() since SCIP version 3.0. In the latter variant, additional callbacks must be added via setter functions as, e.g., SCIPsetSepaCopy(). 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 separators in order to compile.
If you are using separator 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_SepaData" afterwards. For freeing the separator data, see SEPAFREE.
You may also add user parameters for your separator, see How to add additional user parameters for how to add user parameters and the method SCIPincludeSepaGomory() in src/scip/sepa_gomory.c for an example.
The fundamental callback methods of the plugins are the ones that have to be implemented in order to obtain an operational algorithm. They are passed together with the separator itself to SCIP using SCIPincludeSepa() or SCIPincludeSepaBasic(), see Interface Methods.
Additional documentation for the callback methods, in particular to their input parameters, can be found in type_sepa.h.
The SEPAEXECLP callback is executed during the price-and-cut loop of the subproblem processing. It should try to generate general purpose cutting planes in order to separate the current LP solution. The method is called in the LP solution loop, which means that a valid LP solution exists.
Usually, the callback searches and produces cuts, that are added with a call to SCIPaddCut(). If the cut should be added to the global cut pool, it calls SCIPaddPoolCut(). In addition to LP rows, the callback may also produce domain reductions or add additional constraints.
Overall, the SEPAEXECLP callback has the following options, which is indicated by the possible return values of the 'result' variable (see type_sepa.h):
The SEPAEXECSOL callback is executed during the separation loop on arbitrary primal solutions. It should try to generate general purpose cutting planes in order to separate the given primal solution. The method is not called in the LP solution loop, which means that there is no valid LP solution.
In the standard SCIP environment, the SEPAEXECSOL callback is not used because only LP solutions are separated. The SEPAEXECSOL callback provides means to support external relaxation handlers like semidefinite relaxations that want to separate an intermediate primal solution vector. Thus, if you do not want to support such external plugins, you do not need to implement this callback method.
Usually, the callback searches and produces cuts, that are added with a call to SCIPaddCut(). If the cut should be added to the global cut pool, it calls SCIPaddPoolCut(). In addition to LP rows, the callback may also produce domain reductions or add other constraints.
Overall, the SEPAEXECSOL callback has the following options, which is indicated by the possible return values of the 'result' variable (see type_sepa.h):
The additional callback methods do not need to be implemented in every case. However, some of them have to be implemented for most applications, they can be used, for example, to initialize and free private data. Additional callbacks can either be passed directly with SCIPincludeSepa() to SCIP or via specific setter functions after a call of SCIPincludeSepaBasic(), see also Interface Methods.
If you have allocated memory for fields in your separator data, remember to free this memory before freeing the separator 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.
The SEPACOPY 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 separator for all copied SCIP instances. This may deteriorate the performance of primal heuristics using sub-SCIPs.
The SEPAINIT callback is executed after the problem is transformed. The separator may, e.g., use this call to initialize its separator data. The difference between the original and the transformed problem is explained in "What is this thing with the original and the transformed problem about?" on Frequently Asked Questions (FAQ).
The SEPAEXIT callback is executed before the transformed problem is freed. In this method, the separator should free all resources that have been allocated for the solving process in SEPAINIT.
The SEPAINITSOL callback is executed when the presolving is finished and the branch-and-bound process is about to begin. The separator may use this call to initialize its branch-and-bound specific data.
The SEPAEXITSOL callback is executed before the branch-and-bound process is freed. The separator should use this call to clean up its branch-and-bound data, in particular to release all LP rows that it has created or captured.