Skip to main content

transit_grid/graphs/topology/
topology_graph.rs

1use std::collections::{HashMap, HashSet};
2
3use petgraph::{
4    stable_graph::{EdgeIndex, NodeIndex, StableDiGraph},
5    visit::EdgeRef,
6    Direction,
7};
8
9use crate::core::{Accessability, EdgeId, NodeId};
10
11use super::{TopoEdge, TopoNode};
12
13/// Represents the topological graph of a transit network as a skew-symmetric graph.
14///
15/// A `TopologyGraph` is a directed graph where each node in the topological graph
16/// maps to a node in the physical graph. This is particularly useful for scenarios
17/// such as rail switches where the directionality of edges matters.
18///
19/// # Skew-Symmetric Model
20///
21/// The `TopologyGraph` struct uses a skew-symmetric model based on the definition by Goldberg & Karzanov (1996).
22/// In this model, let `G = (V, E)` be the directed graph with a function `σ` mapping vertices of `G` to other vertices,
23/// satisfying the following properties:
24///
25/// 1. For every vertex `v`, `σ(v)` ≠ `v`.
26/// 2. For every vertex `v`, `σ(σ(v))` = `v`.
27/// 3. For every edge `(u, v)`, `(σ(v), σ(u))` must also be an edge.
28///
29/// In the context of `TopologyGraph`,
30/// * For each node `v` in `V`, there are two nodes in `V_t`, denoted as `v_entry` and `v_exit`.
31/// * For each edge `(u, v)` in `E`, there are two directed edges in `E_t`: one from `u_exit` to `v_entry` and one from `v_exit` to `u_entry`.
32///
33/// Mathematically,
34/// * `V_t = {v_entry, v_exit | v ∈ V}`
35/// * `E_t = {(u_exit, v_entry), (v_exit, u_entry) | (u, v) ∈ E}`
36///
37///  # Bigroup Algebraic Structure (Additional Information)
38///
39/// According to Dr. R. Muthuraj and P. M. Sitharselvam, M. S. Muthuraman (2010), a set `G` with two binary operations `+` and `*`
40/// is called a bigroup if there exist two proper subsets `G1` and `G2` of `G` such that `G = G1 ∪ G2`.
41///
42#[derive(Debug, Clone)]
43pub struct TopologyGraph {
44    /// the inner graph
45    pub graph: StableDiGraph<TopoNode, TopoEdge, u32>,
46    id_to_index: HashMap<NodeId, (NodeIndex, NodeIndex)>,
47    index_to_id: HashMap<NodeIndex, NodeId>,
48}
49
50impl TopologyGraph {
51    /// Creates a new instance of `TopologyGraph`.
52    pub fn new() -> Self {
53        TopologyGraph {
54            graph: StableDiGraph::<TopoNode, TopoEdge, u32>::new(),
55            id_to_index: HashMap::new(),
56            index_to_id: HashMap::new(),
57        }
58    }
59
60    /// Returns the `NodeId` corresponding to a given `NodeIndex`.
61    ///
62    /// This method is useful when you have the index of a node in the graph and you want to retrieve its identifier.
63    ///
64    /// # Arguments
65    ///
66    /// * `index` - The `NodeIndex` of the node.
67    ///
68    /// # Returns
69    ///
70    /// * `NodeId` - The identifier of the node corresponding to the input index.
71    ///
72    /// # Panics
73    ///
74    /// This function will panic if the `NodeIndex` does not exist in the graph.
75    pub fn index_to_id(&self, index: NodeIndex) -> Option<&NodeId> {
76        self.index_to_id.get(&index)
77    }
78
79    /// Returns the `NodeIndex` corresponding to a given `NodeId`.
80    ///
81    /// This method is useful when you have the identifier of a node and you want to retrieve its index in the graph.
82    /// As each `NodeId` maps to two `TopoNode`s in the graph, this function returns a tuple of `NodeIndex`.
83    ///
84    /// # Arguments
85    ///
86    /// * `id` - The `NodeId` of the node.
87    ///
88    /// # Returns
89    ///
90    /// * A tuple of two `NodeIndex` values corresponding to the two `TopoNode`s for the input `NodeId`.
91    ///
92    /// # Panics
93    ///
94    /// This function will panic if the `NodeId` does not exist in the graph.
95    pub fn id_to_index(&self, id: NodeId) -> Option<&(NodeIndex, NodeIndex)> {
96        self.id_to_index.get(&id)
97    }
98
99    /// Adds a Node with a `NodeId` to the topological graph. This internally adds two `TopoNode`s to the graph.
100    ///
101    /// # Arguments
102    ///
103    /// * `node_id` - The `NodeId` to be added to the graph.
104    ///
105    /// # Returns
106    ///
107    /// * A tuple of two `TopoNodeId`s corresponding to the two `TopoNode`s added for the input `NodeId`.
108    pub fn add_node(&mut self, node_id: NodeId) -> (NodeIndex, NodeIndex) {
109        let topo_node1 = TopoNode {
110            id: NodeIndex::default(), // Temporary value; will be updated
111            node_id,
112        };
113        let topo_node1_id = self.graph.add_node(topo_node1);
114        self.graph.node_weight_mut(topo_node1_id).unwrap().id = topo_node1_id;
115
116        let topo_node2 = TopoNode {
117            id: NodeIndex::default(), // Temporary value; will be updated
118            node_id,
119        };
120        let topo_node2_id = self.graph.add_node(topo_node2);
121        self.graph.node_weight_mut(topo_node2_id).unwrap().id = topo_node2_id;
122
123        self.id_to_index
124            .insert(node_id, (topo_node1_id, topo_node2_id));
125
126        self.index_to_id.insert(topo_node1_id, node_id);
127        self.index_to_id.insert(topo_node2_id, node_id);
128        (topo_node1_id, topo_node2_id)
129    }
130
131    /// Adds a `TopoEdge` to the topological graph.
132    ///
133    /// # Arguments
134    ///
135    /// * `edge_id` - The `EdgeId` to be added to the graph.
136    /// * `from_node_id` - The `NodeId` from which the edge is originating.
137    /// * `to_node_id` - The `NodeId` to which the edge is pointing.
138    ///
139    /// # Returns
140    ///
141    /// * `TopoEdgeId` - The ID of the added edge.
142    pub fn add_edge(
143        &mut self,
144        edge_id: EdgeId,
145        from_node_id: NodeId,
146        to_node_id: NodeId,
147    ) -> (EdgeIndex, EdgeIndex) {
148        let (from_topo_node_id1, from_topo_node_id2) =
149            *self.id_to_index.get(&from_node_id).unwrap();
150        let (to_topo_node_id1, to_topo_node_id2) = *self.id_to_index.get(&to_node_id).unwrap();
151
152        let from_topo_node_id = if self.has_incoming(from_topo_node_id1) {
153            from_topo_node_id2
154        } else {
155            from_topo_node_id1
156        };
157
158        let to_topo_node_id = if self.has_incoming(to_topo_node_id1) {
159            to_topo_node_id2
160        } else {
161            to_topo_node_id1
162        };
163
164        let from_node_id: NodeId = *self.index_to_id.get(&from_topo_node_id).unwrap();
165        let to_node_id: NodeId = *self.index_to_id.get(&to_topo_node_id).unwrap();
166
167        let topo_edge1 = TopoEdge {
168            id: EdgeIndex::new(0), // Temporary value; will be updated
169            from: from_node_id,
170            to: to_node_id,
171            edge_id,
172        };
173        let topo_edge1_id = self
174            .graph
175            .add_edge(from_topo_node_id, to_topo_node_id, topo_edge1);
176        self.graph.edge_weight_mut(topo_edge1_id).unwrap().id = topo_edge1_id;
177
178        let from_topo_node_id = self.get_other_toponode(from_topo_node_id).unwrap();
179        let to_topo_node_id = self.get_other_toponode(to_topo_node_id).unwrap();
180
181        let topo_edge2 = TopoEdge {
182            id: EdgeIndex::new(0), // Temporary value; will be updated
183            from: to_node_id,
184            to: from_node_id,
185            edge_id,
186        };
187        let topo_edge2_id = self
188            .graph
189            .add_edge(to_topo_node_id, from_topo_node_id, topo_edge2);
190        self.graph.edge_weight_mut(topo_edge2_id).unwrap().id = topo_edge2_id;
191
192        (topo_edge1_id, topo_edge2_id)
193    }
194
195    /// Checks if there are no edges in the specified direction leading to any of the nodes in the neighbors list.
196    ///
197    /// # Arguments
198    ///
199    /// * `topo_node_id` - The `NodeIndex` of the node to check.
200    /// * `neighbors` - A vector of `NodeId` that the node should not have edges towards in the given direction.
201    /// * `dir` - The direction of the edges to check (Outgoing or Incoming).
202    ///
203    /// # Returns
204    ///
205    /// * `bool` - True if none of the neighbors have an edge in the given direction to the node, otherwise false.
206    pub fn no_edges_in_direction(
207        &self,
208        topo_node_id: NodeIndex,
209        neighbors: Vec<NodeId>,
210        dir: Direction,
211    ) -> bool {
212        // Convert the neighbors Vec into a HashSet for faster lookup
213        let neighbors_set: HashSet<_> = neighbors.into_iter().collect();
214
215        // Check for each neighbor of the node
216        for edge in self.graph.edges_directed(topo_node_id, dir) {
217            if neighbors_set.contains(&self.graph[edge.target()].node_id) {
218                // If any edge in the given direction leads to a node in the neighbors list, return false
219                return false;
220            }
221        }
222
223        // If we've gone through all edges and none lead to a node in the neighbors list, return true
224        true
225    }
226
227    /// Returns the `NodeIndex` of the `NodeId` that does not have any edge in the opposite direction leading to any node in `neighbors`.
228    ///
229    /// # Arguments
230    ///
231    /// * `node_id` - The ID of the node to check.
232    /// * `neighbors` - A vector of `NodeId`s to check.
233    /// * `dir` - The `Direction` in which to check the edges.
234    ///
235    /// # Returns
236    ///
237    /// * `Option<NodeIndex>` - The `NodeIndex` of the `NodeId` if it does not have any edge in the opposite direction leading to nodes in `neighbors`, otherwise `None`.
238    pub fn find_node_index_with_edges(
239        &self,
240        node_id: NodeId,
241        neighbors: Vec<NodeId>,
242        dir: Direction,
243    ) -> Option<NodeIndex> {
244        let topo_node_ids = self.id_to_index.get(&node_id)?;
245        if self.no_edges_in_direction(topo_node_ids.0, neighbors.clone(), dir.opposite()) {
246            return Some(topo_node_ids.0);
247        }
248        if self.no_edges_in_direction(topo_node_ids.1, neighbors, dir.opposite()) {
249            return Some(topo_node_ids.1);
250        }
251        None
252    }
253
254    /// Returns the `NodeIndex` of the other `TopoNode` for a given `TopoNode`.
255    ///
256    /// # Arguments
257    ///
258    /// * `topo_node_id` - The `NodeIndex` of the `TopoNode`.
259    ///
260    /// # Returns
261    ///
262    /// * `Option<NodeIndex>` - The `NodeIndex` of the other `TopoNode` for the given `TopoNode`, if it exists.
263    pub fn get_other_toponode(&self, topo_node_id: NodeIndex) -> Option<NodeIndex> {
264        let node_id = self.index_to_id.get(&topo_node_id)?;
265        let topo_node_ids = self.id_to_index.get(node_id)?;
266        if topo_node_ids.0 == topo_node_id {
267            Some(topo_node_ids.1)
268        } else if topo_node_ids.1 == topo_node_id {
269            Some(topo_node_ids.0)
270        } else {
271            None
272        }
273    }
274    /// Adds an edge with a certain accessibility into the graph.
275    ///
276    /// # Arguments
277    ///
278    /// * `edge_id` - The identifier of the edge that should be added.
279    /// * `from_node_id` - The identifier of the node where the edge should start.
280    /// * `to_node_id` - The identifier of the node where the edge should end.
281    /// * `accessability` - The type of accessability of the edge. This can be either `ReachableNodes` or `UnreachableNodes`.
282    ///
283    /// # Returns
284    ///
285    /// A tuple of `EdgeIndex` values that were assigned to the newly created edges.
286    ///
287    /// # Panics
288    ///
289    /// The function will panic if it's unable to add an edge with the provided accessibility. This might occur if it cannot find nodes with the desired edge accessability or if the respective `TopoNode`s for the given nodes cannot be found.
290    pub fn add_edge_with_accessibility(
291        &mut self,
292        edge_id: EdgeId,
293        from_node_id: NodeId,
294        to_node_id: NodeId,
295        accessability: Accessability,
296    ) -> (EdgeIndex, EdgeIndex) {
297        let direction = match &accessability {
298            Accessability::ReachableNodes(_) => (Direction::Incoming, Direction::Outgoing),
299            Accessability::UnreachableNodes(_) => (Direction::Outgoing, Direction::Incoming),
300        };
301
302        let nodes = match &accessability {
303            Accessability::ReachableNodes(nodes) => nodes,
304            Accessability::UnreachableNodes(nodes) => nodes,
305        };
306
307        let u1 = self.find_node_index_with_edges(from_node_id, nodes.clone(), direction.0);
308        let v1 = self.find_node_index_with_edges(to_node_id, nodes.clone(), direction.1);
309
310        if let (Some(u1), Some(v1)) = (u1, v1) {
311            let u2 = self.get_other_toponode(u1);
312            let v2 = self.get_other_toponode(v1);
313
314            if let (Some(u2), Some(v2)) = (u2, v2) {
315                let from_node_id = *self.index_to_id.get(&u1).unwrap();
316                let to_node_id = *self.index_to_id.get(&v1).unwrap();
317
318                let topo_edge1 = TopoEdge {
319                    id: EdgeIndex::new(0), // Temporary value; will be updated
320                    from: from_node_id,
321                    to: to_node_id,
322                    edge_id,
323                };
324                let topo_edge1_id = self.graph.add_edge(u1, v1, topo_edge1);
325                self.graph.edge_weight_mut(topo_edge1_id).unwrap().id = topo_edge1_id;
326
327                let topo_edge2 = TopoEdge {
328                    id: EdgeIndex::new(0), // Temporary value; will be updated
329                    from: to_node_id,
330                    to: from_node_id,
331                    edge_id,
332                };
333                let topo_edge2_id = self.graph.add_edge(v2, u2, topo_edge2);
334                self.graph.edge_weight_mut(topo_edge2_id).unwrap().id = topo_edge2_id;
335                return (topo_edge1_id, topo_edge2_id);
336            }
337        }
338
339        unreachable!("Could not add edge with accessibility");
340    }
341
342    /// Checks if a node has an incoming edge in the topological graph.
343    ///
344    /// # Arguments
345    ///
346    /// * `node` - The ID of the node to check.
347    ///
348    /// # Returns
349    ///
350    /// * `bool` - `true` if the node has at least one incoming edge, `false` otherwise.
351    pub fn has_incoming(&self, node: NodeIndex) -> bool {
352        self.graph
353            .neighbors_directed(node, petgraph::Incoming)
354            .next()
355            .is_some()
356    }
357
358    /// Reverse the direction of a given edge.
359    ///
360    /// # Arguments
361    ///
362    /// * `edge_index` - The index of the edge to reverse.
363    ///
364    /// # Panics
365    ///
366    /// This function will panic if the edge does not exist in the graph.
367    pub fn reverse_edge(&mut self, edge_index: EdgeIndex) {
368        let (source, target) = self.graph.edge_endpoints(edge_index).unwrap();
369        let weight = self.graph.edge_weight(edge_index).unwrap().clone();
370        self.graph.remove_edge(edge_index);
371        self.graph.add_edge(target, source, weight);
372    }
373
374    /// Returns the indices of edges between two nodes in all directions.
375    ///
376    /// # Arguments
377    ///
378    /// * `node1_id` - The ID of the first node.
379    /// * `node2_id` - The ID of the second node.
380    ///
381    /// # Returns
382    ///
383    /// * `Option<(EdgeIndex, EdgeIndex)>` - The indices of the two edges between the nodes, if they exist.
384    ///
385    /// # Panics
386    ///
387    /// This function will panic if the nodes do not exist in the graph.
388    pub fn find_edge_indices(
389        &self,
390        node1_id: NodeId,
391        node2_id: NodeId,
392    ) -> Option<(EdgeIndex, EdgeIndex)> {
393        let (node1_index1, node1_index2) = self.id_to_index(node1_id).unwrap();
394        let (node2_index1, node2_index2) = self.id_to_index(node2_id).unwrap();
395
396        let mut edges = Vec::new();
397
398        for &source_index in &[node1_index1, node1_index2] {
399            for &target_index in &[node2_index1, node2_index2] {
400                if let Some(edge) = self.graph.find_edge(*source_index, *target_index) {
401                    edges.push(edge);
402                }
403                if let Some(edge) = self.graph.find_edge(*target_index, *source_index) {
404                    edges.push(edge);
405                }
406            }
407        }
408
409        if edges.len() == 2 {
410            Some((edges[0], edges[1]))
411        } else {
412            None
413        }
414    }
415
416    /// Checks if an edge is in the same direction as its neighboring edges.
417    ///
418    /// # Arguments
419    ///
420    /// * `edge_index` - The `EdgeIndex` of the edge to check.
421    ///
422    /// # Returns
423    ///
424    /// * `bool` - `true` if the edge is in the same direction as its neighboring edges, `false` otherwise.
425    ///
426    /// # Panics
427    ///
428    /// This function will panic if the `EdgeIndex` does not exist in the graph.
429    pub fn edge_is_in_neighbors_direction(&self, edge_index: EdgeIndex) -> bool {
430        let (source, target) = self.graph.edge_endpoints(edge_index).unwrap();
431
432        // Check if the source's neighbors are in the same direction
433        let source_has_same_direction = self
434            .graph
435            .neighbors_directed(source, petgraph::Direction::Incoming)
436            .any(|neighbor| neighbor != target);
437
438        let target_has_same_direction = self
439            .graph
440            .neighbors_directed(target, petgraph::Direction::Outgoing)
441            .any(|neighbor| neighbor != source);
442
443        source_has_same_direction && target_has_same_direction
444    }
445}
446
447impl Default for TopologyGraph {
448    fn default() -> Self {
449        Self::new()
450    }
451}
452
453#[cfg(test)]
454mod tests {
455    use petgraph::dot::Dot;
456
457    use super::*;
458
459    #[test]
460    fn test_topology_graph() {
461        let mut topo_graph = TopologyGraph::new();
462
463        let node_id1 = 1;
464        let node_id2 = 2;
465
466        let (added_node_id1_1, added_node_id1_2) = topo_graph.add_node(node_id1);
467        let (added_node_id2_1, added_node_id2_2) = topo_graph.add_node(node_id2);
468
469        // Each call to add_node() adds two nodes, so the total node count should be 4.
470        assert_eq!(topo_graph.graph.node_count(), 4);
471
472        let edge_id1 = 1;
473
474        let topo_edge = TopoEdge {
475            id: EdgeIndex::new(0),
476            from: node_id1,
477            to: node_id2,
478            edge_id: edge_id1,
479        };
480
481        topo_graph
482            .graph
483            .add_edge(added_node_id1_1, added_node_id2_1, topo_edge.clone());
484        topo_graph
485            .graph
486            .add_edge(added_node_id2_2, added_node_id1_2, topo_edge);
487
488        assert_eq!(topo_graph.graph.edge_count(), 2);
489
490        // Test if has_incoming works as expected
491        assert!(topo_graph.has_incoming(added_node_id2_1));
492        assert!(!topo_graph.has_incoming(added_node_id1_1));
493
494        assert!(topo_graph.has_incoming(added_node_id1_2));
495        assert!(!topo_graph.has_incoming(added_node_id2_2));
496    }
497
498    #[test]
499    fn test_no_edges_in_direction() {
500        // Create a new TopologyGraph
501        let mut graph = TopologyGraph::new();
502
503        // Define some nodes
504        let node1 = 1;
505        let node2 = 2;
506        let node3 = 3;
507
508        // Add nodes to the graph
509        let node1_id = graph.add_node(node1);
510        let node2_id = graph.add_node(node2);
511        let _node3_id = graph.add_node(node3);
512
513        // Add edges to the graph
514        let edge_id1 = 1;
515        let edge_id2 = 2;
516        graph.add_edge(edge_id1, node1, node2);
517        graph.add_edge(edge_id2, node1, node3);
518
519        // Check that there are outgoing edges from node1_id.0 to node2
520        assert!(!graph.no_edges_in_direction(node1_id.0, vec![node2], Direction::Outgoing));
521
522        // Check that there are outgoing edges from node1_id.0 to node2 and node3
523        assert!(!graph.no_edges_in_direction(node1_id.0, vec![node2, node3], Direction::Outgoing));
524
525        // Check that there are no outgoing edges from node2_id.1 to node1
526        assert!(graph.no_edges_in_direction(node2_id.0, vec![node1], Direction::Outgoing));
527    }
528
529    #[test]
530    fn test_find_node_index_with_edges() {
531        // Create a new TopologyGraph
532        let mut graph = TopologyGraph::new();
533
534        // Define some nodes and edges
535        let node1 = 1;
536        let node2 = 2;
537        let node3 = 3;
538        let node4 = 4;
539
540        // Add nodes and edges to the graph
541        let topo_node1 = graph.add_node(node1);
542        let topo_node2 = graph.add_node(node2);
543        let topo_node3 = graph.add_node(node3);
544        let topo_node4 = graph.add_node(node4);
545
546        graph.add_edge(1, node1, node2);
547        graph.add_edge(2, node1, node3);
548        graph.add_edge(3, node2, node3);
549        graph.add_edge(4, node3, node4);
550
551        // Check if the function works as expected
552        assert_eq!(
553            graph.find_node_index_with_edges(node1, vec![node2, node3], Direction::Outgoing),
554            Some(topo_node1.0)
555        );
556        assert_eq!(
557            graph.find_node_index_with_edges(node2, vec![node1, node3], Direction::Incoming),
558            Some(topo_node2.0)
559        );
560        assert_eq!(
561            graph.find_node_index_with_edges(node3, vec![node2, node4], Direction::Outgoing),
562            Some(topo_node3.0)
563        );
564        assert_eq!(
565            graph.find_node_index_with_edges(node4, vec![node3], Direction::Incoming),
566            Some(topo_node4.0)
567        );
568    }
569
570    #[test]
571    fn test_get_other_toponode() {
572        let mut topo_graph = TopologyGraph::new();
573
574        // Add some nodes to the graph
575        let node_id1: NodeId = 1;
576        let node_id2: NodeId = 2;
577
578        let (topo_node_id1_1, topo_node_id1_2) = topo_graph.add_node(node_id1);
579        let (topo_node_id2_1, topo_node_id2_2) = topo_graph.add_node(node_id2);
580
581        // Assert that get_other_toponode returns the correct other TopoNode
582        assert_eq!(
583            topo_graph.get_other_toponode(topo_node_id1_1),
584            Some(topo_node_id1_2)
585        );
586        assert_eq!(
587            topo_graph.get_other_toponode(topo_node_id1_2),
588            Some(topo_node_id1_1)
589        );
590
591        assert_eq!(
592            topo_graph.get_other_toponode(topo_node_id2_1),
593            Some(topo_node_id2_2)
594        );
595        assert_eq!(
596            topo_graph.get_other_toponode(topo_node_id2_2),
597            Some(topo_node_id2_1)
598        );
599
600        // For non-existing NodeIndex, the function should return None
601        assert_eq!(topo_graph.get_other_toponode(NodeIndex::new(100)), None);
602    }
603
604    #[test]
605    fn test_add_edge_with_accessibility() {
606        let mut topo_graph = TopologyGraph::new();
607
608        // Add some nodes to the graph
609        let node_id1 = 1;
610        let node_id2 = 2;
611        topo_graph.add_node(node_id1);
612        topo_graph.add_node(node_id2);
613
614        // Add an edge with accessibility
615        let edge_id = 1;
616        let accessability = Accessability::ReachableNodes(vec![node_id1, node_id2]);
617        let (edge_index1, edge_index2) =
618            topo_graph.add_edge_with_accessibility(edge_id, node_id1, node_id2, accessability);
619
620        // Assert that the edge has been added correctly
621        assert!(topo_graph.graph.edge_weight(edge_index1).is_some());
622        assert!(topo_graph.graph.edge_weight(edge_index2).is_some());
623        println!("{}", Dot::new(&topo_graph.graph));
624
625        let edge_indices = topo_graph.find_edge_indices(1, 2);
626        assert!(edge_indices.is_some());
627        assert_eq!(edge_indices.unwrap().0, EdgeIndex::new(0));
628    }
629
630    #[test]
631    fn test_add_edge_with_accessibility_scenario_reachable() {
632        let mut topo_graph = TopologyGraph::new();
633
634        // Add nodes to the graph
635        let node_ids: Vec<NodeId> = (0..5).collect();
636        for node_id in &node_ids {
637            topo_graph.add_node(*node_id);
638        }
639
640        // Add edge from 4 to 0
641        let edge_id = 1;
642        let accessability = Accessability::ReachableNodes(vec![node_ids[0]]);
643        topo_graph.add_edge_with_accessibility(edge_id, node_ids[4], node_ids[0], accessability);
644
645        // Add edges from 1 to 2 and 1 to 3
646        let edge_id = 2;
647        let accessability = Accessability::ReachableNodes(vec![]);
648        topo_graph.add_edge_with_accessibility(
649            edge_id,
650            node_ids[1],
651            node_ids[2],
652            accessability.clone(),
653        );
654        let edge_id = 3;
655        topo_graph.add_edge_with_accessibility(edge_id, node_ids[1], node_ids[3], accessability);
656
657        // Add edge from 0 to 1
658        let edge_id = 4;
659        let accessability =
660            Accessability::ReachableNodes(vec![node_ids[4], node_ids[2], node_ids[3]]);
661
662        topo_graph.add_edge_with_accessibility(edge_id, node_ids[0], node_ids[1], accessability);
663
664        // Assert that all edges have been added correctly
665        for i in 1..=4 {
666            let edge_index1 = EdgeIndex::new(i * 2 - 2);
667            let edge_index2 = EdgeIndex::new(i * 2 - 1);
668            assert!(topo_graph.graph.edge_weight(edge_index1).is_some());
669            assert!(topo_graph.graph.edge_weight(edge_index2).is_some());
670        }
671    }
672
673    #[test]
674    fn test_add_edge_with_accessibility_scenario_unreachable() {
675        let mut topo_graph = TopologyGraph::new();
676
677        // Add nodes to the graph
678        let node_ids: Vec<NodeId> = (0..4).collect();
679        for node_id in &node_ids {
680            topo_graph.add_node(*node_id);
681        }
682
683        let node_idx0 = *topo_graph.id_to_index.get(&node_ids[0]).unwrap();
684        let node_idx1 = *topo_graph.id_to_index.get(&node_ids[1]).unwrap();
685        let node_idx2 = *topo_graph.id_to_index.get(&node_ids[2]).unwrap();
686        let node_idx3 = *topo_graph.id_to_index.get(&node_ids[3]).unwrap();
687
688        let topo_edge = TopoEdge {
689            id: EdgeIndex::new(0),
690            from: node_ids[0],
691            to: node_ids[1],
692            edge_id: 1,
693        };
694
695        // Add edge from 0 to 1
696        topo_graph
697            .graph
698            .add_edge(node_idx0.0, node_idx1.0, topo_edge.clone());
699        topo_graph
700            .graph
701            .add_edge(node_idx1.1, node_idx0.1, topo_edge.clone());
702
703        let topo_edge = TopoEdge {
704            id: EdgeIndex::new(0),
705            from: node_ids[1],
706            to: node_ids[2],
707            edge_id: 2,
708        };
709
710        topo_graph
711            .graph
712            .add_edge(node_idx1.0, node_idx2.0, topo_edge.clone());
713        topo_graph
714            .graph
715            .add_edge(node_idx2.1, node_idx1.1, topo_edge.clone());
716
717        // Add edge from 1 to 3
718        let edge_id = 3;
719        let accessability = Accessability::UnreachableNodes(vec![node_ids[2]]);
720
721        topo_graph.add_edge_with_accessibility(edge_id, node_ids[1], node_ids[3], accessability);
722
723        assert!(
724            topo_graph
725                .graph
726                .find_edge(node_idx1.0, node_idx3.0)
727                .is_some()
728                || topo_graph
729                    .graph
730                    .find_edge(node_idx1.0, node_idx3.1)
731                    .is_some()
732        );
733
734        // Assert that all edges have been added correctly
735        for i in 1..=3 {
736            let edge_index1 = EdgeIndex::new(i * 2 - 2);
737            let edge_index2 = EdgeIndex::new(i * 2 - 1);
738            assert!(topo_graph.graph.edge_weight(edge_index1).is_some());
739            assert!(topo_graph.graph.edge_weight(edge_index2).is_some());
740        }
741    }
742
743    #[test]
744    fn test_default() {
745        let topo_graph = TopologyGraph::default();
746
747        // Ensure that the graph is empty
748        assert_eq!(topo_graph.graph.node_count(), 0);
749        assert_eq!(topo_graph.graph.edge_count(), 0);
750        assert_eq!(topo_graph.id_to_index.len(), 0);
751        assert_eq!(topo_graph.index_to_id.len(), 0);
752    }
753
754    #[test]
755    fn test_reverse_edge() {
756        let mut topo_graph = TopologyGraph::default();
757        let node1 = topo_graph.add_node(1);
758        let node2 = topo_graph.add_node(2);
759        let edge_index = topo_graph.add_edge(33, 1, 2);
760
761        // Ensure that the edge is initially from node1 to node2
762        assert_eq!(
763            topo_graph.graph.edge_endpoints(edge_index.0),
764            Some((node1.0, node2.0))
765        );
766
767        topo_graph.reverse_edge(edge_index.0);
768
769        assert_eq!(
770            topo_graph.graph.edge_endpoints(edge_index.0),
771            Some((node2.0, node1.0))
772        );
773    }
774}