| //! pyo3 binding — the `vectorhd_core` Python extension module. | |
| //! | |
| //! Built by maturin with `--features python`. **Importing it is always optional**: the Python side | |
| //! probes for it once (`vectorhd.analytic.backend`) and falls back to the pure-Python kernel, so | |
| //! neither the package nor its test suite ever acquires a hard Rust dependency. | |
| //! | |
| //! Zero-copy where it matters: array arguments arrive as `PyReadonlyArray*` views over the caller's | |
| //! numpy buffers, so a window call marshals pointers rather than copying the target patch. That | |
| //! matters here — P0 exists to measure per-call FFI overhead against a kernel whose per-window work | |
| //! is only tens of microseconds, and a copy of the patch would dominate the thing being measured. | |
| use pyo3::prelude::*; | |
| /// True when this build implements C-5's quadratic per-region colour model (P1a). | |
| fn supports_quadratic() -> bool { | |
| true | |
| } | |
| /// True when this build implements the four bezigon priors (P1b R1). | |
| /// | |
| /// Probed, not assumed, for the same reason `supports_quadratic` is: a P1a extension exposes | |
| /// `windowed_data` and nothing else, and a missing probe must read as "no" so the caller keeps its | |
| /// Python path rather than calling into a symbol that is not there. | |
| fn supports_priors() -> bool { | |
| true | |
| } | |
| /// True when this build exposes the per-window objective handle (P1b R2). | |
| /// | |
| /// This is the third capability level: `auto` resolves objective-handle → kernel-only → python, | |
| /// and each rung must be independently probeable so an older extension degrades to the rung it | |
| /// actually implements instead of failing. | |
| fn supports_objective_handle() -> bool { | |
| true | |
| } | |
| /// The exception type a Rust panic surfaces as, so Python can guard against it explicitly. | |
| /// | |
| /// pyo3 raises `pyo3_runtime.PanicException`, whose module name is **synthetic** — there is no | |
| /// importable `pyo3_runtime`, so Python cannot obtain the class by importing it and would | |
| /// otherwise have to trigger a panic to learn the type. Since it derives from `BaseException` | |
| /// rather than `Exception`, a bare `except Exception` does not catch it, and the orchestrator's | |
| /// never-fail refine guard needs the class to widen its catch correctly. Exposing it is the | |
| /// extension declaring its own failure mode instead of leaving the caller to guess. | |
| fn panic_exception_type(py: Python<'_>) -> Py<pyo3::types::PyType> { | |
| py.get_type::<pyo3::panic::PanicException>().unbind() | |
| } | |
| /// Crate version, so Python can log which kernel it actually loaded. | |
| fn version() -> &'static str { | |
| env!("CARGO_PKG_VERSION") | |
| } | |
| /// Build-time facts worth asserting from the Python side. | |
| fn kernel_info() -> PyResult<std::collections::HashMap<String, String>> { | |
| let mut m = std::collections::HashMap::new(); | |
| m.insert("version".into(), env!("CARGO_PKG_VERSION").into()); | |
| m.insert("name".into(), env!("CARGO_PKG_NAME").into()); | |
| // The optimizer's forward model is f64 throughout; a f32 build would silently break the | |
| // 1e-9 equivalence gates, so the Python side asserts this. | |
| m.insert("float".into(), "f64".into()); | |
| // Capability flags. The Python dispatch routes quadratic-colour windows here only when this | |
| // says yes, so an older extension keeps the per-window Python fallback instead of silently | |
| // producing flat-colour answers for a quadratic model. | |
| m.insert("quadratic_color".into(), "true".into()); | |
| m.insert("priors".into(), "true".into()); | |
| m.insert("objective_handle".into(), "true".into()); | |
| Ok(m) | |
| } | |
| fn vectorhd_core(m: &Bound<'_, PyModule>) -> PyResult<()> { | |
| m.add_function(wrap_pyfunction!(version, m)?)?; | |
| m.add_function(wrap_pyfunction!(kernel_info, m)?)?; | |
| m.add_function(wrap_pyfunction!(supports_quadratic, m)?)?; | |
| m.add_function(wrap_pyfunction!(supports_priors, m)?)?; | |
| m.add_function(wrap_pyfunction!(supports_objective_handle, m)?)?; | |
| m.add_function(wrap_pyfunction!(panic_exception_type, m)?)?; | |
| crate::window::register(m)?; | |
| crate::priors::register(m)?; | |
| crate::handle::register(m)?; | |
| Ok(()) | |
| } | |