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