Arbitrary Angle SU(4) Gates

A general \(SU(4)\) entangler gate is available on the H-Series device. This gate is available as Optype.TK2 via TKET. This article showcases the workflow to prepare and submit a circuit with the \(SU(4)\) gate with a Quantum Volume Test (QVT) use case.

Native gates are gates on a quantum computer that the hardware physically executes. Different quantum computers may have different gates that are physically executed on the hardware. Writing a gate in a quantum circuit submitted to hardware doesn’t guarantee its physical execution on the device. For instance, on H-Series quantum computers, a Hadamard gate written in the circuit is not the actual gate executed. When users submit circuits using a Hadamard gate, the gate is translated into a \(U1q\) gate followed by a \(Rz\) gate, which the ion trap device physically executes. For a listing of the H-Series hardware native gates, see the following links:

The H-Series hardware compiler handles the translation from circuits users submit to the native gates run on hardware. In the H-Series Quantum Charge-Coupled Device (QCCD) architecture, the hardware compilation includes the assignment of which physical qubit corresponds to which qubit in a circuit as well as how qubits will be transported around the device. Since transport, as well as gating, incurs a small amount of error with each operation, the H-Series compiler aims to minimize the number of gates that need to be executed.

compilation stack

H-Series Hardware Compilation

H-Series Hardware Native Gates

On the Quantinuum H-Series devices there are different native 2-qubit gates available. The default native 2-qubit gates are an arbitrary-angle 2-qubit gate, \(Rzz(\theta)\), or a fully entangling 2-qubit gate, \(ZZ()\). Note that \(ZZ()\) is equal to, \(Rzz(\frac{\pi}{2})\). In TKET, these are OpType.ZZPhase and OpType.ZZMax, respectively. An additional native gate is available, the General \(SU(4)\) Entangler gate, \(Rxxyyzz(\alpha, \beta\, \gamma)\), which is available as OpType.TK2 within TKET.

By default, TKET and the hardware compiler compile to \(Rzz(\theta)\) or \(ZZ()\). Only one native gate can be specified at a time. This ensures everything aligns in the global operations of the circuit.

To compile to the general \(SU(4)\) entangler, a Quantinuum backend compilation configuration or a call to the set_compilation_config_target_2qb_gate method needs to have this set as the desired native gate to use.

  1. QuantinuumBackendCompilationConfig: The native_2qb_gate argument needs to be set to OpType.TK2 to enable use of the desired gate.

  2. QuantinuumBackend.set_compilation_config_target_2qb_gate: The native_2qb_gate argument needs to be set to the desired gate.

The General \(SU(4)\) Entangler Gate

The General \(SU(4)\) Entangler gate is available in TKET as OpType.TK2. This gate is a combination of OpType.XXPhase, OpType.YYPhase and OpType.ZZPhase, and requires three angles as input, \(\alpha\), \(\beta\) and \(\gamma\). The definition of the gate is provided below:

\[\begin{equation} \textrm{TK2}(\alpha, \beta, \gamma) = e^{-\frac{1}{2} i \pi \alpha (\hat{X} \bigotimes \hat{X})} \quad e^{-\frac{1}{2} i \pi \beta (\hat{Y} \bigotimes \hat{Y})} \quad e^{-\frac{1}{2} \pi \gamma (\hat{Z} \bigotimes \hat{Z})} = e^{-\frac{1}{2} i \alpha (\hat{X} \bigotimes \hat{X}) -\frac{1}{2} i \pi \beta (\hat{Y} \bigotimes \hat{Y}) -\frac{1}{2} i \pi \gamma (\hat{Z} \bigotimes \hat{Z})} \end{equation} \]

This gate can be used as follows within TKET.

from pytket.circuit.display import render_circuit_jupyter
from pytket.circuit import Circuit
from sympy import Symbol

symbols = [Symbol("a"), Symbol("b"), Symbol("c")]
circuit = Circuit(2)
circuit.TK2(*symbols, *circuit.qubits)
render_circuit_jupyter(circuit)

This circuit can be converted to a QASM string using the circuit_to_qasm_str function and by specifying the Quantinuum header hqslib1.

