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.
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
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
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))
|