GRAM

This function computes either the controllability Gramian, the observability Gramian, or a Cholesky factor of one of those matrices for a continuous-time state-space model.

A W_c + W_c A^T + B B^T = 0

A^T W_o + W_o A + C^T C = 0

The controllability Gramian measures the input energy needed to reach state directions, while the observability Gramian measures how strongly state directions appear in the outputs. These matrices are standard tools in balanced realization, model reduction, and control system diagnostics.

Excel Usage

=GRAM(A, B, C, D, gram_type)
  • A (list[list], required): State dynamics matrix A.
  • B (list[list], required): Input matrix B.
  • C (list[list], required): Output matrix C.
  • D (list[list], required): Feedthrough matrix D.
  • gram_type (str, required): Type of Gramian computation (‘c’, ‘o’, ‘cf’, or ‘of’).

Returns (list[list]): The requested Gramian matrix.

Example 1: Controllability Gramian

Inputs:

A B C D gram_type
-1 0 1 1 1 0 c
0 -2 1

Excel formula:

=GRAM({-1,0;0,-2}, {1;1}, {1,1}, {0}, "c")

Expected output:

Result
0.5 0.333333
0.333333 0.25
Example 2: Observability Gramian for stable second-order system

Inputs:

A B C D gram_type
-1 0 1 1 1 0 o
0 -2 1

Excel formula:

=GRAM({-1,0;0,-2}, {1;1}, {1,1}, {0}, "o")

Expected output:

Result
0.5 0.333333
0.333333 0.25
Example 3: Cholesky factor of controllability Gramian

Inputs:

A B C D gram_type
-1 0 1 1 1 0 cf
0 -2 1

Excel formula:

=GRAM({-1,0;0,-2}, {1;1}, {1,1}, {0}, "cf")

Expected output:

Result
0.707107 0.471405
0 0.166667
Example 4: Cholesky factor of observability Gramian

Inputs:

A B C D gram_type
-1 0 1 1 1 0 of
0 -2 1

Excel formula:

=GRAM({-1,0;0,-2}, {1;1}, {1,1}, {0}, "of")

Expected output:

Result
0.707107 0.471405
0 0.166667

Python Code

Show Code
import control as ct
import numpy as np
from scipy.linalg import cholesky, solve_continuous_lyapunov

def gram(A, B, C, D, gram_type):
    """
    Compute the Gramian (controllability or observability).

    See: https://python-control.readthedocs.io/en/latest/generated/control.gram.html

    This example function is provided as-is without any representation of accuracy.

    Args:
        A (list[list]): State dynamics matrix A.
        B (list[list]): Input matrix B.
        C (list[list]): Output matrix C.
        D (list[list]): Feedthrough matrix D.
        gram_type (str): Type of Gramian computation ('c', 'o', 'cf', or 'of'). Valid options: Controllability, Observability, Controllability Cholesky, Observability Cholesky.

    Returns:
        list[list]: The requested Gramian matrix.
    """
    try:
        def to_np(x):
            if x is None:
                return None
            if not isinstance(x, list):
                return np.array([[float(x)]])
            if x and not isinstance(x[0], list):
                x = [x]
            return np.array([[float(v) if v is not None and str(v) != "" else 0.0 for v in row] for row in x])

        a_np = to_np(A)
        b_np = to_np(B)
        c_np = to_np(C)
        d_np = to_np(D)

        sys = ct.ss(a_np, b_np, c_np, d_np)

        try:
            W = ct.gram(sys, gram_type)
            return W.tolist()
        except Exception:
            if gram_type == "c":
                W = solve_continuous_lyapunov(a_np, -(b_np @ b_np.T))
                return W.tolist()
            if gram_type == "o":
                W = solve_continuous_lyapunov(a_np.T, -(c_np.T @ c_np))
                return W.tolist()
            if gram_type == "cf":
                Wc = solve_continuous_lyapunov(a_np, -(b_np @ b_np.T))
                Rc = cholesky(Wc, lower=False)
                return Rc.tolist()
            if gram_type == "of":
                Wo = solve_continuous_lyapunov(a_np.T, -(c_np.T @ c_np))
                Ro = cholesky(Wo, lower=False)
                return Ro.tolist()
            return "Error: gram_type must be one of c, o, cf, of"
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

State dynamics matrix A.
Input matrix B.
Output matrix C.
Feedthrough matrix D.
Type of Gramian computation ('c', 'o', 'cf', or 'of').