Spaces:
Sleeping
Sleeping
| #!/usr/bin/env python3 | |
| """ | |
| Generate a benchmark JSON file with: | |
| - 1 optimized configuration (ARITHMETIC) | |
| - 9 random configurations (NON-ARITHMETIC) | |
| This demonstrates the Rivin-Delaunay theorem: maximal volume configurations | |
| for a fixed combinatorial triangulation are always arithmetic. | |
| """ | |
| import numpy as np | |
| import json | |
| from datetime import datetime | |
| import sys | |
| sys.path.insert(0, '.') | |
| from ideal_poly_volume_toolkit.geometry import ( | |
| delaunay_triangulation_indices, | |
| ideal_poly_volume_via_delaunay, | |
| ) | |
| from ideal_poly_volume_toolkit.rivin_delaunay import ( | |
| check_delaunay_realizability, | |
| optimize_hyperbolic_volume, | |
| realize_angles_as_points, | |
| ) | |
| from ideal_poly_volume_toolkit.rivin_holonomy import ( | |
| check_arithmeticity, | |
| check_arithmeticity_from_vertices, | |
| ) | |
| def generate_random_sphere_points(n_points, seed=None): | |
| """Generate n random points uniformly on the unit sphere.""" | |
| if seed is not None: | |
| np.random.seed(seed) | |
| points_3d = [] | |
| for _ in range(n_points): | |
| vec = np.random.randn(3) | |
| vec = vec / np.linalg.norm(vec) | |
| points_3d.append(vec) | |
| points_3d = np.array(points_3d) | |
| # Stereographic projection from north pole to complex plane | |
| complex_points = [] | |
| for x, y, z in points_3d: | |
| if z > 0.9999: | |
| complex_points.append(complex(np.inf, np.inf)) | |
| else: | |
| w = complex(x / (1 - z), y / (1 - z)) | |
| complex_points.append(w) | |
| return np.array(complex_points) | |
| def generate_random_plane_points(n_points, seed=None, scale=2.0): | |
| """Generate n random points in the complex plane (truly generic).""" | |
| if seed is not None: | |
| np.random.seed(seed) | |
| # Use Gaussian distribution centered at origin | |
| real_parts = np.random.randn(n_points) * scale | |
| imag_parts = np.random.randn(n_points) * scale | |
| return real_parts + 1j * imag_parts | |
| def optimize_for_fixed_combinatorics(complex_points): | |
| """Optimize volume for fixed combinatorics using Rivin-Delaunay.""" | |
| finite_mask = np.isfinite(complex_points) | |
| finite_points = complex_points[finite_mask] | |
| # Get triangulation | |
| triangulation_indices = delaunay_triangulation_indices(finite_points) | |
| triangles = [tuple(tri) for tri in triangulation_indices] | |
| # Check realizability | |
| realizability = check_delaunay_realizability(triangles, verbose=False) | |
| if not realizability['realizable']: | |
| return None, None, triangles | |
| # Optimize | |
| opt_result = optimize_hyperbolic_volume( | |
| triangles, | |
| initial_angles=realizability['angles_radians'], | |
| verbose=False | |
| ) | |
| if not opt_result['success']: | |
| return None, None, triangles | |
| # Realize geometry | |
| realization = realize_angles_as_points( | |
| triangles, | |
| opt_result['angles'], | |
| verbose=False | |
| ) | |
| if realization['points'] is not None: | |
| vertex_list = realization['vertex_list'] | |
| points_2d = realization['points'] | |
| optimized_complex = np.zeros(len(vertex_list), dtype=complex) | |
| for i, v in enumerate(vertex_list): | |
| optimized_complex[v] = complex(points_2d[i, 0], points_2d[i, 1]) | |
| return optimized_complex, opt_result['volume'], triangles | |
| return None, None, triangles | |
| def main(): | |
| print("Generating Arithmeticity Benchmark") | |
| print("=" * 60) | |
| n_points = 10 | |
| base_seed = 42 | |
| configs = [] | |
| # Generate the first config: optimized (should be arithmetic) | |
| print(f"\nConfig 0: Optimized (Rivin-Delaunay)") | |
| print("-" * 40) | |
| random_points = generate_random_sphere_points(n_points, seed=base_seed) | |
| optimized_points, opt_volume, triangles = optimize_for_fixed_combinatorics(random_points) | |
| if optimized_points is not None: | |
| # Check arithmeticity | |
| arith_result = check_arithmeticity(triangles, verbose=True) | |
| finite_optimized = optimized_points[np.isfinite(optimized_points)] | |
| volume = ideal_poly_volume_via_delaunay(finite_optimized, use_bloch_wigner=True) | |
| config = { | |
| "id": 0, | |
| "type": "optimized", | |
| "description": "Rivin-Delaunay optimized configuration (maximal volume for fixed combinatorics)", | |
| "expected_arithmetic": True, | |
| "n_vertices": int(len(finite_optimized)), | |
| "n_triangles": int(len(triangles)), | |
| "volume": float(volume), | |
| "vertices_real": [float(z.real) for z in finite_optimized], | |
| "vertices_imag": [float(z.imag) for z in finite_optimized], | |
| "triangles": [[int(x) for x in t] for t in triangles], | |
| "holonomy_traces": [float(t) for t in arith_result['traces']], | |
| "is_arithmetic": bool(arith_result['is_arithmetic']), | |
| "seed": int(base_seed), | |
| } | |
| configs.append(config) | |
| print(f" Volume: {volume:.6f}") | |
| print(f" Arithmetic: {arith_result['is_arithmetic']}") | |
| print(f" Traces: {arith_result['n_integral']}/{arith_result['n_generators']} integral") | |
| else: | |
| print(" ERROR: Optimization failed") | |
| # Generate 9 random configs (should be non-arithmetic) | |
| # Use random plane points (not sphere points) to get generic configurations | |
| for i in range(1, 10): | |
| seed = base_seed + i * 100 # Different seeds for variety | |
| print(f"\nConfig {i}: Random (seed={seed})") | |
| print("-" * 40) | |
| # Use random plane points for truly generic configurations | |
| finite_points = generate_random_plane_points(n_points, seed=seed) | |
| if len(finite_points) < 3: | |
| print(" ERROR: Not enough finite points") | |
| continue | |
| # Get triangulation | |
| triangulation_indices = delaunay_triangulation_indices(finite_points) | |
| triangles = [tuple(int(x) for x in tri) for tri in triangulation_indices] | |
| # Check arithmeticity using actual geometry (computes shears from vertices) | |
| arith_result = check_arithmeticity_from_vertices(finite_points, verbose=True) | |
| volume = ideal_poly_volume_via_delaunay(finite_points, use_bloch_wigner=True) | |
| config = { | |
| "id": i, | |
| "type": "random", | |
| "description": f"Random plane configuration (seed={seed})", | |
| "expected_arithmetic": False, | |
| "n_vertices": int(len(finite_points)), | |
| "n_triangles": int(len(triangles)), | |
| "volume": float(volume), | |
| "vertices_real": [float(z.real) for z in finite_points], | |
| "vertices_imag": [float(z.imag) for z in finite_points], | |
| "triangles": [[int(x) for x in t] for t in triangles], | |
| "holonomy_traces": [float(t) for t in arith_result['traces']], | |
| "is_arithmetic": bool(arith_result['is_arithmetic']), | |
| "seed": int(seed), | |
| } | |
| configs.append(config) | |
| print(f" Volume: {volume:.6f}") | |
| print(f" Arithmetic: {arith_result['is_arithmetic']}") | |
| print(f" Traces: {arith_result['n_integral']}/{arith_result['n_generators']} integral") | |
| # Build final benchmark | |
| benchmark = { | |
| "metadata": { | |
| "title": "Arithmeticity Benchmark: Optimized vs Random Configurations", | |
| "description": "Demonstrates that Rivin-Delaunay optimized configurations are arithmetic (all holonomy traces are integers), while random configurations are typically not.", | |
| "created": datetime.now().isoformat(), | |
| "n_vertices": n_points, | |
| "theory": "By Rivin's theorem, the maximal volume configuration for a fixed combinatorial triangulation is unique and arithmetic. Random configurations do not achieve this maximum and are generically non-arithmetic.", | |
| }, | |
| "summary": { | |
| "total_configs": len(configs), | |
| "optimized_configs": sum(1 for c in configs if c['type'] == 'optimized'), | |
| "random_configs": sum(1 for c in configs if c['type'] == 'random'), | |
| "arithmetic_count": sum(1 for c in configs if c['is_arithmetic']), | |
| "non_arithmetic_count": sum(1 for c in configs if not c['is_arithmetic']), | |
| }, | |
| "configurations": configs, | |
| } | |
| # Save | |
| output_file = "arithmeticity_benchmark.json" | |
| with open(output_file, 'w') as f: | |
| json.dump(benchmark, f, indent=2) | |
| print("\n" + "=" * 60) | |
| print("SUMMARY") | |
| print("=" * 60) | |
| print(f"Total configurations: {len(configs)}") | |
| print(f"Arithmetic: {benchmark['summary']['arithmetic_count']}") | |
| print(f"Non-arithmetic: {benchmark['summary']['non_arithmetic_count']}") | |
| print(f"\nSaved to: {output_file}") | |
| # Verify: optimized should be arithmetic, randoms should (mostly) not be | |
| optimized_configs = [c for c in configs if c['type'] == 'optimized'] | |
| random_configs = [c for c in configs if c['type'] == 'random'] | |
| print("\nVerification:") | |
| if all(c['is_arithmetic'] for c in optimized_configs): | |
| print(" [PASS] All optimized configs are arithmetic") | |
| else: | |
| print(" [FAIL] Some optimized configs are NOT arithmetic!") | |
| non_arith_randoms = sum(1 for c in random_configs if not c['is_arithmetic']) | |
| print(f" Random configs: {non_arith_randoms}/{len(random_configs)} are non-arithmetic") | |
| if __name__ == "__main__": | |
| main() | |