Skip to content
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

Predictions #544

Merged
merged 52 commits into from
Jan 13, 2021
Merged

Predictions #544

merged 52 commits into from
Jan 13, 2021

Conversation

paulstapor
Copy link
Contributor

This is a concept for predictions within pyPESTO. I pushed this branch for a PR now, although 2 things are still missing:

  • I want to reuse the existing conversion reaction model in the unit tests (and modify it using libsbml inplace) rather than pushing a slightly altered model into the conversion reaction folder. I will remove the current extended conversion reaction model then, but I added to have the tests running-
  • I will add a wrapper for PEtab models, which uses the current framework, but makes things easier for the user, if they wirk with PEtab.

Yet, it's just before christmas and I wanted to have things pushed as I think most corner stones are set and we may want to discuss whether the overall concept is okay.

Why this whole concept of predictions? It's for four reasons:

  • I want and need something more flexible than just the AMICI output with the list of rdatas.
  • I added some functionality, which allows to use predictions also for large models and large datasets. For this purpose, the prediction class is just a very thin wrapper around some ndarrays.
  • This will be integrated into the concepts of Ensembles/Ensemble Predictions, which is next on my list and also particularly suited for large-scale models (which I also need somewhat urgently)
  • We will need something like predictions, if we ever want to implement prediction uncertainty in pyPESTO, which I think is very reasonable to have.

@codecov-io
Copy link

codecov-io commented Jan 7, 2021

Codecov Report

Merging #544 (c70a47d) into develop (71722e5) will decrease coverage by 6.35%.
The diff coverage is 29.83%.

Impacted file tree graph

@@             Coverage Diff             @@
##           develop     #544      +/-   ##
===========================================
- Coverage    90.43%   84.07%   -6.36%     
===========================================
  Files           65       69       +4     
  Lines         4225     4403     +178     
===========================================
- Hits          3821     3702     -119     
- Misses         404      701     +297     
Impacted Files Coverage Δ
pypesto/prediction/amici_predictor.py 14.08% <14.08%> (ø)
pypesto/prediction/prediction.py 22.50% <22.50%> (ø)
pypesto/petab/importer.py 76.64% <42.85%> (-4.28%) ⬇️
pypesto/objective/amici.py 89.75% <100.00%> (-1.10%) ⬇️
pypesto/objective/base.py 87.71% <100.00%> (ø)
pypesto/prediction/__init__.py 100.00% <100.00%> (ø)
pypesto/prediction/constants.py 100.00% <100.00%> (ø)
pypesto/petab/pysb_importer.py 0.00% <0.00%> (-100.00%) ⬇️
pypesto/startpoint/util.py 50.00% <0.00%> (-35.00%) ⬇️
pypesto/optimize/optimizer.py 57.14% <0.00%> (-33.70%) ⬇️
... and 12 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 71722e5...c70a47d. Read the comment docs.

Copy link
Member

@dilpath dilpath left a comment

Choose a reason for hiding this comment

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

Looks good 👍 Making states available to the user would be useful for state uncertainties.

The notebook fails during optimization.

