Question

I have to try to move from the non-class-based coding style into class-based coding style, but facing an issue. The optimize() function takes a callback function mycallback(). The code works perfectly fine in Non-class-based method, but when I moved it to class-based method, I got an error "mycallback() takes exactly 3 arguments (1 given)".

What is the right way to pass a callback function in the class-based method?

(A) Non-class-based method:

def mycallback(model, where):
    pass

model = Model()
model.optimize(mycallback)

(B) Class-based method:

class A:
    def __init__(self):
        self.model = Model()

    def solve(self):
        # Try method 1:
        self.model.optimize(self.mycallback())      <--- Error: mycallback() takes exactly 3 arguments (1 given)
        # Try method 2:
        # self.model.optimize(self.mycallback)  <--- Error:  Callback argument must be a function

    def mycallback(self, model, where):     
        pass

While this is a problem regarding passing a callback function to Gurobi's (an optimization solver) built-in function, I believe it is a more general question on how to pass a callback function defined in a class to another function in Python.


Error For method 2:

   self.model.optimize(self.mycallback)  
   File "model.pxi", line 458, in gurobipy.Model.optimize      (../../src/python/gurobipy.c:34263)
   gurobipy.GurobiError: Callback argument must be a function

Looks like it is likely to be Gurobi API issue. Wonder if any Gurobi dev will response.

Was it helpful?

Solution

In general, self.model.optimize(self.mycallback) should work (note: no parens after mycallback).

It may fail if the code serializes the callable e.g., to be send via pipe/socket to another process (even on different machine):

from multiprocessing import Pool

class C:
    def method(self, i):
        return "called", i

if __name__=="__main__":
    print(Pool().map(C().method, range(10)))

It works on recent Python versions where methods are pickable.

Or it may fail if model.optimize() has a bug and check for the exact function type instead of accepting any callable.

OTHER TIPS

This issue still persists in gurobi 9.1. A simple workaround I found is to put the callback inside a method in your class, for instance:

def solve(self):
    self.model.update()
       def lazyCallback(model, where):
       ...
    self.model.optimize(lazyCallback)

It seems that if you remove the callback from the object, then it works. You can use this as a work-around until you can get a callback working in-class. That is, call this line from within the class...

def solve(self):
    self.model.optimize(mycallback)

...to this function outside of the class.

def mycallback(self, model, where):     
    pass

Not elegant at all, but hopefully a developer chimes in.

The problem with self.mycallback is that it is a method while Gurobi's optimize method really expects a function.

There are multiple ways to make self.mycallback a function. Here are some examples:

One approach would be making mycallback a static method:

@staticmethod
def mycallback(model, where): 
    pass

Another would be wrapping the method into a function as follows:

self.model.optimize(lambda model, where: self.mycallback(model, where))    

My two cents solution:


import queue as Queue
class A:
    def start(self):
       Queue.listen(callback=received)

    @staticmethod
    def received(*args):
       print(args)

Queue is defined in another module/file.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top