SPECIAL_ORTHO_GROUP

Overview

The SPECIAL_ORTHO_GROUP function generates random rotation matrices from the special orthogonal group SO(N), which is the group of all orthogonal N \times N matrices with determinant +1. These matrices represent proper rotations in N-dimensional Euclidean space, preserving both distances and orientation.

This implementation uses the scipy.stats.special_ortho_group distribution from SciPy. The function draws random samples from the Haar distribution, which is the unique uniform distribution on SO(N). The Haar measure, named after mathematician Alfréd Haar, provides a mathematically rigorous way to sample “uniformly at random” from continuous groups like SO(N). For more mathematical background, see the Wikipedia article on orthogonal groups.

An orthogonal matrix Q satisfies the property:

Q Q^T = Q^T Q = I

where I is the identity matrix and Q^T is the transpose of Q. For SO(N) specifically, the additional constraint \det(Q) = +1 ensures the transformation is a pure rotation without reflection.

The algorithm generates a Haar-distributed orthogonal matrix using scipy.stats.ortho_group, then adjusts the result to guarantee the determinant is +1. For 3D rotations specifically, SciPy also provides scipy.spatial.transform.Rotation.random as an alternative.

Random rotation matrices from SO(N) have applications in:

  • 3D graphics and robotics: Generating random orientations for objects or sensors
  • Monte Carlo simulations: Sampling over rotation spaces
  • Statistical physics: Modeling spin systems and lattice gauge theories
  • Machine learning: Random projections and data augmentation with rotational transformations

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

Excel Usage

=SPECIAL_ORTHO_GROUP(dim, size, seed)
  • dim (int, required): Dimension of the square matrix (must be >= 2).
  • size (int, optional, default: null): Number of matrices to generate (must be >= 1). If omitted, returns a single matrix.
  • seed (int, optional, default: null): Random seed for reproducibility.

Returns (list[list]): 2D orthogonal matrix, or error message string.

Examples

Example 1: Single 3x3 orthogonal matrix

Inputs:

dim seed
3 42

Excel formula:

=SPECIAL_ORTHO_GROUP(3, 42)

Expected output:

Result
-0.6 -0.28396 -0.74791
0.16702 -0.95875 0.23003
-0.78237 0.0131 0.62268

Example 2: Two 3x3 orthogonal matrices stacked

Inputs:

dim size seed
3 2 42

Excel formula:

=SPECIAL_ORTHO_GROUP(3, 2, 42)

Expected output:

Result
-0.6 0.19174 -0.77668
0.16702 -0.91944 -0.35601
-0.78237 -0.34332 0.51964
-0.97717 0.0153 0.2119
-0.15023 0.6555 -0.7401
-0.15022 -0.75504 -0.63824

Example 3: Single 4x4 orthogonal matrix

Inputs:

dim seed
4 42

Excel formula:

=SPECIAL_ORTHO_GROUP(4, 42)

Expected output:

Result
-0.28654 -0.81725 0.35728 -0.34978
-0.07976 0.09445 -0.61237 -0.78085
-0.37363 -0.38245 -0.66888 0.51646
-0.8786 0.4206 0.22352 -0.03467

Example 4: Three 4x4 orthogonal matrices stacked

Inputs:

dim size seed
4 3 42

Excel formula:

=SPECIAL_ORTHO_GROUP(4, 3, 42)

Expected output:

Result
-0.28654 -0.86948 -0.34295 -0.21044
0.07976 -0.03961 -0.49616 0.86365
0.37363 -0.48695 0.70777 0.34977
-0.8786 0.07289 0.36779 0.29578
0.13105 0.57196 0.77031 -0.24962
0.13104 0.53472 -0.60554 -0.57466
-0.88384 0.39686 -0.06705 0.23839
0.42951 0.479 -0.18827 0.74204
0.48258 0.24018 0.02527 0.8419
-0.5577 0.31707 -0.72488 0.25098
-0.47635 -0.71085 0.21804 0.46929
0.47873 -0.58006 -0.65297 -0.08932

Python Code

import numpy as np
from scipy.stats import special_ortho_group as scipy_special_ortho_group

def special_ortho_group(dim, size=None, seed=None):
    """
    Draws random samples from the special orthogonal group SO(N), returning orthogonal matrices with determinant +1.

    See: https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.special_ortho_group.html

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

    Args:
        dim (int): Dimension of the square matrix (must be >= 2).
        size (int, optional): Number of matrices to generate (must be >= 1). If omitted, returns a single matrix. Default is None.
        seed (int, optional): Random seed for reproducibility. Default is None.

    Returns:
        list[list]: 2D orthogonal matrix, or error message string.
    """
    def to_2d_list(arr):
        """Convert numpy arrays to 2D lists."""
        arr = np.asarray(arr)
        if arr.ndim == 2:
            return arr.tolist()
        elif arr.ndim == 3:
            # Multiple matrices: flatten into a single 2D list
            return [row for mat in arr for row in mat.tolist()]
        else:
            return "Invalid output shape from scipy.special_ortho_group."

    def valid_matrix(matrix):
        """Check for invalid values in a 2D matrix."""
        for row in matrix:
            for val in row:
                if not isinstance(val, (int, float)):
                    return False
                if val != val or val in (float('inf'), float('-inf')):
                    return False
        return True

    # Validate dim - accept float if it represents an integer
    if isinstance(dim, float) and dim.is_integer():
        dim = int(dim)
    if not isinstance(dim, int) or dim < 2:
        return "Invalid input: dim must be an integer >= 2."

    # Validate size - accept float if it represents an integer
    if size is not None:
        if isinstance(size, float) and size.is_integer():
            size = int(size)
        if not isinstance(size, int) or size < 1:
            return "Invalid input: size must be an integer >= 1 or None."

    try:
        # Draw samples, only pass size if not None
        rng_seed = int(seed) if seed is not None else None
        if size is None:
            result = scipy_special_ortho_group.rvs(dim, random_state=rng_seed)
        else:
            result = scipy_special_ortho_group.rvs(dim, size=size, random_state=rng_seed)
    except Exception as e:
        return f"scipy.special_ortho_group error: {e}"

    out = to_2d_list(result)

    if isinstance(out, list) and all(isinstance(row, list) for row in out):
        if not valid_matrix(out):
            return "Invalid output: matrix contains non-numeric or special values."
        return out

    return out

Online Calculator