Native bit-faithful NNS.reg hot path (gravity, NNS.part, NNS.dep, NNS.copula, single nns_reg_mv entrypoint) - #37
Merged
Conversation
The NNS gravity central tendency (nns.dependence._gravity) is called heavily inside the NNS.reg partition/regression path that nns_arma drives per-lag, per-step in its nonlin/both recursion. It was pure Python. This ports _gravity (and its quartile/binning helpers) faithfully to the C++ binding layer as a new gravity_exact symbol, and routes _gravity through it via the existing _native fallback. It is intentionally separate from the vendored nns::gravity, which derives from a different revision and diverges from the R-faithful Python by up to ~0.3; gravity_exact matches the Python to floating-point summation order (max relative diff ~9e-12 over 200k randomized cases, exact for normal-scale and discrete inputs). Full test suite remains green and the nonlin ARMA forecast is bit-for-bit identical with native on vs off, ~1.6x faster on a representative series.
nns_part is the recursive partitioner at the heart of NNS.reg, which nns_arma drives per-lag, per-step in its nonlin/both recursion. It was pure Python with per-depth numpy unique/char operations on small arrays. This ports the noise_reduction="off" path (the only mode the numeric NNS.reg / nns_arma hot path uses) faithfully to the C++ binding layer as nns_part_off, aggregating centers and regression points with the previously added gravity_exact. The Python wrapper routes through it via the _native fallback and keeps the pure-Python implementation for the mean/median/mode paths. Verified bit-exact against the pure-Python nns_part over 8000 randomized cases (quadrant labels, prior labels, order, and regression-point values all identical; worst value diff 0.0). Full suite stays green; the nonlin ARMA forecast is bit-for-bit identical with native on vs off and the cumulative native path (gravity + partition) is ~1.9x faster on a representative series.
Profiling the nonlin nns_arma forecast showed _copula_signed (via nns_dep -> _dep_pair -> _directional_dep) dominating ~70% of runtime: nns_dep is called per-lag, per-step through _regression_dependence. This ports nns_dep faithfully to the C++ binding layer as nns_dep_pair, covering the xonly partition, directional/global copulas, the signed and degree-0 copulas, n-d partial moments, and the discrete-case gravity combination. Co-moments reuse the verified nns:: kernels. To keep comparison thresholds bit-identical to numpy, it implements numpy's exact pairwise summation (np_pairwise_sum) for all means/sums, and gravity_exact now uses it too: a ~1e-11 difference in a gravity center was enough to flip a discrete partition split and diverge the dependence result. gravity and nns_dep are now bit-exact (0 ULP) vs the pure-Python implementations over >200k and >16k randomized cases respectively (continuous, discrete, ARMA-like, dependent). Full suite stays green; the nonlin ARMA forecast is bit-for-bit identical with native on vs off, and the cumulative native path (gravity + partition + dependence) is ~6.5x faster on a representative series.
…path After the native nns_dep landed, profiling showed the multivariate _copula term in _regression_dependence (pm_matrix + n-d partial moments over the 3-column scaled matrix) as the dominant remaining cost (~42%) of the nonlin nns_arma forecast. This ports nns.copula._copula faithfully to the C++ binding layer as copula_nd: general-d n-dimensional partial moments (clpm/cupm/dpm) mirroring the pure-Python formulas with numpy-faithful pairwise means, and the discrete/continuous copula blend. The partial-moment covariance matrices reuse the verified nns::pm_matrix kernel; per-column targets are supplied by the caller (np.mean) so they are already bit-identical. Verified bit-exact (0 ULP) vs pure-Python _copula over 40k randomized cases (d=2..4, continuous, discrete, dependent, and the regression-style duplicated-column 3-col input). Full suite stays green; the nonlin ARMA forecast is bit-for-bit identical with native on vs off, and the cumulative native path (gravity + partition + dependence + copula) is ~7.9x faster on a representative series.
Folds the remaining Python orchestration of the NNS.reg multivariate-call regression-points path into one native call: the XONLY off-noise partition, initial regression points, central point, endpoint values, and the consolidation/clamping. Refactors the partition loop into shared partition_labels / regression_points_from_prior helpers reused by nns_part_off, and reuses gravity_exact, np_pairwise_sum, and the verified nns::fast_lm kernel. It is wired at the multivariate_call branch of _nns_reg_univariate_core for the common regime (noise=off, non-class, non-smooth, standard XONLY partition with dependence != 1 and integer dep_order); other regimes fall through to pure Python. Because every NNS.reg multivariate caller routes through this branch, the acceleration now carries across nns_m_reg and thus NNS.stack / NNS.boost, not just nns_arma. Verified bit-exact (0 ULP) vs the pure-Python multivariate path over 6k randomized cases (ARMA-like, dependent, independent, nonlinear; varied orders); the nns_part_off refactor remains bit-exact. Full suite green. The nonlin ARMA forecast and nns_m_reg Point.est are bit-for-bit identical with native on vs off, ~14x and ~11x faster respectively on representative inputs.
OVVO-Financial
pushed a commit
that referenced
this pull request
Jun 15, 2026
Ships the native bit-faithful NNS.reg hot path (gravity, NNS.part, NNS.dep, NNS.copula, and the single nns_reg_mv multivariate-call entrypoint) merged in #37, accelerating nns_arma, nns_m_reg, NNS.stack, and NNS.boost while preserving R-faithful numeric behavior (native path is optional with a pure-Python fallback).
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.
Summary
Follow-up to the ARMA forecast-loop optimization (#33, merged). This PR pushes the entire
NNS.regmultivariate hot path into the C++ binding layer, each piece a bit-faithful port of the R-faithful pure-Python implementation, wired through the existing_nativegraceful-fallback pattern. Numeric behavior is unchanged; this is purely an acceleration via the optional native backend, with the Python fallback intact for every uncovered regime.The existing vendored
nns::gravity/partitioncould not be reused — they derive from a different revision and diverge from the Python the parity suite enforces (e.g.nns::gravitywas off by up to ~0.3) — so these are fresh, verified ports.Changes (5 commits)
gravity_exact— faithful port ofnns.dependence._gravity(quartile/binning central tendency), used pervasively for partition centers, regression points, and consolidation.nns_part_off— faithful port of thenoise_reduction="off"path ofnns.part.nns_part, the recursive partitioner at the heart ofNNS.reg.nns_dep_pair— faithful port ofnns.dependence.nns_dep(xonly partition, directional/global copulas, signed and degree-0 copulas, n-d partial moments, discrete-case gravity combination). Co-moments reuse the verifiednns::kernels. Also adds numpy-exact pairwise summation so all means/sums used as comparison thresholds are bit-identical to numpy — a ~1e-11 difference in a gravity center was enough to flip a discrete partition split, sogravity_exactuses it too.copula_nd— faithful port ofnns.copula._copula(general-d n-dimensional partial moments + discrete/continuous blend), reusing the verifiednns::pm_matrixkernel.nns_reg_mv— single native entrypoint that folds the remaining orchestration (XONLY partition, initial regression points, central point, endpoint values, consolidation/clamping) into one native call. Wired at themultivariate_callbranch of_nns_reg_univariate_corefor the common regime (noise=off, non-class, non-smooth, standard XONLY branch); other regimes fall through to pure Python.Because every
NNS.regmultivariate caller routes through that branch, the acceleration carries acrossnns_m_regand thereforeNNS.stackandNNS.boost, not justnns_arma.Verification
gravity_exact200k cases;nns_part8k;nns_dep16k;copula_nd40k;nns_reg_mv6k.Performance (native on vs off, identical output)
nns_armanonlin forecastnns_m_regNNS.stackNNS.boosthttps://claude.ai/code/session_01AN7vrBnRhxGd4eZv6A34VX
Generated by Claude Code