ket.operations¶
Functions to manipulate quantum operations.
This module provides essential functions in the Ket library for manipulating quantum operations. It includes functions for controlled and inverse operations, facilitating quantum circuit construction.
Functions ket.operations¶
|
Convert a gate into a controlled version where the first argument is the control. |
|
Return the adjoint (inverse/Hermitian conjugate) of a gate. |
|
Apply a gate, execute a body block, then apply the gate's adjoint: \(UVU^\dagger\). |
|
Create a sequential composition (concatenation) of quantum gates. |
|
Context manager that conditions all operations in its body on a qubit state. |
|
Add control qubits to a gate. |
|
Capture a full quantum state snapshot of the given qubit registers. |
|
Calculate the expectation value of a Hamiltonian for the current quantum state. |
|
Context manager that reverses the order of all quantum operations in its body. |
|
Decorator factory for self-contained, reusable quantum kernel functions. |
|
Create a tensor-product (parallel) composition of quantum gates. |
|
Measure qubits in the computational basis and return a measurement handle. |
|
Sample the measurement outcomes of a quantum state over multiple shots. |
|
Apply a gate and schedule its adjoint for automatic uncomputation. |
|
Decorator factory that automatically allocates auxiliary qubits for a gate. |
- C(gate: Callable) Callable¶
Convert a gate into a controlled version where the first argument is the control.
This is a convenience decorator/wrapper that reorders the arguments of a gate so that the first positional argument becomes the control qubit(s). It is equivalent to calling
ctrl(control_qubits, gate)(*args).Example
from ket import * p = Process() c, a, b = p.alloc(3) CX = C(X) # CX is equivalent to CNOT CCNOT = C(C(X)) # Toffoli gate CX(c, a) # CNOT: c is control, a is target CCNOT(c, a, b) # Toffoli: c and a are controls, b is target
- Parameters:
gate – The quantum gate to make controlled.
- Returns:
A new callable where the first positional argument is used as the control qubit(s) for
gate.
- adj(gate: Callable[[Any], Any]) Callable[[Any], Any]¶
Return the adjoint (inverse/Hermitian conjugate) of a gate.
Creates a new callable that applies the time-reversed sequence of operations of the given
gate. For unitary gates, the adjoint is the inverse operation.The process is inferred automatically from the
Quantarguments. If noQuantis present in the arguments (e.g., when all parameters are classical), the keyword argumentket_processmust be supplied explicitly.- Usage:
from ket import * p = Process() a, b = p.alloc(2) # Prepare a Bell state and then un-prepare it bell = cat(kron(H, I), CNOT) bell(a, b) # entangle adj(bell)(a, b) # un-entangle: returns |00⟩
- Parameters:
gate – The quantum gate to invert.
- Returns:
A new callable that applies the adjoint of
gate. Accepts the same arguments asgateplus an optionalket_processkeyword argument.
- around(gate: Callable, *args, ket_process: Process | None = None, **kwargs)¶
Apply a gate, execute a body block, then apply the gate’s adjoint: \(UVU^\dagger\).
This context manager implements the conjugation pattern common in quantum algorithms. It applies the given gate \(U\), then runs the code inside the
withblock (\(V\)), and finally automatically appends \(U^\dagger\) (the adjoint of \(U\)). The resulting circuit fragment is \(UVU^\dagger\).This is particularly useful for changing the basis of an operation without manually writing the inverse:
Example
from ket import * p = Process() a, b = p.alloc(2) # CZ gate expressed as H-CNOT-H (change of basis) with around(H, b): CNOT(a, b) # CNOT in the Z basis becomes CZ in the X basis
Example with multi-qubit gate:
from ket import * p = Process() a, b = p.alloc(2) bell = cat(kron(H, I), CNOT) with around(bell, a, b): # Apply bell(a, b) = U X(a) # V is applied in the Bell basis # adj(bell)(a, b) = U† is applied on exit
- Parameters:
gate – The quantum gate \(U\) to apply before and invert after the body.
*args – Positional arguments forwarded to
gate.ket_process – Explicitly specify the quantum process. If
None, the process is inferred automatically from the qubit arguments.**kwargs – Additional keyword arguments forwarded to
gate.
- Raises:
RuntimeError – If the body block attempts an operation that violates uncomputation rules (e.g., writing to a blocked auxiliary qubit).
- cat(*gates) Callable[[Any], Any]¶
Create a sequential composition (concatenation) of quantum gates.
Returns a new callable that applies all the given
gatesone after the other to the same set of arguments. This is the quantum analogue of function composition when all gates operate on the same qubits.Gate application order matches the argument order:
cat(U, V)first appliesU, thenV(left-to-right).Example
from ket import * # A Z gate can be decomposed as H-X-H z_gate = cat(H, X, H) p = Process() q = p.alloc() z_gate(q) # equivalent to Z(q)
- Parameters:
*gates – Quantum gate callables to compose sequentially. Each gate receives the full argument list.
- Returns:
A single callable that applies all
gatesin order. If only one argument was passed to the composed callable, returns that argument; if multiple were passed, returns them as a tuple.
- control(control_qubits: Quant, state: int | list[int] | None = None)¶
Context manager that conditions all operations in its body on a qubit state.
Opens a controlled scope where every quantum operation applied inside is conditioned on the
control_qubitsbeing in the \(\left|1\right\rangle\) state (by default). All operations in the body are automatically wrapped in a multi-qubit controlled block.- Usage:
with control(control_qubits): # All operations here are applied only if control_qubits = |1⟩ ...
Example
from ket import * p = Process() c = p.alloc(2) a, b = p.alloc(2) # CNOT: flip `a` if c[0] = |1⟩ with control(c[0]): X(a) # Toffoli: flip `a` if both c[0] = c[1] = |1⟩ with control(c): X(a) # Fredkin (CSWAP): swap `a` and `b` if c[0] = |1⟩ with control(c[0]): SWAP(a, b)
- Parameters:
control_qubits – The qubit(s) that act as controls. All qubits must belong to the same process.
state – Deprecated. Bit pattern specifying the control state. Use
with control(q == state):instead. Defaults to \(\left|1\right\rangle\).
Deprecated since version 0.9.3: The
stateargument is deprecated. Replace::- with control(q, state=0b01):
…
with:
with control(q == 0b01): ...
- ctrl(control_qubits: Quant, gate: Callable, state: int | list[int] | None = None) Callable¶
Add control qubits to a gate.
Create a new callable that applies the given
gatewith the control qubits.- Usage:
from ket import * p = Process() c = p.alloc(2) a, b = q.alloc(2) # CNOT c[0] a ctrl(c[0], X)(a) # Toffoli c[0] c[1] a ctrl(c, X)(a) # CSWAP c[0] a b ctrl(c[0], SWAP)(a, b)
- Parameters:
control_qubits – The qubits to control the quantum operations.
gate – The gate to apply with the control qubits.
- Returns:
A new callable that applies the given gate with the control qubits.
- dump(*qubits: list[Quant]) QuantumState¶
Capture a full quantum state snapshot of the given qubit registers.
Returns a
QuantumStateobject containing the probability amplitudes of every basis state with non-zero amplitude. This operation is only supported by simulators and is not available on real quantum hardware.Example
from ket import * p = Process() q = p.alloc(2) CNOT(H(q[0]), q[1]) # Bell state state = dump(q) print(state.states) # {0: (0.707..+0j), 3: (0.707..+0j)} print(state.show()) # pretty printed state
- Parameters:
*qubits – One or more qubit registers to capture. Multiple registers are shown as separate ket labels.
- Returns:
A snapshot of the current quantum state, mapping basis state integers to their complex amplitudes.
- exp_value(hamiltonian: Hamiltonian | Pauli) ExpValue¶
Calculate the expectation value of a Hamiltonian for the current quantum state.
Registers an expectation-value computation for the given Hamiltonian or Pauli operator. In live execution mode the result is computed immediately; in batch mode it is deferred until execution.
The Hamiltonian should be constructed using the
obscontext manager and the Pauli gate functions (X,Y,Z).Example
from ket import * p = Process() q = p.alloc(2) CNOT(H(q[0]), q[1]) # Prepare |Bell⟩ with obs(): h = X(q[0]) * X(q[1]) # XX observable ev = exp_value(h) print(ev.get()) # ⟨Bell|XX|Bell⟩ = 1.0
- Parameters:
hamiltonian – The observable to evaluate.
- Returns:
A handle to the expectation value result. Access
valueor call.get()to retrieve the result as afloat.
- inverse(process: Process)¶
Context manager that reverses the order of all quantum operations in its body.
All gates applied inside the
with inverse(process):block are collected and then appended to the circuit in reverse order, with each individual gate also inverted (i.e., the adjoint of each gate). This implements the unitary inverse \(U^\dagger\) of the sequence \(U\).- Usage:
with inverse(process): # Operations here are appended in reversed, adjoint form ...
Example
from ket import * p = Process() q = p.alloc(2) H(q[0]) CNOT(q[0], q[1]) # Prepare Bell state with inverse(p): # Undo the Bell preparation CNOT(q[0], q[1]) H(q[0]) # q is now back to |00⟩
- Parameters:
process – The process to apply the inverse scope to.
- kernel(*p_args, **p_kwargs)¶
Decorator factory for self-contained, reusable quantum kernel functions.
Creates a two-level decorator that:
Accepts
Processconstructor arguments (*p_args,**p_kwargs).Returns an inner decorator that maps named parameters to qubit allocations and wraps a Python function as a standalone quantum kernel.
Each call to the resulting kernel function creates a fresh
Process, allocates the declared qubits, calls the function body, and returns its return value. This makes kernels ideal for benchmarking, unit testing, or defining reusable quantum subroutines that always start from a clean state.Example
from math import sqrt, pi from ket import * # A kernel that measures ⟨CHSH⟩ for a Bell state. @kernel(num_qubits=2, simulator="dense")(a=1, b=1) def bell_chsh(a, b): CNOT(H(a), b) with obs(): a0 = Z(a) a1 = X(a) b0 = -(X(b) + Z(b)) / sqrt(2) b1 = (X(b) - Z(b)) / sqrt(2) h = a0 * b0 + a0 * b1 + a1 * b0 - a1 * b1 return exp_value(h).get() print(bell_chsh()) # Approx. 2√2 ≈ 2.828
- Parameters:
- Returns:
A decorator that accepts
**names(parameter-name to qubit-count mappings) and returns a decorator that wraps a quantum function as a self-contained kernel.
- kron(*gates, n: int = 1) Callable[[Any], Any]¶
Create a tensor-product (parallel) composition of quantum gates.
Returns a new callable that applies each gate to the corresponding positional argument independently. Unlike
cat, which applies all gates to the same arguments,kronmaps each gate to a separate argument, mirroring the tensor product \(U_1 \otimes U_2 \otimes \cdots\).The optional
nparameter repeats the entire gate listntimes, which is useful for applying the same set of gates across multiple register pairs.Example
from ket import * p = Process() a, b, c, d = p.alloc(4) HX = kron(H, X) HX(a, b) # H on a, X on b # Apply H⊗H⊗H to three separate qubits at once HHH = kron(H, n=3) HHH(a, b, c) # H on each
- Parameters:
*gates – Quantum gate callables to apply in parallel. Gate
iis applied to argumenti.n – Repeat the full gate list
ntimes. Defaults to1.
- Returns:
A new callable that applies each gate to its corresponding argument and returns all results as a tuple.
- Raises:
ValueError – If the number of gates does not match the number of arguments supplied when the resulting callable is called.
- measure(qubits: Quant) Measurement¶
Measure qubits in the computational basis and return a measurement handle.
Schedules a measurement operation on the given
qubits.The measured integer represents the bit-string of the qubits in big-endian order: the first qubit in the register is the most significant bit.
Example
from ket import * p = Process() q = p.alloc(2) CNOT(H(q[0]), q[1]) # Bell state result = measure(q) print(result.value) # 0 or 3 (|00⟩ or |11⟩)
- Parameters:
qubits – The qubits to measure.
- Returns:
A handle to the measurement result. Access
valueto retrieve the outcome as an unsigned integer, orNoneif not yet available.
- sample(qubits: Quant, shots: int = 2048) Samples¶
Sample the measurement outcomes of a quantum state over multiple shots.
Runs the circuit
shotstimes (or simulates doing so) and returns the empirical outcome distribution as aSamplesobject. Unlikemeasure, which collapses the state to a single outcome,sampleaccumulates counts over many simulated shots.Example
from ket import * p = Process() q = p.alloc(2) CNOT(H(q[0]), q[1]) # Bell state results = sample(q, shots=4096) print(results.value) # e.g., {0: 2051, 3: 2045} print(results.probability) # normalized probabilities
- Parameters:
qubits – The qubits to sample.
shots – Number of measurement repetitions (shots). Defaults to
2048.
- Returns:
A handle to the sample result, mapping measurement outcome integers to their counts. Returns
Noneif the result is not yet available (batch mode).
- undo(gate: Callable[[Quant], Any], qubits: Quant) Quant¶
Apply a gate and schedule its adjoint for automatic uncomputation.
Applies the specified
gatetoqubitsimmediately and returns a newQuantwrapping the same qubits. When this returned object is garbage-collected or goes out of scope (or is used as a context manager and exits), the adjoint ofgateis automatically appended to the circuit, uncomputing the operation.This is the recommended pattern for managing temporary quantum states: apply a computation, use its result, and rely on automatic cleanup rather than manually calling
adj.Example
from ket import * p = Process() c, t = p.alloc(2) H(c) # Compute a CNOT result, then auto-uncompute on scope exit with undo(ctrl(c, X), t) as flipped: sample_result = sample(c + flipped) # adjoint CNOT is automatically applied here
- Parameters:
gate – The quantum gate or operation to apply. Must accept a
Quantas its argument.qubits – The target qubits for the gate.
- Returns:
A
Quantwrapping the same qubit indices. When this object is finalized, the adjoint ofgateis appended to the circuit.- Raises:
RuntimeError – If
gateattempts a non-permutation operation on an auxiliary qubit, which would violate uncomputation safety.
- using_aux(unsafe: bool = False, **names)¶
Decorator factory that automatically allocates auxiliary qubits for a gate.
Wraps a gate function so that one or more auxiliary (ancilla) qubit arguments are allocated automatically by the process, rather than requiring the caller to manage them. The allocated qubits are passed as keyword arguments to the wrapped function.
Each entry in
namesmaps an argument name to the number of auxiliary qubits to allocate. The count can be either a fixedintor aCallablethat accepts a subset of the gate’s other arguments (by name) and returns the desired qubit count.When
unsafe=True, the gate body is executed inside ablock_buildermarked as diagonal, which disables some safety checks. This is only appropriate for operations that are provably diagonal in the computational basis.Example
from ket import * @using_aux(a=lambda c: 0 if len(c) <= 2 else 1) def v_chain(c, t, a=None): # Multi-controlled X using a V-chain ancilla. if len(c) <= 2: ctrl(c, X)(t) else: with around(ctrl(c[:2], X), a): v_chain(a + c[2:], t) p = Process() c = p.alloc(4) # 4 control qubits t = p.alloc() # 1 target qubit v_chain(c=c, t=t) # ancilla allocated automatically
- Parameters:
unsafe – If
True, mark the operation as diagonal, skipping certain uncomputation safety checks. Defaults toFalse.**names – Keyword arguments mapping parameter names (
str) to the number of auxiliary qubits needed (int), or a callable that computes the count from other gate arguments.
- Returns:
A decorator that wraps a gate function with automatic auxiliary qubit allocation.