Skip to content

Commit c4dd483

Browse files
author
Kris Brown
committed
factoring morphisms into composites in C-Set as well as slice cats.
1 parent 3f04962 commit c4dd483

File tree

4 files changed

+145
-1
lines changed

4 files changed

+145
-1
lines changed

src/categorical_algebra/CSets.jl

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import ...Schemas: objects, homs, attrtypes, attrs, ob, hom, dom, codom
2727
using ...Columns
2828
import ...Theories: compose, , id, meet, , join, , top, ⊤, bottom, ⊥
2929
using ..FreeDiagrams, ..Limits, ..Subobjects, ..FinSets, ..FinCats
30-
import ..Limits: limit, colimit, universal
30+
import ..Limits: limit, colimit, universal, factorize
3131
import ..Subobjects: Subobject, implies, , subtract, \, negate, ¬, non, ~
3232
import ..Sets: SetOb, SetFunction, TypeSet
3333
import ..FinSets: FinSet, FinFunction, FinDomFunction, force, predicate, is_monic, is_epic
@@ -778,6 +778,57 @@ partial_assignments(x::AbstractVector) =
778778
in_hom(S, c) = [dom(S,f) => f for f in hom(S) if codom(S,f) == c]
779779
out_hom(S, c) = [f => codom(S,f) for f in hom(S) if dom(S,f) == c]
780780

781+
# Factorization
782+
###############
783+
784+
""" factorize(s::Span; initial=Dict(), single=true, kw...)
785+
786+
Factor a morphism f: A->C by finding a morphism h: B → C such that f=g⋅h.
787+
788+
B
789+
g ↗ ↘ h = ?
790+
A ⟶ C
791+
f
792+
793+
This function assumes that the general form of factorizing involves creating an
794+
"initial" dict for homomorphism search. In some categories this may not be the
795+
case, which would mean factorize_constraints should actually return a dictionary
796+
that gets passed as kwargs to homomorphism search.
797+
"""
798+
function factorize(s::Span; initial=Dict(), single::Bool=true, kw...)
799+
f, g = s
800+
init = factorize_constraints(f,g; initial=initial)
801+
if isnothing(init) return single ? nothing : typeof(f)[] end
802+
search = single ? homomorphism : homomorphisms
803+
search(codom(g), codom(f); initial=NamedTuple(init), kw...)
804+
end
805+
806+
"""
807+
Use the data of f:A->C and g:A->B to initialize search for Hom(B,C)
808+
if f(a) = c, then g(a) must equal c.
809+
"""
810+
function factorize_constraints(f::ACSetTransformation{S},
811+
g::ACSetTransformation{S};
812+
initial=Dict()) where S
813+
dom(f) == dom(g) || error("f and g are not a span: $jf \n$jg")
814+
res = Dict{Symbol, Dict{Int,Int}}()
815+
for ob in objects(S)
816+
init = haskey(initial, ob) ? initial[ob] : Dict{Int,Int}()
817+
for (a, g_a) in enumerate(collect(g[ob]))
818+
f_a = f[ob](a)
819+
if haskey(init, g_a)
820+
if init[g_a] != f_a
821+
return nothing
822+
end
823+
else
824+
init[g_a] = f_a
825+
end
826+
end
827+
res[ob] = init
828+
end
829+
return res
830+
end
831+
781832
# Limits and colimits
782833
#####################
783834

src/categorical_algebra/Slices.jl

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ using ...Theories: ThCategory
88
import ...Theories: dom, codom, compose, id
99
import ..Limits: limit, colimit, universal
1010
import ..FinSets: force
11+
import ..CSets: factorize_constraints, is_natural, homomorphism, homomorphisms,
12+
components, factorize
1113

1214
"""
1315
The data of the object of a slice category (say, some category C sliced over an
@@ -86,6 +88,9 @@ function slice_diagram(f::FreeDiagram)::FreeDiagram
8688
FreeDiagram(obs,homs)
8789
end
8890

91+
factorize_constraints(f::SliceHom,g::SliceHom; initial=Dict()) =
92+
factorize_constraints(f.f,g.f; initial=initial)
93+
8994
"""
9095
Convert a limit problem in the slice category to a limit problem of the
9196
underlying category.
@@ -127,4 +132,51 @@ function universal(lim::SliceLimit, sp::Multispan)
127132
return SliceHom(apx, apx2, u)
128133
end
129134

