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
PyBNF today applies one objective function to an entire fit: objfunc is a single config key (default chi_sq), resolved to a single ObjectiveFunction whose NoiseModel (ADR-0011) is a class attribute applied uniformly to every observable / data column.
PEtab v2 specifies noise per observable: noiseDistribution (normal / laplace) and observableTransformation (lin / log / log10) are columns of observables.tsv, so different observables in one problem can carry different noise models. Faithful PEtab v2 import — the observables chunk of the importer (#407) — therefore requires per-observable noise selection. This is a hard prerequisite, not a nicety: most PEtab problems have several observables, and heterogeneous noise across them is routine.
It also clears the bar on its own merits, independent of PEtab: a single fit against heterogeneous data — e.g. molecule counts (NegBinomial) alongside concentrations (Gaussian), or some observables on a log scale and others linear — is impossible today and is a natural modeling need.
Current state
config['objfunc'] → one code → OBJFUNC_REGISTRY → one ObjectiveFunction built via from_config, held as Configuration.obj.
The NoiseModel is a class attribute (noise = Gaussian(...), noise = NegBinomial()) on that single objective; SummationObjective.eval_point applies it to every compared column uniformly.
The σ-source is likewise global per objfunc: the per-point _SD data column (chi_sq/lognormal), a magic-named free parameter sigma__FREE / r__FREE (the _dynamic objfuncs, via the global free_noise_params map), or the neg_bin_r config constant.
What's needed
Per-observable selection of the noise model — each observable (matched to its exp-data column by name; see the Observable glossary entry) gets its own distribution family × additive-noise-scale × location, plus its own σ-source.
The ADR-0011 NoiseModel kernel is already a pure per-point object and is well-suited as-is — the harness (SummationObjective) just needs to choose the kernel per column instead of reading one class-level self.noise. So this is largely a selection/dispatch extension, not a rewrite of the noise math.
A config surface to express per-observable noise, with the existing single global objfunc preserved as the default/shorthand (backward compatible).
A per-observable σ-source, retiring the hardcoded sigma__FREE / r__FREE magic names in favour of per-observable noise parameters (PEtab gives each its own id).
Design questions (for an ADR before building)
Config surface. How does a .conf declare per-observable noise — an objfunc keyable by suffix/observable, a per-suffix override block, a small table? The global form must keep working unchanged.
σ-source generalization. How do per-observable noise parameters reach the kernel once free_noise_params is no longer global? This is where the sigma__FREE / r__FREE magic-string coupling that ADR-0011 already flagged would finally be dissolved.
Laplace noise family. PyBNF has Laplace only as a prior, not a noise family — close that gap here (a one-file kernel like the prior Laplace seam, plus σ-source/normalizer wiring) or in a separate follow-up?
Blast radius. Does evaluate_multiple / the σ-injection need to change, or only objfunc construction + eval_point dispatch?
Relevant ADRs: 0011 (NoiseModel = per-point kernel; the objfunc wrapper owns the σ-source), 0004 (noise = orthogonal axes, PEtab-defaulted not PEtab-bound). The per-observable selection design should land a new ADR.
Motivation
PyBNF today applies one objective function to an entire fit:
objfuncis a single config key (defaultchi_sq), resolved to a singleObjectiveFunctionwhoseNoiseModel(ADR-0011) is a class attribute applied uniformly to every observable / data column.PEtab v2 specifies noise per observable:
noiseDistribution(normal/laplace) andobservableTransformation(lin/log/log10) are columns ofobservables.tsv, so different observables in one problem can carry different noise models. Faithful PEtab v2 import — the observables chunk of the importer (#407) — therefore requires per-observable noise selection. This is a hard prerequisite, not a nicety: most PEtab problems have several observables, and heterogeneous noise across them is routine.It also clears the bar on its own merits, independent of PEtab: a single fit against heterogeneous data — e.g. molecule counts (NegBinomial) alongside concentrations (Gaussian), or some observables on a log scale and others linear — is impossible today and is a natural modeling need.
Current state
config['objfunc']→ one code →OBJFUNC_REGISTRY→ oneObjectiveFunctionbuilt viafrom_config, held asConfiguration.obj.NoiseModelis a class attribute (noise = Gaussian(...),noise = NegBinomial()) on that single objective;SummationObjective.eval_pointapplies it to every compared column uniformly._SDdata column (chi_sq/lognormal), a magic-named free parametersigma__FREE/r__FREE(the_dynamicobjfuncs, via the globalfree_noise_paramsmap), or theneg_bin_rconfig constant.What's needed
NoiseModelkernel is already a pure per-point object and is well-suited as-is — the harness (SummationObjective) just needs to choose the kernel per column instead of reading one class-levelself.noise. So this is largely a selection/dispatch extension, not a rewrite of the noise math.objfuncpreserved as the default/shorthand (backward compatible).sigma__FREE/r__FREEmagic names in favour of per-observable noise parameters (PEtab gives each its own id).Design questions (for an ADR before building)
.confdeclare per-observable noise — anobjfunckeyable by suffix/observable, a per-suffix override block, a small table? The global form must keep working unchanged.free_noise_paramsis no longer global? This is where thesigma__FREE/r__FREEmagic-string coupling that ADR-0011 already flagged would finally be dissolved.evaluate_multiple/ the σ-injection need to change, or only objfunc construction +eval_pointdispatch?Scope & relationships