Skip to content

Commit 980926f

Browse files
dweindldilpath
andauthored
Fix test_priors_to_measurements (#336)
Fixes an issue in `test_priors_to_measurements` which led to evaluating the prior at the wrong parameter values (using the location parameter of the prior instead of the actually estimated parameters). The problem was in the test code, not the tested code. --------- Co-authored-by: Dilan Pathirana <[email protected]>
1 parent 4be03c8 commit 980926f

File tree

1 file changed

+52
-21
lines changed

1 file changed

+52
-21
lines changed

tests/v1/test_priors.py

+52-21
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,15 @@
88
from scipy.stats import norm
99

1010
import petab.v1
11-
from petab.v1 import get_simulation_conditions
11+
from petab.v1 import (
12+
ESTIMATE,
13+
MEASUREMENT,
14+
OBJECTIVE_PRIOR_TYPE,
15+
OBSERVABLE_ID,
16+
SIMULATION,
17+
get_simulation_conditions,
18+
get_simulation_df,
19+
)
1220
from petab.v1.priors import priors_to_measurements
1321

1422

@@ -17,20 +25,31 @@
1725
)
1826
def test_priors_to_measurements(problem_id):
1927
"""Test the conversion of priors to measurements."""
28+
# setup
2029
petab_problem_priors: petab.v1.Problem = (
2130
benchmark_models_petab.get_problem(problem_id)
2231
)
2332
petab_problem_priors.visualization_df = None
2433
assert petab.v1.lint_problem(petab_problem_priors) is False
25-
2634
if problem_id == "Isensee_JCB2018":
2735
# required to match the stored simulation results below
2836
petab.v1.flatten_timepoint_specific_output_overrides(
2937
petab_problem_priors
3038
)
3139
assert petab.v1.lint_problem(petab_problem_priors) is False
40+
3241
original_problem = deepcopy(petab_problem_priors)
42+
# All priors in this test case are defined on parameter scale, hence
43+
# the dummy measurements will take the scaled nominal values.
44+
x_scaled_dict = dict(
45+
zip(
46+
original_problem.x_free_ids,
47+
original_problem.x_nominal_free_scaled,
48+
strict=True,
49+
)
50+
)
3351

52+
# convert priors to measurements
3453
petab_problem_measurements = priors_to_measurements(petab_problem_priors)
3554

3655
# check that the original problem is not modified
@@ -45,6 +64,7 @@ def test_priors_to_measurements(problem_id):
4564
getattr(original_problem, attr)
4665
)
4766
).empty, diff
67+
4868
# check that measurements and observables were added
4969
assert petab.v1.lint_problem(petab_problem_measurements) is False
5070
assert (
@@ -59,6 +79,7 @@ def test_priors_to_measurements(problem_id):
5979
petab_problem_measurements.measurement_df.shape[0]
6080
> petab_problem_priors.measurement_df.shape[0]
6181
)
82+
6283
# ensure we didn't introduce any new conditions
6384
assert len(
6485
get_simulation_conditions(petab_problem_measurements.measurement_df)
@@ -67,26 +88,40 @@ def test_priors_to_measurements(problem_id):
6788
# verify that the objective function value is the same
6889

6990
# load/construct the simulation results
70-
simulation_df_priors = petab.v1.get_simulation_df(
91+
simulation_df_priors = get_simulation_df(
7192
Path(
7293
benchmark_models_petab.MODELS_DIR,
7394
problem_id,
7495
f"simulatedData_{problem_id}.tsv",
7596
)
7697
)
77-
simulation_df_measurements = pd.concat(
78-
[
79-
petab_problem_measurements.measurement_df.rename(
80-
columns={petab.v1.MEASUREMENT: petab.v1.SIMULATION}
81-
)[
82-
petab_problem_measurements.measurement_df[
83-
petab.v1.C.OBSERVABLE_ID
84-
].str.startswith("prior_")
85-
],
86-
simulation_df_priors,
98+
# for the prior observables, we need to "simulate" the model with the
99+
# nominal parameter values
100+
simulated_prior_observables = (
101+
petab_problem_measurements.measurement_df.rename(
102+
columns={MEASUREMENT: SIMULATION}
103+
)[
104+
petab_problem_measurements.measurement_df[
105+
OBSERVABLE_ID
106+
].str.startswith("prior_")
87107
]
88108
)
89109

110+
def apply_parameter_values(row):
111+
# apply the parameter values to the observable formula for the prior
112+
if row[OBSERVABLE_ID].startswith("prior_"):
113+
row[SIMULATION] = x_scaled_dict[
114+
row[OBSERVABLE_ID].removeprefix("prior_")
115+
]
116+
return row
117+
118+
simulated_prior_observables = simulated_prior_observables.apply(
119+
apply_parameter_values, axis=1
120+
)
121+
simulation_df_measurements = pd.concat(
122+
[simulation_df_priors, simulated_prior_observables]
123+
)
124+
90125
llh_priors = petab.v1.calculate_llh_for_table(
91126
petab_problem_priors.measurement_df,
92127
simulation_df_priors,
@@ -102,10 +137,8 @@ def test_priors_to_measurements(problem_id):
102137

103138
# get prior objective function contribution
104139
parameter_ids = petab_problem_priors.parameter_df.index.values[
105-
(petab_problem_priors.parameter_df[petab.v1.ESTIMATE] == 1)
106-
& petab_problem_priors.parameter_df[
107-
petab.v1.OBJECTIVE_PRIOR_TYPE
108-
].notna()
140+
(petab_problem_priors.parameter_df[ESTIMATE] == 1)
141+
& petab_problem_priors.parameter_df[OBJECTIVE_PRIOR_TYPE].notna()
109142
]
110143
priors = petab.v1.get_priors_from_df(
111144
petab_problem_priors.parameter_df,
@@ -117,9 +150,7 @@ def test_priors_to_measurements(problem_id):
117150
prior_type, prior_pars, par_scale, par_bounds = prior
118151
if prior_type == petab.v1.PARAMETER_SCALE_NORMAL:
119152
prior_contrib += norm.logpdf(
120-
petab_problem_priors.x_nominal_free_scaled[
121-
petab_problem_priors.x_free_ids.index(parameter_id)
122-
],
153+
x_scaled_dict[parameter_id],
123154
loc=prior_pars[0],
124155
scale=prior_pars[1],
125156
)
@@ -134,4 +165,4 @@ def test_priors_to_measurements(problem_id):
134165
llh_priors + prior_contrib, llh_measurements, rtol=1e-3, atol=1e-16
135166
), (llh_priors + prior_contrib, llh_measurements)
136167
# check that the tolerance is not too high
137-
assert np.abs(prior_contrib) > 1e-3 * np.abs(llh_priors)
168+
assert np.abs(prior_contrib) > 1e-8 * np.abs(llh_priors)

0 commit comments

Comments
 (0)