from pytket.qasm.qasm import circuit_to_qasm_str

print(circuit_to_qasm_str(circuit, header="hqslib1"))
OPENQASM 2.0;
include "hqslib1.inc";

qreg q[2];
Rxxyyzz((a)*pi,(b)*pi,(c)*pi) q[0],q[1];

Gate Compilation Optimizations

Users have the option of submitting circuits using whichever quantum gate set they desire. Users do not need to think about which physical gates will be executed or how physical qubits will move around the device since the hardware compiler manages this. In certain cases, however, users may want to know that the circuit they submit is going to be run on the device exactly as they write it. For example, when running benchmarking circuits users may want circuits to be executed exactly as specified in the circuit even if its not the most optimal in total number of 2-qubit gates.

There are two ways to think about using these options:

  1. Circuits written in any gate set

  2. Circuits written in the hardware’s native gate set

Options for submitting circuits are summarized in the figure.

H-Series Stack Compiler Options

Circuits written in any gate set

Users are free to submit circuits written with any gate set, not just the native gate set of the hardware. In this case, TKET will rebase the circuit to the native gate set it believes is most optimal and the hardware compiler will handle further optimizations of gate combinations as it applies to transport and ion assignment. We recommend this for the majority of use cases.

  • get_compiled_circuit: The TKET optimization level to apply with optimisation_level (default: 2). With TKET optimizations turned on, the hardware compiler will provide further gate combination logic as makes sense for ions and transport

    • 2: powerful optimizations, compilation can be time-consuming

    • 1: basic optimization, compiles quickly

    • 0: rebase the circuit with TKET

Circuits written in the hardware’s native gate set

For circuits that are written using only gates in the hardware’s native gate set, users may still choose to apply the TKET optimizations specified above to their circuit even if the circuit is written in the native gate set since TKET may find further reductions in the number of quantum gates. This can be explored using get_compiled_circuit.

Users may also not want to utilize TKET optimizations and submit their circuit as is using the native gates exactly as specified in the circuit. If this is desired, the get_compiled_circuit function can be skipped. The process_circuit function will detect that the circuit submitted is written with the native gate set and run the circuit as written.

The workflow for submitting circuits using \(Rzz(\theta)\) or \(ZZ()\) utilize all but the purple steps below. To use the general \(SU(4)\) entangler, either route can be chosen, utilizing the purpose steps. The get_compiled_circuit function is optimal depending if the user would like to apply TKET optimizations or not.

TKET Job Submission Workflow

Quantum Volume Test

Now we illustrate the above compilation options using the Quantum Volume Test.

Quantum volume is a benchmarking test that was initially proposed by IBM (arXiv:1811.12926). It is a test that aims to verify the quality as well as the quantity of qubits on the machine. The test does this by performing rounds of single and two qubit gates between random pairs of qubits for as many rounds as qubits in the test. For example, for quantum volume of \(2^N\) where \(N=4\), there are 4 layers of repeated operations over 4 qubits. It verifies the quantum computer can perform quality computation with reasonable-sized circuits. The advantage to using quantum volume is that it gives users the confidence that not only do they have the number of qubits to support running their circuit, but the 2-qubit gate fidelity meets the threshold to support circuits of significant depth as well. Quantum algorithms need not only qubits, but the ability to run many gates. Quantinuum has steadily been increasing the quantum volume of H-Series machines.

First we import the functions we need in TKET.

import numpy as np

from pytket import Circuit, OpType
from pytket.circuit.display import render_circuit_jupyter

from pytket.extensions.quantinuum import QuantinuumBackend
from pytket.extensions.quantinuum.backends.quantinuum import (
    QuantinuumBackendCompilationConfig,
)

Decomposing a random \(SU(4)\) unitary into a circuit primitive

To set up the Quantum Volume test, we start by building up the repeated circuit elements. The function in the code cell below defines a TKET Circuit Box with pytket.circuit.CircBox. Circuit Boxes are useful for composing larger circuits from smaller subcircuits that utilize the same set of gates.

