Skip to content

Commit

Permalink
csettransformation category
Browse files Browse the repository at this point in the history
loose kwarg for product and terminal

example where distinguished object is not a leaf object

SigmaMigration can return a DiagramHom with components kwarg

master -> main

Pragmatic -> abstract

no stratify nor box product for now

CSetTransformations

cleanup

cascade effect of fixing homsearch bug

finish rebase
  • Loading branch information
Kris Brown committed Sep 5, 2023
1 parent 05cfe2a commit 22924bb
Show file tree
Hide file tree
Showing 9 changed files with 331 additions and 97 deletions.
2 changes: 1 addition & 1 deletion benchmark/Graphs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ SYMGRAPHS = Dict(k => SymmetricGraph(g) for (k,g) in LG_SYMGRAPHS)
#########

# `bench_iter_edges` and `bench_has_edge` adapted from Graphs:
# https://github.com/JuliaGraphs/Graphs.jl/blob/master/benchmark/core.jl
# https://github.com/JuliaGraphs/Graphs.jl/blob/main/benchmark/core.jl

function bench_iter_edges(g)
count = 0
Expand Down
251 changes: 202 additions & 49 deletions src/categorical_algebra/CSets.jl

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/categorical_algebra/Chase.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ import ..Categories: ob_map
#####

"""Distill the component of a morphism that merges elements together"""
egd(e::CSetTransformation) = factorize(image(e),e)
egd(e::ACSetTransformation) = factorize(image(e),e)
"""Distill the component of a morphism that adds new elements"""
tgd(e::CSetTransformation) = factorize(coimage(e), e)
tgd(e::ACSetTransformation) = factorize(coimage(e), e)
"""Check if id up to isomorphism"""
no_change(f) = all(c->is_monic(c) && is_epic(c), components(f))

Expand Down
13 changes: 7 additions & 6 deletions src/categorical_algebra/DataMigrations.jl
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
""" Functorial data migration for attributed C-sets.
"""
module DataMigrations
export DataMigration, SigmaMigration, DeltaMigration, migrate, migrate!,
representable, yoneda, colimit_representables, subobject_classifier,
internal_hom, SigmaMigrationFunctor, DeltaMigrationFunctor,
DataMigrationFunctor, functor
export DataMigration, SigmaMigration, DeltaMigration, SigmaMigrationFunctor,
DeltaMigrationFunctor, DataMigrationFunctor, functor, migrate, migrate!,
representable, yoneda, colimit_representables, subobject_classifier,
internal_hom

using ACSets
using ACSets.DenseACSets: constructor, datatypes
using ...GATs
using ...Theories: ob, hom, dom, codom, attr, AttrTypeExpr,
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
using ..FinCats: make_map, mapvals
using ..Chase: collage, crel_type, pres_to_eds, add_srctgt, chase
using ..FinSets: VarSet
using ..Subobjects
using ..CSets: abstract_attributes

# Data types
############
Expand Down Expand Up @@ -429,7 +431,6 @@ to recover the presentation from the datatype. Thus, this method for
representable(::Type{T}, ob::Symbol; kw...) where T <: StructACSet =
representable(T, Presentation(T), ob; kw...)


