Spaces:
Sleeping
Sleeping
File size: 7,360 Bytes
e0ef700 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
#!/usr/bin/env python3
"""
Sample random triangulations using edge flips.
Two strategies depending on size:
1. Small n (≤15): Start with plantri enumeration, then flip
2. Large n (>15): Start with Delaunay of random points, then flip many times
The flip graph is not an expander, so for uniform sampling we need
approximately O(n^2) flips for mixing (diameter is O(n^2)).
"""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent))
import numpy as np
from scipy.spatial import Delaunay
from ideal_poly_volume_toolkit.rivin_delaunay import random_edge_flips
from ideal_poly_volume_toolkit.plantri_interface import find_plantri_executable
from ideal_poly_volume_toolkit.planar_utils import extract_faces_from_planar_embedding
import subprocess
def sample_small_triangulation(n_vertices: int, n_flips: int, min_connectivity: int = 3, seed: int = 42):
"""
Sample a random triangulation for small n using plantri + flips.
Args:
n_vertices: Number of vertices (recommend ≤15)
n_flips: Number of random edge flips
min_connectivity: Minimum connectivity (3 or 4)
seed: Random seed
Returns:
List of triangles
"""
print(f"Sampling small triangulation (n={n_vertices})...")
print(f" Strategy: plantri enumeration → pick one → flip {n_flips} times")
# Get all triangulations from plantri
plantri = find_plantri_executable()
if plantri is None:
raise RuntimeError("plantri not found")
args = [plantri, f'-pc{min_connectivity}', '-a', str(n_vertices)]
result = subprocess.run(args, capture_output=True, text=True)
# Parse triangulations
triangulations = []
for line in result.stdout.split('\n'):
line = line.strip()
if not line or line.startswith('>'):
continue
parts = line.split(maxsplit=1)
if len(parts) != 2:
continue
n = int(parts[0])
adj_str = parts[1]
# Build adjacency dict
adj = {}
vertex_lists = adj_str.split(',')
for v_idx, neighbor_str in enumerate(vertex_lists):
neighbors = []
for letter in neighbor_str:
neighbor_idx = ord(letter) - ord('a')
neighbors.append(neighbor_idx)
adj[v_idx] = neighbors
# Extract faces properly
triangles = extract_faces_from_planar_embedding(n, adj)
if triangles:
triangulations.append(triangles)
print(f" Found {len(triangulations)} triangulations from plantri")
# Pick one randomly
np.random.seed(seed)
idx = np.random.randint(len(triangulations))
closed_tri = triangulations[idx]
# Remove vertex 0 to get planar triangulation
planar_tri = [tri for tri in closed_tri if 0 not in tri]
print(f" Selected triangulation #{idx}")
print(f" Planar triangulation: {len(planar_tri)} triangles")
# Apply random flips
if n_flips > 0:
flipped = random_edge_flips(planar_tri, n_flips=n_flips, seed=seed)
print(f" Applied {n_flips} random edge flips")
return flipped
else:
return planar_tri
def sample_large_triangulation(n_vertices: int, n_flips: int, seed: int = 42):
"""
Sample a random triangulation for large n using Delaunay + many flips.
For uniform sampling, use n_flips ≈ 10 * n_vertices^2 (conservative estimate).
Args:
n_vertices: Number of vertices
n_flips: Number of random edge flips (recommend ≥ 10*n^2 for mixing)
seed: Random seed
Returns:
List of triangles
"""
print(f"Sampling large triangulation (n={n_vertices})...")
print(f" Strategy: Delaunay(random points) → flip {n_flips} times")
# Generate random points
np.random.seed(seed)
points = np.random.rand(n_vertices, 2)
# Compute Delaunay triangulation
tri = Delaunay(points)
initial_triangles = [tuple(simplex) for simplex in tri.simplices]
print(f" Generated {n_vertices} random points")
print(f" Delaunay triangulation: {len(initial_triangles)} triangles")
# Apply many random flips for mixing
if n_flips > 0:
flipped = random_edge_flips(initial_triangles, n_flips=n_flips, seed=seed)
print(f" Applied {n_flips} random edge flips")
# Note on mixing
diameter_estimate = n_vertices * n_vertices
if n_flips < 10 * diameter_estimate:
print(f" WARNING: For uniform sampling, recommend ≥{10*diameter_estimate} flips")
print(f" (flip graph diameter ≈ n^2 = {diameter_estimate})")
return flipped
else:
return initial_triangles
def sample_triangulation(n_vertices: int, n_flips: int = None, min_connectivity: int = 3, seed: int = 42):
"""
Sample a random triangulation using the appropriate strategy for the size.
Args:
n_vertices: Number of vertices
n_flips: Number of flips (if None, use default based on size)
min_connectivity: Minimum connectivity for small n (3 or 4)
seed: Random seed
Returns:
List of triangles
"""
# Choose strategy and default flips based on size
if n_vertices <= 15:
# Small: Use plantri + moderate flips
if n_flips is None:
n_flips = 100 * n_vertices # Conservative
return sample_small_triangulation(n_vertices, n_flips, min_connectivity, seed)
else:
# Large: Use Delaunay + many flips
if n_flips is None:
# For mixing, need ~O(n^2) flips (flip graph diameter)
# Use 10*n^2 to be conservative
n_flips = 10 * n_vertices * n_vertices
return sample_large_triangulation(n_vertices, n_flips, seed)
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser(description='Sample random triangulations')
parser.add_argument('--n', type=int, required=True, help='Number of vertices')
parser.add_argument('--flips', type=int, help='Number of edge flips (auto if not specified)')
parser.add_argument('--connectivity', type=int, default=3, choices=[3, 4],
help='Min connectivity for small n (3 or 4)')
parser.add_argument('--seed', type=int, default=42, help='Random seed')
parser.add_argument('--samples', type=int, default=1, help='Number of samples to generate')
args = parser.parse_args()
print("="*70)
print("RANDOM TRIANGULATION SAMPLING VIA EDGE FLIPS")
print("="*70)
print()
for i in range(args.samples):
if args.samples > 1:
print(f"\nSample {i+1}/{args.samples}:")
print("-"*70)
seed = args.seed + i if args.samples > 1 else args.seed
triangles = sample_triangulation(
n_vertices=args.n,
n_flips=args.flips,
min_connectivity=args.connectivity,
seed=seed
)
print(f"\nResult: {len(triangles)} triangles")
# Show a few triangles
print(f"Sample triangles: {triangles[:5]}")
# Could test realizability here
# from ideal_poly_volume_toolkit.rivin_delaunay import check_delaunay_realizability
# result = check_delaunay_realizability(triangles, verbose=False)
# print(f"Realizable: {result['realizable']}")
print()
print("="*70)
|