Skip to content

Commit

Permalink
Add a structurally_biorthogonal check for star bicoloring (#155)
Browse files Browse the repository at this point in the history
* Add a function structurally_biorthogonal for star bicoloring

* Improve code coverage

* Increase code coverage

* Use new check in random tests

---------

Co-authored-by: Guillaume Dalle <[email protected]>
  • Loading branch information
amontoison and gdalle authored Nov 13, 2024
1 parent a331efe commit b429209
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 13 deletions.
1 change: 1 addition & 0 deletions docs/src/dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ SparseMatrixColorings.remap_colors
SparseMatrixColorings.directly_recoverable_columns
SparseMatrixColorings.symmetrically_orthogonal_columns
SparseMatrixColorings.structurally_orthogonal_columns
SparseMatrixColorings.structurally_biorthogonal
SparseMatrixColorings.valid_dynamic_order
```

Expand Down
87 changes: 81 additions & 6 deletions src/check.jl
Original file line number Diff line number Diff line change
@@ -1,18 +1,42 @@
function proper_length_coloring(
A::AbstractMatrix, color::AbstractVector{<:Integer}; verbose::Bool
A::AbstractMatrix, color::AbstractVector{<:Integer}; verbose::Bool=false
)
if length(color) != size(A, 2)
m, n = size(A)
if length(color) != n
if verbose
@warn "$(length(color)) colors provided for $(size(A, 2)) columns."
@warn "$(length(color)) colors provided for $n columns."
end
return false
end
return true
end

function proper_length_bicoloring(
A::AbstractMatrix,
row_color::AbstractVector{<:Integer},
column_color::AbstractVector{<:Integer};
verbose::Bool=false,
)
m, n = size(A)
bool = true
if length(row_color) != m
if verbose
@warn "$(length(row_color)) colors provided for $m rows."
end
bool = false
end
if length(column_color) != n
if verbose
@warn "$(length(column_color)) colors provided for $n columns."
end
bool = false
end
return bool
end

"""
structurally_orthogonal_columns(
A::AbstractMatrix, color::AbstractVector{<:Integer}
A::AbstractMatrix, color::AbstractVector{<:Integer};
verbose=false
)
Expand Down Expand Up @@ -56,8 +80,8 @@ end
)
Return `true` if coloring the columns of the symmetric matrix `A` with the vector `color` results in a partition that is symmetrically orthogonal, and `false` otherwise.
A partition of the columns of a symmetrix matrix `A` is _symmetrically orthogonal_ if, for every nonzero element `A[i, j]`, either of the following statements holds:
A partition of the columns of a symmetric matrix `A` is _symmetrically orthogonal_ if, for every nonzero element `A[i, j]`, either of the following statements holds:
1. the group containing the column `A[:, j]` has no other column with a nonzero in row `i`
2. the group containing the column `A[:, i]` has no other column with a nonzero in row `j`
Expand Down Expand Up @@ -102,6 +126,57 @@ function symmetrically_orthogonal_columns(
return true
end

"""
structurally_biorthogonal(
A::AbstractMatrix, row_color::AbstractVector{<:Integer}, column_color::AbstractVector{<:Integer};
verbose=false
)
Return `true` if bicoloring of the matrix `A` with the vectors `row_color` and `column_color` results in a bipartition that is structurally biorthogonal, and `false` otherwise.
A bipartition of the rows and columns of a matrix `A` is _structurally biorthogonal_ if, for every nonzero element `A[i, j]`, either of the following statements holds:
1. the group containing the column `A[:, j]` has no other column with a nonzero in row `i`
2. the group containing the row `A[i, :]` has no other row with a nonzero in column `j`
!!! warning
This function is not coded with efficiency in mind, it is designed for small-scale tests.
"""
function structurally_biorthogonal(
A::AbstractMatrix,
row_color::AbstractVector{<:Integer},
column_color::AbstractVector{<:Integer};
verbose::Bool=false,
)
if !proper_length_bicoloring(A, row_color, column_color; verbose)
return false
end
column_group = group_by_color(column_color)
row_group = group_by_color(row_color)
for i in axes(A, 1), j in axes(A, 2)
iszero(A[i, j]) && continue
ci, cj = row_color[i], column_color[j]
gi, gj = row_group[ci], column_group[cj]
A_gj_rowi = view(A, i, gj)
A_gi_columnj = view(A, gi, j)
nonzeros_gj_rowi = count(!iszero, A_gj_rowi)
nonzeros_gi_columnj = count(!iszero, A_gi_columnj)
if nonzeros_gj_rowi > 1 && nonzeros_gi_columnj > 1
if verbose
gj_incompatible_columns = gj[findall(!iszero, A_gj_rowi)]
gi_incompatible_rows = gi[findall(!iszero, A_gi_columnj)]
@warn """
For coefficient (i=$i, j=$j) with row color ci=$ci and column color cj=$cj:
- In row color ci=$ci, rows $gi_incompatible_rows all have nonzeros in column j=$j.
- In column color cj=$cj, columns $gj_incompatible_columns all have nonzeros in row i=$i.
"""
end
return false
end
end
return true
end

"""
directly_recoverable_columns(
A::AbstractMatrix, color::AbstractVector{<:Integer}
Expand Down
2 changes: 1 addition & 1 deletion src/graph.jl
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ end
Undirected graph without self-loops representing the nonzeros of a symmetric matrix (typically a Hessian matrix).
The adjacency graph of a symmetrix matric `A ∈ ℝ^{n × n}` is `G(A) = (V, E)` where
The adjacency graph of a symmetric matrix `A ∈ ℝ^{n × n}` is `G(A) = (V, E)` where
- `V = 1:n` is the set of rows or columns `i`/`j`
- `(i, j) ∈ E` whenever `A[i, j] ≠ 0` and `i ≠ j`
Expand Down
35 changes: 35 additions & 0 deletions test/check.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ using LinearAlgebra
using SparseMatrixColorings:
structurally_orthogonal_columns,
symmetrically_orthogonal_columns,
structurally_biorthogonal,
directly_recoverable_columns,
what_fig_41,
efficient_fig_1
Expand Down Expand Up @@ -122,3 +123,37 @@ For coefficient (i=2, j=3) with column colors (ci=3, cj=1):
@test !directly_recoverable_columns(A, [1, 2, 1, 3, 1, 4, 2, 5, 1, 2])
@test !directly_recoverable_columns(A, [1, 2, 1, 4, 1, 4, 3, 5, 1, 2])
end

@testset "Structurally biorthogonal" begin
A = [
1 5 7 9 11
2 0 0 0 12
3 0 0 0 13
4 6 8 10 14
]

# success

@test structurally_biorthogonal(A, [1, 2, 2, 3], [1, 2, 2, 2, 3])

# failure

@test !structurally_biorthogonal(A, [1, 2, 2, 3], [1, 2, 2, 2])
@test !structurally_biorthogonal(A, [1, 2, 2, 3, 4], [1, 2, 2, 2, 3])
@test !structurally_biorthogonal(A, [1, 1, 1, 2], [1, 1, 1, 1, 2])

@test_logs (:warn, "4 colors provided for 5 columns.") !structurally_biorthogonal(
A, [1, 2, 2, 3], [1, 2, 2, 2]; verbose=true
)
@test_logs (:warn, "5 colors provided for 4 rows.") !structurally_biorthogonal(
A, [1, 2, 2, 3, 4], [1, 2, 2, 2, 3]; verbose=true
)
@test_logs (
:warn,
"""
For coefficient (i=1, j=1) with row color ci=1 and column color cj=1:
- In row color ci=1, rows [1, 2, 3] all have nonzeros in column j=1.
- In column color cj=1, columns [1, 2, 3, 4] all have nonzeros in row i=1.
""",
) !structurally_biorthogonal(A, [1, 1, 1, 2], [1, 1, 1, 1, 2]; verbose=true)
end
23 changes: 17 additions & 6 deletions test/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ using SparseMatrixColorings:
matrix_versions,
respectful_similar,
structurally_orthogonal_columns,
symmetrically_orthogonal_columns
symmetrically_orthogonal_columns,
structurally_biorthogonal
using Test

function test_coloring_decompression(
Expand Down Expand Up @@ -59,13 +60,16 @@ function test_coloring_decompression(
if partition == :column
@test structurally_orthogonal_columns(A0, color)
@test directly_recoverable_columns(A0, color)
else
elseif partition == :row
@test structurally_orthogonal_columns(transpose(A0), color)
@test directly_recoverable_columns(transpose(A0), color)
end
else
@test symmetrically_orthogonal_columns(A0, color)
@test directly_recoverable_columns(A0, color)
# structure == :symmetric
if partition == :column
@test symmetrically_orthogonal_columns(A0, color)
@test directly_recoverable_columns(A0, color)
end
end
end
end
Expand Down Expand Up @@ -163,9 +167,16 @@ function test_bicoloring_decompression(
result = coloring(A, problem, algo; decompression_eltype=Float64)
end
Br, Bc = compress(A, result)
@test size(Br, 1) == length(unique(row_colors(result)))
@test size(Bc, 2) == length(unique(column_colors(result)))
row_color, column_color = row_colors(result), column_colors(result)
@test size(Br, 1) == length(unique(row_color))
@test size(Bc, 2) == length(unique(column_color))
@test ncolors(result) == size(Br, 1) + size(Bc, 2)

if decompression == :direct
@testset "Recoverability" begin
@test structurally_biorthogonal(A0, row_color, column_color)
end
end
@testset "Full decompression" begin
@test decompress(Br, Bc, result) A0
@test decompress(Br, Bc, result) A0 # check result wasn't modified
Expand Down

0 comments on commit b429209

Please sign in to comment.