circle_bundles.synthetic
Synthetic datasets and geometric models used for demonstrations and validation.
Typical usage
>>> from circle_bundles.synthetic import sample_s2_trivial, make_tri_prism, mesh_to_density
- circle_bundles.synthetic.mesh_to_density(mesh, *, grid_size=32, sigma=0.05, n_surface_samples=5000, normalize=True, rng=None, eps=1e-12)[source]
Convert a mesh into a flattened density on a cubic grid in [-1,1]^3 by sampling the surface and placing a Gaussian in distance-to-surface.
Notes
The mesh is copied, centered (center_mass), and scaled into ~unit ball.
Deterministic sampling via rng is not guaranteed across trimesh versions; rng is accepted for API consistency but is best-effort.
- circle_bundles.synthetic.get_density_axes(flat_densities, *, grid_size=32, smallest=True, return_eigs=False, eps=1e-12)[source]
Compute principal axes for a batch of 3D densities on a [-1,1]^3 grid.
- For each density rho, compute weighted covariance/inertia:
M = Σ rho(x) (x - μ)(x - μ)^T
- and return either:
smallest-eigenvalue direction (least spread), or
largest-eigenvalue direction (most spread).
- circle_bundles.synthetic.get_mesh_sample(mesh, O3_data)[source]
Apply each 3x3 matrix in O3_data to mesh vertices and return flattened vertex arrays.
- Parameters:
mesh (trimesh.Trimesh)
O3_data ((n_samples, 9)) – Flattened 3x3 matrices (SO(3) or O(3)).
- Returns:
mesh_samples – Each row is rotated vertices flattened in (x0,y0,z0,x1,y1,z1,…) order.
- Return type:
(n_samples, 3*N)
- circle_bundles.synthetic.make_tri_prism(*, height=1.0, radius=1.0)[source]
Create a triangular prism with base an equilateral triangle in the (y,z)-plane and extrusion along the x-axis.
- Returns:
mesh (trimesh.Trimesh)
face_groups (list[(start, end_exclusive)]) –
- Ranges of triangle faces belonging to the 5 prism faces, in order:
0: bottom triangle 1: top triangle 2: side face between vertices 0-1 3: side face between vertices 1-2 4: side face between vertices 2-0
- Parameters:
- Return type:
- circle_bundles.synthetic.make_star_pyramid(*, n_points=5, radius_outer=1.0, radius_inner=0.5, height=1.0)[source]
- Create a star-based pyramid mesh:
base is a 2D star polygon in the yz-plane at x=0
apex at (height, 0, 0)
Notes
Triangulates the base polygon using trimesh.creation.triangulate_polygon.
Then connects the apex to the boundary cycle (2*n_points edges).
- circle_bundles.synthetic.mesh_vertex_normals(X, *, n_vertices=None, vertex_dim=3, idx=(0, 1, 2), eps=1e-12)[source]
Compute the oriented unit normal determined by three vertices from flattened mesh-vertex data.
Raises a ValueError if any triple is colinear or degenerate.
- Parameters:
- Returns:
Shape (3,) or (N,3) of unit normals.
- Return type:
normals
- circle_bundles.synthetic.make_density_visualizer(*, grid_size=32, axis='x', cmap='inferno', normalize=False, figsize=(3.0, 3.0), dpi=150)[source]
Returns a visualization function for densities on a grid_size^3 voxel grid.
- circle_bundles.synthetic.make_tri_prism_visualizer(mesh, face_groups=None, *, face_colors_list=None, alpha=1.0, show_edges=True, edge_color='black', edge_width=2.5, elev=0.0, azim=0.0, figsize=(4.0, 4.0), dpi=150, depth_sort=True)[source]
Visualize a triangular prism-style mesh with custom face group coloring.
- Parameters:
mesh (trimesh.Trimesh-like) – Must have .vertices and .faces.
face_groups (groups of face indices (explicit lists or (start,end_excl) ranges))
depth_sort (bool) – If True, manually sorts triangles back-to-front using projected depth (helps with alpha blending / occlusion in Matplotlib).
alpha (float)
show_edges (bool)
edge_color (str)
edge_width (float)
elev (float)
azim (float)
dpi (int)
- Returns:
flat_mesh is expected to be (n_vertices*3,) giving vertex positions.
- Return type:
vis_func(flat_mesh) -> Figure
- circle_bundles.synthetic.make_star_pyramid_visualizer(mesh, *, base_color='#94A3B8', edge_color='gray', alpha=1.0, colormap=None, figsize=(4.0, 4.0), dpi=150, elev=0.0, azim=0.0)[source]
Visualizer for a star pyramid mesh with a smooth gradient on side faces.
FIX: side-face ordering is computed ONCE from the template mesh vertices, so colors stay attached to the same faces under rotation.
- circle_bundles.synthetic.sample_nat_img_kb(n_points, *, n=3, noise=0.0, rng=None, eps=1e-12, return_angles=False)[source]
Synthetic “natural image” patch model parameterizing a Klein bottle.
Returns mean-centered and L2-normalized patches built from a 1D quadratic + linear profile along a direction in RP^1 (theta folded to [0, pi)), with a corresponding alpha “fiber” angle adjusted so the model is continuous under the identification.
- circle_bundles.synthetic.get_gradient_dirs(patches, n=None, eps=1e-12)[source]
Compute predominant GRADIENT-axis angles in RP^1 for n×n grayscale patches. Returns (thetas, strengths).
- circle_bundles.synthetic.sample_opt_flow_torus(n_points, *, dim=3, sigma=0.0, rng=None, sample_r=False, r_min=0.6, r_lam=8.0, r_values=None, return_r=True, contrast_renorm=True, eps=1e-12)[source]
Sample the optical-flow patch torus model (dim x dim DCT basis, flattened length 2*dim^2), with RP^1 base angle theta folded to [0, pi).
Optionally extends the model with an r-parameter in [r_min, 1] that mixes each patch with its perpendicular patch, using:
perp patch = (alpha + pi/2, theta - pi/2) (then re-fold), lambda = 1/sqrt(2 - r), patch_mix = lambda*patch + sqrt(1-lambda^2)*perp.
- Default behavior:
sample_r=True
r ~ truncated exponential away from 1 on [r_min, 1] (so virtually all mass near 1, but never below r_min).
- Noise:
if sigma > 0 and contrast_renorm=True, we add Gaussian noise then contrast-renormalize.
- circle_bundles.synthetic.make_flow_patches(alpha, theta, r=None, *, contrast_renorm=False, eps=1e-12)[source]
Generate samples from the extended torus optical-flow patch model (3x3, length 18).
Folds theta into [0, pi) (RP^1 base) and adjusts alpha accordingly.
- If r is provided, it mixes a patch with a perpendicular patch:
patches_mix = λ * patches + sqrt(1-λ^2) * patches_perp
where λ = 1/sqrt(2-r).
If contrast_renorm=True, rescales each patch to unit “contrast norm”.
- circle_bundles.synthetic.sample_sphere(n, dim=2, *, rng=None)[source]
Sample n points ~uniformly from S^{dim} ⊂ R^{dim+1} via Gaussian normalization.
Examples
dim=2 -> S^2 in R^3, output shape (n,3) dim=3 -> S^3 in R^4, output shape (n,4)
- circle_bundles.synthetic.hopf_projection(data, *, v=None, eps=1e-12)[source]
Generalized Hopf projection defined by q ↦ q v q^{-1}, where v ∈ S^2 ⊂ Im(H).
- circle_bundles.synthetic.sample_s2_trivial(n_points, *, sigma=0.0, rng=None, radius_mean=1.0, radius_clip=(0.0, 5.0))[source]
Product bundle S^2 × S^1 embedded as (base ∈ R^3, fiber ∈ R^2) in R^5.
- Returns:
data ((n_points, 5) = [base_x, base_y, base_z, fiber_u, fiber_v])
base_points ((n_points, 3) points on S^2)
angles ((n_points,) fiber angles in radians)
- Parameters:
- Return type:
- circle_bundles.synthetic.sample_so3(n_samples, *, rule=None, v=None, rng=None, eps=1e-12)[source]
Sample SO(3) (flattened rotation matrices) with optional structured rules.
- Parameters:
n_samples (int) – Number of samples.
rule (None | 'fiber' | 'equator') –
None: Haar-random rotations; returns (data, base_points) where base_points are first columns.
- ’fiber’: fix first column to v (or random) and sample a fiber angle θ;
returns (data, theta) with theta shape (n_samples,).
- ’equator’: choose u on great circle orthogonal to v via angle φ, then choose fiber angle θ;
returns (data, angles) with angles shape (n_samples,2) = (phi, theta).
v ((3,) array-like, optional) – If None, sampled randomly for the structured rules.
rng (np.random.Generator, optional)
eps (float) – Stability floor.
- Returns:
data ((n_samples, 9) ndarray) – Flattened rotation matrices (row-major from (n,3,3) reshape).
extra (ndarray) – Depends on rule (base_points / theta / [phi,theta]).
- Return type:
- circle_bundles.synthetic.project_o3(O3_data, v=None)[source]
Given flattened O(3) matrices (N, 9), return the image of v under each matrix.
- Parameters:
O3_data ((N,9) ndarray) – Row-major flattened 3x3 matrices.
v ((3,) ndarray, optional) – Vector to project. Default is e1.
- Returns:
out – (M_i @ v) for each matrix M_i.
- Return type:
(N,3) ndarray
- circle_bundles.synthetic.get_patch_types_list()[source]
Generate the 28 possible filament patch types (legacy convention).
Each patch type is a list of [i,j] pixel coordinates (0..2) where a vector arrow lives. For a 3x3 patch, there are 28 “filament” patterns; we later include sign flips to get 56.
- circle_bundles.synthetic.make_step_edges(n_patches, spots, *, angle_range=(0.0, 6.283185307179586), normalize=True, rng=None, eps=1e-12)[source]
Generate optical-flow step edge patches as flattened vectors of length 18 (3x3x2).
Conventions
Patch array shaped (n, 3, 3, 2) with last axis = (u,v).
Flattening order is ‘F’ to match legacy notebooks.
- param n_patches:
Number of patches to generate.
- type n_patches:
int
- param spots:
Pixel coordinates where the flow vector is placed.
- type spots:
sequence of (i,j)
- param angle_range:
Sample directions uniformly from [a_min, a_max]. If a_min == a_max, all patches use that fixed angle.
- type angle_range:
(float, float)
- param normalize:
If True, contrast-normalize using get_contrast_norms and mean-center each channel.
- type normalize:
bool
- param rng:
Random generator for reproducibility.
- type rng:
np.random.Generator, optional
- param eps:
Stability floor for normalization.
- type eps:
float
- returns:
patch_vectors ((n_patches, 18))
angles ((n_patches,))
- circle_bundles.synthetic.make_all_step_edges(angle=None, *, normalize=True)[source]
Return the 56 canonical step-edge patterns (28 types + sign flips).
- If angle is None:
Returns scalar +/-1 edge patterns as (56, 9) using legacy ‘F’ convention.
- If angle is not None:
Returns flow vectors (56, 18) at that fixed direction (cos(angle), sin(angle)).
- circle_bundles.synthetic.sample_binary_step_edges(samples_per_filament, *, rng=None)[source]
Sample step-edge patches across all 28 filament types.
- circle_bundles.synthetic.mean_center(patch_vector, *, copy=True)[source]
Mean-center a single patch vector (length 9 or 18).
- For length 18 (flow):
mean-center u-channel entries (first 9)
mean-center v-channel entries (last 9)
- circle_bundles.synthetic.sample_step_edge_torus(n_samples, *, d=3, m=10, thresh=0.01, rng=None, eps=1e-12)[source]
Generate step-edge torus samples.
- Construction (legacy):
sample half-planes parameterized by (offset r, direction phi) inside a disk
build range patches by averaging sign over m×m subgrid per pixel
create flow patches by multiplying by (cos theta, sin theta)
keep high-contrast patches (contrast norm > thresh)
mean-center & contrast normalize
- Parameters:
- Returns:
flow_patches ((N_kept, 2*d^2))
coords ((N_kept, 3) where columns are (offset, phi, theta))
range_patches ((N_kept, d^2))
norms ((N_kept,) contrast norms (pre-normalization))
- Return type:
- circle_bundles.synthetic.sample_foldy_klein_bottle(n, *, amp_fiber=1.0, amp_base=1.0, base_radius=1.0, noise=0.05, rigid_motion=True, translate_scale=0.0, det_sign=1, rng=None)[source]
- Like sample_foldy_klein_bottle, but optionally:
embed the base circle nonlinearly in R^4 (using folded_circle_r4),
then apply a global rigid motion in ambient space to intermix coordinates.
- Returns:
X ((n, D) array) – Ambient coordinates. D=6 if base is 2D circle; D=8 if base is 4D folded.
t ((n,) array) – Base parameter (ground truth).
- Parameters:
- Return type:
- circle_bundles.synthetic.sample_R3_torus(n_points, *, R=2.0, r_center=0.8, r_amplitude=0.3, r_frequency=2, r_phase=0.0, sigma=0.0, rng=None, require_ring_torus=True, return_theta=False, return_alpha=False)[source]
Sample a torus-of-revolution in R^3 where the tube (fiber) radius varies sinusoidally with base angle theta.
- Parameterization:
r(theta) = r_center + r_amplitude * sin(r_frequency * theta + r_phase)
x = (R + r(theta) cos(alpha)) cos(theta) y = (R + r(theta) cos(alpha)) sin(theta) z = r(theta) sin(alpha)
- Returns:
data ((n_points, 3))
base_points ((n_points, 2) = (cos(theta), sin(theta)))
alpha ((n_points,) fiber angle)
theta ((n_points,) base angle (optional))
r_vals ((n_points,) realized tube radius after noise (only returned when) – return_theta and return_alpha are both True)
- Parameters:
- Return type:
Tuple[ndarray, ndarray, ndarray] | Tuple[ndarray, ndarray, ndarray, ndarray] | Tuple[ndarray, ndarray, ndarray, ndarray, ndarray]