""" Visualization pipeline for v_mix — 15+ plot types. """ import numpy as np import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt import os class VisualizationPipeline: def __init__(self, output_dir: str = "./output"): self.output_dir = output_dir os.makedirs(output_dir, exist_ok=True) self.files = [] def _save(self, name: str): path = os.path.join(self.output_dir, name) plt.savefig(path, dpi=150, bbox_inches='tight') plt.close() self.files.append(path) return path def plot_zero_distribution(self, zeros): fig, ax = plt.subplots(figsize=(10, 4)) gammas = [z['imaginary_part'] for z in zeros] ax.plot(gammas, np.zeros(len(gammas)), '|', markersize=10, alpha=0.7) ax.set_xlabel('Imaginary part γ') ax.set_title('Zeta Zero Distribution on Critical Line') ax.set_yticks([]) return self._save("vmix_zero_distribution.png") def plot_spacing_histogram(self, spacings): fig, ax = plt.subplots(figsize=(8, 5)) ax.hist(spacings, bins=50, density=True, alpha=0.7, color='steelblue', edgecolor='black') # Wigner surmise overlay s = np.linspace(0, max(spacings), 200) wigner = (np.pi / 2) * s * np.exp(-np.pi * s**2 / 4) ax.plot(s, wigner, 'r--', linewidth=2, label='GUE Wigner surmise') ax.set_xlabel('Normalized spacing s') ax.set_ylabel('Probability density') ax.set_title('Nearest-Neighbor Spacing Distribution') ax.legend() return self._save("vmix_spacing_histogram.png") def plot_pair_correlation(self, pc_result): fig, ax = plt.subplots(figsize=(8, 5)) alpha = np.array(pc_result['alpha']) emp = np.array(pc_result['empirical']) gue = np.array(pc_result['gue_prediction']) ax.plot(alpha, emp, label='Empirical', color='steelblue', linewidth=1.5) ax.plot(alpha, gue, '--', label='GUE', color='red', linewidth=1.5) ax.set_xlabel('α') ax.set_ylabel('F(α)') ax.set_title('Pair Correlation Function') ax.legend() ax.set_xlim(0, max(alpha)) return self._save("vmix_pair_correlation.png") def plot_gue_convergence(self, result): fig, ax = plt.subplots(figsize=(8, 6)) N = np.array(result['n_zeros']) dev = np.array(result['ks_distances']) ax.loglog(N, dev, 'o-', color='steelblue', linewidth=2, markersize=6) # Overlay fits if 'power_law_fit_N' in result: fit = result['power_law_fit_N'] beta = fit['exponent_beta'] c = np.exp(np.polyfit(np.log(N), np.log(dev), 1)[1]) fit_line = c * N ** (-beta) ax.loglog(N, fit_line, '--', color='red', label=f"N^(-{beta:.3f}) R²={fit['fit_quality_r2']:.3f}") if 'power_law_fit_inv_sqrt' in result: fit = result['power_law_fit_inv_sqrt'] ax.plot([], [], ' ', label=f"1/√N fit: R²={fit['fit_quality_r2']:.3f}") ax.set_xlabel('Number of zeros N') ax.set_ylabel('KS distance to Wigner surmise') ax.set_title('GUE Convergence Rate: KS Distance vs N (NOVEL)') ax.legend() ax.grid(True, which='both', linestyle='--', alpha=0.5) return self._save("vmix_gue_convergence.png") def plot_cramer_gaps(self, result): fig, ax = plt.subplots(figsize=(8, 5)) thresholds = np.array(result['thresholds']) emp = np.array(result['empirical_survival']) cra = np.array(result['cramer_survival']) gra = np.array(result['granville_survival']) ax.semilogy(thresholds, emp, 'o', label='Empirical', markersize=4, alpha=0.7) ax.semilogy(thresholds, cra, '-', label='Cramér: e^{-λ}', color='red') ax.semilogy(thresholds, gra, '--', label='Granville: e^{-3.56λ}', color='green') ax.set_xlabel('λ = gap / (ln p)²') ax.set_ylabel('P(ratio > λ)') ax.set_title("Cramér Gap Tail Distribution (up to 5M)") ax.legend() ax.grid(True, which='both', linestyle='--', alpha=0.5) return self._save("vmix_cramer_gaps.png") def plot_lindeloef(self, result): fig, ax = plt.subplots(figsize=(8, 5)) gamma = np.array(result['gamma_values']) ratios = np.array(result['ratios']) ax.plot(gamma, ratios, 'o', markersize=3, alpha=0.5, color='steelblue') ax.axhline(y=result['bourgain_bound'], color='red', linestyle='--', label=f"Bourgain bound = {result['bourgain_bound']:.4f}") ax.set_xlabel('γ_n') ax.set_ylabel('log|ζ(1/2+iγ)| / log(γ)') ax.set_title('Lindelöf Hypothesis: Empirical Exponent θ') ax.legend() ax.set_xscale('log') return self._save("vmix_lindeloef.png") def plot_chebyshev_bias(self, result): fig, ax = plt.subplots(figsize=(8, 5)) x = np.array(result['sample_x']) ratios = np.array(result['ratios_4']) ax.plot(x, ratios, '-', color='steelblue', linewidth=1) ax.axhline(y=1.0, color='red', linestyle='--', label='Equal counts') ax.set_xlabel('x') ax.set_ylabel('π(x;4,3) / π(x;4,1)') ax.set_title("Chebyshev Bias: Primes mod 4") ax.set_xscale('log') ax.legend() return self._save("vmix_chebyshev_bias.png") def plot_lehmer_phenomena(self, result): fig, ax = plt.subplots(figsize=(8, 5)) bins = np.array(result['bin_centers']) hist = np.array(result['spacing_histogram']) wigner = np.array(result['wigner_prediction']) ax.plot(bins, hist, label='Empirical', color='steelblue', linewidth=1.5) ax.plot(bins, wigner, '--', label='GUE Wigner', color='red', linewidth=1.5) ax.set_xlabel('Normalized spacing s') ax.set_ylabel('Density') ax.set_title(f"Lehmer Phenomena: Spacing Distribution (min={result['min_normalized_spacing']:.5f})") ax.legend() return self._save("vmix_lehmer_phenomena.png") def plot_new_strategies_comparison(self, results_list): fig, ax = plt.subplots(figsize=(8, 5)) names = [r.get('strategy', 'unknown') for r in results_list] values = [r.get('mae', 0) or r.get('mean_persistence_entropy', 0) for r in results_list] colors = ['steelblue', 'green', 'orange'] ax.bar(range(len(names)), values, color=colors[:len(names)]) ax.set_xticks(range(len(names))) ax.set_xticklabels([n.replace('_', '\n') for n in names], fontsize=8) ax.set_ylabel('Metric (lower = better for MAE)') ax.set_title('New Strategy Performance Comparison') return self._save("vmix_new_strategies_comparison.png") def plot_entropy_convergence(self, result): fig, ax = plt.subplots(figsize=(8, 5)) sizes = np.array(result['window_sizes']) ent = np.array(result['entropies']) ax.plot(sizes, ent, 'o-', color='steelblue', linewidth=2, markersize=6) ax.set_xlabel('Window size (zeros)') ax.set_ylabel('Shannon entropy of spacings') ax.set_title('Entropy of Zero Spacing Distribution') ax.set_xscale('log') ax.grid(True, linestyle='--', alpha=0.5) return self._save("vmix_entropy_convergence.png") def plot_ktuple_comparison(self, result): fig, ax = plt.subplots(figsize=(8, 5)) names = [] errors = [] for k, v in result['patterns'].items(): names.append(k) errors.append(v['relative_error']) ax.barh(range(len(names)), errors, color='steelblue') ax.set_yticks(range(len(names))) ax.set_yticklabels(names) ax.set_xlabel('Relative error |observed - predicted| / predicted') ax.set_title('Hardy-Littlewood k-Tuple Accuracy') ax.set_xlim(0, max(errors) * 1.2) return self._save("vmix_ktuple_accuracy.png") def get_files(self): return self.files