csaps-js 0.1.0 → 0.1.1
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/dist/index.cjs +127 -80
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -3
- package/dist/index.d.ts +6 -3
- package/dist/index.global.js +127 -80
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +127 -80
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -84,43 +84,70 @@ var PPoly = class {
|
|
|
84
84
|
}
|
|
85
85
|
return lo;
|
|
86
86
|
}
|
|
87
|
-
/** Evaluate component `n` at scalar `xq` with derivative order `nu`. */
|
|
88
|
-
evalScalar(n, xq, nu, extrapolate) {
|
|
89
|
-
const i = this.findPiece(xq, extrapolate);
|
|
90
|
-
if (i < 0) return NaN;
|
|
91
|
-
const s = xq - this.breaks[i];
|
|
92
|
-
const order = this.order;
|
|
93
|
-
const pieces = this.pieces;
|
|
94
|
-
const ndim = this.ndim;
|
|
95
|
-
let res = 0;
|
|
96
|
-
for (let k = 0; k < order; k++) {
|
|
97
|
-
const e = order - 1 - k;
|
|
98
|
-
if (e < nu) continue;
|
|
99
|
-
let f = 1;
|
|
100
|
-
for (let t = 0; t < nu; t++) f *= e - t;
|
|
101
|
-
const coef = this.c[(k * pieces + i) * ndim + n];
|
|
102
|
-
res += coef * f * Math.pow(s, e - nu);
|
|
103
|
-
}
|
|
104
|
-
return res;
|
|
105
|
-
}
|
|
106
87
|
/**
|
|
107
|
-
* Evaluate all components at every point in `xs
|
|
88
|
+
* Evaluate all components at every point in `xs` (Horner's method).
|
|
89
|
+
*
|
|
90
|
+
* The piece lookup is done once per evaluation point and reused across all
|
|
91
|
+
* components, and the polynomial is evaluated with Horner's scheme rather
|
|
92
|
+
* than `Math.pow` — both matter for multivariate and N-D gridded data where
|
|
93
|
+
* `ndim` is large.
|
|
108
94
|
*
|
|
109
95
|
* @returns Flat row-major array with logical shape `(ndim, xs.length)`:
|
|
110
96
|
* `out[n * xs.length + q]`.
|
|
111
97
|
*/
|
|
112
98
|
evalAll(xs, nu, extrapolate) {
|
|
99
|
+
const { order, pieces, ndim, breaks, c } = this;
|
|
100
|
+
const deg = order - 1;
|
|
113
101
|
const L = xs.length;
|
|
114
|
-
const out = new Float64Array(
|
|
102
|
+
const out = new Float64Array(ndim * L);
|
|
103
|
+
if (nu > deg) return out;
|
|
104
|
+
const effDeg = deg - nu;
|
|
105
|
+
const kstep = pieces * ndim;
|
|
106
|
+
let mult = null;
|
|
107
|
+
if (nu !== 0) {
|
|
108
|
+
mult = new Float64Array(effDeg + 1);
|
|
109
|
+
for (let k = 0; k <= effDeg; k++) mult[k] = fallingFactorial(deg - k, nu);
|
|
110
|
+
}
|
|
115
111
|
for (let q = 0; q < L; q++) {
|
|
116
112
|
const xq = xs[q];
|
|
117
|
-
|
|
118
|
-
|
|
113
|
+
const i = this.findPiece(xq, extrapolate);
|
|
114
|
+
if (i < 0) {
|
|
115
|
+
for (let n = 0; n < ndim; n++) out[n * L + q] = NaN;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const s = xq - breaks[i];
|
|
119
|
+
const start = i * ndim;
|
|
120
|
+
if (nu === 0) {
|
|
121
|
+
for (let n = 0; n < ndim; n++) {
|
|
122
|
+
let off = start + n;
|
|
123
|
+
let res = c[off];
|
|
124
|
+
for (let k = 1; k <= deg; k++) {
|
|
125
|
+
off += kstep;
|
|
126
|
+
res = res * s + c[off];
|
|
127
|
+
}
|
|
128
|
+
out[n * L + q] = res;
|
|
129
|
+
}
|
|
130
|
+
} else {
|
|
131
|
+
const m = mult;
|
|
132
|
+
for (let n = 0; n < ndim; n++) {
|
|
133
|
+
let off = start + n;
|
|
134
|
+
let res = c[off] * m[0];
|
|
135
|
+
for (let k = 1; k <= effDeg; k++) {
|
|
136
|
+
off += kstep;
|
|
137
|
+
res = res * s + c[off] * m[k];
|
|
138
|
+
}
|
|
139
|
+
out[n * L + q] = res;
|
|
140
|
+
}
|
|
119
141
|
}
|
|
120
142
|
}
|
|
121
143
|
return out;
|
|
122
144
|
}
|
|
123
145
|
};
|
|
146
|
+
function fallingFactorial(base, nu) {
|
|
147
|
+
let f = 1;
|
|
148
|
+
for (let t = 0; t < nu; t++) f *= base - t;
|
|
149
|
+
return f;
|
|
150
|
+
}
|
|
124
151
|
|
|
125
152
|
// src/umv.ts
|
|
126
153
|
function computeSmooth(traceR, traceQtw) {
|
|
@@ -152,9 +179,8 @@ function normalizeSmooth(x, w, smooth) {
|
|
|
152
179
|
const s = smooth == null ? 0.5 : smooth;
|
|
153
180
|
return s / (s + (1 - s) * k);
|
|
154
181
|
}
|
|
155
|
-
function makeSpline(x,
|
|
182
|
+
function makeSpline(x, y, N, w, smooth, normalizedsmooth) {
|
|
156
183
|
const M = x.length;
|
|
157
|
-
const N = y2d.length;
|
|
158
184
|
const dx = new Float64Array(M - 1);
|
|
159
185
|
for (let i = 0; i < M - 1; i++) {
|
|
160
186
|
dx[i] = x[i + 1] - x[i];
|
|
@@ -162,18 +188,18 @@ function makeSpline(x, y2d, w, smooth, normalizedsmooth) {
|
|
|
162
188
|
throw new Error("Items of 'xdata' must satisfy x1 < x2 < ... < xN.");
|
|
163
189
|
}
|
|
164
190
|
}
|
|
165
|
-
const
|
|
191
|
+
const md = M - 1;
|
|
192
|
+
const dydx = new Float64Array(N * md);
|
|
166
193
|
for (let n = 0; n < N; n++) {
|
|
167
|
-
const
|
|
168
|
-
const
|
|
169
|
-
for (let i = 0; i <
|
|
170
|
-
dydx[n] = row;
|
|
194
|
+
const yb = n * M;
|
|
195
|
+
const db = n * md;
|
|
196
|
+
for (let i = 0; i < md; i++) dydx[db + i] = (y[yb + i + 1] - y[yb + i]) / dx[i];
|
|
171
197
|
}
|
|
172
198
|
if (M === 2) {
|
|
173
199
|
const c2 = new Float64Array(2 * 1 * N);
|
|
174
200
|
for (let n = 0; n < N; n++) {
|
|
175
|
-
c2[0 * N + n] = dydx[n]
|
|
176
|
-
c2[1 * N + n] =
|
|
201
|
+
c2[0 * N + n] = dydx[n * md];
|
|
202
|
+
c2[1 * N + n] = y[n * M];
|
|
177
203
|
}
|
|
178
204
|
return { pp: new PPoly(c2, Float64Array.from(x), 2, 1, N), smooth: 1 };
|
|
179
205
|
}
|
|
@@ -221,44 +247,63 @@ function makeSpline(x, y2d, w, smooth, normalizedsmooth) {
|
|
|
221
247
|
}
|
|
222
248
|
const B = new Float64Array(m * N);
|
|
223
249
|
for (let i = 0; i < m; i++) {
|
|
224
|
-
for (let n = 0; n < N; n++)
|
|
250
|
+
for (let n = 0; n < N; n++) {
|
|
251
|
+
const db = n * md + i;
|
|
252
|
+
B[i * N + n] = dydx[db + 1] - dydx[db];
|
|
253
|
+
}
|
|
225
254
|
}
|
|
226
255
|
const U = ldltBandSolve(m, 2, lower, B, N);
|
|
227
|
-
const
|
|
256
|
+
const uFull = new Float64Array(M * N);
|
|
257
|
+
for (let i = 1; i < M - 1; i++) {
|
|
258
|
+
const src = (i - 1) * N;
|
|
259
|
+
const dst = i * N;
|
|
260
|
+
for (let n = 0; n < N; n++) uFull[dst + n] = U[src + n];
|
|
261
|
+
}
|
|
228
262
|
const d1 = new Float64Array((M - 1) * N);
|
|
229
263
|
for (let i = 0; i < M - 1; i++) {
|
|
230
264
|
const inv = dr[i];
|
|
231
|
-
|
|
265
|
+
const a = i * N;
|
|
266
|
+
const b = a + N;
|
|
267
|
+
for (let n = 0; n < N; n++) d1[a + n] = (uFull[b + n] - uFull[a + n]) * inv;
|
|
232
268
|
}
|
|
233
|
-
const d1At = (r, n) => r === 0 || r === M ? 0 : d1[(r - 1) * N + n];
|
|
234
269
|
const d2 = new Float64Array(M * N);
|
|
235
|
-
|
|
236
|
-
|
|
270
|
+
const lastD1 = (M - 2) * N;
|
|
271
|
+
const lastD2 = (M - 1) * N;
|
|
272
|
+
for (let n = 0; n < N; n++) {
|
|
273
|
+
d2[n] = d1[n];
|
|
274
|
+
d2[lastD2 + n] = -d1[lastD1 + n];
|
|
275
|
+
}
|
|
276
|
+
for (let i = 1; i < M - 1; i++) {
|
|
277
|
+
const di = i * N;
|
|
278
|
+
const dim1 = di - N;
|
|
279
|
+
for (let n = 0; n < N; n++) d2[di + n] = d1[di + n] - d1[dim1 + n];
|
|
237
280
|
}
|
|
238
281
|
const yi = new Float64Array(M * N);
|
|
239
282
|
for (let i = 0; i < M; i++) {
|
|
240
283
|
const f = pp6 * iw[i];
|
|
241
|
-
|
|
284
|
+
const row = i * N;
|
|
285
|
+
for (let n = 0; n < N; n++) yi[row + n] = y[n * M + i] - f * d2[row + n];
|
|
242
286
|
}
|
|
243
|
-
const puAt = (r, n) => r === 0 || r === M - 1 ? 0 : p * U[(r - 1) * N + n];
|
|
244
287
|
const pieces = M - 1;
|
|
245
288
|
const c = new Float64Array(4 * pieces * N);
|
|
289
|
+
const o1 = pieces * N;
|
|
290
|
+
const o2 = 2 * pieces * N;
|
|
291
|
+
const o3 = 3 * pieces * N;
|
|
246
292
|
for (let i = 0; i < pieces; i++) {
|
|
247
293
|
const h = dx[i];
|
|
248
294
|
const invh = dr[i];
|
|
295
|
+
const ri = i * N;
|
|
296
|
+
const ri1 = ri + N;
|
|
249
297
|
for (let n = 0; n < N; n++) {
|
|
250
|
-
const pu0 =
|
|
251
|
-
const pu1 =
|
|
252
|
-
const yi0 = yi[
|
|
253
|
-
const yi1 = yi[
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
c[
|
|
259
|
-
c[(1 * pieces + i) * N + n] = c2;
|
|
260
|
-
c[(2 * pieces + i) * N + n] = c3;
|
|
261
|
-
c[(3 * pieces + i) * N + n] = c4;
|
|
298
|
+
const pu0 = p * uFull[ri + n];
|
|
299
|
+
const pu1 = p * uFull[ri1 + n];
|
|
300
|
+
const yi0 = yi[ri + n];
|
|
301
|
+
const yi1 = yi[ri1 + n];
|
|
302
|
+
const ci = ri + n;
|
|
303
|
+
c[ci] = (pu1 - pu0) * invh;
|
|
304
|
+
c[o1 + ci] = 3 * pu0;
|
|
305
|
+
c[o2 + ci] = (yi1 - yi0) * invh - h * (2 * pu0 + pu1);
|
|
306
|
+
c[o3 + ci] = yi0;
|
|
262
307
|
}
|
|
263
308
|
}
|
|
264
309
|
return { pp: new PPoly(c, Float64Array.from(x), 4, pieces, N), smooth: p };
|
|
@@ -270,36 +315,44 @@ function prepareUnivariate(xdata, ydata, weights, axis) {
|
|
|
270
315
|
const x = toFloat64(xdata);
|
|
271
316
|
const M = x.length;
|
|
272
317
|
if (x.length < 2) throw new Error("'xdata' must contain at least 2 data points.");
|
|
273
|
-
let
|
|
318
|
+
let y;
|
|
319
|
+
let N;
|
|
274
320
|
let isVector;
|
|
275
321
|
let transposed = false;
|
|
276
322
|
const first = ydata[0];
|
|
277
323
|
if (Array.isArray(first) || first instanceof Float64Array) {
|
|
278
|
-
const
|
|
279
|
-
const R =
|
|
280
|
-
const C =
|
|
324
|
+
const yin = ydata;
|
|
325
|
+
const R = yin.length;
|
|
326
|
+
const C = yin[0].length;
|
|
281
327
|
const ndimY = 2;
|
|
282
328
|
const ax = axis < 0 ? ndimY + axis : axis;
|
|
283
329
|
if (ax === 1) {
|
|
284
330
|
if (C !== M) throw new Error(`'ydata' shape[${ax}] (${C}) must equal 'xdata' size (${M}).`);
|
|
285
|
-
|
|
331
|
+
N = R;
|
|
332
|
+
y = new Float64Array(N * M);
|
|
333
|
+
for (let n = 0; n < N; n++) {
|
|
334
|
+
const row = yin[n];
|
|
335
|
+
const base = n * M;
|
|
336
|
+
for (let i = 0; i < M; i++) y[base + i] = row[i];
|
|
337
|
+
}
|
|
286
338
|
} else if (ax === 0) {
|
|
287
339
|
if (R !== M) throw new Error(`'ydata' shape[${ax}] (${R}) must equal 'xdata' size (${M}).`);
|
|
288
340
|
transposed = true;
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
341
|
+
N = C;
|
|
342
|
+
y = new Float64Array(N * M);
|
|
343
|
+
for (let i = 0; i < M; i++) {
|
|
344
|
+
const row = yin[i];
|
|
345
|
+
for (let n = 0; n < N; n++) y[n * M + i] = row[n];
|
|
294
346
|
}
|
|
295
347
|
} else {
|
|
296
348
|
throw new Error(`Unsupported axis ${axis} for 2-D ydata.`);
|
|
297
349
|
}
|
|
298
350
|
isVector = false;
|
|
299
351
|
} else {
|
|
300
|
-
const
|
|
301
|
-
if (
|
|
302
|
-
|
|
352
|
+
const yin = ydata;
|
|
353
|
+
if (yin.length !== M) throw new Error(`'ydata' size (${yin.length}) must equal 'xdata' size (${M}).`);
|
|
354
|
+
N = 1;
|
|
355
|
+
y = toFloat64(yin);
|
|
303
356
|
isVector = true;
|
|
304
357
|
}
|
|
305
358
|
let w;
|
|
@@ -309,7 +362,6 @@ function prepareUnivariate(xdata, ydata, weights, axis) {
|
|
|
309
362
|
w = toFloat64(weights);
|
|
310
363
|
if (w.length !== M) throw new Error("Weights vector size must equal xdata size.");
|
|
311
364
|
}
|
|
312
|
-
const N = y2d.length;
|
|
313
365
|
const restore = (flat, L) => {
|
|
314
366
|
if (isVector) {
|
|
315
367
|
const out2 = new Array(L);
|
|
@@ -333,13 +385,13 @@ function prepareUnivariate(xdata, ydata, weights, axis) {
|
|
|
333
385
|
}
|
|
334
386
|
return out;
|
|
335
387
|
};
|
|
336
|
-
return { x,
|
|
388
|
+
return { x, y, N, w, restore };
|
|
337
389
|
}
|
|
338
390
|
var CubicSmoothingSpline = class {
|
|
339
391
|
constructor(xdata, ydata, options = {}) {
|
|
340
392
|
const { weights, smooth = null, axis = -1, normalizedsmooth = false } = options;
|
|
341
393
|
const prep = prepareUnivariate(xdata, ydata, weights, axis);
|
|
342
|
-
const res = makeSpline(prep.x, prep.
|
|
394
|
+
const res = makeSpline(prep.x, prep.y, prep.N, prep.w, smooth ?? null, normalizedsmooth);
|
|
343
395
|
this.pp = res.pp;
|
|
344
396
|
this.smooth = res.smooth;
|
|
345
397
|
this.restore = prep.restore;
|
|
@@ -389,17 +441,19 @@ var NdArray = class _NdArray {
|
|
|
389
441
|
const d = this.shape.length;
|
|
390
442
|
const newShape = perm.map((axis) => this.shape[axis]);
|
|
391
443
|
const oldStrides = cStrides(this.shape);
|
|
392
|
-
const
|
|
444
|
+
const ps = perm.map((axis) => oldStrides[axis]);
|
|
393
445
|
const total = this.data.length;
|
|
446
|
+
const src = this.data;
|
|
394
447
|
const out = new Float64Array(total);
|
|
395
448
|
const idx = new Array(d).fill(0);
|
|
449
|
+
let off = 0;
|
|
396
450
|
for (let pos = 0; pos < total; pos++) {
|
|
397
|
-
|
|
398
|
-
for (let a = 0; a < d; a++) off += idx[a] * permutedStrides[a];
|
|
399
|
-
out[pos] = this.data[off];
|
|
451
|
+
out[pos] = src[off];
|
|
400
452
|
for (let a = d - 1; a >= 0; a--) {
|
|
453
|
+
off += ps[a];
|
|
401
454
|
if (++idx[a] < newShape[a]) break;
|
|
402
455
|
idx[a] = 0;
|
|
456
|
+
off -= ps[a] * newShape[a];
|
|
403
457
|
}
|
|
404
458
|
}
|
|
405
459
|
return new _NdArray(out, newShape);
|
|
@@ -481,15 +535,8 @@ function buildGrid(xs, yNd, weights, smooth, normalizedsmooth) {
|
|
|
481
535
|
const lastSize = shape[shape.length - 1];
|
|
482
536
|
const total = coeffs.data.length;
|
|
483
537
|
const Nrest = total / lastSize;
|
|
484
|
-
const y2d = new Array(Nrest);
|
|
485
|
-
for (let n = 0; n < Nrest; n++) {
|
|
486
|
-
const row = new Array(lastSize);
|
|
487
|
-
const base = n * lastSize;
|
|
488
|
-
for (let col = 0; col < lastSize; col++) row[col] = coeffs.data[base + col];
|
|
489
|
-
y2d[n] = row;
|
|
490
|
-
}
|
|
491
538
|
const w = weights[i] ?? new Float64Array(lastSize).fill(1);
|
|
492
|
-
const res = makeSpline(xs[i],
|
|
539
|
+
const res = makeSpline(xs[i], coeffs.data, Nrest, w, smooth[i], normalizedsmooth);
|
|
493
540
|
const order = res.pp.order;
|
|
494
541
|
const pcs = res.pp.pieces;
|
|
495
542
|
orders[i] = order;
|