diff --git a/src/categorical_algebra/CSets.jl b/src/categorical_algebra/CSets.jl index 93b82e53c..771073639 100644 --- a/src/categorical_algebra/CSets.jl +++ b/src/categorical_algebra/CSets.jl @@ -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 @@ -666,7 +669,6 @@ end @cartesian_monoidal_instance ACSet ACSetTransformation @cocartesian_monoidal_instance ACSet ACSetTransformation - # Limits and colimits ##################### diff --git a/src/categorical_algebra/HomSearch.jl b/src/categorical_algebra/HomSearch.jl index d197fb24f..cbe90b225 100644 --- a/src/categorical_algebra/HomSearch.jl +++ b/src/categorical_algebra/HomSearch.jl @@ -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 @@ -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 ###################### diff --git a/src/categorical_algebra/Slices.jl b/src/categorical_algebra/Slices.jl index 2cbd796a2..e7fe4b13c 100644 --- a/src/categorical_algebra/Slices.jl +++ b/src/categorical_algebra/Slices.jl @@ -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. @@ -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. @@ -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 diff --git a/test/categorical_algebra/HomSearch.jl b/test/categorical_algebra/HomSearch.jl index 865409615..4cc775aea 100644 --- a/test/categorical_algebra/HomSearch.jl +++ b/test/categorical_algebra/HomSearch.jl @@ -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 ########################### diff --git a/test/categorical_algebra/Slices.jl b/test/categorical_algebra/Slices.jl index 7c71c7a39..0ef02edd5 100644 --- a/test/categorical_algebra/Slices.jl +++ b/test/categorical_algebra/Slices.jl @@ -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