determinant.py 34 KB

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