Skip to content

Commit 8e3b35b

Browse files
committed
Only store non-zero blocks in BlockMap
1 parent cda88dc commit 8e3b35b

File tree

3 files changed

+99
-4
lines changed

3 files changed

+99
-4
lines changed

src/LinearMaps.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export ⊗, kronsum, ⊕
55

66
using LinearAlgebra
77
using SparseArrays
8+
import Base.Broadcast: materialize!
89

910
if VERSION < v"1.2-"
1011
import Base: has_offset_axes

src/blockmap.jl

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,87 @@ struct BlockMap{T,As<:Tuple{Vararg{LinearMap}},Rs<:Tuple{Vararg{Int}},Rranges<:T
33
rows::Rs
44
rowranges::Rranges
55
colranges::Cranges
6+
# Store BlockMap size explicitly, to allow south-east corner to be
7+
# empty.
8+
M::Int
9+
N::Int
10+
611
function BlockMap{T,R,S}(maps::R, rows::S) where {T, R<:Tuple{Vararg{LinearMap}}, S<:Tuple{Vararg{Int}}}
712
for A in maps
813
promote_type(T, eltype(A)) == T || throw(InexactError())
914
end
1015
rowranges, colranges = rowcolranges(maps, rows)
11-
return new{T,R,S,typeof(rowranges),typeof(colranges)}(maps, rows, rowranges, colranges)
16+
M,N = last(last(rowranges)), last(last(colranges))
17+
return new{T,R,S,typeof(rowranges),typeof(colranges)}(maps, rows, rowranges, colranges, M, N)
1218
end
19+
20+
BlockMap{T}(maps::As, rows::Rs, rowranges::Rranges, colranges::Cranges,
21+
M::Integer, N::Integer) where {T,As,Rs,Rranges,Cranges} =
22+
new{T,As,Rs,Rranges,Cranges}(maps, rows, rowranges, colranges, M, N)
1323
end
1424

1525
BlockMap{T}(maps::As, rows::S) where {T,As<:Tuple{Vararg{LinearMap}},S} = BlockMap{T,As,S}(maps, rows)
1626

27+
"""
28+
BlockMap{T}(M, N, (m, n), maps, is, js)
29+
30+
Constructor for a `BlockMap` of `eltype(T)` for the case when all rows
31+
have the same column dimensions; these are specified using the vectors
32+
`m` and `n`. Only those blocks `maps` that are actually occupied need
33+
to be specified, their coordinates are given by the vectors `is` and `js`.
34+
"""
35+
function BlockMap{T}(M::Int, N::Int, (m,n), maps, is, js) where T
36+
all(m .> 0) && all(n .> 0) ||
37+
throw(ArgumentError("Block sizes must be positive integers"))
38+
39+
sum(m) == M && sum(n) == N ||
40+
throw(DimensionMismatch("Cumulative block dimensions $(sum(m))×$(sum(n)) do not agree with overall size $(M)×$(N)"))
41+
42+
allunique(zip(is, js)) ||
43+
throw(ArgumentError("Not all block indices unique"))
44+
45+
for (A,i,j) in zip(maps,is,js)
46+
promote_type(T, eltype(A)) == T || throw(InexactError())
47+
0 < i length(m) || throw(ArgumentError("Invalid block row $(i) ∉ 1:$(length(m))"))
48+
0 < j length(n) || throw(ArgumentError("Invalid block column $(j) ∉ 1:$(length(n))"))
49+
size(A) == (m[i],n[j]) ||
50+
throw(DimensionMismatch("Block of size $(size(A)) does not match size at $((i,j)): $((m[i],n[j]))"))
51+
end
52+
53+
# It would be nice to do just maps = map(LinearMap, maps)
54+
maps = (map(A -> A isa LinearMap ? A : LinearMap(A), maps)...,)
55+
56+
rows = zeros(Int, length(m))
57+
colranges = ()
58+
colstarts = 1 .+ vcat(0,cumsum(n))
59+
for p = sortperm(collect(zip(is,js)))
60+
A,i,j = maps[p],is[p],js[p]
61+
rows[i] += 1
62+
colranges = (colranges..., colstarts[j]:colstarts[j+1]-1)
63+
end
64+
rows = (rows...,)
65+
66+
rowranges = ()
67+
i = 1
68+
for (mm,r) in zip(m,rows)
69+
iprev = i
70+
i += mm
71+
rowranges = (rowranges...,iprev:i-1)
72+
end
73+
74+
BlockMap{T}(maps, rows, rowranges, colranges, M, N)
75+
end
76+
77+
function Base.show(io::IO, B::BlockMap{T}) where T
78+
nrows = length(B.rows)
79+
# One block in one row may actually span multiple block columns
80+
# for other rows, but it's nice to get a sense of the structure
81+
# anyway.
82+
ncols = maximum(B.rows)
83+
M,N = size(B)
84+
write(io, "$(nrows)×$(ncols)-blocked $(M)×$(N) BlockMap{$T}")
85+
end
86+
1787
MulStyle(A::BlockMap) = MulStyle(A.maps...)
1888

1989
function check_dim(A::LinearMap, dim, n)
@@ -49,7 +119,7 @@ function rowcolranges(maps, rows)
49119
return rowranges::NTuple{length(rows), UnitRange{Int}}, colranges::NTuple{length(maps), UnitRange{Int}}
50120
end
51121

52-
Base.size(A::BlockMap) = (last(last(A.rowranges)), last(last(A.colranges)))
122+
Base.size(A::BlockMap) = (A.M, A.N)
53123

54124
############
55125
# concatenation
@@ -306,6 +376,7 @@ LinearAlgebra.adjoint(A::BlockMap) = AdjointMap(A)
306376
maps, rows, yinds, xinds = A.maps, A.rows, A.rowranges, A.colranges
307377
mapind = 0
308378
@views @inbounds for (row, yi) in zip(rows, yinds)
379+
iszero(row) && continue
309380
yrow = selectdim(y, 1, yi)
310381
mapind += 1
311382
mul!(yrow, maps[mapind], selectdim(x, 1, xinds[mapind]), α, β)
@@ -377,6 +448,29 @@ for Atype in (AbstractVector, AbstractMatrix)
377448
end
378449
end
379450

451+
############
452+
# materialization into matrices
453+
############
454+
455+
function materialize!(M::MT, A::BlockMap) where {MT<:AbstractArray}
456+
axes(M) == axes(A) ||
457+
throw(DimensionMismatch("Cannot materialize BlockMap of size $(size(A)) into matrix of size $(size(M))"))
458+
459+
M .= false
460+
461+
maps, rows, yinds, xinds = A.maps, A.rows, A.rowranges, A.colranges
462+
mapind = 0
463+
@views @inbounds for (row, yi) in zip(rows, yinds)
464+
Mrow = selectdim(M, 1, yi)
465+
for _ in 1:row
466+
mapind +=1
467+
copyto!(selectdim(Mrow, 2, xinds[mapind]), convert(Matrix, maps[mapind]))
468+
end
469+
end
470+
471+
M
472+
end
473+
380474
############
381475
# show methods
382476
############

src/conversion.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ function Base.convert(::Type{Matrix}, Aλ::CompositeMap{<:Any,<:Tuple{UniformSca
4444
return A.lmap*J.λ
4545
end
4646

47-
Base.Matrix(A::BlockMap) = hvcat(A.rows, convert.(AbstractMatrix, A.maps)...)
47+
Base.Matrix(A::BlockMap) = materialize!(Matrix{eltype(A)}(undef, size(A)), A)
4848

4949
# sparse: create sparse matrix representation of LinearMap
5050
function SparseArrays.sparse(A::LinearMap{T}) where {T}
@@ -73,4 +73,4 @@ end
7373

7474
Base.convert(::Type{SparseMatrixCSC}, A::LinearMap) = sparse(A)
7575
Base.convert(::Type{SparseMatrixCSC}, A::WrappedMap) = convert(SparseMatrixCSC, A.lmap)
76-
Base.convert(::Type{SparseMatrixCSC}, A::BlockMap) = hvcat(A.rows, convert.(SparseMatrixCSC, A.maps)...)
76+
Base.convert(::Type{SparseMatrixCSC}, A::BlockMap) = materialize!(spzeros(eltype(A), size(A)...), A)

0 commit comments

Comments
 (0)