Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
e39f42d
update data types
m-philipps May 13, 2026
e7d39e2
add checks for mapping table
m-philipps May 13, 2026
718ceb1
Use SBML in test
m-philipps May 13, 2026
41afda8
rollback
m-philipps May 15, 2026
f907d2b
move from sciml pr
dilpath Jun 19, 2026
ac83767
aliased model logic
BSnelling Jun 22, 2026
c2b90ec
keep FIXME comment
BSnelling Jun 22, 2026
4fb47c3
add hybridization class
m-philipps May 15, 2026
bf961e6
add sciml problem config
m-philipps May 15, 2026
e57426e
create Problem for sciml extension
m-philipps May 25, 2026
0506e17
Assign SciML-specific lint checks
m-philipps May 26, 2026
e9d3cad
Proble.add_hybridization method
m-philipps May 26, 2026
6be22ed
add hybridization df property, setter
m-philipps May 26, 2026
5229ba7
fix
m-philipps May 26, 2026
63adac6
add a basic test for sciml
m-philipps May 26, 2026
bc683a4
methods for adding nn, array data to problem
m-philipps May 26, 2026
4c2c150
Non-local petab_sciml import
m-philipps May 27, 2026
96a5595
add dependency
m-philipps May 27, 2026
0dd5a84
Update petab_sciml dependency to use Git URL
dilpath May 27, 2026
16d928b
neural_nets to neural_networks
BSnelling Jun 11, 2026
99fc71d
add required params check to sciml validations
BSnelling Jun 15, 2026
a5dd0a1
ruff format
BSnelling Jun 15, 2026
1a1f4c6
implement feedback from code review
BSnelling Jun 17, 2026
c6f414b
move sciml code to separate files and resolve circular imports
BSnelling Jun 17, 2026
5d041fa
fixup ruff
BSnelling Jun 17, 2026
48c03ac
fix docs build issue
BSnelling Jun 17, 2026
6f6977d
Update petab/v2/core.py
BSnelling Jun 17, 2026
d4f6a05
add to docstrings
BSnelling Jun 17, 2026
85cdf65
Revert "move sciml code to separate files and resolve circular imports"
BSnelling Jun 18, 2026
e636cd6
use problem.extensions.sciml for separation
BSnelling Jun 18, 2026
1ce17e2
revert mapping table fixes that should be implemented in the other pr
dilpath Jun 19, 2026
22fd309
fix aliased model logic - again
BSnelling Jun 22, 2026
1d7d2c2
move sciml validation to own file
BSnelling Jun 23, 2026
365f1c8
use pypi petab_sciml
BSnelling Jun 23, 2026
c7d3929
feedback from second round review
BSnelling Jun 25, 2026
7a3e0ce
refactor sciml specifics into more helpers
BSnelling Jun 25, 2026
42095f7
fixup from rebase
BSnelling Jun 26, 2026
44cb96c
sciml caveat in parameters validation
BSnelling Jun 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import sys
import warnings

from pydantic import BaseModel

# -- Path setup --------------------------------------------------------------

# If extensions (or modules to document with autodoc) are in another directory,
Expand Down Expand Up @@ -126,6 +128,9 @@ def skip_some_objects(app, what, name, obj, skip, options):
"""Exclude some objects from the documentation"""
if getattr(obj, "__module__", None) == "collections":
return True
# Napoleon + Pydantic v2 bug: BaseModel itself triggers __getattr__ error
if obj is BaseModel:
return True


def setup(app):
Expand Down
1 change: 1 addition & 0 deletions doc/modules.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ API Reference
petab.v2.converters
petab.v2.core
petab.v2.experiments
petab.v2.extensions
petab.v2.lint
petab.v2.models
petab.v2.petab1to2
2 changes: 2 additions & 0 deletions petab/v2/C.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,8 @@
MAPPING_FILES = "mapping_files"
#: Extensions key in the YAML file
EXTENSIONS = "extensions"
#: PEtab SciML extension
EXT_ID_SCIML = "sciml"


# MAPPING
Expand Down
92 changes: 81 additions & 11 deletions petab/v2/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
Annotated,
Any,
Generic,
Literal,
Self,
TypeVar,
get_args,
Expand Down Expand Up @@ -308,6 +309,21 @@ def __iadd__(self, other: T) -> BaseTable[T]:
return self


# SciML extension classes — imported after BaseTable is defined to avoid
# circular imports (sciml.py does not import from core.py).
from .extensions.sciml import ( # noqa: E402
SciMLConfig,
SciMLExt,
)


class ProblemExtensions:
"""Runtime extension state attached to a :class:`Problem`."""

def __init__(self, sciml: SciMLExt = None):
self.sciml: SciMLExt = sciml or SciMLExt()


class Observable(BaseModel):
"""Observable definition."""

