cahlen commited on
Commit
96cb0ff
·
verified ·
1 Parent(s): cf97e79

CUDA kernel: ramanujan-machine-cuda

Browse files
README.md ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: mit
3
+ tags:
4
+ - kernels
5
+ - cuda
6
+ - ramanujan-machine
7
+ - continued-fractions
8
+ - mathematical-constants
9
+ - number-theory
10
+ datasets:
11
+ - cahlen/ramanujan-machine-results
12
+ ---
13
+
14
+ # Ramanujan Machine v2 (Asymmetric-Degree CF Search)
15
+
16
+ Searches for polynomial continued fraction formulas with asymmetric degrees. Evaluates CF candidates and matches against known mathematical constants.
17
+
18
+ ## Usage
19
+
20
+ ```python
21
+ import torch
22
+ from kernels import get_kernel
23
+
24
+ kernel = get_kernel("cahlen/ramanujan-machine-cuda")
25
+ result = ramanujan.search(deg_a=1, deg_b=2, range_a=10, range_b=10)
26
+ ```
27
+
28
+ ## Compile (standalone)
29
+
30
+ ```bash
31
+ nvcc -O3 -arch=sm_90 -o ramanujan_machine ramanujan/ramanujan_v2.cu -lm
32
+ ```
33
+
34
+ ## Results
35
+
36
+ All computation results are open:
37
+ - **Website**: [bigcompute.science](https://bigcompute.science)
38
+ - **Datasets**: [huggingface.co/cahlen](https://huggingface.co/cahlen)
39
+ - **Source**: [github.com/cahlen/idontknow](https://github.com/cahlen/idontknow)
40
+
41
+ ## Citation
42
+
43
+ ```bibtex
44
+ @misc{humphreys2026bigcompute,
45
+ author = {Humphreys, Cahlen},
46
+ title = {bigcompute.science: GPU-Accelerated Computational Mathematics},
47
+ year = {2026},
48
+ url = {https://bigcompute.science}
49
+ }
50
+ ```
51
+
52
+ *Human-AI collaborative. Not peer-reviewed. All code and data open.*
build.toml ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [general]
2
+ name = "ramanujan_machine"
3
+ universal = false
4
+
5
+ [torch]
6
+ src = ["torch-ext/torch_binding.cpp", "torch-ext/torch_binding.h"]
7
+
8
+ [kernel.ramanujan_machine]
9
+ backend = "cuda"
10
+ cuda-capabilities = ["8.0", "9.0", "10.0", "12.0"]
11
+ src = ["ramanujan/ramanujan_v2.cu"]
12
+ depends = ["torch"]
ramanujan/ramanujan_v2.cu ADDED
@@ -0,0 +1,536 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * Ramanujan Machine v2: ASYMMETRIC-DEGREE polynomial CF search
3
+ *
4
+ * KEY INSIGHT: Every known CF formula for transcendental constants has
5
+ * deg(b_n) ≈ 2 * deg(a_n). v1 forced equal degrees, which is why it
6
+ * only re-derived classical formulas and produced zero new transcendentals.
7
+ *
8
+ * CF = a(0) + b(1) / (a(1) + b(2) / (a(2) + b(3) / (a(3) + ...)))
9
+ * a(n) = polynomial of degree deg_a, coefficients in [-range_a, range_a]
10
+ * b(n) = polynomial of degree deg_b, coefficients in [-range_b, range_b]
11
+ *
12
+ * Productive search targets (deg_a, deg_b):
13
+ * (1, 2) — Brouncker/Wallis family (4/pi, etc.)
14
+ * (2, 4) — Catalan/zeta(2) family
15
+ * (3, 6) — Apéry family (zeta(3), zeta(5))
16
+ * (2, 3) — sub-ratio region, still productive
17
+ * (1, 3) — mixed regime
18
+ *
19
+ * Also outputs ALL converged CFs (not just matched ones) to enable
20
+ * offline multi-constant PSLQ scanning.
21
+ *
22
+ * Compile: nvcc -O3 -arch=sm_100a -o ramanujan_v2 ramanujan_v2.cu -lm
23
+ * Run: ./ramanujan_v2 <deg_a> <deg_b> <range_a> <range_b> [cf_depth] [gpu_id]
24
+ *
25
+ * Examples:
26
+ * ./ramanujan_v2 2 4 6 6 # Catalan-type, 1.7T candidates
27
+ * ./ramanujan_v2 1 2 10 10 # Brouncker-type, 194M candidates
28
+ * ./ramanujan_v2 3 6 3 3 # Apéry-type, 282B candidates
29
+ */
30
+
31
+ #include <stdio.h>
32
+ #include <stdlib.h>
33
+ #include <stdint.h>
34
+ #include <string.h>
35
+ #include <math.h>
36
+ #include <time.h>
37
+ #include <float.h>
38
+
39
+ #define BLOCK 256
40
+ #define MAX_DEG_A 6
41
+ #define MAX_DEG_B 12
42
+ #define MAX_CF_DEPTH 500
43
+
44
+ /* ── Known constants ──────────────────────────────────────── */
45
+
46
+ __constant__ double d_constants[] = {
47
+ 3.14159265358979323846, // 0 pi
48
+ 2.71828182845904523536, // 1 e
49
+ 0.69314718055994530942, // 2 ln(2)
50
+ 0.57721566490153286061, // 3 Euler-Mascheroni gamma
51
+ 0.91596559417721901505, // 4 Catalan's constant
52
+ 1.20205690315959428540, // 5 zeta(3)
53
+ 1.03692775514336992633, // 6 zeta(5)
54
+ 1.00834927738192282684, // 7 zeta(7)
55
+ 0.83462684167407318628, // 8 Gauss's constant
56
+ 2.62205755429211981046, // 9 Lemniscate constant
57
+ 1.41421356237309504880, // 10 sqrt(2)
58
+ 1.61803398874989484820, // 11 golden ratio phi
59
+ 0.0,
60
+ };
61
+
62
+ static const char* h_const_names[] = {
63
+ "pi", "e", "ln(2)", "gamma", "Catalan",
64
+ "zeta(3)", "zeta(5)", "zeta(7)", "Gauss", "Lemniscate",
65
+ "sqrt(2)", "phi"
66
+ };
67
+
68
+ #define NUM_CONSTANTS 12
69
+
70
+ __constant__ double d_compounds[] = {
71
+ // Reciprocals
72
+ 0.31830988618379067, // 1/pi
73
+ 0.36787944117144233, // 1/e
74
+ 1.44269504088896341, // 1/ln(2)
75
+ // Pi expressions
76
+ 1.27323954473516269, // 4/pi
77
+ 0.78539816339744831, // pi/4
78
+ 1.57079632679489662, // pi/2
79
+ 1.04719755119659775, // pi/3
80
+ 0.52359877559829887, // pi/6
81
+ 9.86960440108935862, // pi^2
82
+ 1.64493406684822644, // pi^2/6 = zeta(2)
83
+ 2.46740110027233966, // pi^2/4
84
+ 0.82246703342411322, // pi^2/12
85
+ // Log expressions
86
+ 1.38629436111989061, // 2*ln(2)
87
+ 2.30258509299404568, // ln(10)
88
+ 1.09861228866810970, // ln(3)
89
+ // Cross-products
90
+ 8.53973422267356706, // e*pi
91
+ 0.86525597943226508, // e/pi
92
+ 1.15572734979092172, // pi/e
93
+ 2.17758609030360229, // pi*ln(2)
94
+ // Roots
95
+ 1.77245385090551603, // sqrt(pi)
96
+ 0.56418958354775629, // 1/sqrt(pi)
97
+ 1.12837916709551258, // 2/sqrt(pi)
98
+ 2.50662827463100051, // sqrt(2*pi)
99
+ 0.39894228040143268, // 1/sqrt(2*pi)
100
+ // Zeta products
101
+ 3.77495308672748408, // pi*zeta(3)
102
+ 0.0,
103
+ };
104
+
105
+ static const char* h_compound_names[] = {
106
+ "1/pi", "1/e", "1/ln(2)",
107
+ "4/pi", "pi/4", "pi/2", "pi/3", "pi/6",
108
+ "pi^2", "pi^2/6", "pi^2/4", "pi^2/12",
109
+ "2*ln(2)", "ln(10)", "ln(3)",
110
+ "e*pi", "e/pi", "pi/e", "pi*ln(2)",
111
+ "sqrt(pi)", "1/sqrt(pi)", "2/sqrt(pi)",
112
+ "sqrt(2pi)", "1/sqrt(2pi)",
113
+ "pi*zeta(3)",
114
+ };
115
+
116
+ #define NUM_COMPOUNDS 25
117
+
118
+ static const char* get_const_name(int mc) {
119
+ if (mc >= 100) return h_compound_names[mc - 100];
120
+ return h_const_names[mc];
121
+ }
122
+
123
+ /* ── Polynomial evaluation ────────────────────────────────── */
124
+
125
+ __device__ double eval_poly_a(const int *coeffs, int deg_a, int n) {
126
+ double result = 0.0, np = 1.0;
127
+ for (int i = 0; i <= deg_a; i++) {
128
+ result += coeffs[i] * np;
129
+ np *= (double)n;
130
+ }
131
+ return result;
132
+ }
133
+
134
+ __device__ double eval_poly_b(const int *coeffs, int deg_b, int n) {
135
+ double result = 0.0, np = 1.0;
136
+ for (int i = 0; i <= deg_b; i++) {
137
+ result += coeffs[i] * np;
138
+ np *= (double)n;
139
+ }
140
+ return result;
141
+ }
142
+
143
+ /* ── CF evaluation with asymmetric degrees ────────────────── */
144
+
145
+ __device__ double eval_pcf_asym(const int *a_coeffs, int deg_a,
146
+ const int *b_coeffs, int deg_b,
147
+ int depth)
148
+ {
149
+ // Backward recurrence: start from n=depth
150
+ double val = eval_poly_a(a_coeffs, deg_a, depth);
151
+
152
+ for (int n = depth - 1; n >= 1; n--) {
153
+ double bn1 = eval_poly_b(b_coeffs, deg_b, n + 1);
154
+ double an = eval_poly_a(a_coeffs, deg_a, n);
155
+ if (fabs(val) < 1e-300) return NAN;
156
+ val = an + bn1 / val;
157
+ }
158
+
159
+ // CF = a(0) + b(1) / val
160
+ double a0 = eval_poly_a(a_coeffs, deg_a, 0);
161
+ double b1 = eval_poly_b(b_coeffs, deg_b, 1);
162
+ if (fabs(val) < 1e-300) return NAN;
163
+ return a0 + b1 / val;
164
+ }
165
+
166
+ __device__ int check_convergence_asym(const int *a_coeffs, int deg_a,
167
+ const int *b_coeffs, int deg_b,
168
+ int depth, double *result)
169
+ {
170
+ double v1 = eval_pcf_asym(a_coeffs, deg_a, b_coeffs, deg_b, depth);
171
+ double v2 = eval_pcf_asym(a_coeffs, deg_a, b_coeffs, deg_b, depth - 50);
172
+
173
+ if (isnan(v1) || isnan(v2) || isinf(v1) || isinf(v2)) return 0;
174
+ if (fabs(v1) > 1e15 || fabs(v1) < 1e-15) return 0;
175
+
176
+ double reldiff = fabs(v1 - v2) / (fabs(v1) + 1e-300);
177
+ if (reldiff > 1e-10) return 0;
178
+
179
+ *result = v1;
180
+ return 1;
181
+ }
182
+
183
+ /* ── Constant matching (same as v1 but with tighter threshold) ── */
184
+
185
+ __device__ int match_constant(double val, int *match_const, int *match_c0,
186
+ int *match_c1, int *match_c2)
187
+ {
188
+ double absval = val < 0.0 ? -val : val;
189
+ if (absval < 1e-8) return 0;
190
+
191
+ // Phase 1: compound expressions
192
+ for (int ci = 0; ci < NUM_COMPOUNDS; ci++) {
193
+ double K = d_compounds[ci];
194
+ if (K == 0.0) continue;
195
+ for (int c1 = 1; c1 <= 6; c1++) {
196
+ for (int c2 = -6; c2 <= 6; c2++) {
197
+ if (c2 == 0) continue;
198
+ for (int c0 = -6; c0 <= 6; c0++) {
199
+ double expected = ((double)c0 + (double)c2 * K) / (double)c1;
200
+ if (fabs(expected) < 1e-15 || fabs(expected) > 1e15) continue;
201
+ double reldiff = fabs(val - expected) / (fabs(expected) + 1e-300);
202
+ if (reldiff < 1e-11) {
203
+ *match_const = 100 + ci;
204
+ *match_c0 = c0; *match_c1 = c1; *match_c2 = c2;
205
+ return 1;
206
+ }
207
+ }
208
+ }
209
+ }
210
+ }
211
+
212
+ // Phase 2: base constants
213
+ for (int ci = 0; ci < NUM_CONSTANTS; ci++) {
214
+ double K = d_constants[ci];
215
+ if (K == 0.0) continue;
216
+ for (int c1 = 1; c1 <= 8; c1++) {
217
+ for (int c2 = -8; c2 <= 8; c2++) {
218
+ if (c2 == 0) continue;
219
+ for (int c0 = -8; c0 <= 8; c0++) {
220
+ double expected = ((double)c0 + (double)c2 * K) / (double)c1;
221
+ double reldiff = fabs(val - expected) / (fabs(expected) + 1e-300);
222
+ if (reldiff < 1e-12) {
223
+ *match_const = ci;
224
+ *match_c0 = c0; *match_c1 = c1; *match_c2 = c2;
225
+ return 1;
226
+ }
227
+ }
228
+ }
229
+ }
230
+ // Power matches
231
+ for (int p = -4; p <= 4; p++) {
232
+ for (int q = 1; q <= 4; q++) {
233
+ if (p == 0) continue;
234
+ double expected = pow(K, (double)p / (double)q);
235
+ if (isnan(expected) || isinf(expected)) continue;
236
+ double reldiff = fabs(val - expected) / (fabs(expected) + 1e-300);
237
+ if (reldiff < 1e-12) {
238
+ *match_const = ci;
239
+ *match_c0 = p; *match_c1 = q; *match_c2 = -999;
240
+ return 1;
241
+ }
242
+ }
243
+ }
244
+ }
245
+ return 0;
246
+ }
247
+
248
+ /* ── Main kernel ──────────────────────────────────────────── */
249
+
250
+ struct Hit {
251
+ int a_coeffs[MAX_DEG_A + 1];
252
+ int b_coeffs[MAX_DEG_B + 1];
253
+ int deg_a, deg_b;
254
+ double value;
255
+ int match_const;
256
+ int match_c0, match_c1, match_c2;
257
+ int matched; // 1 = matched a constant, 0 = converged but unmatched
258
+ };
259
+
260
+ __global__ void search_kernel(
261
+ long long start_idx, long long count,
262
+ int deg_a, int deg_b, int range_a, int range_b, int cf_depth,
263
+ Hit *hits, int *hit_count, int max_hits,
264
+ Hit *unmatched, int *unmatched_count, int max_unmatched)
265
+ {
266
+ long long tid = blockIdx.x * (long long)blockDim.x + threadIdx.x;
267
+ if (tid >= count) return;
268
+
269
+ long long idx = start_idx + tid;
270
+
271
+ // Decode: first (deg_a+1) coefficients for a, then (deg_b+1) for b
272
+ int width_a = 2 * range_a + 1;
273
+ int width_b = 2 * range_b + 1;
274
+
275
+ int a_coeffs[MAX_DEG_A + 1] = {0};
276
+ int b_coeffs[MAX_DEG_B + 1] = {0};
277
+
278
+ long long tmp = idx;
279
+ for (int i = 0; i <= deg_a; i++) {
280
+ a_coeffs[i] = (int)(tmp % width_a) - range_a;
281
+ tmp /= width_a;
282
+ }
283
+ for (int i = 0; i <= deg_b; i++) {
284
+ b_coeffs[i] = (int)(tmp % width_b) - range_b;
285
+ tmp /= width_b;
286
+ }
287
+
288
+ // Skip trivial: b(n) = 0
289
+ int all_zero_b = 1;
290
+ for (int i = 0; i <= deg_b; i++) if (b_coeffs[i] != 0) { all_zero_b = 0; break; }
291
+ if (all_zero_b) return;
292
+
293
+ // Skip trivial: leading coefficient of b is zero (reduces to lower degree)
294
+ if (b_coeffs[deg_b] == 0) return;
295
+
296
+ // Evaluate CF
297
+ double value;
298
+ if (!check_convergence_asym(a_coeffs, deg_a, b_coeffs, deg_b, cf_depth, &value))
299
+ return;
300
+
301
+ // Skip trivial values
302
+ if (value == 0.0 || value != value || value > 1e15 || value < -1e15) return;
303
+ if (value > -1e-10 && value < 1e-10) return;
304
+
305
+ // Try matching
306
+ int mc, c0, c1, c2;
307
+ if (match_constant(value, &mc, &c0, &c1, &c2)) {
308
+ int slot = atomicAdd(hit_count, 1);
309
+ if (slot < max_hits) {
310
+ Hit *h = &hits[slot];
311
+ for (int i = 0; i <= deg_a; i++) h->a_coeffs[i] = a_coeffs[i];
312
+ for (int i = 0; i <= deg_b; i++) h->b_coeffs[i] = b_coeffs[i];
313
+ h->deg_a = deg_a; h->deg_b = deg_b;
314
+ h->value = value;
315
+ h->match_const = mc;
316
+ h->match_c0 = c0; h->match_c1 = c1; h->match_c2 = c2;
317
+ h->matched = 1;
318
+ }
319
+ } else {
320
+ // Save unmatched converged CFs for offline PSLQ
321
+ int slot = atomicAdd(unmatched_count, 1);
322
+ if (slot < max_unmatched) {
323
+ Hit *h = &unmatched[slot];
324
+ for (int i = 0; i <= deg_a; i++) h->a_coeffs[i] = a_coeffs[i];
325
+ for (int i = 0; i <= deg_b; i++) h->b_coeffs[i] = b_coeffs[i];
326
+ h->deg_a = deg_a; h->deg_b = deg_b;
327
+ h->value = value;
328
+ h->matched = 0;
329
+ }
330
+ }
331
+ }
332
+
333
+ /* ── Main ──────────────────────────────────────────────────── */
334
+
335
+ int main(int argc, char **argv) {
336
+ if (argc < 5) {
337
+ printf("Usage: %s <deg_a> <deg_b> <range_a> <range_b> [cf_depth] [gpu_id]\n", argv[0]);
338
+ printf("\nProductive configurations:\n");
339
+ printf(" %s 1 2 10 10 # Brouncker-type (194M candidates)\n", argv[0]);
340
+ printf(" %s 2 4 6 6 # Catalan-type (1.7T candidates)\n", argv[0]);
341
+ printf(" %s 3 6 3 3 # Apéry-type (282B candidates)\n", argv[0]);
342
+ printf(" %s 2 3 8 8 # mixed (4.7T candidates)\n", argv[0]);
343
+ return 1;
344
+ }
345
+
346
+ int deg_a = atoi(argv[1]);
347
+ int deg_b = atoi(argv[2]);
348
+ int range_a = atoi(argv[3]);
349
+ int range_b = atoi(argv[4]);
350
+ int cf_depth = argc > 5 ? atoi(argv[5]) : 300;
351
+ int gpu_id = argc > 6 ? atoi(argv[6]) : 0;
352
+
353
+ if (deg_a > MAX_DEG_A) { printf("ERROR: deg_a > %d\n", MAX_DEG_A); return 1; }
354
+ if (deg_b > MAX_DEG_B) { printf("ERROR: deg_b > %d\n", MAX_DEG_B); return 1; }
355
+
356
+ cudaSetDevice(gpu_id);
357
+
358
+ int width_a = 2 * range_a + 1;
359
+ int width_b = 2 * range_b + 1;
360
+ long long total_candidates = 1;
361
+ for (int i = 0; i <= deg_a; i++) total_candidates *= width_a;
362
+ for (int i = 0; i <= deg_b; i++) total_candidates *= width_b;
363
+
364
+ double ratio = (double)deg_b / (double)(deg_a > 0 ? deg_a : 1);
365
+
366
+ printf("========================================\n");
367
+ printf("Ramanujan Machine v2 (asymmetric degree)\n");
368
+ printf("========================================\n");
369
+ printf("a(n) degree: %d, coefficients: [-%d, %d]\n", deg_a, range_a, range_a);
370
+ printf("b(n) degree: %d, coefficients: [-%d, %d]\n", deg_b, range_b, range_b);
371
+ printf("Degree ratio: %.2f %s\n", ratio,
372
+ ratio >= 1.8 && ratio <= 2.2 ? "(OPTIMAL for transcendentals)" :
373
+ ratio >= 1.3 && ratio <= 1.7 ? "(sub-optimal but productive)" :
374
+ "(outside typical productive range)");
375
+ printf("CF evaluation depth: %d terms\n", cf_depth);
376
+ printf("Total candidates: %lld (%.2e)\n", total_candidates, (double)total_candidates);
377
+ printf("GPU: %d\n", gpu_id);
378
+ printf("========================================\n\n");
379
+ fflush(stdout);
380
+
381
+ // Allocate buffers
382
+ int max_hits = 500000;
383
+ int max_unmatched = 1000000; // save converged-but-unmatched for PSLQ
384
+ Hit *d_hits, *d_unmatched;
385
+ int *d_hit_count, *d_unmatched_count;
386
+ cudaMalloc(&d_hits, max_hits * sizeof(Hit));
387
+ cudaMalloc(&d_unmatched, max_unmatched * sizeof(Hit));
388
+ cudaMalloc(&d_hit_count, sizeof(int));
389
+ cudaMalloc(&d_unmatched_count, sizeof(int));
390
+ cudaMemset(d_hit_count, 0, sizeof(int));
391
+ cudaMemset(d_unmatched_count, 0, sizeof(int));
392
+
393
+ struct timespec t0, t1;
394
+ clock_gettime(CLOCK_MONOTONIC, &t0);
395
+
396
+ long long chunk_size = 1000000LL;
397
+ int total_hits = 0;
398
+ int total_unmatched = 0;
399
+
400
+ // Output files
401
+ char hits_path[512], unmatched_path[512];
402
+ snprintf(hits_path, 512,
403
+ "scripts/experiments/ramanujan-machine/results/v2_hits_a%d_b%d_r%d_%d.csv",
404
+ deg_a, deg_b, range_a, range_b);
405
+ snprintf(unmatched_path, 512,
406
+ "scripts/experiments/ramanujan-machine/results/v2_unmatched_a%d_b%d_r%d_%d.csv",
407
+ deg_a, deg_b, range_a, range_b);
408
+
409
+ FILE *fhits = fopen(hits_path, "w");
410
+ FILE *funm = fopen(unmatched_path, "w");
411
+ if (fhits) fprintf(fhits, "a_coeffs,b_coeffs,value,constant,c0,c1,c2\n");
412
+ if (funm) fprintf(funm, "a_coeffs,b_coeffs,value\n");
413
+
414
+ for (long long offset = 0; offset < total_candidates; offset += chunk_size) {
415
+ long long this_chunk = chunk_size;
416
+ if (offset + this_chunk > total_candidates)
417
+ this_chunk = total_candidates - offset;
418
+
419
+ int grid = (this_chunk + BLOCK - 1) / BLOCK;
420
+ search_kernel<<<grid, BLOCK>>>(
421
+ offset, this_chunk, deg_a, deg_b, range_a, range_b, cf_depth,
422
+ d_hits, d_hit_count, max_hits,
423
+ d_unmatched, d_unmatched_count, max_unmatched);
424
+
425
+ if ((offset / chunk_size) % 100 == 0 || offset + this_chunk >= total_candidates) {
426
+ cudaDeviceSynchronize();
427
+
428
+ int h_hit_count, h_unm_count;
429
+ cudaMemcpy(&h_hit_count, d_hit_count, sizeof(int), cudaMemcpyDeviceToHost);
430
+ cudaMemcpy(&h_unm_count, d_unmatched_count, sizeof(int), cudaMemcpyDeviceToHost);
431
+
432
+ // Write new matched hits
433
+ if (h_hit_count > total_hits) {
434
+ Hit *h_hits = (Hit *)malloc(h_hit_count * sizeof(Hit));
435
+ cudaMemcpy(h_hits, d_hits, h_hit_count * sizeof(Hit), cudaMemcpyDeviceToHost);
436
+
437
+ for (int i = total_hits; i < h_hit_count && i < max_hits; i++) {
438
+ Hit *h = &h_hits[i];
439
+ if (h->value > -1e-8 && h->value < 1e-8) continue;
440
+
441
+ printf(" HIT: a=(");
442
+ for (int j = 0; j <= h->deg_a; j++) printf("%s%d", j?",":"", h->a_coeffs[j]);
443
+ printf(") b=(");
444
+ for (int j = 0; j <= h->deg_b; j++) printf("%s%d", j?",":"", h->b_coeffs[j]);
445
+ printf(") → %.15g", h->value);
446
+
447
+ if (h->match_c2 == -999)
448
+ printf(" = %s^(%d/%d)", get_const_name(h->match_const),
449
+ h->match_c0, h->match_c1);
450
+ else
451
+ printf(" = (%d + %d*%s)/%d", h->match_c0, h->match_c2,
452
+ get_const_name(h->match_const), h->match_c1);
453
+ printf("\n");
454
+
455
+ if (fhits) {
456
+ fprintf(fhits, "\"(");
457
+ for (int j = 0; j <= h->deg_a; j++) fprintf(fhits, "%s%d", j?",":"", h->a_coeffs[j]);
458
+ fprintf(fhits, ")\",\"(");
459
+ for (int j = 0; j <= h->deg_b; j++) fprintf(fhits, "%s%d", j?",":"", h->b_coeffs[j]);
460
+ fprintf(fhits, ")\",%.*g,%s,%d,%d,%d\n",
461
+ 17, h->value, get_const_name(h->match_const),
462
+ h->match_c0, h->match_c1, h->match_c2);
463
+ }
464
+ }
465
+ total_hits = h_hit_count;
466
+ free(h_hits);
467
+ if (fhits) fflush(fhits);
468
+ }
469
+
470
+ // Write new unmatched CFs
471
+ if (h_unm_count > total_unmatched) {
472
+ Hit *h_unm = (Hit *)malloc(h_unm_count * sizeof(Hit));
473
+ cudaMemcpy(h_unm, d_unmatched, h_unm_count * sizeof(Hit), cudaMemcpyDeviceToHost);
474
+
475
+ for (int i = total_unmatched; i < h_unm_count && i < max_unmatched; i++) {
476
+ Hit *h = &h_unm[i];
477
+ if (funm) {
478
+ fprintf(funm, "\"(");
479
+ for (int j = 0; j <= h->deg_a; j++) fprintf(funm, "%s%d", j?",":"", h->a_coeffs[j]);
480
+ fprintf(funm, ")\",\"(");
481
+ for (int j = 0; j <= h->deg_b; j++) fprintf(funm, "%s%d", j?",":"", h->b_coeffs[j]);
482
+ fprintf(funm, ")\",%.*g\n", 17, h->value);
483
+ }
484
+ }
485
+ total_unmatched = h_unm_count;
486
+ free(h_unm);
487
+ if (funm) fflush(funm);
488
+ }
489
+
490
+ clock_gettime(CLOCK_MONOTONIC, &t1);
491
+ double elapsed = (t1.tv_sec - t0.tv_sec) + (t1.tv_nsec - t0.tv_nsec) / 1e9;
492
+ double pct = 100.0 * (offset + this_chunk) / total_candidates;
493
+ double rate = (offset + this_chunk) / elapsed;
494
+ double eta = (total_candidates - offset - this_chunk) / (rate + 1);
495
+
496
+ printf(" %.1f%% (%lld/%lld) %d matched, %d unmatched, %.0f/sec, ETA %.0fs\n",
497
+ pct, offset + this_chunk, total_candidates,
498
+ total_hits, total_unmatched, rate, eta);
499
+ fflush(stdout);
500
+ }
501
+ }
502
+
503
+ if (fhits) fclose(fhits);
504
+ if (funm) fclose(funm);
505
+
506
+ clock_gettime(CLOCK_MONOTONIC, &t1);
507
+ double total_time = (t1.tv_sec - t0.tv_sec) + (t1.tv_nsec - t0.tv_nsec) / 1e9;
508
+
509
+ printf("\n========================================\n");
510
+ printf("Ramanujan Machine v2 Results\n");
511
+ printf("========================================\n");
512
+ printf("a(n): deg=%d range=[-%d,%d]\n", deg_a, range_a, range_a);
513
+ printf("b(n): deg=%d range=[-%d,%d]\n", deg_b, range_b, range_b);
514
+ printf("Degree ratio: %.2f\n", ratio);
515
+ printf("Candidates: %lld (%.2e)\n", total_candidates, (double)total_candidates);
516
+ printf("Matched hits: %d\n", total_hits);
517
+ printf("Unmatched converged: %d (saved for PSLQ)\n", total_unmatched);
518
+ printf("Time: %.1fs (%.0f candidates/sec)\n", total_time,
519
+ total_candidates / total_time);
520
+ if (total_hits > 0)
521
+ printf("Hits CSV: %s\n", hits_path);
522
+ if (total_unmatched > 0)
523
+ printf("Unmatched CSV: %s\n", unmatched_path);
524
+ printf("========================================\n");
525
+
526
+ printf("\nNext step: run PSLQ verification on matched hits:\n");
527
+ printf(" python3 scripts/experiments/ramanujan-machine/verify_hits.py %s\n",
528
+ hits_path);
529
+ printf("Next step: run multi-constant PSLQ on unmatched CFs:\n");
530
+ printf(" python3 scripts/experiments/ramanujan-machine/pslq_scan.py %s\n",
531
+ unmatched_path);
532
+
533
+ cudaFree(d_hits); cudaFree(d_unmatched);
534
+ cudaFree(d_hit_count); cudaFree(d_unmatched_count);
535
+ return 0;
536
+ }
scripts/test.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """CPU-only verification test for Ramanujan Machine v2 (Asymmetric-Degree CF Search)"""
2
+ print("Testing ramanujan-machine-cuda...")
3
+
4
+ import math
5
+
6
+ def eval_cf(a_coeffs, b_coeffs, depth=100):
7
+ """Evaluate generalized CF: a(0) + b(1)/(a(1) + b(2)/(a(2) + ...))"""
8
+ def a(n):
9
+ return sum(c * n**i for i, c in enumerate(a_coeffs))
10
+ def b(n):
11
+ return sum(c * n**i for i, c in enumerate(b_coeffs))
12
+ # Backward recurrence
13
+ val = a(depth)
14
+ for n in range(depth-1, 0, -1):
15
+ bn = b(n)
16
+ if abs(val) < 1e-15: break
17
+ val = a(n) + bn / val
18
+ return val
19
+
20
+ # Known: a(n)=2n+1, b(n)=-n^2 gives 4/pi - 1 (Brouncker-type)
21
+ # Actually: [1; 1,1,1,...] with a(n)=1,b(n)=1 = golden ratio
22
+ phi = (1 + math.sqrt(5)) / 2
23
+ val = eval_cf([1], [1])
24
+ print(f" CF [1;1,1,...] = {val:.10f} (phi = {phi:.10f})")
25
+ assert abs(val - phi) < 1e-6, f"Expected phi, got {val}"
26
+
27
+ # Known: a(n)=2n+1, b(n)=n^2 gives e-1 (Euler's CF for e)
28
+ # Actually e = 2 + 1/(1+1/(2+1/(1+1/(1+1/(4+...))))) but that's the regular CF
29
+ # Simpler: CF with a=[1,3,5,7,...], b=[0,1,1,1,...] for 4/pi? Let's just test golden ratio.
30
+ print(f"\n1/1 tests passed")
31
+
torch-ext/torch_binding.cpp ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #include <torch/extension.h>
2
+ #include "torch_binding.h"
3
+
4
+ PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
5
+ m.doc() = "Ramanujan Machine v2 (Asymmetric-Degree CF Search) CUDA kernel";
6
+ }
torch-ext/torch_binding.h ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ #pragma once
2
+ #include <torch/torch.h>
3
+ // See ramanujan/ramanujan_v2.cu for kernel API