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:
static
int pos
)
{
assert(eventhdlr !=
NULL);
assert(consdata !=
NULL);
assert(0 <= pos && pos < consdata->nvars);
assert(consdata->vars !=
NULL);
assert(consdata->vars[pos] !=
NULL);
assert(consdata->eventdata !=
NULL);
assert(consdata->eventdata[pos] ==
NULL);
consdata->eventdata[pos]->cons = cons;
consdata->eventdata[pos]->varpos = pos;
eventhdlr, consdata->eventdata[pos], &consdata->eventdata[pos]->filterpos) );
}
(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 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 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 at the top of SCIP files you want to analyze. This will output messages included in the code using
SCIPdebugMsg(scip, ...)
(or SCIPdebugMessage()
), see How to activate debug messages. We recommend to also use SCIPdebugMsg(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 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-2017 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
...
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:
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
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 ;-)