@cloudglides/nox 1.1.6 → 3.0.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 (66) hide show
  1. package/README.md +9 -9
  2. package/example/package.json +1 -1
  3. package/example/src/App.css +38 -7
  4. package/example/src/App.jsx +328 -25
  5. package/package.json +7 -6
  6. package/pnpm-workspace.yaml +3 -0
  7. package/src/core.browser.js +37 -4
  8. package/src/core.js +37 -4
  9. package/src/generators/logistic.js +30 -25
  10. package/src/generators/mixer.js +7 -7
  11. package/src/generators/mt19937.js +10 -7
  12. package/src/generators/pcg64.js +23 -12
  13. package/src/generators/splitmix64.js +12 -6
  14. package/src/generators/tent.js +12 -7
  15. package/src/generators/xorshift64.js +6 -3
  16. package/src/index.browser.js +168 -1
  17. package/src/index.d.ts +68 -4
  18. package/src/index.js +169 -1
  19. package/src/rng.browser.js +5 -6
  20. package/src/rng.js +95 -94
  21. package/src/utils/arrays.js +149 -0
  22. package/src/utils/bits.js +146 -21
  23. package/src/utils/categorical.js +68 -31
  24. package/src/utils/clustering.js +143 -0
  25. package/src/utils/combinatorics.js +113 -69
  26. package/src/utils/confidence.js +145 -0
  27. package/src/utils/decomposition.js +204 -0
  28. package/src/utils/diagnostics.js +309 -0
  29. package/src/utils/distributions-advanced.js +122 -0
  30. package/src/utils/distributions-extra.js +102 -11
  31. package/src/utils/distributions-special.js +77 -20
  32. package/src/utils/distributions.js +99 -35
  33. package/src/utils/effects.js +172 -0
  34. package/src/utils/entropy.browser.js +29 -26
  35. package/src/utils/entropy.js +18 -8
  36. package/src/utils/helpers.js +64 -0
  37. package/src/utils/hypothesis.js +167 -0
  38. package/src/utils/integration.js +137 -0
  39. package/src/utils/interpolation.js +221 -0
  40. package/src/utils/logistic.js +91 -0
  41. package/src/utils/matrix.js +242 -0
  42. package/src/utils/noise.js +36 -22
  43. package/src/utils/odesolvers.js +176 -0
  44. package/src/utils/optimization.js +215 -0
  45. package/src/utils/polynomial.js +136 -0
  46. package/src/utils/precomputed.js +166 -0
  47. package/src/utils/probability.js +199 -0
  48. package/src/utils/pvalues.js +142 -0
  49. package/src/utils/regression.js +170 -0
  50. package/src/utils/resampling.js +112 -0
  51. package/src/utils/rootfinding.js +158 -0
  52. package/src/utils/sampling.js +86 -77
  53. package/src/utils/seed.js +10 -4
  54. package/src/utils/seeding.js +24 -12
  55. package/src/utils/sequence.js +116 -32
  56. package/src/utils/state.js +48 -36
  57. package/src/utils/statistics.js +64 -2
  58. package/src/utils/stochastic.js +91 -31
  59. package/src/utils/stratified.js +108 -0
  60. package/src/utils/timeseries.js +166 -0
  61. package/src/utils/transforms.js +146 -0
  62. package/test/comprehensive.js +4 -3
  63. package/test/new-features.js +52 -0
  64. package/IMPROVEMENTS.md +0 -58
  65. package/PERFORMANCE.md +0 -69
  66. package/example/pnpm-lock.yaml +0 -1006
