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

RFC: Make matrix transpose default for permutedims #24839

Merged
merged 1 commit into from
Dec 14, 2017
Merged

Conversation

andyferris
Copy link
Member

There has been a lot of discussion at #20978 about the future of transpose (and adjoint, etc).

Basically, we would like a nicer way of doing a "shallow" transpose of an array of data, that has no linear algebra connotations. The current syntax would be permutedims(matrix, (2,1))

The approach here is to simplify this to permutedims(matrix) by making a default argument for perm = (2,1).

@andyferris
Copy link
Member Author

andyferris commented Nov 29, 2017

The second commit adds permutedims(vector) to create a row-matrix (like the old transpose, avoiding RowVector altogether). Happy for feedback on this one (seems convenient, but slightly misnamed).

@jebej
Copy link
Contributor

jebej commented Nov 29, 2017

IIUC this won't work for AbstractArrays with more than 2 dimensions. A general default might be to shift the dimensions circularly (1,2,3) -> (3,1,2)

@andyferris
Copy link
Member Author

That was on purpose - happy to discuss but I worried about making it too magical.

"""
permutedims(v::AbstractVector)

Reshapes vector `v` into a `1 × length(v)` row matrix.
Copy link
Member

Choose a reason for hiding this comment

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

Typically we stick to the imperative in docstrings, so this should be "reshape" instead of "reshapes."

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks - I always forget this!

`ndims(A)`. This is a generalization of transpose for multi-dimensional arrays. Transpose is
equivalent to `permutedims(A, [2,1])`.
`ndims(A)`. This is a generalization of matrix transposition for multi-dimensional arrays.
The value of `perm` defaults to `(2,1)` for transposing the elements of a matrix.
Copy link
Member

Choose a reason for hiding this comment

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

"Transposing the elements of a matrix" makes it sound (at least to me) like it recursively calls permutedims on the elements. Perhaps "transposing the dimensions of a matrix"? Unless that isn't a standard way to say it.

Copy link
Member

@Sacha0 Sacha0 Dec 1, 2017

Choose a reason for hiding this comment

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

Perhaps "flips the array cross its diagonal" or "mirrors the array across its diagonal"? Edit: That is, perhaps best to avoid "transpose" in this docstring?

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, avoiding the word "transpose" would be ideal.

@kshyatt kshyatt added the arrays [a, r, r, a, y, s] label Nov 30, 2017
@@ -1466,7 +1466,7 @@ end

## Permute array dims ##

function permutedims(B::StridedArray, perm)
function permutedims(B::StridedArray, perm = (2,1))
Copy link
Member

Choose a reason for hiding this comment

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

Perhaps better to provide a default (2, 1) perm only for two-dimensional StridedArrays? For example, perhaps add the short child

permutedims(B::StridedMatrix) = permutedims(B, (2, 1))

rather than modify this general method's signature?

Copy link
Member Author

Choose a reason for hiding this comment

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

I thought of that at first, but I note that you get a pretty coherent error message for 3D arrays as this is.

@@ -1487,7 +1489,7 @@ function checkdims_perm(P::AbstractArray{TP,N}, B::AbstractArray{TB,N}, perm) wh
end

for (V, PT, BT) in [((:N,), BitArray, BitArray), ((:T,:N), Array, StridedArray)]
@eval @generated function permutedims!(P::$PT{$(V...)}, B::$BT{$(V...)}, perm) where $(V...)
@eval @generated function permutedims!(P::$PT{$(V...)}, B::$BT{$(V...)}, perm = (2,1)) where $(V...)
Copy link
Member

Choose a reason for hiding this comment

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

Likewise here, perhaps better to provide a default (2, 1) permutation only for two-dimensional BitArrays?

@@ -108,11 +108,18 @@ julia> permutedims(A, [3, 2, 1])
6 8
```
"""
function Base.permutedims(A::AbstractArray, perm)
function Base.permutedims(A::AbstractArray, perm = (2,1))
Copy link
Member

Choose a reason for hiding this comment

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

And likewise here re. confining the default (2, 1) permutation to two-dimensional objects? :)

@andyferris andyferris force-pushed the ajf/permutedims branch 2 times, most recently from 7a91959 to 7ed8ac8 Compare December 2, 2017 13:23
@andyferris
Copy link
Member Author

