• Community Moderator

Hi Ana,

technically applying your heuristic and function could be done from within a callback every time you get a feasible solution. But since the solver has no clue about the other function which you are trying to minimize, you would need to store your measures of quality someplace and then implement a routine to pick the best solution. Gurobi would have no information as to your measure of the quality of the solution, so - in this context - it would not be possible to have the solver tell you which of the solutions is the best.

But this would not be that much different to what you do now - instead of assessing all the solutions at once, you would assess them "on-the-fly". Perhaps this is useful though - maybe you could terminate early as soon as you find a solution which is "good enough"?

Alternatively, you could attempt to "include" the "heuristic" and the "function" in the model formulation. Then, the solver knows which solutions are "good" and which are "bad", and will definitely not return solutions which would be discarded by the "heuristic".

If you need a code example for the first option - let me know.

Hope this helps.

Best regards
Jonasz

Many thanks, Jonasz, that is what I thought and the reason to ask here. Yes, please, send me a code example for the first option, only to know if I can improve something or, as you said, maybe with a good enough solution is enough for us.

The next step will be to formulate the overall problem, but we think that it considerably will improve the complexity (and the solution time for sure)

I look forward to receiving the code example.

Best,

Ana

• Community Moderator

A trivial code example would be:

import gurobipy as gp
def mycallback(model, where):    if where == GRB.Callback.MIPSOL        solution = model.cbGetSolution(model._vars)        model._solutions[model._solution_count] = solution        # perform your heuristic and quality check. I assume it's stored in variable quality'        model._solutions_quality[model._solution_count] = quality        # You can also check here if the solution is good enough.        # I don't know how you measure quality, so I leave this out.         # If you are happy with the quality you get, you can use model.terminate() to terminate.        model._solution_count += 1m = gp.Model()# define your model herem._solution_count = 0m._solutions = {}m._solutions_quality = {}m._vars = m.getVars()# ...m.optimize(mycallback)

Hope this makes sense. I have no way of testing this code now, so please treat it as rough guidance.

Best regards
Jonasz

Thanks, Jonasz!

I have a doubt. The value of solution on the above code ( or print(model.cbGetSolution(model._vars))) is always an empty list. Could it be because I have no objective function to optimize? How should I proceed in this case?

Many thanks again!

If I write an objective function, the behaviour is still the same. I write a simplified version of the code. In my case, there may be hundreds of solutions with the same objective function so I want to use the heuristic inside the callback to select the best one. I don't know where the error is and why I get an empty list, so I can not treat the solution.

def mycallback(model, where):

if where == GRB.Callback.MIPSOL:
print(model.cbGetSolution(model._vars))
solution = model.cbGetSolution(model._vars)
model._solutions[model._solution_count] = solution

# perform your heuristic and quality check. I assume it's stored in variable quality'
# As solution is an empty list, I comment the following lines

#model._solutions_quality[model._solution_count] = quality
# You can also check here if the solution is good enough.
# I don't know how you measure quality, so I leave this out.
# If you are happy with the quality you get, you can use model.terminate() to terminate.
model._solution_count += 1

m = Model() # Gurobi model

# Parameters

# Gurobi variables

# constraints

...

# objective function
m.setObjective( quicksum(P[i,j] for i in I for j in I), GRB.MAXIMIZE )

#With or without the following 2 lines, the result is the same

m.setParam('PoolSearchMode',2)
m.setParam('PoolSolutions',10) #

m._solution_count = 0
m._solutions = {}
m._solutions_quality = {}
m._vars = m.getVars()

m.optimize(mycallback)

This is the output:

Set parameter Threads to value 1
Set parameter TimeLimit to value 500
Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (linux64)
Thread count: 6 physical cores, 6 logical processors, using up to 1 threads
Optimize a model with 199 rows, 81 columns and 493 nonzeros
Model fingerprint: 0x665c8cbc
Variable types: 0 continuous, 81 integer (81 binary)
Coefficient statistics:
Matrix range     [1e+00, 1e+00]
Objective range  [1e+00, 1e+00]
Bounds range     [1e+00, 1e+00]
RHS range        [1e+00, 1e+01]
[ ]  (this is the result of  print(model.cbGetSolution(model._vars)))
Found heuristic solution: objective 16.0000000
Presolve removed 179 rows and 71 columns
Presolve time: 0.00s
Presolved: 20 rows, 10 columns, 60 nonzeros
Variable types: 0 continuous, 10 integer (10 binary)

Explored 0 nodes (0 simplex iterations) in 0.00 seconds (0.00 work units)
Thread count was 1 (of 6 available processors)

Solution count 1: 16

Optimal solution found (tolerance 1.00e-04)
Best objective 1.600000000000e+01, best bound 1.600000000000e+01, gap 0.0000%

User-callback calls 209, time in user-callback 0.00 sec