Native GPU sampling: the same chromatic sweep the browser runs, on Vulkan, Metal or DX12.
Why this is a separate crate
ferrotherm is std-only with zero dependencies, and that is load-bearing rather than
decorative: it is what lets the same source compile to wasm32-unknown-unknown and to a
microcontroller. A GPU backend needs a driver stack. So it lives out here beside silicon,
serve and cloud, each of which exists for exactly the same reason.
Why it does not have its own shader
The WGSL comes from [ferrotherm::wgsl::sweep_shader] — the same string the browser fetches
through ft_shader. A second copy would be a second implementation of the update rule, and the
two would drift the first time one was tuned. The core crate already pins the sigmoid with a
test (the_shader_states_the_same_update_as_the_kernel); binding that same text here means a
native run and a browser run cannot disagree about the arithmetic, only about the hardware.
What it does not promise
Not bit-identical to the CPU sampler. The shader's RNG is a counter-based hash of
(step, node), chosen so a lane needs no state and the result does not depend on the order
lanes happen to execute in. The CPU sampler draws from its own stream. Both sample the same
distribution; neither reproduces the other's individual flips, and a test that asserted they did
would be asserting something false.
What they DO agree on is physics, and that is what [Gpu::sweep]'s tests check: the same
magnetisation at the same temperature, and a planted instance solved to its known optimum.
use ferrotherm::{ising::lattice2d, wgsl::GpuModel};
# fn main() -> Result<(), String> {
let g = lattice2d(8, 1.0);
let m = GpuModel::from_graph(&g);
let mut spins = vec![1i8; 64];
let gpu = ferrotherm_gpu::Gpu::new().ok_or("no adapter")?;
gpu.sweep(&m, &mut spins, 0.44, 100)?;
# Ok(()) }