If you need to debug your own code that uses SCIP, here are some tips and tricks:
Use asserts in your code to show preconditions for the parameters, invariants and postconditions. Assertions are boolean expressions which inevitably have to evaluate to
TRUE
. Consider the following example:staticSCIP* scip, /**< SCIP data structure */SCIP_CONS* cons, /**< linear constraint */SCIP_EVENTHDLR* eventhdlr, /**< event handler to call for the event processing */int pos /**< array position of variable to catch bound change events for */){SCIP_CONSDATA* consdata;assert(scip != NULL);assert(cons != NULL);assert(eventhdlr != NULL);consdata = SCIPconsGetData(cons);assert(consdata != NULL);assert(0 <= pos && pos < consdata->nvars);assert(consdata->vars != NULL);assert(consdata->vars[pos] != NULL);assert(SCIPvarIsTransformed(consdata->vars[pos]));assert(consdata->eventdata != NULL);assert(consdata->eventdata[pos] == NULL);consdata->eventdata[pos]->cons = cons;consdata->eventdata[pos]->varpos = pos;SCIP_CALL( SCIPcatchVarEvent(scip, consdata->vars[pos],eventhdlr, consdata->eventdata[pos], &consdata->eventdata[pos]->filterpos) );consdata->removedfixings = consdata->removedfixings && SCIPvarIsActive(consdata->vars[pos]);return SCIP_OKAY;}(Source: src/scip/cons_linear.c)
As you can see, both pointers and integers are checked for valid values at the beginning of the function
consdataCatchEvent()
. This is particularly important for, e.g., array indices like the variablepos
in this example, where using theconsdata->nvars[pos]
pointer could result in unexspected behaviour if the asserted precondition onpos
were not matched and <pos> were an arbitrary index outside the array range.- In order to activate assertions, use the Debug mode by compiling SCIP via and run the code. See Makefiles / Installation information for further information about compiler options for SCIP.make OPT=dbg
- Spending only little extra time on asserting preconditions saves most of the time spent on debugging!
- Turn on additional debug output by adding the line at the top of SCIP files you want to analyze. This will output messages included in the code using#define SCIP_DEBUG
SCIPdebugMsg(scip, ...)
(orSCIPdebugMessage()
), see How to activate debug messages. We recommend to also useSCIPdebugMsg(scip, ...)
in your own code for being able to activate debug output in the same way. - If available on your system, we recommend to use a debugger like
gdb
to trace all function calls on the stack, display values of certain expressions, manually break the running code, and so forth. - If available on your system, you can use software like valgrind to check for uninitialized values or segmentation faults.
- For checking the usage of SCIP memory, you can use
SCIPprintMemoryDiagnostic()
. This outputs memory that is currently in use, which can be useful after aSCIPfree()
call. - If there are memory leaks for which you cannot detect the origin, you can remake your code with the option NOBLKBUFMEM=true (do not forget to clean your code before with
make OPT=... LPS=... clean
). After that valgrind (or similar) helps to detect leaked memory. - If your code cuts off a feasible solution, but you do not know which component is responsible, you can use the debugging mechanism (see How to add a debug solution). Therefore, a given solution is read and it is checked for every reduction, whether the solution will be pruned globally.
How to activate debug messages
For example, if we include a #define SCIP_DEBUG
at the top of heur_oneopt.h, recompile SCIP in DBG mode, and run the SCIP interactive shell to solve p0033.mps from the MIPLIB 3.0 , we get some output like:
How to add a debug solution
Continuing the example above, we finish the solving process. The optimal solution can now be written to a file:
If we afterwards recompile SCIP with the additional compiler flag DEBUGSOL=true
, set the parameter misc/debugsol = check/p0033.sol
, and run SCIP again it will output:
Further debug output would only appear, if the solution was cut off in the solving process.