Skip to content

Commit b06ba20

Browse files
authored
Remove defaults for alpha and beta in 5-arg mul! (#96)
1 parent 0c2de32 commit b06ba20

File tree

9 files changed

+149
-59
lines changed

9 files changed

+149
-59
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ jobs:
1010
test:
1111
name: Julia ${{ matrix.version }} - ${{ matrix.os }}
1212
runs-on: ${{ matrix.os }}
13+
continue-on-error: ${{ matrix.version == 'nightly' }}
1314
strategy:
1415
matrix:
1516
version:

README.md

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@ transformations or linear operators acting on vectors. The only requirement for
99
a LinearMap is that it can act on a vector (by multiplication) efficiently.
1010

1111
## What's new in v2.7
12-
* Speed-up of scaled `LinearMap`s by avoiding allocations
12+
* Potential reduction of memory allocations in multiplication of
13+
`LinearCombination`s, `BlockMap`s, and real- or complex-scaled `LinearMap`s.
14+
For the latter, a new internal type `ScaledMap` has been introduced.
15+
* Multiplication code for `CompositeMap`s has been refactored to facilitate to
16+
provide memory for storage of intermediate results by directly calling helper
17+
functions.
1318

1419
## What's new in v2.6
1520
* New feature: "lazy" Kronecker product, Kronecker sums, and powers thereof
@@ -18,25 +23,25 @@ a LinearMap is that it can act on a vector (by multiplication) efficiently.
1823
* Compatibility with the generic multiply-and-add interface (a.k.a. 5-arg
1924
`mul!`) introduced in julia v1.3
2025

21-
## What's new in v2.5.0
26+
## What's new in v2.5
2227
* New feature: concatenation of `LinearMap`s objects with `UniformScaling`s,
2328
consistent with (h-, v-, and hc-)concatenation of matrices. Note, matrices
2429
`A` must be wrapped as `LinearMap(A)`, `UniformScaling`s are promoted to
2530
`LinearMap`s automatically.
2631

27-
## What's new in v2.4.0
32+
## What's new in v2.4
2833
* Support restricted to Julia v1.0+.
2934

30-
## What's new in v2.3.0
35+
## What's new in v2.3
3136
* Fully Julia v0.7/v1.0/v1.1 compatible.
3237
* Full support of noncommutative number types such as quaternions.
3338

34-
## What's new in v2.2.0
39+
## What's new in v2.2
3540
* Fully Julia v0.7/v1.0 compatible.
3641
* A `convert(SparseMatrixCSC, A::LinearMap)` function, that calls the `sparse`
3742
matrix generating function.
3843

39-
## What's new in v2.1.0
44+
## What's new in v2.1
4045
* Fully Julia v0.7 compatible; dropped compatibility for previous versions of
4146
Julia from LinearMaps.jl v2.0.0 on.
4247
* A 5-argument version for `mul!(y, A::LinearMap, x, α=1, β=0)`, which
@@ -66,7 +71,7 @@ in Julia versions below 0.7).
6671
## Philosophy
6772

6873
Several iterative linear algebra methods such as linear solvers or eigensolvers
69-
only require an efficient evaluation of the matrix vector product, where the
74+
only require an efficient evaluation of the matrix-vector product, where the
7075
concept of a matrix can be formalized / generalized to a linear map (or linear
7176
operator in the special case of a square matrix).
7277

@@ -89,10 +94,11 @@ The LinearMaps package provides the following functionality:
8994
`isposdef`) of the existing matrix or linear map.
9095

