diff --git a/Project.toml b/Project.toml index 7def7bf..7cf3472 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "NamedGraphs" uuid = "678767b0-92e7-4007-89e4-4527a8725b19" authors = ["Matthew Fishman , Joseph Tindall and contributors"] -version = "0.8.2" +version = "0.8.3" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" diff --git a/examples/Project.toml b/examples/Project.toml index 7b23abb..3553a9f 100644 --- a/examples/Project.toml +++ b/examples/Project.toml @@ -4,4 +4,4 @@ NamedGraphs = "678767b0-92e7-4007-89e4-4527a8725b19" [compat] Graphs = "1.12.0" -NamedGraphs = "0.8.1" +NamedGraphs = "0.8.3" diff --git a/src/NamedGraphs.jl b/src/NamedGraphs.jl index a7b0b74..c3021b1 100644 --- a/src/NamedGraphs.jl +++ b/src/NamedGraphs.jl @@ -9,6 +9,7 @@ include("utils.jl") include("abstractnamededge.jl") include("namededge.jl") include("abstractnamedgraph.jl") +include("abstractgraphindices.jl") include("decorate.jl") include("simplecycles.jl") include("shortestpaths.jl") diff --git a/src/abstractgraphindices.jl b/src/abstractgraphindices.jl new file mode 100644 index 0000000..e3eddc6 --- /dev/null +++ b/src/abstractgraphindices.jl @@ -0,0 +1,32 @@ +using Graphs: AbstractEdge + +abstract type AbstractGraphIndices{T} end +abstract type AbstractVertices{V} <: AbstractGraphIndices{V} end +abstract type AbstractEdges{V, E <: AbstractEdge{V}} <: AbstractGraphIndices{E} end + +struct Vertices{V, Vs} <: AbstractVertices{V} + vertices::Vs + Vertices(vertices::Vs) where {Vs} = new{eltype(Vs), Vs}(vertices) +end +struct Edges{V, E <: AbstractEdge{V}, Es} <: AbstractEdges{V, E} + edges::Es + function Edges(edges::Es) where {Es} + E = eltype(Es) + return new{vertextype{E}, E, Es}(edges) + end +end + +parent_graph_indices(vs::AbstractVertices) = vs.vertices +parent_graph_indices(es::AbstractEdges) = es.edges + +# Interface +Base.eltype(::Type{<:AbstractGraphIndices{T}}) where {T} = T + +Base.iterate(gi::AbstractGraphIndices) = iterate(parent_graph_indices(gi)) +Base.iterate(gi::AbstractGraphIndices, state) = iterate(parent_graph_indices(gi), state) +Base.length(gi::AbstractGraphIndices) = length(parent_graph_indices(gi)) + +Base.getindex(gi::AbstractGraphIndices, ind) = parent_graph_indices(gi)[ind] + +to_graph_indices(graph, indices) = indices +to_graph_indices(graph, indices::Pair) = edgetype(graph)(indices) diff --git a/src/abstractnamedgraph.jl b/src/abstractnamedgraph.jl index 6c40eb3..43694e4 100644 --- a/src/abstractnamedgraph.jl +++ b/src/abstractnamedgraph.jl @@ -2,6 +2,7 @@ using Dictionaries: set! using Graphs: Graphs, AbstractGraph, + AbstractSimpleGraph, IsDirected, a_star, add_edge!, @@ -34,7 +35,8 @@ using .GraphsExtensions: incident_edges, partition_vertices, rename_vertices, - subgraph + subgraph, + similar_graph using SimpleTraits: SimpleTraits, Not, @traitfn abstract type AbstractNamedGraph{V} <: AbstractGraph{V} end @@ -45,6 +47,7 @@ abstract type AbstractNamedGraph{V} <: AbstractGraph{V} end Graphs.vertices(graph::AbstractNamedGraph) = not_implemented() position_graph(graph::AbstractNamedGraph) = not_implemented() +position_graph(graph::AbstractSimpleGraph) = graph Graphs.rem_vertex!(graph::AbstractNamedGraph, vertex) = not_implemented() Graphs.add_vertex!(graph::AbstractNamedGraph, vertex) = not_implemented() @@ -61,10 +64,12 @@ end # graph `position_graph(graph::AbstractNamedGraph)`. # Inverse map of `ordered_vertices`. vertex_positions(graph::AbstractNamedGraph) = not_implemented() +vertex_positions(graph::AbstractSimpleGraph) = vertices(graph) # Outputs an object that when indexed by a vertex position # returns the corresponding vertex. ordered_vertices(graph::AbstractNamedGraph) = not_implemented() +ordered_vertices(graph::AbstractSimpleGraph) = vertices(graph) Graphs.edgetype(graph::AbstractNamedGraph) = edgetype(typeof(graph)) Graphs.edgetype(::Type{<:AbstractNamedGraph}) = not_implemented() @@ -102,6 +107,7 @@ end # position_graph_type(graph::AbstractNamedGraph) = typeof(position_graph(graph)) +position_graph_type(T::Type{<:AbstractNamedGraph}) = Base.promote_op(position_graph, T) function Graphs.has_vertex(graph::AbstractNamedGraph, vertex) # TODO: `vertices` should have fast lookup! @@ -112,16 +118,6 @@ Graphs.SimpleDiGraph(graph::AbstractNamedGraph) = SimpleDiGraph(position_graph(g Base.zero(G::Type{<:AbstractNamedGraph}) = G() -# TODO: Implement using `copyto!`? -function GraphsExtensions.directed_graph(graph::AbstractNamedGraph) - digraph = directed_graph_type(typeof(graph))(vertices(graph)) - for e in edges(graph) - add_edge!(digraph, e) - add_edge!(digraph, reverse(e)) - end - return digraph -end - # Default, can overload Base.eltype(graph::AbstractNamedGraph) = eltype(vertices(graph)) @@ -436,11 +432,20 @@ 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 not_implemented() + return similar_graph(graph, vertices, map(reverse, collect(edges(graph)))) end +# This wont be the most efficient way for a given graph type. @traitfn function Base.reverse!(g::AbstractNamedGraph::IsDirected) - return not_implemented() + + edge_list = collect(edges(g)) + + for edge in edge_list + rem_edge!(g, edge) + add_edge!(g, reverse(edge)) + end + + return g end # TODO: Move to `namedgraph.jl`, or make the output generic? @@ -493,9 +498,7 @@ end # traversal algorithms. function Graphs.tree(graph::AbstractNamedGraph, parents) n = length(parents) - # TODO: Use `directed_graph` here to make more generic? - ## t = GenericNamedGraph(DiGraph(n), vertices(graph)) - t = directed_graph_type(typeof(graph))(vertices(graph)) + t = similar_graph(directed_graph_type(typeof(graph)), vertices(graph)) for destination in eachindex(parents) source = parents[destination] if source != destination diff --git a/src/lib/GraphsExtensions/src/abstractgraph.jl b/src/lib/GraphsExtensions/src/abstractgraph.jl index fae210b..0934b29 100644 --- a/src/lib/GraphsExtensions/src/abstractgraph.jl +++ b/src/lib/GraphsExtensions/src/abstractgraph.jl @@ -47,18 +47,34 @@ undirected_graph_type(g::AbstractGraph) = undirected_graph_type(typeof(g)) @traitfn directed_graph(graph::::IsDirected) = graph -convert_vertextype(::Type{V}, graph::AbstractGraph{V}) where {V} = graph -function convert_vertextype(V::Type, graph::AbstractGraph) - return not_implemented() +convert_vertextype(::Type{V}, G::AbstractGraph{V}) where {V} = G +convert_vertextype(::Type, ::AbstractGraph) = not_implemented() + +convert_vertextype(::Type{V}, G::Type{<:AbstractGraph{V}}) where {V} = G +convert_vertextype(::Type, ::Type{<:AbstractGraph}) = not_implemented() + +similar_graph(graph::AbstractGraph) = similar_graph(typeof(graph)) +similar_graph(T::Type{<:AbstractGraph}) = T() + +function similar_graph(graph_or_type, vertices) + new_graph = similar_graph(graph_or_type) + add_vertices!(new_graph, vertices) + return new_graph +end +function similar_graph(graph_or_type, vertices, edges) + new_graph = similar_graph(graph_or_type, vertices) + add_edges!(new_graph, edges) + return new_graph end -function graph_from_vertices(graph_type::Type{<:AbstractGraph}, vertices) - return graph_type(vertices) +function similar_graph(graph::AbstractGraph, vertex_type::Type) + new_graph = convert_vertextype(vertex_type, similar_graph(typeof(graph))) + return new_graph end # TODO: Handle metadata in a generic way @traitfn function directed_graph(graph::::(!IsDirected)) - digraph = graph_from_vertices(directed_graph_type(graph), vertices(graph)) + digraph = similar_graph(directed_graph_type(graph), vertices(graph)) for e in edges(graph) add_edge!(digraph, e) add_edge!(digraph, reverse(e)) @@ -74,7 +90,7 @@ end # to avoid method overwrite warnings, see: # https://github.com/mauro3/SimpleTraits.jl#method-overwritten-warnings @traitfn function undirected_graph(graph::::IsDirected) - undigraph = graph_from_vertices(undirected_graph_type(typeof(graph)), vertices(graph)) + undigraph = similar_graph(undirected_graph_type(typeof(graph)), vertices(graph)) for e in edges(graph) # TODO: Check for repeated edges? add_edge!(undigraph, e) diff --git a/src/lib/GraphsExtensions/src/simplegraph.jl b/src/lib/GraphsExtensions/src/simplegraph.jl index 568ac14..f5de819 100644 --- a/src/lib/GraphsExtensions/src/simplegraph.jl +++ b/src/lib/GraphsExtensions/src/simplegraph.jl @@ -5,8 +5,11 @@ function permute_vertices(graph::AbstractSimpleGraph, permutation) end # https://github.com/JuliaGraphs/Graphs.jl/issues/365 -function graph_from_vertices(graph_type::Type{<:AbstractSimpleGraph}, vertices) - @assert vertices == Base.OneTo(length(vertices)) +similar_graph(graph_type::Type{<:AbstractSimpleGraph}) = graph_type() +function similar_graph(graph::AbstractSimpleGraph, vertices::Base.OneTo) + return similar_graph(typeof(graph), vertices) +end +function similar_graph(graph_type::Type{<:AbstractSimpleGraph}, vertices::Base.OneTo) return graph_type(length(vertices)) end diff --git a/src/lib/GraphsExtensions/test/runtests.jl b/src/lib/GraphsExtensions/test/runtests.jl index 3729ba5..4d54c4b 100644 --- a/src/lib/GraphsExtensions/test/runtests.jl +++ b/src/lib/GraphsExtensions/test/runtests.jl @@ -11,6 +11,7 @@ using AbstractTrees: rootindex using Dictionaries: Dictionary, Indices using Graphs: + AbstractGraph, add_edge!, add_vertex!, dst, @@ -44,6 +45,7 @@ using NamedGraphs.GraphsExtensions: add_edge, add_edges, add_edges!, + add_vertices!, all_edges, arrange_edge, arranged_edges, @@ -83,6 +85,7 @@ using NamedGraphs.GraphsExtensions: rem_edges!, rename_vertices, root_vertex, + similar_graph, subgraph, tree_graph_node, undirected_graph, @@ -108,6 +111,7 @@ using Test: @test, @test_broken, @test_throws, @testset # - random_bfs_tree @testset "NamedGraphs.GraphsExtensions" begin + # has_vertices g = path_graph(4) @test has_vertices(g, 1:3) @@ -552,6 +556,23 @@ using Test: @test, @test_broken, @test_throws, @testset @test_throws ErrorException root_vertex(g) @test_throws MethodError root_vertex(binary_tree(3)) + # similar_graph + g = path_graph(4) + @test similar_graph(g) isa typeof(g) + @test similar_graph(typeof(g)) isa typeof(g) + @test similar_graph(g, vertices(g), edges(g)) == g + @test !(similar_graph(g, vertices(g), edges(g)) === g) + sg = similar_graph(g, vertices(g)) + @test vertices(sg) == vertices(g) + @test isempty(edges(sg)) + + struct Graph <: AbstractGraph{Int} + field + end + + g = Graph(1) + @test_throws MethodError similar_graph(g) + # add_edge g = SimpleGraph(4) add_edge!(g, 1 => 2) diff --git a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl index 9d1fa51..1a2c1b1 100644 --- a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl @@ -2,6 +2,7 @@ using Dictionaries: Dictionary using Graphs: AbstractEdge, AbstractGraph, + AbstractSimpleGraph, Graphs, add_vertex!, dst, @@ -11,23 +12,40 @@ using Graphs: rem_vertex!, src, vertices -using ..NamedGraphs: NamedGraphs, AbstractNamedGraph, NamedGraph +using ..NamedGraphs: NamedGraphs, AbstractNamedGraph, NamedGraph, NamedDiGraph using ..NamedGraphs.GraphsExtensions: - GraphsExtensions, add_vertices!, not_implemented, rem_vertices!, subgraph, vertextype + GraphsExtensions, + add_vertices!, + not_implemented, + rem_vertices!, + subgraph, + convert_vertextype # For you own graph type `g`, you should define a method for this function if you # desire custom partitioning. partitioned_vertices(g::AbstractGraph) = [vertices(g)] +#TODO: Write this in terms of traits +function similar_quotient_graph(g::AbstractGraph) + if is_directed(g) + sg = NamedDiGraph() + else + sg = NamedGraph() + end + return convert_vertextype(keytype(partitioned_vertices(g)), sg) +end + # For fast quotient edge checking and graph construction, one should overload this function. function quotient_graph(g::AbstractGraph) - qg = NamedGraph(keys(partitioned_vertices(g))) + qg = similar_quotient_graph(g) + + add_vertices!(qg, keys(partitioned_vertices(g))) for e in edges(g) qv_src = parent(quotientvertex(g, src(e))) qv_dst = parent(quotientvertex(g, dst(e))) - qe = qv_src => qv_dst + qe = edgetype(qg)(qv_src => qv_dst) if qv_src != qv_dst && !has_edge(qg, qe) add_edge!(qg, qe) end @@ -117,12 +135,6 @@ function unpartitioned_graph_type(pg::AbstractPartitionedGraph) return typeof(unpartitioned_graph(pg)) end - -# AbstractGraph interface. -function Graphs.is_directed(graph_type::Type{<:AbstractPartitionedGraph}) - return is_directed(unpartitioned_graph_type(graph_type)) -end - #Functions for the abstract type Graphs.vertices(pg::AbstractPartitionedGraph) = vertices(unpartitioned_graph(pg)) Graphs.edges(pg::AbstractPartitionedGraph) = edges(unpartitioned_graph(pg)) diff --git a/src/lib/PartitionedGraphs/src/partitionedgraph.jl b/src/lib/PartitionedGraphs/src/partitionedgraph.jl index 1ef0f55..2cd10ad 100644 --- a/src/lib/PartitionedGraphs/src/partitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/partitionedgraph.jl @@ -1,15 +1,33 @@ using Dictionaries: Dictionary using Graphs: - AbstractEdge, AbstractGraph, add_edge!, edges, has_edge, induced_subgraph, vertices, dst, src, edgetype + AbstractEdge, + AbstractGraph, + add_edge!, + edges, + has_edge, + induced_subgraph, + vertices, + dst, + src, + edgetype using ..NamedGraphs: NamedGraphs, NamedEdge, NamedGraph -using ..NamedGraphs.GraphsExtensions: GraphsExtensions, boundary_edges, is_self_loop, partition_vertices +using ..NamedGraphs.GraphsExtensions: + GraphsExtensions, + boundary_edges, + is_self_loop, + partition_vertices, + directed_graph_type, + undirected_graph_type, + vertextype using ..NamedGraphs.OrderedDictionaries: OrderedDictionary # TODO: Parametrize `partitioned_vertices` and `which_partition`, # see https://github.com/mtfishman/NamedGraphs.jl/issues/63. -struct PartitionedGraph{V, PV, G <: AbstractGraph{V}, P <: Dictionary} <: AbstractPartitionedGraph{V, PV} +struct PartitionedGraph{ + V, PV, G <: AbstractGraph{V}, QG <: AbstractGraph{PV}, P <: Dictionary, + } <: AbstractPartitionedGraph{V, PV} graph::G - quotient_graph::NamedGraph{PV} + quotient_graph::QG partitioned_vertices::P which_partition::Dictionary{V, PV} end @@ -176,3 +194,25 @@ function NamedGraphs.induced_subgraph_from_vertices( sg, vs = NamedGraphs.induced_subgraph_from_vertices(pg, subvertices.vertices) return unpartitioned_graph(sg), vs end + + +function GraphsExtensions.undirected_graph(g::PartitionedGraph) + dg = GraphsExtensions.undirected_graph(unpartitioned_graph(g)) + return PartitionedGraph(dg, partitioned_vertices(g)) +end +function GraphsExtensions.directed_graph(g::PartitionedGraph) + dg = GraphsExtensions.directed_graph(unpartitioned_graph(g)) + return PartitionedGraph(dg, partitioned_vertices(g)) +end +function GraphsExtensions.undirected_graph_type(type::Type{<:PartitionedGraph{V, PV}}) where {V, PV} + UG = undirected_graph_type(unpartitioned_graph_type(type)) + QG = undirected_graph_type(quotient_graph_type(type)) + P = fieldtype(type, :partitioned_vertices) + return PartitionedGraph{V, PV, UG, QG, P} +end +function GraphsExtensions.directed_graph_type(type::Type{<:PartitionedGraph{V, PV}}) where {V, PV} + DG = directed_graph_type(unpartitioned_graph_type(type)) + QG = directed_graph_type(quotient_graph_type(type)) + P = fieldtype(type, :partitioned_vertices) + return PartitionedGraph{V, PV, DG, QG, P} +end diff --git a/src/lib/PartitionedGraphs/src/partitionedview.jl b/src/lib/PartitionedGraphs/src/partitionedview.jl index 422f585..35c9382 100644 --- a/src/lib/PartitionedGraphs/src/partitionedview.jl +++ b/src/lib/PartitionedGraphs/src/partitionedview.jl @@ -9,3 +9,7 @@ end unpartitioned_graph(pv::PartitionedView) = getfield(pv, :graph) partitioned_vertices(pv::PartitionedView) = getfield(pv, :partitioned_vertices) + +function unpartitioned_graph_type(graph_type::Type{<:PartitionedView}) + return fieldtype(graph_type, :graph) +end diff --git a/src/lib/PartitionedGraphs/src/quotientedge.jl b/src/lib/PartitionedGraphs/src/quotientedge.jl index 42a1673..ef71d02 100644 --- a/src/lib/PartitionedGraphs/src/quotientedge.jl +++ b/src/lib/PartitionedGraphs/src/quotientedge.jl @@ -1,5 +1,5 @@ using Graphs: AbstractGraph, Graphs, AbstractEdge, dst, src, ne, has_edge -using ..NamedGraphs: AbstractNamedEdge +using ..NamedGraphs: NamedGraphs, AbstractNamedGraph, AbstractNamedEdge, AbstractEdges, Edges using ..NamedGraphs.GraphsExtensions: GraphsExtensions, not_implemented, rem_edges!, rem_edge """ @@ -100,3 +100,17 @@ function GraphsExtensions.rem_edges!(g::AbstractGraph, sv::QuotientEdge) end rem_quotientedge!(g::AbstractGraph, sv::QuotientEdge) = rem_edges!(g, sv) + +struct QuotientEdgeEdges{V, E, QE, Es} <: AbstractEdges{V, E} + quotientedge::QuotientEdge{QE} + edges::Edges{V, E, Es} +end + +quotient_index(qes::QuotientEdgeEdges) = getfield(qes, :quotientedge) +departition(qes::QuotientEdgeEdges) = getfield(qes, :edges) + +NamedGraphs.parent_graph_indices(qes::QuotientEdgeEdges) = departition(qes) + +function NamedGraphs.to_graph_indices(g, qe::QuotientEdge) + return QuotientEdgeEdges(qe, Edges(collect(edges(g, qe)))) +end diff --git a/src/lib/PartitionedGraphs/src/quotientvertex.jl b/src/lib/PartitionedGraphs/src/quotientvertex.jl index 283b21d..330aa44 100644 --- a/src/lib/PartitionedGraphs/src/quotientvertex.jl +++ b/src/lib/PartitionedGraphs/src/quotientvertex.jl @@ -1,5 +1,5 @@ using Graphs: AbstractGraph, Graphs, nv, induced_subgraph -using ..NamedGraphs: NamedGraphs, AbstractNamedGraph +using ..NamedGraphs: NamedGraphs, AbstractNamedGraph, AbstractVertices, Vertices, Edges using ..NamedGraphs.GraphsExtensions: GraphsExtensions, rem_vertices!, subgraph using ..NamedGraphs.OrderedDictionaries: OrderedIndices @@ -87,3 +87,17 @@ function NamedGraphs.induced_subgraph_from_vertices( sg, vs = NamedGraphs.induced_subgraph_from_vertices(g, subvertices.vertices) return unpartitioned_graph(sg), vs end + +struct QuotientVertexVertices{V, QV, Vs} <: AbstractVertices{V} + quotientvertex::QuotientVertex{QV} + vertices::Vertices{V, Vs} +end + +quotient_index(qvs::QuotientVertexVertices) = getfield(qvs, :quotientvertex) +departition(qvs::QuotientVertexVertices) = getfield(qvs, :vertices) + +NamedGraphs.parent_graph_indices(qvs::QuotientVertexVertices) = departition(qvs) + +function NamedGraphs.to_graph_indices(g, qv::QuotientVertex) + return QuotientVertexVertices(qv, Vertices(collect(vertices(g, qv)))) +end diff --git a/src/lib/PartitionedGraphs/src/quotientview.jl b/src/lib/PartitionedGraphs/src/quotientview.jl index b8eb4e9..22aab93 100644 --- a/src/lib/PartitionedGraphs/src/quotientview.jl +++ b/src/lib/PartitionedGraphs/src/quotientview.jl @@ -1,6 +1,7 @@ using Graphs: AbstractGraph, rem_vertex!, rem_edge!, vertices, edges -using .GraphsExtensions: add_edges! using ..NamedGraphs: NamedGraph, position_graph_type +using .GraphsExtensions: directed_graph_type, undirected_graph_type +using ..SimilarType: similar_type struct QuotientView{V, G <: AbstractGraph} <: AbstractNamedGraph{V} graph::G @@ -11,21 +12,24 @@ Base.parent(qg::QuotientView) = qg.graph parent_graph_type(g::AbstractGraph) = parent_graph_type(typeof(g)) parent_graph_type(::Type{<:QuotientView{V, G}}) where {V, G} = G -function Base.copy(qv::QuotientView) - qg = quotient_graph_type(parent_graph_type(qv))(vertices(qv)) - add_edges!(qg, edges(qv)) - return qg -end +Base.copy(qv::QuotientView) = copy(quotient_graph(parent(qv))) NamedGraphs.edgetype(Q::Type{<:QuotientView}) = quotient_graph_edgetype(parent_graph_type(Q)) -Graphs.vertices(qg::QuotientView) = parent.(quotientvertices(parent(qg))) -Graphs.edges(qg::QuotientView) = parent.(quotientedges(parent(qg))) +Graphs.vertices(qg::QuotientView) = keys(partitioned_vertices(parent(qg))) +Graphs.edges(qg::QuotientView) = edges(quotient_graph(parent(qg))) function NamedGraphs.position_graph_type(type::Type{<:QuotientView}) return position_graph_type(quotient_graph_type(parent_graph_type(type))) end +function NamedGraphs.GraphsExtensions.directed_graph_type(type::Type{<:QuotientView}) + return directed_graph_type(quotient_graph_type(parent_graph_type(type))) +end +function NamedGraphs.GraphsExtensions.undirected_graph_type(type::Type{<:QuotientView}) + return undirected_graph_type(quotient_graph_type(parent_graph_type(type))) +end + function Graphs.rem_vertex!(qg::QuotientView, v) rem_quotientvertex!(parent(qg), QuotientVertex(v)) return qg @@ -36,9 +40,9 @@ function Graphs.rem_edge!(qg::QuotientView, v) end for f in [ + :(NamedGraphs.vertex_positions), :(NamedGraphs.induced_subgraph_from_vertices), :(NamedGraphs.ordered_vertices), - :(NamedGraphs.vertex_positions), :(NamedGraphs.position_graph), ] @eval begin @@ -47,3 +51,9 @@ for f in [ end end end + +function NamedGraphs.SimilarType.similar_type(type::Type{<:QuotientView}) + return similar_type(quotient_graph_type(parent_graph_type(type))) +end + +quotientview(g::AbstractGraph) = QuotientView(g) diff --git a/src/namedgraph.jl b/src/namedgraph.jl index 05d1a12..65ebf8d 100644 --- a/src/namedgraph.jl +++ b/src/namedgraph.jl @@ -171,10 +171,10 @@ end GenericNamedGraph() = GenericNamedGraph(Any[]) function GenericNamedGraph(graph::GenericNamedGraph) - return GenericNamedGraph{vertextype(graph), position_graph_type(graph_type)}(graph) + return GenericNamedGraph{vertextype(graph), position_graph_type(graph)}(graph) end function GenericNamedGraph{V}(graph::GenericNamedGraph) where {V} - return GenericNamedGraph{V, position_graph_type(graph_type)}(graph) + return GenericNamedGraph{V, position_graph_type(graph)}(graph) end function GenericNamedGraph{<:Any, G}( graph::GenericNamedGraph @@ -218,7 +218,7 @@ end # Assumes the subvertices were already processed by `to_vertices`. # TODO: Implement an edgelist version function induced_subgraph_from_vertices(graph::AbstractGraph, subvertices) - subgraph = typeof(graph)(subvertices) + subgraph = similar_graph(graph, subvertices) subvertices_set = Set(subvertices) for src in subvertices for dst in outneighbors(graph, src) @@ -240,6 +240,14 @@ function Graphs.induced_subgraph( return induced_subgraph_from_vertices(graph, to_vertices(graph, subvertices)) end +function Base.reverse!(graph::GenericNamedGraph) + reverse!(graph.position_graph) + return graph +end +function Base.reverse(graph::GenericNamedGraph) + return GenericNamedGraph(reverse(graph.position_graph), copy(graph.vertices)) +end + # # Type aliases # diff --git a/test/Project.toml b/test/Project.toml index bccfd82..025ec6c 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -24,7 +24,7 @@ Graphs = "1.12" GraphsFlows = "0.1.1" KaHyPar = "0.3.1" Metis = "1.5.0" -NamedGraphs = "0.8.1" +NamedGraphs = "0.8.3" SafeTestsets = "0.1.0" SimpleGraphAlgorithms = "0.6.0" SimpleGraphConverter = "0.1.0" diff --git a/test/test_namedgraph.jl b/test/test_namedgraph.jl index 5e9f46b..93bcdfd 100644 --- a/test/test_namedgraph.jl +++ b/test/test_namedgraph.jl @@ -297,6 +297,23 @@ end h = degree_histogram(g, indegree) @test h[0] == 2 @test h[1] == 2 + + rg = reverse(g) + + @test !has_edge(rg, "A" => "B") + @test !has_edge(rg, "B" => "C") + @test has_edge(rg, "B" => "A") + @test has_edge(rg, "C" => "B") + + rg = reverse!(copy(g)) + + @test !has_edge(rg, "A" => "B") + @test !has_edge(rg, "B" => "C") + @test has_edge(rg, "B" => "A") + @test has_edge(rg, "C" => "B") + + @test reverse(reverse(g)) == g + @test reverse!(reverse!(copy(g))) == g end @testset "BFS traversal" begin g = named_grid((3, 3)) diff --git a/test/test_partitionedgraph.jl b/test/test_partitionedgraph.jl index 4a0fd07..3a57289 100644 --- a/test/test_partitionedgraph.jl +++ b/test/test_partitionedgraph.jl @@ -255,17 +255,23 @@ Graphs.vertices(mg::MyUnpartitionedGraph) = vertices(mg.g) Graphs.edgetype(mg::MyUnpartitionedGraph) = edgetype(mg.g) Graphs.has_edge(mg::MyUnpartitionedGraph, e) = has_edge(mg.g, e) +Graphs.is_directed(mg::MyUnpartitionedGraph) = is_directed(mg.g) + struct MyGraph{V, P} <: AbstractGraph{V} g::NamedGraph{V} partitioned_vertices::P end + Graphs.edges(mg::MyGraph) = edges(mg.g) Graphs.vertices(mg::MyGraph) = vertices(mg.g) Graphs.edgetype(mg::MyGraph) = edgetype(mg.g) Graphs.has_edge(mg::MyGraph, e) = has_edge(mg.g, e) +Graphs.is_directed(mg::MyGraph) = is_directed(mg.g) +NamedGraphs.position_graph(mg::MyGraph) = NamedGraphs.position_graph(mg.g) + PartitionedGraphs.partitioned_vertices(mg::MyGraph) = mg.partitioned_vertices PartitionedGraphs.quotient_graph_type(::Type{<:MyGraph}) = NamedGraph{Int} @@ -287,6 +293,9 @@ end Graphs.edges(wg::WrapperGraph) = edges(wg.g) Graphs.vertices(wg::WrapperGraph) = vertices(wg.g) +Graphs.is_directed(wg::WrapperGraph) = is_directed(wg.g) +NamedGraphs.position_graph(wg::WrapperGraph) = NamedGraphs.position_graph(wg.g) + PartitionedGraphs.partitioned_vertices(wg::WrapperGraph) = partitioned_vertices(wg.g) @testset "Partitioning of non-partitioned graphs" begin