QubitArray ​
QubitArray[N] is a fixed-width, indexable qubit register. It's the right tool when you have a register of qubits that you want to address by index — addresses for QROM, oracle inputs, multi-control patterns — without exploding the register into N individual Qubit parameters.
When to Use Each Quantum Type ​
| Type | Use For |
|---|---|
Qubit | Single qubits passed individually |
List[Qubit] | Variable-length collections; cannot be indexed |
QInt[N] | Quantum integer arithmetic (+=, -=, qft, phi_add) |
QubitArray[N] | Bit-level register access (oracle inputs, address decoding) |
QubitArray and QInt are parallel types — convert between them with qint_from_qarray and qarray_from_qint when you need both arithmetic and bit-level access.
Construction ​
qa: QubitArray[4] = [0, 1, 0, 1] -- type-directed, length must matchThe list literal length must equal N. Each element must be 0 or 1 (or any Int expression — non-literals are validated at runtime).
Indexed Operations ​
qa: QubitArray[3] = [0, 0, 0]
qa[0] <- hadamard -- single-slot gate
qa[1] <- pauli_x
qa[0], qa[2] <- cnot -- two-slot gateThe whole register is consumed and rebuilt by every indexed rebind. The typechecker tracks qa as one linear value — there is no per-slot tracking.
Cross-register operations work the same way:
control: QubitArray[3] = [1, 0, 0]
target: QubitArray[3] = [0, 0, 0]
control[0], target[2] <- cnot -- consumes both, rebuilds bothExtraction ​
To pull individual qubits out of a register, use destructure:
qa: QubitArray[3] = [1, 0, 1]
[q0, q1, q2]: List[Qubit] = destructure(qa)
m0 = measure(q0)
m1 = measure(q1)
m2 = measure(q2)q = qa[0] is a type error. The only legal positions for qa[i] are the LHS of a rebind.
This restriction keeps linearity tracking simple — the whole register is one linear value, never decomposed implicitly.
Measurement ​
measure(qa) returns the integer value of the register, reconstructed LSB-first from per-qubit measurements:
qa: QubitArray[3] = [1, 0, 1]
result: Int = measure(qa) -- 1 + 0*2 + 1*4 = 5This is equivalent to converting through QInt (measure(qint_from_qarray(qa))) but skips the wrapping step. Endianness matches QInt's convention: bit i of the register contributes 2^i to the result.
Iteration ​
There is no special for q in qa syntax. Use the standard for-loop accumulator pattern with index-based access:
final = for i in range(0, 8) with state = qa
state[i] <- hadamard
end forEach iteration consumes and rebuilds state; the loop accumulator threads it through.
Conversion to/from QInt ​
QubitArray and QInt are parallel types. Conversion is a zero-cost view change:
qa: QubitArray[8] = [1, 0, 1, 0, 0, 0, 0, 0]
qi: QInt[8] = qint_from_qarray(qa)
qi += 5 -- arithmetic via QInt
qa: QubitArray[8] = qarray_from_qint(qi)
qa[7] <- pauli_x -- bit-level access via QubitArrayUse this pattern when an algorithm needs both — e.g., a Grover oracle that performs arithmetic on the input register.
Multi-Control Gates ​
mcnot and mcz generalize the Toffoli and doubly-controlled-Z gates to arbitrary control width.
fn toffoli_n(controls: QubitArray[4], target: Qubit) -> Tuple[QubitArray[4], Qubit] with Quantum
mcnot(controls, target)
end fnmcnot flips target if and only if every qubit in controls is |1⟩:
controls: QubitArray[3] = [1, 1, 1]
target: Qubit = 0
(controls, target) = mcnot(controls, target)
measure(target) -- 1 (flipped)mcz applies a Z phase to the last qubit, controlled on all preceding qubits:
controls: QubitArray[3] = [1, 1, 1] -- last qubit is the phase target
controls = mcz(controls)Both functions follow the standard linearity convention: controls and target are consumed and returned as fresh IDs. For N ≥ 3, the implementation uses an internal V-chain ancilla decomposition — no manual ancilla management needed.
Common Pitfalls ​
q = qa[i]is a type error. Usedestructure(qa)to extract.- Subarray slicing (
qa[0..3]) is not yet supported. Workaround:destructureand reassemble withqarray_from_qubits. - Width mismatch in conversions —
qint_from_qarrayof aQubitArray[3]returnsQInt[3], notQInt[N]for some other N. - Compile-time bounds checks:
qa[5]on aQubitArray[3]is a typecheck error when the index is a literal. - Duplicate indices:
qa[1], qa[1] <- cnotis rejected at compile time; runtime-equal dynamic indices (qa[i], qa[j] <- cnotwherei == jat runtime) are rejected at runtime.
When NOT to Use QubitArray ​
- Variable-width or runtime-determined width: use
List[Qubit]and explicit destructure. - Pure arithmetic: use
QInt[N]directly. - Single qubits: use
Qubit.
See Also ​
- Linear Types — Why qubits can't be copied
- Quantum Integers — The arithmetic-oriented register type
- Compute Blocks — Automatic uncomputation for ancillas