Skip to content

Commit 9076be5

Browse files
committed
..
1 parent e54c2f4 commit 9076be5

File tree

7 files changed

+65
-69
lines changed

7 files changed

+65
-69
lines changed

doc/examples/getting_started_extended/GettingStartedExtended.ipynb

Lines changed: 25 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -152,46 +152,40 @@
152152
"cell_type": "markdown",
153153
"metadata": {},
154154
"source": [
155-
"### Observables\n",
155+
"### Observation model\n",
156156
"\n",
157-
"Specifying observables is beyond the scope of SBML. Here we define them manually.\n",
157+
"Specifying the observation model (i.e., the quantities that are observed, as well as the respective error models) is beyond the scope of SBML. Here we define that manually.\n",
158+
"\n",
159+
"If you are looking for a more scalable way of defining observables, then checkout [PEtab](https://github.com/PEtab-dev/PEtab). Another possibility is using SBML's [`AssignmentRule`s](https://sbml.org/software/libsbml/5.18.0/docs/formatted/python-api/classlibsbml_1_1_assignment_rule.html) to specify model outputs within the SBML file.\n",
158160
"\n",
159-
"If you are looking for a more scalable way for defining observables, then checkout [PEtab](https://github.com/PEtab-dev/PEtab). Another possibility is using SBML's [`AssignmentRule`s](https://sbml.org/software/libsbml/5.18.0/docs/formatted/python-api/classlibsbml_1_1_assignment_rule.html) to specify model outputs within the SBML file."
160-
]
161-
},
162-
{
163-
"cell_type": "code",
164-
"execution_count": 5,
165-
"metadata": {},
166-
"outputs": [],
167-
"source": [
168-
"# Define observables\n",
169-
"observables = {\n",
170-
" \"observable_x1\": {\"name\": \"\", \"formula\": \"x1\"},\n",
171-
" \"observable_x2\": {\"name\": \"\", \"formula\": \"x2\"},\n",
172-
" \"observable_x3\": {\"name\": \"\", \"formula\": \"x3\"},\n",
173-
" \"observable_x1_scaled\": {\"name\": \"\", \"formula\": \"scaling_x1 * x1\"},\n",
174-
" \"observable_x2_offsetted\": {\"name\": \"\", \"formula\": \"offset_x2 + x2\"},\n",
175-
" \"observable_x1withsigma\": {\"name\": \"\", \"formula\": \"x1\"},\n",
176-
"}"
177-
]
178-
},
179-
{
180-
"cell_type": "markdown",
181-
"metadata": {},
182-
"source": [
183-
"### $\\sigma$ parameters\n",
184161
"\n",
185-
"To specify measurement noise as a parameter, we simply provide a dictionary with (preexisting) parameter names as keys and a list of observable names as values to indicate which sigma parameter is to be used for which observable."
162+
"\n",
163+
"For model import in AMICI, the different types of measurements are represented as `MeasurementChannels`.\n",
164+
"The measurement channel is characterized by an ID, an optional name, the observation function (`MeasurementChannels.formula`), the type of noise distribution (`MeasurementChannels.noise_distribution`, defaults to a normal distribution), and the scale parameter of that distribution (`MeasurementChannels.sigma`).\n",
165+
"The symbols used in the observation function and for the scale parameter must already be defined in the model."
186166
]
187167
},
188168
{
189169
"cell_type": "code",
190-
"execution_count": 6,
170+
"execution_count": 5,
191171
"metadata": {},
192172
"outputs": [],
193173
"source": [
194-
"sigmas = {\"observable_x1withsigma\": \"observable_x1withsigma_sigma\"}"
174+
"# Define observation model\n",
175+
"from amici import MeasurementChannel as MC\n",
176+
"\n",
177+
"observation_model = [\n",
178+
" MC(id_=\"observable_x1\", formula=\"x1\"),\n",
179+
" MC(id_=\"observable_x2\", formula=\"x2\"),\n",
180+
" MC(id_=\"observable_x3\", formula=\"x3\"),\n",
181+
" MC(id_=\"observable_x1_scaled\", formula=\"scaling_x1 * x1\"),\n",
182+
" MC(id_=\"observable_x2_offsetted\", formula=\"offset_x2 + x2\"),\n",
183+
" MC(\n",
184+
" id_=\"observable_x1withsigma\",\n",
185+
" formula=\"x1\",\n",
186+
" sigma=\"observable_x1withsigma_sigma\",\n",
187+
" ),\n",
188+
"]"
195189
]
196190
},
197191
{
@@ -340,9 +334,8 @@
340334
" model_name,\n",
341335
" model_output_dir,\n",
342336
" verbose=logging.INFO,\n",
343-
" observables=observables,\n",
337+
" observation_model=observation_model,\n",
344338
" constant_parameters=constant_parameters,\n",
345-
" sigmas=sigmas,\n",
346339
")"
347340
]
348341
},

python/benchmark/benchmark_pysb.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,11 @@
7575
pysb_model,
7676
outdir,
7777
compute_conservation_laws=compute_conservation_laws,
78-
observables=list(pysb_model.observables.keys()),
78+
observation_model=list(
79+
map(
80+
amici.MeasurementChannel, pysb_model.observables.keys()
81+
)
82+
),
7983
)
8084

