-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
* Add a function transpose_graph * Use the formatter on src/graph.jl * Remove old code * Add unit tests for transpose_graph * Clean up transpose * No docs * Docs * Codecov --------- Co-authored-by: Alexis Montoison <[email protected]>
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,6 +35,7 @@ using SparseArrays: | |
SparseMatrixCSC, | ||
dropzeros, | ||
dropzeros!, | ||
ftranspose!, | ||
nnz, | ||
nonzeros, | ||
nzrange, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,8 +19,8 @@ The vertices are colored in a greedy fashion, following the `order` supplied. | |
function partial_distance2_coloring( | ||
bg::BipartiteGraph, ::Val{side}, order::AbstractOrder | ||
) where {side} | ||
color = Vector{Int}(undef, length(bg, Val(side))) | ||
forbidden_colors = Vector{Int}(undef, length(bg, Val(side))) | ||
color = Vector{Int}(undef, nb_vertices(bg, Val(side))) | ||
forbidden_colors = Vector{Int}(undef, nb_vertices(bg, Val(side))) | ||
vertices_in_order = vertices(bg, Val(side), order) | ||
partial_distance2_coloring!(color, forbidden_colors, bg, Val(side), vertices_in_order) | ||
return color | ||
|
@@ -76,11 +76,11 @@ The vertices are colored in a greedy fashion, following the `order` supplied. | |
""" | ||
function star_coloring(g::Graph{false}, order::AbstractOrder) | ||
# Initialize data structures | ||
nvertices = length(g) | ||
color = zeros(Int, nvertices) | ||
forbidden_colors = zeros(Int, nvertices) | ||
first_neighbor = fill((0, 0), nvertices) # at first no neighbors have been encountered | ||
treated = zeros(Int, nvertices) | ||
n = nb_vertices(g) | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong. |
||
color = zeros(Int, n) | ||
forbidden_colors = zeros(Int, n) | ||
first_neighbor = fill((0, 0), n) # at first no neighbors have been encountered | ||
treated = zeros(Int, n) | ||
star = Dict{Tuple{Int,Int},Int}() | ||
hub = Int[] | ||
vertices_in_order = vertices(g, order) | ||
|
@@ -269,12 +269,12 @@ The vertices are colored in a greedy fashion, following the `order` supplied. | |
""" | ||
function acyclic_coloring(g::Graph{false}, order::AbstractOrder) | ||
# Initialize data structures | ||
nvertices = length(g) | ||
nedges = nnz(g) ÷ 2 # symmetric sparse matrix with empty diagonal | ||
This comment has been minimized.
Sorry, something went wrong.
amontoison
Author
Collaborator
|
||
color = zeros(Int, nvertices) | ||
forbidden_colors = zeros(Int, nvertices) | ||
first_neighbor = fill((0, 0), nvertices) # at first no neighbors have been encountered | ||
first_visit_to_tree = fill((0, 0), nedges) | ||
n = nb_vertices(g) | ||
e = nb_edges(g) ÷ 2 # symmetric sparse matrix with empty diagonal | ||
color = zeros(Int, n) | ||
forbidden_colors = zeros(Int, n) | ||
first_neighbor = fill((0, 0), n) # at first no neighbors have been encountered | ||
first_visit_to_tree = fill((0, 0), e) | ||
forest = DisjointSets{Tuple{Int,Int}}() | ||
vertices_in_order = vertices(g, order) | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,63 +3,72 @@ | |
""" | ||
Graph{loops,T} | ||
Undirected graph structure stored in Compressed Sparse Column (CSC) format. | ||
Note that the underyling CSC may not be square, so the term "graph" is slightly abusive. | ||
Store a sparse matrix (in CSC) without its values, keeping only the pattern of nonzeros. | ||
It can be seen as a graph mapping columns to rows, hence the name `Graph`. | ||
The type parameter `loops` must be set to: | ||
- `true` if coefficients `(i, i)` present in the CSC are counted as edges in the graph (e.g. for each half of a bipartite graph) | ||
- `false` otherwise (e.g. for an adjacency graph) | ||
# Fields | ||
- `colptr::Vector{T}`: same as for `SparseMatrixCSC` | ||
- `rowval::Vector{T}`: same as for `SparseMatrixCSC` | ||
Copied from `SparseMatrixCSC`: | ||
- `m::Int`: number of rows | ||
- `n::Int`: number of columns | ||
- `colptr::Vector{T}`: column `j` is in `colptr[j]:(colptr[j+1]-1)` | ||
This comment has been minimized.
Sorry, something went wrong.
amontoison
Author
Collaborator
|
||
- `rowval::Vector{T}`: row indices of stored values | ||
""" | ||
struct Graph{loops,T<:Integer} | ||
m::Int | ||
This comment has been minimized.
Sorry, something went wrong.
amontoison
Author
Collaborator
|
||
n::Int | ||
colptr::Vector{T} | ||
rowval::Vector{T} | ||
end | ||
|
||
function Graph{loops}(S::SparseMatrixCSC{Tv,Ti}) where {loops,Tv,Ti} | ||
return Graph{loops,Ti}(S.colptr, S.rowval) | ||
return Graph{loops,Ti}(S.m, S.n, S.colptr, S.rowval) | ||
end | ||
|
||
Base.length(g::Graph) = length(g.colptr) - 1 | ||
SparseArrays.nnz(g::Graph) = length(g.rowval) | ||
This comment has been minimized.
Sorry, something went wrong.
amontoison
Author
Collaborator
|
||
SparseArrays.rowvals(g::Graph) = g.rowval | ||
SparseArrays.nzrange(g::Graph, j::Integer) = g.colptr[j]:(g.colptr[j + 1] - 1) | ||
|
||
nb_vertices(g::Graph) = g.n | ||
vertices(g::Graph) = 1:nb_vertices(g) | ||
|
||
SparseArrays.nnz(g::Graph{true}) = length(g.rowval) | ||
nb_edges(g::Graph{true}) = length(g.rowval) | ||
|
||
function SparseArrays.nnz(g::Graph{false}) | ||
n = 0 | ||
for j in 1:(length(g.colptr) - 1) | ||
for k in g.colptr[j]:(g.colptr[j + 1] - 1) | ||
i = g.rowval[k] | ||
function nb_edges(g::Graph{false}) | ||
e = 0 | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong. |
||
for j in vertices(g) | ||
for k in nzrange(g, j) | ||
i = rowvals(g)[k] | ||
if i != j | ||
n += 1 | ||
e += 1 | ||
end | ||
end | ||
end | ||
return n | ||
return e | ||
end | ||
|
||
vertices(g::Graph) = 1:length(g) | ||
|
||
function neighbors(g::Graph{true}, v::Integer) | ||
return view(g.rowval, g.colptr[v]:(g.colptr[v + 1] - 1)) | ||
return view(rowvals(g), nzrange(g, v)) | ||
end | ||
|
||
function neighbors(g::Graph{false}, v::Integer) | ||
neighbors_with_loops = view(g.rowval, g.colptr[v]:(g.colptr[v + 1] - 1)) | ||
neighbors_with_loops = view(rowvals(g), nzrange(g, v)) | ||
return Iterators.filter(!=(v), neighbors_with_loops) # TODO: optimize | ||
end | ||
|
||
function degree(g::Graph{true}, v::Integer) | ||
return length(g.colptr[v]:(g.colptr[v + 1] - 1)) | ||
return length(nzrange(g, v)) | ||
end | ||
|
||
function degree(g::Graph{false}, v::Integer) | ||
d = length(g.colptr[v]:(g.colptr[v + 1] - 1)) | ||
for k in g.colptr[v]:(g.colptr[v + 1] - 1) | ||
if g.rowval[k] == v | ||
d = length(nzrange(g, v)) | ||
for k in nzrange(g, v) | ||
if rowvals(g)[k] == v | ||
d -= 1 | ||
end | ||
end | ||
|
@@ -69,6 +78,17 @@ end | |
maximum_degree(g::Graph) = maximum(Base.Fix1(degree, g), vertices(g)) | ||
minimum_degree(g::Graph) = minimum(Base.Fix1(degree, g), vertices(g)) | ||
|
||
""" | ||
transpose(g::Graph) | ||
Return a [`Graph`](@ref) corresponding to the transpose of (the underlying matrix of) `g`. | ||
""" | ||
function Base.transpose(g::Graph{loops,T}) where {loops,T} | ||
S = SparseMatrixCSC{T,T}(g.m, g.n, g.colptr, g.rowval, g.rowval) | ||
Sᵀ = convert(SparseMatrixCSC, transpose(S)) # TODO: use ftranspose! without segfault? | ||
return Graph{loops}(Sᵀ) | ||
end | ||
|
||
## Bipartite graph | ||
|
||
""" | ||
|
@@ -88,16 +108,17 @@ struct BipartiteGraph{T<:Integer} | |
g2::Graph{true,T} | ||
end | ||
|
||
Base.length(bg::BipartiteGraph, ::Val{1}) = length(bg.g1) | ||
Base.length(bg::BipartiteGraph, ::Val{2}) = length(bg.g2) | ||
SparseArrays.nnz(bg::BipartiteGraph) = nnz(bg.g1) | ||
nb_vertices(bg::BipartiteGraph, ::Val{1}) = nb_vertices(bg.g1) | ||
nb_vertices(bg::BipartiteGraph, ::Val{2}) = nb_vertices(bg.g2) | ||
|
||
nb_edges(bg::BipartiteGraph) = nb_edges(bg.g1) | ||
|
||
""" | ||
vertices(bg::BipartiteGraph, Val(side)) | ||
Return the list of vertices of `bg` from the specified `side` as a range `1:n`. | ||
""" | ||
vertices(bg::BipartiteGraph, ::Val{side}) where {side} = 1:length(bg, Val(side)) | ||
vertices(bg::BipartiteGraph, ::Val{side}) where {side} = 1:nb_vertices(bg, Val(side)) | ||
|
||
""" | ||
neighbors(bg::BipartiteGraph, Val(side), v::Integer) | ||
|
@@ -159,7 +180,7 @@ function bipartite_graph(A::SparseMatrixCSC; symmetric_pattern::Bool=false) | |
checksquare(A) # proxy for checking full symmetry | ||
g1 = g2 | ||
else | ||
g1 = Graph{true}(convert(SparseMatrixCSC, transpose(A))) # rows to columns | ||
g1 = transpose(g2) # rows to columns | ||
end | ||
return BipartiteGraph(g1, g2) | ||
end |
2 comments
on commit 939c9df
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gdalle I added 6 comments.
Update: I see why you need g.n
, g.m
and nnz(g)
now for transpose(g)
.
If we define transpose_graph(S)
directly, as I did before that you modified my PR, these routines are not needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It felt cleaner to reuse the transpose
function from Base
and have it work from graph to graph, rather than from a matrix to a graph.
Why not keeping
nvertices
?It's more explicit than
n
.