ml-intern
File size: 8,914 Bytes
1c60ea2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
"""
Self-Play Conjecture Generator for Zeta Zeros
===============================================
Inspired by STP (Self-play Theorem Prover, arXiv:2502.00212)
and Bourbaki (arXiv:2507.02726)

Key idea: A dual-role system where:
- CONJECTURER generates hypotheses about zero properties
- PROVER tests them numerically with 100k zeros
- Feedback loop: verified conjectures strengthen the conjecturer;
  falsified ones teach it what NOT to propose

This creates its own curriculum of "barely verifiable" conjectures.
"""

import numpy as np
from typing import Dict, List, Tuple, Callable
import random
from scipy import stats as sp_stats


class Conjecture:
    def __init__(self, statement: str, test_func: Callable, category: str):
        self.statement = statement
        self.test_func = test_func
        self.category = category
        self.verification_result = None
        self.confidence = 0.0


class SelfPlayConjectureEngine:
    """
    Generates, tests, and refines conjectures about zeta zeros.
    """

    def __init__(self, zeros: List[float]):
        self.zeros = np.array(zeros)
        self.spacings = np.diff(self.zeros)
        self.normalized = self.spacings / np.mean(self.spacings)
        self.results = {}
        self.verified_conjectures = []
        self.falsified_conjectures = []

    def _generate_conjectures(self, n_conjectures: int = 20) -> List[Conjecture]:
        """Generate candidate conjectures based on observed patterns."""
        conjectures = []

        # Conjecture 1: Spacing bounds
        max_s = np.max(self.normalized)
        conjectures.append(Conjecture(
            f"All normalized spacings s_n satisfy s_n > 0.01 (observed min={np.min(self.normalized):.5f})",
            lambda: np.all(self.normalized > 0.01),
            "spacing_bounds"
        ))

        # Conjecture 2: Mean spacing trend
        windows = [100, 1000, 10000, 50000, 100000]
        means = [np.mean(self.normalized[:w]) for w in windows if w <= len(self.normalized)]
        conjectures.append(Conjecture(
            f"Mean normalized spacing converges to 1.0 (current: {means[-1]:.6f})",
            lambda: abs(means[-1] - 1.0) < 0.01,
            "mean_convergence"
        ))

        # Conjecture 3: Variance bound (GUE prediction: ~0.178)
        var = np.var(self.normalized)
        conjectures.append(Conjecture(
            f"Variance of normalized spacings < 0.2 (GUE prediction: ~0.178, observed: {var:.6f})",
            lambda: var < 0.2,
            "variance"
        ))

        # Conjecture 4: Arithmetic progression avoidance
        def test_ap():
            n_test = min(5000, len(self.zeros))
            ap_count = 0
            for i in range(n_test - 2):
                d1 = self.zeros[i+1] - self.zeros[i]
                d2 = self.zeros[i+2] - self.zeros[i+1]
                if abs(d1 - d2) < 0.001:
                    ap_count += 1
            return ap_count < 10
        conjectures.append(Conjecture(
            "No arithmetic progressions among zeros (< 10 near-AP in first 5000)",
            test_ap,
            "arithmetic_progressions"
        ))

        # Conjecture 5: Repulsion (GUE level repulsion)
        small_count = np.sum(self.normalized < 0.1)
        conjectures.append(Conjecture(
            f"Level repulsion: fewer than 5% of spacings < 0.1 (observed: {small_count/len(self.normalized):.4%})",
            lambda: small_count / len(self.normalized) < 0.05,
            "level_repulsion"
        ))

        # Conjecture 6: Skewness (GUE: ~0.09)
        skew = sp_stats.skew(self.normalized)
        conjectures.append(Conjecture(
            f"Skewness of spacings matches GUE (~0.09, observed: {skew:.4f})",
            lambda: abs(skew - 0.09) < 0.05,
            "skewness"
        ))

        # Conjecture 7: Ratio of consecutive spacings
        ratios = self.normalized[1:] / self.normalized[:-1]
        conjectures.append(Conjecture(
            f"Max consecutive spacing ratio < 10 (observed: {np.max(ratios):.2f})",
            lambda: np.max(ratios) < 10,
            "ratio_bound"
        ))

        # Conjecture 8: Long-range correlation decay
        def test_correlation_decay():
            max_lag = 100
            autocorrs = []
            for lag in range(1, max_lag + 1):
                if lag < len(self.normalized):
                    c = np.corrcoef(self.normalized[:-lag], self.normalized[lag:])[0, 1]
                    autocorrs.append(c)
            return all(abs(c) < 0.1 for c in autocorrs)
        conjectures.append(Conjecture(
            "Long-range correlations decay to < 0.1 for all lags > 0",
            test_correlation_decay,
            "correlation"
        ))

        # Conjecture 9: Sum of spacings exactness
        total = np.sum(self.spacings)
        predicted = self.zeros[-1] - self.zeros[0]
        conjectures.append(Conjecture(
            f"Sum of spacings equals γ_N - γ_1 exactly (error: {abs(total - predicted):.10f})",
            lambda: abs(total - predicted) < 1e-6,
            "telescoping"
        ))

        # Conjecture 10: Even-odd symmetry in spacing distribution
        n_half = len(self.normalized) // 2
        first_half = self.normalized[:n_half]
        second_half = self.normalized[n_half:]
        conjectures.append(Conjecture(
            "First-half and second-half spacing distributions are statistically similar (KS test)",
            lambda: sp_stats.ks_2samp(first_half, second_half)[1] > 0.01,
            "symmetry"
        ))

        return conjectures[:n_conjectures]

    def run_self_play(self, n_rounds: int = 3) -> Dict:
        """
        Run self-play: generate conjectures, test, learn from results.
        """
        print(f"  [SelfPlay] Running {n_rounds} rounds of conjecture generation...")

        all_verified = []
        all_falsified = []
        round_results = []

        for round_idx in range(n_rounds):
            conjectures = self._generate_conjectures()

            verified = 0
            falsified = 0
            results = []

            for c in conjectures:
                try:
                    result = c.test_func()
                    c.verification_result = result
                    c.confidence = 1.0 if result else 0.0

                    if result:
                        verified += 1
                        all_verified.append(c)
                    else:
                        falsified += 1
                        all_falsified.append(c)

                    results.append({
                        'statement': c.statement,
                        'verified': result,
                        'category': c.category,
                    })
                except Exception as e:
                    results.append({
                        'statement': c.statement,
                        'verified': False,
                        'error': str(e),
                        'category': c.category,
                    })

            round_results.append({
                'round': round_idx + 1,
                'verified': verified,
                'falsified': falsified,
                'results': results,
            })

            print(f"    Round {round_idx + 1}: {verified} verified, {falsified} falsified")

        # Compute statistics
        categories = {}
        for c in all_verified + all_falsified:
            cat = c.category
            if cat not in categories:
                categories[cat] = {'verified': 0, 'falsified': 0}
            if c.verification_result:
                categories[cat]['verified'] += 1
            else:
                categories[cat]['falsified'] += 1

        self.results = {
            'strategy': 'self_play_conjecture_generator',
            'n_rounds': n_rounds,
            'total_verified': len(all_verified),
            'total_falsified': len(all_falsified),
            'verification_rate': len(all_verified) / max(len(all_verified) + len(all_falsified), 1),
            'round_results': round_results,
            'category_breakdown': categories,
            'verified_conjectures': [c.statement for c in all_verified],
            'falsified_conjectures': [c.statement for c in all_falsified],
        }
        return self.results

    def summary(self) -> str:
        r = self.results
        s = f"Self-Play Conjecture Engine\n{'='*50}\n"
        s += f"Rounds: {r['n_rounds']}\n"
        s += f"Total verified: {r['total_verified']}, falsified: {r['total_falsified']}\n"
        s += f"Verification rate: {r['verification_rate']:.1%}\n"
        s += "\nVerified conjectures:\n"
        for c in r['verified_conjectures'][:5]:
            s += f"  ✓ {c[:80]}\n"
        if r['falsified_conjectures']:
            s += "\nFalsified conjectures (learn from these):\n"
            for c in r['falsified_conjectures'][:3]:
                s += f"  ✗ {c[:80]}\n"
        return s