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

Return hash map/set for vertices #56

Merged
merged 5 commits into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
26 changes: 13 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ julia> using NamedGraphs

julia> g = NamedGraph(grid((4,)), ["A", "B", "C", "D"])
NamedGraph{String} with 4 vertices:
4-element Vector{String}:
4-element Indices{String}
"A"
"B"
"C"
Expand All @@ -61,7 +61,7 @@ and 3 edge(s):

julia> g = NamedGraph(grid((4,)); vertices=["A", "B", "C", "D"]) # Same as above
NamedGraph{String} with 4 vertices:
4-element Vector{String}:
4-element Indices{String}
"A"
"B"
"C"
Expand Down Expand Up @@ -94,7 +94,7 @@ julia> neighbors(g, "B")

julia> g[["A", "B"]]
NamedGraph{String} with 2 vertices:
2-element Vector{String}:
2-element Indices{String}
"A"
"B"

Expand All @@ -119,7 +119,7 @@ the vertex names label cartesian coordinates:
```julia
julia> g = NamedGraph(grid((2, 2)); vertices=(2, 2))
NamedGraph{Tuple{Int64, Int64}} with 4 vertices:
4-element Vector{Tuple{Int64, Int64}}:
4-element Indices{Tuple{Int64, Int64}}
(1, 1)
(2, 1)
(1, 2)
Expand Down Expand Up @@ -162,7 +162,7 @@ You can use vertex names to get [induced subgraphs](https://juliagraphs.org/Grap
```julia
julia> subgraph(v -> v[1] == 1, g)
NamedGraph{Tuple{Int64, Int64}} with 2 vertices:
2-element Vector{Tuple{Int64, Int64}}:
2-element Indices{Tuple{Int64, Int64}}
(1, 1)
(1, 2)

Expand All @@ -172,7 +172,7 @@ and 1 edge(s):

julia> subgraph(v -> v[2] == 2, g)
NamedGraph{Tuple{Int64, Int64}} with 2 vertices:
2-element Vector{Tuple{Int64, Int64}}:
2-element Indices{Tuple{Int64, Int64}}
(1, 2)
(2, 2)

Expand All @@ -182,7 +182,7 @@ and 1 edge(s):

julia> g[[(1, 1), (2, 2)]]
NamedGraph{Tuple{Int64, Int64}} with 2 vertices:
2-element Vector{Tuple{Int64, Int64}}:
2-element Indices{Tuple{Int64, Int64}}
(1, 1)
(2, 2)

