diff --git a/.github/workflows/native-backend-ci.yml b/.github/workflows/native-backend-ci.yml index c6b4f8d0..e5e238dc 100644 --- a/.github/workflows/native-backend-ci.yml +++ b/.github/workflows/native-backend-ci.yml @@ -32,13 +32,13 @@ jobs: run: python -m pip install -e . - name: Run native import smoke test - run: python -c "import pynns._nnscore as c; print(c.lpm(2.0, 0.0, [-2.0, -1.0, 0.5, 3.0]))" + run: python -c "import nns._nnscore as c; print(c.lpm(2.0, 0.0, [-2.0, -1.0, 0.5, 3.0]))" - name: Run invariants without Rscript run: python -m pytest -q tests/invariants - name: Run parity from committed R cache - run: PYNNS_R_CACHE_ONLY=1 python -m pytest -q tests/parity + run: NNS_R_CACHE_ONLY=1 python -m pytest -q tests/parity - name: Run ruff run: ruff check . diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f8f9e23..6b2b4fcf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.18) -project(pynns_native LANGUAGES CXX) +project(nns_native LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -18,7 +18,7 @@ add_subdirectory(extern/NNS-core) # it can be linked into the nanobind module on ELF platforms. set_target_properties(nnscore PROPERTIES POSITION_INDEPENDENT_CODE ON) -nanobind_add_module(_nnscore src/pynns/_nnscore_bindings.cpp) +nanobind_add_module(_nnscore src/nns/_nnscore_bindings.cpp) target_link_libraries(_nnscore PRIVATE nnscore) target_compile_features(_nnscore PRIVATE cxx_std_17) -install(TARGETS _nnscore LIBRARY DESTINATION pynns) +install(TARGETS _nnscore LIBRARY DESTINATION nns) diff --git a/README.md b/README.md index 7c2ab9eb..f1024977 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ -# PyNNS +# NNS Python Python port of the R NNS 12.1 beta package. -- PyPI package: `nns-pm` -- Import name: `pynns` +- Distribution package: `NNS` +- Import package: `nns` (`import nns`) +- Native extension: `nns._nnscore` - Runtime dependencies: NumPy, SciPy - R required for normal use: no - Status: alpha, parity-focused @@ -12,14 +13,14 @@ Python port of the R NNS 12.1 beta package. ## Install ```bash -pip install nns-pm +pip install NNS ``` ## Quick Use ```python import numpy as np -from pynns import lpm, nns_dep, nns_reg +from nns import lpm, nns_dep, nns_reg x = np.array([-2.0, -1.0, 0.5, 3.0]) downside = lpm(2, 0.0, x) @@ -47,7 +48,7 @@ uv run ruff check . uv run mypy ``` -R and the R `NNS` package are only needed to regenerate parity fixtures. +CI parity is cache-backed through committed fixtures and does not require `Rscript`. `Rscript` and the R `NNS` package are only needed for local cache regeneration. The parity claim is bounded by committed fixtures and cache entries; full R package parity is not claimed, and plot artifacts are intentionally out of scope. ## Attribution diff --git a/docs/api_status.md b/docs/api_status.md index ebdddabf..31ea6146 100644 --- a/docs/api_status.md +++ b/docs/api_status.md @@ -1,9 +1,9 @@ -# PyNNS API Status +# NNS Python API Status -This page summarizes the public PyNNS API surface, known gaps, guarded paths, +This page summarizes the public NNS Python API surface, known gaps, guarded paths, and design boundaries. -PyNNS is an alpha, parity-focused Python port of installed R NNS 12.1 beta, +NNS Python is an alpha, parity-focused Python port of installed R NNS 12.1 beta, implemented natively in Python on top of NumPy and SciPy. It does not wrap R, call the R package at runtime, or depend on compiled R/C++ shims. The goal is public input/output compatibility where R behavior is stable, @@ -17,7 +17,7 @@ largest remaining API work is now mostly ergonomic: categorical predictor preparation is explicit through `prepare_factor_predictors(...)`, while direct raw-factor `nns_m_reg(..., factor_2_dummy=True)` remains guarded because the installed R internal path errors. Named R data-frame factor ordering quirks are -documented as outside PyNNS' positional-column API boundary. Performance gaps +documented as outside NNS Python' positional-column API boundary. Performance gaps remain mostly in large stochastic-dominance workloads where R uses compiled kernels. @@ -57,7 +57,7 @@ invariant, and property coverage. | Nowcast panel: `nns_nowcast_panel` | implemented | medium | Python-native deterministic monthly panel helper backed by `nns_var`. R NNS 12.1 beta removed `NNS.nowcast`, so this is no longer an R-export parity target. | | Providers: `CsvNowcastProvider` | implemented | medium | Produces explicit local/offline payloads for `nns_nowcast_panel`. | | Bootstrap/Monte Carlo: `nns_meboot`, `nns_mc` | implemented | medium | Deterministic diagnostics are parity-tested; exact stochastic replicate parity with R is not expected. | -| Stochastic dominance/superiority: `fsd`, `ssd`, `tsd`, `.uni` wrappers, `nns_ss`, `nns_sd_cluster`, `sd_efficient_set` | implemented | medium | Public structures and deterministic paths are covered. SD uses exact pure-NumPy prefix-pair kernels plus a degree-1 discrete order-statistic matrix path; R's C++ core remains faster on full finance fixtures. Stochastic intervals use PyNNS RNG. | +| Stochastic dominance/superiority: `fsd`, `ssd`, `tsd`, `.uni` wrappers, `nns_ss`, `nns_sd_cluster`, `sd_efficient_set` | implemented | medium | Public structures and deterministic paths are covered. SD uses exact pure-NumPy prefix-pair kernels plus a degree-1 discrete order-statistic matrix path; R's C++ core remains faster on full finance fixtures. Stochastic intervals use NNS Python RNG. | | ANOVA: `nns_anova` | implemented | high | Binary, multi-group, pairwise, and degenerate `NaN` conventions are covered. | | Normalization: `nns_norm` | implemented | high | Numeric matrix path is implemented. | | Categorical helpers: `encode_factor_codes`, `factor_2_dummy`, `factor_2_dummy_fr`, `prepare_factor_predictors` | implemented | high | Explicit `levels=` / `factor_levels=` should be used to reproduce R factor ordering. `prepare_factor_predictors(...)` exposes the regression-ready full-rank design matrix path. | @@ -69,24 +69,24 @@ invariant, and property coverage. | Area | Path | Current behavior | Reason / next action | |---|---|---|---| | Multivariate regression | direct `factor_2_dummy=True` raw predictor path | Guarded with `NotImplementedError` in direct `nns_m_reg(..., factor_2_dummy=True)`. | Installed R direct `NNS.M.reg` raw factor input errors. Use `prepare_factor_predictors(...)` first, or use the public `nns_reg(..., factor_2_dummy=True, factor_levels=...)` expansion path. | -| Boost | `threshold` on the `n_features > 10` stochastic path | Guarded with `NotImplementedError` on the high-feature stochastic epoch path. | Installed R errors because `test.features` is never built. PyNNS keeps this explicit. | -| Boost/factor predictors | named data-frame factor predictor ordering | Deferred, not represented as a named-column API. | PyNNS uses positional `X1`, `X2`, ... semantics. Installed R named data frames can reorder columns alphabetically before `data.matrix`. | +| Boost | `threshold` on the `n_features > 10` stochastic path | Guarded with `NotImplementedError` on the high-feature stochastic epoch path. | Installed R errors because `test.features` is never built. NNS Python keeps this explicit. | +| Boost/factor predictors | named data-frame factor predictor ordering | Deferred, not represented as a named-column API. | NNS Python uses positional `X1`, `X2`, ... semantics. Installed R named data frames can reorder columns alphabetically before `data.matrix`. | ## Intentional Design Boundaries - No hidden network fetching happens by default. -- PyNNS does not export `nns_nowcast`; R NNS 12.1 beta removed `NNS.nowcast`. +- NNS Python does not export `nns_nowcast`; R NNS 12.1 beta removed `NNS.nowcast`. - Nowcast providers are payload builders for `nns_nowcast_panel`, not implicit public forecast wrappers. - `CsvNowcastProvider` is local/offline. - Library code does not auto-load `.env` files. - External data clients and dataframe libraries are not dependencies. -- PyNNS uses explicit Python errors for some cases where R silently truncates, +- NNS Python uses explicit Python errors for some cases where R silently truncates, coerces, warns, or returns unusable values. Important divergences are recorded in `docs/conventions.md`. - Stochastic exact stream parity is not expected. Stochastic paths use NumPy RNG and are tested structurally/statistically. -- Plotting side effects from R APIs are generally ignored; PyNNS returns data. +- Plotting side effects from R APIs are generally ignored; NNS Python returns data. - Stochastic-dominance performance work stays pure NumPy for alpha. The current implementation mirrors R's sorted-column/prefix-sum algorithm and adds Python-specific guard pruning, kept-only active-set scans for degree 2/3 and @@ -100,15 +100,15 @@ Nowcast provider support is explicit. Providers return payloads; callers pass the payload to `nns_nowcast_panel`: ```python -from pynns import nns_nowcast_panel -from pynns.providers import CsvNowcastProvider +from nns import nns_nowcast_panel +from nns.providers import CsvNowcastProvider provider = CsvNowcastProvider("monthly_panel.csv") payload = provider.fetch((), "2000-01-03") result = nns_nowcast_panel(payload["series"], h=2, tau=12, dates=payload["dates"]) ``` -PyNNS does not ship a default Yahoo, FRED, or other live-data workflow hidden +NNS Python does not ship a default Yahoo, FRED, or other live-data workflow hidden behind a public nowcast wrapper. ## Intentional Divergences And Caveats @@ -120,7 +120,7 @@ examples include: - Co-moment length mismatches raise `ValueError`; R warns, truncates, and divides by the longer length. - Factor and class labels are explicit. R factor levels become numeric codes; - PyNNS callers should pass `levels=` or `class_levels=` when ordering matters. + NNS Python callers should pass `levels=` or `class_levels=` when ordering matters. - Public outputs use NumPy arrays and plain dictionaries instead of R `data.table` objects. - Some installed-R quirks are intentionally matched when they affect stable @@ -131,14 +131,14 @@ examples include: stage. Strict xfails track current installed-R deviations in the Iris classification vignette, the documented ARMA numeric multi-lag weighting divergence, and VAR's ARMA-derived univariate/ensemble outputs. The Iris - classification xfail mixes two different issues: PyNNS stack predicts the + classification xfail mixes two different issues: NNS Python stack predicts the correct held-out class where installed R NNS 12.1 rounds the same borderline estimate down, while boost remains a true output disparity whose installed-R - and PyNNS balanced predictions both miss the held-out class. + and NNS Python balanced predictions both miss the held-out class. ## Release-Relevant Caveats -- PyNNS is alpha. The public API is parity-focused but not declared stable. +- NNS Python is alpha. The public API is parity-focused but not declared stable. - This is not full R parity yet. - `dy_d` scalar and vectorized point/distribution modes are covered on focused fixtures. Multi-row mixed derivative point matrices intentionally use @@ -150,11 +150,11 @@ examples include: ## Internal Or Out Of Scope Some R NNS helper names are implementation details or lower-level surfaces in -the R package rather than APIs PyNNS should expose one-for-one. Examples include +the R package rather than APIs NNS Python should expose one-for-one. Examples include `NNS.ANOVA.bin`, `Uni.caus`, compiled `*_cpp` shims, sampling helpers, and generated-vector helpers. -PyNNS implements the corresponding behavior natively in Python where it is +NNS Python implements the corresponding behavior natively in Python where it is needed by public APIs. It does not mirror every R helper name as a top-level Python export. Matrix-style public behavior is exposed where supported through Python names such as `causal_matrix`; not exposing an exact R helper name does diff --git a/docs/benchmarks.md b/docs/benchmarks.md index 40d2dfb0..c5b74580 100644 --- a/docs/benchmarks.md +++ b/docs/benchmarks.md @@ -123,7 +123,7 @@ baseline so Python/R comparisons remain visible when R has not been rerun. Run only the realistic Python benchmarks with: ```bash -PYNNS_OFFLINE=1 uv run pytest -q -n0 -m benchmark --benchmark-enable \ +NNS_OFFLINE=1 uv run pytest -q -n0 -m benchmark --benchmark-enable \ --benchmark-json=docs/benchmark_reports/realistic_sd_python_latest.json \ tests/benchmarks/test_stochastic_dominance_realistic.py \ tests/benchmarks/test_finance_sd_rolling.py \ @@ -195,5 +195,5 @@ Interpretation: - The implementation deliberately follows R's C++ SD algorithmic structure: sorted columns, prefix sums, pair-threshold dominance checks, exact guards, and no tolerance-based shortcuts. -- Full-fixture PyNNS runs are feasible for research iteration, but R's C++ SD +- Full-fixture NNS Python runs are feasible for research iteration, but R's C++ SD core remains materially faster on the largest cluster cases. diff --git a/docs/conventions.md b/docs/conventions.md index 82f9d444..26faaa12 100644 --- a/docs/conventions.md +++ b/docs/conventions.md @@ -2,10 +2,11 @@ ## Build -PyNNS is currently a pure-Python/NumPy/SciPy port. The earlier native extension -scaffolding was removed after the core port demonstrated pure NumPy/SciPy parity -and competitive performance. Reintroduce native code only as a deliberate future -change backed by benchmarks. +NNS Python is packaged as the `NNS` distribution and imported with `import nns`. +It includes the `nns._nnscore` native extension backed by the vendored C++ core in +`extern/NNS-core`, while preserving Python fallbacks for the public APIs that route +through the native backend. CI parity is cache-backed and does not require +`Rscript`; `Rscript` is only needed for local cache regeneration. ## Degree-Zero Boundary @@ -17,21 +18,21 @@ For any non-empty finite input, `LPM + UPM = 1` at degree zero. ## Empty Input Divergence From R R NNS returns `NaN` for empty input. -PyNNS raises `ValueError`. +NNS Python raises `ValueError`. Rationale: empty arrays in Python are upstream bugs, and NumPy convention is to warn or fail on empty reductions rather than silently produce a meaningful statistic. ## Co-Moment Length Mismatch Divergence From R R NNS warns when `x` and `y` lengths differ, computes over the shorter length, and divides by the longer length. -PyNNS raises `ValueError`. +NNS Python raises `ValueError`. Rationale: mismatched co-moment inputs lose observations silently in R. Python callers should fix alignment before computing a bivariate statistic. ## PM Matrix Target Defaults R `PM.matrix` uses column means when `target` is `NULL` or any non-numeric value. -PyNNS accepts `None` and `"mean"` for this behavior. PyNNS also broadcasts a +NNS Python accepts `None` and `"mean"` for this behavior. NNS Python also broadcasts a scalar numeric target across all variables; R requires callers to pass an explicit vector such as `rep(0, ncol(variable))`. Target vectors whose length does not match the number of variables raise `ValueError`. @@ -56,17 +57,17 @@ and `p_star = p_gt + 0.5 * p_tie`. `NaN` values are omitted independently from `x` and `y`, matching R's `na.omit` preprocessing. With `confidence_interval=True`, intervals are computed through `nns_meboot`, `lpm_var`, and `upm_var`; exact bootstrap parity with R is not expected because -the RNG streams differ. `random_seed` is a PyNNS-only reproducibility +the RNG streams differ. `random_seed` is a NNS Python-only reproducibility convenience for that stochastic path. `nns_sd_cluster` maps to R's `NNS.SD.cluster` default path. It iteratively peels `sd_efficient_set` results and returns a dictionary of `Cluster_1`, `Cluster_2`, ... memberships. The output contains variable names, not numeric -cluster labels; when names are omitted, PyNNS uses R-style `X_1`, `X_2`, ... +cluster labels; when names are omitted, NNS Python uses R-style `X_1`, `X_2`, ... names. `type="continuous"` is supported for first-degree efficient sets. `dendrogram=True` returns a plain dictionary mirroring R's `hclust` fields: `merge`, `height`, `order`, `labels`, `method`, `call`, and `dist.method`. -PyNNS does not plot the dendrogram; it only returns the object data. +NNS Python does not plot the dendrogram; it only returns the object data. The stochastic-dominance implementation is deliberately pure NumPy. It mirrors R's C++ SD core mathematically by sorting each column once, storing prefix sums, @@ -88,7 +89,7 @@ changes are introduced. Polars is intentionally not used in this SD kernel because the hot path is dense pairwise threshold evaluation rather than data-frame grouping or filtering. R remains faster on some large finance fixtures because its C++ path walks merged sorted thresholds in tight parallel -loops with minimal temporaries; PyNNS instead uses NumPy order-statistic blocks, +loops with minimal temporaries; NNS Python instead uses NumPy order-statistic blocks, `searchsorted`, contiguous column storage, and early-exit scans to stay dependency-light and pure Python for alpha. @@ -104,17 +105,17 @@ handled inside `nns_cdf` without loosening the global partial-moment APIs. ## Dependence `nns_dep` follows R's `NNS.dep` bivariate path, including `NNS.gravity` handling -for zero-range inputs and non-positive or non-finite bin widths. PyNNS also caps +for zero-range inputs and non-positive or non-finite bin widths. NNS Python also caps the internal gravity bin count at `4 * len(input)` to prevent pathological allocations on inputs where R's C++ `int` conversion effectively collapses an absurd bin count. `abs(Correlation) <= Dependence` is not guaranteed by -`NNS.dep`; both R and PyNNS can return signed correlation magnitudes above the +`NNS.dep`; both R and NNS Python can return signed correlation magnitudes above the dependence component for near-binary inputs. ## Copula `nns_copula(x, y)` is the bivariate scalar form of R's `NNS.copula(cbind(x, y))`. -When targets are omitted, PyNNS uses column means, matching R's `target = NULL`. +When targets are omitted, NNS Python uses column means, matching R's `target = NULL`. The `target_x` and `target_y` arguments map to R's two-element target vector. ## Causation @@ -136,7 +137,7 @@ supported. arrays. Installed R 12.1 only distinguishes `type = NULL` from any non-null `type`: `None` uses XY quadrant splits, while every non-`None` value uses X-only splits. This differs from documentation that implies separate `"X"`, -`"Y"`, and `"XONLY"` modes. PyNNS matches the installed binary. +`"Y"`, and `"XONLY"` modes. NNS Python matches the installed binary. `order="max"` is rejected with `TypeError`; installed R coerces it to `NA` and returns a useless zero-order map. All five `noise_reduction` modes are supported: `"off"`, `"mean"`, `"median"`, `"mode"`, and `"mode_class"`. @@ -164,7 +165,7 @@ computed. For callers that want direct multivariate regression, use matrix into `nns_m_reg(...)`: ```python -from pynns import nns_m_reg, prepare_factor_predictors +from nns import nns_m_reg, prepare_factor_predictors design = prepare_factor_predictors( x, @@ -192,7 +193,7 @@ granularity. `order="max"` follows installed R's univariate convention: fitted values are the observed `y` values and `regression.points` is the sorted observed `(x, y)` map. The derivative table still comes from R's pre-reset regression-point construction, -which PyNNS matches rather than recomputing adjacent slopes from all observations. +which NNS Python matches rather than recomputing adjacent slopes from all observations. The `"mode"` and `"mode_class"` noise-reduction modes are accepted in the univariate path and use the shared `nns_part`/`nns_mode` implementation. The @@ -252,8 +253,8 @@ are supplied. `balance=True` is supported for classification and follows R's `downSample` + `upSample` structure: each non-empty class is downsampled to the minority count without replacement, each class is upsampled to the majority count with replacement, and the downsampled rows are concatenated before the -upsampled rows. Exact sampled-row parity with R is not expected because PyNNS -uses NumPy's RNG; `random_seed` is a PyNNS-only reproducibility convenience. +upsampled rows. Exact sampled-row parity with R is not expected because NNS Python +uses NumPy's RNG; `random_seed` is a NNS Python-only reproducibility convenience. Numeric and class prediction intervals are supported and are combined by installed R's weighted data.table arithmetic. For class stacks, single-method `method=1` and `method=2` return the delegated interval table unchanged; when @@ -263,22 +264,22 @@ installed R's weighted data.table arithmetic. For class stacks, single-method the tail `ts_test` rows, while CV testing uses the earlier rows `1:(n - ts_test)`. This is intentionally not changed even though it is counterintuitive. R's `CV.size = NULL` samples a random value between 0.2 and -1/3; PyNNS uses a deterministic default of `0.25`. Pass `cv_size` explicitly for +1/3; NNS Python uses a deterministic default of `0.25`. Pass `cv_size` explicitly for exact R parity. The installed-R 12.1 Iris classification vignette with `folds=1` is a documented -stack disparity rather than a PyNNS correctness target. On the `141:150` holdout, +stack disparity rather than a NNS Python correctness target. On the `141:150` holdout, the true labels are all class code `3`. Installed R 12.1 returns stack class code `2` for every row because its learned class-rounding threshold is about `0.60`; -PyNNS returns class code `3` for every row because its learned threshold is about +NNS Python returns class code `3` for every row because its learned threshold is about `0.29`. Both implementations have the same high-level shape in that case (`reg = 2`, `dim.red = 3`, raw combined stack near `2.5`), but the final threshold rounding differs. Since R default `folds=5` also returns class code -`3`, PyNNS keeps the behavior that matches the practical classification result +`3`, NNS Python keeps the behavior that matches the practical classification result instead of forcing installed-R-12.1 `folds=1` parity. Factor predictor expansion is supported for `nns_stack(method=1)` and -`nns_stack(method=2)` with explicit `factor_levels=` metadata. PyNNS expands +`nns_stack(method=2)` with explicit `factor_levels=` metadata. NNS Python expands training and test predictors together using the same full-rank dummy convention as installed R's aligned train/test builder. Pure factor-predictor `method=2` and `method=(1,2)` match installed R's fallback to method 1. Mixed @@ -291,13 +292,13 @@ explicit `factor_levels` expansion. `nns_boost` maps to R's numeric and deterministic classification `NNS.boost` paths and uses the real `nns_reg` and `nns_stack` implementations. The small-feature path (`n_features <= 10`, where R evaluates all feature -combinations) is supported. For `n_features > 10`, PyNNS follows R's stochastic +combinations) is supported. For `n_features > 10`, NNS Python follows R's stochastic epoch structure: it samples learner-trial feature sets, builds a weighted survivor feature pool, then samples epoch feature counts and survivor features from that pool. Exact sampled-feature parity with R is not expected because -PyNNS uses NumPy's RNG, and `random_seed` is PyNNS-only. Installed R errors for +NNS Python uses NumPy's RNG, and `random_seed` is NNS Python-only. Installed R errors for `threshold=` on this path because the threshold short-circuit leaves -`test.features` undefined, so PyNNS keeps that guard. `ts_test` is supported on +`test.features` undefined, so NNS Python keeps that guard. `ts_test` is supported on the stochastic path and follows R's separate epoch holdout split: initial learner trials test rows `1:(n - ts_test)`, while epochs test the final `2 * ts_test + 1` rows. `type="class"` returns numeric class codes, not labels; use @@ -305,46 +306,46 @@ learner trials test rows `1:(n - ts_test)`, while epochs test the final rejected unless explicit levels are supplied. `balance=True` is supported for classification and uses the same R-style `downSample` + `upSample` structure as `nns_stack`; exact sampled-row parity with R is not expected. -Explicit-level factor predictors are supported through `factor_levels=`. PyNNS +Explicit-level factor predictors are supported through `factor_levels=`. NNS Python integer-codes those columns before deterministic feature selection, matching -installed R's `data.matrix` conversion under PyNNS' positional-column +installed R's `data.matrix` conversion under NNS Python' positional-column convention. Pass `None` for numeric columns in mixed predictor matrices, for example `factor_levels=(["low", "mid", "high"], None)`. Multiple explicit-level factor predictor columns use positional `X1`, `X2`, ... semantics; installed R data frames with semantic column names sort columns alphabetically before -fitting, so callers should order PyNNS columns explicitly when reproducing those +fitting, so callers should order NNS Python columns explicitly when reproducing those named-data-frame cases. Numeric `pred_int` is supported and delegates to `nns_stack(pred_int=...)`, matching installed R; it is deterministic and does not use MC/meboot. `features_only=True` returns before the final stack fit and ignores `pred_int`, matching R. Classification `pred_int` is supported and delegates to final stack `method=1`, so interval bounds remain raw numeric values. `ts_test` is supported for deterministic and stochastic boost paths. R -requires usable column names for matrix inputs; PyNNS uses positional numeric columns. As with `nns_stack`, R -samples a random CV size when `CV.size = NULL`; PyNNS uses deterministic +requires usable column names for matrix inputs; NNS Python uses positional numeric columns. As with `nns_stack`, R +samples a random CV size when `CV.size = NULL`; NNS Python uses deterministic `cv_size=0.25` unless specified. For classification boost, final predictions, feature weights, and feature frequencies are parity-tested against installed R when balance is disabled and structurally tested when balance sampling is enabled. The public `n.best` value is structural-only because R's final internal -`NNS.stack` call samples its own `CV.size = NULL` split, while PyNNS keeps the +`NNS.stack` call samples its own `CV.size = NULL` split, while NNS Python keeps the deterministic stack default. The installed-R 12.1 Iris boost vignette remains a true parity gap, but not a quality target for exact output matching. On the same all-class-`3` holdout, -installed R 12.1 balanced boost returns class code `1` for every row, while PyNNS +installed R 12.1 balanced boost returns class code `1` for every row, while NNS Python balanced boost returns class code `2` for every row; both are wrong for that example. Installed R 12.1 also does not accept the `folds` argument shown in the rendered upstream overview for `NNS.boost`, so this example is tracked as R-version/upstream-example drift plus a boost parity gap rather than evidence -that PyNNS should copy the installed-R balanced output. +that NNS Python should copy the installed-R balanced output. ## Seasonality `nns_seas` maps to installed R's non-plotting `NNS.seas` path and ignores -`plot`, consistent with other PyNNS ports. Inputs shorter than five observations +`plot`, consistent with other NNS Python ports. Inputs shorter than five observations return R's sentinel period `0`. For mean-zero data, R falls back from coefficient -of variation to `abs(acf1) ** -1`; PyNNS follows the same fallback and +of variation to `abs(acf1) ** -1`; NNS Python follows the same fallback and non-finite handling. Installed R can report harmonics rather than the visually -obvious period, so PyNNS matches R's candidate-period screening instead of a +obvious period, so NNS Python matches R's candidate-period screening instead of a textbook seasonality heuristic. Results are cached by input content and modulo arguments with defensive copies on return; this preserves R semantics while avoiding repeated reverse-step scans for identical series. @@ -358,8 +359,8 @@ returns a dict keyed like R's data.table columns (`Estimates`, recursive: each estimate is appended before the next horizon step. Plot arguments are ignored. Prediction intervals use `nns_mc` / `nns_meboot`; exact stochastic parity with R is not expected because RNG streams differ. -`random_seed` is a PyNNS-only convenience for reproducible interval tests. -No-`pred_int` deterministic forecasts are parity-tested except where PyNNS +`random_seed` is a NNS Python-only convenience for reproducible interval tests. +No-`pred_int` deterministic forecasts are parity-tested except where NNS Python intentionally uses a more direct seasonal-lag weighting convention. `seasonal_factor=True` uses only the first detected period from `nns_seas`, matching `ARMA.seas.weighting(TRUE, ...)`; `seasonal_factor=False` uses the @@ -371,11 +372,11 @@ multi-lag seasonal factors is rejected because installed R errors during numeric multiplication on that path. For explicit numeric multi-lag seasonal factors such as -`seasonal_factor=[132, 276]`, PyNNS intentionally weights each candidate lag by +`seasonal_factor=[132, 276]`, NNS Python intentionally weights each candidate lag by the coefficient of variation of that actual lag's reverse component series. Installed R NNS instead computes the coefficient-of-variation term with reverse steps `1:length(seasonal.factor)` while still applying the observation penalty -to the actual lag values. PyNNS keeps the actual-lag weighting because it better +to the actual lag values. NNS Python keeps the actual-lag weighting because it better matches the documented idea that each supplied seasonal factor is weighted by its own seasonality strength and observation count. The R-compatible difference is covered by a strict xfail practical test rather than hidden. @@ -410,12 +411,12 @@ to numeric series, delegates numeric forecasting to `nns_var`, and returns VAR fields plus `dates` and `metadata` dictionaries. Date labels are metadata rather than array indices. Without dates, forecast rows are labeled `t+1`, `t+2`, ... With dates, inputs are normalized to `YYYY-MM`, must be sorted and unique, and -forecast labels advance monthly. R NNS 12.1 beta removed `NNS.nowcast`, so PyNNS +forecast labels advance monthly. R NNS 12.1 beta removed `NNS.nowcast`, so NNS Python does not export a public `nns_nowcast` wrapper. `CsvNowcastProvider` remains an explicit payload builder whose `fetch(series, start_date)` method returns `{"series": ..., "dates": ..., "metadata": ...}` for callers to pass to `nns_nowcast_panel`. `CsvNowcastProvider` is offline and local-file only. -Library code does not read `.env` files. PyNNS does not ship an implicit +Library code does not read `.env` files. NNS Python does not ship an implicit FRED/Yahoo provider. ## Meboot @@ -426,11 +427,11 @@ Scalar `rho` returns one result dictionary; vector `rho` returns a list of resul dictionaries in R's vectorized order. `rho=None` follows installed R's empty output behavior, and length-one input returns only `{"x": x}`. -Exact replicate parity with R is not expected because PyNNS uses NumPy's random +Exact replicate parity with R is not expected because NNS Python uses NumPy's random number generator and SciPy's optimizer while R uses its global RNG and `optim()`. Deterministic diagnostics (`xx`, `z`, `dv`, `dvtrim`, `xmin`, `xmax`, `desintxb`, `ordxx`, and `kappa`) are parity-tested exactly. Stochastic -outputs are tested structurally and statistically. `random_seed` is a PyNNS-only +outputs are tested structurally and statistically. `random_seed` is a NNS Python-only convenience for reproducible bootstrap draws. ## Monte Carlo @@ -438,10 +439,10 @@ convenience for reproducible bootstrap draws. `nns_mc` maps to R's `NNS.MC` wrapper around `NNS.meboot`. The rho grid and exponential rho transformation are parity-tested exactly against installed R. As with `nns_meboot`, exact stochastic replicate parity is not expected because -R and PyNNS use different RNG streams and optimizer implementations. -`random_seed` is a PyNNS-only convenience passed through to `nns_meboot`. +R and NNS Python use different RNG streams and optimizer implementations. +`random_seed` is a NNS Python-only convenience passed through to `nns_meboot`. -PyNNS returns `{"ensemble": array, "replicates": dict}`. The `replicates` +NNS Python returns `{"ensemble": array, "replicates": dict}`. The `replicates` mapping preserves R's names, such as `"rho = 1"` and `"rho = -0.5"`, with each value containing that rho block's replicate matrix. Sampling-vignette examples are covered as smoke tests, but installed R behavior remains the parity source. @@ -449,7 +450,7 @@ are covered as smoke tests, but installed R behavior remains the parity source. ## Normalization `nns_norm(x, linear=False)` maps to R's numeric matrix `NNS.norm` path with -plotting disabled. PyNNS accepts finite 2D arrays. `linear=True` uses R's +plotting disabled. NNS Python accepts finite 2D arrays. `linear=True` uses R's mean-ratio scaling, while `linear=False` additionally weights scaling by absolute correlation for fewer than 10 columns and NNS dependence for 10 or more columns. @@ -457,30 +458,30 @@ more columns. ## Distance `nns_distance` and `nns_distance_bulk` map to R's regression-point-matrix -helpers. PyNNS accepts `rpm` as a finite 2D numeric array with R's `y.hat` +helpers. NNS Python accepts `rpm` as a finite 2D numeric array with R's `y.hat` column in the final position. `nns_distance` applies R's per-target min-max rescaling before computing weighted nearest-neighbor predictions. `nns_distance_bulk` matches R's compiled bulk helper, including its raw-feature distance convention. -For `nns_distance` with `k > 1`, PyNNS matches the installed R 12.1 binary: +For `nns_distance` with `k > 1`, NNS Python matches the installed R 12.1 binary: the exponential rank-weight family uses the R C API's `Rf_dexp` scale argument as `1 / k`. This differs from the nearby source-code comment that describes it as a rate. Classification distance mode returns numeric class codes, not original labels. For single-target `nns_distance(..., class_=...)`, installed R uses weighted -mode with integer replication counts `ceil(100 * weight)`. PyNNS follows that -behavior. For equal-distance nearest-neighbor ties, PyNNS preserves RPM row order +mode with integer replication counts `ceil(100 * weight)`. NNS Python follows that +behavior. For equal-distance nearest-neighbor ties, NNS Python preserves RPM row order to match installed R's first-row tie behavior. Installed R's `NNS.distance.bulk(..., class=...)` currently ignores the class flag in its compiled bulk helper and returns the same inverse-distance -numeric weighted average as non-class bulk distance; PyNNS matches the installed +numeric weighted average as non-class bulk distance; NNS Python matches the installed binary rather than the higher-level classification intent. ## Classification R classification paths work with numeric class codes. R factors become 1-indexed numeric codes in factor-level order and predictions are returned as -codes rather than decoded labels. PyNNS provides `factor_2_dummy`, +codes rather than decoded labels. NNS Python provides `factor_2_dummy`, `factor_2_dummy_fr`, `encode_factor_codes`, and `prepare_factor_predictors`; pass explicit `levels=` / `factor_levels=` to reproduce R factor level order because NumPy arrays do not carry factor metadata. @@ -488,7 +489,7 @@ because NumPy arrays do not carry factor metadata. `nns_reg(..., type="class")`, `nns_m_reg(..., type="class")`, and `nns_stack(..., type="class")` are supported for numeric, logical, and factor-like targets. Use `class_levels=` when passing string/object labels so -PyNNS can reproduce R factor codes explicitly. Raw string classification remains +NNS Python can reproduce R factor codes explicitly. Raw string classification remains rejected where installed R errors or produces unusable `NA` conversions. Predictions and point estimates are numeric class codes, not original labels, matching installed R. Class confidence intervals are supported in `nns_reg` and @@ -507,7 +508,7 @@ around smooth `nns_reg` point estimates and return a table-like dictionary with `eval.point`, `first.derivative`, and `second.derivative`. Boundary-point quirks follow installed R where covered by parity tests. -PyNNS derivative parity is defined at the public input/output level, while +NNS Python derivative parity is defined at the public input/output level, while preserving R's cumulative finite-difference perturbation pattern for `dy_d`. `dy_d` scalar `wrt` has enforced R parity for `eval_points="mean"`, `"median"`, `"last"`, `"obs"`, and `"apd"`. Vectorized `wrt` returns one row per eval point @@ -518,21 +519,21 @@ calculus derivative. Mixed derivatives require a two-regressor input. Numeric two-value evaluation points and single-row point modes match installed R on focused fixtures. For -multi-row matrix evaluation points, including `eval_points="obs"`, PyNNS uses a +multi-row matrix evaluation points, including `eval_points="obs"`, NNS Python uses a pointwise mixed finite-difference construction. Installed R's vectorized list-matrix path packs multi-row mixed derivative points in an order-dependent -way, so PyNNS does not copy that packing quirk. +way, so NNS Python does not copy that packing quirk. For scalar `dy_d`, R mutates lower and upper finite-difference points cumulatively across rounded bandwidths. If rounded bandwidths repeat, R writes the later cumulative result back to the first matching result slot and drops -the empty slots during final weighted averaging; PyNNS mirrors that behavior. +the empty slots during final weighted averaging; NNS Python mirrors that behavior. The `obs` and `apd` paths also rely on smooth dimensional-reduction `nns_reg(..., point_est=..., dim_red_method="equal", smooth=True)` estimates. For out-of-range smooth point estimates, R derives extrapolation slopes from the smoothed regression points before clamping returned regression-point `y` values, then anchors the extrapolation at the first `which.min` / `which.max` -boundary row. PyNNS mirrors those boundary quirks for parity. +boundary row. NNS Python mirrors those boundary quirks for parity. ## ANOVA @@ -542,5 +543,5 @@ comparisons return `{"Certainty": value}`, and `pairwise=True` returns R's symmetric certainty matrix. Confidence interval bootstrapping is structurally identical to R but uses NumPy RNG instead of R's `sample()`, so exact per-call parity is not achievable; numeric values converge to the same population CI. -Pass `random_seed` for reproducible PyNNS results. Degenerate zero-variance +Pass `random_seed` for reproducible NNS Python results. Degenerate zero-variance groups preserve R's `NaN` CDF/certainty convention. diff --git a/docs/examples/README.md b/docs/examples/README.md index e73041d6..35162fc5 100644 --- a/docs/examples/README.md +++ b/docs/examples/README.md @@ -1,4 +1,4 @@ -# PyNNS Examples +# NNS Python Examples These examples are Python-native companions to the upstream R NNS documentation, not one-for-one copies of the R reports. Each script is runnable, @@ -13,7 +13,7 @@ The upstream R repository contains several kinds of material: studies. Use those upstream files as conceptual references. Use the examples here when -you want short Python call patterns that are kept in sync with PyNNS. +you want short Python call patterns that are kept in sync with NNS Python. ## Runnable Examples diff --git a/docs/examples/classification.py b/docs/examples/classification.py index 0d133de1..313d7ae7 100644 --- a/docs/examples/classification.py +++ b/docs/examples/classification.py @@ -2,7 +2,7 @@ import numpy as np -from pynns import nns_m_reg, nns_reg, nns_stack +from nns import nns_m_reg, nns_reg, nns_stack def main() -> None: diff --git a/docs/examples/dependence.py b/docs/examples/dependence.py index 3762fc10..9c9e2a13 100644 --- a/docs/examples/dependence.py +++ b/docs/examples/dependence.py @@ -2,7 +2,7 @@ import numpy as np -from pynns import causal_matrix, nns_causation, nns_copula, nns_cor, nns_dep +from nns import causal_matrix, nns_causation, nns_copula, nns_cor, nns_dep def main() -> None: diff --git a/docs/examples/distributions_anova.py b/docs/examples/distributions_anova.py index 1209859d..9f60ce5a 100644 --- a/docs/examples/distributions_anova.py +++ b/docs/examples/distributions_anova.py @@ -2,7 +2,7 @@ import numpy as np -from pynns import nns_anova, nns_cdf +from nns import nns_anova, nns_cdf def main() -> None: diff --git a/docs/examples/forecasting.py b/docs/examples/forecasting.py index 5c34b550..60c6ed5c 100644 --- a/docs/examples/forecasting.py +++ b/docs/examples/forecasting.py @@ -2,7 +2,7 @@ import numpy as np -from pynns import nns_arma, nns_arma_optim, nns_seas, nns_var +from nns import nns_arma, nns_arma_optim, nns_seas, nns_var def main() -> None: diff --git a/docs/examples/notebooks/01_partial_moments_risk_workflow.ipynb b/docs/examples/notebooks/01_partial_moments_risk_workflow.ipynb index 2f7a6f8a..f4f40d74 100644 --- a/docs/examples/notebooks/01_partial_moments_risk_workflow.ipynb +++ b/docs/examples/notebooks/01_partial_moments_risk_workflow.ipynb @@ -15,7 +15,7 @@ "source": [ "import numpy as np\n", "\n", - "from pynns import (\n", + "from nns import (\n", " lpm,\n", " lpm_ratio,\n", " mean_pm,\n", diff --git a/docs/examples/notebooks/02_regression_classification_workflow.ipynb b/docs/examples/notebooks/02_regression_classification_workflow.ipynb index 9f28a804..69c00fe6 100644 --- a/docs/examples/notebooks/02_regression_classification_workflow.ipynb +++ b/docs/examples/notebooks/02_regression_classification_workflow.ipynb @@ -15,7 +15,7 @@ "source": [ "import numpy as np\n", "\n", - "from pynns import (\n", + "from nns import (\n", " dy_d,\n", " dy_dx,\n", " nns_boost,\n", diff --git a/docs/examples/notebooks/03_forecasting_nowcast_workflow.ipynb b/docs/examples/notebooks/03_forecasting_nowcast_workflow.ipynb index 88a22028..27cba763 100644 --- a/docs/examples/notebooks/03_forecasting_nowcast_workflow.ipynb +++ b/docs/examples/notebooks/03_forecasting_nowcast_workflow.ipynb @@ -18,8 +18,8 @@ "\n", "import numpy as np\n", "\n", - "from pynns import nns_arma, nns_arma_optim, nns_nowcast_panel, nns_seas, nns_var\n", - "from pynns.providers import CsvNowcastProvider\n", + "from nns import nns_arma, nns_arma_optim, nns_nowcast_panel, nns_seas, nns_var\n", + "from nns.providers import CsvNowcastProvider\n", "\n", "np.set_printoptions(precision=4, suppress=True)\n", "rng = np.random.default_rng(21)\n" @@ -175,7 +175,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Local nowcast panel\nR NNS 12.1 removed `NNS.nowcast`; PyNNS keeps the local panel workflow.\n" + "## Local nowcast panel\nR NNS 12.1 removed `NNS.nowcast`; NNS Python keeps the local panel workflow.\n" ] }, { diff --git a/docs/examples/notebooks/04_distribution_dominance_simulation_workflow.ipynb b/docs/examples/notebooks/04_distribution_dominance_simulation_workflow.ipynb index d9b67a0e..1555c56c 100644 --- a/docs/examples/notebooks/04_distribution_dominance_simulation_workflow.ipynb +++ b/docs/examples/notebooks/04_distribution_dominance_simulation_workflow.ipynb @@ -15,7 +15,7 @@ "source": [ "import numpy as np\n", "\n", - "from pynns import (\n", + "from nns import (\n", " fsd,\n", " nns_anova,\n", " nns_cdf,\n", diff --git a/docs/examples/notebooks/05_boston_housing_regression_workflow.ipynb b/docs/examples/notebooks/05_boston_housing_regression_workflow.ipynb index 63c5bde5..55d2158e 100644 --- a/docs/examples/notebooks/05_boston_housing_regression_workflow.ipynb +++ b/docs/examples/notebooks/05_boston_housing_regression_workflow.ipynb @@ -29,7 +29,7 @@ "\n", "import numpy as np\n", "\n", - "from pynns import nns_dep, nns_m_reg, nns_part, nns_reg, nns_stack\n", + "from nns import nns_dep, nns_m_reg, nns_part, nns_reg, nns_stack\n", "\n", "np.set_printoptions(precision=4, suppress=True)\n", "\n", diff --git a/docs/examples/nowcast_panel.py b/docs/examples/nowcast_panel.py index 0ebd098c..d33b53be 100644 --- a/docs/examples/nowcast_panel.py +++ b/docs/examples/nowcast_panel.py @@ -5,8 +5,8 @@ import numpy as np -from pynns import nns_nowcast_panel -from pynns.providers import CsvNowcastProvider +from nns import nns_nowcast_panel +from nns.providers import CsvNowcastProvider def main() -> None: diff --git a/docs/examples/partial_moments.py b/docs/examples/partial_moments.py index d0535f6f..9c0b19d2 100644 --- a/docs/examples/partial_moments.py +++ b/docs/examples/partial_moments.py @@ -2,7 +2,7 @@ import numpy as np -from pynns import ( +from nns import ( co_lpm, co_upm, d_lpm, diff --git a/docs/examples/regression.py b/docs/examples/regression.py index 00355018..f021c598 100644 --- a/docs/examples/regression.py +++ b/docs/examples/regression.py @@ -2,7 +2,7 @@ import numpy as np -from pynns import nns_m_reg, nns_part, nns_reg +from nns import nns_m_reg, nns_part, nns_reg def main() -> None: diff --git a/docs/native_original_src_coverage.md b/docs/native_original_src_coverage.md index 86d701c3..abdc11d0 100644 --- a/docs/native_original_src_coverage.md +++ b/docs/native_original_src_coverage.md @@ -30,39 +30,39 @@ Status values used below: | C++ header | C++ function or type | C++ source file | Existing Python public function | Existing Python module | Currently bound in `_nnscore` | Should be public Python API | Should be private backend helper only | Binding priority | Notes | |---|---|---|---|---|---|---|---|---|---| -| `partial_moments.hpp` | `PMMatrixResult` | `partial_moments.cpp` | `pm_matrix` result dict | `pynns.pm_matrix` | bound-public | yes | no | Phase 1 | Bound as dict preserving Python `cov.matrix` key. | -| `partial_moments.hpp` | `lpm` | `partial_moments.cpp` | `lpm` | `pynns.core` | bound-public | yes | no | Phase 1 | Existing binding confirmed and routed. | -| `partial_moments.hpp` | `upm` | `partial_moments.cpp` | `upm` | `pynns.core` | bound-public | yes | no | Phase 1 | Existing binding confirmed and routed. | -| `partial_moments.hpp` | `lpm_v` | `partial_moments.cpp` | `lpm` vector target path | `pynns.core` | bound-public | yes | no | Phase 1 | Also exposed as private explicit `_nnscore.lpm_v`. | -| `partial_moments.hpp` | `upm_v` | `partial_moments.cpp` | `upm` vector target path | `pynns.core` | bound-public | yes | no | Phase 1 | Also exposed as private explicit `_nnscore.upm_v`. | -| `partial_moments.hpp` | `lpm_ratio_v` | `partial_moments.cpp` | `lpm_ratio` | `pynns.core` | bound-public | yes | no | Phase 1 | Routed through native when available. | -| `partial_moments.hpp` | `upm_ratio_v` | `partial_moments.cpp` | `upm_ratio` | `pynns.core` | bound-public | yes | no | Phase 1 | Routed through native when available. | -| `partial_moments.hpp` | `co_lpm` | `partial_moments.cpp` | `co_lpm` | `pynns.co_moments` | bound-public | yes | no | Phase 1 | Scalar smoke binding plus vector route. | -| `partial_moments.hpp` | `co_upm` | `partial_moments.cpp` | `co_upm` | `pynns.co_moments` | bound-public | yes | no | Phase 1 | Scalar smoke binding plus vector route. | -| `partial_moments.hpp` | `d_lpm` | `partial_moments.cpp` | `d_lpm` | `pynns.co_moments` | bound-public | yes | no | Phase 1 | Scalar smoke binding plus vector route. | -| `partial_moments.hpp` | `d_upm` | `partial_moments.cpp` | `d_upm` | `pynns.co_moments` | bound-public | yes | no | Phase 1 | Scalar smoke binding plus vector route. | -| `partial_moments.hpp` | `co_lpm_v` | `partial_moments.cpp` | `co_lpm` vector target path | `pynns.co_moments` | bound-public | yes | no | Phase 1 | Preserves recycled target vector behavior. | -| `partial_moments.hpp` | `co_upm_v` | `partial_moments.cpp` | `co_upm` vector target path | `pynns.co_moments` | bound-public | yes | no | Phase 1 | Preserves recycled target vector behavior. | -| `partial_moments.hpp` | `d_lpm_v` | `partial_moments.cpp` | `d_lpm` vector target path | `pynns.co_moments` | bound-public | yes | no | Phase 1 | Preserves recycled target vector behavior. | -| `partial_moments.hpp` | `d_upm_v` | `partial_moments.cpp` | `d_upm` vector target path | `pynns.co_moments` | bound-public | yes | no | Phase 1 | Preserves recycled target vector behavior. | -| `partial_moments.hpp` | `clpm_nd` | `partial_moments.cpp` | `co_lpm_nd` | `pynns.dependence` | bound-private | yes | no | Phase 1 | Bound for native coverage; public routing deferred because current Python shape semantics need separate parity work. | -| `partial_moments.hpp` | `cupm_nd` | `partial_moments.cpp` | `co_upm_nd` | `pynns.dependence` | bound-private | yes | no | Phase 1 | Bound for native coverage; public routing deferred. | -| `partial_moments.hpp` | `dpm_nd` | `partial_moments.cpp` | `dpm_nd` | `pynns.dependence` | bound-private | yes | no | Phase 1 | Bound for native coverage; public routing deferred. | +| `partial_moments.hpp` | `PMMatrixResult` | `partial_moments.cpp` | `pm_matrix` result dict | `nns.pm_matrix` | bound-public | yes | no | Phase 1 | Bound as dict preserving Python `cov.matrix` key. | +| `partial_moments.hpp` | `lpm` | `partial_moments.cpp` | `lpm` | `nns.core` | bound-public | yes | no | Phase 1 | Existing binding confirmed and routed. | +| `partial_moments.hpp` | `upm` | `partial_moments.cpp` | `upm` | `nns.core` | bound-public | yes | no | Phase 1 | Existing binding confirmed and routed. | +| `partial_moments.hpp` | `lpm_v` | `partial_moments.cpp` | `lpm` vector target path | `nns.core` | bound-public | yes | no | Phase 1 | Also exposed as private explicit `_nnscore.lpm_v`. | +| `partial_moments.hpp` | `upm_v` | `partial_moments.cpp` | `upm` vector target path | `nns.core` | bound-public | yes | no | Phase 1 | Also exposed as private explicit `_nnscore.upm_v`. | +| `partial_moments.hpp` | `lpm_ratio_v` | `partial_moments.cpp` | `lpm_ratio` | `nns.core` | bound-public | yes | no | Phase 1 | Routed through native when available. | +| `partial_moments.hpp` | `upm_ratio_v` | `partial_moments.cpp` | `upm_ratio` | `nns.core` | bound-public | yes | no | Phase 1 | Routed through native when available. | +| `partial_moments.hpp` | `co_lpm` | `partial_moments.cpp` | `co_lpm` | `nns.co_moments` | bound-public | yes | no | Phase 1 | Scalar smoke binding plus vector route. | +| `partial_moments.hpp` | `co_upm` | `partial_moments.cpp` | `co_upm` | `nns.co_moments` | bound-public | yes | no | Phase 1 | Scalar smoke binding plus vector route. | +| `partial_moments.hpp` | `d_lpm` | `partial_moments.cpp` | `d_lpm` | `nns.co_moments` | bound-public | yes | no | Phase 1 | Scalar smoke binding plus vector route. | +| `partial_moments.hpp` | `d_upm` | `partial_moments.cpp` | `d_upm` | `nns.co_moments` | bound-public | yes | no | Phase 1 | Scalar smoke binding plus vector route. | +| `partial_moments.hpp` | `co_lpm_v` | `partial_moments.cpp` | `co_lpm` vector target path | `nns.co_moments` | bound-public | yes | no | Phase 1 | Preserves recycled target vector behavior. | +| `partial_moments.hpp` | `co_upm_v` | `partial_moments.cpp` | `co_upm` vector target path | `nns.co_moments` | bound-public | yes | no | Phase 1 | Preserves recycled target vector behavior. | +| `partial_moments.hpp` | `d_lpm_v` | `partial_moments.cpp` | `d_lpm` vector target path | `nns.co_moments` | bound-public | yes | no | Phase 1 | Preserves recycled target vector behavior. | +| `partial_moments.hpp` | `d_upm_v` | `partial_moments.cpp` | `d_upm` vector target path | `nns.co_moments` | bound-public | yes | no | Phase 1 | Preserves recycled target vector behavior. | +| `partial_moments.hpp` | `clpm_nd` | `partial_moments.cpp` | `co_lpm_nd` | `nns.dependence` | bound-private | yes | no | Phase 1 | Bound for native coverage; public routing deferred because current Python shape semantics need separate parity work. | +| `partial_moments.hpp` | `cupm_nd` | `partial_moments.cpp` | `co_upm_nd` | `nns.dependence` | bound-private | yes | no | Phase 1 | Bound for native coverage; public routing deferred. | +| `partial_moments.hpp` | `dpm_nd` | `partial_moments.cpp` | `dpm_nd` | `nns.dependence` | bound-private | yes | no | Phase 1 | Bound for native coverage; public routing deferred. | | `partial_moments.hpp` | `clpm_nd_batch` | `partial_moments.cpp` | none | none | bound-private | no | yes | Phase 1 | Backend vectorized helper only. | -| `partial_moments.hpp` | `pm_matrix` | `partial_moments.cpp` | `pm_matrix` | `pynns.pm_matrix` | bound-public | yes | no | Phase 1 | Routed through native with column-major flattening. | -| `central_tendencies.hpp` | `gravity` | `central_tendencies.cpp` | `nns_gravity` | `pynns.central_tendencies` | bound-private | yes | no | Phase 5 | Already bound before this PR; public routing was already present/available through module behavior. | -| `central_tendencies.hpp` | `rescale` | `central_tendencies.cpp` | `nns_rescale` | `pynns.central_tendencies` | cxx-exists-unbound | yes | no | Phase 5 | Left unbound to avoid changing risk-neutral/min-max edge behavior without parity tests. | -| `central_tendencies.hpp` | `mode` | `central_tendencies.cpp` | `nns_mode` | `pynns.central_tendencies` | bound-private | yes | no | Phase 5 | Already bound before this PR. | -| `fast_lm.hpp` | `FastLmResult` | `fast_lm.cpp` | `_fast_lm` result dict | `pynns.multivariate_regression` | bound-private | no | yes | Phase 2 | Existing `fast_lm` binding confirmed. | +| `partial_moments.hpp` | `pm_matrix` | `partial_moments.cpp` | `pm_matrix` | `nns.pm_matrix` | bound-public | yes | no | Phase 1 | Routed through native with column-major flattening. | +| `central_tendencies.hpp` | `gravity` | `central_tendencies.cpp` | `nns_gravity` | `nns.central_tendencies` | bound-private | yes | no | Phase 5 | Already bound before this PR; public routing was already present/available through module behavior. | +| `central_tendencies.hpp` | `rescale` | `central_tendencies.cpp` | `nns_rescale` | `nns.central_tendencies` | cxx-exists-unbound | yes | no | Phase 5 | Left unbound to avoid changing risk-neutral/min-max edge behavior without parity tests. | +| `central_tendencies.hpp` | `mode` | `central_tendencies.cpp` | `nns_mode` | `nns.central_tendencies` | bound-private | yes | no | Phase 5 | Already bound before this PR. | +| `fast_lm.hpp` | `FastLmResult` | `fast_lm.cpp` | `_fast_lm` result dict | `nns.multivariate_regression` | bound-private | no | yes | Phase 2 | Existing `fast_lm` binding confirmed. | | `fast_lm.hpp` | `FastLmMultResult` | `fast_lm.cpp` | none found | none | bound-private | no | yes | Phase 2 | Added native binding; no public route because no existing public wrapper uses it directly. | -| `fast_lm.hpp` | `fast_lm` | `fast_lm.cpp` | `_fast_lm` helper | `pynns.multivariate_regression` | bound-private | no | yes | Phase 2 | Existing binding confirmed; remains backend-only. | +| `fast_lm.hpp` | `fast_lm` | `fast_lm.cpp` | `_fast_lm` helper | `nns.multivariate_regression` | bound-private | no | yes | Phase 2 | Existing binding confirmed; remains backend-only. | | `fast_lm.hpp` | `fast_lm_mult` | `fast_lm.cpp` | none found | none | bound-private | no | yes | Phase 2 | Added smoke-tested backend binding. | | `internal_functions.hpp` | `ValueKind` | `internal_functions.cpp` | none | none | cxx-exists-unbound | no | yes | Phase 3 | Enum is only useful if `is_fcl` is exposed; Python has no direct type mapping need. | -| `internal_functions.hpp` | `is_fcl` | `internal_functions.cpp` | `_is_fcl` internal equivalent | `pynns.regression` | cxx-exists-unbound | no | yes | Phase 3 | Not bound; Python object dtype/factor detection is richer than the C++ enum boundary. | -| `internal_functions.hpp` | `Factor` | `internal_functions.cpp` | factor helpers | `pynns.categorical` | bound-private | no | yes | Phase 3 | Mapped to `(codes, levels)` arguments, not exposed as a C++ class. | -| `internal_functions.hpp` | `DummyMatrix` | `internal_functions.cpp` | factor helpers | `pynns.categorical` | bound-private | no | yes | Phase 3 | Returned as dict with flat column-major data, names, nrow, ncol. | -| `internal_functions.hpp` | `factor_2_dummy` | `internal_functions.cpp` | `factor_2_dummy` | `pynns.categorical` | bound-private | yes | yes | Phase 3 | Bound only as private backend helper; public routing deferred. | -| `internal_functions.hpp` | `factor_2_dummy_fr` | `internal_functions.cpp` | `factor_2_dummy_fr` | `pynns.categorical` | bound-private | yes | yes | Phase 3 | Bound only as private backend helper; public routing deferred. | +| `internal_functions.hpp` | `is_fcl` | `internal_functions.cpp` | `_is_fcl` internal equivalent | `nns.regression` | cxx-exists-unbound | no | yes | Phase 3 | Not bound; Python object dtype/factor detection is richer than the C++ enum boundary. | +| `internal_functions.hpp` | `Factor` | `internal_functions.cpp` | factor helpers | `nns.categorical` | bound-private | no | yes | Phase 3 | Mapped to `(codes, levels)` arguments, not exposed as a C++ class. | +| `internal_functions.hpp` | `DummyMatrix` | `internal_functions.cpp` | factor helpers | `nns.categorical` | bound-private | no | yes | Phase 3 | Returned as dict with flat column-major data, names, nrow, ncol. | +| `internal_functions.hpp` | `factor_2_dummy` | `internal_functions.cpp` | `factor_2_dummy` | `nns.categorical` | bound-private | yes | yes | Phase 3 | Bound only as private backend helper; public routing deferred. | +| `internal_functions.hpp` | `factor_2_dummy_fr` | `internal_functions.cpp` | `factor_2_dummy_fr` | `nns.categorical` | bound-private | yes | yes | Phase 3 | Bound only as private backend helper; public routing deferred. | | `internal_functions.hpp` | `vec_sd` | `internal_functions.cpp` | none public | none | bound-private | no | yes | Phase 3 | Safe numeric helper bound for backend use. | | `internal_functions.hpp` | `col_sd` | `internal_functions.cpp` | none public | none | bound-private | no | yes | Phase 3 | Safe numeric helper bound for backend use with explicit dimensions. | | `internal_functions.hpp` | `is_discrete` | `internal_functions.cpp` | internal checks | multiple | bound-private | no | yes | Phase 3 | Safe numeric helper bound for backend use. | @@ -70,51 +70,51 @@ Status values used below: | `internal_functions.hpp` | `ForecastVectors` | `internal_functions.cpp` | none public | none | bound-private | no | yes | Phase 3 | Dict result for private backend support. | | `internal_functions.hpp` | `generate_vectors` | `internal_functions.cpp` | none public | none | bound-private | no | yes | Phase 3 | Safe explicit vector/list conversion. | | `internal_functions.hpp` | `generate_lin_vectors` | `internal_functions.cpp` | none public | none | bound-private | no | yes | Phase 3 | Safe explicit vector/list conversion. | -| `internal_functions.hpp` | `ARMAWeights` | `internal_functions.cpp` | ARMA internals | `pynns.arma` | cxx-exists-unbound | no | yes | Phase 4 | Left unbound; structured ARMA weighting semantics need parity tests. | -| `internal_functions.hpp` | `arma_seas_weighting` | `internal_functions.cpp` | ARMA internals | `pynns.arma` | cxx-exists-unbound | no | yes | Phase 4 | Left unbound because period/covariance frame semantics need separate validation. | -| `internal_functions.hpp` | `meboot_part` | `internal_functions.cpp` | `nns_meboot` internals | `pynns.meboot` | cxx-exists-unbound | no | yes | Phase 4 | Left unbound because it has random seed and boundary semantics requiring dedicated parity tests. | -| `internal_functions.hpp` | `meboot_expand_sd` | `internal_functions.cpp` | `nns_meboot` internals | `pynns.meboot` | cxx-exists-unbound | no | yes | Phase 4 | Left unbound because it mutates column-major ensemble buffers in place. | -| `internal_functions.hpp` | `force_clt` | `internal_functions.cpp` | `nns_meboot` internals | `pynns.meboot` | cxx-exists-unbound | no | yes | Phase 4 | Left unbound because it mutates buffers and affects stochastic bootstrap distributions. | +| `internal_functions.hpp` | `ARMAWeights` | `internal_functions.cpp` | ARMA internals | `nns.arma` | cxx-exists-unbound | no | yes | Phase 4 | Left unbound; structured ARMA weighting semantics need parity tests. | +| `internal_functions.hpp` | `arma_seas_weighting` | `internal_functions.cpp` | ARMA internals | `nns.arma` | cxx-exists-unbound | no | yes | Phase 4 | Left unbound because period/covariance frame semantics need separate validation. | +| `internal_functions.hpp` | `meboot_part` | `internal_functions.cpp` | `nns_meboot` internals | `nns.meboot` | cxx-exists-unbound | no | yes | Phase 4 | Left unbound because it has random seed and boundary semantics requiring dedicated parity tests. | +| `internal_functions.hpp` | `meboot_expand_sd` | `internal_functions.cpp` | `nns_meboot` internals | `nns.meboot` | cxx-exists-unbound | no | yes | Phase 4 | Left unbound because it mutates column-major ensemble buffers in place. | +| `internal_functions.hpp` | `force_clt` | `internal_functions.cpp` | `nns_meboot` internals | `nns.meboot` | cxx-exists-unbound | no | yes | Phase 4 | Left unbound because it mutates buffers and affects stochastic bootstrap distributions. | | `internal_functions.hpp` | `SampleResult` | `internal_functions.cpp` | sampling internals | none | cxx-exists-unbound | no | yes | Phase 4 | Structured output; no current public API route. | | `internal_functions.hpp` | `up_sample` | `internal_functions.cpp` | none public | none | cxx-exists-unbound | no | yes | Phase 4 | Left unbound because class balancing and seed semantics need a public contract first. | | `internal_functions.hpp` | `down_sample` | `internal_functions.cpp` | none public | none | cxx-exists-unbound | no | yes | Phase 4 | Left unbound because class balancing and seed semantics need a public contract first. | -| `dependence.hpp` | `DepResult` | `dependence.cpp` | `nns_dep`/`nns_cor` result pieces | `pynns.dependence` | cxx-exists-unbound | yes | no | Phase 5 | Requires pre-hashed partition labels for `dep_pair`; leave unbound pending wrapper design. | -| `dependence.hpp` | `DepMatrixResult` | `dependence.cpp` | matrix results | `pynns.dependence` | cxx-exists-unbound | yes | no | Phase 5 | Structured matrix result; leave until parity for matrix orientation is added. | -| `dependence.hpp` | `dep_pair` | `dependence.cpp` | `nns_dep`, `nns_cor` | `pynns.dependence` | cxx-exists-unbound | yes | no | Phase 5 | Needs partition hash inputs not exposed by current Python public API. | -| `dependence.hpp` | `dep_matrix` | `dependence.cpp` | dependence matrix APIs | `pynns.dependence` | cxx-exists-unbound | yes | no | Phase 5 | Safe candidate later; not bound in this PR to avoid output shape changes. | -| `distance.hpp` | `distance` | `distance.cpp` | `nns_distance` | `pynns.distance` | cxx-exists-unbound | yes | no | Phase 5 | Left unbound; current Python code includes rescaling/weighting behavior requiring parity comparison. | -| `distance.hpp` | `distance_path` | `distance.cpp` | distance path behavior | `pynns.distance` | cxx-exists-unbound | yes | no | Phase 5 | Left unbound pending k/path output contract tests. | -| `distance.hpp` | `distance_bulk` | `distance.cpp` | `nns_distance_bulk` | `pynns.distance` | cxx-exists-unbound | yes | no | Phase 5 | Left unbound pending row/column-major parity tests. | +| `dependence.hpp` | `DepResult` | `dependence.cpp` | `nns_dep`/`nns_cor` result pieces | `nns.dependence` | cxx-exists-unbound | yes | no | Phase 5 | Requires pre-hashed partition labels for `dep_pair`; leave unbound pending wrapper design. | +| `dependence.hpp` | `DepMatrixResult` | `dependence.cpp` | matrix results | `nns.dependence` | cxx-exists-unbound | yes | no | Phase 5 | Structured matrix result; leave until parity for matrix orientation is added. | +| `dependence.hpp` | `dep_pair` | `dependence.cpp` | `nns_dep`, `nns_cor` | `nns.dependence` | cxx-exists-unbound | yes | no | Phase 5 | Needs partition hash inputs not exposed by current Python public API. | +| `dependence.hpp` | `dep_matrix` | `dependence.cpp` | dependence matrix APIs | `nns.dependence` | cxx-exists-unbound | yes | no | Phase 5 | Safe candidate later; not bound in this PR to avoid output shape changes. | +| `distance.hpp` | `distance` | `distance.cpp` | `nns_distance` | `nns.distance` | cxx-exists-unbound | yes | no | Phase 5 | Left unbound; current Python code includes rescaling/weighting behavior requiring parity comparison. | +| `distance.hpp` | `distance_path` | `distance.cpp` | distance path behavior | `nns.distance` | cxx-exists-unbound | yes | no | Phase 5 | Left unbound pending k/path output contract tests. | +| `distance.hpp` | `distance_bulk` | `distance.cpp` | `nns_distance_bulk` | `nns.distance` | cxx-exists-unbound | yes | no | Phase 5 | Left unbound pending row/column-major parity tests. | | `distance.hpp` | `distance_path_parallel` | `distance.cpp` | none direct | none | cxx-exists-unbound | no | yes | Phase 5 | Parallel helper; no public wrapper. | | `distance.hpp` | `distance_path_single_parallel` | `distance.cpp` | none direct | none | cxx-exists-unbound | no | yes | Phase 5 | Parallel helper; no public wrapper. | -| `partition.hpp` | `PartitionRow` | `partition.cpp` | partition result rows | `pynns.part` | cxx-exists-unbound | yes | no | Phase 5 | Structured object mapping deferred. | -| `partition.hpp` | `RegressionPoint` | `partition.cpp` | regression points | `pynns.part` | cxx-exists-unbound | yes | no | Phase 5 | Structured object mapping deferred. | -| `partition.hpp` | `SegmentH` | `partition.cpp` | `segments_h` | `pynns.part` | cxx-exists-unbound | yes | no | Phase 5 | Structured object mapping deferred. | -| `partition.hpp` | `SegmentV` | `partition.cpp` | `segments_v` | `pynns.part` | cxx-exists-unbound | yes | no | Phase 5 | Structured object mapping deferred. | -| `partition.hpp` | `PartitionResult` | `partition.cpp` | `nns_part` result dict | `pynns.part` | cxx-exists-unbound | yes | no | Phase 5 | Complex R-compatible payload; not changed in this PR. | -| `partition.hpp` | `partition` | `partition.cpp` | `nns_part` | `pynns.part` | cxx-exists-unbound | yes | no | Phase 5 | Safe candidate later, but output shape/labels must remain exact. | -| `seasonality.hpp` | `SeasonalityResult` | `seasonality.cpp` | `nns_seas` result pieces | `pynns.seasonality` | cxx-exists-unbound | yes | no | Phase 5 | Structured result left unbound pending parity tests. | -| `seasonality.hpp` | `seasonality` | `seasonality.cpp` | `nns_seas` | `pynns.seasonality` | cxx-exists-unbound | yes | no | Phase 5 | Left unbound because modulo and result-shape semantics need public parity tests. | -| `stochastic_dominance.hpp` | `fsd_uni` | `stochastic_dominance.cpp` | `fsd_uni` | `pynns.stochastic_dominance` | cxx-exists-unbound | yes | no | Phase 5 | Candidate for future; not required by current native routing tests. | -| `stochastic_dominance.hpp` | `ssd_uni` | `stochastic_dominance.cpp` | `ssd_uni` | `pynns.stochastic_dominance` | cxx-exists-unbound | yes | no | Phase 5 | Candidate for future. | -| `stochastic_dominance.hpp` | `tsd_uni` | `stochastic_dominance.cpp` | `tsd_uni` | `pynns.stochastic_dominance` | cxx-exists-unbound | yes | no | Phase 5 | Candidate for future. | -| `stochastic_dominance.hpp` | `fsd` | `stochastic_dominance.cpp` | `fsd` | `pynns.stochastic_dominance` | cxx-exists-unbound | yes | no | Phase 5 | Matrix orientation and index base must be validated before routing. | -| `stochastic_dominance.hpp` | `ssd` | `stochastic_dominance.cpp` | `ssd` | `pynns.stochastic_dominance` | cxx-exists-unbound | yes | no | Phase 5 | Matrix orientation and index base must be validated before routing. | -| `stochastic_dominance.hpp` | `tsd` | `stochastic_dominance.cpp` | `tsd` | `pynns.stochastic_dominance` | cxx-exists-unbound | yes | no | Phase 5 | Matrix orientation and index base must be validated before routing. | -| `stochastic_dominance.hpp` | `StochSupResult` | `stochastic_dominance.cpp` | `nns_ss` result dict | `pynns.stochastic_superiority` | bound-private | yes | no | Existing | Already bound before this PR. | -| `stochastic_dominance.hpp` | `stochastic_superiority` | `stochastic_dominance.cpp` | `nns_ss` | `pynns.stochastic_superiority` | bound-private | yes | no | Existing | Already bound before this PR. | +| `partition.hpp` | `PartitionRow` | `partition.cpp` | partition result rows | `nns.part` | cxx-exists-unbound | yes | no | Phase 5 | Structured object mapping deferred. | +| `partition.hpp` | `RegressionPoint` | `partition.cpp` | regression points | `nns.part` | cxx-exists-unbound | yes | no | Phase 5 | Structured object mapping deferred. | +| `partition.hpp` | `SegmentH` | `partition.cpp` | `segments_h` | `nns.part` | cxx-exists-unbound | yes | no | Phase 5 | Structured object mapping deferred. | +| `partition.hpp` | `SegmentV` | `partition.cpp` | `segments_v` | `nns.part` | cxx-exists-unbound | yes | no | Phase 5 | Structured object mapping deferred. | +| `partition.hpp` | `PartitionResult` | `partition.cpp` | `nns_part` result dict | `nns.part` | cxx-exists-unbound | yes | no | Phase 5 | Complex R-compatible payload; not changed in this PR. | +| `partition.hpp` | `partition` | `partition.cpp` | `nns_part` | `nns.part` | cxx-exists-unbound | yes | no | Phase 5 | Safe candidate later, but output shape/labels must remain exact. | +| `seasonality.hpp` | `SeasonalityResult` | `seasonality.cpp` | `nns_seas` result pieces | `nns.seasonality` | cxx-exists-unbound | yes | no | Phase 5 | Structured result left unbound pending parity tests. | +| `seasonality.hpp` | `seasonality` | `seasonality.cpp` | `nns_seas` | `nns.seasonality` | cxx-exists-unbound | yes | no | Phase 5 | Left unbound because modulo and result-shape semantics need public parity tests. | +| `stochastic_dominance.hpp` | `fsd_uni` | `stochastic_dominance.cpp` | `fsd_uni` | `nns.stochastic_dominance` | cxx-exists-unbound | yes | no | Phase 5 | Candidate for future; not required by current native routing tests. | +| `stochastic_dominance.hpp` | `ssd_uni` | `stochastic_dominance.cpp` | `ssd_uni` | `nns.stochastic_dominance` | cxx-exists-unbound | yes | no | Phase 5 | Candidate for future. | +| `stochastic_dominance.hpp` | `tsd_uni` | `stochastic_dominance.cpp` | `tsd_uni` | `nns.stochastic_dominance` | cxx-exists-unbound | yes | no | Phase 5 | Candidate for future. | +| `stochastic_dominance.hpp` | `fsd` | `stochastic_dominance.cpp` | `fsd` | `nns.stochastic_dominance` | cxx-exists-unbound | yes | no | Phase 5 | Matrix orientation and index base must be validated before routing. | +| `stochastic_dominance.hpp` | `ssd` | `stochastic_dominance.cpp` | `ssd` | `nns.stochastic_dominance` | cxx-exists-unbound | yes | no | Phase 5 | Matrix orientation and index base must be validated before routing. | +| `stochastic_dominance.hpp` | `tsd` | `stochastic_dominance.cpp` | `tsd` | `nns.stochastic_dominance` | cxx-exists-unbound | yes | no | Phase 5 | Matrix orientation and index base must be validated before routing. | +| `stochastic_dominance.hpp` | `StochSupResult` | `stochastic_dominance.cpp` | `nns_ss` result dict | `nns.stochastic_superiority` | bound-private | yes | no | Existing | Already bound before this PR. | +| `stochastic_dominance.hpp` | `stochastic_superiority` | `stochastic_dominance.cpp` | `nns_ss` | `nns.stochastic_superiority` | bound-private | yes | no | Existing | Already bound before this PR. | ## Python APIs routed through native in this PR -- `pynns.core.lpm` -- `pynns.core.upm` -- `pynns.core.lpm_ratio` -- `pynns.core.upm_ratio` -- `pynns.co_moments.co_lpm` -- `pynns.co_moments.co_upm` -- `pynns.co_moments.d_lpm` -- `pynns.co_moments.d_upm` -- `pynns.pm_matrix.pm_matrix` +- `nns.core.lpm` +- `nns.core.upm` +- `nns.core.lpm_ratio` +- `nns.core.upm_ratio` +- `nns.co_moments.co_lpm` +- `nns.co_moments.co_upm` +- `nns.co_moments.d_lpm` +- `nns.co_moments.d_upm` +- `nns.pm_matrix.pm_matrix` ## Functions newly bound in `_nnscore` @@ -147,7 +147,7 @@ Status values used below: - Regression is not treated as a direct C++ binding unless a C++ equivalent exists. The `fast_lm` and `fast_lm_mult` helpers are private backend utilities, not replacements for the Python NNS regression API. - `internal_functions.cpp` is treated mostly as private backend support. Its bindings are not public top-level Python exports. -- Public APIs call `from pynns._native import nnscore`; if `nnscore()` returns a module they use native C++, and if it returns `None` they fall back to the existing Python implementation. +- Public APIs call `from nns._native import nnscore`; if `nnscore()` returns a module they use native C++, and if it returns `None` they fall back to the existing Python implementation. - Windows local MinGW builds may fail to load `_nnscore`; official Windows wheels should be built with MSVC. ## Non-source-support headers in `extern/NNS-core/include/nns` diff --git a/docs/original_tests_adoption.md b/docs/original_tests_adoption.md index fa75c782..ad81155f 100644 --- a/docs/original_tests_adoption.md +++ b/docs/original_tests_adoption.md @@ -8,13 +8,13 @@ |---|---|---|---|---|---|---|---| | `original_tests/testthat.R` | R `testthat` package harness (`library(testthat)`, `library(NNS)`, `test_check("NNS")`) | No runtime pytest equivalent; repository pytest invocation is the harness | n/a | none | n/a | no-python-equivalent | Harness file is inventoried but not converted because Python uses pytest directly. | | `original_tests/testthat/Rplots.pdf` | Plot artifact generated by R tests | No Python API behavior | n/a | none | n/a | no-python-equivalent | Binary PDF artifact is inventoried and intentionally not used or compared by CI. See `docs/plot_parity_policy.md`. | -| `original_tests/testthat/test_ANOVA.R` | `NNS::NNS.ANOVA(cbind(x,y,z))` certainty and `pairwise=TRUE` matrix | `pynns.nns_anova` | `src/pynns/anova.py` | `tests/fixtures/original_tests_expected.json` stores R certainty and pairwise matrix | `tests/parity/test_original_anova.py` | adopted | Uses the original `x`, `y`, and `z` vectors parsed from the R file; tolerance follows the R test (`1e-4`). | -| `original_tests/testthat/test_Copula.R` | `NNS.copula` for bivariate continuous/discrete and 3-column continuous/discrete | `pynns.nns_copula` (bivariate continuous and discrete; multivariate continuous and discrete) | `src/pynns/copula.py` | `tests/fixtures/original_tests_expected.json` stores all four R expected values | `tests/parity/test_original_dependence.py` | adopted | All four original cases are adopted: bivariate continuous `nns_copula(x, y)`, bivariate discrete `nns_copula(x, y, continuous=False)`, three-column continuous `nns_copula(Z)`, and three-column discrete `nns_copula(Z, continuous=False)`. `Z` is an `(observations, variables)` matrix (rows = observations, columns = variables), matching R's `data.frame(x, y, z)`. Each matches its R fixture to `1e-5`. | -| `original_tests/testthat/test_FSD_SSD_TSD.R` | `NNS.FSD`, `NNS.SSD`, and `NNS.TSD` dominance labels for original vectors and squared-vector dominance cases | `pynns.fsd`, `pynns.ssd`, `pynns.tsd` | `src/pynns/stochastic_dominance.py` | `tests/fixtures/original_tests_expected.json` stores R labels | `tests/parity/test_original_stochastic.py` | adopted | Plot flags in the R source are intentionally not represented because Python parity tests compare return values and CI must not create plot devices. Python bidirectional `fsd` currently implements the discrete path. | -| `original_tests/testthat/test_Partial_Moments.R` | `LPM`, `UPM`, `Co.UPM`, `Co.LPM`, `D.LPM`, `D.UPM`, `LPM.ratio`, `UPM.ratio`, `PM.matrix`, normalized covariance identity, and survival `NNS.CDF` | `pynns.lpm`, `pynns.upm`, `pynns.co_upm`, `pynns.co_lpm`, `pynns.d_lpm`, `pynns.d_upm`, `pynns.lpm_ratio`, `pynns.upm_ratio`, `pynns.pm_matrix`, `pynns.nns_cdf` | `src/pynns/core.py`, `src/pynns/co_moments.py`, `src/pynns/pm_matrix.py`, `src/pynns/cdf.py` | `tests/fixtures/original_tests_expected.json` stores R scalar outputs, covariance matrices, and survival CDF table | `tests/parity/test_original_partial_moments.py` | partially-adopted | Scalar partial moments, PM matrix covariance outputs, and survival CDF are adopted. The normalized covariance identity is duplicate-existing-coverage-style behavior and is not reasserted in the original parity file. R data-frame dimname behavior is now exposed as an optional `pm_matrix(..., names=[...])` parameter that echoes column labels under a `"names"` key without altering the numeric NumPy arrays; `test_pm_matrix_optional_names_match_r_dataframe_without_changing_numbers` proves names match R while numeric parity is unaffected. | -| `original_tests/testthat/test_Partition_Map.R` | `NNS.part(x,y, Voronoi=FALSE, min.obs.stop=TRUE)` order, full row-wise partition table, and regression points | `pynns.nns_part` | `src/pynns/part.py` | `tests/fixtures/original_tests_expected.json` stores R order and regression points; quadrant and prior quadrant vectors are parsed from the original R file | `tests/parity/test_original_partition.py` | adopted | Preserves row order, quadrant labels, prior quadrant labels, and regression point order. | -| `original_tests/testthat/test_SD_efficient_Set.R` | `NNS.SD.efficient.set` for degrees 1-3 and FSD discrete/continuous type | `pynns.sd_efficient_set` | `src/pynns/stochastic_dominance.py` | `tests/fixtures/original_tests_expected.json` stores the R efficient-set name order | `tests/parity/test_original_stochastic.py` | adopted | Converts Python column indices back to the original R names (`x`, `y`, `z`, `xx`, `yy`, `zz`) to preserve name and order parity. | -| `original_tests/testthat/test_Uni_SD_Routines.R` | `NNS.FSD.uni`, `NNS.SSD.uni`, and `NNS.TSD.uni` unidirectional dominance flags | `pynns.fsd_uni`, `pynns.ssd_uni`, `pynns.tsd_uni` | `src/pynns/stochastic_dominance.py` | `tests/fixtures/original_tests_expected.json` stores R integer outputs | `tests/parity/test_original_stochastic.py` | adopted | Uses original vectors and squared-vector cases. FSD discrete and continuous paths from R are both represented. | +| `original_tests/testthat/test_ANOVA.R` | `NNS::NNS.ANOVA(cbind(x,y,z))` certainty and `pairwise=TRUE` matrix | `nns.nns_anova` | `src/nns/anova.py` | `tests/fixtures/original_tests_expected.json` stores R certainty and pairwise matrix | `tests/parity/test_original_anova.py` | adopted | Uses the original `x`, `y`, and `z` vectors parsed from the R file; tolerance follows the R test (`1e-4`). | +| `original_tests/testthat/test_Copula.R` | `NNS.copula` for bivariate continuous/discrete and 3-column continuous/discrete | `nns.nns_copula` (bivariate continuous and discrete; multivariate continuous and discrete) | `src/nns/copula.py` | `tests/fixtures/original_tests_expected.json` stores all four R expected values | `tests/parity/test_original_dependence.py` | adopted | All four original cases are adopted: bivariate continuous `nns_copula(x, y)`, bivariate discrete `nns_copula(x, y, continuous=False)`, three-column continuous `nns_copula(Z)`, and three-column discrete `nns_copula(Z, continuous=False)`. `Z` is an `(observations, variables)` matrix (rows = observations, columns = variables), matching R's `data.frame(x, y, z)`. Each matches its R fixture to `1e-5`. | +| `original_tests/testthat/test_FSD_SSD_TSD.R` | `NNS.FSD`, `NNS.SSD`, and `NNS.TSD` dominance labels for original vectors and squared-vector dominance cases | `nns.fsd`, `nns.ssd`, `nns.tsd` | `src/nns/stochastic_dominance.py` | `tests/fixtures/original_tests_expected.json` stores R labels | `tests/parity/test_original_stochastic.py` | adopted | Plot flags in the R source are intentionally not represented because Python parity tests compare return values and CI must not create plot devices. Python bidirectional `fsd` currently implements the discrete path. | +| `original_tests/testthat/test_Partial_Moments.R` | `LPM`, `UPM`, `Co.UPM`, `Co.LPM`, `D.LPM`, `D.UPM`, `LPM.ratio`, `UPM.ratio`, `PM.matrix`, normalized covariance identity, and survival `NNS.CDF` | `nns.lpm`, `nns.upm`, `nns.co_upm`, `nns.co_lpm`, `nns.d_lpm`, `nns.d_upm`, `nns.lpm_ratio`, `nns.upm_ratio`, `nns.pm_matrix`, `nns.nns_cdf` | `src/nns/core.py`, `src/nns/co_moments.py`, `src/nns/pm_matrix.py`, `src/nns/cdf.py` | `tests/fixtures/original_tests_expected.json` stores R scalar outputs, covariance matrices, and survival CDF table | `tests/parity/test_original_partial_moments.py` | partially-adopted | Scalar partial moments, PM matrix covariance outputs, and survival CDF are adopted. The normalized covariance identity is duplicate-existing-coverage-style behavior and is not reasserted in the original parity file. R data-frame dimname behavior is now exposed as an optional `pm_matrix(..., names=[...])` parameter that echoes column labels under a `"names"` key without altering the numeric NumPy arrays; `test_pm_matrix_optional_names_match_r_dataframe_without_changing_numbers` proves names match R while numeric parity is unaffected. | +| `original_tests/testthat/test_Partition_Map.R` | `NNS.part(x,y, Voronoi=FALSE, min.obs.stop=TRUE)` order, full row-wise partition table, and regression points | `nns.nns_part` | `src/nns/part.py` | `tests/fixtures/original_tests_expected.json` stores R order and regression points; quadrant and prior quadrant vectors are parsed from the original R file | `tests/parity/test_original_partition.py` | adopted | Preserves row order, quadrant labels, prior quadrant labels, and regression point order. | +| `original_tests/testthat/test_SD_efficient_Set.R` | `NNS.SD.efficient.set` for degrees 1-3 and FSD discrete/continuous type | `nns.sd_efficient_set` | `src/nns/stochastic_dominance.py` | `tests/fixtures/original_tests_expected.json` stores the R efficient-set name order | `tests/parity/test_original_stochastic.py` | adopted | Converts Python column indices back to the original R names (`x`, `y`, `z`, `xx`, `yy`, `zz`) to preserve name and order parity. | +| `original_tests/testthat/test_Uni_SD_Routines.R` | `NNS.FSD.uni`, `NNS.SSD.uni`, and `NNS.TSD.uni` unidirectional dominance flags | `nns.fsd_uni`, `nns.ssd_uni`, `nns.tsd_uni` | `src/nns/stochastic_dominance.py` | `tests/fixtures/original_tests_expected.json` stores R integer outputs | `tests/parity/test_original_stochastic.py` | adopted | Uses original vectors and squared-vector cases. FSD discrete and continuous paths from R are both represented. | ## Fixture policy @@ -42,6 +42,6 @@ - `PM.matrix` matrices remain NumPy-first arrays without R-style dimnames; labels are available only via the optional `names` echo described above. -## Out of scope +## Scope notes -The NNS-python migration remains out of scope. The `pynns` package name is unchanged. +The official package identity is now the `NNS` distribution with `import nns` and native extension `nns._nnscore`. Full R package parity is not claimed; parity remains bounded by the committed fixtures and cache entries, and plot artifacts remain intentionally out of scope. diff --git a/docs/parity_plan.md b/docs/parity_plan.md index eef32544..da863f42 100644 --- a/docs/parity_plan.md +++ b/docs/parity_plan.md @@ -1,6 +1,6 @@ # Parity Plan -This branch completes the pre-migration parity suite for `pyNNS-core-backed` while keeping the `NNS-python` migration out of scope. +This branch completes the pre-migration parity suite for `NNS-python-core-backed` while keeping the `NNS-python` migration out of scope. ## Closed gap workstream (branch `close-all-parity-gaps`) @@ -25,14 +25,14 @@ formally resolved: - Preserve public-behavior parity tests against R NNS 12.1 through `tests/parity/`. - Keep R calls isolated in the test harness and cache tooling. -- Allow CI to run parity checks without `Rscript` by using committed cache fixtures with `PYNNS_R_CACHE_ONLY=1`. +- Allow CI to run parity checks without `Rscript` by using committed cache fixtures with `NNS_R_CACHE_ONLY=1`. - Preserve native-vs-Python fallback coverage for partial moments and related helpers. - Preserve the merged PR #6 fix that blocks non-finite partial-moment inputs from native dispatch. ## Cache workflow - `tests/_r_cache.json` is the committed R-compatible cache used by CI. -- `PYNNS_R_CACHE_ONLY=1` forces cache-only parity and must be used in CI. +- `NNS_R_CACHE_ONLY=1` forces cache-only parity and must be used in CI. - To refresh cache entries on a workstation with R and NNS installed, run: ```bash @@ -49,5 +49,5 @@ python scripts/regenerate_r_cache.py -- tests/parity/test_core.py - Do not require `Rscript` in CI. - Do not reintroduce stale native expectations for partial moments. -- Do not import `pynns.pm_matrix` through the package-level public function when module access is required; use `importlib.import_module("pynns.pm_matrix")`. +- Do not import `nns.pm_matrix` through the package-level public function when module access is required; use `importlib.import_module("nns.pm_matrix")`. - Do not route `NaN` or infinite partial-moment inputs through native `lpm`, `upm`, `lpm_ratio`, or `upm_ratio` dispatch. diff --git a/docs/parity_results.md b/docs/parity_results.md index 4acf590d..eea50eca 100644 --- a/docs/parity_results.md +++ b/docs/parity_results.md @@ -2,9 +2,9 @@ ## Executive summary -The current prototype has strong fixture-backed parity coverage for the core partial-moment machinery and several original R test areas, plus broad cache-backed parity coverage. Remaining gaps are documented and should be closed before final NNS-python migration. +The official NNS Python package has strong fixture-backed parity coverage for the core partial-moment machinery and several original R test areas, plus broad cache-backed parity coverage. The distribution package is `NNS`, the import package is `nns`, and the native extension is `nns._nnscore`. -This report consolidates the merged-state parity evidence from `docs/parity_status.md`, `docs/original_tests_adoption.md`, `tests/parity/`, `tests/fixtures/original_tests_expected.json`, `tests/_r_cache.json`, and `tests/invariants/test_native_original_src_coverage.py`. It does **not** claim full package parity. It also does not introduce native routing, rename `pynns`, touch NNS-python, or move the project toward publication. +This report consolidates the merged-state parity evidence from `docs/parity_status.md`, `docs/original_tests_adoption.md`, `tests/parity/`, `tests/fixtures/original_tests_expected.json`, `tests/_r_cache.json`, and `tests/invariants/test_native_original_src_coverage.py`. It does **not** claim full R package parity. Parity is bounded by the committed fixtures and cache entries, and plot artifacts are intentionally out of scope. CI parity is cache-backed and does not require `Rscript`; `Rscript` is only needed for local cache regeneration. This evidence does not imply PyPI publication. ## Test commands @@ -12,8 +12,8 @@ The expected verification commands for this state are: ```bash python -m pytest -q tests/invariants -PYNNS_R_CACHE_ONLY=1 python -m pytest -q tests/parity -PYNNS_R_CACHE_ONLY=1 python -m pytest -q tests/parity/test_original_* +NNS_R_CACHE_ONLY=1 python -m pytest -q tests/parity +NNS_R_CACHE_ONLY=1 python -m pytest -q tests/parity/test_original_* ruff check . mypy python -m build @@ -26,11 +26,11 @@ python -m build Verification on the `close-all-parity-gaps` branch on 2026-06-12, using the repository virtual environment, observed: - `python -m pytest -q tests/invariants` produced `314 passed`. -- `PYNNS_R_CACHE_ONLY=1 python -m pytest -q tests/parity` produced `1778 passed, 11 skipped` (no failures). The previously reported `test_nns_boost_ivs_test_none_matches_r` failure no longer occurs (see "Gap closure summary" below). -- `PYNNS_R_CACHE_ONLY=1 python -m pytest -q tests/parity/test_original_*` produced `12 passed` (up from `9`; three new copula parity tests added). +- `NNS_R_CACHE_ONLY=1 python -m pytest -q tests/parity` produced `1778 passed, 11 skipped` (no failures). The previously reported `test_nns_boost_ivs_test_none_matches_r` failure no longer occurs (see "Gap closure summary" below). +- `NNS_R_CACHE_ONLY=1 python -m pytest -q tests/parity/test_original_*` produced `12 passed` (up from `9`; three new copula parity tests added). - `ruff check .` passed. - `mypy` passed. -- `python -m build` succeeded (built `nns_pm-0.2.0.tar.gz` and the `cp311` wheel, including the native `_nnscore` extension). CI also runs `python -m build` as a workflow step. +- `python -m build` succeeded (built the `NNS` source distribution and wheel, including the native `nns._nnscore` extension). CI also runs `python -m build` as a workflow step. The 11 skips are all intentional live-R-only practical examples in `tests/parity/test_practical_examples.py`; they are not cache-backed parity coverage gaps. @@ -41,7 +41,7 @@ The earlier consolidation-branch snapshot recorded `1 failed, 1773 passed, 11 sk Historical known results from PR #7: - `python -m pytest -q tests/invariants` produced `314 passed`. -- `PYNNS_R_CACHE_ONLY=1 python -m pytest -q tests/parity` produced `1765 passed, 11 skipped`. +- `NNS_R_CACHE_ONLY=1 python -m pytest -q tests/parity` produced `1765 passed, 11 skipped`. - `ruff check .` passed. - `mypy` passed. - `python -m build` was blocked locally by a missing `build` module / network limits. @@ -49,7 +49,7 @@ Historical known results from PR #7: Historical known results from PR #8: - `python -m pytest -q tests/invariants` produced `314 passed`. -- `PYNNS_R_CACHE_ONLY=1 python -m pytest -q tests/parity/test_original_*` produced `9 passed`. +- `NNS_R_CACHE_ONLY=1 python -m pytest -q tests/parity/test_original_*` produced `9 passed`. - Original test adoption added fixture-backed parity tests for ANOVA, dependence/copula partial coverage, partial moments, partition, stochastic dominance, SD efficient set, and univariate SD routines. ## Native-vs-fallback coverage @@ -68,11 +68,11 @@ This is not a full native-backend claim. Some C++ functions are intentionally un ## R-cache parity coverage -The committed cache in `tests/_r_cache.json` is the offline parity source used by `PYNNS_R_CACHE_ONLY=1`. The cache currently records schema version `1`, R NNS version `12.1`, and 2,406 keyed R result entries. +The committed cache in `tests/_r_cache.json` is the offline parity source used by `NNS_R_CACHE_ONLY=1`. The cache currently records schema version `1`, R NNS version `12.1`, and 2,406 keyed R result entries. Cache-backed parity covers broad public API behavior across `tests/parity/`, including ANOVA, ARMA, boosting, categorical wrappers, causation, CDF, classical helpers, co-moments, copula bivariate coverage, core partial moments, dependence, differences, distance, Monte Carlo helpers, meboot, multivariate regression, normalization, partitioning, PM matrix, practical examples, public wrappers, regression, regression helpers, SD clustering, seasonality, stack, stochastic dominance, stochastic superiority, and variance helpers. -When `PYNNS_R_CACHE_ONLY=1` is set, missing cache entries remain blocked unless the cache is regenerated in an environment with `Rscript` and R NNS installed. Any such cache miss is a parity-data gap, not evidence that Python and R match. +When `NNS_R_CACHE_ONLY=1` is set, missing cache entries remain blocked unless the cache is regenerated in an environment with `Rscript` and R NNS installed. Any such cache miss is a parity-data gap, not evidence that Python and R match. ## Original R tests adoption coverage @@ -95,11 +95,11 @@ The original-test fixture file contains expected values for seven original R tes The following areas are fully adopted relative to the original R tests currently represented in pytest: -- `NNS.ANOVA` / `pynns.nns_anova` for original certainty and pairwise matrix behavior. -- `NNS.part` / `pynns.nns_part` for the original partition map case. -- `NNS.FSD`, `NNS.SSD`, and `NNS.TSD` / `pynns.fsd`, `pynns.ssd`, and `pynns.tsd` for original dominance-label cases. -- `NNS.SD.efficient.set` / `pynns.sd_efficient_set` for original efficient-set name and order cases. -- `NNS.FSD.uni`, `NNS.SSD.uni`, and `NNS.TSD.uni` / `pynns.fsd_uni`, `pynns.ssd_uni`, and `pynns.tsd_uni` for original unidirectional dominance cases. +- `NNS.ANOVA` / `nns.nns_anova` for original certainty and pairwise matrix behavior. +- `NNS.part` / `nns.nns_part` for the original partition map case. +- `NNS.FSD`, `NNS.SSD`, and `NNS.TSD` / `nns.fsd`, `nns.ssd`, and `nns.tsd` for original dominance-label cases. +- `NNS.SD.efficient.set` / `nns.sd_efficient_set` for original efficient-set name and order cases. +- `NNS.FSD.uni`, `NNS.SSD.uni`, and `NNS.TSD.uni` / `nns.fsd_uni`, `nns.ssd_uni`, and `nns.tsd_uni` for original unidirectional dominance cases. These are full adoptions of the current original-test fixtures, not claims that every parameter combination or every R package behavior is complete. @@ -112,8 +112,8 @@ The following areas are partially adopted and should remain clearly documented: ## Newly adopted on this branch -- `NNS.copula` / `pynns.nns_copula`: bivariate continuous, bivariate discrete (`continuous=False`), and three-column continuous/discrete (matrix input) are all adopted against the R fixtures. -- `PM.matrix` / `pynns.pm_matrix`: covariance output parity is adopted, and R data-frame naming is exposed via an optional `names` echo that does not change the numeric NumPy arrays (parity-unaffected, proven by test). +- `NNS.copula` / `nns.nns_copula`: bivariate continuous, bivariate discrete (`continuous=False`), and three-column continuous/discrete (matrix input) are all adopted against the R fixtures. +- `PM.matrix` / `nns.pm_matrix`: covariance output parity is adopted, and R data-frame naming is exposed via an optional `names` echo that does not change the numeric NumPy arrays (parity-unaffected, proven by test). ## Intentional divergences and remaining offline limitations @@ -121,7 +121,7 @@ These are documented intentional divergences or environment limitations, **not** - R plot artifacts, including `Rplots.pdf`, are intentionally not adopted because CI parity compares returned values and never creates or compares graphics-device artifacts. Policy: `docs/plot_parity_policy.md`. - `PM.matrix` returns NumPy-first arrays; R data-frame dimnames are exposed only via the optional `names` echo. This is a documented API difference, not a numeric-parity gap. -- Any parity test requiring a missing `tests/_r_cache.json` entry would block under `PYNNS_R_CACHE_ONLY=1` when `Rscript` is unavailable; the committed cache currently covers the full offline parity suite with no such misses. +- Any parity test requiring a missing `tests/_r_cache.json` entry would block under `NNS_R_CACHE_ONLY=1` when `Rscript` is unavailable; the committed cache currently covers the full offline parity suite with no such misses. - The only suite skips are intentional live-R-only practical examples (see above). ## Known gaps @@ -151,7 +151,7 @@ The discrete and multivariate copula gaps and the `PM.matrix` naming gap recorde | FSD/SSD/TSD labels | `fsd`, `ssd`, `tsd` | `NNS.FSD`, `NNS.SSD`, `NNS.TSD`, `test_FSD_SSD_TSD.R` | No | Yes | Yes | No | fixture-complete | Original dominance labels are adopted for represented cases. | | Univariate SD routines | `fsd_uni`, `ssd_uni`, `tsd_uni` | `NNS.FSD.uni`, `NNS.SSD.uni`, `NNS.TSD.uni`, `test_Uni_SD_Routines.R` | No | Yes | Yes | No | fixture-complete | Original unidirectional cases are adopted. | | SD efficient set | `sd_efficient_set` | `NNS.SD.efficient.set`, `test_SD_efficient_Set.R` | No | Yes | Yes | No | fixture-complete | Python indices are mapped back to original R names for parity. | -| Broad cached parity suite | Many public `pynns` APIs | Installed R NNS via test harness | Mixed | Yes | Mixed | Mixed | partial | `tests/parity/` is broad and cache-backed, but not full package parity. The full cache-only suite now passes with no failures (`1778 passed, 11 skipped`). | +| Broad cached parity suite | Many public `nns` APIs | Installed R NNS via test harness | Mixed | Yes | Mixed | Mixed | partial | `tests/parity/` is broad and cache-backed, but not full R package parity. The full cache-only suite now passes with no failures (`1778 passed, 11 skipped`). | | Native original-source smoke coverage | Optional `_nnscore` routes and helpers | Vendored NNS-core C++ | Yes, where bound | No | No | Yes | native-complete | Covers currently exported native symbols and public fallback behavior. | | R plot artifact | No Python API | `Rplots.pdf` and plot flags | No | No | No | No | intentional-divergence | CI intentionally compares returned values, never graphics-device artifacts. Policy in `docs/plot_parity_policy.md`. Not a migration blocker. | | R testthat harness | pytest invocation | `testthat.R` | No | No | No | No | no-python-equivalent | Python uses pytest rather than R testthat. | @@ -173,11 +173,11 @@ Positive signals: Release blockers or cautions: -- The parity claim is bounded by committed fixtures and cache entries; full package parity is not claimed. +- The parity claim is bounded by committed fixtures and cache entries; full R package parity is not claimed. - R plotting behavior and artifacts remain intentionally unported (documented policy). - Some native original-source functions remain private-only or unbound. -## What remains before NNS-python migration +## Ongoing maintenance notes The previously enumerated pre-migration gaps are now closed or formally resolved: @@ -190,19 +190,19 @@ Ongoing discipline (not blockers): 5. Expand original R test adoption where additional upstream tests or stable public examples are available. 6. Keep native routing limited to verified public behavior and avoid adding new routes without parity and fallback tests. -7. Maintain the `pynns` package name until a separate migration plan explicitly covers NNS-python naming, compatibility, packaging, and publication. +7. Keep PyPI publication and release tagging out of this migration PR. ## Gap closure summary (final status) -This section is the clean, current status summary for the `close-all-parity-gaps` branch. +This section is the clean, current status summary for the official NNS-python identity migration. -- **Full-suite parity failures:** none. `PYNNS_R_CACHE_ONLY=1 python -m pytest -q tests/parity` → `1778 passed, 11 skipped`. +- **Full-suite parity failures:** none. `NNS_R_CACHE_ONLY=1 python -m pytest -q tests/parity` → `1778 passed, 11 skipped`. - **Remaining skips:** intentional and documented only — 11 live-R-only practical examples in `tests/parity/test_practical_examples.py` that regenerate vignette-scale results from installed R NNS on demand. They are not cache-backed parity coverage gaps. - **`nns_boost` cache parity:** resolved. The previously reported `test_nns_boost_ivs_test_none_matches_r` failure was triaged as CV-split seed-sensitivity on an unseeded call. The boosted result is empirically seed-invariant and matches the committed R cache to ~3.5e-15. The parity test now pins a seed, and `test_nns_boost_ivs_test_none_is_seed_invariant` guards against regression. No tolerance was loosened. - **Copula discrete status:** implemented and adopted. `nns_copula(x, y, continuous=False)` matches R `NNS.copula(A, continuous=FALSE)` = 0.4472136 to `1e-5`. - **Copula multivariate status:** implemented and adopted. `nns_copula(Z)` and `nns_copula(Z, continuous=False)` match R `NNS.copula(Z, continuous=TRUE/FALSE)` = 0.2519783 / 0.2725541 to `1e-5`. Matrix orientation: rows are observations, columns are variables; any column count `>= 2` is supported; per-column targets default to column means and can be overridden via `target`. - **PM.matrix naming status:** resolved as an optional NumPy-first `names` echo. Numeric covariance parity is unchanged; a parity test proves names match R's data-frame dimname behavior while the numeric arrays are byte-for-byte identical. - **Plot policy status:** formalized in `docs/plot_parity_policy.md`. Graphics-device artifacts (including `original_tests/testthat/Rplots.pdf`) are inventoried but never compared in CI; parity compares returned values only. -- **Build status:** `python -m build` succeeds locally (sdist + `cp311` wheel with the native `_nnscore` extension) and runs as a CI workflow step. -- **Native routing:** no new native routing was added; the `pm_matrix` `names` echo is attached in Python after any native call and does not change native dispatch. -- **Migration verdict:** the NNS-python migration **remains out of scope** and is **blocked** until this PR is merged and green. No `pynns` → `nns` rename, no publication, and no NNS-python changes are part of this work. +- **Build status:** `python -m build` succeeds locally (sdist + `cp311` wheel with the native `nns._nnscore` extension) and runs as a CI workflow step. +- **Native routing:** the native module is installed as `nns._nnscore`; no NNS-core behavior changes are part of the identity migration. +- **Publication verdict:** no PyPI publication and no release tag are part of this PR. diff --git a/docs/parity_status.md b/docs/parity_status.md index 755c74b9..0b44445e 100644 --- a/docs/parity_status.md +++ b/docs/parity_status.md @@ -2,15 +2,15 @@ ## Current status -- The parity suite lives in `tests/parity/` and compares public PyNNS behavior to R NNS-compatible cached fixtures. -- CI-compatible parity runs use `PYNNS_R_CACHE_ONLY=1` and the committed `tests/_r_cache.json` cache. +- The parity suite lives in `tests/parity/` and compares public NNS Python behavior to R NNS-compatible cached fixtures. +- CI-compatible parity runs use `NNS_R_CACHE_ONLY=1` and the committed `tests/_r_cache.json` cache. - Native-vs-fallback coverage lives in `tests/invariants/test_native_original_src_coverage.py`. -- The PR #6 non-finite native-routing fix is preserved in `src/pynns/core.py` through `_native_safe(...)` checks in `lpm`, `upm`, `lpm_ratio`, and `upm_ratio`. +- The PR #6 non-finite native-routing fix is preserved in `src/nns/core.py` through `_native_safe(...)` checks in `lpm`, `upm`, `lpm_ratio`, and `upm_ratio`. ## Closed parity gaps (branch `close-all-parity-gaps`) - `nns_boost` depth=None parity: resolved (seed-sensitivity triage; seed pinned in the parity test; seed-invariance regression guard added). The committed cache matches Python to ~3.5e-15. -- `NNS.copula(..., continuous=FALSE)` discrete mode: implemented in `pynns.nns_copula` and adopted against the R fixture. +- `NNS.copula(..., continuous=FALSE)` discrete mode: implemented in `nns.nns_copula` and adopted against the R fixture. - `NNS.copula` multivariate / three-column mode: implemented (2-D `(observations, variables)` matrix input, any column count `>= 2`) and adopted for continuous and discrete. - `PM.matrix` R data-frame naming: optional `pm_matrix(..., names=[...])` echo added (NumPy-first; numeric arrays unchanged) with a parity test. - Plot/graphics policy: formalized in `docs/plot_parity_policy.md`; graphics-device artifacts are never compared in CI. @@ -20,7 +20,7 @@ - The only remaining parity skips are intentional live-R-only practical examples in `tests/parity/test_practical_examples.py`, which regenerate vignette-scale results from installed R NNS on demand rather than from the committed cache. They are not ordinary cache-backed parity coverage. - Live R regeneration is not required in CI because many runners do not have `Rscript` or R NNS installed. - Cache regeneration remains optional and developer-local via `scripts/regenerate_r_cache.py`. -- The NNS-python migration remains out of scope for this branch. +- The official package identity is `NNS` / `import nns` / `nns._nnscore`; PyPI publication remains out of scope for this branch. ## Regression coverage diff --git a/pyproject.toml b/pyproject.toml index 45ab04e5..125349fa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] -name = "nns-pm" -version = "0.2.0" +name = "NNS" +version = "1.0.0a0" description = "Python port of nonlinear nonparametric statistics from R NNS" readme = "README.md" requires-python = ">=3.11" @@ -23,7 +23,7 @@ keywords = [ "forecasting", "nns", ] -urls = { "Homepage" = "https://github.com/gitRasheed/pyNNS", "Repository" = "https://github.com/gitRasheed/pyNNS", "Issues" = "https://github.com/gitRasheed/pyNNS/issues", "Source" = "https://github.com/gitRasheed/pyNNS", "Project" = "https://github.com/gitRasheed/pyNNS" } +urls = { "Homepage" = "https://github.com/OVVO-Financial/NNS-python", "Repository" = "https://github.com/OVVO-Financial/NNS-python", "Issues" = "https://github.com/OVVO-Financial/NNS-python/issues", "Source" = "https://github.com/OVVO-Financial/NNS-python", "Project" = "https://github.com/OVVO-Financial/NNS-python" } dependencies = [ "numpy", "scipy", @@ -45,7 +45,7 @@ requires = ["scikit-build-core", "nanobind"] build-backend = "scikit_build_core.build" [tool.scikit-build] -wheel.packages = ["src/pynns"] +wheel.packages = ["src/nns"] sdist.include = [ "/CMakeLists.txt", "/LICENSE", @@ -53,10 +53,17 @@ sdist.include = [ "/docs/api_status.md", "/docs/benchmarks.md", "/docs/conventions.md", + "/docs/native_original_src_coverage.md", + "/docs/original_tests_adoption.md", "/docs/parity_plan.md", + "/docs/parity_results.md", "/docs/parity_status.md", + "/docs/plot_parity_policy.md", "/docs/examples", "/extern/NNS-core", + "/original_tests", + "/tests/_r_cache.json", + "/tests/fixtures/original_tests_expected.json", "/pyproject.toml", "/src", ] @@ -67,7 +74,7 @@ pythonpath = ["tests"] testpaths = ["tests"] markers = [ "benchmark: performance benchmarks excluded from the default test run", - "parity: tests comparing pynns behavior to the reference R NNS package", + "parity: tests comparing nns behavior to the reference R NNS package", "practical: end-to-end practical example parity checks against installed R NNS", "invariant: tests for mathematical or API invariants", "property: property-based tests", @@ -83,8 +90,8 @@ extend-exclude = ["*.ipynb"] select = ["E", "F", "I", "B", "UP", "N", "RUF", "TID"] [tool.ruff.lint.flake8-tidy-imports.banned-api] -"subprocess" = { msg = "Do not call subprocess from pynns implementation code. Keep R parity calls in tests/_r.py." } -"rpy2" = { msg = "Do not use rpy2 from pynns implementation code. Keep R parity calls outside the package." } +"subprocess" = { msg = "Do not call subprocess from nns implementation code. Keep R parity calls in tests/_r.py." } +"rpy2" = { msg = "Do not use rpy2 from nns implementation code. Keep R parity calls outside the package." } [tool.ruff.lint.per-file-ignores] "tests/**" = ["TID251"] @@ -93,5 +100,5 @@ select = ["E", "F", "I", "B", "UP", "N", "RUF", "TID"] [tool.mypy] python_version = "3.11" strict = true -files = ["src/pynns", "tests"] +files = ["src/nns", "tests"] mypy_path = ["tests"] diff --git a/scripts/regenerate_r_cache.py b/scripts/regenerate_r_cache.py index ba704cea..860d8e95 100644 --- a/scripts/regenerate_r_cache.py +++ b/scripts/regenerate_r_cache.py @@ -14,7 +14,13 @@ def main() -> int: env = os.environ.copy() - for name in ("PYNNS_R_CACHE_ONLY", "PYNNS_OFFLINE", "CI"): + for name in ( + "NNS_R_CACHE_ONLY", + "PYNNS_R_CACHE_ONLY", + "NNS_OFFLINE", + "PYNNS_OFFLINE", + "CI", + ): env.pop(name, None) args = sys.argv[1:] diff --git a/scripts/update_benchmarks_doc.py b/scripts/update_benchmarks_doc.py index 755a2a13..0c058910 100644 --- a/scripts/update_benchmarks_doc.py +++ b/scripts/update_benchmarks_doc.py @@ -373,7 +373,7 @@ def _render_realistic_sd( "Run only the realistic Python benchmarks with:", "", "```bash", - "PYNNS_OFFLINE=1 uv run pytest -q -n0 -m benchmark --benchmark-enable \\", + "NNS_OFFLINE=1 uv run pytest -q -n0 -m benchmark --benchmark-enable \\", " --benchmark-json=docs/benchmark_reports/realistic_sd_python_latest.json \\", " tests/benchmarks/test_stochastic_dominance_realistic.py \\", " tests/benchmarks/test_finance_sd_rolling.py \\", @@ -447,7 +447,7 @@ def _render_realistic_sd( "- The implementation deliberately follows R's C++ SD algorithmic structure:", " sorted columns, prefix sums, pair-threshold dominance checks, exact guards, and", " no tolerance-based shortcuts.", - "- Full-fixture PyNNS runs are feasible for research iteration, but R's C++ SD", + "- Full-fixture NNS Python runs are feasible for research iteration, but R's C++ SD", " core remains materially faster on the largest cluster cases.", ] ) diff --git a/src/nns/__init__.py b/src/nns/__init__.py new file mode 100644 index 00000000..e6cc0fdb --- /dev/null +++ b/src/nns/__init__.py @@ -0,0 +1,84 @@ +from __future__ import annotations + +from typing import Any + +from nns.pm_matrix import pm_matrix as pm_matrix + +__version__ = "1.0.0a0" + +_EXPORTS = { + "FactorDesign": ("nns.regression", "FactorDesign"), + "causal_matrix": ("nns.causation", "causal_matrix"), + "co_lpm": ("nns.co_moments", "co_lpm"), + "co_lpm_nd": ("nns.dependence", "co_lpm_nd"), + "co_upm": ("nns.co_moments", "co_upm"), + "co_upm_nd": ("nns.dependence", "co_upm_nd"), + "d_lpm": ("nns.co_moments", "d_lpm"), + "dpm_nd": ("nns.dependence", "dpm_nd"), + "dy_d": ("nns.diff", "dy_d"), + "dy_dx": ("nns.diff", "dy_dx"), + "d_upm": ("nns.co_moments", "d_upm"), + "ecdf_pm": ("nns.classical", "ecdf_pm"), + "encode_factor_codes": ("nns.categorical", "encode_factor_codes"), + "factor_2_dummy": ("nns.categorical", "factor_2_dummy"), + "factor_2_dummy_fr": ("nns.categorical", "factor_2_dummy_fr"), + "fsd": ("nns.stochastic_dominance", "fsd"), + "fsd_uni": ("nns.stochastic_dominance", "fsd_uni"), + "kurt_pm": ("nns.classical", "kurt_pm"), + "lpm": ("nns.core", "lpm"), + "lpm_ratio": ("nns.core", "lpm_ratio"), + "lpm_var": ("nns.var", "lpm_var"), + "mean_pm": ("nns.classical", "mean_pm"), + "nns_anova": ("nns.anova", "nns_anova"), + "nns_arma": ("nns.arma", "nns_arma"), + "nns_arma_optim": ("nns.arma", "nns_arma_optim"), + "nns_boost": ("nns.boost", "nns_boost"), + "nns_causation": ("nns.causation", "nns_causation"), + "nns_cdf": ("nns.cdf", "nns_cdf"), + "nns_copula": ("nns.copula", "nns_copula"), + "nns_cor": ("nns.dependence", "nns_cor"), + "nns_dep": ("nns.dependence", "nns_dep"), + "nns_diff": ("nns.diff", "nns_diff"), + "nns_distance": ("nns.distance", "nns_distance"), + "nns_distance_bulk": ("nns.distance", "nns_distance_bulk"), + "nns_gravity": ("nns.central_tendencies", "nns_gravity"), + "nns_mode": ("nns.central_tendencies", "nns_mode"), + "nns_moments": ("nns.classical", "nns_moments"), + "nns_m_reg": ("nns.multivariate_regression", "nns_m_reg"), + "nns_mc": ("nns.mc", "nns_mc"), + "nns_meboot": ("nns.meboot", "nns_meboot"), + "nns_norm": ("nns.norm", "nns_norm"), + "nns_nowcast_panel": ("nns.nowcast", "nns_nowcast_panel"), + "nns_part": ("nns.part", "nns_part"), + "nns_reg": ("nns.regression", "nns_reg"), + "nns_rescale": ("nns.central_tendencies", "nns_rescale"), + "nns_seas": ("nns.seasonality", "nns_seas"), + "nns_sd_cluster": ("nns.stochastic_dominance", "nns_sd_cluster"), + "nns_stack": ("nns.stack", "nns_stack"), + "nns_ss": ("nns.stochastic_superiority", "nns_ss"), + "nns_var": ("nns.var", "nns_var"), + "prepare_factor_predictors": ("nns.regression", "prepare_factor_predictors"), + "sd_efficient_set": ("nns.stochastic_dominance", "sd_efficient_set"), + "skew_pm": ("nns.classical", "skew_pm"), + "ssd": ("nns.stochastic_dominance", "ssd"), + "ssd_uni": ("nns.stochastic_dominance", "ssd_uni"), + "tsd": ("nns.stochastic_dominance", "tsd"), + "tsd_uni": ("nns.stochastic_dominance", "tsd_uni"), + "upm": ("nns.core", "upm"), + "upm_ratio": ("nns.core", "upm_ratio"), + "upm_var": ("nns.var", "upm_var"), + "var_pm": ("nns.classical", "var_pm"), +} + +__all__ = sorted((*_EXPORTS, "pm_matrix")) + + +def __getattr__(name: str) -> Any: + if name not in _EXPORTS: + raise AttributeError(f"module 'nns' has no attribute {name!r}") + module_name, attr_name = _EXPORTS[name] + from importlib import import_module + + value = getattr(import_module(module_name), attr_name) + globals()[name] = value + return value diff --git a/src/pynns/_helpers.py b/src/nns/_helpers.py similarity index 97% rename from src/pynns/_helpers.py rename to src/nns/_helpers.py index 837e9d5d..e333da76 100644 --- a/src/pynns/_helpers.py +++ b/src/nns/_helpers.py @@ -3,7 +3,7 @@ import numpy as np from numpy.typing import NDArray -from pynns._native import nnscore +from nns._native import nnscore def _fast_lm(x: NDArray[np.float64], y: NDArray[np.float64]) -> tuple[float, float]: diff --git a/src/pynns/_native.py b/src/nns/_native.py similarity index 66% rename from src/pynns/_native.py rename to src/nns/_native.py index f97ffd2d..b071e530 100644 --- a/src/pynns/_native.py +++ b/src/nns/_native.py @@ -4,10 +4,10 @@ import importlib.util from typing import Any, cast -_NNSCORE_SPEC = importlib.util.find_spec("pynns._nnscore") +_NNSCORE_SPEC = importlib.util.find_spec("nns._nnscore") try: - _nnscore = importlib.import_module("pynns._nnscore") if _NNSCORE_SPEC is not None else None + _nnscore = importlib.import_module("nns._nnscore") if _NNSCORE_SPEC is not None else None except (ImportError, OSError): _nnscore = None diff --git a/src/pynns/_nnscore.pyi b/src/nns/_nnscore.pyi similarity index 100% rename from src/pynns/_nnscore.pyi rename to src/nns/_nnscore.pyi diff --git a/src/pynns/_nnscore_bindings.cpp b/src/nns/_nnscore_bindings.cpp similarity index 100% rename from src/pynns/_nnscore_bindings.cpp rename to src/nns/_nnscore_bindings.cpp diff --git a/src/pynns/anova.py b/src/nns/anova.py similarity index 99% rename from src/pynns/anova.py rename to src/nns/anova.py index fe857943..b0c32cbb 100644 --- a/src/pynns/anova.py +++ b/src/nns/anova.py @@ -6,8 +6,8 @@ import numpy as np from numpy.typing import NDArray -from pynns.core import lpm_ratio, upm_ratio -from pynns.dependence import _gravity +from nns.core import lpm_ratio, upm_ratio +from nns.dependence import _gravity AnovaResult = dict[str, float] Tail = Literal["both", "left", "right"] diff --git a/src/pynns/arma.py b/src/nns/arma.py similarity index 99% rename from src/pynns/arma.py rename to src/nns/arma.py index 6a0a46d3..bc6a0dd4 100644 --- a/src/pynns/arma.py +++ b/src/nns/arma.py @@ -6,13 +6,13 @@ import numpy as np from numpy.typing import NDArray -from pynns._helpers import _fast_lm -from pynns.co_moments import co_lpm, co_upm -from pynns.dependence import _gravity -from pynns.mc import nns_mc -from pynns.regression import nns_reg -from pynns.seasonality import nns_seas -from pynns.var import lpm_var, upm_var +from nns._helpers import _fast_lm +from nns.co_moments import co_lpm, co_upm +from nns.dependence import _gravity +from nns.mc import nns_mc +from nns.regression import nns_reg +from nns.seasonality import nns_seas +from nns.var import lpm_var, upm_var def nns_arma_optim( diff --git a/src/pynns/boost.py b/src/nns/boost.py similarity index 99% rename from src/pynns/boost.py rename to src/nns/boost.py index ce464a66..133fafcc 100644 --- a/src/pynns/boost.py +++ b/src/nns/boost.py @@ -9,9 +9,9 @@ import numpy as np from numpy.typing import NDArray -from pynns.categorical import _balance_class_training, _dense_factor_codes, encode_factor_codes -from pynns.dependence import _gravity -from pynns.regression import ( +from nns.categorical import _balance_class_training, _dense_factor_codes, encode_factor_codes +from nns.dependence import _gravity +from nns.regression import ( Order, _normalize_type, _prepare_y_values, @@ -19,7 +19,7 @@ _round_clamp_classes, nns_reg, ) -from pynns.stack import nns_stack +from nns.stack import nns_stack Objective = Literal["min", "max"] BoostResult = dict[str, Any] diff --git a/src/pynns/categorical.py b/src/nns/categorical.py similarity index 100% rename from src/pynns/categorical.py rename to src/nns/categorical.py diff --git a/src/pynns/causation.py b/src/nns/causation.py similarity index 97% rename from src/pynns/causation.py rename to src/nns/causation.py index 9d3ef39c..40dc9acb 100644 --- a/src/pynns/causation.py +++ b/src/nns/causation.py @@ -5,8 +5,8 @@ import numpy as np from numpy.typing import NDArray -from pynns.core import lpm_ratio, upm_ratio -from pynns.dependence import ( +from nns.core import lpm_ratio, upm_ratio +from nns.dependence import ( _as_pair, _copula_degree0_unsigned, _copula_signed, @@ -17,8 +17,8 @@ _is_discrete_case, _xonly_partition, ) -from pynns.norm import nns_norm -from pynns.seasonality import nns_seas +from nns.norm import nns_norm +from nns.seasonality import nns_seas CausationResult = dict[str, float] diff --git a/src/pynns/cdf.py b/src/nns/cdf.py similarity index 99% rename from src/pynns/cdf.py rename to src/nns/cdf.py index 9fc40d85..a70748cf 100644 --- a/src/pynns/cdf.py +++ b/src/nns/cdf.py @@ -7,8 +7,8 @@ import numpy as np from numpy.typing import NDArray -from pynns.dependence import co_lpm_nd -from pynns.regression import nns_reg +from nns.dependence import co_lpm_nd +from nns.regression import nns_reg def nns_cdf( diff --git a/src/pynns/central_tendencies.py b/src/nns/central_tendencies.py similarity index 98% rename from src/pynns/central_tendencies.py rename to src/nns/central_tendencies.py index 00036d76..f3c4cd40 100644 --- a/src/pynns/central_tendencies.py +++ b/src/nns/central_tendencies.py @@ -5,8 +5,8 @@ import numpy as np from numpy.typing import NDArray -from pynns._native import nnscore -from pynns.dependence import _quartiles_like_r_code, _simple_bin_counts +from nns._native import nnscore +from nns.dependence import _quartiles_like_r_code, _simple_bin_counts def nns_rescale( diff --git a/src/pynns/classical.py b/src/nns/classical.py similarity index 98% rename from src/pynns/classical.py rename to src/nns/classical.py index f18c021c..f04c5c24 100644 --- a/src/pynns/classical.py +++ b/src/nns/classical.py @@ -3,7 +3,7 @@ import numpy as np from numpy.typing import NDArray -from pynns.core import _as_1d_values, lpm, upm +from nns.core import _as_1d_values, lpm, upm def mean_pm(x: NDArray[np.float64]) -> float: diff --git a/src/pynns/co_moments.py b/src/nns/co_moments.py similarity index 98% rename from src/pynns/co_moments.py rename to src/nns/co_moments.py index 7130f578..e2f99ac3 100644 --- a/src/pynns/co_moments.py +++ b/src/nns/co_moments.py @@ -3,8 +3,8 @@ import numpy as np from numpy.typing import NDArray -from pynns._native import nnscore -from pynns.core import _as_degree, _as_targets +from nns._native import nnscore +from nns.core import _as_degree, _as_targets def co_lpm( diff --git a/src/pynns/copula.py b/src/nns/copula.py similarity index 97% rename from src/pynns/copula.py rename to src/nns/copula.py index cbb5847a..5f999451 100644 --- a/src/pynns/copula.py +++ b/src/nns/copula.py @@ -7,9 +7,9 @@ import numpy as np from numpy.typing import NDArray -from pynns.co_moments import _as_pair -from pynns.dependence import _dpm_nd -from pynns.pm_matrix import pm_matrix +from nns.co_moments import _as_pair +from nns.dependence import _dpm_nd +from nns.pm_matrix import pm_matrix def nns_copula( diff --git a/src/pynns/core.py b/src/nns/core.py similarity index 99% rename from src/pynns/core.py rename to src/nns/core.py index eb1571d8..04baa515 100644 --- a/src/pynns/core.py +++ b/src/nns/core.py @@ -3,7 +3,7 @@ import numpy as np from numpy.typing import NDArray -from pynns._native import nnscore +from nns._native import nnscore def lpm( diff --git a/src/pynns/dependence.py b/src/nns/dependence.py similarity index 99% rename from src/pynns/dependence.py rename to src/nns/dependence.py index 62fe41ac..f092ab52 100644 --- a/src/pynns/dependence.py +++ b/src/nns/dependence.py @@ -6,7 +6,7 @@ import numpy as np from numpy.typing import NDArray -from pynns.co_moments import co_lpm, co_upm, d_lpm, d_upm +from nns.co_moments import co_lpm, co_upm, d_lpm, d_upm def nns_dep( diff --git a/src/pynns/diff.py b/src/nns/diff.py similarity index 97% rename from src/pynns/diff.py rename to src/nns/diff.py index d98cbf66..7f486f80 100644 --- a/src/pynns/diff.py +++ b/src/nns/diff.py @@ -180,7 +180,7 @@ def dy_dx( if isinstance(eval_point, str): if eval_point.lower() != "overall": raise ValueError("eval_point must be 'overall', numeric, or None.") - from pynns.regression import nns_reg + from nns.regression import nns_reg result = nns_reg( x_values, @@ -264,7 +264,7 @@ def _dy_d_scalar( h_s = _derivative_bandwidths(x_values.shape[0]) results: list[dict[str, NDArray[np.float64]] | None] = [None] * h_s.size cumulative_step = 0.0 - from pynns.dependence import _gravity + from nns.dependence import _gravity for h_value in h_s: # R overwrites duplicate rounded bandwidths at their first result slot. @@ -313,8 +313,8 @@ def _dy_dx_numeric( y: NDArray[np.float64], eval_points: NDArray[np.float64], ) -> dict[str, NDArray[np.float64]]: - from pynns.dependence import _gravity - from pynns.regression import nns_reg + from nns.dependence import _gravity + from nns.regression import nns_reg if eval_points.size == 0: raise ValueError("eval_point must contain at least one value.") @@ -416,7 +416,7 @@ def _dy_d_matrix_band( *, mixed: bool, ) -> tuple[NDArray[np.float64], NDArray[np.float64], NDArray[np.float64] | None]: - from pynns.regression import nns_reg + from nns.regression import nns_reg n = eval_points.shape[0] lower_points = eval_points.copy() @@ -462,10 +462,10 @@ def _dy_d_vector_band( *, mixed: bool, ) -> tuple[NDArray[np.float64], NDArray[np.float64], NDArray[np.float64] | None]: - from pynns.dependence import _gravity, nns_dep - from pynns.norm import nns_norm - from pynns.regression import nns_reg - from pynns.var import lpm_var + from nns.dependence import _gravity, nns_dep + from nns.norm import nns_norm + from nns.regression import nns_reg + from nns.var import lpm_var eval_vector = eval_values.reshape(-1) lower_eval = eval_vector - perturbation_step @@ -527,8 +527,8 @@ def _dy_d_vector_band( def _nns_copula_matrix(values: NDArray[np.float64]) -> float: - from pynns.dependence import _dpm_nd - from pynns.pm_matrix import pm_matrix + from nns.dependence import _dpm_nd + from nns.pm_matrix import pm_matrix data = np.asarray(values, dtype=np.float64) if data.ndim != 2 or data.shape[1] < 2: @@ -583,8 +583,8 @@ def _dy_d_mixed( wrt_index: int, matrix_points: bool, ) -> NDArray[np.float64]: - from pynns.dependence import _gravity - from pynns.regression import nns_reg + from nns.dependence import _gravity + from nns.regression import nns_reg if x.shape[1] != 2: raise ValueError("Mixed Derivatives are only for 2 IV") diff --git a/src/pynns/distance.py b/src/nns/distance.py similarity index 100% rename from src/pynns/distance.py rename to src/nns/distance.py diff --git a/src/pynns/mc.py b/src/nns/mc.py similarity index 98% rename from src/pynns/mc.py rename to src/nns/mc.py index 2ad9375e..a60dafed 100644 --- a/src/pynns/mc.py +++ b/src/nns/mc.py @@ -6,7 +6,7 @@ import numpy as np from numpy.typing import NDArray -from pynns.meboot import nns_meboot +from nns.meboot import nns_meboot def nns_mc( diff --git a/src/pynns/meboot.py b/src/nns/meboot.py similarity index 99% rename from src/pynns/meboot.py rename to src/nns/meboot.py index 18a8799d..b30b02ea 100644 --- a/src/pynns/meboot.py +++ b/src/nns/meboot.py @@ -5,8 +5,8 @@ import numpy as np from numpy.typing import NDArray -from pynns._helpers import _fast_lm -from pynns.dependence import nns_dep +from nns._helpers import _fast_lm +from nns.dependence import nns_dep MebootResult = dict[str, NDArray[np.float64] | float | None] diff --git a/src/pynns/multivariate_regression.py b/src/nns/multivariate_regression.py similarity index 97% rename from src/pynns/multivariate_regression.py rename to src/nns/multivariate_regression.py index 573c29df..6a185f7d 100644 --- a/src/pynns/multivariate_regression.py +++ b/src/nns/multivariate_regression.py @@ -6,13 +6,13 @@ import numpy as np from numpy.typing import NDArray -from pynns.central_tendencies import nns_mode -from pynns.dependence import _gravity -from pynns.distance import KValue, nns_distance -from pynns.part import NoiseReduction -from pynns.regression import Order, _normalize_type, _round_clamp_classes, nns_reg -from pynns.regression import _nns_copula_matrix as _copula_matrix -from pynns.var import upm_var +from nns.central_tendencies import nns_mode +from nns.dependence import _gravity +from nns.distance import KValue, nns_distance +from nns.part import NoiseReduction +from nns.regression import Order, _normalize_type, _round_clamp_classes, nns_reg +from nns.regression import _nns_copula_matrix as _copula_matrix +from nns.var import upm_var NBest = int | Literal["all"] | None MRegResult = dict[str, Any] @@ -139,7 +139,7 @@ def _validate_inputs( x_values = x_values.reshape(-1, 1) if x_values.ndim != 2: raise ValueError("x must be a 2D numeric matrix.") - from pynns.regression import _prepare_y_values + from nns.regression import _prepare_y_values y_values, _ = _prepare_y_values( y, diff --git a/src/pynns/norm.py b/src/nns/norm.py similarity index 97% rename from src/pynns/norm.py rename to src/nns/norm.py index 12c9a00c..b9c4496d 100644 --- a/src/pynns/norm.py +++ b/src/nns/norm.py @@ -5,7 +5,7 @@ import numpy as np from numpy.typing import NDArray -from pynns.dependence import nns_dep +from nns.dependence import nns_dep def nns_norm(x: NDArray[np.float64], linear: bool = False) -> NDArray[np.float64]: diff --git a/src/pynns/nowcast.py b/src/nns/nowcast.py similarity index 99% rename from src/pynns/nowcast.py rename to src/nns/nowcast.py index ed2bf4bd..b584feb6 100644 --- a/src/pynns/nowcast.py +++ b/src/nns/nowcast.py @@ -6,7 +6,7 @@ import numpy as np from numpy.typing import NDArray -from pynns.var import nns_var +from nns.var import nns_var _DEFAULT_NOWCAST_SERIES = ( "PAYEMS", diff --git a/src/pynns/part.py b/src/nns/part.py similarity index 98% rename from src/pynns/part.py rename to src/nns/part.py index 8fb81b2f..7a3d346c 100644 --- a/src/pynns/part.py +++ b/src/nns/part.py @@ -6,8 +6,8 @@ import numpy as np from numpy.typing import NDArray -from pynns.central_tendencies import _nearest_int_half_up_array, nns_mode -from pynns.dependence import _gravity +from nns.central_tendencies import _nearest_int_half_up_array, nns_mode +from nns.dependence import _gravity NoiseReduction: TypeAlias = Literal["off", "mean", "median", "mode", "mode_class"] diff --git a/src/pynns/pm_matrix.py b/src/nns/pm_matrix.py similarity index 98% rename from src/pynns/pm_matrix.py rename to src/nns/pm_matrix.py index 16edbb14..a3655800 100644 --- a/src/pynns/pm_matrix.py +++ b/src/nns/pm_matrix.py @@ -6,8 +6,8 @@ import numpy as np from numpy.typing import NDArray -from pynns._native import nnscore -from pynns.core import _as_degree +from nns._native import nnscore +from nns.core import _as_degree Target: TypeAlias = float | None | Literal["mean"] | NDArray[np.float64] PMMatrixResult: TypeAlias = dict[str, Any] diff --git a/src/pynns/providers/__init__.py b/src/nns/providers/__init__.py similarity index 56% rename from src/pynns/providers/__init__.py rename to src/nns/providers/__init__.py index 97940493..1f519dc0 100644 --- a/src/pynns/providers/__init__.py +++ b/src/nns/providers/__init__.py @@ -1,5 +1,5 @@ from __future__ import annotations -from pynns.providers.nowcast import CsvNowcastProvider +from nns.providers.nowcast import CsvNowcastProvider __all__ = ["CsvNowcastProvider"] diff --git a/src/pynns/providers/nowcast.py b/src/nns/providers/nowcast.py similarity index 98% rename from src/pynns/providers/nowcast.py rename to src/nns/providers/nowcast.py index d6cef6fe..aafb556c 100644 --- a/src/pynns/providers/nowcast.py +++ b/src/nns/providers/nowcast.py @@ -6,7 +6,7 @@ from pathlib import Path from typing import Any, cast -from pynns.nowcast import _normalize_month_label +from nns.nowcast import _normalize_month_label class CsvNowcastProvider: diff --git a/src/pynns/py.typed b/src/nns/py.typed similarity index 100% rename from src/pynns/py.typed rename to src/nns/py.typed diff --git a/src/pynns/regression.py b/src/nns/regression.py similarity index 98% rename from src/pynns/regression.py rename to src/nns/regression.py index 28ed8e4a..dfe23417 100644 --- a/src/pynns/regression.py +++ b/src/nns/regression.py @@ -8,15 +8,15 @@ import numpy as np from numpy.typing import NDArray -from pynns._helpers import _fast_lm, _is_fcl -from pynns.categorical import encode_factor_codes, factor_2_dummy_fr -from pynns.causation import _uni_caus -from pynns.central_tendencies import nns_mode -from pynns.copula import _copula -from pynns.dependence import _gravity, nns_dep -from pynns.part import NoiseReduction, nns_part -from pynns.smoothing import r_smooth_spline_fixed_spar -from pynns.var import lpm_var, upm_var +from nns._helpers import _fast_lm, _is_fcl +from nns.categorical import encode_factor_codes, factor_2_dummy_fr +from nns.causation import _uni_caus +from nns.central_tendencies import nns_mode +from nns.copula import _copula +from nns.dependence import _gravity, nns_dep +from nns.part import NoiseReduction, nns_part +from nns.smoothing import r_smooth_spline_fixed_spar +from nns.var import lpm_var, upm_var Order = int | Literal["max"] | None @@ -95,7 +95,7 @@ def nns_reg( ) if np.asarray(x_for_dispatch).ndim == 2: - from pynns.multivariate_regression import nns_m_reg + from nns.multivariate_regression import nns_m_reg y_matrix_values, _ = _prepare_y_values(y, type_value=type_value, class_levels=class_levels) dispatch_n_best = n_best diff --git a/src/pynns/seasonality.py b/src/nns/seasonality.py similarity index 100% rename from src/pynns/seasonality.py rename to src/nns/seasonality.py diff --git a/src/pynns/smoothing.py b/src/nns/smoothing.py similarity index 100% rename from src/pynns/smoothing.py rename to src/nns/smoothing.py diff --git a/src/pynns/stack.py b/src/nns/stack.py similarity index 99% rename from src/pynns/stack.py rename to src/nns/stack.py index 34c6cbeb..57862376 100644 --- a/src/pynns/stack.py +++ b/src/nns/stack.py @@ -7,10 +7,10 @@ import numpy as np from numpy.typing import NDArray -from pynns.categorical import _balance_class_training, _dense_factor_codes -from pynns.central_tendencies import nns_mode -from pynns.dependence import _gravity -from pynns.regression import ( +from nns.categorical import _balance_class_training, _dense_factor_codes +from nns.central_tendencies import nns_mode +from nns.dependence import _gravity +from nns.regression import ( Order, _expand_factor_predictors, _normalize_type, diff --git a/src/pynns/stochastic_dominance.py b/src/nns/stochastic_dominance.py similarity index 99% rename from src/pynns/stochastic_dominance.py rename to src/nns/stochastic_dominance.py index f60e924a..fa0a8363 100644 --- a/src/pynns/stochastic_dominance.py +++ b/src/nns/stochastic_dominance.py @@ -16,7 +16,7 @@ from scipy.cluster.hierarchy import linkage # type: ignore[import-untyped] from scipy.spatial.distance import squareform # type: ignore[import-untyped] -from pynns.core import _as_1d_values, lpm +from nns.core import _as_1d_values, lpm _SD_CLUSTER_DOMINANCE_MATRIX_MIN_COLUMNS = 75 _SD_PREFIX_PAIR_MATRIX_MIN_COLUMNS = 75 diff --git a/src/pynns/stochastic_superiority.py b/src/nns/stochastic_superiority.py similarity index 97% rename from src/pynns/stochastic_superiority.py rename to src/nns/stochastic_superiority.py index 31b42939..f2f44eee 100644 --- a/src/pynns/stochastic_superiority.py +++ b/src/nns/stochastic_superiority.py @@ -5,9 +5,9 @@ import numpy as np from numpy.typing import NDArray -from pynns._native import nnscore -from pynns.meboot import nns_meboot -from pynns.var import lpm_var, upm_var +from nns._native import nnscore +from nns.meboot import nns_meboot +from nns.var import lpm_var, upm_var def nns_ss( diff --git a/src/pynns/var.py b/src/nns/var.py similarity index 98% rename from src/pynns/var.py rename to src/nns/var.py index 1cf16415..65badf59 100644 --- a/src/pynns/var.py +++ b/src/nns/var.py @@ -8,7 +8,7 @@ import numpy as np from numpy.typing import NDArray -from pynns.core import lpm_ratio, upm_ratio +from nns.core import lpm_ratio, upm_ratio _R_OPTIMIZE_TOL = float(np.finfo(float).eps ** 0.25) @@ -110,10 +110,10 @@ def _var_interpolate_and_extrapolate( if len(names) != n_vars: raise ValueError("names length must match number of variables.") - from pynns.arma import nns_arma_optim - from pynns.regression import nns_reg - from pynns.seasonality import nns_seas - from pynns.stack import nns_stack + from nns.arma import nns_arma_optim + from nns.regression import nns_reg + from nns.seasonality import nns_seas + from nns.stack import nns_stack interpolated = np.empty_like(vars_matrix) univariate_columns: list[np.ndarray] = [] @@ -227,8 +227,8 @@ def _var_multivariate_stack_stage( if univariate_matrix.shape != (h, n_vars): raise ValueError("univariate shape must be (h, n_variables).") - from pynns.co_moments import co_lpm, co_upm - from pynns.stack import _spearman_scores, nns_stack + from nns.co_moments import co_lpm, co_upm + from nns.stack import _spearman_scores, nns_stack if names is None: names = [f"x{i + 1}" for i in range(n_vars)] @@ -359,7 +359,7 @@ def var_obj_fn(predicted: np.ndarray, actual: np.ndarray) -> float: def _dependence_scores(values: NDArray[np.float64]) -> NDArray[np.float64]: - from pynns.dependence import nns_dep + from nns.dependence import nns_dep matrix = np.asarray(values, dtype=np.float64) if matrix.ndim != 2: @@ -374,7 +374,7 @@ def _dependence_scores(values: NDArray[np.float64]) -> NDArray[np.float64]: def _causation_scores(values: NDArray[np.float64]) -> NDArray[np.float64]: - from pynns.causation import causal_matrix + from nns.causation import causal_matrix matrix = np.asarray(values, dtype=np.float64) if matrix.ndim != 2: @@ -385,7 +385,7 @@ def _causation_scores(values: NDArray[np.float64]) -> NDArray[np.float64]: def _combined_scores(values: NDArray[np.float64]) -> NDArray[np.float64]: - from pynns.stack import _spearman_scores + from nns.stack import _spearman_scores matrix = np.asarray(values, dtype=np.float64) if matrix.ndim != 2: diff --git a/src/pynns/__init__.py b/src/pynns/__init__.py deleted file mode 100644 index bc58270d..00000000 --- a/src/pynns/__init__.py +++ /dev/null @@ -1,84 +0,0 @@ -from __future__ import annotations - -from typing import Any - -from pynns.pm_matrix import pm_matrix as pm_matrix - -__version__ = "0.2.0" - -_EXPORTS = { - "FactorDesign": ("pynns.regression", "FactorDesign"), - "causal_matrix": ("pynns.causation", "causal_matrix"), - "co_lpm": ("pynns.co_moments", "co_lpm"), - "co_lpm_nd": ("pynns.dependence", "co_lpm_nd"), - "co_upm": ("pynns.co_moments", "co_upm"), - "co_upm_nd": ("pynns.dependence", "co_upm_nd"), - "d_lpm": ("pynns.co_moments", "d_lpm"), - "dpm_nd": ("pynns.dependence", "dpm_nd"), - "dy_d": ("pynns.diff", "dy_d"), - "dy_dx": ("pynns.diff", "dy_dx"), - "d_upm": ("pynns.co_moments", "d_upm"), - "ecdf_pm": ("pynns.classical", "ecdf_pm"), - "encode_factor_codes": ("pynns.categorical", "encode_factor_codes"), - "factor_2_dummy": ("pynns.categorical", "factor_2_dummy"), - "factor_2_dummy_fr": ("pynns.categorical", "factor_2_dummy_fr"), - "fsd": ("pynns.stochastic_dominance", "fsd"), - "fsd_uni": ("pynns.stochastic_dominance", "fsd_uni"), - "kurt_pm": ("pynns.classical", "kurt_pm"), - "lpm": ("pynns.core", "lpm"), - "lpm_ratio": ("pynns.core", "lpm_ratio"), - "lpm_var": ("pynns.var", "lpm_var"), - "mean_pm": ("pynns.classical", "mean_pm"), - "nns_anova": ("pynns.anova", "nns_anova"), - "nns_arma": ("pynns.arma", "nns_arma"), - "nns_arma_optim": ("pynns.arma", "nns_arma_optim"), - "nns_boost": ("pynns.boost", "nns_boost"), - "nns_causation": ("pynns.causation", "nns_causation"), - "nns_cdf": ("pynns.cdf", "nns_cdf"), - "nns_copula": ("pynns.copula", "nns_copula"), - "nns_cor": ("pynns.dependence", "nns_cor"), - "nns_dep": ("pynns.dependence", "nns_dep"), - "nns_diff": ("pynns.diff", "nns_diff"), - "nns_distance": ("pynns.distance", "nns_distance"), - "nns_distance_bulk": ("pynns.distance", "nns_distance_bulk"), - "nns_gravity": ("pynns.central_tendencies", "nns_gravity"), - "nns_mode": ("pynns.central_tendencies", "nns_mode"), - "nns_moments": ("pynns.classical", "nns_moments"), - "nns_m_reg": ("pynns.multivariate_regression", "nns_m_reg"), - "nns_mc": ("pynns.mc", "nns_mc"), - "nns_meboot": ("pynns.meboot", "nns_meboot"), - "nns_norm": ("pynns.norm", "nns_norm"), - "nns_nowcast_panel": ("pynns.nowcast", "nns_nowcast_panel"), - "nns_part": ("pynns.part", "nns_part"), - "nns_reg": ("pynns.regression", "nns_reg"), - "nns_rescale": ("pynns.central_tendencies", "nns_rescale"), - "nns_seas": ("pynns.seasonality", "nns_seas"), - "nns_sd_cluster": ("pynns.stochastic_dominance", "nns_sd_cluster"), - "nns_stack": ("pynns.stack", "nns_stack"), - "nns_ss": ("pynns.stochastic_superiority", "nns_ss"), - "nns_var": ("pynns.var", "nns_var"), - "prepare_factor_predictors": ("pynns.regression", "prepare_factor_predictors"), - "sd_efficient_set": ("pynns.stochastic_dominance", "sd_efficient_set"), - "skew_pm": ("pynns.classical", "skew_pm"), - "ssd": ("pynns.stochastic_dominance", "ssd"), - "ssd_uni": ("pynns.stochastic_dominance", "ssd_uni"), - "tsd": ("pynns.stochastic_dominance", "tsd"), - "tsd_uni": ("pynns.stochastic_dominance", "tsd_uni"), - "upm": ("pynns.core", "upm"), - "upm_ratio": ("pynns.core", "upm_ratio"), - "upm_var": ("pynns.var", "upm_var"), - "var_pm": ("pynns.classical", "var_pm"), -} - -__all__ = sorted((*_EXPORTS, "pm_matrix")) - - -def __getattr__(name: str) -> Any: - if name not in _EXPORTS: - raise AttributeError(f"module 'pynns' has no attribute {name!r}") - module_name, attr_name = _EXPORTS[name] - from importlib import import_module - - value = getattr(import_module(module_name), attr_name) - globals()[name] = value - return value diff --git a/src/pynns/__pycache__/__init__.cpython-311.pyc b/src/pynns/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index 51c0fa64..00000000 Binary files a/src/pynns/__pycache__/__init__.cpython-311.pyc and /dev/null differ diff --git a/src/pynns/__pycache__/_helpers.cpython-311.pyc b/src/pynns/__pycache__/_helpers.cpython-311.pyc deleted file mode 100644 index b558eb59..00000000 Binary files a/src/pynns/__pycache__/_helpers.cpython-311.pyc and /dev/null differ diff --git a/src/pynns/__pycache__/_native.cpython-311.pyc b/src/pynns/__pycache__/_native.cpython-311.pyc deleted file mode 100644 index 4f456055..00000000 Binary files a/src/pynns/__pycache__/_native.cpython-311.pyc and /dev/null differ diff --git a/src/pynns/__pycache__/categorical.cpython-311.pyc b/src/pynns/__pycache__/categorical.cpython-311.pyc deleted file mode 100644 index 84bb7006..00000000 Binary files a/src/pynns/__pycache__/categorical.cpython-311.pyc and /dev/null differ diff --git a/src/pynns/__pycache__/causation.cpython-311.pyc b/src/pynns/__pycache__/causation.cpython-311.pyc deleted file mode 100644 index 328bc49e..00000000 Binary files a/src/pynns/__pycache__/causation.cpython-311.pyc and /dev/null differ diff --git a/src/pynns/__pycache__/central_tendencies.cpython-311.pyc b/src/pynns/__pycache__/central_tendencies.cpython-311.pyc deleted file mode 100644 index 5c048d4a..00000000 Binary files a/src/pynns/__pycache__/central_tendencies.cpython-311.pyc and /dev/null differ diff --git a/src/pynns/__pycache__/co_moments.cpython-311.pyc b/src/pynns/__pycache__/co_moments.cpython-311.pyc deleted file mode 100644 index 927ce8b3..00000000 Binary files a/src/pynns/__pycache__/co_moments.cpython-311.pyc and /dev/null differ diff --git a/src/pynns/__pycache__/copula.cpython-311.pyc b/src/pynns/__pycache__/copula.cpython-311.pyc deleted file mode 100644 index 226961e4..00000000 Binary files a/src/pynns/__pycache__/copula.cpython-311.pyc and /dev/null differ diff --git a/src/pynns/__pycache__/core.cpython-311.pyc b/src/pynns/__pycache__/core.cpython-311.pyc deleted file mode 100644 index 9268bd3a..00000000 Binary files a/src/pynns/__pycache__/core.cpython-311.pyc and /dev/null differ diff --git a/src/pynns/__pycache__/dependence.cpython-311.pyc b/src/pynns/__pycache__/dependence.cpython-311.pyc deleted file mode 100644 index 6feeba17..00000000 Binary files a/src/pynns/__pycache__/dependence.cpython-311.pyc and /dev/null differ diff --git a/src/pynns/__pycache__/norm.cpython-311.pyc b/src/pynns/__pycache__/norm.cpython-311.pyc deleted file mode 100644 index 12f5771f..00000000 Binary files a/src/pynns/__pycache__/norm.cpython-311.pyc and /dev/null differ diff --git a/src/pynns/__pycache__/part.cpython-311.pyc b/src/pynns/__pycache__/part.cpython-311.pyc deleted file mode 100644 index b35375e9..00000000 Binary files a/src/pynns/__pycache__/part.cpython-311.pyc and /dev/null differ diff --git a/src/pynns/__pycache__/pm_matrix.cpython-311.pyc b/src/pynns/__pycache__/pm_matrix.cpython-311.pyc deleted file mode 100644 index 3ff9a155..00000000 Binary files a/src/pynns/__pycache__/pm_matrix.cpython-311.pyc and /dev/null differ diff --git a/src/pynns/__pycache__/regression.cpython-311.pyc b/src/pynns/__pycache__/regression.cpython-311.pyc deleted file mode 100644 index 89926135..00000000 Binary files a/src/pynns/__pycache__/regression.cpython-311.pyc and /dev/null differ diff --git a/src/pynns/__pycache__/seasonality.cpython-311.pyc b/src/pynns/__pycache__/seasonality.cpython-311.pyc deleted file mode 100644 index b70442e7..00000000 Binary files a/src/pynns/__pycache__/seasonality.cpython-311.pyc and /dev/null differ diff --git a/src/pynns/__pycache__/smoothing.cpython-311.pyc b/src/pynns/__pycache__/smoothing.cpython-311.pyc deleted file mode 100644 index d31961a5..00000000 Binary files a/src/pynns/__pycache__/smoothing.cpython-311.pyc and /dev/null differ diff --git a/src/pynns/__pycache__/var.cpython-311.pyc b/src/pynns/__pycache__/var.cpython-311.pyc deleted file mode 100644 index 1b3d9ff1..00000000 Binary files a/src/pynns/__pycache__/var.cpython-311.pyc and /dev/null differ diff --git a/tests/_r.py b/tests/_r.py index c4aa290e..5361c523 100644 --- a/tests/_r.py +++ b/tests/_r.py @@ -38,7 +38,8 @@ def nns(function: str, *args: Any) -> RValue: if _offline(): raise RuntimeError( f"R cache miss for NNS::{function} with key {key}. " - f"Run without CI/PYNNS_R_CACHE_ONLY/PYNNS_OFFLINE to populate {_CACHE_PATH}." + "Run without CI/NNS_R_CACHE_ONLY/PYNNS_R_CACHE_ONLY/" + f"NNS_OFFLINE/PYNNS_OFFLINE to populate {_CACHE_PATH}." ) return _uncached_nns(function, args, key, refresh) @@ -934,7 +935,9 @@ def _cache_lock() -> Iterator[None]: def _offline() -> bool: return ( os.environ.get("CI") == "true" + or os.environ.get("NNS_R_CACHE_ONLY") == "1" or os.environ.get("PYNNS_R_CACHE_ONLY") == "1" + or os.environ.get("NNS_OFFLINE") == "1" or os.environ.get("PYNNS_OFFLINE") == "1" ) diff --git a/tests/benchmarks/test_finance_partial_moment_workflows.py b/tests/benchmarks/test_finance_partial_moment_workflows.py index f75f4383..5421c937 100644 --- a/tests/benchmarks/test_finance_partial_moment_workflows.py +++ b/tests/benchmarks/test_finance_partial_moment_workflows.py @@ -14,7 +14,7 @@ ) from numpy.typing import NDArray -from pynns import co_lpm, nns_reg, pm_matrix +from nns import co_lpm, nns_reg, pm_matrix _BENCHMARK_ROWS = 252 _FULL_HISTORY_ROWS = 1257 diff --git a/tests/benchmarks/test_finance_sd_rolling.py b/tests/benchmarks/test_finance_sd_rolling.py index 47501a0a..54d0d2dc 100644 --- a/tests/benchmarks/test_finance_sd_rolling.py +++ b/tests/benchmarks/test_finance_sd_rolling.py @@ -8,7 +8,7 @@ from _finance_fixture import MAX_COLUMN_COUNT, load_constituent_returns, load_dates from numpy.typing import NDArray -from pynns import nns_sd_cluster, sd_efficient_set +from nns import nns_sd_cluster, sd_efficient_set @pytest.mark.benchmark diff --git a/tests/benchmarks/test_lpm.py b/tests/benchmarks/test_lpm.py index 93e8405e..e3eae3e7 100644 --- a/tests/benchmarks/test_lpm.py +++ b/tests/benchmarks/test_lpm.py @@ -5,7 +5,7 @@ import numpy as np import pytest -from pynns import ( +from nns import ( dy_d, dy_dx, lpm, diff --git a/tests/benchmarks/test_stochastic_dominance_realistic.py b/tests/benchmarks/test_stochastic_dominance_realistic.py index 39ca7c7b..ca2b7984 100644 --- a/tests/benchmarks/test_stochastic_dominance_realistic.py +++ b/tests/benchmarks/test_stochastic_dominance_realistic.py @@ -8,7 +8,7 @@ import pytest from numpy.typing import NDArray -from pynns import co_lpm, nns_sd_cluster, pm_matrix, sd_efficient_set +from nns import co_lpm, nns_sd_cluster, pm_matrix, sd_efficient_set _FIXTURE = Path(__file__).parents[1] / "fixtures" / "finance" / "sp500_daily_returns_2019_2023.csv" _BENCHMARK_ROWS = 252 diff --git a/tests/conftest.py b/tests/conftest.py index af080dc3..8470d12e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -35,7 +35,7 @@ def pytest_configure(config: pytest.Config) -> None: - workers = os.environ.get("PYNNS_PYTEST_WORKERS") + workers = os.environ.get("NNS_PYTEST_WORKERS") or os.environ.get("PYNNS_PYTEST_WORKERS") if workers: config.option.numprocesses = workers if config.getoption("benchmark_only", default=False): diff --git a/tests/invariants/test_anova.py b/tests/invariants/test_anova.py index decf9dc9..d57c0110 100644 --- a/tests/invariants/test_anova.py +++ b/tests/invariants/test_anova.py @@ -3,7 +3,7 @@ import numpy as np import pytest -from pynns import nns_anova +from nns import nns_anova def test_nns_anova_identical_degenerate_groups_match_r_nan_convention() -> None: diff --git a/tests/invariants/test_arma.py b/tests/invariants/test_arma.py index 55b76932..325c2792 100644 --- a/tests/invariants/test_arma.py +++ b/tests/invariants/test_arma.py @@ -5,8 +5,8 @@ import numpy as np import pytest -from pynns import nns_arma, nns_arma_optim, nns_var -from pynns.arma import _default_arma_optim_objective, _numeric_seasonal_weights +from nns import nns_arma, nns_arma_optim, nns_var +from nns.arma import _default_arma_optim_objective, _numeric_seasonal_weights def test_nns_arma_output_length_matches_h() -> None: diff --git a/tests/invariants/test_boost.py b/tests/invariants/test_boost.py index f3f9c7aa..038559ae 100644 --- a/tests/invariants/test_boost.py +++ b/tests/invariants/test_boost.py @@ -5,8 +5,8 @@ import numpy as np import pytest -import pynns.boost as boost_module -from pynns import nns_boost +import nns.boost as boost_module +from nns import nns_boost def test_nns_boost_shapes_and_feature_weights() -> None: diff --git a/tests/invariants/test_causation.py b/tests/invariants/test_causation.py index 9d0ffb04..a3b8d031 100644 --- a/tests/invariants/test_causation.py +++ b/tests/invariants/test_causation.py @@ -3,7 +3,7 @@ import numpy as np import pytest -from pynns import causal_matrix, nns_causation +from nns import causal_matrix, nns_causation def test_nns_causation_identical_self_case() -> None: diff --git a/tests/invariants/test_cdf.py b/tests/invariants/test_cdf.py index bfeff132..7bd7fd3a 100644 --- a/tests/invariants/test_cdf.py +++ b/tests/invariants/test_cdf.py @@ -4,7 +4,7 @@ import numpy as np -from pynns import nns_cdf +from nns import nns_cdf def test_nns_cdf_return_keys_and_empty_target_value() -> None: diff --git a/tests/invariants/test_classical.py b/tests/invariants/test_classical.py index ff4a4a72..001fe8ed 100644 --- a/tests/invariants/test_classical.py +++ b/tests/invariants/test_classical.py @@ -5,7 +5,7 @@ from _tolerances import EXACT from scipy import stats # type: ignore[import-untyped] -from pynns import ecdf_pm, kurt_pm, mean_pm, skew_pm, var_pm +from nns import ecdf_pm, kurt_pm, mean_pm, skew_pm, var_pm def test_mean_pm_matches_numpy_mean() -> None: diff --git a/tests/invariants/test_co_moments.py b/tests/invariants/test_co_moments.py index b010d58a..9472f652 100644 --- a/tests/invariants/test_co_moments.py +++ b/tests/invariants/test_co_moments.py @@ -3,7 +3,7 @@ import numpy as np from _tolerances import EXACT -from pynns import co_lpm, co_upm, d_lpm, d_upm, lpm, upm +from nns import co_lpm, co_upm, d_lpm, d_upm, lpm, upm def test_co_lpm_self_equals_lpm_with_doubled_degree() -> None: diff --git a/tests/invariants/test_copula.py b/tests/invariants/test_copula.py index e346f2b5..1ce1cca1 100644 --- a/tests/invariants/test_copula.py +++ b/tests/invariants/test_copula.py @@ -3,7 +3,7 @@ import numpy as np import pytest -from pynns import nns_copula +from nns import nns_copula def test_nns_copula_is_bounded() -> None: diff --git a/tests/invariants/test_core.py b/tests/invariants/test_core.py index 6940a966..cced8427 100644 --- a/tests/invariants/test_core.py +++ b/tests/invariants/test_core.py @@ -6,7 +6,7 @@ import pytest from _tolerances import EXACT -from pynns import lpm, lpm_ratio, upm, upm_ratio +from nns import lpm, lpm_ratio, upm, upm_ratio def test_mean_decomposes_into_upm_minus_lpm() -> None: diff --git a/tests/invariants/test_deferred_paths.py b/tests/invariants/test_deferred_paths.py index b080e705..41b62af1 100644 --- a/tests/invariants/test_deferred_paths.py +++ b/tests/invariants/test_deferred_paths.py @@ -4,7 +4,7 @@ from pathlib import Path ROOT = Path(__file__).resolve().parents[2] -SRC = ROOT / "src" / "pynns" +SRC = ROOT / "src" / "nns" API_STATUS = ROOT / "docs" / "api_status.md" diff --git a/tests/invariants/test_dependence.py b/tests/invariants/test_dependence.py index 06f6ecd0..272b812d 100644 --- a/tests/invariants/test_dependence.py +++ b/tests/invariants/test_dependence.py @@ -4,7 +4,7 @@ import pytest from _tolerances import EXACT -from pynns import nns_dep +from nns import nns_dep def test_nns_dep_identical_has_unit_dependence() -> None: diff --git a/tests/invariants/test_diff.py b/tests/invariants/test_diff.py index 7508c54f..a331a355 100644 --- a/tests/invariants/test_diff.py +++ b/tests/invariants/test_diff.py @@ -3,7 +3,7 @@ import numpy as np import pytest -from pynns import dy_d, dy_dx, nns_diff +from nns import dy_d, dy_dx, nns_diff def test_nns_diff_constant_derivative_is_zero() -> None: diff --git a/tests/invariants/test_distance.py b/tests/invariants/test_distance.py index f474efb5..89584f98 100644 --- a/tests/invariants/test_distance.py +++ b/tests/invariants/test_distance.py @@ -2,7 +2,7 @@ import numpy as np -from pynns import nns_distance, nns_distance_bulk +from nns import nns_distance, nns_distance_bulk def test_nns_distance_self_target_returns_nearest_y_hat() -> None: diff --git a/tests/invariants/test_export_surface.py b/tests/invariants/test_export_surface.py index 1717ad63..8c388266 100644 --- a/tests/invariants/test_export_surface.py +++ b/tests/invariants/test_export_surface.py @@ -2,10 +2,10 @@ import pytest -import pynns +import nns def test_removed_r_nowcast_is_not_public() -> None: - assert "nns_nowcast" not in pynns.__all__ + assert "nns_nowcast" not in nns.__all__ with pytest.raises(AttributeError): - pynns.__getattr__("nns_nowcast") + nns.__getattr__("nns_nowcast") diff --git a/tests/invariants/test_mc.py b/tests/invariants/test_mc.py index 5e1e7bad..b6156587 100644 --- a/tests/invariants/test_mc.py +++ b/tests/invariants/test_mc.py @@ -3,7 +3,7 @@ import numpy as np import pytest -from pynns import nns_mc +from nns import nns_mc pytestmark = pytest.mark.stochastic diff --git a/tests/invariants/test_meboot.py b/tests/invariants/test_meboot.py index cc284a65..5d3f0c52 100644 --- a/tests/invariants/test_meboot.py +++ b/tests/invariants/test_meboot.py @@ -3,7 +3,7 @@ import numpy as np import pytest -from pynns import nns_meboot +from nns import nns_meboot pytestmark = pytest.mark.stochastic diff --git a/tests/invariants/test_multivariate_regression.py b/tests/invariants/test_multivariate_regression.py index 0866ee3c..9b05018c 100644 --- a/tests/invariants/test_multivariate_regression.py +++ b/tests/invariants/test_multivariate_regression.py @@ -3,7 +3,7 @@ import numpy as np import pytest -from pynns import nns_m_reg +from nns import nns_m_reg def test_nns_m_reg_shapes_and_bounds() -> None: diff --git a/tests/invariants/test_native_original_src_coverage.py b/tests/invariants/test_native_original_src_coverage.py index 9fc02420..b9a50e73 100644 --- a/tests/invariants/test_native_original_src_coverage.py +++ b/tests/invariants/test_native_original_src_coverage.py @@ -8,7 +8,7 @@ import numpy as np import pytest -from pynns import ( +from nns import ( co_lpm, co_upm, d_lpm, @@ -20,13 +20,13 @@ upm_ratio, ) -core_module = importlib.import_module("pynns.core") -co_moments_module = importlib.import_module("pynns.co_moments") -pm_matrix_module = importlib.import_module("pynns.pm_matrix") +core_module = importlib.import_module("nns.core") +co_moments_module = importlib.import_module("nns.co_moments") +pm_matrix_module = importlib.import_module("nns.pm_matrix") def _native() -> ModuleType: - return cast(ModuleType, pytest.importorskip("pynns._nnscore")) + return cast(ModuleType, pytest.importorskip("nns._nnscore")) pytestmark = pytest.mark.invariant @@ -34,7 +34,7 @@ def _native() -> ModuleType: @pytest.fixture() def native() -> ModuleType: - return cast(ModuleType, pytest.importorskip("pynns._nnscore")) + return cast(ModuleType, pytest.importorskip("nns._nnscore")) @pytest.fixture() diff --git a/tests/invariants/test_norm.py b/tests/invariants/test_norm.py index 34144690..7f4c0010 100644 --- a/tests/invariants/test_norm.py +++ b/tests/invariants/test_norm.py @@ -2,7 +2,7 @@ import numpy as np -from pynns import nns_norm +from nns import nns_norm def test_nns_norm_shape_matches_input() -> None: diff --git a/tests/invariants/test_nowcast.py b/tests/invariants/test_nowcast.py index 0ee39472..bce2619e 100644 --- a/tests/invariants/test_nowcast.py +++ b/tests/invariants/test_nowcast.py @@ -7,8 +7,8 @@ import numpy as np import pytest -from pynns import nns_nowcast_panel, nns_var -from pynns.providers import CsvNowcastProvider +from nns import nns_nowcast_panel, nns_var +from nns.providers import CsvNowcastProvider def _panel() -> np.ndarray: diff --git a/tests/invariants/test_part.py b/tests/invariants/test_part.py index 0fa9b900..9763ddb8 100644 --- a/tests/invariants/test_part.py +++ b/tests/invariants/test_part.py @@ -3,7 +3,7 @@ import numpy as np import pytest -from pynns import nns_part +from nns import nns_part def test_nns_part_return_shape_and_quadrant_lengths() -> None: diff --git a/tests/invariants/test_pm_matrix.py b/tests/invariants/test_pm_matrix.py index 65355ff3..1f6b062e 100644 --- a/tests/invariants/test_pm_matrix.py +++ b/tests/invariants/test_pm_matrix.py @@ -4,7 +4,7 @@ import pytest from _tolerances import EXACT -from pynns import pm_matrix +from nns import pm_matrix def test_pm_matrix_reconstructs_cov_matrix() -> None: diff --git a/tests/invariants/test_regression.py b/tests/invariants/test_regression.py index 2995b944..5b7e5587 100644 --- a/tests/invariants/test_regression.py +++ b/tests/invariants/test_regression.py @@ -5,8 +5,8 @@ import numpy as np import pytest -from pynns import nns_m_reg, nns_reg, prepare_factor_predictors -from pynns.regression import _coefficients +from nns import nns_m_reg, nns_reg, prepare_factor_predictors +from nns.regression import _coefficients def test_nns_reg_shapes_and_bounds() -> None: diff --git a/tests/invariants/test_regression_helpers.py b/tests/invariants/test_regression_helpers.py index 178aa2cb..6640df11 100644 --- a/tests/invariants/test_regression_helpers.py +++ b/tests/invariants/test_regression_helpers.py @@ -3,8 +3,8 @@ import numpy as np import pytest -from pynns import lpm_var, nns_mode, nns_rescale, upm_var -from pynns._helpers import _fast_lm, _is_fcl +from nns import lpm_var, nns_mode, nns_rescale, upm_var +from nns._helpers import _fast_lm, _is_fcl @pytest.mark.invariant diff --git a/tests/invariants/test_sampling.py b/tests/invariants/test_sampling.py index e5ebe502..88ff4c51 100644 --- a/tests/invariants/test_sampling.py +++ b/tests/invariants/test_sampling.py @@ -2,7 +2,7 @@ import numpy as np -from pynns.categorical import _balance_class_training, _down_sample_rows, _up_sample_rows +from nns.categorical import _balance_class_training, _down_sample_rows, _up_sample_rows def test_down_and_up_sample_match_r_class_counts_and_grouping() -> None: diff --git a/tests/invariants/test_sd_cluster.py b/tests/invariants/test_sd_cluster.py index 1bc2c7ef..a555b630 100644 --- a/tests/invariants/test_sd_cluster.py +++ b/tests/invariants/test_sd_cluster.py @@ -3,7 +3,7 @@ import numpy as np import pytest -from pynns import nns_sd_cluster +from nns import nns_sd_cluster def test_nns_sd_cluster_covers_columns_once_and_is_deterministic() -> None: diff --git a/tests/invariants/test_seasonality.py b/tests/invariants/test_seasonality.py index 06a04de3..24ea967e 100644 --- a/tests/invariants/test_seasonality.py +++ b/tests/invariants/test_seasonality.py @@ -4,7 +4,7 @@ import numpy as np -from pynns import nns_seas +from nns import nns_seas def test_nns_seas_shapes_and_period_bounds() -> None: diff --git a/tests/invariants/test_stack.py b/tests/invariants/test_stack.py index 88673f5e..3f340ec7 100644 --- a/tests/invariants/test_stack.py +++ b/tests/invariants/test_stack.py @@ -5,8 +5,8 @@ import numpy as np import pytest -from pynns import nns_stack -from pynns.stack import ( +from nns import nns_stack +from nns.stack import ( _cv_split, _distance_bulk_prediction, _distance_path_predictions, diff --git a/tests/invariants/test_stochastic_dominance.py b/tests/invariants/test_stochastic_dominance.py index d7ebf774..3986f8d8 100644 --- a/tests/invariants/test_stochastic_dominance.py +++ b/tests/invariants/test_stochastic_dominance.py @@ -2,7 +2,7 @@ import numpy as np -from pynns import fsd, ssd, tsd +from nns import fsd, ssd, tsd def test_sd_antisymmetry() -> None: diff --git a/tests/invariants/test_stochastic_dominance_prefix_pairs.py b/tests/invariants/test_stochastic_dominance_prefix_pairs.py index 10252742..265e011d 100644 --- a/tests/invariants/test_stochastic_dominance_prefix_pairs.py +++ b/tests/invariants/test_stochastic_dominance_prefix_pairs.py @@ -3,8 +3,8 @@ import numpy as np import pytest -from pynns import nns_sd_cluster, sd_efficient_set -from pynns import stochastic_dominance as sd +from nns import nns_sd_cluster, sd_efficient_set +from nns import stochastic_dominance as sd @pytest.mark.parametrize( diff --git a/tests/invariants/test_stochastic_superiority.py b/tests/invariants/test_stochastic_superiority.py index 91cf3b1a..ac2b273a 100644 --- a/tests/invariants/test_stochastic_superiority.py +++ b/tests/invariants/test_stochastic_superiority.py @@ -3,7 +3,7 @@ import numpy as np import pytest -from pynns import nns_ss +from nns import nns_ss @pytest.mark.stochastic diff --git a/tests/invariants/test_var.py b/tests/invariants/test_var.py index 53dcd28c..77c16b79 100644 --- a/tests/invariants/test_var.py +++ b/tests/invariants/test_var.py @@ -4,7 +4,7 @@ import numpy as np -from pynns.var import _var_interpolate_and_extrapolate, _var_multivariate_stack_stage +from nns.var import _var_interpolate_and_extrapolate, _var_multivariate_stack_stage def test_var_interpolate_and_extrapolate_shape_and_names() -> None: diff --git a/tests/parity/test_anova.py b/tests/parity/test_anova.py index 323fa423..4a386a94 100644 --- a/tests/parity/test_anova.py +++ b/tests/parity/test_anova.py @@ -4,7 +4,7 @@ import pytest from _r import RValue, nns_anova_custom -from pynns import nns_anova +from nns import nns_anova ANOVA_PARITY = 3e-5 SIZES = [30, 100, 500] diff --git a/tests/parity/test_arma.py b/tests/parity/test_arma.py index b73d16d5..074cc4a6 100644 --- a/tests/parity/test_arma.py +++ b/tests/parity/test_arma.py @@ -7,7 +7,7 @@ from _r import RValue, nns, nns_arma_optim_custom, nns_arma_pred_int from _tolerances import COMPOUND -from pynns import nns_arma, nns_arma_optim +from nns import nns_arma, nns_arma_optim @pytest.mark.parity diff --git a/tests/parity/test_boost.py b/tests/parity/test_boost.py index 18a7f48a..2dc855ce 100644 --- a/tests/parity/test_boost.py +++ b/tests/parity/test_boost.py @@ -7,8 +7,8 @@ from _r import nns_boost_factor_predictor, nns_boost_multi_factor_predictor, nns_boost_numeric from _tolerances import COMPOUND -from pynns import nns_boost -from pynns.boost import _accuracy, _all_feature_sets, _learner_scores, _sse +from nns import nns_boost +from nns.boost import _accuracy, _all_feature_sets, _learner_scores, _sse @pytest.mark.parity diff --git a/tests/parity/test_categorical.py b/tests/parity/test_categorical.py index 0636aac9..f234d2d3 100644 --- a/tests/parity/test_categorical.py +++ b/tests/parity/test_categorical.py @@ -5,7 +5,7 @@ from _r import factor_dummy_custom from _tolerances import EXACT -from pynns import encode_factor_codes, factor_2_dummy, factor_2_dummy_fr +from nns import encode_factor_codes, factor_2_dummy, factor_2_dummy_fr @pytest.mark.parity diff --git a/tests/parity/test_causation.py b/tests/parity/test_causation.py index 256bc3b5..30ce4949 100644 --- a/tests/parity/test_causation.py +++ b/tests/parity/test_causation.py @@ -5,7 +5,7 @@ from _r import nns from _tolerances import EXACT -from pynns import causal_matrix, nns_causation +from nns import causal_matrix, nns_causation SIZES = [50, 200, 1000] RELATIONSHIPS = ["linear", "independent", "quadratic", "sin", "asymmetric"] diff --git a/tests/parity/test_cdf.py b/tests/parity/test_cdf.py index ee40a0e5..35f5ac88 100644 --- a/tests/parity/test_cdf.py +++ b/tests/parity/test_cdf.py @@ -7,7 +7,7 @@ from _r import nns_cdf_custom from _tolerances import COMPOUND, EXACT -from pynns import nns_cdf +from nns import nns_cdf @pytest.mark.parity diff --git a/tests/parity/test_classical.py b/tests/parity/test_classical.py index 583f04de..327f46fc 100644 --- a/tests/parity/test_classical.py +++ b/tests/parity/test_classical.py @@ -7,7 +7,7 @@ from _r import nns from _tolerances import EXACT -from pynns import kurt_pm, mean_pm, nns_moments, skew_pm, var_pm +from nns import kurt_pm, mean_pm, nns_moments, skew_pm, var_pm @pytest.mark.parity diff --git a/tests/parity/test_co_moments.py b/tests/parity/test_co_moments.py index b95e5ebb..75f5576b 100644 --- a/tests/parity/test_co_moments.py +++ b/tests/parity/test_co_moments.py @@ -9,7 +9,7 @@ from conftest import EdgeCase from numpy.typing import NDArray -from pynns import co_lpm, co_upm, d_lpm, d_upm +from nns import co_lpm, co_upm, d_lpm, d_upm DEGREES = [0.0, 0.5, 1.0, 2.0, 3.0] RHO_VALUES = [-0.7, 0.0, 0.7] diff --git a/tests/parity/test_copula.py b/tests/parity/test_copula.py index 872487c4..20db59b9 100644 --- a/tests/parity/test_copula.py +++ b/tests/parity/test_copula.py @@ -5,7 +5,7 @@ from _r import nns from _tolerances import EXACT -from pynns import nns_copula +from nns import nns_copula SIZES = [50, 200, 1000] RELATIONSHIPS = ["correlated_normal", "independent", "anti_monotonic"] diff --git a/tests/parity/test_core.py b/tests/parity/test_core.py index 7e139b82..ec0455fe 100644 --- a/tests/parity/test_core.py +++ b/tests/parity/test_core.py @@ -11,7 +11,7 @@ from conftest import EdgeCase from numpy.typing import NDArray -from pynns import lpm, lpm_ratio, upm +from nns import lpm, lpm_ratio, upm DEGREES = [0.0, 0.5, 1.0, 2.0, 3.0] SIZES = [10, 100, 1000] @@ -123,7 +123,10 @@ def test_edge_cases_match_r_category( return if not np.all(np.isfinite(edge_case.values)) and ( - os.environ.get("PYNNS_OFFLINE") == "1" or os.environ.get("PYNNS_R_CACHE_ONLY") == "1" + os.environ.get("NNS_OFFLINE") == "1" + or os.environ.get("PYNNS_OFFLINE") == "1" + or os.environ.get("NNS_R_CACHE_ONLY") == "1" + or os.environ.get("PYNNS_R_CACHE_ONLY") == "1" ): result = function(degree, target, edge_case.values) if edge_case.name == "contains-nan": diff --git a/tests/parity/test_dependence.py b/tests/parity/test_dependence.py index 088ae678..e2d6813a 100644 --- a/tests/parity/test_dependence.py +++ b/tests/parity/test_dependence.py @@ -5,7 +5,7 @@ from _r import nns from _tolerances import EXACT -from pynns import nns_cor, nns_dep +from nns import nns_cor, nns_dep SIZES = [50, 200, 1000] RELATIONSHIPS = ["linear", "independent", "quadratic", "sin", "noise"] diff --git a/tests/parity/test_diff.py b/tests/parity/test_diff.py index 927d4dc7..4f769a19 100644 --- a/tests/parity/test_diff.py +++ b/tests/parity/test_diff.py @@ -7,7 +7,7 @@ from _r import dy_d_scalar, dy_d_scalar_mixed, dy_dx_numeric, dy_dx_overall, nns_diff_custom from _tolerances import EXACT -from pynns import dy_d, dy_dx, nns_diff +from nns import dy_d, dy_dx, nns_diff DIFF_PARITY = 1e-5 DY_D_PARITY = 1e-3 diff --git a/tests/parity/test_distance.py b/tests/parity/test_distance.py index fc0b7fd6..bf7d8235 100644 --- a/tests/parity/test_distance.py +++ b/tests/parity/test_distance.py @@ -7,7 +7,7 @@ from _r import nns, nns_distance_bulk_custom from _tolerances import EXACT -from pynns import nns_distance, nns_distance_bulk +from nns import nns_distance, nns_distance_bulk @pytest.mark.parity diff --git a/tests/parity/test_mc.py b/tests/parity/test_mc.py index af134643..357d2c75 100644 --- a/tests/parity/test_mc.py +++ b/tests/parity/test_mc.py @@ -6,8 +6,8 @@ import pytest from _r import RValue, nns_mc_grid, nns_mc_stat_summary -from pynns import nns_mc -from pynns.mc import _format_r_number, _generate_mc_rhos +from nns import nns_mc +from nns.mc import _format_r_number, _generate_mc_rhos @pytest.mark.parity diff --git a/tests/parity/test_meboot.py b/tests/parity/test_meboot.py index 5a6fe0ca..ddef9aef 100644 --- a/tests/parity/test_meboot.py +++ b/tests/parity/test_meboot.py @@ -7,7 +7,7 @@ from _r import RValue, nns_meboot_diagnostics, nns_meboot_stat_summary from _tolerances import COMPOUND -from pynns import nns_meboot +from nns import nns_meboot def _diagnostic_series() -> list[np.ndarray]: diff --git a/tests/parity/test_multivariate_regression.py b/tests/parity/test_multivariate_regression.py index 7855bc2c..ab923956 100644 --- a/tests/parity/test_multivariate_regression.py +++ b/tests/parity/test_multivariate_regression.py @@ -7,9 +7,9 @@ from _r import nns from _tolerances import COMPOUND -from pynns import nns_m_reg, nns_reg -from pynns.part import NoiseReduction -from pynns.regression import Order +from nns import nns_m_reg, nns_reg +from nns.part import NoiseReduction +from nns.regression import Order @pytest.mark.parity diff --git a/tests/parity/test_norm.py b/tests/parity/test_norm.py index fe39edc5..7acdb080 100644 --- a/tests/parity/test_norm.py +++ b/tests/parity/test_norm.py @@ -5,7 +5,7 @@ from _r import nns from _tolerances import EXACT -from pynns import nns_norm +from nns import nns_norm SIZES = [50, 200, 1000] diff --git a/tests/parity/test_original_anova.py b/tests/parity/test_original_anova.py index 9a231834..15caf68f 100644 --- a/tests/parity/test_original_anova.py +++ b/tests/parity/test_original_anova.py @@ -3,7 +3,7 @@ import numpy as np import pytest -from pynns import nns_anova +from nns import nns_anova from ._original import expected, r_vector diff --git a/tests/parity/test_original_dependence.py b/tests/parity/test_original_dependence.py index a7d2a7f5..50630505 100644 --- a/tests/parity/test_original_dependence.py +++ b/tests/parity/test_original_dependence.py @@ -3,7 +3,7 @@ import numpy as np import pytest -from pynns import nns_copula +from nns import nns_copula from ._original import expected, r_vector diff --git a/tests/parity/test_original_partial_moments.py b/tests/parity/test_original_partial_moments.py index 08cd6d9d..a6e6f7db 100644 --- a/tests/parity/test_original_partial_moments.py +++ b/tests/parity/test_original_partial_moments.py @@ -3,7 +3,7 @@ import numpy as np import pytest -from pynns import co_lpm, co_upm, d_lpm, d_upm, lpm, lpm_ratio, nns_cdf, pm_matrix, upm, upm_ratio +from nns import co_lpm, co_upm, d_lpm, d_upm, lpm, lpm_ratio, nns_cdf, pm_matrix, upm, upm_ratio from ._original import expected, r_vector diff --git a/tests/parity/test_original_partition.py b/tests/parity/test_original_partition.py index 34d73466..1d691448 100644 --- a/tests/parity/test_original_partition.py +++ b/tests/parity/test_original_partition.py @@ -3,7 +3,7 @@ import numpy as np import pytest -from pynns import nns_part +from nns import nns_part from ._original import expected, r_string_vector_assignment, r_vector diff --git a/tests/parity/test_original_stochastic.py b/tests/parity/test_original_stochastic.py index 758318f0..f57ad216 100644 --- a/tests/parity/test_original_stochastic.py +++ b/tests/parity/test_original_stochastic.py @@ -3,7 +3,7 @@ import numpy as np import pytest -from pynns import fsd, fsd_uni, sd_efficient_set, ssd, ssd_uni, tsd, tsd_uni +from nns import fsd, fsd_uni, sd_efficient_set, ssd, ssd_uni, tsd, tsd_uni from ._original import expected, r_vector diff --git a/tests/parity/test_part.py b/tests/parity/test_part.py index eedad6c9..6aab7175 100644 --- a/tests/parity/test_part.py +++ b/tests/parity/test_part.py @@ -8,8 +8,8 @@ from _r import nns from _tolerances import EXACT -from pynns import nns_part -from pynns.part import NoiseReduction +from nns import nns_part +from nns.part import NoiseReduction SIZES = [50, 200, 1000] RELATIONSHIPS = ["linear", "quadratic", "sin", "random"] diff --git a/tests/parity/test_pm_matrix.py b/tests/parity/test_pm_matrix.py index 3a224d7a..46172fba 100644 --- a/tests/parity/test_pm_matrix.py +++ b/tests/parity/test_pm_matrix.py @@ -9,7 +9,7 @@ from _r import nns from _tolerances import EXACT -from pynns import pm_matrix +from nns import pm_matrix PMTarget: TypeAlias = float | Literal["mean"] | np.ndarray | None diff --git a/tests/parity/test_practical_examples.py b/tests/parity/test_practical_examples.py index c347566b..456129c2 100644 --- a/tests/parity/test_practical_examples.py +++ b/tests/parity/test_practical_examples.py @@ -10,7 +10,7 @@ import pytest from _tolerances import COMPOUND, EXACT -from pynns import ( +from nns import ( co_lpm, co_upm, d_lpm, @@ -130,7 +130,7 @@ def test_boston_housing_factor_path_matches_installed_r_example() -> None: }, "params": { "n_best": float(actual_result["NNS.reg.n.best"]), - # R returns the winning rounded grid threshold, while PyNNS keeps the + # R returns the winning rounded grid threshold, while NNS Python keeps the # equivalent objective threshold that produced the same stack surface. "threshold": float(expected["metrics"]["params"]["threshold"]), }, @@ -211,7 +211,7 @@ def test_iris_stack_classification_vignette_predicts_holdout_class() -> None: @pytest.mark.practical @pytest.mark.xfail( reason=( - "Installed R NNS 12.1 and PyNNS balanced Iris boost remain a true " + "Installed R NNS 12.1 and NNS Python balanced Iris boost remain a true " "diagnostic parity gap; both miss the all-class-3 holdout." ), strict=True, @@ -243,7 +243,7 @@ def test_iris_boost_classification_vignette_gap_is_explicit() -> None: reason=( "Intentional ARMA weighting divergence: installed R weights numeric " "multi-lag seasonal factors using reverse steps 1:length(lags), while " - "PyNNS weights each candidate using its actual lag." + "NNS Python weights each candidate using its actual lag." ), strict=True, ) @@ -257,7 +257,7 @@ def test_sunspots_arma_example_matches_installed_r() -> None: ) # This documents the installed-R compatibility delta, not a target fix. - # PyNNS uses the actual seasonal factors when estimating lag strength; + # NNS Python uses the actual seasonal factors when estimating lag strength; # installed R uses the seasonal factor's position in the input vector. np.testing.assert_allclose(actual, _array(expected["estimates"]), atol=COMPOUND) diff --git a/tests/parity/test_public_wrappers.py b/tests/parity/test_public_wrappers.py index 5b229eb8..2a798b68 100644 --- a/tests/parity/test_public_wrappers.py +++ b/tests/parity/test_public_wrappers.py @@ -7,7 +7,7 @@ from _r import nns from _tolerances import EXACT -from pynns import co_lpm_nd, co_upm_nd, dpm_nd, nns_gravity +from nns import co_lpm_nd, co_upm_nd, dpm_nd, nns_gravity @pytest.mark.parity diff --git a/tests/parity/test_regression.py b/tests/parity/test_regression.py index 5a4f0413..dc07487b 100644 --- a/tests/parity/test_regression.py +++ b/tests/parity/test_regression.py @@ -7,9 +7,9 @@ from _r import nns, nns_reg_factor_dimred, nns_reg_factor_predictor from _tolerances import COMPOUND -from pynns import nns_reg -from pynns.part import NoiseReduction -from pynns.regression import Order +from nns import nns_reg +from nns.part import NoiseReduction +from nns.regression import Order SIZES = [50, 200, 1000] RELATIONSHIPS = ["linear", "quadratic", "sin", "random"] diff --git a/tests/parity/test_regression_helpers.py b/tests/parity/test_regression_helpers.py index 6b3eec0f..3a0eaba2 100644 --- a/tests/parity/test_regression_helpers.py +++ b/tests/parity/test_regression_helpers.py @@ -5,7 +5,7 @@ from _r import nns from _tolerances import EXACT, STOCHASTIC -from pynns import lpm_var, nns_mode, nns_rescale, upm_var +from nns import lpm_var, nns_mode, nns_rescale, upm_var MODE_CASES = [ np.array([1.0, 2.0, 2.0, 3.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0]), diff --git a/tests/parity/test_sd_cluster.py b/tests/parity/test_sd_cluster.py index 31679120..a9bae63d 100644 --- a/tests/parity/test_sd_cluster.py +++ b/tests/parity/test_sd_cluster.py @@ -4,7 +4,7 @@ import pytest from _r import RValue, nns, nns_sd_cluster_dendrogram -from pynns import nns_sd_cluster +from nns import nns_sd_cluster @pytest.mark.parity diff --git a/tests/parity/test_seasonality.py b/tests/parity/test_seasonality.py index e4a85537..753b9532 100644 --- a/tests/parity/test_seasonality.py +++ b/tests/parity/test_seasonality.py @@ -7,7 +7,7 @@ from _r import nns from _tolerances import COMPOUND -from pynns import nns_seas +from nns import nns_seas @pytest.mark.parity diff --git a/tests/parity/test_stack.py b/tests/parity/test_stack.py index 6c149a7f..d09115b4 100644 --- a/tests/parity/test_stack.py +++ b/tests/parity/test_stack.py @@ -7,7 +7,7 @@ from _r import nns_stack_factor_predictor, nns_stack_mixed_factor_predictor, nns_stack_numeric from _tolerances import COMPOUND -from pynns import nns_stack +from nns import nns_stack @pytest.mark.parity diff --git a/tests/parity/test_stochastic_dominance.py b/tests/parity/test_stochastic_dominance.py index e6b16cc7..408cf306 100644 --- a/tests/parity/test_stochastic_dominance.py +++ b/tests/parity/test_stochastic_dominance.py @@ -6,7 +6,7 @@ import pytest from _r import RValue, nns -from pynns import fsd, fsd_uni, sd_efficient_set, ssd, ssd_uni, tsd, tsd_uni +from nns import fsd, fsd_uni, sd_efficient_set, ssd, ssd_uni, tsd, tsd_uni SIZES = [50, 200, 1000] diff --git a/tests/parity/test_stochastic_superiority.py b/tests/parity/test_stochastic_superiority.py index 04e97a15..c5cb4976 100644 --- a/tests/parity/test_stochastic_superiority.py +++ b/tests/parity/test_stochastic_superiority.py @@ -7,7 +7,7 @@ from _r import nns from _tolerances import EXACT -from pynns import nns_ss +from nns import nns_ss @pytest.mark.parity diff --git a/tests/parity/test_var.py b/tests/parity/test_var.py index 5223f2ea..a46a7173 100644 --- a/tests/parity/test_var.py +++ b/tests/parity/test_var.py @@ -6,7 +6,7 @@ import pytest from _r import nns -from pynns.var import ( +from nns.var import ( _lag_mtx, _var_interpolate_and_extrapolate, _var_multivariate_stack_stage, diff --git a/tests/property/test_anova.py b/tests/property/test_anova.py index 64d77c67..ba052ed9 100644 --- a/tests/property/test_anova.py +++ b/tests/property/test_anova.py @@ -6,7 +6,7 @@ from hypothesis import strategies as st from hypothesis.extra.numpy import arrays -from pynns import nns_anova +from nns import nns_anova MIN_MEANINGFUL_RANGE = np.finfo(np.float64).tiny diff --git a/tests/property/test_arma.py b/tests/property/test_arma.py index 95fd1524..24a99562 100644 --- a/tests/property/test_arma.py +++ b/tests/property/test_arma.py @@ -6,8 +6,8 @@ from hypothesis import strategies as st from hypothesis.extra.numpy import arrays -from pynns import nns_arma -from pynns.arma import _numeric_seasonal_weights +from nns import nns_arma +from nns.arma import _numeric_seasonal_weights finite_arrays = arrays( dtype=np.float64, diff --git a/tests/property/test_boost.py b/tests/property/test_boost.py index a7e22bb7..9b3fe37c 100644 --- a/tests/property/test_boost.py +++ b/tests/property/test_boost.py @@ -6,7 +6,7 @@ from hypothesis import strategies as st from hypothesis.extra.numpy import arrays -from pynns import nns_boost +from nns import nns_boost finite_matrices = arrays( dtype=np.float64, diff --git a/tests/property/test_causation.py b/tests/property/test_causation.py index bc0ded8a..6fff4e83 100644 --- a/tests/property/test_causation.py +++ b/tests/property/test_causation.py @@ -5,7 +5,7 @@ from hypothesis import strategies as st from hypothesis.extra.numpy import arrays -from pynns import nns_causation +from nns import nns_causation finite_arrays = arrays( dtype=np.float64, diff --git a/tests/property/test_cdf.py b/tests/property/test_cdf.py index 9707f8d2..053e8aaf 100644 --- a/tests/property/test_cdf.py +++ b/tests/property/test_cdf.py @@ -7,7 +7,7 @@ from hypothesis import strategies as st from hypothesis.extra.numpy import arrays -from pynns import nns_cdf +from nns import nns_cdf finite_floats = st.floats(min_value=-50, max_value=50, allow_nan=False, allow_infinity=False) diff --git a/tests/property/test_classical.py b/tests/property/test_classical.py index 814db257..7080549f 100644 --- a/tests/property/test_classical.py +++ b/tests/property/test_classical.py @@ -6,7 +6,7 @@ from hypothesis import strategies as st from hypothesis.extra.numpy import arrays -from pynns import ecdf_pm, kurt_pm, mean_pm, skew_pm, var_pm +from nns import ecdf_pm, kurt_pm, mean_pm, skew_pm, var_pm @given( diff --git a/tests/property/test_co_moments.py b/tests/property/test_co_moments.py index e1811a6d..89d4214e 100644 --- a/tests/property/test_co_moments.py +++ b/tests/property/test_co_moments.py @@ -5,7 +5,7 @@ from hypothesis import given from hypothesis import strategies as st -from pynns import co_lpm, co_upm, d_lpm, d_upm +from nns import co_lpm, co_upm, d_lpm, d_upm finite_values = st.lists( st.floats( diff --git a/tests/property/test_copula.py b/tests/property/test_copula.py index 9190930a..aac04b4e 100644 --- a/tests/property/test_copula.py +++ b/tests/property/test_copula.py @@ -5,7 +5,7 @@ from hypothesis import strategies as st from hypothesis.extra.numpy import arrays -from pynns import nns_copula +from nns import nns_copula finite_arrays = arrays( dtype=np.float64, diff --git a/tests/property/test_core.py b/tests/property/test_core.py index 75fc7517..8f55099e 100644 --- a/tests/property/test_core.py +++ b/tests/property/test_core.py @@ -6,7 +6,7 @@ from hypothesis import strategies as st from hypothesis.extra.numpy import arrays -from pynns import lpm, upm +from nns import lpm, upm finite_arrays = arrays( dtype=np.float64, diff --git a/tests/property/test_dependence.py b/tests/property/test_dependence.py index 1e545429..60a32e19 100644 --- a/tests/property/test_dependence.py +++ b/tests/property/test_dependence.py @@ -5,7 +5,7 @@ from hypothesis import strategies as st from hypothesis.extra.numpy import arrays -from pynns import nns_dep +from nns import nns_dep finite_arrays = arrays( dtype=np.float64, diff --git a/tests/property/test_diff.py b/tests/property/test_diff.py index 7183439d..7edff31c 100644 --- a/tests/property/test_diff.py +++ b/tests/property/test_diff.py @@ -4,7 +4,7 @@ from hypothesis import given from hypothesis import strategies as st -from pynns import nns_diff +from nns import nns_diff @given(st.floats(min_value=-10.0, max_value=10.0, allow_nan=False, allow_infinity=False)) diff --git a/tests/property/test_distance.py b/tests/property/test_distance.py index dcd8ac2e..972b26d3 100644 --- a/tests/property/test_distance.py +++ b/tests/property/test_distance.py @@ -5,7 +5,7 @@ from hypothesis import strategies as st from hypothesis.extra.numpy import arrays -from pynns import nns_distance, nns_distance_bulk +from nns import nns_distance, nns_distance_bulk feature_matrices = arrays( dtype=np.float64, diff --git a/tests/property/test_mc.py b/tests/property/test_mc.py index 8ea8a028..c093bee9 100644 --- a/tests/property/test_mc.py +++ b/tests/property/test_mc.py @@ -6,7 +6,7 @@ from hypothesis import strategies as st from hypothesis.extra.numpy import arrays -from pynns import nns_mc +from nns import nns_mc pytestmark = pytest.mark.stochastic diff --git a/tests/property/test_meboot.py b/tests/property/test_meboot.py index 1bf36cb1..8955504f 100644 --- a/tests/property/test_meboot.py +++ b/tests/property/test_meboot.py @@ -6,7 +6,7 @@ from hypothesis import strategies as st from hypothesis.extra.numpy import arrays -from pynns import nns_meboot +from nns import nns_meboot pytestmark = pytest.mark.stochastic diff --git a/tests/property/test_multivariate_regression.py b/tests/property/test_multivariate_regression.py index f2c4dff7..b239b387 100644 --- a/tests/property/test_multivariate_regression.py +++ b/tests/property/test_multivariate_regression.py @@ -7,9 +7,9 @@ from hypothesis import strategies as st from hypothesis.extra.numpy import arrays -from pynns import nns_m_reg -from pynns.part import NoiseReduction -from pynns.regression import Order +from nns import nns_m_reg +from nns.part import NoiseReduction +from nns.regression import Order matrix_arrays = arrays( dtype=np.float64, diff --git a/tests/property/test_norm.py b/tests/property/test_norm.py index 12fa0cfb..f4f99ab7 100644 --- a/tests/property/test_norm.py +++ b/tests/property/test_norm.py @@ -5,7 +5,7 @@ from hypothesis import strategies as st from hypothesis.extra.numpy import arrays -from pynns import nns_norm +from nns import nns_norm finite_matrices = arrays( dtype=np.float64, diff --git a/tests/property/test_part.py b/tests/property/test_part.py index 3c815998..eb4d170b 100644 --- a/tests/property/test_part.py +++ b/tests/property/test_part.py @@ -7,8 +7,8 @@ from hypothesis import strategies as st from hypothesis.extra.numpy import arrays -from pynns import nns_part -from pynns.part import NoiseReduction +from nns import nns_part +from nns.part import NoiseReduction finite_arrays = arrays( dtype=np.float64, diff --git a/tests/property/test_pm_matrix.py b/tests/property/test_pm_matrix.py index 2f14fbfb..40ff0e93 100644 --- a/tests/property/test_pm_matrix.py +++ b/tests/property/test_pm_matrix.py @@ -5,7 +5,7 @@ from hypothesis import strategies as st from hypothesis.extra.numpy import arrays -from pynns import pm_matrix +from nns import pm_matrix @given( diff --git a/tests/property/test_regression.py b/tests/property/test_regression.py index 447b0098..62c7f7a8 100644 --- a/tests/property/test_regression.py +++ b/tests/property/test_regression.py @@ -7,9 +7,9 @@ from hypothesis import strategies as st from hypothesis.extra.numpy import arrays -from pynns import nns_reg -from pynns.part import NoiseReduction -from pynns.regression import Order +from nns import nns_reg +from nns.part import NoiseReduction +from nns.regression import Order finite_arrays = arrays( dtype=np.float64, diff --git a/tests/property/test_regression_helpers.py b/tests/property/test_regression_helpers.py index a8441ba0..0fe96d6c 100644 --- a/tests/property/test_regression_helpers.py +++ b/tests/property/test_regression_helpers.py @@ -5,7 +5,7 @@ from hypothesis import strategies as st from hypothesis.extra.numpy import arrays -from pynns import lpm_var, nns_mode, nns_rescale, upm_var +from nns import lpm_var, nns_mode, nns_rescale, upm_var finite_arrays = arrays( dtype=np.float64, diff --git a/tests/property/test_sd_cluster.py b/tests/property/test_sd_cluster.py index 722af881..e89948d5 100644 --- a/tests/property/test_sd_cluster.py +++ b/tests/property/test_sd_cluster.py @@ -5,7 +5,7 @@ from hypothesis import strategies as st from hypothesis.extra.numpy import arrays -from pynns import nns_sd_cluster +from nns import nns_sd_cluster @given( diff --git a/tests/property/test_seasonality.py b/tests/property/test_seasonality.py index 435a0e98..d6fe23b9 100644 --- a/tests/property/test_seasonality.py +++ b/tests/property/test_seasonality.py @@ -7,7 +7,7 @@ from hypothesis import strategies as st from hypothesis.extra.numpy import arrays -from pynns import nns_seas +from nns import nns_seas finite_arrays = arrays( dtype=np.float64, diff --git a/tests/property/test_stack.py b/tests/property/test_stack.py index c531b156..6e4257ca 100644 --- a/tests/property/test_stack.py +++ b/tests/property/test_stack.py @@ -6,7 +6,7 @@ from hypothesis import strategies as st from hypothesis.extra.numpy import arrays -from pynns import nns_stack +from nns import nns_stack finite_matrices = arrays( dtype=np.float64, diff --git a/tests/property/test_stochastic_dominance.py b/tests/property/test_stochastic_dominance.py index 5c9f0366..7d194180 100644 --- a/tests/property/test_stochastic_dominance.py +++ b/tests/property/test_stochastic_dominance.py @@ -5,7 +5,7 @@ from hypothesis import strategies as st from hypothesis.extra.numpy import arrays -from pynns import fsd, ssd, tsd +from nns import fsd, ssd, tsd finite_arrays = arrays( dtype=np.float64, diff --git a/tests/property/test_stochastic_superiority.py b/tests/property/test_stochastic_superiority.py index 1c75bdd8..9ed2690f 100644 --- a/tests/property/test_stochastic_superiority.py +++ b/tests/property/test_stochastic_superiority.py @@ -5,7 +5,7 @@ from hypothesis import strategies as st from hypothesis.extra.numpy import arrays -from pynns import nns_ss +from nns import nns_ss @given( diff --git a/uv.lock b/uv.lock index 3f97bfc8..9424fa43 100644 --- a/uv.lock +++ b/uv.lock @@ -328,8 +328,8 @@ wheels = [ ] [[package]] -name = "nns-pm" -version = "0.2.0" +name = "nns" +version = "1.0.0a0" source = { editable = "." } dependencies = [ { name = "numpy" },