Skip to content

Commit

Permalink
Custom adjacency graph from bipartite
Browse files Browse the repository at this point in the history
  • Loading branch information
gdalle committed Oct 6, 2024
1 parent c28490d commit ea3b404
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 24 deletions.
4 changes: 3 additions & 1 deletion docs/src/dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ The docstrings on this page describe internals, they are not part of the public

```@docs
SparseMatrixColorings.SparsityPatternCSC
SparseMatrixColorings.AdjacencyGraph
SparseMatrixColorings.BipartiteGraph
SparseMatrixColorings.AbstractAdjacencyGraph
SparseMatrixColorings.AdjacencyGraph
SparseMatrixColorings.AdjacencyFromBipartiteGraph
SparseMatrixColorings.vertices
SparseMatrixColorings.neighbors
transpose
Expand Down
16 changes: 8 additions & 8 deletions src/coloring.jl
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ function partial_distance2_coloring!(
end

"""
star_coloring(g::AdjacencyGraph, order::AbstractOrder)
star_coloring(g::AbstractAdjacencyGraph, order::AbstractOrder)
Compute a star coloring of all vertices in the adjacency graph `g` and return a tuple `(color, star_set)`, where
Expand All @@ -67,14 +67,14 @@ The vertices are colored in a greedy fashion, following the `order` supplied.
# See also
- [`AdjacencyGraph`](@ref)
- [`AbstractAdjacencyGraph`](@ref)
- [`AbstractOrder`](@ref)
# References
> [_New Acyclic and Star Coloring Algorithms with Application to Computing Hessians_](https://epubs.siam.org/doi/abs/10.1137/050639879), Gebremedhin et al. (2007), Algorithm 4.1
"""
function star_coloring(g::AdjacencyGraph, order::AbstractOrder)
function star_coloring(g::AbstractAdjacencyGraph, order::AbstractOrder)
# Initialize data structures
nv = nb_vertices(g)
color = zeros(Int, nv)
Expand Down Expand Up @@ -157,7 +157,7 @@ function _treat!(
treated::AbstractVector{<:Integer},
forbidden_colors::AbstractVector{<:Integer},
# not modified
g::AdjacencyGraph,
g::AbstractAdjacencyGraph,
v::Integer,
w::Integer,
color::AbstractVector{<:Integer},
Expand All @@ -175,7 +175,7 @@ function _update_stars!(
star::Dict{<:Tuple,<:Integer},
hub::AbstractVector{<:Integer},
# not modified
g::AdjacencyGraph,
g::AbstractAdjacencyGraph,
v::Integer,
color::AbstractVector{<:Integer},
first_neighbor::AbstractVector{<:Tuple},
Expand Down Expand Up @@ -247,7 +247,7 @@ function symmetric_coefficient(
end

"""
acyclic_coloring(g::AdjacencyGraph, order::AbstractOrder)
acyclic_coloring(g::AbstractAdjacencyGraph, order::AbstractOrder)
Compute an acyclic coloring of all vertices in the adjacency graph `g` and return a tuple `(color, tree_set)`, where
Expand All @@ -260,14 +260,14 @@ The vertices are colored in a greedy fashion, following the `order` supplied.
# See also
- [`AdjacencyGraph`](@ref)
- [`AbstractAdjacencyGraph`](@ref)
- [`AbstractOrder`](@ref)
# References
> [_New Acyclic and Star Coloring Algorithms with Application to Computing Hessians_](https://epubs.siam.org/doi/abs/10.1137/050639879), Gebremedhin et al. (2007), Algorithm 3.1
"""
function acyclic_coloring(g::AdjacencyGraph, order::AbstractOrder)
function acyclic_coloring(g::AbstractAdjacencyGraph, order::AbstractOrder)
# Initialize data structures
nv = nb_vertices(g)
ne = nb_edges(g)
Expand Down
75 changes: 62 additions & 13 deletions src/graph.jl
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,16 @@ end

## Adjacency graph

"""
AbstractAdjacencyGraph{T}
Supertype for various adjacency graph implementations:
- [`AdjacencyGraph`](@ref)
- [`AdjacencyFromBipartiteGraph`](@ref)
"""
abstract type AbstractAdjacencyGraph{T} end

"""
AdjacencyGraph{T}
Expand All @@ -117,7 +127,7 @@ The adjacency graph of a symmetrix matric `A ∈ ℝ^{n × n}` is `G(A) = (V, E)
> [_What Color Is Your Jacobian? SparsityPatternCSC Coloring for Computing Derivatives_](https://epubs.siam.org/doi/10.1137/S0036144504444711), Gebremedhin et al. (2005)
"""
struct AdjacencyGraph{T}
struct AdjacencyGraph{T} <: AbstractAdjacencyGraph{T}
S::SparsityPatternCSC{T}
end

Expand All @@ -126,15 +136,15 @@ AdjacencyGraph(A::SparseMatrixCSC) = AdjacencyGraph(SparsityPatternCSC(A))

pattern(g::AdjacencyGraph) = g.S
nb_vertices(g::AdjacencyGraph) = pattern(g).n
vertices(g::AdjacencyGraph) = 1:nb_vertices(g)
vertices(g::AbstractAdjacencyGraph) = 1:nb_vertices(g)

function neighbors(g::AdjacencyGraph, v::Integer)
S = pattern(g)
neighbors_with_loops = view(rowvals(S), nzrange(S, v))
return Iterators.filter(!=(v), neighbors_with_loops) # TODO: optimize
end

function degree(g::AdjacencyGraph, v::Integer)
function degree(g::AbstractAdjacencyGraph, v::Integer)
d = 0
for u in neighbors(g, v)
if u != v
Expand All @@ -144,22 +154,18 @@ function degree(g::AdjacencyGraph, v::Integer)
return d
end

function nb_edges(g::AdjacencyGraph)
S = pattern(g)
function nb_edges(g::AbstractAdjacencyGraph)
ne = 0
for j in vertices(g)
for k in nzrange(S, j)
i = rowvals(S)[k]
if i > j
ne += 1
end
for v in vertices(g)
for u in neighbors(g, v)
ne += 1
end
end
return ne
end

maximum_degree(g::AdjacencyGraph) = maximum(Base.Fix1(degree, g), vertices(g))
minimum_degree(g::AdjacencyGraph) = minimum(Base.Fix1(degree, g), vertices(g))
maximum_degree(g::AbstractAdjacencyGraph) = maximum(Base.Fix1(degree, g), vertices(g))
minimum_degree(g::AbstractAdjacencyGraph) = minimum(Base.Fix1(degree, g), vertices(g))

## Bipartite graph

Expand Down Expand Up @@ -258,3 +264,46 @@ function degree_dist2(bg::BipartiteGraph{T}, ::Val{side}, v::Integer) where {T,s
end
return length(neighbors_dist2)
end

## Adjacency graph from bipartite

"""
AdjacencyFromBipartiteGraph{T}
Custom version of [`AdjacencyGraph`](@ref) constructed from a [`BipartiteGraph`](@ref).
If the bipartite graph represents a matrix `A`, then this graph represents the block matrix `[0 A; A' 0]` (of size `(n+m) x (n+m)`).
# Constructors
AdjacencyFromBipartiteGraph(A::AbstractMatrix)
# Fields
- `bg::BipartiteGraph{T}`: bipartite graph representation of the matrix `A`
"""
struct AdjacencyFromBipartiteGraph{T} <: AbstractAdjacencyGraph{T}
bg::BipartiteGraph{T}
end

function AdjacencyFromBipartiteGraph(A::AbstractMatrix; kwargs...)
return AdjacencyFromBipartiteGraph(BipartiteGraph(A; kwargs...))
end

function nb_vertices(abg::AdjacencyFromBipartiteGraph)
@compat (; bg) = abg
m, n = nb_vertices(bg, Val(1)), nb_vertices(bg, Val(2))
return m + n
end

function neighbors(abg::AdjacencyFromBipartiteGraph, v::Integer)
@compat (; bg) = abg
m, n = nb_vertices(bg, Val(1)), nb_vertices(bg, Val(2))
if 1 <= v <= n
# v is a column
return neighbors(bg, Val(2), v)
else
# v is a row
@assert n + 1 <= v <= n + m
return neighbors(bg, Val(1), v - n)
end
end
2 changes: 1 addition & 1 deletion src/order.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Instance of [`AbstractOrder`](@ref) which sorts vertices using their index in th
"""
struct NaturalOrder <: AbstractOrder end

function vertices(g::AdjacencyGraph, ::NaturalOrder)
function vertices(g::AbstractAdjacencyGraph, ::NaturalOrder)
return vertices(g)
end

Expand Down
36 changes: 35 additions & 1 deletion test/graph.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ using SparseArrays
using SparseMatrixColorings:
SparsityPatternCSC,
AdjacencyGraph,
AdjacencyFromBipartiteGraph,
BipartiteGraph,
degree,
degree_dist2,
nb_vertices,
nb_edges,
neighbors
neighbors,
star_coloring,
acyclic_coloring
using Test

## SparsityPatternCSC
Expand Down Expand Up @@ -118,3 +121,34 @@ end;
@test collect(neighbors(g, 7)) == [1, 2, 4, 5, 6, 8]
@test collect(neighbors(g, 8)) == [1, 2, 3, 5, 6, 7]
end

@testset "AdjacencyFromBipartiteGraph" begin
A = sparse([
1 0 0 0 0 1 1 1
0 1 0 0 1 0 1 1
0 0 1 0 1 1 0 1
0 0 0 1 1 1 1 0
])

abg = AdjacencyFromBipartiteGraph(A)

@test nb_vertices(abg) == 4 + 8
# neighbors of columns
@test neighbors(abg, 1) == [1]
@test neighbors(abg, 2) == [2]
@test neighbors(abg, 3) == [3]
@test neighbors(abg, 4) == [4]
@test neighbors(abg, 5) == [2, 3, 4]
@test neighbors(abg, 6) == [1, 3, 4]
@test neighbors(abg, 7) == [1, 2, 4]
@test neighbors(abg, 8) == [1, 2, 3]
# neighbors of rows
@test neighbors(abg, 8 + 1) == [1, 6, 7, 8]
@test neighbors(abg, 8 + 2) == [2, 5, 7, 8]
@test neighbors(abg, 8 + 3) == [3, 5, 6, 8]
@test neighbors(abg, 8 + 4) == [4, 5, 6, 7]

# TODO: remove once we have better tests, this is just to check whether it runs
@test length(star_coloring(abg, NaturalOrder())[1]) == 12
@test length(acyclic_coloring(abg, NaturalOrder())[1]) == 12
end

0 comments on commit ea3b404

Please sign in to comment.