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:

    static
    SCIP* 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);
    SCIP_CALL( SCIPallocBlockMemory(scip, &(consdata->eventdata[pos])) ); /*lint !e866*/
    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;
    }

    As you can see, both pointers and integers are checked for valid values at the beginning of the function consCatchEvent(). This is particularly important for, e.g., array indices like the variable pos in this example, where using the consdata->vars[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
    cmake -DCMAKE_BUILD_TYPE=Debug
    or the Makefile equivalent
    make OPT=dbg
    and run the code. See Building SCIP using CMake and Building SCIP using the Makefile system for further information about compiler options for SCIP. As a rule of thumb, 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 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 recompile your code with the option cmake -DNOBLKBUFMEM=on (or make NOBLKBUFMEM=true if you are using the Makefile system. Also for the Makefile system, do not forget to clean your code before with make OPT=... LPS=... clean) Only with that change, valgrind (or similar) reliably 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.c, recompile SCIP in Debug 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-2024 Zuse Institute 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:

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 recompile SCIP with the additional compiler flag cmake -DDEBUGSOL=on (make DEBUGSOL=true in the Makefile system), set the parameter misc/debugsol = check/p0033.sol, and run SCIP again 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.