@@ -0,0 +1,91 @@
1
+ export class LogisticRegression {
2
+ constructor(X, y, learningRate = 0.01, iterations = 1000) {
3
+ if (!Array.isArray(X) || !Array.isArray(y)) {
4
+ throw new TypeError('X and y must be arrays');
5
+ }
6
+ if (X.length !== y.length || X.length < 2) {
7
+ throw new RangeError('X and y must have same length >= 2');
8
+ }
9
+ if (!Array.isArray(X[0])) {
10
+ throw new TypeError('X must be array of arrays (features)');
11
+ }
12
+ if (y.some(val => val !== 0 && val !== 1)) {
13
+ throw new RangeError('y must contain only 0 and 1 (binary classification)');
14
+ }
15
+
16
+ const n = X.length;
17
+ const p = X[0].length;
18
+
19
+ this.coefficients = new Array(p + 1).fill(0);
20
+ this.learningRate = learningRate;
21
+ this.iterations = iterations;
22
+
23
+ for (let iter = 0; iter < iterations; iter++) {
24
+ const gradients = new Array(p + 1).fill(0);
25
+
26
+ for (let i = 0; i < n; i++) {
27
+ let z = this.coefficients[0];
28
+ for (let j = 0; j < p; j++) {
29
+ z += this.coefficients[j + 1] * X[i][j];
30
+ }
31
+
32
+ const sigmoid = 1 / (1 + Math.exp(-z));
33
+ const error = sigmoid - y[i];
34
+
35
+ gradients[0] += error;
36
+ for (let j = 0; j < p; j++) {
37
+ gradients[j + 1] += error * X[i][j];
38
+ }
39
+ }
40
+
41
+ for (let j = 0; j < p + 1; j++) {
42
+ this.coefficients[j] -= (learningRate / n) * gradients[j];
43
+ }
44
+ }
45
+
46
+ this.intercept = this.coefficients[0];
47
+ }
48
+
49
+ predict(X) {
50
+ if (typeof X[0] === 'number') {
51
+ let z = this.intercept;
52
+ for (let i = 0; i < X.length; i++) {
53
+ z += this.coefficients[i + 1] * X[i];
54
+ }
55
+ return 1 / (1 + Math.exp(-z));
56
+ }
57
+
58
+ return X.map(row => {
59
+ let z = this.intercept;
60
+ for (let i = 0; i < row.length; i++) {
61
+ z += this.coefficients[i + 1] * row[i];
62
+ }
63
+ return 1 / (1 + Math.exp(-z));
64
+ });
65
+ }
66
+
67
+ predictClass(X, threshold = 0.5) {
68
+ const probs = this.predict(X);
69
+ if (Array.isArray(probs)) {
70
+ return probs.map(p => p >= threshold ? 1 : 0);
71
+ }
72
+ return probs >= threshold ? 1 : 0;
73
+ }
74
+
75
+ evaluate(X, y) {
76
+ if (X.length !== y.length) {
77
+ throw new RangeError('X and y must have same length');
78
+ }
79
+
80
+ const predictions = this.predictClass(X);
81
+ let correct = 0;
82
+
83
+ for (let i = 0; i < y.length; i++) {
84
+ if (predictions[i] === y[i]) {
85
+ correct++;
86
+ }
87
+ }
88
+
89
+ return correct / y.length;
90
+ }
91
+ }
@@ -0,0 +1,242 @@
1
+ export function matrixAdd(A, B) {
2
+ if (!Array.isArray(A) || !Array.isArray(B)) {
3
+ throw new TypeError('A and B must be matrices');
4
+ }
5
+ if (A.length !== B.length || A[0].length !== B[0].length) {
6
+ throw new RangeError('matrices must have same dimensions');
7
+ }
8
+
9
+ const result = new Array(A.length);
10
+ for (let i = 0; i < A.length; i++) {
11
+ result[i] = new Array(A[0].length);
12
+ for (let j = 0; j < A[0].length; j++) {
13
+ result[i][j] = A[i][j] + B[i][j];
14
+ }
15
+ }
16
+ return result;
17
+ }
18
+
19
+ export function matrixSub(A, B) {
20
+ if (!Array.isArray(A) || !Array.isArray(B)) {
21
+ throw new TypeError('A and B must be matrices');
22
+ }
23
+ if (A.length !== B.length || A[0].length !== B[0].length) {
24
+ throw new RangeError('matrices must have same dimensions');
25
+ }
26
+
27
+ const result = new Array(A.length);
28
+ for (let i = 0; i < A.length; i++) {
29
+ result[i] = new Array(A[0].length);
30
+ for (let j = 0; j < A[0].length; j++) {
31
+ result[i][j] = A[i][j] - B[i][j];
32
+ }
33
+ }
34
+ return result;
35
+ }
36
+
37
+ export function matrixMul(A, B) {
38
+ if (!Array.isArray(A) || !Array.isArray(B)) {
39
+ throw new TypeError('A and B must be matrices');
40
+ }
41
+ if (A[0].length !== B.length) {
42
+ throw new RangeError('incompatible dimensions for multiplication');
43
+ }
44
+
45
+ const result = new Array(A.length);
46
+ for (let i = 0; i < A.length; i++) {
47
+ result[i] = new Array(B[0].length);
48
+ for (let j = 0; j < B[0].length; j++) {
49
+ let sum = 0;
50
+ for (let k = 0; k < B.length; k++) {
51
+ sum += A[i][k] * B[k][j];
52
+ }
53
+ result[i][j] = sum;
54
+ }
55
+ }
56
+ return result;
57
+ }
58
+
59
+ export function elementwiseMul(A, B) {
60
+ if (!Array.isArray(A) || !Array.isArray(B)) {
61
+ throw new TypeError('A and B must be matrices');
62
+ }
63
+ if (A.length !== B.length || A[0].length !== B[0].length) {
64
+ throw new RangeError('matrices must have same dimensions');
65
+ }
66
+
67
+ const result = new Array(A.length);
68
+ for (let i = 0; i < A.length; i++) {
69
+ result[i] = new Array(A[0].length);
70
+ for (let j = 0; j < A[0].length; j++) {
71
+ result[i][j] = A[i][j] * B[i][j];
72
+ }
73
+ }
74
+ return result;
75
+ }
76
+
77
+ export function scalarMul(scalar, A) {
78
+ if (typeof scalar !== 'number') {
79
+ throw new TypeError('scalar must be number');
80
+ }
81
+ if (!Array.isArray(A)) {
82
+ throw new TypeError('A must be matrix');
83
+ }
84
+
85
+ const result = new Array(A.length);
86
+ for (let i = 0; i < A.length; i++) {
87
+ result[i] = new Array(A[0].length);
88
+ for (let j = 0; j < A[0].length; j++) {
89
+ result[i][j] = scalar * A[i][j];
90
+ }
91
+ }
92
+ return result;
93
+ }
94
+
95
+ export function transpose(A) {
96
+ if (!Array.isArray(A) || A.length === 0) {
97
+ throw new TypeError('A must be non-empty matrix');
98
+ }
99
+
100
+ const result = new Array(A[0].length);
101
+ for (let j = 0; j < A[0].length; j++) {
102
+ result[j] = new Array(A.length);
103
+ for (let i = 0; i < A.length; i++) {
104
+ result[j][i] = A[i][j];
105
+ }
106
+ }
107
+ return result;
108
+ }
109
+
110
+ export function determinant(A) {
111
+ if (!Array.isArray(A) || A.length !== A[0].length) {
112
+ throw new TypeError('A must be square matrix');
113
+ }
114
+
115
+ const n = A.length;
116
+ if (n === 1) return A[0][0];
117
+ if (n === 2) return A[0][0] * A[1][1] - A[0][1] * A[1][0];
118
+
119
+ const M = A.map(row => [...row]);
120
+ let det = 1;
121
+
122
+ for (let i = 0; i < n; i++) {
123
+ let maxRow = i;
124
+ for (let k = i + 1; k < n; k++) {
125
+ if (Math.abs(M[k][i]) > Math.abs(M[maxRow][i])) {
126
+ maxRow = k;
127
+ }
128
+ }
129
+
130
+ if (Math.abs(M[maxRow][i]) < 1e-10) {
131
+ return 0;
132
+ }
133
+
134
+ if (maxRow !== i) {
135
+ [M[i], M[maxRow]] = [M[maxRow], M[i]];
136
+ det *= -1;
137
+ }
138
+
139
+ det *= M[i][i];
140
+
141
+ for (let k = i + 1; k < n; k++) {
142
+ const factor = M[k][i] / M[i][i];
143
+ for (let j = i; j < n; j++) {
144
+ M[k][j] -= factor * M[i][j];
145
+ }
146
+ }
147
+ }
148
+
149
+ return det;
150
+ }
151
+
152
+ export function trace(A) {
153
+ if (!Array.isArray(A) || A.length !== A[0].length) {
154
+ throw new TypeError('A must be square matrix');
155
+ }
156
+
157
+ let sum = 0;
158
+ for (let i = 0; i < A.length; i++) {
159
+ sum += A[i][i];
160
+ }
161
+ return sum;
162
+ }
163
+
164
+ export function norm(A, type = 'frobenius') {
165
+ if (!Array.isArray(A)) {
166
+ throw new TypeError('A must be matrix');
167
+ }
168
+
169
+ if (type === 'frobenius') {
170
+ let sum = 0;
171
+ for (let i = 0; i < A.length; i++) {
172
+ for (let j = 0; j < A[0].length; j++) {
173
+ sum += A[i][j] * A[i][j];
174
+ }
175
+ }
176
+ return Math.sqrt(sum);
177
+ }
178
+
179
+ if (type === 'spectral') {
180
+ let maxNorm = 0;
181
+ for (let j = 0; j < A[0].length; j++) {
182
+ let colSum = 0;
183
+ for (let i = 0; i < A.length; i++) {
184
+ colSum += Math.abs(A[i][j]);
185
+ }
186
+ maxNorm = Math.max(maxNorm, colSum);
187
+ }
188
+ return maxNorm;
189
+ }
190
+
191
+ throw new RangeError('unknown norm type');
192
+ }
193
+
194
+ export function inverse(A) {
195
+ if (!Array.isArray(A) || A.length !== A[0].length) {
196
+ throw new TypeError('A must be square matrix');
197
+ }
198
+
199
+ const n = A.length;
200
+ const aug = A.map((row, i) => {
201
+ const augRow = [...row];
202
+ for (let j = 0; j < n; j++) {
203
+ augRow.push(i === j ? 1 : 0);
204
+ }
205
+ return augRow;
206
+ });
207
+
208
+ for (let i = 0; i < n; i++) {
209
+ let maxRow = i;
210
+ for (let k = i + 1; k < n; k++) {
211
+ if (Math.abs(aug[k][i]) > Math.abs(aug[maxRow][i])) {
212
+ maxRow = k;
213
+ }
214
+ }
215
+
216
+ if (Math.abs(aug[maxRow][i]) < 1e-10) {
217
+ throw new RangeError('matrix is singular');
218
+ }
219
+
220
+ [aug[i], aug[maxRow]] = [aug[maxRow], aug[i]];
221
+
222
+ const pivot = aug[i][i];
223
+ for (let j = 0; j < 2 * n; j++) {
224
+ aug[i][j] /= pivot;
225
+ }
226
+
227
+ for (let k = 0; k < n; k++) {
228
+ if (k !== i) {
229
+ const factor = aug[k][i];
230
+ for (let j = 0; j < 2 * n; j++) {
231
+ aug[k][j] -= factor * aug[i][j];
232
+ }
233
+ }
234
+ }
235
+ }
236
+
237
+ const result = new Array(n);
238
+ for (let i = 0; i < n; i++) {
239
+ result[i] = aug[i].slice(n);
240
+ }
241
+ return result;
242
+ }
@@ -1,17 +1,13 @@
1
- const permutation = [151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,
2
- 140,36,103,30,69,142,8,99,37,240,21,10,23,190,6,148,
3
- 247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,
4
- 57,177,33,88,237,149,56,87,174,20,125,136,171,168,68,175,
5
- 74,165,71,134,139,48,27,166,77,146,158,231,83,111,229,122,
6
- 60,211,133,230,206,39,323,234,35,158,234,144,12,234,165,
7
- 35,38,112,106,43,23,147,126,65,189,235,64,211,205,230,108,
8
- 248,191,227,21,205,45,94,2,32,284,239,210,246,24,97,78,
9
- 101,40,71,224,255,326,167,21,213,2,223,166,205,57,130,180,
10
- 51,108,203,190,57,74,76,88,207,208,239,170,251,67,77,51,
11
- 133,69,249,2,127,80,60,159,168,81,163,64,143,146,157,41,
12
- 244,8,51,153,78,130,83,142,46,168,134,55,63,160,166,56,
13
- 172,85,24,254,192,221,100,495,32,163,244,236,37,46,28,141,
14
- 149,34,69,193,70,199,154,159,147,12,130,199,10,154,27,112];
1
+ const permutation = [
2
+ 151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,190,6,148,
3
+ 247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,88,237,149,56,87,174,20,125,136,171,168,68,175,
4
+ 74,165,71,134,139,48,27,166,77,146,158,231,83,111,229,122,60,211,133,230,206,39,223,202,148,35,158,236,156,67,126,65,
5
+ 189,238,72,108,248,191,227,21,205,45,94,2,32,230,239,210,246,24,97,78,101,40,71,224,255,200,167,21,213,2,223,166,
6
+ 205,57,130,180,51,108,203,190,57,74,76,88,207,208,239,170,251,67,77,51,133,69,249,2,127,80,60,159,168,81,163,64,
7
+ 143,146,157,41,244,8,51,153,78,130,83,142,46,168,134,55,63,160,166,56,172,85,24,254,192,221,100,73,32,163,244,236,
8
+ 37,46,28,141,149,34,69,193,70,199,154,159,147,12,130,199,10,154,27,112,44,135,58,137,73,109,35,199,17,92,107,203,
9
+ 190,57,74,76,88,207,208,239,170,251,67,77,51,133,69,249,2,127,80,60,159,168,81,163,64,143,146,157,41,51,51,51,51
10
+ ];
15
11
 
