Search code examples
optimizationcallbackscippyscipopt

BESTSOLFOUND event handler on PySCIPopt


I am working on the problem in which I am willing to check and test some of the SCIP's event handlers. I see this SCIP document page and have some questions:

  • Do the event handlers callbacks return with something that may be printable? or just for assertation?
  • What exactly is the difference between SCIPincludeEventhdlr() and SCIPincludeEventhdlrBasic()?
  • When we really need to use an additional callback besides an event handler?

I tried to call an execute event handler to get the BESTSOLFOUND thought the solving process, but I got the following error:

class MyEvent(Eventhdlr):

    def eventinit(self):
        calls.append('eventinit')
        self.model.catchEvent(SCIP_EVENTTYPE.BESTSOLFOUND, self)

    def eventexit(self):
        calls.append('eventexit')
        self.model.dropEvent(SCIP_EVENTTYPE.BESTSOLFOUND, self)

    def eventexec(self, event):
        calls.append('eventexec')
        self.model.eventexec(SCIP_EVENTTYPE.BESTSOLFOUND, self)

AttributeError                            Traceback (most recent call last)
<ipython-input-11-c33e5174ab7e> in eventexec(self, event)
     16     def eventexec(self, event):
     17         calls.append('eventexec')
---> 18         self.model.eventexec(SCIP_EVENTTYPE.BESTSOLFOUND, self)
     19 
     20 

AttributeError: 'pyscipopt.scip.Model' object has no attribute 'eventexec'

Also, an MRE could be found here. I am wondering where am doing wrong and how can I fix that?

Just updated code:

import pytest
from pyscipopt import Model, Eventhdlr, SCIP_RESULT, SCIP_EVENTTYPE, SCIP_PARAMSETTING

calls = []

class MyEvent(Eventhdlr):

    def eventinit(self):
        calls.append('eventinit')
        self.model.catchEvent(SCIP_EVENTTYPE.BESTSOLFOUND, self)

    def eventexit(self):
        calls.append('eventexit')
        self.model.dropEvent(SCIP_EVENTTYPE.BESTSOLFOUND, self)

    def eventexec(self, event):
         calls.append('eventexec')


def test_event():
    # create solver instance
    s = Model()
    s.redirectOutput()
    s.printVersion()
    print("--------------------------------------")

    s.setPresolve(SCIP_PARAMSETTING.OFF)
    s.setSeparating(SCIP_PARAMSETTING.OFF)
    s.setHeuristics(SCIP_PARAMSETTING.OFF)

    eventhdlr = MyEvent()
    s.includeEventhdlr(eventhdlr, "BESTSOLFOUND", "python event handler to catch BESTSOLFOUND")

    # add some variables
    x = s.addVar("x", vtype="I")
    y = s.addVar("y", vtype="I")

    # add some constraint
    s.setObjective(7*x + 9*y, "maximize")
    s.addCons(-x + 3*y <=  6)
    s.addCons(7*x +  y <=  35)

    # solve problem
    s.optimize()

    # print solution
    print("--------------------------------------")
    print("x = ", s.getVal(x))
    print("y = ", s.getVal(y))

    del s

    assert 'eventinit' in calls
    assert 'eventexit' in calls
    assert 'eventexec' in calls

if __name__ == "__main__":
    test_event()


SCIP version 8.0.3 [precision: 8 byte] [memory: block] [mode: optimized] [LP solver: Soplex 6.0.3] [GitHash: 62fab8a2e3]
Copyright (C) 2002-2022 Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB)
--------------------------------------
presolving:
   (0.0s) symmetry computation started: requiring (bin +, int +, cont +), (fixed: bin -, int -, cont -)
   (0.0s) no symmetry present
presolving (0 rounds: 0 fast, 0 medium, 0 exhaustive):
 0 deleted vars, 0 deleted constraints, 0 added constraints, 0 tightened bounds, 0 added holes, 0 changed sides, 0 changed coefficients
 0 implications, 0 cliques
presolved problem has 2 variables (0 bin, 2 int, 0 impl, 0 cont) and 2 constraints
      2 constraints of type <linear>
transformed objective value is always integral (scale: 1)
Presolving Time: 0.00

 time | node  | left  |LP iter|LP it/n|mem/heur|mdpt |vars |cons |rows |cuts |sepa|confs|strbr|  dualbound   | primalbound  |  gap   | compl. 
  0.0s|     1 |     0 |     1 |     - |   575k |   0 |   2 |   2 |   2 |   0 |  0 |   0 |   0 | 5.900000e+01 |      --      |    Inf | unknown
  0.0s|     1 |     2 |     1 |     - |   575k |   0 |   2 |   2 |   2 |   0 |  1 |   0 |   0 | 5.900000e+01 |      --      |    Inf | unknown
* 0.0s|     2 |     1 |     1 |   0.0 |    LP  |   1 |   2 |   2 |   2 |   0 |  1 |   0 |   0 | 5.500000e+01 | 3.500000e+01 |  57.14%| unknown
* 0.0s|     3 |     0 |     2 |   0.5 |    LP  |   1 |   2 |   1 |   2 |   0 |  1 |   0 |   0 | 5.500000e+01 | 5.500000e+01 |   0.00%|  58.64%

SCIP Status        : problem is solved [optimal solution found]
Solving Time (sec) : 0.01
Solving Nodes      : 3
Primal Bound       : +5.50000000000000e+01 (2 solutions)
Dual Bound         : +5.50000000000000e+01
Gap                : 0.00 %

Solution

  • I am quite sure you don't need the self.model.eventexec(SCIP_EVENTTYPE.BESTSOLFOUND, self) line in your exec callback. You just put the code you want to run there