Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- lib/python3.12/site-packages/sympy/matrices/__init__.py +72 -0
- lib/python3.12/site-packages/sympy/matrices/decompositions.py +1621 -0
- lib/python3.12/site-packages/sympy/matrices/eigen.py +1346 -0
- lib/python3.12/site-packages/sympy/matrices/graph.py +279 -0
- lib/python3.12/site-packages/sympy/matrices/immutable.py +196 -0
- lib/python3.12/site-packages/sympy/matrices/matrices.py +687 -0
- lib/python3.12/site-packages/sympy/matrices/solvers.py +942 -0
- lib/python3.12/site-packages/sympy/matrices/sparse.py +473 -0
- lib/python3.12/site-packages/sympy/matrices/subspaces.py +174 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/appellseqs.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/constructor.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/densearith.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/densebasic.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/densetools.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/dispersion.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/distributedmodules.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/domainmatrix.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/euclidtools.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/factortools.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/fglmtools.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/fields.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/galoistools.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/groebnertools.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/heuristicgcd.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/modulargcd.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/monomials.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/multivariate_resultants.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/orderings.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/orthopolys.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/partfrac.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/polyconfig.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/polyerrors.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/polyfuncs.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/polymatrix.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/polyoptions.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/polyroots.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/polyutils.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/rationaltools.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/ring_series.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/rootisolation.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/rootoftools.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/solvers.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/specialpolys.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/sqfreetools.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/__pycache__/subresultants_qq_zz.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/agca/__init__.py +5 -0
- lib/python3.12/site-packages/sympy/polys/agca/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/agca/__pycache__/extensions.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/polys/agca/__pycache__/homomorphisms.cpython-312.pyc +0 -0
lib/python3.12/site-packages/sympy/matrices/__init__.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""A module that handles matrices.
|
| 2 |
+
|
| 3 |
+
Includes functions for fast creating matrices like zero, one/eye, random
|
| 4 |
+
matrix, etc.
|
| 5 |
+
"""
|
| 6 |
+
from .exceptions import ShapeError, NonSquareMatrixError
|
| 7 |
+
from .kind import MatrixKind
|
| 8 |
+
from .dense import (
|
| 9 |
+
GramSchmidt, casoratian, diag, eye, hessian, jordan_cell,
|
| 10 |
+
list2numpy, matrix2numpy, matrix_multiply_elementwise, ones,
|
| 11 |
+
randMatrix, rot_axis1, rot_axis2, rot_axis3, rot_ccw_axis1,
|
| 12 |
+
rot_ccw_axis2, rot_ccw_axis3, rot_givens,
|
| 13 |
+
symarray, wronskian, zeros)
|
| 14 |
+
from .dense import MutableDenseMatrix
|
| 15 |
+
from .matrixbase import DeferredVector, MatrixBase
|
| 16 |
+
|
| 17 |
+
MutableMatrix = MutableDenseMatrix
|
| 18 |
+
Matrix = MutableMatrix
|
| 19 |
+
|
| 20 |
+
from .sparse import MutableSparseMatrix
|
| 21 |
+
from .sparsetools import banded
|
| 22 |
+
from .immutable import ImmutableDenseMatrix, ImmutableSparseMatrix
|
| 23 |
+
|
| 24 |
+
ImmutableMatrix = ImmutableDenseMatrix
|
| 25 |
+
SparseMatrix = MutableSparseMatrix
|
| 26 |
+
|
| 27 |
+
from .expressions import (
|
| 28 |
+
MatrixSlice, BlockDiagMatrix, BlockMatrix, FunctionMatrix, Identity,
|
| 29 |
+
Inverse, MatAdd, MatMul, MatPow, MatrixExpr, MatrixSymbol, Trace,
|
| 30 |
+
Transpose, ZeroMatrix, OneMatrix, blockcut, block_collapse, matrix_symbols, Adjoint,
|
| 31 |
+
hadamard_product, HadamardProduct, HadamardPower, Determinant, det,
|
| 32 |
+
diagonalize_vector, DiagMatrix, DiagonalMatrix, DiagonalOf, trace,
|
| 33 |
+
DotProduct, kronecker_product, KroneckerProduct,
|
| 34 |
+
PermutationMatrix, MatrixPermute, MatrixSet, Permanent, per)
|
| 35 |
+
|
| 36 |
+
from .utilities import dotprodsimp
|
| 37 |
+
|
| 38 |
+
__all__ = [
|
| 39 |
+
'ShapeError', 'NonSquareMatrixError', 'MatrixKind',
|
| 40 |
+
|
| 41 |
+
'GramSchmidt', 'casoratian', 'diag', 'eye', 'hessian', 'jordan_cell',
|
| 42 |
+
'list2numpy', 'matrix2numpy', 'matrix_multiply_elementwise', 'ones',
|
| 43 |
+
'randMatrix', 'rot_axis1', 'rot_axis2', 'rot_axis3', 'symarray',
|
| 44 |
+
'wronskian', 'zeros', 'rot_ccw_axis1', 'rot_ccw_axis2', 'rot_ccw_axis3',
|
| 45 |
+
'rot_givens',
|
| 46 |
+
|
| 47 |
+
'MutableDenseMatrix',
|
| 48 |
+
|
| 49 |
+
'DeferredVector', 'MatrixBase',
|
| 50 |
+
|
| 51 |
+
'Matrix', 'MutableMatrix',
|
| 52 |
+
|
| 53 |
+
'MutableSparseMatrix',
|
| 54 |
+
|
| 55 |
+
'banded',
|
| 56 |
+
|
| 57 |
+
'ImmutableDenseMatrix', 'ImmutableSparseMatrix',
|
| 58 |
+
|
| 59 |
+
'ImmutableMatrix', 'SparseMatrix',
|
| 60 |
+
|
| 61 |
+
'MatrixSlice', 'BlockDiagMatrix', 'BlockMatrix', 'FunctionMatrix',
|
| 62 |
+
'Identity', 'Inverse', 'MatAdd', 'MatMul', 'MatPow', 'MatrixExpr',
|
| 63 |
+
'MatrixSymbol', 'Trace', 'Transpose', 'ZeroMatrix', 'OneMatrix',
|
| 64 |
+
'blockcut', 'block_collapse', 'matrix_symbols', 'Adjoint',
|
| 65 |
+
'hadamard_product', 'HadamardProduct', 'HadamardPower', 'Determinant',
|
| 66 |
+
'det', 'diagonalize_vector', 'DiagMatrix', 'DiagonalMatrix',
|
| 67 |
+
'DiagonalOf', 'trace', 'DotProduct', 'kronecker_product',
|
| 68 |
+
'KroneckerProduct', 'PermutationMatrix', 'MatrixPermute', 'MatrixSet',
|
| 69 |
+
'Permanent', 'per',
|
| 70 |
+
|
| 71 |
+
'dotprodsimp',
|
| 72 |
+
]
|
lib/python3.12/site-packages/sympy/matrices/decompositions.py
ADDED
|
@@ -0,0 +1,1621 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import copy
|
| 2 |
+
|
| 3 |
+
from sympy.core import S
|
| 4 |
+
from sympy.core.function import expand_mul
|
| 5 |
+
from sympy.functions.elementary.miscellaneous import Min, sqrt
|
| 6 |
+
from sympy.functions.elementary.complexes import sign
|
| 7 |
+
|
| 8 |
+
from .exceptions import NonSquareMatrixError, NonPositiveDefiniteMatrixError
|
| 9 |
+
from .utilities import _get_intermediate_simp, _iszero
|
| 10 |
+
from .determinant import _find_reasonable_pivot_naive
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def _rank_decomposition(M, iszerofunc=_iszero, simplify=False):
|
| 14 |
+
r"""Returns a pair of matrices (`C`, `F`) with matching rank
|
| 15 |
+
such that `A = C F`.
|
| 16 |
+
|
| 17 |
+
Parameters
|
| 18 |
+
==========
|
| 19 |
+
|
| 20 |
+
iszerofunc : Function, optional
|
| 21 |
+
A function used for detecting whether an element can
|
| 22 |
+
act as a pivot. ``lambda x: x.is_zero`` is used by default.
|
| 23 |
+
|
| 24 |
+
simplify : Bool or Function, optional
|
| 25 |
+
A function used to simplify elements when looking for a
|
| 26 |
+
pivot. By default SymPy's ``simplify`` is used.
|
| 27 |
+
|
| 28 |
+
Returns
|
| 29 |
+
=======
|
| 30 |
+
|
| 31 |
+
(C, F) : Matrices
|
| 32 |
+
`C` and `F` are full-rank matrices with rank as same as `A`,
|
| 33 |
+
whose product gives `A`.
|
| 34 |
+
|
| 35 |
+
See Notes for additional mathematical details.
|
| 36 |
+
|
| 37 |
+
Examples
|
| 38 |
+
========
|
| 39 |
+
|
| 40 |
+
>>> from sympy import Matrix
|
| 41 |
+
>>> A = Matrix([
|
| 42 |
+
... [1, 3, 1, 4],
|
| 43 |
+
... [2, 7, 3, 9],
|
| 44 |
+
... [1, 5, 3, 1],
|
| 45 |
+
... [1, 2, 0, 8]
|
| 46 |
+
... ])
|
| 47 |
+
>>> C, F = A.rank_decomposition()
|
| 48 |
+
>>> C
|
| 49 |
+
Matrix([
|
| 50 |
+
[1, 3, 4],
|
| 51 |
+
[2, 7, 9],
|
| 52 |
+
[1, 5, 1],
|
| 53 |
+
[1, 2, 8]])
|
| 54 |
+
>>> F
|
| 55 |
+
Matrix([
|
| 56 |
+
[1, 0, -2, 0],
|
| 57 |
+
[0, 1, 1, 0],
|
| 58 |
+
[0, 0, 0, 1]])
|
| 59 |
+
>>> C * F == A
|
| 60 |
+
True
|
| 61 |
+
|
| 62 |
+
Notes
|
| 63 |
+
=====
|
| 64 |
+
|
| 65 |
+
Obtaining `F`, an RREF of `A`, is equivalent to creating a
|
| 66 |
+
product
|
| 67 |
+
|
| 68 |
+
.. math::
|
| 69 |
+
E_n E_{n-1} ... E_1 A = F
|
| 70 |
+
|
| 71 |
+
where `E_n, E_{n-1}, \dots, E_1` are the elimination matrices or
|
| 72 |
+
permutation matrices equivalent to each row-reduction step.
|
| 73 |
+
|
| 74 |
+
The inverse of the same product of elimination matrices gives
|
| 75 |
+
`C`:
|
| 76 |
+
|
| 77 |
+
.. math::
|
| 78 |
+
C = \left(E_n E_{n-1} \dots E_1\right)^{-1}
|
| 79 |
+
|
| 80 |
+
It is not necessary, however, to actually compute the inverse:
|
| 81 |
+
the columns of `C` are those from the original matrix with the
|
| 82 |
+
same column indices as the indices of the pivot columns of `F`.
|
| 83 |
+
|
| 84 |
+
References
|
| 85 |
+
==========
|
| 86 |
+
|
| 87 |
+
.. [1] https://en.wikipedia.org/wiki/Rank_factorization
|
| 88 |
+
|
| 89 |
+
.. [2] Piziak, R.; Odell, P. L. (1 June 1999).
|
| 90 |
+
"Full Rank Factorization of Matrices".
|
| 91 |
+
Mathematics Magazine. 72 (3): 193. doi:10.2307/2690882
|
| 92 |
+
|
| 93 |
+
See Also
|
| 94 |
+
========
|
| 95 |
+
|
| 96 |
+
sympy.matrices.matrixbase.MatrixBase.rref
|
| 97 |
+
"""
|
| 98 |
+
|
| 99 |
+
F, pivot_cols = M.rref(simplify=simplify, iszerofunc=iszerofunc,
|
| 100 |
+
pivots=True)
|
| 101 |
+
rank = len(pivot_cols)
|
| 102 |
+
|
| 103 |
+
C = M.extract(range(M.rows), pivot_cols)
|
| 104 |
+
F = F[:rank, :]
|
| 105 |
+
|
| 106 |
+
return C, F
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
def _liupc(M):
|
| 110 |
+
"""Liu's algorithm, for pre-determination of the Elimination Tree of
|
| 111 |
+
the given matrix, used in row-based symbolic Cholesky factorization.
|
| 112 |
+
|
| 113 |
+
Examples
|
| 114 |
+
========
|
| 115 |
+
|
| 116 |
+
>>> from sympy import SparseMatrix
|
| 117 |
+
>>> S = SparseMatrix([
|
| 118 |
+
... [1, 0, 3, 2],
|
| 119 |
+
... [0, 0, 1, 0],
|
| 120 |
+
... [4, 0, 0, 5],
|
| 121 |
+
... [0, 6, 7, 0]])
|
| 122 |
+
>>> S.liupc()
|
| 123 |
+
([[0], [], [0], [1, 2]], [4, 3, 4, 4])
|
| 124 |
+
|
| 125 |
+
References
|
| 126 |
+
==========
|
| 127 |
+
|
| 128 |
+
.. [1] Symbolic Sparse Cholesky Factorization using Elimination Trees,
|
| 129 |
+
Jeroen Van Grondelle (1999)
|
| 130 |
+
https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.39.7582
|
| 131 |
+
"""
|
| 132 |
+
# Algorithm 2.4, p 17 of reference
|
| 133 |
+
|
| 134 |
+
# get the indices of the elements that are non-zero on or below diag
|
| 135 |
+
R = [[] for r in range(M.rows)]
|
| 136 |
+
|
| 137 |
+
for r, c, _ in M.row_list():
|
| 138 |
+
if c <= r:
|
| 139 |
+
R[r].append(c)
|
| 140 |
+
|
| 141 |
+
inf = len(R) # nothing will be this large
|
| 142 |
+
parent = [inf]*M.rows
|
| 143 |
+
virtual = [inf]*M.rows
|
| 144 |
+
|
| 145 |
+
for r in range(M.rows):
|
| 146 |
+
for c in R[r][:-1]:
|
| 147 |
+
while virtual[c] < r:
|
| 148 |
+
t = virtual[c]
|
| 149 |
+
virtual[c] = r
|
| 150 |
+
c = t
|
| 151 |
+
|
| 152 |
+
if virtual[c] == inf:
|
| 153 |
+
parent[c] = virtual[c] = r
|
| 154 |
+
|
| 155 |
+
return R, parent
|
| 156 |
+
|
| 157 |
+
def _row_structure_symbolic_cholesky(M):
|
| 158 |
+
"""Symbolic cholesky factorization, for pre-determination of the
|
| 159 |
+
non-zero structure of the Cholesky factororization.
|
| 160 |
+
|
| 161 |
+
Examples
|
| 162 |
+
========
|
| 163 |
+
|
| 164 |
+
>>> from sympy import SparseMatrix
|
| 165 |
+
>>> S = SparseMatrix([
|
| 166 |
+
... [1, 0, 3, 2],
|
| 167 |
+
... [0, 0, 1, 0],
|
| 168 |
+
... [4, 0, 0, 5],
|
| 169 |
+
... [0, 6, 7, 0]])
|
| 170 |
+
>>> S.row_structure_symbolic_cholesky()
|
| 171 |
+
[[0], [], [0], [1, 2]]
|
| 172 |
+
|
| 173 |
+
References
|
| 174 |
+
==========
|
| 175 |
+
|
| 176 |
+
.. [1] Symbolic Sparse Cholesky Factorization using Elimination Trees,
|
| 177 |
+
Jeroen Van Grondelle (1999)
|
| 178 |
+
https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.39.7582
|
| 179 |
+
"""
|
| 180 |
+
|
| 181 |
+
R, parent = M.liupc()
|
| 182 |
+
inf = len(R) # this acts as infinity
|
| 183 |
+
Lrow = copy.deepcopy(R)
|
| 184 |
+
|
| 185 |
+
for k in range(M.rows):
|
| 186 |
+
for j in R[k]:
|
| 187 |
+
while j != inf and j != k:
|
| 188 |
+
Lrow[k].append(j)
|
| 189 |
+
j = parent[j]
|
| 190 |
+
|
| 191 |
+
Lrow[k] = sorted(set(Lrow[k]))
|
| 192 |
+
|
| 193 |
+
return Lrow
|
| 194 |
+
|
| 195 |
+
|
| 196 |
+
def _cholesky(M, hermitian=True):
|
| 197 |
+
"""Returns the Cholesky-type decomposition L of a matrix A
|
| 198 |
+
such that L * L.H == A if hermitian flag is True,
|
| 199 |
+
or L * L.T == A if hermitian is False.
|
| 200 |
+
|
| 201 |
+
A must be a Hermitian positive-definite matrix if hermitian is True,
|
| 202 |
+
or a symmetric matrix if it is False.
|
| 203 |
+
|
| 204 |
+
Examples
|
| 205 |
+
========
|
| 206 |
+
|
| 207 |
+
>>> from sympy import Matrix
|
| 208 |
+
>>> A = Matrix(((25, 15, -5), (15, 18, 0), (-5, 0, 11)))
|
| 209 |
+
>>> A.cholesky()
|
| 210 |
+
Matrix([
|
| 211 |
+
[ 5, 0, 0],
|
| 212 |
+
[ 3, 3, 0],
|
| 213 |
+
[-1, 1, 3]])
|
| 214 |
+
>>> A.cholesky() * A.cholesky().T
|
| 215 |
+
Matrix([
|
| 216 |
+
[25, 15, -5],
|
| 217 |
+
[15, 18, 0],
|
| 218 |
+
[-5, 0, 11]])
|
| 219 |
+
|
| 220 |
+
The matrix can have complex entries:
|
| 221 |
+
|
| 222 |
+
>>> from sympy import I
|
| 223 |
+
>>> A = Matrix(((9, 3*I), (-3*I, 5)))
|
| 224 |
+
>>> A.cholesky()
|
| 225 |
+
Matrix([
|
| 226 |
+
[ 3, 0],
|
| 227 |
+
[-I, 2]])
|
| 228 |
+
>>> A.cholesky() * A.cholesky().H
|
| 229 |
+
Matrix([
|
| 230 |
+
[ 9, 3*I],
|
| 231 |
+
[-3*I, 5]])
|
| 232 |
+
|
| 233 |
+
Non-hermitian Cholesky-type decomposition may be useful when the
|
| 234 |
+
matrix is not positive-definite.
|
| 235 |
+
|
| 236 |
+
>>> A = Matrix([[1, 2], [2, 1]])
|
| 237 |
+
>>> L = A.cholesky(hermitian=False)
|
| 238 |
+
>>> L
|
| 239 |
+
Matrix([
|
| 240 |
+
[1, 0],
|
| 241 |
+
[2, sqrt(3)*I]])
|
| 242 |
+
>>> L*L.T == A
|
| 243 |
+
True
|
| 244 |
+
|
| 245 |
+
See Also
|
| 246 |
+
========
|
| 247 |
+
|
| 248 |
+
sympy.matrices.dense.DenseMatrix.LDLdecomposition
|
| 249 |
+
sympy.matrices.matrixbase.MatrixBase.LUdecomposition
|
| 250 |
+
QRdecomposition
|
| 251 |
+
"""
|
| 252 |
+
|
| 253 |
+
from .dense import MutableDenseMatrix
|
| 254 |
+
|
| 255 |
+
if not M.is_square:
|
| 256 |
+
raise NonSquareMatrixError("Matrix must be square.")
|
| 257 |
+
if hermitian and not M.is_hermitian:
|
| 258 |
+
raise ValueError("Matrix must be Hermitian.")
|
| 259 |
+
if not hermitian and not M.is_symmetric():
|
| 260 |
+
raise ValueError("Matrix must be symmetric.")
|
| 261 |
+
|
| 262 |
+
L = MutableDenseMatrix.zeros(M.rows, M.rows)
|
| 263 |
+
|
| 264 |
+
if hermitian:
|
| 265 |
+
for i in range(M.rows):
|
| 266 |
+
for j in range(i):
|
| 267 |
+
L[i, j] = ((1 / L[j, j])*(M[i, j] -
|
| 268 |
+
sum(L[i, k]*L[j, k].conjugate() for k in range(j))))
|
| 269 |
+
|
| 270 |
+
Lii2 = (M[i, i] -
|
| 271 |
+
sum(L[i, k]*L[i, k].conjugate() for k in range(i)))
|
| 272 |
+
|
| 273 |
+
if Lii2.is_positive is False:
|
| 274 |
+
raise NonPositiveDefiniteMatrixError(
|
| 275 |
+
"Matrix must be positive-definite")
|
| 276 |
+
|
| 277 |
+
L[i, i] = sqrt(Lii2)
|
| 278 |
+
|
| 279 |
+
else:
|
| 280 |
+
for i in range(M.rows):
|
| 281 |
+
for j in range(i):
|
| 282 |
+
L[i, j] = ((1 / L[j, j])*(M[i, j] -
|
| 283 |
+
sum(L[i, k]*L[j, k] for k in range(j))))
|
| 284 |
+
|
| 285 |
+
L[i, i] = sqrt(M[i, i] -
|
| 286 |
+
sum(L[i, k]**2 for k in range(i)))
|
| 287 |
+
|
| 288 |
+
return M._new(L)
|
| 289 |
+
|
| 290 |
+
def _cholesky_sparse(M, hermitian=True):
|
| 291 |
+
"""
|
| 292 |
+
Returns the Cholesky decomposition L of a matrix A
|
| 293 |
+
such that L * L.T = A
|
| 294 |
+
|
| 295 |
+
A must be a square, symmetric, positive-definite
|
| 296 |
+
and non-singular matrix
|
| 297 |
+
|
| 298 |
+
Examples
|
| 299 |
+
========
|
| 300 |
+
|
| 301 |
+
>>> from sympy import SparseMatrix
|
| 302 |
+
>>> A = SparseMatrix(((25,15,-5),(15,18,0),(-5,0,11)))
|
| 303 |
+
>>> A.cholesky()
|
| 304 |
+
Matrix([
|
| 305 |
+
[ 5, 0, 0],
|
| 306 |
+
[ 3, 3, 0],
|
| 307 |
+
[-1, 1, 3]])
|
| 308 |
+
>>> A.cholesky() * A.cholesky().T == A
|
| 309 |
+
True
|
| 310 |
+
|
| 311 |
+
The matrix can have complex entries:
|
| 312 |
+
|
| 313 |
+
>>> from sympy import I
|
| 314 |
+
>>> A = SparseMatrix(((9, 3*I), (-3*I, 5)))
|
| 315 |
+
>>> A.cholesky()
|
| 316 |
+
Matrix([
|
| 317 |
+
[ 3, 0],
|
| 318 |
+
[-I, 2]])
|
| 319 |
+
>>> A.cholesky() * A.cholesky().H
|
| 320 |
+
Matrix([
|
| 321 |
+
[ 9, 3*I],
|
| 322 |
+
[-3*I, 5]])
|
| 323 |
+
|
| 324 |
+
Non-hermitian Cholesky-type decomposition may be useful when the
|
| 325 |
+
matrix is not positive-definite.
|
| 326 |
+
|
| 327 |
+
>>> A = SparseMatrix([[1, 2], [2, 1]])
|
| 328 |
+
>>> L = A.cholesky(hermitian=False)
|
| 329 |
+
>>> L
|
| 330 |
+
Matrix([
|
| 331 |
+
[1, 0],
|
| 332 |
+
[2, sqrt(3)*I]])
|
| 333 |
+
>>> L*L.T == A
|
| 334 |
+
True
|
| 335 |
+
|
| 336 |
+
See Also
|
| 337 |
+
========
|
| 338 |
+
|
| 339 |
+
sympy.matrices.sparse.SparseMatrix.LDLdecomposition
|
| 340 |
+
sympy.matrices.matrixbase.MatrixBase.LUdecomposition
|
| 341 |
+
QRdecomposition
|
| 342 |
+
"""
|
| 343 |
+
|
| 344 |
+
from .dense import MutableDenseMatrix
|
| 345 |
+
|
| 346 |
+
if not M.is_square:
|
| 347 |
+
raise NonSquareMatrixError("Matrix must be square.")
|
| 348 |
+
if hermitian and not M.is_hermitian:
|
| 349 |
+
raise ValueError("Matrix must be Hermitian.")
|
| 350 |
+
if not hermitian and not M.is_symmetric():
|
| 351 |
+
raise ValueError("Matrix must be symmetric.")
|
| 352 |
+
|
| 353 |
+
dps = _get_intermediate_simp(expand_mul, expand_mul)
|
| 354 |
+
Crowstruc = M.row_structure_symbolic_cholesky()
|
| 355 |
+
C = MutableDenseMatrix.zeros(M.rows)
|
| 356 |
+
|
| 357 |
+
for i in range(len(Crowstruc)):
|
| 358 |
+
for j in Crowstruc[i]:
|
| 359 |
+
if i != j:
|
| 360 |
+
C[i, j] = M[i, j]
|
| 361 |
+
summ = 0
|
| 362 |
+
|
| 363 |
+
for p1 in Crowstruc[i]:
|
| 364 |
+
if p1 < j:
|
| 365 |
+
for p2 in Crowstruc[j]:
|
| 366 |
+
if p2 < j:
|
| 367 |
+
if p1 == p2:
|
| 368 |
+
if hermitian:
|
| 369 |
+
summ += C[i, p1]*C[j, p1].conjugate()
|
| 370 |
+
else:
|
| 371 |
+
summ += C[i, p1]*C[j, p1]
|
| 372 |
+
else:
|
| 373 |
+
break
|
| 374 |
+
else:
|
| 375 |
+
break
|
| 376 |
+
|
| 377 |
+
C[i, j] = dps((C[i, j] - summ) / C[j, j])
|
| 378 |
+
|
| 379 |
+
else: # i == j
|
| 380 |
+
C[j, j] = M[j, j]
|
| 381 |
+
summ = 0
|
| 382 |
+
|
| 383 |
+
for k in Crowstruc[j]:
|
| 384 |
+
if k < j:
|
| 385 |
+
if hermitian:
|
| 386 |
+
summ += C[j, k]*C[j, k].conjugate()
|
| 387 |
+
else:
|
| 388 |
+
summ += C[j, k]**2
|
| 389 |
+
else:
|
| 390 |
+
break
|
| 391 |
+
|
| 392 |
+
Cjj2 = dps(C[j, j] - summ)
|
| 393 |
+
|
| 394 |
+
if hermitian and Cjj2.is_positive is False:
|
| 395 |
+
raise NonPositiveDefiniteMatrixError(
|
| 396 |
+
"Matrix must be positive-definite")
|
| 397 |
+
|
| 398 |
+
C[j, j] = sqrt(Cjj2)
|
| 399 |
+
|
| 400 |
+
return M._new(C)
|
| 401 |
+
|
| 402 |
+
|
| 403 |
+
def _LDLdecomposition(M, hermitian=True):
|
| 404 |
+
"""Returns the LDL Decomposition (L, D) of matrix A,
|
| 405 |
+
such that L * D * L.H == A if hermitian flag is True, or
|
| 406 |
+
L * D * L.T == A if hermitian is False.
|
| 407 |
+
This method eliminates the use of square root.
|
| 408 |
+
Further this ensures that all the diagonal entries of L are 1.
|
| 409 |
+
A must be a Hermitian positive-definite matrix if hermitian is True,
|
| 410 |
+
or a symmetric matrix otherwise.
|
| 411 |
+
|
| 412 |
+
Examples
|
| 413 |
+
========
|
| 414 |
+
|
| 415 |
+
>>> from sympy import Matrix, eye
|
| 416 |
+
>>> A = Matrix(((25, 15, -5), (15, 18, 0), (-5, 0, 11)))
|
| 417 |
+
>>> L, D = A.LDLdecomposition()
|
| 418 |
+
>>> L
|
| 419 |
+
Matrix([
|
| 420 |
+
[ 1, 0, 0],
|
| 421 |
+
[ 3/5, 1, 0],
|
| 422 |
+
[-1/5, 1/3, 1]])
|
| 423 |
+
>>> D
|
| 424 |
+
Matrix([
|
| 425 |
+
[25, 0, 0],
|
| 426 |
+
[ 0, 9, 0],
|
| 427 |
+
[ 0, 0, 9]])
|
| 428 |
+
>>> L * D * L.T * A.inv() == eye(A.rows)
|
| 429 |
+
True
|
| 430 |
+
|
| 431 |
+
The matrix can have complex entries:
|
| 432 |
+
|
| 433 |
+
>>> from sympy import I
|
| 434 |
+
>>> A = Matrix(((9, 3*I), (-3*I, 5)))
|
| 435 |
+
>>> L, D = A.LDLdecomposition()
|
| 436 |
+
>>> L
|
| 437 |
+
Matrix([
|
| 438 |
+
[ 1, 0],
|
| 439 |
+
[-I/3, 1]])
|
| 440 |
+
>>> D
|
| 441 |
+
Matrix([
|
| 442 |
+
[9, 0],
|
| 443 |
+
[0, 4]])
|
| 444 |
+
>>> L*D*L.H == A
|
| 445 |
+
True
|
| 446 |
+
|
| 447 |
+
See Also
|
| 448 |
+
========
|
| 449 |
+
|
| 450 |
+
sympy.matrices.dense.DenseMatrix.cholesky
|
| 451 |
+
sympy.matrices.matrixbase.MatrixBase.LUdecomposition
|
| 452 |
+
QRdecomposition
|
| 453 |
+
"""
|
| 454 |
+
|
| 455 |
+
from .dense import MutableDenseMatrix
|
| 456 |
+
|
| 457 |
+
if not M.is_square:
|
| 458 |
+
raise NonSquareMatrixError("Matrix must be square.")
|
| 459 |
+
if hermitian and not M.is_hermitian:
|
| 460 |
+
raise ValueError("Matrix must be Hermitian.")
|
| 461 |
+
if not hermitian and not M.is_symmetric():
|
| 462 |
+
raise ValueError("Matrix must be symmetric.")
|
| 463 |
+
|
| 464 |
+
D = MutableDenseMatrix.zeros(M.rows, M.rows)
|
| 465 |
+
L = MutableDenseMatrix.eye(M.rows)
|
| 466 |
+
|
| 467 |
+
if hermitian:
|
| 468 |
+
for i in range(M.rows):
|
| 469 |
+
for j in range(i):
|
| 470 |
+
L[i, j] = (1 / D[j, j])*(M[i, j] - sum(
|
| 471 |
+
L[i, k]*L[j, k].conjugate()*D[k, k] for k in range(j)))
|
| 472 |
+
|
| 473 |
+
D[i, i] = (M[i, i] -
|
| 474 |
+
sum(L[i, k]*L[i, k].conjugate()*D[k, k] for k in range(i)))
|
| 475 |
+
|
| 476 |
+
if D[i, i].is_positive is False:
|
| 477 |
+
raise NonPositiveDefiniteMatrixError(
|
| 478 |
+
"Matrix must be positive-definite")
|
| 479 |
+
|
| 480 |
+
else:
|
| 481 |
+
for i in range(M.rows):
|
| 482 |
+
for j in range(i):
|
| 483 |
+
L[i, j] = (1 / D[j, j])*(M[i, j] - sum(
|
| 484 |
+
L[i, k]*L[j, k]*D[k, k] for k in range(j)))
|
| 485 |
+
|
| 486 |
+
D[i, i] = M[i, i] - sum(L[i, k]**2*D[k, k] for k in range(i))
|
| 487 |
+
|
| 488 |
+
return M._new(L), M._new(D)
|
| 489 |
+
|
| 490 |
+
def _LDLdecomposition_sparse(M, hermitian=True):
|
| 491 |
+
"""
|
| 492 |
+
Returns the LDL Decomposition (matrices ``L`` and ``D``) of matrix
|
| 493 |
+
``A``, such that ``L * D * L.T == A``. ``A`` must be a square,
|
| 494 |
+
symmetric, positive-definite and non-singular.
|
| 495 |
+
|
| 496 |
+
This method eliminates the use of square root and ensures that all
|
| 497 |
+
the diagonal entries of L are 1.
|
| 498 |
+
|
| 499 |
+
Examples
|
| 500 |
+
========
|
| 501 |
+
|
| 502 |
+
>>> from sympy import SparseMatrix
|
| 503 |
+
>>> A = SparseMatrix(((25, 15, -5), (15, 18, 0), (-5, 0, 11)))
|
| 504 |
+
>>> L, D = A.LDLdecomposition()
|
| 505 |
+
>>> L
|
| 506 |
+
Matrix([
|
| 507 |
+
[ 1, 0, 0],
|
| 508 |
+
[ 3/5, 1, 0],
|
| 509 |
+
[-1/5, 1/3, 1]])
|
| 510 |
+
>>> D
|
| 511 |
+
Matrix([
|
| 512 |
+
[25, 0, 0],
|
| 513 |
+
[ 0, 9, 0],
|
| 514 |
+
[ 0, 0, 9]])
|
| 515 |
+
>>> L * D * L.T == A
|
| 516 |
+
True
|
| 517 |
+
|
| 518 |
+
"""
|
| 519 |
+
|
| 520 |
+
from .dense import MutableDenseMatrix
|
| 521 |
+
|
| 522 |
+
if not M.is_square:
|
| 523 |
+
raise NonSquareMatrixError("Matrix must be square.")
|
| 524 |
+
if hermitian and not M.is_hermitian:
|
| 525 |
+
raise ValueError("Matrix must be Hermitian.")
|
| 526 |
+
if not hermitian and not M.is_symmetric():
|
| 527 |
+
raise ValueError("Matrix must be symmetric.")
|
| 528 |
+
|
| 529 |
+
dps = _get_intermediate_simp(expand_mul, expand_mul)
|
| 530 |
+
Lrowstruc = M.row_structure_symbolic_cholesky()
|
| 531 |
+
L = MutableDenseMatrix.eye(M.rows)
|
| 532 |
+
D = MutableDenseMatrix.zeros(M.rows, M.cols)
|
| 533 |
+
|
| 534 |
+
for i in range(len(Lrowstruc)):
|
| 535 |
+
for j in Lrowstruc[i]:
|
| 536 |
+
if i != j:
|
| 537 |
+
L[i, j] = M[i, j]
|
| 538 |
+
summ = 0
|
| 539 |
+
|
| 540 |
+
for p1 in Lrowstruc[i]:
|
| 541 |
+
if p1 < j:
|
| 542 |
+
for p2 in Lrowstruc[j]:
|
| 543 |
+
if p2 < j:
|
| 544 |
+
if p1 == p2:
|
| 545 |
+
if hermitian:
|
| 546 |
+
summ += L[i, p1]*L[j, p1].conjugate()*D[p1, p1]
|
| 547 |
+
else:
|
| 548 |
+
summ += L[i, p1]*L[j, p1]*D[p1, p1]
|
| 549 |
+
else:
|
| 550 |
+
break
|
| 551 |
+
else:
|
| 552 |
+
break
|
| 553 |
+
|
| 554 |
+
L[i, j] = dps((L[i, j] - summ) / D[j, j])
|
| 555 |
+
|
| 556 |
+
else: # i == j
|
| 557 |
+
D[i, i] = M[i, i]
|
| 558 |
+
summ = 0
|
| 559 |
+
|
| 560 |
+
for k in Lrowstruc[i]:
|
| 561 |
+
if k < i:
|
| 562 |
+
if hermitian:
|
| 563 |
+
summ += L[i, k]*L[i, k].conjugate()*D[k, k]
|
| 564 |
+
else:
|
| 565 |
+
summ += L[i, k]**2*D[k, k]
|
| 566 |
+
else:
|
| 567 |
+
break
|
| 568 |
+
|
| 569 |
+
D[i, i] = dps(D[i, i] - summ)
|
| 570 |
+
|
| 571 |
+
if hermitian and D[i, i].is_positive is False:
|
| 572 |
+
raise NonPositiveDefiniteMatrixError(
|
| 573 |
+
"Matrix must be positive-definite")
|
| 574 |
+
|
| 575 |
+
return M._new(L), M._new(D)
|
| 576 |
+
|
| 577 |
+
|
| 578 |
+
def _LUdecomposition(M, iszerofunc=_iszero, simpfunc=None, rankcheck=False):
|
| 579 |
+
"""Returns (L, U, perm) where L is a lower triangular matrix with unit
|
| 580 |
+
diagonal, U is an upper triangular matrix, and perm is a list of row
|
| 581 |
+
swap index pairs. If A is the original matrix, then
|
| 582 |
+
``A = (L*U).permuteBkwd(perm)``, and the row permutation matrix P such
|
| 583 |
+
that $P A = L U$ can be computed by ``P = eye(A.rows).permuteFwd(perm)``.
|
| 584 |
+
|
| 585 |
+
See documentation for LUCombined for details about the keyword argument
|
| 586 |
+
rankcheck, iszerofunc, and simpfunc.
|
| 587 |
+
|
| 588 |
+
Parameters
|
| 589 |
+
==========
|
| 590 |
+
|
| 591 |
+
rankcheck : bool, optional
|
| 592 |
+
Determines if this function should detect the rank
|
| 593 |
+
deficiency of the matrixis and should raise a
|
| 594 |
+
``ValueError``.
|
| 595 |
+
|
| 596 |
+
iszerofunc : function, optional
|
| 597 |
+
A function which determines if a given expression is zero.
|
| 598 |
+
|
| 599 |
+
The function should be a callable that takes a single
|
| 600 |
+
SymPy expression and returns a 3-valued boolean value
|
| 601 |
+
``True``, ``False``, or ``None``.
|
| 602 |
+
|
| 603 |
+
It is internally used by the pivot searching algorithm.
|
| 604 |
+
See the notes section for a more information about the
|
| 605 |
+
pivot searching algorithm.
|
| 606 |
+
|
| 607 |
+
simpfunc : function or None, optional
|
| 608 |
+
A function that simplifies the input.
|
| 609 |
+
|
| 610 |
+
If this is specified as a function, this function should be
|
| 611 |
+
a callable that takes a single SymPy expression and returns
|
| 612 |
+
an another SymPy expression that is algebraically
|
| 613 |
+
equivalent.
|
| 614 |
+
|
| 615 |
+
If ``None``, it indicates that the pivot search algorithm
|
| 616 |
+
should not attempt to simplify any candidate pivots.
|
| 617 |
+
|
| 618 |
+
It is internally used by the pivot searching algorithm.
|
| 619 |
+
See the notes section for a more information about the
|
| 620 |
+
pivot searching algorithm.
|
| 621 |
+
|
| 622 |
+
Examples
|
| 623 |
+
========
|
| 624 |
+
|
| 625 |
+
>>> from sympy import Matrix
|
| 626 |
+
>>> a = Matrix([[4, 3], [6, 3]])
|
| 627 |
+
>>> L, U, _ = a.LUdecomposition()
|
| 628 |
+
>>> L
|
| 629 |
+
Matrix([
|
| 630 |
+
[ 1, 0],
|
| 631 |
+
[3/2, 1]])
|
| 632 |
+
>>> U
|
| 633 |
+
Matrix([
|
| 634 |
+
[4, 3],
|
| 635 |
+
[0, -3/2]])
|
| 636 |
+
|
| 637 |
+
See Also
|
| 638 |
+
========
|
| 639 |
+
|
| 640 |
+
sympy.matrices.dense.DenseMatrix.cholesky
|
| 641 |
+
sympy.matrices.dense.DenseMatrix.LDLdecomposition
|
| 642 |
+
QRdecomposition
|
| 643 |
+
LUdecomposition_Simple
|
| 644 |
+
LUdecompositionFF
|
| 645 |
+
LUsolve
|
| 646 |
+
"""
|
| 647 |
+
|
| 648 |
+
combined, p = M.LUdecomposition_Simple(iszerofunc=iszerofunc,
|
| 649 |
+
simpfunc=simpfunc, rankcheck=rankcheck)
|
| 650 |
+
|
| 651 |
+
# L is lower triangular ``M.rows x M.rows``
|
| 652 |
+
# U is upper triangular ``M.rows x M.cols``
|
| 653 |
+
# L has unit diagonal. For each column in combined, the subcolumn
|
| 654 |
+
# below the diagonal of combined is shared by L.
|
| 655 |
+
# If L has more columns than combined, then the remaining subcolumns
|
| 656 |
+
# below the diagonal of L are zero.
|
| 657 |
+
# The upper triangular portion of L and combined are equal.
|
| 658 |
+
def entry_L(i, j):
|
| 659 |
+
if i < j:
|
| 660 |
+
# Super diagonal entry
|
| 661 |
+
return M.zero
|
| 662 |
+
elif i == j:
|
| 663 |
+
return M.one
|
| 664 |
+
elif j < combined.cols:
|
| 665 |
+
return combined[i, j]
|
| 666 |
+
|
| 667 |
+
# Subdiagonal entry of L with no corresponding
|
| 668 |
+
# entry in combined
|
| 669 |
+
return M.zero
|
| 670 |
+
|
| 671 |
+
def entry_U(i, j):
|
| 672 |
+
return M.zero if i > j else combined[i, j]
|
| 673 |
+
|
| 674 |
+
L = M._new(combined.rows, combined.rows, entry_L)
|
| 675 |
+
U = M._new(combined.rows, combined.cols, entry_U)
|
| 676 |
+
|
| 677 |
+
return L, U, p
|
| 678 |
+
|
| 679 |
+
def _LUdecomposition_Simple(M, iszerofunc=_iszero, simpfunc=None,
|
| 680 |
+
rankcheck=False):
|
| 681 |
+
r"""Compute the PLU decomposition of the matrix.
|
| 682 |
+
|
| 683 |
+
Parameters
|
| 684 |
+
==========
|
| 685 |
+
|
| 686 |
+
rankcheck : bool, optional
|
| 687 |
+
Determines if this function should detect the rank
|
| 688 |
+
deficiency of the matrixis and should raise a
|
| 689 |
+
``ValueError``.
|
| 690 |
+
|
| 691 |
+
iszerofunc : function, optional
|
| 692 |
+
A function which determines if a given expression is zero.
|
| 693 |
+
|
| 694 |
+
The function should be a callable that takes a single
|
| 695 |
+
SymPy expression and returns a 3-valued boolean value
|
| 696 |
+
``True``, ``False``, or ``None``.
|
| 697 |
+
|
| 698 |
+
It is internally used by the pivot searching algorithm.
|
| 699 |
+
See the notes section for a more information about the
|
| 700 |
+
pivot searching algorithm.
|
| 701 |
+
|
| 702 |
+
simpfunc : function or None, optional
|
| 703 |
+
A function that simplifies the input.
|
| 704 |
+
|
| 705 |
+
If this is specified as a function, this function should be
|
| 706 |
+
a callable that takes a single SymPy expression and returns
|
| 707 |
+
an another SymPy expression that is algebraically
|
| 708 |
+
equivalent.
|
| 709 |
+
|
| 710 |
+
If ``None``, it indicates that the pivot search algorithm
|
| 711 |
+
should not attempt to simplify any candidate pivots.
|
| 712 |
+
|
| 713 |
+
It is internally used by the pivot searching algorithm.
|
| 714 |
+
See the notes section for a more information about the
|
| 715 |
+
pivot searching algorithm.
|
| 716 |
+
|
| 717 |
+
Returns
|
| 718 |
+
=======
|
| 719 |
+
|
| 720 |
+
(lu, row_swaps) : (Matrix, list)
|
| 721 |
+
If the original matrix is a $m, n$ matrix:
|
| 722 |
+
|
| 723 |
+
*lu* is a $m, n$ matrix, which contains result of the
|
| 724 |
+
decomposition in a compressed form. See the notes section
|
| 725 |
+
to see how the matrix is compressed.
|
| 726 |
+
|
| 727 |
+
*row_swaps* is a $m$-element list where each element is a
|
| 728 |
+
pair of row exchange indices.
|
| 729 |
+
|
| 730 |
+
``A = (L*U).permute_backward(perm)``, and the row
|
| 731 |
+
permutation matrix $P$ from the formula $P A = L U$ can be
|
| 732 |
+
computed by ``P=eye(A.row).permute_forward(perm)``.
|
| 733 |
+
|
| 734 |
+
Raises
|
| 735 |
+
======
|
| 736 |
+
|
| 737 |
+
ValueError
|
| 738 |
+
Raised if ``rankcheck=True`` and the matrix is found to
|
| 739 |
+
be rank deficient during the computation.
|
| 740 |
+
|
| 741 |
+
Notes
|
| 742 |
+
=====
|
| 743 |
+
|
| 744 |
+
About the PLU decomposition:
|
| 745 |
+
|
| 746 |
+
PLU decomposition is a generalization of a LU decomposition
|
| 747 |
+
which can be extended for rank-deficient matrices.
|
| 748 |
+
|
| 749 |
+
It can further be generalized for non-square matrices, and this
|
| 750 |
+
is the notation that SymPy is using.
|
| 751 |
+
|
| 752 |
+
PLU decomposition is a decomposition of a $m, n$ matrix $A$ in
|
| 753 |
+
the form of $P A = L U$ where
|
| 754 |
+
|
| 755 |
+
* $L$ is a $m, m$ lower triangular matrix with unit diagonal
|
| 756 |
+
entries.
|
| 757 |
+
* $U$ is a $m, n$ upper triangular matrix.
|
| 758 |
+
* $P$ is a $m, m$ permutation matrix.
|
| 759 |
+
|
| 760 |
+
So, for a square matrix, the decomposition would look like:
|
| 761 |
+
|
| 762 |
+
.. math::
|
| 763 |
+
L = \begin{bmatrix}
|
| 764 |
+
1 & 0 & 0 & \cdots & 0 \\
|
| 765 |
+
L_{1, 0} & 1 & 0 & \cdots & 0 \\
|
| 766 |
+
L_{2, 0} & L_{2, 1} & 1 & \cdots & 0 \\
|
| 767 |
+
\vdots & \vdots & \vdots & \ddots & \vdots \\
|
| 768 |
+
L_{n-1, 0} & L_{n-1, 1} & L_{n-1, 2} & \cdots & 1
|
| 769 |
+
\end{bmatrix}
|
| 770 |
+
|
| 771 |
+
.. math::
|
| 772 |
+
U = \begin{bmatrix}
|
| 773 |
+
U_{0, 0} & U_{0, 1} & U_{0, 2} & \cdots & U_{0, n-1} \\
|
| 774 |
+
0 & U_{1, 1} & U_{1, 2} & \cdots & U_{1, n-1} \\
|
| 775 |
+
0 & 0 & U_{2, 2} & \cdots & U_{2, n-1} \\
|
| 776 |
+
\vdots & \vdots & \vdots & \ddots & \vdots \\
|
| 777 |
+
0 & 0 & 0 & \cdots & U_{n-1, n-1}
|
| 778 |
+
\end{bmatrix}
|
| 779 |
+
|
| 780 |
+
And for a matrix with more rows than the columns,
|
| 781 |
+
the decomposition would look like:
|
| 782 |
+
|
| 783 |
+
.. math::
|
| 784 |
+
L = \begin{bmatrix}
|
| 785 |
+
1 & 0 & 0 & \cdots & 0 & 0 & \cdots & 0 \\
|
| 786 |
+
L_{1, 0} & 1 & 0 & \cdots & 0 & 0 & \cdots & 0 \\
|
| 787 |
+
L_{2, 0} & L_{2, 1} & 1 & \cdots & 0 & 0 & \cdots & 0 \\
|
| 788 |
+
\vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \ddots
|
| 789 |
+
& \vdots \\
|
| 790 |
+
L_{n-1, 0} & L_{n-1, 1} & L_{n-1, 2} & \cdots & 1 & 0
|
| 791 |
+
& \cdots & 0 \\
|
| 792 |
+
L_{n, 0} & L_{n, 1} & L_{n, 2} & \cdots & L_{n, n-1} & 1
|
| 793 |
+
& \cdots & 0 \\
|
| 794 |
+
\vdots & \vdots & \vdots & \ddots & \vdots & \vdots
|
| 795 |
+
& \ddots & \vdots \\
|
| 796 |
+
L_{m-1, 0} & L_{m-1, 1} & L_{m-1, 2} & \cdots & L_{m-1, n-1}
|
| 797 |
+
& 0 & \cdots & 1 \\
|
| 798 |
+
\end{bmatrix}
|
| 799 |
+
|
| 800 |
+
.. math::
|
| 801 |
+
U = \begin{bmatrix}
|
| 802 |
+
U_{0, 0} & U_{0, 1} & U_{0, 2} & \cdots & U_{0, n-1} \\
|
| 803 |
+
0 & U_{1, 1} & U_{1, 2} & \cdots & U_{1, n-1} \\
|
| 804 |
+
0 & 0 & U_{2, 2} & \cdots & U_{2, n-1} \\
|
| 805 |
+
\vdots & \vdots & \vdots & \ddots & \vdots \\
|
| 806 |
+
0 & 0 & 0 & \cdots & U_{n-1, n-1} \\
|
| 807 |
+
0 & 0 & 0 & \cdots & 0 \\
|
| 808 |
+
\vdots & \vdots & \vdots & \ddots & \vdots \\
|
| 809 |
+
0 & 0 & 0 & \cdots & 0
|
| 810 |
+
\end{bmatrix}
|
| 811 |
+
|
| 812 |
+
Finally, for a matrix with more columns than the rows, the
|
| 813 |
+
decomposition would look like:
|
| 814 |
+
|
| 815 |
+
.. math::
|
| 816 |
+
L = \begin{bmatrix}
|
| 817 |
+
1 & 0 & 0 & \cdots & 0 \\
|
| 818 |
+
L_{1, 0} & 1 & 0 & \cdots & 0 \\
|
| 819 |
+
L_{2, 0} & L_{2, 1} & 1 & \cdots & 0 \\
|
| 820 |
+
\vdots & \vdots & \vdots & \ddots & \vdots \\
|
| 821 |
+
L_{m-1, 0} & L_{m-1, 1} & L_{m-1, 2} & \cdots & 1
|
| 822 |
+
\end{bmatrix}
|
| 823 |
+
|
| 824 |
+
.. math::
|
| 825 |
+
U = \begin{bmatrix}
|
| 826 |
+
U_{0, 0} & U_{0, 1} & U_{0, 2} & \cdots & U_{0, m-1}
|
| 827 |
+
& \cdots & U_{0, n-1} \\
|
| 828 |
+
0 & U_{1, 1} & U_{1, 2} & \cdots & U_{1, m-1}
|
| 829 |
+
& \cdots & U_{1, n-1} \\
|
| 830 |
+
0 & 0 & U_{2, 2} & \cdots & U_{2, m-1}
|
| 831 |
+
& \cdots & U_{2, n-1} \\
|
| 832 |
+
\vdots & \vdots & \vdots & \ddots & \vdots
|
| 833 |
+
& \cdots & \vdots \\
|
| 834 |
+
0 & 0 & 0 & \cdots & U_{m-1, m-1}
|
| 835 |
+
& \cdots & U_{m-1, n-1} \\
|
| 836 |
+
\end{bmatrix}
|
| 837 |
+
|
| 838 |
+
About the compressed LU storage:
|
| 839 |
+
|
| 840 |
+
The results of the decomposition are often stored in compressed
|
| 841 |
+
forms rather than returning $L$ and $U$ matrices individually.
|
| 842 |
+
|
| 843 |
+
It may be less intiuitive, but it is commonly used for a lot of
|
| 844 |
+
numeric libraries because of the efficiency.
|
| 845 |
+
|
| 846 |
+
The storage matrix is defined as following for this specific
|
| 847 |
+
method:
|
| 848 |
+
|
| 849 |
+
* The subdiagonal elements of $L$ are stored in the subdiagonal
|
| 850 |
+
portion of $LU$, that is $LU_{i, j} = L_{i, j}$ whenever
|
| 851 |
+
$i > j$.
|
| 852 |
+
* The elements on the diagonal of $L$ are all 1, and are not
|
| 853 |
+
explicitly stored.
|
| 854 |
+
* $U$ is stored in the upper triangular portion of $LU$, that is
|
| 855 |
+
$LU_{i, j} = U_{i, j}$ whenever $i <= j$.
|
| 856 |
+
* For a case of $m > n$, the right side of the $L$ matrix is
|
| 857 |
+
trivial to store.
|
| 858 |
+
* For a case of $m < n$, the below side of the $U$ matrix is
|
| 859 |
+
trivial to store.
|
| 860 |
+
|
| 861 |
+
So, for a square matrix, the compressed output matrix would be:
|
| 862 |
+
|
| 863 |
+
.. math::
|
| 864 |
+
LU = \begin{bmatrix}
|
| 865 |
+
U_{0, 0} & U_{0, 1} & U_{0, 2} & \cdots & U_{0, n-1} \\
|
| 866 |
+
L_{1, 0} & U_{1, 1} & U_{1, 2} & \cdots & U_{1, n-1} \\
|
| 867 |
+
L_{2, 0} & L_{2, 1} & U_{2, 2} & \cdots & U_{2, n-1} \\
|
| 868 |
+
\vdots & \vdots & \vdots & \ddots & \vdots \\
|
| 869 |
+
L_{n-1, 0} & L_{n-1, 1} & L_{n-1, 2} & \cdots & U_{n-1, n-1}
|
| 870 |
+
\end{bmatrix}
|
| 871 |
+
|
| 872 |
+
For a matrix with more rows than the columns, the compressed
|
| 873 |
+
output matrix would be:
|
| 874 |
+
|
| 875 |
+
.. math::
|
| 876 |
+
LU = \begin{bmatrix}
|
| 877 |
+
U_{0, 0} & U_{0, 1} & U_{0, 2} & \cdots & U_{0, n-1} \\
|
| 878 |
+
L_{1, 0} & U_{1, 1} & U_{1, 2} & \cdots & U_{1, n-1} \\
|
| 879 |
+
L_{2, 0} & L_{2, 1} & U_{2, 2} & \cdots & U_{2, n-1} \\
|
| 880 |
+
\vdots & \vdots & \vdots & \ddots & \vdots \\
|
| 881 |
+
L_{n-1, 0} & L_{n-1, 1} & L_{n-1, 2} & \cdots
|
| 882 |
+
& U_{n-1, n-1} \\
|
| 883 |
+
\vdots & \vdots & \vdots & \ddots & \vdots \\
|
| 884 |
+
L_{m-1, 0} & L_{m-1, 1} & L_{m-1, 2} & \cdots
|
| 885 |
+
& L_{m-1, n-1} \\
|
| 886 |
+
\end{bmatrix}
|
| 887 |
+
|
| 888 |
+
For a matrix with more columns than the rows, the compressed
|
| 889 |
+
output matrix would be:
|
| 890 |
+
|
| 891 |
+
.. math::
|
| 892 |
+
LU = \begin{bmatrix}
|
| 893 |
+
U_{0, 0} & U_{0, 1} & U_{0, 2} & \cdots & U_{0, m-1}
|
| 894 |
+
& \cdots & U_{0, n-1} \\
|
| 895 |
+
L_{1, 0} & U_{1, 1} & U_{1, 2} & \cdots & U_{1, m-1}
|
| 896 |
+
& \cdots & U_{1, n-1} \\
|
| 897 |
+
L_{2, 0} & L_{2, 1} & U_{2, 2} & \cdots & U_{2, m-1}
|
| 898 |
+
& \cdots & U_{2, n-1} \\
|
| 899 |
+
\vdots & \vdots & \vdots & \ddots & \vdots
|
| 900 |
+
& \cdots & \vdots \\
|
| 901 |
+
L_{m-1, 0} & L_{m-1, 1} & L_{m-1, 2} & \cdots & U_{m-1, m-1}
|
| 902 |
+
& \cdots & U_{m-1, n-1} \\
|
| 903 |
+
\end{bmatrix}
|
| 904 |
+
|
| 905 |
+
About the pivot searching algorithm:
|
| 906 |
+
|
| 907 |
+
When a matrix contains symbolic entries, the pivot search algorithm
|
| 908 |
+
differs from the case where every entry can be categorized as zero or
|
| 909 |
+
nonzero.
|
| 910 |
+
The algorithm searches column by column through the submatrix whose
|
| 911 |
+
top left entry coincides with the pivot position.
|
| 912 |
+
If it exists, the pivot is the first entry in the current search
|
| 913 |
+
column that iszerofunc guarantees is nonzero.
|
| 914 |
+
If no such candidate exists, then each candidate pivot is simplified
|
| 915 |
+
if simpfunc is not None.
|
| 916 |
+
The search is repeated, with the difference that a candidate may be
|
| 917 |
+
the pivot if ``iszerofunc()`` cannot guarantee that it is nonzero.
|
| 918 |
+
In the second search the pivot is the first candidate that
|
| 919 |
+
iszerofunc can guarantee is nonzero.
|
| 920 |
+
If no such candidate exists, then the pivot is the first candidate
|
| 921 |
+
for which iszerofunc returns None.
|
| 922 |
+
If no such candidate exists, then the search is repeated in the next
|
| 923 |
+
column to the right.
|
| 924 |
+
The pivot search algorithm differs from the one in ``rref()``, which
|
| 925 |
+
relies on ``_find_reasonable_pivot()``.
|
| 926 |
+
Future versions of ``LUdecomposition_simple()`` may use
|
| 927 |
+
``_find_reasonable_pivot()``.
|
| 928 |
+
|
| 929 |
+
See Also
|
| 930 |
+
========
|
| 931 |
+
|
| 932 |
+
sympy.matrices.matrixbase.MatrixBase.LUdecomposition
|
| 933 |
+
LUdecompositionFF
|
| 934 |
+
LUsolve
|
| 935 |
+
"""
|
| 936 |
+
|
| 937 |
+
if rankcheck:
|
| 938 |
+
# https://github.com/sympy/sympy/issues/9796
|
| 939 |
+
pass
|
| 940 |
+
|
| 941 |
+
if S.Zero in M.shape:
|
| 942 |
+
# Define LU decomposition of a matrix with no entries as a matrix
|
| 943 |
+
# of the same dimensions with all zero entries.
|
| 944 |
+
return M.zeros(M.rows, M.cols), []
|
| 945 |
+
|
| 946 |
+
dps = _get_intermediate_simp()
|
| 947 |
+
lu = M.as_mutable()
|
| 948 |
+
row_swaps = []
|
| 949 |
+
|
| 950 |
+
pivot_col = 0
|
| 951 |
+
|
| 952 |
+
for pivot_row in range(0, lu.rows - 1):
|
| 953 |
+
# Search for pivot. Prefer entry that iszeropivot determines
|
| 954 |
+
# is nonzero, over entry that iszeropivot cannot guarantee
|
| 955 |
+
# is zero.
|
| 956 |
+
# XXX ``_find_reasonable_pivot`` uses slow zero testing. Blocked by bug #10279
|
| 957 |
+
# Future versions of LUdecomposition_simple can pass iszerofunc and simpfunc
|
| 958 |
+
# to _find_reasonable_pivot().
|
| 959 |
+
# In pass 3 of _find_reasonable_pivot(), the predicate in ``if x.equals(S.Zero):``
|
| 960 |
+
# calls sympy.simplify(), and not the simplification function passed in via
|
| 961 |
+
# the keyword argument simpfunc.
|
| 962 |
+
iszeropivot = True
|
| 963 |
+
|
| 964 |
+
while pivot_col != M.cols and iszeropivot:
|
| 965 |
+
sub_col = (lu[r, pivot_col] for r in range(pivot_row, M.rows))
|
| 966 |
+
|
| 967 |
+
pivot_row_offset, pivot_value, is_assumed_non_zero, ind_simplified_pairs =\
|
| 968 |
+
_find_reasonable_pivot_naive(sub_col, iszerofunc, simpfunc)
|
| 969 |
+
|
| 970 |
+
iszeropivot = pivot_value is None
|
| 971 |
+
|
| 972 |
+
if iszeropivot:
|
| 973 |
+
# All candidate pivots in this column are zero.
|
| 974 |
+
# Proceed to next column.
|
| 975 |
+
pivot_col += 1
|
| 976 |
+
|
| 977 |
+
if rankcheck and pivot_col != pivot_row:
|
| 978 |
+
# All entries including and below the pivot position are
|
| 979 |
+
# zero, which indicates that the rank of the matrix is
|
| 980 |
+
# strictly less than min(num rows, num cols)
|
| 981 |
+
# Mimic behavior of previous implementation, by throwing a
|
| 982 |
+
# ValueError.
|
| 983 |
+
raise ValueError("Rank of matrix is strictly less than"
|
| 984 |
+
" number of rows or columns."
|
| 985 |
+
" Pass keyword argument"
|
| 986 |
+
" rankcheck=False to compute"
|
| 987 |
+
" the LU decomposition of this matrix.")
|
| 988 |
+
|
| 989 |
+
candidate_pivot_row = None if pivot_row_offset is None else pivot_row + pivot_row_offset
|
| 990 |
+
|
| 991 |
+
if candidate_pivot_row is None and iszeropivot:
|
| 992 |
+
# If candidate_pivot_row is None and iszeropivot is True
|
| 993 |
+
# after pivot search has completed, then the submatrix
|
| 994 |
+
# below and to the right of (pivot_row, pivot_col) is
|
| 995 |
+
# all zeros, indicating that Gaussian elimination is
|
| 996 |
+
# complete.
|
| 997 |
+
return lu, row_swaps
|
| 998 |
+
|
| 999 |
+
# Update entries simplified during pivot search.
|
| 1000 |
+
for offset, val in ind_simplified_pairs:
|
| 1001 |
+
lu[pivot_row + offset, pivot_col] = val
|
| 1002 |
+
|
| 1003 |
+
if pivot_row != candidate_pivot_row:
|
| 1004 |
+
# Row swap book keeping:
|
| 1005 |
+
# Record which rows were swapped.
|
| 1006 |
+
# Update stored portion of L factor by multiplying L on the
|
| 1007 |
+
# left and right with the current permutation.
|
| 1008 |
+
# Swap rows of U.
|
| 1009 |
+
row_swaps.append([pivot_row, candidate_pivot_row])
|
| 1010 |
+
|
| 1011 |
+
# Update L.
|
| 1012 |
+
lu[pivot_row, 0:pivot_row], lu[candidate_pivot_row, 0:pivot_row] = \
|
| 1013 |
+
lu[candidate_pivot_row, 0:pivot_row], lu[pivot_row, 0:pivot_row]
|
| 1014 |
+
|
| 1015 |
+
# Swap pivot row of U with candidate pivot row.
|
| 1016 |
+
lu[pivot_row, pivot_col:lu.cols], lu[candidate_pivot_row, pivot_col:lu.cols] = \
|
| 1017 |
+
lu[candidate_pivot_row, pivot_col:lu.cols], lu[pivot_row, pivot_col:lu.cols]
|
| 1018 |
+
|
| 1019 |
+
# Introduce zeros below the pivot by adding a multiple of the
|
| 1020 |
+
# pivot row to a row under it, and store the result in the
|
| 1021 |
+
# row under it.
|
| 1022 |
+
# Only entries in the target row whose index is greater than
|
| 1023 |
+
# start_col may be nonzero.
|
| 1024 |
+
start_col = pivot_col + 1
|
| 1025 |
+
|
| 1026 |
+
for row in range(pivot_row + 1, lu.rows):
|
| 1027 |
+
# Store factors of L in the subcolumn below
|
| 1028 |
+
# (pivot_row, pivot_row).
|
| 1029 |
+
lu[row, pivot_row] = \
|
| 1030 |
+
dps(lu[row, pivot_col]/lu[pivot_row, pivot_col])
|
| 1031 |
+
|
| 1032 |
+
# Form the linear combination of the pivot row and the current
|
| 1033 |
+
# row below the pivot row that zeros the entries below the pivot.
|
| 1034 |
+
# Employing slicing instead of a loop here raises
|
| 1035 |
+
# NotImplementedError: Cannot add Zero to MutableSparseMatrix
|
| 1036 |
+
# in sympy/matrices/tests/test_sparse.py.
|
| 1037 |
+
# c = pivot_row + 1 if pivot_row == pivot_col else pivot_col
|
| 1038 |
+
for c in range(start_col, lu.cols):
|
| 1039 |
+
lu[row, c] = dps(lu[row, c] - lu[row, pivot_row]*lu[pivot_row, c])
|
| 1040 |
+
|
| 1041 |
+
if pivot_row != pivot_col:
|
| 1042 |
+
# matrix rank < min(num rows, num cols),
|
| 1043 |
+
# so factors of L are not stored directly below the pivot.
|
| 1044 |
+
# These entries are zero by construction, so don't bother
|
| 1045 |
+
# computing them.
|
| 1046 |
+
for row in range(pivot_row + 1, lu.rows):
|
| 1047 |
+
lu[row, pivot_col] = M.zero
|
| 1048 |
+
|
| 1049 |
+
pivot_col += 1
|
| 1050 |
+
|
| 1051 |
+
if pivot_col == lu.cols:
|
| 1052 |
+
# All candidate pivots are zero implies that Gaussian
|
| 1053 |
+
# elimination is complete.
|
| 1054 |
+
return lu, row_swaps
|
| 1055 |
+
|
| 1056 |
+
if rankcheck:
|
| 1057 |
+
if iszerofunc(
|
| 1058 |
+
lu[Min(lu.rows, lu.cols) - 1, Min(lu.rows, lu.cols) - 1]):
|
| 1059 |
+
raise ValueError("Rank of matrix is strictly less than"
|
| 1060 |
+
" number of rows or columns."
|
| 1061 |
+
" Pass keyword argument"
|
| 1062 |
+
" rankcheck=False to compute"
|
| 1063 |
+
" the LU decomposition of this matrix.")
|
| 1064 |
+
|
| 1065 |
+
return lu, row_swaps
|
| 1066 |
+
|
| 1067 |
+
def _LUdecompositionFF(M):
|
| 1068 |
+
"""Compute a fraction-free LU decomposition.
|
| 1069 |
+
|
| 1070 |
+
Returns 4 matrices P, L, D, U such that PA = L D**-1 U.
|
| 1071 |
+
If the elements of the matrix belong to some integral domain I, then all
|
| 1072 |
+
elements of L, D and U are guaranteed to belong to I.
|
| 1073 |
+
|
| 1074 |
+
See Also
|
| 1075 |
+
========
|
| 1076 |
+
|
| 1077 |
+
sympy.matrices.matrixbase.MatrixBase.LUdecomposition
|
| 1078 |
+
LUdecomposition_Simple
|
| 1079 |
+
LUsolve
|
| 1080 |
+
|
| 1081 |
+
References
|
| 1082 |
+
==========
|
| 1083 |
+
|
| 1084 |
+
.. [1] W. Zhou & D.J. Jeffrey, "Fraction-free matrix factors: new forms
|
| 1085 |
+
for LU and QR factors". Frontiers in Computer Science in China,
|
| 1086 |
+
Vol 2, no. 1, pp. 67-80, 2008.
|
| 1087 |
+
"""
|
| 1088 |
+
|
| 1089 |
+
from sympy.matrices import SparseMatrix
|
| 1090 |
+
|
| 1091 |
+
zeros = SparseMatrix.zeros
|
| 1092 |
+
eye = SparseMatrix.eye
|
| 1093 |
+
n, m = M.rows, M.cols
|
| 1094 |
+
U, L, P = M.as_mutable(), eye(n), eye(n)
|
| 1095 |
+
DD = zeros(n, n)
|
| 1096 |
+
oldpivot = 1
|
| 1097 |
+
|
| 1098 |
+
for k in range(n - 1):
|
| 1099 |
+
if U[k, k] == 0:
|
| 1100 |
+
for kpivot in range(k + 1, n):
|
| 1101 |
+
if U[kpivot, k]:
|
| 1102 |
+
break
|
| 1103 |
+
else:
|
| 1104 |
+
raise ValueError("Matrix is not full rank")
|
| 1105 |
+
|
| 1106 |
+
U[k, k:], U[kpivot, k:] = U[kpivot, k:], U[k, k:]
|
| 1107 |
+
L[k, :k], L[kpivot, :k] = L[kpivot, :k], L[k, :k]
|
| 1108 |
+
P[k, :], P[kpivot, :] = P[kpivot, :], P[k, :]
|
| 1109 |
+
|
| 1110 |
+
L [k, k] = Ukk = U[k, k]
|
| 1111 |
+
DD[k, k] = oldpivot * Ukk
|
| 1112 |
+
|
| 1113 |
+
for i in range(k + 1, n):
|
| 1114 |
+
L[i, k] = Uik = U[i, k]
|
| 1115 |
+
|
| 1116 |
+
for j in range(k + 1, m):
|
| 1117 |
+
U[i, j] = (Ukk * U[i, j] - U[k, j] * Uik) / oldpivot
|
| 1118 |
+
|
| 1119 |
+
U[i, k] = 0
|
| 1120 |
+
|
| 1121 |
+
oldpivot = Ukk
|
| 1122 |
+
|
| 1123 |
+
DD[n - 1, n - 1] = oldpivot
|
| 1124 |
+
|
| 1125 |
+
return P, L, DD, U
|
| 1126 |
+
|
| 1127 |
+
def _singular_value_decomposition(A):
|
| 1128 |
+
r"""Returns a Condensed Singular Value decomposition.
|
| 1129 |
+
|
| 1130 |
+
Explanation
|
| 1131 |
+
===========
|
| 1132 |
+
|
| 1133 |
+
A Singular Value decomposition is a decomposition in the form $A = U \Sigma V^H$
|
| 1134 |
+
where
|
| 1135 |
+
|
| 1136 |
+
- $U, V$ are column orthogonal matrix.
|
| 1137 |
+
- $\Sigma$ is a diagonal matrix, where the main diagonal contains singular
|
| 1138 |
+
values of matrix A.
|
| 1139 |
+
|
| 1140 |
+
A column orthogonal matrix satisfies
|
| 1141 |
+
$\mathbb{I} = U^H U$ while a full orthogonal matrix satisfies
|
| 1142 |
+
relation $\mathbb{I} = U U^H = U^H U$ where $\mathbb{I}$ is an identity
|
| 1143 |
+
matrix with matching dimensions.
|
| 1144 |
+
|
| 1145 |
+
For matrices which are not square or are rank-deficient, it is
|
| 1146 |
+
sufficient to return a column orthogonal matrix because augmenting
|
| 1147 |
+
them may introduce redundant computations.
|
| 1148 |
+
In condensed Singular Value Decomposition we only return column orthogonal
|
| 1149 |
+
matrices because of this reason
|
| 1150 |
+
|
| 1151 |
+
If you want to augment the results to return a full orthogonal
|
| 1152 |
+
decomposition, you should use the following procedures.
|
| 1153 |
+
|
| 1154 |
+
- Augment the $U, V$ matrices with columns that are orthogonal to every
|
| 1155 |
+
other columns and make it square.
|
| 1156 |
+
- Augment the $\Sigma$ matrix with zero rows to make it have the same
|
| 1157 |
+
shape as the original matrix.
|
| 1158 |
+
|
| 1159 |
+
The procedure will be illustrated in the examples section.
|
| 1160 |
+
|
| 1161 |
+
Examples
|
| 1162 |
+
========
|
| 1163 |
+
|
| 1164 |
+
we take a full rank matrix first:
|
| 1165 |
+
|
| 1166 |
+
>>> from sympy import Matrix
|
| 1167 |
+
>>> A = Matrix([[1, 2],[2,1]])
|
| 1168 |
+
>>> U, S, V = A.singular_value_decomposition()
|
| 1169 |
+
>>> U
|
| 1170 |
+
Matrix([
|
| 1171 |
+
[ sqrt(2)/2, sqrt(2)/2],
|
| 1172 |
+
[-sqrt(2)/2, sqrt(2)/2]])
|
| 1173 |
+
>>> S
|
| 1174 |
+
Matrix([
|
| 1175 |
+
[1, 0],
|
| 1176 |
+
[0, 3]])
|
| 1177 |
+
>>> V
|
| 1178 |
+
Matrix([
|
| 1179 |
+
[-sqrt(2)/2, sqrt(2)/2],
|
| 1180 |
+
[ sqrt(2)/2, sqrt(2)/2]])
|
| 1181 |
+
|
| 1182 |
+
If a matrix if square and full rank both U, V
|
| 1183 |
+
are orthogonal in both directions
|
| 1184 |
+
|
| 1185 |
+
>>> U * U.H
|
| 1186 |
+
Matrix([
|
| 1187 |
+
[1, 0],
|
| 1188 |
+
[0, 1]])
|
| 1189 |
+
>>> U.H * U
|
| 1190 |
+
Matrix([
|
| 1191 |
+
[1, 0],
|
| 1192 |
+
[0, 1]])
|
| 1193 |
+
|
| 1194 |
+
>>> V * V.H
|
| 1195 |
+
Matrix([
|
| 1196 |
+
[1, 0],
|
| 1197 |
+
[0, 1]])
|
| 1198 |
+
>>> V.H * V
|
| 1199 |
+
Matrix([
|
| 1200 |
+
[1, 0],
|
| 1201 |
+
[0, 1]])
|
| 1202 |
+
>>> A == U * S * V.H
|
| 1203 |
+
True
|
| 1204 |
+
|
| 1205 |
+
>>> C = Matrix([
|
| 1206 |
+
... [1, 0, 0, 0, 2],
|
| 1207 |
+
... [0, 0, 3, 0, 0],
|
| 1208 |
+
... [0, 0, 0, 0, 0],
|
| 1209 |
+
... [0, 2, 0, 0, 0],
|
| 1210 |
+
... ])
|
| 1211 |
+
>>> U, S, V = C.singular_value_decomposition()
|
| 1212 |
+
|
| 1213 |
+
>>> V.H * V
|
| 1214 |
+
Matrix([
|
| 1215 |
+
[1, 0, 0],
|
| 1216 |
+
[0, 1, 0],
|
| 1217 |
+
[0, 0, 1]])
|
| 1218 |
+
>>> V * V.H
|
| 1219 |
+
Matrix([
|
| 1220 |
+
[1/5, 0, 0, 0, 2/5],
|
| 1221 |
+
[ 0, 1, 0, 0, 0],
|
| 1222 |
+
[ 0, 0, 1, 0, 0],
|
| 1223 |
+
[ 0, 0, 0, 0, 0],
|
| 1224 |
+
[2/5, 0, 0, 0, 4/5]])
|
| 1225 |
+
|
| 1226 |
+
If you want to augment the results to be a full orthogonal
|
| 1227 |
+
decomposition, you should augment $V$ with an another orthogonal
|
| 1228 |
+
column.
|
| 1229 |
+
|
| 1230 |
+
You are able to append an arbitrary standard basis that are linearly
|
| 1231 |
+
independent to every other columns and you can run the Gram-Schmidt
|
| 1232 |
+
process to make them augmented as orthogonal basis.
|
| 1233 |
+
|
| 1234 |
+
>>> V_aug = V.row_join(Matrix([[0,0,0,0,1],
|
| 1235 |
+
... [0,0,0,1,0]]).H)
|
| 1236 |
+
>>> V_aug = V_aug.QRdecomposition()[0]
|
| 1237 |
+
>>> V_aug
|
| 1238 |
+
Matrix([
|
| 1239 |
+
[0, sqrt(5)/5, 0, -2*sqrt(5)/5, 0],
|
| 1240 |
+
[1, 0, 0, 0, 0],
|
| 1241 |
+
[0, 0, 1, 0, 0],
|
| 1242 |
+
[0, 0, 0, 0, 1],
|
| 1243 |
+
[0, 2*sqrt(5)/5, 0, sqrt(5)/5, 0]])
|
| 1244 |
+
>>> V_aug.H * V_aug
|
| 1245 |
+
Matrix([
|
| 1246 |
+
[1, 0, 0, 0, 0],
|
| 1247 |
+
[0, 1, 0, 0, 0],
|
| 1248 |
+
[0, 0, 1, 0, 0],
|
| 1249 |
+
[0, 0, 0, 1, 0],
|
| 1250 |
+
[0, 0, 0, 0, 1]])
|
| 1251 |
+
>>> V_aug * V_aug.H
|
| 1252 |
+
Matrix([
|
| 1253 |
+
[1, 0, 0, 0, 0],
|
| 1254 |
+
[0, 1, 0, 0, 0],
|
| 1255 |
+
[0, 0, 1, 0, 0],
|
| 1256 |
+
[0, 0, 0, 1, 0],
|
| 1257 |
+
[0, 0, 0, 0, 1]])
|
| 1258 |
+
|
| 1259 |
+
Similarly we augment U
|
| 1260 |
+
|
| 1261 |
+
>>> U_aug = U.row_join(Matrix([0,0,1,0]))
|
| 1262 |
+
>>> U_aug = U_aug.QRdecomposition()[0]
|
| 1263 |
+
>>> U_aug
|
| 1264 |
+
Matrix([
|
| 1265 |
+
[0, 1, 0, 0],
|
| 1266 |
+
[0, 0, 1, 0],
|
| 1267 |
+
[0, 0, 0, 1],
|
| 1268 |
+
[1, 0, 0, 0]])
|
| 1269 |
+
|
| 1270 |
+
>>> U_aug.H * U_aug
|
| 1271 |
+
Matrix([
|
| 1272 |
+
[1, 0, 0, 0],
|
| 1273 |
+
[0, 1, 0, 0],
|
| 1274 |
+
[0, 0, 1, 0],
|
| 1275 |
+
[0, 0, 0, 1]])
|
| 1276 |
+
>>> U_aug * U_aug.H
|
| 1277 |
+
Matrix([
|
| 1278 |
+
[1, 0, 0, 0],
|
| 1279 |
+
[0, 1, 0, 0],
|
| 1280 |
+
[0, 0, 1, 0],
|
| 1281 |
+
[0, 0, 0, 1]])
|
| 1282 |
+
|
| 1283 |
+
We add 2 zero columns and one row to S
|
| 1284 |
+
|
| 1285 |
+
>>> S_aug = S.col_join(Matrix([[0,0,0]]))
|
| 1286 |
+
>>> S_aug = S_aug.row_join(Matrix([[0,0,0,0],
|
| 1287 |
+
... [0,0,0,0]]).H)
|
| 1288 |
+
>>> S_aug
|
| 1289 |
+
Matrix([
|
| 1290 |
+
[2, 0, 0, 0, 0],
|
| 1291 |
+
[0, sqrt(5), 0, 0, 0],
|
| 1292 |
+
[0, 0, 3, 0, 0],
|
| 1293 |
+
[0, 0, 0, 0, 0]])
|
| 1294 |
+
|
| 1295 |
+
|
| 1296 |
+
|
| 1297 |
+
>>> U_aug * S_aug * V_aug.H == C
|
| 1298 |
+
True
|
| 1299 |
+
|
| 1300 |
+
"""
|
| 1301 |
+
|
| 1302 |
+
AH = A.H
|
| 1303 |
+
m, n = A.shape
|
| 1304 |
+
if m >= n:
|
| 1305 |
+
V, S = (AH * A).diagonalize()
|
| 1306 |
+
|
| 1307 |
+
ranked = []
|
| 1308 |
+
for i, x in enumerate(S.diagonal()):
|
| 1309 |
+
if not x.is_zero:
|
| 1310 |
+
ranked.append(i)
|
| 1311 |
+
|
| 1312 |
+
V = V[:, ranked]
|
| 1313 |
+
|
| 1314 |
+
Singular_vals = [sqrt(S[i, i]) for i in range(S.rows) if i in ranked]
|
| 1315 |
+
|
| 1316 |
+
S = S.diag(*Singular_vals)
|
| 1317 |
+
V, _ = V.QRdecomposition()
|
| 1318 |
+
U = A * V * S.inv()
|
| 1319 |
+
else:
|
| 1320 |
+
U, S = (A * AH).diagonalize()
|
| 1321 |
+
|
| 1322 |
+
ranked = []
|
| 1323 |
+
for i, x in enumerate(S.diagonal()):
|
| 1324 |
+
if not x.is_zero:
|
| 1325 |
+
ranked.append(i)
|
| 1326 |
+
|
| 1327 |
+
U = U[:, ranked]
|
| 1328 |
+
Singular_vals = [sqrt(S[i, i]) for i in range(S.rows) if i in ranked]
|
| 1329 |
+
|
| 1330 |
+
S = S.diag(*Singular_vals)
|
| 1331 |
+
U, _ = U.QRdecomposition()
|
| 1332 |
+
V = AH * U * S.inv()
|
| 1333 |
+
|
| 1334 |
+
return U, S, V
|
| 1335 |
+
|
| 1336 |
+
def _QRdecomposition_optional(M, normalize=True):
|
| 1337 |
+
def dot(u, v):
|
| 1338 |
+
return u.dot(v, hermitian=True)
|
| 1339 |
+
|
| 1340 |
+
dps = _get_intermediate_simp(expand_mul, expand_mul)
|
| 1341 |
+
|
| 1342 |
+
A = M.as_mutable()
|
| 1343 |
+
ranked = []
|
| 1344 |
+
|
| 1345 |
+
Q = A
|
| 1346 |
+
R = A.zeros(A.cols)
|
| 1347 |
+
|
| 1348 |
+
for j in range(A.cols):
|
| 1349 |
+
for i in range(j):
|
| 1350 |
+
if Q[:, i].is_zero_matrix:
|
| 1351 |
+
continue
|
| 1352 |
+
|
| 1353 |
+
R[i, j] = dot(Q[:, i], Q[:, j]) / dot(Q[:, i], Q[:, i])
|
| 1354 |
+
R[i, j] = dps(R[i, j])
|
| 1355 |
+
Q[:, j] -= Q[:, i] * R[i, j]
|
| 1356 |
+
|
| 1357 |
+
Q[:, j] = dps(Q[:, j])
|
| 1358 |
+
if Q[:, j].is_zero_matrix is not True:
|
| 1359 |
+
ranked.append(j)
|
| 1360 |
+
R[j, j] = M.one
|
| 1361 |
+
|
| 1362 |
+
Q = Q.extract(range(Q.rows), ranked)
|
| 1363 |
+
R = R.extract(ranked, range(R.cols))
|
| 1364 |
+
|
| 1365 |
+
if normalize:
|
| 1366 |
+
# Normalization
|
| 1367 |
+
for i in range(Q.cols):
|
| 1368 |
+
norm = Q[:, i].norm()
|
| 1369 |
+
Q[:, i] /= norm
|
| 1370 |
+
R[i, :] *= norm
|
| 1371 |
+
|
| 1372 |
+
return M.__class__(Q), M.__class__(R)
|
| 1373 |
+
|
| 1374 |
+
|
| 1375 |
+
def _QRdecomposition(M):
|
| 1376 |
+
r"""Returns a QR decomposition.
|
| 1377 |
+
|
| 1378 |
+
Explanation
|
| 1379 |
+
===========
|
| 1380 |
+
|
| 1381 |
+
A QR decomposition is a decomposition in the form $A = Q R$
|
| 1382 |
+
where
|
| 1383 |
+
|
| 1384 |
+
- $Q$ is a column orthogonal matrix.
|
| 1385 |
+
- $R$ is a upper triangular (trapezoidal) matrix.
|
| 1386 |
+
|
| 1387 |
+
A column orthogonal matrix satisfies
|
| 1388 |
+
$\mathbb{I} = Q^H Q$ while a full orthogonal matrix satisfies
|
| 1389 |
+
relation $\mathbb{I} = Q Q^H = Q^H Q$ where $I$ is an identity
|
| 1390 |
+
matrix with matching dimensions.
|
| 1391 |
+
|
| 1392 |
+
For matrices which are not square or are rank-deficient, it is
|
| 1393 |
+
sufficient to return a column orthogonal matrix because augmenting
|
| 1394 |
+
them may introduce redundant computations.
|
| 1395 |
+
And an another advantage of this is that you can easily inspect the
|
| 1396 |
+
matrix rank by counting the number of columns of $Q$.
|
| 1397 |
+
|
| 1398 |
+
If you want to augment the results to return a full orthogonal
|
| 1399 |
+
decomposition, you should use the following procedures.
|
| 1400 |
+
|
| 1401 |
+
- Augment the $Q$ matrix with columns that are orthogonal to every
|
| 1402 |
+
other columns and make it square.
|
| 1403 |
+
- Augment the $R$ matrix with zero rows to make it have the same
|
| 1404 |
+
shape as the original matrix.
|
| 1405 |
+
|
| 1406 |
+
The procedure will be illustrated in the examples section.
|
| 1407 |
+
|
| 1408 |
+
Examples
|
| 1409 |
+
========
|
| 1410 |
+
|
| 1411 |
+
A full rank matrix example:
|
| 1412 |
+
|
| 1413 |
+
>>> from sympy import Matrix
|
| 1414 |
+
>>> A = Matrix([[12, -51, 4], [6, 167, -68], [-4, 24, -41]])
|
| 1415 |
+
>>> Q, R = A.QRdecomposition()
|
| 1416 |
+
>>> Q
|
| 1417 |
+
Matrix([
|
| 1418 |
+
[ 6/7, -69/175, -58/175],
|
| 1419 |
+
[ 3/7, 158/175, 6/175],
|
| 1420 |
+
[-2/7, 6/35, -33/35]])
|
| 1421 |
+
>>> R
|
| 1422 |
+
Matrix([
|
| 1423 |
+
[14, 21, -14],
|
| 1424 |
+
[ 0, 175, -70],
|
| 1425 |
+
[ 0, 0, 35]])
|
| 1426 |
+
|
| 1427 |
+
If the matrix is square and full rank, the $Q$ matrix becomes
|
| 1428 |
+
orthogonal in both directions, and needs no augmentation.
|
| 1429 |
+
|
| 1430 |
+
>>> Q * Q.H
|
| 1431 |
+
Matrix([
|
| 1432 |
+
[1, 0, 0],
|
| 1433 |
+
[0, 1, 0],
|
| 1434 |
+
[0, 0, 1]])
|
| 1435 |
+
>>> Q.H * Q
|
| 1436 |
+
Matrix([
|
| 1437 |
+
[1, 0, 0],
|
| 1438 |
+
[0, 1, 0],
|
| 1439 |
+
[0, 0, 1]])
|
| 1440 |
+
|
| 1441 |
+
>>> A == Q*R
|
| 1442 |
+
True
|
| 1443 |
+
|
| 1444 |
+
A rank deficient matrix example:
|
| 1445 |
+
|
| 1446 |
+
>>> A = Matrix([[12, -51, 0], [6, 167, 0], [-4, 24, 0]])
|
| 1447 |
+
>>> Q, R = A.QRdecomposition()
|
| 1448 |
+
>>> Q
|
| 1449 |
+
Matrix([
|
| 1450 |
+
[ 6/7, -69/175],
|
| 1451 |
+
[ 3/7, 158/175],
|
| 1452 |
+
[-2/7, 6/35]])
|
| 1453 |
+
>>> R
|
| 1454 |
+
Matrix([
|
| 1455 |
+
[14, 21, 0],
|
| 1456 |
+
[ 0, 175, 0]])
|
| 1457 |
+
|
| 1458 |
+
QRdecomposition might return a matrix Q that is rectangular.
|
| 1459 |
+
In this case the orthogonality condition might be satisfied as
|
| 1460 |
+
$\mathbb{I} = Q.H*Q$ but not in the reversed product
|
| 1461 |
+
$\mathbb{I} = Q * Q.H$.
|
| 1462 |
+
|
| 1463 |
+
>>> Q.H * Q
|
| 1464 |
+
Matrix([
|
| 1465 |
+
[1, 0],
|
| 1466 |
+
[0, 1]])
|
| 1467 |
+
>>> Q * Q.H
|
| 1468 |
+
Matrix([
|
| 1469 |
+
[27261/30625, 348/30625, -1914/6125],
|
| 1470 |
+
[ 348/30625, 30589/30625, 198/6125],
|
| 1471 |
+
[ -1914/6125, 198/6125, 136/1225]])
|
| 1472 |
+
|
| 1473 |
+
If you want to augment the results to be a full orthogonal
|
| 1474 |
+
decomposition, you should augment $Q$ with an another orthogonal
|
| 1475 |
+
column.
|
| 1476 |
+
|
| 1477 |
+
You are able to append an identity matrix,
|
| 1478 |
+
and you can run the Gram-Schmidt
|
| 1479 |
+
process to make them augmented as orthogonal basis.
|
| 1480 |
+
|
| 1481 |
+
>>> Q_aug = Q.row_join(Matrix.eye(3))
|
| 1482 |
+
>>> Q_aug = Q_aug.QRdecomposition()[0]
|
| 1483 |
+
>>> Q_aug
|
| 1484 |
+
Matrix([
|
| 1485 |
+
[ 6/7, -69/175, 58/175],
|
| 1486 |
+
[ 3/7, 158/175, -6/175],
|
| 1487 |
+
[-2/7, 6/35, 33/35]])
|
| 1488 |
+
>>> Q_aug.H * Q_aug
|
| 1489 |
+
Matrix([
|
| 1490 |
+
[1, 0, 0],
|
| 1491 |
+
[0, 1, 0],
|
| 1492 |
+
[0, 0, 1]])
|
| 1493 |
+
>>> Q_aug * Q_aug.H
|
| 1494 |
+
Matrix([
|
| 1495 |
+
[1, 0, 0],
|
| 1496 |
+
[0, 1, 0],
|
| 1497 |
+
[0, 0, 1]])
|
| 1498 |
+
|
| 1499 |
+
Augmenting the $R$ matrix with zero row is straightforward.
|
| 1500 |
+
|
| 1501 |
+
>>> R_aug = R.col_join(Matrix([[0, 0, 0]]))
|
| 1502 |
+
>>> R_aug
|
| 1503 |
+
Matrix([
|
| 1504 |
+
[14, 21, 0],
|
| 1505 |
+
[ 0, 175, 0],
|
| 1506 |
+
[ 0, 0, 0]])
|
| 1507 |
+
>>> Q_aug * R_aug == A
|
| 1508 |
+
True
|
| 1509 |
+
|
| 1510 |
+
A zero matrix example:
|
| 1511 |
+
|
| 1512 |
+
>>> from sympy import Matrix
|
| 1513 |
+
>>> A = Matrix.zeros(3, 4)
|
| 1514 |
+
>>> Q, R = A.QRdecomposition()
|
| 1515 |
+
|
| 1516 |
+
They may return matrices with zero rows and columns.
|
| 1517 |
+
|
| 1518 |
+
>>> Q
|
| 1519 |
+
Matrix(3, 0, [])
|
| 1520 |
+
>>> R
|
| 1521 |
+
Matrix(0, 4, [])
|
| 1522 |
+
>>> Q*R
|
| 1523 |
+
Matrix([
|
| 1524 |
+
[0, 0, 0, 0],
|
| 1525 |
+
[0, 0, 0, 0],
|
| 1526 |
+
[0, 0, 0, 0]])
|
| 1527 |
+
|
| 1528 |
+
As the same augmentation rule described above, $Q$ can be augmented
|
| 1529 |
+
with columns of an identity matrix and $R$ can be augmented with
|
| 1530 |
+
rows of a zero matrix.
|
| 1531 |
+
|
| 1532 |
+
>>> Q_aug = Q.row_join(Matrix.eye(3))
|
| 1533 |
+
>>> R_aug = R.col_join(Matrix.zeros(3, 4))
|
| 1534 |
+
>>> Q_aug * Q_aug.T
|
| 1535 |
+
Matrix([
|
| 1536 |
+
[1, 0, 0],
|
| 1537 |
+
[0, 1, 0],
|
| 1538 |
+
[0, 0, 1]])
|
| 1539 |
+
>>> R_aug
|
| 1540 |
+
Matrix([
|
| 1541 |
+
[0, 0, 0, 0],
|
| 1542 |
+
[0, 0, 0, 0],
|
| 1543 |
+
[0, 0, 0, 0]])
|
| 1544 |
+
>>> Q_aug * R_aug == A
|
| 1545 |
+
True
|
| 1546 |
+
|
| 1547 |
+
See Also
|
| 1548 |
+
========
|
| 1549 |
+
|
| 1550 |
+
sympy.matrices.dense.DenseMatrix.cholesky
|
| 1551 |
+
sympy.matrices.dense.DenseMatrix.LDLdecomposition
|
| 1552 |
+
sympy.matrices.matrixbase.MatrixBase.LUdecomposition
|
| 1553 |
+
QRsolve
|
| 1554 |
+
"""
|
| 1555 |
+
return _QRdecomposition_optional(M, normalize=True)
|
| 1556 |
+
|
| 1557 |
+
def _upper_hessenberg_decomposition(A):
|
| 1558 |
+
"""Converts a matrix into Hessenberg matrix H.
|
| 1559 |
+
|
| 1560 |
+
Returns 2 matrices H, P s.t.
|
| 1561 |
+
$P H P^{T} = A$, where H is an upper hessenberg matrix
|
| 1562 |
+
and P is an orthogonal matrix
|
| 1563 |
+
|
| 1564 |
+
Examples
|
| 1565 |
+
========
|
| 1566 |
+
|
| 1567 |
+
>>> from sympy import Matrix
|
| 1568 |
+
>>> A = Matrix([
|
| 1569 |
+
... [1,2,3],
|
| 1570 |
+
... [-3,5,6],
|
| 1571 |
+
... [4,-8,9],
|
| 1572 |
+
... ])
|
| 1573 |
+
>>> H, P = A.upper_hessenberg_decomposition()
|
| 1574 |
+
>>> H
|
| 1575 |
+
Matrix([
|
| 1576 |
+
[1, 6/5, 17/5],
|
| 1577 |
+
[5, 213/25, -134/25],
|
| 1578 |
+
[0, 216/25, 137/25]])
|
| 1579 |
+
>>> P
|
| 1580 |
+
Matrix([
|
| 1581 |
+
[1, 0, 0],
|
| 1582 |
+
[0, -3/5, 4/5],
|
| 1583 |
+
[0, 4/5, 3/5]])
|
| 1584 |
+
>>> P * H * P.H == A
|
| 1585 |
+
True
|
| 1586 |
+
|
| 1587 |
+
|
| 1588 |
+
References
|
| 1589 |
+
==========
|
| 1590 |
+
|
| 1591 |
+
.. [#] https://mathworld.wolfram.com/HessenbergDecomposition.html
|
| 1592 |
+
"""
|
| 1593 |
+
|
| 1594 |
+
M = A.as_mutable()
|
| 1595 |
+
|
| 1596 |
+
if not M.is_square:
|
| 1597 |
+
raise NonSquareMatrixError("Matrix must be square.")
|
| 1598 |
+
|
| 1599 |
+
n = M.cols
|
| 1600 |
+
P = M.eye(n)
|
| 1601 |
+
H = M
|
| 1602 |
+
|
| 1603 |
+
for j in range(n - 2):
|
| 1604 |
+
|
| 1605 |
+
u = H[j + 1:, j]
|
| 1606 |
+
|
| 1607 |
+
if u[1:, :].is_zero_matrix:
|
| 1608 |
+
continue
|
| 1609 |
+
|
| 1610 |
+
if sign(u[0]) != 0:
|
| 1611 |
+
u[0] = u[0] + sign(u[0]) * u.norm()
|
| 1612 |
+
else:
|
| 1613 |
+
u[0] = u[0] + u.norm()
|
| 1614 |
+
|
| 1615 |
+
v = u / u.norm()
|
| 1616 |
+
|
| 1617 |
+
H[j + 1:, :] = H[j + 1:, :] - 2 * v * (v.H * H[j + 1:, :])
|
| 1618 |
+
H[:, j + 1:] = H[:, j + 1:] - (H[:, j + 1:] * (2 * v)) * v.H
|
| 1619 |
+
P[:, j + 1:] = P[:, j + 1:] - (P[:, j + 1:] * (2 * v)) * v.H
|
| 1620 |
+
|
| 1621 |
+
return H, P
|
lib/python3.12/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
|
lib/python3.12/site-packages/sympy/matrices/graph.py
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.utilities.iterables import \
|
| 2 |
+
flatten, connected_components, strongly_connected_components
|
| 3 |
+
from .exceptions import NonSquareMatrixError
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def _connected_components(M):
|
| 7 |
+
"""Returns the list of connected vertices of the graph when
|
| 8 |
+
a square matrix is viewed as a weighted graph.
|
| 9 |
+
|
| 10 |
+
Examples
|
| 11 |
+
========
|
| 12 |
+
|
| 13 |
+
>>> from sympy import Matrix
|
| 14 |
+
>>> A = Matrix([
|
| 15 |
+
... [66, 0, 0, 68, 0, 0, 0, 0, 67],
|
| 16 |
+
... [0, 55, 0, 0, 0, 0, 54, 53, 0],
|
| 17 |
+
... [0, 0, 0, 0, 1, 2, 0, 0, 0],
|
| 18 |
+
... [86, 0, 0, 88, 0, 0, 0, 0, 87],
|
| 19 |
+
... [0, 0, 10, 0, 11, 12, 0, 0, 0],
|
| 20 |
+
... [0, 0, 20, 0, 21, 22, 0, 0, 0],
|
| 21 |
+
... [0, 45, 0, 0, 0, 0, 44, 43, 0],
|
| 22 |
+
... [0, 35, 0, 0, 0, 0, 34, 33, 0],
|
| 23 |
+
... [76, 0, 0, 78, 0, 0, 0, 0, 77]])
|
| 24 |
+
>>> A.connected_components()
|
| 25 |
+
[[0, 3, 8], [1, 6, 7], [2, 4, 5]]
|
| 26 |
+
|
| 27 |
+
Notes
|
| 28 |
+
=====
|
| 29 |
+
|
| 30 |
+
Even if any symbolic elements of the matrix can be indeterminate
|
| 31 |
+
to be zero mathematically, this only takes the account of the
|
| 32 |
+
structural aspect of the matrix, so they will considered to be
|
| 33 |
+
nonzero.
|
| 34 |
+
"""
|
| 35 |
+
if not M.is_square:
|
| 36 |
+
raise NonSquareMatrixError
|
| 37 |
+
|
| 38 |
+
V = range(M.rows)
|
| 39 |
+
E = sorted(M.todok().keys())
|
| 40 |
+
return connected_components((V, E))
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def _strongly_connected_components(M):
|
| 44 |
+
"""Returns the list of strongly connected vertices of the graph when
|
| 45 |
+
a square matrix is viewed as a weighted graph.
|
| 46 |
+
|
| 47 |
+
Examples
|
| 48 |
+
========
|
| 49 |
+
|
| 50 |
+
>>> from sympy import Matrix
|
| 51 |
+
>>> A = Matrix([
|
| 52 |
+
... [44, 0, 0, 0, 43, 0, 45, 0, 0],
|
| 53 |
+
... [0, 66, 62, 61, 0, 68, 0, 60, 67],
|
| 54 |
+
... [0, 0, 22, 21, 0, 0, 0, 20, 0],
|
| 55 |
+
... [0, 0, 12, 11, 0, 0, 0, 10, 0],
|
| 56 |
+
... [34, 0, 0, 0, 33, 0, 35, 0, 0],
|
| 57 |
+
... [0, 86, 82, 81, 0, 88, 0, 80, 87],
|
| 58 |
+
... [54, 0, 0, 0, 53, 0, 55, 0, 0],
|
| 59 |
+
... [0, 0, 2, 1, 0, 0, 0, 0, 0],
|
| 60 |
+
... [0, 76, 72, 71, 0, 78, 0, 70, 77]])
|
| 61 |
+
>>> A.strongly_connected_components()
|
| 62 |
+
[[0, 4, 6], [2, 3, 7], [1, 5, 8]]
|
| 63 |
+
"""
|
| 64 |
+
if not M.is_square:
|
| 65 |
+
raise NonSquareMatrixError
|
| 66 |
+
|
| 67 |
+
# RepMatrix uses the more efficient DomainMatrix.scc() method
|
| 68 |
+
rep = getattr(M, '_rep', None)
|
| 69 |
+
if rep is not None:
|
| 70 |
+
return rep.scc()
|
| 71 |
+
|
| 72 |
+
V = range(M.rows)
|
| 73 |
+
E = sorted(M.todok().keys())
|
| 74 |
+
return strongly_connected_components((V, E))
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def _connected_components_decomposition(M):
|
| 78 |
+
"""Decomposes a square matrix into block diagonal form only
|
| 79 |
+
using the permutations.
|
| 80 |
+
|
| 81 |
+
Explanation
|
| 82 |
+
===========
|
| 83 |
+
|
| 84 |
+
The decomposition is in a form of $A = P^{-1} B P$ where $P$ is a
|
| 85 |
+
permutation matrix and $B$ is a block diagonal matrix.
|
| 86 |
+
|
| 87 |
+
Returns
|
| 88 |
+
=======
|
| 89 |
+
|
| 90 |
+
P, B : PermutationMatrix, BlockDiagMatrix
|
| 91 |
+
*P* is a permutation matrix for the similarity transform
|
| 92 |
+
as in the explanation. And *B* is the block diagonal matrix of
|
| 93 |
+
the result of the permutation.
|
| 94 |
+
|
| 95 |
+
If you would like to get the diagonal blocks from the
|
| 96 |
+
BlockDiagMatrix, see
|
| 97 |
+
:meth:`~sympy.matrices.expressions.blockmatrix.BlockDiagMatrix.get_diag_blocks`.
|
| 98 |
+
|
| 99 |
+
Examples
|
| 100 |
+
========
|
| 101 |
+
|
| 102 |
+
>>> from sympy import Matrix, pprint
|
| 103 |
+
>>> A = Matrix([
|
| 104 |
+
... [66, 0, 0, 68, 0, 0, 0, 0, 67],
|
| 105 |
+
... [0, 55, 0, 0, 0, 0, 54, 53, 0],
|
| 106 |
+
... [0, 0, 0, 0, 1, 2, 0, 0, 0],
|
| 107 |
+
... [86, 0, 0, 88, 0, 0, 0, 0, 87],
|
| 108 |
+
... [0, 0, 10, 0, 11, 12, 0, 0, 0],
|
| 109 |
+
... [0, 0, 20, 0, 21, 22, 0, 0, 0],
|
| 110 |
+
... [0, 45, 0, 0, 0, 0, 44, 43, 0],
|
| 111 |
+
... [0, 35, 0, 0, 0, 0, 34, 33, 0],
|
| 112 |
+
... [76, 0, 0, 78, 0, 0, 0, 0, 77]])
|
| 113 |
+
|
| 114 |
+
>>> P, B = A.connected_components_decomposition()
|
| 115 |
+
>>> pprint(P)
|
| 116 |
+
PermutationMatrix((1 3)(2 8 5 7 4 6))
|
| 117 |
+
>>> pprint(B)
|
| 118 |
+
[[66 68 67] ]
|
| 119 |
+
[[ ] ]
|
| 120 |
+
[[86 88 87] 0 0 ]
|
| 121 |
+
[[ ] ]
|
| 122 |
+
[[76 78 77] ]
|
| 123 |
+
[ ]
|
| 124 |
+
[ [55 54 53] ]
|
| 125 |
+
[ [ ] ]
|
| 126 |
+
[ 0 [45 44 43] 0 ]
|
| 127 |
+
[ [ ] ]
|
| 128 |
+
[ [35 34 33] ]
|
| 129 |
+
[ ]
|
| 130 |
+
[ [0 1 2 ]]
|
| 131 |
+
[ [ ]]
|
| 132 |
+
[ 0 0 [10 11 12]]
|
| 133 |
+
[ [ ]]
|
| 134 |
+
[ [20 21 22]]
|
| 135 |
+
|
| 136 |
+
>>> P = P.as_explicit()
|
| 137 |
+
>>> B = B.as_explicit()
|
| 138 |
+
>>> P.T*B*P == A
|
| 139 |
+
True
|
| 140 |
+
|
| 141 |
+
Notes
|
| 142 |
+
=====
|
| 143 |
+
|
| 144 |
+
This problem corresponds to the finding of the connected components
|
| 145 |
+
of a graph, when a matrix is viewed as a weighted graph.
|
| 146 |
+
"""
|
| 147 |
+
from sympy.combinatorics.permutations import Permutation
|
| 148 |
+
from sympy.matrices.expressions.blockmatrix import BlockDiagMatrix
|
| 149 |
+
from sympy.matrices.expressions.permutation import PermutationMatrix
|
| 150 |
+
|
| 151 |
+
iblocks = M.connected_components()
|
| 152 |
+
|
| 153 |
+
p = Permutation(flatten(iblocks))
|
| 154 |
+
P = PermutationMatrix(p)
|
| 155 |
+
|
| 156 |
+
blocks = []
|
| 157 |
+
for b in iblocks:
|
| 158 |
+
blocks.append(M[b, b])
|
| 159 |
+
B = BlockDiagMatrix(*blocks)
|
| 160 |
+
return P, B
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
def _strongly_connected_components_decomposition(M, lower=True):
|
| 164 |
+
"""Decomposes a square matrix into block triangular form only
|
| 165 |
+
using the permutations.
|
| 166 |
+
|
| 167 |
+
Explanation
|
| 168 |
+
===========
|
| 169 |
+
|
| 170 |
+
The decomposition is in a form of $A = P^{-1} B P$ where $P$ is a
|
| 171 |
+
permutation matrix and $B$ is a block diagonal matrix.
|
| 172 |
+
|
| 173 |
+
Parameters
|
| 174 |
+
==========
|
| 175 |
+
|
| 176 |
+
lower : bool
|
| 177 |
+
Makes $B$ lower block triangular when ``True``.
|
| 178 |
+
Otherwise, makes $B$ upper block triangular.
|
| 179 |
+
|
| 180 |
+
Returns
|
| 181 |
+
=======
|
| 182 |
+
|
| 183 |
+
P, B : PermutationMatrix, BlockMatrix
|
| 184 |
+
*P* is a permutation matrix for the similarity transform
|
| 185 |
+
as in the explanation. And *B* is the block triangular matrix of
|
| 186 |
+
the result of the permutation.
|
| 187 |
+
|
| 188 |
+
Examples
|
| 189 |
+
========
|
| 190 |
+
|
| 191 |
+
>>> from sympy import Matrix, pprint
|
| 192 |
+
>>> A = Matrix([
|
| 193 |
+
... [44, 0, 0, 0, 43, 0, 45, 0, 0],
|
| 194 |
+
... [0, 66, 62, 61, 0, 68, 0, 60, 67],
|
| 195 |
+
... [0, 0, 22, 21, 0, 0, 0, 20, 0],
|
| 196 |
+
... [0, 0, 12, 11, 0, 0, 0, 10, 0],
|
| 197 |
+
... [34, 0, 0, 0, 33, 0, 35, 0, 0],
|
| 198 |
+
... [0, 86, 82, 81, 0, 88, 0, 80, 87],
|
| 199 |
+
... [54, 0, 0, 0, 53, 0, 55, 0, 0],
|
| 200 |
+
... [0, 0, 2, 1, 0, 0, 0, 0, 0],
|
| 201 |
+
... [0, 76, 72, 71, 0, 78, 0, 70, 77]])
|
| 202 |
+
|
| 203 |
+
A lower block triangular decomposition:
|
| 204 |
+
|
| 205 |
+
>>> P, B = A.strongly_connected_components_decomposition()
|
| 206 |
+
>>> pprint(P)
|
| 207 |
+
PermutationMatrix((8)(1 4 3 2 6)(5 7))
|
| 208 |
+
>>> pprint(B)
|
| 209 |
+
[[44 43 45] [0 0 0] [0 0 0] ]
|
| 210 |
+
[[ ] [ ] [ ] ]
|
| 211 |
+
[[34 33 35] [0 0 0] [0 0 0] ]
|
| 212 |
+
[[ ] [ ] [ ] ]
|
| 213 |
+
[[54 53 55] [0 0 0] [0 0 0] ]
|
| 214 |
+
[ ]
|
| 215 |
+
[ [0 0 0] [22 21 20] [0 0 0] ]
|
| 216 |
+
[ [ ] [ ] [ ] ]
|
| 217 |
+
[ [0 0 0] [12 11 10] [0 0 0] ]
|
| 218 |
+
[ [ ] [ ] [ ] ]
|
| 219 |
+
[ [0 0 0] [2 1 0 ] [0 0 0] ]
|
| 220 |
+
[ ]
|
| 221 |
+
[ [0 0 0] [62 61 60] [66 68 67]]
|
| 222 |
+
[ [ ] [ ] [ ]]
|
| 223 |
+
[ [0 0 0] [82 81 80] [86 88 87]]
|
| 224 |
+
[ [ ] [ ] [ ]]
|
| 225 |
+
[ [0 0 0] [72 71 70] [76 78 77]]
|
| 226 |
+
|
| 227 |
+
>>> P = P.as_explicit()
|
| 228 |
+
>>> B = B.as_explicit()
|
| 229 |
+
>>> P.T * B * P == A
|
| 230 |
+
True
|
| 231 |
+
|
| 232 |
+
An upper block triangular decomposition:
|
| 233 |
+
|
| 234 |
+
>>> P, B = A.strongly_connected_components_decomposition(lower=False)
|
| 235 |
+
>>> pprint(P)
|
| 236 |
+
PermutationMatrix((0 1 5 7 4 3 2 8 6))
|
| 237 |
+
>>> pprint(B)
|
| 238 |
+
[[66 68 67] [62 61 60] [0 0 0] ]
|
| 239 |
+
[[ ] [ ] [ ] ]
|
| 240 |
+
[[86 88 87] [82 81 80] [0 0 0] ]
|
| 241 |
+
[[ ] [ ] [ ] ]
|
| 242 |
+
[[76 78 77] [72 71 70] [0 0 0] ]
|
| 243 |
+
[ ]
|
| 244 |
+
[ [0 0 0] [22 21 20] [0 0 0] ]
|
| 245 |
+
[ [ ] [ ] [ ] ]
|
| 246 |
+
[ [0 0 0] [12 11 10] [0 0 0] ]
|
| 247 |
+
[ [ ] [ ] [ ] ]
|
| 248 |
+
[ [0 0 0] [2 1 0 ] [0 0 0] ]
|
| 249 |
+
[ ]
|
| 250 |
+
[ [0 0 0] [0 0 0] [44 43 45]]
|
| 251 |
+
[ [ ] [ ] [ ]]
|
| 252 |
+
[ [0 0 0] [0 0 0] [34 33 35]]
|
| 253 |
+
[ [ ] [ ] [ ]]
|
| 254 |
+
[ [0 0 0] [0 0 0] [54 53 55]]
|
| 255 |
+
|
| 256 |
+
>>> P = P.as_explicit()
|
| 257 |
+
>>> B = B.as_explicit()
|
| 258 |
+
>>> P.T * B * P == A
|
| 259 |
+
True
|
| 260 |
+
"""
|
| 261 |
+
from sympy.combinatorics.permutations import Permutation
|
| 262 |
+
from sympy.matrices.expressions.blockmatrix import BlockMatrix
|
| 263 |
+
from sympy.matrices.expressions.permutation import PermutationMatrix
|
| 264 |
+
|
| 265 |
+
iblocks = M.strongly_connected_components()
|
| 266 |
+
if not lower:
|
| 267 |
+
iblocks = list(reversed(iblocks))
|
| 268 |
+
|
| 269 |
+
p = Permutation(flatten(iblocks))
|
| 270 |
+
P = PermutationMatrix(p)
|
| 271 |
+
|
| 272 |
+
rows = []
|
| 273 |
+
for a in iblocks:
|
| 274 |
+
cols = []
|
| 275 |
+
for b in iblocks:
|
| 276 |
+
cols.append(M[a, b])
|
| 277 |
+
rows.append(cols)
|
| 278 |
+
B = BlockMatrix(rows)
|
| 279 |
+
return P, B
|
lib/python3.12/site-packages/sympy/matrices/immutable.py
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from mpmath.matrices.matrices import _matrix
|
| 2 |
+
|
| 3 |
+
from sympy.core import Basic, Dict, Tuple
|
| 4 |
+
from sympy.core.numbers import Integer
|
| 5 |
+
from sympy.core.cache import cacheit
|
| 6 |
+
from sympy.core.sympify import _sympy_converter as sympify_converter, _sympify
|
| 7 |
+
from sympy.matrices.dense import DenseMatrix
|
| 8 |
+
from sympy.matrices.expressions import MatrixExpr
|
| 9 |
+
from sympy.matrices.matrixbase import MatrixBase
|
| 10 |
+
from sympy.matrices.repmatrix import RepMatrix
|
| 11 |
+
from sympy.matrices.sparse import SparseRepMatrix
|
| 12 |
+
from sympy.multipledispatch import dispatch
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def sympify_matrix(arg):
|
| 16 |
+
return arg.as_immutable()
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
sympify_converter[MatrixBase] = sympify_matrix
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def sympify_mpmath_matrix(arg):
|
| 23 |
+
mat = [_sympify(x) for x in arg]
|
| 24 |
+
return ImmutableDenseMatrix(arg.rows, arg.cols, mat)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
sympify_converter[_matrix] = sympify_mpmath_matrix
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
class ImmutableRepMatrix(RepMatrix, MatrixExpr): # type: ignore
|
| 31 |
+
"""Immutable matrix based on RepMatrix
|
| 32 |
+
|
| 33 |
+
Uses DomainMAtrix as the internal representation.
|
| 34 |
+
"""
|
| 35 |
+
|
| 36 |
+
#
|
| 37 |
+
# This is a subclass of RepMatrix that adds/overrides some methods to make
|
| 38 |
+
# the instances Basic and immutable. ImmutableRepMatrix is a superclass for
|
| 39 |
+
# both ImmutableDenseMatrix and ImmutableSparseMatrix.
|
| 40 |
+
#
|
| 41 |
+
|
| 42 |
+
def __new__(cls, *args, **kwargs):
|
| 43 |
+
return cls._new(*args, **kwargs)
|
| 44 |
+
|
| 45 |
+
__hash__ = MatrixExpr.__hash__
|
| 46 |
+
|
| 47 |
+
def copy(self):
|
| 48 |
+
return self
|
| 49 |
+
|
| 50 |
+
@property
|
| 51 |
+
def cols(self):
|
| 52 |
+
return self._cols
|
| 53 |
+
|
| 54 |
+
@property
|
| 55 |
+
def rows(self):
|
| 56 |
+
return self._rows
|
| 57 |
+
|
| 58 |
+
@property
|
| 59 |
+
def shape(self):
|
| 60 |
+
return self._rows, self._cols
|
| 61 |
+
|
| 62 |
+
def as_immutable(self):
|
| 63 |
+
return self
|
| 64 |
+
|
| 65 |
+
def _entry(self, i, j, **kwargs):
|
| 66 |
+
return self[i, j]
|
| 67 |
+
|
| 68 |
+
def __setitem__(self, *args):
|
| 69 |
+
raise TypeError("Cannot set values of {}".format(self.__class__))
|
| 70 |
+
|
| 71 |
+
def is_diagonalizable(self, reals_only=False, **kwargs):
|
| 72 |
+
return super().is_diagonalizable(
|
| 73 |
+
reals_only=reals_only, **kwargs)
|
| 74 |
+
|
| 75 |
+
is_diagonalizable.__doc__ = SparseRepMatrix.is_diagonalizable.__doc__
|
| 76 |
+
is_diagonalizable = cacheit(is_diagonalizable)
|
| 77 |
+
|
| 78 |
+
def analytic_func(self, f, x):
|
| 79 |
+
return self.as_mutable().analytic_func(f, x).as_immutable()
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
class ImmutableDenseMatrix(DenseMatrix, ImmutableRepMatrix): # type: ignore
|
| 83 |
+
"""Create an immutable version of a matrix.
|
| 84 |
+
|
| 85 |
+
Examples
|
| 86 |
+
========
|
| 87 |
+
|
| 88 |
+
>>> from sympy import eye, ImmutableMatrix
|
| 89 |
+
>>> ImmutableMatrix(eye(3))
|
| 90 |
+
Matrix([
|
| 91 |
+
[1, 0, 0],
|
| 92 |
+
[0, 1, 0],
|
| 93 |
+
[0, 0, 1]])
|
| 94 |
+
>>> _[0, 0] = 42
|
| 95 |
+
Traceback (most recent call last):
|
| 96 |
+
...
|
| 97 |
+
TypeError: Cannot set values of ImmutableDenseMatrix
|
| 98 |
+
"""
|
| 99 |
+
|
| 100 |
+
# MatrixExpr is set as NotIterable, but we want explicit matrices to be
|
| 101 |
+
# iterable
|
| 102 |
+
_iterable = True
|
| 103 |
+
_class_priority = 8
|
| 104 |
+
_op_priority = 10.001
|
| 105 |
+
|
| 106 |
+
@classmethod
|
| 107 |
+
def _new(cls, *args, **kwargs):
|
| 108 |
+
if len(args) == 1 and isinstance(args[0], ImmutableDenseMatrix):
|
| 109 |
+
return args[0]
|
| 110 |
+
if kwargs.get('copy', True) is False:
|
| 111 |
+
if len(args) != 3:
|
| 112 |
+
raise TypeError("'copy=False' requires a matrix be initialized as rows,cols,[list]")
|
| 113 |
+
rows, cols, flat_list = args
|
| 114 |
+
else:
|
| 115 |
+
rows, cols, flat_list = cls._handle_creation_inputs(*args, **kwargs)
|
| 116 |
+
flat_list = list(flat_list) # create a shallow copy
|
| 117 |
+
|
| 118 |
+
rep = cls._flat_list_to_DomainMatrix(rows, cols, flat_list)
|
| 119 |
+
|
| 120 |
+
return cls._fromrep(rep)
|
| 121 |
+
|
| 122 |
+
@classmethod
|
| 123 |
+
def _fromrep(cls, rep):
|
| 124 |
+
rows, cols = rep.shape
|
| 125 |
+
flat_list = rep.to_sympy().to_list_flat()
|
| 126 |
+
obj = Basic.__new__(cls,
|
| 127 |
+
Integer(rows),
|
| 128 |
+
Integer(cols),
|
| 129 |
+
Tuple(*flat_list, sympify=False))
|
| 130 |
+
obj._rows = rows
|
| 131 |
+
obj._cols = cols
|
| 132 |
+
obj._rep = rep
|
| 133 |
+
return obj
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
# make sure ImmutableDenseMatrix is aliased as ImmutableMatrix
|
| 137 |
+
ImmutableMatrix = ImmutableDenseMatrix
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
class ImmutableSparseMatrix(SparseRepMatrix, ImmutableRepMatrix): # type:ignore
|
| 141 |
+
"""Create an immutable version of a sparse matrix.
|
| 142 |
+
|
| 143 |
+
Examples
|
| 144 |
+
========
|
| 145 |
+
|
| 146 |
+
>>> from sympy import eye, ImmutableSparseMatrix
|
| 147 |
+
>>> ImmutableSparseMatrix(1, 1, {})
|
| 148 |
+
Matrix([[0]])
|
| 149 |
+
>>> ImmutableSparseMatrix(eye(3))
|
| 150 |
+
Matrix([
|
| 151 |
+
[1, 0, 0],
|
| 152 |
+
[0, 1, 0],
|
| 153 |
+
[0, 0, 1]])
|
| 154 |
+
>>> _[0, 0] = 42
|
| 155 |
+
Traceback (most recent call last):
|
| 156 |
+
...
|
| 157 |
+
TypeError: Cannot set values of ImmutableSparseMatrix
|
| 158 |
+
>>> _.shape
|
| 159 |
+
(3, 3)
|
| 160 |
+
"""
|
| 161 |
+
is_Matrix = True
|
| 162 |
+
_class_priority = 9
|
| 163 |
+
|
| 164 |
+
@classmethod
|
| 165 |
+
def _new(cls, *args, **kwargs):
|
| 166 |
+
rows, cols, smat = cls._handle_creation_inputs(*args, **kwargs)
|
| 167 |
+
|
| 168 |
+
rep = cls._smat_to_DomainMatrix(rows, cols, smat)
|
| 169 |
+
|
| 170 |
+
return cls._fromrep(rep)
|
| 171 |
+
|
| 172 |
+
@classmethod
|
| 173 |
+
def _fromrep(cls, rep):
|
| 174 |
+
rows, cols = rep.shape
|
| 175 |
+
smat = rep.to_sympy().to_dok()
|
| 176 |
+
obj = Basic.__new__(cls, Integer(rows), Integer(cols), Dict(smat))
|
| 177 |
+
obj._rows = rows
|
| 178 |
+
obj._cols = cols
|
| 179 |
+
obj._rep = rep
|
| 180 |
+
return obj
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
@dispatch(ImmutableDenseMatrix, ImmutableDenseMatrix)
|
| 184 |
+
def _eval_is_eq(lhs, rhs): # noqa:F811
|
| 185 |
+
"""Helper method for Equality with matrices.sympy.
|
| 186 |
+
|
| 187 |
+
Relational automatically converts matrices to ImmutableDenseMatrix
|
| 188 |
+
instances, so this method only applies here. Returns True if the
|
| 189 |
+
matrices are definitively the same, False if they are definitively
|
| 190 |
+
different, and None if undetermined (e.g. if they contain Symbols).
|
| 191 |
+
Returning None triggers default handling of Equalities.
|
| 192 |
+
|
| 193 |
+
"""
|
| 194 |
+
if lhs.shape != rhs.shape:
|
| 195 |
+
return False
|
| 196 |
+
return (lhs - rhs).is_zero_matrix
|
lib/python3.12/site-packages/sympy/matrices/matrices.py
ADDED
|
@@ -0,0 +1,687 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#
|
| 2 |
+
# A module consisting of deprecated matrix classes. New code should not be
|
| 3 |
+
# added here.
|
| 4 |
+
#
|
| 5 |
+
from sympy.core.basic import Basic
|
| 6 |
+
from sympy.core.symbol import Dummy
|
| 7 |
+
|
| 8 |
+
from .common import MatrixCommon
|
| 9 |
+
|
| 10 |
+
from .exceptions import NonSquareMatrixError
|
| 11 |
+
|
| 12 |
+
from .utilities import _iszero, _is_zero_after_expand_mul, _simplify
|
| 13 |
+
|
| 14 |
+
from .determinant import (
|
| 15 |
+
_find_reasonable_pivot, _find_reasonable_pivot_naive,
|
| 16 |
+
_adjugate, _charpoly, _cofactor, _cofactor_matrix, _per,
|
| 17 |
+
_det, _det_bareiss, _det_berkowitz, _det_bird, _det_laplace, _det_LU,
|
| 18 |
+
_minor, _minor_submatrix)
|
| 19 |
+
|
| 20 |
+
from .reductions import _is_echelon, _echelon_form, _rank, _rref
|
| 21 |
+
from .subspaces import _columnspace, _nullspace, _rowspace, _orthogonalize
|
| 22 |
+
|
| 23 |
+
from .eigen import (
|
| 24 |
+
_eigenvals, _eigenvects,
|
| 25 |
+
_bidiagonalize, _bidiagonal_decomposition,
|
| 26 |
+
_is_diagonalizable, _diagonalize,
|
| 27 |
+
_is_positive_definite, _is_positive_semidefinite,
|
| 28 |
+
_is_negative_definite, _is_negative_semidefinite, _is_indefinite,
|
| 29 |
+
_jordan_form, _left_eigenvects, _singular_values)
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
# This class was previously defined in this module, but was moved to
|
| 33 |
+
# sympy.matrices.matrixbase. We import it here for backwards compatibility in
|
| 34 |
+
# case someone was importing it from here.
|
| 35 |
+
from .matrixbase import MatrixBase
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
__doctest_requires__ = {
|
| 39 |
+
('MatrixEigen.is_indefinite',
|
| 40 |
+
'MatrixEigen.is_negative_definite',
|
| 41 |
+
'MatrixEigen.is_negative_semidefinite',
|
| 42 |
+
'MatrixEigen.is_positive_definite',
|
| 43 |
+
'MatrixEigen.is_positive_semidefinite'): ['matplotlib'],
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
class MatrixDeterminant(MatrixCommon):
|
| 48 |
+
"""Provides basic matrix determinant operations. Should not be instantiated
|
| 49 |
+
directly. See ``determinant.py`` for their implementations."""
|
| 50 |
+
|
| 51 |
+
def _eval_det_bareiss(self, iszerofunc=_is_zero_after_expand_mul):
|
| 52 |
+
return _det_bareiss(self, iszerofunc=iszerofunc)
|
| 53 |
+
|
| 54 |
+
def _eval_det_berkowitz(self):
|
| 55 |
+
return _det_berkowitz(self)
|
| 56 |
+
|
| 57 |
+
def _eval_det_lu(self, iszerofunc=_iszero, simpfunc=None):
|
| 58 |
+
return _det_LU(self, iszerofunc=iszerofunc, simpfunc=simpfunc)
|
| 59 |
+
|
| 60 |
+
def _eval_det_bird(self):
|
| 61 |
+
return _det_bird(self)
|
| 62 |
+
|
| 63 |
+
def _eval_det_laplace(self):
|
| 64 |
+
return _det_laplace(self)
|
| 65 |
+
|
| 66 |
+
def _eval_determinant(self): # for expressions.determinant.Determinant
|
| 67 |
+
return _det(self)
|
| 68 |
+
|
| 69 |
+
def adjugate(self, method="berkowitz"):
|
| 70 |
+
return _adjugate(self, method=method)
|
| 71 |
+
|
| 72 |
+
def charpoly(self, x='lambda', simplify=_simplify):
|
| 73 |
+
return _charpoly(self, x=x, simplify=simplify)
|
| 74 |
+
|
| 75 |
+
def cofactor(self, i, j, method="berkowitz"):
|
| 76 |
+
return _cofactor(self, i, j, method=method)
|
| 77 |
+
|
| 78 |
+
def cofactor_matrix(self, method="berkowitz"):
|
| 79 |
+
return _cofactor_matrix(self, method=method)
|
| 80 |
+
|
| 81 |
+
def det(self, method="bareiss", iszerofunc=None):
|
| 82 |
+
return _det(self, method=method, iszerofunc=iszerofunc)
|
| 83 |
+
|
| 84 |
+
def per(self):
|
| 85 |
+
return _per(self)
|
| 86 |
+
|
| 87 |
+
def minor(self, i, j, method="berkowitz"):
|
| 88 |
+
return _minor(self, i, j, method=method)
|
| 89 |
+
|
| 90 |
+
def minor_submatrix(self, i, j):
|
| 91 |
+
return _minor_submatrix(self, i, j)
|
| 92 |
+
|
| 93 |
+
_find_reasonable_pivot.__doc__ = _find_reasonable_pivot.__doc__
|
| 94 |
+
_find_reasonable_pivot_naive.__doc__ = _find_reasonable_pivot_naive.__doc__
|
| 95 |
+
_eval_det_bareiss.__doc__ = _det_bareiss.__doc__
|
| 96 |
+
_eval_det_berkowitz.__doc__ = _det_berkowitz.__doc__
|
| 97 |
+
_eval_det_bird.__doc__ = _det_bird.__doc__
|
| 98 |
+
_eval_det_laplace.__doc__ = _det_laplace.__doc__
|
| 99 |
+
_eval_det_lu.__doc__ = _det_LU.__doc__
|
| 100 |
+
_eval_determinant.__doc__ = _det.__doc__
|
| 101 |
+
adjugate.__doc__ = _adjugate.__doc__
|
| 102 |
+
charpoly.__doc__ = _charpoly.__doc__
|
| 103 |
+
cofactor.__doc__ = _cofactor.__doc__
|
| 104 |
+
cofactor_matrix.__doc__ = _cofactor_matrix.__doc__
|
| 105 |
+
det.__doc__ = _det.__doc__
|
| 106 |
+
per.__doc__ = _per.__doc__
|
| 107 |
+
minor.__doc__ = _minor.__doc__
|
| 108 |
+
minor_submatrix.__doc__ = _minor_submatrix.__doc__
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
class MatrixReductions(MatrixDeterminant):
|
| 112 |
+
"""Provides basic matrix row/column operations. Should not be instantiated
|
| 113 |
+
directly. See ``reductions.py`` for some of their implementations."""
|
| 114 |
+
|
| 115 |
+
def echelon_form(self, iszerofunc=_iszero, simplify=False, with_pivots=False):
|
| 116 |
+
return _echelon_form(self, iszerofunc=iszerofunc, simplify=simplify,
|
| 117 |
+
with_pivots=with_pivots)
|
| 118 |
+
|
| 119 |
+
@property
|
| 120 |
+
def is_echelon(self):
|
| 121 |
+
return _is_echelon(self)
|
| 122 |
+
|
| 123 |
+
def rank(self, iszerofunc=_iszero, simplify=False):
|
| 124 |
+
return _rank(self, iszerofunc=iszerofunc, simplify=simplify)
|
| 125 |
+
|
| 126 |
+
def rref_rhs(self, rhs):
|
| 127 |
+
"""Return reduced row-echelon form of matrix, matrix showing
|
| 128 |
+
rhs after reduction steps. ``rhs`` must have the same number
|
| 129 |
+
of rows as ``self``.
|
| 130 |
+
|
| 131 |
+
Examples
|
| 132 |
+
========
|
| 133 |
+
|
| 134 |
+
>>> from sympy import Matrix, symbols
|
| 135 |
+
>>> r1, r2 = symbols('r1 r2')
|
| 136 |
+
>>> Matrix([[1, 1], [2, 1]]).rref_rhs(Matrix([r1, r2]))
|
| 137 |
+
(Matrix([
|
| 138 |
+
[1, 0],
|
| 139 |
+
[0, 1]]), Matrix([
|
| 140 |
+
[ -r1 + r2],
|
| 141 |
+
[2*r1 - r2]]))
|
| 142 |
+
"""
|
| 143 |
+
r, _ = _rref(self.hstack(self, self.eye(self.rows), rhs))
|
| 144 |
+
return r[:, :self.cols], r[:, -rhs.cols:]
|
| 145 |
+
|
| 146 |
+
def rref(self, iszerofunc=_iszero, simplify=False, pivots=True,
|
| 147 |
+
normalize_last=True):
|
| 148 |
+
return _rref(self, iszerofunc=iszerofunc, simplify=simplify,
|
| 149 |
+
pivots=pivots, normalize_last=normalize_last)
|
| 150 |
+
|
| 151 |
+
echelon_form.__doc__ = _echelon_form.__doc__
|
| 152 |
+
is_echelon.__doc__ = _is_echelon.__doc__
|
| 153 |
+
rank.__doc__ = _rank.__doc__
|
| 154 |
+
rref.__doc__ = _rref.__doc__
|
| 155 |
+
|
| 156 |
+
def _normalize_op_args(self, op, col, k, col1, col2, error_str="col"):
|
| 157 |
+
"""Validate the arguments for a row/column operation. ``error_str``
|
| 158 |
+
can be one of "row" or "col" depending on the arguments being parsed."""
|
| 159 |
+
if op not in ["n->kn", "n<->m", "n->n+km"]:
|
| 160 |
+
raise ValueError("Unknown {} operation '{}'. Valid col operations "
|
| 161 |
+
"are 'n->kn', 'n<->m', 'n->n+km'".format(error_str, op))
|
| 162 |
+
|
| 163 |
+
# define self_col according to error_str
|
| 164 |
+
self_cols = self.cols if error_str == 'col' else self.rows
|
| 165 |
+
|
| 166 |
+
# normalize and validate the arguments
|
| 167 |
+
if op == "n->kn":
|
| 168 |
+
col = col if col is not None else col1
|
| 169 |
+
if col is None or k is None:
|
| 170 |
+
raise ValueError("For a {0} operation 'n->kn' you must provide the "
|
| 171 |
+
"kwargs `{0}` and `k`".format(error_str))
|
| 172 |
+
if not 0 <= col < self_cols:
|
| 173 |
+
raise ValueError("This matrix does not have a {} '{}'".format(error_str, col))
|
| 174 |
+
|
| 175 |
+
elif op == "n<->m":
|
| 176 |
+
# we need two cols to swap. It does not matter
|
| 177 |
+
# how they were specified, so gather them together and
|
| 178 |
+
# remove `None`
|
| 179 |
+
cols = {col, k, col1, col2}.difference([None])
|
| 180 |
+
if len(cols) > 2:
|
| 181 |
+
# maybe the user left `k` by mistake?
|
| 182 |
+
cols = {col, col1, col2}.difference([None])
|
| 183 |
+
if len(cols) != 2:
|
| 184 |
+
raise ValueError("For a {0} operation 'n<->m' you must provide the "
|
| 185 |
+
"kwargs `{0}1` and `{0}2`".format(error_str))
|
| 186 |
+
col1, col2 = cols
|
| 187 |
+
if not 0 <= col1 < self_cols:
|
| 188 |
+
raise ValueError("This matrix does not have a {} '{}'".format(error_str, col1))
|
| 189 |
+
if not 0 <= col2 < self_cols:
|
| 190 |
+
raise ValueError("This matrix does not have a {} '{}'".format(error_str, col2))
|
| 191 |
+
|
| 192 |
+
elif op == "n->n+km":
|
| 193 |
+
col = col1 if col is None else col
|
| 194 |
+
col2 = col1 if col2 is None else col2
|
| 195 |
+
if col is None or col2 is None or k is None:
|
| 196 |
+
raise ValueError("For a {0} operation 'n->n+km' you must provide the "
|
| 197 |
+
"kwargs `{0}`, `k`, and `{0}2`".format(error_str))
|
| 198 |
+
if col == col2:
|
| 199 |
+
raise ValueError("For a {0} operation 'n->n+km' `{0}` and `{0}2` must "
|
| 200 |
+
"be different.".format(error_str))
|
| 201 |
+
if not 0 <= col < self_cols:
|
| 202 |
+
raise ValueError("This matrix does not have a {} '{}'".format(error_str, col))
|
| 203 |
+
if not 0 <= col2 < self_cols:
|
| 204 |
+
raise ValueError("This matrix does not have a {} '{}'".format(error_str, col2))
|
| 205 |
+
|
| 206 |
+
else:
|
| 207 |
+
raise ValueError('invalid operation %s' % repr(op))
|
| 208 |
+
|
| 209 |
+
return op, col, k, col1, col2
|
| 210 |
+
|
| 211 |
+
def _eval_col_op_multiply_col_by_const(self, col, k):
|
| 212 |
+
def entry(i, j):
|
| 213 |
+
if j == col:
|
| 214 |
+
return k * self[i, j]
|
| 215 |
+
return self[i, j]
|
| 216 |
+
return self._new(self.rows, self.cols, entry)
|
| 217 |
+
|
| 218 |
+
def _eval_col_op_swap(self, col1, col2):
|
| 219 |
+
def entry(i, j):
|
| 220 |
+
if j == col1:
|
| 221 |
+
return self[i, col2]
|
| 222 |
+
elif j == col2:
|
| 223 |
+
return self[i, col1]
|
| 224 |
+
return self[i, j]
|
| 225 |
+
return self._new(self.rows, self.cols, entry)
|
| 226 |
+
|
| 227 |
+
def _eval_col_op_add_multiple_to_other_col(self, col, k, col2):
|
| 228 |
+
def entry(i, j):
|
| 229 |
+
if j == col:
|
| 230 |
+
return self[i, j] + k * self[i, col2]
|
| 231 |
+
return self[i, j]
|
| 232 |
+
return self._new(self.rows, self.cols, entry)
|
| 233 |
+
|
| 234 |
+
def _eval_row_op_swap(self, row1, row2):
|
| 235 |
+
def entry(i, j):
|
| 236 |
+
if i == row1:
|
| 237 |
+
return self[row2, j]
|
| 238 |
+
elif i == row2:
|
| 239 |
+
return self[row1, j]
|
| 240 |
+
return self[i, j]
|
| 241 |
+
return self._new(self.rows, self.cols, entry)
|
| 242 |
+
|
| 243 |
+
def _eval_row_op_multiply_row_by_const(self, row, k):
|
| 244 |
+
def entry(i, j):
|
| 245 |
+
if i == row:
|
| 246 |
+
return k * self[i, j]
|
| 247 |
+
return self[i, j]
|
| 248 |
+
return self._new(self.rows, self.cols, entry)
|
| 249 |
+
|
| 250 |
+
def _eval_row_op_add_multiple_to_other_row(self, row, k, row2):
|
| 251 |
+
def entry(i, j):
|
| 252 |
+
if i == row:
|
| 253 |
+
return self[i, j] + k * self[row2, j]
|
| 254 |
+
return self[i, j]
|
| 255 |
+
return self._new(self.rows, self.cols, entry)
|
| 256 |
+
|
| 257 |
+
def elementary_col_op(self, op="n->kn", col=None, k=None, col1=None, col2=None):
|
| 258 |
+
"""Performs the elementary column operation `op`.
|
| 259 |
+
|
| 260 |
+
`op` may be one of
|
| 261 |
+
|
| 262 |
+
* ``"n->kn"`` (column n goes to k*n)
|
| 263 |
+
* ``"n<->m"`` (swap column n and column m)
|
| 264 |
+
* ``"n->n+km"`` (column n goes to column n + k*column m)
|
| 265 |
+
|
| 266 |
+
Parameters
|
| 267 |
+
==========
|
| 268 |
+
|
| 269 |
+
op : string; the elementary row operation
|
| 270 |
+
col : the column to apply the column operation
|
| 271 |
+
k : the multiple to apply in the column operation
|
| 272 |
+
col1 : one column of a column swap
|
| 273 |
+
col2 : second column of a column swap or column "m" in the column operation
|
| 274 |
+
"n->n+km"
|
| 275 |
+
"""
|
| 276 |
+
|
| 277 |
+
op, col, k, col1, col2 = self._normalize_op_args(op, col, k, col1, col2, "col")
|
| 278 |
+
|
| 279 |
+
# now that we've validated, we're all good to dispatch
|
| 280 |
+
if op == "n->kn":
|
| 281 |
+
return self._eval_col_op_multiply_col_by_const(col, k)
|
| 282 |
+
if op == "n<->m":
|
| 283 |
+
return self._eval_col_op_swap(col1, col2)
|
| 284 |
+
if op == "n->n+km":
|
| 285 |
+
return self._eval_col_op_add_multiple_to_other_col(col, k, col2)
|
| 286 |
+
|
| 287 |
+
def elementary_row_op(self, op="n->kn", row=None, k=None, row1=None, row2=None):
|
| 288 |
+
"""Performs the elementary row operation `op`.
|
| 289 |
+
|
| 290 |
+
`op` may be one of
|
| 291 |
+
|
| 292 |
+
* ``"n->kn"`` (row n goes to k*n)
|
| 293 |
+
* ``"n<->m"`` (swap row n and row m)
|
| 294 |
+
* ``"n->n+km"`` (row n goes to row n + k*row m)
|
| 295 |
+
|
| 296 |
+
Parameters
|
| 297 |
+
==========
|
| 298 |
+
|
| 299 |
+
op : string; the elementary row operation
|
| 300 |
+
row : the row to apply the row operation
|
| 301 |
+
k : the multiple to apply in the row operation
|
| 302 |
+
row1 : one row of a row swap
|
| 303 |
+
row2 : second row of a row swap or row "m" in the row operation
|
| 304 |
+
"n->n+km"
|
| 305 |
+
"""
|
| 306 |
+
|
| 307 |
+
op, row, k, row1, row2 = self._normalize_op_args(op, row, k, row1, row2, "row")
|
| 308 |
+
|
| 309 |
+
# now that we've validated, we're all good to dispatch
|
| 310 |
+
if op == "n->kn":
|
| 311 |
+
return self._eval_row_op_multiply_row_by_const(row, k)
|
| 312 |
+
if op == "n<->m":
|
| 313 |
+
return self._eval_row_op_swap(row1, row2)
|
| 314 |
+
if op == "n->n+km":
|
| 315 |
+
return self._eval_row_op_add_multiple_to_other_row(row, k, row2)
|
| 316 |
+
|
| 317 |
+
|
| 318 |
+
class MatrixSubspaces(MatrixReductions):
|
| 319 |
+
"""Provides methods relating to the fundamental subspaces of a matrix.
|
| 320 |
+
Should not be instantiated directly. See ``subspaces.py`` for their
|
| 321 |
+
implementations."""
|
| 322 |
+
|
| 323 |
+
def columnspace(self, simplify=False):
|
| 324 |
+
return _columnspace(self, simplify=simplify)
|
| 325 |
+
|
| 326 |
+
def nullspace(self, simplify=False, iszerofunc=_iszero):
|
| 327 |
+
return _nullspace(self, simplify=simplify, iszerofunc=iszerofunc)
|
| 328 |
+
|
| 329 |
+
def rowspace(self, simplify=False):
|
| 330 |
+
return _rowspace(self, simplify=simplify)
|
| 331 |
+
|
| 332 |
+
# This is a classmethod but is converted to such later in order to allow
|
| 333 |
+
# assignment of __doc__ since that does not work for already wrapped
|
| 334 |
+
# classmethods in Python 3.6.
|
| 335 |
+
def orthogonalize(cls, *vecs, **kwargs):
|
| 336 |
+
return _orthogonalize(cls, *vecs, **kwargs)
|
| 337 |
+
|
| 338 |
+
columnspace.__doc__ = _columnspace.__doc__
|
| 339 |
+
nullspace.__doc__ = _nullspace.__doc__
|
| 340 |
+
rowspace.__doc__ = _rowspace.__doc__
|
| 341 |
+
orthogonalize.__doc__ = _orthogonalize.__doc__
|
| 342 |
+
|
| 343 |
+
orthogonalize = classmethod(orthogonalize) # type:ignore
|
| 344 |
+
|
| 345 |
+
|
| 346 |
+
class MatrixEigen(MatrixSubspaces):
|
| 347 |
+
"""Provides basic matrix eigenvalue/vector operations.
|
| 348 |
+
Should not be instantiated directly. See ``eigen.py`` for their
|
| 349 |
+
implementations."""
|
| 350 |
+
|
| 351 |
+
def eigenvals(self, error_when_incomplete=True, **flags):
|
| 352 |
+
return _eigenvals(self, error_when_incomplete=error_when_incomplete, **flags)
|
| 353 |
+
|
| 354 |
+
def eigenvects(self, error_when_incomplete=True, iszerofunc=_iszero, **flags):
|
| 355 |
+
return _eigenvects(self, error_when_incomplete=error_when_incomplete,
|
| 356 |
+
iszerofunc=iszerofunc, **flags)
|
| 357 |
+
|
| 358 |
+
def is_diagonalizable(self, reals_only=False, **kwargs):
|
| 359 |
+
return _is_diagonalizable(self, reals_only=reals_only, **kwargs)
|
| 360 |
+
|
| 361 |
+
def diagonalize(self, reals_only=False, sort=False, normalize=False):
|
| 362 |
+
return _diagonalize(self, reals_only=reals_only, sort=sort,
|
| 363 |
+
normalize=normalize)
|
| 364 |
+
|
| 365 |
+
def bidiagonalize(self, upper=True):
|
| 366 |
+
return _bidiagonalize(self, upper=upper)
|
| 367 |
+
|
| 368 |
+
def bidiagonal_decomposition(self, upper=True):
|
| 369 |
+
return _bidiagonal_decomposition(self, upper=upper)
|
| 370 |
+
|
| 371 |
+
@property
|
| 372 |
+
def is_positive_definite(self):
|
| 373 |
+
return _is_positive_definite(self)
|
| 374 |
+
|
| 375 |
+
@property
|
| 376 |
+
def is_positive_semidefinite(self):
|
| 377 |
+
return _is_positive_semidefinite(self)
|
| 378 |
+
|
| 379 |
+
@property
|
| 380 |
+
def is_negative_definite(self):
|
| 381 |
+
return _is_negative_definite(self)
|
| 382 |
+
|
| 383 |
+
@property
|
| 384 |
+
def is_negative_semidefinite(self):
|
| 385 |
+
return _is_negative_semidefinite(self)
|
| 386 |
+
|
| 387 |
+
@property
|
| 388 |
+
def is_indefinite(self):
|
| 389 |
+
return _is_indefinite(self)
|
| 390 |
+
|
| 391 |
+
def jordan_form(self, calc_transform=True, **kwargs):
|
| 392 |
+
return _jordan_form(self, calc_transform=calc_transform, **kwargs)
|
| 393 |
+
|
| 394 |
+
def left_eigenvects(self, **flags):
|
| 395 |
+
return _left_eigenvects(self, **flags)
|
| 396 |
+
|
| 397 |
+
def singular_values(self):
|
| 398 |
+
return _singular_values(self)
|
| 399 |
+
|
| 400 |
+
eigenvals.__doc__ = _eigenvals.__doc__
|
| 401 |
+
eigenvects.__doc__ = _eigenvects.__doc__
|
| 402 |
+
is_diagonalizable.__doc__ = _is_diagonalizable.__doc__
|
| 403 |
+
diagonalize.__doc__ = _diagonalize.__doc__
|
| 404 |
+
is_positive_definite.__doc__ = _is_positive_definite.__doc__
|
| 405 |
+
is_positive_semidefinite.__doc__ = _is_positive_semidefinite.__doc__
|
| 406 |
+
is_negative_definite.__doc__ = _is_negative_definite.__doc__
|
| 407 |
+
is_negative_semidefinite.__doc__ = _is_negative_semidefinite.__doc__
|
| 408 |
+
is_indefinite.__doc__ = _is_indefinite.__doc__
|
| 409 |
+
jordan_form.__doc__ = _jordan_form.__doc__
|
| 410 |
+
left_eigenvects.__doc__ = _left_eigenvects.__doc__
|
| 411 |
+
singular_values.__doc__ = _singular_values.__doc__
|
| 412 |
+
bidiagonalize.__doc__ = _bidiagonalize.__doc__
|
| 413 |
+
bidiagonal_decomposition.__doc__ = _bidiagonal_decomposition.__doc__
|
| 414 |
+
|
| 415 |
+
|
| 416 |
+
class MatrixCalculus(MatrixCommon):
|
| 417 |
+
"""Provides calculus-related matrix operations."""
|
| 418 |
+
|
| 419 |
+
def diff(self, *args, evaluate=True, **kwargs):
|
| 420 |
+
"""Calculate the derivative of each element in the matrix.
|
| 421 |
+
|
| 422 |
+
Examples
|
| 423 |
+
========
|
| 424 |
+
|
| 425 |
+
>>> from sympy import Matrix
|
| 426 |
+
>>> from sympy.abc import x, y
|
| 427 |
+
>>> M = Matrix([[x, y], [1, 0]])
|
| 428 |
+
>>> M.diff(x)
|
| 429 |
+
Matrix([
|
| 430 |
+
[1, 0],
|
| 431 |
+
[0, 0]])
|
| 432 |
+
|
| 433 |
+
See Also
|
| 434 |
+
========
|
| 435 |
+
|
| 436 |
+
integrate
|
| 437 |
+
limit
|
| 438 |
+
"""
|
| 439 |
+
# XXX this should be handled here rather than in Derivative
|
| 440 |
+
from sympy.tensor.array.array_derivatives import ArrayDerivative
|
| 441 |
+
deriv = ArrayDerivative(self, *args, evaluate=evaluate)
|
| 442 |
+
# XXX This can rather changed to always return immutable matrix
|
| 443 |
+
if not isinstance(self, Basic) and evaluate:
|
| 444 |
+
return deriv.as_mutable()
|
| 445 |
+
return deriv
|
| 446 |
+
|
| 447 |
+
def _eval_derivative(self, arg):
|
| 448 |
+
return self.applyfunc(lambda x: x.diff(arg))
|
| 449 |
+
|
| 450 |
+
def integrate(self, *args, **kwargs):
|
| 451 |
+
"""Integrate each element of the matrix. ``args`` will
|
| 452 |
+
be passed to the ``integrate`` function.
|
| 453 |
+
|
| 454 |
+
Examples
|
| 455 |
+
========
|
| 456 |
+
|
| 457 |
+
>>> from sympy import Matrix
|
| 458 |
+
>>> from sympy.abc import x, y
|
| 459 |
+
>>> M = Matrix([[x, y], [1, 0]])
|
| 460 |
+
>>> M.integrate((x, ))
|
| 461 |
+
Matrix([
|
| 462 |
+
[x**2/2, x*y],
|
| 463 |
+
[ x, 0]])
|
| 464 |
+
>>> M.integrate((x, 0, 2))
|
| 465 |
+
Matrix([
|
| 466 |
+
[2, 2*y],
|
| 467 |
+
[2, 0]])
|
| 468 |
+
|
| 469 |
+
See Also
|
| 470 |
+
========
|
| 471 |
+
|
| 472 |
+
limit
|
| 473 |
+
diff
|
| 474 |
+
"""
|
| 475 |
+
return self.applyfunc(lambda x: x.integrate(*args, **kwargs))
|
| 476 |
+
|
| 477 |
+
def jacobian(self, X):
|
| 478 |
+
"""Calculates the Jacobian matrix (derivative of a vector-valued function).
|
| 479 |
+
|
| 480 |
+
Parameters
|
| 481 |
+
==========
|
| 482 |
+
|
| 483 |
+
``self`` : vector of expressions representing functions f_i(x_1, ..., x_n).
|
| 484 |
+
X : set of x_i's in order, it can be a list or a Matrix
|
| 485 |
+
|
| 486 |
+
Both ``self`` and X can be a row or a column matrix in any order
|
| 487 |
+
(i.e., jacobian() should always work).
|
| 488 |
+
|
| 489 |
+
Examples
|
| 490 |
+
========
|
| 491 |
+
|
| 492 |
+
>>> from sympy import sin, cos, Matrix
|
| 493 |
+
>>> from sympy.abc import rho, phi
|
| 494 |
+
>>> X = Matrix([rho*cos(phi), rho*sin(phi), rho**2])
|
| 495 |
+
>>> Y = Matrix([rho, phi])
|
| 496 |
+
>>> X.jacobian(Y)
|
| 497 |
+
Matrix([
|
| 498 |
+
[cos(phi), -rho*sin(phi)],
|
| 499 |
+
[sin(phi), rho*cos(phi)],
|
| 500 |
+
[ 2*rho, 0]])
|
| 501 |
+
>>> X = Matrix([rho*cos(phi), rho*sin(phi)])
|
| 502 |
+
>>> X.jacobian(Y)
|
| 503 |
+
Matrix([
|
| 504 |
+
[cos(phi), -rho*sin(phi)],
|
| 505 |
+
[sin(phi), rho*cos(phi)]])
|
| 506 |
+
|
| 507 |
+
See Also
|
| 508 |
+
========
|
| 509 |
+
|
| 510 |
+
hessian
|
| 511 |
+
wronskian
|
| 512 |
+
"""
|
| 513 |
+
if not isinstance(X, MatrixBase):
|
| 514 |
+
X = self._new(X)
|
| 515 |
+
# Both X and ``self`` can be a row or a column matrix, so we need to make
|
| 516 |
+
# sure all valid combinations work, but everything else fails:
|
| 517 |
+
if self.shape[0] == 1:
|
| 518 |
+
m = self.shape[1]
|
| 519 |
+
elif self.shape[1] == 1:
|
| 520 |
+
m = self.shape[0]
|
| 521 |
+
else:
|
| 522 |
+
raise TypeError("``self`` must be a row or a column matrix")
|
| 523 |
+
if X.shape[0] == 1:
|
| 524 |
+
n = X.shape[1]
|
| 525 |
+
elif X.shape[1] == 1:
|
| 526 |
+
n = X.shape[0]
|
| 527 |
+
else:
|
| 528 |
+
raise TypeError("X must be a row or a column matrix")
|
| 529 |
+
|
| 530 |
+
# m is the number of functions and n is the number of variables
|
| 531 |
+
# computing the Jacobian is now easy:
|
| 532 |
+
return self._new(m, n, lambda j, i: self[j].diff(X[i]))
|
| 533 |
+
|
| 534 |
+
def limit(self, *args):
|
| 535 |
+
"""Calculate the limit of each element in the matrix.
|
| 536 |
+
``args`` will be passed to the ``limit`` function.
|
| 537 |
+
|
| 538 |
+
Examples
|
| 539 |
+
========
|
| 540 |
+
|
| 541 |
+
>>> from sympy import Matrix
|
| 542 |
+
>>> from sympy.abc import x, y
|
| 543 |
+
>>> M = Matrix([[x, y], [1, 0]])
|
| 544 |
+
>>> M.limit(x, 2)
|
| 545 |
+
Matrix([
|
| 546 |
+
[2, y],
|
| 547 |
+
[1, 0]])
|
| 548 |
+
|
| 549 |
+
See Also
|
| 550 |
+
========
|
| 551 |
+
|
| 552 |
+
integrate
|
| 553 |
+
diff
|
| 554 |
+
"""
|
| 555 |
+
return self.applyfunc(lambda x: x.limit(*args))
|
| 556 |
+
|
| 557 |
+
|
| 558 |
+
# https://github.com/sympy/sympy/pull/12854
|
| 559 |
+
class MatrixDeprecated(MatrixCommon):
|
| 560 |
+
"""A class to house deprecated matrix methods."""
|
| 561 |
+
def berkowitz_charpoly(self, x=Dummy('lambda'), simplify=_simplify):
|
| 562 |
+
return self.charpoly(x=x)
|
| 563 |
+
|
| 564 |
+
def berkowitz_det(self):
|
| 565 |
+
"""Computes determinant using Berkowitz method.
|
| 566 |
+
|
| 567 |
+
See Also
|
| 568 |
+
========
|
| 569 |
+
|
| 570 |
+
det
|
| 571 |
+
berkowitz
|
| 572 |
+
"""
|
| 573 |
+
return self.det(method='berkowitz')
|
| 574 |
+
|
| 575 |
+
def berkowitz_eigenvals(self, **flags):
|
| 576 |
+
"""Computes eigenvalues of a Matrix using Berkowitz method.
|
| 577 |
+
|
| 578 |
+
See Also
|
| 579 |
+
========
|
| 580 |
+
|
| 581 |
+
berkowitz
|
| 582 |
+
"""
|
| 583 |
+
return self.eigenvals(**flags)
|
| 584 |
+
|
| 585 |
+
def berkowitz_minors(self):
|
| 586 |
+
"""Computes principal minors using Berkowitz method.
|
| 587 |
+
|
| 588 |
+
See Also
|
| 589 |
+
========
|
| 590 |
+
|
| 591 |
+
berkowitz
|
| 592 |
+
"""
|
| 593 |
+
sign, minors = self.one, []
|
| 594 |
+
|
| 595 |
+
for poly in self.berkowitz():
|
| 596 |
+
minors.append(sign * poly[-1])
|
| 597 |
+
sign = -sign
|
| 598 |
+
|
| 599 |
+
return tuple(minors)
|
| 600 |
+
|
| 601 |
+
def berkowitz(self):
|
| 602 |
+
from sympy.matrices import zeros
|
| 603 |
+
berk = ((1,),)
|
| 604 |
+
if not self:
|
| 605 |
+
return berk
|
| 606 |
+
|
| 607 |
+
if not self.is_square:
|
| 608 |
+
raise NonSquareMatrixError()
|
| 609 |
+
|
| 610 |
+
A, N = self, self.rows
|
| 611 |
+
transforms = [0] * (N - 1)
|
| 612 |
+
|
| 613 |
+
for n in range(N, 1, -1):
|
| 614 |
+
T, k = zeros(n + 1, n), n - 1
|
| 615 |
+
|
| 616 |
+
R, C = -A[k, :k], A[:k, k]
|
| 617 |
+
A, a = A[:k, :k], -A[k, k]
|
| 618 |
+
|
| 619 |
+
items = [C]
|
| 620 |
+
|
| 621 |
+
for i in range(0, n - 2):
|
| 622 |
+
items.append(A * items[i])
|
| 623 |
+
|
| 624 |
+
for i, B in enumerate(items):
|
| 625 |
+
items[i] = (R * B)[0, 0]
|
| 626 |
+
|
| 627 |
+
items = [self.one, a] + items
|
| 628 |
+
|
| 629 |
+
for i in range(n):
|
| 630 |
+
T[i:, i] = items[:n - i + 1]
|
| 631 |
+
|
| 632 |
+
transforms[k - 1] = T
|
| 633 |
+
|
| 634 |
+
polys = [self._new([self.one, -A[0, 0]])]
|
| 635 |
+
|
| 636 |
+
for i, T in enumerate(transforms):
|
| 637 |
+
polys.append(T * polys[i])
|
| 638 |
+
|
| 639 |
+
return berk + tuple(map(tuple, polys))
|
| 640 |
+
|
| 641 |
+
def cofactorMatrix(self, method="berkowitz"):
|
| 642 |
+
return self.cofactor_matrix(method=method)
|
| 643 |
+
|
| 644 |
+
def det_bareis(self):
|
| 645 |
+
return _det_bareiss(self)
|
| 646 |
+
|
| 647 |
+
def det_LU_decomposition(self):
|
| 648 |
+
"""Compute matrix determinant using LU decomposition.
|
| 649 |
+
|
| 650 |
+
|
| 651 |
+
Note that this method fails if the LU decomposition itself
|
| 652 |
+
fails. In particular, if the matrix has no inverse this method
|
| 653 |
+
will fail.
|
| 654 |
+
|
| 655 |
+
TODO: Implement algorithm for sparse matrices (SFF),
|
| 656 |
+
https://www.eecis.udel.edu/~saunders/papers/sffge/it5.ps
|
| 657 |
+
|
| 658 |
+
See Also
|
| 659 |
+
========
|
| 660 |
+
|
| 661 |
+
|
| 662 |
+
det
|
| 663 |
+
det_bareiss
|
| 664 |
+
berkowitz_det
|
| 665 |
+
"""
|
| 666 |
+
return self.det(method='lu')
|
| 667 |
+
|
| 668 |
+
def jordan_cell(self, eigenval, n):
|
| 669 |
+
return self.jordan_block(size=n, eigenvalue=eigenval)
|
| 670 |
+
|
| 671 |
+
def jordan_cells(self, calc_transformation=True):
|
| 672 |
+
P, J = self.jordan_form()
|
| 673 |
+
return P, J.get_diag_blocks()
|
| 674 |
+
|
| 675 |
+
def minorEntry(self, i, j, method="berkowitz"):
|
| 676 |
+
return self.minor(i, j, method=method)
|
| 677 |
+
|
| 678 |
+
def minorMatrix(self, i, j):
|
| 679 |
+
return self.minor_submatrix(i, j)
|
| 680 |
+
|
| 681 |
+
def permuteBkwd(self, perm):
|
| 682 |
+
"""Permute the rows of the matrix with the given permutation in reverse."""
|
| 683 |
+
return self.permute_rows(perm, direction='backward')
|
| 684 |
+
|
| 685 |
+
def permuteFwd(self, perm):
|
| 686 |
+
"""Permute the rows of the matrix with the given permutation."""
|
| 687 |
+
return self.permute_rows(perm, direction='forward')
|
lib/python3.12/site-packages/sympy/matrices/solvers.py
ADDED
|
@@ -0,0 +1,942 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core.function import expand_mul
|
| 2 |
+
from sympy.core.symbol import Dummy, uniquely_named_symbol, symbols
|
| 3 |
+
from sympy.utilities.iterables import numbered_symbols
|
| 4 |
+
|
| 5 |
+
from .exceptions import ShapeError, NonSquareMatrixError, NonInvertibleMatrixError
|
| 6 |
+
from .eigen import _fuzzy_positive_definite
|
| 7 |
+
from .utilities import _get_intermediate_simp, _iszero
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def _diagonal_solve(M, rhs):
|
| 11 |
+
"""Solves ``Ax = B`` efficiently, where A is a diagonal Matrix,
|
| 12 |
+
with non-zero diagonal entries.
|
| 13 |
+
|
| 14 |
+
Examples
|
| 15 |
+
========
|
| 16 |
+
|
| 17 |
+
>>> from sympy import Matrix, eye
|
| 18 |
+
>>> A = eye(2)*2
|
| 19 |
+
>>> B = Matrix([[1, 2], [3, 4]])
|
| 20 |
+
>>> A.diagonal_solve(B) == B/2
|
| 21 |
+
True
|
| 22 |
+
|
| 23 |
+
See Also
|
| 24 |
+
========
|
| 25 |
+
|
| 26 |
+
sympy.matrices.dense.DenseMatrix.lower_triangular_solve
|
| 27 |
+
sympy.matrices.dense.DenseMatrix.upper_triangular_solve
|
| 28 |
+
gauss_jordan_solve
|
| 29 |
+
cholesky_solve
|
| 30 |
+
LDLsolve
|
| 31 |
+
LUsolve
|
| 32 |
+
QRsolve
|
| 33 |
+
pinv_solve
|
| 34 |
+
cramer_solve
|
| 35 |
+
"""
|
| 36 |
+
|
| 37 |
+
if not M.is_diagonal():
|
| 38 |
+
raise TypeError("Matrix should be diagonal")
|
| 39 |
+
if rhs.rows != M.rows:
|
| 40 |
+
raise TypeError("Size mismatch")
|
| 41 |
+
|
| 42 |
+
return M._new(
|
| 43 |
+
rhs.rows, rhs.cols, lambda i, j: rhs[i, j] / M[i, i])
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def _lower_triangular_solve(M, rhs):
|
| 47 |
+
"""Solves ``Ax = B``, where A is a lower triangular matrix.
|
| 48 |
+
|
| 49 |
+
See Also
|
| 50 |
+
========
|
| 51 |
+
|
| 52 |
+
upper_triangular_solve
|
| 53 |
+
gauss_jordan_solve
|
| 54 |
+
cholesky_solve
|
| 55 |
+
diagonal_solve
|
| 56 |
+
LDLsolve
|
| 57 |
+
LUsolve
|
| 58 |
+
QRsolve
|
| 59 |
+
pinv_solve
|
| 60 |
+
cramer_solve
|
| 61 |
+
"""
|
| 62 |
+
|
| 63 |
+
from .dense import MutableDenseMatrix
|
| 64 |
+
|
| 65 |
+
if not M.is_square:
|
| 66 |
+
raise NonSquareMatrixError("Matrix must be square.")
|
| 67 |
+
if rhs.rows != M.rows:
|
| 68 |
+
raise ShapeError("Matrices size mismatch.")
|
| 69 |
+
if not M.is_lower:
|
| 70 |
+
raise ValueError("Matrix must be lower triangular.")
|
| 71 |
+
|
| 72 |
+
dps = _get_intermediate_simp()
|
| 73 |
+
X = MutableDenseMatrix.zeros(M.rows, rhs.cols)
|
| 74 |
+
|
| 75 |
+
for j in range(rhs.cols):
|
| 76 |
+
for i in range(M.rows):
|
| 77 |
+
if M[i, i] == 0:
|
| 78 |
+
raise TypeError("Matrix must be non-singular.")
|
| 79 |
+
|
| 80 |
+
X[i, j] = dps((rhs[i, j] - sum(M[i, k]*X[k, j]
|
| 81 |
+
for k in range(i))) / M[i, i])
|
| 82 |
+
|
| 83 |
+
return M._new(X)
|
| 84 |
+
|
| 85 |
+
def _lower_triangular_solve_sparse(M, rhs):
|
| 86 |
+
"""Solves ``Ax = B``, where A is a lower triangular matrix.
|
| 87 |
+
|
| 88 |
+
See Also
|
| 89 |
+
========
|
| 90 |
+
|
| 91 |
+
upper_triangular_solve
|
| 92 |
+
gauss_jordan_solve
|
| 93 |
+
cholesky_solve
|
| 94 |
+
diagonal_solve
|
| 95 |
+
LDLsolve
|
| 96 |
+
LUsolve
|
| 97 |
+
QRsolve
|
| 98 |
+
pinv_solve
|
| 99 |
+
cramer_solve
|
| 100 |
+
"""
|
| 101 |
+
|
| 102 |
+
if not M.is_square:
|
| 103 |
+
raise NonSquareMatrixError("Matrix must be square.")
|
| 104 |
+
if rhs.rows != M.rows:
|
| 105 |
+
raise ShapeError("Matrices size mismatch.")
|
| 106 |
+
if not M.is_lower:
|
| 107 |
+
raise ValueError("Matrix must be lower triangular.")
|
| 108 |
+
|
| 109 |
+
dps = _get_intermediate_simp()
|
| 110 |
+
rows = [[] for i in range(M.rows)]
|
| 111 |
+
|
| 112 |
+
for i, j, v in M.row_list():
|
| 113 |
+
if i > j:
|
| 114 |
+
rows[i].append((j, v))
|
| 115 |
+
|
| 116 |
+
X = rhs.as_mutable()
|
| 117 |
+
|
| 118 |
+
for j in range(rhs.cols):
|
| 119 |
+
for i in range(rhs.rows):
|
| 120 |
+
for u, v in rows[i]:
|
| 121 |
+
X[i, j] -= v*X[u, j]
|
| 122 |
+
|
| 123 |
+
X[i, j] = dps(X[i, j] / M[i, i])
|
| 124 |
+
|
| 125 |
+
return M._new(X)
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
def _upper_triangular_solve(M, rhs):
|
| 129 |
+
"""Solves ``Ax = B``, where A is an upper triangular matrix.
|
| 130 |
+
|
| 131 |
+
See Also
|
| 132 |
+
========
|
| 133 |
+
|
| 134 |
+
lower_triangular_solve
|
| 135 |
+
gauss_jordan_solve
|
| 136 |
+
cholesky_solve
|
| 137 |
+
diagonal_solve
|
| 138 |
+
LDLsolve
|
| 139 |
+
LUsolve
|
| 140 |
+
QRsolve
|
| 141 |
+
pinv_solve
|
| 142 |
+
cramer_solve
|
| 143 |
+
"""
|
| 144 |
+
|
| 145 |
+
from .dense import MutableDenseMatrix
|
| 146 |
+
|
| 147 |
+
if not M.is_square:
|
| 148 |
+
raise NonSquareMatrixError("Matrix must be square.")
|
| 149 |
+
if rhs.rows != M.rows:
|
| 150 |
+
raise ShapeError("Matrix size mismatch.")
|
| 151 |
+
if not M.is_upper:
|
| 152 |
+
raise TypeError("Matrix is not upper triangular.")
|
| 153 |
+
|
| 154 |
+
dps = _get_intermediate_simp()
|
| 155 |
+
X = MutableDenseMatrix.zeros(M.rows, rhs.cols)
|
| 156 |
+
|
| 157 |
+
for j in range(rhs.cols):
|
| 158 |
+
for i in reversed(range(M.rows)):
|
| 159 |
+
if M[i, i] == 0:
|
| 160 |
+
raise ValueError("Matrix must be non-singular.")
|
| 161 |
+
|
| 162 |
+
X[i, j] = dps((rhs[i, j] - sum(M[i, k]*X[k, j]
|
| 163 |
+
for k in range(i + 1, M.rows))) / M[i, i])
|
| 164 |
+
|
| 165 |
+
return M._new(X)
|
| 166 |
+
|
| 167 |
+
def _upper_triangular_solve_sparse(M, rhs):
|
| 168 |
+
"""Solves ``Ax = B``, where A is an upper triangular matrix.
|
| 169 |
+
|
| 170 |
+
See Also
|
| 171 |
+
========
|
| 172 |
+
|
| 173 |
+
lower_triangular_solve
|
| 174 |
+
gauss_jordan_solve
|
| 175 |
+
cholesky_solve
|
| 176 |
+
diagonal_solve
|
| 177 |
+
LDLsolve
|
| 178 |
+
LUsolve
|
| 179 |
+
QRsolve
|
| 180 |
+
pinv_solve
|
| 181 |
+
cramer_solve
|
| 182 |
+
"""
|
| 183 |
+
|
| 184 |
+
if not M.is_square:
|
| 185 |
+
raise NonSquareMatrixError("Matrix must be square.")
|
| 186 |
+
if rhs.rows != M.rows:
|
| 187 |
+
raise ShapeError("Matrix size mismatch.")
|
| 188 |
+
if not M.is_upper:
|
| 189 |
+
raise TypeError("Matrix is not upper triangular.")
|
| 190 |
+
|
| 191 |
+
dps = _get_intermediate_simp()
|
| 192 |
+
rows = [[] for i in range(M.rows)]
|
| 193 |
+
|
| 194 |
+
for i, j, v in M.row_list():
|
| 195 |
+
if i < j:
|
| 196 |
+
rows[i].append((j, v))
|
| 197 |
+
|
| 198 |
+
X = rhs.as_mutable()
|
| 199 |
+
|
| 200 |
+
for j in range(rhs.cols):
|
| 201 |
+
for i in reversed(range(rhs.rows)):
|
| 202 |
+
for u, v in reversed(rows[i]):
|
| 203 |
+
X[i, j] -= v*X[u, j]
|
| 204 |
+
|
| 205 |
+
X[i, j] = dps(X[i, j] / M[i, i])
|
| 206 |
+
|
| 207 |
+
return M._new(X)
|
| 208 |
+
|
| 209 |
+
|
| 210 |
+
def _cholesky_solve(M, rhs):
|
| 211 |
+
"""Solves ``Ax = B`` using Cholesky decomposition,
|
| 212 |
+
for a general square non-singular matrix.
|
| 213 |
+
For a non-square matrix with rows > cols,
|
| 214 |
+
the least squares solution is returned.
|
| 215 |
+
|
| 216 |
+
See Also
|
| 217 |
+
========
|
| 218 |
+
|
| 219 |
+
sympy.matrices.dense.DenseMatrix.lower_triangular_solve
|
| 220 |
+
sympy.matrices.dense.DenseMatrix.upper_triangular_solve
|
| 221 |
+
gauss_jordan_solve
|
| 222 |
+
diagonal_solve
|
| 223 |
+
LDLsolve
|
| 224 |
+
LUsolve
|
| 225 |
+
QRsolve
|
| 226 |
+
pinv_solve
|
| 227 |
+
cramer_solve
|
| 228 |
+
"""
|
| 229 |
+
|
| 230 |
+
if M.rows < M.cols:
|
| 231 |
+
raise NotImplementedError(
|
| 232 |
+
'Under-determined System. Try M.gauss_jordan_solve(rhs)')
|
| 233 |
+
|
| 234 |
+
hermitian = True
|
| 235 |
+
reform = False
|
| 236 |
+
|
| 237 |
+
if M.is_symmetric():
|
| 238 |
+
hermitian = False
|
| 239 |
+
elif not M.is_hermitian:
|
| 240 |
+
reform = True
|
| 241 |
+
|
| 242 |
+
if reform or _fuzzy_positive_definite(M) is False:
|
| 243 |
+
H = M.H
|
| 244 |
+
M = H.multiply(M)
|
| 245 |
+
rhs = H.multiply(rhs)
|
| 246 |
+
hermitian = not M.is_symmetric()
|
| 247 |
+
|
| 248 |
+
L = M.cholesky(hermitian=hermitian)
|
| 249 |
+
Y = L.lower_triangular_solve(rhs)
|
| 250 |
+
|
| 251 |
+
if hermitian:
|
| 252 |
+
return (L.H).upper_triangular_solve(Y)
|
| 253 |
+
else:
|
| 254 |
+
return (L.T).upper_triangular_solve(Y)
|
| 255 |
+
|
| 256 |
+
|
| 257 |
+
def _LDLsolve(M, rhs):
|
| 258 |
+
"""Solves ``Ax = B`` using LDL decomposition,
|
| 259 |
+
for a general square and non-singular matrix.
|
| 260 |
+
|
| 261 |
+
For a non-square matrix with rows > cols,
|
| 262 |
+
the least squares solution is returned.
|
| 263 |
+
|
| 264 |
+
Examples
|
| 265 |
+
========
|
| 266 |
+
|
| 267 |
+
>>> from sympy import Matrix, eye
|
| 268 |
+
>>> A = eye(2)*2
|
| 269 |
+
>>> B = Matrix([[1, 2], [3, 4]])
|
| 270 |
+
>>> A.LDLsolve(B) == B/2
|
| 271 |
+
True
|
| 272 |
+
|
| 273 |
+
See Also
|
| 274 |
+
========
|
| 275 |
+
|
| 276 |
+
sympy.matrices.dense.DenseMatrix.LDLdecomposition
|
| 277 |
+
sympy.matrices.dense.DenseMatrix.lower_triangular_solve
|
| 278 |
+
sympy.matrices.dense.DenseMatrix.upper_triangular_solve
|
| 279 |
+
gauss_jordan_solve
|
| 280 |
+
cholesky_solve
|
| 281 |
+
diagonal_solve
|
| 282 |
+
LUsolve
|
| 283 |
+
QRsolve
|
| 284 |
+
pinv_solve
|
| 285 |
+
cramer_solve
|
| 286 |
+
"""
|
| 287 |
+
|
| 288 |
+
if M.rows < M.cols:
|
| 289 |
+
raise NotImplementedError(
|
| 290 |
+
'Under-determined System. Try M.gauss_jordan_solve(rhs)')
|
| 291 |
+
|
| 292 |
+
hermitian = True
|
| 293 |
+
reform = False
|
| 294 |
+
|
| 295 |
+
if M.is_symmetric():
|
| 296 |
+
hermitian = False
|
| 297 |
+
elif not M.is_hermitian:
|
| 298 |
+
reform = True
|
| 299 |
+
|
| 300 |
+
if reform or _fuzzy_positive_definite(M) is False:
|
| 301 |
+
H = M.H
|
| 302 |
+
M = H.multiply(M)
|
| 303 |
+
rhs = H.multiply(rhs)
|
| 304 |
+
hermitian = not M.is_symmetric()
|
| 305 |
+
|
| 306 |
+
L, D = M.LDLdecomposition(hermitian=hermitian)
|
| 307 |
+
Y = L.lower_triangular_solve(rhs)
|
| 308 |
+
Z = D.diagonal_solve(Y)
|
| 309 |
+
|
| 310 |
+
if hermitian:
|
| 311 |
+
return (L.H).upper_triangular_solve(Z)
|
| 312 |
+
else:
|
| 313 |
+
return (L.T).upper_triangular_solve(Z)
|
| 314 |
+
|
| 315 |
+
|
| 316 |
+
def _LUsolve(M, rhs, iszerofunc=_iszero):
|
| 317 |
+
"""Solve the linear system ``Ax = rhs`` for ``x`` where ``A = M``.
|
| 318 |
+
|
| 319 |
+
This is for symbolic matrices, for real or complex ones use
|
| 320 |
+
mpmath.lu_solve or mpmath.qr_solve.
|
| 321 |
+
|
| 322 |
+
See Also
|
| 323 |
+
========
|
| 324 |
+
|
| 325 |
+
sympy.matrices.dense.DenseMatrix.lower_triangular_solve
|
| 326 |
+
sympy.matrices.dense.DenseMatrix.upper_triangular_solve
|
| 327 |
+
gauss_jordan_solve
|
| 328 |
+
cholesky_solve
|
| 329 |
+
diagonal_solve
|
| 330 |
+
LDLsolve
|
| 331 |
+
QRsolve
|
| 332 |
+
pinv_solve
|
| 333 |
+
LUdecomposition
|
| 334 |
+
cramer_solve
|
| 335 |
+
"""
|
| 336 |
+
|
| 337 |
+
if rhs.rows != M.rows:
|
| 338 |
+
raise ShapeError(
|
| 339 |
+
"``M`` and ``rhs`` must have the same number of rows.")
|
| 340 |
+
|
| 341 |
+
m = M.rows
|
| 342 |
+
n = M.cols
|
| 343 |
+
|
| 344 |
+
if m < n:
|
| 345 |
+
raise NotImplementedError("Underdetermined systems not supported.")
|
| 346 |
+
|
| 347 |
+
try:
|
| 348 |
+
A, perm = M.LUdecomposition_Simple(
|
| 349 |
+
iszerofunc=iszerofunc, rankcheck=True)
|
| 350 |
+
except ValueError:
|
| 351 |
+
raise NonInvertibleMatrixError("Matrix det == 0; not invertible.")
|
| 352 |
+
|
| 353 |
+
dps = _get_intermediate_simp()
|
| 354 |
+
b = rhs.permute_rows(perm).as_mutable()
|
| 355 |
+
|
| 356 |
+
# forward substitution, all diag entries are scaled to 1
|
| 357 |
+
for i in range(m):
|
| 358 |
+
for j in range(min(i, n)):
|
| 359 |
+
scale = A[i, j]
|
| 360 |
+
b.zip_row_op(i, j, lambda x, y: dps(x - y * scale))
|
| 361 |
+
|
| 362 |
+
# consistency check for overdetermined systems
|
| 363 |
+
if m > n:
|
| 364 |
+
for i in range(n, m):
|
| 365 |
+
for j in range(b.cols):
|
| 366 |
+
if not iszerofunc(b[i, j]):
|
| 367 |
+
raise ValueError("The system is inconsistent.")
|
| 368 |
+
|
| 369 |
+
b = b[0:n, :] # truncate zero rows if consistent
|
| 370 |
+
|
| 371 |
+
# backward substitution
|
| 372 |
+
for i in range(n - 1, -1, -1):
|
| 373 |
+
for j in range(i + 1, n):
|
| 374 |
+
scale = A[i, j]
|
| 375 |
+
b.zip_row_op(i, j, lambda x, y: dps(x - y * scale))
|
| 376 |
+
|
| 377 |
+
scale = A[i, i]
|
| 378 |
+
b.row_op(i, lambda x, _: dps(x / scale))
|
| 379 |
+
|
| 380 |
+
return rhs.__class__(b)
|
| 381 |
+
|
| 382 |
+
|
| 383 |
+
def _QRsolve(M, b):
|
| 384 |
+
"""Solve the linear system ``Ax = b``.
|
| 385 |
+
|
| 386 |
+
``M`` is the matrix ``A``, the method argument is the vector
|
| 387 |
+
``b``. The method returns the solution vector ``x``. If ``b`` is a
|
| 388 |
+
matrix, the system is solved for each column of ``b`` and the
|
| 389 |
+
return value is a matrix of the same shape as ``b``.
|
| 390 |
+
|
| 391 |
+
This method is slower (approximately by a factor of 2) but
|
| 392 |
+
more stable for floating-point arithmetic than the LUsolve method.
|
| 393 |
+
However, LUsolve usually uses an exact arithmetic, so you do not need
|
| 394 |
+
to use QRsolve.
|
| 395 |
+
|
| 396 |
+
This is mainly for educational purposes and symbolic matrices, for real
|
| 397 |
+
(or complex) matrices use mpmath.qr_solve.
|
| 398 |
+
|
| 399 |
+
See Also
|
| 400 |
+
========
|
| 401 |
+
|
| 402 |
+
sympy.matrices.dense.DenseMatrix.lower_triangular_solve
|
| 403 |
+
sympy.matrices.dense.DenseMatrix.upper_triangular_solve
|
| 404 |
+
gauss_jordan_solve
|
| 405 |
+
cholesky_solve
|
| 406 |
+
diagonal_solve
|
| 407 |
+
LDLsolve
|
| 408 |
+
LUsolve
|
| 409 |
+
pinv_solve
|
| 410 |
+
QRdecomposition
|
| 411 |
+
cramer_solve
|
| 412 |
+
"""
|
| 413 |
+
|
| 414 |
+
dps = _get_intermediate_simp(expand_mul, expand_mul)
|
| 415 |
+
Q, R = M.QRdecomposition()
|
| 416 |
+
y = Q.T * b
|
| 417 |
+
|
| 418 |
+
# back substitution to solve R*x = y:
|
| 419 |
+
# We build up the result "backwards" in the vector 'x' and reverse it
|
| 420 |
+
# only in the end.
|
| 421 |
+
x = []
|
| 422 |
+
n = R.rows
|
| 423 |
+
|
| 424 |
+
for j in range(n - 1, -1, -1):
|
| 425 |
+
tmp = y[j, :]
|
| 426 |
+
|
| 427 |
+
for k in range(j + 1, n):
|
| 428 |
+
tmp -= R[j, k] * x[n - 1 - k]
|
| 429 |
+
|
| 430 |
+
tmp = dps(tmp)
|
| 431 |
+
|
| 432 |
+
x.append(tmp / R[j, j])
|
| 433 |
+
|
| 434 |
+
return M.vstack(*x[::-1])
|
| 435 |
+
|
| 436 |
+
|
| 437 |
+
def _gauss_jordan_solve(M, B, freevar=False):
|
| 438 |
+
"""
|
| 439 |
+
Solves ``Ax = B`` using Gauss Jordan elimination.
|
| 440 |
+
|
| 441 |
+
There may be zero, one, or infinite solutions. If one solution
|
| 442 |
+
exists, it will be returned. If infinite solutions exist, it will
|
| 443 |
+
be returned parametrically. If no solutions exist, It will throw
|
| 444 |
+
ValueError.
|
| 445 |
+
|
| 446 |
+
Parameters
|
| 447 |
+
==========
|
| 448 |
+
|
| 449 |
+
B : Matrix
|
| 450 |
+
The right hand side of the equation to be solved for. Must have
|
| 451 |
+
the same number of rows as matrix A.
|
| 452 |
+
|
| 453 |
+
freevar : boolean, optional
|
| 454 |
+
Flag, when set to `True` will return the indices of the free
|
| 455 |
+
variables in the solutions (column Matrix), for a system that is
|
| 456 |
+
undetermined (e.g. A has more columns than rows), for which
|
| 457 |
+
infinite solutions are possible, in terms of arbitrary
|
| 458 |
+
values of free variables. Default `False`.
|
| 459 |
+
|
| 460 |
+
Returns
|
| 461 |
+
=======
|
| 462 |
+
|
| 463 |
+
x : Matrix
|
| 464 |
+
The matrix that will satisfy ``Ax = B``. Will have as many rows as
|
| 465 |
+
matrix A has columns, and as many columns as matrix B.
|
| 466 |
+
|
| 467 |
+
params : Matrix
|
| 468 |
+
If the system is underdetermined (e.g. A has more columns than
|
| 469 |
+
rows), infinite solutions are possible, in terms of arbitrary
|
| 470 |
+
parameters. These arbitrary parameters are returned as params
|
| 471 |
+
Matrix.
|
| 472 |
+
|
| 473 |
+
free_var_index : List, optional
|
| 474 |
+
If the system is underdetermined (e.g. A has more columns than
|
| 475 |
+
rows), infinite solutions are possible, in terms of arbitrary
|
| 476 |
+
values of free variables. Then the indices of the free variables
|
| 477 |
+
in the solutions (column Matrix) are returned by free_var_index,
|
| 478 |
+
if the flag `freevar` is set to `True`.
|
| 479 |
+
|
| 480 |
+
Examples
|
| 481 |
+
========
|
| 482 |
+
|
| 483 |
+
>>> from sympy import Matrix
|
| 484 |
+
>>> A = Matrix([[1, 2, 1, 1], [1, 2, 2, -1], [2, 4, 0, 6]])
|
| 485 |
+
>>> B = Matrix([7, 12, 4])
|
| 486 |
+
>>> sol, params = A.gauss_jordan_solve(B)
|
| 487 |
+
>>> sol
|
| 488 |
+
Matrix([
|
| 489 |
+
[-2*tau0 - 3*tau1 + 2],
|
| 490 |
+
[ tau0],
|
| 491 |
+
[ 2*tau1 + 5],
|
| 492 |
+
[ tau1]])
|
| 493 |
+
>>> params
|
| 494 |
+
Matrix([
|
| 495 |
+
[tau0],
|
| 496 |
+
[tau1]])
|
| 497 |
+
>>> taus_zeroes = { tau:0 for tau in params }
|
| 498 |
+
>>> sol_unique = sol.xreplace(taus_zeroes)
|
| 499 |
+
>>> sol_unique
|
| 500 |
+
Matrix([
|
| 501 |
+
[2],
|
| 502 |
+
[0],
|
| 503 |
+
[5],
|
| 504 |
+
[0]])
|
| 505 |
+
|
| 506 |
+
|
| 507 |
+
>>> A = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 10]])
|
| 508 |
+
>>> B = Matrix([3, 6, 9])
|
| 509 |
+
>>> sol, params = A.gauss_jordan_solve(B)
|
| 510 |
+
>>> sol
|
| 511 |
+
Matrix([
|
| 512 |
+
[-1],
|
| 513 |
+
[ 2],
|
| 514 |
+
[ 0]])
|
| 515 |
+
>>> params
|
| 516 |
+
Matrix(0, 1, [])
|
| 517 |
+
|
| 518 |
+
>>> A = Matrix([[2, -7], [-1, 4]])
|
| 519 |
+
>>> B = Matrix([[-21, 3], [12, -2]])
|
| 520 |
+
>>> sol, params = A.gauss_jordan_solve(B)
|
| 521 |
+
>>> sol
|
| 522 |
+
Matrix([
|
| 523 |
+
[0, -2],
|
| 524 |
+
[3, -1]])
|
| 525 |
+
>>> params
|
| 526 |
+
Matrix(0, 2, [])
|
| 527 |
+
|
| 528 |
+
|
| 529 |
+
>>> from sympy import Matrix
|
| 530 |
+
>>> A = Matrix([[1, 2, 1, 1], [1, 2, 2, -1], [2, 4, 0, 6]])
|
| 531 |
+
>>> B = Matrix([7, 12, 4])
|
| 532 |
+
>>> sol, params, freevars = A.gauss_jordan_solve(B, freevar=True)
|
| 533 |
+
>>> sol
|
| 534 |
+
Matrix([
|
| 535 |
+
[-2*tau0 - 3*tau1 + 2],
|
| 536 |
+
[ tau0],
|
| 537 |
+
[ 2*tau1 + 5],
|
| 538 |
+
[ tau1]])
|
| 539 |
+
>>> params
|
| 540 |
+
Matrix([
|
| 541 |
+
[tau0],
|
| 542 |
+
[tau1]])
|
| 543 |
+
>>> freevars
|
| 544 |
+
[1, 3]
|
| 545 |
+
|
| 546 |
+
|
| 547 |
+
See Also
|
| 548 |
+
========
|
| 549 |
+
|
| 550 |
+
sympy.matrices.dense.DenseMatrix.lower_triangular_solve
|
| 551 |
+
sympy.matrices.dense.DenseMatrix.upper_triangular_solve
|
| 552 |
+
cholesky_solve
|
| 553 |
+
diagonal_solve
|
| 554 |
+
LDLsolve
|
| 555 |
+
LUsolve
|
| 556 |
+
QRsolve
|
| 557 |
+
pinv
|
| 558 |
+
|
| 559 |
+
References
|
| 560 |
+
==========
|
| 561 |
+
|
| 562 |
+
.. [1] https://en.wikipedia.org/wiki/Gaussian_elimination
|
| 563 |
+
|
| 564 |
+
"""
|
| 565 |
+
|
| 566 |
+
from sympy.matrices import Matrix, zeros
|
| 567 |
+
|
| 568 |
+
cls = M.__class__
|
| 569 |
+
aug = M.hstack(M.copy(), B.copy())
|
| 570 |
+
B_cols = B.cols
|
| 571 |
+
row, col = aug[:, :-B_cols].shape
|
| 572 |
+
|
| 573 |
+
# solve by reduced row echelon form
|
| 574 |
+
A, pivots = aug.rref(simplify=True)
|
| 575 |
+
A, v = A[:, :-B_cols], A[:, -B_cols:]
|
| 576 |
+
pivots = list(filter(lambda p: p < col, pivots))
|
| 577 |
+
rank = len(pivots)
|
| 578 |
+
|
| 579 |
+
# Get index of free symbols (free parameters)
|
| 580 |
+
# non-pivots columns are free variables
|
| 581 |
+
free_var_index = [c for c in range(A.cols) if c not in pivots]
|
| 582 |
+
|
| 583 |
+
# Bring to block form
|
| 584 |
+
permutation = Matrix(pivots + free_var_index).T
|
| 585 |
+
|
| 586 |
+
# check for existence of solutions
|
| 587 |
+
# rank of aug Matrix should be equal to rank of coefficient matrix
|
| 588 |
+
if not v[rank:, :].is_zero_matrix:
|
| 589 |
+
raise ValueError("Linear system has no solution")
|
| 590 |
+
|
| 591 |
+
# Free parameters
|
| 592 |
+
# what are current unnumbered free symbol names?
|
| 593 |
+
name = uniquely_named_symbol('tau', [aug],
|
| 594 |
+
compare=lambda i: str(i).rstrip('1234567890'),
|
| 595 |
+
modify=lambda s: '_' + s).name
|
| 596 |
+
gen = numbered_symbols(name)
|
| 597 |
+
tau = Matrix([next(gen) for k in range((col - rank)*B_cols)]).reshape(
|
| 598 |
+
col - rank, B_cols)
|
| 599 |
+
|
| 600 |
+
# Full parametric solution
|
| 601 |
+
V = A[:rank, free_var_index]
|
| 602 |
+
vt = v[:rank, :]
|
| 603 |
+
free_sol = tau.vstack(vt - V * tau, tau)
|
| 604 |
+
|
| 605 |
+
# Undo permutation
|
| 606 |
+
sol = zeros(col, B_cols)
|
| 607 |
+
|
| 608 |
+
for k in range(col):
|
| 609 |
+
sol[permutation[k], :] = free_sol[k,:]
|
| 610 |
+
|
| 611 |
+
sol, tau = cls(sol), cls(tau)
|
| 612 |
+
|
| 613 |
+
if freevar:
|
| 614 |
+
return sol, tau, free_var_index
|
| 615 |
+
else:
|
| 616 |
+
return sol, tau
|
| 617 |
+
|
| 618 |
+
|
| 619 |
+
def _pinv_solve(M, B, arbitrary_matrix=None):
|
| 620 |
+
"""Solve ``Ax = B`` using the Moore-Penrose pseudoinverse.
|
| 621 |
+
|
| 622 |
+
There may be zero, one, or infinite solutions. If one solution
|
| 623 |
+
exists, it will be returned. If infinite solutions exist, one will
|
| 624 |
+
be returned based on the value of arbitrary_matrix. If no solutions
|
| 625 |
+
exist, the least-squares solution is returned.
|
| 626 |
+
|
| 627 |
+
Parameters
|
| 628 |
+
==========
|
| 629 |
+
|
| 630 |
+
B : Matrix
|
| 631 |
+
The right hand side of the equation to be solved for. Must have
|
| 632 |
+
the same number of rows as matrix A.
|
| 633 |
+
arbitrary_matrix : Matrix
|
| 634 |
+
If the system is underdetermined (e.g. A has more columns than
|
| 635 |
+
rows), infinite solutions are possible, in terms of an arbitrary
|
| 636 |
+
matrix. This parameter may be set to a specific matrix to use
|
| 637 |
+
for that purpose; if so, it must be the same shape as x, with as
|
| 638 |
+
many rows as matrix A has columns, and as many columns as matrix
|
| 639 |
+
B. If left as None, an appropriate matrix containing dummy
|
| 640 |
+
symbols in the form of ``wn_m`` will be used, with n and m being
|
| 641 |
+
row and column position of each symbol.
|
| 642 |
+
|
| 643 |
+
Returns
|
| 644 |
+
=======
|
| 645 |
+
|
| 646 |
+
x : Matrix
|
| 647 |
+
The matrix that will satisfy ``Ax = B``. Will have as many rows as
|
| 648 |
+
matrix A has columns, and as many columns as matrix B.
|
| 649 |
+
|
| 650 |
+
Examples
|
| 651 |
+
========
|
| 652 |
+
|
| 653 |
+
>>> from sympy import Matrix
|
| 654 |
+
>>> A = Matrix([[1, 2, 3], [4, 5, 6]])
|
| 655 |
+
>>> B = Matrix([7, 8])
|
| 656 |
+
>>> A.pinv_solve(B)
|
| 657 |
+
Matrix([
|
| 658 |
+
[ _w0_0/6 - _w1_0/3 + _w2_0/6 - 55/18],
|
| 659 |
+
[-_w0_0/3 + 2*_w1_0/3 - _w2_0/3 + 1/9],
|
| 660 |
+
[ _w0_0/6 - _w1_0/3 + _w2_0/6 + 59/18]])
|
| 661 |
+
>>> A.pinv_solve(B, arbitrary_matrix=Matrix([0, 0, 0]))
|
| 662 |
+
Matrix([
|
| 663 |
+
[-55/18],
|
| 664 |
+
[ 1/9],
|
| 665 |
+
[ 59/18]])
|
| 666 |
+
|
| 667 |
+
See Also
|
| 668 |
+
========
|
| 669 |
+
|
| 670 |
+
sympy.matrices.dense.DenseMatrix.lower_triangular_solve
|
| 671 |
+
sympy.matrices.dense.DenseMatrix.upper_triangular_solve
|
| 672 |
+
gauss_jordan_solve
|
| 673 |
+
cholesky_solve
|
| 674 |
+
diagonal_solve
|
| 675 |
+
LDLsolve
|
| 676 |
+
LUsolve
|
| 677 |
+
QRsolve
|
| 678 |
+
pinv
|
| 679 |
+
|
| 680 |
+
Notes
|
| 681 |
+
=====
|
| 682 |
+
|
| 683 |
+
This may return either exact solutions or least squares solutions.
|
| 684 |
+
To determine which, check ``A * A.pinv() * B == B``. It will be
|
| 685 |
+
True if exact solutions exist, and False if only a least-squares
|
| 686 |
+
solution exists. Be aware that the left hand side of that equation
|
| 687 |
+
may need to be simplified to correctly compare to the right hand
|
| 688 |
+
side.
|
| 689 |
+
|
| 690 |
+
References
|
| 691 |
+
==========
|
| 692 |
+
|
| 693 |
+
.. [1] https://en.wikipedia.org/wiki/Moore-Penrose_pseudoinverse#Obtaining_all_solutions_of_a_linear_system
|
| 694 |
+
|
| 695 |
+
"""
|
| 696 |
+
|
| 697 |
+
from sympy.matrices import eye
|
| 698 |
+
|
| 699 |
+
A = M
|
| 700 |
+
A_pinv = M.pinv()
|
| 701 |
+
|
| 702 |
+
if arbitrary_matrix is None:
|
| 703 |
+
rows, cols = A.cols, B.cols
|
| 704 |
+
w = symbols('w:{}_:{}'.format(rows, cols), cls=Dummy)
|
| 705 |
+
arbitrary_matrix = M.__class__(cols, rows, w).T
|
| 706 |
+
|
| 707 |
+
return A_pinv.multiply(B) + (eye(A.cols) -
|
| 708 |
+
A_pinv.multiply(A)).multiply(arbitrary_matrix)
|
| 709 |
+
|
| 710 |
+
|
| 711 |
+
def _cramer_solve(M, rhs, det_method="laplace"):
|
| 712 |
+
"""Solves system of linear equations using Cramer's rule.
|
| 713 |
+
|
| 714 |
+
This method is relatively inefficient compared to other methods.
|
| 715 |
+
However it only uses a single division, assuming a division-free determinant
|
| 716 |
+
method is provided. This is helpful to minimize the chance of divide-by-zero
|
| 717 |
+
cases in symbolic solutions to linear systems.
|
| 718 |
+
|
| 719 |
+
Parameters
|
| 720 |
+
==========
|
| 721 |
+
M : Matrix
|
| 722 |
+
The matrix representing the left hand side of the equation.
|
| 723 |
+
rhs : Matrix
|
| 724 |
+
The matrix representing the right hand side of the equation.
|
| 725 |
+
det_method : str or callable
|
| 726 |
+
The method to use to calculate the determinant of the matrix.
|
| 727 |
+
The default is ``'laplace'``. If a callable is passed, it should take a
|
| 728 |
+
single argument, the matrix, and return the determinant of the matrix.
|
| 729 |
+
|
| 730 |
+
Returns
|
| 731 |
+
=======
|
| 732 |
+
x : Matrix
|
| 733 |
+
The matrix that will satisfy ``Ax = B``. Will have as many rows as
|
| 734 |
+
matrix A has columns, and as many columns as matrix B.
|
| 735 |
+
|
| 736 |
+
Examples
|
| 737 |
+
========
|
| 738 |
+
|
| 739 |
+
>>> from sympy import Matrix
|
| 740 |
+
>>> A = Matrix([[0, -6, 1], [0, -6, -1], [-5, -2, 3]])
|
| 741 |
+
>>> B = Matrix([[-30, -9], [-18, -27], [-26, 46]])
|
| 742 |
+
>>> x = A.cramer_solve(B)
|
| 743 |
+
>>> x
|
| 744 |
+
Matrix([
|
| 745 |
+
[ 0, -5],
|
| 746 |
+
[ 4, 3],
|
| 747 |
+
[-6, 9]])
|
| 748 |
+
|
| 749 |
+
References
|
| 750 |
+
==========
|
| 751 |
+
|
| 752 |
+
.. [1] https://en.wikipedia.org/wiki/Cramer%27s_rule#Explicit_formulas_for_small_systems
|
| 753 |
+
|
| 754 |
+
"""
|
| 755 |
+
from .dense import zeros
|
| 756 |
+
|
| 757 |
+
def entry(i, j):
|
| 758 |
+
return rhs[i, sol] if j == col else M[i, j]
|
| 759 |
+
|
| 760 |
+
if det_method == "bird":
|
| 761 |
+
from .determinant import _det_bird
|
| 762 |
+
det = _det_bird
|
| 763 |
+
elif det_method == "laplace":
|
| 764 |
+
from .determinant import _det_laplace
|
| 765 |
+
det = _det_laplace
|
| 766 |
+
elif isinstance(det_method, str):
|
| 767 |
+
det = lambda matrix: matrix.det(method=det_method)
|
| 768 |
+
else:
|
| 769 |
+
det = det_method
|
| 770 |
+
det_M = det(M)
|
| 771 |
+
x = zeros(*rhs.shape)
|
| 772 |
+
for sol in range(rhs.shape[1]):
|
| 773 |
+
for col in range(rhs.shape[0]):
|
| 774 |
+
x[col, sol] = det(M.__class__(*M.shape, entry)) / det_M
|
| 775 |
+
return M.__class__(x)
|
| 776 |
+
|
| 777 |
+
|
| 778 |
+
def _solve(M, rhs, method='GJ'):
|
| 779 |
+
"""Solves linear equation where the unique solution exists.
|
| 780 |
+
|
| 781 |
+
Parameters
|
| 782 |
+
==========
|
| 783 |
+
|
| 784 |
+
rhs : Matrix
|
| 785 |
+
Vector representing the right hand side of the linear equation.
|
| 786 |
+
|
| 787 |
+
method : string, optional
|
| 788 |
+
If set to ``'GJ'`` or ``'GE'``, the Gauss-Jordan elimination will be
|
| 789 |
+
used, which is implemented in the routine ``gauss_jordan_solve``.
|
| 790 |
+
|
| 791 |
+
If set to ``'LU'``, ``LUsolve`` routine will be used.
|
| 792 |
+
|
| 793 |
+
If set to ``'QR'``, ``QRsolve`` routine will be used.
|
| 794 |
+
|
| 795 |
+
If set to ``'PINV'``, ``pinv_solve`` routine will be used.
|
| 796 |
+
|
| 797 |
+
If set to ``'CRAMER'``, ``cramer_solve`` routine will be used.
|
| 798 |
+
|
| 799 |
+
It also supports the methods available for special linear systems
|
| 800 |
+
|
| 801 |
+
For positive definite systems:
|
| 802 |
+
|
| 803 |
+
If set to ``'CH'``, ``cholesky_solve`` routine will be used.
|
| 804 |
+
|
| 805 |
+
If set to ``'LDL'``, ``LDLsolve`` routine will be used.
|
| 806 |
+
|
| 807 |
+
To use a different method and to compute the solution via the
|
| 808 |
+
inverse, use a method defined in the .inv() docstring.
|
| 809 |
+
|
| 810 |
+
Returns
|
| 811 |
+
=======
|
| 812 |
+
|
| 813 |
+
solutions : Matrix
|
| 814 |
+
Vector representing the solution.
|
| 815 |
+
|
| 816 |
+
Raises
|
| 817 |
+
======
|
| 818 |
+
|
| 819 |
+
ValueError
|
| 820 |
+
If there is not a unique solution then a ``ValueError`` will be
|
| 821 |
+
raised.
|
| 822 |
+
|
| 823 |
+
If ``M`` is not square, a ``ValueError`` and a different routine
|
| 824 |
+
for solving the system will be suggested.
|
| 825 |
+
"""
|
| 826 |
+
|
| 827 |
+
if method in ('GJ', 'GE'):
|
| 828 |
+
try:
|
| 829 |
+
soln, param = M.gauss_jordan_solve(rhs)
|
| 830 |
+
|
| 831 |
+
if param:
|
| 832 |
+
raise NonInvertibleMatrixError("Matrix det == 0; not invertible. "
|
| 833 |
+
"Try ``M.gauss_jordan_solve(rhs)`` to obtain a parametric solution.")
|
| 834 |
+
|
| 835 |
+
except ValueError:
|
| 836 |
+
raise NonInvertibleMatrixError("Matrix det == 0; not invertible.")
|
| 837 |
+
|
| 838 |
+
return soln
|
| 839 |
+
|
| 840 |
+
elif method == 'LU':
|
| 841 |
+
return M.LUsolve(rhs)
|
| 842 |
+
elif method == 'CH':
|
| 843 |
+
return M.cholesky_solve(rhs)
|
| 844 |
+
elif method == 'QR':
|
| 845 |
+
return M.QRsolve(rhs)
|
| 846 |
+
elif method == 'LDL':
|
| 847 |
+
return M.LDLsolve(rhs)
|
| 848 |
+
elif method == 'PINV':
|
| 849 |
+
return M.pinv_solve(rhs)
|
| 850 |
+
elif method == 'CRAMER':
|
| 851 |
+
return M.cramer_solve(rhs)
|
| 852 |
+
else:
|
| 853 |
+
return M.inv(method=method).multiply(rhs)
|
| 854 |
+
|
| 855 |
+
|
| 856 |
+
def _solve_least_squares(M, rhs, method='CH'):
|
| 857 |
+
"""Return the least-square fit to the data.
|
| 858 |
+
|
| 859 |
+
Parameters
|
| 860 |
+
==========
|
| 861 |
+
|
| 862 |
+
rhs : Matrix
|
| 863 |
+
Vector representing the right hand side of the linear equation.
|
| 864 |
+
|
| 865 |
+
method : string or boolean, optional
|
| 866 |
+
If set to ``'CH'``, ``cholesky_solve`` routine will be used.
|
| 867 |
+
|
| 868 |
+
If set to ``'LDL'``, ``LDLsolve`` routine will be used.
|
| 869 |
+
|
| 870 |
+
If set to ``'QR'``, ``QRsolve`` routine will be used.
|
| 871 |
+
|
| 872 |
+
If set to ``'PINV'``, ``pinv_solve`` routine will be used.
|
| 873 |
+
|
| 874 |
+
Otherwise, the conjugate of ``M`` will be used to create a system
|
| 875 |
+
of equations that is passed to ``solve`` along with the hint
|
| 876 |
+
defined by ``method``.
|
| 877 |
+
|
| 878 |
+
Returns
|
| 879 |
+
=======
|
| 880 |
+
|
| 881 |
+
solutions : Matrix
|
| 882 |
+
Vector representing the solution.
|
| 883 |
+
|
| 884 |
+
Examples
|
| 885 |
+
========
|
| 886 |
+
|
| 887 |
+
>>> from sympy import Matrix, ones
|
| 888 |
+
>>> A = Matrix([1, 2, 3])
|
| 889 |
+
>>> B = Matrix([2, 3, 4])
|
| 890 |
+
>>> S = Matrix(A.row_join(B))
|
| 891 |
+
>>> S
|
| 892 |
+
Matrix([
|
| 893 |
+
[1, 2],
|
| 894 |
+
[2, 3],
|
| 895 |
+
[3, 4]])
|
| 896 |
+
|
| 897 |
+
If each line of S represent coefficients of Ax + By
|
| 898 |
+
and x and y are [2, 3] then S*xy is:
|
| 899 |
+
|
| 900 |
+
>>> r = S*Matrix([2, 3]); r
|
| 901 |
+
Matrix([
|
| 902 |
+
[ 8],
|
| 903 |
+
[13],
|
| 904 |
+
[18]])
|
| 905 |
+
|
| 906 |
+
But let's add 1 to the middle value and then solve for the
|
| 907 |
+
least-squares value of xy:
|
| 908 |
+
|
| 909 |
+
>>> xy = S.solve_least_squares(Matrix([8, 14, 18])); xy
|
| 910 |
+
Matrix([
|
| 911 |
+
[ 5/3],
|
| 912 |
+
[10/3]])
|
| 913 |
+
|
| 914 |
+
The error is given by S*xy - r:
|
| 915 |
+
|
| 916 |
+
>>> S*xy - r
|
| 917 |
+
Matrix([
|
| 918 |
+
[1/3],
|
| 919 |
+
[1/3],
|
| 920 |
+
[1/3]])
|
| 921 |
+
>>> _.norm().n(2)
|
| 922 |
+
0.58
|
| 923 |
+
|
| 924 |
+
If a different xy is used, the norm will be higher:
|
| 925 |
+
|
| 926 |
+
>>> xy += ones(2, 1)/10
|
| 927 |
+
>>> (S*xy - r).norm().n(2)
|
| 928 |
+
1.5
|
| 929 |
+
|
| 930 |
+
"""
|
| 931 |
+
|
| 932 |
+
if method == 'CH':
|
| 933 |
+
return M.cholesky_solve(rhs)
|
| 934 |
+
elif method == 'QR':
|
| 935 |
+
return M.QRsolve(rhs)
|
| 936 |
+
elif method == 'LDL':
|
| 937 |
+
return M.LDLsolve(rhs)
|
| 938 |
+
elif method == 'PINV':
|
| 939 |
+
return M.pinv_solve(rhs)
|
| 940 |
+
else:
|
| 941 |
+
t = M.H
|
| 942 |
+
return (t * M).solve(t * rhs, method=method)
|
lib/python3.12/site-packages/sympy/matrices/sparse.py
ADDED
|
@@ -0,0 +1,473 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from collections.abc import Callable
|
| 2 |
+
|
| 3 |
+
from sympy.core.containers import Dict
|
| 4 |
+
from sympy.utilities.exceptions import sympy_deprecation_warning
|
| 5 |
+
from sympy.utilities.iterables import is_sequence
|
| 6 |
+
from sympy.utilities.misc import as_int
|
| 7 |
+
|
| 8 |
+
from .matrixbase import MatrixBase
|
| 9 |
+
from .repmatrix import MutableRepMatrix, RepMatrix
|
| 10 |
+
|
| 11 |
+
from .utilities import _iszero
|
| 12 |
+
|
| 13 |
+
from .decompositions import (
|
| 14 |
+
_liupc, _row_structure_symbolic_cholesky, _cholesky_sparse,
|
| 15 |
+
_LDLdecomposition_sparse)
|
| 16 |
+
|
| 17 |
+
from .solvers import (
|
| 18 |
+
_lower_triangular_solve_sparse, _upper_triangular_solve_sparse)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class SparseRepMatrix(RepMatrix):
|
| 22 |
+
"""
|
| 23 |
+
A sparse matrix (a matrix with a large number of zero elements).
|
| 24 |
+
|
| 25 |
+
Examples
|
| 26 |
+
========
|
| 27 |
+
|
| 28 |
+
>>> from sympy import SparseMatrix, ones
|
| 29 |
+
>>> SparseMatrix(2, 2, range(4))
|
| 30 |
+
Matrix([
|
| 31 |
+
[0, 1],
|
| 32 |
+
[2, 3]])
|
| 33 |
+
>>> SparseMatrix(2, 2, {(1, 1): 2})
|
| 34 |
+
Matrix([
|
| 35 |
+
[0, 0],
|
| 36 |
+
[0, 2]])
|
| 37 |
+
|
| 38 |
+
A SparseMatrix can be instantiated from a ragged list of lists:
|
| 39 |
+
|
| 40 |
+
>>> SparseMatrix([[1, 2, 3], [1, 2], [1]])
|
| 41 |
+
Matrix([
|
| 42 |
+
[1, 2, 3],
|
| 43 |
+
[1, 2, 0],
|
| 44 |
+
[1, 0, 0]])
|
| 45 |
+
|
| 46 |
+
For safety, one may include the expected size and then an error
|
| 47 |
+
will be raised if the indices of any element are out of range or
|
| 48 |
+
(for a flat list) if the total number of elements does not match
|
| 49 |
+
the expected shape:
|
| 50 |
+
|
| 51 |
+
>>> SparseMatrix(2, 2, [1, 2])
|
| 52 |
+
Traceback (most recent call last):
|
| 53 |
+
...
|
| 54 |
+
ValueError: List length (2) != rows*columns (4)
|
| 55 |
+
|
| 56 |
+
Here, an error is not raised because the list is not flat and no
|
| 57 |
+
element is out of range:
|
| 58 |
+
|
| 59 |
+
>>> SparseMatrix(2, 2, [[1, 2]])
|
| 60 |
+
Matrix([
|
| 61 |
+
[1, 2],
|
| 62 |
+
[0, 0]])
|
| 63 |
+
|
| 64 |
+
But adding another element to the first (and only) row will cause
|
| 65 |
+
an error to be raised:
|
| 66 |
+
|
| 67 |
+
>>> SparseMatrix(2, 2, [[1, 2, 3]])
|
| 68 |
+
Traceback (most recent call last):
|
| 69 |
+
...
|
| 70 |
+
ValueError: The location (0, 2) is out of designated range: (1, 1)
|
| 71 |
+
|
| 72 |
+
To autosize the matrix, pass None for rows:
|
| 73 |
+
|
| 74 |
+
>>> SparseMatrix(None, [[1, 2, 3]])
|
| 75 |
+
Matrix([[1, 2, 3]])
|
| 76 |
+
>>> SparseMatrix(None, {(1, 1): 1, (3, 3): 3})
|
| 77 |
+
Matrix([
|
| 78 |
+
[0, 0, 0, 0],
|
| 79 |
+
[0, 1, 0, 0],
|
| 80 |
+
[0, 0, 0, 0],
|
| 81 |
+
[0, 0, 0, 3]])
|
| 82 |
+
|
| 83 |
+
Values that are themselves a Matrix are automatically expanded:
|
| 84 |
+
|
| 85 |
+
>>> SparseMatrix(4, 4, {(1, 1): ones(2)})
|
| 86 |
+
Matrix([
|
| 87 |
+
[0, 0, 0, 0],
|
| 88 |
+
[0, 1, 1, 0],
|
| 89 |
+
[0, 1, 1, 0],
|
| 90 |
+
[0, 0, 0, 0]])
|
| 91 |
+
|
| 92 |
+
A ValueError is raised if the expanding matrix tries to overwrite
|
| 93 |
+
a different element already present:
|
| 94 |
+
|
| 95 |
+
>>> SparseMatrix(3, 3, {(0, 0): ones(2), (1, 1): 2})
|
| 96 |
+
Traceback (most recent call last):
|
| 97 |
+
...
|
| 98 |
+
ValueError: collision at (1, 1)
|
| 99 |
+
|
| 100 |
+
See Also
|
| 101 |
+
========
|
| 102 |
+
DenseMatrix
|
| 103 |
+
MutableSparseMatrix
|
| 104 |
+
ImmutableSparseMatrix
|
| 105 |
+
"""
|
| 106 |
+
|
| 107 |
+
@classmethod
|
| 108 |
+
def _handle_creation_inputs(cls, *args, **kwargs):
|
| 109 |
+
if len(args) == 1 and isinstance(args[0], MatrixBase):
|
| 110 |
+
rows = args[0].rows
|
| 111 |
+
cols = args[0].cols
|
| 112 |
+
smat = args[0].todok()
|
| 113 |
+
return rows, cols, smat
|
| 114 |
+
|
| 115 |
+
smat = {}
|
| 116 |
+
# autosizing
|
| 117 |
+
if len(args) == 2 and args[0] is None:
|
| 118 |
+
args = [None, None, args[1]]
|
| 119 |
+
|
| 120 |
+
if len(args) == 3:
|
| 121 |
+
r, c = args[:2]
|
| 122 |
+
if r is c is None:
|
| 123 |
+
rows = cols = None
|
| 124 |
+
elif None in (r, c):
|
| 125 |
+
raise ValueError(
|
| 126 |
+
'Pass rows=None and no cols for autosizing.')
|
| 127 |
+
else:
|
| 128 |
+
rows, cols = as_int(args[0]), as_int(args[1])
|
| 129 |
+
|
| 130 |
+
if isinstance(args[2], Callable):
|
| 131 |
+
op = args[2]
|
| 132 |
+
|
| 133 |
+
if None in (rows, cols):
|
| 134 |
+
raise ValueError(
|
| 135 |
+
"{} and {} must be integers for this "
|
| 136 |
+
"specification.".format(rows, cols))
|
| 137 |
+
|
| 138 |
+
row_indices = [cls._sympify(i) for i in range(rows)]
|
| 139 |
+
col_indices = [cls._sympify(j) for j in range(cols)]
|
| 140 |
+
|
| 141 |
+
for i in row_indices:
|
| 142 |
+
for j in col_indices:
|
| 143 |
+
value = cls._sympify(op(i, j))
|
| 144 |
+
if value != cls.zero:
|
| 145 |
+
smat[i, j] = value
|
| 146 |
+
|
| 147 |
+
return rows, cols, smat
|
| 148 |
+
|
| 149 |
+
elif isinstance(args[2], (dict, Dict)):
|
| 150 |
+
def update(i, j, v):
|
| 151 |
+
# update smat and make sure there are no collisions
|
| 152 |
+
if v:
|
| 153 |
+
if (i, j) in smat and v != smat[i, j]:
|
| 154 |
+
raise ValueError(
|
| 155 |
+
"There is a collision at {} for {} and {}."
|
| 156 |
+
.format((i, j), v, smat[i, j])
|
| 157 |
+
)
|
| 158 |
+
smat[i, j] = v
|
| 159 |
+
|
| 160 |
+
# manual copy, copy.deepcopy() doesn't work
|
| 161 |
+
for (r, c), v in args[2].items():
|
| 162 |
+
if isinstance(v, MatrixBase):
|
| 163 |
+
for (i, j), vv in v.todok().items():
|
| 164 |
+
update(r + i, c + j, vv)
|
| 165 |
+
elif isinstance(v, (list, tuple)):
|
| 166 |
+
_, _, smat = cls._handle_creation_inputs(v, **kwargs)
|
| 167 |
+
for i, j in smat:
|
| 168 |
+
update(r + i, c + j, smat[i, j])
|
| 169 |
+
else:
|
| 170 |
+
v = cls._sympify(v)
|
| 171 |
+
update(r, c, cls._sympify(v))
|
| 172 |
+
|
| 173 |
+
elif is_sequence(args[2]):
|
| 174 |
+
flat = not any(is_sequence(i) for i in args[2])
|
| 175 |
+
if not flat:
|
| 176 |
+
_, _, smat = \
|
| 177 |
+
cls._handle_creation_inputs(args[2], **kwargs)
|
| 178 |
+
else:
|
| 179 |
+
flat_list = args[2]
|
| 180 |
+
if len(flat_list) != rows * cols:
|
| 181 |
+
raise ValueError(
|
| 182 |
+
"The length of the flat list ({}) does not "
|
| 183 |
+
"match the specified size ({} * {})."
|
| 184 |
+
.format(len(flat_list), rows, cols)
|
| 185 |
+
)
|
| 186 |
+
|
| 187 |
+
for i in range(rows):
|
| 188 |
+
for j in range(cols):
|
| 189 |
+
value = flat_list[i*cols + j]
|
| 190 |
+
value = cls._sympify(value)
|
| 191 |
+
if value != cls.zero:
|
| 192 |
+
smat[i, j] = value
|
| 193 |
+
|
| 194 |
+
if rows is None: # autosizing
|
| 195 |
+
keys = smat.keys()
|
| 196 |
+
rows = max(r for r, _ in keys) + 1 if keys else 0
|
| 197 |
+
cols = max(c for _, c in keys) + 1 if keys else 0
|
| 198 |
+
|
| 199 |
+
else:
|
| 200 |
+
for i, j in smat.keys():
|
| 201 |
+
if i and i >= rows or j and j >= cols:
|
| 202 |
+
raise ValueError(
|
| 203 |
+
"The location {} is out of the designated range"
|
| 204 |
+
"[{}, {}]x[{}, {}]"
|
| 205 |
+
.format((i, j), 0, rows - 1, 0, cols - 1)
|
| 206 |
+
)
|
| 207 |
+
|
| 208 |
+
return rows, cols, smat
|
| 209 |
+
|
| 210 |
+
elif len(args) == 1 and isinstance(args[0], (list, tuple)):
|
| 211 |
+
# list of values or lists
|
| 212 |
+
v = args[0]
|
| 213 |
+
c = 0
|
| 214 |
+
for i, row in enumerate(v):
|
| 215 |
+
if not isinstance(row, (list, tuple)):
|
| 216 |
+
row = [row]
|
| 217 |
+
for j, vv in enumerate(row):
|
| 218 |
+
if vv != cls.zero:
|
| 219 |
+
smat[i, j] = cls._sympify(vv)
|
| 220 |
+
c = max(c, len(row))
|
| 221 |
+
rows = len(v) if c else 0
|
| 222 |
+
cols = c
|
| 223 |
+
return rows, cols, smat
|
| 224 |
+
|
| 225 |
+
else:
|
| 226 |
+
# handle full matrix forms with _handle_creation_inputs
|
| 227 |
+
rows, cols, mat = super()._handle_creation_inputs(*args)
|
| 228 |
+
for i in range(rows):
|
| 229 |
+
for j in range(cols):
|
| 230 |
+
value = mat[cols*i + j]
|
| 231 |
+
if value != cls.zero:
|
| 232 |
+
smat[i, j] = value
|
| 233 |
+
|
| 234 |
+
return rows, cols, smat
|
| 235 |
+
|
| 236 |
+
@property
|
| 237 |
+
def _smat(self):
|
| 238 |
+
|
| 239 |
+
sympy_deprecation_warning(
|
| 240 |
+
"""
|
| 241 |
+
The private _smat attribute of SparseMatrix is deprecated. Use the
|
| 242 |
+
.todok() method instead.
|
| 243 |
+
""",
|
| 244 |
+
deprecated_since_version="1.9",
|
| 245 |
+
active_deprecations_target="deprecated-private-matrix-attributes"
|
| 246 |
+
)
|
| 247 |
+
|
| 248 |
+
return self.todok()
|
| 249 |
+
|
| 250 |
+
def _eval_inverse(self, **kwargs):
|
| 251 |
+
return self.inv(method=kwargs.get('method', 'LDL'),
|
| 252 |
+
iszerofunc=kwargs.get('iszerofunc', _iszero),
|
| 253 |
+
try_block_diag=kwargs.get('try_block_diag', False))
|
| 254 |
+
|
| 255 |
+
def applyfunc(self, f):
|
| 256 |
+
"""Apply a function to each element of the matrix.
|
| 257 |
+
|
| 258 |
+
Examples
|
| 259 |
+
========
|
| 260 |
+
|
| 261 |
+
>>> from sympy import SparseMatrix
|
| 262 |
+
>>> m = SparseMatrix(2, 2, lambda i, j: i*2+j)
|
| 263 |
+
>>> m
|
| 264 |
+
Matrix([
|
| 265 |
+
[0, 1],
|
| 266 |
+
[2, 3]])
|
| 267 |
+
>>> m.applyfunc(lambda i: 2*i)
|
| 268 |
+
Matrix([
|
| 269 |
+
[0, 2],
|
| 270 |
+
[4, 6]])
|
| 271 |
+
|
| 272 |
+
"""
|
| 273 |
+
if not callable(f):
|
| 274 |
+
raise TypeError("`f` must be callable.")
|
| 275 |
+
|
| 276 |
+
# XXX: This only applies the function to the nonzero elements of the
|
| 277 |
+
# matrix so is inconsistent with DenseMatrix.applyfunc e.g.
|
| 278 |
+
# zeros(2, 2).applyfunc(lambda x: x + 1)
|
| 279 |
+
dok = {}
|
| 280 |
+
for k, v in self.todok().items():
|
| 281 |
+
fv = f(v)
|
| 282 |
+
if fv != 0:
|
| 283 |
+
dok[k] = fv
|
| 284 |
+
|
| 285 |
+
return self._new(self.rows, self.cols, dok)
|
| 286 |
+
|
| 287 |
+
def as_immutable(self):
|
| 288 |
+
"""Returns an Immutable version of this Matrix."""
|
| 289 |
+
from .immutable import ImmutableSparseMatrix
|
| 290 |
+
return ImmutableSparseMatrix(self)
|
| 291 |
+
|
| 292 |
+
def as_mutable(self):
|
| 293 |
+
"""Returns a mutable version of this matrix.
|
| 294 |
+
|
| 295 |
+
Examples
|
| 296 |
+
========
|
| 297 |
+
|
| 298 |
+
>>> from sympy import ImmutableMatrix
|
| 299 |
+
>>> X = ImmutableMatrix([[1, 2], [3, 4]])
|
| 300 |
+
>>> Y = X.as_mutable()
|
| 301 |
+
>>> Y[1, 1] = 5 # Can set values in Y
|
| 302 |
+
>>> Y
|
| 303 |
+
Matrix([
|
| 304 |
+
[1, 2],
|
| 305 |
+
[3, 5]])
|
| 306 |
+
"""
|
| 307 |
+
return MutableSparseMatrix(self)
|
| 308 |
+
|
| 309 |
+
def col_list(self):
|
| 310 |
+
"""Returns a column-sorted list of non-zero elements of the matrix.
|
| 311 |
+
|
| 312 |
+
Examples
|
| 313 |
+
========
|
| 314 |
+
|
| 315 |
+
>>> from sympy import SparseMatrix
|
| 316 |
+
>>> a=SparseMatrix(((1, 2), (3, 4)))
|
| 317 |
+
>>> a
|
| 318 |
+
Matrix([
|
| 319 |
+
[1, 2],
|
| 320 |
+
[3, 4]])
|
| 321 |
+
>>> a.CL
|
| 322 |
+
[(0, 0, 1), (1, 0, 3), (0, 1, 2), (1, 1, 4)]
|
| 323 |
+
|
| 324 |
+
See Also
|
| 325 |
+
========
|
| 326 |
+
|
| 327 |
+
sympy.matrices.sparse.SparseMatrix.row_list
|
| 328 |
+
"""
|
| 329 |
+
return [tuple(k + (self[k],)) for k in sorted(self.todok().keys(), key=lambda k: list(reversed(k)))]
|
| 330 |
+
|
| 331 |
+
def nnz(self):
|
| 332 |
+
"""Returns the number of non-zero elements in Matrix."""
|
| 333 |
+
return len(self.todok())
|
| 334 |
+
|
| 335 |
+
def row_list(self):
|
| 336 |
+
"""Returns a row-sorted list of non-zero elements of the matrix.
|
| 337 |
+
|
| 338 |
+
Examples
|
| 339 |
+
========
|
| 340 |
+
|
| 341 |
+
>>> from sympy import SparseMatrix
|
| 342 |
+
>>> a = SparseMatrix(((1, 2), (3, 4)))
|
| 343 |
+
>>> a
|
| 344 |
+
Matrix([
|
| 345 |
+
[1, 2],
|
| 346 |
+
[3, 4]])
|
| 347 |
+
>>> a.RL
|
| 348 |
+
[(0, 0, 1), (0, 1, 2), (1, 0, 3), (1, 1, 4)]
|
| 349 |
+
|
| 350 |
+
See Also
|
| 351 |
+
========
|
| 352 |
+
|
| 353 |
+
sympy.matrices.sparse.SparseMatrix.col_list
|
| 354 |
+
"""
|
| 355 |
+
return [tuple(k + (self[k],)) for k in
|
| 356 |
+
sorted(self.todok().keys(), key=list)]
|
| 357 |
+
|
| 358 |
+
def scalar_multiply(self, scalar):
|
| 359 |
+
"Scalar element-wise multiplication"
|
| 360 |
+
return scalar * self
|
| 361 |
+
|
| 362 |
+
def solve_least_squares(self, rhs, method='LDL'):
|
| 363 |
+
"""Return the least-square fit to the data.
|
| 364 |
+
|
| 365 |
+
By default the cholesky_solve routine is used (method='CH'); other
|
| 366 |
+
methods of matrix inversion can be used. To find out which are
|
| 367 |
+
available, see the docstring of the .inv() method.
|
| 368 |
+
|
| 369 |
+
Examples
|
| 370 |
+
========
|
| 371 |
+
|
| 372 |
+
>>> from sympy import SparseMatrix, Matrix, ones
|
| 373 |
+
>>> A = Matrix([1, 2, 3])
|
| 374 |
+
>>> B = Matrix([2, 3, 4])
|
| 375 |
+
>>> S = SparseMatrix(A.row_join(B))
|
| 376 |
+
>>> S
|
| 377 |
+
Matrix([
|
| 378 |
+
[1, 2],
|
| 379 |
+
[2, 3],
|
| 380 |
+
[3, 4]])
|
| 381 |
+
|
| 382 |
+
If each line of S represent coefficients of Ax + By
|
| 383 |
+
and x and y are [2, 3] then S*xy is:
|
| 384 |
+
|
| 385 |
+
>>> r = S*Matrix([2, 3]); r
|
| 386 |
+
Matrix([
|
| 387 |
+
[ 8],
|
| 388 |
+
[13],
|
| 389 |
+
[18]])
|
| 390 |
+
|
| 391 |
+
But let's add 1 to the middle value and then solve for the
|
| 392 |
+
least-squares value of xy:
|
| 393 |
+
|
| 394 |
+
>>> xy = S.solve_least_squares(Matrix([8, 14, 18])); xy
|
| 395 |
+
Matrix([
|
| 396 |
+
[ 5/3],
|
| 397 |
+
[10/3]])
|
| 398 |
+
|
| 399 |
+
The error is given by S*xy - r:
|
| 400 |
+
|
| 401 |
+
>>> S*xy - r
|
| 402 |
+
Matrix([
|
| 403 |
+
[1/3],
|
| 404 |
+
[1/3],
|
| 405 |
+
[1/3]])
|
| 406 |
+
>>> _.norm().n(2)
|
| 407 |
+
0.58
|
| 408 |
+
|
| 409 |
+
If a different xy is used, the norm will be higher:
|
| 410 |
+
|
| 411 |
+
>>> xy += ones(2, 1)/10
|
| 412 |
+
>>> (S*xy - r).norm().n(2)
|
| 413 |
+
1.5
|
| 414 |
+
|
| 415 |
+
"""
|
| 416 |
+
t = self.T
|
| 417 |
+
return (t*self).inv(method=method)*t*rhs
|
| 418 |
+
|
| 419 |
+
def solve(self, rhs, method='LDL'):
|
| 420 |
+
"""Return solution to self*soln = rhs using given inversion method.
|
| 421 |
+
|
| 422 |
+
For a list of possible inversion methods, see the .inv() docstring.
|
| 423 |
+
"""
|
| 424 |
+
if not self.is_square:
|
| 425 |
+
if self.rows < self.cols:
|
| 426 |
+
raise ValueError('Under-determined system.')
|
| 427 |
+
elif self.rows > self.cols:
|
| 428 |
+
raise ValueError('For over-determined system, M, having '
|
| 429 |
+
'more rows than columns, try M.solve_least_squares(rhs).')
|
| 430 |
+
else:
|
| 431 |
+
return self.inv(method=method).multiply(rhs)
|
| 432 |
+
|
| 433 |
+
RL = property(row_list, None, None, "Alternate faster representation")
|
| 434 |
+
CL = property(col_list, None, None, "Alternate faster representation")
|
| 435 |
+
|
| 436 |
+
def liupc(self):
|
| 437 |
+
return _liupc(self)
|
| 438 |
+
|
| 439 |
+
def row_structure_symbolic_cholesky(self):
|
| 440 |
+
return _row_structure_symbolic_cholesky(self)
|
| 441 |
+
|
| 442 |
+
def cholesky(self, hermitian=True):
|
| 443 |
+
return _cholesky_sparse(self, hermitian=hermitian)
|
| 444 |
+
|
| 445 |
+
def LDLdecomposition(self, hermitian=True):
|
| 446 |
+
return _LDLdecomposition_sparse(self, hermitian=hermitian)
|
| 447 |
+
|
| 448 |
+
def lower_triangular_solve(self, rhs):
|
| 449 |
+
return _lower_triangular_solve_sparse(self, rhs)
|
| 450 |
+
|
| 451 |
+
def upper_triangular_solve(self, rhs):
|
| 452 |
+
return _upper_triangular_solve_sparse(self, rhs)
|
| 453 |
+
|
| 454 |
+
liupc.__doc__ = _liupc.__doc__
|
| 455 |
+
row_structure_symbolic_cholesky.__doc__ = _row_structure_symbolic_cholesky.__doc__
|
| 456 |
+
cholesky.__doc__ = _cholesky_sparse.__doc__
|
| 457 |
+
LDLdecomposition.__doc__ = _LDLdecomposition_sparse.__doc__
|
| 458 |
+
lower_triangular_solve.__doc__ = lower_triangular_solve.__doc__
|
| 459 |
+
upper_triangular_solve.__doc__ = upper_triangular_solve.__doc__
|
| 460 |
+
|
| 461 |
+
|
| 462 |
+
class MutableSparseMatrix(SparseRepMatrix, MutableRepMatrix):
|
| 463 |
+
|
| 464 |
+
@classmethod
|
| 465 |
+
def _new(cls, *args, **kwargs):
|
| 466 |
+
rows, cols, smat = cls._handle_creation_inputs(*args, **kwargs)
|
| 467 |
+
|
| 468 |
+
rep = cls._smat_to_DomainMatrix(rows, cols, smat)
|
| 469 |
+
|
| 470 |
+
return cls._fromrep(rep)
|
| 471 |
+
|
| 472 |
+
|
| 473 |
+
SparseMatrix = MutableSparseMatrix
|
lib/python3.12/site-packages/sympy/matrices/subspaces.py
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .utilities import _iszero
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def _columnspace(M, simplify=False):
|
| 5 |
+
"""Returns a list of vectors (Matrix objects) that span columnspace of ``M``
|
| 6 |
+
|
| 7 |
+
Examples
|
| 8 |
+
========
|
| 9 |
+
|
| 10 |
+
>>> from sympy import Matrix
|
| 11 |
+
>>> M = Matrix(3, 3, [1, 3, 0, -2, -6, 0, 3, 9, 6])
|
| 12 |
+
>>> M
|
| 13 |
+
Matrix([
|
| 14 |
+
[ 1, 3, 0],
|
| 15 |
+
[-2, -6, 0],
|
| 16 |
+
[ 3, 9, 6]])
|
| 17 |
+
>>> M.columnspace()
|
| 18 |
+
[Matrix([
|
| 19 |
+
[ 1],
|
| 20 |
+
[-2],
|
| 21 |
+
[ 3]]), Matrix([
|
| 22 |
+
[0],
|
| 23 |
+
[0],
|
| 24 |
+
[6]])]
|
| 25 |
+
|
| 26 |
+
See Also
|
| 27 |
+
========
|
| 28 |
+
|
| 29 |
+
nullspace
|
| 30 |
+
rowspace
|
| 31 |
+
"""
|
| 32 |
+
|
| 33 |
+
reduced, pivots = M.echelon_form(simplify=simplify, with_pivots=True)
|
| 34 |
+
|
| 35 |
+
return [M.col(i) for i in pivots]
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def _nullspace(M, simplify=False, iszerofunc=_iszero):
|
| 39 |
+
"""Returns list of vectors (Matrix objects) that span nullspace of ``M``
|
| 40 |
+
|
| 41 |
+
Examples
|
| 42 |
+
========
|
| 43 |
+
|
| 44 |
+
>>> from sympy import Matrix
|
| 45 |
+
>>> M = Matrix(3, 3, [1, 3, 0, -2, -6, 0, 3, 9, 6])
|
| 46 |
+
>>> M
|
| 47 |
+
Matrix([
|
| 48 |
+
[ 1, 3, 0],
|
| 49 |
+
[-2, -6, 0],
|
| 50 |
+
[ 3, 9, 6]])
|
| 51 |
+
>>> M.nullspace()
|
| 52 |
+
[Matrix([
|
| 53 |
+
[-3],
|
| 54 |
+
[ 1],
|
| 55 |
+
[ 0]])]
|
| 56 |
+
|
| 57 |
+
See Also
|
| 58 |
+
========
|
| 59 |
+
|
| 60 |
+
columnspace
|
| 61 |
+
rowspace
|
| 62 |
+
"""
|
| 63 |
+
|
| 64 |
+
reduced, pivots = M.rref(iszerofunc=iszerofunc, simplify=simplify)
|
| 65 |
+
|
| 66 |
+
free_vars = [i for i in range(M.cols) if i not in pivots]
|
| 67 |
+
basis = []
|
| 68 |
+
|
| 69 |
+
for free_var in free_vars:
|
| 70 |
+
# for each free variable, we will set it to 1 and all others
|
| 71 |
+
# to 0. Then, we will use back substitution to solve the system
|
| 72 |
+
vec = [M.zero] * M.cols
|
| 73 |
+
vec[free_var] = M.one
|
| 74 |
+
|
| 75 |
+
for piv_row, piv_col in enumerate(pivots):
|
| 76 |
+
vec[piv_col] -= reduced[piv_row, free_var]
|
| 77 |
+
|
| 78 |
+
basis.append(vec)
|
| 79 |
+
|
| 80 |
+
return [M._new(M.cols, 1, b) for b in basis]
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
def _rowspace(M, simplify=False):
|
| 84 |
+
"""Returns a list of vectors that span the row space of ``M``.
|
| 85 |
+
|
| 86 |
+
Examples
|
| 87 |
+
========
|
| 88 |
+
|
| 89 |
+
>>> from sympy import Matrix
|
| 90 |
+
>>> M = Matrix(3, 3, [1, 3, 0, -2, -6, 0, 3, 9, 6])
|
| 91 |
+
>>> M
|
| 92 |
+
Matrix([
|
| 93 |
+
[ 1, 3, 0],
|
| 94 |
+
[-2, -6, 0],
|
| 95 |
+
[ 3, 9, 6]])
|
| 96 |
+
>>> M.rowspace()
|
| 97 |
+
[Matrix([[1, 3, 0]]), Matrix([[0, 0, 6]])]
|
| 98 |
+
"""
|
| 99 |
+
|
| 100 |
+
reduced, pivots = M.echelon_form(simplify=simplify, with_pivots=True)
|
| 101 |
+
|
| 102 |
+
return [reduced.row(i) for i in range(len(pivots))]
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
def _orthogonalize(cls, *vecs, normalize=False, rankcheck=False):
|
| 106 |
+
"""Apply the Gram-Schmidt orthogonalization procedure
|
| 107 |
+
to vectors supplied in ``vecs``.
|
| 108 |
+
|
| 109 |
+
Parameters
|
| 110 |
+
==========
|
| 111 |
+
|
| 112 |
+
vecs
|
| 113 |
+
vectors to be made orthogonal
|
| 114 |
+
|
| 115 |
+
normalize : bool
|
| 116 |
+
If ``True``, return an orthonormal basis.
|
| 117 |
+
|
| 118 |
+
rankcheck : bool
|
| 119 |
+
If ``True``, the computation does not stop when encountering
|
| 120 |
+
linearly dependent vectors.
|
| 121 |
+
|
| 122 |
+
If ``False``, it will raise ``ValueError`` when any zero
|
| 123 |
+
or linearly dependent vectors are found.
|
| 124 |
+
|
| 125 |
+
Returns
|
| 126 |
+
=======
|
| 127 |
+
|
| 128 |
+
list
|
| 129 |
+
List of orthogonal (or orthonormal) basis vectors.
|
| 130 |
+
|
| 131 |
+
Examples
|
| 132 |
+
========
|
| 133 |
+
|
| 134 |
+
>>> from sympy import I, Matrix
|
| 135 |
+
>>> v = [Matrix([1, I]), Matrix([1, -I])]
|
| 136 |
+
>>> Matrix.orthogonalize(*v)
|
| 137 |
+
[Matrix([
|
| 138 |
+
[1],
|
| 139 |
+
[I]]), Matrix([
|
| 140 |
+
[ 1],
|
| 141 |
+
[-I]])]
|
| 142 |
+
|
| 143 |
+
See Also
|
| 144 |
+
========
|
| 145 |
+
|
| 146 |
+
MatrixBase.QRdecomposition
|
| 147 |
+
|
| 148 |
+
References
|
| 149 |
+
==========
|
| 150 |
+
|
| 151 |
+
.. [1] https://en.wikipedia.org/wiki/Gram%E2%80%93Schmidt_process
|
| 152 |
+
"""
|
| 153 |
+
from .decompositions import _QRdecomposition_optional
|
| 154 |
+
|
| 155 |
+
if not vecs:
|
| 156 |
+
return []
|
| 157 |
+
|
| 158 |
+
all_row_vecs = (vecs[0].rows == 1)
|
| 159 |
+
|
| 160 |
+
vecs = [x.vec() for x in vecs]
|
| 161 |
+
M = cls.hstack(*vecs)
|
| 162 |
+
Q, R = _QRdecomposition_optional(M, normalize=normalize)
|
| 163 |
+
|
| 164 |
+
if rankcheck and Q.cols < len(vecs):
|
| 165 |
+
raise ValueError("GramSchmidt: vector set not linearly independent")
|
| 166 |
+
|
| 167 |
+
ret = []
|
| 168 |
+
for i in range(Q.cols):
|
| 169 |
+
if all_row_vecs:
|
| 170 |
+
col = cls(Q[:, i].T)
|
| 171 |
+
else:
|
| 172 |
+
col = cls(Q[:, i])
|
| 173 |
+
ret.append(col)
|
| 174 |
+
return ret
|
lib/python3.12/site-packages/sympy/polys/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (5.57 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/appellseqs.cpython-312.pyc
ADDED
|
Binary file (10.4 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/constructor.cpython-312.pyc
ADDED
|
Binary file (13.9 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/densearith.cpython-312.pyc
ADDED
|
Binary file (45.1 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/densebasic.cpython-312.pyc
ADDED
|
Binary file (50.5 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/densetools.cpython-312.pyc
ADDED
|
Binary file (37.2 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/dispersion.cpython-312.pyc
ADDED
|
Binary file (6.24 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/distributedmodules.cpython-312.pyc
ADDED
|
Binary file (27.8 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/domainmatrix.cpython-312.pyc
ADDED
|
Binary file (532 Bytes). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/euclidtools.cpython-312.pyc
ADDED
|
Binary file (54.1 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/factortools.cpython-312.pyc
ADDED
|
Binary file (57.2 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/fglmtools.cpython-312.pyc
ADDED
|
Binary file (9.65 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/fields.cpython-312.pyc
ADDED
|
Binary file (36 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/galoistools.cpython-312.pyc
ADDED
|
Binary file (74.8 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/groebnertools.cpython-312.pyc
ADDED
|
Binary file (33.5 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/heuristicgcd.cpython-312.pyc
ADDED
|
Binary file (5 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/modulargcd.cpython-312.pyc
ADDED
|
Binary file (73.8 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/monomials.cpython-312.pyc
ADDED
|
Binary file (26.3 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/multivariate_resultants.cpython-312.pyc
ADDED
|
Binary file (21.2 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/orderings.cpython-312.pyc
ADDED
|
Binary file (14.8 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/orthopolys.cpython-312.pyc
ADDED
|
Binary file (15.5 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/partfrac.cpython-312.pyc
ADDED
|
Binary file (17.9 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/polyconfig.cpython-312.pyc
ADDED
|
Binary file (2.24 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/polyerrors.cpython-312.pyc
ADDED
|
Binary file (9.76 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/polyfuncs.cpython-312.pyc
ADDED
|
Binary file (11.9 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/polymatrix.cpython-312.pyc
ADDED
|
Binary file (17.4 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/polyoptions.cpython-312.pyc
ADDED
|
Binary file (32.8 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/polyroots.cpython-312.pyc
ADDED
|
Binary file (49.6 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/polyutils.cpython-312.pyc
ADDED
|
Binary file (24.4 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/rationaltools.cpython-312.pyc
ADDED
|
Binary file (3.63 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/ring_series.cpython-312.pyc
ADDED
|
Binary file (72.4 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/rootisolation.cpython-312.pyc
ADDED
|
Binary file (80.3 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/rootoftools.cpython-312.pyc
ADDED
|
Binary file (52.5 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/solvers.cpython-312.pyc
ADDED
|
Binary file (16.8 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/specialpolys.cpython-312.pyc
ADDED
|
Binary file (29.9 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/sqfreetools.cpython-312.pyc
ADDED
|
Binary file (24.2 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/__pycache__/subresultants_qq_zz.cpython-312.pyc
ADDED
|
Binary file (90.4 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/agca/__init__.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Module for algebraic geometry and commutative algebra."""
|
| 2 |
+
|
| 3 |
+
from .homomorphisms import homomorphism
|
| 4 |
+
|
| 5 |
+
__all__ = ['homomorphism']
|
lib/python3.12/site-packages/sympy/polys/agca/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (348 Bytes). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/agca/__pycache__/extensions.cpython-312.pyc
ADDED
|
Binary file (16.7 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/polys/agca/__pycache__/homomorphisms.cpython-312.pyc
ADDED
|
Binary file (31.7 kB). View file
|
|
|