Give the response genealogy more than one way to be evaluated - #162
Merged
Merged
Conversation
Contributor
|
📚 Docs preview: https://qatlashub.github.io/AbstractQAtlas.jl/previews/PR162/ (updates on each push to this PR) |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
structure/response.jl declares WHICH derivative a response function is. Until now there was exactly one way to evaluate it, nested forward-mode AD in a package extension, so a project holding a measured potential rather than a differentiable one could not traverse the declared genealogy at all. Finite differences were already here three times, written inline and unnamed: the entropy secant in relations/region_entropy.jl:598 and the two log-log secants in relations/finite_size.jl:44,51. Those difference two MEASURED points and stay where they are, but they are why a route is a declared thing now. The genealogy work was already factored out of the AD path, so the route is the only thing that differs. `_genealogy_derivative` now holds it once and both paths call it; the extension supplies its `nth` and nothing else, and its 44 tests are unchanged and green. Routes: AutoDiff (the extension), CentralDifference(h), Richardson(h; levels). A new one is a subtype plus an `nth_derivative` method. Measured on `F(h) = -log(2cosh h)` at `h = 0.3`, `M = -F'(h) = tanh(0.3)`: | route | error | observed_order | |---|---|---| | AutoDiff | 0 | n/a, no step | | Richardson(1e-2) | 6.3e-14 | n/a, at machine precision | | CentralDifference(1e-2) | 8.9e-6 | 2.00 | | CentralDifference(1e-9) | 2.7e-8 | 0.32 | The last row is why `observed_order` is on the normal return path. Its error is smaller than the row above it and its step is on the roundoff side, and only the order says so. The control is a kinked function, where a central difference is first-order exactly and the diagnostic reports 1. `derivative_report` runs several routes and reports each value with its order, so a route that cannot run (no backend) is a NaN row rather than an aborted sweep. Mutation, against test/core/test_derivative_routes.jl: | mutation | assertions failed | |---|---| | drop the genealogy's minus sign | 5 | | `_route_order` always 1 | 2 | | central difference to forward difference | 8 | Additive (`git diff -- src/ | grep -E "^-[^-]"` is empty), so 0.7.15 -> 0.7.16. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
sotashimozono
force-pushed
the
feat/derivative-routes
branch
from
September 15, 2026 12:46
3ef76b3 to
df87940
Compare
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.
Stacked on #161 (base
feat/conventions), which it needs only for the version line.structure/response.jldeclares WHICH derivative a response function is. There was exactly one way to evaluate it, nested forward-mode AD in a package extension, so a project holding a measured potential rather than a differentiable one could not traverse the declared genealogy at all.Finite differences were already here three times, written inline and unnamed:
relations/region_entropy.jl:598(S2 - S1) / (log(ℓ2) - log(ℓ1))relations/finite_size.jl:44(log(O2) - log(O1)) / (log(L2) - log(L1))relations/finite_size.jl:51(log(-log(O2)) - log(-log(O1))) / (log(L2) - log(L1))Those difference two MEASURED points and stay where they are, but they are why a route is a declared thing now.
No duplicated genealogy
The AD path had already factored the genealogy out, so the route is the only thing that differs:
_genealogy_derivativenow holds that once and both paths call it. The extension supplies itsnthand nothing else; its 44 tests are unchanged and green.Routes:
AutoDiff(the extension),CentralDifference(h),Richardson(h; levels). A new one is a subtype plus annth_derivativemethod.The trust number is on the normal return path
Measured on
F(h) = -log(2cosh h)ath = 0.3, whereM = -F'(h) = tanh(0.3):observed_orderAutoDiff()Richardson(1e-2)CentralDifference(1e-2)CentralDifference(1e-9)The last row is the point. Its error is smaller than the row above it and its step is on the roundoff side, and only the order says so. A threshold would have passed it.
The control the diagnostic needs is a function it must fail on: on a kinked potential a central difference is first-order exactly, and
observed_orderreports 1.derivative_reportruns several routes and reports each value with its order, so a route that cannot run (missing backend) is aNaNrow rather than an aborted sweep.Mutation
Against
test/core/test_derivative_routes.jl(26 assertions, runs with no backend):_route_orderalways 1test/ext/test_derivative_routes_ad.jl(21 assertions) pins AD against Richardson across seven genealogy edges, comparing the sign before the magnitude.Additive (
git diff -- src/ | grep -E "^-[^-]"is empty), so0.7.15 -> 0.7.16.🤖 Generated with Claude Code