From 0bad8fe78da3a6cddffc732b16875276ff795382 Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Tue, 20 Oct 2020 15:58:41 -0700 Subject: [PATCH 1/3] CLEANUP: More descriptive error message for missing subparts. --- src/categorical_algebra/CSetDataStructures.jl | 6 +++--- test/categorical_algebra/CSetDataStructures.jl | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/categorical_algebra/CSetDataStructures.jl b/src/categorical_algebra/CSetDataStructures.jl index a7dc094a5..b846d80e2 100644 --- a/src/categorical_algebra/CSetDataStructures.jl +++ b/src/categorical_algebra/CSetDataStructures.jl @@ -354,7 +354,7 @@ subpart_name(expr::GATExpr{:compose}) = mapreduce(subpart_name, vcat, args(expr) elseif name ∈ AD.attr :(acs.tables.$(dom(AD,name)).$name) else - throw(KeyError(name)) + throw(ArgumentError("$(repr(name)) not in $(CD.hom) or $(AD.attr)")) end end @@ -410,7 +410,7 @@ incident(acs::ACSet, part, expr::GATExpr; kw...) = :(broadcast_findall(part, acs.tables.$(dom(AD,name)).$name)) end else - throw(KeyError(name)) + throw(ArgumentError("$(repr(name)) not in $(CD.hom) or $(AD.attr)")) end end @@ -521,7 +521,7 @@ set_subpart!(acs::ACSet, name::Symbol, new_subpart) = :(acs.tables.$ob.$name[part] = subpart) end else - throw(KeyError(name)) + throw(ArgumentError("$(repr(name)) not in $(CD.hom) or $(AD.attr)")) end end diff --git a/test/categorical_algebra/CSetDataStructures.jl b/test/categorical_algebra/CSetDataStructures.jl index f8ad589a0..0e9d7bb18 100644 --- a/test/categorical_algebra/CSetDataStructures.jl +++ b/test/categorical_algebra/CSetDataStructures.jl @@ -44,8 +44,9 @@ set_subpart!(dds, 1, :Φ, 1) @test has_subpart(dds, :Φ) @test !has_subpart(dds, :nonsubpart) -@test_throws KeyError subpart(dds, 1, :nonsubpart) -@test_throws KeyError set_subpart!(dds, 1, :nonsubpart, 1) +@test_throws ArgumentError subpart(dds, 1, :nonsubpart) +@test_throws ArgumentError incident(dds, 1, :nonsuppart) +@test_throws ArgumentError set_subpart!(dds, 1, :nonsubpart, 1) # Deletion. dds = DDS() From 36fd6b6a1dd95c5955c6fdc7047ffe322e39667f Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Tue, 20 Oct 2020 16:22:05 -0700 Subject: [PATCH 2/3] ENH: Add convenience function `parts` for C-sets. A shorthand for `1:nparts(...)`. --- src/categorical_algebra/CSetDataStructures.jl | 10 +++++++--- src/categorical_algebra/CSets.jl | 2 +- src/graphs/BasicGraphs.jl | 6 +++--- src/wiring_diagrams/Undirected.jl | 9 ++++----- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/categorical_algebra/CSetDataStructures.jl b/src/categorical_algebra/CSetDataStructures.jl index b846d80e2..0f137bf9f 100644 --- a/src/categorical_algebra/CSetDataStructures.jl +++ b/src/categorical_algebra/CSetDataStructures.jl @@ -3,7 +3,7 @@ module CSetDataStructures export AbstractACSet, ACSet, AbstractCSet, CSet, Schema, FreeSchema, AbstractACSetType, ACSetType, ACSetTableType, AbstractCSetType, CSetType, - tables, nparts, has_part, subpart, has_subpart, incident, + tables, parts, nparts, has_part, subpart, has_subpart, incident, add_part!, add_parts!, set_subpart!, set_subparts!, rem_part!, rem_parts!, copy_parts!, copy_parts_only!, disjoint_union @@ -255,7 +255,7 @@ function Base.show(io::IO, ::MIME"text/plain", acs::T) where {T<:AbstractACSet} # TODO: Set option `row_number_column_title=name` when next version of # PrettyTables is released, instead of making new table. cols = map(col -> replace_unassigned(col, "#undef"), fieldarrays(table)) - table = StructArray((; ob => 1:nparts(acs,ob), cols...)) + table = StructArray((; ob => parts(acs, ob), cols...)) pretty_table(io, table, nosubheader=true) end end @@ -273,7 +273,7 @@ function Base.show(io::IO, ::MIME"text/html", acs::T) where {T<:AbstractACSet} if !(eltype(table) <: EmptyTuple || isempty(table)) # TODO: Set option `row_number_column_title`. See above. cols = map(col -> replace_unassigned(col, "#undef"), fieldarrays(table)) - table = StructArray((; ob => 1:nparts(acs,ob), cols...)) + table = StructArray((; ob => parts(acs, ob), cols...)) pretty_table(io, table, backend=:html, standalone=false, nosubheader=true) end end @@ -298,6 +298,10 @@ directly mutate these tables, especially when indexing is enabled! """ tables(acs::ACSet) = acs.tables +""" Parts of given type in a C-set. +""" +parts(acs::ACSet, type) = 1:nparts(acs, type) + """ Number of parts of given type in a C-set. """ nparts(acs::ACSet, type) = length(acs.tables[type]) diff --git a/src/categorical_algebra/CSets.jl b/src/categorical_algebra/CSets.jl index 0a6cd4b04..8923daf6b 100644 --- a/src/categorical_algebra/CSets.jl +++ b/src/categorical_algebra/CSets.jl @@ -182,7 +182,7 @@ function colimit(diagram::AbstractFreeDiagram{ACS}) where T = Ts.parameters[d] data = Vector{Union{Some{T},Nothing}}(nothing, nparts(Y, c)) for (ι, X) in zip(ιs, Xs) - for i in 1:nparts(X, c) + for i in parts(X, c) j = ι[c](i) if isnothing(data[j]) data[j] = Some(subpart(X, i, attr)) diff --git a/src/graphs/BasicGraphs.jl b/src/graphs/BasicGraphs.jl index 1c47e3a5b..5fa578382 100644 --- a/src/graphs/BasicGraphs.jl +++ b/src/graphs/BasicGraphs.jl @@ -51,8 +51,8 @@ src(g::AbstractACSet, args...) = subpart(g, args..., :src) tgt(g::AbstractACSet, args...) = subpart(g, args..., :tgt) dst(g::AbstractACSet, args...) = tgt(g, args...) # LightGraphs compatibility -vertices(g::AbstractACSet) = 1:nv(g) -edges(g::AbstractACSet) = 1:ne(g) +vertices(g::AbstractACSet) = parts(g, :V) +edges(g::AbstractACSet) = parts(g, :E) edges(g::AbstractACSet, src::Int, tgt::Int) = (e for e in incident(g, src, :src) if subpart(g, e, :tgt) == tgt) @@ -279,7 +279,7 @@ end vertex(g::AbstractACSet, args...) = subpart(g, args..., :vertex) -half_edges(g::AbstractACSet) = 1:nparts(g, :H) +half_edges(g::AbstractACSet) = parts(g, :H) half_edges(g::AbstractACSet, v) = incident(g, v, :vertex) function half_edge_pairs(g::AbstractACSet, src::Int, tgt::Int) diff --git a/src/wiring_diagrams/Undirected.jl b/src/wiring_diagrams/Undirected.jl index 8754237a1..fa88c02da 100644 --- a/src/wiring_diagrams/Undirected.jl +++ b/src/wiring_diagrams/Undirected.jl @@ -81,13 +81,12 @@ end nboxes(d::AbstractUWD) = nparts(d, :Box) njunctions(d::AbstractUWD) = nparts(d, :Junction) -boxes(d::AbstractUWD) = 1:nboxes(d) -junctions(d::AbstractUWD) = 1:njunctions(d) +boxes(d::AbstractUWD) = parts(d, :Box) +junctions(d::AbstractUWD) = parts(d, :Junction) -ports(d::AbstractUWD; outer::Bool=false) = - 1:nparts(d, outer ? :OuterPort : :Port) +ports(d::AbstractUWD; outer::Bool=false) = parts(d, outer ? :OuterPort : :Port) ports(d::AbstractUWD, box) = - box == outer_box(d) ? (1:nparts(d, :OuterPort)) : incident(d, box, :box) + box == outer_box(d) ? parts(d, :OuterPort) : incident(d, box, :box) ports_with_junction(d::AbstractUWD, junction; outer::Bool=false) = incident(d, junction, outer ? :outer_junction : :junction) From bfe87ca0fefa806a72998294af7eabffb35b4ee1 Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Tue, 20 Oct 2020 16:59:12 -0700 Subject: [PATCH 3/3] ENH: Indexing notation for C-sets. Syntactic sugar for the `subpart` function. --- src/categorical_algebra/CSetDataStructures.jl | 17 +++++++++++++++-- test/categorical_algebra/CSetDataStructures.jl | 5 +++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/categorical_algebra/CSetDataStructures.jl b/src/categorical_algebra/CSetDataStructures.jl index 0f137bf9f..2f718e7c5 100644 --- a/src/categorical_algebra/CSetDataStructures.jl +++ b/src/categorical_algebra/CSetDataStructures.jl @@ -329,13 +329,24 @@ end """ Get subpart of part in C-set. Both single and vectorized access are supported. Chaining, or composition, of -parts is also supported. For example, given a vertex-attributed graph `g`, +subparts 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`. +returns the vertex attribute of the source vertex of the edge `e`. As a +shorthand, subparts can also be accessed by indexing: + +``` +g[e, :src] == subpart(g, e, :src) +``` + +Be warned that indexing with lists of subparts works as above: +`g[e,[:src,:vattr]]` is equivalent to `subpart(g, e, [:src,:vattr])`. This +differs from DataFrames but note that the alternative interpretation of +`[:src,:vattr]` as two independent columns does not even make sense, since they +have different domains (belong to different tables). """ subpart(acs::ACSet, part, name::Symbol) = subpart(acs,name)[part] subpart(acs::ACSet, name::Symbol) = _subpart(acs,Val(name)) @@ -362,6 +373,8 @@ subpart_name(expr::GATExpr{:compose}) = mapreduce(subpart_name, vcat, args(expr) end end +Base.getindex(acs::ACSet, args...) = subpart(acs, args...) + """ Get superparts incident to part in C-set. If the subpart is indexed, this takes constant time; otherwise, it takes linear diff --git a/test/categorical_algebra/CSetDataStructures.jl b/test/categorical_algebra/CSetDataStructures.jl index 0e9d7bb18..554ed2c9c 100644 --- a/test/categorical_algebra/CSetDataStructures.jl +++ b/test/categorical_algebra/CSetDataStructures.jl @@ -159,6 +159,11 @@ X, parent, height = TheoryDendrogram[[:X, :parent, :height]] @test subpart(d, 3, id(X)) == 3 @test incident(d, 10, compose(parent, height)) == [1,2,3] +# Indexing syntax. +@test d[3, :parent] == 4 +@test d[3, [:parent, :height]] == 10 +@test d[:, :parent] == [4,4,4,5,5] + # Copying parts. d2 = Dendrogram{Int}() copy_parts!(d2, d, X=[4,5])