python: drop the NumPy runtime dependency (zero third-party deps) (#317)
Makes the Python binding truly dependency-free (Task 5b). `pip install wickra` now pulls **zero** third-party packages, and `import wickra` never imports NumPy (verified: `"numpy" not in sys.modules` after a batch call).
## What changed
- **Inputs** accept any sequence or buffer of numbers — `array.array`, `memoryview`, a NumPy `ndarray`, or a plain `list` — via `Vec` extraction (newtypes `Buf1`/`BufI64`). `PyBuffer` is unavailable under `abi3-py39`, and `unsafe_code = forbid` rules out a zero-copy slice, so inputs are copied once (negligible vs. compute).
- **Single-output `batch(...)`** returns a stdlib `array.array('d')` (native buffer protocol → `numpy.asarray` zero-copy).
- **Multi-output `batch(...)`** returns a buffer-protocol `Matrix` preserving `.shape`, integer-row and `[i, j]` access, and `.tolist()`.
- **NumPy** moves to an optional extra (`pip install wickra[numpy]`); it is never required.
- Streaming `update(...)` is unchanged; results are numerically identical.
## Implementation
- Trait `IntoPyData` + a `matrix()`/`f64_array()` helper collapse the ~400 batch call sites; `array.array` is built via `bytemuck` (Zlib/MIT/Apache — `cargo deny check licenses` ok).
- Tests migrated to `array.array`/`Matrix` (1-D `.shape`→`len()`, `.dtype`→`.typecode`; numeric comparisons normalize through a `_to_np` helper). The non-contiguous-input test now asserts acceptance instead of rejection.
## Verification (local)
- `pytest bindings/python/tests` — **1991 passed**.
- `cargo fmt --all`, `cargo clippy --workspace --all-targets --all-features -- -D warnings`, `cargo test --workspace --all-features`, `cargo deny check licenses` — all green.
**BREAKING for Python**: batch return types change from NumPy arrays to `array.array`/`Matrix`. Documented in CHANGELOG; ships with the data-layer release bundle (no separate tag).
This commit is contained in:
@@ -15,8 +15,23 @@ import pytest
|
||||
import wickra as ta
|
||||
|
||||
|
||||
def _equal_with_nan(a: np.ndarray, b: np.ndarray, tol: float = 1e-9) -> bool:
|
||||
def _to_np(x) -> np.ndarray:
|
||||
"""Normalize a Wickra batch result to a float64 ndarray.
|
||||
|
||||
Scalar batches return a stdlib ``array.array``; multi-output batches return a
|
||||
``Matrix`` exposing ``.shape``/``.tolist()``.
|
||||
"""
|
||||
if isinstance(x, np.ndarray):
|
||||
return x.astype(np.float64)
|
||||
if hasattr(x, "tolist") and hasattr(x, "shape"):
|
||||
return np.asarray(x.tolist(), dtype=np.float64)
|
||||
return np.asarray(x, dtype=np.float64)
|
||||
|
||||
|
||||
def _equal_with_nan(a, b, tol: float = 1e-9) -> bool:
|
||||
"""NumPy ``==`` treats NaN as not-equal; emulate ``equal_nan`` for floats."""
|
||||
a = _to_np(a)
|
||||
b = _to_np(b)
|
||||
if a.shape != b.shape:
|
||||
return False
|
||||
both_nan = np.isnan(a) & np.isnan(b)
|
||||
|
||||
Reference in New Issue
Block a user