Skip to content

Commit 4abefba

Browse files
authored
Abstract constructors (#59)
* Fix special graph deprecations * simplify constructors, use abstract graphs * simplify digraph constructor * add getindex on graph weights * throw method error when getting property not weight * lower-cost constructor * more tests on constructors and type parameters * add test for different edge type * copy constructor to avoid shared matrix mutations * copy constructor copies the matrix * construction from directed to undirected and reverse * additional tests for coverage * reduce number of test types * additional constructor tests * additional constructor tests
1 parent 04989c5 commit 4abefba

File tree

4 files changed

+193
-69
lines changed

4 files changed

+193
-69
lines changed

src/SimpleWeightedGraphs.jl

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,6 @@ end
5757
convert(::Type{SparseMatrixCSC{T, U}}, g::AbstractSimpleWeightedGraph) where T<:Real where U<:Integer = SparseMatrixCSC{T, U}(g.weights)
5858

5959

60-
61-
62-
6360
### INTERFACE
6461

6562
nv(g::AbstractSimpleWeightedGraph{T, U}) where T where U = T(size(g.weights, 1))
@@ -87,7 +84,7 @@ end
8784

8885
has_vertex(g::AbstractSimpleWeightedGraph, v::Integer) = v in vertices(g)
8986

90-
function rem_edge!(g::AbstractSimpleWeightedGraph{T, U}, u::Integer, v::Integer) where T where U
87+
function rem_edge!(g::AbstractSimpleWeightedGraph{T, U}, u::Integer, v::Integer) where {T, U}
9188
rem_edge!(g, edgetype(g)(T(u), T(v), one(U)))
9289
end
9390

@@ -139,12 +136,15 @@ include("persistence.jl")
139136
const WGraph = SimpleWeightedGraph
140137
const WDiGraph = SimpleWeightedDiGraph
141138

142-
SimpleWeightedDiGraph(g::SimpleWeightedGraph) = SimpleWeightedDiGraph(g.weights)
143-
SimpleWeightedDiGraph{T,U}(g::SimpleWeightedGraph) where T<:Integer where U<:Real =
144-
SimpleWeightedDiGraph(SparseMatrixCSC{U, T}(g.weights))
139+
SimpleWeightedDiGraph(g::SimpleWeightedGraph) = SimpleWeightedDiGraph(copy(g.weights))
140+
function SimpleWeightedDiGraph{T, U}(g::SimpleWeightedGraph) where {T<:Integer, U<:Real}
141+
return SimpleWeightedDiGraph(SparseMatrixCSC{U, T}(copy(g.weights)))
142+
end
145143

146144
SimpleWeightedGraph(g::SimpleWeightedDiGraph) = SimpleWeightedGraph(g.weights .+ g.weights')
147-
SimpleWeightedGraph{T,U}(g::SimpleWeightedDiGraph) where T<:Integer where U<:Real =
148-
SimpleWeightedGraph(SparseMatrixCSC{U, T}(g.weights .+ g.weights'))
145+
146+
function SimpleWeightedGraph{T, U}(g::SimpleWeightedDiGraph) where {T<:Integer, U<:Real}
147+
return SimpleWeightedGraph(SparseMatrixCSC{U, T}(g.weights .+ g.weights'))
148+
end
149149

150150
end # module

src/simpleweighteddigraph.jl

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,38 +5,42 @@ A type representing a directed graph with weights of type `U`.
55
66
Note that adding or removing vertices or edges is not particularly performant;
77
see MetaGraphs.jl for possible alternatives.
8+
9+
The primary constructor takes a sparse adjacency matrix as input, of which
10+
the transpose is stored.
11+
To provide the transpose directly, the keyword argument `permute=true` can be used.
812
"""
913
mutable struct SimpleWeightedDiGraph{T<:Integer, U<:Real} <: AbstractSimpleWeightedGraph{T, U}
1014
weights::SparseMatrixCSC{U, T} # indexed by [dst, src]
11-
function SimpleWeightedDiGraph{T, U}(adjmx::SparseMatrixCSC{U,T}; permute=true) where T <: Integer where U <: Real
12-
dima,dimb = size(adjmx)
13-
isequal(dima,dimb) || error("Adjacency / distance matrices must be square")
15+
function SimpleWeightedDiGraph{T, U}(adjmx::SparseMatrixCSC{U,T}; permute=true) where {T <: Integer, U <: Real}
16+
dima, dimb = size(adjmx)
17+
isequal(dima, dimb) || error("Adjacency / distance matrices must be square")
1418
permute ? new{T, U}(permutedims(adjmx)) : new{T, U}(adjmx)
1519
end
1620

1721
end
1822

19-
SimpleWeightedDiGraph{T}(adjmx::SparseMatrixCSC{U, T}; permute=true) where T<:Integer where U<:Real =
23+
SimpleWeightedDiGraph{T}(adjmx::SparseMatrixCSC{U, T}; permute=true) where {T<:Integer, U<:Real} =
2024
SimpleWeightedDiGraph{T, U}(adjmx; permute=permute)
2125

22-
SimpleWeightedDiGraph(adjmx::SparseMatrixCSC{U, T}; permute=true) where T<:Integer where U<:Real =
26+
SimpleWeightedDiGraph(adjmx::SparseMatrixCSC{U, T}; permute=true) where {T<:Integer, U<:Real} =
2327
SimpleWeightedDiGraph{T, U}(adjmx; permute=permute)
2428

25-
SimpleWeightedDiGraph(m::AbstractMatrix{U}) where U <: Real =
29+
SimpleWeightedDiGraph(m::AbstractMatrix{U}) where {U <: Real} =
2630
SimpleWeightedDiGraph{Int, U}(SparseMatrixCSC{U, Int}(m))
27-
SimpleWeightedDiGraph{T}(m::AbstractMatrix{U}) where T<:Integer where U<:Real =
31+
SimpleWeightedDiGraph{T}(m::AbstractMatrix{U}) where {T <: Integer, U <: Real} =
2832
SimpleWeightedDiGraph{T, U}(SparseMatrixCSC{U, T}(m))
29-
SimpleWeightedDiGraph{T, U}(m::AbstractMatrix) where T<:Integer where U<:Real =
33+
SimpleWeightedDiGraph{T, U}(m::AbstractMatrix) where {T <: Integer, U <: Real} =
3034
SimpleWeightedDiGraph{T, U}(SparseMatrixCSC{U, T}(m))
3135

32-
SimpleWeightedDiGraph(g::SimpleWeightedDiGraph) = SimpleWeightedDiGraph(g.weights, false)
33-
SimpleWeightedDiGraph{T,U}(g::SimpleWeightedDiGraph) where T<:Integer where U<:Real =
34-
SimpleWeightedDiGraph(SparseMatrixCSC{U, T}(g.weights); permute=false)
36+
SimpleWeightedDiGraph(g::SimpleWeightedDiGraph) = SimpleWeightedDiGraph(copy(g.weights), permute=false)
37+
SimpleWeightedDiGraph{T,U}(g::SimpleWeightedDiGraph) where {T <: Integer, U <: Real} =
38+
SimpleWeightedDiGraph(SparseMatrixCSC{U, T}(copy(g.weights)), permute=false)
3539

3640

3741
ne(g::SimpleWeightedDiGraph) = nnz(g.weights)
3842

39-
function SimpleWeightedDiGraph{T,U}(n::Integer = 0) where T<:Integer where U<:Real
43+
function SimpleWeightedDiGraph{T,U}(n::Integer = 0) where {T<:Integer, U<:Real}
4044
weights = spzeros(U, T, T(n), T(n))
4145
return SimpleWeightedDiGraph{T, U}(weights)
4246
end
@@ -51,24 +55,27 @@ SimpleWeightedDiGraph(n::T) where T<:Integer = SimpleWeightedDiGraph{T, Float64}
5155
SimpleWeightedDiGraph(::Type{T}) where T<:Integer = SimpleWeightedDiGraph{T, Float64}(zero(T))
5256

5357
# Graph(UInt8, Float32)
54-
SimpleWeightedDiGraph(::Type{T}, ::Type{U}) where T<:Integer where U<:Real = SimpleWeightedDiGraph{U, T}(zero(T))
58+
SimpleWeightedDiGraph(::Type{T}, ::Type{U}) where {T <: Integer, U <: Real} = SimpleWeightedDiGraph{T, U}(zero(T))
5559

56-
# DiGraph(AbstractSimpleGraph)
57-
function SimpleWeightedDiGraph(g::LightGraphs.SimpleGraphs.AbstractSimpleGraph, ::Type{U}=Float64) where U <: Real
58-
T = eltype(g)
60+
# DiGraph(AbstractGraph, ::Type{U})
61+
function SimpleWeightedDiGraph(g::LightGraphs.AbstractGraph{T}, ::Type{U}=Float64) where {U <: Real, T}
5962
return SimpleWeightedDiGraph{T}(adjacency_matrix(g, U))
6063
end
6164

62-
# DiGraph(AbstractSimpleGraph, defaultweight)
63-
function SimpleWeightedDiGraph(g::LightGraphs.SimpleGraphs.AbstractSimpleGraph, x::U) where U <: Real
64-
T = eltype(g)
65-
return SimpleWeightedDiGraph{T, U}(x.*adjacency_matrix(g, U))
65+
"""
66+
DiGraph(g::AbstractGraph, x::Real)
67+
68+
Construct a weighted digraph from other graph `g` with initial weight `x`.
69+
"""
70+
function SimpleWeightedDiGraph(g::LightGraphs.AbstractGraph{T}, x::U) where {U <: Real, T}
71+
m = adjacency_matrix(g, U)'
72+
return SimpleWeightedDiGraph{T, U}(x .* m, permute=false)
6673
end
6774

6875
# DiGraph(srcs, dsts, weights)
6976
function SimpleWeightedDiGraph(i::AbstractVector{T}, j::AbstractVector{T}, v::AbstractVector{U}; combine = +) where T<:Integer where U<:Real
7077
m = max(maximum(j), maximum(i))
71-
SimpleWeightedDiGraph{T, U}(sparse(j, i, v, m, m, combine); permute=false)
78+
SimpleWeightedDiGraph{T, U}(sparse(j, i, v, m, m, combine), permute=false)
7279
end
7380

7481
LightGraphs.SimpleDiGraph(g::SimpleWeightedDiGraph) = SimpleDiGraph(g.weights)
@@ -110,12 +117,22 @@ copy(g::SimpleWeightedDiGraph) = SimpleWeightedDiGraph(copy(g.weights'))
110117

111118
==(g::SimpleWeightedDiGraph, h::SimpleWeightedDiGraph) = g.weights == h.weights
112119

120+
is_directed(::Type{<:SimpleWeightedDiGraph}) = true
113121

114122
"""
115-
is_directed(g)
123+
g[e::SimpleWeightedGraph, Val{:weight}()]
116124
117-
Return `true` if `g` is a directed graph.
125+
Equivalent to g[src(e), dst(e)].
118126
"""
119-
is_directed(::Type{SimpleWeightedDiGraph}) = true
120-
is_directed(::Type{SimpleWeightedDiGraph{T,U}}) where T where U = true
121-
is_directed(g::SimpleWeightedDiGraph) = true
127+
function Base.getindex(g::SimpleWeightedDiGraph{T, U}, e::AbstractEdge, ::Val{:weight}) where {T, U, S}
128+
return g.weights[dst(e), src(e)]
129+
end
130+
131+
"""
132+
g[e::SimpleWeightedGraph, i::Integer, j::Integer, Val{:weight}()]
133+
134+
Return the weight of edge (i, j).
135+
"""
136+
function Base.getindex(g::SimpleWeightedDiGraph{T, U}, i::Integer, j::Integer, ::Val{:weight}) where {T, U, S}
137+
return g.weights[j, i]
138+
end

src/simpleweightedgraph.jl

Lines changed: 44 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11

2-
32
"""
43
SimpleWeightedGraph{T, U}
54
@@ -10,7 +9,7 @@ see MetaGraphs.jl for possible alternatives.
109
"""
1110
mutable struct SimpleWeightedGraph{T<:Integer, U<:Real} <: AbstractSimpleWeightedGraph{T, U}
1211
weights::SparseMatrixCSC{U,T}
13-
function SimpleWeightedGraph{T, U}(adjmx::SparseMatrixCSC{U, T}) where T<:Integer where U<:Real
12+
function SimpleWeightedGraph{T, U}(adjmx::SparseMatrixCSC{U, T}) where {T<:Integer, U<:Real}
1413
dima,dimb = size(adjmx)
1514
isequal(dima,dimb) || error("Adjacency / distance matrices must be square")
1615
issymmetric(adjmx) || error("Adjacency / distance matrices must be symmetric")
@@ -21,32 +20,31 @@ end
2120

2221
ne(g::SimpleWeightedGraph) = nnz(g.weights) ÷ 2
2322

24-
SimpleWeightedGraph{T}(adjmx::SparseMatrixCSC{U, T}) where T<:Integer where U<:Real =
23+
SimpleWeightedGraph{T}(adjmx::SparseMatrixCSC{U, T}) where {T <: Integer, U <: Real} =
2524
SimpleWeightedGraph{T, U}(adjmx)
2625

27-
SimpleWeightedGraph(adjmx::SparseMatrixCSC{U, T}) where T<:Integer where U<:Real =
26+
SimpleWeightedGraph(adjmx::SparseMatrixCSC{U, T}) where {T <: Integer, U <: Real} =
2827
SimpleWeightedGraph{T, U}(adjmx)
2928

30-
SimpleWeightedGraph(m::AbstractMatrix{U}) where U <: Real =
29+
SimpleWeightedGraph(m::AbstractMatrix{U}) where {U <: Real} =
3130
SimpleWeightedGraph{Int, U}(SparseMatrixCSC{U, Int}(m))
3231
SimpleWeightedGraph{T}(m::AbstractMatrix{U}) where T<:Integer where U<:Real =
3332
SimpleWeightedGraph{T, U}(SparseMatrixCSC{U, T}(m))
3433
SimpleWeightedGraph{T, U}(m::AbstractMatrix) where T<:Integer where U<:Real =
3534
SimpleWeightedGraph{T, U}(SparseMatrixCSC{U, T}(m))
3635

3736

38-
SimpleWeightedGraph(g::SimpleWeightedGraph) = SimpleWeightedGraph(g.weights)
39-
SimpleWeightedGraph{T,U}(g::SimpleWeightedGraph) where T<:Integer where U<:Real =
40-
SimpleWeightedGraph(SparseMatrixCSC{U, T}(g.weights))
41-
37+
SimpleWeightedGraph(g::SimpleWeightedGraph) = SimpleWeightedGraph(copy(g.weights))
38+
function SimpleWeightedGraph{T,U}(g::SimpleWeightedGraph) where {T<:Integer, U<:Real}
39+
return SimpleWeightedGraph(SparseMatrixCSC{U, T}(copy(g.weights)))
40+
end
4241

4342
# Graph{UInt8}(6), Graph{Int16}(7), Graph{UInt8}()
4443
function (::Type{SimpleWeightedGraph{T, U}})(n::Integer = 0) where T<:Integer where U<:Real
4544
weights = spzeros(U, T, T(n), T(n))
4645
return SimpleWeightedGraph{T, U}(weights)
4746
end
4847

49-
5048
# Graph()
5149
SimpleWeightedGraph() = SimpleWeightedGraph(Matrix{Float64}(undef, 0, 0))
5250

@@ -57,39 +55,51 @@ SimpleWeightedGraph(n::T) where T<:Integer = SimpleWeightedGraph{T, Float64}(n)
5755
SimpleWeightedGraph(::Type{T}) where T<:Integer = SimpleWeightedGraph{T, Float64}(zero(T))
5856

5957
# Graph(UInt8, Float32)
60-
SimpleWeightedGraph(::Type{T}, ::Type{U}) where T<:Integer where U<:Real = SimpleWeightedGraph{U, T}(zero(T))
58+
SimpleWeightedGraph(::Type{T}, ::Type{U}) where {T<:Integer, U<:Real} = SimpleWeightedGraph{T, U}(zero(T))
6159

6260
# Graph(SimpleGraph)
63-
SimpleWeightedGraph(g::LightGraphs.SimpleGraphs.SimpleGraph{T}, ::Type{U}=Float64) where T <: Integer where U <: Real =
64-
SimpleWeightedGraph{T, U}(adjacency_matrix(g, U))
6561

66-
# Graph(SimpleDiGraph)
67-
SimpleWeightedGraph(g::LightGraphs.SimpleGraphs.SimpleDiGraph{T}, ::Type{U}=Float64) where T <: Integer where U <: Real =
68-
SimpleWeightedGraph{T, U}(adjacency_matrix(LightGraphs.SimpleGraphs.SimpleGraph(g), U))
69-
70-
# Graph(SimpleGraph, defaultweight)
71-
SimpleWeightedGraph(g::LightGraphs.SimpleGraphs.SimpleGraph{T}, x::U) where T <: Integer where U <: Real =
72-
SimpleWeightedGraph{T, U}(x.*adjacency_matrix(g, U))
62+
function SimpleWeightedGraph(g::LightGraphs.AbstractGraph{T}, ::Type{U}=Float64) where {T <: Integer, U <: Real}
63+
adj_matrix = if LightGraphs.is_directed(g)
64+
# TODO abstract function instead of SimpleGraph constructor
65+
adjacency_matrix(LightGraphs.SimpleGraphs.SimpleGraph(g), U)
66+
else
67+
adjacency_matrix(g, U)
68+
end
69+
return SimpleWeightedGraph{T, U}(adj_matrix)
70+
end
7371

74-
# Graph(SimpleDiGraph, defaultweight)
75-
SimpleWeightedGraph(g::LightGraphs.SimpleGraphs.SimpleDiGraph{T}, x::U) where T <: Integer where U <: Real =
76-
SimpleWeightedGraph{T, U}(x.*adjacency_matrix(LightGraphs.SimpleGraphs.SimpleGraph(g), U))
72+
function SimpleWeightedGraph(g::LightGraphs.AbstractGraph{T}, x::U) where {T <: Integer, U <: Real}
73+
adj_matrix = if LightGraphs.is_directed(g)
74+
# TODO abstract function instead of SimpleGraph constructor
75+
adjacency_matrix(LightGraphs.SimpleGraphs.SimpleGraph(g), U)
76+
else
77+
adjacency_matrix(g, U)
78+
end
79+
return SimpleWeightedGraph{T, U}(x .* adj_matrix)
80+
end
7781

7882
# SimpleWeightedGraph{T, U}(SimpleGraph)
79-
function (::Type{SimpleWeightedGraph{T, U}})(g::LightGraphs.SimpleGraphs.SimpleGraph) where T<:Integer where U <: Real
80-
SimpleWeightedGraph{T, U}(adjacency_matrix(LightGraphs.SimpleGraphs.SimpleGraph{T}(g), U))
83+
function (::Type{SimpleWeightedGraph{T, U}})(g::LightGraphs.AbstractGraph) where {T<:Integer, U <: Real}
84+
adj_matrix = if LightGraphs.is_directed(g)
85+
# TODO abstract function instead of SimpleGraph constructor
86+
adjacency_matrix(LightGraphs.SimpleGraphs.SimpleGraph{T}(g), U)
87+
else
88+
adjacency_matrix(g, U)
89+
end
90+
return SimpleWeightedGraph{T, U}(adj_matrix)
8191
end
8292

8393
# Graph(srcs, dsts, weights)
84-
function SimpleWeightedGraph(i::AbstractVector{T}, j::AbstractVector{T}, v::AbstractVector{U}; combine = +) where T<:Integer where U<:Real
94+
function SimpleWeightedGraph(i::AbstractVector{T}, j::AbstractVector{T}, v::AbstractVector{U}; combine = +) where {T<:Integer, U<:Real}
8595
m = max(maximum(i), maximum(j))
8696
s = sparse(vcat(i,j), vcat(j,i), vcat(v,v), m, m, combine)
8797
SimpleWeightedGraph{T, U}(s)
8898
end
8999

90100
LightGraphs.SimpleGraph(g::SimpleWeightedGraph) = SimpleGraph(g.weights)
91101

92-
edgetype(::SimpleWeightedGraph{T, U}) where T<:Integer where U<:Real= SimpleWeightedGraphEdge{T,U}
102+
edgetype(::SimpleWeightedGraph{T, U}) where {T<:Integer, U<:Real} = SimpleWeightedGraphEdge{T,U}
93103

94104
edges(g::SimpleWeightedGraph) = (SimpleWeightedEdge(x[1], x[2], x[3]) for x in zip(findnz(triu(g.weights))...))
95105
weights(g::SimpleWeightedGraph) = g.weights
@@ -125,12 +135,12 @@ end
125135

126136
==(g::SimpleWeightedGraph, h::SimpleWeightedGraph) = g.weights == h.weights
127137

138+
is_directed(::Type{<:SimpleWeightedGraph}) = false
128139

129-
"""
130-
is_directed(g)
140+
function Base.getindex(g::SimpleWeightedGraph{T, U}, e::AbstractEdge, ::Val{:weight}) where {T, U, S}
141+
return g.weights[src(e), dst(e)]
142+
end
131143

132-
Return `true` if `g` is a directed graph.
133-
"""
134-
is_directed(::Type{SimpleWeightedGraph}) = false
135-
is_directed(::Type{SimpleWeightedGraph{T, U}}) where T where U = false
136-
is_directed(g::SimpleWeightedGraph) = false
144+
function Base.getindex(g::SimpleWeightedGraph{T, U}, i::Integer, j::Integer, ::Val{:weight}) where {T, U, S}
145+
return g.weights[i, j]
146+
end

0 commit comments

Comments
 (0)