wgblas 0.1.2 → 1.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.
Files changed (64) hide show
  1. package/README.md +3 -0
  2. package/dist/wgblas.browser.js +1249 -37
  3. package/index.d.mts +9 -0
  4. package/index.mjs +9 -0
  5. package/package.json +47 -1
  6. package/src/classes/GpuMatrix.d.mts +98 -0
  7. package/src/classes/GpuMatrix.mjs +109 -0
  8. package/src/classes/GpuVector.d.mts +11 -7
  9. package/src/classes/GpuVector.mjs +31 -5
  10. package/src/dasum/dasum.d.mts +48 -0
  11. package/src/dasum/dasum.mjs +121 -0
  12. package/src/init.mjs +3 -1
  13. package/src/isamax/isamax.mjs +66 -67
  14. package/src/random/random.d.mts +37 -0
  15. package/src/random/random.mjs +17 -0
  16. package/src/sasum/sasum.mjs +55 -51
  17. package/src/saxpy/saxpy.mjs +48 -35
  18. package/src/scopy/scopy.mjs +43 -32
  19. package/src/sdot/sdot.mjs +60 -55
  20. package/src/sgemv/sgemv.d.mts +127 -0
  21. package/src/sgemv/sgemv.mjs +148 -0
  22. package/src/sger/sger.d.mts +111 -0
  23. package/src/sger/sger.mjs +136 -0
  24. package/src/shaders/browser-shaders.mjs +26 -0
  25. package/src/shaders/dasum.wgsl +98 -0
  26. package/src/shaders/f64add.wgsl +281 -0
  27. package/src/shaders/isamax.wgsl +32 -9
  28. package/src/shaders/reduction/sumF64.wgsl +49 -0
  29. package/src/shaders/sasum.wgsl +18 -4
  30. package/src/shaders/sdot.wgsl +18 -4
  31. package/src/shaders/sgemv_n.wgsl +75 -0
  32. package/src/shaders/sgemv_t.wgsl +65 -0
  33. package/src/shaders/sger.wgsl +48 -0
  34. package/src/shaders/snrm2.wgsl +22 -4
  35. package/src/shaders/ssymv.wgsl +69 -0
  36. package/src/shaders/ssyr.wgsl +60 -0
  37. package/src/shaders/ssyr2.wgsl +63 -0
  38. package/src/shaders/strmv.wgsl +103 -0
  39. package/src/shaders/strsv_apply_inverse.wgsl +47 -0
  40. package/src/shaders/strsv_invert_block.wgsl +109 -0
  41. package/src/shaders/strsv_update.wgsl +75 -0
  42. package/src/snrm2/snrm2.mjs +56 -52
  43. package/src/srot/srot.mjs +57 -41
  44. package/src/srotm/srotm.mjs +54 -38
  45. package/src/sscal/sscal.mjs +43 -32
  46. package/src/sswap/sswap.mjs +49 -34
  47. package/src/ssymv/ssymv.d.mts +117 -0
  48. package/src/ssymv/ssymv.mjs +135 -0
  49. package/src/ssyr/ssyr.d.mts +100 -0
  50. package/src/ssyr/ssyr.mjs +106 -0
  51. package/src/ssyr2/ssyr2.d.mts +112 -0
  52. package/src/ssyr2/ssyr2.mjs +130 -0
  53. package/src/strmv/strmv.d.mts +117 -0
  54. package/src/strmv/strmv.mjs +138 -0
  55. package/src/strsv/strsv.d.mts +106 -0
  56. package/src/strsv/strsv.mjs +207 -0
  57. package/src/util/benchmark.mjs +1 -1
  58. package/src/util/bindgroup.mjs +14 -10
  59. package/src/util/buffer.mjs +7 -2
  60. package/src/util/compute.mjs +41 -15
  61. package/src/util/f64pack.mjs +152 -0
  62. package/src/util/pipeline.mjs +32 -17
  63. package/src/util/result.mjs +8 -4
  64. package/src/util/workgroup.mjs +10 -10
package/index.d.mts CHANGED
@@ -2,6 +2,7 @@
2
2
  * @module docs
3
3
  */
4
4
  export { GpuVector } from "./src/classes/GpuVector.mjs";
