causal_hub/models/graphs/
undirected.rs1use ndarray::prelude::*;
2use serde::{
3 Deserialize, Deserializer, Serialize, Serializer,
4 de::{MapAccess, Visitor},
5 ser::SerializeMap,
6};
7
8use crate::{
9 impl_json_io,
10 models::{Graph, HasLabels},
11 types::{Error, Labels, Result, Set},
12};
13
14#[derive(Clone, Debug)]
16pub struct UnGraph {
17 labels: Labels,
18 adjacency_matrix: Array2<bool>,
19}
20
21impl UnGraph {
22 #[inline]
24 fn check_vertex(&self, x: usize) -> Result<()> {
25 if x >= self.labels.len() {
26 return Err(Error::IndexOutOfBounds(x));
27 }
28 Ok(())
29 }
30
31 pub fn neighbors(&self, x: &Set<usize>) -> Result<Set<usize>> {
46 x.iter().try_for_each(|&v| self.check_vertex(v))?;
48
49 let mut neighbors: Set<_> = x
51 .iter()
52 .flat_map(|&v| {
53 self.adjacency_matrix
54 .row(v)
55 .into_iter()
56 .enumerate()
57 .filter_map(|(y, &has_edge)| has_edge.then_some(y))
58 })
59 .collect();
60
61 neighbors.sort();
63
64 Ok(neighbors)
66 }
67}
68
69impl HasLabels for UnGraph {
70 fn labels(&self) -> &Labels {
71 &self.labels
72 }
73}
74
75impl Graph for UnGraph {
76 fn empty<I, V>(labels: I) -> Result<Self>
77 where
78 I: IntoIterator<Item = V>,
79 V: AsRef<str>,
80 {
81 let mut n = 0;
83 let mut labels: Labels = labels
85 .into_iter()
86 .inspect(|_| n += 1)
87 .map(|x| x.as_ref().to_owned())
88 .collect();
89
90 if labels.len() != n {
92 return Err(Error::NonUniqueLabels());
93 }
94
95 labels.sort();
97
98 let adjacency_matrix: Array2<_> = Array::from_elem((n, n), false);
100
101 Ok(Self {
102 labels,
103 adjacency_matrix,
104 })
105 }
106
107 fn complete<I, V>(labels: I) -> Result<Self>
108 where
109 I: IntoIterator<Item = V>,
110 V: AsRef<str>,
111 {
112 let mut graph = Self::empty(labels)?;
114 graph.adjacency_matrix.fill(true);
116 graph.adjacency_matrix.diag_mut().fill(false);
118
119 Ok(graph)
120 }
121
122 fn vertices(&self) -> Set<usize> {
123 (0..self.labels.len()).collect()
124 }
125
126 fn has_vertex(&self, x: usize) -> bool {
127 x < self.labels.len()
129 }
130
131 fn add_vertex<V>(&mut self, x: V) -> usize
132 where
133 V: AsRef<str>,
134 {
135 let x = x.as_ref().to_owned();
137 let (i, f) = self.labels.insert_full(x.clone());
139
140 if !f {
142 return i;
144 }
145
146 self.labels.sort();
148
149 debug_assert!(self.labels.contains(&x));
151 debug_assert!(self.labels.iter().is_sorted());
153
154 let i = self
158 .labels
159 .iter()
160 .filter(|&y| y.as_str() < x.as_str())
161 .count();
162
163 let n = self.adjacency_matrix.nrows();
165 let mut adjacency_matrix = Array2::from_elem((n + 1, n + 1), false);
167 adjacency_matrix
169 .slice_mut(s![0..i, 0..i])
170 .assign(&self.adjacency_matrix.slice(s![0..i, 0..i]));
171 adjacency_matrix
173 .slice_mut(s![0..i, (i + 1)..(n + 1)])
174 .assign(&self.adjacency_matrix.slice(s![0..i, i..n]));
175 adjacency_matrix
177 .slice_mut(s![(i + 1)..(n + 1), 0..i])
178 .assign(&self.adjacency_matrix.slice(s![i..n, 0..i]));
179 adjacency_matrix
181 .slice_mut(s![(i + 1)..(n + 1), (i + 1)..(n + 1)])
182 .assign(&self.adjacency_matrix.slice(s![i..n, i..n]));
183 self.adjacency_matrix = adjacency_matrix;
185
186 debug_assert_eq!(self.labels.len(), self.adjacency_matrix.nrows());
188 debug_assert!(self.adjacency_matrix.is_square());
190
191 i
193 }
194
195 fn del_vertex(&mut self, x: usize) -> bool {
196 let Some(label) = self.labels.shift_remove_index(x) else {
198 return false;
200 };
201
202 debug_assert!(!self.labels.contains(&label));
204 debug_assert!(self.labels.iter().is_sorted());
206
207 let n = self.adjacency_matrix.nrows();
209 let mut adjacency_matrix = Array2::from_elem((n - 1, n - 1), false);
211 adjacency_matrix
213 .slice_mut(s![0..x, 0..x])
214 .assign(&self.adjacency_matrix.slice(s![0..x, 0..x]));
215 adjacency_matrix
217 .slice_mut(s![0..x, x..(n - 1)])
218 .assign(&self.adjacency_matrix.slice(s![0..x, (x + 1)..n]));
219 adjacency_matrix
221 .slice_mut(s![x..(n - 1), 0..x])
222 .assign(&self.adjacency_matrix.slice(s![(x + 1)..n, 0..x]));
223 adjacency_matrix
225 .slice_mut(s![x..(n - 1), x..(n - 1)])
226 .assign(&self.adjacency_matrix.slice(s![(x + 1)..n, (x + 1)..n]));
227 self.adjacency_matrix = adjacency_matrix;
229
230 debug_assert_eq!(self.labels.len(), self.adjacency_matrix.nrows());
232 debug_assert!(self.adjacency_matrix.is_square());
234
235 true
236 }
237
238 fn edges(&self) -> Set<(usize, usize)> {
239 self.adjacency_matrix
241 .indexed_iter()
242 .filter_map(|((x, y), &has_edge)| {
243 (has_edge && x <= y).then_some((x, y))
245 })
246 .collect()
247 }
248
249 fn has_edge(&self, x: usize, y: usize) -> Result<bool> {
250 self.check_vertex(x)?;
252 self.check_vertex(y)?;
253
254 Ok(self.adjacency_matrix[[x, y]])
255 }
256
257 fn add_edge(&mut self, x: usize, y: usize) -> Result<bool> {
258 self.check_vertex(x)?;
260 self.check_vertex(y)?;
261
262 if self.adjacency_matrix[[x, y]] {
264 return Ok(false);
265 }
266
267 self.adjacency_matrix[[x, y]] = true;
269 self.adjacency_matrix[[y, x]] = true;
270
271 Ok(true)
272 }
273
274 fn del_edge(&mut self, x: usize, y: usize) -> Result<bool> {
275 self.check_vertex(x)?;
277 self.check_vertex(y)?;
278
279 if !self.adjacency_matrix[[x, y]] {
281 return Ok(false);
282 }
283
284 self.adjacency_matrix[[x, y]] = false;
286 self.adjacency_matrix[[y, x]] = false;
287
288 Ok(true)
289 }
290
291 fn select(&self, x: &Set<usize>) -> Result<Self>
292 where
293 Self: Sized,
294 {
295 x.iter().try_for_each(|&v| self.check_vertex(v))?;
297
298 let mut x = x.clone();
300 x.sort();
301
302 let labels: Labels = x.iter().map(|&v| self.labels[v].clone()).collect();
304 let mut adjacency_matrix: Array2<bool> = Array::from_elem((x.len(), x.len()), false);
306 for (i, &v_i) in x.iter().enumerate() {
308 for (j, &v_j) in x.iter().enumerate() {
309 adjacency_matrix[[i, j]] = self.adjacency_matrix[[v_i, v_j]];
310 }
311 }
312
313 Self::from_adjacency_matrix(labels, adjacency_matrix)
314 }
315
316 fn from_adjacency_matrix(
317 mut labels: Labels,
318 mut adjacency_matrix: Array2<bool>,
319 ) -> Result<Self> {
320 if labels.len() != adjacency_matrix.nrows() {
322 return Err(Error::IncompatibleShape(
323 &labels.len().to_string(),
324 &adjacency_matrix.nrows().to_string(),
325 ));
326 }
327 if adjacency_matrix.nrows() != adjacency_matrix.ncols() {
329 return Err(Error::IncompatibleShape(
330 &adjacency_matrix.nrows().to_string(),
331 &adjacency_matrix.ncols().to_string(),
332 ));
333 }
334 if adjacency_matrix != adjacency_matrix.t() {
336 return Err(Error::InvalidParameter(
337 "adjacency_matrix",
338 "Adjacency matrix must be symmetric.",
339 ));
340 }
341
342 if !labels.is_sorted() {
344 let mut indices: Vec<usize> = (0..labels.len()).collect();
346 indices.sort_by_key(|&i| &labels[i]);
348 labels.sort();
350 let mut new_adjacency_matrix = adjacency_matrix.clone();
352 indices.iter().enumerate().for_each(|(i, &j)| {
354 new_adjacency_matrix
355 .row_mut(i)
356 .assign(&adjacency_matrix.row(j));
357 });
358 adjacency_matrix = new_adjacency_matrix;
360 let mut new_adjacency_matrix = adjacency_matrix.clone();
362 indices.iter().enumerate().for_each(|(i, &j)| {
364 new_adjacency_matrix
365 .column_mut(i)
366 .assign(&adjacency_matrix.column(j));
367 });
368 adjacency_matrix = new_adjacency_matrix;
370 }
371
372 Ok(Self {
374 labels,
375 adjacency_matrix,
376 })
377 }
378
379 #[inline]
380 fn to_adjacency_matrix(&self) -> Array2<bool> {
381 self.adjacency_matrix.clone()
382 }
383}
384
385impl Serialize for UnGraph {
386 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
387 where
388 S: Serializer,
389 {
390 let edges = self
392 .edges()
393 .into_iter()
394 .map(|(x, y)| {
395 let x = self.index_to_label(x).map_err(serde::ser::Error::custom)?;
396 let y = self.index_to_label(y).map_err(serde::ser::Error::custom)?;
397 Ok((x.to_owned(), y.to_owned()))
398 })
399 .collect::<std::result::Result<Vec<_>, S::Error>>()?;
400
401 let mut map = serializer.serialize_map(Some(3))?;
403
404 map.serialize_entry("labels", &self.labels)?;
406 map.serialize_entry("edges", &edges)?;
408 map.serialize_entry("type", "ungraph")?;
410
411 map.end()
413 }
414}
415
416impl<'de> Deserialize<'de> for UnGraph {
417 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
418 where
419 D: Deserializer<'de>,
420 {
421 #[derive(Deserialize)]
422 #[serde(field_identifier, rename_all = "snake_case")]
423 enum Field {
424 Labels,
425 Edges,
426 Type,
427 }
428
429 struct UnGraphVisitor;
430
431 impl<'de> Visitor<'de> for UnGraphVisitor {
432 type Value = UnGraph;
433
434 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
435 formatter.write_str("struct UnGraph")
436 }
437
438 fn visit_map<V>(self, mut map: V) -> std::result::Result<UnGraph, V::Error>
439 where
440 V: MapAccess<'de>,
441 {
442 use serde::de::Error as E;
443
444 let mut labels = None;
446 let mut edges = None;
447 let mut type_ = None;
448
449 while let Some(key) = map.next_key()? {
451 match key {
452 Field::Labels => {
453 if labels.is_some() {
454 return Err(E::duplicate_field("labels"));
455 }
456 labels = Some(map.next_value()?);
457 }
458 Field::Edges => {
459 if edges.is_some() {
460 return Err(E::duplicate_field("edges"));
461 }
462 edges = Some(map.next_value()?);
463 }
464 Field::Type => {
465 if type_.is_some() {
466 return Err(E::duplicate_field("type"));
467 }
468 type_ = Some(map.next_value()?);
469 }
470 }
471 }
472
473 let labels = labels.ok_or_else(|| E::missing_field("labels"))?;
475 let edges = edges.ok_or_else(|| E::missing_field("edges"))?;
476
477 let type_: String = type_.ok_or_else(|| E::missing_field("type"))?;
479 if type_ != "ungraph" {
480 return Err(E::custom(format!(
481 "Invalid type for UnGraph: expected 'ungraph', found '{type_}'"
482 )));
483 }
484
485 let labels: Labels = labels;
487 let edges: Vec<(String, String)> = edges;
488 let shape = (labels.len(), labels.len());
489 let mut adjacency_matrix = Array2::from_elem(shape, false);
490 edges.into_iter().try_for_each(|(x, y)| {
491 let x = labels
492 .get_index_of(&x)
493 .ok_or_else(|| E::custom(format!("Vertex `{x}` label does not exist")))?;
494 let y = labels
495 .get_index_of(&y)
496 .ok_or_else(|| E::custom(format!("Vertex `{y}` label does not exist")))?;
497 adjacency_matrix[(x, y)] = true;
498 Ok(())
499 })?;
500
501 UnGraph::from_adjacency_matrix(labels, adjacency_matrix)
502 .map_err(|evidence| E::custom(evidence.to_string()))
503 }
504 }
505
506 const FIELDS: &[&str] = &["labels", "edges", "type"];
507
508 deserializer.deserialize_struct("UnGraph", FIELDS, UnGraphVisitor)
509 }
510}
511
512impl_json_io!(UnGraph);