Skip to content

Commit 101c79c

Browse files
authored
Update petab.v2 (#361)
* Adapt petab.v2 to the updated specs draft. * Make the new pydantic objects the primary objects and generate the DataFrames only on demand. Validation is still a bit rough. We'll need nicer error messages and pydantic settings are too lax for validating files.
1 parent 5b47448 commit 101c79c

File tree

16 files changed

+1569
-984
lines changed

16 files changed

+1569
-984
lines changed

doc/modules.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ API Reference
1616
petab.v1.core
1717
petab.v1.distributions
1818
petab.v1.lint
19+
petab.v1.math
1920
petab.v1.measurements
2021
petab.v1.models
2122
petab.v1.observables

petab/v1/lint.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@
5353
"observable_table_has_nontrivial_noise_formula",
5454
]
5555

56+
#: Regular expression pattern for valid PEtab IDs
57+
_petab_id_pattern = re.compile(r"^[a-zA-Z_]\w*$")
58+
5659

5760
def _check_df(df: pd.DataFrame, req_cols: Iterable, name: str) -> None:
5861
"""Check if given columns are present in DataFrame
@@ -1041,10 +1044,13 @@ def assert_model_parameters_in_condition_or_parameter_table(
10411044
mapping_df[MODEL_ENTITY_ID],
10421045
strict=True,
10431046
)
1044-
# mapping table entities mapping to already allowed parameters
1045-
if to_id in allowed_in_condition_cols
1046-
# mapping table entities mapping to species
1047-
or model.is_state_variable(to_id)
1047+
if not pd.isna(to_id)
1048+
and (
1049+
# mapping table entities mapping to already allowed parameters
1050+
to_id in allowed_in_condition_cols
1051+
# mapping table entities mapping to species
1052+
or model.is_state_variable(to_id)
1053+
)
10481054
}
10491055

10501056
allowed_in_parameter_table = (
@@ -1186,7 +1192,7 @@ def is_valid_identifier(x: str) -> bool:
11861192
if pd.isna(x):
11871193
return False
11881194

1189-
return re.match(r"^[a-zA-Z_]\w*$", x) is not None
1195+
return _petab_id_pattern.match(x) is not None
11901196

11911197

11921198
def check_ids(ids: Iterable[str], kind: str = "") -> None:

petab/v1/math/sympify.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515
def sympify_petab(expr: str | int | float) -> sp.Expr | sp.Basic:
1616
"""Convert PEtab math expression to sympy expression.
1717
18+
.. note::
19+
20+
All symbols in the returned expression will have the `real=True`
21+
assumption.
22+
1823
Args:
1924
expr: PEtab math expression.
2025
@@ -26,14 +31,22 @@ def sympify_petab(expr: str | int | float) -> sp.Expr | sp.Basic:
2631
The sympy expression corresponding to `expr`.
2732
Boolean values are converted to numeric values.
2833
"""
34+
if isinstance(expr, sp.Expr):
35+
# TODO: check if only PEtab-compatible symbols and functions are used
36+
return expr
37+
2938
if isinstance(expr, int) or isinstance(expr, np.integer):
3039
return sp.Integer(expr)
3140
if isinstance(expr, float) or isinstance(expr, np.floating):
3241
return sp.Float(expr)
3342

34-
# Set error listeners
35-
input_stream = InputStream(expr)
43+
try:
44+
input_stream = InputStream(expr)
45+
except TypeError as e:
46+
raise TypeError(f"Error parsing {expr!r}: {e.args[0]}") from e
47+
3648
lexer = PetabMathExprLexer(input_stream)
49+
# Set error listeners
3750
lexer.removeErrorListeners()
3851
lexer.addErrorListener(MathErrorListener())
3952

petab/v2/C.py

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -125,28 +125,14 @@
125125

126126
#: Condition ID column in the condition table
127127
CONDITION_ID = "conditionId"
128-
# TODO: removed?
129-
#: Condition name column in the condition table
130-
CONDITION_NAME = "conditionName"
131128
#: Column in the condition table with the ID of an entity that is changed
132129
TARGET_ID = "targetId"
133-
#: Column in the condition table with the operation type
134-
OPERATION_TYPE = "operationType"
135130
#: Column in the condition table with the new value of the target entity
136131
TARGET_VALUE = "targetValue"
137-
# operation types:
138-
OT_CUR_VAL = "setCurrentValue"
139-
OT_NO_CHANGE = "noChange"
140-
141-
OPERATION_TYPES = [
142-
OT_CUR_VAL,
143-
OT_NO_CHANGE,
144-
]
145132

146133
CONDITION_DF_COLS = [
147134
CONDITION_ID,
148135
TARGET_ID,
149-
OPERATION_TYPE,
150136
TARGET_VALUE,
151137
]
152138

@@ -161,25 +147,25 @@
161147

162148
# OBSERVABLES
163149

164-
#: Observable name column in the observables table
150+
#: Observable name column in the observable table
165151
OBSERVABLE_NAME = "observableName"
166-
#: Observable formula column in the observables table
152+
#: Observable formula column in the observable table
167153
OBSERVABLE_FORMULA = "observableFormula"
168-
#: Noise formula column in the observables table
154+
#: Noise formula column in the observable table
169155
NOISE_FORMULA = "noiseFormula"
170-
#: Observable transformation column in the observables table
156+
#: Observable transformation column in the observable table
171157
OBSERVABLE_TRANSFORMATION = "observableTransformation"
172-
#: Noise distribution column in the observables table
158+
#: Noise distribution column in the observable table
173159
NOISE_DISTRIBUTION = "noiseDistribution"
174160

175-
#: Mandatory columns of observables table
161+
#: Mandatory columns of observable table
176162
OBSERVABLE_DF_REQUIRED_COLS = [
177163
OBSERVABLE_ID,
178164
OBSERVABLE_FORMULA,
179165
NOISE_FORMULA,
180166
]
181167

182-
#: Optional columns of observables table
168+
#: Optional columns of observable table
183169
OBSERVABLE_DF_OPTIONAL_COLS = [
184170
OBSERVABLE_NAME,
185171
OBSERVABLE_TRANSFORMATION,
@@ -382,14 +368,17 @@
382368
PETAB_ENTITY_ID = "petabEntityId"
383369
#: Model entity ID column in the mapping table
384370
MODEL_ENTITY_ID = "modelEntityId"
371+
#: Arbitrary name
372+
NAME = "name"
373+
385374
#: Required columns of the mapping table
386375
MAPPING_DF_REQUIRED_COLS = [PETAB_ENTITY_ID, MODEL_ENTITY_ID]
387376

388377
# MORE
389378

390379
#: Simulated value column in the simulation table
391380
SIMULATION = "simulation"
392-
#: Residual value column in the residuals table
381+
#: Residual value column in the residual table
393382
RESIDUAL = "residual"
394383
#: ???
395384
NOISE_VALUE = "noiseValue"

petab/v2/__init__.py

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,31 @@
55

66
from warnings import warn
77

8-
# TODO: remove v1 star imports
9-
from ..v1.calculate import * # noqa: F403, F401, E402
10-
from ..v1.composite_problem import * # noqa: F403, F401, E402
11-
from ..v1.core import * # noqa: F403, F401, E402
12-
from ..v1.format_version import __format_version__ # noqa: F401, E402
13-
from ..v1.mapping import * # noqa: F403, F401, E402
14-
from ..v1.measurements import * # noqa: F403, F401, E402
15-
from ..v1.observables import * # noqa: F403, F401, E402
16-
from ..v1.parameter_mapping import * # noqa: F403, F401, E402
17-
from ..v1.parameters import * # noqa: F403, F401, E402
18-
from ..v1.sampling import * # noqa: F403, F401, E402
19-
from ..v1.sbml import * # noqa: F403, F401, E402
20-
from ..v1.simulate import * # noqa: F403, F401, E402
21-
from ..v1.yaml import * # noqa: F403, F401, E402
22-
238
warn(
249
"Support for PEtab2.0 and all of petab.v2 is experimental "
2510
"and subject to changes!",
2611
stacklevel=1,
2712
)
2813

14+
# TODO: move this module to v2
15+
from petab.v1.mapping import ( # noqa: F403, F401, E402
16+
get_mapping_df,
17+
write_mapping_df,
18+
)
19+
from petab.v1.measurements import ( # noqa: F401, E402
20+
get_measurement_df,
21+
write_measurement_df,
22+
)
23+
from petab.v1.observables import ( # noqa: F401, E402
24+
get_observable_df,
25+
write_observable_df,
26+
)
27+
from petab.v1.parameters import ( # noqa: F401, E402
28+
get_parameter_df,
29+
write_parameter_df,
30+
)
31+
from petab.v1.yaml import load_yaml # noqa: F401, E402
32+
2933
# import after v1
3034
from ..version import __version__ # noqa: F401, E402
3135
from . import ( # noqa: F401, E402
@@ -38,5 +42,5 @@
3842
write_experiment_df,
3943
)
4044
from .lint import lint_problem # noqa: F401, E402
41-
from .models import Model # noqa: F401, E402
42-
from .problem import Problem # noqa: F401, E402
45+
from .models import MODEL_TYPE_PYSB, MODEL_TYPE_SBML, Model # noqa: F401, E402
46+
from .problem import Problem, ProblemConfig # noqa: F401, E402

petab/v2/conditions.py

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,8 @@
55
from pathlib import Path
66

77
import pandas as pd
8-
import sympy as sp
98

10-
from .. import v2
11-
from ..v1.math import sympify_petab
12-
from .C import *
13-
from .lint import assert_no_leading_trailing_whitespace
9+
from ..v1.lint import assert_no_leading_trailing_whitespace
1410

1511
__all__ = [
1612
"get_condition_df",
@@ -50,19 +46,3 @@ def write_condition_df(df: pd.DataFrame, filename: str | Path) -> None:
5046
"""
5147
df = get_condition_df(df)
5248
df.to_csv(filename, sep="\t", index=False)
53-
54-
55-
def get_condition_table_free_symbols(problem: v2.Problem) -> set[sp.Basic]:
56-
"""Free symbols from condition table assignments.
57-
58-
Collects all free symbols from the condition table `targetValue` column.
59-
60-
:returns: Set of free symbols.
61-
"""
62-
if problem.condition_df is None:
63-
return set()
64-
65-
free_symbols = set()
66-
for target_value in problem.condition_df[TARGET_VALUE]:
67-
free_symbols |= sympify_petab(target_value).free_symbols
68-
return free_symbols

0 commit comments

Comments
 (0)