243 lines
12 KiB
Julia
243 lines
12 KiB
Julia
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||
|
||
"""
|
||
RowVector(vector)
|
||
|
||
A lazy-view wrapper of an `AbstractVector`, which turns a length-`n` vector into a `1×n`
|
||
shaped row vector and represents the transpose of a vector (the elements are also transposed
|
||
recursively). This type is usually constructed (and unwrapped) via the [`transpose`](@ref)
|
||
function or `.'` operator (or related [`ctranspose`](@ref) or `'` operator).
|
||
|
||
By convention, a vector can be multiplied by a matrix on its left (`A * v`) whereas a row
|
||
vector can be multiplied by a matrix on its right (such that `v.' * A = (A.' * v).'`). It
|
||
differs from a `1×n`-sized matrix by the facts that its transpose returns a vector and the
|
||
inner product `v1.' * v2` returns a scalar, but will otherwise behave similarly.
|
||
"""
|
||
struct RowVector{T,V<:AbstractVector} <: AbstractMatrix{T}
|
||
vec::V
|
||
function RowVector{T,V}(v::V) where V<:AbstractVector where T
|
||
check_types(T,v)
|
||
new(v)
|
||
end
|
||
end
|
||
|
||
@inline check_types(::Type{T1}, ::AbstractVector{T2}) where {T1,T2} = check_types(T1, T2)
|
||
@pure check_types(::Type{T1}, ::Type{T2}) where {T1,T2} = T1 === transpose_type(T2) ? nothing :
|
||
error("Element type mismatch. Tried to create a `RowVector{$T1}` from an `AbstractVector{$T2}`")
|
||
|
||
const ConjRowVector{T,CV<:ConjVector} = RowVector{T,CV}
|
||
|
||
# The element type may be transformed as transpose is recursive
|
||
@inline transpose_type{T}(::Type{T}) = promote_op(transpose, T)
|
||
|
||
# Constructors that take a vector
|
||
@inline RowVector(vec::AbstractVector{T}) where {T} = RowVector{transpose_type(T),typeof(vec)}(vec)
|
||
@inline RowVector{T}(vec::AbstractVector{T}) where {T} = RowVector{T,typeof(vec)}(vec)
|
||
|
||
# Constructors that take a size and default to Array
|
||
@inline RowVector{T}(n::Int) where {T} = RowVector{T}(Vector{transpose_type(T)}(n))
|
||
@inline RowVector{T}(n1::Int, n2::Int) where {T} = n1 == 1 ?
|
||
RowVector{T}(Vector{transpose_type(T)}(n2)) :
|
||
error("RowVector expects 1×N size, got ($n1,$n2)")
|
||
@inline RowVector{T}(n::Tuple{Int}) where {T} = RowVector{T}(Vector{transpose_type(T)}(n[1]))
|
||
@inline RowVector{T}(n::Tuple{Int,Int}) where {T} = n[1] == 1 ?
|
||
RowVector{T}(Vector{transpose_type(T)}(n[2])) :
|
||
error("RowVector expects 1×N size, got $n")
|
||
|
||
# Conversion of underlying storage
|
||
convert(::Type{RowVector{T,V}}, rowvec::RowVector) where {T,V<:AbstractVector} =
|
||
RowVector{T,V}(convert(V,rowvec.vec))
|
||
|
||
# similar tries to maintain the RowVector wrapper and the parent type
|
||
@inline similar(rowvec::RowVector) = RowVector(similar(parent(rowvec)))
|
||
@inline similar(rowvec::RowVector, ::Type{T}) where {T} = RowVector(similar(parent(rowvec), transpose_type(T)))
|
||
|
||
# Resizing similar currently loses its RowVector property.
|
||
@inline similar(rowvec::RowVector, ::Type{T}, dims::Dims{N}) where {T,N} = similar(parent(rowvec), T, dims)
|
||
|
||
# Basic methods
|
||
"""
|
||
transpose(v::AbstractVector)
|
||
|
||
The transposition operator (`.'`).
|
||
|
||
# Example
|
||
|
||
```jldoctest
|
||
julia> v = [1,2,3]
|
||
3-element Array{Int64,1}:
|
||
1
|
||
2
|
||
3
|
||
|
||
julia> transpose(v)
|
||
1×3 RowVector{Int64,Array{Int64,1}}:
|
||
1 2 3
|
||
```
|
||
"""
|
||
@inline transpose(vec::AbstractVector) = RowVector(vec)
|
||
@inline ctranspose(vec::AbstractVector) = RowVector(_conj(vec))
|
||
|
||
@inline transpose(rowvec::RowVector) = rowvec.vec
|
||
@inline transpose(rowvec::ConjRowVector) = copy(rowvec.vec) # remove the ConjArray wrapper from any raw vector
|
||
@inline ctranspose(rowvec::RowVector) = conj(rowvec.vec)
|
||
@inline ctranspose(rowvec::RowVector{<:Real}) = rowvec.vec
|
||
|
||
parent(rowvec::RowVector) = rowvec.vec
|
||
|
||
"""
|
||
conj(v::RowVector)
|
||
|
||
Returns a [`ConjArray`](@ref) lazy view of the input, where each element is conjugated.
|
||
|
||
### Example
|
||
|
||
```jldoctest
|
||
julia> v = [1+im, 1-im].'
|
||
1×2 RowVector{Complex{Int64},Array{Complex{Int64},1}}:
|
||
1+1im 1-1im
|
||
|
||
julia> conj(v)
|
||
1×2 RowVector{Complex{Int64},ConjArray{Complex{Int64},1,Array{Complex{Int64},1}}}:
|
||
1-1im 1+1im
|
||
```
|
||
"""
|
||
@inline conj(rowvec::RowVector) = RowVector(_conj(rowvec.vec))
|
||
@inline conj(rowvec::RowVector{<:Real}) = rowvec
|
||
|
||
# AbstractArray interface
|
||
@inline length(rowvec::RowVector) = length(rowvec.vec)
|
||
@inline size(rowvec::RowVector) = (1, length(rowvec.vec))
|
||
@inline size(rowvec::RowVector, d) = ifelse(d==2, length(rowvec.vec), 1)
|
||
@inline indices(rowvec::RowVector) = (Base.OneTo(1), indices(rowvec.vec)[1])
|
||
@inline indices(rowvec::RowVector, d) = ifelse(d == 2, indices(rowvec.vec)[1], Base.OneTo(1))
|
||
IndexStyle(::RowVector) = IndexLinear()
|
||
IndexStyle(::Type{<:RowVector}) = IndexLinear()
|
||
|
||
@propagate_inbounds getindex(rowvec::RowVector, i) = transpose(rowvec.vec[i])
|
||
@propagate_inbounds setindex!(rowvec::RowVector, v, i) = (setindex!(rowvec.vec, transpose(v), i); rowvec)
|
||
|
||
# Cartesian indexing is distorted by getindex
|
||
# Furthermore, Cartesian indexes don't have to match shape, apparently!
|
||
@inline function getindex(rowvec::RowVector, i::CartesianIndex)
|
||
@boundscheck if !(i.I[1] == 1 && i.I[2] ∈ indices(rowvec.vec)[1] && check_tail_indices(i.I...))
|
||
throw(BoundsError(rowvec, i.I))
|
||
end
|
||
@inbounds return transpose(rowvec.vec[i.I[2]])
|
||
end
|
||
@inline function setindex!(rowvec::RowVector, v, i::CartesianIndex)
|
||
@boundscheck if !(i.I[1] == 1 && i.I[2] ∈ indices(rowvec.vec)[1] && check_tail_indices(i.I...))
|
||
throw(BoundsError(rowvec, i.I))
|
||
end
|
||
@inbounds rowvec.vec[i.I[2]] = transpose(v)
|
||
end
|
||
|
||
@propagate_inbounds getindex(rowvec::RowVector, ::CartesianIndex{0}) = getindex(rowvec)
|
||
@propagate_inbounds getindex(rowvec::RowVector, i::CartesianIndex{1}) = getindex(rowvec, i.I[1])
|
||
|
||
@propagate_inbounds setindex!(rowvec::RowVector, v, ::CartesianIndex{0}) = setindex!(rowvec, v)
|
||
@propagate_inbounds setindex!(rowvec::RowVector, v, i::CartesianIndex{1}) = setindex!(rowvec, v, i.I[1])
|
||
|
||
@inline check_tail_indices(i1, i2) = true
|
||
@inline check_tail_indices(i1, i2, i3, is...) = i3 == 1 ? check_tail_indices(i1, i2, is...) : false
|
||
|
||
# helper function for below
|
||
@inline to_vec(rowvec::RowVector) = map(transpose, transpose(rowvec))
|
||
@inline to_vec(x::Number) = x
|
||
@inline to_vecs(rowvecs...) = (map(to_vec, rowvecs)...)
|
||
|
||
# map: Preserve the RowVector by un-wrapping and re-wrapping, but note that `f`
|
||
# expects to operate within the transposed domain, so to_vec transposes the elements
|
||
@inline map(f, rowvecs::RowVector...) = RowVector(map(transpose∘f, to_vecs(rowvecs...)...))
|
||
|
||
# broacast (other combinations default to higher-dimensional array)
|
||
@inline broadcast(f, rowvecs::Union{Number,RowVector}...) =
|
||
RowVector(broadcast(transpose∘f, to_vecs(rowvecs...)...))
|
||
|
||
# Horizontal concatenation #
|
||
|
||
@inline hcat(X::RowVector...) = transpose(vcat(map(transpose, X)...))
|
||
@inline hcat(X::Union{RowVector,Number}...) = transpose(vcat(map(transpose, X)...))
|
||
|
||
@inline typed_hcat(::Type{T}, X::RowVector...) where {T} =
|
||
transpose(typed_vcat(T, map(transpose, X)...))
|
||
@inline typed_hcat(::Type{T}, X::Union{RowVector,Number}...) where {T} =
|
||
transpose(typed_vcat(T, map(transpose, X)...))
|
||
|
||
# Multiplication #
|
||
|
||
# inner product -> dot product specializations
|
||
@inline *(rowvec::RowVector{T}, vec::AbstractVector{T}) where {T<:Real} = dot(parent(rowvec), vec)
|
||
@inline *(rowvec::ConjRowVector{T}, vec::AbstractVector{T}) where {T<:Real} = dot(rowvec', vec)
|
||
@inline *(rowvec::ConjRowVector, vec::AbstractVector) = dot(rowvec', vec)
|
||
|
||
# Generic behavior
|
||
@inline function *(rowvec::RowVector, vec::AbstractVector)
|
||
if length(rowvec) != length(vec)
|
||
throw(DimensionMismatch("A has dimensions $(size(rowvec)) but B has dimensions $(size(vec))"))
|
||
end
|
||
sum(@inbounds(return rowvec[i]*vec[i]) for i = 1:length(vec))
|
||
end
|
||
@inline *(rowvec::RowVector, mat::AbstractMatrix) = transpose(mat.' * transpose(rowvec))
|
||
*(::RowVector, ::RowVector) = throw(DimensionMismatch("Cannot multiply two transposed vectors"))
|
||
@inline *(vec::AbstractVector, rowvec::RowVector) = vec .* rowvec
|
||
*(vec::AbstractVector, rowvec::AbstractVector) = throw(DimensionMismatch("Cannot multiply two vectors"))
|
||
|
||
# Transposed forms
|
||
A_mul_Bt(::RowVector, ::AbstractVector) = throw(DimensionMismatch("Cannot multiply two transposed vectors"))
|
||
@inline A_mul_Bt(rowvec::RowVector, mat::AbstractMatrix) = transpose(mat * transpose(rowvec))
|
||
@inline A_mul_Bt(rowvec1::RowVector, rowvec2::RowVector) = rowvec1*transpose(rowvec2)
|
||
A_mul_Bt(vec::AbstractVector, rowvec::RowVector) = throw(DimensionMismatch("Cannot multiply two vectors"))
|
||
@inline A_mul_Bt(vec1::AbstractVector, vec2::AbstractVector) = vec1 * transpose(vec2)
|
||
@inline A_mul_Bt(mat::AbstractMatrix, rowvec::RowVector) = mat * transpose(rowvec)
|
||
|
||
@inline At_mul_Bt(rowvec::RowVector, vec::AbstractVector) = transpose(rowvec) * transpose(vec)
|
||
@inline At_mul_Bt(vec::AbstractVector, mat::AbstractMatrix) = transpose(mat * vec)
|
||
At_mul_Bt(rowvec1::RowVector, rowvec2::RowVector) = throw(DimensionMismatch("Cannot multiply two vectors"))
|
||
@inline At_mul_Bt(vec::AbstractVector, rowvec::RowVector) = transpose(vec)*transpose(rowvec)
|
||
At_mul_Bt(vec::AbstractVector, rowvec::AbstractVector) = throw(DimensionMismatch(
|
||
"Cannot multiply two transposed vectors"))
|
||
@inline At_mul_Bt(mat::AbstractMatrix, rowvec::RowVector) = mat.' * transpose(rowvec)
|
||
|
||
At_mul_B(::RowVector, ::AbstractVector) = throw(DimensionMismatch("Cannot multiply two vectors"))
|
||
@inline At_mul_B(vec::AbstractVector, mat::AbstractMatrix) = transpose(At_mul_B(mat,vec))
|
||
@inline At_mul_B(rowvec1::RowVector, rowvec2::RowVector) = transpose(rowvec1) * rowvec2
|
||
At_mul_B(vec::AbstractVector, rowvec::RowVector) = throw(DimensionMismatch(
|
||
"Cannot multiply two transposed vectors"))
|
||
@inline At_mul_B(vec1::AbstractVector{T}, vec2::AbstractVector{T}) where {T<:Real} =
|
||
reduce(+, map(At_mul_B, vec1, vec2)) # Seems to be overloaded...
|
||
@inline At_mul_B(vec1::AbstractVector, vec2::AbstractVector) = transpose(vec1) * vec2
|
||
|
||
# Conjugated forms
|
||
A_mul_Bc(::RowVector, ::AbstractVector) = throw(DimensionMismatch("Cannot multiply two transposed vectors"))
|
||
@inline A_mul_Bc(rowvec::RowVector, mat::AbstractMatrix) = ctranspose(mat * ctranspose(rowvec))
|
||
@inline A_mul_Bc(rowvec1::RowVector, rowvec2::RowVector) = rowvec1 * ctranspose(rowvec2)
|
||
A_mul_Bc(vec::AbstractVector, rowvec::RowVector) = throw(DimensionMismatch("Cannot multiply two vectors"))
|
||
@inline A_mul_Bc(vec1::AbstractVector, vec2::AbstractVector) = vec1 * ctranspose(vec2)
|
||
@inline A_mul_Bc(mat::AbstractMatrix, rowvec::RowVector) = mat * ctranspose(rowvec)
|
||
|
||
@inline Ac_mul_Bc(rowvec::RowVector, vec::AbstractVector) = ctranspose(rowvec) * ctranspose(vec)
|
||
@inline Ac_mul_Bc(vec::AbstractVector, mat::AbstractMatrix) = ctranspose(mat * vec)
|
||
Ac_mul_Bc(rowvec1::RowVector, rowvec2::RowVector) = throw(DimensionMismatch("Cannot multiply two vectors"))
|
||
@inline Ac_mul_Bc(vec::AbstractVector, rowvec::RowVector) = ctranspose(vec)*ctranspose(rowvec)
|
||
Ac_mul_Bc(vec::AbstractVector, rowvec::AbstractVector) = throw(DimensionMismatch("Cannot multiply two transposed vectors"))
|
||
@inline Ac_mul_Bc(mat::AbstractMatrix, rowvec::RowVector) = mat' * ctranspose(rowvec)
|
||
|
||
Ac_mul_B(::RowVector, ::AbstractVector) = throw(DimensionMismatch("Cannot multiply two vectors"))
|
||
@inline Ac_mul_B(vec::AbstractVector, mat::AbstractMatrix) = ctranspose(Ac_mul_B(mat,vec))
|
||
@inline Ac_mul_B(rowvec1::RowVector, rowvec2::RowVector) = ctranspose(rowvec1) * rowvec2
|
||
Ac_mul_B(vec::AbstractVector, rowvec::RowVector) = throw(DimensionMismatch("Cannot multiply two transposed vectors"))
|
||
@inline Ac_mul_B(vec1::AbstractVector, vec2::AbstractVector) = ctranspose(vec1)*vec2
|
||
|
||
# Left Division #
|
||
|
||
\(mat::AbstractMatrix, rowvec::RowVector) = throw(DimensionMismatch("Cannot left-divide transposed vector by matrix"))
|
||
At_ldiv_B(mat::AbstractMatrix, rowvec::RowVector) = throw(DimensionMismatch("Cannot left-divide transposed vector by matrix"))
|
||
Ac_ldiv_B(mat::AbstractMatrix, rowvec::RowVector) = throw(DimensionMismatch("Cannot left-divide transposed vector by matrix"))
|
||
|
||
# Right Division #
|
||
|
||
@inline /(rowvec::RowVector, mat::AbstractMatrix) = transpose(transpose(mat) \ transpose(rowvec))
|
||
@inline A_rdiv_Bt(rowvec::RowVector, mat::AbstractMatrix) = transpose(mat \ transpose(rowvec))
|
||
@inline A_rdiv_Bc(rowvec::RowVector, mat::AbstractMatrix) = ctranspose(mat \ ctranspose(rowvec))
|