Skip to content

Commit

Permalink
Allow for condition-specific dynamic parameter overrides with differe…
Browse files Browse the repository at this point in the history
…nt scales (Closes #100)
  • Loading branch information
dweindl committed Apr 9, 2019
1 parent 13b67de commit 60a84f0
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 33 deletions.
40 changes: 13 additions & 27 deletions petab/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -534,37 +534,10 @@ def _apply_dynamic_parameter_overrides(mapping,
for condition_idx, overrider_id \
in enumerate(condition_df[overridee_id]):
if isinstance(overridee_id, str):
_check_dynamic_parameter_override(
overridee_id, overrider_id, parameter_df)
mapping[condition_idx][par_sim_id_to_ix[overridee_id]] = \
overrider_id


def _check_dynamic_parameter_override(
overridee_id, overrider_id, parameter_df: pd.DataFrame):
"""Check for valid replacement of parameter overridee_id by overrider_id.
Matching scales, etc."""

if 'parameterScale' not in parameter_df:
return # Nothing to check

# in case both parameters are in parameter table, their scale
# must match.
if overridee_id in parameter_df.index \
and parameter_df.loc[overridee_id, 'parameterScale'] \
!= parameter_df.loc[overrider_id, 'parameterScale']:
raise ValueError(f'Cannot override {overridee_id} with '
f'with {overrider_id} which have '
'different parameterScale.')

# if not, the scale of the overrider must be lin
# (or needs to be unscaled)
if parameter_df.loc[overrider_id, 'parameterScale'] != 'lin':
raise ValueError(f'No scale given for parameter {overridee_id}, '
f'assuming "lin" which does not match scale of '
f'overriding parameter {overrider_id}')


def fill_in_nominal_values(mapping, parameter_df: pd.DataFrame):
"""Replace non-estimated parameters by nominalValues.
Expand All @@ -588,6 +561,19 @@ def fill_in_nominal_values(mapping, parameter_df: pd.DataFrame):
if isinstance(val, str):
try:
mapping[i_condition][i_val] = overrides[val]
# rescale afterwards. if there the parameter is not
# overridden, the previous line raises and we save the
# lookup

# all overrides will be scaled to 'lin'
if 'parameterScale' in parameter_df:
scale = parameter_df.loc[val, 'parameterScale']
if scale == 'log':
mapping[i_condition][i_val] = \
np.exp(mapping[i_condition][i_val])
elif scale == 'log10':
mapping[i_condition][i_val] = \
10**mapping[i_condition][i_val]
except KeyError:
pass

Expand Down
88 changes: 82 additions & 6 deletions tests/test_petab.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ def condition_df_2_conditions():
return condition_df


@pytest.fixture
def minimal_sbml_model():
document = libsbml.SBMLDocument(3, 1)
model = document.createModel()
model.setTimeUnits("second")
model.setExtentUnits("mole")
model.setSubstanceUnits('mole')
return document, model


@pytest.fixture
def petab_problem():
# create test model
Expand Down Expand Up @@ -261,7 +271,7 @@ def test_partial_override(self, condition_df_2_conditions):

assert actual == expected

def test_parameterized_condition_table(self):
def test_parameterized_condition_table(self, minimal_sbml_model):
condition_df = pd.DataFrame(data={
'conditionId': ['condition1', 'condition2', 'condition3'],
'conditionName': ['', 'Condition 2', ''],
Expand All @@ -282,12 +292,9 @@ def test_parameterized_condition_table(self):
'parameterId': ['dynamicOverride1_1', 'dynamicOverride1_2'],
'parameterName': ['', '...'], # ...
})
parameter_df.set_index('parameterId', inplace=True)

document = libsbml.SBMLDocument(3, 1)
model = document.createModel()
model.setTimeUnits("second")
model.setExtentUnits("mole")
model.setSubstanceUnits('mole')
document, model = minimal_sbml_model
model.createParameter().setId('dynamicParameter1')

assert petab.get_model_parameters(model) == ['dynamicParameter1']
Expand All @@ -305,6 +312,75 @@ def test_parameterized_condition_table(self):

assert actual == expected

def test_parameterized_condition_table_changed_scale(
self, minimal_sbml_model):
"""Test overriding a dynamic parameter `overridee` with
- a log10 parameter to be estimated (condition 1)
- lin parameter not estimated (condition2)
- log10 parameter not estimated (condition 3)
- constant override (condition 4)"""

document, model = minimal_sbml_model
model.createParameter().setId('overridee')
assert petab.get_model_parameters(model) == ['overridee']

condition_df = pd.DataFrame(data={
'conditionId':
['condition1', 'condition2', 'condition3', 'condition4'],
'conditionName': '',
'overridee':
['dynamicOverrideLog10', 'fixedOverrideLin',
'fixedOverrideLog10', 10.0]
})
condition_df.set_index('conditionId', inplace=True)

measurement_df = pd.DataFrame(data={
'simulationConditionId':
['condition1', 'condition2', 'condition3', 'condition4'],
'observableId':
['obs1', 'obs2', 'obs1', 'obs2'],
'observableParameters': '',
'noiseParameters': '',
})

parameter_df = pd.DataFrame(data={
'parameterId': ['dynamicOverrideLog10',
'fixedOverrideLin',
'fixedOverrideLog10'],
'parameterName': '',
'estimate': [1, 0, 0],
'nominalValue': [np.nan, 2, -2],
'parameterScale': ['log10', 'lin', 'log10']
})
parameter_df.set_index('parameterId', inplace=True)

actual_par_map = \
petab.get_optimization_to_simulation_parameter_mapping(
measurement_df=measurement_df,
condition_df=condition_df,
parameter_df=parameter_df,
sbml_model=model
)

actual_scale_map = petab.get_optimization_to_simulation_scale_mapping(
parameter_df=parameter_df,
mapping_par_opt_to_par_sim=actual_par_map
)

expected_par_map = [['dynamicOverrideLog10'],
[2.0],
# rescaled:
[0.01],
[10.0]]

expected_scale_map = [['log10'],
['lin'],
['lin'],
['lin']]

assert actual_par_map == expected_par_map
assert actual_scale_map == expected_scale_map


def test_get_observable_id():
assert petab.get_observable_id('observable_obs1') == 'obs1'
Expand Down

0 comments on commit 60a84f0

Please sign in to comment.