From 0f405213d314d8cb2bf3faa61a5990f4ab1d0cff Mon Sep 17 00:00:00 2001 From: ruv Date: Tue, 21 Jul 2026 17:03:49 -0700 Subject: [PATCH] feat(adr-185): P4 benchmarks, examples, and README extras for the SOTA wheels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ADR-185 §4.2 pytest-benchmark micro-benchmarks + runnable examples + README extras table for the aether/meridian/mat bindings. - python/bench/test_bench_{aether,meridian,mat}.py — follow the existing test_bench_vitals.py pattern (skipped by default; --benchmark-only). - python/examples/{reid_from_csi,cross_room_calibrate,mat_triage}.py — typed, runnable, mypy --strict clean. - python/README.md — SOTA extras table + example links. Measured on a RELEASE wheel (maturin develop --release --features sota), reference machine per ADR-117 §10: AETHER embed() mean ~150 us/window (target <2 ms) PASS batch scaling 1/8/64: 140 / 1091 / 8509 us (linear, no O(n^2)) PASS MERIDIAN normalize() mean ~2.2 us/frame (target <200 us) PASS MERIDIAN encode() mean ~6.9 us (target <200 us) PASS MAT ingest+scan_once() mean ~40 ms/256-frame (< 500 ms interval) PASS Acceptance self-verification (ADR-185 §6), all run just now: §6.1 default wheel 279 KB (<=5 MB); build_features has no p6-* feature PASS §6.2 pytest tests/test_aether.py 9/9 PASS §6.3 pytest tests/test_meridian.py 13/13 PASS §6.4 pytest tests/test_mat.py 7/7 PASS §6.5 benchmarks meet all targets (above) PASS §6.6 parity harness: 3/3 SHA golden gates green (cargo test --features sota, 6/6); CI *wiring* as a release gate is out of python/ scope PARTIAL §6.7 SOTA accuracy bars on labeled fixtures: NOT met (no labeled fixtures / trained models available; parity proves path-equality, not accuracy) OPEN §6.8 .pyi stubs present for all three; mypy --strict on the 3 examples PASS §6.9 base wheel `import wifi_densepose.{aether,meridian,mat}` raises a clear ImportError naming the extra PASS No regression: 76 pre-existing tests pass on the default wheel. Status NOT flipped to Accepted: §6.7 (accuracy bars) is unmet, §6.6 CI wiring is pending, and the per-extra wheel-size hoists (sensing-server / train / mat leaf crates) remain follow-ups. docs/adr/ is owned by another agent this session, so the ADR ledger edit is deferred to that owner. --- python/README.md | 23 ++++++++++++ python/bench/test_bench_aether.py | 44 ++++++++++++++++++++++ python/bench/test_bench_mat.py | 44 ++++++++++++++++++++++ python/bench/test_bench_meridian.py | 29 +++++++++++++++ python/examples/cross_room_calibrate.py | 45 +++++++++++++++++++++++ python/examples/mat_triage.py | 49 +++++++++++++++++++++++++ python/examples/reid_from_csi.py | 39 ++++++++++++++++++++ 7 files changed, 273 insertions(+) create mode 100644 python/bench/test_bench_aether.py create mode 100644 python/bench/test_bench_mat.py create mode 100644 python/bench/test_bench_meridian.py create mode 100644 python/examples/cross_room_calibrate.py create mode 100644 python/examples/mat_triage.py create mode 100644 python/examples/reid_from_csi.py diff --git a/python/README.md b/python/README.md index 4d085cb2..675dee3f 100644 --- a/python/README.md +++ b/python/README.md @@ -43,6 +43,29 @@ pip install "wifi-densepose[client]" # + WebSocket/MQTT clients Wheels are published for Linux (x86_64, aarch64), macOS (x86_64, arm64), and Windows (amd64). +### SOTA extras (ADR-185) + +Three optional subsystems bind the Rust SOTA modules as compiled-feature +wheels. Each raises a clear `ImportError` if you import it without the extra: + +| Extra | Module | What it adds | +|-------|--------|--------------| +| `[aether]` | `wifi_densepose.aether` | Contrastive CSI embeddings / re-identification (ADR-024) — `EmbeddingExtractor`, `cosine_similarity`, `info_nce_loss` | +| `[meridian]` | `wifi_densepose.meridian` | Cross-environment domain generalization (ADR-027) — `HardwareNormalizer`, `GeometryEncoder`, `RapidAdaptation`, `CrossDomainEvaluator` | +| `[mat]` | `wifi_densepose.mat` | Mass-Casualty Assessment disaster-survivor detection + START triage — `DisasterResponse`, `Survivor`, `TriageStatus` | +| `[sota]` | all three | Convenience superset | + +```bash +pip install "wifi-densepose[aether]" # re-identification embeddings +pip install "wifi-densepose[meridian]" # cross-room calibration +pip install "wifi-densepose[mat]" # disaster triage +pip install "wifi-densepose[sota]" # all three +``` + +Runnable examples: [`examples/reid_from_csi.py`](examples/reid_from_csi.py), +[`examples/cross_room_calibrate.py`](examples/cross_room_calibrate.py), +[`examples/mat_triage.py`](examples/mat_triage.py). + ## Usage ### Extract breathing rate from a CSI stream diff --git a/python/bench/test_bench_aether.py b/python/bench/test_bench_aether.py new file mode 100644 index 00000000..fb6d2775 --- /dev/null +++ b/python/bench/test_bench_aether.py @@ -0,0 +1,44 @@ +"""ADR-185 §4.2 — AETHER embed() micro-benchmarks. + +Target (release build, ADR-024 §2.8 FP32 <1 ms with headroom): steady-state +`embed()` < 2 ms/window, and batched `embed()` scales roughly linearly (no +accidental O(n²)). + +Run with: + pytest python/bench/test_bench_aether.py --benchmark-only + +Skipped by default (they live in `bench/`, outside `testpaths`). Timing +targets are validated on a RELEASE wheel (`maturin develop --release +--features sota`); a debug wheel will be several× slower. +""" + +from __future__ import annotations + +import math + +import pytest + +from wifi_densepose import aether + + +def _window(frames: int = 8, subc: int = 56) -> list[list[float]]: + return [[math.sin(0.1 * t + 0.03 * k) for k in range(subc)] for t in range(frames)] + + +def _extractor() -> aether.EmbeddingExtractor: + return aether.EmbeddingExtractor(n_subcarriers=56, config=aether.AetherConfig()) + + +def test_embed_per_window(benchmark) -> None: + ext = _extractor() + window = _window() + out = benchmark(lambda: ext.embed(window)) + assert len(out) == 128 + + +@pytest.mark.parametrize("batch", [1, 8, 64]) +def test_embed_batch_scaling(benchmark, batch: int) -> None: + ext = _extractor() + windows = [_window() for _ in range(batch)] + out = benchmark(lambda: [ext.embed(w) for w in windows]) + assert len(out) == batch diff --git a/python/bench/test_bench_mat.py b/python/bench/test_bench_mat.py new file mode 100644 index 00000000..2420660b --- /dev/null +++ b/python/bench/test_bench_mat.py @@ -0,0 +1,44 @@ +"""ADR-185 §4.2 — MAT scan micro-benchmark. + +Measures the cost of one full ingest + `scan_once()` cycle over the +committed 256-frame CSI stream. The per-cycle cost should stay comfortably +below the configured scan interval (default 500 ms) so the binding is not +the bottleneck. + +Run with: + pytest python/bench/test_bench_mat.py --benchmark-only + +Validated on a RELEASE wheel; a debug wheel will be several× slower. +""" + +from __future__ import annotations + +import json +from pathlib import Path + +from wifi_densepose import mat + +_FIXTURE = Path(__file__).resolve().parents[1] / "tests" / "golden" / "mat_input.json" + + +def _stream() -> list[dict]: + return json.loads(_FIXTURE.read_text())["stream"] + + +def test_scan_cycle_cost(benchmark) -> None: + stream = _stream() + + def _run() -> int: + cfg = mat.DisasterConfig( + mat.DisasterType.Earthquake, sensitivity=0.9, confidence_threshold=0.1 + ) + resp = mat.DisasterResponse(cfg) + resp.initialize_event(0.0, 0.0, "bench") + resp.add_zone(mat.ScanZone.rectangle("Zone A", 0.0, 0.0, 50.0, 30.0)) + for frame in stream: + resp.push_csi_data(frame["amplitude"], frame["phase"]) + resp.scan_once() + return len(resp.survivors()) + + survivors = benchmark(_run) + assert survivors == 1 diff --git a/python/bench/test_bench_meridian.py b/python/bench/test_bench_meridian.py new file mode 100644 index 00000000..d9492bbb --- /dev/null +++ b/python/bench/test_bench_meridian.py @@ -0,0 +1,29 @@ +"""ADR-185 §4.2 — MERIDIAN micro-benchmarks. + +Targets (release build, ADR-027 §4.1/§4.3 ×2 headroom): `normalize()` +< 200 µs/frame, `encode()` < 200 µs. + +Run with: + pytest python/bench/test_bench_meridian.py --benchmark-only + +Validated on a RELEASE wheel; a debug wheel will be several× slower. +""" + +from __future__ import annotations + +from wifi_densepose import meridian as mer + + +def test_normalize_per_frame(benchmark) -> None: + norm = mer.HardwareNormalizer() + amp = [10.0 + 0.05 * k for k in range(64)] + phase = [0.01 * k for k in range(64)] + out = benchmark(lambda: norm.normalize(amp, phase, mer.HardwareType.Esp32S3)) + assert len(out.amplitude) == 56 + + +def test_geometry_encode(benchmark) -> None: + enc = mer.GeometryEncoder(mer.MeridianGeometryConfig()) + aps = [[0.0, 0.0, 2.5], [5.0, 0.0, 2.5], [0.0, 4.0, 2.5]] + out = benchmark(lambda: enc.encode(aps)) + assert len(out) == 64 diff --git a/python/examples/cross_room_calibrate.py b/python/examples/cross_room_calibrate.py new file mode 100644 index 00000000..0f07d77d --- /dev/null +++ b/python/examples/cross_room_calibrate.py @@ -0,0 +1,45 @@ +"""MERIDIAN cross-room calibration (ADR-185 P2, `[meridian]` extra). + +Hardware-invariant CSI normalization, AP-geometry encoding, and few-shot +rapid adaptation — the tch-free domain-generalization path. + + pip install wifi-densepose[meridian] + python examples/cross_room_calibrate.py +""" + +from __future__ import annotations + +import math + +from wifi_densepose.meridian import ( + GeometryEncoder, + HardwareNormalizer, + HardwareType, + MeridianGeometryConfig, + RapidAdaptation, +) + + +def main() -> None: + # 1. Normalize a 64-subcarrier ESP32 frame to the canonical 56-tone grid. + norm = HardwareNormalizer() + amp = [10.0 + 0.05 * k for k in range(64)] + phase = [0.01 * k for k in range(64)] + frame = norm.normalize(amp, phase, HardwareType.detect(64)) + print(f"canonical subcarriers: {len(frame.amplitude)} (hw={frame.hardware_type})") + + # 2. Encode AP positions into a permutation-invariant geometry embedding. + enc = GeometryEncoder(MeridianGeometryConfig()) + geometry = enc.encode([[0.0, 0.0, 2.5], [5.0, 0.0, 2.5], [0.0, 4.0, 2.5]]) + print(f"geometry embedding dim: {len(geometry)}") + + # 3. Few-shot rapid adaptation over a handful of unlabeled frames. + ra = RapidAdaptation(min_calibration_frames=10, lora_rank=4) + for i in range(12): + ra.push_frame([math.sin(0.1 * i + 0.05 * d) for d in range(16)]) + result = ra.adapt() + print(f"adapted over {result.frames_used} frames, final_loss={result.final_loss:.4f}") + + +if __name__ == "__main__": + main() diff --git a/python/examples/mat_triage.py b/python/examples/mat_triage.py new file mode 100644 index 00000000..5dade08c --- /dev/null +++ b/python/examples/mat_triage.py @@ -0,0 +1,49 @@ +"""MAT disaster-survivor triage from CSI (ADR-185 P3, `[mat]` extra). + +Ingest a CSI stream, run one detection cycle, and list detected survivors +by START triage class. + + pip install wifi-densepose[mat] + python examples/mat_triage.py + +Note: the stream here is synthetic (breathing-modulated) — it demonstrates +the API and pipeline, not validated detection accuracy on real rubble. +""" + +from __future__ import annotations + +import math +from collections.abc import Iterator + +from wifi_densepose.mat import DisasterConfig, DisasterResponse, DisasterType, ScanZone + + +def breathing_stream( + frames: int = 256, subc: int = 56, fs: float = 20.0 +) -> Iterator[tuple[list[float], list[float]]]: + for t in range(frames): + tt = t / fs + breath = 2.0 * math.sin(2 * math.pi * 0.3 * tt) + amp = [10.0 + 0.05 * k + breath for k in range(subc)] + phase = [0.01 * k + 0.1 * math.sin(2 * math.pi * 0.3 * tt) for k in range(subc)] + yield amp, phase + + +def main() -> None: + cfg = DisasterConfig(DisasterType.Earthquake, sensitivity=0.9, confidence_threshold=0.1) + resp = DisasterResponse(cfg) + resp.initialize_event(0.0, 0.0, "Collapsed Building A") + resp.add_zone(ScanZone.rectangle("North Wing", 0.0, 0.0, 50.0, 30.0)) + + for amp, phase in breathing_stream(): + resp.push_csi_data(amp, phase) + resp.scan_once() + + survivors = resp.survivors() + print(f"detected {len(survivors)} survivor(s)") + for s in survivors: + print(f" {s.id[:8]} triage={s.triage_status} confidence={s.confidence:.3f}") + + +if __name__ == "__main__": + main() diff --git a/python/examples/reid_from_csi.py b/python/examples/reid_from_csi.py new file mode 100644 index 00000000..6c31ad96 --- /dev/null +++ b/python/examples/reid_from_csi.py @@ -0,0 +1,39 @@ +"""AETHER re-identification from CSI (ADR-185 P1, `[aether]` extra). + +Compute 128-dim contrastive embeddings for CSI windows and score them by +cosine similarity — the primitive behind room fingerprinting and person +re-identification. + + pip install wifi-densepose[aether] + python examples/reid_from_csi.py +""" + +from __future__ import annotations + +import math + +from wifi_densepose.aether import AetherConfig, EmbeddingExtractor, cosine_similarity + + +def make_window(phase_shift: float, frames: int = 8, subc: int = 56) -> list[list[float]]: + """A synthetic CSI window; `phase_shift` stands in for a different scene.""" + return [ + [math.sin(0.1 * t + 0.03 * k + phase_shift) for k in range(subc)] + for t in range(frames) + ] + + +def main() -> None: + ext = EmbeddingExtractor(n_subcarriers=56, config=AetherConfig()) + + same_a = ext.embed(make_window(0.0)) + same_b = ext.embed(make_window(0.0)) # same scene + other = ext.embed(make_window(1.5)) # different scene + + print(f"embedding dim: {len(same_a)}") + print(f"same-scene similarity: {cosine_similarity(same_a, same_b):.4f}") + print(f"cross-scene similarity: {cosine_similarity(same_a, other):.4f}") + + +if __name__ == "__main__": + main()