From cf6370fc5a667023ec20ec3cb20caf4926bdf803 Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Mon, 29 Nov 2021 21:38:23 -0800 Subject: [PATCH] ENH: Colimits of finite sets whose elements are meaningfully named. --- src/categorical_algebra/FinSets.jl | 63 ++++++++++++++++++------- src/categorical_algebra/FreeDiagrams.jl | 4 +- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/src/categorical_algebra/FinSets.jl b/src/categorical_algebra/FinSets.jl index 58ff2d134..f77398250 100644 --- a/src/categorical_algebra/FinSets.jl +++ b/src/categorical_algebra/FinSets.jl @@ -1123,42 +1123,69 @@ end #-------------------- """ Compute colimit of finite sets whose elements are meaningfully named. + +This situation seems to be mathematically uninteresting but is practically +important. The colimit is computed by reduction to the skeleton of **FinSet** +(`FinSet{Int}`) and the names are assigned afterwards, following some reasonable +conventions and add tags where necessary to avoid name clashes. """ struct NamedColimit <: ColimitAlgorithm end -function colimit(::Type{<:Tuple{<:FinSet{S,T},<:FinFunction}}, d) where - {S, T<:Union{Symbol,AbstractString}} +function colimit(::Type{<:Tuple{<:FinSet{<:Any,T},<:FinFunction}}, d) where + {T <: Union{Symbol,AbstractString}} colimit(d, NamedColimit()) end -function colimit(span::Multispan{<:FinSet{S,T}}, ::NamedColimit) where {S,T} - X = skeletize(apex(span), index=false) - feet_skel = map(skeletize, feet(span)) - legs_skel = map((f, Y) -> skeletize(f, X, Y), legs(span), feet_skel) - span_skel = Multispan(dom(X), legs_skel) - colim_skel = colimit(span_skel) +function colimit(d::FixedShapeFreeDiagram{<:FinSet{<:Any,T},Hom}, + alg::NamedColimit) where {T,Hom} + # Reducing to the case of bipartite free diagrams is a bit lazy, but at least + # using `SpecializeColimit` below should avoid some gross inefficiencies. + colimit(BipartiteFreeDiagram{FinSet{<:Any,T},Hom}(d), alg) +end +function colimit(d::BipartiteFreeDiagram{<:FinSet{<:Any,T}}, ::NamedColimit) where T + # Compute colimit of diagram in the skeleton of FinSet (`FinSet{Int}`). + # Note: no performance would be gained by using `DisjointSets{T}` from + # DataStructures.jl because it is just a wrapper around `IntDisjointSets` that + # internally builds the very same indices that we use below. + sets₁_skel = map(set -> skeletize(set, index=false), ob₁(d)) + sets₂_skel = map(set -> skeletize(set, index=true), ob₂(d)) + funcs = map(edges(d)) do e + skeletize(hom(d,e), sets₁_skel[src(d,e)], sets₂_skel[tgt(d,e)]) + end + d_skel = BipartiteFreeDiagram{FinSetInt,eltype(funcs)}() + add_vertices₁!(d_skel, nv₁(d), ob₁=dom.(sets₁_skel)) + add_vertices₂!(d_skel, nv₂(d), ob₂=dom.(sets₂_skel)) + add_edges!(d_skel, src(d), tgt(d), hom=funcs) + colim_skel = colimit(d_skel, SpecializeColimit()) + # Assign elements/names to the colimit set. elems = Vector{T}(undef, length(apex(colim_skel))) - for (ι, Y) in zip(colim_skel, feet_skel) + for (ι, Y) in zip(colim_skel, sets₂_skel) for i in dom(Y) elems[ι(i)] = Y(i) end end - ι = compose(first(span_skel), first(colim_skel)) - for i in dom(X) - elems[ι(i)] = X(i) + # The vector should already be filled, but to reduce arbitrariness we prefer + # names from the layer 1 sets whenever possible. For example, when computing a + # pushout, we prefer names from the apex of cospan to names from the feet. + for (u, X) in zip(vertices₁(d_skel), sets₁_skel) + e = first(incident(d_skel, u, :src)) + f, ι = hom(d_skel, e), legs(colim_skel)[tgt(d_skel, e)] + for i in dom(X) + elems[ι(f(i))] = X(i) + end end + # Eliminate clashes in provisional list of names. unique_by_tagging!(elems) - Z = FinSet(elems) - ιs = map(colim_skel, feet_skel) do ι, Y - FinFunction(Dict(Y(i) => elems[ι(i)] for i in dom(Y)), Z) + ιs = map(colim_skel, sets₂_skel) do ι, Y + FinFunction(Dict(Y(i) => elems[ι(i)] for i in dom(Y)), FinSet(elems)) end - Colimit(span, Multicospan(Z, ιs)) + Colimit(d, Multicospan(FinSet(elems), ιs)) end -function skeletize(set::FinSet; index::Bool=true) - # FIXME: Should support `unique_index`. +function skeletize(set::FinSet; index::Bool=false) + # FIXME: We should support `unique_index` and it should be used here. FinDomFunction(collect(set), set, index=index) end function skeletize(f::FinFunction, X, Y) diff --git a/src/categorical_algebra/FreeDiagrams.jl b/src/categorical_algebra/FreeDiagrams.jl index 8623f35bf..abb5de1ec 100644 --- a/src/categorical_algebra/FreeDiagrams.jl +++ b/src/categorical_algebra/FreeDiagrams.jl @@ -458,7 +458,7 @@ function BipartiteFreeDiagram{Ob,Hom}(F::Functor{<:FinCat{Int}}; end return d end -BipartiteFreeDiagram(F::Functor{<:FinCat{Int},<:TypeCat{Ob,Hom}}; kw...) where {Ob,Hom} = +BipartiteFreeDiagram(F::Functor{<:FinCat{Int},<:Cat{Ob,Hom}}; kw...) where {Ob,Hom} = BipartiteFreeDiagram{Ob,Hom}(F; kw...) # Free diagrams @@ -551,7 +551,7 @@ function FreeDiagram{Ob,Hom}(F::Functor{<:FinCat{Int}}) where {Ob,Hom} diagram[:hom] = collect_hom(F) diagram end -FreeDiagram(F::Functor{<:FinCat{Int},<:TypeCat{Ob,Hom}}) where {Ob,Hom} = +FreeDiagram(F::Functor{<:FinCat{Int},<:Cat{Ob,Hom}}) where {Ob,Hom} = FreeDiagram{Ob,Hom}(F) (::Type{BFD})(diagram::FreeDiagram; kw...) where BFD <: BipartiteFreeDiagram =