Skip to content

Commit

Permalink
Merge pull request #576 from epatters/migrations-with-attrs
Browse files Browse the repository at this point in the history
Data migration with data attributes
  • Loading branch information
epatters committed Nov 23, 2021
2 parents f96378c + b4b865c commit 0fd5c5f
Show file tree
Hide file tree
Showing 15 changed files with 283 additions and 69 deletions.
9 changes: 2 additions & 7 deletions src/categorical_algebra/CSetDataStructures.jl
Original file line number Diff line number Diff line change
Expand Up @@ -724,13 +724,8 @@ end

attrtype_instantiations = map(enumerate(s.attrtypes)) do (i,d)
if d affected_attrtypes
quote
T = eltype(fn_vals[$(q(abc[d][1]))])
$(Expr(:block, (map(abc[d][2:end]) do a
:(@assert T == eltype(fn_vals[$(q(a))]))
end)...))
T
end
:(mapreduce(eltype, typejoin,
$(Expr(:tuple, (:(fn_vals[$(q(a))]) for a in abc[d])...))))
else
:($(Ts[i]))
end
Expand Down
8 changes: 6 additions & 2 deletions src/categorical_algebra/CSets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,15 @@ end

# ACSets as set-valued FinDomFunctors.

# TODO: We should wrap `SchemaDescType` instead of creating a presentation.
const ACSetDomCat = FinCats.FinCatPresentation{
Symbol, Union{FreeSchema.Ob,FreeSchema.AttrType},
Union{FreeSchema.Hom,FreeSchema.Attr,FreeSchema.AttrType}}

