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/src/srot/srot.mjs CHANGED
@@ -24,9 +24,11 @@ export async function srot(device, n, x, incx, y, incy, c, s) {
24
24
  !Number.isInteger(incy)
25
25
  )
26
26
  throw new Error("n, incx, and incy must be integers.");
27
- if (isNaN(c) || isNaN(s)) throw new Error("c and s must not be NaN.");
28
- if (!isFinite(c)) throw new Error("c must be finite.");
29
- if (!isFinite(s)) throw new Error("s must be finite.");
27
+ if (typeof c !== "number") throw new Error("c must be a number.");
28
+ if (typeof s !== "number") throw new Error("s must be a number.");
29
+ if (Number.isNaN(c) || Number.isNaN(s)) throw new Error("c and s must not be NaN.");
30
+ if (!Number.isFinite(c)) throw new Error("c must be finite.");
31
+ if (!Number.isFinite(s)) throw new Error("s must be finite.");
30
32
  if (incx <= 0 || incy <= 0)
31
33
  throw new Error("incx and incy must be positive.");
32
34
  if (!xIsGpu && !(x instanceof Float32Array))
@@ -49,47 +51,61 @@ export async function srot(device, n, x, incx, y, incy, c, s) {
49
51
 
50
52
  const pipeline = await getPipeline(device, "srot");
51
53
 
52
- const xBuffer = xIsGpu ? x._buf : uploadBuffer(x, "srot-x", true);
53
- const yBuffer = yIsGpu ? y._buf : uploadBuffer(y, "srot-y", true);
54
- const paramsBuffer = createParamsBuffer(
55
- [
56
- { value: n, type: "u32" },
57
- { value: c, type: "f32" },
58
- { value: s, type: "f32" },
59
- { value: incx, type: "u32" },
60
- { value: incy, type: "u32" },
61
- ],
62
- "srot-params",
63
- );
54
+ let xBuffer = null;
55
+ let yBuffer = null;
56
+ let paramsBuffer = null;
57
+ let readX = null;
58
+ let readY = null;
64
59
 
65
- const bindGroup = createBindGroup(pipeline.getBindGroupLayout(0), [
66
- xBuffer,
67
- yBuffer,
68
- paramsBuffer,
69
- ]);
70
- const { commandEncoder, ts } = runComputePass(
71
- pipeline,
72
- bindGroup,
73
- calcWorkgroups(n),
74
- );
75
- const readX = xIsGpu ? null : stageReadback(commandEncoder, xBuffer);
76
- const readY = yIsGpu ? null : stageReadback(commandEncoder, yBuffer);
77
- submit(commandEncoder);
60
+ try {
61
+ xBuffer = xIsGpu ? x._buf : uploadBuffer(x, "srot-x", true);
62
+ yBuffer = yIsGpu ? y._buf : uploadBuffer(y, "srot-y", true);
63
+ paramsBuffer = createParamsBuffer(
64
+ [
65
+ { value: n, type: "u32" },
66
+ { value: c, type: "f32" },
67
+ { value: s, type: "f32" },
68
+ { value: incx, type: "u32" },
69
+ { value: incy, type: "u32" },
70
+ ],
71
+ "srot-params",
72
+ );
78
73
 
79
- const gpuTimeMs = await extractTimestamp(ts);
74
+ const bindGroup = createBindGroup(pipeline.getBindGroupLayout(0), [
75
+ xBuffer,
76
+ yBuffer,
77
+ paramsBuffer,
78
+ ]);
79
+ const { commandEncoder, ts } = runComputePass(
80
+ pipeline,
81
+ bindGroup,
82
+ calcWorkgroups(n),
83
+ );
84
+ readX = xIsGpu ? null : stageReadback(commandEncoder, xBuffer);
85
+ readY = yIsGpu ? null : stageReadback(commandEncoder, yBuffer);
86
+ submit(commandEncoder);
80
87
 
81
- if (xIsGpu && yIsGpu) {
82
- destroyBuffers(paramsBuffer);
83
- if (gpuTimeMs !== undefined) return { gpuTimeMs };
84
- return {};
85
- }
88
+ const gpuTimeMs = await extractTimestamp(ts);
86
89
 
87
- const [xResult, yResult] = await Promise.all([
88
- extractResult(readX, Float32Array),
89
- extractResult(readY, Float32Array),
90
- ]);
91
- destroyBuffers(xBuffer, yBuffer, paramsBuffer, readX, readY);
90
+ if (xIsGpu && yIsGpu) {
91
+ if (gpuTimeMs !== undefined) return { gpuTimeMs };
92
+ return {};
93
+ }
92
94
 
93
- if (gpuTimeMs !== undefined) return { x: xResult, y: yResult, gpuTimeMs };
94
- return { x: xResult, y: yResult };
95
+ const xPromise = extractResult(readX, Float32Array);
96
+ const yPromise = extractResult(readY, Float32Array);
97
+ readX = null; // ownership transferred — extractResult's own finally destroys it
98
+ readY = null;
99
+ const [xResult, yResult] = await Promise.all([xPromise, yPromise]);
100
+
101
+ if (gpuTimeMs !== undefined) return { x: xResult, y: yResult, gpuTimeMs };
102
+ return { x: xResult, y: yResult };
103
+ } finally {
104
+ if (!xIsGpu && xBuffer) destroyBuffers(xBuffer);
105
+ if (!yIsGpu && yBuffer) destroyBuffers(yBuffer);
106
+ if (paramsBuffer) destroyBuffers(paramsBuffer);
107
+ // Only reached if extractTimestamp threw before ownership was transferred above.
108
+ if (readX) destroyBuffers(readX);
109
+ if (readY) destroyBuffers(readY);
110
+ }
95
111
  }
