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

#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.