@danielsimonjr/mathts-functions 0.18.0 → 0.20.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.
@@ -7,11 +7,18 @@
7
7
  * storage buffers, and reads back once, so the transfer is amortized across the
8
8
  * whole chain.
9
9
  *
10
- * **Read this before reaching for the GPU:** for element-wise work the GPU is
11
- * *slower* than WASM (~1.9×, measured) *and* less precise (f32 vs f64). It earns
12
- * its place only where WASM cannot load. `fuseUnaryChainAsync` therefore tries
13
- * WASM first. The GPU wins decisively on **compute-bound** work see
14
- * `gpuMatmul`, where O(n³) arithmetic amortizes the O(n²) transfer.
10
+ * **Read this before reaching for the GPU:** for element-wise chains the GPU is
11
+ * the *fastest* tier (3.2–8.3× over WASM see the table on
12
+ * `fuseUnaryChainAsync`), but it computes in **f32** where every CPU tier is
13
+ * f64-exact. That is the whole trade, and `enableGpu()` is how a caller consents
14
+ * to it. `fuseUnaryChainAsync` therefore tries the GPU first, but only when the
15
+ * flag is on; with it off (the default) the GPU never runs.
16
+ *
17
+ * An earlier revision of this comment claimed the GPU was ~1.9× *slower* than
18
+ * WASM. That was an artifact of a `Float32Array.from()` in this very file — the
19
+ * generic `Array.from` path, which cost 433 ms at n=2²⁰ where the constructor
20
+ * costs 5.9 ms. Do not re-derive a tier ranking from a single tier's number; see
21
+ * `gpu-vs-wasm.browser.test.ts`, which measures all three in one run.
15
22
  *
16
23
  * Contract (mirrors the WASM `elementwiseChainDispatch`): a **never-throw**
17
24
  * best-effort fast path. It returns `null` — never rejects — whenever the GPU is
@@ -38,9 +45,12 @@ declare const WGSL_OP_BODY: {
38
45
  };
39
46
  /** Ops that have a GPU kernel. A chain outside this set falls back. */
40
47
  export type GpuElementwiseOp = keyof typeof WGSL_OP_BODY;
41
- export declare const GPU_ELEMENTWISE_OPS: GpuElementwiseOp[];
48
+ export declare const GPU_ELEMENTWISE_OPS: readonly GpuElementwiseOp[];
42
49
  /** Whether every op in the chain has a GPU kernel. */
43
50
  export declare function isGpuChainSupported(ops: readonly string[]): ops is readonly GpuElementwiseOp[];
51
+ /** Reductions that can be fused onto the end of a chain. */
52
+ export type GpuReduceOp = 'sum' | 'max' | 'min';
53
+ export declare const GPU_REDUCE_OPS: readonly ["sum", "max", "min"];
44
54
  /** Drop the cached shaders/buffers (device loss, or between tests). */
45
55
  export declare function resetGpuElementwise(): void;
46
56
  /** Options for a GPU element-wise dispatch. */
