pub mod classification;
pub mod conv;
pub mod data;
pub mod datasets;
pub mod distributed; pub mod enhanced_scirs2_integration; pub mod explainability;
pub mod foundation; pub mod functional;
pub mod generative; pub mod geometric; pub mod hypergraph;
pub mod jit;
pub mod lottery_ticket; pub mod matching; pub mod multimodal;
pub mod neural_operators;
pub mod neuromorphic;
pub mod optimal_transport; pub mod parameter;
pub mod pool;
pub mod quantum;
pub mod scirs2_integration;
pub mod spectral; pub mod temporal;
pub mod utils;
use torsh_tensor::Tensor;
#[derive(Debug, Clone)]
pub struct GraphData {
pub x: Tensor,
pub edge_index: Tensor,
pub edge_attr: Option<Tensor>,
pub batch: Option<Tensor>,
pub num_nodes: usize,
pub num_edges: usize,
}
impl GraphData {
pub fn new(x: Tensor, edge_index: Tensor) -> Self {
let num_nodes = x.shape().dims()[0];
let num_edges = edge_index.shape().dims()[1];
Self {
x,
edge_index,
edge_attr: None,
batch: None,
num_nodes,
num_edges,
}
}
pub fn with_edge_attr(mut self, edge_attr: Tensor) -> Self {
self.edge_attr = Some(edge_attr);
self
}
pub fn with_batch(mut self, batch: Tensor) -> Self {
self.batch = Some(batch);
self
}
pub fn with_edge_attr_opt(mut self, edge_attr: Option<Tensor>) -> Self {
self.edge_attr = edge_attr;
self
}
pub fn memory_stats(&self) -> GraphMemoryStats {
let x_bytes = self.x.numel() * std::mem::size_of::<f32>(); let edge_index_bytes = self.edge_index.numel() * std::mem::size_of::<f32>(); let edge_attr_bytes = self
.edge_attr
.as_ref()
.map(|t| t.numel() * std::mem::size_of::<f32>())
.unwrap_or(0);
let batch_bytes = self
.batch
.as_ref()
.map(|t| t.numel() * std::mem::size_of::<f32>())
.unwrap_or(0);
GraphMemoryStats {
total_bytes: x_bytes + edge_index_bytes + edge_attr_bytes + batch_bytes,
node_features_bytes: x_bytes,
edge_index_bytes,
edge_attr_bytes,
batch_bytes,
}
}
pub fn validate(&self) -> Result<(), GraphValidationError> {
if let Ok(edge_data) = self.edge_index.to_vec() {
let max_node_id = edge_data.iter().fold(0.0f32, |a, &b| a.max(b));
if max_node_id >= self.num_nodes as f32 {
return Err(GraphValidationError::InvalidNodeIndex {
node_id: max_node_id as i64,
num_nodes: self.num_nodes,
});
}
}
if let Some(ref edge_attr) = self.edge_attr {
if edge_attr.shape().dims()[0] != self.num_edges {
return Err(GraphValidationError::EdgeAttrSizeMismatch {
expected: self.num_edges,
actual: edge_attr.shape().dims()[0],
});
}
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct GraphMemoryStats {
pub total_bytes: usize,
pub node_features_bytes: usize,
pub edge_index_bytes: usize,
pub edge_attr_bytes: usize,
pub batch_bytes: usize,
}
#[derive(Debug, thiserror::Error)]
pub enum GraphValidationError {
#[error("Invalid node index {node_id}, graph has only {num_nodes} nodes")]
InvalidNodeIndex { node_id: i64, num_nodes: usize },
#[error("Edge attribute size mismatch: expected {expected}, got {actual}")]
EdgeAttrSizeMismatch { expected: usize, actual: usize },
#[error("Tensor operation error: {0}")]
TensorError(String),
}
pub trait GraphLayer: std::fmt::Debug {
fn forward(&self, graph: &GraphData) -> GraphData;
fn parameters(&self) -> Vec<Tensor>;
}
pub mod attention_viz {
use torsh_tensor::Tensor;
#[derive(Debug, Clone)]
pub struct AttentionWeights {
pub edge_weights: Tensor, pub node_weights: Option<Tensor>, pub layer_name: String,
pub head_index: Option<usize>,
}
impl AttentionWeights {
pub fn new(edge_weights: Tensor, layer_name: String) -> Self {
Self {
edge_weights,
node_weights: None,
layer_name,
head_index: None,
}
}
pub fn with_node_weights(mut self, node_weights: Tensor) -> Self {
self.node_weights = Some(node_weights);
self
}
pub fn with_head_index(mut self, head_index: usize) -> Self {
self.head_index = Some(head_index);
self
}
pub fn normalize(&self) -> Self {
Self {
edge_weights: self.edge_weights.clone(),
node_weights: self.node_weights.clone(),
layer_name: self.layer_name.clone(),
head_index: self.head_index,
}
}
}
}
pub mod importance_analysis {
use torsh_tensor::Tensor;
#[derive(Debug, Clone)]
pub struct NodeImportance {
pub centrality_scores: Tensor, pub gradient_norm: Option<Tensor>, pub attention_sum: Option<Tensor>, pub feature_attribution: Option<Tensor>, }
impl NodeImportance {
pub fn new(centrality_scores: Tensor) -> Self {
Self {
centrality_scores,
gradient_norm: None,
attention_sum: None,
feature_attribution: None,
}
}
pub fn combined_importance(
&self,
weights: &[f32],
) -> Result<Tensor, Box<dyn std::error::Error>> {
Ok(self.centrality_scores.mul_scalar(weights[0])?)
}
}
}