weavatrix_graph/matrix/bit/
mod.rs1use crate::Vec;
2use crate::{EdgeEndpoints, GraphError, NodeIndex, Result, Topology};
3use serde::{Deserialize, Deserializer, Serialize, de::Error as _};
4
5#[cfg(feature = "unsafe-fast")]
6mod unsafe_fast;
7
8#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
9pub struct BitMatrix {
10 node_count: u32,
11 edge_count: usize,
12 words: Vec<u64>,
13}
14
15impl BitMatrix {
16 pub fn try_new(node_count: usize) -> Result<Self> {
22 let compact = u32::try_from(node_count).map_err(|_| GraphError::IndexCapacityExceeded {
23 category: "bit matrix nodes",
24 count: node_count,
25 })?;
26 let cells = node_count
27 .checked_mul(node_count)
28 .ok_or(GraphError::ArithmeticOverflow {
29 operation: "bit matrix dimensions",
30 })?;
31 let word_count = cells
32 .checked_add(63)
33 .ok_or(GraphError::ArithmeticOverflow {
34 operation: "bit matrix word count",
35 })?
36 / 64;
37 Ok(Self {
38 node_count: compact,
39 edge_count: 0,
40 words: vec![0; word_count],
41 })
42 }
43
44 #[must_use]
45 pub const fn node_count(&self) -> usize {
46 self.node_count as usize
47 }
48
49 #[must_use]
50 pub const fn edge_count(&self) -> usize {
51 self.edge_count
52 }
53
54 #[must_use]
55 pub fn storage_bytes(&self) -> usize {
56 self.words.len() * size_of::<u64>()
57 }
58
59 #[must_use]
60 #[inline]
61 pub fn contains(&self, source: NodeIndex, target: NodeIndex) -> bool {
62 let count = self.node_count();
63 let source = source.index();
64 let target = target.index();
65 if source >= count || target >= count {
66 return false;
67 }
68 let slot = source * count + target;
69 self.words[slot / 64] & (1_u64 << (slot % 64)) != 0
70 }
71
72 pub fn insert(&mut self, source: NodeIndex, target: NodeIndex) -> Result<bool> {
78 let slot = self.slot(source, target)?;
79 let mask = 1_u64 << (slot % 64);
80 let word = &mut self.words[slot / 64];
81 let inserted = *word & mask == 0;
82 *word |= mask;
83 self.edge_count += usize::from(inserted);
84 Ok(inserted)
85 }
86
87 pub fn insert_undirected(&mut self, left: NodeIndex, right: NodeIndex) -> Result<usize> {
93 let first = usize::from(self.insert(left, right)?);
94 let second = usize::from(left != right && self.insert(right, left)?);
95 Ok(first + second)
96 }
97
98 pub fn remove(&mut self, source: NodeIndex, target: NodeIndex) -> bool {
99 let Ok(slot) = self.slot(source, target) else {
100 return false;
101 };
102 let mask = 1_u64 << (slot % 64);
103 let word = &mut self.words[slot / 64];
104 let removed = *word & mask != 0;
105 *word &= !mask;
106 self.edge_count -= usize::from(removed);
107 removed
108 }
109
110 pub fn outgoing(&self, source: NodeIndex) -> impl Iterator<Item = NodeIndex> + '_ {
111 let valid = source.index() < self.node_count();
112 (0..self.node_count()).filter_map(move |target| {
113 let target = NodeIndex::new(u32::try_from(target).ok()?);
114 (valid && self.contains(source, target)).then_some(target)
115 })
116 }
117
118 pub fn incoming(&self, target: NodeIndex) -> impl Iterator<Item = NodeIndex> + '_ {
119 let valid = target.index() < self.node_count();
120 (0..self.node_count()).filter_map(move |source| {
121 let source = NodeIndex::new(u32::try_from(source).ok()?);
122 (valid && self.contains(source, target)).then_some(source)
123 })
124 }
125
126 pub fn edges(&self) -> impl Iterator<Item = EdgeEndpoints> + '_ {
127 self.words
128 .iter()
129 .enumerate()
130 .flat_map(|(word, bits)| SetBits::new(word * 64, *bits))
131 .filter_map(|slot| {
132 let source = u32::try_from(slot / self.node_count()).ok()?;
133 let target = u32::try_from(slot % self.node_count()).ok()?;
134 Some(EdgeEndpoints::new(
135 NodeIndex::new(source),
136 NodeIndex::new(target),
137 ))
138 })
139 }
140
141 pub fn to_topology(&self) -> Result<Topology> {
147 Topology::try_from_edges(self.node_count(), self.edges())
148 }
149
150 fn slot(&self, source: NodeIndex, target: NodeIndex) -> Result<usize> {
151 let count = self.node_count();
152 for node in [source, target] {
153 if node.index() >= count {
154 return Err(GraphError::InvalidNodeIndex {
155 node: node.index(),
156 node_count: count,
157 });
158 }
159 }
160 Ok(source.index() * count + target.index())
161 }
162}
163
164struct SetBits {
165 base: usize,
166 bits: u64,
167}
168
169impl SetBits {
170 const fn new(base: usize, bits: u64) -> Self {
171 Self { base, bits }
172 }
173}
174
175impl Iterator for SetBits {
176 type Item = usize;
177
178 fn next(&mut self) -> Option<Self::Item> {
179 if self.bits == 0 {
180 return None;
181 }
182 let offset = usize::try_from(self.bits.trailing_zeros()).unwrap_or(usize::MAX);
183 self.bits &= self.bits - 1;
184 Some(self.base + offset)
185 }
186}
187
188#[derive(Deserialize)]
189struct BitWire {
190 node_count: u32,
191 #[serde(default, rename = "edge_count")]
192 _edge_count: usize,
193 words: Vec<u64>,
194}
195
196impl<'de> Deserialize<'de> for BitMatrix {
197 fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
198 where
199 D: Deserializer<'de>,
200 {
201 let wire = BitWire::deserialize(deserializer)?;
202 let mut matrix = Self::try_new(wire.node_count as usize).map_err(D::Error::custom)?;
203 if matrix.words.len() != wire.words.len() {
204 return Err(D::Error::custom("invalid bit matrix word count"));
205 }
206 matrix.words = wire.words;
207 let cells = matrix.node_count() * matrix.node_count();
208 if let Some(last) = matrix.words.last() {
209 let used = cells % 64;
210 if used != 0 && *last & !((1_u64 << used) - 1) != 0 {
211 return Err(D::Error::custom("bit matrix contains out-of-range bits"));
212 }
213 }
214 matrix.edge_count = matrix
215 .words
216 .iter()
217 .map(|word| usize::try_from(word.count_ones()).unwrap_or(usize::MAX))
218 .sum();
219 Ok(matrix)
220 }
221}