@@ -48,47 +48,63 @@ export async function srotm(device, n, x, incx, y, incy, param) {
48
48
 
49
49
  const pipeline = await getPipeline(device, "srotm");
50
50
 
51
- const xBuffer = xIsGpu ? x._buf : uploadBuffer(x, "srotm-x", true);
52
- const yBuffer = yIsGpu ? y._buf : uploadBuffer(y, "srotm-y", true);
53
- const paramBuffer = uploadBuffer(param, "srotm-param", false);
54
- const paramsBuffer = createParamsBuffer(
55
- [
56
- { value: n, type: "u32" },
57
- { value: incx, type: "u32" },
58
- { value: incy, type: "u32" },
59
- ],
60
- "srotm-params",
61
- );
51
+ let xBuffer = null;
52
+ let yBuffer = null;
53
+ let paramBuffer = null;
54
+ let paramsBuffer = null;
55
+ let readX = null;
56
+ let readY = null;
62
57
 
63
- const bindGroup = createBindGroup(pipeline.getBindGroupLayout(0), [
64
- xBuffer,
65
- yBuffer,
66
- paramBuffer,
67
- paramsBuffer,
68
- ]);
69
- const { commandEncoder, ts } = runComputePass(
70
- pipeline,
71
- bindGroup,
72
- calcWorkgroups(n),
73
- );
74
- const readX = xIsGpu ? null : stageReadback(commandEncoder, xBuffer);
75
- const readY = yIsGpu ? null : stageReadback(commandEncoder, yBuffer);
76
- submit(commandEncoder);
58
+ try {
59
+ xBuffer = xIsGpu ? x._buf : uploadBuffer(x, "srotm-x", true);
60
+ yBuffer = yIsGpu ? y._buf : uploadBuffer(y, "srotm-y", true);
61
+ paramBuffer = uploadBuffer(param, "srotm-param", false);
62
+ paramsBuffer = createParamsBuffer(
63
+ [
64
+ { value: n, type: "u32" },
65
+ { value: incx, type: "u32" },
66
+ { value: incy, type: "u32" },
67
+ ],
68
+ "srotm-params",
69
+ );
77
70
 
78
- const gpuTimeMs = await extractTimestamp(ts);
71
+ const bindGroup = createBindGroup(pipeline.getBindGroupLayout(0), [
72
+ xBuffer,
73
+ yBuffer,
74
+ paramBuffer,
75
+ paramsBuffer,
76
+ ]);
77
+ const { commandEncoder, ts } = runComputePass(
78
+ pipeline,
79
+ bindGroup,
80
+ calcWorkgroups(n),
81
+ );
82
+ readX = xIsGpu ? null : stageReadback(commandEncoder, xBuffer);
83
+ readY = yIsGpu ? null : stageReadback(commandEncoder, yBuffer);
84
+ submit(commandEncoder);
79
85
 
80
- if (xIsGpu && yIsGpu) {
81
- destroyBuffers(paramBuffer, paramsBuffer);
82
- if (gpuTimeMs !== undefined) return { gpuTimeMs };
83
- return {};
84
- }
86
+ const gpuTimeMs = await extractTimestamp(ts);
85
87
 
86
- const [xResult, yResult] = await Promise.all([
87
- extractResult(readX, Float32Array),
88
- extractResult(readY, Float32Array),
89
- ]);
90
- destroyBuffers(xBuffer, yBuffer, paramBuffer, paramsBuffer, readX, readY);
88
+ if (xIsGpu && yIsGpu) {
89
+ if (gpuTimeMs !== undefined) return { gpuTimeMs };
90
+ return {};
91
+ }
91
92
 
92
- if (gpuTimeMs !== undefined) return { x: xResult, y: yResult, gpuTimeMs };
93
- return { x: xResult, y: yResult };
93
+ const xPromise = extractResult(readX, Float32Array);
94
+ const yPromise = extractResult(readY, Float32Array);
95
+ readX = null; // ownership transferred — extractResult's own finally destroys it
96
+ readY = null;
97
+ const [xResult, yResult] = await Promise.all([xPromise, yPromise]);
98
+
99
+ if (gpuTimeMs !== undefined) return { x: xResult, y: yResult, gpuTimeMs };
100
+ return { x: xResult, y: yResult };
101
+ } finally {
102
+ if (!xIsGpu && xBuffer) destroyBuffers(xBuffer);
103
+ if (!yIsGpu && yBuffer) destroyBuffers(yBuffer);
104
+ if (paramBuffer) destroyBuffers(paramBuffer);
105
+ if (paramsBuffer) destroyBuffers(paramsBuffer);
106
+ // Only reached if extractTimestamp threw before ownership was transferred above.
107
+ if (readX) destroyBuffers(readX);
108
+ if (readY) destroyBuffers(readY);
109
+ }
94
110
  }
