diff --git a/Project.toml b/Project.toml index 140c7af..4f4bc03 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "DataGraphs" uuid = "b5a273c3-7e6c-41f6-98bd-8d7f1525a36a" -version = "0.3.17" +version = "0.4.0" authors = ["Matthew Fishman and contributors"] [workspace] @@ -22,6 +22,6 @@ DataGraphsGraphsFlowsExt = "GraphsFlows" Dictionaries = "0.4" Graphs = "1" GraphsFlows = "0.1.1" -NamedGraphs = "0.10" +NamedGraphs = "0.11" SimpleTraits = "0.9" julia = "1.7" diff --git a/docs/Project.toml b/docs/Project.toml index 6dc61c2..23e2179 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -8,7 +8,7 @@ Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306" path = ".." [compat] -DataGraphs = "0.3" +DataGraphs = "0.4" Documenter = "1.10" ITensorFormatter = "0.2.27" Literate = "2.20.1" diff --git a/examples/Project.toml b/examples/Project.toml index 44b4e85..e16be5f 100644 --- a/examples/Project.toml +++ b/examples/Project.toml @@ -7,6 +7,6 @@ NamedGraphs = "678767b0-92e7-4007-89e4-4527a8725b19" path = ".." [compat] -DataGraphs = "0.3" +DataGraphs = "0.4" Graphs = "1.12" -NamedGraphs = "0.10" +NamedGraphs = "0.11, 0.10" diff --git a/ext/DataGraphsGraphsFlowsExt/DataGraphsGraphsFlowsExt.jl b/ext/DataGraphsGraphsFlowsExt/DataGraphsGraphsFlowsExt.jl index 4b181e8..e5dd703 100644 --- a/ext/DataGraphsGraphsFlowsExt/DataGraphsGraphsFlowsExt.jl +++ b/ext/DataGraphsGraphsFlowsExt/DataGraphsGraphsFlowsExt.jl @@ -2,7 +2,18 @@ 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...) +function GraphsFlows.mincut( + graph::AbstractDataGraph, + source_vertex, + target_vertex; + kwargs... + ) + return GraphsFlows.mincut( + underlying_graph(graph), + source_vertex, + target_vertex; + kwargs... + ) end + end diff --git a/src/abstractdatagraph.jl b/src/abstractdatagraph.jl index 3c63b6e..20dd760 100644 --- a/src/abstractdatagraph.jl +++ b/src/abstractdatagraph.jl @@ -1,16 +1,14 @@ using Dictionaries: Indices, set!, unset! -using Graphs: Graphs, AbstractEdge, IsDirected, a_star, add_edge!, add_vertex!, edges, ne, - nv, steiner_tree, vertices +using Graphs: Graphs, AbstractEdge, IsDirected, a_star, add_edge!, add_vertex!, edges, + indegree, ne, nv, outdegree, steiner_tree, vertices using NamedGraphs.GraphsExtensions: GraphsExtensions, add_vertices!, arrange_edge, incident_edges, is_edge_arranged, similar_graph, vertextype using NamedGraphs.OrdinalIndexing: OrdinalSuffixedInteger using NamedGraphs.SimilarType: similar_type using NamedGraphs: NamedGraphs, AbstractEdges, AbstractNamedEdge, AbstractNamedGraph, - AbstractVertices, position_graph_type + AbstractVertices, NamedGraph, position_graph_type using SimpleTraits: SimpleTraits, @traitfn, Not -is_underlying_graph(::Type{<:AbstractNamedGraph}) = true - abstract type AbstractDataGraph{V, VD, ED} <: AbstractNamedGraph{V} end vertex_data_type(::Type{<:AbstractGraph}) = Any @@ -104,8 +102,153 @@ GraphsExtensions.directed_graph_type(::AbstractDataGraph) = not_implemented() GraphsExtensions.undirected_graph_type(::AbstractDataGraph) = not_implemented() # Thase canot be implemented abstractly. -function GraphsExtensions.convert_vertextype(vertextype::Type, graph::AbstractDataGraph) - return not_implemented() +GraphsExtensions.convert_vertextype(::Type, ::AbstractDataGraph) = not_implemented() + +# =================================== `similar_graph` ==================================== # + +# Construct a similar `AbstractDataGraph` with `vertices` and `edges`. +function GraphsExtensions.similar_graph( + graph::AbstractDataGraph, + vertices, + edges + ) + new_underlying_graph = similar_graph(underlying_graph(graph), vertices, edges) + return similar_graph(graph, new_underlying_graph) +end + +# Construct `graph_type` with `vertices` and `edges`. +function GraphsExtensions.similar_graph( + graph_type::Type{<:AbstractDataGraph}, + vertices, + edges + ) + underlying_graph = similar_graph(underlying_graph_type(graph_type), vertices, edges) + return similar_graph(graph_type, underlying_graph) +end + +# Construct a similar `AbstractDataGraph` defined by an `underlying_graph_type` with +# `vertex_data_type` and `edge_data_type`. +function GraphsExtensions.similar_graph( + graph::AbstractDataGraph, + underlying_graph_type::Type{<:AbstractGraph}, + vertex_data_type, + edge_data_type + ) + underlying_graph = similar_graph(underlying_graph_type, vertices(graph), edges(graph)) + return similar_graph(graph, underlying_graph, vertex_data_type, edge_data_type) +end +function GraphsExtensions.similar_graph( + graph::AbstractDataGraph, + underlying_graph_type::Type{<:AbstractGraph} + ) + underlying_graph = similar_graph(underlying_graph_type, vertices(graph), edges(graph)) + return similar_graph(graph, underlying_graph_type) +end + +# Construct a similar `AbstractDataGraph` defined by `underlying_graph` with `vertex_data_type` and `edge_data_type`. +# To be specialized (has fallback). +""" + similar_graph(datagraph::AbstractDataGraph, graph::AbstractGraph, [VD=vertex_data_type(datagraph), ED=edge_data_type(datagraph)]) + similar_graph(datagraph::AbstractDataGraph, G, [VD=vertex_data_type(datagraph), ED=edge_data_type(datagraph)]) + +Create an uninitialized data graph, similar to the provided `datagraph`, but with underlying +graph defined by `graph`. If instead a type `G` is given as the second argument, +then an empty underlying graph is first constructed using `graph = similar_graph(G)`. +Optionally, one may specify the vertex and edge data types, which default to those of the +provided source `datagraph`. + +Custom `AbstractDataGraph` subtypes may specialize on first method (either the two or four argument version) +to choose the return type of the resulting graph, based on the arguments. +If they do not specialize on this method, then the default is +`DataGraph(graph; vertex_data_type, edge_data_type)`. +""" +function GraphsExtensions.similar_graph( + ::AbstractDataGraph, + underlying_graph::AbstractGraph, + vertex_data_type, + edge_data_type + ) + return DataGraph(underlying_graph; vertex_data_type, edge_data_type) +end +function GraphsExtensions.similar_graph( + graph::AbstractDataGraph, + underlying_graph::AbstractGraph + ) + VD = vertex_data_type(graph) + ED = edge_data_type(graph) + return similar_graph(graph, underlying_graph, VD, ED) +end + +# Construct a subtype of `graph_type` with an empty underlying_graph of type `underlying_graph_type` +# and `vertex_data_type` and `edge_data_type`. +function GraphsExtensions.similar_graph( + graph_type::Type{<:AbstractDataGraph}, + underlying_graph_type::Type{<:AbstractGraph}, + vertex_data_type, + edge_data_type + ) + underlying_graph = similar_graph(underlying_graph_type) + return similar_graph(graph_type, underlying_graph, vertex_data_type, edge_data_type) +end +function GraphsExtensions.similar_graph( + graph_type::Type{<:AbstractDataGraph}, + underlying_graph_type::Type{<:AbstractGraph} + ) + underlying_graph = similar_graph(underlying_graph_type) + return similar_graph(graph_type, underlying_graph) +end + +# Construct a subtype of `graph_type` defined by `underlying_graph` with `vertex_data_type` and `edge_data_type`. +# To be specialized if `graph_type` doesn't have the appropriate constructor. One can +# specialize on either the two or four argument method. +""" + similar_graph(DG::Type{<:AbstractDataGraph}, graph::AbstractGraph) + similar_graph(DG::Type{<:AbstractDataGraph}, graph::AbstractGraph, VD, ED) + + similar_graph(DG::Type{<:AbstractDataGraph}, G) + similar_graph(DG::Type{<:AbstractDataGraph}, G, VD, ED) + +Create an uninitialized data graph that acts like the specified data graph type +`DG`, but with underlying graph defined by `graph`. +If instead a type `G` is given as the second argument, then +an empty underlying graph is first constructed using `graph = similar_graph(G)`. +Optionally, one may also specify the vertex and edge data types of the resulting data graph. + +By default, calls the constructors `DG(graph)` and `DG(graph, VD, ED)` respectively. +A given custom `AbstractDataGraph` subtype may specialize on the first two methods above +(in terms a graph `graph`) if such constructors do not exist for that subtype. +""" +function GraphsExtensions.similar_graph( + graph_type::Type{<:AbstractDataGraph}, + underlying_graph::AbstractGraph, + vertex_data_type, + edge_data_type + ) + return graph_type(underlying_graph, vertex_data_type, edge_data_type) +end +function GraphsExtensions.similar_graph( + graph_type::Type{<:AbstractDataGraph}, + underlying_graph::AbstractGraph + ) + return graph_type(underlying_graph) +end + +# Set the underlying graph of a data graph, preserving data. Graphs must match. +function set_underlying_graph(datagraph::AbstractDataGraph, graph::AbstractGraph) + if vertices(datagraph) != vertices(graph) || edges(datagraph) != edges(graph) + throw( + ArgumentError( + "New underlying graph must have the same vertices and edges as the data graph" + ) + ) + end + + new_datagraph = similar_graph(datagraph, graph) + + vertex_data(new_datagraph) .= vertex_data(datagraph) + edge_data(new_datagraph) .= edge_data(datagraph) + + return new_datagraph end # Fix for ambiguity error with `AbstractGraph` version @@ -157,7 +300,7 @@ function reverse_data_direction(graph::AbstractDataGraph, edge::AbstractEdge, da end @traitfn function GraphsExtensions.directed_graph(graph::AbstractDataGraph::(!IsDirected)) - digraph = directed_graph(typeof(graph))(directed_graph(underlying_graph(graph))) + digraph = similar_graph(graph, directed_graph(underlying_graph(graph))) for v in vertices(graph) # TODO: Only loop over `keys(vertex_data(graph))` if isassigned(graph, v) @@ -178,12 +321,9 @@ end end function GraphsExtensions.rename_vertices(f::Function, graph::AbstractDataGraph) - # Uses the two-argument `similar_graph` method so the new graph has correct vertex type renamed_vertices = map(f, vertices(graph)) - renamed_graph = similar_graph(graph, eltype(renamed_vertices)) - - add_vertices!(renamed_graph, renamed_vertices) + renamed_graph = similar_graph(graph, renamed_vertices) for vertex in vertices(graph) if isassigned(graph, vertex) @@ -203,9 +343,8 @@ function GraphsExtensions.rename_vertices(f::Function, graph::AbstractDataGraph) end function Base.reverse(graph::AbstractDataGraph) - reversed_graph = similar_graph(graph) + reversed_graph = similar_graph(graph, vertices(graph)) for v in vertices(graph) - add_vertex!(reversed_graph, v) if isassigned(graph, v) reversed_graph[v] = graph[v] end diff --git a/src/datagraph.jl b/src/datagraph.jl index cf317f1..60edbe4 100644 --- a/src/datagraph.jl +++ b/src/datagraph.jl @@ -53,28 +53,37 @@ edge_data_type(G::Type{<:DataGraph}) = eltype(fieldtype(G, :edge_data)) # Extras -function GraphsExtensions.similar_graph(T::Type{<:DataGraph}) - similar_underlying_graph = similar_graph(underlying_graph_type(T)) - return T(similar_underlying_graph) +# Overwrite the `AbstractDataGraph` fallback (even though they coincide for `DataGraph`) +function GraphsExtensions.similar_graph( + ::DataGraph, + underlying_graph::AbstractGraph, + vertex_data_type, + edge_data_type + ) + return similar_graph(DataGraph, underlying_graph, vertex_data_type, edge_data_type) end -function GraphsExtensions.similar_graph(dg::DataGraph, underlying_graph::AbstractGraph) - return DataGraph( - underlying_graph; - vertex_data_type = vertex_data_type(dg), - edge_data_type = edge_data_type(dg) + +# Constructor method needs overwritten. +function GraphsExtensions.similar_graph( + T::Type{<:DataGraph}, + underlying_graph::AbstractGraph, + vertex_data_type = vertex_data_type(T), + edge_data_type = edge_data_type(T) ) + return T(underlying_graph; vertex_data_type, edge_data_type) end function Base.copy(graph::DataGraph) # Need to manually copy the keys of Dictionaries, see: # https://github.com/andyferris/Dictionaries.jl/issues/98 return _DataGraph( - copy(underlying_graph(graph)), copy(vertex_data(graph)), copy(edge_data(graph)) + copy(graph.underlying_graph), copy(graph.vertex_data), copy(graph.edge_data) ) end function DataGraph{V}( - underlying_graph::AbstractGraph; vertex_data_type::Type = Any, + underlying_graph::AbstractGraph; + vertex_data_type::Type = Any, edge_data_type::Type = Any ) where {V} converted_underlying_graph = convert_vertextype(V, underlying_graph) @@ -107,12 +116,12 @@ DataGraph{V}(graph::DataGraph{V}) where {V} = copy(graph) function DataGraph{V}(graph::DataGraph) where {V} # TODO: Make sure this properly copies converted_underlying_graph = convert_vertextype(V, underlying_graph(graph)) - converted_vertex_data = Dictionary{V}(vertex_data(graph)) - # This doesn't convert properly. - # converted_edge_data = Dictionary{edgetype(converted_underlying_graph)}(edge_data(graph)) + converted_vertex_data = Dictionary{V}(assigned_vertex_data(graph)) + + old_edge_data = assigned_edge_data(graph) converted_edge_data = Dictionary( - edgetype(converted_underlying_graph).(keys(edge_data(graph))), - values(edge_data(graph)) + edgetype(converted_underlying_graph).(keys(old_edge_data)), + collect(old_edge_data) ) return _DataGraph( converted_underlying_graph, diff --git a/src/dataview.jl b/src/dataview.jl index 158b1c7..667cd94 100644 --- a/src/dataview.jl +++ b/src/dataview.jl @@ -67,17 +67,15 @@ function Base.isassigned(view::EdgeDataView, key::Pair) end Base.getindex(view::VertexOrEdgeDataView{K}, key::K) where {K} = _getindex(view, key) -function Base.getindex(view::VertexOrEdgeDataView, key) - return _getindex(view, to_graph_index(view.graph, key)) -end +Base.getindex(view::VertexOrEdgeDataView, key) = _getindex(view, key) function _getindex(view::VertexDataView, key) - key in keys(view) || throw(IndexError("VertexDataView does not contain index: $key")) - return get_vertex_data(view.graph, key) + isassigned(view, key) || throw(IndexError("VertexDataView does not contain index $key")) + return getindex(view.graph, key) end function _getindex(view::EdgeDataView, key) - key in keys(view) || throw(IndexError("EdgeDataView does not contain index: $key")) - return get_edge_data(view.graph, key) + isassigned(view, key) || throw(IndexError("EdgeDataView does not contain index $key")) + return getindex(view.graph, key) end # Support indexing with `Indices`. diff --git a/src/lib/DataGraphsPartitionedGraphsExt/src/DataGraphsPartitionedGraphsExt.jl b/src/lib/DataGraphsPartitionedGraphsExt/src/DataGraphsPartitionedGraphsExt.jl index 3373975..2453de3 100644 --- a/src/lib/DataGraphsPartitionedGraphsExt/src/DataGraphsPartitionedGraphsExt.jl +++ b/src/lib/DataGraphsPartitionedGraphsExt/src/DataGraphsPartitionedGraphsExt.jl @@ -1,9 +1,9 @@ module DataGraphsPartitionedGraphsExt -using ..DataGraphs: AbstractDataGraph, DataGraph, DataGraphs, _DataGraph, _getindex, - edge_data, edgetype, get_edge_data, get_edges_data, get_index_data, get_vertex_data, - get_vertices_data, is_edge_assigned, is_graph_index_assigned, is_vertex_assigned, - set_edge_data!, set_edges_data!, set_index_data!, set_vertex_data!, set_vertices_data!, - underlying_graph, vertex_data +using ..DataGraphs: AbstractDataGraph, DataGraph, DataGraphs, IsUnderlyingGraph, _DataGraph, + _getindex, edge_data, edgetype, get_edge_data, get_edges_data, get_index_data, + get_vertex_data, get_vertices_data, is_edge_assigned, is_graph_index_assigned, + is_underlying_graph, is_vertex_assigned, set_edge_data!, set_edges_data!, + set_index_data!, set_vertex_data!, set_vertices_data!, underlying_graph, vertex_data using Dictionaries: Dictionary, IndexError, Indices using Graphs: Graphs, AbstractEdge, AbstractGraph, edges, vertices using NamedGraphs.GraphsExtensions: @@ -14,10 +14,11 @@ using NamedGraphs.PartitionedGraphs: AbstractPartitionedGraph, PartitionedGraph, QuotientVertexSlice, QuotientVertexVertex, QuotientVertexVertices, QuotientVertices, QuotientVerticesVertices, QuotientView, departition, has_quotientedge, has_quotientvertex, parent_graph_type, partitioned_vertices, partitionedgraph, - quotient_graph, quotient_graph_vertextype, quotientedges, quotientvertex, - quotientvertices, unpartitioned_graph + quotient_graph, quotient_graph_type, quotientedges, quotientvertex, quotientvertices, + unpartitioned_graph using NamedGraphs: NamedGraphs, Edges, Vertices, get_graph_index, to_edges, to_graph_index, to_vertices +using SimpleTraits: SimpleTraits, @traitfn, Not # ======================== DataGraphs interface for QuotientView ========================= # @@ -50,7 +51,14 @@ function DataGraphs.set_edge_data!(qv::QuotientView, val, e) return qv end -DataGraphs.underlying_graph(qv::QuotientView) = underlying_graph(copy(qv)) +function DataGraphs.is_underlying_graph(qv::Type{<:QuotientView}) + return is_underlying_graph(quotient_graph_type(qv)) +end + +@traitfn DataGraphs.underlying_graph(qv::QuotientView::IsUnderlyingGraph) = copy(qv) +@traitfn function DataGraphs.underlying_graph(qv::QuotientView::(!IsUnderlyingGraph)) + return underlying_graph(copy(qv)) +end function Base.isassigned(qv::QuotientView, ind) return DataGraphs.isassigned_datagraph(qv, to_graph_index(qv, ind)) diff --git a/src/traits/isunderlyinggraph.jl b/src/traits/isunderlyinggraph.jl index 428925f..16d1e20 100644 --- a/src/traits/isunderlyinggraph.jl +++ b/src/traits/isunderlyinggraph.jl @@ -10,3 +10,6 @@ is_underlying_graph(::Type{<:AbstractGraph}) = false using Graphs.SimpleGraphs: AbstractSimpleGraph is_underlying_graph(::Type{<:AbstractSimpleGraph}) = true + +using NamedGraphs: GenericNamedGraph +is_underlying_graph(::Type{<:GenericNamedGraph}) = true diff --git a/test/Project.toml b/test/Project.toml index c3279fc..5ebc3e8 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -15,12 +15,12 @@ path = ".." [compat] Aqua = "0.8.11" -DataGraphs = "0.3" +DataGraphs = "0.4" Dictionaries = "0.4.4" Graphs = "1.12" GraphsFlows = "0.1.1" ITensorPkgSkeleton = "0.3.42" -NamedGraphs = "0.10" +NamedGraphs = "0.11" SafeTestsets = "0.1" Suppressor = "0.2.8" Test = "1.10"