Skip to main content

bidirected_adjacency_array/
graph.rs

1use std::iter;
2
3use bitvec::vec::BitVec;
4use permutation::Permutation;
5use tagged_vec::TaggedVec;
6
7use crate::{
8    index::{
9        DirectedEdgeIndex, DirectedNodeIndex, EdgeIndex, GraphIndexInteger, NodeIndex,
10        OptionalEdgeIndex,
11    },
12    io::gfa1::PlainGfaEdgeData,
13};
14
15#[cfg(test)]
16mod tests;
17
18#[derive(Debug)]
19pub struct BidirectedAdjacencyArray<IndexType: GraphIndexInteger, NodeData, EdgeData> {
20    /// Maps directed nodes to their edge lists.
21    ///
22    /// Each bidirected node is represented by two consecutive directed nodes.
23    /// The forward side is identified by [`DirectedNodeIndex::is_forward`].
24    ///
25    /// The last element is a sentinel value to simplify edge list iteration.
26    pub(crate) node_array: TaggedVec<DirectedNodeIndex<IndexType>, DirectedEdgeIndex<IndexType>>,
27
28    /// The edge lists for all directed nodes.
29    ///
30    /// Each bidirected edge is represented by two reverse-complemental directed edges.
31    /// Even ++ and -- self loops are represented by two distinct but same directed edges.
32    pub(crate) edge_array: TaggedVec<DirectedEdgeIndex<IndexType>, DirectedNodeIndex<IndexType>>,
33
34    /// Data associated with the nodes.
35    ///
36    /// Since each bidirected node is represented by two directed nodes,
37    /// the data for both directed nodes is stored at the same index.
38    /// Hence, the data of a directed node `n` is stored at index `n / 2`.
39    pub(crate) node_data: TaggedVec<NodeIndex<IndexType>, NodeData>,
40
41    /// Keys for finding the data associated with the edges.
42    ///
43    /// Each bidirected edge is represented by two directed edges,
44    /// and both directed edges share the same data.
45    /// However, we treat one directed edge as the "forward" direction and the other as the "reverse" direction.
46    /// We only store the data for the "forward" direction.
47    pub(crate) edge_data_keys: TaggedVec<DirectedEdgeIndex<IndexType>, EdgeDataKey<IndexType>>,
48
49    /// The actual edge data.
50    ///
51    /// This should be accessed via the `edge_data_keys`.
52    pub(crate) edge_data: TaggedVec<EdgeIndex<IndexType>, BidirectedEdgeData<IndexType, EdgeData>>,
53}
54
55#[derive(Debug, Clone, Copy)]
56pub(crate) struct EdgeDataKey<IndexType: GraphIndexInteger> {
57    inverse: DirectedEdgeIndex<IndexType>,
58    data_index: OptionalEdgeIndex<IndexType>,
59}
60
61#[derive(Debug, Clone, Copy)]
62pub(crate) struct BidirectedEdgeData<IndexType, EdgeData> {
63    forward: DirectedEdgeIndex<IndexType>,
64    reverse: DirectedEdgeIndex<IndexType>,
65    data: EdgeData,
66}
67
68pub struct DirectedEdge<IndexType> {
69    from: DirectedNodeIndex<IndexType>,
70    to: DirectedNodeIndex<IndexType>,
71    index: DirectedEdgeIndex<IndexType>,
72}
73
74pub struct DirectedEdgeDataView<'a, IndexType, EdgeData> {
75    is_forward: bool,
76    edge: EdgeIndex<IndexType>,
77    data: &'a EdgeData,
78}
79
80pub struct EdgeView<'a, IndexType, EdgeData> {
81    from: DirectedNodeIndex<IndexType>,
82    to: DirectedNodeIndex<IndexType>,
83    forward: DirectedEdgeIndex<IndexType>,
84    reverse: DirectedEdgeIndex<IndexType>,
85    data: &'a EdgeData,
86}
87
88#[derive(Debug, Clone, Hash, PartialEq, Eq)]
89pub struct BidirectedEdge<IndexType, EdgeData> {
90    pub from: NodeIndex<IndexType>,
91    /// True if this edge originates from the forward side of the `from` node.
92    pub from_forward: bool,
93    pub to: NodeIndex<IndexType>,
94    /// True if this edge terminates at the forward side of the `to` node.
95    pub to_forward: bool,
96    pub data: EdgeData,
97}
98
99impl<IndexType: GraphIndexInteger, NodeData, EdgeData>
100    BidirectedAdjacencyArray<IndexType, NodeData, EdgeData>
101{
102    /// Creates a bidirected adjacency array with the given bidirected nodes and edges.
103    ///
104    /// # Example
105    ///
106    /// ```rust
107    /// use bidirected_adjacency_array::graph::BidirectedAdjacencyArray;
108    /// use bidirected_adjacency_array::index::{NodeIndex, EdgeIndex};
109    /// use bidirected_adjacency_array::graph::BidirectedEdge;
110    /// use tagged_vec::TaggedVec;
111    ///
112    /// let nodes = TaggedVec::from(vec!['A', 'B', 'C']);
113    /// let edges = TaggedVec::from(vec![
114    ///         BidirectedEdge::new(NodeIndex::from_usize(0).into_directed_forward(), NodeIndex::from_usize(1).into_directed_forward(), 1), // A+ -> B+
115    ///         BidirectedEdge::new(NodeIndex::from_usize(1).into_directed_reverse(), NodeIndex::from_usize(2).into_directed_forward(), 2), // B- -> C+
116    /// ]);
117    /// let graph = BidirectedAdjacencyArray::<u8, _, _>::new(nodes, edges);
118    /// ```
119    pub fn new(
120        nodes: TaggedVec<NodeIndex<IndexType>, NodeData>,
121        edges: TaggedVec<EdgeIndex<IndexType>, BidirectedEdge<IndexType, EdgeData>>,
122    ) -> Self {
123        let mut node_array = TaggedVec::from_iter(iter::repeat_n(
124            DirectedEdgeIndex::from_usize(0),
125            nodes.len() * 2 + 1,
126        ));
127
128        // Count the number of outgoing edges for each directed node.
129        for edge in edges.iter_values() {
130            let from_directed_forward =
131                DirectedNodeIndex::from_bidirected(edge.from, edge.from_forward);
132            node_array[from_directed_forward].increment();
133            let from_directed_reverse =
134                DirectedNodeIndex::from_bidirected(edge.to, edge.to_forward).invert();
135            node_array[from_directed_reverse].increment();
136        }
137
138        // Convert counts to edge list limits by computing the prefix sum.
139        let directed_edge_count =
140            node_array
141                .iter_values_mut()
142                .fold(DirectedEdgeIndex::zero(), |sum, element| {
143                    let sum = sum.add(*element);
144                    *element = sum;
145                    sum
146                });
147        assert_eq!(
148            directed_edge_count,
149            node_array.iter_values().last().copied().unwrap(),
150        );
151
152        // Create edge data structures.
153        let mut edge_array = TaggedVec::from_iter(iter::repeat_n(
154            DirectedNodeIndex::from_usize(0),
155            directed_edge_count.into_usize(),
156        ));
157        let mut edge_data_keys = TaggedVec::from_iter(iter::repeat_n(
158            EdgeDataKey {
159                inverse: DirectedEdgeIndex::zero(),
160                data_index: OptionalEdgeIndex::new_none(),
161            },
162            directed_edge_count.into_usize(),
163        ));
164        let mut edge_data = TaggedVec::new();
165
166        // Now add edges by counting down the edge list limits.
167        // Afterwards, the node array will contain the correct edge list offsets.
168        for (edge_index, edge) in edges.into_iter() {
169            let from_directed_forward =
170                DirectedNodeIndex::from_bidirected(edge.from, edge.from_forward);
171            let to_directed_forward = DirectedNodeIndex::from_bidirected(edge.to, edge.to_forward);
172            let edge_index_forward = {
173                node_array[from_directed_forward].decrement();
174                node_array[from_directed_forward]
175            };
176
177            let from_directed_reverse = to_directed_forward.invert();
178            let to_directed_reverse = from_directed_forward.invert();
179            let edge_index_reverse = {
180                node_array[from_directed_reverse].decrement();
181                node_array[from_directed_reverse]
182            };
183
184            edge_array[edge_index_forward] = to_directed_forward;
185            edge_array[edge_index_reverse] = to_directed_reverse;
186
187            edge_data_keys[edge_index_forward] = EdgeDataKey {
188                inverse: edge_index_reverse,
189                data_index: edge_index.into(),
190            };
191            edge_data_keys[edge_index_reverse] = EdgeDataKey {
192                inverse: edge_index_forward,
193                data_index: OptionalEdgeIndex::new_none(),
194            };
195
196            let data_index = edge_data.push(BidirectedEdgeData {
197                forward: edge_index_forward,
198                reverse: edge_index_reverse,
199                data: edge.data,
200            });
201            assert_eq!(edge_index, data_index);
202        }
203
204        Self {
205            node_array,
206            edge_array,
207            node_data: nodes,
208            edge_data_keys,
209            edge_data,
210        }
211    }
212
213    /// Reorder the edges of the given node according to the given comparator.
214    ///
215    /// The comparator receives two different to-nodes, representing the two edges from the given node to these to-nodes.
216    pub fn reorder_edges(
217        &mut self,
218        node: DirectedNodeIndex<IndexType>,
219        comparator: impl FnMut(
220            DirectedNodeIndex<IndexType>,
221            DirectedNodeIndex<IndexType>,
222        ) -> std::cmp::Ordering,
223    ) {
224        self.reorder_edges_with_buffers(
225            node,
226            comparator,
227            &mut Permutation::one(0),
228            &mut BitVec::new(),
229        );
230    }
231
232    fn reorder_edges_with_buffers(
233        &mut self,
234        node: DirectedNodeIndex<IndexType>,
235        mut comparator: impl FnMut(
236            DirectedNodeIndex<IndexType>,
237            DirectedNodeIndex<IndexType>,
238        ) -> std::cmp::Ordering,
239        permutation: &mut Permutation,
240        bitvec: &mut BitVec,
241    ) {
242        let offset = self.node_array[node].into_usize();
243        let limit = self.node_array[node.add(DirectedNodeIndex::from_usize(1))].into_usize();
244
245        // TODO allow for different length, and construct permutation forwards maybe?
246        permutation.assign_from_sort_by(
247            &self.edge_array.as_untagged_slice()[offset..limit],
248            |a, b| comparator(*a, *b),
249        );
250
251        // Update edge array.
252        permutation
253            .apply_slice_in_place(&mut self.edge_array.as_untagged_mut_slice()[offset..limit]);
254
255        // Update edge data keys.
256        bitvec.clear();
257        bitvec.resize(limit - offset, false);
258        for edge_index in offset..limit {
259            if bitvec[edge_index - offset] {
260                continue;
261            }
262
263            let permuted_edge_index =
264                DirectedEdgeIndex::from_usize(permutation.apply_idx(edge_index - offset) + offset);
265            let edge_index = DirectedEdgeIndex::from_usize(edge_index);
266            let inverse_edge_index = self.edge_data_keys[edge_index].inverse;
267
268            self.edge_data_keys[inverse_edge_index].inverse = permuted_edge_index;
269
270            if (offset..limit).contains(&inverse_edge_index.into_usize()) {
271                // The inverse edge is from the same directed node, so we mark it as permuted, such that we don't permute it twice.
272                // This can happen for self loops.
273                bitvec.set(inverse_edge_index.into_usize() - offset, true);
274
275                let permuted_inverse_edge_index = DirectedEdgeIndex::from_usize(
276                    permutation.apply_idx(inverse_edge_index.into_usize() - offset) + offset,
277                );
278                self.edge_data_keys[edge_index].inverse = permuted_inverse_edge_index;
279            }
280        }
281
282        permutation
283            .apply_slice_in_place(&mut self.edge_data_keys.as_untagged_mut_slice()[offset..limit]);
284
285        // Update edge data.
286        bitvec.clear();
287        bitvec.resize(limit - offset, false);
288        for edge_index in offset..limit {
289            if bitvec[edge_index - offset] {
290                continue;
291            }
292
293            let edge_index = DirectedEdgeIndex::from_usize(edge_index);
294            let inverse_edge_index = self.edge_data_keys[edge_index].inverse;
295
296            let edge_data_key = self.edge_data_keys[edge_index]
297                .data_index
298                .into_option()
299                .xor(
300                    self.edge_data_keys[inverse_edge_index]
301                        .data_index
302                        .into_option(),
303                )
304                .unwrap();
305            let edge_data = &mut self.edge_data[edge_data_key];
306
307            if (offset..limit).contains(&edge_data.forward.into_usize()) {
308                edge_data.forward = DirectedEdgeIndex::from_usize(
309                    permutation.apply_idx(edge_data.forward.into_usize() - offset) + offset,
310                );
311            }
312
313            if (offset..limit).contains(&edge_data.reverse.into_usize()) {
314                edge_data.reverse = DirectedEdgeIndex::from_usize(
315                    permutation.apply_idx(edge_data.reverse.into_usize() - offset) + offset,
316                );
317            }
318
319            if (offset..limit).contains(&inverse_edge_index.into_usize()) {
320                // The inverse edge is from the same directed node, so we mark it as permuted, such that we don't permute its data twice.
321                // This can happen for self loops.
322                bitvec.set(inverse_edge_index.into_usize() - offset, true);
323            }
324        }
325    }
326
327    pub fn node_count(&self) -> usize {
328        self.node_data.len()
329    }
330
331    pub fn edge_count(&self) -> usize {
332        self.edge_data.len()
333    }
334
335    pub fn iter_nodes(&self) -> impl Iterator<Item = NodeIndex<IndexType>> {
336        self.node_data.iter_indices()
337    }
338
339    pub fn iter_edges(&self) -> impl Iterator<Item = EdgeIndex<IndexType>> {
340        self.edge_data.iter_indices()
341    }
342
343    pub fn iter_outgoing_edges(
344        &self,
345        node: DirectedNodeIndex<IndexType>,
346    ) -> impl Iterator<Item = DirectedEdge<IndexType>> {
347        let start = self.node_array[node];
348        let end = self.node_array[node.add(DirectedNodeIndex::from_usize(1))];
349        self.edge_array
350            .iter()
351            .take(end.into_usize())
352            .skip(start.into_usize())
353            .map(move |(edge_index, &to_node)| DirectedEdge {
354                from: node,
355                to: to_node,
356                index: edge_index,
357            })
358    }
359
360    /// Iterate over the bidirected edges incident to the given bidirected node.
361    pub fn iter_incident_edges(
362        &self,
363        node: NodeIndex<IndexType>,
364    ) -> impl Iterator<Item = EdgeIndex<IndexType>> {
365        let forward_node = DirectedNodeIndex::from_bidirected(node, true);
366        let reverse_node = DirectedNodeIndex::from_bidirected(node, false);
367        self.iter_outgoing_edges(forward_node)
368            .chain(self.iter_outgoing_edges(reverse_node))
369            .filter_map(|directed_edge| {
370                let directed_edge_data = self.directed_edge_data(directed_edge.index());
371                if directed_edge.from() == directed_edge.to()
372                    || directed_edge.from() == directed_edge.to().invert()
373                {
374                    directed_edge_data
375                        .is_forward()
376                        .then_some(directed_edge_data.edge())
377                } else {
378                    Some(directed_edge_data.edge())
379                }
380            })
381    }
382
383    pub fn node_data(&self, node: NodeIndex<IndexType>) -> &NodeData {
384        &self.node_data[node]
385    }
386
387    pub fn edge(&self, edge: EdgeIndex<IndexType>) -> EdgeView<'_, IndexType, EdgeData> {
388        let bidirected_edge_data = &self.edge_data[edge];
389
390        let forward_to = self.edge_array[bidirected_edge_data.forward];
391        let reverse_to = self.edge_array[bidirected_edge_data.reverse];
392
393        if forward_to == reverse_to {
394            // ++ or -- self loop case: both directed edges go from node to its reverse.
395            let from = forward_to.invert();
396            let to = forward_to;
397            EdgeView {
398                from,
399                to,
400                forward: bidirected_edge_data.forward,
401                reverse: bidirected_edge_data.reverse,
402                data: &bidirected_edge_data.data,
403            }
404        } else if forward_to.invert() == reverse_to {
405            // +- or -+ self loop case: directed edges are self loops.
406            let from = forward_to;
407            let to = forward_to;
408            EdgeView {
409                from,
410                to,
411                forward: bidirected_edge_data.forward,
412                reverse: bidirected_edge_data.reverse,
413                data: &bidirected_edge_data.data,
414            }
415        } else {
416            // Normal case: directed edges go between two different nodes.
417            let from = reverse_to.invert();
418            let to = forward_to;
419            EdgeView {
420                from,
421                to,
422                forward: bidirected_edge_data.forward,
423                reverse: bidirected_edge_data.reverse,
424                data: &bidirected_edge_data.data,
425            }
426        }
427    }
428
429    pub fn directed_edge_data<'this>(
430        &'this self,
431        directed_edge: DirectedEdgeIndex<IndexType>,
432    ) -> DirectedEdgeDataView<'this, IndexType, EdgeData> {
433        let key = &self.edge_data_keys[directed_edge];
434        if let Some(edge) = key.data_index.into_option() {
435            DirectedEdgeDataView {
436                is_forward: true,
437                edge,
438                data: &self.edge_data[edge].data,
439            }
440        } else {
441            let inverse_key = &self.edge_data_keys[key.inverse];
442            let Some(edge) = inverse_key.data_index.into_option() else {
443                panic!(
444                    "Edge data for edge {:?} and its inverse {:?} are both missing",
445                    directed_edge, key.inverse
446                );
447            };
448            DirectedEdgeDataView {
449                is_forward: false,
450                edge,
451                data: &self.edge_data[edge].data,
452            }
453        }
454    }
455
456    pub fn directed_edge_into_bidirected(
457        &self,
458        directed_edge: DirectedEdgeIndex<IndexType>,
459    ) -> EdgeIndex<IndexType> {
460        let key = &self.edge_data_keys[directed_edge];
461        if let Some(edge) = key.data_index.into_option() {
462            edge
463        } else {
464            let inverse_key = &self.edge_data_keys[key.inverse];
465            inverse_key
466                .data_index
467                .expect("Edge data for directed edge and its inverse are both missing")
468        }
469    }
470}
471
472impl<IndexType> DirectedEdge<IndexType> {
473    pub fn from(&self) -> DirectedNodeIndex<IndexType>
474    where
475        IndexType: Copy,
476    {
477        self.from
478    }
479
480    pub fn to(&self) -> DirectedNodeIndex<IndexType>
481    where
482        IndexType: Copy,
483    {
484        self.to
485    }
486
487    pub fn index(&self) -> DirectedEdgeIndex<IndexType>
488    where
489        IndexType: Copy,
490    {
491        self.index
492    }
493}
494
495impl<'a, IndexType, EdgeData> DirectedEdgeDataView<'a, IndexType, EdgeData> {
496    pub fn is_forward(&self) -> bool {
497        self.is_forward
498    }
499
500    pub fn edge(&self) -> EdgeIndex<IndexType>
501    where
502        IndexType: Copy,
503    {
504        self.edge
505    }
506
507    pub fn data(&self) -> &EdgeData {
508        self.data
509    }
510}
511
512impl<'a, IndexType, EdgeData> EdgeView<'a, IndexType, EdgeData> {
513    pub fn from(&self) -> DirectedNodeIndex<IndexType>
514    where
515        IndexType: Copy,
516    {
517        self.from
518    }
519
520    pub fn to(&self) -> DirectedNodeIndex<IndexType>
521    where
522        IndexType: Copy,
523    {
524        self.to
525    }
526
527    pub fn forward(&self) -> DirectedEdgeIndex<IndexType>
528    where
529        IndexType: Copy,
530    {
531        self.forward
532    }
533
534    pub fn reverse(&self) -> DirectedEdgeIndex<IndexType>
535    where
536        IndexType: Copy,
537    {
538        self.reverse
539    }
540
541    pub fn data(&self) -> &EdgeData {
542        self.data
543    }
544}
545
546impl<IndexType: GraphIndexInteger, EdgeData> BidirectedEdge<IndexType, EdgeData> {
547    pub fn new(
548        from: DirectedNodeIndex<IndexType>,
549        to: DirectedNodeIndex<IndexType>,
550        data: EdgeData,
551    ) -> Self {
552        Self {
553            from: from.into_bidirected(),
554            from_forward: from.is_forward(),
555            to: to.into_bidirected(),
556            to_forward: to.is_forward(),
557            data,
558        }
559    }
560}
561
562impl<IndexType: GraphIndexInteger> BidirectedEdge<IndexType, PlainGfaEdgeData> {
563    pub fn new_gfa(
564        from: DirectedNodeIndex<IndexType>,
565        to: DirectedNodeIndex<IndexType>,
566        overlap: u16,
567    ) -> Self {
568        Self {
569            from: from.into_bidirected(),
570            from_forward: from.is_forward(),
571            to: to.into_bidirected(),
572            to_forward: to.is_forward(),
573            data: PlainGfaEdgeData::new(overlap),
574        }
575    }
576}