Skip to content

Commit 19623f8

Browse files
committed
Add a function substitutable_bidirectional
1 parent ee27403 commit 19623f8

4 files changed

Lines changed: 121 additions & 8 deletions

File tree

docs/src/dev.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ SparseMatrixColorings.symmetrically_orthogonal_columns
5151
SparseMatrixColorings.structurally_orthogonal_columns
5252
SparseMatrixColorings.structurally_biorthogonal
5353
SparseMatrixColorings.substitutable_columns
54+
SparseMatrixColorings.substitutable_bidirectional
5455
SparseMatrixColorings.valid_dynamic_order
5556
```
5657

src/check.jl

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ A bipartition of the rows and columns of a matrix `A` is _structurally biorthogo
130130
1. the group containing the column `A[:, j]` has no other column with a nonzero in row `i`
131131
2. the group containing the row `A[i, :]` has no other row with a nonzero in column `j`
132132
133+
It is equivalent to an __star bicoloring__.
134+
133135
!!! warning
134136
This function is not coded with efficiency in mind, it is designed for small-scale tests.
135137
"""
@@ -142,8 +144,8 @@ function structurally_biorthogonal(
142144
if !proper_length_bicoloring(A, row_color, column_color; verbose)
143145
return false
144146
end
145-
column_group = group_by_color(column_color)
146147
row_group = group_by_color(row_color)
148+
column_group = group_by_color(column_color)
147149
for i in axes(A, 1), j in axes(A, 2)
148150
iszero(A[i, j]) && continue
149151
ci, cj = row_color[i], column_color[j]
@@ -313,6 +315,48 @@ function substitutable_columns(
313315
return true
314316
end
315317

318+
"""
319+
substitutable_bidirectional(
320+
A::AbstractMatrix, order_nonzeros::AbstractMatrix, row_color::AbstractVector{<:Integer}, column_color::AbstractVector{<:Integer};
321+
verbose=false
322+
)
323+
324+
Return `true` if bicoloring of the matrix `A` with the vectors `row_color` and `column_color` results in a bipartition that is substitutable, and `false` otherwise.
325+
For all nonzeros `A[i, j]`, `order_nonzeros[i, j]` provides its order of recovery.
326+
327+
A bipartition of the rows and columns of a matrix `A` is _substitutable_ if, for every nonzero element `A[i, j]`, either of the following statements holds:
328+
329+
1. the group containing the column `A[:, j]` has all nonzeros in row `i` ordered before `A[i, j]`
330+
2. the group containing the row `A[i, :]` has all nonzeros in column `j` ordered before `A[i, j]`
331+
332+
It is equivalent to an __acyclic bicoloring__.
333+
334+
!!! warning
335+
This function is not coded with efficiency in mind, it is designed for small-scale tests.
336+
"""
337+
function substitutable_bidirectional(
338+
A::AbstractMatrix,
339+
order_nonzeros::AbstractMatrix,
340+
row_color::AbstractVector{<:Integer},
341+
column_color::AbstractVector{<:Integer};
342+
verbose::Bool=false,
343+
)
344+
if !proper_length_bicoloring(A, row_color, column_color; verbose)
345+
return false
346+
end
347+
row_group = group_by_color(row_color)
348+
column_group = group_by_color(column_color)
349+
for i in axes(A, 1), j in axes(A, 2)
350+
iszero(A[i, j]) && continue
351+
ci, cj = row_color[i], column_color[j]
352+
check = _substitutable_check(
353+
A, order_nonzeros; i, j, ci, cj, row_group, column_group, verbose
354+
)
355+
!check && return false
356+
end
357+
return true
358+
end
359+
316360
function _substitutable_check(
317361
A::AbstractMatrix,
318362
order_nonzeros::AbstractMatrix;

test/check.jl

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,3 +282,28 @@ For coefficient (i=3, j=5) with colors (ci=3, cj=0):
282282
""",
283283
) substitutable_columns(A3, B3, [1, 2, 3, 3, 0]; verbose=true)
284284
end
285+
286+
@testset "Substitutable bidirectional" begin
287+
A = [
288+
1 0 0
289+
0 1 0
290+
0 0 1
291+
]
292+
B = [
293+
1 0 0
294+
0 2 0
295+
0 0 3
296+
]
297+
298+
# success
299+
300+
substitutable_bidirectional(A, B, [1, 0, 0], [0, 1, 1])
301+
302+
# failure
303+
304+
log = (:warn, "2 colors provided for 3 columns.")
305+
@test_logs log !substitutable_bidirectional(A, B, [1, 0, 0], [0, 1]; verbose=true)
306+
307+
log = (:warn, "4 colors provided for 3 rows.")
308+
@test_logs log !substitutable_bidirectional(A, B, [1, 0, 0, 1], [0, 1, 1]; verbose=true)
309+
end

test/utils.jl

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,25 @@ using SparseMatrixColorings:
88
AdjacencyGraph,
99
LinearSystemColoringResult,
1010
TreeSetColoringResult,
11+
BicoloringResult,
1112
directly_recoverable_columns,
1213
matrix_versions,
1314
respectful_similar,
1415
structurally_orthogonal_columns,
1516
symmetrically_orthogonal_columns,
1617
structurally_biorthogonal,
17-
substitutable_columns
18+
substitutable_columns,
19+
substitutable_bidirectional
1820
using Test
1921

2022
const _ALL_ORDERS = (
2123
NaturalOrder(), LargestFirst(), SmallestLast(), IncidenceDegree(), DynamicLargestFirst()
2224
)
2325

2426
function order_from_trees(result::TreeSetColoringResult)
25-
(; ag, color, reverse_bfs_orders, diagonal_indices, tree_edge_indices, nt) = result
27+
(; A, ag, reverse_bfs_orders, diagonal_indices, tree_edge_indices, nt) = result
2628
(; S) = ag
27-
n = length(color)
29+
m, n = size(A)
2830
nnzS = nnz(S)
2931
nzval = zeros(Int, nnzS)
3032
order_nonzeros = SparseMatrixCSC(n, n, S.colptr, S.rowval, nzval)
@@ -46,6 +48,36 @@ function order_from_trees(result::TreeSetColoringResult)
4648
return order_nonzeros
4749
end
4850

51+
function order_from_trees(result::BicoloringResult)
52+
(; A, abg, row_color, column_color, symmetric_result, large_colptr, large_rowval) =
53+
result
54+
@assert symmetric_result isa TreeSetColoringResult
55+
(; ag, reverse_bfs_orders, tree_edge_indices, nt) = symmetric_result
56+
(; S) = ag
57+
m, n = size(A)
58+
nnzA = nnz(S) ÷ 2
59+
nzval = zeros(Int, nnzA)
60+
colptr = large_colptr[1:(n + 1)]
61+
rowval = large_rowval[1:nnzA]
62+
rowval .-= n
63+
order_nonzeros = SparseMatrixCSC(m, n, colptr, rowval, nzval)
64+
counter = 0
65+
for k in 1:nt
66+
first = tree_edge_indices[k]
67+
last = tree_edge_indices[k + 1] - 1
68+
for pos in first:last
69+
(i, j) = reverse_bfs_orders[pos]
70+
counter += 1
71+
if i > j
72+
order_nonzeros[i - n, j] = counter
73+
else
74+
order_nonzeros[j - n, i] = counter
75+
end
76+
end
77+
end
78+
return order_nonzeros
79+
end
80+
4981
function test_coloring_decompression(
5082
A0::AbstractMatrix,
5183
problem::ColoringProblem{structure,partition},
@@ -123,10 +155,12 @@ function test_coloring_decompression(
123155
end
124156
end
125157

126-
if decompression == :substitution
127-
if structure == :symmetric
128-
order_nonzeros = order_from_trees(result)
129-
@test substitutable_columns(A0, order_nonzeros, color)
158+
@testset "Substitutable" begin
159+
if decompression == :substitution
160+
if structure == :symmetric
161+
order_nonzeros = order_from_trees(result)
162+
@test substitutable_columns(A0, order_nonzeros, color)
163+
end
130164
end
131165
end
132166

@@ -268,6 +302,15 @@ function test_bicoloring_decompression(
268302
@test structurally_biorthogonal(A0, row_color, column_color)
269303
end
270304
end
305+
306+
if decompression == :substitution
307+
@testset "Substitutable" begin
308+
order_nonzeros = order_from_trees(result)
309+
@test substitutable_bidirectional(
310+
A0, order_nonzeros, row_color, column_color
311+
)
312+
end
313+
end
271314
end
272315

273316
@testset "More orders is better" begin

0 commit comments

Comments
 (0)