Skip to content

Commit

Permalink
Migrate from LightGraphs to Graphs.jl (#41)
Browse files Browse the repository at this point in the history
* Migrate from LightGraphs to Graphs.jl
  • Loading branch information
simonschoelly authored Oct 20, 2021
1 parent 8cdc596 commit 065eebd
Show file tree
Hide file tree
Showing 21 changed files with 105 additions and 106 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ jobs:
julia --project=docs -e '
using Documenter: DocMeta, doctest
using SimpleValueGraphs
DocMeta.setdocmeta!(SimpleValueGraphs, :DocTestSetup, :(using SimpleValueGraphs, LightGraphs, Random); recursive=true)
DocMeta.setdocmeta!(SimpleValueGraphs, :DocTestSetup, :(using SimpleValueGraphs, Graphs, Random); recursive=true)
doctest(SimpleValueGraphs)' # change MYPACKAGE to the name of your package
- run: julia --project=docs docs/make.jl
env:
Expand Down
8 changes: 4 additions & 4 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
name = "SimpleValueGraphs"
uuid = "b43c691f-cac2-5415-8122-396fe16a49fc"
authors = ["Simon Schoelly <[email protected]>"]
version = "0.3.2"
version = "0.4.0"

[deps]
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
LightGraphs = "093fc24a-ae57-5d10-9952-331d41423f4d"
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
SimpleTraits = "699a6c99-e7fa-54fc-8d76-47d257e15c1d"
Expand All @@ -15,8 +15,8 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[compat]
DataStructures = "0.18.9"
LightGraphs = "1.3"
Graphs = "1.4.1"
Requires = "1.0"
SimpleTraits = "0.9.3"
SimpleWeightedGraphs = "1.1.1"
SimpleWeightedGraphs = "1.2"
julia = "1.5"
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
[![codecov](https://codecov.io/gh/simonschoelly/SimpleValueGraphs.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/simonschoelly/SimpleValueGraphs.jl)
[![](https://img.shields.io/badge/chat-Zulip%23graphs-yellow)](https://julialang.zulipchat.com/#narrow/stream/228745-graphs)

SimpleValueGraphs is as [LightGraphs.jl](https://github.com/JuliaGraphs/LightGraphs.jl) compatible package for graphs with multiple, homogeneous vertex, edge and graph metadata. In particular it provides:
SimpleValueGraphs is a [Graphs.jl](https://github.com/JuliaGraphs/Graphs.jl) compatible package for graphs with multiple, homogeneous vertex, edge and graph metadata. In particular it provides:
- an abstract interface for graphs with metadata
- concrete implementations of mutable graphs with metadata

Expand All @@ -26,12 +26,12 @@ Compared to [MetaGraphs.jl](https://github.com/JuliaGraphs/MetaGraphs.jl) it has
```julia
using SimpleValueGraphs

using LightGraphs: smallgraph
using Graphs: smallgraph
using Plots
using GraphRecipes: graphplot
using Colors: RGB, Color

# Load a LightGraphs.SimpleGraph
# Load a Graphs.SimpleGraph
gs = smallgraph(:house)

# Convert to a ValGraph with vertex and edge values
Expand All @@ -58,25 +58,25 @@ graphplot(gv;

## Benchmarks

This is a comparison of running `LightGraphs.dijkstra_shortest_paths` on the [egonets-Facebook](https://snap.stanford.edu/data/egonets-Facebook.html) graph for multiple graph types.
This is a comparison of running `Graphs.dijkstra_shortest_paths` on the [egonets-Facebook](https://snap.stanford.edu/data/egonets-Facebook.html) graph for multiple graph types.

| graph type | time (ms) |
| ------------------------------------------------- | --------- |
| LightGraphs.SimpleGraph + Matrix weights | 6.5 |
| LightGraphs.SimpleGraph + SparseMatrixCSC weights | 11.4 |
| Graphs.SimpleGraph + Matrix weights | 6.5 |
| Graphs.SimpleGraph + SparseMatrixCSC weights | 11.4 |
| SimpleWeightedGraphs.SimpleWeightedGraph | 11.7 |
| MetaGraphs.MetaGraph | 141.9 |
| SimpleValueGraphs.ValGraph | 12.4 |

Currently a lot of LightGraphs algorithm do not optimally work with graphs that store edge metadata
Currently a lot of Graphs.jl algorithms do not optimally work with graphs that store edge metadata
internally. The next benchmark is an optimized version of the same algorithm that can be found
in `SimpleValueGraphs.Experimental.dijkstra_shortests_pasts`. Clearly, this is a huge improvement for
`ValGraph` and `SimpleWeightedGraph`.

| graph type | time (ms) |
| ------------------------------------------------- | --------- |
| LightGraphs.SimpleGraph + Matrix weights | 6.8 |
| LightGraphs.SimpleGraph + SparseMatrixCSC weights | 10.8 |
| Graphs.SimpleGraph + Matrix weights | 6.8 |
| Graphs.SimpleGraph + SparseMatrixCSC weights | 10.8 |
| SimpleWeightedGraphs.SimpleWeightedGraph | 2.9 |
| MetaGraphs.MetaGraph | 147.3 |
| SimpleValueGraphs.ValGraph | 3.1 |
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[deps]
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
LightGraphs = "093fc24a-ae57-5d10-9952-331d41423f4d"
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
MetaGraphs = "626554b9-1ddb-594c-aa3c-2596fe9399a5"
SNAPDatasets = "fc66bc1b-447b-53fc-8f09-bc9cfb0b0c10"
SimpleValueGraphs = "b43c691f-cac2-5415-8122-396fe16a49fc"
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/benchmarks.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using BenchmarkTools
using SimpleValueGraphs, LightGraphs
using SimpleValueGraphs, Graphs
using SimpleWeightedGraphs, MetaGraphs
using SNAPDatasets: loadsnap, snap_graphs
using SparseArrays
Expand Down
2 changes: 1 addition & 1 deletion docs/Project.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[deps]
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
LightGraphs = "093fc24a-ae57-5d10-9952-331d41423f4d"
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
SimpleValueGraphs = "b43c691f-cac2-5415-8122-396fe16a49fc"
2 changes: 1 addition & 1 deletion docs/make.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Documenter, SimpleValueGraphs

DocMeta.setdocmeta!(SimpleValueGraphs, :DocTestSetup, :(using SimpleValueGraphs, LightGraphs, Random); recursive=true)
DocMeta.setdocmeta!(SimpleValueGraphs, :DocTestSetup, :(using SimpleValueGraphs, Graphs, Random); recursive=true)
makedocs(
sitename = "SimpleValueGraphs.jl",
authors = "Simon Schoelly",
Expand Down
8 changes: 4 additions & 4 deletions docs/src/custom-valuegraph-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
While this package provides some concrete graph types, one is often interested in creating
their own graph types. This section will explain how one can create a custom graph type that
will work well with the methods from as package as well as the ones from
[LightGraphs.jl](https://github.com/JuliaGraphs/LightGraphs.jl)
[Graphs.jl](https://github.com/JuliaGraphs/Graphs.jl)

## The AbstractValGraph type

All value graphs should be subtypes of *AbstractValGraph* that has the signature

```julia
AbstractValGraph{V <: Integer, V_VALS, E_VALS, G_VALS} <: LightGraphs.AbstractGraph{V}
AbstractValGraph{V <: Integer, V_VALS, E_VALS, G_VALS} <: Graphs.AbstractGraph{V}
```
where the parameters have the following meaning:
- `V` is the type used for indexing vertices, called the *eltype* of the graph. Should
Expand All @@ -29,8 +29,8 @@ MyGraphType{W} <: AbstractValGraph{Int, Tuple{}, Tuple{Int, W}, Tuple{}}
is a graph type that has neither vertex nor graph values and two edge values,
one of them of type `Int` and the other of type `W`.

As a subtype of `LightGraphs.AbstractGraph`, an `AbstractValGraph` should implement the
[methods required by LightGraphs](https://github.com/JuliaGraphs/LightGraphs.jl) as well as some
As a subtype of `Graphs.AbstractGraph`, an `AbstractValGraph` should implement the
[methods required by Graphs.jl](https://github.com/JuliaGraphs/Graphs.jl) as well as some
other methods. Luckily a lot of the methods required for `AbstractGraph` already have a default
implementation, so the number of necessary methods is actually much shorter.

Expand Down
10 changes: 5 additions & 5 deletions docs/src/graphtypes.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ and three concrete implementations.
The abstract type `AbstractValGraph` denotes a graph that can have multiple vertex
and edge values. It has the signature
```julia
AbstractValGraph{V, V_VALS, E_VALS} <: LightGraphs.AbstractGraph{V}
AbstractValGraph{V, V_VALS, E_VALS} <: Graphs.AbstractGraph{V}
```
where the parameters have the following meaning:
- `V` is the type used for indexing vertices, called the *eltype* of the graph. Should
Expand Down Expand Up @@ -116,7 +116,7 @@ Graphs without any values an be created with
```julia
ValGraph{V = Int32}(n)
```
where `n` is the number of vertices. One notable difference to LightGraphs is that the
where `n` is the number of vertices. One notable difference to Graphs.jl is that the
eltype is not bases on the type of `n` but is always taken from the parameter `V`.

```julia
Expand Down Expand Up @@ -185,9 +185,9 @@ julia> g2 = ValDiGraph{Int8}(4;
edge value types: ()
```

#### Graph from LightGraphs SimpleGraphs
#### Graph from Graphs.jl SimpleGraphs

One can also initialize a graph from a LightGraphs `SimpleGraph` or `SimpleDiGraph`. If
One can also initialize a graph from a Graphs.jl `SimpleGraph` or `SimpleDiGraph`. If
edge values are specified (with the `edgeval_types` keyword) we also need an initializer for
edge values. We do that by using the `edgeval_init` keyword argument which can be
either `undef` or a function `(s, d) -> values` that takes a source and target vertex and
Expand All @@ -199,7 +199,7 @@ Furthermore, if the eltype is not specified as a parameter, it is taken from the
graph.

```julia
julia> using LightGraphs: smallgraph, PathDiGraph
julia> using Graphs.jl: smallgraph, PathDiGraph

julia> g_simple = smallgraph(:house)
{5, 6} undirected simple Int64 graph
Expand Down
8 changes: 4 additions & 4 deletions docs/src/matrices.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ that contain the topology of the graph.
## Adjacency matrices

The `adjacency_matrix` function creates an adjacency matrix from a value graph . In contrast
to LightGraphs, this is just a immutable *matrix view* of the graph, i.e if the graph
to Graphs.jl, this is just a immutable *matrix view* of the graph, i.e if the graph
changes, then so does this matrix. Therefore to get a mutable adjacency matrix one has to convert
it before to some other matrix type.

It is also possible to use the constructor `AdjacencyMatrix` to create a view of any
`LightGraphs.AbstractGraph`.
`Graphs.AbstractGraph`.

```julia
julia> g1 = SimpleValueGraphs.swissmetro_graph();
Expand Down Expand Up @@ -65,8 +65,8 @@ julia> ValMatrix(g, :b, nothing)
"xyz" nothing nothing
```
One can also use the `LightGraphs.weights` function to obtain this matrix. If the graph
does not have any edge values, this returns a `LightGraphs.DefaultDistance` instead.
One can also use the `Graphs.weights` function to obtain this matrix. If the graph
does not have any edge values, this returns a `Graphs.DefaultDistance` instead.
```julia
julia> weights(g1, :a)
Expand Down
2 changes: 1 addition & 1 deletion examples/Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[deps]
GraphRecipes = "bd48cda9-67a9-57be-86fa-5b3c104eda73"
LightGraphs = "093fc24a-ae57-5d10-9952-331d41423f4d"
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
Pluto = "c3e4b0f8-55cb-11ea-2926-15256bba5781"
SimpleValueGraphs = "b43c691f-cac2-5415-8122-396fe16a49fc"
12 changes: 6 additions & 6 deletions examples/custom-graph-type.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ begin
using SparseArrays

# We are going to test our new packages with some functions
# from LightGraphs and GraphsRecipes
using LightGraphs
# from Graphs.jl and GraphsRecipes
using Graphs
using GraphRecipes, Plots
pyplot() # gr backend has issues showing edge labels so we use pyplot instead
end
Expand Down Expand Up @@ -46,7 +46,7 @@ where
* `E_VALS` are the types of the edge values
* `G_VALS` are the types of the edge values
As this type of `LightGraphs.AbstractGraph`, it can also be used with LightGraphs functions as long as we correctly implement the `AbstractGraph` interface.
As this type of `Graphs.AbstractGraph`, it can also be used with Graphs functions as long as we correctly implement the `AbstractGraph` interface.
In our case the vertex type will be `Int` as this type is also used for indexing rows and columns in a matrix.
Expand All @@ -73,7 +73,7 @@ md"""
## Implementing the AbstractValGraph interface
To be able to do anything meaningful with that graph, we have to implement the interface for `LightGraphs.AbstractGraph` as well as the interface for `SimpleGraphs.AbstractValGraph`. Luckily, SimpleValueGraphs provides already some sensible defaults (that can be overriden for performance or other reasons) for a lot of LightGraphs functions so that what we will have to implement is less than is usually required by LightGraphs. Nevertheless we need to implement the following functions:
To be able to do anything meaningful with that graph, we have to implement the interface for `Graphs.AbstractGraph` as well as the interface for `SimpleGraphs.AbstractValGraph`. Luckily, SimpleValueGraphs provides already some sensible defaults (that can be overriden for performance or other reasons) for a lot of Graphs.jl functions so that what we will have to implement is less than is usually required by Graphs.jl. Nevertheless we need to implement the following functions:
* `nv(::GraphView)`
* `is_directed(::Type{<:GraphView})`
Expand Down Expand Up @@ -134,7 +134,7 @@ SimpleValueGraphs.get_edgeval(g::GraphView, u, v, key::Integer) = weight=g.matri
# ╔═╡ ed96451c-5596-11eb-1c39-8b28c0e661af
md"""
##### zero
This function is a bit of an anomaly and should in my opinion not be part of the LightGraphs interface. It is also rarely used in LightGraphs, so it might not be a very big issue to omit it. Nevertheless we implement it here for the sake of completeness.
This function is a bit of an anomaly and should in my opinion not be part of the Graphs.jl interface. It is also rarely used in Graphs.jl, so it might not be a very big issue to omit it. Nevertheless we implement it here for the sake of completeness.
`zero(G)` should create a graph with zero vertices, given a graph type. We do this here by trying to create a matrix of size (0, 0) and the correct matrix type, and then wrap a `GraphView` around it:
"""
Expand Down Expand Up @@ -235,7 +235,7 @@ md"""
### Shortest paths
Let's use some functions from LightGraphs to find the shortest path from vertex `2` to `5`:
Let's use some functions from Graphs.jl to find the shortest path from vertex `2` to `5`:
"""

# ╔═╡ 1147357a-2095-11eb-224e-61ad831db786
Expand Down
2 changes: 1 addition & 1 deletion src/Experimental.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This module contains experimental stuff that does not respect SemVer
"""
module Experimental

using LightGraphs: AbstractGraph, weights, nv, vertices, outneighbors, DijkstraState, DefaultDistance
using Graphs: AbstractGraph, weights, nv, vertices, outneighbors, DijkstraState, DefaultDistance
using DataStructures: PriorityQueue, dequeue!
using SimpleValueGraphs: ValGraph, edgevals_type, outedgevals
using SimpleWeightedGraphs: SimpleWeightedGraph, weighttype
Expand Down
11 changes: 5 additions & 6 deletions src/SimpleValueGraphs.jl
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
module SimpleValueGraphs

using LightGraphs: AbstractGraph, AbstractEdgeIter, AbstractEdge, num_self_loops
using Graphs: AbstractGraph, AbstractEdgeIter, AbstractEdge, num_self_loops

using LightGraphs.SimpleGraphs: AbstractSimpleGraph, AbstractSimpleEdge,
using Graphs.SimpleGraphs: AbstractSimpleGraph, AbstractSimpleEdge,
SimpleGraph, SimpleDiGraph, SimpleEdge, IsDirected

using Base: OneTo


import LightGraphs
const LG = LightGraphs
import Graphs

import LightGraphs:
import Graphs:
nv, ne, is_directed,
eltype, vertices, add_vertex!, rem_vertex!, has_vertex,
edgetype, edges, src, dst, reverse,
Expand Down Expand Up @@ -67,7 +66,7 @@ export
AdjacencyMatrix,
ValMatrix,

# overridden methods from LightGraphs
# overridden methods from Graphs.jl
nv,
ne,
is_directed,
Expand Down
30 changes: 15 additions & 15 deletions src/abstractvaluegraph.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ ZeroGraphValGraph{V, V_VALS, E_VALS} = AbstractValGraph{V, V_VALS, E_VALS, <: Ab
# eltype
# ------------------------------------------------------

LG.eltype(::Type{<:AbstractValGraph{V}}) where {V} = V
Graphs.eltype(::Type{<:AbstractValGraph{V}}) where {V} = V

# This should not be necessary, as Base implements `eltype(x) = eltype(typeof(x))`
# but unfortunately LightGraphs redefines `eltype(::AbstractGraph)` as not defined
LG.eltype(g::AbstractValGraph) = eltype(typeof(g))
# but unfortunately Graphs.jl redefines `eltype(::AbstractGraph)` as not defined
Graphs.eltype(g::AbstractValGraph) = eltype(typeof(g))


# ------------------------------------------------------
Expand Down Expand Up @@ -214,30 +214,30 @@ end


# ======================================================
# Partial default implementation of LightGraphs interface
# Partial default implementation of Graphs.jl interface
# ======================================================

LG.vertices(g::AbstractValGraph) = OneTo{eltype(g)}(nv(g))
Graphs.vertices(g::AbstractValGraph) = OneTo{eltype(g)}(nv(g))

LG.has_vertex(g::AbstractValGraph, v) = v vertices(g)
Graphs.has_vertex(g::AbstractValGraph, v) = v vertices(g)

"""
edges(g::AbstractValGraph[, key])
Return the edges of `g`. By default add no edge values
but when `key=:` then add edge values.
"""
LG.edges(g::AbstractValGraph, key=nothing) = ValEdgeIter(g, key)
Graphs.edges(g::AbstractValGraph, key=nothing) = ValEdgeIter(g, key)

LG.edgetype(g::AbstractValGraph) = eltype(edges(g))
Graphs.edgetype(g::AbstractValGraph) = eltype(edges(g))

LG.ne(g::AbstractValGraph) = length(edges(g))
Graphs.ne(g::AbstractValGraph) = length(edges(g))

# TODO a Base.Generator would be better here, but it causes problems with sort. Maybe
# add a custom iterator
LG.outneighbors(g::AbstractValGraph, u) = [v for v vertices(g) if has_edge(g, u, v)]
Graphs.outneighbors(g::AbstractValGraph, u) = [v for v vertices(g) if has_edge(g, u, v)]

LG.inneighbors(g::AbstractValGraph, v) = is_directed(g) ? [u for u vertices(g) if has_edge(g, u, v)] : outneighbors(g, v)
Graphs.inneighbors(g::AbstractValGraph, v) = is_directed(g) ? [u for u vertices(g) if has_edge(g, u, v)] : outneighbors(g, v)


# ======================================================
Expand All @@ -264,9 +264,9 @@ If the edge already exists, return `false` but still change the edge values.
"""
function add_edge! end

LG.add_edge!(g::ZeroEdgeValGraph, s, d) = add_edge!(g, s, d, ())
Graphs.add_edge!(g::ZeroEdgeValGraph, s, d) = add_edge!(g, s, d, ())

LG.add_edge!(g::OneEdgeValGraph, s, d; val) = add_edge!(g, s, d, tuple(val))
Graphs.add_edge!(g::OneEdgeValGraph, s, d; val) = add_edge!(g, s, d, tuple(val))


# -----------------------------------------------------
Expand All @@ -288,9 +288,9 @@ Return `true` if the vertex was added successfully, otherwise return `false`.
"""
function add_vertex! end

LG.add_vertex!(g::ZeroVertexValGraph) = add_vertex!(g, ())
Graphs.add_vertex!(g::ZeroVertexValGraph) = add_vertex!(g, ())

LG.add_vertex!(g::OneVertexValGraph; val) = add_vertex!(g, tuple(val))
Graphs.add_vertex!(g::OneVertexValGraph; val) = add_vertex!(g, tuple(val))


# ======================================================
Expand Down
Loading

2 comments on commit 065eebd

@simonschoelly
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/47108

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.4.0 -m "<description of version>" 065eebdcc1860cdffbb526d35807496400e2e0bc
git push origin v0.4.0

Please sign in to comment.