Scippy

SCIP

Solving Constraint Integer Programs

Debugging

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, taken from the file src/scip/cons_linear.c:
    SCIP_RETCODE consdataCatchEvent(
    SCIP* scip, /**< SCIP data structure *\/
    * SCIP_CONSDATA* consdata, /**< linear constraint data *\/
    * SCIP_EVENTHDLR* eventhdlr, /**< event handler to call for the event processing *\/
    * int pos /**< array position of variable to catch bound change events for *\/
    * )
    * {
    * assert(scip != NULL);
    * assert(consdata != NULL);
    * assert(eventhdlr != NULL);
    * assert(0 <= pos && pos < consdata->nvars);
    * ...
    * }
    *
    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 variable pos in this example, where using the consdata->nvars[pos] pointer could result in unexspected behaviour if the asserted precondition on pos 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
    make OPT=dbg
    and run the code. See Makefiles / Installation information for further information about compiler options for SCIP.
  • 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
    #define SCIP_DEBUG
    at the top of SCIP files you want to analyze. This will output messages included in the code using SCIPdebugMessage() (see How to activate debug messages). We recommend to also use SCIPdebugMessage() 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 a SCIPfree() 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 define SCIP_DEBUG_SOLUTION in the file debug.h to be a filename containing a solution in SCIP format (see How to add a debug solution). This solution is then read and it is checked for every cut, whether the solution violates the cut.

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:

SCIP version 1.1.0 [precision: 8 byte] [memory: block] [mode: debug] [LP solver: SoPlex 1.4.0]
Copyright (c) 2002-2014 Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB)
user parameter file <scip.set> not found - using default parameters
SCIP> read check/IP/miplib/p0033.mps
original problem has 33 variables (33 bin, 0 int, 0 impl, 0 cont) and 16 constraints
SCIP> optimize
...
0.1s| 1 | 0 | 132 | 257k| 0 | 14 | 30 | 13 | 13 | 30 | 51 | 39 | 0 | 0 | 3.026472e+03 | 3.347000e+03 | 10.59%
[src/scip/heur_oneopt.c:332] debug: Row <R122> has activity 110
[src/scip/heur_oneopt.c:332] debug: Row <R123> has activity 216
...
[src/scip/heur_oneopt.c:101] debug: Try to shift down variable <t_C157> with
[src/scip/heur_oneopt.c:102] debug: lb:<-0> <= val:<1> <= ub:<1> and obj:<171> by at most: <1>
[src/scip/heur_oneopt.c:135] debug: -> The shift value had to be reduced to <0>, because of row <R122>.
[src/scip/heur_oneopt.c:137] debug: lhs:<-1e+20> <= act:<110> <= rhs:<148>, colval:<-60>
...
[src/scip/heur_oneopt.c:383] debug: Only one shiftcand found, var <t_C167>, which is now shifted by<-1.0>
k 0.1s| 1 | 0 | 132 | 258k| 0 | 14 | 30 | 13 | 13 | 30 | 51 | 39 | 0 | 0 | 3.026472e+03 | 3.164000e+03 | 4.54%
[src/scip/heur_oneopt.c:436] debug: found feasible shifted solution:
objective value: 3164.00000000012
C157 1 (obj:171)
C163 1 (obj:163)
C164 1 (obj:69)
C170 1 (obj:49)
C172 1 (obj:258)
C174 1 (obj:250)
C175 1 (obj:500)
C179 1 (obj:318)
C181 1 (obj:318)
C182 1 (obj:159)
C183 1.00000000000038 (obj:318)
C184 1 (obj:159)
C185 1 (obj:318)
C186 1 (obj:114)
[src/scip/heur_oneopt.c:498] debug: Finished 1-opt heuristic
...

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:

SCIP> display solution
objective value: 3089
C157 1 (obj:171)
C163 1 (obj:163)
C164 1 (obj:69)
C166 1 (obj:183)
C170 1 (obj:49)
C174 1 (obj:250)
C177 1 (obj:500)
C179 1 (obj:318)
C181 1 (obj:318)
C182 1 (obj:159)
C183 1 (obj:318)
C184 1 (obj:159)
C185 1 (obj:318)
C186 1 (obj:114)
SCIP> write solution check/p0033.sol
written solution information to file <check/p0033.sol>

If we afterwards use #define SCIP_DEBUG_SOLUTION "check/p0033.sol" in debug.h, recompile and run SCIP, it will output:

SCIP> read check/IP/miplib/p0033.mps
original problem has 33 variables (33 bin, 0 int, 0 impl, 0 cont) and 16 constraints
SCIP> optimize
presolving:
***** debug: reading solution file <check/p0033.sol>
***** debug: read 15 non-zero entries

Further debug output would only appear, if the solution was cut off in the solving process. Of course, this is not the case! Hopefully...otherwise, please send a bug report ;-)