use num_complex::Complex;
use std::collections::HashMap;
use std::fmt::Debug;
use thiserror::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct NodeId(pub u64);
#[derive(Clone, Debug, PartialEq)]
pub struct FractalGraphEdge {
pub destination: NodeId,
pub edge_type: EdgeType,
pub weight: Complex<f32>,
}
#[derive(Debug, Clone)]
pub struct Node<T> {
pub id: NodeId,
pub payload: T,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum EdgeType {
Excitatory,
Inhibitory,
Resonant,
}
#[derive(Debug)]
pub struct FractalGraph<T> {
nodes: HashMap<NodeId, Node<T>>,
edges: HashMap<NodeId, Vec<FractalGraphEdge>>,
next_node_id: u64,
}
#[derive(Debug, Error, PartialEq, Eq)]
pub enum GraphError {
#[error("Node not found: {0:?}")]
NodeNotFound(NodeId),
#[error("Edge already exists from {0:?} to {1:?} with type {2:?}")]
DuplicateEdge(NodeId, NodeId, EdgeType),
}
impl<T> FractalGraph<T> {
pub fn new() -> Self {
FractalGraph {
nodes: HashMap::new(),
edges: HashMap::new(),
next_node_id: 0,
}
}
}
impl<T> Default for FractalGraph<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: Debug + PartialEq> FractalGraph<T> {
pub fn add_node(&mut self, payload: T) -> NodeId {
let id = NodeId(self.next_node_id);
self.next_node_id += 1;
let node = Node { id, payload };
self.nodes.insert(id, node);
self.edges.insert(id, Vec::new());
id
}
pub fn add_edge(
&mut self,
from: NodeId,
to: NodeId,
edge_type: EdgeType,
initial_weight: Complex<f32>,
) -> Result<(), GraphError> {
if !self.nodes.contains_key(&from) {
return Err(GraphError::NodeNotFound(from));
}
if !self.nodes.contains_key(&to) {
return Err(GraphError::NodeNotFound(to));
}
let outgoing_edges = self.edges.get_mut(&from).unwrap();
if outgoing_edges
.iter()
.any(|edge| edge.destination == to && edge.edge_type == edge_type)
{
return Err(GraphError::DuplicateEdge(from, to, edge_type));
}
outgoing_edges.push(FractalGraphEdge {
destination: to,
edge_type,
weight: initial_weight,
});
Ok(())
}
pub fn remove_node(&mut self, node_id: NodeId) -> Result<Node<T>, GraphError> {
let removed_node = self.nodes.remove(&node_id).ok_or(GraphError::NodeNotFound(node_id))?;
self.edges.remove(&node_id);
for (_id, outgoing_edges) in self.edges.iter_mut() {
outgoing_edges.retain(|edge| edge.destination != node_id);
}
Ok(removed_node)
}
pub fn get_edges_for_node_mut(
&mut self,
node_id: NodeId,
) -> Option<&mut Vec<FractalGraphEdge>> {
self.edges.get_mut(&node_id)
}
pub fn get_node(&self, node_id: NodeId) -> Option<&Node<T>> {
self.nodes.get(&node_id)
}
pub fn node_count(&self) -> usize {
self.nodes.len()
}
pub fn edge_count(&self) -> usize {
self.edges.values().map(|v| v.len()).sum()
}
pub fn is_acyclic(&self) -> bool {
let mut visited = HashMap::new(); let mut recursion_stack = HashMap::new();
for node_id in self.nodes.keys() {
if self._is_cyclic_util(*node_id, &mut visited, &mut recursion_stack) {
return false; }
}
true }
fn _is_cyclic_util(
&self,
node_id: NodeId,
visited: &mut HashMap<NodeId, bool>,
recursion_stack: &mut HashMap<NodeId, bool>,
) -> bool {
if recursion_stack.get(&node_id).cloned().unwrap_or(false) {
return true;
}
if visited.get(&node_id).cloned().unwrap_or(false) {
return false;
}
visited.insert(node_id, true);
recursion_stack.insert(node_id, true);
if let Some(outgoing_edges) = self.edges.get(&node_id) {
for edge in outgoing_edges {
if self._is_cyclic_util(edge.destination, visited, recursion_stack) {
return true;
}
}
}
recursion_stack.insert(node_id, false);
false
}
pub fn all_edges_mut(&mut self) -> impl Iterator<Item = &mut FractalGraphEdge> {
self.edges.values_mut().flatten()
}
}