Routing on a device

In many quantum computing architectures, not all qubits are directly connected, meaning that operations between distant qubits must be routed through intermediate qubits. This is accomplished by inserting SWAP operations between qubits in a circuit.

The SingleOperationSwapRouter Converter

pyqonvert provides the SingleOperationSwapRouter Converter that inserts SWAP operations into a quantum circuit or QuantumProgram to enable operations on devices with limited connectivity.

Initalizing the SingleOperationSwapRouter Class.

The SingleOperationSwapRouter class is imported from the pyqonvert.routing module.

Parameters
  • swap_back - if set to True, additional SWAP operations are inserted to retrieve the original layout.
  • only_before_decomposition - if set to True, SWAPS are only inserted outside of decomposition blocks. This option comes in handy during noise mapping which extensively makes use of decomposition blocks. Naively routing together with noise mapping requires a large number of SWAPs contributing to additional complexity. To minimize the number of SWAPs and allow for remapping without disrupting noise mapping, SWAPs are strategically inserted only before decomposition blocks that involve one or two qubits. To learn more about noise mapping, refer to this research article.

Note that when using setting only_before_decomposition=True, not all operations in the circuit (especially decomposition blocks involving more qubits in the first place) are routed. For full routing, a second pass with swap_back=True is necessary.

from pyqonvert.routing import SingleOperationSwapRouter

# initialize converter
noise_inserter = SingleOperationSwapRouter(
    swap_back=True,
    only_before_decomposition=False,
)


# Ready to use for routing.

Examples

Here is a simple example showing the functionality of the routing converter.

from pyqonvert.routing import SingleOperationSwapRouter
from qoqo import devices, Circuit
from qoqo import operations as ops

router = SingleOperationSwapRouter(True, False)

# Initialize device
number_qubits = 3
device = devices.GenericDevice(number_qubits)
device.set_two_qubit_gate_time("CNOT", 0, 1, 1.0)
device.set_two_qubit_gate_time("CNOT", 1, 0, 1.0)
device.set_two_qubit_gate_time("CNOT", 1, 2, 1.0)
device.set_two_qubit_gate_time("CNOT", 2, 1, 1.0)

# initialize circuit
circuit = Circuit()
circuit += ops.CNOT(0,2)
circuit += ops.PragmaRepeatedMeasurement("ro", 10)

# Routing
circuit = router.convert(circuit, device, [])

routing decomposition blocks Figure 1: SWAP gates insertions in a circuit for routing over a device of linear topology.

The following example shows how only_before_decomposition flag functions.

from pyqonvert.routing import SingleOperationSwapRouter
from qoqo import devices, Circuit
from qoqo import operations as ops

router = SingleOperationSwapRouter(False, True)

# Initialize device
number_qubits = 3
device = devices.GenericDevice(number_qubits)
device.set_two_qubit_gate_time("CNOT", 0, 1, 1.0)
device.set_two_qubit_gate_time("CNOT", 1, 0, 1.0)
device.set_two_qubit_gate_time("CNOT", 1, 2, 1.0)
device.set_two_qubit_gate_time("CNOT", 2, 1, 1.0)

# Initialize circuit
circuit = Circuit()
map = {0: 2, 2:0}
circuit += ops.PragmaStartDecompositionBlock([0, 2], map)
circuit += ops.CNOT(0, 2)
circuit += ops.CNOT(2, 0)
circuit += ops.PragmaStopDecompositionBlock([0, 2])
circuit += ops.PragmaStartDecompositionBlock([0, 1, 2], {})
circuit += ops.CNOT(1, 2)
circuit += ops.PragmaStopDecompositionBlock([0, 1, 2])

# Routing
circuit = router.convert(circuit, device, [])

routing decomposition blocks Figure 2: SWAP gates are added outside of the original decomposition blocks when only_before_decomposition = True.