Reputation: 1694
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.
Upvotes: 12
Views: 18002
Reputation: 7928
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.
Upvotes: 0
Reputation: 460
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))
Upvotes: 0
Reputation: 1119
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)
Upvotes: 2
Reputation: 111
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.
Upvotes: 0
Reputation: 414235
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.
Upvotes: 6