Skip to main content

Writing solution pool to .sol file

Answered

Comments

19 comments

  • Official comment
    Simranjit Kaur
    Gurobi Staff Gurobi Staff
    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?.
  • Eli Towle
    Gurobi Staff Gurobi Staff

    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
  • Sylvia CHEN
    Gurobi-versary
    Conversationalist

    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
  • Eli Towle
    Gurobi Staff Gurobi Staff

    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
  • Eli Towle
    Gurobi Staff Gurobi Staff

    You're right, you should use PoolObjVal instead.

    1
  • Sriram Sankaranarayanan
    Gurobi-versary
    First Question
    First Comment

    Yes, that's perfect! Thank you. 

    0
  • Sylvia CHEN
    Gurobi-versary
    Conversationalist

    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
  • Matthias Miltenberger
    Gurobi Staff Gurobi Staff

    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,
    Matthias

    0
  • Sylvia CHEN
    Gurobi-versary
    Conversationalist

    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
  • Sylvia CHEN
    Gurobi-versary
    Conversationalist

    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 = i
    xn = 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.3

     

    It prints the optimal solution only.

     

     

    0
  • Sylvia CHEN
    Gurobi-versary
    Conversationalist

    Hi Eli,

    Thank you! It works!

    0
  • Michela Ceria
    Gurobi-versary
    First Comment

    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
  • Eli Towle
    Gurobi Staff Gurobi Staff

    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
  • Michela Ceria
    Gurobi-versary
    First Comment

    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
  • Sylvia CHEN
    Gurobi-versary
    Conversationalist

    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
  • Eli Towle
    Gurobi Staff Gurobi Staff

    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
  • Michela Ceria
    Gurobi-versary
    First Comment

    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
  • Eli Towle
    Gurobi Staff Gurobi Staff
    1. 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.
    2. 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
  • Maliheh Aramon
    Gurobi Staff Gurobi Staff

    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

Post is closed for comments.