Writing solution pool to .sol file
AnsweredI have a model that returns multiple solutions. Is there a way I can write the suboptimal solutions to a file?
I was hoping that I could do something like the following:
for i in range(model.getAttr("SolCount")):
model.params.SolutionNumber = i
model.write('sol_'+str(i)+'.sol')
However, it only seems to save the optimal solution all the time. Is there a way to get around this?
-
Hi Sriram,
The easiest way to do this is probably with the SolFiles parameter, which directs Gurobi to automatically write new incumbent solutions to disk. Does that work for you?
Thanks,
Eli
2 -
Yes, that's perfect! Thank you.
0 -
Hi Eli,
Thank you. SolFiles works great.
I used SolFiles which generated 17 files, but in the code, I print SolCount which return 10 solutions. I don't know why they are different.
In my model, I set the default Gurobi Optimizer to find one optimal solution.
print(m.getAttr(GRB.Attr.SolCount))0 -
Hi Sylvia,
This is because Gurobi finds 17 feasible solutions along the way and they are written to disk immediately. The number of best solutions stored internally is 10, so the first 7 are overwritten by better ones during the solution process. It is often not helpful to keep many more solutions inside the solver, because in most cases, only the best one is relevant.
You can set how many solutions Gurobi is supposed to keep via the parameter PoolSolutions.
Cheers,
Matthias0 -
Hi Matthias,
Thank you for your reply.
I set the PoolSolutions to 9, and the SolFiles still generates 17 files. Is there a way to only save the best solutions of PoolSolutions to .sol file?
Cheers,
Sylvia
0 -
I have found a way to save .sol file from another answer. https://support.gurobi.com/hc/en-us/community/posts/360072723431-Get-all-solutions
I leave my code here,
pool_size = m.getAttr(GRB.Attr.SolCount)
for number in range(pool_size):
m.setParam(GRB.Param.SolutionNumber, number)
m.write('{}_{}.sol'.format(m.getAttr(GRB.Attr.ModelName),number))However, it only seems to save the optimal solution all the time. Is there a way to get around this?1 -
The Model.write() method only writes the current solution, so it does not write pool solutions based on the SolutionNumber parameter. We have this recorded as a feature request.
Your two options are:
- Use the SolFiles parameter. All solutions are written to disk as soon as they are found, so all of the pool solutions that you are interested in are written to disk. The only difference is that the pool solution files are (i) not numbered the way you would like, and (ii) solutions that were found earlier and are no longer in the pool are also written to disk. I wouldn't expect this to be a big problem, since you can easily identify the \( n \) pool solution files as those with the largest \( n \) numerical identifiers in the filename.
- You can write the pool solutions to disk yourself after the optimization is complete. An example is below, though keep in mind that files written manually may not capture the exact same precision as Gurobi's built-in write function/method.
gv = m.getVars()
names = m.getAttr('VarName', gv)
for i in range(m.SolCount):
m.params.SolutionNumber = i
xn = m.getAttr('Xn', gv)
lines = ["{} {}".format(v1, v2) for v1, v2 in zip(names, xn)]
with open('{}_{}.sol'.format(m.ModelName, i), 'w') as f:
f.write("# Solution for model {}\n".format(m.modelName))
f.write("# Objective value = {}\n".format(m.PoolObjVal))
f.write("\n".join(lines))Edit: changed ObjVal to PoolObjVal.
1 -
Hi Eli,
Thank you for your reply.
In the second option, the solution only returns the optimal solution.
gv = m.getVars()names = m.getAttr('VarName', gv)for i in range(m.SolCount):m.params.SolutionNumber = ixn = m.getAttr('Xn', gv)print(m.ObjVal)The results show as below.
Solution count 7: 12418.3 12429.5 12436.6 ... 12449.9
Optimal solution found (tolerance 0.00e+00)
Best objective 1.241830000000e+04, best bound 1.241830000000e+04, gap 0.0000%
12418.3
12418.3
12418.3
12418.3
12418.3
12418.3
12418.3It prints the optimal solution only.
0 -
You're right, you should use PoolObjVal instead.
1 -
Hi Eli,
Thank you! It works!
0 -
Hi,
I am new to Gurobi and I have a similar problem; may you please give me an example of use for SolFiles?
Does it work in gurobipy?Thanks!
0 -
Yes, it works in \( \texttt{gurobipy} \). You need to set the SolFiles parameter to the desired base name of the output solution files. For example, if you have a Model object \( \texttt{m} \), you can use:
m.Params.SolFiles = 'mymodel'
See the Python Parameter Examples section of the documentation for more details.
0 -
Hi,
thank you for your reply!
I tried with SolFiles, but again only one solution is printed, even if I know that the problem should have more than one solution.
How can I tell Gurobi to print into a file all the possible solutions?Thanks!
0 -
Hi Michela,
I tried with my model, it prints all the possible solutions. The code I used is
m.setParam("PoolSolutions",10)
m.setParam("SolFiles", "mySLM1")It writes "mySLM1_n.sol" for all solutions.
The size of the PoolSolution has no impact. I tried with sizes 2 and 10, both of them write all solutions.
0 -
When you set the SolFiles parameter, Gurobi only writes solution files for incumbent solutions it encounters during the optimization process. If the first solution Gurobi finds is optimal, it won't encounter any more incumbent solutions, and thus won't write any additional solution files to disk.
The PoolSearchMode and PoolSolutions parameters control how Gurobi approaches its search for suboptimal solutions. For example, setting PoolSearchMode=2 and PoolSolutions=3 will direct Gurobi to search for the three best solutions. Instead of creating many solution files by setting the SolFiles parameter, you could iterate over the solutions programmatically using the SolutionNumber parameter.
0 -
Dear all,
again problems with printing all solutions.My problem is: given a matrix A composed of zeros and ones, find all possible vectors x composed of zeros and ones such that their sum is a given value (in the case I am posting, 10) and such that A*x= a vector composed of all ones.Below you can find my code using gurobipy.Two bad things happen:1) only 10 solutions are posted, while I know they should be 36;2) sometimes non-integer values are displayed in the solution.What am I doing wrong?Thanks for your help.//==//A=np.array([ [1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0],[0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,0],[0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],[0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0],[1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0],[0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0],[1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0],[0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0],[0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0],[0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0],[0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,0],[0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0],[0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],[0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0],[0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0],[0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0],[1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0],[0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,1,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0],[0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0],[0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],[0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0],[0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0],[0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0]])j=np.array([ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ])m = gp.Model()v = m.addMVar(40, lb=0, ub=1, vtype=GRB.INTEGER, name ="v")m.update()m.addConstr(v@j == 10)m.addConstr(A@v == j)m.update()m.setParam("PoolSearchMode",2)m.setParam("SolFiles", "Solutions")m.optimize()
0 -
- The number of MIP solutions stored by Gurobi depends on the PoolSolutions parameter. The default value is 10. To direct Gurobi to retain more than 10 solutions, you can increase the value of this parameter. When using the SolFiles parameter, Gurobi writes the intermediate solutions to disk as soon as it finds them, independent of the PoolSolutions parameter.
- It's normal for Gurobi and other solvers to sometimes return slightly non-integral solution values for integer variables. You can read more about this in the article Why does Gurobi sometimes return non-integral values for integer variables?.
0 -
Hi Everyone,
Gurobi 9.5 was recently released. Included in this release is a modification to the Model.write() method. This method can now write the pool solutions selected by the SolutionNumber parameter into files.
We hope this new modification would make it easier to write pool solutions into files. Please let us know if you find any issues using this.
Best regards,
Maliheh
0
Please sign in to leave a comment.
Comments
18 comments