The Circuit box below contains a blueprint for the decomposition of a random general \(SU(4)\) unitary distributed with the Haar Measure into a circuit primitive over 2 qubits. The implementation is based on arxiv.0609050. The random \(SU(4)\) unitary is generated using scipy.stats.unitary_group.

from pytket.circuit import CircBox, Circuit, Unitary2qBox
import numpy as np
from scipy.stats import unitary_group


def haar_random_su4_box() -> CircBox:
    r = unitary_group.rvs(dim=4)
    r[0, :] /= np.linalg.det(r)
    circuit = Circuit(2)
    box = Unitary2qBox(r)
    circuit.add_unitary2qbox(box, 0, 1)
    return CircBox(circuit)

Circuit Boxes can be visualized utilizing the get_circuit function.

from pytket.circuit.display import render_circuit_jupyter

circuit = haar_random_su4_box().get_circuit()
render_circuit_jupyter(circuit)

In the cell below, the CircBox is inspected and further optimizations are applied. DecomposeBoxes simplifies OpType.CircBox operations into the underlying gate operations recursively. Further decompositions are performed with the KAKDecomposition function. We specify use the OpType.TK2 gate in this function call as the target 2-qubit gate after decomposition.

The pytket sequence pass, consists of two passes:

Both passes are arguments to pytket.passes.SequencePass. SequencePass allows both passes to be applied to the input circuit with one apply call.

from pytket.passes import DecomposeBoxes, KAKDecomposition, SequencePass
from pytket.circuit import OpType

sequence_pass = SequencePass(
    [DecomposeBoxes(), KAKDecomposition(target_2qb_gate=OpType.TK2)]
)
sequence_pass.apply(circuit)
render_circuit_jupyter(circuit)

Building the Quantum Volume Test Circuit

Now we’re ready to set up a Quantum Volume test circuit using the Circuit Box we created. The steps to set up the circuit are straightforward using the CircBox instances to build up the full circuit.

circuit = Circuit(4)
for _ in range(4):
    permutation = np.random.permutation(circuit.qubits)
    for i in range(0, 4, 2):
        box = haar_random_su4_box()
        circuit.add_circbox(box, [permutation[i], permutation[i + 1]])
circuit.measure_all()
[CircBox q[0], q[3]; CircBox q[2], q[1]; CircBox q[0], q[3]; CircBox q[1], q[2]; CircBox q[0], q[3]; CircBox q[1], q[2]; CircBox q[0], q[1]; CircBox q[2], q[3]; Measure q[0] --> c[0]; Measure q[1] --> c[1]; Measure q[2] --> c[2]; Measure q[3] --> c[3]; ]

The SequencePass defined in the code cell in the previous subsection is used below on the QVT circuit and we can see the full set of gates in the circuit as well as the result of optimizations performed by KAKDecomposition.

sequence_pass.apply(circuit)
from pytket.circuit.display import render_circuit_jupyter

render_circuit_jupyter(circuit)

Define Native 2-Qubit Gate to Use With QuantinuumBackend

Now that the circuit has been set up, we will demonstrate submission of the circuit for H-Series backends using both the \(SU(4)\) (OpType.TK2) gate and \(Rzz\) (OpType.ZZPhase) as the target 2-qubit gate.

General \(SU(4)\) Entangler

To run a circuit containing the general \(SU(4)\) gate on H-Series devices, the compilation configuration for the QuantinuumBackend needs to have OpType.TK2 set as the native 2-qubit gate. This is done using QuantinuumBackendCompilationConfig and specifying the compilation configuration when initializing QuantinuumBackend. By setting target_2qb_gate, local TKET compilation and submission for costing and processing to H-Series with OpType.TK2 is enabled.

from pytket.extensions.quantinuum import QuantinuumBackend
from pytket.extensions.quantinuum.backends.quantinuum import (
    QuantinuumBackendCompilationConfig,
)

from pytket.circuit import OpType

compilation_config = QuantinuumBackendCompilationConfig(
    allow_implicit_swaps=True, target_2qb_gate=OpType.TK2
)

quantinuum_backend = QuantinuumBackend(
    device_name="H1-1E", compilation_config=compilation_config
)
quantinuum_backend.login()

