sync 2e7068faf55e
Browse files- README.md +82 -0
- build/webgpu/bench.json +147 -0
- build/webgpu/dft-contiguous-naive.wgsl.jinja +170 -0
- build/webgpu/dft-general.wgsl.jinja +221 -0
- build/webgpu/dft-rank4.wgsl.jinja +205 -0
- build/webgpu/dft-tiled-real.wgsl.jinja +136 -0
- build/webgpu/fft-radix2-dit-storage.wgsl.jinja +233 -0
- build/webgpu/fft-stockham-shared.wgsl.jinja +193 -0
- build/webgpu/manifest.json +816 -0
- build/webgpu/metadata.json +23 -0
- build/webgpu/test.json +1374 -0
README.md
CHANGED
|
@@ -1,3 +1,85 @@
|
|
| 1 |
---
|
|
|
|
| 2 |
license: apache-2.0
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
library_name: kernels
|
| 3 |
license: apache-2.0
|
| 4 |
+
tags:
|
| 5 |
+
- kernel
|
| 6 |
+
- webgpu
|
| 7 |
+
- wgsl
|
| 8 |
---
|
| 9 |
+
# ai.onnx.DFT
|
| 10 |
+
|
| 11 |
+
`ai.onnx` · standard ONNX operator · ONNX opset ≥ 20
|
| 12 |
+
|
| 13 |
+
## Description
|
| 14 |
+
|
| 15 |
+
Computes the discrete Fourier Transform (DFT) of the input along a specified axis. For a signal of length N, output bin k is `sum_{n=0}^{N-1} exp(-2*pi*j*k*n/N) * x[n]`; the inverse divides by N and negates the exponent sign. Supports forward/inverse, real-to-complex (RFFT), and complex-to-real (IRFFT) modes via the `onesided` and `inverse` attributes.
|
| 16 |
+
|
| 17 |
+
See the [ONNX `DFT` spec](https://onnx.ai/onnx/operators/onnx__DFT.html) for the reference semantics.
|
| 18 |
+
|
| 19 |
+
## Inputs
|
| 20 |
+
|
| 21 |
+
| Name | Bind key | Logical dtype | WebGPU storage | Rank | Shape | Description | Presence |
|
| 22 |
+
| --- | --- | --- | --- | --- | --- | --- | --- |
|
| 23 |
+
| `input` | `input` | `T` | same as logical dtype | — | — | Input signal tensor; the last dimension is 1 for real values or 2 for complex `(real, imaginary)` pairs. | required |
|
| 24 |
+
| `dft_length` | `dft_length` | `L` | same as logical dtype | `0` | — | Optional int32 scalar controlling the signal length used for the transform; input is zero-padded or truncated to this length. | optional |
|
| 25 |
+
| `axis` | `axis` | `I` | `int32` | `0` | — | Optional logical int64 scalar specifying the dimension over which to compute the DFT (the last axis is reserved for the real/imaginary component and is excluded); signed axes use int32 WebGPU storage and default to `-2` when omitted. | optional |
|
| 26 |
+
|
| 27 |
+
## Outputs
|
| 28 |
+
|
| 29 |
+
| Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
|
| 30 |
+
| --- | --- | --- | --- | --- | --- | --- |
|
| 31 |
+
| `output` | `output` | `T` | same as `input` | — | DFT result tensor; last dimension is 2 (complex) for forward DFT and RFFT, or 1 (real) for IRFFT. | required |
|
| 32 |
+
|
| 33 |
+
## Attributes
|
| 34 |
+
|
| 35 |
+
Default values (overridable per request):
|
| 36 |
+
|
| 37 |
+
| Attribute | Default | Description |
|
| 38 |
+
| --- | --- | --- |
|
| 39 |
+
| `inverse` | `0` | When set to 1, computes the inverse DFT (IDFT/IRFFT) instead of the forward transform; default is 0 (forward). |
|
| 40 |
+
| `onesided` | `0` | When set to 1, exploits conjugate symmetry to return only the non-redundant half of the spectrum (RFFT for forward, IRFFT for inverse); default is 0 (full spectrum). |
|
| 41 |
+
|
| 42 |
+
## Type constraints
|
| 43 |
+
|
| 44 |
+
| Variable | Allowed dtypes |
|
| 45 |
+
| --- | --- |
|
| 46 |
+
| `T` | `float32` |
|
| 47 |
+
| `L` | `int32` |
|
| 48 |
+
| `I` | `int64` |
|
| 49 |
+
|
| 50 |
+
## Files
|
| 51 |
+
|
| 52 |
+
- [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, provenance)
|
| 53 |
+
- [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
|
| 54 |
+
- [`test.json`](build/webgpu/test.json) — correctness cases
|
| 55 |
+
- [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
|
| 56 |
+
- [`dft-contiguous-naive.wgsl.jinja`](build/webgpu/dft-contiguous-naive.wgsl.jinja)
|
| 57 |
+
- [`dft-general.wgsl.jinja`](build/webgpu/dft-general.wgsl.jinja)
|
| 58 |
+
- [`dft-rank4.wgsl.jinja`](build/webgpu/dft-rank4.wgsl.jinja)
|
| 59 |
+
- [`dft-tiled-real.wgsl.jinja`](build/webgpu/dft-tiled-real.wgsl.jinja)
|
| 60 |
+
- [`fft-radix2-dit-storage.wgsl.jinja`](build/webgpu/fft-radix2-dit-storage.wgsl.jinja)
|
| 61 |
+
- [`fft-stockham-shared.wgsl.jinja`](build/webgpu/fft-stockham-shared.wgsl.jinja)
|
| 62 |
+
|
| 63 |
+
## Use with `@huggingface/kernels`
|
| 64 |
+
|
| 65 |
+
The loader automatically allocates outputs whose metadata it can derive from the manifest contract and this call.
|
| 66 |
+
|
| 67 |
+
The explicit `outputs` entries provide shape and logical dtype metadata for the results listed below:
|
| 68 |
+
|
| 69 |
+
- `output`
|
| 70 |
+
|
| 71 |
+
Each entry either requests an optional result or supplies metadata that cannot be inferred from the inputs.
|
| 72 |
+
|
| 73 |
+
The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
|
| 74 |
+
|
| 75 |
+
Replace each `*Data` placeholder with a typed array containing the corresponding input data.
|
| 76 |
+
|
| 77 |
+
```js
|
| 78 |
+
import { getKernel } from "@huggingface/kernels";
|
| 79 |
+
|
| 80 |
+
const kernel = await getKernel("webgpu-kernels/ai.onnx.DFT", { version: 1 });
|
| 81 |
+
// Explicit destinations request optional results or supply metadata that cannot be inferred.
|
| 82 |
+
const { output } = await kernel({ input: { data: inputData, shape: [1, 1, 1, 1, 1, 1, 2, 1] } }, {
|
| 83 |
+
outputs: { output: { shape: [1, 1, 1, 1, 1, 1, 2, 2], dtype: "float32" } },
|
| 84 |
+
});
|
| 85 |
+
```
|
build/webgpu/bench.json
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"op": "ai.onnx.DFT",
|
| 3 |
+
"tunableSpace": { "WORKGROUP_SIZE": [64, 128, 256] },
|
| 4 |
+
"cases": [
|
| 5 |
+
{
|
| 6 |
+
"name": "batch4_len64_complex",
|
| 7 |
+
"inputs": { "input": { "dtype": "float32", "shape": [4, 64, 2] } },
|
| 8 |
+
"outputs": { "output": { "dtype": "float32", "shape": [4, 64, 2] } }
|
| 9 |
+
},
|
| 10 |
+
{
|
| 11 |
+
"name": "fft_batch_axis1_len1024_batch64_real_full",
|
| 12 |
+
"preset": "smoke",
|
| 13 |
+
"vars": { "transforms": 64, "n": 1024, "log2n": 10 },
|
| 14 |
+
"inputs": { "input": { "dtype": "float32", "shape": [64, 1024, 1], "dist": "normal", "seed": 901 } },
|
| 15 |
+
"outputs": { "output": { "dtype": "float32", "shape": [64, 1024, 2] } },
|
| 16 |
+
"bench": {
|
| 17 |
+
"primary": true,
|
| 18 |
+
"metrics": [{ "type": "gflops", "value": "5 * args.transforms * args.n * args.log2n" }]
|
| 19 |
+
}
|
| 20 |
+
},
|
| 21 |
+
{
|
| 22 |
+
"name": "fft_batch_axis1_len2048_batch32_real_onesided",
|
| 23 |
+
"preset": "smoke",
|
| 24 |
+
"attrs": { "onesided": 1 },
|
| 25 |
+
"vars": { "transforms": 32, "n": 2048, "log2n": 11 },
|
| 26 |
+
"inputs": { "input": { "dtype": "float32", "shape": [32, 2048, 1], "dist": "normal", "seed": 902 } },
|
| 27 |
+
"outputs": { "output": { "dtype": "float32", "shape": [32, 1025, 2] } },
|
| 28 |
+
"bench": { "metrics": [{ "type": "gflops", "value": "5 * args.transforms * args.n * args.log2n" }] }
|
| 29 |
+
},
|
| 30 |
+
{
|
| 31 |
+
"name": "fft_rank4_axis1_len1024_batch2_inner8",
|
| 32 |
+
"preset": "smoke",
|
| 33 |
+
"attrs": {},
|
| 34 |
+
"vars": { "transforms": 16, "n": 1024, "log2n": 10 },
|
| 35 |
+
"inputs": {
|
| 36 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [1] } },
|
| 37 |
+
"input": { "dtype": "float32", "shape": [2, 1024, 8, 1], "dist": "normal", "seed": 903 }
|
| 38 |
+
},
|
| 39 |
+
"outputs": { "output": { "dtype": "float32", "shape": [2, 1024, 8, 2] } },
|
| 40 |
+
"bench": { "metrics": [{ "type": "gflops", "value": "5 * args.transforms * args.n * args.log2n" }] }
|
| 41 |
+
},
|
| 42 |
+
{
|
| 43 |
+
"name": "fft_rank2_axis0_len512_single",
|
| 44 |
+
"preset": "smoke",
|
| 45 |
+
"attrs": {},
|
| 46 |
+
"vars": { "transforms": 1, "n": 512, "log2n": 9 },
|
| 47 |
+
"inputs": { "input": { "dtype": "float32", "shape": [512, 1], "dist": "normal", "seed": 904 } },
|
| 48 |
+
"outputs": { "output": { "dtype": "float32", "shape": [512, 2] } },
|
| 49 |
+
"bench": { "metrics": [{ "type": "gflops", "value": "5 * args.transforms * args.n * args.log2n" }] }
|
| 50 |
+
},
|
| 51 |
+
{
|
| 52 |
+
"name": "tiled_batch_axis1_len1000_batch64_real_full",
|
| 53 |
+
"preset": "stress",
|
| 54 |
+
"vars": { "transforms": 64, "n": 1000 },
|
| 55 |
+
"inputs": { "input": { "dtype": "float32", "shape": [64, 1000, 1], "dist": "normal", "seed": 905 } },
|
| 56 |
+
"outputs": { "output": { "dtype": "float32", "shape": [64, 1000, 2] } },
|
| 57 |
+
"bench": { "metrics": [{ "type": "gflops", "value": "8 * args.transforms * args.n * args.n" }] }
|
| 58 |
+
},
|
| 59 |
+
{
|
| 60 |
+
"name": "fft_storage_inplace_batch_axis1_len4096_batch8_real_full",
|
| 61 |
+
"preset": "stress",
|
| 62 |
+
"vars": { "transforms": 8, "n": 4096, "log2n": 12 },
|
| 63 |
+
"attrs": { "inverse": 0, "onesided": 0 },
|
| 64 |
+
"inputs": { "input": { "dtype": "float32", "shape": [8, 4096, 1], "dist": "normal", "seed": 921, "scale": 2 } },
|
| 65 |
+
"outputs": { "output": { "dtype": "float32", "shape": [8, 4096, 2], "dist": "empty" } },
|
| 66 |
+
"bench": {
|
| 67 |
+
"primary": true,
|
| 68 |
+
"metrics": [{ "type": "gflops", "value": "5 * args.transforms * args.n * args.log2n" }]
|
| 69 |
+
}
|
| 70 |
+
},
|
| 71 |
+
{
|
| 72 |
+
"name": "tiled_batch_axis1_len4095_batch8_fft_boundary_minus1",
|
| 73 |
+
"preset": "stress",
|
| 74 |
+
"vars": { "transforms": 8, "n": 4095 },
|
| 75 |
+
"attrs": { "inverse": 0, "onesided": 0 },
|
| 76 |
+
"inputs": { "input": { "dtype": "float32", "shape": [8, 4095, 1], "dist": "normal", "seed": 925, "scale": 2 } },
|
| 77 |
+
"outputs": { "output": { "dtype": "float32", "shape": [8, 4095, 2], "dist": "empty" } },
|
| 78 |
+
"bench": { "metrics": [{ "type": "gflops", "value": "8 * args.transforms * args.n * args.n" }] }
|
| 79 |
+
},
|
| 80 |
+
{
|
| 81 |
+
"name": "fft_storage_scratch_batch_axis1_len4096_batch8_real_onesided",
|
| 82 |
+
"preset": "stress",
|
| 83 |
+
"vars": { "transforms": 8, "n": 4096, "log2n": 12 },
|
| 84 |
+
"attrs": { "inverse": 0, "onesided": 1 },
|
| 85 |
+
"inputs": { "input": { "dtype": "float32", "shape": [8, 4096, 1], "dist": "normal", "seed": 924, "scale": 2 } },
|
| 86 |
+
"outputs": { "output": { "dtype": "float32", "shape": [8, 2049, 2], "dist": "empty" } },
|
| 87 |
+
"bench": { "metrics": [{ "type": "gflops", "value": "5 * args.transforms * args.n * args.log2n" }] }
|
| 88 |
+
},
|
| 89 |
+
{
|
| 90 |
+
"name": "general_len_pow2_1024_batch32_dftlength_demotes_fft",
|
| 91 |
+
"preset": "stress",
|
| 92 |
+
"vars": { "transforms": 32, "n": 1024, "log2n": 10 },
|
| 93 |
+
"attrs": { "inverse": 0, "onesided": 0 },
|
| 94 |
+
"inputs": {
|
| 95 |
+
"input": { "dtype": "float32", "shape": [32, 1024, 1], "dist": "normal", "seed": 922, "scale": 2 },
|
| 96 |
+
"dft_length": { "dtype": "int32", "shape": [], "dist": "constant", "value": 1024 }
|
| 97 |
+
},
|
| 98 |
+
"outputs": { "output": { "dtype": "float32", "shape": [32, 1024, 2], "dist": "empty" } },
|
| 99 |
+
"bench": { "metrics": [{ "type": "gflops", "value": "5 * args.transforms * args.n * args.log2n" }] }
|
| 100 |
+
},
|
| 101 |
+
{
|
| 102 |
+
"name": "audio-rfft-explicit-length2048-batch32-pathology",
|
| 103 |
+
"preset": "model",
|
| 104 |
+
"attrs": { "inverse": 0, "onesided": 1 },
|
| 105 |
+
"vars": { "transforms": 32, "n": 2048, "log2n": 11 },
|
| 106 |
+
"inputs": {
|
| 107 |
+
"input": { "dtype": "float32", "shape": [32, 2048, 1], "dist": "normal", "seed": 923, "scale": 2 },
|
| 108 |
+
"dft_length": { "dtype": "int32", "shape": [], "dist": "constant", "value": 2048 }
|
| 109 |
+
},
|
| 110 |
+
"outputs": { "output": { "dtype": "float32", "shape": [32, 1025, 2], "dist": "empty" } },
|
| 111 |
+
"bench": { "metrics": [{ "type": "gflops", "value": "5 * args.transforms * args.n * args.log2n" }] }
|
| 112 |
+
},
|
| 113 |
+
{
|
| 114 |
+
"name": "audio-fft-explicit-axis2048-batch32-pathology",
|
| 115 |
+
"preset": "stress",
|
| 116 |
+
"provenance": {
|
| 117 |
+
"notes": "Explicit-axis FFT paired with an equal-element-count implicit-axis control to expose the optional-axis dispatch boundary."
|
| 118 |
+
},
|
| 119 |
+
"attrs": { "inverse": 0, "onesided": 0 },
|
| 120 |
+
"vars": { "transforms": 32, "n": 2048, "log2n": 11 },
|
| 121 |
+
"inputs": {
|
| 122 |
+
"input": { "dtype": "float32", "shape": [32, 2048, 1], "dist": "normal", "seed": 926, "scale": 2 },
|
| 123 |
+
"axis": { "dtype": "int32", "shape": [], "dist": "constant", "value": 1 }
|
| 124 |
+
},
|
| 125 |
+
"outputs": { "output": { "dtype": "float32", "shape": [32, 2048, 2], "dist": "empty" } },
|
| 126 |
+
"bench": { "metrics": [{ "type": "gflops", "value": "5 * args.transforms * args.n * args.log2n" }] }
|
| 127 |
+
},
|
| 128 |
+
{
|
| 129 |
+
"name": "fft_storage_generic_batch_axis1_len8192_batch4_real_onesided",
|
| 130 |
+
"preset": "stress",
|
| 131 |
+
"vars": { "transforms": 4, "n": 8192, "log2n": 13 },
|
| 132 |
+
"attrs": { "inverse": 0, "onesided": 1 },
|
| 133 |
+
"inputs": { "input": { "dtype": "float32", "shape": [4, 8192, 1], "dist": "normal", "seed": 927, "scale": 2 } },
|
| 134 |
+
"outputs": { "output": { "dtype": "float32", "shape": [4, 4097, 2], "dist": "empty" } },
|
| 135 |
+
"bench": { "metrics": [{ "type": "gflops", "value": "5 * args.transforms * args.n * args.log2n" }] }
|
| 136 |
+
},
|
| 137 |
+
{
|
| 138 |
+
"name": "tiled_real_nonpow2_len4097_batch2_onesided",
|
| 139 |
+
"preset": "stress",
|
| 140 |
+
"vars": { "transforms": 2, "n": 4097, "bins": 2049 },
|
| 141 |
+
"attrs": { "inverse": 0, "onesided": 1 },
|
| 142 |
+
"inputs": { "input": { "dtype": "float32", "shape": [2, 4097, 1], "dist": "normal", "seed": 928, "scale": 2 } },
|
| 143 |
+
"outputs": { "output": { "dtype": "float32", "shape": [2, 2049, 2], "dist": "empty" } },
|
| 144 |
+
"bench": { "metrics": [{ "type": "gflops", "value": "8 * args.transforms * args.bins * args.n" }] }
|
| 145 |
+
}
|
| 146 |
+
]
|
| 147 |
+
}
|
build/webgpu/dft-contiguous-naive.wgsl.jinja
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{{ env.wgsl.resourceDeclarations }}
|
| 2 |
+
// Backend-stable unit-circle twiddles for DFT kernels. Retain the phase as a
|
| 3 |
+
// rational turn and route all polynomial evaluation through the shared precise
|
| 4 |
+
// trig core so FFT, direct DFT, STFT, and cosine windows agree exactly.
|
| 5 |
+
//
|
| 6 |
+
// phase is in [0, period). sign is -1 for a forward transform and +1 for an
|
| 7 |
+
// inverse transform. The result is (cos, sin).
|
| 8 |
+
// Backend-stable f32 sine/cosine core.
|
| 9 |
+
//
|
| 10 |
+
// Shader transcendental accuracy is implementation-defined, and some portable
|
| 11 |
+
// backends are only accurate to roughly 1e-4. Each path retains the most accurate
|
| 12 |
+
// available phase representation, reduces it to [-pi, pi], then uses these
|
| 13 |
+
// polynomials. The half-pi core is degree 13 for sine and degree 12 for cosine;
|
| 14 |
+
// truncation error is well below one f32 ULP over its documented interval.
|
| 15 |
+
const PRECISE_TRIG_PI: f32 = 3.141592653589793;
|
| 16 |
+
const PRECISE_TRIG_TWO_PI: f32 = 6.283185307179586;
|
| 17 |
+
const PRECISE_TRIG_HALF_PI: f32 = 1.5707963267948966;
|
| 18 |
+
|
| 19 |
+
// Input must be in [-pi/2, pi/2]. Returns (cos(x), sin(x)).
|
| 20 |
+
fn precise_sincos_half_pi(x: f32) -> vec2<f32> {
|
| 21 |
+
let x2 = x * x;
|
| 22 |
+
|
| 23 |
+
var sinPolynomial = 1.6059043836821613e-10;
|
| 24 |
+
sinPolynomial = fma(sinPolynomial, x2, -2.505210838544172e-8);
|
| 25 |
+
sinPolynomial = fma(sinPolynomial, x2, 2.7557319223985893e-6);
|
| 26 |
+
sinPolynomial = fma(sinPolynomial, x2, -1.984126984126984e-4);
|
| 27 |
+
sinPolynomial = fma(sinPolynomial, x2, 8.333333333333333e-3);
|
| 28 |
+
sinPolynomial = fma(sinPolynomial, x2, -1.6666666666666666e-1);
|
| 29 |
+
let sine = x * fma(sinPolynomial, x2, 1.0);
|
| 30 |
+
|
| 31 |
+
var cosPolynomial = 2.08767569878681e-9;
|
| 32 |
+
cosPolynomial = fma(cosPolynomial, x2, -2.755731922398589e-7);
|
| 33 |
+
cosPolynomial = fma(cosPolynomial, x2, 2.48015873015873e-5);
|
| 34 |
+
cosPolynomial = fma(cosPolynomial, x2, -1.388888888888889e-3);
|
| 35 |
+
cosPolynomial = fma(cosPolynomial, x2, 4.1666666666666664e-2);
|
| 36 |
+
cosPolynomial = fma(cosPolynomial, x2, -5.0e-1);
|
| 37 |
+
let cosine = fma(cosPolynomial, x2, 1.0);
|
| 38 |
+
|
| 39 |
+
return vec2<f32>(cosine, sine);
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
// Input must be in [-pi, pi]. Returns (cos(x), sin(x)).
|
| 43 |
+
fn precise_sincos_centered(x: f32) -> vec2<f32> {
|
| 44 |
+
var folded = x;
|
| 45 |
+
var cosineSign = 1.0;
|
| 46 |
+
if (folded > PRECISE_TRIG_HALF_PI) {
|
| 47 |
+
folded = PRECISE_TRIG_PI - folded;
|
| 48 |
+
cosineSign = -1.0;
|
| 49 |
+
} else if (folded < -PRECISE_TRIG_HALF_PI) {
|
| 50 |
+
folded = -PRECISE_TRIG_PI - folded;
|
| 51 |
+
cosineSign = -1.0;
|
| 52 |
+
}
|
| 53 |
+
let value = precise_sincos_half_pi(folded);
|
| 54 |
+
return vec2<f32>(cosineSign * value.x, value.y);
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
fn dft_precise_twiddle(phase: u32, period: u32, sign: f32) -> vec2<f32> {
|
| 59 |
+
let reducedPhase = phase % period;
|
| 60 |
+
if (reducedPhase == 0u) {
|
| 61 |
+
return vec2<f32>(1.0, sign * 0.0);
|
| 62 |
+
}
|
| 63 |
+
if (period % 4u == 0u) {
|
| 64 |
+
let quarter = period / 4u;
|
| 65 |
+
if (reducedPhase == quarter) {
|
| 66 |
+
return vec2<f32>(0.0, sign);
|
| 67 |
+
}
|
| 68 |
+
if (reducedPhase == 2u * quarter) {
|
| 69 |
+
return vec2<f32>(-1.0, sign * 0.0);
|
| 70 |
+
}
|
| 71 |
+
if (reducedPhase == 3u * quarter) {
|
| 72 |
+
return vec2<f32>(0.0, -sign);
|
| 73 |
+
}
|
| 74 |
+
}
|
| 75 |
+
// Center the rational turn before multiplying by 2*pi. This avoids the
|
| 76 |
+
// cancellation in `2*pi*(N-1)/N - 2*pi` for bins near the end of a row.
|
| 77 |
+
let turns = f32(reducedPhase) / f32(period);
|
| 78 |
+
let centeredTurns = turns - floor(turns + 0.5);
|
| 79 |
+
return precise_sincos_centered(sign * PRECISE_TRIG_TWO_PI * centeredTurns);
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
fn is_nyquist(freq: u32, signal_length: u32) -> bool {
|
| 84 |
+
return signal_length % 2u == 0u && freq * 2u == signal_length;
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
{% macro dft_naive_accumulate(transform_index, input_axis_length, output_axis_length, batch_index, other_index, include_batch=true, include_other=true) %}
|
| 88 |
+
var real = 0.0;
|
| 89 |
+
var imag = 0.0;
|
| 90 |
+
|
| 91 |
+
let inverse = params.inverse != 0u;
|
| 92 |
+
let onesided_inverse = inverse && params.onesided != 0u;
|
| 93 |
+
let signal_length = select({{ input_axis_length }}, {{ output_axis_length }}, onesided_inverse);
|
| 94 |
+
|
| 95 |
+
if (onesided_inverse) {
|
| 96 |
+
for (var freq = 0u; freq < {{ input_axis_length }}; freq = freq + 1u) {
|
| 97 |
+
let input_base = transform_input_base(freq{% if include_batch %}, {{ batch_index }}{% endif %}{% if include_other %}, {{ other_index }}{% endif %});
|
| 98 |
+
let xr = input[input_base];
|
| 99 |
+
var xi = 0.0;
|
| 100 |
+
if (params.inputComponents == 2u) {
|
| 101 |
+
xi = input[input_base + 1u];
|
| 102 |
+
}
|
| 103 |
+
let phase = ({{ transform_index }} * freq) % signal_length;
|
| 104 |
+
let twiddle = dft_precise_twiddle(phase, signal_length, 1.0);
|
| 105 |
+
let c = twiddle.x;
|
| 106 |
+
let s = twiddle.y;
|
| 107 |
+
var weight = 1.0;
|
| 108 |
+
if (freq != 0u && !is_nyquist(freq, signal_length)) {
|
| 109 |
+
weight = 2.0;
|
| 110 |
+
}
|
| 111 |
+
real = real + weight * (xr * c - xi * s);
|
| 112 |
+
imag = imag + weight * (xr * s + xi * c);
|
| 113 |
+
}
|
| 114 |
+
real = real / f32(signal_length);
|
| 115 |
+
imag = imag / f32(signal_length);
|
| 116 |
+
} else {
|
| 117 |
+
let sign = select(-1.0, 1.0, inverse);
|
| 118 |
+
for (var n = 0u; n < {{ input_axis_length }}; n = n + 1u) {
|
| 119 |
+
let input_base = transform_input_base(n{% if include_batch %}, {{ batch_index }}{% endif %}{% if include_other %}, {{ other_index }}{% endif %});
|
| 120 |
+
let xr = input[input_base];
|
| 121 |
+
var xi = 0.0;
|
| 122 |
+
if (params.inputComponents == 2u) {
|
| 123 |
+
xi = input[input_base + 1u];
|
| 124 |
+
}
|
| 125 |
+
let phase = ({{ transform_index }} * n) % signal_length;
|
| 126 |
+
let twiddle = dft_precise_twiddle(phase, signal_length, sign);
|
| 127 |
+
let c = twiddle.x;
|
| 128 |
+
let s = twiddle.y;
|
| 129 |
+
real = real + xr * c - xi * s;
|
| 130 |
+
imag = imag + xr * s + xi * c;
|
| 131 |
+
}
|
| 132 |
+
if (inverse) {
|
| 133 |
+
real = real / f32(signal_length);
|
| 134 |
+
imag = imag / f32(signal_length);
|
| 135 |
+
}
|
| 136 |
+
}
|
| 137 |
+
{% endmacro %}
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
fn transform_input_base(sample: u32{% if source.batched %}, batch: u32{% endif %}) -> u32 {
|
| 141 |
+
{% if source.batched %}
|
| 142 |
+
return (batch * params.signalLength + sample) * params.inputComponents;
|
| 143 |
+
{% else %}
|
| 144 |
+
return sample * params.inputComponents;
|
| 145 |
+
{% endif %}
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
@compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
|
| 149 |
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) nwg: vec3<u32>) {
|
| 150 |
+
// Fold a long 1D output grid into y at the device dispatch limit.
|
| 151 |
+
let index = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
|
| 152 |
+
if (index >= params.count) {
|
| 153 |
+
return;
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
{% if source.batched %}
|
| 157 |
+
let transform_index = index % params.outputLength;
|
| 158 |
+
let batch = index / params.outputLength;
|
| 159 |
+
{{ dft_naive_accumulate("transform_index", "params.signalLength", "params.outputLength", "batch", "0u", true, false) }}
|
| 160 |
+
{% else %}
|
| 161 |
+
let transform_index = index;
|
| 162 |
+
{{ dft_naive_accumulate("transform_index", "params.signalLength", "params.outputLength", "0u", "0u", false, false) }}
|
| 163 |
+
{% endif %}
|
| 164 |
+
|
| 165 |
+
let output_base = index * params.outputComponents;
|
| 166 |
+
output[output_base] = real;
|
| 167 |
+
if (params.outputComponents == 2u) {
|
| 168 |
+
output[output_base + 1u] = imag;
|
| 169 |
+
}
|
| 170 |
+
}
|
build/webgpu/dft-general.wgsl.jinja
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{{ env.wgsl.resourceDeclarations }}
|
| 2 |
+
// Backend-stable unit-circle twiddles for DFT kernels. Retain the phase as a
|
| 3 |
+
// rational turn and route all polynomial evaluation through the shared precise
|
| 4 |
+
// trig core so FFT, direct DFT, STFT, and cosine windows agree exactly.
|
| 5 |
+
//
|
| 6 |
+
// phase is in [0, period). sign is -1 for a forward transform and +1 for an
|
| 7 |
+
// inverse transform. The result is (cos, sin).
|
| 8 |
+
// Backend-stable f32 sine/cosine core.
|
| 9 |
+
//
|
| 10 |
+
// Shader transcendental accuracy is implementation-defined, and some portable
|
| 11 |
+
// backends are only accurate to roughly 1e-4. Each path retains the most accurate
|
| 12 |
+
// available phase representation, reduces it to [-pi, pi], then uses these
|
| 13 |
+
// polynomials. The half-pi core is degree 13 for sine and degree 12 for cosine;
|
| 14 |
+
// truncation error is well below one f32 ULP over its documented interval.
|
| 15 |
+
const PRECISE_TRIG_PI: f32 = 3.141592653589793;
|
| 16 |
+
const PRECISE_TRIG_TWO_PI: f32 = 6.283185307179586;
|
| 17 |
+
const PRECISE_TRIG_HALF_PI: f32 = 1.5707963267948966;
|
| 18 |
+
|
| 19 |
+
// Input must be in [-pi/2, pi/2]. Returns (cos(x), sin(x)).
|
| 20 |
+
fn precise_sincos_half_pi(x: f32) -> vec2<f32> {
|
| 21 |
+
let x2 = x * x;
|
| 22 |
+
|
| 23 |
+
var sinPolynomial = 1.6059043836821613e-10;
|
| 24 |
+
sinPolynomial = fma(sinPolynomial, x2, -2.505210838544172e-8);
|
| 25 |
+
sinPolynomial = fma(sinPolynomial, x2, 2.7557319223985893e-6);
|
| 26 |
+
sinPolynomial = fma(sinPolynomial, x2, -1.984126984126984e-4);
|
| 27 |
+
sinPolynomial = fma(sinPolynomial, x2, 8.333333333333333e-3);
|
| 28 |
+
sinPolynomial = fma(sinPolynomial, x2, -1.6666666666666666e-1);
|
| 29 |
+
let sine = x * fma(sinPolynomial, x2, 1.0);
|
| 30 |
+
|
| 31 |
+
var cosPolynomial = 2.08767569878681e-9;
|
| 32 |
+
cosPolynomial = fma(cosPolynomial, x2, -2.755731922398589e-7);
|
| 33 |
+
cosPolynomial = fma(cosPolynomial, x2, 2.48015873015873e-5);
|
| 34 |
+
cosPolynomial = fma(cosPolynomial, x2, -1.388888888888889e-3);
|
| 35 |
+
cosPolynomial = fma(cosPolynomial, x2, 4.1666666666666664e-2);
|
| 36 |
+
cosPolynomial = fma(cosPolynomial, x2, -5.0e-1);
|
| 37 |
+
let cosine = fma(cosPolynomial, x2, 1.0);
|
| 38 |
+
|
| 39 |
+
return vec2<f32>(cosine, sine);
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
// Input must be in [-pi, pi]. Returns (cos(x), sin(x)).
|
| 43 |
+
fn precise_sincos_centered(x: f32) -> vec2<f32> {
|
| 44 |
+
var folded = x;
|
| 45 |
+
var cosineSign = 1.0;
|
| 46 |
+
if (folded > PRECISE_TRIG_HALF_PI) {
|
| 47 |
+
folded = PRECISE_TRIG_PI - folded;
|
| 48 |
+
cosineSign = -1.0;
|
| 49 |
+
} else if (folded < -PRECISE_TRIG_HALF_PI) {
|
| 50 |
+
folded = -PRECISE_TRIG_PI - folded;
|
| 51 |
+
cosineSign = -1.0;
|
| 52 |
+
}
|
| 53 |
+
let value = precise_sincos_half_pi(folded);
|
| 54 |
+
return vec2<f32>(cosineSign * value.x, value.y);
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
fn dft_precise_twiddle(phase: u32, period: u32, sign: f32) -> vec2<f32> {
|
| 59 |
+
let reducedPhase = phase % period;
|
| 60 |
+
if (reducedPhase == 0u) {
|
| 61 |
+
return vec2<f32>(1.0, sign * 0.0);
|
| 62 |
+
}
|
| 63 |
+
if (period % 4u == 0u) {
|
| 64 |
+
let quarter = period / 4u;
|
| 65 |
+
if (reducedPhase == quarter) {
|
| 66 |
+
return vec2<f32>(0.0, sign);
|
| 67 |
+
}
|
| 68 |
+
if (reducedPhase == 2u * quarter) {
|
| 69 |
+
return vec2<f32>(-1.0, sign * 0.0);
|
| 70 |
+
}
|
| 71 |
+
if (reducedPhase == 3u * quarter) {
|
| 72 |
+
return vec2<f32>(0.0, -sign);
|
| 73 |
+
}
|
| 74 |
+
}
|
| 75 |
+
// Center the rational turn before multiplying by 2*pi. This avoids the
|
| 76 |
+
// cancellation in `2*pi*(N-1)/N - 2*pi` for bins near the end of a row.
|
| 77 |
+
let turns = f32(reducedPhase) / f32(period);
|
| 78 |
+
let centeredTurns = turns - floor(turns + 0.5);
|
| 79 |
+
return precise_sincos_centered(sign * PRECISE_TRIG_TWO_PI * centeredTurns);
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
// General naive DFT with an optional runtime transform axis and an optional
|
| 84 |
+
// runtime dft_length (opset-20 `axis` / `dft_length` tensor inputs). The output
|
| 85 |
+
// shape already encodes the per-axis transform length, so we read every output
|
| 86 |
+
// element's logical coords from the output dims, locate the transform axis, and
|
| 87 |
+
// sum over the (zero-padded / truncated) signal of `signal_length` samples.
|
| 88 |
+
//
|
| 89 |
+
// Layout: the last tensor dimension is the complex-component axis (1 = real,
|
| 90 |
+
// 2 = complex). The dims passed in params are the logical dims (tensor dims
|
| 91 |
+
// minus the component axis) packed into 7 slots; leading unused slots hold 1.
|
| 92 |
+
// `pad = 7 - n_logical` maps a logical axis to its padded slot, so the row-major
|
| 93 |
+
// stride math is uniform.
|
| 94 |
+
//
|
| 95 |
+
// Packing depends on whether the axis input is present. The default -2 axis
|
| 96 |
+
// uses a right-aligned (outer, axis, inner) shape. A runtime axis instead keeps
|
| 97 |
+
// every logical dimension right-aligned in the same seven slots (rank <= 8).
|
| 98 |
+
|
| 99 |
+
fn is_nyquist(freq: u32, signal_length: u32) -> bool {
|
| 100 |
+
return signal_length % 2u == 0u && freq * 2u == signal_length;
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
fn resolve_axis() -> u32 {
|
| 104 |
+
{% if source.hasAxisInput %}
|
| 105 |
+
let raw = axis_in[0];
|
| 106 |
+
let rank = i32(params.rank);
|
| 107 |
+
let norm = select(raw, raw + rank, raw < 0);
|
| 108 |
+
return u32(norm);
|
| 109 |
+
{% else %}
|
| 110 |
+
return params.axisAttr;
|
| 111 |
+
{% endif %}
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
@compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
|
| 115 |
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) nwg: vec3<u32>) {
|
| 116 |
+
// 2D-folded flat index over output elements (one per complex sample).
|
| 117 |
+
let index = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
|
| 118 |
+
if (index >= params.count) { return; }
|
| 119 |
+
|
| 120 |
+
let in_dims = array<u32, 7>(params.inD0, params.inD1, params.inD2, params.inD3, params.inD4, params.inD5, params.inD6);
|
| 121 |
+
let out_dims = array<u32, 7>(params.outD0, params.outD1, params.outD2, params.outD3, params.outD4, params.outD5, params.outD6);
|
| 122 |
+
|
| 123 |
+
// pad = number of leading 1-slots; logical dims live in [pad .. 6].
|
| 124 |
+
let n_logical = params.rank - 1u;
|
| 125 |
+
let pad = 7u - n_logical;
|
| 126 |
+
let resolved_axis = resolve_axis();
|
| 127 |
+
{% if source.skipRuntimeAxis1 %}
|
| 128 |
+
// Complementary to the guarded shared-memory FFT pass in the dynamic-axis
|
| 129 |
+
// specialization. Axis 1 is already complete, so this dispatch is a uniform
|
| 130 |
+
// no-op for that case.
|
| 131 |
+
if (resolved_axis == 1u) { return; }
|
| 132 |
+
{% endif %}
|
| 133 |
+
let transform_slot = resolved_axis + pad;
|
| 134 |
+
|
| 135 |
+
// Decode the right-aligned logical output coords from the flat index.
|
| 136 |
+
var coords = array<u32, 7>(0u, 0u, 0u, 0u, 0u, 0u, 0u);
|
| 137 |
+
var rem = index;
|
| 138 |
+
for (var slot = 7u; slot > pad; slot = slot - 1u) {
|
| 139 |
+
let dim = out_dims[slot - 1u];
|
| 140 |
+
coords[slot - 1u] = rem % dim;
|
| 141 |
+
rem = rem / dim;
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
let transform_index = coords[transform_slot];
|
| 145 |
+
let input_axis_length = in_dims[transform_slot];
|
| 146 |
+
let output_axis_length = out_dims[transform_slot];
|
| 147 |
+
|
| 148 |
+
let inverse = params.inverse != 0u;
|
| 149 |
+
let onesided_inverse = inverse && params.onesided != 0u;
|
| 150 |
+
var signal_length = input_axis_length;
|
| 151 |
+
{% if source.hasDftLengthInput %}
|
| 152 |
+
if (dft_len_in[0] > 0) { signal_length = u32(dft_len_in[0]); }
|
| 153 |
+
{% else %}
|
| 154 |
+
if (params.dftLengthAttr > 0u) { signal_length = params.dftLengthAttr; }
|
| 155 |
+
{% endif %}
|
| 156 |
+
if (onesided_inverse) { signal_length = output_axis_length; }
|
| 157 |
+
|
| 158 |
+
var real = 0.0;
|
| 159 |
+
var imag = 0.0;
|
| 160 |
+
|
| 161 |
+
if (onesided_inverse) {
|
| 162 |
+
for (var freq = 0u; freq < input_axis_length; freq = freq + 1u) {
|
| 163 |
+
var in_coords = coords;
|
| 164 |
+
in_coords[transform_slot] = freq;
|
| 165 |
+
var in_linear = 0u;
|
| 166 |
+
for (var slot = 0u; slot < 7u; slot = slot + 1u) {
|
| 167 |
+
in_linear = in_linear * in_dims[slot] + in_coords[slot];
|
| 168 |
+
}
|
| 169 |
+
let in_base = in_linear * params.inputComponents;
|
| 170 |
+
let xr = input[in_base];
|
| 171 |
+
var xi = 0.0;
|
| 172 |
+
if (params.inputComponents == 2u) { xi = input[in_base + 1u]; }
|
| 173 |
+
// Range-reduce the phase: (k*n mod N) keeps |angle| < 2*PI so cos/sin stay
|
| 174 |
+
// accurate. f32(k)*f32(n) for k,n up to ~2047 reaches ~12867 rad where f32
|
| 175 |
+
// ULP (~1e-3 rad) corrupts the twiddle; k*n is exact in u32 here.
|
| 176 |
+
let kn_mod = (transform_index * freq) % signal_length;
|
| 177 |
+
let twiddle = dft_precise_twiddle(kn_mod, signal_length, 1.0);
|
| 178 |
+
let c = twiddle.x;
|
| 179 |
+
let s = twiddle.y;
|
| 180 |
+
var weight = 1.0;
|
| 181 |
+
if (freq != 0u && !is_nyquist(freq, signal_length)) { weight = 2.0; }
|
| 182 |
+
real = real + weight * (xr * c - xi * s);
|
| 183 |
+
imag = imag + weight * (xr * s + xi * c);
|
| 184 |
+
}
|
| 185 |
+
real = real / f32(signal_length);
|
| 186 |
+
imag = imag / f32(signal_length);
|
| 187 |
+
} else {
|
| 188 |
+
let sign = select(-1.0, 1.0, inverse);
|
| 189 |
+
for (var n = 0u; n < signal_length; n = n + 1u) {
|
| 190 |
+
if (n >= input_axis_length) { continue; }
|
| 191 |
+
var in_coords = coords;
|
| 192 |
+
in_coords[transform_slot] = n;
|
| 193 |
+
var in_linear = 0u;
|
| 194 |
+
for (var slot = 0u; slot < 7u; slot = slot + 1u) {
|
| 195 |
+
in_linear = in_linear * in_dims[slot] + in_coords[slot];
|
| 196 |
+
}
|
| 197 |
+
let in_base = in_linear * params.inputComponents;
|
| 198 |
+
let xr = input[in_base];
|
| 199 |
+
var xi = 0.0;
|
| 200 |
+
if (params.inputComponents == 2u) { xi = input[in_base + 1u]; }
|
| 201 |
+
// Range-reduce the phase (see onesided_inverse branch): (k*n mod N) keeps
|
| 202 |
+
// |angle| < 2*PI; k*n is exact in u32 for the naive path's index range.
|
| 203 |
+
let kn_mod = (transform_index * n) % signal_length;
|
| 204 |
+
let twiddle = dft_precise_twiddle(kn_mod, signal_length, sign);
|
| 205 |
+
let c = twiddle.x;
|
| 206 |
+
let s = twiddle.y;
|
| 207 |
+
real = real + xr * c - xi * s;
|
| 208 |
+
imag = imag + xr * s + xi * c;
|
| 209 |
+
}
|
| 210 |
+
if (inverse) {
|
| 211 |
+
real = real / f32(signal_length);
|
| 212 |
+
imag = imag / f32(signal_length);
|
| 213 |
+
}
|
| 214 |
+
}
|
| 215 |
+
|
| 216 |
+
let output_base = index * params.outputComponents;
|
| 217 |
+
output[output_base] = real;
|
| 218 |
+
if (params.outputComponents == 2u) {
|
| 219 |
+
output[output_base + 1u] = imag;
|
| 220 |
+
}
|
| 221 |
+
}
|
build/webgpu/dft-rank4.wgsl.jinja
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{% macro flat_index_2d(name="i", bound="params.count", guardInline=false, note="dispatch-limit") %}
|
| 2 |
+
{% if note == "dispatch-limit" %}
|
| 3 |
+
// 2D-folded flat index: gid.y carries the high bits past the
|
| 4 |
+
// maxComputeWorkgroupsPerDimension dispatch limit (outputs > 16.7M elements).
|
| 5 |
+
{% elif note == "limit" %}
|
| 6 |
+
// 2D-folded flat index: gid.y carries the high bits past the
|
| 7 |
+
// maxComputeWorkgroupsPerDimension limit.
|
| 8 |
+
{% elif note == "device-axis" %}
|
| 9 |
+
// The flat dispatch is folded across x/y at the device's per-axis workgroup
|
| 10 |
+
// limit; gid.y carries the high portion of the output index.
|
| 11 |
+
{% elif note == "vec4-limit" %}
|
| 12 |
+
// 2D-folded flat vec4 index: gid.y carries the high bits past the
|
| 13 |
+
// maxComputeWorkgroupsPerDimension limit (the dispatch caps x and spills into y).
|
| 14 |
+
{% elif note == "element-limit" %}
|
| 15 |
+
// 2D-folded flat element index: gid.y carries the high bits past the
|
| 16 |
+
// maxComputeWorkgroupsPerDimension limit.
|
| 17 |
+
{% elif note == "dispatch" %}
|
| 18 |
+
// 2D-folded flat index: gid.y carries the high bits past the
|
| 19 |
+
// maxComputeWorkgroupsPerDimension dispatch limit.
|
| 20 |
+
{% endif %}
|
| 21 |
+
{% if bound == "" %}
|
| 22 |
+
let {{ name }} = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
|
| 23 |
+
{%- elif guardInline %}
|
| 24 |
+
let {{ name }} = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
|
| 25 |
+
if ({{ name }} >= {{ bound }}) { return; }
|
| 26 |
+
{%- else %}
|
| 27 |
+
let {{ name }} = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
|
| 28 |
+
if ({{ name }} >= {{ bound }}) {
|
| 29 |
+
return;
|
| 30 |
+
}
|
| 31 |
+
{%- endif %}
|
| 32 |
+
{% endmacro %}
|
| 33 |
+
|
| 34 |
+
{{ env.wgsl.resourceDeclarations }}
|
| 35 |
+
// Backend-stable unit-circle twiddles for DFT kernels. Retain the phase as a
|
| 36 |
+
// rational turn and route all polynomial evaluation through the shared precise
|
| 37 |
+
// trig core so FFT, direct DFT, STFT, and cosine windows agree exactly.
|
| 38 |
+
//
|
| 39 |
+
// phase is in [0, period). sign is -1 for a forward transform and +1 for an
|
| 40 |
+
// inverse transform. The result is (cos, sin).
|
| 41 |
+
// Backend-stable f32 sine/cosine core.
|
| 42 |
+
//
|
| 43 |
+
// Shader transcendental accuracy is implementation-defined, and some portable
|
| 44 |
+
// backends are only accurate to roughly 1e-4. Each path retains the most accurate
|
| 45 |
+
// available phase representation, reduces it to [-pi, pi], then uses these
|
| 46 |
+
// polynomials. The half-pi core is degree 13 for sine and degree 12 for cosine;
|
| 47 |
+
// truncation error is well below one f32 ULP over its documented interval.
|
| 48 |
+
const PRECISE_TRIG_PI: f32 = 3.141592653589793;
|
| 49 |
+
const PRECISE_TRIG_TWO_PI: f32 = 6.283185307179586;
|
| 50 |
+
const PRECISE_TRIG_HALF_PI: f32 = 1.5707963267948966;
|
| 51 |
+
|
| 52 |
+
// Input must be in [-pi/2, pi/2]. Returns (cos(x), sin(x)).
|
| 53 |
+
fn precise_sincos_half_pi(x: f32) -> vec2<f32> {
|
| 54 |
+
let x2 = x * x;
|
| 55 |
+
|
| 56 |
+
var sinPolynomial = 1.6059043836821613e-10;
|
| 57 |
+
sinPolynomial = fma(sinPolynomial, x2, -2.505210838544172e-8);
|
| 58 |
+
sinPolynomial = fma(sinPolynomial, x2, 2.7557319223985893e-6);
|
| 59 |
+
sinPolynomial = fma(sinPolynomial, x2, -1.984126984126984e-4);
|
| 60 |
+
sinPolynomial = fma(sinPolynomial, x2, 8.333333333333333e-3);
|
| 61 |
+
sinPolynomial = fma(sinPolynomial, x2, -1.6666666666666666e-1);
|
| 62 |
+
let sine = x * fma(sinPolynomial, x2, 1.0);
|
| 63 |
+
|
| 64 |
+
var cosPolynomial = 2.08767569878681e-9;
|
| 65 |
+
cosPolynomial = fma(cosPolynomial, x2, -2.755731922398589e-7);
|
| 66 |
+
cosPolynomial = fma(cosPolynomial, x2, 2.48015873015873e-5);
|
| 67 |
+
cosPolynomial = fma(cosPolynomial, x2, -1.388888888888889e-3);
|
| 68 |
+
cosPolynomial = fma(cosPolynomial, x2, 4.1666666666666664e-2);
|
| 69 |
+
cosPolynomial = fma(cosPolynomial, x2, -5.0e-1);
|
| 70 |
+
let cosine = fma(cosPolynomial, x2, 1.0);
|
| 71 |
+
|
| 72 |
+
return vec2<f32>(cosine, sine);
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
// Input must be in [-pi, pi]. Returns (cos(x), sin(x)).
|
| 76 |
+
fn precise_sincos_centered(x: f32) -> vec2<f32> {
|
| 77 |
+
var folded = x;
|
| 78 |
+
var cosineSign = 1.0;
|
| 79 |
+
if (folded > PRECISE_TRIG_HALF_PI) {
|
| 80 |
+
folded = PRECISE_TRIG_PI - folded;
|
| 81 |
+
cosineSign = -1.0;
|
| 82 |
+
} else if (folded < -PRECISE_TRIG_HALF_PI) {
|
| 83 |
+
folded = -PRECISE_TRIG_PI - folded;
|
| 84 |
+
cosineSign = -1.0;
|
| 85 |
+
}
|
| 86 |
+
let value = precise_sincos_half_pi(folded);
|
| 87 |
+
return vec2<f32>(cosineSign * value.x, value.y);
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
fn dft_precise_twiddle(phase: u32, period: u32, sign: f32) -> vec2<f32> {
|
| 92 |
+
let reducedPhase = phase % period;
|
| 93 |
+
if (reducedPhase == 0u) {
|
| 94 |
+
return vec2<f32>(1.0, sign * 0.0);
|
| 95 |
+
}
|
| 96 |
+
if (period % 4u == 0u) {
|
| 97 |
+
let quarter = period / 4u;
|
| 98 |
+
if (reducedPhase == quarter) {
|
| 99 |
+
return vec2<f32>(0.0, sign);
|
| 100 |
+
}
|
| 101 |
+
if (reducedPhase == 2u * quarter) {
|
| 102 |
+
return vec2<f32>(-1.0, sign * 0.0);
|
| 103 |
+
}
|
| 104 |
+
if (reducedPhase == 3u * quarter) {
|
| 105 |
+
return vec2<f32>(0.0, -sign);
|
| 106 |
+
}
|
| 107 |
+
}
|
| 108 |
+
// Center the rational turn before multiplying by 2*pi. This avoids the
|
| 109 |
+
// cancellation in `2*pi*(N-1)/N - 2*pi` for bins near the end of a row.
|
| 110 |
+
let turns = f32(reducedPhase) / f32(period);
|
| 111 |
+
let centeredTurns = turns - floor(turns + 0.5);
|
| 112 |
+
return precise_sincos_centered(sign * PRECISE_TRIG_TWO_PI * centeredTurns);
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
fn is_nyquist(freq: u32, signal_length: u32) -> bool {
|
| 117 |
+
return signal_length % 2u == 0u && freq * 2u == signal_length;
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
{% macro dft_naive_accumulate(transform_index, input_axis_length, output_axis_length, batch_index, other_index, include_batch=true, include_other=true) %}
|
| 121 |
+
var real = 0.0;
|
| 122 |
+
var imag = 0.0;
|
| 123 |
+
|
| 124 |
+
let inverse = params.inverse != 0u;
|
| 125 |
+
let onesided_inverse = inverse && params.onesided != 0u;
|
| 126 |
+
let signal_length = select({{ input_axis_length }}, {{ output_axis_length }}, onesided_inverse);
|
| 127 |
+
|
| 128 |
+
if (onesided_inverse) {
|
| 129 |
+
for (var freq = 0u; freq < {{ input_axis_length }}; freq = freq + 1u) {
|
| 130 |
+
let input_base = transform_input_base(freq{% if include_batch %}, {{ batch_index }}{% endif %}{% if include_other %}, {{ other_index }}{% endif %});
|
| 131 |
+
let xr = input[input_base];
|
| 132 |
+
var xi = 0.0;
|
| 133 |
+
if (params.inputComponents == 2u) {
|
| 134 |
+
xi = input[input_base + 1u];
|
| 135 |
+
}
|
| 136 |
+
let phase = ({{ transform_index }} * freq) % signal_length;
|
| 137 |
+
let twiddle = dft_precise_twiddle(phase, signal_length, 1.0);
|
| 138 |
+
let c = twiddle.x;
|
| 139 |
+
let s = twiddle.y;
|
| 140 |
+
var weight = 1.0;
|
| 141 |
+
if (freq != 0u && !is_nyquist(freq, signal_length)) {
|
| 142 |
+
weight = 2.0;
|
| 143 |
+
}
|
| 144 |
+
real = real + weight * (xr * c - xi * s);
|
| 145 |
+
imag = imag + weight * (xr * s + xi * c);
|
| 146 |
+
}
|
| 147 |
+
real = real / f32(signal_length);
|
| 148 |
+
imag = imag / f32(signal_length);
|
| 149 |
+
} else {
|
| 150 |
+
let sign = select(-1.0, 1.0, inverse);
|
| 151 |
+
for (var n = 0u; n < {{ input_axis_length }}; n = n + 1u) {
|
| 152 |
+
let input_base = transform_input_base(n{% if include_batch %}, {{ batch_index }}{% endif %}{% if include_other %}, {{ other_index }}{% endif %});
|
| 153 |
+
let xr = input[input_base];
|
| 154 |
+
var xi = 0.0;
|
| 155 |
+
if (params.inputComponents == 2u) {
|
| 156 |
+
xi = input[input_base + 1u];
|
| 157 |
+
}
|
| 158 |
+
let phase = ({{ transform_index }} * n) % signal_length;
|
| 159 |
+
let twiddle = dft_precise_twiddle(phase, signal_length, sign);
|
| 160 |
+
let c = twiddle.x;
|
| 161 |
+
let s = twiddle.y;
|
| 162 |
+
real = real + xr * c - xi * s;
|
| 163 |
+
imag = imag + xr * s + xi * c;
|
| 164 |
+
}
|
| 165 |
+
if (inverse) {
|
| 166 |
+
real = real / f32(signal_length);
|
| 167 |
+
imag = imag / f32(signal_length);
|
| 168 |
+
}
|
| 169 |
+
}
|
| 170 |
+
{% endmacro %}
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
fn input_index(batch: u32, d1: u32, d2: u32) -> u32 {
|
| 174 |
+
return ((batch * params.inputD1 + d1) * params.inputD2 + d2) * params.inputComponents;
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
fn output_index(batch: u32, d1: u32, d2: u32) -> u32 {
|
| 178 |
+
return ((batch * params.outputD1 + d1) * params.outputD2 + d2) * params.outputComponents;
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
fn transform_input_base(sample: u32, batch: u32, other: u32) -> u32 {
|
| 182 |
+
return input_index(batch, other, sample);
|
| 183 |
+
}
|
| 184 |
+
|
| 185 |
+
@compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
|
| 186 |
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) nwg: vec3<u32>) {
|
| 187 |
+
{{ flat_index_2d("index", guardInline=true) }}
|
| 188 |
+
|
| 189 |
+
let out_d2 = index % params.outputD2;
|
| 190 |
+
var t = index / params.outputD2;
|
| 191 |
+
let out_d1 = t % params.outputD1;
|
| 192 |
+
let batch = t / params.outputD1;
|
| 193 |
+
let transform_index = out_d2;
|
| 194 |
+
let other_index = out_d1;
|
| 195 |
+
let input_axis_length = params.inputD2;
|
| 196 |
+
let output_axis_length = params.outputD2;
|
| 197 |
+
|
| 198 |
+
{{ dft_naive_accumulate("transform_index", "input_axis_length", "output_axis_length", "batch", "other_index") }}
|
| 199 |
+
|
| 200 |
+
let out_base = output_index(batch, out_d1, out_d2);
|
| 201 |
+
output[out_base] = real;
|
| 202 |
+
if (params.outputComponents == 2u) {
|
| 203 |
+
output[out_base + 1u] = imag;
|
| 204 |
+
}
|
| 205 |
+
}
|
build/webgpu/dft-tiled-real.wgsl.jinja
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Portable direct transform for large real signals. A 256-lane workgroup owns
|
| 2 |
+
// 256 adjacent frequency bins and stages one input row or windowed STFT frame
|
| 3 |
+
// once, avoiding a global reload per (frequency, sample) pair. Twiddles advance
|
| 4 |
+
// by complex multiplication and reset from sin/cos every 64 samples to bound
|
| 5 |
+
// phase drift without transcendental functions in the inner loop.
|
| 6 |
+
{{ env.wgsl.resourceDeclarations }}
|
| 7 |
+
// Backend-stable unit-circle twiddles for DFT kernels. Retain the phase as a
|
| 8 |
+
// rational turn and route all polynomial evaluation through the shared precise
|
| 9 |
+
// trig core so FFT, direct DFT, STFT, and cosine windows agree exactly.
|
| 10 |
+
//
|
| 11 |
+
// phase is in [0, period). sign is -1 for a forward transform and +1 for an
|
| 12 |
+
// inverse transform. The result is (cos, sin).
|
| 13 |
+
// Backend-stable f32 sine/cosine core.
|
| 14 |
+
//
|
| 15 |
+
// Shader transcendental accuracy is implementation-defined, and some portable
|
| 16 |
+
// backends are only accurate to roughly 1e-4. Each path retains the most accurate
|
| 17 |
+
// available phase representation, reduces it to [-pi, pi], then uses these
|
| 18 |
+
// polynomials. The half-pi core is degree 13 for sine and degree 12 for cosine;
|
| 19 |
+
// truncation error is well below one f32 ULP over its documented interval.
|
| 20 |
+
const PRECISE_TRIG_PI: f32 = 3.141592653589793;
|
| 21 |
+
const PRECISE_TRIG_TWO_PI: f32 = 6.283185307179586;
|
| 22 |
+
const PRECISE_TRIG_HALF_PI: f32 = 1.5707963267948966;
|
| 23 |
+
|
| 24 |
+
// Input must be in [-pi/2, pi/2]. Returns (cos(x), sin(x)).
|
| 25 |
+
fn precise_sincos_half_pi(x: f32) -> vec2<f32> {
|
| 26 |
+
let x2 = x * x;
|
| 27 |
+
|
| 28 |
+
var sinPolynomial = 1.6059043836821613e-10;
|
| 29 |
+
sinPolynomial = fma(sinPolynomial, x2, -2.505210838544172e-8);
|
| 30 |
+
sinPolynomial = fma(sinPolynomial, x2, 2.7557319223985893e-6);
|
| 31 |
+
sinPolynomial = fma(sinPolynomial, x2, -1.984126984126984e-4);
|
| 32 |
+
sinPolynomial = fma(sinPolynomial, x2, 8.333333333333333e-3);
|
| 33 |
+
sinPolynomial = fma(sinPolynomial, x2, -1.6666666666666666e-1);
|
| 34 |
+
let sine = x * fma(sinPolynomial, x2, 1.0);
|
| 35 |
+
|
| 36 |
+
var cosPolynomial = 2.08767569878681e-9;
|
| 37 |
+
cosPolynomial = fma(cosPolynomial, x2, -2.755731922398589e-7);
|
| 38 |
+
cosPolynomial = fma(cosPolynomial, x2, 2.48015873015873e-5);
|
| 39 |
+
cosPolynomial = fma(cosPolynomial, x2, -1.388888888888889e-3);
|
| 40 |
+
cosPolynomial = fma(cosPolynomial, x2, 4.1666666666666664e-2);
|
| 41 |
+
cosPolynomial = fma(cosPolynomial, x2, -5.0e-1);
|
| 42 |
+
let cosine = fma(cosPolynomial, x2, 1.0);
|
| 43 |
+
|
| 44 |
+
return vec2<f32>(cosine, sine);
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
// Input must be in [-pi, pi]. Returns (cos(x), sin(x)).
|
| 48 |
+
fn precise_sincos_centered(x: f32) -> vec2<f32> {
|
| 49 |
+
var folded = x;
|
| 50 |
+
var cosineSign = 1.0;
|
| 51 |
+
if (folded > PRECISE_TRIG_HALF_PI) {
|
| 52 |
+
folded = PRECISE_TRIG_PI - folded;
|
| 53 |
+
cosineSign = -1.0;
|
| 54 |
+
} else if (folded < -PRECISE_TRIG_HALF_PI) {
|
| 55 |
+
folded = -PRECISE_TRIG_PI - folded;
|
| 56 |
+
cosineSign = -1.0;
|
| 57 |
+
}
|
| 58 |
+
let value = precise_sincos_half_pi(folded);
|
| 59 |
+
return vec2<f32>(cosineSign * value.x, value.y);
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
fn dft_precise_twiddle(phase: u32, period: u32, sign: f32) -> vec2<f32> {
|
| 64 |
+
let reducedPhase = phase % period;
|
| 65 |
+
if (reducedPhase == 0u) {
|
| 66 |
+
return vec2<f32>(1.0, sign * 0.0);
|
| 67 |
+
}
|
| 68 |
+
if (period % 4u == 0u) {
|
| 69 |
+
let quarter = period / 4u;
|
| 70 |
+
if (reducedPhase == quarter) {
|
| 71 |
+
return vec2<f32>(0.0, sign);
|
| 72 |
+
}
|
| 73 |
+
if (reducedPhase == 2u * quarter) {
|
| 74 |
+
return vec2<f32>(-1.0, sign * 0.0);
|
| 75 |
+
}
|
| 76 |
+
if (reducedPhase == 3u * quarter) {
|
| 77 |
+
return vec2<f32>(0.0, -sign);
|
| 78 |
+
}
|
| 79 |
+
}
|
| 80 |
+
// Center the rational turn before multiplying by 2*pi. This avoids the
|
| 81 |
+
// cancellation in `2*pi*(N-1)/N - 2*pi` for bins near the end of a row.
|
| 82 |
+
let turns = f32(reducedPhase) / f32(period);
|
| 83 |
+
let centeredTurns = turns - floor(turns + 0.5);
|
| 84 |
+
return precise_sincos_centered(sign * PRECISE_TRIG_TWO_PI * centeredTurns);
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
const N: u32 = {{ source.transformLength }}u;
|
| 88 |
+
const NUM_BINS: u32 = {{ source.numBins }}u;
|
| 89 |
+
const WG: u32 = {{ source.workgroupSize }}u;
|
| 90 |
+
const TWIDDLE_RESET: u32 = 64u;
|
| 91 |
+
|
| 92 |
+
var<workgroup> signalTile: array<f32, N>;
|
| 93 |
+
|
| 94 |
+
@compute @workgroup_size(WG, 1, 1)
|
| 95 |
+
fn main(
|
| 96 |
+
@builtin(workgroup_id) wg: vec3<u32>,
|
| 97 |
+
@builtin(local_invocation_id) lid: vec3<u32>,
|
| 98 |
+
) {
|
| 99 |
+
let lane = lid.x;
|
| 100 |
+
let batch = wg.y;
|
| 101 |
+
for (var n = lane; n < N; n = n + WG) {
|
| 102 |
+
signalTile[n] = input[(batch * N + n) * params.inner];
|
| 103 |
+
}
|
| 104 |
+
workgroupBarrier();
|
| 105 |
+
|
| 106 |
+
let freq = wg.x * WG + lane;
|
| 107 |
+
if (freq >= NUM_BINS) {
|
| 108 |
+
return;
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
let stepTwiddle = dft_precise_twiddle(freq, N, -1.0);
|
| 112 |
+
var real = 0.0;
|
| 113 |
+
var imag = 0.0;
|
| 114 |
+
|
| 115 |
+
for (var base = 0u; base < N; base = base + TWIDDLE_RESET) {
|
| 116 |
+
// Reduce the reset phase in integer space first. Computing angleStep*base
|
| 117 |
+
// directly loses enough f32 precision at high bins/long signals to create
|
| 118 |
+
// visible drift even though each 64-sample recurrence remains stable.
|
| 119 |
+
let phase = (freq * base) % N;
|
| 120 |
+
var twiddle = dft_precise_twiddle(phase, N, -1.0);
|
| 121 |
+
let end = min(N, base + TWIDDLE_RESET);
|
| 122 |
+
for (var n = base; n < end; n = n + 1u) {
|
| 123 |
+
let value = signalTile[n];
|
| 124 |
+
real = real + value * twiddle.x;
|
| 125 |
+
imag = imag + value * twiddle.y;
|
| 126 |
+
twiddle = vec2<f32>(
|
| 127 |
+
twiddle.x * stepTwiddle.x - twiddle.y * stepTwiddle.y,
|
| 128 |
+
twiddle.x * stepTwiddle.y + twiddle.y * stepTwiddle.x,
|
| 129 |
+
);
|
| 130 |
+
}
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
let outputBase = (batch * NUM_BINS + freq) * 2u;
|
| 134 |
+
output[outputBase] = real;
|
| 135 |
+
output[outputBase + 1u] = imag;
|
| 136 |
+
}
|
build/webgpu/fft-radix2-dit-storage.wgsl.jinja
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Portable single-dispatch radix-2 DIT FFT for transforms too large for the
|
| 2 |
+
// guaranteed workgroup-storage budget. One workgroup owns each transform and
|
| 3 |
+
// uses either its full-spectrum output or a private global scratch row as an
|
| 4 |
+
// in-place complex buffer. Uniform storageBarrier calls replace the global
|
| 5 |
+
// barrier that would otherwise require one dispatch per FFT stage.
|
| 6 |
+
//
|
| 7 |
+
// Direct output is used only when each transform owns a contiguous full-spectrum
|
| 8 |
+
// row. Scratch-backed DFT supports one-sided output. STFT accepts real or
|
| 9 |
+
// complex windowed frames and copies NUM_BINS after the final stage.
|
| 10 |
+
{{ env.wgsl.resourceDeclarations }}
|
| 11 |
+
{% set STFT = source.mode == "stft" %}
|
| 12 |
+
{% set USE_SCRATCH = source.mode != "dft" %}
|
| 13 |
+
{% if not STFT %}
|
| 14 |
+
// Backend-stable unit-circle twiddles for DFT kernels. Retain the phase as a
|
| 15 |
+
// rational turn and route all polynomial evaluation through the shared precise
|
| 16 |
+
// trig core so FFT, direct DFT, STFT, and cosine windows agree exactly.
|
| 17 |
+
//
|
| 18 |
+
// phase is in [0, period). sign is -1 for a forward transform and +1 for an
|
| 19 |
+
// inverse transform. The result is (cos, sin).
|
| 20 |
+
{% set needPreciseTrigCentered = true %}
|
| 21 |
+
{% set needPreciseTrigTwoPi = true %}
|
| 22 |
+
// Backend-stable f32 sine/cosine core.
|
| 23 |
+
//
|
| 24 |
+
// Shader transcendental accuracy is implementation-defined, and some portable
|
| 25 |
+
// backends are only accurate to roughly 1e-4. Each path retains the most accurate
|
| 26 |
+
// available phase representation, reduces it to [-pi, pi], then uses these
|
| 27 |
+
// polynomials. The half-pi core is degree 13 for sine and degree 12 for cosine;
|
| 28 |
+
// truncation error is well below one f32 ULP over its documented interval.
|
| 29 |
+
{% if needPreciseTrigCentered %}
|
| 30 |
+
const PRECISE_TRIG_PI: f32 = 3.141592653589793;
|
| 31 |
+
{% endif %}
|
| 32 |
+
{% if needPreciseTrigTwoPi %}
|
| 33 |
+
const PRECISE_TRIG_TWO_PI: f32 = 6.283185307179586;
|
| 34 |
+
{% endif %}
|
| 35 |
+
{% if needPreciseTrigCentered %}
|
| 36 |
+
const PRECISE_TRIG_HALF_PI: f32 = 1.5707963267948966;
|
| 37 |
+
{% endif %}
|
| 38 |
+
|
| 39 |
+
// Input must be in [-pi/2, pi/2]. Returns (cos(x), sin(x)).
|
| 40 |
+
fn precise_sincos_half_pi(x: f32) -> vec2<f32> {
|
| 41 |
+
let x2 = x * x;
|
| 42 |
+
|
| 43 |
+
var sinPolynomial = 1.6059043836821613e-10;
|
| 44 |
+
sinPolynomial = fma(sinPolynomial, x2, -2.505210838544172e-8);
|
| 45 |
+
sinPolynomial = fma(sinPolynomial, x2, 2.7557319223985893e-6);
|
| 46 |
+
sinPolynomial = fma(sinPolynomial, x2, -1.984126984126984e-4);
|
| 47 |
+
sinPolynomial = fma(sinPolynomial, x2, 8.333333333333333e-3);
|
| 48 |
+
sinPolynomial = fma(sinPolynomial, x2, -1.6666666666666666e-1);
|
| 49 |
+
let sine = x * fma(sinPolynomial, x2, 1.0);
|
| 50 |
+
|
| 51 |
+
var cosPolynomial = 2.08767569878681e-9;
|
| 52 |
+
cosPolynomial = fma(cosPolynomial, x2, -2.755731922398589e-7);
|
| 53 |
+
cosPolynomial = fma(cosPolynomial, x2, 2.48015873015873e-5);
|
| 54 |
+
cosPolynomial = fma(cosPolynomial, x2, -1.388888888888889e-3);
|
| 55 |
+
cosPolynomial = fma(cosPolynomial, x2, 4.1666666666666664e-2);
|
| 56 |
+
cosPolynomial = fma(cosPolynomial, x2, -5.0e-1);
|
| 57 |
+
let cosine = fma(cosPolynomial, x2, 1.0);
|
| 58 |
+
|
| 59 |
+
return vec2<f32>(cosine, sine);
|
| 60 |
+
}
|
| 61 |
+
{% if needPreciseTrigCentered %}
|
| 62 |
+
|
| 63 |
+
// Input must be in [-pi, pi]. Returns (cos(x), sin(x)).
|
| 64 |
+
fn precise_sincos_centered(x: f32) -> vec2<f32> {
|
| 65 |
+
var folded = x;
|
| 66 |
+
var cosineSign = 1.0;
|
| 67 |
+
if (folded > PRECISE_TRIG_HALF_PI) {
|
| 68 |
+
folded = PRECISE_TRIG_PI - folded;
|
| 69 |
+
cosineSign = -1.0;
|
| 70 |
+
} else if (folded < -PRECISE_TRIG_HALF_PI) {
|
| 71 |
+
folded = -PRECISE_TRIG_PI - folded;
|
| 72 |
+
cosineSign = -1.0;
|
| 73 |
+
}
|
| 74 |
+
let value = precise_sincos_half_pi(folded);
|
| 75 |
+
return vec2<f32>(cosineSign * value.x, value.y);
|
| 76 |
+
}
|
| 77 |
+
{% endif %}
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
fn dft_precise_twiddle(phase: u32, period: u32, sign: f32) -> vec2<f32> {
|
| 81 |
+
let reducedPhase = phase % period;
|
| 82 |
+
if (reducedPhase == 0u) {
|
| 83 |
+
return vec2<f32>(1.0, sign * 0.0);
|
| 84 |
+
}
|
| 85 |
+
if (period % 4u == 0u) {
|
| 86 |
+
let quarter = period / 4u;
|
| 87 |
+
if (reducedPhase == quarter) {
|
| 88 |
+
return vec2<f32>(0.0, sign);
|
| 89 |
+
}
|
| 90 |
+
if (reducedPhase == 2u * quarter) {
|
| 91 |
+
return vec2<f32>(-1.0, sign * 0.0);
|
| 92 |
+
}
|
| 93 |
+
if (reducedPhase == 3u * quarter) {
|
| 94 |
+
return vec2<f32>(0.0, -sign);
|
| 95 |
+
}
|
| 96 |
+
}
|
| 97 |
+
// Center the rational turn before multiplying by 2*pi. This avoids the
|
| 98 |
+
// cancellation in `2*pi*(N-1)/N - 2*pi` for bins near the end of a row.
|
| 99 |
+
let turns = f32(reducedPhase) / f32(period);
|
| 100 |
+
let centeredTurns = turns - floor(turns + 0.5);
|
| 101 |
+
return precise_sincos_centered(sign * PRECISE_TRIG_TWO_PI * centeredTurns);
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
{% endif %}
|
| 105 |
+
|
| 106 |
+
{% if STFT %}
|
| 107 |
+
const PI: f32 = 3.141592653589793;
|
| 108 |
+
{% endif %}
|
| 109 |
+
const N: u32 = {{ source.fftLength }}u;
|
| 110 |
+
const N_HALF: u32 = N / 2u;
|
| 111 |
+
const LOG2_N: u32 = {{ source.log2Length }}u;
|
| 112 |
+
{% if USE_SCRATCH %}
|
| 113 |
+
const NUM_BINS: u32 = {{ source.numBins }}u;
|
| 114 |
+
{% endif %}
|
| 115 |
+
const WG: u32 = {{ tunables.WORKGROUP_SIZE }}u;
|
| 116 |
+
const SIGN: f32 = {{ "1.0" if source.inverse else "-1.0" }};
|
| 117 |
+
|
| 118 |
+
@compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
|
| 119 |
+
fn main(
|
| 120 |
+
@builtin(workgroup_id) wgid: vec3<u32>,
|
| 121 |
+
@builtin(local_invocation_id) lid: vec3<u32>,
|
| 122 |
+
@builtin(num_workgroups) nwg: vec3<u32>,
|
| 123 |
+
) {
|
| 124 |
+
let tid = lid.x;
|
| 125 |
+
let transform = wgid.x + wgid.y * nwg.x;
|
| 126 |
+
let transformBase = transform * N * 2u;
|
| 127 |
+
|
| 128 |
+
{% if STFT %}
|
| 129 |
+
let batch = transform / params.numFrames;
|
| 130 |
+
let frame = transform - batch * params.numFrames;
|
| 131 |
+
let start = frame * params.frameStep;
|
| 132 |
+
{% else %}
|
| 133 |
+
let outerIndex = transform / params.inner;
|
| 134 |
+
let innerIndex = transform - outerIndex * params.inner;
|
| 135 |
+
{% endif %}
|
| 136 |
+
|
| 137 |
+
for (var n = tid; n < N; n = n + WG) {
|
| 138 |
+
var value = vec2<f32>(0.0);
|
| 139 |
+
{% if STFT %}
|
| 140 |
+
let signalIndex = start + n;
|
| 141 |
+
let inputBase = (batch * params.signalLength + signalIndex) * {{ source.inComp }}u;
|
| 142 |
+
let w = window[n];
|
| 143 |
+
value.x = signal[inputBase] * w;
|
| 144 |
+
{% if source.inComp == 2 %}
|
| 145 |
+
value.y = signal[inputBase + 1u] * w;
|
| 146 |
+
{% endif %}
|
| 147 |
+
{% else %}
|
| 148 |
+
let inputBase = ((outerIndex * N + n) * params.inner + innerIndex) * {{ source.inComp }}u;
|
| 149 |
+
value.x = input[inputBase];
|
| 150 |
+
{% if source.inComp == 2 %}
|
| 151 |
+
value.y = input[inputBase + 1u];
|
| 152 |
+
{% endif %}
|
| 153 |
+
{% endif %}
|
| 154 |
+
let reversed = reverseBits(n) >> (32u - LOG2_N);
|
| 155 |
+
let dst = transformBase + reversed * 2u;
|
| 156 |
+
{% if USE_SCRATCH %}
|
| 157 |
+
scratch[dst] = value.x;
|
| 158 |
+
scratch[dst + 1u] = value.y;
|
| 159 |
+
{% else %}
|
| 160 |
+
output[dst] = value.x;
|
| 161 |
+
output[dst + 1u] = value.y;
|
| 162 |
+
{% endif %}
|
| 163 |
+
}
|
| 164 |
+
storageBarrier();
|
| 165 |
+
|
| 166 |
+
{% for stage in range(1, source.log2Length + 1) %}
|
| 167 |
+
{
|
| 168 |
+
const SPAN: u32 = 1u << {{ stage }}u;
|
| 169 |
+
const HALF: u32 = SPAN >> 1u;
|
| 170 |
+
for (var butterfly = tid; butterfly < N_HALF; butterfly = butterfly + WG) {
|
| 171 |
+
let group = butterfly / HALF;
|
| 172 |
+
let j = butterfly - group * HALF;
|
| 173 |
+
let lo = transformBase + (group * SPAN + j) * 2u;
|
| 174 |
+
let hi = lo + HALF * 2u;
|
| 175 |
+
{% if USE_SCRATCH %}
|
| 176 |
+
let a = vec2<f32>(scratch[lo], scratch[lo + 1u]);
|
| 177 |
+
let b = vec2<f32>(scratch[hi], scratch[hi + 1u]);
|
| 178 |
+
{% else %}
|
| 179 |
+
let a = vec2<f32>(output[lo], output[lo + 1u]);
|
| 180 |
+
let b = vec2<f32>(output[hi], output[hi + 1u]);
|
| 181 |
+
{% endif %}
|
| 182 |
+
{% if STFT %}
|
| 183 |
+
let angle = SIGN * 2.0 * PI * f32(j) / f32(SPAN);
|
| 184 |
+
let c = cos(angle);
|
| 185 |
+
let sn = sin(angle);
|
| 186 |
+
{% else %}
|
| 187 |
+
let twiddle = dft_precise_twiddle(j, SPAN, SIGN);
|
| 188 |
+
let c = twiddle.x;
|
| 189 |
+
let sn = twiddle.y;
|
| 190 |
+
{% endif %}
|
| 191 |
+
let t = vec2<f32>(b.x * c - b.y * sn, b.x * sn + b.y * c);
|
| 192 |
+
let sum = a + t;
|
| 193 |
+
let difference = a - t;
|
| 194 |
+
{% if USE_SCRATCH %}
|
| 195 |
+
scratch[lo] = sum.x;
|
| 196 |
+
scratch[lo + 1u] = sum.y;
|
| 197 |
+
scratch[hi] = difference.x;
|
| 198 |
+
scratch[hi + 1u] = difference.y;
|
| 199 |
+
{% else %}
|
| 200 |
+
output[lo] = sum.x;
|
| 201 |
+
output[lo + 1u] = sum.y;
|
| 202 |
+
output[hi] = difference.x;
|
| 203 |
+
output[hi + 1u] = difference.y;
|
| 204 |
+
{% endif %}
|
| 205 |
+
}
|
| 206 |
+
storageBarrier();
|
| 207 |
+
}
|
| 208 |
+
{% endfor %}
|
| 209 |
+
|
| 210 |
+
{% if source.inverse %}
|
| 211 |
+
const SCALE: f32 = 1.0 / f32(N);
|
| 212 |
+
{% elif USE_SCRATCH %}
|
| 213 |
+
const SCALE: f32 = 1.0;
|
| 214 |
+
{% endif %}
|
| 215 |
+
{% if USE_SCRATCH %}
|
| 216 |
+
for (var k = tid; k < NUM_BINS; k = k + WG) {
|
| 217 |
+
let src = transformBase + k * 2u;
|
| 218 |
+
{% if STFT %}
|
| 219 |
+
let dst = (transform * NUM_BINS + k) * 2u;
|
| 220 |
+
{% else %}
|
| 221 |
+
let dst = ((outerIndex * NUM_BINS + k) * params.inner + innerIndex) * 2u;
|
| 222 |
+
{% endif %}
|
| 223 |
+
output[dst] = scratch[src] * SCALE;
|
| 224 |
+
output[dst + 1u] = scratch[src + 1u] * SCALE;
|
| 225 |
+
}
|
| 226 |
+
{% elif source.inverse %}
|
| 227 |
+
for (var k = tid; k < N; k = k + WG) {
|
| 228 |
+
let index = transformBase + k * 2u;
|
| 229 |
+
output[index] = output[index] * SCALE;
|
| 230 |
+
output[index + 1u] = output[index + 1u] * SCALE;
|
| 231 |
+
}
|
| 232 |
+
{% endif %}
|
| 233 |
+
}
|
build/webgpu/fft-stockham-shared.wgsl.jinja
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{{ env.wgsl.resourceDeclarations }}
|
| 2 |
+
// Backend-stable unit-circle twiddles for DFT kernels. Retain the phase as a
|
| 3 |
+
// rational turn and route all polynomial evaluation through the shared precise
|
| 4 |
+
// trig core so FFT, direct DFT, STFT, and cosine windows agree exactly.
|
| 5 |
+
//
|
| 6 |
+
// phase is in [0, period). sign is -1 for a forward transform and +1 for an
|
| 7 |
+
// inverse transform. The result is (cos, sin).
|
| 8 |
+
// Backend-stable f32 sine/cosine core.
|
| 9 |
+
//
|
| 10 |
+
// Shader transcendental accuracy is implementation-defined, and some portable
|
| 11 |
+
// backends are only accurate to roughly 1e-4. Each path retains the most accurate
|
| 12 |
+
// available phase representation, reduces it to [-pi, pi], then uses these
|
| 13 |
+
// polynomials. The half-pi core is degree 13 for sine and degree 12 for cosine;
|
| 14 |
+
// truncation error is well below one f32 ULP over its documented interval.
|
| 15 |
+
const PRECISE_TRIG_PI: f32 = 3.141592653589793;
|
| 16 |
+
const PRECISE_TRIG_TWO_PI: f32 = 6.283185307179586;
|
| 17 |
+
const PRECISE_TRIG_HALF_PI: f32 = 1.5707963267948966;
|
| 18 |
+
|
| 19 |
+
// Input must be in [-pi/2, pi/2]. Returns (cos(x), sin(x)).
|
| 20 |
+
fn precise_sincos_half_pi(x: f32) -> vec2<f32> {
|
| 21 |
+
let x2 = x * x;
|
| 22 |
+
|
| 23 |
+
var sinPolynomial = 1.6059043836821613e-10;
|
| 24 |
+
sinPolynomial = fma(sinPolynomial, x2, -2.505210838544172e-8);
|
| 25 |
+
sinPolynomial = fma(sinPolynomial, x2, 2.7557319223985893e-6);
|
| 26 |
+
sinPolynomial = fma(sinPolynomial, x2, -1.984126984126984e-4);
|
| 27 |
+
sinPolynomial = fma(sinPolynomial, x2, 8.333333333333333e-3);
|
| 28 |
+
sinPolynomial = fma(sinPolynomial, x2, -1.6666666666666666e-1);
|
| 29 |
+
let sine = x * fma(sinPolynomial, x2, 1.0);
|
| 30 |
+
|
| 31 |
+
var cosPolynomial = 2.08767569878681e-9;
|
| 32 |
+
cosPolynomial = fma(cosPolynomial, x2, -2.755731922398589e-7);
|
| 33 |
+
cosPolynomial = fma(cosPolynomial, x2, 2.48015873015873e-5);
|
| 34 |
+
cosPolynomial = fma(cosPolynomial, x2, -1.388888888888889e-3);
|
| 35 |
+
cosPolynomial = fma(cosPolynomial, x2, 4.1666666666666664e-2);
|
| 36 |
+
cosPolynomial = fma(cosPolynomial, x2, -5.0e-1);
|
| 37 |
+
let cosine = fma(cosPolynomial, x2, 1.0);
|
| 38 |
+
|
| 39 |
+
return vec2<f32>(cosine, sine);
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
// Input must be in [-pi, pi]. Returns (cos(x), sin(x)).
|
| 43 |
+
fn precise_sincos_centered(x: f32) -> vec2<f32> {
|
| 44 |
+
var folded = x;
|
| 45 |
+
var cosineSign = 1.0;
|
| 46 |
+
if (folded > PRECISE_TRIG_HALF_PI) {
|
| 47 |
+
folded = PRECISE_TRIG_PI - folded;
|
| 48 |
+
cosineSign = -1.0;
|
| 49 |
+
} else if (folded < -PRECISE_TRIG_HALF_PI) {
|
| 50 |
+
folded = -PRECISE_TRIG_PI - folded;
|
| 51 |
+
cosineSign = -1.0;
|
| 52 |
+
}
|
| 53 |
+
let value = precise_sincos_half_pi(folded);
|
| 54 |
+
return vec2<f32>(cosineSign * value.x, value.y);
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
fn dft_precise_twiddle(phase: u32, period: u32, sign: f32) -> vec2<f32> {
|
| 59 |
+
let reducedPhase = phase % period;
|
| 60 |
+
if (reducedPhase == 0u) {
|
| 61 |
+
return vec2<f32>(1.0, sign * 0.0);
|
| 62 |
+
}
|
| 63 |
+
if (period % 4u == 0u) {
|
| 64 |
+
let quarter = period / 4u;
|
| 65 |
+
if (reducedPhase == quarter) {
|
| 66 |
+
return vec2<f32>(0.0, sign);
|
| 67 |
+
}
|
| 68 |
+
if (reducedPhase == 2u * quarter) {
|
| 69 |
+
return vec2<f32>(-1.0, sign * 0.0);
|
| 70 |
+
}
|
| 71 |
+
if (reducedPhase == 3u * quarter) {
|
| 72 |
+
return vec2<f32>(0.0, -sign);
|
| 73 |
+
}
|
| 74 |
+
}
|
| 75 |
+
// Center the rational turn before multiplying by 2*pi. This avoids the
|
| 76 |
+
// cancellation in `2*pi*(N-1)/N - 2*pi` for bins near the end of a row.
|
| 77 |
+
let turns = f32(reducedPhase) / f32(period);
|
| 78 |
+
let centeredTurns = turns - floor(turns + 0.5);
|
| 79 |
+
return precise_sincos_centered(sign * PRECISE_TRIG_TWO_PI * centeredTurns);
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
// Radix-2 Stockham autosort FFT: one transform per workgroup, entirely in
|
| 84 |
+
// shared memory. log2(N) ping-pong stages alternate between the two halves of
|
| 85 |
+
// a 2*N scratch pair (no bit reversal; the autosort write pattern leaves the
|
| 86 |
+
// final stage in natural order). Threads stride over the N/2 butterflies of
|
| 87 |
+
// each stage with a workgroupBarrier between stages. Large transform batches
|
| 88 |
+
// may bind a compact precomputed twiddle table; smaller batches evaluate the
|
| 89 |
+
// same roots inline and avoid the extra dispatch.
|
| 90 |
+
// Mode "dft" views the input as [outer, N, inner, inComp] and the output as
|
| 91 |
+
// [outer, numBins, inner, 2]; params.inner carries the (runtime) inner count
|
| 92 |
+
// and dispatch.x = outer * inner workgroups. Mode "stft" runs one workgroup
|
| 93 |
+
// per (batch, frame): the window multiply is fused into the frame load. STFT
|
| 94 |
+
// selection admits only complete frames, so every frame load is in bounds. The
|
| 95 |
+
// inverse+onesided (IRFFT) combination is outside this specialization.
|
| 96 |
+
// Mode "logmel" runs one workgroup per spectrogram frame of
|
| 97 |
+
// a real mono signal: the frame load is centered (start - N/2) with reflection
|
| 98 |
+
// below zero and zeros at/after params.audioLength, and the epilogue folds the
|
| 99 |
+
// one-sided power spectrum through packed triangular mel bands into log10
|
| 100 |
+
// mel-major output instead of storing complex bins.
|
| 101 |
+
|
| 102 |
+
const N: u32 = {{ source.fftLength }}u;
|
| 103 |
+
const N_HALF: u32 = N / 2u;
|
| 104 |
+
const NUM_BINS: u32 = {{ source.numBins }}u;
|
| 105 |
+
const WG: u32 = {{ tunables.WORKGROUP_SIZE }}u;
|
| 106 |
+
// Twiddle = exp(SIGN * i * 2*pi * k / span); SIGN flips for the inverse
|
| 107 |
+
// transform, exactly like the naive kernels' `sign` select.
|
| 108 |
+
const SIGN: f32 = {{ "1.0" if source.inverse else "-1.0" }};
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
// Ping-pong scratch: stage reads [srcOff, srcOff+N), writes [dstOff, dstOff+N).
|
| 112 |
+
var<workgroup> shRe: array<f32, 2u * N>;
|
| 113 |
+
var<workgroup> shIm: array<f32, 2u * N>;
|
| 114 |
+
|
| 115 |
+
@compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
|
| 116 |
+
fn main(@builtin(workgroup_id) wgid: vec3<u32>,
|
| 117 |
+
@builtin(local_invocation_id) lid: vec3<u32>,
|
| 118 |
+
@builtin(num_workgroups) nwg: vec3<u32>) {
|
| 119 |
+
let tid = lid.x;
|
| 120 |
+
{% if source.runtimeAxis is defined and source.runtimeAxis %}
|
| 121 |
+
// Dynamic-axis DFT pairs this axis-specialized pass with a general pass.
|
| 122 |
+
// The uniform guard ensures exactly one performs work for each invocation.
|
| 123 |
+
let rawAxis = axis_in[0];
|
| 124 |
+
let normalizedAxis = select(rawAxis, rawAxis + {{ source.axisRank }}, rawAxis < 0);
|
| 125 |
+
if (normalizedAxis != {{ source.expectedAxis }}) { return; }
|
| 126 |
+
{% endif %}
|
| 127 |
+
// Flat workgroup index, 2D-folded: dispatches that would exceed the
|
| 128 |
+
// maxComputeWorkgroupsPerDimension limit on a single axis split the
|
| 129 |
+
// one-workgroup-per-(batch,frame) [or per-(outer,inner)] grid across x/y and
|
| 130 |
+
// recover the flat index here. Collapses to wgid.x when nwg.y == 1 (every DFT
|
| 131 |
+
// dispatch and any STFT dispatch under the per-axis limit).
|
| 132 |
+
let flat = wgid.x + wgid.y * nwg.x;
|
| 133 |
+
|
| 134 |
+
let outerIndex = flat / params.inner;
|
| 135 |
+
let innerIndex = flat - outerIndex * params.inner;
|
| 136 |
+
for (var n = tid; n < N; n = n + WG) {
|
| 137 |
+
let base = ((outerIndex * N + n) * params.inner + innerIndex) * {{ source.inComp }}u;
|
| 138 |
+
shRe[n] = input[base];
|
| 139 |
+
{% if source.inComp == 2 %}
|
| 140 |
+
shIm[n] = input[base + 1u];
|
| 141 |
+
{% else %}
|
| 142 |
+
shIm[n] = 0.0;
|
| 143 |
+
{% endif %}
|
| 144 |
+
}
|
| 145 |
+
workgroupBarrier();
|
| 146 |
+
|
| 147 |
+
// Stockham stages. Stage s (ns = 2^s) reads a = src[j], b = src[j + RADIX2_N/2],
|
| 148 |
+
// applies w = exp(SIGN*i*pi*(j mod ns)/ns) to b, and writes
|
| 149 |
+
// dst[(j div ns)*2*ns + (j mod ns)] = a + w*b
|
| 150 |
+
// dst[(j div ns)*2*ns + (j mod ns) + ns] = a - w*b
|
| 151 |
+
// |angle| <= pi, so twiddle accuracy never depends on large-argument
|
| 152 |
+
// sin/cos reduction (unlike the naive kernels' k*n angles).
|
| 153 |
+
var srcOff = 0u;
|
| 154 |
+
var dstOff = N;
|
| 155 |
+
for (var s = 0u; (1u << s) < N; s = s + 1u) {
|
| 156 |
+
let ns = 1u << s;
|
| 157 |
+
for (var j = tid; j < N_HALF; j = j + WG) {
|
| 158 |
+
let jm = j & (ns - 1u);
|
| 159 |
+
let outBase = ((j >> s) << (s + 1u)) + jm;
|
| 160 |
+
let ar = shRe[srcOff + j];
|
| 161 |
+
let ai = shIm[srcOff + j];
|
| 162 |
+
let br = shRe[srcOff + N_HALF + j];
|
| 163 |
+
let bi = shIm[srcOff + N_HALF + j];
|
| 164 |
+
let twiddle = dft_precise_twiddle(jm, 2u * ns, SIGN);
|
| 165 |
+
let c = twiddle.x;
|
| 166 |
+
let sn = twiddle.y;
|
| 167 |
+
let tr = br * c - bi * sn;
|
| 168 |
+
let ti = br * sn + bi * c;
|
| 169 |
+
shRe[dstOff + outBase] = ar + tr;
|
| 170 |
+
shIm[dstOff + outBase] = ai + ti;
|
| 171 |
+
shRe[dstOff + outBase + ns] = ar - tr;
|
| 172 |
+
shIm[dstOff + outBase + ns] = ai - ti;
|
| 173 |
+
}
|
| 174 |
+
workgroupBarrier();
|
| 175 |
+
let swap = srcOff;
|
| 176 |
+
srcOff = dstOff;
|
| 177 |
+
dstOff = swap;
|
| 178 |
+
}
|
| 179 |
+
|
| 180 |
+
// The final swap leaves the natural-order result at srcOff. The 1/N inverse
|
| 181 |
+
// scale is a power-of-two reciprocal, so the multiply is exact and matches
|
| 182 |
+
// the naive kernels' division.
|
| 183 |
+
{% if source.inverse %}
|
| 184 |
+
const SCALE: f32 = 1.0 / f32(N);
|
| 185 |
+
{% else %}
|
| 186 |
+
const SCALE: f32 = 1.0;
|
| 187 |
+
{% endif %}
|
| 188 |
+
for (var k = tid; k < NUM_BINS; k = k + WG) {
|
| 189 |
+
let outBase = ((outerIndex * NUM_BINS + k) * params.inner + innerIndex) * 2u;
|
| 190 |
+
output[outBase] = shRe[srcOff + k] * SCALE;
|
| 191 |
+
output[outBase + 1u] = shIm[srcOff + k] * SCALE;
|
| 192 |
+
}
|
| 193 |
+
}
|
build/webgpu/manifest.json
ADDED
|
@@ -0,0 +1,816 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"domain": "ai.onnx",
|
| 3 |
+
"name": "DFT",
|
| 4 |
+
"sinceVersion": 20,
|
| 5 |
+
"description": "Computes the discrete Fourier Transform (DFT) of the input along a specified axis. For a signal of length N, output bin k is `sum_{n=0}^{N-1} exp(-2*pi*j*k*n/N) * x[n]`; the inverse divides by N and negates the exponent sign. Supports forward/inverse, real-to-complex (RFFT), and complex-to-real (IRFFT) modes via the `onesided` and `inverse` attributes.",
|
| 6 |
+
"inputs": [
|
| 7 |
+
{
|
| 8 |
+
"role": "input",
|
| 9 |
+
"dtype": "T",
|
| 10 |
+
"description": "Input signal tensor; the last dimension is 1 for real values or 2 for complex `(real, imaginary)` pairs."
|
| 11 |
+
},
|
| 12 |
+
{
|
| 13 |
+
"role": "dft_length",
|
| 14 |
+
"dtype": "L",
|
| 15 |
+
"optional": true,
|
| 16 |
+
"description": "Optional int32 scalar controlling the signal length used for the transform; input is zero-padded or truncated to this length.",
|
| 17 |
+
"rank": 0
|
| 18 |
+
},
|
| 19 |
+
{
|
| 20 |
+
"role": "axis",
|
| 21 |
+
"dtype": "I",
|
| 22 |
+
"optional": true,
|
| 23 |
+
"description": "Optional logical int64 scalar specifying the dimension over which to compute the DFT (the last axis is reserved for the real/imaginary component and is excluded); signed axes use int32 WebGPU storage and default to `-2` when omitted.",
|
| 24 |
+
"rank": 0
|
| 25 |
+
}
|
| 26 |
+
],
|
| 27 |
+
"outputs": [
|
| 28 |
+
{
|
| 29 |
+
"role": "output",
|
| 30 |
+
"dtype": "T",
|
| 31 |
+
"description": "DFT result tensor; last dimension is 2 (complex) for forward DFT and RFFT, or 1 (real) for IRFFT.",
|
| 32 |
+
"rank": "ranks.input"
|
| 33 |
+
}
|
| 34 |
+
],
|
| 35 |
+
"attributes": { "inverse": 0, "onesided": 0 },
|
| 36 |
+
"attributeDescriptions": {
|
| 37 |
+
"inverse": "When set to 1, computes the inverse DFT (IDFT/IRFFT) instead of the forward transform; default is 0 (forward).",
|
| 38 |
+
"onesided": "When set to 1, exploits conjugate symmetry to return only the non-redundant half of the spectrum (RFFT for forward, IRFFT for inverse); default is 0 (full spectrum)."
|
| 39 |
+
},
|
| 40 |
+
"attributeConstraints": { "inverse": { "values": [0, 1] }, "onesided": { "values": [0, 1] } },
|
| 41 |
+
"typeConstraints": { "T": ["float32"], "L": ["int32"], "I": ["int64"] },
|
| 42 |
+
"args": {
|
| 43 |
+
"input": { "kind": "tensor", "semantic": "input", "role": "input" },
|
| 44 |
+
"dft_length": { "kind": "tensor", "semantic": "dft_length", "role": "dft_length", "required": false },
|
| 45 |
+
"axis": {
|
| 46 |
+
"kind": "tensor",
|
| 47 |
+
"semantic": "axis",
|
| 48 |
+
"role": "axis",
|
| 49 |
+
"dtype": "int32",
|
| 50 |
+
"narrowing": "checked",
|
| 51 |
+
"required": false
|
| 52 |
+
},
|
| 53 |
+
"output": { "kind": "tensor", "semantic": "output", "role": "output" }
|
| 54 |
+
},
|
| 55 |
+
"tunables": { "WORKGROUP_SIZE": 128, "TILED_REAL_WORKGROUP_SIZE": 256, "TILED_REAL_MIN_LENGTH": 512 },
|
| 56 |
+
"derive": {
|
| 57 |
+
"dftAxisAttr": "ranks.input - 2",
|
| 58 |
+
"dftAttrOuter": "outer(shapes.input, dftAxisAttr)",
|
| 59 |
+
"dftAttrInputInner": "inner(shapes.input, dftAxisAttr) / dim(shapes.input, ranks.input - 1)",
|
| 60 |
+
"dftAttrOutputOuter": "outer(shapes.output, dftAxisAttr)",
|
| 61 |
+
"dftAttrOutputInner": "inner(shapes.output, dftAxisAttr) / dim(shapes.output, ranks.output - 1)",
|
| 62 |
+
"rank3BatchDispatchFits": "dim(shapes.input, 0) <= device.limits.maxComputeWorkgroupsPerDimension",
|
| 63 |
+
"rank3Axis1SharedFftFits": "rank3BatchDispatchFits and pow2ceil(dim(shapes.input, 1)) == dim(shapes.input, 1) and dim(shapes.input, 1) >= 4 and dim(shapes.input, 1) * 16 <= device.limits.maxComputeWorkgroupStorageSize",
|
| 64 |
+
"rank3Axis1StorageFftFits": "rank3BatchDispatchFits and pow2ceil(dim(shapes.input, 1)) == dim(shapes.input, 1) and dim(shapes.input, 1) >= 4 and dim(shapes.input, 1) * 16 > device.limits.maxComputeWorkgroupStorageSize",
|
| 65 |
+
"rank3Axis1FftLog2": "log2ceil(dim(shapes.input, 1))",
|
| 66 |
+
"rank3Axis1ScratchFits": "dim(shapes.input, 0) * dim(shapes.input, 1) * 2 * 4 <= device.limits.maxStorageBufferBindingSize and dim(shapes.input, 0) * dim(shapes.input, 1) * 2 * 4 <= device.limits.maxBufferSize"
|
| 67 |
+
},
|
| 68 |
+
"bindingSets": {
|
| 69 |
+
"contiguousTransform": [
|
| 70 |
+
{
|
| 71 |
+
"name": "input",
|
| 72 |
+
"arg": "input",
|
| 73 |
+
"semantic": "input",
|
| 74 |
+
"buffer": { "type": "read-only-storage" },
|
| 75 |
+
"elementType": "f32"
|
| 76 |
+
},
|
| 77 |
+
{ "name": "output", "arg": "output", "semantic": "output", "buffer": { "type": "storage" }, "elementType": "f32" },
|
| 78 |
+
{
|
| 79 |
+
"name": "params",
|
| 80 |
+
"semantic": "kernel.params",
|
| 81 |
+
"buffer": { "type": "uniform" },
|
| 82 |
+
"struct": { "name": "Params", "fields": [{ "name": "inner", "type": "u32", "value": 1 }] }
|
| 83 |
+
}
|
| 84 |
+
],
|
| 85 |
+
"dftScratch": [
|
| 86 |
+
{
|
| 87 |
+
"name": "input",
|
| 88 |
+
"arg": "input",
|
| 89 |
+
"semantic": "input",
|
| 90 |
+
"buffer": { "type": "read-only-storage" },
|
| 91 |
+
"elementType": "f32"
|
| 92 |
+
},
|
| 93 |
+
{ "name": "scratch", "semantic": "fft_scratch", "buffer": { "type": "storage" }, "elementType": "f32" },
|
| 94 |
+
{ "name": "output", "arg": "output", "semantic": "output", "buffer": { "type": "storage" }, "elementType": "f32" },
|
| 95 |
+
{
|
| 96 |
+
"name": "params",
|
| 97 |
+
"semantic": "kernel.params",
|
| 98 |
+
"buffer": { "type": "uniform" },
|
| 99 |
+
"struct": { "name": "Params", "fields": [{ "name": "inner", "type": "u32", "value": 1 }] }
|
| 100 |
+
}
|
| 101 |
+
],
|
| 102 |
+
"fftLen": [
|
| 103 |
+
{
|
| 104 |
+
"name": "input",
|
| 105 |
+
"arg": "input",
|
| 106 |
+
"semantic": "input",
|
| 107 |
+
"buffer": { "type": "read-only-storage" },
|
| 108 |
+
"elementType": "f32"
|
| 109 |
+
},
|
| 110 |
+
{
|
| 111 |
+
"name": "dft_len_in",
|
| 112 |
+
"arg": "dft_length",
|
| 113 |
+
"semantic": "dft_length",
|
| 114 |
+
"buffer": { "type": "read-only-storage" },
|
| 115 |
+
"elementType": "i32",
|
| 116 |
+
"length": 1
|
| 117 |
+
},
|
| 118 |
+
{ "name": "output", "arg": "output", "semantic": "output", "buffer": { "type": "storage" }, "elementType": "f32" },
|
| 119 |
+
{
|
| 120 |
+
"name": "params",
|
| 121 |
+
"semantic": "kernel.params",
|
| 122 |
+
"buffer": { "type": "uniform" },
|
| 123 |
+
"struct": { "name": "Params", "fields": [{ "name": "inner", "type": "u32", "value": 1 }] }
|
| 124 |
+
}
|
| 125 |
+
],
|
| 126 |
+
"fftLenStatic": [
|
| 127 |
+
{
|
| 128 |
+
"name": "input",
|
| 129 |
+
"arg": "input",
|
| 130 |
+
"semantic": "input",
|
| 131 |
+
"buffer": { "type": "read-only-storage" },
|
| 132 |
+
"elementType": "f32"
|
| 133 |
+
},
|
| 134 |
+
{ "name": "output", "arg": "output", "semantic": "output", "buffer": { "type": "storage" }, "elementType": "f32" },
|
| 135 |
+
{
|
| 136 |
+
"name": "params",
|
| 137 |
+
"semantic": "kernel.params",
|
| 138 |
+
"buffer": { "type": "uniform" },
|
| 139 |
+
"struct": { "name": "Params", "fields": [{ "name": "inner", "type": "u32", "value": 1 }] }
|
| 140 |
+
}
|
| 141 |
+
],
|
| 142 |
+
"fftAxis": [
|
| 143 |
+
{
|
| 144 |
+
"name": "input",
|
| 145 |
+
"arg": "input",
|
| 146 |
+
"semantic": "input",
|
| 147 |
+
"buffer": { "type": "read-only-storage" },
|
| 148 |
+
"elementType": "f32"
|
| 149 |
+
},
|
| 150 |
+
{
|
| 151 |
+
"name": "axis_in",
|
| 152 |
+
"arg": "axis",
|
| 153 |
+
"semantic": "axis",
|
| 154 |
+
"buffer": { "type": "read-only-storage" },
|
| 155 |
+
"elementType": "i32",
|
| 156 |
+
"length": 1
|
| 157 |
+
},
|
| 158 |
+
{ "name": "output", "arg": "output", "semantic": "output", "buffer": { "type": "storage" }, "elementType": "f32" },
|
| 159 |
+
{
|
| 160 |
+
"name": "params",
|
| 161 |
+
"semantic": "kernel.params",
|
| 162 |
+
"buffer": { "type": "uniform" },
|
| 163 |
+
"struct": { "name": "Params", "fields": [{ "name": "inner", "type": "u32", "value": 1 }] }
|
| 164 |
+
}
|
| 165 |
+
],
|
| 166 |
+
"sharedFftTransform": [
|
| 167 |
+
{
|
| 168 |
+
"name": "input",
|
| 169 |
+
"arg": "input",
|
| 170 |
+
"semantic": "input",
|
| 171 |
+
"buffer": { "type": "read-only-storage" },
|
| 172 |
+
"elementType": "f32"
|
| 173 |
+
},
|
| 174 |
+
{ "name": "output", "arg": "output", "semantic": "output", "buffer": { "type": "storage" }, "elementType": "f32" },
|
| 175 |
+
{
|
| 176 |
+
"name": "params",
|
| 177 |
+
"semantic": "kernel.params",
|
| 178 |
+
"buffer": { "type": "uniform" },
|
| 179 |
+
"struct": {
|
| 180 |
+
"name": "Params",
|
| 181 |
+
"fields": [
|
| 182 |
+
{
|
| 183 |
+
"name": "inner",
|
| 184 |
+
"type": "u32",
|
| 185 |
+
"value": "inner(shapes.input, dftAxisAttr) / dim(shapes.input, ranks.input - 1)"
|
| 186 |
+
}
|
| 187 |
+
]
|
| 188 |
+
}
|
| 189 |
+
}
|
| 190 |
+
],
|
| 191 |
+
"rank2Naive": [
|
| 192 |
+
{
|
| 193 |
+
"name": "input",
|
| 194 |
+
"arg": "input",
|
| 195 |
+
"semantic": "input",
|
| 196 |
+
"buffer": { "type": "read-only-storage" },
|
| 197 |
+
"elementType": "f32"
|
| 198 |
+
},
|
| 199 |
+
{ "name": "output", "arg": "output", "semantic": "output", "buffer": { "type": "storage" }, "elementType": "f32" },
|
| 200 |
+
{
|
| 201 |
+
"name": "params",
|
| 202 |
+
"semantic": "kernel.params",
|
| 203 |
+
"buffer": { "type": "uniform" },
|
| 204 |
+
"struct": {
|
| 205 |
+
"name": "Params",
|
| 206 |
+
"fields": [
|
| 207 |
+
{ "name": "count", "type": "u32", "value": "numel(shapes.output) / dim(shapes.output, 1)" },
|
| 208 |
+
{ "name": "signalLength", "type": "u32", "value": "dim(shapes.input, 0)" },
|
| 209 |
+
{ "name": "inputComponents", "type": "u32", "value": "dim(shapes.input, 1)" },
|
| 210 |
+
{ "name": "outputComponents", "type": "u32", "value": "dim(shapes.output, 1)" },
|
| 211 |
+
{ "name": "outputLength", "type": "u32", "value": "dim(shapes.output, 0)" },
|
| 212 |
+
{ "name": "inverse", "type": "u32", "value": "attrs.inverse" },
|
| 213 |
+
{ "name": "onesided", "type": "u32", "value": "attrs.onesided" }
|
| 214 |
+
]
|
| 215 |
+
}
|
| 216 |
+
}
|
| 217 |
+
],
|
| 218 |
+
"rank3Naive": [
|
| 219 |
+
{
|
| 220 |
+
"name": "input",
|
| 221 |
+
"arg": "input",
|
| 222 |
+
"semantic": "input",
|
| 223 |
+
"buffer": { "type": "read-only-storage" },
|
| 224 |
+
"elementType": "f32"
|
| 225 |
+
},
|
| 226 |
+
{ "name": "output", "arg": "output", "semantic": "output", "buffer": { "type": "storage" }, "elementType": "f32" },
|
| 227 |
+
{
|
| 228 |
+
"name": "params",
|
| 229 |
+
"semantic": "kernel.params",
|
| 230 |
+
"buffer": { "type": "uniform" },
|
| 231 |
+
"struct": {
|
| 232 |
+
"name": "Params",
|
| 233 |
+
"fields": [
|
| 234 |
+
{ "name": "count", "type": "u32", "value": "numel(shapes.output) / dim(shapes.output, 2)" },
|
| 235 |
+
{ "name": "signalLength", "type": "u32", "value": "dim(shapes.input, 1)" },
|
| 236 |
+
{ "name": "inputComponents", "type": "u32", "value": "dim(shapes.input, 2)" },
|
| 237 |
+
{ "name": "outputComponents", "type": "u32", "value": "dim(shapes.output, 2)" },
|
| 238 |
+
{ "name": "outputLength", "type": "u32", "value": "dim(shapes.output, 1)" },
|
| 239 |
+
{ "name": "inverse", "type": "u32", "value": "attrs.inverse" },
|
| 240 |
+
{ "name": "onesided", "type": "u32", "value": "attrs.onesided" }
|
| 241 |
+
]
|
| 242 |
+
}
|
| 243 |
+
}
|
| 244 |
+
],
|
| 245 |
+
"rank4Naive": [
|
| 246 |
+
{
|
| 247 |
+
"name": "input",
|
| 248 |
+
"arg": "input",
|
| 249 |
+
"semantic": "input",
|
| 250 |
+
"buffer": { "type": "read-only-storage" },
|
| 251 |
+
"elementType": "f32"
|
| 252 |
+
},
|
| 253 |
+
{ "name": "output", "arg": "output", "semantic": "output", "buffer": { "type": "storage" }, "elementType": "f32" },
|
| 254 |
+
{
|
| 255 |
+
"name": "params",
|
| 256 |
+
"semantic": "kernel.params",
|
| 257 |
+
"buffer": { "type": "uniform" },
|
| 258 |
+
"struct": {
|
| 259 |
+
"name": "Params",
|
| 260 |
+
"fields": [
|
| 261 |
+
{ "name": "count", "type": "u32", "value": "numel(shapes.output) / dim(shapes.output, 3)" },
|
| 262 |
+
{ "name": "inputD1", "type": "u32", "value": "dim(shapes.input, 1)" },
|
| 263 |
+
{ "name": "inputD2", "type": "u32", "value": "dim(shapes.input, 2)" },
|
| 264 |
+
{ "name": "outputD1", "type": "u32", "value": "dim(shapes.output, 1)" },
|
| 265 |
+
{ "name": "outputD2", "type": "u32", "value": "dim(shapes.output, 2)" },
|
| 266 |
+
{ "name": "inputComponents", "type": "u32", "value": "dim(shapes.input, 3)" },
|
| 267 |
+
{ "name": "outputComponents", "type": "u32", "value": "dim(shapes.output, 3)" },
|
| 268 |
+
{ "name": "inverse", "type": "u32", "value": "attrs.inverse" },
|
| 269 |
+
{ "name": "onesided", "type": "u32", "value": "attrs.onesided" }
|
| 270 |
+
]
|
| 271 |
+
}
|
| 272 |
+
}
|
| 273 |
+
],
|
| 274 |
+
"genNone": [
|
| 275 |
+
{
|
| 276 |
+
"name": "input",
|
| 277 |
+
"arg": "input",
|
| 278 |
+
"semantic": "input",
|
| 279 |
+
"buffer": { "type": "read-only-storage" },
|
| 280 |
+
"elementType": "f32"
|
| 281 |
+
},
|
| 282 |
+
{ "name": "output", "arg": "output", "semantic": "output", "buffer": { "type": "storage" }, "elementType": "f32" },
|
| 283 |
+
{
|
| 284 |
+
"name": "params",
|
| 285 |
+
"semantic": "kernel.params",
|
| 286 |
+
"buffer": { "type": "uniform" },
|
| 287 |
+
"struct": {
|
| 288 |
+
"name": "Params",
|
| 289 |
+
"fields": [
|
| 290 |
+
{ "name": "count", "type": "u32", "value": "numel(shapes.output) / dim(shapes.output, ranks.output - 1)" },
|
| 291 |
+
{ "name": "rank", "type": "u32", "value": "4" },
|
| 292 |
+
{ "name": "inputComponents", "type": "u32", "value": "dim(shapes.input, ranks.input - 1)" },
|
| 293 |
+
{ "name": "outputComponents", "type": "u32", "value": "dim(shapes.output, ranks.output - 1)" },
|
| 294 |
+
{ "name": "inverse", "type": "u32", "value": "attrs.inverse" },
|
| 295 |
+
{ "name": "onesided", "type": "u32", "value": "attrs.onesided" },
|
| 296 |
+
{ "name": "inD0", "type": "u32", "value": 1 },
|
| 297 |
+
{ "name": "inD1", "type": "u32", "value": 1 },
|
| 298 |
+
{ "name": "inD2", "type": "u32", "value": 1 },
|
| 299 |
+
{ "name": "inD3", "type": "u32", "value": 1 },
|
| 300 |
+
{ "name": "inD4", "type": "u32", "value": "dftAttrOuter" },
|
| 301 |
+
{ "name": "inD5", "type": "u32", "value": "dim(shapes.input, dftAxisAttr)" },
|
| 302 |
+
{ "name": "inD6", "type": "u32", "value": "dftAttrInputInner" },
|
| 303 |
+
{ "name": "outD0", "type": "u32", "value": 1 },
|
| 304 |
+
{ "name": "outD1", "type": "u32", "value": 1 },
|
| 305 |
+
{ "name": "outD2", "type": "u32", "value": 1 },
|
| 306 |
+
{ "name": "outD3", "type": "u32", "value": 1 },
|
| 307 |
+
{ "name": "outD4", "type": "u32", "value": "dftAttrOutputOuter" },
|
| 308 |
+
{ "name": "outD5", "type": "u32", "value": "dim(shapes.output, dftAxisAttr)" },
|
| 309 |
+
{ "name": "outD6", "type": "u32", "value": "dftAttrOutputInner" },
|
| 310 |
+
{ "name": "axisAttr", "type": "u32", "value": "1" },
|
| 311 |
+
{ "name": "dftLengthAttr", "type": "u32", "value": 0 }
|
| 312 |
+
]
|
| 313 |
+
}
|
| 314 |
+
}
|
| 315 |
+
],
|
| 316 |
+
"genAxis": [
|
| 317 |
+
{
|
| 318 |
+
"name": "input",
|
| 319 |
+
"arg": "input",
|
| 320 |
+
"semantic": "input",
|
| 321 |
+
"buffer": { "type": "read-only-storage" },
|
| 322 |
+
"elementType": "f32"
|
| 323 |
+
},
|
| 324 |
+
{ "name": "output", "arg": "output", "semantic": "output", "buffer": { "type": "storage" }, "elementType": "f32" },
|
| 325 |
+
{
|
| 326 |
+
"name": "axis_in",
|
| 327 |
+
"arg": "axis",
|
| 328 |
+
"semantic": "axis",
|
| 329 |
+
"buffer": { "type": "read-only-storage" },
|
| 330 |
+
"elementType": "i32",
|
| 331 |
+
"length": 1
|
| 332 |
+
},
|
| 333 |
+
{
|
| 334 |
+
"name": "params",
|
| 335 |
+
"semantic": "kernel.params",
|
| 336 |
+
"buffer": { "type": "uniform" },
|
| 337 |
+
"struct": {
|
| 338 |
+
"name": "Params",
|
| 339 |
+
"fields": [
|
| 340 |
+
{ "name": "count", "type": "u32", "value": "numel(shapes.output) / dim(shapes.output, ranks.output - 1)" },
|
| 341 |
+
{ "name": "rank", "type": "u32", "value": "ranks.input" },
|
| 342 |
+
{ "name": "inputComponents", "type": "u32", "value": "dim(shapes.input, ranks.input - 1)" },
|
| 343 |
+
{ "name": "outputComponents", "type": "u32", "value": "dim(shapes.output, ranks.output - 1)" },
|
| 344 |
+
{ "name": "inverse", "type": "u32", "value": "attrs.inverse" },
|
| 345 |
+
{ "name": "onesided", "type": "u32", "value": "attrs.onesided" },
|
| 346 |
+
{ "name": "inD0", "type": "u32", "value": "dim(shapes.input, ranks.input - 8) if ranks.input >= 8 else 1" },
|
| 347 |
+
{ "name": "inD1", "type": "u32", "value": "dim(shapes.input, ranks.input - 7) if ranks.input >= 7 else 1" },
|
| 348 |
+
{ "name": "inD2", "type": "u32", "value": "dim(shapes.input, ranks.input - 6) if ranks.input >= 6 else 1" },
|
| 349 |
+
{ "name": "inD3", "type": "u32", "value": "dim(shapes.input, ranks.input - 5) if ranks.input >= 5 else 1" },
|
| 350 |
+
{ "name": "inD4", "type": "u32", "value": "dim(shapes.input, ranks.input - 4) if ranks.input >= 4 else 1" },
|
| 351 |
+
{ "name": "inD5", "type": "u32", "value": "dim(shapes.input, ranks.input - 3) if ranks.input >= 3 else 1" },
|
| 352 |
+
{ "name": "inD6", "type": "u32", "value": "dim(shapes.input, ranks.input - 2)" },
|
| 353 |
+
{
|
| 354 |
+
"name": "outD0",
|
| 355 |
+
"type": "u32",
|
| 356 |
+
"value": "dim(shapes.output, ranks.output - 8) if ranks.output >= 8 else 1"
|
| 357 |
+
},
|
| 358 |
+
{
|
| 359 |
+
"name": "outD1",
|
| 360 |
+
"type": "u32",
|
| 361 |
+
"value": "dim(shapes.output, ranks.output - 7) if ranks.output >= 7 else 1"
|
| 362 |
+
},
|
| 363 |
+
{
|
| 364 |
+
"name": "outD2",
|
| 365 |
+
"type": "u32",
|
| 366 |
+
"value": "dim(shapes.output, ranks.output - 6) if ranks.output >= 6 else 1"
|
| 367 |
+
},
|
| 368 |
+
{
|
| 369 |
+
"name": "outD3",
|
| 370 |
+
"type": "u32",
|
| 371 |
+
"value": "dim(shapes.output, ranks.output - 5) if ranks.output >= 5 else 1"
|
| 372 |
+
},
|
| 373 |
+
{
|
| 374 |
+
"name": "outD4",
|
| 375 |
+
"type": "u32",
|
| 376 |
+
"value": "dim(shapes.output, ranks.output - 4) if ranks.output >= 4 else 1"
|
| 377 |
+
},
|
| 378 |
+
{
|
| 379 |
+
"name": "outD5",
|
| 380 |
+
"type": "u32",
|
| 381 |
+
"value": "dim(shapes.output, ranks.output - 3) if ranks.output >= 3 else 1"
|
| 382 |
+
},
|
| 383 |
+
{ "name": "outD6", "type": "u32", "value": "dim(shapes.output, ranks.output - 2)" },
|
| 384 |
+
{ "name": "dftLengthAttr", "type": "u32", "value": 0 }
|
| 385 |
+
]
|
| 386 |
+
}
|
| 387 |
+
}
|
| 388 |
+
],
|
| 389 |
+
"genLen": [
|
| 390 |
+
{
|
| 391 |
+
"name": "input",
|
| 392 |
+
"arg": "input",
|
| 393 |
+
"semantic": "input",
|
| 394 |
+
"buffer": { "type": "read-only-storage" },
|
| 395 |
+
"elementType": "f32"
|
| 396 |
+
},
|
| 397 |
+
{ "name": "output", "arg": "output", "semantic": "output", "buffer": { "type": "storage" }, "elementType": "f32" },
|
| 398 |
+
{
|
| 399 |
+
"name": "dft_len_in",
|
| 400 |
+
"arg": "dft_length",
|
| 401 |
+
"semantic": "dft_length",
|
| 402 |
+
"buffer": { "type": "read-only-storage" },
|
| 403 |
+
"elementType": "i32",
|
| 404 |
+
"length": 1
|
| 405 |
+
},
|
| 406 |
+
{
|
| 407 |
+
"name": "params",
|
| 408 |
+
"semantic": "kernel.params",
|
| 409 |
+
"buffer": { "type": "uniform" },
|
| 410 |
+
"struct": {
|
| 411 |
+
"name": "Params",
|
| 412 |
+
"fields": [
|
| 413 |
+
{ "name": "count", "type": "u32", "value": "numel(shapes.output) / dim(shapes.output, ranks.output - 1)" },
|
| 414 |
+
{ "name": "rank", "type": "u32", "value": "4" },
|
| 415 |
+
{ "name": "inputComponents", "type": "u32", "value": "dim(shapes.input, ranks.input - 1)" },
|
| 416 |
+
{ "name": "outputComponents", "type": "u32", "value": "dim(shapes.output, ranks.output - 1)" },
|
| 417 |
+
{ "name": "inverse", "type": "u32", "value": "attrs.inverse" },
|
| 418 |
+
{ "name": "onesided", "type": "u32", "value": "attrs.onesided" },
|
| 419 |
+
{ "name": "inD0", "type": "u32", "value": 1 },
|
| 420 |
+
{ "name": "inD1", "type": "u32", "value": 1 },
|
| 421 |
+
{ "name": "inD2", "type": "u32", "value": 1 },
|
| 422 |
+
{ "name": "inD3", "type": "u32", "value": 1 },
|
| 423 |
+
{ "name": "inD4", "type": "u32", "value": "dftAttrOuter" },
|
| 424 |
+
{ "name": "inD5", "type": "u32", "value": "dim(shapes.input, dftAxisAttr)" },
|
| 425 |
+
{ "name": "inD6", "type": "u32", "value": "dftAttrInputInner" },
|
| 426 |
+
{ "name": "outD0", "type": "u32", "value": 1 },
|
| 427 |
+
{ "name": "outD1", "type": "u32", "value": 1 },
|
| 428 |
+
{ "name": "outD2", "type": "u32", "value": 1 },
|
| 429 |
+
{ "name": "outD3", "type": "u32", "value": 1 },
|
| 430 |
+
{ "name": "outD4", "type": "u32", "value": "dftAttrOutputOuter" },
|
| 431 |
+
{ "name": "outD5", "type": "u32", "value": "dim(shapes.output, dftAxisAttr)" },
|
| 432 |
+
{ "name": "outD6", "type": "u32", "value": "dftAttrOutputInner" },
|
| 433 |
+
{ "name": "axisAttr", "type": "u32", "value": "1" }
|
| 434 |
+
]
|
| 435 |
+
}
|
| 436 |
+
}
|
| 437 |
+
],
|
| 438 |
+
"genAxisLen": [
|
| 439 |
+
{
|
| 440 |
+
"name": "input",
|
| 441 |
+
"arg": "input",
|
| 442 |
+
"semantic": "input",
|
| 443 |
+
"buffer": { "type": "read-only-storage" },
|
| 444 |
+
"elementType": "f32"
|
| 445 |
+
},
|
| 446 |
+
{ "name": "output", "arg": "output", "semantic": "output", "buffer": { "type": "storage" }, "elementType": "f32" },
|
| 447 |
+
{
|
| 448 |
+
"name": "dft_len_in",
|
| 449 |
+
"arg": "dft_length",
|
| 450 |
+
"semantic": "dft_length",
|
| 451 |
+
"buffer": { "type": "read-only-storage" },
|
| 452 |
+
"elementType": "i32",
|
| 453 |
+
"length": 1
|
| 454 |
+
},
|
| 455 |
+
{
|
| 456 |
+
"name": "axis_in",
|
| 457 |
+
"arg": "axis",
|
| 458 |
+
"semantic": "axis",
|
| 459 |
+
"buffer": { "type": "read-only-storage" },
|
| 460 |
+
"elementType": "i32",
|
| 461 |
+
"length": 1
|
| 462 |
+
},
|
| 463 |
+
{
|
| 464 |
+
"name": "params",
|
| 465 |
+
"semantic": "kernel.params",
|
| 466 |
+
"buffer": { "type": "uniform" },
|
| 467 |
+
"struct": {
|
| 468 |
+
"name": "Params",
|
| 469 |
+
"fields": [
|
| 470 |
+
{ "name": "count", "type": "u32", "value": "numel(shapes.output) / dim(shapes.output, ranks.output - 1)" },
|
| 471 |
+
{ "name": "rank", "type": "u32", "value": "ranks.input" },
|
| 472 |
+
{ "name": "inputComponents", "type": "u32", "value": "dim(shapes.input, ranks.input - 1)" },
|
| 473 |
+
{ "name": "outputComponents", "type": "u32", "value": "dim(shapes.output, ranks.output - 1)" },
|
| 474 |
+
{ "name": "inverse", "type": "u32", "value": "attrs.inverse" },
|
| 475 |
+
{ "name": "onesided", "type": "u32", "value": "attrs.onesided" },
|
| 476 |
+
{ "name": "inD0", "type": "u32", "value": "dim(shapes.input, ranks.input - 8) if ranks.input >= 8 else 1" },
|
| 477 |
+
{ "name": "inD1", "type": "u32", "value": "dim(shapes.input, ranks.input - 7) if ranks.input >= 7 else 1" },
|
| 478 |
+
{ "name": "inD2", "type": "u32", "value": "dim(shapes.input, ranks.input - 6) if ranks.input >= 6 else 1" },
|
| 479 |
+
{ "name": "inD3", "type": "u32", "value": "dim(shapes.input, ranks.input - 5) if ranks.input >= 5 else 1" },
|
| 480 |
+
{ "name": "inD4", "type": "u32", "value": "dim(shapes.input, ranks.input - 4) if ranks.input >= 4 else 1" },
|
| 481 |
+
{ "name": "inD5", "type": "u32", "value": "dim(shapes.input, ranks.input - 3) if ranks.input >= 3 else 1" },
|
| 482 |
+
{ "name": "inD6", "type": "u32", "value": "dim(shapes.input, ranks.input - 2)" },
|
| 483 |
+
{
|
| 484 |
+
"name": "outD0",
|
| 485 |
+
"type": "u32",
|
| 486 |
+
"value": "dim(shapes.output, ranks.output - 8) if ranks.output >= 8 else 1"
|
| 487 |
+
},
|
| 488 |
+
{
|
| 489 |
+
"name": "outD1",
|
| 490 |
+
"type": "u32",
|
| 491 |
+
"value": "dim(shapes.output, ranks.output - 7) if ranks.output >= 7 else 1"
|
| 492 |
+
},
|
| 493 |
+
{
|
| 494 |
+
"name": "outD2",
|
| 495 |
+
"type": "u32",
|
| 496 |
+
"value": "dim(shapes.output, ranks.output - 6) if ranks.output >= 6 else 1"
|
| 497 |
+
},
|
| 498 |
+
{
|
| 499 |
+
"name": "outD3",
|
| 500 |
+
"type": "u32",
|
| 501 |
+
"value": "dim(shapes.output, ranks.output - 5) if ranks.output >= 5 else 1"
|
| 502 |
+
},
|
| 503 |
+
{
|
| 504 |
+
"name": "outD4",
|
| 505 |
+
"type": "u32",
|
| 506 |
+
"value": "dim(shapes.output, ranks.output - 4) if ranks.output >= 4 else 1"
|
| 507 |
+
},
|
| 508 |
+
{
|
| 509 |
+
"name": "outD5",
|
| 510 |
+
"type": "u32",
|
| 511 |
+
"value": "dim(shapes.output, ranks.output - 3) if ranks.output >= 3 else 1"
|
| 512 |
+
},
|
| 513 |
+
{ "name": "outD6", "type": "u32", "value": "dim(shapes.output, ranks.output - 2)" }
|
| 514 |
+
]
|
| 515 |
+
}
|
| 516 |
+
}
|
| 517 |
+
]
|
| 518 |
+
},
|
| 519 |
+
"variants": [
|
| 520 |
+
{
|
| 521 |
+
"id": "batch_runtime_axis1_fft",
|
| 522 |
+
"priority": 30,
|
| 523 |
+
"when": ["present.axis", "numel(shapes.axis) == 1", "not present.dft_length", "ranks.input == 3", "ranks.output == 3", "attrs.onesided == 0", "dim(shapes.input, 0) == dim(shapes.output, 0)", "dim(shapes.input, 1) == dim(shapes.output, 1)", "rank3Axis1SharedFftFits", "dim(shapes.input, 2) >= 1", "dim(shapes.input, 2) <= 2", "dim(shapes.output, 2) == 2"],
|
| 524 |
+
"passes": [
|
| 525 |
+
{
|
| 526 |
+
"id": "axis1_fft",
|
| 527 |
+
"name": "DFT.BatchRuntimeAxis1FftShared",
|
| 528 |
+
"source": {
|
| 529 |
+
"shader": "fft-stockham-shared.wgsl.jinja",
|
| 530 |
+
"inputs": {
|
| 531 |
+
"mode": "\"dft\"",
|
| 532 |
+
"fftLength": "dim(shapes.input, 1)",
|
| 533 |
+
"numBins": "dim(shapes.output, 1)",
|
| 534 |
+
"inComp": "dim(shapes.input, 2)",
|
| 535 |
+
"inverse": "attrs.inverse != 0",
|
| 536 |
+
"runtimeAxis": true,
|
| 537 |
+
"axisRank": 3,
|
| 538 |
+
"expectedAxis": 1
|
| 539 |
+
}
|
| 540 |
+
},
|
| 541 |
+
"bindings": "fftAxis",
|
| 542 |
+
"dispatch": { "x": "dim(shapes.input, 0)" }
|
| 543 |
+
},
|
| 544 |
+
{
|
| 545 |
+
"id": "other_axis_general",
|
| 546 |
+
"name": "DFT.BatchRuntimeOtherAxisGeneral",
|
| 547 |
+
"source": {
|
| 548 |
+
"shader": "dft-general.wgsl.jinja",
|
| 549 |
+
"inputs": { "hasAxisInput": true, "hasDftLengthInput": false, "skipRuntimeAxis1": true }
|
| 550 |
+
},
|
| 551 |
+
"bindings": "genAxis",
|
| 552 |
+
"dispatch": {
|
| 553 |
+
"threads": "numel(shapes.output) / dim(shapes.output, ranks.output - 1)",
|
| 554 |
+
"workgroupSize": "tunables.WORKGROUP_SIZE"
|
| 555 |
+
}
|
| 556 |
+
}
|
| 557 |
+
]
|
| 558 |
+
},
|
| 559 |
+
{
|
| 560 |
+
"id": "batch_axis1_fft_storage_onesided",
|
| 561 |
+
"priority": 32,
|
| 562 |
+
"when": ["not present.axis", "not present.dft_length", "ranks.input == 3", "ranks.output == 3", "dftAxisAttr == 1", "attrs.onesided != 0", "attrs.inverse == 0", "dim(shapes.input, 0) == dim(shapes.output, 0)", "dim(shapes.input, 0) > 0", "rank3BatchDispatchFits", "rank3Axis1StorageFftFits", "rank3Axis1ScratchFits", "dim(shapes.output, 1) == floor(dim(shapes.input, 1) / 2) + 1", "dim(shapes.input, 2) == 1", "dim(shapes.output, 2) == 2"],
|
| 563 |
+
"intermediates": [
|
| 564 |
+
{ "id": "fft_scratch", "dtype": "float32", "shape": "[dim(shapes.input, 0) * dim(shapes.input, 1) * 2]" }
|
| 565 |
+
],
|
| 566 |
+
"passes": [
|
| 567 |
+
{
|
| 568 |
+
"id": "main",
|
| 569 |
+
"name": "DFT.BatchAxis1FftStorageOnesided",
|
| 570 |
+
"source": {
|
| 571 |
+
"shader": "fft-radix2-dit-storage.wgsl.jinja",
|
| 572 |
+
"inputs": {
|
| 573 |
+
"mode": "\"dft_scratch\"",
|
| 574 |
+
"fftLength": "dim(shapes.input, 1)",
|
| 575 |
+
"log2Length": "rank3Axis1FftLog2",
|
| 576 |
+
"numBins": "dim(shapes.output, 1)",
|
| 577 |
+
"inComp": 1,
|
| 578 |
+
"inverse": false
|
| 579 |
+
}
|
| 580 |
+
},
|
| 581 |
+
"bindings": "dftScratch",
|
| 582 |
+
"dispatch": { "x": "dim(shapes.input, 0)" }
|
| 583 |
+
}
|
| 584 |
+
]
|
| 585 |
+
},
|
| 586 |
+
{
|
| 587 |
+
"id": "batch_axis1_fft_storage_inplace",
|
| 588 |
+
"priority": 31,
|
| 589 |
+
"when": ["not present.axis", "not present.dft_length", "ranks.input == 3", "ranks.output == 3", "dftAxisAttr == 1", "attrs.onesided == 0", "dim(shapes.input, 0) == dim(shapes.output, 0)", "rank3BatchDispatchFits", "rank3Axis1StorageFftFits", "dim(shapes.output, 1) == dim(shapes.input, 1)", "dim(shapes.input, 2) >= 1", "dim(shapes.input, 2) <= 2", "dim(shapes.output, 2) == 2"],
|
| 590 |
+
"passes": [
|
| 591 |
+
{
|
| 592 |
+
"id": "main",
|
| 593 |
+
"name": "DFT.BatchAxis1FftStorageInplace",
|
| 594 |
+
"source": {
|
| 595 |
+
"shader": "fft-radix2-dit-storage.wgsl.jinja",
|
| 596 |
+
"inputs": {
|
| 597 |
+
"mode": "\"dft\"",
|
| 598 |
+
"fftLength": "dim(shapes.input, 1)",
|
| 599 |
+
"log2Length": "rank3Axis1FftLog2",
|
| 600 |
+
"numBins": "dim(shapes.output, 1)",
|
| 601 |
+
"inComp": "dim(shapes.input, 2)",
|
| 602 |
+
"inverse": "attrs.inverse != 0"
|
| 603 |
+
}
|
| 604 |
+
},
|
| 605 |
+
"bindings": "contiguousTransform",
|
| 606 |
+
"dispatch": { "x": "dim(shapes.input, 0)" }
|
| 607 |
+
}
|
| 608 |
+
]
|
| 609 |
+
},
|
| 610 |
+
{
|
| 611 |
+
"id": "batch_axis1_tiled_real_nonpow2",
|
| 612 |
+
"priority": 29,
|
| 613 |
+
"requires": { "limits": { "maxComputeInvocationsPerWorkgroup": 256, "maxComputeWorkgroupSizeX": 256 } },
|
| 614 |
+
"when": ["not present.axis", "not present.dft_length", "ranks.input == 3", "ranks.output == 3", "dftAxisAttr == 1", "attrs.inverse == 0", "dim(shapes.input, 0) == dim(shapes.output, 0)", "dim(shapes.input, 0) > 0", "rank3BatchDispatchFits", "dim(shapes.input, 1) >= tunables.TILED_REAL_MIN_LENGTH", "pow2ceil(dim(shapes.input, 1)) != dim(shapes.input, 1)", "dim(shapes.input, 1) * 4 <= device.limits.maxComputeWorkgroupStorageSize", "dim(shapes.input, 2) == 1", "dim(shapes.output, 2) == 2", "((attrs.onesided == 0 and dim(shapes.output, 1) == dim(shapes.input, 1)) or (attrs.onesided != 0 and dim(shapes.output, 1) == floor(dim(shapes.input, 1) / 2) + 1))", "ceilDiv(dim(shapes.output, 1), tunables.TILED_REAL_WORKGROUP_SIZE) <= device.limits.maxComputeWorkgroupsPerDimension"],
|
| 615 |
+
"passes": [
|
| 616 |
+
{
|
| 617 |
+
"id": "main",
|
| 618 |
+
"name": "DFT.BatchAxis1TiledRealNonPowerOfTwo",
|
| 619 |
+
"source": {
|
| 620 |
+
"shader": "dft-tiled-real.wgsl.jinja",
|
| 621 |
+
"inputs": {
|
| 622 |
+
"mode": "\"dft\"",
|
| 623 |
+
"transformLength": "dim(shapes.input, 1)",
|
| 624 |
+
"numBins": "dim(shapes.output, 1)",
|
| 625 |
+
"workgroupSize": "tunables.TILED_REAL_WORKGROUP_SIZE"
|
| 626 |
+
}
|
| 627 |
+
},
|
| 628 |
+
"bindings": "contiguousTransform",
|
| 629 |
+
"dispatch": {
|
| 630 |
+
"x": "ceilDiv(dim(shapes.output, 1), tunables.TILED_REAL_WORKGROUP_SIZE)",
|
| 631 |
+
"y": "dim(shapes.input, 0)"
|
| 632 |
+
}
|
| 633 |
+
}
|
| 634 |
+
]
|
| 635 |
+
},
|
| 636 |
+
{
|
| 637 |
+
"id": "axis_fft_shared",
|
| 638 |
+
"priority": 30,
|
| 639 |
+
"when": ["not present.axis", "not present.dft_length", "ranks.input == ranks.output", "((ranks.input == 2 and dftAxisAttr == 0) or (ranks.input == 3 and dftAxisAttr == 1 and dim(shapes.input, 0) == dim(shapes.output, 0)) or (ranks.input == 4 and dftAxisAttr == 2 and dim(shapes.input, 0) == dim(shapes.output, 0) and dim(shapes.input, 1) == dim(shapes.output, 1)))", "pow2ceil(dim(shapes.input, dftAxisAttr)) == dim(shapes.input, dftAxisAttr)", "dim(shapes.input, dftAxisAttr) >= 4", "dim(shapes.input, dftAxisAttr) * 16 <= device.limits.maxComputeWorkgroupStorageSize", "outer(shapes.input, dftAxisAttr) * inner(shapes.input, dftAxisAttr) / dim(shapes.input, ranks.input - 1) <= device.limits.maxComputeWorkgroupsPerDimension", "dim(shapes.input, ranks.input - 1) >= 1", "dim(shapes.input, ranks.input - 1) <= 2", "((attrs.onesided == 0 and dim(shapes.output, dftAxisAttr) == dim(shapes.input, dftAxisAttr) and dim(shapes.output, ranks.input - 1) == 2) or (attrs.onesided != 0 and attrs.inverse == 0 and dim(shapes.input, ranks.input - 1) == 1 and dim(shapes.output, dftAxisAttr) == floor(dim(shapes.input, dftAxisAttr) / 2) + 1 and dim(shapes.output, ranks.input - 1) == 2))"],
|
| 640 |
+
"passes": [
|
| 641 |
+
{
|
| 642 |
+
"id": "main",
|
| 643 |
+
"name": "DFT.AxisFftShared",
|
| 644 |
+
"source": {
|
| 645 |
+
"shader": "fft-stockham-shared.wgsl.jinja",
|
| 646 |
+
"inputs": {
|
| 647 |
+
"mode": "\"dft\"",
|
| 648 |
+
"fftLength": "dim(shapes.input, dftAxisAttr)",
|
| 649 |
+
"numBins": "dim(shapes.output, dftAxisAttr)",
|
| 650 |
+
"inComp": "dim(shapes.input, ranks.input - 1)",
|
| 651 |
+
"inverse": "attrs.inverse != 0"
|
| 652 |
+
}
|
| 653 |
+
},
|
| 654 |
+
"bindings": "sharedFftTransform",
|
| 655 |
+
"dispatch": {
|
| 656 |
+
"x": "outer(shapes.input, dftAxisAttr) * inner(shapes.input, dftAxisAttr) / dim(shapes.input, ranks.input - 1)"
|
| 657 |
+
}
|
| 658 |
+
}
|
| 659 |
+
]
|
| 660 |
+
},
|
| 661 |
+
{
|
| 662 |
+
"id": "batch_axis1_fft_explicit_equal_length",
|
| 663 |
+
"priority": 31,
|
| 664 |
+
"when": ["not present.axis", "present.dft_length", "numel(shapes.dft_length) == 1", "ranks.input == 3", "ranks.output == 3", "dftAxisAttr == 1", "dim(shapes.input, 0) == dim(shapes.output, 0)", "rank3Axis1SharedFftFits", "dim(shapes.input, 2) >= 1", "dim(shapes.input, 2) <= 2", "((attrs.onesided == 0 and dim(shapes.output, 1) == dim(shapes.input, 1) and dim(shapes.output, 2) == 2) or (attrs.onesided != 0 and attrs.inverse == 0 and dim(shapes.input, 2) == 1 and dim(shapes.output, 1) == floor(dim(shapes.input, 1) / 2) + 1 and dim(shapes.output, 2) == 2))"],
|
| 665 |
+
"passes": [
|
| 666 |
+
{
|
| 667 |
+
"id": "main",
|
| 668 |
+
"name": "DFT.BatchAxis1FftSharedExplicitLength",
|
| 669 |
+
"source": {
|
| 670 |
+
"shader": "fft-stockham-shared.wgsl.jinja",
|
| 671 |
+
"inputs": {
|
| 672 |
+
"mode": "\"dft\"",
|
| 673 |
+
"fftLength": "dim(shapes.input, 1)",
|
| 674 |
+
"numBins": "dim(shapes.output, 1)",
|
| 675 |
+
"inComp": "dim(shapes.input, 2)",
|
| 676 |
+
"inverse": "attrs.inverse != 0"
|
| 677 |
+
}
|
| 678 |
+
},
|
| 679 |
+
"bindings": "fftLenStatic",
|
| 680 |
+
"dispatch": { "x": "dim(shapes.input, 0)" }
|
| 681 |
+
}
|
| 682 |
+
]
|
| 683 |
+
},
|
| 684 |
+
{
|
| 685 |
+
"id": "rank2_axis0",
|
| 686 |
+
"priority": 20,
|
| 687 |
+
"when": ["not present.axis", "not present.dft_length", "ranks.input == 2", "ranks.output == 2", "dftAxisAttr == 0", "dim(shapes.input, 1) >= 1", "dim(shapes.input, 1) <= 2", "((attrs.onesided == 0 and dim(shapes.output, 0) == dim(shapes.input, 0) and dim(shapes.output, 1) == 2) or (attrs.onesided != 0 and attrs.inverse == 0 and dim(shapes.input, 1) == 1 and dim(shapes.output, 0) == floor(dim(shapes.input, 0) / 2) + 1 and dim(shapes.output, 1) == 2) or (attrs.onesided != 0 and attrs.inverse != 0 and dim(shapes.input, 1) == 2 and dim(shapes.input, 0) == floor(dim(shapes.output, 0) / 2) + 1 and dim(shapes.output, 1) == 1))"],
|
| 688 |
+
"passes": [
|
| 689 |
+
{
|
| 690 |
+
"id": "main",
|
| 691 |
+
"name": "DFT.Rank2Axis0",
|
| 692 |
+
"source": { "shader": "dft-contiguous-naive.wgsl.jinja", "inputs": { "batched": false } },
|
| 693 |
+
"bindings": "rank2Naive",
|
| 694 |
+
"dispatch": {
|
| 695 |
+
"threads": "numel(shapes.output) / dim(shapes.output, 1)",
|
| 696 |
+
"workgroupSize": "tunables.WORKGROUP_SIZE"
|
| 697 |
+
}
|
| 698 |
+
}
|
| 699 |
+
]
|
| 700 |
+
},
|
| 701 |
+
{
|
| 702 |
+
"id": "batch_axis1",
|
| 703 |
+
"priority": 10,
|
| 704 |
+
"when": ["not present.axis", "not present.dft_length", "ranks.input == 3", "ranks.output == 3", "dftAxisAttr == 1", "dim(shapes.input, 0) == dim(shapes.output, 0)", "dim(shapes.input, 2) >= 1", "dim(shapes.input, 2) <= 2", "((attrs.onesided == 0 and dim(shapes.output, 1) == dim(shapes.input, 1) and dim(shapes.output, 2) == 2) or (attrs.onesided != 0 and attrs.inverse == 0 and dim(shapes.input, 2) == 1 and dim(shapes.output, 1) == floor(dim(shapes.input, 1) / 2) + 1 and dim(shapes.output, 2) == 2) or (attrs.onesided != 0 and attrs.inverse != 0 and dim(shapes.input, 2) == 2 and dim(shapes.input, 1) == floor(dim(shapes.output, 1) / 2) + 1 and dim(shapes.output, 2) == 1))"],
|
| 705 |
+
"passes": [
|
| 706 |
+
{
|
| 707 |
+
"id": "main",
|
| 708 |
+
"name": "DFT",
|
| 709 |
+
"source": { "shader": "dft-contiguous-naive.wgsl.jinja", "inputs": { "batched": true } },
|
| 710 |
+
"bindings": "rank3Naive",
|
| 711 |
+
"dispatch": {
|
| 712 |
+
"threads": "numel(shapes.output) / dim(shapes.output, 2)",
|
| 713 |
+
"workgroupSize": "tunables.WORKGROUP_SIZE"
|
| 714 |
+
}
|
| 715 |
+
}
|
| 716 |
+
]
|
| 717 |
+
},
|
| 718 |
+
{
|
| 719 |
+
"id": "rank4_axis2",
|
| 720 |
+
"priority": 10,
|
| 721 |
+
"when": ["not present.axis", "not present.dft_length", "ranks.input == 4", "ranks.output == 4", "dftAxisAttr == 2", "dim(shapes.input, 0) == dim(shapes.output, 0)", "dim(shapes.input, 1) == dim(shapes.output, 1)", "dim(shapes.input, 3) >= 1", "dim(shapes.input, 3) <= 2", "((attrs.onesided == 0 and dim(shapes.output, 2) == dim(shapes.input, 2) and dim(shapes.output, 3) == 2) or (attrs.onesided != 0 and attrs.inverse == 0 and dim(shapes.input, 3) == 1 and dim(shapes.output, 2) == floor(dim(shapes.input, 2) / 2) + 1 and dim(shapes.output, 3) == 2) or (attrs.onesided != 0 and attrs.inverse != 0 and dim(shapes.input, 3) == 2 and dim(shapes.input, 2) == floor(dim(shapes.output, 2) / 2) + 1 and dim(shapes.output, 3) == 1))"],
|
| 722 |
+
"passes": [
|
| 723 |
+
{
|
| 724 |
+
"id": "main",
|
| 725 |
+
"name": "DFT.Rank4Axis2",
|
| 726 |
+
"shader": "dft-rank4.wgsl.jinja",
|
| 727 |
+
"bindings": "rank4Naive",
|
| 728 |
+
"dispatch": {
|
| 729 |
+
"threads": "numel(shapes.output) / dim(shapes.output, 3)",
|
| 730 |
+
"workgroupSize": "tunables.WORKGROUP_SIZE"
|
| 731 |
+
}
|
| 732 |
+
}
|
| 733 |
+
]
|
| 734 |
+
},
|
| 735 |
+
{
|
| 736 |
+
"id": "general_attr",
|
| 737 |
+
"priority": 5,
|
| 738 |
+
"when": ["not present.axis", "not present.dft_length", "ranks.input >= 2", "dftAxisAttr <= ranks.input - 2", "ranks.input == ranks.output", "dim(shapes.input, ranks.input - 1) >= 1", "dim(shapes.input, ranks.input - 1) <= 2", "dim(shapes.output, ranks.output - 1) >= 1", "dim(shapes.output, ranks.output - 1) <= 2"],
|
| 739 |
+
"passes": [
|
| 740 |
+
{
|
| 741 |
+
"id": "main",
|
| 742 |
+
"name": "DFT.General",
|
| 743 |
+
"source": {
|
| 744 |
+
"shader": "dft-general.wgsl.jinja",
|
| 745 |
+
"inputs": { "hasAxisInput": false, "hasDftLengthInput": false, "skipRuntimeAxis1": false }
|
| 746 |
+
},
|
| 747 |
+
"bindings": "genNone",
|
| 748 |
+
"dispatch": {
|
| 749 |
+
"threads": "numel(shapes.output) / dim(shapes.output, ranks.output - 1)",
|
| 750 |
+
"workgroupSize": "tunables.WORKGROUP_SIZE"
|
| 751 |
+
}
|
| 752 |
+
}
|
| 753 |
+
]
|
| 754 |
+
},
|
| 755 |
+
{
|
| 756 |
+
"id": "general_axis_len",
|
| 757 |
+
"priority": 5,
|
| 758 |
+
"when": ["present.axis and numel(shapes.axis) == 1", "present.dft_length and numel(shapes.dft_length) == 1", "ranks.input >= 2", "ranks.input <= 8", "ranks.input == ranks.output", "dim(shapes.input, ranks.input - 1) >= 1", "dim(shapes.input, ranks.input - 1) <= 2", "dim(shapes.output, ranks.output - 1) >= 1", "dim(shapes.output, ranks.output - 1) <= 2"],
|
| 759 |
+
"passes": [
|
| 760 |
+
{
|
| 761 |
+
"id": "main",
|
| 762 |
+
"name": "DFT.General",
|
| 763 |
+
"source": {
|
| 764 |
+
"shader": "dft-general.wgsl.jinja",
|
| 765 |
+
"inputs": { "hasAxisInput": true, "hasDftLengthInput": true, "skipRuntimeAxis1": false }
|
| 766 |
+
},
|
| 767 |
+
"bindings": "genAxisLen",
|
| 768 |
+
"dispatch": {
|
| 769 |
+
"threads": "numel(shapes.output) / dim(shapes.output, ranks.output - 1)",
|
| 770 |
+
"workgroupSize": "tunables.WORKGROUP_SIZE"
|
| 771 |
+
}
|
| 772 |
+
}
|
| 773 |
+
]
|
| 774 |
+
},
|
| 775 |
+
{
|
| 776 |
+
"id": "general_axis",
|
| 777 |
+
"priority": 5,
|
| 778 |
+
"when": ["present.axis and numel(shapes.axis) == 1", "not present.dft_length", "ranks.input >= 2", "ranks.input <= 8", "ranks.input == ranks.output", "dim(shapes.input, ranks.input - 1) >= 1", "dim(shapes.input, ranks.input - 1) <= 2", "dim(shapes.output, ranks.output - 1) >= 1", "dim(shapes.output, ranks.output - 1) <= 2"],
|
| 779 |
+
"passes": [
|
| 780 |
+
{
|
| 781 |
+
"id": "main",
|
| 782 |
+
"name": "DFT.General",
|
| 783 |
+
"source": {
|
| 784 |
+
"shader": "dft-general.wgsl.jinja",
|
| 785 |
+
"inputs": { "hasAxisInput": true, "hasDftLengthInput": false, "skipRuntimeAxis1": false }
|
| 786 |
+
},
|
| 787 |
+
"bindings": "genAxis",
|
| 788 |
+
"dispatch": {
|
| 789 |
+
"threads": "numel(shapes.output) / dim(shapes.output, ranks.output - 1)",
|
| 790 |
+
"workgroupSize": "tunables.WORKGROUP_SIZE"
|
| 791 |
+
}
|
| 792 |
+
}
|
| 793 |
+
]
|
| 794 |
+
},
|
| 795 |
+
{
|
| 796 |
+
"id": "general_len",
|
| 797 |
+
"priority": 5,
|
| 798 |
+
"when": ["not present.axis", "present.dft_length and numel(shapes.dft_length) == 1", "ranks.input >= 2", "dftAxisAttr <= ranks.input - 2", "ranks.input == ranks.output", "dim(shapes.input, ranks.input - 1) >= 1", "dim(shapes.input, ranks.input - 1) <= 2", "dim(shapes.output, ranks.output - 1) >= 1", "dim(shapes.output, ranks.output - 1) <= 2"],
|
| 799 |
+
"passes": [
|
| 800 |
+
{
|
| 801 |
+
"id": "main",
|
| 802 |
+
"name": "DFT.General",
|
| 803 |
+
"source": {
|
| 804 |
+
"shader": "dft-general.wgsl.jinja",
|
| 805 |
+
"inputs": { "hasAxisInput": false, "hasDftLengthInput": true, "skipRuntimeAxis1": false }
|
| 806 |
+
},
|
| 807 |
+
"bindings": "genLen",
|
| 808 |
+
"dispatch": {
|
| 809 |
+
"threads": "numel(shapes.output) / dim(shapes.output, ranks.output - 1)",
|
| 810 |
+
"workgroupSize": "tunables.WORKGROUP_SIZE"
|
| 811 |
+
}
|
| 812 |
+
}
|
| 813 |
+
]
|
| 814 |
+
}
|
| 815 |
+
]
|
| 816 |
+
}
|
build/webgpu/metadata.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "ai.onnx.DFT",
|
| 3 |
+
"id": "_ai_onnx_dft_webgpu_dbca2e5",
|
| 4 |
+
"version": 1,
|
| 5 |
+
"license": "Apache-2.0",
|
| 6 |
+
"backend": { "type": "webgpu" },
|
| 7 |
+
"digest": {
|
| 8 |
+
"algorithm": "sha256",
|
| 9 |
+
"files": {
|
| 10 |
+
"bench.json": "jbPgs835f9m0aqqbSqY7zJJ+sRdHAdkjUzasXhjUEMM=",
|
| 11 |
+
"dft-contiguous-naive.wgsl.jinja": "HizdEBeuURKgl0Zxsz3xs62LJA6qmXvTYeYgC/XW5FE=",
|
| 12 |
+
"dft-general.wgsl.jinja": "fRvFH2YPz8DQLaTWSwXfScJErAFyeim5yqU39h+DYIc=",
|
| 13 |
+
"dft-rank4.wgsl.jinja": "QYtIZb6gpPc4fPIglkSt50Nr3KgbmuqoZk3EsuCgS3w=",
|
| 14 |
+
"dft-tiled-real.wgsl.jinja": "hbc3jiXVFIDZzBLo4O5hKQDqPIrNFdNktE85DhjWAzY=",
|
| 15 |
+
"fft-radix2-dit-storage.wgsl.jinja": "xLm5AhxAvxBdznovw1gQsQsaJGxusSmdtcaxUs00i2w=",
|
| 16 |
+
"fft-stockham-shared.wgsl.jinja": "7J4RLO2Q8puehryHRRHVhrrNxjNik5718G/qu6zoF6I=",
|
| 17 |
+
"manifest.json": "9IvgHvMofuhCPOTfi61UqnxP5X0IdE11o+JDI1V+dSs=",
|
| 18 |
+
"test.json": "pmZjTxGLBuze2IB7hjXRl0ks8BfzGeJ1OWCsaNDm/ao="
|
| 19 |
+
}
|
| 20 |
+
},
|
| 21 |
+
"provenance": { "kernel": { "sha": "2e7068faf55e7f43df740015f6d1ee49391a41c5", "dirty": false } },
|
| 22 |
+
"webgpu": { "manifestSpec": "1.0", "specialized": true, "opPath": "ops/ai.onnx.DFT" }
|
| 23 |
+
}
|
build/webgpu/test.json
ADDED
|
@@ -0,0 +1,1374 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"op": "ai.onnx.DFT",
|
| 3 |
+
"fixtureArrays": {
|
| 4 |
+
"ort_rank2_complex_axis0_output_output": [36, 0, -4, 9.65685, -4, 4, -4, 1.65685, -4, 0, -4, -1.65685, -4, -4, -4, -9.65685]
|
| 5 |
+
},
|
| 6 |
+
"cases": [
|
| 7 |
+
{
|
| 8 |
+
"name": "dispatch_cliff_batch_axis1_over_8m_elements",
|
| 9 |
+
"attrs": { "inverse": 0, "onesided": 0 },
|
| 10 |
+
"inputs": { "input": { "dtype": "float32", "shape": [2099200, 4, 1] } },
|
| 11 |
+
"outputs": { "output": { "dtype": "float32", "shape": [2099200, 4, 2], "tolerance": 0.001 } }
|
| 12 |
+
},
|
| 13 |
+
{
|
| 14 |
+
"name": "dispatch_cliff_rank4_axis1_over_8m_elements",
|
| 15 |
+
"attrs": { "inverse": 0, "onesided": 0 },
|
| 16 |
+
"inputs": {
|
| 17 |
+
"input": { "dtype": "float32", "shape": [1050000, 4, 2, 1] },
|
| 18 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [1] } }
|
| 19 |
+
},
|
| 20 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1050000, 4, 2, 2], "tolerance": 0.001 } }
|
| 21 |
+
},
|
| 22 |
+
{
|
| 23 |
+
"name": "dispatch_cliff_rank4_axis2_over_8m_elements",
|
| 24 |
+
"attrs": { "inverse": 0, "onesided": 0 },
|
| 25 |
+
"inputs": { "input": { "dtype": "float32", "shape": [1050000, 2, 4, 1] } },
|
| 26 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1050000, 2, 4, 2], "tolerance": 0.001 } }
|
| 27 |
+
},
|
| 28 |
+
{
|
| 29 |
+
"name": "real_full_default_axis",
|
| 30 |
+
"inputs": {
|
| 31 |
+
"input": {
|
| 32 |
+
"dtype": "float32",
|
| 33 |
+
"shape": [1, 4, 1],
|
| 34 |
+
"data": { "kind": "values", "values": [1.0, 2.0, 0.0, -1.0] }
|
| 35 |
+
}
|
| 36 |
+
},
|
| 37 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 4, 2], "tolerance": 0.00001 } }
|
| 38 |
+
},
|
| 39 |
+
{
|
| 40 |
+
"name": "default_axis_minus2_rank2",
|
| 41 |
+
"provenance": {
|
| 42 |
+
"source": "ONNX DFT-20 schema",
|
| 43 |
+
"notes": "Omitting the optional axis input must use -2. For rank 2 that normalizes to axis 0, the only signal axis."
|
| 44 |
+
},
|
| 45 |
+
"inputs": {
|
| 46 |
+
"input": { "dtype": "float32", "shape": [4, 1], "data": { "kind": "values", "values": [1.0, 0.0, 0.0, 0.0] } }
|
| 47 |
+
},
|
| 48 |
+
"outputs": {
|
| 49 |
+
"output": {
|
| 50 |
+
"dtype": "float32",
|
| 51 |
+
"shape": [4, 2],
|
| 52 |
+
"tolerance": 0.00001,
|
| 53 |
+
"data": { "kind": "values", "values": [1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0] }
|
| 54 |
+
}
|
| 55 |
+
}
|
| 56 |
+
},
|
| 57 |
+
{
|
| 58 |
+
"name": "default_axis_minus2_rank4",
|
| 59 |
+
"provenance": {
|
| 60 |
+
"source": "ONNX DFT-20 schema",
|
| 61 |
+
"notes": "Omitting the optional axis input must use -2. For rank 4 that is axis 2, independently transforming each [batch, inner] signal."
|
| 62 |
+
},
|
| 63 |
+
"inputs": {
|
| 64 |
+
"input": {
|
| 65 |
+
"dtype": "float32",
|
| 66 |
+
"shape": [1, 2, 4, 1],
|
| 67 |
+
"data": { "kind": "values", "values": [1.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0] }
|
| 68 |
+
}
|
| 69 |
+
},
|
| 70 |
+
"outputs": {
|
| 71 |
+
"output": {
|
| 72 |
+
"dtype": "float32",
|
| 73 |
+
"shape": [1, 2, 4, 2],
|
| 74 |
+
"tolerance": 0.00001,
|
| 75 |
+
"data": {
|
| 76 |
+
"kind": "values",
|
| 77 |
+
"values": [1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 2.0, 0.0, 2.0, 0.0, 2.0, 0.0, 2.0, 0.0]
|
| 78 |
+
}
|
| 79 |
+
}
|
| 80 |
+
}
|
| 81 |
+
},
|
| 82 |
+
{
|
| 83 |
+
"name": "real_subnormal_impulse_preserves_all_bins_gpu_gap",
|
| 84 |
+
"skipGpu": {
|
| 85 |
+
"category": "permanent",
|
| 86 |
+
"reason": "Portable WGSL floating-point semantics do not guarantee preservation of the subnormal values required by this fixture. Backend evidence: Metal FTZ: subnormal 1e-40 input flushes to zero on GPU, every output bin reads 0 instead of the subnormal value (tolerance 0)."
|
| 87 |
+
},
|
| 88 |
+
"provenance": {
|
| 89 |
+
"source": "onnxruntime/test/providers/cpu/signal/signal_ops_test.cc",
|
| 90 |
+
"test": "SignalOpsTest.DFT17_Float_radix2",
|
| 91 |
+
"notes": "Focused amplitude edge: a real impulse of 1e-40 has a DFT with every real frequency bin equal to that subnormal value."
|
| 92 |
+
},
|
| 93 |
+
"attrs": { "inverse": 0, "onesided": 0 },
|
| 94 |
+
"inputs": {
|
| 95 |
+
"input": {
|
| 96 |
+
"dtype": "float32",
|
| 97 |
+
"shape": [1, 4, 1],
|
| 98 |
+
"data": { "kind": "values", "values": [1e-40, 0.0, 0.0, 0.0] }
|
| 99 |
+
}
|
| 100 |
+
},
|
| 101 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 4, 2], "tolerance": 0 } }
|
| 102 |
+
},
|
| 103 |
+
{
|
| 104 |
+
"name": "complex_subnormal_impulse_preserves_all_bins_gpu_gap",
|
| 105 |
+
"skipGpu": {
|
| 106 |
+
"category": "permanent",
|
| 107 |
+
"reason": "Portable WGSL floating-point semantics do not guarantee preservation of the subnormal values required by this fixture. Backend evidence: Metal FTZ: subnormal 1e-40 input flushes to zero on GPU, every output bin reads 0 instead of the subnormal value (tolerance 0)."
|
| 108 |
+
},
|
| 109 |
+
"provenance": {
|
| 110 |
+
"source": "onnxruntime/test/providers/cpu/signal/signal_ops_test.cc",
|
| 111 |
+
"test": "SignalOpsTest.DFT17_Float_radix2",
|
| 112 |
+
"notes": "Complex companion to the real impulse edge: a subnormal complex impulse at sample zero should appear unchanged in every frequency bin."
|
| 113 |
+
},
|
| 114 |
+
"attrs": { "inverse": 0, "onesided": 0 },
|
| 115 |
+
"inputs": {
|
| 116 |
+
"input": {
|
| 117 |
+
"dtype": "float32",
|
| 118 |
+
"shape": [1, 4, 2],
|
| 119 |
+
"data": { "kind": "values", "values": [1e-40, -1e-40, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] }
|
| 120 |
+
}
|
| 121 |
+
},
|
| 122 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 4, 2], "tolerance": 0 } }
|
| 123 |
+
},
|
| 124 |
+
{
|
| 125 |
+
"name": "complex_inverse_subnormal_dc_preserves_time_signal_gpu_gap",
|
| 126 |
+
"skipGpu": {
|
| 127 |
+
"category": "permanent",
|
| 128 |
+
"reason": "Portable WGSL floating-point semantics do not guarantee preservation of the subnormal values required by this fixture. Backend evidence: Metal FTZ: subnormal 1e-40 input flushes to zero on GPU, every output bin reads 0 instead of the subnormal value (tolerance 0)."
|
| 129 |
+
},
|
| 130 |
+
"provenance": {
|
| 131 |
+
"source": "onnxruntime/test/providers/cpu/signal/signal_ops_test.cc",
|
| 132 |
+
"test": "SignalOpsTest.DFT17_Float_inverse",
|
| 133 |
+
"notes": "Inverse companion: a subnormal DC bin scaled by 1/N should produce a valid finite subnormal constant signal."
|
| 134 |
+
},
|
| 135 |
+
"attrs": { "inverse": 1, "onesided": 0 },
|
| 136 |
+
"inputs": {
|
| 137 |
+
"input": {
|
| 138 |
+
"dtype": "float32",
|
| 139 |
+
"shape": [1, 4, 2],
|
| 140 |
+
"data": { "kind": "values", "values": [4e-40, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] }
|
| 141 |
+
}
|
| 142 |
+
},
|
| 143 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 4, 2], "tolerance": 0 } }
|
| 144 |
+
},
|
| 145 |
+
{
|
| 146 |
+
"name": "rank2_axis0_subnormal_impulse_preserves_all_bins_gpu_gap",
|
| 147 |
+
"skipGpu": {
|
| 148 |
+
"category": "permanent",
|
| 149 |
+
"reason": "Portable WGSL floating-point semantics do not guarantee preservation of the subnormal values required by this fixture. Backend evidence: Metal FTZ: subnormal 1e-40 input flushes to zero on GPU, every output bin reads 0 instead of the subnormal value (tolerance 0)."
|
| 150 |
+
},
|
| 151 |
+
"provenance": {
|
| 152 |
+
"source": "onnxruntime/test/providers/cpu/signal/signal_ops_test.cc",
|
| 153 |
+
"test": "SignalOpsTest.DFT17_Float_radix2",
|
| 154 |
+
"notes": "Rank-2 axis-0 FFT companion: a real subnormal impulse should preserve the same subnormal real value in every bin."
|
| 155 |
+
},
|
| 156 |
+
"attrs": { "inverse": 0, "onesided": 0 },
|
| 157 |
+
"inputs": {
|
| 158 |
+
"input": { "dtype": "float32", "shape": [4, 1], "data": { "kind": "values", "values": [1e-40, 0.0, 0.0, 0.0] } }
|
| 159 |
+
},
|
| 160 |
+
"outputs": { "output": { "dtype": "float32", "shape": [4, 2], "tolerance": 0 } }
|
| 161 |
+
},
|
| 162 |
+
{
|
| 163 |
+
"name": "rank4_axis1_subnormal_impulse_preserves_all_bins_gpu_gap",
|
| 164 |
+
"skipGpu": {
|
| 165 |
+
"category": "permanent",
|
| 166 |
+
"reason": "Portable WGSL floating-point semantics do not guarantee preservation of the subnormal values required by this fixture. Backend evidence: Metal FTZ: subnormal 1e-40 input flushes to zero on GPU, every output bin reads 0 instead of the subnormal value (tolerance 0)."
|
| 167 |
+
},
|
| 168 |
+
"provenance": {
|
| 169 |
+
"source": "onnxruntime/test/providers/cpu/signal/signal_ops_test.cc",
|
| 170 |
+
"test": "SignalOpsTest.DFT17_Float_radix2",
|
| 171 |
+
"notes": "Rank-4 axis-1 FFT companion: the subnormal impulse is along the first signal axis with one inner position."
|
| 172 |
+
},
|
| 173 |
+
"attrs": { "inverse": 0, "onesided": 0 },
|
| 174 |
+
"inputs": {
|
| 175 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [1] } },
|
| 176 |
+
"input": {
|
| 177 |
+
"dtype": "float32",
|
| 178 |
+
"shape": [1, 4, 1, 1],
|
| 179 |
+
"data": { "kind": "values", "values": [1e-40, 0.0, 0.0, 0.0] }
|
| 180 |
+
}
|
| 181 |
+
},
|
| 182 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 4, 1, 2], "tolerance": 0 } }
|
| 183 |
+
},
|
| 184 |
+
{
|
| 185 |
+
"name": "rank4_axis2_subnormal_impulse_preserves_all_bins_gpu_gap",
|
| 186 |
+
"skipGpu": {
|
| 187 |
+
"category": "permanent",
|
| 188 |
+
"reason": "Portable WGSL floating-point semantics do not guarantee preservation of the subnormal values required by this fixture. Backend evidence: Metal FTZ: subnormal 1e-40 input flushes to zero on GPU, every output bin reads 0 instead of the subnormal value (tolerance 0)."
|
| 189 |
+
},
|
| 190 |
+
"provenance": {
|
| 191 |
+
"source": "onnxruntime/test/providers/cpu/signal/signal_ops_test.cc",
|
| 192 |
+
"test": "SignalOpsTest.DFT17_Float_radix2",
|
| 193 |
+
"notes": "Rank-4 axis-2 FFT companion: the subnormal impulse is along the second signal axis with one outer channel."
|
| 194 |
+
},
|
| 195 |
+
"attrs": { "inverse": 0, "onesided": 0 },
|
| 196 |
+
"inputs": {
|
| 197 |
+
"input": {
|
| 198 |
+
"dtype": "float32",
|
| 199 |
+
"shape": [1, 1, 4, 1],
|
| 200 |
+
"data": { "kind": "values", "values": [1e-40, 0.0, 0.0, 0.0] }
|
| 201 |
+
}
|
| 202 |
+
},
|
| 203 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 1, 4, 2], "tolerance": 0 } }
|
| 204 |
+
},
|
| 205 |
+
{
|
| 206 |
+
"name": "negative_axis_minus2_rank3",
|
| 207 |
+
"provenance": {
|
| 208 |
+
"source": "https://github.com/onnx/onnx/blob/main/docs/Operators.md#DFT",
|
| 209 |
+
"notes": "DFT axis accepts negative values in [-r, -2]; for rank 3, axis=-2 is the last signal axis and matches axis=1."
|
| 210 |
+
},
|
| 211 |
+
"attrs": {},
|
| 212 |
+
"inputs": {
|
| 213 |
+
"input": {
|
| 214 |
+
"dtype": "float32",
|
| 215 |
+
"shape": [1, 4, 1],
|
| 216 |
+
"data": { "kind": "values", "values": [1.0, 2.0, 0.0, -1.0] }
|
| 217 |
+
}
|
| 218 |
+
},
|
| 219 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 4, 2], "tolerance": 0.00001 } }
|
| 220 |
+
},
|
| 221 |
+
{
|
| 222 |
+
"name": "ort_axis_input_overrides_default_axis0",
|
| 223 |
+
"provenance": {
|
| 224 |
+
"source": "onnxruntime/test/providers/cpu/signal/signal_ops_test.cc",
|
| 225 |
+
"test": "SignalOpsTest.DFT20_invertible_real",
|
| 226 |
+
"notes": "Opset-20 axis is an input tensor. This same-shape axis=0 case is a regression lock ensuring the runtime axis overrides the default axis attribute."
|
| 227 |
+
},
|
| 228 |
+
"inputs": {
|
| 229 |
+
"input": {
|
| 230 |
+
"dtype": "float32",
|
| 231 |
+
"shape": [4, 3, 1],
|
| 232 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.09, "cosStep": 0.21, "scale": 4.0 }
|
| 233 |
+
},
|
| 234 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [0] } }
|
| 235 |
+
},
|
| 236 |
+
"outputs": { "output": { "dtype": "float32", "shape": [4, 3, 2], "tolerance": 0.0001 } }
|
| 237 |
+
},
|
| 238 |
+
{
|
| 239 |
+
"name": "ort_axis_input_overrides_default_rank4_axis2",
|
| 240 |
+
"provenance": {
|
| 241 |
+
"source": "onnxruntime/test/providers/cpu/signal/signal_ops_test.cc",
|
| 242 |
+
"test": "SignalOpsTest.DFT20_invertible_real",
|
| 243 |
+
"notes": "Rank-4 projection of the opset-20 axis-input path. The output shape also matches the default axis, so this locks correct runtime-axis decoding rather than allowing a silent axis-1 transform."
|
| 244 |
+
},
|
| 245 |
+
"inputs": {
|
| 246 |
+
"input": {
|
| 247 |
+
"dtype": "float32",
|
| 248 |
+
"shape": [1, 4, 4, 1],
|
| 249 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.17, "scale": 3.0 }
|
| 250 |
+
},
|
| 251 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [2] } }
|
| 252 |
+
},
|
| 253 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 4, 4, 2], "tolerance": 0.0001 } }
|
| 254 |
+
},
|
| 255 |
+
{
|
| 256 |
+
"name": "real_onesided_even",
|
| 257 |
+
"attrs": { "onesided": 1 },
|
| 258 |
+
"inputs": {
|
| 259 |
+
"input": {
|
| 260 |
+
"dtype": "float32",
|
| 261 |
+
"shape": [1, 4, 1],
|
| 262 |
+
"data": { "kind": "values", "values": [1.0, 2.0, 0.0, -1.0] }
|
| 263 |
+
}
|
| 264 |
+
},
|
| 265 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 3, 2], "tolerance": 0.00001 } }
|
| 266 |
+
},
|
| 267 |
+
{
|
| 268 |
+
"name": "complex_inverse",
|
| 269 |
+
"attrs": { "inverse": 1 },
|
| 270 |
+
"inputs": {
|
| 271 |
+
"input": {
|
| 272 |
+
"dtype": "float32",
|
| 273 |
+
"shape": [1, 4, 2],
|
| 274 |
+
"data": { "kind": "values", "values": [2.0, 0.0, 1.0, -1.0, 0.0, 0.0, 1.0, 1.0] }
|
| 275 |
+
}
|
| 276 |
+
},
|
| 277 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 4, 2], "tolerance": 0.00001 } }
|
| 278 |
+
},
|
| 279 |
+
{
|
| 280 |
+
"name": "real_onesided_odd_length",
|
| 281 |
+
"attrs": { "onesided": 1 },
|
| 282 |
+
"inputs": {
|
| 283 |
+
"input": {
|
| 284 |
+
"dtype": "float32",
|
| 285 |
+
"shape": [1, 5, 1],
|
| 286 |
+
"data": { "kind": "values", "values": [0.0, 1.0, 2.0, 3.0, 4.0] }
|
| 287 |
+
}
|
| 288 |
+
},
|
| 289 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 3, 2], "tolerance": 0.00001 } }
|
| 290 |
+
},
|
| 291 |
+
{
|
| 292 |
+
"name": "real_full_batch2",
|
| 293 |
+
"inputs": {
|
| 294 |
+
"input": {
|
| 295 |
+
"dtype": "float32",
|
| 296 |
+
"shape": [2, 4, 1],
|
| 297 |
+
"data": { "kind": "values", "values": [1.0, 0.0, -1.0, 0.0, 0.0, 2.0, 0.0, -2.0] }
|
| 298 |
+
}
|
| 299 |
+
},
|
| 300 |
+
"outputs": { "output": { "dtype": "float32", "shape": [2, 4, 2], "tolerance": 0.00001 } }
|
| 301 |
+
},
|
| 302 |
+
{
|
| 303 |
+
"name": "complex_inverse_length3",
|
| 304 |
+
"attrs": { "inverse": 1 },
|
| 305 |
+
"inputs": {
|
| 306 |
+
"input": {
|
| 307 |
+
"dtype": "float32",
|
| 308 |
+
"shape": [1, 3, 2],
|
| 309 |
+
"data": { "kind": "values", "values": [6.0, 0.0, -3.0, 1.7320508, -3.0, -1.7320508] }
|
| 310 |
+
}
|
| 311 |
+
},
|
| 312 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 3, 2], "tolerance": 0.00001 } }
|
| 313 |
+
},
|
| 314 |
+
{
|
| 315 |
+
"name": "real_radix2_onesided_length8_ort",
|
| 316 |
+
"provenance": {
|
| 317 |
+
"source": "onnxruntime/test/providers/cpu/signal/signal_ops_test.cc",
|
| 318 |
+
"test": "SignalOpsTest.DFT17_Float_radix2_onesided"
|
| 319 |
+
},
|
| 320 |
+
"attrs": { "onesided": 1 },
|
| 321 |
+
"inputs": {
|
| 322 |
+
"input": {
|
| 323 |
+
"dtype": "float32",
|
| 324 |
+
"shape": [1, 8, 1],
|
| 325 |
+
"data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0] }
|
| 326 |
+
}
|
| 327 |
+
},
|
| 328 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 5, 2], "tolerance": 0.00002 } }
|
| 329 |
+
},
|
| 330 |
+
{
|
| 331 |
+
"name": "fft_batch_axis1_len256_batch4_real_onesided_perf_compact",
|
| 332 |
+
"provenance": {
|
| 333 |
+
"notes": "Compact correctness sibling for the batch-axis onesided FFT benchmark; preserves real input, axis=1, power-of-two length, and onesided output without benchmark-scale tensors."
|
| 334 |
+
},
|
| 335 |
+
"attrs": { "onesided": 1 },
|
| 336 |
+
"inputs": { "input": { "dtype": "float32", "shape": [4, 256, 1] } },
|
| 337 |
+
"outputs": { "output": { "dtype": "float32", "shape": [4, 129, 2], "tolerance": 0.0002 } }
|
| 338 |
+
},
|
| 339 |
+
{
|
| 340 |
+
"name": "complex_full_batch2_length3",
|
| 341 |
+
"inputs": {
|
| 342 |
+
"input": {
|
| 343 |
+
"dtype": "float32",
|
| 344 |
+
"shape": [2, 3, 2],
|
| 345 |
+
"data": { "kind": "values", "values": [1.0, 0.0, 0.0, 1.0, -1.0, 0.0, 2.0, 0.0, 0.0, -1.0, 1.0, 1.0] }
|
| 346 |
+
}
|
| 347 |
+
},
|
| 348 |
+
"outputs": { "output": { "dtype": "float32", "shape": [2, 3, 2], "tolerance": 0.00001 } }
|
| 349 |
+
},
|
| 350 |
+
{
|
| 351 |
+
"name": "real_naive_length5_full_ort",
|
| 352 |
+
"provenance": {
|
| 353 |
+
"source": "onnxruntime/test/providers/cpu/signal/signal_ops_test.cc",
|
| 354 |
+
"test": "SignalOpsTest.DFT17_Float_naive"
|
| 355 |
+
},
|
| 356 |
+
"inputs": {
|
| 357 |
+
"input": {
|
| 358 |
+
"dtype": "float32",
|
| 359 |
+
"shape": [1, 5, 1],
|
| 360 |
+
"data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, 5.0] }
|
| 361 |
+
}
|
| 362 |
+
},
|
| 363 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 5, 2], "tolerance": 0.00002 } }
|
| 364 |
+
},
|
| 365 |
+
{
|
| 366 |
+
"name": "real_radix2_length8_full_ort",
|
| 367 |
+
"provenance": {
|
| 368 |
+
"source": "onnxruntime/test/providers/cpu/signal/signal_ops_test.cc",
|
| 369 |
+
"test": "SignalOpsTest.DFT17_Float_radix2"
|
| 370 |
+
},
|
| 371 |
+
"inputs": {
|
| 372 |
+
"input": {
|
| 373 |
+
"dtype": "float32",
|
| 374 |
+
"shape": [1, 8, 1],
|
| 375 |
+
"data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0] }
|
| 376 |
+
}
|
| 377 |
+
},
|
| 378 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 8, 2], "tolerance": 0.00004 } }
|
| 379 |
+
},
|
| 380 |
+
{
|
| 381 |
+
"name": "real_naive_length5_onesided_ort",
|
| 382 |
+
"provenance": {
|
| 383 |
+
"source": "onnxruntime/test/providers/cpu/signal/signal_ops_test.cc",
|
| 384 |
+
"test": "SignalOpsTest.DFT17_Float_naive_onesided"
|
| 385 |
+
},
|
| 386 |
+
"attrs": { "onesided": 1 },
|
| 387 |
+
"inputs": {
|
| 388 |
+
"input": {
|
| 389 |
+
"dtype": "float32",
|
| 390 |
+
"shape": [1, 5, 1],
|
| 391 |
+
"data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, 5.0] }
|
| 392 |
+
}
|
| 393 |
+
},
|
| 394 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 3, 2], "tolerance": 0.00002 } }
|
| 395 |
+
},
|
| 396 |
+
{
|
| 397 |
+
"name": "ort_dft_length_truncates_forward",
|
| 398 |
+
"provenance": {
|
| 399 |
+
"source": "onnxruntime/test/providers/cpu/signal/signal_ops_test.cc",
|
| 400 |
+
"test": "SignalOpsTest.DFT20_Float_naive",
|
| 401 |
+
"notes": "Valid opset-20 dft_length input that truncates the transform length below the input extent."
|
| 402 |
+
},
|
| 403 |
+
"inputs": {
|
| 404 |
+
"input": {
|
| 405 |
+
"dtype": "float32",
|
| 406 |
+
"shape": [1, 5, 1],
|
| 407 |
+
"data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, 5.0] }
|
| 408 |
+
},
|
| 409 |
+
"dft_length": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [3] } },
|
| 410 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [1] } }
|
| 411 |
+
},
|
| 412 |
+
"outputs": {
|
| 413 |
+
"output": {
|
| 414 |
+
"dtype": "float32",
|
| 415 |
+
"shape": [1, 3, 2],
|
| 416 |
+
"tolerance": 0.00002,
|
| 417 |
+
"data": { "kind": "values", "values": [6.0, 0.0, -1.5, 0.866025, -1.5, -0.866025] }
|
| 418 |
+
}
|
| 419 |
+
}
|
| 420 |
+
},
|
| 421 |
+
{
|
| 422 |
+
"name": "ort_dft_length_zero_pads_forward",
|
| 423 |
+
"provenance": {
|
| 424 |
+
"source": "onnxruntime/test/providers/cpu/signal/signal_ops_test.cc",
|
| 425 |
+
"test": "SignalOpsTest.DFT20_Float_naive",
|
| 426 |
+
"notes": "Valid opset-20 dft_length input that zero-pads the transform length beyond the input extent."
|
| 427 |
+
},
|
| 428 |
+
"inputs": {
|
| 429 |
+
"input": { "dtype": "float32", "shape": [1, 3, 1], "data": { "kind": "values", "values": [1.0, 2.0, 3.0] } },
|
| 430 |
+
"dft_length": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [5] } },
|
| 431 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [1] } }
|
| 432 |
+
},
|
| 433 |
+
"outputs": {
|
| 434 |
+
"output": {
|
| 435 |
+
"dtype": "float32",
|
| 436 |
+
"shape": [1, 5, 2],
|
| 437 |
+
"tolerance": 0.00002,
|
| 438 |
+
"data": {
|
| 439 |
+
"kind": "values",
|
| 440 |
+
"values": [6.0, 0.0, -0.809017, -3.665469, 0.309017, 1.677599, 0.309017, -1.677599, -0.809017, 3.665469]
|
| 441 |
+
}
|
| 442 |
+
}
|
| 443 |
+
}
|
| 444 |
+
},
|
| 445 |
+
{
|
| 446 |
+
"name": "complex_inverse_length5_ort",
|
| 447 |
+
"provenance": {
|
| 448 |
+
"source": "onnxruntime/test/providers/cpu/signal/signal_ops_test.cc",
|
| 449 |
+
"test": "SignalOpsTest.DFT17_Float_inverse"
|
| 450 |
+
},
|
| 451 |
+
"attrs": { "inverse": 1 },
|
| 452 |
+
"inputs": {
|
| 453 |
+
"input": {
|
| 454 |
+
"dtype": "float32",
|
| 455 |
+
"shape": [1, 5, 2],
|
| 456 |
+
"data": {
|
| 457 |
+
"kind": "values",
|
| 458 |
+
"values": [15.0, 0.0, -2.499999, 3.440955, -2.5, 0.8123, -2.499999, -0.812299, -2.500003, -3.440953]
|
| 459 |
+
}
|
| 460 |
+
}
|
| 461 |
+
},
|
| 462 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 5, 2], "tolerance": 0.0001 } }
|
| 463 |
+
},
|
| 464 |
+
{
|
| 465 |
+
"name": "ort_irfft_radix2_even_length",
|
| 466 |
+
"provenance": {
|
| 467 |
+
"source": "onnxruntime/test/providers/cpu/signal/signal_ops_test.cc",
|
| 468 |
+
"test": "SignalOpsTest.DFT20_IRFFT_radix2"
|
| 469 |
+
},
|
| 470 |
+
"attrs": { "onesided": 1, "inverse": 1 },
|
| 471 |
+
"inputs": {
|
| 472 |
+
"input": {
|
| 473 |
+
"dtype": "float32",
|
| 474 |
+
"shape": [1, 5, 2],
|
| 475 |
+
"data": { "kind": "values", "values": [36.0, 0.0, -4.0, 9.65685, -4.0, 4.0, -4.0, 1.65685, -4.0, 0.0] }
|
| 476 |
+
}
|
| 477 |
+
},
|
| 478 |
+
"outputs": {
|
| 479 |
+
"output": {
|
| 480 |
+
"dtype": "float32",
|
| 481 |
+
"shape": [1, 8, 1],
|
| 482 |
+
"tolerance": 0.0001,
|
| 483 |
+
"data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0] }
|
| 484 |
+
}
|
| 485 |
+
}
|
| 486 |
+
},
|
| 487 |
+
{
|
| 488 |
+
"name": "ort_irfft_radix2_rank3_axis0",
|
| 489 |
+
"provenance": {
|
| 490 |
+
"source": "onnxruntime/test/providers/cpu/signal/signal_ops_test.cc",
|
| 491 |
+
"test": "SignalOpsTest.DFT20_IRFFT_radix2",
|
| 492 |
+
"notes": "Axis-0 rank-3 projection of ORT's even-length IRFFT case."
|
| 493 |
+
},
|
| 494 |
+
"attrs": { "onesided": 1, "inverse": 1 },
|
| 495 |
+
"inputs": {
|
| 496 |
+
"input": {
|
| 497 |
+
"dtype": "float32",
|
| 498 |
+
"shape": [5, 1, 2],
|
| 499 |
+
"data": { "kind": "values", "values": [36.0, 0.0, -4.0, 9.65685, -4.0, 4.0, -4.0, 1.65685, -4.0, 0.0] }
|
| 500 |
+
},
|
| 501 |
+
"dft_length": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [8] } },
|
| 502 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [0] } }
|
| 503 |
+
},
|
| 504 |
+
"outputs": {
|
| 505 |
+
"output": {
|
| 506 |
+
"dtype": "float32",
|
| 507 |
+
"shape": [8, 1, 1],
|
| 508 |
+
"tolerance": 0.0001,
|
| 509 |
+
"data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0] }
|
| 510 |
+
}
|
| 511 |
+
}
|
| 512 |
+
},
|
| 513 |
+
{
|
| 514 |
+
"name": "ort_irfft_naive_odd_length",
|
| 515 |
+
"provenance": {
|
| 516 |
+
"source": "onnxruntime/test/providers/cpu/signal/signal_ops_test.cc",
|
| 517 |
+
"test": "SignalOpsTest.DFT20_IRFFT_naive",
|
| 518 |
+
"notes": "Odd-length IRFFT needs explicit dft_length to disambiguate from the even default."
|
| 519 |
+
},
|
| 520 |
+
"attrs": { "onesided": 1, "inverse": 1 },
|
| 521 |
+
"inputs": {
|
| 522 |
+
"input": {
|
| 523 |
+
"dtype": "float32",
|
| 524 |
+
"shape": [1, 3, 2],
|
| 525 |
+
"data": { "kind": "values", "values": [15.0, 0.0, -2.499999, 3.440955, -2.5, 0.8123] }
|
| 526 |
+
},
|
| 527 |
+
"dft_length": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [5] } },
|
| 528 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [1] } }
|
| 529 |
+
},
|
| 530 |
+
"outputs": {
|
| 531 |
+
"output": {
|
| 532 |
+
"dtype": "float32",
|
| 533 |
+
"shape": [1, 5, 1],
|
| 534 |
+
"tolerance": 0.0001,
|
| 535 |
+
"data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, 5.0] }
|
| 536 |
+
}
|
| 537 |
+
}
|
| 538 |
+
},
|
| 539 |
+
{
|
| 540 |
+
"name": "ort_invertible_real_rank4_axis1_projection",
|
| 541 |
+
"provenance": {
|
| 542 |
+
"source": "onnxruntime/test/providers/cpu/signal/signal_ops_test.cc",
|
| 543 |
+
"test": "SignalOpsTest.DFT20_invertible_real",
|
| 544 |
+
"notes": "Single forward DFT projection from ORT's rank-4 real invertibility sweep."
|
| 545 |
+
},
|
| 546 |
+
"attrs": {},
|
| 547 |
+
"inputs": {
|
| 548 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [1] } },
|
| 549 |
+
"input": {
|
| 550 |
+
"dtype": "float32",
|
| 551 |
+
"shape": [2, 3, 2, 1],
|
| 552 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.19, "scale": 8.0 }
|
| 553 |
+
}
|
| 554 |
+
},
|
| 555 |
+
"outputs": { "output": { "dtype": "float32", "shape": [2, 3, 2, 2], "tolerance": 0.0001 } }
|
| 556 |
+
},
|
| 557 |
+
{
|
| 558 |
+
"name": "ort_invertible_complex_rank4_axis2_projection",
|
| 559 |
+
"provenance": {
|
| 560 |
+
"source": "onnxruntime/test/providers/cpu/signal/signal_ops_test.cc",
|
| 561 |
+
"test": "SignalOpsTest.DFT20_invertible_complex",
|
| 562 |
+
"notes": "Single forward DFT projection from ORT's rank-4 complex invertibility sweep."
|
| 563 |
+
},
|
| 564 |
+
"attrs": {},
|
| 565 |
+
"inputs": {
|
| 566 |
+
"input": {
|
| 567 |
+
"dtype": "float32",
|
| 568 |
+
"shape": [2, 2, 4, 2],
|
| 569 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23, "scale": 5.0 }
|
| 570 |
+
}
|
| 571 |
+
},
|
| 572 |
+
"outputs": { "output": { "dtype": "float32", "shape": [2, 2, 4, 2], "tolerance": 0.0001 } }
|
| 573 |
+
},
|
| 574 |
+
{
|
| 575 |
+
"name": "ort_invertible_complex_rank4_axis0",
|
| 576 |
+
"provenance": {
|
| 577 |
+
"source": "onnxruntime/test/providers/cpu/signal/signal_ops_test.cc",
|
| 578 |
+
"test": "SignalOpsTest.DFT20_invertible_complex",
|
| 579 |
+
"notes": "Single forward DFT projection from ORT's rank-4 complex invertibility sweep."
|
| 580 |
+
},
|
| 581 |
+
"attrs": {},
|
| 582 |
+
"inputs": {
|
| 583 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [0] } },
|
| 584 |
+
"input": {
|
| 585 |
+
"dtype": "float32",
|
| 586 |
+
"shape": [4, 2, 3, 2],
|
| 587 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.17, "scale": 3.0 }
|
| 588 |
+
}
|
| 589 |
+
},
|
| 590 |
+
"outputs": { "output": { "dtype": "float32", "shape": [4, 2, 3, 2], "tolerance": 0.0001 } }
|
| 591 |
+
},
|
| 592 |
+
{
|
| 593 |
+
"name": "ort_invertible_real_rank3_axis0",
|
| 594 |
+
"provenance": {
|
| 595 |
+
"source": "onnxruntime/test/providers/cpu/signal/signal_ops_test.cc",
|
| 596 |
+
"test": "SignalOpsTest.DFT20_invertible_real",
|
| 597 |
+
"notes": "Single forward DFT projection from ORT's axis-0 invertibility sweep."
|
| 598 |
+
},
|
| 599 |
+
"attrs": {},
|
| 600 |
+
"inputs": {
|
| 601 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [0] } },
|
| 602 |
+
"input": {
|
| 603 |
+
"dtype": "float32",
|
| 604 |
+
"shape": [4, 3, 1],
|
| 605 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.09, "cosStep": 0.21, "scale": 4.0 }
|
| 606 |
+
}
|
| 607 |
+
},
|
| 608 |
+
"outputs": { "output": { "dtype": "float32", "shape": [4, 3, 2], "tolerance": 0.0001 } }
|
| 609 |
+
},
|
| 610 |
+
{
|
| 611 |
+
"name": "ort_rank2_complex_axis0",
|
| 612 |
+
"provenance": {
|
| 613 |
+
"source": "onnxruntime/test/providers/cpu/signal/signal_ops_test.cc",
|
| 614 |
+
"test": "SignalOpsTest.DFT20_2D_complex"
|
| 615 |
+
},
|
| 616 |
+
"attrs": {},
|
| 617 |
+
"inputs": {
|
| 618 |
+
"input": {
|
| 619 |
+
"dtype": "float32",
|
| 620 |
+
"shape": [8, 2],
|
| 621 |
+
"data": {
|
| 622 |
+
"kind": "values",
|
| 623 |
+
"values": [1.0, 0.0, 2.0, 0.0, 3.0, 0.0, 4.0, 0.0, 5.0, 0.0, 6.0, 0.0, 7.0, 0.0, 8.0, 0.0]
|
| 624 |
+
}
|
| 625 |
+
}
|
| 626 |
+
},
|
| 627 |
+
"outputs": {
|
| 628 |
+
"output": {
|
| 629 |
+
"dtype": "float32",
|
| 630 |
+
"shape": [8, 2],
|
| 631 |
+
"tolerance": 0.0001,
|
| 632 |
+
"data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/ort_rank2_complex_axis0_output_output" } }
|
| 633 |
+
}
|
| 634 |
+
}
|
| 635 |
+
},
|
| 636 |
+
{
|
| 637 |
+
"name": "ort_rank2_real_onesided_axis0",
|
| 638 |
+
"provenance": {
|
| 639 |
+
"source": "onnxruntime/test/providers/cpu/signal/signal_ops_test.cc",
|
| 640 |
+
"test": "SignalOpsTest.DFT20_2D_real"
|
| 641 |
+
},
|
| 642 |
+
"attrs": { "onesided": 1 },
|
| 643 |
+
"inputs": {
|
| 644 |
+
"input": {
|
| 645 |
+
"dtype": "float32",
|
| 646 |
+
"shape": [8, 1],
|
| 647 |
+
"data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0] }
|
| 648 |
+
}
|
| 649 |
+
},
|
| 650 |
+
"outputs": {
|
| 651 |
+
"output": {
|
| 652 |
+
"dtype": "float32",
|
| 653 |
+
"shape": [5, 2],
|
| 654 |
+
"tolerance": 0.0001,
|
| 655 |
+
"data": { "kind": "values", "values": [36.0, 0.0, -4.0, 9.65685, -4.0, 4.0, -4.0, 1.65685, -4.0, 0.0] }
|
| 656 |
+
}
|
| 657 |
+
}
|
| 658 |
+
},
|
| 659 |
+
{
|
| 660 |
+
"name": "ort_rank2_real_full_axis0",
|
| 661 |
+
"provenance": {
|
| 662 |
+
"source": "onnxruntime/test/providers/cpu/signal/signal_ops_test.cc",
|
| 663 |
+
"test": "SignalOpsTest.DFT20_2D_real"
|
| 664 |
+
},
|
| 665 |
+
"attrs": {},
|
| 666 |
+
"inputs": {
|
| 667 |
+
"input": {
|
| 668 |
+
"dtype": "float32",
|
| 669 |
+
"shape": [8, 1],
|
| 670 |
+
"data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0] }
|
| 671 |
+
},
|
| 672 |
+
"dft_length": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [8] } },
|
| 673 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [0] } }
|
| 674 |
+
},
|
| 675 |
+
"outputs": {
|
| 676 |
+
"output": {
|
| 677 |
+
"dtype": "float32",
|
| 678 |
+
"shape": [8, 2],
|
| 679 |
+
"tolerance": 0.0001,
|
| 680 |
+
"data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/ort_rank2_complex_axis0_output_output" } }
|
| 681 |
+
}
|
| 682 |
+
}
|
| 683 |
+
},
|
| 684 |
+
{
|
| 685 |
+
"name": "ort_rank2_irfft_even_axis0",
|
| 686 |
+
"provenance": {
|
| 687 |
+
"source": "onnxruntime/test/providers/cpu/signal/signal_ops_test.cc",
|
| 688 |
+
"test": "SignalOpsTest.DFT20_2D_complex_onesided_inverse"
|
| 689 |
+
},
|
| 690 |
+
"attrs": { "onesided": 1, "inverse": 1 },
|
| 691 |
+
"inputs": {
|
| 692 |
+
"input": {
|
| 693 |
+
"dtype": "float32",
|
| 694 |
+
"shape": [5, 2],
|
| 695 |
+
"data": { "kind": "values", "values": [36.0, 0.0, -4.0, 9.65685, -4.0, 4.0, -4.0, 1.65685, -4.0, 0.0] }
|
| 696 |
+
}
|
| 697 |
+
},
|
| 698 |
+
"outputs": {
|
| 699 |
+
"output": {
|
| 700 |
+
"dtype": "float32",
|
| 701 |
+
"shape": [8, 1],
|
| 702 |
+
"tolerance": 0.0001,
|
| 703 |
+
"data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0] }
|
| 704 |
+
}
|
| 705 |
+
}
|
| 706 |
+
},
|
| 707 |
+
{
|
| 708 |
+
"name": "ort_rank2_irfft_odd_axis0",
|
| 709 |
+
"provenance": {
|
| 710 |
+
"source": "onnxruntime/test/providers/cpu/signal/signal_ops_test.cc",
|
| 711 |
+
"test": "SignalOpsTest.DFT20_IRFFT_naive",
|
| 712 |
+
"notes": "Rank-2 axis-0 projection of ORT's odd-length IRFFT case; explicit output length disambiguates odd from even."
|
| 713 |
+
},
|
| 714 |
+
"attrs": { "onesided": 1, "inverse": 1 },
|
| 715 |
+
"inputs": {
|
| 716 |
+
"input": {
|
| 717 |
+
"dtype": "float32",
|
| 718 |
+
"shape": [3, 2],
|
| 719 |
+
"data": { "kind": "values", "values": [15.0, 0.0, -2.499999, 3.440955, -2.5, 0.8123] }
|
| 720 |
+
},
|
| 721 |
+
"dft_length": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [5] } },
|
| 722 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [0] } }
|
| 723 |
+
},
|
| 724 |
+
"outputs": {
|
| 725 |
+
"output": {
|
| 726 |
+
"dtype": "float32",
|
| 727 |
+
"shape": [5, 1],
|
| 728 |
+
"tolerance": 0.0001,
|
| 729 |
+
"data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, 5.0] }
|
| 730 |
+
}
|
| 731 |
+
}
|
| 732 |
+
},
|
| 733 |
+
{
|
| 734 |
+
"name": "onnx_backend_dft_axis1_rank4",
|
| 735 |
+
"provenance": {
|
| 736 |
+
"source": "cmake/external/onnx/onnx/backend/test/data/node/test_dft",
|
| 737 |
+
"notes": "Uses the official shape and attributes with deterministic generated data to avoid committing the large random tensor literal."
|
| 738 |
+
},
|
| 739 |
+
"attrs": {},
|
| 740 |
+
"inputs": {
|
| 741 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [1] } },
|
| 742 |
+
"input": {
|
| 743 |
+
"dtype": "float32",
|
| 744 |
+
"shape": [1, 10, 10, 1],
|
| 745 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.019, "cosStep": 0.031, "scale": 1.0 }
|
| 746 |
+
}
|
| 747 |
+
},
|
| 748 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 10, 10, 2], "tolerance": 0.0001 } }
|
| 749 |
+
},
|
| 750 |
+
{
|
| 751 |
+
"name": "onnx_backend_dft_axis2_rank4",
|
| 752 |
+
"provenance": {
|
| 753 |
+
"source": "cmake/external/onnx/onnx/backend/test/data/node/test_dft_axis",
|
| 754 |
+
"notes": "Uses the official shape and attributes with deterministic generated data to avoid committing the large random tensor literal."
|
| 755 |
+
},
|
| 756 |
+
"attrs": {},
|
| 757 |
+
"inputs": {
|
| 758 |
+
"input": {
|
| 759 |
+
"dtype": "float32",
|
| 760 |
+
"shape": [1, 10, 10, 1],
|
| 761 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.019, "cosStep": 0.031, "scale": 1.0 }
|
| 762 |
+
}
|
| 763 |
+
},
|
| 764 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 10, 10, 2], "tolerance": 0.0001 } }
|
| 765 |
+
},
|
| 766 |
+
{
|
| 767 |
+
"name": "onnx_backend_dft_inverse_axis1_rank4",
|
| 768 |
+
"provenance": {
|
| 769 |
+
"source": "cmake/external/onnx/onnx/backend/test/data/node/test_dft_inverse",
|
| 770 |
+
"notes": "Uses the official shape and attributes with deterministic generated data to avoid committing the large random tensor literal."
|
| 771 |
+
},
|
| 772 |
+
"attrs": { "inverse": 1 },
|
| 773 |
+
"inputs": {
|
| 774 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [1] } },
|
| 775 |
+
"input": {
|
| 776 |
+
"dtype": "float32",
|
| 777 |
+
"shape": [1, 10, 10, 2],
|
| 778 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.017, "cosStep": 0.029, "scale": 1.0 }
|
| 779 |
+
}
|
| 780 |
+
},
|
| 781 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 10, 10, 2], "tolerance": 0.0001 } }
|
| 782 |
+
},
|
| 783 |
+
{
|
| 784 |
+
"name": "onnx_backend_dft_irfft_axis1_rank4",
|
| 785 |
+
"provenance": {
|
| 786 |
+
"source": "cmake/external/onnx/onnx/backend/test/data/node/test_dft_irfft",
|
| 787 |
+
"notes": "Uses the official shape and attributes with deterministic generated data to avoid committing the large random tensor literal."
|
| 788 |
+
},
|
| 789 |
+
"attrs": { "inverse": 1, "onesided": 1 },
|
| 790 |
+
"inputs": {
|
| 791 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [1] } },
|
| 792 |
+
"input": {
|
| 793 |
+
"dtype": "float32",
|
| 794 |
+
"shape": [1, 6, 10, 2],
|
| 795 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.023, "cosStep": 0.037, "scale": 1.0 }
|
| 796 |
+
}
|
| 797 |
+
},
|
| 798 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 10, 10, 1], "tolerance": 0.0001 } }
|
| 799 |
+
},
|
| 800 |
+
{
|
| 801 |
+
"name": "onnx_backend_dft_rfft_axis1_rank4",
|
| 802 |
+
"provenance": {
|
| 803 |
+
"source": "cmake/external/onnx/onnx/backend/test/data/node/test_dft_rfft",
|
| 804 |
+
"notes": "Uses the official shape and attributes with deterministic generated data to avoid committing the large random tensor literal."
|
| 805 |
+
},
|
| 806 |
+
"attrs": { "onesided": 1 },
|
| 807 |
+
"inputs": {
|
| 808 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [1] } },
|
| 809 |
+
"input": {
|
| 810 |
+
"dtype": "float32",
|
| 811 |
+
"shape": [1, 10, 10, 1],
|
| 812 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.021, "cosStep": 0.034, "scale": 1.0 }
|
| 813 |
+
}
|
| 814 |
+
},
|
| 815 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 6, 10, 2], "tolerance": 0.0001 } }
|
| 816 |
+
},
|
| 817 |
+
{
|
| 818 |
+
"name": "onnx_backend_dft_opset19_axis1_rank4",
|
| 819 |
+
"provenance": {
|
| 820 |
+
"source": "cmake/external/onnx/onnx/backend/test/data/node/test_dft_opset19",
|
| 821 |
+
"notes": "Uses the official shape and attributes with deterministic generated data to avoid committing the large random tensor literal."
|
| 822 |
+
},
|
| 823 |
+
"attrs": {},
|
| 824 |
+
"inputs": {
|
| 825 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [1] } },
|
| 826 |
+
"input": {
|
| 827 |
+
"dtype": "float32",
|
| 828 |
+
"shape": [1, 10, 10, 1],
|
| 829 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.026, "cosStep": 0.041, "scale": 1.0 }
|
| 830 |
+
}
|
| 831 |
+
},
|
| 832 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 10, 10, 2], "tolerance": 0.0001 } }
|
| 833 |
+
},
|
| 834 |
+
{
|
| 835 |
+
"name": "onnx_backend_dft_axis_opset19_axis2_rank4",
|
| 836 |
+
"provenance": {
|
| 837 |
+
"source": "cmake/external/onnx/onnx/backend/test/data/node/test_dft_axis_opset19",
|
| 838 |
+
"notes": "Uses the official shape and attributes with deterministic generated data to avoid committing the large random tensor literal."
|
| 839 |
+
},
|
| 840 |
+
"attrs": {},
|
| 841 |
+
"inputs": {
|
| 842 |
+
"input": {
|
| 843 |
+
"dtype": "float32",
|
| 844 |
+
"shape": [1, 10, 10, 1],
|
| 845 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.026, "cosStep": 0.041, "scale": 1.0 }
|
| 846 |
+
}
|
| 847 |
+
},
|
| 848 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 10, 10, 2], "tolerance": 0.0001 } }
|
| 849 |
+
},
|
| 850 |
+
{
|
| 851 |
+
"name": "onnx_backend_dft_inverse_opset19_axis1_rank4",
|
| 852 |
+
"provenance": {
|
| 853 |
+
"source": "cmake/external/onnx/onnx/backend/test/data/node/test_dft_inverse_opset19",
|
| 854 |
+
"notes": "Uses the official shape and attributes with deterministic generated data to avoid committing the large random tensor literal."
|
| 855 |
+
},
|
| 856 |
+
"attrs": { "inverse": 1 },
|
| 857 |
+
"inputs": {
|
| 858 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [1] } },
|
| 859 |
+
"input": {
|
| 860 |
+
"dtype": "float32",
|
| 861 |
+
"shape": [1, 10, 10, 2],
|
| 862 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.027, "cosStep": 0.039, "scale": 1.0 }
|
| 863 |
+
}
|
| 864 |
+
},
|
| 865 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 10, 10, 2], "tolerance": 0.0001 } }
|
| 866 |
+
},
|
| 867 |
+
{
|
| 868 |
+
"name": "onnx_backend_dft_irfft_opset19_axis1_rank4",
|
| 869 |
+
"provenance": {
|
| 870 |
+
"source": "cmake/external/onnx/onnx/backend/test/data/node/test_dft_irfft_opset19",
|
| 871 |
+
"notes": "Uses the official shape and attributes with deterministic generated data to avoid committing the large random tensor literal."
|
| 872 |
+
},
|
| 873 |
+
"attrs": { "inverse": 1, "onesided": 1 },
|
| 874 |
+
"inputs": {
|
| 875 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [1] } },
|
| 876 |
+
"input": {
|
| 877 |
+
"dtype": "float32",
|
| 878 |
+
"shape": [1, 6, 10, 2],
|
| 879 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.025, "cosStep": 0.043, "scale": 1.0 }
|
| 880 |
+
}
|
| 881 |
+
},
|
| 882 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 10, 10, 1], "tolerance": 0.0001 } }
|
| 883 |
+
},
|
| 884 |
+
{
|
| 885 |
+
"name": "onnx_backend_dft_rfft_opset19_axis1_rank4",
|
| 886 |
+
"provenance": {
|
| 887 |
+
"source": "cmake/external/onnx/onnx/backend/test/data/node/test_dft_rfft_opset19",
|
| 888 |
+
"notes": "Uses the official shape and attributes with deterministic generated data to avoid committing the large random tensor literal."
|
| 889 |
+
},
|
| 890 |
+
"attrs": { "onesided": 1 },
|
| 891 |
+
"inputs": {
|
| 892 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [1] } },
|
| 893 |
+
"input": {
|
| 894 |
+
"dtype": "float32",
|
| 895 |
+
"shape": [1, 10, 10, 1],
|
| 896 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.029, "cosStep": 0.046, "scale": 1.0 }
|
| 897 |
+
}
|
| 898 |
+
},
|
| 899 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 6, 10, 2], "tolerance": 0.0001 } }
|
| 900 |
+
},
|
| 901 |
+
{
|
| 902 |
+
"name": "fft_rank2_axis0_real_onesided_len256",
|
| 903 |
+
"attrs": { "onesided": 1 },
|
| 904 |
+
"inputs": {
|
| 905 |
+
"input": {
|
| 906 |
+
"dtype": "float32",
|
| 907 |
+
"shape": [256, 1],
|
| 908 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.021, "cosStep": 0.031, "scale": 1.0 }
|
| 909 |
+
}
|
| 910 |
+
},
|
| 911 |
+
"outputs": { "output": { "dtype": "float32", "shape": [129, 2], "tolerance": 0.001, "relTolerance": 0.0001 } }
|
| 912 |
+
},
|
| 913 |
+
{
|
| 914 |
+
"name": "fft_rank2_axis0_complex_full_len512",
|
| 915 |
+
"attrs": {},
|
| 916 |
+
"inputs": {
|
| 917 |
+
"input": {
|
| 918 |
+
"dtype": "float32",
|
| 919 |
+
"shape": [512, 2],
|
| 920 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.017, "cosStep": 0.029, "scale": 1.0 }
|
| 921 |
+
}
|
| 922 |
+
},
|
| 923 |
+
"outputs": { "output": { "dtype": "float32", "shape": [512, 2], "tolerance": 0.02, "relTolerance": 0.0001 } }
|
| 924 |
+
},
|
| 925 |
+
{
|
| 926 |
+
"name": "fft_batch_axis1_real_full_len1024_batch2",
|
| 927 |
+
"attrs": {},
|
| 928 |
+
"inputs": {
|
| 929 |
+
"input": {
|
| 930 |
+
"dtype": "float32",
|
| 931 |
+
"shape": [2, 1024, 1],
|
| 932 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.041, "scale": 1.0 }
|
| 933 |
+
}
|
| 934 |
+
},
|
| 935 |
+
"outputs": { "output": { "dtype": "float32", "shape": [2, 1024, 2], "tolerance": 0.1, "relTolerance": 0.0001 } }
|
| 936 |
+
},
|
| 937 |
+
{
|
| 938 |
+
"name": "tiled_real_nonpow2_len513_full",
|
| 939 |
+
"provenance": {
|
| 940 |
+
"notes": "Compact correctness lock for the large non-power-of-two tiled direct DFT, including its 64-lane tunable dispatch, final partial frequency workgroup, and twiddle resets."
|
| 941 |
+
},
|
| 942 |
+
"tunables": { "WORKGROUP_SIZE": 64 },
|
| 943 |
+
"inputs": {
|
| 944 |
+
"input": {
|
| 945 |
+
"dtype": "float32",
|
| 946 |
+
"shape": [1, 513, 1],
|
| 947 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.027, "scale": 0.5 }
|
| 948 |
+
}
|
| 949 |
+
},
|
| 950 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 513, 2], "tolerance": 0.03, "relTolerance": 0.0002 } }
|
| 951 |
+
},
|
| 952 |
+
{
|
| 953 |
+
"name": "tiled_real_nonpow2_len4095_full_boundary",
|
| 954 |
+
"provenance": {
|
| 955 |
+
"notes": "Boundary-length accuracy lock for the non-power-of-two tiled DFT. Exercises all shared-memory slots and many twiddle resets at the largest supported signal length."
|
| 956 |
+
},
|
| 957 |
+
"inputs": {
|
| 958 |
+
"input": {
|
| 959 |
+
"dtype": "float32",
|
| 960 |
+
"shape": [1, 4095, 1],
|
| 961 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.0013, "cosStep": 0.0027, "scale": 0.05 }
|
| 962 |
+
}
|
| 963 |
+
},
|
| 964 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 4095, 2], "tolerance": 0.1, "relTolerance": 0.0005 } }
|
| 965 |
+
},
|
| 966 |
+
{
|
| 967 |
+
"name": "fft_batch_axis1_complex_inverse_len512",
|
| 968 |
+
"attrs": { "inverse": 1 },
|
| 969 |
+
"inputs": {
|
| 970 |
+
"input": {
|
| 971 |
+
"dtype": "float32",
|
| 972 |
+
"shape": [1, 512, 2],
|
| 973 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.023, "cosStep": 0.037, "scale": 1.0 }
|
| 974 |
+
}
|
| 975 |
+
},
|
| 976 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 512, 2], "tolerance": 0.0001, "relTolerance": 0.0001 } }
|
| 977 |
+
},
|
| 978 |
+
{
|
| 979 |
+
"name": "fft_batch_axis1_real_onesided_len2048",
|
| 980 |
+
"attrs": { "onesided": 1 },
|
| 981 |
+
"inputs": {
|
| 982 |
+
"input": {
|
| 983 |
+
"dtype": "float32",
|
| 984 |
+
"shape": [1, 2048, 1],
|
| 985 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.011, "cosStep": 0.019, "scale": 1.0 }
|
| 986 |
+
}
|
| 987 |
+
},
|
| 988 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 1025, 2], "tolerance": 0.06, "relTolerance": 0.0001 } }
|
| 989 |
+
},
|
| 990 |
+
{
|
| 991 |
+
"name": "fft_batch_axis1_real_full_len4096_storage_inplace",
|
| 992 |
+
"provenance": {
|
| 993 |
+
"notes": "Locks the portable single-dispatch storage-buffer radix-2 route above the shared Stockham size ceiling."
|
| 994 |
+
},
|
| 995 |
+
"inputs": {
|
| 996 |
+
"input": {
|
| 997 |
+
"dtype": "float32",
|
| 998 |
+
"shape": [1, 4096, 1],
|
| 999 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.009, "cosStep": 0.017, "scale": 0.5 }
|
| 1000 |
+
}
|
| 1001 |
+
},
|
| 1002 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 4096, 2], "tolerance": 0.35, "relTolerance": 0.001 } }
|
| 1003 |
+
},
|
| 1004 |
+
{
|
| 1005 |
+
"name": "fft_batch_axis1_complex_inverse_len4096_storage_inplace",
|
| 1006 |
+
"provenance": {
|
| 1007 |
+
"notes": "The storage-buffer radix-2 route only takes over above the shared-memory ceiling at length 2048, and every case that long was a forward transform of a real signal. This one is the inverse of a complex signal, which is the route's other three knobs at once: the twiddle sign, the two-component load, and the 1/N scaling."
|
| 1008 |
+
},
|
| 1009 |
+
"attrs": { "inverse": 1, "onesided": 0 },
|
| 1010 |
+
"inputs": {
|
| 1011 |
+
"input": {
|
| 1012 |
+
"dtype": "float32",
|
| 1013 |
+
"shape": [1, 4096, 2],
|
| 1014 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.009, "cosStep": 0.017, "scale": 0.5 }
|
| 1015 |
+
}
|
| 1016 |
+
},
|
| 1017 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 4096, 2], "tolerance": 0.0001, "relTolerance": 0.001 } }
|
| 1018 |
+
},
|
| 1019 |
+
{
|
| 1020 |
+
"name": "fft_batch_axis1_real_onesided_len4096_storage_scratch",
|
| 1021 |
+
"provenance": {
|
| 1022 |
+
"notes": "Covers the one-dispatch storage-buffer DIT route when a onesided output is too small to serve as its own FFT scratch."
|
| 1023 |
+
},
|
| 1024 |
+
"attrs": { "onesided": 1 },
|
| 1025 |
+
"inputs": {
|
| 1026 |
+
"input": {
|
| 1027 |
+
"dtype": "float32",
|
| 1028 |
+
"shape": [1, 4096, 1],
|
| 1029 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.009, "cosStep": 0.017, "scale": 0.5 }
|
| 1030 |
+
}
|
| 1031 |
+
},
|
| 1032 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 2049, 2], "tolerance": 0.35, "relTolerance": 0.001 } }
|
| 1033 |
+
},
|
| 1034 |
+
{
|
| 1035 |
+
"name": "fft_rank4_axis1_real_full_len256_inner3",
|
| 1036 |
+
"attrs": {},
|
| 1037 |
+
"inputs": {
|
| 1038 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [1] } },
|
| 1039 |
+
"input": {
|
| 1040 |
+
"dtype": "float32",
|
| 1041 |
+
"shape": [2, 256, 3, 1],
|
| 1042 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.027, "cosStep": 0.033, "scale": 1.0 }
|
| 1043 |
+
}
|
| 1044 |
+
},
|
| 1045 |
+
"outputs": {
|
| 1046 |
+
"output": { "dtype": "float32", "shape": [2, 256, 3, 2], "tolerance": 0.015, "relTolerance": 0.0001 }
|
| 1047 |
+
}
|
| 1048 |
+
},
|
| 1049 |
+
{
|
| 1050 |
+
"name": "fft_rank4_axis1_real_onesided_len16_inner5",
|
| 1051 |
+
"attrs": { "onesided": 1 },
|
| 1052 |
+
"inputs": {
|
| 1053 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [1] } },
|
| 1054 |
+
"input": {
|
| 1055 |
+
"dtype": "float32",
|
| 1056 |
+
"shape": [1, 16, 5, 1],
|
| 1057 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.043, "cosStep": 0.021, "scale": 1.0 }
|
| 1058 |
+
}
|
| 1059 |
+
},
|
| 1060 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 9, 5, 2], "tolerance": 0.0001 } }
|
| 1061 |
+
},
|
| 1062 |
+
{
|
| 1063 |
+
"name": "fft_rank4_axis2_real_onesided_len512",
|
| 1064 |
+
"attrs": { "onesided": 1 },
|
| 1065 |
+
"inputs": {
|
| 1066 |
+
"input": {
|
| 1067 |
+
"dtype": "float32",
|
| 1068 |
+
"shape": [2, 3, 512, 1],
|
| 1069 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.023, "cosStep": 0.039, "scale": 1.0 }
|
| 1070 |
+
}
|
| 1071 |
+
},
|
| 1072 |
+
"outputs": {
|
| 1073 |
+
"output": { "dtype": "float32", "shape": [2, 3, 257, 2], "tolerance": 0.002, "relTolerance": 0.0001 }
|
| 1074 |
+
}
|
| 1075 |
+
},
|
| 1076 |
+
{
|
| 1077 |
+
"name": "empty_zero_dim",
|
| 1078 |
+
"attrs": { "inverse": 0, "onesided": 0 },
|
| 1079 |
+
"inputs": { "input": { "dtype": "float32", "shape": [0, 4, 1], "data": { "kind": "values", "values": [] } } },
|
| 1080 |
+
"outputs": { "output": { "dtype": "float32", "shape": [0, 4, 2], "tolerance": 0 } }
|
| 1081 |
+
},
|
| 1082 |
+
{
|
| 1083 |
+
"name": "ort_caseB_empty",
|
| 1084 |
+
"attrs": { "onesided": 1 },
|
| 1085 |
+
"inputs": { "input": { "dtype": "float32", "shape": [1, 0, 1], "data": { "kind": "values", "values": [] } } },
|
| 1086 |
+
"outputs": {
|
| 1087 |
+
"output": {
|
| 1088 |
+
"dtype": "float32",
|
| 1089 |
+
"shape": [1, 1, 2],
|
| 1090 |
+
"data": { "kind": "values", "values": [0.0, 0.0] },
|
| 1091 |
+
"tolerance": 0.001
|
| 1092 |
+
}
|
| 1093 |
+
}
|
| 1094 |
+
},
|
| 1095 |
+
{
|
| 1096 |
+
"name": "irfft_naive_axis1_over_128_dispatch_guard",
|
| 1097 |
+
"provenance": {
|
| 1098 |
+
"source": "synthetic",
|
| 1099 |
+
"test": "naive batch_axis1 IRFFT, numel(output) > 128 dispatch coverage",
|
| 1100 |
+
"notes": "Odd output length 255 (non-power-of-2) forces the naive batch_axis1 variant (FFT radix-2 paths don't apply). dim(output,2)==1 on the onesided-inverse path, so the dispatch numerator must be numel(output)/dim(output,2)=255, not numel(output)/2=127: the latter only launches ceil(127/WG) workgroups and silently drops outputs >= ~128. numel(output)=255 > WG(128) so the drop is observable."
|
| 1101 |
+
},
|
| 1102 |
+
"attrs": { "inverse": 1, "onesided": 1 },
|
| 1103 |
+
"inputs": {
|
| 1104 |
+
"input": {
|
| 1105 |
+
"dtype": "float32",
|
| 1106 |
+
"shape": [1, 128, 2],
|
| 1107 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.019, "scale": 1.0 }
|
| 1108 |
+
},
|
| 1109 |
+
"dft_length": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [255] } },
|
| 1110 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [1] } }
|
| 1111 |
+
},
|
| 1112 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 255, 1], "tolerance": 0.01, "relTolerance": 0.01 } }
|
| 1113 |
+
},
|
| 1114 |
+
{
|
| 1115 |
+
"name": "dft_length_default_axis_no_axis_input",
|
| 1116 |
+
"provenance": {
|
| 1117 |
+
"notes": "dft_length input present, axis input omitted (default transform axis 1). Exercises the general_len variant: forward DFT of a real rank-3 signal where dft_length matches the signal extent."
|
| 1118 |
+
},
|
| 1119 |
+
"inputs": {
|
| 1120 |
+
"input": {
|
| 1121 |
+
"dtype": "float32",
|
| 1122 |
+
"shape": [1, 4, 1],
|
| 1123 |
+
"data": { "kind": "values", "values": [1.0, 2.0, 0.0, -1.0] }
|
| 1124 |
+
},
|
| 1125 |
+
"dft_length": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [4] } }
|
| 1126 |
+
},
|
| 1127 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 4, 2], "tolerance": 0.00001 } }
|
| 1128 |
+
},
|
| 1129 |
+
{
|
| 1130 |
+
"name": "general_axis_input_len2048_naive_vs_fft_batch32",
|
| 1131 |
+
"attrs": { "inverse": 0, "onesided": 0 },
|
| 1132 |
+
"inputs": {
|
| 1133 |
+
"input": {
|
| 1134 |
+
"dtype": "float32",
|
| 1135 |
+
"shape": [32, 2048, 1],
|
| 1136 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.0007, "cosStep": 0.0013, "scale": 1.0 }
|
| 1137 |
+
},
|
| 1138 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [1] } }
|
| 1139 |
+
},
|
| 1140 |
+
"outputs": { "output": { "dtype": "float32", "shape": [32, 2048, 2], "tolerance": 0.05, "relTolerance": 0.01 } }
|
| 1141 |
+
},
|
| 1142 |
+
{
|
| 1143 |
+
"name": "general_axis_input_gidy_fold_over_8m_rows",
|
| 1144 |
+
"attrs": { "inverse": 0, "onesided": 0 },
|
| 1145 |
+
"inputs": {
|
| 1146 |
+
"input": {
|
| 1147 |
+
"dtype": "float32",
|
| 1148 |
+
"shape": [2099200, 4, 1],
|
| 1149 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.019, "scale": 1.0 }
|
| 1150 |
+
},
|
| 1151 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [1] } }
|
| 1152 |
+
},
|
| 1153 |
+
"outputs": {
|
| 1154 |
+
"output": { "dtype": "float32", "shape": [2099200, 4, 2], "tolerance": 0.0001, "relTolerance": 0.0001 }
|
| 1155 |
+
}
|
| 1156 |
+
},
|
| 1157 |
+
{
|
| 1158 |
+
"name": "whisper_nfft400_naive_batch_axis1_onesided",
|
| 1159 |
+
"attrs": { "onesided": 1 },
|
| 1160 |
+
"inputs": {
|
| 1161 |
+
"input": {
|
| 1162 |
+
"dtype": "float32",
|
| 1163 |
+
"shape": [200, 400, 1],
|
| 1164 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.011, "cosStep": 0.017, "scale": 1.0 }
|
| 1165 |
+
}
|
| 1166 |
+
},
|
| 1167 |
+
"outputs": { "output": { "dtype": "float32", "shape": [200, 201, 2], "tolerance": 0.02, "relTolerance": 0.001 } }
|
| 1168 |
+
},
|
| 1169 |
+
{
|
| 1170 |
+
"name": "irfft_batch_axis1_len512_reconstruct_real",
|
| 1171 |
+
"attrs": { "inverse": 1, "onesided": 1 },
|
| 1172 |
+
"inputs": {
|
| 1173 |
+
"input": {
|
| 1174 |
+
"dtype": "float32",
|
| 1175 |
+
"shape": [8, 257, 2],
|
| 1176 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.023, "cosStep": 0.031, "scale": 1.0 }
|
| 1177 |
+
}
|
| 1178 |
+
},
|
| 1179 |
+
"outputs": { "output": { "dtype": "float32", "shape": [8, 512, 1], "tolerance": 0.02, "relTolerance": 0.01 } }
|
| 1180 |
+
},
|
| 1181 |
+
{
|
| 1182 |
+
"name": "general_len_rank4_axis1_dftlength_zeropad_len6",
|
| 1183 |
+
"provenance": {
|
| 1184 |
+
"notes": "dft_length=6 zero-pads a rank-4 axis-1 signal of length 4 up to 6 samples, forcing the general_len variant (rank-4 coordinate decode + zero-pad guard n>=input_axis_length). Untested rank-4 dft_length path."
|
| 1185 |
+
},
|
| 1186 |
+
"attrs": { "inverse": 0, "onesided": 0 },
|
| 1187 |
+
"inputs": {
|
| 1188 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [1] } },
|
| 1189 |
+
"input": {
|
| 1190 |
+
"dtype": "float32",
|
| 1191 |
+
"shape": [2, 4, 3, 1],
|
| 1192 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.037, "cosStep": 0.061, "scale": 2.0 }
|
| 1193 |
+
},
|
| 1194 |
+
"dft_length": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [6] } }
|
| 1195 |
+
},
|
| 1196 |
+
"outputs": {
|
| 1197 |
+
"output": { "dtype": "float32", "shape": [2, 6, 3, 2], "tolerance": 0.0002, "relTolerance": 0.0001 }
|
| 1198 |
+
}
|
| 1199 |
+
},
|
| 1200 |
+
{
|
| 1201 |
+
"name": "general_irfft_rank4_axis2_odd_len5_no_nyquist",
|
| 1202 |
+
"provenance": {
|
| 1203 |
+
"source": "onnxruntime/test/providers/cpu/signal/signal_ops_test.cc",
|
| 1204 |
+
"test": "SignalOpsTest.DFT20_IRFFT_naive",
|
| 1205 |
+
"notes": "Rank-4 axis-2 odd-length IRFFT via general_axis_len: odd output length 5 has no exact Nyquist bin, so no weight-2 doubling should occur. Exercises the general kernel onesided_inverse branch + rank-4 coordinate decode."
|
| 1206 |
+
},
|
| 1207 |
+
"attrs": { "inverse": 1, "onesided": 1 },
|
| 1208 |
+
"inputs": {
|
| 1209 |
+
"input": {
|
| 1210 |
+
"dtype": "float32",
|
| 1211 |
+
"shape": [2, 2, 3, 2],
|
| 1212 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.029, "cosStep": 0.047, "scale": 3.0 }
|
| 1213 |
+
},
|
| 1214 |
+
"dft_length": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [5] } },
|
| 1215 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [2] } }
|
| 1216 |
+
},
|
| 1217 |
+
"outputs": { "output": { "dtype": "float32", "shape": [2, 2, 5, 1], "tolerance": 0.0005, "relTolerance": 0.001 } }
|
| 1218 |
+
},
|
| 1219 |
+
{
|
| 1220 |
+
"name": "general_axis_len_negative_axis_rank3_dftlength_truncate",
|
| 1221 |
+
"provenance": {
|
| 1222 |
+
"notes": "Negative axis input (-2 -> axis 1 for rank 3) combined with a truncating dft_length (6 -> 4) forces general_axis_len and exercises runtime resolve_axis() negative normalization together with signal truncation. Untested combination."
|
| 1223 |
+
},
|
| 1224 |
+
"attrs": { "inverse": 0, "onesided": 0 },
|
| 1225 |
+
"inputs": {
|
| 1226 |
+
"input": {
|
| 1227 |
+
"dtype": "float32",
|
| 1228 |
+
"shape": [3, 6, 1],
|
| 1229 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.033, "cosStep": 0.052, "scale": 2.0 }
|
| 1230 |
+
},
|
| 1231 |
+
"dft_length": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [4] } },
|
| 1232 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [-2] } }
|
| 1233 |
+
},
|
| 1234 |
+
"outputs": { "output": { "dtype": "float32", "shape": [3, 4, 2], "tolerance": 0.0002, "relTolerance": 0.0001 } }
|
| 1235 |
+
},
|
| 1236 |
+
{
|
| 1237 |
+
"name": "general_len_rank2_axis0_dftlength_zeropad_odd",
|
| 1238 |
+
"provenance": {
|
| 1239 |
+
"notes": "Rank-2 axis-0 with dft_length=5 zero-padding a length-3 signal forces general_len; validates the rank-2 (n_logical=1, pad=2) coordinate decode of dft-general under zero-padding."
|
| 1240 |
+
},
|
| 1241 |
+
"attrs": { "inverse": 0, "onesided": 0 },
|
| 1242 |
+
"inputs": {
|
| 1243 |
+
"input": { "dtype": "float32", "shape": [3, 1], "data": { "kind": "values", "values": [1.0, 2.0, 3.0] } },
|
| 1244 |
+
"dft_length": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [5] } }
|
| 1245 |
+
},
|
| 1246 |
+
"outputs": { "output": { "dtype": "float32", "shape": [5, 2], "tolerance": 0.00002 } }
|
| 1247 |
+
},
|
| 1248 |
+
{
|
| 1249 |
+
"name": "fft_explicit_equal_length128_batch4_onesided",
|
| 1250 |
+
"provenance": {
|
| 1251 |
+
"notes": "Correctness lock for the optimized explicit-dft_length route when the runtime length equals the power-of-two signal length."
|
| 1252 |
+
},
|
| 1253 |
+
"attrs": { "inverse": 0, "onesided": 1 },
|
| 1254 |
+
"inputs": {
|
| 1255 |
+
"input": {
|
| 1256 |
+
"dtype": "float32",
|
| 1257 |
+
"shape": [4, 128, 1],
|
| 1258 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.033, "cosStep": 0.052, "scale": 2.0 }
|
| 1259 |
+
},
|
| 1260 |
+
"dft_length": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [128] } }
|
| 1261 |
+
},
|
| 1262 |
+
"outputs": { "output": { "dtype": "float32", "shape": [4, 65, 2], "tolerance": 0.002, "relTolerance": 0.001 } }
|
| 1263 |
+
},
|
| 1264 |
+
{
|
| 1265 |
+
"name": "fft_runtime_axis1_full_len16_batch2",
|
| 1266 |
+
"provenance": {
|
| 1267 |
+
"notes": "A scalar runtime axis selects axis 1, checking the guarded Stockham FFT and the complementary general-axis pass on the same input."
|
| 1268 |
+
},
|
| 1269 |
+
"attrs": { "inverse": 0, "onesided": 0 },
|
| 1270 |
+
"inputs": {
|
| 1271 |
+
"input": {
|
| 1272 |
+
"dtype": "float32",
|
| 1273 |
+
"shape": [2, 16, 1],
|
| 1274 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.031, "cosStep": 0.047, "scale": 1.5 }
|
| 1275 |
+
},
|
| 1276 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [1] } }
|
| 1277 |
+
},
|
| 1278 |
+
"outputs": {
|
| 1279 |
+
"output": { "dtype": "float32", "shape": [2, 16, 2], "tolerance": 0.00002, "relTolerance": 0.00001 }
|
| 1280 |
+
}
|
| 1281 |
+
},
|
| 1282 |
+
{
|
| 1283 |
+
"name": "fft_batch_axis1_real_full_len8192_storage_inplace_generic",
|
| 1284 |
+
"provenance": { "notes": "Non-4096 correctness lock for the geometry-derived storage-buffer radix-2 route." },
|
| 1285 |
+
"inputs": {
|
| 1286 |
+
"input": {
|
| 1287 |
+
"dtype": "float32",
|
| 1288 |
+
"shape": [1, 8192, 1],
|
| 1289 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.0045, "cosStep": 0.0085, "scale": 0.25 }
|
| 1290 |
+
}
|
| 1291 |
+
},
|
| 1292 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 8192, 2], "tolerance": 0.7, "relTolerance": 0.002 } }
|
| 1293 |
+
},
|
| 1294 |
+
{
|
| 1295 |
+
"name": "fft_batch_axis1_real_onesided_len8192_storage_scratch_generic",
|
| 1296 |
+
"provenance": { "notes": "Non-4096 correctness lock for the geometry-derived one-sided radix-2 scratch route." },
|
| 1297 |
+
"attrs": { "onesided": 1 },
|
| 1298 |
+
"inputs": {
|
| 1299 |
+
"input": {
|
| 1300 |
+
"dtype": "float32",
|
| 1301 |
+
"shape": [1, 8192, 1],
|
| 1302 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.0045, "cosStep": 0.0085, "scale": 0.25 }
|
| 1303 |
+
}
|
| 1304 |
+
},
|
| 1305 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 4097, 2], "tolerance": 0.7, "relTolerance": 0.002 } }
|
| 1306 |
+
},
|
| 1307 |
+
{
|
| 1308 |
+
"name": "tiled_real_nonpow2_len4097_onesided_above_old_boundary",
|
| 1309 |
+
"provenance": {
|
| 1310 |
+
"notes": "Proves the tiled direct transform is bounded by device workgroup storage rather than the former 4095 shape cutoff."
|
| 1311 |
+
},
|
| 1312 |
+
"attrs": { "onesided": 1 },
|
| 1313 |
+
"inputs": {
|
| 1314 |
+
"input": {
|
| 1315 |
+
"dtype": "float32",
|
| 1316 |
+
"shape": [1, 4097, 1],
|
| 1317 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.0013, "cosStep": 0.0027, "scale": 0.05 }
|
| 1318 |
+
}
|
| 1319 |
+
},
|
| 1320 |
+
"outputs": { "output": { "dtype": "float32", "shape": [1, 2049, 2], "tolerance": 0.15, "relTolerance": 0.001 } }
|
| 1321 |
+
},
|
| 1322 |
+
{
|
| 1323 |
+
"name": "rank5_batched_axis3",
|
| 1324 |
+
"provenance": {
|
| 1325 |
+
"notes": "ONNX DFT permits arbitrary batch rank around the transformed axis. With the axis given as an attribute the general kernel needs only (outer, axis, inner), so the batch rank never reaches it."
|
| 1326 |
+
},
|
| 1327 |
+
"attrs": { "inverse": 0, "onesided": 0 },
|
| 1328 |
+
"inputs": {
|
| 1329 |
+
"input": {
|
| 1330 |
+
"dtype": "float32",
|
| 1331 |
+
"shape": [2, 1, 2, 4, 1],
|
| 1332 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.29, "scale": 0.5 }
|
| 1333 |
+
}
|
| 1334 |
+
},
|
| 1335 |
+
"outputs": { "output": { "dtype": "float32", "shape": [2, 1, 2, 4, 2], "tolerance": 0.00001 } }
|
| 1336 |
+
},
|
| 1337 |
+
{
|
| 1338 |
+
"name": "rank6_axis2_batch_and_inner",
|
| 1339 |
+
"provenance": {
|
| 1340 |
+
"notes": "A rank the packing has never seen, with batch dims on both sides of the transform axis (outer 4, inner 2). If any rank ceiling had merely been raised rather than removed, this is the case that finds it."
|
| 1341 |
+
},
|
| 1342 |
+
"attrs": { "inverse": 0, "onesided": 0 },
|
| 1343 |
+
"inputs": {
|
| 1344 |
+
"axis": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [2] } },
|
| 1345 |
+
"input": {
|
| 1346 |
+
"dtype": "float32",
|
| 1347 |
+
"shape": [2, 2, 3, 1, 2, 1],
|
| 1348 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.41, "scale": 0.6 }
|
| 1349 |
+
}
|
| 1350 |
+
},
|
| 1351 |
+
"outputs": { "output": { "dtype": "float32", "shape": [2, 2, 3, 1, 2, 2], "tolerance": 0.00001 } }
|
| 1352 |
+
},
|
| 1353 |
+
{
|
| 1354 |
+
"name": "rank8_default_axis_contract_ceiling",
|
| 1355 |
+
"provenance": { "notes": "Exercises the manifest's advertised rank-8 ceiling with the ONNX default axis (-2)." },
|
| 1356 |
+
"attrs": { "inverse": 0, "onesided": 0 },
|
| 1357 |
+
"inputs": {
|
| 1358 |
+
"input": {
|
| 1359 |
+
"dtype": "float32",
|
| 1360 |
+
"shape": [1, 1, 1, 1, 1, 1, 2, 1],
|
| 1361 |
+
"data": { "kind": "values", "values": [1.0, 2.0] }
|
| 1362 |
+
}
|
| 1363 |
+
},
|
| 1364 |
+
"outputs": {
|
| 1365 |
+
"output": {
|
| 1366 |
+
"dtype": "float32",
|
| 1367 |
+
"shape": [1, 1, 1, 1, 1, 1, 2, 2],
|
| 1368 |
+
"tolerance": 0.00001,
|
| 1369 |
+
"data": { "kind": "values", "values": [3.0, 0.0, -1.0, 0.0] }
|
| 1370 |
+
}
|
| 1371 |
+
}
|
| 1372 |
+
}
|
| 1373 |
+
]
|
| 1374 |
+
}
|