File size: 4,145 Bytes
5e84645 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | //! 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).
#[pyfunction]
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.
#[pyfunction]
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.
#[pyfunction]
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.
#[pyfunction]
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.
#[pyfunction]
fn version() -> &'static str {
env!("CARGO_PKG_VERSION")
}
/// Build-time facts worth asserting from the Python side.
#[pyfunction]
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)
}
#[pymodule]
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(())
}
|