Skip to content

Commit

Permalink
Merge pull request #298 from epatters/cset-access-composition
Browse files Browse the repository at this point in the history
Composite morphisms and attributes in C-set accessors
  • Loading branch information
epatters authored Oct 19, 2020
2 parents e30d1c9 + 6bb97a7 commit ee276bc
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 6 deletions.
46 changes: 41 additions & 5 deletions src/categorical_algebra/CSetDataStructures.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ using Compat: isnothing, only
using PrettyTables: pretty_table
using StructArrays

using ...Theories: Schema, FreeSchema, dom, codom,
CatDesc, CatDescType, AttrDesc, AttrDescType, SchemaType,
ob_num, hom_num, data_num, attr_num, dom_num, codom_num
using ...Syntax: GATExpr, args
using ...Theories: Schema, FreeSchema, dom, codom, codom_num,
CatDesc, CatDescType, AttrDesc, AttrDescType, SchemaType
using ...Present

# Data types
Expand Down Expand Up @@ -324,11 +324,29 @@ end

""" Get subpart of part in C-set.
Both single and vectorized access are supported.
Both single and vectorized access are supported. Chaining, or composition, of
parts is also supported. For example, given a vertex-attributed graph `g`,
```
subpart(g, e, [:src, :vattr])
```
returns the vertex attribute of the source vertex of the edge `e`.
"""
subpart(acs::ACSet, part, name::Symbol) = subpart(acs,name)[part]
subpart(acs::ACSet, name::Symbol) = _subpart(acs,Val(name))

function subpart(acs::ACSet, part, names::AbstractVector{Symbol})
foldl(names, init=part) do part, name
subpart(acs, part, name)
end
end
subpart(acs::ACSet, part, expr::GATExpr) = subpart(acs, part, subpart_name(expr))

subpart_name(expr::GATExpr{:generator}) = first(expr)::Symbol
subpart_name(expr::GATExpr{:id}) = Symbol[]
subpart_name(expr::GATExpr{:compose}) = mapreduce(subpart_name, vcat, args(expr))

@generated function _subpart(acs::ACSet{CD,AD,Ts}, ::Val{name}) where
{CD,AD,Ts,name}
if name CD.hom
Expand All @@ -343,7 +361,15 @@ end
""" Get superparts incident to part in C-set.
If the subpart is indexed, this takes constant time; otherwise, it takes linear
time. Both single and vectorized access are supported.
time. As with [`subpart`](@ref), both single and vectorized access, as well as
chained access, are supported. Note that sequences of morphisms are supplied in
the usual left-to-right order, so that
```
incident(g, x, [:src, :vattr])
```
returns the list of all edges whose source vertex has vertex attribute `x`.
Note that when the subpart is indexed, this function returns a view of the
underlying index, which should not be mutated. To ensure that a fresh copy is
Expand All @@ -353,6 +379,16 @@ returned, regardless of whether indexing is enabled, set the keyword argument
incident(acs::ACSet, part, name::Symbol; copy::Bool=false) =
_incident(acs, part, Val(name); copy=copy)

function incident(acs::ACSet, part, names::AbstractVector{Symbol};
copy::Bool=false)
# Don't need to pass `copy` because copy will be made regardless.
foldr(names, init=part) do name, part
reduce(vcat, incident(acs, part, name), init=Int[])
end
end
incident(acs::ACSet, part, expr::GATExpr; kw...) =
incident(acs, part, subpart_name(expr); kw...)

@generated function _incident(acs::ACSet{CD,AD,Ts,Idxed}, part, ::Val{name};
copy::Bool=false) where {CD,AD,Ts,Idxed,name}
if name CD.hom
Expand Down
16 changes: 15 additions & 1 deletion test/categorical_algebra/CSetDataStructures.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
module TestCSetDataStructures
using Test

using Catlab: @present
using Catlab: @present, generator
using Catlab.Theories: compose, id
using Catlab.CSetDataStructures

# Discrete dynamical systems
Expand Down Expand Up @@ -145,6 +146,19 @@ set_subpart!(d, [4,5], :parent, 5)
@test subpart(d, 4, :height) == 10
@test subpart(d, :, :height) == [0,0,0,10,20]

# Chained accessors.
@test subpart(d, 3, [:parent, :parent]) == 5
@test subpart(d, 3, [:parent, :height]) == 10
@test incident(d, 5, [:parent, :parent]) == [1,2,3,4,5]
@test incident(d, 10, [:parent, :height]) == [1,2,3]

X, parent, height = generator.(Ref(TheoryDendrogram), [:X, :parent, :height])
@test subpart(d, 3, parent) == 4
@test subpart(d, 3, compose(parent, height)) == 10
@test subpart(d, 3, id(X)) == 3
@test incident(d, 10, compose(parent, height)) == [1,2,3]

# Copying parts.
d2 = Dendrogram{Int}()
copy_parts!(d2, d, X=[4,5])
@test nparts(d2, :X) == 2
Expand Down

0 comments on commit ee276bc

Please sign in to comment.