Skip to main content

weavatrix_graph/matrix/
dense.rs

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