From b625310281812df93f43657844ffa306f5a75e74 Mon Sep 17 00:00:00 2001 From: Owen Lynch Date: Thu, 7 Sep 2023 16:52:44 -0700 Subject: [PATCH 01/10] added Gatlab dependency theories up to monoidal working all theories reporting for duty! basic graphs work! graphs working, progress on categoricalalgebra fixing conflicting function declarations choo choo that the sound of the integration train --- Project.toml | 2 + src/Catlab.jl | 14 +- src/categorical_algebra/ACSetsGATsInterop.jl | 47 +- src/categorical_algebra/CSets.jl | 7 +- src/categorical_algebra/CatElements.jl | 3 +- src/categorical_algebra/Categories.jl | 27 +- src/categorical_algebra/Chase.jl | 5 +- .../CommutativeDiagrams.jl | 2 +- src/categorical_algebra/Diagrams.jl | 4 +- src/categorical_algebra/FinCats.jl | 25 +- src/categorical_algebra/FinRelations.jl | 2 +- src/categorical_algebra/FinSets.jl | 5 +- src/categorical_algebra/FreeDiagrams.jl | 5 +- .../FunctorialDataMigrations.jl | 4 +- src/categorical_algebra/Limits.jl | 50 +- src/categorical_algebra/Matrices.jl | 2 +- src/categorical_algebra/Permutations.jl | 2 +- src/categorical_algebra/Sets.jl | 4 +- src/categorical_algebra/Slices.jl | 2 +- src/categorical_algebra/StructuredCospans.jl | 3 +- src/categorical_algebra/Subobjects.jl | 23 +- src/gats/GATs.jl | 18 - src/gats/MetaUtils.jl | 176 ---- src/gats/Presentations.jl | 252 ------ src/gats/Rewriting.jl | 101 --- src/gats/SyntaxSystems.jl | 561 ------------ src/gats/TheoriesInstances.jl | 702 --------------- src/graphs/BasicGraphs.jl | 9 +- src/graphs/BipartiteGraphs.jl | 2 + src/graphs/GraphAlgorithms.jl | 2 +- src/graphs/GraphGenerators.jl | 3 - src/graphs/PropertyGraphs.jl | 4 +- src/theories/Category.jl | 104 +-- src/theories/HigherCategory.jl | 251 +++--- src/theories/IndexedCategory.jl | 125 ++- src/theories/Limits.jl | 100 +-- src/theories/Monoidal.jl | 191 ++-- src/theories/MonoidalAdditive.jl | 105 ++- src/theories/MonoidalMultiple.jl | 51 +- src/theories/Preorders.jl | 65 +- src/theories/Relations.jl | 28 +- src/theories/Schema.jl | 30 +- src/theories/Theories.jl | 10 +- test/Project.toml | 2 + test/categorical_algebra/Categories.jl | 5 +- test/categorical_algebra/FinCats.jl | 5 +- test/graphs/GraphAlgorithms.jl | 15 +- test/graphs/GraphGenerators.jl | 6 +- test/programs/DiagrammaticPrograms.jl | 816 ++++++++++++++++++ 49 files changed, 1498 insertions(+), 2479 deletions(-) delete mode 100644 src/gats/GATs.jl delete mode 100644 src/gats/MetaUtils.jl delete mode 100644 src/gats/Presentations.jl delete mode 100644 src/gats/Rewriting.jl delete mode 100644 src/gats/SyntaxSystems.jl delete mode 100644 src/gats/TheoriesInstances.jl create mode 100644 test/programs/DiagrammaticPrograms.jl diff --git a/Project.toml b/Project.toml index 74939c2e0..df1ef6880 100644 --- a/Project.toml +++ b/Project.toml @@ -6,10 +6,12 @@ version = "0.15.5" [deps] ACSets = "227ef7b5-1206-438b-ac65-934d6da304b8" +AlgebraicInterfaces = "23cfdc9f-0504-424a-be1f-4892b28e2f0c" Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" CompTime = "0fb5dd42-039a-4ca4-a1d7-89a96eae6d39" Compose = "a81c6b42-2e10-5240-aca2-a61377ecd94b" DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" +GATlab = "f0ffcf3b-d13a-433e-917c-cc44ccf5ead2" GeneralizedGenerated = "6b9d7cbe-bcb9-11e9-073f-15a7a543e2eb" JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" LightXML = "9c8b4983-aa76-5018-a973-4c85ecc9e179" diff --git a/src/Catlab.jl b/src/Catlab.jl index 4cf3d6815..d79721127 100644 --- a/src/Catlab.jl +++ b/src/Catlab.jl @@ -2,21 +2,19 @@ module Catlab using Reexport -include("gats/GATs.jl") include("theories/Theories.jl") include("categorical_algebra/ACSetsGATsInterop.jl") include("graphs/Graphs.jl") include("categorical_algebra/CategoricalAlgebra.jl") -include("wiring_diagrams/WiringDiagrams.jl") -include("graphics/Graphics.jl") -include("programs/Programs.jl") +# include("wiring_diagrams/WiringDiagrams.jl") +# include("graphics/Graphics.jl") +# include("programs/Programs.jl") -@reexport using .GATs @reexport using .Theories @reexport using .Graphs @reexport using .CategoricalAlgebra -@reexport using .WiringDiagrams -@reexport using .Graphics -@reexport using .Programs +# @reexport using .WiringDiagrams +# @reexport using .Graphics +# @reexport using .Programs end diff --git a/src/categorical_algebra/ACSetsGATsInterop.jl b/src/categorical_algebra/ACSetsGATsInterop.jl index fa30df796..e6adbcc64 100644 --- a/src/categorical_algebra/ACSetsGATsInterop.jl +++ b/src/categorical_algebra/ACSetsGATsInterop.jl @@ -8,8 +8,11 @@ using DataStructures: OrderedDict using ACSets import ACSets: Schema -using ...GATs, ...Theories -import ...GATs: Presentation +using GATlab +import GATlab: Presentation +using .ThCategory + +using MLStyle # Schema <-> presentation ######################### @@ -22,6 +25,21 @@ function Schema(p::Presentation) BasicSchema(obs,homs,attrtypes,attrs) end +function Schema(c::GATContext) + obs, attrtypes = Symbol[], Symbol[] + homs, attrs = Tuple{Symbol, Symbol, Symbol}[], Tuple{Symbol, Symbol, Symbol}[] + for binding in p.scope + type = getvalue(binding) + @match (nameof(type.body.head), type.body.args...) begin + (:Ob,) => push!(obs, nameof(binding)) + (:Hom, x, y) => push!(homs, nameof.((binding, x.body, y.body))) + (:AttrType,) => push!(attrtypes, nameof(binding)) + (:Attr, x, y) => push!(attrs, nameof.((binding, x.body, y.body))) + end + end + BasicSchema(obs,homs,attrtypes,attrs) +end + function Presentation(::Type{S}) where S <: TypeLevelBasicSchema{Symbol} Presentation(Schema(S)) end @@ -34,17 +52,16 @@ function Presentation(::Type{<:StructACSet{S}}) where S <: TypeLevelBasicSchema{ Presentation(Schema(S)) end -function Presentation(s::BasicSchema{Symbol}) - pres = Presentation(FreeSchema) - obs = OrderedDict(x => Ob(FreeSchema.Ob, x) for x in Schemas.objects(s)) - attrtypes = OrderedDict(x => AttrType(FreeSchema.AttrType, x) for x in Schemas.attrtypes(s)) - homs = [Hom(f, obs[d], obs[c]) for (f,d,c) in Schemas.homs(s)] - attrs = [Attr(f, obs[d], attrtypes[c]) for (f,d,c) in Schemas.attrs(s)] - - foreach(gens -> add_generators!(pres, gens), (values(obs), homs, values(attrtypes), attrs)) - return pres -end +# function Presentation(s::BasicSchema{Symbol}) +# pres = Presentation(FreeSchema) +# obs = OrderedDict(x => Ob(FreeSchema.Ob, x) for x in Schemas.objects(s)) +# attrtypes = OrderedDict(x => AttrType(FreeSchema.AttrType, x) for x in Schemas.attrtypes(s)) +# homs = [Hom(f, obs[d], obs[c]) for (f,d,c) in Schemas.homs(s)] +# attrs = [Attr(f, obs[d], attrtypes[c]) for (f,d,c) in Schemas.attrs(s)] +# foreach(gens -> add_generators!(pres, gens), (values(obs), homs, values(attrtypes), attrs)) +# return pres +# end function DenseACSets.struct_acset(name::Symbol, parent, p::Presentation; index::Vector=[], unique_index::Vector=[]) @@ -79,14 +96,14 @@ end JSONACSets.generate_json_acset_schema(pres::Presentation) = generate_json_acset_schema(Schema(pres)) -JSONACSets.parse_json_acset_schema(::Type{Presentation{ThSchema,Symbol}}, +JSONACSets.parse_json_acset_schema(::Type{Presentation}, data::AbstractDict) = Presentation(parse_json_acset_schema(BasicSchema, data)) JSONACSets.parse_json_acset_schema(data) = - parse_json_acset_schema(Presentation{ThSchema,Symbol}, data) + parse_json_acset_schema(Presentation, data) JSONACSets.read_json_acset_schema(fname::AbstractString) = - read_json_acset_schema(Presentation{ThSchema,Symbol}, fname) + read_json_acset_schema(Presentation, fname) # ACSet <-> GAT exprs ##################### diff --git a/src/categorical_algebra/CSets.jl b/src/categorical_algebra/CSets.jl index d5ee65e72..49e63235d 100644 --- a/src/categorical_algebra/CSets.jl +++ b/src/categorical_algebra/CSets.jl @@ -21,8 +21,9 @@ using ACSets.Columns using ACSets.DenseACSets: indices, unique_indices, attr_type, attrtype_type, datatypes, constructor -using ...GATs, ...Graphs.BasicGraphs -using ...Theories: ThCategory, Hom, Ob, Attr, AttrType +using GATlab +using ...Graphs.BasicGraphs +using ...Theories import ...Theories: ob, hom, dom, codom, compose, ⋅, id, meet, ∧, join, ∨, top, ⊤, bottom, ⊥, ⊕, ⊗ @@ -707,6 +708,8 @@ end #################### @instance ThCategory{ACSet, ACSetTransformation} begin + @import Ob, Hom + dom(α::ACSetTransformation) = α.dom codom(α::ACSetTransformation) = α.codom diff --git a/src/categorical_algebra/CatElements.jl b/src/categorical_algebra/CatElements.jl index ecb8a7bc5..1f6b6f06e 100644 --- a/src/categorical_algebra/CatElements.jl +++ b/src/categorical_algebra/CatElements.jl @@ -3,7 +3,8 @@ export SchElements, AbstractElements, Elements, elements, inverse_elements using DataStructures: OrderedDict -using ...GATs, ...Theories +using GATlab +using ...Theories using ..CSets, ..FinSets, ..HomSearch @present SchElements(FreeSchema) begin diff --git a/src/categorical_algebra/Categories.jl b/src/categorical_algebra/Categories.jl index 205c20c0f..d37d23979 100644 --- a/src/categorical_algebra/Categories.jl +++ b/src/categorical_algebra/Categories.jl @@ -19,9 +19,10 @@ export Category, Cat, TypeCat, Functor, Transformation, dom, codom, compose, id, using StructEquality -using ...GATs -import ...Theories: ThCategory2, ob, hom, dom, codom, compose, ⋅, ∘, id, - composeH, * +using GATlab +import GATlab: op +import ...Theories: ThCategory2, ob, hom +import .ThCategory2: dom, codom, compose, ⋅, ∘, id, composeH, * # Categories ############ @@ -281,9 +282,7 @@ const IdIdTransformation{C<:Cat} = IdentityTransformation{C,C,IdentityFunctor{C} codom(F::Functor) = F.codom id(C::Cat) = IdentityFunctor(C) - function compose(F::Functor, G::Functor; strict::Bool=true) - !strict || codom(F) == dom(G) || - error("Domain mismatch in composition $F ⋅ $G") + function compose(F::Functor, G::Functor) compose_id(F, G) end @@ -291,24 +290,16 @@ const IdIdTransformation{C<:Cat} = IdentityTransformation{C,C,IdentityFunctor{C} codom(α::Transformation) = α.codom id(F::Functor) = IdentityTransformation(F) - function compose(α::Transformation, β::Transformation; strict::Bool=true) - !strict || codom(α) == dom(β) || - error("Domain mismatch in vertical composition $α ⋅ $β") + function compose(α::Transformation, β::Transformation) compose_id(α, β) end - function composeH(α::Transformation, β::Transformation; strict::Bool=true) - !strict || codom_ob(α) == dom_ob(β) || - error("Domain mismatch in horizontal composition $α * $β") + function composeH(α::Transformation, β::Transformation) composeH_id(α, β) end - function composeH(α::Transformation, H::Functor; strict::Bool=true) - !strict || codom_ob(α) == dom(H) || - error("Domain mismatch in whiskering $α * $H") + function composeH(α::Transformation, H::Functor) composeH_id(α, H) end - function composeH(F::Functor, β::Transformation; strict::Bool=true) - !strict || codom(F) == dom_ob(β) || - error("Domain mismatch in whiskering $F * $β") + function composeH(F::Functor, β::Transformation) composeH_id(F, β) end end diff --git a/src/categorical_algebra/Chase.jl b/src/categorical_algebra/Chase.jl index 1ef2e44c5..f68647b86 100644 --- a/src/categorical_algebra/Chase.jl +++ b/src/categorical_algebra/Chase.jl @@ -18,7 +18,8 @@ module Chase export chase using ACSets.DenseACSets: datatypes -using ...GATs, ...Theories +using GATlab +using ...Theories using ..CSets, ..HomSearch, ..FinSets, ..FinCats, ..Limits, ..FreeDiagrams using ..FinCats: FinCatPresentation import ..Limits: universal @@ -467,7 +468,7 @@ end Preserves the original name of the inputs if it is unambiguous, otherwise disambiguates with index in original input. E.g. (A,B)⊔(B,C) → (A,B#1,B#2,C) """ -function coproduct_fincat(Xs::AbstractVector{<: FinCatPresentation{ThSchema}}; kw...) +function coproduct_fincat(Xs::AbstractVector{<:FinCatPresentation{ThSchema.Meta.T}}; kw...) Xps = [X.presentation for X in Xs] # Collect all generators and identify conflicting names cnflobs, cnflats, cnflhoms, cnflattrs = map([:Ob,:AttrType,:Hom,:Attr]) do x diff --git a/src/categorical_algebra/CommutativeDiagrams.jl b/src/categorical_algebra/CommutativeDiagrams.jl index 316f0b0dc..7e0104331 100644 --- a/src/categorical_algebra/CommutativeDiagrams.jl +++ b/src/categorical_algebra/CommutativeDiagrams.jl @@ -10,7 +10,7 @@ export SquareDiagram, SquareOb, SquareHom, ob, hom, dom, codom, src, tgt, using StaticArrays: StaticVector, SVector using StructEquality -using ...GATs +using GATlab using ...Theories: ThDoubleCategory import ...Theories: ob, hom, dom, codom, src, tgt, top, bottom, compose, id, pcompose, pid, ⋅, * diff --git a/src/categorical_algebra/Diagrams.jl b/src/categorical_algebra/Diagrams.jl index 74661d591..65f95d98e 100644 --- a/src/categorical_algebra/Diagrams.jl +++ b/src/categorical_algebra/Diagrams.jl @@ -6,7 +6,7 @@ export Diagram, SimpleDiagram, DiagramHom, id, op, co, using StructEquality -using ...GATs +using GATlab import ...Theories: dom, codom, id, compose, ⋅, ∘, munit using ...Theories: ThCategory, composeH, FreeSchema import ..Categories: ob_map, hom_map, op, co @@ -309,4 +309,4 @@ isnothing(dom_shape) ? DiagramHom{op}([Pair(j, f)], d, d′) : DiagramHom{op}(Dict(only(ob_generators(dom(diagram(d′)))) => Pair(j, f)),d,d′) end -end \ No newline at end of file +end diff --git a/src/categorical_algebra/FinCats.jl b/src/categorical_algebra/FinCats.jl index e05054548..5d6559795 100644 --- a/src/categorical_algebra/FinCats.jl +++ b/src/categorical_algebra/FinCats.jl @@ -23,8 +23,8 @@ using StaticArrays: SVector using DataStructures: IntDisjointSets, in_same_set, num_groups using ACSets -using ...GATs -import ...GATs: equations +using GATlab +import GATlab: equations using ...Theories: ThCategory, ThSchema, ThPointedSetCategory, ThPointedSetSchema, ObExpr, HomExpr, AttrExpr, AttrTypeExpr, FreeSchema, FreePointedSetCategory, zeromap import ...Theories: dom, codom, id, compose, ⋅, ∘ @@ -32,7 +32,6 @@ using ...Graphs import ...Graphs: edges, src, tgt, enumerate_paths @reexport using ..Categories import ..Categories: CatSize, ob, hom, ob_map, hom_map, component, op - # Categories ############ @@ -305,18 +304,18 @@ function FinCatPresentation(pres::Presentation{T}) where T S = pres.syntax FinCatPresentation{T,S.Ob,S.Hom}(pres) end -function FinCatPresentation(pres::Presentation{ThSchema}) +function FinCatPresentation(pres::Presentation{ThSchema.Meta.T}) S = pres.syntax Ob = Union{S.Ob, S.AttrType} Hom = Union{S.Hom, S.Attr, S.AttrType} - FinCatPresentation{ThSchema,Ob,Hom}(pres) + FinCatPresentation{ThSchema.Meta.T,Ob,Hom}(pres) end -function FinCatPresentation(pres::Presentation{ThPointedSetSchema}) +function FinCatPresentation(pres::Presentation{ThPointedSetSchema.Meta.T}) S = pres.syntax Ob = Union{S.Ob, S.AttrType} Hom = Union{S.Hom, S.Attr, S.AttrType} - FinCatPresentation{ThPointedSetSchema,Ob,Hom}(pres) + FinCatPresentation{ThPointedSetSchema.Meta.T,Ob,Hom}(pres) end """ Computes the graph generating a finitely @@ -328,12 +327,12 @@ in the resulting graph. presentation(C::FinCatPresentation) = C.presentation ob_generators(C::FinCatPresentation) = generators(presentation(C), :Ob) -ob_generators(C::Union{FinCatPresentation{ThSchema},FinCatPresentation{ThPointedSetSchema}}) = let P = presentation(C) +ob_generators(C::Union{FinCatPresentation{ThSchema.Meta.T},FinCatPresentation{ThPointedSetSchema.Meta.T}}) = let P = presentation(C) vcat(generators(P, :Ob), generators(P, :AttrType)) end hom_generators(C::FinCatPresentation) = generators(presentation(C), :Hom) -hom_generators(C::Union{FinCatPresentation{ThSchema},FinCatPresentation{ThPointedSetSchema}}) = let P = presentation(C) +hom_generators(C::Union{FinCatPresentation{ThSchema.Meta.T},FinCatPresentation{ThPointedSetSchema.Meta.T}}) = let P = presentation(C) vcat(generators(P, :Hom), generators(P, :Attr)) end equations(C::FinCatPresentation) = equations(presentation(C)) @@ -348,7 +347,7 @@ hom_generator_name(C::FinCatPresentation, f::GATExpr{:generator}) = first(f) ob(C::FinCatPresentation, x::GATExpr) = gat_typeof(x) == :Ob ? x : error("Expression $x is not an object") -ob(C::Union{FinCatPresentation{ThSchema},FinCatPresentation{ThPointedSetSchema}}, x::GATExpr) = +ob(C::Union{FinCatPresentation{ThSchema.Meta.T},FinCatPresentation{ThPointedSetSchema.Meta.T}}, x::GATExpr) = gat_typeof(x) ∈ (:Ob, :AttrType) ? x : error("Expression $x is not an object or attribute type") @@ -357,12 +356,12 @@ hom(C::FinCatPresentation, fs::AbstractVector) = mapreduce(f -> hom(C, f), compose, fs) hom(C::FinCatPresentation, f::GATExpr) = gat_typeof(f) == :Hom ? f : error("Expression $f is not a morphism") -hom(::Union{FinCatPresentation{ThSchema},FinCatPresentation{ThPointedSetSchema}}, f::GATExpr) = +hom(::Union{FinCatPresentation{ThSchema.Meta.T},FinCatPresentation{ThPointedSetSchema.Meta.T}}, f::GATExpr) = gat_typeof(f) ∈ (:Hom, :Attr, :AttrType) ? f : error("Expression $f is not a morphism or attribute") -id(C::FinCatPresentation{ThSchema}, x::AttrTypeExpr) = x -compose(C::FinCatPresentation{ThSchema}, f::AttrTypeExpr, g::AttrTypeExpr) = +id(C::FinCatPresentation{ThSchema.Meta.T}, x::AttrTypeExpr) = x +compose(C::FinCatPresentation{ThSchema.Meta.T}, f::AttrTypeExpr, g::AttrTypeExpr) = (f == g) ? f : error("Invalid composite of attribute type identities: $f != $g") function Base.show(io::IO, C::FinCatPresentation) diff --git a/src/categorical_algebra/FinRelations.jl b/src/categorical_algebra/FinRelations.jl index c2ae464df..5fc98fd05 100644 --- a/src/categorical_algebra/FinRelations.jl +++ b/src/categorical_algebra/FinRelations.jl @@ -7,7 +7,7 @@ export BoolRig, FinRel, FinRelation, FinRelationCallable, FinRelationMatrix, import Base: +, * using StructEquality -using ...GATs +using GATlab using ...Theories: ThDistributiveBicategoryRelations import ...Theories: dom, codom, id, compose, ⋅, ∘, dagger, dunit, dcounit, otimes, ⊗, munit, braid, oplus, ⊕, mzero, swap, diff --git a/src/categorical_algebra/FinSets.jl b/src/categorical_algebra/FinSets.jl index 9803ae37f..53b19e1bc 100644 --- a/src/categorical_algebra/FinSets.jl +++ b/src/categorical_algebra/FinSets.jl @@ -15,7 +15,8 @@ import Tables, PrettyTables using ACSets @reexport using ..Sets -using ...GATs, ...Theories, ...Graphs +using GATlab +using ...Theories, ...Graphs using ..FinCats, ..FreeDiagrams, ..Limits, ..Subobjects import ...Theories: Ob, meet, ∧, join, ∨, top, ⊤, bottom, ⊥, ⋅, dom, codom, compose @@ -1464,4 +1465,4 @@ join(A::SubFinSet{Int}, B::SubFinSet{Int}, ::SubOpBoolean) = top(X::FinSet{Int}, ::SubOpBoolean) = SubFinSet(trues(length(X))) bottom(X::FinSet{Int}, ::SubOpBoolean) = SubFinSet(falses(length(X))) -end \ No newline at end of file +end diff --git a/src/categorical_algebra/FreeDiagrams.jl b/src/categorical_algebra/FreeDiagrams.jl index 296718d46..5430d348c 100644 --- a/src/categorical_algebra/FreeDiagrams.jl +++ b/src/categorical_algebra/FreeDiagrams.jl @@ -22,8 +22,9 @@ using StructEquality using StaticArrays: StaticVector, SVector using ACSets -using ...GATs, ...Theories, ...Graphs, ..FinCats -import ...Theories: ob, hom, dom, codom +using GATlab +using ...Theories, ...Graphs, ..FinCats +import AlgebraicInterfaces: ob, hom, dom, codom import ..FinCats: FreeCatGraph, FinDomFunctor, collect_ob, collect_hom # Diagram interface diff --git a/src/categorical_algebra/FunctorialDataMigrations.jl b/src/categorical_algebra/FunctorialDataMigrations.jl index b69b0a053..c3b37b8d3 100644 --- a/src/categorical_algebra/FunctorialDataMigrations.jl +++ b/src/categorical_algebra/FunctorialDataMigrations.jl @@ -10,12 +10,12 @@ using MLStyle: @match using ACSets using ACSets.DenseACSets: constructor, datatypes -using ...GATs +using GATlab using ...Theories: ob, hom, dom, codom, attr, AttrTypeExpr, ⋅ using ..Categories, ..FinCats, ..Limits, ..Diagrams, ..FinSets, ..CSets, ..HomSearch using ...Graphs, ..FreeDiagrams import ..Categories: ob_map, hom_map -import ...GATs: functor +import GATlab: functor using ..FinCats: make_map, mapvals, presentation_key using ..Chase: collage, crel_type, pres_to_eds, add_srctgt, chase using ..FinSets: VarSet diff --git a/src/categorical_algebra/Limits.jl b/src/categorical_algebra/Limits.jl index 39284b44d..adfdcc6e7 100644 --- a/src/categorical_algebra/Limits.jl +++ b/src/categorical_algebra/Limits.jl @@ -19,7 +19,8 @@ using StructEquality using StaticArrays: StaticVector, SVector using ACSets -using ...GATs, ...Theories +using GATlab +using ...Theories import ...Theories: dom, codom, ob, hom, terminal, product, proj1, proj2, equalizer, incl, initial, coproduct, coproj1, coproj2, coequalizer, proj, @@ -298,29 +299,40 @@ Implements an instance of [`ThCartesianCategory`](@ref) assuming that finite products have been implemented following the limits interface. """ macro cartesian_monoidal_instance(Ob, Hom) - esc(quote - import Catlab.Theories: ThCartesianCategory, otimes, ⊗, munit, braid, σ, - mcopy, delete, pair, proj1, proj2, Δ, ◊ + thcc = ThCartesianCategory.Meta.theory + instance_body = quote + @import Ob, Hom, dom, codom, compose, ⋅, id, munit, delete, pair - @instance ThCartesianCategory{$Ob, $Hom} begin - @import dom, codom, compose, ⋅, id, munit, delete, pair + otimes(A::$Ob, B::$Ob) = ob(product(A, B)) - otimes(A::$Ob, B::$Ob) = ob(product(A, B)) + function otimes(f::$Hom, g::$Hom) + π1, π2 = product(dom(f), dom(g)) + pair(product(codom(f), codom(g)), π1⋅f, π2⋅g) + end - function otimes(f::$Hom, g::$Hom) - π1, π2 = product(dom(f), dom(g)) - pair(product(codom(f), codom(g)), π1⋅f, π2⋅g) - end + function braid(A::$Ob, B::$Ob) + AB, BA = product(A, B), product(B, A) + pair(BA, proj2(AB), proj1(AB)) + end - function braid(A::$Ob, B::$Ob) - AB, BA = product(A, B), product(B, A) - pair(BA, proj2(AB), proj1(AB)) - end + mcopy(A::$Ob) = pair(id(A),id(A)) + proj1(A::$Ob, B::$Ob) = proj1(product(A, B)) + proj2(A::$Ob, B::$Ob) = proj2(product(A, B)) + end + instance_code = ModelInterface.generate_instance( + ThCartesianCategory.Meta.theory, + ThCartesianCategory, + Dict(zip(sorts(thcc), [Ob, Hom])), + nothing, + [], + instance_body; + escape=false + ) + esc(quote + import Catlab.Theories: ThCartesianCategory, otimes, ⊗, munit, braid, σ, + mcopy, delete, pair, proj1, proj2, Δ, ◊ - mcopy(A::$Ob) = pair(id(A),id(A)) - proj1(A::$Ob, B::$Ob) = proj1(product(A, B)) - proj2(A::$Ob, B::$Ob) = proj2(product(A, B)) - end + $instance_code otimes(As::AbstractVector{<:$Ob}) = ob(product(As)) diff --git a/src/categorical_algebra/Matrices.jl b/src/categorical_algebra/Matrices.jl index 79d015879..d5dbabbbb 100644 --- a/src/categorical_algebra/Matrices.jl +++ b/src/categorical_algebra/Matrices.jl @@ -10,7 +10,7 @@ using LinearAlgebra: I using SparseArrays import SparseArrays: blockdiag -using ...GATs +using GATlab using ...Theories: ThDistributiveSemiadditiveCategory import ...Theories: dom, codom, id, compose, ⋅, ∘, otimes, ⊗, munit, braid, oplus, ⊕, mzero, swap, diff --git a/src/categorical_algebra/Permutations.jl b/src/categorical_algebra/Permutations.jl index 76802baf3..0528d8942 100644 --- a/src/categorical_algebra/Permutations.jl +++ b/src/categorical_algebra/Permutations.jl @@ -4,7 +4,7 @@ module Permutations export cycles, permutation_to_expr, adjacent_transpositions_by_bubble_sort!, adjacent_transpositions_by_insertion_sort! -using ...GATs +using GATlab using ...Theories: dom, codom, compose, id, otimes, munit, braid # Decomposition diff --git a/src/categorical_algebra/Sets.jl b/src/categorical_algebra/Sets.jl index 2f1cf826f..36b0d200b 100644 --- a/src/categorical_algebra/Sets.jl +++ b/src/categorical_algebra/Sets.jl @@ -10,7 +10,8 @@ export SetOb, TypeSet, PredicatedSet, SetFunction, ConstantFunction, Ob using StructEquality -using ...GATs, ..Categories, ..FreeDiagrams, ..Limits +using GATlab +using ..Categories, ..FreeDiagrams, ..Limits using ...Theories: ThCategory import ...Theories: Ob, dom, codom, id, compose, ⋅, ∘ import ..Categories: show_type_constructor, show_domains @@ -178,6 +179,7 @@ end """ Category of sets and functions. """ + @instance ThCategory{SetOb, SetFunction} begin dom(f::SetFunction) = f.dom codom(f::SetFunction) = f.codom diff --git a/src/categorical_algebra/Slices.jl b/src/categorical_algebra/Slices.jl index 2cbd796a2..021c49e1a 100644 --- a/src/categorical_algebra/Slices.jl +++ b/src/categorical_algebra/Slices.jl @@ -3,7 +3,7 @@ export Slice, SliceHom using StructEquality -using ...GATs +using GATlab using ..FreeDiagrams, ..Limits, ..CSets using ...Theories: ThCategory import ...Theories: dom, codom, compose, id diff --git a/src/categorical_algebra/StructuredCospans.jl b/src/categorical_algebra/StructuredCospans.jl index 7654dfa76..2b038d125 100644 --- a/src/categorical_algebra/StructuredCospans.jl +++ b/src/categorical_algebra/StructuredCospans.jl @@ -11,7 +11,8 @@ using StructEquality using StaticArrays: StaticVector, SVector using ACSets -using ...GATs, ..FreeDiagrams, ..Limits, ..FinSets, ..CSets +using GATlab +using ..FreeDiagrams, ..Limits, ..FinSets, ..CSets import ..FreeDiagrams: apex, legs, feet, left, right, bundle_legs import ..CSets: components, force using ...Theories: ThCategory diff --git a/src/categorical_algebra/Subobjects.jl b/src/categorical_algebra/Subobjects.jl index 58e3bf56a..505822547 100644 --- a/src/categorical_algebra/Subobjects.jl +++ b/src/categorical_algebra/Subobjects.jl @@ -15,8 +15,9 @@ import Base: \, ~ using StructEquality using StaticArrays: SVector -using ...GATs, ...Theories, ..Limits +using GATlab import ...Theories: ob, hom, meet, ∧, join, ∨, top, ⊤, bottom, ⊥ +using ...Theories, ..Limits # Theories ########## @@ -29,9 +30,9 @@ are dependent on another type. In fact, if we supported GAT morphisms, it should be possible to define a projection morphism of GATs from `ThSubobjectLattice` to `ThAlgebraicLattice` that sends `Ob` to the unit type. """ -@signature ThSubobjectLattice{Ob,Sub} begin +@signature ThSubobjectLattice begin Ob::TYPE - Sub(ob::X)::TYPE + Sub(ob::Ob)::TYPE @op begin (∧) := meet @@ -40,8 +41,8 @@ be possible to define a projection morphism of GATs from `ThSubobjectLattice` to (⊥) := bottom end - meet(A::Sub(X), B::Sub(X))::Sub(X) ⊣ (X::Ob) - join(A::Sub(X), B::Sub(X))::Sub(X) ⊣ (X::Ob) + meet(A::Sub(X), B::Sub(X))::Sub(X) ⊣ [X::Ob] + join(A::Sub(X), B::Sub(X))::Sub(X) ⊣ [X::Ob] top(X::Ob)::Sub(X) bottom(X::Ob)::Sub(X) end @@ -49,27 +50,27 @@ end """ Theory of Heyting algebra of subobjects in a Heyting category, such as a topos. """ -@signature ThSubobjectHeytingAlgebra{Ob,Sub} <: ThSubobjectLattice{Ob,Sub} begin +@signature ThSubobjectHeytingAlgebra <: ThSubobjectLattice begin @op begin (⟹) := implies (¬) := negate end - implies(A::Sub(X), B::Sub(X))::Sub(X) ⊣ (X::Ob) - negate(A::Sub(X))::Sub(X) ⊣ (X::Ob) + implies(A::Sub(X), B::Sub(X))::Sub(X) ⊣ [X::Ob] + negate(A::Sub(X))::Sub(X) ⊣ [X::Ob] end """ Theory of bi-Heyting algebra of subobjects in a bi-Heyting topos, such as a presheaf topos. """ -@signature ThSubobjectBiHeytingAlgebra{Ob,Sub} <: ThSubobjectHeytingAlgebra{Ob,Sub} begin +@signature ThSubobjectBiHeytingAlgebra <: ThSubobjectHeytingAlgebra begin @op begin (\) := subtract (~) := non end - subtract(A::Sub(X), B::Sub(X))::Sub(X) ⊣ (X::Ob) - non(A::Sub(X))::Sub(X) ⊣ (X::Ob) + subtract(A::Sub(X), B::Sub(X))::Sub(X) ⊣ [X::Ob] + non(A::Sub(X))::Sub(X) ⊣ [X::Ob] end # Data types diff --git a/src/gats/GATs.jl b/src/gats/GATs.jl deleted file mode 100644 index d4b45e675..000000000 --- a/src/gats/GATs.jl +++ /dev/null @@ -1,18 +0,0 @@ -""" Generalized algebraic theories (GATs) in Julia. -""" -module GATs - -using Reexport - -include("MetaUtils.jl") -include("TheoriesInstances.jl") -include("SyntaxSystems.jl") -include("Rewriting.jl") -include("Presentations.jl") - -@reexport using .TheoriesInstances -@reexport using .SyntaxSystems -@reexport using .Rewriting -@reexport using .Presentations - -end diff --git a/src/gats/MetaUtils.jl b/src/gats/MetaUtils.jl deleted file mode 100644 index efa38cea8..000000000 --- a/src/gats/MetaUtils.jl +++ /dev/null @@ -1,176 +0,0 @@ -""" General-purpose tools for metaprogramming in Julia. -""" -module MetaUtils -export Expr0, JuliaFunction, JuliaFunctionSig, parse_docstring, parse_function, - parse_function_sig, generate_docstring, generate_function, - append_expr!, concat_expr, replace_symbols, strip_lines - -using Base.Meta: ParseError -using StructEquality -using MLStyle: @match - -# Data types -############ - -const Expr0 = Union{Symbol,Expr} - -@struct_hash_equal struct JuliaFunction - call_expr::Expr - return_type::Union{Expr0,Nothing} - impl::Union{Expr,Nothing} - doc::Union{String,Nothing} - - function JuliaFunction(call_expr::Expr, return_type=nothing, - impl=nothing, doc=nothing) - new(call_expr, return_type, impl, doc) - end -end - -@struct_hash_equal struct JuliaFunctionSig - name::Symbol - types::Vector{Expr0} -end - -# Parsing Julia functions -######################### - -""" Parse Julia expression that is (possibly) annotated with docstring. -""" -function parse_docstring(expr::Expr)::Tuple{Union{String,Nothing},Expr} - expr = strip_lines(expr) - if expr.head == :macrocall && ( - # XXX: It seems that the @doc macro can show up in two forms. - expr.args[1] == GlobalRef(Core, Symbol("@doc")) || - expr.args[1] == Expr(:core, Symbol("@doc"))) - (expr.args[2], expr.args[3]) - else - (nothing, expr) - end -end - -""" Parse Julia function definition into standardized form. -""" -function parse_function(expr::Expr)::JuliaFunction - doc, expr = parse_docstring(expr) - fun_expr, impl = @match expr begin - Expr(:(=), args...) => args - Expr(:function, args...) => args - _ => throw(ParseError("Ill-formed function definition $expr")) - end - @match fun_expr begin - (Expr(:(::), Expr(:call, args...), return_type) => - JuliaFunction(Expr(:call, args...), return_type, impl, doc)) - (Expr(:call, args...) => - JuliaFunction(Expr(:call, args...), nothing, impl, doc)) - _ => throw(ParseError("Ill-formed function header $fun_expr")) - end -end - -""" Parse signature of Julia function. -""" -function parse_function_sig(call_expr::Expr)::JuliaFunctionSig - name, args = @match call_expr begin - Expr(:call, name::Symbol, Expr(:parameters, kw...), args...) => (name, args) - Expr(:call, name::Symbol, args...) => (name, args) - _ => throw(ParseError("Ill-formed function signature $call_expr")) - end - types = map(args) do expr - @match expr begin - Expr(:(::), _, type) => type - Expr(:(::), type) => type - _ => :Any - end - end - JuliaFunctionSig(name, types) -end -parse_function_sig(fun::JuliaFunction) = parse_function_sig(fun.call_expr) - -""" Wrap Julia expression with docstring. -""" -function generate_docstring(expr::Expr, doc::Union{String,Nothing})::Expr - if isnothing(doc) - expr - else - Expr(:macrocall, GlobalRef(Core, Symbol("@doc")), - LineNumberNode(0), doc, expr) - end -end - -""" Generate Julia expression for function definition. -""" -function generate_function(fun::JuliaFunction)::Expr - head = if isnothing(fun.return_type) - fun.call_expr - else - Expr(:(::), fun.call_expr, fun.return_type) - end - body = if isnothing(fun.impl) - Expr(:block) - else - # Wrap implementation inside block if not already. - impl = fun.impl - impl.head == :block ? impl : Expr(:block, impl) - end - expr = Expr(:function, head, body) - generate_docstring(expr, fun.doc) -end - -# Operations on Julia expressions -################################# - -""" Append a Julia expression to a block expression. -""" -function append_expr!(block::Expr, expr)::Expr - @assert block.head == :block - @match expr begin - Expr(:block, args...) => append!(block.args, args) - _ => push!(block.args, expr) - end - block -end - -""" Concatenate two Julia expressions into a block expression. -""" -function concat_expr(expr1::Expr, expr2::Expr)::Expr - @match (expr1, expr2) begin - (Expr(:block, a1...), Expr(:block, a2...)) => Expr(:block, a1..., a2...) - (Expr(:block, a1...), _) => Expr(:block, a1..., expr2) - (_, Expr(:block, a2...)) => Expr(:block, expr1, a2...) - _ => Expr(:block, expr1, expr2) - end -end - -""" Replace symbols occurring anywhere in a Julia function (except the name). -""" -function replace_symbols(bindings::AbstractDict, f::JuliaFunction)::JuliaFunction - JuliaFunction( - Expr(f.call_expr.head, f.call_expr.args[1], - (replace_symbols(bindings, a) for a in f.call_expr.args[2:end])...), - isnothing(f.return_type) ? nothing : replace_symbols(bindings, f.return_type), - isnothing(f.impl) ? nothing : replace_symbols(bindings, f.impl), - f.doc - ) -end - -""" Replace symbols occuring anywhere in a Julia expression. -""" -function replace_symbols(bindings::AbstractDict, expr) - recurse(expr) = replace_symbols(bindings, expr) - @match expr begin - Expr(head, args...) => Expr(head, map(recurse,args)...) - sym::Symbol => get(bindings, sym, sym) - _ => expr - end -end - -""" Remove all LineNumberNodes from a Julia expression. -""" -function strip_lines(expr::Expr; recurse::Bool=false)::Expr - args = [ x for x in expr.args if !isa(x, LineNumberNode) ] - if recurse - args = [ isa(x, Expr) ? strip_lines(x; recurse=true) : x for x in args ] - end - Expr(expr.head, args...) -end - -end diff --git a/src/gats/Presentations.jl b/src/gats/Presentations.jl deleted file mode 100644 index f0dc1e96e..000000000 --- a/src/gats/Presentations.jl +++ /dev/null @@ -1,252 +0,0 @@ -""" Finite presentations of a model of a generalized algebraic theory (GAT). - -We support two methods for defining models of a GAT: as Julia objects using the -`@instance` macro and as syntactic objects using the `@present` macro. -Instances are useful for casting generic data structures, such as matrices, -abstract tensor systems, and wiring diagrams, in categorical language. -Presentations define small categories by generators and relations and are useful -in applications like knowledge representation. -""" -module Presentations -export @present, Presentation, generator, generators, generator_index, - has_generator, equations, add_generator!, add_generators!, add_definition!, - add_equation!, add_equations!, change_theory - -using Base.Meta: ParseError -using MLStyle: @match - -using ..MetaUtils, ..SyntaxSystems -import ..TheoriesInstances as GAT -import ..SyntaxSystems: parse_json_sexpr, to_json_sexpr, generator_switch_syntax -# Data types -############ - -struct Presentation{Theory,Name} - syntax::Module - generators::NamedTuple - generator_name_index::Dict{Name,Pair{Symbol,Int}} - equations::Vector{Pair} -end - -function Presentation{Name}(syntax::Module) where Name - Theory = syntax.theory() - theory = GAT.theory(Theory) - names = Tuple(type.name for type in theory.types) - vectors = ((getfield(syntax, name){:generator})[] for name in names) - Presentation{Theory,Name}(syntax, NamedTuple{names}(vectors), - Dict{Name,Pair{Symbol,Int}}(), Pair[]) -end -Presentation(syntax::Module) = Presentation{Symbol}(syntax) - -function Base.:(==)(pres1::Presentation, pres2::Presentation) - pres1.syntax == pres2.syntax && pres1.generators == pres2.generators && - pres1.equations == pres2.equations -end -""" -Move a presentation to a new syntax, -duplicating all the data on shared names. In particular, -this is lossless if all the generators of the original -presentation are at names in the new syntax. -""" -function change_theory(syntax::Module,pres::Presentation{S,Name}) where {S,Name} - T = syntax.theory() - pres_new = Presentation(syntax) - types = intersect(keys(pres_new.generators),keys(pres.generators)) - for t in types map(pres.generators[t]) do x - add_generator!(pres_new,generator_switch_syntax(syntax,x)) end end - #XX: test on equations - pres_new -end -function Base.copy(pres::Presentation{T,Name}) where {T,Name} - Presentation{T,Name}(pres.syntax, map(copy, pres.generators), - copy(pres.generator_name_index), copy(pres.equations)) -end - -# Presentation -############## - -""" Get all generators of a presentation. -""" -generators(pres::Presentation) = collect(Iterators.flatten(pres.generators)) -generators(pres::Presentation, type::Symbol) = pres.generators[type] -generators(pres::Presentation, type::Type) = generators(pres, nameof(type)) - -""" Retrieve generators by name. - -Generators can also be retrieved using indexing notation, so that -`generator(pres, name)` and `pres[name]` are equivalent. -""" -function generator(pres::Presentation, name) - type, index = pres.generator_name_index[name] - pres.generators[type][index] -end -Base.getindex(pres::Presentation, name) = generator.(Ref(pres), name) - -""" Does the presentation contain a generator with the given name? -""" -function has_generator(pres::Presentation, name) - haskey(pres.generator_name_index, name) -end - -""" Add a generator to a presentation. -""" -function add_generator!(pres::Presentation, expr) - name, type = first(expr), gat_typeof(expr) - generators = pres.generators[type] - if !isnothing(name) - if haskey(pres.generator_name_index, name) - error("Name $name already defined in presentation") - end - pres.generator_name_index[name] = type => length(generators)+1 - end - push!(generators, expr) - expr -end - -""" Add iterable of generators to a presentation. -""" -function add_generators!(pres::Presentation, exprs) - for expr in exprs - add_generator!(pres, expr) - end -end - -""" Get all equations of a presentation. -""" -equations(pres::Presentation) = pres.equations - -""" Add an equation between terms to a presentation. -""" -add_equation!(pres::Presentation, lhs::GATExpr, rhs::GATExpr) = - push!(pres.equations, lhs => rhs) - -""" Add sequence of equations to a presentation. -""" -add_equations!(pres::Presentation, eqs) = append!(pres.equations, eqs) - -""" Add a generator defined by an equation. -""" -function add_definition!(pres::Presentation, name::Symbol, rhs::GATExpr) - generator = SyntaxSystems.generator_like(rhs, name) - add_generator!(pres, generator) - add_equation!(pres, generator, rhs) - generator -end - -""" Get the index of a generator, relative to generators of same GAT type. -""" -function generator_index(pres::Presentation{T,Name}, name::Name) where {T,Name} - last(pres.generator_name_index[name]) -end -function generator_index(pres::Presentation, expr::GATExpr{:generator}) - name = first(expr) - !isnothing(name) || error("Cannot lookup unnamed generator by name") - generator_index(pres, name) -end - -""" Shorthand for contructing a term in the presentation -""" -function make_term(pres::Presentation, expr) - @match expr begin - ::Symbol => pres[expr] - Expr(:call, term_constructor, args...) => - invoke_term(pres.syntax, term_constructor, - map(arg -> make_term(pres, arg), args)...) - end -end - -""" Create a new generator in a presentation of a given type -""" -function make_generator(pres::Presentation, name::Union{Symbol,Nothing}, - type::Symbol, type_args::Vector) - invoke_term(pres.syntax, type, name, - map(e -> make_term(pres, e), type_args)...) -end - -""" Create and add a new generator -""" -function construct_generator!(pres::Presentation, name::Union{Symbol,Nothing}, - type::Symbol, type_args::Vector=[]) - add_generator!(pres, make_generator(pres, name, type, type_args)) -end - -""" Create and add multiple generators -""" -function construct_generators!(pres::Presentation, names::AbstractVector, - type::Symbol, type_args::Vector=[]) - for name in names - construct_generator!(pres, name, type, type_args) - end -end - -# Presentation Definition -######################### - -function add_to_presentation(pres, block) - pres = copy(pres) - @match strip_lines(block) begin - Expr(:block, lines...) => - for line in lines - eval_stmt!(pres, line) - end - _ => error("Must pass in a block") - end - pres -end - -function parse_type_expr(type_expr) - @match type_expr begin - Expr(:call, f::Symbol, args...) => (f,[args...]) - f::Symbol => (f,[]) - _ => error("Ill-formed type expression $type_expr") - end -end - -function eval_stmt!(pres::Presentation, stmt::Expr) - @match stmt begin - Expr(:(::), name::Symbol, type_expr) => - construct_generator!(pres, name, parse_type_expr(type_expr)...) - Expr(:(::), Expr(:tuple, names...), type_expr) => - construct_generators!(pres, [names...], parse_type_expr(type_expr)...) - Expr(:(::), type_expr) => - construct_generator!(pres, nothing, parse_type_expr(type_expr)...) - Expr(:(:=), name::Symbol, def_expr) => - add_definition!(pres, name, make_term(pres, def_expr)) - Expr(:call, :(==), lhs, rhs) => - add_equation!(pres, make_term(pres, lhs), make_term(pres, rhs)) - end -end - -# Presentation macro -#################### - -""" Define a presentation using a convenient syntax. -""" -macro present(head, body) - name, pres = @match head begin - Expr(:call, name::Symbol, syntax_name::Symbol) => - (name, :($(GlobalRef(Presentations, :Presentation))($(esc(syntax_name))))) - Expr(:(<:), name::Symbol, parent::Symbol) => (name,:(copy($(esc(parent))))) - _ => throw(ParseError("Ill-formed presentation header $head")) - end - quote - $(esc(name)) = $(esc(add_to_presentation))($pres, $(Expr(:quote, body))) - end -end - -# Serialization -############### - -function to_json_sexpr(pres::Presentation, expr::GATExpr) - to_json_sexpr(expr; - by_reference = name -> has_generator(pres, name)) -end - -function parse_json_sexpr(pres::Presentation{Theory,Name}, - syntax::Module, sexpr) where {Theory,Name} - parse_json_sexpr(syntax, sexpr; - symbols = Name == Symbol, - parse_reference = name -> generator(pres, name)) -end - -end diff --git a/src/gats/Rewriting.jl b/src/gats/Rewriting.jl deleted file mode 100644 index 9668f462e..000000000 --- a/src/gats/Rewriting.jl +++ /dev/null @@ -1,101 +0,0 @@ -""" Rewriting for GAT expressions. - -The current content of this module is just a stopgap until I can implement -a generic term rewriting system. -""" -module Rewriting -export associate, associate_unit_inv, associate_unit, - distribute_unary, involute, normalize_zero - -using ..SyntaxSystems - -#Warning: assumes expr has only two args! -""" Simplify associative binary operation. - -Maintains the normal form `op(e1,e2,...)` where `e1`,`e2`,... are expressions -that are *not* applications of `op()` -""" -function associate(expr::E)::E where E <: GATExpr - op, e1, e2 = head(expr), first(expr), last(expr) - args1 = head(e1) == op ? args(e1) : [e1] - args2 = head(e2) == op ? args(e2) : [e2] - E([args1; args2], gat_type_args(expr)) -end - -""" Simplify associative binary operation with unit. - -Reduces a freely generated (typed) monoid to normal form. -""" -function associate_unit(expr::GATExpr, unit::Function)::GATExpr - e1, e2 = first(expr), last(expr) - if (head(e1) == nameof(unit)) e2 - elseif (head(e2) == nameof(unit)) e1 - else associate(expr) end -end - -""" Simplify associative binary operation with unit and inverses. -""" -function associate_unit_inv(expr::E, unit::Function, - inverse::Function)::GATExpr where E <: GATExpr - op, e1, e2 = head(expr), first(expr), last(expr) - if (head(e1) == nameof(unit)) e2 - elseif (head(e2) == nameof(unit)) e1 - else - args1 = head(e1) == op ? args(e1) : [e1] - args2 = head(e2) == op ? args(e2) : [e2] - while !isempty(args1) && !isempty(args2) - l = args1[end]; r = args2[1] - if (head(l) == nameof(inverse) && first(l) == r || - head(r) == nameof(inverse) && l == first(r)) - pop!(args1); popfirst!(args2) - else break end - end - newargs = [args1; args2] - # XXX: Assumes that the unit/identity takes exactly one argument, hence this - # function will not work for the algebraic theory of groups. - if (isempty(newargs)) unit(only(unique(gat_type_args(expr)))) - elseif (length(newargs) == 1) only(newargs) - else E(newargs, gat_type_args(expr)) end - end -end - -""" Distribute unary operation over binary operation. -""" -function distribute_unary(expr::GATExpr, unary::Function, binary::Function; - unit::Union{Function,Nothing}=nothing, - contravariant::Bool=false)::GATExpr - if (head(expr) != nameof(unary)) return expr end - @assert length(args(expr)) == 1 - arg = first(expr) - if head(arg) == nameof(binary) - binary(map(unary, (contravariant ? reverse : identity)(args(arg)))) - elseif !isnothing(unit) && head(arg) == nameof(unit) - arg - else - expr - end -end - -""" Simplify involutive unary operation. -""" -function involute(expr::GATExpr) - @assert length(args(expr)) == 1 - arg = first(expr) - head(expr) == head(arg) ? first(arg) : expr -end -""" -If given GATExpr contains a zero morphism, -collapse the expression to a single zero morphism. -""" -function normalize_zero(expr::E;zname=:zeromap) where E <: GATExpr - ztype = E - for subexpr in args(expr) - if head(subexpr) == zname - ztype = typeof(subexpr) - s,t = gat_type_args(expr) - return ztype([s,t],[s,t]) - end end - expr -end -end - diff --git a/src/gats/SyntaxSystems.jl b/src/gats/SyntaxSystems.jl deleted file mode 100644 index 128f1e298..000000000 --- a/src/gats/SyntaxSystems.jl +++ /dev/null @@ -1,561 +0,0 @@ -""" Syntax systems for generalized algebraic theories (GATs). - -In general, a single theory may have many different syntaxes. The purpose of -this module to enable the simple but flexible construction of syntax systems. -""" -module SyntaxSystems -export @syntax, GATExpr, SyntaxDomainError, head, args, first, last, - gat_typeof, gat_type_args, invoke_term, functor, - to_json_sexpr, parse_json_sexpr, show_sexpr, show_unicode, show_latex - -import Base.Meta: ParseError, show_sexpr -using MLStyle: @match - -import ..TheoriesInstances as GAT -using ..TheoriesInstances: Context, Theory, TypeConstructor, TermConstructor -import ..TheoriesInstances: invoke_term -using ..MetaUtils - -# Data types -############ - -""" Base type for expression in the syntax of a GAT. - -We define Julia types for each *type constructor* in the theory, e.g., object, -morphism, and 2-morphism in the theory of 2-categories. Of course, Julia's -type system does not support dependent types, so the type parameters are -incorporated in the Julia types. (They are stored as extra data in the -expression instances.) - -The concrete types are structurally similar to the core type `Expr` in Julia. -However, the *term constructor* is represented as a type parameter, rather than -as a `head` field. This makes dispatch using Julia's type system more -convenient. -""" -abstract type GATExpr{T} end - -head(::GATExpr{T}) where T = T -args(expr::GATExpr) = expr.args -Base.first(expr::GATExpr) = first(args(expr)) -Base.last(expr::GATExpr) = last(args(expr)) -gat_typeof(expr::GATExpr) = nameof(typeof(expr)) -gat_type_args(expr::GATExpr) = expr.type_args - -""" Get name of GAT generator expression as a `Symbol`. - -If the generator has no name, returns `nothing`. -""" -function Base.nameof(expr::GATExpr{:generator}) - name = first(expr) - isnothing(name) ? nothing : Symbol(name) -end - -function Base.:(==)(e1::GATExpr{T}, e2::GATExpr{S}) where {T,S} - T == S && e1.args == e2.args && e1.type_args == e2.type_args -end -function Base.hash(e::GATExpr, h::UInt) - hash(args(e), hash(head(e), h)) -end - -function Base.show(io::IO, expr::GATExpr) - print(io, head(expr)) - print(io, "(") - join(io, args(expr), ",") - print(io, ")") -end -function Base.show(io::IO, expr::GATExpr{:generator}) - print(io, first(expr)) -end -function Base.show(io::IO, ::MIME"text/plain", expr::GATExpr) - show_unicode(io, expr) -end -function Base.show(io::IO, ::MIME"text/latex", expr::GATExpr) - print(io, "\$") - show_latex(io, expr) - print(io, "\$") -end - -struct SyntaxDomainError <: Exception - constructor::Symbol - args::Vector -end - -function Base.showerror(io::IO, exc::SyntaxDomainError) - print(io, "Domain error in term constructor $(exc.constructor)(") - join(io, exc.args, ",") - print(io, ")") -end - -# Syntax -######## - -""" Define a *syntax* system for a generalized algebraic theory (GAT). - -A syntax system consists of Julia types (with top type `GATExpr`) for each type -constructor in the signature, plus Julia functions for - -1. *Generators*: creating new generator terms, e.g., objects or morphisms -2. *Accessors*: accessing type parameters, e.g., domains and codomains -3. *Term constructors*: applying term constructors, e.g., composition and - monoidal products - -Julia code for all this is generated by the macro. Any of the methods can be -overriden with custom simplification logic. -""" -macro syntax(syntax_head, mod_name, body=nothing) - if isnothing(body); body = Expr(:block) end - @assert body.head == :block - syntax_name, base_types = @match syntax_head begin - Expr(:call, name::Symbol, args...) => begin - @warn "Using Haskell-style theory declaration with parentheses is deprecated," * - " use Julia-style with curly braces." - (name, args) - end - Expr(:curly, name::Symbol, args...) => (name, args) - name::Symbol => (name, []) - _ => throw(ParseError("Ill-formed syntax signature $syntax_head")) - end - functions = map(parse_function, strip_lines(body).args) - - expr = Expr(:call, :syntax_code, Expr(:quote, syntax_name), - esc(Expr(:ref, :Type, base_types...)), - esc(mod_name), esc(nameof(__module__)), functions) - Expr(:block, - Expr(:call, esc(:eval), expr), - :(Core.@__doc__ $(esc(syntax_name)))) -end -function syntax_code(name::Symbol, base_types::Vector{Type}, - theory_type::Type, outer_module::Module, - functions::Vector) - theory = GAT.theory(theory_type) - theory_ref = GlobalRef(parentmodule(theory_type), nameof(theory_type)) - - # Generate module with syntax types and type/term generators. - mod = Expr(:module, true, name, - Expr(:block, [ - # Prevents error about export not being at toplevel. - # https://github.com/JuliaLang/julia/issues/28991 - LineNumberNode(0); - Expr(:export, [cons.name for cons in theory.types]...); - Expr(:using, Expr(:., :., :., nameof(outer_module))); - :(theory() = $theory_ref); - gen_types(theory, base_types); - gen_type_accessors(theory); - gen_term_generators(theory, outer_module); - gen_term_constructors(theory, outer_module); - ]...)) - - # Generate toplevel functions. - toplevel = [] - bindings = Dict{Symbol,Any}( - c.name => Expr(:(.), name, QuoteNode(c.name)) for c in theory.types) - syntax_fns = Dict(parse_function_sig(f) => f for f in functions) - for f in interface(theory) - sig = parse_function_sig(f) - bindings[:new] = Expr(:(.), name, QuoteNode(sig.name)) - if haskey(syntax_fns, sig) - # Case 1: The method is overriden in the syntax body. - expr = generate_function(replace_symbols(bindings, syntax_fns[sig])) - elseif !isnothing(f.impl) - # Case 2: The method has a default implementation in the theory. - expr = generate_function(replace_symbols(bindings, f)) - else - # Case 3: Call the default syntax method. - params = [ gensym("x$i") for i in eachindex(sig.types) ] - call_expr = Expr(:call, sig.name, - [ Expr(:(::), pair...) for pair in zip(params, sig.types) ]...) - body = Expr(:call, :new, params...) - f_impl = JuliaFunction(call_expr, f.return_type, body) - expr = generate_function(replace_symbols(bindings, f_impl)) - end - push!(toplevel, expr) - end - Expr(:toplevel, mod, toplevel...) -end - -""" Complete set of Julia functions for a syntax system. -""" -function interface(theory::Theory)::Vector{JuliaFunction} - [ GAT.interface(theory); - [ GAT.constructor(constructor_for_generator(cons), theory) - for cons in theory.types ]; ] -end - -""" Generate syntax type definitions. -""" -function gen_type(cons::TypeConstructor, base_type::Type=Any)::Expr - base_expr = GlobalRef(SyntaxSystems, :GATExpr) - base_name = if base_type == Any - base_expr - else - GlobalRef(parentmodule(base_type), nameof(base_type)) - end - expr = :(struct $(cons.name){T} <: $base_name{T} - args::Vector - type_args::Vector{$base_expr} - end) - generate_docstring(strip_lines(expr, recurse=true), cons.doc) -end -function gen_types(theory::Theory, base_types::Vector{Type})::Vector{Expr} - if isempty(base_types) - map(gen_type, theory.types) - else - map(gen_type, theory.types, base_types) - end -end - -""" Generate accessor methods for type parameters. -""" -function gen_type_accessors(cons::TypeConstructor)::Vector{Expr} - fns = [] - sym = gensym(:x) - for (i, param) in enumerate(cons.params) - call_expr = Expr(:call, param, Expr(:(::), sym, cons.name)) - return_type = GAT.strip_type(cons.context[param]) - body = Expr(:ref, Expr(:(.), sym, QuoteNode(:type_args)), i) - push!(fns, generate_function(JuliaFunction(call_expr, return_type, body))) - end - fns -end -function gen_type_accessors(theory::Theory)::Vector{Expr} - vcat(map(gen_type_accessors, theory.types)...) -end - -""" Generate methods for syntax term constructors. -""" -function gen_term_constructor(cons::TermConstructor, theory::Theory, - mod::Module; dispatch_type::Symbol=Symbol())::Expr - head = GAT.constructor(cons, theory) - call_expr, return_type = head.call_expr, head.return_type - if dispatch_type == Symbol() - dispatch_type = cons.name - end - body = Expr(:block) - - # Create expression to check constructor domain. - eqs = GAT.equations(cons, theory) - if !isempty(eqs) - clauses = [ Expr(:call,:(==),lhs,rhs) for (lhs,rhs) in eqs ] - conj = foldr((x,y) -> Expr(:(&&),x,y), clauses) - insert!(call_expr.args, 2, - Expr(:parameters, Expr(:kw, :strict, false))) - push!(body.args, - Expr(:if, - Expr(:(&&), :strict, Expr(:call, :(!), conj)), - Expr(:call, :throw, - Expr(:call, GlobalRef(SyntaxSystems, :SyntaxDomainError), - Expr(:quote, cons.name), - Expr(:vect, cons.params...))))) - end - - # Create call to expression constructor. - type_params = gen_term_constructor_params(cons, theory, mod) - push!(body.args, - Expr(:call, - Expr(:curly, return_type, Expr(:quote, dispatch_type)), - Expr(:vect, cons.params...), - Expr(:vect, type_params...))) - - generate_function(JuliaFunction(call_expr, return_type, body)) -end -function gen_term_constructors(theory::Theory, mod::Module)::Vector{Expr} - [ gen_term_constructor(cons, theory, mod) for cons in theory.terms ] -end - -""" Generate expressions for type parameters of term constructor. - -Besides expanding the implicit variables, we must handle two annoying issues: - -1. Add types for method dispatch where necessary (see `GAT.add_type_dispatch`) - FIXME: We are currently only handling the nullary case (e.g., `munit()`). - To handle the general case, we need to do basic type inference. - -2. Rebind the term constructors to ensure that user overrides are preferred over - the default term constructors. -""" -function gen_term_constructor_params(cons, theory, mod)::Vector - expr = GAT.expand_term_type(cons, theory) - raw_params = @match expr begin - Expr(:call, name::Symbol, args...) => args - _::Symbol => [] - end - - bindings = Dict(c.name => GlobalRef(mod, c.name) for c in theory.terms) - params = [] - for expr in raw_params - expr = replace_nullary_constructors(expr, theory) - expr = replace_symbols(bindings, expr) - push!(params, expr) - end - params -end -function replace_nullary_constructors(expr, theory) - @match expr begin - Expr(:call, name::Symbol) => begin - terms = theory.terms[findall(cons -> cons.name == name, theory.terms)] - @assert length(terms) == 1 - Expr(:call, name, terms[1].typ) - end - Expr(:call, name::Symbol, args...) => - Expr(:call, name, [replace_nullary_constructors(a,theory) for a in args]...) - _ => expr - end -end - -""" Generate methods for term generators. - -Generators are extra term constructors created automatically for the syntax. -""" -function gen_term_generator(cons::TypeConstructor, theory::Theory, mod::Module)::Expr - gen_term_constructor(constructor_for_generator(cons), theory, mod; - dispatch_type = :generator) -end -function gen_term_generators(theory::Theory, mod::Module)::Vector{Expr} - [ gen_term_generator(cons, theory, mod) for cons in theory.types ] -end -function constructor_for_generator(cons::TypeConstructor)::TermConstructor - value_param = :__value__ - params = [ value_param; cons.params ] - typ = Expr(:call, cons.name, cons.params...) - context = merge(Context(value_param => :Any), cons.context) - TermConstructor(cons.name, params, typ, context) -end - -# Reflection -############ - -""" Invoke a term constructor by name in a syntax system. - -This method provides reflection for syntax systems. In everyday use the generic -method for the constructor should be called directly, not through this function. -""" -function invoke_term(syntax_module::Module, constructor_name::Symbol, args...) - theory_type = syntax_module.theory() - theory = GAT.theory(theory_type) - syntax_types = Tuple(getfield(syntax_module, cons.name) for cons in theory.types) - invoke_term(theory_type, syntax_types, constructor_name, args...) -end - -""" Name of constructor that created expression. -""" -constructor_name(expr::GATExpr) = head(expr) -constructor_name(expr::GATExpr{:generator}) = gat_typeof(expr) - -""" Create generator of the same type as the given expression. -""" -function generator_like(expr::GATExpr, value)::GATExpr - invoke_term(syntax_module(expr), gat_typeof(expr), - value, gat_type_args(expr)...) -end -"""As with [generator_like](@ref), but change the syntax instead of the name.""" -function generator_switch_syntax(syntax::Module,expr::GATExpr)::GATExpr - invoke_term(syntax, gat_typeof(expr), - nameof(expr), map(x->generator_switch_syntax(syntax,x),gat_type_args(expr))...) -end -""" Get syntax module of given expression. -""" -syntax_module(expr::GATExpr) = parentmodule(typeof(expr)) - -# Functors -########## - -""" Functor from GAT expression to GAT instance. - -Strictly speaking, we should call these "structure-preserving functors" or, -better, "model homomorphisms of GATs". But this is a category theory library, -so we'll go with the simpler "functor". - -A functor is completely determined by its action on the generators. There are -several ways to specify this mapping: - - 1. Specify a Julia instance type for each GAT type, using the required `types` - tuple. For this to work, the generator constructors must be defined for the - instance types. - - 2. Explicitly map each generator term to an instance value, using the - `generators` dictionary. - - 3. For each GAT type (e.g., object and morphism), specify a function mapping - generator terms of that type to an instance value, using the `terms` - dictionary. - -The `terms` dictionary can also be used for special handling of non-generator -expressions. One use case for this capability is defining forgetful functors, -which map non-generators to generators. -""" -function functor(types::Tuple, expr::GATExpr; - generators::AbstractDict=Dict(), terms::AbstractDict=Dict()) - # Special case: look up a specific generator. - if head(expr) == :generator && haskey(generators, expr) - return generators[expr] - end - - # Special case: look up by type of term (usually a generator). - name = constructor_name(expr) - if haskey(terms, name) - return terms[name](expr) - end - - # Otherwise, we need to call a term constructor (possibly for a generator). - # Recursively evalute the arguments. - term_args = [] - for arg in args(expr) - if isa(arg, GATExpr) - arg = functor(types, arg; generators=generators, terms=terms) - end - push!(term_args, arg) - end - - # Invoke the constructor in the codomain category! - theory_type = syntax_module(expr).theory() - invoke_term(theory_type, types, name, term_args...) -end - -# Serialization -############### - -""" Serialize expression as JSON-able S-expression. - -The format is an S-expression encoded as JSON, e.g., "compose(f,g)" is -represented as ["compose", f, g]. -""" -function to_json_sexpr(expr::GATExpr; by_reference::Function = x->false) - if head(expr) == :generator && by_reference(first(expr)) - to_json_sexpr(first(expr)) - else - [ string(constructor_name(expr)); - [ to_json_sexpr(arg; by_reference=by_reference) for arg in args(expr) ] ] - end -end -to_json_sexpr(x::Union{Bool,Real,String,Nothing}; kw...) = x -to_json_sexpr(x; kw...) = string(x) - -""" Deserialize expression from JSON-able S-expression. - -If `symbols` is true (the default), strings are converted to symbols. -""" -function parse_json_sexpr(syntax_module::Module, sexpr; - parse_head::Function = identity, - parse_reference::Function = x->error("Loading terms by name is disabled"), - parse_value::Function = identity, - symbols::Bool = true, - ) - theory_type = syntax_module.theory() - theory = GAT.theory(theory_type) - type_lens = Dict(cons.name => length(cons.params) for cons in theory.types) - - function parse_impl(sexpr::Vector, ::Type{Val{:expr}}) - name = Symbol(parse_head(symbols ? Symbol(sexpr[1]) : sexpr[1])) - nargs = length(sexpr) - 1 - args = map(enumerate(sexpr[2:end])) do (i, arg) - arg_kind = ((i == 1 && get(type_lens, name, nothing) == nargs-1) || - arg isa Union{Bool,Number,Nothing}) ? :value : :expr - parse_impl(arg, Val{arg_kind}) - end - invoke_term(syntax_module, name, args...) - end - parse_impl(x, ::Type{Val{:value}}) = parse_value(x) - parse_impl(x::String, ::Type{Val{:expr}}) = - parse_reference(symbols ? Symbol(x) : x) - parse_impl(x::String, ::Type{Val{:value}}) = - parse_value(symbols ? Symbol(x) : x) - - parse_impl(sexpr, Val{:expr}) -end - -# Pretty-print -############## - -""" Show the syntax expression as an S-expression. - -Cf. the standard library function `Meta.show_sexpr`. -""" -show_sexpr(expr::GATExpr) = show_sexpr(stdout, expr) - -function show_sexpr(io::IO, expr::GATExpr) - if head(expr) == :generator - print(io, repr(first(expr))) - else - print(io, "(") - join(io, [string(head(expr)); - [sprint(show_sexpr, arg) for arg in args(expr)]], " ") - print(io, ")") - end -end - -""" Show the expression in infix notation using Unicode symbols. -""" -show_unicode(expr::GATExpr) = show_unicode(stdout, expr) -show_unicode(io::IO, x::Any; kw...) = show(io, x) - -function show_unicode(io::IO, expr::GATExpr; kw...) - # By default, show in prefix notation. - print(io, head(expr)) - print(io, "{") - join(io, [sprint(show_unicode, arg) for arg in args(expr)], ",") - print(io, "}") -end - -function show_unicode(io::IO, expr::GATExpr{:generator}; kw...) - print(io, first(expr)) -end - -function show_unicode_infix(io::IO, expr::GATExpr, op::String; - paren::Bool=false) - show_unicode_paren(io, expr) = show_unicode(io, expr; paren=true) - if (paren) print(io, "(") end - join(io, [sprint(show_unicode_paren, arg) for arg in args(expr)], op) - if (paren) print(io, ")") end -end - -""" Show the expression in infix notation using LaTeX math. - -Does *not* include `\$` or `\\[begin|end]{equation}` delimiters. -""" -show_latex(expr::GATExpr) = show_latex(stdout, expr) -show_latex(io::IO, sym::Symbol; kw...) = print(io, sym) -show_latex(io::IO, x::Any; kw...) = show(io, x) - -function show_latex(io::IO, expr::GATExpr; kw...) - # By default, show in prefix notation. - print(io, "\\mathop{\\mathrm{$(head(expr))}}") - print(io, "\\left[") - join(io, [sprint(show_latex, arg) for arg in args(expr)], ",") - print(io, "\\right]") -end - -function show_latex(io::IO, expr::GATExpr{:generator}; kw...) - # Try to be smart about using text or math mode. - content = string(first(expr)) - if all(isletter, content) && length(content) > 1 - print(io, "\\mathrm{$content}") - else - print(io, content) - end -end - -function show_latex_infix(io::IO, expr::GATExpr, op::String; - paren::Bool=false, kw...) - show_latex_paren(io, expr) = show_latex(io, expr, paren=true) - sep = op == " " ? op : " $op " - if (paren) print(io, "\\left(") end - join(io, [sprint(show_latex_paren, arg) for arg in args(expr)], sep) - if (paren) print(io, "\\right)") end -end - -function show_latex_postfix(io::IO, expr::GATExpr, op::String; kw...) - @assert length(args(expr)) == 1 - print(io, "{") - show_latex(io, first(expr), paren=true) - print(io, "}") - print(io, op) -end - -function show_latex_script(io::IO, expr::GATExpr, head::String; - super::Bool=false, kw...) - print(io, head, super ? "^" : "_", "{") - join(io, [sprint(show_latex, arg) for arg in args(expr)], ",") - print(io, "}") -end - -end diff --git a/src/gats/TheoriesInstances.jl b/src/gats/TheoriesInstances.jl deleted file mode 100644 index 93cbb156a..000000000 --- a/src/gats/TheoriesInstances.jl +++ /dev/null @@ -1,702 +0,0 @@ -""" Generalized algebraic theories (GATs) and their models in Julia (instances). -""" -module TheoriesInstances -export @theory, @signature, @instance, theory, invoke_term - -using Base.Meta: ParseError -using StructEquality -using DataStructures: OrderedDict -using Logging -using MLStyle: @match - -using ..MetaUtils - -# Data types -############ - -const Context = OrderedDict{Symbol,Expr0} - -""" Type constructor in a GAT. -""" -@struct_hash_equal struct TypeConstructor - name::Symbol - params::Vector{Symbol} - context::Context - doc::Union{String,Nothing} - - function TypeConstructor(name::Symbol, params::Vector, - context::Context, doc=nothing) - new(name, params, context, doc) - end -end - -""" Term constructor in a GAT. -""" -@struct_hash_equal struct TermConstructor - name::Symbol - params::Vector{Symbol} - typ::Expr0 - context::Context - doc::Union{String,Nothing} - - function TermConstructor(name::Symbol, params::Vector, typ::Expr0, - context::Context, doc=nothing) - new(name, params, typ, context, doc) - end -end - -""" Axiom constructor in a GAT. -""" -@struct_hash_equal struct AxiomConstructor - name::Symbol - left::Expr0 - right::Expr0 - context::Context - doc::Union{String,Nothing} - - function AxiomConstructor(name::Symbol, left::Expr0, right::Expr0, - context::Context, doc=nothing) - new(name, left, right, context, doc) - end -end - -""" Data structure for a generalized algebraic theory (GAT). -""" -@struct_hash_equal struct Theory - types::Vector{TypeConstructor} - terms::Vector{TermConstructor} - axioms::Vector{AxiomConstructor} - aliases::Dict{Symbol,Symbol} -end - -struct TheoryBinding - name::Symbol - params::Vector{Symbol} -end -struct TheoryHead - main::TheoryBinding - base::Vector{TheoryBinding} - TheoryHead(main, base=[]) = new(main, base) -end - -# Theories -########## - -""" Define a generalized algebraic theory (GAT). - -Four kinds of things can go in the theory body: - -1. Type constructors, indicated by the special type `TYPE`, e.g., - `Hom(X::Ob,Y::Ob)::TYPE` -2. Term constructors, e.g., - `id(X::Ob)::Hom(X,X)` -3. Function aliases, e.g., - `@op Hom :→` -4. Equality axioms, e.g., - `f ⋅ id(B) == f ⊣ (A::Ob, B::Ob, f::(A → B))` - -A theory can extend existing theories (at present only one). -""" -macro theory(head, body) - theory_builder(head, body) -end - -""" Define a signature for a generalized algebraic theory (GAT). - -A signature is the same as a theory, except it may not contain axioms, and -therefore only three kinds of things can go in the signature body: - -1. Type constructors, indicated by the special type `TYPE`, e.g., - `Hom(X::Ob,Y::Ob)::TYPE` -2. Term constructors, e.g., - `id(X::Ob)::Hom(X,X)` -3. Function aliases, e.g., - `@op Hom :→` - -A signature can extend existing theories (at present only one). -""" -macro signature(head, body) - theory_builder(head, body, signature=true) -end - -""" Retrieve generalized algebraic theory associated with abstract type. - -For example, if `Category` is imported from `Catlab.Theories`, then -`theory(Category)`returns the theory of a category. -""" -function theory end - -""" Define how a theory is built, set up as a separate function to allow both - the signature and theory macros to share code and throw an error if any - axioms are defined in a signature. -""" -function theory_builder(head, body; signature=false) - # Parse theory header. - head = parse_theory_head(head) - @assert all(param in head.main.params - for base in head.base for param in base.params) - @assert length(head.base) <= 1 "Multiple theory extension not supported" - base_name = isempty(head.base) ? nothing : only(head.base).name - - # Parse theory body: GAT types/terms and function aliases. - types, terms, axioms, aliases = parse_theory_body(body) - if signature && length(axioms) > 0 - throw(ParseError("@signature macro does not allow axioms to be defined")) - end - theory = Theory(types, terms, axioms, aliases) - - # We must generate and evaluate the code at *run time* because the base - # theory, if specified, is not available at *parse time*. - expr = :(theory_code($head, $theory, $(esc(base_name)))) - Expr(:block, - Expr(:call, esc(:eval), expr), - :(Core.@__doc__ $(esc(head.main.name)))) -end - -function theory_code(head, theory, base_type) - # Add types/terms/aliases from base theory, if provided. - if !isnothing(base_type) - base_theory = TheoriesInstances.theory(base_type) - base_params = [ type.name for type in base_theory.types ] - bindings = Dict(zip(base_params, only(head.base).params)) - base_theory = replace_types(bindings, base_theory) - theory = Theory([base_theory.types; theory.types], - [base_theory.terms; theory.terms], - [base_theory.axioms; theory.axioms], - merge(base_theory.aliases, theory.aliases)) - end - theory = replace_types(theory.aliases, theory) - - # Names of generic functions in interface defined by theory. - names = unique!(vcat( - [ param for type in theory.types for param in type.params ], # Accessors. - [ term.name for term in theory.terms ], # Term constructors. - collect(keys(theory.aliases)) # Unicode aliases. - )) - - # Generate block with abstract type definition, registration of theory, - # and stubs for generic functions. - Expr(:block, - Expr(:abstract, head.main.name), - Expr(:(=), - Expr(:call, GlobalRef(TheoriesInstances, :theory), - Expr(:(::), Expr(:curly, :Type, head.main.name))), - theory), - (Expr(:function, name) for name in names)..., - ) -end - -function parse_theory_head(expr::Expr)::TheoryHead - parse = parse_theory_binding - parse_jl = parse_theory_binding_jlstyle - parse_either = parse_theory_binding_either - @match expr begin - (Expr(:call, :(=>), Expr(:tuple, bases), main) - => TheoryHead(parse(main), map(parse, bases))) - (Expr(:(<:), main, Expr(:tuple,bases)) - => TheoryHead(parse_jl(main), map(parse_jl, bases))) - Expr(:call, :(=>), base, main) => TheoryHead(parse(main), [parse(base)]) - Expr(:(<:), main, base) => TheoryHead(parse_jl(main), [parse_jl(base)]) - _ => TheoryHead(parse_either(expr)) - end -end - -function parse_theory_binding(expr::Expr)::TheoryBinding - @warn "Using Haskell-style theory declaration with parentheses is deprecated," * - " use Julia-style with curly braces." - @match expr begin - Expr(:call, name::Symbol, params...) => TheoryBinding(name, params) - _ => throw(ParseError("Ill-formed theory binding $expr")) - end -end - -function parse_theory_binding_jlstyle(expr::Expr)::TheoryBinding - @match expr begin - Expr(:curly, name::Symbol, params...) => TheoryBinding(name, params) - _ => throw(ParseError("Ill-formed theory binding $expr")) - end -end - -function parse_theory_binding_either(expr::Expr)::TheoryBinding - @match expr begin - Expr(:call, name::Symbol, params...) => TheoryBinding(name, params) - Expr(:curly, name::Symbol, params...) => TheoryBinding(name, params) - _ => throw(ParseError("Ill-formed theory binding $expr")) - end -end - -""" Parse the body of a GAT declaration. -""" -function parse_theory_body(expr::Expr) - @assert expr.head == :block - aliases = Dict{Symbol, Symbol}() - types = OrderedDict{Symbol,TypeConstructor}() - terms = TermConstructor[] - axioms = AxiomConstructor[] - for elem in strip_lines(expr).args - elem = strip_lines(elem) - head = last(parse_docstring(elem)).head - if head in (:(::), :call, :comparison, :where) - cons = parse_constructor(elem) - if isa(cons, TypeConstructor) - if haskey(types, cons.name) - throw(ParseError("Duplicate type constructor $elem")) - else - types[cons.name] = cons - end - elseif isa(cons, TermConstructor) - push!(terms, cons) - else - push!(axioms, cons) - end - elseif head == :macrocall && elem.args[1] == Symbol("@op") - if elem.args[2].head == :(:=) - aliases[elem.args[2].args[1]] = elem.args[2].args[2] - elseif elem.args[2].head == :block - merge!(aliases, Dict(map(x -> if x.head == :(:=) - x.args[1] => x.args[2] - else - throw(ParseError("Ill-formed alias $x")) - end, strip_lines(elem.args[2]).args))) - else - throw(ParseError("Ill-formed alias $elem")) - end - else - throw(ParseError("Ill-formed theory element $elem")) - end - end - return (collect(values(types)), terms, axioms, aliases) -end - -""" Get type constructor by name. - -Unlike term constructors, type constructors cannot be overloaded, so there is at -most one type constructor with a given name. -""" -function get_type(theory::Theory, name::Symbol)::TypeConstructor - indices = findall(cons -> cons.name == name, theory.types) - length(indices) < 1 && error("Malformed GAT definition type constructor for $name is missing") - length(indices) > 1 && error("Malformed GAT definition type constructor for $name cannot be overloaded") - theory.types[indices[1]] -end -function has_type(theory::Theory, name::Symbol)::Bool - findfirst(cons -> cons.name == name, theory.types) != nothing -end - -""" Add a type-valued first argument to a Julia function signature. - -We need this to avoid ambiguity in method dispatch when a term constructor has -no arguments (e.g., `munit()`) or more generally has no arguments that are types -in the signature (e.g., object generators in a category). - -The fundamental reason why these cases must be treated specially is that Julia -does not (yet?) support -[dispatching on return type](https://github.com/JuliaLang/julia/issues/19206). -""" -function add_type_dispatch(call_expr::Expr, type_expr::Expr0)::Expr - @match call_expr begin - (Expr(:call, name, args...) => - Expr(:call, name, :(::Type{$type_expr}), args...)) - _ => throw(ParseError("Ill-formed call expression $call_expr")) - end -end - -# GAT expressions -################# - -""" Parse a raw expression in a GAT. - -A "raw expression" is a just composition of function and constant symbols. -""" -function parse_raw_expr(expr) - @match expr begin - Expr(:call, args...) => map(parse_raw_expr, args) - head::Symbol => nothing - _ => throw(ParseError("Ill-formed raw expression $expr")) - end - expr # Return the expression unmodified. This function just checks syntax. -end - -""" Parse context for term or type in a GAT. -""" -function parse_context(expr::Expr)::Context - context = Context() - args = expr.head == :tuple ? expr.args : [ expr ] - for arg in args - name, type = @match arg begin - Expr(:(::), name::Symbol, type) => (name, parse_raw_expr(type)) - name::Symbol => (name, :Any) - _ => throw(ParseError("Ill-formed context expression $expr")) - end - if haskey(context, name) - throw(ParseError("Name $name already defined")) - end - context[name] = type - end - context -end - -""" Parse type or term constructor in a GAT. -""" -function parse_constructor(expr::Expr)::Union{TypeConstructor,TermConstructor, - AxiomConstructor} - # Context is optional. - doc, expr = parse_docstring(expr) - cons_expr, context = @match expr begin - Expr(:call, :<=, inner, context) => (inner, parse_context(context)) - Expr(:call, :⊣, inner, context) => (inner, parse_context(context)) - Expr(:comparison, cons_left, cons_sym, cons_right, :⊣, context) => ( - Expr(:call, cons_sym, cons_left, cons_right), parse_context(context)) - Expr(:where, inner, context) => (inner, parse_context(context)) - _ => (expr, Context()) - end - - # Allow abbreviated syntax where tail of context is included in parameters. - function parse_param(param::Expr0)::Symbol - name, type = @match param begin - Expr(:(::), name::Symbol, type) => (name, parse_raw_expr(type)) - name::Symbol => (name, :Any) - _ => throw(ParseError("Ill-formed type/term parameter $param")) - end - if !haskey(context, name) - context[name] = type - end - name - end - - @match cons_expr begin - (Expr(:(::), name::Symbol, :TYPE) - => TypeConstructor(name, [], context, doc)) - (Expr(:(::), Expr(:call, name::Symbol, params...), :TYPE) - => TypeConstructor(name, map(parse_param, params), context, doc)) - (Expr(:(::), Expr(:call, name::Symbol, params...), type) - => TermConstructor(name, map(parse_param, params), parse_raw_expr(type), - context, doc)) - (Expr(:call, :(==), left, right) - => AxiomConstructor(:(==), left, right, context, doc)) - _ => throw(ParseError("Ill-formed type/term constructor $cons_expr")) - end -end - -""" Replace names of type constructors in a GAT. -""" -function replace_types(bindings::Dict, theory::Theory)::Theory - Theory([ replace_types(bindings, t) for t in theory.types ], - [ replace_types(bindings, t) for t in theory.terms ], - [ replace_types(bindings, t) for t in theory.axioms ], - replace_types(bindings, theory.aliases)) -end -function replace_types(bindings::Dict, cons::TypeConstructor)::TypeConstructor - TypeConstructor(replace_symbols(bindings, cons.name), cons.params, - replace_types(bindings, cons.context), cons.doc) -end -function replace_types(bindings::Dict, cons::TermConstructor)::TermConstructor - TermConstructor(cons.name, cons.params, - replace_symbols(bindings, cons.typ), - replace_types(bindings, cons.context), cons.doc) -end -function replace_types(bindings::Dict, cons::AxiomConstructor)::AxiomConstructor - AxiomConstructor(cons.name, - replace_symbols(bindings, cons.left), - replace_symbols(bindings, cons.right), - replace_types(bindings, cons.context), cons.doc) -end -function replace_types(bindings::Dict, aliases::Dict)::Dict - Dict(a => replace_symbols(bindings, aliases[a]) - for a in keys(aliases)) -end -function replace_types(bindings::Dict, context::Context)::Context - Context(((name => @match expr begin - (Expr(:call, sym::Symbol, args...) => - Expr(:call, replace_symbols(bindings, sym), args...)) - sym::Symbol => replace_symbols(bindings, sym) - end) for (name, expr) in context)) -end - -""" Remove type parameters from dependent type. -""" -function strip_type(expr)::Symbol - @match expr begin - Expr(:call, head::Symbol, args...) => head - sym::Symbol => sym - end -end - -# GAT expressions -################# - -""" Expand context variables that occur implicitly in an expression. - -Reference: (Cartmell, 1986, Sec 10: 'Informal syntax'). -""" -function expand_in_context(expr, params::Vector{Symbol}, - context::Context, theory::Theory) - @match expr begin - Expr(:call, name::Symbol, args...) => begin - expanded = [expand_in_context(e, params, context, theory) for e in args] - Expr(:call, name, expanded...) - end - name::Symbol => begin - if name in params - name - elseif haskey(context, name) - expand_symbol_in_context(name, params, context, theory) - else - error("Name $name missing from context $context") - end - end - _ => throw(ParseError("Ill-formed raw expression $expr")) - end -end -function expand_symbol_in_context(sym::Symbol, params::Vector{Symbol}, - context::Context, theory::Theory) - # This code expands symbols that occur as direct arguments to type - # constructors. If there are term constructors in between, it does not work: - # indeed, it cannot work in general because the term constructors are not - # necessarily injective. For example, we can expand :X in - # (:X => :Ob, :f => :(Hom(X))) - # but not in - # (:X => :Ob, :Y => :Ob, :f => :(Hom(otimes(X,Y)))) - names = collect(keys(context)) - start = findfirst(names .== sym) - for name in names[start+1:end] - expr = context[name] - if isa(expr, Expr) && expr.head == :call && sym in expr.args[2:end] - cons = get_type(theory, expr.args[1]) - accessor = cons.params[findfirst(expr.args[2:end] .== sym)] - expanded = Expr(:call, accessor, name) - return expand_in_context(expanded, params, context, theory) - end - end - error("Name $sym does not occur explicitly among $params in context $context") -end - -""" Expand context variables that occur implicitly in the type expression -of a term constructor. -""" -function expand_term_type(cons::TermConstructor, theory::Theory) - isa(cons.typ, Symbol) ? cons.typ : - expand_in_context(cons.typ, cons.params, cons.context, theory) -end - -""" Implicit equations defined by a context. - -This function allows a generalized algebraic theory (GAT) to be expressed as -an essentially algebraic theory, i.e., as partial functions whose domains are -defined by equations. - -References: - - (Cartmell, 1986, Sec 6: "Essentially algebraic theories and categories with - finite limits") - - (Freyd, 1972, "Aspects of topoi") -""" -function equations(context::Context, theory::Theory)::Vector{Pair} - # The same restrictions as `expand_symbol_in_context` apply here. - eqs = Pair[] - names = collect(keys(context)) - for (start, var) in enumerate(names) - for name in names[start+1:end] - expr = context[name] - if isa(expr, Symbol) && !has_type(theory, expr) - # If the constructor is a symbol and there isn't a matching type in - # the theory, assume it's a Julia type. For now, these are - # completely ignored by the syntax system. - continue - end - expr = isa(expr, Symbol) ? Expr(:call, expr) : expr - cons = get_type(theory, expr.args[1]) - accessors = cons.params[findall(expr.args[2:end] .== var)] - append!(eqs, (Expr(:call, a, name) => var for a in accessors)) - end - end - eqs -end - -""" Implicit equations defined by context, allowing for implicit variables. -""" -function equations(params::Vector{Symbol}, context::Context, - theory::Theory)::Vector{Pair} - eqs = [ (expand_in_context(lhs, params, context, theory) => - expand_in_context(rhs, params, context, theory)) - for (lhs, rhs) in equations(context, theory) ] - # Remove tautologies (expr == expr) resulting from expansions. - # FIXME: Should we worry about redundancies from the symmetry of equality, - # i.e., (expr1 == expr2) && (expr2 == expr1)? - filter(eq -> eq.first != eq.second, eqs) -end - -""" Implicit equations for term constructor. -""" -function equations(cons::TermConstructor, theory::Theory)::Vector{Pair} - equations(cons.params, cons.context, theory) -end - -# Instances -########### - -""" Define an *instance* of a generalized algebraic theory (GAT). -""" -macro instance(head, body) - # Parse the instance definition. - head = parse_theory_binding_either(head) - functions, ext_functions = parse_instance_body(body) - - # We must generate and evaluate the code at *run time* because the theory - # type is not defined at *parse time*. - # Also, we "throw away" any docstring. - # FIXME: Is there a better place to put the docstring? - expr = :(instance_code($(esc(head.name)), $(esc(head.params)), $functions, $ext_functions)) - Expr(:block, - Expr(:call, esc(:eval), expr), - :(Core.@__doc__ abstract type $(esc(gensym(:instance_doc))) end)) # /dev/null -end -function instance_code(theory_type, instance_types, instance_fns, external_fns) - code = Expr(:block) - theory = TheoriesInstances.theory(theory_type) - bindings = Dict(zip([type.name for type in theory.types], instance_types)) - bound_fns = [ replace_symbols(bindings, f) for f in interface(theory) ] - bound_fns = OrderedDict(parse_function_sig(f) => f for f in bound_fns) - instance_fns = Dict(parse_function_sig(f) => f for f in instance_fns) - for (sig, f) in bound_fns - if sig.name in external_fns - continue - elseif haskey(instance_fns, sig) - f_impl = instance_fns[sig] - elseif !isnothing(f.impl) - f_impl = f - else - error("Method $(f.call_expr) not implemented in $(nameof(mod)) instance") - end - push!(code.args, generate_function(f_impl)) - end - return code -end - -""" Parse the body of a GAT instance definition. -""" -function parse_instance_body(expr::Expr) - @assert expr.head == :block - funs = JuliaFunction[] - ext_funs = Symbol[] - for elem in strip_lines(expr).args - elem = strip_lines(elem) - head = elem.head - if head == :macrocall && elem.args[1] == Symbol("@import") - ext_funs = @match elem.args[2] begin - sym::Symbol => [ext_funs; [sym]] - Expr(:tuple, args...) => [ext_funs; Symbol[args...]] - end - else - push!(funs, parse_function(elem)) - end - end - return (funs, ext_funs) -end - -""" Complete set of Julia functions for a theory. -""" -function interface(theory::Theory)::Vector{JuliaFunction} - [ accessors(theory); - constructors(theory); - alias_functions(theory) ] -end - -""" Julia functions for type parameter accessors. -""" -function accessors(theory::Theory)::Vector{JuliaFunction} - vcat(map(accessors, theory.types)...) -end -function accessors(cons::TypeConstructor)::Vector{JuliaFunction} - [ JuliaFunction(Expr(:call, param, Expr(:(::), cons.name)), - strip_type(cons.context[param])) - for param in cons.params ] -end - -""" Julia functions for term constructors of GAT. -""" -function constructors(theory::Theory)::Vector{JuliaFunction} - [ constructor(cons, theory) for cons in theory.terms ] -end -function constructor(cons::Union{TypeConstructor,TermConstructor}, - theory::Theory)::JuliaFunction - arg_names = cons.params - arg_types = [ strip_type(cons.context[name]) for name in arg_names ] - args = [ Expr(:(::), name, typ) for (name,typ) in zip(arg_names, arg_types) ] - return_type = cons isa TermConstructor ? strip_type(cons.typ) : cons.name - - call_expr = Expr(:call, cons.name, args...) - if !any(has_type(theory, typ) for typ in arg_types) - call_expr = add_type_dispatch(call_expr, return_type) - end - JuliaFunction(call_expr, return_type) -end - -""" Julia functions for term and type aliases of GAT. -""" -function alias_functions(theory::Theory)::Vector{JuliaFunction} - collect(Iterators.flatten(map(collect(theory.aliases)) do alias - # collect all of the destination function definitions to alias - # allows an alias to overite all the type definitions of a function - dests = filter(term -> term.name == last(alias), theory.terms) - # If there are no matching functions, throw a parse error - if isempty(dests) && !any(type.name == last(alias) for type in theory.types) - throw(ParseError("Cannot alias undefined type or term $alias")) - end - # For each destination, create a Julia function - map(dests) do dest - fun = constructor(dest, theory) - fun.call_expr.args[1] = first(alias) - # Extract arguments from function header, handling special case of - # created by `add_type_dispatch`. - args = map(fun.call_expr.args[2:end]) do arg - @match arg begin - # Special case: dispatch on return type. - Expr(:(::), Expr(:curly, :Type, type)) => type - # Main case: typed parameter. - Expr(:(::), param, type) => param - _ => throw(ParseError("Cannot parse argument $arg for alias $alias")) - end - end - body = Expr(:call, dest.name, args...) - JuliaFunction(fun.call_expr, fun.return_type, body) - end - end)) -end - -""" Invoke a term constructor by name on an instance. - -This method provides reflection for GATs. In everyday use the generic -method for the constructor should be called directly, not through this function. - -Cf. Julia's builtin `invoke()` function. -""" -function invoke_term(theory_type::Type, instance_types::Tuple, - constructor_name::Symbol, args...) - # Get the corresponding Julia method from the parent module. - method = getfield(parentmodule(theory_type), constructor_name) - args = collect(Any, args) - - # Add dispatch on return type, if necessary. - if !any(typeof(arg) <: typ for typ in instance_types for arg in args) - # Case 1: Name refers to type constructor, e.g., generator constructor - # in syntax system. - theory = TheoriesInstances.theory(theory_type) - index = findfirst(cons -> cons.name == constructor_name, theory.types) - if isnothing(index) - # Case 2: Name refers to term constructor. - # FIXME: Terms constructors can be overloaded, so there may be multiple - # term constructors with the same name. Distinguishing them requires type - # inference. I am punting on that right now. - constructor = theory.terms[ - findfirst(cons -> cons.name == constructor_name, theory.terms)] - return_name = strip_type(constructor.typ) - index = findfirst(cons -> cons.name == return_name, theory.types) - end - insert!(args, 1, instance_types[index]) - end - - # Invoke the method! - method(args...) -end - -end diff --git a/src/graphs/BasicGraphs.jl b/src/graphs/BasicGraphs.jl index 092a87f51..16dbf1d02 100644 --- a/src/graphs/BasicGraphs.jl +++ b/src/graphs/BasicGraphs.jl @@ -16,7 +16,7 @@ export HasVertices, HasGraph, AbstractGraph, Graph, SchGraph, add_edge!, add_edges!, add_vertex!, add_vertices!, add_vertices_with_indices!, rem_edge!, rem_edges!, rem_vertex!, rem_vertices!, neighbors, inneighbors, outneighbors, all_neighbors, degree, induced_subgraph, - AbstractSymmetricGraph, SymmetricGraph, SchSymmetricGraph, inv, + AbstractSymmetricGraph, SymmetricGraph, SchSymmetricGraph, inv, is_directed, AbstractReflexiveGraph, ReflexiveGraph, SchReflexiveGraph, refl, AbstractSymmetricReflexiveGraph, SymmetricReflexiveGraph, SchSymmetricReflexiveGraph, AbstractHalfEdgeGraph, HalfEdgeGraph, SchHalfEdgeGraph, vertex, half_edges, @@ -28,6 +28,7 @@ export HasVertices, HasGraph, AbstractGraph, Graph, SchGraph, import Base: inv using ACSets, ...ACSetsGATsInterop +using ...Theories import ...Theories: src, tgt # Base types @@ -239,7 +240,7 @@ function induced_subgraph(g::G, vs::AbstractVector{Int}) where G <: HasGraph end # Symmetric graphs -################## + @present SchSymmetricGraph <: SchGraph begin inv::Hom(E,E) @@ -298,6 +299,8 @@ neighbors(g::AbstractSymmetricGraph, v::Int) = @inline outneighbors(g::AbstractSymmetricGraph, v::Int) = neighbors(g, v) @inline all_neighbors(g::AbstractSymmetricGraph, v::Int) = neighbors(g, v) +@generated is_directed(::Type{T}) where {S, T<:StructACSet{S}} = :inv ∉ homs(S) + # Reflexive graphs ################## @@ -571,7 +574,7 @@ Interchange src and tgt, keeping all other data the same """ function Base.reverse!(g::G) where G<:HasGraph tmp = deepcopy(g[:tgt]) - g[:tgt] = g[:src] + g[:tgt] = g[:src] g[:src] = tmp return g end diff --git a/src/graphs/BipartiteGraphs.jl b/src/graphs/BipartiteGraphs.jl index 608e80e15..e0af1f878 100644 --- a/src/graphs/BipartiteGraphs.jl +++ b/src/graphs/BipartiteGraphs.jl @@ -18,6 +18,8 @@ export HasBipartiteVertices, HasBipartiteGraph, rem_edge₁₂!, rem_edge₂₁!, rem_edges₁₂!, rem_edges₂₁! using ACSets, ...ACSetsGATsInterop +using GATlab +using ...Theories using ..BasicGraphs: flatten import ..BasicGraphs: nv, ne, vertices, edges, src, tgt, add_edge!, add_edges!, rem_edge!, rem_edges!, inneighbors, outneighbors diff --git a/src/graphs/GraphAlgorithms.jl b/src/graphs/GraphAlgorithms.jl index 6674bf1ca..78bea8682 100644 --- a/src/graphs/GraphAlgorithms.jl +++ b/src/graphs/GraphAlgorithms.jl @@ -7,7 +7,7 @@ export connected_components, connected_component_projection, connected_component using DataStructures: Stack, DefaultDict using ACSets -using ...Theories: dom, codom +using ...Theories.ThSchema: dom, codom using ..BasicGraphs using ..PropertyGraphs: ReflexiveEdgePropertyGraph diff --git a/src/graphs/GraphGenerators.jl b/src/graphs/GraphGenerators.jl index a99631676..01c21388e 100644 --- a/src/graphs/GraphGenerators.jl +++ b/src/graphs/GraphGenerators.jl @@ -7,7 +7,6 @@ using Random: GLOBAL_RNG using ACSets using ..BasicGraphs -using ...Theories: hom """ Path graph on ``n`` vertices. """ @@ -73,8 +72,6 @@ function parallel_arrows(::Type{T}, n::Int; V=(;), E=(;)) where T <: ACSet g end -# Should this be exported from `BasicGraphs`? -@generated is_directed(::Type{T}) where {S, T<:StructACSet{S}} = :inv ∉ hom(S) getRNG(seed::Integer, rng::AbstractRNG) = seed >= 0 ? MersenneTwister(seed) : rng diff --git a/src/graphs/PropertyGraphs.jl b/src/graphs/PropertyGraphs.jl index 8e0b88efc..37e790df4 100644 --- a/src/graphs/PropertyGraphs.jl +++ b/src/graphs/PropertyGraphs.jl @@ -6,7 +6,9 @@ export AbstractPropertyGraph, PropertyGraph, SchPropertyGraph, set_gprop!, set_vprop!, set_eprop!, set_gprops!, set_vprops!, set_eprops! using ACSets -using ...GATs, ..BasicGraphs +using GATlab +using ...Theories +using ..BasicGraphs import ..BasicGraphs: nv, ne, src, tgt, inv, edges, vertices, has_edge, has_vertex, add_edge!, add_edges!, add_vertex!, add_vertices! diff --git a/src/theories/Category.jl b/src/theories/Category.jl index 5261fec39..4ebc484f2 100644 --- a/src/theories/Category.jl +++ b/src/theories/Category.jl @@ -5,50 +5,14 @@ export ThCategory, FreeCategory, Ob, Hom, dom, codom, id, compose, ⋅, ThMCategory, FreeMCategory, Tight, reflexive, transitive, ThPointedSetCategory, FreePointedSetCategory, zeromap -import Base: inv, show - # Category ########## -""" Theory of *categories* - -**Note**: Throughout Catlab, we compose morphisms in diagrammatic order (from -left to right), i.e., if ``f:A→B`` and ``g:B→C`` then the composite morphism -``f⋅g:A→C`` is `compose(f,g)`. Under this convention, functions are applied on -the right, e.g., if ``a ∈ A`` then ``af ∈ B``. - -We retain the usual meaning of the symbol ``∘`` (`\\circ`), i.e., ``g∘f`` is -`compose(f,g)`. This usage is too entrenched to overturn. However, we use the -symbol ``⋅`` (`\\cdot`) for composition in diagrammatic order. -""" -@theory ThCategory{Ob,Hom} begin - # Unicode aliases. - @op begin - (→) := Hom - (⋅) := compose - end - - """ Object in a category """ - Ob::TYPE - - """ Morphism in a category """ - Hom(dom::Ob,codom::Ob)::TYPE - - id(A::Ob)::(A → A) - compose(f::(A → B), g::(B → C))::(A → C) ⊣ (A::Ob, B::Ob, C::Ob) - - # Category axioms. - ((f ⋅ g) ⋅ h == f ⋅ (g ⋅ h) - ⊣ (A::Ob, B::Ob, C::Ob, D::Ob, f::(A → B), g::(B → C), h::(C → D))) - f ⋅ id(B) == f ⊣ (A::Ob, B::Ob, f::(A → B)) - id(A) ⋅ f == f ⊣ (A::Ob, B::Ob, f::(A → B)) -end - # Convenience constructors compose(fs::AbstractVector) = foldl(compose, fs) compose(f, g, h, fs...) = compose([f, g, h, fs...]) -@syntax FreeCategory{ObExpr,HomExpr} ThCategory begin +@symbolic_model FreeCategory{ObExpr,HomExpr} ThCategory begin compose(f::Hom, g::Hom) = associate_unit(new(f,g; strict=true), id) end @@ -67,6 +31,7 @@ function show(io::IO, ::MIME"text/plain", expr::HomExpr) print(io, " → ") show_unicode(io, codom(expr)) end + function show(io::IO, ::MIME"text/latex", expr::HomExpr) print(io, "\$") show_latex(io, expr) @@ -82,14 +47,14 @@ end """ Theory of *groupoids*. """ -@theory ThGroupoid{Ob,Hom} <: ThCategory{Ob,Hom} begin - inv(f::(A → B))::(B → A) ⊣ (A::Ob, B::Ob) +@theory ThGroupoid <: ThCategory begin + inv(f::(A → B))::(B → A) ⊣ [A::Ob, B::Ob] - f ⋅ inv(f) == id(A) ⊣ (A::Ob, B::Ob, f::(A → B)) - inv(f) ⋅ f == id(B) ⊣ (A::Ob, B::Ob, f::(A → B)) + f ⋅ inv(f) == id(A) ⊣ [A::Ob, B::Ob, f::(A → B)] + inv(f) ⋅ f == id(B) ⊣ [A::Ob, B::Ob, f::(A → B)] end -@syntax FreeGroupoid{ObExpr,HomExpr} ThGroupoid begin +@symbolic_model FreeGroupoid{ObExpr,HomExpr} ThGroupoid begin compose(f::Hom, g::Hom) = associate_unit_inv(new(f,g; strict=true), id, inv) inv(f::Hom) = distribute_unary(involute(new(f)), inv, compose, unit=id, contravariant=true) @@ -102,23 +67,23 @@ end Axiomatized as a covariant category action. """ -@theory ThCopresheaf{Ob,Hom,El} <: ThCategory{Ob,Hom} begin +@theory ThCopresheaf <: ThCategory begin # copresheaf = object-indexed family El(ob::Ob)::TYPE # functoriality = covariant action - act(x::El(A), f::Hom(A,B))::El(B) ⊣ (A::Ob, B::Ob) - @op (⋅) := act + act(x::El(A), f::Hom(A,B))::El(B) ⊣ [A::Ob, B::Ob] + # @op (⋅) := act # action equations - act(act(x, f), g) == act(x, (f ⋅ g)) ⊣ - (A::Ob, B::Ob, C::Ob, f::(A → B), g::(B → C), x::El(A)) - act(x, id(A)) == x ⊣ (A::Ob, x::El(A)) + (act(act(x, f), g) == act(x, (f ⋅ g))) ⊣ + [A::Ob, B::Ob, C::Ob, f::(A → B), g::(B → C), x::El(A)] + (act(x, id(A)) == x) ⊣ [A::Ob, x::El(A)] end abstract type ElExpr{T} <: GATExpr{T} end -@syntax FreeCopresheaf{ObExpr,HomExpr,ElExpr} ThCopresheaf begin +@symbolic_model FreeCopresheaf{ObExpr,HomExpr,ElExpr} ThCopresheaf begin compose(f::Hom, g::Hom) = associate_unit(new(f,g; strict=true), id) end @@ -126,21 +91,21 @@ end Axiomatized as a contravariant category action. """ -@theory ThPresheaf{Ob,Hom,El} <: ThCategory{Ob,Hom} begin +@theory ThPresheaf <: ThCategory begin # presheaf = object-indexed family El(ob::Ob)::TYPE # functoriality = contravariant action - coact(f::Hom(A,B), x::El(B))::El(A) ⊣ (A::Ob, B::Ob) - @op (⋅) := coact + coact(f::Hom(A,B), x::El(B))::El(A) ⊣ [A::Ob, B::Ob] + # @op (⋅) := coact # action equations - coact(f, coact(g, x)) == coact((f ⋅ g), x) ⊣ - (A::Ob, B::Ob, C::Ob, f::(A → B), g::(B → C), x::El(C)) - coact(id(A), x) == x ⊣ (A::Ob, x::El(A)) + (coact(f, coact(g, x)) == coact((f ⋅ g), x)) ⊣ + [A::Ob, B::Ob, C::Ob, f::(A → B), g::(B → C), x::El(C)] + (coact(id(A), x) == x) ⊣ [A::Ob, x::El(A)] end -@syntax FreePresheaf{ObExpr,HomExpr,ElExpr} ThPresheaf begin +@symbolic_model FreePresheaf{ObExpr,HomExpr,ElExpr} ThPresheaf begin compose(f::Hom, g::Hom) = associate_unit(new(f,g; strict=true), id) end @@ -149,6 +114,7 @@ function show(io::IO, ::MIME"text/plain", expr::ElExpr) print(io, ": ") show_unicode(io, ob(expr)) end + function show(io::IO, ::MIME"text/latex", expr::ElExpr) print(io, "\$") show_latex(io, expr) @@ -180,21 +146,21 @@ the arrow category of Set spanned by injections. In the following GAT, tightness is axiomatized as a property of morphisms: a dependent family of sets over the hom-sets, each having at most one inhabitant. """ -@theory ThMCategory{Ob,Hom,Tight} <: ThCategory{Ob,Hom} begin - Tight(hom::Hom(A,B))::TYPE ⊣ (A::Ob, B::Ob) +@theory ThMCategory <: ThCategory begin + Tight(hom::Hom(A,B))::TYPE ⊣ [A::Ob, B::Ob] # Tightness is a property. - t == t′ ⊣ (A::Ob, B::Ob, f::Hom(A,B), t::Tight(f), t′::Tight(f)) + (t == t′) ⊣ [A::Ob, B::Ob, f::Hom(A,B), t::Tight(f), t′::Tight(f)] # Tight morphisms form a subcategory. reflexive(A::Ob)::Tight(id(A)) - transitive(t::Tight(f), u::Tight(g))::Tight(compose(f,g)) ⊣ - (A::Ob, B::Ob, C::Ob, f::Hom(A,B), g::Hom(B,C), t::Tight(f), u::Tight(g)) + transitive(t, u)::Tight(compose(f,g)) ⊣ + [A::Ob, B::Ob, C::Ob, f::Hom(A,B), g::Hom(B,C), t::Tight(f), u::Tight(g)] end abstract type TightExpr{T} <: GATExpr{T} end -@syntax FreeMCategory{ObExpr,HomExpr,TightExpr} ThMCategory begin +@symbolic_model FreeMCategory{ObExpr,HomExpr,TightExpr} ThMCategory begin compose(f::Hom, g::Hom) = associate_unit(new(f,g; strict=true), id) transitive(t::Tight, u::Tight) = associate_unit(new(t,u; strict=true), reflexive) end @@ -204,17 +170,17 @@ Theory of a pointed set-enriched category. We axiomatize a category equipped with zero morphisms. A functor from an ordinary category into a freely generated -pointed-set enriched category, -equivalently, a pointed-set enriched category in which no two nonzero maps +pointed-set enriched category, +equivalently, a pointed-set enriched category in which no two nonzero maps compose to a zero map, is a good notion of a functor that's total on objects and partial on morphisms. """ -@theory ThPointedSetCategory{Ob,Hom} <: ThCategory{Ob,Hom} begin - zeromap(A,B)::Hom(A,B)⊣(A::Ob,B::Ob) - compose(zeromap(A,B),f::(B→C))==zeromap(A,C)⊣(A::Ob,B::Ob,C::Ob) - compose(g::(A→B),zeromap(A,B))==zeromap(A,C)⊣(A::Ob,B::Ob,C::Ob) +@theory ThPointedSetCategory <: ThCategory begin + zeromap(A,B)::Hom(A,B)⊣[A::Ob,B::Ob] + (compose(zeromap(A,B),f)==zeromap(A,C))⊣[A::Ob,B::Ob,C::Ob,f::(B→C)] + (compose(g,zeromap(A,B))==zeromap(A,C))⊣[A::Ob,B::Ob,C::Ob,g::(C→A)] end -@syntax FreePointedSetCategory{ObExpr,HomExpr} ThPointedSetCategory begin +@symbolic_model FreePointedSetCategory{ObExpr,HomExpr} ThPointedSetCategory begin compose(f::Hom,g::Hom) = associate_unit(normalize_zero(new(f,g; strict=true)), id) end diff --git a/src/theories/HigherCategory.jl b/src/theories/HigherCategory.jl index bac5b7d1e..7dd4b5210 100644 --- a/src/theories/HigherCategory.jl +++ b/src/theories/HigherCategory.jl @@ -9,7 +9,6 @@ export ThCategory2, FreeCategory2, Hom2, Hom2Expr, composeH, *, FreeSymmetricMonoidalDoubleCategory, ThCartesianDoubleCategory -import Base: * abstract type Hom2Expr{T} <: CategoryExpr{T} end abstract type ArrExpr{T} <: CategoryExpr{T} end @@ -21,26 +20,26 @@ abstract type CellExpr{T} <: CategoryExpr{T} end """ Theory of *2-categories* """ -@signature ThCategory2{Ob,Hom,Hom2} <: ThCategory{Ob,Hom} begin - """ 2-morphism in a 2-category """ - Hom2(dom::Hom(A,B), codom::Hom(A,B))::TYPE ⊣ (A::Ob, B::Ob) +@signature ThCategory2 <: ThCategory begin + # """ 2-morphism in a 2-category """ + Hom2(dom::Hom(A,B), codom::Hom(A,B))::TYPE ⊣ [A::Ob, B::Ob] @op begin (⇒) := Hom2 (*) := composeH end # Hom categories: vertical composition - id(f)::(f ⇒ f) ⊣ (A::Ob, B::Ob, f::(A → B)) + id(f)::(f ⇒ f) ⊣ [A::Ob, B::Ob, f::(A → B)] compose(α::(f ⇒ g), β::(g ⇒ h))::(f ⇒ h) ⊣ - (A::Ob, B::Ob, f::(A → B), g::(A → B), h::(A → B)) + [A::Ob, B::Ob, f::(A → B), g::(A → B), h::(A → B)] # Horizontal composition, including whiskering composeH(α::(f ⇒ g), β::(h ⇒ k))::((f ⋅ h) ⇒ (g ⋅ k)) ⊣ - (A::Ob, B::Ob, C::Ob, f::(A → B), g::(A → B), h::(B → C), k::(B → C)) + [A::Ob, B::Ob, C::Ob, f::(A → B), g::(A → B), h::(B → C), k::(B → C)] composeH(α::(f ⇒ g), h::(B → C))::((f ⋅ h) ⇒ (g ⋅ h)) ⊣ - (A::Ob, B::Ob, C::Ob, f::(A → B), g::(A → B)) + [A::Ob, B::Ob, C::Ob, f::(A → B), g::(A → B)] composeH(f::(A → B), β::(g ⇒ h))::((f ⋅ g) ⇒ (f ⋅ h)) ⊣ - (A::Ob, B::Ob, C::Ob, g::(B → C), h::(B → C)) + [A::Ob, B::Ob, C::Ob, g::(B → C), h::(B → C)] end # Convenience constructors @@ -51,7 +50,7 @@ composeH(α, β, γ, αs...) = composeH([α, β, γ, αs...]) This syntax checks domains of morphisms but not 2-morphisms. """ -@syntax FreeCategory2{ObExpr,HomExpr,Hom2Expr} ThCategory2 begin +@symbolic_model FreeCategory2{ObExpr,HomExpr,Hom2Expr} ThCategory2 begin compose(f::Hom, g::Hom) = associate_unit(new(f,g; strict=true), id) compose(α::Hom2, β::Hom2) = associate_unit(new(α,β), id) composeH(α::Hom2, β::Hom2) = associate(new(α,β)) @@ -85,13 +84,13 @@ The domain and codomain (top and bottom) of a cell are given by the domain and codomain in ``D₁`` and the source and target (left and right) are given by the functors ``S,T``. """ -@theory ThDoubleCategory{Ob,Hom,Pro,Cell} <: ThCategory{Ob,Hom} begin - """ Proarrow (horizontal morphism) in a double category """ +@theory ThDoubleCategory <: ThCategory begin + # """ Proarrow (horizontal morphism) in a double category """ Pro(src::Ob, tgt::Ob)::TYPE - """ 2-cell in a double category """ + # """ 2-cell in a double category """ Cell(dom::Pro(A,B), codom::Pro(C,D), - src::Hom(A,C), tgt::Hom(B,D))::TYPE ⊣ (A::Ob, B::Ob, C::Ob, D::Ob) + src::Hom(A,C), tgt::Hom(B,D))::TYPE ⊣ [A::Ob, B::Ob, C::Ob, D::Ob] @op begin (↛) := Pro @@ -104,46 +103,46 @@ functors ``S,T``. # Category D₁: category structure on proarrows and cells. compose(α::Cell(m, n, f, g), β::Cell(n, p, h, k))::Cell(m, p, f⋅h, g⋅k) ⊣ - (A::Ob, B::Ob, C::Ob, X::Ob, Y::Ob, Z::Ob, + [A::Ob, B::Ob, C::Ob, X::Ob, Y::Ob, Z::Ob, m::(A ↛ X), n::(B ↛ Y), p::(C ↛ Z), - f::(A → B), g::(X → Y), h::(B → C), k::(Y → Z)) - id(m::(A ↛ B))::Cell(m, m, id(A), id(B)) ⊣ (A::Ob, B::Ob) + f::(A → B), g::(X → Y), h::(B → C), k::(Y → Z)] + id(m::(A ↛ B))::Cell(m, m, id(A), id(B)) ⊣ [A::Ob, B::Ob] (α ⋅ β) ⋅ γ == α ⋅ (β ⋅ γ) ⊣ - (A::Ob, B::Ob, C::Ob, D::Ob, W::Ob, X::Ob, Y::Ob, Z::Ob, + [A::Ob, B::Ob, C::Ob, D::Ob, W::Ob, X::Ob, Y::Ob, Z::Ob, m::(A↛W), n::(B↛X), p::(C↛Y), q::(D↛Z), f::(A→B), g::(B→C), h::(C→D), i::(W→X), j::(X→Y), k::(Y→Z), - α::Cell(m,n,f,i), β::Cell(n,p,g,j), γ::Cell(p,q,h,k)) - α ⋅ id(n) == α ⊣ (A::Ob, B::Ob, C::Ob, D::Ob, m::(A↛B), n::(C↛D), - f::(A→C), g::(B→D), α::Cell(m,n,f,g)) - id(m) ⋅ α == α ⊣ (A::Ob, B::Ob, C::Ob, D::Ob, m::(A↛B), n::(C↛D), - f::(A→C), g::(B→D), α::Cell(m,n,f,g)) + α::Cell(m,n,f,i), β::Cell(n,p,g,j), γ::Cell(p,q,h,k)] + α ⋅ id(n) == α ⊣ [A::Ob, B::Ob, C::Ob, D::Ob, m::(A↛B), n::(C↛D), + f::(A→C), g::(B→D), α::Cell(m,n,f,g)] + id(m) ⋅ α == α ⊣ [A::Ob, B::Ob, C::Ob, D::Ob, m::(A↛B), n::(C↛D), + f::(A→C), g::(B→D), α::Cell(m,n,f,g)] # External category operations. - pcompose(m::(A ↛ B), n::(B ↛ C))::(A ↛ C) ⊣ (A::Ob, B::Ob, C::Ob) - pid(A::Ob)::(A ↛ A) ⊣ (A::Ob) + pcompose(m::(A ↛ B), n::(B ↛ C))::(A ↛ C) ⊣ [A::Ob, B::Ob, C::Ob] + pid(A)::(A ↛ A) ⊣ [A::Ob] pcompose(α::Cell(m, p, f, g), β::Cell(n, q, g, h))::Cell(m*n, p*q, f, h) ⊣ - (A::Ob, B::Ob, C::Ob, X::Ob, Y::Ob, Z::Ob, + [A::Ob, B::Ob, C::Ob, X::Ob, Y::Ob, Z::Ob, m::(A ↛ B), n::(B ↛ C), p::(X ↛ Y), q::(Y ↛ Z), - f::(A → X), g::(B → Y), h::(C → Z)) - pid(f::(A → B))::Cell(pid(A), pid(B), f, f) ⊣ (A::Ob, B::Ob) + f::(A → X), g::(B → Y), h::(C → Z)] + pid(f::(A → B))::Cell(pid(A), pid(B), f, f) ⊣ [A::Ob, B::Ob] # External category axioms. (m * n) * p == m * (n * p) ⊣ - (A::Ob, B::Ob, C::Ob, D::Ob, m::(A ↛ B), n::(B ↛ C), p::(C ↛ D)) - m * pid(B) == m ⊣ (A::Ob, B::Ob, m::(A ↛ B)) - pid(A) * m == m ⊣ (A::Ob, B::Ob, m::(A ↛ B)) + [A::Ob, B::Ob, C::Ob, D::Ob, m::(A ↛ B), n::(B ↛ C), p::(C ↛ D)] + m * pid(B) == m ⊣ [A::Ob, B::Ob, m::(A ↛ B)] + pid(A) * m == m ⊣ [A::Ob, B::Ob, m::(A ↛ B)] (α * β) * γ == α * (β * γ) ⊣ - (A::Ob, B::Ob, C::Ob, D::Ob, W::Ob, X::Ob, Y::Ob, Z::Ob, + [A::Ob, B::Ob, C::Ob, D::Ob, W::Ob, X::Ob, Y::Ob, Z::Ob, m::(A↛B), n::(B↛C), p::(C↛D), u::(W↛X), v::(X↛Y), w::(Y↛Z), f::(A→W), g::(B→X), h::(C→Y), k::(D→Z), - α::Cell(m,u,f,g), β::Cell(n,v,g,h), γ::Cell(p,w,h,k)) - α * pid(g) == α ⊣ (A::Ob, B::Ob, C::Ob, D::Ob, m::(A↛B), n::(C↛D), - f::(A→C), g::(B→D), α::Cell(m,n,f,g)) - pid(f) * α == α ⊣ (A::Ob, B::Ob, C::Ob, D::Ob, m::(A↛B), n::(C↛D), - f::(A→C), g::(B→D), α::Cell(m,n,f,g)) + α::Cell(m,u,f,g), β::Cell(n,v,g,h), γ::Cell(p,w,h,k)] + α * pid(g) == α ⊣ [A::Ob, B::Ob, C::Ob, D::Ob, m::(A↛B), n::(C↛D), + f::(A→C), g::(B→D), α::Cell(m,n,f,g)] + pid(f) * α == α ⊣ [A::Ob, B::Ob, C::Ob, D::Ob, m::(A↛B), n::(C↛D), + f::(A→C), g::(B→D), α::Cell(m,n,f,g)] # TODO: Interchange laws. end @@ -156,7 +155,7 @@ pcompose(α, β, γ, αs...) = pcompose([α, β, γ, αs...]) Checks domains of morphisms but not 2-morphisms. """ -@syntax FreeDoubleCategory{ObExpr,HomExpr,ProExpr,CellExpr} ThDoubleCategory begin +@symbolic_model FreeDoubleCategory{ObExpr,HomExpr,ProExpr,CellExpr} ThDoubleCategory begin compose(f::Hom, g::Hom) = associate_unit(new(f,g; strict=true), id) compose(α::Cell, β::Cell) = associate_unit(new(α,β), id) pcompose(m::Pro, n::Pro) = associate_unit(new(m,n; strict=true), pid) @@ -184,33 +183,33 @@ Grandis-Pare 1999. In the case where the double category is actually a 2-category, tabulators specialize to cotensors, a more familiar 2-categorical limit. """ -@theory ThDoubleCategoryWithTabulators{Ob,Hom,Pro,Cell,Tab} <: ThDoubleCategory{Ob,Hom,Pro,Cell} begin - Tab(proarrow::(A ↛ B))::TYPE +@theory ThDoubleCategoryWithTabulators <: ThDoubleCategory begin + Tab(proarrow::(A ↛ B))::TYPE ⊣ [A::Ob, B::Ob] # data: a cell with domain p and two specified projections - tabulator(p::(A↛B))::Tab(p) ⊣ (A::Ob,B::Ob) - ob(τ::Tab(p))::Ob ⊣ (A::Ob,B::Ob,p::(A↛B)) - proj1(τ::Tab(p))::(ob(τ) → A) ⊣ (A::Ob, B::Ob, p::(A↛B)) - proj2(τ::Tab(p))::(ob(τ) → B) ⊣ (A::Ob, B::Ob, p::(A↛B)) + tabulator(p::(A↛B))::Tab(p) ⊣ [A::Ob,B::Ob] + ob(τ::Tab(p))::Ob ⊣ [A::Ob,B::Ob,p::(A↛B)] + proj1(τ::Tab(p))::(ob(τ) → A) ⊣ [A::Ob, B::Ob, p::(A↛B)] + proj2(τ::Tab(p))::(ob(τ) → B) ⊣ [A::Ob, B::Ob, p::(A↛B)] cell(τ::Tab(p))::Cell(pid(ob(τ)), p, proj1(τ), proj2(τ)) ⊣ - (A::Ob, B::Ob, p::(A↛B)) + [A::Ob, B::Ob, p::(A↛B)] # factorization existence universal(τ::Tab(p), θ::Cell(pid(X),p,f,g))::(X→ob(τ)) ⊣ - (A::Ob, B::Ob, X::Ob, p::(A↛B), f::(X→A), g::(X→B)) + [A::Ob, B::Ob, X::Ob, p::(A↛B), f::(X→A), g::(X→B)] universal(τ,θ) ⋅ proj1(τ) == f ⊣ - (A::Ob, B::Ob, X::Ob, p::(A↛B), f::(X→A), g::(X→B), - τ::Tab(p), θ::Cell(pid(X),p,f,g)) + [A::Ob, B::Ob, X::Ob, p::(A↛B), f::(X→A), g::(X→B), + τ::Tab(p), θ::Cell(pid(X),p,f,g)] universal(τ,θ) ⋅ proj2(τ) == g ⊣ - (A::Ob, B::Ob, X::Ob, p::(A↛B), f::(X→A), g::(X→B), - τ::Tab(p), θ::Cell(pid(X),p,f,g)) + [A::Ob, B::Ob, X::Ob, p::(A↛B), f::(X→A), g::(X→B), + τ::Tab(p), θ::Cell(pid(X),p,f,g)] pid(universal(τ,θ)) ⋅ cell(τ) == θ ⊣ - (A::Ob, B::Ob, X::Ob, p::(A↛B), f::(X→A), g::(X→B), - τ::Tab(p), θ::Cell(pid(X),p,f,g)) + [A::Ob, B::Ob, X::Ob, p::(A↛B), f::(X→A), g::(X→B), + τ::Tab(p), θ::Cell(pid(X),p,f,g)] # uniqueness of factorization - universal(τ, pid(h) ⋅ cell(τ)) == h ⊣ (A::Ob,B::Ob,X::Ob,p::(A↛B), - τ::Tab(p),h::(X→ob(τ))) + universal(τ, pid(h) ⋅ cell(τ)) == h ⊣ [A::Ob,B::Ob,X::Ob,p::(A↛B), + τ::Tab(p),h::(X→ob(τ))] end # Equipment @@ -226,21 +225,21 @@ References: - Shulman, 2008: Framed bicategories and monoidal fibrations - Cruttwell & Shulman, 2010: A unified framework for generalized multicategories """ -@theory ThEquipment{Ob,Hom,Pro,Cell} <: ThDoubleCategory{Ob,Hom,Pro,Cell} begin - companion(f::(A → B))::(A ↛ B) ⊣ (A::Ob, B::Ob) - conjoint(f::(A → B))::(B ↛ A) ⊣ (A::Ob, B::Ob) +@theory ThEquipment <: ThDoubleCategory begin + companion(f::(A → B))::(A ↛ B) ⊣ [A::Ob, B::Ob] + conjoint(f::(A → B))::(B ↛ A) ⊣ [A::Ob, B::Ob] # Binding cells (unit and counit) for companions and conjoints. - companion_unit(f::(A → B))::Cell(pid(A), companion(f), id(A), f) ⊣ (A::Ob, B::Ob) - companion_counit(f::(A → B))::Cell(companion(f), pid(B), f, id(B)) ⊣ (A::Ob, B::Ob) - conjoint_unit(f::(A → B))::Cell(pid(A), conjoint(f), f, id(A)) ⊣ (A::Ob, B::Ob) - conjoint_counit(f::(A → B))::Cell(conjoint(f), pid(B), id(B), f) ⊣ (A::Ob, B::Ob) + companion_unit(f::(A → B))::Cell(pid(A), companion(f), id(A), f) ⊣ [A::Ob, B::Ob] + companion_counit(f::(A → B))::Cell(companion(f), pid(B), f, id(B)) ⊣ [A::Ob, B::Ob] + conjoint_unit(f::(A → B))::Cell(pid(A), conjoint(f), f, id(A)) ⊣ [A::Ob, B::Ob] + conjoint_counit(f::(A → B))::Cell(conjoint(f), pid(B), id(B), f) ⊣ [A::Ob, B::Ob] # Triangle-style identities for binding cells. - companion_unit(f) ⋅ companion_counit(f) == pid(f) ⊣ (A::Ob, B::Ob, f::(A → B)) - companion_unit(f) * companion_counit(f) == id(companion(f)) ⊣ (A::Ob, B::Ob, f::(A → B)) - conjoint_unit(f) ⋅ conjoint_counit(f) == pid(f) ⊣ (A::Ob, B::Ob, f::(A → B)) - conjoint_counit(f) * conjoint_unit(f) == id(conjoint(f)) ⊣ (A::Ob, B::Ob, f::(A → B)) + companion_unit(f) ⋅ companion_counit(f) == pid(f) ⊣ [A::Ob, B::Ob, f::(A → B)] + companion_unit(f) * companion_counit(f) == id(companion(f)) ⊣ [A::Ob, B::Ob, f::(A → B)] + conjoint_unit(f) ⋅ conjoint_counit(f) == pid(f) ⊣ [A::Ob, B::Ob, f::(A → B)] + conjoint_counit(f) * conjoint_unit(f) == id(conjoint(f)) ⊣ [A::Ob, B::Ob, f::(A → B)] end # Monoidal double category @@ -270,66 +269,66 @@ References: FIXME: Should also inherit `ThMonoidalCategory{Ob,Hom}` but multiple inheritance is not supported. """ -@theory ThMonoidalDoubleCategory{Ob,Hom,Pro,Cell} <: ThDoubleCategory{Ob,Hom,Pro,Cell} begin +@theory ThMonoidalDoubleCategory <: ThDoubleCategory begin @op (⊗) := otimes # Monoidal operations on D₀. otimes(A::Ob, B::Ob)::Ob otimes(f::(A → B), g::(C → D))::((A ⊗ C) → (B ⊗ D)) ⊣ - (A::Ob, B::Ob, C::Ob, D::Ob) + [A::Ob, B::Ob, C::Ob, D::Ob] munit()::Ob # Monoid axioms for (D₀,⊗₀,I₀). - (A ⊗ B) ⊗ C == A ⊗ (B ⊗ C) ⊣ (A::Ob, B::Ob, C::Ob) - A ⊗ munit() == A ⊣ (A::Ob) - munit() ⊗ A == A ⊣ (A::Ob) - (f ⊗ g) ⊗ h == f ⊗ (g ⊗ h) ⊣ (A::Ob, B::Ob, C::Ob, X::Ob, Y::Ob, Z::Ob, - f::(A → X), g::(B → Y), h::(C → Z)) + (A ⊗ B) ⊗ C == A ⊗ (B ⊗ C) ⊣ [A::Ob, B::Ob, C::Ob] + A ⊗ munit() == A ⊣ [A::Ob] + munit() ⊗ A == A ⊣ [A::Ob] + (f ⊗ g) ⊗ h == f ⊗ (g ⊗ h) ⊣ [A::Ob, B::Ob, C::Ob, X::Ob, Y::Ob, Z::Ob, + f::(A → X), g::(B → Y), h::(C → Z)] # Functorality axioms for (D₀,⊗₀,I₀). ((f ⊗ g) ⋅ (h ⊗ k) == (f ⋅ h) ⊗ (g ⋅ k) - ⊣ (A::Ob, B::Ob, C::Ob, X::Ob, Y::Ob, Z::Ob, - f::(A → B), h::(B → C), g::(X → Y), k::(Y → Z))) - id(A ⊗ B) == id(A) ⊗ id(B) ⊣ (A::Ob, B::Ob) + ⊣ [A::Ob, B::Ob, C::Ob, X::Ob, Y::Ob, Z::Ob, + f::(A → B), h::(B → C), g::(X → Y), k::(Y → Z)]) + id(A ⊗ B) == id(A) ⊗ id(B) ⊣ [A::Ob, B::Ob] # Monoidal operations on D₁ + src/tgt are strict monoidal functors. otimes(m::(A ↛ B), n::(C ↛ D))::((A ⊗ C) ↛ (B ⊗ D)) ⊣ - (A::Ob, B::Ob, C::Ob, D::Ob) + [A::Ob, B::Ob, C::Ob, D::Ob] otimes(α::Cell(m,n,f,g), β::Cell(m′,n′,f′,g′))::Cell(m⊗m′,n⊗n′,f⊗f′,g⊗g′) ⊣ - (A::Ob, B::Ob, C::Ob, D::Ob, A′::Ob, B′::Ob, C′::Ob, D′::Ob, + [A::Ob, B::Ob, C::Ob, D::Ob, A′::Ob, B′::Ob, C′::Ob, D′::Ob, f::(A → C), g::(B → D), f′::(A′ → C′), g′::(B′ → D′), - m::(A ↛ B), n::(C ↛ D), m′::(A′ ↛ B′), n′::(C′ ↛ D′)) + m::(A ↛ B), n::(C ↛ D), m′::(A′ ↛ B′), n′::(C′ ↛ D′)] # Monoid axioms for (D₁,⊗₁,I₁). - (m ⊗ n) ⊗ p == m ⊗ (n ⊗ p) ⊣ (A::Ob, B::Ob, C::Ob, X::Ob, Y::Ob, Z::Ob, - m::(A ↛ X), n::(B ↛ Y), p::(C ↛ Z)) - m ⊗ pid(munit()) == m ⊣ (A::Ob, B::Ob, m::(A ↛ B)) - pid(munit()) ⊗ m == m ⊣ (A::Ob, B::Ob, m::(A ↛ B)) + (m ⊗ n) ⊗ p == m ⊗ (n ⊗ p) ⊣ [A::Ob, B::Ob, C::Ob, X::Ob, Y::Ob, Z::Ob, + m::(A ↛ X), n::(B ↛ Y), p::(C ↛ Z)] + m ⊗ pid(munit()) == m ⊣ [A::Ob, B::Ob, m::(A ↛ B)] + pid(munit()) ⊗ m == m ⊣ [A::Ob, B::Ob, m::(A ↛ B)] (α ⊗ β) ⊗ γ == α ⊗ (β ⊗ γ) ⊣ - (A₁::Ob, A₂::Ob, A₃::Ob, B₁::Ob, B₂::Ob, B₃::Ob, + [A₁::Ob, A₂::Ob, A₃::Ob, B₁::Ob, B₂::Ob, B₃::Ob, C₁::Ob, C₂::Ob, C₃::Ob, D₁::Ob, D₂::Ob, D₃::Ob, f₁::(A₁→C₁), f₂::(A₂→C₂), f₃::(A₃→C₃), g₁::(B₁→D₁), g₂::(B₂→D₂), g₃::(B₃→D₃), m₁::(A₁↛B₁), m₂::(A₂↛B₂), m₃::(A₃↛B₃), n₁::(C₁↛D₁), n₂::(C₂↛D₂), n₃::(C₃↛D₃), - α::Cell(m₁,n₁,f₁,g₁), β::Cell(m₂,n₂,f₂,g₂), γ::Cell(m₃,m₃,f₃,g₃)) + α::Cell(m₁,n₁,f₁,g₁), β::Cell(m₂,n₂,f₂,g₂), γ::Cell(m₃,m₃,f₃,g₃)] # Functorality axioms for (D₁,⊗₁,I₁). ((α ⊗ α′) ⋅ (β ⊗ β′)) == (α ⋅ β) ⊗ (α′ ⋅ β′) ⊣ - (A::Ob, B::Ob, C::Ob, A′::Ob, B′::Ob, C′::Ob, + [A::Ob, B::Ob, C::Ob, A′::Ob, B′::Ob, C′::Ob, X::Ob, Y::Ob, Z::Ob, X′::Ob, Y′::Ob, Z′::Ob, f::(A→B), g::(X→Y), f′::(A′→B′), g′::(X′→Y′), h::(B→C), k::(Y→Z), h′::(B′→C′), k′::(Y′→Z′), m::(A↛X), n::(B↛Y), p::(C↛Z), m′::(A′↛X′), n′::(B′↛Y′), p′::(C′↛Z′), α::Cell(m,n,f,g), α′::Cell(m′,n′,f′,g′), - β::Cell(n,p,h,k), β′::Cell(n′,p′,h′,k′)) + β::Cell(n,p,h,k), β′::Cell(n′,p′,h′,k′)] id(m ⊗ n) == id(m) ⊗ id(n) ⊣ - (A::Ob, B::Ob, X::Ob, Y::Ob, m::(A ↛ X), n::(B ↛ Y)) + [A::Ob, B::Ob, X::Ob, Y::Ob, m::(A ↛ X), n::(B ↛ Y)] # External functorality of ⊗: D×D → D and I: 1 → D. # TODO: Interchange of external composition of cells. ((m ⊗ n) * (p ⊗ q) == (m * p) ⊗ (n * q) - ⊣ (A::Ob, B::Ob, C::Ob, X::Ob, Y::Ob, Z::Ob, - m::(A ↛ B), p::(B ↛ C), n::(X ↛ Y), q::(Y ↛ Z))) - pid(A ⊗ B) == pid(A) ⊗ pid(B) ⊣ (A::Ob, B::Ob) + ⊣ [A::Ob, B::Ob, C::Ob, X::Ob, Y::Ob, Z::Ob, + m::(A ↛ B), p::(B ↛ C), n::(X ↛ Y), q::(Y ↛ Z)]) + pid(A ⊗ B) == pid(A) ⊗ pid(B) ⊣ [A::Ob, B::Ob] end """ Theory of *symmetric monoidal double categories* @@ -342,42 +341,42 @@ everything (except the braiding) is strict. See FIXME: Should also inherit `ThSymmetricMonoidalCategory{Ob,Hom}` but multiple inheritance is not supported. """ -@theory ThSymmetricMonoidalDoubleCategory{Ob,Hom,Pro,Cell} <: ThMonoidalDoubleCategory{Ob,Hom,Pro,Cell} begin +@theory ThSymmetricMonoidalDoubleCategory <: ThMonoidalDoubleCategory begin + @op (σ) := braid braid(A::Ob, B::Ob)::((A ⊗ B) → (B ⊗ A)) braid(m::(A ↛ C), n::(B ↛ D))::Cell(m ⊗ n, n ⊗ m, σ(A,B), σ(C,D)) ⊣ - (A::Ob, B::Ob, C::Ob, D::Ob) - @op (σ) := braid + [A::Ob, B::Ob, C::Ob, D::Ob] # Involutivity axioms. - σ(A,B) ⋅ σ(B,A) == id(A ⊗ B) ⊣ (A::Ob, B::Ob) - σ(m,n) ⋅ σ(n,m) == id(m ⊗ n) ⊣ (A::Ob, B::Ob, C::Ob, D::Ob, - m::(A ↛ C), n::(B ↛ D)) + σ(A,B) ⋅ σ(B,A) == id(A ⊗ B) ⊣ [A::Ob, B::Ob] + σ(m,n) ⋅ σ(n,m) == id(m ⊗ n) ⊣ [A::Ob, B::Ob, C::Ob, D::Ob, + m::(A ↛ C), n::(B ↛ D)] # Naturality axioms. - (f⊗g) ⋅ σ(C,D) == σ(A,B) ⋅ (g⊗f) ⊣ (A::Ob, B::Ob, C::Ob, D::Ob, - f::(A → C), g::(B → D)) + (f⊗g) ⋅ σ(C,D) == σ(A,B) ⋅ (g⊗f) ⊣ [A::Ob, B::Ob, C::Ob, D::Ob, + f::(A → C), g::(B → D)] ((α⊗β) ⋅ σ(m′,n′) == σ(m,n) ⋅ (β⊗α) ⊣ - (A::Ob, B::Ob, C::Ob, D::Ob, A′::Ob, B′::Ob, C′::Ob, D′::Ob, + [A::Ob, B::Ob, C::Ob, D::Ob, A′::Ob, B′::Ob, C′::Ob, D′::Ob, f::(A → C), g::(B → D), f′::(A′ → C′), g′::(B′ → D′), m::(A ↛ B), n::(C ↛ D), m′::(A′ ↛ B′), n′::(C′ ↛ D′), - α::Cell(m,n,f,g), β::Cell(m′,n′,f′,g′))) + α::Cell(m,n,f,g), β::Cell(m′,n′,f′,g′)]) # Coherence axioms. - σ(A,B⊗C) == (σ(A,B) ⊗ id(C)) ⋅ (id(B) ⊗ σ(A,C)) ⊣ (A::Ob, B::Ob, C::Ob) - σ(A⊗B,C) == (id(A) ⊗ σ(B,C)) ⋅ (σ(A,C) ⊗ id(B)) ⊣ (A::Ob, B::Ob, C::Ob) + σ(A,B⊗C) == (σ(A,B) ⊗ id(C)) ⋅ (id(B) ⊗ σ(A,C)) ⊣ [A::Ob, B::Ob, C::Ob] + σ(A⊗B,C) == (id(A) ⊗ σ(B,C)) ⋅ (σ(A,C) ⊗ id(B)) ⊣ [A::Ob, B::Ob, C::Ob] σ(m,n⊗p) == (σ(m,n) ⊗ id(p)) ⋅ (id(n) ⊗ σ(m,p)) ⊣ - (A::Ob, B::Ob, C::Ob, X::Ob, Y::Ob, Z::Ob, m::(A↛X), n::(B↛Y), p::(C↛Z)) + [A::Ob, B::Ob, C::Ob, X::Ob, Y::Ob, Z::Ob, m::(A↛X), n::(B↛Y), p::(C↛Z)] σ(m⊗n,p) == (id(m) ⊗ σ(n,p)) ⋅ (σ(m,p) ⊗ id(n)) ⊣ - (A::Ob, B::Ob, C::Ob, X::Ob, Y::Ob, Z::Ob, m::(A↛X), n::(B↛Y), p::(C↛Z)) + [A::Ob, B::Ob, C::Ob, X::Ob, Y::Ob, Z::Ob, m::(A↛X), n::(B↛Y), p::(C↛Z)] # Interchange of braiding with external composition. σ(m*n, m′*n′) == σ(m,m′) * σ(n,n′) ⊣ - (A::Ob, B::Ob, C::Ob, A′::Ob, B′::Ob, C′::Ob, - m::(A↛B), n::(B↛C), m′::(A′↛B′), n′::(B′↛C′)) - σ(pid(A), pid(B)) == pid(σ(A,B)) ⊣ (A::Ob, B::Ob) + [A::Ob, B::Ob, C::Ob, A′::Ob, B′::Ob, C′::Ob, + m::(A↛B), n::(B↛C), m′::(A′↛B′), n′::(B′↛C′)] + σ(pid(A), pid(B)) == pid(σ(A,B)) ⊣ [A::Ob, B::Ob] end -@syntax FreeSymmetricMonoidalDoubleCategory{ObExpr,HomExpr,ProExpr,CellExpr} ThSymmetricMonoidalDoubleCategory begin +@symbolic_model FreeSymmetricMonoidalDoubleCategory{ObExpr, HomExpr, ProExpr, CellExpr} ThSymmetricMonoidalDoubleCategory begin otimes(A::Ob, B::Ob) = associate_unit(new(A,B), munit) otimes(f::Hom, g::Hom) = associate(new(f,g)) otimes(f::Pro, g::Pro) = associate(new(f,g)) @@ -404,45 +403,45 @@ in a compatible way. Reference: Aleiferi 2018, PhD thesis. """ -@theory ThCartesianDoubleCategory{Ob,Hom,Pro,Cell} <: ThSymmetricMonoidalDoubleCategory{Ob,Hom,Pro,Cell} begin +@theory ThCartesianDoubleCategory <: ThSymmetricMonoidalDoubleCategory begin # Pairing and projection in D₀. - pair(f::(A → B), g::(A → C))::(A → (B ⊗ C)) ⊣ (A::Ob, B::Ob, C::Ob) + pair(f::(A → B), g::(A → C))::(A → (B ⊗ C)) ⊣ [A::Ob, B::Ob, C::Ob] proj1(A::Ob, B::Ob)::((A ⊗ B) → A) proj2(A::Ob, B::Ob)::((A ⊗ B) → B) - pair(f,g) ⋅ proj1(B,C) == f ⊣ (A::Ob, B::Ob, C::Ob, f::(A → B), g::(A → C)) - pair(f,g) ⋅ proj2(B,C) == g ⊣ (A::Ob, B::Ob, C::Ob, f::(A → B), g::(A → C)) + pair(f,g) ⋅ proj1(B,C) == f ⊣ [A::Ob, B::Ob, C::Ob, f::(A → B), g::(A → C)] + pair(f,g) ⋅ proj2(B,C) == g ⊣ [A::Ob, B::Ob, C::Ob, f::(A → B), g::(A → C)] pair(h ⋅ proj1(B,C), h ⋅ proj2(B,C)) == h ⊣ - (A::Ob, B::Ob, C::Ob, h::(A → (B ⊗ C))) + [A::Ob, B::Ob, C::Ob, h::(A → (B ⊗ C))] # Pairing and projection in D₁. pair(α::Cell(m,p,f,h), β::Cell(m,q,g,k))::Cell(m,p⊗q,pair(f,g),pair(h,k)) ⊣ - (A::Ob, B::Ob, W::Ob, X::Ob, Y::Ob, Z::Ob, + [A::Ob, B::Ob, W::Ob, X::Ob, Y::Ob, Z::Ob, f::(A → W), g::(A → Y), h::(B → X), k::(B → Z), - m::(A ↛ B), p::(W ↛ X), q::(Y ↛ Z)) + m::(A ↛ B), p::(W ↛ X), q::(Y ↛ Z)] proj1(m::(A ↛ B), n::(C ↛ D))::Cell(m⊗n, m, proj1(A,C), proj1(B,D)) ⊣ - (A::Ob, B::Ob, C::Ob, D::Ob) + [A::Ob, B::Ob, C::Ob, D::Ob] proj2(m::(A ↛ B), n::(C ↛ D))::Cell(m⊗n, n, proj2(A,C), proj2(B,D)) ⊣ - (A::Ob, B::Ob, C::Ob, D::Ob) + [A::Ob, B::Ob, C::Ob, D::Ob] # TODO: Pairing/projection axioms for D₁. # Interchange of pairing with external composition. pair(α * γ, β * δ) == pair(α,β) * pair(γ,δ) ⊣ - (A::Ob, B::Ob, C::Ob, P::Ob, Q::Ob, W::Ob, X::Ob, Y::Ob, Z::Ob, + [A::Ob, B::Ob, C::Ob, P::Ob, Q::Ob, W::Ob, X::Ob, Y::Ob, Z::Ob, f::(A → W), g::(A → Y), h::(B → X), k::(B → Z), i::(C → P), j::(C → Q), m::(A ↛ B), n::(B ↛ C), p::(W ↛ X), q::(Y ↛ Z), u::(X ↛ P), v::(Z ↛ Q), - α::Cell(m,p,f,h), β::Cell(m,q,g,k), γ::Cell(n,u,h,i), δ::Cell(n,v,k,j)) + α::Cell(m,p,f,h), β::Cell(m,q,g,k), γ::Cell(n,u,h,i), δ::Cell(n,v,k,j)] pair(pid(f), pid(g)) == pid(pair(f,g)) ⊣ - (A::Ob, B::Ob, C::Ob, f::(A → B), g::(B → C)) + [A::Ob, B::Ob, C::Ob, f::(A → B), g::(B → C)] # Interchange of projection with external composition. proj1(m * n, p * q) == proj1(m, p) * proj2(n, q) ⊣ - (A::Ob, B::Ob, C::Ob, X::Ob, Y::Ob, Z::Ob, - m::(A ↛ B), n::(B ↛ C), p::(X ↛ Y), q::(Y ↛ Z)) + [A::Ob, B::Ob, C::Ob, X::Ob, Y::Ob, Z::Ob, + m::(A ↛ B), n::(B ↛ C), p::(X ↛ Y), q::(Y ↛ Z)] proj2(m * n, p * q) == proj2(m, p) * proj2(n, q) ⊣ - (A::Ob, B::Ob, C::Ob, X::Ob, Y::Ob, Z::Ob, - m::(A ↛ B), n::(B ↛ C), p::(X ↛ Y), q::(Y ↛ Z)) - proj1(pid(A), pid(B)) == pid(proj1(A, B)) ⊣ (A::Ob, B::Ob) - proj2(pid(A), pid(B)) == pid(proj2(A, B)) ⊣ (A::Ob, B::Ob) + [A::Ob, B::Ob, C::Ob, X::Ob, Y::Ob, Z::Ob, + m::(A ↛ B), n::(B ↛ C), p::(X ↛ Y), q::(Y ↛ Z)] + proj1(pid(A), pid(B)) == pid(proj1(A, B)) ⊣ [A::Ob, B::Ob] + proj2(pid(A), pid(B)) == pid(proj2(A, B)) ⊣ [A::Ob, B::Ob] end diff --git a/src/theories/IndexedCategory.jl b/src/theories/IndexedCategory.jl index e6f8b0344..e4cd56ba4 100644 --- a/src/theories/IndexedCategory.jl +++ b/src/theories/IndexedCategory.jl @@ -3,8 +3,6 @@ export ThDisplayedCategory, Fib, FibHom, ob, hom, ThOpindexedMonoidalCategory, ThOpindexedMonoidalCategoryLax, ThCoindexedMonoidalCategory -import Base: * - # Displayed category #################### @@ -20,26 +18,26 @@ References: - [nLab: displayed category](https://ncatlab.org/nlab/show/displayed+category) - Ahrens & Lumsdaine, 2019: Displayed categories, Definition 3.1 """ -@theory ThDisplayedCategory{Ob,Hom,Fib,FibHom} <: ThCategory{Ob,Hom} begin - """ Fiber over an object. """ +@theory ThDisplayedCategory <: ThCategory begin + # """ Fiber over an object. """ Fib(ob::Ob)::TYPE - """ Fiber over a morphism, with given fibers over the domain and codomain. """ - FibHom(hom::Hom(A,B), dom::Fib(A), codom::Fib(B))::TYPE ⊣ (A::Ob, B::Ob) + # """ Fiber over a morphism, with given fibers over the domain and codomain. """ + FibHom(hom::Hom(A,B), dom::Fib(A), codom::Fib(B))::TYPE ⊣ [A::Ob, B::Ob] - id(x::Fib(A))::FibHom(id(A),x,x) ⊣ (A::Ob) + id(x::Fib(A))::FibHom(id(A),x,x) ⊣ [A::Ob] compose(f̄::FibHom(f,x,y), ḡ::FibHom(g,y,z))::FibHom(compose(f,g),x,z) ⊣ - (A::Ob, B::Ob, C::Ob, f::(A → B), g::(B → C)) + [A::Ob, B::Ob, C::Ob, x::Fib(A), y::Fib(B), z::Fib(C), f::(A → B), g::(B → C)] # Displayed category axioms: category axioms over the base category. ((f̄ ⋅ ḡ) ⋅ h̄ == f̄ ⋅ (ḡ ⋅ h̄) ⊣ - (A::Ob, B::Ob, C::Ob, D::Ob, f::(A → B), g::(B → C), h::(C → D), + [A::Ob, B::Ob, C::Ob, D::Ob, f::(A → B), g::(B → C), h::(C → D), w::Fib(A), x::Fib(B), y::Fib(C), z::Fib(D), - f̄::FibHom(f,w,x), ḡ::FibHom(g,x,y), h̄::FibHom(h,y,z))) - f̄ ⋅ id(y) == f̄ ⊣ (A::Ob, B::Ob, f::(A → B), - x::Fib(A), y::Fib(B), f̄::FibHom(f,x,y)) - id(x) ⋅ f̄ == f̄ ⊣ (A::Ob, B::Ob, f::(A → B), - x::Fib(A), y::Fib(B), f̄::FibHom(f,x,y)) + f̄::FibHom(f,w,x), ḡ::FibHom(g,x,y), h̄::FibHom(h,y,z)]) + f̄ ⋅ id(y) == f̄ ⊣ [A::Ob, B::Ob, f::(A → B), + x::Fib(A), y::Fib(B), f̄::FibHom(f,x,y)] + id(x) ⋅ f̄ == f̄ ⊣ [A::Ob, B::Ob, f::(A → B), + x::Fib(A), y::Fib(B), f̄::FibHom(f,x,y)] end # Opindexed category @@ -57,72 +55,73 @@ opindexed category as a generalized algebraic theory. The symbol `*` is used for the actions since a common mathematical notation for the "pushforward functor" induced by an indexing morphism ``f: A → B`` is ``f_*: F(A) \to F(B)``. """ -@theory ThOpindexedCategory{Ob,Hom,Fib,FibHom} <: ThCategory{Ob,Hom} begin +@theory ThOpindexedCategory <: ThCategory begin + @op begin - (⇢) := FibHom # XXX: Type inference not good enough to use `→` here also. + (⇢) := FibHom (*) := act end Fib(ob::Ob)::TYPE - FibHom(dom::Fib(A), codom::Fib(A))::TYPE ⊣ (A::Ob) + FibHom(dom::Fib(A), codom::Fib(A))::TYPE ⊣ [A::Ob] # Category operations for each fiber. - id(X::Fib(A))::FibHom(X,X) ⊣ (A::Ob) + id(X::Fib(A))::FibHom(X,X) ⊣ [A::Ob] compose(u::FibHom(X,Y), v::FibHom(Y,Z))::FibHom(X,Z) ⊣ - (A::Ob, X::Fib(A), Y::Fib(A), Z::Fib(A)) + [A::Ob, X::Fib(A), Y::Fib(A), Z::Fib(A)] # Transitions between fibers. - act(X::Fib(A), f::Hom(A,B))::Fib(B) ⊣ (A::Ob, B::Ob) + act(X::Fib(A), f::Hom(A,B))::Fib(B) ⊣ [A::Ob, B::Ob] act(u::FibHom(X,Y), f::Hom(A,B))::FibHom(act(X,f), act(Y,f)) ⊣ - (A::Ob, B::Ob, X::Fib(A), Y::Fib(A)) + [A::Ob, B::Ob, X::Fib(A), Y::Fib(A)] # Category axioms for each fiber. ((u ⋅ v) ⋅ w == u ⋅ (v ⋅ w) - ⊣ (A::Ob, W::Fib(A), X::Fib(A), Y::Fib(A), Z::Fib(A), - u::(W ⇢ X), v::(X ⇢ Y), w::(Y ⇢ Z))) - u ⋅ id(X) == u ⊣ (A::Ob, X::Fib(A), Y::Fib(A), u::(X ⇢ Y)) - id(X) ⋅ u == u ⊣ (A::Ob, X::Fib(A), Y::Fib(A), v::(X ⇢ Y)) + ⊣ [A::Ob, W::Fib(A), X::Fib(A), Y::Fib(A), Z::Fib(A), + u::(W ⇢ X), v::(X ⇢ Y), w::(Y ⇢ Z)]) + u ⋅ id(X) == u ⊣ [A::Ob, X::Fib(A), Y::Fib(A), u::(X ⇢ Y)] + id(X) ⋅ u == u ⊣ [A::Ob, X::Fib(A), Y::Fib(A), u::(X ⇢ Y)] # Functorality of transitions. - (u⋅v)*f == (u*f)⋅(v*f) ⊣ (A::Ob, B::Ob, X::Fib(A), Y::Fib(A), Z::Fib(A), - f::(A → B), u::(X ⇢ Y), v::(Y ⇢ Z)) - (id(X))*f == id(X*f) ⊣ (A::Ob, B::Ob, X::Fib(A), f::(A → B)) - - X*(f⋅g) == (X*f)*g ⊣ (A::Ob, B::Ob, C::Ob, X::Fib(A), f::(A → B), g::(A → C)) - u*(f⋅g) == (u*f)*g ⊣ (A::Ob, B::Ob, C::Ob, X::Fib(A), Y::Fib(A), - f::(A → B), g::(B → C), u::(X ⇢ Y)) - X*(id(A)) == X ⊣ (A::Ob, X::Fib(A)) - u*(id(A)) == u ⊣ (A::Ob, X::Fib(A), Y::Fib(A), u::(X ⇢ Y)) + (u⋅v)*f == (u*f)⋅(v*f) ⊣ [A::Ob, B::Ob, X::Fib(A), Y::Fib(A), Z::Fib(A), + f::(A → B), u::(X ⇢ Y), v::(Y ⇢ Z)] + (id(X))*f == id(X*f) ⊣ [A::Ob, B::Ob, X::Fib(A), f::(A → B)] + + X*(f⋅g) == (X*f)*g ⊣ [A::Ob, B::Ob, C::Ob, X::Fib(A), f::(A → B), g::(A → C)] + u*(f⋅g) == (u*f)*g ⊣ [A::Ob, B::Ob, C::Ob, X::Fib(A), Y::Fib(A), + f::(A → B), g::(B → C), u::(X ⇢ Y)] + X*(id(A)) == X ⊣ [A::Ob, X::Fib(A)] + u*(id(A)) == u ⊣ [A::Ob, X::Fib(A), Y::Fib(A), u::(X ⇢ Y)] end # Opindexed monoidal category ############################# # Not a standard or appealing theory, but a building block for those below. -@theory ThOpindexedMonoidalCategoryPre{Ob,Hom,Fib,FibHom} <: ThOpindexedCategory{Ob,Hom,Fib,FibHom} begin +@theory ThOpindexedMonoidalCategoryPre <: ThOpindexedCategory begin @op (⊗) := otimes # Monoid operations in each fiber. - otimes(X::Fib(A), Y::Fib(A))::Fib(A) ⊣ (A::Ob) + otimes(X::Fib(A), Y::Fib(A))::Fib(A) ⊣ [A::Ob] otimes(u::FibHom(X,Y), v::FibHom(W,Z))::FibHom(otimes(X,W), otimes(Y,Z)) ⊣ - (A::Ob, W::Fib(A), X::Fib(A), Y::Fib(A), Z::Fib(A)) + [A::Ob, W::Fib(A), X::Fib(A), Y::Fib(A), Z::Fib(A)] munit(A::Ob)::Fib(A) # Monoid axioms for each fiber. - (X ⊗ Y) ⊗ Z == X ⊗ (Y ⊗ Z) ⊣ (A::Ob, X::Fib(A), Y::Fib(A), Z::Fib(A)) - munit(A) ⊗ X == X ⊣ (A::Ob, X::Fib(A)) - X ⊗ munit(A) == X ⊣ (A::Ob, X::Fib(A)) + (X ⊗ Y) ⊗ Z == X ⊗ (Y ⊗ Z) ⊣ [A::Ob, X::Fib(A), Y::Fib(A), Z::Fib(A)] + munit(A) ⊗ X == X ⊣ [A::Ob, X::Fib(A)] + X ⊗ munit(A) == X ⊣ [A::Ob, X::Fib(A)] ((u ⊗ v) ⊗ w == u ⊗ (v ⊗ w) ⊣ - (A::Ob, U::Fib(A), V::Fib(A), W::Fib(A), X::Fib(A), Y::Fib(A), Z::Fib(A), - u::(U ⇢ X), v::(V ⇢ Y), w::(W ⇢ Z))) - id(munit(A)) ⊗ u == u ⊣ (A::Ob, X::Fib(A), Y::Fib(A), u::(X ⇢ Y)) - u ⊗ id(munit(A)) == u ⊣ (A::Ob, X::Fib(A), Y::Fib(A), u::(X ⇢ Y)) + [A::Ob, U::Fib(A), V::Fib(A), W::Fib(A), X::Fib(A), Y::Fib(A), Z::Fib(A), + u::(U ⇢ X), v::(V ⇢ Y), w::(W ⇢ Z)]) + id(munit(A)) ⊗ u == u ⊣ [A::Ob, X::Fib(A), Y::Fib(A), u::(X ⇢ Y)] + u ⊗ id(munit(A)) == u ⊣ [A::Ob, X::Fib(A), Y::Fib(A), u::(X ⇢ Y)] # Monoid functorality axioms for each fiber. ((t ⊗ u) ⋅ (v ⊗ w) == (t ⋅ v) ⊗ (u ⋅ w) - ⊣ (A::Ob, U::Fib(A), V::Fib(A), W::Fib(A), X::Fib(A), Y::Fib(A), Z::Fib(A), - t::(U ⇢ V), v::(V ⇢ W), u::(X ⇢ Y), w::(Y ⇢ Z))) - id(X ⊗ Y) == id(X) ⊗ id(Y) ⊣ (A::Ob, X::Fib(A), Y::Fib(A)) + ⊣ [A::Ob, U::Fib(A), V::Fib(A), W::Fib(A), X::Fib(A), Y::Fib(A), Z::Fib(A), + t::(U ⇢ V), v::(V ⇢ W), u::(X ⇢ Y), w::(Y ⇢ Z)]) + id(X ⊗ Y) == id(X) ⊗ id(Y) ⊣ [A::Ob, X::Fib(A), Y::Fib(A)] end """ Theory of an opindexed, or covariantly indexed, monoidal category. @@ -138,13 +137,13 @@ References: - Shulman, 2008: Framed bicategories and monoidal fibrations - Shulman, 2013: Enriched indexed categories """ -@theory ThOpindexedMonoidalCategory{Ob,Hom,Fib,FibHom} <: ThOpindexedMonoidalCategoryPre{Ob,Hom,Fib,FibHom} begin - (X ⊗ Y) * f == (X*f) ⊗ (Y*f) ⊣ (A::Ob, B::Ob, X::Fib(A), Y::Fib(A), f::(A → B)) - munit(A) * f == munit(B) ⊣ (A::Ob, B::Ob, f::(A → B)) +@theory ThOpindexedMonoidalCategory <: ThOpindexedMonoidalCategoryPre begin + (X ⊗ Y) * f == (X*f) ⊗ (Y*f) ⊣ [A::Ob, B::Ob, X::Fib(A), Y::Fib(A), f::(A → B)] + munit(A) * f == munit(B) ⊣ [A::Ob, B::Ob, f::(A → B)] (u ⊗ v) * f == (u*f) ⊗ (v*f) ⊣ - (A::Ob, B::Ob, X::Fib(A), Y::Fib(A), Z::Fib(A), W::Fib(A), - f::(A → B), u::(X → Z), v::(Y → W)) + [A::Ob, B::Ob, X::Fib(A), Y::Fib(A), Z::Fib(A), W::Fib(A), + f::(A → B), u::(X ⇢ Z), v::(Y ⇢ W)] end """ Theory of an opindexed monoidal category with lax transition functors. @@ -160,21 +159,21 @@ References: - Moeller & Vasilakopoulou, 2020: Monoidal Grothendieck construction, Remark 3.18 [this paper is about monoidal indexed categories, a different notion!] """ -@theory ThOpindexedMonoidalCategoryLax{Ob,Hom,Fib,FibHom} <: ThOpindexedMonoidalCategoryPre{Ob,Hom,Fib,FibHom} begin +@theory ThOpindexedMonoidalCategoryLax <: ThOpindexedMonoidalCategoryPre begin # Components of the laxator for `f: A → B`. otimes(f::(A → B), X::Fib(A), Y::Fib(A))::FibHom(((X*f) ⊗ (Y*f)), ((X⊗Y) * f)) ⊣ - (A::Ob, B::Ob) - munit(f::(A → B))::FibHom(munit(B), (munit(A)*f)) ⊣ (A::Ob, B::Ob) + [A::Ob, B::Ob] + munit(f::(A → B))::FibHom(munit(B), (munit(A)*f)) ⊣ [A::Ob, B::Ob] # Naturality for laxity cells. ⊗(f,X,Y) ⋅ ((u⊗v) * f) == ((u*f) ⊗ (v*f)) ⋅ ⊗(f,Z,W) ⊣ - (A::Ob, B::Ob, X::Fib(A), Y::Fib(A), Z::Fib(A), W::Fib(A), - f::(A → B), u::(X ⇢ Z), v::(Y ⇢ W)) + [A::Ob, B::Ob, X::Fib(A), Y::Fib(A), Z::Fib(A), W::Fib(A), + f::(A → B), u::(X ⇢ Z), v::(Y ⇢ W)] # Functorality of laxity cells. ⊗(f⋅g,X,Y) == ⊗(g,X*f,Y*f) ⋅ (⊗(f,X,Y)*g) ⊣ - (A::Ob, B::Ob, C::Ob, f::(A → B), g::(B → C), X::Fib(A), Y::Fib(A)) - ⊗(id(A),X,Y) == id(X⊗Y) ⊣ (A::Ob, X::Fib(A), Y::Fib(A)) + [A::Ob, B::Ob, C::Ob, f::(A → B), g::(B → C), X::Fib(A), Y::Fib(A)] + ⊗(id(A),X,Y) == id(X⊗Y) ⊣ [A::Ob, X::Fib(A), Y::Fib(A)] end """ Theory of an opindexed monoidal category with cocartesian indexing category. @@ -189,20 +188,20 @@ References: - Shulman, 2008: Framed bicategories and monoidal fibrations - Shulman, 2013: Enriched indexed categories """ -@signature ThCoindexedMonoidalCategory{Ob,Hom,Fib,FibHom} <: ThOpindexedMonoidalCategory{Ob,Hom,Fib,FibHom} begin +@signature ThCoindexedMonoidalCategory <: ThOpindexedMonoidalCategory begin # XXX: Copy-paste from `MonoidalAdditive`. # TODO: Axioms of cocartesian monoidal category. + @op (⊕) := oplus oplus(A::Ob, B::Ob)::Ob oplus(f::(A → B), g::(C → D))::((A ⊕ C) → (B ⊕ D)) ⊣ - (A::Ob, B::Ob, C::Ob, D::Ob) - @op (⊕) := oplus + [A::Ob, B::Ob, C::Ob, D::Ob] mzero()::Ob swap(A::Ob, B::Ob)::Hom(oplus(A,B),oplus(B,A)) plus(A::Ob)::((A ⊕ A) → A) zero(A::Ob)::(mzero() → A) - copair(f::(A → C), g::(B → C))::((A ⊕ B) → C) ⊣ (A::Ob, B::Ob, C::Ob) + copair(f::(A → C), g::(B → C))::((A ⊕ B) → C) ⊣ [A::Ob, B::Ob, C::Ob] coproj1(A::Ob, B::Ob)::(A → (A ⊕ B)) coproj2(A::Ob, B::Ob)::(B → (A ⊕ B)) end diff --git a/src/theories/Limits.jl b/src/theories/Limits.jl index 082c0b99c..52e5bee5a 100644 --- a/src/theories/Limits.jl +++ b/src/theories/Limits.jl @@ -18,7 +18,7 @@ products" (a category in which finite products exist) but of a "category with For a monoidal category axiomatization of finite products, see [`ThCartesianCategory`](@ref). """ -@theory ThCategoryWithProducts{Ob,Hom,Terminal,Product} <: ThCategory{Ob,Hom} begin +@theory ThCategoryWithProducts <: ThCategory begin Terminal()::TYPE Product(foot1::Ob, foot2::Ob)::TYPE @@ -29,22 +29,22 @@ For a monoidal category axiomatization of finite products, see # Binary products. product(A::Ob, B::Ob)::Product(A,B) - ob(Π::Product(A,B))::Ob ⊣ (A::Ob, B::Ob) - proj1(Π::Product(A,B))::(ob(Π) → A) ⊣ (A::Ob, B::Ob) - proj2(Π::Product(A,B))::(ob(Π) → B) ⊣ (A::Ob, B::Ob) + ob(Π::Product(A,B))::Ob ⊣ [A::Ob, B::Ob] + proj1(Π::Product(A,B))::(ob(Π) → A) ⊣ [A::Ob, B::Ob] + proj2(Π::Product(A,B))::(ob(Π) → B) ⊣ [A::Ob, B::Ob] (pair(Π::Product(A,B), f::(C → A), g::(C → B))::(C → ob(Π)) - ⊣ (A::Ob, B::Ob, C::Ob)) + ⊣ [A::Ob, B::Ob, C::Ob]) # Projection axioms. (pair(Π,f,g) ⋅ proj1(Π) == f - ⊣ (A::Ob, B::Ob, C::Ob, Π::Product(A,B), f::(C → A), g::(C → B))) + ⊣ [A::Ob, B::Ob, C::Ob, Π::Product(A,B), f::(C → A), g::(C → B)]) (pair(Π,f,g) ⋅ proj2(Π) == g - ⊣ (A::Ob, B::Ob, C::Ob, Π::Product(A,B), f::(C → A), g::(C → B))) + ⊣ [A::Ob, B::Ob, C::Ob, Π::Product(A,B), f::(C → A), g::(C → B)]) # Uniqueness axioms. - f == g ⊣ (C::Ob, ⊤::Terminal(), f::(C → ob(⊤)), g::(C → ob(⊤))) - (pair(h⋅proj1(Π), h⋅proj2(Π)) == h - ⊣ (A::Ob, B::Ob, C::Ob, Π::Product(A,B), h::(C → ob(Π)))) + f == g ⊣ [C::Ob, ⊤::Terminal(), f::(C → ob(⊤)), g::(C → ob(⊤))] + (pair(Π, h⋅proj1(Π), h⋅proj2(Π)) == h + ⊣ [A::Ob, B::Ob, C::Ob, Π::Product(A,B), h::(C → ob(Π))]) end """ Theory of a *(finitely) complete category* @@ -56,32 +56,33 @@ ideas". Strictly speaking, this theory is not of a "finitely complete category" (a category in which finite limits exist) but of a "category with *chosen* finite limits". """ -@theory ThCompleteCategory{Ob,Hom,Terminal,Product,Equalizer} <: - ThCategoryWithProducts{Ob,Hom,Terminal,Product} begin - Equalizer(f::(A → B), g::(A → B))::TYPE ⊣ (A::Ob, B::Ob) +@theory ThCompleteCategory <: ThCategoryWithProducts begin + Equalizer(f::(A → B), g::(A → B))::TYPE ⊣ [A::Ob, B::Ob] # Equalizers. - equalizer(f::(A → B), g::(A → B))::Equalizer(f,g) ⊣ (A::Ob, B::Ob) - ob(eq::Equalizer(f,g))::Ob ⊣ (A::Ob, B::Ob, f::(A → B), g::(A → B)) + equalizer(f::(A → B), g::(A → B))::Equalizer(f,g) ⊣ [A::Ob, B::Ob] + ob(eq::Equalizer(f,g))::Ob ⊣ [A::Ob, B::Ob, f::(A → B), g::(A → B)] (incl(eq::Equalizer(f,g))::(ob(eq) → A) - ⊣ (A::Ob, B::Ob, f::(A → B), g::(A → B))) + ⊣ [A::Ob, B::Ob, f::(A → B), g::(A → B)]) (factorize(eq::Equalizer(f,g), h::(C → A), eq_h::Equalizer(h⋅f,h⋅g))::(ob(eq_h) → ob(eq)) - ⊣ (A::Ob, B::Ob, C::Ob, f::(A → B), g::(A → B))) + ⊣ [A::Ob, B::Ob, C::Ob, f::(A → B), g::(A → B)]) # Equalizer axioms. - (incl(eq)⋅f == incl(eq)⋅g - ⊣ (A::Ob, B::Ob, f::(A → B), g::(A → B), eq::Equalizer(f,g))) - (incl(eq) == id(A) - ⊣ (A::Ob, B::Ob, f::(A → B), eq::Equalizer(f,f))) - (factorize(eq,h,eq_h) ⋅ incl(eq) == incl(eq_h) ⋅ h - ⊣ (A::Ob, B::Ob, C::Ob, f::(A → B), g::(A → B), h::(C → A), - eq::Equalizer(f,g), eq_h::Equalizer(h⋅f, h⋅g))) - (factorize(eq, k⋅incl(eq), eq_k) == k - ⊣ (A::Ob, B::Ob, D::Ob, f::(A → B), g::(A → B), eq::Equalizer(f,g), - k::(D → ob(eq)), eq_k::Equalizer(k⋅incl(eq)⋅f, k⋅incl(eq)⋅g))) + # (incl(eq)⋅f == incl(eq)⋅g + # ⊣ [A::Ob, B::Ob, f::(A → B), g::(A → B), eq::Equalizer(f,g)]) + # (incl(eq) == id(A) + # ⊣ [A::Ob, B::Ob, f::(A → B), eq::Equalizer(f,f)]) + # (factorize(eq,h,eq_h) ⋅ incl(eq) == incl(eq_h) ⋅ h + # ⊣ [A::Ob, B::Ob, C::Ob, f::(A → B), g::(A → B), h::(C → A), + # eq::Equalizer(f,g), eq_h::Equalizer(h⋅f, h⋅g)]) + # (factorize(eq, k⋅incl(eq), eq_k) == k + # ⊣ [A::Ob, B::Ob, D::Ob, f::(A → B), g::(A → B), eq::Equalizer(f,g), + # k::(D → ob(eq)), eq_k::Equalizer(k⋅incl(eq)⋅f, k⋅incl(eq)⋅g)]) end +using .ThCompleteCategory + # Colimits ########## @@ -94,7 +95,7 @@ of [`ThCategoryWithProducts`](@ref). For a monoidal category axiomatization of finite coproducts, see [`ThCocartesianCategory`](@ref). """ -@theory ThCategoryWithCoproducts{Ob,Hom,Initial,Coproduct} <: ThCategory{Ob,Hom} begin +@theory ThCategoryWithCoproducts <: ThCategory begin Initial()::TYPE Coproduct(foot1::Ob, foot2::Ob)::TYPE @@ -105,22 +106,22 @@ For a monoidal category axiomatization of finite coproducts, see # Binary coproducts. coproduct(A::Ob, B::Ob)::Coproduct(A,B) - ob(⨆::Coproduct(A,B))::Ob ⊣ (A::Ob, B::Ob) - coproj1(⨆::Coproduct(A,B))::(A → ob(⨆)) ⊣ (A::Ob, B::Ob) - coproj2(⨆::Coproduct(A,B))::(B → ob(⨆)) ⊣ (A::Ob, B::Ob) + ob(⨆::Coproduct(A,B))::Ob ⊣ [A::Ob, B::Ob] + coproj1(⨆::Coproduct(A,B))::(A → ob(⨆)) ⊣ [A::Ob, B::Ob] + coproj2(⨆::Coproduct(A,B))::(B → ob(⨆)) ⊣ [A::Ob, B::Ob] (copair(⨆::Coproduct(A,B), f::(A → C), g::(B → C))::(ob(⨆) → C) - ⊣ (A::Ob, B::Ob, C::Ob)) + ⊣ [A::Ob, B::Ob, C::Ob]) # Coprojection axioms. (coproj1(⨆) ⋅ copair(⨆,f,g) == f - ⊣ (A::Ob, B::Ob, C::Ob, ⨆::Coproduct(A,B), f::(A → C), g::(B → C))) + ⊣ [A::Ob, B::Ob, C::Ob, ⨆::Coproduct(A,B), f::(A → C), g::(B → C)]) (coproj2(⨆) ⋅ copair(⨆,f,g) == g - ⊣ (A::Ob, B::Ob, C::Ob, ⨆::Coproduct(A,B), f::(A → C), g::(B → C))) + ⊣ [A::Ob, B::Ob, C::Ob, ⨆::Coproduct(A,B), f::(A → C), g::(B → C)]) # Uniqueness axioms. - f == g ⊣ (C::Ob, ⊥::Initial(), f::(ob(⊥) → C), g::(ob(⊥) → C)) - (copair(coproj1(⨆)⋅h, coproj2(⨆)⋅h) == h - ⊣ (A::Ob, B::Ob, C::Ob, ⨆::Coproduct(A,B), h::(ob(⨆) → C))) + f == g ⊣ [C::Ob, ⊥::Initial(), f::(ob(⊥) → C), g::(ob(⊥) → C)] + (copair(⨆, coproj1(⨆)⋅h, coproj2(⨆)⋅h) == h + ⊣ [A::Ob, B::Ob, C::Ob, ⨆::Coproduct(A,B), h::(ob(⨆) → C)]) end """ Theory of a *(finitely) cocomplete category* @@ -128,29 +129,28 @@ end Finite colimits are presented in biased style, via finite coproducts and coequalizers. The axioms are dual to those of [`ThCompleteCategory`](@ref). """ -@theory ThCocompleteCategory{Ob,Hom,Initial,Coproduct,Coequalizer} <: - ThCategoryWithCoproducts{Ob,Hom,Initial,Coproduct} begin - Coequalizer(f::(A → B), g::(A → B))::TYPE ⊣ (A::Ob, B::Ob) +@theory ThCocompleteCategory <: ThCategoryWithCoproducts begin + Coequalizer(f::(A → B), g::(A → B))::TYPE ⊣ [A::Ob, B::Ob] # Coequalizers. - coequalizer(f::(A → B), g::(A → B))::Coequalizer(f,g) ⊣ (A::Ob, B::Ob) - ob(eq::Coequalizer(f,g))::Ob ⊣ (A::Ob, B::Ob, f::(A → B), g::(A → B)) + coequalizer(f::(A → B), g::(A → B))::Coequalizer(f,g) ⊣ [A::Ob, B::Ob] + ob(eq::Coequalizer(f,g))::Ob ⊣ [A::Ob, B::Ob, f::(A → B), g::(A → B)] (proj(eq::Coequalizer(f,g))::(B → ob(eq)) - ⊣ (A::Ob, B::Ob, f::(A → B), g::(A → B))) + ⊣ [A::Ob, B::Ob, f::(A → B), g::(A → B)]) (factorize(coeq::Coequalizer(f,g), h::(B → C), coeq_h::Coequalizer(f⋅h,g⋅h))::(ob(coeq) → ob(coeq_h)) - ⊣ (A::Ob, B::Ob, C::Ob, f::(A → B), g::(A → B))) + ⊣ [A::Ob, B::Ob, C::Ob, f::(A → B), g::(A → B)]) # Coequalizer axioms. (f⋅proj(coeq) == g⋅proj(coeq) - ⊣ (A::Ob, B::Ob, f::(A → B), g::(A → B), coeq::Coequalizer(f,g))) + ⊣ [A::Ob, B::Ob, f::(A → B), g::(A → B), coeq::Coequalizer(f,g)]) (proj(coeq) == id(B) - ⊣ (A::Ob, B::Ob, f::(A → B), coeq::Coequalizer(f,f))) + ⊣ [A::Ob, B::Ob, f::(A → B), coeq::Coequalizer(f,f)]) (proj(coeq) ⋅ factorize(coeq,h,coeq_h) == h ⋅ proj(coeq_h) - ⊣ (A::Ob, B::Ob, C::Ob, f::(A → B), g::(A → B), h::(B → C), - coeq::Coequalizer(f,g), coeq_h::Coequalizer(f⋅h, g⋅h))) + ⊣ [A::Ob, B::Ob, C::Ob, f::(A → B), g::(A → B), h::(B → C), + coeq::Coequalizer(f,g), coeq_h::Coequalizer(f⋅h, g⋅h)]) (factorize(coeq, proj(coeq)⋅k, coeq_k) == k - ⊣ (A::Ob, B::Ob, D::Ob, f::(A → B), g::(A → B), + ⊣ [A::Ob, B::Ob, D::Ob, f::(A → B), g::(A → B), coeq::Coequalizer(f,g), k::(ob(coeq) → D), - coeq_k::Coequalizer(f⋅proj(coeq)⋅k, g⋅proj(coeq)⋅k))) + coeq_k::Coequalizer(f⋅proj(coeq)⋅k, g⋅proj(coeq)⋅k)]) end diff --git a/src/theories/Monoidal.jl b/src/theories/Monoidal.jl index 23f98ba56..73a35a9e5 100644 --- a/src/theories/Monoidal.jl +++ b/src/theories/Monoidal.jl @@ -14,7 +14,6 @@ export ThMonoidalCategory, otimes, munit, ⊗, collect, ndims, ThTracedMonoidalCategory, FreeTracedMonoidalCategory, trace, ThHypergraphCategory, FreeHypergraphCategory -import Base: collect, ndims # Monoidal category ################### @@ -25,32 +24,32 @@ To avoid associators and unitors, we assume that the monoidal category is *strict*. By the coherence theorem this involves no loss of generality, but we might add a theory for weak monoidal categories later. """ -@theory ThMonoidalCategory{Ob,Hom} <: ThCategory{Ob,Hom} begin +@theory ThMonoidalCategory <: ThCategory begin @op (⊗) := otimes # Monoid operations. otimes(A::Ob, B::Ob)::Ob otimes(f::(A → B), g::(C → D))::((A ⊗ C) → (B ⊗ D)) ⊣ - (A::Ob, B::Ob, C::Ob, D::Ob) + [A::Ob, B::Ob, C::Ob, D::Ob] munit()::Ob # Monoid axioms. # # The last two axioms are the naturality equations associated with the left # and right unitors, in the strict case where they are identities. - (A ⊗ B) ⊗ C == A ⊗ (B ⊗ C) ⊣ (A::Ob, B::Ob, C::Ob) - munit() ⊗ A == A ⊣ (A::Ob) - A ⊗ munit() == A ⊣ (A::Ob) - (f ⊗ g) ⊗ h == f ⊗ (g ⊗ h) ⊣ (A::Ob, B::Ob, C::Ob, X::Ob, Y::Ob, Z::Ob, - f::(A → X), g::(B → Y), h::(C → Z)) - id(munit()) ⊗ f == f ⊣ (A::Ob, B::Ob, f::(A → B)) - f ⊗ id(munit()) == f ⊣ (A::Ob, B::Ob, f::(A → B)) + (A ⊗ B) ⊗ C == A ⊗ (B ⊗ C) ⊣ [A::Ob, B::Ob, C::Ob] + munit() ⊗ A == A ⊣ [A::Ob] + A ⊗ munit() == A ⊣ [A::Ob] + (f ⊗ g) ⊗ h == f ⊗ (g ⊗ h) ⊣ [A::Ob, B::Ob, C::Ob, X::Ob, Y::Ob, Z::Ob, + f::(A → X), g::(B → Y), h::(C → Z)] + id(munit()) ⊗ f == f ⊣ [A::Ob, B::Ob, f::(A → B)] + f ⊗ id(munit()) == f ⊣ [A::Ob, B::Ob, f::(A → B)] # Functorality axioms. ((f ⊗ g) ⋅ (h ⊗ k) == (f ⋅ h) ⊗ (g ⋅ k) - ⊣ (A::Ob, B::Ob, C::Ob, X::Ob, Y::Ob, Z::Ob, - f::(A → B), h::(B → C), g::(X → Y), k::(Y → Z))) - id(A ⊗ B) == id(A) ⊗ id(B) ⊣ (A::Ob, B::Ob) + ⊣ [A::Ob, B::Ob, C::Ob, X::Ob, Y::Ob, Z::Ob, + f::(A → B), h::(B → C), g::(X → Y), k::(Y → Z)]) + id(A ⊗ B) == id(A) ⊗ id(B) ⊣ [A::Ob, B::Ob] end # Convenience constructors @@ -83,29 +82,29 @@ show_latex(io::IO, expr::ObExpr{:munit}; kw...) = print(io, "I") """ Theory of (strict) *symmetric monoidal categories* """ -@theory ThSymmetricMonoidalCategory{Ob,Hom} <: ThMonoidalCategory{Ob,Hom} begin +@theory ThSymmetricMonoidalCategory <: ThMonoidalCategory begin braid(A::Ob, B::Ob)::((A ⊗ B) → (B ⊗ A)) @op (σ) := braid # Involutivity axiom. - σ(A,B) ⋅ σ(B,A) == id(A ⊗ B) ⊣ (A::Ob, B::Ob) + σ(A,B) ⋅ σ(B,A) == id(A ⊗ B) ⊣ [A::Ob, B::Ob] # Coherence axioms. # # Note: The last two axioms are deducible from the first two axioms together # with the naturality equations for the left/right unitors. We record them for # the sake of clarity and uniformity. - σ(A,B⊗C) == (σ(A,B) ⊗ id(C)) ⋅ (id(B) ⊗ σ(A,C)) ⊣ (A::Ob, B::Ob, C::Ob) - σ(A⊗B,C) == (id(A) ⊗ σ(B,C)) ⋅ (σ(A,C) ⊗ id(B)) ⊣ (A::Ob, B::Ob, C::Ob) - σ(A,munit()) == id(A) ⊣ (A::Ob) - σ(munit(),A) == id(A) ⊣ (A::Ob) + σ(A,B⊗C) == (σ(A,B) ⊗ id(C)) ⋅ (id(B) ⊗ σ(A,C)) ⊣ [A::Ob, B::Ob, C::Ob] + σ(A⊗B,C) == (id(A) ⊗ σ(B,C)) ⋅ (σ(A,C) ⊗ id(B)) ⊣ [A::Ob, B::Ob, C::Ob] + σ(A,munit()) == id(A) ⊣ [A::Ob] + σ(munit(),A) == id(A) ⊣ [A::Ob] # Naturality axiom. - (f ⊗ g) ⋅ σ(B,D) == σ(A,C) ⋅ (g ⊗ f) ⊣ (A::Ob, B::Ob, C::Ob, D::Ob, - f::(A → B), g::(C → D)) + (f ⊗ g) ⋅ σ(B,D) == σ(A,C) ⋅ (g ⊗ f) ⊣ [A::Ob, B::Ob, C::Ob, D::Ob, + f::(A → B), g::(C → D)] end -@syntax FreeSymmetricMonoidalCategory{ObExpr,HomExpr} ThSymmetricMonoidalCategory begin +@symbolic_model FreeSymmetricMonoidalCategory{ObExpr,HomExpr} ThSymmetricMonoidalCategory begin otimes(A::Ob, B::Ob) = associate_unit(new(A,B), munit) otimes(f::Hom, g::Hom) = associate(new(f,g)) compose(f::Hom, g::Hom) = associate_unit(new(f,g; strict=true), id) @@ -128,32 +127,32 @@ monoidal category **Set**. FIXME: This theory should also extend `ThCopresheaf` but multiple inheritance is not yet supported. """ -@theory ThSymmetricMonoidalCopresheaf{Ob,Hom,El} <: ThSymmetricMonoidalCategory{Ob,Hom} begin +@theory ThSymmetricMonoidalCopresheaf <: ThSymmetricMonoidalCategory begin El(ob::Ob)::TYPE # Functor. - act(x::El(A), f::Hom(A,B))::El(B) ⊣ (A::Ob, B::Ob) - @op (⋅) := act + act(x::El(A), f::Hom(A,B))::El(B) ⊣ [A::Ob, B::Ob] + # @op (⋅) := act # Laxator. - otimes(x::El(A), y::El(B))::El(otimes(A,B)) ⊣ (A::Ob, B::Ob) + otimes(x::El(A), y::El(B))::El(otimes(A,B)) ⊣ [A::Ob, B::Ob] elunit()::El(munit()) # Functorality axioms. - (x ⋅ f) ⋅ g == x ⋅ (f ⋅ g) ⊣ - (A::Ob, B::Ob, C::Ob, f::(A → B), g::(B → C), x::El(A)) - x ⋅ id(A) == x ⊣ (A::Ob, x::El(A)) + act(act(x, f), g) == act(x, (f ⋅ g)) ⊣ + [A::Ob, B::Ob, C::Ob, f::(A → B), g::(B → C), x::El(A)] + # x ⋅ id(A) == x ⊣ [A::Ob, x::El(A)] # Naturality of laxator. - (x ⊗ y) ⋅ (f ⊗ g) == (x ⋅ f) ⊗ (y ⋅ g) ⊣ - (A::Ob, B::Ob, C::Ob, D::Ob, x::El(A), y::El(B), f::(A → C), g::(B → D)) + # (x ⊗ y), (f ⊗ g) == (x ⋅ f) ⊗ (y ⋅ g) ⊣ + # [A::Ob, B::Ob, C::Ob, D::Ob, x::El(A), y::El(B), f::(A → C), g::(B → D)] - # Commutative monoid axioms for laxator. - (x ⊗ y) ⊗ z == x ⊗ (y ⊗ z) ⊣ - (A::Ob, B::Ob, C::Ob, x::El(A), y::El(B), z::El(C)) - x ⊗ elunit() == x ⊣ (A::Ob, x::El(A)) - elunit() ⊗ x == x ⊣ (A::Ob, x::El(A)) - (x ⊗ y) ⋅ σ(A,B) == y ⊗ x ⊣ (A::Ob, B::Ob, x::El(A), y::El(B)) + # # Commutative monoid axioms for laxator. + # (x ⊗ y) ⊗ z == x ⊗ (y ⊗ z) ⊣ + # [A::Ob, B::Ob, C::Ob, x::El(A), y::El(B), z::El(C)] + # x ⊗ elunit() == x ⊣ [A::Ob, x::El(A)] + # elunit() ⊗ x == x ⊣ [A::Ob, x::El(A)] + # (x ⊗ y) ⋅ σ(A,B) == y ⊗ x ⊣ [A::Ob, B::Ob, x::El(A), y::El(B)] end # Cartesian category @@ -174,21 +173,21 @@ References: Section 6.6: "Cartesian center" - Selinger, 1999, "Categorical structure of asynchrony" """ -@theory ThMonoidalCategoryWithDiagonals{Ob,Hom} <: ThSymmetricMonoidalCategory{Ob,Hom} begin +@theory ThMonoidalCategoryWithDiagonals <: ThSymmetricMonoidalCategory begin mcopy(A::Ob)::(A → (A ⊗ A)) @op (Δ) := mcopy delete(A::Ob)::(A → munit()) @op (◊) := delete # Commutative comonoid axioms. - Δ(A) ⋅ (Δ(A) ⊗ id(A)) == Δ(A) ⋅ (id(A) ⊗ Δ(A)) ⊣ (A::Ob) - Δ(A) ⋅ (◊(A) ⊗ id(A)) == id(A) ⊣ (A::Ob) - Δ(A) ⋅ (id(A) ⊗ ◊(A)) == id(A) ⊣ (A::Ob) - Δ(A) ⋅ σ(A,A) == Δ(A) ⊣ (A::Ob) + Δ(A) ⋅ (Δ(A) ⊗ id(A)) == Δ(A) ⋅ (id(A) ⊗ Δ(A)) ⊣ [A::Ob] + Δ(A) ⋅ (◊(A) ⊗ id(A)) == id(A) ⊣ [A::Ob] + Δ(A) ⋅ (id(A) ⊗ ◊(A)) == id(A) ⊣ [A::Ob] + Δ(A) ⋅ σ(A,A) == Δ(A) ⊣ [A::Ob] # Coherence axioms. - Δ(A⊗B) == (Δ(A) ⊗ Δ(B)) ⋅ (id(A) ⊗ σ(A,B) ⊗ id(B)) ⊣ (A::Ob, B::Ob) - ◊(A⊗B) == ◊(A) ⊗ ◊(B) ⊣ (A::Ob, B::Ob) + Δ(A⊗B) == (Δ(A) ⊗ Δ(B)) ⋅ (id(A) ⊗ σ(A,B) ⊗ id(B)) ⊣ [A::Ob, B::Ob] + ◊(A⊗B) == ◊(A) ⊗ ◊(B) ⊣ [A::Ob, B::Ob] Δ(munit()) == id(munit()) ◊(munit()) == id(munit()) end @@ -198,19 +197,19 @@ end For the traditional axiomatization of products, see [`ThCategoryWithProducts`](@ref). """ -@theory ThCartesianCategory{Ob,Hom} <: ThMonoidalCategoryWithDiagonals{Ob,Hom} begin - pair(f::(A → B), g::(A → C))::(A → (B ⊗ C)) ⊣ (A::Ob, B::Ob, C::Ob) +@theory ThCartesianCategory <: ThMonoidalCategoryWithDiagonals begin + pair(f::(A → B), g::(A → C))::(A → (B ⊗ C)) ⊣ [A::Ob, B::Ob, C::Ob] proj1(A::Ob, B::Ob)::((A ⊗ B) → A) proj2(A::Ob, B::Ob)::((A ⊗ B) → B) # Definitions of pairing and projections. - pair(f,g) == Δ(C)⋅(f⊗g) ⊣ (A::Ob, B::Ob, C::Ob, f::(C → A), g::(C → B)) - proj1(A,B) == id(A)⊗◊(B) ⊣ (A::Ob, B::Ob) - proj2(A,B) == ◊(A)⊗id(B) ⊣ (A::Ob, B::Ob) + pair(f,g) == Δ(C)⋅(f⊗g) ⊣ [A::Ob, B::Ob, C::Ob, f::(C → A), g::(C → B)] + proj1(A,B) == id(A)⊗◊(B) ⊣ [A::Ob, B::Ob] + proj2(A,B) == ◊(A)⊗id(B) ⊣ [A::Ob, B::Ob] # Naturality axioms. - f⋅Δ(B) == Δ(A)⋅(f⊗f) ⊣ (A::Ob, B::Ob, f::(A → B)) - f⋅◊(B) == ◊(A) ⊣ (A::Ob, B::Ob, f::(A → B)) + f⋅Δ(B) == Δ(A)⋅(f⊗f) ⊣ [A::Ob, B::Ob, f::(A → B)] + f⋅◊(B) == ◊(A) ⊣ [A::Ob, B::Ob, f::(A → B)] end """ Syntax for a free cartesian category. @@ -219,7 +218,7 @@ In this syntax, the pairing and projection operations are defined using duplication and deletion, and do not have their own syntactic elements. This convention could be dropped or reversed. """ -@syntax FreeCartesianCategory{ObExpr,HomExpr} ThCartesianCategory begin +@symbolic_model FreeCartesianCategory{ObExpr,HomExpr} ThCartesianCategory begin otimes(A::Ob, B::Ob) = associate_unit(new(A,B), munit) otimes(f::Hom, g::Hom) = associate(new(f,g)) compose(f::Hom, g::Hom) = associate_unit(new(f,g; strict=true), id) @@ -243,8 +242,8 @@ The terminology is nonstandard (is there any standard terminology?) but is supposed to mean a monoidal category with coherent diagonals and codiagonals. Unlike in a biproduct category, the naturality axioms need not be satisfied. """ -@signature ThMonoidalCategoryWithBidiagonals{Ob,Hom} <: - ThMonoidalCategoryWithDiagonals{Ob,Hom} begin +@signature ThMonoidalCategoryWithBidiagonals <: + ThMonoidalCategoryWithDiagonals begin mmerge(A::Ob)::((A ⊗ A) → A) @op (∇) := mmerge create(A::Ob)::(munit() → A) @@ -256,28 +255,28 @@ end Mathematically the same as [`ThSemiadditiveCategory`](@ref) but written multiplicatively, instead of additively. """ -@theory ThBiproductCategory{Ob,Hom} <: ThMonoidalCategoryWithBidiagonals{Ob,Hom} begin - pair(f::(A → B), g::(A → C))::(A → (B ⊗ C)) ⊣ (A::Ob, B::Ob, C::Ob) - copair(f::(A → C), g::(B → C))::((A ⊗ B) → C) ⊣ (A::Ob, B::Ob, C::Ob) +@theory ThBiproductCategory <: ThMonoidalCategoryWithBidiagonals begin + pair(f::(A → B), g::(A → C))::(A → (B ⊗ C)) ⊣ [A::Ob, B::Ob, C::Ob] + copair(f::(A → C), g::(B → C))::((A ⊗ B) → C) ⊣ [A::Ob, B::Ob, C::Ob] proj1(A::Ob, B::Ob)::((A ⊗ B) → A) proj2(A::Ob, B::Ob)::((A ⊗ B) → B) coproj1(A::Ob, B::Ob)::(A → (A ⊗ B)) coproj2(A::Ob, B::Ob)::(B → (A ⊗ B)) # Naturality axioms. - f⋅Δ(B) == Δ(A)⋅(f⊗f) ⊣ (A::Ob, B::Ob, f::(A → B)) - f⋅◊(B) == ◊(A) ⊣ (A::Ob, B::Ob, f::(A → B)) - ∇(A)⋅f == (f⊗f)⋅∇(B) ⊣ (A::Ob, B::Ob, f::(A → B)) - □(A)⋅f == □(B) ⊣ (A::Ob, B::Ob, f::(A → B)) + f⋅Δ(B) == Δ(A)⋅(f⊗f) ⊣ [A::Ob, B::Ob, f::(A → B)] + f⋅◊(B) == ◊(A) ⊣ [A::Ob, B::Ob, f::(A → B)] + ∇(A)⋅f == (f⊗f)⋅∇(B) ⊣ [A::Ob, B::Ob, f::(A → B)] + □(A)⋅f == □(B) ⊣ [A::Ob, B::Ob, f::(A → B)] # Bimonoid axioms. (These follow from naturality + coherence axioms.) - ∇(A)⋅Δ(A) == (Δ(A)⊗Δ(A)) ⋅ (id(A)⊗σ(A,A)⊗id(A)) ⋅ (∇(A)⊗∇(A)) ⊣ (A::Ob) - ∇(A)⋅◊(A) == ◊(A) ⊗ ◊(A) ⊣ (A::Ob) - □(A)⋅Δ(A) == □(A) ⊗ □(A) ⊣ (A::Ob) - □(A)⋅◊(A) == id(munit()) ⊣ (A::Ob) + ∇(A)⋅Δ(A) == (Δ(A)⊗Δ(A)) ⋅ (id(A)⊗σ(A,A)⊗id(A)) ⋅ (∇(A)⊗∇(A)) ⊣ [A::Ob] + ∇(A)⋅◊(A) == ◊(A) ⊗ ◊(A) ⊣ [A::Ob] + □(A)⋅Δ(A) == □(A) ⊗ □(A) ⊣ [A::Ob] + □(A)⋅◊(A) == id(munit()) ⊣ [A::Ob] end -@syntax FreeBiproductCategory{ObExpr,HomExpr} ThBiproductCategory begin +@symbolic_model FreeBiproductCategory{ObExpr,HomExpr} ThBiproductCategory begin otimes(A::Ob, B::Ob) = associate_unit(new(A,B), munit) otimes(f::Hom, g::Hom) = associate(new(f,g)) compose(f::Hom, g::Hom) = associate_unit(new(f,g; strict=true), id) @@ -300,7 +299,7 @@ show_latex(io::IO, expr::HomExpr{:create}; kw...) = """ Theory of (symmetric) *closed monoidal categories* """ -@signature ThClosedMonoidalCategory{Ob,Hom} <: ThSymmetricMonoidalCategory{Ob,Hom} begin +@signature ThClosedMonoidalCategory <: ThSymmetricMonoidalCategory begin # Internal hom of A and B, an object representing Hom(A,B) hom(A::Ob, B::Ob)::Ob @@ -308,12 +307,12 @@ show_latex(io::IO, expr::HomExpr{:create}; kw...) = ev(A::Ob, B::Ob)::((hom(A,B) ⊗ A) → B) # Currying (aka, lambda abstraction) - curry(A::Ob, B::Ob, f::((A ⊗ B) → C))::(A → hom(B,C)) ⊣ (C::Ob) + curry(A::Ob, B::Ob, f::((A ⊗ B) → C))::(A → hom(B,C)) ⊣ [C::Ob] end """ Syntax for a free closed monoidal category. """ -@syntax FreeClosedMonoidalCategory{ObExpr,HomExpr} ThClosedMonoidalCategory begin +@symbolic_model FreeClosedMonoidalCategory{ObExpr,HomExpr} ThClosedMonoidalCategory begin otimes(A::Ob, B::Ob) = associate_unit(new(A,B), munit) otimes(f::Hom, g::Hom) = associate(new(f,g)) compose(f::Hom, g::Hom) = associate_unit(new(f,g; strict=true), id) @@ -344,17 +343,17 @@ A CCC is a cartesian category with internal homs (aka, exponential objects). FIXME: This theory should also extend `ThClosedMonoidalCategory`, but multiple inheritance is not yet supported. """ -@signature ThCartesianClosedCategory{Ob,Hom} <: ThCartesianCategory{Ob,Hom} begin +@signature ThCartesianClosedCategory <: ThCartesianCategory begin hom(A::Ob, B::Ob)::Ob ev(A::Ob, B::Ob)::((hom(A,B) ⊗ A) → B) - curry(A::Ob, B::Ob, f::((A ⊗ B) → C))::(A → hom(B,C)) ⊣ (C::Ob) + curry(A::Ob, B::Ob, f::((A ⊗ B) → C))::(A → hom(B,C)) ⊣ [C::Ob] end """ Syntax for a free cartesian closed category. See also `FreeCartesianCategory`. """ -@syntax FreeCartesianClosedCategory{ObExpr,HomExpr} ThCartesianClosedCategory begin +@symbolic_model FreeCartesianClosedCategory{ObExpr,HomExpr} ThCartesianClosedCategory begin otimes(A::Ob, B::Ob) = associate_unit(new(A,B), munit) otimes(f::Hom, g::Hom) = associate(new(f,g)) compose(f::Hom, g::Hom) = associate_unit(new(f,g; strict=true), id) @@ -369,7 +368,7 @@ end """ Theory of *compact closed categories* """ -@theory ThCompactClosedCategory{Ob,Hom} <: ThClosedMonoidalCategory{Ob,Hom} begin +@theory ThCompactClosedCategory <: ThClosedMonoidalCategory begin # Dual A^* of object A dual(A::Ob)::Ob @@ -380,16 +379,16 @@ end dcounit(A::Ob)::((A ⊗ dual(A)) → munit()) # Adjoint mate of morphism f. - mate(f::(A → B))::(dual(B) → dual(A)) ⊣ (A::Ob, B::Ob) + mate(f::(A → B))::(dual(B) → dual(A)) ⊣ [A::Ob, B::Ob] # Axioms for closed monoidal structure. - hom(A, B) == B ⊗ dual(A) ⊣ (A::Ob, B::Ob) - ev(A, B) == id(B) ⊗ (σ(dual(A), A) ⋅ dcounit(A)) ⊣ (A::Ob, B::Ob) + hom(A, B) == B ⊗ dual(A) ⊣ [A::Ob, B::Ob] + ev(A, B) == id(B) ⊗ (σ(dual(A), A) ⋅ dcounit(A)) ⊣ [A::Ob, B::Ob] (curry(A, B, f) == (id(A) ⊗ (dunit(B) ⋅ σ(dual(B), B))) ⋅ (f ⊗ id(dual(B))) - ⊣ (A::Ob, B::Ob, C::Ob, f::((A ⊗ B) → C))) + ⊣ [A::Ob, B::Ob, C::Ob, f::((A ⊗ B) → C)]) end -@syntax FreeCompactClosedCategory{ObExpr,HomExpr} ThCompactClosedCategory begin +@symbolic_model FreeCompactClosedCategory{ObExpr,HomExpr} ThCompactClosedCategory begin dual(A::Ob) = distribute_unary(involute(new(A)), dual, otimes, unit=munit, contravariant=true) otimes(A::Ob, B::Ob) = associate_unit(new(A,B), munit) @@ -424,11 +423,11 @@ show_latex(io::IO, expr::HomExpr{:mate}; kw...) = """ Theory of *dagger categories* """ -@signature ThDaggerCategory{Ob,Hom} <: ThCategory{Ob,Hom} begin - dagger(f::(A → B))::(B → A) ⊣ (A::Ob, B::Ob) +@signature ThDaggerCategory <: ThCategory begin + dagger(f::(A → B))::(B → A) ⊣ [A::Ob, B::Ob] end -@syntax FreeDaggerCategory{ObExpr,HomExpr} ThDaggerCategory begin +@symbolic_model FreeDaggerCategory{ObExpr,HomExpr} ThDaggerCategory begin compose(f::Hom, g::Hom) = associate_unit(new(f,g; strict=true), id) dagger(f::Hom) = distribute_dagger(involute(new(f))) end @@ -447,11 +446,11 @@ category](https://ncatlab.org/nlab/show/symmetric+monoidal+dagger-category). FIXME: This theory should also extend `ThDaggerCategory`, but multiple inheritance is not yet supported. """ -@signature ThDaggerSymmetricMonoidalCategory{Ob,Hom} <: ThSymmetricMonoidalCategory{Ob,Hom} begin - dagger(f::(A → B))::(B → A) ⊣ (A::Ob, B::Ob) +@signature ThDaggerSymmetricMonoidalCategory <: ThSymmetricMonoidalCategory begin + dagger(f::(A → B))::(B → A) ⊣ [A::Ob, B::Ob] end -@syntax FreeDaggerSymmetricMonoidalCategory{ObExpr,HomExpr} ThDaggerSymmetricMonoidalCategory begin +@symbolic_model FreeDaggerSymmetricMonoidalCategory{ObExpr,HomExpr} ThDaggerSymmetricMonoidalCategory begin otimes(A::Ob, B::Ob) = associate_unit(new(A,B), munit) otimes(f::Hom, g::Hom) = associate(new(f,g)) compose(f::Hom, g::Hom) = associate_unit(new(f,g; strict=true), id) @@ -473,11 +472,11 @@ categories. FIXME: This theory should also extend `ThDaggerCategory`, but multiple inheritance is not yet supported. """ -@signature ThDaggerCompactCategory{Ob,Hom} <: ThCompactClosedCategory{Ob,Hom} begin - dagger(f::(A → B))::(B → A) ⊣ (A::Ob, B::Ob) +@signature ThDaggerCompactCategory <: ThCompactClosedCategory begin + dagger(f::(A → B))::(B → A) ⊣ [A::Ob, B::Ob] end -@syntax FreeDaggerCompactCategory{ObExpr,HomExpr} ThDaggerCompactCategory begin +@symbolic_model FreeDaggerCompactCategory{ObExpr,HomExpr} ThDaggerCompactCategory begin dual(A::Ob) = distribute_unary(involute(new(A)), dual, otimes, unit=munit, contravariant=true) otimes(A::Ob, B::Ob) = associate_unit(new(A,B), munit) @@ -496,11 +495,11 @@ show_latex(io::IO, expr::HomExpr{:dagger}; kw...) = """ Theory of *traced monoidal categories* """ -@signature ThTracedMonoidalCategory{Ob,Hom} <: ThSymmetricMonoidalCategory{Ob,Hom} begin +@signature ThTracedMonoidalCategory <: ThSymmetricMonoidalCategory begin trace(X::Ob, A::Ob, B::Ob, f::((X ⊗ A) → (X ⊗ B)))::(A → B) end -@syntax FreeTracedMonoidalCategory{ObExpr,HomExpr} ThTracedMonoidalCategory begin +@symbolic_model FreeTracedMonoidalCategory{ObExpr,HomExpr} ThTracedMonoidalCategory begin otimes(A::Ob, B::Ob) = associate_unit(new(A,B), munit) otimes(f::Hom, g::Hom) = associate(new(f,g)) compose(f::Hom, g::Hom) = associate_unit(new(f,g; strict=true), id) @@ -524,19 +523,19 @@ categories" and "spidered/dungeon categories", among other things. FIXME: Should also inherit `ThClosedMonoidalCategory` and `ThDaggerCategory`, but multiple inheritance is not yet supported. """ -@theory ThHypergraphCategory{Ob,Hom} <: ThMonoidalCategoryWithBidiagonals{Ob,Hom} begin +@theory ThHypergraphCategory <: ThMonoidalCategoryWithBidiagonals begin # Self-dual compact closed category. dunit(A::Ob)::(munit() → (A ⊗ A)) dcounit(A::Ob)::((A ⊗ A) → munit()) - dagger(f::(A → B))::(B → A) ⊣ (A::Ob, B::Ob) + dagger(f::(A → B))::(B → A) ⊣ [A::Ob, B::Ob] - dunit(A) == create(A) ⋅ mcopy(A) ⊣ (A::Ob) - dcounit(A) == mmerge(A) ⋅ delete(A) ⊣ (A::Ob) - (dagger(f) == (id(B) ⊗ dunit(A)) ⋅ (id(B) ⊗ f ⊗ id(A)) ⋅ (dcounit(B) ⊗ id(A)) - ⊣ (A::Ob, B::Ob, f::(A → B))) + dunit(A) == create(A) ⋅ mcopy(A) ⊣ [A::Ob] + dcounit(A) == mmerge(A) ⋅ delete(A) ⊣ [A::Ob] + (dagger(f) == ((id(B) ⊗ dunit(A)) ⋅ (id(B) ⊗ f ⊗ id(A)) ⋅ (dcounit(B) ⊗ id(A))) + ⊣ [A::Ob, B::Ob, f::(A → B)]) end -@syntax FreeHypergraphCategory{ObExpr,HomExpr} ThHypergraphCategory begin +@symbolic_model FreeHypergraphCategory{ObExpr,HomExpr} ThHypergraphCategory begin otimes(A::Ob, B::Ob) = associate_unit(new(A,B), munit) otimes(f::Hom, g::Hom) = associate(new(f,g)) compose(f::Hom, g::Hom) = associate_unit(new(f,g; strict=true), id) diff --git a/src/theories/MonoidalAdditive.jl b/src/theories/MonoidalAdditive.jl index fda58416e..6f6655d39 100644 --- a/src/theories/MonoidalAdditive.jl +++ b/src/theories/MonoidalAdditive.jl @@ -7,8 +7,6 @@ export ThMonoidalCategoryAdditive, ThSymmetricMonoidalCategoryAdditive, mcopy, delete, pair, proj1, proj2, Δ, ◊, +, antipode, ThHypergraphCategoryAdditive -import Base: collect, ndims, +, zero - # Monoidal category ################### @@ -17,11 +15,11 @@ import Base: collect, ndims, +, zero Mathematically the same as [`ThMonoidalCategory`](@ref) but with different notation. """ -@signature ThMonoidalCategoryAdditive{Ob,Hom} <: ThCategory{Ob,Hom} begin - oplus(A::Ob, B::Ob)::Ob - oplus(f::(A → B), g::(C → D))::((A ⊕ C) → (B ⊕ D)) <= - (A::Ob, B::Ob, C::Ob, D::Ob) +@signature ThMonoidalCategoryAdditive <: ThCategory begin @op (⊕) := oplus + oplus(A::Ob, B::Ob)::Ob + oplus(f::(A → B), g::(C → D))::((A ⊕ C) → (B ⊕ D)) ⊣ + [A::Ob, B::Ob, C::Ob, D::Ob] mzero()::Ob end @@ -53,12 +51,12 @@ show_latex(io::IO, expr::ObExpr{:mzero}; kw...) = print(io, "O") Mathematically the same as [`ThSymmetricMonoidalCategory`](@ref) but with different notation. """ -@signature ThSymmetricMonoidalCategoryAdditive{Ob,Hom} <: - ThMonoidalCategoryAdditive{Ob,Hom} begin +@signature ThSymmetricMonoidalCategoryAdditive <: + ThMonoidalCategoryAdditive begin swap(A::Ob, B::Ob)::Hom(oplus(A,B),oplus(B,A)) end -@syntax FreeSymmetricMonoidalCategoryAdditive{ObExpr,HomExpr} ThSymmetricMonoidalCategoryAdditive begin +@symbolic_model FreeSymmetricMonoidalCategoryAdditive{ObExpr,HomExpr} ThSymmetricMonoidalCategoryAdditive begin oplus(A::Ob, B::Ob) = associate_unit(new(A,B), mzero) oplus(f::Hom, g::Hom) = associate(new(f,g)) compose(f::Hom, g::Hom) = associate_unit(new(f,g; strict=true), id) @@ -79,20 +77,20 @@ Unlike in a cocartesian category, the naturality axioms need not be satisfied. For references, see [`ThMonoidalCategoryWithDiagonals`](@ref). """ -@theory ThMonoidalCategoryWithCodiagonals{Ob,Hom} <: - ThSymmetricMonoidalCategoryAdditive{Ob,Hom} begin +@theory ThMonoidalCategoryWithCodiagonals <: + ThSymmetricMonoidalCategoryAdditive begin plus(A::Ob)::((A ⊕ A) → A) zero(A::Ob)::(mzero() → A) # Commutative monoid axioms. - (plus(A) ⊕ id(A)) ⋅ plus(A) == (id(A) ⊕ plus(A)) ⋅ plus(A) ⊣ (A::Ob) - (zero(A) ⊕ id(A)) ⋅ plus(A) == id(A) ⊣ (A::Ob) - (id(A) ⊕ zero(A)) ⋅ plus(A) == id(A) ⊣ (A::Ob) - plus(A) == swap(A,A) ⋅ plus(A) ⊣ (A::Ob) + (plus(A) ⊕ id(A)) ⋅ plus(A) == (id(A) ⊕ plus(A)) ⋅ plus(A) ⊣ [A::Ob] + (zero(A) ⊕ id(A)) ⋅ plus(A) == id(A) ⊣ [A::Ob] + (id(A) ⊕ zero(A)) ⋅ plus(A) == id(A) ⊣ [A::Ob] + plus(A) == swap(A,A) ⋅ plus(A) ⊣ [A::Ob] # Coherence axioms. - plus(A⊕B) == (id(A) ⊕ swap(B,A) ⊕ id(B)) ⋅ (plus(A) ⊕ plus(B)) ⊣ (A::Ob, B::Ob) - zero(A⊕B) == zero(A) ⊕ zero(B) ⊣ (A::Ob, B::Ob) + plus(A⊕B) == (id(A) ⊕ swap(B,A) ⊕ id(B)) ⋅ (plus(A) ⊕ plus(B)) ⊣ [A::Ob, B::Ob] + zero(A⊕B) == zero(A) ⊕ zero(B) ⊣ [A::Ob, B::Ob] plus(mzero()) == id(mzero()) zero(mzero()) == id(mzero()) end @@ -102,19 +100,19 @@ end For the traditional axiomatization of coproducts, see [`ThCategoryWithCoproducts`](@ref). """ -@theory ThCocartesianCategory{Ob,Hom} <: ThMonoidalCategoryWithCodiagonals{Ob,Hom} begin - copair(f::(A → C), g::(B → C))::((A ⊕ B) → C) <= (A::Ob, B::Ob, C::Ob) +@theory ThCocartesianCategory <: ThMonoidalCategoryWithCodiagonals begin + copair(f::(A → C), g::(B → C))::((A ⊕ B) → C) ⊣ [A::Ob, B::Ob, C::Ob] coproj1(A::Ob, B::Ob)::(A → (A ⊕ B)) coproj2(A::Ob, B::Ob)::(B → (A ⊕ B)) # Definitions of copairing and coprojections. - copair(f,g) == (f⊕g)⋅plus(C) ⊣ (A::Ob, B::Ob, C::Ob, f::(A → C), g::(B → C)) - coproj1(A,B) == id(A)⊕zero(B) ⊣ (A::Ob, B::Ob) - coproj2(A,B) == zero(A)⊕id(B) ⊣ (A::Ob, B::Ob) + copair(f,g) == (f⊕g)⋅plus(C) ⊣ [A::Ob, B::Ob, C::Ob, f::(A → C), g::(B → C)] + coproj1(A,B) == id(A)⊕zero(B) ⊣ [A::Ob, B::Ob] + coproj2(A,B) == zero(A)⊕id(B) ⊣ [A::Ob, B::Ob] # Naturality axioms. - plus(A)⋅f == (f⊕f)⋅plus(B) ⊣ (A::Ob, B::Ob, f::(A → B)) - zero(A)⋅f == zero(B) ⊣ (A::Ob, B::Ob, f::(A → B)) + plus(A)⋅f == (f⊕f)⋅plus(B) ⊣ [A::Ob, B::Ob, f::(A → B)] + zero(A)⋅f == zero(B) ⊣ [A::Ob, B::Ob, f::(A → B)] end """ Syntax for a free cocartesian category. @@ -123,7 +121,7 @@ In this syntax, the copairing and inclusion operations are defined using merging and creation, and do not have their own syntactic elements. This convention could be dropped or reversed. """ -@syntax FreeCocartesianCategory{ObExpr,HomExpr} ThCocartesianCategory begin +@symbolic_model FreeCocartesianCategory{ObExpr,HomExpr} ThCocartesianCategory begin oplus(A::Ob, B::Ob) = associate_unit(new(A,B), mzero) oplus(f::Hom, g::Hom) = associate(new(f,g)) compose(f::Hom, g::Hom) = associate_unit(new(f,g; strict=true), id) @@ -150,21 +148,21 @@ end """ Theory of *monoidal categories with bidiagonals*, in additive notation -Mathematically the same as [`ThMonoidalCategoryWithBidiagonals`](@ref) but +Mathematically the same as (@ref) but written additively, instead of multiplicatively. """ -@theory ThMonoidalCategoryWithBidiagonalsAdditive{Ob,Hom} <: - ThMonoidalCategoryWithCodiagonals{Ob,Hom} begin +@theory ThMonoidalCategoryWithBidiagonalsAdditive <: + ThMonoidalCategoryWithCodiagonals begin mcopy(A::Ob)::(A → (A ⊕ A)) @op (Δ) := mcopy delete(A::Ob)::(A → mzero()) @op (◊) := delete # Commutative comonoid axioms. - Δ(A) == Δ(A) ⋅ swap(A,A) ⊣ (A::Ob) - Δ(A) ⋅ (Δ(A) ⊕ id(A)) == Δ(A) ⋅ (id(A) ⊕ Δ(A)) ⊣ (A::Ob) - Δ(A) ⋅ (◊(A) ⊕ id(A)) == id(A) ⊣ (A::Ob) - Δ(A) ⋅ (id(A) ⊕ ◊(A)) == id(A) ⊣ (A::Ob) + Δ(A) == Δ(A) ⋅ swap(A,A) ⊣ [A::Ob] + Δ(A) ⋅ (Δ(A) ⊕ id(A)) == Δ(A) ⋅ (id(A) ⊕ Δ(A)) ⊣ [A::Ob] + Δ(A) ⋅ (◊(A) ⊕ id(A)) == id(A) ⊣ [A::Ob] + Δ(A) ⋅ (id(A) ⊕ ◊(A)) == id(A) ⊣ [A::Ob] end """ Theory of *semiadditive categories* @@ -172,29 +170,28 @@ end Mathematically the same as [`ThBiproductCategory`](@ref) but written additively, instead of multiplicatively. """ -@theory ThSemiadditiveCategory{Ob,Hom} <: - ThMonoidalCategoryWithBidiagonalsAdditive{Ob,Hom} begin - pair(f::(A → B), g::(A → C))::(A → (B ⊕ C)) ⊣ (A::Ob, B::Ob, C::Ob) - copair(f::(A → C), g::(B → C))::((A ⊕ B) → C) ⊣ (A::Ob, B::Ob, C::Ob) +@theory ThSemiadditiveCategory <: + ThMonoidalCategoryWithBidiagonalsAdditive begin + pair(f::(A → B), g::(A → C))::(A → (B ⊕ C)) ⊣ [A::Ob, B::Ob, C::Ob] + copair(f::(A → C), g::(B → C))::((A ⊕ B) → C) ⊣ [A::Ob, B::Ob, C::Ob] proj1(A::Ob, B::Ob)::((A ⊕ B) → A) proj2(A::Ob, B::Ob)::((A ⊕ B) → B) coproj1(A::Ob, B::Ob)::(A → (A ⊕ B)) coproj2(A::Ob, B::Ob)::(B → (A ⊕ B)) - plus(f::(A → B), g::(A → B))::(A → B) ⊣ (A::Ob, B::Ob) - @op (+) := plus - + plus(f::(A → B), g::(A → B))::(A → B) ⊣ [A::Ob, B::Ob] + # Naturality axioms. - f⋅Δ(B) == Δ(A)⋅(f⊕f) ⊣ (A::Ob, B::Ob, f::(A → B)) - f⋅◊(B) == ◊(A) ⊣ (A::Ob, B::Ob, f::(A → B)) - plus(A)⋅f == (f⊕f)⋅plus(B) ⊣ (A::Ob, B::Ob, f::(A → B)) - zero(A)⋅f == zero(B) ⊣ (A::Ob, B::Ob, f::(A → B)) + f⋅Δ(B) == Δ(A)⋅(f⊕f) ⊣ [A::Ob, B::Ob, f::(A → B)] + f⋅◊(B) == ◊(A) ⊣ [A::Ob, B::Ob, f::(A → B)] + plus(A)⋅f == (f⊕f)⋅plus(B) ⊣ [A::Ob, B::Ob, f::(A → B)] + zero(A)⋅f == zero(B) ⊣ [A::Ob, B::Ob, f::(A → B)] # Bimonoid axioms. (These follow from naturality + coherence axioms.) - plus(A)⋅Δ(A) == (Δ(A)⊕Δ(A)) ⋅ (id(A)⊕swap(A,A)⊕id(A)) ⋅ (plus(A)⊕plus(A)) ⊣ (A::Ob) - plus(A)⋅◊(A) == ◊(A) ⊕ ◊(A) ⊣ (A::Ob) - zero(A)⋅Δ(A) == zero(A) ⊕ zero(A) ⊣ (A::Ob) - zero(A)⋅◊(A) == id(mzero()) ⊣ (A::Ob) + plus(A)⋅Δ(A) == (Δ(A)⊕Δ(A)) ⋅ (id(A)⊕swap(A,A)⊕id(A)) ⋅ (plus(A)⊕plus(A)) ⊣ [A::Ob] + plus(A)⋅◊(A) == ◊(A) ⊕ ◊(A) ⊣ [A::Ob] + zero(A)⋅Δ(A) == zero(A) ⊕ zero(A) ⊣ [A::Ob] + zero(A)⋅◊(A) == id(mzero()) ⊣ [A::Ob] end """ Theory of *additive categories* @@ -202,13 +199,13 @@ end An additive category is a biproduct category enriched in abelian groups. Thus, it is a semiadditive category where the hom-monoids have negatives. """ -@theory ThAdditiveCategory{Ob,Hom} <: ThSemiadditiveCategory{Ob,Hom} begin +@theory ThAdditiveCategory <: ThSemiadditiveCategory begin antipode(A::Ob)::(A → A) # Antipode axioms. - antipode(A) ⋅ f == f ⋅ antipode(B) ⊣ (A::Ob, B::Ob, f::(A → B)) - Δ(A)⋅(id(A)⊕antipode(A))⋅plus(A) == ◊(A)⋅zero(A) ⊣ (A::Ob) - Δ(A)⋅(antipode(A)⊕id(A))⋅plus(A) == ◊(A)⋅zero(A) ⊣ (A::Ob) + antipode(A) ⋅ f == f ⋅ antipode(B) ⊣ [A::Ob, B::Ob, f::(A → B)] + Δ(A)⋅(id(A)⊕antipode(A))⋅plus(A) == ◊(A)⋅zero(A) ⊣ [A::Ob] + Δ(A)⋅(antipode(A)⊕id(A))⋅plus(A) == ◊(A)⋅zero(A) ⊣ [A::Ob] end # Hypergraph category @@ -219,8 +216,8 @@ end Mathematically the same as [`ThHypergraphCategory`](@ref) but with different notation. """ -@signature ThHypergraphCategoryAdditive{Ob,Hom} <: - ThSymmetricMonoidalCategoryAdditive{Ob,Hom} begin +@signature ThHypergraphCategoryAdditive <: + ThSymmetricMonoidalCategoryAdditive begin # Supply of Frobenius monoids. mcopy(A::Ob)::(A → (A ⊕ A)) @op (Δ) := mcopy @@ -234,5 +231,5 @@ notation. # Self-dual compact closed category. dunit(A::Ob)::(mzero() → (A ⊕ A)) dcounit(A::Ob)::((A ⊕ A) → mzero()) - dagger(f::(A → B))::(B → A) ⊣ (A::Ob, B::Ob) + dagger(f::(A → B))::(B → A) ⊣ [A::Ob, B::Ob] end diff --git a/src/theories/MonoidalMultiple.jl b/src/theories/MonoidalMultiple.jl index ebee0f3be..dd1e716dc 100644 --- a/src/theories/MonoidalMultiple.jl +++ b/src/theories/MonoidalMultiple.jl @@ -16,11 +16,11 @@ we ignore coherence isomorphisms such as associators and unitors. FIXME: This theory should also inherit `ThMonoidalCategory`, but multiple inheritance is not supported. """ -@signature ThRigCategory{Ob,Hom} <: ThSymmetricMonoidalCategoryAdditive{Ob,Hom} begin +@signature ThRigCategory <: ThSymmetricMonoidalCategoryAdditive begin + @op (⊗) := otimes otimes(A::Ob, B::Ob)::Ob otimes(f::(A → B), g::(C → D))::((A ⊗ C) → (B ⊗ D)) ⊣ - (A::Ob, B::Ob, C::Ob, D::Ob) - @op (⊗) := otimes + [A::Ob, B::Ob, C::Ob, D::Ob] munit()::Ob end @@ -28,7 +28,7 @@ end FIXME: Should also inherit `ThSymmetricMonoidalCategory`. """ -@signature ThSymmetricRigCategory{Ob,Hom} <: ThRigCategory{Ob,Hom} begin +@signature ThSymmetricRigCategory <: ThRigCategory begin braid(A::Ob, B::Ob)::((A ⊗ B) → (B ⊗ A)) @op (σ) := braid end @@ -40,29 +40,30 @@ universal invariants", Section 3.2 FIXME: Should also inherit `ThCocartesianCategory`. """ -@theory ThDistributiveMonoidalCategory{Ob,Hom} <: ThSymmetricRigCategory{Ob,Hom} begin +@theory ThDistributiveMonoidalCategory <: ThSymmetricRigCategory begin + plus(A::Ob)::((A ⊕ A) → A) zero(A::Ob)::(mzero() → A) - copair(f::(A → C), g::(B → C))::((A ⊕ B) → C) <= (A::Ob, B::Ob, C::Ob) + copair(f::(A → C), g::(B → C))::((A ⊕ B) → C) ⊣ [A::Ob, B::Ob, C::Ob] coproj1(A::Ob, B::Ob)::(A → (A ⊕ B)) coproj2(A::Ob, B::Ob)::(B → (A ⊕ B)) - copair(f,g) == (f⊕g)⋅plus(C) ⊣ (A::Ob, B::Ob, C::Ob, f::(A → C), g::(B → C)) - coproj1(A,B) == id(A)⊕zero(B) ⊣ (A::Ob, B::Ob) - coproj2(A,B) == zero(A)⊕id(B) ⊣ (A::Ob, B::Ob) + copair(f,g) == (f⊕g)⋅plus(C) ⊣ [A::Ob, B::Ob, C::Ob, f::(A → C), g::(B → C)] + coproj1(A,B) == id(A)⊕zero(B) ⊣ [A::Ob, B::Ob] + coproj2(A,B) == zero(A)⊕id(B) ⊣ [A::Ob, B::Ob] # Naturality axioms. - plus(A)⋅f == (f⊕f)⋅plus(B) ⊣ (A::Ob, B::Ob, f::(A → B)) - zero(A)⋅f == zero(B) ⊣ (A::Ob, B::Ob, f::(A → B)) + plus(A)⋅f == (f⊕f)⋅plus(B) ⊣ [A::Ob, B::Ob, f::(A → B)] + zero(A)⋅f == zero(B) ⊣ [A::Ob, B::Ob, f::(A → B)] end """ Theory of a *distributive monoidal category with diagonals* FIXME: Should also inherit `ThMonoidalCategoryWithDiagonals`. """ -@theory ThDistributiveMonoidalCategoryWithDiagonals{Ob,Hom} <: - ThDistributiveMonoidalCategory{Ob,Hom} begin +@theory ThDistributiveMonoidalCategoryWithDiagonals <: + ThDistributiveMonoidalCategory begin mcopy(A::Ob)::(A → (A ⊗ A)) @op (Δ) := mcopy delete(A::Ob)::(A → munit()) @@ -78,19 +79,21 @@ biproduct. FIXME: Should also inherit `ThSemiadditiveCategory` """ -@theory ThDistributiveSemiadditiveCategory{Ob,Hom} <: ThDistributiveMonoidalCategory{Ob,Hom} begin +@theory ThDistributiveSemiadditiveCategory <: ThDistributiveMonoidalCategory begin mcopy(A::Ob)::(A → (A ⊕ A)) @op (Δ) := mcopy delete(A::Ob)::(A → mzero()) @op (◊) := delete - pair(f::(A → B), g::(A → C))::(A → (B ⊕ C)) ⊣ (A::Ob, B::Ob, C::Ob) + plus(f::(A → B), g::(A → B))::(A → B) ⊣ [A::Ob, B::Ob] + + pair(f::(A → B), g::(A → C))::(A → (B ⊕ C)) ⊣ [A::Ob, B::Ob, C::Ob] proj1(A::Ob, B::Ob)::((A ⊕ B) → A) proj2(A::Ob, B::Ob)::((A ⊕ B) → B) # Naturality axioms. - f⋅Δ(B) == Δ(A)⋅(f⊕f) ⊣ (A::Ob, B::Ob, f::(A → B)) - f⋅◊(B) == ◊(A) ⊣ (A::Ob, B::Ob, f::(A → B)) + f⋅Δ(B) == Δ(A)⋅(f⊕f) ⊣ [A::Ob, B::Ob, f::(A → B)] + f⋅◊(B) == ◊(A) ⊣ [A::Ob, B::Ob, f::(A → B)] end """ Theory of a *distributive category* @@ -100,16 +103,16 @@ is the cartesian product, see [`ThDistributiveMonoidalCategory`](@ref). FIXME: Should also inherit `ThCartesianCategory`. """ -@theory ThDistributiveCategory{Ob,Hom} <: ThDistributiveMonoidalCategoryWithDiagonals{Ob,Hom} begin - pair(f::(A → B), g::(A → C))::(A → (B ⊗ C)) ⊣ (A::Ob, B::Ob, C::Ob) +@theory ThDistributiveCategory <: ThDistributiveMonoidalCategoryWithDiagonals begin + pair(f::(A → B), g::(A → C))::(A → (B ⊗ C)) ⊣ [A::Ob, B::Ob, C::Ob] proj1(A::Ob, B::Ob)::((A ⊗ B) → A) proj2(A::Ob, B::Ob)::((A ⊗ B) → B) - pair(f,g) == Δ(C)⋅(f⊗g) ⊣ (A::Ob, B::Ob, C::Ob, f::(C → A), g::(C → B)) - proj1(A,B) == id(A)⊗◊(B) ⊣ (A::Ob, B::Ob) - proj2(A,B) == ◊(A)⊗id(B) ⊣ (A::Ob, B::Ob) + pair(f,g) == Δ(C)⋅(f⊗g) ⊣ [A::Ob, B::Ob, C::Ob, f::(C → A), g::(C → B)] + proj1(A,B) == id(A)⊗◊(B) ⊣ [A::Ob, B::Ob] + proj2(A,B) == ◊(A)⊗id(B) ⊣ [A::Ob, B::Ob] # Naturality axioms. - f⋅Δ(B) == Δ(A)⋅(f⊗f) ⊣ (A::Ob, B::Ob, f::(A → B)) - f⋅◊(B) == ◊(A) ⊣ (A::Ob, B::Ob, f::(A → B)) + f⋅Δ(B) == Δ(A)⋅(f⊗f) ⊣ [A::Ob, B::Ob, f::(A → B)] + f⋅◊(B) == ◊(A) ⊣ [A::Ob, B::Ob, f::(A → B)] end diff --git a/src/theories/Preorders.jl b/src/theories/Preorders.jl index 7df91aaef..f82a624d3 100644 --- a/src/theories/Preorders.jl +++ b/src/theories/Preorders.jl @@ -3,8 +3,6 @@ export ThThinCategory, FreeThinCategory, ThPreorder, ThPoset, FreePreorder, El, Leq, ≤, lhs, rhs, reflexive, transitive, ThLattice, ThAlgebraicLattice, meet, ∧, join, ∨, top, ⊤, bottom, ⊥ -import Base: join - # Thin category ############### @@ -13,11 +11,11 @@ import Base: join Thin categories have at most one morphism between any two objects and are isomorphic to preorders. """ -@theory ThThinCategory{Ob,Hom} <: ThCategory{Ob,Hom} begin - f == g ⊣ (A::Ob, B::Ob, f::Hom(A,B), g::Hom(A,B)) +@theory ThThinCategory <: ThCategory begin + f == g ⊣ [A::Ob, B::Ob, f::Hom(A,B), g::Hom(A,B)] end -@syntax FreeThinCategory{ObExpr,HomExpr} ThThinCategory begin +@symbolic_model FreeThinCategory{ObExpr,HomExpr} ThThinCategory begin compose(f::Hom, g::Hom) = associate_unit(new(f,g; strict=true), id) end @@ -25,11 +23,11 @@ end Thin SMCs are isomorphic to commutative monoidal prosets. """ -@theory ThThinSymmetricMonoidalCategory{Ob,Hom} <: ThSymmetricMonoidalCategory{Ob,Hom} begin - f == g ⊣ (A::Ob, B::Ob, f::Hom(A,B), g::Hom(A,B)) +@theory ThThinSymmetricMonoidalCategory <: ThSymmetricMonoidalCategory begin + f == g ⊣ [A::Ob, B::Ob, f::Hom(A,B), g::Hom(A,B)] end -@syntax FreeThinSymmetricMonoidalCategory{ObExpr,HomExpr} ThThinSymmetricMonoidalCategory begin +@symbolic_model FreeThinSymmetricMonoidalCategory{ObExpr,HomExpr} ThThinSymmetricMonoidalCategory begin compose(f::Hom, g::Hom) = associate_unit(new(f,g; strict=true), id) otimes(A::Ob, B::Ob) = associate_unit(new(A,B), munit) otimes(f::Hom, g::Hom) = associate(new(f,g)) @@ -44,18 +42,19 @@ The generalized algebraic theory of preorders encodes inequalities ``A≤B`` as dependent types ```Leq(A,B)`` and the axioms of reflexivity and transitivity as term constructors. """ -@theory ThPreorder{El,Leq} begin +@theory ThPreorder begin + @op (≤) := Leq + El::TYPE Leq(lhs::El, rhs::El)::TYPE - @op (≤) := Leq reflexive(A::El)::(A≤A) - transitive(f::(A≤B), g::(B≤C))::(A≤C) ⊣ (A::El, B::El, C::El) + transitive(f::(A≤B), g::(B≤C))::(A≤C) ⊣ [A::El, B::El, C::El] - f == g ⊣ (A::El, B::El, f::(A≤B), g::(A≤B)) + f == g ⊣ [A::El, B::El, f::(A≤B), g::(A≤B)] end -@syntax FreePreorder{ObExpr,HomExpr} ThPreorder begin +@symbolic_model FreePreorder{ObExpr,HomExpr} ThPreorder begin transitive(f::Leq, g::Leq) = associate(new(f,g; strict=true)) end @@ -71,8 +70,8 @@ end """ Theory of *partial orders* (posets) """ -@theory ThPoset{El,Leq} <: ThPreorder{El,Leq} begin - A == B ⊣ (A::El, B::El, f::(A≤B), g::(B≤A)) +@theory ThPoset <: ThPreorder begin + A == B ⊣ [A::El, B::El, f::(A≤B), g::(B≤A)] end # Lattice @@ -88,7 +87,8 @@ hence the names for the inequality constructors in the theory. Compare with This is one of two standard axiomatizations of a lattice, the other being [`ThAlgebraicLattice`](@ref). """ -@theory ThLattice{El,Leq} <: ThPoset{El,Leq} begin +@theory ThLattice <: ThPoset begin + @op begin (∧) := meet (⊤) := top @@ -100,7 +100,7 @@ This is one of two standard axiomatizations of a lattice, the other being meet(A::El, B::El)::El proj1(A::El, B::El)::((A ∧ B) ≤ A) proj2(A::El, B::El)::((A ∧ B) ≤ B) - pair(f::(C ≤ A), g::(C ≤ B))::(C ≤ (A ∧ B)) ⊣ (A::El, B::El, C::El) + pair(f::(C ≤ A), g::(C ≤ B))::(C ≤ (A ∧ B)) ⊣ [A::El, B::El, C::El] # Top = maximum. top()::El @@ -110,7 +110,7 @@ This is one of two standard axiomatizations of a lattice, the other being join(A::El, B::El)::El coproj1(A::El, B::El)::(A ≤ (A ∨ B)) coproj2(A::El, B::El)::(B ≤ (A ∨ B)) - copair(f::(A ≤ C), g::(B ≤ C))::((A ∨ B) ≤ C) ⊣ (A::El, B::El, C::El) + copair(f::(A ≤ C), g::(B ≤ C))::((A ∨ B) ≤ C) ⊣ [A::El, B::El, C::El] # Bottom = minimum. bottom()::El @@ -130,7 +130,8 @@ an equality type `Eq(lhs::El, rhs::El)::TYPE` combined with term constructors do not employ that trick here because at that point it is more convenient to just start with the poset structure, as in [`ThLattice`](@ref). """ -@theory ThAlgebraicLattice{El} begin +@theory ThAlgebraicLattice begin + @op begin (∧) := meet (⊤) := top @@ -138,25 +139,27 @@ just start with the poset structure, as in [`ThLattice`](@ref). (⊥) := bottom end + El::TYPE + # Meet/top as idempotent, commutative, associative, unital operation. meet(A::El, B::El)::El top()::El - (A ∧ B) ∧ C == A ∧ (B ∧ C) ⊣ (A::El, B::El, C::El) - A ∧ ⊤() == A ⊣ (A::El) - ⊤() ∧ A == A ⊣ (A::El) - A ∧ B == B ∧ A ⊣ (A::El, B::El) - A ∧ A == A ⊣ (A::El) + (A ∧ B) ∧ C == A ∧ (B ∧ C) ⊣ [A::El, B::El, C::El] + A ∧ ⊤() == A ⊣ [A::El] + ⊤() ∧ A == A ⊣ [A::El] + A ∧ B == B ∧ A ⊣ [A::El, B::El] + A ∧ A == A ⊣ [A::El] # Join/bottom as idempotent, commutative, associative, unital operation. join(A::El, B::El)::El bottom()::El - (A ∨ B) ∨ C == A ∨ (B ∨ C) ⊣ (A::El, B::El, C::El) - A ∨ ⊥() == A ⊣ (A::El) - ⊥() ∨ A == A ⊣ (A::El) - A ∨ B == B ∨ A ⊣ (A::El, B::El) - A ∨ A == A ⊣ (A::El) + (A ∨ B) ∨ C == A ∨ (B ∨ C) ⊣ [A::El, B::El, C::El] + A ∨ ⊥() == A ⊣ [A::El] + ⊥() ∨ A == A ⊣ [A::El] + A ∨ B == B ∨ A ⊣ [A::El, B::El] + A ∨ A == A ⊣ [A::El] # Absorption laws. - A ∨ (A ∧ B) == A ⊣ (A::El, B::El) - A ∧ (A ∨ B) == A ⊣ (A::El, B::El) + A ∨ (A ∧ B) == A ⊣ [A::El, B::El] + A ∧ (A ∨ B) == A ⊣ [A::El, B::El] end diff --git a/src/theories/Relations.jl b/src/theories/Relations.jl index f7158ebbb..eed0ae5c0 100644 --- a/src/theories/Relations.jl +++ b/src/theories/Relations.jl @@ -3,8 +3,6 @@ export ThBicategoryRelations, FreeBicategoryRelations, ThDistributiveBicategoryRelations, meet, join, top, bottom, plus, zero, coplus, cozero -import Base: join, zero - # Bicategory of relations ######################### @@ -18,13 +16,13 @@ References: - Walters, 2009, blog post, "Categorical algebras of relations", http://rfcwalters.blogspot.com/2009/10/categorical-algebras-of-relations.html """ -@signature ThBicategoryRelations{Ob,Hom} <: ThHypergraphCategory{Ob,Hom} begin +@signature ThBicategoryRelations <: ThHypergraphCategory begin # Logical operations. - meet(R::(A → B), S::(A → B))::(A → B) ⊣ (A::Ob, B::Ob) + meet(R::(A → B), S::(A → B))::(A → B) ⊣ [A::Ob, B::Ob] top(A::Ob, B::Ob)::(A → B) end -@syntax FreeBicategoryRelations{ObExpr,HomExpr} ThBicategoryRelations begin +@symbolic_model FreeBicategoryRelations{ObExpr,HomExpr} ThBicategoryRelations begin otimes(A::Ob, B::Ob) = associate_unit(new(A,B), munit) otimes(R::Hom, S::Hom) = associate(new(R,S)) compose(R::Hom, S::Hom) = associate_unit(new(R,S; strict=true), id) @@ -43,7 +41,7 @@ References: - Carboni & Walters, 1987, "Cartesian bicategories I", Sec. 5 - Baez & Erbele, 2015, "Categories in control" """ -@signature ThAbelianBicategoryRelations{Ob,Hom} <: ThHypergraphCategoryAdditive{Ob,Hom} begin +@signature ThAbelianBicategoryRelations <: ThHypergraphCategoryAdditive begin # Second supply of Frobenius monoids. plus(A::Ob)::((A ⊕ A) → A) zero(A::Ob)::(mzero() → A) @@ -51,13 +49,13 @@ References: cozero(A::Ob)::(A → mzero()) # Logical operations. - meet(R::(A → B), S::(A → B))::(A → B) ⊣ (A::Ob, B::Ob) + meet(R::(A → B), S::(A → B))::(A → B) ⊣ [A::Ob, B::Ob] top(A::Ob, B::Ob)::(A → B) - join(R::(A → B), S::(A → B))::(A → B) ⊣ (A::Ob, B::Ob) + join(R::(A → B), S::(A → B))::(A → B) ⊣ [A::Ob, B::Ob] bottom(A::Ob, B::Ob)::(A → B) end -@syntax FreeAbelianBicategoryRelations{ObExpr,HomExpr} ThAbelianBicategoryRelations begin +@symbolic_model FreeAbelianBicategoryRelations{ObExpr,HomExpr} ThAbelianBicategoryRelations begin oplus(A::Ob, B::Ob) = associate_unit(new(A,B), mzero) oplus(R::Hom, S::Hom) = associate(new(R,S)) compose(R::Hom, S::Hom) = associate_unit(new(R,S; strict=true), id) @@ -80,10 +78,10 @@ References: FIXME: Should also inherit `ThBicategoryOfRelations`, but multiple inheritance is not yet supported. """ -@signature ThDistributiveBicategoryRelations{Ob,Hom} <: - ThDistributiveMonoidalCategoryWithDiagonals{Ob,Hom} begin +@signature ThDistributiveBicategoryRelations <: + ThDistributiveMonoidalCategoryWithDiagonals begin # Self-dual dagger compact category. - dagger(R::(A → B))::(B → A) ⊣ (A::Ob, B::Ob) + dagger(R::(A → B))::(B → A) ⊣ [A::Ob, B::Ob] dunit(A::Ob)::(munit() → (A ⊗ A)) dcounit(A::Ob)::((A ⊗ A) → munit()) @@ -98,13 +96,13 @@ not yet supported. cozero(A::Ob)::(A → mzero()) # The coproduct is automatically a biproduct, due to compact closed structure. - pair(R::(A → B), S::(A → C))::(A → (B ⊕ C)) ⊣ (A::Ob, B::Ob, C::Ob) + pair(R::(A → B), S::(A → C))::(A → (B ⊕ C)) ⊣ [A::Ob, B::Ob, C::Ob] proj1(A::Ob, B::Ob)::((A ⊕ B) → A) proj2(A::Ob, B::Ob)::((A ⊕ B) → B) # Logical operations. - meet(R::(A → B), S::(A → B))::(A → B) ⊣ (A::Ob, B::Ob) + meet(R::(A → B), S::(A → B))::(A → B) ⊣ [A::Ob, B::Ob] top(A::Ob, B::Ob)::(A → B) - join(R::(A → B), S::(A → B))::(A → B) ⊣ (A::Ob, B::Ob) + join(R::(A → B), S::(A → B))::(A → B) ⊣ [A::Ob, B::Ob] bottom(A::Ob, B::Ob)::(A → B) end diff --git a/src/theories/Schema.jl b/src/theories/Schema.jl index 4d25a91e1..5f749d5c7 100644 --- a/src/theories/Schema.jl +++ b/src/theories/Schema.jl @@ -10,45 +10,45 @@ Attr : C^op x D → Set. In GAT form, this is given by extending the theory of categories with two extra types, AttrType for objects of D, and Attr, for elements of the sets given by the profunctor. """ -@theory ThSchema{Ob,Hom,AttrType,Attr} <: ThCategory{Ob,Hom} begin +@theory ThSchema <: ThCategory begin AttrType::TYPE Attr(dom::Ob,codom::AttrType)::TYPE - """ Composition is given by the action of the profunctor on C. - """ - compose(f::Hom(A,B), g::Attr(B,X))::Attr(A,X) ⊣ (A::Ob, B::Ob, X::AttrType) + # """ Composition is given by the action of the profunctor on C. + # """ + compose(f::Hom(A,B), g::Attr(B,X))::Attr(A,X) ⊣ [A::Ob, B::Ob, X::AttrType] (compose(f, compose(g, a)) == compose(compose(f, g), a) - ⊣ (A::Ob, B::Ob, C::Ob, X::AttrType, f::Hom(A,B), g::Hom(B,C), a::Attr(C, X))) - compose(id(A), a) == a ⊣ (A::Ob, X::AttrType, a::Attr(A,X)) + ⊣ [A::Ob, B::Ob, C::Ob, X::AttrType, f::Hom(A,B), g::Hom(B,C), a::Attr(C, X)]) + compose(id(A), a) == a ⊣ [A::Ob, X::AttrType, a::Attr(A,X)] end abstract type SchemaExpr{T} <: GATExpr{T} end abstract type AttrTypeExpr{T} <: SchemaExpr{T} end abstract type AttrExpr{T} <: SchemaExpr{T} end -@syntax FreeSchema{ObExpr,HomExpr,AttrTypeExpr,AttrExpr} ThSchema begin +@symbolic_model FreeSchema{ObExpr,HomExpr,AttrTypeExpr,AttrExpr} ThSchema begin # should have a normal representation for precompose of a morphism + a generator attribute compose(f::Hom, g::Hom) = associate_unit(new(f,g; strict=true), id) compose(f::Hom, x::Attr) = associate_unit(new(f,x; strict=true), id) end -@theory ThPointedSetSchema{Ob,Hom,AttrType,Attr} <: ThPointedSetCategory{Ob,Hom} begin +@theory ThPointedSetSchema <: ThPointedSetCategory begin AttrType::TYPE Attr(dom::Ob,codom::AttrType)::TYPE zeromap(A::Ob,X::AttrType)::Attr(A,X) - compose(f::Hom(A,B), g::Attr(B,X))::Attr(A,X) ⊣ (A::Ob, B::Ob, X::AttrType) + compose(f::Hom(A,B), g::Attr(B,X))::Attr(A,X) ⊣ [A::Ob, B::Ob, X::AttrType] - compose(f::Hom(A,B),zeromap(B,X)) == zeromap(A,X) ⊣ (A::Ob, B::Ob, X::AttrType) - compose(zeromap(A,B),f::Hom(B,X)) == zeromap(A,X) ⊣ (A::Ob, B::Ob, X::AttrType) + compose(f,zeromap(B,X)) == zeromap(A,X) ⊣ [A::Ob, B::Ob, X::AttrType, f::Hom(A,B)] + compose(zeromap(A,B),f) == zeromap(A,X) ⊣ [A::Ob, B::Ob, X::AttrType, f::Attr(B,X)] (compose(f, compose(g, a)) == compose(compose(f, g), a) - ⊣ (A::Ob, B::Ob, C::Ob, X::AttrType, f::Hom(A,B), g::Hom(B,C), a::Attr(C, X))) - compose(id(A), a) == a ⊣ (A::Ob, X::AttrType, a::Attr(A,X)) + ⊣ [A::Ob, B::Ob, C::Ob, X::AttrType, f::Hom(A,B), g::Hom(B,C), a::Attr(C, X)]) + compose(id(A), a) == a ⊣ [A::Ob, X::AttrType, a::Attr(A,X)] end -@syntax FreePointedSetSchema{ObExpr,HomExpr,AttrTypeExpr,AttrExpr} ThPointedSetSchema begin +@symbolic_model FreePointedSetSchema{ObExpr,HomExpr,AttrTypeExpr,AttrExpr} ThPointedSetSchema begin compose(f::Hom,g::Hom) = associate_unit(normalize_zero(new(f,g; strict=true)), id) compose(f::Hom,a::Attr) = associate_unit(normalize_zero(new(f,a; strict=true)), id) -end \ No newline at end of file +end diff --git a/src/theories/Theories.jl b/src/theories/Theories.jl index f3aba36bc..51fcc6677 100644 --- a/src/theories/Theories.jl +++ b/src/theories/Theories.jl @@ -6,13 +6,11 @@ are also included. module Theories export CategoryExpr, ObExpr, HomExpr -import Base: ≤ -import ACSets: dom, codom, ob, hom, attr, attrtype +import Base: ≤, +, *, inv, zero, join, show, collect, ndims +import AlgebraicInterfaces: dom, codom, compose, id, Ob, ob, Hom, hom, munit, + mcompose, ocompose, oapply, attr, attrtype -using ..GATs -import ..GATs: GATExpr, show_unicode, show_latex -using ..GATs.SyntaxSystems: show_unicode_infix, show_latex_infix, - show_latex_postfix, show_latex_script +using GATlab """ Base type for GAT expressions in categories or other categorical structures. diff --git a/test/Project.toml b/test/Project.toml index 6c098a2cd..dd534a3b9 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,8 +1,10 @@ [deps] +ACSets = "227ef7b5-1206-438b-ac65-934d6da304b8" Catlab = "134e5e36-593f-5add-ad60-77f754baafbe" Compose = "a81c6b42-2e10-5240-aca2-a61377ecd94b" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" +GATlab = "f0ffcf3b-d13a-433e-917c-cc44ccf5ead2" Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" diff --git a/test/categorical_algebra/Categories.jl b/test/categorical_algebra/Categories.jl index 00fea3c1f..bbf464e8c 100644 --- a/test/categorical_algebra/Categories.jl +++ b/test/categorical_algebra/Categories.jl @@ -1,8 +1,9 @@ module TestCategories using Test -using Catlab.GATs, Catlab.Theories -using Catlab.CategoricalAlgebra.Sets, Catlab.CategoricalAlgebra.Categories +using GATlab +using Catlab.Theories +using Catlab.CategoricalAlgebra # Instances ########### diff --git a/test/categorical_algebra/FinCats.jl b/test/categorical_algebra/FinCats.jl index f3586c8ac..ed8e28458 100644 --- a/test/categorical_algebra/FinCats.jl +++ b/test/categorical_algebra/FinCats.jl @@ -1,7 +1,8 @@ module TestFinCats using Test -using Catlab.GATs, Catlab.Theories, Catlab.CategoricalAlgebra, Catlab.Graphs +using GATlab +using Catlab.Theories, Catlab.CategoricalAlgebra, Catlab.Graphs # Categories on graphs ###################### @@ -159,7 +160,7 @@ G_refl = FinDomFunctor(path_graph(ReflexiveGraph, 3)) @test is_functorial(G_refl) G = compose(FinFunctor(Dict(:V=>:V, :E=>:E), Dict(:src=>:src, :tgt=>:tgt), SchGraph, SchReflexiveGraph), - G_refl, strict=false) + G_refl) @test dom(G) == FinCat(SchGraph) @test codom(G) == codom(G_refl) @test ob_map(G, :V) == FinSet(3) diff --git a/test/graphs/GraphAlgorithms.jl b/test/graphs/GraphAlgorithms.jl index dd042f24f..b17e762f3 100644 --- a/test/graphs/GraphAlgorithms.jl +++ b/test/graphs/GraphAlgorithms.jl @@ -1,7 +1,7 @@ module TestGraphAlgorithms using Test -using Catlab.Graphs, Catlab.CategoricalAlgebra +using Catlab.Graphs using Catlab.Theories # Connectivity @@ -72,4 +72,17 @@ ep = enumerate_paths(g) (2, 3, [3]) (3, 3, [])] +# Trees +####### + +g = Searching.tree([1, 1, 1, 2, 2]) +g′ = @acset Graph begin + V = 5 + E = 4 + src = [1, 1, 2, 2] + tgt = [2, 3, 4, 5] +end + +@test is_isomorphic(g,g′) + end diff --git a/test/graphs/GraphGenerators.jl b/test/graphs/GraphGenerators.jl index dc20d8216..9cc277acc 100644 --- a/test/graphs/GraphGenerators.jl +++ b/test/graphs/GraphGenerators.jl @@ -2,7 +2,7 @@ module TestGraphGenerators using Test using Catlab.Graphs.BasicGraphs, Catlab.Graphs.GraphGenerators -using Catlab.CategoricalAlgebra: homomorphisms +# using Catlab.CategoricalAlgebra: homomorphisms # Path graphs #------------ @@ -49,12 +49,12 @@ g = wheel_graph(Graph, n) @test (nv(g), ne(g)) == (n, 2(n-1)) triangle = Graph(3) add_edges!(triangle, [1,2,1], [2,3,3]) -@test length(homomorphisms(triangle, g)) == n-1 +# @test length(homomorphisms(triangle, g)) == n-1 g = wheel_graph(SymmetricGraph, n) @test (nv(g), ne(g)) == (n, 4(n-1)) triangle = cycle_graph(SymmetricGraph, 3) -@test length(homomorphisms(triangle, g)) == 6(n-1) # == 3! * (n-1) +# @test length(homomorphisms(triangle, g)) == 6(n-1) # == 3! * (n-1) # Parallel arrows #---------------- diff --git a/test/programs/DiagrammaticPrograms.jl b/test/programs/DiagrammaticPrograms.jl new file mode 100644 index 000000000..dfa70f017 --- /dev/null +++ b/test/programs/DiagrammaticPrograms.jl @@ -0,0 +1,816 @@ +module TestDiagrammaticPrograms +using Test + +using Catlab.GATs, Catlab.Graphs, Catlab.CategoricalAlgebra +using Catlab.Theories: FreeCategory, FreePointedSetCategory, FreePointedSetSchema +using Catlab.Programs.DiagrammaticPrograms, Catlab.CategoricalAlgebra.DataMigrations +using Catlab.Graphs.BasicGraphs: NamedGraph +using Catlab.Programs.DiagrammaticPrograms: get_keyword_arg_val, destructure_unary_call +using Catlab.WiringDiagrams.CPortGraphs +@present SchSet(ThSchema) begin + X::Ob +end +@present SchDDS <: SchSet begin + Φ::Hom(X,X) +end + +# Graphs +######## + +g = @graph begin + s + t + s → t + s → t +end +@test subpart(g,:vname) == [:s,:t] + +g = @graph NamedGraph{Symbol,Symbol} begin + x, y + (f, g): x → y +end +@test g == parallel_arrows(NamedGraph{Symbol,Symbol}, 2, + V=(vname=[:x,:y],), E=(ename=[:f,:g],)) + +tri_parsed = @graph NamedGraph{Symbol,Symbol} begin + v0, v1, v2 + fst: v0 → v1 + snd: v1 → v2 + comp: v0 → v2 +end +tri = @acset NamedGraph{Symbol,Symbol} begin + V = 3 + E = 3 + src = [1,2,1] + tgt = [2,3,3] + vname = [:v0, :v1, :v2] + ename = [:fst, :snd, :comp] +end +@test tri_parsed == tri + +# Categories +############ + +Δ¹_parsed = @fincat begin + V, E + (δ₀, δ₁): V → E + σ₀: E → V + + σ₀ ∘ δ₀ == id(V) + σ₀ ∘ δ₁ == id(V) +end +Δ¹_graph = @acset NamedGraph{Symbol,Symbol} begin + V = 2 + E = 3 + src = [1,1,2] + tgt = [2,2,1] + vname = [:V, :E] + ename = [:δ₀, :δ₁, :σ₀] +end +Δ¹ = FinCat(Δ¹_graph, [ [1,3] => empty(Path, Δ¹_graph, 1), + [2,3] => empty(Path, Δ¹_graph, 1) ]) +@test Δ¹_parsed == Δ¹ + +# Functors +########## + +# Underlying graph of circular port graph. +F = @finfunctor SchGraph SchCPortGraph begin + V => Box + E => Wire + src => src ⨟ box + tgt => tgt ⨟ box +end +@test F == FinFunctor(Dict(:V => :Box, :E => :Wire), + Dict(:src => [:src, :box], :tgt => [:tgt, :box]), + SchGraph, SchCPortGraph) + +# Incomplete definitions. +@test_throws ErrorException begin + @finfunctor SchGraph SchCPortGraph begin + V => Box + end +end +@test_throws ErrorException begin + @finfunctor SchGraph SchCPortGraph begin + V => Box + src => src ⨟ box + tgt => tgt ⨟ box + end +end + +# Failure of functorality. +@test_throws ErrorException begin + @finfunctor SchGraph SchCPortGraph begin + V => Box + E => Wire + src => src + tgt => tgt + end +end + +# Extra definitions. +@test_throws ErrorException begin + @finfunctor SchGraph SchReflexiveGraph begin + V => Box + E => Wire + src => src + tgt => tgt + refl => refl + end +end + +# GAT expressions. +F = @finfunctor SchDDS SchDDS begin + X => X; Φ => id(X) +end +F′ = @finfunctor SchDDS SchDDS begin + X => X; Φ => id{X} +end +@test F == F′ + +# Diagrams +########## + +C = FinCat(SchGraph) +d = @diagram C begin + v::V + (e1, e2)::E + (t: e1 → v)::tgt + (s: e2 → v)::src +end +J = FinCat(@present P(FreeCategory) begin + (v,e1,e2)::Ob + t::Hom(e1,v) + s::Hom(e2,v) +end) +F_parsed = diagram(d) +@test ob_generators(dom(F_parsed)) == ob_generators(J) +F = FinFunctor(Dict(:v=>:V,:e1=>:E,:e2=>:E), + Dict(:t=>:tgt,:s=>:src), J, C) +@test F_parsed.ob_map == F.ob_map + +d = @diagram SchGraph begin + v => V + (e1, e2) => E + t: e1 → v => tgt + s: e2 → v => src +end +@test all([ob_map(diagram(d),x) == ob_map(F,x) for x in ob_generators(dom(F))]) + +d = @diagram SchGraph begin + v::V + (e1, e2)::E + (e1 → v)::tgt + (e2 → v)::src +end +F_parsed = diagram(d) +J_parsed = dom(F_parsed) +@test src(graph(J_parsed)) == src(graph(J)) +@test tgt(graph(J_parsed)) == tgt(graph(J)) + +d′ = @free_diagram SchGraph begin + v::V + (e1, e2)::E + tgt(e1) == v + v == src(e2) +end +@test d′ == d + +d = @free_diagram SchGraph begin + (e1, e2)::E + tgt(e1) == src(e2) +end +@test is_functorial(diagram(d)) +@test collect_ob(d) == SchGraph[[:E, :E, :V]] +@test collect_hom(d) == SchGraph[[:tgt, :src]] + +d = @diagram SchDDS begin + x::X + (f: x → x)::(Φ⋅Φ) +end +@test only(collect_ob(d)) == SchDDS[:X] +@test only(collect_hom(d)) == compose(SchDDS[:Φ], SchDDS[:Φ]) + +# Diagrams with parameters + #------------------------- + + d = @free_diagram SchWeightedGraph begin + v::V + (e1, e2)::E + tgt(e1) == v + src(e2) == v + w :: Weight + w == 5.0 + weight(e1) == w + weight(e2) == w +end +@test sort(nameof.(collect_ob(d))) == [:E,:E,:V,:Weight] +@test sort(nameof.(collect_hom(d))) == [:src, :tgt, :weight, :weight] +@test d.params == Dict(:w => 5.0) + +d = @free_diagram SchWeightedGraph begin + (e1, e2)::E + tgt(e1) == src(e2) + weight(e1) == 0.5 + weight(e2) == 1.5 +end +@test sort(nameof.(collect_ob(d))) == [:E, :E, :V, :Weight, :Weight] +@test sort(nameof.(collect_hom(d))) == [:src, :tgt,:weight, :weight] +@test sort(collect(values(d.params))) == [0.5,1.5] +# Migrations +############ + +# Pullback migration +#------------------- + +# Graph with reversed edges. +M = @migration SchGraph SchGraph begin + V => V + E => E + src => tgt + tgt => src +end +@test M isa DataMigrations.DeltaSchemaMigration +@test functor(M) == FinFunctor(Dict(:V => :V, :E => :E), + Dict(:src => :tgt, :tgt => :src), + SchGraph, SchGraph) + +# Variant where target schema is not given. +M = @migration SchGraph begin + V => V + E => E + (src: E → V) => tgt + (tgt: E → V) => src +end +J = FinCat(@present P(ThSchema) begin + (V,E)::Ob + (src,tgt)::Hom(E,V) +end) + +@test functor(M) == FinDomFunctor(Dict(:E=>:E,:V=>:V), Dict(:tgt=>:src,:src=>:tgt), J, FinCat(SchGraph)) + +# Conjunctive migration +#---------------------- + +# Graph with edges that are paths of length 2. +M = @migration SchGraph SchGraph begin + V => V + E => @join begin + v::V + (e₁, e₂)::E + (e₁ → v)::tgt + (e₂ → v)::src + end + src => e₁ ⋅ src + tgt => e₂ ⋅ tgt +end +#Syntactic variant +@migration SchGraph SchGraph begin + V => V + E => @join begin + v::V + (e₁, e₂)::E + (e₁ → v)::tgt + (e₂ → v)::src + end + src => src∘e₁ + tgt => tgt∘e₂ +end +@test M isa DataMigrations.ConjSchemaMigration +F = functor(M) +F_E = diagram(ob_map(F, :E)) +@test nameof.(collect_ob(F_E)) == [:V, :E, :E] +@test nameof.(collect_hom(F_E)) == [:tgt, :src] +F_tgt = hom_map(F, :tgt) +@test collect_ob(F_tgt)[1][2] == (:V => SchGraph[:tgt]) + +# Syntactic variant of above. +M′ = @migration SchGraph SchGraph begin + V => V + E => @join begin + v::V + (e₁, e₂)::E + tgt(e₁) == v + src(e₂) == v + end + src => src(e₁) + tgt => tgt(e₂) +end +@test functor(M′) == F + +# "Bouquet graph" on set. +# This is the right adjoint to the underlying edge set functor. +M = @migration SchGraph SchSet begin + V => @product begin end + E => X + src => begin end + tgt => begin end +end +@test M isa DataMigrations.ConjSchemaMigration +@test isempty(shape(ob_map(functor(M), :V))) + +# Syntactic variant of above. +M′ = @migration SchGraph SchSet begin + V => @unit + E => X +end +@test M′ isa DataMigrations.ConjSchemaMigration +@test isempty(shape(ob_map(functor(M′), :V))) +@test isempty(shape(ob_map(functor(M),:V))) + +# Cartesian product of graph with itself. +M = @migration SchGraph SchGraph begin + V => @product (v₁::V; v₂::V) + E => @product (e₁::E; e₂::E) + src => (v₁ => e₁⋅src; v₂ => e₂⋅src) + tgt => (v₁ => e₁⋅tgt; v₂ => e₂⋅tgt) +end +F = functor(M) +F_V = diagram(ob_map(F, :V)) +@test collect_ob(F_V) == fill(SchGraph[:V], 2) +s = hom_generators(dom(F))[1] +F_src = hom_map(F, :src) +@test components(diagram_map(F_src)) == Dict(:v₂=>s,:v₁=>s) + +# Reflexive graph from graph. +M = @migration SchReflexiveGraph SchGraph begin + V => @join begin + v::V + ℓ::E + (s: ℓ → v)::src + (t: ℓ → v)::tgt + end + E => @join begin + (v₁, v₂)::V + (ℓ₁, ℓ₂, e)::E + (s₁: ℓ₁ → v₁)::src + (t₁: ℓ₁ → v₁)::tgt + (s₂: ℓ₂ → v₂)::src + (t₂: ℓ₂ → v₂)::tgt + (s: e → v₁)::src + (t: e → v₂)::tgt + end + refl => begin + (v₁, v₂) => v + (ℓ₁, ℓ₂, e) => ℓ + (s₁, s₂, s) => s + (t₁, t₂, t) => t + end + src => begin + v => v₁; ℓ => ℓ₁; s => s₁; t => t₁ + end + tgt => begin + v => v₂; ℓ => ℓ₂; s => s₂; t => t₂ + end +end +F = functor(M) +F_tgt = hom_map(F, :tgt) +@test ob_map(F_tgt, :v)[2] == id(SchGraph[:V]) + +# Free/initial port graph on a graph. +# This is the left adjoint to the underlying graph functor. +M = @migration SchGraph begin + Box => V + Wire => E + InPort => @join begin + v => V + e::E + (t: e → v)::tgt + end + OutPort => @join begin + v::V + e::E + (s: e → v)::src + end + (in_port_box: InPort → Box) => v + (out_port_box: OutPort → Box) => v + (src: Wire → OutPort) => begin + v => src + end + (tgt: Wire → InPort) => begin + v => tgt + end +end +F = functor(M) +F_src = hom_map(F, :src) +@test collect_ob(F_src) == [(SchGraph[:E], :v=>SchGraph[:src]),(SchGraph[:E],:e=>id(SchGraph[:E]))] +@test collect_hom(F_src) == [id(SchGraph[:E])] + +#XX:Yoneda is really slow +yGraph = yoneda(Graph) + +@migration(SchGraph, begin + I => @join begin v::V end +end) + +@test is_isomorphic( + @acset(Graph, begin E=2;V=3;src=[1,2];tgt=[2,3] end), + @acset_colim(yGraph, begin (e1,e2)::E; src(e1) == tgt(e2) end) +) + +# Gluing migration +#----------------- +# Discrete graph on set. +# This is the left adjoint to the underlying vertex set functor. +M = @migration SchGraph SchSet begin + V => X + E => @empty +end + +# Coproduct of graph with itself. +M = @migration SchGraph SchGraph begin + V => @cases (v₁::V; v₂::V) + E => @cases (e₁::E; e₂::E) + src => begin + e₁ => v₁ ∘ src + e₂ => v₂ ∘ src + end + tgt => begin + e₁ => v₁ ∘ tgt + e₂ => v₂ ∘ tgt + end +end +@test M isa DataMigrations.GlueSchemaMigration +F = functor(M) +F_V = diagram(ob_map(F, :V)) +@test collect_ob(F_V) == fill(SchGraph[:V], 2) +F_src = hom_map(F, :src) +@test [x[2] for x in collect_ob(F_src)] == [:(e₁) => SchGraph[:src], :(e₂) => SchGraph[:src]] + +# Free reflexive graph on a graph. +M = @migration SchReflexiveGraph SchGraph begin + V => V + E => @cases (e::E; v::V) + src => (e => src) + tgt => (e => tgt) + refl => v +end +F = functor(M) +F_tgt = hom_map(F, :tgt) +@test ob_map(F_tgt,:e) == (SchGraph[:V],SchGraph[:tgt]) + +# Vertices in a graph and their connected components. +M = @migration SchGraph begin + V => V + Component => @glue begin + e::E; v::V + (e → v)::src + (e → v)::tgt + end + (component: V → Component) => v +end +F = functor(M) +F_C = diagram(ob_map(F, :Component)) +@test ob_map(F_C,:e) == SchGraph[:E] +@test nameof.(collect_hom(F_C)) == [:src, :tgt] + +# Gluc migration +#--------------- + +# Labeled graph with edges that are paths of length <= 2. +M = @migration SchLabeledGraph SchLabeledGraph begin + V => V + E => @cases begin + v => V + e => E + path => @join begin + v::V + (e₁, e₂)::E + (e₁ → v)::tgt + (e₂ → v)::src + end + end + src => begin + e => src + path => e₁⋅src + end + tgt => begin + e => tgt + path => e₂⋅tgt + end + Label => Label + label => label +end + + +F = functor(M) +@test ob_map(F, :V) isa DataMigrations.GlucQuery +@test M isa DataMigrations.GlucSchemaMigration +F_src = hom_map(F, :src) +x = only(ob_generators(dom(diagram(ob_map(F,:V))))) +@test collect_ob(shape_map(F_src)) == fill(x,3) +F_src_v, F_src_e, F_src_path = collect(values(components(diagram_map(F_src)))) +@test collect_ob(F_src_v) == [(SchGraph[:V], SchGraph[:V]=>id(SchGraph[:V]))] +@test collect_ob(F_src_e) == [(Ob(FreeCategory,:(e₁)), :V => SchGraph[:src])] +@test collect_ob(F_src_path) == [(SchGraph[:E], :V => SchGraph[:src])] + +# Graph with edges that are minimal paths b/w like vertices in bipartite graph. +M = @migration SchGraph SchBipartiteGraph begin + V => @cases (v₁::V₁; v₂::V₂) + E => @cases begin + e₁ => @join begin + v₂::V₂; e₁₂::E₁₂; e₂₁::E₂₁ + (e₁₂ → v₂)::tgt₂ + (e₂₁ → v₂)::src₂ + end + e₂ => @join begin + v₁::V₁; e₂₁::E₂₁; e₁₂::E₁₂ + (e₂₁ → v₁)::tgt₁ + (e₁₂ → v₁)::src₁ + end + end + src => begin + e₁ => v₁ ∘ (e₁₂ ⋅ src₁) + e₂ => v₂ ∘ (e₂₁ ⋅ src₂) + end + tgt => begin + e₁ => v₁ ∘ (e₂₁ ⋅ tgt₁) + e₂ => v₂ ∘ (e₁₂ ⋅ tgt₂) + end +end +F = functor(M) +@test ob_map(F, :V) isa DataMigrations.GlucQuery +@test M isa DataMigrations.GlucSchemaMigration +F_src = hom_map(F, :src) +@test collect_ob(shape_map(F_src)) == [Ob(FreeCategory.Ob,:(v₁)),Ob(FreeCategory.Ob,:(v₂))] +F_src1, F_src2 = collect(values(components(diagram_map(F_src)))) +@test collect_ob(F_src1) == [(Ob(FreeCategory.Ob,:(e₁₂)), :V₁ => SchBipartiteGraph[:src₁])] +@test collect_ob(F_src2) == [(Ob(FreeCategory.Ob,:(e₂₁)), :V₂ => SchBipartiteGraph[:src₂])] + +# Box product of reflexive graph with itself. +M = @migration SchReflexiveGraph SchReflexiveGraph begin + V => @product (v₁::V; v₂::V) + E => @glue begin + vv => @product (v₁::V; v₂::V) + ev => @product (e₁::E; v₂::V) + ve => @product (v₁::V; e₂::E) + (refl₁: vv → ev) => (e₁ => v₁⋅refl; v₂ => v₂) + (refl₂: vv → ve) => (v₁ => v₁; e₂ => v₂⋅refl) + end + src => begin + ev => (v₁ => e₁⋅src; v₂ => v₂) + ve => (v₁ => v₁; v₂ => e₂⋅src) + end + tgt => begin + ev => (v₁ => e₁⋅tgt; v₂ => v₂) + ve => (v₁ => v₁; v₂ => e₂⋅tgt) + end + refl => vv +end +F = functor(M) +@test ob_map(F, :V) isa DataMigrations.GlucQuery +@test M isa DataMigrations.GlucSchemaMigration +F_src = hom_map(F, :src) +x = only(ob_generators(codom(shape_map(F_src)))) +@test collect_ob(shape_map(F_src)) == fill(x,3) +F_src_vv, F_src_ev, F_src_ve = [components(diagram_map(F_src))[a] for a in [:vv,:ev,:ve]] +@test map(last,collect_ob(F_src_ev)) == [:v₂=>id(SchGraph[:V]), + :v₁=> SchGraph[:src]] + +#Little parsing functions +@test get_keyword_arg_val(:(x=3)) == 3 +@test_throws ErrorException get_keyword_arg_val(:("not an assignment!"+3)) +@test destructure_unary_call(:(f(g(x)))) == (:(f∘g),:x) + +# Migrations with code +# +#------------------------------------ +@present SchMechLink <: SchGraph begin + Pos::AttrType + Len::AttrType + pos::Attr(V,Pos) + len::Attr(E,Len) +end +@acset_type MechLink(SchMechLink, index=[:src,:tgt]) + +G = @acset MechLink{Vector{Float64},Float64} begin + V = 3 + E = 2 + src = [1,2] + tgt = [2,3] + len = [1.0,1.0] + pos = [[1.0,1.0,1.0],[2.0,2.0,2.0],[2.0,2.0,1.0]] +end + +#Rotate the whole linkage by a bit +M = @migration SchMechLink SchMechLink begin + V => V + E => E + Pos => Pos + Len => Len + src => src + tgt => tgt + pos => begin + θ = π/5 + M = [[cos(θ),sin(θ),0] [-sin(θ),cos(θ),0] [0,0,1]] + x -> M*pos(x) + end + len => len +end +@test length(M.params) ==1 && M.params[:pos] isa Function +@test hom_map(functor(M),:pos) isa FreePointedSetSchema.Attr{:zeromap} +#Filter impossible edges out of a mechanical linkage +M = @migration SchMechLink SchMechLink begin + V => V + E => @join begin + e :: E + L :: Len + (l:e→L) :: (x->len(x)^2) + (d:e→L) :: (x->sum((pos(src(x))-pos(tgt(x))).^2)) + end + Pos => Pos + Len => Len + src => src(e) + tgt => tgt(e) + pos => pos + len => len(e) +end +migE = ob_map(functor(M),:E) +@test migE isa QueryDiagram +ps = ob_map(functor(M),:E).params +@test length(ps) == 2 +@test length(M.params) == 0 +#variant +M′ = @migration SchMechLink begin + V => V + E => @join begin + e :: E + L :: Len + (l:e→L) :: (x->len(x)^2) + (d:e→L) :: (x->sum((pos(src(x))-pos(tgt(x))).^2)) + end + Pos => Pos + Len => Len + (src:E→V) => src(e) + (tgt:E→V) => tgt(e) + (pos:V→Pos) => pos + (len:E→Len) => len(e) +end +F,F′ = functor(M),functor(M′) +@test all([ob_map(F,a)==ob_map(F′,a) for a in [:V,:Pos,:Len]]) +#The two migrations aren't perfectly equal because of intensional equality of +#Julia functions. +@test diagram(ob_map(F,:E)) == diagram(ob_map(F′,:E)) +@test all([hom_map(F,a) == hom_map(F′,a) for a in [:src,:tgt,:pos,:len]]) +#Also, the domains are only isomorphic because +#presentations involve meaningless ordering of the generators. +@test ob_generators(dom(F)) == ob_generators(dom(F′)) +#Filter impossible edges out of a mechanical linkage while rotating +M = @migration SchMechLink SchMechLink begin + V => V + E => @join begin + e :: E + L :: Len + (l:e→L) :: (x->len(x)^2) + (d:e→L) :: (x->sum((pos(src(x))-pos(tgt(x))).^2)) + end + Pos => Pos + Len => Len + src => src(e) + tgt => tgt(e) + pos => begin + θ = π/5 + M = [[cos(θ),sin(θ),0] [-sin(θ),cos(θ),0] [0,0,1]] + x -> M*pos(x) + end + len => len(e) +end +@test length(M.params) ==1 && length(ob_map(functor(M),:E).params) == 2 +#Filter out impossible edges, but then weirdly double all the lengths +M = @migration SchMechLink begin + V => V + E => @join begin + e :: E + L :: Len + (l:e→L) :: (x->len(x)^2) + (d:e→L) :: (x->sum((pos(src(x))-pos(tgt(x))).^2)) + end + Pos => Pos + Len => Len + (src:E→V) => src(e) + (tgt:E→V) => tgt(e) + (pos:V→Pos) => pos + (len:E→Len) => (len(e)|>(x->2x)) +end +#unabstracting x->2x over the unused variables +#for the functions in the acset to be migrated +f = M.params[:len](:src,:tgt,:pos,:len) +using Random.Random +xs = rand(Float64,100) +@test all([f(x)==2*x for x in xs]) +#Variant +M′ = @migration SchMechLink SchMechLink begin + V => V + E => @join begin + e :: E + L :: Len + (l:e→L) :: (x->len(x)^2) + (d:e→L) :: (x->sum((pos(src(x))-pos(tgt(x))).^2)) + end + Pos => Pos + Len => Len + src => src(e) + tgt => tgt(e) + pos => pos + len => (len(e)|>(x->2x)) +end +F,F′ = functor(M),functor(M′) +@test all([ob_map(F,a)==ob_map(F′,a) for a in [:V,:Pos,:Len]]) +@test diagram(ob_map(F,:E)) == diagram(ob_map(F′,:E)) + +#disjoint union linkage with itself, second copy reflected through origin +M = @migration SchMechLink SchMechLink begin + V => @cases (v₁::V;v₂::V) + E=> @cases (e₁::E;e₂::E) + Pos => Pos + Len => Len + src => begin + e₁ => v₁ ∘ src + e₂ => v₂ ∘ src + end + tgt => begin + e₁ => v₁ ∘ tgt + e₂ => v₂ ∘ tgt + end + pos => begin + v₁ => pos + v₂ => (pos|> (x-> -x)) + end + len => (e₁ => len ; e₂ => len) +end +@test isempty(M.params) +M_pos = hom_map(functor(M),:pos) +@test only(keys(M_pos.params)) == :(v₂) +f = M_pos.params[:(v₂)](:src,:tgt,:pos,:len) +xs = rand(Float64,100) +@test all([f(x)==-x for x in xs]) +@test (SchMechLink[:Pos],:(v₂)=>SchMechLink[:pos]) in collect_ob(M_pos) + +M′ = @migration SchMechLink SchMechLink begin + V => @cases (v₁::V;v₂::V) + E=> @cases (e₁::E;e₂::E) + Pos => Pos + Len => Len + src => begin + e₁ => v₁ ∘ src + e₂ => v₂ ∘ src + end + tgt => begin + e₁ => v₁ ∘ tgt + e₂ => v₂ ∘ tgt + end + pos => begin + v₁ => pos + v₂ => (x->-pos(x)) + end + len => (e₁ => len ; e₂ => len) +end +@test isempty(M′.params) +M′_pos = hom_map(functor(M′),:pos) +#XX:need to build querydiagramhom +#= +@test only(keys(M′_pos.params)) == :(v₂) +f = M′_pos.params[:(v₂)](:src,:tgt,:pos,:len) +xs = rand(Float64,100) +@test all([f(x)==-x for x in xs]) +=# + +#Filter impossible edges and also make a copy reflected through the +#origin. +M = @migration SchMechLink SchMechLink begin + V => @cases (v₁::V;v₂::V) + E=> @cases begin + e₁ => @join begin + e :: E + L :: Len + (l:e→L) :: (x->len(x)^2) + (d:e→L) :: (x->sum((pos(src(x))-pos(tgt(x))).^2)) + end + e₂ => @join begin + e :: E + L :: Len + (l:e→L) :: (x->len(x)^2) + (d:e→L) :: (x->sum((pos(src(x))-pos(tgt(x))).^2)) + end +end + Pos => Pos + Len => Len + src => begin + e₁ => v₁ ∘ (e⋅src) + e₂ => v₂ ∘ (e⋅src) + end + tgt => begin + e₁ => v₁ ∘ (e⋅tgt) + e₂ => v₂ ∘ (e⋅tgt) + end + pos => begin + v₁ => pos + v₂ => (pos|> (x-> -x)) + end + len => (e₁ => e⋅len ; e₂ => e⋅len) +end +@test isempty(M.params) +@test length(hom_map(functor(M),:pos).params) == 1 + +end \ No newline at end of file From 24f4631a1da4d02f61846b406560d294bbadbe27 Mon Sep 17 00:00:00 2001 From: Owen Lynch Date: Tue, 10 Oct 2023 15:46:29 -0700 Subject: [PATCH 02/10] all of categorical algebra loads --- src/categorical_algebra/FunctorialDataMigrations.jl | 10 +++++----- src/graphs/NamedGraphs.jl | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/categorical_algebra/FunctorialDataMigrations.jl b/src/categorical_algebra/FunctorialDataMigrations.jl index c3b37b8d3..388e363d9 100644 --- a/src/categorical_algebra/FunctorialDataMigrations.jl +++ b/src/categorical_algebra/FunctorialDataMigrations.jl @@ -299,7 +299,7 @@ which works because left Kan extensions take representables to representables representables (they can be infinite), this function thus inherits any limitations of our implementation of left pushforward data migrations. """ -function representable(T, C::Presentation{ThSchema}, ob::Symbol; +function representable(T, C::Presentation{ThSchema.Meta.T}, ob::Symbol; return_unit_id::Bool=false) C₀ = Presentation{Symbol}(FreeSchema) add_generator!(C₀, C[ob]) @@ -339,7 +339,7 @@ map from B into the A which sends the point of B to f applied to the point of A) Returns the classifier as well as a dictionary of subobjects corresponding to the parts of the classifier. """ -function subobject_classifier(T::Type, S::Presentation{ThSchema}; kw...) +function subobject_classifier(T::Type, S::Presentation{ThSchema.Meta.T}; kw...) isempty(generators(S, :AttrType)) || error("Cannot compute Ω for schema with attributes") y = yoneda(T, S; kw...) @@ -374,7 +374,7 @@ Given a map f: a->b, we compute that f(Aᵢ) = Bⱼ by constructing the followin where f* is given by `yoneda`. """ -function internal_hom(G::T, F::T, S::Presentation{ThSchema}; kw...) where T<:ACSet +function internal_hom(G::T, F::T, S::Presentation{ThSchema.Meta.T}; kw...) where T<:ACSet y = yoneda(T, S; kw...) obs = nameof.(generators(S, :Ob)) prods = Dict(ob => product(ob_map(y, ob),G) for ob in obs) @@ -405,7 +405,7 @@ they can be supplied via the `cache` keyword argument. Returns a `FinDomFunctor` with domain `op(C)`. """ -function yoneda(cons, C::Presentation{ThSchema}; +function yoneda(cons, C::Presentation{ThSchema.Meta.T}; cache::AbstractDict=Dict{Symbol,Any}()) # Compute any representables that have not already been computed. for c in nameof.(generators(C, :Ob)) @@ -444,7 +444,7 @@ yoneda(X::DynamicACSet; kw...) = yoneda(constructor(X), Presentation(X.schema); Returns a `FreeDiagram` whose objects are the generating objects of `pres` and whose homs are the generating homs of `pres`. """ -function FreeDiagrams.FreeDiagram(pres::Presentation{ThSchema, Symbol}) +function FreeDiagrams.FreeDiagram(pres::Presentation{ThSchema.Meta.T, Symbol}) obs = Array{FreeSchema.Ob}(generators(pres, :Ob)) homs = Array{FreeSchema.Hom}(generators(pres, :Hom)) doms = map(h -> generator_index(pres, nameof(dom(h))), homs) diff --git a/src/graphs/NamedGraphs.jl b/src/graphs/NamedGraphs.jl index c52ab100c..63ff82154 100644 --- a/src/graphs/NamedGraphs.jl +++ b/src/graphs/NamedGraphs.jl @@ -10,7 +10,8 @@ export has_vertex_names, has_edge_names, vertex_name, edge_name, vertex_named, edge_named, AbstractNamedGraph, NamedGraph using ACSets -using ...GATs, ..BasicGraphs +using GATlab +using ..BasicGraphs # Names interface ################# From 515a1ee865c0c98d3de9928d43340f7bd32a7311 Mon Sep 17 00:00:00 2001 From: Owen Lynch Date: Wed, 11 Oct 2023 17:21:16 -0700 Subject: [PATCH 03/10] the glorious future is here --- src/Catlab.jl | 12 +- src/categorical_algebra/ACSetsGATsInterop.jl | 20 +- src/categorical_algebra/CSets.jl | 2 +- src/categorical_algebra/FinCats.jl | 4 +- src/categorical_algebra/Limits.jl | 4 +- src/categorical_algebra/Slices.jl | 4 +- src/graphics/GraphvizCategories.jl | 9 +- src/graphics/TikZWiringDiagrams.jl | 2 +- src/graphics/WiringDiagramLayouts.jl | 3 +- src/graphs/BasicGraphs.jl | 2 +- src/programs/GenerateJuliaPrograms.jl | 4 +- src/programs/ParseJuliaPrograms.jl | 12 +- src/programs/RelationalPrograms.jl | 3 +- src/theories/Theories.jl | 1 + src/wiring_diagrams/CPortGraphs.jl | 3 +- src/wiring_diagrams/Directed.jl | 4 +- src/wiring_diagrams/Expressions.jl | 14 +- src/wiring_diagrams/MonoidalDirected.jl | 47 +-- src/wiring_diagrams/MonoidalUndirected.jl | 2 +- src/wiring_diagrams/ScheduleUndirected.jl | 2 +- src/wiring_diagrams/Undirected.jl | 3 +- test/categorical_algebra/CSets.jl | 3 +- test/categorical_algebra/CatElements.jl | 3 +- test/categorical_algebra/Chase.jl | 3 +- test/categorical_algebra/FinSets.jl | 3 +- .../FunctorialDataMigrations.jl | 3 +- test/categorical_algebra/StructuredCospans.jl | 3 +- test/gats/GATs.jl | 14 - test/gats/MetaUtils.jl | 56 ---- test/gats/Presentations.jl | 155 ---------- test/gats/SyntaxSystems.jl | 197 ------------ test/gats/TheoriesInstances.jl | 286 ------------------ test/graphics/GraphvizCategories.jl | 3 +- test/graphs/GraphAlgorithms.jl | 4 +- test/programs/GenerateJuliaPrograms.jl | 3 +- test/programs/ParseJuliaPrograms.jl | 5 +- test/runtests.jl | 4 - test/theories/Category.jl | 4 +- test/theories/Theories.jl | 3 +- test/wiring_diagrams/Expressions.jl | 2 +- test/wiring_diagrams/MonoidalDirected.jl | 16 +- 41 files changed, 119 insertions(+), 808 deletions(-) delete mode 100644 test/gats/GATs.jl delete mode 100644 test/gats/MetaUtils.jl delete mode 100644 test/gats/Presentations.jl delete mode 100644 test/gats/SyntaxSystems.jl delete mode 100644 test/gats/TheoriesInstances.jl diff --git a/src/Catlab.jl b/src/Catlab.jl index d79721127..fd52af047 100644 --- a/src/Catlab.jl +++ b/src/Catlab.jl @@ -6,15 +6,15 @@ include("theories/Theories.jl") include("categorical_algebra/ACSetsGATsInterop.jl") include("graphs/Graphs.jl") include("categorical_algebra/CategoricalAlgebra.jl") -# include("wiring_diagrams/WiringDiagrams.jl") -# include("graphics/Graphics.jl") -# include("programs/Programs.jl") +include("wiring_diagrams/WiringDiagrams.jl") +include("graphics/Graphics.jl") +include("programs/Programs.jl") @reexport using .Theories @reexport using .Graphs @reexport using .CategoricalAlgebra -# @reexport using .WiringDiagrams -# @reexport using .Graphics -# @reexport using .Programs +@reexport using .WiringDiagrams +@reexport using .Graphics +@reexport using .Programs end diff --git a/src/categorical_algebra/ACSetsGATsInterop.jl b/src/categorical_algebra/ACSetsGATsInterop.jl index e6adbcc64..930c34b09 100644 --- a/src/categorical_algebra/ACSetsGATsInterop.jl +++ b/src/categorical_algebra/ACSetsGATsInterop.jl @@ -10,7 +10,7 @@ import ACSets: Schema using GATlab import GATlab: Presentation -using .ThCategory +using ..Theories using MLStyle @@ -52,16 +52,16 @@ function Presentation(::Type{<:StructACSet{S}}) where S <: TypeLevelBasicSchema{ Presentation(Schema(S)) end -# function Presentation(s::BasicSchema{Symbol}) -# pres = Presentation(FreeSchema) -# obs = OrderedDict(x => Ob(FreeSchema.Ob, x) for x in Schemas.objects(s)) -# attrtypes = OrderedDict(x => AttrType(FreeSchema.AttrType, x) for x in Schemas.attrtypes(s)) -# homs = [Hom(f, obs[d], obs[c]) for (f,d,c) in Schemas.homs(s)] -# attrs = [Attr(f, obs[d], attrtypes[c]) for (f,d,c) in Schemas.attrs(s)] +function Presentation(s::BasicSchema{Symbol}) + pres = Presentation(FreeSchema) + obs = OrderedDict(x => Ob(FreeSchema.Ob, x) for x in Schemas.objects(s)) + attrtypes = OrderedDict(x => AttrType(FreeSchema.AttrType, x) for x in Schemas.attrtypes(s)) + homs = [Hom(f, obs[d], obs[c]) for (f,d,c) in Schemas.homs(s)] + attrs = [Attr(f, obs[d], attrtypes[c]) for (f,d,c) in Schemas.attrs(s)] -# foreach(gens -> add_generators!(pres, gens), (values(obs), homs, values(attrtypes), attrs)) -# return pres -# end + foreach(gens -> add_generators!(pres, gens), (values(obs), homs, values(attrtypes), attrs)) + return pres +end function DenseACSets.struct_acset(name::Symbol, parent, p::Presentation; index::Vector=[], unique_index::Vector=[]) diff --git a/src/categorical_algebra/CSets.jl b/src/categorical_algebra/CSets.jl index 49e63235d..adcdd3d7a 100644 --- a/src/categorical_algebra/CSets.jl +++ b/src/categorical_algebra/CSets.jl @@ -708,7 +708,7 @@ end #################### @instance ThCategory{ACSet, ACSetTransformation} begin - @import Ob, Hom + @import Ob, Hom, → dom(α::ACSetTransformation) = α.dom codom(α::ACSetTransformation) = α.codom diff --git a/src/categorical_algebra/FinCats.jl b/src/categorical_algebra/FinCats.jl index 5d6559795..c2bbf709a 100644 --- a/src/categorical_algebra/FinCats.jl +++ b/src/categorical_algebra/FinCats.jl @@ -678,13 +678,13 @@ end function Categories.do_composeH(F::FinDomFunctorMap, β::Transformation) G, H = dom(β), codom(β) FinTransformationMap(mapvals(c -> component(β, c), F.ob_map), - compose(F, G, strict=false), compose(F, H, strict=false)) + compose(F, G), compose(F, H)) end function Categories.do_composeH(α::FinTransformationMap, H::Functor) F, G = dom(α), codom(α) FinTransformationMap(mapvals(f->hom_map(H,f),α.components), - compose(F, H, strict=false), compose(G, H, strict=false)) + compose(F, H), compose(G, H)) end function Base.show(io::IO, α::FinTransformationMap) diff --git a/src/categorical_algebra/Limits.jl b/src/categorical_algebra/Limits.jl index adfdcc6e7..ede533bc5 100644 --- a/src/categorical_algebra/Limits.jl +++ b/src/categorical_algebra/Limits.jl @@ -301,7 +301,7 @@ products have been implemented following the limits interface. macro cartesian_monoidal_instance(Ob, Hom) thcc = ThCartesianCategory.Meta.theory instance_body = quote - @import Ob, Hom, dom, codom, compose, ⋅, id, munit, delete, pair + @import Ob, Hom, →, dom, codom, compose, ⋅, id, munit, delete, pair otimes(A::$Ob, B::$Ob) = ob(product(A, B)) @@ -319,6 +319,7 @@ macro cartesian_monoidal_instance(Ob, Hom) proj1(A::$Ob, B::$Ob) = proj1(product(A, B)) proj2(A::$Ob, B::$Ob) = proj2(product(A, B)) end + instance_code = ModelInterface.generate_instance( ThCartesianCategory.Meta.theory, ThCartesianCategory, @@ -328,6 +329,7 @@ macro cartesian_monoidal_instance(Ob, Hom) instance_body; escape=false ) + esc(quote import Catlab.Theories: ThCartesianCategory, otimes, ⊗, munit, braid, σ, mcopy, delete, pair, proj1, proj2, Δ, ◊ diff --git a/src/categorical_algebra/Slices.jl b/src/categorical_algebra/Slices.jl index 021c49e1a..661f2af48 100644 --- a/src/categorical_algebra/Slices.jl +++ b/src/categorical_algebra/Slices.jl @@ -67,9 +67,7 @@ end dom(f::SliceHom) = f.dom codom(f::SliceHom) = f.codom id(A::Slice) = SliceHom(A, A, id(dom(A.slice))) - function compose(f::SliceHom, g::SliceHom; strict::Bool=false) - !strict || codom(f) == dom(g) || - error("Domain mismatch in composition: $(codom(f)) != $(dom(g))") + function compose(f::SliceHom, g::SliceHom) SliceHom(dom(f), codom(g), compose(f.f, g.f)) end end diff --git a/src/graphics/GraphvizCategories.jl b/src/graphics/GraphvizCategories.jl index bea2c06ee..0e0eac1ad 100644 --- a/src/graphics/GraphvizCategories.jl +++ b/src/graphics/GraphvizCategories.jl @@ -3,7 +3,8 @@ module GraphvizCategories export to_graphviz, to_graphviz_property_graph -using ...GATs, ...Theories, ...CategoricalAlgebra, ...Graphs, ..GraphvizGraphs +using GATlab +using ...Theories, ...CategoricalAlgebra, ...Graphs, ..GraphvizGraphs import ..Graphviz import ..GraphvizGraphs: to_graphviz, to_graphviz_property_graph @@ -40,11 +41,11 @@ end # Categories ############ -function to_graphviz_property_graph(pres::Presentation{ThCategory}; kw...) +function to_graphviz_property_graph(pres::Presentation{ThCategory.Meta.T}; kw...) to_graphviz_property_graph(pres, :Ob, :Hom; kw...) end -function to_graphviz_property_graph(pres::Presentation{ThMCategory}; +function to_graphviz_property_graph(pres::Presentation{ThMCategory.Meta.T}; tight_attrs::AbstractDict=Dict(:arrowhead => "empty"), kw...) pg = to_graphviz_property_graph(pres, :Ob, :Hom; kw...) for tight_hom in generators(pres, :Tight) @@ -106,7 +107,7 @@ end # Schemas ######### -function to_graphviz_property_graph(pres::Presentation{ThSchema}; kw...) +function to_graphviz_property_graph(pres::Presentation{ThSchema.Meta.T}; kw...) pg = to_graphviz_property_graph(pres, :Ob, :Hom; kw...) ob_vertices = vertices(pg) hom_edges = edges(pg) diff --git a/src/graphics/TikZWiringDiagrams.jl b/src/graphics/TikZWiringDiagrams.jl index 4b2e060e3..0c06ec5e3 100644 --- a/src/graphics/TikZWiringDiagrams.jl +++ b/src/graphics/TikZWiringDiagrams.jl @@ -6,7 +6,7 @@ export to_tikz, layout_to_tikz using DataStructures: OrderedDict using MLStyle: @match -using ...GATs: GATExpr, show_latex +using GATlab: GATExpr, show_latex using ...WiringDiagrams, ...WiringDiagrams.WiringDiagramSerialization using ..WiringDiagramLayouts using ..WiringDiagramLayouts: AbstractVector2D, Vector2D, BoxLayout, box_label, diff --git a/src/graphics/WiringDiagramLayouts.jl b/src/graphics/WiringDiagramLayouts.jl index fec4756b8..59e59f69a 100644 --- a/src/graphics/WiringDiagramLayouts.jl +++ b/src/graphics/WiringDiagramLayouts.jl @@ -16,7 +16,8 @@ using Logging using StaticArrays: StaticVector, SVector using Statistics: mean -using ...GATs, ...WiringDiagrams +using GATlab +using ...WiringDiagrams using ...Theories: ObExpr, HomExpr, dom, codom, compose, id, otimes, braid # Data types diff --git a/src/graphs/BasicGraphs.jl b/src/graphs/BasicGraphs.jl index 16dbf1d02..3556300f4 100644 --- a/src/graphs/BasicGraphs.jl +++ b/src/graphs/BasicGraphs.jl @@ -299,7 +299,7 @@ neighbors(g::AbstractSymmetricGraph, v::Int) = @inline outneighbors(g::AbstractSymmetricGraph, v::Int) = neighbors(g, v) @inline all_neighbors(g::AbstractSymmetricGraph, v::Int) = neighbors(g, v) -@generated is_directed(::Type{T}) where {S, T<:StructACSet{S}} = :inv ∉ homs(S) +@generated is_directed(::Type{T}) where {S, T<:StructACSet{S}} = (:inv, :E, :E) ∉ homs(S) # Reflexive graphs ################## diff --git a/src/programs/GenerateJuliaPrograms.jl b/src/programs/GenerateJuliaPrograms.jl index f89cd4ddd..fea425139 100644 --- a/src/programs/GenerateJuliaPrograms.jl +++ b/src/programs/GenerateJuliaPrograms.jl @@ -7,7 +7,9 @@ export Block, CompileState, compile, compile_expr, compile_block, using GeneralizedGenerated: mk_function using ...Catlab -import ...GATs.MetaUtils: Expr0, concat_expr +using GATlab +import GATlab: compile +import GATlab.Util.MetaUtils: Expr0, concat_expr using ...Theories: ObExpr, HomExpr, dom, codom # Compilation diff --git a/src/programs/ParseJuliaPrograms.jl b/src/programs/ParseJuliaPrograms.jl index 81e0ff1a2..8b19c75dd 100644 --- a/src/programs/ParseJuliaPrograms.jl +++ b/src/programs/ParseJuliaPrograms.jl @@ -6,8 +6,8 @@ export @program, parse_wiring_diagram using GeneralizedGenerated: mk_function using MLStyle: @match -using ...GATs -import ...GATs.MetaUtils: Expr0 +using GATlab +import GATlab.Util.MetaUtils: Expr0 using ...Theories: ObExpr, HomExpr, otimes, munit using ...WiringDiagrams using ..GenerateJuliaPrograms: make_return_value @@ -109,15 +109,15 @@ end """ Make a lookup table assigning names to generators or term constructors. """ function make_lookup_table(pres::Presentation, syntax_module::Module, names) - theory = GATs.theory(syntax_module.theory()) - terms = Set([ term.name for term in theory.terms ]) + theory = syntax_module.THEORY_MODULE.Meta.theory + terms = Set(nameof.(keys(theory.resolvers))) table = Dict{Symbol,Any}() for name in names if has_generator(pres, name) table[name] = generator(pres, name) elseif name in terms - table[name] = (args...) -> invoke_term(syntax_module, name, args...) + table[name] = (args...) -> invoke_term(syntax_module, name, args) end end table @@ -129,7 +129,7 @@ function eval_type_expr(pres::Presentation, syntax_module::Module, expr::Expr0) function _eval_type_expr(expr) @match expr begin Expr(:curly, name, args...) => - invoke_term(syntax_module, name, map(_eval_type_expr, args)...) + invoke_term(syntax_module, name, map(_eval_type_expr, args)) name::Symbol => generator(pres, name) _ => error("Invalid type expression $expr") end diff --git a/src/programs/RelationalPrograms.jl b/src/programs/RelationalPrograms.jl index effe42b94..a462b0acd 100644 --- a/src/programs/RelationalPrograms.jl +++ b/src/programs/RelationalPrograms.jl @@ -8,7 +8,8 @@ export RelationDiagram, UntypedRelationDiagram, TypedRelationDiagram, using MLStyle: @match -using ...GATs, ...CategoricalAlgebra.CSets +using GATlab +using ...CategoricalAlgebra.CSets using ...WiringDiagrams.UndirectedWiringDiagrams # Data types diff --git a/src/theories/Theories.jl b/src/theories/Theories.jl index 51fcc6677..c77cecab5 100644 --- a/src/theories/Theories.jl +++ b/src/theories/Theories.jl @@ -11,6 +11,7 @@ import AlgebraicInterfaces: dom, codom, compose, id, Ob, ob, Hom, hom, munit, mcompose, ocompose, oapply, attr, attrtype using GATlab +import GATlab: show_unicode, show_latex """ Base type for GAT expressions in categories or other categorical structures. diff --git a/src/wiring_diagrams/CPortGraphs.jl b/src/wiring_diagrams/CPortGraphs.jl index 03039ed46..d6fce927f 100644 --- a/src/wiring_diagrams/CPortGraphs.jl +++ b/src/wiring_diagrams/CPortGraphs.jl @@ -3,7 +3,8 @@ export SchCPortGraph, SchOpenCPortGraph, SchSymCPortGraph, SchOpenSymCPortGraph, CPortGraph, OpenCPortGraph, SymCPortGraph, OpenSymCPortGraph, ThBundledCPG, BundledCPG -using ...GATs, ...Theories, ...CategoricalAlgebra, ...Graphs +using GATlab +using ...Theories, ...CategoricalAlgebra, ...Graphs import ...CategoricalAlgebra: migrate! import ..DirectedWiringDiagrams: ocompose diff --git a/src/wiring_diagrams/Directed.jl b/src/wiring_diagrams/Directed.jl index 0cb8cc38b..e7b8d2b99 100644 --- a/src/wiring_diagrams/Directed.jl +++ b/src/wiring_diagrams/Directed.jl @@ -30,7 +30,8 @@ export AbstractBox, Box, WiringDiagram, SchWiringDiagram, using StructEquality -using ...GATs, ...Graphs.BasicGraphs, ...CategoricalAlgebra.CSets +using GATlab +using ...Graphs.BasicGraphs, ...CategoricalAlgebra.CSets import ...CategoricalAlgebra.HomSearch: is_isomorphic import ...CategoricalAlgebra.FinCats: graph import ...Graphs: all_neighbors, neighbors, outneighbors, inneighbors @@ -721,6 +722,7 @@ function singleton_diagram(T::Type, box::AbstractBox) return d end singleton_diagram(box::AbstractBox) = singleton_diagram(Any, box) +singleton_diagram(m::Module, box::AbstractBox) = singleton_diagram(m.Meta.T, box) """ The wiring diagram induced by a subset of its boxes. diff --git a/src/wiring_diagrams/Expressions.jl b/src/wiring_diagrams/Expressions.jl index 4f19888ed..f745207ee 100644 --- a/src/wiring_diagrams/Expressions.jl +++ b/src/wiring_diagrams/Expressions.jl @@ -12,8 +12,8 @@ go the other way around. module WiringDiagramExpressions export to_ob_expr, to_hom_expr, to_wiring_diagram, to_undirected_wiring_diagram -using ...GATs, ...Theories, ...CategoricalAlgebra -using ...GATs.SyntaxSystems: syntax_module +using GATlab, ...Theories, ...CategoricalAlgebra +using GATlab.Models.SymbolicModels: syntax_module using ...Graphs, ..DirectedWiringDiagrams, ..UndirectedWiringDiagrams, ..MonoidalDirectedWiringDiagrams, ..MonoidalUndirectedWiringDiagrams using ..WiringDiagramAlgorithms: crossing_minimization_by_sort @@ -25,7 +25,7 @@ using ..DirectedWiringDiagrams: WiringDiagramGraph """ Convert a morphism expression into a wiring diagram. """ function to_wiring_diagram(expr::GATExpr, args...) - T = syntax_module(expr).theory() + T = syntax_module(expr).THEORY_MODULE.Meta.T to_wiring_diagram(T, expr, args...) end function to_wiring_diagram(T::Type, expr::GATExpr) @@ -118,13 +118,13 @@ end to_hom_expr(::Type{Ob}, ::Type{Hom}, box::Box{<:Hom}) where {Ob,Hom} = box.value -function to_hom_expr(Ob::Type, Hom::Type, box::Box) +function to_hom_expr(Ob::Type, HomT::Type, box::Box) dom = otimes(to_ob_exprs(Ob, input_ports(box))) codom = otimes(to_ob_exprs(Ob, output_ports(box))) Hom(box.value, dom, codom) end function to_hom_expr(Ob::Type, Hom::Type, op::BoxOp) - invoke_term(parentmodule(Hom), head(op), to_hom_expr(Ob, Hom, op.box)) + invoke_term(parentmodule(Hom), head(op), [to_hom_expr(Ob, Hom, op.box)]) end function to_hom_expr(Ob::Type, Hom::Type, junction::Junction) junction_to_expr(Ob, junction) @@ -261,11 +261,11 @@ end """ to_ob_expr(Syntax::Module, x) = to_ob_expr(Syntax.Ob, x) to_ob_expr(::Type{Ob}, ob::Ob) where Ob = ob -to_ob_expr(Ob::Type, value) = Ob(Ob, value) +to_ob_expr(T::Type, value) = Ob(T, value) to_ob_expr(Ob::Type, ports::Ports) = otimes(to_ob_exprs(Ob, ports)) to_ob_expr(Ob::Type, op::PortOp) = - invoke_term(parentmodule(Ob), head(op), to_ob_expr(Ob, op.value)) + invoke_term(parentmodule(Ob), head(op), [to_ob_expr(Ob, op.value)]) to_ob_exprs(Ob::Type, values) = Ob[ to_ob_expr(Ob, value) for value in values ] diff --git a/src/wiring_diagrams/MonoidalDirected.jl b/src/wiring_diagrams/MonoidalDirected.jl index e84557fda..6997fe272 100644 --- a/src/wiring_diagrams/MonoidalDirected.jl +++ b/src/wiring_diagrams/MonoidalDirected.jl @@ -13,8 +13,9 @@ export Ports, Junction, PortOp, BoxOp, functor, permute, using StructEquality -using ...GATs, ...Theories -import ...GATs: functor, head +using GATlab +using ...Theories +import GATlab: functor, head import ...Theories: dom, codom, id, compose, ⋅, ∘, otimes, ⊗, munit, braid, σ, oplus, ⊕, mzero, swap, mcopy, delete, Δ, ◊, mmerge, create, ∇, □, dual, dunit, dcounit, mate, dagger, @@ -241,32 +242,32 @@ cozero(A::Ports) = coplus(A, 0) # Cartesian category #------------------- -mcopy(A::Ports{ThMonoidalCategoryWithDiagonals}, n::Int) = implicit_mcopy(A, n) +mcopy(A::Ports{ThMonoidalCategoryWithDiagonals.Meta.T}, n::Int) = implicit_mcopy(A, n) -mcopy(A::Ports{ThCartesianCategory}, n::Int) = implicit_mcopy(A, n) +mcopy(A::Ports{ThCartesianCategory.Meta.T}, n::Int) = implicit_mcopy(A, n) # Cocartesian category #--------------------- -mmerge(A::Ports{ThMonoidalCategoryWithCodiagonals}, n::Int) = implicit_mmerge(A, n) +mmerge(A::Ports{ThMonoidalCategoryWithCodiagonals.Meta.T}, n::Int) = implicit_mmerge(A, n) -mmerge(A::Ports{ThCocartesianCategory}, n::Int) = implicit_mmerge(A, n) +mmerge(A::Ports{ThCocartesianCategory.Meta.T}, n::Int) = implicit_mmerge(A, n) # Biproduct category #------------------- # The coherence laws relating diagonal to codiagonal do not hold for general # bidiagonals, so an explicit representation is needed. -mcopy(A::Ports{ThMonoidalCategoryWithBidiagonals}, n::Int) = junctioned_mcopy(A, n) -mmerge(A::Ports{ThMonoidalCategoryWithBidiagonals}, n::Int) = junctioned_mmerge(A, n) +mcopy(A::Ports{ThMonoidalCategoryWithBidiagonals.Meta.T}, n::Int) = junctioned_mcopy(A, n) +mmerge(A::Ports{ThMonoidalCategoryWithBidiagonals.Meta.T}, n::Int) = junctioned_mmerge(A, n) -mcopy(A::Ports{ThBiproductCategory}, n::Int) = implicit_mcopy(A, n) -mmerge(A::Ports{ThBiproductCategory}, n::Int) = implicit_mmerge(A, n) +mcopy(A::Ports{ThBiproductCategory.Meta.T}, n::Int) = implicit_mcopy(A, n) +mmerge(A::Ports{ThBiproductCategory.Meta.T}, n::Int) = implicit_mmerge(A, n) # Dagger category #---------------- -dagger(f::WiringDiagram{ThDaggerSymmetricMonoidalCategory}) = +dagger(f::WiringDiagram{ThDaggerSymmetricMonoidalCategory.Meta.T}) = functor(f, identity, dagger, contravariant=true) # Compact closed category @@ -275,18 +276,18 @@ dagger(f::WiringDiagram{ThDaggerSymmetricMonoidalCategory}) = junctioned_dunit(A::Ports) = junction_caps(A, otimes(dual(A),A)) junctioned_dcounit(A::Ports) = junction_cups(A, otimes(A,dual(A))) -dual(A::Ports{ThCompactClosedCategory}) = dual_ports(A) -dunit(A::Ports{ThCompactClosedCategory}) = junctioned_dunit(A) -dcounit(A::Ports{ThCompactClosedCategory}) = junctioned_dcounit(A) -mate(f::WiringDiagram{ThCompactClosedCategory}) = +dual(A::Ports{ThCompactClosedCategory.Meta.T}) = dual_ports(A) +dunit(A::Ports{ThCompactClosedCategory.Meta.T}) = junctioned_dunit(A) +dcounit(A::Ports{ThCompactClosedCategory.Meta.T}) = junctioned_dcounit(A) +mate(f::WiringDiagram{ThCompactClosedCategory.Meta.T}) = functor(f, dual, mate, contravariant=true, monoidal_contravariant=true) -dual(A::Ports{ThDaggerCompactCategory}) = dual_ports(A) -dunit(A::Ports{ThDaggerCompactCategory}) = junctioned_dunit(A) -dcounit(A::Ports{ThDaggerCompactCategory}) = junctioned_dcounit(A) -dagger(f::WiringDiagram{ThDaggerCompactCategory}) = +dual(A::Ports{ThDaggerCompactCategory.Meta.T}) = dual_ports(A) +dunit(A::Ports{ThDaggerCompactCategory.Meta.T}) = junctioned_dunit(A) +dcounit(A::Ports{ThDaggerCompactCategory.Meta.T}) = junctioned_dcounit(A) +dagger(f::WiringDiagram{ThDaggerCompactCategory.Meta.T}) = functor(f, identity, dagger, contravariant=true) -mate(f::WiringDiagram{ThDaggerCompactCategory}) = +mate(f::WiringDiagram{ThDaggerCompactCategory.Meta.T}) = functor(f, dual, mate, contravariant=true, monoidal_contravariant=true) # Traced monoidal category @@ -306,7 +307,7 @@ function trace(X::Ports, f::WiringDiagram; unsubstituted::Bool=false) unsubstituted ? h : substitute(h) end -const TracedMon = ThTracedMonoidalCategory +const TracedMon = ThTracedMonoidalCategory.Meta.T trace(X::Ports{TracedMon}, A::Ports{TracedMon}, B::Ports{TracedMon}, f::WiringDiagram{TracedMon}) = trace(X, f) @@ -314,7 +315,7 @@ trace(X::Ports{TracedMon}, A::Ports{TracedMon}, # Bicategory of relations #------------------------ -const BiRel = ThBicategoryRelations +const BiRel = ThBicategoryRelations.Meta.T mcopy(A::Ports{BiRel}, n::Int) = junctioned_mcopy(A, n) mmerge(A::Ports{BiRel}, n::Int) = junctioned_mmerge(A, n) @@ -332,7 +333,7 @@ top(A::Ports{BiRel}, B::Ports{BiRel}) = compose(delete(A), create(B)) # Abelian bicategory of relations #-------------------------------- -const AbBiRel = ThAbelianBicategoryRelations +const AbBiRel = ThAbelianBicategoryRelations.Meta.T # Additive notation. FIXME: Use @instance. oplus(f::WiringDiagram{AbBiRel}, g::WiringDiagram{AbBiRel}) = otimes(f,g) diff --git a/src/wiring_diagrams/MonoidalUndirected.jl b/src/wiring_diagrams/MonoidalUndirected.jl index d5b9c43ca..6415fd0d4 100644 --- a/src/wiring_diagrams/MonoidalUndirected.jl +++ b/src/wiring_diagrams/MonoidalUndirected.jl @@ -7,7 +7,7 @@ export HypergraphDiagram, SchUntypedHypergraphDiagram, SchHypergraphDiagram, using StructEquality -using ...GATs, ...CategoricalAlgebra.CSets +using GATlab, ...CategoricalAlgebra.CSets using ...Theories: ThHypergraphCategory import ...Theories: dom, codom, compose, id, ⋅, ∘, otimes, ⊗, munit, braid, σ, mcopy, Δ, mmerge, ∇, delete, ◊, create, □, dunit, dcounit, dagger diff --git a/src/wiring_diagrams/ScheduleUndirected.jl b/src/wiring_diagrams/ScheduleUndirected.jl index dec88c0de..4c7c27e0d 100644 --- a/src/wiring_diagrams/ScheduleUndirected.jl +++ b/src/wiring_diagrams/ScheduleUndirected.jl @@ -11,7 +11,7 @@ export AbstractNestedUWD, AbstractScheduledUWD, NestedUWD, ScheduledUWD, import Base: schedule using DataStructures: IntDisjointSets, union!, in_same_set -using ...GATs, ...CategoricalAlgebra.CSets, ...CategoricalAlgebra.FinSets +using GATlab, ...CategoricalAlgebra.CSets, ...CategoricalAlgebra.FinSets using ..UndirectedWiringDiagrams, ..WiringDiagramAlgebras using ..UndirectedWiringDiagrams: flat diff --git a/src/wiring_diagrams/Undirected.jl b/src/wiring_diagrams/Undirected.jl index 4e7b1f2e8..bedd89314 100644 --- a/src/wiring_diagrams/Undirected.jl +++ b/src/wiring_diagrams/Undirected.jl @@ -9,7 +9,8 @@ export UndirectedWiringDiagram, HasUWD, add_wires!, singleton_diagram, cospan_diagram, junction_diagram, ocompose, substitute -using ...GATs, ...CategoricalAlgebra.CSets, ...CategoricalAlgebra.Limits +using GATlab +using ...CategoricalAlgebra.CSets, ...CategoricalAlgebra.Limits using ...CategoricalAlgebra.FinSets: FinSet, FinFunction using ...Theories: dom, codom, compose, ⋅, id, oplus import ..DirectedWiringDiagrams: box, boxes, nboxes, add_box!, add_wire!, diff --git a/test/categorical_algebra/CSets.jl b/test/categorical_algebra/CSets.jl index 916b726c6..aa30a13c3 100644 --- a/test/categorical_algebra/CSets.jl +++ b/test/categorical_algebra/CSets.jl @@ -1,7 +1,8 @@ module TestCSets using Test -using Catlab.GATs, Catlab.Theories, Catlab.Graphs, Catlab.CategoricalAlgebra +using GATlab +using Catlab.Theories, Catlab.Graphs, Catlab.CategoricalAlgebra @present SchDDS(FreeSchema) begin X::Ob diff --git a/test/categorical_algebra/CatElements.jl b/test/categorical_algebra/CatElements.jl index 7ff985abc..edbf2480a 100644 --- a/test/categorical_algebra/CatElements.jl +++ b/test/categorical_algebra/CatElements.jl @@ -1,6 +1,7 @@ module TestCatElements using Test -using Catlab.GATs, Catlab.Theories, Catlab.Graphs, Catlab.CategoricalAlgebra +using GATlab +using Catlab.Theories, Catlab.Graphs, Catlab.CategoricalAlgebra arr = path_graph(Graph, 2) diff --git a/test/categorical_algebra/Chase.jl b/test/categorical_algebra/Chase.jl index 1b3b8c609..de9e0e00c 100644 --- a/test/categorical_algebra/Chase.jl +++ b/test/categorical_algebra/Chase.jl @@ -1,7 +1,8 @@ module TestChase using Test -using Catlab.GATs, Catlab.Theories, Catlab.CategoricalAlgebra, Catlab.Graphs +using GATlab +using Catlab.Theories, Catlab.CategoricalAlgebra, Catlab.Graphs using Catlab.CategoricalAlgebra.Chase: egd, tgd, crel_type, pres_to_eds, from_c_rel, collage diff --git a/test/categorical_algebra/FinSets.jl b/test/categorical_algebra/FinSets.jl index 27fb6591b..e2d8d0589 100644 --- a/test/categorical_algebra/FinSets.jl +++ b/test/categorical_algebra/FinSets.jl @@ -1,7 +1,8 @@ module TestFinSets using Test -using Catlab.GATs, Catlab.Theories, Catlab.CategoricalAlgebra +using GATlab +using Catlab.Theories, Catlab.CategoricalAlgebra using Catlab.CategoricalAlgebra.FinSets: VarSet sshow(args...) = sprint(show, args...) diff --git a/test/categorical_algebra/FunctorialDataMigrations.jl b/test/categorical_algebra/FunctorialDataMigrations.jl index 8ca0bda8c..a652b0c83 100644 --- a/test/categorical_algebra/FunctorialDataMigrations.jl +++ b/test/categorical_algebra/FunctorialDataMigrations.jl @@ -1,7 +1,8 @@ module TestFunctorialDataMigrations using Test -using Catlab.GATs, Catlab.Theories, Catlab.Graphs, Catlab.CategoricalAlgebra +using GATlab +using Catlab.Theories, Catlab.Graphs, Catlab.CategoricalAlgebra @present SchSet(FreeSchema) begin X::Ob diff --git a/test/categorical_algebra/StructuredCospans.jl b/test/categorical_algebra/StructuredCospans.jl index 4cb521637..af58ca16d 100644 --- a/test/categorical_algebra/StructuredCospans.jl +++ b/test/categorical_algebra/StructuredCospans.jl @@ -1,7 +1,8 @@ module TestStructuredCospans using Test -using Catlab.GATs, Catlab.Theories, Catlab.Graphs, Catlab.CategoricalAlgebra +using GATlab +using Catlab.Theories, Catlab.Graphs, Catlab.CategoricalAlgebra # Structured cospans of C-sets ############################## diff --git a/test/gats/GATs.jl b/test/gats/GATs.jl deleted file mode 100644 index 330f28080..000000000 --- a/test/gats/GATs.jl +++ /dev/null @@ -1,14 +0,0 @@ -using Test - -@testset "TheoriesInstances" begin - include("MetaUtils.jl") - include("TheoriesInstances.jl") -end - -@testset "SyntaxSystems" begin - include("SyntaxSystems.jl") -end - -@testset "Presentations" begin - include("Presentations.jl") -end diff --git a/test/gats/MetaUtils.jl b/test/gats/MetaUtils.jl deleted file mode 100644 index 7aa812d1e..000000000 --- a/test/gats/MetaUtils.jl +++ /dev/null @@ -1,56 +0,0 @@ -module TestMetaUtils -using Test - -using Base.Meta: ParseError - -using Catlab.GATs.MetaUtils - -strip_all(expr) = strip_lines(expr, recurse=true) -parse_fun(expr) = parse_function(strip_all(expr)) - -# Function generation -@test (generate_function(JuliaFunction(:(f(x,y)))) == - strip_all(:(function f(x,y) end))) -@test (generate_function(JuliaFunction(:(f(x::Int,y::Int)), :Int)) == - strip_all(:(function f(x::Int,y::Int)::Int end))) -@test (generate_function(JuliaFunction(:(f(x)), :Bool, :(isnothing(x)))) == - strip_all(:(function f(x)::Bool isnothing(x) end))) - -fun_with_docstring_expr = quote - """Is nothing""" - function f(x)::Bool - isnothing(x) - end -end -@test (strip_all(generate_function( - JuliaFunction(:(f(x)), :Bool, :(isnothing(x)), "Is nothing"))) == - strip_all(fun_with_docstring_expr).args[1]) - -# Function parsing -@test_throws ParseError parse_fun(:(f(x,y))) -@test (parse_fun(:(function f(x,y) x end)) == - JuliaFunction(:(f(x,y)), nothing, quote x end)) - -@test parse_fun((quote - """ My docstring - """ - function f(x,y) x end -end).args[1]) == JuliaFunction(:(f(x,y)), nothing, quote x end, " My docstring\n") - -@test (parse_fun(:(function f(x::Int,y::Int)::Int x end)) == - JuliaFunction(:(f(x::Int,y::Int)), :Int, quote x end)) - -@test (parse_fun(:(f(x,y) = x)) == - JuliaFunction(:(f(x,y)), nothing, quote x end)) - -sig = JuliaFunctionSig(:f, [:Int,:Int]) -@test parse_function_sig(:(f(x::Int,y::Int))) == sig -@test parse_function_sig(:(f(::Int,::Int))) == sig -@test parse_function_sig(:(f(x,y))) == JuliaFunctionSig(:f, [:Any,:Any]) - -# Type transformations -bindings = Dict((:r => :R, :s => :S, :t => :T)) -@test replace_symbols(bindings, :(foo(x::r,y::s)::t)) == :(foo(x::R,y::S)::T) -@test replace_symbols(bindings, :(foo(xs::Vararg{r}))) == :(foo(xs::Vararg{R})) - -end diff --git a/test/gats/Presentations.jl b/test/gats/Presentations.jl deleted file mode 100644 index 9c04890f0..000000000 --- a/test/gats/Presentations.jl +++ /dev/null @@ -1,155 +0,0 @@ -module TestPresentations - -using Test -using Catlab.GATs, Catlab.Theories - -presentation_theory(::Presentation{Theory}) where Theory = Theory - -# Presentation -############## - -A, B, C = Ob(FreeCategory, :A, :B, :C) -f, g, h = Hom(:f, A, B), Hom(:g, B, C), Hom(:h, A, C) - -# Generators -pres = Presentation(FreeCategory) -@test presentation_theory(pres) == ThCategory -@test !has_generator(pres, :A) -add_generator!(pres, A) -@test generators(pres) == [A] -@test generator(pres, :A) == A -@test has_generator(pres, :A) -add_generator!(pres, B) -@test generators(pres) == [A, B] -@test_throws Exception add_generator!(pres, A) -@test pres[:A] == A -@test pres[[:A,:B]] == [A, B] -@test generator_index(pres, :B) == 2 -@test generator_index(pres, B) == 2 - -add_generators!(pres, (f,g)) -@test generators(pres) == [A, B, f, g] -@test generators(pres, :Ob) == [A, B] -@test generators(pres, :Hom) == [f, g] -@test generators(pres, FreeCategory.Ob) == [A, B] -@test generators(pres, FreeCategory.Hom) == [f, g] - -# Equations -add_equation!(pres, compose(f,g), h) -@test length(equations(pres)) == 1 - -f′, g′ = Hom(:f′, A, B), Hom(:g′, B, C) -add_generators!(pres, [f′, g′]) -add_equations!(pres, [f => f′, g => g′]) -@test length(equations(pres)) == 3 - -# Presentation macro -#################### - -@present Company(FreeCategory) begin - # Primitive concepts. - Employee::Ob - Department::Ob - Str::Ob - - first_name::Hom(Employee, Str) - last_name::Hom(Employee, Str) - manager::Hom(Employee, Employee) - works_in::Hom(Employee, Department) - secretary::Hom(Department, Employee) - - # Defined concepts. - second_level_manager := compose(manager, manager) - third_level_manager := compose(manager, manager, manager) - - # Managers work in the same department as their employees. - compose(manager, works_in) == works_in - # The secretary of a department works in that department. - compose(secretary, works_in) == id(Department) -end - -# Check type parameter. -@test presentation_theory(Company) == ThCategory - -# Check generators. -Employee, Department, Str = Ob(FreeCategory, :Employee, :Department, :Str) -@test generators(Company) == [ - Employee, - Department, - Str, - Hom(:first_name, Employee, Str), - Hom(:last_name, Employee, Str), - Hom(:manager, Employee, Employee), - Hom(:works_in, Employee, Department), - Hom(:secretary, Department, Employee), - Hom(:second_level_manager, Employee, Employee), - Hom(:third_level_manager, Employee, Employee), -] - -# Check equations. -manager = Hom(:manager, Employee, Employee) -works_in = Hom(:works_in, Employee, Department) -secretary = Hom(:secretary, Department, Employee) -@test equations(Company) == [ - Hom(:second_level_manager, Employee, Employee) => compose(manager, manager), - Hom(:third_level_manager, Employee, Employee) => compose(manager, manager, manager), - compose(manager, works_in) => works_in, - compose(secretary, works_in) => id(Department), -] - -# Generators with compound type arguments. -@present C(FreeSymmetricMonoidalCategory) begin - A::Ob - B::Ob - f::Hom(otimes(A,B),otimes(B,A)) - scalar::Hom(munit(),munit()) # Nullary case. -end -A, B = Ob(FreeSymmetricMonoidalCategory, :A, :B) -I = munit(FreeSymmetricMonoidalCategory.Ob) -@test generator(C, :f) == Hom(:f, otimes(A,B), otimes(B,A)) -@test generator(C, :scalar) == Hom(:scalar, I, I) - -# Inheritance. -@present SchSet(FreeCategory) begin - X::Ob -end -@present SchDDS <: SchSet begin - Φ::Hom(X,X) -end -X = Ob(FreeCategory, :X) -Φ = Hom(:Φ, X, X) -@test generators(SchDDS, :Ob) == [X] -@test generators(SchDDS, :Hom) == [Φ] - -# Abbreviated syntax. -@present SchGraph(FreeCategory) begin - V::Ob - E::Ob - src::Hom(E,V) - tgt::Hom(E,V) -end -@present SchGraph′(FreeCategory) begin - (V, E)::Ob - (src, tgt)::Hom(E,V) -end -@test SchGraph == SchGraph′ - -# Serialization -############### - -to_json(expr) = to_json_sexpr(Company, expr) -from_json(sexpr) = parse_json_sexpr(Company, FreeCategory, sexpr) - -# To JSON -to_json(generator(Company, :Employee)) == "Employee" -to_json(generator(Company, :manager)) == "manager" -to_json(compose(generator(Company, :manager), generator(Company, :manager))) == - ["compose", "manager", "manager"] - -# From JSON -@test from_json("Employee") == generator(Company, :Employee) -@test from_json("manager") == generator(Company, :manager) -@test from_json(["compose", "manager", "manager"]) == - compose(generator(Company, :manager), generator(Company, :manager)) - -end diff --git a/test/gats/SyntaxSystems.jl b/test/gats/SyntaxSystems.jl deleted file mode 100644 index fe04cb869..000000000 --- a/test/gats/SyntaxSystems.jl +++ /dev/null @@ -1,197 +0,0 @@ -""" Test the SyntaxSystems module. - -The unit tests are sparse because many of the Theory module tests are -effectively just tests of the SyntaxSystems module. -""" -module TestSyntaxSystems - -using Test -using Catlab.GATs - -# Monoid -######## - -""" Theory of monoids. -""" -@signature ThMonoid{Elem} begin - Elem::TYPE - munit()::Elem - mtimes(x::Elem,y::Elem)::Elem -end - -""" Syntax for the theory of monoids. -""" -@syntax FreeMonoid ThMonoid - -Elem(mod::Module, args...) = Elem(mod.Elem, args...) - -@test isa(FreeMonoid, Module) -@test occursin("theory of monoids", string(Docs.doc(FreeMonoid))) -@test sort(names(FreeMonoid)) == sort([:FreeMonoid, :Elem]) - -x, y, z = Elem(FreeMonoid,:x), Elem(FreeMonoid,:y), Elem(FreeMonoid,:z) -@test isa(mtimes(x,y), FreeMonoid.Elem) -@test isa(munit(FreeMonoid.Elem), FreeMonoid.Elem) -@test gat_typeof(x) == :Elem -@test gat_typeof(mtimes(x,y)) == :Elem -@test mtimes(mtimes(x,y),z) != mtimes(x,mtimes(y,z)) - -# Test equality -@test x == Elem(FreeMonoid,:x) -@test x != y -@test Elem(FreeMonoid,"X") == Elem(FreeMonoid,"X") -@test Elem(FreeMonoid,"X") != Elem(FreeMonoid,"Y") - -# Test hash -@test hash(x) == hash(x) -@test hash(x) != hash(y) -@test hash(mtimes(x,y)) == hash(mtimes(x,y)) -@test hash(mtimes(x,y)) != hash(mtimes(x,z)) - -@syntax FreeMonoidAssoc ThMonoid begin - mtimes(x::Elem, y::Elem) = associate(new(x,y)) -end - -x, y, z = [ Elem(FreeMonoidAssoc,sym) for sym in [:x,:y,:z] ] -e = munit(FreeMonoidAssoc.Elem) -@test mtimes(mtimes(x,y),z) == mtimes(x,mtimes(y,z)) -@test mtimes(e,x) != x && mtimes(x,e) != x - -@syntax FreeMonoidAssocUnit ThMonoid begin - mtimes(x::Elem, y::Elem) = associate_unit(new(x,y), munit) -end - -x, y, z = [ Elem(FreeMonoidAssocUnit,sym) for sym in [:x,:y,:z] ] -e = munit(FreeMonoidAssocUnit.Elem) -@test mtimes(mtimes(x,y),z) == mtimes(x,mtimes(y,z)) -@test mtimes(e,x) == x && mtimes(x,e) == x - -abstract type MonoidExpr{T} <: GATExpr{T} end -@syntax FreeMonoidTyped{MonoidExpr} ThMonoid - -x = Elem(FreeMonoidTyped.Elem, :x) -@test FreeMonoidTyped.Elem <: MonoidExpr -@test isa(x, FreeMonoidTyped.Elem) && isa(x, MonoidExpr) - -@signature ThMonoidNumeric{Elem} <: ThMonoid{Elem} begin - elem_int(x::Int)::Elem -end -@syntax FreeMonoidNumeric ThMonoidNumeric - -x = elem_int(FreeMonoidNumeric.Elem, 1) -@test isa(x, FreeMonoidNumeric.Elem) -@test first(x) == 1 - -""" A monoid with two distinguished elements. -""" -@signature ThMonoidTwo{Elem} <: ThMonoid{Elem} begin - one()::Elem - two()::Elem -end - -""" The free monoid on two generators. -""" -@syntax FreeMonoidTwo ThMonoidTwo begin - Elem(::Type{Elem}, value) = error("No extra generators allowed!") -end - -x, y = one(FreeMonoidTwo.Elem), two(FreeMonoidTwo.Elem) -@test all(isa(expr, FreeMonoidTwo.Elem) for expr in [x, y, mtimes(x,y)]) -@test_throws ErrorException Elem(FreeMonoidTwo, :x) - -# Category -########## - -@signature ThCategory{Ob,Hom} begin - Ob::TYPE - Hom(dom::Ob, codom::Ob)::TYPE - - id(X::Ob)::Hom(X,X) - compose(f::Hom(X,Y), g::Hom(Y,Z))::Hom(X,Z) ⊣ (X::Ob, Y::Ob, Z::Ob) -end - -@syntax FreeCategory ThCategory begin - compose(f::Hom, g::Hom) = associate(new(f,g)) -end - -@test isa(FreeCategory, Module) -@test sort(names(FreeCategory)) == sort([:FreeCategory, :Ob, :Hom]) - -X, Y, Z, W = [ Ob(FreeCategory.Ob, sym) for sym in [:X, :Y, :Z, :W] ] -f, g, h = Hom(:f, X, Y), Hom(:g, Y, Z), Hom(:h, Z, W) -@test isa(X, FreeCategory.Ob) && isa(f, FreeCategory.Hom) -@test_throws MethodError FreeCategory.Hom(:f) -@test dom(f) == X -@test codom(f) == Y - -@test isa(id(X), FreeCategory.Hom) -@test dom(id(X)) == X -@test codom(id(X)) == X - -@test isa(compose(f,g), FreeCategory.Hom) -@test dom(compose(f,g)) == X -@test codom(compose(f,g)) == Z -@test isa(compose(f,f), FreeCategory.Hom) # Doesn't check domains. -@test compose(compose(f,g),h) == compose(f,compose(g,h)) - -@syntax FreeCategoryStrict ThCategory begin - compose(f::Hom, g::Hom) = associate(new(f,g; strict=true)) -end - -X, Y = Ob(FreeCategoryStrict.Ob, :X), Ob(FreeCategoryStrict.Ob, :Y) -f, g = Hom(:f, X, Y), Hom(:g, Y, X) - -@test isa(compose(f,g), FreeCategoryStrict.Hom) -@test_throws SyntaxDomainError compose(f,f) - -# Functor -######### - -@instance ThMonoid{String} begin - munit(::Type{String}) = "" - mtimes(x::String, y::String) = string(x,y) -end - -F(expr; kw...) = functor((String,), expr; kw...) - -x, y, z = Elem(FreeMonoid,:x), Elem(FreeMonoid,:y), Elem(FreeMonoid,:z) -gens = Dict(x => "x", y => "y", z => "z") -@test F(mtimes(x,mtimes(y,z)); generators=gens) == "xyz" -@test F(mtimes(x,munit(FreeMonoid.Elem)); generators=gens) == "x" - -terms = Dict(:Elem => (x) -> string(first(x))) -@test F(mtimes(x,mtimes(y,z)); terms=terms) == "xyz" -@test F(mtimes(x,munit(FreeMonoid.Elem)); terms=terms) == "x" - -# Serialization -############### - -# To JSON -X, Y, Z = [ Ob(FreeCategory.Ob, sym) for sym in [:X, :Y, :Z] ] -f = Hom(:f, X, Y) -g = Hom(:g, Y, Z) -@test to_json_sexpr(X) == ["Ob", "X"] -@test to_json_sexpr(f) == ["Hom", "f", ["Ob", "X"], ["Ob", "Y"]] -@test to_json_sexpr(compose(f,g)) == [ - "compose", - ["Hom", "f", ["Ob", "X"], ["Ob", "Y"]], - ["Hom", "g", ["Ob", "Y"], ["Ob", "Z"]], -] -@test to_json_sexpr(f, by_reference=x->true) == "f" -@test to_json_sexpr(compose(f,g), by_reference=x->true) == ["compose","f","g"] - -# From JSON -@test parse_json_sexpr(FreeMonoid, [:Elem, "x"]) == Elem(FreeMonoid, :x) -@test parse_json_sexpr(FreeMonoid, [:munit]) == munit(FreeMonoid.Elem) -@test parse_json_sexpr(FreeCategory, ["Ob", "X"]) == X -@test parse_json_sexpr(FreeCategory, ["Ob", "X"]; symbols=false) == - Ob(FreeCategory.Ob, "X") -@test parse_json_sexpr(FreeCategory, ["Hom", "f", ["Ob", "X"], ["Ob", "Y"]]) == f -@test parse_json_sexpr(FreeCategory, ["Hom", "f", ["Ob", "X"], ["Ob", "Y"]]; symbols=false) == - Hom("f", Ob(FreeCategory.Ob, "X"), Ob(FreeCategory.Ob, "Y")) - -# Round trip -@test parse_json_sexpr(FreeCategory, to_json_sexpr(compose(f,g))) == compose(f,g) -@test parse_json_sexpr(FreeCategory, to_json_sexpr(id(X))) == id(X) - -end diff --git a/test/gats/TheoriesInstances.jl b/test/gats/TheoriesInstances.jl deleted file mode 100644 index 92528d336..000000000 --- a/test/gats/TheoriesInstances.jl +++ /dev/null @@ -1,286 +0,0 @@ -module TestTheoriesInstances - -using Base.Meta: ParseError -using Test -using DataStructures: OrderedDict - -using Catlab.GATs -import Catlab.TheoriesInstances as GAT - -# GAT expressions -################# - -# Raw expressions -@test GAT.parse_raw_expr(:(Ob)) == :Ob -@test GAT.parse_raw_expr(:(Hom(X,Y))) == :(Hom(X,Y)) -@test_throws ParseError GAT.parse_raw_expr(:("Ob")) -@test_throws ParseError GAT.parse_raw_expr(:(Hom(X,0))) - -# Contexts -@test (GAT.parse_context(:((X::Ob, Y::Ob))) == - GAT.Context((:X => :Ob, :Y => :Ob))) -@test (GAT.parse_context(:((X::Ob, Y::Ob, f::Hom(X,Y)))) == - GAT.Context((:X => :Ob, :Y => :Ob, :f => :(Hom(X,Y))))) -@test GAT.parse_context(:(())) == GAT.Context() -@test_throws ParseError GAT.parse_context(:((X::Ob, X::Ob))) # Repeat variables - -# Type constructor -expr = :(Ob::TYPE) -cons = GAT.TypeConstructor(:Ob, [], GAT.Context()) -@test GAT.parse_constructor(expr) == cons - -expr = (quote "Object" Ob::TYPE end).args[2] -cons = GAT.TypeConstructor(:Ob, [], GAT.Context(), "Object") -@test GAT.parse_constructor(expr) == cons - -expr = :(Hom(X,Y)::TYPE ⊣ (X::Ob, Y::Ob)) -context = GAT.Context((:X => :Ob, :Y => :Ob)) -cons = GAT.TypeConstructor(:Hom, [:X,:Y], context) -@test GAT.parse_constructor(expr) == cons - -# Term constructor -expr = :(unit()::Ob) -cons = GAT.TermConstructor(:unit, [], :Ob, GAT.Context()) -@test GAT.parse_constructor(expr) == cons - -expr = (quote "Monoidal unit" munit()::Ob end).args[2] -cons = GAT.TermConstructor(:munit, [], :Ob, GAT.Context(), "Monoidal unit") -@test GAT.parse_constructor(expr) == cons - -cons = GAT.TermConstructor(:id, [:X], :(Hom(X,X)), GAT.Context(:X => :Ob)) -@test GAT.parse_constructor(:(id(X)::Hom(X,X) ⊣ (X::Ob))) == cons -@test GAT.parse_constructor(:(id(X::Ob)::Hom(X,X))) == cons - -expr = :(compose(f,g)::Hom(X,Z) ⊣ (X::Ob, Y::Ob, Z::Ob, f::Hom(X,Y), g::Hom(Y,Z))) -context = GAT.Context((:X => :Ob, :Y => :Ob, :Z => :Ob, - :f => :(Hom(X,Y)), :g => :(Hom(Y,Z)))) -cons = GAT.TermConstructor(:compose, [:f,:g], :(Hom(X,Z)), context) -@test GAT.parse_constructor(expr) == cons -expr = :(compose(f::Hom(X,Y), g::Hom(Y,Z))::Hom(X,Z) ⊣ (X::Ob, Y::Ob, Z::Ob)) -@test GAT.parse_constructor(expr) == cons - -# Type transformations -bindings = Dict((:Ob => :Obj, :Hom => :Mor)) -cons = GAT.TypeConstructor(:Hom, [:X,:Y], - GAT.Context((:X => :Ob, :Y => :Ob))) -target = GAT.TypeConstructor(:Mor, [:X,:Y], - GAT.Context((:X => :Obj, :Y => :Obj))) -@test GAT.replace_types(bindings, cons) == target - -cons = GAT.TermConstructor(:compose, [:f,:g], :(Hom(X,Z)), - GAT.Context((:X => :Ob, :Y => :Ob, :Z => :Ob, - :f => :(Hom(X,Y)), :g => :(Hom(Y,Z))))) -target = GAT.TermConstructor(:compose, [:f,:g], :(Mor(X,Z)), - GAT.Context((:X => :Obj, :Y => :Obj, :Z => :Obj, - :f => :(Mor(X,Y)), :g => :(Mor(Y,Z))))) -@test GAT.replace_types(bindings, cons) == target - -cons = GAT.AxiomConstructor(:(==), Meta.parse("compose(compose(f,g),Hom(C,D))"), - Meta.parse("compose(f,compose(g,Hom(C,D)))"), - GAT.Context((:A => :Ob, :B => :Ob, :C => :Ob, :D => :Ob, - :f => :(Hom(A,B)), :g => :(Hom(B,C))))) -target = GAT.AxiomConstructor(:(==), Meta.parse("compose(compose(f,g),Mor(C,D))"), - Meta.parse("compose(f,compose(g,Mor(C,D)))"), - GAT.Context((:A => :Obj, :B => :Obj, :C => :Obj, :D => :Obj, - :f => :(Mor(A,B)), :g => :(Mor(B,C))))) -@test GAT.replace_types(bindings, cons) == target - -cons = Dict(:→ => :Hom) -target = Dict(:→ => :Mor) -@test GAT.replace_types(bindings, cons) == target - -@test GAT.strip_type(:Ob) == :Ob -@test GAT.strip_type(:(Hom(X,Y))) == :Hom -@test GAT.strip_type(:(Hom(dual(X),dual(Y)))) == :Hom - -# Theories -############ - -# This try-catch block is a necessary work around because of a current bug -# where test_throws doesn't catch errors thrown from inside of a macro -@test_throws ParseError try @eval @signature ThCategory{Ob,Hom} begin - Ob::TYPE - Hom(dom, codom)::TYPE ⊣ (dom::Ob, codom::Ob) - @op (→) := Hom - - id(X)::(X → X) ⊣ (X::Ob) - compose(f,g)::(X → Z) ⊣ (X::Ob, Y::Ob, Z::Ob, f::(X → Y), g::(Y → Z)) - @op (⋅) := compose - - (f ⋅ g) ⋅ h == f ⋅ (g ⋅ h) ⊣ ( A::Ob, B::Ob, C::Ob, D::Ob, - f::(A → B), g::(B → C), h::(C → D)) - f ⋅ id(B) == f ⊣ (A::Ob, B::Ob, f::(A → B)) - id(A) ⋅ f == f ⊣ (A::Ob, B::Ob, f::(A → B)) -end -catch err; - throw(err.error) -end - -""" Theory of categories -""" -@theory ThCategory{Ob,Hom} begin - Ob::TYPE - Hom(dom, codom)::TYPE ⊣ (dom::Ob, codom::Ob) - @op (→) := Hom - - id(X)::(X → X) ⊣ (X::Ob) - compose(f,g)::(X → Z) ⊣ (X::Ob, Y::Ob, Z::Ob, f::(X → Y), g::(Y → Z)) - @op (⋅) := compose - - (f ⋅ g) ⋅ h == f ⋅ (g ⋅ h) ⊣ ( A::Ob, B::Ob, C::Ob, D::Ob, - f::(A → B), g::(B → C), h::(C → D)) - f ⋅ id(B) == f ⊣ (A::Ob, B::Ob, f::(A → B)) - id(A) ⋅ f == f ⊣ (A::Ob, B::Ob, f::(A → B)) -end - -@test ThCategory isa Type -@test occursin("theory of categories", lowercase(string(Docs.doc(ThCategory)))) -@test isempty(methods(dom)) && isempty(methods(codom)) -@test isempty(methods(id)) && isempty(methods(compose)) - -# Manually constructed theory of categories -types = [ - GAT.TypeConstructor(:Ob, [], GAT.Context()), - GAT.TypeConstructor(:Hom, [:dom,:codom], - GAT.Context((:dom => :Ob, :codom => :Ob))), -] -terms = [ - GAT.TermConstructor(:id, [:X], :(Hom(X,X)), GAT.Context(:X => :Ob)), - GAT.TermConstructor(:compose, [:f,:g], :(Hom(X,Z)), - GAT.Context((:X => :Ob, :Y => :Ob, :Z => :Ob, - :f => :(Hom(X,Y)), :g => :(Hom(Y,Z))))), -] -axioms = [ - GAT.AxiomConstructor(:(==), Meta.parse("compose(compose(f,g),h)"), - Meta.parse("compose(f,compose(g,h))"), - GAT.Context((:A => :Ob, :B => :Ob, :C => :Ob, :D => :Ob, - :f => :(Hom(A,B)), :g => :(Hom(B,C)), :h => :(Hom(C,D))))), - GAT.AxiomConstructor(:(==), Meta.parse("compose(f,id(B))"), :f, - GAT.Context((:A => :Ob, :B => :Ob, :f => :(Hom(A,B))))), - GAT.AxiomConstructor(:(==), Meta.parse("compose(id(A),f)"), :f, - GAT.Context((:A => :Ob, :B => :Ob, :f => :(Hom(A,B))))), -] -aliases = Dict(:⋅ => :compose, :→ => :Hom) -category_theory = GAT.Theory(types, terms, axioms, aliases) - -@test GAT.theory(ThCategory) == category_theory - -""" Equivalent shorthand definition of Category theory -""" -@theory ThCategoryAbbrev{Ob,Hom} begin - @op begin - (→) := Hom - (⋅) := compose - end - - Ob::TYPE - Hom(dom::Ob, codom::Ob)::TYPE - - id(X::Ob)::(X → X) - (compose(f::(X → Y),g::(Y → Z))::(X → Z)) where (X::Ob, Y::Ob, Z::Ob) - - (f ⋅ g) ⋅ h == f ⋅ (g ⋅ h) ⊣ ( A::Ob, B::Ob, C::Ob, D::Ob, - f::(A → B), g::(B → C), h::(C → D)) - f ⋅ id(B) == f ⊣ (A::Ob, B::Ob, f::(A → B)) - id(A) ⋅ f == f ⊣ (A::Ob, B::Ob, f::(A → B)) -end - -@test GAT.theory(ThCategoryAbbrev) == category_theory - -# Methods for theory -accessors = [ GAT.JuliaFunction(:(dom(::Hom)), :Ob), - GAT.JuliaFunction(:(codom(::Hom)), :Ob) ] -constructors = [ GAT.JuliaFunction(:(id(X::Ob)), :Hom), - GAT.JuliaFunction(:(compose(f::Hom, g::Hom)), :Hom) ] -alias_functions = [ GAT.JuliaFunction(:(⋅(f::Hom, g::Hom)), :Hom, :(compose(f, g))) ] -theory = GAT.theory(ThCategory) -@test GAT.accessors(theory) == accessors -@test GAT.constructors(theory) == constructors -@test GAT.alias_functions(theory) == alias_functions -@test GAT.interface(theory) == [accessors; constructors; alias_functions] - -# Theory extension -@signature ThSemigroup{S} begin - S::TYPE - times(x::S,y::S)::S -end - -@signature ThMonoidExt{M} <: ThSemigroup{M} begin - munit()::M -end - -@test ThSemigroup isa Type && ThMonoidExt isa Type - -theory = GAT.Theory( - [ GAT.TypeConstructor(:M, [], GAT.Context()) ], - [ GAT.TermConstructor(:times, [:x,:y], :M, - GAT.Context((:x => :M, :y => :M))), - GAT.TermConstructor(:munit, [], :M, GAT.Context()) ], - [], - Dict{Symbol,Symbol}() -) - -@test GAT.theory(ThMonoidExt) == theory - -# GAT expressions in a theory -################################ - -theory = GAT.theory(ThCategory) -context = GAT.Context((:X => :Ob, :Y => :Ob, :Z => :Ob, - :f => :(Hom(X,Y)), :g => :(Hom(Y,Z)))) -@test GAT.expand_in_context(:X, [:f,:g], context, theory) == :(dom(f)) -@test (GAT.expand_in_context(:(Hom(X,Z)), [:f,:g], context, theory) == - :(Hom(dom(f),codom(g)))) - -context = GAT.Context((:X => :Ob, :Y => :Ob, :Z => :Ob, :f => :(Hom(X,Y)))) -@test_throws ErrorException GAT.expand_in_context(:W, [:f], context, theory) -@test_throws ErrorException GAT.expand_in_context(:Z, [:f], context, theory) - -context = GAT.Context((:X => :Ob, :Y => :Ob, :f => :(Hom(X,Y)))) -@test GAT.equations(context, theory) == [ :(dom(f)) => :X, :(codom(f)) => :Y ] -@test GAT.equations([:f], context, theory) == [] - -context = GAT.Context((:X => :Ob, :Y => :Ob, :Z => :Ob, - :f => :(Hom(X,Y)), :g => :(Hom(Y,Z)))) -@test (GAT.equations(context, theory) == - [ :(dom(f)) => :X, :(codom(f)) => :Y, - :(dom(g)) => :Y, :(codom(g)) => :Z ]) -@test GAT.equations([:f,:g], context, theory) == [ :(dom(g)) => :(codom(f)) ] - -# Instances -########### - -""" Vectors as an instance of the theory of semigroups -""" -@instance ThSemigroup{Vector} begin - times(x::Vector, y::Vector) = [x; y] -end - -@test times([1,2],[3,4]) == [1,2,3,4] - -@signature ThMonoid{M} begin - M::TYPE - munit()::M - times(x::M,y::M)::M -end - -# Incomplete instance of Monoid -# XXX: Cannot use `@test_warn` since generated code won't be at toplevel. -#@test_warn "not implemented" @instance ThMonoid{String} begin -# times(x::AbsStringtractString, y::String) = string(x,y) -#end - -# Complete instance of Monoid -@instance ThMonoid{String} begin - munit(::Type{String}) = "" - times(x::String, y::String) = string(x,y) -end - -@test munit(String) == "" -@test times("a", "b") == "ab" - -# Reflection -@test invoke_term(ThMonoid, (String,), :munit) == "" -@test invoke_term(ThMonoid, (String,), :times, "a", "b") == "ab" - -end diff --git a/test/graphics/GraphvizCategories.jl b/test/graphics/GraphvizCategories.jl index 872e32ee6..90b1834bf 100644 --- a/test/graphics/GraphvizCategories.jl +++ b/test/graphics/GraphvizCategories.jl @@ -1,7 +1,8 @@ module TestGraphvizCategories using Test -using Catlab.GATs, Catlab.Theories, Catlab.CategoricalAlgebra, Catlab.Graphs +using GATlab +using Catlab.Theories, Catlab.CategoricalAlgebra, Catlab.Graphs using Catlab.Graphics.GraphvizCategories using Catlab.Graphics: Graphviz diff --git a/test/graphs/GraphAlgorithms.jl b/test/graphs/GraphAlgorithms.jl index b17e762f3..a0fb02614 100644 --- a/test/graphs/GraphAlgorithms.jl +++ b/test/graphs/GraphAlgorithms.jl @@ -3,6 +3,8 @@ using Test using Catlab.Graphs using Catlab.Theories +using ACSets +using Catlab.CategoricalAlgebra # Connectivity ############## @@ -75,7 +77,7 @@ ep = enumerate_paths(g) # Trees ####### -g = Searching.tree([1, 1, 1, 2, 2]) +g = GraphSearching.tree([1, 1, 1, 2, 2]) g′ = @acset Graph begin V = 5 E = 4 diff --git a/test/programs/GenerateJuliaPrograms.jl b/test/programs/GenerateJuliaPrograms.jl index 204e2a5aa..a1a553b40 100644 --- a/test/programs/GenerateJuliaPrograms.jl +++ b/test/programs/GenerateJuliaPrograms.jl @@ -2,7 +2,8 @@ module TestGenerateJuliaPrograms using Test -using Catlab.GATs, Catlab.Theories +using GATlab +using Catlab.Theories using Catlab.Programs.GenerateJuliaPrograms ℝ = Ob(FreeCartesianCategory, :ℝ) diff --git a/test/programs/ParseJuliaPrograms.jl b/test/programs/ParseJuliaPrograms.jl index b0fa2ccaf..1d7a8188b 100644 --- a/test/programs/ParseJuliaPrograms.jl +++ b/test/programs/ParseJuliaPrograms.jl @@ -2,7 +2,8 @@ module TestParseJuliaPrograms using Test -using Catlab.GATs, Catlab.Theories, Catlab.WiringDiagrams +using GATlab +using Catlab.Theories, Catlab.WiringDiagrams using Catlab.Programs using Catlab.Programs.ParseJuliaPrograms: normalize_arguments @@ -29,7 +30,7 @@ f, g, h, m, n = (generator(C, name) for name in [:f, :g, :h, :m, :n]) parsed = @program(C, (x::X) -> f(x)) @test parsed == to_wiring_diagram(f) -@test parsed isa WiringDiagram{ThBiproductCategory} +@test parsed isa WiringDiagram{ThBiproductCategory.Meta.T} # Composition: one-dimensional. diff --git a/test/runtests.jl b/test/runtests.jl index c6f4f625b..61ef70606 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,9 +1,5 @@ using Test -@testset "GATs" begin - include("gats/GATs.jl") -end - @testset "Theories" begin include("theories/Theories.jl") end diff --git a/test/theories/Category.jl b/test/theories/Category.jl index 77f1fa400..7b3e2c4a0 100644 --- a/test/theories/Category.jl +++ b/test/theories/Category.jl @@ -101,14 +101,14 @@ f = Hom(:f, A, B) x = El(:x, B) @test ob(x) == B @test ob(coact(f, x)) == A -@test f ⋅ x == coact(f, x) +# @test f ⋅ x == coact(f, x) A, B = Ob(FreeCopresheaf, :A, :B) f = Hom(:f, A, B) x = El(:x, A) @test ob(x) == A @test ob(act(x, f)) == B -@test x ⋅ f == act(x, f) +# @test x ⋅ f == act(x, f) # Infix notation (Unicode) @test unicode(x) == "x" diff --git a/test/theories/Theories.jl b/test/theories/Theories.jl index d231b3222..ec0b4eafc 100644 --- a/test/theories/Theories.jl +++ b/test/theories/Theories.jl @@ -1,7 +1,8 @@ module TestTheories using Test -using Catlab.GATs, Catlab.Theories +using GATlab +using Catlab.Theories sexpr(expr::GATExpr) = sprint(show_sexpr, expr) unicode(expr::GATExpr; all::Bool=false) = diff --git a/test/wiring_diagrams/Expressions.jl b/test/wiring_diagrams/Expressions.jl index 1cfc84623..8e3a14ffd 100644 --- a/test/wiring_diagrams/Expressions.jl +++ b/test/wiring_diagrams/Expressions.jl @@ -3,7 +3,7 @@ module TestWiringDiagramExpressions using Test using Catlab.Theories, Catlab.Graphs, Catlab.WiringDiagrams -using Catlab.GATs.SyntaxSystems: syntax_module +using GATlab.Models.SymbolicModels: syntax_module using Catlab.WiringDiagrams.WiringDiagramExpressions: find_parallel, find_series, transitive_reduction! diff --git a/test/wiring_diagrams/MonoidalDirected.jl b/test/wiring_diagrams/MonoidalDirected.jl index e33521f84..9aed3f3f1 100644 --- a/test/wiring_diagrams/MonoidalDirected.jl +++ b/test/wiring_diagrams/MonoidalDirected.jl @@ -102,7 +102,7 @@ A = Ports([:A]) @test compose(mcopy(A), otimes(id(A),delete(A))) == id(A) # Cartesian categories -A = Ports{ThCartesianCategory}([:A]) +A = Ports{ThCartesianCategory.Meta.T}([:A]) @test mcopy(A) == mcopy(Ports([:A])) @test delete(A) == delete(Ports([:A])) @test_throws MethodError mmerge(A) @@ -129,7 +129,7 @@ A = Ports([:A]) @test compose(otimes(id(A),create(A)), mmerge(A)) == id(A) # Cocartesian categories -A = Ports{ThCocartesianCategory}([:A]) +A = Ports{ThCocartesianCategory.Meta.T}([:A]) @test mmerge(A) == mmerge(Ports([:A])) @test create(A) == create(Ports([:A])) @test_throws MethodError mcopy(A) @@ -140,14 +140,14 @@ A = Ports{ThCocartesianCategory}([:A]) # Monoidal categories with bidiagonals, and non-naturality of explicit # representation. -A = Ports{ThMonoidalCategoryWithBidiagonals}([:A]) +A = Ports{ThMonoidalCategoryWithBidiagonals.Meta.T}([:A]) @test boxes(mcopy(A)) == [ Junction(:A,1,2) ] @test boxes(mcopy(otimes(A,A))) == repeat([ Junction(:A,1,2) ], 2) @test compose(create(A), mcopy(A)) != create(otimes(A,A)) @test compose(mmerge(A), delete(A)) != delete(otimes(A,A)) # Biproduct categories, and naturality of implicit representation. -A = Ports{ThBiproductCategory}([:A]) +A = Ports{ThBiproductCategory.Meta.T}([:A]) @test compose(create(A), mcopy(A)) == create(otimes(A,A)) @test compose(mmerge(A), delete(A)) == delete(otimes(A,A)) @@ -177,7 +177,7 @@ g = singleton_diagram(ThDaggerSymmetricMonoidalCategory, Box(:g,[:B],[:A])) ### Duals -A, B = [ Ports{ThCompactClosedCategory}([sym]) for sym in [:A, :B] ] +A, B = [ Ports{ThCompactClosedCategory.Meta.T}([sym]) for sym in [:A, :B] ] I = munit(typeof(A)) @test boxes(dunit(A)) == [ Junction(:A, [], [PortOp{:dual}(:A), :A]) ] @@ -218,7 +218,7 @@ g = singleton_diagram(ThCompactClosedCategory, Box(:g,[:B],[:A])) # Traced monoidal category #------------------------- -const TracedMon = ThTracedMonoidalCategory +const TracedMon = ThTracedMonoidalCategory.Meta.T A, B, X, Y = [ Ports{TracedMon}([sym]) for sym in [:A,:B,:X,:Y] ] I = munit(typeof(A)) f = singleton_diagram(TracedMon, Box(:f, [:X,:A], [:X,:B])) @@ -253,7 +253,7 @@ f = singleton_diagram(TracedMon, Box(:f, [:A], [:B])) # Bicategory of relations #------------------------ -A, B = [ Ports{ThBicategoryRelations}([sym]) for sym in [:A, :B] ] +A, B = [ Ports{ThBicategoryRelations.Meta.T}([sym]) for sym in [:A, :B] ] R = singleton_diagram(ThBicategoryRelations, Box(:R,[:A],[:B])) S = singleton_diagram(ThBicategoryRelations, Box(:S,[:A],[:B])) @@ -270,7 +270,7 @@ S = singleton_diagram(ThBicategoryRelations, Box(:S,[:A],[:B])) # Abelian bicategory of relations #-------------------------------- -A, B = [ Ports{ThAbelianBicategoryRelations}([sym]) for sym in [:A, :B] ] +A, B = [ Ports{ThAbelianBicategoryRelations.Meta.T}([sym]) for sym in [:A, :B] ] R = singleton_diagram(ThAbelianBicategoryRelations, Box(:R,[:A],[:B])) S = singleton_diagram(ThAbelianBicategoryRelations, Box(:S,[:A],[:B])) From f506f0358a1b9e1ba912e1066d821e7cf7b1936c Mon Sep 17 00:00:00 2001 From: Owen Lynch Date: Thu, 12 Oct 2023 14:30:40 -0700 Subject: [PATCH 04/10] fix docs and experiments --- .github/workflows/experiments.yml | 2 +- benchmark/Graphs.jl | 2 +- docs/Project.toml | 2 + docs/literate/graphs/graphs.jl | 2 +- docs/literate/graphs/graphs_label.jl | 2 +- docs/literate/sketches/cat_elements.jl | 2 +- docs/literate/sketches/meets.jl | 2 +- docs/literate/sketches/partitions.jl | 2 +- docs/literate/sketches/preorders.jl | 4 +- docs/literate/sketches/smc.jl | 2 +- docs/literate/wiring_diagrams/wd_cset.jl | 2 +- .../wiring_diagrams/wiring_diagram_basics.jl | 2 +- docs/make.jl | 1 - docs/src/apis/categorical_algebra.md | 2 +- docs/src/apis/gats.md | 290 ---------- docs/src/apis/theories.md | 12 +- docs/src/index.md | 25 +- experiments/CompAlgebra/Project.toml | 16 - .../CompAlgebra/docs/algebraic_nets.jl | 46 -- experiments/CompAlgebra/src/AlgebraicNets.jl | 305 ----------- experiments/CompAlgebra/src/CompAlgebra.jl | 6 - experiments/CompAlgebra/src/MathFormulas.jl | 510 ------------------ experiments/CompAlgebra/test/AlgebraicNets.jl | 184 ------- experiments/CompAlgebra/test/MathFormulas.jl | 152 ------ experiments/CompAlgebra/test/runtests.jl | 9 - experiments/Markov/Project.toml | 1 + experiments/Markov/src/MarkovCategories.jl | 15 +- experiments/Markov/src/StochExpr.jl | 2 +- experiments/Markov/test/MarkovCategories.jl | 4 +- experiments/Markov/test/StochMapsExpr.jl | 2 +- test/Project.toml | 1 + test/categorical_algebra/DataMigrations.jl | 2 +- test/programs/DiagrammaticPrograms.jl | 2 +- 33 files changed, 38 insertions(+), 1575 deletions(-) delete mode 100644 docs/src/apis/gats.md delete mode 100644 experiments/CompAlgebra/Project.toml delete mode 100644 experiments/CompAlgebra/docs/algebraic_nets.jl delete mode 100644 experiments/CompAlgebra/src/AlgebraicNets.jl delete mode 100644 experiments/CompAlgebra/src/CompAlgebra.jl delete mode 100644 experiments/CompAlgebra/src/MathFormulas.jl delete mode 100644 experiments/CompAlgebra/test/AlgebraicNets.jl delete mode 100644 experiments/CompAlgebra/test/MathFormulas.jl delete mode 100644 experiments/CompAlgebra/test/runtests.jl diff --git a/.github/workflows/experiments.yml b/.github/workflows/experiments.yml index 692e185cf..e6cc2b002 100644 --- a/.github/workflows/experiments.yml +++ b/.github/workflows/experiments.yml @@ -9,7 +9,7 @@ jobs: strategy: fail-fast: false matrix: - experiment: [CompAlgebra, Markov, TensorNetworks] + experiment: [Markov, TensorNetworks] steps: - uses: actions/checkout@v3 diff --git a/benchmark/Graphs.jl b/benchmark/Graphs.jl index 64823e6a2..cc074d3b5 100644 --- a/benchmark/Graphs.jl +++ b/benchmark/Graphs.jl @@ -7,7 +7,7 @@ import Graphs as SimpleGraphs import MetaGraphs const LG, MG = SimpleGraphs, MetaGraphs -using Catlab.GATs, Catlab.CategoricalAlgebra, Catlab.Graphs +using GATlab, Catlab.CategoricalAlgebra, Catlab.Graphs using Catlab.WiringDiagrams: query using Catlab.Programs: @relation diff --git a/docs/Project.toml b/docs/Project.toml index 9ba2a35b7..fa2349acc 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -5,7 +5,9 @@ Compose = "a81c6b42-2e10-5240-aca2-a61377ecd94b" Convex = "f65535da-76fb-5f13-bab9-19810c17039a" DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +GATlab = "f0ffcf3b-d13a-433e-917c-cc44ccf5ead2" JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" +JSONSchema = "7d188eb4-7ad8-530c-ae41-71a32a6d4692" LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306" PrettyTables = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d" diff --git a/docs/literate/graphs/graphs.jl b/docs/literate/graphs/graphs.jl index 01251b376..39cccf345 100644 --- a/docs/literate/graphs/graphs.jl +++ b/docs/literate/graphs/graphs.jl @@ -1,4 +1,4 @@ -using Catlab.GATs, Catlab.Theories +using GATlab, Catlab.Theories using Catlab.CategoricalAlgebra using Catlab.Graphs using Catlab.Graphics diff --git a/docs/literate/graphs/graphs_label.jl b/docs/literate/graphs/graphs_label.jl index ecc04b792..7de1d28b5 100644 --- a/docs/literate/graphs/graphs_label.jl +++ b/docs/literate/graphs/graphs_label.jl @@ -1,7 +1,7 @@ # # Labeled Graphs # This example demonstrates how to define new C-Sets from existing C-Sets via the example of adding labels to a graph. We treat labels as members of an arbitrary FinSet of labels rather than a data attribute for pedagogical reasons. When you think of graphs where the labels are numbers, colors, or values of some kind, you would want to make them attributes. The motivation for this example is to be the simplest extension to the theory of graphs that you could possibly make. -using Catlab.GATs, Catlab.Theories +using GATlab, Catlab.Theories using Catlab.CategoricalAlgebra using Catlab.Graphs using Catlab.Graphics diff --git a/docs/literate/sketches/cat_elements.jl b/docs/literate/sketches/cat_elements.jl index 0acd4cd28..ce35486cf 100644 --- a/docs/literate/sketches/cat_elements.jl +++ b/docs/literate/sketches/cat_elements.jl @@ -1,6 +1,6 @@ # # The Category of Elements # A very useful construction in applied category theory is the Category of Elements, which is also called the Grothendieck construction. This is a very general technique in category theory, but we will look at how you can use it to explain why graphs are so useful in computer science. We have already seen that C-Sets are a model of relational databases that can be used to store data as a collection of interlocking tables. Relational databases are the bread and butter of the computing industry. Every company on earth uses software that is backed by a relational database. Most data that is not stored in a relational DB is often stored in some kind of graph data structure. This sketch will show how these approaches are interchangeable via the category of elements, which associates to every database instance a graph and a graph homomorphism into the schema of the graph. -using Catlab.GATs, Catlab.CategoricalAlgebra, Catlab.Graphs, Catlab.Graphics +using GATlab, Catlab.CategoricalAlgebra, Catlab.Graphs, Catlab.Graphics using Colors # Let's tell Catlab how to draw categories of elements. diff --git a/docs/literate/sketches/meets.jl b/docs/literate/sketches/meets.jl index e25a0c4c3..0d490e816 100644 --- a/docs/literate/sketches/meets.jl +++ b/docs/literate/sketches/meets.jl @@ -9,7 +9,7 @@ using Core: GeneratedFunctionStub using Test -using Catlab.GATs, Catlab.Theories, Catlab.CategoricalAlgebra +using GATlab, Catlab.Theories, Catlab.CategoricalAlgebra using Catlab.Graphics import Catlab.Theories: compose diff --git a/docs/literate/sketches/partitions.jl b/docs/literate/sketches/partitions.jl index 52592b43a..7f9508fdc 100644 --- a/docs/literate/sketches/partitions.jl +++ b/docs/literate/sketches/partitions.jl @@ -10,7 +10,7 @@ using Core: GeneratedFunctionStub using Test -using Catlab.GATs, Catlab.Theories, Catlab.CategoricalAlgebra +using GATlab, Catlab.Theories, Catlab.CategoricalAlgebra import Catlab.Theories: compose using DataStructures using PrettyTables diff --git a/docs/literate/sketches/preorders.jl b/docs/literate/sketches/preorders.jl index 358486d90..17a28e20c 100644 --- a/docs/literate/sketches/preorders.jl +++ b/docs/literate/sketches/preorders.jl @@ -12,7 +12,7 @@ using Core: GeneratedFunctionStub using Test -using Catlab.GATs, Catlab.Theories, Catlab.CategoricalAlgebra +using GATlab, Catlab.Theories, Catlab.CategoricalAlgebra import Catlab.Theories: compose #= @@ -114,7 +114,7 @@ end # expressions are represented at expression trees ex = compose(P, [:f, :g]) # the head of an expression is the root of the expression tree -GATs.head(ex) +head(ex) # the julia type of the expression typeof(ex) # the GAT type of the expression diff --git a/docs/literate/sketches/smc.jl b/docs/literate/sketches/smc.jl index 271f940c6..729e306a6 100644 --- a/docs/literate/sketches/smc.jl +++ b/docs/literate/sketches/smc.jl @@ -3,7 +3,7 @@ #md # [![](https://img.shields.io/badge/show-nbviewer-579ACA.svg)](@__NBVIEWER_ROOT_URL__/generated/sketches/smc.ipynb) # # This vignette supports section 4.4.3 of Seven Sketches in Compositionality, which introduces the definition of symmetric monoidal categories (SMCs). SMCs are a core concept in applied category theory and are a workhorse of Catlab's utility in computing applications. We will discuss the definition as a GAT, see examples of working with formulas, and conversions to wiring diagrams (sometimes called string diagrams). SMCs are useful for modeling mathematical structures like programs or processes where the objects represent data or things and the morphisms represent processes that happen to those things. -using Catlab.GATs, Catlab.Theories +using GATlab, Catlab.Theories using Catlab.CategoricalAlgebra using Catlab.WiringDiagrams using Catlab.Programs diff --git a/docs/literate/wiring_diagrams/wd_cset.jl b/docs/literate/wiring_diagrams/wd_cset.jl index 58de0520e..59e7e3f98 100644 --- a/docs/literate/wiring_diagrams/wd_cset.jl +++ b/docs/literate/wiring_diagrams/wd_cset.jl @@ -1,6 +1,6 @@ # # Wiring Diagrams as Attributed C-Sets # Catlab supports many different flavors of diagrammatic syntax. These support the different combinatorial data structures that we use for representing categorical constructions. We will discuss DirectedWiringDiagrams, UndirectedWiringDiagrams, and CPortGraphs in this document. -using Catlab.GATs, Catlab.Theories +using GATlab, Catlab.Theories using Catlab.CategoricalAlgebra using Catlab.WiringDiagrams using Catlab.Programs diff --git a/docs/literate/wiring_diagrams/wiring_diagram_basics.jl b/docs/literate/wiring_diagrams/wiring_diagram_basics.jl index 19f18e65e..866118cb4 100644 --- a/docs/literate/wiring_diagrams/wiring_diagram_basics.jl +++ b/docs/literate/wiring_diagrams/wiring_diagram_basics.jl @@ -2,7 +2,7 @@ # #md # [![](https://img.shields.io/badge/show-nbviewer-579ACA.svg)](@__NBVIEWER_ROOT_URL__/generated/wiring_diagrams/wiring_diagram_basics.ipynb) # -# using Catlab.GATs, you can create, manipulate, serialize, and visualize *wiring +# using GATlab, you can create, manipulate, serialize, and visualize *wiring # diagrams*, also known as [string # diagrams](https://ncatlab.org/nlab/show/string+diagram). The flexible data # structure for wiring diagrams allows arbitrary data to be attached to boxes, diff --git a/docs/make.jl b/docs/make.jl index 5656b3808..cce4ea7af 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -76,7 +76,6 @@ makedocs( ], ], "Modules" => Any[ - "apis/gats.md", "apis/theories.md", "apis/categorical_algebra.md", "apis/graphs.md", diff --git a/docs/src/apis/categorical_algebra.md b/docs/src/apis/categorical_algebra.md index b5811fdb6..2a72173bf 100644 --- a/docs/src/apis/categorical_algebra.md +++ b/docs/src/apis/categorical_algebra.md @@ -63,7 +63,7 @@ We can also add equations to this presentation, but we currently do nothing with We will now give an example of how this all works in practice. ```@example -using Catlab.GATs, Catlab.CategoricalAlgebra +using GATlab, Catlab.CategoricalAlgebra # Write down the schema for a weighted graph @present SchWeightedGraph(FreeSchema) begin diff --git a/docs/src/apis/gats.md b/docs/src/apis/gats.md deleted file mode 100644 index 94a238d9a..000000000 --- a/docs/src/apis/gats.md +++ /dev/null @@ -1,290 +0,0 @@ -# Theories, instances, and expressions - -At the core of Catlab is a system for defining generalized algebraic structures, -such as categories and monoidal categories, and then creating instances of these -structures in Julia code. The objects, morphisms, and even higher-order -morphisms can also be represented as typed symbolic expressions, as in a -computer algebra system. These expressions can be manipulated abstractly or -transformed into more concrete representations, such as [wiring diagrams](@ref -wiring_diagrams) or [Julia functions](@ref programs). - -The basic elements of this system are: - -1. **Generalized algebraic theories** (GATs), defined using the - [`@theory`](@ref) macro. Categories and other typed (multisorted) algebraic - structures can be defined as GATs. Alternatively, the [`@signature`](@ref) - macro can be used when only the signature (not the axioms) of the GAT are to - be specified. - -2. **Instances**, or concrete implementations, of theories, defined using the - [`@instance`](@ref) macro. - -3. **Syntax systems** for theories, defined using the [`@syntax`](@ref) macro. - These are type-safe expression trees constructed using ordinary Julia - functions. - -We'll explain each of these elements in greater detail in the following -sections. From the programming perspective, theories can be thought of as -*interfaces* and bear some resemblance to [type -classes](https://en.wikipedia.org/wiki/Type_class) in languages like Haskell. -Both instances and syntax systems can then be thought of as *implementations* of -the interface. - -## [Theories](@id gats) - -[Generalized algebraic -theories](https://ncatlab.org/nlab/show/generalized+algebraic+theory) (GATs) are -the natural logical system in which to define categories and related algebraic -structures. GATs generalize the typed (multisorted) [algebraic -theories](https://ncatlab.org/nlab/show/algebraic+theory) of [universal -algebra](https://en.wikipedia.org/wiki/Universal_algebra) by incorporating a -fragment of dependent type theory; they are perhaps the simplest dependently -typed logics. - -Catlab implements a version of the GAT formalism on top of Julia's type system, -taking advantage of Julia macros to provide a pleasant syntax. GATs are defined -using the [`@theory`](@ref) macro. - -For example, the theory of categories could be defined by: - -```@setup category -using Catlab.GATs -import Catlab.Theories: Ob, Hom, ObExpr, HomExpr, dom, codom, compose, ⋅, id -``` - -```@example category -@theory ThCategory{Ob,Hom} begin - @op begin - (→) := Hom - (⋅) := compose - end - - Ob::TYPE - Hom(dom::Ob, codom::Ob)::TYPE - - id(A::Ob)::(A → A) - compose(f::(A → B), g::(B → C))::(A → C) ⊣ (A::Ob, B::Ob, C::Ob) - - (f ⋅ g) ⋅ h == f ⋅ (g ⋅ h) ⊣ (A::Ob, B::Ob, C::Ob, D::Ob, - f::(A → B), g::(B → C), h::(C → D)) - f ⋅ id(B) == f ⊣ (A::Ob, B::Ob, f::(A → B)) - id(A) ⋅ f == f ⊣ (A::Ob, B::Ob, f::(A → B)) -end -nothing # hide -``` - -The code is simplified only slightly from the official Catlab definition of -`ThCategory`. The theory has two *type constructors*, `Ob` (object) and `Hom` -(morphism). The type `Hom` is a dependent type, depending on two objects, named -`dom` (domain) and `codom` (codomain). The theory has two *term constructors*, -`id` (identity) and `compose` (composition). - -Notice how the return types of the term constructors depend on the argument -values. For example, the term `id(A)` has type `Hom(A,A)`. The term constructor -`compose` also uses *context variables*, listed to the right of the `⊣` -symbol. These context variables can also be defined after a `where` clause, -but the left hand side must be surrounded by parentheses. This allows us to -write `compose(f,g)`, instead of the more verbose `compose(A,B,C,f,g)` (for -discussion, see Cartmell, 1986, Sec 10: Informal syntax). - -Notice the `@op` call where we can create method aliases that can then be used -throughout the rest of the theory and outside of definition. We can either use -this block notation, or a single line notation such as `@op (⋅) := compose` to -define a single alias. Here we utilize this functionality by replacing the `Hom` -and `compose` methods with their equivalent Unicode characters, `→` and `⋅` -respectively. These aliases are also automatically available to definitions that -inherit a theory that already has the alias defined. - -!!! note - - In general, a GAT consists of a *signature*, defining the types and terms of - the theory, and a set of *axioms*, the equational laws satisfied by models - of the theory. The theory of categories, for example, has axioms of - unitality and associativity. At present, Catlab supports the specification - of both signatures and the axioms, but is not currently utilizing the axiom - definitions in any way, reflecting its status as a programming library, not - a proof assistant. It is the programmer's responsibility to ensure any - declared instances of an algebraic structure satisfy its axioms. - -#### References - -- Cartmell, 1986: Generalized algebraic theories and contextual categories, - [DOI:10.1016/0168-0072(86)90053-9](https://doi.org/10.1016/0168-0072(86)90053-9) -- Cartmell, 1978, PhD thesis: *Generalized algebraic theories and contextual - categories* -- Pitts, 1995: Categorical logic, Sec 6: Dependent types - -## [Instances](@id instances) - -A theory can have one or more *instances*, or instantiations by ordinary -Julia types and functions. This feature builds on Julia's support for generic -functions with [multiple -dispatch](https://docs.julialang.org/en/v1/manual/methods/). - -Instances are declared using the [`@instance`](@ref) macro. In an instance of a -theory, each theory type is mapped to a Julia type and each term is mapped -to a Julia method of the same name. For example, the category of matrices could -be defined as an instance of the theory `Category` defined above: - -```@example category -using LinearAlgebra: I - -struct MatrixDomain - eltype::Type - dim::Int -end - -@instance ThCategory{MatrixDomain, Matrix} begin - dom(M::Matrix) = MatrixDomain(eltype(M), size(M,1)) - codom(M::Matrix) = MatrixDomain(eltype(M), size(M,2)) - - id(m::MatrixDomain) = Matrix{m.eltype}(I, m.dim, m.dim) - compose(M::Matrix, N::Matrix) = M*N -end -``` - -```@example category -A = Matrix{Float64}([0 1; 1 0]) -id(dom(A)) -``` - -In this instance, the theory type `Ob` is mapped to the custom Julia type -`MatrixDomain`. The latter type has two fields, a Julia type `eltype` -representing a field $k$ and an integer `dim` representing the dimensionality -$n$, and so can be interpreted as the $n$-dimensional vector space $k^n$. The -theory `Hom` is mapped to the standard Julia type `Matrix`. - -## [Syntax systems](@id syntax-systems) - -Theories can also be instantiated as systems of symbolic expressions, using -the [`@syntax`](@ref) macro. The symbolic expressions are expression trees, as -commonly used in computer algebra systems. They are similar to Julia's `Expr` -type but they are instead subtyped from Catlab's [`GATExpr`](@ref) type and they -have a more refined type hierarchy. - -A single theory can have different syntax systems, treating different terms -as primitive or performing different simplication or normalization procedures. -Catlab tries to make it easy to define new syntax systems. Many of the -theories included with Catlab have default syntax systems, but the user is -encouraged to define their own to suit their needs. - -To get started, you can always call the `@syntax` macro with an empty body. -Below, we subtype from Catlab's abstract types `ObExpr` and `HomExpr` to enable -LaTeX pretty-printing and other convenient features, but this is not required. - -```@example category -@syntax CategoryExprs{ObExpr, HomExpr} ThCategory begin -end - -A, B, C, D = [ Ob(CategoryExprs.Ob, X) for X in [:A, :B, :C, :D] ] -f, g, h = Hom(:f, A, B), Hom(:g, B, C), Hom(:h, C, D) - -compose(compose(f,g),h) -``` - -The resulting symbolic expressions perform no simplification. For example, the -associativity law is not satisfied: - -```@example category -compose(compose(f,g),h) == compose(f,compose(g,h)) -``` - -Thus, unlike instances of a theory, syntactic expressions are not expected to -obey all the axioms of the theory. - -However, the user may supply logic in the body of the `@syntax` macro to enforce -the axioms or perform other kinds of simplification. Below, we use the -[`associate`](@ref) function provided by Catlab to convert the binary -expressions representing composition into $n$-ary expressions for any number -$n$. The option `strict=true` tells Catlab to check that the domain and codomain -objects are strictly equal and throw an error if they are not. - -```@example category -@syntax SimplifyingCategoryExprs{ObExpr, HomExpr} ThCategory begin - compose(f::Hom, g::Hom) = associate(new(f,g; strict=true)) -end - -A, B, C, D = [ Ob(SimplifyingCategoryExprs.Ob, X) for X in [:A, :B, :C, :D] ] -f, g, h = Hom(:f, A, B), Hom(:g, B, C), Hom(:h, C, D) - -compose(compose(f,g),h) -``` - -Now the associativity law *is* satisfied: - -```@example category -compose(compose(f,g),h) == compose(f,compose(g,h)) -``` - -### Primitive versus derived operations - -In some algebraic structures, there is a choice as to which operations should be -considered primitive and which should be derived. For example, in a [cartesian -monoidal category](https://ncatlab.org/nlab/show/cartesian+monoidal+category), -the copy operation $\Delta_X: X \to X \otimes X$ can be defined in terms of the -pairing operation $\langle f, g \rangle$, or vice versa. In addition, the -projections $\pi_{X,Y}: X \otimes Y \to X$ and $\pi_{X,Y}': X \otimes Y \to Y$ -can be defined in terms of the deleting operation (terminal morphism) or left as -primitive. - -In Catlab, the recommended way to deal with such situations is to define *all* -the operations in the theory and then allow particular syntax systems to -determine which operations, if any, will be derived from others. In the case of -the cartesian monoidal category, we could define a signature `CartesianCategory` -by inheriting from the builtin theory `SymmetricMonoidalCategory`. - -```@setup cartesian-monoidal-category -using Catlab.GATs -import Catlab.Theories: Ob, Hom, ObExpr, HomExpr, ThSymmetricMonoidalCategory, - dom, codom, compose, id, otimes, munit, braid -``` - -```@example cartesian-monoidal-category -@signature ThCartesianCategory{Ob,Hom} <: ThSymmetricMonoidalCategory{Ob,Hom} begin - mcopy(A::Ob)::(A → (A ⊗ A)) - delete(A::Ob)::(A → munit()) - - pair(f::(A → B), g::(A → C))::(A → (B ⊗ C)) ⊣ (A::Ob, B::Ob, C::Ob) - proj1(A::Ob, B::Ob)::((A ⊗ B) → A) - proj2(A::Ob, B::Ob)::((A ⊗ B) → B) -end -nothing # hide -``` - -We could then define the copying operation in terms of the pairing. - -```@example cartesian-monoidal-category -@syntax CartesianCategoryExprsV1{ObExpr,HomExpr} ThCartesianCategory begin - mcopy(A::Ob) = pair(id(A), id(A)) -end - -A = Ob(CartesianCategoryExprsV1.Ob, :A) -mcopy(A) -``` - -Alternatively, we could define the pairing and projections in terms of the -copying and deleting operations. - -```@example cartesian-monoidal-category -@syntax CartesianCategoryExprsV2{ObExpr,HomExpr} ThCartesianCategory begin - pair(f::Hom, g::Hom) = compose(mcopy(dom(f)), otimes(f,g)) - proj1(A::Ob, B::Ob) = otimes(id(A), delete(B)) - proj2(A::Ob, B::Ob) = otimes(delete(A), id(B)) -end - -A, B, C = [ Ob(CartesianCategoryExprsV2.Ob, X) for X in [:A, :B, :C] ] -f, g = Hom(:f, A, B), Hom(:g, A, C) -pair(f, g) -``` - -## API - -```@autodocs -Modules = [ - GATs.TheoriesInstances, - GATs.SyntaxSystems, - GATs.Rewriting, - GATs.Presentations, -] -Private = false -``` diff --git a/docs/src/apis/theories.md b/docs/src/apis/theories.md index 150fba60e..309f1698a 100644 --- a/docs/src/apis/theories.md +++ b/docs/src/apis/theories.md @@ -1,12 +1,12 @@ # Standard library of theories Through the module `Catlab.Theories`, Catlab provides a standard library of -[generalized algebraic theories](@ref gats) for categories, monoidal categories, -and other categorical structures. The theories correspond, in most cases, to -standard definitions in category theory and they are used throughout Catlab and -the AlgebraicJulia ecosystem to structure programs and provide a common -interface for applied category theory. The module also provides default [syntax -systems](@ref syntax-systems) for many of the theories. +[generalized algebraic theories](https://algebraicjulia.github.io/GATlab.jl) for +categories, monoidal categories, and other categorical structures. The theories +correspond, in most cases, to standard definitions in category theory and they +are used throughout Catlab and the AlgebraicJulia ecosystem to structure +programs and provide a common interface for applied category theory. The module +also provides default syntax systems for many of the theories. Categorical structures for which theories are provided include: diff --git a/docs/src/index.md b/docs/src/index.md index 5bfdad5c4..104321663 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -58,29 +58,7 @@ diagrams, Petri nets, and the like. ### What is a GAT? -Generalized Algebraic Theories (GATs) are the backbone of Catlab so let's expand a bit on GATs and how they fit into the bigger picture of algebra. - -An algebraic structure, like a group or category, is a mathematical object whose axioms all take the form of equations that are universally quantified (the equations have no exceptions). That’s not a formal definition but it’s a good heuristic. There are different ways to make this precise. The oldest, going back to universal algebra in the early 20th centrury, are algebraic theories. - -[Universal algebra](https://en.wikipedia.org/wiki/Universal_algebra) (sometimes called general algebra) is the field of mathematics that studies algebraic structures themselves, not examples ("models") of algebraic structures. For instance, rather than take particular groups as the object of study, in universal algebra one takes the class of groups as an object of study. In an algebraic theory, you have a collection of (total) operations and they obey a set of equational axioms. Classically, there is only a single generating type, but there are also typed or multi-sorted versions of algebraic theories. Most of the classical structures of abstract algebra, such as groups, rings, and modules, can be defined as algebraic theories. - -Importantly, the theory of categories is [not algebraic](https://mathoverflow.net/q/354920). In other words, a category cannot be defined as a (multi-sorted) algebraic theory. The reason is that the operation of composition is partial, since you can only compose morphisms with compatible (co)domains. Now, categories sure feel like algebraic structures, so people have come up with generalizations of algebraic theories that accomodate categories and related structures. - -The first of these was Freyd’s essentially algebraic theories. In an essentially algebraic theory, you can have partially defined operations; however, to maintain the equational character of the system, the domains of operations must themselves be defined equationally. For example, the theory of categories would be defined as having two types, Ob and Hom, and the composition operation `compose(f::Hom,g::Hom)::Hom` would have domain given by the equation `codom(f) == dom(g)`. As your theories get more elaborate, the sets of equations defining the domains get more complicated and reasoning about the structure is overwhelming. - -Later, Cartmell proposed generalized algebraic theories, which solves the same problem but in a different way. Rather than having partial operations, you have total operations but on dependent types (types that are parameterized by values). So now the composition operation has signature `compose(f::Hom(A,B), g::Hom(B,C))::Hom(A,C) where (A::Ob, B::Ob, C::Ob)` exactly as appears in Catlab. This is closer to the way that mathematicians actually think and write about categories. For example, if you look at the definitions of category, functor, and natural transformation in [Emily Riehl’s textbook](http://www.math.jhu.edu/~eriehl/context/), you will see that they are already essentially in the form of a GAT, whereas they require translation into an essentially algebraic theory. Nevertheless, GATs and essentially algebraic theories have the same expressive power, at least in their standard set-based semantics. GATs provide a version of the computer scientist's type theory that plays well with the mathematician's algebra, thus, providing a perfect opportunity for computer algebra systems. - -## Overview of Key Components - -There are several core parts to the Catlab design, we start with a brief overview of each one - -1. [Catlab.GAT](@ref gats) provides `@theory` - defines a new Generalized Algebraic Theory. These are algebraic versions of what a logician would call a logical theory. They support equational reasoning and a limited version of dependent types. Namely the dependent types must also form an algebraic theory and you are only allowed to have equations between terms of the same type. Catlab ships with many predefined theories for important concepts like Categories and "doctrines" of categories like Symmetric Monoidal Categories. These predefined theories are defined in Catlab.Theories, but you can make your own with the `@theory` macro. - -2. [Catlab.Syntax](@ref syntax-systems) provides initial algebras for a GAT, which are declared with `@syntax`. These are represented as a typed version of Expr that allows you to customize the normalization procedure for example in a FreeCategory, the composition operation which is a unital and associative binary operation is normalized into lists. This allows you to write algorithms on Syntax trees that use different styles of simplification. The only styles available now are support for normalizing unital and associate operations like comoposition and a monoidal product. - -3. [`@instance`](@ref) associates Julia data structures as semantics for a GAT. This is known as functorial semantics, where you associate every type in the GAT with a type in Julia and every term constructor in the GAT to a julia function (possibly a struct constructor). These functions and types must satisfy the axioms that are encoded in the GAT. - -4. [`@present`](@ref) enumerates a finite set of generators for a model of the GAT just like you would write out a group (model of the theory of groups) as list of generators and relations, the presentation lets you enumerate the objects and morphisms that generate a category. +See [GATlab documentation](https://algebraicjulia.github.io/Gatlab.jl) ## Conventions @@ -148,7 +126,6 @@ debug messages. ```@contents Pages = [ - "apis/gats.md", "apis/theories.md", "apis/wiring_diagrams.md", "apis/graphics.md", diff --git a/experiments/CompAlgebra/Project.toml b/experiments/CompAlgebra/Project.toml deleted file mode 100644 index ea44c7f94..000000000 --- a/experiments/CompAlgebra/Project.toml +++ /dev/null @@ -1,16 +0,0 @@ -name = "CompAlgebra" -uuid = "d77033f9-55b8-43bc-9cdc-5e0578f628dc" -authors = ["Evan Patterson "] -version = "0.1.0" - -[deps] -Catlab = "134e5e36-593f-5add-ad60-77f754baafbe" -GeneralizedGenerated = "6b9d7cbe-bcb9-11e9-073f-15a7a543e2eb" -Match = "7eb4fadd-790c-5f42-8a69-bfa0b872bfbf" -Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" -StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" -StructEquality = "6ec83bb0-ed9f-11e9-3b4c-2b04cb4e219c" -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - -[compat] -StructEquality = "2.1" diff --git a/experiments/CompAlgebra/docs/algebraic_nets.jl b/experiments/CompAlgebra/docs/algebraic_nets.jl deleted file mode 100644 index 034b82400..000000000 --- a/experiments/CompAlgebra/docs/algebraic_nets.jl +++ /dev/null @@ -1,46 +0,0 @@ -# # Algebraic networks -# -#md # [![](https://img.shields.io/badge/show-nbviewer-579ACA.svg)](@__NBVIEWER_ROOT_URL__/generated/experimental/algebraic_nets.ipynb) -# - -using Catlab.GATs, Catlab.Theories, Catlab.Graphics -using Catlab.Experimental.AlgebraicNets -using Catlab.Experimental.MathFormulas - -import TikzPictures -#- -R = Ob(AlgebraicNet, "\\mathbb{R}") -f_sin = Hom(:sin, R, R) -f_cos = Hom(:cos, R, R) -display(f_sin) -display(f_cos) -#- -f = compose(mcopy(R),otimes(f_sin,f_cos),mmerge(R)) -#- -to_tikz(f, labels=true) -#- -compile_expr(f; args=[:x]) -#- -to_formula(f, [:x]) -#- -f = compose(mcopy(R,3), otimes(f_sin, f_cos, f_sin)) -#- -to_tikz(f) -#- -compile_expr(f; args=[:x]) -#- -f = compose(linear(2,R,R), f_sin, linear(2,R,R)) -#- -to_tikz(f) -#- -compile_expr(f; args=[:x]) -#- -to_formula(f, [:x]) -#- -f = compose(mcopy(R), otimes(id(R),Hom(:cos,R,R)), Hom(:*,otimes(R,R),R)) -#- -to_tikz(f) -#- -compile_expr(f; args=[:x]) -#- -to_formula(f, [:x]) diff --git a/experiments/CompAlgebra/src/AlgebraicNets.jl b/experiments/CompAlgebra/src/AlgebraicNets.jl deleted file mode 100644 index f6e28e84d..000000000 --- a/experiments/CompAlgebra/src/AlgebraicNets.jl +++ /dev/null @@ -1,305 +0,0 @@ -""" Computer algebra via monoidal categories. - -In a conventional computer algebra system, algebraic expressions are represented -as *trees* whose leaves are variables or constants and whose internal nodes are -arithmetic operations or elementary or special functions. The idea here is to -represent expressions as morphisms in a monoidal category. -""" -module AlgebraicNets -export ThAlgebraicNet, AlgebraicNet, Ob, Hom, dom, codom, id, compose, ⋅, ∘, - otimes, ⊗, munit, braid, mcopy, delete, mmerge, create, linear, constant, - compile, compile_expr, compile_expr_vector, compile_block, evaluate - -using GeneralizedGenerated: mk_function -using Match -import StaticArrays - -using Catlab.GATs -using Catlab.GATs.MetaUtils: concat_expr -import Catlab.GATs: show_latex, show_unicode -using Catlab.GATs.SyntaxSystems: show_latex_infix, show_unicode_infix -using Catlab.Theories: ThMonoidalCategoryWithBidiagonals, ObExpr, HomExpr -import Catlab.Theories: Ob, Hom, dom, codom, - id, compose, ⋅, ∘, otimes, ⊗, munit, braid, mcopy, delete, mmerge, create -using Catlab.Programs -import Catlab.Programs: compile, compile_expr, compile_block, evaluate_hom -import Catlab.Programs.GenerateJuliaPrograms: genvar, genvars, to_function_expr, - generator_expr, input_exprs - -# Syntax -######## - -""" Theory of *algebraic networks* - -TODO: Explain -""" -@signature ThAlgebraicNet{Ob,Hom} <: ThMonoidalCategoryWithBidiagonals{Ob,Hom} begin - linear(x::Any, A::Ob, B::Ob)::(A → B) - constant(x::Any, A::Ob)::(munit() → A) -end - -@syntax AlgebraicNet{ObExpr,HomExpr} ThAlgebraicNet begin - # FIXME: `compose` and `otimes` should delegate to wiring layer when possible. - compose(f::Hom, g::Hom) = associate_unit(new(f,g; strict=true), id) - otimes(A::Ob, B::Ob) = associate_unit(new(A,B), munit) - otimes(f::Hom, g::Hom) = associate(new(f,g)) - - mcopy(A::Ob) = mcopy(A, 2) - mmerge(A::Ob) = mmerge(A, 2) -end - -function mcopy(A::AlgebraicNet.Ob, n::Int) - AlgebraicNet.Hom{:mcopy}([A, n], [A, otimes(fill(A, n))]) -end -function mmerge(A::AlgebraicNet.Ob, n::Int) - AlgebraicNet.Hom{:mmerge}([A, n], [otimes(fill(A, n)), A]) -end - -# Compilation -############# - -""" Internal state for compilation of algebraic network into Julia function. -""" -mutable struct AlgebraicNetState <: CompileState - nvars::Int - constants::Dict{Symbol,Int} - constants_sym::Symbol - AlgebraicNetState(; nvars=0, constants=Dict{Symbol,Int}(), constants_sym=Symbol()) = - new(nvars, constants, constants_sym) -end - -""" Compile an algebraic network into a Julia function. - -This method of "functorial compilation" generates simple imperative code with no -optimizations. Still, the code should be fast provided the original expression -is properly factored, with no duplicate computations. -""" -function compile(f::Union{AlgebraicNet.Hom,Block}; - return_constants::Bool=false, vector::Bool=false, kw...) - expr, constants = vector ? compile_expr_vector(f; kw...) : compile_expr(f; kw...) - compiled = mk_function(Main, expr) - return_constants ? (compiled, constants) : compiled -end - -""" Compile an algebraic network into a Julia function expression. - -The function signature is: - - arguments = inputs (domain) of network - - keyword arguments = symbolic constants (coefficients) of network, if any -""" -function compile_expr(f::AlgebraicNet.Hom; args::Vector{Symbol}=Symbol[]) - inputs = isempty(args) ? input_exprs(ndims(dom(f)), kind=:variables) : args - block, constants = compile_block(f, inputs) - function_expr = to_function_expr(block; kwargs=constants) - (function_expr, constants) -end - -""" Compile an algebraic network into a Julia function expression. - -The function signature is: - - first argument = input vector - - second argument = constant (coefficients) vector - -Unlike `compile_expr`, this method assumes the network has a single output. -""" -function compile_expr_vector(f::AlgebraicNet.Hom; inputs_sym::Symbol=:x, - constants_sym::Symbol=:c) - # Compile algebraic network in block. - inputs = input_exprs(ndims(dom(f)), prefix=inputs_sym, kind=:array) - block, constants = compile_block(f, inputs, constants_sym=constants_sym) - - # Create call expression (function header). - call_expr = Expr(:tuple, inputs_sym, constants_sym) - - # Create function body. - @assert length(block.outputs) == 1 - return_expr = Expr(:return, block.outputs[1]) - body_expr = concat_expr(block.code, return_expr) - - (Expr(:function, call_expr, body_expr), constants) -end - -""" Compile an algebraic network into a block of Julia code. -""" -function compile_block(f::AlgebraicNet.Hom, inputs::Vector; kw...)::Tuple{Block,Vector} - state = AlgebraicNetState(; kw...) - block = compile_block(f, inputs, state) - constants = [ k for (k,v) in sort(collect(state.constants), by=x->x[2]) ] - return (block, constants) -end - -function compile_block(f::AlgebraicNet.Hom{:linear}, inputs::Vector, - state::AlgebraicNetState)::Block - nin, nout = ndims(dom(f)), ndims(codom(f)) - outputs = genvars(state, nout) - @assert length(inputs) == nin - - value = first(f) - if value isa Symbol - value = genconst(state, value) - elseif value isa AbstractMatrix - value = Expr(:call, - Expr(:curly, GlobalRef(StaticArrays, :SMatrix), size(value)...), - value...) - end - lhs = nout == 1 ? outputs[1] : Expr(:tuple, outputs...) - rhs = if nin == 0 - 0 - elseif nin == 1 - Expr(:call, :(*), value, inputs[1]) - else - Expr(:call, :(*), value, Expr(:vect, inputs...)) - end - Block(Expr(:(=), lhs, rhs), inputs, outputs) -end - -function compile_block(f::AlgebraicNet.Hom{:constant}, inputs::Vector, - state::AlgebraicNetState)::Block - @assert isempty(inputs) - outputs = genvars(state, ndims(codom(f))) - value = first(f) - value = isa(value, Symbol) ? genconst(state, value) : value - lhs = length(outputs) == 1 ? outputs[1] : Expr(:tuple, outputs...) - Block(Expr(:(=), lhs, value), inputs, outputs) -end - -# FIXME: Wiring layers are gone. Migrate to another data structure? -#= -function compile_block(f::AlgebraicNet.Hom{:wiring}, inputs::Vector, - state::CompileState)::Block - nout = ndims(codom(f)) - outputs = genvars(state, nout) - terms = [ [] for i in 1:nout ] - for (src, tgts) in first(f).wires - x = inputs[src] - for (tgt, c) in tgts - push!(terms[tgt], c == 1 ? x : Expr(:call, :(.*), c, x)) - end - end - code = multiple_assign_expr(outputs, map(sum_expr, terms)) - Block(code, inputs, outputs) -end -=# - -function compile_block(f::AlgebraicNet.Hom{:mmerge}, inputs::Vector, - state::CompileState)::Block - out = genvar(state) - code = Expr(:(=), out, sum_expr(inputs)) - Block(code, inputs, [out]) -end - -function compile_block(f::AlgebraicNet.Hom{:create}, inputs::Vector, - state::CompileState)::Block - nout = ndims(codom(f)) - outputs = genvars(state, nout) - code = multiple_assign_expr(outputs, zeros(nout)) - Block(code, inputs, outputs) -end - -""" Generate Julia expression for morphism generator in algebraic net. -""" -function generator_expr(f::AlgebraicNet.Hom{:generator}, inputs::Vector, - state::AlgebraicNetState) - value = first(f) - if isempty(inputs) - # If nullary, a constant, possibly symbolic. - isa(value, Symbol) ? genconst(state, value) : value - else - # Otherwise, a broadcasting function call. - Expr(:(.), value, Expr(:tuple, inputs...)) - end -end - -""" Generate Julia expression for single or multiple assignment. -""" -function multiple_assign_expr(lhs::Vector, rhs::Vector)::Expr - @assert length(lhs) == length(rhs) - if length(lhs) == 1 - Expr(:(=), lhs[1], rhs[1]) - else - Expr(:(=), Expr(:tuple, lhs...), Expr(:tuple, rhs...)) - end -end - -""" Generate Julia expression for sum of zero, one, or more terms. -""" -function sum_expr(T::Type, terms::Vector) - if length(terms) == 0 - zero(T) - elseif length(terms) == 1 - terms[1] - else - Expr(:call, GlobalRef(Base, :broadcast), :(+), terms...) - end -end -sum_expr(terms::Vector) = sum_expr(Float64, terms) - -""" Generate a constant (symbol or expression). - -See also `gensym` and `genvar`. -""" -function genconst(state::AlgebraicNetState, name::Symbol) - i = get!(state.constants, name, length(state.constants)+1) - state.constants_sym == Symbol() ? name : :($(state.constants_sym)[$i]) -end - -# Evaluation -############ - -function evaluate_hom(f::AlgebraicNet.Hom{:generator}, xs::Vector; kw...) - value = first(f) - y = if value isa Symbol && head(dom(f)) != :munit - # Broadcasting function call. - getfield(Main, value).(xs...) - else - value - end - y isa Tuple ? collect(y) : [y] -end - -function evaluate_hom(f::AlgebraicNet.Hom{:wiring}, xs::Vector; kw...) - # XXX: We can't properly preallocate the y's because we don't their dims. - ys = repeat(Any[0.0], ndims(codom(f))) - for (src, tgts) in first(f).wires - for (tgt, c) in tgts - y = c * xs[src] - ys[tgt] == 0 ? (ys[tgt] = y) : (ys[tgt] += y) - end - end - ys -end - -evaluate_hom(f::AlgebraicNet.Hom{:mmerge}, xs::Vector; kw...) = [.+(xs...)] -evaluate_hom(f::AlgebraicNet.Hom{:create}, xs::Vector; kw...) = zeros(ndims(codom(f))) -evaluate_hom(f::AlgebraicNet.Hom{:linear}, xs::Vector; kw...) = first(f) * xs -evaluate_hom(f::AlgebraicNet.Hom{:constant}, xs::Vector; kw...) = first(f) - -# Display -######### - -""" Denote composition by a semicolon ala the computer scientists. - -In this context, `⋅` is too easily confused for multiplication, ` ` (space) is -too implicit, and `∘` has a right-to-left connotation. -""" -function show_latex(io::IO, expr::AlgebraicNet.Hom{:compose}; paren::Bool=false, kw...) - show_latex_infix(io, expr, ";"; paren=paren, kw...) -end -function show_unicode(io::IO, expr::AlgebraicNet.Hom{:compose}; kw...) - show_unicode_infix(io, expr, "; "; kw...) -end - -function show_latex(io::IO, expr::AlgebraicNet.Hom{:linear}; kw...) - print(io, "\\mathop{\\mathrm{linear}}\\left[$(first(expr))\\right]") -end -function show_unicode(io::IO, expr::AlgebraicNet.Hom{:linear}; kw...) - print(io, "linear{$(first(expr))}") -end -function show_latex(io::IO, expr::AlgebraicNet.Hom{:constant}; kw...) - print(io, first(expr)) -end -function show_unicode(io::IO, expr::AlgebraicNet.Hom{:constant}; kw...) - print(io, first(expr)) -end - -end diff --git a/experiments/CompAlgebra/src/CompAlgebra.jl b/experiments/CompAlgebra/src/CompAlgebra.jl deleted file mode 100644 index 0059dd0e8..000000000 --- a/experiments/CompAlgebra/src/CompAlgebra.jl +++ /dev/null @@ -1,6 +0,0 @@ -module CompAlgebra - -include("AlgebraicNets.jl") -include("MathFormulas.jl") - -end diff --git a/experiments/CompAlgebra/src/MathFormulas.jl b/experiments/CompAlgebra/src/MathFormulas.jl deleted file mode 100644 index 7949964e6..000000000 --- a/experiments/CompAlgebra/src/MathFormulas.jl +++ /dev/null @@ -1,510 +0,0 @@ -""" Formulas (expression trees) for computer algebra. - -This module is *not* an implementation of a conventional, general-purpose CAS. -There are already many highly developed computer algebra systems. The -objectives are to: - -- represent formulas as expression trees, whenever that is convenient -- display formulas in conventional mathematical notation with free variables -- allow interoperation with existing computer algebra systems -""" -module MathFormulas -export Formula, head, args, first, last, to_formula, to_wiring_diagram, - show_latex, show_latex_formula, show_sexpr - -import Base: first, last, show -import Base.Meta: show_sexpr - -using StructEquality -using Match - -using Catlab.GATs -import Catlab.GATs: head, args, show_latex -using Catlab.GATs.MetaUtils: strip_lines -using Catlab.WiringDiagrams -import Catlab.WiringDiagrams: to_wiring_diagram - -using ..AlgebraicNets -import ..AlgebraicNets: Ob, Hom, compose, id, dom, codom, otimes, munit, braid, - mcopy, delete, mmerge, create, linear, constant, evaluate - -# Data types -############ - -""" An expression tree for computer algebra. - -We call the expression trees "formulas" to avoid confusion with Julia -expressions (`Base.Expr`) or GAT expressions (`Catlab.GATs.GATExpr`). Usually -the operations (head symbols) are interpreted as Julia functions, e.g., `:/` is -right multiplication by the matrix pseudoinverse while `:./` is the elementwise -division. -""" -@struct_hash_equal struct Formula - head::Symbol - args::Vector - Formula(head::Symbol, args...) = new(head, collect(args)) - Formula(sexp::Tuple) = new(sexp[1], - [ x isa Tuple ? Formula(x) : x for x in sexp[2:end] ]) -end -head(form::Formula) = form.head -args(form::Formula) = form.args -first(form::Formula) = first(args(form)) -last(form::Formula) = last(args(form)) - -# Conversion -############ - -""" Convert Julia expression to formula. - -Only a subset of the Julia syntax is supported. -""" -function to_formula(expr::Expr) - expr_to_formula(strip_lines(expr, recurse=true)) -end - -function expr_to_formula(expr) - @match expr begin - Expr(:call, [args...]) => Formula(map(expr_to_formula, args)...) - Expr(:->, [var::Symbol, body]) => Formula(:->, var, expr_to_formula(body)) - Expr(:&&, [args...]) => Formula(:&, map(expr_to_formula, args)...) - Expr(:||, [args...]) => Formula(:|, map(expr_to_formula, args)...) - Expr(:block, [arg]) => expr_to_formula(arg) - Expr(:block, _) => error("Cannot parse multiline block as formula") - symbol::Symbol => symbol - literal::Number => literal - _ => error("Cannot parse expression as formula: $expr") - end -end - -""" Convert algebraic network to formula. - -Assumes that the network has a single output. -""" -function to_formula(f::AlgebraicNet.Hom, vars::Vector{Symbol}) - formulas = functor((NFormula, Formulas), f) - @assert codom(formulas).n == 1 - substitute(formulas.terms[1], Dict(zip(formulas.vars, vars))) -end - -struct NFormula - n::Int -end -struct Formulas - terms::Vector - vars::Vector{Symbol} -end - -""" Algebraic networks realized by formulas with free variables. - -These methods should only be used with `gensym`-ed variables since they assume -that any two formulas have disjoint variables. -""" -@instance ThAlgebraicNet{NFormula, Formulas} begin - dom(f::Formulas) = NFormula(length(f.inputs)) - codom(f::Formulas) = NFormula(length(f.terms)) - - function compose(f1::Formulas, f2::Formulas)::Formulas - replacements = Dict(zip(f2.vars, f1.terms)) - terms = [ substitute(term, replacements) for term in f2.terms ] - Formulas(terms, f1.vars) - end - - function id(A::NFormula)::Formulas - vars = gensyms(A) - Formulas(vars, vars) - end - - munit(::Type{NFormula}) = NFormula(0) - otimes(A::NFormula, B::NFormula) = NFormula(A.n + B.n) - - function otimes(b1::Formulas, b2::Formulas)::Formulas - Formulas([b1.terms; b2.terms], [b1.vars; b2.vars]) - end - function braid(A::NFormula, B::NFormula)::Formulas - v1, v2 = gensyms(A), gensyms(B) - Formulas([v2; v1], [v1; v2]) - end - - mcopy(A::NFormula) = mcopy(A, 2) - mmerge(A::NFormula) = mmerge(A, 2) - delete(A::NFormula) = Formulas([], gensyms(A)) - create(A::NFormula) = Formulas(zeros(Int, A.n), []) - - function linear(value::Any, A::NFormula, B::NFormula)::Formulas - nin, nout = A.n, B.n - @assert nin == 1 && nout == 1 # FIXME: Relax this. - var = gensym() - Formulas([Formula(:*, value, var)], [var]) - end - - function constant(x::Any, A::NFormula)::Formulas - @assert A.n == 1 - Formulas([x], Symbol[]) - end -end - -# FIXME: Wiring layers are gone. Migrate to another data structure? -#= -function wiring(f::Any, A::NFormula, B::NFormula)::Formulas - vars = gensyms(A) - terms = [ [] for i in 1:B.n ] - for (src, tgts) in f.wires - x = vars[src] - for (tgt, c) in tgts - push!(terms[tgt], c == 1 ? x : Formula(:*, c, x)) - end - end - Formulas(map(sum_formulas, terms), vars) -end -=# - -function mcopy(A::NFormula, n::Int)::Formulas - vars = gensyms(A) - Formulas(reduce(vcat, fill(vars, n)), vars) -end - -function mmerge(A::NFormula, n::Int)::Formulas - vars = gensyms(A.n * n) - Formulas([sum_formulas(vars[i:A.n:end]) for i in 1:A.n], vars) -end - -Ob(::Type{NFormula}, value::Any) = NFormula(1) - -function Hom(value::Any, A::NFormula, B::NFormula)::Formulas - nin, nout = A.n, B.n - @assert nout == 1 - vars = gensyms(nin) - term = if isa(value, Symbol) && nin >= 1 - Formula(value, vars...) - else - value - end - Formulas([term], vars) -end - -gensyms(n::Int) = [ gensym() for i in 1:n ] -gensyms(n::Int, tag) = [ gensym(tag) for i in 1:n ] -gensyms(A::NFormula, args...) = gensyms(A.n, args...) - -""" Simultaneous substitution of variables in formula. -""" -function substitute(form::Formula, subst::Dict) - Formula(head(form), [substitute(arg, subst) for arg in args(form)]...) -end - -""" Simultaneous substitution of symbols in Julia expression. -""" -function substitute(expr::Expr, subst::Dict) - Expr(expr.head, [substitute(arg, subst) for arg in expr.args]...) -end -substitute(sym::Symbol, subst::Dict) = get(subst, sym, sym) -substitute(x::Any, subst::Dict) = x - -""" Create formula for sum of zero, one, or more terms. -""" -function sum_formulas(T::Type, terms::Vector) - if length(terms) == 0 - zero(T) - elseif length(terms) == 1 - terms[1] - else - Formula(:+, terms...) - end -end -sum_formulas(terms::Vector) = sum_formulas(Int, terms) - -""" Convert a formula, or formulas, to a wiring diagram. - -The wiring diagram has an input for each symbol in `vars` and an output for -each given formula. All terminal symbols not appearing in `vars` are treated as -symbolic constants. - -The algorithm creates wiring diagrams in normal form for a cartesian category, -meaning that subformulas are maximally shared (cf. `normalize_copy!` for wiring -diagrams and the congruence closure algorithm). -""" -function to_wiring_diagram(formula::Formula, vars::Vector{Symbol}; kw...) - to_wiring_diagram([formula], vars; kw...) -end - -function to_wiring_diagram(formulas::Vector{Formula}, vars::Vector{Symbol}; - to_box::Function=to_untyped_box, - to_port::Function=to_untyped_port) - diagram = WiringDiagram(map(to_port, vars), map(to_port, formulas)) - - term_map = Dict{Union{Formula,Symbol},Tuple{Int,Int}}(( - var => (input_id(diagram), i) for (i, var) in enumerate(vars) - )) - function visit(term::Formula) - get!(term_map, term) do - preds = map(visit, args(term)) - v = add_box!(diagram, to_box(term)) - add_wires!(diagram, (pred => (v, i) for (i, pred) in enumerate(preds))) - (v,1) - end - end - function visit(sym::Symbol) - get!(term_map, sym) do - v = add_box!(diagram, to_box(sym)) - (v,1) - end - end - function visit(num::Number) - v = add_box!(diagram, to_box(num)) - (v,1) - end - - outs = map(visit, formulas) - add_wires!(diagram, - (out => (output_id(diagram), i) for (i, out) in enumerate(outs))) - diagram -end - -to_untyped_box(form::Formula) = - Box(head(form), repeat([nothing], length(args(form))), [nothing]) -to_untyped_box(x) = Box(x, Nothing[], [nothing]) -to_untyped_port(x) = nothing - -# Evaluation -############ - -""" Evaluate a formula, optionally with vectorization. -""" -function evaluate(form::Formula, env::Dict; vector::Bool=true) - evaluate_formula(form, env; vector=vector) -end - -function evaluate_formula(form::Formula, env::Dict; vector::Bool=true) - f = evaluate_formula(head(form), env) - f_args = (evaluate_formula(arg, env) for arg in args(form)) - vector ? f.(f_args...) : f(f_args...) -end - -function evaluate_formula(name::Symbol, env::Dict; kw...) - # Look up name in formula environment. - get(env, name) do - # Failing that, look up name as field of Julia module. - # XXX: Should the module always be Main? - getfield(Main, name) - end -end - -evaluate_formula(literal::Number, env; kw...) = literal - -# Pretty-print -############## - -function show(io::IO, form::Formula) - print(io, "Formula(") - join(io, [ sprint(show, x) for x in [head(form); args(form)] ], ", ") - print(io, ")") -end - -function show(io::IO, ::MIME"text/latex", form::Formula) - print(io, "\$") - show_latex(io, form) - print(io, "\$") -end - -""" Show the expression in infix notation using LaTeX math. - -Does *not* include `\$` or `\\[begin|end]{equation}` delimiters. -""" -show_latex(form::Formula) = show_latex(stdout, form) -show_latex(io::IO, form::Formula) = show_latex_formula(io, form) - -""" Show the formula as an S-expression. - -Cf. the standard library function `Meta.show_sexpr`. -""" -show_sexpr(form::Formula) = show_sexpr(stdout, form) - -function show_sexpr(io::IO, form::Formula) - print(io, "(") - join(io, [sprint(show, head(form)); - [sprint(show_sexpr, arg) for arg in args(form)]], " ") - print(io, ")") -end - -# Show terminal nodes as LaTeX. - -function show_latex_formula(io::IO, bool::Bool; kw...) - print(io, bool ? "\\top" : "\\bot") -end - -function show_latex_formula(io::IO, num::Number; kw...) - if num < 0 - # Treat negative literals as compound expressions. - show_latex_formula(io, Formula(:-, abs(num)); kw...) - else - print(io, isinf(num) ? "\\infty" : num) - end -end - -function show_latex_formula(io::IO, sym::Symbol; kw...) - if sym in latex_symbol_aliases - print(io, "\\", sym) - else - print(io, get(latex_symbol_table, sym) do - length(string(sym)) == 1 ? sym : "\\mathrm{$sym}" - end) - end -end - -# Show non-terminal nodes as LaTeX. - -function show_latex_formula(io::IO, form::Formula; kw...) - # Special case: Dispatch on special LaTeX commands (e.g., \frac). - headsym = head(form) - if haskey(latex_command_table, headsym) - return show_latex_command(io, form, headsym; kw...) - end - - # Special case: Dispatch on operators (prefix, infix, and postfix). - nargs = length(args(form)) - if nargs == 1 - if haskey(latex_prefix_table, headsym) - return show_latex_prefix(io, form, headsym; kw...) - end - if haskey(latex_postfix_table, headsym) - return show_latex_postfix(io, form, headsym; kw...) - end - elseif nargs > 1 - if haskey(latex_infix_table, headsym) - return show_latex_infix(io, form, headsym; kw...) - end - end - - # General case: Dispatch on head symbol as value type. - show_latex_formula(io, form, Val{headsym}; kw...) -end - -# Default LaTeX pretty-printer. - -function show_latex_formula(io::IO, form::Formula, ::Type; kw...) - show_latex_formula(io, head(form)) - print(io, "\\left(") - join(io, [sprint(show_latex_formula, arg) for arg in args(form)], ",") - print(io, "\\right)") -end - -# Special LaTeX pretty-printers. - -function show_latex_formula(io::IO, form::Formula, ::Type{Val{:^}}; - parent_precedence::Int=typemax(Int), kw...) - @assert length(args(form)) == 2 - precedence = 2 - latex_paren_when(io, precedence >= parent_precedence) do - show_latex_formula(io, first(form); parent_precedence=precedence, kw...) - print(io, "^{") - show_latex_formula(io, last(form)) - print(io, "}") - end -end -function show_latex_formula(io::IO, form::Formula, ::Type{Val{:.^}}; kw...) - show_latex_formula(io, form, Val{:^}; kw...) -end - -# LaTeX pretty-printers for unary and binary operators. - -function show_latex_infix(io::IO, form::Formula, sym::Symbol; - parent_precedence::Int=typemax(Int), kw...) - op, precedence = latex_infix_table[sym] - sep = if isempty(op) - any(isa(x,Number) for x in args(form)[2:end]) ? " \\cdot " : " " - else - " $op " - end - show_child = (io, form) -> - show_latex_formula(io, form; parent_precedence=precedence, kw...) - latex_paren_when(io, precedence >= parent_precedence) do - join(io, [sprint(show_child, arg) for arg in args(form)], sep) - end -end - -function show_latex_prefix(io::IO, form::Formula, sym::Symbol; - parent_precedence::Int=typemax(Int), kw...) - @assert length(args(form)) == 1 - op, precedence = latex_prefix_table[sym] - latex_paren_when(io, precedence >= parent_precedence) do - print(io, op, " ") - show_latex_formula(io, first(form); parent_precedence=precedence, kw...) - end -end - -function show_latex_postfix(io::IO, form::Formula, sym::Symbol; - parent_precedence::Int=typemax(Int), kw...) - @assert length(args(form)) == 1 - op, precedence = latex_postfix_table[sym] - latex_paren_when(io, precedence >= parent_precedence) do - show_latex_formula(io, first(form); parent_precedence=precedence, kw...) - print(io, " ", op) - end -end - -function show_latex_command(io::IO, form::Formula, sym::Symbol; kw...) - cmd = latex_command_table[sym] - print(io, cmd, "{") - join(io, [sprint(show_latex_formula, arg) for arg in args(form)], "}{") - print(io, "}") -end - -function latex_paren_when(f::Function, io::IO, paren::Bool) - if (paren) print(io, "\\left(") end - f() - if (paren) print(io, "\\right)") end -end - -# Operator precedence follows the usual conventions of mathematics. -# -# https://en.wikipedia.org/wiki/Order_of_operations -# https://rosettacode.org/wiki/Operator_precedence#Mathematica - -const latex_infix_table = Dict{Symbol,Tuple{String,Int}}( - #:^ => ("^", 2), - #:.^ => ("^", 2), - :* => ("", 4), - :.* => ("", 4), - :+ => ("+", 5), - :- => ("-", 5), - :(==) => ("=", 6), - :!= => ("\\neq", 6), - :< => ("<", 6), - :> => (">", 6), - :<= => ("\\leq", 6), - :>= => ("\\geq", 6), - :in => ("\\in", 6), - :isa => (":", 6), - :& => ("\\wedge", 8), - :| => ("\\vee", 8), - :-> => ("\\mapsto", 9), -) -const latex_prefix_table = Dict{Symbol,Tuple{String,Int}}( - :- => ("-", 3), - :! => ("\\neg", 3), -) -const latex_postfix_table = Dict{Symbol,Tuple{String,Int}}( - :factorial => ("!", 2), -) - -const latex_command_table = Dict{Symbol,String}( - :/ => "\\frac", - :./ => "\\frac", - :binomial => "\\binom", - :sqrt => "\\sqrt", -) -const latex_symbol_table = Dict{Symbol,String}( - :Inf => "\\infty", - :Int => "\\mathbb{Z}", :Integer => "\\mathbb{Z}", - :Signed => "\\mmathbb{Z}", :Unsigned => "\\mathbb{N}", - :Real => "\\mathbb{R}", :Complex => "\\mathbb{C}", -) -const latex_symbol_aliases = Set{Symbol}([ - :alpha, :beta, :gamma, :Gamma, :delta, :Delta, :epsilon, :varepsilon, - :zeta, :eta, :theta, :vartheta, :Theta, :iota, :kappa, :lambda, :Lambda, - :mu, :nu, :xi, :Xi, :pi, :Pi, :rho, :varrho, :sigma, :Sigma, :tau, - :upsilon, :Upsilon, :phi, :varphi, :Phi, :chi, :psi, :Psi, :omega, :Omega, - :exp, :log, :sin, :cos, :tan, -]) - -end diff --git a/experiments/CompAlgebra/test/AlgebraicNets.jl b/experiments/CompAlgebra/test/AlgebraicNets.jl deleted file mode 100644 index 31fcd528c..000000000 --- a/experiments/CompAlgebra/test/AlgebraicNets.jl +++ /dev/null @@ -1,184 +0,0 @@ -module TestAlgebraicNets - -import Pkg -using Test - -using Catlab.GATs -using CompAlgebra.AlgebraicNets - -R = Ob(AlgebraicNet, :R) - -# Compilation and evaluation -############################ - -# Generator -x = collect(range(-2,stop=2,length=50)) -f = Hom(:sin,R,R) -f_comp = compile(f) -@test f_comp(x) == sin.(x) -f_comp = compile(f,args=[:x]) -@test f_comp(x) == sin.(x) -@test evaluate(f,x) == sin.(x) - -# Composition -f = compose(linear(2,R,R), Hom(:sin,R,R)) -f_comp = compile(f) -@test f_comp(x) == sin.(2x) -@test evaluate(f,x) == sin.(2x) - -f = compose(linear(2,R,R), Hom(:sin,R,R), linear(2,R,R)) -f_comp = compile(f) -@test f_comp(x) == 2*sin.(2x) -@test evaluate(f,x) == 2*sin.(2x) - -# Monoidal product -y = collect(range(0,stop=4,length=50)) -f = otimes(Hom(:cos,R,R), Hom(:sin,R,R)) -f_comp = compile(f) -@test f_comp(x,y) == (cos.(x),sin.(y)) -f_comp = compile(f,args=[:x,:y]) -@test f_comp(x,y) == (cos.(x),sin.(y)) -@test evaluate(f,x,y) == (cos.(x),sin.(y)) - -# Braiding -f = braid(R,R) -f_comp = compile(f) -@test f_comp(x,y) == (y,x) -@test evaluate(f,x,y) == (y,x) - -f = compose(braid(R,R), otimes(Hom(:cos,R,R), Hom(:sin,R,R))) -f_comp = compile(f) -@test f_comp(x,y) == (cos.(y),sin.(x)) -@test evaluate(f,x,y) == (cos.(y),sin.(x)) - -# Diagonals -f = mcopy(R) -f_comp = compile(f) -@test f_comp(x) == (x,x) -@test evaluate(f,x) == (x,x) - -f = mcopy(R,3) -f_comp = compile(f) -@test f_comp(x) == (x,x,x) -@test evaluate(f,x) == (x,x,x) - -f = compose(mcopy(R), otimes(id(R), delete(R))) -f_comp = compile(f) -@test f_comp(x) == x -@test evaluate(f,x) == x - -f = compose(mcopy(R), otimes(Hom(:cos,R,R), Hom(:sin,R,R))) -f_comp = compile(f) -@test f_comp(x) == (cos.(x),sin.(x)) -@test evaluate(f,x) == (cos.(x),sin.(x)) - -# Codiagonals -z = collect(range(-4,stop=0,length=50)) -f = mmerge(R) -f_comp = compile(f) -@test f_comp(x,y) == @. x+y -@test evaluate(f,x,y) == @. x+y - -f = mmerge(R,3) -f_comp = compile(f) -@test f_comp(x,y,z) == @. x+y+z -@test evaluate(f,x,y,z) == @. x+y+z - -f = compose(otimes(id(R), create(R)), mmerge(R)) -f_comp = compile(f) -@test f_comp(x) == x -@test evaluate(f,x) == x - -# FIXME: Wire layers have been removed. -#= -wires = [ 1 => 3, 1 => 2, 2 => 2, 3 => 2, 3 => 1 ] -f = wiring(wires, otimes(R,R,R), otimes(R,R,R)) -f_comp = compile(f) -@test f_comp(x,y,z) == (z, x+y+z, x) -@test evaluate(f,x,y,z) == (z, x+y+z, x) -=# - -# More complex expressions -f = compose(otimes(id(R),constant(1,R)), mmerge(R)) -f_comp = compile(f) -@test f_comp(x) == @. x+1 -@test evaluate(f,x) == @. x+1 - -f = compose(otimes(id(R),constant([1,1],otimes(R,R))), mmerge(R,3)) -f_comp = compile(f) -@test f_comp(x) ≈ @. x+2 -@test evaluate(f,x) ≈ @. x+2 - -f = compose(mcopy(R), otimes(Hom(:cos,R,R), Hom(:sin,R,R)), mmerge(R)) -f_comp = compile(f) -@test f_comp(x) == @. cos(x) + sin(x) -@test evaluate(f,x) == @. cos(x) + sin(x) - -f = compose(mcopy(R), otimes(id(R),Hom(:cos,R,R)), Hom(:*,otimes(R,R),R)) -f_comp = compile(f) -@test f_comp(x) == @. x * cos(x) -@test evaluate(f,x) == @. x * cos(x) - -f = compose(braid(R,R), otimes(id(R),compose(linear(2,R,R),Hom(:sin,R,R))), - Hom(:*,otimes(R,R),R), linear(2,R,R)) -f_comp = compile(f) -@test f_comp(x,y) == @. 2y * sin(2x) -@test evaluate(f,x,y) == @. 2y * sin(2x) - -# Multidimensional linear maps -A = [1 2; 3 4] -f = compose(linear(A,otimes(R,R),otimes(R,R)), mmerge(R)) -f_comp = compile(f) -target = dropdims(sum([x y]*A', dims=2), dims=2) -@test f_comp(x,y) ≈ target -@test evaluate(f,x,y) ≈ target - -# Compilation with symbolic coefficients -f = linear(:c, R, R) -f_comp = compile(f) -@test f_comp(x,c=1) == x -@test f_comp(x,c=2) == 2x -f_comp, f_const = compile(f, return_constants=true, vector=true) -@test f_const == [:c] -@test f_comp([x],[2]) == 2x - -f = compose(linear(:k,R,R), Hom(:sin,R,R), linear(:A,R,R)) -f_comp = compile(f) -@test f_comp(x,k=1,A=2) == @. 2 * sin(x) -@test f_comp(x,k=2,A=1) == @. sin(2x) -f_comp, f_const = compile(f, return_constants=true, vector=true) -@test f_const == [:k,:A] -@test f_comp([x],[1,2]) == @. 2 * sin(x) -@test f_comp([x],[2,1]) == @. sin(2x) - -f = compose(otimes(id(R),constant(:c,R)), mmerge(R)) -f_comp = compile(f) -@test f_comp(x,c=2) ≈ @. x+2 - -# Display -######### - -unicode(expr::GATExpr) = sprint(show_unicode, expr) -latex(expr::GATExpr) = sprint(show_latex, expr) - -# Generator -f = Hom(:sin,R,R) -@test unicode(f) == "sin" -@test latex(f) == "\\mathrm{sin}" - -# Composition -f = compose(linear(2,R,R), Hom(:sin,R,R)) -@test unicode(f) == "linear{2}; sin" -@test latex(f) == "\\mathop{\\mathrm{linear}}\\left[2\\right] ; \\mathrm{sin}" - -# Monoidal product -f = otimes(Hom(:cos,R,R), Hom(:sin,R,R)) -@test unicode(f) == "cos⊗sin" -@test latex(f) == "\\mathrm{cos} \\otimes \\mathrm{sin}" - -# Diagonals and codiagonals -f = compose(otimes(id(R),constant(1,R)), mmerge(R)) -@test unicode(f) == "(id{R}⊗1); mmerge{R,2}" -@test latex(f) == "\\left(\\mathrm{id}_{R} \\otimes 1\\right) ; \\nabla_{R,2}" - -end diff --git a/experiments/CompAlgebra/test/MathFormulas.jl b/experiments/CompAlgebra/test/MathFormulas.jl deleted file mode 100644 index 1ad716743..000000000 --- a/experiments/CompAlgebra/test/MathFormulas.jl +++ /dev/null @@ -1,152 +0,0 @@ -module TestMathFormulas - -using Test - -using Catlab.WiringDiagrams -using CompAlgebra.AlgebraicNets -using CompAlgebra.MathFormulas - -# Conversion -############ - -# Convert Julia expressions to formulas. -@test to_formula(:(sin(x))) == Formula(:sin, :x) -@test to_formula(:(sin(2x))) == Formula((:sin, (:*, 2, :x))) -@test to_formula(:(2*sin(2x))) == Formula((:*, 2, (:sin, (:*, 2, :x)))) -@test to_formula(:(x == y)) == Formula(:(==), :x, :y) -@test to_formula(:(x -> 2x)) == Formula((:->, :x, (:*, 2, :x))) -@test to_formula(:(A && B)) == Formula(:&, :A, :B) -@test to_formula(:(A || B)) == Formula(:|, :A, :B) - -# Convert algebraic networks to formulas. -R = Ob(AlgebraicNet, :R) -f = Hom(:sin, R, R) -@test to_formula(f, [:x]) == Formula(:sin, :x) - -f = compose(linear(2,R,R), Hom(:sin,R,R)) -@test to_formula(f,[:x]) == Formula((:sin, (:*, 2, :x))) -f = compose(linear(2,R,R), Hom(:sin,R,R), linear(2,R,R)) -@test to_formula(f,[:x]) == Formula((:*, 2, (:sin, (:*, 2, :x)))) - -f = compose(Hom(:cos,R,R), Hom(:sin,R,R), Hom(:tan,R,R)) -@test to_formula(f,[:x]) == Formula((:tan, (:sin, (:cos, :x)))) - -f = compose(otimes(id(R),constant(1,R)), mmerge(R)) -@test to_formula(f,[:x]) == Formula(:+, :x, 1) -@test to_formula(mmerge(R,1),[:x]) == :x -@test to_formula(mmerge(R,3),[:x,:y,:z]) == Formula(:+, :x, :y, :z) - -f = compose(otimes(Hom(:cos,R,R), Hom(:sin,R,R)), mmerge(R)) -@test to_formula(f,[:x,:y]) == Formula((:+, (:cos, :x), (:sin, :y))) -f = compose(mcopy(R), otimes(Hom(:cos,R,R), Hom(:sin,R,R)), mmerge(R)) -@test to_formula(f,[:x]) == Formula((:+, (:cos, :x), (:sin, :x))) - -# FIXME: Wiring layers have been removed. -#= -w = [ 1 => 1, 2 => 2, 3 => 1, 4 => 2 ] -f = compose(wiring(w, otimes(R,R,R,R), otimes(R,R)), Hom(:*, otimes(R,R), R)) -@test to_formula(f,[:w,:x,:y,:z]) == Formula((:*, (:+, :w, :y), (:+, :x, :z))) - -f = wiring([ 1 => 1, 1 => 1, 2 => 1 ], otimes(R,R), R) -@test to_formula(f,[:x,:y]) == Formula((:+, (:*, 2, :x), :y)) -=# - -# Convert formulas to wiring diagrams. -make_box = (value, arity) -> Box(value, repeat([nothing], arity), [nothing]) - -d = to_wiring_diagram(Formula(:*, :x, :y), [:x, :y]) -@test boxes(d) == [ make_box(:*, 2) ] -v_times, = box_ids(d) -@test wires(d) == map(Wire, [ - (input_id(d), 1) => (v_times, 1), - (input_id(d), 2) => (v_times, 2), - (v_times, 1) => (output_id(d), 1), -]) - -d = to_wiring_diagram(Formula((:+, (:cos, :x), (:sin, :x))), [:x]) -@test boxes(d) == [ make_box(:cos, 1), make_box(:sin, 1), make_box(:+, 2) ] -v_cos, v_sin, v_plus = box_ids(d) -@test wires(d) == map(Wire, [ - (input_id(d), 1) => (v_cos, 1), - (input_id(d), 1) => (v_sin, 1), - (v_cos, 1) => (v_plus, 1), - (v_sin, 1) => (v_plus, 2), - (v_plus, 1) => (output_id(d), 1), -]) - -d = to_wiring_diagram(Formula((:*, (:cos, :x), (:cos, :x))), [:x]) -@test boxes(d) == [ make_box(:cos, 1), make_box(:*, 2) ] -v_cos, v_times = box_ids(d) -@test wires(d) == map(Wire, [ - (input_id(d), 1) => (v_cos, 1), - (v_cos, 1) => (v_times, 1), - (v_cos, 1) => (v_times, 2), - (v_times, 1) => (output_id(d), 1), -]) - -# Evaluation -############ - -# Standard evaluation. -@test evaluate(Formula(:sin, :x), Dict(:x => pi/2)) == sin(pi/2) -@test evaluate(Formula(:(==), :x, :y), Dict(:x => 1, :y => 1)) == true - -# Vectorized evaluation. -x = collect(range(0, stop=2pi, length=10)) -@test evaluate(Formula(:sin, :x), Dict(:x => x)) == sin.(x) -@test evaluate(Formula((:*, (:sin, :x), (:cos, :x))), Dict(:x => x)) == - @. sin(x) * cos(x) - -# Pretty-print -############## - -latex(form::Formula) = sprint(show_latex, form) -sexpr(form::Formula) = sprint(show_sexpr, form) - -# LaTeX pretty-print. -@test latex(Formula(:f, :x , :y)) == "f\\left(x,y\\right)" -@test latex(Formula(:cos, :x)) == "\\cos\\left(x\\right)" -@test latex(Formula(:+, :x, :y)) == "x + y" -@test latex(Formula(:+, :x, :y, :z)) == "x + y + z" -@test latex(Formula(:+, :alpha, :beta)) == "\\alpha + \\beta" -@test latex(Formula(:-, :x, :y)) == "x - y" -@test latex(Formula(:-, :x)) == "- x" -@test latex(Formula(:*, :x, :y)) == "x y" -@test latex(Formula(:.*, :x, :y)) == "x y" -@test latex(Formula(:*, 2, :x)) == "2 x" -@test latex(Formula(:*, 2, 2)) == "2 \\cdot 2" -@test latex(Formula(:/, :x, :y)) == "\\frac{x}{y}" -@test latex(Formula(:./, :x, :y)) == "\\frac{x}{y}" -@test latex(Formula((:*, :x, (:+, :y, :z)))) == "x \\left(y + z\\right)" -@test latex(Formula(:^, :x, :y)) == "x^{y}" -@test latex(Formula(:.^, :x, :y)) == "x^{y}" -@test latex(Formula(:+, Inf, 1)) == "\\infty + 1" -@test latex(Formula((:^, (:+, :x, :y), 2))) == "\\left(x + y\\right)^{2}" -@test latex(Formula((:^, 2, (:+, :x, :y)))) == "2^{x + y}" -@test latex(Formula((:^, (:^, :x, :a), :b))) == "\\left(x^{a}\\right)^{b}" -@test latex(Formula(:factorial, :x)) == "x !" -@test latex(Formula(:(==), :x, :y)) == "x = y" -@test latex(Formula(:<, :x, :y)) == "x < y" -@test latex(Formula(:<=, :x, :y)) == "x \\leq y" -@test latex(Formula(:&, :A, :B)) == "A \\wedge B" -@test latex(Formula(:|, true, false)) == "\\top \\vee \\bot" -@test latex(Formula(:!, :A)) == "\\neg A" - -# Operator precedence in LaTeX pretty-print. -@test latex(Formula((:+, (:*, :a, :b), (:*, :c, :d)))) == "a b + c d" -@test latex(Formula((:*, (:+, :a, :b), (:+, :c, :d)))) == - "\\left(a + b\\right) \\left(c + d\\right)" -@test latex(Formula((:(==), (:+, :x, :y), (:+, :y, :x)))) == "x + y = y + x" -@test latex(Formula((:&, (:(==), :x, :y), (:(==), :z, :w)))) == - "x = y \\wedge z = w" -@test latex(Formula((:-, (:+, :x, :y)))) == "- \\left(x + y\\right)" -@test_skip latex(Formula((:+, :x, (:-, :y)))) == "x + \\left(- y\\right)" -@test latex(Formula((:factorial, (:*, :m, :n)))) == "\\left(m n\\right) !" -@test latex(Formula(:^, -1, :n)) == "\\left(- 1\\right)^{n}" - -# S-expression pretty-print. -@test sexpr(Formula(:f)) == "(:f)" -@test sexpr(Formula(:+, :x, :y)) == "(:+ :x :y)" -@test sexpr(Formula((:*, :x, (:+, :y, 1)))) == "(:* :x (:+ :y 1))" - -end diff --git a/experiments/CompAlgebra/test/runtests.jl b/experiments/CompAlgebra/test/runtests.jl deleted file mode 100644 index 21f105ca2..000000000 --- a/experiments/CompAlgebra/test/runtests.jl +++ /dev/null @@ -1,9 +0,0 @@ -using Test - -@testset "AlgebraicNets" begin - include("AlgebraicNets.jl") -end - -@testset "MathFormulas.jl" begin - include("MathFormulas.jl") -end diff --git a/experiments/Markov/Project.toml b/experiments/Markov/Project.toml index adf1b116e..29be0ba11 100644 --- a/experiments/Markov/Project.toml +++ b/experiments/Markov/Project.toml @@ -6,4 +6,5 @@ version = "0.1.0" [deps] Catlab = "134e5e36-593f-5add-ad60-77f754baafbe" Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" +GATlab = "f0ffcf3b-d13a-433e-917c-cc44ccf5ead2" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/experiments/Markov/src/MarkovCategories.jl b/experiments/Markov/src/MarkovCategories.jl index 572c3e136..65f2fb9ee 100644 --- a/experiments/Markov/src/MarkovCategories.jl +++ b/experiments/Markov/src/MarkovCategories.jl @@ -3,8 +3,9 @@ export ThMarkovCategory, FreeMarkovCategory, Ob, Hom, dom, codom, compose, ⋅, ∘, otimes, ⊗, braid, mcopy, Δ, delete, ◊, expectation, 𝔼 -using Catlab.GATs, Catlab.Theories, Catlab.WiringDiagrams -import Catlab.GATs: show_latex +using GATlab +using Catlab.Theories, Catlab.WiringDiagrams +import GATlab: show_latex import Catlab.Theories: Ob, Hom, dom, codom, compose, ⋅, ∘, otimes, ⊗, braid, mcopy, Δ, delete, ◊ @@ -13,12 +14,12 @@ import Catlab.Theories: Ob, Hom, dom, codom, compose, ⋅, ∘, otimes, ⊗, bra """ Theory of *Markov categories with expectation* """ -@signature ThMarkovCategory{Ob,Hom} <: ThMonoidalCategoryWithDiagonals{Ob,Hom} begin - expectation(M::(A → B))::(A → B) <= (A::Ob, B::Ob) +@signature ThMarkovCategory <: ThMonoidalCategoryWithDiagonals begin + expectation(M::(A → B))::(A → B) ⊣ [A::Ob, B::Ob] @op (𝔼) := expectation end -@syntax FreeMarkovCategory{ObExpr,HomExpr} ThMarkovCategory begin +@symbolic_model FreeMarkovCategory{ObExpr,HomExpr} ThMarkovCategory begin otimes(A::Ob, B::Ob) = associate_unit(new(A,B), munit) otimes(f::Hom, g::Hom) = associate(new(f,g)) compose(f::Hom, g::Hom) = associate_unit(new(f,g; strict=true), id) @@ -33,9 +34,9 @@ end # Wiring diagrams ################# -mcopy(A::Ports{ThMarkovCategory}, n::Int) = implicit_mcopy(A, n) +mcopy(A::Ports{ThMarkovCategory.Meta.T}, n::Int) = implicit_mcopy(A, n) -function expectation(M::WiringDiagram{ThMarkovCategory}) +function expectation(M::WiringDiagram{ThMarkovCategory.Meta.T}) if nboxes(M) <= 1 functor(M, identity, expectation_box) else diff --git a/experiments/Markov/src/StochExpr.jl b/experiments/Markov/src/StochExpr.jl index e517e2226..d4798d175 100644 --- a/experiments/Markov/src/StochExpr.jl +++ b/experiments/Markov/src/StochExpr.jl @@ -1,6 +1,6 @@ export crand -using Catlab.GATs, Catlab.Theories +using GATlab, Catlab.Theories # How do you give semantics to a stochastic map? You call it. diff --git a/experiments/Markov/test/MarkovCategories.jl b/experiments/Markov/test/MarkovCategories.jl index 687ec6b6c..ea44c9c37 100644 --- a/experiments/Markov/test/MarkovCategories.jl +++ b/experiments/Markov/test/MarkovCategories.jl @@ -1,7 +1,7 @@ module TestMarkovCategories using Test -using Catlab.GATs, Catlab.WiringDiagrams +using GATlab, Catlab.WiringDiagrams using Markov.MarkovCategories # Theories @@ -23,7 +23,7 @@ M = Hom(:M, A, B) # Wiring diagrams ################# -A, B, C = [ Ports{ThMarkovCategory}([sym]) for sym in [:A, :B, :C] ] +A, B, C = [ Ports{ThMarkovCategory.Meta.T}([sym]) for sym in [:A, :B, :C] ] M = singleton_diagram(ThMarkovCategory, Box(:M,[:A],[:B])) N = singleton_diagram(ThMarkovCategory, Box(:N,[:B],[:C])) diff --git a/experiments/Markov/test/StochMapsExpr.jl b/experiments/Markov/test/StochMapsExpr.jl index f0858b1f2..a7251d409 100644 --- a/experiments/Markov/test/StochMapsExpr.jl +++ b/experiments/Markov/test/StochMapsExpr.jl @@ -1,6 +1,6 @@ using Test -using Catlab.GATs, Catlab.Theories +using GATlab, Catlab.Theories using Markov X = Ob(FreeCartesianCategory, :Float64) diff --git a/test/Project.toml b/test/Project.toml index dd534a3b9..0a1303586 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,5 +1,6 @@ [deps] ACSets = "227ef7b5-1206-438b-ac65-934d6da304b8" +AlgebraicInterfaces = "23cfdc9f-0504-424a-be1f-4892b28e2f0c" Catlab = "134e5e36-593f-5add-ad60-77f754baafbe" Compose = "a81c6b42-2e10-5240-aca2-a61377ecd94b" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" diff --git a/test/categorical_algebra/DataMigrations.jl b/test/categorical_algebra/DataMigrations.jl index f14dc492c..671d23b20 100644 --- a/test/categorical_algebra/DataMigrations.jl +++ b/test/categorical_algebra/DataMigrations.jl @@ -1,7 +1,7 @@ module TestDataMigrations using Test -using Catlab.GATs, Catlab.Theories, Catlab.Graphs, Catlab.CategoricalAlgebra +using GATlab, Catlab.Theories, Catlab.Graphs, Catlab.CategoricalAlgebra using Catlab.Programs.DiagrammaticPrograms @present SchSet(FreeSchema) begin diff --git a/test/programs/DiagrammaticPrograms.jl b/test/programs/DiagrammaticPrograms.jl index dfa70f017..809f8cc1d 100644 --- a/test/programs/DiagrammaticPrograms.jl +++ b/test/programs/DiagrammaticPrograms.jl @@ -1,7 +1,7 @@ module TestDiagrammaticPrograms using Test -using Catlab.GATs, Catlab.Graphs, Catlab.CategoricalAlgebra +using GATlab, Catlab.Graphs, Catlab.CategoricalAlgebra using Catlab.Theories: FreeCategory, FreePointedSetCategory, FreePointedSetSchema using Catlab.Programs.DiagrammaticPrograms, Catlab.CategoricalAlgebra.DataMigrations using Catlab.Graphs.BasicGraphs: NamedGraph From f46491970eac678d5b7cca68f25f421643a3828a Mon Sep 17 00:00:00 2001 From: Owen Lynch Date: Thu, 12 Oct 2023 15:52:12 -0700 Subject: [PATCH 05/10] fix benchmarks --- benchmark/Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/benchmark/Project.toml b/benchmark/Project.toml index 8146378d6..e9acf7bf5 100644 --- a/benchmark/Project.toml +++ b/benchmark/Project.toml @@ -2,6 +2,7 @@ BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" Catlab = "134e5e36-593f-5add-ad60-77f754baafbe" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" +GATlab = "f0ffcf3b-d13a-433e-917c-cc44ccf5ead2" Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" MetaGraphs = "626554b9-1ddb-594c-aa3c-2596fe9399a5" PkgBenchmark = "32113eaa-f34f-5b0d-bd6c-c81e245fc73d" From d82a11e45f3f67d7963abee318296c965d062e29 Mon Sep 17 00:00:00 2001 From: Owen Lynch Date: Fri, 13 Oct 2023 16:55:01 -0700 Subject: [PATCH 06/10] add test for GATContext -> Schema --- src/categorical_algebra/ACSetsGATsInterop.jl | 16 +++++++++------- test/categorical_algebra/ACSetsGATsInterop.jl | 11 +++++++++++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/categorical_algebra/ACSetsGATsInterop.jl b/src/categorical_algebra/ACSetsGATsInterop.jl index 930c34b09..9f739b977 100644 --- a/src/categorical_algebra/ACSetsGATsInterop.jl +++ b/src/categorical_algebra/ACSetsGATsInterop.jl @@ -28,13 +28,15 @@ end function Schema(c::GATContext) obs, attrtypes = Symbol[], Symbol[] homs, attrs = Tuple{Symbol, Symbol, Symbol}[], Tuple{Symbol, Symbol, Symbol}[] - for binding in p.scope - type = getvalue(binding) - @match (nameof(type.body.head), type.body.args...) begin - (:Ob,) => push!(obs, nameof(binding)) - (:Hom, x, y) => push!(homs, nameof.((binding, x.body, y.body))) - (:AttrType,) => push!(attrtypes, nameof(binding)) - (:Attr, x, y) => push!(attrs, nameof.((binding, x.body, y.body))) + for scope in allscopes(gettypecontext(c)) + for binding in scope + type = getvalue(binding) + @match (nameof(type.body.head), type.body.args...) begin + (:Ob,) => push!(obs, nameof(binding)) + (:Hom, x, y) => push!(homs, nameof.((binding, x.body, y.body))) + (:AttrType,) => push!(attrtypes, nameof(binding)) + (:Attr, x, y) => push!(attrs, nameof.((binding, x.body, y.body))) + end end end BasicSchema(obs,homs,attrtypes,attrs) diff --git a/test/categorical_algebra/ACSetsGATsInterop.jl b/test/categorical_algebra/ACSetsGATsInterop.jl index a244be880..6028b00a2 100644 --- a/test/categorical_algebra/ACSetsGATsInterop.jl +++ b/test/categorical_algebra/ACSetsGATsInterop.jl @@ -1,6 +1,7 @@ module TestACSetsGATsInterop using Test +using GATlab using Catlab.CategoricalAlgebra using Catlab.Graphs: SchGraph, SchWeightedGraph @@ -29,6 +30,15 @@ X, parent, height = SchDendrogram[[:X, :parent, :height]] @test subpart(d, id(X)) == 1:5 @test subpart(d, compose(parent, height)) == [10,10,10,20,20] +@gatcontext SchDendrogram′(ThSchema) begin + X::Ob + R::AttrType + parent::Hom(X,X) + height::Attr(X,R) +end + +@test Schema(SchDendrogram′) == Schema(SchDendrogram) + # JSON serialization #------------------- @@ -44,4 +54,5 @@ for schema in [SchGraph, SchWeightedGraph] @test roundtrip_json_acset_schema(schema) == schema end + end From 2727d32854161ea3498c298f184aaf74f0defaf7 Mon Sep 17 00:00:00 2001 From: Owen Lynch Date: Fri, 13 Oct 2023 17:05:33 -0700 Subject: [PATCH 07/10] uncommented some theory axioms --- src/theories/Limits.jl | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/theories/Limits.jl b/src/theories/Limits.jl index 52e5bee5a..3d4d2db53 100644 --- a/src/theories/Limits.jl +++ b/src/theories/Limits.jl @@ -69,16 +69,16 @@ finite limits". ⊣ [A::Ob, B::Ob, C::Ob, f::(A → B), g::(A → B)]) # Equalizer axioms. - # (incl(eq)⋅f == incl(eq)⋅g - # ⊣ [A::Ob, B::Ob, f::(A → B), g::(A → B), eq::Equalizer(f,g)]) - # (incl(eq) == id(A) - # ⊣ [A::Ob, B::Ob, f::(A → B), eq::Equalizer(f,f)]) - # (factorize(eq,h,eq_h) ⋅ incl(eq) == incl(eq_h) ⋅ h - # ⊣ [A::Ob, B::Ob, C::Ob, f::(A → B), g::(A → B), h::(C → A), - # eq::Equalizer(f,g), eq_h::Equalizer(h⋅f, h⋅g)]) - # (factorize(eq, k⋅incl(eq), eq_k) == k - # ⊣ [A::Ob, B::Ob, D::Ob, f::(A → B), g::(A → B), eq::Equalizer(f,g), - # k::(D → ob(eq)), eq_k::Equalizer(k⋅incl(eq)⋅f, k⋅incl(eq)⋅g)]) + (incl(eq)⋅f == incl(eq)⋅g + ⊣ [A::Ob, B::Ob, f::(A → B), g::(A → B), eq::Equalizer(f,g)]) + (incl(eq) == id(A) + ⊣ [A::Ob, B::Ob, f::(A → B), eq::Equalizer(f,f)]) + (factorize(eq,h,eq_h) ⋅ incl(eq) == incl(eq_h) ⋅ h + ⊣ [A::Ob, B::Ob, C::Ob, f::(A → B), g::(A → B), h::(C → A), + eq::Equalizer(f,g), eq_h::Equalizer(h⋅f, h⋅g)]) + (factorize(eq, k⋅incl(eq), eq_k) == k + ⊣ [A::Ob, B::Ob, D::Ob, f::(A → B), g::(A → B), eq::Equalizer(f,g), + k::(D → ob(eq)), eq_k::Equalizer(k⋅incl(eq)⋅f, k⋅incl(eq)⋅g)]) end using .ThCompleteCategory From 9aa9fde95b73345a975cd8426e4c4abcad81d991 Mon Sep 17 00:00:00 2001 From: Owen Lynch Date: Fri, 13 Oct 2023 17:07:18 -0700 Subject: [PATCH 08/10] bring back homomorphisms test, delete diagrammaticprograms --- test/graphs/GraphGenerators.jl | 6 +- test/programs/DiagrammaticPrograms.jl | 816 -------------------------- 2 files changed, 3 insertions(+), 819 deletions(-) delete mode 100644 test/programs/DiagrammaticPrograms.jl diff --git a/test/graphs/GraphGenerators.jl b/test/graphs/GraphGenerators.jl index 9cc277acc..dc20d8216 100644 --- a/test/graphs/GraphGenerators.jl +++ b/test/graphs/GraphGenerators.jl @@ -2,7 +2,7 @@ module TestGraphGenerators using Test using Catlab.Graphs.BasicGraphs, Catlab.Graphs.GraphGenerators -# using Catlab.CategoricalAlgebra: homomorphisms +using Catlab.CategoricalAlgebra: homomorphisms # Path graphs #------------ @@ -49,12 +49,12 @@ g = wheel_graph(Graph, n) @test (nv(g), ne(g)) == (n, 2(n-1)) triangle = Graph(3) add_edges!(triangle, [1,2,1], [2,3,3]) -# @test length(homomorphisms(triangle, g)) == n-1 +@test length(homomorphisms(triangle, g)) == n-1 g = wheel_graph(SymmetricGraph, n) @test (nv(g), ne(g)) == (n, 4(n-1)) triangle = cycle_graph(SymmetricGraph, 3) -# @test length(homomorphisms(triangle, g)) == 6(n-1) # == 3! * (n-1) +@test length(homomorphisms(triangle, g)) == 6(n-1) # == 3! * (n-1) # Parallel arrows #---------------- diff --git a/test/programs/DiagrammaticPrograms.jl b/test/programs/DiagrammaticPrograms.jl deleted file mode 100644 index 809f8cc1d..000000000 --- a/test/programs/DiagrammaticPrograms.jl +++ /dev/null @@ -1,816 +0,0 @@ -module TestDiagrammaticPrograms -using Test - -using GATlab, Catlab.Graphs, Catlab.CategoricalAlgebra -using Catlab.Theories: FreeCategory, FreePointedSetCategory, FreePointedSetSchema -using Catlab.Programs.DiagrammaticPrograms, Catlab.CategoricalAlgebra.DataMigrations -using Catlab.Graphs.BasicGraphs: NamedGraph -using Catlab.Programs.DiagrammaticPrograms: get_keyword_arg_val, destructure_unary_call -using Catlab.WiringDiagrams.CPortGraphs -@present SchSet(ThSchema) begin - X::Ob -end -@present SchDDS <: SchSet begin - Φ::Hom(X,X) -end - -# Graphs -######## - -g = @graph begin - s - t - s → t - s → t -end -@test subpart(g,:vname) == [:s,:t] - -g = @graph NamedGraph{Symbol,Symbol} begin - x, y - (f, g): x → y -end -@test g == parallel_arrows(NamedGraph{Symbol,Symbol}, 2, - V=(vname=[:x,:y],), E=(ename=[:f,:g],)) - -tri_parsed = @graph NamedGraph{Symbol,Symbol} begin - v0, v1, v2 - fst: v0 → v1 - snd: v1 → v2 - comp: v0 → v2 -end -tri = @acset NamedGraph{Symbol,Symbol} begin - V = 3 - E = 3 - src = [1,2,1] - tgt = [2,3,3] - vname = [:v0, :v1, :v2] - ename = [:fst, :snd, :comp] -end -@test tri_parsed == tri - -# Categories -############ - -Δ¹_parsed = @fincat begin - V, E - (δ₀, δ₁): V → E - σ₀: E → V - - σ₀ ∘ δ₀ == id(V) - σ₀ ∘ δ₁ == id(V) -end -Δ¹_graph = @acset NamedGraph{Symbol,Symbol} begin - V = 2 - E = 3 - src = [1,1,2] - tgt = [2,2,1] - vname = [:V, :E] - ename = [:δ₀, :δ₁, :σ₀] -end -Δ¹ = FinCat(Δ¹_graph, [ [1,3] => empty(Path, Δ¹_graph, 1), - [2,3] => empty(Path, Δ¹_graph, 1) ]) -@test Δ¹_parsed == Δ¹ - -# Functors -########## - -# Underlying graph of circular port graph. -F = @finfunctor SchGraph SchCPortGraph begin - V => Box - E => Wire - src => src ⨟ box - tgt => tgt ⨟ box -end -@test F == FinFunctor(Dict(:V => :Box, :E => :Wire), - Dict(:src => [:src, :box], :tgt => [:tgt, :box]), - SchGraph, SchCPortGraph) - -# Incomplete definitions. -@test_throws ErrorException begin - @finfunctor SchGraph SchCPortGraph begin - V => Box - end -end -@test_throws ErrorException begin - @finfunctor SchGraph SchCPortGraph begin - V => Box - src => src ⨟ box - tgt => tgt ⨟ box - end -end - -# Failure of functorality. -@test_throws ErrorException begin - @finfunctor SchGraph SchCPortGraph begin - V => Box - E => Wire - src => src - tgt => tgt - end -end - -# Extra definitions. -@test_throws ErrorException begin - @finfunctor SchGraph SchReflexiveGraph begin - V => Box - E => Wire - src => src - tgt => tgt - refl => refl - end -end - -# GAT expressions. -F = @finfunctor SchDDS SchDDS begin - X => X; Φ => id(X) -end -F′ = @finfunctor SchDDS SchDDS begin - X => X; Φ => id{X} -end -@test F == F′ - -# Diagrams -########## - -C = FinCat(SchGraph) -d = @diagram C begin - v::V - (e1, e2)::E - (t: e1 → v)::tgt - (s: e2 → v)::src -end -J = FinCat(@present P(FreeCategory) begin - (v,e1,e2)::Ob - t::Hom(e1,v) - s::Hom(e2,v) -end) -F_parsed = diagram(d) -@test ob_generators(dom(F_parsed)) == ob_generators(J) -F = FinFunctor(Dict(:v=>:V,:e1=>:E,:e2=>:E), - Dict(:t=>:tgt,:s=>:src), J, C) -@test F_parsed.ob_map == F.ob_map - -d = @diagram SchGraph begin - v => V - (e1, e2) => E - t: e1 → v => tgt - s: e2 → v => src -end -@test all([ob_map(diagram(d),x) == ob_map(F,x) for x in ob_generators(dom(F))]) - -d = @diagram SchGraph begin - v::V - (e1, e2)::E - (e1 → v)::tgt - (e2 → v)::src -end -F_parsed = diagram(d) -J_parsed = dom(F_parsed) -@test src(graph(J_parsed)) == src(graph(J)) -@test tgt(graph(J_parsed)) == tgt(graph(J)) - -d′ = @free_diagram SchGraph begin - v::V - (e1, e2)::E - tgt(e1) == v - v == src(e2) -end -@test d′ == d - -d = @free_diagram SchGraph begin - (e1, e2)::E - tgt(e1) == src(e2) -end -@test is_functorial(diagram(d)) -@test collect_ob(d) == SchGraph[[:E, :E, :V]] -@test collect_hom(d) == SchGraph[[:tgt, :src]] - -d = @diagram SchDDS begin - x::X - (f: x → x)::(Φ⋅Φ) -end -@test only(collect_ob(d)) == SchDDS[:X] -@test only(collect_hom(d)) == compose(SchDDS[:Φ], SchDDS[:Φ]) - -# Diagrams with parameters - #------------------------- - - d = @free_diagram SchWeightedGraph begin - v::V - (e1, e2)::E - tgt(e1) == v - src(e2) == v - w :: Weight - w == 5.0 - weight(e1) == w - weight(e2) == w -end -@test sort(nameof.(collect_ob(d))) == [:E,:E,:V,:Weight] -@test sort(nameof.(collect_hom(d))) == [:src, :tgt, :weight, :weight] -@test d.params == Dict(:w => 5.0) - -d = @free_diagram SchWeightedGraph begin - (e1, e2)::E - tgt(e1) == src(e2) - weight(e1) == 0.5 - weight(e2) == 1.5 -end -@test sort(nameof.(collect_ob(d))) == [:E, :E, :V, :Weight, :Weight] -@test sort(nameof.(collect_hom(d))) == [:src, :tgt,:weight, :weight] -@test sort(collect(values(d.params))) == [0.5,1.5] -# Migrations -############ - -# Pullback migration -#------------------- - -# Graph with reversed edges. -M = @migration SchGraph SchGraph begin - V => V - E => E - src => tgt - tgt => src -end -@test M isa DataMigrations.DeltaSchemaMigration -@test functor(M) == FinFunctor(Dict(:V => :V, :E => :E), - Dict(:src => :tgt, :tgt => :src), - SchGraph, SchGraph) - -# Variant where target schema is not given. -M = @migration SchGraph begin - V => V - E => E - (src: E → V) => tgt - (tgt: E → V) => src -end -J = FinCat(@present P(ThSchema) begin - (V,E)::Ob - (src,tgt)::Hom(E,V) -end) - -@test functor(M) == FinDomFunctor(Dict(:E=>:E,:V=>:V), Dict(:tgt=>:src,:src=>:tgt), J, FinCat(SchGraph)) - -# Conjunctive migration -#---------------------- - -# Graph with edges that are paths of length 2. -M = @migration SchGraph SchGraph begin - V => V - E => @join begin - v::V - (e₁, e₂)::E - (e₁ → v)::tgt - (e₂ → v)::src - end - src => e₁ ⋅ src - tgt => e₂ ⋅ tgt -end -#Syntactic variant -@migration SchGraph SchGraph begin - V => V - E => @join begin - v::V - (e₁, e₂)::E - (e₁ → v)::tgt - (e₂ → v)::src - end - src => src∘e₁ - tgt => tgt∘e₂ -end -@test M isa DataMigrations.ConjSchemaMigration -F = functor(M) -F_E = diagram(ob_map(F, :E)) -@test nameof.(collect_ob(F_E)) == [:V, :E, :E] -@test nameof.(collect_hom(F_E)) == [:tgt, :src] -F_tgt = hom_map(F, :tgt) -@test collect_ob(F_tgt)[1][2] == (:V => SchGraph[:tgt]) - -# Syntactic variant of above. -M′ = @migration SchGraph SchGraph begin - V => V - E => @join begin - v::V - (e₁, e₂)::E - tgt(e₁) == v - src(e₂) == v - end - src => src(e₁) - tgt => tgt(e₂) -end -@test functor(M′) == F - -# "Bouquet graph" on set. -# This is the right adjoint to the underlying edge set functor. -M = @migration SchGraph SchSet begin - V => @product begin end - E => X - src => begin end - tgt => begin end -end -@test M isa DataMigrations.ConjSchemaMigration -@test isempty(shape(ob_map(functor(M), :V))) - -# Syntactic variant of above. -M′ = @migration SchGraph SchSet begin - V => @unit - E => X -end -@test M′ isa DataMigrations.ConjSchemaMigration -@test isempty(shape(ob_map(functor(M′), :V))) -@test isempty(shape(ob_map(functor(M),:V))) - -# Cartesian product of graph with itself. -M = @migration SchGraph SchGraph begin - V => @product (v₁::V; v₂::V) - E => @product (e₁::E; e₂::E) - src => (v₁ => e₁⋅src; v₂ => e₂⋅src) - tgt => (v₁ => e₁⋅tgt; v₂ => e₂⋅tgt) -end -F = functor(M) -F_V = diagram(ob_map(F, :V)) -@test collect_ob(F_V) == fill(SchGraph[:V], 2) -s = hom_generators(dom(F))[1] -F_src = hom_map(F, :src) -@test components(diagram_map(F_src)) == Dict(:v₂=>s,:v₁=>s) - -# Reflexive graph from graph. -M = @migration SchReflexiveGraph SchGraph begin - V => @join begin - v::V - ℓ::E - (s: ℓ → v)::src - (t: ℓ → v)::tgt - end - E => @join begin - (v₁, v₂)::V - (ℓ₁, ℓ₂, e)::E - (s₁: ℓ₁ → v₁)::src - (t₁: ℓ₁ → v₁)::tgt - (s₂: ℓ₂ → v₂)::src - (t₂: ℓ₂ → v₂)::tgt - (s: e → v₁)::src - (t: e → v₂)::tgt - end - refl => begin - (v₁, v₂) => v - (ℓ₁, ℓ₂, e) => ℓ - (s₁, s₂, s) => s - (t₁, t₂, t) => t - end - src => begin - v => v₁; ℓ => ℓ₁; s => s₁; t => t₁ - end - tgt => begin - v => v₂; ℓ => ℓ₂; s => s₂; t => t₂ - end -end -F = functor(M) -F_tgt = hom_map(F, :tgt) -@test ob_map(F_tgt, :v)[2] == id(SchGraph[:V]) - -# Free/initial port graph on a graph. -# This is the left adjoint to the underlying graph functor. -M = @migration SchGraph begin - Box => V - Wire => E - InPort => @join begin - v => V - e::E - (t: e → v)::tgt - end - OutPort => @join begin - v::V - e::E - (s: e → v)::src - end - (in_port_box: InPort → Box) => v - (out_port_box: OutPort → Box) => v - (src: Wire → OutPort) => begin - v => src - end - (tgt: Wire → InPort) => begin - v => tgt - end -end -F = functor(M) -F_src = hom_map(F, :src) -@test collect_ob(F_src) == [(SchGraph[:E], :v=>SchGraph[:src]),(SchGraph[:E],:e=>id(SchGraph[:E]))] -@test collect_hom(F_src) == [id(SchGraph[:E])] - -#XX:Yoneda is really slow -yGraph = yoneda(Graph) - -@migration(SchGraph, begin - I => @join begin v::V end -end) - -@test is_isomorphic( - @acset(Graph, begin E=2;V=3;src=[1,2];tgt=[2,3] end), - @acset_colim(yGraph, begin (e1,e2)::E; src(e1) == tgt(e2) end) -) - -# Gluing migration -#----------------- -# Discrete graph on set. -# This is the left adjoint to the underlying vertex set functor. -M = @migration SchGraph SchSet begin - V => X - E => @empty -end - -# Coproduct of graph with itself. -M = @migration SchGraph SchGraph begin - V => @cases (v₁::V; v₂::V) - E => @cases (e₁::E; e₂::E) - src => begin - e₁ => v₁ ∘ src - e₂ => v₂ ∘ src - end - tgt => begin - e₁ => v₁ ∘ tgt - e₂ => v₂ ∘ tgt - end -end -@test M isa DataMigrations.GlueSchemaMigration -F = functor(M) -F_V = diagram(ob_map(F, :V)) -@test collect_ob(F_V) == fill(SchGraph[:V], 2) -F_src = hom_map(F, :src) -@test [x[2] for x in collect_ob(F_src)] == [:(e₁) => SchGraph[:src], :(e₂) => SchGraph[:src]] - -# Free reflexive graph on a graph. -M = @migration SchReflexiveGraph SchGraph begin - V => V - E => @cases (e::E; v::V) - src => (e => src) - tgt => (e => tgt) - refl => v -end -F = functor(M) -F_tgt = hom_map(F, :tgt) -@test ob_map(F_tgt,:e) == (SchGraph[:V],SchGraph[:tgt]) - -# Vertices in a graph and their connected components. -M = @migration SchGraph begin - V => V - Component => @glue begin - e::E; v::V - (e → v)::src - (e → v)::tgt - end - (component: V → Component) => v -end -F = functor(M) -F_C = diagram(ob_map(F, :Component)) -@test ob_map(F_C,:e) == SchGraph[:E] -@test nameof.(collect_hom(F_C)) == [:src, :tgt] - -# Gluc migration -#--------------- - -# Labeled graph with edges that are paths of length <= 2. -M = @migration SchLabeledGraph SchLabeledGraph begin - V => V - E => @cases begin - v => V - e => E - path => @join begin - v::V - (e₁, e₂)::E - (e₁ → v)::tgt - (e₂ → v)::src - end - end - src => begin - e => src - path => e₁⋅src - end - tgt => begin - e => tgt - path => e₂⋅tgt - end - Label => Label - label => label -end - - -F = functor(M) -@test ob_map(F, :V) isa DataMigrations.GlucQuery -@test M isa DataMigrations.GlucSchemaMigration -F_src = hom_map(F, :src) -x = only(ob_generators(dom(diagram(ob_map(F,:V))))) -@test collect_ob(shape_map(F_src)) == fill(x,3) -F_src_v, F_src_e, F_src_path = collect(values(components(diagram_map(F_src)))) -@test collect_ob(F_src_v) == [(SchGraph[:V], SchGraph[:V]=>id(SchGraph[:V]))] -@test collect_ob(F_src_e) == [(Ob(FreeCategory,:(e₁)), :V => SchGraph[:src])] -@test collect_ob(F_src_path) == [(SchGraph[:E], :V => SchGraph[:src])] - -# Graph with edges that are minimal paths b/w like vertices in bipartite graph. -M = @migration SchGraph SchBipartiteGraph begin - V => @cases (v₁::V₁; v₂::V₂) - E => @cases begin - e₁ => @join begin - v₂::V₂; e₁₂::E₁₂; e₂₁::E₂₁ - (e₁₂ → v₂)::tgt₂ - (e₂₁ → v₂)::src₂ - end - e₂ => @join begin - v₁::V₁; e₂₁::E₂₁; e₁₂::E₁₂ - (e₂₁ → v₁)::tgt₁ - (e₁₂ → v₁)::src₁ - end - end - src => begin - e₁ => v₁ ∘ (e₁₂ ⋅ src₁) - e₂ => v₂ ∘ (e₂₁ ⋅ src₂) - end - tgt => begin - e₁ => v₁ ∘ (e₂₁ ⋅ tgt₁) - e₂ => v₂ ∘ (e₁₂ ⋅ tgt₂) - end -end -F = functor(M) -@test ob_map(F, :V) isa DataMigrations.GlucQuery -@test M isa DataMigrations.GlucSchemaMigration -F_src = hom_map(F, :src) -@test collect_ob(shape_map(F_src)) == [Ob(FreeCategory.Ob,:(v₁)),Ob(FreeCategory.Ob,:(v₂))] -F_src1, F_src2 = collect(values(components(diagram_map(F_src)))) -@test collect_ob(F_src1) == [(Ob(FreeCategory.Ob,:(e₁₂)), :V₁ => SchBipartiteGraph[:src₁])] -@test collect_ob(F_src2) == [(Ob(FreeCategory.Ob,:(e₂₁)), :V₂ => SchBipartiteGraph[:src₂])] - -# Box product of reflexive graph with itself. -M = @migration SchReflexiveGraph SchReflexiveGraph begin - V => @product (v₁::V; v₂::V) - E => @glue begin - vv => @product (v₁::V; v₂::V) - ev => @product (e₁::E; v₂::V) - ve => @product (v₁::V; e₂::E) - (refl₁: vv → ev) => (e₁ => v₁⋅refl; v₂ => v₂) - (refl₂: vv → ve) => (v₁ => v₁; e₂ => v₂⋅refl) - end - src => begin - ev => (v₁ => e₁⋅src; v₂ => v₂) - ve => (v₁ => v₁; v₂ => e₂⋅src) - end - tgt => begin - ev => (v₁ => e₁⋅tgt; v₂ => v₂) - ve => (v₁ => v₁; v₂ => e₂⋅tgt) - end - refl => vv -end -F = functor(M) -@test ob_map(F, :V) isa DataMigrations.GlucQuery -@test M isa DataMigrations.GlucSchemaMigration -F_src = hom_map(F, :src) -x = only(ob_generators(codom(shape_map(F_src)))) -@test collect_ob(shape_map(F_src)) == fill(x,3) -F_src_vv, F_src_ev, F_src_ve = [components(diagram_map(F_src))[a] for a in [:vv,:ev,:ve]] -@test map(last,collect_ob(F_src_ev)) == [:v₂=>id(SchGraph[:V]), - :v₁=> SchGraph[:src]] - -#Little parsing functions -@test get_keyword_arg_val(:(x=3)) == 3 -@test_throws ErrorException get_keyword_arg_val(:("not an assignment!"+3)) -@test destructure_unary_call(:(f(g(x)))) == (:(f∘g),:x) - -# Migrations with code -# -#------------------------------------ -@present SchMechLink <: SchGraph begin - Pos::AttrType - Len::AttrType - pos::Attr(V,Pos) - len::Attr(E,Len) -end -@acset_type MechLink(SchMechLink, index=[:src,:tgt]) - -G = @acset MechLink{Vector{Float64},Float64} begin - V = 3 - E = 2 - src = [1,2] - tgt = [2,3] - len = [1.0,1.0] - pos = [[1.0,1.0,1.0],[2.0,2.0,2.0],[2.0,2.0,1.0]] -end - -#Rotate the whole linkage by a bit -M = @migration SchMechLink SchMechLink begin - V => V - E => E - Pos => Pos - Len => Len - src => src - tgt => tgt - pos => begin - θ = π/5 - M = [[cos(θ),sin(θ),0] [-sin(θ),cos(θ),0] [0,0,1]] - x -> M*pos(x) - end - len => len -end -@test length(M.params) ==1 && M.params[:pos] isa Function -@test hom_map(functor(M),:pos) isa FreePointedSetSchema.Attr{:zeromap} -#Filter impossible edges out of a mechanical linkage -M = @migration SchMechLink SchMechLink begin - V => V - E => @join begin - e :: E - L :: Len - (l:e→L) :: (x->len(x)^2) - (d:e→L) :: (x->sum((pos(src(x))-pos(tgt(x))).^2)) - end - Pos => Pos - Len => Len - src => src(e) - tgt => tgt(e) - pos => pos - len => len(e) -end -migE = ob_map(functor(M),:E) -@test migE isa QueryDiagram -ps = ob_map(functor(M),:E).params -@test length(ps) == 2 -@test length(M.params) == 0 -#variant -M′ = @migration SchMechLink begin - V => V - E => @join begin - e :: E - L :: Len - (l:e→L) :: (x->len(x)^2) - (d:e→L) :: (x->sum((pos(src(x))-pos(tgt(x))).^2)) - end - Pos => Pos - Len => Len - (src:E→V) => src(e) - (tgt:E→V) => tgt(e) - (pos:V→Pos) => pos - (len:E→Len) => len(e) -end -F,F′ = functor(M),functor(M′) -@test all([ob_map(F,a)==ob_map(F′,a) for a in [:V,:Pos,:Len]]) -#The two migrations aren't perfectly equal because of intensional equality of -#Julia functions. -@test diagram(ob_map(F,:E)) == diagram(ob_map(F′,:E)) -@test all([hom_map(F,a) == hom_map(F′,a) for a in [:src,:tgt,:pos,:len]]) -#Also, the domains are only isomorphic because -#presentations involve meaningless ordering of the generators. -@test ob_generators(dom(F)) == ob_generators(dom(F′)) -#Filter impossible edges out of a mechanical linkage while rotating -M = @migration SchMechLink SchMechLink begin - V => V - E => @join begin - e :: E - L :: Len - (l:e→L) :: (x->len(x)^2) - (d:e→L) :: (x->sum((pos(src(x))-pos(tgt(x))).^2)) - end - Pos => Pos - Len => Len - src => src(e) - tgt => tgt(e) - pos => begin - θ = π/5 - M = [[cos(θ),sin(θ),0] [-sin(θ),cos(θ),0] [0,0,1]] - x -> M*pos(x) - end - len => len(e) -end -@test length(M.params) ==1 && length(ob_map(functor(M),:E).params) == 2 -#Filter out impossible edges, but then weirdly double all the lengths -M = @migration SchMechLink begin - V => V - E => @join begin - e :: E - L :: Len - (l:e→L) :: (x->len(x)^2) - (d:e→L) :: (x->sum((pos(src(x))-pos(tgt(x))).^2)) - end - Pos => Pos - Len => Len - (src:E→V) => src(e) - (tgt:E→V) => tgt(e) - (pos:V→Pos) => pos - (len:E→Len) => (len(e)|>(x->2x)) -end -#unabstracting x->2x over the unused variables -#for the functions in the acset to be migrated -f = M.params[:len](:src,:tgt,:pos,:len) -using Random.Random -xs = rand(Float64,100) -@test all([f(x)==2*x for x in xs]) -#Variant -M′ = @migration SchMechLink SchMechLink begin - V => V - E => @join begin - e :: E - L :: Len - (l:e→L) :: (x->len(x)^2) - (d:e→L) :: (x->sum((pos(src(x))-pos(tgt(x))).^2)) - end - Pos => Pos - Len => Len - src => src(e) - tgt => tgt(e) - pos => pos - len => (len(e)|>(x->2x)) -end -F,F′ = functor(M),functor(M′) -@test all([ob_map(F,a)==ob_map(F′,a) for a in [:V,:Pos,:Len]]) -@test diagram(ob_map(F,:E)) == diagram(ob_map(F′,:E)) - -#disjoint union linkage with itself, second copy reflected through origin -M = @migration SchMechLink SchMechLink begin - V => @cases (v₁::V;v₂::V) - E=> @cases (e₁::E;e₂::E) - Pos => Pos - Len => Len - src => begin - e₁ => v₁ ∘ src - e₂ => v₂ ∘ src - end - tgt => begin - e₁ => v₁ ∘ tgt - e₂ => v₂ ∘ tgt - end - pos => begin - v₁ => pos - v₂ => (pos|> (x-> -x)) - end - len => (e₁ => len ; e₂ => len) -end -@test isempty(M.params) -M_pos = hom_map(functor(M),:pos) -@test only(keys(M_pos.params)) == :(v₂) -f = M_pos.params[:(v₂)](:src,:tgt,:pos,:len) -xs = rand(Float64,100) -@test all([f(x)==-x for x in xs]) -@test (SchMechLink[:Pos],:(v₂)=>SchMechLink[:pos]) in collect_ob(M_pos) - -M′ = @migration SchMechLink SchMechLink begin - V => @cases (v₁::V;v₂::V) - E=> @cases (e₁::E;e₂::E) - Pos => Pos - Len => Len - src => begin - e₁ => v₁ ∘ src - e₂ => v₂ ∘ src - end - tgt => begin - e₁ => v₁ ∘ tgt - e₂ => v₂ ∘ tgt - end - pos => begin - v₁ => pos - v₂ => (x->-pos(x)) - end - len => (e₁ => len ; e₂ => len) -end -@test isempty(M′.params) -M′_pos = hom_map(functor(M′),:pos) -#XX:need to build querydiagramhom -#= -@test only(keys(M′_pos.params)) == :(v₂) -f = M′_pos.params[:(v₂)](:src,:tgt,:pos,:len) -xs = rand(Float64,100) -@test all([f(x)==-x for x in xs]) -=# - -#Filter impossible edges and also make a copy reflected through the -#origin. -M = @migration SchMechLink SchMechLink begin - V => @cases (v₁::V;v₂::V) - E=> @cases begin - e₁ => @join begin - e :: E - L :: Len - (l:e→L) :: (x->len(x)^2) - (d:e→L) :: (x->sum((pos(src(x))-pos(tgt(x))).^2)) - end - e₂ => @join begin - e :: E - L :: Len - (l:e→L) :: (x->len(x)^2) - (d:e→L) :: (x->sum((pos(src(x))-pos(tgt(x))).^2)) - end -end - Pos => Pos - Len => Len - src => begin - e₁ => v₁ ∘ (e⋅src) - e₂ => v₂ ∘ (e⋅src) - end - tgt => begin - e₁ => v₁ ∘ (e⋅tgt) - e₂ => v₂ ∘ (e⋅tgt) - end - pos => begin - v₁ => pos - v₂ => (pos|> (x-> -x)) - end - len => (e₁ => e⋅len ; e₂ => e⋅len) -end -@test isempty(M.params) -@test length(hom_map(functor(M),:pos).params) == 1 - -end \ No newline at end of file From efae01db470fe8d09845af208c43535bcab5a4d4 Mon Sep 17 00:00:00 2001 From: Owen Lynch Date: Mon, 16 Oct 2023 15:54:34 -0700 Subject: [PATCH 09/10] use new Meta API for syntax modules --- src/programs/ParseJuliaPrograms.jl | 2 +- src/wiring_diagrams/Expressions.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/programs/ParseJuliaPrograms.jl b/src/programs/ParseJuliaPrograms.jl index 8b19c75dd..16449f149 100644 --- a/src/programs/ParseJuliaPrograms.jl +++ b/src/programs/ParseJuliaPrograms.jl @@ -109,7 +109,7 @@ end """ Make a lookup table assigning names to generators or term constructors. """ function make_lookup_table(pres::Presentation, syntax_module::Module, names) - theory = syntax_module.THEORY_MODULE.Meta.theory + theory = syntax_module.Meta.theory terms = Set(nameof.(keys(theory.resolvers))) table = Dict{Symbol,Any}() diff --git a/src/wiring_diagrams/Expressions.jl b/src/wiring_diagrams/Expressions.jl index f745207ee..afe754854 100644 --- a/src/wiring_diagrams/Expressions.jl +++ b/src/wiring_diagrams/Expressions.jl @@ -25,7 +25,7 @@ using ..DirectedWiringDiagrams: WiringDiagramGraph """ Convert a morphism expression into a wiring diagram. """ function to_wiring_diagram(expr::GATExpr, args...) - T = syntax_module(expr).THEORY_MODULE.Meta.T + T = syntax_module(expr).Meta.theory_type to_wiring_diagram(T, expr, args...) end function to_wiring_diagram(T::Type, expr::GATExpr) From 3b9e3041cf3c3f480c4cc1ec92de32306393a0e4 Mon Sep 17 00:00:00 2001 From: Owen Lynch Date: Mon, 16 Oct 2023 17:27:52 -0700 Subject: [PATCH 10/10] fixed some docs --- docs/src/index.md | 2 +- src/theories/MonoidalAdditive.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/index.md b/docs/src/index.md index 104321663..2d19fa475 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -58,7 +58,7 @@ diagrams, Petri nets, and the like. ### What is a GAT? -See [GATlab documentation](https://algebraicjulia.github.io/Gatlab.jl) +See [GATlab documentation](https://algebraicjulia.github.io/GATlab.jl) ## Conventions diff --git a/src/theories/MonoidalAdditive.jl b/src/theories/MonoidalAdditive.jl index 6f6655d39..17509229f 100644 --- a/src/theories/MonoidalAdditive.jl +++ b/src/theories/MonoidalAdditive.jl @@ -148,7 +148,7 @@ end """ Theory of *monoidal categories with bidiagonals*, in additive notation -Mathematically the same as (@ref) but +Mathematically the same as [`ThMonoidalCategoryWithBidiagonals`](@ref) but written additively, instead of multiplicatively. """ @theory ThMonoidalCategoryWithBidiagonalsAdditive <: