Skip to content

Commit a0765b4

Browse files
authored
Merge pull request #3160 from smartin71/main
New Parmest UI
2 parents 662c843 + 7f27d2f commit a0765b4

30 files changed

+3623
-433
lines changed

doc/OnlineDocs/contributed_packages/parmest/datarec.rst

+26-30
Original file line numberDiff line numberDiff line change
@@ -3,56 +3,52 @@
33
Data Reconciliation
44
====================
55

6-
The method :class:`~pyomo.contrib.parmest.parmest.Estimator.theta_est`
7-
can optionally return model values. This feature can be used to return
8-
reconciled data using a user specified objective. In this case, the list
9-
of variable names the user wants to estimate (theta_names) is set to an
10-
empty list and the objective function is defined to minimize
6+
The optional argument ``return_values`` in :class:`~pyomo.contrib.parmest.parmest.Estimator.theta_est`
7+
can be used for data reconciliation or to return model values based on the specified objective.
8+
9+
For data reconciliation, the ``m.unknown_parameters`` is empty
10+
and the objective function is defined to minimize
1111
measurement to model error. Note that the model used for data
1212
reconciliation may differ from the model used for parameter estimation.
1313

14-
The following example illustrates the use of parmest for data
15-
reconciliation. The functions
14+
The functions
1615
:class:`~pyomo.contrib.parmest.graphics.grouped_boxplot` or
1716
:class:`~pyomo.contrib.parmest.graphics.grouped_violinplot` can be used
1817
to visually compare the original and reconciled data.
1918

20-
Here's a stylized code snippet showing how box plots might be created:
21-
22-
.. doctest::
23-
:skipif: True
24-
25-
>>> import pyomo.contrib.parmest.parmest as parmest
26-
>>> pest = parmest.Estimator(model_function, data, [], objective_function)
27-
>>> obj, theta, data_rec = pest.theta_est(return_values=['A', 'B'])
28-
>>> parmest.graphics.grouped_boxplot(data, data_rec)
29-
30-
Returned Values
31-
^^^^^^^^^^^^^^^
19+
The following example from the reactor design subdirectory returns reconciled values for experiment outputs
20+
(`ca`, `cb`, `cc`, and `cd`) and then uses those values in
21+
parameter estimation (`k1`, `k2`, and `k3`).
3222

33-
Here's a full program that can be run to see returned values (in this case it
34-
is the response function that is defined in the model file):
23+
.. literalinclude:: ../../../../pyomo/contrib/parmest/examples/reactor_design/datarec_example.py
24+
:language: python
25+
26+
The following example returns model values from a Pyomo Expression.
3527

3628
.. doctest::
3729
:skipif: not ipopt_available or not parmest_available
3830

3931
>>> import pandas as pd
4032
>>> import pyomo.contrib.parmest.parmest as parmest
41-
>>> from pyomo.contrib.parmest.examples.rooney_biegler.rooney_biegler import rooney_biegler_model
42-
43-
>>> theta_names = ['asymptote', 'rate_constant']
33+
>>> from pyomo.contrib.parmest.examples.rooney_biegler.rooney_biegler import RooneyBieglerExperiment
4434

35+
>>> # Generate data
4536
>>> data = pd.DataFrame(data=[[1,8.3],[2,10.3],[3,19.0],
4637
... [4,16.0],[5,15.6],[7,19.8]],
4738
... columns=['hour', 'y'])
4839

49-
>>> def SSE(model, data):
50-
... expr = sum((data.y[i]\
51-
... - model.response_function[data.hour[i]])**2 for i in data.index)
40+
>>> # Create an experiment list
41+
>>> exp_list = []
42+
>>> for i in range(data.shape[0]):
43+
... exp_list.append(RooneyBieglerExperiment(data.loc[i, :]))
44+
45+
>>> # Define objective
46+
>>> def SSE(model):
47+
... expr = (model.experiment_outputs[model.y]
48+
... - model.response_function[model.experiment_outputs[model.hour]]
49+
... ) ** 2
5250
... return expr
5351

54-
>>> pest = parmest.Estimator(rooney_biegler_model, data, theta_names, SSE,
55-
... solver_options=None)
52+
>>> pest = parmest.Estimator(exp_list, obj_function=SSE, solver_options=None)
5653
>>> obj, theta, var_values = pest.theta_est(return_values=['response_function'])
5754
>>> #print(var_values)
58-

doc/OnlineDocs/contributed_packages/parmest/driver.rst

+66-70
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Parameter Estimation
44
==================================
55

66
Parameter Estimation using parmest requires a Pyomo model, experimental
7-
data which defines multiple scenarios, and a list of parameter names
7+
data which defines multiple scenarios, and parameters
88
(thetas) to estimate. parmest uses Pyomo [PyomoBookII]_ and (optionally)
99
mpi-sppy [mpisppy]_ to solve a
1010
two-stage stochastic programming problem, where the experimental data is
@@ -36,13 +36,12 @@ which includes the following methods:
3636
~pyomo.contrib.parmest.parmest.Estimator.likelihood_ratio_test
3737
~pyomo.contrib.parmest.parmest.Estimator.leaveNout_bootstrap_test
3838

39-
Additional functions are available in parmest to group data, plot
40-
results, and fit distributions to theta values.
39+
Additional functions are available in parmest to plot
40+
results and fit distributions to theta values.
4141

4242
.. autosummary::
4343
:nosignatures:
4444

45-
~pyomo.contrib.parmest.parmest.group_data
4645
~pyomo.contrib.parmest.graphics.pairwise_plot
4746
~pyomo.contrib.parmest.graphics.grouped_boxplot
4847
~pyomo.contrib.parmest.graphics.grouped_violinplot
@@ -58,88 +57,78 @@ Section.
5857
.. testsetup:: *
5958
:skipif: not __import__('pyomo.contrib.parmest.parmest').contrib.parmest.parmest.parmest_available
6059

60+
# Data
6161
import pandas as pd
62-
from pyomo.contrib.parmest.examples.rooney_biegler.rooney_biegler import rooney_biegler_model as model_function
63-
data = pd.DataFrame(data=[[1,8.3],[2,10.3],[3,19.0],
64-
[4,16.0],[5,15.6],[6,19.8]],
65-
columns=['hour', 'y'])
66-
theta_names = ['asymptote', 'rate_constant']
67-
def objective_function(model, data):
68-
expr = sum((data.y[i] - model.response_function[data.hour[i]])**2 for i in data.index)
62+
data = pd.DataFrame(
63+
data=[[1, 8.3], [2, 10.3], [3, 19.0],
64+
[4, 16.0], [5, 15.6], [7, 19.8]],
65+
columns=['hour', 'y'],
66+
)
67+
68+
# Sum of squared error function
69+
def SSE(model):
70+
expr = (
71+
model.experiment_outputs[model.y]
72+
- model.response_function[model.experiment_outputs[model.hour]]
73+
) ** 2
6974
return expr
7075

76+
# Create an experiment list
77+
from pyomo.contrib.parmest.examples.rooney_biegler.rooney_biegler import RooneyBieglerExperiment
78+
exp_list = []
79+
for i in range(data.shape[0]):
80+
exp_list.append(RooneyBieglerExperiment(data.loc[i, :]))
81+
7182
.. doctest::
7283
:skipif: not __import__('pyomo.contrib.parmest.parmest').contrib.parmest.parmest.parmest_available
7384

7485
>>> import pyomo.contrib.parmest.parmest as parmest
75-
>>> pest = parmest.Estimator(model_function, data, theta_names, objective_function)
86+
>>> pest = parmest.Estimator(exp_list, obj_function=SSE)
7687

7788
Optionally, solver options can be supplied, e.g.,
7889

7990
.. doctest::
8091
:skipif: not __import__('pyomo.contrib.parmest.parmest').contrib.parmest.parmest.parmest_available
8192

8293
>>> solver_options = {"max_iter": 6000}
83-
>>> pest = parmest.Estimator(model_function, data, theta_names, objective_function, solver_options)
84-
85-
86-
87-
Model function
88-
--------------
89-
90-
The first argument is a function which uses data for a single scenario
91-
to return a populated and initialized Pyomo model for that scenario.
92-
93-
Parameters that the user would like to estimate can be defined as
94-
**mutable parameters (Pyomo `Param`) or variables (Pyomo `Var`)**.
95-
Within parmest, any parameters that are to be estimated are converted to unfixed variables.
96-
Variables that are to be estimated are also unfixed.
97-
98-
The model does not have to be specifically written as a
99-
two-stage stochastic programming problem for parmest.
100-
That is, parmest can modify the
101-
objective, see :ref:`ObjFunction` below.
102-
103-
Data
104-
----
105-
106-
The second argument is the data which will be used to populate the Pyomo
107-
model. Supported data formats include:
108-
109-
* **Pandas Dataframe** where each row is a separate scenario and column
110-
names refer to observed quantities. Pandas DataFrames are easily
111-
stored and read in from csv, excel, or databases, or created directly
112-
in Python.
113-
* **List of Pandas Dataframe** where each entry in the list is a separate scenario.
114-
Dataframes store observed quantities, referenced by index and column.
115-
* **List of dictionaries** where each entry in the list is a separate
116-
scenario and the keys (or nested keys) refer to observed quantities.
117-
Dictionaries are often preferred over DataFrames when using static and
118-
time series data. Dictionaries are easily stored and read in from
119-
json or yaml files, or created directly in Python.
120-
* **List of json file names** where each entry in the list contains a
121-
json file name for a separate scenario. This format is recommended
122-
when using large datasets in parallel computing.
123-
124-
The data must be compatible with the model function that returns a
125-
populated and initialized Pyomo model for a single scenario. Data can
126-
include multiple entries per variable (time series and/or duplicate
127-
sensors). This information can be included in custom objective
128-
functions, see :ref:`ObjFunction` below.
129-
130-
Theta names
131-
-----------
132-
133-
The third argument is a list of parameters or variable names that the user wants to
134-
estimate. The list contains strings with `Param` and/or `Var` names from the Pyomo
135-
model.
94+
>>> pest = parmest.Estimator(exp_list, obj_function=SSE, solver_options=solver_options)
95+
96+
97+
List of experiment objects
98+
--------------------------
99+
100+
The first argument is a list of experiment objects which is used to
101+
create one labeled model for each expeirment.
102+
The template :class:`~pyomo.contrib.parmest.experiment.Experiment`
103+
can be used to generate a list of experiment objects.
104+
105+
A labeled Pyomo model ``m`` has the following additional suffixes (Pyomo `Suffix`):
106+
107+
* ``m.experiment_outputs`` which defines experiment output (Pyomo `Param`, `Var`, or `Expression`)
108+
and their associated data values (float, int).
109+
* ``m.unknown_parameters`` which defines the mutable parameters or variables (Pyomo `Param` or `Var`)
110+
to estimate along with their component unique identifier (Pyomo `ComponentUID`).
111+
Within parmest, any parameters that are to be estimated are converted to unfixed variables.
112+
Variables that are to be estimated are also unfixed.
113+
114+
The experiment class has one required method:
115+
116+
* :class:`~pyomo.contrib.parmest.experiment.Experiment.get_labeled_model` which returns the labeled Pyomo model.
117+
Note that the model does not have to be specifically written as a
118+
two-stage stochastic programming problem for parmest.
119+
That is, parmest can modify the
120+
objective, see :ref:`ObjFunction` below.
121+
122+
Parmest comes with several :ref:`examplesection` that illustrates how to set up the list of experiment objects.
123+
The examples commonly include additional :class:`~pyomo.contrib.parmest.experiment.Experiment` class methods to
124+
create the model, finalize the model, and label the model. The user can customize methods to suit their needs.
136125