9196
3. A framework for combining objects of type `LinearMap` and of type
92-
`AbstractMatrix` using linear combinations, transposition and composition,
97+
`AbstractMatrix` using linear combinations, transposition, composition,
98+
concatenation and Kronecker product/sums,
9399
where the linear map resulting from these operations is never explicitly
94-
evaluated but only its matrix vector product is defined (i.e. lazy
95-
evaluation). The matrix vector product is written to minimize memory
100+
evaluated but only its matrix-vector product is defined (i.e. lazy
101+
evaluation). The matrix-vector product is written to minimize memory
96102
allocation by using a minimal number of temporary vectors. There is full
97103
support for the in-place version `mul!`, which should be preferred for
98104
higher efficiency in critical algorithms. In addition, it tries to recognize
@@ -159,7 +165,7 @@ The LinearMaps package provides the following functionality:
159165
argument corresponding to the input, and `true` if it accepts two vector
160166
arguments where the first will be mutated so as to contain the result.
161167
In both cases, the resulting `A::FunctionMap` will support both the
162-
mutating and non-mutating matrix vector multiplication. Default value is
168+
mutating and non-mutating matrix-vector multiplication. Default value is
163169
guessed based on the number of arguments for the first method in the
164170
method list of `f`; it is not possible to use `f` and `fc` where only
165171
one of the two is mutating and the other is not.
@@ -190,6 +196,12 @@ The LinearMaps package provides the following functionality:
190196
handle both `A::AbstractMatrix` and `A::LinearMap`, it is recommended to use
191197
`convert(Matrix, A*X)`.
192198
199+
* `convert(AbstractMatrix, A::LinearMap)`, `convert(AbstractArray, A::LinearMap)`
200+
201+
Create an `AbstractMatrix` representation of the `LinearMap`. This falls
202+
back to `Matrix(A)`, but avoids explicit construction in case the `LinearMap`
203+
object is matrix-based.
204+
193205
* `SparseArrays.sparse(A::LinearMap)` and `convert(SparseMatrixCSC, A::LinearMap)`
194206
195207
Create a sparse matrix representation of the `LinearMap` object, by
@@ -206,7 +218,7 @@ The LinearMaps package provides the following functionality:
206218
* `mul!(Y::AbstractMatrix, A::LinearMap, X::AbstractMatrix)`: applies `A` to
207219
each column of `X` and stores the results in the corresponding columns of
208220
`Y`;
209-
* `mul!(y::AbstractVector, A::LinearMap, x::AbstractVector, α::Number=true, β::Number=false)`:
221+
* `mul!(y::AbstractVector, A::LinearMap, x::AbstractVector, α::Number, β::Number)`:
210222
computes `A * x * α + y * β` and stores the result in `y`. Analogously for `X,Y::AbstractMatrix`.
211223
212224
Applying the adjoint or transpose of `A` (if defined) to `x` works exactly
@@ -240,7 +252,7 @@ constructor described above.
240252
* `FunctionMap`
241253
242254
Type for wrapping an arbitrary function that is supposed to implement the
243-
matrix vector product as a `LinearMap`.
255+
matrix-vector product as a `LinearMap`.
244256
245257
* `WrappedMap`
246258
@@ -252,17 +264,25 @@ constructor described above.
252264
will never evaluate `mat1*mat2`, since this is more costly than evaluating
253265
`mat1*(mat2*x)` and the latter is the only operation that needs to be performed
254266
by `LinearMap` objects anyway. While the cost of matrix addition is comparable
255-
to matrix vector multiplication, this too is not performed explicitly since
267+
to matrix-vector multiplication, this too is not performed explicitly since
256268
this would require new storage of the same amount as of the original matrices.
257269
270+
* `ScaledMap`
271+
272+
Type for representing a scalar multiple of any `LinearMap` type. A
273+
`ScaledMap` will be automatically constructed if real or complex `LinearMap`
274+
objects are multiplied by real or complex scalars from the left or from the
275+
right.
276+
258277
* `UniformScalingMap`
259278
260279
Type for representing a scalar multiple of the identity map (a.k.a. uniform
261280
scaling) of a certain size `M=N`, obtained simply as `UniformScalingMap(λ, M)`.
262281
The type `T` of the resulting `LinearMap` object is inferred from the type of
263-
`λ`. A `UniformScalingMap` of the correct size will be automatically created
264-
if `LinearMap` objects are multiplied by scalars from the left or from the right,
265-
respecting the order of multiplication.
282+
`λ`. A `UniformScalingMap` of the correct size will be automatically
283+
constructed if `LinearMap` objects are multiplied by scalars from the left
284+
or from the right (respecting the order of multiplication), if either the
285+
`eltype` of the `LinearMap` or the scalar are of non-commutative type, .
266286
267287
* `LinearCombination`, `CompositeMap`, `TransposeMap` and `AdjointMap`
268288

src/LinearMaps.jl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,13 @@ convert_to_lmaps(A) = (convert_to_lmaps_(A),)
7171

