Skip to content

Commit 92951cb

Browse files
authored
Merge pull request #3550 from sscini/parmest_obj_regularization
Adding regularization objective option to parmest
2 parents 2a8e709 + 3b2d37d commit 92951cb

4 files changed

Lines changed: 1114 additions & 26 deletions

File tree

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# ____________________________________________________________________________________
2+
#
3+
# Pyomo: Python Optimization Modeling Objects
4+
# Copyright (c) 2008-2026 National Technology and Engineering Solutions of Sandia, LLC
5+
# Under the terms of Contract DE-NA0003525 with National Technology and Engineering
6+
# Solutions of Sandia, LLC, the U.S. Government retains certain rights in this
7+
# software. This software is distributed under the 3-clause BSD License.
8+
# ____________________________________________________________________________________
9+
10+
from pyomo.common.dependencies import pandas as pd
11+
import pyomo.contrib.parmest.parmest as parmest
12+
from pyomo.contrib.parmest.examples.rooney_biegler.rooney_biegler import (
13+
RooneyBieglerExperiment,
14+
)
15+
16+
17+
def main():
18+
"""
19+
Evaluate L2 regularization on the Rooney-Biegler example.
20+
21+
Notes
22+
-----
23+
The model response saturates for large positive ``rate_constant`` values:
24+
``y = asymptote * (1 - exp(-rate_constant * hour))``.
25+
If ``rate_constant`` is both unpenalized and unbounded, the objective can be
26+
nearly flat in that direction, which can lead to extreme fitted values.
27+
"""
28+
29+
# Rooney & Biegler Reference Values
30+
# a = 19.14, b = 0.53
31+
theta_ref = {'asymptote': 19.14, 'rate_constant': 0.53}
32+
33+
# L2 setup: create a 'Stiff' Prior for 'asymptote' but leave 'rate_constant' flexible.
34+
prior_FIM_l2 = pd.DataFrame(
35+
[[1000.0, 0.0], [0.0, 0.0]],
36+
index=['asymptote', 'rate_constant'],
37+
columns=['asymptote', 'rate_constant'],
38+
)
39+
40+
# Data
41+
data = pd.DataFrame(
42+
data=[[1, 8.3], [2, 10.3], [3, 19.0], [4, 16.0], [5, 15.6], [7, 19.8]],
43+
columns=['hour', 'y'],
44+
)
45+
46+
# Create an experiment list
47+
exp_list = []
48+
for i in range(data.shape[0]):
49+
exp = RooneyBieglerExperiment(data.loc[i, :])
50+
# Example-scoped stabilization: keep rate_constant in a practical range.
51+
m = exp.get_labeled_model()
52+
m.rate_constant.setlb(0.0)
53+
m.rate_constant.setub(5.0)
54+
exp_list.append(exp)
55+
56+
# Create an instance of the parmest estimator (L2)
57+
pest_l2 = parmest.Estimator(
58+
exp_list,
59+
obj_function="SSE",
60+
regularization='L2',
61+
prior_FIM=prior_FIM_l2,
62+
theta_ref=theta_ref,
63+
)
64+
65+
# Parameter estimation and covariance for L2
66+
obj_l2, theta_l2 = pest_l2.theta_est()
67+
cov_l2 = pest_l2.cov_est()
68+
69+
if parmest.graphics.seaborn_available:
70+
parmest.graphics.pairwise_plot(
71+
(theta_l2, cov_l2, 100),
72+
theta_star=theta_l2,
73+
alpha=0.8,
74+
distributions=['MVN'],
75+
title='L2 regularized theta estimates within 80% confidence region',
76+
)
77+
78+
# Assert statements compare parameter estimation (theta) to an expected value
79+
relative_error = abs(theta_l2['asymptote'] - 19.1426) / 19.1426
80+
assert relative_error < 0.01
81+
relative_error = abs(theta_l2['rate_constant'] - 0.5311) / 0.5311
82+
assert relative_error < 0.01
83+
84+
return {"L2": (obj_l2, theta_l2, cov_l2)}
85+
86+
87+
if __name__ == "__main__":
88+
results = main()
89+
for reg_name, (obj, theta, cov) in results.items():
90+
print(f"{reg_name} estimated parameters (theta):", theta)
91+
print(f"{reg_name} objective function value at theta:", obj)
92+
print(f"{reg_name} covariance of parameter estimates:", cov)

0 commit comments

Comments
 (0)