From 2a68bec520f55cb6bd9bd87f0a349986d34d646a Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 13 Jul 2026 01:05:17 +0000 Subject: [PATCH 1/2] Fix multivariate default n.best to scale with observations, not features The default n.best in nns_m_reg used floor((1-dependence)*sqrt(ncol)), mirroring R's Multivariate_Regression.R:129 where n is shadowed by ncol(original.IVs). With sqrt(#features) <= 2.24 for typical widths, the default collapsed to n.best = 1 for any dependence above ~0.1, making order=None predictions identical to order="max" (pure 1-NN) on continuous multivariate data. Use sqrt(nrow) instead, consistent with the sqrt(n_obs) grid nns_stack already cross-validates over. Verified against a live patched R NNS 13.1 build on shared data: order=None now differs from order="max" identically in both languages (59/60 test rows bit-exact, remaining row is the out-of-support gradient-extension path). Full test suite passes; no recorded R-parity case is affected because all cached multivariate fixtures resolve to n.best = 1 under both formulas. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01BPbZvtDw4h2XJo9w5hw57h --- src/nns/multivariate_regression.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nns/multivariate_regression.py b/src/nns/multivariate_regression.py index 74926961..65c5bf5b 100644 --- a/src/nns/multivariate_regression.py +++ b/src/nns/multivariate_regression.py @@ -385,7 +385,7 @@ def _resolve_n_best( if n_best is not None: return max(1, int(n_best)) dependence = _copula_matrix(np.column_stack((x, y))) - return max(1, math.floor((1.0 - dependence) * math.sqrt(x.shape[1]))) + return max(1, math.floor((1.0 - dependence) * math.sqrt(x.shape[0]))) def _k_as_count(k: KValue, row_count: int) -> int: From 2d9a54840964b1239d6394da00a2e8a57a3cf045 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 13 Jul 2026 01:12:12 +0000 Subject: [PATCH 2/2] Mirror R's tryCatch fallback around NNS.dep in regression dependence R Regression.R wraps the NNS.dep call in tryCatch with a 0.1 fallback before averaging with the copula measure; the port only guarded the copula half. Wrap the nns_dep call the same way so the order=NULL dependence chain is a literal transcription of the R block. Verified against live R NNS 13.1 on nine datasets spanning n<100 and n>=100: NNS.dep and NNS.copula components bit-identical, composed dependence within 1 ulp, and every resolved dep.reduced.order equal (order=NULL and integer overrides). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01BPbZvtDw4h2XJo9w5hw57h --- src/nns/regression.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/nns/regression.py b/src/nns/regression.py index 389b6f68..4e3ae8f1 100644 --- a/src/nns/regression.py +++ b/src/nns/regression.py @@ -1071,7 +1071,12 @@ def _as_point_est(point_est: NDArray[np.float64] | float | None) -> NDArray[np.f def _regression_dependence(x: NDArray[np.float64], y: NDArray[np.float64]) -> float: - dep = nns_dep(x, y, asym=True)["Dependence"] + # Mirrors R Regression.R's tryCatch chain: NNS.dep error -> 0.1, copula + # error -> keep the NNS.dep component, NA -> 0.1. + try: + dep = nns_dep(x, y, asym=True)["Dependence"] + except (ValueError, FloatingPointError, ZeroDivisionError): + dep = 0.1 try: scaled = np.column_stack( (