wgblas 1.2.1 → 2.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 (96) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +54 -72
  3. package/dist/wgblas.browser.js +1637 -858
  4. package/index.d.mts +51 -6
  5. package/index.mjs +8 -0
  6. package/package.json +56 -2
  7. package/src/classes/GpuMatrix.mjs +17 -10
  8. package/src/classes/GpuVector.mjs +28 -10
  9. package/src/dasum/dasum.d.mts +6 -6
  10. package/src/dasum/dasum.mjs +25 -21
  11. package/src/devdocs.mjs +13 -0
  12. package/src/idamax/idamax.d.mts +69 -0
  13. package/src/idamax/idamax.mjs +130 -0
  14. package/src/init.mjs +115 -49
  15. package/src/isamax/isamax.d.mts +21 -3
  16. package/src/isamax/isamax.mjs +16 -14
  17. package/src/random/random.d.mts +1 -0
  18. package/src/sasum/sasum.d.mts +3 -3
  19. package/src/sasum/sasum.mjs +15 -13
  20. package/src/saxpy/saxpy.d.mts +3 -3
  21. package/src/saxpy/saxpy.mjs +10 -8
  22. package/src/scopy/scopy.d.mts +3 -3
  23. package/src/scopy/scopy.mjs +10 -8
  24. package/src/sdot/sdot.d.mts +3 -3
  25. package/src/sdot/sdot.mjs +16 -14
  26. package/src/sgemm/sgemm.d.mts +102 -0
  27. package/src/sgemm/sgemm.mjs +208 -0
  28. package/src/sgemmtr/sgemmtr.d.mts +104 -0
  29. package/src/sgemmtr/sgemmtr.mjs +204 -0
  30. package/src/sgemv/sgemv.d.mts +1 -38
  31. package/src/sgemv/sgemv.mjs +42 -26
  32. package/src/sger/sger.d.mts +1 -34
  33. package/src/sger/sger.mjs +12 -8
  34. package/src/shaders/block_transfer.wgsl +42 -0
  35. package/src/shaders/dasum.wgsl +3 -2
  36. package/src/shaders/f64/dekker.wgsl +4 -85
  37. package/src/shaders/f64/utils/abs.wgsl +10 -0
  38. package/src/shaders/f64/utils/add.wgsl +77 -0
  39. package/src/shaders/f64/utils/equal.wgsl +7 -0
  40. package/src/shaders/f64/utils/greater.wgsl +12 -0
  41. package/src/shaders/f64/utils/multiply.wgsl +81 -0
  42. package/src/shaders/idamax.wgsl +96 -0
  43. package/src/shaders/index.mjs +164 -14
  44. package/src/shaders/reduction/argmaxF64.wgsl +50 -0
  45. package/src/shaders/reduction/scaledSum.wgsl +65 -0
  46. package/src/shaders/reduction/sumF64.wgsl +2 -2
  47. package/src/shaders/sgemm_large.wgsl +206 -0
  48. package/src/shaders/sgemm_small.wgsl +212 -0
  49. package/src/shaders/sgemmtr_large.wgsl +120 -0
  50. package/src/shaders/sgemmtr_small.wgsl +113 -0
  51. package/src/shaders/sgemv_n.wgsl +3 -1
  52. package/src/shaders/sgemv_t.wgsl +3 -1
  53. package/src/shaders/snrm2.wgsl +72 -23
  54. package/src/shaders/ssymv.wgsl +3 -1
  55. package/src/shaders/symmetrize.wgsl +31 -0
  56. package/src/shaders/triangularize.wgsl +44 -0
  57. package/src/snrm2/snrm2.d.mts +3 -3
  58. package/src/snrm2/snrm2.mjs +33 -21
  59. package/src/srot/srot.d.mts +3 -5
  60. package/src/srot/srot.mjs +11 -9
  61. package/src/srotm/srotm.d.mts +3 -5
  62. package/src/srotm/srotm.mjs +19 -10
  63. package/src/sscal/sscal.d.mts +4 -4
  64. package/src/sscal/sscal.mjs +12 -10
  65. package/src/sswap/sswap.d.mts +3 -3
  66. package/src/sswap/sswap.mjs +11 -9
  67. package/src/ssymm/ssymm.d.mts +103 -0
  68. package/src/ssymm/ssymm.mjs +218 -0
  69. package/src/ssymv/ssymv.d.mts +1 -36
  70. package/src/ssymv/ssymv.mjs +12 -8
  71. package/src/ssyr/ssyr.d.mts +1 -30
  72. package/src/ssyr/ssyr.mjs +11 -7
  73. package/src/ssyr2/ssyr2.d.mts +1 -34
  74. package/src/ssyr2/ssyr2.mjs +12 -8
  75. package/src/ssyr2k/ssyr2k.d.mts +100 -0
  76. package/src/ssyr2k/ssyr2k.mjs +202 -0
  77. package/src/ssyrk/ssyrk.d.mts +90 -0
  78. package/src/ssyrk/ssyrk.mjs +177 -0
  79. package/src/strmm/strmm.d.mts +100 -0
  80. package/src/strmm/strmm.mjs +226 -0
  81. package/src/strmv/strmv.d.mts +1 -36
  82. package/src/strmv/strmv.mjs +12 -8
  83. package/src/strsm/strsm.d.mts +99 -0
  84. package/src/strsm/strsm.mjs +360 -0
  85. package/src/strsv/strsv.d.mts +1 -32
  86. package/src/strsv/strsv.mjs +18 -12
  87. package/src/util/benchmark.mjs +4 -6
  88. package/src/util/bindgroup.mjs +1 -3
  89. package/src/util/buffer.mjs +116 -20
  90. package/src/util/compute.mjs +12 -12
  91. package/src/util/constants.mjs +57 -0
  92. package/src/util/device.mjs +34 -0
  93. package/src/util/f64.mjs +3 -3
  94. package/src/util/pipeline.mjs +5 -6
  95. package/src/util/workgroup.mjs +55 -7
  96. package/src/shaders/browser-shaders.mjs +0 -55
@@ -11,6 +11,7 @@ import { extractTimestamp } from "../util/benchmark.mjs";
11
11
  import { getPipeline } from "../util/pipeline.mjs";
12
12
  import { calcWorkgroups } from "../util/workgroup.mjs";
13
13
  import { GpuVector } from "../classes/GpuVector.mjs";
14
+ import { requireSameDevice } from "../util/device.mjs";
14
15
 
15
16
  export async function sswap(device, n, x, incx, y, incy) {
16
17
  const xIsGpu = x instanceof GpuVector;
@@ -18,6 +19,7 @@ export async function sswap(device, n, x, incx, y, incy) {
18
19
 
19
20
  if (!(device instanceof GPUDevice))
20
21
  throw new Error("device must be a GPUDevice.");
22
+ requireSameDevice(device, "sswap", { x, y });
21
23
  if (
22
24
  !Number.isInteger(n) ||
23
25
  !Number.isInteger(incx) ||
@@ -53,9 +55,9 @@ export async function sswap(device, n, x, incx, y, incy) {
53
55
  let yReadBuffer = null;
54
56
 
55
57
  try {
56
- xBuffer = xIsGpu ? x._buf : uploadBuffer(x, "sswap-x", true);
57
- yBuffer = yIsGpu ? y._buf : uploadBuffer(y, "sswap-y", true);
58
- paramsBuffer = createParamsBuffer(
58
+ xBuffer = xIsGpu ? x._buf : uploadBuffer(device, x, "sswap-x", true);
59
+ yBuffer = yIsGpu ? y._buf : uploadBuffer(device, y, "sswap-y", true);
60
+ paramsBuffer = createParamsBuffer(device,
59
61
  [
60
62
  { value: n, type: "u32" },
61
63
  { value: incx, type: "u32" },
@@ -64,20 +66,20 @@ export async function sswap(device, n, x, incx, y, incy) {
64
66
  "sswap-params",
65
67
  );
66
68
 
67
- const bindGroup = createBindGroup(pipeline.getBindGroupLayout(0), [
69
+ const bindGroup = createBindGroup(device, pipeline.getBindGroupLayout(0), [
68
70
  xBuffer,
69
71
  yBuffer,
70
72
  paramsBuffer,
71
73
  ]);
72
- const { commandEncoder, ts } = runComputePass(
74
+ const { commandEncoder, ts } = runComputePass(device,
73
75
  pipeline,
74
76
  bindGroup,
75
- calcWorkgroups(n),
77
+ calcWorkgroups(device, n),
76
78
  );
77
- xReadBuffer = xIsGpu ? null : stageReadback(commandEncoder, xBuffer);
78
- yReadBuffer = yIsGpu ? null : stageReadback(commandEncoder, yBuffer);
79
+ xReadBuffer = xIsGpu ? null : stageReadback(device, commandEncoder, xBuffer);
80
+ yReadBuffer = yIsGpu ? null : stageReadback(device, commandEncoder, yBuffer);
79
81
 
80
- submit(commandEncoder);
82
+ submit(device, commandEncoder);
81
83
 
82
84
  const gpuTimeMs = await extractTimestamp(ts);
83
85
 
@@ -0,0 +1,103 @@
1
+ import { GpuMatrix } from "../classes/GpuMatrix.mjs";
2
+
3
+ /**
4
+ * Performs the symmetric matrix-matrix operation
5
+ * C := alpha * A * B + beta * C (`side='left'`) or
6
+ * C := alpha * B * A + beta * C (`side='right'`) — `A` is symmetric, only
7
+ * its `uplo` triangle stored; `B` and `C` are general m×n matrices.
8
+ *
9
+ * - `side='left'`: `A` is m×m — `A` premultiplies `B`
10
+ * - `side='right'`: `A` is n×n — `A` postmultiplies `B`
11
+ *
12
+ * No dedicated fused kernel — a `symmetrize` pass materializes a dense
13
+ * copy of `A` (mirroring the unstored triangle), then a plain `sgemm`
14
+ * pass (`sgemm_small.wgsl`/`sgemm_large.wgsl`, unmodified) does the
15
+ * actual multiply, both on one command encoder.
16
+ *
17
+ * {@includeCode ../../examples/ssymm/ssymm.js}
18
+ *
19
+ * **Browser (standalone HTML):**
20
+ * {@includeCode ../../examples/ssymm/web/ssymm.html}
21
+ *
22
+ * @param device - GPUDevice from `init()`
23
+ * @param side - `'left'` for A*B, `'right'` for B*A
24
+ * @param uplo - `'lower'` if only `A`'s lower triangle is stored, `'upper'` for upper
25
+ * @param m - rows of B and C
26
+ * @param n - columns of B and C
27
+ * @param alpha - scalar multiplier for the matrix product
28
+ * @param A - Float32Array, symmetric, row-major or column-major (see `layout`)
29
+ * @param lda - leading dimension of A as stored
30
+ * @param B - Float32Array, row-major or column-major (see `layout`)
31
+ * @param ldb - leading dimension of B as stored
32
+ * @param beta - scalar multiplier for C
33
+ * @param C - Float32Array input/output matrix, row-major or column-major
34
+ * @param ldc - leading dimension of C as stored
35
+ * @param layout - storage layout shared by A/B/C when they're Float32Array
36
+ * (default: `'row-major'`) — column-major A keeps representing the same
37
+ * symmetric matrix but flips which physical triangle looks stored, so
38
+ * `uplo` is adjusted internally to compensate
39
+ * @returns updated C as a Float32Array
40
+ * @see <a href="https://github.com/manit2004/wgblas/blob/main/src/ssymm/ssymm.mjs#L23">Source code: ssymm.mjs (L23)</a>
41
+ * @category BLAS Level 3
42
+ */
43
+ export declare function ssymm(
44
+ device: GPUDevice,
45
+ side: 'left' | 'right',
46
+ uplo: 'lower' | 'upper',
47
+ m: number,
48
+ n: number,
49
+ alpha: number,
50
+ A: Float32Array,
51
+ lda: number,
52
+ B: Float32Array,
53
+ ldb: number,
54
+ beta: number,
55
+ C: Float32Array,
56
+ ldc: number,
57
+ layout?: 'row-major' | 'column-major',
58
+ ): Promise<{ C: Float32Array; gpuTimeMs?: number }>;
59
+
60
+ /**
61
+ * Performs the symmetric matrix-matrix operation
62
+ * C := alpha * A * B + beta * C (`side='left'`) or
63
+ * C := alpha * B * A + beta * C (`side='right'`)
64
+ *
65
+ * A, B, and C are all kept GPU-resident. Each matrix's own `layout` (set at
66
+ * `GpuMatrix.from` time) determines the operation — there is no separate
67
+ * `layout` argument here. A and B must be GpuMatrix whenever C is, and vice
68
+ * versa — mixing a GpuMatrix with a plain Float32Array is not supported.
69
+ *
70
+ * {@includeCode ../../examples/ssymm/gpu.ssymm.js}
71
+ *
72
+ * @param device - GPUDevice from `init()`
73
+ * @param side - `'left'` for A*B, `'right'` for B*A
74
+ * @param uplo - `'lower'` if only `A`'s lower triangle is stored, `'upper'` for upper
75
+ * @param m - rows of B and C
76
+ * @param n - columns of B and C
77
+ * @param alpha - scalar multiplier for the matrix product
78
+ * @param A - GpuMatrix, symmetric
79
+ * @param lda - leading dimension of A (must equal A.lda)
80
+ * @param B - GpuMatrix
81
+ * @param ldb - leading dimension of B (must equal B.lda)
82
+ * @param beta - scalar multiplier for C
83
+ * @param C - GpuMatrix (mutated in place)
84
+ * @param ldc - leading dimension of C (must equal C.lda)
85
+ * @returns no C — it stays GPU-resident; call `C.read()` yourself for a CPU readback (see the example)
86
+ * @see <a href="https://github.com/manit2004/wgblas/blob/main/src/ssymm/ssymm.mjs#L23">Source code: ssymm.mjs (L23)</a>
87
+ * @category BLAS Level 3
88
+ */
89
+ export declare function ssymm(
90
+ device: GPUDevice,
91
+ side: 'left' | 'right',
92
+ uplo: 'lower' | 'upper',
93
+ m: number,
94
+ n: number,
95
+ alpha: number,
96
+ A: GpuMatrix,
97
+ lda: number,
98
+ B: GpuMatrix,
99
+ ldb: number,
100
+ beta: number,
101
+ C: GpuMatrix,
102
+ ldc: number,
103
+ ): Promise<{ gpuTimeMs?: number }>;
@@ -0,0 +1,218 @@
1
+ import {
2
+ uploadBuffer,
3
+ createParamsBuffer,
4
+ createStorageBuffer,
5
+ stageReadback,
6
+ destroyBuffers,
7
+ vec4ViewBinding,
8
+ } from "../util/buffer.mjs";
9
+ import { createBindGroup } from "../util/bindgroup.mjs";
10
+ import { beginTimedEncoder, encodePass, submit } from "../util/compute.mjs";
11
+ import { extractResult } from "../util/result.mjs";
12
+ import { resolveTimestamp, extractTimestamp } from "../util/benchmark.mjs";
13
+ import { getPipeline } from "../util/pipeline.mjs";
14
+ import { GpuMatrix } from "../classes/GpuMatrix.mjs";
15
+ import { requireWorkgroupCount } from "../util/workgroup.mjs";
16
+ import { BM_SMALL, BN_SMALL, BM_LARGE, BN_LARGE, LARGE_TILE_WORKGROUP_THRESHOLD } from "../util/constants.mjs";
17
+ import { TILE_WG_2D } from "../util/constants.mjs";
18
+ import { requireSameDevice } from "../util/device.mjs";
19
+
20
+
21
+ // ssymm: C := alpha*A*B + beta*C (side='left') or alpha*B*A + beta*C
22
+ // (side='right'), A symmetric. No fused kernel — symmetrize then sgemm,
23
+ // both on one command encoder. See symmetrize.wgsl.
24
+ export async function ssymm(
25
+ device, side, uplo, m, n, alpha, A, lda, B, ldb, beta, C, ldc, layout = "row-major",
26
+ ) {
27
+ const AIsGpu = A instanceof GpuMatrix;
28
+ const BIsGpu = B instanceof GpuMatrix;
29
+ const CIsGpu = C instanceof GpuMatrix;
30
+
31
+ if (!(device instanceof GPUDevice))
32
+ throw new Error("device must be a GPUDevice.");
33
+ requireSameDevice(device, "ssymm", { A, B, C });
34
+ if (side !== "left" && side !== "right")
35
+ throw new Error("side must be 'left' or 'right'.");
36
+ if (uplo !== "lower" && uplo !== "upper")
37
+ throw new Error("uplo must be 'lower' or 'upper'.");
38
+ if (layout !== "row-major" && layout !== "column-major")
39
+ throw new Error("layout must be 'row-major' or 'column-major'.");
40
+ if (typeof alpha !== "number")
41
+ throw new Error("alpha must be a number.");
42
+ if (Number.isNaN(alpha)) throw new Error("alpha must not be NaN.");
43
+ if (!Number.isFinite(alpha)) throw new Error("alpha must be finite.");
44
+ if (typeof beta !== "number")
45
+ throw new Error("beta must be a number.");
46
+ if (Number.isNaN(beta)) throw new Error("beta must not be NaN.");
47
+ if (!Number.isFinite(beta)) throw new Error("beta must be finite.");
48
+ if (
49
+ !Number.isInteger(m) || !Number.isInteger(n) ||
50
+ !Number.isInteger(lda) || !Number.isInteger(ldb) || !Number.isInteger(ldc)
51
+ )
52
+ throw new Error("m, n, lda, ldb, and ldc must be integers.");
53
+ if (!AIsGpu && !(A instanceof Float32Array))
54
+ throw new Error("A must be a Float32Array or GpuMatrix.");
55
+ if (!BIsGpu && !(B instanceof Float32Array))
56
+ throw new Error("B must be a Float32Array or GpuMatrix.");
57
+ if (!CIsGpu && !(C instanceof Float32Array))
58
+ throw new Error("C must be a Float32Array or GpuMatrix.");
59
+ if ((AIsGpu || BIsGpu) && !CIsGpu)
60
+ throw new Error("C must be a GpuMatrix when A or B is a GpuMatrix.");
61
+ if (CIsGpu && (!AIsGpu || !BIsGpu))
62
+ throw new Error("A and B must be GpuMatrix when C is a GpuMatrix.");
63
+ if (m < 0 || n < 0) throw new Error("m and n must be non-negative.");
64
+ if (m === 0 || n === 0) return CIsGpu ? {} : { C };
65
+
66
+ const effLayoutA = AIsGpu ? A.layout : layout;
67
+ const effLayoutB = BIsGpu ? B.layout : layout;
68
+ const effLayoutC = CIsGpu ? C.layout : layout;
69
+
70
+ // A: symmetric, order = m (side='left') or n (side='right').
71
+ const aOrder = side === "left" ? m : n;
72
+ if (lda < aOrder) throw new Error("lda must be >= " + (side === "left" ? "m" : "n") + ".");
73
+ if (AIsGpu) {
74
+ if (lda !== A.lda) throw new Error("lda must match A.lda when A is a GpuMatrix.");
75
+ if (A.rows < aOrder || A.cols < aOrder) throw new Error("A is too small for the given m/n and side.");
76
+ } else if (A.length < (aOrder - 1) * lda + aOrder) {
77
+ throw new Error("A does not have enough elements for the given dimensions and lda.");
78
+ }
79
+
80
+ // B: always m x n, no trans flag — same shape rule as sgemm's C.
81
+ const bOuter = effLayoutB === "column-major" ? n : m;
82
+ const bInner = effLayoutB === "column-major" ? m : n;
83
+ if (ldb < bInner)
84
+ throw new Error(`ldb must be >= ${effLayoutB === "column-major" ? "rows" : "cols"} of B as stored.`);
85
+ if (BIsGpu) {
86
+ if (ldb !== B.lda) throw new Error("ldb must match B.lda when B is a GpuMatrix.");
87
+ if (B.rows < m || B.cols < n) throw new Error("B is too small for the given m and n.");
88
+ } else if (B.length < (bOuter - 1) * ldb + bInner) {
89
+ throw new Error("B does not have enough elements for the given dimensions and ldb.");
90
+ }
91
+
92
+ // C: always m x n.
93
+ const cOuter = effLayoutC === "column-major" ? n : m;
94
+ const cInner = effLayoutC === "column-major" ? m : n;
95
+ if (ldc < cInner)
96
+ throw new Error(`ldc must be >= ${effLayoutC === "column-major" ? "rows" : "cols"} of C as stored.`);
97
+ if (CIsGpu) {
98
+ if (ldc !== C.lda) throw new Error("ldc must match C.lda when C is a GpuMatrix.");
99
+ if (C.rows < m || C.cols < n) throw new Error("C is too small for the given m and n.");
100
+ } else if (C.length < (cOuter - 1) * ldc + cInner) {
101
+ throw new Error("C does not have enough elements for the given dimensions and ldc.");
102
+ }
103
+
104
+ // A = A^T, so column-major storage is still A, but the populated
105
+ // triangle swaps — uplo flips (same reasoning ssyr/ssyrk use).
106
+ const uploEffA = effLayoutA === "column-major" ? (uplo === "lower" ? "upper" : "lower") : uplo;
107
+
108
+ const transB = effLayoutB === "column-major" ? "transpose" : "no-transpose";
109
+ const transDense = "no-transpose"; // Adense is always row-major
110
+
111
+ // X*Y (X=Adense,Y=B for side='left', swapped for 'right'). Column-major
112
+ // C: compute C^T instead (swap X/Y, flip trans, swap m_g/n_g) — sgemm's own trick.
113
+ let mg = m, ng = n;
114
+ const kg = aOrder;
115
+ let transX = side === "left" ? transDense : transB;
116
+ let transY = side === "left" ? transB : transDense;
117
+ const flip = (t) => (t === "no-transpose" ? "transpose" : "no-transpose");
118
+ let swapXY = side === "right"; // which JS buffer plays "X" vs "Y" below
119
+ if (effLayoutC === "column-major") {
120
+ [transX, transY] = [flip(transY), flip(transX)];
121
+ swapXY = !swapXY;
122
+ [mg, ng] = [ng, mg];
123
+ }
124
+
125
+ const ldDense = aOrder; // Adense is tightly packed, row-major
126
+ const largeWgX = Math.ceil(ng / BN_LARGE);
127
+ const largeWgY = Math.ceil(mg / BM_LARGE);
128
+ const useLargeTile = largeWgX * largeWgY >= LARGE_TILE_WORKGROUP_THRESHOLD;
129
+ const gemmPipeline = await getPipeline(device, useLargeTile ? "sgemm_large" : "sgemm_small");
130
+ const symPipeline = await getPipeline(device, "symmetrize");
131
+ const gemmWgCount = useLargeTile
132
+ ? {
133
+ x: requireWorkgroupCount(device, largeWgX, "ssymm", "x"),
134
+ y: requireWorkgroupCount(device, largeWgY, "ssymm", "y"),
135
+ }
136
+ : {
137
+ x: requireWorkgroupCount(device, Math.ceil(ng / BN_SMALL), "ssymm", "x"),
138
+ y: requireWorkgroupCount(device, Math.ceil(mg / BM_SMALL), "ssymm", "y"),
139
+ };
140
+
141
+ const ABuffer = AIsGpu ? A._buf : uploadBuffer(device, A, "ssymm-A", false);
142
+ const BBuffer = BIsGpu ? B._buf : uploadBuffer(device, B, "ssymm-B", false);
143
+ const CBuffer = CIsGpu ? C._buf : uploadBuffer(device, C, "ssymm-C", true);
144
+ const AdenseBuffer = createStorageBuffer(device, aOrder * ldDense * 4, "ssymm-Adense");
145
+ let symParams = null, gemmParams = null;
146
+
147
+ try {
148
+ symParams = createParamsBuffer(device,
149
+ [
150
+ { value: aOrder, type: "u32" },
151
+ { value: lda, type: "u32" },
152
+ { value: ldDense, type: "u32" },
153
+ { value: uploEffA === "upper" ? 1 : 0, type: "u32" },
154
+ ],
155
+ "ssymm-sym-params",
156
+ );
157
+ const symBindGroup = createBindGroup(device, symPipeline.getBindGroupLayout(0), [ABuffer, AdenseBuffer, symParams]);
158
+
159
+ // X/Y buffers and their own ld, matching swapXY above.
160
+ const XBuffer = swapXY ? BBuffer : AdenseBuffer;
161
+ const ldX = swapXY ? ldb : ldDense;
162
+ const YBuffer = swapXY ? AdenseBuffer : BBuffer;
163
+ const ldY = swapXY ? ldDense : ldb;
164
+
165
+ gemmParams = createParamsBuffer(device,
166
+ [
167
+ { value: mg, type: "u32" },
168
+ { value: ng, type: "u32" },
169
+ { value: kg, type: "u32" },
170
+ { value: alpha, type: "f32" },
171
+ { value: beta, type: "f32" },
172
+ { value: ldX, type: "u32" },
173
+ { value: ldY, type: "u32" },
174
+ { value: ldc, type: "u32" },
175
+ { value: transX === "transpose" ? 1 : 0, type: "u32" },
176
+ { value: transY === "transpose" ? 1 : 0, type: "u32" },
177
+ ],
178
+ "ssymm-gemm-params",
179
+ );
180
+ const gemmBindGroup = createBindGroup(device, gemmPipeline.getBindGroupLayout(0), [
181
+ XBuffer,
182
+ vec4ViewBinding(device, XBuffer),
183
+ YBuffer,
184
+ vec4ViewBinding(device, YBuffer),
185
+ CBuffer,
186
+ gemmParams,
187
+ ]);
188
+
189
+ const { commandEncoder, querySet } = beginTimedEncoder(device);
190
+ const symDesc = querySet ? { timestampWrites: { querySet, beginningOfPassWriteIndex: 0 } } : undefined;
191
+ const gemmDesc = querySet ? { timestampWrites: { querySet, endOfPassWriteIndex: 1 } } : undefined;
192
+ encodePass(commandEncoder, symPipeline, symBindGroup, { x: Math.ceil(aOrder / TILE_WG_2D), y: Math.ceil(aOrder / TILE_WG_2D) }, symDesc);
193
+ encodePass(commandEncoder, gemmPipeline, gemmBindGroup, gemmWgCount, gemmDesc);
194
+
195
+ const ts = resolveTimestamp(device, commandEncoder, querySet);
196
+ const readBuffer = CIsGpu ? null : stageReadback(device, commandEncoder, CBuffer);
197
+
198
+ submit(device, commandEncoder);
199
+
200
+ const gpuTimeMs = await extractTimestamp(ts);
201
+
202
+ if (CIsGpu) {
203
+ if (gpuTimeMs !== undefined) return { gpuTimeMs };
204
+ return {};
205
+ }
206
+
207
+ const result = await extractResult(readBuffer, Float32Array);
208
+ if (gpuTimeMs !== undefined) return { C: result, gpuTimeMs };
209
+ return { C: result };
210
+ } finally {
211
+ if (!AIsGpu) destroyBuffers(ABuffer);
212
+ if (!BIsGpu) destroyBuffers(BBuffer);
213
+ if (!CIsGpu) destroyBuffers(CBuffer);
214
+ destroyBuffers(AdenseBuffer);
215
+ if (symParams) destroyBuffers(symParams);
216
+ if (gemmParams) destroyBuffers(gemmParams);
217
+ }
218
+ }
@@ -44,41 +44,6 @@ export declare function ssymv(
44
44
  layout?: 'row-major' | 'column-major',
45
45
  ): Promise<{ y: Float32Array; gpuTimeMs?: number }>;
46
46
 
47
- /**
48
- * Performs the symmetric matrix-vector operation y = alpha * A * x + beta * y
49
- *
50
- * A is kept GPU-resident; x and y are CPU Float32Arrays. `A`'s own `layout`
51
- * (set at `GpuMatrix.from` time) determines the operation — there is no
52
- * separate `layout` argument here.
53
- *
54
- * @param device - GPUDevice from `init()`
55
- * @param uplo - `'lower'` to use the lower triangle, `'upper'` to use the upper triangle
56
- * @param n - order of the matrix A
57
- * @param alpha - scalar multiplier for A*x
58
- * @param A - GpuMatrix, GPU-resident
59
- * @param lda - leading dimension of A (must equal A.lda)
60
- * @param x - Float32Array input vector
61
- * @param incx - stride for x (must be a positive integer)
62
- * @param beta - scalar multiplier for y
63
- * @param y - Float32Array input/output vector
64
- * @param incy - stride for y (must be a positive integer)
65
- * @see <a href="https://github.com/manit2004/wgblas/blob/main/src/ssymv/ssymv.mjs#L15">Source code: ssymv.mjs (L15)</a>
66
- * @category BLAS Level 2
67
- */
68
- export declare function ssymv(
69
- device: GPUDevice,
70
- uplo: 'lower' | 'upper',
71
- n: number,
72
- alpha: number,
73
- A: GpuMatrix,
74
- lda: number,
75
- x: Float32Array,
76
- incx: number,
77
- beta: number,
78
- y: Float32Array,
79
- incy: number,
80
- ): Promise<{ y: Float32Array; gpuTimeMs?: number }>;
81
-
82
47
  /**
83
48
  * Performs the symmetric matrix-vector operation y = alpha * A * x + beta * y
84
49
  *
@@ -86,7 +51,7 @@ export declare function ssymv(
86
51
  * `layout` (set at `GpuMatrix.from` time) determines the operation — there is
87
52
  * no separate `layout` argument here.
88
53
  *
89
- * {@includeCode ../../examples/ssymv/gpuvec.ssymv.js}
54
+ * {@includeCode ../../examples/ssymv/gpu.ssymv.js}
90
55
  *
91
56
  * @param device - GPUDevice from `init()`
92
57
  * @param uplo - `'lower'` to use the lower triangle, `'upper'` to use the upper triangle
@@ -11,6 +11,7 @@ import { extractTimestamp } from "../util/benchmark.mjs";
11
11
  import { getPipeline } from "../util/pipeline.mjs";
12
12
  import { GpuVector } from "../classes/GpuVector.mjs";
13
13
  import { GpuMatrix } from "../classes/GpuMatrix.mjs";
14
+ import { requireSameDevice } from "../util/device.mjs";
14
15
 
15
16
  export async function ssymv(device, uplo, n, alpha, A, lda, x, incx, beta, y, incy, layout = "row-major") {
16
17
  const xIsGpu = x instanceof GpuVector;
@@ -19,6 +20,7 @@ export async function ssymv(device, uplo, n, alpha, A, lda, x, incx, beta, y, in
19
20
 
20
21
  if (!(device instanceof GPUDevice))
21
22
  throw new Error("device must be a GPUDevice.");
23
+ requireSameDevice(device, "ssymv", { A, x, y });
22
24
  if (uplo !== "lower" && uplo !== "upper")
23
25
  throw new Error("uplo must be 'lower' or 'upper'.");
24
26
  if (layout !== "row-major" && layout !== "column-major")
@@ -53,6 +55,8 @@ export async function ssymv(device, uplo, n, alpha, A, lda, x, incx, beta, y, in
53
55
  );
54
56
  if (xIsGpu && !AIsGpu)
55
57
  throw new Error("A must be a GpuMatrix when x and y are GpuVectors.");
58
+ if (AIsGpu && !xIsGpu)
59
+ throw new Error("x and y must be GpuVectors when A is a GpuMatrix.");
56
60
  if (xIsGpu && x._buf === y._buf)
57
61
  throw new Error("x and y must not reference the same GPU buffer when both are GpuVectors.");
58
62
  if (AIsGpu && lda !== A.lda)
@@ -87,10 +91,10 @@ export async function ssymv(device, uplo, n, alpha, A, lda, x, incx, beta, y, in
87
91
  let paramsBuffer = null;
88
92
 
89
93
  try {
90
- ABuffer = AIsGpu ? A._buf : uploadBuffer(A, "ssymv-A", false);
91
- xBuffer = xIsGpu ? x._buf : uploadBuffer(x, "ssymv-x", false);
92
- yBuffer = yIsGpu ? y._buf : uploadBuffer(y, "ssymv-y", true);
93
- paramsBuffer = createParamsBuffer(
94
+ ABuffer = AIsGpu ? A._buf : uploadBuffer(device, A, "ssymv-A", false);
95
+ xBuffer = xIsGpu ? x._buf : uploadBuffer(device, x, "ssymv-x", false);
96
+ yBuffer = yIsGpu ? y._buf : uploadBuffer(device, y, "ssymv-y", true);
97
+ paramsBuffer = createParamsBuffer(device,
94
98
  [
95
99
  { value: n, type: "u32" },
96
100
  { value: alpha, type: "f32" },
@@ -103,7 +107,7 @@ export async function ssymv(device, uplo, n, alpha, A, lda, x, incx, beta, y, in
103
107
  "ssymv-params",
104
108
  );
105
109
 
106
- const bindGroup = createBindGroup(pipeline.getBindGroupLayout(0), [
110
+ const bindGroup = createBindGroup(device, pipeline.getBindGroupLayout(0), [
107
111
  ABuffer,
108
112
  xBuffer,
109
113
  yBuffer,
@@ -111,10 +115,10 @@ export async function ssymv(device, uplo, n, alpha, A, lda, x, incx, beta, y, in
111
115
  ]);
112
116
 
113
117
  const wgCount = Math.min(n, device.limits.maxComputeWorkgroupsPerDimension);
114
- const { commandEncoder, ts } = runComputePass(pipeline, bindGroup, wgCount);
115
- const readBuffer = yIsGpu ? null : stageReadback(commandEncoder, yBuffer);
118
+ const { commandEncoder, ts } = runComputePass(device, pipeline, bindGroup, wgCount);
119
+ const readBuffer = yIsGpu ? null : stageReadback(device, commandEncoder, yBuffer);
116
120
 
117
- submit(commandEncoder);
121
+ submit(device, commandEncoder);
118
122
 
119
123
  const gpuTimeMs = await extractTimestamp(ts);
120
124
 
@@ -39,35 +39,6 @@ export declare function ssyr(
39
39
  layout?: 'row-major' | 'column-major',
40
40
  ): Promise<{ A: Float32Array; gpuTimeMs?: number }>;
41
41
 
42
- /**
43
- * Performs the symmetric rank-1 update A = alpha * x * x^T + A
44
- *
45
- * A is kept GPU-resident; x is a CPU Float32Array. `A`'s own `layout` (set at
46
- * `GpuMatrix.from` time) determines the operation — there is no separate
47
- * `layout` argument here.
48
- *
49
- * @param device - GPUDevice from `init()`
50
- * @param uplo - `'lower'` to use the lower triangle, `'upper'` to use the upper triangle
51
- * @param n - order of the matrix A
52
- * @param alpha - scalar multiplier for x*x^T
53
- * @param x - Float32Array input vector
54
- * @param incx - stride for x (must be a positive integer)
55
- * @param A - GpuMatrix, GPU-resident
56
- * @param lda - leading dimension of A (must equal A.lda)
57
- * @see <a href="https://github.com/manit2004/wgblas/blob/main/src/ssyr/ssyr.mjs#L15">Source code: ssyr.mjs (L15)</a>
58
- * @category BLAS Level 2
59
- */
60
- export declare function ssyr(
61
- device: GPUDevice,
62
- uplo: 'lower' | 'upper',
63
- n: number,
64
- alpha: number,
65
- x: Float32Array,
66
- incx: number,
67
- A: GpuMatrix,
68
- lda: number,
69
- ): Promise<{ gpuTimeMs?: number }>;
70
-
71
42
  /**
72
43
  * Performs the symmetric rank-1 update A = alpha * x * x^T + A
73
44
  *
@@ -75,7 +46,7 @@ export declare function ssyr(
75
46
  * `GpuMatrix.from` time) determines the operation — there is no separate
76
47
  * `layout` argument here.
77
48
  *
78
- * {@includeCode ../../examples/ssyr/gpuvec.ssyr.js}
49
+ * {@includeCode ../../examples/ssyr/gpu.ssyr.js}
79
50
  *
80
51
  * @param device - GPUDevice from `init()`
81
52
  * @param uplo - `'lower'` to use the lower triangle, `'upper'` to use the upper triangle
package/src/ssyr/ssyr.mjs CHANGED
@@ -11,6 +11,7 @@ import { extractTimestamp } from "../util/benchmark.mjs";
11
11
  import { getPipeline } from "../util/pipeline.mjs";
12
12
  import { GpuVector } from "../classes/GpuVector.mjs";
13
13
  import { GpuMatrix } from "../classes/GpuMatrix.mjs";
14
+ import { requireSameDevice } from "../util/device.mjs";
14
15
 
15
16
  export async function ssyr(device, uplo, n, alpha, x, incx, A, lda, layout = "row-major") {
16
17
  const xIsGpu = x instanceof GpuVector;
@@ -18,6 +19,7 @@ export async function ssyr(device, uplo, n, alpha, x, incx, A, lda, layout = "ro
18
19
 
19
20
  if (!(device instanceof GPUDevice))
20
21
  throw new Error("device must be a GPUDevice.");
22
+ requireSameDevice(device, "ssyr", { A, x });
21
23
  if (uplo !== "lower" && uplo !== "upper")
22
24
  throw new Error("uplo must be 'lower' or 'upper'.");
23
25
  if (layout !== "row-major" && layout !== "column-major")
@@ -36,6 +38,8 @@ export async function ssyr(device, uplo, n, alpha, x, incx, A, lda, layout = "ro
36
38
  throw new Error("x must be a Float32Array or GpuVector.");
37
39
  if (xIsGpu && !AIsGpu)
38
40
  throw new Error("A must be a GpuMatrix when x is a GpuVector.");
41
+ if (AIsGpu && !xIsGpu)
42
+ throw new Error("x must be a GpuVector when A is a GpuMatrix.");
39
43
  if (AIsGpu && xIsGpu && A._buf === x._buf)
40
44
  throw new Error("A and x must not reference the same GPU buffer.");
41
45
  if (AIsGpu && lda !== A.lda)
@@ -61,9 +65,9 @@ export async function ssyr(device, uplo, n, alpha, x, incx, A, lda, layout = "ro
61
65
  let paramsBuffer = null;
62
66
 
63
67
  try {
64
- xBuffer = xIsGpu ? x._buf : uploadBuffer(x, "ssyr-x", false);
65
- ABuffer = AIsGpu ? A._buf : uploadBuffer(A, "ssyr-A", true);
66
- paramsBuffer = createParamsBuffer(
68
+ xBuffer = xIsGpu ? x._buf : uploadBuffer(device, x, "ssyr-x", false);
69
+ ABuffer = AIsGpu ? A._buf : uploadBuffer(device, A, "ssyr-A", true);
70
+ paramsBuffer = createParamsBuffer(device,
67
71
  [
68
72
  { value: n, type: "u32" },
69
73
  { value: alpha, type: "f32" },
@@ -74,7 +78,7 @@ export async function ssyr(device, uplo, n, alpha, x, incx, A, lda, layout = "ro
74
78
  "ssyr-params",
75
79
  );
76
80
 
77
- const bindGroup = createBindGroup(pipeline.getBindGroupLayout(0), [
81
+ const bindGroup = createBindGroup(device, pipeline.getBindGroupLayout(0), [
78
82
  xBuffer,
79
83
  ABuffer,
80
84
  paramsBuffer,
@@ -83,10 +87,10 @@ export async function ssyr(device, uplo, n, alpha, x, incx, A, lda, layout = "ro
83
87
  // One workgroup per row of A; clamped to device limit — the shader's
84
88
  // grid-stride loop handles remaining rows when n > dispatch count.
85
89
  const wgCount = Math.min(n, device.limits.maxComputeWorkgroupsPerDimension);
86
- const { commandEncoder, ts } = runComputePass(pipeline, bindGroup, wgCount);
87
- const readBuffer = AIsGpu ? null : stageReadback(commandEncoder, ABuffer);
90
+ const { commandEncoder, ts } = runComputePass(device, pipeline, bindGroup, wgCount);
91
+ const readBuffer = AIsGpu ? null : stageReadback(device, commandEncoder, ABuffer);
88
92
 
89
- submit(commandEncoder);
93
+ submit(device, commandEncoder);
90
94
 
91
95
  const gpuTimeMs = await extractTimestamp(ts);
92
96
 
@@ -43,39 +43,6 @@ export declare function ssyr2(
43
43
  layout?: 'row-major' | 'column-major',
44
44
  ): Promise<{ A: Float32Array; gpuTimeMs?: number }>;
45
45
 
46
- /**
47
- * Performs the symmetric rank-2 update A = alpha * x * y^T + alpha * y * x^T + A
48
- *
49
- * A is kept GPU-resident; x and y are CPU Float32Arrays. `A`'s own `layout`
50
- * (set at `GpuMatrix.from` time) determines the operation — there is no
51
- * separate `layout` argument here.
52
- *
53
- * @param device - GPUDevice from `init()`
54
- * @param uplo - `'lower'` to use the lower triangle, `'upper'` to use the upper triangle
55
- * @param n - order of the matrix A
56
- * @param alpha - scalar multiplier for x*y^T + y*x^T
57
- * @param x - Float32Array input vector
58
- * @param incx - stride for x (must be a positive integer)
59
- * @param y - Float32Array input vector
60
- * @param incy - stride for y (must be a positive integer)
61
- * @param A - GpuMatrix, row-major, GPU-resident, Float32-backed
62
- * @param lda - leading dimension of A (must equal A.lda)
63
- * @see <a href="https://github.com/manit2004/wgblas/blob/main/src/ssyr2/ssyr2.mjs#L15">Source code: ssyr2.mjs (L15)</a>
64
- * @category BLAS Level 2
65
- */
66
- export declare function ssyr2(
67
- device: GPUDevice,
68
- uplo: 'lower' | 'upper',
69
- n: number,
70
- alpha: number,
71
- x: Float32Array,
72
- incx: number,
73
- y: Float32Array,
74
- incy: number,
75
- A: GpuMatrix,
76
- lda: number,
77
- ): Promise<{ gpuTimeMs?: number }>;
78
-
79
46
  /**
80
47
  * Performs the symmetric rank-2 update A = alpha * x * y^T + alpha * y * x^T + A
81
48
  *
@@ -83,7 +50,7 @@ export declare function ssyr2(
83
50
  * `GpuMatrix.from` time) determines the operation — there is no separate
84
51
  * `layout` argument here.
85
52
  *
86
- * {@includeCode ../../examples/ssyr2/gpuvec.ssyr2.js}
53
+ * {@includeCode ../../examples/ssyr2/gpu.ssyr2.js}
87
54
  *
88
55
  * @param device - GPUDevice from `init()`
89
56
  * @param uplo - `'lower'` to use the lower triangle, `'upper'` to use the upper triangle