Skip to main content

weavatrix_graph/matrix/
dense.rs

1use crate::Vec;
2use crate::{GraphError, NodeIndex, Result};
3use serde::{Deserialize, Deserializer, Serialize, de::Error as _};
4
5#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
6pub struct DenseMatrix<T> {
7    node_count: u32,
8    edge_count: usize,
9    cells: Vec<Option<T>>,
10}
11
12impl<T> DenseMatrix<T> {
13    /// Creates a fixed-size directed adjacency matrix.
14    ///
15    /// # Errors
16    ///
17    /// Returns an error when the dimensions exceed index or address capacity.
18    pub fn try_new(node_count: usize) -> Result<Self> {
19        let compact = u32::try_from(node_count).map_err(|_| GraphError::IndexCapacityExceeded {
20            category: "matrix nodes",
21            count: node_count,
22        })?;
23        let cells = node_count
24            .checked_mul(node_count)
25            .ok_or(GraphError::ArithmeticOverflow {
26                operation: "dense matrix dimensions",
27            })?;
28        Ok(Self {
29            node_count: compact,
30            edge_count: 0,
31            cells: core::iter::repeat_with(|| None).take(cells).collect(),
32        })
33    }
34
35    #[must_use]
36    pub const fn node_count(&self) -> usize {
37        self.node_count as usize
38    }
39
40    #[must_use]
41    pub const fn edge_count(&self) -> usize {
42        self.edge_count
43    }
44
45    #[must_use]
46    pub fn get(&self, source: NodeIndex, target: NodeIndex) -> Option<&T> {
47        self.slot(source, target)
48            .ok()
49            .and_then(|slot| self.cells[slot].as_ref())
50    }
51
52    #[must_use]
53    pub fn get_mut(&mut self, source: NodeIndex, target: NodeIndex) -> Option<&mut T> {
54        self.slot(source, target)
55            .ok()
56            .and_then(|slot| self.cells[slot].as_mut())
57    }
58
59    /// Inserts or replaces one directed edge value.
60    ///
61    /// # Errors
62    ///
63    /// Returns an error when either endpoint is outside the fixed matrix.
64    pub fn insert(&mut self, source: NodeIndex, target: NodeIndex, value: T) -> Result<Option<T>> {
65        let slot = self.slot(source, target)?;
66        let previous = self.cells[slot].replace(value);
67        self.edge_count += usize::from(previous.is_none());
68        Ok(previous)
69    }
70
71    pub fn remove(&mut self, source: NodeIndex, target: NodeIndex) -> Option<T> {
72        let slot = self.slot(source, target).ok()?;
73        let previous = self.cells[slot].take();
74        self.edge_count -= usize::from(previous.is_some());
75        previous
76    }
77
78    pub fn outgoing(&self, source: NodeIndex) -> impl Iterator<Item = (NodeIndex, &T)> {
79        let valid = source.index() < self.node_count();
80        (0..self.node_count()).filter_map(move |target| {
81            let target = NodeIndex::new(u32::try_from(target).ok()?);
82            valid
83                .then(|| self.get(source, target))
84                .flatten()
85                .map(|value| (target, value))
86        })
87    }
88
89    pub fn incoming(&self, target: NodeIndex) -> impl Iterator<Item = (NodeIndex, &T)> {
90        let valid = target.index() < self.node_count();
91        (0..self.node_count()).filter_map(move |source| {
92            let source = NodeIndex::new(u32::try_from(source).ok()?);
93            valid
94                .then(|| self.get(source, target))
95                .flatten()
96                .map(|value| (source, value))
97        })
98    }
99
100    pub fn edges(&self) -> impl Iterator<Item = (NodeIndex, NodeIndex, &T)> {
101        self.cells.iter().enumerate().filter_map(|(slot, value)| {
102            let value = value.as_ref()?;
103            let source = u32::try_from(slot / self.node_count()).ok()?;
104            let target = u32::try_from(slot % self.node_count()).ok()?;
105            Some((NodeIndex::new(source), NodeIndex::new(target), value))
106        })
107    }
108
109    fn slot(&self, source: NodeIndex, target: NodeIndex) -> Result<usize> {
110        let count = self.node_count();
111        if source.index() >= count {
112            return Err(GraphError::InvalidNodeIndex {
113                node: source.index(),
114                node_count: count,
115            });
116        }
117        if target.index() >= count {
118            return Err(GraphError::InvalidNodeIndex {
119                node: target.index(),
120                node_count: count,
121            });
122        }
123        Ok(source.index() * count + target.index())
124    }
125}
126
127impl<T: Clone> DenseMatrix<T> {
128    /// Inserts the same value in both directions.
129    ///
130    /// # Errors
131    ///
132    /// Returns an error when either endpoint is outside the fixed matrix.
133    pub fn insert_undirected(
134        &mut self,
135        left: NodeIndex,
136        right: NodeIndex,
137        value: T,
138    ) -> Result<(Option<T>, Option<T>)> {
139        let reverse = value.clone();
140        let first = self.insert(left, right, value)?;
141        let second = if left == right {
142            first.clone()
143        } else {
144            self.insert(right, left, reverse)?
145        };
146        Ok((first, second))
147    }
148}
149
150#[derive(Deserialize)]
151struct DenseWire<T> {
152    node_count: u32,
153    #[serde(rename = "edge_count")]
154    _edge_count: usize,
155    cells: Vec<Option<T>>,
156}
157
158impl<'de, T> Deserialize<'de> for DenseMatrix<T>
159where
160    T: Deserialize<'de>,
161{
162    fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
163    where
164        D: Deserializer<'de>,
165    {
166        let wire = DenseWire::deserialize(deserializer)?;
167        let node_count = wire.node_count as usize;
168        let expected = node_count
169            .checked_mul(node_count)
170            .ok_or_else(|| D::Error::custom("dense matrix dimensions overflow"))?;
171        if wire.cells.len() != expected {
172            return Err(D::Error::custom(format!(
173                "dense matrix has {} cells, expected {expected}",
174                wire.cells.len()
175            )));
176        }
177        let edge_count = wire.cells.iter().filter(|cell| cell.is_some()).count();
178        Ok(Self {
179            node_count: wire.node_count,
180            edge_count,
181            cells: wire.cells,
182        })
183    }
184}