OK I did another pass, do you think the docstrings are clearer now?

Copy link
Member

@Sacha0 Sacha0 left a comment

Choose a reason for hiding this comment

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

Looks great! :) (Potentially modulo the permutedims(v::AbstractVector) method, about which I lack an opinion, but I imagine others might feel strongly about.)

@andyferris
Copy link
Member Author

OK I don't understand the documentation reference system. It's currently complaining that it can't find a reference to permutedims. There are docstrings, and it's listed in the "stdlib" reference.

test/arrayops.jl Outdated

m = [1 2; 3 4]
@test permutedims(m) == [1 3; 2 4]

Copy link
Member

Choose a reason for hiding this comment

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

CI reports a trailing whitespace failure on this line (583)?

@@ -77,11 +77,10 @@ end
@inline genperm(I, perm::AbstractVector{Int}) = genperm(I, (perm...,))

"""
permutedims(A, perm)
permutedims(A::AbstractArray, perm])
Copy link
Member

Choose a reason for hiding this comment

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

Rogue ]? :)

Permute the dimensions of the matrix `m`, by flipping the elements across the diagonal of
the matrix. Differs from [`transpose`](@ref) in that the operation is not recursive.
"""
Base.permutedims(A::AbstractMatrix) = permutedims(A, (2,1))
Copy link
Member

Choose a reason for hiding this comment

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

The Base. qualification should be unnecessary?

@andyferris
Copy link
Member Author

This is working now.

@ararslan ararslan added the needs news A NEWS entry is required for this change label Dec 9, 2017
@andyferris andyferris removed the needs news A NEWS entry is required for this change label Dec 10, 2017
@andyferris
Copy link
Member Author

andyferris commented Dec 10, 2017

Alright, news done.

@Sacha0 How do you think this will work in combination with the changes you are making for transpose?

In particular, what is the best way of getting a left-eigenvector out of an eigen.vectors matrix? In this case (for Hermitian eigendecomposition) you want non-recursive vector transpose, but you do want a RowVector. Do you think permutedims(::AbstractVector) should be the way of creating the RowVector non-recursively (as in permutedims(eigen.vectors[i, :])), or is there a better trick?

The other way to go is ((eigen.vectors')[:, i])', which should be fast after you're done. And the view version of that should also work (if a little convoluted). Also, this seems equivalent to (inv(eigen.vectors)[:, i])' for the general (non-Hermitian) case, so maybe using ' is better than permutedims for this? (EDIT: I'm getting the feeling that I've overthought this... eig doesn't support block matrices anyway).

Does anyone else have an opinion if permutedims(::AbstractVector) should even exist?

 * Also allow `permutedims(vector)` to make row matrix
 * Make clearer the relationships between `transpose`, `adjoint` and
   `permutedims` in the docstrings.
@Sacha0
Copy link
Member

Sacha0 commented Dec 10, 2017

How do you think...

Unfortunately beyond the scope of things I can think about for now 😄. Apologies and best!

@andyferris
Copy link
Member Author

No probs, Sacha. I think I may have been overthinking it... I can't think of a proper linear-algebra use of non-recursive transpose other than that, so I think this should be fine.

@andyferris
Copy link
Member Author

Given the lack of nay-saying, I'll go ahead and merge this now.

I think this will solve the "data transpose" problem neatly enough for v1.0. We can always improve later on, as opportunities present themselves.

@andyferris andyferris merged commit a9bb7d3 into master Dec 14, 2017
@martinholters martinholters deleted the ajf/permutedims branch December 14, 2017 10:39
@algorithmx
Copy link

algorithmx commented Jul 26, 2018

HOW do I use type annotations/type asserts? Conceptually LinearAlgebra.Adjoint{Float64,Array{Float64,2}} and LinearAlgebra.Transpose{Float64,Array{Float64,2}} are still Array{Float64,2}; if they come as results of a function, say f(), what can I do to secure the type passed to a variable is conceptually Array{Float64,2}? I am not allowed to use
res::Array{Float64,2} = f()

@ararslan
Copy link
Member

@algorithmx, it's best to ask questions on Discourse.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
arrays [a, r, r, a, y, s]
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants