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

Add PetabProblem class for handling PEtab-defined simulation con… #2255

Merged
merged 15 commits into from
Jan 16, 2024
Prev Previous commit
Next Next commit
AmiciPetabProblem -> PetabProblem
  • Loading branch information
dweindl committed Jan 16, 2024
commit d8065465a48b069f76b6d579fdab0bbdb879c2a4
119 changes: 23 additions & 96 deletions python/examples/example_petab/petab.ipynb

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions python/sdist/amici/petab/petab_problem.py
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@
from .parameter_mapping import create_parameter_mapping


class AmiciPetabProblem:
class PetabProblem:

Check warning on line 14 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L14

Added line #L14 was not covered by tests
Copy link
Member

Choose a reason for hiding this comment

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

why is codecov not reporting any coverage here?

Copy link
Member Author

Choose a reason for hiding this comment

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

Oops. Because I started putting tests in a separate PEtab directory which was not included in the pytest invocation.

"""Manage experimental conditions based on a PEtab problem definition.

Create :class:`ExpData` objects from a PEtab problem definition, and handle
@@ -33,7 +33,7 @@
memory if the given PEtab problem comprises many simulation conditions.
"""

def __init__(

Check warning on line 36 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L36

Added line #L36 was not covered by tests
self,
petab_problem: petab.Problem,
amici_model: Optional[amici.Model] = None,
@@ -42,57 +42,57 @@
simulation_conditions: Union[pd.DataFrame, list[dict]] = None,
store_edatas: bool = True,
):
self._petab_problem = copy.deepcopy(petab_problem)

Check warning on line 45 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L45

Added line #L45 was not covered by tests

if amici_model is not None:
self._amici_model = amici_model

Check warning on line 48 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L47-L48

Added lines #L47 - L48 were not covered by tests
else:
from .petab_import import import_petab_problem

self._amici_model = import_petab_problem(petab_problem)

self._scaled_parameters = scaled_parameters

Check warning on line 54 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L54

Added line #L54 was not covered by tests

self._simulation_conditions = simulation_conditions or (

Check warning on line 56 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L56

Added line #L56 was not covered by tests
petab_problem.get_simulation_conditions_from_measurement_df()
)
if not isinstance(self._simulation_conditions, pd.DataFrame):
self._simulation_conditions = pd.DataFrame(

Check warning on line 60 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L59-L60

Added lines #L59 - L60 were not covered by tests
self._simulation_conditions
)
if (

Check warning on line 63 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L63

Added line #L63 was not covered by tests
preeq_id := PREEQUILIBRATION_CONDITION_ID
) in self._simulation_conditions:
self._simulation_conditions[

Check warning on line 66 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L66

Added line #L66 was not covered by tests
preeq_id
] = self._simulation_conditions[preeq_id].fillna("")

if problem_parameters is None:

Check warning on line 70 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L70

Added line #L70 was not covered by tests
# Use PEtab nominal values as default
self._problem_parameters = self._default_parameters()
if scaled_parameters is True:

Check warning on line 73 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L72-L73

Added lines #L72 - L73 were not covered by tests
raise NotImplementedError(
"scaled_parameters=True in combination with default "
"parameters is not implemented yet."
)
scaled_parameters = False

Check warning on line 78 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L78

Added line #L78 was not covered by tests
Copy link
Member

Choose a reason for hiding this comment

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

remove?

else:
self._problem_parameters = problem_parameters
self._scaled_parameters = scaled_parameters

Check warning on line 81 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L80-L81

Added lines #L80 - L81 were not covered by tests

if store_edatas:
self._parameter_mapping = create_parameter_mapping(

Check warning on line 84 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L83-L84

Added lines #L83 - L84 were not covered by tests
petab_problem=self._petab_problem,
simulation_conditions=self._simulation_conditions,
scaled_parameters=self._scaled_parameters,
amici_model=self._amici_model,
)
self._create_edatas()

Check warning on line 90 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L90

Added line #L90 was not covered by tests
else:
self._parameter_mapping = None
self._edatas = None

Check warning on line 93 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L92-L93

Added lines #L92 - L93 were not covered by tests

