Skip to content

Matrix Exponential

The matrix exponential \(e^A\) is fundamental in solving differential equations.

linalg.expm

1. Basic Usage

import numpy as np
from scipy import linalg

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

    exp_A = linalg.expm(A)

    print("A =")
    print(A)
    print()
    print("exp(A) =")
    print(exp_A)

if __name__ == "__main__":
    main()

2. Definition

\[e^A = \sum_{k=0}^{\infty} \frac{A^k}{k!} = I + A + \frac{A^2}{2!} + \frac{A^3}{3!} + \cdots\]

3. Diagonal Matrix

import numpy as np
from scipy import linalg

def main():
    # For diagonal matrices: exp(D) has exp on diagonal
    D = np.diag([1, 2, 3])

    exp_D = linalg.expm(D)

    print("exp(D) =")
    print(exp_D)
    print()
    print("Diagonal elements:", np.diag(exp_D))
    print("Expected:", np.exp([1, 2, 3]))

if __name__ == "__main__":
    main()

Properties

1. exp(0) = I

import numpy as np
from scipy import linalg

def main():
    n = 3
    Z = np.zeros((n, n))

    print("exp(0) =")
    print(linalg.expm(Z))

if __name__ == "__main__":
    main()

2. exp(A) exp(-A) = I

import numpy as np
from scipy import linalg

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

    exp_A = linalg.expm(A)
    exp_neg_A = linalg.expm(-A)

    print("exp(A) @ exp(-A) =")
    print((exp_A @ exp_neg_A).round(10))

if __name__ == "__main__":
    main()

3. Commuting Matrices

import numpy as np
from scipy import linalg

def main():
    # If AB = BA, then exp(A+B) = exp(A) exp(B)
    A = np.diag([1, 2])
    B = np.diag([3, 4])

    exp_ApB = linalg.expm(A + B)
    exp_A_exp_B = linalg.expm(A) @ linalg.expm(B)

    print("exp(A+B) =")
    print(exp_ApB)
    print()
    print("exp(A) @ exp(B) =")
    print(exp_A_exp_B)

if __name__ == "__main__":
    main()

Applications

1. Linear ODE Solution

import numpy as np
from scipy import linalg

def main():
    # dx/dt = Ax, x(0) = x0
    # Solution: x(t) = exp(At) @ x0

    A = np.array([[-1, 1],
                  [0, -2]])
    x0 = np.array([1, 1])

    # Solution at t = 1
    t = 1
    x_t = linalg.expm(A * t) @ x0

    print(f"x(0) = {x0}")
    print(f"x({t}) = {x_t}")

if __name__ == "__main__":
    main()

2. State Transition Matrix

import numpy as np
from scipy import linalg
import matplotlib.pyplot as plt

def main():
    A = np.array([[-0.5, 1],
                  [-1, -0.5]])
    x0 = np.array([1, 0])

    times = np.linspace(0, 10, 100)
    trajectory = np.array([linalg.expm(A * t) @ x0 for t in times])

    fig, ax = plt.subplots()
    ax.plot(trajectory[:, 0], trajectory[:, 1])
    ax.set_xlabel('x1')
    ax.set_ylabel('x2')
    ax.set_title('System Trajectory')
    ax.axis('equal')
    plt.show()

if __name__ == "__main__":
    main()

3. Rotation Matrix

import numpy as np
from scipy import linalg

def main():
    # Skew-symmetric matrix generates rotation
    theta = np.pi / 4
    A = np.array([[0, -theta],
                  [theta, 0]])

    R = linalg.expm(A)

    print(f"Rotation by {np.degrees(theta)} degrees:")
    print(R)
    print()
    print("Expected:")
    print(np.array([[np.cos(theta), -np.sin(theta)],
                    [np.sin(theta), np.cos(theta)]]))

if __name__ == "__main__":
    main()

Summary

Function Description
linalg.expm(A) Matrix exponential
linalg.expm_frechet(A, E) Frechet derivative

Key: \(e^A \neq\) element-wise exp. Use np.exp(A) for element-wise.