7272
function Base.:(*)(A::LinearMap, x::AbstractVector)
7373
size(A, 2) == length(x) || throw(DimensionMismatch("mul!"))
74-
return @inbounds mul!(similar(x, promote_type(eltype(A), eltype(x)), size(A, 1)), A, x)
74+
return @inbounds A_mul_B!(similar(x, promote_type(eltype(A), eltype(x)), size(A, 1)), A, x)
7575
end
76-
function LinearAlgebra.mul!(y::AbstractVector, A::LinearMap, x::AbstractVector, α::Number=true, β::Number=false)
76+
function LinearAlgebra.mul!(y::AbstractVector, A::LinearMap, x::AbstractVector)
77+
@boundscheck check_dim_mul(y, A, x)
78+
return @inbounds A_mul_B!(y, A, x)
79+
end
80+
function LinearAlgebra.mul!(y::AbstractVector, A::LinearMap, x::AbstractVector, α::Number, β::Number)
7781
@boundscheck check_dim_mul(y, A, x)
7882
if isone(α)
7983
iszero(β) && (A_mul_B!(y, A, x); return y)

src/blockmap.jl

Lines changed: 64 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -303,9 +303,21 @@ LinearAlgebra.adjoint(A::BlockMap) = AdjointMap(A)
303303
############
304304

305305
@inline function _blockmul!(y, A::BlockMap, x, α, β)
306+
if iszero(α)
307+
iszero(β) && return fill!(y, zero(eltype(y)))
308+
isone(β) && return y
309+
return rmul!(y, β)
310+
end
311+
return __blockmul!(MulStyle(A), y, A, x, α, β)
312+
end
313+
314+
@inline __blockmul!(::FiveArg, y, A, x, α, β) = ___blockmul!(y, A, x, α, β, nothing)
315+
@inline __blockmul!(::ThreeArg, y, A, x, α, β) = ___blockmul!(y, A, x, α, β, similar(y))
316+
317+
function ___blockmul!(y, A, x, α, β, ::Nothing)
306318
maps, rows, yinds, xinds = A.maps, A.rows, A.rowranges, A.colranges
307319
mapind = 0
308-
@views @inbounds for (row, yi) in zip(rows, yinds)
320+
@views for (row, yi) in zip(rows, yinds)
309321
yrow = selectdim(y, 1, yi)
310322
mapind += 1
311323
mul!(yrow, maps[mapind], selectdim(x, 1, xinds[mapind]), α, β)
@@ -316,24 +328,50 @@ LinearAlgebra.adjoint(A::BlockMap) = AdjointMap(A)
316328
end
317329
return y
318330
end
331+
function ___blockmul!(y, A, x, α, β, z)
332+
maps, rows, yinds, xinds = A.maps, A.rows, A.rowranges, A.colranges
333+
mapind = 0
334+
@views for (row, yi) in zip(rows, yinds)
335+
yrow = selectdim(y, 1, yi)
336+
zrow = selectdim(z, 1, yi)
337+
mapind += 1
338+
if MulStyle(maps[mapind]) === ThreeArg() && !iszero(β)
339+
!isone(β) && rmul!(yrow, β)
340+
muladd!(ThreeArg(), yrow, maps[mapind], selectdim(x, 1, xinds[mapind]), α, zrow)
341+
else
342+
mul!(yrow, maps[mapind], selectdim(x, 1, xinds[mapind]), α, β)
343+
end
344+
for _ in 2:row
345+
mapind +=1
346+
muladd!(MulStyle(maps[mapind]), yrow, maps[mapind], selectdim(x, 1, xinds[mapind]), α, zrow)
347+
end
348+
end
349+
return y
350+
end
319351

