Skip to main content

open_hypergraphs/lax/var/
forget.rs

1//! # Forgetful functors for `Var`.
2//!
3//! The [`Forget`] functor "forgets" operations created by the [`Var`] interface while preserving connectivity.
4//! Concretely, any operation labeled [`HasVar::var`] is mapped to a *spider*:
5//! An [`OpenHypergraph`] with the same interfaces, but whose hypergraph is a single node. If the
6//! operation has no sources and targets, it's mapped to the empty [`OpenHypergraph`].
7use crate::finite_function::FiniteFunction;
8use crate::lax::functor::*;
9use crate::lax::var::*;
10use crate::lax::*;
11
12/// Replace operations labeled `HasVar::var()` with spiders, or the empty diagram.
13pub fn forget<
14    O: Clone + PartialEq + std::fmt::Debug,
15    A: Clone + PartialEq + HasVar + std::fmt::Debug,
16>(
17    f: &OpenHypergraph<O, A>,
18) -> OpenHypergraph<O, A> {
19    Forget.map_arrow(f)
20}
21
22/// Replace `1 → 1` operations labeled `HasVar::var()` with spiders, or the empty diagram.
23pub fn forget_monogamous<
24    O: Clone + PartialEq + std::fmt::Debug,
25    A: Clone + PartialEq + HasVar + std::fmt::Debug,
26>(
27    f: &OpenHypergraph<O, A>,
28) -> OpenHypergraph<O, A> {
29    ForgetMonogamous.map_arrow(f)
30}
31
32// TODO: ideally we could get the user to prove their A type is isomorphism to
33// `Either VarLabel Other`, then use this to return an OpenHypergraph over the `Other` type.
34// However, we need Clone, and this would seem to require putting a closure in the Functor struct.
35// This can be worked around by using `Hypergraph::map_nodes`.
36
37/// Functor to replace operations labeled `HasVar::var()` with spiders, or the empty diagram.
38///
39/// More precisely: if an operation labeled `HasVar::var()` has `m` sources and `n` targets, all labeled `a`,
40/// then it will be replaced with a `m → 1 ← n` spider, with all nodes labeled `a`.
41/// If `m = 0 = n`, the operation will be replaced by the *empty* diagram:
42/// this corresponds to the Extra-special frobenius axiom, which says that 0 → 0 Frobenius maps are
43/// the empty OpenHypergraph.
44/// See p2 <https://arxiv.org/pdf/1601.02307> - the "extra law".
45#[derive(Clone)]
46pub struct Forget;
47
48impl<O: Clone + PartialEq, A: HasVar + Clone + PartialEq> Functor<O, A, O, A> for Forget {
49    // Identity-on-objects
50    fn map_object(&self, o: &O) -> impl ExactSizeIterator<Item = O> {
51        std::iter::once(o.clone())
52    }
53
54    fn map_operation(&self, a: &A, source: &[O], target: &[O]) -> OpenHypergraph<O, A> {
55        // Eliminate var-labeled operations which have all their sources + targets the same type.
56        if *a == HasVar::var() && all_elements_equal(source, target) {
57            // Extra-special frobenius axiom: 0 → 0 copy is the empty diagram
58            if source.is_empty() && target.is_empty() {
59                return OpenHypergraph::empty();
60            }
61
62            // At least one must have a value
63            let label = {
64                if source.is_empty() {
65                    target[0].clone()
66                } else {
67                    source[0].clone()
68                }
69            };
70
71            let s = FiniteFunction::terminal(source.len());
72            let t = FiniteFunction::terminal(target.len());
73
74            return OpenHypergraph::<O, A>::spider(s, t, vec![label]).unwrap();
75        }
76        OpenHypergraph::singleton(a.clone(), source.to_vec(), target.to_vec())
77    }
78
79    fn map_arrow(&self, f: &OpenHypergraph<O, A>) -> OpenHypergraph<O, A> {
80        dyn_functor::define_map_arrow(self, f)
81    }
82}
83
84// Are all elements of both lists equal? (not pairwise- equivalent to reduce(equal, concat(s, t)))
85fn all_elements_equal<T: PartialEq>(a: &[T], b: &[T]) -> bool {
86    a.iter()
87        .chain(b.iter())
88        .all(|x| *x == *a.first().unwrap_or(x))
89}
90
91// not public: no use for this except via forget_monogamous
92#[derive(Clone)]
93struct ForgetMonogamous;
94
95impl<O: Clone + PartialEq + std::fmt::Debug, A: HasVar + Clone + PartialEq + std::fmt::Debug>
96    Functor<O, A, O, A> for ForgetMonogamous
97{
98    // Identity-on-objects
99    fn map_object(&self, o: &O) -> impl ExactSizeIterator<Item = O> {
100        std::iter::once(o.clone())
101    }
102
103    fn map_operation(&self, a: &A, source: &[O], target: &[O]) -> OpenHypergraph<O, A> {
104        if source.len() != 1 || target.len() != 1 {
105            return OpenHypergraph::singleton(a.clone(), source.to_vec(), target.to_vec());
106        }
107
108        // Eliminate var-labeled operations which have all their sources + targets the same type.
109        if *a == HasVar::var() && all_elements_equal(source, target) {
110            // Extra-special frobenius axiom: 0 → 0 copy is the empty diagram
111            if source.is_empty() && target.is_empty() {
112                return OpenHypergraph::empty();
113            }
114
115            // At least one must have a value
116            let label = {
117                if source.is_empty() {
118                    target[0].clone()
119                } else {
120                    source[0].clone()
121                }
122            };
123
124            let s = FiniteFunction::terminal(source.len());
125            let t = FiniteFunction::terminal(target.len());
126
127            return OpenHypergraph::<O, A>::spider(s, t, vec![label]).unwrap();
128        }
129        OpenHypergraph::singleton(a.clone(), source.to_vec(), target.to_vec())
130    }
131
132    fn map_arrow(&self, f: &OpenHypergraph<O, A>) -> OpenHypergraph<O, A> {
133        dyn_functor::define_map_arrow(self, f)
134    }
135}