# 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.