Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rewrite diagm with Pair's #24047

Merged
merged 2 commits into from
Oct 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,8 @@ Library improvements

* REPL Undo via Ctrl-/ and Ctrl-_

* `diagm` now accepts several diagonal index/vector `Pair`s ([#24047]).

* New function `equalto(x)`, which returns a function that compares its argument to `x`
using `isequal` ([#23812]).

Expand Down Expand Up @@ -467,10 +469,16 @@ Deprecated or removed
* The tuple-of-types form of `cfunction`, `cfunction(f, returntype, (types...))`, has been deprecated
in favor of the tuple-type form `cfunction(f, returntype, Tuple{types...})` ([#23066]).

* `diagm(v::AbstractVector, k::Integer=0)` has been deprecated in favor of
`diagm(k => v)` ([#24047]).

* `diagm(x::Number)` has been deprecated in favor of `fill(x, 1, 1)` ([#24047]).

* `diagm(A::SparseMatrixCSC)` has been deprecated in favor of
`spdiagm(sparsevec(A))` ([#23341]).

* `diagm(A::BitMatrix)` has been deprecated, use `diagm(vec(A))` instead ([#23373]).
* `diagm(A::BitMatrix)` has been deprecated, use `diagm(0 => vec(A))` or
`BitMatrix(Diagonal(vec(A)))` instead ([#23373], [#24047]).

* `ℯ` (written as `\mscre<TAB>` or `\euler<TAB>`) is now the only (by default) exported
name for Euler's number, and the type has changed from `Irrational{:e}` to
Expand Down
15 changes: 14 additions & 1 deletion base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1641,7 +1641,7 @@ import .LinAlg: diagm
@deprecate diagm(A::SparseMatrixCSC) sparse(Diagonal(sparsevec(A)))

# PR #23373
@deprecate diagm(A::BitMatrix) diagm(vec(A))
@deprecate diagm(A::BitMatrix) BitMatrix(Diagonal(vec(A)))

# PR 23341
@eval GMP @deprecate gmp_version() version() false
Expand Down Expand Up @@ -1870,6 +1870,19 @@ end
nothing
end

function diagm(v::BitVector)
depwarn(string("diagm(v::BitVector) is deprecated, use diagm(0 => v) or ",
"BitMatrix(Diagonal(v)) instead"), :diagm)
return BitMatrix(Diagonal(v))
end
function diagm(v::AbstractVector)
depwarn(string("diagm(v::AbstractVector) is deprecated, use diagm(0 => v) or ",
"Matrix(Diagonal(v)) instead"), :diagm)
return Matrix(Diagonal(v))
end
@deprecate diagm(v::AbstractVector, k::Integer) diagm(k => v)
@deprecate diagm(x::Number) fill(x, 1, 1)

# issue #20816
@deprecate strwidth textwidth
@deprecate charwidth textwidth
Expand Down
11 changes: 1 addition & 10 deletions base/linalg/bitarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ function tril(B::BitMatrix, k::Integer=0)
A
end

## diag and related
## diag

function diag(B::BitMatrix)
n = minimum(size(B))
Expand All @@ -85,15 +85,6 @@ function diag(B::BitMatrix)
v
end

function diagm(v::BitVector)
n = length(v)
a = falses(n, n)
for i=1:n
a[i,i] = v[i]
end
a
end

## norm and rank

svd(A::BitMatrix) = svd(float(A))
Expand Down
47 changes: 36 additions & 11 deletions base/linalg/dense.jl
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,8 @@ diagind(A::AbstractMatrix, k::Integer=0) = diagind(size(A,1), size(A,2), k)
diag(M, k::Integer=0)

The `k`th diagonal of a matrix, as a vector.
Use [`diagm`](@ref) to construct a diagonal matrix.

See also: [`diagm`](@ref)

# Examples
```jldoctest
Expand All @@ -265,29 +266,53 @@ julia> diag(A,1)
diag(A::AbstractMatrix, k::Integer=0) = A[diagind(A,k)]

"""
diagm(v, k::Integer=0)
diagm(kv::Pair{<:Integer,<:AbstractVector}...)

Construct a square matrix from `Pair`s of diagonals and vectors.
Vector `kv.second` will be placed on the `kv.first` diagonal.
`diagm` constructs a full matrix; if you want storage-efficient
versions with fast arithmetic, see [`Diagonal`](@ref), [`Bidiagonal`](@ref)
[`Tridiagonal`](@ref) and [`SymTridiagonal`](@ref).

Construct a matrix by placing `v` on the `k`th diagonal. This constructs a full matrix; if
you want a storage-efficient version with fast arithmetic, use [`Diagonal`](@ref) instead.
See also: [`spdiagm`](@ref)

# Examples
```jldoctest
julia> diagm([1,2,3],1)
julia> diagm(1 => [1,2,3])
4×4 Array{Int64,2}:
0 1 0 0
0 0 2 0
0 0 0 3
0 0 0 0

julia> diagm(1 => [1,2,3], -1 => [4,5])
4×4 Array{Int64,2}:
0 1 0 0
4 0 2 0
0 5 0 3
0 0 0 0
```
"""
function diagm(v::AbstractVector{T}, k::Integer=0) where T
n = length(v) + abs(k)
A = zeros(T,n,n)
A[diagind(A,k)] = v
A
function diagm(kv::Pair{<:Integer,<:AbstractVector}...)
A = diagm_container(kv...)
for p in kv
inds = diagind(A, p.first)
for (i, val) in enumerate(p.second)
A[inds[i]] += val
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why += rather than =?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be consistent with spdiagm, since combine in sparse defaults to +. See #24047 (comment) . Do you have another opinion about this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, do IIUC that its purpose is e.g. [sp]diagm(0 => [1, 2, 3], 0 => [4, 5, 6]) yielding [sp]diagm(0 => [5, 7, 9])? If so, cheers, and thanks! :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think it is the most natural thing to do.

end
end
return A
end
function diagm_container(kv::Pair{<:Integer,<:AbstractVector}...)
T = promote_type(map(x -> eltype(x.second), kv)...)
n = mapreduce(x -> length(x.second) + abs(x.first), max, kv)
return zeros(T, n, n)
end
function diagm_container(kv::Pair{<:Integer,<:BitVector}...)
n = mapreduce(x -> length(x.second) + abs(x.first), max, kv)
return falses(n, n)
end

diagm(x::Number) = (X = Matrix{typeof(x)}(1,1); X[1,1] = x; X)

function trace(A::Matrix{T}) where T
n = checksquare(A)
Expand Down
2 changes: 1 addition & 1 deletion base/linalg/diagonal.jl
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Diagonal{T}(V::AbstractVector) where {T} = Diagonal{T}(convert(AbstractVector{T}
convert(::Type{Diagonal{T}}, D::Diagonal{T}) where {T} = D
convert(::Type{Diagonal{T}}, D::Diagonal) where {T} = Diagonal{T}(convert(AbstractVector{T}, D.diag))
convert(::Type{AbstractMatrix{T}}, D::Diagonal) where {T} = convert(Diagonal{T}, D)
convert(::Type{Matrix}, D::Diagonal) = diagm(D.diag)
convert(::Type{Matrix}, D::Diagonal) = diagm(0 => D.diag)
convert(::Type{Array}, D::Diagonal) = convert(Matrix, D)
full(D::Diagonal) = convert(Array, D)

Expand Down
6 changes: 3 additions & 3 deletions base/linalg/generic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -739,13 +739,13 @@ of the [`eltype`](@ref) of `A`.
julia> rank(eye(3))
3

julia> rank(diagm([1, 0, 2]))
julia> rank(diagm(0 => [1, 0, 2]))
2

julia> rank(diagm([1, 0.001, 2]), 0.1)
julia> rank(diagm(0 => [1, 0.001, 2]), 0.1)
2

julia> rank(diagm([1, 0.001, 2]), 0.00001)
julia> rank(diagm(0 => [1, 0.001, 2]), 0.00001)
3
```
"""
Expand Down
10 changes: 5 additions & 5 deletions base/linalg/lapack.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1161,17 +1161,17 @@ of `A`. `fact` may be `E`, in which case `A` will be equilibrated and copied
to `AF`; `F`, in which case `AF` and `ipiv` from a previous `LU` factorization
are inputs; or `N`, in which case `A` will be copied to `AF` and then
factored. If `fact = F`, `equed` may be `N`, meaning `A` has not been
equilibrated; `R`, meaning `A` was multiplied by `diagm(R)` from the left;
`C`, meaning `A` was multiplied by `diagm(C)` from the right; or `B`, meaning
`A` was multiplied by `diagm(R)` from the left and `diagm(C)` from the right.
equilibrated; `R`, meaning `A` was multiplied by `Diagonal(R)` from the left;
`C`, meaning `A` was multiplied by `Diagonal(C)` from the right; or `B`, meaning
`A` was multiplied by `Diagonal(R)` from the left and `Diagonal(C)` from the right.
If `fact = F` and `equed = R` or `B` the elements of `R` must all be positive.
If `fact = F` and `equed = C` or `B` the elements of `C` must all be positive.

Returns the solution `X`; `equed`, which is an output if `fact` is not `N`,
and describes the equilibration that was performed; `R`, the row equilibration
diagonal; `C`, the column equilibration diagonal; `B`, which may be overwritten
with its equilibrated form `diagm(R)*B` (if `trans = N` and `equed = R,B`) or
`diagm(C)*B` (if `trans = T,C` and `equed = C,B`); `rcond`, the reciprocal
with its equilibrated form `Diagonal(R)*B` (if `trans = N` and `equed = R,B`) or
`Diagonal(C)*B` (if `trans = T,C` and `equed = C,B`); `rcond`, the reciprocal
condition number of `A` after equilbrating; `ferr`, the forward error bound for
each solution vector in `X`; `berr`, the forward error bound for each solution
vector in `X`; and `work`, the reciprocal pivot growth factor.
Expand Down
16 changes: 8 additions & 8 deletions base/linalg/svd.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ end
Compute the singular value decomposition (SVD) of `A` and return an `SVD` object.

`U`, `S`, `V` and `Vt` can be obtained from the factorization `F` with `F[:U]`,
`F[:S]`, `F[:V]` and `F[:Vt]`, such that `A = U*diagm(S)*Vt`.
`F[:S]`, `F[:V]` and `F[:Vt]`, such that `A = U*Diagonal(S)*Vt`.
The algorithm produces `Vt` and hence `Vt` is more efficient to extract than `V`.
The singular values in `S` are sorted in descending order.

Expand All @@ -52,7 +52,7 @@ julia> A = [1. 0. 0. 0. 2.; 0. 0. 3. 0. 0.; 0. 0. 0. 0. 0.; 0. 2. 0. 0. 0.]
julia> F = svdfact(A)
Base.LinAlg.SVD{Float64,Float64,Array{Float64,2}}([0.0 1.0 0.0 0.0; 1.0 0.0 0.0 0.0; 0.0 0.0 0.0 -1.0; 0.0 0.0 1.0 0.0], [3.0, 2.23607, 2.0, 0.0], [-0.0 0.0 … -0.0 0.0; 0.447214 0.0 … 0.0 0.894427; -0.0 1.0 … -0.0 0.0; 0.0 0.0 … 1.0 0.0])

julia> F[:U] * diagm(F[:S]) * F[:Vt]
julia> F[:U] * Diagonal(F[:S]) * F[:Vt]
4×5 Array{Float64,2}:
1.0 0.0 0.0 0.0 2.0
0.0 0.0 3.0 0.0 0.0
Expand All @@ -71,7 +71,7 @@ svdfact(x::Integer; thin::Bool=true) = svdfact(float(x), thin=thin)
svd(A; thin::Bool=true) -> U, S, V

Computes the SVD of `A`, returning `U`, vector `S`, and `V` such that
`A == U*diagm(S)*V'`. The singular values in `S` are sorted in descending order.
`A == U*Diagonal(S)*V'`. The singular values in `S` are sorted in descending order.

If `thin=true` (default), a thin SVD is returned. For a ``M \\times N`` matrix
`A`, `U` is ``M \\times M`` for a full SVD (`thin=false`) and
Expand All @@ -93,7 +93,7 @@ julia> A = [1. 0. 0. 0. 2.; 0. 0. 3. 0. 0.; 0. 0. 0. 0. 0.; 0. 2. 0. 0. 0.]
julia> U, S, V = svd(A)
([0.0 1.0 0.0 0.0; 1.0 0.0 0.0 0.0; 0.0 0.0 0.0 -1.0; 0.0 0.0 1.0 0.0], [3.0, 2.23607, 2.0, 0.0], [-0.0 0.447214 -0.0 0.0; 0.0 0.0 1.0 0.0; … ; -0.0 0.0 -0.0 1.0; 0.0 0.894427 0.0 0.0])

julia> U*diagm(S)*V'
julia> U*Diagonal(S)*V'
4×5 Array{Float64,2}:
1.0 0.0 0.0 0.0 2.0
0.0 0.0 3.0 0.0 0.0
Expand Down Expand Up @@ -266,17 +266,17 @@ function getindex(obj::GeneralizedSVD{T}, d::Symbol) where T
elseif d == :D1
m = size(obj.U, 1)
if m - obj.k - obj.l >= 0
return [eye(T, obj.k) zeros(T, obj.k, obj.l); zeros(T, obj.l, obj.k) diagm(obj.a[obj.k + 1:obj.k + obj.l]); zeros(T, m - obj.k - obj.l, obj.k + obj.l)]
return [eye(T, obj.k) zeros(T, obj.k, obj.l); zeros(T, obj.l, obj.k) Diagonal(obj.a[obj.k + 1:obj.k + obj.l]); zeros(T, m - obj.k - obj.l, obj.k + obj.l)]
else
return [eye(T, m, obj.k) [zeros(T, obj.k, m - obj.k); diagm(obj.a[obj.k + 1:m])] zeros(T, m, obj.k + obj.l - m)]
return [eye(T, m, obj.k) [zeros(T, obj.k, m - obj.k); Diagonal(obj.a[obj.k + 1:m])] zeros(T, m, obj.k + obj.l - m)]
end
elseif d == :D2
m = size(obj.U, 1)
p = size(obj.V, 1)
if m - obj.k - obj.l >= 0
return [zeros(T, obj.l, obj.k) diagm(obj.b[obj.k + 1:obj.k + obj.l]); zeros(T, p - obj.l, obj.k + obj.l)]
return [zeros(T, obj.l, obj.k) Diagonal(obj.b[obj.k + 1:obj.k + obj.l]); zeros(T, p - obj.l, obj.k + obj.l)]
else
return [zeros(T, p, obj.k) [diagm(obj.b[obj.k + 1:m]); zeros(T, obj.k + p - m, m - obj.k)] [zeros(T, m - obj.k, obj.k + obj.l - m); eye(T, obj.k + p - m, obj.k + obj.l - m)]]
return [zeros(T, p, obj.k) [Diagonal(obj.b[obj.k + 1:m]); zeros(T, obj.k + p - m, m - obj.k)] [zeros(T, m - obj.k, obj.k + obj.l - m); eye(T, obj.k + p - m, obj.k + obj.l - m)]]
end
elseif d == :R
return obj.R
Expand Down
2 changes: 1 addition & 1 deletion base/linalg/uniformscaling.jl
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ function isapprox(J::UniformScaling,A::AbstractMatrix;
rtol::Real=rtoldefault(promote_leaf_eltypes(A),eltype(J),atol),
nans::Bool=false, norm::Function=vecnorm)
n = checksquare(A)
Jnorm = norm === vecnorm ? abs(J.λ)*sqrt(n) : (norm === Base.norm ? abs(J.λ) : norm(diagm(fill(J.λ, n))))
Jnorm = norm === vecnorm ? abs(J.λ)*sqrt(n) : (norm === Base.norm ? abs(J.λ) : norm(Diagonal(fill(J.λ, n))))
return norm(A - J) <= max(atol, rtol*max(norm(A), Jnorm))
end
isapprox(A::AbstractMatrix,J::UniformScaling;kwargs...) = isapprox(J,A;kwargs...)
Expand Down
4 changes: 2 additions & 2 deletions test/arrayops.jl
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ end
tmp[rng...] = A[rng...]
@test tmp == cat(3,zeros(Int,2,3),[0 0 0; 0 47 52],zeros(Int,2,3),[0 0 0; 0 127 132])

@test cat([1,2],1,2,3.,4.,5.) == diagm([1,2,3.,4.,5.])
@test cat([1,2],1,2,3.,4.,5.) == diagm(0 => [1,2,3.,4.,5.])
blk = [1 2;3 4]
tmp = cat([1,3],blk,blk)
@test tmp[1:2,1:2,1] == blk
Expand Down Expand Up @@ -2044,7 +2044,7 @@ end # module AutoRetType
@testset "concatenations of dense matrices/vectors yield dense matrices/vectors" begin
N = 4
densevec = ones(N)
densemat = diagm(ones(N))
densemat = diagm(0 => ones(N))
# Test that concatenations of homogeneous pairs of either dense matrices or dense vectors
# (i.e., Matrix-Matrix concatenations, and Vector-Vector concatenations) yield dense arrays
for densearray in (densevec, densemat)
Expand Down
6 changes: 5 additions & 1 deletion test/bitarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1406,7 +1406,11 @@ timesofar("cat")
@check_bit_operation qr(b1)

b1 = bitrand(v1)
@check_bit_operation diagm(b1) BitMatrix
@check_bit_operation diagm(0 => b1) BitMatrix

b1 = bitrand(v1)
b2 = bitrand(v1)
@check_bit_operation diagm(-1 => b1, 1 => b2) BitMatrix

b1 = bitrand(n1, n1)
@check_bit_operation diag(b1)
Expand Down
2 changes: 1 addition & 1 deletion test/broadcast.jl
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ for arr in (identity, as_sub)
@test A == fill(7, 2, 2)
A = arr(zeros(3,3))
broadcast_setindex!(A, 10:12, 1:3, 1:3)
@test A == diagm(10:12)
@test A == diagm(0 => 10:12)
@test_throws BoundsError broadcast_setindex!(A, 7, [1,-1], [1 2])

for f in ((==), (<) , (!=), (<=))
Expand Down
2 changes: 1 addition & 1 deletion test/linalg/arnoldi.jl
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ end
# Ensure singular values from svds are in
# the correct order
@testset "singular values ordered correctly" begin
B = sparse(diagm([1.0, 2.0, 34.0, 5.0, 6.0]))
B = sparse(Diagonal([1.0, 2.0, 34.0, 5.0, 6.0]))
S3 = svds(B, ritzvec=false, nsv=2)
@test S3[1][:S] ≈ [34.0, 6.0]
S4 = svds(B, nsv=2)
Expand Down
12 changes: 6 additions & 6 deletions test/linalg/bidiag.jl
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,12 @@ srand(1)
@testset "Constructor and basic properties" begin
@test size(T, 1) == size(T, 2) == n
@test size(T) == (n, n)
@test Array(T) == diagm(dv) + diagm(ev, uplo == :U ? 1 : -1)
@test Array(T) == diagm(0 => dv, (uplo == :U ? 1 : -1) => ev)
@test Bidiagonal(Array(T), uplo) == T
@test big.(T) == T
@test Array(abs.(T)) == abs.(diagm(dv)) + abs.(diagm(ev, uplo == :U ? 1 : -1))
@test Array(real(T)) == real(diagm(dv)) + real(diagm(ev, uplo == :U ? 1 : -1))
@test Array(imag(T)) == imag(diagm(dv)) + imag(diagm(ev, uplo == :U ? 1 : -1))
@test Array(abs.(T)) == abs.(diagm(0 => dv, (uplo == :U ? 1 : -1) => ev))
@test Array(real(T)) == real(diagm(0 => dv, (uplo == :U ? 1 : -1) => ev))
@test Array(imag(T)) == imag(diagm(0 => dv, (uplo == :U ? 1 : -1) => ev))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changes are a lovely illustration of how this API change improves things :).

end

@testset for func in (conj, transpose, adjoint)
Expand Down Expand Up @@ -241,7 +241,7 @@ srand(1)
Test.test_approx_eq_modphase(u1, u2)
Test.test_approx_eq_modphase(v1, v2)
end
@test 0 ≈ vecnorm(u2*diagm(d2)*v2'-Tfull) atol=n*max(n^2*eps(relty),vecnorm(u1*diagm(d1)*v1'-Tfull))
@test 0 ≈ vecnorm(u2*Diagonal(d2)*v2'-Tfull) atol=n*max(n^2*eps(relty),vecnorm(u1*Diagonal(d1)*v1'-Tfull))
@inferred svdvals(T)
@inferred svd(T)
end
Expand All @@ -264,7 +264,7 @@ srand(1)
TriSym = SymTridiagonal(T.dv, T.ev)
@test Array(TriSym*T) ≈ Array(TriSym)*Array(T)
# test pass-through of A_mul_B! for AbstractTriangular*Bidiagonal
Tri = UpperTriangular(diagm(T.ev, 1))
Tri = UpperTriangular(diagm(1 => T.ev))
@test Array(Tri*T) ≈ Array(Tri)*Array(T)
end

Expand Down
2 changes: 1 addition & 1 deletion test/linalg/blas.jl
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ srand(100)
bH = zeros(elty,2,n)
bH[1,2:n] = ev
bH[2,:] = dv
fullH = diagm(dv) + diagm(conj(ev),-1) + diagm(ev,1)
fullH = diagm(0 => dv, -1 => conj(ev), 1 => ev)
@test BLAS.hbmv('U',1,bH,x) ≈ fullH*x
end
end
Expand Down
Loading