Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a2b1e84
[PartitionedGraphs] Some `QuotientView` fixes and improvements
Nov 24, 2025
be7d219
[PartitionedGraphs] Add directed/undirected graph type methos for `Pa…
Nov 24, 2025
e453d7f
[PartitionedGraphs] Add `similar_type` method for `QuotientView`.
Nov 24, 2025
9487b51
[PartitionedGraphs] Add `quotientview` interface function
Nov 24, 2025
6bfb9b4
[PartitionedGraphs] `quotient_graph` now returns a graph of similar t…
Nov 24, 2025
a7d5a58
Version bump
Nov 24, 2025
b6f1f6b
[PartitionGraphs] fallback method for `show`
Nov 24, 2025
3702ef6
Fix missing import
Nov 24, 2025
4dc4611
Remove broken import
Nov 25, 2025
f1641a2
[PartitionedGraphs] Add `edgeless_quotient_graph` interface function.
Nov 25, 2025
36381cf
[PartitionedGraphs] `quotient_graph` now returns a (un)directed graph…
Dec 3, 2025
703363a
[PartitionedGraphs] Explictly construct edges of correct type when cr…
Dec 3, 2025
d277466
[PartitionedGraphs] `PartitionedGraph` now takes the quotient graph t…
Dec 3, 2025
e55cb9f
Fix typo as `graph_type` -> `graph`.
Dec 3, 2025
fa2608c
[PartitionedGraphs] Add directed/undireced graph constructor function…
Dec 3, 2025
e81da68
Remove redundant `directed_graph` method, add fallbacks `NamedGraph` …
Dec 3, 2025
d7fe1ee
[PartitionedGraphs] Remove overlapping method
Dec 3, 2025
809f5b8
Add fall back for `position_graph_type` based on `promote_op`.
Dec 3, 2025
02789eb
Test structs now implemented the correct interface for `NamedGraphs`
Dec 3, 2025
0ae1425
Add `add_vertices!` method for `AbstractSimpleGraph`;
Dec 4, 2025
e1627a9
Rename `graph_from_vertices` -> `similar_graph` and add single, doubl…
Dec 4, 2025
1929506
Rename `edgeless_quotient_graph` to `similar_quotient_graph`.
Dec 4, 2025
3cd86a3
Add tests for `similar_graph` function
Dec 4, 2025
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,7 +1,7 @@
name = "NamedGraphs"
uuid = "678767b0-92e7-4007-89e4-4527a8725b19"
authors = ["Matthew Fishman <mfishman@flatironinstitute.org>, Joseph Tindall <jtindall@flatironinstitute.org> and contributors"]
version = "0.8.2"
version = "0.8.3"

[deps]
AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c"
Expand Down
2 changes: 1 addition & 1 deletion examples/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ NamedGraphs = "678767b0-92e7-4007-89e4-4527a8725b19"

[compat]
Graphs = "1.12.0"
NamedGraphs = "0.8.1"
NamedGraphs = "0.8.3"
22 changes: 8 additions & 14 deletions src/abstractnamedgraph.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ using Dictionaries: set!
using Graphs:
Graphs,
AbstractGraph,
AbstractSimpleGraph,
IsDirected,
a_star,
add_edge!,
Expand Down Expand Up @@ -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
Expand All @@ -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()
Expand All @@ -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()
Expand Down Expand Up @@ -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!
Expand All @@ -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))

Expand Down Expand Up @@ -493,9 +489,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
Expand Down
28 changes: 24 additions & 4 deletions src/lib/GraphsExtensions/src/abstractgraph.jl
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,33 @@ function convert_vertextype(V::Type, graph::AbstractGraph)
return not_implemented()
end

function graph_from_vertices(graph_type::Type{<:AbstractGraph}, vertices)
return graph_type(vertices)
similar_graph(graph::AbstractGraph) = similar_graph(typeof(graph))
function similar_graph(T::Type{<:AbstractGraph})
try
return T()
catch e
if e isa MethodError
return not_implemented()
else
rethrow(e)
end
end
end

function similar_graph(graph, vertices)
new_graph = convert_vertextype(eltype(vertices), similar_graph(graph))
add_vertices!(new_graph, vertices)
return new_graph
end
function similar_graph(graph, vertices, edges)
new_graph = similar_graph(graph, vertices)
add_edges!(new_graph, edges)
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))
Expand All @@ -74,7 +94,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)
Expand Down
12 changes: 8 additions & 4 deletions src/lib/GraphsExtensions/src/simplegraph.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ 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))
return graph_type(length(vertices))
end
similar_graph(graph_type::Type{<:AbstractSimpleGraph}) = graph_type()

function convert_vertextype(vertextype::Type, graph::AbstractSimpleGraph)
return not_implemented()
Expand All @@ -30,3 +27,10 @@ undirected_graph_type(G::Type{<:SimpleGraph}) = G
# TODO: Use traits to make this more general.
directed_graph_type(G::Type{<:SimpleDiGraph}) = G
undirected_graph_type(G::Type{<:SimpleDiGraph}) = SimpleGraph{vertextype(G)}

function add_vertices!(graph::AbstractSimpleGraph, vertices::Base.OneTo{Int})
for _ in vertices
add_vertex!(graph)
end
return graph
end
27 changes: 27 additions & 0 deletions src/lib/GraphsExtensions/test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ using AbstractTrees:
rootindex
using Dictionaries: Dictionary, Indices
using Graphs:
AbstractGraph,
add_edge!,
add_vertex!,
dst,
Expand Down Expand Up @@ -44,6 +45,7 @@ using NamedGraphs.GraphsExtensions:
add_edge,
add_edges,
add_edges!,
add_vertices!,
all_edges,
arrange_edge,
arranged_edges,
Expand Down Expand Up @@ -83,6 +85,7 @@ using NamedGraphs.GraphsExtensions:
rem_edges!,
rename_vertices,
root_vertex,
similar_graph,
subgraph,
tree_graph_node,
undirected_graph,
Expand All @@ -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)
Expand Down Expand Up @@ -552,6 +556,29 @@ 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 "Not implemented" similar_graph(g)

# add_vertices!
g = path_graph(4)
add_vertices!(g, vertices(g))
@test nv(g) == 8
@test_throws MethodError add_vertices!(g, [2, 3])

# add_edge
g = SimpleGraph(4)
add_edge!(g, 1 => 2)
Expand Down
32 changes: 22 additions & 10 deletions src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ using Dictionaries: Dictionary
using Graphs:
AbstractEdge,
AbstractGraph,
AbstractSimpleGraph,
Graphs,
add_vertex!,
dst,
Expand All @@ -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
Expand Down Expand Up @@ -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))
Expand Down
48 changes: 44 additions & 4 deletions src/lib/PartitionedGraphs/src/partitionedgraph.jl
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
9 changes: 9 additions & 0 deletions src/lib/PartitionedGraphs/src/partitionedview.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,12 @@ 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

# So partitions of `AbstractSimpleGraph` make `AbstractSimpleGraph`s
function _quotient_graph_from_vertices(graph::PartitionedView, vertices)
return _quotient_graph_from_vertices(unpartitioned_graph(graph), vertices)
end
Loading
Loading