Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Subobject image/preimage, completion of a partial subobject #605

Closed
Closed
Show file tree
Hide file tree
Changes from 4 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
67 changes: 66 additions & 1 deletion src/categorical_algebra/CSets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export ACSetTransformation, CSetTransformation,
ACSetHomomorphismAlgorithm, BacktrackingSearch, HomomorphismQuery,
components, force, is_natural, homomorphism, homomorphisms, is_homomorphic,
isomorphism, isomorphisms, is_isomorphic,
generate_json_acset, parse_json_acset, read_json_acset, write_json_acset
generate_json_acset, parse_json_acset, read_json_acset, write_json_acset, hom_inv, induce_subobject

using Base.Iterators: flatten
using Base.Meta: quot
Expand Down Expand Up @@ -1115,6 +1115,71 @@ function common_ob(A::Subobject, B::Subobject)
return X
end

"""
f:A->B as a map of subobjects of A to subjects of B
"""
(f::ACSetTransformation)(X::SubACSet)::SubACSet = begin
codom(hom(X)) == dom(f) || error("Cannot apply $f to $X")
Subobject(codom(f); Dict(
[k=>f.(collect(components(X)[k])) for (k,f) in pairs(components(f))])...)
end

"""
f:A->B as a map from A to a subobject of B
i.e. we cast the ACSet A to its top subobject
"""
(f::ACSetTransformation)(X::StructACSet)::SubACSet =
X == dom(f) ? f(top(X)) : error("Cannot apply $f to $X")

""" hom_inv(f::ACSetTransformation,Y::Subobject)::SubACSet
Inverse of f:A->B as a map of subobjects of B to subjects of A.
It can be thought of as incident, but for homomorphisms.
"""
hom_inv(f::ACSetTransformation,Y::Subobject)::SubACSet = begin
codom(hom(Y)) == codom(f) || error("Cannot apply $f to $X")

Subobject(dom(f); Dict{Symbol, Vector{Int}}(
[k => vcat([preimage(f,y) for y in collect(components(Y)[k])]...)
for (k,f) in pairs(components(f))])...)
end

""" hom_inv(f::CSetTransformation,Y::StructACSet)::SubACSet
Inverse f:A->B as a map from subobjects of B to subobjects of A.
Cast an ACSet to subobject, though this has a trivial answer when computing
the preimage (it is necessarily the top subobject of A).
"""
hom_inv(f::CSetTransformation,Y::StructACSet)::SubACSet =
kris-brown marked this conversation as resolved.
Show resolved Hide resolved
Y = codom(f) ? top(dom(f)) : error("Cannot apply inverse of $f to $Y")


""" induce_subobject(X::StructACSet{S}; vs...)

Takes a partially-specified ACSet subobject and completes it so that there are
no undefined references.

For example: induce_subobject(my_wiring_diagram; Wire=[2])
will yield a subobject of my_wiring_diagram with Wire#2, as well as the
ports it's connected to and the boxes those ports are connected to.
"""
function induce_subobject(X::StructACSet{S}; vs...) where {S}
vs = Dict([k=>Set(get(vs, k, [])) for k in ob(S)])
changed = true
while changed
changed = false
for (k, d, cd) in zip(hom(S), dom(S), codom(S))
for v in X[collect(vs[d]), k]
if v ∉ vs[cd]
push!(vs[cd], v)
changed=true
end
end
end
end
return Subobject(X; Dict{Symbol,Vector{Int}}([k=>sort(collect(v))
for (k,v) in pairs(vs)])...)
end


# FIXME: Should these two accessors go elsewhere?

@generated function all_subparts(X::StructACSet{S},
Expand Down
30 changes: 30 additions & 0 deletions test/categorical_algebra/CSets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,36 @@ A = Subobject(S, X=[3,4,5])
@test ¬Subobject(ι₂) |> force == Subobject(ι₁)
@test ~A |> force == ⊤(S) |> force

# Image and preimage
g1 = path_graph(Graph, 2)
g2 = apex(coproduct(g1, g1))
g3 = @acset Graph begin V=2; E=3; src=[1,1,2]; tgt=[1,2,2] end
h = homomorphisms(g1,g2)[2] # V=[3,4], E=[2]
ϕ = homomorphism(g2,g3; initial=(V=[1,1,2,2],))
@test components(hom(h(g1))) == (
V = FinFunction([3, 4], 2, 4), E = FinFunction([2], 1, 2))
@test hom_inv(h, Subobject(g2, V=[1])) |> force == bottom(g1) |> force
@test hom_inv(h, Subobject(g2, V=[3])) |> force == Subobject(g1, V=[1]) |> force
@test ϕ(h(g1)) == Subobject(g3, V=[2,2], E=[3])

# Preimage
G = path_graph(Graph, 1) ⊕ path_graph(Graph, 2) ⊕ path_graph(Graph, 3)
H = let Loop=apex(terminal(Graph)); Loop ⊕ Loop ⊕ Loop end
# Each path graph gets its own loop
f = homomorphism(G, H; initial=(V=Dict([1=>1,2=>2,4=>3]),))
for i in 1:3
@test is_isomorphic(dom(hom(hom_inv(f, Subobject(H, V=[i],E=[i])))),
path_graph(Graph, i))
end

# Induce subobject
e1 = Subobject(g2, V=[1,2], E=[1])
@test induce_subobject(g2, E=[1]) |> force == e1 |> force
@test induce_subobject(g2, V=[1]) |> force == Subobject(g2, V=[1]) |> force
@test induce_subobject(g2, E=[2]) |> force == h(g1) |> force



# Serialization
###############

Expand Down