8185
amici_model_module = amici.import_model_module(

python/sdist/amici/petab/pysb_import.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ def import_model_pysb(
283283
id_=observable.name,
284284
sigma=f"{observable.name}_sigma",
285285
noise_distribution=petab_noise_distribution_to_amici(
286-
observable.get_name(NOISE_DISTRIBUTION)
286+
observable.get(NOISE_DISTRIBUTION)
287287
),
288288
)
289289
for _, observable in petab_problem.observable_df.iterrows()

python/sdist/amici/pysb_import.py

Lines changed: 12 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,7 @@
5555
def pysb2jax(
5656
model: pysb.Model,
5757
output_dir: str | Path | None = None,
58-
observables: list[str] = None,
59-
sigmas: dict[str, str] = None,
60-
noise_distributions: dict[str, str | Callable] | None = None,
58+
observation_model: list[MeasurementChannel] = None,
6159
verbose: int | bool = False,
6260
compute_conservation_laws: bool = True,
6361
simplify: Callable = _default_simplify,
@@ -87,20 +85,16 @@ def pysb2jax(
8785
:param output_dir:
8886
see :meth:`amici.de_export.ODEExporter.set_paths`
8987
90-
:param observables:
91-
list of :class:`pysb.core.Expression` or :class:`pysb.core.Observable`
92-
names in the provided model that should be mapped to observables
93-
94-
:param sigmas:
95-
dict of :class:`pysb.core.Expression` names that should be mapped to
96-
sigmas
97-
98-
:param noise_distributions:
99-
dict with names of observable Expressions as keys and a noise type
100-
identifier, or a callable generating a custom noise formula string
101-
(see :py:func:`amici.import_utils.noise_distribution_to_cost_function`
102-
). If nothing is passed for some observable id, a normal model is
103-
assumed as default.
88+
:param observation_model:
89+
The different measurement channels that make up the observation
90+
model, see also :class:`amici.import_utils.MeasurementChannel`.
91+
The ID is expected to be the name of a :class:`pysb.Expression` or
92+
:class:`pysb.Observable` in the provided model that should be mapped to
93+
an observable.
94+
``sigma`` is expected to be the name of a :class:`pysb.Expression` to
95+
be mapped to the scale parameter of the noise distribution.
96+
``MeasurementChannel.formula`` is expected to be
97+
``None``. Event-observables are not supported.
10498
10599
:param verbose: verbosity level for logging, True/False default to
106100
:attr:`logging.DEBUG`/:attr:`logging.ERROR`
@@ -126,20 +120,12 @@ def pysb2jax(
126120
:param pysb_model_has_obs_and_noise:
127121
if set to ``True``, the pysb model is expected to have extra observables and noise variables added
128122
"""
129-
if observables is None:
130-
observables = []
131-
132-
if sigmas is None:
133-
sigmas = {}
134-
135123
model_name = model_name or model.name
136124

137125
set_log_level(logger, verbose)
138126
ode_model = ode_model_from_pysb_importer(
139127
model,
140-
observables=observables,
141-
sigmas=sigmas,
142-
noise_distributions=noise_distributions,
128+
observation_model=observation_model,
143129
compute_conservation_laws=compute_conservation_laws,
144130
simplify=simplify,
145131
cache_simplify=cache_simplify,

python/tests/conftest.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import amici
88
import pytest
99
from pathlib import Path
10+
11+
from amici import MeasurementChannel
1012
from amici.testing import TemporaryDirectoryWinSafe as TemporaryDirectory
1113

1214

@@ -78,7 +80,7 @@ def pysb_example_presimulation_module():
7880
model,
7981
outdir,
8082
verbose=True,
81-
observables=["pPROT_obs"],
83+
observation_model=[MeasurementChannel("pPROT_obs")],
8284
constant_parameters=constant_parameters,
8385
)
8486

python/tests/test_events.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@
55
import amici
66
import numpy as np
77
import pytest
8-
from amici import import_model_module, SensitivityMethod, SensitivityOrder
8+
from amici import (
9+
import_model_module,
10+
SensitivityMethod,
11+
SensitivityOrder,
12+
MeasurementChannel as MC,
13+
)
914
from amici.antimony_import import antimony2amici
1015
from amici.gradient_check import check_derivatives
1116
from amici.testing import skip_on_valgrind
@@ -1127,11 +1132,7 @@ def test_posteq_events_are_handled(tempdir):
11271132
E1: at time > 1: target = target + bolus
11281133
E2: at some_time >= 2: target = target + bolus
11291134
""",
1130-
observables={
1131-
"obs_target": {
1132-
"formula": "target",
1133-
}
1134-
},
1135+
observation_model=[MC("obs_target", formula="target")],
11351136
model_name=model_name,
11361137
output_dir=tempdir,
11371138
verbose=True,

python/tests/test_pysb.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,12 @@ def test_compare_to_pysb_simulation(example):
185185
outdir,
186186
verbose=logging.INFO,
187187
compute_conservation_laws=compute_conservation_laws,
188-
observables=list(pysb_model.observables.keys()),
188+
observation_model=list(
189+
map(
190+
amici.MeasurementChannel,
191+
pysb_model.observables.keys(),
192+
)
193+
),
189194
)
190195

191196
amici_model_module = amici.import_model_module(
@@ -338,7 +343,12 @@ def test_heavyside_and_special_symbols():
338343
)
339344

340345
with TemporaryDirectoryWinSafe(prefix=model.name) as outdir:
341-
pysb2amici(model, outdir, verbose=True, observables=["a"])
346+
pysb2amici(
347+
model,
348+
outdir,
349+
verbose=True,
350+
observation_model=[amici.MeasurementChannel("a")],
351+
)
342352

343353
model_module = amici.import_model_module(
344354
module_name=model.name, module_path=outdir

0 commit comments

Comments
 (0)