Solver Recipes

Accessing Solver Status

After a solve, the results object has a member Solution.Status that contains the solver status. The following snippet shows an example of access via a print statement:

results = opt.solve(instance)
#print ("The solver returned a status of:"+str(results.solver.status))

The use of the Python str function to cast the value to a be string makes it easy to test it. In particular, the value ‘optimal’ indicates that the solver succeeded. It is also possible to access Pyomo data that can be compared with the solver status as in the following code snippet:

from pyomo.opt import SolverStatus, TerminationCondition

#...

if (results.solver.status == SolverStatus.ok) and (results.solver.termination_condition == TerminationCondition.optimal):
     print ("this is feasible and optimal")
elif results.solver.termination_condition == TerminationCondition.infeasible:
     print ("do something about it? or exit?")
else:
     # something else is wrong
     print (str(results.solver))

Alternatively,

from pyomo.opt import TerminationCondition

...

results = opt.solve(model, load_solutions=False)
if results.solver.termination_condition == TerminationCondition.optimal:
    model.solutions.load_from(results)
else:
    print ("Solution is not optimal")
    # now do something about it? or exit? ...

Display of Solver Output

To see the output of the solver, use the option tee=True as in

results = opt.solve(instance, tee=True)

This can be useful for troubleshooting solver difficulties.

Sending Options to the Solver

Most solvers accept options and Pyomo can pass options through to a solver. In scripts or callbacks, the options can be attached to the solver object by adding to its options dictionary as illustrated by this snippet:

optimizer = pyo.SolverFactory['cbc']
optimizer.options["threads"] = 4

If multiple options are needed, then multiple dictionary entries should be added.

Sometimes it is desirable to pass options as part of the call to the solve function as in this snippet:

results = optimizer.solve(instance, options={'threads' : 4}, tee=True)

The quoted string is passed directly to the solver. If multiple options need to be passed to the solver in this way, they should be separated by a space within the quoted string. Notice that tee is a Pyomo option and is solver-independent, while the string argument to options is passed to the solver without very little processing by Pyomo. If the solver does not have a “threads” option, it will probably complain, but Pyomo will not.

There are no default values for options on a SolverFactory object. If you directly modify its options dictionary, as was done above, those options will persist across every call to optimizer.solve(…) unless you delete them from the options dictionary. You can also pass a dictionary of options into the opt.solve(…) method using the options keyword. Those options will only persist within that solve and temporarily override any matching options in the options dictionary on the solver object.

Specifying the Path to a Solver

Often, the executables for solvers are in the path; however, for situations where they are not, the SolverFactory function accepts the keyword executable, which you can use to set an absolute or relative path to a solver executable. E.g.,

opt = pyo.SolverFactory("ipopt", executable="../ipopt")

Warm Starts

Some solvers support a warm start based on current values of variables. To use this feature, set the values of variables in the instance and pass warmstart=True to the solve() method. E.g.,

instance = model.create()
instance.y[0] = 1
instance.y[1] = 0

opt = pyo.SolverFactory("cplex")

results = opt.solve(instance, warmstart=True)

Note

The Cplex and Gurobi LP file (and Python) interfaces will generate an MST file with the variable data and hand this off to the solver in addition to the LP file.

Warning

Solvers using the NL file interface (e.g., “gurobi_ampl”, “cplexamp”) do not accept warmstart as a keyword to the solve() method as the NL file format, by default, includes variable initialization data (drawn from the current value of all variables).

Solving Multiple Instances in Parallel

Building and solving Pyomo models in parallel is a common requirement for many applications. We recommend using MPI for Python (mpi4py) for this purpose. For more information on mpi4py, see the mpi4py documentation (https://mpi4py.readthedocs.io/en/stable/). The example below demonstrates how to use mpi4py to solve two pyomo models in parallel. The example can be run with the following command:

mpirun -np 2 python -m mpi4py parallel.py
#  ___________________________________________________________________________
#
#  Pyomo: Python Optimization Modeling Objects
#  Copyright (c) 2008-2024
#  National Technology and Engineering Solutions of Sandia, LLC
#  Under the terms of Contract DE-NA0003525 with National Technology and
#  Engineering Solutions of Sandia, LLC, the U.S. Government retains certain
#  rights in this software.
#  This software is distributed under the 3-clause BSD License.
#  ___________________________________________________________________________

# parallel.py
# run with mpirun -np 2 python -m mpi4py parallel.py
import pyomo.environ as pyo
from mpi4py import MPI

rank = MPI.COMM_WORLD.Get_rank()
size = MPI.COMM_WORLD.Get_size()
assert (
    size == 2
), 'This example only works with 2 processes; please us mpirun -np 2 python -m mpi4py parallel.py'

# Create a solver
opt = pyo.SolverFactory('cplex_direct')

#
# A simple model with binary variables
#
model = pyo.ConcreteModel()
model.n = pyo.Param(initialize=4)
model.x = pyo.Var(pyo.RangeSet(model.n), within=pyo.Binary)
model.obj = pyo.Objective(expr=sum(model.x.values()))

if rank == 1:
    model.x[1].fix(1)

results = opt.solve(model)
print('rank: ', rank, '    objective: ', pyo.value(model.obj.expr))

Changing the temporary directory

A “temporary” directory is used for many intermediate files. Normally, the name of the directory for temporary files is provided by the operating system, but the user can specify their own directory name. The pyomo command-line --tempdir option propagates through to the TempFileManager service. One can accomplish the same through the following few lines of code in a script:

from pyomo.common.tempfiles import TempfileManager
TempfileManager.tempdir = YourDirectoryNameGoesHere