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