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

Find invalid entries in homs #62

Merged
merged 9 commits into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
24 changes: 22 additions & 2 deletions src/ACSetInterface.jl
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
module ACSetInterface
export ACSet, acset_schema, acset_name, dom_parts, subpart_type,
export ACSet, acset_schema, acset_name, dom_parts, codom_parts, subpart_type,
nparts, maxpart, parts, has_part, has_subpart, subpart, incident,
add_part!, add_parts!, set_subpart!, set_subparts!, clear_subpart!,
rem_part!, rem_parts!, cascading_rem_part!, cascading_rem_parts!, gc!,
copy_parts!, copy_parts_only!, disjoint_union, tables, pretty_tables,
@acset, constructor, PartsType, DenseParts, MarkAsDeleted, rem_free_vars!
@acset, constructor, undefined_subparts, PartsType, DenseParts, MarkAsDeleted,
rem_free_vars!, parts_type

using MLStyle: @match
using StaticArrays: StaticArray
Expand Down Expand Up @@ -113,6 +114,15 @@ where X is the dom of the f in the schema
"""
function dom_parts end

"""
Get the parts of the codomain of a morphism in an acset

dom_parts(acs, f) == parts(acs, Y)

where Y is the codom of the f in the schema
"""
function codom_parts end

"""
Get the type assigned to a subpart in an acset, i.e.

