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}