|
| 1 | +/*! Diagrams in models of a double theory. |
| 2 | +
|
| 3 | +A diagram in a [model](super::model) consists of a [morphism](super::model_morphism) |
| 4 | +of models together with its domain, which is a free mode. |
| 5 | +
|
| 6 | +Diagrams are currently used primarily to represent instances of models from |
| 7 | +a fibered perspective. |
| 8 | +
|
| 9 | +# References |
| 10 | +
|
| 11 | +- TODO dev-docs |
| 12 | + */ |
| 13 | + |
| 14 | +use std::hash::Hash; |
| 15 | + |
| 16 | +use nonempty::NonEmpty; |
| 17 | +use thiserror::Error; |
| 18 | + |
| 19 | +use super::model::DiscreteDblModel; |
| 20 | +use super::model_morphism::{DblModelMapping, DiscreteDblModelMapping}; |
| 21 | +use crate::one::*; |
| 22 | +use crate::validate::{self, Validate}; |
| 23 | + |
| 24 | +/** A diagram in a model of a double theory defined by a [mapping](DblModelMapping). |
| 25 | +
|
| 26 | +This struct owns its data, namely, the domain model and the model |
| 27 | +mapping. The domain is assumed to |
| 28 | +be a valid model of a double theory. If that is in question, the |
| 29 | +model should be validated *before* validating this object. |
| 30 | +*/ |
| 31 | +pub struct DblModelDiagram<Map, Dom>(pub Map, pub Dom); |
| 32 | + |
| 33 | +/// A diagram in a model of a discrete double theory. |
| 34 | +pub type DiscreteDblModelDiagram<DomId, CodId, Cat> = |
| 35 | + DblModelDiagram<DiscreteDblModelMapping<DomId, CodId>, DiscreteDblModel<DomId, Cat>>; |
| 36 | + |
| 37 | +impl<DomId, CodId, Cat> DiscreteDblModelDiagram<DomId, CodId, Cat> |
| 38 | +where |
| 39 | + DomId: Eq + Clone + Hash, |
| 40 | + CodId: Eq + Clone + Hash, |
| 41 | + Cat: FgCategory, |
| 42 | + Cat::Ob: Hash, |
| 43 | + Cat::Mor: Hash, |
| 44 | +{ |
| 45 | + /// The domain of the diagram. |
| 46 | + pub fn domain(&self) -> &DiscreteDblModel<DomId, Cat> { |
| 47 | + &self.1 |
| 48 | + } |
| 49 | + |
| 50 | + /// The mapping of the diagram. |
| 51 | + pub fn mapping(&self) -> &DiscreteDblModelMapping<DomId, CodId> { |
| 52 | + &self.0 |
| 53 | + } |
| 54 | +} |
| 55 | + |
| 56 | +/** An invalid assignment in a double model diagram defined explicitly by data. |
| 57 | + * |
| 58 | + * Note that, by specifying a model morphism via its action on generators, we |
| 59 | + * obtain for free that identities are sent to identities and composites of |
| 60 | + * generators are sent to their composites in the codomain. |
| 61 | +*/ |
| 62 | +#[derive(Debug, Error, PartialEq, Clone)] |
| 63 | +pub enum InvalidDblModelDiagram<Ob, Mor> { |
| 64 | + /// Missing data |
| 65 | + #[error("Object `{0}` is not mapped to anything in the codomain")] |
| 66 | + MissingOb(Ob), |
| 67 | + |
| 68 | + /// Missing data |
| 69 | + #[error("Morphism `{0}` is not mapped to anything in the codomain")] |
| 70 | + MissingMor(Mor), |
| 71 | +} |
| 72 | + |
| 73 | +impl<DomId, CodId, Cat> Validate for DiscreteDblModelDiagram<DomId, CodId, Cat> |
| 74 | +where |
| 75 | + DomId: Eq + Clone + Hash, |
| 76 | + CodId: Eq + Clone + Hash, |
| 77 | + Cat: FgCategory, |
| 78 | + Cat::Ob: Hash, |
| 79 | + Cat::Mor: Hash, |
| 80 | +{ |
| 81 | + type ValidationError = InvalidDblModelDiagram<DomId, DomId>; |
| 82 | + |
| 83 | + fn validate(&self) -> Result<(), NonEmpty<Self::ValidationError>> { |
| 84 | + validate::wrap_errors(self.iter_invalid()) |
| 85 | + } |
| 86 | +} |
| 87 | + |
| 88 | +impl<DomId, CodId, Cat> DiscreteDblModelDiagram<DomId, CodId, Cat> |
| 89 | +where |
| 90 | + DomId: Eq + Clone + Hash, |
| 91 | + CodId: Eq + Clone + Hash, |
| 92 | + Cat: FgCategory, |
| 93 | + Cat::Ob: Hash, |
| 94 | + Cat::Mor: Hash, |
| 95 | +{ |
| 96 | + /// An iterator over invalid objects and morphisms in the diagram. |
| 97 | + pub fn iter_invalid(&self) -> impl Iterator<Item = InvalidDblModelDiagram<DomId, DomId>> + '_ { |
| 98 | + let DblModelDiagram(mapping, dom) = self; |
| 99 | + |
| 100 | + // Diagrams can always be indexed by free models. |
| 101 | + assert!(dom.is_free(), "Domain model should be free"); |
| 102 | + let ob_errors = dom.object_generators().filter_map(|v| { |
| 103 | + if mapping.apply_ob(&v).is_some() { |
| 104 | + None |
| 105 | + } else { |
| 106 | + Some(InvalidDblModelDiagram::MissingOb(v)) |
| 107 | + } |
| 108 | + }); |
| 109 | + |
| 110 | + let mor_errors = dom.morphism_generators().flat_map(|f| { |
| 111 | + if mapping.apply_basic_mor(&f).is_some() { |
| 112 | + vec![] // No errors |
| 113 | + } else { |
| 114 | + [InvalidDblModelDiagram::MissingMor(f)].to_vec() |
| 115 | + } |
| 116 | + }); |
| 117 | + |
| 118 | + ob_errors.chain(mor_errors) |
| 119 | + } |
| 120 | +} |
| 121 | + |
| 122 | +#[cfg(test)] |
| 123 | +mod tests { |
| 124 | + use super::*; |
| 125 | + |
| 126 | + use std::sync::Arc; |
| 127 | + use ustr::ustr; |
| 128 | + |
| 129 | + use crate::one::fin_category::FinMor; |
| 130 | + use crate::stdlib::*; |
| 131 | + |
| 132 | + #[test] |
| 133 | + fn discrete_model_diagram() { |
| 134 | + let th = Arc::new(th_schema()); |
| 135 | + let mut model = DiscreteDblModel::new(th.clone()); |
| 136 | + let entity = ustr("entity"); |
| 137 | + model.add_ob(entity, ustr("Entity")); |
| 138 | + model.add_ob(ustr("type"), ustr("AttrType")); |
| 139 | + model.add_mor(ustr("a"), entity, ustr("type"), FinMor::Generator(ustr("Attr"))); |
| 140 | + |
| 141 | + let mut f: DiscreteDblModelMapping<_, _> = Default::default(); |
| 142 | + f.assign_ob(entity, 'x'); |
| 143 | + f.assign_ob(ustr("type"), 'y'); |
| 144 | + f.assign_basic_mor(ustr("a"), Path::pair('p', 'q')); |
| 145 | + |
| 146 | + let diagram = DblModelDiagram(f.clone(), model.clone()); |
| 147 | + assert_eq!(diagram.domain(), &model); |
| 148 | + assert_eq!(diagram.mapping(), &f); |
| 149 | + } |
| 150 | + |
| 151 | + #[test] |
| 152 | + fn validate_model_diagram() { |
| 153 | + let theory = Arc::new(th_signed_category()); |
| 154 | + let negloop = negative_loop(theory.clone()); |
| 155 | + |
| 156 | + let mut f: DiscreteDblModelMapping<_, _> = Default::default(); |
| 157 | + f.assign_ob(ustr("x"), ustr("x")); |
| 158 | + f.assign_basic_mor(ustr(""), Path::Id(ustr("negative"))); |
| 159 | + let dmd = DblModelDiagram(f, negloop); |
| 160 | + assert!(dmd.validate().is_err()); |
| 161 | + // A bad map from h to itself that is wrong for the ob (it is in the map |
| 162 | + // but sent to something that doesn't exist) and for the hom generator |
| 163 | + // (not in the map) |
| 164 | + } |
| 165 | +} |
0 commit comments