Expand All @@ -200,7 +200,7 @@ You can also take [disjoint unions](https://en.wikipedia.org/wiki/Disjoint_union
```julia
julia> g₁ = g
NamedGraph{Tuple{Int64, Int64}} with 4 vertices:
4-element Vector{Tuple{Int64, Int64}}:
4-element Indices{Tuple{Int64, Int64}}
(1, 1)
(2, 1)
(1, 2)
Expand All @@ -215,7 +215,7 @@ and 4 edge(s):

julia> g₂ = g
NamedGraph{Tuple{Int64, Int64}} with 4 vertices:
4-element Vector{Tuple{Int64, Int64}}:
4-element Indices{Tuple{Int64, Int64}}
(1, 1)
(2, 1)
(1, 2)
Expand All @@ -230,7 +230,7 @@ and 4 edge(s):

julia> disjoint_union(g₁, g₂)
NamedGraph{Tuple{Tuple{Int64, Int64}, Int64}} with 8 vertices:
8-element Vector{Tuple{Tuple{Int64, Int64}, Int64}}:
8-element Indices{Tuple{Tuple{Int64, Int64}, Int64}}
((1, 1), 1)
((2, 1), 1)
((1, 2), 1)
Expand All @@ -253,7 +253,7 @@ and 8 edge(s):

julia> g₁ ⊔ g₂ # Same as above
NamedGraph{Tuple{Tuple{Int64, Int64}, Int64}} with 8 vertices:
8-element Vector{Tuple{Tuple{Int64, Int64}, Int64}}:
8-element Indices{Tuple{Tuple{Int64, Int64}, Int64}}
((1, 1), 1)
((2, 1), 1)
((1, 2), 1)
Expand Down Expand Up @@ -293,7 +293,7 @@ The original graphs can be obtained from subgraphs:
```julia
julia> rename_vertices(v -> v[1], subgraph(v -> v[2] == 1, g₁ ⊔ g₂))
NamedGraph{Tuple{Int64, Int64}} with 4 vertices:
4-element Vector{Tuple{Int64, Int64}}:
4-element Indices{Tuple{Int64, Int64}}
(1, 1)
(2, 1)
(1, 2)
Expand All @@ -308,7 +308,7 @@ and 4 edge(s):

julia> rename_vertices(v -> v[1], subgraph(v -> v[2] == 2, g₁ ⊔ g₂))
NamedGraph{Tuple{Int64, Int64}} with 4 vertices:
4-element Vector{Tuple{Int64, Int64}}:
4-element Indices{Tuple{Int64, Int64}}
(1, 1)
(2, 1)
(1, 2)
Expand Down
107 changes: 36 additions & 71 deletions src/abstractnamedgraph.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,16 @@ parent_graph(graph::AbstractNamedGraph) = not_implemented()
# ?
parent_graph_type(graph::AbstractNamedGraph) = not_implemented()

function Graphs.has_vertex(graph::AbstractNamedGraph, vertex)
return not_implemented()
end

parent_vertextype(graph::AbstractNamedGraph) = vertextype(parent_graph(graph))
rem_vertex!(graph::AbstractNamedGraph, vertex) = not_implemented()
add_vertex!(graph::AbstractNamedGraph, vertex) = not_implemented()

# Convert vertex to parent vertex
# Inverse map of `parent_vertex_to_vertex`.
vertex_to_parent_vertex(graph::AbstractNamedGraph, vertex) = not_implemented()

# Convert parent vertex to vertex.
# Use `vertices`, assumes `vertices` is indexed by a parent vertex (a Vector for linear indexed parent vertices, a dictionary in general).
function parent_vertex_to_vertex(graph::AbstractNamedGraph, parent_vertex)
return vertices(graph)[parent_vertex]
end

Graphs.SimpleDiGraph(graph::AbstractNamedGraph) = SimpleDiGraph(parent_graph(graph))

# Convenient shorthands for using in higher order functions like `map`.
function vertex_to_parent_vertex(graph::AbstractNamedGraph)
return Base.Fix1(vertex_to_parent_vertex, graph)
end
function parent_vertex_to_vertex(graph::AbstractNamedGraph)
return Base.Fix1(parent_vertex_to_vertex, graph)
end
parent_vertex_to_vertex(graph::AbstractNamedGraph, parent_vertex) = not_implemented()

# TODO: rename `edge_type`?
edgetype(graph::AbstractNamedGraph) = not_implemented()
Expand All @@ -61,6 +46,39 @@ convert_vertextype(::Type, ::AbstractNamedGraph) = not_implemented()
# end
copy(graph::AbstractNamedGraph) = not_implemented()

# This should be overloaded for multi-dimensional indexing.
# Get the subset of vertices of the graph, for example
# for an input slice `subvertices(graph, "X", :)`.
function subvertices(graph::AbstractNamedGraph, vertices)
return not_implemented()
end

function merge_vertices!(
graph::AbstractNamedGraph, merge_vertices; merged_vertex=first(merge_vertices)
)
return not_implemented()
end

#
# Derived interface
#

function Graphs.has_vertex(graph::AbstractNamedGraph, vertex)
return vertex ∈ vertices(graph)
end

parent_vertextype(graph::AbstractNamedGraph) = vertextype(parent_graph(graph))

Graphs.SimpleDiGraph(graph::AbstractNamedGraph) = SimpleDiGraph(parent_graph(graph))

# Convenient shorthands for using in higher order functions like `map`.
function vertex_to_parent_vertex(graph::AbstractNamedGraph)
return Base.Fix1(vertex_to_parent_vertex, graph)
end
function parent_vertex_to_vertex(graph::AbstractNamedGraph)
return Base.Fix1(parent_vertex_to_vertex, graph)
end

zero(G::Type{<:AbstractNamedGraph}) = G()

# TODO: Implement using `copyto!`?
Expand All @@ -78,13 +96,6 @@ eltype(graph::AbstractNamedGraph) = eltype(vertices(graph))

parent_eltype(graph::AbstractNamedGraph) = eltype(parent_graph(graph))

# This should be overloaded for multi-dimensional indexing.
# Get the subset of vertices of the graph, for example
# for an input slice `subvertices(graph, "X", :)`.
function subvertices(graph::AbstractNamedGraph, vertices)
return not_implemented()
end

function subvertices(graph::AbstractNamedGraph{V}, vertices::Vector{V}) where {V}
return vertices
end
Expand Down Expand Up @@ -429,45 +440,6 @@ function union(
return union(union(graph1, graph2), graph3, graph_rest...)
end

function rem_vertex!(graph::AbstractNamedGraph, vertex)
if vertex ∉ vertices(graph)
return false
end

parent_vertex = vertex_to_parent_vertex(graph, vertex)
rem_vertex!(parent_graph(graph), parent_vertex)

# Insert the last vertex into the position of the vertex
# that is being deleted, then remove the last vertex.
last_vertex = last(vertices(graph))
vertices(graph)[parent_vertex] = last_vertex
last_vertex = pop!(vertices(graph))

# Insert the last vertex into the position of the vertex
# that is being deleted, then remove the last vertex.
# TODO: Make this more generic
graph.vertex_to_parent_vertex[last_vertex] = parent_vertex
# TODO: Make this more generic
delete!(graph.vertex_to_parent_vertex, vertex)

return true
end

function add_vertex!(graph::AbstractNamedGraph, vertex)
if vertex ∈ vertices(graph)
return false
end

add_vertex!(parent_graph(graph))
# Update the vertex list
push!(vertices(graph), vertex)
# Update the reverse map
# TODO: Make this more generic
insert!(graph.vertex_to_parent_vertex, vertex, last(parent_vertices(graph)))

return true
end

is_directed(G::Type{<:AbstractNamedGraph}) = is_directed(parent_graph_type(G))

is_directed(graph::AbstractNamedGraph) = is_directed(parent_graph(graph))
Expand Down Expand Up @@ -508,18 +480,11 @@ function connected_components(graph::AbstractNamedGraph)
return map(parent_vertices_to_vertices(graph), parent_connected_components)
end

function merge_vertices!(
graph::AbstractNamedGraph, merge_vertices; merged_vertex=first(merge_vertices)
)
return not_implemented()
end

function merge_vertices(
graph::AbstractNamedGraph, merge_vertices; merged_vertex=first(merge_vertices)
)
merged_graph = copy(graph)
add_vertex!(merged_graph, merged_vertex)

for vertex in merge_vertices
for e in incident_edges(graph, vertex; dir=:both)
merged_edge = rename_vertices(v -> v == vertex ? merged_vertex : v, e)
Expand Down
19 changes: 15 additions & 4 deletions src/distance.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,21 @@ function eccentricities(graph::AbstractGraph, vs, distmx=weights(graph))
return map(vertex -> eccentricity(graph, vertex, distmx), vs)
end

function eccentricities_center(eccentricities)
rad = eccentricities_radius(eccentricities)
return filter(x -> eccentricities[x] == rad, 1:length(eccentricities))
mtfishman marked this conversation as resolved.
Show resolved Hide resolved
end
function eccentricities_periphery(eccentricities)
diam = eccentricities_diameter(eccentricities)
return filter(x -> eccentricities[x] == diam, 1:length(eccentricities))
mtfishman marked this conversation as resolved.
Show resolved Hide resolved
end
eccentricities_radius(eccentricities) = minimum(eccentricities)
eccentricities_diameter(eccentricities) = maximum(eccentricities)

function _center(graph::AbstractNamedGraph, distmx)
# TODO: Why does this return the parent vertices?
return parent_vertices_to_vertices(
graph, center(eccentricities(graph, vertices(graph), distmx))
graph, eccentricities_center(eccentricities(graph, vertices(graph), distmx))
)
end

Expand All @@ -44,7 +55,7 @@ function center(graph::AbstractNamedGraph, distmx::AbstractMatrix)
end

function _radius(graph::AbstractNamedGraph, distmx)
return radius(eccentricities(graph, vertices(graph), distmx))
return eccentricities_radius(eccentricities(graph, vertices(graph), distmx))
end

function radius(graph::AbstractNamedGraph, distmx=weights(graph))
Expand All @@ -57,7 +68,7 @@ function radius(graph::AbstractNamedGraph, distmx::AbstractMatrix)
end

function _diameter(graph::AbstractNamedGraph, distmx)
return diameter(eccentricities(graph, vertices(graph), distmx))
return eccentricities_diameter(eccentricities(graph, vertices(graph), distmx))
end

function diameter(graph::AbstractNamedGraph, distmx=weights(graph))
Expand All @@ -72,7 +83,7 @@ end
function _periphery(graph::AbstractNamedGraph, distmx)
# TODO: Why does this return the parent vertices?
return parent_vertices_to_vertices(
graph, periphery(eccentricities(graph, vertices(graph), distmx))
graph, eccentricities_periphery(eccentricities(graph, vertices(graph), distmx))
)
end

Expand Down
40 changes: 35 additions & 5 deletions src/namedgraph.jl
Original file line number Diff line number Diff line change
@@ -1,18 +1,48 @@
struct GenericNamedGraph{V,G<:AbstractSimpleGraph{Int}} <: AbstractNamedGraph{V}
parent_graph::G
vertices::Vector{V}
parent_vertex_to_vertex::Vector{V}
vertex_to_parent_vertex::Dictionary{V,Int}
end

# AbstractNamedGraph required interface.
parent_graph_type(G::Type{<:GenericNamedGraph}) = fieldtype(G, :parent_graph)
parent_graph(graph::GenericNamedGraph) = getfield(graph, :parent_graph)
vertices(graph::GenericNamedGraph) = getfield(graph, :vertices)
function vertex_to_parent_vertex(graph::GenericNamedGraph, vertex)
return graph.vertex_to_parent_vertex[vertex]
end
function Graphs.has_vertex(graph::GenericNamedGraph, vertex)
return haskey(graph.vertex_to_parent_vertex, vertex)
function parent_vertex_to_vertex(graph::GenericNamedGraph, parent_vertex)
return graph.parent_vertex_to_vertex[parent_vertex]
end
vertices(graph::GenericNamedGraph) = keys(graph.vertex_to_parent_vertex)

function add_vertex!(graph::GenericNamedGraph, vertex)
if vertex ∈ vertices(graph)
return false
end
add_vertex!(graph.parent_graph)
# Update the forward map
push!(graph.parent_vertex_to_vertex, vertex)
# Update the reverse map
insert!(graph.vertex_to_parent_vertex, vertex, nv(graph.parent_graph))
return true
end

function rem_vertex!(graph::GenericNamedGraph, vertex)
if vertex ∉ vertices(graph)
return false
end
parent_vertex = graph.vertex_to_parent_vertex[vertex]
rem_vertex!(graph.parent_graph, parent_vertex)
# Insert the last vertex into the position of the vertex
# that is being deleted, then remove the last vertex.
last_vertex = last(graph.parent_vertex_to_vertex)
graph.parent_vertex_to_vertex[parent_vertex] = last_vertex
last_vertex = pop!(graph.parent_vertex_to_vertex)
# Insert the last vertex into the position of the vertex
# that is being deleted, then remove the last vertex.
graph.vertex_to_parent_vertex[last_vertex] = parent_vertex
delete!(graph.vertex_to_parent_vertex, vertex)
return true
end

function convert_vertextype(V::Type, graph::GenericNamedGraph)
Expand Down Expand Up @@ -172,7 +202,7 @@ end
is_directed(G::Type{<:GenericNamedGraph}) = is_directed(parent_graph_type(G))

# TODO: Implement an edgelist version
function induced_subgraph(graph::AbstractNamedGraph, subvertices::Vector)
function induced_subgraph(graph::AbstractNamedGraph, subvertices)
subgraph = typeof(graph)(subvertices)
subvertices_set = Set(subvertices)
for src in subvertices
Expand Down
6 changes: 3 additions & 3 deletions test/test_namedgraph.jl
Original file line number Diff line number Diff line change
Expand Up @@ -154,11 +154,11 @@ end
@test indegree(g, "D") == 0
@test outdegree(g, "D") == 0

@test degrees(g) == [1, 2, 1, 0]
@test degrees(g) == Dictionary(vertices(g), [1, 2, 1, 0])
@test degrees(g, ["B", "C"]) == [2, 1]
@test degrees(g, Indices(["B", "C"])) == Dictionary(["B", "C"], [2, 1])
@test indegrees(g) == [0, 1, 1, 0]
@test outdegrees(g) == [1, 1, 0, 0]
@test indegrees(g) == Dictionary(vertices(g), [0, 1, 1, 0])
@test outdegrees(g) == Dictionary(vertices(g), [1, 1, 0, 0])

h = degree_histogram(g)
@test h[0] == 1
Expand Down
Loading