-
Notifications
You must be signed in to change notification settings - Fork 577
Simplify _Q_opt in parmest with block scenario structure. #3789
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 6 commits
10f6b37
b0b7e2c
3e95e91
8d3a4d5
3982e1b
e829344
e464097
dc5ee76
6355818
099f541
76ee05e
d91ce3f
1aea99f
7d93cc0
d7d2214
7f21344
32d8d41
4e8c3ee
1e802ba
4b89bb3
4777253
c58d5f3
ea734b7
490abea
1074fb0
9543456
af4df1a
d4c4125
9d396fa
a97b21e
0afb5ba
191b131
2c0760e
bbe994b
2c2e024
2333a4b
b9af9f1
6cd3b4b
b46e1a7
3261798
fc478be
acba985
145c2d8
337095d
837192c
4b46c30
b9cf010
062a9ee
5baaa2f
c8194ac
26d70e3
4aa027d
26ba2ea
dd926f8
7b70d1d
43cbaa4
07798c9
b325f0d
8b47430
aac1476
66a1396
3957dc9
382ea20
0da606f
935b700
1fc71ee
56ac15d
60dc796
6bf439e
a68906b
f31a35f
b124def
2908c78
58435d3
db646ba
d222c4f
9d829c4
7ffab76
e267983
e3ae6e6
345c3f2
6c3d5a0
49b787a
8cf624e
c248c74
b975089
a63e4fc
b92aa7d
471dbe7
98d91fc
a6821ae
f0ef6d6
1f86b03
72b4345
c495e0f
82ee4ce
559a900
65067d5
db26533
c9b19d7
7bba006
2cd2614
5ba4fab
ee57ec9
7cffb34
ebbd279
0c1e605
92a2dd6
854366b
72204d9
ed2cf54
cc0513a
9c0d0a3
4df5b48
4fc0336
b372edd
41e8e98
9fe600f
48ba6a6
ba4091c
eba10b3
c997944
d2b5d74
ae9808f
9fa6528
cb1ecea
261a78f
3dca7d7
959d58e
b21c34d
1b6c63a
f768ea3
867e642
6d7eb7a
a39d367
deb3c5a
9fcf4f1
a188dd7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -971,6 +971,92 @@ def _instance_creation_callback(self, experiment_number=None, cb_data=None): | |||||
| model = self._create_parmest_model(experiment_number) | ||||||
| return model | ||||||
|
|
||||||
| def _create_scenario_blocks(self): | ||||||
| # Create scenario block structure | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
This comment can be removed now that there is a docstring |
||||||
| # Code is still heavily hypothetical and needs to be thought over and debugged. | ||||||
| # Utility function for _Q_opt_simple | ||||||
| # Make a block of model scenarios, one for each experiment in exp_list | ||||||
|
|
||||||
| # Create a parent model to hold scenario blocks | ||||||
| model = pyo.ConcreteModel() | ||||||
| model.exp_scenarios = pyo.Block(range(len(self.exp_list))) | ||||||
| for i in range(len(self.exp_list)): | ||||||
| # Create parmest model for experiment i | ||||||
| parmest_model = self._create_parmest_model(i) | ||||||
| # Assign parmest model to block | ||||||
| model.exp_scenarios[i].transfer_attributes_from(parmest_model) | ||||||
|
|
||||||
| # Make an objective that sums over all scenario blocks | ||||||
| def total_obj(m): | ||||||
| return sum( | ||||||
| block.Total_Cost_Objective for block in m.exp_scenarios.values() | ||||||
| ) / len(self.exp_list) | ||||||
|
|
||||||
| model.Obj = pyo.Objective(rule=total_obj, sense=pyo.minimize) | ||||||
|
|
||||||
| # Make sure all the parameters are linked across blocks | ||||||
| for name in self.estimator_theta_names: | ||||||
| # Get the variable from the first block | ||||||
| ref_var = getattr(model.exp_scenarios[0], name) | ||||||
| for i in range(1, len(self.exp_list)): | ||||||
| curr_var = getattr(model.exp_scenarios[i], name) | ||||||
| # Constrain current variable to equal reference variable | ||||||
| model.add_component( | ||||||
| f"Link_{name}_Block0_Block{i}", | ||||||
| pyo.Constraint(expr=curr_var == ref_var), | ||||||
| ) | ||||||
|
|
||||||
| # Deactivate the objective in each block to avoid double counting | ||||||
| for i in range(len(self.exp_list)): | ||||||
| model.exp_scenarios[i].Total_Cost_Objective.deactivate() | ||||||
|
|
||||||
| model.pprint() | ||||||
|
|
||||||
| return model | ||||||
|
|
||||||
| # Redesigning simpler version of _Q_opt | ||||||
| # Still work in progress | ||||||
| def _Q_opt_simple( | ||||||
|
sscini marked this conversation as resolved.
Outdated
|
||||||
| self, | ||||||
| return_values=None, | ||||||
| bootlist=None, | ||||||
| ThetaVals=None, | ||||||
| solver="ipopt", | ||||||
| calc_cov=NOTSET, | ||||||
| cov_n=NOTSET, | ||||||
| ): | ||||||
| ''' | ||||||
|
sscini marked this conversation as resolved.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use double quotes for docstrings
Suggested change
|
||||||
| Making new version of _Q_opt that uses scenario blocks, similar to DoE. | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you make this method summary a bit more verbose. It currently reads like a commit message rather than a description of what the method is doing. You don't need to refer to it as a "new version". |
||||||
|
|
||||||
| Steps: | ||||||
| 1. Load model - parmest model should be labeled | ||||||
| 2. Create scenario blocks (biggest redesign) - clone model to have one per experiment | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| 3. Define objective and constraints for the block | ||||||
| 4. Solve the block as a single problem | ||||||
| 5. Analyze results and extract parameter estimates | ||||||
|
|
||||||
| ''' | ||||||
|
|
||||||
| # Create scenario blocks using utility function | ||||||
| model = self._create_scenario_blocks() | ||||||
|
|
||||||
| solver = SolverFactory('ipopt') | ||||||
| if self.solver_options is not None: | ||||||
| for key in self.solver_options: | ||||||
| solver.options[key] = self.solver_options[key] | ||||||
|
|
||||||
| solve_result = solver.solve(model, tee=self.tee) | ||||||
| assert_optimal_termination(solve_result) | ||||||
|
|
||||||
| # Extract objective value | ||||||
| obj_value = pyo.value(model.Obj) | ||||||
| theta_estimates = {} | ||||||
| # Extract theta estimates from first block | ||||||
| for name in self.estimator_theta_names: | ||||||
| theta_estimates[name] = pyo.value(getattr(model.exp_scenarios[0], name)) | ||||||
|
sscini marked this conversation as resolved.
Outdated
|
||||||
|
|
||||||
| return obj_value, theta_estimates | ||||||
|
|
||||||
| def _Q_opt( | ||||||
| self, | ||||||
| ThetaVals=None, | ||||||
|
|
@@ -1683,6 +1769,77 @@ def theta_est( | |||||
| cov_n=cov_n, | ||||||
| ) | ||||||
|
|
||||||
| # Replicate of theta_est for testing simplified _Q_opt | ||||||
| # Still work in progress | ||||||
| def theta_est_simple( | ||||||
| self, solver="ipopt", return_values=[], calc_cov=NOTSET, cov_n=NOTSET | ||||||
| ): | ||||||
| """ | ||||||
| Parameter estimation using all scenarios in the data | ||||||
|
|
||||||
| Parameters | ||||||
| ---------- | ||||||
| solver: str, optional | ||||||
| Currently only "ef_ipopt" is supported. Default is "ef_ipopt". | ||||||
| return_values: list, optional | ||||||
| List of Variable names, used to return values from the model | ||||||
| for data reconciliation | ||||||
| calc_cov: boolean, optional | ||||||
| DEPRECATED. | ||||||
|
|
||||||
| If True, calculate and return the covariance matrix | ||||||
| (only for "ef_ipopt" solver). Default is NOTSET | ||||||
| cov_n: int, optional | ||||||
| DEPRECATED. | ||||||
|
|
||||||
| If calc_cov=True, then the user needs to supply the number of datapoints | ||||||
| that are used in the objective function. Default is NOTSET | ||||||
|
|
||||||
| Returns | ||||||
| ------- | ||||||
| obj_val: float | ||||||
| The objective function value | ||||||
| theta_vals: pd.Series | ||||||
| Estimated values for theta | ||||||
| var_values: pd.DataFrame | ||||||
| Variable values for each variable name in | ||||||
| return_values (only for solver='ipopt') | ||||||
| """ | ||||||
| assert isinstance(solver, str) | ||||||
| assert isinstance(return_values, list) | ||||||
| assert (calc_cov is NOTSET) or isinstance(calc_cov, bool) | ||||||
|
|
||||||
| if calc_cov is not NOTSET: | ||||||
| deprecation_warning( | ||||||
| "theta_est(): `calc_cov` and `cov_n` are deprecated options and " | ||||||
| "will be removed in the future. Please use the `cov_est()` function " | ||||||
| "for covariance calculation.", | ||||||
| version="6.9.5", | ||||||
| ) | ||||||
| else: | ||||||
| calc_cov = False | ||||||
|
|
||||||
| # check if we are using deprecated parmest | ||||||
| if self.pest_deprecated is not None and calc_cov: | ||||||
| return self.pest_deprecated.theta_est( | ||||||
| solver=solver, | ||||||
| return_values=return_values, | ||||||
| calc_cov=calc_cov, | ||||||
| cov_n=cov_n, | ||||||
| ) | ||||||
| elif self.pest_deprecated is not None and not calc_cov: | ||||||
| return self.pest_deprecated.theta_est( | ||||||
| solver=solver, return_values=return_values | ||||||
| ) | ||||||
|
|
||||||
| return self._Q_opt_simple( | ||||||
| solver=solver, | ||||||
| return_values=return_values, | ||||||
| bootlist=None, | ||||||
| calc_cov=calc_cov, | ||||||
| cov_n=cov_n, | ||||||
| ) | ||||||
|
|
||||||
| def cov_est(self, method="finite_difference", solver="ipopt", step=1e-3): | ||||||
| """ | ||||||
| Covariance matrix calculation using all scenarios in the data | ||||||
|
|
||||||
Uh oh!
There was an error while loading. Please reload this page.