Skip to content

Commit

Permalink
Merge pull request #62 from slwu89/find-undefined
Browse files Browse the repository at this point in the history
Find invalid entries in homs
  • Loading branch information
kris-brown authored Oct 3, 2023
2 parents b11b6c2 + 6fe42eb commit a058764
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 41 deletions.
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)
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

0 comments on commit a058764

Please sign in to comment.