use crate::kg_store::KgStore;
use crate::namespace::Namespace;
use crate::store::ArrowGraphStore;
use crate::triple_store::SimpleTripleStore;
use crate::y_layer::YLayer;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GraphBackend {
InMemory,
Simple,
KnowledgeGraph,
}
#[derive(Debug, Clone)]
pub struct HardwareCapabilities {
pub cuda_available: bool,
pub mps_available: bool,
pub cpu_cores: usize,
pub memory_bytes: u64,
}
pub fn detect_hardware() -> HardwareCapabilities {
let cpu_cores = std::thread::available_parallelism()
.map(|p| p.get())
.unwrap_or(1);
let cuda_available = std::process::Command::new("nvidia-smi")
.arg("--query-gpu=name")
.arg("--format=csv,noheader")
.output()
.map(|o| o.status.success())
.unwrap_or(false);
let mps_available = cfg!(target_os = "macos") && cfg!(target_arch = "aarch64");
HardwareCapabilities {
cuda_available,
mps_available,
cpu_cores,
memory_bytes: 0, }
}
pub fn available_backends() -> Vec<GraphBackend> {
vec![
GraphBackend::InMemory,
GraphBackend::Simple,
GraphBackend::KnowledgeGraph,
]
}
pub fn recommended_backend() -> GraphBackend {
GraphBackend::KnowledgeGraph
}
#[derive(Debug, Clone)]
pub struct GraphStoreConfig {
pub backend: GraphBackend,
pub default_namespace: Namespace,
pub default_y_layer: YLayer,
}
impl Default for GraphStoreConfig {
fn default() -> Self {
Self {
backend: GraphBackend::KnowledgeGraph,
default_namespace: Namespace::World,
default_y_layer: YLayer::Semantic,
}
}
}
impl GraphStoreConfig {
pub fn new(backend: GraphBackend) -> Self {
Self {
backend,
..Default::default()
}
}
pub fn with_namespace(mut self, ns: Namespace) -> Self {
self.default_namespace = ns;
self
}
pub fn with_y_layer(mut self, layer: YLayer) -> Self {
self.default_y_layer = layer;
self
}
}
pub enum CreatedStore {
InMemory(ArrowGraphStore),
Simple(SimpleTripleStore),
KnowledgeGraph(KgStore),
}
impl CreatedStore {
pub fn len(&self) -> usize {
match self {
Self::InMemory(s) => s.len(),
Self::Simple(s) => s.len(),
Self::KnowledgeGraph(s) => s.len(),
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn into_arrow(self) -> ArrowGraphStore {
match self {
Self::InMemory(s) => s,
_ => panic!("expected InMemory variant"),
}
}
pub fn into_simple(self) -> SimpleTripleStore {
match self {
Self::Simple(s) => s,
_ => panic!("expected Simple variant"),
}
}
pub fn into_kg(self) -> KgStore {
match self {
Self::KnowledgeGraph(s) => s,
_ => panic!("expected KnowledgeGraph variant"),
}
}
pub fn try_into_arrow(self) -> Option<ArrowGraphStore> {
match self {
Self::InMemory(s) => Some(s),
_ => None,
}
}
pub fn try_into_simple(self) -> Option<SimpleTripleStore> {
match self {
Self::Simple(s) => Some(s),
_ => None,
}
}
pub fn try_into_kg(self) -> Option<KgStore> {
match self {
Self::KnowledgeGraph(s) => Some(s),
_ => None,
}
}
}
pub fn create_graph_store(config: &GraphStoreConfig) -> CreatedStore {
match config.backend {
GraphBackend::InMemory => CreatedStore::InMemory(ArrowGraphStore::new()),
GraphBackend::Simple => CreatedStore::Simple(SimpleTripleStore::with_defaults(
config.default_namespace,
config.default_y_layer,
)),
GraphBackend::KnowledgeGraph => CreatedStore::KnowledgeGraph(KgStore::with_defaults(
config.default_namespace,
config.default_y_layer,
)),
}
}
pub fn create_default_store() -> KgStore {
KgStore::new()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_detect_hardware() {
let hw = detect_hardware();
assert!(hw.cpu_cores >= 1);
if cfg!(target_os = "macos") && cfg!(target_arch = "aarch64") {
assert!(hw.mps_available);
}
}
#[test]
fn test_available_backends() {
let backends = available_backends();
assert_eq!(backends.len(), 3);
assert!(backends.contains(&GraphBackend::InMemory));
assert!(backends.contains(&GraphBackend::Simple));
assert!(backends.contains(&GraphBackend::KnowledgeGraph));
}
#[test]
fn test_recommended_backend() {
assert_eq!(recommended_backend(), GraphBackend::KnowledgeGraph);
}
#[test]
fn test_create_in_memory() {
let config = GraphStoreConfig::new(GraphBackend::InMemory);
let store = create_graph_store(&config);
assert!(store.is_empty());
let arrow = store.into_arrow();
assert_eq!(arrow.len(), 0);
}
#[test]
fn test_create_simple() {
let config = GraphStoreConfig::new(GraphBackend::Simple)
.with_namespace(Namespace::Research)
.with_y_layer(YLayer::Reasoning);
let store = create_graph_store(&config);
assert!(store.is_empty());
let simple = store.into_simple();
assert_eq!(simple.len(), 0);
}
#[test]
fn test_create_knowledge_graph() {
let config = GraphStoreConfig::new(GraphBackend::KnowledgeGraph);
let store = create_graph_store(&config);
assert!(store.is_empty());
let kg = store.into_kg();
assert!(!kg.prefixes().is_empty());
}
#[test]
fn test_create_default_store() {
let store = create_default_store();
assert!(store.is_empty());
assert!(!store.prefixes().is_empty());
}
#[test]
fn test_default_config() {
let config = GraphStoreConfig::default();
assert_eq!(config.backend, GraphBackend::KnowledgeGraph);
assert_eq!(config.default_namespace, Namespace::World);
assert_eq!(config.default_y_layer, YLayer::Semantic);
}
#[test]
fn test_try_into_wrong_variant() {
let store = create_graph_store(&GraphStoreConfig::new(GraphBackend::InMemory));
assert!(store.try_into_kg().is_none());
let store = create_graph_store(&GraphStoreConfig::new(GraphBackend::KnowledgeGraph));
assert!(store.try_into_arrow().is_none());
}
#[test]
fn test_created_store_len() {
let store = create_graph_store(&GraphStoreConfig::new(GraphBackend::InMemory));
assert_eq!(store.len(), 0);
assert!(store.is_empty());
}
#[test]
fn test_graceful_fallback_no_gpu() {
let _hw = detect_hardware();
for backend in available_backends() {
let config = GraphStoreConfig::new(backend);
let store = create_graph_store(&config);
assert!(store.is_empty());
}
}
}