rotate_spharm_coeffs#

ktch.harmonic.rotate_spharm_coeffs(coeffs, rotation, *, domain, n_dim=3)[source]#

Rotate flat SPHARM coefficients in a specific domain.

Coefficients carry two independent rotations.

  • "codomain" re-poses the shape, x'(p) = R x(p), leaving the correspondence untouched. Use it for a convention change applied to every specimen.

  • "parameter" re-indexes the parameter sphere, x'(R p) = x(p). The point set does not move. Use it to bring an associated field along with its geometry.

  • "coupled" applies both, x'(R p) = R x(p). This is the residual freedom of first_order registration (M1 = U S Vᵀ is unchanged by (U, V) -> (UG, VG)), so it moves a specimen to a different representative.

A specimen on a different representative looks like one rotated by 180 degrees about a principal axis. Correcting that with "codomain" fixes the pose and leaves the correspondence inconsistent with the remaining sample.

Parameters:
coeffsarray-like of shape (n_features,) or (n_samples, n_features)

Real SPHARM coefficients in the axis-major flat layout produced by SphericalHarmonicAnalysis.transform() ([cx_0_0, cx_1_-1, ..., cy_..., cz_...]). The output has the same shape.

rotationarray-like of shape (k, k) or (n_samples, k, k)

Orthogonal matrix, or one per sample. k is n_dim for domain="codomain" and 3 otherwise. Improper matrices (det=-1) are accepted, and the two domains differ: in the parameter domain one reverses the parameterization without moving the shape, in the codomain it mirrors the shape.

domain{“parameter”, “codomain”, “coupled”}

Which domain the rotation applies to.

n_dimint, default=3

Codomain dimension, needed to split the flat width (n_dim * (l_max + 1) ** 2), which does not decompose on its own. domain="coupled" requires 3.

Returns:
ndarray

Rotated coefficients, same shape and layout as coeffs.

Raises:
ValueError

For an unknown domain, a width that cannot be split with the given n_dim, a rotation of the wrong shape or count, a non-orthogonal rotation, or domain="coupled" with n_dim != 3.

See also

rotate_parameter_sphere

the same parameter-domain rotation on the ((l_max+1)**2, D) layout, when that is the shape already held.

Examples

>>> import numpy as np
>>> from ktch.harmonic import SphericalHarmonicRegistration, rotate_spharm_coeffs
>>> coeffs = np.random.default_rng(0).standard_normal((4, 3 * (3 + 1) ** 2))
>>> registered = SphericalHarmonicRegistration(scale=False).fit_transform(coeffs)
>>> flip = np.diag([1.0, -1.0, -1.0])  # a 180-degree turn about x
>>> corrected = rotate_spharm_coeffs(registered, flip, domain="coupled")
>>> corrected.shape
(4, 48)