16
12
  const fade = (t) => t * t * t * (t * (t * 6 - 15) + 10);
17
13
  const lerp = (t, a, b) => a + t * (b - a);
@@ -23,12 +19,21 @@ const grad = (hash, x, y) => {
23
19
  };
24
20
 
25
21
  export const perlin2D = (rng, x, y, octaves = 1) => {
26
- let value = 0;
27
- let amplitude = 1;
28
- let frequency = 1;
29
- let maxValue = 0;
30
-
31
- for (let i = 0; i < octaves; i++) {
22
+ if (typeof x !== 'number' || typeof y !== 'number') {
23
+ throw new TypeError('x and y must be numbers');
24
+ }
25
+ if (typeof octaves !== 'number' || !Number.isInteger(octaves)) {
26
+ throw new TypeError('octaves must be an integer');
27
+ }
28
+ if (octaves <= 0) {
29
+ throw new RangeError('octaves must be positive');
30
+ }
31
+ let value = 0;
32
+ let amplitude = 1;
33
+ let frequency = 1;
34
+ let maxValue = 0;
35
+
36
+ for (let i = 0; i < octaves; i++) {
32
37
  const xi = Math.floor((x * frequency) % 256);
33
38
  const yi = Math.floor((y * frequency) % 256);
34
39
 
@@ -60,8 +65,17 @@ export const perlin2D = (rng, x, y, octaves = 1) => {
60
65
  };
61
66
 
62
67
  export const valueNoise = (rng, x, y, scale = 1) => {
63
- const sx = Math.floor(x * scale);
64
- const sy = Math.floor(y * scale);
68
+ if (typeof x !== 'number' || typeof y !== 'number') {
69
+ throw new TypeError('x and y must be numbers');
70
+ }
71
+ if (typeof scale !== 'number') {
72
+ throw new TypeError('scale must be a number');
73
+ }
74
+ if (scale <= 0) {
75
+ throw new RangeError('scale must be positive');
76
+ }
77
+ const sx = Math.floor(x * scale);
78
+ const sy = Math.floor(y * scale);
65
79
 
66
80
  const n00 = Math.sin(sx * 12.9898 + sy * 78.233) * 43758.5453;
67
81
  const n10 = Math.sin((sx + 1) * 12.9898 + sy * 78.233) * 43758.5453;
@@ -0,0 +1,176 @@
1
+ export function eulerMethod(dydt, y0, t0, tf, h) {
2
+ if (typeof dydt !== 'function') {
3
+ throw new TypeError('dydt must be function');
4
+ }
5
+ if (typeof y0 !== 'number' || typeof t0 !== 'number' || typeof tf !== 'number') {
6
+ throw new TypeError('y0, t0, tf must be numbers');
7
+ }
8
+ if (typeof h !== 'number' || h <= 0) {
9
+ throw new RangeError('step size h must be positive');
10
+ }
11
+
12
+ const t = [];
13
+ const y = [];
14
+ let tCurrent = t0;
15
+ let yCurrent = y0;
16
+
17
+ while (tCurrent <= tf) {
18
+ t.push(tCurrent);
19
+ y.push(yCurrent);
20
+ yCurrent += h * dydt(tCurrent, yCurrent);
21
+ tCurrent += h;
22
+ }
23
+
24
+ return { t, y };
25
+ }
26
+
27
+ export function rk4(dydt, y0, t0, tf, h) {
28
+ if (typeof dydt !== 'function') {
29
+ throw new TypeError('dydt must be function');
30
+ }
31
+ if (typeof y0 !== 'number' || typeof t0 !== 'number' || typeof tf !== 'number') {
32
+ throw new TypeError('y0, t0, tf must be numbers');
33
+ }
34
+ if (typeof h !== 'number' || h <= 0) {
35
+ throw new RangeError('step size h must be positive');
36
+ }
37
+
38
+ const t = [];
39
+ const y = [];
40
+ let tCurrent = t0;
41
+ let yCurrent = y0;
42
+
43
+ while (tCurrent <= tf) {
44
+ t.push(tCurrent);
45
+ y.push(yCurrent);
46
+
47
+ const k1 = dydt(tCurrent, yCurrent);
48
+ const k2 = dydt(tCurrent + h / 2, yCurrent + (h / 2) * k1);
49
+ const k3 = dydt(tCurrent + h / 2, yCurrent + (h / 2) * k2);
50
+ const k4 = dydt(tCurrent + h, yCurrent + h * k3);
51
+
52
+ yCurrent += (h / 6) * (k1 + 2 * k2 + 2 * k3 + k4);
53
+ tCurrent += h;
54
+ }
55
+
56
+ return { t, y };
57
+ }
58
+
59
+ export function systemEuler(dydt, y0, t0, tf, h) {
60
+ if (typeof dydt !== 'function') {
61
+ throw new TypeError('dydt must be function');
62
+ }
63
+ if (!Array.isArray(y0)) {
64
+ throw new TypeError('y0 must be array');
65
+ }
66
+ if (typeof t0 !== 'number' || typeof tf !== 'number') {
67
+ throw new TypeError('t0, tf must be numbers');
68
+ }
69
+ if (typeof h !== 'number' || h <= 0) {
70
+ throw new RangeError('step size h must be positive');
71
+ }
72
+
73
+ const t = [];
74
+ const y = [];
75
+ let tCurrent = t0;
76
+ let yCurrent = [...y0];
77
+
78
+ while (tCurrent <= tf) {
79
+ t.push(tCurrent);
80
+ y.push([...yCurrent]);
81
+
82
+ const dydt_val = dydt(tCurrent, yCurrent);
83
+ for (let i = 0; i < yCurrent.length; i++) {
84
+ yCurrent[i] += h * dydt_val[i];
85
+ }
86
+ tCurrent += h;
87
+ }
88
+
89
+ return { t, y };
90
+ }
91
+
92
+ export function systemRK4(dydt, y0, t0, tf, h) {
93
+ if (typeof dydt !== 'function') {
94
+ throw new TypeError('dydt must be function');
95
+ }
96
+ if (!Array.isArray(y0)) {
97
+ throw new TypeError('y0 must be array');
98
+ }
99
+ if (typeof t0 !== 'number' || typeof tf !== 'number') {
100
+ throw new TypeError('t0, tf must be numbers');
101
+ }
102
+ if (typeof h !== 'number' || h <= 0) {
103
+ throw new RangeError('step size h must be positive');
104
+ }
105
+
106
+ const t = [];
107
+ const y = [];
108
+ let tCurrent = t0;
109
+ let yCurrent = [...y0];
110
+
111
+ while (tCurrent <= tf) {
112
+ t.push(tCurrent);
113
+ y.push([...yCurrent]);
114
+
115
+ const k1 = dydt(tCurrent, yCurrent);
116
+ const y_k2 = yCurrent.map((yi, i) => yi + (h / 2) * k1[i]);
117
+ const k2 = dydt(tCurrent + h / 2, y_k2);
118
+ const y_k3 = yCurrent.map((yi, i) => yi + (h / 2) * k2[i]);
119
+ const k3 = dydt(tCurrent + h / 2, y_k3);
120
+ const y_k4 = yCurrent.map((yi, i) => yi + h * k3[i]);
121
+ const k4 = dydt(tCurrent + h, y_k4);
122
+
123
+ for (let i = 0; i < yCurrent.length; i++) {
124
+ yCurrent[i] += (h / 6) * (k1[i] + 2 * k2[i] + 2 * k3[i] + k4[i]);
125
+ }
126
+ tCurrent += h;
127
+ }
128
+
129
+ return { t, y };
130
+ }
131
+
132
+ export function rk45Adaptive(dydt, y0, t0, tf, tol = 1e-6, hmax = (tf - t0) / 10, hmin = 1e-8) {
133
+ if (typeof dydt !== 'function') {
134
+ throw new TypeError('dydt must be function');
135
+ }
136
+ if (typeof y0 !== 'number') {
137
+ throw new TypeError('y0 must be number');
138
+ }
139
+
140
+ const t = [t0];
141
+ const y = [y0];
142
+ let tCurrent = t0;
143
+ let yCurrent = y0;
144
+ let h = hmax;
145
+
146
+ while (tCurrent < tf) {
147
+ if (tCurrent + h > tf) {
148
+ h = tf - tCurrent;
149
+ }
150
+
151
+ const k1 = dydt(tCurrent, yCurrent);
152
+ const k2 = dydt(tCurrent + h / 4, yCurrent + (h / 4) * k1);
153
+ const k3 = dydt(tCurrent + 3 * h / 8, yCurrent + (3 * h / 32) * k1 + (9 * h / 32) * k2);
154
+ const k4 = dydt(tCurrent + 12 * h / 13, yCurrent + (1932 * h / 2197) * k1 - (7200 * h / 2197) * k2 + (7296 * h / 2197) * k3);
155
+ const k5 = dydt(tCurrent + h, yCurrent + (439 * h / 216) * k1 - 8 * h * k2 + (3680 * h / 513) * k3 - (845 * h / 4104) * k4);
156
+ const k6 = dydt(tCurrent + h / 2, yCurrent - (8 * h / 27) * k1 + 2 * h * k2 - (3544 * h / 2565) * k3 + (1859 * h / 4104) * k4 - (11 * h / 40) * k5);
157
+
158
+ const y4 = yCurrent + (25 * h / 216) * k1 + (1408 * h / 2565) * k3 + (2197 * h / 4104) * k4 - (h / 5) * k5;
159
+ const y5 = yCurrent + (16 * h / 135) * k1 + (6656 * h / 12825) * k3 + (28561 * h / 56430) * k4 - (9 * h / 50) * k5 + (2 * h / 55) * k6;
160
+
161
+ const error = Math.abs(y5 - y4);
162
+ const hNew = h * Math.pow(tol / error, 0.2);
163
+
164
+ if (error <= tol) {
165
+ tCurrent += h;
166
+ yCurrent = y5;
167
+ t.push(tCurrent);
168
+ y.push(yCurrent);
169
+ h = Math.min(Math.max(hNew, hmin), hmax);
170
+ } else {
171
+ h = Math.max(hNew, hmin);
172
+ }
173
+ }
174
+
175
+ return { t, y };
176
+ }