Skip to content

Matrix Square Root

The matrix square root \(X\) satisfies \(X^2 = A\).

Mental Model

The matrix square root finds \(X\) such that multiplying \(X\) by itself recovers the original matrix \(A\). Unlike the scalar case, a matrix can have multiple square roots (or none at all for certain singular matrices). SciPy computes the principal square root via the Schur decomposition, which is numerically stable and works even when eigenvalues are complex.

linalg.sqrtm

1. Basic Usage

```python import numpy as np from scipy import linalg

def main(): A = np.array([[4, 0], [0, 9]])

sqrt_A = linalg.sqrtm(A)

print("A =")
print(A)
print()
print("sqrt(A) =")
print(sqrt_A)

if name == "main": main() ```

2. Verify X² = A

```python import numpy as np from scipy import linalg

def main(): A = np.array([[4, 2], [2, 3]])

X = linalg.sqrtm(A)

print("sqrt(A) =")
print(X)
print()
print("sqrt(A) @ sqrt(A) =")
print((X @ X).round(10))
print()
print("Original A =")
print(A)

if name == "main": main() ```

3. Non-Unique

```python import numpy as np from scipy import linalg

def main(): A = np.array([[4, 0], [0, 4]])

# Principal square root
X1 = linalg.sqrtm(A)

# Another square root
X2 = -X1  # (-X)² = X² = A

print("Principal sqrt(A):")
print(X1)
print()
print("Another sqrt(A):")
print(X2)
print()
print("Both satisfy X² = A:")
print(f"X1² = A: {np.allclose(X1 @ X1, A)}")
print(f"X2² = A: {np.allclose(X2 @ X2, A)}")

if name == "main": main() ```

Properties

1. Positive Definite Input

```python import numpy as np from scipy import linalg

def main(): # Positive definite -> real square root A = np.array([[4, 2], [2, 5]])

X = linalg.sqrtm(A)

print("A is positive definite")
print("sqrt(A) is real:")
print(X.real.round(6))

if name == "main": main() ```

2. Negative Eigenvalues

```python import numpy as np from scipy import linalg

def main(): # Negative eigenvalues -> complex square root A = np.array([[-1, 0], [0, 4]])

X = linalg.sqrtm(A)

print("A has negative eigenvalue:")
print(A)
print()
print("sqrt(A) is complex:")
print(X)

if name == "main": main() ```

Applications

1. Whitening Transform

```python import numpy as np from scipy import linalg

def main(): np.random.seed(42)

# Covariance matrix
Sigma = np.array([[2, 1],
                  [1, 2]])

# Whitening: W = Sigma^{-1/2}
Sigma_sqrt = linalg.sqrtm(Sigma)
W = np.linalg.inv(Sigma_sqrt)

# Generate correlated data
data = np.random.randn(1000, 2) @ linalg.sqrtm(Sigma)

# Whiten
whitened = data @ W

print("Original covariance:")
print(np.cov(data.T).round(2))
print()
print("Whitened covariance:")
print(np.cov(whitened.T).round(2))

if name == "main": main() ```

2. Geometric Mean

```python import numpy as np from scipy import linalg

def main(): A = np.array([[4, 0], [0, 9]]) B = np.array([[1, 0], [0, 4]])

# Matrix geometric mean: A#B = A^{1/2} (A^{-1/2} B A^{-1/2})^{1/2} A^{1/2}
A_sqrt = linalg.sqrtm(A)
A_inv_sqrt = linalg.sqrtm(np.linalg.inv(A))

inner = A_inv_sqrt @ B @ A_inv_sqrt
geo_mean = A_sqrt @ linalg.sqrtm(inner) @ A_sqrt

print("Geometric mean A#B:")
print(geo_mean.real.round(6))

if name == "main": main() ```

Summary

Function Description
linalg.sqrtm(A) Principal matrix square root

Key: Returns principal square root. May be complex if A has negative eigenvalues.


Exercises

Exercise 1. Compute the matrix square root of \(A = \begin{pmatrix} 5 & 2 \\ 2 & 8 \end{pmatrix}\) and verify that \(X^2 = A\) by checking \(\|X^2 - A\|_F < 10^{-12}\). Also verify that the square root is real (since \(A\) is positive definite).

Solution to Exercise 1
import numpy as np
from scipy import linalg

A = np.array([[5, 2],
               [2, 8]], dtype=float)

X = linalg.sqrtm(A)
error = np.linalg.norm(X @ X - A)
is_real = np.allclose(X.imag, 0) if np.iscomplexobj(X) else True

print(f"sqrt(A) =\n{X.real.round(6)}")
print(f"X^2 - A error: {error:.2e}")
print(f"Result is real: {is_real}")

Exercise 2. Implement the whitening transform for the covariance matrix \(\Sigma = \begin{pmatrix} 4 & 2 \\ 2 & 3 \end{pmatrix}\). Compute \(W = \Sigma^{-1/2}\) using sqrtm and np.linalg.inv. Generate 1000 correlated samples from \(N(0, \Sigma)\), apply the whitening transform, and verify that the sample covariance of the whitened data is approximately the identity.

Solution to Exercise 2
import numpy as np
from scipy import linalg

np.random.seed(0)
Sigma = np.array([[4, 2],
                   [2, 3]], dtype=float)

Sigma_sqrt = linalg.sqrtm(Sigma)
W = np.linalg.inv(Sigma_sqrt.real)

# Generate correlated samples
samples = np.random.randn(1000, 2) @ Sigma_sqrt.real

# Whiten
whitened = samples @ W
cov_whitened = np.cov(whitened.T)

print(f"Whitened covariance:\n{cov_whitened.round(2)}")
print(f"Close to identity: {np.allclose(cov_whitened, np.eye(2), atol=0.15)}")

Exercise 3. Compute the matrix geometric mean of \(A = \begin{pmatrix} 4 & 1 \\ 1 & 3 \end{pmatrix}\) and \(B = \begin{pmatrix} 2 & 0 \\ 0 & 5 \end{pmatrix}\) using the formula \(A \# B = A^{1/2} (A^{-1/2} B A^{-1/2})^{1/2} A^{1/2}\). Verify that the result is symmetric and its eigenvalues lie between those of \(A\) and \(B\).

Solution to Exercise 3
import numpy as np
from scipy import linalg

A = np.array([[4, 1],
               [1, 3]], dtype=float)
B = np.array([[2, 0],
               [0, 5]], dtype=float)

A_sqrt = linalg.sqrtm(A).real
A_inv_sqrt = linalg.sqrtm(np.linalg.inv(A)).real

inner = A_inv_sqrt @ B @ A_inv_sqrt
geo_mean = A_sqrt @ linalg.sqrtm(inner).real @ A_sqrt

print(f"Geometric mean A#B:\n{geo_mean.round(6)}")
print(f"Symmetric: {np.allclose(geo_mean, geo_mean.T)}")

eig_geo = np.sort(np.linalg.eigvalsh(geo_mean))
eig_A = np.sort(np.linalg.eigvalsh(A))
eig_B = np.sort(np.linalg.eigvalsh(B))
print(f"Eigenvalues of A: {eig_A}")
print(f"Eigenvalues of A#B: {eig_geo.round(6)}")
print(f"Eigenvalues of B: {eig_B}")