# Extension points: methods, passes, noise channels and formats The engine plans, runs, compiles and converts through one registry (`engine::registry`). Each simulation method, compiler pass, noise channel and program format in it is a component behind a trait, and every built-in is registered through the same trait a host's own implements. A host that embeds the engine as a Rust library registers its own beside them; they run where the built-ins run, and the engine checks what they give. ```sh runnable wai-quantum components # every component here, its version and digest wai-quantum components --json # with each one's descriptor ``` The command line, both MCP servers (`list_components`) and the job service (`GET /v1`, `quantum_service_info`) list the built-ins. None of them loads a component from a file, a path or a request: registration happens in the process of the host that embeds the engine, in Rust. ## The four traits | Trait | It declares | The engine calls | The engine checks | |---|---|---|---| | `Method` | its name, version, accuracy classes, what circuits and answers it takes (its descriptor), whether it models noise, where `auto` tries it | `plan`: admit the job (or refuse it) and estimate its memory and work; `run`: carry the plan out | a job with noise is refused for a method that models none; every channel the job names must be one it takes; its result must carry an accuracy class it declared and leave the members the engine writes as they are | | `Pass` | its name, version, the gates it takes and gives, and how its result is checked: by the equivalence ladder or by a distance bound (`Check::Ladder`, `Check::Fidelity`) | `run`, after the level's own passes and before twirling and decoupling, when a compile's `passes` names it | its gates on the program's qubits and reading back; its output held to its input on the whole circuit by the check it declares (never by a hash); a distance within its bound and within what is left of the compile's ε budget | | `Channel` | its name, version, parameters with their ranges, the qubits it acts on, its Kraus operators and, where it is one, its Pauli form | `kraus` and `pauli` at a job's values; `taken_by`, for how each noisy method takes it | its operators completely positive and trace preserving (their sum of `K†K` within 1e-9 of the identity) at every job's values, and its Pauli form a distribution whose channel is its operators' | | `FrontEnd` | its name, version, whether it is binary, its file extensions and profiles | `read` (a program, as OpenQASM 3) and `write` | what it reads must read as OpenQASM 3; a conversion holds what it writes to the program by the equivalence ladder, as for the built-in formats | A registered pass is checked by the engine on the whole circuit. A built-in pass checks itself where it acts, as its record in the compile certificate says, and the tests hold every built-in to the gates it declares it gives. ## How a built-in is identified, and a registered one Every component has an identity: its kind, its name, its version and the BLAKE3 digest of its descriptor in canonical JSON (keys sorted, no whitespace), and whether it is built in. A built-in's version is the engine's. A result made by built-ins alone is the result it always was, byte for byte: it names its method, its compile's passes, its noise's channels and the engine's version, and the receipt seals them. The digests for that version are the ones `wai-quantum components` lists. A result that a registered component took part in also carries `components`: the identity of every component that made it (its program's format, its compile's passes, its noise channels, its method), each registered one marked `"origin": "registered"` and `"certified": false`. The receipt seals it with the rest of the result, so a registered component cannot be passed off as a built-in, and a name a built-in holds is never registered. ## Writing one A noise channel, registered and run: `X` with probability `p` after each gate, which the density matrix takes through its Kraus operators. ```rust runnable features=quantum_engine use wai_quantum::engine::registry::{Channel, Component, Param, Registry, Value, json}; use wai_quantum::gates::C; struct BitFlip; impl Channel for BitFlip { fn name(&self) -> &str { "bit_flip" } fn version(&self) -> &str { "1.0.0" } fn title(&self) -> &str { "bit flip" } fn descriptor(&self) -> Value { json!({ "does": "X with probability p after each gate" }) } fn params(&self) -> Vec { vec![Param::new("p", 0.0, 1.0, "a probability")] } fn arity(&self) -> u32 { 1 } fn kraus(&self, v: &[f64]) -> Vec> { let (a, b) = ((1.0 - v[0]).sqrt(), v[0].sqrt()); let c = |re| C { re, im: 0.0 }; vec![vec![c(a), c(0.0), c(0.0), c(a)], vec![c(0.0), c(b), c(b), c(0.0)]] } fn pauli(&self, v: &[f64]) -> Option> { Some(vec![(1.0 - v[0], "I".into()), (v[0], "X".into())]) } } fn main() { let mut r = Registry::with_builtins(); let id = r.register(Component::channel(BitFlip)).expect("registers"); let job = json!({ "qasm": "OPENQASM 3.0;\nqubit q;\nx q;\n", "observables": ["Z0"], "noise": { "bit_flip": 0.1 } }); let (plan, result) = r.plan_and_run(&job, 1 << 30).expect("runs"); assert_eq!(plan.method, "density_matrix"); let z = result["expectations"][0]["value"].as_f64().unwrap(); assert!((z - (-0.8)).abs() < 1e-12, "⟨Z⟩ = −1 + 2p"); assert_eq!(result["components"][1]["digest"], json!(id.digest)); println!("{}", result["components"]); } ``` `examples/extension_points.rs` has one of each, a method (`basis_table`, for circuits that permute basis states), a pass (`cancel_pairs`), a channel (`bit_flip`) and a format (`gate_list`), written against the public traits alone, registered, and run end to end with their tests: ```sh norun=ci:wai-quantum-registry.yml cargo test --locked --release --features quantum_engine,quantum_receipt --example extension_points cargo run --locked --release --features quantum_engine,quantum_receipt --example extension_points ``` ## What is refused, and how | Code | When | |---|---| | `J060` | a compile's `passes` names a pass this host does not hold | | `J061` | a noise channel the method cannot take: a method that models no noise, amplitude damping on the stabilizer's Pauli frames, a registered channel on a method that takes the built-in channels alone | | `J062` | a channel whose operators fail the validity check at the job's values | | `J063` | a registration the registry does not take: a name its kind holds already (a built-in's among them), a name or version that is not plain text, a descriptor that is not a JSON object, a channel on other than one or two qubits, a pass whose fidelity check states no finite bound | | `J064` | a registered component's answer that breaks the contract: a method's result without a class it declared, or one that writes a member the engine writes; a pass's gates off the program's qubits or that do not read back | | `J065` | an intent on a job whose noise names a registered channel: the intent checks model the built-in channels alone | A method named for a job it cannot take is refused with `J011`, as a built-in is, and `auto` passes over it to the next. [job-refusals.md](job-refusals.md) has every code. ## Determinism Components are kept in the order they are registered, the built-ins first in a fixed order, and every listing and every choice follows that order: `auto` tries methods by their rank (the built-ins hold 10, 20, 30, …, in the order they have always been tried), equal ranks in the order of registration. A registered component's results are the same bytes for the same job, as the built-ins' are, when the component itself is deterministic. Device-target jobs, sets and sweeps run on the built-in components.