Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@ jobs:
run: npm ci
- name: Sync Python dependencies
run: uv sync --project services/analysis-engine --group dev --frozen
- name: Install stable Rust toolchain
run: rustup toolchain install stable --profile minimal
- name: Build and install Rust numeric extension
run: |
VENV_PY="$PWD/services/analysis-engine/.venv/bin/python"
uvx maturin@1.9.6 build --release \
--manifest-path services/analysis-engine/rust/Cargo.toml \
--interpreter "$VENV_PY" \
--out services/analysis-engine/rust/dist
uv pip install --python "$VENV_PY" services/analysis-engine/rust/dist/*.whl
- name: Run quickcheck
run: ./scripts/harness/quickcheck.sh

Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ __pycache__/
.mypy_cache/
.ruff_cache/
apps/desktop/src-tauri/target/
services/analysis-engine/rust/target/
/target/
*.pyc
*.pyo
Expand Down
6 changes: 6 additions & 0 deletions services/analysis-engine/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ python_version = "3.12"
strict = true
files = ["src"]

# The optional Rust extension ships no type stubs / py.typed marker, and may be
# absent entirely (the engine falls back to the Python reference kernels).
[[tool.mypy.overrides]]
module = ["bandscope_numeric"]
ignore_missing_imports = true

[tool.ruff]
line-length = 100

Expand Down
229 changes: 229 additions & 0 deletions services/analysis-engine/rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions services/analysis-engine/rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[package]
name = "bandscope_numeric"
version = "0.1.0"
edition = "2021"
description = "Rust numeric kernels for the BandScope analysis engine (checkerboard SSM novelty + Viterbi chord decoding)."
license = "MIT"
repository = "https://github.com/ContextualWisdomLab/bandscope"

[workspace]

[lib]
name = "bandscope_numeric"
crate-type = ["cdylib"]

# Permissive-licensed dependencies only (Apache-2.0 / MIT). No GPL/AGPL.
[dependencies]
pyo3 = { version = "0.29", features = ["extension-module"] }
numpy = "0.29"
ndarray = "0.16"

[profile.release]
opt-level = 3
lto = true
codegen-units = 1
35 changes: 35 additions & 0 deletions services/analysis-engine/rust/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# bandscope_numeric

Rust (PyO3/maturin) extension holding the numeric-heavy kernels of the BandScope
analysis engine. Per the project rule that software with both Python and Rust
does its numeric/mathematical computation on the Rust side, the two hottest
pure-numeric kernels were ported from Python to Rust:

| Kernel | Rust function | Python reference (parity oracle) |
| --- | --- | --- |
| Checkerboard SSM novelty (structural segmentation) | `checkerboard_novelty` | `sections/segmenter.py::_checkerboard_novelty_reference` |
| Log-space Viterbi chord decoding | `viterbi_decode` | `chords/chord_recognizer.py::_viterbi_decode_reference` |

## Design constraints

- **Ported, not re-derived.** The Rust code is a line-for-line port of the
reference math. Results are unchanged.
- **Parity is the acceptance gate.** `tests/test_numeric_parity.py` asserts the
Rust output matches the NumPy reference over representative fixtures
(novelty: `<= 1e-6` max abs diff; Viterbi states: exact equality).
- **Python reference retained.** The engine calls the Rust path by default and
transparently falls back to the NumPy reference when the extension is not
installed.
- **Permissive dependencies only** (PyO3, `numpy`, `ndarray` — all MIT/Apache-2.0).

## Build

```bash
# From services/analysis-engine, into the project venv:
uvx maturin build --release --manifest-path rust/Cargo.toml \
--interpreter .venv/bin/python --out rust/dist
uv pip install --python .venv/bin/python rust/dist/*.whl
```

CI builds and installs the extension before running the Python suite, so the
Rust path (and its parity gate) is exercised on every run.
20 changes: 20 additions & 0 deletions services/analysis-engine/rust/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[build-system]
requires = ["maturin>=1.7,<2.0"]
build-backend = "maturin"

[project]
name = "bandscope-numeric"
version = "0.1.0"
description = "Rust numeric kernels for the BandScope analysis engine"
requires-python = ">=3.12"
license = { text = "MIT" }
classifiers = [
"Programming Language :: Rust",
"Programming Language :: Python :: Implementation :: CPython",
]

[tool.maturin]
# Build a standalone extension module named `bandscope_numeric`.
module-name = "bandscope_numeric"
manifest-path = "Cargo.toml"
strip = true
Loading