open_hypergraphs/semifinite/
types.rs

1use crate::array::*;
2use crate::category::*;
3use crate::finite_function::FiniteFunction;
4
5use core::fmt::Debug;
6use core::ops::{Add, Shr};
7use num_traits::{One, Zero};
8
9/// A function whose *source* is finite, but whose *target* may be non-finite.
10/// This is really just an array!
11pub struct SemifiniteFunction<K: ArrayKind, T>(pub K::Type<T>);
12
13impl<K: ArrayKind, T> Clone for SemifiniteFunction<K, T>
14where
15    K::Type<T>: Clone,
16{
17    fn clone(&self) -> Self {
18        Self(self.0.clone())
19    }
20}
21
22impl<K: ArrayKind, T> SemifiniteFunction<K, T>
23where
24    K::Type<T>: Array<K, T>,
25{
26    pub fn len(&self) -> K::I {
27        self.0.len()
28    }
29
30    // An array of length 1, containing the element x.
31    pub fn singleton(x: T) -> SemifiniteFunction<K, T> {
32        SemifiniteFunction(K::Type::<T>::fill(x, K::I::one()))
33    }
34
35    pub fn coproduct(&self, other: &Self) -> Self {
36        SemifiniteFunction(self.0.concatenate(&other.0))
37    }
38}
39
40/// As a special case, a we can precompose a [`FiniteFunction`] with a [`SemifiniteFunction`].
41/// This is also overloaded with the `>>` syntax.
42pub fn compose_semifinite<K: ArrayKind, T>(
43    lhs: &FiniteFunction<K>,
44    rhs: &SemifiniteFunction<K, T>,
45) -> Option<SemifiniteFunction<K, T>>
46where
47    K::Type<T>: Array<K, T>,
48{
49    if lhs.target() != rhs.0.len() {
50        return None;
51    }
52
53    // TODO: had to reimplement composition of finite functions again.
54    let table = rhs.0.gather(lhs.table.get_range(..));
55    Some(SemifiniteFunction(table))
56}
57
58// NOTE: we can't derive PartialEq because it will introduce an unnecessary `T: PartialEq` bound
59impl<K: ArrayKind, T> PartialEq<SemifiniteFunction<K, T>> for SemifiniteFunction<K, T>
60where
61    K::Type<T>: PartialEq,
62{
63    fn eq(&self, other: &SemifiniteFunction<K, T>) -> bool {
64        self.0 == other.0
65    }
66}
67
68impl<K: ArrayKind, T> Debug for SemifiniteFunction<K, T>
69where
70    K::Type<T>: Debug,
71{
72    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73        f.debug_tuple("SemifiniteFunction").field(&self.0).finish()
74    }
75}
76
77impl<K: ArrayKind, T> Add<&SemifiniteFunction<K, T>> for &SemifiniteFunction<K, T>
78where
79    K::Type<T>: Array<K, T>,
80{
81    type Output = Option<SemifiniteFunction<K, T>>; // NOTE: redundant Option, always succeeds
82
83    fn add(self, rhs: &SemifiniteFunction<K, T>) -> Self::Output {
84        Some(self.coproduct(rhs))
85    }
86}
87
88impl<K: ArrayKind, T> Add<SemifiniteFunction<K, T>> for SemifiniteFunction<K, T>
89where
90    K::Type<T>: Array<K, T>,
91{
92    type Output = SemifiniteFunction<K, T>;
93
94    fn add(self, rhs: Self) -> Self::Output {
95        self.coproduct(&rhs)
96    }
97}
98
99impl<K: ArrayKind, T> Zero for SemifiniteFunction<K, T>
100where
101    K::Type<T>: Array<K, T>,
102{
103    fn zero() -> Self {
104        SemifiniteFunction(K::Type::<T>::empty())
105    }
106
107    fn is_zero(&self) -> bool {
108        self.0.is_empty()
109    }
110}
111
112/// A [`FiniteFunction`] can be precomposed with a [`SemifiniteFunction`] to re-index it.
113impl<K: ArrayKind, T> Shr<&SemifiniteFunction<K, T>> for &FiniteFunction<K>
114where
115    K::Type<T>: Array<K, T>,
116{
117    type Output = Option<SemifiniteFunction<K, T>>;
118
119    fn shr(self, other: &SemifiniteFunction<K, T>) -> Option<SemifiniteFunction<K, T>> {
120        compose_semifinite(self, other)
121    }
122}