Skip to content

Fresh R NNS 13.0 parity regeneration and corrective Python fixes - #3

Merged
OVVO-Financial merged 3 commits into
mainfrom
claude/quirky-franklin-bsk4md
Jun 13, 2026
Merged

Fresh R NNS 13.0 parity regeneration and corrective Python fixes#3
OVVO-Financial merged 3 commits into
mainfrom
claude/quirky-franklin-bsk4md

Conversation

@OVVO-Financial

Copy link
Copy Markdown
Owner

Summary

This PR freshly regenerated tests/_r_cache.json from vendored live R NNS 13.0 after starting from an empty cache, and fixes the deterministic Python parity gaps that the fresh run revealed.

  • Python implementation changes in this PR are limited to matching fresh R NNS 13.0 outputs.
  • Cache-only parity was run only after fresh regeneration, as a replay check.

What was done

Made regeneration truly fresh. Added a --fresh mode to scripts/regenerate_r_cache.py that refuses to run in CI, verifies a live local R NNS 13.0 install, moves the existing tests/_r_cache.json to tests/_r_cache.json.bak, and repopulates every entry from a live R call. No existing entries were reused.

Installed live vendored R NNS 13.0. Installed via scripts/install_local_r_nns.py (R CMD INSTALL tools/NNS), never from CRAN. Verified independently:

R version 4.3.3 (2024-02-29)
Rscript (R) version 4.3.3 (2024-02-29)
packageVersion("NNS") == 13.0
source: tools/NNS (extracted package dir)

R dependencies needed to load NNS came from Ubuntu binaries; Rfast (+zigg) was built from upstream GitHub release v2.1.5.1-apollo because CRAN was unreachable. NNS itself came only from tools/NNS.

Regenerated the full cache from live R. python scripts/regenerate_r_cache.py --fresh -- -n 0 tests/parity. Result: nns_version: 13.0, schema_version: 1, 2385 entries (was 2406). The 21 dropped keys are stale — an instrumented cache-only replay confirmed no current parity test computes them. 456 of the 2384 shared keys changed value; fresh live R 13.0 values are authoritative.

