redscript-mc 2.1.1 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +66 -21
  3. package/README.zh.md +61 -61
  4. package/dist/src/__tests__/e2e/basic.test.js +25 -0
  5. package/dist/src/__tests__/e2e/coroutine.test.js +22 -0
  6. package/dist/src/__tests__/mc-integration.test.js +25 -13
  7. package/dist/src/__tests__/schedule.test.js +105 -0
  8. package/dist/src/__tests__/tuner/engine.test.d.ts +4 -0
  9. package/dist/src/__tests__/tuner/engine.test.js +232 -0
  10. package/dist/src/__tests__/typechecker.test.js +63 -0
  11. package/dist/src/emit/compile.js +1 -0
  12. package/dist/src/emit/index.js +3 -1
  13. package/dist/src/lir/lower.js +26 -0
  14. package/dist/src/mir/lower.js +341 -12
  15. package/dist/src/mir/types.d.ts +10 -0
  16. package/dist/src/optimizer/copy_prop.js +4 -0
  17. package/dist/src/optimizer/coroutine.d.ts +2 -0
  18. package/dist/src/optimizer/coroutine.js +33 -1
  19. package/dist/src/optimizer/dce.js +7 -1
  20. package/dist/src/optimizer/lir/const_imm.js +1 -1
  21. package/dist/src/optimizer/lir/dead_slot.js +1 -1
  22. package/dist/src/tuner/adapters/ln-polynomial.d.ts +23 -0
  23. package/dist/src/tuner/adapters/ln-polynomial.js +142 -0
  24. package/dist/src/tuner/adapters/sqrt-newton.d.ts +28 -0
  25. package/dist/src/tuner/adapters/sqrt-newton.js +125 -0
  26. package/dist/src/tuner/cli.d.ts +5 -0
  27. package/dist/src/tuner/cli.js +168 -0
  28. package/dist/src/tuner/engine.d.ts +17 -0
  29. package/dist/src/tuner/engine.js +215 -0
  30. package/dist/src/tuner/metrics.d.ts +15 -0
  31. package/dist/src/tuner/metrics.js +51 -0
  32. package/dist/src/tuner/simulator.d.ts +35 -0
  33. package/dist/src/tuner/simulator.js +78 -0
  34. package/dist/src/tuner/types.d.ts +32 -0
  35. package/dist/src/tuner/types.js +6 -0
  36. package/dist/src/typechecker/index.d.ts +2 -0
  37. package/dist/src/typechecker/index.js +29 -0
  38. package/docs/ROADMAP.md +35 -0
  39. package/docs/STDLIB_ROADMAP.md +142 -0
  40. package/editors/vscode/package-lock.json +3 -3
  41. package/editors/vscode/package.json +1 -1
  42. package/examples/coroutine-demo.mcrs +11 -10
  43. package/jest.config.js +19 -0
  44. package/package.json +1 -1
  45. package/src/__tests__/e2e/basic.test.ts +27 -0
  46. package/src/__tests__/e2e/coroutine.test.ts +23 -0
  47. package/src/__tests__/fixtures/array-test.mcrs +21 -22
  48. package/src/__tests__/fixtures/counter.mcrs +17 -0
  49. package/src/__tests__/fixtures/foreach-at-test.mcrs +9 -10
  50. package/src/__tests__/mc-integration.test.ts +25 -13
  51. package/src/__tests__/schedule.test.ts +112 -0
  52. package/src/__tests__/tuner/engine.test.ts +260 -0
  53. package/src/__tests__/typechecker.test.ts +68 -0
  54. package/src/emit/compile.ts +1 -0
  55. package/src/emit/index.ts +3 -1
  56. package/src/lir/lower.ts +27 -0
  57. package/src/mir/lower.ts +355 -9
  58. package/src/mir/types.ts +4 -0
  59. package/src/optimizer/copy_prop.ts +4 -0
  60. package/src/optimizer/coroutine.ts +37 -1
  61. package/src/optimizer/dce.ts +6 -1
  62. package/src/optimizer/lir/const_imm.ts +1 -1
  63. package/src/optimizer/lir/dead_slot.ts +1 -1
  64. package/src/stdlib/bigint.mcrs +155 -192
  65. package/src/stdlib/bits.mcrs +158 -0
  66. package/src/stdlib/color.mcrs +160 -0
  67. package/src/stdlib/geometry.mcrs +124 -0
  68. package/src/stdlib/list.mcrs +125 -0
  69. package/src/stdlib/math.mcrs +90 -0
  70. package/src/stdlib/math_hp.mcrs +65 -0
  71. package/src/stdlib/random.mcrs +67 -0
  72. package/src/stdlib/signal.mcrs +112 -0
  73. package/src/stdlib/timer.mcrs +10 -5
  74. package/src/stdlib/vec.mcrs +27 -0
  75. package/src/tuner/adapters/ln-polynomial.ts +147 -0
  76. package/src/tuner/adapters/sqrt-newton.ts +135 -0
  77. package/src/tuner/cli.ts +158 -0
  78. package/src/tuner/engine.ts +272 -0
  79. package/src/tuner/metrics.ts +66 -0
  80. package/src/tuner/simulator.ts +69 -0
  81. package/src/tuner/types.ts +44 -0
  82. package/src/typechecker/index.ts +39 -0
  83. package/docs/ARCHITECTURE.zh.md +0 -1088
  84. package/docs/COMPILATION_STATS.md +0 -142
  85. package/docs/IMPLEMENTATION_GUIDE.md +0 -512
