Repository navigation
Formules_zeta
Version enrichie — Projet Riemann_Lab · hprzeta Mise à jour : 2 juin 2026 · Auteur : hprzeta
- leçons Vérif A/B v4.1 : erreur de position (perturbation 1ᵉʳ ordre) et distinction comptage/position
- leçon v4.2 : finition Newton (ordre 2, dérivée analytique, critère dps vs LMFDB absolu)
- leçon v4.2 mesurée : Newton réfuté (Z' coûteux), goulot = mpmath.siegelz, pas l'algorithme
- §17 modèle de coût complet (mur de latence) :
$t_{\text{appel}} \propto N \text{dps}^2$ ,$T_{\text{total}} \approx n n_{\text{itér}} t_{\text{appel}}/W$ , leviers et régimes
Convergente absolument pour
Ce produit encode la distribution des nombres premiers : chaque facteur correspond à un premier
Propriété clé :
Énoncé : Tous les zéros non triviaux de
-
Zéros triviaux :
$s = -2, -4, -6, \ldots$ (pôles de$\sin(\pi s/2)$ masqués par$\Gamma$ ). -
Zéros non triviaux : dans la bande critique
$0 < \text{Re}(s) < 1$ . On les note$\rho = \tfrac{1}{2} + i\gamma_n$ (si HR vraie).
Détecter les changements de signe de
Pourquoi pas Re(ζ) directement ? En v1/v2, utiliser
$\text{Re}(\zeta(\tfrac{1}{2}+it))$ comme détecteur provoquait des faux positifs : la rotation de phase$e^{-i\theta(t)}$ peut annuler la partie réelle sans que$\zeta = 0$ . La fonction$Z(t)$ élimine ce problème.
Avantage : un seul appel à zeta() au lieu de deux.
compute_zeros_v2.py L171 : mp.im(loggamma(mp.mpf("0.25") + mp.mpc(0, t)/2)) - (t/2)*mp.log(mp.pi)
Pour
Coefficients de Bernoulli (origine des termes correctifs) :
Seuil de bascule : theta_rapide.py utilise l'asymptotique pour
où
avec
Pour un tableau de points
riemann_siegel_batch.py L291 (Z_batch) :
phases = thetas[:, None] - ts[:, None] * log_ns[None, :] # shape (M, N_max)
Z = 2.0 * np.dot(np.cos(phases), inv_sqn) # shape (M,)Gain mesuré : ×7 à ×15 (CPU numpy) · ×8 à ×12 supplémentaires (GPU GTX 960M).
⚠️ Le code ci-dessus est la version NAÏVE (un$N_{\max}$ fixe pour tout le bloc). Elle est fausse dès que le bloc couvre une large plage de$t$ — voir §5.4.
Symptôme mesuré (v4.1) : la première version Z_batch produisait 359 désaccords
de signe vs mpmath.siegelz → autant de zéros potentiellement ratés lors d'un run long.
Solution Z_vect_correct : masque booléen ligne par ligne
Chaque ligne
N_k = np.floor(np.sqrt(ts / (2*np.pi))).astype(int) # N(t_k) propre à chaque point
mask = ns[None, :] <= N_k[:, None] # (M, N_max) booléen
phases = thetas[:, None] - ts[:, None] * log_ns[None, :]
Z = 2.0 * np.sum(np.where(mask, np.cos(phases) * inv_sqn, 0.0), axis=1)Résultat après correction : 0 désaccord sur 4 plages testées
(
La somme tronquée à la correction
| Niveau de correction | Précision typique |
|---|---|
| RS sans correction | |
| RS + |
|
| RS + |
|
| RS + 3–5 termes | |
mpmath.siegelz |
Conséquence pratique (v4.1) : l'écart
Point clé : l'erreur de troncature §5.5 est une erreur sur l'amplitude de
$Z$ . Ce qui compte pour un zéro, c'est l'erreur de position qui en découle. Les deux sont liées, mais ne sont pas du même ordre de grandeur — d'où le résultat de la Vérif B.
Si l'affinage (Illinois en C, ou findroot) cherche une racine de la fonction tronquée
Lecture de la formule — l'erreur de position est l'erreur d'amplitude divisée par la pente du croisement. Deux régimes :
| Régime du croisement | Pente |
Erreur de position |
|---|---|---|
| Croisement raide | grande | erreur faible ( |
| Croisement plat (zéro proche d'un extremum, ou paire de zéros proches) | petite | erreur amplifiée (jusqu'à |
C'est pourquoi le même reste
Exemple mesuré (Vérif B, bracket
| Méthode | Racine trouvée | Cible |
|---|---|---|
illinois_mpfr.so (RS tronquée |
racine de |
|
mpmath.findroot(siegelz) dps=50 |
vrai zéro de |
|
| Écart |
|
L'écart
Conclusion architecturale : Illinois_C pur sur mpmath.siegelz) après
le pré-affinage C. Voir §6.4 pour les trois architectures comparées.
Illinois est une variante de la méthode de la sécante avec correction anti-stagnation.
Initialisation :
Itération :
- Si
$f_c \cdot f_b < 0$ :$a \leftarrow b$ ,$f_a \leftarrow f_b$ - Si
$f_c \cdot f_a < 0$ : correction Illinois :$f_a \leftarrow f_a / 2$ (évite la stagnation)
Convergence : superlinéaire (ordre
| Paramètre | Valeur | Justification |
|---|---|---|
tol_affinage |
1e-12 |
Cohérent avec 35 dps (v2 utilisait 1e-20 → impossible à 35 dps) |
dps_affinage |
35 | Suffisant pour |
maxsteps |
80 | Convergence garantie en < 30 itérations typiquement |
solver |
"illinois" |
Via mpmath.findroot(..., solver="illinois")
|
compute_zeros_v3.py → parallel_scanner.py L258 : findroot(Z_fast, (a,b), solver="illinois", tol=1e-12, maxsteps=80)
| Version | Fichier | Fonction détecteur | solver |
tol |
dps |
maxsteps |
|---|---|---|---|---|---|---|
| v1 |
compute_zeros_v1.py L55 |
zeta_on_critical = Re(ζ) |
'newton' puis 'bisect'
|
1e-12 |
50 | 50 |
| v2 |
compute_zeros_v2.py L197–201 |
siegelz(t) scalaire |
"illinois" |
1e-20 |
50 | 100 |
| v3 |
parallel_scanner.py L258 |
Z_fast(t) vectorisé |
"illinois" |
1e-12 |
35 | 80 |
Problème v1 : Re(ζ) comme détecteur → faux positifs (voir §3.1).
Problème v2 : tol=1e-20 irréalisable à 50 dps (≈ 50 chiffres) → timeout / dépassement maxsteps.
Solution v3 : tol=1e-12 cohérent avec 35 dps (12 chiffres demandés < 35 disponibles).
La Vérif B a fait apparaître une tension fondamentale : la racine que produit le C est
rapide mais imprécise (§5.6), tandis que la racine mpmath.siegelz est précise mais lente.
Trois architectures résolvent ce compromis différemment :
| Architecture | Mécanisme de la racine finale | Vitesse | Précision position | Statut |
|---|---|---|---|---|
| Illinois_C pur (v4.1 brut) | racine de |
~41 z/s |
|
rejeté pour catalogue |
| Callback Python/C (v5) |
siegelz appelé dans la boucle C |
~1.1 z/s |
|
casse le parallélisme (GMP non thread-safe) |
| Hybride post-fork (cible v4.2) | Illinois_C pré-affine, puis findroot(siegelz) poli en Python
|
à mesurer |
|
architecture retenue |
Pourquoi l'hybride post-fork préserve le parallélisme ×4 : la boucle C ne fait aucun
appel Python (donc aucun verrou GMP partagé entre processus). Le C rend une borne approchée
Comme findroot converge en très peu
d'itérations (la convergence superlinéaire de la sécante part d'un excellent point initial),
ce qui limite le surcoût du raffinage exact.
Distinction à ne jamais confondre : ce raffinage siegelz ne sert qu'à la précision des
positions (comparaison LMFDB). Il n'est pas nécessaire pour vérifier HR jusqu'à
Mesure clé (validation hybride v4.2) : la finition
findroot(siegelz)après pré-affinage C donne$\text{Écart}_P = 0.00$ (vs LMFDB) sur les trois plages$t\approx 350, 1000, 9900$ — précision parfaite, critère$<10^{-10}$ largement battu. Reste à optimiser la vitesse du raffinage, où le bon levier est le nombre d'évaluations desiegelz, pas ledps.
La vitesse de convergence d'un solveur se mesure par son ordre
| Solveur | Ordre |
Évaluations / pas | Évaluations pour passer |
|---|---|---|---|
| Illinois (sécante modifiée) | 1 | ~27 | |
| Newton |
|
2 ( |
~6 (3 pas) |
Partant du point initial fourni par le C,
3 pas suffisent pour passer sous
- Illinois n'évalue que
$Z$ (1 appelsiegelzpar pas, ~296 ms à$t\approx 9000$ ) ; - Newton évalue
$Z$ et$Z'$ (2 appels par pas), or$Z'$ est plus cher que$Z$ (§6.5.2).
Le produit mpmath.siegelz à grand
Leçon épistémique : l'analyse d'ordre de convergence ci-dessus était une prédiction a priori, valable seulement sous l'hypothèse « coût/éval constant et
$Z'$ aussi cheap que$Z$ ». Cette hypothèse est fausse (§6.5.2) ; la mesure (§6.5.5) prime sur la prédiction.
Garde-fou : Newton n'est pas borné. Si le croisement est plat (
$Z'$ petit, §5.6) ou sur une paire de zéros proches, l'itéré peut sortir du bracket$[\gamma_c \pm \delta]$ . Prévoir un fallback Illinois borné dans ce cas.
Newton requiert
soustrait deux nombres quasi égaux : annulation catastrophique qui détruit ~la moitié des
chiffres significatifs. Une dérivée imprécise abaisse l'ordre effectif de Newton (
Solution : la dérivée analytique mpmath.siegelz(t, derivative=1) (pas de différence finie).
Elle est calculée exactement, sans annulation, et garantit la cascade quadratique du §6.5.1.
⚠️ Mais elle n'est PAS gratuite (idée fausse corrigée par la mesure, §6.5.5).siegelzne renvoie pas$Z'$ « en bonus » d'un calcul de$Z$ : il le recalcule à part. Or$$\zeta'(s) = -\sum_{n\geq 1}\frac{\ln n}{n^{s}}$$ porte des poids logarithmiques$\ln n$ absents de$\zeta(s)=\sum n^{-s}$ . Cette série converge plus lentement, donc l'évaluation de$Z'$ est plus coûteuse que celle de$Z$ , pas comparable. C'est précisément ce qui annule le gain d'itérations de Newton (§6.5.1).
Piège : le critère LMFDB est absolu (
$|\gamma_{\text{calc}} - \gamma_{\text{LMFDB}}| < 10^{-10}$ ), alors que ledpscontrôle un nombre de chiffres significatifs. Plus$\gamma$ est grand, plus il faut de chiffres pour la même précision absolue.
Pour garantir
À
dps |
Chiffres dispo | Marge au-dessus de |
Verdict |
|---|---|---|---|
| 15 | 15 | ~1 chiffre | ❌ insuffisant (aucune marge pour l'annulation RS interne) |
| 20 | 20 | ~6 chiffres | |
| 25 | 25 | ~11 chiffres | ✅ sûr |
| 30 | 30 | ~16 chiffres | ✅ confortable |
⚠️ Le test sur T=1000 ne révèle PAS ce problème : à T=1000,$\gamma < 396$ (~3 chiffres avant la virgule), donc mêmedps=15paraît suffisant. Le risque n'apparaît qu'au sommet. → Valider tout abaissement dedpssur un échantillon$t\approx 9900\text{–}10 000$ , jamais sur T=1000.
Ce qui suit est la prédiction faite avant mesure. Elle s'est révélée trop optimiste : elle suppose 6 évaluations toutes au même coût que
$Z$ , alors que les évaluations de$Z'$ sont plus chères (§6.5.2). Conservée ici pour la traçabilité du raisonnement.
Le coût d'une opération multi-précision croît comme
soit T=10000 en ~50 min. Prédiction non confirmée : la mesure donne ~0.4 z/s (§6.5.5).
Mesure (Vérif B v3, dps=25,
$t\approx 9000$ ) : Newton + dps=25 donne 0.4 z/s — plus lent que Illinois + polish dps=30 (0.5 z/s). La prédiction du §6.5.1/§6.5.4 est réfutée. Précision toujours parfaite ($\text{Écart}_P = 0.00$ ) dans les deux cas.
Bilan de performance — toutes les options comparées (
| Approche | z/s ×4 | Précision (vs LMFDB) | Commentaire |
|---|---|---|---|
Illinois_C pur (commit d9bb267) |
41 |
|
racines de |
Illinois + polish findroot dps=30 |
0.5 |
|
27 itér × ~296 ms |
| Newton + dps=25, 5 pas | 0.4 |
|
|
Cause racine confirmée : à siegelz coûte ~296 ms (somme RS à
mpmath.siegelz à grand siegelz.
Conséquence — deux livrables, deux régimes (rappel §11.1, distinction comptage/position) :
| Run | Zéros | Temps ×4 estimé | Usage |
|---|---|---|---|
| T=300 | 138 | ~60 s (mesuré, tout en fallback mpmath |
test |
| T=1000 | ~396 | ~16 min | validation complète faisable |
| T=10000 | ~10 142 | ~7 h | catalogue de positions |
Les 20 premières références LMFDB (mpmath (
Décision ouverte : pour vérifier HR jusqu'à T=10000, Illinois_C pur (41 z/s, ~5 min) suffit déjà (comptage Turing, §11.1). Le polish lent (~7 h) n'est requis que pour produire un catalogue de positions comparables à LMFDB — or ce catalogue existe déjà via v2/v3 (CSV 10 142 zéros, 50 dps). À trancher selon le livrable visé, pas à subir par défaut.
La formule de Riemann-von Mangoldt donne l'espacement moyen :
Division par 5 (non par 3 comme en v2) pour sécurité contre les paires de zéros proches.
Plafond absolu à 0.10 pour
⚠️ Bug noté dans le code v3 :compute_zeros_v3.pyL220 retournemin(round(step_theorique, 3), 0.02)alors que les commentaires L207 et L213–215 spécifient0.10. La valeur documentée cible est 0.10 (intention v3). Corriger dans la Phase C ou un patch v3.1.
compute_zeros_v3.py L205–219 :
def step_adaptatif(T_MAX: float) -> float:
# STEP sûr = min(espacement_moyen / 5, 0.10)
espacement_moyen = 2 * math.pi / math.log(T_MAX / (2 * math.pi))
step_theorique = espacement_moyen / 5.0
return min(round(step_theorique, 3), 0.10) # plafond cibleValeurs typiques :
| Espacement moyen | STEP (cible v3) | |
|---|---|---|
| 1 000 | 1.24 | 0.10 (plafonné) |
| 10 000 | 0.84 | 0.10 (plafonné) |
| 100 000 | 0.70 | 0.10 (plafonné) |
| 500 000 | 0.60 | 0.10 (plafonné) |
où
Propriété :
⚠️ Erreur classique corrigée en v3 : la formule incorrecte$\dfrac{T}{2\pi}\ln\dfrac{T}{2\pi}$ (sans le$e$ au dénominateur) sous-estimait$N(T)$ de ~64% pour$T = 100 000$ (49 346 au lieu de 138 067 zéros attendus), faussant toutes les estimations de temps.
Comparaison numérique :
|
|
|
Erreur | |
|---|---|---|---|
| 1 000 | 396 | 229 | −42% |
| 10 000 | 10 142 | 5 968 | −41% |
| 100 000 | 138 067 | 49 346 | −64% |
Implémentation discrète (turing_validation.py L127–158) :
# Chemin horizontal : σ de 3.0 (où arg ζ ≈ 0) à ½
sigmas = np.linspace(3.0, 0.5, 51) # 50 pas
variation = 0.0
arg_prev = 0.0
for sigma in sigmas[1:]:
s = mpc(sigma, T)
z = zeta(s)
arg_cur = float(mp.atan2(im(z), re(z)))
# Correction de branche — suivi continu (évite les sauts de 2π)
d = arg_cur - arg_prev
if d > math.pi: arg_cur -= 2 * math.pi
if d < -math.pi: arg_cur += 2 * math.pi
variation += arg_cur - arg_prev
arg_prev = arg_cur
S_T = variation / math.piPourquoi démarrer à σ = 3 ? Pour
Stratégie à 3 niveaux selon l'opération :
| Opération | Précision | Justification |
|---|---|---|
θ asymptotique (float64) |
15 chiffres | Numpy/Python natif — ×50 |
| Détection Z_fast | 25 dps | Suffisant pour signe de Z(t) |
| Affinage Illinois | 35 dps | Atteint tol = 1e-12 en 35 dps |
| Validation / publication | 50 dps | Pour les 1000 premiers zéros |
Avant correction : tout à 50 dps → θ via
loggammampmath → 3× plus lent.
La conjecture de Montgomery (1973) affirme que
Tracé dans v3 : histogramme de
Critère de complétude du calcul :
- Calculer
$N_{\text{exact}}(T) = \lfloor\theta(T)/\pi\rfloor + 1 + \text{round}(S(T))$ - Compter
$n_{\text{calc}} = |{t_k \leq T}|$ -
Complet
$\iff n_{\text{calc}} = N_{\text{exact}}(T)$
Appliqué aux points de contrôle
Idée centrale (leçon Vérif B v4.1) : vérifier HR jusqu'à
$T$ et cataloguer les positions précises des zéros sont deux objectifs distincts qui ne demandent pas la même chose. La méthode de Turing valide le nombre de zéros, jamais leur position.
La vérification numérique de HR sur
Si cette égalité tient, alors
Conséquence directe — cette condition ne fait intervenir que le dénombrement des
changements de signe. Déplacer chaque position raffinée de
| Vérif | Ce qu'elle teste | Résultat v4.1 | Interprétation |
|---|---|---|---|
| A | comptage Turing + LMFDB sur les 20 premiers (fallback mpmath, |
✅ 138/138, Turing COMPLET | la vérification de HR tient |
| B | précision des positions à grand |
❌ erreurs |
le catalogue de positions exige une finition mpmath (§6.4) |
En résumé : v4.1 (Illinois_C pur, 41 z/s) suffit pour vérifier HR jusqu'à
L'argument §11.1 tient sous une hypothèse : que le comptage des changements de signe soit
exact, c.-à-d. que la détection ne manque aucun croisement et n'en invente aucun.
Près d'un zéro où Z_batch float64 (jusqu'à 359 désaccords de signe, voir §5.4), corrigé par Z_vect_correct.
Garde-fous en place :
- détection via
Z_vect_correct(masque$n \leq N(t_k)$ ) → 0 désaccord sur les 4 plages testées (§5.4) ; - pas de balayage STEP sécurisé (§7) → évite de sauter une paire de zéros proches ;
- Turing COMPLET sur le run = preuve a posteriori que le comptage est bon (aucun manquant).
Tant que Turing ressort COMPLET, l'égalité §11.1 est satisfaite et HR est vérifiée sur l'intervalle — indépendamment de la précision des positions individuelles. C'est le point de vigilance n°1 à surveiller au-delà de
$T=10 000$ .
|
|
|
|---|---|
| 1 | 14.134725141734693 |
| 2 | 21.022039638771555 |
| 3 | 25.010857580145688 |
| 4 | 30.424876125859513 |
| 5 | 32.935061587739189 |
| 6 | 37.586178158825671 |
| 7 | 40.918719012147495 |
| 8 | 43.327073280914999 |
| 9 | 48.005150881167159 |
| 10 | 49.773832477672302 |
| 11 | 52.970321477714460 |
| 12 | 56.446247697063246 |
| 13 | 59.347044002602353 |
| 14 | 60.831778524609882 |
| 15 | 65.112544048081607 |
| 16 | 67.079810529494173 |
| 17 | 69.546401711173978 |
| 18 | 72.067157674481890 |
| 19 | 75.704690699083934 |
| 20 | 77.144840069680455 |
Seuil de validation LMFDB :
La somme porte sur tous les zéros non triviaux
Conséquence de HR :
| Indicateur | Valeur |
|---|---|
| Zéros calculés | 10 142 |
| 9 998.85 | |
| Vitesse | ~3.59 z/s (batch CPU) |
| Score LMFDB | 20/20 zéros à |
| Validation Turing | ✅ COMPLET (aucun manquant) |
| Méthode détection | Riemann-Siegel vectorisé (Z_batch) |
| Méthode affinage | Illinois (35 dps, tol |
Version v1 : première implémentation, abandon vers
compute_zeros_v1.py L45 :
# Détecteur : partie réelle de ζ(½ + it) — MÉTHODE INCORRECTE
# Cause des faux positifs (voir §3.1 pour explication)
return float(zeta(0.5 + 1j * t).real)Formule sous-jacente (incorrecte comme détecteur) :
compute_zeros_v1.py L132 : if zeta_abs < 1e-9
compute_zeros_v1.py L55, L65 :
# Tentative 1 : Newton (rapide mais peut diverger)
zero = findroot(zeta_on_critical, t_mid, solver='newton', tol=1e-12, maxsteps=50)
# Fallback : bisection (robuste, convergence linéaire)
zero = findroot(zeta_real, (t_left, t_right), solver='bisect', tol=1e-12)Problème Newton : diverge si la dérivée est petite (zéro simple mais proche d'un extremum de Re(ζ)).
Remplacé par : Illinois en v2/v3 (voir §6).
Version v2 : Illinois introduit, siegelz scalaire, tol=1e-20 (trop strict).
compute_zeros_v2.py L171 :
theta = (
mp.im(loggamma(mp.mpf("0.25") + mp.mpc(0, t) / 2))
- (t / 2) * mp.log(mp.pi)
)Formule correspondante :
compute_zeros_v2.py remplace Re(ζ) par siegelz(t) de mpmath :
Amélioration v2 : plus de faux positifs.
Problème v2 : appel scalaire — pas de vectorisation → lent.
compute_zeros_v2.py L191–201 :
def affiner_zero(t_gauche: float, t_droite: float, tol: float = 1e-20) -> float:
t0 = findroot(
lambda t: siegelz(t),
(t_gauche, t_droite),
solver="illinois",
tol=tol, # 1e-20 à 50 dps → ~1e-48 impossible
maxsteps=100 # dépassé systématiquement
)Problème : tol=1e-20 exige 20 chiffres exacts dans la solution, mais à 50 dps l'Illinois ne converge pas en 100 steps — timeout systématique sur les zéros rapprochés.
Solution v3 : tol=1e-12 à 35 dps (voir §6.2).
Synthèse des formules établies le 2 juin 2026. Version animée :
docs/animation_mur_latence.html. Rassemble en un seul endroit le coût de localisation d'un zéro précis et les leviers d'optimisation. Renvois : troncature §5.5, erreur de position §5.6, goulot mesuré §6.5.5, espacement §7.1, comptage §8 et §11.1.
Localiser un zéro précis n'est pas une opération atomique : c'est une chaîne d'évaluations de
Niveau 1 — coût d'une évaluation
Conséquence directe :
Niveau 2 — coût d'un zéro. L'affinage (sécante / Illinois, §6.1) est une récurrence : l'itération
Il n'existe aucun parallélisme à l'intérieur d'un zéro.
Niveau 3 — coût du run complet. Les
| Grandeur | Calcul | Valeur |
|---|---|---|
| Termes |
37 | |
Coût d'un appel siegelz
|
mesuré (dps ≈ 30) | ≈ 296 ms |
| Itérations / zéro | findroot Illinois | ≈ 27 |
| Coût d'un zéro |
|
≈ 8,0 s |
| Débit (4 workers) | ≈ 0,5 z/s | |
| Run |
|
≈ 5–7 h |
La latence d'un appel
siegelzà grand$t$ est le goulot mesuré (§6.5.5) : à$t \approx 9000$ , ~296 ms pour$N \approx 37$ termes. Réduire$n_{\text{itér}}$ (Newton) ne sert à rien — c'est$t_{\text{appel}}$ qu'il faut attaquer.
Pour passer de ~7 h à la cible de 30 min :
Position de chaque levier dans
| Levier | Agit sur | Gain | Réaliste (i7, 4 cœurs) |
|---|---|---|---|
| Cœurs |
dénominateur (linéaire) | ×14 ⇒ |
❌ |
| GPU GTX 960M | détection seule (10–20 %) | nul sur l'affinage | ❌ |
| RAM / swap | hors formule (compute-bound) | nul (swap ≈ ×1000 plus lent) | ❌ |
|
|
numérateur | réfuté §6.5.5 | ❌ |
| ≈ ×1,4 (plancher §17.4) | |||
| Librairie Arb |
|
≈ ×10–20 | ✅ → ~20–25 min |
Nombre de cœurs requis pour la cible (librairie mpmath inchangée) :
Le vrai levier est logiciel, pas matériel : seule la bascule mpmath.siegelz → Arb (acb_dirichlet_hardy_z, arithmétique de boules en C) réduit
La précision absolue requise à hauteur
On ne peut donc pas réduire
Le mur de latence ne pèse que dans un des deux objectifs (rappel §11.1) :
| Objectif | Méthode | Temps T=10000 |
|---|---|---|
| Vérifier HR jusqu'à |
Illinois_C pur, sans polish siegelz
|
≈ 41 z/s ⇒ ~4 min |
| Catalogue de positions |
polish siegelz, ou réutiliser le CSV v2 existant
|
~7 h, ou 0 min |
Pour la simple vérification de HR, le niveau 2 lent disparaît : le coût d'un appel
siegelz(§17.1) ne pèse que dans le régime « catalogue de positions ». Ce catalogue existe déjà (CSV v2, 10 142 zéros, 50 dps).
| Phase | temps cumulé (4 workers) | appels | ms/appel | % mur×W |
|---|---|---|---|---|
mpmath_petit_t |
11.27 s | 138 | 81.65 | 35.6 % |
illinois_C |
5.22 s | 511 | 10.21 | 16.5 % |
turing |
2.33 s | 1 | 2 330.99 | 7.4 % |
detection |
0.09 s | 4 | 21.53 | 0.3 % |
Vitesse globale : 82 z/s (×80 vs v3 à 1.02 z/s). Turing COMPLET, LMFDB 19/20.
Le noyau Illinois_C est réellement rapide en pipeline (10 ms/appel). Le goulot
résiduel est mpmath_petit_t : les 138 zéros à mpmath.siegelz
à dps=35. Ce coût croît en
À dps=35 ≈ 116 bits) coûte cher par terme. La détection (Z_vect_correct) est négligeable.
Tentative : with _mp.workprec(50): _mp.findroot(_mp.siegelz, (a,b), ...).
Résultat : gain marginal (~12 %). Cause : mpmath.siegelz re-lit mp.dps
global (35) à chaque appel interne. Le contexte workprec contrôle la précision
de la boucle d'itération Illinois, pas la précision des évaluations de la fonction.
La seule façon d'appeler siegelz en float64 natif :
_mp.fp.siegelz(t) # float64 pur — précision ~1e-15, coût ~×40 plus faiblePour tol = 1e-12 → fp.siegelz (float64) est
amplement suffisant. La précision float64 (STEP = 0.1.
Gain estimé : 81 ms → ~2 ms par appel → 138 zéros en ~0.3 s au lieu de 11.3 s → pipeline T=1000 : ~6 s → ~200+ z/s.
Auteur : hprzeta — Riemann_Lab — Mise à jour : 3 juin 2026
Commit
581e34d— brancheRiemann_Lab_C. Contexte : la version antérieureillinois_mpfr(a, b, tol)recalculait$Z(a)$ et$Z(b)$ en C via$Z_{\text{mpfr}}$ (RS tronquée$C_0+C_1$ ). SiZ_vect_correct(Python) et$Z_{\text{mpfr}}$ (C) divergeaient en signe sur les bornes, Illinois cherchait un « pseudo-zéro RS » décalé de ~0.3 — sans déclencher de fallback.
Pour
Nouvelle signature C :
double illinois_refine(double a, double b,
double fa, double fb, // ← passés depuis Python
int prec_bits,
double tol, int max_iter);fa = Z_vals[i] et fb = Z_vals[i+1] sont les valeurs déjà calculées par
Z_vect_correct lors du balayage → zéro recalcul, et l'encadrement initial est
garanti cohérent avec
Interface ctypes correspondante :
lib.illinois_refine.restype = ctypes.c_double
lib.illinois_refine.argtypes = [
ctypes.c_double, # a
ctypes.c_double, # b
ctypes.c_double, # fa = Z(a) calculé par Z_vect_correct
ctypes.c_double, # fb = Z(b)
ctypes.c_int, # prec_bits (170 par défaut)
ctypes.c_double, # tol
ctypes.c_int, # max_iter
]Les itérations intermédiaires évaluent Z_double est supprimé du .so.
| Test | Valeur | Statut |
|---|---|---|
test_illinois.py (10 premiers LMFDB) |
10/10 illinois_C pur · erreurs |
✅ |
| Benchmark |
×30.78 vs mpmath.findroot (objectif ×5–10 dépassé) |
✅ |
| Run T=1000 — zéros / vitesse / Turing / LMFDB | 649/649 · 16.15 z/s · COMPLET · 19/20 | ✅ |
| Run T=10 000 — zéros / vitesse / Turing / LMFDB | 10 141 · 18.65 z/s · COMPLET · 19/20 | ✅ |
| illinois_C (t ≥ 300) sur T=10 000 | 98.6 % (10 004 zéros) · 0 fallback | ✅ |
Profil phases — Run T=10 000 (cumulé 4 workers) :
| Phase | Temps cumulé | Appels | ms/appel |
|---|---|---|---|
illinois_C |
1 592.5 s | 10 004 | 159 ms |
mpmath_petit_t ( |
79.5 s | 138 | 576 ms |
turing |
35.0 s | 1 | — |
detection |
2.8 s | 20 | 138 ms |
Point de vigilance — LMFDB 19/20 : le zéro
$n°20$ ($\gamma_{20} = 77.1448\ldots$ ) donne un écart de$8.06 \times 10^{-10}$ dans tous les runs (T=300, T=1000, T=10 000). Ce cas limite est stable et reproductible : il ne constitue pas un bug, mais un zéro dont le croisement est suffisamment plat pour que l'erreur de position §5.6 s'approche du seuil$10^{-10}$ sans le franchir.
L'évaluation de PREC = 170 bits (
Comme
| Plage de |
|
ms/appel illinois_C mesuré |
|---|---|---|
| 7–8 | ~58.9 ms | |
| 8–10 | ~70 ms | |
| 21–28 | ~100 ms | |
| 34–39 | ~159 ms |
Rapport de coût :
À
Avec
Ce goulot est incompressible sans changer de bibliothèque (Arb
acb_dirichlet_hardy_z, §12 de Bibliotheques.md) ou de formule (Odlyzko–Schönhage). Aucune optimisation algorithmique à l'intérieur de la boucle Illinois ne peut le réduire.
Le pipeline v4.1 utilise 4 workers sur des segments équilibrés en largeur d'intervalle, pas en nombre de zéros. Deux causes indépendantes créent un déséquilibre :
| Source | Worker affecté | Cause |
|---|---|---|
Goulot mpmath_petit_t) |
Worker 0 |
Concentre les 138 zéros dont l'affinage passe par mpmath.fp.siegelz ou mp.siegelz (fallback légitime, seuil T_SEUIL = 300) |
| Goulot |
Worker 3 |
|
| Worker | Plage | Durée | Zéros affinés illinois_C | Cause dominante |
|---|---|---|---|---|
| 0 | ~450 s | ~2 372 | mpmath_petit_t (138 zéros) + illinois_C moyen-t | |
| 1 | ~530 s | ~2 500 | illinois_C moyen-t | |
| 2 | ~535 s | ~2 630 | illinois_C grand-t | |
| 3 | ~543 s | ~2 500 | illinois_C grand-t ( |
Le worker 3 détermine la durée totale du run (
Pour T=10 000, les 138 zéros à mpmath_petit_t (576 ms/appel dans ces run, 79.5 s total) est absorbé dans la
durée totale sans effet dominant. En pratique, les workers 1–3 compensent ce déséquilibre.
Résolution possible : partitionner les segments en volume de zéros estimé via
| Métrique | Run T=1000 | Run T=10 000 |
|---|---|---|
| Vitesse globale | 16.15 z/s | 18.65 z/s |
| Illinois_C (%) | 78.7 % | 98.6 % |
| mpmath_petit_t | 138 zéros (21.3 %) | 138 zéros (1.4 %) |
| Turing | COMPLET | COMPLET |
| LMFDB | 19/20 | 19/20 |
| Durée | ~40 s (4 workers) | ~9.1 min (4 workers) |
Comparaison v3 : v3 (
compute_zeros_v3.py) atteignait ~3.59 z/s. v4.1 Option B atteint 18.65 z/s → gain ×5.2 sur un run réel T=10 000.
Entrée rapide pour retrouver les chiffres clés de Vérif B sans relire §5.6 et §6.4–6.5.
L'erreur de position d'un zéro affiné par Illinois_C pur (sur
| Méthode d'affinage | Erreur amplitude RS |
Erreur position typique | Critère LMFDB |
|---|---|---|---|
|
Illinois_C pur (Z_mpfr RS |
|
❌ | |
Illinois_C + polish mpmath.siegelz
|
— | ✅ |
Exemple mesuré (Vérif B, bracket
| Méthode | Racine trouvée | Cible LMFDB |
|---|---|---|
illinois_refine C/libmpfr (Z_mpfr |
— | |
mpmath.findroot(siegelz) dps=50 |
||
| Écart |
|
| Objectif | Illinois_C pur | Avec polish siegelz |
|---|---|---|
| Vérifier HR (comptage Turing) | ✅ suffit (~41 z/s) | non requis |
| Catalogue de positions LMFDB | ❌ (~1e-2 erreur) | ✅ (<1e-10, ~0.5 z/s) |
La variabilité
$10^{-4}$ à$10^{-2}$ vient de$|Z'(\gamma)|$ (pente du croisement), pas de$|R(\gamma)|$ qui est quasi-constante à hauteur fixée. Références approfondies : §5.6 (analyse Taylor), §6.4 (architectures), §6.5 (Newton vs Illinois).
| Méthode | temps/appel (ms) | Speedup | T_total estimé |
|---|---|---|---|
mpmath.siegelz dps=35 |
21.13 ms | 1× | ~21 min |
arb_fpwrap_cdouble_hardy_z |
0.77 ms | ×27 | ~1–15 min |
Speedup par tranche :
-
$t \in [100, 1000]$ → ×29 -
$t \in [1000, 5000]$ → ×28 -
$t \in [5000, 10000]$ → ×26
Erreur :
Explication : mpmath utilise MPFR (allocations heap, ~1000 malloc/free par zéro).
Arb reste en double IEEE 754 (registres CPU, 0 allocation).
Illinois converge à tol=1e-12 — précision double largement suffisante.
Accès : ctypes + libflint bundlée python-flint 0.8.0 (pas de sudo apt).
Module : src/calculs/optimisation/arb_wrapper.py (commit b563db2)
La densité locale des zéros de
L'espacement moyen entre deux zéros consécutifs est son inverse :
Valeurs numériques :
|
|
STEP recommandé | |
|---|---|---|
| 100 | ~1.21 | 0.1 |
| 1 000 | ~0.91 | 0.1 |
| 5 000 | ~0.80 | 0.1 → 0.05 (seuil) |
| 10 000 | ~0.73 | 0.05 |
| 50 000 | ~0.63 | 0.05 → 0.02 (seuil) |
| 100 000 | ~0.59 | 0.02 |
Pour garantir qu'un bracket
En pratique, on prend STEP
STEP adaptatif implémenté dans compute_zeros_v4_1.py (step_pour_t) — v2 (commit 181fdd1) :
| Tranche |
STEP | Justification |
|---|---|---|
| 0.05 |
|
|
| 0.010 | gap min mesuré 0.01940 à |
Historique : v1 (commit
7467731) : 0.1/0.05/0.02 → 30 manquants sur T=100k. v2 (commit181fdd1) : cap à 0.010 pour$t \geq 5000$ (STEP ÷5 et ÷2 respectivement).
Condition minimale pour qu'aucun bracket ne soit raté à la frontière :
| Version | Overlap | Suffisant ? |
|---|---|---|
| v1 (2026-06-10) | proportionnel (×4 STEP) ≈ 0.4 | ❌ trop petit si STEP change en milieu de segment |
| v2 (2026-06-10) | fixe 2.0 | ✅ couvre ≥ 40 × STEP dans la tranche la plus dense |
Test T=10 000 v1 (STEP=0.1 pour tout
| Indicateur | Valeur |
|---|---|
| Zéros trouvés | 10 137 / 10 142 |
| Manquants | 6 (Turing-Backlund INCOMPLET) |
| Durée | 2.58 min · 65.50 z/s |
Test T=10 000 v2 (STEP=0.05 pour
| Indicateur | Valeur |
|---|---|
| Zéros trouvés | 10 141 / 10 142 |
| Manquants | 0 — Turing-Backlund COMPLET ✅ |
| Durée | 2.60 min · 64.97 z/s |
| Commit |
50837f7 — Riemann_Lab_C
|
Le surcoût du STEP plus fin est quasi nul (+0.02 min) car les brackets supplémentaires sont traités par Z_batch vectorisé (numpy, 0 appel Python par point).
Run T=100 000 v1 adaptatif (STEP=0.1/0.05/0.02, overlap=2.0, commit 7467731) :
| Indicateur | Valeur |
|---|---|
| Zéros trouvés | — |
| Manquants | ~30 (t > 50 000) |
| Cause | STEP=0.02 < gap min mesuré 0.01940 à t=66678 |
Run T=100 000 v2 adaptatif (STEP=0.1/0.05/0.02, overlap=2.0, commit 50837f7) :
| Indicateur | Valeur |
|---|---|
| Zéros trouvés | 138 039 / 138 069 |
| Manquants | 68 — Turing-Backlund INCOMPLET ❌ |
| Durée | 105.1 min · 21.89 z/s |
| Cause | STEP=0.02 toujours insuffisant pour |
Run T=100 000 v3 (STEP=0.05 pour 181fdd1) :
| Indicateur | Valeur |
|---|---|
| Zéros trouvés | TUÉ — régression ×11 (5M points scan, ~0.5 z/s) |
| Turing-Backlund | non atteint — run interrompu |
Run T=100 000 v4 (STEP=δ(t)/3, continu, commit d2f62c1) :
| Indicateur | Valeur |
|---|---|
| Zéros trouvés | 135 997 / 138 069 |
| Manquants | 2 072 — Turing-Backlund INCOMPLET ❌ |
| Durée | 113 min · 20.05 z/s |
| LMFDB | 19/20 ✅ |
| Cause | STEP≈0.22 à |
Leçon v4 : STEP=δ/3 est pire que v1 (2072 vs 356 manquants). La formule δ/3 est basée
sur l'espacement moyen ; la queue GUE produit des gaps scan_arb.c (×7.5 C pur).
| Run | STEP | Zéros | Manquants | Durée | Turing |
|---|---|---|---|---|---|
| v1 (paliers) | 0.1/0.05/0.02 | 137 711 | 356 | 1h58 | ❌ |
| v2 (paliers) | 0.1/0.05/0.02 | 138 001 | 68 | 105 min | ❌ |
| v3 (cap 0.010) | 0.010 pour |
TUÉ | — | — | — |
| v4 (δ/3) | ~0.22 à T=100k | 135 997 | 2 072 ❌ | 113 min | ❌ |
Prochaine étape (v6) : scan_arb.c (×7.5) + STEP≤0.010 + W=8 → cible ~15 min, 0 manquant.
| Config (prec_fast, prec_full) | Limbes ph1+ph2 | ms/appel | Gain vs ref |
|---|---|---|---|
| (64, 116) | 1+2 | 0.443 ms | ×1.00 — référence v7 |
| (48, 116) | 1+2 | 0.559 ms | ×0.79 ❌ |
| (32, 116) | 1+2 | 0.875 ms | ×0.51 ❌ |
| (64, 96) | 1+2 | 0.476 ms | ×0.93 ❌ |
| (64, 80) | 1+2 | 0.416 ms | ×1.06 ✅ optimal |
W=4 : 2 505.8 z/s · W=8 : 2 474.0 z/s (×0.99 — context-switching, contre-productif)
prec_full = 80 bits est optimal sur i7-7500U (×1.06 vs 116 bits). Gain marginal.
W=8 est contre-productif sur dual-core HT (4 threads logiques, nproc=4).
Durée T=100k plancher :
Contre-intuitif : prec=32 (1 limbe) est plus lent que prec=64 (1 limbe aussi).
Raison : mpfr alloue toujours un minimum de 64 bits par limbe (granularité de l'architecture 64 bits).
prec=32 déclenche des conversions d'arrondi supplémentaires sans réduire la taille mémoire.
| Levier | Gain estimé | Condition |
|---|---|---|
| CPU 8 cœurs physiques (i9, Ryzen 9) | ×2 (W=8 réel) | ~15 min T=100k |
Arb acb_dirichlet_hardy_z pour affinage |
à benchmarker | — |
| GPU CUDA détection Z_double | ~×100 détection | mais détection = 0.2% du temps |
Auteur : hprzeta — Riemann_Lab — Mise à jour : 3 juin 2026 (§19–21) · 6 juin 2026 (§22) · 9 juin 2026 (§25) · 10 juin 2026 (§23 STEP adaptatif) · 11 juin 2026 (§24 benchmark v8 plancher i7) · ~1385 lignes
- Interpolation inverse quadratique (si 3 points distincts)
- Méthode de la sécante (si 2 points)
- Bisection (fallback garanti — convergence assurée)
| Méthode | Ordre φ | Évals Z/iter | Iter typique | Verdict |
|---|---|---|---|---|
| Illinois | ~1.44 | 1 | ~6 | v7/v8 |
| Brent | ~1.84 | 1 | ~4 | ✅ v9 |
| Newton | 2.0 | 2 | ~3 | neutre |
Nombre d'itérations (tol=1e-11, δ₀=0.01) :
double brent_refine_adaptive(
double a, double b,
double fa, double fb,
int prec_fast, // = 64 bits (phase 1, 1 limb SIMD AVX2)
int prec_full, // = 80 bits (phase 2, validation finale)
double tol, // = 1e-11
int max_iter // = 50
);- T=100 000 : 138 069 / 138 069 zéros (0 manquant)
- Sans turbo : 28.0 min · 82.15 z/s · ×1.80 vs v8
- Avec turbo : 26.6 min · 86.5 z/s · gain turbo ×1.05 (bottleneck MPFR/mémoire)
- Turing-Backlund : COMPLET ✅ · LMFDB : 19/20 ✅
- Brent C utilisé : 99.9% · fallback mpmath : 0.1% (t < 300)
- Gain cumulé v1→v9 turbo : ×4 500
v12 introduit un algorithme d'affinage à 2 phases combinant la vitesse de Z_rs_double (double natif) avec la précision de Z_arb (Arb/FLINT) :
| Phase | Fonction | Ordre | Coût | Cible |
|---|---|---|---|---|
| Phase 1 | Illinois Z_rs_double (C0+C1) |
~1.44 | ~0.015 ms | |
| Phase 2 | Newton Z_arb × 2 |
2.0 (quadratique) | ~3.5 ms |
Modificateur Illinois (accélère la convergence superlinéaire) :
Nombre d'itérations Phase 1 estimé (bracket
Coût Phase 1 total :
Depuis le bracket Phase 1 :
Convergence quadratique depuis
2 Newton steps suffisent largement pour la tolérance
t < 200.0 → mpmath_petit_t (Z_arb trop imprécis à très petit t)
signe_incohérent → Illinois Z_arb classique (convergence garantie)
Le fallback concerne ~0.06 % des zéros sur T=100 000 (
avec
Coût moyen observé T=100k :
| Version | Backend | Coût/zéro | Précision | Turing T=100k |
|---|---|---|---|---|
| v7/v8 | Illinois MPFR prec=64/116 bits | ~10–130 ms | 1e-11 | ✅ |
| v9 | Brent MPFR prec=64/80 bits | ~64 ms | 1e-11 | ✅ |
| v10 | Brent MPFR prec=64/80 bits + W=8 | ~64 ms | 1e-11 | ✅ |
| v12 | Illinois hybride Z_rs_double + Newton Z_arb | ~4.7 ms | 1e-12 | ✅ |
Gain v10 → v12 :
- ~138 080 zéros · 0 manquant · 8.8 min · Turing COMPLET ✅ · LMFDB 20/20 ✅
- Gain vs v10 : ×2.69 direct (T=100k) · ×16.9 (benchmark T=10k vs Z_arb pur)
- Phase 1 utilisée : 99.94 % · Fallback mpmath : 0.06 % (t < 200)
Méthode validée mathématiquement (Claude.ai web, 14 juin 2026). Applicable à v13+ pour distribuer le scan sur plusieurs machines sans re-scanner depuis
$T = 0$ .
La formule de Riemann-von Mangoldt :
avec l'approximation de Stirling (valide pour
permet à un worker de démarrer son scan à l'indice
Conséquence pratique : pour scanner
La condition de pas adaptatif :
est locale (dépend uniquement de
Pour éviter de manquer un zéro tombant sur une frontière, chaque worker scanne avec un
recouvrement de
Fusion des résultats : dédupliquer les doublons de la zone de recouvrement par :
Si chaque worker valide «
Vérification d'intégrité : comparer
|
|
|
|
|---|---|---|
| 10 000 | 10 142 | ≈ 10 140 |
| 100 000 | 138 069 | ≈ 138 069 |
| 200 000 | 303 372 | ≈ 303 300 |
| 1 000 000 | 1 747 146 | ≈ 1 746 900 |
L'approximation Stirling peut être à
La formule Riemann-Siegel tronquée
Constante
|
|
|
|---|---|
| 65 | |
| 200 | |
| 1 000 | |
| 10 000 | |
| 20 000 | |
| 50 000 |
La Phase 1 Illinois converge vers le pseudo-zéro
Pour
Après la Phase 1, on dispose du pseudo-zéro
La condition
Par marge de sécurité (×1.25 sur le seuil théorique), v15 utilise :
À
int n_newton = (t_curr < SEUIL_1NEWTON) ? 2 : 1;-
$t < 20 000$ (12,8 % des zéros à$T=100 000$ ) : 2 Newton Z_arb -
$t \geq 20 000$ (87,2 %) : 1 Newton Z_arb
Piège confirmé le 04/07/2026 : forcer 1 Newton pour tout
Le seuil SEUIL_1NEWTON et la logique du §30 restent inchangés en v16 —
seule l'évaluation de arb_fpwrap_cdouble_hardy_z (qui escalade en interne 64→8192 bits jusqu'à certifier
~1e-16), Phase 2 appelle désormais acb_dirichlet_hardy_z à précision fixe 64 bits,
un seul calcul. Le biais
En dérivant
Après normalisation
C'est la décroissance cubique (pas linéaire, comme sous Poisson) qui rend un balayage par grille de pas constant viable en pratique.
À hauteur
Validée par lecture de code (_step_adaptatif, Riemann_Lab_C) :
L'approximation grossière
Suite de §32 : la résolution de grille (GUE) ayant été quantifiée et écartée, cette section établit la cause réelle, confirmée par test A/B expérimental (JOURNAL, entrée du 06/09/2026) et triple revue mathématique indépendante.
Un zéro simple
avec, d'après Gonek-Hughes (moments de
Numériquement, le seuil de détection sur grille vaut
Appliqué à
| Énoncé initial | Statut | Forme correcte |
|---|---|---|
| Erreur ULP accumulée |
❌ imprécis | Les termes de la somme RS décroissent en |
|
|
Seuls les maxima locaux croissent en |
|
|
|
notation | Forme standard : |
Écartés : bruit ULP de précision binaire (ratio seuil/bruit
Non-menace pour l'HR : ce sont des zéros non détectés par une formule
tronquée, pas des contre-exemples — ils sont bien sur la droite critique.
Objectif 1 reste validé. Test discriminant proposé (instrumentation
scan_arb.c,
Auteur : hprzeta — Riemann_Lab — Mise à jour : 3 juin 2026 (§19–21) · 6 juin 2026 (§22) · 9 juin 2026 (§25) · 10 juin 2026 (§23 STEP adaptatif) · 11 juin 2026 (§24 benchmark v8 plancher i7) · 12 juin 2026 (§27 Brent C/mpfr v9) · 13 juin 2026 (§28 Illinois hybride 2-phases v12) · 15 juin 2026 (§29 découpage fenêtres T) · 4 juillet 2026 (§30 biais Z_rs + SEUIL_1NEWTON v15) · 8 août 2026 (§31 Z_arb précision fixe v16) · 18 août 2026 (§32 résolution de grille et comptage — loi GUE, formule maîtresse M, variance de Selberg) · 6 septembre 2026 (§33 — cause du déficit établie : fragilité RS à bas t, ULP et GUE réfutés par test A/B) · 12 septembre 2026 (correction rendu KaTeX : _ et # — GitHub retirait le backslash en mode math) · 1 779 lignes