Fixed Python to match fresh R 13.0 (the fresh run surfaced 216 failing tests, all from four deterministic divergences):

  1. LPM.VaR/UPM.VaR integer degrees 1–4 (var.py): ported R 13.0's exact polynomial root-finding inversion (.NNS_LPM_VaR_integer) replacing the old optimize() search. Also fixed dependent regression/M.reg CIs and stack/boost prediction intervals.
  2. Distance bulk-path kernel (distance.py): added nns_distance_path_single_bulk using the population sd of ranks sqrt((k²−1)/12) in the lognormal weight (the single-point kernel keeps sample sd), and used it for n.best>1 fitted values and multi-point estimates, as R does.
  3. NNS.M.reg out-of-hull multi-point extrapolation (multivariate_regression.py): mirrored R 13.0's vectorized outsider path and dropped the obsolete single-outsider dims-dropping quirk.
  4. NNS.ARMA numeric multi-lag seasonal weighting (arma.py): weight each seasonal factor by its position in the vector (R's seq(n,1,-i)), not its lag value. This resolved the two previously xfail-ed Sunspots ARMA and macro VAR practical examples, which now pass against live R; their xfail markers were removed.

Structural test/return updates justified by fresh R output: R 13.0's NNS.boost no longer returns n.best, so the Python return dict and stale assertions were updated, and final-estimate NaN handling now matches R.

ARMA nonseasonal nonlinear reconciliation. Live R 13.0 returns [128.5, 113.5, 155.5, 213.667]; Python returns the same. The previously reported [125.25, 107.75, 158.75, …] Python values do not reproduce with the current implementation. Smoke expectations were verified against this live run and needed no change.

Manual diagnostic scripts. No scripts/compare_nns*.py exist in this repo, so there was no $RPM-based univariate NNS.reg extraction to fix. The univariate regression-point diagnostic in test_r13_smoke.py already uses NNS.reg(..., multivariate.call = TRUE)$y.

Verification (all run after fresh regeneration)

python scripts/install_local_r_nns.py                              # NNS 13.0
python scripts/regenerate_r_cache.py --fresh -- -n 0 tests/parity  # 1794 passed, 1 xfailed (from empty)
PYNNS_R_CACHE_ONLY=1 python -m pytest -q -n 0 tests/parity         # 1794 passed, 1 xfailed
python -m pytest -q tests/parity/test_r13_smoke.py                 # 6 passed
python -m pytest -q tests/invariants                               # 317 passed
ruff check .                                                       # pass
mypy                                                               # pass
python -m build                                                    # sdist + wheel built

If --fresh were not used, the equivalent manual step is:

mv tests/_r_cache.json tests/_r_cache.pre-fresh-r13.json
python scripts/regenerate_r_cache.py -- -n 0 tests/parity

Two independent from-empty regenerations agree on 2384/2385 entries; the only difference is the stochastic bootstrap CI block of one NNS.ANOVA entry (deterministic core identical), covered by a @pytest.mark.stochastic range check. The sole remaining xfail is the balanced-Iris NNS.boost diagnostic, documented as a stochastic RNG sampling gap. No deterministic parity gap is excluded from the suite.

Full provenance: docs/r13_cache_regeneration.md.

https://claude.ai/code/session_015pEPxpJWbiHizgcFEm1AWf


Generated by Claude Code

claude added 3 commits June 13, 2026 00:32
- scripts/regenerate_r_cache.py --fresh refuses to run in CI, verifies a
  live local R NNS 13.0 install, moves the existing tests/_r_cache.json to
  tests/_r_cache.json.bak, and regenerates every entry from an empty cache
  so no existing entries can be reused.
- Clear NNS_R_CACHE_ONLY and NNS_OFFLINE toggles (in addition to the PYNNS
  variants and CI) before invoking pytest.
- Start docs/r13_cache_regeneration.md provenance record for the fresh
  live R NNS 13.0 regeneration (final counts to be filled in when the
  in-progress fresh regeneration completes).
- Ignore vendored NNS local-install build artifacts and the fresh-mode
  cache backup.

https://claude.ai/code/session_015pEPxpJWbiHizgcFEm1AWf
A from-empty cache regeneration against live vendored R NNS 13.0 surfaced
216 failing parity tests. All traced to four deterministic divergences;
Python is fixed to match fresh R 13.0 in each case:

- LPM.VaR/UPM.VaR integer degrees 1-4: port R's exact polynomial
  root-finding inversion (.NNS_LPM_VaR_integer) replacing the old
  optimize() search. Fixes regression/M.reg confidence intervals and
  stack/boost prediction intervals built on these helpers.
- Distance kernels: add nns_distance_path_single_bulk mirroring R's bulk
  path kernel, which uses the population sd of ranks sqrt((k^2-1)/12) in
  the lognormal weight; the single-point NNS.distance kernel keeps the
  sample sd. Use the bulk kernel for n.best>1 fitted values and
  multi-point estimates as R does.
- NNS.M.reg out-of-hull multi-point extrapolation: mirror R 13.0's
  vectorized outsider path (bulk kernel estimates, pmax(d, 1e-10)
  gradient guards) and drop the old dims-dropping single-outsider quirk
  R no longer has.
- NNS.ARMA numeric multi-lag seasonal weighting: weight each seasonal
  factor by its position in the vector (R's seq(n, 1, -i)), not its lag
  value. Removes the two formerly-xfailed Sunspots ARMA and macro VAR
  practical examples, which now pass against live R.

Structural updates justified by fresh R output: NNS.boost no longer
returns n.best, so the Python return and stale test assertions were
updated, and final-estimate NaN handling now matches R. The remaining
balanced-Iris boost xfail is documented as a stochastic RNG sampling gap.

https://claude.ai/code/session_015pEPxpJWbiHizgcFEm1AWf
…pty cache)

Freshly regenerated from an empty cache against live vendored R NNS 13.0
(packageVersion('NNS') == 13.0), installed from tools/NNS only. No prior
cache entries were reused: scripts/regenerate_r_cache.py --fresh moved the
old cache aside and every entry was produced by a live R call.

- 2385 entries (was 2406): an instrumented cache-only replay confirmed the
  21 dropped keys are stale and not computed by any current parity test.
- 456 of the 2384 shared keys changed value vs the prior committed cache;
  the fresh live R 13.0 values are authoritative. See
  docs/r13_cache_regeneration.md for the per-function breakdown.
- Two independent from-empty regenerations agree on 2384/2385 entries; the
  sole difference is the stochastic bootstrap CI block of one NNS.ANOVA
  entry (deterministic core identical), covered by a @pytest.mark.stochastic
  range check.

Cache-only parity replay, smoke, and invariants all pass against this cache.

https://claude.ai/code/session_015pEPxpJWbiHizgcFEm1AWf
@OVVO-Financial
OVVO-Financial merged commit 3f77b2f into main Jun 13, 2026
4 checks passed
@OVVO-Financial
OVVO-Financial deleted the claude/quirky-franklin-bsk4md branch June 13, 2026 02:30
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.

2 participants