320352
@inline function _transblockmul!(y, A::BlockMap, x, α, β, transform)
321353
maps, rows, xinds, yinds = A.maps, A.rows, A.rowranges, A.colranges
322-
@views @inbounds begin
323-
# first block row (rowind = 1) of A, meaning first block column of A', fill all of y
324-
xcol = selectdim(x, 1, first(xinds))
325-
for rowind in 1:first(rows)
326-
mul!(selectdim(y, 1, yinds[rowind]), transform(maps[rowind]), xcol, α, β)
327-
end
328-
mapind = first(rows)
329-
# subsequent block rows of A (block columns of A'),
330-
# add results to corresponding parts of y
331-
# TODO: think about multithreading
332-
for (row, xi) in zip(Base.tail(rows), Base.tail(xinds))
333-
xcol = selectdim(x, 1, xi)
334-
for _ in 1:row
335-
mapind +=1
336-
mul!(selectdim(y, 1, yinds[mapind]), transform(maps[mapind]), xcol, α, true)
354+
if iszero(α)
355+
iszero(β) && return fill!(y, zero(eltype(y)))
356+
isone(β) && return y
357+
return rmul!(y, β)
358+
else
359+
@views begin
360+
# first block row (rowind = 1) of A, meaning first block column of A', fill all of y
361+
xcol = selectdim(x, 1, first(xinds))
362+
for rowind in 1:first(rows)
363+
mul!(selectdim(y, 1, yinds[rowind]), transform(maps[rowind]), xcol, α, β)
364+
end
365+
mapind = first(rows)
366+
# subsequent block rows of A (block columns of A'),
367+
# add results to corresponding parts of y
368+
# TODO: think about multithreading
369+
for (row, xi) in zip(Base.tail(rows), Base.tail(xinds))
370+
xcol = selectdim(x, 1, xi)
371+
for _ in 1:row
372+
mapind +=1
373+
mul!(selectdim(y, 1, yinds[mapind]), transform(maps[mapind]), xcol, α, true)
374+
end
337375
end
338376
end
339377
end
@@ -345,34 +383,34 @@ end
345383
############
346384

347385
Base.@propagate_inbounds A_mul_B!(y::AbstractVector, A::BlockMap, x::AbstractVector) =
348-
mul!(y, A, x)
386+
mul!(y, A, x, true, false)
349387

350388
Base.@propagate_inbounds A_mul_B!(y::AbstractVector, A::TransposeMap{<:Any,<:BlockMap}, x::AbstractVector) =
351-
mul!(y, A, x)
389+
mul!(y, A, x, true, false)
352390

353391
Base.@propagate_inbounds At_mul_B!(y::AbstractVector, A::BlockMap, x::AbstractVector) =
354-
mul!(y, transpose(A), x)
392+
mul!(y, transpose(A), x, true, false)
355393

356394
Base.@propagate_inbounds A_mul_B!(y::AbstractVector, A::AdjointMap{<:Any,<:BlockMap}, x::AbstractVector) =
357-
mul!(y, A, x)
395+
mul!(y, A, x, true, false)
358396

359397
Base.@propagate_inbounds Ac_mul_B!(y::AbstractVector, A::BlockMap, x::AbstractVector) =
360-
mul!(y, adjoint(A), x)
398+
mul!(y, adjoint(A), x, true, false)
361399

362400
for Atype in (AbstractVector, AbstractMatrix)
363401
@eval Base.@propagate_inbounds function LinearAlgebra.mul!(y::$Atype, A::BlockMap, x::$Atype,
364-
α::Number=true, β::Number=false)
402+
α::Number, β::Number)
365403
require_one_based_indexing(y, x)
366404
@boundscheck check_dim_mul(y, A, x)
367-
return _blockmul!(y, A, x, α, β)
405+
return @inbounds _blockmul!(y, A, x, α, β)
368406
end
369407

370408
for (maptype, transform) in ((:(TransposeMap{<:Any,<:BlockMap}), :transpose), (:(AdjointMap{<:Any,<:BlockMap}), :adjoint))
371409
@eval Base.@propagate_inbounds function LinearAlgebra.mul!(y::$Atype, wrapA::$maptype, x::$Atype,
372-
α::Number=true, β::Number=false)
410+
α::Number, β::Number)
373411
require_one_based_indexing(y, x)
374412
@boundscheck check_dim_mul(y, wrapA, x)
375-
return _transblockmul!(y, wrapA.lmap, x, α, β, $transform)
413+
return @inbounds _transblockmul!(y, wrapA.lmap, x, α, β, $transform)
376414
end
377415
end
378416
end
@@ -468,7 +506,7 @@ Base.@propagate_inbounds Ac_mul_B!(y::AbstractVector, A::BlockDiagonalMap, x::Ab
468506

469507
for Atype in (AbstractVector, AbstractMatrix)
470508
@eval Base.@propagate_inbounds function LinearAlgebra.mul!(y::$Atype, A::BlockDiagonalMap, x::$Atype,
471-
α::Number=true, β::Number=false)
509+
α::Number, β::Number)
472510
require_one_based_indexing(y, x)
473511
@boundscheck check_dim_mul(y, A, x)
474512
return _blockscaling!(y, A, x, α, β)

