Matrix versus matrix transpose


#1

I’m a bit confused: how is the matrix transpose expected to work?

Let’s say I form a transpose:

julia> a = rand(3,3)
3×3 Array{Float64,2}:
 0.138434  0.0714774  0.570142
 0.169383  0.0834496  0.1411
 0.704381  0.0703364  0.0379085

julia> transpose(a)
3×3 Transpose{Float64,Array{Float64,2}}:
 0.138434   0.169383   0.704381
 0.0714774  0.0834496  0.0703364
 0.570142   0.1411     0.0379085

But in my mind that transpose IS an ARRAY, so I should be able to pass it to a function that expects an Array{Float64,2}. However that function is not going to accept that as an argument, because now I’m going to pass a Transpose{Float64,Array{Float64,2}} instead of a Array{Float64,2}.

Am I missing something?

(Sorry, I forgot to say that I’m talking about 0.7.)


#2

A Transpose is still an AbstractArray, so you can pass it to any function that expects an AbstractArray. If a function expects exactly an Array{Float64, 2} then either (1) that function is doing something that relies on the exact internal representation of an Array{Float64, 2}, in which case you can copy() the transpose to get a “plain” array, or (2) that function’s input type signatures are too restrictive, in which case the function’s input types should be updated.


#3

OK, that makes sense. Thanks.


#4

Don’t type your functions to ::Array. Instead type them to ::AbstractArray. And make your types have type parameters instead of ::Array. It makes this all a non-issue without changing performance, and gives you a lot of new features for free.


#5

That is good advice.


#6

As I understand this is a version 0.7 behaviour,
because for version 0.6 transpose(a) returns a 3×3 Array{Float64,2}

I do not find any trace of this change in the release notes for the master.
Could you point me where the change happened.


#7

Transposes are lazy now.


#8

The PR is here: https://github.com/JuliaLang/julia/pull/25364

I just flagged it as needing a NEWS update.


#9

I just found this conversation when trying to understand why I couldn’t pass a transposed array to lyap. Why shouldn’t it be able to accept a transpose or adjoint of an array?

julia> A = rand(2,2)
2×2 Array{Float64,2}:
 0.14733  0.885718
 0.79308  0.426695

julia> Q = I+zeros(2,2)
2×2 Array{Float64,2}:
 1.0  0.0
 0.0  1.0

julia> lyap(A,Q)
2×2 Array{Float64,2}:
  0.44532   -0.638588 
 -0.638588   0.0151193

julia> lyap(transpose(A),Q)
ERROR: MethodError: no method matching lyap(::Transpose{Float64,Array{Float64,2}}, ::Array{Float64,2})
Closest candidates are:
  lyap(::Union{DenseArray{T<:Union{Complex{Float32}, Complex{Float64}, Float32, Float64},2}, ReinterpretArray{T<:Union{Complex{Float32}, Complex{Float64}, Float32, Float64},2,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray}, ReshapedArray{T<:Union{Complex{Float32}, Complex{Float64}, Float32, Float64},2,A,MI} where MI<:Tuple{Vararg{SignedMultiplicativeInverse{Int64},N} where N} where A<:Union{ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray}, SubArray{T<:Union{Complex{Float32}, Complex{Float64}, Float32, Float64},2,A,I,L} where L where I<:Tuple{Vararg{Union{Int64, AbstractRange{Int64}, AbstractCartesianIndex},N} where N} where A<:Union{ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, ReshapedArray{T,N,A,MI} where MI<:Tuple{Vararg{SignedMultiplicativeInverse{Int64},N} where N} where A<:Union{ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, DenseArray}}, ::Union{DenseArray{T<:Union{Complex{Float32}, Complex{Float64}, Float32, Float64},2}, ReinterpretArray{T<:Union{Complex{Float32}, Complex{Float64}, Float32, Float64},2,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray}, ReshapedArray{T<:Union{Complex{Float32}, Complex{Float64}, Float32, Float64},2,A,MI} where MI<:Tuple{Vararg{SignedMultiplicativeInverse{Int64},N} where N} where A<:Union{ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray}, SubArray{T<:Union{Complex{Float32}, Complex{Float64}, Float32, Float64},2,A,I,L} where L where I<:Tuple{Vararg{Union{Int64, AbstractRange{Int64}, AbstractCartesianIndex},N} where N} where A<:Union{ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, ReshapedArray{T,N,A,MI} where MI<:Tuple{Vararg{SignedMultiplicativeInverse{Int64},N} where N} where A<:Union{ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, DenseArray}}) where T<:Union{Complex{Float32}, Complex{Float64}, Float32, Float64} at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.1/LinearAlgebra/src/dense.jl:1476
Stacktrace:
 [1] top-level scope at none:0

#10

From glancing at the source code, the implementation of lyap seems to rely on some LAPACK routines which probably expect strided matrices: https://github.com/JuliaLang/julia/blob/69ac379c9d38f758b32e769711c8b1ac90afa25d/stdlib/LinearAlgebra/src/dense.jl#L1458

You should be able to do lyap(copy(transpose(A)), Q) to make this work in your case.