@@ -62,5 +72,49 @@ export interface GpuChainOptions extends GPUContextOptions {
62
72
  * @returns the f32 results, or `null` to signal "fall back to another tier"
63
73
  */
64
74
  export declare function elementwiseChainGpuDispatch(ops: readonly string[], xs: Float64Array | Float32Array, options?: GpuChainOptions): Promise<Float32Array | null>;
75
+ /**
76
+ * Apply `ops` on the GPU and **reduce the result on-device**, returning a single
77
+ * number instead of an array.
78
+ *
79
+ * The point is the readback, not the arithmetic: reducing on the device replaces
80
+ * an **n-float** transfer back to the CPU with an **n/256-float** one. Measured
81
+ * end-to-end through THIS function (not a prototype), NVIDIA Pascal,
82
+ * `sum(exp(sin(x)))`:
83
+ *
84
+ * | n | WASM chain + JS sum | GPU chain + JS sum | fused GPU reduce |
85
+ * | --------- | ------------------- | ------------------ | ---------------- |
86
+ * | 262,144 | 25.6 ms | 16.7 ms | **9.9 ms** |
87
+ * | 1,048,576 | 96.8 ms | 34.3 ms | **25.4 ms** |
88
+ * | 4,194,304 | 260.0 ms | 100.0 ms | **72.2 ms** |
89
+ *
90
+ * **1.35-1.7x** over the shipped GPU path, **2.6-3.8x** over the CPU tier.
91
+ *
92
+ * Quote the **1.39x at n=2^22** if you quote one number: it is the only ratio here that
93
+ * reproduces run to run (1.31-1.39x over four runs). The 1.7x is the n=262,144 row, and
94
+ * that size swings 1.19-2.83x between runs — the GPU work is short enough that fixed
95
+ * costs dominate. A headline should not be a lucky sample.
96
+ *
97
+ * Why not more: a bare-WGSL prototype of this hit ~2x, but it pre-converted its
98
+ * input outside the timed region. The real f64->f32 conversion is an n-scaling cost
99
+ * that BOTH paths pay, so it dilutes the ratio as n grows (the absolute saving is
100
+ * steady: ~28 ms at n=2^22). The prototype's number was not a lie, it was measuring
101
+ * a workload no caller has. Quote the numbers above, not those.
102
+ *
103
+ * **An empty `ops` is declined on purpose.** A *standalone* GPU reduction uploads
104
+ * n floats to produce one number — pure transfer tax, measured 3-9x SLOWER than a
105
+ * plain JS sum. There is no chain to amortise the upload against, so this returns
106
+ * `null` and lets the caller use the CPU, which is genuinely the faster path. The
107
+ * upload is only worth paying for when real work rides along with it.
108
+ *
109
+ * Same never-throw contract as {@link elementwiseChainGpuDispatch}: returns `null`
110
+ * — never rejects — whenever the GPU is unavailable, not opted into, the input is
111
+ * below `GPU_MIN_ELEMENTS`, an op has no kernel, or a device limit is exceeded.
112
+ *
113
+ * Precision: f32, like every GPU path here. For `sum` the tree reduction is
114
+ * pairwise, so its error grows O(log n) rather than the O(n) of a sequential
115
+ * accumulate — it is better-conditioned than the JS loop it replaces, even though
116
+ * it works in f32.
117
+ */
118
+ export declare function elementwiseChainReduceGpuDispatch(ops: readonly string[], xs: Float64Array | Float32Array, reduce: GpuReduceOp, options?: GpuChainOptions): Promise<number | null>;
65
119
  export {};
66
120
  //# sourceMappingURL=elementwise-gpu.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"elementwise-gpu.d.ts","sourceRoot":"","sources":["../../src/gpu/elementwise-gpu.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAOL,KAAK,iBAAiB,EACvB,MAAM,2BAA2B,CAAC;AA+CnC,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;CAgCR,CAAC;AAEX,uEAAuE;AACvE,MAAM,MAAM,gBAAgB,GAAG,MAAM,OAAO,YAAY,CAAC;AAEzD,eAAO,MAAM,mBAAmB,EAAgC,gBAAgB,EAAE,CAAC;AAEnF,sDAAsD;AACtD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,GAAG,GAAG,IAAI,SAAS,gBAAgB,EAAE,CAE9F;AAsED,uEAAuE;AACvE,wBAAgB,mBAAmB,IAAI,IAAI,CAG1C;AAED,+CAA+C;AAC/C,MAAM,WAAW,eAAgB,SAAQ,iBAAiB;IACxD;;;;;;OAMG;IACH,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED;;;;;;GAMG;AACH,wBAAsB,2BAA2B,CAC/C,GAAG,EAAE,SAAS,MAAM,EAAE,EACtB,EAAE,EAAE,YAAY,GAAG,YAAY,EAC/B,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAuH9B"}
1
+ {"version":3,"file":"elementwise-gpu.d.ts","sourceRoot":"","sources":["../../src/gpu/elementwise-gpu.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAOL,KAAK,iBAAiB,EACvB,MAAM,2BAA2B,CAAC;AA+CnC,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;CAgCR,CAAC;AAEX,uEAAuE;AACvE,MAAM,MAAM,gBAAgB,GAAG,MAAM,OAAO,YAAY,CAAC;AAEzD,eAAO,MAAM,mBAAmB,EAAE,SAAS,gBAAgB,EAEpC,CAAC;AAExB,sDAAsD;AACtD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,GAAG,GAAG,IAAI,SAAS,gBAAgB,EAAE,CAE9F;AAoCD,4DAA4D;AAC5D,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;AAIhD,eAAO,MAAM,cAAc,gCAAkE,CAAC;AAwG9F,uEAAuE;AACvE,wBAAgB,mBAAmB,IAAI,IAAI,CAG1C;AAED,+CAA+C;AAC/C,MAAM,WAAW,eAAgB,SAAQ,iBAAiB;IACxD;;;;;;OAMG;IACH,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AA4BD;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,SAAS,MAAM,EAAE,EACtB,EAAE,EAAE,YAAY,GAAG,YAAY,EAC/B,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAE9B;AA8ID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,wBAAgB,iCAAiC,CAC/C,GAAG,EAAE,SAAS,MAAM,EAAE,EACtB,EAAE,EAAE,YAAY,GAAG,YAAY,EAC/B,MAAM,EAAE,WAAW,EACnB,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAExB"}
package/dist/index.js CHANGED
@@ -24,6 +24,7 @@ __export(typed_exports, {
24
24
  GAUSS_WORKER_THRESHOLD: () => GAUSS_WORKER_THRESHOLD,
25
25
  GPU_ELEMENTWISE_OPS: () => GPU_ELEMENTWISE_OPS,
26
26
  GPU_MIN_ELEMENTS: () => GPU_MIN_ELEMENTS2,
27
+ GPU_REDUCE_OPS: () => GPU_REDUCE_OPS,
27
28
  WASM_INTERP_THRESHOLD: () => WASM_INTERP_THRESHOLD,
28
29
  abs: () => abs,
29
30
  acos: () => acos,
@@ -153,6 +154,7 @@ __export(typed_exports, {
153
154
  eigenvectorCentrality: () => eigenvectorCentrality,
154
155
  element: () => element,
155
156
  elementwiseChainGpuDispatch: () => elementwiseChainGpuDispatch,
157
+ elementwiseChainReduceGpuDispatch: () => elementwiseChainReduceGpuDispatch,
156
158
  eliminate: () => eliminate,
157
159
  ellipticE: () => ellipticE,
158
160
  ellipticEIncomplete: () => ellipticEIncomplete,
@@ -195,6 +197,7 @@ __export(typed_exports, {
195
197
  functionExpand: () => functionExpand,
196
198
  fuseUnaryChain: () => fuseUnaryChain,
197
199
  fuseUnaryChainAsync: () => fuseUnaryChainAsync,
200
+ fuseUnaryChainReduceAsync: () => fuseUnaryChainReduceAsync,
198
201
  gammaDist: () => gammaDist,
199
202
  gammaPDF: () => gammaPDF,
200
203
  gammainc: () => gammainc,
@@ -7413,7 +7416,9 @@ var WGSL_OP_BODY = {
7413
7416
  // An f32 fast path may be less precise. It may not be WRONG. Chains containing
7414
7417
  // these fall back to the exact WASM/JS tiers.
7415
7418
  };
7416
- var GPU_ELEMENTWISE_OPS = Object.keys(WGSL_OP_BODY);
7419
+ var GPU_ELEMENTWISE_OPS = Object.keys(
7420
+ WGSL_OP_BODY
7421
+ );
7417
7422
  function isGpuChainSupported(ops) {
7418
7423
  return ops.every((op) => op in WGSL_OP_BODY);
7419
7424
  }
@@ -7438,6 +7443,48 @@ function wgslFor(op) {
7438
7443
  }
7439
7444
  `;
7440
7445
  }
7446
+ var GPU_REDUCE_OPS = ["sum", "max", "min"];
7447
+ var reduceKey = (r) => `reduce:${r}`;
7448
+ var WGSL_REDUCE = {
7449
+ sum: { identity: "0.0", combine: "a + b" },
7450
+ max: { identity: "neg_inf()", combine: "max(a, b)" },
7451
+ min: { identity: "pos_inf()", combine: "min(a, b)" }
7452
+ };
7453
+ function wgslForReduce(r) {
7454
+ const { identity: identity2, combine: combine2 } = WGSL_REDUCE[r];
7455
+ return `
7456
+ @group(0) @binding(0) var<storage, read> inp: array<f32>;
7457
+ @group(0) @binding(1) var<storage, read_write> partials: array<f32>;
7458
+ @group(0) @binding(2) var<uniform> params: vec4<u32>; // n, nanBits, +infBits, -infBits
7459
+
7460
+ ${WGSL_IEEE}
7461
+
7462
+ // NOTE: "shared" is a RESERVED WORD in WGSL \u2014 this must not be named that.
7463
+ var<workgroup> sdata: array<f32, ${WORKGROUP_SIZE}>;
7464
+
7465
+ fn identity() -> f32 { return ${identity2}; }
7466
+ fn combine(a: f32, b: f32) -> f32 { return ${combine2}; }
7467
+
7468
+ @compute @workgroup_size(${WORKGROUP_SIZE})
7469
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>,
7470
+ @builtin(local_invocation_id) lid: vec3<u32>,
7471
+ @builtin(workgroup_id) wid: vec3<u32>) {
7472
+ var v: f32 = identity();
7473
+ if (gid.x < params.x) { v = inp[gid.x]; }
7474
+ sdata[lid.x] = v;
7475
+ workgroupBarrier();
7476
+
7477
+ var s: u32 = ${WORKGROUP_SIZE}u / 2u;
7478
+ loop {
7479
+ if (s == 0u) { break; }
7480
+ if (lid.x < s) { sdata[lid.x] = combine(sdata[lid.x], sdata[lid.x + s]); }
7481
+ workgroupBarrier();
7482
+ s = s >> 1u;
7483
+ }
7484
+ if (lid.x == 0u) { partials[wid.x] = sdata[0]; }
7485
+ }
7486
+ `;
7487
+ }
7441
7488
  var resources = null;
7442
7489
  async function getResources(options) {
7443
7490
  const device = await getGpuDevice(options);
@@ -7448,6 +7495,9 @@ async function getResources(options) {
7448
7495
  for (const op of GPU_ELEMENTWISE_OPS) {
7449
7496
  shaders.registerShader(op, wgslFor(op));
7450
7497
  }
7498
+ for (const r of GPU_REDUCE_OPS) {
7499
+ shaders.registerShader(reduceKey(r), wgslForReduce(r));
7500
+ }
7451
7501
  shaders.precompileRegistered();
7452
7502
  resources = { device, shaders, pool: new BufferPool(ctx) };
7453
7503
  return resources;
@@ -7456,7 +7506,16 @@ function resetGpuElementwise() {
7456
7506
  resources?.pool.destroy();
7457
7507
  resources = null;
7458
7508
  }
7459
- async function elementwiseChainGpuDispatch(ops, xs, options) {
7509
+ var gpuQueue = Promise.resolve();
7510
+ function serializeGpu(run) {
7511
+ const next = gpuQueue.then(run, run);
7512
+ gpuQueue = next.catch(() => void 0);
7513
+ return next;
7514
+ }
7515
+ function elementwiseChainGpuDispatch(ops, xs, options) {
7516
+ return serializeGpu(() => chainGpuDispatchImpl(ops, xs, options));
7517
+ }
7518
+ async function chainGpuDispatchImpl(ops, xs, options) {
7460
7519
  const n = xs.length;
7461
7520
  const enabled = options?.gpu ?? isGpuEnabled();
7462
7521
  if (!enabled) return null;
@@ -7470,6 +7529,7 @@ async function elementwiseChainGpuDispatch(ops, xs, options) {
7470
7529
  let bufB;
7471
7530
  let staging;
7472
7531
  let params;
7532
+ let scopePushed = false;
7473
7533
  let scopePopped = false;
7474
7534
  try {
7475
7535
  const r = await getResources(options);
@@ -7480,8 +7540,9 @@ async function elementwiseChainGpuDispatch(ops, xs, options) {
7480
7540
  if (workgroups > limits.maxComputeWorkgroupsPerDimension) return null;
7481
7541
  if (bytes > limits.maxStorageBufferBindingSize) return null;
7482
7542
  if (bytes > limits.maxBufferSize) return null;
7483
- const input = xs instanceof Float32Array ? xs : Float32Array.from(xs);
7543
+ const input = xs instanceof Float32Array ? xs : new Float32Array(xs);
7484
7544
  device.pushErrorScope("validation");
7545
+ scopePushed = true;
7485
7546
  bufA = pool.acquireStorageBuffer(bytes, "chain-a", true, true);
7486
7547
  bufB = pool.acquireStorageBuffer(bytes, "chain-b", true, true);
7487
7548
  staging = pool.acquireStagingBuffer(bytes, "chain-staging");
@@ -7520,7 +7581,7 @@ async function elementwiseChainGpuDispatch(ops, xs, options) {
7520
7581
  } catch {
7521
7582
  return null;
7522
7583
  } finally {
7523
- if (res && !scopePopped) {
7584
+ if (res && scopePushed && !scopePopped) {
7524
7585
  try {
7525
7586
  await res.device.popErrorScope();
7526
7587
  } catch {
@@ -7534,6 +7595,120 @@ async function elementwiseChainGpuDispatch(ops, xs, options) {
7534
7595
  }
7535
7596
  }
7536
7597
  }
7598
+ function elementwiseChainReduceGpuDispatch(ops, xs, reduce3, options) {
7599
+ return serializeGpu(() => chainReduceGpuDispatchImpl(ops, xs, reduce3, options));
7600
+ }
7601
+ async function chainReduceGpuDispatchImpl(ops, xs, reduce3, options) {
7602
+ const n = xs.length;
7603
+ const enabled = options?.gpu ?? isGpuEnabled();
7604
+ if (!enabled) return null;
7605
+ if (ops.length === 0) return null;
7606
+ if (n < GPU_MIN_ELEMENTS) return null;
7607
+ if (!isGpuChainSupported(ops)) return null;
7608
+ if (!GPU_REDUCE_OPS.includes(reduce3)) return null;
7609
+ const bytes = n * 4;
7610
+ const workgroups = Math.ceil(n / WORKGROUP_SIZE);
7611
+ const partialBytes = workgroups * 4;
7612
+ let res;
7613
+ let bufA;
7614
+ let bufB;
7615
+ let partials;
7616
+ let staging;
7617
+ let params;
7618
+ let scopePushed = false;
7619
+ let scopePopped = false;
7620
+ try {
7621
+ const r = await getResources(options);
7622
+ if (!r) return null;
7623
+ res = r;
7624
+ const { device, shaders, pool } = r;
7625
+ const limits = device.limits;
7626
+ if (workgroups > limits.maxComputeWorkgroupsPerDimension) return null;
7627
+ if (bytes > limits.maxStorageBufferBindingSize) return null;
7628
+ if (bytes > limits.maxBufferSize) return null;
7629
+ const input = xs instanceof Float32Array ? xs : new Float32Array(xs);
7630
+ device.pushErrorScope("validation");
7631
+ scopePushed = true;
7632
+ bufA = pool.acquireStorageBuffer(bytes, "chain-a", true, true);
7633
+ bufB = pool.acquireStorageBuffer(bytes, "chain-b", true, true);
7634
+ partials = pool.acquireStorageBuffer(partialBytes, "reduce-partials", true, true);
7635
+ staging = pool.acquireStagingBuffer(partialBytes, "reduce-staging");
7636
+ params = pool.acquireUniformBuffer(16, "chain-params");
7637
+ device.queue.writeBuffer(bufA, 0, input);
7638
+ device.queue.writeBuffer(params, 0, new Uint32Array([n, 2143289344, 2139095040, 4286578688]));
7639
+ const encoder = device.createCommandEncoder({ label: "elementwise-chain-reduce" });
7640
+ let src = bufA;
7641
+ let dst2 = bufB;
7642
+ for (const op of ops) {
7643
+ const pipeline = shaders.getRegisteredPipeline(op);
7644
+ const bindGroup = device.createBindGroup({
7645
+ layout: pipeline.getBindGroupLayout(0),
7646
+ entries: [
7647
+ { binding: 0, resource: { buffer: src } },
7648
+ { binding: 1, resource: { buffer: dst2 } },
7649
+ { binding: 2, resource: { buffer: params } }
7650
+ ]
7651
+ });
7652
+ const pass = encoder.beginComputePass({ label: `chain:${op}` });
7653
+ pass.setPipeline(pipeline);
7654
+ pass.setBindGroup(0, bindGroup);
7655
+ pass.dispatchWorkgroups(workgroups);
7656
+ pass.end();
7657
+ [src, dst2] = [dst2, src];
7658
+ }
7659
+ const reducePipeline = shaders.getRegisteredPipeline(reduceKey(reduce3));
7660
+ const reduceBind = device.createBindGroup({
7661
+ layout: reducePipeline.getBindGroupLayout(0),
7662
+ entries: [
7663
+ { binding: 0, resource: { buffer: src } },
7664
+ { binding: 1, resource: { buffer: partials } },
7665
+ { binding: 2, resource: { buffer: params } }
7666
+ ]
7667
+ });
7668
+ const reducePass = encoder.beginComputePass({ label: `reduce:${reduce3}` });
7669
+ reducePass.setPipeline(reducePipeline);
7670
+ reducePass.setBindGroup(0, reduceBind);
7671
+ reducePass.dispatchWorkgroups(workgroups);
7672
+ reducePass.end();
7673
+ encoder.copyBufferToBuffer(partials, 0, staging, 0, partialBytes);
7674
+ device.queue.submit([encoder.finish()]);
7675
+ const validationError = await device.popErrorScope();
7676
+ scopePopped = true;
7677
+ if (validationError) return null;
7678
+ await staging.mapAsync(GPUMapMode.READ, 0, partialBytes);
7679
+ const parts = new Float32Array(staging.getMappedRange(0, partialBytes).slice(0));
7680
+ staging.unmap();
7681
+ return foldPartials(parts, reduce3);
7682
+ } catch {
7683
+ return null;
7684
+ } finally {
7685
+ if (res && scopePushed && !scopePopped) {
7686
+ try {
7687
+ await res.device.popErrorScope();
7688
+ } catch {
7689
+ }
7690
+ }
7691
+ if (res) {
7692
+ if (bufA) res.pool.release(bufA);
7693
+ if (bufB) res.pool.release(bufB);
7694
+ if (partials) res.pool.release(partials);
7695
+ if (staging) res.pool.release(staging);
7696
+ if (params) res.pool.release(params);
7697
+ }
7698
+ }
7699
+ }
7700
+ function foldPartials(parts, reduce3) {
7701
+ if (reduce3 === "sum") {
7702
+ let acc2 = 0;
7703
+ for (let i = 0; i < parts.length; i++) acc2 += parts[i];
7704
+ return acc2;
7705
+ }
7706
+ let acc = reduce3 === "max" ? -Infinity : Infinity;
7707
+ for (let i = 0; i < parts.length; i++) {
7708
+ acc = reduce3 === "max" ? Math.max(acc, parts[i]) : Math.min(acc, parts[i]);
7709
+ }
7710
+ return acc;
7711
+ }
7537
7712
 
7538
7713
  // src/typed/fused.ts
7539
7714
  var SCALAR = {
@@ -7562,7 +7737,7 @@ function fuseUnaryChain(ops, xs) {
7562
7737
  return jsChain(ops, xs);
7563
7738
  }
7564
7739
  function jsChain(ops, xs) {
7565
- const out = Float64Array.from(xs);
7740
+ const out = new Float64Array(xs);
7566
7741
  for (const op of ops) {
7567
7742
  const f = SCALAR[op];
7568
7743
  for (let i = 0; i < out.length; i++) out[i] = f(out[i]);
@@ -7570,12 +7745,30 @@ function jsChain(ops, xs) {
7570
7745
  return out;
7571
7746
  }
7572
7747
  async function fuseUnaryChainAsync(ops, xs, options) {
7748
+ const gpu = await elementwiseChainGpuDispatch(ops, xs, options);
7749
+ if (gpu) return new Float64Array(gpu);
7573
7750
  const wasm = elementwiseChainDispatch(ops, xs);
7574
7751
  if (wasm) return wasm;
7575
- const gpu = await elementwiseChainGpuDispatch(ops, xs, options);
7576
- if (gpu) return Float64Array.from(gpu);
7577
7752
  return jsChain(ops, xs);
7578
7753
  }
7754
+ async function fuseUnaryChainReduceAsync(ops, xs, reduce3, options) {
7755
+ const gpu = await elementwiseChainReduceGpuDispatch(ops, xs, reduce3, options);
7756
+ if (gpu !== null) return gpu;
7757
+ const chained = elementwiseChainDispatch(ops, xs) ?? jsChain(ops, xs);
7758
+ return reduceF64(chained, reduce3);
7759
+ }
7760
+ function reduceF64(xs, reduce3) {
7761
+ if (reduce3 === "sum") {
7762
+ let acc2 = 0;
7763
+ for (let i = 0; i < xs.length; i++) acc2 += xs[i];
7764
+ return acc2;
7765
+ }
7766
+ let acc = reduce3 === "max" ? -Infinity : Infinity;
7767
+ for (let i = 0; i < xs.length; i++) {
7768
+ acc = reduce3 === "max" ? Math.max(acc, xs[i]) : Math.min(acc, xs[i]);
7769
+ }
7770
+ return acc;
7771
+ }
7579
7772
 
7580
7773
  // src/typed/distributions.ts
7581
7774
  import { mathTyped as mathTyped11 } from "@danielsimonjr/mathts-core";
@@ -44940,6 +45133,7 @@ export {
44940
45133
  GAUSS_WORKER_THRESHOLD,
44941
45134
  GPU_ELEMENTWISE_OPS,
44942
45135
  GPU_MIN_ELEMENTS2 as GPU_MIN_ELEMENTS,
45136
+ GPU_REDUCE_OPS,
44943
45137
  WASM_INTERP_THRESHOLD,
44944
45138
  abs,
44945
45139
  acf,
@@ -45157,6 +45351,7 @@ export {
45157
45351
  element,
45158
45352
  elementaryCharge,
45159
45353
  elementwiseChainGpuDispatch,
45354
+ elementwiseChainReduceGpuDispatch,
45160
45355
  eliminate,
45161
45356
  ellipticE,
45162
45357
  ellipticEIncomplete,
@@ -45285,6 +45480,7 @@ export {
45285
45480
  functionExpand,
45286
45481
  fuseUnaryChain,
45287
45482
  fuseUnaryChainAsync,
45483
+ fuseUnaryChainReduceAsync,
45288
45484
  gamma,
45289
45485
  gammaCDF,
45290
45486
  gammaDist,
@@ -13,7 +13,7 @@
13
13
  * array is below threshold, so the result is always correct.
14
14
  */
15
15
  import { type WasmElementwiseOp } from '../wasm/elementwise/wasm-bridge.js';
16
- import { type GpuChainOptions } from '../gpu/elementwise-gpu.js';
16
+ import { type GpuChainOptions, type GpuReduceOp } from '../gpu/elementwise-gpu.js';
17
17
  /**
18
18
  * Apply `ops` left-to-right over `xs` (i.e. `ops[last](…ops[0](x))`), fused in
19
19
  * WASM when possible. Returns a new `Float64Array`; never mutates `xs`.
@@ -26,31 +26,49 @@ export declare function fuseUnaryChain(ops: WasmElementwiseOp[], xs: Float64Arra
26
26
  * `fuseUnaryChain`, because a GPU dispatch is inherently asynchronous and
27
27
  * `fuseUnaryChain`'s synchronous signature is public API.
28
28
  *
29
- * Tiers, in order: **WASM (f64) → GPU (f32) → JS (f64)**.
29
+ * Tiers, in order: **GPU (f32, opt-in) → WASM (f64) → JS (f64)**.
30
30
  *
31
- * ### Why WASM is tried BEFORE the GPU
31
+ * ### Why the GPU is tried first but ONLY when explicitly enabled
32
32
  *
33
- * Measured in Chrome on an NVIDIA Pascal adapter, chain `sin→exp→tanh→cos`:
33
+ * Chain `sin→exp→tanh→cos`, 5 reps. ONE run, 2026-07-13, Chrome on an NVIDIA
34
+ * Pascal adapter — the same run quoted in the CHANGELOG and the reference docs, so
35
+ * the three tables agree. Pinned by `gpu-vs-wasm.browser.test.ts`:
34
36
  *
35
- * | n | JS | WASM | GPU | GPU vs WASM |
36
- * | --------- | ------- | ------- | ------- | ----------- |
37
- * | 65,536 | 90 ms | 16 ms | 28 ms | **0.57×** |
38
- * | 262,144 | 400 ms | 60 ms | 103 ms | **0.59×** |
39
- * | 1,048,576 | 1613 ms | 250 ms | 457 ms | **0.55×** |
37
+ * | n | JS | WASM | GPU | GPU vs WASM |
38
+ * | --------- | ------ | ------ | ---------- | ----------- |
39
+ * | 65,536 | 44 ms | 17 ms | **5.2 ms** | **3.2×** |
40
+ * | 262,144 | 185 ms | 63 ms | **7.5 ms** | **8.3×** |
41
+ * | 1,048,576 | 711 ms | 256 ms | **35 ms** | **7.2×** |
40
42
  *
41
- * **WASM is ~1.8× faster than the GPU and it is f64-exact while the GPU is
42
- * f32.** For element-wise chains the GPU is therefore both slower *and* less
43
- * precise, so it must never pre-empt WASM. (An earlier revision had GPU first;
44
- * that only looked like a win because a separate bug meant WASM never loaded in
45
- * browsers, making the baseline pure JS.)
43
+ * The GPU is the fastest tier by a wide margin. It is nevertheless **last-resort
44
+ * by default**, because it computes in f32 while every other tier is f64-exact.
45
+ * `enableGpu()` is how a caller consents to that trade: precision for speed. With
46
+ * the flag off the default this function is exactly WASM JS and returns
47
+ * bit-identical f64 results, so opting out costs nothing.
46
48
  *
47
- * The GPU still earns its place where WASM is **unavailable** — there it beats
48
- * the JS scalar pass ~2–2.5×. It engages only when the caller opted in via
49
- * `enableGpu()`, WASM declined, a device exists, the array clears
50
- * `GPU_MIN_ELEMENTS`, and every op has a GPU kernel.
49
+ * ### Provenance of these numbers (read before changing the order)
51
50
  *
52
- * (The GPU *does* win decisively for compute-bound work like a large matmul —
53
- * see `gpuMatmul`. This ordering is specific to memory-bound element-wise work.)
51
+ * This ordering has been wrong twice, both times from a benchmark measuring
52
+ * something other than what it claimed:
53
+ *
54
+ * 1. GPU-first was first adopted on a "2.3-2.9x faster than JS" result. That
55
+ * baseline was pure JS only because a *separate* bug meant WASM never loaded
56
+ * in browsers. Fixing WASM revealed it beat the GPU, so the order was flipped
57
+ * to WASM-first.
58
+ * 2. That flip was also wrong. The GPU figure it rested on was inflated by
59
+ * `Float32Array.from(f64array)` in the dispatch — the generic `Array.from`
60
+ * path, which runs ToNumber per element. Naming the denominators, because they
61
+ * differ: the *conversion* alone was 73x slower (433 ms vs 5.9 ms at n=2^20),
62
+ * which made the *end-to-end dispatch* 12.2x slower (439.80 ms -> 36.06 ms).
63
+ * With that fixed, the GPU wins outright, as above.
64
+ *
65
+ * The lesson both times: a tier's number is only as good as the tier it is
66
+ * compared against. Re-measure ALL THREE tiers in one run before touching this
67
+ * order — `gpu-vs-wasm.browser.test.ts` does exactly that and fails loudly if the
68
+ * ranking changes.
69
+ *
70
+ * (The GPU also wins decisively for compute-bound work like a large matmul — see
71
+ * `gpuMatmul`. It is not merely a memory-bound-work story.)
54
72
  *
55
73
  * **Precision.** Always returns a `Float64Array`. When the GPU tier runs, the
56
74
  * *values* carry f32 precision (~7 significant digits) even though the container
@@ -65,4 +83,25 @@ export declare function fuseUnaryChain(ops: WasmElementwiseOp[], xs: Float64Arra
65
83
  * `elementwiseChainGpuDispatch` directly — it is exported for exactly that.
66
84
  */
67
85
  export declare function fuseUnaryChainAsync(ops: WasmElementwiseOp[], xs: Float64Array, options?: GpuChainOptions): Promise<Float64Array>;
86
+ /**
87
+ * Apply `ops` and then reduce to a single number — `sum(exp(sin(x)))` and friends.
88
+ *
89
+ * Tiers: **GPU (f32, opt-in) → WASM chain + JS reduce → JS chain + JS reduce.**
90
+ *
91
+ * When the GPU tier runs, the reduction happens **on the device**, so only n/256
92
+ * floats cross the bus instead of n. That is the whole reason this function exists.
93
+ * Measured end-to-end for `sum(exp(sin(x)))` on an NVIDIA Pascal adapter: **1.35-1.7x**
94
+ * faster than `fuseUnaryChainAsync(...)` followed by a JS loop, and **2.6-3.8x** faster
95
+ * than the CPU tier. (See `elementwiseChainReduceGpuDispatch` for the full table and
96
+ * for why the ratio shrinks as n grows.)
97
+ *
98
+ * Reach for it only when you want the **scalar**. If you also need the transformed
99
+ * array, use `fuseUnaryChainAsync` — you have to pay the n-float readback anyway, and
100
+ * summing it in JS afterwards costs almost nothing on top.
101
+ *
102
+ * Precision follows the tier that ran: f32 (~7 significant digits) on the GPU, exact
103
+ * f64 on WASM/JS. `enableGpu()` is the consent; with the flag off this is a pure f64
104
+ * computation.
105
+ */
106
+ export declare function fuseUnaryChainReduceAsync(ops: WasmElementwiseOp[], xs: Float64Array, reduce: GpuReduceOp, options?: GpuChainOptions): Promise<number>;
68
107
  //# sourceMappingURL=fused.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fused.d.ts","sourceRoot":"","sources":["../../src/typed/fused.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAA+B,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAyB9F;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,iBAAiB,EAAE,EAAE,EAAE,EAAE,YAAY,GAAG,YAAY,CAIvF;AAYD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,iBAAiB,EAAE,EACxB,EAAE,EAAE,YAAY,EAChB,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,YAAY,CAAC,CAUvB"}
1
+ {"version":3,"file":"fused.d.ts","sourceRoot":"","sources":["../../src/typed/fused.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAGL,KAAK,eAAe,EACpB,KAAK,WAAW,EACjB,MAAM,2BAA2B,CAAC;AAyBnC;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,iBAAiB,EAAE,EAAE,EAAE,EAAE,YAAY,GAAG,YAAY,CAIvF;AAcD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8DG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,iBAAiB,EAAE,EACxB,EAAE,EAAE,YAAY,EAChB,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,YAAY,CAAC,CAgBvB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,iBAAiB,EAAE,EACxB,EAAE,EAAE,YAAY,EAChB,MAAM,EAAE,WAAW,EACnB,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,MAAM,CAAC,CAQjB"}
@@ -47,7 +47,7 @@ export * from './matrix-ops.js';
47
47
  export { cond } from './matrix-ops.js';
48
48
  export * from './gpu.js';
49
49
  export { enableGpu, disableGpu, isGpuEnabled, GPU_MIN_ELEMENTS } from '@danielsimonjr/mathts-gpu';
50
- export { elementwiseChainGpuDispatch, isGpuChainSupported, resetGpuElementwise, GPU_ELEMENTWISE_OPS, type GpuElementwiseOp, type GpuChainOptions, } from '../gpu/elementwise-gpu.js';
50
+ export { elementwiseChainGpuDispatch, elementwiseChainReduceGpuDispatch, GPU_REDUCE_OPS, type GpuReduceOp, isGpuChainSupported, resetGpuElementwise, GPU_ELEMENTWISE_OPS, type GpuElementwiseOp, type GpuChainOptions, } from '../gpu/elementwise-gpu.js';
51
51
  export * from './relational.js';
52
52
  export { typedRelational } from './relational.js';
53
53
  export * from './string.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/typed/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAOH,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAGlD,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAGtD,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAGlD,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG1C,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAG5C,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAG5C,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAK5C,cAAc,UAAU,CAAC;AACzB,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAGpC,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAG5C,cAAc,oBAAoB,CAAC;AACnC,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAGxD,cAAc,eAAe,CAAC;AAG9B,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAG5C,cAAc,kBAAkB,CAAC;AAGjC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,cAAc,CAAC;AAG7B,cAAc,oBAAoB,CAAC;AAGnC,cAAc,YAAY,CAAC;AAG3B,cAAc,mBAAmB,CAAC;AAGlC,cAAc,iBAAiB,CAAC;AAWhC,cAAc,iBAAiB,CAAC;AAGhC,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAGvC,cAAc,UAAU,CAAC;AAIzB,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAClG,OAAO,EACL,2BAA2B,EAC3B,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,KAAK,gBAAgB,EACrB,KAAK,eAAe,GACrB,MAAM,2BAA2B,CAAC;AAInC,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAGlD,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI1C,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGpD,cAAc,WAAW,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAsCtC;;;;;;GAMG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8B1B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/typed/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAOH,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAGlD,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAGtD,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAGlD,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG1C,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAG5C,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAG5C,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAK5C,cAAc,UAAU,CAAC;AACzB,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAGpC,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAG5C,cAAc,oBAAoB,CAAC;AACnC,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAGxD,cAAc,eAAe,CAAC;AAG9B,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAG5C,cAAc,kBAAkB,CAAC;AAGjC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,cAAc,CAAC;AAG7B,cAAc,oBAAoB,CAAC;AAGnC,cAAc,YAAY,CAAC;AAG3B,cAAc,mBAAmB,CAAC;AAGlC,cAAc,iBAAiB,CAAC;AAWhC,cAAc,iBAAiB,CAAC;AAGhC,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAGvC,cAAc,UAAU,CAAC;AAIzB,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAClG,OAAO,EACL,2BAA2B,EAC3B,iCAAiC,EACjC,cAAc,EACd,KAAK,WAAW,EAChB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,KAAK,gBAAgB,EACrB,KAAK,eAAe,GACrB,MAAM,2BAA2B,CAAC;AAInC,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAGlD,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI1C,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGpD,cAAc,WAAW,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAsCtC;;;;;;GAMG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8B1B,CAAC"}
package/package.json CHANGED
@@ -1,76 +1,76 @@
1
- {
2
- "name": "@danielsimonjr/mathts-functions",
3
- "version": "0.18.0",
4
- "description": "Mathematical functions for MathTS - arithmetic, algebra, trigonometry, statistics, and more",
5
- "author": "Daniel Simon Jr.",
6
- "license": "MIT",
7
- "type": "module",
8
- "main": "./dist/index.js",
9
- "module": "./dist/index.js",
10
- "types": "./dist/index.d.ts",
11
- "exports": {
12
- ".": {
13
- "import": "./dist/index.js",
14
- "types": "./dist/index.d.ts"
15
- }
16
- },
17
- "files": [
18
- "dist",
19
- "types",
20
- "README.md"
21
- ],
22
- "scripts": {
23
- "build": "tsup src/index.ts --format esm --clean && tsc -p tsconfig.dts.json && node scripts/copy-wasm.mjs",
24
- "dev": "tsup src/index.ts --format esm --dts --watch",
25
- "test": "vitest run",
26
- "test:diff": "node tests/diff-special.test.mjs && node tests/diff-elementwise.test.mjs && node tests/diff-fusion.test.mjs",
27
- "golden:gen": "python -X utf8 tests/golden/gen_special_goldens.py",
28
- "test:watch": "vitest",
29
- "test:coverage": "vitest run --coverage",
30
- "typecheck": "tsc --noEmit",
31
- "lint": "eslint src --ext .ts",
32
- "lint:fix": "eslint src --ext .ts --fix",
33
- "clean": "rm -rf dist",
34
- "build:prod": "tsup src/index.ts --format esm --clean --minify --treeshake"
35
- },
36
- "dependencies": {
37
- "@danielsimonjr/mathts-core": "^0.6.0",
38
- "@danielsimonjr/mathts-expression": "^0.6.0",
39
- "@danielsimonjr/mathts-gpu": "^0.1.1",
40
- "@danielsimonjr/mathts-matrix": "^0.3.2",
41
- "@danielsimonjr/mathts-parallel": "^0.3.4",
42
- "bignumber.js": "^9.1.2",
43
- "complex.js": "^2.2.5",
44
- "decimal.js": "^10.4.3",
45
- "escape-latex": "^1.2.0",
46
- "fraction.js": "^5.2.1",
47
- "javascript-natural-sort": "^0.7.1",
48
- "seedrandom": "^3.0.5",
49
- "tiny-emitter": "^2.1.0",
50
- "typed-function": "github:danielsimonjr/typed-function"
51
- },
52
- "devDependencies": {
53
- "@types/node": "^25.5.2",
54
- "@webgpu/types": "^0.1.67",
55
- "tsup": "^8.0.0",
56
- "typescript": "^5.3.0",
57
- "vitest": "^4.1.5"
58
- },
59
- "publishConfig": {
60
- "access": "public"
61
- },
62
- "repository": {
63
- "type": "git",
64
- "url": "https://github.com/danielsimonjr/mathts",
65
- "directory": "functions"
66
- },
67
- "keywords": [
68
- "math",
69
- "typescript",
70
- "functions",
71
- "arithmetic",
72
- "algebra",
73
- "trigonometry",
74
- "statistics"
75
- ]
76
- }
1
+ {
2
+ "name": "@danielsimonjr/mathts-functions",
3
+ "version": "0.20.0",
4
+ "description": "Mathematical functions for MathTS - arithmetic, algebra, trigonometry, statistics, and more",
5
+ "author": "Daniel Simon Jr.",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "main": "./dist/index.js",
9
+ "module": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "import": "./dist/index.js",
14
+ "types": "./dist/index.d.ts"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "types",
20
+ "README.md"
21
+ ],
22
+ "scripts": {
23
+ "build": "tsup src/index.ts --format esm --clean && tsc -p tsconfig.dts.json && node scripts/copy-wasm.mjs",
24
+ "dev": "tsup src/index.ts --format esm --dts --watch",
25
+ "test": "vitest run",
26
+ "test:diff": "node tests/diff-special.test.mjs && node tests/diff-elementwise.test.mjs && node tests/diff-fusion.test.mjs",
27
+ "golden:gen": "python -X utf8 tests/golden/gen_special_goldens.py",
28
+ "test:watch": "vitest",
29
+ "test:coverage": "vitest run --coverage",
30
+ "typecheck": "tsc --noEmit",
31
+ "lint": "eslint src --ext .ts",
32
+ "lint:fix": "eslint src --ext .ts --fix",
33
+ "clean": "rm -rf dist",
34
+ "build:prod": "tsup src/index.ts --format esm --clean --minify --treeshake"
35
+ },
36
+ "dependencies": {
37
+ "@danielsimonjr/mathts-core": "^0.6.0",
38
+ "@danielsimonjr/mathts-expression": "^0.6.0",
39
+ "@danielsimonjr/mathts-gpu": "^0.1.1",
40
+ "@danielsimonjr/mathts-matrix": "^0.4.0",
41
+ "@danielsimonjr/mathts-parallel": "^0.3.4",
42
+ "bignumber.js": "^9.1.2",
43
+ "complex.js": "^2.2.5",
44
+ "decimal.js": "^10.4.3",
45
+ "escape-latex": "^1.2.0",
46
+ "fraction.js": "^5.2.1",
47
+ "javascript-natural-sort": "^0.7.1",
48
+ "seedrandom": "^3.0.5",
49
+ "tiny-emitter": "^2.1.0",
50
+ "typed-function": "github:danielsimonjr/typed-function"
51
+ },
52
+ "devDependencies": {
53
+ "@types/node": "^25.5.2",
54
+ "@webgpu/types": "^0.1.67",
55
+ "tsup": "^8.0.0",
56
+ "typescript": "^5.3.0",
57
+ "vitest": "^4.1.5"
58
+ },
59
+ "publishConfig": {
60
+ "access": "public"
61
+ },
62
+ "repository": {
63
+ "type": "git",
64
+ "url": "https://github.com/danielsimonjr/mathts",
65
+ "directory": "functions"
66
+ },
67
+ "keywords": [
68
+ "math",
69
+ "typescript",
70
+ "functions",
71
+ "arithmetic",
72
+ "algebra",
73
+ "trigonometry",
74
+ "statistics"
75
+ ]
76
+ }