Add core symbolic gradient support to Pyomo.DoE#3928
Conversation
Add schema, filtering, and error-path tests for factorial-result tables and DataFrame input handling. Validation: - 101 passed, 5 warnings, 10 subtests passed in 30.00s Notes: - plotting tests emit expected matplotlib warnings under the non-interactive Agg backend because draw_factorial_figure() calls plt.show().
Add a determinant-based reactor regression test, guard Codecov uploads when coverage.xml is absent, and align local formatting with Black expectations. Validation: - focused reactor regression subset: 2 passed, 28 deselected in 10.08s - full DoE suite: 102 passed, 5 warnings, 10 subtests passed in 30.29s Notes: - plotting-related tests continue to emit expected matplotlib warnings under the non-interactive Agg backend because draw_factorial_figure() calls plt.show().
Add coverage for draw_factorial_figure guard branches covering more-than-two sensitivity variables and missing fixed design variables. Validation: - focused plotting-guard subset: 2 passed, 37 deselected in 1.92s - full DoE suite: 104 passed, 5 warnings, 10 subtests passed in 31.47s Notes: - plotting-related tests continue to emit expected matplotlib warnings under the non-interactive Agg backend because draw_factorial_figure() calls plt.show().
|
@adowling2 @slilonfe5 @smondal13 @sscini This is the new symbolic PR (created from the unpolluted branch) |
| experiment_grad = None | ||
| if self._gradient_method == GradientMethod.pynumero: | ||
| experiment_grad = ExperimentGradients( | ||
| model.scenario_blocks[0], automatic=True, symbolic=True |
There was a problem hiding this comment.
@adowling2 @smondal13 , per discussion with Shuvo on 05/11: Shuvo and I decided that the cleanest path forward to address potential conflicts with Shuvo's multi-experiment PR #3866 is to leave symbolic PR as is for now. When PR3866 is merged, we will address the conflict with fd_scenario_blocks and fd_scenarios within multi-experiment vs scenatio_blocks and scenarios within the current Pyomo main and the symbolic PR.
Alternative would be to merge this PR with multi-experiment as of now, but it will create a dependency and we are not sure we should proceed this way.
There was a problem hiding this comment.
Shilpa to double-check and address: If main does not use model.scenarios currently, @snarasi2 will modify all occurrences of model.scenarios to to model.fd_scenarios to facilitate multi-experiment merge. Additionally, this will help distinguish scenario building for finite difference methods for parameter perturbations vs parameter scenarios for uncertainty-aware design.
There was a problem hiding this comment.
@smondal13 @adowling2 I checked upstream/main and confirmed that literal model.scenarios already exists in pyomo/contrib/doe/doe.py and pyomo/contrib/doe/tests/test_doe_build.py. Since the proposed rename was conditional on main not currently using model.scenarios, I did not rename model.scenarios to model.fd_scenarios in this PR. I’m leaving this deferred to the multi-experiment merge as discussed on 05/11.
| # focused on replacing active general-purpose coverage with | ||
| # Rooney-Biegler or polynomial examples rather than rewriting inactive, | ||
| # branch-specific tests. | ||
| def DISABLE_test_rooney_biegler_obj_cholesky_solve_bad_prior(self): |
There was a problem hiding this comment.
Maybe this should be deleted? It's been disabled for a while, and it'll still live in the commit history.
There was a problem hiding this comment.
Removed the disabled Rooney-Biegler Cholesky/bad-prior test based on Miranda’s review comment and alignment from @slilonfe5 and @smondal13 on Slack. Commit: 5ad99f0
| # The GreyBox path here uses cyipopt rather than the standalone | ||
| # IPOPT executable, and the local failure mode we diagnosed was a | ||
| # missing MA57/HSL runtime on that cyipopt solve path. | ||
| cyipopt_skip_reason = ( |
There was a problem hiding this comment.
This is a nitpick. All of the skip reasons are variations of the same thing (MA57/HSL issues). Do you really need all three different messages? Or can you just standardize to the one that's on line 315?
There was a problem hiding this comment.
Thanks — standardized the MA57/HSL-related skip reasons to use the same message as line 315 in ab2f729.
|
|
||
|
|
||
| @unittest.skipIf(not ipopt_available, "The 'ipopt' command is not available") | ||
| @unittest.skipIf(not numpy_available, "Numpy is not available") |
There was a problem hiding this comment.
Aren't these numpy_available checks redundant (because of lines 29-30)?
There was a problem hiding this comment.
Thanks — removed the redundant NumPy skip decorators in test_utils.py because the file-level SkipTest already handles missing NumPy in f1c9a8f.
| self.assertEqual(jacobian.shape, expected.shape) | ||
| self.assertTrue(np.allclose(jacobian, expected, atol=1e-7, rtol=1e-7)) | ||
|
|
||
| @unittest.skipIf(not scipy_available, "scipy is not available") |
There was a problem hiding this comment.
Similar comment - isn't this scipy_available check redundant?
There was a problem hiding this comment.
Thanks — removed the redundant SciPy skip decorator in test_utils.py because the file-level SkipTest already handles missing SciPy in dcbfee6.
There was a problem hiding this comment.
@mrmundt, @adowling2 I also noticed the same pattern in test_greybox.py: the file has a file-level SkipTest for NumPy/SciPy, but the TestFIMExternalGreyBox class still has NumPy/SciPy skipIf decorators. Would you prefer that I keep this PR scoped to the test_utils.py comments, or should I also remove the redundant decorators within test_greybox.py and similar files for consistency?
| default: None --> don't save | ||
|
|
||
| """ | ||
| if self._gradient_method == GradientMethod.kaug: |
There was a problem hiding this comment.
Maybe I am misunderstanding - kaug is listed as an acceptable option in the docstring, but it's not actually?
There was a problem hiding this comment.
Thanks — I checked the code path more closely. kaug is supported for FIM computation through compute_FIM(), but run_doe() explicitly rejects GradientMethod.kaug because it is not supported for DoE optimization. I clarified that scope in the gradient_method docstring in 1096e52.
Fixes # .
This PR ports the core symbolic-gradient functionality from the historical pyomo-doe-symbolic work into the current pyomo.contrib.doe implementation.
Summary/Motivation:
Rather than merging the old branch directly, this change transplants the symbolic DoE pieces onto current main so that symbolic sensitivities work with the newer DoE implementation already present in Pyomo, including the current objective and GreyBox-oriented code paths.
This PR ports the core symbolic-gradient functionality from adowling2#7 onto current Pyomo main and adapts it to the current pyomo.contrib.doe implementation.
This contribution was prepared with coding assistance from OpenAI Codex. All design decisions, validation, testing, and quality-assurance responsibility remain with Shilpa Narasimhan.
Changes proposed in this PR:
GradientMethodsupport toDesignOfExperimentsExperimentGradientsfrompyomo.contrib.doeExperimentGradientsso symbolic and automatic differentiation are set up togetherrun_doe()from being called withGradientMethod.kaugcyipopt/ MA57-HSL test behavior where relevantValidation performed locally:
python -m black -S -C --check --diff pyomo/contrib/doe→ passedtypos --config .github/workflows/typos.toml pyomo/contrib/doe DOE_SYMBOLIC_PR_NOTES.md→ passed134 passed, 0 failed, 0 skippedcyipopttests were run with MA57/HSL available in the local environmentLegal Acknowledgement
By contributing to this software project, I have read the contribution guide and agree to the following terms and conditions for my contribution: