Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/categorical_algebra/CSets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ import ...Theories: ob, hom, dom, codom, compose, ⋅, id,
using ..FreeDiagrams, ..Limits, ..Subobjects, ..Sets, ..FinSets, ..FinCats
using ..FinSets: VarFunction, LooseVarFunction, IdentityFunction, VarSet
import ..Limits: limit, colimit, universal
import ...Theories: compose, ⋅, id, meet, ∧, join, ∨, top, ⊤, bottom, ⊥
using ..FreeDiagrams, ..Limits, ..Subobjects, ..FinSets, ..FinCats
import ..Limits: limit, colimit, universal, factorize
import ..Subobjects: Subobject, implies, ⟹, subtract, \, negate, ¬, non, ~
import ..Sets: SetOb, SetFunction, TypeSet
using ..Sets
Expand Down Expand Up @@ -666,7 +669,6 @@ end
@cartesian_monoidal_instance ACSet ACSetTransformation
@cocartesian_monoidal_instance ACSet ACSetTransformation


# Limits and colimits
#####################

Expand Down
54 changes: 54 additions & 0 deletions src/categorical_algebra/HomSearch.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ using ...Theories, ..CSets, ..FinSets, ..FreeDiagrams, ..Subobjects
using ...Graphs.BasicGraphs
using ..CSets: map_components
using ACSets.DenseACSets: attrtype_type, delete_subobj!
import ..Limits: factorize

using Random
using CompTime
Expand Down Expand Up @@ -471,6 +472,59 @@ function escape_assignment_lhs(expr)
end



# Factorization
###############

""" factorize(s::Span; initial=Dict(), single=true, kw...)

Factor a morphism f: A->C by finding a morphism h: B → C such that f=g⋅h.

B
g ↗ ↘ h = ?
A ⟶ C
f

This function assumes that the general form of factorizing involves creating an
"initial" dict for homomorphism search. In some categories this may not be the
case, which would mean factorize_constraints should actually return a dictionary
that gets passed as kwargs to homomorphism search.
"""
function factorize(s::Span; initial=Dict(), single::Bool=true, kw...)
f, g = s
init = factorize_constraints(f,g; initial=initial)
if isnothing(init) return single ? nothing : typeof(f)[] end
search = single ? homomorphism : homomorphisms
search(codom(g), codom(f); initial=NamedTuple(init), kw...)
end

"""
Use the data of f:A->C and g:A->B to initialize search for Hom(B,C)
if f(a) = c, then g(a) must equal c.
"""
function factorize_constraints(f::ACSetTransformation,
g::ACSetTransformation;
initial=Dict())
dom(f) == dom(g) || error("f and g are not a span: $jf \n$jg")
S = acset_schema(dom(f))
res = Dict{Symbol, Dict{Int,Int}}()
for o in ob(S)
init = haskey(initial, o) ? initial[o] : Dict{Int,Int}()
for (a, g_a) in enumerate(collect(g[o]))
f_a = f[o](a)
if haskey(init, g_a)
if init[g_a] != f_a
return nothing
end
else
init[g_a] = f_a
end
end
res[o] = init
end
return res
end

# Maximum Common C-Set
######################

Expand Down
53 changes: 52 additions & 1 deletion src/categorical_algebra/Slices.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ using ...Theories: ThCategory
import ...Theories: dom, codom, compose, id
import ..Limits: limit, colimit, universal
import ..FinSets: force

import ..CSets: is_natural, components
import ..HomSearch: factorize_constraints, homomorphism, homomorphisms, factorize
"""
The data of the object of a slice category (say, some category C sliced over an
object X in Ob(C)) is the data of a homomorphism in Hom(A,X) for some ob A.
Expand Down Expand Up @@ -86,6 +87,9 @@ function slice_diagram(f::FreeDiagram)::FreeDiagram
FreeDiagram(obs,homs)
end

factorize_constraints(f::SliceHom,g::SliceHom; initial=Dict()) =
factorize_constraints(f.f,g.f; initial=initial)

"""
Convert a limit problem in the slice category to a limit problem of the
underlying category.
Expand Down Expand Up @@ -132,4 +136,51 @@ function universal(lim::SliceLimit, sp::Multispan)
return SliceHom(apx, apx2, u)
end

is_natural(x::SliceHom) = is_natural(x.f)
components(x::SliceHom) = components(x.f)
Base.getindex(α::SliceHom, c) = x.f[c]

"""
This could be made more efficient as a constraint *during* homomorphism finding.

This would require implementing a new constraint to homomorphism search that
restricts the codomain for each part of A, i.e. ∀ a ∈ A: h(a) ∈ g⁻¹(f(a)).
"""
function homomorphisms(X::Slice,Y::Slice; kw...)
map(filter(h->force(X.slice)==force(compose(h,Y.slice)),
homomorphisms(dom(X), dom(Y); kw...)) ) do h
SliceHom(X, Y, h)
end |> collect
end

"""
Because the constraint isn't incorporated into the search process, we cannot
stop early.
"""
function homomorphism(X::Slice,Y::Slice; kw...)
hs = homomorphisms(X,Y; kw...)
return isempty(hs) ? nothing : first(hs)
end

"""
Factorizing a cospan is equivalent to looking for a slice morphism. f->g

B
h = ? ↗ ↘ g
A ⟶ C
f
"""
function factorize(s::Cospan; initial=Dict(), single::Bool=true, kw...)
f, g = Slice.(s)
search = single ? homomorphism : homomorphisms
res = search(f, g; initial=NamedTuple(initial), kw...)
if isnothing(res)
return nothing
else
return [x.f for x in res]
end
end



end # module
14 changes: 14 additions & 0 deletions test/categorical_algebra/HomSearch.jl
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,20 @@ end
@test_throws ErrorException @acset_transformation g h begin V = [4,3,2,1]; E = [1,2,3,4] end


# Factorizing morphisms
#----------------------

p2, p3 = path_graph(Graph, 2), path_graph(Graph, 3)
loop = apex(terminal(Graph))
f = ACSetTransformation(Graph(2), p3; V=[2,1])
g1 = ACSetTransformation(Graph(2), p2; V=[1,2])
g2 = ACSetTransformation(Graph(2), p2; V=[2,1])
@test isnothing(factorize(Span(f, g1)))
@test length(factorize(Span(f, g2); single=false)) == 1
f2 = homomorphism(Graph(2), loop)
@test isnothing(factorize(Span(f2, id(Graph(2))); monic=true))
@test factorize(Span(f2, id(Graph(2)))) == f2

# Enumeration of subobjects
###########################

Expand Down
28 changes: 28 additions & 0 deletions test/categorical_algebra/Slices.jl
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,32 @@ slice_dia = FreeDiagram{Slice,SliceHom}(Multispan(A, [f, g]))
clim = colimit(slice_dia)
@test is_isomorphic(dom(apex(clim)), d)


# Factorizing morphisms (morally same tests as in CSets)
#-------------------------------------------------------
p2G, p3G = [path_graph(Graph, x) for x in [3,5]]
p2, p3 = [Slice(homomorphism(p, two)) for p in [p2G,p3G]] # ⊚→□→⊚ and ⊚→□→⊚□→⊚
loop = Slice(id(two)) # ⊚ ↔ □
g2G = Slice(ACSetTransformation(Graph(2), two; V=[1,1])) # ⊚ ⊚
f = SliceHom(g2G, p3, ACSetTransformation(Graph(2), p3G; V=[3,1]))
g1 = SliceHom(g2G, p2, CSetTransformation(Graph(2), p2G; V=[1,3]))
g2 = SliceHom(g2G, p2, CSetTransformation(Graph(2), p2G; V=[3,1]))
@test isnothing(factorize(Span(f, g1)))
@test length(factorize(Span(f, g2); single=false)) == 1
f2 = homomorphism(g2G, loop)
@test isnothing(factorize(Span(f2, id(g2G)); monic=true))
@test factorize(Span(f2, id(g2G))) == f2

# Factorize C-set morphisms where the second one is known
#--------------------------------------------------------

A = path_graph(Graph, 2)
B = @acset Graph begin V=4; E=3; src=[1,1,3]; tgt=[2,4,4] end
C = @acset Graph begin V=2; E=2; src=1; tgt=2 end

f = ACSetTransformation(A, C; V=[1,2], E=[1])
g = ACSetTransformation(B, C; V=[1,2,1,2], E=[1,2,1])

@test length(factorize(Cospan(f,g); single=false)) == 2

end # module