idealpolyhedra / examples /analysis /analyze_distribution_shape.py
igriv's picture
Major reorganization and feature additions
d7d27f0
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()