# Example: Topological Compilation

This example demonstrates how the built-in compiler can be used to ensure that circuits obey the qudit connectivity of a system.

## Connectivity Graphs

Connectivity graphs for our system can be declared through Graph. We can instantiate custom connectivity arrangements or use pre-configured systems.

[2]:

import trueq as tq
import trueq.visualization as tqv

# create an instance of the Regetti Aspen-11 chip.
graph = tqv.Graph.aspen_11(show_labels=True)
graph.plot()


## Allocating Labels

We can begin by relabeling our circuit so that their labels match that of our connectivity graphs using AllocateLabels.

[3]:

circ = tq.Circuit(
[
{(0, 1): tq.Gate.cx},
{(0, 2): tq.Gate.random(4)},
{(0, 3): tq.Gate.cx},
{(0, 4): tq.Gate.random(4)},
]
)

alloc = tq.compilation.AllocateLabels(graph)
circ = alloc.apply(circ)
circ.draw()

[3]:


We see that labels 0, 1, 2, 3, 4 of our input circuit got respectively mapped to labels 26, 11, 25, 27, 10. AllocateLabels's remapping attempts to maximize the number of two-qudit gates that act on valid connections on the targeted graph. In the above example (26, 11), (26, 25), and (26, 27) are all connected on the graph. However, since no qudit in the Aspen-11 chip contains more than three connections, we cannot make this circuit obey the couplings through relabeling alone. Therefore the final CNOT gate of (26, 10) is not valid connection. To ensure that all gates obey the connectivity we need to also use DeferredSwapper.

## Deferred Swapper

The DeferredSwapper is used to compile circuits so that every gate obeys the topology of our graph. After relabeling the circuit to match that of our graph, the Deferred Swapper will insert swaps in order to ensure that all two-qudit gates are only between labels that can be coupled by the system.

[4]:

swapper = tq.compilation.DeferredSwapper(graph)
circ = swapper.apply(circ)
circ.draw()

[4]:


Here the Deferred Swapper inserted swaps before the final Cycle in order to make the two-qubit gate on (26, 10) valid. Additionally, this swapping algorithm will combine gates into swap sections where its optimal to do so. For example:

[5]:

circ = tq.Circuit([{(26, 10): tq.Gate.random(4)}, {(10, 26): tq.Gate.random(4)}])
circ = swapper.apply(circ)
circ.draw()

[5]:


Here both of the two-qudit gates get placed in the same opening and closing pair of swap gates.

## Clifford Decomposition

When the Deferred Swapper encounters a Clifford gate, it may perform a Clifford decomposition when it sees that it is optimal to do so.

[6]:

circ = tq.Circuit([{(26, 10): tq.Gate.cx}])
circ = swapper.apply(circ)
circ.draw()

[6]:


Here, the Deferred Swapper determines that tq.Gate.cx is a Clifford gate and chooses to decompose it as such. The output is a series of one- and two-qubit Cliffords gates that obey our graph topology.

Additionally, this Clifford decomposition algorithm can be ran directly by using decompose_clifford.