1pub mod graph;
7pub mod types;
8
9pub use graph::Graph;
11pub use types::{Edge, EdgeWeight, Node};
12
13pub use petgraph::graph::IndexType;
15
16use petgraph::graph::{Graph as PetGraph, NodeIndex};
18use petgraph::visit::EdgeRef;
19use petgraph::{Directed, Undirected};
20use scirs2_core::ndarray::{Array1, Array2};
21use std::collections::HashMap;
22use std::fmt::Debug;
23use std::hash::Hash;
24
25use crate::error::{GraphError, Result};
26
27pub struct DiGraph<N: Node, E: EdgeWeight, Ix: IndexType = u32> {
29 graph: PetGraph<N, E, Directed, Ix>,
30 node_indices: HashMap<N, NodeIndex<Ix>>,
31}
32
33pub struct MultiGraph<N: Node, E: EdgeWeight, Ix: IndexType = u32> {
35 graph: PetGraph<N, Vec<E>, Undirected, Ix>,
36 node_indices: HashMap<N, NodeIndex<Ix>>,
37}
38
39pub struct MultiDiGraph<N: Node, E: EdgeWeight, Ix: IndexType = u32> {
41 graph: PetGraph<N, Vec<E>, Directed, Ix>,
42 node_indices: HashMap<N, NodeIndex<Ix>>,
43}
44
45pub struct BipartiteGraph<N: Node, E: EdgeWeight, Ix: IndexType = u32> {
47 graph: PetGraph<N, E, Undirected, Ix>,
48 node_indices: HashMap<N, NodeIndex<Ix>>,
49 left_nodes: std::collections::HashSet<N>,
50 right_nodes: std::collections::HashSet<N>,
51}
52
53pub struct Hypergraph<N: Node, E: EdgeWeight, Ix: IndexType = u32> {
55 nodes: HashMap<N, NodeIndex<Ix>>,
56 hyperedges: Vec<Hyperedge<N, E>>,
57 _phantom: std::marker::PhantomData<Ix>,
58}
59
60#[derive(Debug, Clone)]
62pub struct Hyperedge<N: Node, E: EdgeWeight> {
63 pub nodes: Vec<N>,
65 pub weight: E,
67 pub id: usize,
69}
70
71impl<N: Node + std::fmt::Debug, E: EdgeWeight, Ix: IndexType> Default for DiGraph<N, E, Ix> {
73 fn default() -> Self {
74 Self::new()
75 }
76}
77
78impl<N: Node + std::fmt::Debug, E: EdgeWeight, Ix: IndexType> DiGraph<N, E, Ix> {
79 pub fn new() -> Self {
81 DiGraph {
82 graph: PetGraph::default(),
83 node_indices: HashMap::new(),
84 }
85 }
86
87 pub fn add_node(&mut self, node: N) -> NodeIndex<Ix> {
89 if let Some(idx) = self.node_indices.get(&node) {
90 return *idx;
91 }
92
93 let idx = self.graph.add_node(node.clone());
94 self.node_indices.insert(node, idx);
95 idx
96 }
97
98 pub fn add_edge(&mut self, source: N, target: N, weight: E) -> Result<()> {
100 let source_idx = self.add_node(source);
101 let target_idx = self.add_node(target);
102
103 self.graph.add_edge(source_idx, target_idx, weight);
104 Ok(())
105 }
106
107 pub fn nodes(&self) -> Vec<&N> {
109 self.graph.node_weights().collect()
110 }
111
112 pub fn node_count(&self) -> usize {
114 self.graph.node_count()
115 }
116
117 pub fn edge_count(&self) -> usize {
119 self.graph.edge_count()
120 }
121
122 pub fn contains_node(&self, node: &N) -> bool {
124 self.node_indices.contains_key(node)
125 }
126
127 pub fn has_node(&self, node: &N) -> bool {
129 self.contains_node(node)
130 }
131
132 pub fn inner(&self) -> &PetGraph<N, E, Directed, Ix> {
134 &self.graph
135 }
136
137 pub fn neighbors(&self, node: &N) -> Result<Vec<N>>
139 where
140 N: Clone,
141 {
142 if let Some(&idx) = self.node_indices.get(node) {
143 let neighbors: Vec<N> = self
144 .graph
145 .neighbors(idx)
146 .map(|neighbor_idx| self.graph[neighbor_idx].clone())
147 .collect();
148 Ok(neighbors)
149 } else {
150 Err(GraphError::node_not_found("unknown node"))
151 }
152 }
153
154 pub fn successors(&self, node: &N) -> Result<Vec<N>>
156 where
157 N: Clone,
158 {
159 if let Some(&idx) = self.node_indices.get(node) {
160 let successors: Vec<N> = self
161 .graph
162 .neighbors_directed(idx, petgraph::Direction::Outgoing)
163 .map(|neighbor_idx| self.graph[neighbor_idx].clone())
164 .collect();
165 Ok(successors)
166 } else {
167 Err(GraphError::node_not_found("unknown node"))
168 }
169 }
170
171 pub fn predecessors(&self, node: &N) -> Result<Vec<N>>
173 where
174 N: Clone,
175 {
176 if let Some(&idx) = self.node_indices.get(node) {
177 let predecessors: Vec<N> = self
178 .graph
179 .neighbors_directed(idx, petgraph::Direction::Incoming)
180 .map(|neighbor_idx| self.graph[neighbor_idx].clone())
181 .collect();
182 Ok(predecessors)
183 } else {
184 Err(GraphError::node_not_found("unknown node"))
185 }
186 }
187
188 pub fn edge_weight(&self, source: &N, target: &N) -> Result<E>
190 where
191 E: Clone,
192 {
193 if let (Some(&src_idx), Some(&tgt_idx)) =
194 (self.node_indices.get(source), self.node_indices.get(target))
195 {
196 if let Some(edge_ref) = self.graph.find_edge(src_idx, tgt_idx) {
197 Ok(self.graph[edge_ref].clone())
198 } else {
199 Err(GraphError::edge_not_found("unknown", "unknown"))
200 }
201 } else {
202 Err(GraphError::node_not_found("unknown node"))
203 }
204 }
205
206 pub fn edges(&self) -> Vec<Edge<N, E>>
208 where
209 N: Clone,
210 E: Clone,
211 {
212 let mut result = Vec::new();
213 let node_map: HashMap<NodeIndex<Ix>, &N> = self
214 .graph
215 .node_indices()
216 .map(|idx| (idx, self.graph.node_weight(idx).unwrap()))
217 .collect();
218
219 for edge in self.graph.edge_references() {
220 let source = node_map[&edge.source()].clone();
221 let target = node_map[&edge.target()].clone();
222 let weight = edge.weight().clone();
223
224 result.push(Edge {
225 source,
226 target,
227 weight,
228 });
229 }
230
231 result
232 }
233
234 pub fn has_edge(&self, source: &N, target: &N) -> bool {
236 if let (Some(&src_idx), Some(&tgt_idx)) =
237 (self.node_indices.get(source), self.node_indices.get(target))
238 {
239 self.graph.contains_edge(src_idx, tgt_idx)
240 } else {
241 false
242 }
243 }
244
245 pub fn degree(&self, node: &N) -> usize {
247 if let Some(idx) = self.node_indices.get(node) {
248 self.graph.neighbors(*idx).count()
249 } else {
250 0
251 }
252 }
253
254 pub fn node_index(&self, node: &N) -> Option<NodeIndex<Ix>> {
256 self.node_indices.get(node).copied()
257 }
258
259 pub fn adjacency_matrix(&self) -> Array2<f64>
261 where
262 E: Clone + Into<f64>,
263 {
264 let n = self.node_count();
265 let mut matrix = Array2::zeros((n, n));
266
267 let mut node_to_idx = HashMap::new();
269 for (i, node_idx) in self.graph.node_indices().enumerate() {
270 node_to_idx.insert(node_idx, i);
271 }
272
273 for edge in self.graph.edge_references() {
275 let src_idx = node_to_idx[&edge.source()];
276 let tgt_idx = node_to_idx[&edge.target()];
277 let weight: f64 = edge.weight().clone().into();
278 matrix[[src_idx, tgt_idx]] = weight;
279 }
280
281 matrix
282 }
283
284 pub fn out_degree_vector(&self) -> Array1<usize> {
286 let n = self.node_count();
287 let mut degrees = Array1::zeros(n);
288
289 let mut node_to_idx = HashMap::new();
291 for (i, node_idx) in self.graph.node_indices().enumerate() {
292 node_to_idx.insert(node_idx, i);
293 }
294
295 for node_idx in self.graph.node_indices() {
297 let out_degree = self
298 .graph
299 .neighbors_directed(node_idx, petgraph::Direction::Outgoing)
300 .count();
301 let idx = node_to_idx[&node_idx];
302 degrees[idx] = out_degree;
303 }
304
305 degrees
306 }
307
308 pub fn in_degree_vector(&self) -> Array1<usize> {
310 let n = self.node_count();
311 let mut degrees = Array1::zeros(n);
312
313 let mut node_to_idx = HashMap::new();
315 for (i, node_idx) in self.graph.node_indices().enumerate() {
316 node_to_idx.insert(node_idx, i);
317 }
318
319 for node_idx in self.graph.node_indices() {
321 let in_degree = self
322 .graph
323 .neighbors_directed(node_idx, petgraph::Direction::Incoming)
324 .count();
325 let idx = node_to_idx[&node_idx];
326 degrees[idx] = in_degree;
327 }
328
329 degrees
330 }
331}
332
333impl<N: Node + std::fmt::Debug, E: EdgeWeight, Ix: IndexType> Default for MultiGraph<N, E, Ix> {
335 fn default() -> Self {
336 MultiGraph {
337 graph: PetGraph::default(),
338 node_indices: HashMap::new(),
339 }
340 }
341}
342
343impl<N: Node + std::fmt::Debug, E: EdgeWeight, Ix: IndexType> Default for MultiDiGraph<N, E, Ix> {
344 fn default() -> Self {
345 MultiDiGraph {
346 graph: PetGraph::default(),
347 node_indices: HashMap::new(),
348 }
349 }
350}
351
352impl<N: Node + std::fmt::Debug, E: EdgeWeight, Ix: IndexType> Default for BipartiteGraph<N, E, Ix> {
353 fn default() -> Self {
354 BipartiteGraph {
355 graph: PetGraph::default(),
356 node_indices: HashMap::new(),
357 left_nodes: std::collections::HashSet::new(),
358 right_nodes: std::collections::HashSet::new(),
359 }
360 }
361}
362
363impl<N: Node + std::fmt::Debug, E: EdgeWeight, Ix: IndexType> Hypergraph<N, E, Ix> {
364 pub fn new() -> Self {
366 Hypergraph {
367 nodes: HashMap::new(),
368 hyperedges: Vec::new(),
369 _phantom: std::marker::PhantomData,
370 }
371 }
372
373 pub fn nodes(&self) -> impl Iterator<Item = &N> {
375 self.nodes.keys()
376 }
377
378 pub fn hyperedges(&self) -> &Vec<Hyperedge<N, E>> {
380 &self.hyperedges
381 }
382
383 pub fn has_node(&self, node: &N) -> bool {
385 self.nodes.contains_key(node)
386 }
387
388 pub fn add_node(&mut self, node: N) -> NodeIndex<Ix> {
390 if let Some(&idx) = self.nodes.get(&node) {
391 return idx;
392 }
393
394 let idx = NodeIndex::new(self.nodes.len());
395 self.nodes.insert(node, idx);
396 idx
397 }
398
399 pub fn add_hyperedge_from_vec(&mut self, nodes: Vec<N>, weight: E) -> Result<usize>
401 where
402 N: Clone,
403 {
404 for node in &nodes {
406 self.add_node(node.clone());
407 }
408
409 let id = self.hyperedges.len();
410 self.hyperedges.push(Hyperedge { nodes, weight, id });
411 Ok(id)
412 }
413
414 pub fn add_hyperedge(&mut self, nodes: Vec<N>, weight: E) -> Result<usize>
416 where
417 N: Clone,
418 {
419 self.add_hyperedge_from_vec(nodes, weight)
420 }
421
422 pub fn are_connected(&self, source: &N, target: &N) -> bool
424 where
425 N: PartialEq,
426 {
427 for hyperedge in &self.hyperedges {
428 if hyperedge.nodes.contains(source) && hyperedge.nodes.contains(target) {
429 return true;
430 }
431 }
432 false
433 }
434
435 pub fn connecting_hyperedges(&self, source: &N, target: &N) -> Vec<&Hyperedge<N, E>>
437 where
438 N: PartialEq,
439 {
440 self.hyperedges
441 .iter()
442 .filter(|hyperedge| {
443 hyperedge.nodes.contains(source) && hyperedge.nodes.contains(target)
444 })
445 .collect()
446 }
447
448 pub fn hyperedges_containing_node(&self, node: &N) -> Vec<&Hyperedge<N, E>>
450 where
451 N: PartialEq,
452 {
453 self.hyperedges
454 .iter()
455 .filter(|hyperedge| hyperedge.nodes.contains(node))
456 .collect()
457 }
458
459 pub fn neighbors(&self, node: &N) -> Vec<N>
461 where
462 N: Clone + PartialEq,
463 {
464 let mut neighbors = std::collections::HashSet::new();
465
466 for hyperedge in &self.hyperedges {
467 if hyperedge.nodes.contains(node) {
468 for neighbor in &hyperedge.nodes {
469 if neighbor != node {
470 neighbors.insert(neighbor.clone());
471 }
472 }
473 }
474 }
475
476 neighbors.into_iter().collect()
477 }
478
479 pub fn to_graph(&self) -> Graph<N, E, Ix>
481 where
482 N: Clone,
483 E: Clone + Default,
484 {
485 let mut graph = Graph::new();
486
487 for node in self.nodes() {
489 graph.add_node(node.clone());
490 }
491
492 for hyperedge in &self.hyperedges {
494 for i in 0..hyperedge.nodes.len() {
495 for j in (i + 1)..hyperedge.nodes.len() {
496 let _ = graph.add_edge(
497 hyperedge.nodes[i].clone(),
498 hyperedge.nodes[j].clone(),
499 hyperedge.weight.clone(),
500 );
501 }
502 }
503 }
504
505 graph
506 }
507
508 pub fn node_count(&self) -> usize {
510 self.nodes.len()
511 }
512
513 pub fn hyperedge_count(&self) -> usize {
515 self.hyperedges.len()
516 }
517
518 pub fn degree(&self, node: &N) -> usize
520 where
521 N: PartialEq,
522 {
523 self.hyperedges
524 .iter()
525 .filter(|hyperedge| hyperedge.nodes.contains(node))
526 .count()
527 }
528
529 pub fn remove_hyperedge(&mut self, hyperedge_id: usize) -> Result<Hyperedge<N, E>>
531 where
532 N: Clone,
533 E: Clone,
534 {
535 if hyperedge_id < self.hyperedges.len() {
536 Ok(self.hyperedges.remove(hyperedge_id))
537 } else {
538 Err(GraphError::Other("Hyperedge ID out of bounds".to_string()))
539 }
540 }
541
542 pub fn incidence_matrix(&self) -> Vec<Vec<f64>>
546 where
547 N: Clone + PartialEq,
548 {
549 let nodes: Vec<N> = self.nodes().cloned().collect();
550 let mut matrix = vec![vec![0.0; self.hyperedges.len()]; nodes.len()];
551
552 for (edge_idx, hyperedge) in self.hyperedges.iter().enumerate() {
553 for node in &hyperedge.nodes {
554 if let Some(node_idx) = nodes.iter().position(|n| n == node) {
555 matrix[node_idx][edge_idx] = 1.0;
556 }
557 }
558 }
559
560 matrix
561 }
562
563 pub fn maximal_cliques(&self) -> Vec<Vec<N>>
567 where
568 N: Clone + PartialEq,
569 {
570 let mut cliques = Vec::new();
571 let mut visited_nodes = std::collections::HashSet::new();
572
573 for hyperedge in &self.hyperedges {
574 let mut clique = Vec::new();
575 for node in &hyperedge.nodes {
576 if !visited_nodes.contains(node) {
577 clique.push(node.clone());
578 visited_nodes.insert(node.clone());
579 }
580 }
581 if !clique.is_empty() {
582 cliques.push(clique);
583 }
584 }
585
586 cliques
587 }
588
589 pub fn hyperedge_size_stats(&self) -> (usize, usize, f64) {
592 if self.hyperedges.is_empty() {
593 return (0, 0, 0.0);
594 }
595
596 let sizes: Vec<usize> = self.hyperedges.iter().map(|e| e.nodes.len()).collect();
597 let min_size = *sizes.iter().min().unwrap_or(&0);
598 let max_size = *sizes.iter().max().unwrap_or(&0);
599 let avg_size = sizes.iter().sum::<usize>() as f64 / sizes.len() as f64;
600
601 (min_size, max_size, avg_size)
602 }
603
604 pub fn is_uniform(&self) -> bool {
606 if self.hyperedges.is_empty() {
607 return true;
608 }
609
610 let first_size = self.hyperedges[0].nodes.len();
611 self.hyperedges.iter().all(|e| e.nodes.len() == first_size)
612 }
613}
614
615impl<N: Node + std::fmt::Debug, E: EdgeWeight, Ix: IndexType> Default for Hypergraph<N, E, Ix> {
616 fn default() -> Self {
617 Self::new()
618 }
619}