use serde::{Deserialize, Serialize};
use std::sync::OnceLock;
pub const HNSW_AVAILABLE: bool = true;
#[cfg(feature = "attention")]
pub const ATTENTION_AVAILABLE: bool = true;
#[cfg(not(feature = "attention"))]
pub const ATTENTION_AVAILABLE: bool = false;
#[cfg(feature = "graph")]
pub const GRAPH_AVAILABLE: bool = true;
#[cfg(not(feature = "graph"))]
pub const GRAPH_AVAILABLE: bool = false;
#[cfg(feature = "gnn")]
pub const GNN_AVAILABLE: bool = true;
#[cfg(not(feature = "gnn"))]
pub const GNN_AVAILABLE: bool = false;
pub const SONA_AVAILABLE: bool = true;
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
pub const SIMD_AVAILABLE: bool = true;
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
pub const SIMD_AVAILABLE: bool = false;
#[cfg(feature = "parallel")]
pub const PARALLEL_AVAILABLE: bool = true;
#[cfg(not(feature = "parallel"))]
pub const PARALLEL_AVAILABLE: bool = false;
static CAPABILITIES: OnceLock<RuvectorCapabilities> = OnceLock::new();
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct RuvectorCapabilities {
pub hnsw: bool,
pub attention: bool,
pub graph: bool,
pub gnn: bool,
pub sona: bool,
pub simd: bool,
pub parallel: bool,
pub quantization_level: u8,
pub max_efficient_dim: usize,
pub estimated_ops_per_sec: u64,
}
impl Default for RuvectorCapabilities {
fn default() -> Self {
Self::detect()
}
}
impl RuvectorCapabilities {
pub fn detect() -> Self {
*CAPABILITIES.get_or_init(|| Self::probe_capabilities())
}
pub fn cached() -> &'static Self {
CAPABILITIES.get_or_init(|| Self::probe_capabilities())
}
pub fn redetect() -> Self {
Self::probe_capabilities()
}
fn probe_capabilities() -> Self {
let quantization_level = Self::probe_quantization_level();
let (max_efficient_dim, estimated_ops_per_sec) = Self::probe_performance();
Self {
hnsw: HNSW_AVAILABLE,
attention: ATTENTION_AVAILABLE,
graph: GRAPH_AVAILABLE,
gnn: GNN_AVAILABLE,
sona: SONA_AVAILABLE,
simd: SIMD_AVAILABLE,
parallel: PARALLEL_AVAILABLE,
quantization_level,
max_efficient_dim,
estimated_ops_per_sec,
}
}
fn probe_quantization_level() -> u8 {
if SIMD_AVAILABLE && PARALLEL_AVAILABLE {
3 } else if SIMD_AVAILABLE {
2 } else {
1 }
}
fn probe_performance() -> (usize, u64) {
if SIMD_AVAILABLE && PARALLEL_AVAILABLE {
(4096, 16_000_000) } else if SIMD_AVAILABLE {
(2048, 8_000_000) } else {
(1024, 2_000_000) }
}
pub fn full_intelligence(&self) -> bool {
self.hnsw && self.sona && self.attention
}
pub fn graph_reasoning(&self) -> bool {
self.graph && self.gnn
}
pub fn summary(&self) -> String {
let mut features = Vec::new();
if self.hnsw {
features.push("HNSW");
}
if self.attention {
features.push("FlashAttn");
}
if self.graph {
features.push("Graph");
}
if self.gnn {
features.push("GNN");
}
if self.sona {
features.push("SONA");
}
if self.simd {
features.push("SIMD");
}
if self.parallel {
features.push("Parallel");
}
format!(
"Ruvector [{}] Q{} max_dim={} ~{}M ops/s",
features.join("+"),
self.quantization_level,
self.max_efficient_dim,
self.estimated_ops_per_sec / 1_000_000
)
}
pub fn recommended_batch_size(&self) -> usize {
if self.parallel && self.simd {
256
} else if self.simd {
64
} else {
16
}
}
pub fn recommended_hnsw_params(&self) -> (usize, usize, usize) {
if self.parallel && self.simd {
(32, 200, 100) } else if self.simd {
(16, 100, 50) } else {
(8, 50, 25) }
}
}
#[macro_export]
macro_rules! with_hnsw {
($code:expr) => {
if $crate::capabilities::HNSW_AVAILABLE {
$code
}
};
($code:expr, $fallback:expr) => {
if $crate::capabilities::HNSW_AVAILABLE {
$code
} else {
$fallback
}
};
}
#[macro_export]
macro_rules! with_attention {
($code:expr) => {
#[cfg(feature = "attention")]
{
$code
}
};
($code:expr, $fallback:expr) => {
#[cfg(feature = "attention")]
{
$code
}
#[cfg(not(feature = "attention"))]
{
$fallback
}
};
}
#[macro_export]
macro_rules! with_graph {
($code:expr) => {
#[cfg(feature = "graph")]
{
$code
}
};
($code:expr, $fallback:expr) => {
#[cfg(feature = "graph")]
{
$code
}
#[cfg(not(feature = "graph"))]
{
$fallback
}
};
}
#[macro_export]
macro_rules! with_gnn {
($code:expr) => {
#[cfg(feature = "gnn")]
{
$code
}
};
($code:expr, $fallback:expr) => {
#[cfg(feature = "gnn")]
{
$code
}
#[cfg(not(feature = "gnn"))]
{
$fallback
}
};
}
pub fn gate_feature<T, F: FnOnce() -> T>(feature: bool, f: F) -> Option<T> {
if feature {
Some(f())
} else {
None
}
}
pub fn gate_feature_or<T, F: FnOnce() -> T>(feature: bool, f: F, fallback: T) -> T {
if feature {
f()
} else {
fallback
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_capabilities_detection() {
let caps = RuvectorCapabilities::detect();
assert!(caps.hnsw);
assert!(caps.sona);
assert!(caps.quantization_level >= 1);
assert!(caps.max_efficient_dim >= 512);
}
#[test]
fn test_capabilities_cached() {
let caps1 = RuvectorCapabilities::detect();
let caps2 = RuvectorCapabilities::cached();
assert_eq!(caps1.hnsw, caps2.hnsw);
assert_eq!(caps1.attention, caps2.attention);
}
#[test]
fn test_capabilities_summary() {
let caps = RuvectorCapabilities::detect();
let summary = caps.summary();
assert!(summary.contains("Ruvector"));
assert!(summary.contains("HNSW"));
assert!(summary.contains("SONA"));
}
#[test]
fn test_recommended_params() {
let caps = RuvectorCapabilities::detect();
let batch_size = caps.recommended_batch_size();
assert!(batch_size >= 16);
let (m, ef_c, ef_s) = caps.recommended_hnsw_params();
assert!(m >= 8);
assert!(ef_c >= ef_s);
}
#[test]
fn test_feature_gates() {
let result = gate_feature(true, || 42);
assert_eq!(result, Some(42));
let result = gate_feature(false, || 42);
assert_eq!(result, None);
let result = gate_feature_or(true, || 42, 0);
assert_eq!(result, 42);
let result = gate_feature_or(false, || 42, 0);
assert_eq!(result, 0);
}
}