Repository: sympy/sympy

Issue:
block_collapse fails on inverse-transpose of block matrix
When a block matrix is inversed then transposed (in that order) `block_collapse()` incorrectly returns 1 instead of a matrix. StackOverflow post here, https://stackoverflow.com/questions/75475985/does-block-collapse-in-sympy-fail-on-inverse-transpose-of-a-block-matrix . Reprex below, my `sympy` version is 1.11.1

```python
from sympy import (BlockMatrix, symbols, Identity, ZeroMatrix, block_collapse)
n = symbols('n')
In = Identity(n)
Zeron = ZeroMatrix(n,n)
A = BlockMatrix([
  [In, Zeron],
  [Zeron, In]])
At = A.transpose()
Ati = At.inverse()
Ai = A.inverse()
Ait = Ai.transpose()

block_collapse(A)
# Matrix([
# [I, 0],
# [0, I]])
block_collapse(At)
# Matrix([
# [I, 0],
# [0, I]])
block_collapse(Ati)
# Matrix([
# [I, 0],
# [0, I]])
block_collapse(Ai)
# Matrix([
# [I, 0],
# [0, I]])
block_collapse(Ait) # This one fails
# 1
```

Calling `block_collapse()` on the intermediate matrix corrects the issue.

```python
Aib = block_collapse(Ai)
Aibt = Aib.transpose()
block_collapse(Aibt)
# Matrix([
# [I, 0],
# [0, I]])
```

Discussion:
sympy-bot: :white_check_mark:

Hi, I am the [SymPy bot](https://github.com/sympy/sympy-bot). I'm here to help you write a release notes entry. Please read the [guide on how to write release notes](https://github.com/sympy/sympy/wiki/Writing-Release-Notes).



* No release notes entry will be added for this pull request.

<details><summary>Click here to see the pull request description that was parsed.</summary>

    <!-- Your title above should be a short description of what
    was changed. Do not include the issue number in the title. -->

    #### References to other Issues or PRs
    <!-- If this pull request fixes an issue, write "Fixes #NNNN" in that exact
    format, e.g. "Fixes #1234" (see
    https://tinyurl.com/auto-closing for more information). Also, please
    write a comment on that issue linking back to this pull request once it is
    open. -->


    #### Brief description of what is fixed or changed

    Fixes #24735

    #### Other comments

    This is an experimental fix of #24735

    #### Release Notes

    <!-- Write the release notes for this release below between the BEGIN and END
    statements. The basic format is a bulleted list with the name of the subpackage
    and the release note for this PR. For example:

    * solvers
      * Added a new solver for logarithmic equations.

    * functions
      * Fixed a bug with log of integers. Formerly, `log(-x)` incorrectly gave `-log(x)`.

    * physics.units
      * Corrected a semantical error in the conversion between volt and statvolt which
        reported the volt as being larger than the statvolt.

    or if no release note(s) should be included use:

    NO ENTRY

    See https://github.com/sympy/sympy/wiki/Writing-Release-Notes for more
    information on how to write release notes. The bot will check your release
    notes automatically to see if they are formatted correctly. -->

    <!-- BEGIN RELEASE NOTES -->
    NO ENTRY
    <!-- END RELEASE NOTES -->


</details><p>

sympy-bot: :white_check_mark:

Hi, I am the [SymPy bot](https://github.com/sympy/sympy-bot). I'm here to help you write a release notes entry. Please read the [guide on how to write release notes](https://github.com/sympy/sympy/wiki/Writing-Release-Notes).



* No release notes entry will be added for this pull request.

<details><summary>Click here to see the pull request description that was parsed.</summary>

    <!-- Your title above should be a short description of what
    was changed. Do not include the issue number in the title. -->

    #### References to other Issues or PRs
    <!-- If this pull request fixes an issue, write "Fixes #NNNN" in that exact
    format, e.g. "Fixes #1234" (see
    https://tinyurl.com/auto-closing for more information). Also, please
    write a comment on that issue linking back to this pull request once it is
    open. -->


    #### Brief description of what is fixed or changed

    Fixes #24735

    #### Other comments

    This is an experimental fix of #24735

    #### Release Notes

    <!-- Write the release notes for this release below between the BEGIN and END
    statements. The basic format is a bulleted list with the name of the subpackage
    and the release note for this PR. For example:

    * solvers
      * Added a new solver for logarithmic equations.

    * functions
      * Fixed a bug with log of integers. Formerly, `log(-x)` incorrectly gave `-log(x)`.

    * physics.units
      * Corrected a semantical error in the conversion between volt and statvolt which
        reported the volt as being larger than the statvolt.

    or if no release note(s) should be included use:

    NO ENTRY

    See https://github.com/sympy/sympy/wiki/Writing-Release-Notes for more
    information on how to write release notes. The bot will check your release
    notes automatically to see if they are formatted correctly. -->

    <!-- BEGIN RELEASE NOTES -->
    NO ENTRY
    <!-- END RELEASE NOTES -->


</details><p>

sympy-bot: :white_check_mark:

Hi, I am the [SymPy bot](https://github.com/sympy/sympy-bot). I'm here to help you write a release notes entry. Please read the [guide on how to write release notes](https://github.com/sympy/sympy/wiki/Writing-Release-Notes).



* No release notes entry will be added for this pull request.

<details><summary>Click here to see the pull request description that was parsed.</summary>

    <!-- Your title above should be a short description of what
    was changed. Do not include the issue number in the title. -->

    #### References to other Issues or PRs
    <!-- If this pull request fixes an issue, write "Fixes #NNNN" in that exact
    format, e.g. "Fixes #1234" (see
    https://tinyurl.com/auto-closing for more information). Also, please
    write a comment on that issue linking back to this pull request once it is
    open. -->


    #### Brief description of what is fixed or changed

    Fixes #24735

    #### Other comments

    This is an experimental fix of #24735

    #### Release Notes

    <!-- Write the release notes for this release below between the BEGIN and END
    statements. The basic format is a bulleted list with the name of the subpackage
    and the release note for this PR. For example:

    * solvers
      * Added a new solver for logarithmic equations.

    * functions
      * Fixed a bug with log of integers. Formerly, `log(-x)` incorrectly gave `-log(x)`.

    * physics.units
      * Corrected a semantical error in the conversion between volt and statvolt which
        reported the volt as being larger than the statvolt.

    or if no release note(s) should be included use:

    NO ENTRY

    See https://github.com/sympy/sympy/wiki/Writing-Release-Notes for more
    information on how to write release notes. The bot will check your release
    notes automatically to see if they are formatted correctly. -->

    <!-- BEGIN RELEASE NOTES -->
    NO ENTRY
    <!-- END RELEASE NOTES -->


</details><p>

github-actions[bot]: Benchmark results from GitHub Actions

Lower numbers are good, higher numbers are bad. A ratio less than 1
means a speed up and greater than 1 means a slowdown. Green lines
beginning with `+` are slowdowns (the PR is slower then master or
master is slower than the previous release). Red lines beginning
with `-` are speedups.

Significantly changed benchmark results (PR vs master)
```diff

```
Significantly changed benchmark results (master vs previous release)
```diff
       before           after         ratio
     [41d90958]       [31656382]
     <sympy-1.11.1^0>                 
-        1.17±0ms         700±20μs     0.60  solve.TimeSparseSystem.time_linear_eq_to_matrix(10)
-     3.33±0.01ms         1.28±0ms     0.38  solve.TimeSparseSystem.time_linear_eq_to_matrix(20)
-     6.67±0.01ms         1.87±0ms     0.28  solve.TimeSparseSystem.time_linear_eq_to_matrix(30)

```
Full benchmark results can be found as artifacts in GitHub Actions
(click on checks at the top of the PR).

Patch:
diff --git a/sympy/matrices/expressions/adjoint.py b/sympy/matrices/expressions/adjoint.py
index cf626ecc9f..2039a7b2eb 100644
--- a/sympy/matrices/expressions/adjoint.py
+++ b/sympy/matrices/expressions/adjoint.py
@@ -1,6 +1,5 @@
 from sympy.core import Basic
 from sympy.functions import adjoint, conjugate
-from sympy.matrices.expressions.transpose import transpose
 from sympy.matrices.expressions.matexpr import MatrixExpr
 
 
@@ -50,12 +49,12 @@ def _entry(self, i, j, **kwargs):
     def _eval_adjoint(self):
         return self.arg
 
+    def _eval_transpose(self):
+        return self.arg.conjugate()
+
     def _eval_conjugate(self):
-        return transpose(self.arg)
+        return self.arg.transpose()
 
     def _eval_trace(self):
         from sympy.matrices.expressions.trace import Trace
         return conjugate(Trace(self.arg))
-
-    def _eval_transpose(self):
-        return conjugate(self.arg)
diff --git a/sympy/matrices/expressions/blockmatrix.py b/sympy/matrices/expressions/blockmatrix.py
index 0b038c8775..a73fd1ccb3 100644
--- a/sympy/matrices/expressions/blockmatrix.py
+++ b/sympy/matrices/expressions/blockmatrix.py
@@ -795,8 +795,8 @@ def bc_dist(expr):
 
 def bc_matmul(expr):
     if isinstance(expr, MatPow):
-        if expr.args[1].is_Integer:
-            factor, matrices = (1, [expr.args[0]]*expr.args[1])
+        if expr.args[1].is_Integer and expr.args[1] > 0:
+            factor, matrices = 1, [expr.args[0]]*expr.args[1]
         else:
             return expr
     else:
diff --git a/sympy/matrices/expressions/inverse.py b/sympy/matrices/expressions/inverse.py
index 7cb8436915..45bceaeea0 100644
--- a/sympy/matrices/expressions/inverse.py
+++ b/sympy/matrices/expressions/inverse.py
@@ -54,6 +54,15 @@ def shape(self):
     def _eval_inverse(self):
         return self.arg
 
+    def _eval_transpose(self):
+        return Inverse(self.arg.transpose())
+
+    def _eval_adjoint(self):
+        return Inverse(self.arg.adjoint())
+
+    def _eval_conjugate(self):
+        return Inverse(self.arg.conjugate())
+
     def _eval_determinant(self):
         from sympy.matrices.expressions.determinant import det
         return 1/det(self.arg)
diff --git a/sympy/matrices/expressions/matpow.py b/sympy/matrices/expressions/matpow.py
index ce380b0f30..b9ca7aa993 100644
--- a/sympy/matrices/expressions/matpow.py
+++ b/sympy/matrices/expressions/matpow.py
@@ -89,7 +89,15 @@ def doit(self, **hints):
 
     def _eval_transpose(self):
         base, exp = self.args
-        return MatPow(base.T, exp)
+        return MatPow(base.transpose(), exp)
+
+    def _eval_adjoint(self):
+        base, exp = self.args
+        return MatPow(base.adjoint(), exp)
+
+    def _eval_conjugate(self):
+        base, exp = self.args
+        return MatPow(base.conjugate(), exp)
 
     def _eval_derivative(self, x):
         return Pow._eval_derivative(self, x)
diff --git a/sympy/matrices/expressions/transpose.py b/sympy/matrices/expressions/transpose.py
index c83e3e27c1..b11f7fc214 100644
--- a/sympy/matrices/expressions/transpose.py
+++ b/sympy/matrices/expressions/transpose.py
@@ -1,6 +1,4 @@
 from sympy.core.basic import Basic
-from sympy.functions import adjoint, conjugate
-
 from sympy.matrices.expressions.matexpr import MatrixExpr
 
 
@@ -53,10 +51,10 @@ def _entry(self, i, j, expand=False, **kwargs):
         return self.arg._entry(j, i, expand=expand, **kwargs)
 
     def _eval_adjoint(self):
-        return conjugate(self.arg)
+        return self.arg.conjugate()
 
     def _eval_conjugate(self):
-        return adjoint(self.arg)
+        return self.arg.adjoint()
 
     def _eval_transpose(self):
         return self.arg


Test patch:
diff --git a/sympy/matrices/expressions/tests/test_blockmatrix.py b/sympy/matrices/expressions/tests/test_blockmatrix.py
index d990ae590d..75ad13ead6 100644
--- a/sympy/matrices/expressions/tests/test_blockmatrix.py
+++ b/sympy/matrices/expressions/tests/test_blockmatrix.py
@@ -4,12 +4,16 @@
     block_collapse, bc_matmul, bc_block_plus_ident, BlockDiagMatrix,
     BlockMatrix, bc_dist, bc_matadd, bc_transpose, bc_inverse,
     blockcut, reblock_2x2, deblock)
-from sympy.matrices.expressions import (MatrixSymbol, Identity,
-        Inverse, trace, Transpose, det, ZeroMatrix, OneMatrix)
+from sympy.matrices.expressions import (
+    MatrixSymbol, Identity, trace, det, ZeroMatrix, OneMatrix)
+from sympy.matrices.expressions.inverse import Inverse
+from sympy.matrices.expressions.matpow import MatPow
+from sympy.matrices.expressions.transpose import Transpose
 from sympy.matrices.common import NonInvertibleMatrixError
 from sympy.matrices import (
     Matrix, ImmutableMatrix, ImmutableSparseMatrix, zeros)
-from sympy.core import Tuple, symbols, Expr, S, Function
+from sympy.core import Tuple, Expr, S, Function
+from sympy.core.symbol import Symbol, symbols
 from sympy.functions import transpose, im, re
 
 i, j, k, l, m, n, p = symbols('i:n, p', integer=True)
@@ -450,3 +454,16 @@ def test_block_matrix_derivative():
     A = Matrix(3, 3, [Function(f'a{i}')(x) for i in range(9)])
     bc = BlockMatrix([[A[:2, :2], A[:2, 2]], [A[2, :2], A[2:, 2]]])
     assert Matrix(bc.diff(x)) - A.diff(x) == zeros(3, 3)
+
+
+def test_transpose_inverse_commute():
+    n = Symbol('n')
+    I = Identity(n)
+    Z = ZeroMatrix(n, n)
+    A = BlockMatrix([[I, Z], [Z, I]])
+
+    assert block_collapse(A.transpose().inverse()) == A
+    assert block_collapse(A.inverse().transpose()) == A
+
+    assert block_collapse(MatPow(A.transpose(), -2)) == MatPow(A, -2)
+    assert block_collapse(MatPow(A, -2).transpose()) == MatPow(A, -2)
diff --git a/sympy/matrices/expressions/tests/test_inverse.py b/sympy/matrices/expressions/tests/test_inverse.py
index baf1289544..f5a7763068 100644
--- a/sympy/matrices/expressions/tests/test_inverse.py
+++ b/sympy/matrices/expressions/tests/test_inverse.py
@@ -60,3 +60,10 @@ def test_inverse_matpow_canonicalization():
 def test_nonsquare_error():
     A = MatrixSymbol('A', 3, 4)
     raises(NonSquareMatrixError, lambda: Inverse(A))
+
+
+def test_adjoint_trnaspose_conjugate():
+    A = MatrixSymbol('A', n, n)
+    assert A.transpose().inverse() == A.inverse().transpose()
+    assert A.conjugate().inverse() == A.inverse().conjugate()
+    assert A.adjoint().inverse() == A.inverse().adjoint()