Getting a first integer feasible point with callbacks
OngoingHey,
I am trying to get a first integer feasible point fast (or to be more precise, its objective value) and then terminate the optimization process and thought about doing that via a callback.
Unfortunatly the compiler tells me the following:
gurobipy.gurobipy.GurobiError: Callback argument must be a function
Here is my callback and how I start the optimization:
def cb(self, model, where):
if where == GRB.Callback.MIPSOL:
self.ub = model.cbGet(GRB.Callback.MIPSOL_OBJ)
model.terminate()
self.m.optimize(self.cb)
self.m is the MILP model I am working with (in a class based setting). Any suggestions, why the callback function is not recognized as a function?
Best,
Lukas
-
Official comment
This post is more than three years old. Some information may not be up to date. For current information, please check the Gurobi Documentation or Knowledge Base. If you need more help, please create a new post in the community forum. Or why not try our AI Gurobot?. -
Hi Lukas,
An easier approach might be to use the SolutionLimit parameter as a termination criterion. By setting the SolutionLimit parameter to 1, the optimization would terminate as soon as one feasible solution is found.
To answer your question about the callback, the error occurs because the
self.cb
is not a function. It is an instance method of an object. There is a difference between methods and functions in Python. A method is invoked by an object and it cannot exist without an object calling to it. However, a function is independent and is invoked by its name.There are two options to fix this:
- The first option is to make the
self.cb
method a static method as shown below:
class Method:
@staticmethod
def cb(model, where):
if where == GRB.Callback.MIPSOL:
model._ub = model.cbGet(GRB.Callback.MIPSOL_OBJ)
model.terminate()- The second option is to define the callback as a function outside the class and pass it as an argument to the method calling the
optimize()
method.
To pass data to the callback function, you should do it through the Model object. You should define the statement
model._ub = float("inf")
before the optimization begins, then your callback function can query the value ofmodel._ub
. Note that the name of the user data field must begin with an underscore.You might find the snippet below useful as a minimum working example of what you are interested in.
import gurobipy
from gurobipy import GRB
class Model:
def__init__(self):
self.model = gurobipy.read("examples/data/glass4.mps")
# Initialize the upper bound
self.model._ub = float("inf")
@staticmethod
def cb(model, where):
if where == GRB.Callback.MIPSOL:
model._ub = model.cbGet(GRB.Callback.MIPSOL_OBJ)
model.terminate()
def get_first_solution(self):
self.model.optimize(Model.cb)
print(f"ub: {self.model._ub}")
if __name__ == "__main__":
model = Model()
model.get_first_solution()Best regards,
Maliheh
0 - The first option is to make the
-
Hi Maliheh,
thank you for the fast answer, the solution approach with the SolutionLimit set to 1 is perfectly feasible for my use case. Also thank you for explaining the problems with callbacks!
Best,Lukas
0 -
Is the following way of using the nested function a reasonable way to do this? I have a lot of instance attributes that I need to pass to the callback.
I am curious if there is any latency that might arise if callback accesses instance data (e.g., large data matrix) from a nested function.
import gurobipy
from gurobipy import GRB
class Model:
def __init__(self):
self.model = gurobipy.read("examples/data/glass4.mps")
# Initialize the upper bound
# self.model._ub = float("inf")
self.ub=float("inf)
def get_first_solution(self):
def cb(model, where):
if where == GRB.Callback.MIPSOL:
self.ub = model.cbGet(GRB.Callback.MIPSOL_OBJ) # note the use of self here
model.terminate()
self.model.optimize(cb)
print(f"ub: {self.ub}")0
Post is closed for comments.
Comments
4 comments