""" Unified Zeta Engine (v1 + v2 combined) - Zero computation (v1) - Spectral analysis: pair correlation, GUE, Δ₃, form factor (v2) - Mertens function (v2) - Chebyshev ψ(x) (v2) - Operator candidate search (v2) """ import numpy as np from mpmath import mp, mpf, mpc, zetazero, zeta, gamma, pi, log, exp, sqrt, fabs from mpmath import re as mp_re, im as mp_im, siegeltheta from typing import List, Dict, Tuple from dataclasses import dataclass, field from datetime import datetime import json @dataclass class ZetaZero: index: int imaginary_part: float real_part: float = 0.5 verified: bool = False precision: int = 50 def to_complex(self): return complex(self.real_part, self.imaginary_part) class UnifiedZetaEngine: def __init__(self, precision: int = 50, zeros_file: str = None): mp.dps = precision self.precision = precision self._zeros_cache: Dict[int, ZetaZero] = {} self._log: List[str] = [] self.zeros_file = zeros_file self.loaded_zeros = [] if zeros_file: self._load_zeros_file(zeros_file) def log(self, msg: str): self._log.append(f"[{datetime.now().strftime('%H:%M:%S')}] {msg}") def _load_zeros_file(self, filepath: str): with open(filepath) as f: for line in f: line = line.strip() if line and not line.startswith('#'): try: self.loaded_zeros.append(float(line)) except ValueError: pass self.log(f"Loaded {len(self.loaded_zeros)} zeros from {filepath}") def compute_zeros(self, n: int = 100) -> List[ZetaZero]: zeros = [] for k in range(1, n + 1): if k in self._zeros_cache: zeros.append(self._zeros_cache[k]) continue z = zetazero(k) zero = ZetaZero( index=k, imaginary_part=float(mp_im(z)), real_part=float(mp_re(z)), verified=abs(float(mp_re(z)) - 0.5) < 1e-10, precision=self.precision ) zeros.append(zero) self._zeros_cache[k] = zero return zeros def get_loaded_zeros(self, n: int = None) -> List[ZetaZero]: if n is None: n = len(self.loaded_zeros) zeros = [] for i, gamma in enumerate(self.loaded_zeros[:n]): zeros.append(ZetaZero(index=i + 1, imaginary_part=gamma, real_part=0.5, verified=True)) return zeros def pair_correlation_function(self, zeros: List[ZetaZero], alpha_max: float = 5.0, n_bins: int = 200, use_normalization: bool = True) -> Dict: gamma = np.array([z.imaginary_part for z in zeros]) n = len(gamma) spacings = np.diff(gamma) mean_s = np.mean(spacings) diffs = [] max_pairs = 500000 count = 0 step = max(1, n // 2000) for i in range(0, n, step): for j in range(i + 1, min(i + 500, n)): d = (gamma[j] - gamma[i]) / mean_s if use_normalization else (gamma[j] - gamma[i]) if d < alpha_max: diffs.append(d) count += 1 if count >= max_pairs: break if count >= max_pairs: break diffs = np.array(diffs) hist, edges = np.histogram(diffs, bins=n_bins, range=(0, alpha_max)) bin_width = edges[1] - edges[0] density = hist / (len(diffs) * bin_width) alpha_centers = (edges[:-1] + edges[1:]) / 2 gue = 1.0 - (np.sin(np.pi * alpha_centers) / (np.pi * alpha_centers + 1e-15)) ** 2 return { 'alpha': alpha_centers.tolist(), 'empirical': density.tolist(), 'gue_prediction': gue.tolist(), 'n_pairs': len(diffs), 'n_zeros': n, } def compute_gue_deviation(self, zeros: List[ZetaZero]) -> float: pc = self.pair_correlation_function(zeros, alpha_max=3.0, n_bins=100) emp = np.array(pc['empirical']) gue = np.array(pc['gue_prediction']) dx = pc['alpha'][1] - pc['alpha'][0] if len(pc['alpha']) > 1 else 0.1 l2 = float(np.sqrt(np.sum((emp - gue) ** 2) * dx)) return l2 def mertens_function(self, x_max: int) -> Dict: mu = [0] * (x_max + 1) mu[1] = 1 is_prime = [True] * (x_max + 1) primes = [] for i in range(2, x_max + 1): if is_prime[i]: primes.append(i) mu[i] = -1 for p in primes: if i * p > x_max: break is_prime[i * p] = False if i % p == 0: mu[i * p] = 0 break else: mu[i * p] = -mu[i] M = np.cumsum(mu) x_arr = np.arange(1, x_max + 1) ratios = np.abs(M[1:]) / np.sqrt(x_arr) return { 'x': x_arr.tolist(), 'M': M[1:].tolist(), 'ratios': ratios.tolist(), 'max_ratio': float(np.max(ratios)), 'mean_ratio': float(np.mean(ratios)), } def chebyshev_psi(self, x_max: int) -> Dict: is_prime = np.ones(x_max + 1, dtype=bool) is_prime[:2] = False for i in range(2, int(x_max ** 0.5) + 1): if is_prime[i]: is_prime[i * i::i] = False primes = np.where(is_prime)[0] lambda_n = np.zeros(x_max + 1) for p in primes: log_p = np.log(p) pk = p while pk <= x_max: lambda_n[pk] = log_p pk *= p psi = np.cumsum(lambda_n) x_arr = np.arange(1, x_max + 1) errors = np.abs(psi[1:] - x_arr) bounds = x_arr ** 0.5 * (np.log(x_arr + 1)) ** 2 return { 'x': x_arr.tolist(), 'psi': psi[1:].tolist(), 'errors': errors.tolist(), 'bounds': bounds.tolist(), 'within_bound': bool(np.all(errors < bounds)), } def get_log(self): return self._log