137126
.. _ObjFunction:
138127

139128
Objective function
140129
------------------
141130

142-
The fourth argument is an optional argument which defines the
131+
The second argument is an optional argument which defines the
143132
optimization objective function to use in parameter estimation.
144133

145134
If no objective function is specified, the Pyomo model is used "as is" and
@@ -150,20 +139,27 @@ stochastic programming problem.
150139
If the Pyomo model is not written as a two-stage stochastic programming problem in
151140
this format, and/or if the user wants to use an objective that is
152141
different than the original model, a custom objective function can be
153-
defined for parameter estimation. The objective function arguments
154-
include `model` and `data` and the objective function returns a Pyomo
142+
defined for parameter estimation. The objective function has a single argument,
143+
which is the model from a single experiment.
144+
The objective function returns a Pyomo
155145
expression which is used to define "SecondStageCost". The objective
156146
function can be used to customize data points and weights that are used
157147
in parameter estimation.
158148

149+
Parmest includes one built in objective function to compute the sum of squared errors ("SSE") between the
150+
``m.experiment_outputs`` model values and data values.
151+
159152
Suggested initialization procedure for parameter estimation problems
160153
--------------------------------------------------------------------
161154

162155
To check the quality of initial guess values provided for the fitted parameters, we suggest solving a
163156
square instance of the problem prior to solving the parameter estimation problem using the following steps:
164157

165-
1. Create :class:`~pyomo.contrib.parmest.parmest.Estimator` object. To initialize the parameter estimation solve from the square problem solution, set optional argument ``solver_options = {bound_push: 1e-8}``.
158+
1. Create :class:`~pyomo.contrib.parmest.parmest.Estimator` object. To initialize the parameter
159+
estimation solve from the square problem solution, set optional argument ``solver_options = {bound_push: 1e-8}``.
166160

167-
2. Call :class:`~pyomo.contrib.parmest.parmest.Estimator.objective_at_theta` with optional argument ``(initialize_parmest_model=True)``. Different initial guess values for the fitted parameters can be provided using optional argument `theta_values` (**Pandas Dataframe**)
161+
2. Call :class:`~pyomo.contrib.parmest.parmest.Estimator.objective_at_theta` with optional
162+
argument ``(initialize_parmest_model=True)``. Different initial guess values for the fitted
163+
parameters can be provided using optional argument `theta_values` (**Pandas Dataframe**)
168164

169165
3. Solve parameter estimation problem by calling :class:`~pyomo.contrib.parmest.parmest.Estimator.theta_est`

doc/OnlineDocs/contributed_packages/parmest/examples.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Additional use cases include:
2020
* Parameter estimation using mpi4py, the example saves results to a file
2121
for later analysis/graphics (semibatch example)
2222

23-
The description below uses the reactor design example. The file
23+
The example below uses the reactor design example. The file
2424
**reactor_design.py** includes a function which returns an populated
2525
instance of the Pyomo model. Note that the model is defined to maximize
2626
`cb` and that `k1`, `k2`, and `k3` are fixed. The _main_ program is

doc/OnlineDocs/contributed_packages/parmest/scencreate.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@ scenarios to the screen, accessing them via the ``ScensItator`` a ``print``
1818
:language: python
1919

2020
.. note::
21-
This example may produce an error message your version of Ipopt is not based
21+
This example may produce an error message if your version of Ipopt is not based
2222
on a good linear solver.

0 commit comments

Comments
 (0)