@@ -0,0 +1,142 @@
1
+ "use strict";
2
+ /**
3
+ * ln(x) polynomial approximation adapter — atanh series form.
4
+ *
5
+ * Algorithm:
6
+ * Input x: fixed-point integer (×10000), e.g. 10000 = 1.0
7
+ * 1. Range reduction: find k s.t. xr ∈ [10000, 20000)
8
+ * 2. s = (xr - 10000) * 10000 / (xr + 10000) → s ∈ [0, 3333]
9
+ * 3. ln(xr/10000) ≈ A1*s/SCALE + A3*s³/SCALE² + A5*s⁵/SCALE³
10
+ * (coefficients absorb the factor of 2; theoretical: A1=20000, A3=6667, A5=4000)
11
+ * 4. ln(x/10000) = k * LN2 + ln(xr/10000)
12
+ *
13
+ * Intermediate overflow analysis (s ≤ 3333, SCALE = 10000):
14
+ * s² = s*s ≤ 11M — fits int32 (max ~2.1B)
15
+ * s2 = s²/SCALE ≤ 1111
16
+ * s3 = s*s2 ≤ 3.7M — fits int32
17
+ * s5 = s3*s2 ≤ 4.1M — fits int32
18
+ * A1*s ≤ 22000*3333 ≤ 73M — fits int32
19
+ * A3*s3 ≤ 7000*3703 ≤ 26M — fits int32
20
+ * A5*s5 ≤ 5000*4115 ≤ 21M — fits int32
21
+ */
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.lnPolynomialAdapter = exports.defaultParams = void 0;
24
+ const simulator_1 = require("../simulator");
25
+ const SCALE = 10000;
26
+ const LN2_SCALED = 6931; // ln(2) * 10000
27
+ exports.defaultParams = {
28
+ A1: 20000,
29
+ A3: 6667,
30
+ A5: 4000,
31
+ };
32
+ exports.lnPolynomialAdapter = {
33
+ name: 'ln-polynomial',
34
+ description: 'ln(x) atanh series approximation using fixed-point int32 arithmetic',
35
+ params: [
36
+ { name: 'A1', range: [18000, 22000], integer: true },
37
+ { name: 'A3', range: [5000, 9000], integer: true },
38
+ { name: 'A5', range: [2000, 6000], integer: true },
39
+ ],
40
+ simulate(input, params) {
41
+ const A1 = (0, simulator_1.i32)(params['A1']);
42
+ const A3 = (0, simulator_1.i32)(params['A3']);
43
+ const A5 = (0, simulator_1.i32)(params['A5']);
44
+ let x = (0, simulator_1.i32)(input);
45
+ if (x <= 0)
46
+ return -2147483648;
47
+ // Step 1: range reduction to [10000, 20000)
48
+ let k = 0;
49
+ while (x < 10000) {
50
+ x = (0, simulator_1.i32)(x * 2);
51
+ k--;
52
+ }
53
+ while (x >= 20000) {
54
+ x = (0, simulator_1.i32)(x / 2);
55
+ k++;
56
+ }
57
+ // Step 2: s = (xr - SCALE) * SCALE / (xr + SCALE)
58
+ const num = (0, simulator_1.i32)((0, simulator_1.i32)(x - SCALE) * SCALE);
59
+ const den = (0, simulator_1.i32)(x + SCALE);
60
+ if (den === 0)
61
+ return Infinity;
62
+ const s = (0, simulator_1.i32)(num / den); // s ∈ [0, 3333]
63
+ // Step 3: atanh series (overflow-safe, each power divided by SCALE)
64
+ const s2 = (0, simulator_1.i32)((0, simulator_1.i32)(s * s) / SCALE); // s²/SCALE ∈ [0, 1111]
65
+ const s3 = (0, simulator_1.i32)((0, simulator_1.i32)(s * s2) / SCALE); // s³/SCALE² ∈ [0, 370]
66
+ const s5 = (0, simulator_1.i32)((0, simulator_1.i32)(s3 * s2) / SCALE); // s⁵/SCALE⁴ ∈ [0, 41]
67
+ const lnxr = (0, simulator_1.i32)((0, simulator_1.i32)(A1 * s / SCALE) +
68
+ (0, simulator_1.i32)(A3 * s3 / SCALE) +
69
+ (0, simulator_1.i32)(A5 * s5 / SCALE));
70
+ // Step 4: ln(x) = k*ln(2) + ln(xr)
71
+ return (0, simulator_1.i32)((0, simulator_1.i32)(k * LN2_SCALED) + lnxr);
72
+ },
73
+ reference(input) {
74
+ if (input <= 0)
75
+ return -Infinity;
76
+ return Math.log(input / SCALE) * SCALE;
77
+ },
78
+ sampleInputs() {
79
+ const inputs = [];
80
+ // logarithmic sampling from 0.01 to 100 (×10000: 100 to 1_000_000)
81
+ const logMin = Math.log10(100);
82
+ const logMax = Math.log10(1_000_000);
83
+ const steps = 200;
84
+ for (let i = 0; i <= steps; i++) {
85
+ const v = logMin + (i / steps) * (logMax - logMin);
86
+ inputs.push(Math.round(Math.pow(10, v)));
87
+ }
88
+ return inputs;
89
+ },
90
+ generateCode(params, meta) {
91
+ const A1 = Math.round(params['A1']);
92
+ const A3 = Math.round(params['A3']);
93
+ const A5 = Math.round(params['A5']);
94
+ return `// AUTO-GENERATED by redscript tune — DO NOT EDIT
95
+ // Adapter: ln-polynomial | Date: ${meta.tuneDate}
96
+ // max_error: ${meta.maxError.toFixed(6)} | mae: ${meta.mae.toFixed(6)} | estimated_cmds: ${meta.estimatedCmds}
97
+ // Run \`redscript tune --adapter ln-polynomial\` to regenerate
98
+ //
99
+ // atanh series coefficients (×10000 scale):
100
+ // A1 = ${A1} (theoretical 2×10000 = 20000)
101
+ // A3 = ${A3} (theoretical 2/3×10000 = 6667)
102
+ // A5 = ${A5} (theoretical 2/5×10000 = 4000)
103
+
104
+ fn ln(x: int): int {
105
+ // Input x: fixed-point ×10000; returns ln(x/10000)×10000
106
+ // Valid range: x ∈ [100, 1000000] (0.01 ~ 100.0)
107
+ // max_error: ${meta.maxError.toFixed(6)} (in ×10000 units, i.e. ${(meta.maxError / SCALE).toFixed(8)} in real units)
108
+
109
+ let scale: int = ${SCALE};
110
+ let ln2: int = ${LN2_SCALED};
111
+ let A1: int = ${A1};
112
+ let A3: int = ${A3};
113
+ let A5: int = ${A5};
114
+
115
+ // Step 1: range reduction — bring x into [scale, 2*scale)
116
+ let xr: int = x;
117
+ let k: int = 0;
118
+ while (xr < scale) {
119
+ xr = xr * 2;
120
+ k = k - 1;
121
+ }
122
+ while (xr >= scale * 2) {
123
+ xr = xr / 2;
124
+ k = k + 1;
125
+ }
126
+
127
+ // Step 2: s = (xr - scale) * scale / (xr + scale)
128
+ let s: int = (xr - scale) * scale / (xr + scale);
129
+
130
+ // Step 3: atanh series ln(xr/scale) ≈ A1*s + A3*s³ + A5*s⁵ (all /scale)
131
+ let s2: int = s * s / scale;
132
+ let s3: int = s * s2;
133
+ let s5: int = s3 * s2;
134
+ let lnxr: int = A1 * s / scale + A3 * s3 / scale + A5 * s5 / scale;
135
+
136
+ // Step 4: ln(x) = k*ln(2) + ln(xr/scale)
137
+ return k * ln2 + lnxr;
138
+ }
139
+ `;
140
+ },
141
+ };
142
+ //# sourceMappingURL=ln-polynomial.js.map
@@ -0,0 +1,28 @@
1
+ /**
2
+ * sqrt(x) Newton's method adapter — fixed-point ×10000 scale.
3
+ *
4
+ * Algorithm:
5
+ * Input x: fixed-point integer (×10000), e.g. 10000 = 1.0
6
+ * Target: return floor(sqrt(x/10000) * 10000)
7
+ *
8
+ * 1. If x <= 0, return 0
9
+ * 2. Initial guess: g = x >> INIT_SHIFT (default: x/2)
10
+ * 3. Newton iteration: g = (g + x * 10000 / g) / 2, repeated N times
11
+ * Overflow-safe: x * 10000 / g → (x * 100) / (g / 100)
12
+ * 4. Return g
13
+ *
14
+ * Valid range: x ∈ [1, 1_000_000] (real range 0.0001 to 100.0)
15
+ * Larger x needs more iterations; beyond 1M, convergence is not guaranteed
16
+ * within N=12 iterations from an x/2 initial guess.
17
+ *
18
+ * Overflow analysis (x ≤ 1_000_000, SCALE = 10000):
19
+ * x * 100 ≤ 100_000_000 — fits int32 (max ~2.1B) ✓
20
+ * g / 100 is never 0 for g ≥ 100 (guard included) ✓
21
+ *
22
+ * Parameters:
23
+ * N: iteration count, range [4, 12], integer
24
+ * INIT_SHIFT: initial guess right shift, range [0, 3], integer (1 → x/2)
25
+ */
26
+ import { TunerAdapter } from '../types';
27
+ export declare const defaultParams: Record<string, number>;
28
+ export declare const sqrtNewtonAdapter: TunerAdapter;
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ /**
3
+ * sqrt(x) Newton's method adapter — fixed-point ×10000 scale.
4
+ *
5
+ * Algorithm:
6
+ * Input x: fixed-point integer (×10000), e.g. 10000 = 1.0
7
+ * Target: return floor(sqrt(x/10000) * 10000)
8
+ *
9
+ * 1. If x <= 0, return 0
10
+ * 2. Initial guess: g = x >> INIT_SHIFT (default: x/2)
11
+ * 3. Newton iteration: g = (g + x * 10000 / g) / 2, repeated N times
12
+ * Overflow-safe: x * 10000 / g → (x * 100) / (g / 100)
13
+ * 4. Return g
14
+ *
15
+ * Valid range: x ∈ [1, 1_000_000] (real range 0.0001 to 100.0)
16
+ * Larger x needs more iterations; beyond 1M, convergence is not guaranteed
17
+ * within N=12 iterations from an x/2 initial guess.
18
+ *
19
+ * Overflow analysis (x ≤ 1_000_000, SCALE = 10000):
20
+ * x * 100 ≤ 100_000_000 — fits int32 (max ~2.1B) ✓
21
+ * g / 100 is never 0 for g ≥ 100 (guard included) ✓
22
+ *
23
+ * Parameters:
24
+ * N: iteration count, range [4, 12], integer
25
+ * INIT_SHIFT: initial guess right shift, range [0, 3], integer (1 → x/2)
26
+ */
27
+ Object.defineProperty(exports, "__esModule", { value: true });
28
+ exports.sqrtNewtonAdapter = exports.defaultParams = void 0;
29
+ const simulator_1 = require("../simulator");
30
+ const SCALE = 10000;
31
+ exports.defaultParams = {
32
+ N: 8,
33
+ INIT_SHIFT: 1,
34
+ };
35
+ exports.sqrtNewtonAdapter = {
36
+ name: 'sqrt-newton',
37
+ description: 'sqrt(x) Newton iteration using fixed-point int32 arithmetic (×10000 scale)',
38
+ params: [
39
+ { name: 'N', range: [4, 12], integer: true },
40
+ { name: 'INIT_SHIFT', range: [0, 3], integer: true },
41
+ ],
42
+ simulate(input, params) {
43
+ const N = Math.round(params['N']);
44
+ const INIT_SHIFT = Math.round(params['INIT_SHIFT']);
45
+ let x = (0, simulator_1.i32)(input);
46
+ if (x <= 0)
47
+ return 0;
48
+ // Initial guess: x >> INIT_SHIFT
49
+ let g = (0, simulator_1.i32)(x >> INIT_SHIFT);
50
+ if (g <= 0)
51
+ g = 1; // guard against zero/negative guess
52
+ // Newton iterations: g = (g + x * 10000 / g) / 2
53
+ // Overflow-safe for x ≤ 1_000_000: split x*10000/g as (x*100)/(g/100)
54
+ for (let iter = 0; iter < N; iter++) {
55
+ const gDiv = (0, simulator_1.i32)(g / 100);
56
+ if (gDiv <= 0)
57
+ break; // guard: g is too small, stop
58
+ const xdivg = (0, simulator_1.i32)((0, simulator_1.i32)(x * 100) / gDiv); // ≈ x * 10000 / g
59
+ g = (0, simulator_1.i32)((0, simulator_1.i32)(g + xdivg) / 2);
60
+ if (g <= 0) {
61
+ g = 1;
62
+ break;
63
+ }
64
+ }
65
+ return g;
66
+ },
67
+ reference(input) {
68
+ if (input <= 0)
69
+ return 0;
70
+ return Math.floor(Math.sqrt(input / SCALE) * SCALE);
71
+ },
72
+ sampleInputs() {
73
+ const inputs = [];
74
+ // Logarithmic sampling from 0.01 to 100.0 (×10000: 100 to 1_000_000)
75
+ // Lower bound 100 avoids g/100=0 in overflow-safe Newton step.
76
+ const logMin = Math.log10(100);
77
+ const logMax = Math.log10(1_000_000);
78
+ const steps = 200;
79
+ for (let i = 0; i <= steps; i++) {
80
+ const v = logMin + (i / steps) * (logMax - logMin);
81
+ const x = Math.round(Math.pow(10, v));
82
+ inputs.push(x);
83
+ }
84
+ // Exact perfect squares (k=1..10 → x up to 100 * 10000 = 1_000_000)
85
+ for (let k = 1; k <= 10; k++) {
86
+ inputs.push(k * k * SCALE); // sqrt should be exactly k * SCALE
87
+ }
88
+ return inputs;
89
+ },
90
+ generateCode(params, meta) {
91
+ const N = Math.round(params['N']);
92
+ const INIT_SHIFT = Math.round(params['INIT_SHIFT']);
93
+ const iters = Array.from({ length: N }, (_, i) => ` g = (g + x * 100 / (g / 100)) / 2; // iteration ${i + 1}`).join('\n');
94
+ return `// AUTO-GENERATED by redscript tune — DO NOT EDIT
95
+ // Adapter: sqrt-newton | Date: ${meta.tuneDate}
96
+ // max_error: ${meta.maxError.toFixed(6)} | mae: ${meta.mae.toFixed(6)} | estimated_cmds: ${meta.estimatedCmds}
97
+ // Run \`redscript tune --adapter sqrt-newton\` to regenerate
98
+ //
99
+ // Parameters:
100
+ // N = ${N} (Newton iteration count)
101
+ // INIT_SHIFT = ${INIT_SHIFT} (initial guess = x >> ${INIT_SHIFT}, i.e. x / ${1 << INIT_SHIFT})
102
+
103
+ fn sqrt_fx(x: int): int {
104
+ // Input x: fixed-point ×10000, returns floor(sqrt(x/10000)*10000)
105
+ // Valid range: x ∈ [0, 1000000] (real range 0.0 to 100.0)
106
+ // max_error: ${meta.maxError.toFixed(6)} (in ×10000 units)
107
+ //
108
+ // Overflow-safe: x*10000/g computed as (x*100)/(g/100), valid for x ≤ 1_000_000
109
+
110
+ if (x <= 0) { return 0; }
111
+
112
+ // Initial guess: x / ${1 << INIT_SHIFT}
113
+ let g: int = x / ${1 << INIT_SHIFT};
114
+ if (g <= 0) { g = 1; }
115
+
116
+ // Newton iterations: g = (g + x * 10000 / g) / 2
117
+ // Split to avoid overflow: (x * 100) / (g / 100) ≈ x * 10000 / g
118
+ ${iters}
119
+
120
+ return g;
121
+ }
122
+ `;
123
+ },
124
+ };
125
+ //# sourceMappingURL=sqrt-newton.js.map
@@ -0,0 +1,5 @@
1
+ /**
2
+ * CLI entry point for `redscript tune`.
3
+ * Usage: redscript tune --adapter <name> [--budget N] [--out path]
4
+ */
5
+ export {};
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ /**
3
+ * CLI entry point for `redscript tune`.
4
+ * Usage: redscript tune --adapter <name> [--budget N] [--out path]
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ const fs = __importStar(require("fs"));
41
+ const path = __importStar(require("path"));
42
+ const engine_1 = require("./engine");
43
+ const ln_polynomial_1 = require("./adapters/ln-polynomial");
44
+ const sqrt_newton_1 = require("./adapters/sqrt-newton");
45
+ const ADAPTERS = {
46
+ 'ln-polynomial': ln_polynomial_1.lnPolynomialAdapter,
47
+ 'sqrt-newton': sqrt_newton_1.sqrtNewtonAdapter,
48
+ };
49
+ function printUsage() {
50
+ console.log(`Usage: redscript tune --adapter <name> [--budget N] [--out path]
51
+
52
+ Available adapters:
53
+ ${Object.entries(ADAPTERS)
54
+ .map(([name, a]) => ` ${name.padEnd(20)} ${a.description}`)
55
+ .join('\n')}
56
+
57
+ Options:
58
+ --adapter <name> Adapter to use (required)
59
+ --budget <N> Max optimizer iterations (default: 10000)
60
+ --out <path> Output .mcrs file path (optional)
61
+ `);
62
+ }
63
+ function parseArgs(args) {
64
+ const result = { budget: 10000, strategy: 'nm' };
65
+ for (let i = 0; i < args.length; i++) {
66
+ if (args[i] === '--adapter' && args[i + 1]) {
67
+ result.adapter = args[++i];
68
+ }
69
+ else if (args[i] === '--budget' && args[i + 1]) {
70
+ result.budget = parseInt(args[++i], 10);
71
+ }
72
+ else if (args[i] === '--out' && args[i + 1]) {
73
+ result.out = args[++i];
74
+ }
75
+ else if (args[i] === '--strategy' && args[i + 1]) {
76
+ const s = args[++i];
77
+ if (s === 'nm' || s === 'sa')
78
+ result.strategy = s;
79
+ }
80
+ }
81
+ return result;
82
+ }
83
+ function renderProgressBar(fraction, width = 30) {
84
+ const filled = Math.round(fraction * width);
85
+ const bar = '█'.repeat(filled) + '░'.repeat(width - filled);
86
+ return `[${bar}]`;
87
+ }
88
+ async function main() {
89
+ // Skip 'node', 'ts-node', 'cli.ts' etc from argv
90
+ const rawArgs = process.argv.slice(2);
91
+ // Support `redscript tune` prefix (first arg might be 'tune')
92
+ const args = rawArgs[0] === 'tune' ? rawArgs.slice(1) : rawArgs;
93
+ if (args.includes('--help') || args.includes('-h') || args.length === 0) {
94
+ printUsage();
95
+ process.exit(0);
96
+ }
97
+ const { adapter: adapterName, budget, out, strategy } = parseArgs(args);
98
+ if (!adapterName) {
99
+ console.error('Error: --adapter is required');
100
+ printUsage();
101
+ process.exit(1);
102
+ }
103
+ const adapter = ADAPTERS[adapterName];
104
+ if (!adapter) {
105
+ console.error(`Error: unknown adapter "${adapterName}"`);
106
+ console.error(`Available: ${Object.keys(ADAPTERS).join(', ')}`);
107
+ process.exit(1);
108
+ }
109
+ console.log(`\nredscript tune — ${adapter.name}`);
110
+ console.log(`Description: ${adapter.description}`);
111
+ console.log(`Strategy: ${strategy === 'sa' ? 'Simulated Annealing' : 'Nelder-Mead'}`);
112
+ console.log(`Budget: ${budget} iterations`);
113
+ console.log(`Parameters: ${adapter.params.map(p => p.name).join(', ')}\n`);
114
+ let lastProgress = 0;
115
+ const startTime = Date.now();
116
+ const searchFn = strategy === 'sa' ? engine_1.searchSA : engine_1.search;
117
+ const result = searchFn(adapter, budget, (iteration, bestError) => {
118
+ const fraction = iteration / budget;
119
+ const bar = renderProgressBar(fraction);
120
+ const errorStr = isFinite(bestError) ? bestError.toFixed(6) : 'Inf';
121
+ process.stdout.write(`\r ${bar} ${(fraction * 100).toFixed(1)}% iter=${iteration} best_max_error=${errorStr} `);
122
+ lastProgress = iteration;
123
+ });
124
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
125
+ process.stdout.write('\n');
126
+ console.log(`\n${'─'.repeat(60)}`);
127
+ console.log(`Optimization complete in ${elapsed}s`);
128
+ console.log(`Budget used: ${result.budgetUsed}/${budget} iterations`);
129
+ console.log(`\nResults:`);
130
+ console.log(` max_error : ${result.maxError.toFixed(8)}`);
131
+ console.log(` mae : ${result.mae.toFixed(8)}`);
132
+ console.log(` rmse : ${result.rmse.toFixed(8)}`);
133
+ console.log(`\nBest parameters:`);
134
+ for (const [k, v] of Object.entries(result.params)) {
135
+ console.log(` ${k.padEnd(12)} = ${v}`);
136
+ }
137
+ // Estimate command count (rough: ~4 cmds per param + 10 overhead)
138
+ const estimatedCmds = adapter.params.length * 4 + 15;
139
+ const meta = {
140
+ maxError: result.maxError,
141
+ mae: result.mae,
142
+ rmse: result.rmse,
143
+ estimatedCmds,
144
+ tuneDate: new Date().toISOString().split('T')[0],
145
+ budgetUsed: result.budgetUsed,
146
+ };
147
+ const code = adapter.generateCode(result.params, meta);
148
+ if (out) {
149
+ const outPath = path.resolve(out);
150
+ const dir = path.dirname(outPath);
151
+ if (!fs.existsSync(dir)) {
152
+ fs.mkdirSync(dir, { recursive: true });
153
+ }
154
+ fs.writeFileSync(outPath, code, 'utf8');
155
+ console.log(`\nWrote: ${outPath}`);
156
+ }
157
+ else {
158
+ console.log(`\n${'─'.repeat(60)}`);
159
+ console.log('Generated code:');
160
+ console.log('─'.repeat(60));
161
+ console.log(code);
162
+ }
163
+ }
164
+ main().catch(err => {
165
+ console.error(err);
166
+ process.exit(1);
167
+ });
168
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Nelder-Mead simplex optimization engine for hyperparameter tuning.
3
+ * Suitable for continuous parameter spaces without gradient information.
4
+ * Supports mixed-integer parameters.
5
+ */
6
+ import { TunerAdapter, SearchResult } from './types';
7
+ export type ProgressCallback = (iteration: number, bestError: number) => void;
8
+ /**
9
+ * Run Nelder-Mead optimization.
10
+ */
11
+ export declare function search(adapter: TunerAdapter, budget?: number, onProgress?: ProgressCallback): SearchResult;
12
+ /**
13
+ * Simulated Annealing search strategy.
14
+ * More robust than Nelder-Mead for integer parameters and multimodal objectives.
15
+ * Uses 3 independent restarts, taking the global best.
16
+ */
17
+ export declare function searchSA(adapter: TunerAdapter, budget: number, onProgress?: ProgressCallback): SearchResult;