_decomp_ldl.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. from __future__ import division, print_function, absolute_import
  2. from warnings import warn
  3. import numpy as np
  4. from numpy import (atleast_2d, ComplexWarning, arange, zeros_like, imag, diag,
  5. iscomplexobj, tril, triu, argsort, empty_like)
  6. from .decomp import _asarray_validated
  7. from .lapack import get_lapack_funcs, _compute_lwork
  8. __all__ = ['ldl']
  9. def ldl(A, lower=True, hermitian=True, overwrite_a=False, check_finite=True):
  10. """ Computes the LDLt or Bunch-Kaufman factorization of a symmetric/
  11. hermitian matrix.
  12. This function returns a block diagonal matrix D consisting blocks of size
  13. at most 2x2 and also a possibly permuted unit lower triangular matrix
  14. ``L`` such that the factorization ``A = L D L^H`` or ``A = L D L^T``
  15. holds. If ``lower`` is False then (again possibly permuted) upper
  16. triangular matrices are returned as outer factors.
  17. The permutation array can be used to triangularize the outer factors
  18. simply by a row shuffle, i.e., ``lu[perm, :]`` is an upper/lower
  19. triangular matrix. This is also equivalent to multiplication with a
  20. permutation matrix ``P.dot(lu)`` where ``P`` is a column-permuted
  21. identity matrix ``I[:, perm]``.
  22. Depending on the value of the boolean ``lower``, only upper or lower
  23. triangular part of the input array is referenced. Hence a triangular
  24. matrix on entry would give the same result as if the full matrix is
  25. supplied.
  26. Parameters
  27. ----------
  28. a : array_like
  29. Square input array
  30. lower : bool, optional
  31. This switches between the lower and upper triangular outer factors of
  32. the factorization. Lower triangular (``lower=True``) is the default.
  33. hermitian : bool, optional
  34. For complex-valued arrays, this defines whether ``a = a.conj().T`` or
  35. ``a = a.T`` is assumed. For real-valued arrays, this switch has no
  36. effect.
  37. overwrite_a : bool, optional
  38. Allow overwriting data in ``a`` (may enhance performance). The default
  39. is False.
  40. check_finite : bool, optional
  41. Whether to check that the input matrices contain only finite numbers.
  42. Disabling may give a performance gain, but may result in problems
  43. (crashes, non-termination) if the inputs do contain infinities or NaNs.
  44. Returns
  45. -------
  46. lu : ndarray
  47. The (possibly) permuted upper/lower triangular outer factor of the
  48. factorization.
  49. d : ndarray
  50. The block diagonal multiplier of the factorization.
  51. perm : ndarray
  52. The row-permutation index array that brings lu into triangular form.
  53. Raises
  54. ------
  55. ValueError
  56. If input array is not square.
  57. ComplexWarning
  58. If a complex-valued array with nonzero imaginary parts on the
  59. diagonal is given and hermitian is set to True.
  60. Examples
  61. --------
  62. Given an upper triangular array `a` that represents the full symmetric
  63. array with its entries, obtain `l`, 'd' and the permutation vector `perm`:
  64. >>> import numpy as np
  65. >>> from scipy.linalg import ldl
  66. >>> a = np.array([[2, -1, 3], [0, 2, 0], [0, 0, 1]])
  67. >>> lu, d, perm = ldl(a, lower=0) # Use the upper part
  68. >>> lu
  69. array([[ 0. , 0. , 1. ],
  70. [ 0. , 1. , -0.5],
  71. [ 1. , 1. , 1.5]])
  72. >>> d
  73. array([[-5. , 0. , 0. ],
  74. [ 0. , 1.5, 0. ],
  75. [ 0. , 0. , 2. ]])
  76. >>> perm
  77. array([2, 1, 0])
  78. >>> lu[perm, :]
  79. array([[ 1. , 1. , 1.5],
  80. [ 0. , 1. , -0.5],
  81. [ 0. , 0. , 1. ]])
  82. >>> lu.dot(d).dot(lu.T)
  83. array([[ 2., -1., 3.],
  84. [-1., 2., 0.],
  85. [ 3., 0., 1.]])
  86. Notes
  87. -----
  88. This function uses ``?SYTRF`` routines for symmetric matrices and
  89. ``?HETRF`` routines for Hermitian matrices from LAPACK. See [1]_ for
  90. the algorithm details.
  91. Depending on the ``lower`` keyword value, only lower or upper triangular
  92. part of the input array is referenced. Moreover, this keyword also defines
  93. the structure of the outer factors of the factorization.
  94. .. versionadded:: 1.1.0
  95. See also
  96. --------
  97. cholesky, lu
  98. References
  99. ----------
  100. .. [1] J.R. Bunch, L. Kaufman, Some stable methods for calculating
  101. inertia and solving symmetric linear systems, Math. Comput. Vol.31,
  102. 1977. DOI: 10.2307/2005787
  103. """
  104. a = atleast_2d(_asarray_validated(A, check_finite=check_finite))
  105. if a.shape[0] != a.shape[1]:
  106. raise ValueError('The input array "a" should be square.')
  107. # Return empty arrays for empty square input
  108. if a.size == 0:
  109. return empty_like(a), empty_like(a), np.array([], dtype=int)
  110. n = a.shape[0]
  111. r_or_c = complex if iscomplexobj(a) else float
  112. # Get the LAPACK routine
  113. if r_or_c is complex and hermitian:
  114. s, sl = 'hetrf', 'hetrf_lwork'
  115. if np.any(imag(diag(a))):
  116. warn('scipy.linalg.ldl():\nThe imaginary parts of the diagonal'
  117. 'are ignored. Use "hermitian=False" for factorization of'
  118. 'complex symmetric arrays.', ComplexWarning, stacklevel=2)
  119. else:
  120. s, sl = 'sytrf', 'sytrf_lwork'
  121. solver, solver_lwork = get_lapack_funcs((s, sl), (a,))
  122. lwork = _compute_lwork(solver_lwork, n, lower=lower)
  123. ldu, piv, info = solver(a, lwork=lwork, lower=lower,
  124. overwrite_a=overwrite_a)
  125. if info < 0:
  126. raise ValueError('{} exited with the internal error "illegal value '
  127. 'in argument number {}". See LAPACK documentation '
  128. 'for the error codes.'.format(s.upper(), -info))
  129. swap_arr, pivot_arr = _ldl_sanitize_ipiv(piv, lower=lower)
  130. d, lu = _ldl_get_d_and_l(ldu, pivot_arr, lower=lower, hermitian=hermitian)
  131. lu, perm = _ldl_construct_tri_factor(lu, swap_arr, pivot_arr, lower=lower)
  132. return lu, d, perm
  133. def _ldl_sanitize_ipiv(a, lower=True):
  134. """
  135. This helper function takes the rather strangely encoded permutation array
  136. returned by the LAPACK routines ?(HE/SY)TRF and converts it into
  137. regularized permutation and diagonal pivot size format.
  138. Since FORTRAN uses 1-indexing and LAPACK uses different start points for
  139. upper and lower formats there are certain offsets in the indices used
  140. below.
  141. Let's assume a result where the matrix is 6x6 and there are two 2x2
  142. and two 1x1 blocks reported by the routine. To ease the coding efforts,
  143. we still populate a 6-sized array and fill zeros as the following ::
  144. pivots = [2, 0, 2, 0, 1, 1]
  145. This denotes a diagonal matrix of the form ::
  146. [x x ]
  147. [x x ]
  148. [ x x ]
  149. [ x x ]
  150. [ x ]
  151. [ x]
  152. In other words, we write 2 when the 2x2 block is first encountered and
  153. automatically write 0 to the next entry and skip the next spin of the
  154. loop. Thus, a separate counter or array appends to keep track of block
  155. sizes are avoided. If needed, zeros can be filtered out later without
  156. losing the block structure.
  157. Parameters
  158. ----------
  159. a : ndarray
  160. The permutation array ipiv returned by LAPACK
  161. lower : bool, optional
  162. The switch to select whether upper or lower triangle is chosen in
  163. the LAPACK call.
  164. Returns
  165. -------
  166. swap_ : ndarray
  167. The array that defines the row/column swap operations. For example,
  168. if row two is swapped with row four, the result is [0, 3, 2, 3].
  169. pivots : ndarray
  170. The array that defines the block diagonal structure as given above.
  171. """
  172. n = a.size
  173. swap_ = arange(n)
  174. pivots = zeros_like(swap_, dtype=int)
  175. skip_2x2 = False
  176. # Some upper/lower dependent offset values
  177. # range (s)tart, r(e)nd, r(i)ncrement
  178. x, y, rs, re, ri = (1, 0, 0, n, 1) if lower else (-1, -1, n-1, -1, -1)
  179. for ind in range(rs, re, ri):
  180. # If previous spin belonged already to a 2x2 block
  181. if skip_2x2:
  182. skip_2x2 = False
  183. continue
  184. cur_val = a[ind]
  185. # do we have a 1x1 block or not?
  186. if cur_val > 0:
  187. if cur_val != ind+1:
  188. # Index value != array value --> permutation required
  189. swap_[ind] = swap_[cur_val-1]
  190. pivots[ind] = 1
  191. # Not.
  192. elif cur_val < 0 and cur_val == a[ind+x]:
  193. # first neg entry of 2x2 block identifier
  194. if -cur_val != ind+2:
  195. # Index value != array value --> permutation required
  196. swap_[ind+x] = swap_[-cur_val-1]
  197. pivots[ind+y] = 2
  198. skip_2x2 = True
  199. else: # Doesn't make sense, give up
  200. raise ValueError('While parsing the permutation array '
  201. 'in "scipy.linalg.ldl", invalid entries '
  202. 'found. The array syntax is invalid.')
  203. return swap_, pivots
  204. def _ldl_get_d_and_l(ldu, pivs, lower=True, hermitian=True):
  205. """
  206. Helper function to extract the diagonal and triangular matrices for
  207. LDL.T factorization.
  208. Parameters
  209. ----------
  210. ldu : ndarray
  211. The compact output returned by the LAPACK routing
  212. pivs : ndarray
  213. The sanitized array of {0, 1, 2} denoting the sizes of the pivots. For
  214. every 2 there is a succeeding 0.
  215. lower : bool, optional
  216. If set to False, upper triangular part is considered.
  217. hermitian : bool, optional
  218. If set to False a symmetric complex array is assumed.
  219. Returns
  220. -------
  221. d : ndarray
  222. The block diagonal matrix.
  223. lu : ndarray
  224. The upper/lower triangular matrix
  225. """
  226. is_c = iscomplexobj(ldu)
  227. d = diag(diag(ldu))
  228. n = d.shape[0]
  229. blk_i = 0 # block index
  230. # row/column offsets for selecting sub-, super-diagonal
  231. x, y = (1, 0) if lower else (0, 1)
  232. lu = tril(ldu, -1) if lower else triu(ldu, 1)
  233. diag_inds = arange(n)
  234. lu[diag_inds, diag_inds] = 1
  235. for blk in pivs[pivs != 0]:
  236. # increment the block index and check for 2s
  237. # if 2 then copy the off diagonals depending on uplo
  238. inc = blk_i + blk
  239. if blk == 2:
  240. d[blk_i+x, blk_i+y] = ldu[blk_i+x, blk_i+y]
  241. # If Hermitian matrix is factorized, the cross-offdiagonal element
  242. # should be conjugated.
  243. if is_c and hermitian:
  244. d[blk_i+y, blk_i+x] = ldu[blk_i+x, blk_i+y].conj()
  245. else:
  246. d[blk_i+y, blk_i+x] = ldu[blk_i+x, blk_i+y]
  247. lu[blk_i+x, blk_i+y] = 0.
  248. blk_i = inc
  249. return d, lu
  250. def _ldl_construct_tri_factor(lu, swap_vec, pivs, lower=True):
  251. """
  252. Helper function to construct explicit outer factors of LDL factorization.
  253. If lower is True the permuted factors are multiplied as L(1)*L(2)*...*L(k).
  254. Otherwise, the permuted factors are multiplied as L(k)*...*L(2)*L(1). See
  255. LAPACK documentation for more details.
  256. Parameters
  257. ----------
  258. lu : ndarray
  259. The triangular array that is extracted from LAPACK routine call with
  260. ones on the diagonals.
  261. swap_vec : ndarray
  262. The array that defines the row swapping indices. If k'th entry is m
  263. then rows k,m are swapped. Notice that m'th entry is not necessarily
  264. k to avoid undoing the swapping.
  265. pivs : ndarray
  266. The array that defines the block diagonal structure returned by
  267. _ldl_sanitize_ipiv().
  268. lower : bool, optional
  269. The boolean to switch between lower and upper triangular structure.
  270. Returns
  271. -------
  272. lu : ndarray
  273. The square outer factor which satisfies the L * D * L.T = A
  274. perm : ndarray
  275. The permutation vector that brings the lu to the triangular form
  276. Notes
  277. -----
  278. Note that the original argument "lu" is overwritten.
  279. """
  280. n = lu.shape[0]
  281. perm = arange(n)
  282. # Setup the reading order of the permutation matrix for upper/lower
  283. rs, re, ri = (n-1, -1, -1) if lower else (0, n, 1)
  284. for ind in range(rs, re, ri):
  285. s_ind = swap_vec[ind]
  286. if s_ind != ind:
  287. # Column start and end positions
  288. col_s = ind if lower else 0
  289. col_e = n if lower else ind+1
  290. # If we stumble upon a 2x2 block include both cols in the perm.
  291. if pivs[ind] == (0 if lower else 2):
  292. col_s += -1 if lower else 0
  293. col_e += 0 if lower else 1
  294. lu[[s_ind, ind], col_s:col_e] = lu[[ind, s_ind], col_s:col_e]
  295. perm[[s_ind, ind]] = perm[[ind, s_ind]]
  296. return lu, argsort(perm)