catlog/dbl/discrete_tabulator/
model.rs1use std::rc::Rc;
4
5use derivative::Derivative;
6use derive_more::From;
7
8use super::theory::*;
9use crate::dbl::{category::*, model::*, theory::DblTheory};
10use crate::validate::{self, Validate};
11use crate::zero::pretty::*;
12use crate::{one::*, zero::*};
13
14#[derive(Clone, PartialEq, Debug, Eq, From)]
16pub enum TabOb {
17 #[from]
19 Basic(QualifiedName),
20
21 Tabulated(Box<TabMor>),
23}
24
25impl TabOb {
26 pub fn basic(self) -> Option<QualifiedName> {
28 match self {
29 TabOb::Basic(id) => Some(id),
30 _ => None,
31 }
32 }
33
34 pub fn tabulated(self) -> Option<TabMor> {
36 match self {
37 TabOb::Tabulated(mor) => Some(*mor),
38 _ => None,
39 }
40 }
41
42 pub fn unwrap_basic(self) -> QualifiedName {
44 self.basic().expect("Object should be a basic object")
45 }
46
47 pub fn unwrap_tabulated(self) -> TabMor {
49 self.tabulated().expect("Object should be a tabulated morphism")
50 }
51}
52
53#[derive(Clone, PartialEq, Debug, Eq, From)]
57pub enum TabEdge {
58 #[from]
60 Basic(QualifiedName),
61
62 Square {
64 dom: Box<TabMor>,
66
67 cod: Box<TabMor>,
69
70 pre: Box<TabEdge>,
72
73 post: Box<TabEdge>,
75 },
76}
77
78pub type TabMor = Path<TabOb, TabEdge>;
80
81impl From<QualifiedName> for TabMor {
82 fn from(value: QualifiedName) -> Self {
83 Path::single(value.into())
84 }
85}
86
87#[derive(Clone, Default, PartialEq, Eq)]
88struct DiscreteTabGenerators {
89 objects: HashFinSet<QualifiedName>,
90 morphisms: HashFinSet<QualifiedName>,
91 dom: HashColumn<QualifiedName, TabOb>,
92 cod: HashColumn<QualifiedName, TabOb>,
93}
94
95impl Graph for DiscreteTabGenerators {
96 type V = TabOb;
97 type E = TabEdge;
98
99 fn has_vertex(&self, ob: &Self::V) -> bool {
100 match ob {
101 TabOb::Basic(v) => self.objects.contains(v),
102 TabOb::Tabulated(p) => (*p).contained_in(self),
103 }
104 }
105
106 fn has_edge(&self, edge: &Self::E) -> bool {
107 match edge {
108 TabEdge::Basic(e) => {
109 self.morphisms.contains(e) && self.dom.is_set(e) && self.cod.is_set(e)
110 }
111 TabEdge::Square { dom, cod, pre, post } => {
112 if !(dom.contained_in(self) && cod.contained_in(self)) {
113 return false;
114 }
115 let path1 = dom.clone().concat_in(self, Path::single(*post.clone()));
116 let path2 = Path::single(*pre.clone()).concat_in(self, *cod.clone());
117 path1.is_some() && path2.is_some() && path1 == path2
118 }
119 }
120 }
121
122 fn src(&self, edge: &Self::E) -> Self::V {
123 match edge {
124 TabEdge::Basic(e) => {
125 self.dom.apply_to_ref(e).expect("Domain of morphism should be defined")
126 }
127 TabEdge::Square { dom, .. } => TabOb::Tabulated(dom.clone()),
128 }
129 }
130
131 fn tgt(&self, edge: &Self::E) -> Self::V {
132 match edge {
133 TabEdge::Basic(e) => {
134 self.cod.apply_to_ref(e).expect("Codomain of morphism should be defined")
135 }
136 TabEdge::Square { cod, .. } => TabOb::Tabulated(cod.clone()),
137 }
138 }
139}
140
141#[derive(Clone, Derivative)]
148#[derivative(PartialEq, Eq)]
149pub struct DiscreteTabModel {
150 #[derivative(PartialEq(compare_with = "Rc::ptr_eq"))]
151 theory: Rc<DiscreteTabTheory>,
152 generators: DiscreteTabGenerators,
153 equations: [(); 0], ob_types: IndexedHashColumn<QualifiedName, TabObType>,
155 mor_types: IndexedHashColumn<QualifiedName, TabMorType>,
156}
157
158impl DiscreteTabModel {
159 pub fn new(theory: Rc<DiscreteTabTheory>) -> Self {
161 Self {
162 theory,
163 generators: Default::default(),
164 equations: Default::default(),
165 ob_types: Default::default(),
166 mor_types: Default::default(),
167 }
168 }
169
170 pub fn tabulated(&self, mor: TabMor) -> TabOb {
172 TabOb::Tabulated(Box::new(mor))
173 }
174
175 pub fn tabulated_gen(&self, f: QualifiedName) -> TabOb {
177 self.tabulated(Path::single(TabEdge::Basic(f)))
178 }
179
180 pub fn iter_invalid(&self) -> impl Iterator<Item = InvalidDblModel> + '_ {
182 type Invalid = InvalidDblModel;
183 let ob_errors = self.generators.objects.iter().filter_map(|x| {
184 if self.ob_types.get(&x).is_some_and(|typ| self.theory.has_ob_type(typ)) {
185 None
186 } else {
187 Some(Invalid::ObType(x))
188 }
189 });
190 let mor_errors = self.generators.morphisms.iter().flat_map(|e| {
191 let mut errs = Vec::new();
192 let dom = self.generators.dom.get(&e).filter(|x| self.has_ob(x));
193 let cod = self.generators.cod.get(&e).filter(|x| self.has_ob(x));
194 if dom.is_none() {
195 errs.push(Invalid::Dom(e.clone()));
196 }
197 if cod.is_none() {
198 errs.push(Invalid::Cod(e.clone()));
199 }
200 if let Some(mor_type) =
201 self.mor_types.get(&e).filter(|typ| self.theory.has_mor_type(typ))
202 {
203 if dom.is_some_and(|x| self.ob_type(x) != self.theory.src(mor_type)) {
204 errs.push(Invalid::DomType(e.clone()));
205 }
206 if cod.is_some_and(|x| self.ob_type(x) != self.theory.tgt(mor_type)) {
207 errs.push(Invalid::CodType(e.clone()));
208 }
209 } else {
210 errs.push(Invalid::MorType(e));
211 }
212 errs.into_iter()
213 });
214 ob_errors.chain(mor_errors)
215 }
216}
217
218impl Category for DiscreteTabModel {
219 type Ob = TabOb;
220 type Mor = TabMor;
221
222 fn has_ob(&self, x: &Self::Ob) -> bool {
223 self.generators.has_vertex(x)
224 }
225 fn has_mor(&self, path: &Self::Mor) -> bool {
226 path.contained_in(&self.generators)
227 }
228 fn dom(&self, path: &Self::Mor) -> Self::Ob {
229 path.src(&self.generators)
230 }
231 fn cod(&self, path: &Self::Mor) -> Self::Ob {
232 path.tgt(&self.generators)
233 }
234
235 fn compose(&self, path: Path<Self::Ob, Self::Mor>) -> Self::Mor {
236 path.flatten_in(&self.generators).expect("Paths should be composable")
237 }
238}
239
240impl FgCategory for DiscreteTabModel {
241 type ObGen = QualifiedName;
242 type MorGen = QualifiedName;
243
244 fn ob_generators(&self) -> impl Iterator<Item = Self::ObGen> {
245 self.generators.objects.iter()
246 }
247 fn mor_generators(&self) -> impl Iterator<Item = Self::MorGen> {
248 self.generators.morphisms.iter()
249 }
250
251 fn mor_generator_dom(&self, f: &Self::MorGen) -> Self::Ob {
252 self.generators.dom.apply_to_ref(f).expect("Domain should be defined")
253 }
254 fn mor_generator_cod(&self, f: &Self::MorGen) -> Self::Ob {
255 self.generators.cod.apply_to_ref(f).expect("Codomain should be defined")
256 }
257}
258
259impl DblModel for DiscreteTabModel {
260 type ObType = TabObType;
261 type MorType = TabMorType;
262 type ObOp = TabObOp;
263 type MorOp = TabMorOp;
264 type Theory = DiscreteTabTheory;
265
266 fn theory(&self) -> Rc<Self::Theory> {
267 self.theory.clone()
268 }
269
270 fn ob_type(&self, ob: &Self::Ob) -> Self::ObType {
271 match ob {
272 TabOb::Basic(x) => self.ob_generator_type(x),
273 TabOb::Tabulated(m) => TabObType::Tabulator(Box::new(self.mor_type(m))),
274 }
275 }
276
277 fn mor_type(&self, mor: &Self::Mor) -> Self::MorType {
278 let types = mor.clone().map(
279 |x| self.ob_type(&x),
280 |edge| match edge {
281 TabEdge::Basic(f) => self.mor_generator_type(&f),
282 TabEdge::Square { dom, .. } => {
283 let typ = self.mor_type(&dom); TabMorType::Hom(Box::new(TabObType::Tabulator(Box::new(typ))))
285 }
286 },
287 );
288 self.theory.compose_types(types).expect("Morphism types should have composite")
289 }
290
291 fn ob_act(&self, _ob: Self::Ob, _op: &Self::ObOp) -> Self::Ob {
292 panic!("Action on objects not implemented")
293 }
294
295 fn mor_act(&self, _path: Path<Self::Ob, Self::Mor>, _op: &Self::MorOp) -> Self::Mor {
296 panic!("Action on morphisms not implemented")
297 }
298}
299
300impl FpDblModel for DiscreteTabModel {
301 fn ob_generator_type(&self, ob: &Self::ObGen) -> Self::ObType {
302 self.ob_types.apply_to_ref(ob).expect("Object should have type")
303 }
304 fn mor_generator_type(&self, mor: &Self::MorGen) -> Self::MorType {
305 self.mor_types.apply_to_ref(mor).expect("Morphism should have type")
306 }
307
308 fn ob_generators_with_type(&self, obtype: &Self::ObType) -> impl Iterator<Item = Self::ObGen> {
309 self.ob_types.preimage(obtype)
310 }
311 fn mor_generators_with_type(
312 &self,
313 mortype: &Self::MorType,
314 ) -> impl Iterator<Item = Self::MorGen> {
315 self.mor_types.preimage(mortype)
316 }
317
318 fn equations(&self) -> impl Iterator<Item = (Self::Mor, Self::Mor)> {
319 self.equations.iter().map(|()| unreachable!())
320 }
321}
322
323impl MutDblModel for DiscreteTabModel {
324 fn add_ob(&mut self, x: Self::ObGen, ob_type: Self::ObType) {
325 self.ob_types.set(x.clone(), ob_type);
326 self.generators.objects.insert(x);
327 }
328
329 fn make_mor(&mut self, f: Self::MorGen, mor_type: Self::MorType) {
330 self.mor_types.set(f.clone(), mor_type);
331 self.generators.morphisms.insert(f);
332 }
333
334 fn get_dom(&self, f: &Self::MorGen) -> Option<&Self::Ob> {
335 self.generators.dom.get(f)
336 }
337 fn get_cod(&self, f: &Self::MorGen) -> Option<&Self::Ob> {
338 self.generators.cod.get(f)
339 }
340 fn set_dom(&mut self, f: Self::MorGen, x: Self::Ob) {
341 self.generators.dom.set(f, x);
342 }
343 fn set_cod(&mut self, f: Self::MorGen, x: Self::Ob) {
344 self.generators.cod.set(f, x);
345 }
346}
347
348impl PrintableDblModel for DiscreteTabModel {
349 fn ob_to_doc<'a>(&self, ob: &Self::Ob, ob_ns: &Namespace, mor_ns: &Namespace) -> D<'a> {
350 match ob {
351 TabOb::Basic(name) => t(ob_ns.label_string(name)),
352 TabOb::Tabulated(mor) => self.mor_to_doc(mor, ob_ns, mor_ns),
354 }
355 }
356
357 fn mor_to_doc<'a>(&self, mor: &Self::Mor, ob_ns: &Namespace, mor_ns: &Namespace) -> D<'a> {
358 match mor {
359 Path::Id(ob) => unop(t("Id"), self.ob_to_doc(ob, ob_ns, mor_ns)),
360 Path::Seq(edges) => intersperse(edges.iter().map(|e| edge_to_doc(e, mor_ns)), t(" ⋅ ")),
361 }
362 }
363
364 fn ob_type_to_doc<'a>(ob_type: &Self::ObType) -> D<'a> {
365 ob_type.to_doc()
366 }
367
368 fn mor_type_to_doc<'a>(mor_type: &Self::MorType) -> D<'a> {
369 mor_type.to_doc()
370 }
371}
372
373fn edge_to_doc<'a>(edge: &TabEdge, mor_ns: &Namespace) -> D<'a> {
374 match edge {
375 TabEdge::Basic(name) => t(mor_ns.label_string(name)),
376 TabEdge::Square { dom: _, cod: _, pre, post } => {
377 tuple([edge_to_doc(pre, mor_ns), edge_to_doc(post, mor_ns)])
378 }
379 }
380}
381
382impl std::fmt::Display for DiscreteTabModel {
383 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
384 write!(f, "{}", DblModelPrinter::new().doc(self).pretty())
385 }
386}
387
388impl Validate for DiscreteTabModel {
389 type ValidationError = InvalidDblModel;
390
391 fn validate(&self) -> Result<(), nonempty::NonEmpty<Self::ValidationError>> {
392 validate::wrap_errors(self.iter_invalid())
393 }
394}
395
396#[cfg(test)]
397mod tests {
398 use expect_test::expect;
399 use nonempty::nonempty;
400
401 use super::*;
402 use crate::{
403 stdlib::{models::*, theories::*},
404 zero::name,
405 };
406
407 #[test]
408 fn validate() {
409 let th = Rc::new(th_category_links());
410 let mut model = DiscreteTabModel::new(th);
411 model.add_ob(name("x"), name("Object").into());
412 model.add_mor(name("f"), name("x").into(), name("x").into(), name("Link").into());
413 assert_eq!(model.validate(), Err(nonempty![InvalidDblModel::CodType(name("f"))]));
414 }
415
416 #[test]
417 fn pretty_print() {
418 let model = backward_link(Rc::new(th_category_links()));
419 let expected = expect![[r#"
420 model generated by 2 objects and 2 morphisms
421 x : Object
422 y : Object
423 f : x -> y : Hom Object
424 link : y -> f : Link"#]];
425 expected.assert_eq(&format!("{model}"));
426 }
427}