diff --git a/petab/v1/simulate.py b/petab/v1/simulate.py index 334929ad..46001a72 100644 --- a/petab/v1/simulate.py +++ b/petab/v1/simulate.py @@ -241,20 +241,39 @@ def sample_noise( simulated_value, ) - # default noise distribution is petab.C.NORMAL - noise_distribution = petab_problem.observable_df.loc[ + observable_row = petab_problem.observable_df.loc[ measurement_row[petab.C.OBSERVABLE_ID] - ].get(petab.C.NOISE_DISTRIBUTION, petab.C.NORMAL) + ] + # default noise distribution is petab.C.NORMAL + noise_distribution = observable_row.get( + petab.C.NOISE_DISTRIBUTION, petab.C.NORMAL + ) # an empty noise distribution column in an observables table can result in # `noise_distribution == float('nan')` if pd.isna(noise_distribution): noise_distribution = petab.C.NORMAL + observable_transformation = observable_row.get( + petab.C.OBSERVABLE_TRANSFORMATION, petab.C.LIN + ) + transform = lambda x: x # noqa: E731 + # observableTransformation=log -> the log of the simulated value is + # distributed according to `noise_distribution` + if observable_transformation == petab.C.LOG: + simulated_value = np.log(simulated_value) + transform = np.exp + elif observable_transformation == petab.C.LOG10: + simulated_value = np.log10(simulated_value) + transform = lambda x: np.power(10, x) # noqa: E731 + # below is e.g.: `np.random.normal(loc=simulation, scale=noise_value)` simulated_value_with_noise = getattr(rng, noise_distribution)( loc=simulated_value, scale=noise_value * noise_scaling_factor ) + # apply observable transformation, ensure `float` type + simulated_value_with_noise = float(transform(simulated_value_with_noise)) + if zero_bounded and np.sign(simulated_value) != np.sign( simulated_value_with_noise ):