""" Wrapper type to interpret attributed C-set as a functor.
"""
@auto_hash_equals struct ACSetFunctor{ACS<:ACSet} <:
Functor{FinCats.FinCatPresentation{Symbol,FreeSchema.Ob,FreeSchema.Hom},
TypeCat{SetOb,FinDomFunction{Int}}}
Functor{ACSetDomCat,TypeCat{SetOb,FinDomFunction{Int}}}
acset::ACS
end
FinDomFunctor(X::ACSet) = ACSetFunctor(X)
Expand Down
10 changes: 6 additions & 4 deletions src/categorical_algebra/DataMigrations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,14 @@ function migrate(X::FinDomFunctor, F::ConjSchemaMigration;
limits = make_map(ob_generators(tgt_schema)) do c
Fc = ob_map(F, c)
# XXX: Disable domain check because acsets don't store schema equations.
lim = limit(compose(Fc, X, strict=false), alg=ToBipartiteLimit())
lim = limit(compose(Fc, X, strict=false),
alg=SpecializeLimit(fallback=ToBipartiteLimit()))
if tabular
J = shape(Fc)
lim = TabularLimit(lim, names=(ob_name(J, j) for j in ob_generators(J)))
TabularLimit(lim, names=(ob_name(J, j) for j in ob_generators(J)))
else
lim
end
lim
end
funcs = make_map(hom_generators(tgt_schema)) do f
Ff, c, d = hom_map(F, f), dom(tgt_schema, f), codom(tgt_schema, f)
Expand All @@ -199,7 +201,7 @@ function migrate(X::FinDomFunctor, F::GlueSchemaMigration)
colimits = make_map(ob_generators(tgt_schema)) do c
Fc = ob_map(F, c)
# XXX: Force composition to tighten the codomain types.
colimit(force(compose(Fc, X, strict=false)))
colimit(force(compose(Fc, X, strict=false)), alg=SpecializeColimit())
end
funcs = make_map(hom_generators(tgt_schema)) do f
Ff, c, d = hom_map(F, f), dom(tgt_schema, f), codom(tgt_schema, f)
Expand Down
9 changes: 2 additions & 7 deletions src/categorical_algebra/Diagrams.jl
Original file line number Diff line number Diff line change
Expand Up @@ -227,13 +227,8 @@ end
# In a cocomplete category `C`, colimits define a functor `Diag{id,C} → C`.
# Dually, in a complete category `C`, limits define functor `Diag{op,C} → C`.

function limit(d::Diagram{op}; alg=nothing)
limit(diagram(d), (isnothing(alg) ? () : (alg,))...)
end

function colimit(d::Diagram{id}; alg=nothing)
colimit(diagram(d), (isnothing(alg) ? () : (alg,))...)
end
limit(d::Diagram{op}; alg=nothing) = limit(diagram(d), alg)
colimit(d::Diagram{id}; alg=nothing) = colimit(diagram(d), alg)

function universal(f::DiagramHom{op}, dom_lim, codom_lim)
J′ = shape(codom(f))
Expand Down
18 changes: 13 additions & 5 deletions src/categorical_algebra/FinCats.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ using StaticArrays: SVector
@reexport using ..Categories
using ...GAT, ...Present, ...Syntax
import ...Present: equations
using ...Theories: Category, Schema, ObExpr, HomExpr
using ...Theories: Category, Schema, ObExpr, HomExpr, AttrExpr, AttrTypeExpr
import ...Theories: dom, codom, id, compose, ,
using ...CSetDataStructures, ...Graphs
import ...Graphs: edges, src, tgt
Expand Down Expand Up @@ -207,8 +207,10 @@ end
The presentation type can, of course, be a category (`Theories.Category`). It
can also be a schema (`Theories.Schema`). In this case, the schema's objects and
attribute types are regarded as the category's objects and the schema's
morphisms and attributes as the category's morphisms. Formalizing the schema as
a profunctor, this amounts to taking the collage of the profunctor.
morphisms, attributes, and attribute types as the category's morphisms (where
the attribute types are identity morphisms). When the schema is formalized as a
profunctor whose codomain category is discrete, this amounts to taking the
collage of the profunctor.
"""
@auto_hash_equals struct FinCatPresentation{T,Ob,Hom} <: FinCat{Ob,Hom}
presentation::Presentation{T}
Expand All @@ -220,7 +222,9 @@ function FinCatPresentation(pres::Presentation{T}) where T
end
function FinCatPresentation(pres::Presentation{Schema})
S = pres.syntax
FinCatPresentation{Schema,Union{S.Ob,S.AttrType},Union{S.Hom,S.Attr}}(pres)
Ob = Union{S.Ob, S.AttrType}
Hom = Union{S.Hom, S.Attr, S.AttrType}
FinCatPresentation{Schema,Ob,Hom}(pres)
end

presentation(C::FinCatPresentation) = C.presentation
Expand Down Expand Up @@ -250,9 +254,13 @@ hom(C::FinCatPresentation, fs::AbstractVector) =
hom(C::FinCatPresentation, f::GATExpr) =
gat_typeof(f) == :Hom ? f : error("Expression $f is not a morphism")
hom(C::FinCatPresentation{Schema}, f::GATExpr) =
gat_typeof(f) (:Hom, :Attr) ? f :
gat_typeof(f) (:Hom, :Attr, :AttrType) ? f :
error("Expression $f is not a morphism or attribute")

id(C::FinCatPresentation{Schema}, x::AttrTypeExpr) = x
compose(C::FinCatPresentation{Schema}, f::AttrTypeExpr, g::AttrTypeExpr) =
(f == g) ? f : error("Invalid composite of attribute type identities: $f != $g")

function Base.show(io::IO, C::FinCatPresentation)
print(io, "FinCat(")
show(io, presentation(C))
Expand Down
58 changes: 44 additions & 14 deletions src/categorical_algebra/FinSets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -368,13 +368,12 @@ Sets.do_compose(f::Union{FinFunctionVector,IndexedFinFunction},
# Limits
########

function limit(Xs::EmptyDiagram{<:FinSet{Int}})
Limit(Xs, SMultispan{0}(FinSet(1)))
end
limit(Xs::EmptyDiagram{<:FinSet{Int}}) = Limit(Xs, SMultispan{0}(FinSet(1)))

function universal(lim::Limit{<:FinSet{Int},<:EmptyDiagram}, cone::SMultispan{0})
universal(lim::Limit{<:FinSet{Int},<:EmptyDiagram}, cone::SMultispan{0}) =
ConstantFunction(1, apex(cone), FinSet(1))
end

limit(Xs::SingletonDiagram{<:FinSet{Int}}) = limit(Xs, SpecializeLimit())

function limit(Xs::ObjectPair{<:FinSet{Int}})
m, n = length.(Xs)
Expand Down Expand Up @@ -665,6 +664,12 @@ function limit(d::BipartiteFreeDiagram{Ob,Hom}) where
@assert !any(isempty(incident(d, v, :tgt)) for v in vertices₂(d))
d_original = d

# For uniformity, e.g. when pairing below, ensure that all objects in layer 2
# are type sets.
if !all(x isa TypeSet for x in ob₂(d))
d = map(d, ob₁=identity, ob₂=ensure_type_set, hom=ensure_type_set_codom)
end

# It is generally optimal to compute all equalizers (self joins) first, so as
# to reduce the sizes of later pullbacks (joins) and products (cross joins).
d, ιs = equalize_all(d)
Expand Down Expand Up @@ -730,6 +735,14 @@ function limit(d::BipartiteFreeDiagram{Ob,Hom}) where
end
end

ensure_type_set(s::FinSet) = TypeSet(eltype(s))
ensure_type_set(s::TypeSet) = s
ensure_type_set_codom(f::FinFunction) =
SetFunctionCallable(f, dom(f), TypeSet(eltype(codom(f))))
ensure_type_set_codom(f::IndexedFinFunction) =
IndexedFinDomFunction(f.func, index=f.index)
ensure_type_set_codom(f::FinDomFunction) = f

""" Compute all possible equalizers in a bipartite free diagram.
The result is a new bipartite free diagram that has the same vertices but is
Expand Down Expand Up @@ -878,14 +891,15 @@ column_name(i::Integer) = Symbol("x$i") # Same default as DataFrames.jl.
# Colimits
##########

