Skip to content

Commit 70b2803

Browse files
authored
Merge pull request #3137 from mrmundt/solver-refactor
Solver Refactor Part 1: Introducing the new solver interface
2 parents 8d599f5 + 9b21273 commit 70b2803

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+9211
-25
lines changed

.github/workflows/test_branches.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ jobs:
7575
python: 3.9
7676
TARGET: win
7777
PYENV: conda
78-
PACKAGES: glpk pytest-qt
78+
PACKAGES: glpk pytest-qt filelock
7979

8080
- os: ubuntu-latest
8181
python: '3.11'

.github/workflows/test_pr_and_main.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ jobs:
7676
- os: windows-latest
7777
TARGET: win
7878
PYENV: conda
79-
PACKAGES: glpk pytest-qt
79+
PACKAGES: glpk pytest-qt filelock
8080

8181
- os: ubuntu-latest
8282
python: '3.11'

doc/OnlineDocs/conf.py

+1
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
'sphinx.ext.doctest',
8484
'sphinx.ext.todo',
8585
'sphinx_copybutton',
86+
'enum_tools.autoenum',
8687
#'sphinx.ext.githubpages',
8788
]
8889

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
.. automodule:: pyomo.__future__
3+
:noindex:

doc/OnlineDocs/developer_reference/index.rst

+2
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,5 @@ scripts using Pyomo.
1212
config.rst
1313
deprecation.rst
1414
expressions/index.rst
15+
future.rst
16+
solvers.rst
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
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

Comments
 (0)