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

EHN: Left pushforward data migration #433

Merged
merged 5 commits into from
Jun 21, 2021
Merged
Show file tree
Hide file tree
Changes from 3 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
8 changes: 8 additions & 0 deletions src/categorical_algebra/CSets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import ..Limits: limit, colimit, universal
import ..FinSets: FinSet, FinFunction, FinDomFunction, force
using ...Theories: Category, CatDesc, AttrDesc, ob, hom, attr, adom, acodom
import ...Theories: dom, codom, compose, ⋅, id
using ...Present
import ...Present: Presentation

# FinSets interop
#################
Expand Down Expand Up @@ -603,6 +605,12 @@ function (::Type{T})(X::ACSet, FOb::AbstractDict,
migrate!(Y, X, FOb, FHom)
end

"""Get the Schema from an ACSet
"""
function Presentation(::ACSet{CD, AD}) where {CD, AD}
return Presentation(CD, AD)
end

""" Serialize an ACSet object to a JSON string
"""
function generate_json_acset(x::T) where T <: AbstractACSet
Expand Down
2 changes: 2 additions & 0 deletions src/categorical_algebra/CategoricalAlgebra.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ include("ACSetViews.jl")
include("GraphCategories.jl")
include("StructuredCospans.jl")
include("CommutativeDiagrams.jl")
include("DataMigration.jl")

@reexport using .FreeDiagrams
@reexport using .CommutativeDiagrams
@reexport using .Limits
@reexport using .CSets
@reexport using .ACSetViews
@reexport using .StructuredCospans
@reexport using .DataMigration

end
159 changes: 159 additions & 0 deletions src/categorical_algebra/DataMigration.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
""" Data Migration functors
"""
module DataMigration

export Σ, Functor

using ...Theories
using ...Theories: CatDesc, AttrDesc, ob, hom, dom, codom
using ...CSetDataStructures
using ..FinSets, ..CSets, ..Limits, ..FreeDiagrams
using ...Graphs
using ...Present

import ...CategoricalAlgebra.FreeDiagrams: FreeDiagram

""" Functor

A functor ``F: \\mathcal{C} \\to \\mathcal{D}`` consists of a map of objects and a
map of homomorphisms from a domain category ``\\mathcal{C}`` to a codomain
category ``\\mathcal{D}``.
"""
struct Functor{Schema, Ob, Hom}
FOb::Dict{Ob, Ob}
FHom::Dict{Hom, Hom}
dom::Presentation{Schema, Symbol}
codom::Presentation{Schema, Symbol}
end

""" Inv(F::Functor)

For a functor ``F: \\mathcal{C} \to \\mathcal{D}``,
returns a function which when given an object (resp. morphism)
of ``\\mathcal{D}``, returns an array of objects (resp. morphisms)
of ``\\mathcal{C}`` in the preimage.
"""
function Inv(F::Functor{Schema, Ob, Hom}) where {Schema, Ob, Hom}
y::Union{Ob, Hom} -> begin
y isa Ob ? D = F.FOb : D = F.FHom
[k for (k,v) in D if v == y]
end
end
slibkind marked this conversation as resolved.
Show resolved Hide resolved


""" add_hom!(d::FreeDiagram{Ob, Hom}, src_ob::Ob, tgt_ob::Ob, hom::Hom)

Adds a hom to `d` from the vertex with object `src_ob` to the vertex with object `tgt_ob`.
"""
function add_hom!(d::FreeDiagram, src_ob, tgt_ob, hom)
src_idx = first(incident(d, src_ob, :ob))
tgt_idx = first(incident(d, tgt_ob, :ob))
return add_edge!(d, src_idx, tgt_idx, hom = hom)
end
slibkind marked this conversation as resolved.
Show resolved Hide resolved

""" FreeDiagram(pres::Presentation{Schema, Symbol})

Returns a `FreeDiagram` whose objects are the generating objects of `pres` and
whose homs are the generating homs of `pres`.
"""
function FreeDiagram(pres::Presentation{Schema, Symbol}) where {Schema}
obs = Array{FreeSchema.Ob}(generators(pres, :Ob))
homs = Array{FreeSchema.Hom}(generators(pres, :Hom))
doms = map(h -> generator_index(pres, nameof(dom(h))), homs)
codoms = map(h -> generator_index(pres, nameof(codom(h))), homs)
return FreeDiagram(obs, collect(zip(homs, doms, codoms)))
end


""" Σ(F::Functor)

which takes a ``\\mathcal{C}``-Set ``X: \\mathcal{C} \\to \\mathsf{Set}``
and produces the pushforward ``\\Sigma_F(X): \\mathcal{D} \\to \\mathsf{Set}``.

``\\mathcal{D}`` must be a free schema with no cycles.
"""
function Σ(F::Functor{Schema, Ob, Hom}) where {Schema, Ob, Hom}
slibkind marked this conversation as resolved.
Show resolved Hide resolved
diagramC = FreeDiagram(F.dom)
diagramD = FreeDiagram(F.codom)
FInv = Inv(F)

comma_cats = FreeDiagram{FreeDiagram, ACSetTransformation}()
slibkind marked this conversation as resolved.
Show resolved Hide resolved
add_vertices!(comma_cats,
nparts(diagramD, :V),
ob = map( _ -> FreeDiagram{Pair{Ob, Hom}, Hom}(), parts(diagramD, :V))
)

for d in topological_sort(diagramD)
F∇d = ob(comma_cats, d)
id_d = id(ob(diagramD, d))

# If FOb[c] = d, add an objects (c, id(d)) to F∇d
preimage_d = FInv(ob(diagramD, d))
id_obs = add_vertices!(F∇d, length(preimage_d), ob = map(c->c=>id_d, preimage_d))

# if FHom[h] = id(d), add a hom h: (dom(h), id(d)) -> (codom(h), id(d)) to F∇d
id_homs = map(FInv(id_d)) do h
add_hom!(F∇d, dom(h) => id_d, codom(h) => id_d, h)
end
# for g: d' -> d in D (note that F∇d' must have already been constructed)
for g in incident(diagramD, d, :tgt)
d′ = src(diagramD, g)
F∇d′ = ob(comma_cats, d′)

# for (c,f) in F∇d' add ob (c, compose(f,g)) to F∇d
vs = add_vertices!(F∇d, nparts(F∇d′, :V), ob = map(ob(F∇d′)) do (c,f)
c => compose(f, hom(diagramD, g))
end)

# for h: (c, f) -> (c', f') in diagram_d', add hom h to F∇d
es = add_edges!(F∇d, vs[src(F∇d′)], vs[tgt(F∇d′)], hom = hom(F∇d′))

# g induces an inclusion F∇d′ to F∇d
add_edge!(comma_cats, d′, d, hom = ACSetTransformation((V = collect(vs), E = collect(es)), F∇d′, F∇d))

# for every (c,f) in the recently added objects. If FHom[h] == f, then add the hom
# h: (c,f) -> (codom(h), idv)
# relies on D being free
for (c, f) in ob(F∇d, vs)
for h in FInv(f)
dom(h) == c && add_hom!(F∇d, c => f, codom(h) => id_d, h)
end
end
end
end # end of populating comma_cats

function pushforward(X::ACSet)
Presentation(X) == F.dom || error("X has the wrong Schema")
slibkind marked this conversation as resolved.
Show resolved Hide resolved

Y = ACSetType(F.codom)()

colimX = map(parts(diagramD, :V)) do i
F∇d = ob(comma_cats, i)
Xobs = map(ob(F∇d)) do (c,_)
FinSet(nparts(X, nameof(c)))
end
Xhoms = map(parts(F∇d, :E)) do g
FinFunction(X[nameof(hom(F∇d, g))], Xobs[src(F∇d, g)], Xobs[tgt(F∇d, g)])
end
colimit(FreeDiagram(Xobs, collect(zip(Xhoms, src(F∇d), tgt(F∇d)))))
end

for d in parts(diagramD, :V)
add_parts!(Y, nameof(ob(diagramD, d)), length(apex(colimX[d])))
end

for g in parts(diagramD, :E)
if nparts(Y, nameof(dom(hom(diagramD, g)))) == 0
continue
end
set_subpart!(Y, nameof(hom(diagramD, g)),
universal(colimX[src(diagramD, g)],
Multicospan(legs(colimX[tgt(diagramD, g)])[hom(comma_cats, g)[:V].func])
).func
)
end
return Y
end
end

end #module
2 changes: 1 addition & 1 deletion src/categorical_algebra/FreeDiagrams.jl
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ function FreeDiagram(obs::AbstractVector{Ob},
@assert all(obs[s] == dom(f) && obs[t] == codom(f) for (f,s,t) in homs)
d = FreeDiagram{Ob,Hom}()
add_vertices!(d, length(obs), ob=obs)
add_edges!(d, getindex.(homs,2), getindex.(homs,3), hom=first.(homs))
length(homs) > 0 && add_edges!(d, getindex.(homs,2), getindex.(homs,3), hom=first.(homs))
return d
end

Expand Down
18 changes: 18 additions & 0 deletions src/theories/Schema.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
export Schema, FreeSchema, Data, Attr, SchemaExpr, DataExpr, AttrExpr

using MLStyle: @match
using ...Present

import ...Present: Presentation

# Schema
########
Expand Down Expand Up @@ -186,3 +189,18 @@ function attrs_by_codom(AD::Type{T}) where
end

SchemaType(pres::Presentation{Schema}) = (CatDescType(pres),AttrDescType(pres))

"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to get a code block in the autogenerated docs you can add

    Presentation(CD::Type{T}, AD::Type{S}) where {T <: CatDesc, S <: AttrDesc}

to the first line of the docstring with 4 leading spaces (which is a code block in markdown)

Inverse of SchemaType. Converts a CatDesc and AttrDesc into a Presentation.
"""
function Presentation(CD::Type{T}, AD::Type{S}) where {T <: CatDesc, S <: AttrDesc}
pres = Presentation(FreeSchema)

obs = map(x -> Ob(FreeSchema, x), collect(ob(CD)))
homs = map(Hom, collect(hom(CD)), obs[collect(dom(CD))], obs[collect(codom(CD))])
datas = map(x -> Data(FreeSchema.Data, x), collect(data(AD)))
attrs = map(FreeSchema.Attr, collect(attr(AD)), obs[collect(adom(AD))], datas[collect(acodom(AD))])

map(gens -> add_generators!(pres, gens), [obs, homs, datas, attrs])
return pres
end
3 changes: 3 additions & 0 deletions test/categorical_algebra/CSets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ using Test
using Catlab, Catlab.Theories, Catlab.Graphs, Catlab.CategoricalAlgebra,
Catlab.CategoricalAlgebra.FinSets
using Catlab.Graphs.BasicGraphs: TheoryGraph
using Catlab.Present

function roundtrip_json_acset(x::T) where T <: AbstractACSet
mktempdir() do dir
Expand Down Expand Up @@ -373,4 +374,6 @@ add_parts!(wg, :E, 4, src=[1,2,3,4], tgt=[2,3,4,1], weight=[101, 102, 103, 100])
Dict(:src => id(X), :tgt => :Φ, :weight => [:Φ, :label]))
@test roundtrip_json_acset(ldds) == ldds

@test Presentation(Graph(1)) == TheoryGraph
@test Presentation(ldds) == TheoryLabeledDDS
end
4 changes: 4 additions & 0 deletions test/categorical_algebra/CategoricalAlgebra.jl
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,8 @@ end
include("StructuredCospans.jl")
end

@testset "DataMigration" begin
include("DataMigration.jl")
end

end
107 changes: 107 additions & 0 deletions test/categorical_algebra/DataMigration.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
module TestDataMigration
using Test

using Catlab.CategoricalAlgebra
using Catlab.Graphs.BasicGraphs: TheoryGraph
using Catlab.Theories: id
using Catlab.Present

@present ThBipartite(FreeSchema) begin
slibkind marked this conversation as resolved.
Show resolved Hide resolved
(V1, V2, E)::Ob
src::Hom(E, V1)
tgt::Hom(E, V2)
end

V1B, V2B, EB = generators(ThBipartite, :Ob)
srcB, tgtB = generators(ThBipartite, :Hom)

VG, EG = generators(TheoryGraph, :Ob)
srcG, tgtG = generators(TheoryGraph, :Hom)

F = Functor(
Dict{FreeSchema.Ob, FreeSchema.Ob}(V1B => VG, V2B => VG, EB => EG),
Dict{FreeSchema.Hom, FreeSchema.Hom}(srcB => srcG, tgtB => tgtG),
ThBipartite, TheoryGraph
)

idF = Functor(
Dict{FreeSchema.Ob, FreeSchema.Ob}(VG => VG, EG => EG),
Dict{FreeSchema.Hom, FreeSchema.Hom}(srcG => srcG, tgtG => tgtG),
TheoryGraph, TheoryGraph
)

ΣF = Σ(F)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you create the sigma migration, you could test the dom and codom functions to make sure they get covered by the tests. This will fix the uncovered lines related warnings on this PR.

X = ACSetType(ThBipartite, index=[:src, :tgt])()
slibkind marked this conversation as resolved.
Show resolved Hide resolved
Y = ΣF(X)

@test nparts(Y, :V) == 0
@test nparts(Y, :E) == 0

X = @acset ACSetType(ThBipartite, index=[:src, :tgt]) begin
V1 = 4
V2 = 3
E = 4
src = [1,2,2,3]
tgt = [1,1,2,3]
end

Y = ΣF(X)
@test nparts(Y, :V) == 7
@test nparts(Y, :E) == 4
@test length(Y[:src] ∩ Y[:tgt]) == 0

@test Σ(idF)(Y) == Y

@present ThSpan(FreeSchema) begin
(L1, L2, A)::Ob
l1::Hom(A, L1)
l2::Hom(A, L2)
end

X = @acset ACSetType(ThSpan, index=[:l1, :l2]) begin
slibkind marked this conversation as resolved.
Show resolved Hide resolved
L1 = 3
L2 = 4
A = 3
l1 = [1,1,2]
l2 = [1,2,3]
end

@present ThInitial(FreeSchema) begin
I::Ob
end

L1, L2, A = generators(ThSpan, :Ob)
l1, l2 = generators(ThSpan, :Hom)
I = generators(ThInitial)[1]

bang = Functor(
Dict{FreeSchema.Ob, FreeSchema.Ob}(L1 => I, L2 => I, A => I),
Dict{FreeSchema.Hom, FreeSchema.Hom}(l1 => id(I), l2 => id(I)),
ThSpan, ThInitial
)

Y = Σ(bang)(X)

@test nparts(Y, :I) == 4

vertex = Functor(
Dict{FreeSchema.Ob, FreeSchema.Ob}(I => VG),
Dict{FreeSchema.Hom, FreeSchema.Hom}(),
ThInitial, TheoryGraph
)
edge = Functor(
Dict{FreeSchema.Ob, FreeSchema.Ob}(I => EG),
Dict{FreeSchema.Hom, FreeSchema.Hom}(),
ThInitial, TheoryGraph
)

Z = Σ(vertex)(Y)
@test nparts(Z, :V) == 4
@test nparts(Z, :E) == 0

Z = Σ(edge)(Y)
@test nparts(Z, :V) == 8
@test nparts(Z, :E) == 4
@test Z[:src] ∪ Z[:tgt] == 1:8

end #module}
2 changes: 2 additions & 0 deletions test/theories/Schema.jl
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,5 @@ AD = AttrDescType(TheoryDecGraph)

@test dom(AD,:vdec) == :V
@test codom(AD,:edec) == :X

@test TheoryDecGraph == Presentation(CD, AD)