csaps-js 0.1.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/LICENSE +25 -0
- package/README.md +192 -0
- package/dist/index.cjs +665 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +166 -0
- package/dist/index.d.ts +166 -0
- package/dist/index.global.js +670 -0
- package/dist/index.global.js.map +1 -0
- package/dist/index.js +660 -0
- package/dist/index.js.map +1 -0
- package/package.json +70 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,665 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/banded.ts
|
|
4
|
+
function ldltBandSolve(n, bw, lower, B, nrhs) {
|
|
5
|
+
const stride = bw + 1;
|
|
6
|
+
const L = new Float64Array(n * stride);
|
|
7
|
+
const d = new Float64Array(n);
|
|
8
|
+
for (let i = 0; i < n; i++) {
|
|
9
|
+
const oMax = Math.min(bw, i);
|
|
10
|
+
for (let o = oMax; o >= 1; o--) {
|
|
11
|
+
const j = i - o;
|
|
12
|
+
let s = lower[i * stride + o];
|
|
13
|
+
const kStart = i - bw < 0 ? 0 : i - bw;
|
|
14
|
+
for (let k = kStart; k < j; k++) {
|
|
15
|
+
const oi = i - k;
|
|
16
|
+
const oj = j - k;
|
|
17
|
+
if (oi <= bw && oj <= bw) {
|
|
18
|
+
s -= L[i * stride + oi] * L[j * stride + oj] * d[k];
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
L[i * stride + o] = s / d[j];
|
|
22
|
+
}
|
|
23
|
+
let di = lower[i * stride];
|
|
24
|
+
for (let o = 1; o <= oMax; o++) {
|
|
25
|
+
const lio = L[i * stride + o];
|
|
26
|
+
di -= lio * lio * d[i - o];
|
|
27
|
+
}
|
|
28
|
+
if (di === 0 || !Number.isFinite(di)) {
|
|
29
|
+
throw new Error("Banded LDL\u1D40 solve failed: matrix is singular or ill-conditioned.");
|
|
30
|
+
}
|
|
31
|
+
d[i] = di;
|
|
32
|
+
}
|
|
33
|
+
const X = new Float64Array(n * nrhs);
|
|
34
|
+
X.set(B);
|
|
35
|
+
for (let i = 0; i < n; i++) {
|
|
36
|
+
const oMax = Math.min(bw, i);
|
|
37
|
+
for (let c = 0; c < nrhs; c++) {
|
|
38
|
+
let v = X[i * nrhs + c];
|
|
39
|
+
for (let o = 1; o <= oMax; o++) {
|
|
40
|
+
v -= L[i * stride + o] * X[(i - o) * nrhs + c];
|
|
41
|
+
}
|
|
42
|
+
X[i * nrhs + c] = v;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
for (let i = 0; i < n; i++) {
|
|
46
|
+
const inv = 1 / d[i];
|
|
47
|
+
for (let c = 0; c < nrhs; c++) {
|
|
48
|
+
X[i * nrhs + c] *= inv;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
for (let i = n - 1; i >= 0; i--) {
|
|
52
|
+
const oMaxUp = Math.min(bw, n - 1 - i);
|
|
53
|
+
for (let c = 0; c < nrhs; c++) {
|
|
54
|
+
let v = X[i * nrhs + c];
|
|
55
|
+
for (let o = 1; o <= oMaxUp; o++) {
|
|
56
|
+
v -= L[(i + o) * stride + o] * X[(i + o) * nrhs + c];
|
|
57
|
+
}
|
|
58
|
+
X[i * nrhs + c] = v;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return X;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/ppoly.ts
|
|
65
|
+
var PPoly = class {
|
|
66
|
+
constructor(c, breaks, order, pieces, ndim) {
|
|
67
|
+
this.c = c;
|
|
68
|
+
this.breaks = breaks;
|
|
69
|
+
this.order = order;
|
|
70
|
+
this.pieces = pieces;
|
|
71
|
+
this.ndim = ndim;
|
|
72
|
+
}
|
|
73
|
+
/** Locate the piece index for `xq`, or `-1` when out of bounds and not extrapolating. */
|
|
74
|
+
findPiece(xq, extrapolate) {
|
|
75
|
+
const b = this.breaks;
|
|
76
|
+
const last = this.pieces;
|
|
77
|
+
if (xq < b[0]) return extrapolate ? 0 : -1;
|
|
78
|
+
if (xq > b[last]) return extrapolate ? this.pieces - 1 : -1;
|
|
79
|
+
if (xq >= b[last]) return this.pieces - 1;
|
|
80
|
+
let lo = 0;
|
|
81
|
+
let hi = this.pieces - 1;
|
|
82
|
+
while (lo < hi) {
|
|
83
|
+
const mid = lo + hi + 1 >> 1;
|
|
84
|
+
if (b[mid] <= xq) lo = mid;
|
|
85
|
+
else hi = mid - 1;
|
|
86
|
+
}
|
|
87
|
+
return lo;
|
|
88
|
+
}
|
|
89
|
+
/** Evaluate component `n` at scalar `xq` with derivative order `nu`. */
|
|
90
|
+
evalScalar(n, xq, nu, extrapolate) {
|
|
91
|
+
const i = this.findPiece(xq, extrapolate);
|
|
92
|
+
if (i < 0) return NaN;
|
|
93
|
+
const s = xq - this.breaks[i];
|
|
94
|
+
const order = this.order;
|
|
95
|
+
const pieces = this.pieces;
|
|
96
|
+
const ndim = this.ndim;
|
|
97
|
+
let res = 0;
|
|
98
|
+
for (let k = 0; k < order; k++) {
|
|
99
|
+
const e = order - 1 - k;
|
|
100
|
+
if (e < nu) continue;
|
|
101
|
+
let f = 1;
|
|
102
|
+
for (let t = 0; t < nu; t++) f *= e - t;
|
|
103
|
+
const coef = this.c[(k * pieces + i) * ndim + n];
|
|
104
|
+
res += coef * f * Math.pow(s, e - nu);
|
|
105
|
+
}
|
|
106
|
+
return res;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Evaluate all components at every point in `xs`.
|
|
110
|
+
*
|
|
111
|
+
* @returns Flat row-major array with logical shape `(ndim, xs.length)`:
|
|
112
|
+
* `out[n * xs.length + q]`.
|
|
113
|
+
*/
|
|
114
|
+
evalAll(xs, nu, extrapolate) {
|
|
115
|
+
const L = xs.length;
|
|
116
|
+
const out = new Float64Array(this.ndim * L);
|
|
117
|
+
for (let q = 0; q < L; q++) {
|
|
118
|
+
const xq = xs[q];
|
|
119
|
+
for (let n = 0; n < this.ndim; n++) {
|
|
120
|
+
out[n * L + q] = this.evalScalar(n, xq, nu, extrapolate);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return out;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// src/umv.ts
|
|
128
|
+
function computeSmooth(traceR, traceQtw) {
|
|
129
|
+
return 1 / (1 + traceR / (6 * traceQtw));
|
|
130
|
+
}
|
|
131
|
+
function normalizeSmooth(x, w, smooth) {
|
|
132
|
+
const n = x.length;
|
|
133
|
+
let xmin = x[0];
|
|
134
|
+
let xmax = x[0];
|
|
135
|
+
for (let i = 1; i < n; i++) {
|
|
136
|
+
if (x[i] < xmin) xmin = x[i];
|
|
137
|
+
if (x[i] > xmax) xmax = x[i];
|
|
138
|
+
}
|
|
139
|
+
const span = xmax - xmin;
|
|
140
|
+
let sumDx2 = 0;
|
|
141
|
+
for (let i = 0; i < n - 1; i++) {
|
|
142
|
+
const d = x[i + 1] - x[i];
|
|
143
|
+
sumDx2 += d * d;
|
|
144
|
+
}
|
|
145
|
+
const effX = 1 + span * span / sumDx2;
|
|
146
|
+
let sw = 0;
|
|
147
|
+
let sw2 = 0;
|
|
148
|
+
for (let i = 0; i < n; i++) {
|
|
149
|
+
sw += w[i];
|
|
150
|
+
sw2 += w[i] * w[i];
|
|
151
|
+
}
|
|
152
|
+
const effW = sw * sw / sw2;
|
|
153
|
+
const k = 80 * Math.pow(span, 3) * Math.pow(n, -2) * Math.pow(effX, -0.5) * Math.pow(effW, -0.5);
|
|
154
|
+
const s = smooth == null ? 0.5 : smooth;
|
|
155
|
+
return s / (s + (1 - s) * k);
|
|
156
|
+
}
|
|
157
|
+
function makeSpline(x, y2d, w, smooth, normalizedsmooth) {
|
|
158
|
+
const M = x.length;
|
|
159
|
+
const N = y2d.length;
|
|
160
|
+
const dx = new Float64Array(M - 1);
|
|
161
|
+
for (let i = 0; i < M - 1; i++) {
|
|
162
|
+
dx[i] = x[i + 1] - x[i];
|
|
163
|
+
if (!(dx[i] > 0)) {
|
|
164
|
+
throw new Error("Items of 'xdata' must satisfy x1 < x2 < ... < xN.");
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const dydx = new Array(N);
|
|
168
|
+
for (let n = 0; n < N; n++) {
|
|
169
|
+
const row = new Float64Array(M - 1);
|
|
170
|
+
const yn = y2d[n];
|
|
171
|
+
for (let i = 0; i < M - 1; i++) row[i] = (yn[i + 1] - yn[i]) / dx[i];
|
|
172
|
+
dydx[n] = row;
|
|
173
|
+
}
|
|
174
|
+
if (M === 2) {
|
|
175
|
+
const c2 = new Float64Array(2 * 1 * N);
|
|
176
|
+
for (let n = 0; n < N; n++) {
|
|
177
|
+
c2[0 * N + n] = dydx[n][0];
|
|
178
|
+
c2[1 * N + n] = y2d[n][0];
|
|
179
|
+
}
|
|
180
|
+
return { pp: new PPoly(c2, Float64Array.from(x), 2, 1, N), smooth: 1 };
|
|
181
|
+
}
|
|
182
|
+
const m = M - 2;
|
|
183
|
+
const dr = new Float64Array(M - 1);
|
|
184
|
+
for (let i = 0; i < M - 1; i++) dr[i] = 1 / dx[i];
|
|
185
|
+
const iw = new Float64Array(M);
|
|
186
|
+
for (let i = 0; i < M; i++) iw[i] = 1 / w[i];
|
|
187
|
+
const qd = new Float64Array(m);
|
|
188
|
+
for (let i = 0; i < m; i++) {
|
|
189
|
+
const a = dr[i];
|
|
190
|
+
const b = dr[i] + dr[i + 1];
|
|
191
|
+
const cc = dr[i + 1];
|
|
192
|
+
qd[i] = a * a * iw[i] + b * b * iw[i + 1] + cc * cc * iw[i + 2];
|
|
193
|
+
}
|
|
194
|
+
const q1 = new Float64Array(Math.max(0, m - 1));
|
|
195
|
+
for (let i = 0; i < m - 1; i++) {
|
|
196
|
+
q1[i] = -dr[i + 1] * (dr[i] + dr[i + 1]) * iw[i + 1] - dr[i + 1] * (dr[i + 1] + dr[i + 2]) * iw[i + 2];
|
|
197
|
+
}
|
|
198
|
+
const q2 = new Float64Array(Math.max(0, m - 2));
|
|
199
|
+
for (let i = 0; i < m - 2; i++) {
|
|
200
|
+
q2[i] = dr[i + 1] * dr[i + 2] * iw[i + 2];
|
|
201
|
+
}
|
|
202
|
+
let p;
|
|
203
|
+
if (normalizedsmooth) {
|
|
204
|
+
p = normalizeSmooth(x, w, smooth);
|
|
205
|
+
} else if (smooth == null) {
|
|
206
|
+
let traceR = 0;
|
|
207
|
+
let traceQ = 0;
|
|
208
|
+
for (let i = 0; i < m; i++) {
|
|
209
|
+
traceR += 2 * (dx[i] + dx[i + 1]);
|
|
210
|
+
traceQ += qd[i];
|
|
211
|
+
}
|
|
212
|
+
p = computeSmooth(traceR, traceQ);
|
|
213
|
+
} else {
|
|
214
|
+
p = smooth;
|
|
215
|
+
}
|
|
216
|
+
const pp6 = 6 * (1 - p);
|
|
217
|
+
const lower = new Float64Array(m * 3);
|
|
218
|
+
for (let i = 0; i < m; i++) {
|
|
219
|
+
const rii = 2 * (dx[i] + dx[i + 1]);
|
|
220
|
+
lower[i * 3] = pp6 * qd[i] + p * rii;
|
|
221
|
+
if (i >= 1) lower[i * 3 + 1] = pp6 * q1[i - 1] + p * dx[i];
|
|
222
|
+
if (i >= 2) lower[i * 3 + 2] = pp6 * q2[i - 2];
|
|
223
|
+
}
|
|
224
|
+
const B = new Float64Array(m * N);
|
|
225
|
+
for (let i = 0; i < m; i++) {
|
|
226
|
+
for (let n = 0; n < N; n++) B[i * N + n] = dydx[n][i + 1] - dydx[n][i];
|
|
227
|
+
}
|
|
228
|
+
const U = ldltBandSolve(m, 2, lower, B, N);
|
|
229
|
+
const uAt = (r, n) => r === 0 || r === M - 1 ? 0 : U[(r - 1) * N + n];
|
|
230
|
+
const d1 = new Float64Array((M - 1) * N);
|
|
231
|
+
for (let i = 0; i < M - 1; i++) {
|
|
232
|
+
const inv = dr[i];
|
|
233
|
+
for (let n = 0; n < N; n++) d1[i * N + n] = (uAt(i + 1, n) - uAt(i, n)) * inv;
|
|
234
|
+
}
|
|
235
|
+
const d1At = (r, n) => r === 0 || r === M ? 0 : d1[(r - 1) * N + n];
|
|
236
|
+
const d2 = new Float64Array(M * N);
|
|
237
|
+
for (let i = 0; i < M; i++) {
|
|
238
|
+
for (let n = 0; n < N; n++) d2[i * N + n] = d1At(i + 1, n) - d1At(i, n);
|
|
239
|
+
}
|
|
240
|
+
const yi = new Float64Array(M * N);
|
|
241
|
+
for (let i = 0; i < M; i++) {
|
|
242
|
+
const f = pp6 * iw[i];
|
|
243
|
+
for (let n = 0; n < N; n++) yi[i * N + n] = y2d[n][i] - f * d2[i * N + n];
|
|
244
|
+
}
|
|
245
|
+
const puAt = (r, n) => r === 0 || r === M - 1 ? 0 : p * U[(r - 1) * N + n];
|
|
246
|
+
const pieces = M - 1;
|
|
247
|
+
const c = new Float64Array(4 * pieces * N);
|
|
248
|
+
for (let i = 0; i < pieces; i++) {
|
|
249
|
+
const h = dx[i];
|
|
250
|
+
const invh = dr[i];
|
|
251
|
+
for (let n = 0; n < N; n++) {
|
|
252
|
+
const pu0 = puAt(i, n);
|
|
253
|
+
const pu1 = puAt(i + 1, n);
|
|
254
|
+
const yi0 = yi[i * N + n];
|
|
255
|
+
const yi1 = yi[(i + 1) * N + n];
|
|
256
|
+
const c1 = (pu1 - pu0) * invh;
|
|
257
|
+
const c2 = 3 * pu0;
|
|
258
|
+
const c3 = (yi1 - yi0) * invh - h * (2 * pu0 + pu1);
|
|
259
|
+
const c4 = yi0;
|
|
260
|
+
c[(0 * pieces + i) * N + n] = c1;
|
|
261
|
+
c[(1 * pieces + i) * N + n] = c2;
|
|
262
|
+
c[(2 * pieces + i) * N + n] = c3;
|
|
263
|
+
c[(3 * pieces + i) * N + n] = c4;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return { pp: new PPoly(c, Float64Array.from(x), 4, pieces, N), smooth: p };
|
|
267
|
+
}
|
|
268
|
+
function toFloat64(a) {
|
|
269
|
+
return a instanceof Float64Array ? a : Float64Array.from(a);
|
|
270
|
+
}
|
|
271
|
+
function prepareUnivariate(xdata, ydata, weights, axis) {
|
|
272
|
+
const x = toFloat64(xdata);
|
|
273
|
+
const M = x.length;
|
|
274
|
+
if (x.length < 2) throw new Error("'xdata' must contain at least 2 data points.");
|
|
275
|
+
let y2d;
|
|
276
|
+
let isVector;
|
|
277
|
+
let transposed = false;
|
|
278
|
+
const first = ydata[0];
|
|
279
|
+
if (Array.isArray(first) || first instanceof Float64Array) {
|
|
280
|
+
const y = ydata;
|
|
281
|
+
const R = y.length;
|
|
282
|
+
const C = y[0].length;
|
|
283
|
+
const ndimY = 2;
|
|
284
|
+
const ax = axis < 0 ? ndimY + axis : axis;
|
|
285
|
+
if (ax === 1) {
|
|
286
|
+
if (C !== M) throw new Error(`'ydata' shape[${ax}] (${C}) must equal 'xdata' size (${M}).`);
|
|
287
|
+
y2d = y.map((row) => Array.from(row));
|
|
288
|
+
} else if (ax === 0) {
|
|
289
|
+
if (R !== M) throw new Error(`'ydata' shape[${ax}] (${R}) must equal 'xdata' size (${M}).`);
|
|
290
|
+
transposed = true;
|
|
291
|
+
y2d = new Array(C);
|
|
292
|
+
for (let n = 0; n < C; n++) {
|
|
293
|
+
const row = new Array(M);
|
|
294
|
+
for (let i = 0; i < M; i++) row[i] = y[i][n];
|
|
295
|
+
y2d[n] = row;
|
|
296
|
+
}
|
|
297
|
+
} else {
|
|
298
|
+
throw new Error(`Unsupported axis ${axis} for 2-D ydata.`);
|
|
299
|
+
}
|
|
300
|
+
isVector = false;
|
|
301
|
+
} else {
|
|
302
|
+
const y = Array.from(ydata);
|
|
303
|
+
if (y.length !== M) throw new Error(`'ydata' size (${y.length}) must equal 'xdata' size (${M}).`);
|
|
304
|
+
y2d = [y];
|
|
305
|
+
isVector = true;
|
|
306
|
+
}
|
|
307
|
+
let w;
|
|
308
|
+
if (weights == null) {
|
|
309
|
+
w = new Float64Array(M).fill(1);
|
|
310
|
+
} else {
|
|
311
|
+
w = toFloat64(weights);
|
|
312
|
+
if (w.length !== M) throw new Error("Weights vector size must equal xdata size.");
|
|
313
|
+
}
|
|
314
|
+
const N = y2d.length;
|
|
315
|
+
const restore = (flat, L) => {
|
|
316
|
+
if (isVector) {
|
|
317
|
+
const out2 = new Array(L);
|
|
318
|
+
for (let q = 0; q < L; q++) out2[q] = flat[q];
|
|
319
|
+
return out2;
|
|
320
|
+
}
|
|
321
|
+
if (transposed) {
|
|
322
|
+
const out2 = new Array(L);
|
|
323
|
+
for (let q = 0; q < L; q++) {
|
|
324
|
+
const row = new Array(N);
|
|
325
|
+
for (let n = 0; n < N; n++) row[n] = flat[n * L + q];
|
|
326
|
+
out2[q] = row;
|
|
327
|
+
}
|
|
328
|
+
return out2;
|
|
329
|
+
}
|
|
330
|
+
const out = new Array(N);
|
|
331
|
+
for (let n = 0; n < N; n++) {
|
|
332
|
+
const row = new Array(L);
|
|
333
|
+
for (let q = 0; q < L; q++) row[q] = flat[n * L + q];
|
|
334
|
+
out[n] = row;
|
|
335
|
+
}
|
|
336
|
+
return out;
|
|
337
|
+
};
|
|
338
|
+
return { x, y2d, w, restore };
|
|
339
|
+
}
|
|
340
|
+
var CubicSmoothingSpline = class {
|
|
341
|
+
constructor(xdata, ydata, options = {}) {
|
|
342
|
+
const { weights, smooth = null, axis = -1, normalizedsmooth = false } = options;
|
|
343
|
+
const prep = prepareUnivariate(xdata, ydata, weights, axis);
|
|
344
|
+
const res = makeSpline(prep.x, prep.y2d, prep.w, smooth ?? null, normalizedsmooth);
|
|
345
|
+
this.pp = res.pp;
|
|
346
|
+
this.smooth = res.smooth;
|
|
347
|
+
this.restore = prep.restore;
|
|
348
|
+
}
|
|
349
|
+
/** Evaluate the spline (or one of its derivatives) at the sites `xi`. */
|
|
350
|
+
evaluate(xi, options = {}) {
|
|
351
|
+
const { nu = 0, extrapolate = true } = options;
|
|
352
|
+
const xs = toFloat64(xi);
|
|
353
|
+
const flat = this.pp.evalAll(xs, nu, extrapolate);
|
|
354
|
+
return this.restore(flat, xs.length);
|
|
355
|
+
}
|
|
356
|
+
/** Breakpoints (data sites) of the piecewise polynomial. */
|
|
357
|
+
get breaks() {
|
|
358
|
+
return Array.from(this.pp.breaks);
|
|
359
|
+
}
|
|
360
|
+
/** Spline description: breakpoints, raw coefficients and shape metadata. */
|
|
361
|
+
get spline() {
|
|
362
|
+
return {
|
|
363
|
+
breaks: Array.from(this.pp.breaks),
|
|
364
|
+
coeffs: this.pp.c,
|
|
365
|
+
order: this.pp.order,
|
|
366
|
+
pieces: this.pp.pieces,
|
|
367
|
+
ndim: this.pp.ndim
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
/** Internal piecewise polynomial (used by the N-D grid spline). */
|
|
371
|
+
get _pp() {
|
|
372
|
+
return this.pp;
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
// src/ndarray.ts
|
|
377
|
+
var NdArray = class _NdArray {
|
|
378
|
+
constructor(data, shape) {
|
|
379
|
+
this.data = data;
|
|
380
|
+
this.shape = shape;
|
|
381
|
+
}
|
|
382
|
+
get size() {
|
|
383
|
+
return this.data.length;
|
|
384
|
+
}
|
|
385
|
+
/** Reinterpret the buffer with a new shape (must keep the same element count). */
|
|
386
|
+
reshape(shape) {
|
|
387
|
+
return new _NdArray(this.data, shape.slice());
|
|
388
|
+
}
|
|
389
|
+
/** Return a new array with axes permuted according to `perm`, materialized C-contiguous. */
|
|
390
|
+
transpose(perm) {
|
|
391
|
+
const d = this.shape.length;
|
|
392
|
+
const newShape = perm.map((axis) => this.shape[axis]);
|
|
393
|
+
const oldStrides = cStrides(this.shape);
|
|
394
|
+
const permutedStrides = perm.map((axis) => oldStrides[axis]);
|
|
395
|
+
const total = this.data.length;
|
|
396
|
+
const out = new Float64Array(total);
|
|
397
|
+
const idx = new Array(d).fill(0);
|
|
398
|
+
for (let pos = 0; pos < total; pos++) {
|
|
399
|
+
let off = 0;
|
|
400
|
+
for (let a = 0; a < d; a++) off += idx[a] * permutedStrides[a];
|
|
401
|
+
out[pos] = this.data[off];
|
|
402
|
+
for (let a = d - 1; a >= 0; a--) {
|
|
403
|
+
if (++idx[a] < newShape[a]) break;
|
|
404
|
+
idx[a] = 0;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return new _NdArray(out, newShape);
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
function cStrides(shape) {
|
|
411
|
+
const d = shape.length;
|
|
412
|
+
const strides = new Array(d).fill(1);
|
|
413
|
+
for (let a = d - 2; a >= 0; a--) strides[a] = strides[a + 1] * shape[a + 1];
|
|
414
|
+
return strides;
|
|
415
|
+
}
|
|
416
|
+
function prod(xs) {
|
|
417
|
+
let p = 1;
|
|
418
|
+
for (const x of xs) p *= x;
|
|
419
|
+
return p;
|
|
420
|
+
}
|
|
421
|
+
function flattenNested(y) {
|
|
422
|
+
const shape = [];
|
|
423
|
+
let node = y;
|
|
424
|
+
while (Array.isArray(node)) {
|
|
425
|
+
shape.push(node.length);
|
|
426
|
+
node = node[0];
|
|
427
|
+
}
|
|
428
|
+
const data = new Float64Array(prod(shape));
|
|
429
|
+
let k = 0;
|
|
430
|
+
const fill = (n, depth) => {
|
|
431
|
+
if (depth === shape.length) {
|
|
432
|
+
data[k++] = n;
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
const a = n;
|
|
436
|
+
if (a.length !== shape[depth]) {
|
|
437
|
+
throw new Error("flattenNested: input array is ragged (non-rectangular).");
|
|
438
|
+
}
|
|
439
|
+
for (let i = 0; i < a.length; i++) fill(a[i], depth + 1);
|
|
440
|
+
};
|
|
441
|
+
fill(y, 0);
|
|
442
|
+
return { data, shape };
|
|
443
|
+
}
|
|
444
|
+
function nestArray(data, shape) {
|
|
445
|
+
const strides = cStrides(shape);
|
|
446
|
+
const build = (depth, offset) => {
|
|
447
|
+
if (depth === shape.length - 1) {
|
|
448
|
+
const row = new Array(shape[depth]);
|
|
449
|
+
for (let i = 0; i < shape[depth]; i++) row[i] = data[offset + i];
|
|
450
|
+
return row;
|
|
451
|
+
}
|
|
452
|
+
const arr = new Array(shape[depth]);
|
|
453
|
+
for (let i = 0; i < shape[depth]; i++) arr[i] = build(depth + 1, offset + i * strides[depth]);
|
|
454
|
+
return arr;
|
|
455
|
+
};
|
|
456
|
+
return build(0, 0);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// src/ndg.ts
|
|
460
|
+
function toFloat642(a) {
|
|
461
|
+
return a instanceof Float64Array ? a : Float64Array.from(a);
|
|
462
|
+
}
|
|
463
|
+
function prepareVectors(data, name, minSize = 2) {
|
|
464
|
+
if (!Array.isArray(data)) throw new TypeError(`'${name}' must be a sequence of 1-D vectors.`);
|
|
465
|
+
return data.map((d, axis) => {
|
|
466
|
+
const v = toFloat642(d);
|
|
467
|
+
if (v.length < minSize) {
|
|
468
|
+
throw new Error(`'${name}' must contain at least ${minSize} points for axis ${axis}.`);
|
|
469
|
+
}
|
|
470
|
+
return v;
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
function buildGrid(xs, yNd, weights, smooth, normalizedsmooth) {
|
|
474
|
+
const d = xs.length;
|
|
475
|
+
const orders = new Array(d);
|
|
476
|
+
const pieces = new Array(d);
|
|
477
|
+
const smooths = new Array(d);
|
|
478
|
+
const permute = [d - 1];
|
|
479
|
+
for (let a = 0; a < d - 1; a++) permute.push(a);
|
|
480
|
+
let coeffs = yNd;
|
|
481
|
+
let shape = yNd.shape.slice();
|
|
482
|
+
for (let i = d - 1; i >= 0; i--) {
|
|
483
|
+
const lastSize = shape[shape.length - 1];
|
|
484
|
+
const total = coeffs.data.length;
|
|
485
|
+
const Nrest = total / lastSize;
|
|
486
|
+
const y2d = new Array(Nrest);
|
|
487
|
+
for (let n = 0; n < Nrest; n++) {
|
|
488
|
+
const row = new Array(lastSize);
|
|
489
|
+
const base = n * lastSize;
|
|
490
|
+
for (let col = 0; col < lastSize; col++) row[col] = coeffs.data[base + col];
|
|
491
|
+
y2d[n] = row;
|
|
492
|
+
}
|
|
493
|
+
const w = weights[i] ?? new Float64Array(lastSize).fill(1);
|
|
494
|
+
const res = makeSpline(xs[i], y2d, w, smooth[i], normalizedsmooth);
|
|
495
|
+
const order = res.pp.order;
|
|
496
|
+
const pcs = res.pp.pieces;
|
|
497
|
+
orders[i] = order;
|
|
498
|
+
pieces[i] = pcs;
|
|
499
|
+
smooths[i] = res.smooth;
|
|
500
|
+
const op = order * pcs;
|
|
501
|
+
const flat = new Float64Array(Nrest * op);
|
|
502
|
+
for (let k = 0; k < order; k++) {
|
|
503
|
+
for (let ii = 0; ii < pcs; ii++) {
|
|
504
|
+
const j = k * pcs + ii;
|
|
505
|
+
const cBase = j * Nrest;
|
|
506
|
+
for (let n = 0; n < Nrest; n++) flat[n * op + j] = res.pp.c[cBase + n];
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
const newShape = shape.slice(0, -1);
|
|
510
|
+
newShape.push(op);
|
|
511
|
+
coeffs = new NdArray(flat, newShape).transpose(permute);
|
|
512
|
+
shape = coeffs.shape.slice();
|
|
513
|
+
}
|
|
514
|
+
return { F: coeffs, breaks: xs, orders, pieces, smooths };
|
|
515
|
+
}
|
|
516
|
+
function evalGrid(built, xiList, nu, extrapolate) {
|
|
517
|
+
const { F, breaks, orders, pieces } = built;
|
|
518
|
+
const d = breaks.length;
|
|
519
|
+
const permute = [d - 1];
|
|
520
|
+
for (let a = 0; a < d - 1; a++) permute.push(a);
|
|
521
|
+
let coeffs = F;
|
|
522
|
+
let shape = F.shape.slice();
|
|
523
|
+
for (let i = d - 1; i >= 0; i--) {
|
|
524
|
+
const order = orders[i];
|
|
525
|
+
const pcs = pieces[i];
|
|
526
|
+
const lastSize = shape[shape.length - 1];
|
|
527
|
+
const total = coeffs.data.length;
|
|
528
|
+
const Nrest = total / lastSize;
|
|
529
|
+
const cc = new Float64Array(order * pcs * Nrest);
|
|
530
|
+
for (let k = 0; k < order; k++) {
|
|
531
|
+
for (let ii = 0; ii < pcs; ii++) {
|
|
532
|
+
const j = k * pcs + ii;
|
|
533
|
+
const dst = (k * pcs + ii) * Nrest;
|
|
534
|
+
for (let n = 0; n < Nrest; n++) cc[dst + n] = coeffs.data[n * lastSize + j];
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
const pp = new PPoly(cc, breaks[i], order, pcs, Nrest);
|
|
538
|
+
const xi = xiList[i];
|
|
539
|
+
const Li = xi.length;
|
|
540
|
+
const out = pp.evalAll(xi, nu[i] ?? 0, extrapolate);
|
|
541
|
+
const newShape = shape.slice(0, -1);
|
|
542
|
+
newShape.push(Li);
|
|
543
|
+
coeffs = new NdArray(out, newShape).transpose(permute);
|
|
544
|
+
shape = coeffs.shape.slice();
|
|
545
|
+
}
|
|
546
|
+
return coeffs;
|
|
547
|
+
}
|
|
548
|
+
function normalizeSmooth2(smooth, d) {
|
|
549
|
+
if (smooth == null) return new Array(d).fill(null);
|
|
550
|
+
if (Array.isArray(smooth)) {
|
|
551
|
+
if (smooth.length !== d) {
|
|
552
|
+
throw new Error(`Number of smoothing values (${smooth.length}) must equal dimensions (${d}).`);
|
|
553
|
+
}
|
|
554
|
+
return smooth.map((s) => s == null ? null : s);
|
|
555
|
+
}
|
|
556
|
+
return new Array(d).fill(smooth);
|
|
557
|
+
}
|
|
558
|
+
function normalizeWeights(weights, d, sizes) {
|
|
559
|
+
if (weights == null) return new Array(d).fill(void 0);
|
|
560
|
+
const w = weights;
|
|
561
|
+
const firstIsVector = Array.isArray(w[0]) || w[0] instanceof Float64Array;
|
|
562
|
+
let list;
|
|
563
|
+
if (!firstIsVector) {
|
|
564
|
+
if (d !== 1) throw new Error("'weights' must be a sequence of vectors, one per axis.");
|
|
565
|
+
list = [weights];
|
|
566
|
+
} else {
|
|
567
|
+
list = weights;
|
|
568
|
+
}
|
|
569
|
+
if (list.length !== d) throw new Error(`'weights' dimensions (${list.length}) must equal 'xdata' dimensions (${d}).`);
|
|
570
|
+
return list.map((wv, axis) => {
|
|
571
|
+
const v = toFloat642(wv);
|
|
572
|
+
if (v.length !== sizes[axis]) {
|
|
573
|
+
throw new Error(`'weights' size (${v.length}) must equal 'xdata' size (${sizes[axis]}) for axis ${axis}.`);
|
|
574
|
+
}
|
|
575
|
+
return v;
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
var NdGridCubicSmoothingSpline = class {
|
|
579
|
+
constructor(xdata, ydata, options = {}) {
|
|
580
|
+
const xs = prepareVectors(xdata, "xdata");
|
|
581
|
+
const d = xs.length;
|
|
582
|
+
const sizes = xs.map((v) => v.length);
|
|
583
|
+
const { data, shape } = flattenNested(ydata);
|
|
584
|
+
if (shape.length !== d) {
|
|
585
|
+
throw new Error(`'ydata' must have ${d} dimensions according to 'xdata' (got ${shape.length}).`);
|
|
586
|
+
}
|
|
587
|
+
for (let a = 0; a < d; a++) {
|
|
588
|
+
if (shape[a] !== sizes[a]) {
|
|
589
|
+
throw new Error(`'ydata' size (${shape[a]}) and 'xdata' size (${sizes[a]}) mismatch for axis ${a}.`);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
const smooth = normalizeSmooth2(options.smooth, d);
|
|
593
|
+
const weights = normalizeWeights(options.weights, d, sizes);
|
|
594
|
+
this.built = buildGrid(xs, new NdArray(data, shape), weights, smooth, options.normalizedsmooth ?? false);
|
|
595
|
+
this.smooth = this.built.smooths;
|
|
596
|
+
}
|
|
597
|
+
/** Evaluate the spline on the grid defined by the sequence of site vectors `xi`. */
|
|
598
|
+
evaluate(xi, options = {}) {
|
|
599
|
+
const d = this.built.breaks.length;
|
|
600
|
+
const xiList = prepareVectors(xi, "xi", 1);
|
|
601
|
+
if (xiList.length !== d) throw new Error(`'xi' must have length ${d} according to the grid.`);
|
|
602
|
+
const nu = options.nu ?? new Array(d).fill(0);
|
|
603
|
+
const extrapolate = options.extrapolate ?? true;
|
|
604
|
+
const result = evalGrid(this.built, xiList, nu, extrapolate);
|
|
605
|
+
return nestArray(result.data, result.shape);
|
|
606
|
+
}
|
|
607
|
+
/** Spline description: per-axis breakpoints, raw coefficient tensor and shape metadata. */
|
|
608
|
+
get spline() {
|
|
609
|
+
return {
|
|
610
|
+
breaks: this.built.breaks.map((b) => Array.from(b)),
|
|
611
|
+
coeffs: this.built.F.data,
|
|
612
|
+
coeffsShape: this.built.F.shape.slice(),
|
|
613
|
+
order: this.built.orders.slice(),
|
|
614
|
+
pieces: this.built.pieces.slice()
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
// src/csaps.ts
|
|
620
|
+
function isVectorList(x) {
|
|
621
|
+
return Array.isArray(x) && x.length > 0 && (Array.isArray(x[0]) || x[0] instanceof Float64Array);
|
|
622
|
+
}
|
|
623
|
+
function isArrayLike(x) {
|
|
624
|
+
return Array.isArray(x) || x instanceof Float64Array;
|
|
625
|
+
}
|
|
626
|
+
function csaps(xdata, ydata, arg3, arg4) {
|
|
627
|
+
let xidata;
|
|
628
|
+
let options;
|
|
629
|
+
if (arg3 === void 0) {
|
|
630
|
+
xidata = void 0;
|
|
631
|
+
options = arg4 ?? {};
|
|
632
|
+
} else if (isArrayLike(arg3)) {
|
|
633
|
+
xidata = arg3;
|
|
634
|
+
options = arg4 ?? {};
|
|
635
|
+
} else {
|
|
636
|
+
xidata = void 0;
|
|
637
|
+
options = arg3 ?? {};
|
|
638
|
+
}
|
|
639
|
+
const grid = isVectorList(xdata);
|
|
640
|
+
const spline = grid ? new NdGridCubicSmoothingSpline(xdata, ydata, {
|
|
641
|
+
weights: options.weights,
|
|
642
|
+
smooth: options.smooth,
|
|
643
|
+
normalizedsmooth: options.normalizedsmooth
|
|
644
|
+
}) : new CubicSmoothingSpline(xdata, ydata, {
|
|
645
|
+
weights: options.weights,
|
|
646
|
+
smooth: typeof options.smooth === "number" ? options.smooth : options.smooth ?? null,
|
|
647
|
+
axis: options.axis,
|
|
648
|
+
normalizedsmooth: options.normalizedsmooth
|
|
649
|
+
});
|
|
650
|
+
if (xidata === void 0) return spline;
|
|
651
|
+
const values = grid ? spline.evaluate(xidata) : spline.evaluate(xidata);
|
|
652
|
+
const sm = options.smooth;
|
|
653
|
+
const autoSmooth = Array.isArray(sm) ? sm.some((s) => s == null) : sm == null;
|
|
654
|
+
if (autoSmooth) {
|
|
655
|
+
return { values, smooth: spline.smooth };
|
|
656
|
+
}
|
|
657
|
+
return values;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
exports.CubicSmoothingSpline = CubicSmoothingSpline;
|
|
661
|
+
exports.NdGridCubicSmoothingSpline = NdGridCubicSmoothingSpline;
|
|
662
|
+
exports.PPoly = PPoly;
|
|
663
|
+
exports.csaps = csaps;
|
|
664
|
+
//# sourceMappingURL=index.cjs.map
|
|
665
|
+
//# sourceMappingURL=index.cjs.map
|