wai-quantum 0.6.0

A deterministic quantum stack in pure Rust: byte-exact circuit simulation (statevector / stabilizer / tensor-network MPS / sparse-Pauli backends), sparse Pauli dynamics at utility scale (arbitrary angles, 1024 qubits), belief-propagation tensor networks on the hardware graph, error mitigation, qLDPC decoding, noise learning, circuit-equivalence proofs, a phasor interference-ML layer, information-theoretic limits, noisy channels and state tomography, and signed energy-accounted receipts. No QPU, no cloud, no system libraries in the default features — identical results native, in the browser, and as a WASI component at the edge; the opt-in WebGPU member reaches the platform's GPU interface.
Documentation
//! The gate kernel for HIP devices, and what names a HIP member. The kernel is
//! the CUDA kernel's text, [`cuda::SOURCE`](super::cuda::SOURCE), unchanged:
//! everything it uses (`int2`, `atomicMin`, the launch indices, 64-bit
//! integers) means the same under HIP, and HIP's runtime compiler builds it for
//! each device's own architecture. So the argument that it is bit-exact is the
//! CUDA module's, and the mutants a conforming device must be caught running
//! are the same edits of the same text ([`MUTANTS`]). What differs is the
//! compiler, its options and the device, and the implementation id and the
//! member id bind them. No device is touched here, so all of it is testable on
//! any host; the HIP member itself is `quantum_fabric_hip`.

use super::FabricClass;
pub use super::cuda::{MUTANTS, mutate};

/// The kernel, exactly as it is compiled: the CUDA kernel's text.
pub const SOURCE: &str = super::cuda::SOURCE;

/// The implementation name the id starts with.
pub const IMPLEMENTATION: &str = "hip-lanes64";

/// Version of the host-side layout: interleaved `int2` lanes, one launch per
/// gate with the gate's words as kernel parameters, the report word (the
/// lowest out-of-range op index, `u32::MAX` for none), the probe's `int2`
/// cases and `uint4` outputs, and the launch shape ([`BLOCK`],
/// [`GRID_MAX`]). Bumped on any change to them; it is part of the
/// implementation id.
pub const HOST_LAYOUT: u32 = 1;

/// Threads per block.
pub const BLOCK: u32 = 256;

/// Blocks per launch, at most; the kernel strides over the rest.
pub const GRID_MAX: u32 = 4096;

/// The options the runtime compiler is given, besides the target
/// architecture, which is the device's own: no contraction of a multiply and
/// an add into one fused operation, though the kernel has no floating point
/// for it to touch.
pub const COMPILE_OPTIONS: [&str; 2] = ["-std=c++17", "-ffp-contract=off"];

const DOMAIN_IMPLEMENTATION: &[u8] = b"wai:quantum-fabric-hip\x01";
const DOMAIN_MEMBER: &[u8] = b"wai:quantum-fabric-hip-member\x01";

fn hex(bytes: &[u8]) -> String {
    bytes.iter().map(|b| format!("{b:02x}")).collect()
}

/// A length-prefixed byte string, so that no field reads as part of another.
fn put(o: &mut Vec<u8>, bytes: &[u8]) {
    o.extend_from_slice(&(bytes.len() as u64).to_be_bytes());
    o.extend_from_slice(bytes);
}

/// `"hip-lanes64/<16 hex>"`: the first 8 bytes of
/// BLAKE3(`"wai:quantum-fabric-hip\x01"` ‖ put([`SOURCE`]) ‖ put of each of
/// [`COMPILE_OPTIONS`] ‖ [`HOST_LAYOUT`] as a big-endian `u32`), in hex. Any
/// edit to the kernel's text, its compile options or the host layout changes
/// it, and it differs from the CUDA implementation's id over the same text.
/// The target architecture is the device's and is part of the member id
/// instead.
pub fn implementation_id() -> String {
    implementation_id_of(SOURCE)
}

/// [`implementation_id`] for another kernel text, such as a mutant's.
pub fn implementation_id_of(source: &str) -> String {
    let mut o = DOMAIN_IMPLEMENTATION.to_vec();
    put(&mut o, source.as_bytes());
    for option in COMPILE_OPTIONS {
        put(&mut o, option.as_bytes());
    }
    o.extend_from_slice(&HOST_LAYOUT.to_be_bytes());
    format!("{IMPLEMENTATION}/{}", hex(&blake3::hash(&o).as_bytes()[..8]))
}

/// What a HIP member is, as its id hashes it. The member fills it from the
/// runtime; the fields are plain values here so that the id is testable
/// without a device.
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct DeviceIdentity {
    pub name: String,
    /// The device's UUID, in hex.
    pub uuid: String,
    /// The architecture as the runtime names it, with its feature settings
    /// (`"gfx1151"`, `"gfx90a:sramecc+:xnack-"`): what the kernel is compiled
    /// for.
    pub arch: String,
    /// `"<domain>:<bus>:<device>"`, in hex.
    pub pci: String,
    /// The runtime's version, as `hipRuntimeGetVersion` reports it.
    pub runtime: u32,
    /// The runtime compiler's version, `(major, minor)`.
    pub compiler: (u32, u32),
    /// Compute units.
    pub compute_units: u32,
    /// The device's memory, bytes.
    pub memory: u64,
}

impl DeviceIdentity {
    /// Always [`FabricClass::Gpu`].
    pub fn class(&self) -> FabricClass {
        FabricClass::Gpu
    }

    /// The architecture without its feature settings: `"gfx90a"` for
    /// `"gfx90a:sramecc+:xnack-"`.
    pub fn arch_name(&self) -> &str {
        self.arch.split(':').next().unwrap_or("")
    }

    /// The option that compiles the kernel for this device:
    /// `"--offload-arch=<arch>"`, the feature settings included.
    pub fn arch_option(&self) -> String {
        format!("--offload-arch={}", self.arch)
    }

    /// `"local/gpu-hip-<d12>"`. `d12` is the first 12 hex digits of
    /// BLAKE3(`"wai:quantum-fabric-hip-member\x01"` ‖ put(name) ‖ put(uuid) ‖
    /// put(arch) ‖ put(pci) ‖ runtime ‖ compiler major ‖ compiler minor ‖
    /// compute units ‖ memory), the numbers big-endian. It sorts after every
    /// `local/cpu-<arch>`, so with no energy figures
    /// [`place`](super::place) keeps the CPU reference.
    pub fn member_id(&self) -> String {
        let mut o = DOMAIN_MEMBER.to_vec();
        put(&mut o, self.name.as_bytes());
        put(&mut o, self.uuid.as_bytes());
        put(&mut o, self.arch.as_bytes());
        put(&mut o, self.pci.as_bytes());
        o.extend_from_slice(&self.runtime.to_be_bytes());
        o.extend_from_slice(&self.compiler.0.to_be_bytes());
        o.extend_from_slice(&self.compiler.1.to_be_bytes());
        o.extend_from_slice(&self.compute_units.to_be_bytes());
        o.extend_from_slice(&self.memory.to_be_bytes());
        format!("local/gpu-hip-{}", hex(&blake3::hash(&o).as_bytes()[..6]))
    }

    /// The key a runner declares in CI: `"gpu-hip-<arch name>"`, for instance
    /// `"gpu-hip-gfx1151"`.
    pub fn key(&self) -> String {
        format!("gpu-hip-{}", self.arch_name())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    fn identity() -> DeviceIdentity {
        DeviceIdentity {
            name: "a device".into(),
            uuid: "00112233445566778899aabbccddeeff".into(),
            arch: "gfx1151".into(),
            pci: "0000:c2:00".into(),
            runtime: 70_152_801,
            compiler: (9, 0),
            compute_units: 20,
            memory: 64 << 30,
        }
    }

    #[test]
    fn every_field_of_the_identity_is_in_the_member_id() {
        let base = identity();
        let id = base.member_id();
        assert!(id.starts_with("local/gpu-hip-") && id.len() == "local/gpu-hip-".len() + 12, "{id}");
        assert!(id.as_str() > "local/cpu-x86_64" && id.as_str() > "local/cpu-aarch64", "sorts after every CPU reference");
        let variants = [
            DeviceIdentity { name: "another".into(), ..identity() },
            DeviceIdentity { uuid: "ff".into(), ..identity() },
            DeviceIdentity { arch: "gfx1100".into(), ..identity() },
            DeviceIdentity { arch: "gfx1151:xnack-".into(), ..identity() },
            DeviceIdentity { pci: "0000:c3:00".into(), ..identity() },
            DeviceIdentity { runtime: 60_000_000, ..identity() },
            DeviceIdentity { compiler: (10, 0), ..identity() },
            DeviceIdentity { compiler: (9, 1), ..identity() },
            DeviceIdentity { compute_units: 40, ..identity() },
            DeviceIdentity { memory: 8 << 30, ..identity() },
        ];
        for v in variants {
            assert_ne!(v.member_id(), id, "{v:?}");
        }
        assert_eq!(base.key(), "gpu-hip-gfx1151");
        assert_eq!(base.arch_option(), "--offload-arch=gfx1151");
        assert_eq!(base.class(), FabricClass::Gpu);
        let featured = DeviceIdentity { arch: "gfx90a:sramecc+:xnack-".into(), ..identity() };
        assert_eq!((featured.arch_name(), featured.key().as_str()), ("gfx90a", "gpu-hip-gfx90a"));
        assert_eq!(featured.arch_option(), "--offload-arch=gfx90a:sramecc+:xnack-");
    }

    #[test]
    fn the_implementation_id_follows_the_kernel_text_and_is_not_the_cuda_one() {
        let id = implementation_id();
        assert!(id.starts_with("hip-lanes64/") && id.len() == "hip-lanes64/".len() + 16, "{id}");
        assert_eq!(SOURCE, super::super::cuda::SOURCE);
        assert_ne!(id.split('/').nth(1), super::super::cuda::implementation_id().split('/').nth(1));
        for m in &MUTANTS {
            assert_ne!(implementation_id_of(&mutate(m)), id, "mutant {}", m.name);
        }
    }
}