Matrix exponentials¶

In [ ]:
import numpy as np

The function below calculates the matrix exponential $\exp(A)=\sum_{k=0}^{\infty}A^k/k!$.

In [ ]:
def matrix_exp(A):
    """Compute the matrix exponential of A using the Taylor series method.
    
    Parameters:
        A : a square matrix, of shape n x n

    Returns:
        The matrix exponential of A, i.e. the sum of A^k / k! for k = 0, 1, 2, ...
    
    """
    n = A.shape[0] # The number of rows in A.  
    # We do not check directly that A is square, but the next steps will generate
    # errors if it is not.

    # In the loop below, T will be A^k / k!.  Here k is implicitly zero,
    # so T is the identity matrix of the same size as A.
    T = np.eye(n) 
    # In the loop below, S will be the sum of A^j / j! for j = 0, 1, 2, ..., k.
    # Here k is implicitly zero, so S is the identity matrix of the same size as A.
    S = np.eye(n)
    # We will stop the loop when the largest entry in T is less than epsilon.
    epsilon = 1e-10
    # The main stopping criterion is that the largest entry in T should be less 
    # than epsilon, but we also stop if we have computed 100 terms in the series.
    for k in range(1, 100):
        T = T @ A / k # Update the value of T to A^k / k!
        S = S + T # Update the value of S by adding one more term
        if np.max(np.abs(T)) < epsilon:
            break
    return S

Here is an example where $\exp(A)$ has a simple pattern.

In [ ]:
n = 5
A = np.zeros((n, n))
for i in range(n-1):
    A[i, i+1] = 1
B = matrix_exp(A)
print("A")
print(A)
print("")
print("exp(A)")
print(B)

Here is a random example.

In [ ]:
A = np.random.random(16).reshape((4,4))
B = matrix_exp(A)
print("A")
print(A)
print("")
print("exp(A)")
print(B)

It should work out that $\exp(A)\exp(-A)=I$. We can use this to check the correctness of our function: we put $E=\exp(A)\exp(-A)-I$ (which should be zero) and then use linalg.norm() to measure the size of $E$.

In [ ]:
n = 4
A = np.random.random(n*n).reshape((n,n))
print("A")
print(A)
print("")
E = matrix_exp(A) @ matrix_exp(-A) - np.eye(n)
print("Size of error: " + str(np.linalg.norm(E)))
In [ ]: