Research-Stack/5-Applications/scripts/braid_fft.wgsl
Brandon Schneider ef7b0849d5 fix(adversarial-review): resolve 35 critical coding bugs across 8 subsystems
Security & correctness fixes from full adversarial review:

Lean (7 fixes):
- FixedPoint.lean: guard false theorem with n > 0 precondition
- QFactor.lean: remove double-scaling error in energy decrease
- AVMIsa/Step.lean: implement addSatQ16/subSatQ16 primitives
- BraidEigensolid.lean: fix crossStep second output argument swap
- SSMS.lean: complete ACI preservation proof (with rounding caveat)
- HouseholderQR.lean: add n > 0 precondition to spectral theorem

Verilog (7 fixes):
- q16_lut_core.v: fix multiply shift (16 → 32 bits)
- q16_lut_top.v: fix valid bit (0 → 1)
- cff_accelerator.v: fix SHA-256 padding (len < 448 check)
- research_stack_top.v: fix trigger aliasing (unique counters)
- Blitter6502OISC_small.v: fix address width (15 → 16 bits)
- spatial_hash_bram.v: add OOB write guard
- tmr_oepi_safety_fsm.v: fix double-increment race

WGSL (6 fixes):
- shaders.wgsl: atomicAdd for concurrent writes
- frustration_qubo.wgsl: double-buffer + CAS loop
- braid_fft.wgsl: workgroupBarrier synchronization
- burgers_scar_filter.wgsl: atomic E_bins array

Rust (9 fixes):
- thermodynamic.rs: Arc::from_raw → Arc::clone (double-free)
- thermodynamic.rs: Box::into_raw → Box (leak)
- tools/src/lib.rs: shell injection → shlex.quote
- ene-node/src/lib.rs: LRU caps, constant-time HMAC, peer caps

Python (6 fixes):
- similarity/__init__.py: pickle.load → RestrictedUnpickler
- AI-Feynman: torch.load → weights_only=True (14 calls)
- fetch_arxiv.py, fetch_s2.py: eval → ast.literal_eval
- topology.py: os.system → shutil.copy2
- SSH pipe: os.system → base64 pipe

Build: lake build 3572 jobs, 0 errors
2026-05-31 23:38:03 -05:00

259 lines
10 KiB
WebGPU Shading Language

