feat: multivariate derivative estimation for surface / PDE-style discovery - #23
Merged
Conversation
estimate_derivatives() differentiates along a single axis, which covers ODE discovery but not problems whose data is a surface and whose regression needs more than one partial derivative — PDE-style discovery (u_t = F(u, u_x, u_xx)) or transform laws such as time-temperature superposition, where y(x, T) = f(x + s(T)) implies y_T = s'(T)·y_x and both partials must come from one smoothed surface. Adds jaxsr.derivatives with SurfaceDerivatives, which fits a smoother to scattered or gridded N-D data and returns analytic partial derivatives of that smoother — never finite differences of noisy raw data — with standard errors: - "tensor_spline" (default): penalized tensor-product B-splines, any dimension, gridded or scattered, penalty by GCV. - "local_poly": local polynomial regression for irregular sampling. - "gp": anisotropic squared-exponential Gaussian process, giving the derivative posterior directly (Hermite form of the kernel derivatives). The smoothing hyperparameter is selectable only by criteria blind to the downstream symbolic score — GCV, log marginal likelihood, or a supplied noise level (smoothing="sigma", the s = n·sigma^2 rule). A smoother tuned against the regression that consumes it can manufacture whichever law the regression prefers, and the failure is silent. Smoothing flattens derivatives and biases any coefficient read off them, so the level actually used is reported: smoothing_, smoothing_source_, effective_dof_, residual_std_, noise_std_ and summary(). smoothing_scale re-runs the estimate at a deliberately different level to expose that bias. Also adds estimate_partial_derivatives() as a one-call wrapper, 52 tests (including recovery of an activation energy from a synthetic shift law), a user guide, a skill guide, and an API reference page. Closes #15 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S2L62FUGSWfnCgvPYEupj4
jkitchin
pushed a commit
that referenced
this pull request
Aug 12, 2026
Resolves conflicts with the group-aware resampling and parametric-identity work that landed on main (#16, #17, #18, #20, #23). Two of the four conflicts were substantive rewrites of functions this branch also touched, so weighting was re-layered onto the new versions rather than either side being taken wholesale: - cross_validate: gained groups/strategy upstream. Weights now follow their rows into every fold under all three splitting strategies, and into the per-group scores. A group whose rows all carry zero weight scores NaN rather than 0.0, which would read as a perfect prediction; a fold with no weight on either side raises, since there is nothing to fit or score. - bootstrap_model_selection: gained groups/resample_fn and basis-identity keying upstream. Weights follow their rows through both the row and group resamples. Combining sample_weight with resample_fn is rejected: those replicates regenerate their own rows, so a stored weight has no row to belong to. Weighting and the resampling level are orthogonal choices -- a weight says how precise a row is, a group says which rows are not independent -- so both can be passed together. Tests cover that seam. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JigoMqmadVXVs7ALgnLnvG
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 #15.
estimate_derivatives()differentiates along a single axis, which covers ODE discovery but not problems whose data is a surface and whose regression needs more than one partial — PDE-style discovery (u_t = F(u, u_x, u_xx, ...)) or transform laws such as time–temperature superposition, wherey(x, T) = f(x + s(T))impliesy_T = s'(T)·y_xand both partials must come from one smoothed surface.What this adds
New module
jaxsr.derivatives, exported asSurfaceDerivativesandestimate_partial_derivatives:Gridded data goes in as axes plus an N-D array (
fit([x, T], Y_grid)), so the rectangular-grid case needs no meshgrid glue; scattered data works in any dimension.est.coords_holds the flattened sample locations.Three smoothers, all returning analytic partials of the fitted smoother rather than finite differences of noisy raw data:
"tensor_spline"(default)d"local_poly""gp"nup to a few hundredn, capped bymax_pointsSmoothing selection is blind to the downstream symbolic score, by construction.
smoothing="auto"uses GCV (spline, local poly) or log marginal likelihood (GP);smoothing="sigma"uses a supplied noise level (thes = n·σ²rule); a float is used verbatim. There is no option to tune against a regression score — a smoother selected that way can manufacture whichever law the regression prefers, and the failure is silent.The smoothing level actually used is reported, so smoothing-induced bias is visible rather than inferred:
smoothing_,smoothing_source_,effective_dof_,residual_std_,noise_std_, andsummary().smoothing_scalere-runs the estimate at a deliberately different level, which turns the ×1/×3 sensitivity table from the issue into a two-line loop.Validation
On a synthetic TTS surface (20 frequencies × 12 temperatures, Arrhenius shift, true
E = 55.85kJ/mol), the effective activation energy recovered fromy_T / y_x:tensor_splinelocal_polygpA heat-equation case (
u_t = 0.1·u_xx, 0.2% noise) recoversy = 0.09583*u_xx, drifting to0.09043atsmoothing_scale=10— the smoothing bias made visible.Testing
tests/test_derivatives.py(92% coverage of the new module), including recovery of the activation energy from a synthetic shift law and the visibility of deliberate over-smoothing.scripts/test_under_numpy.py) — no new JAX API, so the shim needed no changes.black --checkandruff checkclean; module doctests pass. Slowest new test is 1.3s locally.Docs
docs/guides/surface-derivatives.md(every code block executed; quoted outputs are real), a skill guide plus a SKILL.md decision-tree entry,docs/api/derivatives.rst, and TOC / README / CHANGELOG entries.estimate_derivativesandSurfaceDerivativesnow cross-reference each other.Not included
No example notebook — the guide covers the same ground without adding an execution-time dependency. Nothing here touches #18's structured library blocks.
🤖 Generated with Claude Code
https://claude.ai/code/session_01S2L62FUGSWfnCgvPYEupj4
Generated by Claude Code