Expand Down Expand Up @@ -926,7 +942,8 @@ class Parameter(BaseModel):
)
#: Nominal value.
nominal_value: Annotated[
float | None, BeforeValidator(_convert_nan_to_none)
# PEtab SciML supports arrays via "array" nominal values
float | Literal["array"] | None, BeforeValidator(_convert_nan_to_none)
Comment thread
BSnelling marked this conversation as resolved.
] = Field(alias=C.NOMINAL_VALUE, default=None)
#: Is the parameter to be estimated?
estimate: bool = Field(alias=C.ESTIMATE, default=True)
Expand Down Expand Up @@ -1133,22 +1150,33 @@ def __init__(
measurement_tables: list[MeasurementTable] = None,
parameter_tables: list[ParameterTable] = None,
mapping_tables: list[MappingTable] = None,
extensions: ProblemExtensions = None,
config: ProblemConfig = None,
):
from ..v2.lint import default_validation_tasks
from ..v2.lint import default_validation_tasks, sciml_validation_tasks
Comment thread
BSnelling marked this conversation as resolved.

self.config = config
self.models: list[Model] = models or []
self.validation_tasks: list[ValidationTask] = (
default_validation_tasks.copy()
)
if (
config
and config.extensions
and config.extensions.get(C.EXT_ID_SCIML)
):
self.validation_tasks: list[ValidationTask] = (
sciml_validation_tasks.copy()
)
else:
self.validation_tasks: list[ValidationTask] = (
default_validation_tasks.copy()
)

self.observable_tables = observable_tables or [ObservableTable()]
self.condition_tables = condition_tables or [ConditionTable()]
self.experiment_tables = experiment_tables or [ExperimentTable()]
self.measurement_tables = measurement_tables or [MeasurementTable()]
self.mapping_tables = mapping_tables or [MappingTable()]
self.parameter_tables = parameter_tables or [ParameterTable()]
self.extensions = extensions or ProblemExtensions()

def __repr__(self):
return f"<{self.__class__.__name__} id={self.id!r}>"
Expand Down Expand Up @@ -1321,6 +1349,13 @@ def from_yaml(
else None
)

sciml = (
SciMLExt.from_config(config, base_path)
if config.extensions and config.extensions.get(C.EXT_ID_SCIML)
else None
)
extensions = ProblemExtensions(sciml=sciml)

return Problem(
config=config,
models=models,
Expand All @@ -1330,6 +1365,7 @@ def from_yaml(
measurement_tables=measurement_tables,
parameter_tables=parameter_tables,
mapping_tables=mapping_tables,
extensions=extensions,
)

@staticmethod
Expand Down Expand Up @@ -1940,14 +1976,21 @@ def validate(

validation_results = ValidationResultList()

if self.config and self.config.extensions:
extensions = ",".join(self.config.extensions.keys())
supported_extensions = {C.EXT_ID_SCIML}
if (
self.config
and self.config.extensions
and (self.config.extensions.keys() - supported_extensions)
):
extensions_without_support = ",".join(
self.config.extensions.keys() - supported_extensions
)
validation_results.append(
ValidationIssue(
ValidationIssueSeverity.WARNING,
"Validation of PEtab extensions is not yet implemented, "
"but the given problem uses the following extensions: "
f"{extensions}",
"The given problem uses the following extensions for "
"which validation is not yet implemented: "
f"{extensions_without_support}",
)
)

Expand Down Expand Up @@ -2505,6 +2548,29 @@ class ProblemConfig(BaseModel):
validate_assignment=True,
)

@field_validator("extensions", mode="before")
@classmethod
def _parse_extensions(cls, v):
"""Parse extensions dict and convert known extensions to their specific
config classes."""
if isinstance(v, dict):
parsed_extensions = {}
for ext_name, ext_config in v.items():
if ext_name == C.EXT_ID_SCIML:
parsed_extensions[ext_name] = (
ext_config
if isinstance(ext_config, SciMLConfig)
else SciMLConfig(**ext_config)
)
else:
parsed_extensions[ext_name] = (
ext_config
if isinstance(ext_config, ExtensionConfig)
else ExtensionConfig(**ext_config)
)
return parsed_extensions
return v

# convert parameter_file to list
@field_validator(
"parameter_files",
Expand Down Expand Up @@ -2542,12 +2608,16 @@ def to_yaml(self, filename: str | Path):

for model_id in data.get("model_files", {}):
data["model_files"][model_id][C.MODEL_LOCATION] = str(
data["model_files"][model_id]["location"]
data["model_files"][model_id][C.MODEL_LOCATION]
)
if data["id"] is None:
# The schema requires a valid id or no id field at all.
del data["id"]

for ext_id in list(data[C.EXTENSIONS]):
if ext_id == C.EXT_ID_SCIML:
data[C.EXTENSIONS][ext_id] = self.extensions[ext_id].to_yaml()

write_yaml(data, filename)

@property
Expand Down
Empty file.
Loading
Loading