About cbSetSolution() and cbUseSolution()
AnsweredDear all,
While using Python, I'm encountering a "weird" behaviour.
In my code, everytime a MIP solution is found (MIPSOL) I check whether it is feasible. In case it isn't I can, heuristically, make it feasible and since Gurobi doesn't know about the "feasibilized" variable values, I then apply cbSetSolution() using the feasibilized values on ALL variables, in order to provide a complete feasible solution.
Since I want to use this solution a soon as possible and not whenever Gurobi is available, when the callback is at MIPNODE and there is a stored MIP solution (this is a True/False condition that gets updated everytime I update/find a MIP soln) I call cbUseSolution().
But, to my surprise, I'm getting the warning message: "Warning: Completing partial solution with 1500 unfixed non-continuous variables out of 1600". The numbers are the least important, what intrigues me is that I'm getting this warning even though I'm explicitly fixing the values for all variables, because of this the Gurobi's completed solution won't be always feasible, something that is displayed in the log, where the Incumbent value isn't updated.
The following log is from a very small instance, I'm checking each time I get an MIP soln., when the "feasibilized" soln. provides a better incumbent to the current one and saves it and finally, when that saved soln. is applied.
Gurobi Optimizer version 10.0.1 build v10.0.1rc0 (win64)
CPU model: AMD Ryzen 7 5700G with Radeon Graphics, instruction set [SSE2|AVX|AVX2]
Thread count: 8 physical cores, 16 logical processors, using up to 1 threads
Optimize a model with 67665 rows, 3475 columns and 885213 nonzeros
Model fingerprint: 0xb4363ff3
Variable types: 1875 continuous, 1600 integer (1600 binary)
Coefficient statistics:
Matrix range [1e+00, 2e+05]
Objective range [2e+01, 1e+08]
Bounds range [1e+00, 1e+00]
RHS range [1e+00, 2e+05]
Variable types: 1875 continuous, 1600 integer (1600 binary)
Root relaxation: objective 6.197290e+08, 4733 iterations, 1.88 seconds (11.04 work units)
Nodes | Current Node | Objective Bounds | Work
Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time
0 0 6.1973e+08 0 176 - 6.1973e+08 - - 3s
0 0 6.2056e+08 0 222 - 6.2056e+08 - - 3s
0 2 6.3707e+08 0 216 - 6.3707e+08 - - 5s
Number of integer solutions found: 1.0
Saved Feasibilized Solution on MIPSOL of node 9.0
Warning: Completing partial solution with 1500 unfixed non-continuous variables out of 1600
Used feasibilized solution on node 9.0
Number of integer solutions found: 2.0
9 11 6.4914e+08 5 83 - 6.4561e+08 - 432 15s
Number of integer solutions found: 3.0
11 13 6.5127e+08 7 106 - 6.4561e+08 - 380 20s
Number of integer solutions found: 4.0
Saved Feasibilized Solution on MIPSOL of node 12.0
Number of integer solutions found: 5.0
Saved Feasibilized Solution on MIPSOL of node 12.0
Warning: Completing partial solution with 1500 unfixed non-continuous variables out of 1600
Warning: Completing partial solution with 1500 unfixed non-continuous variables out of 1600
Used feasibilized solution on node 12.0
.
.
.
* 31 29 13 7.055488e+08 6.4568e+08 8.49% 311 68s --> This is the first Incumbent found and it's obtained by branching
38 32 6.8362e+08 4 201 7.0555e+08 6.5014e+08 7.85% 305 70s
.
.
.
Number of integer solutions found: 91.0
* 134 0 12 6.647996e+08 6.6480e+08 0.00% 270 329s
Cutting planes:
User: 6
Lazy constraints: 218
Explored 135 nodes (41913 simplex iterations) in 329.91 seconds (189.16 work units)
Thread count was 1 (of 16 available processors)
Solution count 2: 6.648e+08 7.05549e+08
Optimal solution found (tolerance 1.00e-04)
Best objective 6.647996099277e+08, best bound 6.647996099277e+08, gap 0.0000%
User-callback calls 1746, time in user-callback 290.03 sec
My callback code is structured something like this (major functionality have been omitted!!)
def Callback(model, where):
if where == GRB.Callback.MIPSOL:
# Run algorithm to "feasibilize" the current MIP soln
if feas_objval <= model.cbGet(GRB.Callback.MIPSOL_OBJBST)- model.Params.IntFeasTol:
for var in model.getVars():
model.cbSetSolution(var, feas_varVal[var])
model._usesol = True
elif where == GRB.Callback.MIPNODE:
if model._usesol:
model._sol = model.cbUseSolution()
model._usesol = False
Something that worked previously was to save these values to a created model parameter and then calling everything at the MIPNODE but I feel like that was a bandaid solution since, for larger instances, the incumbent didn't update when it should had, and the behaviour it presented is the same as the one I'm encountering here.
The parameters for solving the model are:
Set parameter FeasibilityTol to value 0.0001
Set parameter IntFeasTol to value 0.0001
Set parameter Heuristics to value 0
Set parameter Cuts to value 0
Set parameter CliqueCuts to value 0
Set parameter CoverCuts to value 0
Set parameter FlowCoverCuts to value 0
Set parameter FlowPathCuts to value 0
Set parameter GomoryPasses to value 0
Set parameter ImpliedCuts to value 0
Set parameter ProjImpliedCuts to value 0
Set parameter MIRCuts to value 0
Set parameter StrongCGCuts to value 0
Set parameter ZeroHalfCuts to value 0
Set parameter ModKCuts to value 0
Set parameter RLTCuts to value 0
Set parameter RelaxLiftCuts to value 0
Set parameter BQPCuts to value 0
Set parameter LiftProjectCuts to value 0
Set parameter PreCrush to value 1
Set parameter Presolve to value 0
Set parameter Seed to value 10
Set parameter Threads to value 1
Set parameter LazyConstraints to value 1
I appreciate any insights and comments!
Thank you very much,
Nicolás
-
Hi Nicolás,
I think there is some misunderstanding w.r.t. cbSetSolution() and cbUseSolution():
Since I want to use this solution a soon as possible and not whenever Gurobi is available, when the callback is at MIPNODE and there is a stored MIP solution (this is a True/False condition that gets updated everytime I update/find a MIP soln) I call cbUseSolution().
This is not necessary and does not work. If you find a new solution during the MIPSOL callback and set the variable values, Gurobi will try to compute a feasible solution from the specified values after the callback, see the documentation of Model.cbSetSolution()
After the callback, if values have been specified for any variables, the Gurobi optimizer will try to compute a feasible solution from the specified values, possibly filling in values for variables whose values were left undefined. You can also optionally call cbUseSolution within your callback function to try to immediately compute a feasible solution from the specified values.
The last sentence about optionally calling cbUseSolution is especially for the callback MIPNODE because solutions set during MIPNODE callback can immediately be processed. For MIP and MIPSOL the solution cannot be processed immediately but after the callback (and before a succeeding MIPNODE callback).
This can also be seen in the log
Saved Feasibilized Solution on MIPSOL of node 9.0 Warning: Completing partial solution with 1500 unfixed non-continuous variables out of 1600
Used feasibilized solution on node 9.0You set the solution values in the MIPNODE callback and then Gurobi tries to compute a feasible solution. The use-solution call in the MIPNODE callback does not do anything.
Cheers,
Marika0 -
Dear Marika:
I understand now! I thought that having the cbSetSolution() at MIPSOL and then calling cbUseSolution() at MIPNODE would use the stored solution.
Thank you very much for the clarification!
0
Please sign in to leave a comment.
Comments
2 comments