function colimit(Xs::EmptyDiagram{<:FinSet{Int}})
Colimit(Xs, SMulticospan{0}(FinSet(0)))
end
colimit(Xs::EmptyDiagram{<:FinSet{Int}}) = Colimit(Xs, SMulticospan{0}(FinSet(0)))

function universal(colim::Initial{<:FinSet{Int}}, cocone::SMulticospan{0})
FinFunction(Int[], apex(cocone))
cod = apex(cocone)
FinDomFunction(SVector{0,eltype(cod)}(), cod)
end

colimit(Xs::SingletonDiagram{<:FinSet{Int}}) = colimit(Xs, SpecializeColimit())

function colimit(Xs::ObjectPair{<:FinSet{Int}})
m, n = length.(Xs)
ι1 = FinFunction(1:m, m, m+n)
Expand All @@ -895,7 +909,7 @@ end

function universal(colim::BinaryCoproduct{<:FinSet{Int}}, cocone::Cospan)
f, g = cocone
FinFunction(vcat(collect(f), collect(g)), ob(colim), apex(cocone))
FinDomFunction(vcat(collect(f), collect(g)), ob(colim), apex(cocone))
end

function colimit(Xs::DiscreteDiagram{<:FinSet{Int}})
Expand All @@ -907,8 +921,9 @@ function colimit(Xs::DiscreteDiagram{<:FinSet{Int}})
end

function universal(colim::Coproduct{<:FinSet{Int}}, cocone::Multicospan)
FinFunction(reduce(vcat, (collect(f) for f in cocone), init=Int[]),
ob(colim), apex(cocone))
cod = apex(cocone)
FinDomFunction(mapreduce(collect, vcat, cocone, init=eltype(cod)[]),
ob(colim), cod)
end

function colimit(pair::ParallelPair{<:FinSet{Int}})
Expand Down Expand Up @@ -956,13 +971,28 @@ function pass_to_quotient(π::FinFunction{Int,Int}, h::FinFunction{Int,Int})
if q[j] == 0
q[j] = h(i)
else
q[j] == h(i) || error("Quotient map out of coequalizer is ill-defined")
q[j] == h(i) || error("Quotient map of colimit is ill-defined")
end
end
all(>(0), q) || error("Projection map is not surjective")
any(==(0), q) && error("Projection map is not surjective")
FinFunction(q, codom(h))
end

function pass_to_quotient::FinFunction{Int,Int}, h::FinDomFunction{Int})
@assert dom(π) == dom(h)
q = Vector{Union{Some{eltype(codom(h))},Nothing}}(nothing, length(codom(π)))
for i in dom(h)
j = π(i)
if isnothing(q[j])
q[j] = Some(h(i))
else
something(q[j]) == h(i) || error("Quotient map of colimit is ill-defined")
end
end
any(isnothing, q) && error("Projection map is not surjective")
FinDomFunction(map(something, q), codom(h))
end

function colimit(span::Multispan{<:FinSet{Int}})
colimit(span, ComposeCoproductCoequalizer())
end
Expand Down
4 changes: 3 additions & 1 deletion src/categorical_algebra/FreeDiagrams.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ and cospans. Limits and colimits are most commonly taken over free diagrams.
"""
module FreeDiagrams
export FreeDiagram, BipartiteFreeDiagram, FixedShapeFreeDiagram,
DiscreteDiagram, EmptyDiagram, ObjectPair,
DiscreteDiagram, EmptyDiagram, SingletonDiagram, ObjectPair,
Span, Cospan, Multispan, Multicospan, SMultispan, SMulticospan,
ParallelPair, ParallelMorphisms, ComposablePair, ComposableMorphisms,
diagram_type, cone_objects, cocone_objects,
Expand Down Expand Up @@ -76,9 +76,11 @@ DiscreteDiagram(objects::Obs, Hom::Type=Any) where {Ob,Obs<:AbstractVector{Ob}}
DiscreteDiagram{Ob,Hom,Obs}(objects)

const EmptyDiagram{Ob,Hom} = DiscreteDiagram{Ob,Hom,<:StaticVector{0,Ob}}
const SingletonDiagram{Ob,Hom} = DiscreteDiagram{Ob,Hom,<:StaticVector{1,Ob}}
const ObjectPair{Ob,Hom} = DiscreteDiagram{Ob,Hom,<:StaticVector{2,Ob}}

EmptyDiagram{Ob}(Hom::Type=Any) where Ob = DiscreteDiagram(SVector{0,Ob}(), Hom)
SingletonDiagram(ob, Hom::Type=Any) = DiscreteDiagram(SVector(ob), Hom)
ObjectPair(first, second, Hom::Type=Any) =
DiscreteDiagram(SVector(first, second), Hom)

Expand Down
Loading

0 comments on commit 0fd5c5f

Please sign in to comment.