ml-intern
swayam1111 commited on
Commit
cb07656
·
verified ·
1 Parent(s): 75aa635

Upload problem_solvers/ramanujan_machine.py

Browse files
Files changed (1) hide show
  1. problem_solvers/ramanujan_machine.py +241 -0
problem_solvers/ramanujan_machine.py ADDED
@@ -0,0 +1,241 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ '''
2
+ Ramanujan Machine-style Continued Fraction Search
3
+ ====================================================
4
+ Inspired by arXiv:2308.11829 (Elimelech et al.)
5
+ Key insight: polynomial continued fractions converging to mathematical constants
6
+ exhibit "factorial reduction" — gcd(p_n, q_n) grows super-exponentially.
7
+ This property acts as a FILTER that dramatically reduces the search space.
8
+
9
+ We adapt this to search for continued fractions converging to zeta-related values:
10
+ - ζ(2) = π²/6
11
+ - ζ(3) (Apéry's constant)
12
+ - Values at zero ordinates: ζ(1/2 + iγ_n)
13
+ - Li's criterion values λ_n
14
+
15
+ The "conservative matrix field" structure from the paper suggests that formulas
16
+ for related constants share matrix structures. We search for polynomial continued
17
+ fractions and test if they converge to known zeta values.
18
+ '''
19
+
20
+ import numpy as np
21
+ from typing import Dict, List, Tuple
22
+ from dataclasses import dataclass
23
+
24
+
25
+ @dataclass
26
+ class ContinuedFractionFormula:
27
+ '''A discovered continued fraction formula.'''
28
+ target_constant: str
29
+ a_coeffs: List[int] # partial denominators (polynomial in n)
30
+ b_coeffs: List[int] # partial numerators (polynomial in n)
31
+ convergence_value: float
32
+ error: float
33
+ convergence_rate: float
34
+ factorial_reduction_score: float
35
+ depth: int
36
+
37
+
38
+ class RamanujanMachineSearcher:
39
+ '''
40
+ Search for polynomial continued fractions converging to zeta values.
41
+
42
+ A polynomial continued fraction has the form:
43
+ a_0 + b_1/(a_1 + b_2/(a_2 + ...))
44
+ where a_n = polynomial in n, b_n = polynomial in n.
45
+
46
+ The "factorial reduction" heuristic: for formulas converging to constants,
47
+ gcd(p_n, q_n) is unusually large, causing the reduced convergents to grow
48
+ much slower than (n!)^d.
49
+ '''
50
+
51
+ def __init__(self, max_degree: int = 2, max_coeff: int = 5, max_depth: int = 20):
52
+ self.max_degree = max_degree
53
+ self.max_coeff = max_coeff
54
+ self.max_depth = max_depth
55
+ self.results = []
56
+
57
+ def _eval_polynomial(self, coeffs: List[int], n: int) -> int:
58
+ '''Evaluate polynomial Σ coeffs[k] * n^k.'''
59
+ return sum(c * (n ** k) for k, c in enumerate(coeffs))
60
+
61
+ def _compute_convergents(self, a_coeffs: List[int], b_coeffs: List[int],
62
+ depth: int) -> Tuple[List[int], List[int], List[int]]:
63
+ '''
64
+ Compute convergents p_n/q_n for polynomial continued fraction.
65
+ Returns (p_list, q_list, gcd_list).
66
+ '''
67
+ p = [0] * (depth + 2)
68
+ q = [0] * (depth + 2)
69
+ g = [0] * (depth + 2)
70
+
71
+ # Initial conditions
72
+ p[-1] = 1 # p_{-1} = 1
73
+ p[0] = self._eval_polynomial(a_coeffs, 0) # p_0 = a_0
74
+ q[-1] = 0 # q_{-1} = 0
75
+ q[0] = 1 # q_0 = 1
76
+
77
+ for n in range(1, depth + 1):
78
+ a_n = self._eval_polynomial(a_coeffs, n)
79
+ b_n = self._eval_polynomial(b_coeffs, n)
80
+ p[n] = a_n * p[n-1] + b_n * p[n-2]
81
+ q[n] = a_n * q[n-1] + b_n * q[n-2]
82
+ g[n] = self._gcd(abs(p[n]), abs(q[n]))
83
+
84
+ return p[1:depth+1], q[1:depth+1], g[1:depth+1]
85
+
86
+ def _gcd(self, a: int, b: int) -> int:
87
+ while b:
88
+ a, b = b, a % b
89
+ return a
90
+
91
+ def _factorial_reduction_score(self, p: List[int], q: List[int],
92
+ g: List[int], depth: int) -> float:
93
+ '''
94
+ Measure how much gcd reduces the growth of convergents.
95
+ For "good" formulas, reduced p_n/g_n and q_n/g_n grow like s^n
96
+ (exponential), not like (n!)^d (super-exponential).
97
+
98
+ Score = average log-growth-rate of reduced convergents.
99
+ Lower = stronger factorial reduction.
100
+ '''
101
+ if depth < 5:
102
+ return float('inf')
103
+
104
+ growth_rates = []
105
+ for n in range(3, depth):
106
+ if g[n] > 1 and p[n] != 0 and q[n] != 0:
107
+ reduced_p = abs(p[n]) // g[n]
108
+ reduced_q = abs(q[n]) // g[n]
109
+ if reduced_p > 0 and reduced_q > 0:
110
+ rate = (np.log(float(reduced_p)) + np.log(float(reduced_q))) / (2 * n)
111
+ growth_rates.append(rate)
112
+
113
+ if len(growth_rates) < 3:
114
+ return float('inf')
115
+
116
+ return float(np.mean(growth_rates))
117
+
118
+ def _test_convergence(self, a_coeffs: List[int], b_coeffs: List[int],
119
+ targets: Dict[str, float], depth: int = 20) -> List[ContinuedFractionFormula]:
120
+ '''Test if the continued fraction converges to any target constant.'''
121
+ p, q, g = self._compute_convergents(a_coeffs, b_coeffs, depth)
122
+
123
+ if q[-1] == 0 or p[-1] == 0:
124
+ return []
125
+
126
+ frac_value = p[-1] / q[-1]
127
+ frac_score = self._factorial_reduction_score(p, q, g, depth)
128
+
129
+ # Compute convergence rate: how fast do convergents stabilize?
130
+ if depth >= 10:
131
+ late_values = [p[n] / q[n] for n in range(depth-5, depth) if q[n] != 0]
132
+ if len(late_values) >= 3:
133
+ convergence_rate = float(np.std(late_values))
134
+ else:
135
+ convergence_rate = 1.0
136
+ else:
137
+ convergence_rate = 1.0
138
+
139
+ found = []
140
+ for name, target in targets.items():
141
+ if target == 0:
142
+ continue
143
+ error = abs(frac_value - target) / abs(target)
144
+ if error < 0.01: # Within 1%
145
+ found.append(ContinuedFractionFormula(
146
+ target_constant=name,
147
+ a_coeffs=a_coeffs,
148
+ b_coeffs=b_coeffs,
149
+ convergence_value=frac_value,
150
+ error=error,
151
+ convergence_rate=convergence_rate,
152
+ factorial_reduction_score=frac_score,
153
+ depth=depth
154
+ ))
155
+
156
+ return found
157
+
158
+ def search(self, targets: Dict[str, float] = None, n_candidates: int = 5000) -> Dict:
159
+ '''
160
+ Search for polynomial continued fractions converging to targets.
161
+
162
+ Default targets include zeta values and related constants.
163
+ '''
164
+ if targets is None:
165
+ targets = {
166
+ 'zeta_2': np.pi**2 / 6,
167
+ 'zeta_3': 1.202056903159594,
168
+ 'zeta_4': np.pi**4 / 90,
169
+ 'catalan': 0.915965594177219,
170
+ 'euler_mascheroni': 0.5772156649015329,
171
+ 'golden_ratio': (1 + np.sqrt(5)) / 2,
172
+ }
173
+
174
+ print(f" [RamanujanMachine] Searching {n_candidates} candidates...")
175
+ found_formulas = []
176
+ best_scores = []
177
+
178
+ # Generate candidates: simple polynomial forms
179
+ # a_n = a0 + a1*n + a2*n^2, b_n = b0 + b1*n + b2*n^2
180
+ count = 0
181
+ for a0 in range(-self.max_coeff, self.max_coeff + 1):
182
+ for a1 in range(-self.max_coeff, self.max_coeff + 1):
183
+ for b0 in range(1, self.max_coeff + 1): # b0 > 0 for convergence
184
+ for b1 in range(-self.max_coeff, self.max_coeff + 1):
185
+ if count >= n_candidates:
186
+ break
187
+ a_coeffs = [a0, a1]
188
+ b_coeffs = [b0, b1]
189
+
190
+ formulas = self._test_convergence(a_coeffs, b_coeffs, targets)
191
+ if formulas:
192
+ found_formulas.extend(formulas)
193
+
194
+ # Also compute score for all candidates
195
+ p, q, g = self._compute_convergents(a_coeffs, b_coeffs, 15)
196
+ score = self._factorial_reduction_score(p, q, g, 15)
197
+ if score < 5.0: # Interesting reduction
198
+ best_scores.append((score, a_coeffs, b_coeffs))
199
+
200
+ count += 1
201
+ if count >= n_candidates:
202
+ break
203
+ if count >= n_candidates:
204
+ break
205
+ if count >= n_candidates:
206
+ break
207
+
208
+ # Sort by error
209
+ found_formulas.sort(key=lambda f: f.error)
210
+
211
+ self.results = {
212
+ 'strategy': 'ramanujan_machine_continued_fractions',
213
+ 'n_candidates': count,
214
+ 'n_found': len(found_formulas),
215
+ 'found_formulas': [
216
+ {
217
+ 'target': f.target_constant,
218
+ 'a_coeffs': f.a_coeffs,
219
+ 'b_coeffs': f.b_coeffs,
220
+ 'value': f.convergence_value,
221
+ 'error': f.error,
222
+ 'rate': f.convergence_rate,
223
+ 'frac_score': f.factorial_reduction_score,
224
+ }
225
+ for f in found_formulas[:10]
226
+ ],
227
+ 'best_factorial_reduction': sorted(best_scores, key=lambda x: x[0])[:5],
228
+ }
229
+ return self.results
230
+
231
+ def summary(self) -> str:
232
+ r = self.results
233
+ s = f"Ramanujan Machine Search\n{'='*50}\n"
234
+ s += f"Candidates: {r['n_candidates']:,}\n"
235
+ s += f"Formulas found: {r['n_found']}\n"
236
+ for f in r['found_formulas'][:5]:
237
+ s += f" → {f['target']}: a={f['a_coeffs']}, b={f['b_coeffs']}, "
238
+ s += f"error={f['error']:.6f}, value={f['value']:.8f}\n"
239
+ if r['best_factorial_reduction']:
240
+ s += f"Best factorial reduction score: {r['best_factorial_reduction'][0][0]:.4f}\n"
241
+ return s