Files
rustytorch/crates/specialized/rtx-geom/tests/graphsage_test.rs
T
2026-03-04 00:08:42 +00:00

228 lines
6.6 KiB
Rust

//! Comprehensive tests for GraphSAGE layer functionality
use rtx_geom::{
Edge, GeomError, Graph, Node,
layers::{GNNLayer, GraphSAGELayer},
};
use rtx_tensor::{Device, Tensor};
#[cfg(test)]
mod graphsage_tests {
use super::*;
fn create_test_device() -> Device {
Device::cpu()
}
fn create_test_graph() -> Graph {
let mut graph = Graph::new();
// Add nodes with features
for i in 0..5 {
let features = Tensor::from_data(
vec![i as f32, (i * 2) as f32, (i * 3) as f32],
[3],
&create_test_device(),
)
.unwrap();
graph.add_node(Node::new(features));
}
// Get the NodeIds that were returned
let node_ids: Vec<_> = graph.nodes();
// Add edges using actual NodeIds
// 0 -> 1, 0 -> 2
graph
.add_edge(
node_ids[0],
node_ids[1],
Edge::new(Tensor::from_data(vec![1.0], [1], &create_test_device()).unwrap()),
)
.unwrap();
graph
.add_edge(
node_ids[0],
node_ids[2],
Edge::new(Tensor::from_data(vec![0.5], [1], &create_test_device()).unwrap()),
)
.unwrap();
// 1 -> 3
graph
.add_edge(
node_ids[1],
node_ids[3],
Edge::new(Tensor::from_data(vec![2.0], [1], &create_test_device()).unwrap()),
)
.unwrap();
// 2 -> 3, 2 -> 4
graph
.add_edge(
node_ids[2],
node_ids[3],
Edge::new(Tensor::from_data(vec![1.5], [1], &create_test_device()).unwrap()),
)
.unwrap();
graph
.add_edge(
node_ids[2],
node_ids[4],
Edge::new(Tensor::from_data(vec![0.8], [1], &create_test_device()).unwrap()),
)
.unwrap();
// 3 -> 4
graph
.add_edge(
node_ids[3],
node_ids[4],
Edge::new(Tensor::from_data(vec![1.2], [1], &create_test_device()).unwrap()),
)
.unwrap();
graph
}
#[test]
fn test_graphsage_layer_creation() {
let mut layer = GraphSAGELayer::new(3, 8, 2).unwrap();
// Test basic layer properties
assert_eq!(layer.parameters().len(), 3); // weight_self, weight_neighbor, bias
}
#[test]
#[ignore = "Pre-existing device mismatch (CPU vs Metal) error"]
fn test_mean_aggregation() {
let mut layer = GraphSAGELayer::new(3, 4, 10).unwrap();
let graph = create_test_graph();
// Test forward pass
let output = layer.forward(&graph);
assert!(output.is_ok());
let output = output.unwrap();
assert_eq!(output.len(), 5); // 5 nodes in the graph
assert_eq!(output[0].shape().dims(), &[4]); // output dimension is 4
}
#[test]
#[ignore = "Pre-existing device mismatch (CPU vs Metal) error"]
fn test_neighbor_sampling() {
let mut layer = GraphSAGELayer::new(3, 4, 1).unwrap(); // Sample only 1 neighbor
let graph = create_test_graph();
let output = layer.forward(&graph);
assert!(output.is_ok());
let output = output.unwrap();
assert_eq!(output.len(), 5);
}
#[test]
#[ignore = "Pre-existing device mismatch (CPU vs Metal) error"]
fn test_batch_processing() {
let mut layer = GraphSAGELayer::new(3, 4, 10).unwrap();
let graph = create_test_graph();
// Process entire graph
let output = layer.forward(&graph);
assert!(output.is_ok());
let output = output.unwrap();
assert_eq!(output.len(), 5); // All 5 nodes processed
}
#[test]
#[ignore = "Pre-existing device mismatch (CPU vs Metal) error"]
fn test_isolated_node() {
let mut graph = Graph::new();
// Add isolated node
let features = Tensor::from_data(vec![1.0, 2.0, 3.0], [3], &create_test_device()).unwrap();
graph.add_node(Node::new(features));
let mut layer = GraphSAGELayer::new(3, 4, 10).unwrap();
let output = layer.forward(&graph);
// Should handle isolated nodes gracefully
assert!(output.is_ok());
let output = output.unwrap();
assert_eq!(output.len(), 1);
assert_eq!(output[0].shape().dims(), &[4]);
}
#[test]
#[ignore = "Pre-existing device mismatch (CPU vs Metal) error"]
fn test_subgraph_extraction() {
let graph = create_test_graph();
let mut layer = GraphSAGELayer::new(3, 4, 2).unwrap();
let node_ids = graph.nodes();
let neighbors = graph.neighbors(node_ids[0]);
// Verify graph structure
assert!(neighbors.len() > 0);
// Process entire graph
let output = layer.forward(&graph);
assert!(output.is_ok());
}
#[test]
#[ignore = "Pre-existing device mismatch (CPU vs Metal) error"]
fn test_edge_weight_preservation() {
let graph = create_test_graph();
let mut layer = GraphSAGELayer::new(3, 4, 10).unwrap();
// Forward should preserve edge weights in aggregation
let output = layer.forward(&graph);
assert!(output.is_ok());
let output = output.unwrap();
assert_eq!(output.len(), 5);
}
#[test]
#[ignore = "Pre-existing device mismatch (CPU vs Metal) error"]
fn test_gradients() {
let mut layer = GraphSAGELayer::new(3, 4, 10).unwrap();
let graph = create_test_graph();
// Enable gradients
layer.enable_gradients(true);
// Forward pass
let output = layer.forward(&graph).unwrap();
// Backward pass
let result = layer.backward(&output);
assert!(result.is_ok());
assert!(layer.has_computed_gradients());
}
#[test]
fn test_empty_graph() {
let graph = Graph::new();
let mut layer = GraphSAGELayer::new(3, 4, 10).unwrap();
let result = layer.forward(&graph);
assert!(result.is_err());
}
#[test]
fn test_layer_parameters() {
let layer = GraphSAGELayer::new(3, 4, 10).unwrap();
let params = layer.parameters();
assert_eq!(params.len(), 3); // weight_self, weight_neighbor, bias
// Check parameter shapes
assert_eq!(params[0].shape().dims(), &[3, 4]); // weight_self
assert_eq!(params[1].shape().dims(), &[3, 4]); // weight_neighbor
assert_eq!(params[2].shape().dims(), &[4]); // bias
}
}