weavatrix_graph/undirected/
core.rs1use super::{IndexUndirectedGraphView, UndirectedGraphView};
2use crate::Vec;
3use crate::topology::csr::Csr;
4use crate::{EdgeEndpoints, EdgeIndex, GraphError, NodeIndex, Result};
5use serde::{Deserialize, Deserializer, Serialize, de::Error as _};
6
7#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
8pub struct UndirectedTopology {
9 node_count: u32,
10 endpoints: Vec<EdgeEndpoints>,
11 #[serde(skip)]
12 incidence: Csr,
13}
14
15impl UndirectedTopology {
16 pub fn try_from_edges(
24 node_count: usize,
25 edges: impl IntoIterator<Item = EdgeEndpoints>,
26 ) -> Result<Self> {
27 let compact_node_count =
28 u32::try_from(node_count).map_err(|_| GraphError::IndexCapacityExceeded {
29 category: "nodes",
30 count: node_count,
31 })?;
32 let endpoints = edges.into_iter().collect::<Vec<_>>();
33 u32::try_from(endpoints.len()).map_err(|_| GraphError::IndexCapacityExceeded {
34 category: "edges",
35 count: endpoints.len(),
36 })?;
37 let incidence = Csr::try_build_undirected(node_count, &endpoints)?;
38 Ok(Self {
39 node_count: compact_node_count,
40 endpoints,
41 incidence,
42 })
43 }
44
45 #[must_use]
46 pub const fn node_count(&self) -> usize {
47 self.node_count as usize
48 }
49
50 #[must_use]
51 pub const fn edge_count(&self) -> usize {
52 self.endpoints.len()
53 }
54
55 #[must_use]
56 pub fn contains_node(&self, node: NodeIndex) -> bool {
57 node.index() < self.node_count()
58 }
59
60 #[must_use]
61 pub fn contains_edge(&self, edge: EdgeIndex) -> bool {
62 edge.index() < self.edge_count()
63 }
64
65 #[must_use]
66 pub fn edge_endpoints(&self, edge: EdgeIndex) -> Option<EdgeEndpoints> {
67 self.endpoints.get(edge.index()).copied()
68 }
69
70 #[must_use]
71 pub fn incident_edges(
72 &self,
73 node: NodeIndex,
74 ) -> impl DoubleEndedIterator<Item = EdgeIndex> + ExactSizeIterator + '_ {
75 self.incidence.get(node.index()).iter().copied()
76 }
77
78 #[must_use]
79 pub fn incident_edge_at(&self, node: NodeIndex, offset: usize) -> Option<EdgeIndex> {
80 self.incidence.get(node.index()).get(offset).copied()
81 }
82
83 #[must_use]
84 pub fn neighbors(
85 &self,
86 node: NodeIndex,
87 ) -> impl DoubleEndedIterator<Item = NodeIndex> + ExactSizeIterator + '_ {
88 self.incident_edges(node).map(move |edge| {
89 let endpoints = self.endpoints[edge.index()];
90 if endpoints.source() == node {
91 endpoints.target()
92 } else {
93 endpoints.source()
94 }
95 })
96 }
97
98 #[must_use]
99 pub fn degree(&self, node: NodeIndex) -> Option<usize> {
100 self.contains_node(node).then(|| {
101 self.incident_edges(node)
102 .map(|edge| {
103 let endpoints = self.endpoints[edge.index()];
104 usize::from(endpoints.source() == node && endpoints.target() == node)
105 })
106 .sum::<usize>()
107 + self.incident_edges(node).len()
108 })
109 }
110}
111
112impl UndirectedGraphView for UndirectedTopology {
113 type Node = NodeIndex;
114 type Edge = EdgeIndex;
115
116 fn node_count(&self) -> usize {
117 self.node_count()
118 }
119
120 fn edge_count(&self) -> usize {
121 self.edge_count()
122 }
123
124 fn contains_node(&self, node: NodeIndex) -> bool {
125 self.contains_node(node)
126 }
127
128 fn contains_edge(&self, edge: EdgeIndex) -> bool {
129 self.contains_edge(edge)
130 }
131
132 fn node_indices(&self) -> impl Iterator<Item = NodeIndex> + '_ {
133 (0..self.node_count).map(NodeIndex::new)
134 }
135
136 fn edge_indices(&self) -> impl Iterator<Item = EdgeIndex> + '_ {
137 (0..u32::try_from(self.edge_count()).expect("edge count checked")).map(EdgeIndex::new)
138 }
139
140 fn edge_endpoints(&self, edge: EdgeIndex) -> Option<EdgeEndpoints> {
141 self.edge_endpoints(edge)
142 }
143
144 fn incident_edges(
145 &self,
146 node: NodeIndex,
147 ) -> impl DoubleEndedIterator<Item = EdgeIndex> + ExactSizeIterator + '_ {
148 self.incident_edges(node)
149 }
150}
151
152impl IndexUndirectedGraphView for UndirectedTopology {
153 fn node_bound(&self) -> usize {
154 self.node_count()
155 }
156
157 fn edge_bound(&self) -> usize {
158 self.edge_count()
159 }
160
161 fn node_slot(node: NodeIndex) -> usize {
162 node.index()
163 }
164
165 fn edge_slot(edge: EdgeIndex) -> usize {
166 edge.index()
167 }
168
169 fn incident_edge_at(&self, node: NodeIndex, offset: usize) -> Option<EdgeIndex> {
170 self.incident_edge_at(node, offset)
171 }
172}
173
174#[derive(Deserialize)]
175struct UndirectedWire {
176 node_count: u32,
177 endpoints: Vec<EdgeEndpoints>,
178}
179
180impl<'de> Deserialize<'de> for UndirectedTopology {
181 fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
182 where
183 D: Deserializer<'de>,
184 {
185 let wire = UndirectedWire::deserialize(deserializer)?;
186 Self::try_from_edges(wire.node_count as usize, wire.endpoints).map_err(D::Error::custom)
187 }
188}