My 2-cents.
I didn’t like the focus on mutable operations in GraphInterface.jl, but I liked the other bits!
Constructing graphs a vertex/edge at a time is typically a recipe for performance problems and/or multithread contention problems. So I’d rather see “batch” operations in the GraphInterface.jl
in the requirements. (Batch to one at a time is easy to implement, but the reverse is hard.) But I’d more strongly suggest a split between mutable graphs and algorithm interface wrappers…
I’d also encourage an interface that makes it easy to convert among graph types. In my experience, the -one-graph-type-to-rule-them-all- is impractical and you always end up converting at some point. (in-neighbors to out-neighbors for various algorithms… add complex metadata for flow algorithms… add specialized weights for more complex implementations). Hence the meme.
Here were some notes I typed up a few years ago when I was thinking about graphs in Julia. These are in no way complete It was a list of ideas to think about. This is heavily focused on non mutable graphs (but that may wrap underlying mutable containers). Never really got around to doing anything with them. Figure there are better here than on my computer. My apologies if these ideas are now duplicative of others (who likely proposed similar and better ideas). There were definitely lots of inspiration for it (e.g. old BGL ideas) and other graph libraries in Julia and elsewhere. (e.g. these are similar to @gdalle’s notes above.)
GraphViews.jl
- AbstractGraphView{}
- AbstractIndexedGraph <: AbstractFiniteGraph <: AbstractGraph
edges(G) neighbors(G,i) - ?
graphview(df::DataFrame, src=:col1, dst=:col2)
graphview(edgelist::Vector{Pair{X,X}})
graphview(edgepair::Pair{Vector{X},Vector{X}})
graphview(A::SparseMatrixCSC)
graphview(src::Vector{X}, dst::Vector{X})
graphview(src::Vector{X}, dst::Vector{X}; )
graphview(NF::T) where T <: AbstractNeighborFunction
graphview(NF::Function, )
edgesview(data...)
neighborsview(data...)
neighbors(G,i::)
edges(G)
summary(G), census(G), ?
----
Lots of good ideas for graph data in the Meshes.jl
meshdata()... ->
graphdata(...) ->
----
connected components
betweenness centrality
layout
graph eigenvectors
maxflow
-- pagerank
--
collect(breadth_first_search(Network(data)))
pagerank(Network(data))
pagerank()
apsp()
implicit_apsp
x = DynamicNetwork()
data -> algorithm -> output
GraphBase -> Setup for Vector{Pair{X,X}} as a graph. (Restrictions on X?)
Setup for
Vector{Pair{X,X}}: EdgeListGraph
Pair{Vector{X},Vector{X}}: EdgePairGraph as graph?
Setup for Pair{DataFrame, DataFrame} as a graph.
Tuple{}
vertex_census()
edge_census()
graph_census() # vertex,edge census + connectivity
incidence_matrix() --> Sparse, VertexMap
adjacency_matrix() --> Sparse, VertexMap
GraphPipelines # lazy strategies
GraphData # I/O: Snap, GML, Matrix Market, EdgeList, CSV, AdjList,
GraphDataStructures # EdgeList, Adjacency,
collect(G) # gives a list of vertices
collect(edges(G)) # gives a list of edges
collect(edges(G))
eltype(G) # vertex type
---
Step 1: Load a graph.
- GraphData # I/O: Snap, GML, Matrix Market, EdgeList, CSV, AdjList, Metis, ...
using GraphData/GraphIO
edges = load("file.edges"; format=fmt"SNAP")
---
Step 2: What is GraphBase?
In Julia:
Images are Matrix{X} where X <: "Pixel"
What's a graph?
An array of edges.
Vector{Pair{X,X}}: EdgeListGraph #edges are "columns"
Pair{Vector{X},Vector{X}}: EdgePairGraph as graph? # edges are row...
vertex_census()
edge_census()
graph_census() # vertex,edge census + connectivity
collect(vertices(G)) # gives a list of vertices
collect(edges(G)) # gives a list of edges
# degree(G, i::X) = mapreduce(...)
degree(G, i::X) =
neighbors(G, i::X) = ?
---
GraphDataStructures # EdgeList, AdjacencyList..., SetOfSets...
---
GraphAlgorithms
connected components
betweenness centrality
layout
graph eigenvectors
maxflow
--- Some specific scenarios (These are new based on the post here...)
Translation among graph formats.
--> You will always have to translate at some point.
Make it easy to run simple algorithms on simple types.
e.g. the following should be usable as graph objects
Dict{Int,Set{Int}}()
Vector{Set{Int}}()
Vector{Vector{Int}}()
with
vertices(G::Dict{Int,Set{Int}}) = keys(G) # or something similar
vertices(G::Vector{Vector{Int}}) = eachindex(G) # or something similar
vertices(G::Vector{Set{Int}}) = eachindex(G) # or something similar
but also implicitly defined graphs.
struct LineGraph
start::Int
end::Int
end
vertices(G::LineGraph) = start:end
function neighbors(G::LineGraph, i)
if i < start
return nothing
elseif i > end
return nothing
elseif i == start
return (i+1, )
elseif i == end
return (i-1, )
else
return (i-1, i+1)
end
end
edges(G::LineGraph) = GraphInterface.forall_vertices_get_neighbors(G) # this could be a simple implementation.