def set_parameters(

Check warning on line 95 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L95

Added line #L95 was not covered by tests
self,
problem_parameters: dict[str, float],
scaled_parameters: bool = False,
@@ -104,38 +104,38 @@
:param scaled_parameters: Whether the provided parameters are on PEtab
`parameterScale` or not.
"""
if scaled_parameters != self._scaled_parameters:

Check warning on line 107 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L107

Added line #L107 was not covered by tests
dweindl marked this conversation as resolved.
Show resolved Hide resolved
# redo parameter mapping if scale changed
self._parameter_mapping = create_parameter_mapping(

Check warning on line 109 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L109

Added line #L109 was not covered by tests
petab_problem=self._petab_problem,
simulation_conditions=self._simulation_conditions,
scaled_parameters=scaled_parameters,
amici_model=self._amici_model,
)

if set(self._problem_parameters) - set(problem_parameters):

Check warning on line 116 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L116

Added line #L116 was not covered by tests
# not all parameters are provided - update
# bring previously set parameters to the same scale if necessary
if scaled_parameters and not self._scaled_parameters:
self._problem_parameters = (

Check warning on line 120 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L119-L120

Added lines #L119 - L120 were not covered by tests
self._petab_problem.scale_parameters(
self._problem_parameters,
)
)
elif not scaled_parameters and self._scaled_parameters:
self._problem_parameters = (

Check warning on line 126 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L125-L126

Added lines #L125 - L126 were not covered by tests
self._petab_problem.unscale_parameters(
self._problem_parameters,
)
)
self._problem_parameters |= problem_parameters

Check warning on line 131 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L131

Added line #L131 was not covered by tests
else:
self._problem_parameters = problem_parameters

Check warning on line 133 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L133

Added line #L133 was not covered by tests

self._scaled_parameters = scaled_parameters

Check warning on line 135 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L135

Added line #L135 was not covered by tests

if self._edatas:
fill_in_parameters(

Check warning on line 138 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L137-L138

Added lines #L137 - L138 were not covered by tests
edatas=self._edatas,
problem_parameters=self._problem_parameters,
scaled_parameters=self._scaled_parameters,
@@ -143,7 +143,7 @@
amici_model=self._amici_model,
)

def get_edata(

Check warning on line 146 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L146

Added line #L146 was not covered by tests
self, condition_id: str, preequilibration_condition_id: str = None
) -> amici.ExpData:
"""Get ExpData object for a given condition.
@@ -151,7 +151,7 @@
NOTE: If ``store_edatas=True`` was passed to the constructor and the
returned object is modified, the changes will be reflected in the
internal `ExpData` objects. Also, if parameter values of
`AmiciPetabProblem` are changed, all `ExpData` objects will be updated.
`PetabProblem` are changed, all `ExpData` objects will be updated.
Create a deep copy if you want to avoid this.

:param condition_id: PEtab condition ID
@@ -159,45 +159,45 @@
:return: ExpData object
"""
# exists or has to be created?
if self._edatas:
edata_id = condition_id
if preequilibration_condition_id:
edata_id += "+" + preequilibration_condition_id

Check warning on line 165 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L162-L165

Added lines #L162 - L165 were not covered by tests

for edata in self._edatas:
if edata.id == edata_id:
return edata

Check warning on line 169 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L167-L169

Added lines #L167 - L169 were not covered by tests

return self._create_edata(condition_id, preequilibration_condition_id)

Check warning on line 171 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L171

Added line #L171 was not covered by tests

def get_edatas(self):

Check warning on line 173 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L173

Added line #L173 was not covered by tests
"""Get all ExpData objects.

NOTE: If ``store_edatas=True`` was passed to the constructor and the
returned objects are modified, the changes will be reflected in the
internal `ExpData` objects. Also, if parameter values of
`AmiciPetabProblem` are changed, all `ExpData` objects will be updated.
`PetabProblem` are changed, all `ExpData` objects will be updated.
Create a deep copy if you want to avoid this.

:return: List of ExpData objects
"""
if self._edatas:

Check warning on line 184 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L184

Added line #L184 was not covered by tests
# shallow copy
return self._edatas.copy()

Check warning on line 186 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L186

Added line #L186 was not covered by tests

# not storing edatas - create and return
self._parameter_mapping = create_parameter_mapping(

Check warning on line 189 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L189

Added line #L189 was not covered by tests
petab_problem=self._petab_problem,
simulation_conditions=self._simulation_conditions,
scaled_parameters=self._scaled_parameters,
amici_model=self._amici_model,
)
self._create_edatas()
result = self._edatas
self._edatas = []
return result

Check warning on line 198 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L195-L198

Added lines #L195 - L198 were not covered by tests

def _create_edata(

Check warning on line 200 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L200

Added line #L200 was not covered by tests
self, condition_id: str, preequilibration_condition_id: str
) -> amici.ExpData:
"""Create ExpData object for a given condition.
@@ -206,7 +206,7 @@
:param preequilibration_condition_id: PEtab preequilibration condition ID
:return: ExpData object
"""
simulation_condition = pd.DataFrame(

Check warning on line 209 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L209

Added line #L209 was not covered by tests
[
{
SIMULATION_CONDITION_ID: condition_id,
@@ -215,12 +215,12 @@
}
]
)
edatas = create_edatas(

Check warning on line 218 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L218

Added line #L218 was not covered by tests
amici_model=self._amici_model,
petab_problem=self._petab_problem,
simulation_conditions=simulation_condition,
)
parameter_mapping = create_parameter_mapping(

Check warning on line 223 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L223

Added line #L223 was not covered by tests
petab_problem=self._petab_problem,
simulation_conditions=simulation_condition,
scaled_parameters=self._scaled_parameters,
@@ -228,7 +228,7 @@
)

# Fill parameters in ExpDatas (in-place)
fill_in_parameters(

Check warning on line 231 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L231

Added line #L231 was not covered by tests
edatas=edatas,
problem_parameters={
p: self._problem_parameters[p]
@@ -240,21 +240,21 @@
amici_model=self._amici_model,
)

if len(edatas) != 1:

Check warning on line 243 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L243

Added line #L243 was not covered by tests
raise AssertionError("Expected exactly one ExpData object.")
return edatas[0]

Check warning on line 245 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L245

Added line #L245 was not covered by tests

def _create_edatas(

Check warning on line 247 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L247

Added line #L247 was not covered by tests
self,
):
"""Create ExpData objects from PEtab problem definition."""
self._edatas = create_edatas(

Check warning on line 251 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L251

Added line #L251 was not covered by tests
amici_model=self._amici_model,
petab_problem=self._petab_problem,
simulation_conditions=self._simulation_conditions,
)

fill_in_parameters(

Check warning on line 257 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L257

Added line #L257 was not covered by tests
edatas=self._edatas,
problem_parameters=self._problem_parameters,
scaled_parameters=self._scaled_parameters,
@@ -262,16 +262,16 @@
amici_model=self._amici_model,
)

def _default_parameters(self) -> dict[str, float]:

Check warning on line 265 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L265

Added line #L265 was not covered by tests
"""Get unscaled default parameters."""
return {

Check warning on line 267 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L267

Added line #L267 was not covered by tests
t.Index: getattr(t, petab.NOMINAL_VALUE)
for t in self._petab_problem.parameter_df[
self._petab_problem.parameter_df[petab.ESTIMATE] == 1
].itertuples()
}

@property
def model(self) -> amici.Model:

Check warning on line 275 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L274-L275

Added lines #L274 - L275 were not covered by tests
"""AMICI model."""
return self._amici_model

Check warning on line 277 in python/sdist/amici/petab/petab_problem.py

Codecov / codecov/patch

python/sdist/amici/petab/petab_problem.py#L277

Added line #L277 was not covered by tests
16 changes: 8 additions & 8 deletions python/tests/petab/test_petab_problem.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
from amici.petab.petab_problem import AmiciPetabProblem
from amici.petab.petab_problem import PetabProblem
from benchmark_models_petab import get_problem


def test_amici_petab_problem_pregenerate():
"""AmiciPetabProblem with pre-generated ExpDatas"""
"""PetabProblem with pre-generated ExpDatas"""
# any example is fine - the only assumption is that we don't have
# preequilibration
petab_problem = get_problem("Boehm_JProteomeRes2014")
app = AmiciPetabProblem(petab_problem, store_edatas=True)
app = PetabProblem(petab_problem, store_edatas=True)

# ensure all edatas are generated upon construction
assert len(app._edatas) == len(
@@ -30,11 +30,11 @@ def test_amici_petab_problem_pregenerate():


def test_amici_petab_problem_on_demand():
"""AmiciPetabProblem with on-demand ExpDatas"""
"""PetabProblem with on-demand ExpDatas"""
# any example is fine - the only assumption is that we don't have
# preequilibration
petab_problem = get_problem("Boehm_JProteomeRes2014")
app = AmiciPetabProblem(petab_problem, store_edatas=False)
app = PetabProblem(petab_problem, store_edatas=False)

# ensure no edatas are generated upon construction
assert not app._edatas
@@ -65,12 +65,12 @@ def test_amici_petab_problem_on_demand():


def test_amici_petab_problem_pregenerate_equals_on_demand():
"""Check that AmiciPetabProblem produces the same ExpDatas
"""Check that PetabProblem produces the same ExpDatas
independent of the `store_edatas` parameter."""
# any example is fine
petab_problem = get_problem("Boehm_JProteomeRes2014")
app_store_true = AmiciPetabProblem(petab_problem, store_edatas=True)
app_store_false = AmiciPetabProblem(petab_problem, store_edatas=False)
app_store_true = PetabProblem(petab_problem, store_edatas=True)
app_store_false = PetabProblem(petab_problem, store_edatas=False)

parameter_update = {app_store_true.model.getParameterIds()[0]: 0.12345}
app_store_true.set_parameters(parameter_update, scaled_parameters=True)