135+
is_natural(x::SliceHom) = is_natural(x.f)
136+
components(x::SliceHom) = components(x.f)
137+
Base.getindex::SliceHom, c) = x.f[c]
138+
139+
"""
140+
This could be made more efficient as a constraint *during* homomorphism finding.
141+
142+
This would require implementing a new constraint to homomorphism search that
143+
restricts the codomain for each part of A, i.e. ∀ a ∈ A: h(a) ∈ g⁻¹(f(a)).
144+
"""
145+
function homomorphisms(X::Slice,Y::Slice; kw...)
146+
map(filter(h->force(X.slice)==force(compose(h,Y.slice)),
147+
homomorphisms(dom(X), dom(Y); kw...)) ) do h
148+
SliceHom(X, Y, h)
149+
end |> collect
150+
end
151+
152+
"""
153+
Because the constraint isn't incorporated into the search process, we cannot
154+
stop early.
155+
"""
156+
function homomorphism(X::Slice,Y::Slice; kw...)
157+
hs = homomorphisms(X,Y; kw...)
158+
return isempty(hs) ? nothing : first(hs)
159+
end
160+
161+
"""
162+
Factorizing a cospan is equivalent to looking for a slice morphism. f->g
163+
164+
B
165+
h = ? ↗ ↘ g
166+
A ⟶ C
167+
f
168+
"""
169+
function factorize(s::Cospan; initial=Dict(), single::Bool=true, kw...)
170+
f, g = Slice.(s)
171+
search = single ? homomorphism : homomorphisms
172+
res = search(f, g; initial=NamedTuple(initial), kw...)
173+
if isnothing(res)
174+
return nothing
175+
else
176+
return [x.f for x in res]
177+
end
178+
end
179+
180+
181+
130182
end # module

test/categorical_algebra/CSets.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,19 @@ h_ = homomorphism(G, I)
9898
@test !is_monic(h_)
9999
@test !is_epic(h_)
100100

101+
# Factorizing morphisms
102+
#----------------------
103+
104+
p2, p3 = path_graph(Graph, 2), path_graph(Graph, 3)
105+
loop = apex(terminal(Graph))
106+
f = CSetTransformation(Graph(2), p3; V=[2,1])
107+
g1 = CSetTransformation(Graph(2), p2; V=[1,2])
108+
g2 = CSetTransformation(Graph(2), p2; V=[2,1])
109+
@test isnothing(factorize(Span(f, g1)))
110+
@test length(factorize(Span(f, g2); single=false)) == 1
111+
f2 = homomorphism(Graph(2), loop)
112+
@test isnothing(factorize(Span(f2, id(Graph(2))); monic=true))
113+
@test factorize(Span(f2, id(Graph(2)))) == f2
101114

102115
# Limits
103116
#-------

test/categorical_algebra/Slices.jl

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,32 @@ slice_dia = FreeDiagram{Slice,SliceHom}(Multispan(A, [f, g]))
5959
clim = colimit(slice_dia)
6060
@test is_isomorphic(dom(apex(clim)), d)
6161

62+
63+
# Factorizing morphisms (morally same tests as in CSets)
64+
#-------------------------------------------------------
65+
p2G, p3G = [path_graph(Graph, x) for x in [3,5]]
66+
p2, p3 = [Slice(homomorphism(p, two)) for p in [p2G,p3G]] # ⊚→□→⊚ and ⊚→□→⊚□→⊚
67+
loop = Slice(id(two)) # ⊚ ↔ □
68+
g2G = Slice(ACSetTransformation(Graph(2), two; V=[1,1])) # ⊚ ⊚
69+
f = SliceHom(g2G, p3, ACSetTransformation(Graph(2), p3G; V=[3,1]))
70+
g1 = SliceHom(g2G, p2, CSetTransformation(Graph(2), p2G; V=[1,3]))
71+
g2 = SliceHom(g2G, p2, CSetTransformation(Graph(2), p2G; V=[3,1]))
72+
@test isnothing(factorize(Span(f, g1)))
73+
@test length(factorize(Span(f, g2); single=false)) == 1
74+
f2 = homomorphism(g2G, loop)
75+
@test isnothing(factorize(Span(f2, id(g2G)); monic=true))
76+
@test factorize(Span(f2, id(g2G))) == f2
77+
78+
# factorize C-set morphisms where the second one is known
79+
#--------------------------------------------------------
80+
81+
A = path_graph(Graph, 2)
82+
B = @acset Graph begin V=4; E=3; src=[1,1,3]; tgt=[2,4,4] end
83+
C = @acset Graph begin V=2; E=2; src=1; tgt=2 end
84+
85+
f = CSetTransformation(A, C; V=[1,2], E=[1])
86+
g = CSetTransformation(B, C; V=[1,2,1,2], E=[1,2,1])
87+
88+
@test length(factorize(Cospan(f,g); single=false)) == 2
89+
6290
end # module

0 commit comments

Comments
 (0)