Schur complement of a symmetric matrix X given as a 2x2 block matrix consisting of matrices `A`, `B` and `C`. Matrix `A` must be quadratic and non-singular. In case `A` is singular, a pseudo-inverse may be provided using the `pseudo_inv` argument. | Link to Wiki: https://en
(
mat_a: np.ndarray,
mat_b: np.ndarray,
mat_c: np.ndarray,
pseudo_inv: np.ndarray | None = None,
)
| 5 | |
| 6 | |
| 7 | def schur_complement( |
| 8 | mat_a: np.ndarray, |
| 9 | mat_b: np.ndarray, |
| 10 | mat_c: np.ndarray, |
| 11 | pseudo_inv: np.ndarray | None = None, |
| 12 | ) -> np.ndarray: |
| 13 | """ |
| 14 | Schur complement of a symmetric matrix X given as a 2x2 block matrix |
| 15 | consisting of matrices `A`, `B` and `C`. |
| 16 | Matrix `A` must be quadratic and non-singular. |
| 17 | In case `A` is singular, a pseudo-inverse may be provided using |
| 18 | the `pseudo_inv` argument. |
| 19 | |
| 20 | | Link to Wiki: https://en.wikipedia.org/wiki/Schur_complement |
| 21 | | See also Convex Optimization - Boyd and Vandenberghe, A.5.5 |
| 22 | |
| 23 | >>> import numpy as np |
| 24 | >>> a = np.array([[1, 2], [2, 1]]) |
| 25 | >>> b = np.array([[0, 3], [3, 0]]) |
| 26 | >>> c = np.array([[2, 1], [6, 3]]) |
| 27 | >>> schur_complement(a, b, c) |
| 28 | array([[ 5., -5.], |
| 29 | [ 0., 6.]]) |
| 30 | """ |
| 31 | shape_a = np.shape(mat_a) |
| 32 | shape_b = np.shape(mat_b) |
| 33 | shape_c = np.shape(mat_c) |
| 34 | |
| 35 | if shape_a[0] != shape_b[0]: |
| 36 | msg = ( |
| 37 | "Expected the same number of rows for A and B. " |
| 38 | f"Instead found A of size {shape_a} and B of size {shape_b}" |
| 39 | ) |
| 40 | raise ValueError(msg) |
| 41 | |
| 42 | if shape_b[1] != shape_c[1]: |
| 43 | msg = ( |
| 44 | "Expected the same number of columns for B and C. " |
| 45 | f"Instead found B of size {shape_b} and C of size {shape_c}" |
| 46 | ) |
| 47 | raise ValueError(msg) |
| 48 | |
| 49 | a_inv = pseudo_inv |
| 50 | if a_inv is None: |
| 51 | try: |
| 52 | a_inv = np.linalg.inv(mat_a) |
| 53 | except np.linalg.LinAlgError: |
| 54 | raise ValueError( |
| 55 | "Input matrix A is not invertible. Cannot compute Schur complement." |
| 56 | ) |
| 57 | |
| 58 | return mat_c - mat_b.T @ a_inv @ mat_b |
| 59 | |
| 60 | |
| 61 | class TestSchurComplement(unittest.TestCase): |
no outgoing calls