The Bolza Surface
The Bolza surface is a compact genus-2 surface of constant negative
curvature (\(-1\)) — a closed hyperbolic counterpart to the sphere. It is
the quotient of the Poincaré disk by the Fuchsian group that glues
opposite sides of a regular hyperbolic octagon (interior angle \(\pi/4\)).
kmeanssa-ng ships it as BolzaSurface (via create_bolza_surface),
and it is the one built-in space with no geomstats backend: its
geometry is entirely closed-form and quotient-aware.
Why it is special
Unlike the sphere or the hyperboloid, the Bolza surface is a
quotient: points glued across the octagon’s boundary are the same
point of the surface, even though their disk coordinates lie far apart.
Its geodesic operations account for this — log takes each target to
its nearest copy under the group, and exp folds every step back into
the fundamental octagon. This makes it a concrete demonstration that a
manifold can be defined without geomstats, and it is the space on
which the intrinsic \(\varepsilon\)-net strategies are exercised.
Points are (2,) real (Re, Im) coordinates inside the fundamental
octagon.
Creating the surface and sampling
from kmeanssa_ng import create_bolza_surface, RiemannianPoint
surface = create_bolza_surface()
coords = surface.random_uniform(2, random_state=0) # uniform by hyperbolic area
a, b = RiemannianPoint(surface, coords[0]), RiemannianPoint(surface, coords[1])
print(f"quotient distance a-b = {surface.distance(a, b):.3f}")
quotient distance a-b = 0.910
Clustering on the surface
Because the quotient has no faithful Euclidean embedding — points
glued across the boundary are close on the surface yet far apart in any
disk image — you must mesh it with the intrinsic \(\varepsilon\)-net
strategies, which search by geodesic distance.
approximate_geodesic_space(..., intrinsic=True) turns the surface into
a quantum graph you then cluster as usual:
from kmeanssa_ng import approximate_geodesic_space, SimulatedAnnealing
from kmeanssa_ng.quantum_graph.sampling import UniformNodeSampling
# Intrinsic build: O(n^2) in the net size, so this takes a while for large nets.
graph = approximate_geodesic_space(surface, 800, intrinsic=True, random_state=0)
points = graph.sample_points(300, strategy=UniformNodeSampling(random_state=0))
centers = SimulatedAnnealing(points, k=3, random_state=0).run(robust_prop=0.1)
The extrinsic strategies (KD-tree on ambient coordinates) would return
wrong neighbours on a quotient, so they refuse to run there — see
Meshing a Manifold for the intrinsic/extrinsic
distinction. The paper’s examples/paper/bolza.py runs this end to end
and renders the recovered partition inside the fundamental octagon.