@@ -19,8 +19,10 @@ export async function sscal(device, n, alpha, x, incx) {
19
19
  throw new Error("device must be a GPUDevice.");
20
20
  if (!Number.isInteger(n) || !Number.isInteger(incx))
21
21
  throw new Error("n and incx must be integers.");
22
- if (isNaN(alpha)) throw new Error("alpha must not be NaN.");
23
- if (!isFinite(alpha)) throw new Error("alpha must be finite.");
22
+ if (typeof alpha !== "number")
23
+ throw new Error("alpha must be a number.");
24
+ if (Number.isNaN(alpha)) throw new Error("alpha must not be NaN.");
25
+ if (!Number.isFinite(alpha)) throw new Error("alpha must be finite.");
24
26
  if (incx <= 0) throw new Error("incx must be positive.");
25
27
  if (!(x instanceof Float32Array) && !(x instanceof GpuVector))
26
28
  throw new Error("x must be a Float32Array or GpuVector.");
@@ -32,40 +34,49 @@ export async function sscal(device, n, alpha, x, incx) {
32
34
 
33
35
  const pipeline = await getPipeline(device, "sscal");
34
36
 
35
- const xBuffer = xIsGpu ? x._buf : uploadBuffer(x, "sscal-x", true);
36
- const paramsBuffer = createParamsBuffer(
37
- [
38
- { value: n, type: "u32" },
39
- { value: alpha, type: "f32" },
40
- { value: incx, type: "u32" },
41
- ],
42
- "sscal-params",
43
- );
37
+ let xBuffer = null;
38
+ let paramsBuffer = null;
39
+ let readBuffer = null;
44
40
 
45
- const bindGroup = createBindGroup(pipeline.getBindGroupLayout(0), [
46
- xBuffer,
47
- paramsBuffer,
48
- ]);
49
- const { commandEncoder, ts } = runComputePass(
50
- pipeline,
51
- bindGroup,
52
- calcWorkgroups(n),
53
- );
54
- const readBuffer = xIsGpu ? null : stageReadback(commandEncoder, xBuffer);
41
+ try {
42
+ xBuffer = xIsGpu ? x._buf : uploadBuffer(x, "sscal-x", true);
43
+ paramsBuffer = createParamsBuffer(
44
+ [
45
+ { value: n, type: "u32" },
46
+ { value: alpha, type: "f32" },
47
+ { value: incx, type: "u32" },
48
+ ],
49
+ "sscal-params",
50
+ );
55
51
 
56
- submit(commandEncoder);
52
+ const bindGroup = createBindGroup(pipeline.getBindGroupLayout(0), [
53
+ xBuffer,
54
+ paramsBuffer,
55
+ ]);
56
+ const { commandEncoder, ts } = runComputePass(
57
+ pipeline,
58
+ bindGroup,
59
+ calcWorkgroups(n),
60
+ );
61
+ readBuffer = xIsGpu ? null : stageReadback(commandEncoder, xBuffer);
57
62
 
58
- const gpuTimeMs = await extractTimestamp(ts);
63
+ submit(commandEncoder);
59
64
 
60
- if (xIsGpu) {
61
- destroyBuffers(paramsBuffer);
62
- if (gpuTimeMs !== undefined) return { gpuTimeMs };
63
- return {};
64
- }
65
+ const gpuTimeMs = await extractTimestamp(ts);
65
66
 
66
- const result = await extractResult(readBuffer, Float32Array);
67
- destroyBuffers(xBuffer, paramsBuffer, readBuffer);
67
+ if (xIsGpu) {
68
+ if (gpuTimeMs !== undefined) return { gpuTimeMs };
69
+ return {};
70
+ }
68
71
 
69
- if (gpuTimeMs !== undefined) return { result, gpuTimeMs };
70
- return result;
72
+ const result = await extractResult(readBuffer, Float32Array);
73
+ readBuffer = null; // extractResult already destroyed it
74
+ if (gpuTimeMs !== undefined) return { x: result, gpuTimeMs };
75
+ return result;
76
+ } finally {
77
+ if (!xIsGpu && xBuffer) destroyBuffers(xBuffer);
78
+ if (paramsBuffer) destroyBuffers(paramsBuffer);
79
+ // Only reached if extractTimestamp threw before extractResult ran.
80
+ if (readBuffer) destroyBuffers(readBuffer);
81
+ }
71
82
  }
@@ -46,45 +46,60 @@ export async function sswap(device, n, x, incx, y, incy) {
46
46
 
47
47
  const pipeline = await getPipeline(device, "sswap");
48
48
 
49
- const xBuffer = xIsGpu ? x._buf : uploadBuffer(x, "sswap-x", true);
50
- const yBuffer = yIsGpu ? y._buf : uploadBuffer(y, "sswap-y", true);
51
- const paramsBuffer = createParamsBuffer(
52
- [
53
- { value: n, type: "u32" },
54
- { value: incx, type: "u32" },
55
- { value: incy, type: "u32" },
56
- ],
57
- "sswap-params",
58
- );
49
+ let xBuffer = null;
50
+ let yBuffer = null;
51
+ let paramsBuffer = null;
52
+ let xReadBuffer = null;
53
+ let yReadBuffer = null;
59
54
 
60
- const bindGroup = createBindGroup(pipeline.getBindGroupLayout(0), [
61
- xBuffer,
62
- yBuffer,
63
- paramsBuffer,
64
- ]);
65
- const { commandEncoder, ts } = runComputePass(
66
- pipeline,
67
- bindGroup,
68
- calcWorkgroups(n),
69
- );
70
- const xReadBuffer = xIsGpu ? null : stageReadback(commandEncoder, xBuffer);
71
- const yReadBuffer = yIsGpu ? null : stageReadback(commandEncoder, yBuffer);
55
+ try {
56
+ xBuffer = xIsGpu ? x._buf : uploadBuffer(x, "sswap-x", true);
57
+ yBuffer = yIsGpu ? y._buf : uploadBuffer(y, "sswap-y", true);
58
+ paramsBuffer = createParamsBuffer(
59
+ [
60
+ { value: n, type: "u32" },
61
+ { value: incx, type: "u32" },
62
+ { value: incy, type: "u32" },
63
+ ],
64
+ "sswap-params",
65
+ );
72
66
 
73
- submit(commandEncoder);
67
+ const bindGroup = createBindGroup(pipeline.getBindGroupLayout(0), [
68
+ xBuffer,
69
+ yBuffer,
70
+ paramsBuffer,
71
+ ]);
72
+ const { commandEncoder, ts } = runComputePass(
73
+ pipeline,
74
+ bindGroup,
75
+ calcWorkgroups(n),
76
+ );
77
+ xReadBuffer = xIsGpu ? null : stageReadback(commandEncoder, xBuffer);
78
+ yReadBuffer = yIsGpu ? null : stageReadback(commandEncoder, yBuffer);
74
79
 
75
- const gpuTimeMs = await extractTimestamp(ts);
80
+ submit(commandEncoder);
76
81
 
77
- if (xIsGpu && yIsGpu) {
78
- destroyBuffers(paramsBuffer);
79
- if (gpuTimeMs !== undefined) return { gpuTimeMs };
80
- return {};
81
- }
82
+ const gpuTimeMs = await extractTimestamp(ts);
82
83
 
83
- const resultX = await extractResult(xReadBuffer, Float32Array);
84
- const resultY = await extractResult(yReadBuffer, Float32Array);
84
+ if (xIsGpu && yIsGpu) {
85
+ if (gpuTimeMs !== undefined) return { gpuTimeMs };
86
+ return {};
87
+ }
85
88
 
86
- destroyBuffers(xBuffer, xReadBuffer, yBuffer, yReadBuffer, paramsBuffer);
89
+ const resultX = await extractResult(xReadBuffer, Float32Array);
90
+ xReadBuffer = null; // extractResult already destroyed it
91
+ const resultY = await extractResult(yReadBuffer, Float32Array);
92
+ yReadBuffer = null; // extractResult already destroyed it
87
93
 
88
- if (gpuTimeMs !== undefined) return { x: resultX, y: resultY, gpuTimeMs };
89
- return { x: resultX, y: resultY };
94
+ if (gpuTimeMs !== undefined) return { x: resultX, y: resultY, gpuTimeMs };
95
+ return { x: resultX, y: resultY };
96
+ } finally {
97
+ if (!xIsGpu && xBuffer) destroyBuffers(xBuffer);
98
+ if (!yIsGpu && yBuffer) destroyBuffers(yBuffer);
99
+ if (paramsBuffer) destroyBuffers(paramsBuffer);
100
+ // Only reached if extractTimestamp or extractResult threw before
101
+ // clearing these — on the success path they're already null.
102
+ if (xReadBuffer) destroyBuffers(xReadBuffer);
103
+ if (yReadBuffer) destroyBuffers(yReadBuffer);
104
+ }
90
105
  }
@@ -0,0 +1,117 @@
1
+ import { GpuVector } from "../classes/GpuVector.mjs";
2
+ import { GpuMatrix } from "../classes/GpuMatrix.mjs";
3
+
4
+ /**
5
+ * Performs the symmetric matrix-vector operation y = alpha * A * x + beta * y
6
+ *
7
+ * A is an n×n symmetric matrix stored in row-major order. Only the triangle
8
+ * specified by `uplo` is referenced; the other triangle is inferred by symmetry.
9
+ *
10
+ * {@includeCode ../../examples/ssymv/ssymv.js}
11
+ *
12
+ * **Browser (standalone HTML):**
13
+ * {@includeCode ../../examples/ssymv/web/ssymv.html}
14
+ *
15
+ * @param device - GPUDevice from `init()`
16
+ * @param uplo - `'lower'` to use the lower triangle, `'upper'` to use the upper triangle
17
+ * @param n - order of the matrix A (number of rows and columns)
18
+ * @param alpha - scalar multiplier for A*x
19
+ * @param A - Float32Array, row-major or column-major (see `layout`), at least (n-1)*lda+n elements
20
+ * @param lda - leading dimension of A (>= n either way — A is square)
21
+ * @param x - Float32Array input vector, length at least (n-1)*incx+1
22
+ * @param incx - stride for x (must be a positive integer)
23
+ * @param beta - scalar multiplier for y
24
+ * @param y - Float32Array input/output vector, length at least (n-1)*incy+1
25
+ * @param incy - stride for y (must be a positive integer)
26
+ * @param layout - storage layout of `A` (default: `'row-major'`); for a symmetric
27
+ * matrix, column-major storage just means the *other* triangle is the one
28
+ * physically referenced for a given `uplo`
29
+ * @see <a href="https://github.com/manit2004/wgblas/blob/main/src/ssymv/ssymv.mjs#L15">Source code: ssymv.mjs (L15)</a>
30
+ * @category BLAS Level 2
31
+ */
32
+ export declare function ssymv(
33
+ device: GPUDevice,
34
+ uplo: 'lower' | 'upper',
35
+ n: number,
36
+ alpha: number,
37
+ A: Float32Array,
38
+ lda: number,
39
+ x: Float32Array,
40
+ incx: number,
41
+ beta: number,
42
+ y: Float32Array,
43
+ incy: number,
44
+ layout?: 'row-major' | 'column-major',
45
+ ): Promise<{ y: Float32Array; gpuTimeMs?: number }>;
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
+ /**
83
+ * Performs the symmetric matrix-vector operation y = alpha * A * x + beta * y
84
+ *
85
+ * x and y are kept resident on the GPU. A must be a GpuMatrix; its own
86
+ * `layout` (set at `GpuMatrix.from` time) determines the operation — there is
87
+ * no separate `layout` argument here.
88
+ *
89
+ * {@includeCode ../../examples/ssymv/gpuvec.ssymv.js}
90
+ *
91
+ * @param device - GPUDevice from `init()`
92
+ * @param uplo - `'lower'` to use the lower triangle, `'upper'` to use the upper triangle
93
+ * @param n - order of the matrix A
94
+ * @param alpha - scalar multiplier for A*x
95
+ * @param A - GpuMatrix, GPU-resident
96
+ * @param lda - leading dimension of A (must equal A.lda)
97
+ * @param x - GpuVector input vector (not mutated)
98
+ * @param incx - stride for x (must be a positive integer)
99
+ * @param beta - scalar multiplier for y
100
+ * @param y - GpuVector input/output vector (mutated in place)
101
+ * @param incy - stride for y (must be a positive integer)
102
+ * @see <a href="https://github.com/manit2004/wgblas/blob/main/src/ssymv/ssymv.mjs#L15">Source code: ssymv.mjs (L15)</a>
103
+ * @category BLAS Level 2
104
+ */
105
+ export declare function ssymv(
106
+ device: GPUDevice,
107
+ uplo: 'lower' | 'upper',
108
+ n: number,
109
+ alpha: number,
110
+ A: GpuMatrix,
111
+ lda: number,
112
+ x: GpuVector,
113
+ incx: number,
114
+ beta: number,
115
+ y: GpuVector,
116
+ incy: number,
117
+ ): Promise<{ gpuTimeMs?: number }>;
@@ -0,0 +1,135 @@
1
+ import {
2
+ uploadBuffer,
3
+ createParamsBuffer,
4
+ stageReadback,
5
+ destroyBuffers,
6
+ } from "../util/buffer.mjs";
7
+ import { createBindGroup } from "../util/bindgroup.mjs";
8
+ import { runComputePass, submit } from "../util/compute.mjs";
9
+ import { extractResult } from "../util/result.mjs";
10
+ import { extractTimestamp } from "../util/benchmark.mjs";
11
+ import { getPipeline } from "../util/pipeline.mjs";
12
+ import { GpuVector } from "../classes/GpuVector.mjs";
13
+ import { GpuMatrix } from "../classes/GpuMatrix.mjs";
14
+
15
+ export async function ssymv(device, uplo, n, alpha, A, lda, x, incx, beta, y, incy, layout = "row-major") {
16
+ const xIsGpu = x instanceof GpuVector;
17
+ const yIsGpu = y instanceof GpuVector;
18
+ const AIsGpu = A instanceof GpuMatrix;
19
+
20
+ if (!(device instanceof GPUDevice))
21
+ throw new Error("device must be a GPUDevice.");
22
+ if (uplo !== "lower" && uplo !== "upper")
23
+ throw new Error("uplo must be 'lower' or 'upper'.");
24
+ if (layout !== "row-major" && layout !== "column-major")
25
+ throw new Error("layout must be 'row-major' or 'column-major'.");
26
+ if (
27
+ !Number.isInteger(n) ||
28
+ !Number.isInteger(incx) ||
29
+ !Number.isInteger(incy) ||
30
+ !Number.isInteger(lda)
31
+ )
32
+ throw new Error("n, incx, incy, and lda must be integers.");
33
+ if (typeof alpha !== "number")
34
+ throw new Error("alpha must be a number.");
35
+ if (Number.isNaN(alpha)) throw new Error("alpha must not be NaN.");
36
+ if (!Number.isFinite(alpha)) throw new Error("alpha must be finite.");
37
+ if (typeof beta !== "number")
38
+ throw new Error("beta must be a number.");
39
+ if (Number.isNaN(beta)) throw new Error("beta must not be NaN.");
40
+ if (!Number.isFinite(beta)) throw new Error("beta must be finite.");
41
+ if (incx <= 0 || incy <= 0)
42
+ throw new Error("incx and incy must be positive.");
43
+ if (lda < n) throw new Error("lda must be >= n.");
44
+ if (!AIsGpu && !(A instanceof Float32Array))
45
+ throw new Error("A must be a Float32Array or GpuMatrix.");
46
+ if (!xIsGpu && !(x instanceof Float32Array))
47
+ throw new Error("x must be a Float32Array or GpuVector.");
48
+ if (!yIsGpu && !(y instanceof Float32Array))
49
+ throw new Error("y must be a Float32Array or GpuVector.");
50
+ if (xIsGpu !== yIsGpu)
51
+ throw new Error(
52
+ "x and y must be the same type (both Float32Array or both GpuVector).",
53
+ );
54
+ if (xIsGpu && !AIsGpu)
55
+ throw new Error("A must be a GpuMatrix when x and y are GpuVectors.");
56
+ if (xIsGpu && x._buf === y._buf)
57
+ throw new Error("x and y must not reference the same GPU buffer when both are GpuVectors.");
58
+ if (AIsGpu && lda !== A.lda)
59
+ throw new Error("lda must match A.lda when A is a GpuMatrix.");
60
+ if (AIsGpu && (A.rows < n || A.cols < n))
61
+ throw new Error("A is too small for the given n.");
62
+ if (n < 0) throw new Error("n must be non-negative.");
63
+ if (n === 0) return yIsGpu ? {} : { y };
64
+
65
+ if (!AIsGpu && A.length < (n - 1) * lda + n)
66
+ throw new Error(
67
+ "A does not have enough elements for the given n and lda.",
68
+ );
69
+ if (x.length < (n - 1) * incx + 1)
70
+ throw new Error(
71
+ "x does not have enough elements for the given n and incx.",
72
+ );
73
+ if (y.length < (n - 1) * incy + 1)
74
+ throw new Error(
75
+ "y does not have enough elements for the given n and incy.",
76
+ );
77
+
78
+ // GpuMatrix's own layout wins over the argument; A is symmetric, so column-major A reinterpreted row-major just flips which triangle is stored — flip uplo to match.
79
+ const effLayout = AIsGpu ? A.layout : layout;
80
+ const isLower = effLayout === "column-major" ? uplo === "upper" : uplo === "lower";
81
+
82
+ const pipeline = await getPipeline(device, "ssymv");
83
+
84
+ let ABuffer = null;
85
+ let xBuffer = null;
86
+ let yBuffer = null;
87
+ let paramsBuffer = null;
88
+
89
+ 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
+ [
95
+ { value: n, type: "u32" },
96
+ { value: alpha, type: "f32" },
97
+ { value: beta, type: "f32" },
98
+ { value: incx, type: "u32" },
99
+ { value: incy, type: "u32" },
100
+ { value: lda, type: "u32" },
101
+ { value: isLower ? 0 : 1, type: "u32" },
102
+ ],
103
+ "ssymv-params",
104
+ );
105
+
106
+ const bindGroup = createBindGroup(pipeline.getBindGroupLayout(0), [
107
+ ABuffer,
108
+ xBuffer,
109
+ yBuffer,
110
+ paramsBuffer,
111
+ ]);
112
+
113
+ const wgCount = Math.min(n, device.limits.maxComputeWorkgroupsPerDimension);
114
+ const { commandEncoder, ts } = runComputePass(pipeline, bindGroup, wgCount);
115
+ const readBuffer = yIsGpu ? null : stageReadback(commandEncoder, yBuffer);
116
+
117
+ submit(commandEncoder);
118
+
119
+ const gpuTimeMs = await extractTimestamp(ts);
120
+
121
+ if (yIsGpu) {
122
+ if (gpuTimeMs !== undefined) return { gpuTimeMs };
123
+ return {};
124
+ }
125
+
126
+ const result = await extractResult(readBuffer, Float32Array);
127
+ if (gpuTimeMs !== undefined) return { y: result, gpuTimeMs };
128
+ return { y: result };
129
+ } finally {
130
+ if (!AIsGpu && ABuffer) destroyBuffers(ABuffer);
131
+ if (!xIsGpu && xBuffer) destroyBuffers(xBuffer);
132
+ if (!yIsGpu && yBuffer) destroyBuffers(yBuffer);
133
+ if (paramsBuffer) destroyBuffers(paramsBuffer);
134
+ }
135
+ }