diff --git a/NEWS.md b/NEWS.md index 9d0f3fa4069f0a..077c6bec9d5037 100644 --- a/NEWS.md +++ b/NEWS.md @@ -208,6 +208,10 @@ Library improvements * `notify` now returns a count of tasks woken up ([#19841]). + * Introduced a wrapper type for lazy complex conjugation of arrays, `ConjArray`. + Currently, it is used by default for the new `RowVector` type only, and + enforces that both `transpose(vec)` and `ctranspose(vec)` are views not copies. + Compiler/Runtime improvements ----------------------------- diff --git a/base/exports.jl b/base/exports.jl index d9b0b745312eba..671870d65e8283 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -49,6 +49,7 @@ export Complex128, Complex64, Complex32, + ConjArray, DenseMatrix, DenseVecOrMat, DenseVector, diff --git a/base/linalg/conjarray.jl b/base/linalg/conjarray.jl new file mode 100644 index 00000000000000..52815ccd14cd9d --- /dev/null +++ b/base/linalg/conjarray.jl @@ -0,0 +1,45 @@ +# This file is a part of Julia. License is MIT: http://julialang.org/license + +""" + ConjArray(array) + +A lazy-view wrapper of an `AbstractArray`, taking the elementwise complex +conjugate. This type is usually constructed (and unwrapped) via the `conj()` +function (or related `ctranspose()`), but currently this is the default behavior +for `RowVector` only. +""" +immutable ConjArray{T, N, A <: AbstractArray} <: AbstractArray{T, N} + parent::A +end + +@inline ConjArray{T,N}(a::AbstractArray{T,N}) = ConjArray{conj_type(T), N, typeof(a)}(a) + +# This type can cause the element type to change under conjugation - e.g. an array of complex arrays. +@inline conj_type(x) = conj_type(typeof(x)) +@inline conj_type{T}(::Type{T}) = promote_op(conj, T) + +@inline parent(c::ConjArray) = c.parent +@inline parent_type(c::ConjArray) = parent_type(typeof(c)) +@inline parent_type{T,N,A}(::Type{ConjArray{T,N,A}}) = A + +@inline size(a::ConjArray) = size(a.parent) +linearindexing{CA <: ConjArray}(::Union{CA,Type{CA}}) = linearindexing(parent_type(CA)) + +@propagate_inbounds getindex{T,N}(a::ConjArray{T,N}, i::Int) = conj(getindex(a.parent, i)) +@propagate_inbounds getindex{T,N}(a::ConjArray{T,N}, i::Vararg{Int,N}) = conj(getindex(a.parent, i...)) +@propagate_inbounds setindex!{T,N}(a::ConjArray{T,N}, v, i::Int) = setindex!(a.parent, conj(v), i) +@propagate_inbounds setindex!{T,N}(a::ConjArray{T,N}, v, i::Vararg{Int,N}) = setindex!(a.parent, conj(v), i...) + +# Currently, this is default behavior for RowVector only +""" + conj(rowvector) + +Returns a `ConjArray` lazy view of the input, where each element is conjugated. +""" +@inline conj(rv::RowVector) = RowVector(_conj(parent(rv))) +@inline conj(a::ConjArray) = parent(a) + +@inline _conj(a::AbstractArray) = ConjArray(a) +@inline _conj{T<:Real}(a::AbstractArray{T}) = a +@inline _conj(a::ConjArray) = parent(a) +@inline _conj{T<:Real}(a::ConjArray{T}) = parent(a) diff --git a/base/linalg/linalg.jl b/base/linalg/linalg.jl index 2c0d94b7c19e03..6c72636184907a 100644 --- a/base/linalg/linalg.jl +++ b/base/linalg/linalg.jl @@ -22,6 +22,7 @@ export # Types RowVector, + ConjArray, SymTridiagonal, Tridiagonal, Bidiagonal, @@ -239,6 +240,7 @@ copy_oftype{T,N,S}(A::AbstractArray{T,N}, ::Type{S}) = convert(AbstractArray{S,N include("transpose.jl") include("rowvector.jl") +include("conjarray.jl") include("exceptions.jl") include("generic.jl") diff --git a/base/linalg/rowvector.jl b/base/linalg/rowvector.jl index 0ee1aeb1ba7032..c2cae5aed9ce67 100644 --- a/base/linalg/rowvector.jl +++ b/base/linalg/rowvector.jl @@ -67,18 +67,18 @@ julia> transpose(v) ``` """ @inline transpose(vec::AbstractVector) = RowVector(vec) -@inline ctranspose{T}(vec::AbstractVector{T}) = RowVector(conj(vec)) +@inline ctranspose{T}(vec::AbstractVector{T}) = RowVector(_conj(vec)) @inline ctranspose{T<:Real}(vec::AbstractVector{T}) = RowVector(vec) @inline transpose(rowvec::RowVector) = rowvec.vec -@inline ctranspose{T}(rowvec::RowVector{T}) = conj(rowvec.vec) +@inline ctranspose{T}(rowvec::RowVector{T}) = _conj(rowvec.vec) @inline ctranspose{T<:Real}(rowvec::RowVector{T}) = rowvec.vec parent(rowvec::RowVector) = rowvec.vec # Strictly, these are unnecessary but will make things stabler if we introduce # a "view" for conj(::AbstractArray) -@inline conj(rowvec::RowVector) = RowVector(conj(rowvec.vec)) +@inline conj(rowvec::RowVector) = RowVector(_conj(rowvec.vec)) @inline conj{T<:Real}(rowvec::RowVector{T}) = rowvec # AbstractArray interface diff --git a/doc/src/stdlib/linalg.md b/doc/src/stdlib/linalg.md index a3a3a74cb8bd96..f8dc0fe81446dd 100644 --- a/doc/src/stdlib/linalg.md +++ b/doc/src/stdlib/linalg.md @@ -100,6 +100,7 @@ Base.LinAlg.istriu Base.LinAlg.isdiag Base.LinAlg.ishermitian Base.LinAlg.RowVector +Base.LinAlg.ConjArray Base.transpose Base.transpose! Base.ctranspose diff --git a/test/choosetests.jl b/test/choosetests.jl index b02f75e6437a62..6cf243891c57de 100644 --- a/test/choosetests.jl +++ b/test/choosetests.jl @@ -130,7 +130,7 @@ function choosetests(choices = []) "linalg/diagonal", "linalg/pinv", "linalg/givens", "linalg/cholesky", "linalg/lu", "linalg/symmetric", "linalg/generic", "linalg/uniformscaling", "linalg/lq", - "linalg/hessenberg", "linalg/rowvector"] + "linalg/hessenberg", "linalg/rowvector", "linalg/conjarray"] if Base.USE_GPL_LIBS push!(linalgtests, "linalg/arnoldi") end diff --git a/test/linalg/conjarray.jl b/test/linalg/conjarray.jl new file mode 100644 index 00000000000000..b446fb441e37b8 --- /dev/null +++ b/test/linalg/conjarray.jl @@ -0,0 +1,23 @@ +# This file is a part of Julia. License is MIT: http://julialang.org/license + +@testset "RowVector" begin + +@testset "Core" begin + m = [1+im 2; 2 4-im] + cm = ConjArray(m) + @test cm[1,1] == 1-im + @test trace(cm*m) == 27 + + v = [[1+im], [1-im]] + cv = ConjArray(v) + @test cv[1] == [1-im] +end + +@testset "RowVector conjugates" begin + v = [1+im, 1-im] + rv = v' + @test (parent(rv) isa ConjArray) + @test rv' === v +end + +end