You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When fitting an SBML or Antimony model on the new-era experiment: / data:
surface, PyBNF's SBML simulation path ignores the experiment's measurement times
and instead simulates on a uniform grid (t_span=(0, t_end), n_points). As a
result, the fit only works when the data's independent-variable values happen to
land on that uniform grid (e.g. integer times up to t_end); a data point at a
non-grid time (e.g. t = 0.5) is never in the simulation output, and scoring
fails.
This is a PyBNF bug, not a bngsim bug — bngsim already supports arbitrary output
times, and the native BNGL path already uses them; only the SBML/Antimony wrapper
drops them.
Experimental data includes time=0.5, but that time is not in the simulation output.
Real CLI: every evaluation scores inf (the failed match is swallowed into an
infinite objective), so the fit "completes" at objective = inf and recovers
nothing.
The same model/data on bngl_backend = bngsim (native BNGL) fits and recovers
correctly at the identical non-integer times.
Root cause
The TimeCourse action already carries the experiment's measurement grid: TimeCourse.__init__(self, d, explicit_points=None) — "Optional iterable of
independent-variable (time) values at which the simulation should output, derived
from an experiment's data (ADR-0028)" (pybnf/pset.py).
The SBML/Antimony path does not: pybnf/bngsim_sbml_model.py:879-884
handles a TimeCourse by calling self._run_simulation(engine_model, act.time, act.stepnumber + 1, ...), and _run_simulation runs sim.run(t_span=(0.0, end_time), n_points=int(n_points)) — a uniform grid from act.time/act.stepnumber. The action's explicit_points (the actual data
times) are never threaded in.
Reproduction
Model (decay.ant):
model decay
species A = 100, B = 0;
k = 0.5;
conv: A -> B; k*A;
end
Conf (fit.conf):
edition = 2
model: decay.ant
sbml_backend = bngsim
job_type = de
objective = sos
observable: Obs_A, formula: A
experiment: timecourse, data: decay.exp
uniform_var = k 0.05 3.0
population_size = 20
max_iterations = 40
decay.exp with integer times 0,1,…,8 (Obs_A = 100*exp(-0.5 t)) → recovers k = 0.5. ✅
decay.exp with any non-integer time (e.g. add t = 0.5) → fails as above. ❌
The committed tutorial lesson examples/tutorial/11_interop/ is exactly this model
in BNGL/SBML/Antimony; it deliberately uses an integer grid to sidestep the bug, and
its README documents the limitation. It's a ready-made regression fixture: flip its
grid to non-integer and the two SBML/Antimony confs should still recover k once
this is fixed.
Suggested fix
In bngsim_sbml_model.py, thread the TimeCourse action's explicit_points (the
measurement grid) into the simulation, i.e. pass sample_times=... to sim.run(...) in _run_simulation (mirroring net_model.py's BNGL path) instead
of a uniform n_points grid when the action carries explicit points. No bngsim
change is required. (The ParamScan path at bngsim_sbml_model.py:899 has the same
uniform-grid assumption over linspace(min, max, stepnumber+1) and is worth
checking in the same pass.)
Not a bngsim bug
bngsim.Simulator.run already accepts sample_times; the engine supports arbitrary
output times. The gap is entirely in PyBNF's SBML/Antimony wrapper not passing the
grid it already holds.
Context
Supersedes Separate Simulations directory option #251, which I filed on a mistaken premise (blamed RoadRunner and
wrongly claimed no .ant/.xml recovery example) and closed. This is the
correctly-scoped version.
PyBNF main @ d6a84ad; bngsim 0.9.67; petab 0.8.2.
Surfaced while building the edition-2 feature tutorial (examples/tutorial/11_interop/).
Summary
When fitting an SBML or Antimony model on the new-era
experiment:/data:surface, PyBNF's SBML simulation path ignores the experiment's measurement times
and instead simulates on a uniform grid (
t_span=(0, t_end),n_points). As aresult, the fit only works when the data's independent-variable values happen to
land on that uniform grid (e.g. integer times up to
t_end); a data point at anon-grid time (e.g.
t = 0.5) is never in the simulation output, and scoringfails.
This is a PyBNF bug, not a bngsim bug — bngsim already supports arbitrary output
times, and the native BNGL path already uses them; only the SBML/Antimony wrapper
drops them.
Symptom
pybnf/objective.py:324inf(the failed match is swallowed into aninfinite objective), so the fit "completes" at
objective = infand recoversnothing.
The same model/data on
bngl_backend = bngsim(native BNGL) fits and recoverscorrectly at the identical non-integer times.
Root cause
TimeCourseaction already carries the experiment's measurement grid:TimeCourse.__init__(self, d, explicit_points=None)— "Optional iterable ofindependent-variable (time) values at which the simulation should output, derived
from an experiment's data (ADR-0028)" (
pybnf/pset.py).bngsim.Simulator.run(..., sample_times: list[float] | None = None, ...)(bngsim0.9.67).
pybnf/bngsim_model/net_model.py:816-819calls
plan.sim.run(..., sample_times=plan.sample_times).pybnf/bngsim_sbml_model.py:879-884handles a
TimeCourseby callingself._run_simulation(engine_model, act.time, act.stepnumber + 1, ...), and_run_simulationrunssim.run(t_span=(0.0, end_time), n_points=int(n_points))— a uniform grid fromact.time/act.stepnumber. The action'sexplicit_points(the actual datatimes) are never threaded in.
Reproduction
Model (
decay.ant):Conf (
fit.conf):decay.expwith integer times0,1,…,8(Obs_A = 100*exp(-0.5 t)) → recoversk = 0.5. ✅decay.expwith any non-integer time (e.g. addt = 0.5) → fails as above. ❌The committed tutorial lesson
examples/tutorial/11_interop/is exactly this modelin BNGL/SBML/Antimony; it deliberately uses an integer grid to sidestep the bug, and
its README documents the limitation. It's a ready-made regression fixture: flip its
grid to non-integer and the two SBML/Antimony confs should still recover
koncethis is fixed.
Suggested fix
In
bngsim_sbml_model.py, thread theTimeCourseaction'sexplicit_points(themeasurement grid) into the simulation, i.e. pass
sample_times=...tosim.run(...)in_run_simulation(mirroringnet_model.py's BNGL path) insteadof a uniform
n_pointsgrid when the action carries explicit points. No bngsimchange is required. (The
ParamScanpath atbngsim_sbml_model.py:899has the sameuniform-grid assumption over
linspace(min, max, stepnumber+1)and is worthchecking in the same pass.)
Not a bngsim bug
bngsim.Simulator.runalready acceptssample_times; the engine supports arbitraryoutput times. The gap is entirely in PyBNF's SBML/Antimony wrapper not passing the
grid it already holds.
Context
wrongly claimed no
.ant/.xmlrecovery example) and closed. This is thecorrectly-scoped version.
main@d6a84ad; bngsim 0.9.67; petab 0.8.2.examples/tutorial/11_interop/).