doc/example/conversion_reaction/conditions.tsv Outdated Show resolved Hide resolved
pypesto/objective/amici.py Outdated Show resolved Hide resolved
pypesto/objective/amici.py Outdated Show resolved Hide resolved
pypesto/objective/base.py Outdated Show resolved Hide resolved
pypesto/objective/amici_prediction.py Outdated Show resolved Hide resolved
# loop over parameters
for i_par in range(output_sensi[i_out].shape[1]):
# create filename for this condition and parameter
tmp_filename = os.path.join(output_path, output_file_dummy +
Copy link
Member

Choose a reason for hiding this comment

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

Could use the Path object here too.

pypesto/objective/amici_prediction.py Outdated Show resolved Hide resolved
pypesto/objective/amici_prediction.py Outdated Show resolved Hide resolved
test/petab/test_amici_prediction.py Outdated Show resolved Hide resolved
test/petab/test_amici_prediction.py Outdated Show resolved Hide resolved
paulstapor and others added 12 commits January 8, 2021 09:50
Co-authored-by: Dilan Pathirana <59329744+dilpath@users.noreply.github.com>
Co-authored-by: Dilan Pathirana <59329744+dilpath@users.noreply.github.com>
Co-authored-by: Dilan Pathirana <59329744+dilpath@users.noreply.github.com>
Co-authored-by: Dilan Pathirana <59329744+dilpath@users.noreply.github.com>
Co-authored-by: Dilan Pathirana <59329744+dilpath@users.noreply.github.com>
Co-authored-by: Dilan Pathirana <59329744+dilpath@users.noreply.github.com>
Co-authored-by: Dilan Pathirana <59329744+dilpath@users.noreply.github.com>
Co-authored-by: Dilan Pathirana <59329744+dilpath@users.noreply.github.com>
outputs_sensi = amici_sy
timepoints = amici_t
if self.post_processor is not None:
outputs = self.post_processor(outputs)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
outputs = self.post_processor(outputs)
outputs = self.post_processor(amici_y)

No change in logic, but better consistency with code below.

Copy link
Contributor

Choose a reason for hiding this comment

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

One important aspect here though is that this code may have issues if the user-provided post processing routines apply their changes in-place. May want to add documentation about this or add safeguards in the code.

Copy link
Contributor Author

@paulstapor paulstapor Jan 8, 2021

Choose a reason for hiding this comment

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

Will change that a bit anyway, as this part of the code isn't really what I was aiming for. But yes, this is an important suggestion, will do so

pypesto/prediction/amici_predictor.py Outdated Show resolved Hide resolved
n_simulations = 1
else:
# simulate only a subset of conditions
n_simulations = 1 + int(len(self.amici_objective.edatas) /
Copy link
Contributor

Choose a reason for hiding this comment

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

Would feel more comfortable with an explicit call to ceil here.

of the simulations. Default are the timepoints of the amici model.
This method takes a list of ndarrays (as returned in the field
['t'] of amici ReturnData objects) as input.
max_num_conditions:
Copy link
Contributor

Choose a reason for hiding this comment

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

I am not a fan of the name, since its not really a max. What about condition_chunk_size?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

chunk_size is the word I was looking for and didn't find... :D 👍

def conversion_reaction_model():
# read in sbml file
model_name = 'conversion_reaction'
sbml_file = f'../../doc/example/{model_name}/model_{model_name}.xml'
Copy link
Contributor

Choose a reason for hiding this comment

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

please use os.path.dirname and os.path.join

@@ -21,6 +21,10 @@
Objective,
NegLogPriors,
ObjectiveBase)
from .prediction import (
Copy link
Member

Choose a reason for hiding this comment

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

Please add pypesto.prediction to API documentation (update sphinx main file).



def test_petab_prediction():
yaml_file = '../../doc/example/conversion_reaction/' \
Copy link
Member

Choose a reason for hiding this comment

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

Please use relative path based on __file__ to make tests run from different working directories.

@paulstapor
Copy link
Contributor Author

All Comments should be addressed now. In particular, the changes I did now were:

  • refactoring the routine which actually calls amici, such that the actual call is more encapsulated to make sure that memory is freed
  • now states, observables and timepoints can be used in all postprocessing routines (passed via a list of dicts): This will allow an extension by an objective function on the prediction, and hence prediction/validation profiles to be implemented easily
  • using pathlib and nicer path handling
  • separating prediction results (also those routines which write the result to csv's or an h5) from the actual predictor
  • using the conversion reaction example sbml as it is, as modifying it causes trouble in too many other scripts
  • some cleanup in documentation and code

@paulstapor
Copy link
Contributor Author

Fine to merge, or any further comments, remarks?

@dweindl
Copy link
Member

dweindl commented Jan 12, 2021

Didn't get through everything, but two reviewers are enough for me. GTG.

Copy link
Member

@dilpath dilpath left a comment

Choose a reason for hiding this comment

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

Looks good, fine for me, just a few suggestions but they're not so important I think.


from .constants import (MODE_FUN, OBSERVABLE_IDS, TIMEPOINTS, OUTPUT,
OUTPUT_SENSI, TIME, CSV, H5, T, Y, SY, RDATAS)
OUTPUT_SENSI, CSV, H5, T, X, SX, Y, SY, RDATAS)
Copy link
Member

Choose a reason for hiding this comment

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

In general, I think these constants should be unabbreviated and as unambiguous as possible (as in PEtab). e.g. OUTPUT_SENSI -> OUTPUT_SENSITIVITY, and X -> STATE. In particular, STATE instead of X might be important here since x is used for parameters elsewhere in pyPESTO, so if a constant is added at some point to represent parameters, it might also be PARAMETER instead of X too.

Copy link
Member

Choose a reason for hiding this comment

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

Regarding OUTPUT_SENSITIVITY and others, should some of these constants be imported from AMICI? For now, this is fine as is for me.

Copy link
Member

Choose a reason for hiding this comment

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

Another alternative, could be renamed e.g. X to AMICI_X or AMICI_STATE

Copy link
Contributor

Choose a reason for hiding this comment

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

👍

Comment on lines 157 to 159
amici_outputs:
List of Dicts with the fields 't', 'x', 'sx', 'y', and 'sy' as
returned by an amici simulation
Copy link
Member

Choose a reason for hiding this comment

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

I guess this can be removed now

timepoints = [outputs['t'] for outputs in amici_outputs]
# add outputs and sensitivities if requested
if 0 in sensi_orders:
outputs = [outputs['y'] for outputs in amici_outputs]
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
outputs = [outputs['y'] for outputs in amici_outputs]
outputs = [amici_output['y'] for amici_output in amici_outputs]

A little confusing without understanding the scope, since outputs would be on both sides of the assignment operator.

Copy link
Member

Choose a reason for hiding this comment

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

Also could rename elsewhere, where outputs is used to refer to elements of amici_outputs and not the outputs variable.

result.to_csv(tmp_filename, sep='\t')
# postprocess
if self.post_processor is not None:
outputs = self.post_processor(amici_outputs)
Copy link
Member

Choose a reason for hiding this comment

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

outputs is overwritten here. If intended, then the above code that previously assigned a value to outputs (and similarly outputs_sensi) could be moved to be elif of these conditions, as e.g.

        # add outputs and sensitivities if requested and post-processing was skipped
        if self.post_processor is not None:
            ...
        elif 0 in sensi_orders:
            outputs = [amici_output['y'] for amici_output in amici_outputs]
        if self.post_processor_sensi is not None:
            ...
        elif 1 in sensi_orders:
            outputs_sensi = [amici_output['sy'] for amici_output in amici_outputs]

Copy link
Member

Choose a reason for hiding this comment

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

Could have the post-processors be a dictionary such that any output can be processed, e.g.

AMICI_TIME = 't'
AMICI_STATE = 'x'
AMICI_OBSERVABLE = 'y'

# user input
post_processors = {
    AMICI_OBSERVABLE: my_custom_post_processor_callable,
    'sy': my_custom_post_processor_sensi_callable,
}

# default processor
default_post_processors = {
    AMICI_TIME: lambda t: t,
    AMICI_STATE: lambda x: x,
    AMICI_OBSERVABLE: lambda y: y,
    'sy': lambda sy: sy,
}

output_types = amici_outputs[0].keys()

# actual processing
outputs = {}
for output_type in output_types:
    outputs[output_type] = None
    if output_type in post_processors:
        outputs[output_type] = post_processors[output_type](amici_outputs)  # deepcopy amici_outputs?
    elif output_type in default_post_processors:
        outputs[output_type] = default_post_processors[output_type](amici_outputs)  # deepcopy amici_outputs?

pypesto/prediction/prediction.py Outdated Show resolved Hide resolved
pypesto/prediction/prediction.py Outdated Show resolved Hide resolved
paulstapor and others added 2 commits January 12, 2021 13:45
Co-authored-by: Dilan Pathirana <59329744+dilpath@users.noreply.github.com>
Co-authored-by: Dilan Pathirana <59329744+dilpath@users.noreply.github.com>
pypesto/petab/importer.py Outdated Show resolved Hide resolved
pypesto/prediction/amici_predictor.py Outdated Show resolved Hide resolved
# return dependent on sensitivity order
return results

def _get_outputs(self, x, sensi_orders, mode) -> Tuple[List, List, List]:
Copy link
Contributor

Choose a reason for hiding this comment

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

typehints?

@paulstapor paulstapor merged commit 24f3ce3 into develop Jan 13, 2021
@paulstapor paulstapor deleted the predictions branch January 13, 2021 09:26
@yannikschaelte yannikschaelte mentioned this pull request Jan 19, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants