[Pyomo.DoE] Add simultaneous design of multiple experiments#3866
[Pyomo.DoE] Add simultaneous design of multiple experiments#3866smondal13 wants to merge 117 commits intoPyomo:mainfrom
Conversation
…`experiment` argument is provided.
…d replace `self.experiment` with `self.experiment_list[0]`. `doe/reactor_example.py` runs successfully.
…sing the `doe/reactor_multi_experiment.py`
…ultiexperiment.py` both for 3 experiments
…etric-uncertainty
…parametric-uncertainty
…nt for `optimize_experiments()` and `compute_FIM()`
… both of sensitivity and optimize_experiments()
…A-opt gave different result for grid and optimization
pyomo/contrib/doe/doe.py
Outdated
| def __init__( | ||
| self, | ||
| experiment=None, | ||
| experiment_list=None, |
There was a problem hiding this comment.
@adowling2 , @blnicho Should we keep both experiment and experiment_list? The user can pass the experiment_list argument if the user wants to pass the user-initialized experiment object. Another option is to keep the experiment argument and make it able to accept list objects as well, and maybe use a hint, e.g., experiment: list.
Currently, I have added a deprecation warning for the experiment argument.
There was a problem hiding this comment.
I like allowing experiment to be either an instance of the Experiment class (or a child) or a list of Experiment objects. That seems cleaner to me.
| "variable": None, | ||
| "source": None, | ||
| } | ||
| diagnostics_warnings = [] |
There was a problem hiding this comment.
I want to propagate the warning to the final result, which may help the user. If this is not a good choice, let me know @adowling2, @blnicho
…ing and improve error handling; add utility methods for enum labels and input fixing. Run black
|
@blnicho @adowling2 This PR is ready for an initial review. Please take a look when you have a moment. |
| lhs = "lhs" | ||
|
|
||
|
|
||
| class _DoEResultsJSONEncoder(json.JSONEncoder): |
There was a problem hiding this comment.
Numpy floats specifically are shown in a weird format, which is why everything is converted to Python objects.
There was a problem hiding this comment.
This looks strange to me. Are you saving DoE results to a JSON?
There was a problem hiding this comment.
That is one of the options. The user can choose to save the result.
| init_n_workers: int = None, | ||
| init_combo_chunk_size: int = 5000, | ||
| init_combo_parallel_threshold: int = 20000, | ||
| init_max_wall_clock_time: float = None, |
There was a problem hiding this comment.
@adowling2, @blnicho, It may be worth adding an init_solver = None where the user can add flexible solver options that will be passed for initialization. It may save time for initialization. If the user does not pass that, then the default solver from DesignOfExperiments will be used. Let me know if this is a good idea.
There was a problem hiding this comment.
I have been thinking something similar to this... it would be helpful to allow for different solver options: initialization and the full optimization solve.
pyomo/contrib/doe/doe.py
Outdated
| diagnostics_warnings = [] | ||
|
|
||
| # Add experiment(s) for each scenario | ||
| # TODO: Add s_prev = 0 to handle parameter scenarios |
There was a problem hiding this comment.
This is the extension that I will be working on next. So, I have added this as a reference for myself. This is the reason we are using param_scenarios, although we are not using it this time. It does not interfere with the user interface.
|
@blnicho One of the tests is failing. |
… add-multiexperiment
…for `_DoEResultsJSONEncoder` and `optimize_experiments()` API
- update LHS candidate FIM evaluation logging to report the actual\n execution mode so users can immediately see whether initialization\n is running serially or in parallel.\n- add an explicit warning when lhs_parallel=True resolves to\n serial execution because only one worker is available, preventing\n silent fallback confusion during debugging.\n- keep LHS initialization behavior unchanged while improving\n observability for multi-experiment startup diagnostics.
adowling2
left a comment
There was a problem hiding this comment.
There is a lot of added code in this PR. I think it would be helpful to draft a documentation page explaining the key features of this proposed interface. This would be a simple .md file that eventually becomes a RTD page. I find that drafting documentation helps provide more robust motivation and context to engage with the PR.
pyomo/contrib/doe/tests/_review.md
Outdated
| @@ -0,0 +1,362 @@ | |||
| # Review: `_DoEResultsJSONEncoder`, `_enum_label`, and `optimize_experiments()` API | |||
There was a problem hiding this comment.
How was this file generated? I am looking for some additional context to help me digest the contents of this file. @smondal13
There was a problem hiding this comment.
This was for me to review. I forgot to remove this. I asked AI to review the code for edge cases and best practices.
| ] | ||
| sigma_inv = [1 / v for k, v in model.scenario_blocks[0].measurement_error.items()] | ||
| sigma_inv = [ | ||
| 1 / v for k, v in model.fd_scenario_blocks[0].measurement_error.items() |
| objective_option="pseudo_trace", | ||
| ) | ||
|
|
||
| model_direct = pyo.ConcreteModel() |
There was a problem hiding this comment.
What is this block of code doing? Comments would help.
There was a problem hiding this comment.
This is used to check the build structure of the new method. This one particularly tests the helper function that returns the experiment_input vars
|
|
||
| @unittest.skipIf(not ipopt_available, "The 'ipopt' command is not available") | ||
| def test_multi_experiment_structure_and_results(self): | ||
| solver = self._make_solver() |
There was a problem hiding this comment.
What is this test doing? I recommend adding a comment block with a few-sentence description of the contents and goals of this test.
There was a problem hiding this comment.
This applies to all of the tests in this file.
| import json | ||
| import logging | ||
| import math | ||
| import os | ||
| import threading |
There was a problem hiding this comment.
We want to discuss with the Pyomo team if this is the recommended approach for threading.
| init_n_workers: int = None, | ||
| init_combo_chunk_size: int = 5000, | ||
| init_combo_parallel_threshold: int = 20000, | ||
| init_max_wall_clock_time: float = None, |
There was a problem hiding this comment.
I have been thinking something similar to this... it would be helpful to allow for different solver options: initialization and the full optimization solve.
| @@ -537,6 +619,1555 @@ def run_doe(self, model=None, results_file=None): | |||
| with open(results_file, "w") as file: | |||
| json.dump(self.results, file) | |||
|
|
|||
| def optimize_experiments( | |||
There was a problem hiding this comment.
Is this function new? Does it replace an exisiting function?
There was a problem hiding this comment.
This is a new helper function. Instead of writing this code multiple times, I have added a function that just do this.
- Updated the `DesignOfExperiments` class to accept either a single experiment or a list of experiments through the `experiment` parameter, replacing the previous `experiment_list` parameter. - Modified related test cases to reflect this change, ensuring that both single and multiple experiments are handled correctly. - Adjusted error messages and validation checks to align with the new parameter naming. - Ensured backward compatibility by updating all references in the test suite and error handling.
- Add documentation
… add-multiexperiment
- enhance documentation for init_solver usage in multi-experiment optimization
Fixes # .
Summary/Motivation:
This PR adds a new
DesignOfExperiments.optimize_experiments()API inpyomo/contrib/doe/doe.pyto support simultaneous optimization of multiple experiments in one workflow. The motivation is to provide a multi-experiment DoE interface with stronger initialization options, clearer mode handling (template vs. user-initialized experiments), and richer diagnostics/results than the existing single-experiment path.Changes proposed in this PR:
optimize_experiments()for multi-experiment DoE optimization.n_exp.n_expis inferred/validated.initialization_method="lhs") with controls for:sym_break_conssuffix,run_info._DoEResultsJSONEncoderfor numpy/Pyomo-enum values when writingresults_file.Note:
documentation.mdwhich describes the API. This documentation is to help the reviewers to understand the API and will not be merged intoPyomo:mainLegal Acknowledgement
By contributing to this software project, I have read the contribution guide and agree to the following terms and conditions for my contribution: