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