aboutsummaryrefslogtreecommitdiff
path: root/python_originals/polar.py
diff options
context:
space:
mode:
Diffstat (limited to 'python_originals/polar.py')
-rw-r--r--python_originals/polar.py105
1 files changed, 105 insertions, 0 deletions
diff --git a/python_originals/polar.py b/python_originals/polar.py
new file mode 100644
index 0000000..69b6a35
--- /dev/null
+++ b/python_originals/polar.py
@@ -0,0 +1,105 @@
+def polar(a, side="right"):
+ """
+ Compute the polar decomposition.
+
+ Returns the factors of the polar decomposition [1]_ `u` and `p` such
+ that ``a = up`` (if `side` is "right") or ``a = pu`` (if `side` is
+ "left"), where `p` is positive semidefinite. Depending on the shape
+ of `a`, either the rows or columns of `u` are orthonormal. When `a`
+ is a square array, `u` is a square unitary array. When `a` is not
+ square, the "canonical polar decomposition" [2]_ is computed.
+
+ Parameters
+ ----------
+ a : (m, n) array_like
+ The array to be factored.
+ side : {'left', 'right'}, optional
+ Determines whether a right or left polar decomposition is computed.
+ If `side` is "right", then ``a = up``. If `side` is "left", then
+ ``a = pu``. The default is "right".
+
+ Returns
+ -------
+ u : (m, n) ndarray
+ If `a` is square, then `u` is unitary. If m > n, then the columns
+ of `a` are orthonormal, and if m < n, then the rows of `u` are
+ orthonormal.
+ p : ndarray
+ `p` is Hermitian positive semidefinite. If `a` is nonsingular, `p`
+ is positive definite. The shape of `p` is (n, n) or (m, m), depending
+ on whether `side` is "right" or "left", respectively.
+
+ References
+ ----------
+ .. [1] R. A. Horn and C. R. Johnson, "Matrix Analysis", Cambridge
+ University Press, 1985.
+ .. [2] N. J. Higham, "Functions of Matrices: Theory and Computation",
+ SIAM, 2008.
+
+ Examples
+ --------
+ >>> import numpy as np
+ >>> from scipy.linalg import polar
+ >>> a = np.array([[1, -1], [2, 4]])
+ >>> u, p = polar(a)
+ >>> u
+ array([[ 0.85749293, -0.51449576],
+ [ 0.51449576, 0.85749293]])
+ >>> p
+ array([[ 1.88648444, 1.2004901 ],
+ [ 1.2004901 , 3.94446746]])
+
+ A non-square example, with m < n:
+
+ >>> b = np.array([[0.5, 1, 2], [1.5, 3, 4]])
+ >>> u, p = polar(b)
+ >>> u
+ array([[-0.21196618, -0.42393237, 0.88054056],
+ [ 0.39378971, 0.78757942, 0.4739708 ]])
+ >>> p
+ array([[ 0.48470147, 0.96940295, 1.15122648],
+ [ 0.96940295, 1.9388059 , 2.30245295],
+ [ 1.15122648, 2.30245295, 3.65696431]])
+ >>> u.dot(p) # Verify the decomposition.
+ array([[ 0.5, 1. , 2. ],
+ [ 1.5, 3. , 4. ]])
+ >>> u.dot(u.T) # The rows of u are orthonormal.
+ array([[ 1.00000000e+00, -2.07353665e-17],
+ [ -2.07353665e-17, 1.00000000e+00]])
+
+ Another non-square example, with m > n:
+
+ >>> c = b.T
+ >>> u, p = polar(c)
+ >>> u
+ array([[-0.21196618, 0.39378971],
+ [-0.42393237, 0.78757942],
+ [ 0.88054056, 0.4739708 ]])
+ >>> p
+ array([[ 1.23116567, 1.93241587],
+ [ 1.93241587, 4.84930602]])
+ >>> u.dot(p) # Verify the decomposition.
+ array([[ 0.5, 1.5],
+ [ 1. , 3. ],
+ [ 2. , 4. ]])
+ >>> u.T.dot(u) # The columns of u are orthonormal.
+ array([[ 1.00000000e+00, -1.26363763e-16],
+ [ -1.26363763e-16, 1.00000000e+00]])
+
+ """
+ if side not in ['right', 'left']:
+ raise ValueError("`side` must be either 'right' or 'left'")
+ a = np.asarray(a)
+ if a.ndim != 2:
+ raise ValueError("`a` must be a 2-D array.")
+
+ w, s, vh = svd(a, full_matrices=False)
+ u = w.dot(vh)
+ if side == 'right':
+ # a = up
+ p = (vh.T.conj() * s).dot(vh)
+ else:
+ # a = pu
+ p = (w * s).dot(w.T.conj())
+ return u, p
+