All checks were successful
wai-quantum CUDA member / member · CUDA (jetson-hub) (pull_request) Successful in 16s
wai-quantum OpenCL member / member · OpenCL GPU and CPU (jetson-hub) (pull_request) Successful in 37s
wai-quantum WebGPU member / member · discrete GPU, Vulkan (jetson-hub) (pull_request) Successful in 12m24s
spec citations resolve / Code cites no section its SPEC lacks (pull_request) Successful in 5s
standards workspaces build + test / arl-rs (pull_request) Successful in 1m12s
standards workspaces build + test / eoc-rs (pull_request) Successful in 2m4s
standards workspaces build + test / jcp-rs (pull_request) Successful in 1m38s
standards workspaces build + test / joule-ui-rs (pull_request) Successful in 1m29s
standards workspaces build + test / joulehook-rs (pull_request) Successful in 1m19s
standards workspaces build + test / proof-rs (pull_request) Successful in 1m59s
standards workspaces build + test / sandbox-rs (pull_request) Successful in 2m39s
standards workspaces build + test / sense-rs (pull_request) Successful in 1m8s
standards workspaces build + test / jouledesk-rs (pull_request) Successful in 1m27s
standards workspaces build + test / smart-byte-rs (pull_request) Successful in 4m13s
lockfiles / every Cargo.lock is current (pull_request) Successful in 32s
no-vanity-metrics / Scan committed Markdown for vanity metrics (pull_request) Successful in 4s
no-vanity-metrics / WAI public text names no comparison (pull_request) Successful in 4s
no-vanity-metrics / WAI public text names no company or product (pull_request) Successful in 4s
no-vanity-metrics / No new orphan source files (pull_request) Successful in 6s
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Successful in 2m17s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 14s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 6m31s
standards workspaces build + test / jouleclaw-rs (pull_request) Successful in 3m44s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 9m40s
standards workspaces build + test / joule-code-rs (pull_request) Successful in 1m37s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 9s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 28s
standards workspaces build + test / aac-rs (pull_request) Successful in 2m58s
standards workspaces build + test / joulecontract-rs (pull_request) Successful in 58s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 50s
standards workspaces build + test / map-rs (pull_request) Successful in 58s
standards workspaces build + test / mesh-rs (pull_request) Successful in 59s
tests in CI / every test file is run by a job or listed with its reason (pull_request) Successful in 5s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 2m7s
WAI HTTP delivery / WAI HTTP · does this change touch it (pull_request) Successful in 5s
WAI HTTP delivery / HTTP corpus · falsify mutations 0/3 (pull_request) Has been skipped
WAI HTTP delivery / HTTP corpus · falsify mutations 1/3 (pull_request) Has been skipped
WAI HTTP delivery / HTTP corpus · falsify mutations 2/3 (pull_request) Has been skipped
WAI HTTP delivery / HTTP corpus · falsify patches 0/3 (pull_request) Has been skipped
WAI HTTP delivery / HTTP corpus · falsify patches 1/3 (pull_request) Has been skipped
WAI HTTP delivery / HTTP corpus · falsify patches 2/3 (pull_request) Has been skipped
WAI HTTP delivery / HTTP corpus · every patch and mutation in exactly one shard (pull_request) Has been skipped
WAI HTTP delivery / HTTP corpus · verified both ways, and the differential (pull_request) Has been skipped
WAI HTTP delivery / cache rules · controls and labelled caches (pull_request) Has been skipped
standards workspaces build + test / openpay (pull_request) Successful in 3m0s
WAI JPEG-AI whole decode (pinned inputs, required) / fetch pinned JPEG-AI inputs → whole decode + pixel hash, required (pull_request) Successful in 1m25s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 26s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 7m48s
WAI neural sink gates / model-free refusals of the neural sink (pull_request) Successful in 1m15s
wai-pack / wai-pack · does this change touch it (pull_request) Successful in 5s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 21s
wai-quantum CUDA member / member · CUDA absent (linux) (pull_request) Successful in 20s
wai-quantum FPGA member / member · FPGA simulation absent (linux) (pull_request) Successful in 17s
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Successful in 43s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 2m10s
workflow hygiene / every workflow supersedes its own queue (pull_request) Successful in 5s
wai-quantum OpenCL member / member · OpenCL absent (linux) (pull_request) Successful in 20s
wai-quantum-qpu / clients against their contracts (linux) (pull_request) Successful in 19s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 1m42s
wai-quantum FPGA member / member · FPGA datapath, simulated (macos) (pull_request) Successful in 1m11s
wai-quantum WebGPU member / shader checks · browser build · wasip2 inertness · MSRV (pull_request) Successful in 2m43s
wai-quantum OpenCL member / member · OpenCL GPU (macos) (pull_request) Successful in 21s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 9m10s
wai-quantum WebGPU member / member · software Vulkan (linux) (pull_request) Successful in 1m57s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 17m29s
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Successful in 5s
wai-quantum WebGPU member / member · Metal (macos) (pull_request) Successful in 6m8s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 3m22s
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Successful in 5m25s
wai-quantum WebGPU member / member · browser WebGPU (macos) (pull_request) Successful in 3m46s
wai-pack / wai-pack · linux (pull_request) Successful in 1m40s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 33s
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 2m23s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 1m34s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 4m47s
wai-pack / wai-pack · macos (pull_request) Successful in 3m30s
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Successful in 2m57s
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Successful in 5m3s
Breaking: Certificate gains a public field, provenance; a silicon certificate hashes exactly as before, and every pinned digest is unchanged. quantum_fabric now includes quantum_toolchain. - Provenance (silicon / emulator / simulator) inside the certificate hash; placement ranks silicon first. - Sampling members: the sample kernels, the Sampler trait, statistical certification on the qpu-v1 corpus, a virtual QPU, and a virtual device that compiles each circuit for its coupling map and native basis. - quantum_toolchain::device: OpenQASM 2/3 in standard gates only, multi- controlled gates lowered exactly, every qubit measured; counts read back. quantum_toolchain::native: rz/sx/x/cz, prx/cz and gpi/gpi2/zz (turns), routed over a coupling map and verified against the circuit by simulation. from_qasm reads ch. - The OpenCL member (quantum_fabric_opencl), bit-exact on GPUs and CPUs. - The FPGA member, simulated (quantum_fabric_fpga): a Verilog datapath compiled by Verilator at run time; certificates say simulator. - The dense f64 state vector on accelerators (quantum_fabric::sv64): the CPU's own launches with no fused multiply-add, on CUDA and on OpenCL devices with binary64, certified against a pinned digest. - wai-quantum-qpu 0.1.0: clients for three quantum processing protocols, the wai-qpu command, curl transport with secrets off command lines. - CI: OpenCL, FPGA and QPU-client workflows; the CUDA and OpenCL hardware jobs certify the f64 state vector; the hardware host declares its devices. Co-Authored-By: Claude Opus 5.5 <[email protected]>
196 lines
8.4 KiB
Rust
196 lines
8.4 KiB
Rust
//! A native-gate circuit service in turns: the gates run as written.
|
|
//!
|
|
//! - **Auth**: `Authorization: apiKey <key>`.
|
|
//! - **Device**: `GET /backends/{backend}`: its `qubits`. Every pair of a
|
|
//! trapped-ion register interacts, so the map is complete and routing never
|
|
//! inserts a `SWAP`.
|
|
//! - **Submit**: `POST /jobs` of the circuit job type with the `native`
|
|
//! gateset (`gpi`, `gpi2`, `zz`), phases and angles in turns, so every dyadic
|
|
//! angle crosses the wire exactly; debiasing off, so the job runs the
|
|
//! program as compiled. On the simulator, `noise.model` picks a device's
|
|
//! noise.
|
|
//! - **Results**: `GET /jobs/{id}/results/probabilities`: probabilities keyed by
|
|
//! decimal integers whose bit `p` is physical qubit `p`, read back onto
|
|
//! logical qubits through the program's final layout, and made counts by
|
|
//! largest remainders.
|
|
//!
|
|
//! The service reports an energy estimate per job (`stats.kwh`); it is kept as
|
|
//! the service's figure ([`JobInfo::service_kwh`]).
|
|
|
|
use crate::transport::{Request, Transport};
|
|
use crate::{Device, JobInfo, JobStatus, Provider, QpuError, Secret, counts_from_probabilities, field, field_str, json_ok, logical_index};
|
|
use serde_json::{Value, json};
|
|
use wai_quantum::quantum_fabric::Provenance;
|
|
use wai_quantum::quantum_fabric::sampling::Counts;
|
|
use wai_quantum::quantum_toolchain::native::{Basis, Coupling, Dyadic, NativeGate, NativeProgram};
|
|
|
|
/// The service's API base.
|
|
pub const BASE: &str = "https://api.ionq.co/v0.4"; // allow-name: the service's endpoint
|
|
/// The job type of a gate-list circuit.
|
|
pub const JOB_TYPE: &str = "ionq.circuit.v1"; // allow-name: a protocol job type
|
|
|
|
/// A client for one backend: `"simulator"` or a processor such as
|
|
/// a processor's backend name.
|
|
pub struct NativeTurns<T: Transport> {
|
|
transport: T,
|
|
key: Secret,
|
|
backend: String,
|
|
noise: Option<String>,
|
|
base: String,
|
|
}
|
|
|
|
impl<T: Transport> NativeTurns<T> {
|
|
pub fn new(transport: T, key: Secret, backend: impl Into<String>) -> Self {
|
|
NativeTurns { transport, key, backend: backend.into(), noise: None, base: BASE.into() }
|
|
}
|
|
|
|
/// From `WAI_QPU_TURNS_KEY` and `WAI_QPU_TURNS_BACKEND` (default
|
|
/// `simulator`), with `WAI_QPU_TURNS_NOISE` as the simulator's noise model
|
|
/// when set.
|
|
pub fn from_env(transport: T) -> Result<Self, QpuError> {
|
|
let backend = std::env::var("WAI_QPU_TURNS_BACKEND").ok().filter(|b| !b.is_empty()).unwrap_or_else(|| "simulator".into());
|
|
let mut c = NativeTurns::new(transport, Secret::from_env("WAI_QPU_TURNS_KEY")?, backend);
|
|
c.noise = std::env::var("WAI_QPU_TURNS_NOISE").ok().filter(|n| !n.is_empty());
|
|
Ok(c)
|
|
}
|
|
|
|
/// The simulator with a device's noise model, by the name the service
|
|
/// gives it.
|
|
pub fn with_noise(mut self, model: impl Into<String>) -> Self {
|
|
self.noise = Some(model.into());
|
|
self
|
|
}
|
|
|
|
/// Another endpoint: a test server.
|
|
pub fn with_base(mut self, base: impl Into<String>) -> Self {
|
|
self.base = base.into();
|
|
self
|
|
}
|
|
|
|
fn authed(&self, r: Request) -> Request {
|
|
r.header("Authorization", format!("apiKey {}", self.key.expose())).header("Accept", "application/json")
|
|
}
|
|
|
|
fn is_simulator(&self) -> bool {
|
|
self.backend == "simulator"
|
|
}
|
|
}
|
|
|
|
/// A dyadic angle in turns, `m/2^k`: exact in binary64.
|
|
pub fn turns(d: Dyadic) -> f64 {
|
|
let (m, k) = d.turns();
|
|
m as f64 / (1u64 << k) as f64
|
|
}
|
|
|
|
fn complete(n: u16) -> Coupling {
|
|
let mut pairs = Vec::new();
|
|
for a in 0..n {
|
|
for b in a + 1..n {
|
|
pairs.push((a, b));
|
|
}
|
|
}
|
|
Coupling::new(n, &pairs).expect("a complete map is valid")
|
|
}
|
|
|
|
impl<T: Transport> Provider for NativeTurns<T> {
|
|
fn device(&self) -> Result<Device, QpuError> {
|
|
let r = self.transport.send(&self.authed(Request::get(format!("{}/backends/{}", self.base, self.backend))))?;
|
|
let v = json_ok(&r)?;
|
|
let n = field(&v, &["qubits"])?.as_u64().filter(|&n| n > 0 && n <= 64).ok_or_else(|| QpuError::Protocol("qubits".into()))?;
|
|
let provenance = if self.is_simulator() {
|
|
Provenance::Simulator(format!("native-turns simulator, noise {}", self.noise.as_deref().unwrap_or("ideal")))
|
|
} else {
|
|
Provenance::Silicon
|
|
};
|
|
Ok(Device { name: format!("native-turns:{}", self.backend), basis: Basis::GpiZz, coupling: complete(n as u16), provenance })
|
|
}
|
|
|
|
fn submit(&self, program: &NativeProgram, shots: u64) -> Result<String, QpuError> {
|
|
if program.basis != Basis::GpiZz {
|
|
return Err(QpuError::Config("this service runs gpi, gpi2 and zz".into()));
|
|
}
|
|
let mut circuit: Vec<Value> = Vec::with_capacity(program.gates.len());
|
|
let mut width = program.final_layout.iter().copied().max().map_or(0, |q| q + 1);
|
|
for g in &program.gates {
|
|
circuit.push(match *g {
|
|
NativeGate::Gpi(q, p) => {
|
|
width = width.max(q + 1);
|
|
json!({"gate": "gpi", "target": q, "phase": turns(p)})
|
|
}
|
|
NativeGate::Gpi2(q, p) => {
|
|
width = width.max(q + 1);
|
|
json!({"gate": "gpi2", "target": q, "phase": turns(p)})
|
|
}
|
|
NativeGate::Zz(a, b, t) => {
|
|
width = width.max(a.max(b) + 1);
|
|
json!({"gate": "zz", "targets": [a, b], "angle": turns(t)})
|
|
}
|
|
other => return Err(QpuError::Config(format!("{other:?} is not one of this service's native gates"))),
|
|
});
|
|
}
|
|
let mut body = json!({
|
|
"type": JOB_TYPE,
|
|
"backend": self.backend,
|
|
"shots": shots,
|
|
"settings": {"error_mitigation": {"debiasing": false}},
|
|
"input": {"gateset": "native", "qubits": width, "circuit": circuit}
|
|
});
|
|
if self.is_simulator()
|
|
&& let Some(model) = &self.noise
|
|
{
|
|
body["noise"] = json!({"model": model});
|
|
}
|
|
let r = self.transport.send(&self.authed(Request::post(format!("{}/jobs", self.base), body.to_string().into_bytes()).header("Content-Type", "application/json")))?;
|
|
Ok(field_str(&json_ok(&r)?, &["id"])?.to_string())
|
|
}
|
|
|
|
fn status(&self, id: &str) -> Result<JobStatus, QpuError> {
|
|
let r = self.transport.send(&self.authed(Request::get(format!("{}/jobs/{id}", self.base))))?;
|
|
let v = json_ok(&r)?;
|
|
Ok(match field_str(&v, &["status"])? {
|
|
"submitted" | "ready" | "started" => JobStatus::Pending,
|
|
"completed" => JobStatus::Done,
|
|
other => JobStatus::Ended { status: other.to_string(), reason: v.get("failure").map(|f| f.to_string()).unwrap_or_default() },
|
|
})
|
|
}
|
|
|
|
fn counts(&self, id: &str, program: &NativeProgram, shots: u64) -> Result<(Counts, JobInfo), QpuError> {
|
|
let job = json_ok(&self.transport.send(&self.authed(Request::get(format!("{}/jobs/{id}", self.base))))?)?;
|
|
let service_kwh = job.get("stats").and_then(|s| s.get("kwh")).and_then(|k| k.as_f64());
|
|
let r = self.transport.send(&self.authed(Request::get(format!("{}/jobs/{id}/results/probabilities", self.base))))?;
|
|
let v = json_ok(&r)?;
|
|
let map = v.as_object().ok_or_else(|| QpuError::Protocol("probabilities is not a map".into()))?;
|
|
let mut p = vec![0.0f64; 1 << program.n_qubits];
|
|
for (key, prob) in map {
|
|
let physical: u64 = key.parse().map_err(|_| QpuError::Counts(format!("outcome {key:?} is not a decimal integer")))?;
|
|
let prob = prob.as_f64().filter(|x| x.is_finite() && *x >= 0.0).ok_or_else(|| QpuError::Counts(format!("probability of {key:?}")))?;
|
|
p[logical_index(physical, &program.final_layout)] += prob;
|
|
}
|
|
Ok((counts_from_probabilities(&p, shots), JobInfo { service_kwh }))
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn turns_are_exact_binary_fractions() {
|
|
let d = |m, k| Dyadic::new(m, k).unwrap();
|
|
assert_eq!(turns(Dyadic::ZERO), 0.0);
|
|
assert_eq!(turns(d(1, 2)), 0.25);
|
|
assert_eq!(turns(d(3, 2)), 0.75);
|
|
assert_eq!(turns(d(1, 32)), 1.0 / 4294967296.0);
|
|
assert_eq!(json!(turns(d(5, 4))).to_string(), "0.3125");
|
|
}
|
|
|
|
#[test]
|
|
fn every_pair_of_the_register_interacts() {
|
|
let c = complete(4);
|
|
for a in 0..4 {
|
|
for b in 0..4 {
|
|
assert_eq!(c.connected(a, b), a != b);
|
|
}
|
|
}
|
|
}
|
|
}
|