diff --git a/Project.toml b/Project.toml index 21035e4..3864a4c 100644 --- a/Project.toml +++ b/Project.toml @@ -1,24 +1,32 @@ name = "DataGraphs" uuid = "b5a273c3-7e6c-41f6-98bd-8d7f1525a36a" authors = ["Matthew Fishman and contributors"] -version = "0.1.13" +version = "0.2.0" [deps] Dictionaries = "85a47980-9c8c-11e8-2b9f-f7ca1fa99fb4" Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" -GraphsFlows = "06909019-6f44-4949-96fc-b9d9aaa02889" NamedGraphs = "678767b0-92e7-4007-89e4-4527a8725b19" +PackageExtensionCompat = "65ce6f38-6b18-4e1d-a461-8949797d7930" SimpleTraits = "699a6c99-e7fa-54fc-8d76-47d257e15c1d" +[weakdeps] +GraphsFlows = "06909019-6f44-4949-96fc-b9d9aaa02889" + +[extensions] +DataGraphsGraphsFlowsExt = "GraphsFlows" + [compat] Dictionaries = "0.4" Graphs = "1" GraphsFlows = "0.1.1" -NamedGraphs = "0.1.19" +NamedGraphs = "0.4" +PackageExtensionCompat = "1" SimpleTraits = "0.9" julia = "1.7" [extras] +GraphsFlows = "06909019-6f44-4949-96fc-b9d9aaa02889" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] diff --git a/examples/Project.toml b/examples/Project.toml new file mode 100644 index 0000000..6864eaa --- /dev/null +++ b/examples/Project.toml @@ -0,0 +1,4 @@ +[deps] +DataGraphs = "b5a273c3-7e6c-41f6-98bd-8d7f1525a36a" +Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" +NamedGraphs = "678767b0-92e7-4007-89e4-4527a8725b19" diff --git a/examples/datagraph.jl b/examples/datagraph.jl index 4dbd176..f46c305 100644 --- a/examples/datagraph.jl +++ b/examples/datagraph.jl @@ -1,9 +1,8 @@ -using DataGraphs -using Dictionaries -using Graphs +using DataGraphs: DataGraph +using Graphs: Edge, grid, has_edge, has_vertex g = grid((4,)) -dg = DataGraph(g, String, Symbol) +dg = DataGraph(g; vertex_data_eltype=String, edge_data_eltype=Symbol) @show !isassigned(dg, Edge(1, 2)) @show !isassigned(dg, 1 => 2) @show !isassigned(dg, Edge(1 => 2)) diff --git a/examples/disjoint_union.jl b/examples/disjoint_union.jl index 96af38e..f06ea4d 100644 --- a/examples/disjoint_union.jl +++ b/examples/disjoint_union.jl @@ -1,8 +1,9 @@ -using Graphs -using NamedGraphs -using DataGraphs +using Graphs: edges, has_edge, has_vertex, ne, nv, vertices +using NamedGraphs.GraphsExtensions: ⊔ +using NamedGraphs.NamedGraphGenerators: named_grid +using DataGraphs: DataGraph -g = DataGraph(named_grid((2, 2)), String, String) +g = DataGraph(named_grid((2, 2)); vertex_data_eltype=String, edge_data_eltype=String) for v in vertices(g) g[v] = "V$v" diff --git a/examples/multidimdatagraph_1d.jl b/examples/multidimdatagraph_1d.jl index 81ef283..2a06681 100644 --- a/examples/multidimdatagraph_1d.jl +++ b/examples/multidimdatagraph_1d.jl @@ -1,14 +1,16 @@ -using DataGraphs -using Graphs -using NamedGraphs +using DataGraphs: DataGraph +using Graphs: grid, has_edge, has_vertex +using NamedGraphs: NamedGraph, NamedEdge g = NamedGraph(grid((4,)), ["A", "B", "C", "D"]) -dg = DataGraph(g, String, Symbol) +dg = DataGraph(g; vertex_data_eltype=String, edge_data_eltype=Symbol) @show has_vertex(dg, "A") @show has_vertex(dg, "D") -@show !has_vertex(dg, 0) -@show !has_vertex(dg, 5) + +# Broken, see https://github.com/andyferris/Dictionaries.jl/issues/143. +# @show !has_vertex(dg, 0) +# @show !has_vertex(dg, 5) @show has_edge(dg, "A", "B") @show has_edge(dg, "A" => "B") diff --git a/examples/multidimdatagraph_2d.jl b/examples/multidimdatagraph_2d.jl index 33f8d82..92ff83e 100644 --- a/examples/multidimdatagraph_2d.jl +++ b/examples/multidimdatagraph_2d.jl @@ -1,10 +1,9 @@ -using DataGraphs -using NamedGraphs -using Dictionaries -using Graphs +using DataGraphs: DataGraph +using NamedGraphs: NamedEdge +using NamedGraphs.NamedGraphGenerators: named_grid g = named_grid((2, 2)) -dg = DataGraph(g, String, String) +dg = DataGraph(g; vertex_data_eltype=String, edge_data_eltype=String) dg[1, 1] = "X11" diff --git a/examples/slicing.jl b/examples/slicing.jl index 467acd4..85b0644 100644 --- a/examples/slicing.jl +++ b/examples/slicing.jl @@ -1,10 +1,10 @@ -using DataGraphs -using NamedGraphs -using Dictionaries -using Graphs +using DataGraphs: DataGraph +using NamedGraphs.GraphsExtensions: subgraph +using NamedGraphs.NamedGraphGenerators: named_grid +using Graphs: ne, nv g = named_grid((2, 2)) -dg = DataGraph(g, String, String) +dg = DataGraph(g; vertex_data_eltype=String, edge_data_eltype=String) dg[1, 1] = "V11" dg[1, 2] = "V12" diff --git a/ext/DataGraphsGraphsFlowsExt/DataGraphsGraphsFlowsExt.jl b/ext/DataGraphsGraphsFlowsExt/DataGraphsGraphsFlowsExt.jl new file mode 100644 index 0000000..e6bf45c --- /dev/null +++ b/ext/DataGraphsGraphsFlowsExt/DataGraphsGraphsFlowsExt.jl @@ -0,0 +1,8 @@ +module DataGraphsGraphsFlowsExt +using DataGraphs: AbstractDataGraph, underlying_graph +using GraphsFlows: GraphsFlows + +function GraphsFlows.mincut(graph::AbstractDataGraph, args...; kwargs...) + return GraphsFlows.mincut(underlying_graph(graph), args...; kwargs...) +end +end diff --git a/ext/DataGraphsNamedGraphsExt/DataGraphsNamedGraphsExt.jl b/ext/DataGraphsNamedGraphsExt/DataGraphsNamedGraphsExt.jl new file mode 100644 index 0000000..136db5e --- /dev/null +++ b/ext/DataGraphsNamedGraphsExt/DataGraphsNamedGraphsExt.jl @@ -0,0 +1,14 @@ +module DataGraphsNamedGraphsExt +using DataGraphs: DataGraphs, AbstractDataGraph, underlying_graph +using NamedGraphs: NamedGraphs, AbstractNamedGraph + +DataGraphs.is_underlying_graph(::Type{<:AbstractNamedGraph}) = true + +for f in [:(NamedGraphs.parent_graph), :(NamedGraphs.parent_vertices_to_vertices)] + @eval begin + function $f(graph::AbstractDataGraph, args...; kwargs...) + return $f(underlying_graph(graph), args...; kwargs...) + end + end +end +end diff --git a/src/DataGraphs.jl b/src/DataGraphs.jl index 517a143..c603b91 100644 --- a/src/DataGraphs.jl +++ b/src/DataGraphs.jl @@ -1,132 +1,17 @@ module DataGraphs -using Dictionaries -using Graphs -using Graphs.SimpleGraphs # AbstractSimpleGraph -using GraphsFlows -using NamedGraphs -using SimpleTraits - -using NamedGraphs: AbstractNamedGraph, all_edges - -# -# imports -# - -import Base: - get, - getindex, - setindex!, - convert, - show, - isassigned, - eltype, - copy, - hvncat, - hcat, - vcat, - union, - zero - -import Graphs: - a_star, - adjacency_matrix, - add_edge!, - add_vertex!, - bellman_ford_shortest_paths, - bfs_parents, - bfs_tree, - boruvka_mst, - center, - common_neighbors, - connected_components, - connected_components!, - degree, - degree_histogram, - desopo_pape_shortest_paths, - dfs_parents, - dfs_tree, - diameter, - dijkstra_shortest_paths, - eccentricity, - edges, - edgetype, - enumerate_paths, - floyd_warshall_shortest_paths, - has_edge, - has_path, - has_vertex, - induced_subgraph, - inneighbors, - is_connected, - is_cyclic, - is_directed, - is_strongly_connected, - is_weakly_connected, - johnson_shortest_paths, - kruskal_mst, - merge_vertices, - merge_vertices!, - mincut, - ne, - neighbors, - neighborhood, - neighborhood_dists, - nv, - outneighbors, - periphery, - prim_mst, - radius, - rem_edge!, - rem_vertex!, - reverse, - spfa_shortest_paths, - steiner_tree, - topological_sort_by_dfs, - tree, - vertices, - yen_k_shortest_paths - -# TODO: Can we remove the dependency on `NamedGraphs`? -# Maybe need a `GraphExtensions.jl` or -# `GraphInterfaces.jl` package. -import NamedGraphs: - ⊔, - boundary_edges, - boundary_vertices, - convert_vertextype, - directed_graph, - disjoint_union, - eccentricities, - incident_edges, - inner_boundary_vertices, - outer_boundary_vertices, - mincut_partitions, - rename_vertices, - symrcm, - symrcm_permute, - vertextype, - parent_graph, - parent_vertices_to_vertices - -# General functions -not_implemented() = error("Not implemented") - -include(joinpath("traits", "isunderlyinggraph.jl")) +include("utils.jl") +include("traits/isunderlyinggraph.jl") include("abstractdatagraph.jl") include("arrange.jl") include("datagraph.jl") +# TODO: Turn into a weak dependency once `GraphsExtensions` +# is split off from `NamedGraphs`. +include("../ext/DataGraphsNamedGraphsExt/DataGraphsNamedGraphsExt.jl") -# -# exports -# - -export DataGraph, - AbstractDataGraph, - directed_graph, - disjoint_union, - map_vertex_data, - map_edge_data, - map_data, - ⊔ +export AbstractDataGraph, DataGraph -end # module DataGraphs +using PackageExtensionCompat: @require_extensions +function __init__() + @require_extensions +end +end diff --git a/src/abstractdatagraph.jl b/src/abstractdatagraph.jl index ef80635..389fa95 100644 --- a/src/abstractdatagraph.jl +++ b/src/abstractdatagraph.jl @@ -1,88 +1,99 @@ +using Dictionaries: set!, unset! +using Graphs: + Graphs, AbstractEdge, AbstractGraph, IsDirected, add_edge!, edges, ne, nv, vertices +using NamedGraphs.GraphsExtensions: GraphsExtensions, incident_edges, vertextype +using SimpleTraits: SimpleTraits, @traitfn + abstract type AbstractDataGraph{V,VD,ED} <: AbstractGraph{V} end # Minimal interface underlying_graph(::AbstractDataGraph) = not_implemented() underlying_graph_type(::Type{<:AbstractDataGraph}) = not_implemented() vertex_data(::AbstractDataGraph) = not_implemented() -vertex_data_type(::Type{<:AbstractDataGraph}) = not_implemented() +vertex_data_eltype(::Type{<:AbstractDataGraph}) = not_implemented() edge_data(::AbstractDataGraph) = not_implemented() -edge_data_type(::Type{<:AbstractDataGraph}) = not_implemented() +edge_data_eltype(::Type{<:AbstractDataGraph}) = not_implemented() # Derived interface -edgetype(G::Type{<:AbstractDataGraph}) = edgetype(underlying_graph_type(G)) -is_directed(G::Type{<:AbstractDataGraph}) = is_directed(underlying_graph_type(G)) +function Graphs.edgetype(graph_type::Type{<:AbstractDataGraph}) + return Graphs.edgetype(underlying_graph_type(graph_type)) +end +function Graphs.is_directed(graph_type::Type{<:AbstractDataGraph}) + return Graphs.is_directed(underlying_graph_type(graph_type)) +end underlying_graph_type(graph::AbstractDataGraph) = typeof(underlying_graph(graph)) -vertex_data_type(graph::AbstractDataGraph) = vertex_data_type(typeof(graph)) -edge_data_type(graph::AbstractDataGraph) = edge_data_type(typeof(graph)) +vertex_data_eltype(graph::AbstractDataGraph) = eltype(vertex_data(graph)) +edge_data_eltype(graph::AbstractDataGraph) = eltype(edge_data(graph)) # TODO: delete, defined for AbstractGraph{V}? -vertextype(G::Type{<:AbstractDataGraph}) = vertextype(underlying_graph_type(G)) -vertextype(graph::AbstractDataGraph) = vertextype(typeof(graph)) +function GraphsExtensions.vertextype(graph_type::Type{<:AbstractDataGraph}) + return vertextype(underlying_graph_type(graph_type)) +end +GraphsExtensions.vertextype(graph::AbstractDataGraph) = vertextype(typeof(graph)) -zero(G::Type{<:AbstractDataGraph}) = G() +Base.zero(graph_type::Type{<:AbstractDataGraph}) = graph_type() # Graphs overloads for f in [ - :a_star, - :add_edge!, - :add_vertex!, - :adjacency_matrix, - :bellman_ford_shortest_paths, - :bfs_parents, - :bfs_tree, - :boundary_edges, - :boundary_vertices, - :boruvka_mst, - :center, - :common_neighbors, - :connected_components, - :degree, - :degree_histogram, - :desopo_pape_shortest_paths, - :dfs_parents, - :dfs_tree, - :diameter, - :dijkstra_shortest_paths, - :eccentricity, - :eccentricities, - :edges, - :edgetype, - :eltype, - :enumerate_paths, - :floyd_warshall_shortest_paths, - :has_edge, - :has_path, - :has_vertex, - :inneighbors, - :inner_boundary_vertices, - :is_connected, - :is_cyclic, - :is_directed, - :is_strongly_connected, - :is_weakly_connected, - :mincut, - :(GraphsFlows.mincut), - :mincut_partitions, - :ne, - :neighbors, - :neighborhood, - :neighborhood_dists, - :outer_boundary_vertices, - :johnson_shortest_paths, - :spfa_shortest_paths, - :yen_k_shortest_paths, - :kruskal_mst, - :prim_mst, - :nv, - :outneighbors, - :periphery, - :radius, - :symrcm, - :symrcm_permute, - :steiner_tree, - :topological_sort_by_dfs, - :tree, - :vertices, + :(Graphs.a_star), + :(Graphs.add_edge!), + :(Graphs.add_vertex!), + :(Graphs.adjacency_matrix), + :(Graphs.bellman_ford_shortest_paths), + :(Graphs.bfs_parents), + :(Graphs.bfs_tree), + :(Graphs.boruvka_mst), + :(Graphs.center), + :(Graphs.common_neighbors), + :(Graphs.connected_components), + :(Graphs.degree), + :(Graphs.degree_histogram), + :(Graphs.desopo_pape_shortest_paths), + :(Graphs.dfs_parents), + :(Graphs.dfs_tree), + :(Graphs.diameter), + :(Graphs.dijkstra_shortest_paths), + :(Graphs.eccentricity), + :(Graphs.edges), + :(Graphs.edgetype), + :(Graphs.eltype), + :(Graphs.enumerate_paths), + :(Graphs.floyd_warshall_shortest_paths), + :(Graphs.has_edge), + :(Graphs.has_path), + :(Graphs.has_vertex), + :(Graphs.inneighbors), + :(Graphs.is_connected), + :(Graphs.is_cyclic), + :(Graphs.is_directed), + :(Graphs.is_strongly_connected), + :(Graphs.is_weakly_connected), + :(Graphs.mincut), + :(Graphs.ne), + :(Graphs.neighbors), + :(Graphs.neighborhood), + :(Graphs.neighborhood_dists), + :(Graphs.johnson_shortest_paths), + :(Graphs.spfa_shortest_paths), + :(Graphs.yen_k_shortest_paths), + :(Graphs.kruskal_mst), + :(Graphs.prim_mst), + :(Graphs.nv), + :(Graphs.outneighbors), + :(Graphs.periphery), + :(Graphs.radius), + :(Graphs.steiner_tree), + :(Graphs.topological_sort_by_dfs), + :(Graphs.tree), + :(Graphs.vertices), + :(GraphsExtensions.boundary_edges), + :(GraphsExtensions.boundary_vertices), + :(GraphsExtensions.eccentricities), + :(GraphsExtensions.inner_boundary_vertices), + :(GraphsExtensions.mincut_partitions), + :(GraphsExtensions.outer_boundary_vertices), + :(GraphsExtensions.symrcm_perm), + :(GraphsExtensions.symrcm_permute), ] @eval begin function $f(graph::AbstractDataGraph, args...; kwargs...) @@ -91,28 +102,21 @@ for f in [ end end -# NamedGraphs overloads -for f in [:parent_graph, :parent_vertices_to_vertices] - @eval begin - function $f(graph::AbstractDataGraph, args...; kwargs...) - return $f(underlying_graph(graph), args...; kwargs...) - end - end -end - # Fix for ambiguity error with `AbstractGraph` version -function degree(graph::AbstractDataGraph, vertex::Integer) - return degree(underlying_graph(graph), vertex) +function Graphs.degree(graph::AbstractDataGraph, vertex::Integer) + return Graphs.degree(underlying_graph(graph), vertex) end # Fix for ambiguity error with `AbstractGraph` version -function dijkstra_shortest_paths(graph::AbstractDataGraph, vertices::Vector{<:Integer}) - return dijkstra_shortest_paths(underlying_graph(graph), vertices) +function Graphs.dijkstra_shortest_paths( + graph::AbstractDataGraph, vertices::Vector{<:Integer} +) + return Graphs.dijkstra_shortest_paths(underlying_graph(graph), vertices) end # Fix for ambiguity error with `AbstractGraph` version -function eccentricity(graph::AbstractDataGraph, distmx::AbstractMatrix) - return eccentricity(underlying_graph(graph), distmx) +function Graphs.eccentricity(graph::AbstractDataGraph, distmx::AbstractMatrix) + return Graphs.eccentricity(underlying_graph(graph), distmx) end # Fix for ambiguity error with `AbstractGraph` version @@ -125,9 +129,9 @@ function outdegree(graph::AbstractDataGraph, vertex::Integer) return outdegree(underlying_graph(graph), vertex) end -@traitfn directed_graph(graph::AbstractDataGraph::IsDirected) = graph +@traitfn GraphsExtensions.directed_graph(graph::AbstractDataGraph::IsDirected) = graph -@traitfn function directed_graph(graph::AbstractDataGraph::(!IsDirected)) +@traitfn function GraphsExtensions.directed_graph(graph::AbstractDataGraph::(!IsDirected)) digraph = directed_graph(typeof(graph))(directed_graph(underlying_graph(graph))) for v in vertices(graph) # TODO: Only loop over `keys(vertex_data(graph))` @@ -148,7 +152,7 @@ end return digraph end -function reverse(graph::AbstractDataGraph) +function Base.reverse(graph::AbstractDataGraph) reversed_graph = typeof(graph)(reverse(underlying_graph(graph))) for v in vertices(graph) if isassigned(graph, v) @@ -163,7 +167,7 @@ function reverse(graph::AbstractDataGraph) return reversed_graph end -function merge_vertices( +function Graphs.merge_vertices( graph::AbstractDataGraph, merge_vertices; merge_data=(x, y) -> y, @@ -171,11 +175,11 @@ function merge_vertices( merge_edge_data=merge_data, kwargs..., ) - underlying_merged_graph = merge_vertices(underlying_graph(graph); kwargs...) + underlying_merged_graph = Graphs.merge_vertices(underlying_graph(graph); kwargs...) return not_implemented() end -function merge_vertices!( +function Graphs.merge_vertices!( graph::AbstractDataGraph, merge_vertices; merge_data=(x, y) -> y, @@ -184,13 +188,13 @@ function merge_vertices!( kwargs..., ) underlying_merged_graph = copy(underlying_graph(graph)) - merge_vertices!(underlying_merged_graph; kwargs...) + Graphs.merge_vertices!(underlying_merged_graph; kwargs...) return not_implemented() end # Union the vertices and edges of the graphs and # merge the vertex and edge metadata. -function union( +function Base.union( graph1::AbstractDataGraph, graph2::AbstractDataGraph; merge_data=(x, y) -> y, @@ -201,10 +205,10 @@ function union( vertex_data_merge = mergewith(merge_vertex_data, vertex_data(graph1), vertex_data(graph2)) edge_data_merge = mergewith(merge_edge_data, edge_data(graph1), edge_data(graph2)) # TODO: Convert to `promote_type(typeof(graph1), typeof(graph2))` - return DataGraph(underlying_graph_union, vertex_data_merge, edge_data_merge) + return _DataGraph(underlying_graph_union, vertex_data_merge, edge_data_merge) end -function union( +function Base.union( graph1::AbstractDataGraph, graph2::AbstractDataGraph, graph3::AbstractDataGraph, @@ -214,60 +218,62 @@ function union( return union(union(graph1, graph2; kwargs...), graph3, graphs_tail...; kwargs...) end -function rename_vertices(f::Function, graph::AbstractDataGraph) - renamed_underlying_graph = rename_vertices(f, underlying_graph(graph)) +function GraphsExtensions.rename_vertices(f::Function, graph::AbstractDataGraph) + renamed_underlying_graph = GraphsExtensions.rename_vertices(f, underlying_graph(graph)) # TODO: Base the ouput type on `typeof(graph)`, for example: # convert_vertextype(eltype(renamed_vertices), typeof(graph))(renamed_underlying_graph) - renamed_graph = DataGraph{ - vertextype(renamed_underlying_graph),vertex_data_type(graph),edge_data_type(graph) - }( - renamed_underlying_graph + renamed_graph = DataGraph( + renamed_underlying_graph; + vertex_data_eltype=vertex_data_eltype(graph), + edge_data_eltype=edge_data_eltype(graph), ) for v in keys(vertex_data(graph)) renamed_graph[f(v)] = graph[v] end for e in keys(edge_data(graph)) - renamed_graph[rename_vertices(f, e)] = graph[e] + renamed_graph[GraphsExtensions.rename_vertices(f, e)] = graph[e] end return renamed_graph end -function rem_vertex!(graph::AbstractDataGraph, vertex) +function Graphs.rem_vertex!(graph::AbstractDataGraph, vertex) neighbor_edges = incident_edges(graph, vertex) # unset!(vertex_data(graph), to_vertex(graph, vertex...)) unset!(vertex_data(graph), vertex) for neighbor_edge in neighbor_edges unset!(edge_data(graph), neighbor_edge) end - rem_vertex!(underlying_graph(graph), vertex) + Graphs.rem_vertex!(underlying_graph(graph), vertex) return graph end -function rem_edge!(graph::AbstractDataGraph, edge) +function Graphs.rem_edge!(graph::AbstractDataGraph, edge) unset!(edge_data(graph), edge) - rem_edge!(underlying_graph(graph), edge) + Graphs.rem_edge!(underlying_graph(graph), edge) return graph end # Fix ambiguity with: # Graphs.neighbors(graph::AbstractGraph, v::Integer) -neighbors(graph::AbstractDataGraph, v::Integer) = neighbors(underlying_graph(graph), v) +function Graphs.neighbors(graph::AbstractDataGraph, v::Integer) + return Graphs.neighbors(underlying_graph(graph), v) +end # Fix ambiguity with: # Graphs.bfs_tree(graph::AbstractGraph, s::Integer; dir) -function bfs_tree(graph::AbstractDataGraph, s::Integer; kwargs...) - return bfs_tree(underlying_graph(graph), s; kwargs...) +function Graphs.bfs_tree(graph::AbstractDataGraph, s::Integer; kwargs...) + return Graphs.bfs_tree(underlying_graph(graph), s; kwargs...) end # Fix ambiguity with: # Graphs.dfs_tree(graph::AbstractGraph, s::Integer; dir) -function dfs_tree(graph::AbstractDataGraph, s::Integer; kwargs...) - return dfs_tree(underlying_graph(graph), s; kwargs...) +function Graphs.dfs_tree(graph::AbstractDataGraph, s::Integer; kwargs...) + return Graphs.dfs_tree(underlying_graph(graph), s; kwargs...) end function map_vertex_data(f, graph::AbstractDataGraph; vertices=nothing) graph′ = copy(graph) - vs = isnothing(vertices) ? Graphs.vertices(graph) : vertices + vs = isnothing(vertices) ? vertices(graph) : vertices for v in vs graph′[v] = f(graph[v]) end @@ -276,7 +282,7 @@ end function map_edge_data(f, graph::AbstractDataGraph; edges=nothing) graph′ = copy(graph) - es = isnothing(edges) ? Graphs.edges(graph) : edges + es = isnothing(edges) ? edges(graph) : edges for e in es if isassigned(graph, e) graph′[e] = f(graph[e]) @@ -290,58 +296,58 @@ function map_data(f, graph::AbstractDataGraph; vertices=nothing, edges=nothing) return map_edge_data(f, graph; edges) end -function getindex(graph::AbstractDataGraph, vertex) +function Base.getindex(graph::AbstractDataGraph, vertex) return vertex_data(graph)[vertex] end -function get(graph::AbstractDataGraph, vertex, default) +function Base.get(graph::AbstractDataGraph, vertex, default) return get(vertex_data(graph), vertex, default) end -function getindex(graph::AbstractDataGraph, edge::AbstractEdge) +function Base.getindex(graph::AbstractDataGraph, edge::AbstractEdge) is_edge_arranged_ = is_edge_arranged(graph, edge) data = edge_data(graph)[arrange(is_edge_arranged_, edge)] return reverse_data_direction(is_edge_arranged_, graph, data) end # Support syntax `g[v1 => v2]` -function getindex(graph::AbstractDataGraph, edge::Pair) +function Base.getindex(graph::AbstractDataGraph, edge::Pair) return graph[edgetype(graph)(edge)] end -function get(graph::AbstractDataGraph, edge::AbstractEdge, default) +function Base.get(graph::AbstractDataGraph, edge::AbstractEdge, default) is_edge_arranged_ = is_edge_arranged(graph, edge) data = get(edge_data(graph), arrange(is_edge_arranged_, edge), default) return reverse_data_direction(is_edge_arranged_, graph, data) end -function get(graph::AbstractDataGraph, edge::Pair, default) +function Base.get(graph::AbstractDataGraph, edge::Pair, default) return get(graph, edgetype(graph)(edge), default) end # Support syntax `g[1, 2] = g[(1, 2)]` -function getindex(graph::AbstractDataGraph, i1, i2, i...) +function Base.getindex(graph::AbstractDataGraph, i1, i2, i...) return graph[(i1, i2, i...)] end -function isassigned(graph::AbstractDataGraph, vertex) +function Base.isassigned(graph::AbstractDataGraph, vertex) return isassigned(vertex_data(graph), vertex) end -function isassigned(graph::AbstractDataGraph, vertex::AbstractEdge) +function Base.isassigned(graph::AbstractDataGraph, vertex::AbstractEdge) return isassigned(edge_data(graph), arrange(graph, vertex)) end -function isassigned(graph::AbstractDataGraph, vertex::Pair) +function Base.isassigned(graph::AbstractDataGraph, vertex::Pair) return isassigned(graph, edgetype(graph)(vertex)) end -function setindex!(graph::AbstractDataGraph, data, vertex) +function Base.setindex!(graph::AbstractDataGraph, data, vertex) set!(vertex_data(graph), vertex, data) return graph end -function setindex!(graph::AbstractDataGraph, data, edge::AbstractEdge) +function Base.setindex!(graph::AbstractDataGraph, data, edge::AbstractEdge) is_edge_arranged_ = is_edge_arranged(graph, edge) arranged_edge = arrange(is_edge_arranged_, edge) arranged_data = reverse_data_direction(is_edge_arranged_, graph, data) @@ -349,19 +355,19 @@ function setindex!(graph::AbstractDataGraph, data, edge::AbstractEdge) return graph end -function setindex!(graph::AbstractDataGraph, data, edge::Pair) +function Base.setindex!(graph::AbstractDataGraph, data, edge::Pair) graph[edgetype(graph)(edge)] = data return graph end # Support syntax `g[1, 2] = g[(1, 2)]` -function setindex!(graph::AbstractDataGraph, x, i1, i2, i...) +function Base.setindex!(graph::AbstractDataGraph, x, i1, i2, i...) graph[(i1, i2, i...)] = x return graph end -function induced_subgraph(graph::AbstractDataGraph, subvertices::Vector) - underlying_subgraph, vlist = induced_subgraph(underlying_graph(graph), subvertices) +function Graphs.induced_subgraph(graph::AbstractDataGraph, subvertices) + underlying_subgraph, vlist = Graphs.induced_subgraph(underlying_graph(graph), subvertices) subgraph = typeof(graph)(underlying_subgraph) for v in vertices(subgraph) if isassigned(graph, v) @@ -380,7 +386,7 @@ end # Printing # -function show(io::IO, mime::MIME"text/plain", graph::AbstractDataGraph) +function Base.show(io::IO, mime::MIME"text/plain", graph::AbstractDataGraph) println(io, "$(typeof(graph)) with $(nv(graph)) vertices:") show(io, mime, vertices(graph)) println(io, "\n") @@ -399,4 +405,4 @@ function show(io::IO, mime::MIME"text/plain", graph::AbstractDataGraph) return nothing end -show(io::IO, graph::AbstractDataGraph) = show(io, MIME"text/plain"(), graph) +Base.show(io::IO, graph::AbstractDataGraph) = show(io, MIME"text/plain"(), graph) diff --git a/src/arrange.jl b/src/arrange.jl index 1bf4330..8e861d1 100644 --- a/src/arrange.jl +++ b/src/arrange.jl @@ -1,3 +1,6 @@ +using Graphs: IsDirected, src, dst +using SimpleTraits: SimpleTraits, @traitfn + # TODO: Use a function `arrange` like in MetaGraphsNext: # https://github.com/JuliaGraphs/MetaGraphsNext.jl/blob/1539095ee6088aba0d5b1cb057c339ad92557889/src/metagraph.jl#L75-L80 # to sort the vertices, only directed graphs should store data diff --git a/src/datagraph.jl b/src/datagraph.jl index b5feb62..718bf08 100644 --- a/src/datagraph.jl +++ b/src/datagraph.jl @@ -1,6 +1,10 @@ +using Dictionaries: Dictionary +using Graphs: Graphs, edgetype +using Graphs.SimpleGraphs: SimpleGraph +using NamedGraphs.GraphsExtensions: directed_graph, vertextype + # TODO: define VertexDataGraph, a graph with only data on the # vertices, and EdgeDataGraph, a graph with only data on the edges. -# TODO: Constrain `E<:AbstractEdge`. # TODO: Use https://github.com/vtjnash/ComputedFieldTypes.jl to # automatically determine `E` from `G` from `edgetype(G)` # and `V` from `G` as `vertextype(G)`. @@ -8,153 +12,54 @@ struct DataGraph{V,VD,ED,G<:AbstractGraph,E<:AbstractEdge} <: AbstractDataGraph{ underlying_graph::G vertex_data::Dictionary{V,VD} edge_data::Dictionary{E,ED} - function DataGraph{V,VD,ED,G,E}( - underlying_graph::G, vertex_data::Dictionary{V,VD}, edge_data::Dictionary{E,ED} - ) where {V,VD,ED,G<:AbstractGraph,E<:AbstractEdge} - @assert vertextype(underlying_graph) == V - @assert edgetype(underlying_graph) == E - return new{V,VD,ED,G,E}(underlying_graph, vertex_data, edge_data) + global function _DataGraph( + underlying_graph::AbstractGraph, vertex_data::Dictionary, edge_data::Dictionary + ) + return new{ + vertextype(underlying_graph), + eltype(vertex_data), + eltype(edge_data), + typeof(underlying_graph), + edgetype(underlying_graph), + }( + underlying_graph, vertex_data, edge_data + ) end end underlying_graph_type(G::Type{<:DataGraph}) = fieldtype(G, :underlying_graph) -# TODO: rename vertex_data_eltype -vertex_data_type(G::Type{<:DataGraph}) = eltype(fieldtype(G, :vertex_data)) -# TODO: rename edge_data_eltype -edge_data_type(G::Type{<:DataGraph}) = eltype(fieldtype(G, :edge_data)) +vertex_data_eltype(G::Type{<:DataGraph}) = eltype(fieldtype(G, :vertex_data)) +edge_data_eltype(G::Type{<:DataGraph}) = eltype(fieldtype(G, :edge_data)) underlying_graph(graph::DataGraph) = getfield(graph, :underlying_graph) vertex_data(graph::DataGraph) = getfield(graph, :vertex_data) edge_data(graph::DataGraph) = getfield(graph, :edge_data) -# TODO: Is this needed? -underlying_graph_type(graph::DataGraph) = typeof(underlying_graph(graph)) -# TODO: Is this needed? -is_directed(G::Type{<:DataGraph}) = is_directed(underlying_graph_type(G)) +# TODO: Is this needed? Maybe define a generic `AbstractDataGraph` version. +Graphs.is_directed(G::Type{<:DataGraph}) = Graphs.is_directed(underlying_graph_type(G)) # TODO: Implement in terms of `set_underlying_graph`, `set_vertex_data`, etc. # TODO: Use `https://github.com/JuliaObjects/Accessors.jl`? -function copy(graph::DataGraph) +function Base.copy(graph::DataGraph) # Need to manually copy the keys of Dictionaries, see: # https://github.com/andyferris/Dictionaries.jl/issues/98 - return DataGraph( + return _DataGraph( copy(underlying_graph(graph)), copy(vertex_data(graph)), copy(edge_data(graph)) ) end -# -# Constructors -# - -function DataGraph{V,VD,ED,G,E}( - underlying_graph::AbstractGraph=G(), - vertex_data::Dictionary=Dictionary{V,VD}(), - edge_data::Dictionary=Dictionary{E,ED}(), -) where {V,VD,ED,G,E} - return DataGraph{V,VD,ED,G,E}( - convert(G, underlying_graph), - convert(Dictionary{V,VD}, vertex_data), - convert(Dictionary{E,ED}, edge_data), - ) -end - -function DataGraph{V,VD,ED}( - underlying_graph::AbstractGraph{V}, - vertex_data::Dictionary=Dictionary{V,VD}(), - edge_data::Dictionary=Dictionary{edgetype(underlying_graph),ED}(), -) where {V,VD,ED} - G = typeof(underlying_graph) - E = edgetype(underlying_graph) - return DataGraph{V,VD,ED,G,E}(underlying_graph, vertex_data, edge_data) -end - -function DataGraph{V,VD,ED}( - underlying_graph::AbstractGraph=NamedGraph{V}(), - vertex_data::Dictionary=Dictionary{V,VD}(), - edge_data::Dictionary=Dictionary{edgetype(underlying_graph),ED}(), -) where {V,VD,ED} - return DataGraph{V,VD,ED}(convert_vertextype(V, underlying_graph), vertex_data, edge_data) -end - -function DataGraph{<:Any,VD,ED}( - underlying_graph::AbstractGraph, - vertex_data::Dictionary=Dictionary{vertextype(underlying_graph),VD}(), - edge_data::Dictionary=Dictionary{edgetype(underlying_graph),ED}(), -) where {VD,ED} - V = vertextype(underlying_graph) - return DataGraph{V,VD,ED}(underlying_graph, vertex_data, edge_data) -end - -function DataGraph{V,VD}( - underlying_graph::AbstractGraph=NamedGraph{V}(), - vertex_data::Dictionary=Dictionary{V,VD}(), - edge_data::Dictionary=Dictionary{edgetype(underlying_graph),Any}(), -) where {V,VD} - ED = eltype(edge_data) - return DataGraph{V,VD,ED}(underlying_graph, vertex_data, edge_data) -end - -function DataGraph{<:Any,VD}( - underlying_graph::AbstractGraph, - vertex_data::Dictionary=Dictionary{vertextype(underlying_graph),VD}(), - edge_data::Dictionary=Dictionary{edgetype(underlying_graph),Any}(), -) where {VD} - V = vertextype(underlying_graph) - ED = eltype(edge_data) - return DataGraph{V,VD,ED}(underlying_graph, vertex_data, edge_data) -end - -function DataGraph{V}( - underlying_graph::AbstractGraph=NamedGraph{V}(), - vertex_data::Dictionary=Dictionary{V,Any}(), - edge_data::Dictionary=Dictionary{edgetype(underlying_graph),Any}(), -) where {V} - VD = eltype(vertex_data) - ED = eltype(edge_data) - return DataGraph{V,VD,ED}(underlying_graph, vertex_data, edge_data) -end - function DataGraph( - underlying_graph::AbstractGraph=NamedGraph{Any}(), - vertex_data::Dictionary=Dictionary{vertextype(underlying_graph),Any}(), - edge_data::Dictionary=Dictionary{edgetype(underlying_graph),Any}(), + underlying_graph::AbstractGraph; vertex_data_eltype::Type=Any, edge_data_eltype::Type=Any ) - V = vertextype(underlying_graph) - return DataGraph{V}(underlying_graph, vertex_data, edge_data) -end - -# -# Type interface -# - -function DataGraph{V}(underlying_graph::AbstractGraph, VD::Type, ED::Type=Any) where {V} - return DataGraph{V,VD,ED}(underlying_graph) -end - -function DataGraph(underlying_graph::AbstractGraph, VD::Type, ED::Type=Any) - V = vertextype(underlying_graph) - return DataGraph{V,VD,ED}(underlying_graph) + return _DataGraph( + underlying_graph, + Dictionary{vertextype(underlying_graph),vertex_data_eltype}(), + Dictionary{edgetype(underlying_graph),edge_data_eltype}(), + ) end -# -# Convenience constructors for simple graphs -# - -function DataGraph{V,VD,ED,G,E}(nv::Integer, args...) where {V,VD,ED,G,E} - return DataGraph{V,VD,ED,G,E}(SimpleGraph(nv), args...) -end -function DataGraph{V,VD,ED}(nv::Integer, args...) where {V,VD,ED} - return DataGraph{V,VD,ED}(SimpleGraph(nv), args...) -end -function DataGraph{<:Any,VD,ED}(nv::Integer, args...) where {VD,ED} - return DataGraph{<:Any,VD,ED}(SimpleGraph(nv), args...) -end -function DataGraph{V,VD}(nv::Integer, args...) where {V,VD} - return DataGraph{V,VD}(SimpleGraph(nv), args...) -end -function DataGraph{<:Any,VD}(nv::Integer, args...) where {VD} - return DataGraph{<:Any,VD}(SimpleGraph(nv), args...) +function DataGraph{V,VD,ED,G,E}(underlying_graph::AbstractGraph) where {V,VD,ED,G,E} + @assert edgetype(underlying_graph) === E + return _DataGraph(convert(G, underlying_graph), Dictionary{V,VD}(), Dictionary{E,ED}()) end -DataGraph{V}(nv::Integer, args...) where {V} = DataGraph{V}(SimpleGraph(nv), args...) -DataGraph(nv::Integer, args...) = DataGraph(SimpleGraph(nv), args...) # Type conversions DataGraph{V,VD,ED,G}(graph::DataGraph{V,VD,ED,G}) where {V,VD,ED,G} = copy(graph) @@ -162,27 +67,27 @@ DataGraph{V,VD,ED}(graph::DataGraph{V,VD,ED}) where {V,VD,ED} = copy(graph) DataGraph{V,VD}(graph::DataGraph{V,VD}) where {V,VD} = copy(graph) DataGraph{V}(graph::DataGraph{V}) where {V} = copy(graph) function DataGraph{V}(graph::DataGraph) where {V} - E = convert_vertextype(V, edgetype(graph)) # TODO: Make sure this properly copies converted_underlying_graph = convert_vertextype(V, underlying_graph(graph)) converted_vertex_data = Dictionary{V}(vertex_data(graph)) - converted_edge_data = Dictionary{E}(edge_data(graph)) + converted_edge_data = Dictionary{edgetype(converted_underlying_graph)}(edge_data(graph)) return DataGraph{V}( converted_underlying_graph, converted_vertex_data, converted_edge_data ) end -convert_vertextype(::Type{V}, graph::DataGraph{V}) where {V} = graph -function convert_vertextype(V::Type, graph::DataGraph) - return DataGraph{V}(graph) +GraphsExtensions.convert_vertextype(::Type{V}, graph::DataGraph{V}) where {V} = graph +function GraphsExtensions.convert_vertextype(vertextype::Type, graph::DataGraph) + return DataGraph{vertextype}(graph) end -# TODO: implement generic version in terms of `set_underlying_graph_type` -function directed_graph(G::Type{<:DataGraph}) - V = vertextype(G) - VD = vertex_data_type(G) - E = edgetype(G) - ED = edge_data_type(G) - UG = underlying_graph_type(G) - return DataGraph{V,VD,ED,directed_graph(UG),E} +# TODO: implement generic version in terms of `set_underlying_graph_type`. +function GraphsExtensions.directed_graph_type(graph_type::Type{<:DataGraph}) + return DataGraph{ + vertextype(graph_type), + vertex_data_eltype(graph_type), + edgetype(graph_type), + directed_graph_type(underlying_graph_type(graph_type)), + edgetype(graph_type), + } end diff --git a/src/traits/isunderlyinggraph.jl b/src/traits/isunderlyinggraph.jl index b3a985a..6afb1dd 100644 --- a/src/traits/isunderlyinggraph.jl +++ b/src/traits/isunderlyinggraph.jl @@ -1,9 +1,12 @@ +using SimpleTraits: SimpleTraits, @traitdef, @traitimpl + @traitdef IsUnderlyingGraph{X} #! format: off @traitimpl IsUnderlyingGraph{X} <- is_underlying_graph(X) #! format: on +using Graphs: AbstractGraph is_underlying_graph(::Type{<:AbstractGraph}) = false +using Graphs.SimpleGraphs: AbstractSimpleGraph is_underlying_graph(::Type{<:AbstractSimpleGraph}) = true -is_underlying_graph(::Type{<:AbstractNamedGraph}) = true diff --git a/src/utils.jl b/src/utils.jl new file mode 100644 index 0000000..d292515 --- /dev/null +++ b/src/utils.jl @@ -0,0 +1 @@ +not_implemented() = error("Not implemented") diff --git a/test/Project.toml b/test/Project.toml index 9237873..7bbf4af 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -2,6 +2,7 @@ DataGraphs = "b5a273c3-7e6c-41f6-98bd-8d7f1525a36a" Dictionaries = "85a47980-9c8c-11e8-2b9f-f7ca1fa99fb4" Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" +GraphsFlows = "06909019-6f44-4949-96fc-b9d9aaa02889" NamedGraphs = "678767b0-92e7-4007-89e4-4527a8725b19" Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/test/runtests.jl b/test/runtests.jl index df6f655..bb1f738 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,18 +1,45 @@ -using DataGraphs -using Dictionaries -using Graphs -using NamedGraphs -using Suppressor -using Test +@eval module $(gensym()) +using DataGraphs: DataGraphs, DataGraph, is_arranged +using Dictionaries: Indices, dictionary +using Graphs: + add_edge!, + bfs_tree, + connected_components, + degree, + dfs_tree, + dijkstra_shortest_paths, + dst, + edges, + grid, + has_edge, + has_vertex, + indegree, + ne, + nv, + outdegree, + path_graph, + src, + vertices +using Graphs.SimpleGraphs: SimpleDiGraph, SimpleEdge, SimpleGraph +using GraphsFlows: GraphsFlows +using NamedGraphs: NamedDiGraph, NamedGraph +using NamedGraphs.GraphsExtensions: ⊔, rename_vertices +using NamedGraphs.NamedGraphGenerators: named_grid, named_path_graph +using Test: @test, @test_broken, @testset using DataGraphs: is_arranged @testset "DataGraphs.jl" begin - @testset "Examples" begin - examples_path = joinpath(pkgdir(DataGraphs), "examples") - @testset "Run examples: $filename" for filename in readdir(examples_path) - if endswith(filename, ".jl") - @suppress include(joinpath(examples_path, filename)) + @eval module $(gensym()) + using DataGraphs: DataGraphs + using Suppressor: @suppress + using Test: @testset + @testset "Examples" begin + examples_path = joinpath(pkgdir(DataGraphs), "examples") + @testset "Run examples: $filename" for filename in readdir(examples_path) + if endswith(filename, ".jl") + @suppress include(joinpath(examples_path, filename)) + end end end end @@ -38,10 +65,10 @@ using DataGraphs: is_arranged @testset "Basics" begin g = grid((4,)) - dg = DataGraph{<:Any,String,Symbol}(g) - @test !isassigned(dg, Edge(1, 2)) + dg = DataGraph(g; vertex_data_eltype=String, edge_data_eltype=Symbol) + @test !isassigned(dg, SimpleEdge(1, 2)) @test !isassigned(dg, 1 => 2) - @test !isassigned(dg, Edge(1 => 2)) + @test !isassigned(dg, SimpleEdge(1 => 2)) @test !isassigned(dg, 1 => 3) @test !isassigned(dg, 1) @test !isassigned(dg, 2) @@ -76,11 +103,11 @@ using DataGraphs: is_arranged dg[1 => 2] = :E12 dg[2 => 3] = :E23 - dg[Edge(3, 4)] = :E34 + dg[SimpleEdge(3, 4)] = :E34 #@test isassigned(dg, (1, 2)) - @test isassigned(dg, Edge(2, 3)) + @test isassigned(dg, SimpleEdge(2, 3)) @test isassigned(dg, 3 => 4) - @test dg[Edge(1, 2)] == :E12 + @test dg[SimpleEdge(1, 2)] == :E12 @test dg[2 => 3] == :E23 @test dg[3 => 4] == :E34 @@ -91,8 +118,17 @@ using DataGraphs: is_arranged @test dg[(1, 1) => (1, (1, 1))] == "X" vdata = map(v -> "V$v", Indices(1:4)) - edata = map(e -> "E$(src(e))$(dst(e))", Indices([Edge(1, 2), Edge(2, 3), Edge(3, 4)])) - dg = DataGraph(g, vdata, edata) + edata = map(e -> "E$(src(e))$(dst(e))", Indices(SimpleEdge.([1 => 2, 2 => 3, 3 => 4]))) + # TODO: Make a more compact constructor that directly accepts + # vertex and edge data? Maybe `DataGraph(g; vertex_data=vdata, edge_data=edata)` + # or `DataGraph(g; vertex_data=v -> "V$v", edge_data=e -> "E$(src(e))$(dst(e))")`. + dg = DataGraph(g; vertex_data_eltype=eltype(vdata), edge_data_eltype=eltype(edata)) + for v in vertices(dg) + dg[v] = vdata[v] + end + for e in edges(dg) + dg[e] = edata[e] + end @test dg[1] == "V1" @test dg[2] == "V2" @@ -103,15 +139,7 @@ using DataGraphs: is_arranged @test dg[2 => 3] == "E23" @test dg[3 => 4] == "E34" - @test DataGraph(g) isa - DataGraph{Int,Any,Any,SimpleGraph{Int},Graphs.SimpleGraphs.SimpleEdge{Int}} - @test DataGraph{<:Any,String}(g) isa - DataGraph{Int,String,Any,SimpleGraph{Int},Graphs.SimpleGraphs.SimpleEdge{Int}} - @test DataGraph{<:Any,Any,String}(g) isa - DataGraph{Int,Any,String,SimpleGraph{Int},Graphs.SimpleGraphs.SimpleEdge{Int}} - - # TODO: is this needed? - #@test DataGraph{<:Any,String}(g) isa DataGraph{Any,String} + @test DataGraph(g) isa DataGraph{Int,Any,Any,SimpleGraph{Int},SimpleEdge{Int}} # Vertices with mixed types dg = DataGraph(NamedGraph(grid((4,)), [1, "X", 2, "Y"])) @@ -148,7 +176,7 @@ using DataGraphs: is_arranged end @testset "Disjoint unions" begin - g = DataGraph{<:Any,String,String}(named_grid((2, 2))) + g = DataGraph(named_grid((2, 2)); vertex_data_eltype=String, edge_data_eltype=String) for v in vertices(g) g[v] = "V$v" @@ -195,7 +223,7 @@ using DataGraphs: is_arranged g1[1] = ["A", "B", "C"] g1[1 => 2] = ["E", "F"] - g2 = DataGraph(Graph(5)) + g2 = DataGraph(SimpleGraph(5)) add_edge!(g2, 1 => 5) g2[1] = ["C", "D", "E"] @@ -313,4 +341,14 @@ using DataGraphs: is_arranged @test ps.parents == dictionary([1 => 1, 2 => 1, 3 => 2, 4 => 3]) @test ps.pathcounts == dictionary([1 => 1.0, 2 => 1.0, 3 => 1.0, 4 => 1.0]) end + @testset "GraphsFlows.mincut (vertextype=$(eltype(verts))" for verts in ( + [1, 2, 3, 4], ["A", "B", "C", "D"] + ) + g = DataGraph(NamedGraph(path_graph(4), verts)) + part1, part2, flow = GraphsFlows.mincut(g, verts[1], verts[4]) + @test verts[1] ∈ part1 + @test verts[4] ∈ part2 + @test flow == 1 + end +end end