koichi12 commited on
Commit
232d07a
·
verified ·
1 Parent(s): eac6fde

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +2 -0
  2. .venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/__init__.cpython-311.pyc +0 -0
  3. .venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/decompositions.cpython-311.pyc +0 -0
  4. .venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/determinant.cpython-311.pyc +0 -0
  5. .venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/eigen.cpython-311.pyc +0 -0
  6. .venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/exceptions.cpython-311.pyc +0 -0
  7. .venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/graph.cpython-311.pyc +0 -0
  8. .venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/immutable.cpython-311.pyc +0 -0
  9. .venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/inverse.cpython-311.pyc +0 -0
  10. .venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/matrices.cpython-311.pyc +0 -0
  11. .venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/reductions.cpython-311.pyc +0 -0
  12. .venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/repmatrix.cpython-311.pyc +0 -0
  13. .venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/solvers.cpython-311.pyc +0 -0
  14. .venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/sparse.cpython-311.pyc +0 -0
  15. .venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/sparsetools.cpython-311.pyc +0 -0
  16. .venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/subspaces.cpython-311.pyc +0 -0
  17. .venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/utilities.cpython-311.pyc +0 -0
  18. .venv/lib/python3.11/site-packages/sympy/matrices/common.py +3263 -0
  19. .venv/lib/python3.11/site-packages/sympy/matrices/determinant.py +1021 -0
  20. .venv/lib/python3.11/site-packages/sympy/matrices/eigen.py +1346 -0
  21. .venv/lib/python3.11/site-packages/sympy/matrices/expressions/__init__.py +62 -0
  22. .venv/lib/python3.11/site-packages/sympy/matrices/expressions/__pycache__/__init__.cpython-311.pyc +0 -0
  23. .venv/lib/python3.11/site-packages/sympy/matrices/expressions/__pycache__/_shape.cpython-311.pyc +0 -0
  24. .venv/lib/python3.11/site-packages/sympy/matrices/expressions/__pycache__/adjoint.cpython-311.pyc +0 -0
  25. .venv/lib/python3.11/site-packages/sympy/matrices/expressions/__pycache__/applyfunc.cpython-311.pyc +0 -0
  26. .venv/lib/python3.11/site-packages/sympy/matrices/expressions/__pycache__/blockmatrix.cpython-311.pyc +0 -0
  27. .venv/lib/python3.11/site-packages/sympy/matrices/expressions/__pycache__/companion.cpython-311.pyc +0 -0
  28. .venv/lib/python3.11/site-packages/sympy/matrices/expressions/__pycache__/diagonal.cpython-311.pyc +0 -0
  29. .venv/lib/python3.11/site-packages/sympy/matrices/expressions/__pycache__/dotproduct.cpython-311.pyc +0 -0
  30. .venv/lib/python3.11/site-packages/sympy/matrices/expressions/__pycache__/fourier.cpython-311.pyc +0 -0
  31. .venv/lib/python3.11/site-packages/sympy/matrices/expressions/__pycache__/funcmatrix.cpython-311.pyc +0 -0
  32. .venv/lib/python3.11/site-packages/sympy/matrices/expressions/__pycache__/inverse.cpython-311.pyc +0 -0
  33. .venv/lib/python3.11/site-packages/sympy/matrices/expressions/__pycache__/kronecker.cpython-311.pyc +0 -0
  34. .venv/lib/python3.11/site-packages/sympy/matrices/expressions/__pycache__/matadd.cpython-311.pyc +0 -0
  35. .venv/lib/python3.11/site-packages/sympy/matrices/expressions/__pycache__/matpow.cpython-311.pyc +0 -0
  36. .venv/lib/python3.11/site-packages/sympy/matrices/expressions/__pycache__/special.cpython-311.pyc +0 -0
  37. .venv/lib/python3.11/site-packages/sympy/matrices/expressions/adjoint.py +60 -0
  38. .venv/lib/python3.11/site-packages/sympy/matrices/expressions/applyfunc.py +204 -0
  39. .venv/lib/python3.11/site-packages/sympy/matrices/expressions/blockmatrix.py +980 -0
  40. .venv/lib/python3.11/site-packages/sympy/matrices/expressions/dotproduct.py +55 -0
  41. .venv/lib/python3.11/site-packages/sympy/matrices/expressions/factorizations.py +62 -0
  42. .venv/lib/python3.11/site-packages/sympy/matrices/expressions/funcmatrix.py +118 -0
  43. .venv/lib/python3.11/site-packages/sympy/matrices/expressions/inverse.py +112 -0
  44. .venv/lib/python3.11/site-packages/sympy/matrices/expressions/kronecker.py +434 -0
  45. .venv/lib/python3.11/site-packages/sympy/matrices/expressions/matexpr.py +888 -0
  46. .venv/lib/python3.11/site-packages/sympy/matrices/expressions/matpow.py +150 -0
  47. .venv/lib/python3.11/site-packages/sympy/matrices/expressions/slice.py +114 -0
  48. .venv/lib/python3.11/site-packages/sympy/matrices/expressions/tests/__pycache__/test_applyfunc.cpython-311.pyc +0 -0
  49. .venv/lib/python3.11/site-packages/sympy/matrices/expressions/tests/__pycache__/test_derivatives.cpython-311.pyc +0 -0
  50. .venv/lib/python3.11/site-packages/sympy/matrices/expressions/tests/__pycache__/test_dotproduct.cpython-311.pyc +0 -0
.gitattributes CHANGED
@@ -442,3 +442,5 @@ tuning-competition-baseline/.venv/lib/python3.11/site-packages/nvidia/cudnn/lib/
442
  .venv/lib/python3.11/site-packages/sympy/simplify/__pycache__/hyperexpand.cpython-311.pyc filter=lfs diff=lfs merge=lfs -text
443
  .venv/lib/python3.11/site-packages/sympy/crypto/__pycache__/crypto.cpython-311.pyc filter=lfs diff=lfs merge=lfs -text
444
  .venv/lib/python3.11/site-packages/sympy/simplify/tests/__pycache__/test_simplify.cpython-311.pyc filter=lfs diff=lfs merge=lfs -text
 
 
 
442
  .venv/lib/python3.11/site-packages/sympy/simplify/__pycache__/hyperexpand.cpython-311.pyc filter=lfs diff=lfs merge=lfs -text
443
  .venv/lib/python3.11/site-packages/sympy/crypto/__pycache__/crypto.cpython-311.pyc filter=lfs diff=lfs merge=lfs -text
444
  .venv/lib/python3.11/site-packages/sympy/simplify/tests/__pycache__/test_simplify.cpython-311.pyc filter=lfs diff=lfs merge=lfs -text
445
+ .venv/lib/python3.11/site-packages/sympy/plotting/__pycache__/series.cpython-311.pyc filter=lfs diff=lfs merge=lfs -text
446
+ .venv/lib/python3.11/site-packages/sympy/plotting/tests/__pycache__/test_series.cpython-311.pyc filter=lfs diff=lfs merge=lfs -text
.venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (3.39 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/decompositions.cpython-311.pyc ADDED
Binary file (56 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/determinant.cpython-311.pyc ADDED
Binary file (38.7 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/eigen.cpython-311.pyc ADDED
Binary file (55.7 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/exceptions.cpython-311.pyc ADDED
Binary file (1.51 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/graph.cpython-311.pyc ADDED
Binary file (10.5 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/immutable.cpython-311.pyc ADDED
Binary file (9.29 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/inverse.cpython-311.pyc ADDED
Binary file (18.7 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/matrices.cpython-311.pyc ADDED
Binary file (35.1 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/reductions.cpython-311.pyc ADDED
Binary file (15.8 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/repmatrix.cpython-311.pyc ADDED
Binary file (43.1 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/solvers.cpython-311.pyc ADDED
Binary file (34.9 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/sparse.cpython-311.pyc ADDED
Binary file (20.9 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/sparsetools.cpython-311.pyc ADDED
Binary file (12.2 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/subspaces.cpython-311.pyc ADDED
Binary file (6.01 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/__pycache__/utilities.cpython-311.pyc ADDED
Binary file (3.59 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/common.py ADDED
@@ -0,0 +1,3263 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ A module contining deprecated matrix mixin classes.
3
+
4
+ The classes in this module are deprecated and will be removed in a future
5
+ release. They are kept here for backwards compatibility in case downstream
6
+ code was subclassing them.
7
+
8
+ Importing anything else from this module is deprecated so anything here
9
+ should either not be used or should be imported from somewhere else.
10
+ """
11
+
12
+ from collections import defaultdict
13
+ from collections.abc import Iterable
14
+ from inspect import isfunction
15
+ from functools import reduce
16
+
17
+ from sympy.assumptions.refine import refine
18
+ from sympy.core import SympifyError, Add
19
+ from sympy.core.basic import Atom
20
+ from sympy.core.decorators import call_highest_priority
21
+ from sympy.core.logic import fuzzy_and, FuzzyBool
22
+ from sympy.core.numbers import Integer
23
+ from sympy.core.mod import Mod
24
+ from sympy.core.singleton import S
25
+ from sympy.core.symbol import Symbol
26
+ from sympy.core.sympify import sympify
27
+ from sympy.functions.elementary.complexes import Abs, re, im
28
+ from sympy.utilities.exceptions import sympy_deprecation_warning
29
+ from .utilities import _dotprodsimp, _simplify
30
+ from sympy.polys.polytools import Poly
31
+ from sympy.utilities.iterables import flatten, is_sequence
32
+ from sympy.utilities.misc import as_int, filldedent
33
+ from sympy.tensor.array import NDimArray
34
+
35
+ from .utilities import _get_intermediate_simp_bool
36
+
37
+
38
+ # These exception types were previously defined in this module but were moved
39
+ # to exceptions.py. We reimport them here for backwards compatibility in case
40
+ # downstream code was importing them from here.
41
+ from .exceptions import ( # noqa: F401
42
+ MatrixError, ShapeError, NonSquareMatrixError, NonInvertibleMatrixError,
43
+ NonPositiveDefiniteMatrixError
44
+ )
45
+
46
+
47
+ _DEPRECATED_MIXINS = (
48
+ 'MatrixShaping',
49
+ 'MatrixSpecial',
50
+ 'MatrixProperties',
51
+ 'MatrixOperations',
52
+ 'MatrixArithmetic',
53
+ 'MatrixCommon',
54
+ 'MatrixDeterminant',
55
+ 'MatrixReductions',
56
+ 'MatrixSubspaces',
57
+ 'MatrixEigen',
58
+ 'MatrixCalculus',
59
+ 'MatrixDeprecated',
60
+ )
61
+
62
+
63
+ class _MatrixDeprecatedMeta(type):
64
+
65
+ #
66
+ # Override the default __instancecheck__ implementation to ensure that
67
+ # e.g. isinstance(M, MatrixCommon) still works when M is one of the
68
+ # matrix classes. Matrix no longer inherits from MatrixCommon so
69
+ # isinstance(M, MatrixCommon) would now return False by default.
70
+ #
71
+ # There were lots of places in the codebase where this was being done
72
+ # so it seems likely that downstream code may be doing it too. All use
73
+ # of these mixins is deprecated though so we give a deprecation warning
74
+ # unconditionally if they are being used with isinstance.
75
+ #
76
+ # Any code seeing this deprecation warning should be changed to use
77
+ # isinstance(M, MatrixBase) instead which also works in previous versions
78
+ # of SymPy.
79
+ #
80
+
81
+ def __instancecheck__(cls, instance):
82
+
83
+ sympy_deprecation_warning(
84
+ f"""
85
+ Checking whether an object is an instance of {cls.__name__} is
86
+ deprecated.
87
+
88
+ Use `isinstance(obj, Matrix)` instead of `isinstance(obj, {cls.__name__})`.
89
+ """,
90
+ deprecated_since_version="1.13",
91
+ active_deprecations_target="deprecated-matrix-mixins",
92
+ stacklevel=3,
93
+ )
94
+
95
+ from sympy.matrices.matrixbase import MatrixBase
96
+ from sympy.matrices.matrices import (
97
+ MatrixDeterminant,
98
+ MatrixReductions,
99
+ MatrixSubspaces,
100
+ MatrixEigen,
101
+ MatrixCalculus,
102
+ MatrixDeprecated
103
+ )
104
+
105
+ all_mixins = (
106
+ MatrixRequired,
107
+ MatrixShaping,
108
+ MatrixSpecial,
109
+ MatrixProperties,
110
+ MatrixOperations,
111
+ MatrixArithmetic,
112
+ MatrixCommon,
113
+ MatrixDeterminant,
114
+ MatrixReductions,
115
+ MatrixSubspaces,
116
+ MatrixEigen,
117
+ MatrixCalculus,
118
+ MatrixDeprecated
119
+ )
120
+
121
+ if cls in all_mixins and isinstance(instance, MatrixBase):
122
+ return True
123
+ else:
124
+ return super().__instancecheck__(instance)
125
+
126
+
127
+ class MatrixRequired(metaclass=_MatrixDeprecatedMeta):
128
+ """Deprecated mixin class for making matrix classes."""
129
+
130
+ rows = None # type: int
131
+ cols = None # type: int
132
+ _simplify = None
133
+
134
+ def __init_subclass__(cls, **kwargs):
135
+
136
+ # Warn if any downstream code is subclassing this class or any of the
137
+ # deprecated mixin classes that are all ultimately subclasses of this
138
+ # class.
139
+ #
140
+ # We don't want to warn about the deprecated mixins themselves being
141
+ # created, but only about them being used as mixins by downstream code.
142
+ # Otherwise just importing this module would trigger a warning.
143
+ # Ultimately the whole module should be deprecated and removed but for
144
+ # SymPy 1.13 it is premature to do that given that this module was the
145
+ # main way to import matrix exception types in all previous versions.
146
+
147
+ if cls.__name__ not in _DEPRECATED_MIXINS:
148
+ sympy_deprecation_warning(
149
+ f"""
150
+ Inheriting from the Matrix mixin classes is deprecated.
151
+
152
+ The class {cls.__name__} is subclassing a deprecated mixin.
153
+ """,
154
+ deprecated_since_version="1.13",
155
+ active_deprecations_target="deprecated-matrix-mixins",
156
+ stacklevel=3,
157
+ )
158
+
159
+ super().__init_subclass__(**kwargs)
160
+
161
+ @classmethod
162
+ def _new(cls, *args, **kwargs):
163
+ """`_new` must, at minimum, be callable as
164
+ `_new(rows, cols, mat) where mat is a flat list of the
165
+ elements of the matrix."""
166
+ raise NotImplementedError("Subclasses must implement this.")
167
+
168
+ def __eq__(self, other):
169
+ raise NotImplementedError("Subclasses must implement this.")
170
+
171
+ def __getitem__(self, key):
172
+ """Implementations of __getitem__ should accept ints, in which
173
+ case the matrix is indexed as a flat list, tuples (i,j) in which
174
+ case the (i,j) entry is returned, slices, or mixed tuples (a,b)
175
+ where a and b are any combination of slices and integers."""
176
+ raise NotImplementedError("Subclasses must implement this.")
177
+
178
+ def __len__(self):
179
+ """The total number of entries in the matrix."""
180
+ raise NotImplementedError("Subclasses must implement this.")
181
+
182
+ @property
183
+ def shape(self):
184
+ raise NotImplementedError("Subclasses must implement this.")
185
+
186
+
187
+ class MatrixShaping(MatrixRequired):
188
+ """Provides basic matrix shaping and extracting of submatrices"""
189
+
190
+ def _eval_col_del(self, col):
191
+ def entry(i, j):
192
+ return self[i, j] if j < col else self[i, j + 1]
193
+ return self._new(self.rows, self.cols - 1, entry)
194
+
195
+ def _eval_col_insert(self, pos, other):
196
+
197
+ def entry(i, j):
198
+ if j < pos:
199
+ return self[i, j]
200
+ elif pos <= j < pos + other.cols:
201
+ return other[i, j - pos]
202
+ return self[i, j - other.cols]
203
+
204
+ return self._new(self.rows, self.cols + other.cols, entry)
205
+
206
+ def _eval_col_join(self, other):
207
+ rows = self.rows
208
+
209
+ def entry(i, j):
210
+ if i < rows:
211
+ return self[i, j]
212
+ return other[i - rows, j]
213
+
214
+ return classof(self, other)._new(self.rows + other.rows, self.cols,
215
+ entry)
216
+
217
+ def _eval_extract(self, rowsList, colsList):
218
+ mat = list(self)
219
+ cols = self.cols
220
+ indices = (i * cols + j for i in rowsList for j in colsList)
221
+ return self._new(len(rowsList), len(colsList),
222
+ [mat[i] for i in indices])
223
+
224
+ def _eval_get_diag_blocks(self):
225
+ sub_blocks = []
226
+
227
+ def recurse_sub_blocks(M):
228
+ i = 1
229
+ while i <= M.shape[0]:
230
+ if i == 1:
231
+ to_the_right = M[0, i:]
232
+ to_the_bottom = M[i:, 0]
233
+ else:
234
+ to_the_right = M[:i, i:]
235
+ to_the_bottom = M[i:, :i]
236
+ if any(to_the_right) or any(to_the_bottom):
237
+ i += 1
238
+ continue
239
+ else:
240
+ sub_blocks.append(M[:i, :i])
241
+ if M.shape == M[:i, :i].shape:
242
+ return
243
+ else:
244
+ recurse_sub_blocks(M[i:, i:])
245
+ return
246
+
247
+ recurse_sub_blocks(self)
248
+ return sub_blocks
249
+
250
+ def _eval_row_del(self, row):
251
+ def entry(i, j):
252
+ return self[i, j] if i < row else self[i + 1, j]
253
+ return self._new(self.rows - 1, self.cols, entry)
254
+
255
+ def _eval_row_insert(self, pos, other):
256
+ entries = list(self)
257
+ insert_pos = pos * self.cols
258
+ entries[insert_pos:insert_pos] = list(other)
259
+ return self._new(self.rows + other.rows, self.cols, entries)
260
+
261
+ def _eval_row_join(self, other):
262
+ cols = self.cols
263
+
264
+ def entry(i, j):
265
+ if j < cols:
266
+ return self[i, j]
267
+ return other[i, j - cols]
268
+
269
+ return classof(self, other)._new(self.rows, self.cols + other.cols,
270
+ entry)
271
+
272
+ def _eval_tolist(self):
273
+ return [list(self[i,:]) for i in range(self.rows)]
274
+
275
+ def _eval_todok(self):
276
+ dok = {}
277
+ rows, cols = self.shape
278
+ for i in range(rows):
279
+ for j in range(cols):
280
+ val = self[i, j]
281
+ if val != self.zero:
282
+ dok[i, j] = val
283
+ return dok
284
+
285
+ def _eval_vec(self):
286
+ rows = self.rows
287
+
288
+ def entry(n, _):
289
+ # we want to read off the columns first
290
+ j = n // rows
291
+ i = n - j * rows
292
+ return self[i, j]
293
+
294
+ return self._new(len(self), 1, entry)
295
+
296
+ def _eval_vech(self, diagonal):
297
+ c = self.cols
298
+ v = []
299
+ if diagonal:
300
+ for j in range(c):
301
+ for i in range(j, c):
302
+ v.append(self[i, j])
303
+ else:
304
+ for j in range(c):
305
+ for i in range(j + 1, c):
306
+ v.append(self[i, j])
307
+ return self._new(len(v), 1, v)
308
+
309
+ def col_del(self, col):
310
+ """Delete the specified column."""
311
+ if col < 0:
312
+ col += self.cols
313
+ if not 0 <= col < self.cols:
314
+ raise IndexError("Column {} is out of range.".format(col))
315
+ return self._eval_col_del(col)
316
+
317
+ def col_insert(self, pos, other):
318
+ """Insert one or more columns at the given column position.
319
+
320
+ Examples
321
+ ========
322
+
323
+ >>> from sympy import zeros, ones
324
+ >>> M = zeros(3)
325
+ >>> V = ones(3, 1)
326
+ >>> M.col_insert(1, V)
327
+ Matrix([
328
+ [0, 1, 0, 0],
329
+ [0, 1, 0, 0],
330
+ [0, 1, 0, 0]])
331
+
332
+ See Also
333
+ ========
334
+
335
+ col
336
+ row_insert
337
+ """
338
+ # Allows you to build a matrix even if it is null matrix
339
+ if not self:
340
+ return type(self)(other)
341
+
342
+ pos = as_int(pos)
343
+
344
+ if pos < 0:
345
+ pos = self.cols + pos
346
+ if pos < 0:
347
+ pos = 0
348
+ elif pos > self.cols:
349
+ pos = self.cols
350
+
351
+ if self.rows != other.rows:
352
+ raise ShapeError(
353
+ "The matrices have incompatible number of rows ({} and {})"
354
+ .format(self.rows, other.rows))
355
+
356
+ return self._eval_col_insert(pos, other)
357
+
358
+ def col_join(self, other):
359
+ """Concatenates two matrices along self's last and other's first row.
360
+
361
+ Examples
362
+ ========
363
+
364
+ >>> from sympy import zeros, ones
365
+ >>> M = zeros(3)
366
+ >>> V = ones(1, 3)
367
+ >>> M.col_join(V)
368
+ Matrix([
369
+ [0, 0, 0],
370
+ [0, 0, 0],
371
+ [0, 0, 0],
372
+ [1, 1, 1]])
373
+
374
+ See Also
375
+ ========
376
+
377
+ col
378
+ row_join
379
+ """
380
+ # A null matrix can always be stacked (see #10770)
381
+ if self.rows == 0 and self.cols != other.cols:
382
+ return self._new(0, other.cols, []).col_join(other)
383
+
384
+ if self.cols != other.cols:
385
+ raise ShapeError(
386
+ "The matrices have incompatible number of columns ({} and {})"
387
+ .format(self.cols, other.cols))
388
+ return self._eval_col_join(other)
389
+
390
+ def col(self, j):
391
+ """Elementary column selector.
392
+
393
+ Examples
394
+ ========
395
+
396
+ >>> from sympy import eye
397
+ >>> eye(2).col(0)
398
+ Matrix([
399
+ [1],
400
+ [0]])
401
+
402
+ See Also
403
+ ========
404
+
405
+ row
406
+ col_del
407
+ col_join
408
+ col_insert
409
+ """
410
+ return self[:, j]
411
+
412
+ def extract(self, rowsList, colsList):
413
+ r"""Return a submatrix by specifying a list of rows and columns.
414
+ Negative indices can be given. All indices must be in the range
415
+ $-n \le i < n$ where $n$ is the number of rows or columns.
416
+
417
+ Examples
418
+ ========
419
+
420
+ >>> from sympy import Matrix
421
+ >>> m = Matrix(4, 3, range(12))
422
+ >>> m
423
+ Matrix([
424
+ [0, 1, 2],
425
+ [3, 4, 5],
426
+ [6, 7, 8],
427
+ [9, 10, 11]])
428
+ >>> m.extract([0, 1, 3], [0, 1])
429
+ Matrix([
430
+ [0, 1],
431
+ [3, 4],
432
+ [9, 10]])
433
+
434
+ Rows or columns can be repeated:
435
+
436
+ >>> m.extract([0, 0, 1], [-1])
437
+ Matrix([
438
+ [2],
439
+ [2],
440
+ [5]])
441
+
442
+ Every other row can be taken by using range to provide the indices:
443
+
444
+ >>> m.extract(range(0, m.rows, 2), [-1])
445
+ Matrix([
446
+ [2],
447
+ [8]])
448
+
449
+ RowsList or colsList can also be a list of booleans, in which case
450
+ the rows or columns corresponding to the True values will be selected:
451
+
452
+ >>> m.extract([0, 1, 2, 3], [True, False, True])
453
+ Matrix([
454
+ [0, 2],
455
+ [3, 5],
456
+ [6, 8],
457
+ [9, 11]])
458
+ """
459
+
460
+ if not is_sequence(rowsList) or not is_sequence(colsList):
461
+ raise TypeError("rowsList and colsList must be iterable")
462
+ # ensure rowsList and colsList are lists of integers
463
+ if rowsList and all(isinstance(i, bool) for i in rowsList):
464
+ rowsList = [index for index, item in enumerate(rowsList) if item]
465
+ if colsList and all(isinstance(i, bool) for i in colsList):
466
+ colsList = [index for index, item in enumerate(colsList) if item]
467
+
468
+ # ensure everything is in range
469
+ rowsList = [a2idx(k, self.rows) for k in rowsList]
470
+ colsList = [a2idx(k, self.cols) for k in colsList]
471
+
472
+ return self._eval_extract(rowsList, colsList)
473
+
474
+ def get_diag_blocks(self):
475
+ """Obtains the square sub-matrices on the main diagonal of a square matrix.
476
+
477
+ Useful for inverting symbolic matrices or solving systems of
478
+ linear equations which may be decoupled by having a block diagonal
479
+ structure.
480
+
481
+ Examples
482
+ ========
483
+
484
+ >>> from sympy import Matrix
485
+ >>> from sympy.abc import x, y, z
486
+ >>> A = Matrix([[1, 3, 0, 0], [y, z*z, 0, 0], [0, 0, x, 0], [0, 0, 0, 0]])
487
+ >>> a1, a2, a3 = A.get_diag_blocks()
488
+ >>> a1
489
+ Matrix([
490
+ [1, 3],
491
+ [y, z**2]])
492
+ >>> a2
493
+ Matrix([[x]])
494
+ >>> a3
495
+ Matrix([[0]])
496
+
497
+ """
498
+ return self._eval_get_diag_blocks()
499
+
500
+ @classmethod
501
+ def hstack(cls, *args):
502
+ """Return a matrix formed by joining args horizontally (i.e.
503
+ by repeated application of row_join).
504
+
505
+ Examples
506
+ ========
507
+
508
+ >>> from sympy import Matrix, eye
509
+ >>> Matrix.hstack(eye(2), 2*eye(2))
510
+ Matrix([
511
+ [1, 0, 2, 0],
512
+ [0, 1, 0, 2]])
513
+ """
514
+ if len(args) == 0:
515
+ return cls._new()
516
+
517
+ kls = type(args[0])
518
+ return reduce(kls.row_join, args)
519
+
520
+ def reshape(self, rows, cols):
521
+ """Reshape the matrix. Total number of elements must remain the same.
522
+
523
+ Examples
524
+ ========
525
+
526
+ >>> from sympy import Matrix
527
+ >>> m = Matrix(2, 3, lambda i, j: 1)
528
+ >>> m
529
+ Matrix([
530
+ [1, 1, 1],
531
+ [1, 1, 1]])
532
+ >>> m.reshape(1, 6)
533
+ Matrix([[1, 1, 1, 1, 1, 1]])
534
+ >>> m.reshape(3, 2)
535
+ Matrix([
536
+ [1, 1],
537
+ [1, 1],
538
+ [1, 1]])
539
+
540
+ """
541
+ if self.rows * self.cols != rows * cols:
542
+ raise ValueError("Invalid reshape parameters %d %d" % (rows, cols))
543
+ return self._new(rows, cols, lambda i, j: self[i * cols + j])
544
+
545
+ def row_del(self, row):
546
+ """Delete the specified row."""
547
+ if row < 0:
548
+ row += self.rows
549
+ if not 0 <= row < self.rows:
550
+ raise IndexError("Row {} is out of range.".format(row))
551
+
552
+ return self._eval_row_del(row)
553
+
554
+ def row_insert(self, pos, other):
555
+ """Insert one or more rows at the given row position.
556
+
557
+ Examples
558
+ ========
559
+
560
+ >>> from sympy import zeros, ones
561
+ >>> M = zeros(3)
562
+ >>> V = ones(1, 3)
563
+ >>> M.row_insert(1, V)
564
+ Matrix([
565
+ [0, 0, 0],
566
+ [1, 1, 1],
567
+ [0, 0, 0],
568
+ [0, 0, 0]])
569
+
570
+ See Also
571
+ ========
572
+
573
+ row
574
+ col_insert
575
+ """
576
+ # Allows you to build a matrix even if it is null matrix
577
+ if not self:
578
+ return self._new(other)
579
+
580
+ pos = as_int(pos)
581
+
582
+ if pos < 0:
583
+ pos = self.rows + pos
584
+ if pos < 0:
585
+ pos = 0
586
+ elif pos > self.rows:
587
+ pos = self.rows
588
+
589
+ if self.cols != other.cols:
590
+ raise ShapeError(
591
+ "The matrices have incompatible number of columns ({} and {})"
592
+ .format(self.cols, other.cols))
593
+
594
+ return self._eval_row_insert(pos, other)
595
+
596
+ def row_join(self, other):
597
+ """Concatenates two matrices along self's last and rhs's first column
598
+
599
+ Examples
600
+ ========
601
+
602
+ >>> from sympy import zeros, ones
603
+ >>> M = zeros(3)
604
+ >>> V = ones(3, 1)
605
+ >>> M.row_join(V)
606
+ Matrix([
607
+ [0, 0, 0, 1],
608
+ [0, 0, 0, 1],
609
+ [0, 0, 0, 1]])
610
+
611
+ See Also
612
+ ========
613
+
614
+ row
615
+ col_join
616
+ """
617
+ # A null matrix can always be stacked (see #10770)
618
+ if self.cols == 0 and self.rows != other.rows:
619
+ return self._new(other.rows, 0, []).row_join(other)
620
+
621
+ if self.rows != other.rows:
622
+ raise ShapeError(
623
+ "The matrices have incompatible number of rows ({} and {})"
624
+ .format(self.rows, other.rows))
625
+ return self._eval_row_join(other)
626
+
627
+ def diagonal(self, k=0):
628
+ """Returns the kth diagonal of self. The main diagonal
629
+ corresponds to `k=0`; diagonals above and below correspond to
630
+ `k > 0` and `k < 0`, respectively. The values of `self[i, j]`
631
+ for which `j - i = k`, are returned in order of increasing
632
+ `i + j`, starting with `i + j = |k|`.
633
+
634
+ Examples
635
+ ========
636
+
637
+ >>> from sympy import Matrix
638
+ >>> m = Matrix(3, 3, lambda i, j: j - i); m
639
+ Matrix([
640
+ [ 0, 1, 2],
641
+ [-1, 0, 1],
642
+ [-2, -1, 0]])
643
+ >>> _.diagonal()
644
+ Matrix([[0, 0, 0]])
645
+ >>> m.diagonal(1)
646
+ Matrix([[1, 1]])
647
+ >>> m.diagonal(-2)
648
+ Matrix([[-2]])
649
+
650
+ Even though the diagonal is returned as a Matrix, the element
651
+ retrieval can be done with a single index:
652
+
653
+ >>> Matrix.diag(1, 2, 3).diagonal()[1] # instead of [0, 1]
654
+ 2
655
+
656
+ See Also
657
+ ========
658
+
659
+ diag
660
+ """
661
+ rv = []
662
+ k = as_int(k)
663
+ r = 0 if k > 0 else -k
664
+ c = 0 if r else k
665
+ while True:
666
+ if r == self.rows or c == self.cols:
667
+ break
668
+ rv.append(self[r, c])
669
+ r += 1
670
+ c += 1
671
+ if not rv:
672
+ raise ValueError(filldedent('''
673
+ The %s diagonal is out of range [%s, %s]''' % (
674
+ k, 1 - self.rows, self.cols - 1)))
675
+ return self._new(1, len(rv), rv)
676
+
677
+ def row(self, i):
678
+ """Elementary row selector.
679
+
680
+ Examples
681
+ ========
682
+
683
+ >>> from sympy import eye
684
+ >>> eye(2).row(0)
685
+ Matrix([[1, 0]])
686
+
687
+ See Also
688
+ ========
689
+
690
+ col
691
+ row_del
692
+ row_join
693
+ row_insert
694
+ """
695
+ return self[i, :]
696
+
697
+ @property
698
+ def shape(self):
699
+ """The shape (dimensions) of the matrix as the 2-tuple (rows, cols).
700
+
701
+ Examples
702
+ ========
703
+
704
+ >>> from sympy import zeros
705
+ >>> M = zeros(2, 3)
706
+ >>> M.shape
707
+ (2, 3)
708
+ >>> M.rows
709
+ 2
710
+ >>> M.cols
711
+ 3
712
+ """
713
+ return (self.rows, self.cols)
714
+
715
+ def todok(self):
716
+ """Return the matrix as dictionary of keys.
717
+
718
+ Examples
719
+ ========
720
+
721
+ >>> from sympy import Matrix
722
+ >>> M = Matrix.eye(3)
723
+ >>> M.todok()
724
+ {(0, 0): 1, (1, 1): 1, (2, 2): 1}
725
+ """
726
+ return self._eval_todok()
727
+
728
+ def tolist(self):
729
+ """Return the Matrix as a nested Python list.
730
+
731
+ Examples
732
+ ========
733
+
734
+ >>> from sympy import Matrix, ones
735
+ >>> m = Matrix(3, 3, range(9))
736
+ >>> m
737
+ Matrix([
738
+ [0, 1, 2],
739
+ [3, 4, 5],
740
+ [6, 7, 8]])
741
+ >>> m.tolist()
742
+ [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
743
+ >>> ones(3, 0).tolist()
744
+ [[], [], []]
745
+
746
+ When there are no rows then it will not be possible to tell how
747
+ many columns were in the original matrix:
748
+
749
+ >>> ones(0, 3).tolist()
750
+ []
751
+
752
+ """
753
+ if not self.rows:
754
+ return []
755
+ if not self.cols:
756
+ return [[] for i in range(self.rows)]
757
+ return self._eval_tolist()
758
+
759
+ def todod(M):
760
+ """Returns matrix as dict of dicts containing non-zero elements of the Matrix
761
+
762
+ Examples
763
+ ========
764
+
765
+ >>> from sympy import Matrix
766
+ >>> A = Matrix([[0, 1],[0, 3]])
767
+ >>> A
768
+ Matrix([
769
+ [0, 1],
770
+ [0, 3]])
771
+ >>> A.todod()
772
+ {0: {1: 1}, 1: {1: 3}}
773
+
774
+
775
+ """
776
+ rowsdict = {}
777
+ Mlol = M.tolist()
778
+ for i, Mi in enumerate(Mlol):
779
+ row = {j: Mij for j, Mij in enumerate(Mi) if Mij}
780
+ if row:
781
+ rowsdict[i] = row
782
+ return rowsdict
783
+
784
+ def vec(self):
785
+ """Return the Matrix converted into a one column matrix by stacking columns
786
+
787
+ Examples
788
+ ========
789
+
790
+ >>> from sympy import Matrix
791
+ >>> m=Matrix([[1, 3], [2, 4]])
792
+ >>> m
793
+ Matrix([
794
+ [1, 3],
795
+ [2, 4]])
796
+ >>> m.vec()
797
+ Matrix([
798
+ [1],
799
+ [2],
800
+ [3],
801
+ [4]])
802
+
803
+ See Also
804
+ ========
805
+
806
+ vech
807
+ """
808
+ return self._eval_vec()
809
+
810
+ def vech(self, diagonal=True, check_symmetry=True):
811
+ """Reshapes the matrix into a column vector by stacking the
812
+ elements in the lower triangle.
813
+
814
+ Parameters
815
+ ==========
816
+
817
+ diagonal : bool, optional
818
+ If ``True``, it includes the diagonal elements.
819
+
820
+ check_symmetry : bool, optional
821
+ If ``True``, it checks whether the matrix is symmetric.
822
+
823
+ Examples
824
+ ========
825
+
826
+ >>> from sympy import Matrix
827
+ >>> m=Matrix([[1, 2], [2, 3]])
828
+ >>> m
829
+ Matrix([
830
+ [1, 2],
831
+ [2, 3]])
832
+ >>> m.vech()
833
+ Matrix([
834
+ [1],
835
+ [2],
836
+ [3]])
837
+ >>> m.vech(diagonal=False)
838
+ Matrix([[2]])
839
+
840
+ Notes
841
+ =====
842
+
843
+ This should work for symmetric matrices and ``vech`` can
844
+ represent symmetric matrices in vector form with less size than
845
+ ``vec``.
846
+
847
+ See Also
848
+ ========
849
+
850
+ vec
851
+ """
852
+ if not self.is_square:
853
+ raise NonSquareMatrixError
854
+
855
+ if check_symmetry and not self.is_symmetric():
856
+ raise ValueError("The matrix is not symmetric.")
857
+
858
+ return self._eval_vech(diagonal)
859
+
860
+ @classmethod
861
+ def vstack(cls, *args):
862
+ """Return a matrix formed by joining args vertically (i.e.
863
+ by repeated application of col_join).
864
+
865
+ Examples
866
+ ========
867
+
868
+ >>> from sympy import Matrix, eye
869
+ >>> Matrix.vstack(eye(2), 2*eye(2))
870
+ Matrix([
871
+ [1, 0],
872
+ [0, 1],
873
+ [2, 0],
874
+ [0, 2]])
875
+ """
876
+ if len(args) == 0:
877
+ return cls._new()
878
+
879
+ kls = type(args[0])
880
+ return reduce(kls.col_join, args)
881
+
882
+
883
+ class MatrixSpecial(MatrixRequired):
884
+ """Construction of special matrices"""
885
+
886
+ @classmethod
887
+ def _eval_diag(cls, rows, cols, diag_dict):
888
+ """diag_dict is a defaultdict containing
889
+ all the entries of the diagonal matrix."""
890
+ def entry(i, j):
891
+ return diag_dict[(i, j)]
892
+ return cls._new(rows, cols, entry)
893
+
894
+ @classmethod
895
+ def _eval_eye(cls, rows, cols):
896
+ vals = [cls.zero]*(rows*cols)
897
+ vals[::cols+1] = [cls.one]*min(rows, cols)
898
+ return cls._new(rows, cols, vals, copy=False)
899
+
900
+ @classmethod
901
+ def _eval_jordan_block(cls, size: int, eigenvalue, band='upper'):
902
+ if band == 'lower':
903
+ def entry(i, j):
904
+ if i == j:
905
+ return eigenvalue
906
+ elif j + 1 == i:
907
+ return cls.one
908
+ return cls.zero
909
+ else:
910
+ def entry(i, j):
911
+ if i == j:
912
+ return eigenvalue
913
+ elif i + 1 == j:
914
+ return cls.one
915
+ return cls.zero
916
+ return cls._new(size, size, entry)
917
+
918
+ @classmethod
919
+ def _eval_ones(cls, rows, cols):
920
+ def entry(i, j):
921
+ return cls.one
922
+ return cls._new(rows, cols, entry)
923
+
924
+ @classmethod
925
+ def _eval_zeros(cls, rows, cols):
926
+ return cls._new(rows, cols, [cls.zero]*(rows*cols), copy=False)
927
+
928
+ @classmethod
929
+ def _eval_wilkinson(cls, n):
930
+ def entry(i, j):
931
+ return cls.one if i + 1 == j else cls.zero
932
+
933
+ D = cls._new(2*n + 1, 2*n + 1, entry)
934
+
935
+ wminus = cls.diag(list(range(-n, n + 1)), unpack=True) + D + D.T
936
+ wplus = abs(cls.diag(list(range(-n, n + 1)), unpack=True)) + D + D.T
937
+
938
+ return wminus, wplus
939
+
940
+ @classmethod
941
+ def diag(kls, *args, strict=False, unpack=True, rows=None, cols=None, **kwargs):
942
+ """Returns a matrix with the specified diagonal.
943
+ If matrices are passed, a block-diagonal matrix
944
+ is created (i.e. the "direct sum" of the matrices).
945
+
946
+ kwargs
947
+ ======
948
+
949
+ rows : rows of the resulting matrix; computed if
950
+ not given.
951
+
952
+ cols : columns of the resulting matrix; computed if
953
+ not given.
954
+
955
+ cls : class for the resulting matrix
956
+
957
+ unpack : bool which, when True (default), unpacks a single
958
+ sequence rather than interpreting it as a Matrix.
959
+
960
+ strict : bool which, when False (default), allows Matrices to
961
+ have variable-length rows.
962
+
963
+ Examples
964
+ ========
965
+
966
+ >>> from sympy import Matrix
967
+ >>> Matrix.diag(1, 2, 3)
968
+ Matrix([
969
+ [1, 0, 0],
970
+ [0, 2, 0],
971
+ [0, 0, 3]])
972
+
973
+ The current default is to unpack a single sequence. If this is
974
+ not desired, set `unpack=False` and it will be interpreted as
975
+ a matrix.
976
+
977
+ >>> Matrix.diag([1, 2, 3]) == Matrix.diag(1, 2, 3)
978
+ True
979
+
980
+ When more than one element is passed, each is interpreted as
981
+ something to put on the diagonal. Lists are converted to
982
+ matrices. Filling of the diagonal always continues from
983
+ the bottom right hand corner of the previous item: this
984
+ will create a block-diagonal matrix whether the matrices
985
+ are square or not.
986
+
987
+ >>> col = [1, 2, 3]
988
+ >>> row = [[4, 5]]
989
+ >>> Matrix.diag(col, row)
990
+ Matrix([
991
+ [1, 0, 0],
992
+ [2, 0, 0],
993
+ [3, 0, 0],
994
+ [0, 4, 5]])
995
+
996
+ When `unpack` is False, elements within a list need not all be
997
+ of the same length. Setting `strict` to True would raise a
998
+ ValueError for the following:
999
+
1000
+ >>> Matrix.diag([[1, 2, 3], [4, 5], [6]], unpack=False)
1001
+ Matrix([
1002
+ [1, 2, 3],
1003
+ [4, 5, 0],
1004
+ [6, 0, 0]])
1005
+
1006
+ The type of the returned matrix can be set with the ``cls``
1007
+ keyword.
1008
+
1009
+ >>> from sympy import ImmutableMatrix
1010
+ >>> from sympy.utilities.misc import func_name
1011
+ >>> func_name(Matrix.diag(1, cls=ImmutableMatrix))
1012
+ 'ImmutableDenseMatrix'
1013
+
1014
+ A zero dimension matrix can be used to position the start of
1015
+ the filling at the start of an arbitrary row or column:
1016
+
1017
+ >>> from sympy import ones
1018
+ >>> r2 = ones(0, 2)
1019
+ >>> Matrix.diag(r2, 1, 2)
1020
+ Matrix([
1021
+ [0, 0, 1, 0],
1022
+ [0, 0, 0, 2]])
1023
+
1024
+ See Also
1025
+ ========
1026
+ eye
1027
+ diagonal
1028
+ .dense.diag
1029
+ .expressions.blockmatrix.BlockMatrix
1030
+ .sparsetools.banded
1031
+ """
1032
+ from sympy.matrices.matrixbase import MatrixBase
1033
+ from sympy.matrices.dense import Matrix
1034
+ from sympy.matrices import SparseMatrix
1035
+ klass = kwargs.get('cls', kls)
1036
+ if unpack and len(args) == 1 and is_sequence(args[0]) and \
1037
+ not isinstance(args[0], MatrixBase):
1038
+ args = args[0]
1039
+
1040
+ # fill a default dict with the diagonal entries
1041
+ diag_entries = defaultdict(int)
1042
+ rmax = cmax = 0 # keep track of the biggest index seen
1043
+ for m in args:
1044
+ if isinstance(m, list):
1045
+ if strict:
1046
+ # if malformed, Matrix will raise an error
1047
+ _ = Matrix(m)
1048
+ r, c = _.shape
1049
+ m = _.tolist()
1050
+ else:
1051
+ r, c, smat = SparseMatrix._handle_creation_inputs(m)
1052
+ for (i, j), _ in smat.items():
1053
+ diag_entries[(i + rmax, j + cmax)] = _
1054
+ m = [] # to skip process below
1055
+ elif hasattr(m, 'shape'): # a Matrix
1056
+ # convert to list of lists
1057
+ r, c = m.shape
1058
+ m = m.tolist()
1059
+ else: # in this case, we're a single value
1060
+ diag_entries[(rmax, cmax)] = m
1061
+ rmax += 1
1062
+ cmax += 1
1063
+ continue
1064
+ # process list of lists
1065
+ for i, mi in enumerate(m):
1066
+ for j, _ in enumerate(mi):
1067
+ diag_entries[(i + rmax, j + cmax)] = _
1068
+ rmax += r
1069
+ cmax += c
1070
+ if rows is None:
1071
+ rows, cols = cols, rows
1072
+ if rows is None:
1073
+ rows, cols = rmax, cmax
1074
+ else:
1075
+ cols = rows if cols is None else cols
1076
+ if rows < rmax or cols < cmax:
1077
+ raise ValueError(filldedent('''
1078
+ The constructed matrix is {} x {} but a size of {} x {}
1079
+ was specified.'''.format(rmax, cmax, rows, cols)))
1080
+ return klass._eval_diag(rows, cols, diag_entries)
1081
+
1082
+ @classmethod
1083
+ def eye(kls, rows, cols=None, **kwargs):
1084
+ """Returns an identity matrix.
1085
+
1086
+ Parameters
1087
+ ==========
1088
+
1089
+ rows : rows of the matrix
1090
+ cols : cols of the matrix (if None, cols=rows)
1091
+
1092
+ kwargs
1093
+ ======
1094
+ cls : class of the returned matrix
1095
+ """
1096
+ if cols is None:
1097
+ cols = rows
1098
+ if rows < 0 or cols < 0:
1099
+ raise ValueError("Cannot create a {} x {} matrix. "
1100
+ "Both dimensions must be positive".format(rows, cols))
1101
+ klass = kwargs.get('cls', kls)
1102
+ rows, cols = as_int(rows), as_int(cols)
1103
+
1104
+ return klass._eval_eye(rows, cols)
1105
+
1106
+ @classmethod
1107
+ def jordan_block(kls, size=None, eigenvalue=None, *, band='upper', **kwargs):
1108
+ """Returns a Jordan block
1109
+
1110
+ Parameters
1111
+ ==========
1112
+
1113
+ size : Integer, optional
1114
+ Specifies the shape of the Jordan block matrix.
1115
+
1116
+ eigenvalue : Number or Symbol
1117
+ Specifies the value for the main diagonal of the matrix.
1118
+
1119
+ .. note::
1120
+ The keyword ``eigenval`` is also specified as an alias
1121
+ of this keyword, but it is not recommended to use.
1122
+
1123
+ We may deprecate the alias in later release.
1124
+
1125
+ band : 'upper' or 'lower', optional
1126
+ Specifies the position of the off-diagonal to put `1` s on.
1127
+
1128
+ cls : Matrix, optional
1129
+ Specifies the matrix class of the output form.
1130
+
1131
+ If it is not specified, the class type where the method is
1132
+ being executed on will be returned.
1133
+
1134
+ Returns
1135
+ =======
1136
+
1137
+ Matrix
1138
+ A Jordan block matrix.
1139
+
1140
+ Raises
1141
+ ======
1142
+
1143
+ ValueError
1144
+ If insufficient arguments are given for matrix size
1145
+ specification, or no eigenvalue is given.
1146
+
1147
+ Examples
1148
+ ========
1149
+
1150
+ Creating a default Jordan block:
1151
+
1152
+ >>> from sympy import Matrix
1153
+ >>> from sympy.abc import x
1154
+ >>> Matrix.jordan_block(4, x)
1155
+ Matrix([
1156
+ [x, 1, 0, 0],
1157
+ [0, x, 1, 0],
1158
+ [0, 0, x, 1],
1159
+ [0, 0, 0, x]])
1160
+
1161
+ Creating an alternative Jordan block matrix where `1` is on
1162
+ lower off-diagonal:
1163
+
1164
+ >>> Matrix.jordan_block(4, x, band='lower')
1165
+ Matrix([
1166
+ [x, 0, 0, 0],
1167
+ [1, x, 0, 0],
1168
+ [0, 1, x, 0],
1169
+ [0, 0, 1, x]])
1170
+
1171
+ Creating a Jordan block with keyword arguments
1172
+
1173
+ >>> Matrix.jordan_block(size=4, eigenvalue=x)
1174
+ Matrix([
1175
+ [x, 1, 0, 0],
1176
+ [0, x, 1, 0],
1177
+ [0, 0, x, 1],
1178
+ [0, 0, 0, x]])
1179
+
1180
+ References
1181
+ ==========
1182
+
1183
+ .. [1] https://en.wikipedia.org/wiki/Jordan_matrix
1184
+ """
1185
+ klass = kwargs.pop('cls', kls)
1186
+
1187
+ eigenval = kwargs.get('eigenval', None)
1188
+ if eigenvalue is None and eigenval is None:
1189
+ raise ValueError("Must supply an eigenvalue")
1190
+ elif eigenvalue != eigenval and None not in (eigenval, eigenvalue):
1191
+ raise ValueError(
1192
+ "Inconsistent values are given: 'eigenval'={}, "
1193
+ "'eigenvalue'={}".format(eigenval, eigenvalue))
1194
+ else:
1195
+ if eigenval is not None:
1196
+ eigenvalue = eigenval
1197
+
1198
+ if size is None:
1199
+ raise ValueError("Must supply a matrix size")
1200
+
1201
+ size = as_int(size)
1202
+ return klass._eval_jordan_block(size, eigenvalue, band)
1203
+
1204
+ @classmethod
1205
+ def ones(kls, rows, cols=None, **kwargs):
1206
+ """Returns a matrix of ones.
1207
+
1208
+ Parameters
1209
+ ==========
1210
+
1211
+ rows : rows of the matrix
1212
+ cols : cols of the matrix (if None, cols=rows)
1213
+
1214
+ kwargs
1215
+ ======
1216
+ cls : class of the returned matrix
1217
+ """
1218
+ if cols is None:
1219
+ cols = rows
1220
+ klass = kwargs.get('cls', kls)
1221
+ rows, cols = as_int(rows), as_int(cols)
1222
+
1223
+ return klass._eval_ones(rows, cols)
1224
+
1225
+ @classmethod
1226
+ def zeros(kls, rows, cols=None, **kwargs):
1227
+ """Returns a matrix of zeros.
1228
+
1229
+ Parameters
1230
+ ==========
1231
+
1232
+ rows : rows of the matrix
1233
+ cols : cols of the matrix (if None, cols=rows)
1234
+
1235
+ kwargs
1236
+ ======
1237
+ cls : class of the returned matrix
1238
+ """
1239
+ if cols is None:
1240
+ cols = rows
1241
+ if rows < 0 or cols < 0:
1242
+ raise ValueError("Cannot create a {} x {} matrix. "
1243
+ "Both dimensions must be positive".format(rows, cols))
1244
+ klass = kwargs.get('cls', kls)
1245
+ rows, cols = as_int(rows), as_int(cols)
1246
+
1247
+ return klass._eval_zeros(rows, cols)
1248
+
1249
+ @classmethod
1250
+ def companion(kls, poly):
1251
+ """Returns a companion matrix of a polynomial.
1252
+
1253
+ Examples
1254
+ ========
1255
+
1256
+ >>> from sympy import Matrix, Poly, Symbol, symbols
1257
+ >>> x = Symbol('x')
1258
+ >>> c0, c1, c2, c3, c4 = symbols('c0:5')
1259
+ >>> p = Poly(c0 + c1*x + c2*x**2 + c3*x**3 + c4*x**4 + x**5, x)
1260
+ >>> Matrix.companion(p)
1261
+ Matrix([
1262
+ [0, 0, 0, 0, -c0],
1263
+ [1, 0, 0, 0, -c1],
1264
+ [0, 1, 0, 0, -c2],
1265
+ [0, 0, 1, 0, -c3],
1266
+ [0, 0, 0, 1, -c4]])
1267
+ """
1268
+ poly = kls._sympify(poly)
1269
+ if not isinstance(poly, Poly):
1270
+ raise ValueError("{} must be a Poly instance.".format(poly))
1271
+ if not poly.is_monic:
1272
+ raise ValueError("{} must be a monic polynomial.".format(poly))
1273
+ if not poly.is_univariate:
1274
+ raise ValueError(
1275
+ "{} must be a univariate polynomial.".format(poly))
1276
+
1277
+ size = poly.degree()
1278
+ if not size >= 1:
1279
+ raise ValueError(
1280
+ "{} must have degree not less than 1.".format(poly))
1281
+
1282
+ coeffs = poly.all_coeffs()
1283
+ def entry(i, j):
1284
+ if j == size - 1:
1285
+ return -coeffs[-1 - i]
1286
+ elif i == j + 1:
1287
+ return kls.one
1288
+ return kls.zero
1289
+ return kls._new(size, size, entry)
1290
+
1291
+
1292
+ @classmethod
1293
+ def wilkinson(kls, n, **kwargs):
1294
+ """Returns two square Wilkinson Matrix of size 2*n + 1
1295
+ $W_{2n + 1}^-, W_{2n + 1}^+ =$ Wilkinson(n)
1296
+
1297
+ Examples
1298
+ ========
1299
+
1300
+ >>> from sympy import Matrix
1301
+ >>> wminus, wplus = Matrix.wilkinson(3)
1302
+ >>> wminus
1303
+ Matrix([
1304
+ [-3, 1, 0, 0, 0, 0, 0],
1305
+ [ 1, -2, 1, 0, 0, 0, 0],
1306
+ [ 0, 1, -1, 1, 0, 0, 0],
1307
+ [ 0, 0, 1, 0, 1, 0, 0],
1308
+ [ 0, 0, 0, 1, 1, 1, 0],
1309
+ [ 0, 0, 0, 0, 1, 2, 1],
1310
+ [ 0, 0, 0, 0, 0, 1, 3]])
1311
+ >>> wplus
1312
+ Matrix([
1313
+ [3, 1, 0, 0, 0, 0, 0],
1314
+ [1, 2, 1, 0, 0, 0, 0],
1315
+ [0, 1, 1, 1, 0, 0, 0],
1316
+ [0, 0, 1, 0, 1, 0, 0],
1317
+ [0, 0, 0, 1, 1, 1, 0],
1318
+ [0, 0, 0, 0, 1, 2, 1],
1319
+ [0, 0, 0, 0, 0, 1, 3]])
1320
+
1321
+ References
1322
+ ==========
1323
+
1324
+ .. [1] https://blogs.mathworks.com/cleve/2013/04/15/wilkinsons-matrices-2/
1325
+ .. [2] J. H. Wilkinson, The Algebraic Eigenvalue Problem, Claredon Press, Oxford, 1965, 662 pp.
1326
+
1327
+ """
1328
+ klass = kwargs.get('cls', kls)
1329
+ n = as_int(n)
1330
+ return klass._eval_wilkinson(n)
1331
+
1332
+ class MatrixProperties(MatrixRequired):
1333
+ """Provides basic properties of a matrix."""
1334
+
1335
+ def _eval_atoms(self, *types):
1336
+ result = set()
1337
+ for i in self:
1338
+ result.update(i.atoms(*types))
1339
+ return result
1340
+
1341
+ def _eval_free_symbols(self):
1342
+ return set().union(*(i.free_symbols for i in self if i))
1343
+
1344
+ def _eval_has(self, *patterns):
1345
+ return any(a.has(*patterns) for a in self)
1346
+
1347
+ def _eval_is_anti_symmetric(self, simpfunc):
1348
+ if not all(simpfunc(self[i, j] + self[j, i]).is_zero for i in range(self.rows) for j in range(self.cols)):
1349
+ return False
1350
+ return True
1351
+
1352
+ def _eval_is_diagonal(self):
1353
+ for i in range(self.rows):
1354
+ for j in range(self.cols):
1355
+ if i != j and self[i, j]:
1356
+ return False
1357
+ return True
1358
+
1359
+ # _eval_is_hermitian is called by some general SymPy
1360
+ # routines and has a different *args signature. Make
1361
+ # sure the names don't clash by adding `_matrix_` in name.
1362
+ def _eval_is_matrix_hermitian(self, simpfunc):
1363
+ mat = self._new(self.rows, self.cols, lambda i, j: simpfunc(self[i, j] - self[j, i].conjugate()))
1364
+ return mat.is_zero_matrix
1365
+
1366
+ def _eval_is_Identity(self) -> FuzzyBool:
1367
+ def dirac(i, j):
1368
+ if i == j:
1369
+ return 1
1370
+ return 0
1371
+
1372
+ return all(self[i, j] == dirac(i, j)
1373
+ for i in range(self.rows)
1374
+ for j in range(self.cols))
1375
+
1376
+ def _eval_is_lower_hessenberg(self):
1377
+ return all(self[i, j].is_zero
1378
+ for i in range(self.rows)
1379
+ for j in range(i + 2, self.cols))
1380
+
1381
+ def _eval_is_lower(self):
1382
+ return all(self[i, j].is_zero
1383
+ for i in range(self.rows)
1384
+ for j in range(i + 1, self.cols))
1385
+
1386
+ def _eval_is_symbolic(self):
1387
+ return self.has(Symbol)
1388
+
1389
+ def _eval_is_symmetric(self, simpfunc):
1390
+ mat = self._new(self.rows, self.cols, lambda i, j: simpfunc(self[i, j] - self[j, i]))
1391
+ return mat.is_zero_matrix
1392
+
1393
+ def _eval_is_zero_matrix(self):
1394
+ if any(i.is_zero == False for i in self):
1395
+ return False
1396
+ if any(i.is_zero is None for i in self):
1397
+ return None
1398
+ return True
1399
+
1400
+ def _eval_is_upper_hessenberg(self):
1401
+ return all(self[i, j].is_zero
1402
+ for i in range(2, self.rows)
1403
+ for j in range(min(self.cols, (i - 1))))
1404
+
1405
+ def _eval_values(self):
1406
+ return [i for i in self if not i.is_zero]
1407
+
1408
+ def _has_positive_diagonals(self):
1409
+ diagonal_entries = (self[i, i] for i in range(self.rows))
1410
+ return fuzzy_and(x.is_positive for x in diagonal_entries)
1411
+
1412
+ def _has_nonnegative_diagonals(self):
1413
+ diagonal_entries = (self[i, i] for i in range(self.rows))
1414
+ return fuzzy_and(x.is_nonnegative for x in diagonal_entries)
1415
+
1416
+ def atoms(self, *types):
1417
+ """Returns the atoms that form the current object.
1418
+
1419
+ Examples
1420
+ ========
1421
+
1422
+ >>> from sympy.abc import x, y
1423
+ >>> from sympy import Matrix
1424
+ >>> Matrix([[x]])
1425
+ Matrix([[x]])
1426
+ >>> _.atoms()
1427
+ {x}
1428
+ >>> Matrix([[x, y], [y, x]])
1429
+ Matrix([
1430
+ [x, y],
1431
+ [y, x]])
1432
+ >>> _.atoms()
1433
+ {x, y}
1434
+ """
1435
+
1436
+ types = tuple(t if isinstance(t, type) else type(t) for t in types)
1437
+ if not types:
1438
+ types = (Atom,)
1439
+ return self._eval_atoms(*types)
1440
+
1441
+ @property
1442
+ def free_symbols(self):
1443
+ """Returns the free symbols within the matrix.
1444
+
1445
+ Examples
1446
+ ========
1447
+
1448
+ >>> from sympy.abc import x
1449
+ >>> from sympy import Matrix
1450
+ >>> Matrix([[x], [1]]).free_symbols
1451
+ {x}
1452
+ """
1453
+ return self._eval_free_symbols()
1454
+
1455
+ def has(self, *patterns):
1456
+ """Test whether any subexpression matches any of the patterns.
1457
+
1458
+ Examples
1459
+ ========
1460
+
1461
+ >>> from sympy import Matrix, SparseMatrix, Float
1462
+ >>> from sympy.abc import x, y
1463
+ >>> A = Matrix(((1, x), (0.2, 3)))
1464
+ >>> B = SparseMatrix(((1, x), (0.2, 3)))
1465
+ >>> A.has(x)
1466
+ True
1467
+ >>> A.has(y)
1468
+ False
1469
+ >>> A.has(Float)
1470
+ True
1471
+ >>> B.has(x)
1472
+ True
1473
+ >>> B.has(y)
1474
+ False
1475
+ >>> B.has(Float)
1476
+ True
1477
+ """
1478
+ return self._eval_has(*patterns)
1479
+
1480
+ def is_anti_symmetric(self, simplify=True):
1481
+ """Check if matrix M is an antisymmetric matrix,
1482
+ that is, M is a square matrix with all M[i, j] == -M[j, i].
1483
+
1484
+ When ``simplify=True`` (default), the sum M[i, j] + M[j, i] is
1485
+ simplified before testing to see if it is zero. By default,
1486
+ the SymPy simplify function is used. To use a custom function
1487
+ set simplify to a function that accepts a single argument which
1488
+ returns a simplified expression. To skip simplification, set
1489
+ simplify to False but note that although this will be faster,
1490
+ it may induce false negatives.
1491
+
1492
+ Examples
1493
+ ========
1494
+
1495
+ >>> from sympy import Matrix, symbols
1496
+ >>> m = Matrix(2, 2, [0, 1, -1, 0])
1497
+ >>> m
1498
+ Matrix([
1499
+ [ 0, 1],
1500
+ [-1, 0]])
1501
+ >>> m.is_anti_symmetric()
1502
+ True
1503
+ >>> x, y = symbols('x y')
1504
+ >>> m = Matrix(2, 3, [0, 0, x, -y, 0, 0])
1505
+ >>> m
1506
+ Matrix([
1507
+ [ 0, 0, x],
1508
+ [-y, 0, 0]])
1509
+ >>> m.is_anti_symmetric()
1510
+ False
1511
+
1512
+ >>> from sympy.abc import x, y
1513
+ >>> m = Matrix(3, 3, [0, x**2 + 2*x + 1, y,
1514
+ ... -(x + 1)**2, 0, x*y,
1515
+ ... -y, -x*y, 0])
1516
+
1517
+ Simplification of matrix elements is done by default so even
1518
+ though two elements which should be equal and opposite would not
1519
+ pass an equality test, the matrix is still reported as
1520
+ anti-symmetric:
1521
+
1522
+ >>> m[0, 1] == -m[1, 0]
1523
+ False
1524
+ >>> m.is_anti_symmetric()
1525
+ True
1526
+
1527
+ If ``simplify=False`` is used for the case when a Matrix is already
1528
+ simplified, this will speed things up. Here, we see that without
1529
+ simplification the matrix does not appear anti-symmetric:
1530
+
1531
+ >>> print(m.is_anti_symmetric(simplify=False))
1532
+ None
1533
+
1534
+ But if the matrix were already expanded, then it would appear
1535
+ anti-symmetric and simplification in the is_anti_symmetric routine
1536
+ is not needed:
1537
+
1538
+ >>> m = m.expand()
1539
+ >>> m.is_anti_symmetric(simplify=False)
1540
+ True
1541
+ """
1542
+ # accept custom simplification
1543
+ simpfunc = simplify
1544
+ if not isfunction(simplify):
1545
+ simpfunc = _simplify if simplify else lambda x: x
1546
+
1547
+ if not self.is_square:
1548
+ return False
1549
+ return self._eval_is_anti_symmetric(simpfunc)
1550
+
1551
+ def is_diagonal(self):
1552
+ """Check if matrix is diagonal,
1553
+ that is matrix in which the entries outside the main diagonal are all zero.
1554
+
1555
+ Examples
1556
+ ========
1557
+
1558
+ >>> from sympy import Matrix, diag
1559
+ >>> m = Matrix(2, 2, [1, 0, 0, 2])
1560
+ >>> m
1561
+ Matrix([
1562
+ [1, 0],
1563
+ [0, 2]])
1564
+ >>> m.is_diagonal()
1565
+ True
1566
+
1567
+ >>> m = Matrix(2, 2, [1, 1, 0, 2])
1568
+ >>> m
1569
+ Matrix([
1570
+ [1, 1],
1571
+ [0, 2]])
1572
+ >>> m.is_diagonal()
1573
+ False
1574
+
1575
+ >>> m = diag(1, 2, 3)
1576
+ >>> m
1577
+ Matrix([
1578
+ [1, 0, 0],
1579
+ [0, 2, 0],
1580
+ [0, 0, 3]])
1581
+ >>> m.is_diagonal()
1582
+ True
1583
+
1584
+ See Also
1585
+ ========
1586
+
1587
+ is_lower
1588
+ is_upper
1589
+ sympy.matrices.matrixbase.MatrixCommon.is_diagonalizable
1590
+ diagonalize
1591
+ """
1592
+ return self._eval_is_diagonal()
1593
+
1594
+ @property
1595
+ def is_weakly_diagonally_dominant(self):
1596
+ r"""Tests if the matrix is row weakly diagonally dominant.
1597
+
1598
+ Explanation
1599
+ ===========
1600
+
1601
+ A $n, n$ matrix $A$ is row weakly diagonally dominant if
1602
+
1603
+ .. math::
1604
+ \left|A_{i, i}\right| \ge \sum_{j = 0, j \neq i}^{n-1}
1605
+ \left|A_{i, j}\right| \quad {\text{for all }}
1606
+ i \in \{ 0, ..., n-1 \}
1607
+
1608
+ Examples
1609
+ ========
1610
+
1611
+ >>> from sympy import Matrix
1612
+ >>> A = Matrix([[3, -2, 1], [1, -3, 2], [-1, 2, 4]])
1613
+ >>> A.is_weakly_diagonally_dominant
1614
+ True
1615
+
1616
+ >>> A = Matrix([[-2, 2, 1], [1, 3, 2], [1, -2, 0]])
1617
+ >>> A.is_weakly_diagonally_dominant
1618
+ False
1619
+
1620
+ >>> A = Matrix([[-4, 2, 1], [1, 6, 2], [1, -2, 5]])
1621
+ >>> A.is_weakly_diagonally_dominant
1622
+ True
1623
+
1624
+ Notes
1625
+ =====
1626
+
1627
+ If you want to test whether a matrix is column diagonally
1628
+ dominant, you can apply the test after transposing the matrix.
1629
+ """
1630
+ if not self.is_square:
1631
+ return False
1632
+
1633
+ rows, cols = self.shape
1634
+
1635
+ def test_row(i):
1636
+ summation = self.zero
1637
+ for j in range(cols):
1638
+ if i != j:
1639
+ summation += Abs(self[i, j])
1640
+ return (Abs(self[i, i]) - summation).is_nonnegative
1641
+
1642
+ return fuzzy_and(test_row(i) for i in range(rows))
1643
+
1644
+ @property
1645
+ def is_strongly_diagonally_dominant(self):
1646
+ r"""Tests if the matrix is row strongly diagonally dominant.
1647
+
1648
+ Explanation
1649
+ ===========
1650
+
1651
+ A $n, n$ matrix $A$ is row strongly diagonally dominant if
1652
+
1653
+ .. math::
1654
+ \left|A_{i, i}\right| > \sum_{j = 0, j \neq i}^{n-1}
1655
+ \left|A_{i, j}\right| \quad {\text{for all }}
1656
+ i \in \{ 0, ..., n-1 \}
1657
+
1658
+ Examples
1659
+ ========
1660
+
1661
+ >>> from sympy import Matrix
1662
+ >>> A = Matrix([[3, -2, 1], [1, -3, 2], [-1, 2, 4]])
1663
+ >>> A.is_strongly_diagonally_dominant
1664
+ False
1665
+
1666
+ >>> A = Matrix([[-2, 2, 1], [1, 3, 2], [1, -2, 0]])
1667
+ >>> A.is_strongly_diagonally_dominant
1668
+ False
1669
+
1670
+ >>> A = Matrix([[-4, 2, 1], [1, 6, 2], [1, -2, 5]])
1671
+ >>> A.is_strongly_diagonally_dominant
1672
+ True
1673
+
1674
+ Notes
1675
+ =====
1676
+
1677
+ If you want to test whether a matrix is column diagonally
1678
+ dominant, you can apply the test after transposing the matrix.
1679
+ """
1680
+ if not self.is_square:
1681
+ return False
1682
+
1683
+ rows, cols = self.shape
1684
+
1685
+ def test_row(i):
1686
+ summation = self.zero
1687
+ for j in range(cols):
1688
+ if i != j:
1689
+ summation += Abs(self[i, j])
1690
+ return (Abs(self[i, i]) - summation).is_positive
1691
+
1692
+ return fuzzy_and(test_row(i) for i in range(rows))
1693
+
1694
+ @property
1695
+ def is_hermitian(self):
1696
+ """Checks if the matrix is Hermitian.
1697
+
1698
+ In a Hermitian matrix element i,j is the complex conjugate of
1699
+ element j,i.
1700
+
1701
+ Examples
1702
+ ========
1703
+
1704
+ >>> from sympy import Matrix
1705
+ >>> from sympy import I
1706
+ >>> from sympy.abc import x
1707
+ >>> a = Matrix([[1, I], [-I, 1]])
1708
+ >>> a
1709
+ Matrix([
1710
+ [ 1, I],
1711
+ [-I, 1]])
1712
+ >>> a.is_hermitian
1713
+ True
1714
+ >>> a[0, 0] = 2*I
1715
+ >>> a.is_hermitian
1716
+ False
1717
+ >>> a[0, 0] = x
1718
+ >>> a.is_hermitian
1719
+ >>> a[0, 1] = a[1, 0]*I
1720
+ >>> a.is_hermitian
1721
+ False
1722
+ """
1723
+ if not self.is_square:
1724
+ return False
1725
+
1726
+ return self._eval_is_matrix_hermitian(_simplify)
1727
+
1728
+ @property
1729
+ def is_Identity(self) -> FuzzyBool:
1730
+ if not self.is_square:
1731
+ return False
1732
+ return self._eval_is_Identity()
1733
+
1734
+ @property
1735
+ def is_lower_hessenberg(self):
1736
+ r"""Checks if the matrix is in the lower-Hessenberg form.
1737
+
1738
+ The lower hessenberg matrix has zero entries
1739
+ above the first superdiagonal.
1740
+
1741
+ Examples
1742
+ ========
1743
+
1744
+ >>> from sympy import Matrix
1745
+ >>> a = Matrix([[1, 2, 0, 0], [5, 2, 3, 0], [3, 4, 3, 7], [5, 6, 1, 1]])
1746
+ >>> a
1747
+ Matrix([
1748
+ [1, 2, 0, 0],
1749
+ [5, 2, 3, 0],
1750
+ [3, 4, 3, 7],
1751
+ [5, 6, 1, 1]])
1752
+ >>> a.is_lower_hessenberg
1753
+ True
1754
+
1755
+ See Also
1756
+ ========
1757
+
1758
+ is_upper_hessenberg
1759
+ is_lower
1760
+ """
1761
+ return self._eval_is_lower_hessenberg()
1762
+
1763
+ @property
1764
+ def is_lower(self):
1765
+ """Check if matrix is a lower triangular matrix. True can be returned
1766
+ even if the matrix is not square.
1767
+
1768
+ Examples
1769
+ ========
1770
+
1771
+ >>> from sympy import Matrix
1772
+ >>> m = Matrix(2, 2, [1, 0, 0, 1])
1773
+ >>> m
1774
+ Matrix([
1775
+ [1, 0],
1776
+ [0, 1]])
1777
+ >>> m.is_lower
1778
+ True
1779
+
1780
+ >>> m = Matrix(4, 3, [0, 0, 0, 2, 0, 0, 1, 4, 0, 6, 6, 5])
1781
+ >>> m
1782
+ Matrix([
1783
+ [0, 0, 0],
1784
+ [2, 0, 0],
1785
+ [1, 4, 0],
1786
+ [6, 6, 5]])
1787
+ >>> m.is_lower
1788
+ True
1789
+
1790
+ >>> from sympy.abc import x, y
1791
+ >>> m = Matrix(2, 2, [x**2 + y, y**2 + x, 0, x + y])
1792
+ >>> m
1793
+ Matrix([
1794
+ [x**2 + y, x + y**2],
1795
+ [ 0, x + y]])
1796
+ >>> m.is_lower
1797
+ False
1798
+
1799
+ See Also
1800
+ ========
1801
+
1802
+ is_upper
1803
+ is_diagonal
1804
+ is_lower_hessenberg
1805
+ """
1806
+ return self._eval_is_lower()
1807
+
1808
+ @property
1809
+ def is_square(self):
1810
+ """Checks if a matrix is square.
1811
+
1812
+ A matrix is square if the number of rows equals the number of columns.
1813
+ The empty matrix is square by definition, since the number of rows and
1814
+ the number of columns are both zero.
1815
+
1816
+ Examples
1817
+ ========
1818
+
1819
+ >>> from sympy import Matrix
1820
+ >>> a = Matrix([[1, 2, 3], [4, 5, 6]])
1821
+ >>> b = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
1822
+ >>> c = Matrix([])
1823
+ >>> a.is_square
1824
+ False
1825
+ >>> b.is_square
1826
+ True
1827
+ >>> c.is_square
1828
+ True
1829
+ """
1830
+ return self.rows == self.cols
1831
+
1832
+ def is_symbolic(self):
1833
+ """Checks if any elements contain Symbols.
1834
+
1835
+ Examples
1836
+ ========
1837
+
1838
+ >>> from sympy import Matrix
1839
+ >>> from sympy.abc import x, y
1840
+ >>> M = Matrix([[x, y], [1, 0]])
1841
+ >>> M.is_symbolic()
1842
+ True
1843
+
1844
+ """
1845
+ return self._eval_is_symbolic()
1846
+
1847
+ def is_symmetric(self, simplify=True):
1848
+ """Check if matrix is symmetric matrix,
1849
+ that is square matrix and is equal to its transpose.
1850
+
1851
+ By default, simplifications occur before testing symmetry.
1852
+ They can be skipped using 'simplify=False'; while speeding things a bit,
1853
+ this may however induce false negatives.
1854
+
1855
+ Examples
1856
+ ========
1857
+
1858
+ >>> from sympy import Matrix
1859
+ >>> m = Matrix(2, 2, [0, 1, 1, 2])
1860
+ >>> m
1861
+ Matrix([
1862
+ [0, 1],
1863
+ [1, 2]])
1864
+ >>> m.is_symmetric()
1865
+ True
1866
+
1867
+ >>> m = Matrix(2, 2, [0, 1, 2, 0])
1868
+ >>> m
1869
+ Matrix([
1870
+ [0, 1],
1871
+ [2, 0]])
1872
+ >>> m.is_symmetric()
1873
+ False
1874
+
1875
+ >>> m = Matrix(2, 3, [0, 0, 0, 0, 0, 0])
1876
+ >>> m
1877
+ Matrix([
1878
+ [0, 0, 0],
1879
+ [0, 0, 0]])
1880
+ >>> m.is_symmetric()
1881
+ False
1882
+
1883
+ >>> from sympy.abc import x, y
1884
+ >>> m = Matrix(3, 3, [1, x**2 + 2*x + 1, y, (x + 1)**2, 2, 0, y, 0, 3])
1885
+ >>> m
1886
+ Matrix([
1887
+ [ 1, x**2 + 2*x + 1, y],
1888
+ [(x + 1)**2, 2, 0],
1889
+ [ y, 0, 3]])
1890
+ >>> m.is_symmetric()
1891
+ True
1892
+
1893
+ If the matrix is already simplified, you may speed-up is_symmetric()
1894
+ test by using 'simplify=False'.
1895
+
1896
+ >>> bool(m.is_symmetric(simplify=False))
1897
+ False
1898
+ >>> m1 = m.expand()
1899
+ >>> m1.is_symmetric(simplify=False)
1900
+ True
1901
+ """
1902
+ simpfunc = simplify
1903
+ if not isfunction(simplify):
1904
+ simpfunc = _simplify if simplify else lambda x: x
1905
+
1906
+ if not self.is_square:
1907
+ return False
1908
+
1909
+ return self._eval_is_symmetric(simpfunc)
1910
+
1911
+ @property
1912
+ def is_upper_hessenberg(self):
1913
+ """Checks if the matrix is the upper-Hessenberg form.
1914
+
1915
+ The upper hessenberg matrix has zero entries
1916
+ below the first subdiagonal.
1917
+
1918
+ Examples
1919
+ ========
1920
+
1921
+ >>> from sympy import Matrix
1922
+ >>> a = Matrix([[1, 4, 2, 3], [3, 4, 1, 7], [0, 2, 3, 4], [0, 0, 1, 3]])
1923
+ >>> a
1924
+ Matrix([
1925
+ [1, 4, 2, 3],
1926
+ [3, 4, 1, 7],
1927
+ [0, 2, 3, 4],
1928
+ [0, 0, 1, 3]])
1929
+ >>> a.is_upper_hessenberg
1930
+ True
1931
+
1932
+ See Also
1933
+ ========
1934
+
1935
+ is_lower_hessenberg
1936
+ is_upper
1937
+ """
1938
+ return self._eval_is_upper_hessenberg()
1939
+
1940
+ @property
1941
+ def is_upper(self):
1942
+ """Check if matrix is an upper triangular matrix. True can be returned
1943
+ even if the matrix is not square.
1944
+
1945
+ Examples
1946
+ ========
1947
+
1948
+ >>> from sympy import Matrix
1949
+ >>> m = Matrix(2, 2, [1, 0, 0, 1])
1950
+ >>> m
1951
+ Matrix([
1952
+ [1, 0],
1953
+ [0, 1]])
1954
+ >>> m.is_upper
1955
+ True
1956
+
1957
+ >>> m = Matrix(4, 3, [5, 1, 9, 0, 4, 6, 0, 0, 5, 0, 0, 0])
1958
+ >>> m
1959
+ Matrix([
1960
+ [5, 1, 9],
1961
+ [0, 4, 6],
1962
+ [0, 0, 5],
1963
+ [0, 0, 0]])
1964
+ >>> m.is_upper
1965
+ True
1966
+
1967
+ >>> m = Matrix(2, 3, [4, 2, 5, 6, 1, 1])
1968
+ >>> m
1969
+ Matrix([
1970
+ [4, 2, 5],
1971
+ [6, 1, 1]])
1972
+ >>> m.is_upper
1973
+ False
1974
+
1975
+ See Also
1976
+ ========
1977
+
1978
+ is_lower
1979
+ is_diagonal
1980
+ is_upper_hessenberg
1981
+ """
1982
+ return all(self[i, j].is_zero
1983
+ for i in range(1, self.rows)
1984
+ for j in range(min(i, self.cols)))
1985
+
1986
+ @property
1987
+ def is_zero_matrix(self):
1988
+ """Checks if a matrix is a zero matrix.
1989
+
1990
+ A matrix is zero if every element is zero. A matrix need not be square
1991
+ to be considered zero. The empty matrix is zero by the principle of
1992
+ vacuous truth. For a matrix that may or may not be zero (e.g.
1993
+ contains a symbol), this will be None
1994
+
1995
+ Examples
1996
+ ========
1997
+
1998
+ >>> from sympy import Matrix, zeros
1999
+ >>> from sympy.abc import x
2000
+ >>> a = Matrix([[0, 0], [0, 0]])
2001
+ >>> b = zeros(3, 4)
2002
+ >>> c = Matrix([[0, 1], [0, 0]])
2003
+ >>> d = Matrix([])
2004
+ >>> e = Matrix([[x, 0], [0, 0]])
2005
+ >>> a.is_zero_matrix
2006
+ True
2007
+ >>> b.is_zero_matrix
2008
+ True
2009
+ >>> c.is_zero_matrix
2010
+ False
2011
+ >>> d.is_zero_matrix
2012
+ True
2013
+ >>> e.is_zero_matrix
2014
+ """
2015
+ return self._eval_is_zero_matrix()
2016
+
2017
+ def values(self):
2018
+ """Return non-zero values of self."""
2019
+ return self._eval_values()
2020
+
2021
+
2022
+ class MatrixOperations(MatrixRequired):
2023
+ """Provides basic matrix shape and elementwise
2024
+ operations. Should not be instantiated directly."""
2025
+
2026
+ def _eval_adjoint(self):
2027
+ return self.transpose().conjugate()
2028
+
2029
+ def _eval_applyfunc(self, f):
2030
+ out = self._new(self.rows, self.cols, [f(x) for x in self])
2031
+ return out
2032
+
2033
+ def _eval_as_real_imag(self): # type: ignore
2034
+ return (self.applyfunc(re), self.applyfunc(im))
2035
+
2036
+ def _eval_conjugate(self):
2037
+ return self.applyfunc(lambda x: x.conjugate())
2038
+
2039
+ def _eval_permute_cols(self, perm):
2040
+ # apply the permutation to a list
2041
+ mapping = list(perm)
2042
+
2043
+ def entry(i, j):
2044
+ return self[i, mapping[j]]
2045
+
2046
+ return self._new(self.rows, self.cols, entry)
2047
+
2048
+ def _eval_permute_rows(self, perm):
2049
+ # apply the permutation to a list
2050
+ mapping = list(perm)
2051
+
2052
+ def entry(i, j):
2053
+ return self[mapping[i], j]
2054
+
2055
+ return self._new(self.rows, self.cols, entry)
2056
+
2057
+ def _eval_trace(self):
2058
+ return sum(self[i, i] for i in range(self.rows))
2059
+
2060
+ def _eval_transpose(self):
2061
+ return self._new(self.cols, self.rows, lambda i, j: self[j, i])
2062
+
2063
+ def adjoint(self):
2064
+ """Conjugate transpose or Hermitian conjugation."""
2065
+ return self._eval_adjoint()
2066
+
2067
+ def applyfunc(self, f):
2068
+ """Apply a function to each element of the matrix.
2069
+
2070
+ Examples
2071
+ ========
2072
+
2073
+ >>> from sympy import Matrix
2074
+ >>> m = Matrix(2, 2, lambda i, j: i*2+j)
2075
+ >>> m
2076
+ Matrix([
2077
+ [0, 1],
2078
+ [2, 3]])
2079
+ >>> m.applyfunc(lambda i: 2*i)
2080
+ Matrix([
2081
+ [0, 2],
2082
+ [4, 6]])
2083
+
2084
+ """
2085
+ if not callable(f):
2086
+ raise TypeError("`f` must be callable.")
2087
+
2088
+ return self._eval_applyfunc(f)
2089
+
2090
+ def as_real_imag(self, deep=True, **hints):
2091
+ """Returns a tuple containing the (real, imaginary) part of matrix."""
2092
+ # XXX: Ignoring deep and hints...
2093
+ return self._eval_as_real_imag()
2094
+
2095
+ def conjugate(self):
2096
+ """Return the by-element conjugation.
2097
+
2098
+ Examples
2099
+ ========
2100
+
2101
+ >>> from sympy import SparseMatrix, I
2102
+ >>> a = SparseMatrix(((1, 2 + I), (3, 4), (I, -I)))
2103
+ >>> a
2104
+ Matrix([
2105
+ [1, 2 + I],
2106
+ [3, 4],
2107
+ [I, -I]])
2108
+ >>> a.C
2109
+ Matrix([
2110
+ [ 1, 2 - I],
2111
+ [ 3, 4],
2112
+ [-I, I]])
2113
+
2114
+ See Also
2115
+ ========
2116
+
2117
+ transpose: Matrix transposition
2118
+ H: Hermite conjugation
2119
+ sympy.matrices.matrixbase.MatrixBase.D: Dirac conjugation
2120
+ """
2121
+ return self._eval_conjugate()
2122
+
2123
+ def doit(self, **hints):
2124
+ return self.applyfunc(lambda x: x.doit(**hints))
2125
+
2126
+ def evalf(self, n=15, subs=None, maxn=100, chop=False, strict=False, quad=None, verbose=False):
2127
+ """Apply evalf() to each element of self."""
2128
+ options = {'subs':subs, 'maxn':maxn, 'chop':chop, 'strict':strict,
2129
+ 'quad':quad, 'verbose':verbose}
2130
+ return self.applyfunc(lambda i: i.evalf(n, **options))
2131
+
2132
+ def expand(self, deep=True, modulus=None, power_base=True, power_exp=True,
2133
+ mul=True, log=True, multinomial=True, basic=True, **hints):
2134
+ """Apply core.function.expand to each entry of the matrix.
2135
+
2136
+ Examples
2137
+ ========
2138
+
2139
+ >>> from sympy.abc import x
2140
+ >>> from sympy import Matrix
2141
+ >>> Matrix(1, 1, [x*(x+1)])
2142
+ Matrix([[x*(x + 1)]])
2143
+ >>> _.expand()
2144
+ Matrix([[x**2 + x]])
2145
+
2146
+ """
2147
+ return self.applyfunc(lambda x: x.expand(
2148
+ deep, modulus, power_base, power_exp, mul, log, multinomial, basic,
2149
+ **hints))
2150
+
2151
+ @property
2152
+ def H(self):
2153
+ """Return Hermite conjugate.
2154
+
2155
+ Examples
2156
+ ========
2157
+
2158
+ >>> from sympy import Matrix, I
2159
+ >>> m = Matrix((0, 1 + I, 2, 3))
2160
+ >>> m
2161
+ Matrix([
2162
+ [ 0],
2163
+ [1 + I],
2164
+ [ 2],
2165
+ [ 3]])
2166
+ >>> m.H
2167
+ Matrix([[0, 1 - I, 2, 3]])
2168
+
2169
+ See Also
2170
+ ========
2171
+
2172
+ conjugate: By-element conjugation
2173
+ sympy.matrices.matrixbase.MatrixBase.D: Dirac conjugation
2174
+ """
2175
+ return self.T.C
2176
+
2177
+ def permute(self, perm, orientation='rows', direction='forward'):
2178
+ r"""Permute the rows or columns of a matrix by the given list of
2179
+ swaps.
2180
+
2181
+ Parameters
2182
+ ==========
2183
+
2184
+ perm : Permutation, list, or list of lists
2185
+ A representation for the permutation.
2186
+
2187
+ If it is ``Permutation``, it is used directly with some
2188
+ resizing with respect to the matrix size.
2189
+
2190
+ If it is specified as list of lists,
2191
+ (e.g., ``[[0, 1], [0, 2]]``), then the permutation is formed
2192
+ from applying the product of cycles. The direction how the
2193
+ cyclic product is applied is described in below.
2194
+
2195
+ If it is specified as a list, the list should represent
2196
+ an array form of a permutation. (e.g., ``[1, 2, 0]``) which
2197
+ would would form the swapping function
2198
+ `0 \mapsto 1, 1 \mapsto 2, 2\mapsto 0`.
2199
+
2200
+ orientation : 'rows', 'cols'
2201
+ A flag to control whether to permute the rows or the columns
2202
+
2203
+ direction : 'forward', 'backward'
2204
+ A flag to control whether to apply the permutations from
2205
+ the start of the list first, or from the back of the list
2206
+ first.
2207
+
2208
+ For example, if the permutation specification is
2209
+ ``[[0, 1], [0, 2]]``,
2210
+
2211
+ If the flag is set to ``'forward'``, the cycle would be
2212
+ formed as `0 \mapsto 2, 2 \mapsto 1, 1 \mapsto 0`.
2213
+
2214
+ If the flag is set to ``'backward'``, the cycle would be
2215
+ formed as `0 \mapsto 1, 1 \mapsto 2, 2 \mapsto 0`.
2216
+
2217
+ If the argument ``perm`` is not in a form of list of lists,
2218
+ this flag takes no effect.
2219
+
2220
+ Examples
2221
+ ========
2222
+
2223
+ >>> from sympy import eye
2224
+ >>> M = eye(3)
2225
+ >>> M.permute([[0, 1], [0, 2]], orientation='rows', direction='forward')
2226
+ Matrix([
2227
+ [0, 0, 1],
2228
+ [1, 0, 0],
2229
+ [0, 1, 0]])
2230
+
2231
+ >>> from sympy import eye
2232
+ >>> M = eye(3)
2233
+ >>> M.permute([[0, 1], [0, 2]], orientation='rows', direction='backward')
2234
+ Matrix([
2235
+ [0, 1, 0],
2236
+ [0, 0, 1],
2237
+ [1, 0, 0]])
2238
+
2239
+ Notes
2240
+ =====
2241
+
2242
+ If a bijective function
2243
+ `\sigma : \mathbb{N}_0 \rightarrow \mathbb{N}_0` denotes the
2244
+ permutation.
2245
+
2246
+ If the matrix `A` is the matrix to permute, represented as
2247
+ a horizontal or a vertical stack of vectors:
2248
+
2249
+ .. math::
2250
+ A =
2251
+ \begin{bmatrix}
2252
+ a_0 \\ a_1 \\ \vdots \\ a_{n-1}
2253
+ \end{bmatrix} =
2254
+ \begin{bmatrix}
2255
+ \alpha_0 & \alpha_1 & \cdots & \alpha_{n-1}
2256
+ \end{bmatrix}
2257
+
2258
+ If the matrix `B` is the result, the permutation of matrix rows
2259
+ is defined as:
2260
+
2261
+ .. math::
2262
+ B := \begin{bmatrix}
2263
+ a_{\sigma(0)} \\ a_{\sigma(1)} \\ \vdots \\ a_{\sigma(n-1)}
2264
+ \end{bmatrix}
2265
+
2266
+ And the permutation of matrix columns is defined as:
2267
+
2268
+ .. math::
2269
+ B := \begin{bmatrix}
2270
+ \alpha_{\sigma(0)} & \alpha_{\sigma(1)} &
2271
+ \cdots & \alpha_{\sigma(n-1)}
2272
+ \end{bmatrix}
2273
+ """
2274
+ from sympy.combinatorics import Permutation
2275
+
2276
+ # allow british variants and `columns`
2277
+ if direction == 'forwards':
2278
+ direction = 'forward'
2279
+ if direction == 'backwards':
2280
+ direction = 'backward'
2281
+ if orientation == 'columns':
2282
+ orientation = 'cols'
2283
+
2284
+ if direction not in ('forward', 'backward'):
2285
+ raise TypeError("direction='{}' is an invalid kwarg. "
2286
+ "Try 'forward' or 'backward'".format(direction))
2287
+ if orientation not in ('rows', 'cols'):
2288
+ raise TypeError("orientation='{}' is an invalid kwarg. "
2289
+ "Try 'rows' or 'cols'".format(orientation))
2290
+
2291
+ if not isinstance(perm, (Permutation, Iterable)):
2292
+ raise ValueError(
2293
+ "{} must be a list, a list of lists, "
2294
+ "or a SymPy permutation object.".format(perm))
2295
+
2296
+ # ensure all swaps are in range
2297
+ max_index = self.rows if orientation == 'rows' else self.cols
2298
+ if not all(0 <= t <= max_index for t in flatten(list(perm))):
2299
+ raise IndexError("`swap` indices out of range.")
2300
+
2301
+ if perm and not isinstance(perm, Permutation) and \
2302
+ isinstance(perm[0], Iterable):
2303
+ if direction == 'forward':
2304
+ perm = list(reversed(perm))
2305
+ perm = Permutation(perm, size=max_index+1)
2306
+ else:
2307
+ perm = Permutation(perm, size=max_index+1)
2308
+
2309
+ if orientation == 'rows':
2310
+ return self._eval_permute_rows(perm)
2311
+ if orientation == 'cols':
2312
+ return self._eval_permute_cols(perm)
2313
+
2314
+ def permute_cols(self, swaps, direction='forward'):
2315
+ """Alias for
2316
+ ``self.permute(swaps, orientation='cols', direction=direction)``
2317
+
2318
+ See Also
2319
+ ========
2320
+
2321
+ permute
2322
+ """
2323
+ return self.permute(swaps, orientation='cols', direction=direction)
2324
+
2325
+ def permute_rows(self, swaps, direction='forward'):
2326
+ """Alias for
2327
+ ``self.permute(swaps, orientation='rows', direction=direction)``
2328
+
2329
+ See Also
2330
+ ========
2331
+
2332
+ permute
2333
+ """
2334
+ return self.permute(swaps, orientation='rows', direction=direction)
2335
+
2336
+ def refine(self, assumptions=True):
2337
+ """Apply refine to each element of the matrix.
2338
+
2339
+ Examples
2340
+ ========
2341
+
2342
+ >>> from sympy import Symbol, Matrix, Abs, sqrt, Q
2343
+ >>> x = Symbol('x')
2344
+ >>> Matrix([[Abs(x)**2, sqrt(x**2)],[sqrt(x**2), Abs(x)**2]])
2345
+ Matrix([
2346
+ [ Abs(x)**2, sqrt(x**2)],
2347
+ [sqrt(x**2), Abs(x)**2]])
2348
+ >>> _.refine(Q.real(x))
2349
+ Matrix([
2350
+ [ x**2, Abs(x)],
2351
+ [Abs(x), x**2]])
2352
+
2353
+ """
2354
+ return self.applyfunc(lambda x: refine(x, assumptions))
2355
+
2356
+ def replace(self, F, G, map=False, simultaneous=True, exact=None):
2357
+ """Replaces Function F in Matrix entries with Function G.
2358
+
2359
+ Examples
2360
+ ========
2361
+
2362
+ >>> from sympy import symbols, Function, Matrix
2363
+ >>> F, G = symbols('F, G', cls=Function)
2364
+ >>> M = Matrix(2, 2, lambda i, j: F(i+j)) ; M
2365
+ Matrix([
2366
+ [F(0), F(1)],
2367
+ [F(1), F(2)]])
2368
+ >>> N = M.replace(F,G)
2369
+ >>> N
2370
+ Matrix([
2371
+ [G(0), G(1)],
2372
+ [G(1), G(2)]])
2373
+ """
2374
+ return self.applyfunc(
2375
+ lambda x: x.replace(F, G, map=map, simultaneous=simultaneous, exact=exact))
2376
+
2377
+ def rot90(self, k=1):
2378
+ """Rotates Matrix by 90 degrees
2379
+
2380
+ Parameters
2381
+ ==========
2382
+
2383
+ k : int
2384
+ Specifies how many times the matrix is rotated by 90 degrees
2385
+ (clockwise when positive, counter-clockwise when negative).
2386
+
2387
+ Examples
2388
+ ========
2389
+
2390
+ >>> from sympy import Matrix, symbols
2391
+ >>> A = Matrix(2, 2, symbols('a:d'))
2392
+ >>> A
2393
+ Matrix([
2394
+ [a, b],
2395
+ [c, d]])
2396
+
2397
+ Rotating the matrix clockwise one time:
2398
+
2399
+ >>> A.rot90(1)
2400
+ Matrix([
2401
+ [c, a],
2402
+ [d, b]])
2403
+
2404
+ Rotating the matrix anticlockwise two times:
2405
+
2406
+ >>> A.rot90(-2)
2407
+ Matrix([
2408
+ [d, c],
2409
+ [b, a]])
2410
+ """
2411
+
2412
+ mod = k%4
2413
+ if mod == 0:
2414
+ return self
2415
+ if mod == 1:
2416
+ return self[::-1, ::].T
2417
+ if mod == 2:
2418
+ return self[::-1, ::-1]
2419
+ if mod == 3:
2420
+ return self[::, ::-1].T
2421
+
2422
+ def simplify(self, **kwargs):
2423
+ """Apply simplify to each element of the matrix.
2424
+
2425
+ Examples
2426
+ ========
2427
+
2428
+ >>> from sympy.abc import x, y
2429
+ >>> from sympy import SparseMatrix, sin, cos
2430
+ >>> SparseMatrix(1, 1, [x*sin(y)**2 + x*cos(y)**2])
2431
+ Matrix([[x*sin(y)**2 + x*cos(y)**2]])
2432
+ >>> _.simplify()
2433
+ Matrix([[x]])
2434
+ """
2435
+ return self.applyfunc(lambda x: x.simplify(**kwargs))
2436
+
2437
+ def subs(self, *args, **kwargs): # should mirror core.basic.subs
2438
+ """Return a new matrix with subs applied to each entry.
2439
+
2440
+ Examples
2441
+ ========
2442
+
2443
+ >>> from sympy.abc import x, y
2444
+ >>> from sympy import SparseMatrix, Matrix
2445
+ >>> SparseMatrix(1, 1, [x])
2446
+ Matrix([[x]])
2447
+ >>> _.subs(x, y)
2448
+ Matrix([[y]])
2449
+ >>> Matrix(_).subs(y, x)
2450
+ Matrix([[x]])
2451
+ """
2452
+
2453
+ if len(args) == 1 and not isinstance(args[0], (dict, set)) and iter(args[0]) and not is_sequence(args[0]):
2454
+ args = (list(args[0]),)
2455
+
2456
+ return self.applyfunc(lambda x: x.subs(*args, **kwargs))
2457
+
2458
+ def trace(self):
2459
+ """
2460
+ Returns the trace of a square matrix i.e. the sum of the
2461
+ diagonal elements.
2462
+
2463
+ Examples
2464
+ ========
2465
+
2466
+ >>> from sympy import Matrix
2467
+ >>> A = Matrix(2, 2, [1, 2, 3, 4])
2468
+ >>> A.trace()
2469
+ 5
2470
+
2471
+ """
2472
+ if self.rows != self.cols:
2473
+ raise NonSquareMatrixError()
2474
+ return self._eval_trace()
2475
+
2476
+ def transpose(self):
2477
+ """
2478
+ Returns the transpose of the matrix.
2479
+
2480
+ Examples
2481
+ ========
2482
+
2483
+ >>> from sympy import Matrix
2484
+ >>> A = Matrix(2, 2, [1, 2, 3, 4])
2485
+ >>> A.transpose()
2486
+ Matrix([
2487
+ [1, 3],
2488
+ [2, 4]])
2489
+
2490
+ >>> from sympy import Matrix, I
2491
+ >>> m=Matrix(((1, 2+I), (3, 4)))
2492
+ >>> m
2493
+ Matrix([
2494
+ [1, 2 + I],
2495
+ [3, 4]])
2496
+ >>> m.transpose()
2497
+ Matrix([
2498
+ [ 1, 3],
2499
+ [2 + I, 4]])
2500
+ >>> m.T == m.transpose()
2501
+ True
2502
+
2503
+ See Also
2504
+ ========
2505
+
2506
+ conjugate: By-element conjugation
2507
+
2508
+ """
2509
+ return self._eval_transpose()
2510
+
2511
+ @property
2512
+ def T(self):
2513
+ '''Matrix transposition'''
2514
+ return self.transpose()
2515
+
2516
+ @property
2517
+ def C(self):
2518
+ '''By-element conjugation'''
2519
+ return self.conjugate()
2520
+
2521
+ def n(self, *args, **kwargs):
2522
+ """Apply evalf() to each element of self."""
2523
+ return self.evalf(*args, **kwargs)
2524
+
2525
+ def xreplace(self, rule): # should mirror core.basic.xreplace
2526
+ """Return a new matrix with xreplace applied to each entry.
2527
+
2528
+ Examples
2529
+ ========
2530
+
2531
+ >>> from sympy.abc import x, y
2532
+ >>> from sympy import SparseMatrix, Matrix
2533
+ >>> SparseMatrix(1, 1, [x])
2534
+ Matrix([[x]])
2535
+ >>> _.xreplace({x: y})
2536
+ Matrix([[y]])
2537
+ >>> Matrix(_).xreplace({y: x})
2538
+ Matrix([[x]])
2539
+ """
2540
+ return self.applyfunc(lambda x: x.xreplace(rule))
2541
+
2542
+ def _eval_simplify(self, **kwargs):
2543
+ # XXX: We can't use self.simplify here as mutable subclasses will
2544
+ # override simplify and have it return None
2545
+ return MatrixOperations.simplify(self, **kwargs)
2546
+
2547
+ def _eval_trigsimp(self, **opts):
2548
+ from sympy.simplify.trigsimp import trigsimp
2549
+ return self.applyfunc(lambda x: trigsimp(x, **opts))
2550
+
2551
+ def upper_triangular(self, k=0):
2552
+ """Return the elements on and above the kth diagonal of a matrix.
2553
+ If k is not specified then simply returns upper-triangular portion
2554
+ of a matrix
2555
+
2556
+ Examples
2557
+ ========
2558
+
2559
+ >>> from sympy import ones
2560
+ >>> A = ones(4)
2561
+ >>> A.upper_triangular()
2562
+ Matrix([
2563
+ [1, 1, 1, 1],
2564
+ [0, 1, 1, 1],
2565
+ [0, 0, 1, 1],
2566
+ [0, 0, 0, 1]])
2567
+
2568
+ >>> A.upper_triangular(2)
2569
+ Matrix([
2570
+ [0, 0, 1, 1],
2571
+ [0, 0, 0, 1],
2572
+ [0, 0, 0, 0],
2573
+ [0, 0, 0, 0]])
2574
+
2575
+ >>> A.upper_triangular(-1)
2576
+ Matrix([
2577
+ [1, 1, 1, 1],
2578
+ [1, 1, 1, 1],
2579
+ [0, 1, 1, 1],
2580
+ [0, 0, 1, 1]])
2581
+
2582
+ """
2583
+
2584
+ def entry(i, j):
2585
+ return self[i, j] if i + k <= j else self.zero
2586
+
2587
+ return self._new(self.rows, self.cols, entry)
2588
+
2589
+
2590
+ def lower_triangular(self, k=0):
2591
+ """Return the elements on and below the kth diagonal of a matrix.
2592
+ If k is not specified then simply returns lower-triangular portion
2593
+ of a matrix
2594
+
2595
+ Examples
2596
+ ========
2597
+
2598
+ >>> from sympy import ones
2599
+ >>> A = ones(4)
2600
+ >>> A.lower_triangular()
2601
+ Matrix([
2602
+ [1, 0, 0, 0],
2603
+ [1, 1, 0, 0],
2604
+ [1, 1, 1, 0],
2605
+ [1, 1, 1, 1]])
2606
+
2607
+ >>> A.lower_triangular(-2)
2608
+ Matrix([
2609
+ [0, 0, 0, 0],
2610
+ [0, 0, 0, 0],
2611
+ [1, 0, 0, 0],
2612
+ [1, 1, 0, 0]])
2613
+
2614
+ >>> A.lower_triangular(1)
2615
+ Matrix([
2616
+ [1, 1, 0, 0],
2617
+ [1, 1, 1, 0],
2618
+ [1, 1, 1, 1],
2619
+ [1, 1, 1, 1]])
2620
+
2621
+ """
2622
+
2623
+ def entry(i, j):
2624
+ return self[i, j] if i + k >= j else self.zero
2625
+
2626
+ return self._new(self.rows, self.cols, entry)
2627
+
2628
+
2629
+
2630
+ class MatrixArithmetic(MatrixRequired):
2631
+ """Provides basic matrix arithmetic operations.
2632
+ Should not be instantiated directly."""
2633
+
2634
+ _op_priority = 10.01
2635
+
2636
+ def _eval_Abs(self):
2637
+ return self._new(self.rows, self.cols, lambda i, j: Abs(self[i, j]))
2638
+
2639
+ def _eval_add(self, other):
2640
+ return self._new(self.rows, self.cols,
2641
+ lambda i, j: self[i, j] + other[i, j])
2642
+
2643
+ def _eval_matrix_mul(self, other):
2644
+ def entry(i, j):
2645
+ vec = [self[i,k]*other[k,j] for k in range(self.cols)]
2646
+ try:
2647
+ return Add(*vec)
2648
+ except (TypeError, SympifyError):
2649
+ # Some matrices don't work with `sum` or `Add`
2650
+ # They don't work with `sum` because `sum` tries to add `0`
2651
+ # Fall back to a safe way to multiply if the `Add` fails.
2652
+ return reduce(lambda a, b: a + b, vec)
2653
+
2654
+ return self._new(self.rows, other.cols, entry)
2655
+
2656
+ def _eval_matrix_mul_elementwise(self, other):
2657
+ return self._new(self.rows, self.cols, lambda i, j: self[i,j]*other[i,j])
2658
+
2659
+ def _eval_matrix_rmul(self, other):
2660
+ def entry(i, j):
2661
+ return sum(other[i,k]*self[k,j] for k in range(other.cols))
2662
+ return self._new(other.rows, self.cols, entry)
2663
+
2664
+ def _eval_pow_by_recursion(self, num):
2665
+ if num == 1:
2666
+ return self
2667
+
2668
+ if num % 2 == 1:
2669
+ a, b = self, self._eval_pow_by_recursion(num - 1)
2670
+ else:
2671
+ a = b = self._eval_pow_by_recursion(num // 2)
2672
+
2673
+ return a.multiply(b)
2674
+
2675
+ def _eval_pow_by_cayley(self, exp):
2676
+ from sympy.discrete.recurrences import linrec_coeffs
2677
+ row = self.shape[0]
2678
+ p = self.charpoly()
2679
+
2680
+ coeffs = (-p).all_coeffs()[1:]
2681
+ coeffs = linrec_coeffs(coeffs, exp)
2682
+ new_mat = self.eye(row)
2683
+ ans = self.zeros(row)
2684
+
2685
+ for i in range(row):
2686
+ ans += coeffs[i]*new_mat
2687
+ new_mat *= self
2688
+
2689
+ return ans
2690
+
2691
+ def _eval_pow_by_recursion_dotprodsimp(self, num, prevsimp=None):
2692
+ if prevsimp is None:
2693
+ prevsimp = [True]*len(self)
2694
+
2695
+ if num == 1:
2696
+ return self
2697
+
2698
+ if num % 2 == 1:
2699
+ a, b = self, self._eval_pow_by_recursion_dotprodsimp(num - 1,
2700
+ prevsimp=prevsimp)
2701
+ else:
2702
+ a = b = self._eval_pow_by_recursion_dotprodsimp(num // 2,
2703
+ prevsimp=prevsimp)
2704
+
2705
+ m = a.multiply(b, dotprodsimp=False)
2706
+ lenm = len(m)
2707
+ elems = [None]*lenm
2708
+
2709
+ for i in range(lenm):
2710
+ if prevsimp[i]:
2711
+ elems[i], prevsimp[i] = _dotprodsimp(m[i], withsimp=True)
2712
+ else:
2713
+ elems[i] = m[i]
2714
+
2715
+ return m._new(m.rows, m.cols, elems)
2716
+
2717
+ def _eval_scalar_mul(self, other):
2718
+ return self._new(self.rows, self.cols, lambda i, j: self[i,j]*other)
2719
+
2720
+ def _eval_scalar_rmul(self, other):
2721
+ return self._new(self.rows, self.cols, lambda i, j: other*self[i,j])
2722
+
2723
+ def _eval_Mod(self, other):
2724
+ return self._new(self.rows, self.cols, lambda i, j: Mod(self[i, j], other))
2725
+
2726
+ # Python arithmetic functions
2727
+ def __abs__(self):
2728
+ """Returns a new matrix with entry-wise absolute values."""
2729
+ return self._eval_Abs()
2730
+
2731
+ @call_highest_priority('__radd__')
2732
+ def __add__(self, other):
2733
+ """Return self + other, raising ShapeError if shapes do not match."""
2734
+ if isinstance(other, NDimArray): # Matrix and array addition is currently not implemented
2735
+ return NotImplemented
2736
+ other = _matrixify(other)
2737
+ # matrix-like objects can have shapes. This is
2738
+ # our first sanity check.
2739
+ if hasattr(other, 'shape'):
2740
+ if self.shape != other.shape:
2741
+ raise ShapeError("Matrix size mismatch: %s + %s" % (
2742
+ self.shape, other.shape))
2743
+
2744
+ # honest SymPy matrices defer to their class's routine
2745
+ if getattr(other, 'is_Matrix', False):
2746
+ # call the highest-priority class's _eval_add
2747
+ a, b = self, other
2748
+ if a.__class__ != classof(a, b):
2749
+ b, a = a, b
2750
+ return a._eval_add(b)
2751
+ # Matrix-like objects can be passed to CommonMatrix routines directly.
2752
+ if getattr(other, 'is_MatrixLike', False):
2753
+ return MatrixArithmetic._eval_add(self, other)
2754
+
2755
+ raise TypeError('cannot add %s and %s' % (type(self), type(other)))
2756
+
2757
+ @call_highest_priority('__rtruediv__')
2758
+ def __truediv__(self, other):
2759
+ return self * (self.one / other)
2760
+
2761
+ @call_highest_priority('__rmatmul__')
2762
+ def __matmul__(self, other):
2763
+ other = _matrixify(other)
2764
+ if not getattr(other, 'is_Matrix', False) and not getattr(other, 'is_MatrixLike', False):
2765
+ return NotImplemented
2766
+
2767
+ return self.__mul__(other)
2768
+
2769
+ def __mod__(self, other):
2770
+ return self.applyfunc(lambda x: x % other)
2771
+
2772
+ @call_highest_priority('__rmul__')
2773
+ def __mul__(self, other):
2774
+ """Return self*other where other is either a scalar or a matrix
2775
+ of compatible dimensions.
2776
+
2777
+ Examples
2778
+ ========
2779
+
2780
+ >>> from sympy import Matrix
2781
+ >>> A = Matrix([[1, 2, 3], [4, 5, 6]])
2782
+ >>> 2*A == A*2 == Matrix([[2, 4, 6], [8, 10, 12]])
2783
+ True
2784
+ >>> B = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
2785
+ >>> A*B
2786
+ Matrix([
2787
+ [30, 36, 42],
2788
+ [66, 81, 96]])
2789
+ >>> B*A
2790
+ Traceback (most recent call last):
2791
+ ...
2792
+ ShapeError: Matrices size mismatch.
2793
+ >>>
2794
+
2795
+ See Also
2796
+ ========
2797
+
2798
+ matrix_multiply_elementwise
2799
+ """
2800
+
2801
+ return self.multiply(other)
2802
+
2803
+ def multiply(self, other, dotprodsimp=None):
2804
+ """Same as __mul__() but with optional simplification.
2805
+
2806
+ Parameters
2807
+ ==========
2808
+
2809
+ dotprodsimp : bool, optional
2810
+ Specifies whether intermediate term algebraic simplification is used
2811
+ during matrix multiplications to control expression blowup and thus
2812
+ speed up calculation. Default is off.
2813
+ """
2814
+
2815
+ isimpbool = _get_intermediate_simp_bool(False, dotprodsimp)
2816
+ other = _matrixify(other)
2817
+ # matrix-like objects can have shapes. This is
2818
+ # our first sanity check. Double check other is not explicitly not a Matrix.
2819
+ if (hasattr(other, 'shape') and len(other.shape) == 2 and
2820
+ (getattr(other, 'is_Matrix', True) or
2821
+ getattr(other, 'is_MatrixLike', True))):
2822
+ if self.shape[1] != other.shape[0]:
2823
+ raise ShapeError("Matrix size mismatch: %s * %s." % (
2824
+ self.shape, other.shape))
2825
+
2826
+ # honest SymPy matrices defer to their class's routine
2827
+ if getattr(other, 'is_Matrix', False):
2828
+ m = self._eval_matrix_mul(other)
2829
+ if isimpbool:
2830
+ return m._new(m.rows, m.cols, [_dotprodsimp(e) for e in m])
2831
+ return m
2832
+
2833
+ # Matrix-like objects can be passed to CommonMatrix routines directly.
2834
+ if getattr(other, 'is_MatrixLike', False):
2835
+ return MatrixArithmetic._eval_matrix_mul(self, other)
2836
+
2837
+ # if 'other' is not iterable then scalar multiplication.
2838
+ if not isinstance(other, Iterable):
2839
+ try:
2840
+ return self._eval_scalar_mul(other)
2841
+ except TypeError:
2842
+ pass
2843
+
2844
+ return NotImplemented
2845
+
2846
+ def multiply_elementwise(self, other):
2847
+ """Return the Hadamard product (elementwise product) of A and B
2848
+
2849
+ Examples
2850
+ ========
2851
+
2852
+ >>> from sympy import Matrix
2853
+ >>> A = Matrix([[0, 1, 2], [3, 4, 5]])
2854
+ >>> B = Matrix([[1, 10, 100], [100, 10, 1]])
2855
+ >>> A.multiply_elementwise(B)
2856
+ Matrix([
2857
+ [ 0, 10, 200],
2858
+ [300, 40, 5]])
2859
+
2860
+ See Also
2861
+ ========
2862
+
2863
+ sympy.matrices.matrixbase.MatrixBase.cross
2864
+ sympy.matrices.matrixbase.MatrixBase.dot
2865
+ multiply
2866
+ """
2867
+ if self.shape != other.shape:
2868
+ raise ShapeError("Matrix shapes must agree {} != {}".format(self.shape, other.shape))
2869
+
2870
+ return self._eval_matrix_mul_elementwise(other)
2871
+
2872
+ def __neg__(self):
2873
+ return self._eval_scalar_mul(-1)
2874
+
2875
+ @call_highest_priority('__rpow__')
2876
+ def __pow__(self, exp):
2877
+ """Return self**exp a scalar or symbol."""
2878
+
2879
+ return self.pow(exp)
2880
+
2881
+
2882
+ def pow(self, exp, method=None):
2883
+ r"""Return self**exp a scalar or symbol.
2884
+
2885
+ Parameters
2886
+ ==========
2887
+
2888
+ method : multiply, mulsimp, jordan, cayley
2889
+ If multiply then it returns exponentiation using recursion.
2890
+ If jordan then Jordan form exponentiation will be used.
2891
+ If cayley then the exponentiation is done using Cayley-Hamilton
2892
+ theorem.
2893
+ If mulsimp then the exponentiation is done using recursion
2894
+ with dotprodsimp. This specifies whether intermediate term
2895
+ algebraic simplification is used during naive matrix power to
2896
+ control expression blowup and thus speed up calculation.
2897
+ If None, then it heuristically decides which method to use.
2898
+
2899
+ """
2900
+
2901
+ if method is not None and method not in ['multiply', 'mulsimp', 'jordan', 'cayley']:
2902
+ raise TypeError('No such method')
2903
+ if self.rows != self.cols:
2904
+ raise NonSquareMatrixError()
2905
+ a = self
2906
+ jordan_pow = getattr(a, '_matrix_pow_by_jordan_blocks', None)
2907
+ exp = sympify(exp)
2908
+
2909
+ if exp.is_zero:
2910
+ return a._new(a.rows, a.cols, lambda i, j: int(i == j))
2911
+ if exp == 1:
2912
+ return a
2913
+
2914
+ diagonal = getattr(a, 'is_diagonal', None)
2915
+ if diagonal is not None and diagonal():
2916
+ return a._new(a.rows, a.cols, lambda i, j: a[i,j]**exp if i == j else 0)
2917
+
2918
+ if exp.is_Number and exp % 1 == 0:
2919
+ if a.rows == 1:
2920
+ return a._new([[a[0]**exp]])
2921
+ if exp < 0:
2922
+ exp = -exp
2923
+ a = a.inv()
2924
+ # When certain conditions are met,
2925
+ # Jordan block algorithm is faster than
2926
+ # computation by recursion.
2927
+ if method == 'jordan':
2928
+ try:
2929
+ return jordan_pow(exp)
2930
+ except MatrixError:
2931
+ if method == 'jordan':
2932
+ raise
2933
+
2934
+ elif method == 'cayley':
2935
+ if not exp.is_Number or exp % 1 != 0:
2936
+ raise ValueError("cayley method is only valid for integer powers")
2937
+ return a._eval_pow_by_cayley(exp)
2938
+
2939
+ elif method == "mulsimp":
2940
+ if not exp.is_Number or exp % 1 != 0:
2941
+ raise ValueError("mulsimp method is only valid for integer powers")
2942
+ return a._eval_pow_by_recursion_dotprodsimp(exp)
2943
+
2944
+ elif method == "multiply":
2945
+ if not exp.is_Number or exp % 1 != 0:
2946
+ raise ValueError("multiply method is only valid for integer powers")
2947
+ return a._eval_pow_by_recursion(exp)
2948
+
2949
+ elif method is None and exp.is_Number and exp % 1 == 0:
2950
+ if exp.is_Float:
2951
+ exp = Integer(exp)
2952
+ # Decide heuristically which method to apply
2953
+ if a.rows == 2 and exp > 100000:
2954
+ return jordan_pow(exp)
2955
+ elif _get_intermediate_simp_bool(True, None):
2956
+ return a._eval_pow_by_recursion_dotprodsimp(exp)
2957
+ elif exp > 10000:
2958
+ return a._eval_pow_by_cayley(exp)
2959
+ else:
2960
+ return a._eval_pow_by_recursion(exp)
2961
+
2962
+ if jordan_pow:
2963
+ try:
2964
+ return jordan_pow(exp)
2965
+ except NonInvertibleMatrixError:
2966
+ # Raised by jordan_pow on zero determinant matrix unless exp is
2967
+ # definitely known to be a non-negative integer.
2968
+ # Here we raise if n is definitely not a non-negative integer
2969
+ # but otherwise we can leave this as an unevaluated MatPow.
2970
+ if exp.is_integer is False or exp.is_nonnegative is False:
2971
+ raise
2972
+
2973
+ from sympy.matrices.expressions import MatPow
2974
+ return MatPow(a, exp)
2975
+
2976
+ @call_highest_priority('__add__')
2977
+ def __radd__(self, other):
2978
+ return self + other
2979
+
2980
+ @call_highest_priority('__matmul__')
2981
+ def __rmatmul__(self, other):
2982
+ other = _matrixify(other)
2983
+ if not getattr(other, 'is_Matrix', False) and not getattr(other, 'is_MatrixLike', False):
2984
+ return NotImplemented
2985
+
2986
+ return self.__rmul__(other)
2987
+
2988
+ @call_highest_priority('__mul__')
2989
+ def __rmul__(self, other):
2990
+ return self.rmultiply(other)
2991
+
2992
+ def rmultiply(self, other, dotprodsimp=None):
2993
+ """Same as __rmul__() but with optional simplification.
2994
+
2995
+ Parameters
2996
+ ==========
2997
+
2998
+ dotprodsimp : bool, optional
2999
+ Specifies whether intermediate term algebraic simplification is used
3000
+ during matrix multiplications to control expression blowup and thus
3001
+ speed up calculation. Default is off.
3002
+ """
3003
+ isimpbool = _get_intermediate_simp_bool(False, dotprodsimp)
3004
+ other = _matrixify(other)
3005
+ # matrix-like objects can have shapes. This is
3006
+ # our first sanity check. Double check other is not explicitly not a Matrix.
3007
+ if (hasattr(other, 'shape') and len(other.shape) == 2 and
3008
+ (getattr(other, 'is_Matrix', True) or
3009
+ getattr(other, 'is_MatrixLike', True))):
3010
+ if self.shape[0] != other.shape[1]:
3011
+ raise ShapeError("Matrix size mismatch.")
3012
+
3013
+ # honest SymPy matrices defer to their class's routine
3014
+ if getattr(other, 'is_Matrix', False):
3015
+ m = self._eval_matrix_rmul(other)
3016
+ if isimpbool:
3017
+ return m._new(m.rows, m.cols, [_dotprodsimp(e) for e in m])
3018
+ return m
3019
+ # Matrix-like objects can be passed to CommonMatrix routines directly.
3020
+ if getattr(other, 'is_MatrixLike', False):
3021
+ return MatrixArithmetic._eval_matrix_rmul(self, other)
3022
+
3023
+ # if 'other' is not iterable then scalar multiplication.
3024
+ if not isinstance(other, Iterable):
3025
+ try:
3026
+ return self._eval_scalar_rmul(other)
3027
+ except TypeError:
3028
+ pass
3029
+
3030
+ return NotImplemented
3031
+
3032
+ @call_highest_priority('__sub__')
3033
+ def __rsub__(self, a):
3034
+ return (-self) + a
3035
+
3036
+ @call_highest_priority('__rsub__')
3037
+ def __sub__(self, a):
3038
+ return self + (-a)
3039
+
3040
+
3041
+ class MatrixCommon(MatrixArithmetic, MatrixOperations, MatrixProperties,
3042
+ MatrixSpecial, MatrixShaping):
3043
+ """All common matrix operations including basic arithmetic, shaping,
3044
+ and special matrices like `zeros`, and `eye`."""
3045
+ _diff_wrt = True # type: bool
3046
+
3047
+
3048
+ class _MinimalMatrix:
3049
+ """Class providing the minimum functionality
3050
+ for a matrix-like object and implementing every method
3051
+ required for a `MatrixRequired`. This class does not have everything
3052
+ needed to become a full-fledged SymPy object, but it will satisfy the
3053
+ requirements of anything inheriting from `MatrixRequired`. If you wish
3054
+ to make a specialized matrix type, make sure to implement these
3055
+ methods and properties with the exception of `__init__` and `__repr__`
3056
+ which are included for convenience."""
3057
+
3058
+ is_MatrixLike = True
3059
+ _sympify = staticmethod(sympify)
3060
+ _class_priority = 3
3061
+ zero = S.Zero
3062
+ one = S.One
3063
+
3064
+ is_Matrix = True
3065
+ is_MatrixExpr = False
3066
+
3067
+ @classmethod
3068
+ def _new(cls, *args, **kwargs):
3069
+ return cls(*args, **kwargs)
3070
+
3071
+ def __init__(self, rows, cols=None, mat=None, copy=False):
3072
+ if isfunction(mat):
3073
+ # if we passed in a function, use that to populate the indices
3074
+ mat = [mat(i, j) for i in range(rows) for j in range(cols)]
3075
+ if cols is None and mat is None:
3076
+ mat = rows
3077
+ rows, cols = getattr(mat, 'shape', (rows, cols))
3078
+ try:
3079
+ # if we passed in a list of lists, flatten it and set the size
3080
+ if cols is None and mat is None:
3081
+ mat = rows
3082
+ cols = len(mat[0])
3083
+ rows = len(mat)
3084
+ mat = [x for l in mat for x in l]
3085
+ except (IndexError, TypeError):
3086
+ pass
3087
+ self.mat = tuple(self._sympify(x) for x in mat)
3088
+ self.rows, self.cols = rows, cols
3089
+ if self.rows is None or self.cols is None:
3090
+ raise NotImplementedError("Cannot initialize matrix with given parameters")
3091
+
3092
+ def __getitem__(self, key):
3093
+ def _normalize_slices(row_slice, col_slice):
3094
+ """Ensure that row_slice and col_slice do not have
3095
+ `None` in their arguments. Any integers are converted
3096
+ to slices of length 1"""
3097
+ if not isinstance(row_slice, slice):
3098
+ row_slice = slice(row_slice, row_slice + 1, None)
3099
+ row_slice = slice(*row_slice.indices(self.rows))
3100
+
3101
+ if not isinstance(col_slice, slice):
3102
+ col_slice = slice(col_slice, col_slice + 1, None)
3103
+ col_slice = slice(*col_slice.indices(self.cols))
3104
+
3105
+ return (row_slice, col_slice)
3106
+
3107
+ def _coord_to_index(i, j):
3108
+ """Return the index in _mat corresponding
3109
+ to the (i,j) position in the matrix. """
3110
+ return i * self.cols + j
3111
+
3112
+ if isinstance(key, tuple):
3113
+ i, j = key
3114
+ if isinstance(i, slice) or isinstance(j, slice):
3115
+ # if the coordinates are not slices, make them so
3116
+ # and expand the slices so they don't contain `None`
3117
+ i, j = _normalize_slices(i, j)
3118
+
3119
+ rowsList, colsList = list(range(self.rows))[i], \
3120
+ list(range(self.cols))[j]
3121
+ indices = (i * self.cols + j for i in rowsList for j in
3122
+ colsList)
3123
+ return self._new(len(rowsList), len(colsList),
3124
+ [self.mat[i] for i in indices])
3125
+
3126
+ # if the key is a tuple of ints, change
3127
+ # it to an array index
3128
+ key = _coord_to_index(i, j)
3129
+ return self.mat[key]
3130
+
3131
+ def __eq__(self, other):
3132
+ try:
3133
+ classof(self, other)
3134
+ except TypeError:
3135
+ return False
3136
+ return (
3137
+ self.shape == other.shape and list(self) == list(other))
3138
+
3139
+ def __len__(self):
3140
+ return self.rows*self.cols
3141
+
3142
+ def __repr__(self):
3143
+ return "_MinimalMatrix({}, {}, {})".format(self.rows, self.cols,
3144
+ self.mat)
3145
+
3146
+ @property
3147
+ def shape(self):
3148
+ return (self.rows, self.cols)
3149
+
3150
+
3151
+ class _CastableMatrix: # this is needed here ONLY FOR TESTS.
3152
+ def as_mutable(self):
3153
+ return self
3154
+
3155
+ def as_immutable(self):
3156
+ return self
3157
+
3158
+
3159
+ class _MatrixWrapper:
3160
+ """Wrapper class providing the minimum functionality for a matrix-like
3161
+ object: .rows, .cols, .shape, indexability, and iterability. CommonMatrix
3162
+ math operations should work on matrix-like objects. This one is intended for
3163
+ matrix-like objects which use the same indexing format as SymPy with respect
3164
+ to returning matrix elements instead of rows for non-tuple indexes.
3165
+ """
3166
+
3167
+ is_Matrix = False # needs to be here because of __getattr__
3168
+ is_MatrixLike = True
3169
+
3170
+ def __init__(self, mat, shape):
3171
+ self.mat = mat
3172
+ self.shape = shape
3173
+ self.rows, self.cols = shape
3174
+
3175
+ def __getitem__(self, key):
3176
+ if isinstance(key, tuple):
3177
+ return sympify(self.mat.__getitem__(key))
3178
+
3179
+ return sympify(self.mat.__getitem__((key // self.rows, key % self.cols)))
3180
+
3181
+ def __iter__(self): # supports numpy.matrix and numpy.array
3182
+ mat = self.mat
3183
+ cols = self.cols
3184
+
3185
+ return iter(sympify(mat[r, c]) for r in range(self.rows) for c in range(cols))
3186
+
3187
+
3188
+ def _matrixify(mat):
3189
+ """If `mat` is a Matrix or is matrix-like,
3190
+ return a Matrix or MatrixWrapper object. Otherwise
3191
+ `mat` is passed through without modification."""
3192
+
3193
+ if getattr(mat, 'is_Matrix', False) or getattr(mat, 'is_MatrixLike', False):
3194
+ return mat
3195
+
3196
+ if not(getattr(mat, 'is_Matrix', True) or getattr(mat, 'is_MatrixLike', True)):
3197
+ return mat
3198
+
3199
+ shape = None
3200
+
3201
+ if hasattr(mat, 'shape'): # numpy, scipy.sparse
3202
+ if len(mat.shape) == 2:
3203
+ shape = mat.shape
3204
+ elif hasattr(mat, 'rows') and hasattr(mat, 'cols'): # mpmath
3205
+ shape = (mat.rows, mat.cols)
3206
+
3207
+ if shape:
3208
+ return _MatrixWrapper(mat, shape)
3209
+
3210
+ return mat
3211
+
3212
+
3213
+ def a2idx(j, n=None):
3214
+ """Return integer after making positive and validating against n."""
3215
+ if not isinstance(j, int):
3216
+ jindex = getattr(j, '__index__', None)
3217
+ if jindex is not None:
3218
+ j = jindex()
3219
+ else:
3220
+ raise IndexError("Invalid index a[%r]" % (j,))
3221
+ if n is not None:
3222
+ if j < 0:
3223
+ j += n
3224
+ if not (j >= 0 and j < n):
3225
+ raise IndexError("Index out of range: a[%s]" % (j,))
3226
+ return int(j)
3227
+
3228
+
3229
+ def classof(A, B):
3230
+ """
3231
+ Get the type of the result when combining matrices of different types.
3232
+
3233
+ Currently the strategy is that immutability is contagious.
3234
+
3235
+ Examples
3236
+ ========
3237
+
3238
+ >>> from sympy import Matrix, ImmutableMatrix
3239
+ >>> from sympy.matrices.matrixbase import classof
3240
+ >>> M = Matrix([[1, 2], [3, 4]]) # a Mutable Matrix
3241
+ >>> IM = ImmutableMatrix([[1, 2], [3, 4]])
3242
+ >>> classof(M, IM)
3243
+ <class 'sympy.matrices.immutable.ImmutableDenseMatrix'>
3244
+ """
3245
+ priority_A = getattr(A, '_class_priority', None)
3246
+ priority_B = getattr(B, '_class_priority', None)
3247
+ if None not in (priority_A, priority_B):
3248
+ if A._class_priority > B._class_priority:
3249
+ return A.__class__
3250
+ else:
3251
+ return B.__class__
3252
+
3253
+ try:
3254
+ import numpy
3255
+ except ImportError:
3256
+ pass
3257
+ else:
3258
+ if isinstance(A, numpy.ndarray):
3259
+ return B.__class__
3260
+ if isinstance(B, numpy.ndarray):
3261
+ return A.__class__
3262
+
3263
+ raise TypeError("Incompatible classes %s, %s" % (A.__class__, B.__class__))
.venv/lib/python3.11/site-packages/sympy/matrices/determinant.py ADDED
@@ -0,0 +1,1021 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from types import FunctionType
2
+
3
+ from sympy.core.cache import cacheit
4
+ from sympy.core.numbers import Float, Integer
5
+ from sympy.core.singleton import S
6
+ from sympy.core.symbol import uniquely_named_symbol
7
+ from sympy.core.mul import Mul
8
+ from sympy.polys import PurePoly, cancel
9
+ from sympy.functions.combinatorial.numbers import nC
10
+ from sympy.polys.matrices.domainmatrix import DomainMatrix
11
+ from sympy.polys.matrices.ddm import DDM
12
+
13
+ from .exceptions import NonSquareMatrixError
14
+ from .utilities import (
15
+ _get_intermediate_simp, _get_intermediate_simp_bool,
16
+ _iszero, _is_zero_after_expand_mul, _dotprodsimp, _simplify)
17
+
18
+
19
+ def _find_reasonable_pivot(col, iszerofunc=_iszero, simpfunc=_simplify):
20
+ """ Find the lowest index of an item in ``col`` that is
21
+ suitable for a pivot. If ``col`` consists only of
22
+ Floats, the pivot with the largest norm is returned.
23
+ Otherwise, the first element where ``iszerofunc`` returns
24
+ False is used. If ``iszerofunc`` does not return false,
25
+ items are simplified and retested until a suitable
26
+ pivot is found.
27
+
28
+ Returns a 4-tuple
29
+ (pivot_offset, pivot_val, assumed_nonzero, newly_determined)
30
+ where pivot_offset is the index of the pivot, pivot_val is
31
+ the (possibly simplified) value of the pivot, assumed_nonzero
32
+ is True if an assumption that the pivot was non-zero
33
+ was made without being proved, and newly_determined are
34
+ elements that were simplified during the process of pivot
35
+ finding."""
36
+
37
+ newly_determined = []
38
+ col = list(col)
39
+ # a column that contains a mix of floats and integers
40
+ # but at least one float is considered a numerical
41
+ # column, and so we do partial pivoting
42
+ if all(isinstance(x, (Float, Integer)) for x in col) and any(
43
+ isinstance(x, Float) for x in col):
44
+ col_abs = [abs(x) for x in col]
45
+ max_value = max(col_abs)
46
+ if iszerofunc(max_value):
47
+ # just because iszerofunc returned True, doesn't
48
+ # mean the value is numerically zero. Make sure
49
+ # to replace all entries with numerical zeros
50
+ if max_value != 0:
51
+ newly_determined = [(i, 0) for i, x in enumerate(col) if x != 0]
52
+ return (None, None, False, newly_determined)
53
+ index = col_abs.index(max_value)
54
+ return (index, col[index], False, newly_determined)
55
+
56
+ # PASS 1 (iszerofunc directly)
57
+ possible_zeros = []
58
+ for i, x in enumerate(col):
59
+ is_zero = iszerofunc(x)
60
+ # is someone wrote a custom iszerofunc, it may return
61
+ # BooleanFalse or BooleanTrue instead of True or False,
62
+ # so use == for comparison instead of `is`
63
+ if is_zero == False:
64
+ # we found something that is definitely not zero
65
+ return (i, x, False, newly_determined)
66
+ possible_zeros.append(is_zero)
67
+
68
+ # by this point, we've found no certain non-zeros
69
+ if all(possible_zeros):
70
+ # if everything is definitely zero, we have
71
+ # no pivot
72
+ return (None, None, False, newly_determined)
73
+
74
+ # PASS 2 (iszerofunc after simplify)
75
+ # we haven't found any for-sure non-zeros, so
76
+ # go through the elements iszerofunc couldn't
77
+ # make a determination about and opportunistically
78
+ # simplify to see if we find something
79
+ for i, x in enumerate(col):
80
+ if possible_zeros[i] is not None:
81
+ continue
82
+ simped = simpfunc(x)
83
+ is_zero = iszerofunc(simped)
84
+ if is_zero in (True, False):
85
+ newly_determined.append((i, simped))
86
+ if is_zero == False:
87
+ return (i, simped, False, newly_determined)
88
+ possible_zeros[i] = is_zero
89
+
90
+ # after simplifying, some things that were recognized
91
+ # as zeros might be zeros
92
+ if all(possible_zeros):
93
+ # if everything is definitely zero, we have
94
+ # no pivot
95
+ return (None, None, False, newly_determined)
96
+
97
+ # PASS 3 (.equals(0))
98
+ # some expressions fail to simplify to zero, but
99
+ # ``.equals(0)`` evaluates to True. As a last-ditch
100
+ # attempt, apply ``.equals`` to these expressions
101
+ for i, x in enumerate(col):
102
+ if possible_zeros[i] is not None:
103
+ continue
104
+ if x.equals(S.Zero):
105
+ # ``.iszero`` may return False with
106
+ # an implicit assumption (e.g., ``x.equals(0)``
107
+ # when ``x`` is a symbol), so only treat it
108
+ # as proved when ``.equals(0)`` returns True
109
+ possible_zeros[i] = True
110
+ newly_determined.append((i, S.Zero))
111
+
112
+ if all(possible_zeros):
113
+ return (None, None, False, newly_determined)
114
+
115
+ # at this point there is nothing that could definitely
116
+ # be a pivot. To maintain compatibility with existing
117
+ # behavior, we'll assume that an illdetermined thing is
118
+ # non-zero. We should probably raise a warning in this case
119
+ i = possible_zeros.index(None)
120
+ return (i, col[i], True, newly_determined)
121
+
122
+
123
+ def _find_reasonable_pivot_naive(col, iszerofunc=_iszero, simpfunc=None):
124
+ """
125
+ Helper that computes the pivot value and location from a
126
+ sequence of contiguous matrix column elements. As a side effect
127
+ of the pivot search, this function may simplify some of the elements
128
+ of the input column. A list of these simplified entries and their
129
+ indices are also returned.
130
+ This function mimics the behavior of _find_reasonable_pivot(),
131
+ but does less work trying to determine if an indeterminate candidate
132
+ pivot simplifies to zero. This more naive approach can be much faster,
133
+ with the trade-off that it may erroneously return a pivot that is zero.
134
+
135
+ ``col`` is a sequence of contiguous column entries to be searched for
136
+ a suitable pivot.
137
+ ``iszerofunc`` is a callable that returns a Boolean that indicates
138
+ if its input is zero, or None if no such determination can be made.
139
+ ``simpfunc`` is a callable that simplifies its input. It must return
140
+ its input if it does not simplify its input. Passing in
141
+ ``simpfunc=None`` indicates that the pivot search should not attempt
142
+ to simplify any candidate pivots.
143
+
144
+ Returns a 4-tuple:
145
+ (pivot_offset, pivot_val, assumed_nonzero, newly_determined)
146
+ ``pivot_offset`` is the sequence index of the pivot.
147
+ ``pivot_val`` is the value of the pivot.
148
+ pivot_val and col[pivot_index] are equivalent, but will be different
149
+ when col[pivot_index] was simplified during the pivot search.
150
+ ``assumed_nonzero`` is a boolean indicating if the pivot cannot be
151
+ guaranteed to be zero. If assumed_nonzero is true, then the pivot
152
+ may or may not be non-zero. If assumed_nonzero is false, then
153
+ the pivot is non-zero.
154
+ ``newly_determined`` is a list of index-value pairs of pivot candidates
155
+ that were simplified during the pivot search.
156
+ """
157
+
158
+ # indeterminates holds the index-value pairs of each pivot candidate
159
+ # that is neither zero or non-zero, as determined by iszerofunc().
160
+ # If iszerofunc() indicates that a candidate pivot is guaranteed
161
+ # non-zero, or that every candidate pivot is zero then the contents
162
+ # of indeterminates are unused.
163
+ # Otherwise, the only viable candidate pivots are symbolic.
164
+ # In this case, indeterminates will have at least one entry,
165
+ # and all but the first entry are ignored when simpfunc is None.
166
+ indeterminates = []
167
+ for i, col_val in enumerate(col):
168
+ col_val_is_zero = iszerofunc(col_val)
169
+ if col_val_is_zero == False:
170
+ # This pivot candidate is non-zero.
171
+ return i, col_val, False, []
172
+ elif col_val_is_zero is None:
173
+ # The candidate pivot's comparison with zero
174
+ # is indeterminate.
175
+ indeterminates.append((i, col_val))
176
+
177
+ if len(indeterminates) == 0:
178
+ # All candidate pivots are guaranteed to be zero, i.e. there is
179
+ # no pivot.
180
+ return None, None, False, []
181
+
182
+ if simpfunc is None:
183
+ # Caller did not pass in a simplification function that might
184
+ # determine if an indeterminate pivot candidate is guaranteed
185
+ # to be nonzero, so assume the first indeterminate candidate
186
+ # is non-zero.
187
+ return indeterminates[0][0], indeterminates[0][1], True, []
188
+
189
+ # newly_determined holds index-value pairs of candidate pivots
190
+ # that were simplified during the search for a non-zero pivot.
191
+ newly_determined = []
192
+ for i, col_val in indeterminates:
193
+ tmp_col_val = simpfunc(col_val)
194
+ if id(col_val) != id(tmp_col_val):
195
+ # simpfunc() simplified this candidate pivot.
196
+ newly_determined.append((i, tmp_col_val))
197
+ if iszerofunc(tmp_col_val) == False:
198
+ # Candidate pivot simplified to a guaranteed non-zero value.
199
+ return i, tmp_col_val, False, newly_determined
200
+
201
+ return indeterminates[0][0], indeterminates[0][1], True, newly_determined
202
+
203
+
204
+ # This functions is a candidate for caching if it gets implemented for matrices.
205
+ def _berkowitz_toeplitz_matrix(M):
206
+ """Return (A,T) where T the Toeplitz matrix used in the Berkowitz algorithm
207
+ corresponding to ``M`` and A is the first principal submatrix.
208
+ """
209
+
210
+ # the 0 x 0 case is trivial
211
+ if M.rows == 0 and M.cols == 0:
212
+ return M._new(1,1, [M.one])
213
+
214
+ #
215
+ # Partition M = [ a_11 R ]
216
+ # [ C A ]
217
+ #
218
+
219
+ a, R = M[0,0], M[0, 1:]
220
+ C, A = M[1:, 0], M[1:,1:]
221
+
222
+ #
223
+ # The Toeplitz matrix looks like
224
+ #
225
+ # [ 1 ]
226
+ # [ -a 1 ]
227
+ # [ -RC -a 1 ]
228
+ # [ -RAC -RC -a 1 ]
229
+ # [ -RA**2C -RAC -RC -a 1 ]
230
+ # etc.
231
+
232
+ # Compute the diagonal entries.
233
+ # Because multiplying matrix times vector is so much
234
+ # more efficient than matrix times matrix, recursively
235
+ # compute -R * A**n * C.
236
+ diags = [C]
237
+ for i in range(M.rows - 2):
238
+ diags.append(A.multiply(diags[i], dotprodsimp=None))
239
+ diags = [(-R).multiply(d, dotprodsimp=None)[0, 0] for d in diags]
240
+ diags = [M.one, -a] + diags
241
+
242
+ def entry(i,j):
243
+ if j > i:
244
+ return M.zero
245
+ return diags[i - j]
246
+
247
+ toeplitz = M._new(M.cols + 1, M.rows, entry)
248
+ return (A, toeplitz)
249
+
250
+
251
+ # This functions is a candidate for caching if it gets implemented for matrices.
252
+ def _berkowitz_vector(M):
253
+ """ Run the Berkowitz algorithm and return a vector whose entries
254
+ are the coefficients of the characteristic polynomial of ``M``.
255
+
256
+ Given N x N matrix, efficiently compute
257
+ coefficients of characteristic polynomials of ``M``
258
+ without division in the ground domain.
259
+
260
+ This method is particularly useful for computing determinant,
261
+ principal minors and characteristic polynomial when ``M``
262
+ has complicated coefficients e.g. polynomials. Semi-direct
263
+ usage of this algorithm is also important in computing
264
+ efficiently sub-resultant PRS.
265
+
266
+ Assuming that M is a square matrix of dimension N x N and
267
+ I is N x N identity matrix, then the Berkowitz vector is
268
+ an N x 1 vector whose entries are coefficients of the
269
+ polynomial
270
+
271
+ charpoly(M) = det(t*I - M)
272
+
273
+ As a consequence, all polynomials generated by Berkowitz
274
+ algorithm are monic.
275
+
276
+ For more information on the implemented algorithm refer to:
277
+
278
+ [1] S.J. Berkowitz, On computing the determinant in small
279
+ parallel time using a small number of processors, ACM,
280
+ Information Processing Letters 18, 1984, pp. 147-150
281
+
282
+ [2] M. Keber, Division-Free computation of sub-resultants
283
+ using Bezout matrices, Tech. Report MPI-I-2006-1-006,
284
+ Saarbrucken, 2006
285
+ """
286
+
287
+ # handle the trivial cases
288
+ if M.rows == 0 and M.cols == 0:
289
+ return M._new(1, 1, [M.one])
290
+ elif M.rows == 1 and M.cols == 1:
291
+ return M._new(2, 1, [M.one, -M[0,0]])
292
+
293
+ submat, toeplitz = _berkowitz_toeplitz_matrix(M)
294
+
295
+ return toeplitz.multiply(_berkowitz_vector(submat), dotprodsimp=None)
296
+
297
+
298
+ def _adjugate(M, method="berkowitz"):
299
+ """Returns the adjugate, or classical adjoint, of
300
+ a matrix. That is, the transpose of the matrix of cofactors.
301
+
302
+ https://en.wikipedia.org/wiki/Adjugate
303
+
304
+ Parameters
305
+ ==========
306
+
307
+ method : string, optional
308
+ Method to use to find the cofactors, can be "bareiss", "berkowitz",
309
+ "bird", "laplace" or "lu".
310
+
311
+ Examples
312
+ ========
313
+
314
+ >>> from sympy import Matrix
315
+ >>> M = Matrix([[1, 2], [3, 4]])
316
+ >>> M.adjugate()
317
+ Matrix([
318
+ [ 4, -2],
319
+ [-3, 1]])
320
+
321
+ See Also
322
+ ========
323
+
324
+ cofactor_matrix
325
+ sympy.matrices.matrixbase.MatrixBase.transpose
326
+ """
327
+
328
+ return M.cofactor_matrix(method=method).transpose()
329
+
330
+
331
+ # This functions is a candidate for caching if it gets implemented for matrices.
332
+ def _charpoly(M, x='lambda', simplify=_simplify):
333
+ """Computes characteristic polynomial det(x*I - M) where I is
334
+ the identity matrix.
335
+
336
+ A PurePoly is returned, so using different variables for ``x`` does
337
+ not affect the comparison or the polynomials:
338
+
339
+ Parameters
340
+ ==========
341
+
342
+ x : string, optional
343
+ Name for the "lambda" variable, defaults to "lambda".
344
+
345
+ simplify : function, optional
346
+ Simplification function to use on the characteristic polynomial
347
+ calculated. Defaults to ``simplify``.
348
+
349
+ Examples
350
+ ========
351
+
352
+ >>> from sympy import Matrix
353
+ >>> from sympy.abc import x, y
354
+ >>> M = Matrix([[1, 3], [2, 0]])
355
+ >>> M.charpoly()
356
+ PurePoly(lambda**2 - lambda - 6, lambda, domain='ZZ')
357
+ >>> M.charpoly(x) == M.charpoly(y)
358
+ True
359
+ >>> M.charpoly(x) == M.charpoly(y)
360
+ True
361
+
362
+ Specifying ``x`` is optional; a symbol named ``lambda`` is used by
363
+ default (which looks good when pretty-printed in unicode):
364
+
365
+ >>> M.charpoly().as_expr()
366
+ lambda**2 - lambda - 6
367
+
368
+ And if ``x`` clashes with an existing symbol, underscores will
369
+ be prepended to the name to make it unique:
370
+
371
+ >>> M = Matrix([[1, 2], [x, 0]])
372
+ >>> M.charpoly(x).as_expr()
373
+ _x**2 - _x - 2*x
374
+
375
+ Whether you pass a symbol or not, the generator can be obtained
376
+ with the gen attribute since it may not be the same as the symbol
377
+ that was passed:
378
+
379
+ >>> M.charpoly(x).gen
380
+ _x
381
+ >>> M.charpoly(x).gen == x
382
+ False
383
+
384
+ Notes
385
+ =====
386
+
387
+ The Samuelson-Berkowitz algorithm is used to compute
388
+ the characteristic polynomial efficiently and without any
389
+ division operations. Thus the characteristic polynomial over any
390
+ commutative ring without zero divisors can be computed.
391
+
392
+ If the determinant det(x*I - M) can be found out easily as
393
+ in the case of an upper or a lower triangular matrix, then
394
+ instead of Samuelson-Berkowitz algorithm, eigenvalues are computed
395
+ and the characteristic polynomial with their help.
396
+
397
+ See Also
398
+ ========
399
+
400
+ det
401
+ """
402
+
403
+ if not M.is_square:
404
+ raise NonSquareMatrixError()
405
+
406
+ # Use DomainMatrix. We are already going to convert this to a Poly so there
407
+ # is no need to worry about expanding powers etc. Also since this algorithm
408
+ # does not require division or zero detection it is fine to use EX.
409
+ #
410
+ # M.to_DM() will fall back on EXRAW rather than EX. EXRAW is a lot faster
411
+ # for elementary arithmetic because it does not call cancel for each
412
+ # operation but it generates large unsimplified results that are slow in
413
+ # the subsequent call to simplify. Using EX instead is faster overall
414
+ # but at least in some cases EXRAW+simplify gives a simpler result so we
415
+ # preserve that existing behaviour of charpoly for now...
416
+ dM = M.to_DM()
417
+
418
+ K = dM.domain
419
+
420
+ cp = dM.charpoly()
421
+
422
+ x = uniquely_named_symbol(x, [M], modify=lambda s: '_' + s)
423
+
424
+ if K.is_EXRAW or simplify is not _simplify:
425
+ # XXX: Converting back to Expr is expensive. We only do it if the
426
+ # caller supplied a custom simplify function for backwards
427
+ # compatibility or otherwise if the domain was EX. For any other domain
428
+ # there should be no benefit in simplifying at this stage because Poly
429
+ # will put everything into canonical form anyway.
430
+ berk_vector = [K.to_sympy(c) for c in cp]
431
+ berk_vector = [simplify(a) for a in berk_vector]
432
+ p = PurePoly(berk_vector, x)
433
+
434
+ else:
435
+ # Convert from the list of domain elements directly to Poly.
436
+ p = PurePoly(cp, x, domain=K)
437
+
438
+ return p
439
+
440
+
441
+ def _cofactor(M, i, j, method="berkowitz"):
442
+ """Calculate the cofactor of an element.
443
+
444
+ Parameters
445
+ ==========
446
+
447
+ method : string, optional
448
+ Method to use to find the cofactors, can be "bareiss", "berkowitz",
449
+ "bird", "laplace" or "lu".
450
+
451
+ Examples
452
+ ========
453
+
454
+ >>> from sympy import Matrix
455
+ >>> M = Matrix([[1, 2], [3, 4]])
456
+ >>> M.cofactor(0, 1)
457
+ -3
458
+
459
+ See Also
460
+ ========
461
+
462
+ cofactor_matrix
463
+ minor
464
+ minor_submatrix
465
+ """
466
+
467
+ if not M.is_square or M.rows < 1:
468
+ raise NonSquareMatrixError()
469
+
470
+ return S.NegativeOne**((i + j) % 2) * M.minor(i, j, method)
471
+
472
+
473
+ def _cofactor_matrix(M, method="berkowitz"):
474
+ """Return a matrix containing the cofactor of each element.
475
+
476
+ Parameters
477
+ ==========
478
+
479
+ method : string, optional
480
+ Method to use to find the cofactors, can be "bareiss", "berkowitz",
481
+ "bird", "laplace" or "lu".
482
+
483
+ Examples
484
+ ========
485
+
486
+ >>> from sympy import Matrix
487
+ >>> M = Matrix([[1, 2], [3, 4]])
488
+ >>> M.cofactor_matrix()
489
+ Matrix([
490
+ [ 4, -3],
491
+ [-2, 1]])
492
+
493
+ See Also
494
+ ========
495
+
496
+ cofactor
497
+ minor
498
+ minor_submatrix
499
+ """
500
+
501
+ if not M.is_square:
502
+ raise NonSquareMatrixError()
503
+
504
+ return M._new(M.rows, M.cols,
505
+ lambda i, j: M.cofactor(i, j, method))
506
+
507
+ def _per(M):
508
+ """Returns the permanent of a matrix. Unlike determinant,
509
+ permanent is defined for both square and non-square matrices.
510
+
511
+ For an m x n matrix, with m less than or equal to n,
512
+ it is given as the sum over the permutations s of size
513
+ less than or equal to m on [1, 2, . . . n] of the product
514
+ from i = 1 to m of M[i, s[i]]. Taking the transpose will
515
+ not affect the value of the permanent.
516
+
517
+ In the case of a square matrix, this is the same as the permutation
518
+ definition of the determinant, but it does not take the sign of the
519
+ permutation into account. Computing the permanent with this definition
520
+ is quite inefficient, so here the Ryser formula is used.
521
+
522
+ Examples
523
+ ========
524
+
525
+ >>> from sympy import Matrix
526
+ >>> M = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
527
+ >>> M.per()
528
+ 450
529
+ >>> M = Matrix([1, 5, 7])
530
+ >>> M.per()
531
+ 13
532
+
533
+ References
534
+ ==========
535
+
536
+ .. [1] Prof. Frank Ben's notes: https://math.berkeley.edu/~bernd/ban275.pdf
537
+ .. [2] Wikipedia article on Permanent: https://en.wikipedia.org/wiki/Permanent_%28mathematics%29
538
+ .. [3] https://reference.wolfram.com/language/ref/Permanent.html
539
+ .. [4] Permanent of a rectangular matrix : https://arxiv.org/pdf/0904.3251.pdf
540
+ """
541
+ import itertools
542
+
543
+ m, n = M.shape
544
+ if m > n:
545
+ M = M.T
546
+ m, n = n, m
547
+ s = list(range(n))
548
+
549
+ subsets = []
550
+ for i in range(1, m + 1):
551
+ subsets += list(map(list, itertools.combinations(s, i)))
552
+
553
+ perm = 0
554
+ for subset in subsets:
555
+ prod = 1
556
+ sub_len = len(subset)
557
+ for i in range(m):
558
+ prod *= sum(M[i, j] for j in subset)
559
+ perm += prod * S.NegativeOne**sub_len * nC(n - sub_len, m - sub_len)
560
+ perm *= S.NegativeOne**m
561
+ return perm.simplify()
562
+
563
+ def _det_DOM(M):
564
+ DOM = DomainMatrix.from_Matrix(M, field=True, extension=True)
565
+ K = DOM.domain
566
+ return K.to_sympy(DOM.det())
567
+
568
+ # This functions is a candidate for caching if it gets implemented for matrices.
569
+ def _det(M, method="bareiss", iszerofunc=None):
570
+ """Computes the determinant of a matrix if ``M`` is a concrete matrix object
571
+ otherwise return an expressions ``Determinant(M)`` if ``M`` is a
572
+ ``MatrixSymbol`` or other expression.
573
+
574
+ Parameters
575
+ ==========
576
+
577
+ method : string, optional
578
+ Specifies the algorithm used for computing the matrix determinant.
579
+
580
+ If the matrix is at most 3x3, a hard-coded formula is used and the
581
+ specified method is ignored. Otherwise, it defaults to
582
+ ``'bareiss'``.
583
+
584
+ Also, if the matrix is an upper or a lower triangular matrix, determinant
585
+ is computed by simple multiplication of diagonal elements, and the
586
+ specified method is ignored.
587
+
588
+ If it is set to ``'domain-ge'``, then Gaussian elimination method will
589
+ be used via using DomainMatrix.
590
+
591
+ If it is set to ``'bareiss'``, Bareiss' fraction-free algorithm will
592
+ be used.
593
+
594
+ If it is set to ``'berkowitz'``, Berkowitz' algorithm will be used.
595
+
596
+ If it is set to ``'bird'``, Bird's algorithm will be used [1]_.
597
+
598
+ If it is set to ``'laplace'``, Laplace's algorithm will be used.
599
+
600
+ Otherwise, if it is set to ``'lu'``, LU decomposition will be used.
601
+
602
+ .. note::
603
+ For backward compatibility, legacy keys like "bareis" and
604
+ "det_lu" can still be used to indicate the corresponding
605
+ methods.
606
+ And the keys are also case-insensitive for now. However, it is
607
+ suggested to use the precise keys for specifying the method.
608
+
609
+ iszerofunc : FunctionType or None, optional
610
+ If it is set to ``None``, it will be defaulted to ``_iszero`` if the
611
+ method is set to ``'bareiss'``, and ``_is_zero_after_expand_mul`` if
612
+ the method is set to ``'lu'``.
613
+
614
+ It can also accept any user-specified zero testing function, if it
615
+ is formatted as a function which accepts a single symbolic argument
616
+ and returns ``True`` if it is tested as zero and ``False`` if it
617
+ tested as non-zero, and also ``None`` if it is undecidable.
618
+
619
+ Returns
620
+ =======
621
+
622
+ det : Basic
623
+ Result of determinant.
624
+
625
+ Raises
626
+ ======
627
+
628
+ ValueError
629
+ If unrecognized keys are given for ``method`` or ``iszerofunc``.
630
+
631
+ NonSquareMatrixError
632
+ If attempted to calculate determinant from a non-square matrix.
633
+
634
+ Examples
635
+ ========
636
+
637
+ >>> from sympy import Matrix, eye, det
638
+ >>> I3 = eye(3)
639
+ >>> det(I3)
640
+ 1
641
+ >>> M = Matrix([[1, 2], [3, 4]])
642
+ >>> det(M)
643
+ -2
644
+ >>> det(M) == M.det()
645
+ True
646
+ >>> M.det(method="domain-ge")
647
+ -2
648
+
649
+ References
650
+ ==========
651
+
652
+ .. [1] Bird, R. S. (2011). A simple division-free algorithm for computing
653
+ determinants. Inf. Process. Lett., 111(21), 1072-1074. doi:
654
+ 10.1016/j.ipl.2011.08.006
655
+ """
656
+
657
+ # sanitize `method`
658
+ method = method.lower()
659
+
660
+ if method == "bareis":
661
+ method = "bareiss"
662
+ elif method == "det_lu":
663
+ method = "lu"
664
+
665
+ if method not in ("bareiss", "berkowitz", "lu", "domain-ge", "bird",
666
+ "laplace"):
667
+ raise ValueError("Determinant method '%s' unrecognized" % method)
668
+
669
+ if iszerofunc is None:
670
+ if method == "bareiss":
671
+ iszerofunc = _is_zero_after_expand_mul
672
+ elif method == "lu":
673
+ iszerofunc = _iszero
674
+
675
+ elif not isinstance(iszerofunc, FunctionType):
676
+ raise ValueError("Zero testing method '%s' unrecognized" % iszerofunc)
677
+
678
+ n = M.rows
679
+
680
+ if n == M.cols: # square check is done in individual method functions
681
+ if n == 0:
682
+ return M.one
683
+ elif n == 1:
684
+ return M[0, 0]
685
+ elif n == 2:
686
+ m = M[0, 0] * M[1, 1] - M[0, 1] * M[1, 0]
687
+ return _get_intermediate_simp(_dotprodsimp)(m)
688
+ elif n == 3:
689
+ m = (M[0, 0] * M[1, 1] * M[2, 2]
690
+ + M[0, 1] * M[1, 2] * M[2, 0]
691
+ + M[0, 2] * M[1, 0] * M[2, 1]
692
+ - M[0, 2] * M[1, 1] * M[2, 0]
693
+ - M[0, 0] * M[1, 2] * M[2, 1]
694
+ - M[0, 1] * M[1, 0] * M[2, 2])
695
+ return _get_intermediate_simp(_dotprodsimp)(m)
696
+
697
+ dets = []
698
+ for b in M.strongly_connected_components():
699
+ if method == "domain-ge": # uses DomainMatrix to evaluate determinant
700
+ det = _det_DOM(M[b, b])
701
+ elif method == "bareiss":
702
+ det = M[b, b]._eval_det_bareiss(iszerofunc=iszerofunc)
703
+ elif method == "berkowitz":
704
+ det = M[b, b]._eval_det_berkowitz()
705
+ elif method == "lu":
706
+ det = M[b, b]._eval_det_lu(iszerofunc=iszerofunc)
707
+ elif method == "bird":
708
+ det = M[b, b]._eval_det_bird()
709
+ elif method == "laplace":
710
+ det = M[b, b]._eval_det_laplace()
711
+ dets.append(det)
712
+ return Mul(*dets)
713
+
714
+
715
+ # This functions is a candidate for caching if it gets implemented for matrices.
716
+ def _det_bareiss(M, iszerofunc=_is_zero_after_expand_mul):
717
+ """Compute matrix determinant using Bareiss' fraction-free
718
+ algorithm which is an extension of the well known Gaussian
719
+ elimination method. This approach is best suited for dense
720
+ symbolic matrices and will result in a determinant with
721
+ minimal number of fractions. It means that less term
722
+ rewriting is needed on resulting formulae.
723
+
724
+ Parameters
725
+ ==========
726
+
727
+ iszerofunc : function, optional
728
+ The function to use to determine zeros when doing an LU decomposition.
729
+ Defaults to ``lambda x: x.is_zero``.
730
+
731
+ TODO: Implement algorithm for sparse matrices (SFF),
732
+ http://www.eecis.udel.edu/~saunders/papers/sffge/it5.ps.
733
+ """
734
+
735
+ # Recursively implemented Bareiss' algorithm as per Deanna Richelle Leggett's
736
+ # thesis http://www.math.usm.edu/perry/Research/Thesis_DRL.pdf
737
+ def bareiss(mat, cumm=1):
738
+ if mat.rows == 0:
739
+ return mat.one
740
+ elif mat.rows == 1:
741
+ return mat[0, 0]
742
+
743
+ # find a pivot and extract the remaining matrix
744
+ # With the default iszerofunc, _find_reasonable_pivot slows down
745
+ # the computation by the factor of 2.5 in one test.
746
+ # Relevant issues: #10279 and #13877.
747
+ pivot_pos, pivot_val, _, _ = _find_reasonable_pivot(mat[:, 0], iszerofunc=iszerofunc)
748
+ if pivot_pos is None:
749
+ return mat.zero
750
+
751
+ # if we have a valid pivot, we'll do a "row swap", so keep the
752
+ # sign of the det
753
+ sign = (-1) ** (pivot_pos % 2)
754
+
755
+ # we want every row but the pivot row and every column
756
+ rows = [i for i in range(mat.rows) if i != pivot_pos]
757
+ cols = list(range(mat.cols))
758
+ tmp_mat = mat.extract(rows, cols)
759
+
760
+ def entry(i, j):
761
+ ret = (pivot_val*tmp_mat[i, j + 1] - mat[pivot_pos, j + 1]*tmp_mat[i, 0]) / cumm
762
+ if _get_intermediate_simp_bool(True):
763
+ return _dotprodsimp(ret)
764
+ elif not ret.is_Atom:
765
+ return cancel(ret)
766
+ return ret
767
+
768
+ return sign*bareiss(M._new(mat.rows - 1, mat.cols - 1, entry), pivot_val)
769
+
770
+ if not M.is_square:
771
+ raise NonSquareMatrixError()
772
+
773
+ if M.rows == 0:
774
+ return M.one
775
+ # sympy/matrices/tests/test_matrices.py contains a test that
776
+ # suggests that the determinant of a 0 x 0 matrix is one, by
777
+ # convention.
778
+
779
+ return bareiss(M)
780
+
781
+
782
+ def _det_berkowitz(M):
783
+ """ Use the Berkowitz algorithm to compute the determinant."""
784
+
785
+ if not M.is_square:
786
+ raise NonSquareMatrixError()
787
+
788
+ if M.rows == 0:
789
+ return M.one
790
+ # sympy/matrices/tests/test_matrices.py contains a test that
791
+ # suggests that the determinant of a 0 x 0 matrix is one, by
792
+ # convention.
793
+
794
+ berk_vector = _berkowitz_vector(M)
795
+ return (-1)**(len(berk_vector) - 1) * berk_vector[-1]
796
+
797
+
798
+ # This functions is a candidate for caching if it gets implemented for matrices.
799
+ def _det_LU(M, iszerofunc=_iszero, simpfunc=None):
800
+ """ Computes the determinant of a matrix from its LU decomposition.
801
+ This function uses the LU decomposition computed by
802
+ LUDecomposition_Simple().
803
+
804
+ The keyword arguments iszerofunc and simpfunc are passed to
805
+ LUDecomposition_Simple().
806
+ iszerofunc is a callable that returns a boolean indicating if its
807
+ input is zero, or None if it cannot make the determination.
808
+ simpfunc is a callable that simplifies its input.
809
+ The default is simpfunc=None, which indicate that the pivot search
810
+ algorithm should not attempt to simplify any candidate pivots.
811
+ If simpfunc fails to simplify its input, then it must return its input
812
+ instead of a copy.
813
+
814
+ Parameters
815
+ ==========
816
+
817
+ iszerofunc : function, optional
818
+ The function to use to determine zeros when doing an LU decomposition.
819
+ Defaults to ``lambda x: x.is_zero``.
820
+
821
+ simpfunc : function, optional
822
+ The simplification function to use when looking for zeros for pivots.
823
+ """
824
+
825
+ if not M.is_square:
826
+ raise NonSquareMatrixError()
827
+
828
+ if M.rows == 0:
829
+ return M.one
830
+ # sympy/matrices/tests/test_matrices.py contains a test that
831
+ # suggests that the determinant of a 0 x 0 matrix is one, by
832
+ # convention.
833
+
834
+ lu, row_swaps = M.LUdecomposition_Simple(iszerofunc=iszerofunc,
835
+ simpfunc=simpfunc)
836
+ # P*A = L*U => det(A) = det(L)*det(U)/det(P) = det(P)*det(U).
837
+ # Lower triangular factor L encoded in lu has unit diagonal => det(L) = 1.
838
+ # P is a permutation matrix => det(P) in {-1, 1} => 1/det(P) = det(P).
839
+ # LUdecomposition_Simple() returns a list of row exchange index pairs, rather
840
+ # than a permutation matrix, but det(P) = (-1)**len(row_swaps).
841
+
842
+ # Avoid forming the potentially time consuming product of U's diagonal entries
843
+ # if the product is zero.
844
+ # Bottom right entry of U is 0 => det(A) = 0.
845
+ # It may be impossible to determine if this entry of U is zero when it is symbolic.
846
+ if iszerofunc(lu[lu.rows-1, lu.rows-1]):
847
+ return M.zero
848
+
849
+ # Compute det(P)
850
+ det = -M.one if len(row_swaps)%2 else M.one
851
+
852
+ # Compute det(U) by calculating the product of U's diagonal entries.
853
+ # The upper triangular portion of lu is the upper triangular portion of the
854
+ # U factor in the LU decomposition.
855
+ for k in range(lu.rows):
856
+ det *= lu[k, k]
857
+
858
+ # return det(P)*det(U)
859
+ return det
860
+
861
+
862
+ @cacheit
863
+ def __det_laplace(M):
864
+ """Compute the determinant of a matrix using Laplace expansion.
865
+
866
+ This is a recursive function, and it should not be called directly.
867
+ Use _det_laplace() instead. The reason for splitting this function
868
+ into two is to allow caching of determinants of submatrices. While
869
+ one could also define this function inside _det_laplace(), that
870
+ would remove the advantage of using caching in Cramer Solve.
871
+ """
872
+ n = M.shape[0]
873
+ if n == 1:
874
+ return M[0]
875
+ elif n == 2:
876
+ return M[0, 0] * M[1, 1] - M[0, 1] * M[1, 0]
877
+ else:
878
+ return sum((-1) ** i * M[0, i] *
879
+ __det_laplace(M.minor_submatrix(0, i)) for i in range(n))
880
+
881
+
882
+ def _det_laplace(M):
883
+ """Compute the determinant of a matrix using Laplace expansion.
884
+
885
+ While Laplace expansion is not the most efficient method of computing
886
+ a determinant, it is a simple one, and it has the advantage of
887
+ being division free. To improve efficiency, this function uses
888
+ caching to avoid recomputing determinants of submatrices.
889
+ """
890
+ if not M.is_square:
891
+ raise NonSquareMatrixError()
892
+ if M.shape[0] == 0:
893
+ return M.one
894
+ # sympy/matrices/tests/test_matrices.py contains a test that
895
+ # suggests that the determinant of a 0 x 0 matrix is one, by
896
+ # convention.
897
+ return __det_laplace(M.as_immutable())
898
+
899
+
900
+ def _det_bird(M):
901
+ r"""Compute the determinant of a matrix using Bird's algorithm.
902
+
903
+ Bird's algorithm is a simple division-free algorithm for computing, which
904
+ is of lower order than the Laplace's algorithm. It is described in [1]_.
905
+
906
+ References
907
+ ==========
908
+
909
+ .. [1] Bird, R. S. (2011). A simple division-free algorithm for computing
910
+ determinants. Inf. Process. Lett., 111(21), 1072-1074. doi:
911
+ 10.1016/j.ipl.2011.08.006
912
+ """
913
+ def mu(X):
914
+ n = X.shape[0]
915
+ zero = X.domain.zero
916
+
917
+ total = zero
918
+ diag_sums = [zero]
919
+ for i in reversed(range(1, n)):
920
+ total -= X[i][i]
921
+ diag_sums.append(total)
922
+ diag_sums = diag_sums[::-1]
923
+
924
+ elems = [[zero] * i + [diag_sums[i]] + X_i[i + 1:] for i, X_i in
925
+ enumerate(X)]
926
+ return DDM(elems, X.shape, X.domain)
927
+
928
+ Mddm = M._rep.to_ddm()
929
+ n = M.shape[0]
930
+ if n == 0:
931
+ return M.one
932
+ # sympy/matrices/tests/test_matrices.py contains a test that
933
+ # suggests that the determinant of a 0 x 0 matrix is one, by
934
+ # convention.
935
+ Fn1 = Mddm
936
+ for _ in range(n - 1):
937
+ Fn1 = mu(Fn1).matmul(Mddm)
938
+ detA = Fn1[0][0]
939
+ if n % 2 == 0:
940
+ detA = -detA
941
+
942
+ return Mddm.domain.to_sympy(detA)
943
+
944
+
945
+ def _minor(M, i, j, method="berkowitz"):
946
+ """Return the (i,j) minor of ``M``. That is,
947
+ return the determinant of the matrix obtained by deleting
948
+ the `i`th row and `j`th column from ``M``.
949
+
950
+ Parameters
951
+ ==========
952
+
953
+ i, j : int
954
+ The row and column to exclude to obtain the submatrix.
955
+
956
+ method : string, optional
957
+ Method to use to find the determinant of the submatrix, can be
958
+ "bareiss", "berkowitz", "bird", "laplace" or "lu".
959
+
960
+ Examples
961
+ ========
962
+
963
+ >>> from sympy import Matrix
964
+ >>> M = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
965
+ >>> M.minor(1, 1)
966
+ -12
967
+
968
+ See Also
969
+ ========
970
+
971
+ minor_submatrix
972
+ cofactor
973
+ det
974
+ """
975
+
976
+ if not M.is_square:
977
+ raise NonSquareMatrixError()
978
+
979
+ return M.minor_submatrix(i, j).det(method=method)
980
+
981
+
982
+ def _minor_submatrix(M, i, j):
983
+ """Return the submatrix obtained by removing the `i`th row
984
+ and `j`th column from ``M`` (works with Pythonic negative indices).
985
+
986
+ Parameters
987
+ ==========
988
+
989
+ i, j : int
990
+ The row and column to exclude to obtain the submatrix.
991
+
992
+ Examples
993
+ ========
994
+
995
+ >>> from sympy import Matrix
996
+ >>> M = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
997
+ >>> M.minor_submatrix(1, 1)
998
+ Matrix([
999
+ [1, 3],
1000
+ [7, 9]])
1001
+
1002
+ See Also
1003
+ ========
1004
+
1005
+ minor
1006
+ cofactor
1007
+ """
1008
+
1009
+ if i < 0:
1010
+ i += M.rows
1011
+ if j < 0:
1012
+ j += M.cols
1013
+
1014
+ if not 0 <= i < M.rows or not 0 <= j < M.cols:
1015
+ raise ValueError("`i` and `j` must satisfy 0 <= i < ``M.rows`` "
1016
+ "(%d)" % M.rows + "and 0 <= j < ``M.cols`` (%d)." % M.cols)
1017
+
1018
+ rows = [a for a in range(M.rows) if a != i]
1019
+ cols = [a for a in range(M.cols) if a != j]
1020
+
1021
+ return M.extract(rows, cols)
.venv/lib/python3.11/site-packages/sympy/matrices/eigen.py ADDED
@@ -0,0 +1,1346 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from types import FunctionType
2
+ from collections import Counter
3
+
4
+ from mpmath import mp, workprec
5
+ from mpmath.libmp.libmpf import prec_to_dps
6
+
7
+ from sympy.core.sorting import default_sort_key
8
+ from sympy.core.evalf import DEFAULT_MAXPREC, PrecisionExhausted
9
+ from sympy.core.logic import fuzzy_and, fuzzy_or
10
+ from sympy.core.numbers import Float
11
+ from sympy.core.sympify import _sympify
12
+ from sympy.functions.elementary.miscellaneous import sqrt
13
+ from sympy.polys import roots, CRootOf, ZZ, QQ, EX
14
+ from sympy.polys.matrices import DomainMatrix
15
+ from sympy.polys.matrices.eigen import dom_eigenvects, dom_eigenvects_to_sympy
16
+ from sympy.polys.polytools import gcd
17
+
18
+ from .exceptions import MatrixError, NonSquareMatrixError
19
+ from .determinant import _find_reasonable_pivot
20
+
21
+ from .utilities import _iszero, _simplify
22
+
23
+
24
+ __doctest_requires__ = {
25
+ ('_is_indefinite',
26
+ '_is_negative_definite',
27
+ '_is_negative_semidefinite',
28
+ '_is_positive_definite',
29
+ '_is_positive_semidefinite'): ['matplotlib'],
30
+ }
31
+
32
+
33
+ def _eigenvals_eigenvects_mpmath(M):
34
+ norm2 = lambda v: mp.sqrt(sum(i**2 for i in v))
35
+
36
+ v1 = None
37
+ prec = max(x._prec for x in M.atoms(Float))
38
+ eps = 2**-prec
39
+
40
+ while prec < DEFAULT_MAXPREC:
41
+ with workprec(prec):
42
+ A = mp.matrix(M.evalf(n=prec_to_dps(prec)))
43
+ E, ER = mp.eig(A)
44
+ v2 = norm2([i for e in E for i in (mp.re(e), mp.im(e))])
45
+ if v1 is not None and mp.fabs(v1 - v2) < eps:
46
+ return E, ER
47
+ v1 = v2
48
+ prec *= 2
49
+
50
+ # we get here because the next step would have taken us
51
+ # past MAXPREC or because we never took a step; in case
52
+ # of the latter, we refuse to send back a solution since
53
+ # it would not have been verified; we also resist taking
54
+ # a small step to arrive exactly at MAXPREC since then
55
+ # the two calculations might be artificially close.
56
+ raise PrecisionExhausted
57
+
58
+
59
+ def _eigenvals_mpmath(M, multiple=False):
60
+ """Compute eigenvalues using mpmath"""
61
+ E, _ = _eigenvals_eigenvects_mpmath(M)
62
+ result = [_sympify(x) for x in E]
63
+ if multiple:
64
+ return result
65
+ return dict(Counter(result))
66
+
67
+
68
+ def _eigenvects_mpmath(M):
69
+ E, ER = _eigenvals_eigenvects_mpmath(M)
70
+ result = []
71
+ for i in range(M.rows):
72
+ eigenval = _sympify(E[i])
73
+ eigenvect = _sympify(ER[:, i])
74
+ result.append((eigenval, 1, [eigenvect]))
75
+
76
+ return result
77
+
78
+
79
+ # This function is a candidate for caching if it gets implemented for matrices.
80
+ def _eigenvals(
81
+ M, error_when_incomplete=True, *, simplify=False, multiple=False,
82
+ rational=False, **flags):
83
+ r"""Compute eigenvalues of the matrix.
84
+
85
+ Parameters
86
+ ==========
87
+
88
+ error_when_incomplete : bool, optional
89
+ If it is set to ``True``, it will raise an error if not all
90
+ eigenvalues are computed. This is caused by ``roots`` not returning
91
+ a full list of eigenvalues.
92
+
93
+ simplify : bool or function, optional
94
+ If it is set to ``True``, it attempts to return the most
95
+ simplified form of expressions returned by applying default
96
+ simplification method in every routine.
97
+
98
+ If it is set to ``False``, it will skip simplification in this
99
+ particular routine to save computation resources.
100
+
101
+ If a function is passed to, it will attempt to apply
102
+ the particular function as simplification method.
103
+
104
+ rational : bool, optional
105
+ If it is set to ``True``, every floating point numbers would be
106
+ replaced with rationals before computation. It can solve some
107
+ issues of ``roots`` routine not working well with floats.
108
+
109
+ multiple : bool, optional
110
+ If it is set to ``True``, the result will be in the form of a
111
+ list.
112
+
113
+ If it is set to ``False``, the result will be in the form of a
114
+ dictionary.
115
+
116
+ Returns
117
+ =======
118
+
119
+ eigs : list or dict
120
+ Eigenvalues of a matrix. The return format would be specified by
121
+ the key ``multiple``.
122
+
123
+ Raises
124
+ ======
125
+
126
+ MatrixError
127
+ If not enough roots had got computed.
128
+
129
+ NonSquareMatrixError
130
+ If attempted to compute eigenvalues from a non-square matrix.
131
+
132
+ Examples
133
+ ========
134
+
135
+ >>> from sympy import Matrix
136
+ >>> M = Matrix(3, 3, [0, 1, 1, 1, 0, 0, 1, 1, 1])
137
+ >>> M.eigenvals()
138
+ {-1: 1, 0: 1, 2: 1}
139
+
140
+ See Also
141
+ ========
142
+
143
+ MatrixBase.charpoly
144
+ eigenvects
145
+
146
+ Notes
147
+ =====
148
+
149
+ Eigenvalues of a matrix $A$ can be computed by solving a matrix
150
+ equation $\det(A - \lambda I) = 0$
151
+
152
+ It's not always possible to return radical solutions for
153
+ eigenvalues for matrices larger than $4, 4$ shape due to
154
+ Abel-Ruffini theorem.
155
+
156
+ If there is no radical solution is found for the eigenvalue,
157
+ it may return eigenvalues in the form of
158
+ :class:`sympy.polys.rootoftools.ComplexRootOf`.
159
+ """
160
+ if not M:
161
+ if multiple:
162
+ return []
163
+ return {}
164
+
165
+ if not M.is_square:
166
+ raise NonSquareMatrixError("{} must be a square matrix.".format(M))
167
+
168
+ if M._rep.domain not in (ZZ, QQ):
169
+ # Skip this check for ZZ/QQ because it can be slow
170
+ if all(x.is_number for x in M) and M.has(Float):
171
+ return _eigenvals_mpmath(M, multiple=multiple)
172
+
173
+ if rational:
174
+ from sympy.simplify import nsimplify
175
+ M = M.applyfunc(
176
+ lambda x: nsimplify(x, rational=True) if x.has(Float) else x)
177
+
178
+ if multiple:
179
+ return _eigenvals_list(
180
+ M, error_when_incomplete=error_when_incomplete, simplify=simplify,
181
+ **flags)
182
+ return _eigenvals_dict(
183
+ M, error_when_incomplete=error_when_incomplete, simplify=simplify,
184
+ **flags)
185
+
186
+
187
+ eigenvals_error_message = \
188
+ "It is not always possible to express the eigenvalues of a matrix " + \
189
+ "of size 5x5 or higher in radicals. " + \
190
+ "We have CRootOf, but domains other than the rationals are not " + \
191
+ "currently supported. " + \
192
+ "If there are no symbols in the matrix, " + \
193
+ "it should still be possible to compute numeric approximations " + \
194
+ "of the eigenvalues using " + \
195
+ "M.evalf().eigenvals() or M.charpoly().nroots()."
196
+
197
+
198
+ def _eigenvals_list(
199
+ M, error_when_incomplete=True, simplify=False, **flags):
200
+ iblocks = M.strongly_connected_components()
201
+ all_eigs = []
202
+ is_dom = M._rep.domain in (ZZ, QQ)
203
+ for b in iblocks:
204
+
205
+ # Fast path for a 1x1 block:
206
+ if is_dom and len(b) == 1:
207
+ index = b[0]
208
+ val = M[index, index]
209
+ all_eigs.append(val)
210
+ continue
211
+
212
+ block = M[b, b]
213
+
214
+ if isinstance(simplify, FunctionType):
215
+ charpoly = block.charpoly(simplify=simplify)
216
+ else:
217
+ charpoly = block.charpoly()
218
+
219
+ eigs = roots(charpoly, multiple=True, **flags)
220
+
221
+ if len(eigs) != block.rows:
222
+ try:
223
+ eigs = charpoly.all_roots(multiple=True)
224
+ except NotImplementedError:
225
+ if error_when_incomplete:
226
+ raise MatrixError(eigenvals_error_message)
227
+ else:
228
+ eigs = []
229
+
230
+ all_eigs += eigs
231
+
232
+ if not simplify:
233
+ return all_eigs
234
+ if not isinstance(simplify, FunctionType):
235
+ simplify = _simplify
236
+ return [simplify(value) for value in all_eigs]
237
+
238
+
239
+ def _eigenvals_dict(
240
+ M, error_when_incomplete=True, simplify=False, **flags):
241
+ iblocks = M.strongly_connected_components()
242
+ all_eigs = {}
243
+ is_dom = M._rep.domain in (ZZ, QQ)
244
+ for b in iblocks:
245
+
246
+ # Fast path for a 1x1 block:
247
+ if is_dom and len(b) == 1:
248
+ index = b[0]
249
+ val = M[index, index]
250
+ all_eigs[val] = all_eigs.get(val, 0) + 1
251
+ continue
252
+
253
+ block = M[b, b]
254
+
255
+ if isinstance(simplify, FunctionType):
256
+ charpoly = block.charpoly(simplify=simplify)
257
+ else:
258
+ charpoly = block.charpoly()
259
+
260
+ eigs = roots(charpoly, multiple=False, **flags)
261
+
262
+ if sum(eigs.values()) != block.rows:
263
+ try:
264
+ eigs = dict(charpoly.all_roots(multiple=False))
265
+ except NotImplementedError:
266
+ if error_when_incomplete:
267
+ raise MatrixError(eigenvals_error_message)
268
+ else:
269
+ eigs = {}
270
+
271
+ for k, v in eigs.items():
272
+ if k in all_eigs:
273
+ all_eigs[k] += v
274
+ else:
275
+ all_eigs[k] = v
276
+
277
+ if not simplify:
278
+ return all_eigs
279
+ if not isinstance(simplify, FunctionType):
280
+ simplify = _simplify
281
+ return {simplify(key): value for key, value in all_eigs.items()}
282
+
283
+
284
+ def _eigenspace(M, eigenval, iszerofunc=_iszero, simplify=False):
285
+ """Get a basis for the eigenspace for a particular eigenvalue"""
286
+ m = M - M.eye(M.rows) * eigenval
287
+ ret = m.nullspace(iszerofunc=iszerofunc)
288
+
289
+ # The nullspace for a real eigenvalue should be non-trivial.
290
+ # If we didn't find an eigenvector, try once more a little harder
291
+ if len(ret) == 0 and simplify:
292
+ ret = m.nullspace(iszerofunc=iszerofunc, simplify=True)
293
+ if len(ret) == 0:
294
+ raise NotImplementedError(
295
+ "Can't evaluate eigenvector for eigenvalue {}".format(eigenval))
296
+ return ret
297
+
298
+
299
+ def _eigenvects_DOM(M, **kwargs):
300
+ DOM = DomainMatrix.from_Matrix(M, field=True, extension=True)
301
+ DOM = DOM.to_dense()
302
+
303
+ if DOM.domain != EX:
304
+ rational, algebraic = dom_eigenvects(DOM)
305
+ eigenvects = dom_eigenvects_to_sympy(
306
+ rational, algebraic, M.__class__, **kwargs)
307
+ eigenvects = sorted(eigenvects, key=lambda x: default_sort_key(x[0]))
308
+
309
+ return eigenvects
310
+ return None
311
+
312
+
313
+ def _eigenvects_sympy(M, iszerofunc, simplify=True, **flags):
314
+ eigenvals = M.eigenvals(rational=False, **flags)
315
+
316
+ # Make sure that we have all roots in radical form
317
+ for x in eigenvals:
318
+ if x.has(CRootOf):
319
+ raise MatrixError(
320
+ "Eigenvector computation is not implemented if the matrix have "
321
+ "eigenvalues in CRootOf form")
322
+
323
+ eigenvals = sorted(eigenvals.items(), key=default_sort_key)
324
+ ret = []
325
+ for val, mult in eigenvals:
326
+ vects = _eigenspace(M, val, iszerofunc=iszerofunc, simplify=simplify)
327
+ ret.append((val, mult, vects))
328
+ return ret
329
+
330
+
331
+ # This functions is a candidate for caching if it gets implemented for matrices.
332
+ def _eigenvects(M, error_when_incomplete=True, iszerofunc=_iszero, *, chop=False, **flags):
333
+ """Compute eigenvectors of the matrix.
334
+
335
+ Parameters
336
+ ==========
337
+
338
+ error_when_incomplete : bool, optional
339
+ Raise an error when not all eigenvalues are computed. This is
340
+ caused by ``roots`` not returning a full list of eigenvalues.
341
+
342
+ iszerofunc : function, optional
343
+ Specifies a zero testing function to be used in ``rref``.
344
+
345
+ Default value is ``_iszero``, which uses SymPy's naive and fast
346
+ default assumption handler.
347
+
348
+ It can also accept any user-specified zero testing function, if it
349
+ is formatted as a function which accepts a single symbolic argument
350
+ and returns ``True`` if it is tested as zero and ``False`` if it
351
+ is tested as non-zero, and ``None`` if it is undecidable.
352
+
353
+ simplify : bool or function, optional
354
+ If ``True``, ``as_content_primitive()`` will be used to tidy up
355
+ normalization artifacts.
356
+
357
+ It will also be used by the ``nullspace`` routine.
358
+
359
+ chop : bool or positive number, optional
360
+ If the matrix contains any Floats, they will be changed to Rationals
361
+ for computation purposes, but the answers will be returned after
362
+ being evaluated with evalf. The ``chop`` flag is passed to ``evalf``.
363
+ When ``chop=True`` a default precision will be used; a number will
364
+ be interpreted as the desired level of precision.
365
+
366
+ Returns
367
+ =======
368
+
369
+ ret : [(eigenval, multiplicity, eigenspace), ...]
370
+ A ragged list containing tuples of data obtained by ``eigenvals``
371
+ and ``nullspace``.
372
+
373
+ ``eigenspace`` is a list containing the ``eigenvector`` for each
374
+ eigenvalue.
375
+
376
+ ``eigenvector`` is a vector in the form of a ``Matrix``. e.g.
377
+ a vector of length 3 is returned as ``Matrix([a_1, a_2, a_3])``.
378
+
379
+ Raises
380
+ ======
381
+
382
+ NotImplementedError
383
+ If failed to compute nullspace.
384
+
385
+ Examples
386
+ ========
387
+
388
+ >>> from sympy import Matrix
389
+ >>> M = Matrix(3, 3, [0, 1, 1, 1, 0, 0, 1, 1, 1])
390
+ >>> M.eigenvects()
391
+ [(-1, 1, [Matrix([
392
+ [-1],
393
+ [ 1],
394
+ [ 0]])]), (0, 1, [Matrix([
395
+ [ 0],
396
+ [-1],
397
+ [ 1]])]), (2, 1, [Matrix([
398
+ [2/3],
399
+ [1/3],
400
+ [ 1]])])]
401
+
402
+ See Also
403
+ ========
404
+
405
+ eigenvals
406
+ MatrixBase.nullspace
407
+ """
408
+ simplify = flags.get('simplify', True)
409
+ primitive = flags.get('simplify', False)
410
+ flags.pop('simplify', None) # remove this if it's there
411
+ flags.pop('multiple', None) # remove this if it's there
412
+
413
+ if not isinstance(simplify, FunctionType):
414
+ simpfunc = _simplify if simplify else lambda x: x
415
+
416
+ has_floats = M.has(Float)
417
+ if has_floats:
418
+ if all(x.is_number for x in M):
419
+ return _eigenvects_mpmath(M)
420
+ from sympy.simplify import nsimplify
421
+ M = M.applyfunc(lambda x: nsimplify(x, rational=True))
422
+
423
+ ret = _eigenvects_DOM(M)
424
+ if ret is None:
425
+ ret = _eigenvects_sympy(M, iszerofunc, simplify=simplify, **flags)
426
+
427
+ if primitive:
428
+ # if the primitive flag is set, get rid of any common
429
+ # integer denominators
430
+ def denom_clean(l):
431
+ return [(v / gcd(list(v))).applyfunc(simpfunc) for v in l]
432
+
433
+ ret = [(val, mult, denom_clean(es)) for val, mult, es in ret]
434
+
435
+ if has_floats:
436
+ # if we had floats to start with, turn the eigenvectors to floats
437
+ ret = [(val.evalf(chop=chop), mult, [v.evalf(chop=chop) for v in es])
438
+ for val, mult, es in ret]
439
+
440
+ return ret
441
+
442
+
443
+ def _is_diagonalizable_with_eigen(M, reals_only=False):
444
+ """See _is_diagonalizable. This function returns the bool along with the
445
+ eigenvectors to avoid calculating them again in functions like
446
+ ``diagonalize``."""
447
+
448
+ if not M.is_square:
449
+ return False, []
450
+
451
+ eigenvecs = M.eigenvects(simplify=True)
452
+
453
+ for val, mult, basis in eigenvecs:
454
+ if reals_only and not val.is_real: # if we have a complex eigenvalue
455
+ return False, eigenvecs
456
+
457
+ if mult != len(basis): # if the geometric multiplicity doesn't equal the algebraic
458
+ return False, eigenvecs
459
+
460
+ return True, eigenvecs
461
+
462
+ def _is_diagonalizable(M, reals_only=False, **kwargs):
463
+ """Returns ``True`` if a matrix is diagonalizable.
464
+
465
+ Parameters
466
+ ==========
467
+
468
+ reals_only : bool, optional
469
+ If ``True``, it tests whether the matrix can be diagonalized
470
+ to contain only real numbers on the diagonal.
471
+
472
+
473
+ If ``False``, it tests whether the matrix can be diagonalized
474
+ at all, even with numbers that may not be real.
475
+
476
+ Examples
477
+ ========
478
+
479
+ Example of a diagonalizable matrix:
480
+
481
+ >>> from sympy import Matrix
482
+ >>> M = Matrix([[1, 2, 0], [0, 3, 0], [2, -4, 2]])
483
+ >>> M.is_diagonalizable()
484
+ True
485
+
486
+ Example of a non-diagonalizable matrix:
487
+
488
+ >>> M = Matrix([[0, 1], [0, 0]])
489
+ >>> M.is_diagonalizable()
490
+ False
491
+
492
+ Example of a matrix that is diagonalized in terms of non-real entries:
493
+
494
+ >>> M = Matrix([[0, 1], [-1, 0]])
495
+ >>> M.is_diagonalizable(reals_only=False)
496
+ True
497
+ >>> M.is_diagonalizable(reals_only=True)
498
+ False
499
+
500
+ See Also
501
+ ========
502
+
503
+ sympy.matrices.matrixbase.MatrixBase.is_diagonal
504
+ diagonalize
505
+ """
506
+ if not M.is_square:
507
+ return False
508
+
509
+ if all(e.is_real for e in M) and M.is_symmetric():
510
+ return True
511
+
512
+ if all(e.is_complex for e in M) and M.is_hermitian:
513
+ return True
514
+
515
+ return _is_diagonalizable_with_eigen(M, reals_only=reals_only)[0]
516
+
517
+
518
+ #G&VL, Matrix Computations, Algo 5.4.2
519
+ def _householder_vector(x):
520
+ if not x.cols == 1:
521
+ raise ValueError("Input must be a column matrix")
522
+ v = x.copy()
523
+ v_plus = x.copy()
524
+ v_minus = x.copy()
525
+ q = x[0, 0] / abs(x[0, 0])
526
+ norm_x = x.norm()
527
+ v_plus[0, 0] = x[0, 0] + q * norm_x
528
+ v_minus[0, 0] = x[0, 0] - q * norm_x
529
+ if x[1:, 0].norm() == 0:
530
+ bet = 0
531
+ v[0, 0] = 1
532
+ else:
533
+ if v_plus.norm() <= v_minus.norm():
534
+ v = v_plus
535
+ else:
536
+ v = v_minus
537
+ v = v / v[0]
538
+ bet = 2 / (v.norm() ** 2)
539
+ return v, bet
540
+
541
+
542
+ def _bidiagonal_decmp_hholder(M):
543
+ m = M.rows
544
+ n = M.cols
545
+ A = M.as_mutable()
546
+ U, V = A.eye(m), A.eye(n)
547
+ for i in range(min(m, n)):
548
+ v, bet = _householder_vector(A[i:, i])
549
+ hh_mat = A.eye(m - i) - bet * v * v.H
550
+ A[i:, i:] = hh_mat * A[i:, i:]
551
+ temp = A.eye(m)
552
+ temp[i:, i:] = hh_mat
553
+ U = U * temp
554
+ if i + 1 <= n - 2:
555
+ v, bet = _householder_vector(A[i, i+1:].T)
556
+ hh_mat = A.eye(n - i - 1) - bet * v * v.H
557
+ A[i:, i+1:] = A[i:, i+1:] * hh_mat
558
+ temp = A.eye(n)
559
+ temp[i+1:, i+1:] = hh_mat
560
+ V = temp * V
561
+ return U, A, V
562
+
563
+
564
+ def _eval_bidiag_hholder(M):
565
+ m = M.rows
566
+ n = M.cols
567
+ A = M.as_mutable()
568
+ for i in range(min(m, n)):
569
+ v, bet = _householder_vector(A[i:, i])
570
+ hh_mat = A.eye(m-i) - bet * v * v.H
571
+ A[i:, i:] = hh_mat * A[i:, i:]
572
+ if i + 1 <= n - 2:
573
+ v, bet = _householder_vector(A[i, i+1:].T)
574
+ hh_mat = A.eye(n - i - 1) - bet * v * v.H
575
+ A[i:, i+1:] = A[i:, i+1:] * hh_mat
576
+ return A
577
+
578
+
579
+ def _bidiagonal_decomposition(M, upper=True):
580
+ """
581
+ Returns $(U,B,V.H)$ for
582
+
583
+ $$A = UBV^{H}$$
584
+
585
+ where $A$ is the input matrix, and $B$ is its Bidiagonalized form
586
+
587
+ Note: Bidiagonal Computation can hang for symbolic matrices.
588
+
589
+ Parameters
590
+ ==========
591
+
592
+ upper : bool. Whether to do upper bidiagnalization or lower.
593
+ True for upper and False for lower.
594
+
595
+ References
596
+ ==========
597
+
598
+ .. [1] Algorithm 5.4.2, Matrix computations by Golub and Van Loan, 4th edition
599
+ .. [2] Complex Matrix Bidiagonalization, https://github.com/vslobody/Householder-Bidiagonalization
600
+
601
+ """
602
+
603
+ if not isinstance(upper, bool):
604
+ raise ValueError("upper must be a boolean")
605
+
606
+ if upper:
607
+ return _bidiagonal_decmp_hholder(M)
608
+
609
+ X = _bidiagonal_decmp_hholder(M.H)
610
+ return X[2].H, X[1].H, X[0].H
611
+
612
+
613
+ def _bidiagonalize(M, upper=True):
614
+ """
615
+ Returns $B$, the Bidiagonalized form of the input matrix.
616
+
617
+ Note: Bidiagonal Computation can hang for symbolic matrices.
618
+
619
+ Parameters
620
+ ==========
621
+
622
+ upper : bool. Whether to do upper bidiagnalization or lower.
623
+ True for upper and False for lower.
624
+
625
+ References
626
+ ==========
627
+
628
+ .. [1] Algorithm 5.4.2, Matrix computations by Golub and Van Loan, 4th edition
629
+ .. [2] Complex Matrix Bidiagonalization : https://github.com/vslobody/Householder-Bidiagonalization
630
+
631
+ """
632
+
633
+ if not isinstance(upper, bool):
634
+ raise ValueError("upper must be a boolean")
635
+
636
+ if upper:
637
+ return _eval_bidiag_hholder(M)
638
+ return _eval_bidiag_hholder(M.H).H
639
+
640
+
641
+ def _diagonalize(M, reals_only=False, sort=False, normalize=False):
642
+ """
643
+ Return (P, D), where D is diagonal and
644
+
645
+ D = P^-1 * M * P
646
+
647
+ where M is current matrix.
648
+
649
+ Parameters
650
+ ==========
651
+
652
+ reals_only : bool. Whether to throw an error if complex numbers are need
653
+ to diagonalize. (Default: False)
654
+
655
+ sort : bool. Sort the eigenvalues along the diagonal. (Default: False)
656
+
657
+ normalize : bool. If True, normalize the columns of P. (Default: False)
658
+
659
+ Examples
660
+ ========
661
+
662
+ >>> from sympy import Matrix
663
+ >>> M = Matrix(3, 3, [1, 2, 0, 0, 3, 0, 2, -4, 2])
664
+ >>> M
665
+ Matrix([
666
+ [1, 2, 0],
667
+ [0, 3, 0],
668
+ [2, -4, 2]])
669
+ >>> (P, D) = M.diagonalize()
670
+ >>> D
671
+ Matrix([
672
+ [1, 0, 0],
673
+ [0, 2, 0],
674
+ [0, 0, 3]])
675
+ >>> P
676
+ Matrix([
677
+ [-1, 0, -1],
678
+ [ 0, 0, -1],
679
+ [ 2, 1, 2]])
680
+ >>> P.inv() * M * P
681
+ Matrix([
682
+ [1, 0, 0],
683
+ [0, 2, 0],
684
+ [0, 0, 3]])
685
+
686
+ See Also
687
+ ========
688
+
689
+ sympy.matrices.matrixbase.MatrixBase.is_diagonal
690
+ is_diagonalizable
691
+ """
692
+
693
+ if not M.is_square:
694
+ raise NonSquareMatrixError()
695
+
696
+ is_diagonalizable, eigenvecs = _is_diagonalizable_with_eigen(M,
697
+ reals_only=reals_only)
698
+
699
+ if not is_diagonalizable:
700
+ raise MatrixError("Matrix is not diagonalizable")
701
+
702
+ if sort:
703
+ eigenvecs = sorted(eigenvecs, key=default_sort_key)
704
+
705
+ p_cols, diag = [], []
706
+
707
+ for val, mult, basis in eigenvecs:
708
+ diag += [val] * mult
709
+ p_cols += basis
710
+
711
+ if normalize:
712
+ p_cols = [v / v.norm() for v in p_cols]
713
+
714
+ return M.hstack(*p_cols), M.diag(*diag)
715
+
716
+
717
+ def _fuzzy_positive_definite(M):
718
+ positive_diagonals = M._has_positive_diagonals()
719
+ if positive_diagonals is False:
720
+ return False
721
+
722
+ if positive_diagonals and M.is_strongly_diagonally_dominant:
723
+ return True
724
+
725
+ return None
726
+
727
+
728
+ def _fuzzy_positive_semidefinite(M):
729
+ nonnegative_diagonals = M._has_nonnegative_diagonals()
730
+ if nonnegative_diagonals is False:
731
+ return False
732
+
733
+ if nonnegative_diagonals and M.is_weakly_diagonally_dominant:
734
+ return True
735
+
736
+ return None
737
+
738
+
739
+ def _is_positive_definite(M):
740
+ if not M.is_hermitian:
741
+ if not M.is_square:
742
+ return False
743
+ M = M + M.H
744
+
745
+ fuzzy = _fuzzy_positive_definite(M)
746
+ if fuzzy is not None:
747
+ return fuzzy
748
+
749
+ return _is_positive_definite_GE(M)
750
+
751
+
752
+ def _is_positive_semidefinite(M):
753
+ if not M.is_hermitian:
754
+ if not M.is_square:
755
+ return False
756
+ M = M + M.H
757
+
758
+ fuzzy = _fuzzy_positive_semidefinite(M)
759
+ if fuzzy is not None:
760
+ return fuzzy
761
+
762
+ return _is_positive_semidefinite_cholesky(M)
763
+
764
+
765
+ def _is_negative_definite(M):
766
+ return _is_positive_definite(-M)
767
+
768
+
769
+ def _is_negative_semidefinite(M):
770
+ return _is_positive_semidefinite(-M)
771
+
772
+
773
+ def _is_indefinite(M):
774
+ if M.is_hermitian:
775
+ eigen = M.eigenvals()
776
+ args1 = [x.is_positive for x in eigen.keys()]
777
+ any_positive = fuzzy_or(args1)
778
+ args2 = [x.is_negative for x in eigen.keys()]
779
+ any_negative = fuzzy_or(args2)
780
+
781
+ return fuzzy_and([any_positive, any_negative])
782
+
783
+ elif M.is_square:
784
+ return (M + M.H).is_indefinite
785
+
786
+ return False
787
+
788
+
789
+ def _is_positive_definite_GE(M):
790
+ """A division-free gaussian elimination method for testing
791
+ positive-definiteness."""
792
+ M = M.as_mutable()
793
+ size = M.rows
794
+
795
+ for i in range(size):
796
+ is_positive = M[i, i].is_positive
797
+ if is_positive is not True:
798
+ return is_positive
799
+ for j in range(i+1, size):
800
+ M[j, i+1:] = M[i, i] * M[j, i+1:] - M[j, i] * M[i, i+1:]
801
+ return True
802
+
803
+
804
+ def _is_positive_semidefinite_cholesky(M):
805
+ """Uses Cholesky factorization with complete pivoting
806
+
807
+ References
808
+ ==========
809
+
810
+ .. [1] http://eprints.ma.man.ac.uk/1199/1/covered/MIMS_ep2008_116.pdf
811
+
812
+ .. [2] https://www.value-at-risk.net/cholesky-factorization/
813
+ """
814
+ M = M.as_mutable()
815
+ for k in range(M.rows):
816
+ diags = [M[i, i] for i in range(k, M.rows)]
817
+ pivot, pivot_val, nonzero, _ = _find_reasonable_pivot(diags)
818
+
819
+ if nonzero:
820
+ return None
821
+
822
+ if pivot is None:
823
+ for i in range(k+1, M.rows):
824
+ for j in range(k, M.cols):
825
+ iszero = M[i, j].is_zero
826
+ if iszero is None:
827
+ return None
828
+ elif iszero is False:
829
+ return False
830
+ return True
831
+
832
+ if M[k, k].is_negative or pivot_val.is_negative:
833
+ return False
834
+ elif not (M[k, k].is_nonnegative and pivot_val.is_nonnegative):
835
+ return None
836
+
837
+ if pivot > 0:
838
+ M.col_swap(k, k+pivot)
839
+ M.row_swap(k, k+pivot)
840
+
841
+ M[k, k] = sqrt(M[k, k])
842
+ M[k, k+1:] /= M[k, k]
843
+ M[k+1:, k+1:] -= M[k, k+1:].H * M[k, k+1:]
844
+
845
+ return M[-1, -1].is_nonnegative
846
+
847
+
848
+ _doc_positive_definite = \
849
+ r"""Finds out the definiteness of a matrix.
850
+
851
+ Explanation
852
+ ===========
853
+
854
+ A square real matrix $A$ is:
855
+
856
+ - A positive definite matrix if $x^T A x > 0$
857
+ for all non-zero real vectors $x$.
858
+ - A positive semidefinite matrix if $x^T A x \geq 0$
859
+ for all non-zero real vectors $x$.
860
+ - A negative definite matrix if $x^T A x < 0$
861
+ for all non-zero real vectors $x$.
862
+ - A negative semidefinite matrix if $x^T A x \leq 0$
863
+ for all non-zero real vectors $x$.
864
+ - An indefinite matrix if there exists non-zero real vectors
865
+ $x, y$ with $x^T A x > 0 > y^T A y$.
866
+
867
+ A square complex matrix $A$ is:
868
+
869
+ - A positive definite matrix if $\text{re}(x^H A x) > 0$
870
+ for all non-zero complex vectors $x$.
871
+ - A positive semidefinite matrix if $\text{re}(x^H A x) \geq 0$
872
+ for all non-zero complex vectors $x$.
873
+ - A negative definite matrix if $\text{re}(x^H A x) < 0$
874
+ for all non-zero complex vectors $x$.
875
+ - A negative semidefinite matrix if $\text{re}(x^H A x) \leq 0$
876
+ for all non-zero complex vectors $x$.
877
+ - An indefinite matrix if there exists non-zero complex vectors
878
+ $x, y$ with $\text{re}(x^H A x) > 0 > \text{re}(y^H A y)$.
879
+
880
+ A matrix need not be symmetric or hermitian to be positive definite.
881
+
882
+ - A real non-symmetric matrix is positive definite if and only if
883
+ $\frac{A + A^T}{2}$ is positive definite.
884
+ - A complex non-hermitian matrix is positive definite if and only if
885
+ $\frac{A + A^H}{2}$ is positive definite.
886
+
887
+ And this extension can apply for all the definitions above.
888
+
889
+ However, for complex cases, you can restrict the definition of
890
+ $\text{re}(x^H A x) > 0$ to $x^H A x > 0$ and require the matrix
891
+ to be hermitian.
892
+ But we do not present this restriction for computation because you
893
+ can check ``M.is_hermitian`` independently with this and use
894
+ the same procedure.
895
+
896
+ Examples
897
+ ========
898
+
899
+ An example of symmetric positive definite matrix:
900
+
901
+ .. plot::
902
+ :context: reset
903
+ :format: doctest
904
+ :include-source: True
905
+
906
+ >>> from sympy import Matrix, symbols
907
+ >>> from sympy.plotting import plot3d
908
+ >>> a, b = symbols('a b')
909
+ >>> x = Matrix([a, b])
910
+
911
+ >>> A = Matrix([[1, 0], [0, 1]])
912
+ >>> A.is_positive_definite
913
+ True
914
+ >>> A.is_positive_semidefinite
915
+ True
916
+
917
+ >>> p = plot3d((x.T*A*x)[0, 0], (a, -1, 1), (b, -1, 1))
918
+
919
+ An example of symmetric positive semidefinite matrix:
920
+
921
+ .. plot::
922
+ :context: close-figs
923
+ :format: doctest
924
+ :include-source: True
925
+
926
+ >>> A = Matrix([[1, -1], [-1, 1]])
927
+ >>> A.is_positive_definite
928
+ False
929
+ >>> A.is_positive_semidefinite
930
+ True
931
+
932
+ >>> p = plot3d((x.T*A*x)[0, 0], (a, -1, 1), (b, -1, 1))
933
+
934
+ An example of symmetric negative definite matrix:
935
+
936
+ .. plot::
937
+ :context: close-figs
938
+ :format: doctest
939
+ :include-source: True
940
+
941
+ >>> A = Matrix([[-1, 0], [0, -1]])
942
+ >>> A.is_negative_definite
943
+ True
944
+ >>> A.is_negative_semidefinite
945
+ True
946
+ >>> A.is_indefinite
947
+ False
948
+
949
+ >>> p = plot3d((x.T*A*x)[0, 0], (a, -1, 1), (b, -1, 1))
950
+
951
+ An example of symmetric indefinite matrix:
952
+
953
+ .. plot::
954
+ :context: close-figs
955
+ :format: doctest
956
+ :include-source: True
957
+
958
+ >>> A = Matrix([[1, 2], [2, -1]])
959
+ >>> A.is_indefinite
960
+ True
961
+
962
+ >>> p = plot3d((x.T*A*x)[0, 0], (a, -1, 1), (b, -1, 1))
963
+
964
+ An example of non-symmetric positive definite matrix.
965
+
966
+ .. plot::
967
+ :context: close-figs
968
+ :format: doctest
969
+ :include-source: True
970
+
971
+ >>> A = Matrix([[1, 2], [-2, 1]])
972
+ >>> A.is_positive_definite
973
+ True
974
+ >>> A.is_positive_semidefinite
975
+ True
976
+
977
+ >>> p = plot3d((x.T*A*x)[0, 0], (a, -1, 1), (b, -1, 1))
978
+
979
+ Notes
980
+ =====
981
+
982
+ Although some people trivialize the definition of positive definite
983
+ matrices only for symmetric or hermitian matrices, this restriction
984
+ is not correct because it does not classify all instances of
985
+ positive definite matrices from the definition $x^T A x > 0$ or
986
+ $\text{re}(x^H A x) > 0$.
987
+
988
+ For instance, ``Matrix([[1, 2], [-2, 1]])`` presented in
989
+ the example above is an example of real positive definite matrix
990
+ that is not symmetric.
991
+
992
+ However, since the following formula holds true;
993
+
994
+ .. math::
995
+ \text{re}(x^H A x) > 0 \iff
996
+ \text{re}(x^H \frac{A + A^H}{2} x) > 0
997
+
998
+ We can classify all positive definite matrices that may or may not
999
+ be symmetric or hermitian by transforming the matrix to
1000
+ $\frac{A + A^T}{2}$ or $\frac{A + A^H}{2}$
1001
+ (which is guaranteed to be always real symmetric or complex
1002
+ hermitian) and we can defer most of the studies to symmetric or
1003
+ hermitian positive definite matrices.
1004
+
1005
+ But it is a different problem for the existence of Cholesky
1006
+ decomposition. Because even though a non symmetric or a non
1007
+ hermitian matrix can be positive definite, Cholesky or LDL
1008
+ decomposition does not exist because the decompositions require the
1009
+ matrix to be symmetric or hermitian.
1010
+
1011
+ References
1012
+ ==========
1013
+
1014
+ .. [1] https://en.wikipedia.org/wiki/Definiteness_of_a_matrix#Eigenvalues
1015
+
1016
+ .. [2] https://mathworld.wolfram.com/PositiveDefiniteMatrix.html
1017
+
1018
+ .. [3] Johnson, C. R. "Positive Definite Matrices." Amer.
1019
+ Math. Monthly 77, 259-264 1970.
1020
+ """
1021
+
1022
+ _is_positive_definite.__doc__ = _doc_positive_definite
1023
+ _is_positive_semidefinite.__doc__ = _doc_positive_definite
1024
+ _is_negative_definite.__doc__ = _doc_positive_definite
1025
+ _is_negative_semidefinite.__doc__ = _doc_positive_definite
1026
+ _is_indefinite.__doc__ = _doc_positive_definite
1027
+
1028
+
1029
+ def _jordan_form(M, calc_transform=True, *, chop=False):
1030
+ """Return $(P, J)$ where $J$ is a Jordan block
1031
+ matrix and $P$ is a matrix such that $M = P J P^{-1}$
1032
+
1033
+ Parameters
1034
+ ==========
1035
+
1036
+ calc_transform : bool
1037
+ If ``False``, then only $J$ is returned.
1038
+
1039
+ chop : bool
1040
+ All matrices are converted to exact types when computing
1041
+ eigenvalues and eigenvectors. As a result, there may be
1042
+ approximation errors. If ``chop==True``, these errors
1043
+ will be truncated.
1044
+
1045
+ Examples
1046
+ ========
1047
+
1048
+ >>> from sympy import Matrix
1049
+ >>> M = Matrix([[ 6, 5, -2, -3], [-3, -1, 3, 3], [ 2, 1, -2, -3], [-1, 1, 5, 5]])
1050
+ >>> P, J = M.jordan_form()
1051
+ >>> J
1052
+ Matrix([
1053
+ [2, 1, 0, 0],
1054
+ [0, 2, 0, 0],
1055
+ [0, 0, 2, 1],
1056
+ [0, 0, 0, 2]])
1057
+
1058
+ See Also
1059
+ ========
1060
+
1061
+ jordan_block
1062
+ """
1063
+
1064
+ if not M.is_square:
1065
+ raise NonSquareMatrixError("Only square matrices have Jordan forms")
1066
+
1067
+ mat = M
1068
+ has_floats = M.has(Float)
1069
+
1070
+ if has_floats:
1071
+ try:
1072
+ max_prec = max(term._prec for term in M.values() if isinstance(term, Float))
1073
+ except ValueError:
1074
+ # if no term in the matrix is explicitly a Float calling max()
1075
+ # will throw a error so setting max_prec to default value of 53
1076
+ max_prec = 53
1077
+
1078
+ # setting minimum max_dps to 15 to prevent loss of precision in
1079
+ # matrix containing non evaluated expressions
1080
+ max_dps = max(prec_to_dps(max_prec), 15)
1081
+
1082
+ def restore_floats(*args):
1083
+ """If ``has_floats`` is `True`, cast all ``args`` as
1084
+ matrices of floats."""
1085
+
1086
+ if has_floats:
1087
+ args = [m.evalf(n=max_dps, chop=chop) for m in args]
1088
+ if len(args) == 1:
1089
+ return args[0]
1090
+
1091
+ return args
1092
+
1093
+ # cache calculations for some speedup
1094
+ mat_cache = {}
1095
+
1096
+ def eig_mat(val, pow):
1097
+ """Cache computations of ``(M - val*I)**pow`` for quick
1098
+ retrieval"""
1099
+
1100
+ if (val, pow) in mat_cache:
1101
+ return mat_cache[(val, pow)]
1102
+
1103
+ if (val, pow - 1) in mat_cache:
1104
+ mat_cache[(val, pow)] = mat_cache[(val, pow - 1)].multiply(
1105
+ mat_cache[(val, 1)], dotprodsimp=None)
1106
+ else:
1107
+ mat_cache[(val, pow)] = (mat - val*M.eye(M.rows)).pow(pow)
1108
+
1109
+ return mat_cache[(val, pow)]
1110
+
1111
+ # helper functions
1112
+ def nullity_chain(val, algebraic_multiplicity):
1113
+ """Calculate the sequence [0, nullity(E), nullity(E**2), ...]
1114
+ until it is constant where ``E = M - val*I``"""
1115
+
1116
+ # mat.rank() is faster than computing the null space,
1117
+ # so use the rank-nullity theorem
1118
+ cols = M.cols
1119
+ ret = [0]
1120
+ nullity = cols - eig_mat(val, 1).rank()
1121
+ i = 2
1122
+
1123
+ while nullity != ret[-1]:
1124
+ ret.append(nullity)
1125
+
1126
+ if nullity == algebraic_multiplicity:
1127
+ break
1128
+
1129
+ nullity = cols - eig_mat(val, i).rank()
1130
+ i += 1
1131
+
1132
+ # Due to issues like #7146 and #15872, SymPy sometimes
1133
+ # gives the wrong rank. In this case, raise an error
1134
+ # instead of returning an incorrect matrix
1135
+ if nullity < ret[-1] or nullity > algebraic_multiplicity:
1136
+ raise MatrixError(
1137
+ "SymPy had encountered an inconsistent "
1138
+ "result while computing Jordan block: "
1139
+ "{}".format(M))
1140
+
1141
+ return ret
1142
+
1143
+ def blocks_from_nullity_chain(d):
1144
+ """Return a list of the size of each Jordan block.
1145
+ If d_n is the nullity of E**n, then the number
1146
+ of Jordan blocks of size n is
1147
+
1148
+ 2*d_n - d_(n-1) - d_(n+1)"""
1149
+
1150
+ # d[0] is always the number of columns, so skip past it
1151
+ mid = [2*d[n] - d[n - 1] - d[n + 1] for n in range(1, len(d) - 1)]
1152
+ # d is assumed to plateau with "d[ len(d) ] == d[-1]", so
1153
+ # 2*d_n - d_(n-1) - d_(n+1) == d_n - d_(n-1)
1154
+ end = [d[-1] - d[-2]] if len(d) > 1 else [d[0]]
1155
+
1156
+ return mid + end
1157
+
1158
+ def pick_vec(small_basis, big_basis):
1159
+ """Picks a vector from big_basis that isn't in
1160
+ the subspace spanned by small_basis"""
1161
+
1162
+ if len(small_basis) == 0:
1163
+ return big_basis[0]
1164
+
1165
+ for v in big_basis:
1166
+ _, pivots = M.hstack(*(small_basis + [v])).echelon_form(
1167
+ with_pivots=True)
1168
+
1169
+ if pivots[-1] == len(small_basis):
1170
+ return v
1171
+
1172
+ # roots doesn't like Floats, so replace them with Rationals
1173
+ if has_floats:
1174
+ from sympy.simplify import nsimplify
1175
+ mat = mat.applyfunc(lambda x: nsimplify(x, rational=True))
1176
+
1177
+ # first calculate the jordan block structure
1178
+ eigs = mat.eigenvals()
1179
+
1180
+ # Make sure that we have all roots in radical form
1181
+ for x in eigs:
1182
+ if x.has(CRootOf):
1183
+ raise MatrixError(
1184
+ "Jordan normal form is not implemented if the matrix have "
1185
+ "eigenvalues in CRootOf form")
1186
+
1187
+ # most matrices have distinct eigenvalues
1188
+ # and so are diagonalizable. In this case, don't
1189
+ # do extra work!
1190
+ if len(eigs.keys()) == mat.cols:
1191
+ blocks = sorted(eigs.keys(), key=default_sort_key)
1192
+ jordan_mat = mat.diag(*blocks)
1193
+
1194
+ if not calc_transform:
1195
+ return restore_floats(jordan_mat)
1196
+
1197
+ jordan_basis = [eig_mat(eig, 1).nullspace()[0]
1198
+ for eig in blocks]
1199
+ basis_mat = mat.hstack(*jordan_basis)
1200
+
1201
+ return restore_floats(basis_mat, jordan_mat)
1202
+
1203
+ block_structure = []
1204
+
1205
+ for eig in sorted(eigs.keys(), key=default_sort_key):
1206
+ algebraic_multiplicity = eigs[eig]
1207
+ chain = nullity_chain(eig, algebraic_multiplicity)
1208
+ block_sizes = blocks_from_nullity_chain(chain)
1209
+
1210
+ # if block_sizes = = [a, b, c, ...], then the number of
1211
+ # Jordan blocks of size 1 is a, of size 2 is b, etc.
1212
+ # create an array that has (eig, block_size) with one
1213
+ # entry for each block
1214
+ size_nums = [(i+1, num) for i, num in enumerate(block_sizes)]
1215
+
1216
+ # we expect larger Jordan blocks to come earlier
1217
+ size_nums.reverse()
1218
+
1219
+ block_structure.extend(
1220
+ [(eig, size) for size, num in size_nums for _ in range(num)])
1221
+
1222
+ jordan_form_size = sum(size for eig, size in block_structure)
1223
+
1224
+ if jordan_form_size != M.rows:
1225
+ raise MatrixError(
1226
+ "SymPy had encountered an inconsistent result while "
1227
+ "computing Jordan block. : {}".format(M))
1228
+
1229
+ blocks = (mat.jordan_block(size=size, eigenvalue=eig) for eig, size in block_structure)
1230
+ jordan_mat = mat.diag(*blocks)
1231
+
1232
+ if not calc_transform:
1233
+ return restore_floats(jordan_mat)
1234
+
1235
+ # For each generalized eigenspace, calculate a basis.
1236
+ # We start by looking for a vector in null( (A - eig*I)**n )
1237
+ # which isn't in null( (A - eig*I)**(n-1) ) where n is
1238
+ # the size of the Jordan block
1239
+ #
1240
+ # Ideally we'd just loop through block_structure and
1241
+ # compute each generalized eigenspace. However, this
1242
+ # causes a lot of unneeded computation. Instead, we
1243
+ # go through the eigenvalues separately, since we know
1244
+ # their generalized eigenspaces must have bases that
1245
+ # are linearly independent.
1246
+ jordan_basis = []
1247
+
1248
+ for eig in sorted(eigs.keys(), key=default_sort_key):
1249
+ eig_basis = []
1250
+
1251
+ for block_eig, size in block_structure:
1252
+ if block_eig != eig:
1253
+ continue
1254
+
1255
+ null_big = (eig_mat(eig, size)).nullspace()
1256
+ null_small = (eig_mat(eig, size - 1)).nullspace()
1257
+
1258
+ # we want to pick something that is in the big basis
1259
+ # and not the small, but also something that is independent
1260
+ # of any other generalized eigenvectors from a different
1261
+ # generalized eigenspace sharing the same eigenvalue.
1262
+ vec = pick_vec(null_small + eig_basis, null_big)
1263
+ new_vecs = [eig_mat(eig, i).multiply(vec, dotprodsimp=None)
1264
+ for i in range(size)]
1265
+
1266
+ eig_basis.extend(new_vecs)
1267
+ jordan_basis.extend(reversed(new_vecs))
1268
+
1269
+ basis_mat = mat.hstack(*jordan_basis)
1270
+
1271
+ return restore_floats(basis_mat, jordan_mat)
1272
+
1273
+
1274
+ def _left_eigenvects(M, **flags):
1275
+ """Returns left eigenvectors and eigenvalues.
1276
+
1277
+ This function returns the list of triples (eigenval, multiplicity,
1278
+ basis) for the left eigenvectors. Options are the same as for
1279
+ eigenvects(), i.e. the ``**flags`` arguments gets passed directly to
1280
+ eigenvects().
1281
+
1282
+ Examples
1283
+ ========
1284
+
1285
+ >>> from sympy import Matrix
1286
+ >>> M = Matrix([[0, 1, 1], [1, 0, 0], [1, 1, 1]])
1287
+ >>> M.eigenvects()
1288
+ [(-1, 1, [Matrix([
1289
+ [-1],
1290
+ [ 1],
1291
+ [ 0]])]), (0, 1, [Matrix([
1292
+ [ 0],
1293
+ [-1],
1294
+ [ 1]])]), (2, 1, [Matrix([
1295
+ [2/3],
1296
+ [1/3],
1297
+ [ 1]])])]
1298
+ >>> M.left_eigenvects()
1299
+ [(-1, 1, [Matrix([[-2, 1, 1]])]), (0, 1, [Matrix([[-1, -1, 1]])]), (2,
1300
+ 1, [Matrix([[1, 1, 1]])])]
1301
+
1302
+ """
1303
+
1304
+ eigs = M.transpose().eigenvects(**flags)
1305
+
1306
+ return [(val, mult, [l.transpose() for l in basis]) for val, mult, basis in eigs]
1307
+
1308
+
1309
+ def _singular_values(M):
1310
+ """Compute the singular values of a Matrix
1311
+
1312
+ Examples
1313
+ ========
1314
+
1315
+ >>> from sympy import Matrix, Symbol
1316
+ >>> x = Symbol('x', real=True)
1317
+ >>> M = Matrix([[0, 1, 0], [0, x, 0], [-1, 0, 0]])
1318
+ >>> M.singular_values()
1319
+ [sqrt(x**2 + 1), 1, 0]
1320
+
1321
+ See Also
1322
+ ========
1323
+
1324
+ condition_number
1325
+ """
1326
+
1327
+ if M.rows >= M.cols:
1328
+ valmultpairs = M.H.multiply(M).eigenvals()
1329
+ else:
1330
+ valmultpairs = M.multiply(M.H).eigenvals()
1331
+
1332
+ # Expands result from eigenvals into a simple list
1333
+ vals = []
1334
+
1335
+ for k, v in valmultpairs.items():
1336
+ vals += [sqrt(k)] * v # dangerous! same k in several spots!
1337
+
1338
+ # Pad with zeros if singular values are computed in reverse way,
1339
+ # to give consistent format.
1340
+ if len(vals) < M.cols:
1341
+ vals += [M.zero] * (M.cols - len(vals))
1342
+
1343
+ # sort them in descending order
1344
+ vals.sort(reverse=True, key=default_sort_key)
1345
+
1346
+ return vals
.venv/lib/python3.11/site-packages/sympy/matrices/expressions/__init__.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """ A module which handles Matrix Expressions """
2
+
3
+ from .slice import MatrixSlice
4
+ from .blockmatrix import BlockMatrix, BlockDiagMatrix, block_collapse, blockcut
5
+ from .companion import CompanionMatrix
6
+ from .funcmatrix import FunctionMatrix
7
+ from .inverse import Inverse
8
+ from .matadd import MatAdd
9
+ from .matexpr import MatrixExpr, MatrixSymbol, matrix_symbols
10
+ from .matmul import MatMul
11
+ from .matpow import MatPow
12
+ from .trace import Trace, trace
13
+ from .determinant import Determinant, det, Permanent, per
14
+ from .transpose import Transpose
15
+ from .adjoint import Adjoint
16
+ from .hadamard import hadamard_product, HadamardProduct, hadamard_power, HadamardPower
17
+ from .diagonal import DiagonalMatrix, DiagonalOf, DiagMatrix, diagonalize_vector
18
+ from .dotproduct import DotProduct
19
+ from .kronecker import kronecker_product, KroneckerProduct, combine_kronecker
20
+ from .permutation import PermutationMatrix, MatrixPermute
21
+ from .sets import MatrixSet
22
+ from .special import ZeroMatrix, Identity, OneMatrix
23
+
24
+ __all__ = [
25
+ 'MatrixSlice',
26
+
27
+ 'BlockMatrix', 'BlockDiagMatrix', 'block_collapse', 'blockcut',
28
+ 'FunctionMatrix',
29
+
30
+ 'CompanionMatrix',
31
+
32
+ 'Inverse',
33
+
34
+ 'MatAdd',
35
+
36
+ 'Identity', 'MatrixExpr', 'MatrixSymbol', 'ZeroMatrix', 'OneMatrix',
37
+ 'matrix_symbols', 'MatrixSet',
38
+
39
+ 'MatMul',
40
+
41
+ 'MatPow',
42
+
43
+ 'Trace', 'trace',
44
+
45
+ 'Determinant', 'det',
46
+
47
+ 'Transpose',
48
+
49
+ 'Adjoint',
50
+
51
+ 'hadamard_product', 'HadamardProduct', 'hadamard_power', 'HadamardPower',
52
+
53
+ 'DiagonalMatrix', 'DiagonalOf', 'DiagMatrix', 'diagonalize_vector',
54
+
55
+ 'DotProduct',
56
+
57
+ 'kronecker_product', 'KroneckerProduct', 'combine_kronecker',
58
+
59
+ 'PermutationMatrix', 'MatrixPermute',
60
+
61
+ 'Permanent', 'per'
62
+ ]
.venv/lib/python3.11/site-packages/sympy/matrices/expressions/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (2.24 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/expressions/__pycache__/_shape.cpython-311.pyc ADDED
Binary file (6.42 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/expressions/__pycache__/adjoint.cpython-311.pyc ADDED
Binary file (3.28 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/expressions/__pycache__/applyfunc.cpython-311.pyc ADDED
Binary file (9.18 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/expressions/__pycache__/blockmatrix.cpython-311.pyc ADDED
Binary file (54.1 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/expressions/__pycache__/companion.cpython-311.pyc ADDED
Binary file (3.41 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/expressions/__pycache__/diagonal.cpython-311.pyc ADDED
Binary file (10.5 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/expressions/__pycache__/dotproduct.cpython-311.pyc ADDED
Binary file (3.17 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/expressions/__pycache__/fourier.cpython-311.pyc ADDED
Binary file (4.14 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/expressions/__pycache__/funcmatrix.cpython-311.pyc ADDED
Binary file (5.69 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/expressions/__pycache__/inverse.cpython-311.pyc ADDED
Binary file (5.54 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/expressions/__pycache__/kronecker.cpython-311.pyc ADDED
Binary file (24.6 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/expressions/__pycache__/matadd.cpython-311.pyc ADDED
Binary file (10.4 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/expressions/__pycache__/matpow.cpython-311.pyc ADDED
Binary file (9.25 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/expressions/__pycache__/special.cpython-311.pyc ADDED
Binary file (16 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/expressions/adjoint.py ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core import Basic
2
+ from sympy.functions import adjoint, conjugate
3
+ from sympy.matrices.expressions.matexpr import MatrixExpr
4
+
5
+
6
+ class Adjoint(MatrixExpr):
7
+ """
8
+ The Hermitian adjoint of a matrix expression.
9
+
10
+ This is a symbolic object that simply stores its argument without
11
+ evaluating it. To actually compute the adjoint, use the ``adjoint()``
12
+ function.
13
+
14
+ Examples
15
+ ========
16
+
17
+ >>> from sympy import MatrixSymbol, Adjoint, adjoint
18
+ >>> A = MatrixSymbol('A', 3, 5)
19
+ >>> B = MatrixSymbol('B', 5, 3)
20
+ >>> Adjoint(A*B)
21
+ Adjoint(A*B)
22
+ >>> adjoint(A*B)
23
+ Adjoint(B)*Adjoint(A)
24
+ >>> adjoint(A*B) == Adjoint(A*B)
25
+ False
26
+ >>> adjoint(A*B) == Adjoint(A*B).doit()
27
+ True
28
+ """
29
+ is_Adjoint = True
30
+
31
+ def doit(self, **hints):
32
+ arg = self.arg
33
+ if hints.get('deep', True) and isinstance(arg, Basic):
34
+ return adjoint(arg.doit(**hints))
35
+ else:
36
+ return adjoint(self.arg)
37
+
38
+ @property
39
+ def arg(self):
40
+ return self.args[0]
41
+
42
+ @property
43
+ def shape(self):
44
+ return self.arg.shape[::-1]
45
+
46
+ def _entry(self, i, j, **kwargs):
47
+ return conjugate(self.arg._entry(j, i, **kwargs))
48
+
49
+ def _eval_adjoint(self):
50
+ return self.arg
51
+
52
+ def _eval_transpose(self):
53
+ return self.arg.conjugate()
54
+
55
+ def _eval_conjugate(self):
56
+ return self.arg.transpose()
57
+
58
+ def _eval_trace(self):
59
+ from sympy.matrices.expressions.trace import Trace
60
+ return conjugate(Trace(self.arg))
.venv/lib/python3.11/site-packages/sympy/matrices/expressions/applyfunc.py ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core.expr import ExprBuilder
2
+ from sympy.core.function import (Function, FunctionClass, Lambda)
3
+ from sympy.core.symbol import Dummy
4
+ from sympy.core.sympify import sympify, _sympify
5
+ from sympy.matrices.expressions import MatrixExpr
6
+ from sympy.matrices.matrixbase import MatrixBase
7
+
8
+
9
+ class ElementwiseApplyFunction(MatrixExpr):
10
+ r"""
11
+ Apply function to a matrix elementwise without evaluating.
12
+
13
+ Examples
14
+ ========
15
+
16
+ It can be created by calling ``.applyfunc(<function>)`` on a matrix
17
+ expression:
18
+
19
+ >>> from sympy import MatrixSymbol
20
+ >>> from sympy.matrices.expressions.applyfunc import ElementwiseApplyFunction
21
+ >>> from sympy import exp
22
+ >>> X = MatrixSymbol("X", 3, 3)
23
+ >>> X.applyfunc(exp)
24
+ Lambda(_d, exp(_d)).(X)
25
+
26
+ Otherwise using the class constructor:
27
+
28
+ >>> from sympy import eye
29
+ >>> expr = ElementwiseApplyFunction(exp, eye(3))
30
+ >>> expr
31
+ Lambda(_d, exp(_d)).(Matrix([
32
+ [1, 0, 0],
33
+ [0, 1, 0],
34
+ [0, 0, 1]]))
35
+ >>> expr.doit()
36
+ Matrix([
37
+ [E, 1, 1],
38
+ [1, E, 1],
39
+ [1, 1, E]])
40
+
41
+ Notice the difference with the real mathematical functions:
42
+
43
+ >>> exp(eye(3))
44
+ Matrix([
45
+ [E, 0, 0],
46
+ [0, E, 0],
47
+ [0, 0, E]])
48
+ """
49
+
50
+ def __new__(cls, function, expr):
51
+ expr = _sympify(expr)
52
+ if not expr.is_Matrix:
53
+ raise ValueError("{} must be a matrix instance.".format(expr))
54
+
55
+ if expr.shape == (1, 1):
56
+ # Check if the function returns a matrix, in that case, just apply
57
+ # the function instead of creating an ElementwiseApplyFunc object:
58
+ ret = function(expr)
59
+ if isinstance(ret, MatrixExpr):
60
+ return ret
61
+
62
+ if not isinstance(function, (FunctionClass, Lambda)):
63
+ d = Dummy('d')
64
+ function = Lambda(d, function(d))
65
+
66
+ function = sympify(function)
67
+ if not isinstance(function, (FunctionClass, Lambda)):
68
+ raise ValueError(
69
+ "{} should be compatible with SymPy function classes."
70
+ .format(function))
71
+
72
+ if 1 not in function.nargs:
73
+ raise ValueError(
74
+ '{} should be able to accept 1 arguments.'.format(function))
75
+
76
+ if not isinstance(function, Lambda):
77
+ d = Dummy('d')
78
+ function = Lambda(d, function(d))
79
+
80
+ obj = MatrixExpr.__new__(cls, function, expr)
81
+ return obj
82
+
83
+ @property
84
+ def function(self):
85
+ return self.args[0]
86
+
87
+ @property
88
+ def expr(self):
89
+ return self.args[1]
90
+
91
+ @property
92
+ def shape(self):
93
+ return self.expr.shape
94
+
95
+ def doit(self, **hints):
96
+ deep = hints.get("deep", True)
97
+ expr = self.expr
98
+ if deep:
99
+ expr = expr.doit(**hints)
100
+ function = self.function
101
+ if isinstance(function, Lambda) and function.is_identity:
102
+ # This is a Lambda containing the identity function.
103
+ return expr
104
+ if isinstance(expr, MatrixBase):
105
+ return expr.applyfunc(self.function)
106
+ elif isinstance(expr, ElementwiseApplyFunction):
107
+ return ElementwiseApplyFunction(
108
+ lambda x: self.function(expr.function(x)),
109
+ expr.expr
110
+ ).doit(**hints)
111
+ else:
112
+ return self
113
+
114
+ def _entry(self, i, j, **kwargs):
115
+ return self.function(self.expr._entry(i, j, **kwargs))
116
+
117
+ def _get_function_fdiff(self):
118
+ d = Dummy("d")
119
+ function = self.function(d)
120
+ fdiff = function.diff(d)
121
+ if isinstance(fdiff, Function):
122
+ fdiff = type(fdiff)
123
+ else:
124
+ fdiff = Lambda(d, fdiff)
125
+ return fdiff
126
+
127
+ def _eval_derivative(self, x):
128
+ from sympy.matrices.expressions.hadamard import hadamard_product
129
+ dexpr = self.expr.diff(x)
130
+ fdiff = self._get_function_fdiff()
131
+ return hadamard_product(
132
+ dexpr,
133
+ ElementwiseApplyFunction(fdiff, self.expr)
134
+ )
135
+
136
+ def _eval_derivative_matrix_lines(self, x):
137
+ from sympy.matrices.expressions.special import Identity
138
+ from sympy.tensor.array.expressions.array_expressions import ArrayContraction
139
+ from sympy.tensor.array.expressions.array_expressions import ArrayDiagonal
140
+ from sympy.tensor.array.expressions.array_expressions import ArrayTensorProduct
141
+
142
+ fdiff = self._get_function_fdiff()
143
+ lr = self.expr._eval_derivative_matrix_lines(x)
144
+ ewdiff = ElementwiseApplyFunction(fdiff, self.expr)
145
+ if 1 in x.shape:
146
+ # Vector:
147
+ iscolumn = self.shape[1] == 1
148
+ for i in lr:
149
+ if iscolumn:
150
+ ptr1 = i.first_pointer
151
+ ptr2 = Identity(self.shape[1])
152
+ else:
153
+ ptr1 = Identity(self.shape[0])
154
+ ptr2 = i.second_pointer
155
+
156
+ subexpr = ExprBuilder(
157
+ ArrayDiagonal,
158
+ [
159
+ ExprBuilder(
160
+ ArrayTensorProduct,
161
+ [
162
+ ewdiff,
163
+ ptr1,
164
+ ptr2,
165
+ ]
166
+ ),
167
+ (0, 2) if iscolumn else (1, 4)
168
+ ],
169
+ validator=ArrayDiagonal._validate
170
+ )
171
+ i._lines = [subexpr]
172
+ i._first_pointer_parent = subexpr.args[0].args
173
+ i._first_pointer_index = 1
174
+ i._second_pointer_parent = subexpr.args[0].args
175
+ i._second_pointer_index = 2
176
+ else:
177
+ # Matrix case:
178
+ for i in lr:
179
+ ptr1 = i.first_pointer
180
+ ptr2 = i.second_pointer
181
+ newptr1 = Identity(ptr1.shape[1])
182
+ newptr2 = Identity(ptr2.shape[1])
183
+ subexpr = ExprBuilder(
184
+ ArrayContraction,
185
+ [
186
+ ExprBuilder(
187
+ ArrayTensorProduct,
188
+ [ptr1, newptr1, ewdiff, ptr2, newptr2]
189
+ ),
190
+ (1, 2, 4),
191
+ (5, 7, 8),
192
+ ],
193
+ validator=ArrayContraction._validate
194
+ )
195
+ i._first_pointer_parent = subexpr.args[0].args
196
+ i._first_pointer_index = 1
197
+ i._second_pointer_parent = subexpr.args[0].args
198
+ i._second_pointer_index = 4
199
+ i._lines = [subexpr]
200
+ return lr
201
+
202
+ def _eval_transpose(self):
203
+ from sympy.matrices.expressions.transpose import Transpose
204
+ return self.func(self.function, Transpose(self.expr).doit())
.venv/lib/python3.11/site-packages/sympy/matrices/expressions/blockmatrix.py ADDED
@@ -0,0 +1,980 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.assumptions.ask import (Q, ask)
2
+ from sympy.core import Basic, Add, Mul, S
3
+ from sympy.core.sympify import _sympify
4
+ from sympy.functions import adjoint
5
+ from sympy.functions.elementary.complexes import re, im
6
+ from sympy.strategies import typed, exhaust, condition, do_one, unpack
7
+ from sympy.strategies.traverse import bottom_up
8
+ from sympy.utilities.iterables import is_sequence, sift
9
+ from sympy.utilities.misc import filldedent
10
+
11
+ from sympy.matrices import Matrix, ShapeError
12
+ from sympy.matrices.exceptions import NonInvertibleMatrixError
13
+ from sympy.matrices.expressions.determinant import det, Determinant
14
+ from sympy.matrices.expressions.inverse import Inverse
15
+ from sympy.matrices.expressions.matadd import MatAdd
16
+ from sympy.matrices.expressions.matexpr import MatrixExpr, MatrixElement
17
+ from sympy.matrices.expressions.matmul import MatMul
18
+ from sympy.matrices.expressions.matpow import MatPow
19
+ from sympy.matrices.expressions.slice import MatrixSlice
20
+ from sympy.matrices.expressions.special import ZeroMatrix, Identity
21
+ from sympy.matrices.expressions.trace import trace
22
+ from sympy.matrices.expressions.transpose import Transpose, transpose
23
+
24
+
25
+ class BlockMatrix(MatrixExpr):
26
+ """A BlockMatrix is a Matrix comprised of other matrices.
27
+
28
+ The submatrices are stored in a SymPy Matrix object but accessed as part of
29
+ a Matrix Expression
30
+
31
+ >>> from sympy import (MatrixSymbol, BlockMatrix, symbols,
32
+ ... Identity, ZeroMatrix, block_collapse)
33
+ >>> n,m,l = symbols('n m l')
34
+ >>> X = MatrixSymbol('X', n, n)
35
+ >>> Y = MatrixSymbol('Y', m, m)
36
+ >>> Z = MatrixSymbol('Z', n, m)
37
+ >>> B = BlockMatrix([[X, Z], [ZeroMatrix(m,n), Y]])
38
+ >>> print(B)
39
+ Matrix([
40
+ [X, Z],
41
+ [0, Y]])
42
+
43
+ >>> C = BlockMatrix([[Identity(n), Z]])
44
+ >>> print(C)
45
+ Matrix([[I, Z]])
46
+
47
+ >>> print(block_collapse(C*B))
48
+ Matrix([[X, Z + Z*Y]])
49
+
50
+ Some matrices might be comprised of rows of blocks with
51
+ the matrices in each row having the same height and the
52
+ rows all having the same total number of columns but
53
+ not having the same number of columns for each matrix
54
+ in each row. In this case, the matrix is not a block
55
+ matrix and should be instantiated by Matrix.
56
+
57
+ >>> from sympy import ones, Matrix
58
+ >>> dat = [
59
+ ... [ones(3,2), ones(3,3)*2],
60
+ ... [ones(2,3)*3, ones(2,2)*4]]
61
+ ...
62
+ >>> BlockMatrix(dat)
63
+ Traceback (most recent call last):
64
+ ...
65
+ ValueError:
66
+ Although this matrix is comprised of blocks, the blocks do not fill
67
+ the matrix in a size-symmetric fashion. To create a full matrix from
68
+ these arguments, pass them directly to Matrix.
69
+ >>> Matrix(dat)
70
+ Matrix([
71
+ [1, 1, 2, 2, 2],
72
+ [1, 1, 2, 2, 2],
73
+ [1, 1, 2, 2, 2],
74
+ [3, 3, 3, 4, 4],
75
+ [3, 3, 3, 4, 4]])
76
+
77
+ See Also
78
+ ========
79
+ sympy.matrices.matrixbase.MatrixBase.irregular
80
+ """
81
+ def __new__(cls, *args, **kwargs):
82
+ from sympy.matrices.immutable import ImmutableDenseMatrix
83
+ isMat = lambda i: getattr(i, 'is_Matrix', False)
84
+ if len(args) != 1 or \
85
+ not is_sequence(args[0]) or \
86
+ len({isMat(r) for r in args[0]}) != 1:
87
+ raise ValueError(filldedent('''
88
+ expecting a sequence of 1 or more rows
89
+ containing Matrices.'''))
90
+ rows = args[0] if args else []
91
+ if not isMat(rows):
92
+ if rows and isMat(rows[0]):
93
+ rows = [rows] # rows is not list of lists or []
94
+ # regularity check
95
+ # same number of matrices in each row
96
+ blocky = ok = len({len(r) for r in rows}) == 1
97
+ if ok:
98
+ # same number of rows for each matrix in a row
99
+ for r in rows:
100
+ ok = len({i.rows for i in r}) == 1
101
+ if not ok:
102
+ break
103
+ blocky = ok
104
+ if ok:
105
+ # same number of cols for each matrix in each col
106
+ for c in range(len(rows[0])):
107
+ ok = len({rows[i][c].cols
108
+ for i in range(len(rows))}) == 1
109
+ if not ok:
110
+ break
111
+ if not ok:
112
+ # same total cols in each row
113
+ ok = len({
114
+ sum(i.cols for i in r) for r in rows}) == 1
115
+ if blocky and ok:
116
+ raise ValueError(filldedent('''
117
+ Although this matrix is comprised of blocks,
118
+ the blocks do not fill the matrix in a
119
+ size-symmetric fashion. To create a full matrix
120
+ from these arguments, pass them directly to
121
+ Matrix.'''))
122
+ raise ValueError(filldedent('''
123
+ When there are not the same number of rows in each
124
+ row's matrices or there are not the same number of
125
+ total columns in each row, the matrix is not a
126
+ block matrix. If this matrix is known to consist of
127
+ blocks fully filling a 2-D space then see
128
+ Matrix.irregular.'''))
129
+ mat = ImmutableDenseMatrix(rows, evaluate=False)
130
+ obj = Basic.__new__(cls, mat)
131
+ return obj
132
+
133
+ @property
134
+ def shape(self):
135
+ numrows = numcols = 0
136
+ M = self.blocks
137
+ for i in range(M.shape[0]):
138
+ numrows += M[i, 0].shape[0]
139
+ for i in range(M.shape[1]):
140
+ numcols += M[0, i].shape[1]
141
+ return (numrows, numcols)
142
+
143
+ @property
144
+ def blockshape(self):
145
+ return self.blocks.shape
146
+
147
+ @property
148
+ def blocks(self):
149
+ return self.args[0]
150
+
151
+ @property
152
+ def rowblocksizes(self):
153
+ return [self.blocks[i, 0].rows for i in range(self.blockshape[0])]
154
+
155
+ @property
156
+ def colblocksizes(self):
157
+ return [self.blocks[0, i].cols for i in range(self.blockshape[1])]
158
+
159
+ def structurally_equal(self, other):
160
+ return (isinstance(other, BlockMatrix)
161
+ and self.shape == other.shape
162
+ and self.blockshape == other.blockshape
163
+ and self.rowblocksizes == other.rowblocksizes
164
+ and self.colblocksizes == other.colblocksizes)
165
+
166
+ def _blockmul(self, other):
167
+ if (isinstance(other, BlockMatrix) and
168
+ self.colblocksizes == other.rowblocksizes):
169
+ return BlockMatrix(self.blocks*other.blocks)
170
+
171
+ return self * other
172
+
173
+ def _blockadd(self, other):
174
+ if (isinstance(other, BlockMatrix)
175
+ and self.structurally_equal(other)):
176
+ return BlockMatrix(self.blocks + other.blocks)
177
+
178
+ return self + other
179
+
180
+ def _eval_transpose(self):
181
+ # Flip all the individual matrices
182
+ matrices = [transpose(matrix) for matrix in self.blocks]
183
+ # Make a copy
184
+ M = Matrix(self.blockshape[0], self.blockshape[1], matrices)
185
+ # Transpose the block structure
186
+ M = M.transpose()
187
+ return BlockMatrix(M)
188
+
189
+ def _eval_adjoint(self):
190
+ # Adjoint all the individual matrices
191
+ matrices = [adjoint(matrix) for matrix in self.blocks]
192
+ # Make a copy
193
+ M = Matrix(self.blockshape[0], self.blockshape[1], matrices)
194
+ # Transpose the block structure
195
+ M = M.transpose()
196
+ return BlockMatrix(M)
197
+
198
+ def _eval_trace(self):
199
+ if self.rowblocksizes == self.colblocksizes:
200
+ blocks = [self.blocks[i, i] for i in range(self.blockshape[0])]
201
+ return Add(*[trace(block) for block in blocks])
202
+
203
+ def _eval_determinant(self):
204
+ if self.blockshape == (1, 1):
205
+ return det(self.blocks[0, 0])
206
+ if self.blockshape == (2, 2):
207
+ [[A, B],
208
+ [C, D]] = self.blocks.tolist()
209
+ if ask(Q.invertible(A)):
210
+ return det(A)*det(D - C*A.I*B)
211
+ elif ask(Q.invertible(D)):
212
+ return det(D)*det(A - B*D.I*C)
213
+ return Determinant(self)
214
+
215
+ def _eval_as_real_imag(self):
216
+ real_matrices = [re(matrix) for matrix in self.blocks]
217
+ real_matrices = Matrix(self.blockshape[0], self.blockshape[1], real_matrices)
218
+
219
+ im_matrices = [im(matrix) for matrix in self.blocks]
220
+ im_matrices = Matrix(self.blockshape[0], self.blockshape[1], im_matrices)
221
+
222
+ return (BlockMatrix(real_matrices), BlockMatrix(im_matrices))
223
+
224
+ def _eval_derivative(self, x):
225
+ return BlockMatrix(self.blocks.diff(x))
226
+
227
+ def transpose(self):
228
+ """Return transpose of matrix.
229
+
230
+ Examples
231
+ ========
232
+
233
+ >>> from sympy import MatrixSymbol, BlockMatrix, ZeroMatrix
234
+ >>> from sympy.abc import m, n
235
+ >>> X = MatrixSymbol('X', n, n)
236
+ >>> Y = MatrixSymbol('Y', m, m)
237
+ >>> Z = MatrixSymbol('Z', n, m)
238
+ >>> B = BlockMatrix([[X, Z], [ZeroMatrix(m,n), Y]])
239
+ >>> B.transpose()
240
+ Matrix([
241
+ [X.T, 0],
242
+ [Z.T, Y.T]])
243
+ >>> _.transpose()
244
+ Matrix([
245
+ [X, Z],
246
+ [0, Y]])
247
+ """
248
+ return self._eval_transpose()
249
+
250
+ def schur(self, mat = 'A', generalized = False):
251
+ """Return the Schur Complement of the 2x2 BlockMatrix
252
+
253
+ Parameters
254
+ ==========
255
+
256
+ mat : String, optional
257
+ The matrix with respect to which the
258
+ Schur Complement is calculated. 'A' is
259
+ used by default
260
+
261
+ generalized : bool, optional
262
+ If True, returns the generalized Schur
263
+ Component which uses Moore-Penrose Inverse
264
+
265
+ Examples
266
+ ========
267
+
268
+ >>> from sympy import symbols, MatrixSymbol, BlockMatrix
269
+ >>> m, n = symbols('m n')
270
+ >>> A = MatrixSymbol('A', n, n)
271
+ >>> B = MatrixSymbol('B', n, m)
272
+ >>> C = MatrixSymbol('C', m, n)
273
+ >>> D = MatrixSymbol('D', m, m)
274
+ >>> X = BlockMatrix([[A, B], [C, D]])
275
+
276
+ The default Schur Complement is evaluated with "A"
277
+
278
+ >>> X.schur()
279
+ -C*A**(-1)*B + D
280
+ >>> X.schur('D')
281
+ A - B*D**(-1)*C
282
+
283
+ Schur complement with non-invertible matrices is not
284
+ defined. Instead, the generalized Schur complement can
285
+ be calculated which uses the Moore-Penrose Inverse. To
286
+ achieve this, `generalized` must be set to `True`
287
+
288
+ >>> X.schur('B', generalized=True)
289
+ C - D*(B.T*B)**(-1)*B.T*A
290
+ >>> X.schur('C', generalized=True)
291
+ -A*(C.T*C)**(-1)*C.T*D + B
292
+
293
+ Returns
294
+ =======
295
+
296
+ M : Matrix
297
+ The Schur Complement Matrix
298
+
299
+ Raises
300
+ ======
301
+
302
+ ShapeError
303
+ If the block matrix is not a 2x2 matrix
304
+
305
+ NonInvertibleMatrixError
306
+ If given matrix is non-invertible
307
+
308
+ References
309
+ ==========
310
+
311
+ .. [1] Wikipedia Article on Schur Component : https://en.wikipedia.org/wiki/Schur_complement
312
+
313
+ See Also
314
+ ========
315
+
316
+ sympy.matrices.matrixbase.MatrixBase.pinv
317
+ """
318
+
319
+ if self.blockshape == (2, 2):
320
+ [[A, B],
321
+ [C, D]] = self.blocks.tolist()
322
+ d={'A' : A, 'B' : B, 'C' : C, 'D' : D}
323
+ try:
324
+ inv = (d[mat].T*d[mat]).inv()*d[mat].T if generalized else d[mat].inv()
325
+ if mat == 'A':
326
+ return D - C * inv * B
327
+ elif mat == 'B':
328
+ return C - D * inv * A
329
+ elif mat == 'C':
330
+ return B - A * inv * D
331
+ elif mat == 'D':
332
+ return A - B * inv * C
333
+ #For matrices where no sub-matrix is square
334
+ return self
335
+ except NonInvertibleMatrixError:
336
+ raise NonInvertibleMatrixError('The given matrix is not invertible. Please set generalized=True \
337
+ to compute the generalized Schur Complement which uses Moore-Penrose Inverse')
338
+ else:
339
+ raise ShapeError('Schur Complement can only be calculated for 2x2 block matrices')
340
+
341
+ def LDUdecomposition(self):
342
+ """Returns the Block LDU decomposition of
343
+ a 2x2 Block Matrix
344
+
345
+ Returns
346
+ =======
347
+
348
+ (L, D, U) : Matrices
349
+ L : Lower Diagonal Matrix
350
+ D : Diagonal Matrix
351
+ U : Upper Diagonal Matrix
352
+
353
+ Examples
354
+ ========
355
+
356
+ >>> from sympy import symbols, MatrixSymbol, BlockMatrix, block_collapse
357
+ >>> m, n = symbols('m n')
358
+ >>> A = MatrixSymbol('A', n, n)
359
+ >>> B = MatrixSymbol('B', n, m)
360
+ >>> C = MatrixSymbol('C', m, n)
361
+ >>> D = MatrixSymbol('D', m, m)
362
+ >>> X = BlockMatrix([[A, B], [C, D]])
363
+ >>> L, D, U = X.LDUdecomposition()
364
+ >>> block_collapse(L*D*U)
365
+ Matrix([
366
+ [A, B],
367
+ [C, D]])
368
+
369
+ Raises
370
+ ======
371
+
372
+ ShapeError
373
+ If the block matrix is not a 2x2 matrix
374
+
375
+ NonInvertibleMatrixError
376
+ If the matrix "A" is non-invertible
377
+
378
+ See Also
379
+ ========
380
+ sympy.matrices.expressions.blockmatrix.BlockMatrix.UDLdecomposition
381
+ sympy.matrices.expressions.blockmatrix.BlockMatrix.LUdecomposition
382
+ """
383
+ if self.blockshape == (2,2):
384
+ [[A, B],
385
+ [C, D]] = self.blocks.tolist()
386
+ try:
387
+ AI = A.I
388
+ except NonInvertibleMatrixError:
389
+ raise NonInvertibleMatrixError('Block LDU decomposition cannot be calculated when\
390
+ "A" is singular')
391
+ Ip = Identity(B.shape[0])
392
+ Iq = Identity(B.shape[1])
393
+ Z = ZeroMatrix(*B.shape)
394
+ L = BlockMatrix([[Ip, Z], [C*AI, Iq]])
395
+ D = BlockDiagMatrix(A, self.schur())
396
+ U = BlockMatrix([[Ip, AI*B],[Z.T, Iq]])
397
+ return L, D, U
398
+ else:
399
+ raise ShapeError("Block LDU decomposition is supported only for 2x2 block matrices")
400
+
401
+ def UDLdecomposition(self):
402
+ """Returns the Block UDL decomposition of
403
+ a 2x2 Block Matrix
404
+
405
+ Returns
406
+ =======
407
+
408
+ (U, D, L) : Matrices
409
+ U : Upper Diagonal Matrix
410
+ D : Diagonal Matrix
411
+ L : Lower Diagonal Matrix
412
+
413
+ Examples
414
+ ========
415
+
416
+ >>> from sympy import symbols, MatrixSymbol, BlockMatrix, block_collapse
417
+ >>> m, n = symbols('m n')
418
+ >>> A = MatrixSymbol('A', n, n)
419
+ >>> B = MatrixSymbol('B', n, m)
420
+ >>> C = MatrixSymbol('C', m, n)
421
+ >>> D = MatrixSymbol('D', m, m)
422
+ >>> X = BlockMatrix([[A, B], [C, D]])
423
+ >>> U, D, L = X.UDLdecomposition()
424
+ >>> block_collapse(U*D*L)
425
+ Matrix([
426
+ [A, B],
427
+ [C, D]])
428
+
429
+ Raises
430
+ ======
431
+
432
+ ShapeError
433
+ If the block matrix is not a 2x2 matrix
434
+
435
+ NonInvertibleMatrixError
436
+ If the matrix "D" is non-invertible
437
+
438
+ See Also
439
+ ========
440
+ sympy.matrices.expressions.blockmatrix.BlockMatrix.LDUdecomposition
441
+ sympy.matrices.expressions.blockmatrix.BlockMatrix.LUdecomposition
442
+ """
443
+ if self.blockshape == (2,2):
444
+ [[A, B],
445
+ [C, D]] = self.blocks.tolist()
446
+ try:
447
+ DI = D.I
448
+ except NonInvertibleMatrixError:
449
+ raise NonInvertibleMatrixError('Block UDL decomposition cannot be calculated when\
450
+ "D" is singular')
451
+ Ip = Identity(A.shape[0])
452
+ Iq = Identity(B.shape[1])
453
+ Z = ZeroMatrix(*B.shape)
454
+ U = BlockMatrix([[Ip, B*DI], [Z.T, Iq]])
455
+ D = BlockDiagMatrix(self.schur('D'), D)
456
+ L = BlockMatrix([[Ip, Z],[DI*C, Iq]])
457
+ return U, D, L
458
+ else:
459
+ raise ShapeError("Block UDL decomposition is supported only for 2x2 block matrices")
460
+
461
+ def LUdecomposition(self):
462
+ """Returns the Block LU decomposition of
463
+ a 2x2 Block Matrix
464
+
465
+ Returns
466
+ =======
467
+
468
+ (L, U) : Matrices
469
+ L : Lower Diagonal Matrix
470
+ U : Upper Diagonal Matrix
471
+
472
+ Examples
473
+ ========
474
+
475
+ >>> from sympy import symbols, MatrixSymbol, BlockMatrix, block_collapse
476
+ >>> m, n = symbols('m n')
477
+ >>> A = MatrixSymbol('A', n, n)
478
+ >>> B = MatrixSymbol('B', n, m)
479
+ >>> C = MatrixSymbol('C', m, n)
480
+ >>> D = MatrixSymbol('D', m, m)
481
+ >>> X = BlockMatrix([[A, B], [C, D]])
482
+ >>> L, U = X.LUdecomposition()
483
+ >>> block_collapse(L*U)
484
+ Matrix([
485
+ [A, B],
486
+ [C, D]])
487
+
488
+ Raises
489
+ ======
490
+
491
+ ShapeError
492
+ If the block matrix is not a 2x2 matrix
493
+
494
+ NonInvertibleMatrixError
495
+ If the matrix "A" is non-invertible
496
+
497
+ See Also
498
+ ========
499
+ sympy.matrices.expressions.blockmatrix.BlockMatrix.UDLdecomposition
500
+ sympy.matrices.expressions.blockmatrix.BlockMatrix.LDUdecomposition
501
+ """
502
+ if self.blockshape == (2,2):
503
+ [[A, B],
504
+ [C, D]] = self.blocks.tolist()
505
+ try:
506
+ A = A**S.Half
507
+ AI = A.I
508
+ except NonInvertibleMatrixError:
509
+ raise NonInvertibleMatrixError('Block LU decomposition cannot be calculated when\
510
+ "A" is singular')
511
+ Z = ZeroMatrix(*B.shape)
512
+ Q = self.schur()**S.Half
513
+ L = BlockMatrix([[A, Z], [C*AI, Q]])
514
+ U = BlockMatrix([[A, AI*B],[Z.T, Q]])
515
+ return L, U
516
+ else:
517
+ raise ShapeError("Block LU decomposition is supported only for 2x2 block matrices")
518
+
519
+ def _entry(self, i, j, **kwargs):
520
+ # Find row entry
521
+ orig_i, orig_j = i, j
522
+ for row_block, numrows in enumerate(self.rowblocksizes):
523
+ cmp = i < numrows
524
+ if cmp == True:
525
+ break
526
+ elif cmp == False:
527
+ i -= numrows
528
+ elif row_block < self.blockshape[0] - 1:
529
+ # Can't tell which block and it's not the last one, return unevaluated
530
+ return MatrixElement(self, orig_i, orig_j)
531
+ for col_block, numcols in enumerate(self.colblocksizes):
532
+ cmp = j < numcols
533
+ if cmp == True:
534
+ break
535
+ elif cmp == False:
536
+ j -= numcols
537
+ elif col_block < self.blockshape[1] - 1:
538
+ return MatrixElement(self, orig_i, orig_j)
539
+ return self.blocks[row_block, col_block][i, j]
540
+
541
+ @property
542
+ def is_Identity(self):
543
+ if self.blockshape[0] != self.blockshape[1]:
544
+ return False
545
+ for i in range(self.blockshape[0]):
546
+ for j in range(self.blockshape[1]):
547
+ if i==j and not self.blocks[i, j].is_Identity:
548
+ return False
549
+ if i!=j and not self.blocks[i, j].is_ZeroMatrix:
550
+ return False
551
+ return True
552
+
553
+ @property
554
+ def is_structurally_symmetric(self):
555
+ return self.rowblocksizes == self.colblocksizes
556
+
557
+ def equals(self, other):
558
+ if self == other:
559
+ return True
560
+ if (isinstance(other, BlockMatrix) and self.blocks == other.blocks):
561
+ return True
562
+ return super().equals(other)
563
+
564
+
565
+ class BlockDiagMatrix(BlockMatrix):
566
+ """A sparse matrix with block matrices along its diagonals
567
+
568
+ Examples
569
+ ========
570
+
571
+ >>> from sympy import MatrixSymbol, BlockDiagMatrix, symbols
572
+ >>> n, m, l = symbols('n m l')
573
+ >>> X = MatrixSymbol('X', n, n)
574
+ >>> Y = MatrixSymbol('Y', m, m)
575
+ >>> BlockDiagMatrix(X, Y)
576
+ Matrix([
577
+ [X, 0],
578
+ [0, Y]])
579
+
580
+ Notes
581
+ =====
582
+
583
+ If you want to get the individual diagonal blocks, use
584
+ :meth:`get_diag_blocks`.
585
+
586
+ See Also
587
+ ========
588
+
589
+ sympy.matrices.dense.diag
590
+ """
591
+ def __new__(cls, *mats):
592
+ return Basic.__new__(BlockDiagMatrix, *[_sympify(m) for m in mats])
593
+
594
+ @property
595
+ def diag(self):
596
+ return self.args
597
+
598
+ @property
599
+ def blocks(self):
600
+ from sympy.matrices.immutable import ImmutableDenseMatrix
601
+ mats = self.args
602
+ data = [[mats[i] if i == j else ZeroMatrix(mats[i].rows, mats[j].cols)
603
+ for j in range(len(mats))]
604
+ for i in range(len(mats))]
605
+ return ImmutableDenseMatrix(data, evaluate=False)
606
+
607
+ @property
608
+ def shape(self):
609
+ return (sum(block.rows for block in self.args),
610
+ sum(block.cols for block in self.args))
611
+
612
+ @property
613
+ def blockshape(self):
614
+ n = len(self.args)
615
+ return (n, n)
616
+
617
+ @property
618
+ def rowblocksizes(self):
619
+ return [block.rows for block in self.args]
620
+
621
+ @property
622
+ def colblocksizes(self):
623
+ return [block.cols for block in self.args]
624
+
625
+ def _all_square_blocks(self):
626
+ """Returns true if all blocks are square"""
627
+ return all(mat.is_square for mat in self.args)
628
+
629
+ def _eval_determinant(self):
630
+ if self._all_square_blocks():
631
+ return Mul(*[det(mat) for mat in self.args])
632
+ # At least one block is non-square. Since the entire matrix must be square we know there must
633
+ # be at least two blocks in this matrix, in which case the entire matrix is necessarily rank-deficient
634
+ return S.Zero
635
+
636
+ def _eval_inverse(self, expand='ignored'):
637
+ if self._all_square_blocks():
638
+ return BlockDiagMatrix(*[mat.inverse() for mat in self.args])
639
+ # See comment in _eval_determinant()
640
+ raise NonInvertibleMatrixError('Matrix det == 0; not invertible.')
641
+
642
+ def _eval_transpose(self):
643
+ return BlockDiagMatrix(*[mat.transpose() for mat in self.args])
644
+
645
+ def _blockmul(self, other):
646
+ if (isinstance(other, BlockDiagMatrix) and
647
+ self.colblocksizes == other.rowblocksizes):
648
+ return BlockDiagMatrix(*[a*b for a, b in zip(self.args, other.args)])
649
+ else:
650
+ return BlockMatrix._blockmul(self, other)
651
+
652
+ def _blockadd(self, other):
653
+ if (isinstance(other, BlockDiagMatrix) and
654
+ self.blockshape == other.blockshape and
655
+ self.rowblocksizes == other.rowblocksizes and
656
+ self.colblocksizes == other.colblocksizes):
657
+ return BlockDiagMatrix(*[a + b for a, b in zip(self.args, other.args)])
658
+ else:
659
+ return BlockMatrix._blockadd(self, other)
660
+
661
+ def get_diag_blocks(self):
662
+ """Return the list of diagonal blocks of the matrix.
663
+
664
+ Examples
665
+ ========
666
+
667
+ >>> from sympy import BlockDiagMatrix, Matrix
668
+
669
+ >>> A = Matrix([[1, 2], [3, 4]])
670
+ >>> B = Matrix([[5, 6], [7, 8]])
671
+ >>> M = BlockDiagMatrix(A, B)
672
+
673
+ How to get diagonal blocks from the block diagonal matrix:
674
+
675
+ >>> diag_blocks = M.get_diag_blocks()
676
+ >>> diag_blocks[0]
677
+ Matrix([
678
+ [1, 2],
679
+ [3, 4]])
680
+ >>> diag_blocks[1]
681
+ Matrix([
682
+ [5, 6],
683
+ [7, 8]])
684
+ """
685
+ return self.args
686
+
687
+
688
+ def block_collapse(expr):
689
+ """Evaluates a block matrix expression
690
+
691
+ >>> from sympy import MatrixSymbol, BlockMatrix, symbols, Identity, ZeroMatrix, block_collapse
692
+ >>> n,m,l = symbols('n m l')
693
+ >>> X = MatrixSymbol('X', n, n)
694
+ >>> Y = MatrixSymbol('Y', m, m)
695
+ >>> Z = MatrixSymbol('Z', n, m)
696
+ >>> B = BlockMatrix([[X, Z], [ZeroMatrix(m, n), Y]])
697
+ >>> print(B)
698
+ Matrix([
699
+ [X, Z],
700
+ [0, Y]])
701
+
702
+ >>> C = BlockMatrix([[Identity(n), Z]])
703
+ >>> print(C)
704
+ Matrix([[I, Z]])
705
+
706
+ >>> print(block_collapse(C*B))
707
+ Matrix([[X, Z + Z*Y]])
708
+ """
709
+ from sympy.strategies.util import expr_fns
710
+
711
+ hasbm = lambda expr: isinstance(expr, MatrixExpr) and expr.has(BlockMatrix)
712
+
713
+ conditioned_rl = condition(
714
+ hasbm,
715
+ typed(
716
+ {MatAdd: do_one(bc_matadd, bc_block_plus_ident),
717
+ MatMul: do_one(bc_matmul, bc_dist),
718
+ MatPow: bc_matmul,
719
+ Transpose: bc_transpose,
720
+ Inverse: bc_inverse,
721
+ BlockMatrix: do_one(bc_unpack, deblock)}
722
+ )
723
+ )
724
+
725
+ rule = exhaust(
726
+ bottom_up(
727
+ exhaust(conditioned_rl),
728
+ fns=expr_fns
729
+ )
730
+ )
731
+
732
+ result = rule(expr)
733
+ doit = getattr(result, 'doit', None)
734
+ if doit is not None:
735
+ return doit()
736
+ else:
737
+ return result
738
+
739
+ def bc_unpack(expr):
740
+ if expr.blockshape == (1, 1):
741
+ return expr.blocks[0, 0]
742
+ return expr
743
+
744
+ def bc_matadd(expr):
745
+ args = sift(expr.args, lambda M: isinstance(M, BlockMatrix))
746
+ blocks = args[True]
747
+ if not blocks:
748
+ return expr
749
+
750
+ nonblocks = args[False]
751
+ block = blocks[0]
752
+ for b in blocks[1:]:
753
+ block = block._blockadd(b)
754
+ if nonblocks:
755
+ return MatAdd(*nonblocks) + block
756
+ else:
757
+ return block
758
+
759
+ def bc_block_plus_ident(expr):
760
+ idents = [arg for arg in expr.args if arg.is_Identity]
761
+ if not idents:
762
+ return expr
763
+
764
+ blocks = [arg for arg in expr.args if isinstance(arg, BlockMatrix)]
765
+ if (blocks and all(b.structurally_equal(blocks[0]) for b in blocks)
766
+ and blocks[0].is_structurally_symmetric):
767
+ block_id = BlockDiagMatrix(*[Identity(k)
768
+ for k in blocks[0].rowblocksizes])
769
+ rest = [arg for arg in expr.args if not arg.is_Identity and not isinstance(arg, BlockMatrix)]
770
+ return MatAdd(block_id * len(idents), *blocks, *rest).doit()
771
+
772
+ return expr
773
+
774
+ def bc_dist(expr):
775
+ """ Turn a*[X, Y] into [a*X, a*Y] """
776
+ factor, mat = expr.as_coeff_mmul()
777
+ if factor == 1:
778
+ return expr
779
+
780
+ unpacked = unpack(mat)
781
+
782
+ if isinstance(unpacked, BlockDiagMatrix):
783
+ B = unpacked.diag
784
+ new_B = [factor * mat for mat in B]
785
+ return BlockDiagMatrix(*new_B)
786
+ elif isinstance(unpacked, BlockMatrix):
787
+ B = unpacked.blocks
788
+ new_B = [
789
+ [factor * B[i, j] for j in range(B.cols)] for i in range(B.rows)]
790
+ return BlockMatrix(new_B)
791
+ return expr
792
+
793
+
794
+ def bc_matmul(expr):
795
+ if isinstance(expr, MatPow):
796
+ if expr.args[1].is_Integer and expr.args[1] > 0:
797
+ factor, matrices = 1, [expr.args[0]]*expr.args[1]
798
+ else:
799
+ return expr
800
+ else:
801
+ factor, matrices = expr.as_coeff_matrices()
802
+
803
+ i = 0
804
+ while (i+1 < len(matrices)):
805
+ A, B = matrices[i:i+2]
806
+ if isinstance(A, BlockMatrix) and isinstance(B, BlockMatrix):
807
+ matrices[i] = A._blockmul(B)
808
+ matrices.pop(i+1)
809
+ elif isinstance(A, BlockMatrix):
810
+ matrices[i] = A._blockmul(BlockMatrix([[B]]))
811
+ matrices.pop(i+1)
812
+ elif isinstance(B, BlockMatrix):
813
+ matrices[i] = BlockMatrix([[A]])._blockmul(B)
814
+ matrices.pop(i+1)
815
+ else:
816
+ i+=1
817
+ return MatMul(factor, *matrices).doit()
818
+
819
+ def bc_transpose(expr):
820
+ collapse = block_collapse(expr.arg)
821
+ return collapse._eval_transpose()
822
+
823
+
824
+ def bc_inverse(expr):
825
+ if isinstance(expr.arg, BlockDiagMatrix):
826
+ return expr.inverse()
827
+
828
+ expr2 = blockinverse_1x1(expr)
829
+ if expr != expr2:
830
+ return expr2
831
+ return blockinverse_2x2(Inverse(reblock_2x2(expr.arg)))
832
+
833
+ def blockinverse_1x1(expr):
834
+ if isinstance(expr.arg, BlockMatrix) and expr.arg.blockshape == (1, 1):
835
+ mat = Matrix([[expr.arg.blocks[0].inverse()]])
836
+ return BlockMatrix(mat)
837
+ return expr
838
+
839
+
840
+ def blockinverse_2x2(expr):
841
+ if isinstance(expr.arg, BlockMatrix) and expr.arg.blockshape == (2, 2):
842
+ # See: Inverses of 2x2 Block Matrices, Tzon-Tzer Lu and Sheng-Hua Shiou
843
+ [[A, B],
844
+ [C, D]] = expr.arg.blocks.tolist()
845
+
846
+ formula = _choose_2x2_inversion_formula(A, B, C, D)
847
+ if formula != None:
848
+ MI = expr.arg.schur(formula).I
849
+ if formula == 'A':
850
+ AI = A.I
851
+ return BlockMatrix([[AI + AI * B * MI * C * AI, -AI * B * MI], [-MI * C * AI, MI]])
852
+ if formula == 'B':
853
+ BI = B.I
854
+ return BlockMatrix([[-MI * D * BI, MI], [BI + BI * A * MI * D * BI, -BI * A * MI]])
855
+ if formula == 'C':
856
+ CI = C.I
857
+ return BlockMatrix([[-CI * D * MI, CI + CI * D * MI * A * CI], [MI, -MI * A * CI]])
858
+ if formula == 'D':
859
+ DI = D.I
860
+ return BlockMatrix([[MI, -MI * B * DI], [-DI * C * MI, DI + DI * C * MI * B * DI]])
861
+
862
+ return expr
863
+
864
+
865
+ def _choose_2x2_inversion_formula(A, B, C, D):
866
+ """
867
+ Assuming [[A, B], [C, D]] would form a valid square block matrix, find
868
+ which of the classical 2x2 block matrix inversion formulas would be
869
+ best suited.
870
+
871
+ Returns 'A', 'B', 'C', 'D' to represent the algorithm involving inversion
872
+ of the given argument or None if the matrix cannot be inverted using
873
+ any of those formulas.
874
+ """
875
+ # Try to find a known invertible matrix. Note that the Schur complement
876
+ # is currently not being considered for this
877
+ A_inv = ask(Q.invertible(A))
878
+ if A_inv == True:
879
+ return 'A'
880
+ B_inv = ask(Q.invertible(B))
881
+ if B_inv == True:
882
+ return 'B'
883
+ C_inv = ask(Q.invertible(C))
884
+ if C_inv == True:
885
+ return 'C'
886
+ D_inv = ask(Q.invertible(D))
887
+ if D_inv == True:
888
+ return 'D'
889
+ # Otherwise try to find a matrix that isn't known to be non-invertible
890
+ if A_inv != False:
891
+ return 'A'
892
+ if B_inv != False:
893
+ return 'B'
894
+ if C_inv != False:
895
+ return 'C'
896
+ if D_inv != False:
897
+ return 'D'
898
+ return None
899
+
900
+
901
+ def deblock(B):
902
+ """ Flatten a BlockMatrix of BlockMatrices """
903
+ if not isinstance(B, BlockMatrix) or not B.blocks.has(BlockMatrix):
904
+ return B
905
+ wrap = lambda x: x if isinstance(x, BlockMatrix) else BlockMatrix([[x]])
906
+ bb = B.blocks.applyfunc(wrap) # everything is a block
907
+
908
+ try:
909
+ MM = Matrix(0, sum(bb[0, i].blocks.shape[1] for i in range(bb.shape[1])), [])
910
+ for row in range(0, bb.shape[0]):
911
+ M = Matrix(bb[row, 0].blocks)
912
+ for col in range(1, bb.shape[1]):
913
+ M = M.row_join(bb[row, col].blocks)
914
+ MM = MM.col_join(M)
915
+
916
+ return BlockMatrix(MM)
917
+ except ShapeError:
918
+ return B
919
+
920
+
921
+ def reblock_2x2(expr):
922
+ """
923
+ Reblock a BlockMatrix so that it has 2x2 blocks of block matrices. If
924
+ possible in such a way that the matrix continues to be invertible using the
925
+ classical 2x2 block inversion formulas.
926
+ """
927
+ if not isinstance(expr, BlockMatrix) or not all(d > 2 for d in expr.blockshape):
928
+ return expr
929
+
930
+ BM = BlockMatrix # for brevity's sake
931
+ rowblocks, colblocks = expr.blockshape
932
+ blocks = expr.blocks
933
+ for i in range(1, rowblocks):
934
+ for j in range(1, colblocks):
935
+ # try to split rows at i and cols at j
936
+ A = bc_unpack(BM(blocks[:i, :j]))
937
+ B = bc_unpack(BM(blocks[:i, j:]))
938
+ C = bc_unpack(BM(blocks[i:, :j]))
939
+ D = bc_unpack(BM(blocks[i:, j:]))
940
+
941
+ formula = _choose_2x2_inversion_formula(A, B, C, D)
942
+ if formula is not None:
943
+ return BlockMatrix([[A, B], [C, D]])
944
+
945
+ # else: nothing worked, just split upper left corner
946
+ return BM([[blocks[0, 0], BM(blocks[0, 1:])],
947
+ [BM(blocks[1:, 0]), BM(blocks[1:, 1:])]])
948
+
949
+
950
+ def bounds(sizes):
951
+ """ Convert sequence of numbers into pairs of low-high pairs
952
+
953
+ >>> from sympy.matrices.expressions.blockmatrix import bounds
954
+ >>> bounds((1, 10, 50))
955
+ [(0, 1), (1, 11), (11, 61)]
956
+ """
957
+ low = 0
958
+ rv = []
959
+ for size in sizes:
960
+ rv.append((low, low + size))
961
+ low += size
962
+ return rv
963
+
964
+ def blockcut(expr, rowsizes, colsizes):
965
+ """ Cut a matrix expression into Blocks
966
+
967
+ >>> from sympy import ImmutableMatrix, blockcut
968
+ >>> M = ImmutableMatrix(4, 4, range(16))
969
+ >>> B = blockcut(M, (1, 3), (1, 3))
970
+ >>> type(B).__name__
971
+ 'BlockMatrix'
972
+ >>> ImmutableMatrix(B.blocks[0, 1])
973
+ Matrix([[1, 2, 3]])
974
+ """
975
+
976
+ rowbounds = bounds(rowsizes)
977
+ colbounds = bounds(colsizes)
978
+ return BlockMatrix([[MatrixSlice(expr, rowbound, colbound)
979
+ for colbound in colbounds]
980
+ for rowbound in rowbounds])
.venv/lib/python3.11/site-packages/sympy/matrices/expressions/dotproduct.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core import Basic, Expr
2
+ from sympy.core.sympify import _sympify
3
+ from sympy.matrices.expressions.transpose import transpose
4
+
5
+
6
+ class DotProduct(Expr):
7
+ """
8
+ Dot product of vector matrices
9
+
10
+ The input should be two 1 x n or n x 1 matrices. The output represents the
11
+ scalar dotproduct.
12
+
13
+ This is similar to using MatrixElement and MatMul, except DotProduct does
14
+ not require that one vector to be a row vector and the other vector to be
15
+ a column vector.
16
+
17
+ >>> from sympy import MatrixSymbol, DotProduct
18
+ >>> A = MatrixSymbol('A', 1, 3)
19
+ >>> B = MatrixSymbol('B', 1, 3)
20
+ >>> DotProduct(A, B)
21
+ DotProduct(A, B)
22
+ >>> DotProduct(A, B).doit()
23
+ A[0, 0]*B[0, 0] + A[0, 1]*B[0, 1] + A[0, 2]*B[0, 2]
24
+ """
25
+
26
+ def __new__(cls, arg1, arg2):
27
+ arg1, arg2 = _sympify((arg1, arg2))
28
+
29
+ if not arg1.is_Matrix:
30
+ raise TypeError("Argument 1 of DotProduct is not a matrix")
31
+ if not arg2.is_Matrix:
32
+ raise TypeError("Argument 2 of DotProduct is not a matrix")
33
+ if not (1 in arg1.shape):
34
+ raise TypeError("Argument 1 of DotProduct is not a vector")
35
+ if not (1 in arg2.shape):
36
+ raise TypeError("Argument 2 of DotProduct is not a vector")
37
+
38
+ if set(arg1.shape) != set(arg2.shape):
39
+ raise TypeError("DotProduct arguments are not the same length")
40
+
41
+ return Basic.__new__(cls, arg1, arg2)
42
+
43
+ def doit(self, expand=False, **hints):
44
+ if self.args[0].shape == self.args[1].shape:
45
+ if self.args[0].shape[0] == 1:
46
+ mul = self.args[0]*transpose(self.args[1])
47
+ else:
48
+ mul = transpose(self.args[0])*self.args[1]
49
+ else:
50
+ if self.args[0].shape[0] == 1:
51
+ mul = self.args[0]*self.args[1]
52
+ else:
53
+ mul = transpose(self.args[0])*transpose(self.args[1])
54
+
55
+ return mul[0]
.venv/lib/python3.11/site-packages/sympy/matrices/expressions/factorizations.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.matrices.expressions import MatrixExpr
2
+ from sympy.assumptions.ask import Q
3
+
4
+ class Factorization(MatrixExpr):
5
+ arg = property(lambda self: self.args[0])
6
+ shape = property(lambda self: self.arg.shape) # type: ignore
7
+
8
+ class LofLU(Factorization):
9
+ @property
10
+ def predicates(self):
11
+ return (Q.lower_triangular,)
12
+ class UofLU(Factorization):
13
+ @property
14
+ def predicates(self):
15
+ return (Q.upper_triangular,)
16
+
17
+ class LofCholesky(LofLU): pass
18
+ class UofCholesky(UofLU): pass
19
+
20
+ class QofQR(Factorization):
21
+ @property
22
+ def predicates(self):
23
+ return (Q.orthogonal,)
24
+ class RofQR(Factorization):
25
+ @property
26
+ def predicates(self):
27
+ return (Q.upper_triangular,)
28
+
29
+ class EigenVectors(Factorization):
30
+ @property
31
+ def predicates(self):
32
+ return (Q.orthogonal,)
33
+ class EigenValues(Factorization):
34
+ @property
35
+ def predicates(self):
36
+ return (Q.diagonal,)
37
+
38
+ class UofSVD(Factorization):
39
+ @property
40
+ def predicates(self):
41
+ return (Q.orthogonal,)
42
+ class SofSVD(Factorization):
43
+ @property
44
+ def predicates(self):
45
+ return (Q.diagonal,)
46
+ class VofSVD(Factorization):
47
+ @property
48
+ def predicates(self):
49
+ return (Q.orthogonal,)
50
+
51
+
52
+ def lu(expr):
53
+ return LofLU(expr), UofLU(expr)
54
+
55
+ def qr(expr):
56
+ return QofQR(expr), RofQR(expr)
57
+
58
+ def eig(expr):
59
+ return EigenValues(expr), EigenVectors(expr)
60
+
61
+ def svd(expr):
62
+ return UofSVD(expr), SofSVD(expr), VofSVD(expr)
.venv/lib/python3.11/site-packages/sympy/matrices/expressions/funcmatrix.py ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .matexpr import MatrixExpr
2
+ from sympy.core.function import FunctionClass, Lambda
3
+ from sympy.core.symbol import Dummy
4
+ from sympy.core.sympify import _sympify, sympify
5
+ from sympy.matrices import Matrix
6
+ from sympy.functions.elementary.complexes import re, im
7
+
8
+
9
+ class FunctionMatrix(MatrixExpr):
10
+ """Represents a matrix using a function (``Lambda``) which gives
11
+ outputs according to the coordinates of each matrix entries.
12
+
13
+ Parameters
14
+ ==========
15
+
16
+ rows : nonnegative integer. Can be symbolic.
17
+
18
+ cols : nonnegative integer. Can be symbolic.
19
+
20
+ lamda : Function, Lambda or str
21
+ If it is a SymPy ``Function`` or ``Lambda`` instance,
22
+ it should be able to accept two arguments which represents the
23
+ matrix coordinates.
24
+
25
+ If it is a pure string containing Python ``lambda`` semantics,
26
+ it is interpreted by the SymPy parser and casted into a SymPy
27
+ ``Lambda`` instance.
28
+
29
+ Examples
30
+ ========
31
+
32
+ Creating a ``FunctionMatrix`` from ``Lambda``:
33
+
34
+ >>> from sympy import FunctionMatrix, symbols, Lambda, MatPow
35
+ >>> i, j, n, m = symbols('i,j,n,m')
36
+ >>> FunctionMatrix(n, m, Lambda((i, j), i + j))
37
+ FunctionMatrix(n, m, Lambda((i, j), i + j))
38
+
39
+ Creating a ``FunctionMatrix`` from a SymPy function:
40
+
41
+ >>> from sympy import KroneckerDelta
42
+ >>> X = FunctionMatrix(3, 3, KroneckerDelta)
43
+ >>> X.as_explicit()
44
+ Matrix([
45
+ [1, 0, 0],
46
+ [0, 1, 0],
47
+ [0, 0, 1]])
48
+
49
+ Creating a ``FunctionMatrix`` from a SymPy undefined function:
50
+
51
+ >>> from sympy import Function
52
+ >>> f = Function('f')
53
+ >>> X = FunctionMatrix(3, 3, f)
54
+ >>> X.as_explicit()
55
+ Matrix([
56
+ [f(0, 0), f(0, 1), f(0, 2)],
57
+ [f(1, 0), f(1, 1), f(1, 2)],
58
+ [f(2, 0), f(2, 1), f(2, 2)]])
59
+
60
+ Creating a ``FunctionMatrix`` from Python ``lambda``:
61
+
62
+ >>> FunctionMatrix(n, m, 'lambda i, j: i + j')
63
+ FunctionMatrix(n, m, Lambda((i, j), i + j))
64
+
65
+ Example of lazy evaluation of matrix product:
66
+
67
+ >>> Y = FunctionMatrix(1000, 1000, Lambda((i, j), i + j))
68
+ >>> isinstance(Y*Y, MatPow) # this is an expression object
69
+ True
70
+ >>> (Y**2)[10,10] # So this is evaluated lazily
71
+ 342923500
72
+
73
+ Notes
74
+ =====
75
+
76
+ This class provides an alternative way to represent an extremely
77
+ dense matrix with entries in some form of a sequence, in a most
78
+ sparse way.
79
+ """
80
+ def __new__(cls, rows, cols, lamda):
81
+ rows, cols = _sympify(rows), _sympify(cols)
82
+ cls._check_dim(rows)
83
+ cls._check_dim(cols)
84
+
85
+ lamda = sympify(lamda)
86
+ if not isinstance(lamda, (FunctionClass, Lambda)):
87
+ raise ValueError(
88
+ "{} should be compatible with SymPy function classes."
89
+ .format(lamda))
90
+
91
+ if 2 not in lamda.nargs:
92
+ raise ValueError(
93
+ '{} should be able to accept 2 arguments.'.format(lamda))
94
+
95
+ if not isinstance(lamda, Lambda):
96
+ i, j = Dummy('i'), Dummy('j')
97
+ lamda = Lambda((i, j), lamda(i, j))
98
+
99
+ return super().__new__(cls, rows, cols, lamda)
100
+
101
+ @property
102
+ def shape(self):
103
+ return self.args[0:2]
104
+
105
+ @property
106
+ def lamda(self):
107
+ return self.args[2]
108
+
109
+ def _entry(self, i, j, **kwargs):
110
+ return self.lamda(i, j)
111
+
112
+ def _eval_trace(self):
113
+ from sympy.matrices.expressions.trace import Trace
114
+ from sympy.concrete.summations import Sum
115
+ return Trace(self).rewrite(Sum).doit()
116
+
117
+ def _eval_as_real_imag(self):
118
+ return (re(Matrix(self)), im(Matrix(self)))
.venv/lib/python3.11/site-packages/sympy/matrices/expressions/inverse.py ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core.sympify import _sympify
2
+ from sympy.core import S, Basic
3
+
4
+ from sympy.matrices.exceptions import NonSquareMatrixError
5
+ from sympy.matrices.expressions.matpow import MatPow
6
+
7
+
8
+ class Inverse(MatPow):
9
+ """
10
+ The multiplicative inverse of a matrix expression
11
+
12
+ This is a symbolic object that simply stores its argument without
13
+ evaluating it. To actually compute the inverse, use the ``.inverse()``
14
+ method of matrices.
15
+
16
+ Examples
17
+ ========
18
+
19
+ >>> from sympy import MatrixSymbol, Inverse
20
+ >>> A = MatrixSymbol('A', 3, 3)
21
+ >>> B = MatrixSymbol('B', 3, 3)
22
+ >>> Inverse(A)
23
+ A**(-1)
24
+ >>> A.inverse() == Inverse(A)
25
+ True
26
+ >>> (A*B).inverse()
27
+ B**(-1)*A**(-1)
28
+ >>> Inverse(A*B)
29
+ (A*B)**(-1)
30
+
31
+ """
32
+ is_Inverse = True
33
+ exp = S.NegativeOne
34
+
35
+ def __new__(cls, mat, exp=S.NegativeOne):
36
+ # exp is there to make it consistent with
37
+ # inverse.func(*inverse.args) == inverse
38
+ mat = _sympify(mat)
39
+ exp = _sympify(exp)
40
+ if not mat.is_Matrix:
41
+ raise TypeError("mat should be a matrix")
42
+ if mat.is_square is False:
43
+ raise NonSquareMatrixError("Inverse of non-square matrix %s" % mat)
44
+ return Basic.__new__(cls, mat, exp)
45
+
46
+ @property
47
+ def arg(self):
48
+ return self.args[0]
49
+
50
+ @property
51
+ def shape(self):
52
+ return self.arg.shape
53
+
54
+ def _eval_inverse(self):
55
+ return self.arg
56
+
57
+ def _eval_transpose(self):
58
+ return Inverse(self.arg.transpose())
59
+
60
+ def _eval_adjoint(self):
61
+ return Inverse(self.arg.adjoint())
62
+
63
+ def _eval_conjugate(self):
64
+ return Inverse(self.arg.conjugate())
65
+
66
+ def _eval_determinant(self):
67
+ from sympy.matrices.expressions.determinant import det
68
+ return 1/det(self.arg)
69
+
70
+ def doit(self, **hints):
71
+ if 'inv_expand' in hints and hints['inv_expand'] == False:
72
+ return self
73
+
74
+ arg = self.arg
75
+ if hints.get('deep', True):
76
+ arg = arg.doit(**hints)
77
+
78
+ return arg.inverse()
79
+
80
+ def _eval_derivative_matrix_lines(self, x):
81
+ arg = self.args[0]
82
+ lines = arg._eval_derivative_matrix_lines(x)
83
+ for line in lines:
84
+ line.first_pointer *= -self.T
85
+ line.second_pointer *= self
86
+ return lines
87
+
88
+
89
+ from sympy.assumptions.ask import ask, Q
90
+ from sympy.assumptions.refine import handlers_dict
91
+
92
+
93
+ def refine_Inverse(expr, assumptions):
94
+ """
95
+ >>> from sympy import MatrixSymbol, Q, assuming, refine
96
+ >>> X = MatrixSymbol('X', 2, 2)
97
+ >>> X.I
98
+ X**(-1)
99
+ >>> with assuming(Q.orthogonal(X)):
100
+ ... print(refine(X.I))
101
+ X.T
102
+ """
103
+ if ask(Q.orthogonal(expr), assumptions):
104
+ return expr.arg.T
105
+ elif ask(Q.unitary(expr), assumptions):
106
+ return expr.arg.conjugate()
107
+ elif ask(Q.singular(expr), assumptions):
108
+ raise ValueError("Inverse of singular matrix %s" % expr.arg)
109
+
110
+ return expr
111
+
112
+ handlers_dict['Inverse'] = refine_Inverse
.venv/lib/python3.11/site-packages/sympy/matrices/expressions/kronecker.py ADDED
@@ -0,0 +1,434 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Implementation of the Kronecker product"""
2
+ from functools import reduce
3
+ from math import prod
4
+
5
+ from sympy.core import Mul, sympify
6
+ from sympy.functions import adjoint
7
+ from sympy.matrices.exceptions import ShapeError
8
+ from sympy.matrices.expressions.matexpr import MatrixExpr
9
+ from sympy.matrices.expressions.transpose import transpose
10
+ from sympy.matrices.expressions.special import Identity
11
+ from sympy.matrices.matrixbase import MatrixBase
12
+ from sympy.strategies import (
13
+ canon, condition, distribute, do_one, exhaust, flatten, typed, unpack)
14
+ from sympy.strategies.traverse import bottom_up
15
+ from sympy.utilities import sift
16
+
17
+ from .matadd import MatAdd
18
+ from .matmul import MatMul
19
+ from .matpow import MatPow
20
+
21
+
22
+ def kronecker_product(*matrices):
23
+ """
24
+ The Kronecker product of two or more arguments.
25
+
26
+ This computes the explicit Kronecker product for subclasses of
27
+ ``MatrixBase`` i.e. explicit matrices. Otherwise, a symbolic
28
+ ``KroneckerProduct`` object is returned.
29
+
30
+
31
+ Examples
32
+ ========
33
+
34
+ For ``MatrixSymbol`` arguments a ``KroneckerProduct`` object is returned.
35
+ Elements of this matrix can be obtained by indexing, or for MatrixSymbols
36
+ with known dimension the explicit matrix can be obtained with
37
+ ``.as_explicit()``
38
+
39
+ >>> from sympy import kronecker_product, MatrixSymbol
40
+ >>> A = MatrixSymbol('A', 2, 2)
41
+ >>> B = MatrixSymbol('B', 2, 2)
42
+ >>> kronecker_product(A)
43
+ A
44
+ >>> kronecker_product(A, B)
45
+ KroneckerProduct(A, B)
46
+ >>> kronecker_product(A, B)[0, 1]
47
+ A[0, 0]*B[0, 1]
48
+ >>> kronecker_product(A, B).as_explicit()
49
+ Matrix([
50
+ [A[0, 0]*B[0, 0], A[0, 0]*B[0, 1], A[0, 1]*B[0, 0], A[0, 1]*B[0, 1]],
51
+ [A[0, 0]*B[1, 0], A[0, 0]*B[1, 1], A[0, 1]*B[1, 0], A[0, 1]*B[1, 1]],
52
+ [A[1, 0]*B[0, 0], A[1, 0]*B[0, 1], A[1, 1]*B[0, 0], A[1, 1]*B[0, 1]],
53
+ [A[1, 0]*B[1, 0], A[1, 0]*B[1, 1], A[1, 1]*B[1, 0], A[1, 1]*B[1, 1]]])
54
+
55
+ For explicit matrices the Kronecker product is returned as a Matrix
56
+
57
+ >>> from sympy import Matrix, kronecker_product
58
+ >>> sigma_x = Matrix([
59
+ ... [0, 1],
60
+ ... [1, 0]])
61
+ ...
62
+ >>> Isigma_y = Matrix([
63
+ ... [0, 1],
64
+ ... [-1, 0]])
65
+ ...
66
+ >>> kronecker_product(sigma_x, Isigma_y)
67
+ Matrix([
68
+ [ 0, 0, 0, 1],
69
+ [ 0, 0, -1, 0],
70
+ [ 0, 1, 0, 0],
71
+ [-1, 0, 0, 0]])
72
+
73
+ See Also
74
+ ========
75
+ KroneckerProduct
76
+
77
+ """
78
+ if not matrices:
79
+ raise TypeError("Empty Kronecker product is undefined")
80
+ if len(matrices) == 1:
81
+ return matrices[0]
82
+ else:
83
+ return KroneckerProduct(*matrices).doit()
84
+
85
+
86
+ class KroneckerProduct(MatrixExpr):
87
+ """
88
+ The Kronecker product of two or more arguments.
89
+
90
+ The Kronecker product is a non-commutative product of matrices.
91
+ Given two matrices of dimension (m, n) and (s, t) it produces a matrix
92
+ of dimension (m s, n t).
93
+
94
+ This is a symbolic object that simply stores its argument without
95
+ evaluating it. To actually compute the product, use the function
96
+ ``kronecker_product()`` or call the ``.doit()`` or ``.as_explicit()``
97
+ methods.
98
+
99
+ >>> from sympy import KroneckerProduct, MatrixSymbol
100
+ >>> A = MatrixSymbol('A', 5, 5)
101
+ >>> B = MatrixSymbol('B', 5, 5)
102
+ >>> isinstance(KroneckerProduct(A, B), KroneckerProduct)
103
+ True
104
+ """
105
+ is_KroneckerProduct = True
106
+
107
+ def __new__(cls, *args, check=True):
108
+ args = list(map(sympify, args))
109
+ if all(a.is_Identity for a in args):
110
+ ret = Identity(prod(a.rows for a in args))
111
+ if all(isinstance(a, MatrixBase) for a in args):
112
+ return ret.as_explicit()
113
+ else:
114
+ return ret
115
+
116
+ if check:
117
+ validate(*args)
118
+ return super().__new__(cls, *args)
119
+
120
+ @property
121
+ def shape(self):
122
+ rows, cols = self.args[0].shape
123
+ for mat in self.args[1:]:
124
+ rows *= mat.rows
125
+ cols *= mat.cols
126
+ return (rows, cols)
127
+
128
+ def _entry(self, i, j, **kwargs):
129
+ result = 1
130
+ for mat in reversed(self.args):
131
+ i, m = divmod(i, mat.rows)
132
+ j, n = divmod(j, mat.cols)
133
+ result *= mat[m, n]
134
+ return result
135
+
136
+ def _eval_adjoint(self):
137
+ return KroneckerProduct(*list(map(adjoint, self.args))).doit()
138
+
139
+ def _eval_conjugate(self):
140
+ return KroneckerProduct(*[a.conjugate() for a in self.args]).doit()
141
+
142
+ def _eval_transpose(self):
143
+ return KroneckerProduct(*list(map(transpose, self.args))).doit()
144
+
145
+ def _eval_trace(self):
146
+ from .trace import trace
147
+ return Mul(*[trace(a) for a in self.args])
148
+
149
+ def _eval_determinant(self):
150
+ from .determinant import det, Determinant
151
+ if not all(a.is_square for a in self.args):
152
+ return Determinant(self)
153
+
154
+ m = self.rows
155
+ return Mul(*[det(a)**(m/a.rows) for a in self.args])
156
+
157
+ def _eval_inverse(self):
158
+ try:
159
+ return KroneckerProduct(*[a.inverse() for a in self.args])
160
+ except ShapeError:
161
+ from sympy.matrices.expressions.inverse import Inverse
162
+ return Inverse(self)
163
+
164
+ def structurally_equal(self, other):
165
+ '''Determine whether two matrices have the same Kronecker product structure
166
+
167
+ Examples
168
+ ========
169
+
170
+ >>> from sympy import KroneckerProduct, MatrixSymbol, symbols
171
+ >>> m, n = symbols(r'm, n', integer=True)
172
+ >>> A = MatrixSymbol('A', m, m)
173
+ >>> B = MatrixSymbol('B', n, n)
174
+ >>> C = MatrixSymbol('C', m, m)
175
+ >>> D = MatrixSymbol('D', n, n)
176
+ >>> KroneckerProduct(A, B).structurally_equal(KroneckerProduct(C, D))
177
+ True
178
+ >>> KroneckerProduct(A, B).structurally_equal(KroneckerProduct(D, C))
179
+ False
180
+ >>> KroneckerProduct(A, B).structurally_equal(C)
181
+ False
182
+ '''
183
+ # Inspired by BlockMatrix
184
+ return (isinstance(other, KroneckerProduct)
185
+ and self.shape == other.shape
186
+ and len(self.args) == len(other.args)
187
+ and all(a.shape == b.shape for (a, b) in zip(self.args, other.args)))
188
+
189
+ def has_matching_shape(self, other):
190
+ '''Determine whether two matrices have the appropriate structure to bring matrix
191
+ multiplication inside the KroneckerProdut
192
+
193
+ Examples
194
+ ========
195
+ >>> from sympy import KroneckerProduct, MatrixSymbol, symbols
196
+ >>> m, n = symbols(r'm, n', integer=True)
197
+ >>> A = MatrixSymbol('A', m, n)
198
+ >>> B = MatrixSymbol('B', n, m)
199
+ >>> KroneckerProduct(A, B).has_matching_shape(KroneckerProduct(B, A))
200
+ True
201
+ >>> KroneckerProduct(A, B).has_matching_shape(KroneckerProduct(A, B))
202
+ False
203
+ >>> KroneckerProduct(A, B).has_matching_shape(A)
204
+ False
205
+ '''
206
+ return (isinstance(other, KroneckerProduct)
207
+ and self.cols == other.rows
208
+ and len(self.args) == len(other.args)
209
+ and all(a.cols == b.rows for (a, b) in zip(self.args, other.args)))
210
+
211
+ def _eval_expand_kroneckerproduct(self, **hints):
212
+ return flatten(canon(typed({KroneckerProduct: distribute(KroneckerProduct, MatAdd)}))(self))
213
+
214
+ def _kronecker_add(self, other):
215
+ if self.structurally_equal(other):
216
+ return self.__class__(*[a + b for (a, b) in zip(self.args, other.args)])
217
+ else:
218
+ return self + other
219
+
220
+ def _kronecker_mul(self, other):
221
+ if self.has_matching_shape(other):
222
+ return self.__class__(*[a*b for (a, b) in zip(self.args, other.args)])
223
+ else:
224
+ return self * other
225
+
226
+ def doit(self, **hints):
227
+ deep = hints.get('deep', True)
228
+ if deep:
229
+ args = [arg.doit(**hints) for arg in self.args]
230
+ else:
231
+ args = self.args
232
+ return canonicalize(KroneckerProduct(*args))
233
+
234
+
235
+ def validate(*args):
236
+ if not all(arg.is_Matrix for arg in args):
237
+ raise TypeError("Mix of Matrix and Scalar symbols")
238
+
239
+
240
+ # rules
241
+
242
+ def extract_commutative(kron):
243
+ c_part = []
244
+ nc_part = []
245
+ for arg in kron.args:
246
+ c, nc = arg.args_cnc()
247
+ c_part.extend(c)
248
+ nc_part.append(Mul._from_args(nc))
249
+
250
+ c_part = Mul(*c_part)
251
+ if c_part != 1:
252
+ return c_part*KroneckerProduct(*nc_part)
253
+ return kron
254
+
255
+
256
+ def matrix_kronecker_product(*matrices):
257
+ """Compute the Kronecker product of a sequence of SymPy Matrices.
258
+
259
+ This is the standard Kronecker product of matrices [1].
260
+
261
+ Parameters
262
+ ==========
263
+
264
+ matrices : tuple of MatrixBase instances
265
+ The matrices to take the Kronecker product of.
266
+
267
+ Returns
268
+ =======
269
+
270
+ matrix : MatrixBase
271
+ The Kronecker product matrix.
272
+
273
+ Examples
274
+ ========
275
+
276
+ >>> from sympy import Matrix
277
+ >>> from sympy.matrices.expressions.kronecker import (
278
+ ... matrix_kronecker_product)
279
+
280
+ >>> m1 = Matrix([[1,2],[3,4]])
281
+ >>> m2 = Matrix([[1,0],[0,1]])
282
+ >>> matrix_kronecker_product(m1, m2)
283
+ Matrix([
284
+ [1, 0, 2, 0],
285
+ [0, 1, 0, 2],
286
+ [3, 0, 4, 0],
287
+ [0, 3, 0, 4]])
288
+ >>> matrix_kronecker_product(m2, m1)
289
+ Matrix([
290
+ [1, 2, 0, 0],
291
+ [3, 4, 0, 0],
292
+ [0, 0, 1, 2],
293
+ [0, 0, 3, 4]])
294
+
295
+ References
296
+ ==========
297
+
298
+ .. [1] https://en.wikipedia.org/wiki/Kronecker_product
299
+ """
300
+ # Make sure we have a sequence of Matrices
301
+ if not all(isinstance(m, MatrixBase) for m in matrices):
302
+ raise TypeError(
303
+ 'Sequence of Matrices expected, got: %s' % repr(matrices)
304
+ )
305
+
306
+ # Pull out the first element in the product.
307
+ matrix_expansion = matrices[-1]
308
+ # Do the kronecker product working from right to left.
309
+ for mat in reversed(matrices[:-1]):
310
+ rows = mat.rows
311
+ cols = mat.cols
312
+ # Go through each row appending kronecker product to.
313
+ # running matrix_expansion.
314
+ for i in range(rows):
315
+ start = matrix_expansion*mat[i*cols]
316
+ # Go through each column joining each item
317
+ for j in range(cols - 1):
318
+ start = start.row_join(
319
+ matrix_expansion*mat[i*cols + j + 1]
320
+ )
321
+ # If this is the first element, make it the start of the
322
+ # new row.
323
+ if i == 0:
324
+ next = start
325
+ else:
326
+ next = next.col_join(start)
327
+ matrix_expansion = next
328
+
329
+ MatrixClass = max(matrices, key=lambda M: M._class_priority).__class__
330
+ if isinstance(matrix_expansion, MatrixClass):
331
+ return matrix_expansion
332
+ else:
333
+ return MatrixClass(matrix_expansion)
334
+
335
+
336
+ def explicit_kronecker_product(kron):
337
+ # Make sure we have a sequence of Matrices
338
+ if not all(isinstance(m, MatrixBase) for m in kron.args):
339
+ return kron
340
+
341
+ return matrix_kronecker_product(*kron.args)
342
+
343
+
344
+ rules = (unpack,
345
+ explicit_kronecker_product,
346
+ flatten,
347
+ extract_commutative)
348
+
349
+ canonicalize = exhaust(condition(lambda x: isinstance(x, KroneckerProduct),
350
+ do_one(*rules)))
351
+
352
+
353
+ def _kronecker_dims_key(expr):
354
+ if isinstance(expr, KroneckerProduct):
355
+ return tuple(a.shape for a in expr.args)
356
+ else:
357
+ return (0,)
358
+
359
+
360
+ def kronecker_mat_add(expr):
361
+ args = sift(expr.args, _kronecker_dims_key)
362
+ nonkrons = args.pop((0,), None)
363
+ if not args:
364
+ return expr
365
+
366
+ krons = [reduce(lambda x, y: x._kronecker_add(y), group)
367
+ for group in args.values()]
368
+
369
+ if not nonkrons:
370
+ return MatAdd(*krons)
371
+ else:
372
+ return MatAdd(*krons) + nonkrons
373
+
374
+
375
+ def kronecker_mat_mul(expr):
376
+ # modified from block matrix code
377
+ factor, matrices = expr.as_coeff_matrices()
378
+
379
+ i = 0
380
+ while i < len(matrices) - 1:
381
+ A, B = matrices[i:i+2]
382
+ if isinstance(A, KroneckerProduct) and isinstance(B, KroneckerProduct):
383
+ matrices[i] = A._kronecker_mul(B)
384
+ matrices.pop(i+1)
385
+ else:
386
+ i += 1
387
+
388
+ return factor*MatMul(*matrices)
389
+
390
+
391
+ def kronecker_mat_pow(expr):
392
+ if isinstance(expr.base, KroneckerProduct) and all(a.is_square for a in expr.base.args):
393
+ return KroneckerProduct(*[MatPow(a, expr.exp) for a in expr.base.args])
394
+ else:
395
+ return expr
396
+
397
+
398
+ def combine_kronecker(expr):
399
+ """Combine KronekeckerProduct with expression.
400
+
401
+ If possible write operations on KroneckerProducts of compatible shapes
402
+ as a single KroneckerProduct.
403
+
404
+ Examples
405
+ ========
406
+
407
+ >>> from sympy.matrices.expressions import combine_kronecker
408
+ >>> from sympy import MatrixSymbol, KroneckerProduct, symbols
409
+ >>> m, n = symbols(r'm, n', integer=True)
410
+ >>> A = MatrixSymbol('A', m, n)
411
+ >>> B = MatrixSymbol('B', n, m)
412
+ >>> combine_kronecker(KroneckerProduct(A, B)*KroneckerProduct(B, A))
413
+ KroneckerProduct(A*B, B*A)
414
+ >>> combine_kronecker(KroneckerProduct(A, B)+KroneckerProduct(B.T, A.T))
415
+ KroneckerProduct(A + B.T, B + A.T)
416
+ >>> C = MatrixSymbol('C', n, n)
417
+ >>> D = MatrixSymbol('D', m, m)
418
+ >>> combine_kronecker(KroneckerProduct(C, D)**m)
419
+ KroneckerProduct(C**m, D**m)
420
+ """
421
+ def haskron(expr):
422
+ return isinstance(expr, MatrixExpr) and expr.has(KroneckerProduct)
423
+
424
+ rule = exhaust(
425
+ bottom_up(exhaust(condition(haskron, typed(
426
+ {MatAdd: kronecker_mat_add,
427
+ MatMul: kronecker_mat_mul,
428
+ MatPow: kronecker_mat_pow})))))
429
+ result = rule(expr)
430
+ doit = getattr(result, 'doit', None)
431
+ if doit is not None:
432
+ return doit()
433
+ else:
434
+ return result
.venv/lib/python3.11/site-packages/sympy/matrices/expressions/matexpr.py ADDED
@@ -0,0 +1,888 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+ from functools import wraps
3
+
4
+ from sympy.core import S, Integer, Basic, Mul, Add
5
+ from sympy.core.assumptions import check_assumptions
6
+ from sympy.core.decorators import call_highest_priority
7
+ from sympy.core.expr import Expr, ExprBuilder
8
+ from sympy.core.logic import FuzzyBool
9
+ from sympy.core.symbol import Str, Dummy, symbols, Symbol
10
+ from sympy.core.sympify import SympifyError, _sympify
11
+ from sympy.external.gmpy import SYMPY_INTS
12
+ from sympy.functions import conjugate, adjoint
13
+ from sympy.functions.special.tensor_functions import KroneckerDelta
14
+ from sympy.matrices.exceptions import NonSquareMatrixError
15
+ from sympy.matrices.kind import MatrixKind
16
+ from sympy.matrices.matrixbase import MatrixBase
17
+ from sympy.multipledispatch import dispatch
18
+ from sympy.utilities.misc import filldedent
19
+
20
+
21
+ def _sympifyit(arg, retval=None):
22
+ # This version of _sympifyit sympifies MutableMatrix objects
23
+ def deco(func):
24
+ @wraps(func)
25
+ def __sympifyit_wrapper(a, b):
26
+ try:
27
+ b = _sympify(b)
28
+ return func(a, b)
29
+ except SympifyError:
30
+ return retval
31
+
32
+ return __sympifyit_wrapper
33
+
34
+ return deco
35
+
36
+
37
+ class MatrixExpr(Expr):
38
+ """Superclass for Matrix Expressions
39
+
40
+ MatrixExprs represent abstract matrices, linear transformations represented
41
+ within a particular basis.
42
+
43
+ Examples
44
+ ========
45
+
46
+ >>> from sympy import MatrixSymbol
47
+ >>> A = MatrixSymbol('A', 3, 3)
48
+ >>> y = MatrixSymbol('y', 3, 1)
49
+ >>> x = (A.T*A).I * A * y
50
+
51
+ See Also
52
+ ========
53
+
54
+ MatrixSymbol, MatAdd, MatMul, Transpose, Inverse
55
+ """
56
+ __slots__: tuple[str, ...] = ()
57
+
58
+ # Should not be considered iterable by the
59
+ # sympy.utilities.iterables.iterable function. Subclass that actually are
60
+ # iterable (i.e., explicit matrices) should set this to True.
61
+ _iterable = False
62
+
63
+ _op_priority = 11.0
64
+
65
+ is_Matrix: bool = True
66
+ is_MatrixExpr: bool = True
67
+ is_Identity: FuzzyBool = None
68
+ is_Inverse = False
69
+ is_Transpose = False
70
+ is_ZeroMatrix = False
71
+ is_MatAdd = False
72
+ is_MatMul = False
73
+
74
+ is_commutative = False
75
+ is_number = False
76
+ is_symbol = False
77
+ is_scalar = False
78
+
79
+ kind: MatrixKind = MatrixKind()
80
+
81
+ def __new__(cls, *args, **kwargs):
82
+ args = map(_sympify, args)
83
+ return Basic.__new__(cls, *args, **kwargs)
84
+
85
+ # The following is adapted from the core Expr object
86
+
87
+ @property
88
+ def shape(self) -> tuple[Expr, Expr]:
89
+ raise NotImplementedError
90
+
91
+ @property
92
+ def _add_handler(self):
93
+ return MatAdd
94
+
95
+ @property
96
+ def _mul_handler(self):
97
+ return MatMul
98
+
99
+ def __neg__(self):
100
+ return MatMul(S.NegativeOne, self).doit()
101
+
102
+ def __abs__(self):
103
+ raise NotImplementedError
104
+
105
+ @_sympifyit('other', NotImplemented)
106
+ @call_highest_priority('__radd__')
107
+ def __add__(self, other):
108
+ return MatAdd(self, other).doit()
109
+
110
+ @_sympifyit('other', NotImplemented)
111
+ @call_highest_priority('__add__')
112
+ def __radd__(self, other):
113
+ return MatAdd(other, self).doit()
114
+
115
+ @_sympifyit('other', NotImplemented)
116
+ @call_highest_priority('__rsub__')
117
+ def __sub__(self, other):
118
+ return MatAdd(self, -other).doit()
119
+
120
+ @_sympifyit('other', NotImplemented)
121
+ @call_highest_priority('__sub__')
122
+ def __rsub__(self, other):
123
+ return MatAdd(other, -self).doit()
124
+
125
+ @_sympifyit('other', NotImplemented)
126
+ @call_highest_priority('__rmul__')
127
+ def __mul__(self, other):
128
+ return MatMul(self, other).doit()
129
+
130
+ @_sympifyit('other', NotImplemented)
131
+ @call_highest_priority('__rmul__')
132
+ def __matmul__(self, other):
133
+ return MatMul(self, other).doit()
134
+
135
+ @_sympifyit('other', NotImplemented)
136
+ @call_highest_priority('__mul__')
137
+ def __rmul__(self, other):
138
+ return MatMul(other, self).doit()
139
+
140
+ @_sympifyit('other', NotImplemented)
141
+ @call_highest_priority('__mul__')
142
+ def __rmatmul__(self, other):
143
+ return MatMul(other, self).doit()
144
+
145
+ @_sympifyit('other', NotImplemented)
146
+ @call_highest_priority('__rpow__')
147
+ def __pow__(self, other):
148
+ return MatPow(self, other).doit()
149
+
150
+ @_sympifyit('other', NotImplemented)
151
+ @call_highest_priority('__pow__')
152
+ def __rpow__(self, other):
153
+ raise NotImplementedError("Matrix Power not defined")
154
+
155
+ @_sympifyit('other', NotImplemented)
156
+ @call_highest_priority('__rtruediv__')
157
+ def __truediv__(self, other):
158
+ return self * other**S.NegativeOne
159
+
160
+ @_sympifyit('other', NotImplemented)
161
+ @call_highest_priority('__truediv__')
162
+ def __rtruediv__(self, other):
163
+ raise NotImplementedError()
164
+ #return MatMul(other, Pow(self, S.NegativeOne))
165
+
166
+ @property
167
+ def rows(self):
168
+ return self.shape[0]
169
+
170
+ @property
171
+ def cols(self):
172
+ return self.shape[1]
173
+
174
+ @property
175
+ def is_square(self) -> bool | None:
176
+ rows, cols = self.shape
177
+ if isinstance(rows, Integer) and isinstance(cols, Integer):
178
+ return rows == cols
179
+ if rows == cols:
180
+ return True
181
+ return None
182
+
183
+ def _eval_conjugate(self):
184
+ from sympy.matrices.expressions.adjoint import Adjoint
185
+ return Adjoint(Transpose(self))
186
+
187
+ def as_real_imag(self, deep=True, **hints):
188
+ return self._eval_as_real_imag()
189
+
190
+ def _eval_as_real_imag(self):
191
+ real = S.Half * (self + self._eval_conjugate())
192
+ im = (self - self._eval_conjugate())/(2*S.ImaginaryUnit)
193
+ return (real, im)
194
+
195
+ def _eval_inverse(self):
196
+ return Inverse(self)
197
+
198
+ def _eval_determinant(self):
199
+ return Determinant(self)
200
+
201
+ def _eval_transpose(self):
202
+ return Transpose(self)
203
+
204
+ def _eval_trace(self):
205
+ return None
206
+
207
+ def _eval_power(self, exp):
208
+ """
209
+ Override this in sub-classes to implement simplification of powers. The cases where the exponent
210
+ is -1, 0, 1 are already covered in MatPow.doit(), so implementations can exclude these cases.
211
+ """
212
+ return MatPow(self, exp)
213
+
214
+ def _eval_simplify(self, **kwargs):
215
+ if self.is_Atom:
216
+ return self
217
+ else:
218
+ from sympy.simplify import simplify
219
+ return self.func(*[simplify(x, **kwargs) for x in self.args])
220
+
221
+ def _eval_adjoint(self):
222
+ from sympy.matrices.expressions.adjoint import Adjoint
223
+ return Adjoint(self)
224
+
225
+ def _eval_derivative_n_times(self, x, n):
226
+ return Basic._eval_derivative_n_times(self, x, n)
227
+
228
+ def _eval_derivative(self, x):
229
+ # `x` is a scalar:
230
+ if self.has(x):
231
+ # See if there are other methods using it:
232
+ return super()._eval_derivative(x)
233
+ else:
234
+ return ZeroMatrix(*self.shape)
235
+
236
+ @classmethod
237
+ def _check_dim(cls, dim):
238
+ """Helper function to check invalid matrix dimensions"""
239
+ ok = not dim.is_Float and check_assumptions(
240
+ dim, integer=True, nonnegative=True)
241
+ if ok is False:
242
+ raise ValueError(
243
+ "The dimension specification {} should be "
244
+ "a nonnegative integer.".format(dim))
245
+
246
+
247
+ def _entry(self, i, j, **kwargs):
248
+ raise NotImplementedError(
249
+ "Indexing not implemented for %s" % self.__class__.__name__)
250
+
251
+ def adjoint(self):
252
+ return adjoint(self)
253
+
254
+ def as_coeff_Mul(self, rational=False):
255
+ """Efficiently extract the coefficient of a product."""
256
+ return S.One, self
257
+
258
+ def conjugate(self):
259
+ return conjugate(self)
260
+
261
+ def transpose(self):
262
+ from sympy.matrices.expressions.transpose import transpose
263
+ return transpose(self)
264
+
265
+ @property
266
+ def T(self):
267
+ '''Matrix transposition'''
268
+ return self.transpose()
269
+
270
+ def inverse(self):
271
+ if self.is_square is False:
272
+ raise NonSquareMatrixError('Inverse of non-square matrix')
273
+ return self._eval_inverse()
274
+
275
+ def inv(self):
276
+ return self.inverse()
277
+
278
+ def det(self):
279
+ from sympy.matrices.expressions.determinant import det
280
+ return det(self)
281
+
282
+ @property
283
+ def I(self):
284
+ return self.inverse()
285
+
286
+ def valid_index(self, i, j):
287
+ def is_valid(idx):
288
+ return isinstance(idx, (int, Integer, Symbol, Expr))
289
+ return (is_valid(i) and is_valid(j) and
290
+ (self.rows is None or
291
+ (i >= -self.rows) != False and (i < self.rows) != False) and
292
+ (j >= -self.cols) != False and (j < self.cols) != False)
293
+
294
+ def __getitem__(self, key):
295
+ if not isinstance(key, tuple) and isinstance(key, slice):
296
+ from sympy.matrices.expressions.slice import MatrixSlice
297
+ return MatrixSlice(self, key, (0, None, 1))
298
+ if isinstance(key, tuple) and len(key) == 2:
299
+ i, j = key
300
+ if isinstance(i, slice) or isinstance(j, slice):
301
+ from sympy.matrices.expressions.slice import MatrixSlice
302
+ return MatrixSlice(self, i, j)
303
+ i, j = _sympify(i), _sympify(j)
304
+ if self.valid_index(i, j) != False:
305
+ return self._entry(i, j)
306
+ else:
307
+ raise IndexError("Invalid indices (%s, %s)" % (i, j))
308
+ elif isinstance(key, (SYMPY_INTS, Integer)):
309
+ # row-wise decomposition of matrix
310
+ rows, cols = self.shape
311
+ # allow single indexing if number of columns is known
312
+ if not isinstance(cols, Integer):
313
+ raise IndexError(filldedent('''
314
+ Single indexing is only supported when the number
315
+ of columns is known.'''))
316
+ key = _sympify(key)
317
+ i = key // cols
318
+ j = key % cols
319
+ if self.valid_index(i, j) != False:
320
+ return self._entry(i, j)
321
+ else:
322
+ raise IndexError("Invalid index %s" % key)
323
+ elif isinstance(key, (Symbol, Expr)):
324
+ raise IndexError(filldedent('''
325
+ Only integers may be used when addressing the matrix
326
+ with a single index.'''))
327
+ raise IndexError("Invalid index, wanted %s[i,j]" % self)
328
+
329
+ def _is_shape_symbolic(self) -> bool:
330
+ return (not isinstance(self.rows, (SYMPY_INTS, Integer))
331
+ or not isinstance(self.cols, (SYMPY_INTS, Integer)))
332
+
333
+ def as_explicit(self):
334
+ """
335
+ Returns a dense Matrix with elements represented explicitly
336
+
337
+ Returns an object of type ImmutableDenseMatrix.
338
+
339
+ Examples
340
+ ========
341
+
342
+ >>> from sympy import Identity
343
+ >>> I = Identity(3)
344
+ >>> I
345
+ I
346
+ >>> I.as_explicit()
347
+ Matrix([
348
+ [1, 0, 0],
349
+ [0, 1, 0],
350
+ [0, 0, 1]])
351
+
352
+ See Also
353
+ ========
354
+ as_mutable: returns mutable Matrix type
355
+
356
+ """
357
+ if self._is_shape_symbolic():
358
+ raise ValueError(
359
+ 'Matrix with symbolic shape '
360
+ 'cannot be represented explicitly.')
361
+ from sympy.matrices.immutable import ImmutableDenseMatrix
362
+ return ImmutableDenseMatrix([[self[i, j]
363
+ for j in range(self.cols)]
364
+ for i in range(self.rows)])
365
+
366
+ def as_mutable(self):
367
+ """
368
+ Returns a dense, mutable matrix with elements represented explicitly
369
+
370
+ Examples
371
+ ========
372
+
373
+ >>> from sympy import Identity
374
+ >>> I = Identity(3)
375
+ >>> I
376
+ I
377
+ >>> I.shape
378
+ (3, 3)
379
+ >>> I.as_mutable()
380
+ Matrix([
381
+ [1, 0, 0],
382
+ [0, 1, 0],
383
+ [0, 0, 1]])
384
+
385
+ See Also
386
+ ========
387
+ as_explicit: returns ImmutableDenseMatrix
388
+ """
389
+ return self.as_explicit().as_mutable()
390
+
391
+ def __array__(self, dtype=object, copy=None):
392
+ if copy is not None and not copy:
393
+ raise TypeError("Cannot implement copy=False when converting Matrix to ndarray")
394
+ from numpy import empty
395
+ a = empty(self.shape, dtype=object)
396
+ for i in range(self.rows):
397
+ for j in range(self.cols):
398
+ a[i, j] = self[i, j]
399
+ return a
400
+
401
+ def equals(self, other):
402
+ """
403
+ Test elementwise equality between matrices, potentially of different
404
+ types
405
+
406
+ >>> from sympy import Identity, eye
407
+ >>> Identity(3).equals(eye(3))
408
+ True
409
+ """
410
+ return self.as_explicit().equals(other)
411
+
412
+ def canonicalize(self):
413
+ return self
414
+
415
+ def as_coeff_mmul(self):
416
+ return S.One, MatMul(self)
417
+
418
+ @staticmethod
419
+ def from_index_summation(expr, first_index=None, last_index=None, dimensions=None):
420
+ r"""
421
+ Parse expression of matrices with explicitly summed indices into a
422
+ matrix expression without indices, if possible.
423
+
424
+ This transformation expressed in mathematical notation:
425
+
426
+ `\sum_{j=0}^{N-1} A_{i,j} B_{j,k} \Longrightarrow \mathbf{A}\cdot \mathbf{B}`
427
+
428
+ Optional parameter ``first_index``: specify which free index to use as
429
+ the index starting the expression.
430
+
431
+ Examples
432
+ ========
433
+
434
+ >>> from sympy import MatrixSymbol, MatrixExpr, Sum
435
+ >>> from sympy.abc import i, j, k, l, N
436
+ >>> A = MatrixSymbol("A", N, N)
437
+ >>> B = MatrixSymbol("B", N, N)
438
+ >>> expr = Sum(A[i, j]*B[j, k], (j, 0, N-1))
439
+ >>> MatrixExpr.from_index_summation(expr)
440
+ A*B
441
+
442
+ Transposition is detected:
443
+
444
+ >>> expr = Sum(A[j, i]*B[j, k], (j, 0, N-1))
445
+ >>> MatrixExpr.from_index_summation(expr)
446
+ A.T*B
447
+
448
+ Detect the trace:
449
+
450
+ >>> expr = Sum(A[i, i], (i, 0, N-1))
451
+ >>> MatrixExpr.from_index_summation(expr)
452
+ Trace(A)
453
+
454
+ More complicated expressions:
455
+
456
+ >>> expr = Sum(A[i, j]*B[k, j]*A[l, k], (j, 0, N-1), (k, 0, N-1))
457
+ >>> MatrixExpr.from_index_summation(expr)
458
+ A*B.T*A.T
459
+ """
460
+ from sympy.tensor.array.expressions.from_indexed_to_array import convert_indexed_to_array
461
+ from sympy.tensor.array.expressions.from_array_to_matrix import convert_array_to_matrix
462
+ first_indices = []
463
+ if first_index is not None:
464
+ first_indices.append(first_index)
465
+ if last_index is not None:
466
+ first_indices.append(last_index)
467
+ arr = convert_indexed_to_array(expr, first_indices=first_indices)
468
+ return convert_array_to_matrix(arr)
469
+
470
+ def applyfunc(self, func):
471
+ from .applyfunc import ElementwiseApplyFunction
472
+ return ElementwiseApplyFunction(func, self)
473
+
474
+
475
+ @dispatch(MatrixExpr, Expr)
476
+ def _eval_is_eq(lhs, rhs): # noqa:F811
477
+ return False
478
+
479
+ @dispatch(MatrixExpr, MatrixExpr) # type: ignore
480
+ def _eval_is_eq(lhs, rhs): # noqa:F811
481
+ if lhs.shape != rhs.shape:
482
+ return False
483
+ if (lhs - rhs).is_ZeroMatrix:
484
+ return True
485
+
486
+ def get_postprocessor(cls):
487
+ def _postprocessor(expr):
488
+ # To avoid circular imports, we can't have MatMul/MatAdd on the top level
489
+ mat_class = {Mul: MatMul, Add: MatAdd}[cls]
490
+ nonmatrices = []
491
+ matrices = []
492
+ for term in expr.args:
493
+ if isinstance(term, MatrixExpr):
494
+ matrices.append(term)
495
+ else:
496
+ nonmatrices.append(term)
497
+
498
+ if not matrices:
499
+ return cls._from_args(nonmatrices)
500
+
501
+ if nonmatrices:
502
+ if cls == Mul:
503
+ for i in range(len(matrices)):
504
+ if not matrices[i].is_MatrixExpr:
505
+ # If one of the matrices explicit, absorb the scalar into it
506
+ # (doit will combine all explicit matrices into one, so it
507
+ # doesn't matter which)
508
+ matrices[i] = matrices[i].__mul__(cls._from_args(nonmatrices))
509
+ nonmatrices = []
510
+ break
511
+
512
+ else:
513
+ # Maintain the ability to create Add(scalar, matrix) without
514
+ # raising an exception. That way different algorithms can
515
+ # replace matrix expressions with non-commutative symbols to
516
+ # manipulate them like non-commutative scalars.
517
+ return cls._from_args(nonmatrices + [mat_class(*matrices).doit(deep=False)])
518
+
519
+ if mat_class == MatAdd:
520
+ return mat_class(*matrices).doit(deep=False)
521
+ return mat_class(cls._from_args(nonmatrices), *matrices).doit(deep=False)
522
+ return _postprocessor
523
+
524
+
525
+ Basic._constructor_postprocessor_mapping[MatrixExpr] = {
526
+ "Mul": [get_postprocessor(Mul)],
527
+ "Add": [get_postprocessor(Add)],
528
+ }
529
+
530
+
531
+ def _matrix_derivative(expr, x, old_algorithm=False):
532
+
533
+ if isinstance(expr, MatrixBase) or isinstance(x, MatrixBase):
534
+ # Do not use array expressions for explicit matrices:
535
+ old_algorithm = True
536
+
537
+ if old_algorithm:
538
+ return _matrix_derivative_old_algorithm(expr, x)
539
+
540
+ from sympy.tensor.array.expressions.from_matrix_to_array import convert_matrix_to_array
541
+ from sympy.tensor.array.expressions.arrayexpr_derivatives import array_derive
542
+ from sympy.tensor.array.expressions.from_array_to_matrix import convert_array_to_matrix
543
+
544
+ array_expr = convert_matrix_to_array(expr)
545
+ diff_array_expr = array_derive(array_expr, x)
546
+ diff_matrix_expr = convert_array_to_matrix(diff_array_expr)
547
+ return diff_matrix_expr
548
+
549
+
550
+ def _matrix_derivative_old_algorithm(expr, x):
551
+ from sympy.tensor.array.array_derivatives import ArrayDerivative
552
+ lines = expr._eval_derivative_matrix_lines(x)
553
+
554
+ parts = [i.build() for i in lines]
555
+
556
+ from sympy.tensor.array.expressions.from_array_to_matrix import convert_array_to_matrix
557
+
558
+ parts = [[convert_array_to_matrix(j) for j in i] for i in parts]
559
+
560
+ def _get_shape(elem):
561
+ if isinstance(elem, MatrixExpr):
562
+ return elem.shape
563
+ return 1, 1
564
+
565
+ def get_rank(parts):
566
+ return sum(j not in (1, None) for i in parts for j in _get_shape(i))
567
+
568
+ ranks = [get_rank(i) for i in parts]
569
+ rank = ranks[0]
570
+
571
+ def contract_one_dims(parts):
572
+ if len(parts) == 1:
573
+ return parts[0]
574
+ else:
575
+ p1, p2 = parts[:2]
576
+ if p2.is_Matrix:
577
+ p2 = p2.T
578
+ if p1 == Identity(1):
579
+ pbase = p2
580
+ elif p2 == Identity(1):
581
+ pbase = p1
582
+ else:
583
+ pbase = p1*p2
584
+ if len(parts) == 2:
585
+ return pbase
586
+ else: # len(parts) > 2
587
+ if pbase.is_Matrix:
588
+ raise ValueError("")
589
+ return pbase*Mul.fromiter(parts[2:])
590
+
591
+ if rank <= 2:
592
+ return Add.fromiter([contract_one_dims(i) for i in parts])
593
+
594
+ return ArrayDerivative(expr, x)
595
+
596
+
597
+ class MatrixElement(Expr):
598
+ parent = property(lambda self: self.args[0])
599
+ i = property(lambda self: self.args[1])
600
+ j = property(lambda self: self.args[2])
601
+ _diff_wrt = True
602
+ is_symbol = True
603
+ is_commutative = True
604
+
605
+ def __new__(cls, name, n, m):
606
+ n, m = map(_sympify, (n, m))
607
+ if isinstance(name, str):
608
+ name = Symbol(name)
609
+ else:
610
+ if isinstance(name, MatrixBase):
611
+ if n.is_Integer and m.is_Integer:
612
+ return name[n, m]
613
+ name = _sympify(name) # change mutable into immutable
614
+ else:
615
+ name = _sympify(name)
616
+ if not isinstance(name.kind, MatrixKind):
617
+ raise TypeError("First argument of MatrixElement should be a matrix")
618
+ if not getattr(name, 'valid_index', lambda n, m: True)(n, m):
619
+ raise IndexError('indices out of range')
620
+ obj = Expr.__new__(cls, name, n, m)
621
+ return obj
622
+
623
+ @property
624
+ def symbol(self):
625
+ return self.args[0]
626
+
627
+ def doit(self, **hints):
628
+ deep = hints.get('deep', True)
629
+ if deep:
630
+ args = [arg.doit(**hints) for arg in self.args]
631
+ else:
632
+ args = self.args
633
+ return args[0][args[1], args[2]]
634
+
635
+ @property
636
+ def indices(self):
637
+ return self.args[1:]
638
+
639
+ def _eval_derivative(self, v):
640
+
641
+ if not isinstance(v, MatrixElement):
642
+ return self.parent.diff(v)[self.i, self.j]
643
+
644
+ M = self.args[0]
645
+
646
+ m, n = self.parent.shape
647
+
648
+ if M == v.args[0]:
649
+ return KroneckerDelta(self.args[1], v.args[1], (0, m-1)) * \
650
+ KroneckerDelta(self.args[2], v.args[2], (0, n-1))
651
+
652
+ if isinstance(M, Inverse):
653
+ from sympy.concrete.summations import Sum
654
+ i, j = self.args[1:]
655
+ i1, i2 = symbols("z1, z2", cls=Dummy)
656
+ Y = M.args[0]
657
+ r1, r2 = Y.shape
658
+ return -Sum(M[i, i1]*Y[i1, i2].diff(v)*M[i2, j], (i1, 0, r1-1), (i2, 0, r2-1))
659
+
660
+ if self.has(v.args[0]):
661
+ return None
662
+
663
+ return S.Zero
664
+
665
+
666
+ class MatrixSymbol(MatrixExpr):
667
+ """Symbolic representation of a Matrix object
668
+
669
+ Creates a SymPy Symbol to represent a Matrix. This matrix has a shape and
670
+ can be included in Matrix Expressions
671
+
672
+ Examples
673
+ ========
674
+
675
+ >>> from sympy import MatrixSymbol, Identity
676
+ >>> A = MatrixSymbol('A', 3, 4) # A 3 by 4 Matrix
677
+ >>> B = MatrixSymbol('B', 4, 3) # A 4 by 3 Matrix
678
+ >>> A.shape
679
+ (3, 4)
680
+ >>> 2*A*B + Identity(3)
681
+ I + 2*A*B
682
+ """
683
+ is_commutative = False
684
+ is_symbol = True
685
+ _diff_wrt = True
686
+
687
+ def __new__(cls, name, n, m):
688
+ n, m = _sympify(n), _sympify(m)
689
+
690
+ cls._check_dim(m)
691
+ cls._check_dim(n)
692
+
693
+ if isinstance(name, str):
694
+ name = Str(name)
695
+ obj = Basic.__new__(cls, name, n, m)
696
+ return obj
697
+
698
+ @property
699
+ def shape(self):
700
+ return self.args[1], self.args[2]
701
+
702
+ @property
703
+ def name(self):
704
+ return self.args[0].name
705
+
706
+ def _entry(self, i, j, **kwargs):
707
+ return MatrixElement(self, i, j)
708
+
709
+ @property
710
+ def free_symbols(self):
711
+ return {self}
712
+
713
+ def _eval_simplify(self, **kwargs):
714
+ return self
715
+
716
+ def _eval_derivative(self, x):
717
+ # x is a scalar:
718
+ return ZeroMatrix(self.shape[0], self.shape[1])
719
+
720
+ def _eval_derivative_matrix_lines(self, x):
721
+ if self != x:
722
+ first = ZeroMatrix(x.shape[0], self.shape[0]) if self.shape[0] != 1 else S.Zero
723
+ second = ZeroMatrix(x.shape[1], self.shape[1]) if self.shape[1] != 1 else S.Zero
724
+ return [_LeftRightArgs(
725
+ [first, second],
726
+ )]
727
+ else:
728
+ first = Identity(self.shape[0]) if self.shape[0] != 1 else S.One
729
+ second = Identity(self.shape[1]) if self.shape[1] != 1 else S.One
730
+ return [_LeftRightArgs(
731
+ [first, second],
732
+ )]
733
+
734
+
735
+ def matrix_symbols(expr):
736
+ return [sym for sym in expr.free_symbols if sym.is_Matrix]
737
+
738
+
739
+ class _LeftRightArgs:
740
+ r"""
741
+ Helper class to compute matrix derivatives.
742
+
743
+ The logic: when an expression is derived by a matrix `X_{mn}`, two lines of
744
+ matrix multiplications are created: the one contracted to `m` (first line),
745
+ and the one contracted to `n` (second line).
746
+
747
+ Transposition flips the side by which new matrices are connected to the
748
+ lines.
749
+
750
+ The trace connects the end of the two lines.
751
+ """
752
+
753
+ def __init__(self, lines, higher=S.One):
754
+ self._lines = list(lines)
755
+ self._first_pointer_parent = self._lines
756
+ self._first_pointer_index = 0
757
+ self._first_line_index = 0
758
+ self._second_pointer_parent = self._lines
759
+ self._second_pointer_index = 1
760
+ self._second_line_index = 1
761
+ self.higher = higher
762
+
763
+ @property
764
+ def first_pointer(self):
765
+ return self._first_pointer_parent[self._first_pointer_index]
766
+
767
+ @first_pointer.setter
768
+ def first_pointer(self, value):
769
+ self._first_pointer_parent[self._first_pointer_index] = value
770
+
771
+ @property
772
+ def second_pointer(self):
773
+ return self._second_pointer_parent[self._second_pointer_index]
774
+
775
+ @second_pointer.setter
776
+ def second_pointer(self, value):
777
+ self._second_pointer_parent[self._second_pointer_index] = value
778
+
779
+ def __repr__(self):
780
+ built = [self._build(i) for i in self._lines]
781
+ return "_LeftRightArgs(lines=%s, higher=%s)" % (
782
+ built,
783
+ self.higher,
784
+ )
785
+
786
+ def transpose(self):
787
+ self._first_pointer_parent, self._second_pointer_parent = self._second_pointer_parent, self._first_pointer_parent
788
+ self._first_pointer_index, self._second_pointer_index = self._second_pointer_index, self._first_pointer_index
789
+ self._first_line_index, self._second_line_index = self._second_line_index, self._first_line_index
790
+ return self
791
+
792
+ @staticmethod
793
+ def _build(expr):
794
+ if isinstance(expr, ExprBuilder):
795
+ return expr.build()
796
+ if isinstance(expr, list):
797
+ if len(expr) == 1:
798
+ return expr[0]
799
+ else:
800
+ return expr[0](*[_LeftRightArgs._build(i) for i in expr[1]])
801
+ else:
802
+ return expr
803
+
804
+ def build(self):
805
+ data = [self._build(i) for i in self._lines]
806
+ if self.higher != 1:
807
+ data += [self._build(self.higher)]
808
+ data = list(data)
809
+ return data
810
+
811
+ def matrix_form(self):
812
+ if self.first != 1 and self.higher != 1:
813
+ raise ValueError("higher dimensional array cannot be represented")
814
+
815
+ def _get_shape(elem):
816
+ if isinstance(elem, MatrixExpr):
817
+ return elem.shape
818
+ return (None, None)
819
+
820
+ if _get_shape(self.first)[1] != _get_shape(self.second)[1]:
821
+ # Remove one-dimensional identity matrices:
822
+ # (this is needed by `a.diff(a)` where `a` is a vector)
823
+ if _get_shape(self.second) == (1, 1):
824
+ return self.first*self.second[0, 0]
825
+ if _get_shape(self.first) == (1, 1):
826
+ return self.first[1, 1]*self.second.T
827
+ raise ValueError("incompatible shapes")
828
+ if self.first != 1:
829
+ return self.first*self.second.T
830
+ else:
831
+ return self.higher
832
+
833
+ def rank(self):
834
+ """
835
+ Number of dimensions different from trivial (warning: not related to
836
+ matrix rank).
837
+ """
838
+ rank = 0
839
+ if self.first != 1:
840
+ rank += sum(i != 1 for i in self.first.shape)
841
+ if self.second != 1:
842
+ rank += sum(i != 1 for i in self.second.shape)
843
+ if self.higher != 1:
844
+ rank += 2
845
+ return rank
846
+
847
+ def _multiply_pointer(self, pointer, other):
848
+ from ...tensor.array.expressions.array_expressions import ArrayTensorProduct
849
+ from ...tensor.array.expressions.array_expressions import ArrayContraction
850
+
851
+ subexpr = ExprBuilder(
852
+ ArrayContraction,
853
+ [
854
+ ExprBuilder(
855
+ ArrayTensorProduct,
856
+ [
857
+ pointer,
858
+ other
859
+ ]
860
+ ),
861
+ (1, 2)
862
+ ],
863
+ validator=ArrayContraction._validate
864
+ )
865
+
866
+ return subexpr
867
+
868
+ def append_first(self, other):
869
+ self.first_pointer *= other
870
+
871
+ def append_second(self, other):
872
+ self.second_pointer *= other
873
+
874
+
875
+ def _make_matrix(x):
876
+ from sympy.matrices.immutable import ImmutableDenseMatrix
877
+ if isinstance(x, MatrixExpr):
878
+ return x
879
+ return ImmutableDenseMatrix([[x]])
880
+
881
+
882
+ from .matmul import MatMul
883
+ from .matadd import MatAdd
884
+ from .matpow import MatPow
885
+ from .transpose import Transpose
886
+ from .inverse import Inverse
887
+ from .special import ZeroMatrix, Identity
888
+ from .determinant import Determinant
.venv/lib/python3.11/site-packages/sympy/matrices/expressions/matpow.py ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .matexpr import MatrixExpr
2
+ from .special import Identity
3
+ from sympy.core import S
4
+ from sympy.core.expr import ExprBuilder
5
+ from sympy.core.cache import cacheit
6
+ from sympy.core.power import Pow
7
+ from sympy.core.sympify import _sympify
8
+ from sympy.matrices import MatrixBase
9
+ from sympy.matrices.exceptions import NonSquareMatrixError
10
+
11
+
12
+ class MatPow(MatrixExpr):
13
+ def __new__(cls, base, exp, evaluate=False, **options):
14
+ base = _sympify(base)
15
+ if not base.is_Matrix:
16
+ raise TypeError("MatPow base should be a matrix")
17
+
18
+ if base.is_square is False:
19
+ raise NonSquareMatrixError("Power of non-square matrix %s" % base)
20
+
21
+ exp = _sympify(exp)
22
+ obj = super().__new__(cls, base, exp)
23
+
24
+ if evaluate:
25
+ obj = obj.doit(deep=False)
26
+
27
+ return obj
28
+
29
+ @property
30
+ def base(self):
31
+ return self.args[0]
32
+
33
+ @property
34
+ def exp(self):
35
+ return self.args[1]
36
+
37
+ @property
38
+ def shape(self):
39
+ return self.base.shape
40
+
41
+ @cacheit
42
+ def _get_explicit_matrix(self):
43
+ return self.base.as_explicit()**self.exp
44
+
45
+ def _entry(self, i, j, **kwargs):
46
+ from sympy.matrices.expressions import MatMul
47
+ A = self.doit()
48
+ if isinstance(A, MatPow):
49
+ # We still have a MatPow, make an explicit MatMul out of it.
50
+ if A.exp.is_Integer and A.exp.is_positive:
51
+ A = MatMul(*[A.base for k in range(A.exp)])
52
+ elif not self._is_shape_symbolic():
53
+ return A._get_explicit_matrix()[i, j]
54
+ else:
55
+ # Leave the expression unevaluated:
56
+ from sympy.matrices.expressions.matexpr import MatrixElement
57
+ return MatrixElement(self, i, j)
58
+ return A[i, j]
59
+
60
+ def doit(self, **hints):
61
+ if hints.get('deep', True):
62
+ base, exp = (arg.doit(**hints) for arg in self.args)
63
+ else:
64
+ base, exp = self.args
65
+
66
+ # combine all powers, e.g. (A ** 2) ** 3 -> A ** 6
67
+ while isinstance(base, MatPow):
68
+ exp *= base.args[1]
69
+ base = base.args[0]
70
+
71
+ if isinstance(base, MatrixBase):
72
+ # Delegate
73
+ return base ** exp
74
+
75
+ # Handle simple cases so that _eval_power() in MatrixExpr sub-classes can ignore them
76
+ if exp == S.One:
77
+ return base
78
+ if exp == S.Zero:
79
+ return Identity(base.rows)
80
+ if exp == S.NegativeOne:
81
+ from sympy.matrices.expressions import Inverse
82
+ return Inverse(base).doit(**hints)
83
+
84
+ eval_power = getattr(base, '_eval_power', None)
85
+ if eval_power is not None:
86
+ return eval_power(exp)
87
+
88
+ return MatPow(base, exp)
89
+
90
+ def _eval_transpose(self):
91
+ base, exp = self.args
92
+ return MatPow(base.transpose(), exp)
93
+
94
+ def _eval_adjoint(self):
95
+ base, exp = self.args
96
+ return MatPow(base.adjoint(), exp)
97
+
98
+ def _eval_conjugate(self):
99
+ base, exp = self.args
100
+ return MatPow(base.conjugate(), exp)
101
+
102
+ def _eval_derivative(self, x):
103
+ return Pow._eval_derivative(self, x)
104
+
105
+ def _eval_derivative_matrix_lines(self, x):
106
+ from sympy.tensor.array.expressions.array_expressions import ArrayContraction
107
+ from ...tensor.array.expressions.array_expressions import ArrayTensorProduct
108
+ from .matmul import MatMul
109
+ from .inverse import Inverse
110
+ exp = self.exp
111
+ if self.base.shape == (1, 1) and not exp.has(x):
112
+ lr = self.base._eval_derivative_matrix_lines(x)
113
+ for i in lr:
114
+ subexpr = ExprBuilder(
115
+ ArrayContraction,
116
+ [
117
+ ExprBuilder(
118
+ ArrayTensorProduct,
119
+ [
120
+ Identity(1),
121
+ i._lines[0],
122
+ exp*self.base**(exp-1),
123
+ i._lines[1],
124
+ Identity(1),
125
+ ]
126
+ ),
127
+ (0, 3, 4), (5, 7, 8)
128
+ ],
129
+ validator=ArrayContraction._validate
130
+ )
131
+ i._first_pointer_parent = subexpr.args[0].args
132
+ i._first_pointer_index = 0
133
+ i._second_pointer_parent = subexpr.args[0].args
134
+ i._second_pointer_index = 4
135
+ i._lines = [subexpr]
136
+ return lr
137
+ if (exp > 0) == True:
138
+ newexpr = MatMul.fromiter([self.base for i in range(exp)])
139
+ elif (exp == -1) == True:
140
+ return Inverse(self.base)._eval_derivative_matrix_lines(x)
141
+ elif (exp < 0) == True:
142
+ newexpr = MatMul.fromiter([Inverse(self.base) for i in range(-exp)])
143
+ elif (exp == 0) == True:
144
+ return self.doit()._eval_derivative_matrix_lines(x)
145
+ else:
146
+ raise NotImplementedError("cannot evaluate %s derived by %s" % (self, x))
147
+ return newexpr._eval_derivative_matrix_lines(x)
148
+
149
+ def _eval_inverse(self):
150
+ return MatPow(self.base, -self.exp)
.venv/lib/python3.11/site-packages/sympy/matrices/expressions/slice.py ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.matrices.expressions.matexpr import MatrixExpr
2
+ from sympy.core.basic import Basic
3
+ from sympy.core.containers import Tuple
4
+ from sympy.functions.elementary.integers import floor
5
+
6
+ def normalize(i, parentsize):
7
+ if isinstance(i, slice):
8
+ i = (i.start, i.stop, i.step)
9
+ if not isinstance(i, (tuple, list, Tuple)):
10
+ if (i < 0) == True:
11
+ i += parentsize
12
+ i = (i, i+1, 1)
13
+ i = list(i)
14
+ if len(i) == 2:
15
+ i.append(1)
16
+ start, stop, step = i
17
+ start = start or 0
18
+ if stop is None:
19
+ stop = parentsize
20
+ if (start < 0) == True:
21
+ start += parentsize
22
+ if (stop < 0) == True:
23
+ stop += parentsize
24
+ step = step or 1
25
+
26
+ if ((stop - start) * step < 1) == True:
27
+ raise IndexError()
28
+
29
+ return (start, stop, step)
30
+
31
+ class MatrixSlice(MatrixExpr):
32
+ """ A MatrixSlice of a Matrix Expression
33
+
34
+ Examples
35
+ ========
36
+
37
+ >>> from sympy import MatrixSlice, ImmutableMatrix
38
+ >>> M = ImmutableMatrix(4, 4, range(16))
39
+ >>> M
40
+ Matrix([
41
+ [ 0, 1, 2, 3],
42
+ [ 4, 5, 6, 7],
43
+ [ 8, 9, 10, 11],
44
+ [12, 13, 14, 15]])
45
+
46
+ >>> B = MatrixSlice(M, (0, 2), (2, 4))
47
+ >>> ImmutableMatrix(B)
48
+ Matrix([
49
+ [2, 3],
50
+ [6, 7]])
51
+ """
52
+ parent = property(lambda self: self.args[0])
53
+ rowslice = property(lambda self: self.args[1])
54
+ colslice = property(lambda self: self.args[2])
55
+
56
+ def __new__(cls, parent, rowslice, colslice):
57
+ rowslice = normalize(rowslice, parent.shape[0])
58
+ colslice = normalize(colslice, parent.shape[1])
59
+ if not (len(rowslice) == len(colslice) == 3):
60
+ raise IndexError()
61
+ if ((0 > rowslice[0]) == True or
62
+ (parent.shape[0] < rowslice[1]) == True or
63
+ (0 > colslice[0]) == True or
64
+ (parent.shape[1] < colslice[1]) == True):
65
+ raise IndexError()
66
+ if isinstance(parent, MatrixSlice):
67
+ return mat_slice_of_slice(parent, rowslice, colslice)
68
+ return Basic.__new__(cls, parent, Tuple(*rowslice), Tuple(*colslice))
69
+
70
+ @property
71
+ def shape(self):
72
+ rows = self.rowslice[1] - self.rowslice[0]
73
+ rows = rows if self.rowslice[2] == 1 else floor(rows/self.rowslice[2])
74
+ cols = self.colslice[1] - self.colslice[0]
75
+ cols = cols if self.colslice[2] == 1 else floor(cols/self.colslice[2])
76
+ return rows, cols
77
+
78
+ def _entry(self, i, j, **kwargs):
79
+ return self.parent._entry(i*self.rowslice[2] + self.rowslice[0],
80
+ j*self.colslice[2] + self.colslice[0],
81
+ **kwargs)
82
+
83
+ @property
84
+ def on_diag(self):
85
+ return self.rowslice == self.colslice
86
+
87
+
88
+ def slice_of_slice(s, t):
89
+ start1, stop1, step1 = s
90
+ start2, stop2, step2 = t
91
+
92
+ start = start1 + start2*step1
93
+ step = step1 * step2
94
+ stop = start1 + step1*stop2
95
+
96
+ if stop > stop1:
97
+ raise IndexError()
98
+
99
+ return start, stop, step
100
+
101
+
102
+ def mat_slice_of_slice(parent, rowslice, colslice):
103
+ """ Collapse nested matrix slices
104
+
105
+ >>> from sympy import MatrixSymbol
106
+ >>> X = MatrixSymbol('X', 10, 10)
107
+ >>> X[:, 1:5][5:8, :]
108
+ X[5:8, 1:5]
109
+ >>> X[1:9:2, 2:6][1:3, 2]
110
+ X[3:7:2, 4:5]
111
+ """
112
+ row = slice_of_slice(parent.rowslice, rowslice)
113
+ col = slice_of_slice(parent.colslice, colslice)
114
+ return MatrixSlice(parent.parent, row, col)
.venv/lib/python3.11/site-packages/sympy/matrices/expressions/tests/__pycache__/test_applyfunc.cpython-311.pyc ADDED
Binary file (8.73 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/expressions/tests/__pycache__/test_derivatives.cpython-311.pyc ADDED
Binary file (50.7 kB). View file
 
.venv/lib/python3.11/site-packages/sympy/matrices/expressions/tests/__pycache__/test_dotproduct.cpython-311.pyc ADDED
Binary file (3.71 kB). View file