It might be the case that you want to interrupt Simplex based on a stopping criterion (e.g., a maximum IterationLimit) and retrieve the latest basis information. This is currently not possible in Gurobi - as long as Presolve is on.
The mathematical background is that during Presolve, your original model is being transformed into an equivalent presolved model that is conceivably smaller and easier to solve. The solver keeps track of a so-called "uncrush" mapping, to be able to map solutions of the presolved model back to solutions of the original model. Please, see How does presolve work? for more details.
This uncrushing step can only take place after Simplex terminates and the optimization is complete.
While Presolve is on, it is still possible to instruct Gurobi to stop, as soon as an iterate gets primal feasible, and retrieve the last objective value in a callback. This would look as follows:
import gurobipy as gp
from gurobipy import GRB
def simplexcallback(model,where):
if where == GRB.Callback.SIMPLEX:
vio = model.cbGet(GRB.Callback.SPX_PRIMINF)
if vio < 1e-6:
print("Reached primal feasibility")
print(model.cbGet(GRB.Callback.SPX_OBJVAL))
model.terminate()
m = gp.read("myLP.lp")
m.params.Method=0
m.optimize(simplexcallback)
There is a couple of workarounds though that could be helpful when Simplex is prematurely stopped to retrieve the solution information:
- To turn off presolving, i.e., set
Presolve=0. Nevertheless, this could have a drastic negative impact on the performance. You might want to test it on your models and assess the impact on the performance. -
A potentially better workaround would be through two consecutive solves. First, you would solve the model according to your termination criteria, while leaving Presolve on as usual. Note that you can also use Ctrl+C to stop the first solve, in case you're using interactive Python. Then, after termination, set
Presolve=0andIterationLimit=0, and re-optimize. This allows for the retrieval of the latest basis without uncrushing. You could then query the solution X attribute. This would look as follows:import gurobipy as gp m = gp.read("myLP.lp") m.params.Method=0 #Specify your termination criteria, e.g., m.params.IterationLimit=MaxIter m.optimize() try: m.X print("*********** first solve: X available") except: print("*********** first solve: no X available") m.params.Presolve=0 m.params.IterationLimit=0 m.optimize() try: m.X print("*********** resumed solve: X available") for v in m.getVars(): print(f"{v.VarName} = {v.X:.6f}") except: print("*********** resumed solve: no X available")