Skip to content

hyperbolic

pleat.geometries.hyperbolic

Hyperbolic geometry via the Poincare disk model.

Points are complex numbers inside the open unit disk; isometries are Mobius transformations represented as 2x2 complex matrices. Centroids are computed by lifting to the hyperboloid model, taking a weighted Euclidean mean, and projecting back.

MobiusTransform

MobiusTransform(mat)

A Mobius transformation represented as a 2x2 complex matrix.

Source code in pleat/geometries/hyperbolic.py
def __init__(self, mat):
    if not isinstance(mat, np.ndarray):
        mat = np.array(mat)
    assert mat.shape == (2, 2), f"{mat.shape}"
    self.mat = mat

PoincareDiskModel

Bases: Geometry

Hyperbolic geometry using the Poincare disk model with complex coordinates.

apply_mobius

apply_mobius(mat, points)

Apply a Mobius transformation given by a 2x2 matrix to complex-valued points.

Source code in pleat/geometries/hyperbolic.py
def apply_mobius(mat, points):
    """Apply a Mobius transformation given by a 2x2 matrix to complex-valued points."""
    return (mat[0, 0] * points + mat[0, 1]) / (mat[1, 0] * points + mat[1, 1])

complex_to_real

complex_to_real(z)

Convert complex numbers to real 2D coordinate arrays.

Source code in pleat/geometries/hyperbolic.py
def complex_to_real(z):
    """Convert complex numbers to real 2D coordinate arrays."""
    return np.stack([z.real, z.imag], axis=-1)

real_to_complex

real_to_complex(x)

Convert real 2D coordinate arrays to complex numbers.

Source code in pleat/geometries/hyperbolic.py
def real_to_complex(x):
    """Convert real 2D coordinate arrays to complex numbers."""
    assert x.shape[-1] == 2
    return x[..., 0] + 1j * x[..., 1]

poincare_to_hyperboloid

poincare_to_hyperboloid(z)

Map Poincare disk coordinates to the hyperboloid model.

Source code in pleat/geometries/hyperbolic.py
def poincare_to_hyperboloid(z):
    """Map Poincare disk coordinates to the hyperboloid model."""
    pts = complex_to_real(z)
    squared_norm = (pts**2).sum(-1, keepdims=True)
    return np.concatenate([(1 + squared_norm), 2 * pts], axis=-1) / (1 - squared_norm)

hyperboloid_to_poincare

hyperboloid_to_poincare(v)

Map hyperboloid model coordinates back to the Poincare disk.

Source code in pleat/geometries/hyperbolic.py
def hyperboloid_to_poincare(v):
    """Map hyperboloid model coordinates back to the Poincare disk."""
    return real_to_complex(v[..., 1:] / (1 + v[..., :1]))

hyperboloid_centroid

hyperboloid_centroid(vs, ms=None, axis=None)

Compute the centroid on the hyperboloid model, optionally weighted by masses.

Source code in pleat/geometries/hyperbolic.py
def hyperboloid_centroid(vs, ms=None, axis=None):
    """Compute the centroid on the hyperboloid model, optionally weighted by masses."""
    if axis is None:
        assert len(vs.shape) == 2
        axis = 0
    ms = np.ones(list(vs.shape[:-1]), dtype=vs.dtype) if ms is None else ms
    mean = (vs * ms[..., None]).mean(axis)
    mean /= np.sqrt(mean[..., :1] ** 2 - (mean[..., 1:] ** 2).sum(-1, keepdims=True))
    return mean

poincare_centroid

poincare_centroid(zs, ms=None, axis=None)

Compute the centroid of points in the Poincare disk via the hyperboloid model.

Source code in pleat/geometries/hyperbolic.py
def poincare_centroid(zs, ms=None, axis=None):
    """Compute the centroid of points in the Poincare disk via the hyperboloid model."""
    return hyperboloid_to_poincare(hyperboloid_centroid(poincare_to_hyperboloid(zs), ms, axis))