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.
- package/CHANGELOG.md +31 -0
- package/README.md +66 -21
- package/README.zh.md +61 -61
- package/dist/src/__tests__/e2e/basic.test.js +25 -0
- package/dist/src/__tests__/e2e/coroutine.test.js +22 -0
- package/dist/src/__tests__/mc-integration.test.js +25 -13
- package/dist/src/__tests__/schedule.test.js +105 -0
- package/dist/src/__tests__/tuner/engine.test.d.ts +4 -0
- package/dist/src/__tests__/tuner/engine.test.js +232 -0
- package/dist/src/__tests__/typechecker.test.js +63 -0
- package/dist/src/emit/compile.js +1 -0
- package/dist/src/emit/index.js +3 -1
- package/dist/src/lir/lower.js +26 -0
- package/dist/src/mir/lower.js +341 -12
- package/dist/src/mir/types.d.ts +10 -0
- package/dist/src/optimizer/copy_prop.js +4 -0
- package/dist/src/optimizer/coroutine.d.ts +2 -0
- package/dist/src/optimizer/coroutine.js +33 -1
- package/dist/src/optimizer/dce.js +7 -1
- package/dist/src/optimizer/lir/const_imm.js +1 -1
- package/dist/src/optimizer/lir/dead_slot.js +1 -1
- package/dist/src/tuner/adapters/ln-polynomial.d.ts +23 -0
- package/dist/src/tuner/adapters/ln-polynomial.js +142 -0
- package/dist/src/tuner/adapters/sqrt-newton.d.ts +28 -0
- package/dist/src/tuner/adapters/sqrt-newton.js +125 -0
- package/dist/src/tuner/cli.d.ts +5 -0
- package/dist/src/tuner/cli.js +168 -0
- package/dist/src/tuner/engine.d.ts +17 -0
- package/dist/src/tuner/engine.js +215 -0
- package/dist/src/tuner/metrics.d.ts +15 -0
- package/dist/src/tuner/metrics.js +51 -0
- package/dist/src/tuner/simulator.d.ts +35 -0
- package/dist/src/tuner/simulator.js +78 -0
- package/dist/src/tuner/types.d.ts +32 -0
- package/dist/src/tuner/types.js +6 -0
- package/dist/src/typechecker/index.d.ts +2 -0
- package/dist/src/typechecker/index.js +29 -0
- package/docs/ROADMAP.md +35 -0
- package/docs/STDLIB_ROADMAP.md +142 -0
- package/editors/vscode/package-lock.json +3 -3
- package/editors/vscode/package.json +1 -1
- package/examples/coroutine-demo.mcrs +11 -10
- package/jest.config.js +19 -0
- package/package.json +1 -1
- package/src/__tests__/e2e/basic.test.ts +27 -0
- package/src/__tests__/e2e/coroutine.test.ts +23 -0
- package/src/__tests__/fixtures/array-test.mcrs +21 -22
- package/src/__tests__/fixtures/counter.mcrs +17 -0
- package/src/__tests__/fixtures/foreach-at-test.mcrs +9 -10
- package/src/__tests__/mc-integration.test.ts +25 -13
- package/src/__tests__/schedule.test.ts +112 -0
- package/src/__tests__/tuner/engine.test.ts +260 -0
- package/src/__tests__/typechecker.test.ts +68 -0
- package/src/emit/compile.ts +1 -0
- package/src/emit/index.ts +3 -1
- package/src/lir/lower.ts +27 -0
- package/src/mir/lower.ts +355 -9
- package/src/mir/types.ts +4 -0
- package/src/optimizer/copy_prop.ts +4 -0
- package/src/optimizer/coroutine.ts +37 -1
- package/src/optimizer/dce.ts +6 -1
- package/src/optimizer/lir/const_imm.ts +1 -1
- package/src/optimizer/lir/dead_slot.ts +1 -1
- package/src/stdlib/bigint.mcrs +155 -192
- package/src/stdlib/bits.mcrs +158 -0
- package/src/stdlib/color.mcrs +160 -0
- package/src/stdlib/geometry.mcrs +124 -0
- package/src/stdlib/list.mcrs +125 -0
- package/src/stdlib/math.mcrs +90 -0
- package/src/stdlib/math_hp.mcrs +65 -0
- package/src/stdlib/random.mcrs +67 -0
- package/src/stdlib/signal.mcrs +112 -0
- package/src/stdlib/timer.mcrs +10 -5
- package/src/stdlib/vec.mcrs +27 -0
- package/src/tuner/adapters/ln-polynomial.ts +147 -0
- package/src/tuner/adapters/sqrt-newton.ts +135 -0
- package/src/tuner/cli.ts +158 -0
- package/src/tuner/engine.ts +272 -0
- package/src/tuner/metrics.ts +66 -0
- package/src/tuner/simulator.ts +69 -0
- package/src/tuner/types.ts +44 -0
- package/src/typechecker/index.ts +39 -0
- package/docs/ARCHITECTURE.zh.md +0 -1088
- package/docs/COMPILATION_STATS.md +0 -142
- 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,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;
|