catlog/dbl/
theory.rs

1/*! Double theories.
2
3A double theory specifies a categorical structure, meaning a category (or
4categories) equipped with extra structure. The spirit of the formalism is that a
5double theory is "just" a [virtual double category](super::category),
6categorifying Lawvere's idea that a theory is "just" a category. Thus, a double
7theory is a [concept with an
8attitude](https://ncatlab.org/nlab/show/concept+with+an+attitude). To bring out
9these intuitions, the interface for double theories, [`DblTheory`], introduces
10new terminology compared to the references cited below.
11
12# Terminology
13
14A double theory comprises four kinds of things:
15
161. **Object type**, interpreted in models as a set of objects.
17
182. **Morphism type**, having a source and a target object type and interpreted
19   in models as a span of morphisms (or
20   [heteromorphisms](https://ncatlab.org/nlab/show/heteromorphism)) between sets
21   of objects.
22
233. **Object operation**, interpreted in models as a function between sets of
24   objects.
25
264. **Morphism operation**, having a source and target object operation and
27   interpreted in models as map between spans of morphisms.
28
29The dictionary between the type-theoretic and double-categorical terminology is
30summarized by the table:
31
32| Associated type                 | Double theory      | Double category           | Interpreted as |
33|---------------------------------|--------------------|---------------------------|----------------|
34| [`ObType`](DblTheory::ObType)   | Object type        | Object                    | Set            |
35| [`MorType`](DblTheory::MorType) | Morphism type      | Proarrow (loose morphism) | Span           |
36| [`ObOp`](DblTheory::ObOp)       | Object operation   | Arrow (tight morphism)    | Function       |
37| [`MorOp`](DblTheory::MorOp)     | Morphism operation | Cell                      | Map of spans   |
38
39Models of a double theory are *categorical* structures, rather than merely
40*set-theoretical* ones, because each object type is assigned not just a set of
41objects but also a span of morphisms between those objects, constituting a
42category. The morphisms come from a distinguished "Hom" morphism type for each
43object type in the double theory. Similarly, each object operation is not just a
44function but a functor because it comes with an "Hom" operation between the Hom
45types. Moreover, morphism types can be composed to give new ones, as summarized
46by the table:
47
48| Method                                      | Double theory          | Double category        |
49|---------------------------------------------|------------------------|------------------------|
50| [`hom_type`](DblTheory::hom_type)           | Hom type               | Identity proarrow      |
51| [`hom_op`](DblTheory::hom_op)               | Hom operation          | Identity cell on arrow |
52| [`compose_types`](DblTheory::compose_types) | Compose morphism types | Compose proarrows      |
53
54Finally, operations on both objects and morphisms have identities and can be
55composed:
56
57| Method                                          | Double theory                       | Double category           |
58|-------------------------------------------------|-------------------------------------|---------------------------|
59| [`id_ob_op`](DblTheory::id_ob_op)               | Identity operation on object type   | Identity arrow            |
60| [`id_mor_op`](DblTheory::id_mor_op)             | Identity operation on morphism type | Identity cell on proarrow |
61| [`compose_ob_ops`](DblTheory::compose_ob_ops)   | Compose object operations           | Compose arrows            |
62| [`compose_mor_ops`](DblTheory::compose_mor_ops) | Compose morphism operations         | Compose cells             |
63
64# References
65
66- [Lambert & Patterson, 2024](crate::refs::CartDblTheories)
67- [Patterson, 2024](crate::refs::DblProducts),
68  Section 10: Finite-product double theories
69*/
70
71use nonempty::NonEmpty;
72
73use super::{category::*, graph::InvalidVDblGraph, tree::*};
74use crate::one::{InvalidPathEq, Path, tree::OpenTree};
75use crate::zero::QualifiedName;
76
77pub use super::discrete::theory::*;
78pub use super::discrete_tabulator::theory::*;
79pub use super::modal::theory::*;
80
81/** A double theory.
82
83A double theory is "just" a virtual double category (VDC) assumed to have units.
84Reflecting this, this trait has a blanket implementation for any
85[`VDblCategory`]. It is not recommended to implement this trait directly.
86
87See the [module-level docs](super::theory) for background on the terminology.
88 */
89pub trait DblTheory {
90    /** Rust type of object types in the theory.
91
92    Viewing the double theory as a virtual double category, this is the type of
93    objects.
94    */
95    type ObType: Eq + Clone;
96
97    /** Rust type of morphism types in the theory.
98
99    Viewing the double theory as a virtual double category, this is the type of
100    proarrows.
101    */
102    type MorType: Eq + Clone;
103
104    /** Rust type of operations on objects in the double theory.
105
106    Viewing the double theory as a virtual double category, this is the type of
107    arrows.
108    */
109    type ObOp: Eq + Clone;
110
111    /** Rust type of operations on morphisms in the double theory.
112
113    Viewing the double theory as a virtual double category, this is the type of
114    cells.
115    */
116    type MorOp: Eq + Clone;
117
118    /// Does the object type belong to the theory?
119    fn has_ob_type(&self, x: &Self::ObType) -> bool;
120
121    /// Does the morphism type belong to the theory?
122    fn has_mor_type(&self, m: &Self::MorType) -> bool;
123
124    /// Does the object operation belong to the theory?
125    fn has_ob_op(&self, f: &Self::ObOp) -> bool;
126
127    /// Does the morphism operation belong to the theory?
128    fn has_mor_op(&self, α: &Self::MorOp) -> bool;
129
130    /// Source of a morphism type.
131    fn src_type(&self, m: &Self::MorType) -> Self::ObType;
132
133    /// Target of a morphism type.
134    fn tgt_type(&self, m: &Self::MorType) -> Self::ObType;
135
136    /// Domain of an operation on objects.
137    fn ob_op_dom(&self, f: &Self::ObOp) -> Self::ObType;
138
139    /// Codomain of an operation on objects.
140    fn ob_op_cod(&self, f: &Self::ObOp) -> Self::ObType;
141
142    /// Source operation of an operation on morphisms.
143    fn src_op(&self, α: &Self::MorOp) -> Self::ObOp;
144
145    /// Target operation of an operation on morphisms.
146    fn tgt_op(&self, α: &Self::MorOp) -> Self::ObOp;
147
148    /// Domain of an operation on morphisms, a path of morphism types.
149    fn mor_op_dom(&self, α: &Self::MorOp) -> Path<Self::ObType, Self::MorType>;
150
151    /// Codomain of an operation on morphisms, a single morphism type.
152    fn mor_op_cod(&self, α: &Self::MorOp) -> Self::MorType;
153
154    /// Composes a sequence of morphism types, if they have a composite.
155    fn compose_types(&self, path: Path<Self::ObType, Self::MorType>) -> Option<Self::MorType>;
156
157    /** Hom morphism type on an object type.
158
159    Viewing the double theory as a virtual double category, this is the unit
160    proarrow on an object.
161    */
162    fn hom_type(&self, x: Self::ObType) -> Self::MorType {
163        self.compose_types(Path::Id(x))
164            .expect("A double theory should have a hom type for each object type")
165    }
166
167    /// Compose a sequence of operations on objects.
168    fn compose_ob_ops(&self, path: Path<Self::ObType, Self::ObOp>) -> Self::ObOp;
169
170    /** Identity operation on an object type.
171
172    View the double theory as a virtual double category, this is the identity
173    arrow on an object.
174    */
175    fn id_ob_op(&self, x: Self::ObType) -> Self::ObOp {
176        self.compose_ob_ops(Path::Id(x))
177    }
178
179    /** Hom morphism operation on an object operation.
180
181    Viewing the double theory as a virtual double category, this is the unit
182    cell on an arrow.
183     */
184    fn hom_op(&self, f: Self::ObOp) -> Self::MorOp;
185
186    /// Compose operations on morphisms.
187    fn compose_mor_ops(&self, tree: DblTree<Self::ObOp, Self::MorType, Self::MorOp>)
188    -> Self::MorOp;
189
190    /** Identity operation on a morphism type.
191
192    Viewing the double theory as a virtual double category, this is the identity
193    cell on a proarrow.
194    */
195    fn id_mor_op(&self, m: Self::MorType) -> Self::MorOp {
196        self.compose_mor_ops(DblTree::empty(m))
197    }
198}
199
200impl<VDC: VDCWithComposites> DblTheory for VDC {
201    type ObType = VDC::Ob;
202    type MorType = VDC::Pro;
203    type ObOp = VDC::Arr;
204    type MorOp = VDC::Cell;
205
206    fn has_ob_type(&self, x: &Self::ObType) -> bool {
207        self.has_ob(x)
208    }
209    fn has_mor_type(&self, m: &Self::MorType) -> bool {
210        self.has_proarrow(m)
211    }
212    fn has_ob_op(&self, f: &Self::ObOp) -> bool {
213        self.has_arrow(f)
214    }
215    fn has_mor_op(&self, α: &Self::MorOp) -> bool {
216        self.has_cell(α)
217    }
218
219    fn src_type(&self, m: &Self::MorType) -> Self::ObType {
220        self.src(m)
221    }
222    fn tgt_type(&self, m: &Self::MorType) -> Self::ObType {
223        self.tgt(m)
224    }
225    fn ob_op_dom(&self, f: &Self::ObOp) -> Self::ObType {
226        self.dom(f)
227    }
228    fn ob_op_cod(&self, f: &Self::ObOp) -> Self::ObType {
229        self.cod(f)
230    }
231
232    fn src_op(&self, α: &Self::MorOp) -> Self::ObOp {
233        self.cell_src(α)
234    }
235    fn tgt_op(&self, α: &Self::MorOp) -> Self::ObOp {
236        self.cell_tgt(α)
237    }
238    fn mor_op_dom(&self, α: &Self::MorOp) -> Path<Self::ObType, Self::MorType> {
239        self.cell_dom(α)
240    }
241    fn mor_op_cod(&self, α: &Self::MorOp) -> Self::MorType {
242        self.cell_cod(α)
243    }
244
245    fn compose_types(&self, path: Path<Self::ObType, Self::MorType>) -> Option<Self::MorType> {
246        self.composite(path)
247    }
248    fn hom_type(&self, x: Self::ObType) -> Self::MorType {
249        self.unit(x).expect("A double theory should have all hom types")
250    }
251    fn hom_op(&self, f: Self::ObOp) -> Self::MorOp {
252        let y = self.cod(&f);
253        let y_ext = self.unit_ext(y).expect("Codomain of arrow should have hom type");
254        let cell = self.compose_cells(DblTree(
255            OpenTree::linear(vec![DblNode::Spine(f), DblNode::Cell(y_ext)]).unwrap(),
256        ));
257        self.through_unit(cell, 0).expect("Domain of arrow should have hom type")
258    }
259
260    fn compose_ob_ops(&self, path: Path<Self::ObType, Self::ObOp>) -> Self::ObOp {
261        self.compose(path)
262    }
263    fn id_ob_op(&self, x: Self::ObType) -> Self::ObOp {
264        self.id(x)
265    }
266
267    fn compose_mor_ops(
268        &self,
269        tree: DblTree<Self::ObOp, Self::MorType, Self::MorOp>,
270    ) -> Self::MorOp {
271        self.compose_cells(tree)
272    }
273    fn id_mor_op(&self, m: Self::MorType) -> Self::MorOp {
274        self.id_cell(m)
275    }
276}
277
278/// A failure of a double theory to be well defined.
279#[derive(Debug)]
280pub enum InvalidDblTheory {
281    /// Morphism type with an invalid source type.
282    SrcType(QualifiedName),
283
284    /// Morphism type with an invalid target type.
285    TgtType(QualifiedName),
286
287    /// Object operation with an invalid domain.
288    ObOpDom(QualifiedName),
289
290    /// Object operation with an invalid codomain.
291    ObOpCod(QualifiedName),
292
293    /// Morphism operation with an invalid domain.
294    MorOpDom(QualifiedName),
295
296    /// Morphism operation with an invalid codomain.
297    MorOpCod(QualifiedName),
298
299    /// Morphism operation with an invalid source operation.
300    SrcOp(QualifiedName),
301
302    /// Morphism operation with an invalid target operation.
303    TgtOp(QualifiedName),
304
305    /// Morphism operation having a boundary with incompatible corners.
306    MorOpBoundary(QualifiedName),
307
308    /// Equation between morphism types with one or more errors.
309    MorTypeEq(usize, NonEmpty<InvalidPathEq>),
310
311    /// Equation between object operations with one or more errors.
312    ObOpEq(usize, NonEmpty<InvalidPathEq>),
313}
314
315impl From<InvalidVDblGraph<QualifiedName, QualifiedName, QualifiedName>> for InvalidDblTheory {
316    fn from(err: InvalidVDblGraph<QualifiedName, QualifiedName, QualifiedName>) -> Self {
317        match err {
318            InvalidVDblGraph::Dom(id) => InvalidDblTheory::ObOpDom(id),
319            InvalidVDblGraph::Cod(id) => InvalidDblTheory::ObOpCod(id),
320            InvalidVDblGraph::Src(id) => InvalidDblTheory::SrcType(id),
321            InvalidVDblGraph::Tgt(id) => InvalidDblTheory::TgtType(id),
322            InvalidVDblGraph::SquareDom(id) => InvalidDblTheory::MorOpDom(id),
323            InvalidVDblGraph::SquareCod(id) => InvalidDblTheory::MorOpCod(id),
324            InvalidVDblGraph::SquareSrc(id) => InvalidDblTheory::SrcOp(id),
325            InvalidVDblGraph::SquareTgt(id) => InvalidDblTheory::TgtOp(id),
326            InvalidVDblGraph::NotSquare(id) => InvalidDblTheory::MorOpBoundary(id),
327        }
328    }
329}