feat(petab): scale-preserving v1→v2 conversion (petab1to2_preserve_scale)#491
Merged
Conversation
…cale) petab.v2.petab1to2 drops the v1 parameterScale column (removed in v2) with only a warning, so a bare parameterScale=log10 estimated parameter converts to a linear uniform_var -- the same argmin but a far harder, worse-conditioned optimization than the log10 search the modeler specified. This wrapper runs the standard converter, then re-injects the dropped scale in the v2-native form (priorDistribution=log-uniform over the bounds), which PyBNF imports as loguniform_var on the Log10 scale. The optimizer objective excludes the prior, so this sets only the search scale and initial sampling, keeping the pure-MLE problem v1 specified. Priors petab1to2 already carried (parameterScale*Normal etc.) and linear parameters are left untouched. import_job stays a pure v2 importer.
wshlavacek
added a commit
that referenced
this pull request
Jul 19, 2026
…vables import as lognormal (#499, ADR-0073) petab.v2.petab1to2 drops a v1 observableTransformation=log10 (it collapses log10-normal to a blank noiseDistribution), so a log10 observable imported as a linear gaussian and the fit optimized the WRONG objective -- a linear residual with no change-of-variables Jacobian instead of the log10 residual the problem (and the paper) specify. Affects Perelson_Science1996, Borghans_BiophysChem1997, Elowitz_Nature2000 and other multi-decade-signal problems. Directly parallel to the parameterScale drop petab1to2_preserve_scale (#491) already fixes: - petab1to2_preserve_scale now ALSO re-injects observableTransformation as a preserved extra column on the converted v2 observables table (v2 lint-clean; other tools ignore it). Since v2 has no faithful log10 noiseDistribution, this extra column is the only channel for a log10 residual -- the observable-axis twin of the log-uniform parameter-scale re-injection. - The importer (import_.py) and the observables.py adapter select the noise family's ADDITIVE SCALE from observableTransformation, not just the family from noiseDistribution: log10 + normal -> the native lognormal family (Gaussian(LOG10, MEDIAN), which already carries the log10-space residual AND the Jacobian) -> objective = lognormal / a noise_model = lognormal line; log + normal -> Gaussian(LN). Guards against a transformation that contradicts a log noiseDistribution; families with no native token (natural-log, any log laplace) still raise NotImplementedError. A linear problem is byte-for-byte unchanged. ADR-0073 scopes this as an exception to ADR-0023 (which correctly rejected reading observableTransformation for NATIVE v2 problems): the column is a PyBNF-specific converter channel, not a claim that observableTransformation is standard v2. Verified end to end (v1 -> convert -> import -> the built objective's noise is Gaussian(LOG10)) plus the boundary raises. 1271 passed across the petab/objective/noise/config suites; ruff clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds
pybnf.petab.petab1to2_preserve_scale— a scale-preserving PEtab v1 → v2 converter.Why
The official
petab.v2.petab1to2drops the v1parameterScalecolumn (PEtab v2 removed it bydesign) and only warns. For a
parameterScale = log10estimated parameter carrying no objectiveprior — the common case for a multi-decade kinetic parameter — the converted v2 problem is read as a
linear
uniform_varover the raw bounds. That is the same argmin, but a far harder,worse-conditioned optimization than the log10 search the modeler specified — so a tool that imports
the converted problem naively optimizes a different (much harder) problem than intended.
Fix
petab1to2_preserve_scaleruns the standard converter, then re-injects the dropped estimation scalein the v2-native form: each bare log/log10 estimated parameter gets
priorDistribution = log-uniformover its bounds, which PyBNF imports as aloguniform_varon theLog10 scale. Because PyBNF's optimizer objective excludes the prior (
base.py), this sets only thesearch scale and initial sampling — not the objective — so the fit stays the pure-MLE problem v1
specified. Parameters petab1to2 already folded into a prior (
parameterScale*Normal→log-normal,…) and linear parameters are left untouched.
import_jobstays a pure v2 importer — the conversion is an explicit, named step, not areach-back to v1 in the read path.
Tests
tests/test_petab_convert.py(17 cases): a log/log10 param gainslog-uniformover its bounds, alinear param does not, an existing prior is never clobbered, and the float64-column coercion is
pinned. Full
tests/test_petab_import.py+test_petab_convert.pygreen (122 passed).Verified end to end
On the PEtab benchmark collection: Boehm/Perelson/Sneyd now import
loguniform_var(0 linear) andgradient-fit in log10; Borghans, previously a CVODES blowup from a linear box-center start over
10 decades, now fits. Intended as the opt-in migration
petab1to2itself should offer upstream.