Buckets:
MisterAI/LocalAI_Demo_backends / cpu-diffusers.upgrade-tmp /venv /lib /python3.10 /site-packages /sympy /matrices /determinant.py
| from types import FunctionType | |
| from sympy.core.cache import cacheit | |
| from sympy.core.numbers import Float, Integer | |
| from sympy.core.singleton import S | |
| from sympy.core.symbol import uniquely_named_symbol | |
| from sympy.core.mul import Mul | |
| from sympy.polys import PurePoly, cancel | |
| from sympy.functions.combinatorial.numbers import nC | |
| from sympy.polys.matrices.domainmatrix import DomainMatrix | |
| from sympy.polys.matrices.ddm import DDM | |
| from .exceptions import NonSquareMatrixError | |
| from .utilities import ( | |
| _get_intermediate_simp, _get_intermediate_simp_bool, | |
| _iszero, _is_zero_after_expand_mul, _dotprodsimp, _simplify) | |
| def _find_reasonable_pivot(col, iszerofunc=_iszero, simpfunc=_simplify): | |
| """ Find the lowest index of an item in ``col`` that is | |
| suitable for a pivot. If ``col`` consists only of | |
| Floats, the pivot with the largest norm is returned. | |
| Otherwise, the first element where ``iszerofunc`` returns | |
| False is used. If ``iszerofunc`` does not return false, | |
| items are simplified and retested until a suitable | |
| pivot is found. | |
| Returns a 4-tuple | |
| (pivot_offset, pivot_val, assumed_nonzero, newly_determined) | |
| where pivot_offset is the index of the pivot, pivot_val is | |
| the (possibly simplified) value of the pivot, assumed_nonzero | |
| is True if an assumption that the pivot was non-zero | |
| was made without being proved, and newly_determined are | |
| elements that were simplified during the process of pivot | |
| finding.""" | |
| newly_determined = [] | |
| col = list(col) | |
| # a column that contains a mix of floats and integers | |
| # but at least one float is considered a numerical | |
| # column, and so we do partial pivoting | |
| if all(isinstance(x, (Float, Integer)) for x in col) and any( | |
| isinstance(x, Float) for x in col): | |
| col_abs = [abs(x) for x in col] | |
| max_value = max(col_abs) | |
| if iszerofunc(max_value): | |
| # just because iszerofunc returned True, doesn't | |
| # mean the value is numerically zero. Make sure | |
| # to replace all entries with numerical zeros | |
| if max_value != 0: | |
| newly_determined = [(i, 0) for i, x in enumerate(col) if x != 0] | |
| return (None, None, False, newly_determined) | |
| index = col_abs.index(max_value) | |
| return (index, col[index], False, newly_determined) | |
| # PASS 1 (iszerofunc directly) | |
| possible_zeros = [] | |
| for i, x in enumerate(col): | |
| is_zero = iszerofunc(x) | |
| # is someone wrote a custom iszerofunc, it may return | |
| # BooleanFalse or BooleanTrue instead of True or False, | |
| # so use == for comparison instead of `is` | |
| if is_zero == False: | |
| # we found something that is definitely not zero | |
| return (i, x, False, newly_determined) | |
| possible_zeros.append(is_zero) | |
| # by this point, we've found no certain non-zeros | |
| if all(possible_zeros): | |
| # if everything is definitely zero, we have | |
| # no pivot | |
| return (None, None, False, newly_determined) | |
| # PASS 2 (iszerofunc after simplify) | |
| # we haven't found any for-sure non-zeros, so | |
| # go through the elements iszerofunc couldn't | |
| # make a determination about and opportunistically | |
| # simplify to see if we find something | |
| for i, x in enumerate(col): | |
| if possible_zeros[i] is not None: | |
| continue | |
| simped = simpfunc(x) | |
| is_zero = iszerofunc(simped) | |
| if is_zero in (True, False): | |
| newly_determined.append((i, simped)) | |
| if is_zero == False: | |
| return (i, simped, False, newly_determined) | |
| possible_zeros[i] = is_zero | |
| # after simplifying, some things that were recognized | |
| # as zeros might be zeros | |
| if all(possible_zeros): | |
| # if everything is definitely zero, we have | |
| # no pivot | |
| return (None, None, False, newly_determined) | |
| # PASS 3 (.equals(0)) | |
| # some expressions fail to simplify to zero, but | |
| # ``.equals(0)`` evaluates to True. As a last-ditch | |
| # attempt, apply ``.equals`` to these expressions | |
| for i, x in enumerate(col): | |
| if possible_zeros[i] is not None: | |
| continue | |
| if x.equals(S.Zero): | |
| # ``.iszero`` may return False with | |
| # an implicit assumption (e.g., ``x.equals(0)`` | |
| # when ``x`` is a symbol), so only treat it | |
| # as proved when ``.equals(0)`` returns True | |
| possible_zeros[i] = True | |
| newly_determined.append((i, S.Zero)) | |
| if all(possible_zeros): | |
| return (None, None, False, newly_determined) | |
| # at this point there is nothing that could definitely | |
| # be a pivot. To maintain compatibility with existing | |
| # behavior, we'll assume that an illdetermined thing is | |
| # non-zero. We should probably raise a warning in this case | |
| i = possible_zeros.index(None) | |
| return (i, col[i], True, newly_determined) | |
| def _find_reasonable_pivot_naive(col, iszerofunc=_iszero, simpfunc=None): | |
| """ | |
| Helper that computes the pivot value and location from a | |
| sequence of contiguous matrix column elements. As a side effect | |
| of the pivot search, this function may simplify some of the elements | |
| of the input column. A list of these simplified entries and their | |
| indices are also returned. | |
| This function mimics the behavior of _find_reasonable_pivot(), | |
| but does less work trying to determine if an indeterminate candidate | |
| pivot simplifies to zero. This more naive approach can be much faster, | |
| with the trade-off that it may erroneously return a pivot that is zero. | |
| ``col`` is a sequence of contiguous column entries to be searched for | |
| a suitable pivot. | |
| ``iszerofunc`` is a callable that returns a Boolean that indicates | |
| if its input is zero, or None if no such determination can be made. | |
| ``simpfunc`` is a callable that simplifies its input. It must return | |
| its input if it does not simplify its input. Passing in | |
| ``simpfunc=None`` indicates that the pivot search should not attempt | |
| to simplify any candidate pivots. | |
| Returns a 4-tuple: | |
| (pivot_offset, pivot_val, assumed_nonzero, newly_determined) | |
| ``pivot_offset`` is the sequence index of the pivot. | |
| ``pivot_val`` is the value of the pivot. | |
| pivot_val and col[pivot_index] are equivalent, but will be different | |
| when col[pivot_index] was simplified during the pivot search. | |
| ``assumed_nonzero`` is a boolean indicating if the pivot cannot be | |
| guaranteed to be zero. If assumed_nonzero is true, then the pivot | |
| may or may not be non-zero. If assumed_nonzero is false, then | |
| the pivot is non-zero. | |
| ``newly_determined`` is a list of index-value pairs of pivot candidates | |
| that were simplified during the pivot search. | |
| """ | |
| # indeterminates holds the index-value pairs of each pivot candidate | |
| # that is neither zero or non-zero, as determined by iszerofunc(). | |
| # If iszerofunc() indicates that a candidate pivot is guaranteed | |
| # non-zero, or that every candidate pivot is zero then the contents | |
| # of indeterminates are unused. | |
| # Otherwise, the only viable candidate pivots are symbolic. | |
| # In this case, indeterminates will have at least one entry, | |
| # and all but the first entry are ignored when simpfunc is None. | |
| indeterminates = [] | |
| for i, col_val in enumerate(col): | |
| col_val_is_zero = iszerofunc(col_val) | |
| if col_val_is_zero == False: | |
| # This pivot candidate is non-zero. | |
| return i, col_val, False, [] | |
| elif col_val_is_zero is None: | |
| # The candidate pivot's comparison with zero | |
| # is indeterminate. | |
| indeterminates.append((i, col_val)) | |
| if len(indeterminates) == 0: | |
| # All candidate pivots are guaranteed to be zero, i.e. there is | |
| # no pivot. | |
| return None, None, False, [] | |
| if simpfunc is None: | |
| # Caller did not pass in a simplification function that might | |
| # determine if an indeterminate pivot candidate is guaranteed | |
| # to be nonzero, so assume the first indeterminate candidate | |
| # is non-zero. | |
| return indeterminates[0][0], indeterminates[0][1], True, [] | |
| # newly_determined holds index-value pairs of candidate pivots | |
| # that were simplified during the search for a non-zero pivot. | |
| newly_determined = [] | |
| for i, col_val in indeterminates: | |
| tmp_col_val = simpfunc(col_val) | |
| if id(col_val) != id(tmp_col_val): | |
| # simpfunc() simplified this candidate pivot. | |
| newly_determined.append((i, tmp_col_val)) | |
| if iszerofunc(tmp_col_val) == False: | |
| # Candidate pivot simplified to a guaranteed non-zero value. | |
| return i, tmp_col_val, False, newly_determined | |
| return indeterminates[0][0], indeterminates[0][1], True, newly_determined | |
| # This functions is a candidate for caching if it gets implemented for matrices. | |
| def _berkowitz_toeplitz_matrix(M): | |
| """Return (A,T) where T the Toeplitz matrix used in the Berkowitz algorithm | |
| corresponding to ``M`` and A is the first principal submatrix. | |
| """ | |
| # the 0 x 0 case is trivial | |
| if M.rows == 0 and M.cols == 0: | |
| return M._new(1,1, [M.one]) | |
| # | |
| # Partition M = [ a_11 R ] | |
| # [ C A ] | |
| # | |
| a, R = M[0,0], M[0, 1:] | |
| C, A = M[1:, 0], M[1:,1:] | |
| # | |
| # The Toeplitz matrix looks like | |
| # | |
| # [ 1 ] | |
| # [ -a 1 ] | |
| # [ -RC -a 1 ] | |
| # [ -RAC -RC -a 1 ] | |
| # [ -RA**2C -RAC -RC -a 1 ] | |
| # etc. | |
| # Compute the diagonal entries. | |
| # Because multiplying matrix times vector is so much | |
| # more efficient than matrix times matrix, recursively | |
| # compute -R * A**n * C. | |
| diags = [C] | |
| for i in range(M.rows - 2): | |
| diags.append(A.multiply(diags[i], dotprodsimp=None)) | |
| diags = [(-R).multiply(d, dotprodsimp=None)[0, 0] for d in diags] | |
| diags = [M.one, -a] + diags | |
| def entry(i,j): | |
| if j > i: | |
| return M.zero | |
| return diags[i - j] | |
| toeplitz = M._new(M.cols + 1, M.rows, entry) | |
| return (A, toeplitz) | |
| # This functions is a candidate for caching if it gets implemented for matrices. | |
| def _berkowitz_vector(M): | |
| """ Run the Berkowitz algorithm and return a vector whose entries | |
| are the coefficients of the characteristic polynomial of ``M``. | |
| Given N x N matrix, efficiently compute | |
| coefficients of characteristic polynomials of ``M`` | |
| without division in the ground domain. | |
| This method is particularly useful for computing determinant, | |
| principal minors and characteristic polynomial when ``M`` | |
| has complicated coefficients e.g. polynomials. Semi-direct | |
| usage of this algorithm is also important in computing | |
| efficiently sub-resultant PRS. | |
| Assuming that M is a square matrix of dimension N x N and | |
| I is N x N identity matrix, then the Berkowitz vector is | |
| an N x 1 vector whose entries are coefficients of the | |
| polynomial | |
| charpoly(M) = det(t*I - M) | |
| As a consequence, all polynomials generated by Berkowitz | |
| algorithm are monic. | |
| For more information on the implemented algorithm refer to: | |
| [1] S.J. Berkowitz, On computing the determinant in small | |
| parallel time using a small number of processors, ACM, | |
| Information Processing Letters 18, 1984, pp. 147-150 | |
| [2] M. Keber, Division-Free computation of sub-resultants | |
| using Bezout matrices, Tech. Report MPI-I-2006-1-006, | |
| Saarbrucken, 2006 | |
| """ | |
| # handle the trivial cases | |
| if M.rows == 0 and M.cols == 0: | |
| return M._new(1, 1, [M.one]) | |
| elif M.rows == 1 and M.cols == 1: | |
| return M._new(2, 1, [M.one, -M[0,0]]) | |
| submat, toeplitz = _berkowitz_toeplitz_matrix(M) | |
| return toeplitz.multiply(_berkowitz_vector(submat), dotprodsimp=None) | |
| def _adjugate(M, method="berkowitz"): | |
| """Returns the adjugate, or classical adjoint, of | |
| a matrix. That is, the transpose of the matrix of cofactors. | |
| https://en.wikipedia.org/wiki/Adjugate | |
| Parameters | |
| ========== | |
| method : string, optional | |
| Method to use to find the cofactors, can be "bareiss", "berkowitz", | |
| "bird", "laplace" or "lu". | |
| Examples | |
| ======== | |
| >>> from sympy import Matrix | |
| >>> M = Matrix([[1, 2], [3, 4]]) | |
| >>> M.adjugate() | |
| Matrix([ | |
| [ 4, -2], | |
| [-3, 1]]) | |
| See Also | |
| ======== | |
| cofactor_matrix | |
| sympy.matrices.matrixbase.MatrixBase.transpose | |
| """ | |
| return M.cofactor_matrix(method=method).transpose() | |
| # This functions is a candidate for caching if it gets implemented for matrices. | |
| def _charpoly(M, x='lambda', simplify=_simplify): | |
| """Computes characteristic polynomial det(x*I - M) where I is | |
| the identity matrix. | |
| A PurePoly is returned, so using different variables for ``x`` does | |
| not affect the comparison or the polynomials: | |
| Parameters | |
| ========== | |
| x : string, optional | |
| Name for the "lambda" variable, defaults to "lambda". | |
| simplify : function, optional | |
| Simplification function to use on the characteristic polynomial | |
| calculated. Defaults to ``simplify``. | |
| Examples | |
| ======== | |
| >>> from sympy import Matrix | |
| >>> from sympy.abc import x, y | |
| >>> M = Matrix([[1, 3], [2, 0]]) | |
| >>> M.charpoly() | |
| PurePoly(lambda**2 - lambda - 6, lambda, domain='ZZ') | |
| >>> M.charpoly(x) == M.charpoly(y) | |
| True | |
| >>> M.charpoly(x) == M.charpoly(y) | |
| True | |
| Specifying ``x`` is optional; a symbol named ``lambda`` is used by | |
| default (which looks good when pretty-printed in unicode): | |
| >>> M.charpoly().as_expr() | |
| lambda**2 - lambda - 6 | |
| And if ``x`` clashes with an existing symbol, underscores will | |
| be prepended to the name to make it unique: | |
| >>> M = Matrix([[1, 2], [x, 0]]) | |
| >>> M.charpoly(x).as_expr() | |
| _x**2 - _x - 2*x | |
| Whether you pass a symbol or not, the generator can be obtained | |
| with the gen attribute since it may not be the same as the symbol | |
| that was passed: | |
| >>> M.charpoly(x).gen | |
| _x | |
| >>> M.charpoly(x).gen == x | |
| False | |
| Notes | |
| ===== | |
| The Samuelson-Berkowitz algorithm is used to compute | |
| the characteristic polynomial efficiently and without any | |
| division operations. Thus the characteristic polynomial over any | |
| commutative ring without zero divisors can be computed. | |
| If the determinant det(x*I - M) can be found out easily as | |
| in the case of an upper or a lower triangular matrix, then | |
| instead of Samuelson-Berkowitz algorithm, eigenvalues are computed | |
| and the characteristic polynomial with their help. | |
| See Also | |
| ======== | |
| det | |
| """ | |
| if not M.is_square: | |
| raise NonSquareMatrixError() | |
| # Use DomainMatrix. We are already going to convert this to a Poly so there | |
| # is no need to worry about expanding powers etc. Also since this algorithm | |
| # does not require division or zero detection it is fine to use EX. | |
| # | |
| # M.to_DM() will fall back on EXRAW rather than EX. EXRAW is a lot faster | |
| # for elementary arithmetic because it does not call cancel for each | |
| # operation but it generates large unsimplified results that are slow in | |
| # the subsequent call to simplify. Using EX instead is faster overall | |
| # but at least in some cases EXRAW+simplify gives a simpler result so we | |
| # preserve that existing behaviour of charpoly for now... | |
| dM = M.to_DM() | |
| K = dM.domain | |
| cp = dM.charpoly() | |
| x = uniquely_named_symbol(x, [M], modify=lambda s: '_' + s) | |
| if K.is_EXRAW or simplify is not _simplify: | |
| # XXX: Converting back to Expr is expensive. We only do it if the | |
| # caller supplied a custom simplify function for backwards | |
| # compatibility or otherwise if the domain was EX. For any other domain | |
| # there should be no benefit in simplifying at this stage because Poly | |
| # will put everything into canonical form anyway. | |
| berk_vector = [K.to_sympy(c) for c in cp] | |
| berk_vector = [simplify(a) for a in berk_vector] | |
| p = PurePoly(berk_vector, x) | |
| else: | |
| # Convert from the list of domain elements directly to Poly. | |
| p = PurePoly(cp, x, domain=K) | |
| return p | |
| def _cofactor(M, i, j, method="berkowitz"): | |
| """Calculate the cofactor of an element. | |
| Parameters | |
| ========== | |
| method : string, optional | |
| Method to use to find the cofactors, can be "bareiss", "berkowitz", | |
| "bird", "laplace" or "lu". | |
| Examples | |
| ======== | |
| >>> from sympy import Matrix | |
| >>> M = Matrix([[1, 2], [3, 4]]) | |
| >>> M.cofactor(0, 1) | |
| -3 | |
| See Also | |
| ======== | |
| cofactor_matrix | |
| minor | |
| minor_submatrix | |
| """ | |
| if not M.is_square or M.rows < 1: | |
| raise NonSquareMatrixError() | |
| return S.NegativeOne**((i + j) % 2) * M.minor(i, j, method) | |
| def _cofactor_matrix(M, method="berkowitz"): | |
| """Return a matrix containing the cofactor of each element. | |
| Parameters | |
| ========== | |
| method : string, optional | |
| Method to use to find the cofactors, can be "bareiss", "berkowitz", | |
| "bird", "laplace" or "lu". | |
| Examples | |
| ======== | |
| >>> from sympy import Matrix | |
| >>> M = Matrix([[1, 2], [3, 4]]) | |
| >>> M.cofactor_matrix() | |
| Matrix([ | |
| [ 4, -3], | |
| [-2, 1]]) | |
| See Also | |
| ======== | |
| cofactor | |
| minor | |
| minor_submatrix | |
| """ | |
| if not M.is_square: | |
| raise NonSquareMatrixError() | |
| return M._new(M.rows, M.cols, | |
| lambda i, j: M.cofactor(i, j, method)) | |
| def _per(M): | |
| """Returns the permanent of a matrix. Unlike determinant, | |
| permanent is defined for both square and non-square matrices. | |
| For an m x n matrix, with m less than or equal to n, | |
| it is given as the sum over the permutations s of size | |
| less than or equal to m on [1, 2, . . . n] of the product | |
| from i = 1 to m of M[i, s[i]]. Taking the transpose will | |
| not affect the value of the permanent. | |
| In the case of a square matrix, this is the same as the permutation | |
| definition of the determinant, but it does not take the sign of the | |
| permutation into account. Computing the permanent with this definition | |
| is quite inefficient, so here the Ryser formula is used. | |
| Examples | |
| ======== | |
| >>> from sympy import Matrix | |
| >>> M = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) | |
| >>> M.per() | |
| 450 | |
| >>> M = Matrix([1, 5, 7]) | |
| >>> M.per() | |
| 13 | |
| References | |
| ========== | |
| .. [1] Prof. Frank Ben's notes: https://math.berkeley.edu/~bernd/ban275.pdf | |
| .. [2] Wikipedia article on Permanent: https://en.wikipedia.org/wiki/Permanent_%28mathematics%29 | |
| .. [3] https://reference.wolfram.com/language/ref/Permanent.html | |
| .. [4] Permanent of a rectangular matrix : https://arxiv.org/pdf/0904.3251.pdf | |
| """ | |
| import itertools | |
| m, n = M.shape | |
| if m > n: | |
| M = M.T | |
| m, n = n, m | |
| s = list(range(n)) | |
| subsets = [] | |
| for i in range(1, m + 1): | |
| subsets += list(map(list, itertools.combinations(s, i))) | |
| perm = 0 | |
| for subset in subsets: | |
| prod = 1 | |
| sub_len = len(subset) | |
| for i in range(m): | |
| prod *= sum(M[i, j] for j in subset) | |
| perm += prod * S.NegativeOne**sub_len * nC(n - sub_len, m - sub_len) | |
| perm *= S.NegativeOne**m | |
| return perm.simplify() | |
| def _det_DOM(M): | |
| DOM = DomainMatrix.from_Matrix(M, field=True, extension=True) | |
| K = DOM.domain | |
| return K.to_sympy(DOM.det()) | |
| # This functions is a candidate for caching if it gets implemented for matrices. | |
| def _det(M, method="bareiss", iszerofunc=None): | |
| """Computes the determinant of a matrix if ``M`` is a concrete matrix object | |
| otherwise return an expressions ``Determinant(M)`` if ``M`` is a | |
| ``MatrixSymbol`` or other expression. | |
| Parameters | |
| ========== | |
| method : string, optional | |
| Specifies the algorithm used for computing the matrix determinant. | |
| If the matrix is at most 3x3, a hard-coded formula is used and the | |
| specified method is ignored. Otherwise, it defaults to | |
| ``'bareiss'``. | |
| Also, if the matrix is an upper or a lower triangular matrix, determinant | |
| is computed by simple multiplication of diagonal elements, and the | |
| specified method is ignored. | |
| If it is set to ``'domain-ge'``, then Gaussian elimination method will | |
| be used via using DomainMatrix. | |
| If it is set to ``'bareiss'``, Bareiss' fraction-free algorithm will | |
| be used. | |
| If it is set to ``'berkowitz'``, Berkowitz' algorithm will be used. | |
| If it is set to ``'bird'``, Bird's algorithm will be used [1]_. | |
| If it is set to ``'laplace'``, Laplace's algorithm will be used. | |
| Otherwise, if it is set to ``'lu'``, LU decomposition will be used. | |
| .. note:: | |
| For backward compatibility, legacy keys like "bareis" and | |
| "det_lu" can still be used to indicate the corresponding | |
| methods. | |
| And the keys are also case-insensitive for now. However, it is | |
| suggested to use the precise keys for specifying the method. | |
| iszerofunc : FunctionType or None, optional | |
| If it is set to ``None``, it will be defaulted to ``_iszero`` if the | |
| method is set to ``'bareiss'``, and ``_is_zero_after_expand_mul`` if | |
| the method is set to ``'lu'``. | |
| It can also accept any user-specified zero testing function, if it | |
| is formatted as a function which accepts a single symbolic argument | |
| and returns ``True`` if it is tested as zero and ``False`` if it | |
| tested as non-zero, and also ``None`` if it is undecidable. | |
| Returns | |
| ======= | |
| det : Basic | |
| Result of determinant. | |
| Raises | |
| ====== | |
| ValueError | |
| If unrecognized keys are given for ``method`` or ``iszerofunc``. | |
| NonSquareMatrixError | |
| If attempted to calculate determinant from a non-square matrix. | |
| Examples | |
| ======== | |
| >>> from sympy import Matrix, eye, det | |
| >>> I3 = eye(3) | |
| >>> det(I3) | |
| 1 | |
| >>> M = Matrix([[1, 2], [3, 4]]) | |
| >>> det(M) | |
| -2 | |
| >>> det(M) == M.det() | |
| True | |
| >>> M.det(method="domain-ge") | |
| -2 | |
| References | |
| ========== | |
| .. [1] Bird, R. S. (2011). A simple division-free algorithm for computing | |
| determinants. Inf. Process. Lett., 111(21), 1072-1074. doi: | |
| 10.1016/j.ipl.2011.08.006 | |
| """ | |
| # sanitize `method` | |
| method = method.lower() | |
| if method == "bareis": | |
| method = "bareiss" | |
| elif method == "det_lu": | |
| method = "lu" | |
| if method not in ("bareiss", "berkowitz", "lu", "domain-ge", "bird", | |
| "laplace"): | |
| raise ValueError("Determinant method '%s' unrecognized" % method) | |
| if iszerofunc is None: | |
| if method == "bareiss": | |
| iszerofunc = _is_zero_after_expand_mul | |
| elif method == "lu": | |
| iszerofunc = _iszero | |
| elif not isinstance(iszerofunc, FunctionType): | |
| raise ValueError("Zero testing method '%s' unrecognized" % iszerofunc) | |
| n = M.rows | |
| if n == M.cols: # square check is done in individual method functions | |
| if n == 0: | |
| return M.one | |
| elif n == 1: | |
| return M[0, 0] | |
| elif n == 2: | |
| m = M[0, 0] * M[1, 1] - M[0, 1] * M[1, 0] | |
| return _get_intermediate_simp(_dotprodsimp)(m) | |
| elif n == 3: | |
| m = (M[0, 0] * M[1, 1] * M[2, 2] | |
| + M[0, 1] * M[1, 2] * M[2, 0] | |
| + M[0, 2] * M[1, 0] * M[2, 1] | |
| - M[0, 2] * M[1, 1] * M[2, 0] | |
| - M[0, 0] * M[1, 2] * M[2, 1] | |
| - M[0, 1] * M[1, 0] * M[2, 2]) | |
| return _get_intermediate_simp(_dotprodsimp)(m) | |
| dets = [] | |
| for b in M.strongly_connected_components(): | |
| if method == "domain-ge": # uses DomainMatrix to evaluate determinant | |
| det = _det_DOM(M[b, b]) | |
| elif method == "bareiss": | |
| det = M[b, b]._eval_det_bareiss(iszerofunc=iszerofunc) | |
| elif method == "berkowitz": | |
| det = M[b, b]._eval_det_berkowitz() | |
| elif method == "lu": | |
| det = M[b, b]._eval_det_lu(iszerofunc=iszerofunc) | |
| elif method == "bird": | |
| det = M[b, b]._eval_det_bird() | |
| elif method == "laplace": | |
| det = M[b, b]._eval_det_laplace() | |
| dets.append(det) | |
| return Mul(*dets) | |
| # This functions is a candidate for caching if it gets implemented for matrices. | |
| def _det_bareiss(M, iszerofunc=_is_zero_after_expand_mul): | |
| """Compute matrix determinant using Bareiss' fraction-free | |
| algorithm which is an extension of the well known Gaussian | |
| elimination method. This approach is best suited for dense | |
| symbolic matrices and will result in a determinant with | |
| minimal number of fractions. It means that less term | |
| rewriting is needed on resulting formulae. | |
| Parameters | |
| ========== | |
| iszerofunc : function, optional | |
| The function to use to determine zeros when doing an LU decomposition. | |
| Defaults to ``lambda x: x.is_zero``. | |
| TODO: Implement algorithm for sparse matrices (SFF), | |
| http://www.eecis.udel.edu/~saunders/papers/sffge/it5.ps. | |
| """ | |
| # Recursively implemented Bareiss' algorithm as per Deanna Richelle Leggett's | |
| # thesis http://www.math.usm.edu/perry/Research/Thesis_DRL.pdf | |
| def bareiss(mat, cumm=1): | |
| if mat.rows == 0: | |
| return mat.one | |
| elif mat.rows == 1: | |
| return mat[0, 0] | |
| # find a pivot and extract the remaining matrix | |
| # With the default iszerofunc, _find_reasonable_pivot slows down | |
| # the computation by the factor of 2.5 in one test. | |
| # Relevant issues: #10279 and #13877. | |
| pivot_pos, pivot_val, _, _ = _find_reasonable_pivot(mat[:, 0], iszerofunc=iszerofunc) | |
| if pivot_pos is None: | |
| return mat.zero | |
| # if we have a valid pivot, we'll do a "row swap", so keep the | |
| # sign of the det | |
| sign = (-1) ** (pivot_pos % 2) | |
| # we want every row but the pivot row and every column | |
| rows = [i for i in range(mat.rows) if i != pivot_pos] | |
| cols = list(range(mat.cols)) | |
| tmp_mat = mat.extract(rows, cols) | |
| def entry(i, j): | |
| ret = (pivot_val*tmp_mat[i, j + 1] - mat[pivot_pos, j + 1]*tmp_mat[i, 0]) / cumm | |
| if _get_intermediate_simp_bool(True): | |
| return _dotprodsimp(ret) | |
| elif not ret.is_Atom: | |
| return cancel(ret) | |
| return ret | |
| return sign*bareiss(M._new(mat.rows - 1, mat.cols - 1, entry), pivot_val) | |
| if not M.is_square: | |
| raise NonSquareMatrixError() | |
| if M.rows == 0: | |
| return M.one | |
| # sympy/matrices/tests/test_matrices.py contains a test that | |
| # suggests that the determinant of a 0 x 0 matrix is one, by | |
| # convention. | |
| return bareiss(M) | |
| def _det_berkowitz(M): | |
| """ Use the Berkowitz algorithm to compute the determinant.""" | |
| if not M.is_square: | |
| raise NonSquareMatrixError() | |
| if M.rows == 0: | |
| return M.one | |
| # sympy/matrices/tests/test_matrices.py contains a test that | |
| # suggests that the determinant of a 0 x 0 matrix is one, by | |
| # convention. | |
| berk_vector = _berkowitz_vector(M) | |
| return (-1)**(len(berk_vector) - 1) * berk_vector[-1] | |
| # This functions is a candidate for caching if it gets implemented for matrices. | |
| def _det_LU(M, iszerofunc=_iszero, simpfunc=None): | |
| """ Computes the determinant of a matrix from its LU decomposition. | |
| This function uses the LU decomposition computed by | |
| LUDecomposition_Simple(). | |
| The keyword arguments iszerofunc and simpfunc are passed to | |
| LUDecomposition_Simple(). | |
| iszerofunc is a callable that returns a boolean indicating if its | |
| input is zero, or None if it cannot make the determination. | |
| simpfunc is a callable that simplifies its input. | |
| The default is simpfunc=None, which indicate that the pivot search | |
| algorithm should not attempt to simplify any candidate pivots. | |
| If simpfunc fails to simplify its input, then it must return its input | |
| instead of a copy. | |
| Parameters | |
| ========== | |
| iszerofunc : function, optional | |
| The function to use to determine zeros when doing an LU decomposition. | |
| Defaults to ``lambda x: x.is_zero``. | |
| simpfunc : function, optional | |
| The simplification function to use when looking for zeros for pivots. | |
| """ | |
| if not M.is_square: | |
| raise NonSquareMatrixError() | |
| if M.rows == 0: | |
| return M.one | |
| # sympy/matrices/tests/test_matrices.py contains a test that | |
| # suggests that the determinant of a 0 x 0 matrix is one, by | |
| # convention. | |
| lu, row_swaps = M.LUdecomposition_Simple(iszerofunc=iszerofunc, | |
| simpfunc=simpfunc) | |
| # P*A = L*U => det(A) = det(L)*det(U)/det(P) = det(P)*det(U). | |
| # Lower triangular factor L encoded in lu has unit diagonal => det(L) = 1. | |
| # P is a permutation matrix => det(P) in {-1, 1} => 1/det(P) = det(P). | |
| # LUdecomposition_Simple() returns a list of row exchange index pairs, rather | |
| # than a permutation matrix, but det(P) = (-1)**len(row_swaps). | |
| # Avoid forming the potentially time consuming product of U's diagonal entries | |
| # if the product is zero. | |
| # Bottom right entry of U is 0 => det(A) = 0. | |
| # It may be impossible to determine if this entry of U is zero when it is symbolic. | |
| if iszerofunc(lu[lu.rows-1, lu.rows-1]): | |
| return M.zero | |
| # Compute det(P) | |
| det = -M.one if len(row_swaps)%2 else M.one | |
| # Compute det(U) by calculating the product of U's diagonal entries. | |
| # The upper triangular portion of lu is the upper triangular portion of the | |
| # U factor in the LU decomposition. | |
| for k in range(lu.rows): | |
| det *= lu[k, k] | |
| # return det(P)*det(U) | |
| return det | |
| def __det_laplace(M): | |
| """Compute the determinant of a matrix using Laplace expansion. | |
| This is a recursive function, and it should not be called directly. | |
| Use _det_laplace() instead. The reason for splitting this function | |
| into two is to allow caching of determinants of submatrices. While | |
| one could also define this function inside _det_laplace(), that | |
| would remove the advantage of using caching in Cramer Solve. | |
| """ | |
| n = M.shape[0] | |
| if n == 1: | |
| return M[0] | |
| elif n == 2: | |
| return M[0, 0] * M[1, 1] - M[0, 1] * M[1, 0] | |
| else: | |
| return sum((-1) ** i * M[0, i] * | |
| __det_laplace(M.minor_submatrix(0, i)) for i in range(n)) | |
| def _det_laplace(M): | |
| """Compute the determinant of a matrix using Laplace expansion. | |
| While Laplace expansion is not the most efficient method of computing | |
| a determinant, it is a simple one, and it has the advantage of | |
| being division free. To improve efficiency, this function uses | |
| caching to avoid recomputing determinants of submatrices. | |
| """ | |
| if not M.is_square: | |
| raise NonSquareMatrixError() | |
| if M.shape[0] == 0: | |
| return M.one | |
| # sympy/matrices/tests/test_matrices.py contains a test that | |
| # suggests that the determinant of a 0 x 0 matrix is one, by | |
| # convention. | |
| return __det_laplace(M.as_immutable()) | |
| def _det_bird(M): | |
| r"""Compute the determinant of a matrix using Bird's algorithm. | |
| Bird's algorithm is a simple division-free algorithm for computing, which | |
| is of lower order than the Laplace's algorithm. It is described in [1]_. | |
| References | |
| ========== | |
| .. [1] Bird, R. S. (2011). A simple division-free algorithm for computing | |
| determinants. Inf. Process. Lett., 111(21), 1072-1074. doi: | |
| 10.1016/j.ipl.2011.08.006 | |
| """ | |
| def mu(X): | |
| n = X.shape[0] | |
| zero = X.domain.zero | |
| total = zero | |
| diag_sums = [zero] | |
| for i in reversed(range(1, n)): | |
| total -= X[i][i] | |
| diag_sums.append(total) | |
| diag_sums = diag_sums[::-1] | |
| elems = [[zero] * i + [diag_sums[i]] + X_i[i + 1:] for i, X_i in | |
| enumerate(X)] | |
| return DDM(elems, X.shape, X.domain) | |
| Mddm = M._rep.to_ddm() | |
| n = M.shape[0] | |
| if n == 0: | |
| return M.one | |
| # sympy/matrices/tests/test_matrices.py contains a test that | |
| # suggests that the determinant of a 0 x 0 matrix is one, by | |
| # convention. | |
| Fn1 = Mddm | |
| for _ in range(n - 1): | |
| Fn1 = mu(Fn1).matmul(Mddm) | |
| detA = Fn1[0][0] | |
| if n % 2 == 0: | |
| detA = -detA | |
| return Mddm.domain.to_sympy(detA) | |
| def _minor(M, i, j, method="berkowitz"): | |
| """Return the (i,j) minor of ``M``. That is, | |
| return the determinant of the matrix obtained by deleting | |
| the `i`th row and `j`th column from ``M``. | |
| Parameters | |
| ========== | |
| i, j : int | |
| The row and column to exclude to obtain the submatrix. | |
| method : string, optional | |
| Method to use to find the determinant of the submatrix, can be | |
| "bareiss", "berkowitz", "bird", "laplace" or "lu". | |
| Examples | |
| ======== | |
| >>> from sympy import Matrix | |
| >>> M = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) | |
| >>> M.minor(1, 1) | |
| -12 | |
| See Also | |
| ======== | |
| minor_submatrix | |
| cofactor | |
| det | |
| """ | |
| if not M.is_square: | |
| raise NonSquareMatrixError() | |
| return M.minor_submatrix(i, j).det(method=method) | |
| def _minor_submatrix(M, i, j): | |
| """Return the submatrix obtained by removing the `i`th row | |
| and `j`th column from ``M`` (works with Pythonic negative indices). | |
| Parameters | |
| ========== | |
| i, j : int | |
| The row and column to exclude to obtain the submatrix. | |
| Examples | |
| ======== | |
| >>> from sympy import Matrix | |
| >>> M = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) | |
| >>> M.minor_submatrix(1, 1) | |
| Matrix([ | |
| [1, 3], | |
| [7, 9]]) | |
| See Also | |
| ======== | |
| minor | |
| cofactor | |
| """ | |
| if i < 0: | |
| i += M.rows | |
| if j < 0: | |
| j += M.cols | |
| if not 0 <= i < M.rows or not 0 <= j < M.cols: | |
| raise ValueError("`i` and `j` must satisfy 0 <= i < ``M.rows`` " | |
| "(%d)" % M.rows + "and 0 <= j < ``M.cols`` (%d)." % M.cols) | |
| rows = [a for a in range(M.rows) if a != i] | |
| cols = [a for a in range(M.cols) if a != j] | |
| return M.extract(rows, cols) | |
Xet Storage Details
- Size:
- 34.6 kB
- Xet hash:
- ace63bc025578e7451dfe72e563be3374303d812b2184d9118071f91e25d144f
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.