Repository: sympy/sympy

Issue:
90 degree rotation of matrix elements
I think that transpose matrix elements clockwise is often given as a coding test problem.

![image](https://user-images.githubusercontent.com/34944973/77502452-42091100-6e9e-11ea-817a-af5e34b93b29.png)

I see lots of people approach this problem in naive or overly complicated way.

But I have found an article that gives the most rigorous solution about this.

https://blogs.sas.com/content/iml/2013/10/18/rotating-matrices.html

It is by permuting the rows and applying the usual transposition. (And rotating anticlockwise can also be done in similar manner by permuting the columns instead of rows)

```python3
>>> A = MatrixSymbol('A', 4, 4)
>>> A = A.as_explicit()
>>> A
Matrix([
[A[0, 0], A[0, 1], A[0, 2], A[0, 3]],
[A[1, 0], A[1, 1], A[1, 2], A[1, 3]],
[A[2, 0], A[2, 1], A[2, 2], A[2, 3]],
[A[3, 0], A[3, 1], A[3, 2], A[3, 3]]])
>>> A[::, ::-1].T
Matrix([
[A[0, 3], A[1, 3], A[2, 3], A[3, 3]],
[A[0, 2], A[1, 2], A[2, 2], A[3, 2]],
[A[0, 1], A[1, 1], A[2, 1], A[3, 1]],
[A[0, 0], A[1, 0], A[2, 0], A[3, 0]]])
>>> A[::-1, ::].T
Matrix([
[A[3, 0], A[2, 0], A[1, 0], A[0, 0]],
[A[3, 1], A[2, 1], A[1, 1], A[0, 1]],
[A[3, 2], A[2, 2], A[1, 2], A[0, 2]],
[A[3, 3], A[2, 3], A[1, 3], A[0, 3]]])
```

And it even works with rectangular matrices

```python3
>>> A = MatrixSymbol('A', 3, 4).as_explicit()
>>> A
Matrix([
[A[0, 0], A[0, 1], A[0, 2], A[0, 3]],
[A[1, 0], A[1, 1], A[1, 2], A[1, 3]],
[A[2, 0], A[2, 1], A[2, 2], A[2, 3]]])
>>> A[::, ::-1].T
Matrix([
[A[0, 3], A[1, 3], A[2, 3]],
[A[0, 2], A[1, 2], A[2, 2]],
[A[0, 1], A[1, 1], A[2, 1]],
[A[0, 0], A[1, 0], A[2, 0]]])
>>> A[::-1, ::].T
Matrix([
[A[2, 0], A[1, 0], A[0, 0]],
[A[2, 1], A[1, 1], A[0, 1]],
[A[2, 2], A[1, 2], A[0, 2]],
[A[2, 3], A[1, 3], A[0, 3]]])
```

I think that we can have an API like 
https://mathworks.com/help/matlab/ref/rot90.html https://docs.scipy.org/doc/numpy/reference/generated/numpy.rot90.html

Discussion:

Patch:
diff --git a/sympy/matrices/common.py b/sympy/matrices/common.py
index 443dfd6dd5..721be20b87 100644
--- a/sympy/matrices/common.py
+++ b/sympy/matrices/common.py
@@ -2019,6 +2019,51 @@ def replace(self, F, G, map=False, simultaneous=True, exact=None):
         return self.applyfunc(
             lambda x: x.replace(F, G, map=map, simultaneous=simultaneous, exact=exact))
 
+    def rot90(self, k=1):
+        """Rotates Matrix by 90 degrees
+
+        Parameters
+        ==========
+
+        k : int
+            Specifies how many times the matrix is rotated by 90 degrees
+            (clockwise when positive, counter-clockwise when negative).
+
+        Examples
+        ========
+
+        >>> from sympy import Matrix, symbols
+        >>> A = Matrix(2, 2, symbols('a:d'))
+        >>> A
+        Matrix([
+        [a, b],
+        [c, d]])
+
+        Rotating the matrix clockwise one time:
+
+        >>> A.rot90(1)
+        Matrix([
+        [c, a],
+        [d, b]])
+
+        Rotating the matrix anticlockwise two times:
+
+        >>> A.rot90(-2)
+        Matrix([
+        [d, c],
+        [b, a]])
+        """
+
+        mod = k%4
+        if mod == 0:
+            return self
+        if mod == 1:
+            return self[::-1, ::].T
+        if mod == 2:
+            return self[::-1, ::-1]
+        if mod == 3:
+            return self[::, ::-1].T
+
     def simplify(self, **kwargs):
         """Apply simplify to each element of the matrix.
 


Test patch:
diff --git a/sympy/matrices/tests/test_commonmatrix.py b/sympy/matrices/tests/test_commonmatrix.py
index cfb43fa057..f25ffe2f6c 100644
--- a/sympy/matrices/tests/test_commonmatrix.py
+++ b/sympy/matrices/tests/test_commonmatrix.py
@@ -522,6 +522,13 @@ def test_replace_map():
     assert N == K
 
 
+def test_rot90():
+    A = Matrix([[1, 2], [3, 4]])
+    assert A == A.rot90(0) == A.rot90(4)
+    assert A.rot90(2) == A.rot90(-2) == A.rot90(6) == Matrix(((4, 3), (2, 1)))
+    assert A.rot90(3) == A.rot90(-1) == A.rot90(7) == Matrix(((2, 4), (1, 3)))
+    assert A.rot90() == A.rot90(-7) == A.rot90(-3) == Matrix(((3, 1), (4, 2)))
+
 def test_simplify():
     n = Symbol('n')
     f = Function('f')