Skip to main content

open_hypergraphs/lax/
hypergraph.rs

1use crate::array::vec::{VecArray, VecKind};
2use crate::finite_function::*;
3
4use core::fmt::Debug;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8pub struct NodeId(pub usize);
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
12pub struct EdgeId(pub usize);
13
14#[derive(Debug, Clone, PartialEq)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16pub struct Hyperedge {
17    pub sources: Vec<NodeId>,
18    pub targets: Vec<NodeId>,
19}
20
21/// Create a [`Hyperedge`] from a tuple of `(sources, targets)`.
22///
23/// This allows convenient creation of hyperedges from various collection types:
24/// ```
25/// # use open_hypergraphs::lax::{Hyperedge, NodeId};
26/// let edge: Hyperedge = (vec![NodeId(0), NodeId(1)], vec![NodeId(2)]).into();
27/// let edge: Hyperedge = ([NodeId(0), NodeId(1)], [NodeId(2)]).into();
28/// ```
29impl<S, T> From<(S, T)> for Hyperedge
30where
31    S: Into<Vec<NodeId>>,
32    T: Into<Vec<NodeId>>,
33{
34    fn from((sources, targets): (S, T)) -> Self {
35        Hyperedge {
36            sources: sources.into(),
37            targets: targets.into(),
38        }
39    }
40}
41
42pub type Interface = (Vec<NodeId>, Vec<NodeId>);
43
44/// A [`crate::lax::Hypergraph`] represents an "un-quotiented" hypergraph.
45///
46/// It can be thought of as a collection of disconnected operations and wires along with a
47/// *quotient map* which can be used with connected components to produce a `Hypergraph`.
48#[derive(Debug, Clone, PartialEq)]
49#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
50#[cfg_attr(
51    feature = "serde",
52    serde(
53        bound = "O: serde::Serialize + serde::de::DeserializeOwned, A: serde::Serialize + serde::de::DeserializeOwned"
54    )
55)]
56pub struct Hypergraph<O, A> {
57    /// Node labels. Defines a finite map from [`NodeId`] to node label
58    pub nodes: Vec<O>,
59
60    /// Edge labels. Defines a finite map from [`EdgeId`] to edge label
61    pub edges: Vec<A>,
62
63    /// Hyperedges map an *ordered list* of source nodes to an ordered list of target nodes.
64    pub adjacency: Vec<Hyperedge>,
65
66    // A finite endofunction on the set of nodes, identifying nodes to be quotiented.
67    // NOTE: this is a *graph* on the set of nodes.
68    pub quotient: (Vec<NodeId>, Vec<NodeId>),
69}
70
71impl<O, A> Hypergraph<O, A> {
72    /// The empty Hypergraph with no nodes or edges.
73    pub fn empty() -> Self {
74        Hypergraph {
75            nodes: vec![],
76            edges: vec![],
77            adjacency: vec![],
78            quotient: (vec![], vec![]),
79        }
80    }
81
82    /// Check if the quotient map is empty: if so, then this is already a strict OpenHypergraph
83    pub fn is_strict(&self) -> bool {
84        self.quotient.0.is_empty()
85    }
86
87    pub fn from_strict(h: crate::strict::hypergraph::Hypergraph<VecKind, O, A>) -> Self {
88        let mut adjacency = Vec::with_capacity(h.x.0.len());
89        for (sources, targets) in h.s.into_iter().zip(h.t.into_iter()) {
90            adjacency.push(Hyperedge {
91                sources: sources.table.iter().map(|i| NodeId(*i)).collect(),
92                targets: targets.table.iter().map(|i| NodeId(*i)).collect(),
93            })
94        }
95
96        Hypergraph {
97            nodes: h.w.0 .0,
98            edges: h.x.0 .0,
99            adjacency,
100            quotient: (vec![], vec![]),
101        }
102    }
103
104    pub fn discrete(nodes: Vec<O>) -> Self {
105        let mut h = Self::empty();
106        h.nodes = nodes;
107        h
108    }
109
110    /// Add a single node labeled `w` to the [`Hypergraph`]
111    pub fn new_node(&mut self, w: O) -> NodeId {
112        let index = self.nodes.len();
113        self.nodes.push(w);
114        NodeId(index)
115    }
116
117    /// Add a single hyperedge labeled `a` to the [`Hypergraph`]
118    /// it has sources and targets specified by `interface`
119    /// return which `EdgeId` corresponds to that new hyperedge
120    pub fn new_edge(&mut self, x: A, interface: impl Into<Hyperedge>) -> EdgeId {
121        let edge_idx = self.edges.len();
122        self.edges.push(x);
123        self.adjacency.push(interface.into());
124        EdgeId(edge_idx)
125    }
126
127    /// Append a "singleton" operation to the Hypergraph.
128    ///
129    /// 1. For each element t of `source_type` (resp. `target_type`), creates a node labeled t
130    /// 2. creates An edge labeled `x`, and sets its source/target nodes to those from step (1)
131    ///
132    /// Returns the index [`EdgeId`] of the operation in the [`Hypergraph`], and its source and
133    /// target nodes.
134    pub fn new_operation(
135        &mut self,
136        x: A,
137        source_type: Vec<O>,
138        target_type: Vec<O>,
139    ) -> (EdgeId, Interface) {
140        let sources: Vec<NodeId> = source_type.into_iter().map(|t| self.new_node(t)).collect();
141        let targets: Vec<NodeId> = target_type.into_iter().map(|t| self.new_node(t)).collect();
142        let interface = (sources.clone(), targets.clone());
143        let edge_id = self.new_edge(x, Hyperedge { sources, targets });
144        (edge_id, interface)
145    }
146
147    /// Identify a pair of nodes `(v, w)` by adding them to the quotient map.
148    ///
149    /// Note that if the labels of `v` and `w` are not equal, then this will not represent a valid
150    /// `Hypergraph`.
151    /// This is intentional so that typechecking and type inference can be deferred until after
152    /// construction of the `Hypergraph`.
153    pub fn unify(&mut self, v: NodeId, w: NodeId) {
154        // add nodes to the quotient graph
155        self.quotient.0.push(v);
156        self.quotient.1.push(w);
157    }
158
159    /// Add a new *source* node labeled `w` to edge `edge_id`.
160    pub fn add_edge_source(&mut self, edge_id: EdgeId, w: O) -> NodeId {
161        let node_id = self.new_node(w);
162        self.adjacency[edge_id.0].sources.push(node_id);
163        node_id
164    }
165
166    /// Add a new *target* node labeled `w` to edge `edge_id`
167    pub fn add_edge_target(&mut self, edge_id: EdgeId, w: O) -> NodeId {
168        let node_id = self.new_node(w);
169        self.adjacency[edge_id.0].targets.push(node_id);
170        node_id
171    }
172
173    /// Set the nodes of a Hypergraph, possibly changing types.
174    /// Returns None if new nodes array had different length.
175    pub fn with_nodes<T, F: FnOnce(Vec<O>) -> Vec<T>>(self, f: F) -> Option<Hypergraph<T, A>> {
176        let n = self.nodes.len();
177        let nodes = f(self.nodes);
178        if nodes.len() != n {
179            return None;
180        }
181
182        Some(Hypergraph {
183            nodes,
184            edges: self.edges,
185            adjacency: self.adjacency,
186            quotient: self.quotient,
187        })
188    }
189
190    /// Map the node labels of this Hypergraph, possibly changing their type
191    pub fn map_nodes<F: Fn(O) -> T, T>(self, f: F) -> Hypergraph<T, A> {
192        // note: unwrap is safe because length is preserved
193        self.with_nodes(|nodes| nodes.into_iter().map(f).collect())
194            .unwrap()
195    }
196
197    /// Set the edges of a Hypergraph, possibly changing types.
198    /// Returns None if new edges array had different length.
199    pub fn with_edges<T, F: FnOnce(Vec<A>) -> Vec<T>>(self, f: F) -> Option<Hypergraph<O, T>> {
200        let n = self.edges.len();
201        let edges = f(self.edges);
202        if edges.len() != n {
203            return None;
204        }
205
206        Some(Hypergraph {
207            nodes: self.nodes,
208            edges,
209            adjacency: self.adjacency,
210            quotient: self.quotient,
211        })
212    }
213
214    /// Map the edge labels of this Hypergraph, possibly changing their type
215    pub fn map_edges<F: Fn(A) -> T, T>(self, f: F) -> Hypergraph<O, T> {
216        // note: unwrap is safe because length is preserved
217        self.with_edges(|edges| edges.into_iter().map(f).collect())
218            .unwrap()
219    }
220
221    /// Delete the specified edges and their adjacency information.
222    ///
223    /// Panics if any edge id is out of bounds.
224    pub fn delete_edges(&mut self, edge_ids: &[EdgeId]) {
225        let edge_count = self.edges.len();
226        assert_eq!(
227            edge_count,
228            self.adjacency.len(),
229            "malformed hypergraph: edges and adjacency lengths differ"
230        );
231
232        if edge_ids.is_empty() {
233            return;
234        }
235
236        let mut remove = vec![false; edge_count];
237        let mut any_removed = false;
238        let mut remove_count = 0usize;
239        for edge_id in edge_ids {
240            assert!(
241                edge_id.0 < edge_count,
242                "edge id {:?} is out of bounds",
243                edge_id
244            );
245            if !remove[edge_id.0] {
246                remove[edge_id.0] = true;
247                any_removed = true;
248                remove_count += 1;
249            }
250        }
251
252        if !any_removed {
253            return;
254        }
255
256        let mut edges = Vec::with_capacity(edge_count - remove_count);
257        let mut adjacency = Vec::with_capacity(edge_count - remove_count);
258        for (i, (edge, adj)) in self
259            .edges
260            .drain(..)
261            .zip(self.adjacency.drain(..))
262            .enumerate()
263        {
264            if !remove[i] {
265                edges.push(edge);
266                adjacency.push(adj);
267            }
268        }
269
270        self.edges = edges;
271        self.adjacency = adjacency;
272    }
273
274    /// Renamed to `delete_edges` for consistency
275    #[deprecated(since = "0.2.10", note = "renamed delete_edges")]
276    pub fn delete_edge(&mut self, edge_ids: &[EdgeId]) {
277        self.delete_edges(edge_ids)
278    }
279
280    /// Delete the specified nodes, remapping remaining node indices in adjacency and quotient.
281    ///
282    /// Returns the renumber map: `map[old] = Some(new)` for surviving nodes, `None` for deleted.
283    /// Panics if any node id is out of bounds.
284    pub fn delete_nodes_witness(&mut self, node_ids: &[NodeId]) -> Vec<Option<usize>> {
285        if node_ids.is_empty() {
286            return (0..self.nodes.len()).map(Some).collect();
287        }
288
289        let node_count = self.nodes.len();
290        let mut remove = vec![false; node_count];
291        let mut any_removed = false;
292        let mut remove_count = 0usize;
293        for node_id in node_ids {
294            assert!(
295                node_id.0 < node_count,
296                "node id {:?} is out of bounds",
297                node_id
298            );
299            if !remove[node_id.0] {
300                remove[node_id.0] = true;
301                any_removed = true;
302                remove_count += 1;
303            }
304        }
305
306        if !any_removed {
307            return (0..node_count).map(Some).collect();
308        }
309
310        let mut new_index = vec![None; node_count];
311        let mut nodes = Vec::with_capacity(node_count - remove_count);
312        for (i, node) in self.nodes.drain(..).enumerate() {
313            if !remove[i] {
314                let next = nodes.len();
315                new_index[i] = Some(next);
316                nodes.push(node);
317            }
318        }
319        self.nodes = nodes;
320
321        for edge in &mut self.adjacency {
322            edge.sources = edge
323                .sources
324                .iter()
325                .filter_map(|node| new_index[node.0].map(NodeId))
326                .collect();
327            edge.targets = edge
328                .targets
329                .iter()
330                .filter_map(|node| new_index[node.0].map(NodeId))
331                .collect();
332        }
333
334        let mut quotient_left = Vec::with_capacity(self.quotient.0.len());
335        let mut quotient_right = Vec::with_capacity(self.quotient.1.len());
336        for (v, w) in self.quotient.0.iter().zip(self.quotient.1.iter()) {
337            if let (Some(v_new), Some(w_new)) = (new_index[v.0], new_index[w.0]) {
338                quotient_left.push(NodeId(v_new));
339                quotient_right.push(NodeId(w_new));
340            }
341        }
342        self.quotient = (quotient_left, quotient_right);
343
344        new_index
345    }
346
347    /// Delete the specified nodes, remapping remaining node indices in adjacency and quotient.
348    ///
349    /// Panics if any node id is out of bounds.
350    pub fn delete_nodes(&mut self, node_ids: &[NodeId]) {
351        self.delete_nodes_witness(node_ids);
352    }
353}
354
355impl<O: Clone + PartialEq, A: Clone> Hypergraph<O, A> {
356    /// Mutably quotient this [`Hypergraph`], returning the coequalizer calculated from
357    /// `self.quotient`.
358    /// An [`Ok`] result means the hypergraph was quotiented.
359    /// An [`Err`] means the quotient map was invalid: some quotiented nodes had inequal values.
360    pub fn quotient(&mut self) -> Result<FiniteFunction<VecKind>, FiniteFunction<VecKind>> {
361        use std::mem::take;
362        let q = self.coequalizer();
363
364        self.nodes = match coequalizer_universal(&q, &VecArray(take(&mut self.nodes))) {
365            Some(nodes) => nodes.0,
366            None => return Err(q),
367        };
368
369        // map hyperedges
370        for e in &mut self.adjacency {
371            e.sources.iter_mut().for_each(|x| *x = NodeId(q.table[x.0]));
372            e.targets.iter_mut().for_each(|x| *x = NodeId(q.table[x.0]));
373        }
374
375        // clear the quotient map (we just used it)
376        self.quotient = (vec![], vec![]); // empty
377        Ok(q)
378    }
379}
380
381impl<O: Clone, A: Clone> Hypergraph<O, A> {
382    pub fn to_hypergraph(&self) -> crate::strict::Hypergraph<VecKind, O, A> {
383        make_hypergraph(self)
384    }
385
386    pub fn coequalizer(&self) -> FiniteFunction<VecKind> {
387        // Compute the coequalizer (connected components) of the quotient graph
388        let s: FiniteFunction<VecKind> = FiniteFunction {
389            table: VecArray(self.quotient.0.iter().map(|x| x.0).collect()),
390            target: self.nodes.len(),
391        };
392
393        let t: FiniteFunction<VecKind> = FiniteFunction {
394            table: VecArray(self.quotient.1.iter().map(|x| x.0).collect()),
395            target: self.nodes.len(),
396        };
397
398        s.coequalizer(&t)
399            .expect("coequalizer must exist for any graph")
400    }
401}
402
403pub(crate) fn finite_function_coproduct(
404    v1: &[NodeId],
405    v2: &[NodeId],
406    target: usize,
407) -> Vec<NodeId> {
408    v1.iter()
409        .cloned()
410        .chain(v2.iter().map(|&s| NodeId(s.0 + target)))
411        .collect()
412}
413
414pub(crate) fn concat<T: Clone>(v1: &[T], v2: &[T]) -> Vec<T> {
415    v1.iter().cloned().chain(v2.iter().cloned()).collect()
416}
417
418impl<O: Clone, A: Clone> Hypergraph<O, A> {
419    pub(crate) fn coproduct(&self, other: &Hypergraph<O, A>) -> Hypergraph<O, A> {
420        let n = self.nodes.len();
421
422        let adjacency = self
423            .adjacency
424            .iter()
425            .cloned()
426            .chain(other.adjacency.iter().map(|edge| Hyperedge {
427                sources: edge.sources.iter().map(|&s| NodeId(s.0 + n)).collect(),
428                targets: edge.targets.iter().map(|&t| NodeId(t.0 + n)).collect(),
429            }))
430            .collect();
431
432        let quotient = (
433            finite_function_coproduct(&self.quotient.0, &other.quotient.0, n),
434            finite_function_coproduct(&self.quotient.1, &other.quotient.1, n),
435        );
436
437        Hypergraph {
438            nodes: concat(&self.nodes, &other.nodes),
439            edges: concat(&self.edges, &other.edges),
440            adjacency,
441            quotient,
442        }
443    }
444}
445
446/// Create a [`crate::strict::hypergraph::Hypergraph`] by forgetting about the quotient map.
447fn make_hypergraph<O: Clone, A: Clone>(
448    h: &Hypergraph<O, A>,
449) -> crate::strict::hypergraph::Hypergraph<VecKind, O, A> {
450    use crate::finite_function::*;
451    use crate::indexed_coproduct::*;
452    use crate::semifinite::*;
453
454    let s = {
455        let mut lengths = Vec::<usize>::with_capacity(h.edges.len());
456        let mut values = Vec::<usize>::new();
457        for e in h.adjacency.iter() {
458            lengths.push(e.sources.len());
459            values.extend(e.sources.iter().map(|x| x.0));
460        }
461
462        let sources = SemifiniteFunction(VecArray(lengths));
463        let values =
464            FiniteFunction::new(VecArray(values), h.nodes.len()).expect("invalid lax::Hypergraph!");
465        IndexedCoproduct::from_semifinite(sources, values).expect("valid IndexedCoproduct")
466    };
467
468    let t = {
469        let mut lengths = Vec::<usize>::with_capacity(h.edges.len());
470        let mut values = Vec::<usize>::new();
471        for e in h.adjacency.iter() {
472            lengths.push(e.targets.len());
473            values.extend(e.targets.iter().map(|x| x.0));
474        }
475
476        let sources = SemifiniteFunction(VecArray(lengths));
477        let values =
478            FiniteFunction::new(VecArray(values), h.nodes.len()).expect("invalid lax::Hypergraph!");
479        IndexedCoproduct::from_semifinite(sources, values).expect("valid IndexedCoproduct")
480    };
481
482    let w = SemifiniteFunction(VecArray(h.nodes.clone()));
483    let x = SemifiniteFunction(VecArray(h.edges.clone()));
484
485    crate::strict::hypergraph::Hypergraph { s, t, w, x }
486}