"""
The subobject classifier Ω in a presheaf topos is the presheaf that sends each
object A to the set of sieves on it (equivalently, the set of subobjects of the
Expand Down
6 changes: 3 additions & 3 deletions src/categorical_algebra/HomSearch.jl
Original file line number Diff line number Diff line change
Expand Up @@ -198,10 +198,10 @@ function backtracking_search(f, X::ACSet, Y::ACSet;

# Fail early if no monic/isos exist on cardinality grounds.
if iso isa Bool
iso = iso ? Ob : ()
iso = iso ? ObAttr : ()
end
if monic isa Bool
monic = monic ? Ob : ()
monic = monic ? ObAttr : ()
end
iso_failures = Iterators.filter(c->nparts(X,c)!=nparts(Y,c),iso)
mono_failures = Iterators.filter(c->nparts(X,c)>nparts(Y,c),monic)
Expand Down Expand Up @@ -655,7 +655,7 @@ function Base.iterate(Sub::OverlapIterator, state=nothing)
# Get monic maps from Y into each of the objects. The first comes for free
maps = Vector{ACSetTransformation}[[abs_subobj]]
for X in Sub.others
fs = homomorphisms(Y, X; monic=true)
fs = homomorphisms(Y, X; monic=ob(acset_schema(Y)))
real_fs = Set() # quotient fs via automorphisms of Y
for f in fs
if all(rf->all-> forcef) != force(rf), syms), real_fs)
Expand Down
138 changes: 108 additions & 30 deletions test/categorical_algebra/CSets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -96,37 +96,37 @@ clim = colimit(Span(h1,h2));

# Constructors and accessors.
g, h = path_graph(Graph, 4), cycle_graph(Graph, 2)
α = CSetTransformation((V=[1,2,1,2], E=[1,2,1]), g, h)
α = ACSetTransformation((V=[1,2,1,2], E=[1,2,1]), g, h)
@test components(α) == (V=α[:V], E=α[:E])
@test α[:V] isa FinFunction{Int} && α[:E] isa FinFunction{Int}
@test α[:V](3) == 1
@test α[:E](2) == 2
@test startswith(sprint(show, α), "ACSetTransformation((V = ")

α′ = CSetTransformation(g, h, V=[1,2,1,2], E=[1,2,1])
α′ = ACSetTransformation(g, h, V=[1,2,1,2], E=[1,2,1])
@test components(α′) == components(α)
α′′ = CSetTransformation(g, h, V=FinFunction([1,2,1,2]), E=FinFunction([1,2,1]))
α′′ = ACSetTransformation(g, h, V=FinFunction([1,2,1,2]), E=FinFunction([1,2,1]))
@test components(α′′) == components(α)

# Naturality.
d = naturality_failures(α)
@test [collect(d[a]) for a in keys(d)] == [[],[]]
@test is_natural(α)
β = CSetTransformation((V=[1,2,1,2], E=[1,1,1]), g, h)
β = ACSetTransformation((V=[1,2,1,2], E=[1,1,1]), g, h)
d = naturality_failures(β)
@test sort([collect(v) for v in values(d)]) == [[(2,1,2)],[(2,2,1)]]
@test startswith(sprint(show_naturality_failures, β), "Failures")
@test !is_natural(β)
β = CSetTransformation((V=[2,1], E=[2,1]), h, h)
β = ACSetTransformation((V=[2,1], E=[2,1]), h, h)
@test is_natural(β)
β = CSetTransformation((V=[2,1], E=[2,2]), h, h)
β = ACSetTransformation((V=[2,1], E=[2,2]), h, h)

# Category of C-sets.
@test dom(α) === g
@test codom(α) === h
γ = compose(α,β)
@test γ isa TightACSetTransformation
@test γ == CSetTransformation((V=α[:V]β[:V], E=α[:E]β[:E]), g, h)
@test γ isa ACSetTransformation
@test γ == ACSetTransformation((V=α[:V]β[:V], E=α[:E]β[:E]), g, h)
@test id(g) isa TightACSetTransformation
@test force(compose(id(g), α)) == α
@test force(compose(α, id(h))) == α
Expand Down Expand Up @@ -154,14 +154,14 @@ term = cycle_graph(Graph, 1)
lim = terminal(Graph)
@test ob(lim) == term
@test force(delete(lim, g)) ==
CSetTransformation((V=fill(1,4), E=fill(1,3)), g, term)
ACSetTransformation((V=fill(1,4), E=fill(1,3)), g, term)

# Products in Graph: unitality.
lim = product(g, term)
@test ob(lim) == g
@test force(proj1(lim)) == force(id(g))
@test force(proj2(lim)) ==
CSetTransformation((V=fill(1,4), E=fill(1,3)), g, term)
ACSetTransformation((V=fill(1,4), E=fill(1,3)), g, term)

# Product in Graph: two directed intervals (Reyes et al 2004, p. 48).
I = path_graph(Graph, 2)
Expand All @@ -181,17 +181,17 @@ lim = product(g, loop2)
g2 = ob(lim)
@test (nv(g2), ne(g2)) == (nv(g), 2*ne(g))
@test (src(g2), tgt(g2)) == (repeat(src(g), 2), repeat(tgt(g), 2))
α = CSetTransformation((V=[2,3], E=[2]), I, g)
β = CSetTransformation((V=[1,1], E=[2]), I, loop2)
α = ACSetTransformation((V=[2,3], E=[2]), I, g)
β = ACSetTransformation((V=[1,1], E=[2]), I, loop2)
γ = pair(lim, α, β)
@test forceproj1(lim)) == α
@test forceproj2(lim)) == β

# Equalizer in Graph from (Reyes et al 2004, p. 50).
g, h = cycle_graph(Graph, 2), Graph(2)
add_edges!(h, [1,2,2], [2,1,1])
ϕ = CSetTransformation((V=[1,2], E=[1,2]), g, h)
ψ = CSetTransformation((V=[1,2], E=[1,3]), g, h)
ϕ = ACSetTransformation((V=[1,2], E=[1,2]), g, h)
ψ = ACSetTransformation((V=[1,2], E=[1,3]), g, h)
@test is_natural(ϕ) && is_natural(ψ)
eq = equalizer(ϕ, ψ)
@test ob(eq) == I
Expand All @@ -205,8 +205,8 @@ g0, g1, g2 = Graph(2), Graph(3), Graph(2)
add_edges!(g0, [1,1,2], [1,2,2])
add_edges!(g1, [1,2,3], [2,3,3])
add_edges!(g2, [1,2,2], [1,2,2])
ϕ = CSetTransformation((V=[1,2,2], E=[2,3,3]), g1, g0)
ψ = CSetTransformation((V=[1,2], E=[1,3,3]), g2, g0)
ϕ = ACSetTransformation((V=[1,2,2], E=[2,3,3]), g1, g0)
ψ = ACSetTransformation((V=[1,2], E=[1,3,3]), g2, g0)
@test is_natural(ϕ) && is_natural(ψ)
lim = pullback(ϕ, ψ)
@test nv(ob(lim)) == 3
Expand All @@ -229,15 +229,15 @@ lim′ = limit(FinDomFunctor(diagram))
# Initial object in graph: the empty graph.
colim = initial(Graph)
@test ob(colim) == Graph()
@test create(colim, g) == CSetTransformation((V=Int[], E=Int[]), Graph(), g)
@test create(colim, g) == ACSetTransformation((V=Int[], E=Int[]), Graph(), g)

# Coproducts in Graph: unitality.
g = path_graph(Graph, 4)
colim = coproduct(g, Graph())
@test ob(colim) == g
@test force(coproj1(colim)) == force(id(g))
@test force(coproj2(colim)) ==
CSetTransformation((V=Int[], E=Int[]), Graph(), g)
ACSetTransformation((V=Int[], E=Int[]), Graph(), g)

# Coproduct in Graph.
h = cycle_graph(Graph, 2)
Expand All @@ -246,7 +246,7 @@ coprod = ob(colim)
@test nv(coprod) == 6
@test src(coprod) == [1,2,3,5,6]
@test tgt(coprod) == [2,3,4,6,5]
α = CSetTransformation((V=[1,2,1,2], E=[1,2,1]), g, h)
α = ACSetTransformation((V=[1,2,1,2], E=[1,2,1]), g, h)
β = id(h)
γ = copair(colim, α, β)
@test force(coproj1(colim)γ) == α
Expand All @@ -258,17 +258,17 @@ colim2 = coproduct(path_graph(Graph, 4), cycle_graph(Graph, 2))
# Coequalizer in Graph: collapsing a segment to a loop.
g = Graph(2)
add_edge!(g, 1, 2)
α = CSetTransformation((V=[1], E=Int[]), Graph(1), g)
β = CSetTransformation((V=[2], E=Int[]), Graph(1), g)
α = ACSetTransformation((V=[1], E=Int[]), Graph(1), g)
β = ACSetTransformation((V=[2], E=Int[]), Graph(1), g)
@test is_natural(α) && is_natural(β)
coeq = coequalizer(α, β)
@test ob(coeq) == ob(terminal(Graph))
@test force(proj(coeq)[:V]) == FinFunction([1,1])
@test force(proj(coeq)[:E]) == FinFunction([1])

# Pushout in Graph from (Reyes et al 2004, p. 59).
α = CSetTransformation((V=[2], E=Int[]), Graph(1), g)
β = CSetTransformation((V=[1], E=Int[]), Graph(1), ob(terminal(Graph)))
α = ACSetTransformation((V=[2], E=Int[]), Graph(1), g)
β = ACSetTransformation((V=[1], E=Int[]), Graph(1), ob(terminal(Graph)))
@test is_natural(α) && is_natural(β)
colim = pushout(α, β)
@test nv(ob(colim)) == 2
Expand Down Expand Up @@ -335,13 +335,20 @@ end
@acset_type VELabeledGraph(SchVELabeledGraph,
index=[:src,:tgt]) <: AbstractGraph

# Terminal labeled graph.
@test ob(terminal(VELabeledGraph)) == cycle_graph(VELabeledGraph{Tuple{}}, 1; E=(;elabel=[()]), V=(;vlabel=[()]))
# Terminal labeled graph (across all possible choices of Julia data types)
@test ob(terminal(VELabeledGraph; loose=true)) ==
cycle_graph(VELabeledGraph{Tuple{}}, 1; E=(;elabel=[()]), V=(;vlabel=[()]))

# Terminal in the subcategory where all attrs are variables
T2 = ob(terminal(VELabeledGraph{Symbol}; cset=true))
@test T2 == @acset VELabeledGraph{Symbol} begin
V=1; E=1; Label=2; src=1; tgt=1; vlabel=[AttrVar(1)]; elabel=[AttrVar(2)]
end

# Product of labeled graphs.
g = path_graph(VELabeledGraph{Symbol}, 2, V=(vlabel=[:a,:b],), E=(elabel=:f,))
h = path_graph(VELabeledGraph{String}, 2, V=(vlabel=["x","y"],), E=(elabel="f",))
π1, π2 = lim = product(g, h)
π1, π2 = lim = product(g, h; loose=true)
prod′ = ob(lim)
@test prod′ isa VELabeledGraph{Tuple{Symbol,String}}
@test Set(prod′[:vlabel]) == Set([(:a, "x"), (:a, "y"), (:b, "x"), (:b, "y")])
Expand Down Expand Up @@ -451,8 +458,8 @@ Y = path_graph(Graph, 3) ⊕ path_graph(Graph, 2) ⊕ path_graph(Graph, 2)
add_vertex!(Y)
add_edge!(Y, 2, 8)
Z = cycle_graph(Graph, 1) cycle_graph(Graph, 1)
ιY, ιZ = colim = pushout(CSetTransformation(I, Y, V=[3]),
CSetTransformation(I, Z, V=[1]))
ιY, ιZ = colim = pushout(ACSetTransformation(I, Y, V=[3]),
ACSetTransformation(I, Z, V=[1]))
B_implies_C, B = Subobject(ιY), Subobject(ιZ)
C = Subobject(ob(colim), V=2:5, E=2:3)
@test (B C) |> force == B_implies_C |> force
Expand Down Expand Up @@ -557,9 +564,8 @@ l32 = ACSetTransformation((Weight = t32,), w3, w2)
l = l32 l21 # {Int}->{Bool} x {Bool}->{Symbol} = {Int}->{Symbol}
@test collect(l[:Weight]) == [:X,:B]


# Homomorphism search
#####################
#--------------------

A = @acset WG{Bool} begin V=1;E=2;Weight=1;src=1;tgt=1;
weight=[true, AttrVar(1)] end
Expand Down Expand Up @@ -594,6 +600,7 @@ Z = @acset A2{Symbol} begin X=1; D=1; f=[AttrVar(1)]; g=[AttrVar(1)] end

# Colimits
#---------

const WG = WeightedGraph

A = @acset WG{Bool} begin V=1;E=2;Weight=2;src=1;tgt=1;weight=[AttrVar(1),true] end
Expand Down Expand Up @@ -645,8 +652,37 @@ h = abstract_attributes(X)
rem_part!(X, :E, 2)
@test nparts(dom(h), :E) == 2

# Limits
#-------

A = @acset WG{Symbol} begin V=1;E=2;Weight=1;src=1;tgt=1;weight=[AttrVar(1),:X] end
B = @acset WG{Symbol} begin V=1;E=2;Weight=1;src=1;tgt=1;weight=[:X, :Y] end
C = B @acset WG{Symbol} begin V=1 end
AC = homomorphism(A,C)
BC = CSetTransformation(B,C; V=[1],E=[1,2], Weight=[:X])
@test all(is_natural,[AC,BC])
p1, p2 = product(A,A; cset=true);
X = @acset WG{Symbol} begin V=1;E=2;Weight=1;src=1;tgt=1;weight=[:X, :X] end
@test nparts(apex(product(X,X;cset=true)),:Weight) == 1

# Pullback in Graph from (Reyes et al 2004, p. 53), again
g0, g1, g2 = WG{Symbol}.([2,3,2])
add_edges!(g0, [1,1,2], [1,2,2]; weight=[:X,:Y,:Z])
add_edges!(g1, [1,2,3], [2,3,3]; weight=[:Y,:Z,AttrVar(add_part!(g1,:Weight))])
add_edges!(g2, [1,2,2], [1,2,2]; weight=[AttrVar(add_part!(g2,:Weight)), :Z,:Z])
ϕ = only(homomorphisms(g1, g0)) |> CSetTransformation
ψ = only(homomorphisms(g2, g0; initial=(V=[1,2],))) |> CSetTransformation
@test is_natural(ϕ) && is_natural(ψ)
lim = pullback(ϕ, ψ)
@test nv(ob(lim)) == 3
@test sort!(collect(zip(src(ob(lim)), tgt(ob(lim))))) ==
[(2,3), (2,3), (3,3), (3,3)]
@test is_natural(proj1(lim)) && is_natural(proj2(lim))


# Subobjects with variables
#--------------------------

X = @acset SetAttr{Bool} begin X=2;D=1;f=[true, AttrVar(1)] end
A = Subobject(X, X=[1])
B = Subobject(X, X=[2], D=[1])
Expand Down Expand Up @@ -680,4 +716,46 @@ end
src=[1,2]; tgt=3; vlabel=[:a,:b,:c]; elabel=AttrVar.(1:2)
end

# Limits of CSetTransformations between ACSets
#---------------------------------------------
# Example: "Reflexive graphs" where reflexive edges have weight -1
A = @acset WeightedGraph{Float64} begin V=2; E=3;
src=[1, 2, 1]; tgt=[1, 2, 2]; weight=[-1, -1, 5]
end
B = @acset WeightedGraph{Float64} begin V=3; E=5;
src=[1, 2, 3, 1, 2]; tgt=[1, 2, 3, 2, 3]; weight=[-1, -1, -1, 2, 3]
end

# 1. Stratification with LooseACSetTransformation
C_nothing = @acset WeightedGraph{Nothing} begin V=1; E=2; Weight=2
src=1; tgt=1; weight=[nothing,nothing]
end
AC_nothing = LooseACSetTransformation(
(V=[1, 1], E=[1, 1, 2],), (Weight=(_->nothing),), A, C_nothing)
BC_nothing = LooseACSetTransformation(
(V=[1, 1, 1], E=[2, 2, 2, 1, 1],), (Weight=(_->nothing),), B, C_nothing)
res = apex(pullback(AC_nothing, BC_nothing))
@test nparts(res, :E) == 7
@test eltype(res[:weight]) == Tuple{Float64, Float64}

# 2. Stratification with CSetTransformations
C = @acset WeightedGraph{Float64} begin V=1; E=2; Weight=2
src=1; tgt=1; weight=[3.1415, 2.71]
end

AC = CSetTransformation(A, C; V=[1, 1], E=[1, 1, 2])
BC = CSetTransformation(B, C; V=[1, 1, 1], E=[2, 2, 2, 1, 1])
ABC = pullback(AC,BC);
expected = @acset WeightedGraph{Float64} begin V=6; E=7; Weight=3;
src=[1,1,2,3,3,4,5]; tgt=[2,3,4,4,5,6,6]; weight=AttrVar.([1,2,2,1,3,3,1])
end
@test is_isomorphic(apex(ABC),expected)

# 3. Apply commutative monoid to attrs
ABC = pullback(AC,BC; attrfun=(weight=prod,))
expected = @acset WeightedGraph{Float64} begin V=6; E=7;
src=[1,1,2,3,3,4,5]; tgt=[2,3,4,4,5,6,6]; weight=[-5,-2,-2,-5,-3,-3,-5]
end
@test is_isomorphic(apex(ABC),expected)

end
2 changes: 1 addition & 1 deletion test/categorical_algebra/Chase.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ end
core = @acset Graph begin V=2; E=2; src=[1,2]; tgt=[2,1] end

# This adds a self loop to #1 and merges #1/#3
etgd = CSetTransformation(etgd_s,etgd_t; V=[1,2,1], E=[2,3])
etgd = ACSetTransformation(etgd_s,etgd_t; V=[1,2,1], E=[2,3])

@test is_isomorphic(codom(egd(etgd)), core) # EGD has no extra self-edge
@test collect(egd(etgd)[:V]) == [1,2,1] # but it does merge vertices
Expand Down
4 changes: 3 additions & 1 deletion test/categorical_algebra/DataMigrations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -554,9 +554,9 @@ expected = @acset WeightedGraph{Float64} begin
end
@test is_isomorphic(ob_map(colimit_representables(d, yWG), :I), expected)


# Subobject classifier
######################

# Graph and ReflGraph have 'same' subobject classifier
ΩG,_ = subobject_classifier(Graph, SchGraph)
ΩrG,_ = subobject_classifier(ReflexiveGraph, SchReflexiveGraph)
Expand All @@ -575,12 +575,14 @@ G = (star_graph(Graph, 2)⊗path_graph(Graph, 3))
Φ::Hom(X,X)
ΦΦΦΦ == ΦΦ
end

@acset_type DDS42(SchDDS42, index=[])
ΩDDs, _ = subobject_classifier(DDS42, SchDDS42)
@test is_isomorphic(ΩDDs, @acset DDS42 begin X=4; Φ=[1,3,4,4] end)

# Internal Hom
##############

G = ReflexiveGraph(2)
F = path_graph(ReflexiveGraph, 2)
Fᴳ,_ = internal_hom(G,F, SchReflexiveGraph)
Expand Down
Loading

0 comments on commit 22924bb

Please sign in to comment.