import numpy as np import matplotlib.pyplot as plt from scipy import stats from ideal_poly_volume_toolkit.geometry import ideal_poly_volume_via_delaunay # Generate more samples for better statistics n_samples = 10000 np.random.seed(42) volumes = [] print(f"Generating {n_samples} samples...") for i in range(n_samples): if i % 1000 == 0: print(f" {i}/{n_samples}") # Uniform point on sphere vec = np.random.randn(3) vec = vec / np.linalg.norm(vec) x, y, z = vec if z > 0.999: # Skip north pole continue # Stereographic projection w = complex(x/(1-z), y/(1-z)) if abs(w) < 0.01 or abs(w-1) < 0.01: continue vertices = np.array([0+0j, 1+0j, w]) vol = ideal_poly_volume_via_delaunay(vertices, mode='fast', series_terms=96) volumes.append(vol) volumes = np.array(volumes) regular_vol = 1.01494160640965 # Create comprehensive visualization fig, axes = plt.subplots(2, 2, figsize=(12, 10)) # 1. Histogram with multiple overlays ax = axes[0, 0] n, bins, _ = ax.hist(volumes, bins=60, density=True, alpha=0.6, color='blue', edgecolor='black', label='Data') # Try to fit a beta distribution a, b, loc, scale = stats.beta.fit(volumes) x = np.linspace(0, 1.02, 1000) ax.plot(x, stats.beta.pdf(x, a, b, loc, scale), 'r-', linewidth=2, label=f'Beta fit (α={a:.2f}, β={b:.2f})') ax.axvline(regular_vol, color='green', linestyle='--', linewidth=2, label='Regular tetrahedron') ax.set_xlabel('Volume') ax.set_ylabel('Density') ax.set_title('Volume Distribution with Beta Fit') ax.legend() ax.set_xlim(0, 1.1) # 2. Log-log plot to check for power law in tail ax = axes[0, 1] # Create empirical survival function sorted_vols = np.sort(volumes) survival = 1 - np.arange(len(volumes)) / len(volumes) # Plot only the tail tail_start = int(0.9 * len(volumes)) ax.loglog(regular_vol - sorted_vols[tail_start:], survival[tail_start:], 'b.', markersize=3, alpha=0.5) ax.set_xlabel('Regular volume - Volume') ax.set_ylabel('P(V > v)') ax.set_title('Tail Behavior (log-log)') ax.grid(True, which='both', alpha=0.3) # 3. Theoretical vs empirical quantiles ax = axes[1, 0] theoretical_quantiles = np.linspace(0.01, 0.99, 99) empirical_quantiles = np.percentile(volumes, theoretical_quantiles * 100) beta_quantiles = stats.beta.ppf(theoretical_quantiles, a, b, loc, scale) ax.plot(beta_quantiles, empirical_quantiles, 'b.', alpha=0.5) ax.plot([0, 1.1], [0, 1.1], 'r--', linewidth=2) ax.set_xlabel('Beta Distribution Quantiles') ax.set_ylabel('Empirical Quantiles') ax.set_title('Q-Q Plot vs Beta Distribution') ax.grid(True, alpha=0.3) # 4. Relationship to distance from regular config ax = axes[1, 1] # For each volume, compute how far the fourth vertex is from "regular" position # Regular tetrahedron has fourth vertex at exp(2πi/3) regular_w = np.exp(2j * np.pi / 3) # Recompute to get w values w_values = [] vol_values = [] for i in range(1000): # Subset for clarity vec = np.random.randn(3) vec = vec / np.linalg.norm(vec) x, y, z = vec if z > 0.999: continue w = complex(x/(1-z), y/(1-z)) if abs(w) < 0.01 or abs(w-1) < 0.01: continue w_values.append(w) vertices = np.array([0+0j, 1+0j, w]) vol = ideal_poly_volume_via_delaunay(vertices, mode='fast') vol_values.append(vol) w_values = np.array(w_values) vol_values = np.array(vol_values) # Compute "regularity" metric - distance from regular position # Actually there are 3 regular positions due to symmetry regular_positions = [np.exp(2j * np.pi * k / 3) for k in range(3)] distances = [] for w in w_values: min_dist = min(abs(w - reg_pos) for reg_pos in regular_positions) distances.append(min_dist) ax.scatter(distances, vol_values, alpha=0.3, s=10) ax.set_xlabel('Distance from nearest regular position') ax.set_ylabel('Volume') ax.set_title('Volume vs Distance from Regularity') ax.grid(True, alpha=0.3) plt.tight_layout() plt.savefig('tetrahedron_distribution_analysis.png', dpi=150) # Print summary statistics print(f"\nDistribution Analysis Summary:") print(f" Total samples: {len(volumes)}") print(f" Mean: {np.mean(volumes):.4f} ({np.mean(volumes)/regular_vol*100:.1f}% of regular)") print(f" Median: {np.median(volumes):.4f} ({np.median(volumes)/regular_vol*100:.1f}% of regular)") print(f" Mode (approx): {bins[np.argmax(n)]:.4f}") print(f" Skewness: {stats.skew(volumes):.4f}") print(f" Kurtosis: {stats.kurtosis(volumes):.4f}") print(f"\nBeta distribution parameters:") print(f" α = {a:.4f}, β = {b:.4f}") print(f" This suggests the distribution is {('right' if a < b else 'left')}-skewed") plt.close()