Transversal gates¶
Download Notebook - transversal.ipynb
transversal applies an operation across a quantum register, or between corresponding qubits in two registers. It keeps repeated gate application concise and makes the intended register wiring clear.
This is a higher-order function: you supply the gate itself, such as x or cx, as an argument. Run the cells in order to explore both forms.
from guppylang import guppy
from guppylang.std.builtins import array, output
from guppylang.std.quantum import collect_measurements, cx, measure_array, x
from guppyalgos.utils import transversal, qarray
n_qubits = 5
Apply a one-qubit gate¶
For a one-qubit operation \(U\), applying it to every qubit of an \(n\)-qubit register gives
The call transversal(x, qreg) applies X to each qubit. Starting from zero, every qubit becomes one:
The measured result below is therefore always 11111.
@guppy
def single_qubit_example() -> None:
qreg = qarray(n_qubits)
transversal(x, qreg)
output("bits", collect_measurements(measure_array(qreg)))
print(single_qubit_example.emulator(n_qubits).stabilizer_sim().run().collated_counts())
Apply a two-qubit gate¶
With two registers, transversal applies the gate to matching indices:
Both registers must have the same size.
For
cx, the first register supplies the controls and the second supplies the targets.Each control interacts with its matching target, rather than every target.
On computational-basis inputs, the action is
where \(\oplus\) is bitwise XOR. Here the controls are prepared as all ones, so the initially zero targets also become all ones.
@guppy
def two_qubit_example() -> None:
control_qreg = qarray(n_qubits)
target_qreg = qarray(n_qubits)
transversal(x, control_qreg)
transversal(cx, control_qreg, target_qreg)
output("controls", collect_measurements(measure_array(control_qreg)))
output("targets", collect_measurements(measure_array(target_qreg)))
print(two_qubit_example.emulator(2 * n_qubits).stabilizer_sim().run().collated_counts())
One name, six overloads¶
The library registers six implementations with @guppy.overload. At compile time, guppy resolves a call from the gate’s signature and the supplied arguments.
Scope |
One-qubit gate |
Two-qubit gate |
|---|---|---|
Every index |
|
|
Exclude indices |
|
|
Index range |
|
|
All indices are zero-based. The register arguments are borrowed, so they remain available for subsequent gates or measurement.
Exclude selected indices¶
The array argument contains indices to skip. For a five-qubit register, array(1, 3) applies the gate at indices 0, 2, and 4. For a two-qubit gate, it skips the corresponding pairs.
Apply only within a range¶
The range includes its start and excludes its end: 2, 4 applies only at indices 2 and 3. Keep the bounds within the register width.
The complete example below uses every overload in sequence. Each operation acts on the state left by the preceding operation.
@guppy
def main() -> None:
control_qreg = qarray(n_qubits)
target_qreg = qarray(n_qubits)
# Apply at every index.
transversal(x, control_qreg)
transversal(cx, control_qreg, target_qreg)
# Skip indices 1 and 3; swap control and target roles for this CX layer.
transversal(x, target_qreg, array(1, 3))
transversal(cx, target_qreg, control_qreg, array(1, 3))
# Apply only at indices 2 and 3.
transversal(x, control_qreg, 2, 4)
transversal(cx, control_qreg, target_qreg, 2, 4)
output("controls", collect_measurements(measure_array(control_qreg)))
output("targets", collect_measurements(measure_array(target_qreg)))
print(main.emulator(2 * n_qubits).stabilizer_sim().with_seed(2).run().collated_counts())
Read the result¶
All gates in this example act deterministically on computational-basis states. After the final layer:
The control qubits at indices 0, 1, and 4 are one; indices 2 and 3 are zero.
Target indices 1 and 3 are one; the other target qubits are zero.
The explicit output calls record the measured bitstrings for inspection. The stabilizer simulator is sufficient here because X and CX are Clifford gates.
Try replacing X with H in the first example to prepare a superposition. Applying CX from that register to a zero register then prepares one Bell pair per matching index.