_decomp_polar.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. import numpy as np
  2. from scipy._lib._util import _apply_over_batch
  3. from scipy.linalg import svd
  4. __all__ = ['polar']
  5. @_apply_over_batch(('a', 2))
  6. def polar(a, side="right"):
  7. """
  8. Compute the polar decomposition.
  9. Returns the factors of the polar decomposition [1]_ `u` and `p` such
  10. that ``a = up`` (if `side` is "right") or ``a = pu`` (if `side` is
  11. "left"), where `p` is positive semidefinite. Depending on the shape
  12. of `a`, either the rows or columns of `u` are orthonormal. When `a`
  13. is a square array, `u` is a square unitary array. When `a` is not
  14. square, the "canonical polar decomposition" [2]_ is computed.
  15. Parameters
  16. ----------
  17. a : (m, n) array_like
  18. The array to be factored.
  19. side : {'left', 'right'}, optional
  20. Determines whether a right or left polar decomposition is computed.
  21. If `side` is "right", then ``a = up``. If `side` is "left", then
  22. ``a = pu``. The default is "right".
  23. Returns
  24. -------
  25. u : (m, n) ndarray
  26. If `a` is square, then `u` is unitary. If m > n, then the columns
  27. of `u` are orthonormal, and if m < n, then the rows of `u` are
  28. orthonormal.
  29. p : ndarray
  30. `p` is Hermitian positive semidefinite. If `a` is nonsingular, `p`
  31. is positive definite. The shape of `p` is (n, n) or (m, m), depending
  32. on whether `side` is "right" or "left", respectively.
  33. References
  34. ----------
  35. .. [1] R. A. Horn and C. R. Johnson, "Matrix Analysis", Cambridge
  36. University Press, 1985.
  37. .. [2] N. J. Higham, "Functions of Matrices: Theory and Computation",
  38. SIAM, 2008.
  39. Examples
  40. --------
  41. >>> import numpy as np
  42. >>> from scipy.linalg import polar
  43. >>> a = np.array([[1, -1], [2, 4]])
  44. >>> u, p = polar(a)
  45. >>> u
  46. array([[ 0.85749293, -0.51449576],
  47. [ 0.51449576, 0.85749293]])
  48. >>> p
  49. array([[ 1.88648444, 1.2004901 ],
  50. [ 1.2004901 , 3.94446746]])
  51. A non-square example, with m < n:
  52. >>> b = np.array([[0.5, 1, 2], [1.5, 3, 4]])
  53. >>> u, p = polar(b)
  54. >>> u
  55. array([[-0.21196618, -0.42393237, 0.88054056],
  56. [ 0.39378971, 0.78757942, 0.4739708 ]])
  57. >>> p
  58. array([[ 0.48470147, 0.96940295, 1.15122648],
  59. [ 0.96940295, 1.9388059 , 2.30245295],
  60. [ 1.15122648, 2.30245295, 3.65696431]])
  61. >>> u.dot(p) # Verify the decomposition.
  62. array([[ 0.5, 1. , 2. ],
  63. [ 1.5, 3. , 4. ]])
  64. >>> u.dot(u.T) # The rows of u are orthonormal.
  65. array([[ 1.00000000e+00, -2.07353665e-17],
  66. [ -2.07353665e-17, 1.00000000e+00]])
  67. Another non-square example, with m > n:
  68. >>> c = b.T
  69. >>> u, p = polar(c)
  70. >>> u
  71. array([[-0.21196618, 0.39378971],
  72. [-0.42393237, 0.78757942],
  73. [ 0.88054056, 0.4739708 ]])
  74. >>> p
  75. array([[ 1.23116567, 1.93241587],
  76. [ 1.93241587, 4.84930602]])
  77. >>> u.dot(p) # Verify the decomposition.
  78. array([[ 0.5, 1.5],
  79. [ 1. , 3. ],
  80. [ 2. , 4. ]])
  81. >>> u.T.dot(u) # The columns of u are orthonormal.
  82. array([[ 1.00000000e+00, -1.26363763e-16],
  83. [ -1.26363763e-16, 1.00000000e+00]])
  84. """
  85. if side not in ['right', 'left']:
  86. raise ValueError("`side` must be either 'right' or 'left'")
  87. a = np.asarray(a)
  88. if a.ndim != 2:
  89. raise ValueError("`a` must be a 2-D array.")
  90. w, s, vh = svd(a, full_matrices=False)
  91. u = w.dot(vh)
  92. if side == 'right':
  93. # a = up
  94. p = (vh.T.conj() * s).dot(vh)
  95. else:
  96. # a = pu
  97. p = (w * s).dot(w.T.conj())
  98. return u, p