1use crate::array::{Array, ArrayKind, NaturalArray};
2use crate::category::*;
3use crate::finite_function::{coequalizer_universal, FiniteFunction};
4use crate::indexed_coproduct::*;
5use crate::operations::Operations;
6use crate::semifinite::*;
7#[cfg(feature = "experimental")]
8use crate::strict::hypergraph::arrow::HypergraphArrow;
9
10use core::fmt::Debug;
11use core::ops::Add;
12use num_traits::Zero;
13
14#[derive(Debug)]
15pub enum InvalidHypergraph<K: ArrayKind> {
16 SourcesCount(K::I, K::I),
17 TargetsCount(K::I, K::I),
18 SourcesSet(K::I, K::I),
19 TargetsSet(K::I, K::I),
20}
21
22pub struct Hypergraph<K: ArrayKind, O, A> {
23 pub s: IndexedCoproduct<K, FiniteFunction<K>>,
24 pub t: IndexedCoproduct<K, FiniteFunction<K>>,
25 pub w: SemifiniteFunction<K, O>,
26 pub x: SemifiniteFunction<K, A>,
27}
28
29impl<K: ArrayKind, O, A> Hypergraph<K, O, A>
30where
31 K::Type<K::I>: AsRef<K::Index>,
32 K::Type<K::I>: NaturalArray<K>,
33 K::Type<O>: Array<K, O>,
34 K::Type<A>: Array<K, A>,
35{
36 pub fn new(
38 s: IndexedCoproduct<K, FiniteFunction<K>>,
39 t: IndexedCoproduct<K, FiniteFunction<K>>,
40 w: SemifiniteFunction<K, O>,
41 x: SemifiniteFunction<K, A>,
42 ) -> Result<Hypergraph<K, O, A>, InvalidHypergraph<K>> {
43 let h = Hypergraph { s, t, w, x };
44 h.validate()
45 }
46
47 pub fn validate(self) -> Result<Self, InvalidHypergraph<K>> {
53 let n_x = self.x.len();
55 let n_w = self.w.len();
56
57 if self.s.len() != self.x.len() {
59 return Err(InvalidHypergraph::SourcesCount(self.s.len(), n_x));
60 }
61
62 if self.t.len() != self.x.len() {
64 return Err(InvalidHypergraph::TargetsCount(self.t.len(), n_x));
65 }
66
67 let n_s = self.s.values.target();
69 if n_s != n_w {
70 return Err(InvalidHypergraph::SourcesSet(n_s, n_w));
71 }
72
73 let n_t = self.t.values.target();
75 if n_t != n_w {
76 return Err(InvalidHypergraph::TargetsSet(n_t, n_w));
77 }
78
79 Ok(self)
80 }
81
82 pub fn empty() -> Hypergraph<K, O, A> {
85 Hypergraph {
86 s: IndexedCoproduct::initial(K::I::zero()),
87 t: IndexedCoproduct::initial(K::I::zero()),
88 w: SemifiniteFunction::zero(),
89 x: SemifiniteFunction::zero(),
90 }
91 }
92
93 pub fn discrete(w: SemifiniteFunction<K, O>) -> Hypergraph<K, O, A> {
95 Hypergraph {
96 s: IndexedCoproduct::initial(w.len()),
97 t: IndexedCoproduct::initial(w.len()),
98 w,
99 x: SemifiniteFunction::zero(),
100 }
101 }
102
103 pub fn is_discrete(&self) -> bool {
104 self.s.is_empty() && self.t.is_empty() && self.x.0.is_empty()
105 }
106
107 pub fn coproduct(&self, other: &Self) -> Self {
108 Hypergraph {
109 s: self.s.tensor(&other.s),
110 t: self.t.tensor(&other.t),
111 w: self.w.coproduct(&other.w),
112 x: self.x.coproduct(&other.x),
113 }
114 }
115
116 pub fn tensor_operations(Operations { x, a, b }: Operations<K, O, A>) -> Hypergraph<K, O, A> {
117 let inj0 = FiniteFunction::inj0(a.values.len(), b.values.len());
119 let inj1 = FiniteFunction::inj1(a.values.len(), b.values.len());
120 let s = IndexedCoproduct::new(a.sources, inj0).expect("invalid Operations?");
121 let t = IndexedCoproduct::new(b.sources, inj1).expect("invalid Operations?");
122 let w = a.values + b.values;
123 Hypergraph { s, t, w, x }
124 }
125}
126
127impl<K: ArrayKind, O, A> Hypergraph<K, O, A>
128where
129 K::Type<K::I>: NaturalArray<K>,
130 K::Type<O>: Array<K, O>,
131{
132 pub fn in_degree(&self, node: K::I) -> K::I {
134 let node = node.clone();
135 assert!(node < self.w.len(), "node id {:?} is out of bounds", node);
136 let counts = (self.t.values.table.as_ref() as &K::Type<K::I>).bincount(self.w.len());
137 counts.get(node)
138 }
139
140 pub fn out_degree(&self, node: K::I) -> K::I {
142 let node = node.clone();
143 assert!(node < self.w.len(), "node id {:?} is out of bounds", node);
144 let counts = (self.s.values.table.as_ref() as &K::Type<K::I>).bincount(self.w.len());
145 counts.get(node)
146 }
147}
148
149impl<K: ArrayKind, O, A> Hypergraph<K, O, A>
150where
151 K::Type<K::I>: AsRef<K::Index>,
152 K::Type<K::I>: NaturalArray<K>,
153 K::Type<O>: Array<K, O> + PartialEq,
154 K::Type<A>: Array<K, A>,
155{
156 pub fn coequalize_vertices(&self, q: &FiniteFunction<K>) -> Option<Hypergraph<K, O, A>> {
157 let s = self.s.map_values(q)?;
159 let t = self.t.map_values(q)?;
160 let w = SemifiniteFunction(coequalizer_universal(q, &self.w.0)?);
161 let x = self.x.clone();
162 Some(Hypergraph { s, t, w, x })
163 }
164
165 #[cfg(feature = "experimental")]
167 pub(crate) fn pushout_along_span(
168 left: &Hypergraph<K, O, A>,
169 right: &Hypergraph<K, O, A>,
170 f: &FiniteFunction<K>,
171 g: &FiniteFunction<K>,
172 ) -> Option<(
173 Hypergraph<K, O, A>,
174 HypergraphArrow<K, O, A>,
175 HypergraphArrow<K, O, A>,
176 )>
177 where
178 K::Type<K::I>: NaturalArray<K>,
179 K::Type<O>: Array<K, O> + PartialEq,
180 K::Type<A>: Array<K, A> + PartialEq,
181 {
182 if f.target() != left.w.len() || g.target() != right.w.len() {
183 return None;
184 }
185
186 let f_lift = f.inject0(right.w.len());
189 let g_lift = g.inject1(left.w.len());
190 let q = f_lift.coequalizer(&g_lift)?;
191 let coproduct = left + right;
193 let target = coproduct.coequalize_vertices(&q)?;
194
195 let w_left = FiniteFunction::inj0(left.w.len(), right.w.len()).compose(&q)?;
197 let w_right = FiniteFunction::inj1(left.w.len(), right.w.len()).compose(&q)?;
198 let x_left = FiniteFunction::inj0(left.x.len(), right.x.len());
199 let x_right = FiniteFunction::inj1(left.x.len(), right.x.len());
200
201 let left_arrow = HypergraphArrow::new(left.clone(), target.clone(), w_left, x_left).ok()?;
202 let right_arrow =
203 HypergraphArrow::new(right.clone(), target.clone(), w_right, x_right).ok()?;
204
205 Some((target, left_arrow, right_arrow))
206 }
207}
208
209impl<K: ArrayKind, O, A> Add<&Hypergraph<K, O, A>> for &Hypergraph<K, O, A>
210where
211 K::Type<K::I>: NaturalArray<K>,
212 K::Type<O>: Array<K, O>,
213 K::Type<A>: Array<K, A>,
214{
215 type Output = Hypergraph<K, O, A>;
216
217 fn add(self, rhs: &Hypergraph<K, O, A>) -> Self::Output {
218 self.coproduct(rhs)
219 }
220}
221
222impl<K: ArrayKind, O, A> Clone for Hypergraph<K, O, A>
223where
224 K::Type<K::I>: Clone,
225 K::Type<O>: Clone,
226 K::Type<A>: Clone,
227{
228 fn clone(&self) -> Self {
229 Self {
230 s: self.s.clone(),
231 t: self.t.clone(),
232 w: self.w.clone(),
233 x: self.x.clone(),
234 }
235 }
236}
237
238impl<K: ArrayKind, O: Debug, A: Debug> Debug for Hypergraph<K, O, A>
240where
241 K::Index: Debug,
242 K::Type<K::I>: Debug,
243 K::Type<O>: Debug,
244 K::Type<A>: Debug,
245{
246 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
247 f.debug_struct("Hypergraph")
248 .field("s", &self.s)
249 .field("t", &self.t)
250 .field("w", &self.w)
251 .field("x", &self.x)
252 .finish()
253 }
254}