Skip to content

Commit 2292d21

Browse files
authored
Julia 1.0 tests, sparse convert, mutating matmul in sparse (#28)
* minor Julia 1.0 edits * travis test also against Julia v1.0 * removed obsolete precompilation statement * added sparse matrix convert * sparse matrix construction uses mutating multiplication * expanded README and tests * consistent code style
1 parent 784d420 commit 2292d21

File tree

9 files changed

+51
-45
lines changed

9 files changed

+51
-45
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ os:
55
- osx
66
julia:
77
- 0.7
8+
- 1.0
89
- nightly
910

1011
matrix:

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99

1010
A Julia package for defining and working with linear maps, also known as linear transformations or linear operators acting on vectors. The only requirement for a LinearMap is that it can act on a vector (by multiplication) efficiently.
1111

12+
## What's new in v2.2.0
13+
* Fully Julia v0.7/v1.0 compatible.
14+
* A `convert(SparseMatrixCSC, A::LinearMap)` function, that calls the `sparse` matrix generating function.
15+
1216
## What's new in v2.1.0
1317
* Fully Julia v0.7 compatible; dropped compatibility for previous versions of Julia from LinearMaps.jl v2.0.0 on.
1418
* A 5-argument version for `mul!(y, A::LinearMap, x, α=1, β=0)`, which computes `y := α * A * x + β * y` and implements the usual 3-argument `mul!(y, A, x)` for the default `α` and `β`.

src/LinearMaps.jl

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
__precompile__(true)
21
module LinearMaps
32

43
export LinearMap
54

65
using LinearAlgebra
7-
import SparseArrays
6+
using SparseArrays
87

98
abstract type LinearMap{T} end
109

@@ -72,33 +71,34 @@ function Base.Matrix(A::LinearMap)
7271
return mat
7372
end
7473

75-
Base.Array(A::LinearMap) = Base.Matrix(A)
76-
Base.convert(::Type{Matrix}, A:: LinearMap) = Matrix(A)
77-
Base.convert(::Type{Array}, A:: LinearMap) = Matrix(A)
74+
Base.Array(A::LinearMap) = Matrix(A)
75+
Base.convert(::Type{Matrix}, A::LinearMap) = Matrix(A)
76+
Base.convert(::Type{Array}, A::LinearMap) = Matrix(A)
77+
Base.convert(::Type{SparseMatrixCSC}, A::LinearMap) = sparse(A)
7878

7979
# sparse: create sparse matrix representation of LinearMap
80-
function SparseArrays.sparse(A::LinearMap)
80+
function SparseArrays.sparse(A::LinearMap{T}) where {T}
8181
M, N = size(A)
82-
T = eltype(A)
8382
rowind = Int[]
8483
nzval = T[]
8584
colptr = Vector{Int}(undef, N+1)
8685
v = fill(zero(T), N)
86+
Av = Vector{T}(undef, M)
8787

8888
for i = 1:N
8989
v[i] = one(T)
90-
Lv = A * v
91-
js = findall(!iszero, Lv)
90+
mul!(Av, A, v)
91+
js = findall(!iszero, Av)
9292
colptr[i] = length(nzval) + 1
9393
if length(js) > 0
9494
append!(rowind, js)
95-
append!(nzval, Lv[js])
95+
append!(nzval, Av[js])
9696
end
9797
v[i] = zero(T)
9898
end
9999
colptr[N+1] = length(nzval) + 1
100100

101-
return SparseArrays.SparseMatrixCSC(M, N, colptr, rowind, nzval)
101+
return SparseMatrixCSC(M, N, colptr, rowind, nzval)
102102
end
103103

104104
include("transpose.jl") # transposing linear maps
@@ -120,7 +120,7 @@ on length `N` vectors and producing length `M` vectors (with default value `N=M`
120120
also the `eltype` `T` of the corresponding matrix representation needs to be specified, i.e.
121121
whether the action of `f` on a vector will be similar to e.g. multiplying by numbers of type `T`.
122122
If not specified, the devault value `T=Float64` will be assumed. Optionally, a corresponding
123-
function `fc` can be specified that implements the (conjugate) transpose of `f`.
123+
function `fc` can be specified that implements the transpose/adjoint of `f`.
124124
125125
The keyword arguments and their default values for functions `f` are
126126
* issymmetric::Bool = false : whether `A` or `f` acts as a symmetric matrix

src/composition.jl

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ struct CompositeMap{T, As<:Tuple{Vararg{LinearMap}}} <: LinearMap{T}
33
function CompositeMap{T, As}(maps::As) where {T, As}
44
N = length(maps)
55
for n = 2:N
6-
size(maps[n],2) == size(maps[n-1],1) || throw(DimensionMismatch("CompositeMap"))
6+
size(maps[n], 2) == size(maps[n-1], 1) || throw(DimensionMismatch("CompositeMap"))
77
end
88
for n = 1:N
99
promote_type(T, eltype(maps[n])) == T || throw(InexactError())
1010
end
11-
new{T,As}(maps)
11+
new{T, As}(maps)
1212
end
1313
end
1414
CompositeMap{T}(maps::As) where {T, As<:Tuple{Vararg{LinearMap}}} = CompositeMap{T, As}(maps)
@@ -21,52 +21,52 @@ Base.isreal(A::CompositeMap) = all(isreal, A.maps) # sufficient but not necessar
2121
function LinearAlgebra.issymmetric(A::CompositeMap)
2222
N = length(A.maps)
2323
if isodd(N)
24-
issymmetric(A.maps[div(N+1,2)]) || return false
24+
issymmetric(A.maps[div(N+1, 2)]) || return false
2525
end
26-
for n = 1:div(N,2)
26+
for n = 1:div(N, 2)
2727
A.maps[n] == transpose(A.maps[N-n+1]) || return false
2828
end
2929
return true
3030
end
3131
function LinearAlgebra.ishermitian(A::CompositeMap)
3232
N = length(A.maps)
3333
if isodd(N)
34-
ishermitian(A.maps[div(N+1,2)]) || return false
34+
ishermitian(A.maps[div(N+1, 2)]) || return false
3535
end
36-
for n = 1:div(N,2)
36+
for n = 1:div(N, 2)
3737
A.maps[n] == adjoint(A.maps[N-n+1]) || return false
3838
end
3939
return true
4040
end
4141
function LinearAlgebra.isposdef(A::CompositeMap)
4242
N = length(A.maps)
4343
if isodd(N)
44-
isposdef(A.maps[div(N+1,2)]) || return false
44+
isposdef(A.maps[div(N+1, 2)]) || return false
4545
end
46-
for n = 1:div(N,2)
46+
for n = 1:div(N, 2)
4747
A.maps[n] == adjoint(A.maps[N-n+1]) || return false
4848
end
4949
return true
5050
end
5151

5252
# composition of linear maps
5353
function Base.:(*)(A1::CompositeMap, A2::CompositeMap)
54-
size(A1,2) == size(A2,1) || throw(DimensionMismatch("*"))
55-
T = promote_type(eltype(A1),eltype(A2))
54+
size(A1, 2) == size(A2, 1) || throw(DimensionMismatch("*"))
55+
T = promote_type(eltype(A1), eltype(A2))
5656
return CompositeMap{T}(tuple(A2.maps..., A1.maps...))
5757
end
5858
function Base.:(*)(A1::LinearMap, A2::CompositeMap)
59-
size(A1,2) == size(A2,1) || throw(DimensionMismatch("*"))
60-
T = promote_type(eltype(A1),eltype(A2))
59+
size(A1, 2) == size(A2, 1) || throw(DimensionMismatch("*"))
60+
T = promote_type(eltype(A1), eltype(A2))
6161
return CompositeMap{T}(tuple(A2.maps..., A1))
6262
end
6363
function Base.:(*)(A1::CompositeMap, A2::LinearMap)
64-
size(A1,2) == size(A2,1) || throw(DimensionMismatch("*"))
65-
T = promote_type(eltype(A1),eltype(A2))
64+
size(A1, 2) == size(A2, 1) || throw(DimensionMismatch("*"))
65+
T = promote_type(eltype(A1), eltype(A2))
6666
return CompositeMap{T}(tuple(A2, A1.maps...))
6767
end
6868
function Base.:(*)(A1::LinearMap, A2::LinearMap)
69-
size(A1,2) == size(A2,1) || throw(DimensionMismatch("*"))
69+
size(A1, 2) == size(A2, 1) || throw(DimensionMismatch("*"))
7070
T = promote_type(eltype(A1),eltype(A2))
7171
return CompositeMap{T}(tuple(A2, A1))
7272
end
@@ -87,10 +87,10 @@ function A_mul_B!(y::AbstractVector, A::CompositeMap, x::AbstractVector)
8787
A_mul_B!(dest, A.maps[1], x)
8888
source = dest
8989
if N>2
90-
dest = Array{T}(undef, size(A.maps[2],1))
90+
dest = Array{T}(undef, size(A.maps[2], 1))
9191
end
9292
for n=2:N-1
93-
resize!(dest, size(A.maps[n],1))
93+
resize!(dest, size(A.maps[n], 1))
9494
A_mul_B!(dest, A.maps[n], source)
9595
dest, source = source, dest # alternate dest and source
9696
end

src/functionmap.jl

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
struct FunctionMap{T,F1,F2} <: LinearMap{T}
1+
struct FunctionMap{T, F1, F2} <: LinearMap{T}
22
f::F1
33
fc::F2
44
M::Int
@@ -12,8 +12,8 @@ function FunctionMap{T}(f::F1, fc::F2, M::Int, N::Int;
1212
ismutating::Bool = _ismutating(f),
1313
issymmetric::Bool = false,
1414
ishermitian::Bool = (T<:Real && issymmetric),
15-
isposdef::Bool = false) where {T,F1,F2}
16-
FunctionMap{T,F1,F2}(f, fc, M, N, ismutating, issymmetric, ishermitian, isposdef)
15+
isposdef::Bool = false) where {T, F1, F2}
16+
FunctionMap{T, F1, F2}(f, fc, M, N, ismutating, issymmetric, ishermitian, isposdef)
1717
end
1818

1919
# additional constructors
@@ -22,11 +22,11 @@ FunctionMap{T}(f, M::Int, N::Int; kwargs...) where {T} = FunctionMap{T}(f, nothi
2222
FunctionMap{T}(f, fc, M::Int; kwargs...) where {T} = FunctionMap{T}(f, fc, M, M; kwargs...)
2323

2424
# show
25-
function Base.show(io::IO, A::FunctionMap{T,F,Nothing}) where {T,F}
26-
print(io,"LinearMaps.FunctionMap{$T}($(A.f), $(A.M), $(A.N); ismutating=$(A._ismutating), issymmetric=$(A._issymmetric), ishermitian=$(A._ishermitian), isposdef=$(A._isposdef))")
25+
function Base.show(io::IO, A::FunctionMap{T, F, Nothing}) where {T, F}
26+
print(io, "LinearMaps.FunctionMap{$T}($(A.f), $(A.M), $(A.N); ismutating=$(A._ismutating), issymmetric=$(A._issymmetric), ishermitian=$(A._ishermitian), isposdef=$(A._isposdef))")
2727
end
2828
function Base.show(io::IO, A::FunctionMap{T}) where {T}
29-
print(io,"LinearMaps.FunctionMap{$T}($(A.f), $(A.fc), $(A.M), $(A.N); ismutating=$(A._ismutating), issymmetric=$(A._issymmetric), ishermitian=$(A._ishermitian), isposdef=$(A._isposdef))")
29+
print(io, "LinearMaps.FunctionMap{$T}($(A.f), $(A.fc), $(A.M), $(A.N); ismutating=$(A._ismutating), issymmetric=$(A._issymmetric), ishermitian=$(A._ishermitian), isposdef=$(A._isposdef))")
3030
end
3131

3232
# properties
@@ -61,7 +61,7 @@ function At_mul_B!(y::AbstractVector, A::FunctionMap, x::AbstractVector)
6161
if !isreal(A)
6262
x = conj(x)
6363
end
64-
(ismutating(A) ? A.fc(y,x) : copyto!(y, A.fc(x)))
64+
(ismutating(A) ? A.fc(y, x) : copyto!(y, A.fc(x)))
6565
if !isreal(A)
6666
conj!(y)
6767
end

src/linearcombination.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
struct LinearCombination{T, As<:Tuple{Vararg{LinearMap}}, Ts<:Tuple} <: LinearMap{T}
22
maps::As
33
coeffs::Ts
4-
function LinearCombination{T,As,Ts}(maps::As, coeffs::Ts) where {T,As,Ts}
4+
function LinearCombination{T, As, Ts}(maps::As, coeffs::Ts) where {T, As, Ts}
55
N = length(maps)
66
N == length(coeffs) || error("Number of coefficients doesn't match number of terms")
77
sz = size(maps[1])
88
for n = 1:N
99
size(maps[n]) == sz || throw(DimensionMismatch("LinearCombination"))
1010
promote_type(T, eltype(maps[n]), typeof(coeffs[n])) == T || throw(InexactError())
1111
end
12-
new{T,As,Ts}(maps, coeffs)
12+
new{T, As, Ts}(maps, coeffs)
1313
end
1414
end
1515

@@ -32,7 +32,7 @@ function Base.:(+)(A1::LinearMap, A2::LinearCombination)
3232
T = promote_type(eltype(A1), eltype(A2))
3333
return LinearCombination{T}(tuple(A1, A2.maps...), tuple(one(T), A2.coeffs...))
3434
end
35-
Base.:(+)(A1::LinearCombination, A2::LinearMap) = +(A2,A1)
35+
Base.:(+)(A1::LinearCombination, A2::LinearMap) = +(A2, A1)
3636
function Base.:(+)(A1::LinearMap, A2::LinearMap)
3737
size(A1)==size(A2) || throw(DimensionMismatch("+"))
3838
T = promote_type(eltype(A1), eltype(A2))
@@ -53,7 +53,7 @@ function Base.:(-)(A1::LinearCombination, A2::LinearMap)
5353
T = promote_type(eltype(A1), eltype(A2))
5454
return LinearCombination{T}(tuple(A1.maps..., A2), tuple(A1.coeffs..., -one(T)))
5555
end
56-
function Base.:(-)(A1::LinearMap,A2::LinearMap)
56+
function Base.:(-)(A1::LinearMap, A2::LinearMap)
5757
size(A1) == size(A2) || throw(DimensionMismatch("-"))
5858
T = promote_type(eltype(A1), eltype(A2))
5959
return LinearCombination{T}(tuple(A1, A2), tuple(one(T), -one(T)))
@@ -83,7 +83,7 @@ function Base.:(\)(α::Number, A::LinearCombination)
8383
T = promote_type(eltype(α), eltype(A))
8484
return LinearCombination{T}(A.maps, map(x->α\x, A.coeffs))
8585
end
86-
Base.:(/)(A::LinearCombination, α::Number) = \(α,A)
86+
Base.:(/)(A::LinearCombination, α::Number) = \(α, A)
8787

8888
# comparison of LinearCombination objects
8989
Base.:(==)(A::LinearCombination, B::LinearCombination) = (eltype(A)==eltype(B) && A.maps==B.maps && A.coeffs==B.coeffs)

src/transpose.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ LinearAlgebra.adjoint(A::LinearMap{<:Real}) = transpose(A)
1414
LinearAlgebra.adjoint(A::LinearMap) = AdjointMap(A)
1515

1616
# properties
17-
Base.size(A::Union{TransposeMap, AdjointMap}) = (size(A.lmap,2), size(A.lmap,1))
17+
Base.size(A::Union{TransposeMap, AdjointMap}) = (size(A.lmap, 2), size(A.lmap, 1))
1818
LinearAlgebra.issymmetric(A::Union{TransposeMap, AdjointMap}) = issymmetric(A.lmap)
1919
LinearAlgebra.ishermitian(A::Union{TransposeMap, AdjointMap}) = ishermitian(A.lmap)
2020
LinearAlgebra.isposdef(A::Union{TransposeMap, AdjointMap}) = isposdef(A.lmap)

src/wrappedmap.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ function WrappedMap(lmap::Union{AbstractMatrix{T}, LinearMap{T}};
88
issymmetric::Bool = issymmetric(lmap),
99
ishermitian::Bool = ishermitian(lmap),
1010
isposdef::Bool = isposdef(lmap)) where {T}
11-
WrappedMap{T,typeof(lmap)}(lmap, issymmetric, ishermitian, isposdef)
11+
WrappedMap{T, typeof(lmap)}(lmap, issymmetric, ishermitian, isposdef)
1212
end
1313
function WrappedMap{T}(lmap::Union{AbstractMatrix, LinearMap};
1414
issymmetric::Bool = issymmetric(lmap),
1515
ishermitian::Bool = ishermitian(lmap),
1616
isposdef::Bool = isposdef(lmap)) where {T}
17-
WrappedMap{T,typeof(lmap)}(lmap, issymmetric, ishermitian, isposdef)
17+
WrappedMap{T, typeof(lmap)}(lmap, issymmetric, ishermitian, isposdef)
1818
end
1919

2020
# properties

test/runtests.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ B = LinearMap(Hermitian(rand(ComplexF64, 10, 10)))
9595
@test B == B'
9696

9797
# test sparse conversions
98-
@test sparse(M) == SparseArrays.sparse(Array(M))
98+
@test sparse(M) == sparse(Array(M))
99+
@test convert(SparseMatrixCSC, M) == sparse(Array(M))
99100

100101
B = copy(A)
101102
B[rand(1:length(A), 30)] .= 0.

0 commit comments

Comments
 (0)