5
+ export { GpuMatrix } from "./src/classes/GpuMatrix.mjs";
5
6
  export {
6
7
  randomFloat32Array,
7
8
  randomFloat64Array,
@@ -12,10 +13,18 @@ export { saxpy } from "./src/saxpy/saxpy.mjs";
12
13
  export { scopy } from "./src/scopy/scopy.mjs";
13
14
  export { sdot } from "./src/sdot/sdot.mjs";
14
15
  export { sasum } from "./src/sasum/sasum.mjs";
16
+ export { dasum } from "./src/dasum/dasum.mjs";
15
17
  export { snrm2 } from "./src/snrm2/snrm2.mjs";
16
18
  export { isamax } from "./src/isamax/isamax.mjs";
17
19
  export { srot } from "./src/srot/srot.mjs";
18
20
  export { srotm } from "./src/srotm/srotm.mjs";
21
+ export { sgemv } from "./src/sgemv/sgemv.mjs";
22
+ export { ssymv } from "./src/ssymv/ssymv.mjs";
23
+ export { strmv } from "./src/strmv/strmv.mjs";
24
+ export { strsv } from "./src/strsv/strsv.mjs";
25
+ export { sger } from "./src/sger/sger.mjs";
26
+ export { ssyr } from "./src/ssyr/ssyr.mjs";
27
+ export { ssyr2 } from "./src/ssyr2/ssyr2.mjs";
19
28
 
20
29
  /**
21
30
  * Initializes the WebGPU device.
package/index.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  export { init, cleanup, gpuName } from "./src/init.mjs";
2
2
  export { GpuVector } from "./src/classes/GpuVector.mjs";
3
+ export { GpuMatrix } from "./src/classes/GpuMatrix.mjs";
3
4
  export {
4
5
  randomFloat32Array,
5
6
  randomFloat64Array,
@@ -10,7 +11,15 @@ export { saxpy } from "./src/saxpy/saxpy.mjs";
10
11
  export { scopy } from "./src/scopy/scopy.mjs";
11
12
  export { sdot } from "./src/sdot/sdot.mjs";
12
13
  export { sasum } from "./src/sasum/sasum.mjs";
14
+ export { dasum } from "./src/dasum/dasum.mjs";
13
15
  export { snrm2 } from "./src/snrm2/snrm2.mjs";
14
16
  export { isamax } from "./src/isamax/isamax.mjs";
15
17
  export { srot } from "./src/srot/srot.mjs";
16
18
  export { srotm } from "./src/srotm/srotm.mjs";
19
+ export { sgemv } from "./src/sgemv/sgemv.mjs";
20
+ export { ssymv } from "./src/ssymv/ssymv.mjs";
21
+ export { strmv } from "./src/strmv/strmv.mjs";
22
+ export { strsv } from "./src/strsv/strsv.mjs";
23
+ export { sger } from "./src/sger/sger.mjs";
24
+ export { ssyr } from "./src/ssyr/ssyr.mjs";
25
+ export { ssyr2 } from "./src/ssyr2/ssyr2.mjs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wgblas",
3
- "version": "0.1.2",
3
+ "version": "1.1.0",
4
4
  "description": "BLAS on WebGPU",
5
5
  "type": "module",
6
6
  "main": "index.mjs",
@@ -15,6 +15,10 @@
15
15
  "import": "./src/classes/GpuVector.mjs",
16
16
  "types": "./src/classes/GpuVector.d.mts"
17
17
  },
18
+ "./classes/GpuMatrix": {
19
+ "import": "./src/classes/GpuMatrix.mjs",
20
+ "types": "./src/classes/GpuMatrix.d.mts"
21
+ },
18
22
  "./random": {
19
23
  "import": "./src/random/random.mjs",
20
24
  "types": "./src/random/random.d.mts"
@@ -43,6 +47,10 @@
43
47
  "import": "./src/sasum/sasum.mjs",
44
48
  "types": "./src/sasum/sasum.d.mts"
45
49
  },
50
+ "./dasum": {
51
+ "import": "./src/dasum/dasum.mjs",
52
+ "types": "./src/dasum/dasum.d.mts"
53
+ },
46
54
  "./snrm2": {
47
55
  "import": "./src/snrm2/snrm2.mjs",
48
56
  "types": "./src/snrm2/snrm2.d.mts"
@@ -58,6 +66,34 @@
58
66
  "./srotm": {
59
67
  "import": "./src/srotm/srotm.mjs",
60
68
  "types": "./src/srotm/srotm.d.mts"
69
+ },
70
+ "./sgemv": {
71
+ "import": "./src/sgemv/sgemv.mjs",
72
+ "types": "./src/sgemv/sgemv.d.mts"
73
+ },
74
+ "./ssymv": {
75
+ "import": "./src/ssymv/ssymv.mjs",
76
+ "types": "./src/ssymv/ssymv.d.mts"
77
+ },
78
+ "./strmv": {
79
+ "import": "./src/strmv/strmv.mjs",
80
+ "types": "./src/strmv/strmv.d.mts"
81
+ },
82
+ "./strsv": {
83
+ "import": "./src/strsv/strsv.mjs",
84
+ "types": "./src/strsv/strsv.d.mts"
85
+ },
86
+ "./sger": {
87
+ "import": "./src/sger/sger.mjs",
88
+ "types": "./src/sger/sger.d.mts"
89
+ },
90
+ "./ssyr": {
91
+ "import": "./src/ssyr/ssyr.mjs",
92
+ "types": "./src/ssyr/ssyr.d.mts"
93
+ },
94
+ "./ssyr2": {
95
+ "import": "./src/ssyr2/ssyr2.mjs",
96
+ "types": "./src/ssyr2/ssyr2.d.mts"
61
97
  }
62
98
  },
63
99
  "files": [
@@ -89,17 +125,27 @@
89
125
  "webgpu": "^0.4.0"
90
126
  },
91
127
  "devDependencies": {
128
+ "@commitlint/cli": "^21.2.1",
129
+ "@commitlint/config-conventional": "^21.2.0",
92
130
  "@eslint/js": "^10.0.1",
131
+ "@stdlib/blas-base-dasum": "^0.4.1",
93
132
  "@stdlib/blas-base-isamax": "^0.1.1",
94
133
  "@stdlib/blas-base-sasum": "^0.3.1",
95
134
  "@stdlib/blas-base-saxpy": "^0.3.1",
96
135
  "@stdlib/blas-base-scopy": "^0.3.1",
97
136
  "@stdlib/blas-base-sdot": "^0.3.1",
137
+ "@stdlib/blas-base-sgemv": "^0.1.1",
138
+ "@stdlib/blas-base-sger": "^0.1.1",
98
139
  "@stdlib/blas-base-snrm2": "^0.3.1",
99
140
  "@stdlib/blas-base-srot": "^0.2.1",
100
141
  "@stdlib/blas-base-srotm": "^0.2.1",
101
142
  "@stdlib/blas-base-sscal": "^0.3.1",
102
143
  "@stdlib/blas-base-sswap": "^0.3.1",
144
+ "@stdlib/blas-base-ssymv": "^0.1.1",
145
+ "@stdlib/blas-base-ssyr": "^0.1.1",
146
+ "@stdlib/blas-base-ssyr2": "^0.1.1",
147
+ "@stdlib/blas-base-strmv": "^0.1.0",
148
+ "@stdlib/blas-base-strsv": "^0.1.1",
103
149
  "@stdlib/random-array-uniform": "^0.2.2",
104
150
  "@stdlib/random-base-uniform": "^0.2.3",
105
151
  "esbuild": "^0.28.1",
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Represents a Float32Array (or Float64Array) matrix stored in GPU memory,
3
+ * row-major or column-major.
4
+ *
5
+ * `rows`/`cols` always describe the logical shape regardless of layout.
6
+ * `lda` (leading dimension) is the stride between consecutive rows
7
+ * (row-major) or columns (column-major) — must be >= `cols` (row-major) or
8
+ * >= `rows` (column-major). When `lda` equals that minimum the matrix is
9
+ * dense with no padding.
10
+ *
11
+ * @see <a href="https://github.com/manit2004/wgblas/blob/main/src/classes/GpuMatrix.mjs#L7">Source code: GpuMatrix.mjs (L7)</a>
12
+ * @see [MDN: GPUBuffer](https://developer.mozilla.org/en-US/docs/Web/API/GPUBuffer)
13
+ * @category Classes
14
+ */
15
+ export declare class GpuMatrix {
16
+ private constructor();
17
+
18
+ /** @internal */
19
+ readonly _buf: GPUBuffer;
20
+
21
+ /** Number of rows (logical shape, independent of layout). */
22
+ readonly rows: number;
23
+
24
+ /** Number of columns (logical shape, independent of layout). */
25
+ readonly cols: number;
26
+
27
+ /** Leading dimension — stride between row starts (row-major) or column starts (column-major). */
28
+ readonly lda: number;
29
+
30
+ /** Storage layout this matrix was created with — every routine that accepts a GpuMatrix reads this automatically. */
31
+ readonly layout: 'row-major' | 'column-major';
32
+
33
+ /**
34
+ * Uploads a Float32Array or Float64Array matrix to GPU memory, row-major
35
+ * or column-major. A Float64Array is packed as two f32s per element (WGSL
36
+ * has no f64 type) and stored across two GPU buffers internally; `read()`
37
+ * reassembles the original doubles.
38
+ *
39
+ * `rows`/`cols` always describe the logical shape regardless of layout.
40
+ * `lda` defaults to `cols` (row-major) or `rows` (column-major) — dense, no
41
+ * padding. `data` must have at least `rows * lda` (row-major) or
42
+ * `cols * lda` (column-major) elements.
43
+ *
44
+ * @param data - matrix data, in the order matching `layout`
45
+ * @param rows - number of rows
46
+ * @param cols - number of columns
47
+ * @param lda - leading dimension (default: `cols` for row-major, `rows` for column-major)
48
+ * @param layout - storage layout (default: `'row-major'`)
49
+ *
50
+ * @example
51
+ * ```js
52
+ * import { init, GpuMatrix } from "wgblas";
53
+ *
54
+ * await init();
55
+ * // 2×3 matrix: [[1,2,3],[4,5,6]]
56
+ * const mat = GpuMatrix.from(new Float32Array([1,2,3,4,5,6]), 2, 3);
57
+ * console.log(mat.rows, mat.cols, mat.lda); // 2 3 3
58
+ *
59
+ * // Same logical matrix, column-major storage
60
+ * const matCol = GpuMatrix.from(new Float32Array([1,4,2,5,3,6]), 2, 3, undefined, "column-major");
61
+ * ```
62
+ */
63
+ static from(data: Float32Array | Float64Array, rows: number, cols: number, lda?: number, layout?: 'row-major' | 'column-major'): GpuMatrix;
64
+
65
+ /**
66
+ * Downloads the matrix from GPU memory and returns a dense array of shape
67
+ * `rows × cols`, in the same layout it was created with — a `Float32Array`,
68
+ * or a `Float64Array` if this matrix was created from one. If `lda` exceeds
69
+ * the dense minimum, the leading-dimension padding is stripped so the
70
+ * returned array is always tightly packed.
71
+ *
72
+ * @example
73
+ * ```js
74
+ * import { init, GpuMatrix } from "wgblas";
75
+ *
76
+ * await init();
77
+ * const mat = GpuMatrix.from(new Float32Array([1,2,3,4,5,6]), 2, 3);
78
+ * const data = await mat.read();
79
+ * console.log(data); // Float32Array [1, 2, 3, 4, 5, 6]
80
+ * ```
81
+ */
82
+ read(): Promise<Float32Array | Float64Array>;
83
+
84
+ /**
85
+ * Destroys the underlying GPU buffer. Call when the matrix is no longer
86
+ * needed to free GPU memory.
87
+ *
88
+ * @example
89
+ * ```js
90
+ * import { init, GpuMatrix } from "wgblas";
91
+ *
92
+ * await init();
93
+ * const mat = GpuMatrix.from(new Float32Array([1,2,3,4,5,6]), 2, 3);
94
+ * mat.destroy();
95
+ * ```
96
+ */
97
+ destroy(): void;
98
+ }
@@ -0,0 +1,109 @@
1
+ import { getDevice } from "../init.mjs";
2
+ import { uploadBuffer, stageReadback } from "../util/buffer.mjs";
3
+ import { extractResult } from "../util/result.mjs";
4
+ import { packF64, unpackF64 } from "../util/f64pack.mjs";
5
+
6
+ export class GpuMatrix {
7
+ constructor(buffer, rows, cols, lda, auxBuffer = null, layout = "row-major") {
8
+ this._buf = buffer;
9
+ // Non-null only for Float64Array-backed matrices — see GpuVector for why
10
+ // (packF64 splits each element into a "main"/_buf f32 and "aux"/_auxBuf
11
+ // raw u32 — never a Float32Array, see f64pack.mjs).
12
+ this._auxBuf = auxBuffer;
13
+ this.rows = rows;
14
+ this.cols = cols;
15
+ this.lda = lda;
16
+ this.layout = layout;
17
+ }
18
+
19
+ /**
20
+ * Uploads a Float32Array or Float64Array matrix to GPU memory, row-major or
21
+ * column-major. `rows`/`cols` always describe the logical shape regardless
22
+ * of layout. `lda` is the stride between consecutive rows (row-major) or
23
+ * columns (column-major) — defaults to `cols`/`rows` respectively (dense,
24
+ * no padding). `data` must have at least `rows * lda` (row-major) or
25
+ * `cols * lda` (column-major) elements.
26
+ */
27
+ static from(data, rows, cols, lda, layout = "row-major") {
28
+ if (layout !== "row-major" && layout !== "column-major")
29
+ throw new Error("layout must be 'row-major' or 'column-major'.");
30
+ const isRowMajor = layout === "row-major";
31
+ if (lda === undefined) lda = isRowMajor ? cols : rows;
32
+
33
+ if (!(data instanceof Float32Array) && !(data instanceof Float64Array))
34
+ throw new Error("GpuMatrix.from expects a Float32Array or Float64Array.");
35
+ if (!Number.isInteger(rows) || rows <= 0)
36
+ throw new Error("rows must be a positive integer.");
37
+ if (!Number.isInteger(cols) || cols <= 0)
38
+ throw new Error("cols must be a positive integer.");
39
+ const minLda = isRowMajor ? cols : rows;
40
+ if (!Number.isInteger(lda) || lda < minLda)
41
+ throw new Error(`lda must be an integer >= ${isRowMajor ? "cols" : "rows"}.`);
42
+
43
+ // Row-major: `rows` chunks of length `lda` (only the first `cols` of each used).
44
+ // Column-major: `cols` chunks of length `lda` (only the first `rows` of each used).
45
+ const outerCount = isRowMajor ? rows : cols;
46
+ if (data.length < outerCount * lda)
47
+ throw new Error(
48
+ "data does not have enough elements for the given rows, cols, and lda.",
49
+ );
50
+
51
+ if (data instanceof Float64Array) {
52
+ const n = outerCount * lda;
53
+ const main = new Float32Array(n);
54
+ const aux = new Uint32Array(n);
55
+ for (let i = 0; i < n; i++) {
56
+ const packed = packF64(data[i]);
57
+ main[i] = packed[0];
58
+ aux[i] = packed[1];
59
+ }
60
+ const mainBuf = uploadBuffer(main, "gpu-matrix-f64-main", true);
61
+ const auxBuf = uploadBuffer(aux, "gpu-matrix-f64-aux", true);
62
+ return new GpuMatrix(mainBuf, rows, cols, lda, auxBuf, layout);
63
+ }
64
+
65
+ const buf = uploadBuffer(data.subarray(0, outerCount * lda), "gpu-matrix", true);
66
+ return new GpuMatrix(buf, rows, cols, lda, null, layout);
67
+ }
68
+
69
+ async read() {
70
+ const device = getDevice();
71
+ const enc = device.createCommandEncoder();
72
+ const rb = stageReadback(enc, this._buf);
73
+ device.queue.submit([enc.finish()]);
74
+
75
+ const isRowMajor = this.layout !== "column-major";
76
+ const outerCount = isRowMajor ? this.rows : this.cols;
77
+ const innerLen = isRowMajor ? this.cols : this.rows;
78
+
79
+ if (this._auxBuf) {
80
+ const encAux = device.createCommandEncoder();
81
+ const rbAux = stageReadback(encAux, this._auxBuf);
82
+ device.queue.submit([encAux.finish()]);
83
+
84
+ const [main, aux] = await Promise.all([
85
+ extractResult(rb, Float32Array),
86
+ extractResult(rbAux, Uint32Array),
87
+ ]);
88
+ const raw = new Float64Array(outerCount * this.lda);
89
+ for (let i = 0; i < raw.length; i++) raw[i] = unpackF64(main[i], aux[i]);
90
+ if (this.lda === innerLen) return raw;
91
+ const out = new Float64Array(outerCount * innerLen);
92
+ for (let r = 0; r < outerCount; r++)
93
+ out.set(raw.subarray(r * this.lda, r * this.lda + innerLen), r * innerLen);
94
+ return out;
95
+ }
96
+
97
+ const raw = await extractResult(rb, Float32Array);
98
+ if (this.lda === innerLen) return raw;
99
+ const out = new Float32Array(outerCount * innerLen);
100
+ for (let r = 0; r < outerCount; r++)
101
+ out.set(raw.subarray(r * this.lda, r * this.lda + innerLen), r * innerLen);
102
+ return out;
103
+ }
104
+
105
+ destroy() {
106
+ this._buf.destroy();
107
+ if (this._auxBuf) this._auxBuf.destroy();
108
+ }
109
+ }
@@ -12,12 +12,13 @@ export declare class GpuVector {
12
12
  /** Number of elements in the vector. */
13
13
  readonly length: number;
14
14
 
15
- // TODO: widen to Float32ArrayConstructor | Float64ArrayConstructor when Float64 support is added
16
15
  /** Typed array constructor used when reading data back from the GPU. */
17
- readonly dtype: Float32ArrayConstructor;
16
+ readonly dtype: Float32ArrayConstructor | Float64ArrayConstructor;
18
17
 
19
18
  /**
20
- * Uploads a Float32Array to GPU memory.
19
+ * Uploads a Float32Array or Float64Array to GPU memory. A Float64Array is
20
+ * packed as two f32s per element (WGSL has no f64 type) and stored across
21
+ * two GPU buffers internally; `read()` reassembles the original doubles.
21
22
  *
22
23
  * @param data - input vector data
23
24
  * @returns GpuVector backed by a GPU buffer
@@ -29,15 +30,18 @@ export declare class GpuVector {
29
30
  * await init();
30
31
  * const vec = GpuVector.from(new Float32Array([1, 2, 3, 4]));
31
32
  * console.log("length:", vec.length, "dtype:", vec.dtype.name);
33
+ *
34
+ * const dvec = GpuVector.from(new Float64Array([1.1, 2.2, 3.3]));
35
+ * console.log("dtype:", dvec.dtype.name); // Float64Array
32
36
  * ```
33
37
  */
34
- static from(data: Float32Array): GpuVector;
38
+ static from(data: Float32Array | Float64Array): GpuVector;
35
39
 
36
- // TODO: return type will widen to Promise<Float32Array | Float64Array> when Float64 support is added
37
40
  /**
38
41
  * Reads the vector data back from GPU memory.
39
42
  *
40
- * @returns vector data as a Float32Array
43
+ * @returns vector data as a Float32Array, or a Float64Array if this vector
44
+ * was created from one
41
45
  *
42
46
  * @example
43
47
  * ```js
@@ -49,7 +53,7 @@ export declare class GpuVector {
49
53
  * console.log(data);
50
54
  * ```
51
55
  */
52
- read(): Promise<Float32Array>;
56
+ read(): Promise<Float32Array | Float64Array>;
53
57
 
54
58
  /**
55
59
  * Destroys the underlying GPU buffer. Call when the vector is no longer needed
@@ -1,19 +1,31 @@
1
1
  import { getDevice } from "../init.mjs";
2
2
  import { uploadBuffer, stageReadback } from "../util/buffer.mjs";
3
3
  import { extractResult } from "../util/result.mjs";
4
-
5
- //TODO: Need to update this when we support Float64Array.
4
+ import { packF64, unpackF64 } from "../util/f64pack.mjs";
6
5
 
7
6
  export class GpuVector {
8
- constructor(buffer, length, dtype = Float32Array) {
7
+ constructor(buffer, length, dtype = Float32Array, auxBuffer = null) {
9
8
  this._buf = buffer;
9
+ this._auxBuf = auxBuffer;
10
10
  this.length = length;
11
11
  this.dtype = dtype;
12
12
  }
13
13
 
14
14
  static from(data) {
15
+ if (data instanceof Float64Array) {
16
+ const main = new Float32Array(data.length);
17
+ const aux = new Uint32Array(data.length);
18
+ for (let i = 0; i < data.length; i++) {
19
+ const packed = packF64(data[i]);
20
+ main[i] = packed[0];
21
+ aux[i] = packed[1];
22
+ }
23
+ const mainBuf = uploadBuffer(main, "gpu-vector-f64-main", true);
24
+ const auxBuf = uploadBuffer(aux, "gpu-vector-f64-aux", true);
25
+ return new GpuVector(mainBuf, data.length, Float64Array, auxBuf);
26
+ }
15
27
  if (!(data instanceof Float32Array)) {
16
- throw new Error("GpuVector.from expects a Float32Array.");
28
+ throw new Error("GpuVector.from expects a Float32Array or Float64Array.");
17
29
  }
18
30
  const buf = uploadBuffer(data, "gpu-vector", true);
19
31
  return new GpuVector(buf, data.length, data.constructor);
@@ -24,10 +36,24 @@ export class GpuVector {
24
36
  const enc = device.createCommandEncoder();
25
37
  const rb = stageReadback(enc, this._buf);
26
38
  device.queue.submit([enc.finish()]);
27
- return extractResult(rb, this.dtype);
39
+
40
+ if (!this._auxBuf) return extractResult(rb, this.dtype);
41
+
42
+ const encAux = device.createCommandEncoder();
43
+ const rbAux = stageReadback(encAux, this._auxBuf);
44
+ device.queue.submit([encAux.finish()]);
45
+
46
+ const [main, aux] = await Promise.all([
47
+ extractResult(rb, Float32Array),
48
+ extractResult(rbAux, Uint32Array),
49
+ ]);
50
+ const out = new Float64Array(this.length);
51
+ for (let i = 0; i < this.length; i++) out[i] = unpackF64(main[i], aux[i]);
52
+ return out;
28
53
  }
29
54
 
30
55
  destroy() {
31
56
  this._buf.destroy();
57
+ if (this._auxBuf) this._auxBuf.destroy();
32
58
  }
33
59
  }
@@ -0,0 +1,48 @@
1
+ import { GpuVector } from "../classes/GpuVector.mjs";
2
+
3
+ /**
4
+ * Computes the sum of absolute values of a vector of doubles in double
5
+ * precision: result = sum(|x[i]|). Each element of `x` is packed into a
6
+ * [main, aux] f32 pair (see `packF64`/`GpuVector`) since WGSL has no f64
7
+ * type; accumulation is done with f64add.wgsl's IEEE-754 binary64 addition.
8
+ *
9
+ * {@includeCode ../../examples/dasum/dasum.js}
10
+ *
11
+ * **Browser (standalone HTML):**
12
+ * {@includeCode ../../examples/dasum/web/dasum.html}
13
+ *
14
+ * @param device - GPUDevice from `init()`
15
+ * @param n - number of elements (must be a positive integer)
16
+ * @param x - Float64Array input vector
17
+ * @param incx - stride for x (must be a positive integer)
18
+ * @returns absolute sum scalar — always a CPU readback, even for GpuVector inputs
19
+ * @see <a href="https://github.com/manit2004/wgblas/blob/main/src/dasum/dasum.mjs#L18">Source code: dasum.mjs (L18)</a>
20
+ * @category BLAS Level 1
21
+ */
22
+ export declare function dasum(
23
+ device: GPUDevice,
24
+ n: number,
25
+ x: Float64Array,
26
+ incx: number,
27
+ ): Promise<{ asum: number } | { asum: number; gpuTimeMs: number }>;
28
+
29
+ /**
30
+ * Computes the sum of absolute values of a vector of doubles in double
31
+ * precision: result = sum(|x[i]|).
32
+ *
33
+ * {@includeCode ../../examples/dasum/gpuvec.dasum.js}
34
+ *
35
+ * @param device - GPUDevice from `init()`
36
+ * @param n - number of elements (must be a positive integer)
37
+ * @param x - Float64Array-backed GpuVector input vector
38
+ * @param incx - stride for x (must be a positive integer)
39
+ * @returns absolute sum scalar — always a CPU readback, even for GpuVector inputs
40
+ * @see <a href="https://github.com/manit2004/wgblas/blob/main/src/dasum/dasum.mjs#L18">Source code: dasum.mjs (L18)</a>
41
+ * @category BLAS Level 1
42
+ */
43
+ export declare function dasum(
44
+ device: GPUDevice,
45
+ n: number,
46
+ x: GpuVector,
47
+ incx: number,
48
+ ): Promise<{ asum: number } | { asum: number; gpuTimeMs: number }>;
@@ -0,0 +1,121 @@
1
+ import {
2
+ createStorageBuffer,
3
+ createParamsBuffer,
4
+ createResultBuffer,
5
+ stageReadback,
6
+ destroyBuffers,
7
+ } from "../util/buffer.mjs";
8
+ import { createBindGroup } from "../util/bindgroup.mjs";
9
+ import { runComputePass, submit } from "../util/compute.mjs";
10
+ import { extractTimestamp } from "../util/benchmark.mjs";
11
+ import { extractResult } from "../util/result.mjs";
12
+ import { getPipeline } from "../util/pipeline.mjs";
13
+ import { unpackF64 } from "../util/f64pack.mjs";
14
+ import { GpuVector } from "../classes/GpuVector.mjs";
15
+
16
+ const WGS = 64; // workgroup size
17
+
18
+ export async function dasum(device, n, x, incx) {
19
+ const xIsGpu = x instanceof GpuVector;
20
+
21
+ if (!(device instanceof GPUDevice))
22
+ throw new Error("device must be a GPUDevice.");
23
+ if (!Number.isInteger(n) || !Number.isInteger(incx))
24
+ throw new Error("n and incx must be integers.");
25
+ if (incx <= 0) throw new Error("incx must be positive.");
26
+ if (!xIsGpu && !(x instanceof Float64Array))
27
+ throw new Error("x must be a Float64Array or GpuVector.");
28
+ if (xIsGpu && x.dtype !== Float64Array)
29
+ throw new Error("x must be a Float64Array-backed GpuVector.");
30
+ if (n <= 0) return { asum: 0 };
31
+ if (x.length < (n - 1) * incx + 1)
32
+ throw new Error(
33
+ "x does not have enough elements for the given n and incx.",
34
+ );
35
+
36
+ // dasum.wgsl/reduction/sumF64.wgsl are each concatenated with f64add.wgsl
37
+ // (reusing its decode/encode/computeSum/Packed — WGSL has no #include).
38
+ // f64add.wgsl declares no bindings/entry point of its own, so each
39
+ // concatenated module has exactly one @compute entry — omitting entryPoint
40
+ // here lets getPipeline auto-detect it, the stable path (see pipeline.mjs).
41
+ const pipelineMain = await getPipeline(device, ["f64add", "dasum"]);
42
+ const pipelineReduce = await getPipeline(device, ["f64add", "reduction/sumF64"]);
43
+
44
+ let xVec = null;
45
+ let partialsMainBuffer = null;
46
+ let partialsAuxBuffer = null;
47
+ let resultMainBuffer = null;
48
+ let resultAuxBuffer = null;
49
+ let paramsBuffer = null;
50
+ let readMainBuffer = null;
51
+ let readAuxBuffer = null;
52
+
53
+ try {
54
+ xVec = xIsGpu ? x : GpuVector.from(x);
55
+ partialsMainBuffer = createStorageBuffer(2 * WGS * 4, "dasum-partialsMain"); // 2*WGS partial sums, main halves
56
+ partialsAuxBuffer = createStorageBuffer(2 * WGS * 4, "dasum-partialsAux"); // 2*WGS partial sums, aux halves (raw u32 bits)
57
+ resultMainBuffer = createResultBuffer(4, "dasum-result-main"); // final main half
58
+ resultAuxBuffer = createResultBuffer(4, "dasum-result-aux"); // final aux half (raw u32 bits)
59
+ paramsBuffer = createParamsBuffer(
60
+ [
61
+ { value: n, type: "u32" },
62
+ { value: incx, type: "u32" },
63
+ ],
64
+ "dasum-params",
65
+ );
66
+
67
+ const bgMain = createBindGroup(
68
+ pipelineMain.getBindGroupLayout(0),
69
+ [xVec._buf, xVec._auxBuf, partialsMainBuffer, partialsAuxBuffer, paramsBuffer],
70
+ );
71
+ const { commandEncoder: enc1, ts: ts1 } = runComputePass(
72
+ pipelineMain,
73
+ bgMain,
74
+ 2 * WGS,
75
+ ); // dispatch 2*WGS workgroups
76
+
77
+ submit(enc1);
78
+
79
+ const bgReduce = createBindGroup(
80
+ pipelineReduce.getBindGroupLayout(0),
81
+ [partialsMainBuffer, partialsAuxBuffer, resultMainBuffer, resultAuxBuffer],
82
+ );
83
+ const { commandEncoder: enc2, ts: ts2 } = runComputePass(
84
+ pipelineReduce,
85
+ bgReduce,
86
+ 1,
87
+ ); // dispatch 1 workgroup to reduce the partial sums to a single result
88
+ readMainBuffer = stageReadback(enc2, resultMainBuffer);
89
+ readAuxBuffer = stageReadback(enc2, resultAuxBuffer);
90
+
91
+ submit(enc2);
92
+
93
+ const mainPromise = extractResult(readMainBuffer, Float32Array);
94
+ const auxPromise = extractResult(readAuxBuffer, Uint32Array);
95
+ readMainBuffer = null; // ownership transferred — extractResult's own finally destroys it
96
+ readAuxBuffer = null;
97
+
98
+ const [gpuTime1, gpuTime2, mainArr, auxArr] = await Promise.all([
99
+ extractTimestamp(ts1),
100
+ extractTimestamp(ts2),
101
+ mainPromise,
102
+ auxPromise,
103
+ ]);
104
+
105
+ // asum is always a scalar readback — both paths return { asum }
106
+ const asum = unpackF64(mainArr[0], auxArr[0]);
107
+ if (gpuTime1 !== undefined && gpuTime2 !== undefined)
108
+ return { asum, gpuTimeMs: gpuTime1 + gpuTime2 };
109
+ return { asum };
110
+ } finally {
111
+ if (!xIsGpu && xVec) xVec.destroy();
112
+ if (partialsMainBuffer) destroyBuffers(partialsMainBuffer);
113
+ if (partialsAuxBuffer) destroyBuffers(partialsAuxBuffer);
114
+ if (resultMainBuffer) destroyBuffers(resultMainBuffer);
115
+ if (resultAuxBuffer) destroyBuffers(resultAuxBuffer);
116
+ if (paramsBuffer) destroyBuffers(paramsBuffer);
117
+ // Only reached if submit(enc2) threw before ownership was transferred above.
118
+ if (readMainBuffer) destroyBuffers(readMainBuffer);
119
+ if (readAuxBuffer) destroyBuffers(readAuxBuffer);
120
+ }
121
+ }
package/src/init.mjs CHANGED
@@ -41,7 +41,9 @@ export async function init({
41
41
  }
42
42
 
43
43
  _benchmarkEnabled = benchmark;
44
- _device = await _adapter.requestDevice(benchmarkMode(_adapter, benchmark));
44
+ const bmConfig = benchmarkMode(_adapter, benchmark);
45
+ const features = [...(bmConfig.requiredFeatures ?? [])];
46
+ _device = await _adapter.requestDevice({ requiredFeatures: features });
45
47
  // Fires for any GPU error not caught by a pushErrorScope/popErrorScope pair — surfaces silent GPU failures to the console.
46
48
  // See: https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/uncapturederror_event
47
49
  _device.addEventListener("uncapturederror", (e) => {