src/linearcombination.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ LinearAlgebra.adjoint(A::LinearCombination) = LinearCombination{eltype(A)}(map
6666
# multiplication with vectors & matrices
6767
for Atype in (AbstractVector, AbstractMatrix)
6868
@eval Base.@propagate_inbounds function LinearAlgebra.mul!(y::$Atype, A::LinearCombination, x::$Atype,
69-
α::Number=true, β::Number=false)
69+
α::Number, β::Number)
7070
@boundscheck check_dim_mul(y, A, x)
7171
if iszero(α) # trivial cases
7272
iszero(β) && (fill!(y, zero(eltype(y))); return y)
@@ -117,8 +117,8 @@ end
117117
return y
118118
end
119119

120-
A_mul_B!(y::AbstractVector, A::LinearCombination, x::AbstractVector) = mul!(y, A, x)
120+
A_mul_B!(y::AbstractVector, A::LinearCombination, x::AbstractVector) = mul!(y, A, x, true, false)
121121

122-
At_mul_B!(y::AbstractVector, A::LinearCombination, x::AbstractVector) = mul!(y, transpose(A), x)
122+
At_mul_B!(y::AbstractVector, A::LinearCombination, x::AbstractVector) = mul!(y, transpose(A), x, true, false)
123123

124-
Ac_mul_B!(y::AbstractVector, A::LinearCombination, x::AbstractVector) = mul!(y, adjoint(A), x)
124+
Ac_mul_B!(y::AbstractVector, A::LinearCombination, x::AbstractVector) = mul!(y, adjoint(A), x, true, false)

src/uniformscalingmap.jl

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ Base.:(*)(A::UniformScalingMap, x::AbstractVector) =
4343
# multiplication with vector/matrix
4444
for Atype in (AbstractVector, AbstractMatrix)
4545
@eval Base.@propagate_inbounds function LinearAlgebra.mul!(y::$Atype, J::UniformScalingMap, x::$Atype,
46-
α::Number=true, β::Number=false)
46+
α::Number, β::Number)
4747
@boundscheck check_dim_mul(y, J, x)
4848
_scaling!(y, J.λ, x, α, β)
4949
return y
@@ -88,11 +88,10 @@ function _scaling!(y, λ::Number, x, α::Number, β::Number)
8888
end # α-cases
8989
end # function _scaling!
9090

91-
A_mul_B!(y::AbstractVector, A::UniformScalingMap, x::AbstractVector) = mul!(y, A, x)
91+
A_mul_B!(y::AbstractVector, A::UniformScalingMap, x::AbstractVector) = mul!(y, A, x, true, false)
9292
At_mul_B!(y::AbstractVector, A::UniformScalingMap, x::AbstractVector) = A_mul_B!(y, transpose(A), x)
9393
Ac_mul_B!(y::AbstractVector, A::UniformScalingMap, x::AbstractVector) = A_mul_B!(y, adjoint(A), x)
9494

95-
9695
# combine LinearMap and UniformScaling objects in linear combinations
9796
Base.:(+)(A₁::LinearMap, A₂::UniformScaling) = A₁ + UniformScalingMap(A₂.λ, size(A₁, 1))
9897
Base.:(+)(A₁::UniformScaling, A₂::LinearMap) = UniformScalingMap(A₁.λ, size(A₂, 1)) + A₂

src/wrappedmap.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ Ac_mul_B!(y::AbstractVector, A::WrappedMap, x::AbstractVector) =
4949
if VERSION v"1.3.0-alpha.115"
5050
for Atype in (AbstractVector, AbstractMatrix)
5151
@eval Base.@propagate_inbounds LinearAlgebra.mul!(y::$Atype, A::WrappedMap, x::$Atype,
52-
α::Number=true, β::Number=false) =
52+
α::Number, β::Number) =
5353
mul!(y, A.lmap, x, α, β)
5454
end
5555
else

0 commit comments

Comments
 (0)