Spaces:
Sleeping
Sleeping
File size: 7,283 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 217 218 219 220 221 222 223 224 225 226 227 |
#!/usr/bin/env python3
"""
Generate and save triangulations with optimal angles for various n.
"""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent))
import numpy as np
import json
from datetime import datetime
from scipy.spatial import Delaunay
from ideal_poly_volume_toolkit.rivin_delaunay import check_delaunay_realizability, build_edge_adjacency
from math import gcd
from functools import reduce
def lcm(a, b):
"""Compute least common multiple."""
return abs(a * b) // gcd(a, b)
def analyze_and_save_configuration(n_vertices, seed, output_dir='results/data/large_configs'):
"""Generate, analyze, and save a configuration."""
print(f"\n{'='*70}")
print(f"Configuration: n={n_vertices}, seed={seed}")
print(f"{'='*70}")
# Generate random points
np.random.seed(seed)
radii = np.sqrt(np.random.uniform(0, 1, n_vertices))
angles = np.random.uniform(0, 2*np.pi, n_vertices)
vertices_complex = radii * np.exp(1j * angles)
points = np.column_stack([vertices_complex.real, vertices_complex.imag])
print(f"Generated {n_vertices} random points")
# Compute Delaunay triangulation
tri = Delaunay(points)
triangulation = [tuple(sorted(simplex)) for simplex in tri.simplices]
triangulation = sorted(set(triangulation))
print(f"Triangulation: {len(triangulation)} triangles")
# Get optimal angles from Rivin LP
result = check_delaunay_realizability(triangulation, verbose=False, strict=False)
if not result['realizable']:
print("ERROR: Not realizable!")
return None
print(f"✓ Realizable")
# Extract angles
angles_scaled = result['angles']
angles_radians = angles_scaled * np.pi
n_triangles = len(triangulation)
angles_array = angles_radians.reshape((n_triangles, 3))
# Compute dihedral angles
edge_adjacency = build_edge_adjacency(triangulation)
dihedrals = []
for edge, opposite_corners in sorted(edge_adjacency.items()):
if len(opposite_corners) == 2:
angle1 = angles_array[opposite_corners[0][0], opposite_corners[0][1]]
angle2 = angles_array[opposite_corners[1][0], opposite_corners[1][1]]
dihedral = angle1 + angle2
normalized = dihedral / np.pi
dihedrals.append({
'edge': [int(edge[0]), int(edge[1])],
'angle_radians': float(dihedral),
'angle_degrees': float(np.degrees(dihedral)),
'normalized': float(normalized),
})
print(f"Computed {len(dihedrals)} interior edge dihedrals")
# Analyze rational structure
# Check denominators up to 10*n
max_denom = 10 * n_vertices
denominators_found = set()
for d in dihedrals:
norm = d['normalized']
# Try to find rational approximation
for q in range(1, min(max_denom + 1, 1000)):
p = round(norm * q)
if abs(norm - p/q) < 1e-10:
denominators_found.add(q)
d['rational_p'] = int(p)
d['rational_q'] = int(q)
d['rational_error'] = float(abs(norm - p/q))
break
# Find LCM of all denominators
if denominators_found:
common_denominator = reduce(lcm, denominators_found)
else:
common_denominator = None
print(f"Denominators found: {sorted(denominators_found)}")
print(f"Common denominator (LCM): {common_denominator}")
if common_denominator:
print(f"Ratio q/n: {common_denominator/n_vertices:.3f}")
# Check if all angles are rational with common denominator
all_rational = all('rational_q' in d for d in dihedrals)
if all_rational and common_denominator:
# Verify all are multiples of 1/common_denominator
all_multiples = True
for d in dihedrals:
p = round(d['normalized'] * common_denominator)
error = abs(d['normalized'] - p/common_denominator)
if error > 1e-10:
all_multiples = False
break
if all_multiples:
print(f"✓ ALL {len(dihedrals)} angles are exact multiples of π/{common_denominator}")
# Prepare output data
output_data = {
'metadata': {
'n_vertices': int(n_vertices),
'n_triangles': int(n_triangles),
'n_interior_edges': len(dihedrals),
'seed': int(seed),
'generated': datetime.now().isoformat(),
},
'vertex_positions': {
'real': vertices_complex.real.tolist(),
'imag': vertices_complex.imag.tolist(),
},
'triangulation': [[int(v) for v in tri] for tri in triangulation],
'face_angles': {
'radians': angles_array.tolist(),
'degrees': np.degrees(angles_array).tolist(),
},
'dihedral_angles': dihedrals,
'rational_structure': {
'all_rational': all_rational,
'denominators': sorted(denominators_found),
'common_denominator': int(common_denominator) if common_denominator else None,
'ratio_to_n': float(common_denominator / n_vertices) if common_denominator else None,
}
}
# Save to file
output_dir = Path(output_dir)
output_dir.mkdir(parents=True, exist_ok=True)
output_file = output_dir / f"n{n_vertices:03d}_seed{seed:03d}_triangulation.json"
with open(output_file, 'w') as f:
json.dump(output_data, f, indent=2)
print(f"✓ Saved to: {output_file}")
return output_data
def main():
"""Generate and save multiple configurations."""
print("═"*70)
print("LARGE CONFIGURATION GENERATOR")
print("Saving triangulations with optimal angles for various n")
print("═"*70)
# Configurations to generate
configs = [
(30, 42),
(40, 42),
(50, 42),
(60, 42),
(70, 42),
(80, 42),
(89, 42),
(100, 42),
(89, 123), # Same n, different seed
(89, 456), # Another seed for n=89
]
results = []
for n, seed in configs:
result = analyze_and_save_configuration(n, seed)
if result:
results.append({
'n': n,
'seed': seed,
'common_denom': result['rational_structure']['common_denominator'],
'ratio': result['rational_structure']['ratio_to_n'],
})
# Create summary
print(f"\n{'='*70}")
print("SUMMARY")
print(f"{'='*70}")
print(f"\n{'n':>4} {'seed':>6} {'q (LCM)':>10} {'q/n':>8}")
print("-"*30)
for r in results:
if r['common_denom']:
print(f"{r['n']:>4} {r['seed']:>6} {r['common_denom']:>10} {r['ratio']:>8.3f}")
else:
print(f"{r['n']:>4} {r['seed']:>6} {'None':>10} {'N/A':>8}")
# Save summary
summary_file = Path('results/data/large_configs/SUMMARY.json')
with open(summary_file, 'w') as f:
json.dump({
'generated': datetime.now().isoformat(),
'configurations': results,
}, f, indent=2)
print(f"\n✓ Summary saved to: {summary_file}")
print(f"\nAll configurations saved to: results/data/large_configs/")
if __name__ == '__main__':
main()
|