While solving a constraint integer program, SCIP drops thousands of events such as SCIP_EVENTTYPE_VARFIXED (a complete list of all events is given in type_event.h). These events can be caught and used to do something after a certain event happens. Events can be used to speed up the solution process. For example, the set partitioning constraint is only worth propagating if one of the involved variables is fixed. This can be detected by catching the event SCIP_EVENTTYPE_VARFIXED. To be able to catch an event it is necessary to write an event handler which defines what to do after a certain event was caught.
We now explain how users can add their own event handlers. We give the explanation for creating your own source file for each additional event handler. Of course, you can collect different event handlers in one source file or you can put the event handler directly into the constraint handler. In a second step we discuss the usage of an event handler. This means how to catch and drop events. Finally, we give some notes on the existing types of events.
Take src/scip/cons_logior.c, where the event handler is directly included into the constraint handler. As all other default plugins, the event handlers are written in C. C++ users can easily adapt the code by using the scip::ObjEventhdlr wrapper base class and implement the scip_...() virtual methods instead of the SCIP_DECL_EVENT... callback methods.
Additional documentation for the callback methods of an event handler can be found in the file type_event.h. There is also an example written in C which deals with an event handler. You find this example in the directory "examples/Eventhdlr/". An C++ example can be found within the TSP project (examples/TSP/src/EventhdlrNewSol.cpp).
Here is what you have to do to implement an event handler (assuming your event handler is named "bestsol"):
- Copy the template files src/scip/event_xyz.c and src/scip/event_xyz.h into files named "event_bestsol.c" and "event_bestsol.h".
Make sure to adjust your Makefile such that these files are compiled and linked to your project. - Use SCIPincludeEventBestsol() in order to include the event handler into your SCIP instance, e.g., in the main file of your project (see, e.g., src/cmain.c in the Eventhdlr example).
- Open the new files with a text editor and replace all occurrences of "xyz" by "bestsol".
- Adjust the properties of the event handler.
- Implement the interface methods.
- Implement the fundamental callback methods.
- Implement the additional callback methods. This is optional.
Properties of a Event Handler
At the top of the new file "event_bestsol.c" you can find the event handler properties. These are given as compiler defines. In the C++ wrapper class, you have to provide the event handler properties by calling the constructor of the abstract base class scip::ObjEventhdlr from within your constructor. The properties you have to set have the following meaning:
- EVENT_NAME: the name of the event handler.
- This name has to be unique with respect to all other event handlers. If you are searching for an event handler with SCIPfindEventhdlr(), this name is looked up.
- EVENT_DESC: the description of the event handler.
- This string is printed as a description of the event handler.
Event Handler Data
Below the header "Data structures" you can find a struct which is called "struct SCIP_EventhdlrData". In this data structure, you can store the data of your event handler. For example, you should store the adjustable parameters of the event handler in this data structure. If you are using C++, you can add event handler data as usual as object variables to your class.
Defining event handler data is optional. You can leave the struct empty.
Interface Methods
At the bottom of "event_bestsol.c", you can find the interface method SCIPincludeEventBestsol(), which also appears in "event_bestsol.h". SCIPincludeEventBestsol() is called by the user, if (s)he wants to include the event handler, i.e., if (s)he wants to use the event handler in his/her application.
This method only has to be adjusted slightly. It is responsible for notifying SCIP of the presence of the event handler. For this, you can either call SCIPincludeEventhdlr(), or SCIPincludeEventhdlrBasic() 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 event handlers in order to compile.
If you are using event handler 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_EventhdlrData afterwards.
Although this is very uncommon, you may also add user parameters for your event handler, see the method SCIPincludeConshdlrKnapsack() in the knapsack constraint handler for an example.
Fundamental Callback Methods of a Event Handler
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 event handler itself to SCIP using SCIPincludeEventhdlr() or SCIPincludeEventhdlrBasic(), see Interface Methods.
Event handler plugins have only one fundamental callback method, namely the EVENTEXEC method. This method has to be implemented for every event handler; the other callback methods are optional. In the C++ wrapper class scip::ObjEventhdlr, the scip_exec() method (which corresponds to the EVENTEXEC callback) is a virtual abstract member function. You have to implement it in order to be able to construct an object of your event handler class.
Additional documentation for the callback methods can be found in type_event.h.
EVENTEXEC
The EVENTEXEC callback is called after the requested event happened. Then the event handler can do some action in reaction to the event.
Typical the execution method sets a parameter to TRUE to indicate later in solving process that something happened which should be analyzed further. In the knapsack constraint handler you find such a typical example.
Additional Callback Methods of a Event Handler
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 SCIPincludeEventhdlr() to SCIP or via specific setter functions after a call of SCIPincludeEventhdlrBasic(), see also Interface Methods.
EVENTCOPY
The EVENTCOPY 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 event handler for all copied SCIP instances. Note that in most cases the event handler in the copied instance will be initialize by those objects (such as constraint handlers or propagators) which need this event handler (see cons_knapsack.h). In these cases the copy callback can be ignored. In case of general events, such as a new best solution being found (SCIP_EVENTTYPE_BESTSOLFOUND), you might want to implement that callback. The event handler example which you find in the directory "examples/Eventhdlr/" uses that callback.
EVENTFREE
If you are using event handler data, you have to implement this method in order to free the event handler data. This can be done by the following procedure:
If you have allocated memory for fields in your event handler data, remember to free this memory before freeing the event handler 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.
EVENTINIT
The EVENTINIT callback is executed after the problem is transformed. The event handler may, e.g., use this call to initialize its event handler data.
EVENTEXIT
The EVENTEXIT callback is executed before the transformed problem is freed. In this method, the event handler should free all resources that have been allocated for the solving process in EVENTINIT.
EVENTINITSOL
The EVENTINITSOL callback is executed when the presolving is finished and the branch-and-bound process is about to begin. The event handler may use this call to initialize its branch-and-bound specific data.
EVENTEXITSOL
The EVENTEXITSOL callback is executed before the branch-and-bound process is freed. The event handler should use this call to clean up its branch-and-bound specific data.
Catching and Dropping Events
After you have implemented the event handler, you have to tell SCIP for which events this event handler should be used. This can be a general events, such as SCIP_EVENTTYPE_BESTSOLFOUND
, or a variable event which is the most common way.
In case of a general (not variable) event you use the function SCIPcatchEvent() to attach to an event and SCIPdropEvent() to release this event later.
If you want trigger some variable event, you use the method SCIPcatchVarEvent() to attach the variable event and SCIPdropVarEvent() to drop it later.
Event types
All available events are listed in type_event.h. There are atomic events such as SCIP_EVENTTYPE_VARFIXED
and combined events such as SCIP_EVENTTYPE_VARCHANGED
. The events are encoded via bit masks. Each atomic event has a unique power of two. This enables combination of the atomic events.
SCIP only throws atomic events. However, an event handler might be interested in bunch of events. Through the underlying bit masks it is possible to combine the atomic events. For example, SCIP_EVENTTYPE_VARCHANGED
is an event which combines the events SCIP_EVENTTYPE_VARFIXED
, SCIP_EVENTTYPE_VARUNLOCKED
, SCIP_EVENTTYPE_OBJCHANGED
, SCIP_EVENTTYPE_GBDCHANGED
, SCIP_EVENTTYPE_DOMCHANGED
, and SCIP_EVENTTYPE_IMPLADDED
.
Depending on the event type, the event offers different information. The methods which can be used to gain access to this information are given in pub_event.h.