Expand Down Expand Up @@ -391,6 +401,16 @@ Get a nullary callable which constructs an (empty) ACSet of the same type
"""
function constructor end

"""
Given a hom, find which parts in its domain are undefined.
"""
function undefined_subparts end

"""
Get the type used to store parts IDs.
"""
parts_type(::ACSet{PT}) where {PT} = PT

# Pretty printing
#################

Expand Down
17 changes: 17 additions & 0 deletions src/DenseACSets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,14 @@ end
parts(acs, @ct dom(s, f))
end

@inline ACSetInterface.codom_parts(acs::StructACSet{S}, f::Symbol) where {S} = _codom_parts(acs, Val{S}, Val{f})
@inline ACSetInterface.codom_parts(acs::DynamicACSet, f::Symbol) = runtime(_codom_parts, acs, acs.schema, f)

@ct_enable function _codom_parts(acs, @ct(S), @ct(f))
@ct s = Schema(S)
parts(acs, @ct codom(s, f))
end

@inline function ACSetInterface.incident(acs::SimpleACSet, part, f::Symbol)
preimage(dom_parts(acs, f), acs.subparts[f], part)
end
Expand Down Expand Up @@ -644,6 +652,15 @@ end
ACSetInterface.cascading_rem_parts!(acs::ACSet, type, parts) =
delete_subobj!(acs, Dict(type=>parts))

function ACSetInterface.undefined_subparts(acs::SimpleACSet{<:DenseParts}, f::Symbol)
findall([!haskey(acs.subparts[f],i) for i in dom_parts(acs,f)])
end

function ACSetInterface.undefined_subparts(acs::SimpleACSet{<:MarkAsDeleted}, f::Symbol)
codom_ids = codom_parts(acs,f)
kris-brown marked this conversation as resolved.
Show resolved Hide resolved
findall([acs.subparts[f][i] ∉ codom_ids for i in dom_parts(acs,f)])
end

# Copy Parts
############

Expand Down
103 changes: 64 additions & 39 deletions test/ACSets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ for dds_maker in dds_makers
view(dds,:Φ) isa ColumnView

# Deletion.
@test_throws ArgumentError undefined_subparts(dds, :X)
@test undefined_subparts(dds, :Φ) == []
rem_part!(dds, :X, 2)
@test undefined_subparts(dds, :Φ) == [1]
@test nparts(dds, :X) == 2
@test incident(dds, 1, :Φ) == []
if dds.parts[:X] isa IntParts
Expand Down Expand Up @@ -583,50 +586,74 @@ RecSch = BasicSchema(
[],[]
)

# DenseParts
@acset_type RecDataInj(RecSch, index=[:src,:tgt], unique_index=[:thing])
@acset_type RecDataIdx(RecSch, index=[:src,:tgt,:thing])
@acset_type RecDataNoIdx(RecSch)

datainj = @acset RecDataInj begin
Thing=3
Node=3
Edge=3
thing=[1,2,3]
src=[1,1,2]
tgt=[1,2,3]
end

dataidx = @acset RecDataIdx begin
Thing=3
Node=3
Edge=3
thing=[1,2,3]
src=[1,1,2]
tgt=[1,2,3]
end
recdata_makers = [
RecDataInj,
RecDataIdx,
RecDataNoIdx,
() -> DynamicACSet("RecData", RecSch; index=[:src,:tgt]),
() -> AnonACSet(RecSch; index=[:src,:tgt])
]

datanoidx = @acset RecDataNoIdx begin
Thing=3
Node=3
Edge=3
thing=[1,2,3]
src=[1,1,2]
tgt=[1,2,3]
for recdata in recdata_makers
mydata = recdata()
add_parts!(mydata, :Node, 3)
add_parts!(mydata, :Thing, 3, thing=[1,2,3])
add_parts!(mydata, :Edge, 3, src=[1,1,2], tgt=[1,2,3])

@test parts_type(mydata) <: DenseParts

new2old = cascading_rem_parts!(mydata, :Node, 1)

@test length(new2old[:Thing]) == nparts(mydata,:Thing)
@test nparts(mydata,:Thing) == 2
@test length(new2old[:Node]) == nparts(mydata,:Node)
@test nparts(mydata,:Node) == 2
@test length(new2old[:Edge]) == nparts(mydata,:Edge)
@test nparts(mydata,:Edge) == 1
@test incident(mydata, 3, :thing) == []
@test incident(mydata, 3, :src) == []
@test incident(mydata, 3, :tgt) == []
end

map_inj = cascading_rem_parts!(datainj, :Node, 1)
map_idx = cascading_rem_parts!(dataidx, :Node, 1)
map_noidx = cascading_rem_parts!(datanoidx, :Node, 1)

@test map_inj == map_idx
@test map_idx == map_noidx
# MarkAsDeleted parts
@acset_type RecDataInjMarkDel(RecSch, index=[:src,:tgt], unique_index=[:thing], part_type=BitSetParts)
@acset_type RecDataIdxMarkDel(RecSch, index=[:src,:tgt,:thing], part_type=BitSetParts)
@acset_type RecDataNoIdxMarkDel(RecSch, part_type=BitSetParts)

recdata_makers = [
RecDataInjMarkDel,
RecDataIdxMarkDel,
RecDataNoIdxMarkDel,
() -> DynamicACSet("RecData", RecSch; index=[:src,:tgt], part_type=MarkAsDeleted),
() -> AnonACSet(RecSch; index=[:src,:tgt], part_type=MarkAsDeleted)
]

@test nparts(datainj,:Thing) == 2
@test nparts(datainj,:Node) == 2
@test nparts(datainj,:Edge) == 1
@test incident(datainj, 3, :thing) == []
@test incident(datainj, 3, :src) == []
@test incident(datainj, 3, :tgt) == []
for recdata in recdata_makers
mydata = recdata()
add_parts!(mydata, :Node, 3)
add_parts!(mydata, :Thing, 3, thing=[1,2,3])
add_parts!(mydata, :Edge, 3, src=[1,1,2], tgt=[1,2,3])

@test parts_type(mydata) <: MarkAsDeleted

new2old = cascading_rem_parts!(mydata, :Node, 1)

@test length(new2old[:Thing]) == nparts(mydata,:Thing)
@test nparts(mydata,:Thing) == 2
@test length(new2old[:Node]) == nparts(mydata,:Node)
@test nparts(mydata,:Node) == 2
@test length(new2old[:Edge]) == nparts(mydata,:Edge)
@test nparts(mydata,:Edge) == 1
# IDs are not updated in MarkAsDeleted, so "1" is the deleted element
@test incident(mydata, 1, :thing) == []
@test incident(mydata, 1, :src) == []
@test incident(mydata, 1, :tgt) == []
end

# attributes and an injective index
RecAttrSch = BasicSchema(
Expand All @@ -648,9 +675,7 @@ dataattr = @acset RecAttrData{String,Symbol,Float64} begin
attr3=[10.0,11.0,12.0]
end

map_attr = cascading_rem_parts!(dataattr, :Node, 1)

@test map_inj == map_attr
cascading_rem_parts!(dataattr, :Node, 1)

@test all(map(x -> x ∈ subpart(dataattr,:attr1), ["2","3"]))
@test only(subpart(dataattr,:attr2)) == :c
Expand Down