|
| 1 | +Future Solver Interface Changes |
| 2 | +=============================== |
| 3 | + |
| 4 | +Pyomo offers interfaces into multiple solvers, both commercial and open source. |
| 5 | +To support better capabilities for solver interfaces, the Pyomo team is actively |
| 6 | +redesigning the existing interfaces to make them more maintainable and intuitive |
| 7 | +for use. Redesigned interfaces can be found in ``pyomo.contrib.solver``. |
| 8 | + |
| 9 | +.. currentmodule:: pyomo.contrib.solver |
| 10 | + |
| 11 | + |
| 12 | +New Interface Usage |
| 13 | +------------------- |
| 14 | + |
| 15 | +The new interfaces have two modes: backwards compatible and future capability. |
| 16 | +The future capability mode can be accessed directly or by switching the default |
| 17 | +``SolverFactory`` version (see :doc:`future`). Currently, the new versions |
| 18 | +available are: |
| 19 | + |
| 20 | +.. list-table:: Available Redesigned Solvers |
| 21 | + :widths: 25 25 25 |
| 22 | + :header-rows: 1 |
| 23 | + |
| 24 | + * - Solver |
| 25 | + - ``SolverFactory`` (v1) Name |
| 26 | + - ``SolverFactory`` (v3) Name |
| 27 | + * - ipopt |
| 28 | + - ``ipopt_v2`` |
| 29 | + - ``ipopt`` |
| 30 | + * - Gurobi |
| 31 | + - ``gurobi_v2`` |
| 32 | + - ``gurobi`` |
| 33 | + |
| 34 | +Backwards Compatible Mode |
| 35 | +^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 36 | + |
| 37 | +.. testcode:: |
| 38 | + :skipif: not ipopt_available |
| 39 | + |
| 40 | + import pyomo.environ as pyo |
| 41 | + from pyomo.contrib.solver.util import assert_optimal_termination |
| 42 | + |
| 43 | + model = pyo.ConcreteModel() |
| 44 | + model.x = pyo.Var(initialize=1.5) |
| 45 | + model.y = pyo.Var(initialize=1.5) |
| 46 | + |
| 47 | + def rosenbrock(model): |
| 48 | + return (1.0 - model.x) ** 2 + 100.0 * (model.y - model.x**2) ** 2 |
| 49 | + |
| 50 | + model.obj = pyo.Objective(rule=rosenbrock, sense=pyo.minimize) |
| 51 | + |
| 52 | + status = pyo.SolverFactory('ipopt_v2').solve(model) |
| 53 | + assert_optimal_termination(status) |
| 54 | + model.pprint() |
| 55 | + |
| 56 | +.. testoutput:: |
| 57 | + :skipif: not ipopt_available |
| 58 | + :hide: |
| 59 | + |
| 60 | + 2 Var Declarations |
| 61 | + ... |
| 62 | + 3 Declarations: x y obj |
| 63 | + |
| 64 | +Future Capability Mode |
| 65 | +^^^^^^^^^^^^^^^^^^^^^^ |
| 66 | + |
| 67 | +There are multiple ways to utilize the future capability mode: direct import |
| 68 | +or changed ``SolverFactory`` version. |
| 69 | + |
| 70 | +.. testcode:: |
| 71 | + :skipif: not ipopt_available |
| 72 | + |
| 73 | + # Direct import |
| 74 | + import pyomo.environ as pyo |
| 75 | + from pyomo.contrib.solver.util import assert_optimal_termination |
| 76 | + from pyomo.contrib.solver.ipopt import Ipopt |
| 77 | + |
| 78 | + model = pyo.ConcreteModel() |
| 79 | + model.x = pyo.Var(initialize=1.5) |
| 80 | + model.y = pyo.Var(initialize=1.5) |
| 81 | + |
| 82 | + def rosenbrock(model): |
| 83 | + return (1.0 - model.x) ** 2 + 100.0 * (model.y - model.x**2) ** 2 |
| 84 | + |
| 85 | + model.obj = pyo.Objective(rule=rosenbrock, sense=pyo.minimize) |
| 86 | + |
| 87 | + opt = Ipopt() |
| 88 | + status = opt.solve(model) |
| 89 | + assert_optimal_termination(status) |
| 90 | + # Displays important results information; only available in future capability mode |
| 91 | + status.display() |
| 92 | + model.pprint() |
| 93 | + |
| 94 | +.. testoutput:: |
| 95 | + :skipif: not ipopt_available |
| 96 | + :hide: |
| 97 | + |
| 98 | + solution_loader: ... |
| 99 | + ... |
| 100 | + 3 Declarations: x y obj |
| 101 | + |
| 102 | +Changing the ``SolverFactory`` version: |
| 103 | + |
| 104 | +.. testcode:: |
| 105 | + :skipif: not ipopt_available |
| 106 | + |
| 107 | + # Change SolverFactory version |
| 108 | + import pyomo.environ as pyo |
| 109 | + from pyomo.contrib.solver.util import assert_optimal_termination |
| 110 | + from pyomo.__future__ import solver_factory_v3 |
| 111 | + |
| 112 | + model = pyo.ConcreteModel() |
| 113 | + model.x = pyo.Var(initialize=1.5) |
| 114 | + model.y = pyo.Var(initialize=1.5) |
| 115 | + |
| 116 | + def rosenbrock(model): |
| 117 | + return (1.0 - model.x) ** 2 + 100.0 * (model.y - model.x**2) ** 2 |
| 118 | + |
| 119 | + model.obj = pyo.Objective(rule=rosenbrock, sense=pyo.minimize) |
| 120 | + |
| 121 | + status = pyo.SolverFactory('ipopt').solve(model) |
| 122 | + assert_optimal_termination(status) |
| 123 | + # Displays important results information; only available in future capability mode |
| 124 | + status.display() |
| 125 | + model.pprint() |
| 126 | + |
| 127 | +.. testoutput:: |
| 128 | + :skipif: not ipopt_available |
| 129 | + :hide: |
| 130 | + |
| 131 | + solution_loader: ... |
| 132 | + ... |
| 133 | + 3 Declarations: x y obj |
| 134 | + |
| 135 | +.. testcode:: |
| 136 | + :skipif: not ipopt_available |
| 137 | + :hide: |
| 138 | + |
| 139 | + from pyomo.__future__ import solver_factory_v1 |
| 140 | + |
| 141 | +Linear Presolve and Scaling |
| 142 | +^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 143 | + |
| 144 | +The new interface will allow for direct manipulation of linear presolve and scaling |
| 145 | +options for certain solvers. Currently, these options are only available for |
| 146 | +``ipopt``. |
| 147 | + |
| 148 | +.. autoclass:: pyomo.contrib.solver.ipopt.Ipopt |
| 149 | + :members: solve |
| 150 | + |
| 151 | +The ``writer_config`` configuration option can be used to manipulate presolve |
| 152 | +and scaling options: |
| 153 | + |
| 154 | +.. testcode:: |
| 155 | + |
| 156 | + from pyomo.contrib.solver.ipopt import Ipopt |
| 157 | + opt = Ipopt() |
| 158 | + opt.config.writer_config.display() |
| 159 | + |
| 160 | +.. testoutput:: |
| 161 | + |
| 162 | + show_section_timing: false |
| 163 | + skip_trivial_constraints: true |
| 164 | + file_determinism: FileDeterminism.ORDERED |
| 165 | + symbolic_solver_labels: false |
| 166 | + scale_model: true |
| 167 | + export_nonlinear_variables: None |
| 168 | + row_order: None |
| 169 | + column_order: None |
| 170 | + export_defined_variables: true |
| 171 | + linear_presolve: true |
| 172 | + |
| 173 | +Note that, by default, both ``linear_presolve`` and ``scale_model`` are enabled. |
| 174 | +Users can manipulate ``linear_presolve`` and ``scale_model`` to their preferred |
| 175 | +states by changing their values. |
| 176 | + |
| 177 | +.. code-block:: python |
| 178 | +
|
| 179 | + >>> opt.config.writer_config.linear_presolve = False |
| 180 | +
|
| 181 | +
|
| 182 | +Interface Implementation |
| 183 | +------------------------ |
| 184 | + |
| 185 | +All new interfaces should be built upon one of two classes (currently): |
| 186 | +:class:`SolverBase<pyomo.contrib.solver.base.SolverBase>` or |
| 187 | +:class:`PersistentSolverBase<pyomo.contrib.solver.base.PersistentSolverBase>`. |
| 188 | + |
| 189 | +All solvers should have the following: |
| 190 | + |
| 191 | +.. autoclass:: pyomo.contrib.solver.base.SolverBase |
| 192 | + :members: |
| 193 | + |
| 194 | +Persistent solvers include additional members as well as other configuration options: |
| 195 | + |
| 196 | +.. autoclass:: pyomo.contrib.solver.base.PersistentSolverBase |
| 197 | + :show-inheritance: |
| 198 | + :members: |
| 199 | + |
| 200 | +Results |
| 201 | +------- |
| 202 | + |
| 203 | +Every solver, at the end of a |
| 204 | +:meth:`solve<pyomo.contrib.solver.base.SolverBase.solve>` call, will |
| 205 | +return a :class:`Results<pyomo.contrib.solver.results.Results>` |
| 206 | +object. This object is a :py:class:`pyomo.common.config.ConfigDict`, |
| 207 | +which can be manipulated similar to a standard ``dict`` in Python. |
| 208 | + |
| 209 | +.. autoclass:: pyomo.contrib.solver.results.Results |
| 210 | + :show-inheritance: |
| 211 | + :members: |
| 212 | + :undoc-members: |
| 213 | + |
| 214 | + |
| 215 | +Termination Conditions |
| 216 | +^^^^^^^^^^^^^^^^^^^^^^ |
| 217 | + |
| 218 | +Pyomo offers a standard set of termination conditions to map to solver |
| 219 | +returns. The intent of |
| 220 | +:class:`TerminationCondition<pyomo.contrib.solver.results.TerminationCondition>` |
| 221 | +is to notify the user of why the solver exited. The user is expected |
| 222 | +to inspect the :class:`Results<pyomo.contrib.solver.results.Results>` |
| 223 | +object or any returned solver messages or logs for more information. |
| 224 | + |
| 225 | +.. autoclass:: pyomo.contrib.solver.results.TerminationCondition |
| 226 | + :show-inheritance: |
| 227 | + |
| 228 | + |
| 229 | +Solution Status |
| 230 | +^^^^^^^^^^^^^^^ |
| 231 | + |
| 232 | +Pyomo offers a standard set of solution statuses to map to solver |
| 233 | +output. The intent of |
| 234 | +:class:`SolutionStatus<pyomo.contrib.solver.results.SolutionStatus>` |
| 235 | +is to notify the user of what the solver returned at a high level. The |
| 236 | +user is expected to inspect the |
| 237 | +:class:`Results<pyomo.contrib.solver.results.Results>` object or any |
| 238 | +returned solver messages or logs for more information. |
| 239 | + |
| 240 | +.. autoclass:: pyomo.contrib.solver.results.SolutionStatus |
| 241 | + :show-inheritance: |
| 242 | + |
| 243 | + |
| 244 | +Solution |
| 245 | +-------- |
| 246 | + |
| 247 | +Solutions can be loaded back into a model using a ``SolutionLoader``. A specific |
| 248 | +loader should be written for each unique case. Several have already been |
| 249 | +implemented. For example, for ``ipopt``: |
| 250 | + |
| 251 | +.. autoclass:: pyomo.contrib.solver.ipopt.IpoptSolutionLoader |
| 252 | + :show-inheritance: |
| 253 | + :members: |
| 254 | + :inherited-members: |
0 commit comments