Skip to content

Pyomo. DoE: Sensitivity initialization#3639

Open
smondal13 wants to merge 51 commits intoPyomo:mainfrom
smondal13:sensitivity-initialization
Open

Pyomo. DoE: Sensitivity initialization#3639
smondal13 wants to merge 51 commits intoPyomo:mainfrom
smondal13:sensitivity-initialization

Conversation

@smondal13
Copy link
Contributor

@smondal13 smondal13 commented Jun 23, 2025

Fixes # .

Summary/Motivation:

  • Initializing the model to a previously solved point in the design space can help the model converge at another point in the design space for sensitivity analysis
  • Better error messages in the method can help the user in debugging
  • Saving the file can be helpful if the model takes a long time to solve or if the user wants it.
  • The pattern generator in the utils.py file can be used in other cases where it can help with the initialization
  • Correlation matrix computation functionality will be helpful in computing correlation among the parameters

Changes proposed in this PR:

  • Added a new method compute_FIM_factorial() in Pyomo.DoE for sensitivity initialization
  • Added snake_traversal_grid_sampling() and compute_correlation_matrix() in utils.py

Legal Acknowledgement

By contributing to this software project, I have read the contribution guide and agree to the following terms and conditions for my contribution:

  1. I agree my contributions are submitted under the BSD license.
  2. I represent I am authorized to make the contributions and grant the license. If my employer has rights to intellectual property that includes these contributions, I represent that I have received permission to make contributions and grant the required license on behalf of that employer.

…pected. Now, I need to add cases when some of the design variables are not changed.
… use compute_FIM_metrics() method. Added both the nested for loop and change_one_design_at_a_time argument to compute_FIM_factorial() in doe.py. Everything is working fine.
…gn_values` does not need to be in the same order as the `experiment_inputs` as long as the key matches, we are good. Also, if we don't want to change a design variable, just not passing it as a key in the `design_values` dictionary will do the trick. Tested with the `reactor_example.py`
@smondal13
Copy link
Contributor Author

@adowling2 @djlaky, this draft PR is ready for initial review.

@adowling2
Copy link
Member

Does this PR depend on changes in #3575?

Copy link
Member

@adowling2 adowling2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@smondal13 I left some initial comments.

@@ -0,0 +1,267 @@
# ___________________________________________________________________________
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@smondal13 Why are your creating a new utils file instead of updating the current one? There might be a point of confusion regarding branches in git.

Copy link
Contributor Author

@smondal13 smondal13 Jul 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This issue is resolved now. merged the utils file from my previous PR # 3525.


return self.fim_factorial_results

def compute_FIM_factorial(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this function already in Pyomo.DoE somewhere else?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I created this method. I do not see the same name anywhere else.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adowling2, we have compute_FIM_full_factorial(). I have not changed that method, rather I have added a new method. Maybe we can show a deprecation warning for compute_FIM_full_factorial()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, a depreciation warning sounds reasonable.

# design_map_keys so that design_point can be constructed correctly in the loop.
des_ranges = [design_values[k.name] for k in design_map_keys]
if change_one_design_at_a_time:
factorial_points = generate_snake_zigzag_pattern(*des_ranges)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's brainstorm different sensitivity analysis sequences we might use. We can then define an enum.

@smondal13 smondal13 changed the title Sensitivity initialization in Pyomo. DoE Pyomo. DoE: Sensitivity initialization Jun 25, 2025
@smondal13
Copy link
Contributor Author

smondal13 commented Jul 1, 2025

@adowling2 , @djlaky, @blnicho This PR is ready for design review.

@blnicho blnicho self-requested a review September 16, 2025 19:42

return self.fim_factorial_results

def compute_FIM_factorial(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, a depreciation warning sounds reasonable.

factorial_points = snake_traversal_grid_sampling(*design_values)
elif scheme_enum == SensitivityInitialization.nested_for_loop:
factorial_points = product(*design_values)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to split the function here? That way, we can do extensive testing on the results of factorial_points without running the rest of the function.

----------
model : DoE model, optional
The model to perform the full factorial exploration on. Default: None
design_vals : dict,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After reading this doc string, I do not fully appreciate all of options. I do not think we are looking at all of the edge cases with the tests.

@adowling2
Copy link
Member

@smondal13 As a next step, I suggest you create a Google Doc as a mock-up for the documentation you plan to write for this new feature. In the mock-up, you can provide example usage to show the programmatic interface. I would set the Google Doc to allow for anonymous commenting and post the link here. Alternatively, you could do this in a markdown .md file that you add to this PR. Then, at the next Pyomo dev meeting, you can present the draft documentation and get feedback. This will facilitate the design review process.

@smondal13
Copy link
Contributor Author

To see the Google Doc that shows the documentation and example of this feature, click here

@mrmundt mrmundt requested review from adowling2 and mrmundt January 27, 2026 19:49
Copy link
Member

@adowling2 adowling2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see my requested changes.

traversal_scheme="snake_traversal",
file_name: str = None,
):
"""Will run a simulation-based factorial exploration of the experimental input
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change "will run" to "performs"

----------
model : DoE model, optional
The model to perform the full factorial exploration on. Default: None
design_vals : dict,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mrmundt @blnicho Do you have any suggestions for avoiding string-based names? I know that we want to avoid string-based names, but I do not have any good ideas of how to do that in a way that is convenient for the user.


return self.factorial_results

# TODO: Overhaul plotting functions to not use strings
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still want to overhaul the plotting functions to not use strings? Or do we just want this function to be consistent with our user interface above? In other words, if we use string-based names above, I think it it okay to use string-based names here.

def draw_factorial_figure(
self,
results=None,
sensitivity_design_variables=None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's update this user interface to match the function above. It appears that design variables as specified differently in these two functions. Likewise, this function should do design variable name error checking similar to the function above.



def compute_correlation_matrix(
covariance_matrix, var_name: list = None, significant_digits=3
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make the default for significant digits None.

var_name : list, optional
List of variable names corresponding to the rows/columns of the covariance matrix.
significant_digits : int, optional
Number of significant digits to round the correlation matrix to. Default: 3.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Number of significant digits for rounding entries of the correlation matrix. Default: None (no rounding)

----------
covariance_matrix : numpy.ndarray
2D array representing the covariance matrix.
var_name : list, optional
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if this optional argument is not provided? The doc string does not tell me what happens with this information or the consequences of not providing it.


Parameters
----------
covariance_matrix : numpy.ndarray
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be a square DataFrame? If yes, what type of error checking should we do on the column and row names?

# Set the index to the variable names if provided,
corr_df = (
pd.DataFrame(correlation_matrix, index=var_name, columns=var_name)
if var_name
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this logic doing here?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Ready for design review

Development

Successfully merging this pull request may close these issues.

5 participants