// Copyright (c) 2026 Sovereign Research Stack. All rights reserved.
// Released under Apache 2.0 license as described in the file LICENSE.
// Authors: Research Stack Team
//
// braid_fft.wgsl — WebGPU compute shader for FFT on braid phase vectors
//
// Cooley-Tukey radix-2 FFT with bit-reversal permutation and Hann windowing.
// Input: array of PhaseVec (x, y as f32) in time domain
// Output: frequency domain representation
// Supported sizes: 64, 128, 256, 512, 1024
// ════════════════════════════════════════════════════════════
// §1 Data Types
// ════════════════════════════════════════════════════════════
struct PhaseVec {
x: f32,
y: f32,
}
struct FFTUniforms {
n: u32, // FFT size (must be power of 2)
log_n: u32, // log2(n)
window_enable: u32, // 1 = apply Hann window, 0 = skip
}
// ════════════════════════════════════════════════════════════
// §2 Buffers
// ════════════════════════════════════════════════════════════
@group(0) @binding(0) var<uniform> uniforms: FFTUniforms;
@group(0) @binding(1) var<storage, read> input_data: array<PhaseVec>;
@group(0) @binding(2) var<storage, read_write> output_data: array<PhaseVec>;
@group(0) @binding(3) var<storage, read_write> scratch: array<PhaseVec>; // ping-pong buffer
// ════════════════════════════════════════════════════════════
// §3 Complex Arithmetic Helpers
// ════════════════════════════════════════════════════════════
fn cmul(a: PhaseVec, b: PhaseVec) -> PhaseVec {
return PhaseVec(
a.x * b.x - a.y * b.y,
a.x * b.y + a.y * b.x
);
}
fn cadd(a: PhaseVec, b: PhaseVec) -> PhaseVec {
return PhaseVec(a.x + b.x, a.y + b.y);
}
fn csub(a: PhaseVec, b: PhaseVec) -> PhaseVec {
return PhaseVec(a.x - b.x, a.y - b.y);
}
// Twiddle factor: W_N^k = e^{-2πi·k/N}
fn twiddle(k: u32, n: u32) -> PhaseVec {
let angle = -2.0 * 3.14159265358979323846 * f32(k) / f32(n);
return PhaseVec(cos(angle), sin(angle));
}
// ════════════════════════════════════════════════════════════
// §4 Bit-Reversal Permutation
// ════════════════════════════════════════════════════════════
fn bit_reverse(val: u32, bits: u32) -> u32 {
var result: u32 = 0u;
var v = val;
for (var i: u32 = 0u; i < bits; i++) {
result = (result << 1u) | (v & 1u);
v = v >> 1u;
}
return result;
}
// ════════════════════════════════════════════════════════════
// §5 Hann Window
// ════════════════════════════════════════════════════════════
fn hann_window(index: u32, n: u32) -> f32 {
// w(n) = 0.5 * (1 - cos(2π·n/(N-1)))
return 0.5 * (1.0 - cos(2.0 * 3.14159265358979323846 * f32(index) / f32(n - 1u)));
}
// ════════════════════════════════════════════════════════════
// §6 Pass 1: Bit-reverse permutation + optional window
// Each thread handles one element
// ════════════════════════════════════════════════════════════
@compute @workgroup_size(64)
fn bitreverse_pass(@builtin(global_invocation_id) gid: vec3<u32>) {
let n = uniforms.n;
let log_n = uniforms.log_n;
let i = gid.x;
if (i >= n) { return; }
let j = bit_reverse(i, log_n);
var val = input_data[i];
// Apply Hann window if enabled
if (uniforms.window_enable == 1u) {
let w = hann_window(i, n);
val = PhaseVec(val.x * w, val.y * w);
}
scratch[j] = val;
}
// ════════════════════════════════════════════════════════════
// §7 Pass 2: Cooley-Tukey butterfly stages
// Each workgroup handles a block of butterflies
// Uses shared memory for the current stage
// ════════════════════════════════════════════════════════════
var<workgroup> shared_data: array<PhaseVec, 1024>; // max supported size
@compute @workgroup_size(64)
fn fft_stage(
@builtin(global_invocation_id) gid: vec3<u32>,
@builtin(local_invocation_id) lid: vec3<u32>,
@builtin(workgroup_id) wid: vec3<u32>
) {
let n = uniforms.n;
let stage = uniforms.log_n; // set per dispatch via push constant or separate uniform
// For each stage s = 1..log_n:
// butterfly_size = 2^s
// half = butterfly_size / 2
// For each butterfly group at position k:
// t = W_{butterfly_size}^j * data[k + half + j]
// data[k + half + j] = data[k + j] - t
// data[k + j] = data[k + j] + t
// This shader is dispatched once per stage, with 'stage' passed as uniform
let s = stage;
let butterfly_size = 1u << s;
let half = butterfly_size >> 1u;
// Each thread handles one butterfly element
let i = gid.x;
if (i >= n) { return; }
// Determine which butterfly group this thread belongs to
let group = i / butterfly_size;
let pos_in_group = i % butterfly_size;
// FIX: barrier ensures prior dispatch writes are visible before reads
workgroupBarrier();
// Read phase — all threads read before any writes to prevent read/write race
var result_even : PhaseVec;
var result_odd : PhaseVec;
if (pos_in_group < half) {
let k = group * butterfly_size + pos_in_group;
let j = pos_in_group;
let even = scratch[k];
let odd_val = scratch[k + half];
let w = twiddle(j, butterfly_size);
let t = cmul(w, odd_val);
result_even = cadd(even, t);
result_odd = csub(even, t);
}
// FIX: barrier ensures all reads complete before any thread writes
workgroupBarrier();
// Write phase — safe because all reads finished above
if (pos_in_group < half) {
let k = group * butterfly_size + pos_in_group;
scratch[k] = result_even;
scratch[k + half] = result_odd;
}
// FIX: barrier ensures all writes complete before next stage reads
workgroupBarrier();
}
// ════════════════════════════════════════════════════════════
// §8 Final copy to output
// ════════════════════════════════════════════════════════════
@compute @workgroup_size(64)
fn copy_to_output(@builtin(global_invocation_id) gid: vec3<u32>) {
let i = gid.x;
if (i >= uniforms.n) { return; }
output_data[i] = scratch[i];
}
// ════════════════════════════════════════════════════════════
// §9 Inverse FFT (IFFT) — conjugate-twiddle approach
// ════════════════════════════════════════════════════════════
fn twiddle_inverse(k: u32, n: u32) -> PhaseVec {
let angle = 2.0 * 3.14159265358979323846 * f32(k) / f32(n);
return PhaseVec(cos(angle), sin(angle));
}
@compute @workgroup_size(64)
fn ifft_stage(
@builtin(global_invocation_id) gid: vec3<u32>,
@builtin(local_invocation_id) lid: vec3<u32>,
@builtin(workgroup_id) wid: vec3<u32>
) {
let n = uniforms.n;
let stage = uniforms.log_n;
let s = stage;
let butterfly_size = 1u << s;
let half = butterfly_size >> 1u;
let i = gid.x;
if (i >= n) { return; }
let group = i / butterfly_size;
let pos_in_group = i % butterfly_size;
// FIX: barrier ensures prior dispatch writes are visible before reads
workgroupBarrier();
// Read phase — all threads read before any writes to prevent read/write race
var result_even : PhaseVec;
var result_odd : PhaseVec;
if (pos_in_group < half) {
let k = group * butterfly_size + pos_in_group;
let j = pos_in_group;
let even = scratch[k];
let odd_val = scratch[k + half];
let w = twiddle_inverse(j, butterfly_size);
let t = cmul(w, odd_val);
result_even = cadd(even, t);
result_odd = csub(even, t);
}
// FIX: barrier ensures all reads complete before any thread writes
workgroupBarrier();
// Write phase — safe because all reads finished above
if (pos_in_group < half) {
let k = group * butterfly_size + pos_in_group;
scratch[k] = result_even;
scratch[k + half] = result_odd;
}
// FIX: barrier ensures all writes complete before next stage reads
workgroupBarrier();
}
@compute @workgroup_size(64)
fn normalize_output(@builtin(global_invocation_id) gid: vec3<u32>) {
let i = gid.x;
let n = uniforms.n;
if (i >= n) { return; }
let inv_n = 1.0 / f32(n);
let val = scratch[i];
output_data[i] = PhaseVec(val.x * inv_n, val.y * inv_n);
}