Skip to content

Commit 0f315f2

Browse files
committed
Added support to delete graphs
1 parent 9e36661 commit 0f315f2

File tree

10 files changed

+182
-30
lines changed

10 files changed

+182
-30
lines changed

docs/src/usage.md

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,34 @@ julia> ng.grv[3].grv # see that index is deleted
147147
{2, 0} undirected simple Int64 graph
148148
149149
```
150+
Similarly we can delete nodes directly by specifying the unrolled path to that node.
151+
For example the above block does equivalent job to this:
150152

151-
For now removing works only for nodes and not for graphs.
152-
Support of removing graphs is on the way (not particularly hard to implement).
153+
```jldoctest walkthrough
154+
julia> add_vertex!(ng; subgraphs = [3,2]) # restore previously removed node
155+
true
156+
157+
julia> rem_vertex!(ng, [3,2,3]) # directly remove node
158+
```
159+
If the specified path doesn't point to a node, but to a graph, this whole graph is deleted instead.
160+
161+
```jldoctest walkthrough
162+
julia> add_vertex!(ng, SimpleGraph(10); subgraphs=[3]); # add a graph that we will delete later
163+
164+
julia> ng.grv[3].grv
165+
3-element Vector{AbstractGraph}:
166+
{1, 0} undirected simple Int64 graph
167+
{2, 0} undirected simple Int64 graph
168+
{10, 0} undirected simple Int64 graph
169+
170+
julia> rem_vertex!(ng, [3,3]) # delete previously added graph
171+
172+
julia> ng.grv[3].grv
173+
2-element Vector{AbstractGraph}:
174+
{1, 0} undirected simple Int64 graph
175+
{2, 0} undirected simple Int64 graph
176+
177+
```
153178

154179
# Handling Edges
155180
Similarly we can use the `add_edge!` and `rem_edge!`.

src/functionality.jl

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,58 @@ function innergraph(ng::NestedGraph, v::AbstractVector)
7070
end
7171
end
7272

73+
"$(TYPEDSIGNATURES) Check if path id `v` can represent a graph."
74+
function isagraph(ng::NestedGraph, v::AbstractVector)
75+
if length(v) > 1
76+
if 0 <= v[1] <= length(ng.grv)
77+
isagraph(ng.grv[v[1]], v[2:end])
78+
else
79+
return false
80+
end
81+
elseif length(v) == 1
82+
return 0 <= v[1] <= length(ng.grv)
83+
end
84+
end
85+
isagraph(ng::AbstractGraph, v::AbstractVector) = false
86+
7387
"$(TYPEDSIGNATURES) For usage after single vertex removal only"
74-
function update_vmap_after_delete!(ng::NestedGraph, nver::Tuple{T,T}) where T<:Integer
88+
function update_vmapneds_after_delete!(ng::NestedGraph, nver::Tuple{T,T}) where T<:Integer
7589
for (i,vm) in enumerate(ng.vmap)
7690
if vm[1] == nver[1] && vm[2] > nver[2]
7791
ng.vmap[i] = (vm[1], vm[2]-1)
78-
break
7992
end
8093
end
94+
filter!(ned -> src(ned) != nver && dst(ned) != nver, ng.neds)
95+
for (i,ned) in enumerate(ng.neds)
96+
vms = src(ned)
97+
vmd = dst(ned)
98+
if vms[1] == nver[1] && vms[2] > nver[2]
99+
vms = (vms[1], vms[2]-1)
100+
end
101+
if vmd[1] == nver[1] && vmd[2] > nver[2]
102+
vmd = (vmd[1], vmd[2]-1)
103+
end
104+
ng.neds[i] = NestedEdge(vms, vmd)
105+
end
106+
end
107+
108+
function update_vmapneds_after_delete_graph!(ng::NestedGraph, grid::T) where T<:Integer
109+
for (i,vm) in enumerate(ng.vmap)
110+
if vm[1] > grid
111+
ng.vmap[i] = (vm[1]-1, vm[2])
112+
end
113+
end
114+
for (i,ned) in enumerate(ng.neds)
115+
vms = src(ned)
116+
vmd = dst(ned)
117+
if vms[1] > grid
118+
vms = (vms[1]-1, vms[2])
119+
end
120+
if vmd[1] > grid
121+
vmd = (vmd[1]-1, vmd[2])
122+
end
123+
ng.neds[i] = NestedEdge(vms, vmd)
124+
end
81125
end
82126

83127
removeemptygraphs_recursive!(gr::AbstractGraph) = true

src/graphsimpl.jl

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,25 @@ function Graphs.rem_vertex!(ng::NestedGraph, v::T) where T<:Integer
4343
nver = ng.vmap[v]
4444
rem_vertex!(ng.grv[nver[1]], nver[2])
4545
deleteat!(ng.vmap, v)
46-
update_vmap_after_delete!(ng, nver)
46+
update_vmapneds_after_delete!(ng, nver)
4747
end
4848

49+
"$(TYPEDSIGNATURES) Remove node or graph identified by `path2rem`."
50+
function Graphs.rem_vertex!(ng::NestedGraph, path2rem::Vector{T}) where T<:Integer
51+
if !isagraph(ng, path2rem)
52+
rem_vertex!(ng, roll_vertex(ng, path2rem))
53+
else
54+
gr2del = innergraph(ng, path2rem)
55+
for v in vertices(gr2del)
56+
path2remvert = vcat(path2rem, [1]) #always delete the first one. others will be pushed.
57+
rem_vertex!(ng, roll_vertex(ng, path2remvert))
58+
end
59+
parentgr = length(path2rem) > 1 ? innergraph(ng, path2rem[1:end-1]) : ng
60+
deleteat!(parentgr.grv, path2rem[end])
61+
update_vmapneds_after_delete_graph!(parentgr, path2rem[end])
62+
# updatge vmap and neds (the subgraph id part)
63+
end
64+
end
4965

5066
Graphs.add_edge!(ng::NestedGraph, ce::NestedEdge) = add_edge!(ng, edge(ng, ce))
5167
Graphs.add_edge!(ng::NestedGraph, e::Edge) = Graphs.add_edge!(ng, e.src, e.dst)

src/nestedgraph.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ $(TYPEDSIGNATURES)
5555
5656
`extrasubgraph` controls the `NestedGraph` should be initialized with an empty subgraph
5757
"""
58-
function NestedGraph{T,R,N}(;extrasubgraph::Bool=true) where {T,R,N}
58+
function NestedGraph{T,R,N}(;extrasubgraph::Bool=false) where {T,R,N}
5959
ng = NestedGraph(R(), Vector{N}(), Vector{NestedEdge{T}}(), Vector{Tuple{T,T}}())
6060
extrasubgraph && add_vertex!(ng, R())
6161
return ng

test/addremgraphsimple.jl

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
@testset "addremgraphsimple.jl" begin
2+
# test deleting nodes
3+
ngt = test_simple_top()
4+
@test length(ngt.neds) == 2
5+
rem_vertex!(ngt, 1)
6+
@test length(ngt.neds) == 1
7+
8+
# test deleting graphs
9+
ngt = test_simple_top()
10+
rem_vertex!(ngt, [1])
11+
@test length(Set(getindex.(ngt.vmap,1))) == 2
12+
@test length((ngt.grv)) == 2
13+
14+
ngt = test_simple_top()
15+
rem_vertex!(ngt, [3,2])
16+
@test length(Set(getindex.(ngt.grv[3].vmap,1))) == 2
17+
@test length((ngt.grv[3].grv)) == 2
18+
19+
ngt = test_simple_top()
20+
rem_vertex!(ngt, [3,3])
21+
@test length(Set(getindex.(ngt.grv[3].vmap,1))) == 2
22+
@test length((ngt.grv[3].grv)) == 2
23+
24+
ngt = test_simple_top()
25+
rem_vertex!(ngt, [3,3,1])
26+
@test length(Set(getindex.(ngt.grv[3].vmap,1))) == 2
27+
@test length((ngt.grv[3].grv)) == 3
28+
@test length(ngt.grv[3].grv[3].grv) == nv(ngt.grv[3].grv[3]) == ne(ngt.grv[3].grv[3]) == 0
29+
30+
# test deleting nodes
31+
ngt = test_simple_top()
32+
while nv(ngt) > 0
33+
rem_vertex!(ngt, unroll_vertex(ngt, nv(ngt)))
34+
end
35+
@test nv(ngt) == 0
36+
end

test/livetest.jl

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,18 @@
1+
##
2+
# This file is meant to start up a live-test the package
3+
##
4+
15
DynNG = NestedGraph{Int, SimpleGraph{Int}, AbstractGraph}
26
ngdyn = DynNG()
37
add_vertex!(ngdyn, SimpleGraph(3))
4-
@assert nv(ngdyn.grv[1]) == 3
5-
68
add_vertex!(ngdyn, SimpleGraph(6))
7-
@assert nv(ngdyn.grv[1]) == 3 && nv(ngdyn.grv[2]) == 6
8-
99
add_vertex!(ngdyn)
10-
@assert nv(ngdyn.grv[1]) == 4 && nv(ngdyn.grv[2]) == 6
11-
1210
add_vertex!(ngdyn, subgraphs = 2)
13-
@assert nv(ngdyn.grv[1]) == 4 && nv(ngdyn.grv[2]) == 7
14-
15-
ngin = add_vertex!(ngdyn, DynNG())
11+
add_vertex!(ngdyn, DynNG())
12+
ngin = length(ngdyn.grv)
1613
add_vertex!(ngdyn, subgraphs = ngin)
17-
@assert nv(ngdyn.grv[1]) == 4 && nv(ngdyn.grv[2]) == 7 && nv(ngdyn.grv[3]) == 1
18-
1914
add_vertex!(ngdyn, SimpleGraph(2), subgraphs = ngin)
20-
@assert nv(ngdyn.grv[1]) == 4 && nv(ngdyn.grv[2]) == 7 && nv(ngdyn.grv[3]) == 3
21-
2215
add_vertex!(ngdyn, DynNG(), subgraphs = ngin)
23-
@assert nv(ngdyn.grv[1]) == 4 && nv(ngdyn.grv[2]) == 7 && nv(ngdyn.grv[3]) == 3
24-
2516
add_vertex!(ngdyn, subgraphs=[3,2])
26-
@assert nv(ngdyn.grv[1]) == 4 && nv(ngdyn.grv[2]) == 7 && nv(ngdyn.grv[3]) == 4
27-
2817
add_vertex!(ngdyn, SimpleGraph(2), subgraphs=[3,3])
29-
@assert nv(ngdyn.grv[1]) == 4 && nv(ngdyn.grv[2]) == 7 && nv(ngdyn.grv[3]) == 6
30-
31-
@assert nv(ngdyn.grv[3].grv[1]) == 1 && nv(ngdyn.grv[3].grv[2]) == 3 && nv(ngdyn.grv[3].grv[3]) == 2
32-
@assert nv(ngdyn) == nv(ngdyn.grv[1]) + nv(ngdyn.grv[2]) + nv(ngdyn.grv[3])
18+
add_edge!(ngdyn, 1,8)

test/metaadd.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,6 @@
8787
@test nv(ngdyn) == nv(ngdyn.grv[1]) + nv(ngdyn.grv[2]) + nv(ngdyn.grv[3])
8888

8989
# extra subgraph
90-
ngdyn2 = DynMNG()
90+
ngdyn2 = DynMNG(;extrasubgraph=true)
9191
@test length(ngdyn2.grv) == 1
9292
end

test/runtests.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ using TestSetExtensions
55
include("testutils.jl")
66

77
@testset "NestedGraphs.jl" begin
8-
@includetests ["simple", "simpleadd", "metagraphs", "metaadd"]
8+
@includetests ["simple", "simpleadd", "metagraphs", "metaadd", "addremgraphsimple"]
99
end

test/simpleadd.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,6 @@
6969
@test nv(ngdyn) == nv(ngdyn.grv[1]) + nv(ngdyn.grv[2]) + nv(ngdyn.grv[3])
7070

7171
# extra subgraph
72-
ngdyn2 = DynNG()
72+
ngdyn2 = DynNG(;extrasubgraph=true)
7373
@test length(ngdyn2.grv) == 1
7474
end

test/testutils.jl

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,48 @@ function testprops_recu(nmg)
5454
gr isa NestedGraph && testprops_recu(gr)
5555
end
5656
end
57+
58+
function test_simple_top()
59+
DynNG = NestedGraph{Int, SimpleGraph{Int}, AbstractGraph}
60+
ngdyn = DynNG()
61+
add_vertex!(ngdyn, SimpleGraph(3))
62+
add_vertex!(ngdyn, SimpleGraph(6))
63+
add_vertex!(ngdyn)
64+
add_vertex!(ngdyn, subgraphs = 2)
65+
add_vertex!(ngdyn, DynNG())
66+
ngin = length(ngdyn.grv)
67+
add_vertex!(ngdyn, subgraphs = ngin)
68+
add_vertex!(ngdyn, SimpleGraph(2), subgraphs = ngin)
69+
add_vertex!(ngdyn, DynNG(), subgraphs = ngin)
70+
add_vertex!(ngdyn, subgraphs=[3,2])
71+
add_vertex!(ngdyn, SimpleGraph(2), subgraphs=[3,3])
72+
add_edge!(ngdyn, 1,8)
73+
add_edge!(ngdyn, 2,9)
74+
return ngdyn
75+
end
76+
77+
function test_meta_top()
78+
DynMNG = NestedGraph{Int, MetaGraph{Int,Float64}, AbstractGraph}
79+
ngdyn = DynMNG(extrasubgraph=false)
80+
sg1 = MetaGraph(3)
81+
[set_prop!(sg1, v, :vel, "1.$(v)") for v in vertices(sg1)]
82+
add_vertex!(ngdyn, sg1)
83+
add_vertex!(ngdyn, MetaGraph(6))
84+
# ngdyn.grv[2] and ngdyn.flatgr `Dict` are shallow copies
85+
[set_prop!(ngdyn.grv[2], v, :vel, "2.$(v)") for v in 1:6]
86+
# flatnode 10 enters in vmap (1,4)
87+
add_vertex!(ngdyn, :vel, "1.4")
88+
add_vertex!(ngdyn, :vel, "2.7"; subgraphs = 2)
89+
add_vertex!(ngdyn, DynMNG(extrasubgraph=false))
90+
add_vertex!(ngdyn, :vel, "3.1.1"; subgraphs = 3)
91+
sg2 = MetaGraph(2)
92+
[set_prop!(sg2, v, :vel, "3.1.$(v)") for v in vertices(sg2)]
93+
add_vertex!(ngdyn, sg2, subgraphs = 3)
94+
add_vertex!(ngdyn, DynMNG(extrasubgraph=false), subgraphs = 3)
95+
# flatnode 15 enters in subgraph (3,4)
96+
add_vertex!(ngdyn, :vel, "3.3.1",subgraphs=[3,2])
97+
sg3 = MetaGraph(2)
98+
[set_prop!(sg3, v, :vel, "3.3.2.$(v)") for v in vertices(sg3)]
99+
add_vertex!(ngdyn, sg3, subgraphs=[3,3])
100+
return ngdyn
101+
end

0 commit comments

Comments
 (0)