From c0004e759ee449bf815c1e0029febb3c4dd41b42 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 14 Jul 2026 01:37:53 +0000 Subject: [PATCH 1/3] Bump version to 1.3.0 Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_019F6ZjcfXSxZWGMuSmQGmLN --- README.md | 2 +- pyproject.toml | 2 +- src/nns/__init__.py | 2 +- uv.lock | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 32ab9243..709ce83c 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ NNS is built around partial moments, the lower and upper components of variance, |---|---| | Distribution package | `ovvo-nns` | | Import package | `nns` | -| Current version | `1.2.0` | +| Current version | `1.3.0` | | Python | `>=3.11` | | Required runtime dependencies | NumPy, SciPy | | R required at runtime | No | diff --git a/pyproject.toml b/pyproject.toml index 537ffbb2..a961bb11 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "ovvo-nns" -version = "1.2.0" +version = "1.3.0" description = "Python port of nonlinear nonparametric statistics from R NNS" readme = "README.md" requires-python = ">=3.11" diff --git a/src/nns/__init__.py b/src/nns/__init__.py index a24cf3e6..8882f54d 100644 --- a/src/nns/__init__.py +++ b/src/nns/__init__.py @@ -4,7 +4,7 @@ from nns.pm_matrix import pm_matrix as pm_matrix -__version__ = "1.2.0" +__version__ = "1.3.0" _EXPORTS = { "BoostResult": ("nns.boost", "BoostResult"), diff --git a/uv.lock b/uv.lock index 614f14ec..0985a78d 100644 --- a/uv.lock +++ b/uv.lock @@ -1044,7 +1044,7 @@ wheels = [ [[package]] name = "ovvo-nns" -version = "1.2.0" +version = "1.3.0" source = { editable = "." } dependencies = [ { name = "matplotlib" }, From 794f488a9cfb5e758fde1a7f82f9ba8b4d675599 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 14 Jul 2026 01:51:39 +0000 Subject: [PATCH 2/3] Fix benchmark suite: drop obsolete nns_boost factor_levels kwarg and correct R baseline version label The nns_boost rewrite infers factor-predictor levels from the string columns of the variable matrix, so the factor_levels kwarg no longer exists; two boost benchmarks still passed it and raised TypeError. Remove it to match the current API (as the parity tests already do). Correct the R benchmark baseline metadata from 12.1 to 13.0: the recorded values already match the documented R NNS 13.0 numbers, but the stale nns_version label mismatched conftest's _NNS_VERSION pin, which made the benchmark harness refuse to run. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_019F6ZjcfXSxZWGMuSmQGmLN --- tests/benchmarks/_r_baseline.json | 2 +- tests/benchmarks/test_lpm.py | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/benchmarks/_r_baseline.json b/tests/benchmarks/_r_baseline.json index 6bf3fc65..9b41ac95 100644 --- a/tests/benchmarks/_r_baseline.json +++ b/tests/benchmarks/_r_baseline.json @@ -78,6 +78,6 @@ "pm_matrix_50x500_seconds": 0.0072, "sd_efficient_set_50x252_degree2_seconds": 0.0044 }, - "nns_version": "12.1", + "nns_version": "13.0", "schema_version": 1 } diff --git a/tests/benchmarks/test_lpm.py b/tests/benchmarks/test_lpm.py index e3eae3e7..9a8466ab 100644 --- a/tests/benchmarks/test_lpm.py +++ b/tests/benchmarks/test_lpm.py @@ -896,7 +896,6 @@ def test_nns_boost_factor_predictor_50x2( variable[:10], learner_trials=10, cv_size=0.25, - factor_levels=(["low", "mid", "high"], None), feature_importance=False, ) @@ -926,7 +925,6 @@ def test_nns_boost_multi_factor_predictor_50x3( variable[:10], learner_trials=10, cv_size=0.25, - factor_levels=(["low", "mid", "high"], None, ["down", "up"]), feature_importance=False, random_seed=1, ) From a6e4d5cca41561112879bcbcb17e892c87fb2e40 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 14 Jul 2026 02:05:32 +0000 Subject: [PATCH 3/3] Add regression test: nns_stack accepts duplicate predictor columns Locks in nns_stack/nns_reg parity for the cbind(x, x) dimension trick. The R NNS.stack rejected duplicate predictor names; the Python port never had that restriction, and this test guards against a regression. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_019F6ZjcfXSxZWGMuSmQGmLN --- tests/invariants/test_stack.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/invariants/test_stack.py b/tests/invariants/test_stack.py index 9e3471dd..99d02a67 100644 --- a/tests/invariants/test_stack.py +++ b/tests/invariants/test_stack.py @@ -36,6 +36,21 @@ def test_nns_stack_numeric_shapes_and_keys() -> None: assert np.all(np.isfinite(result["stack"])) +def test_nns_stack_accepts_duplicate_predictor_columns() -> None: + # Parity with nns_reg: the cbind(x, x) dimension trick must work for + # nns_stack too. R NNS.stack historically rejected duplicate predictor + # names ("[IVs.train] predictor names must be unique."); the Python port + # never had that restriction and must keep accepting duplicate columns in + # both the training matrix and the point-estimate matrix. + rng = np.random.default_rng(123) + x = rng.standard_normal(60) + y = rng.standard_normal(60) + variable = np.column_stack((x, x)) + + result = nns_stack(variable, y, variable[:5], method=(1,), folds=2, cv_size=0.25) + + assert result["reg"].shape == (5,) + assert np.all(np.isfinite(result["reg"])) def test_nns_stack_pred_int_falls_back_to_point_estimate_when_regression_drops_rows() -> None: