Repository: sympy/sympy

Issue:
# Matrix derivative of determinant in non-matrix expressions

```python
from sympy import *
from sympy.abc import i, j, k

X = MatrixSymbol("X", 3, 3)

dX = Determinant(X)

expr = k*dX

expr.diff(X)

expr = 1/dX
expr.diff(X)
```

This issue is related to the NotImplementedError issue.

This one works:
```python
expr = k + dX
expr.diff(X)
```

Implementation patch:
diff --git a/sympy/tensor/array/expressions/array_expressions.py b/sympy/tensor/array/expressions/array_expressions.py
index aaddf5a2060c..9ad840d61843 100644
--- a/sympy/tensor/array/expressions/array_expressions.py
+++ b/sympy/tensor/array/expressions/array_expressions.py
@@ -850,7 +850,7 @@ def _canonicalize(self):
         if isinstance(expr, ArrayAdd):
             return self._ArrayDiagonal_denest_ArrayAdd(expr, *diagonal_indices)
         if isinstance(expr, ArrayDiagonal):
-            return self._ArrayDiagonal_denest_ArrayDiagonal(expr, *diagonal_indices)
+            return self._ArrayDiagonal_denest_ArrayDiagonal(expr, get_rank(self), *diagonal_indices)
         if isinstance(expr, PermuteDims):
             return self._ArrayDiagonal_denest_PermuteDims(expr, *diagonal_indices)
         if isinstance(expr, (ZeroArray, ZeroMatrix)):
@@ -886,9 +886,9 @@ def diagonal_indices(self):
         return self.args[1:]
 
     @staticmethod
-    def _flatten(expr: ArrayDiagonal, *outer_diagonal_indices):
-        inddown = ArrayDiagonal._push_indices_down(outer_diagonal_indices, list(range(get_rank(expr))), get_rank(expr))
-        inddown = tuple(i for i in inddown if i)
+    def _flatten(expr: ArrayDiagonal, outer_dims, *outer_diagonal_indices):
+        inddown = ArrayDiagonal._push_indices_down(outer_diagonal_indices, list(range(outer_dims)), get_rank(expr))
+        inddown = tuple(i for i in inddown)
         inddow2 = ArrayDiagonal._push_indices_down(expr.diagonal_indices, inddown, get_rank(expr.expr))
         new_diag_indices = []
         for i in inddow2:
@@ -2031,6 +2031,8 @@ def nest_permutation(expr):
 
 
 def _array_tensor_product(*args, **kwargs):
+    if all(not isinstance(i, (_ArrayExpr, _CodegenArrayAbstract)) and get_shape(i) == () for i in args):
+        return Mul.fromiter(args)
     return ArrayTensorProduct(*args, canonicalize=True, **kwargs)
 
 
diff --git a/sympy/tensor/array/expressions/arrayexpr_derivatives.py b/sympy/tensor/array/expressions/arrayexpr_derivatives.py
index 13fd8920836a..ede5b905267d 100644
--- a/sympy/tensor/array/expressions/arrayexpr_derivatives.py
+++ b/sympy/tensor/array/expressions/arrayexpr_derivatives.py
@@ -2,7 +2,7 @@
 from functools import reduce, singledispatch
 
 from sympy.core.singleton import S
-from sympy import MatrixBase, derive_by_array, Integer, Determinant, Function, MatPow, Dummy
+from sympy import MatrixBase, derive_by_array, Integer, Determinant, Function, MatPow, Dummy, Pow, Mul
 from sympy.tensor.array import NDimArray
 from sympy.core.expr import Expr
 from sympy.matrices.expressions.hadamard import HadamardProduct
@@ -37,6 +37,20 @@ def _(expr: Expr, x: _ArrayExpr):
     return ZeroArray(*x.shape)
 
 
+@array_derive.register(Mul)
+def _(expr: Mul, x: _ArrayExpr):
+    args = expr.args
+    return ArrayAdd.fromiter([
+        _array_tensor_product(Mul.fromiter(args[:i]), array_derive(arg, x), Mul.fromiter(args[(i+1):]))
+        for i, arg in enumerate(args)
+    ])
+
+
+@array_derive.register(Pow)
+def _(expr: Pow, x: _ArrayExpr):
+    return Pow._eval_derivative(expr, x)
+
+
 @array_derive.register(Function)
 def _(expr: Function, x: _ArrayExpr):
     if len(expr.args) != 1:
diff --git a/sympy/tensor/array/expressions/from_array_to_matrix.py b/sympy/tensor/array/expressions/from_array_to_matrix.py
index 19b985d0ee02..0ecacda423f2 100644
--- a/sympy/tensor/array/expressions/from_array_to_matrix.py
+++ b/sympy/tensor/array/expressions/from_array_to_matrix.py
@@ -570,7 +570,7 @@ def _(expr: ElementwiseApplyFunction):
     subexpr, removed = _remove_trivial_dims(expr.expr)
     if subexpr.shape == (1, 1) and isinstance(subexpr, MatrixExpr):
         # TODO: move this to ElementwiseApplyFunction
-        return expr.function(subexpr[0, 0]), removed + [0, 1]
+        return expr.function(MatrixElement(subexpr, 0, 0)), removed + [0, 1]
     return ElementwiseApplyFunction(expr.function, subexpr), []
 
 
diff --git a/sympy/tensor/array/expressions/from_matrix_to_array.py b/sympy/tensor/array/expressions/from_matrix_to_array.py
index 3d49512f0069..929c0db1d54d 100644
--- a/sympy/tensor/array/expressions/from_matrix_to_array.py
+++ b/sympy/tensor/array/expressions/from_matrix_to_array.py
@@ -15,7 +15,13 @@
 from sympy.matrices.expressions.matexpr import MatrixExpr
 from sympy.tensor.array.expressions.array_expressions import \
     ArrayElementwiseApplyFunc, _array_tensor_product, _array_contraction, \
-    _array_diagonal, _array_add, _permute_dims, Reshape, get_shape
+    _array_diagonal, _array_add, _permute_dims, Reshape, get_shape, _ArrayExpr, _CodegenArrayAbstract
+
+
+def _array_elementwise_apply_func(function, element):
+    if not isinstance(element, (MatrixExpr, _ArrayExpr, _CodegenArrayAbstract)):
+        return function(element)
+    return ArrayElementwiseApplyFunc(function, element)
 
 
 def convert_matrix_to_array(expr: Basic) -> Basic:
@@ -58,12 +64,12 @@ def convert_matrix_to_array(expr: Basic) -> Basic:
         if (expr.exp > 0) == True:
             base_conv = convert_matrix_to_array(base)
             if get_shape(base_conv) == ():
-                return ArrayElementwiseApplyFunc(lambda x: x**expr.exp, base_conv)
+                return _array_elementwise_apply_func(lambda x: x**expr.exp, base_conv)
             else:
                 return _array_tensor_product(*[base_conv for i in range(expr.exp)])
         elif get_shape(base) == ():
             d = Dummy("d")
-            return ArrayElementwiseApplyFunc(Lambda(d, d**expr.exp), base)
+            return _array_elementwise_apply_func(Lambda(d, d**expr.exp), base)
         else:
             return expr
     elif isinstance(expr, MatPow):
@@ -71,7 +77,7 @@ def convert_matrix_to_array(expr: Basic) -> Basic:
         if expr.exp.is_Integer != True:
             if get_shape(expr) == (1, 1):
                 b = symbols("b", cls=Dummy)
-                return ArrayElementwiseApplyFunc(Lambda(b, b ** expr.exp), convert_matrix_to_array(base))
+                return _array_elementwise_apply_func(Lambda(b, b ** expr.exp), convert_matrix_to_array(base))
             return expr
         elif (expr.exp > 0) == True:
             return convert_matrix_to_array(MatMul.fromiter(base for i in range(expr.exp)))
@@ -87,7 +93,7 @@ def convert_matrix_to_array(expr: Basic) -> Basic:
             return convert_matrix_to_array(HadamardProduct.fromiter(base for i in range(exp)))
         else:
             d = Dummy("d")
-            return ArrayElementwiseApplyFunc(Lambda(d, d**exp), base)
+            return _array_elementwise_apply_func(Lambda(d, d**exp), base)
     elif isinstance(expr, KroneckerProduct):
         kp_args = [convert_matrix_to_array(arg) for arg in expr.args]
         permutation = [2*i for i in range(len(kp_args))] + [2*i + 1 for i in range(len(kp_args))]


Test patch:
diff --git a/sympy/matrices/expressions/tests/test_derivatives.py b/sympy/matrices/expressions/tests/test_derivatives.py
index 45b78d1abeea..31c562dbcb0f 100644
--- a/sympy/matrices/expressions/tests/test_derivatives.py
+++ b/sympy/matrices/expressions/tests/test_derivatives.py
@@ -150,6 +150,18 @@ def test_issue_28708():
     assert expr.diff(X) == - X.T.inv()/Determinant(X)
 
 
+def test_issue_28838_det_in_non_matrix_expr():
+
+    expr = k * Determinant(X)
+    assert expr.diff(X) == k * Determinant(X) * X.T ** (-1)
+
+    expr = 1 / Determinant(X)
+    assert expr.diff(X) == -1 / Determinant(X) * X.T ** (-1)
+
+    expr = k / Determinant(X)
+    assert expr.diff(X) == -k / Determinant(X) * X.T ** (-1)
+
+
 def test_matrix_derivative_with_inverse():
 
     # Cookbook example 61:
diff --git a/sympy/tensor/array/expressions/tests/test_array_expressions.py b/sympy/tensor/array/expressions/tests/test_array_expressions.py
index 439d9363fa13..afe8eed053b8 100644
--- a/sympy/tensor/array/expressions/tests/test_array_expressions.py
+++ b/sympy/tensor/array/expressions/tests/test_array_expressions.py
@@ -819,6 +819,9 @@ def test_array_expr_as_explicit_with_explicit_component_arrays():
 
 
 def test_array_sum():
+    X = MatrixSymbol("X", k, k)
+    Y = MatrixSymbol("Y", k, k)
+
     expr = ArraySum(X, (i, 1, j))
     assert isinstance(expr, Sum)
     assert expr.doit() == j*X
@@ -842,7 +845,7 @@ def test_array_sum():
     assert expr.doit().dummy_eq(ArraySum(ArrayTensorProduct(X**sin(m), i*Y), (m, 1, j)))
 
     expr = ArrayContraction(ArraySum(X, (i, 0, j)), (0, 1))
-    # assert expr.doit() == ArrayContraction(j*T, (0, 1))  # TODO: Not working!
+    assert expr.doit() == ArrayContraction((j + 1)*X, (0, 1))
 
     T = MatrixSymbol("T", 3, 3)
     expr = ArrayContraction(ArraySum(T, (i, 1, j)), (0, 1))
diff --git a/sympy/tensor/array/expressions/tests/test_convert_array_to_matrix.py b/sympy/tensor/array/expressions/tests/test_convert_array_to_matrix.py
index ac205806acdb..4f0ff1aa1e2d 100644
--- a/sympy/tensor/array/expressions/tests/test_convert_array_to_matrix.py
+++ b/sympy/tensor/array/expressions/tests/test_convert_array_to_matrix.py
@@ -694,7 +694,7 @@ def test_convert_array_elementwise_function_to_matrix():
     d = Dummy("d")
 
     expr = ArrayElementwiseApplyFunc(Lambda(d, sin(d)), x.T*y)
-    assert convert_array_to_matrix(expr).dummy_eq(sin((x.T*y)[0, 0]))
+    assert convert_array_to_matrix(expr) == sin(MatrixElement(x.T*y, 0, 0))
 
     expr = ArrayElementwiseApplyFunc(Lambda(d, d**2), x.T*y)
     assert convert_array_to_matrix(expr) == (x.T*y)**2
diff --git a/sympy/tensor/array/expressions/tests/test_convert_matrix_to_array.py b/sympy/tensor/array/expressions/tests/test_convert_matrix_to_array.py
index a63cc4e094e0..121bf6c3369f 100644
--- a/sympy/tensor/array/expressions/tests/test_convert_matrix_to_array.py
+++ b/sympy/tensor/array/expressions/tests/test_convert_matrix_to_array.py
@@ -1,4 +1,4 @@
-from sympy import Lambda, KroneckerProduct, sqrt
+from sympy import Lambda, KroneckerProduct, sqrt, sin
 from sympy.core.symbol import symbols, Dummy
 from sympy.matrices.expressions.hadamard import (HadamardPower, HadamardProduct)
 from sympy.matrices.expressions.inverse import Inverse
@@ -140,3 +140,15 @@ def test_arrayexpr_convert_matrix_to_array():
     expr = sqrt(Trace(X.T*X))
     cg = convert_matrix_to_array(expr)
     assert cg.dummy_eq(ArrayElementwiseApplyFunc(sqrt, ArrayContraction(ArrayTensorProduct(X, X), (0, 2), (1, 3))))
+
+    expr = i**3
+    assert convert_matrix_to_array(expr) == expr
+
+    expr = i**j
+    assert convert_matrix_to_array(expr) == expr
+
+    expr = X ** sin(i)
+    assert convert_matrix_to_array(expr) == expr
+
+    expr = X ** sin(1)
+    assert convert_matrix_to_array(expr) == expr