We can check the target 2-qubit gate.

quantinuum_backend.compilation_config.target_2qb_gate
<OpType.TK2: 41>

We can also look at the native gate set of the pytket backend.

quantinuum_backend.backend_info.gate_set
{<OpType.Barrier: 8>,
 <OpType.WASM: 14>,
 <OpType.SetBits: 15>,
 <OpType.CopyBits: 16>,
 <OpType.RangePredicate: 17>,
 <OpType.ExplicitPredicate: 18>,
 <OpType.ExplicitModifier: 19>,
 <OpType.MultiBit: 20>,
 <OpType.Rz: 36>,
 <OpType.TK2: 41>,
 <OpType.Measure: 63>,
 <OpType.Reset: 65>,
 <OpType.PhasedX: 68>,
 <OpType.ZZMax: 70>,
 <OpType.ZZPhase: 73>,
 <OpType.ClassicalExpBox: 103>}
qv_circuit_wSU4 = quantinuum_backend.get_compiled_circuit(circuit, optimisation_level=2)
qv_circuit_wSU4.name = "QV Example with SU(4)"
print(f"Number of SU(4) Gates: {qv_circuit_wSU4.n_2qb_gates()}")
render_circuit_jupyter(qv_circuit_wSU4)
Number of SU(4) Gates: 4
cost = quantinuum_backend.cost(qv_circuit_wSU4, n_shots=100, syntax_checker="H1-1SC")
print(f"SU(4) circuit cost: {cost} HQC")
SU(4) circuit cost: 6.84 HQC
handle = quantinuum_backend.process_circuit(qv_circuit_wSU4, n_shots=100)
result = quantinuum_backend.get_result(handle)
print(result.get_distribution())
{(0, 0, 0, 0): 0.09, (0, 0, 0, 1): 0.03, (0, 0, 1, 0): 0.07, (0, 0, 1, 1): 0.06, (0, 1, 0, 0): 0.1, (0, 1, 0, 1): 0.02, (0, 1, 1, 0): 0.04, (0, 1, 1, 1): 0.03, (1, 0, 0, 0): 0.33, (1, 0, 1, 0): 0.02, (1, 0, 1, 1): 0.07, (1, 1, 0, 0): 0.06, (1, 1, 1, 0): 0.06, (1, 1, 1, 1): 0.02}

Arbitrary Angle ZZ Gate

The default native 2-qubit gate can also be changed using set_compilation_config_target_2qb_gate. In the example below, we change to using a different native 2-qubit gate, OpType.ZZPhase and display the compiled circuit after TKET compilation.

quantinuum_backend.set_compilation_config_target_2qb_gate(OpType.ZZPhase)
qv_circuit_no_SU4 = quantinuum_backend.get_compiled_circuit(
    circuit, optimisation_level=2
)
qv_circuit_no_SU4.name = "QV Example without SU(4)"

print(f"Number of ZZPhase Gates: {qv_circuit_no_SU4.n_2qb_gates()}")
render_circuit_jupyter(qv_circuit_no_SU4)
Number of ZZPhase Gates: 12
result = quantinuum_backend.run_circuit(qv_circuit_no_SU4, n_shots=100)
print(result.get_distribution())
{(0, 0, 0, 0): 0.17, (0, 0, 0, 1): 0.01, (0, 0, 1, 0): 0.04, (0, 0, 1, 1): 0.04, (0, 1, 0, 0): 0.09, (0, 1, 0, 1): 0.04, (0, 1, 1, 0): 0.04, (0, 1, 1, 1): 0.04, (1, 0, 0, 0): 0.31, (1, 0, 0, 1): 0.02, (1, 0, 1, 1): 0.08, (1, 1, 0, 0): 0.03, (1, 1, 0, 1): 0.03, (1, 1, 1, 0): 0.06}

Summary

pytket-quantinuum enables you to change the default native 2-qubit gate for H-Series devices and emulators.

The Quantum Volume Test is one use case where the \(SU(4)\) gate can be employed. The QuantinuumBackend can be used to compile circuits to a gateset that uses the \(SU(4)\) gate as well as to submit those circuits for processing and costing.