catlog/dbl/model.rs
1//! Models of double theories.
2//!
3//! A model of a double theory is a category (or categories) equipped with
4//! operations specified by the theory, categorifying the familiar idea from logic
5//! that a model of a theory is a set (or sets) equipped with operations. For
6//! background on double theories, see the [`theory`](super::theory) module.
7//!
8//! In the case of a *simple* double theory, which amounts to a small double
9//! category, a **model** of the theory is a span-valued *lax* double functor out of
10//! the theory. Such a model is a "lax copresheaf," categorifying the notion of a
11//! copresheaf or set-valued functor. Though they are "just" lax double functors,
12//! models come with extra intuitions. To bring that out we introduce new jargon,
13//! building on that for double theories.
14//!
15//! # Terminology
16//!
17//! A model of a double theory consists of elements of two kinds:
18//!
19//! 1. **Objects**, each assigned an object type in the theory;
20//!
21//! 2. **Morphisms**, each having a domain and a codomain object and assigned a
22//! morphism type in the theory, compatibly with the domain and codomain types;
23//!
24//! In addition, a model has the following operations:
25//!
26//! - **Object action**: object operations in the theory act on objects in the model
27//! to produce new objects;
28//!
29//! - **Morphism action**: morphism operations in the theory act on morphisms in
30//! the model to produce new morphisms, compatibly with the object action;
31//!
32//! - **Composition**: a path of morphisms in the model has a composite morphism,
33//! whose type is the composite of the corresponding morphism types.
34
35use derivative::Derivative;
36use nonempty::NonEmpty;
37use std::rc::Rc;
38
39#[cfg(feature = "serde")]
40use serde::{Deserialize, Serialize};
41#[cfg(feature = "serde-wasm")]
42use tsify::Tsify;
43
44use super::theory::DblTheory;
45use crate::one::{Category, FgCategory, InvalidPathEq, Path};
46use crate::zero::pretty::*;
47use crate::zero::{Namespace, QualifiedName};
48
49pub use super::discrete::model::*;
50pub use super::discrete_tabulator::model::*;
51pub use super::modal::model::*;
52
53/// A model of a double theory.
54///
55/// As always in logic, a model makes sense only relative to a theory, but a
56/// theory can have many different models. So, when implemented Rust, a model
57/// needs access to its theory but should not *own* its theory. Implementors of
58/// this trait are assumed to own to a reference-counting pointer to the theory.
59///
60/// Objects and morphisms in a model are typed by object types and morphism types in
61/// the theory. There is a design choice about whether identifiers for objects
62/// ([`Ob`](Category::Ob)) and morphisms ([`Mor`](Category::Mor)) are unique
63/// relative to their types or globally within the model. If we took the first
64/// approach (as we do in the Julia package
65/// [ACSets.jl](https://github.com/AlgebraicJulia/ACSets.jl)), one could only make
66/// sense of objects and morphisms when their types are known, so the early methods
67/// in the trait would look like this:
68///
69/// ```ignore
70/// fn has_ob(&self, x: &Self::Ob, t: &Self::ObType) -> bool;
71/// fn has_mor(&self, m: &Self::Mor, t: &Self::MorType) -> bool;
72/// fn dom(&self, m: &Self::Mor, t: &Self::MorType) -> Self::Ob;
73/// fn cod(&self, m: &Self::Mor, t: &Self::MorType) -> Self::Ob;
74/// ```
75///
76/// It will be more convenient for us to take the second approach since in our usage
77/// object and morphism identifiers will be globally unique in a very strong sense
78/// (something like UUIDs).
79pub trait DblModel: Category {
80 /// Rust type of object types defined in the theory.
81 type ObType: Eq;
82
83 /// Rust type of morphism types defined in the theory.
84 type MorType: Eq;
85
86 /// Type of operations on objects defined in the theory.
87 type ObOp: Eq;
88
89 /// Type of operations on morphisms defined in the theory.
90 type MorOp: Eq;
91
92 /// The type of double theory that this is a model of.
93 type Theory: DblTheory<
94 ObType = Self::ObType,
95 MorType = Self::MorType,
96 ObOp = Self::ObOp,
97 MorOp = Self::MorOp,
98 >;
99
100 /// The double theory that this model is a model of.
101 fn theory(&self) -> Rc<Self::Theory>;
102
103 /// Type of an object.
104 fn ob_type(&self, x: &Self::Ob) -> Self::ObType;
105
106 /// Type of a morphism.
107 fn mor_type(&self, m: &Self::Mor) -> Self::MorType;
108
109 /// Acts on an object with an object operation.
110 fn ob_act(&self, x: Self::Ob, f: &Self::ObOp) -> Self::Ob;
111
112 /// Acts on a sequence of morphisms with a morphism operation.
113 fn mor_act(&self, path: Path<Self::Ob, Self::Mor>, α: &Self::MorOp) -> Self::Mor;
114}
115
116/// A finitely presented model of a double theory.
117pub trait FpDblModel: DblModel + FgCategory {
118 /// Type of an object generator.
119 fn ob_generator_type(&self, ob: &Self::ObGen) -> Self::ObType;
120
121 /// Type of a morphism generator.
122 fn mor_generator_type(&self, mor: &Self::MorGen) -> Self::MorType;
123
124 /// Iterates over object generators with the given object type.
125 fn ob_generators_with_type(&self, obtype: &Self::ObType) -> impl Iterator<Item = Self::ObGen> {
126 self.ob_generators().filter(|ob| self.ob_generator_type(ob) == *obtype)
127 }
128
129 /// Iterates over morphism generators with the given morphism type.
130 fn mor_generators_with_type(
131 &self,
132 mortype: &Self::MorType,
133 ) -> impl Iterator<Item = Self::MorGen> {
134 self.mor_generators().filter(|mor| self.mor_generator_type(mor) == *mortype)
135 }
136
137 /// Iterators over basic objects with the given object type.
138 fn objects_with_type(&self, obtype: &Self::ObType) -> impl Iterator<Item = Self::Ob> {
139 self.ob_generators_with_type(obtype).map(|ob_gen| ob_gen.into())
140 }
141
142 /// Iterates over basic morphisms with the given morphism type.
143 fn morphisms_with_type(&self, mortype: &Self::MorType) -> impl Iterator<Item = Self::Mor> {
144 self.mor_generators_with_type(mortype).map(|mor_gen| mor_gen.into())
145 }
146
147 /// Iterates over equations between morphisms.
148 fn equations(&self) -> impl Iterator<Item = (Self::Mor, Self::Mor)>;
149}
150
151/// A mutable, finitely generated model of a double theory.
152pub trait MutDblModel: FpDblModel {
153 /// Adds an object generator to the model.
154 fn add_ob(&mut self, x: Self::ObGen, ob_type: Self::ObType);
155
156 /// Adds a morphism generator to the model.
157 fn add_mor(&mut self, f: Self::MorGen, dom: Self::Ob, cod: Self::Ob, mor_type: Self::MorType) {
158 self.make_mor(f.clone(), mor_type);
159 self.set_dom(f.clone(), dom);
160 self.set_cod(f, cod);
161 }
162
163 /// Adds a morphism generator to the model without setting its (co)domain.
164 fn make_mor(&mut self, f: Self::MorGen, mor_type: Self::MorType);
165
166 /// Gets the domain of a morphism generator, if it is set.
167 fn get_dom(&self, f: &Self::MorGen) -> Option<&Self::Ob>;
168
169 /// Gets the codomain of a morphism generator, if it is set.
170 fn get_cod(&self, f: &Self::MorGen) -> Option<&Self::Ob>;
171
172 /// Sets the domain of a morphism generator.
173 fn set_dom(&mut self, f: Self::MorGen, x: Self::Ob);
174
175 /// Sets the codomain of a morphism generator.
176 fn set_cod(&mut self, f: Self::MorGen, x: Self::Ob);
177}
178
179/// A pretty-printable model of a double theory.
180///
181/// One would assume that a printable model should have a printable theory, but
182/// we haven't bothered to implement pretty printing for theories. So, for now,
183/// we include only what we need of theory pretty printer---printing object and
184/// morphism types---as extra methods here.
185pub trait PrintableDblModel: FpDblModel<ObGen = QualifiedName, MorGen = QualifiedName> {
186 /// Pretty prints an object in the model.
187 fn ob_to_doc<'a>(&self, ob: &Self::Ob, ob_ns: &Namespace, mor_ns: &Namespace) -> D<'a>;
188
189 /// Pretty prints a morphism in the model.
190 fn mor_to_doc<'a>(&self, mor: &Self::Mor, ob_ns: &Namespace, mor_ns: &Namespace) -> D<'a>;
191
192 /// Pretty prints an object type in the model's theory.
193 fn ob_type_to_doc<'a>(ob_type: &Self::ObType) -> D<'a>;
194
195 /// Pretty prints a morphism type in the model's theory.
196 fn mor_type_to_doc<'a>(mor_type: &Self::MorType) -> D<'a>;
197}
198
199/// Pretty-printer for models of double theories.
200#[derive(Derivative)]
201#[derivative(Default(new = "true"))]
202pub struct DblModelPrinter {
203 #[derivative(Default(value = "true"))]
204 include_summary: bool,
205}
206
207impl DblModelPrinter {
208 /// Sets whether to show summary at beginning of model printout.
209 pub fn include_summary(mut self, value: bool) -> Self {
210 self.include_summary = value;
211 self
212 }
213
214 /// Generates a summary string for the model.
215 pub fn summary(&self, model: &impl PrintableDblModel) -> String {
216 let n_ob = model.ob_generators().count();
217 let n_mor = model.mor_generators().count();
218 format!(
219 "model generated by {n_ob} object{} and {n_mor} morphism{}",
220 if n_ob != 1 { "s" } else { "" },
221 if n_mor != 1 { "s" } else { "" },
222 )
223 }
224
225 /// Pretty prints a model (with empty namespaces).
226 pub fn doc<'a>(&self, model: &impl PrintableDblModel) -> D<'a> {
227 let ns = Namespace::new_for_text();
228 self.namespaced_doc(model, &ns, &ns)
229 }
230
231 /// Pretty prints a model with labels from the given namespaces.
232 pub fn namespaced_doc<'a, Model: PrintableDblModel>(
233 &self,
234 model: &Model,
235 ob_ns: &Namespace,
236 mor_ns: &Namespace,
237 ) -> D<'a> {
238 let ob_entries = model.ob_generators().map(|name| {
239 t(ob_ns.label_string(&name))
240 + t(" : ")
241 + Model::ob_type_to_doc(&model.ob_generator_type(&name))
242 });
243
244 let mor_entries = model.mor_generators().map(|name| {
245 t(mor_ns.label_string(&name))
246 + t(" : ")
247 + model.ob_to_doc(&model.mor_generator_dom(&name), ob_ns, mor_ns)
248 + t(" -> ")
249 + model.ob_to_doc(&model.mor_generator_cod(&name), ob_ns, mor_ns)
250 + t(" : ")
251 + Model::mor_type_to_doc(&model.mor_generator_type(&name))
252 });
253
254 let eqn_entries = model.equations().map(|(lhs, rhs)| {
255 let mor_type = Model::mor_type_to_doc(&model.mor_type(&lhs));
256 let src = model.ob_to_doc(&model.dom(&lhs), ob_ns, mor_ns);
257 let tgt = model.ob_to_doc(&model.cod(&lhs), ob_ns, mor_ns);
258 let lhs = model.mor_to_doc(&lhs, ob_ns, mor_ns);
259 let rhs = model.mor_to_doc(&rhs, ob_ns, mor_ns);
260 lhs + t(" = ") + rhs + t(" : ") + mor_type.parens() + tuple([src, tgt])
261 });
262
263 let entries = ob_entries.chain(mor_entries).chain(eqn_entries);
264 let result = intersperse(entries, hardline());
265 if self.include_summary {
266 t(self.summary(model)) + hardline() + result
267 } else {
268 result
269 }
270 }
271}
272
273/// A failure of a model of a double theory to be well defined.
274///
275/// TODO: We are missing the case that an equation has different composite morphism
276/// types on left and right hand sides.
277#[derive(Clone, Debug, PartialEq, Eq)]
278#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
279#[cfg_attr(feature = "serde", serde(tag = "tag", content = "content"))]
280#[cfg_attr(feature = "serde-wasm", derive(Tsify))]
281#[cfg_attr(feature = "serde-wasm", tsify(into_wasm_abi, from_wasm_abi))]
282pub enum InvalidDblModel {
283 /// Domain of morphism generator is undefined or invalid.
284 Dom(QualifiedName),
285
286 /// Codomain of morphism generator is missing or invalid.
287 Cod(QualifiedName),
288
289 /// Object generator has invalid object type.
290 ObType(QualifiedName),
291
292 /// Morphism generator has invalid morphism type.
293 MorType(QualifiedName),
294
295 /// Domain of morphism generator has type incompatible with morphism type.
296 DomType(QualifiedName),
297
298 /// Codomain of morphism generator has type incompatible with morphism type.
299 CodType(QualifiedName),
300
301 /// Equation between morphisms has one or more errors.
302 ///
303 /// FIXME: should not really be an Option, fix after issue 1017 is resolved..
304 Eqn(Option<usize>, NonEmpty<InvalidModelEqn>),
305
306 /// Tried to us a feature not yet supported by the elaborator.
307 UnsupportedFeature(Feature),
308
309 /// No link provided for instantiation cell, or wrong type of link.
310 InvalidLink(QualifiedName),
311
312 /// Reference to an undefined generator or import in an instance term.
313 FiberElement(QualifiedName),
314
315 /// Instance term has an invalid fiber type: an application to an
316 /// argument over the wrong object, or an equation between elements
317 /// with inconvertible fiber types.
318 FiberType(QualifiedName),
319
320 /// Imported instance has a codomain model different from the enclosing
321 /// instance's.
322 ImportCodomain(QualifiedName),
323}
324
325/// A failure of an equation in a model of a double theory to be well defined.
326#[derive(Clone, Debug, PartialEq, Eq)]
327#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
328#[cfg_attr(feature = "serde", serde(tag = "tag", content = "content"))]
329#[cfg_attr(feature = "serde-wasm", derive(Tsify))]
330#[cfg_attr(feature = "serde-wasm", tsify(into_wasm_abi, from_wasm_abi))]
331pub enum InvalidModelEqn {
332 /// Sources of sides of equation don't coincide.
333 Src,
334
335 /// Targets of sides of equation don't coincide.
336 Tgt,
337
338 /// Left-hand side of equation fails to synthesize.
339 Lhs,
340
341 /// Right-hand side of equation fails to synthesize.
342 Rhs,
343
344 /// Sides of equation are not even in the same morphism type.
345 MorType,
346}
347
348impl From<InvalidPathEq> for InvalidModelEqn {
349 fn from(err: InvalidPathEq) -> Self {
350 match err {
351 InvalidPathEq::Lhs => InvalidModelEqn::Lhs,
352 InvalidPathEq::Rhs => InvalidModelEqn::Rhs,
353 InvalidPathEq::Src => InvalidModelEqn::Src,
354 InvalidPathEq::Tgt => InvalidModelEqn::Tgt,
355 }
356 }
357}
358
359/// Various features that the new elaboration does not yet support.
360#[derive(Clone, Debug, PartialEq, Eq)]
361#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
362#[cfg_attr(feature = "serde", serde(tag = "tag", content = "content"))]
363#[cfg_attr(feature = "serde-wasm", derive(Tsify))]
364#[cfg_attr(feature = "serde-wasm", tsify(into_wasm_abi, from_wasm_abi))]
365pub enum Feature {
366 /// Morphism type that is not a basic type or a hom type.
367 ComplexMorType,
368 /// Equation between one or more undefined morphisms.
369 PartialEquation,
370 /// Application of a composite morphism in an instance term. Nested
371 /// applications express the same thing.
372 CompositeApplication,
373}