Skip to content

Quantum Operations ​

All quantum operations require the Quantum effect to be declared on the function. Qubits are linear types and must be either measured or discarded.

Effect Declaration ​

All quantum operations must be used within a function that declares the Quantum effect:

kettle
fn my_quantum_function() -> Int with Quantum
  q: Qubit = 0
  -- quantum operations here
  measure(q)
end fn

The quantum function must be executed with a handler:

kettle
fn main() -> Unit using quantum_simulator, file_io
  result = my_quantum_function()
  print(result)
end fn

Qubit Creation ​

The idiomatic way to create qubits is with type-directed construction:

kettle
q: Qubit = 0              -- single qubit in |0>
q: Qubit = 1              -- single qubit in |1>
[a, b]: List[Qubit] = [0, 0]  -- two qubits in |0>

qubit ​

Signature: qubit() -> Qubit

Creates a single qubit initialized in the |0> state. Provided for backward compatibility and dynamic use cases; prefer q: Qubit = 0 in new code.

qubits ​

Signature: qubits(n: Int) -> List[Qubit]

Creates n qubits, all initialized in the |0> state. Useful when the count is determined at runtime; for a fixed count, prefer [q0, q1]: List[Qubit] = [0, 0].

bell ​

Signature: bell() -> Tuple[Qubit, Qubit]

Creates a Bell pair - two maximally entangled qubits. When measured, they always produce the same result (both 0 or both 1).

Single-Qubit Gates ​

hadamard ​

Signature: hadamard(q: Qubit) -> Qubit

The Hadamard gate creates equal superposition:

  • |0> becomes (|0> + |1>)/sqrt(2)
  • |1> becomes (|0> - |1>)/sqrt(2)

This is the most fundamental gate for creating quantum superposition.

pauli_x ​

Signature: pauli_x(q: Qubit) -> Qubit

The Pauli-X gate (NOT gate). Flips the qubit state:

  • |0> becomes |1>
  • |1> becomes |0>

pauli_y ​

Signature: pauli_y(q: Qubit) -> Qubit

The Pauli-Y gate. Applies both bit flip and phase flip:

  • |0> becomes i|1>
  • |1> becomes -i|0>

pauli_z ​

Signature: pauli_z(q: Qubit) -> Qubit

The Pauli-Z gate (phase flip). Leaves |0> unchanged but flips the phase of |1>:

  • |0> stays |0>
  • |1> becomes -|1>

t_gate ​

Signature: t_gate(q: Qubit) -> Qubit

The T gate applies a pi/4 phase rotation. Important for universal quantum computation.

s_gate ​

Signature: s_gate(q: Qubit) -> Qubit

The S gate applies a pi/2 phase rotation. Equivalent to two T gates.

rx ​

Signature: rx(theta: Float, q: Qubit) -> Qubit

Rotation around the X-axis by angle theta (in radians).

ry ​

Signature: ry(theta: Float, q: Qubit) -> Qubit

Rotation around the Y-axis by angle theta (in radians).

rz ​

Signature: rz(theta: Float, q: Qubit) -> Qubit

Rotation around the Z-axis by angle theta (in radians).

Multi-Qubit Gates ​

cnot ​

Signature: cnot(control: Qubit, target: Qubit) -> Tuple[Qubit, Qubit]

The Controlled-NOT gate. Flips the target qubit if the control qubit is |1>. This is the primary entangling gate.

ControlTargetResult
000, 0
010, 1
101, 1
111, 0

cz ​

Signature: cz(q1: Qubit, q2: Qubit) -> Tuple[Qubit, Qubit]

The Controlled-Z gate. Applies a phase flip when both qubits are |1>. Symmetric - works the same regardless of which qubit is considered control or target.

swap ​

Signature: swap(q1: Qubit, q2: Qubit) -> Tuple[Qubit, Qubit]

Exchanges the states of two qubits.

ccnot ​

Signature: ccnot(c1: Qubit, c2: Qubit, target: Qubit) -> Tuple[Qubit, Qubit, Qubit]

The Toffoli gate (Controlled-Controlled-NOT). Flips the target qubit only when both control qubits are |1>. This gate is universal for classical computation.

ccz ​

Signature: ccz(c1: Qubit, c2: Qubit, target: Qubit) -> Tuple[Qubit, Qubit, Qubit]

The Controlled-Controlled-Z gate. Applies a phase flip when all three qubits are |1>. Useful for phase-based algorithms like Grover's search.

cy ​

Signature: cy(control: Qubit, target: Qubit) -> Tuple[Qubit, Qubit]

The Controlled-Y gate. Applies a Pauli-Y gate to the target qubit when the control qubit is |1>.

ch ​

Signature: ch(control: Qubit, target: Qubit) -> Tuple[Qubit, Qubit]

The Controlled-Hadamard gate. Applies a Hadamard gate to the target qubit when the control qubit is |1>.

crx ​

Signature: crx(theta: Float, control: Qubit, target: Qubit) -> Tuple[Qubit, Qubit]

Controlled X-axis rotation. Applies an RX rotation to the target qubit when the control qubit is |1>.

cry ​

Signature: cry(theta: Float, control: Qubit, target: Qubit) -> Tuple[Qubit, Qubit]

Controlled Y-axis rotation. Applies an RY rotation to the target qubit when the control qubit is |1>. Commonly used in variational quantum algorithms.

crz ​

Signature: crz(theta: Float, control: Qubit, target: Qubit) -> Tuple[Qubit, Qubit]

Controlled Z-axis rotation. Applies an RZ rotation to the target qubit when the control qubit is |1>.

Measurement ​

measure ​

Signature: measure(q: Qubit) -> Int

