determinant.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898
  1. from types import FunctionType
  2. from sympy.core.numbers import Float, Integer
  3. from sympy.core.singleton import S
  4. from sympy.core.symbol import uniquely_named_symbol
  5. from sympy.core.mul import Mul
  6. from sympy.polys import PurePoly, cancel
  7. from sympy.functions.combinatorial.numbers import nC
  8. from sympy.polys.matrices.domainmatrix import DomainMatrix
  9. from .common import NonSquareMatrixError
  10. from .utilities import (
  11. _get_intermediate_simp, _get_intermediate_simp_bool,
  12. _iszero, _is_zero_after_expand_mul, _dotprodsimp, _simplify)
  13. def _find_reasonable_pivot(col, iszerofunc=_iszero, simpfunc=_simplify):
  14. """ Find the lowest index of an item in ``col`` that is
  15. suitable for a pivot. If ``col`` consists only of
  16. Floats, the pivot with the largest norm is returned.
  17. Otherwise, the first element where ``iszerofunc`` returns
  18. False is used. If ``iszerofunc`` does not return false,
  19. items are simplified and retested until a suitable
  20. pivot is found.
  21. Returns a 4-tuple
  22. (pivot_offset, pivot_val, assumed_nonzero, newly_determined)
  23. where pivot_offset is the index of the pivot, pivot_val is
  24. the (possibly simplified) value of the pivot, assumed_nonzero
  25. is True if an assumption that the pivot was non-zero
  26. was made without being proved, and newly_determined are
  27. elements that were simplified during the process of pivot
  28. finding."""
  29. newly_determined = []
  30. col = list(col)
  31. # a column that contains a mix of floats and integers
  32. # but at least one float is considered a numerical
  33. # column, and so we do partial pivoting
  34. if all(isinstance(x, (Float, Integer)) for x in col) and any(
  35. isinstance(x, Float) for x in col):
  36. col_abs = [abs(x) for x in col]
  37. max_value = max(col_abs)
  38. if iszerofunc(max_value):
  39. # just because iszerofunc returned True, doesn't
  40. # mean the value is numerically zero. Make sure
  41. # to replace all entries with numerical zeros
  42. if max_value != 0:
  43. newly_determined = [(i, 0) for i, x in enumerate(col) if x != 0]
  44. return (None, None, False, newly_determined)
  45. index = col_abs.index(max_value)
  46. return (index, col[index], False, newly_determined)
  47. # PASS 1 (iszerofunc directly)
  48. possible_zeros = []
  49. for i, x in enumerate(col):
  50. is_zero = iszerofunc(x)
  51. # is someone wrote a custom iszerofunc, it may return
  52. # BooleanFalse or BooleanTrue instead of True or False,
  53. # so use == for comparison instead of `is`
  54. if is_zero == False:
  55. # we found something that is definitely not zero
  56. return (i, x, False, newly_determined)
  57. possible_zeros.append(is_zero)
  58. # by this point, we've found no certain non-zeros
  59. if all(possible_zeros):
  60. # if everything is definitely zero, we have
  61. # no pivot
  62. return (None, None, False, newly_determined)
  63. # PASS 2 (iszerofunc after simplify)
  64. # we haven't found any for-sure non-zeros, so
  65. # go through the elements iszerofunc couldn't
  66. # make a determination about and opportunistically
  67. # simplify to see if we find something
  68. for i, x in enumerate(col):
  69. if possible_zeros[i] is not None:
  70. continue
  71. simped = simpfunc(x)
  72. is_zero = iszerofunc(simped)
  73. if is_zero in (True, False):
  74. newly_determined.append((i, simped))
  75. if is_zero == False:
  76. return (i, simped, False, newly_determined)
  77. possible_zeros[i] = is_zero
  78. # after simplifying, some things that were recognized
  79. # as zeros might be zeros
  80. if all(possible_zeros):
  81. # if everything is definitely zero, we have
  82. # no pivot
  83. return (None, None, False, newly_determined)
  84. # PASS 3 (.equals(0))
  85. # some expressions fail to simplify to zero, but
  86. # ``.equals(0)`` evaluates to True. As a last-ditch
  87. # attempt, apply ``.equals`` to these expressions
  88. for i, x in enumerate(col):
  89. if possible_zeros[i] is not None:
  90. continue
  91. if x.equals(S.Zero):
  92. # ``.iszero`` may return False with
  93. # an implicit assumption (e.g., ``x.equals(0)``
  94. # when ``x`` is a symbol), so only treat it
  95. # as proved when ``.equals(0)`` returns True
  96. possible_zeros[i] = True
  97. newly_determined.append((i, S.Zero))
  98. if all(possible_zeros):
  99. return (None, None, False, newly_determined)
  100. # at this point there is nothing that could definitely
  101. # be a pivot. To maintain compatibility with existing
  102. # behavior, we'll assume that an illdetermined thing is
  103. # non-zero. We should probably raise a warning in this case
  104. i = possible_zeros.index(None)
  105. return (i, col[i], True, newly_determined)
  106. def _find_reasonable_pivot_naive(col, iszerofunc=_iszero, simpfunc=None):
  107. """
  108. Helper that computes the pivot value and location from a
  109. sequence of contiguous matrix column elements. As a side effect
  110. of the pivot search, this function may simplify some of the elements
  111. of the input column. A list of these simplified entries and their
  112. indices are also returned.
  113. This function mimics the behavior of _find_reasonable_pivot(),
  114. but does less work trying to determine if an indeterminate candidate
  115. pivot simplifies to zero. This more naive approach can be much faster,
  116. with the trade-off that it may erroneously return a pivot that is zero.
  117. ``col`` is a sequence of contiguous column entries to be searched for
  118. a suitable pivot.
  119. ``iszerofunc`` is a callable that returns a Boolean that indicates
  120. if its input is zero, or None if no such determination can be made.
  121. ``simpfunc`` is a callable that simplifies its input. It must return
  122. its input if it does not simplify its input. Passing in
  123. ``simpfunc=None`` indicates that the pivot search should not attempt
  124. to simplify any candidate pivots.
  125. Returns a 4-tuple:
  126. (pivot_offset, pivot_val, assumed_nonzero, newly_determined)
  127. ``pivot_offset`` is the sequence index of the pivot.
  128. ``pivot_val`` is the value of the pivot.
  129. pivot_val and col[pivot_index] are equivalent, but will be different
  130. when col[pivot_index] was simplified during the pivot search.
  131. ``assumed_nonzero`` is a boolean indicating if the pivot cannot be
  132. guaranteed to be zero. If assumed_nonzero is true, then the pivot
  133. may or may not be non-zero. If assumed_nonzero is false, then
  134. the pivot is non-zero.
  135. ``newly_determined`` is a list of index-value pairs of pivot candidates
  136. that were simplified during the pivot search.
  137. """
  138. # indeterminates holds the index-value pairs of each pivot candidate
  139. # that is neither zero or non-zero, as determined by iszerofunc().
  140. # If iszerofunc() indicates that a candidate pivot is guaranteed
  141. # non-zero, or that every candidate pivot is zero then the contents
  142. # of indeterminates are unused.
  143. # Otherwise, the only viable candidate pivots are symbolic.
  144. # In this case, indeterminates will have at least one entry,
  145. # and all but the first entry are ignored when simpfunc is None.
  146. indeterminates = []
  147. for i, col_val in enumerate(col):
  148. col_val_is_zero = iszerofunc(col_val)
  149. if col_val_is_zero == False:
  150. # This pivot candidate is non-zero.
  151. return i, col_val, False, []
  152. elif col_val_is_zero is None:
  153. # The candidate pivot's comparison with zero
  154. # is indeterminate.
  155. indeterminates.append((i, col_val))
  156. if len(indeterminates) == 0:
  157. # All candidate pivots are guaranteed to be zero, i.e. there is
  158. # no pivot.
  159. return None, None, False, []
  160. if simpfunc is None:
  161. # Caller did not pass in a simplification function that might
  162. # determine if an indeterminate pivot candidate is guaranteed
  163. # to be nonzero, so assume the first indeterminate candidate
  164. # is non-zero.
  165. return indeterminates[0][0], indeterminates[0][1], True, []
  166. # newly_determined holds index-value pairs of candidate pivots
  167. # that were simplified during the search for a non-zero pivot.
  168. newly_determined = []
  169. for i, col_val in indeterminates:
  170. tmp_col_val = simpfunc(col_val)
  171. if id(col_val) != id(tmp_col_val):
  172. # simpfunc() simplified this candidate pivot.
  173. newly_determined.append((i, tmp_col_val))
  174. if iszerofunc(tmp_col_val) == False:
  175. # Candidate pivot simplified to a guaranteed non-zero value.
  176. return i, tmp_col_val, False, newly_determined
  177. return indeterminates[0][0], indeterminates[0][1], True, newly_determined
  178. # This functions is a candidate for caching if it gets implemented for matrices.
  179. def _berkowitz_toeplitz_matrix(M):
  180. """Return (A,T) where T the Toeplitz matrix used in the Berkowitz algorithm
  181. corresponding to ``M`` and A is the first principal submatrix.
  182. """
  183. # the 0 x 0 case is trivial
  184. if M.rows == 0 and M.cols == 0:
  185. return M._new(1,1, [M.one])
  186. #
  187. # Partition M = [ a_11 R ]
  188. # [ C A ]
  189. #
  190. a, R = M[0,0], M[0, 1:]
  191. C, A = M[1:, 0], M[1:,1:]
  192. #
  193. # The Toeplitz matrix looks like
  194. #
  195. # [ 1 ]
  196. # [ -a 1 ]
  197. # [ -RC -a 1 ]
  198. # [ -RAC -RC -a 1 ]
  199. # [ -RA**2C -RAC -RC -a 1 ]
  200. # etc.
  201. # Compute the diagonal entries.
  202. # Because multiplying matrix times vector is so much
  203. # more efficient than matrix times matrix, recursively
  204. # compute -R * A**n * C.
  205. diags = [C]
  206. for i in range(M.rows - 2):
  207. diags.append(A.multiply(diags[i], dotprodsimp=None))
  208. diags = [(-R).multiply(d, dotprodsimp=None)[0, 0] for d in diags]
  209. diags = [M.one, -a] + diags
  210. def entry(i,j):
  211. if j > i:
  212. return M.zero
  213. return diags[i - j]
  214. toeplitz = M._new(M.cols + 1, M.rows, entry)
  215. return (A, toeplitz)
  216. # This functions is a candidate for caching if it gets implemented for matrices.
  217. def _berkowitz_vector(M):
  218. """ Run the Berkowitz algorithm and return a vector whose entries
  219. are the coefficients of the characteristic polynomial of ``M``.
  220. Given N x N matrix, efficiently compute
  221. coefficients of characteristic polynomials of ``M``
  222. without division in the ground domain.
  223. This method is particularly useful for computing determinant,
  224. principal minors and characteristic polynomial when ``M``
  225. has complicated coefficients e.g. polynomials. Semi-direct
  226. usage of this algorithm is also important in computing
  227. efficiently sub-resultant PRS.
  228. Assuming that M is a square matrix of dimension N x N and
  229. I is N x N identity matrix, then the Berkowitz vector is
  230. an N x 1 vector whose entries are coefficients of the
  231. polynomial
  232. charpoly(M) = det(t*I - M)
  233. As a consequence, all polynomials generated by Berkowitz
  234. algorithm are monic.
  235. For more information on the implemented algorithm refer to:
  236. [1] S.J. Berkowitz, On computing the determinant in small
  237. parallel time using a small number of processors, ACM,
  238. Information Processing Letters 18, 1984, pp. 147-150
  239. [2] M. Keber, Division-Free computation of sub-resultants
  240. using Bezout matrices, Tech. Report MPI-I-2006-1-006,
  241. Saarbrucken, 2006
  242. """
  243. # handle the trivial cases
  244. if M.rows == 0 and M.cols == 0:
  245. return M._new(1, 1, [M.one])
  246. elif M.rows == 1 and M.cols == 1:
  247. return M._new(2, 1, [M.one, -M[0,0]])
  248. submat, toeplitz = _berkowitz_toeplitz_matrix(M)
  249. return toeplitz.multiply(_berkowitz_vector(submat), dotprodsimp=None)
  250. def _adjugate(M, method="berkowitz"):
  251. """Returns the adjugate, or classical adjoint, of
  252. a matrix. That is, the transpose of the matrix of cofactors.
  253. https://en.wikipedia.org/wiki/Adjugate
  254. Parameters
  255. ==========
  256. method : string, optional
  257. Method to use to find the cofactors, can be "bareiss", "berkowitz" or
  258. "lu".
  259. Examples
  260. ========
  261. >>> from sympy import Matrix
  262. >>> M = Matrix([[1, 2], [3, 4]])
  263. >>> M.adjugate()
  264. Matrix([
  265. [ 4, -2],
  266. [-3, 1]])
  267. See Also
  268. ========
  269. cofactor_matrix
  270. sympy.matrices.common.MatrixCommon.transpose
  271. """
  272. return M.cofactor_matrix(method=method).transpose()
  273. # This functions is a candidate for caching if it gets implemented for matrices.
  274. def _charpoly(M, x='lambda', simplify=_simplify):
  275. """Computes characteristic polynomial det(x*I - M) where I is
  276. the identity matrix.
  277. A PurePoly is returned, so using different variables for ``x`` does
  278. not affect the comparison or the polynomials:
  279. Parameters
  280. ==========
  281. x : string, optional
  282. Name for the "lambda" variable, defaults to "lambda".
  283. simplify : function, optional
  284. Simplification function to use on the characteristic polynomial
  285. calculated. Defaults to ``simplify``.
  286. Examples
  287. ========
  288. >>> from sympy import Matrix
  289. >>> from sympy.abc import x, y
  290. >>> M = Matrix([[1, 3], [2, 0]])
  291. >>> M.charpoly()
  292. PurePoly(lambda**2 - lambda - 6, lambda, domain='ZZ')
  293. >>> M.charpoly(x) == M.charpoly(y)
  294. True
  295. >>> M.charpoly(x) == M.charpoly(y)
  296. True
  297. Specifying ``x`` is optional; a symbol named ``lambda`` is used by
  298. default (which looks good when pretty-printed in unicode):
  299. >>> M.charpoly().as_expr()
  300. lambda**2 - lambda - 6
  301. And if ``x`` clashes with an existing symbol, underscores will
  302. be prepended to the name to make it unique:
  303. >>> M = Matrix([[1, 2], [x, 0]])
  304. >>> M.charpoly(x).as_expr()
  305. _x**2 - _x - 2*x
  306. Whether you pass a symbol or not, the generator can be obtained
  307. with the gen attribute since it may not be the same as the symbol
  308. that was passed:
  309. >>> M.charpoly(x).gen
  310. _x
  311. >>> M.charpoly(x).gen == x
  312. False
  313. Notes
  314. =====
  315. The Samuelson-Berkowitz algorithm is used to compute
  316. the characteristic polynomial efficiently and without any
  317. division operations. Thus the characteristic polynomial over any
  318. commutative ring without zero divisors can be computed.
  319. If the determinant det(x*I - M) can be found out easily as
  320. in the case of an upper or a lower triangular matrix, then
  321. instead of Samuelson-Berkowitz algorithm, eigenvalues are computed
  322. and the characteristic polynomial with their help.
  323. See Also
  324. ========
  325. det
  326. """
  327. if not M.is_square:
  328. raise NonSquareMatrixError()
  329. if M.is_lower or M.is_upper:
  330. diagonal_elements = M.diagonal()
  331. x = uniquely_named_symbol(x, diagonal_elements, modify=lambda s: '_' + s)
  332. m = 1
  333. for i in diagonal_elements:
  334. m = m * (x - simplify(i))
  335. return PurePoly(m, x)
  336. berk_vector = _berkowitz_vector(M)
  337. x = uniquely_named_symbol(x, berk_vector, modify=lambda s: '_' + s)
  338. return PurePoly([simplify(a) for a in berk_vector], x)
  339. def _cofactor(M, i, j, method="berkowitz"):
  340. """Calculate the cofactor of an element.
  341. Parameters
  342. ==========
  343. method : string, optional
  344. Method to use to find the cofactors, can be "bareiss", "berkowitz" or
  345. "lu".
  346. Examples
  347. ========
  348. >>> from sympy import Matrix
  349. >>> M = Matrix([[1, 2], [3, 4]])
  350. >>> M.cofactor(0, 1)
  351. -3
  352. See Also
  353. ========
  354. cofactor_matrix
  355. minor
  356. minor_submatrix
  357. """
  358. if not M.is_square or M.rows < 1:
  359. raise NonSquareMatrixError()
  360. return S.NegativeOne**((i + j) % 2) * M.minor(i, j, method)
  361. def _cofactor_matrix(M, method="berkowitz"):
  362. """Return a matrix containing the cofactor of each element.
  363. Parameters
  364. ==========
  365. method : string, optional
  366. Method to use to find the cofactors, can be "bareiss", "berkowitz" or
  367. "lu".
  368. Examples
  369. ========
  370. >>> from sympy import Matrix
  371. >>> M = Matrix([[1, 2], [3, 4]])
  372. >>> M.cofactor_matrix()
  373. Matrix([
  374. [ 4, -3],
  375. [-2, 1]])
  376. See Also
  377. ========
  378. cofactor
  379. minor
  380. minor_submatrix
  381. """
  382. if not M.is_square or M.rows < 1:
  383. raise NonSquareMatrixError()
  384. return M._new(M.rows, M.cols,
  385. lambda i, j: M.cofactor(i, j, method))
  386. def _per(M):
  387. """Returns the permanent of a matrix. Unlike determinant,
  388. permanent is defined for both square and non-square matrices.
  389. For an m x n matrix, with m less than or equal to n,
  390. it is given as the sum over the permutations s of size
  391. less than or equal to m on [1, 2, . . . n] of the product
  392. from i = 1 to m of M[i, s[i]]. Taking the transpose will
  393. not affect the value of the permanent.
  394. In the case of a square matrix, this is the same as the permutation
  395. definition of the determinant, but it does not take the sign of the
  396. permutation into account. Computing the permanent with this definition
  397. is quite inefficient, so here the Ryser formula is used.
  398. Examples
  399. ========
  400. >>> from sympy import Matrix
  401. >>> M = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
  402. >>> M.per()
  403. 450
  404. >>> M = Matrix([1, 5, 7])
  405. >>> M.per()
  406. 13
  407. References
  408. ==========
  409. .. [1] Prof. Frank Ben's notes: https://math.berkeley.edu/~bernd/ban275.pdf
  410. .. [2] Wikipedia article on Permanent: https://en.wikipedia.org/wiki/Permanent_%28mathematics%29
  411. .. [3] https://reference.wolfram.com/language/ref/Permanent.html
  412. .. [4] Permanent of a rectangular matrix : https://arxiv.org/pdf/0904.3251.pdf
  413. """
  414. import itertools
  415. m, n = M.shape
  416. if m > n:
  417. M = M.T
  418. m, n = n, m
  419. s = list(range(n))
  420. subsets = []
  421. for i in range(1, m + 1):
  422. subsets += list(map(list, itertools.combinations(s, i)))
  423. perm = 0
  424. for subset in subsets:
  425. prod = 1
  426. sub_len = len(subset)
  427. for i in range(m):
  428. prod *= sum([M[i, j] for j in subset])
  429. perm += prod * S.NegativeOne**sub_len * nC(n - sub_len, m - sub_len)
  430. perm *= S.NegativeOne**m
  431. return perm.simplify()
  432. def _det_DOM(M):
  433. DOM = DomainMatrix.from_Matrix(M, field=True, extension=True)
  434. K = DOM.domain
  435. return K.to_sympy(DOM.det())
  436. # This functions is a candidate for caching if it gets implemented for matrices.
  437. def _det(M, method="bareiss", iszerofunc=None):
  438. """Computes the determinant of a matrix if ``M`` is a concrete matrix object
  439. otherwise return an expressions ``Determinant(M)`` if ``M`` is a
  440. ``MatrixSymbol`` or other expression.
  441. Parameters
  442. ==========
  443. method : string, optional
  444. Specifies the algorithm used for computing the matrix determinant.
  445. If the matrix is at most 3x3, a hard-coded formula is used and the
  446. specified method is ignored. Otherwise, it defaults to
  447. ``'bareiss'``.
  448. Also, if the matrix is an upper or a lower triangular matrix, determinant
  449. is computed by simple multiplication of diagonal elements, and the
  450. specified method is ignored.
  451. If it is set to ``'domain-ge'``, then Gaussian elimination method will
  452. be used via using DomainMatrix.
  453. If it is set to ``'bareiss'``, Bareiss' fraction-free algorithm will
  454. be used.
  455. If it is set to ``'berkowitz'``, Berkowitz' algorithm will be used.
  456. Otherwise, if it is set to ``'lu'``, LU decomposition will be used.
  457. .. note::
  458. For backward compatibility, legacy keys like "bareis" and
  459. "det_lu" can still be used to indicate the corresponding
  460. methods.
  461. And the keys are also case-insensitive for now. However, it is
  462. suggested to use the precise keys for specifying the method.
  463. iszerofunc : FunctionType or None, optional
  464. If it is set to ``None``, it will be defaulted to ``_iszero`` if the
  465. method is set to ``'bareiss'``, and ``_is_zero_after_expand_mul`` if
  466. the method is set to ``'lu'``.
  467. It can also accept any user-specified zero testing function, if it
  468. is formatted as a function which accepts a single symbolic argument
  469. and returns ``True`` if it is tested as zero and ``False`` if it
  470. tested as non-zero, and also ``None`` if it is undecidable.
  471. Returns
  472. =======
  473. det : Basic
  474. Result of determinant.
  475. Raises
  476. ======
  477. ValueError
  478. If unrecognized keys are given for ``method`` or ``iszerofunc``.
  479. NonSquareMatrixError
  480. If attempted to calculate determinant from a non-square matrix.
  481. Examples
  482. ========
  483. >>> from sympy import Matrix, eye, det
  484. >>> I3 = eye(3)
  485. >>> det(I3)
  486. 1
  487. >>> M = Matrix([[1, 2], [3, 4]])
  488. >>> det(M)
  489. -2
  490. >>> det(M) == M.det()
  491. True
  492. >>> M.det(method="domain-ge")
  493. -2
  494. """
  495. # sanitize `method`
  496. method = method.lower()
  497. if method == "bareis":
  498. method = "bareiss"
  499. elif method == "det_lu":
  500. method = "lu"
  501. if method not in ("bareiss", "berkowitz", "lu", "domain-ge"):
  502. raise ValueError("Determinant method '%s' unrecognized" % method)
  503. if iszerofunc is None:
  504. if method == "bareiss":
  505. iszerofunc = _is_zero_after_expand_mul
  506. elif method == "lu":
  507. iszerofunc = _iszero
  508. elif not isinstance(iszerofunc, FunctionType):
  509. raise ValueError("Zero testing method '%s' unrecognized" % iszerofunc)
  510. n = M.rows
  511. if n == M.cols: # square check is done in individual method functions
  512. if n == 0:
  513. return M.one
  514. elif n == 1:
  515. return M[0, 0]
  516. elif n == 2:
  517. m = M[0, 0] * M[1, 1] - M[0, 1] * M[1, 0]
  518. return _get_intermediate_simp(_dotprodsimp)(m)
  519. elif n == 3:
  520. m = (M[0, 0] * M[1, 1] * M[2, 2]
  521. + M[0, 1] * M[1, 2] * M[2, 0]
  522. + M[0, 2] * M[1, 0] * M[2, 1]
  523. - M[0, 2] * M[1, 1] * M[2, 0]
  524. - M[0, 0] * M[1, 2] * M[2, 1]
  525. - M[0, 1] * M[1, 0] * M[2, 2])
  526. return _get_intermediate_simp(_dotprodsimp)(m)
  527. dets = []
  528. for b in M.strongly_connected_components():
  529. if method == "domain-ge": # uses DomainMatrix to evaluate determinant
  530. det = _det_DOM(M[b, b])
  531. elif method == "bareiss":
  532. det = M[b, b]._eval_det_bareiss(iszerofunc=iszerofunc)
  533. elif method == "berkowitz":
  534. det = M[b, b]._eval_det_berkowitz()
  535. elif method == "lu":
  536. det = M[b, b]._eval_det_lu(iszerofunc=iszerofunc)
  537. dets.append(det)
  538. return Mul(*dets)
  539. # This functions is a candidate for caching if it gets implemented for matrices.
  540. def _det_bareiss(M, iszerofunc=_is_zero_after_expand_mul):
  541. """Compute matrix determinant using Bareiss' fraction-free
  542. algorithm which is an extension of the well known Gaussian
  543. elimination method. This approach is best suited for dense
  544. symbolic matrices and will result in a determinant with
  545. minimal number of fractions. It means that less term
  546. rewriting is needed on resulting formulae.
  547. Parameters
  548. ==========
  549. iszerofunc : function, optional
  550. The function to use to determine zeros when doing an LU decomposition.
  551. Defaults to ``lambda x: x.is_zero``.
  552. TODO: Implement algorithm for sparse matrices (SFF),
  553. http://www.eecis.udel.edu/~saunders/papers/sffge/it5.ps.
  554. """
  555. # Recursively implemented Bareiss' algorithm as per Deanna Richelle Leggett's
  556. # thesis http://www.math.usm.edu/perry/Research/Thesis_DRL.pdf
  557. def bareiss(mat, cumm=1):
  558. if mat.rows == 0:
  559. return mat.one
  560. elif mat.rows == 1:
  561. return mat[0, 0]
  562. # find a pivot and extract the remaining matrix
  563. # With the default iszerofunc, _find_reasonable_pivot slows down
  564. # the computation by the factor of 2.5 in one test.
  565. # Relevant issues: #10279 and #13877.
  566. pivot_pos, pivot_val, _, _ = _find_reasonable_pivot(mat[:, 0], iszerofunc=iszerofunc)
  567. if pivot_pos is None:
  568. return mat.zero
  569. # if we have a valid pivot, we'll do a "row swap", so keep the
  570. # sign of the det
  571. sign = (-1) ** (pivot_pos % 2)
  572. # we want every row but the pivot row and every column
  573. rows = [i for i in range(mat.rows) if i != pivot_pos]
  574. cols = list(range(mat.cols))
  575. tmp_mat = mat.extract(rows, cols)
  576. def entry(i, j):
  577. ret = (pivot_val*tmp_mat[i, j + 1] - mat[pivot_pos, j + 1]*tmp_mat[i, 0]) / cumm
  578. if _get_intermediate_simp_bool(True):
  579. return _dotprodsimp(ret)
  580. elif not ret.is_Atom:
  581. return cancel(ret)
  582. return ret
  583. return sign*bareiss(M._new(mat.rows - 1, mat.cols - 1, entry), pivot_val)
  584. if not M.is_square:
  585. raise NonSquareMatrixError()
  586. if M.rows == 0:
  587. return M.one
  588. # sympy/matrices/tests/test_matrices.py contains a test that
  589. # suggests that the determinant of a 0 x 0 matrix is one, by
  590. # convention.
  591. return bareiss(M)
  592. def _det_berkowitz(M):
  593. """ Use the Berkowitz algorithm to compute the determinant."""
  594. if not M.is_square:
  595. raise NonSquareMatrixError()
  596. if M.rows == 0:
  597. return M.one
  598. # sympy/matrices/tests/test_matrices.py contains a test that
  599. # suggests that the determinant of a 0 x 0 matrix is one, by
  600. # convention.
  601. berk_vector = _berkowitz_vector(M)
  602. return (-1)**(len(berk_vector) - 1) * berk_vector[-1]
  603. # This functions is a candidate for caching if it gets implemented for matrices.
  604. def _det_LU(M, iszerofunc=_iszero, simpfunc=None):
  605. """ Computes the determinant of a matrix from its LU decomposition.
  606. This function uses the LU decomposition computed by
  607. LUDecomposition_Simple().
  608. The keyword arguments iszerofunc and simpfunc are passed to
  609. LUDecomposition_Simple().
  610. iszerofunc is a callable that returns a boolean indicating if its
  611. input is zero, or None if it cannot make the determination.
  612. simpfunc is a callable that simplifies its input.
  613. The default is simpfunc=None, which indicate that the pivot search
  614. algorithm should not attempt to simplify any candidate pivots.
  615. If simpfunc fails to simplify its input, then it must return its input
  616. instead of a copy.
  617. Parameters
  618. ==========
  619. iszerofunc : function, optional
  620. The function to use to determine zeros when doing an LU decomposition.
  621. Defaults to ``lambda x: x.is_zero``.
  622. simpfunc : function, optional
  623. The simplification function to use when looking for zeros for pivots.
  624. """
  625. if not M.is_square:
  626. raise NonSquareMatrixError()
  627. if M.rows == 0:
  628. return M.one
  629. # sympy/matrices/tests/test_matrices.py contains a test that
  630. # suggests that the determinant of a 0 x 0 matrix is one, by
  631. # convention.
  632. lu, row_swaps = M.LUdecomposition_Simple(iszerofunc=iszerofunc,
  633. simpfunc=simpfunc)
  634. # P*A = L*U => det(A) = det(L)*det(U)/det(P) = det(P)*det(U).
  635. # Lower triangular factor L encoded in lu has unit diagonal => det(L) = 1.
  636. # P is a permutation matrix => det(P) in {-1, 1} => 1/det(P) = det(P).
  637. # LUdecomposition_Simple() returns a list of row exchange index pairs, rather
  638. # than a permutation matrix, but det(P) = (-1)**len(row_swaps).
  639. # Avoid forming the potentially time consuming product of U's diagonal entries
  640. # if the product is zero.
  641. # Bottom right entry of U is 0 => det(A) = 0.
  642. # It may be impossible to determine if this entry of U is zero when it is symbolic.
  643. if iszerofunc(lu[lu.rows-1, lu.rows-1]):
  644. return M.zero
  645. # Compute det(P)
  646. det = -M.one if len(row_swaps)%2 else M.one
  647. # Compute det(U) by calculating the product of U's diagonal entries.
  648. # The upper triangular portion of lu is the upper triangular portion of the
  649. # U factor in the LU decomposition.
  650. for k in range(lu.rows):
  651. det *= lu[k, k]
  652. # return det(P)*det(U)
  653. return det
  654. def _minor(M, i, j, method="berkowitz"):
  655. """Return the (i,j) minor of ``M``. That is,
  656. return the determinant of the matrix obtained by deleting
  657. the `i`th row and `j`th column from ``M``.
  658. Parameters
  659. ==========
  660. i, j : int
  661. The row and column to exclude to obtain the submatrix.
  662. method : string, optional
  663. Method to use to find the determinant of the submatrix, can be
  664. "bareiss", "berkowitz" or "lu".
  665. Examples
  666. ========
  667. >>> from sympy import Matrix
  668. >>> M = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
  669. >>> M.minor(1, 1)
  670. -12
  671. See Also
  672. ========
  673. minor_submatrix
  674. cofactor
  675. det
  676. """
  677. if not M.is_square:
  678. raise NonSquareMatrixError()
  679. return M.minor_submatrix(i, j).det(method=method)
  680. def _minor_submatrix(M, i, j):
  681. """Return the submatrix obtained by removing the `i`th row
  682. and `j`th column from ``M`` (works with Pythonic negative indices).
  683. Parameters
  684. ==========
  685. i, j : int
  686. The row and column to exclude to obtain the submatrix.
  687. Examples
  688. ========
  689. >>> from sympy import Matrix
  690. >>> M = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
  691. >>> M.minor_submatrix(1, 1)
  692. Matrix([
  693. [1, 3],
  694. [7, 9]])
  695. See Also
  696. ========
  697. minor
  698. cofactor
  699. """
  700. if i < 0:
  701. i += M.rows
  702. if j < 0:
  703. j += M.cols
  704. if not 0 <= i < M.rows or not 0 <= j < M.cols:
  705. raise ValueError("`i` and `j` must satisfy 0 <= i < ``M.rows`` "
  706. "(%d)" % M.rows + "and 0 <= j < ``M.cols`` (%d)." % M.cols)
  707. rows = [a for a in range(M.rows) if a != i]
  708. cols = [a for a in range(M.cols) if a != j]
  709. return M.extract(rows, cols)