diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
new file mode 100644
index 00000000..9c2333ea
--- /dev/null
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,60 @@
+name: Docs
+
+on:
+ push:
+ branches: [main]
+ paths:
+ - "docs/**"
+ - "mkdocs.yml"
+ - ".github/workflows/docs.yml"
+ workflow_dispatch:
+
+# Allow only one concurrent deployment, and let in-progress runs finish.
+concurrency:
+ group: pages
+ cancel-in-progress: false
+
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+jobs:
+ build:
+ name: Build site
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out repo
+ uses: actions/checkout@v4
+
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.12"
+
+ - name: Install MkDocs
+ run: |
+ python -m pip install -U pip
+ python -m pip install \
+ "mkdocs-material>=9.5" \
+ "pymdown-extensions>=10"
+
+ - name: Build (strict)
+ run: mkdocs build --strict
+
+ - name: Upload Pages artifact
+ uses: actions/upload-pages-artifact@v3
+ with:
+ path: site
+
+ deploy:
+ name: Deploy to GitHub Pages
+ needs: build
+ runs-on: ubuntu-latest
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ steps:
+ - name: Deploy
+ id: deployment
+ uses: actions/deploy-pages@v4
diff --git a/.gitignore b/.gitignore
index 7c38440c..8cd4fa79 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,3 +23,6 @@ tools/NNS/src/*.o
# Stray R plotting artifact from headless regeneration
Rplots.pdf
+
+# MkDocs build output
+/site/
diff --git a/README.md b/README.md
index 5a179925..8e18db15 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,7 @@
[](https://pypi.org/project/ovvo-nns/)
[](https://www.python.org/)
+[](https://ovvo-financial.github.io/NNS-python/)
[](LICENSE)
`ovvo-nns` brings Nonlinear Nonparametric Statistics to Python as the `nns` import package. It is a parity-focused port of the R `NNS` 13.0+ package, designed for real-world data that violate symmetry, linearity, or distributional assumptions.
@@ -178,6 +179,9 @@ uv run python examples/run_all_vignettes.py
## Documentation
+The full documentation site is hosted at
+****.
+
- [API reference manual](docs/api_reference.md)
- [API status and known gaps](docs/api_status.md)
- [Behavior conventions and intentional divergences](docs/conventions.md)
diff --git a/docs/assets/nns_hex_sticker.png b/docs/assets/nns_hex_sticker.png
new file mode 100644
index 00000000..a0271fb3
Binary files /dev/null and b/docs/assets/nns_hex_sticker.png differ
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 00000000..43cf29f2
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,119 @@
+
+
+[](https://pypi.org/project/ovvo-nns/)
+[](https://www.python.org/)
+[](https://github.com/OVVO-Financial/NNS-python/blob/main/LICENSE)
+
+# NNS Python
+
+`ovvo-nns` brings **Nonlinear Nonparametric Statistics** to Python as the `nns`
+import package. It is a parity-focused port of the R `NNS` 13.0+ package,
+designed for real-world data that violate symmetry, linearity, or distributional
+assumptions.
+
+NNS is built around partial moments — the lower and upper components of variance —
+and uses them across nonlinear dependence, correlation, causation, regression,
+classification, forecasting, stochastic dominance, stochastic superiority, Monte
+Carlo simulation, and numerical differentiation workflows.
+
+!!! note "Origin"
+ NNS was created by Fred Viole as the companion R package to Viole, F. and
+ Nawrocki, D. (2013), *Nonlinear Nonparametric Statistics: Using Partial
+ Moments*. **Book (2nd Edition):** .
+ For a direct quantitative finance implementation of NNS, see
+ [OVVO Labs](https://www.ovvolabs.com).
+
+## Package at a glance
+
+| Item | Value |
+|---|---|
+| Distribution package | `ovvo-nns` |
+| Import package | `nns` |
+| Python | `>=3.11` |
+| Required runtime dependencies | NumPy, SciPy |
+| R required at runtime | No |
+| Native acceleration | Private, optional `nns._nnscore` kernels where available |
+| Public API status | Stable, parity-focused |
+| License | GPL-3.0-only |
+
+The public package is Python-native and does not call R at runtime. Some core
+kernels can use the private `_nnscore` extension when it is present, while public
+functions keep Python implementations and explicit fallback behavior.
+
+## Get started
+
+
+
+- :material-download: **[Install](install.md)**
+
+ `pip install ovvo-nns`, then `import nns`.
+
+- :material-rocket-launch: **[Quick start](quick_start.md)**
+
+ Partial moments, dependence, regression, and forecasting in a few lines.
+
+- :material-book-open-variant: **[API reference](api_reference.md)**
+
+ Function-by-function index with R `NNS` name crosswalks.
+
+- :material-check-decagram: **[API status](api_status.md)**
+
+ Implemented, partial, guarded, and known-gap paths.
+
+
+
+## Main API areas
+
+| Area | Representative functions |
+|---|---|
+| Partial moments | `lpm`, `upm`, `lpm_ratio`, `upm_ratio`, `pm_matrix` |
+| Classical moment helpers | `mean_pm`, `var_pm`, `skew_pm`, `kurt_pm`, `nns_moments` |
+| Dependence, correlation, copula | `nns_dep`, `nns_cor`, `nns_copula` |
+| Causation | `nns_causation`, `causal_matrix` |
+| Regression and classification | `nns_reg`, `nns_m_reg`, `nns_stack`, `nns_boost` |
+| Forecasting | `nns_seas`, `nns_arma`, `nns_arma_optim`, `nns_var` |
+| Distribution tools | `nns_cdf`, `nns_anova`, `nns_norm` |
+| Stochastic dominance | `fsd`, `ssd`, `tsd`, `nns_sd_cluster`, `sd_efficient_set` |
+| Stochastic superiority and simulation | `nns_ss`, `nns_mc`, `nns_meboot` |
+| Differentiation | `nns_diff`, `dy_dx`, `dy_d` |
+| Categorical helpers | `encode_factor_codes`, `factor_2_dummy`, `factor_2_dummy_fr`, `prepare_factor_predictors` |
+
+See [API status](api_status.md) for implemented, partial, guarded, and known-gap
+paths.
+
+## Design boundaries
+
+NNS Python prioritizes stable public behavior from installed R NNS 13.0+, not
+private helper parity. The package returns NumPy arrays and plain dictionaries
+rather than R `data.table` objects, uses explicit Python errors for several
+unsafe R coercions, and generally ignores plotting side effects.
+
+See [behavior conventions](conventions.md) for detailed compatibility notes and
+[parity with R NNS](parity.md) for the parity target and automation.
+
+## Links
+
+- [View on PyPI](https://pypi.org/project/ovvo-nns/)
+- [Browse source code](https://github.com/OVVO-Financial/NNS-python)
+- [Report a bug](https://github.com/OVVO-Financial/NNS-python/issues)
+
+## License
+
+[GPL-3.0-only](https://www.gnu.org/licenses/gpl-3.0.en.html)
+
+## Citation
+
+NNS is the companion to Viole, F. and Nawrocki, D. (2013),
+*Nonlinear Nonparametric Statistics: Using Partial Moments* (ISBN: 1490523995).
+2nd edition: .
+
+## Developers
+
+- **Fred Viole** — author and maintainer
+- **Roberto Spadim** — contributor
+- **Rasheed Khoshnaw** — contributor
+
+## Attribution
+
+Upstream R package and reference implementation:
+[OVVO-Financial/NNS](https://github.com/OVVO-Financial/NNS).
diff --git a/docs/install.md b/docs/install.md
new file mode 100644
index 00000000..e3adc7aa
--- /dev/null
+++ b/docs/install.md
@@ -0,0 +1,55 @@
+# Install
+
+## From PyPI
+
+```bash
+pip install ovvo-nns
+```
+
+The distribution package is **`ovvo-nns`**; the import package is **`nns`**:
+
+```python
+import nns
+
+print(nns.__version__)
+```
+
+Installing `ovvo-nns` includes the Matplotlib plotting API (`nns.plotting`).
+Matplotlib is a regular dependency and is imported lazily, so `import nns` stays
+light. See the [plot parity policy](plot_parity_policy.md) for details.
+
+## Requirements
+
+| Requirement | Value |
+|---|---|
+| Python | `>=3.11` (CPython 3.11, 3.12, 3.13, 3.14) |
+| Runtime dependencies | NumPy, SciPy, Matplotlib |
+| R at runtime | Not required |
+
+R is used only for parity tests and local cache regeneration, never at normal
+runtime.
+
+## Wheels vs. source builds
+
+Published wheels should be preferred when available. They ship the optional
+private native extension (`nns._nnscore`) prebuilt for supported platforms.
+
+Source builds compile the optional native extension with
+[`scikit-build-core`](https://scikit-build-core.readthedocs.io/) and
+[`nanobind`](https://nanobind.readthedocs.io/), which require a C++17 toolchain.
+Public APIs keep Python implementations and explicit fallback behavior, so the
+native extension remains a private, benchmark-backed implementation detail rather
+than a public API.
+
+## Development install
+
+```bash
+uv sync --group dev
+uv run pytest
+uv run ruff check .
+uv run mypy
+```
+
+The default parity suite is cache-backed and does not require `Rscript`.
+`Rscript` and the R `NNS` package are needed only when regenerating parity caches
+or running live R comparison scripts.
diff --git a/docs/quick_start.md b/docs/quick_start.md
new file mode 100644
index 00000000..eb44605e
--- /dev/null
+++ b/docs/quick_start.md
@@ -0,0 +1,75 @@
+# Quick start
+
+These snippets mirror the runnable
+[example vignettes](https://github.com/OVVO-Financial/NNS-python/tree/main/examples/vignettes),
+which are exercised in CI so they stay in sync with the package.
+
+## Partial moments
+
+```python
+import numpy as np
+from nns import lpm, upm
+
+x = np.array([-2.0, -1.0, 0.5, 3.0], dtype=np.float64)
+
+lower = lpm(degree=2, target=0.0, x=x)
+upper = upm(degree=2, target=0.0, x=x)
+
+print("lower partial moment:", lower)
+print("upper partial moment:", upper)
+```
+
+## Nonlinear dependence
+
+```python
+import numpy as np
+from nns import nns_cor, nns_dep
+
+grid = np.linspace(-2.0, 2.0, 80, dtype=np.float64)
+y = grid**2
+
+print("NNS dependence:", nns_dep(grid, y))
+print("NNS correlation:", nns_cor(grid, y))
+```
+
+## Nonlinear regression
+
+Fit a nonlinear regression and estimate new points:
+
+```python
+import numpy as np
+from nns import nns_reg
+
+x = np.linspace(-3.0, 3.0, 80, dtype=np.float64)
+y = np.sin(x) + 0.2 * x
+points = np.array([-1.5, 0.0, 1.5], dtype=np.float64)
+
+fit = nns_reg(x, y, point_est=points, confidence_interval=None)
+
+print("R2:", fit["R2"])
+print(np.column_stack((points, fit["Point.est"])))
+```
+
+## Forecasting
+
+Forecast a univariate series:
+
+```python
+import numpy as np
+from nns import nns_arma, nns_seas
+
+t = np.arange(1, 60, dtype=np.float64)
+series = 10.0 + np.sin(t / 3.0) + 0.05 * t
+
+seasonality = nns_seas(series, modulo=[3, 4, 6], mod_only=True)
+forecast = nns_arma(series, h=3, seasonal_factor=4, method="lin")
+
+print("best seasonal period:", seasonality["best.period"])
+print("forecast:", forecast)
+```
+
+## Next steps
+
+- Browse the full [API reference](api_reference.md).
+- Check the [API status](api_status.md) for partial, guarded, and known-gap paths.
+- Read the [behavior conventions](conventions.md) for intentional divergences from R.
diff --git a/docs/release_notes.md b/docs/release_notes.md
new file mode 100644
index 00000000..ec702f61
--- /dev/null
+++ b/docs/release_notes.md
@@ -0,0 +1,31 @@
+# Release notes
+
+The authoritative, per-version release history is published to:
+
+- **PyPI release history:**
+- **GitHub Releases:**
+
+The currently published version is shown by the badge on the
+[home page](index.md) and by:
+
+```python
+import nns
+
+print(nns.__version__)
+```
+
+## Versioning
+
+`ovvo-nns` uses standard semantic-style version numbers (`MAJOR.MINOR.PATCH`).
+The public API is **stable and parity-focused**: documented public behavior is
+not expected to break across minor releases. Known partial, guarded, and
+known-gap paths are tracked on the [API status](api_status.md) page, and
+intentional divergences from R `NNS` are recorded in the
+[conventions](conventions.md).
+
+Version numbers are kept consistent across `pyproject.toml`, the package
+`__version__`, and the README. This is enforced in CI by
+`scripts/check_version_consistency.py`.
+
+For how releases are built, signed, and published, see the
+[release process](releasing.md).
diff --git a/docs/stylesheets/cosmo.css b/docs/stylesheets/cosmo.css
new file mode 100644
index 00000000..480c4ccd
--- /dev/null
+++ b/docs/stylesheets/cosmo.css
@@ -0,0 +1,77 @@
+/*
+ * Cosmo-flavored theme overrides for Material for MkDocs.
+ *
+ * Goal: match the R NNS pkgdown site, which uses Bootstrap 5 with the
+ * Bootswatch "Cosmo" theme — a flat design with a blue (#2780e3) primary,
+ * a solid-blue top navbar, the Source Sans typeface, and squared corners.
+ */
+
+/* ---- Light scheme: Cosmo palette ------------------------------------- */
+[data-md-color-scheme="default"] {
+ --md-primary-fg-color: #2780e3;
+ --md-primary-fg-color--light: #5a9ee9;
+ --md-primary-fg-color--dark: #1f6ec1;
+ --md-primary-bg-color: #ffffff;
+ --md-primary-bg-color--light: rgba(255, 255, 255, 0.7);
+
+ --md-accent-fg-color: #1f6ec1;
+
+ /* Cosmo body text + links */
+ --md-typeset-color: #373a3c;
+ --md-typeset-a-color: #2780e3;
+
+ /* Cosmo semantic accents (admonitions, etc.) */
+ --md-code-hl-color: rgba(39, 128, 227, 0.15);
+}
+
+/* ---- Dark scheme: keep the Cosmo blue identity ----------------------- */
+[data-md-color-scheme="slate"] {
+ --md-primary-fg-color: #2780e3;
+ --md-primary-fg-color--light: #5a9ee9;
+ --md-primary-fg-color--dark: #1f6ec1;
+ --md-accent-fg-color: #5a9ee9;
+ --md-typeset-a-color: #5a9ee9;
+}
+
+/* ---- Flat, squared corners (Cosmo is flat) --------------------------- */
+.md-typeset pre > code,
+.md-typeset code,
+.md-typeset .highlight,
+.md-typeset .admonition,
+.md-typeset details,
+.md-typeset table:not([class]),
+.md-typeset .md-button,
+.md-search__form,
+.md-search__output {
+ border-radius: 0 !important;
+}
+
+/* Cosmo navbar feel: tighter, uppercase-ish weight on the top tabs */
+.md-tabs {
+ font-weight: 600;
+}
+
+/* Source Sans renders a touch light; nudge headings bolder like Cosmo */
+.md-typeset h1,
+.md-typeset h2,
+.md-typeset h3 {
+ font-weight: 700;
+ letter-spacing: -0.01em;
+}
+
+/* Cosmo-style primary buttons (flat, solid blue) */
+.md-typeset .md-button--primary {
+ background-color: var(--md-primary-fg-color);
+ border-color: var(--md-primary-fg-color);
+ color: #fff;
+}
+.md-typeset .md-button--primary:hover {
+ background-color: var(--md-primary-fg-color--dark);
+ border-color: var(--md-primary-fg-color--dark);
+}
+
+/* Cosmo navbar-brand: bold, slightly larger package name */
+.md-header__title {
+ font-weight: 700;
+ font-size: 1.05rem;
+}
diff --git a/mkdocs.yml b/mkdocs.yml
new file mode 100644
index 00000000..178392c8
--- /dev/null
+++ b/mkdocs.yml
@@ -0,0 +1,93 @@
+site_name: NNS Python
+site_description: Nonlinear Nonparametric Statistics for Python — a parity-focused port of R NNS.
+site_url: https://ovvo-financial.github.io/NNS-python/
+site_author: Fred Viole
+
+repo_name: OVVO-Financial/NNS-python
+repo_url: https://github.com/OVVO-Financial/NNS-python
+edit_uri: edit/main/docs/
+
+copyright: Developed by Fred Viole.
+
+theme:
+ name: material
+ # pkgdown's navbar uses a text brand ("NNS") with the hex sticker shown in
+ # the home page body, so we keep the header text-only and place the sticker
+ # on the home page instead of using it as a header logo.
+ favicon: assets/nns_hex_sticker.png
+ # Match the R NNS pkgdown site (Bootstrap 5 + Bootswatch "Cosmo"):
+ # flat design, a solid blue top navbar, and the Source Sans typeface.
+ font:
+ text: Source Sans 3
+ code: Source Code Pro
+ features:
+ - navigation.instant
+ - navigation.tracking
+ - navigation.tabs
+ - navigation.tabs.sticky
+ - navigation.top
+ - toc.follow
+ - content.code.copy
+ - content.action.edit
+ - search.suggest
+ - search.highlight
+ palette:
+ - media: "(prefers-color-scheme: light)"
+ scheme: default
+ toggle:
+ icon: material/weather-night
+ name: Switch to dark mode
+ - media: "(prefers-color-scheme: dark)"
+ scheme: slate
+ toggle:
+ icon: material/weather-sunny
+ name: Switch to light mode
+ icon:
+ repo: fontawesome/brands/github
+
+extra_css:
+ - stylesheets/cosmo.css
+
+plugins:
+ - search
+
+markdown_extensions:
+ - admonition
+ - attr_list
+ - md_in_html
+ - tables
+ - toc:
+ permalink: true
+ - pymdownx.highlight:
+ anchor_linenums: true
+ - pymdownx.inlinehilite
+ - pymdownx.snippets
+ - pymdownx.superfences
+ - pymdownx.details
+
+extra:
+ social:
+ - icon: fontawesome/brands/github
+ link: https://github.com/OVVO-Financial/NNS-python
+ - icon: fontawesome/brands/python
+ link: https://pypi.org/project/ovvo-nns/
+
+# Pages that exist in docs/ but are surfaced as sub-pages of the nav below.
+# Keep this nav aligned with README "Documentation" links.
+nav:
+ - Home: index.md
+ - Install: install.md
+ - Quick start: quick_start.md
+ - API reference: api_reference.md
+ - API status: api_status.md
+ - Conventions:
+ - Behavior conventions: conventions.md
+ - Plot parity policy: plot_parity_policy.md
+ - Parity with R NNS:
+ - Parity overview: parity.md
+ - Sync contract: sync_contract.md
+ - Sync status: sync_status.md
+ - Benchmarks: benchmarks.md
+ - Release notes:
+ - Release notes: release_notes.md
+ - Release process: releasing.md
diff --git a/pyproject.toml b/pyproject.toml
index f57dd863..518a3dbe 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -48,6 +48,10 @@ dev = [
"pytest-xdist>=3.8.0",
"ruff",
]
+docs = [
+ "mkdocs-material>=9.5",
+ "pymdown-extensions>=10",
+]
[build-system]
requires = ["scikit-build-core", "nanobind"]