Skip to content

fix(petab): reset a re-injected log observable's noiseDistribution to its v1 base family (#679) - #680

Merged
wshlavacek merged 1 commit into
mainfrom
fix/petab-090-log10-noise-distribution-679
Sep 10, 2026
Merged

fix(petab): reset a re-injected log observable's noiseDistribution to its v1 base family (#679)#680
wshlavacek merged 1 commit into
mainfrom
fix/petab-090-log10-noise-distribution-679

Conversation

@wshlavacek

Copy link
Copy Markdown
Collaborator

Fixes #679.

Every v1 observable with observableTransformation = log10 converted into a v2 problem the importer then refused, under petab >= 0.9.0:

PybnfError: Observable 'obs_V': observableTransformation 'log10' contradicts the scale of
noiseDistribution 'log-normal'. Give the residual scale in one place -- a log observableTransformation
over a linear noiseDistribution (normal / laplace).

This is the failure behind the three red pytest legs on #678 and on every PR since 2026-09-07. The py3.11 leg stays green only because petab 0.9.0 requires Python >= 3.12.

What was wrong

The scale-preserving converter was leaning on a petab bug. petab1to2 is designed to fold the v1 transformation into the v2 noiseDistribution; a missing return in that merge left the column blank in every petab < 0.9.0, the importer defaulted the blank to normal, and log10 over normal imported as lognormal as ADR-0073 describes. petab 0.9.0 (PEtab-dev/libpetab-python#502) fixed the merge. PEtab v2 has no log10-normal, so the converter now substitutes the natural-log family (log10 + normal -> log-normal, with a warning), a silent ln 10 rescaling of sigma. Stacked under our re-injected log10 column, that is exactly the contradiction the importer exists to refuse.

What changed

petab1to2_preserve_scale now reads each log observable's v1 noiseDistribution (blank -> normal) alongside its transformation, and inject_observable_transformations takes an optional {observableId: 'normal' | 'laplace'} map and resets the row's noiseDistribution to that linear base while writing the transformation. The scale is then stated once, in the preserved column, whichever petab produced the table: a blank cell (0.8.2) and a folded log-normal / log-laplace (0.9.0) both come back to the v1 family. Rows without a transformation keep whatever petab1to2 wrote, so a linear problem is still byte-identical. petab's substitution warning lands inside the catch_warnings block that already silences the parameterScale warning, for the same reason.

ADR-0073 gains an addendum recording this.

Tests

  • End to end: a converted log10 observable carries noiseDistribution = normal.
  • Injector: log-normal reset to normal with the linear row untouched; log-laplace reset to laplace; the column left alone when no base map is passed.

Three of the four fail against main's convert.py. tests/test_petab_convert.py and tests/test_petab_import.py (175 tests) pass under both petab 0.8.2 and 0.9.0; all tests/test_petab*.py (629 tests) pass under 0.8.2. ruff and the -W docs build are clean.

Not an upstream bug

PEtab v2 removed log10-normal deliberately (PEtab-dev/PEtab#644, libpetab-python#455, editors' decision on PEtab-dev/PEtab#623), and the converter warns about the substitution. The preserved column is the workaround the specification leaves us.

… its v1 base family (#679)

Every v1 observable with observableTransformation = log10 converted into a v2
problem the importer then refused, under petab >= 0.9.0:

    PybnfError: Observable 'obs_V': observableTransformation 'log10' contradicts
    the scale of noiseDistribution 'log-normal'. Give the residual scale in one
    place -- a log observableTransformation over a linear noiseDistribution.

The scale-preserving converter was leaning on a petab bug. petab1to2 is designed
to fold the v1 transformation into the v2 noiseDistribution; a missing return in
that merge left the column blank in every petab < 0.9.0, the importer defaulted
the blank to normal, and log10 over normal imported as lognormal as ADR-0073
describes. petab 0.9.0 (2026-09-07, PEtab-dev/libpetab-python#502) fixed the
merge. PEtab v2 has no log10-normal, so the converter now substitutes the
natural-log family (log10 + normal -> log-normal, with a warning), a silent
ln 10 rescaling of sigma. Stacked under our re-injected log10 column, that is
exactly the contradiction the importer exists to refuse.

petab1to2_preserve_scale now reads each log observable's v1 noiseDistribution
(blank -> normal) alongside its transformation, and
inject_observable_transformations takes an optional {observableId: base} map
and resets the row's noiseDistribution to that linear base while writing the
transformation. The scale is then stated once, in the preserved column,
whichever petab produced the table: a blank cell (0.8.2) and a folded
log-normal / log-laplace (0.9.0) both come back to the v1 family. Rows without
a transformation keep whatever petab1to2 wrote, so a linear problem is still
byte-identical. petab's substitution warning lands inside the catch_warnings
block that already silences the parameterScale warning, for the same reason.

Tests: an end-to-end pin that a converted log10 observable carries
noiseDistribution = normal, and three unit tests on the injector (log-normal
reset to normal with the linear row untouched, log-laplace reset to laplace,
and the column left alone when no base map is passed). Three of the four fail
on main. test_petab_convert.py and test_petab_import.py pass under both
petab 0.8.2 and 0.9.0; the full petab test set passes under 0.8.2.

Not an upstream bug: PEtab v2 removed log10-normal deliberately
(PEtab-dev/PEtab#644, libpetab-python#455) and the converter warns about the
substitution. ADR-0073 gains an addendum recording this.
@wshlavacek
wshlavacek merged commit 255f99d into main Sep 10, 2026
9 checks passed
@wshlavacek
wshlavacek deleted the fix/petab-090-log10-noise-distribution-679 branch September 10, 2026 16:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PEtab v1->v2 converter emits log10 over log-normal under petab 0.9.0, so every converted log10 observable is refused at import

1 participant