fix(petab): refuse a U-tagged free parameter on export instead of writing it as a hard-bounded one (#736) - #737
Merged
Conversation
…ting it as a hard-bounded one (#736) `uniform_var = v 0 10 U` and its `loguniform_var` twin mean the box is enforced only during initialization: it seeds the first population and the search is then free to leave it. `ploop` reads the tag into the value list and the config loader honours it, so the fitter builds a parameter whose box is `(-inf, inf)`. The exporter read `value[0]` and `value[1]` and stopped. The comment above the construction called the third token "the native `bounded` flag, inert for the location families" -- true there, and false where the token is actually legal, since `parse.b_var_def_keys` is exactly `['loguniform_var', 'uniform_var']`, the two families whose p1/p2 become the PEtab bounds. `bounded` therefore defaulted to True and `_petab_uniform_row` wrote `[p1, p2]` into `lowerBound`/`upperBound`, which in PEtab are hard box constraints. Measured: a conf declaring a search free to leave [0, 10] exported to a table byte-identical to the bounded spelling, and the re-imported conf built a parameter with `bounded=True` and box `(0, 10)` -- the `U` gone from the line. No warning, no exception. This is the third defect of the same shape as #719 and #733: the export reads part of a declaration and discards the rest without saying so, against docs/petab.rst's "the export is fit-preserving" and against the module's own contract that everything it cannot write raises NotImplementedError. Unlike the first two this one has no mapping waiting to be written. PEtab's nearest shape -- blank (infinite) bounds plus an explicit `priorDistribution = uniform` over the box -- says something else, because PEtab bounds truncate a prior rather than seed a draw, and it does not survive the trip either: `_resolve_prior`'s uniform arm returns `bounded=True` unconditionally, so such a row re-imports as a bounded parameter. That is the right reading for a PEtab row, whose bounds are hard, so nothing changes on the import side. So the exporter refuses. `_free_parameter_from_var_line` passes the flag through (the parameter it builds now matches the one the fitter builds), and `_petab_uniform_row` raises NotImplementedError when it is off, naming the boundary and saying to drop the tag to export the box as real bounds. Exporting the box with a warning was considered and rejected: the exporter emits no warnings anywhere, so it would be a new channel for one case, and a warning is the signal the #583/#719 work found people do not act on. The cost of refusing is nothing measurable -- no `U`-tagged declaration exists in the repo's examples, tests or benchmarks, or in the BNGL-Models/pybnf-jobs corpus. Tests: five, of which four fail against the old code. Two at the row mapper, parametrized over both flag-bearing families, which also assert the bounded spelling of the same declaration is untouched; two end to end on a real job, one per family; and one holding the other half of the flag, that an explicit `B` tag exports byte-identically to leaving it off -- the guard that threading `bounded` through the constructor perturbs nothing. ADR-0025 carries an amendment recording the refusal, the rejected warn-and-export alternative, and the evidence that the PEtab shape is genuinely absent rather than merely unmapped. The `uniform_var` config-key documentation now says the tag cannot be exported, where a reader of that tag will look. Signed-off-by: Bill Hlavacek <hlavacek@lanl.gov>
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.
Closes #736. Third and last of the export-drops-part-of-a-declaration family, after #719
(PR #734) and #733 (PR #735).
What was wrong
uniform_var = v 0 10 U(and itsloguniform_vartwin) means the box is enforced onlyduring initialization — it seeds the first population and the search is then free to leave
it.
ploopreads the tag and the config loader honours it, so the fitter builds a parameterwhose box is
(-inf, inf).The exporter read
value[0]andvalue[1]and stopped. The comment above the constructioncalled the third token "the native
boundedflag, inert for the location families" — truethere, and false where the token is actually legal:
parse.b_var_def_keysis exactly['loguniform_var', 'uniform_var'], the two families whosep1/p2become the PEtabbounds. So
boundeddefaulted toTrueand_petab_uniform_rowwrote[p1, p2]intolowerBound/upperBound, which in PEtab are hard box constraints.Measured end to end:
A search the config declared unconstrained comes back constrained, silently — against
docs/petab.rst's "the export is fit-preserving" and against the module's own contract that
everything it cannot write raises
NotImplementedError.Why a refusal and not a better mapping
Unlike #719 and #733, there is no mapping waiting to be written. PEtab's nearest shape — blank
(infinite) bounds plus an explicit
priorDistribution = uniformover the box — says somethingelse, because PEtab bounds truncate a prior rather than seed a draw. It does not survive the
trip either:
_resolve_prior's uniform arm returnsbounded=Trueunconditionally. That is the rightreading for a PEtab row, whose bounds are hard, so nothing changes on the import side.
Refuse vs. warn-and-export. I considered writing the box as bounds with a warning and
rejected it: the exporter emits no warnings anywhere, so it would be a new channel for one
case, and a warning is precisely the signal the #583/#719 work found people do not act on.
The cost of refusing is nothing measurable — there is no
U-tagged declaration in the repo'sexamples, tests or benchmarks, or in the
BNGL-Models/pybnf-jobscorpus. ADR-0025 records thedecision and names warn-and-export as the fallback if it ever becomes a real obstacle.
What changed
_free_parameter_from_var_linepasses the flag through, so the parameter the exporterbuilds matches the one the fitter builds.
_petab_uniform_rowraisesNotImplementedErrorwhen the box is off, naming the boundaryand saying to drop the tag to export the box as real bounds.
The untagged and
B-tagged spellings export exactly as before.Tests
Five, of which four fail against the old code.
bounded spelling of the same declaration is untouched.
Btag exports byte-identically toleaving it off — the guard that threading
boundedthrough the constructor perturbs nothing.Full suite green: 5162 passed, 25 skipped.
ruff@0.15.14clean; the-W --keep-goingSphinx build succeeds.
Docs
ADR-0025 carries the amendment.
docs/petab.rstnames the boundary in the export section, andthe
uniform_varconfig-key entry says the tag cannot be exported — where a reader of that tagwill actually look.