-
-
Notifications
You must be signed in to change notification settings - Fork 135
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
ENH: Introducing local sensitivity analysis #575
base: develop
Are you sure you want to change the base?
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #575 +/- ##
===========================================
- Coverage 73.60% 72.01% -1.60%
===========================================
Files 70 72 +2
Lines 10310 10572 +262
===========================================
+ Hits 7589 7613 +24
- Misses 2721 2959 +238 ☔ View full report in Codecov by Sentry. |
…stall to setup.py
58b1cdf
to
1c7f84d
Compare
Tests are not passing
Could you fix it before our review, please? That would help us. @Lucas-Prates |
Sure, I will fix it briefly. This simplified type hinting started at python 3.9. I will make sure the tests pass this time. :P |
Please be aware of #444, we are not supporting type hinting or annotations yet. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why adding a setup.py file?
We are using the pyproject.toml
file now. We no longer support the setup.py
Parameters | ||
---------- | ||
input_filename : str | ||
Input file exported by MonteCarlo class. Each line is a | ||
sample unit described by a dictionary where keys are parameters names | ||
and the values are the sampled parameters values. | ||
|
||
output_filename : str | ||
Output file exported by MonteCarlo.simulate function. Each line is a | ||
sample unit described by a dictionary where keys are target variables | ||
names and the values are the obtained values from the flight simulation. | ||
|
||
parameters_list : list[str] | ||
List of parameters whose values will be extracted. | ||
|
||
target_variables_list : list[str] | ||
List of target variables whose values will be extracted. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Parameters | |
---------- | |
input_filename : str | |
Input file exported by MonteCarlo class. Each line is a | |
sample unit described by a dictionary where keys are parameters names | |
and the values are the sampled parameters values. | |
output_filename : str | |
Output file exported by MonteCarlo.simulate function. Each line is a | |
sample unit described by a dictionary where keys are target variables | |
names and the values are the obtained values from the flight simulation. | |
parameters_list : list[str] | |
List of parameters whose values will be extracted. | |
target_variables_list : list[str] | |
List of target variables whose values will be extracted. | |
Parameters | |
---------- | |
input_filename : str | |
Input file exported by MonteCarlo class. Each line is a | |
sample unit described by a dictionary where keys are parameters names | |
and the values are the sampled parameters values. | |
output_filename : str | |
Output file exported by MonteCarlo.simulate function. Each line is a | |
sample unit described by a dictionary where keys are target variables | |
names and the values are the obtained values from the flight simulation. | |
parameters_list : list[str] | |
List of parameters whose values will be extracted. | |
target_variables_list : list[str] | |
List of target variables whose values will be extracted. | |
Can't have these line skips here. Breaks docs pages
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also happens in a lot of the other functions in this PR
"""Reads MonteCarlo simulation data file and builds parameters and flight | ||
variables matrices from specified |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"from specified..."
Docs not finished here
Returns | ||
------- | ||
parameters_matrix: np.matrix | ||
Numpy matrix contaning input parameters values. Each column correspond | ||
to a parameter in the same order specified by 'parameters_list' input. | ||
|
||
target_variables_matrix: np.matrix | ||
Numpy matrix contaning target variables values. Each column correspond | ||
to a target variable in the same order specified by 'target_variables_list' | ||
input. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Returns | |
------- | |
parameters_matrix: np.matrix | |
Numpy matrix contaning input parameters values. Each column correspond | |
to a parameter in the same order specified by 'parameters_list' input. | |
target_variables_matrix: np.matrix | |
Numpy matrix contaning target variables values. Each column correspond | |
to a target variable in the same order specified by 'target_variables_list' | |
input. | |
Returns | |
------- | |
parameters_matrix: np.matrix | |
Numpy matrix contaning input parameters values. Each column correspond | |
to a parameter in the same order specified by 'parameters_list' input. | |
target_variables_matrix: np.matrix | |
Numpy matrix contaning target variables values. Each column correspond | |
to a target variable in the same order specified by 'target_variables_list' | |
input. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo! containing
I recoomend the installation of this extension: https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker
Returns | |
------- | |
parameters_matrix: np.matrix | |
Numpy matrix contaning input parameters values. Each column correspond | |
to a parameter in the same order specified by 'parameters_list' input. | |
target_variables_matrix: np.matrix | |
Numpy matrix contaning target variables values. Each column correspond | |
to a target variable in the same order specified by 'target_variables_list' | |
input. | |
Returns | |
------- | |
parameters_matrix: np.matrix | |
Numpy matrix containing input parameters values. Each column correspond | |
to a parameter in the same order specified by 'parameters_list' input. | |
target_variables_matrix: np.matrix | |
Numpy matrix containing target variables values. Each column correspond | |
to a target variable in the same order specified by 'target_variables_list' | |
input. |
def __init__( | ||
self, | ||
parameters_names, | ||
target_variables_names, | ||
): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs docs
def fit( | ||
self, | ||
parameters_matrix, | ||
target_data, | ||
): | ||
"""Fits sensitivity model |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any use case where you would define a SensitivityModel
and not call .fit()
immediately afterward?
This seems like something that could/should be inside __init__
def set_parameters_nominal( | ||
self, | ||
parameters_nominal_mean, | ||
parameters_nominal_sd, | ||
): | ||
"""Set parameters nominal mean and standard deviation | ||
|
||
Parameters | ||
---------- | ||
parameters_nominal_mean : np.array | ||
An array contaning the nominal mean for parameters in the | ||
order specified in parameters names at initialization | ||
parameters_nominal_sd : np.array | ||
An array contaning the nominal standard deviation for | ||
parameters in the order specified in parameters names at | ||
initialization | ||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So do you have to set the mean and sd simultaneously?
Also, do you have to set mean and sd of all parameters? Setting for just some of them does not work?
Another thing, to run a Monte Carlo sim, the mean and sd is already given in the Monte Carlo class right? So it would be natural to get them from there automatically
if parameters_matrix.shape[1] != self.n_parameters: | ||
raise ValueError( | ||
"Number of columns (parameters) does not match number of parameters passed at initialization." | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hasn't this already been checked if there is a parameters_matrix
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just a partial review. I could not run your codes to validate it is working, but I suggested a few changes that might improve the quality (i.e. readability) of the code.
@@ -19,6 +19,7 @@ | |||
from cftime import num2pydate | |||
from matplotlib.patches import Ellipse | |||
from packaging import version as packaging_version | |||
import json |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remember to run isort before pushing to origin. You can you the command make isort
in the root directory. THis will ensure the imports are conventionally organized.
Returns | ||
------- | ||
parameters_matrix: np.matrix | ||
Numpy matrix contaning input parameters values. Each column correspond | ||
to a parameter in the same order specified by 'parameters_list' input. | ||
|
||
target_variables_matrix: np.matrix | ||
Numpy matrix contaning target variables values. Each column correspond | ||
to a target variable in the same order specified by 'target_variables_list' | ||
input. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo! containing
I recoomend the installation of this extension: https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker
Returns | |
------- | |
parameters_matrix: np.matrix | |
Numpy matrix contaning input parameters values. Each column correspond | |
to a parameter in the same order specified by 'parameters_list' input. | |
target_variables_matrix: np.matrix | |
Numpy matrix contaning target variables values. Each column correspond | |
to a target variable in the same order specified by 'target_variables_list' | |
input. | |
Returns | |
------- | |
parameters_matrix: np.matrix | |
Numpy matrix containing input parameters values. Each column correspond | |
to a parameter in the same order specified by 'parameters_list' input. | |
target_variables_matrix: np.matrix | |
Numpy matrix containing target variables values. Each column correspond | |
to a target variable in the same order specified by 'target_variables_list' | |
input. |
for i in range(n_parameters): | ||
parameter = parameters_list[i] | ||
parameters_matrix[:, i] = parameters_samples[parameter] | ||
|
||
for i in range(n_variables): | ||
target_variable = target_variables_list[i] | ||
target_variables_matrix[:, i] = target_variables_samples[target_variable] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using enumerate is a more pythonic and readable solution.
for i in range(n_parameters): | |
parameter = parameters_list[i] | |
parameters_matrix[:, i] = parameters_samples[parameter] | |
for i in range(n_variables): | |
target_variable = target_variables_list[i] | |
target_variables_matrix[:, i] = target_variables_samples[target_variable] | |
for i, parameter in enumerate(parameters_list): | |
parameters_matrix[:, i] = parameters_samples[parameter] | |
for i, target_variable in enumerate(target_variables_list): | |
target_variables_matrix[:, i] = target_variables_samples[target_variable] | |
|
||
if number_of_samples_parameters != number_of_samples_variables: | ||
raise ValueError( | ||
"Number of samples for parameters does not match the number of samples for target variables!" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line is too long (over 88 columns), please break it into 2 lines.
"Number of samples for parameters does not match the number of samples for target variables!" | |
"Number of samples for parameters does not match the " | |
"number of samples for target variables!" |
except Exception: | ||
raise Exception( | ||
f"Variable {variable} was not found in {output_filename}!" | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your are using a broad exception catch although you are printing a message that is more related to a particular case of exceptions: the KeyError
.
except Exception: | |
raise Exception( | |
f"Variable {variable} was not found in {output_filename}!" | |
) | |
except KeyError as e: | |
raise KeyError( | |
f"Variable {variable} was not found in {output_filename}!" | |
) from e |
# Auxiliary function that unnests dictionary | ||
def unnest_dict(x): | ||
new_dict = {} | ||
for key, value in x.items(): | ||
# the nested dictionary is inside a list | ||
if isinstance(x[key], list): | ||
# sometimes the object inside the list is another list | ||
# we must skip these cases | ||
if isinstance(value[0], dict): | ||
inner_dict = unnest_dict(value[0]) | ||
inner_dict = { | ||
key + "_" + inner_key: inner_value | ||
for inner_key, inner_value in inner_dict.items() | ||
} | ||
new_dict.update(inner_dict) | ||
else: | ||
new_dict.update({key: value}) | ||
|
||
return new_dict |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would define this function outside the load_monte_carlo_data()
function, this would allow us to re-use this function in other contexts.
Also, the term flatten
is usually used to describe this kid of "parse a nested dictionary" operation. Maybe it would be a good alternative of name.
@@ -49,3 +49,5 @@ | |||
StochasticTail, | |||
StochasticTrapezoidalFins, | |||
) | |||
from .sensitivity import SensitivityModel |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Run isort
|
||
References | ||
---------- | ||
[1] Sobol, Ilya M. "Global sensitivity indices for nonlinear mathematical models and their Monte Carlo estimates." Mathematics and computers in simulation 55.1-3 (2001): 271-280. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Line too long... You can use the \
sign to continue the text in the next line whithout breaking it. Like this:
"something here
continues here, do you see?"
""" | ||
if len(parameters_nominal_mean) != self.n_parameters: | ||
raise ValueError( | ||
"Nominal mean array length does not match number of parameters passed at initilization." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo fix. And line is too long.
"Nominal mean array length does not match number of parameters passed at initilization." | |
"Nominal mean array length does not match number of parameters passed at initialization." |
(same thing happens in a few lines below)
return | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can probally remove this return and the code you keep working.
return |
Pull request type
Checklist
black rocketpy/ tests/
) has passed locallyCurrent behavior
The Sensitivity Analysis notebook teaches the users how to perform the simulations, plot the distribution
of some flight variables (e.g. apogee), and computes the prediction ellipses for the landing point.
New behavior
Our goal is to take sensitivity analysis even further. Briefly, we attempt to answer the following question: Which parameters would reduce the variability of the variable of interest (e.g. apogee) the most if we measured them with greater precision?
To that end, a bit of theory is developed, check the technical document. What was developed resembles the work of [1], a core reference in sensitivity analysis for engineering. His approach is a global sensitivity analysis with a full model containing interaction terms. Our first implementation considers a local sensitivity analysis using only first-order terms.
A quick and dirty test of the functionality of the SensitivityModel class is provided the "sensitivity_model_usage" notebook. This notebook is currently giving weird results! The linear approximations for the variables are, for some reason I still have to figure out, not good enough. This was not happening at previous experimentations that suggested that this approached worked. I have to look carefully at what is happening, but I did not want to delay the PR.
The concepts are discussed in-depth in the "sensitivity_analysis_parameter_importance" notebook (the notebook was not updated to the new SensitivityModel yet!)
Breaking change
Additional information
Technical Document
[1] Sobol, Ilya M. "Global sensitivity indices for nonlinear mathematical models and their Monte Carlo estimates." Mathematics and computers in simulation