Skip to content
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
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "NamedGraphs"
uuid = "678767b0-92e7-4007-89e4-4527a8725b19"
version = "0.11.2"
version = "0.11.3"
authors = ["Matthew Fishman <mfishman@flatironinstitute.org>, Joseph Tindall <jtindall@flatironinstitute.org> and contributors"]

[workspace]
Expand Down
82 changes: 34 additions & 48 deletions src/abstractnamedgraph.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using .GraphsExtensions: GraphsExtensions, directed_graph, incident_edges,
using .GraphsExtensions: GraphsExtensions, all_edges, directed_graph, incident_edges,
partition_vertices, rem_edges, rem_edges!, rem_vertices, rename_vertices,
similar_simplegraph, subgraph
using Dictionaries: set!
Expand Down Expand Up @@ -52,56 +52,40 @@ GraphsExtensions.convert_vertextype(::Type{V}, g::AbstractNamedGraph{V}) where {
GraphsExtensions.convert_vertextype(::Type, g::AbstractNamedGraph) = not_implemented()

function similar_graph(graph::AbstractGraph)
return similar_graph(graph, copy(vertices(graph)), copy(edges(graph)))
end

function similar_graph(graph::AbstractGraph, vertices)
new_edge_type = convert_vertextype(eltype(vertices), edgetype(graph))
return similar_graph(graph, vertices, new_edge_type[])
newgraph = similar_graph(graph, vertices(graph))
add_edges!(newgraph, edges(graph))
return newgraph
end

# Construct `GenericNamedGraph` as a fallback.
@traitfn function similar_graph(
::AbstractGraph::(!IsDirected),
vertices,
edges
graph::AbstractGraph::(!IsDirected),
vertices
)
V = eltype(vertices)
graph = add_edges!(NamedGraph{V}(vertices), edges)
return graph
return NamedGraph{V}(vertices)
end
@traitfn function similar_graph(
::AbstractGraph::IsDirected,
vertices,
edges
graph::AbstractGraph::IsDirected,
vertices
)
V = eltype(vertices)
graph = add_edges!(NamedDiGraph{V}(vertices), edges)
return graph
return NamedDiGraph{V}(vertices)
end

# Passing a type as a first argument attempts to call a constructor. Should be overloaded
# if the constructor doesnt exist for a given `AbstractGraph` concrete type.
similar_graph(T::Type{<:AbstractGraph}) = T()
similar_graph(T::Type{<:AbstractGraph}) = similar_graph(T, vertextype(T)[])
similar_graph(T::Type{<:AbstractGraph}, vertices) = T(vertices)
function similar_graph(T::Type{<:AbstractGraph}, vertices, edges)
graph = similar_graph(T, vertices)
add_edges!(graph, edges)
return graph
end

# If `T <: AbstractSimpleGraph`, then we defer to `GraphsExtensions.similar_simplegraph`.
function similar_graph(T::Type{<:AbstractSimpleGraph}, vertices = 0, edges = [])
return similar_simplegraph(T, vertices, edges)
function similar_graph(T::Type{<:AbstractSimpleGraph}, vertices = 0)
return similar_simplegraph(T, vertices)
end

edgeless_graph(graph::AbstractGraph) = rem_edges(graph, edges(graph))
# The intention is this will fail if `T` cannot be edgeless.
edgeless_graph(T::Type{<:AbstractGraph}, vertices) = similar_graph(T, vertices, [])

empty_graph(graph::AbstractGraph) = rem_vertices(graph, vertices(graph))
# The intention is this will fail if `T` cannot be empty.
empty_graph(T::Type{<:AbstractGraph}) = similar_graph(T, vertextype(T)[], [])

Base.copy(graph::AbstractNamedGraph) = copyto!(similar_graph(graph), graph)

Expand Down Expand Up @@ -413,12 +397,9 @@ function Graphs.has_path(
end

function Base.union(graph1::AbstractNamedGraph, graph2::AbstractNamedGraph)
union_graph = promote_type(typeof(graph1), typeof(graph2))()
union_vertices = union(vertices(graph1), vertices(graph2))
union_graph = similar_graph(graph1, union_vertices)

for v in union_vertices
add_vertex!(union_graph, v)
end
for e in edges(graph1)
add_edge!(union_graph, e)
end
Expand Down Expand Up @@ -448,7 +429,9 @@ Graphs.is_connected(graph::AbstractNamedGraph) = is_connected(position_graph(gra
Graphs.is_cyclic(graph::AbstractNamedGraph) = is_cyclic(position_graph(graph))

@traitfn function Base.reverse(graph::AbstractNamedGraph::IsDirected)
return similar_graph(graph, vertices, map(reverse, collect(edges(graph))))
newgraph = edgeless_graph(graph)
add_edges!(newgraph, map(reverse, edges(graph)))
return newgraph
end

# This wont be the most efficient way for a given graph type.
Expand Down Expand Up @@ -603,15 +586,15 @@ end
# TODO: Implement an edgelist version
function induced_subgraph_from_vertices(graph::AbstractGraph, subvertices)
subgraph = similar_graph(graph, collect(subvertices))
add_edges!(subgraph, subgraph_edges(graph, subvertices))
return subgraph, nothing
end

function subgraph_edges(graph::AbstractGraph, subvertices)
subvertices_set = Set(subvertices)
for src in subvertices
for dst in outneighbors(graph, src)
if dst in subvertices_set && has_edge(graph, src, dst)
add_edge!(subgraph, src => dst)
end
end
return Iterators.filter(edges(graph)) do edge
return src(edge) in subvertices_set && dst(edge) in subvertices_set
end
return subgraph, nothing
end

function GraphsExtensions.edge_subgraph(graph::AbstractNamedGraph, edges)
Expand All @@ -631,19 +614,22 @@ function edge_subgraph_namedgraph(graph, edgelist)
return g
end

@traitfn function GraphsExtensions.directed_graph(graph::AbstractGraph::(!IsDirected))
digraph = similar_graph(directed_graph_type(graph), vertices(graph), edges(graph))
for e in edges(graph)
add_edge!(digraph, reverse(e))
end
@traitfn function GraphsExtensions.directed_graph(graph::AbstractNamedGraph::(!IsDirected))
digraph = similar_graph(directed_graph_type(graph), vertices(graph))
add_edges!(digraph, all_edges(graph))
return digraph
end

@traitfn function GraphsExtensions.undirected_graph(graph::AbstractGraph::IsDirected)
undigraph = edgeless_graph(undirected_graph_type(graph), vertices(graph))
@traitfn function GraphsExtensions.undirected_graph(graph::AbstractNamedGraph::IsDirected)
undigraph = similar_graph(undirected_graph_type(graph), vertices(graph))
for e in edges(graph)
has_edge(undigraph, e) && continue
add_edge!(undigraph, e)
end
return undigraph
end

function GraphsExtensions.forest_cover_edge_sequence(graph::AbstractNamedGraph; kwargs...)
dummy_graph = add_edges!(NamedGraph(vertices(graph)), edges(graph))
return GraphsExtensions.forest_cover_edge_sequence(dummy_graph; kwargs...)
end
25 changes: 7 additions & 18 deletions src/lib/GraphsExtensions/src/abstractgraph.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,36 +29,29 @@ convert_vertextype(::Type, ::Type{<:AbstractGraph}) = not_implemented()
# ==================================== similar_simplegraph =============================== #

function similar_simplegraph(graph::AbstractGraph)
return similar_simplegraph(graph, vertices(graph), edges(graph))
newgraph = similar_simplegraph(graph, vertices(graph))
add_edges!(newgraph, edges(graph))
return newgraph
end

function similar_simplegraph(graph::AbstractGraph, vertices)
new_edge_type = convert_vertextype(eltype(vertices), edgetype(graph))
return similar_simplegraph(graph, vertices, new_edge_type[])
end

function similar_simplegraph(graph::AbstractGraph, vertices::Base.OneTo, edges)
return similar_simplegraph(graph, length(vertices), edges)
function similar_simplegraph(graph::AbstractGraph, vertices::Base.OneTo)
return similar_simplegraph(graph, length(vertices))
end
# To be specialized (optional, has following fallback)
@traitfn function similar_simplegraph(
graph::AbstractGraph::(!IsDirected),
nvertices::Int,
edges
nvertices::Int
)
new_graph = SimpleGraph(nvertices)
add_edges!(new_graph, edges)
return new_graph
end

# To be specialized (optional, has following fallback)
@traitfn function similar_simplegraph(
graph::AbstractGraph::IsDirected,
nvertices::Int,
edges
nvertices::Int
)
new_graph = SimpleDiGraph(nvertices)
add_edges!(new_graph, edges)
return new_graph
end

Expand All @@ -68,10 +61,6 @@ function similar_simplegraph(T::Type{<:AbstractSimpleGraph}, vertices::Base.OneT
return similar_simplegraph(T, length(vertices))
end
similar_simplegraph(T::Type{<:AbstractSimpleGraph}, nvertices::Int) = T(nvertices)
function similar_simplegraph(T::Type{<:AbstractSimpleGraph}, vertices, edges)
new_graph = similar_simplegraph(T, vertices)
return add_edges!(new_graph, edges)
end

@traitfn directed_graph(graph::::IsDirected) = graph
@traitfn undirected_graph(graph::::(!IsDirected)) = graph
Expand Down
6 changes: 2 additions & 4 deletions src/lib/GraphsExtensions/src/simplegraph.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,8 @@ directed_graph_type(G::Type{<:SimpleDiGraph}) = G
undirected_graph_type(G::Type{<:SimpleDiGraph}) = SimpleGraph{vertextype(G)}

@traitfn function directed_graph(graph::AbstractSimpleGraph::(!IsDirected))
digraph = similar_simplegraph(directed_graph_type(graph), vertices(graph), edges(graph))
for e in edges(graph)
add_edge!(digraph, reverse(e))
end
digraph = similar_simplegraph(directed_graph_type(graph), vertices(graph))
add_edges!(digraph, all_edges(graph))
return digraph
end

Expand Down
13 changes: 13 additions & 0 deletions src/lib/GraphsExtensions/src/trees_and_forests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,18 @@ function forest_cover(g::AbstractGraph; spanning_tree = spanning_tree)
return identity.(forests)
end

function forest_cover_edge_sequence(g::AbstractGraph; root_vertex = default_root_vertex)
forests = forest_cover(g)
rv = edgetype(g)[]
for forest in forests
trees = [subgraph(forest, vs) for vs in connected_components(forest)]
for tree in trees
tree_edges = post_order_dfs_edges(tree, root_vertex(tree))
push!(rv, vcat(tree_edges, reverse(reverse.(tree_edges)))...)
end
end
return rv
end

# TODO: Define in `NamedGraphs.PartitionedGraphs`.
# forest_cover(g::PartitionedGraph; kwargs...) = not_implemented()
4 changes: 0 additions & 4 deletions src/lib/GraphsExtensions/test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -497,10 +497,6 @@ using Test: @test, @test_broken, @test_throws, @testset
@test isempty(edges(similar_simplegraph(g, vertices(g))))
@test isempty(edges(similar_simplegraph(typeof(g), vertices(g))))

@test similar_simplegraph(g, vertices(g), edges(g)) == g
@test similar_simplegraph(typeof(g), vertices(g), edges(g)) == g
@test !(similar_simplegraph(g, vertices(g), edges(g)) === g)

# add_edge
g = SimpleGraph(4)
add_edge!(g, 1 => 2)
Expand Down
2 changes: 0 additions & 2 deletions src/lib/OrderedDictionaries/src/orderedindices.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ struct OrderedIndices{I} <: AbstractIndices{I}
end
OrderedIndices(indices) = OrderedIndices{eltype(indices)}(indices)

OrderedIndices{I}(indices::OrderedIndices{I}) where {I} = copy(indices)

ordered_indices(indices::OrderedIndices) = getfield(indices, :ordered_indices)
# TODO: Better name for this?
index_positions(indices::OrderedIndices) = getfield(indices, :index_positions)
Expand Down
38 changes: 10 additions & 28 deletions src/namedgraph.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@ using Graphs: Graphs, AbstractGraph, add_edge!, add_vertex!, edgetype, has_edge,
struct GenericNamedGraph{V, G <: AbstractSimpleGraph{Int}} <: AbstractNamedGraph{V}
position_graph::G
vertices::OrderedIndices{V}
global function _GenericNamedGraph(position_graph, vertices)
@assert length(vertices) == nv(position_graph)
return new{eltype(vertices), typeof(position_graph)}(position_graph, vertices)
function GenericNamedGraph{V, G}(graph, vertices) where {V, G}
position_graph = G(graph)
vertices = OrderedIndices{V}(to_vertices(position_graph, vertices))

@assert length(vertices) == nv(graph)

return new{V, G}(position_graph, vertices)
end
end

Expand Down Expand Up @@ -80,24 +84,6 @@ to_vertices(vertices) = vertices
to_vertices(vertices::AbstractArray) = vec(vertices)
to_vertices(vertices::Integer) = Base.OneTo(vertices)

# Inner constructor
# TODO: Is this needed?
function GenericNamedGraph{V, G}(
position_graph::G, vertices::OrderedIndices{V}
) where {V, G <: AbstractSimpleGraph{Int}}
return _GenericNamedGraph(position_graph, vertices)
end

function GenericNamedGraph{V, G}(
position_graph::AbstractSimpleGraph, vertices
) where {V, G <: AbstractSimpleGraph{Int}}
return GenericNamedGraph{V, G}(
convert(G, position_graph), OrderedIndices{V}(
to_vertices(position_graph, vertices)
)
)
end

function GenericNamedGraph{V}(position_graph::AbstractSimpleGraph, vertices) where {V}
return GenericNamedGraph{V, typeof(position_graph)}(position_graph, vertices)
end
Expand Down Expand Up @@ -225,16 +211,12 @@ const NamedDiGraph{V} = GenericNamedGraph{V, SimpleDiGraph{Int}}

function similar_graph(
::GenericNamedGraph{<:Any, G},
vertices,
edges
vertices
) where {G}
V = eltype(vertices)
graph = similar_graph(GenericNamedGraph{V, G}, vertices, edges)
graph = similar_graph(GenericNamedGraph{V, G}, vertices)
# HACK: Unsure why this annotation is needed, but some type inference fails without it.
return graph::GenericNamedGraph{V, G}
end

function similar_graph(T::Type{<:GenericNamedGraph}, vertices, edges)
graph = add_edges!(T(vertices), edges)
return graph
end
similar_graph(T::Type{<:GenericNamedGraph}, vertices) = T(vertices)
3 changes: 2 additions & 1 deletion src/steiner_tree.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ function namedgraph_steiner_tree(
push!(featured_vertices, dst(named_edge))
end

tree = similar_graph(g, featured_vertices, named_edges)
tree = NamedGraph(featured_vertices)
add_edges!(tree, named_edges)

return tree
end
Expand Down
12 changes: 3 additions & 9 deletions test/test_abstractnamedgraph.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,15 @@ end

TestGraph{V}(vertices = V[]) where {V} = TestGraph(NamedGraph(vertices))

function NamedGraphs.similar_graph(g::Type{<:TestGraph}, vertices, edges)
return TestGraph(similar_graph(NamedGraph, vertices, edges))
function NamedGraphs.similar_graph(g::Type{<:TestGraph}, vertices)
return TestGraph(similar_graph(NamedGraph, vertices))
end

Graphs.vertices(g::TestGraph) = vertices(g.graph)
Graphs.edges(g::TestGraph) = edges(g.graph)

Graphs.is_directed(::Type{<:TestGraph}) = false

Base.:(==)(g1::TestGraph, g2::AbstractGraph) = g1.graph == g2
Base.:(==)(g1::TestGraph, g2::TestGraph) = g1.graph == g2.graph

Graphs.edgetype(::Type{<:TestGraph{V}}) where {V} = edgetype(NamedGraph{V})
Expand Down Expand Up @@ -181,7 +180,7 @@ end

@test similar_graph(g) isa NamedGraph
@test similar_graph(g) == ug
@test !(similar_graph(g) === ug)
@test similar_graph(g) !== ug

@test similar_graph(typeof(g)) isa typeof(g)
@test similar_graph(typeof(g)) == typeof(g)()
Expand All @@ -193,11 +192,6 @@ end
@test isempty(edges(similar_graph(g, vertices(g))))
@test isempty(edges(similar_graph(typeof(g), vertices(g))))

@test similar_graph(g, vertices(g), edges(g)) == ug
@test !(similar_graph(g, vertices(g), edges(g)) === ug)
@test similar_graph(typeof(g), vertices(g), edges(g)) == g
@test !(similar_graph(typeof(g), vertices(g), edges(g)) === g)

@test nv(empty_graph(ug)) == 0
@test ne(empty_graph(ug)) == 0

Expand Down
Loading