Measures a qubit in the computational basis, collapsing its superposition. Returns 0 or 1. This consumes the qubit - it cannot be used again after measurement.

measure_all ​

Signature: measure_all(qs: List[Qubit]) -> List[Int]

Measures all qubits in a list, returning a list of 0s and 1s. Consumes all the qubits.

discard ​

Signature: discard(q: Qubit) -> Unit

Discards a linear value (like a qubit) without measuring it. Use this when you need to dispose of a qubit but don't care about its measurement result.

expect_z ​

Signature: expect_z(q: Qubit) -> Tuple[Expectation, Qubit]

Computes the Pauli-Z expectation value ⟨ψ|Z|ψ⟩ for a qubit. Returns an Expectation record containing:

  • value: The expectation value in [-1, 1]
  • std_error: The standard error of the estimate

Behavior by mode:

Modevaluestd_error
quantum_simulator(shots = N)Statistical estimatesqrt(variance / N)
quantum_simulator(exact = True)Exact from statevector0.0

Non-destructive: The qubit is returned and can be used for further operations.

This is essential for variational quantum algorithms (VQE, QAOA) that need to compute energy expectations without destroying the quantum state.

Classically Controlled Gates ​

cgate ​

Signature: cgate(classical: Int, gate: fn(Qubit) -> Qubit, q: Qubit) -> Qubit

Applies a gate conditionally based on a classical bit value. If classical is 1, the gate is applied; otherwise, the qubit passes through unchanged.

cond_gate ​

Signature: cond_gate(condition: Bool, gate: fn(Qubit) -> Qubit) -> fn(Qubit) -> Qubit

Creates a conditional gate function. Returns a gate that either applies the given gate (if condition is true) or acts as identity (if condition is false).

Gate Summary Table ​

GateQubitsSyntaxDescription
hadamard1q <- hadamardCreates superposition
pauli_x1q <- pauli_xBit flip (NOT)
pauli_y1q <- pauli_yBit and phase flip
pauli_z1q <- pauli_zPhase flip
t_gate1q <- t_gatepi/4 phase
s_gate1q <- s_gatepi/2 phase
rx(theta)1q <- rx(1.57)X-axis rotation
ry(theta)1q <- ry(1.57)Y-axis rotation
rz(theta)1q <- rz(1.57)Z-axis rotation
cnot2c, t <- cnotControlled-NOT
cz2a, b <- czControlled-Z
cy2c, t <- cyControlled-Y
ch2c, t <- chControlled-Hadamard
crx(theta)2(c, t) = crx(1.57, c, t)Controlled X-rotation
cry(theta)2(c, t) = cry(1.57, c, t)Controlled Y-rotation
crz(theta)2(c, t) = crz(1.57, c, t)Controlled Z-rotation
swap2a, b <- swapSwap states
ccnot3c1, c2, t <- ccnotToffoli (CCNOT)
ccz3(c1, c2, t) = ccz(c1, c2, t)Controlled-controlled-Z

QML Library Functions ​

The standard library includes functions for quantum machine learning, available via import stdlib/quantum.

hardware_efficient_layer ​

Signature: hardware_efficient_layer(qs: List[Qubit], params: List[Float]) -> List[Qubit] with Quantum

Applies a hardware-efficient ansatz layer: RX-RY-RZ rotations on each qubit followed by a linear CNOT entanglement chain. This is the standard variational form used in VQE and QML.

Parameters layout: [rx0, ry0, rz0, rx1, ry1, rz1, ...] (3 parameters per qubit).

kettle
[q0, q1, q2]: List[Qubit] = [0, 0, 0]
params = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
qs = hardware_efficient_layer([q0, q1, q2], params)

zz_feature_map_full ​

Signature: zz_feature_map_full(data: List[Float]) -> List[Qubit] with Quantum

Encodes classical data using angle encoding, then applies ZZ interactions between adjacent qubits with interaction strength proportional to x_i * x_{i+1}. Creates entanglement that captures correlations in the data, important for quantum advantage in kernel methods.

kettle
features = [0.3, 0.7, 0.5]
qs = zz_feature_map_full(features)

angle_encode ​

Signature: angle_encode(data: List[Float]) -> List[Qubit] with Quantum

Encodes a list of classical values into qubit states using RY rotations. Each feature value in [0, 1] is mapped to a rotation angle.

kettle
data = [0.25, 0.75, 0.5]
qs = angle_encode(data)  -- Creates 3 qubits with encoded data

QFT Operations ​

The Quantum Fourier Transform (QFT) operates on QInt registers, transforming between computational and Fourier bases. These operations enable efficient quantum arithmetic.

qft ​

Signature: qft(x: QInt) -> QInt

Applies the Quantum Fourier Transform, converting a QInt register from the computational basis to the Fourier basis. Use the rebind sugar: x <- qft.

iqft ​

Signature: iqft(x: QInt) -> QInt

Applies the Inverse Quantum Fourier Transform, converting back from the Fourier basis to the computational basis.

phi_add ​

Signature: phi_add(constant: Int, x: QInt) -> QInt

Adds a classical constant to a QInt register in Fourier space. This uses only O(n) phase gates instead of O(n²) for standard addition. The QInt must already be in Fourier space (after qft).

Linear Types and Qubits ​

Qubits are linear types in Kettle, meaning:

  1. No cloning: You cannot copy a qubit (enforced by quantum mechanics)
  2. Must consume: Every qubit must be either measured or discarded
  3. Single use: After measurement or discard, the qubit cannot be used again

This is enforced at compile time:

kettle
-- This won't compile!
fn bad() -> Unit with Quantum
  q: Qubit = 0
  measure(q)
  measure(q)  -- Error: q already consumed
end fn

See Also ​