Skip to main content
`Ctrl`+`K`
scipy.linalg.

# cossin

scipy.linalg.cossin(X, p=None, q=None, separate=False, swap_sign=False, compute_u=True, compute_vh=True)

  Compute the cosine-sine (CS) decomposition of an orthogonal/unitary matrix.

  X is an `(m, m)` orthogonal/unitary matrix, partitioned as the following where upper left block has the shape of `(p, q)`:

                                 ┌                   ┐
                                 │ I  0  0 │ 0  0  0 │
      ┌           ┐   ┌         ┐│ 0  C  0 │ 0 -S  0 │┌         ┐*
      │ X11 │ X12 │   │ U1 │    ││ 0  0  0 │ 0  0 -I ││ V1 │    │
      │ ────┼──── │ = │────┼────││─────────┼─────────││────┼────│
      │ X21 │ X22 │   │    │ U2 ││ 0  0  0 │ I  0  0 ││    │ V2 │
      └           ┘   └         ┘│ 0  S  0 │ 0  C  0 │└         ┘
                                 │ 0  0  I │ 0  0  0 │
                                 └                   ┘

  `U1`, `U2`, `V1`, `V2` are square orthogonal/unitary matrices of dimensions `(p,p)`, `(m-p,m-p)`, `(q,q)`, and `(m-q,m-q)` respectively, and `C` and `S` are `(r, r)` nonnegative diagonal matrices satisfying `C^2 + S^2 = I` where `r = min(p, m-p, q, m-q)`.

  Moreover, the rank of the identity matrices are `min(p, q) - r`, `min(p, m - q) - r`, `min(m - p, q) - r`, and `min(m - p, m - q) - r` respectively.

  X can be supplied either by itself and block specifications p, q or its subblocks in an iterable from which the shapes would be derived. See the examples below.

  The documentation is written assuming array arguments are of specified “core” shapes. However, array argument(s) of this function may have additional “batch” dimensions prepended to the core shape. In this case, the array is treated as a batch of lower-dimensional slices; see Batched Linear Operations for details.

  Parameters:
    X array_like, iterable

      complex unitary or real orthogonal matrix to be decomposed, or iterable of subblocks `X11`, `X12`, `X21`, `X22`, when `p`, `q` are omitted.

    p int, optional

      Number of rows of the upper left block `X11`, used only when X is given as an array.

    q int, optional

      Number of columns of the upper left block `X11`, used only when X is given as an array.

    separate bool, optional

      if `True`, the low level components are returned instead of the matrix factors, i.e. `(u1,u2)`, `theta`, `(v1h,v2h)` instead of `u`, `cs`, `vh`.

    swap_sign bool, optional

      if `True`, the `-S`, `-I` block will be the bottom left, otherwise (by default) they will be in the upper right block.

    compute_u bool, optional

      if `False`, `u` won’t be computed and an empty array is returned.

    compute_vh bool, optional

      if `False`, `vh` won’t be computed and an empty array is returned.

  Returns:
    u ndarray

      When `compute_u=True`, contains the block diagonal orthogonal/unitary matrix consisting of the blocks `U1` (`p` x `p`) and `U2` (`m-p` x `m-p`) orthogonal/unitary matrices. If `separate=True`, this contains the tuple of `(U1, U2)`.

    cs ndarray
      The cosine-sine factor with the structure described above.

        If `separate=True`, this contains the `theta` array containing the angles in radians.

    vh ndarray

      When `compute_vh=True`, contains the block diagonal orthogonal/unitary matrix consisting of the blocks ``V1H` (`q` x `q`) and `V2H` (`m-q` x `m-q`) orthogonal/unitary matrices. If `separate=True`, this contains the tuple of `(V1H, V2H)`.

  References

  [1]

  Brian D. Sutton. Computing the complete CS decomposition. Numer. Algorithms, 50(1):33-65, 2009.

  Examples

      >>> import numpy as np
      >>> from scipy.linalg import cossin
      >>> from scipy.stats import unitary_group
      >>> x = unitary_group.rvs(4)
      >>> u, cs, vdh = cossin(x, p=2, q=2)
      >>> np.allclose(x, u @ cs @ vdh)
      True

  Same can be entered via subblocks without the need of `p` and `q`. Also let’s skip the computation of `u`

      >>> ue, cs, vdh = cossin((x[:2, :2], x[:2, 2:], x[2:, :2], x[2:, 2:]),
      ...                      compute_u=False)
      >>> print(ue)
      []
      >>> np.allclose(x, u @ cs @ vdh)
      True