Download

Download this file as Jupyter notebook: rc_kak.ipynb.

# Example: Randomly Compiling Arbitrary Multi-qudit Gates

This example shows how to use the `RCKak`

compiler pass
to randomly compile circuits with non-Clifford multi-qudit gates.
Randomizing and correction cycles can be applied around hard cycles in the circuit
without the need to first convert all entangling gates into Clifford operations.

## Generating randomly compiled circuits

Consider some circuit with alternating easy and hard cycles, where entangling gates are considered to be hard, and single-qubit gates are considered to be easy:

```
[2]:
```

```
import trueq as tq
import numpy as np
import trueq.compilation as tqc
from trueq.config import fsim_factory
from trueq import CircuitCollection
cycle1 = {1: tq.Gate.h, 4: tq.Gate.h}
cycle2 = {(0, 1): tq.Gate.random(4)}
cycle3 = {1: tq.Gate.t, 3: tq.Gate.random(2)}
cycle4 = tq.Cycle({(1, 3): fsim_factory(11, 13)})
circuit = tq.Circuit([cycle1, cycle2, cycle3, cycle4] * 2)
circuit += cycle1
circuit.measure_all()
circuit.draw()
```

```
[2]:
```

This circuit contains non-Clifford entangling gates which are not handled
by the `randomly_compile()`

protocol by default. To randomly
compile this circuit, one approach is to first decompose the circuit into
a logically equivalent circuit using a specified Clifford entangler:

```
[3]:
```

```
rc_circuits = tq.randomly_compile(circuit, entangler=tq.Gate.cx)
rc_circuits[0].draw()
```

```
[3]:
```

which increases the circuit depth (see Example: Randomized Compilation with different Compilation Options).

An alterative approach is to use custom randomizing operations for each gate in the circuit. One-qudit gates in marked cycles are twirled by the one-qudit Pauli group. For two-qubit gates, each randomizing operation is sampled uniformly from the set of local operations such that the corresponding correction cycle may also be applied locally. Any multi-qudit or larger multi-qubit gates are randomized with local operations found via empirical decomposition.

This variant of randomized compiling can implemented using the
`RCKak`

compiler pass:

```
[4]:
```

```
def rc_kak(circuit, n_compilations=30, local=True):
circuits = CircuitCollection(circuit.__copy__() for _ in range(n_compilations))
passes = [
tqc.MarkCycles(), # mark any cycles with two-qubit gates
tqc.RCKak(local=local), # apply randomizing cycles around marked cycles
tqc.Merge(),
tqc.RemoveEmptyCycle(),
]
return tqc.Compiler(passes).compile(circuits)
```

```
[5]:
```

```
kak_rc_circuits = rc_kak(circuit, n_compilations=30, local=True)
kak_rc_circuits[0].draw()
```

```
[5]:
```

By default, randomizing and correction gates are applied around all gates in
the hard cycles. To specify that gates should be applied to idling qubits as well,
set the `local`

argument to `False`

:

```
[6]:
```

```
rc_kak(circuit, n_compilations=30, local=False)[0].draw()
```

```
[6]:
```

We now have a `CircuitCollection`

of randomly compiled circuits,
each of which are logically equivalent to the original, with preserved two-qubit
gates.

## Comparing RC performance

We can compare the ideal output distribution of the circuit with the observed distribution obtained using a noisy simulator. Using our collection of randomly compiled circuits, we can estimate the ideal distribution by averaging noisy observations over the collection:

```
[7]:
```

```
# use an ideal simulator to obtain the ideal output distribution
sim = tq.Simulator()
ideal_result = sim.sample(circuit, n_shots=np.inf)
# create a noisy simulator to observe the output in the presence of overrotation noise
noisy_sim = tq.Simulator().add_overrotation(single_sys=0.01, multi_sys=0.1)
noisy_result = noisy_sim.sample(circuit, n_shots=np.inf)
# estimate the ideal distribution using the collection of circuits obtained from RCKak
kak_rc_result = sum(
noisy_sim.sample(kak_circuit, n_shots=np.inf) for kak_circuit in kak_rc_circuits
)
# likewise using the collection of circuits obtained from RC with an additional
# decomposition step
rc_result = sum(
noisy_sim.sample(rc_circuit, n_shots=np.inf) for rc_circuit in rc_circuits
)
# compare the total variation distance between the estimated distributions and the ideal
bare_tvd = noisy_result.tvd(ideal_result)
kak_rc_tvd = kak_rc_result.normalized().tvd(ideal_result)
rc_tvd = rc_result.normalized().tvd(ideal_result)
print("TOTAL VARIATION DISTANCE")
print("Noisy simulation without RC: {:.4f}".format(bare_tvd[0]))
print("Noisy simulation with RCKak: {:.4f}".format(kak_rc_tvd[0]))
print("Noisy simulation with RC (using entangler): {:.4f}".format(rc_tvd[0]))
```

```
TOTAL VARIATION DISTANCE
Noisy simulation without RC: inf
Noisy simulation with RCKak: 0.0557
Noisy simulation with RC (using entangler): inf
```

```
Warning: divide by zero encountered in divide
(/home/jenkins/workspace/release trueq/trueq/results.py:489)
Warning: divide by zero encountered in divide
(/home/jenkins/workspace/release trueq/trueq/results.py:489)
```

## Imperfect coherent error suppression

Note that the randomizing operation implemented by
`RCKak`

is not a full twirl operation, and therefore
not all coherent errors will be suppressed.

For example, consider a single cycle containing a `YY`

rotation simulated with
overrotation noise:

```
[8]:
```

```
circuit = tq.Circuit({(0, 1): tq.Gate.from_generators("YY", 45)})
circuit.measure_all()
circuit.draw()
```

```
[8]:
```

```
[9]:
```

```
kak_rc_circuits = rc_kak(circuit, n_compilations=30, local=True)
# obtain the ideal output distribution
ideal_result = sim.sample(circuit, n_shots=np.inf)
# simulate with overrotation noise
noisy_sim = tq.Simulator().add_overrotation(multi_sys=0.1)
noisy_result = noisy_sim.sample(circuit, n_shots=np.inf)
# estimate using RCKak
kak_rc_result = sum(
noisy_sim.sample(kak_circuit, n_shots=np.inf) for kak_circuit in kak_rc_circuits
)
# compare the total variation distance
bare_tvd = noisy_result.tvd(ideal_result)
kak_rc_tvd = kak_rc_result.normalized().tvd(ideal_result)
print("TOTAL VARIATION DISTANCE")
print("Noisy simulation without RC: {:.4f}".format(bare_tvd[0]))
print("Noisy simulation with RCKak: {:.4f}".format(kak_rc_tvd[0]))
```

```
TOTAL VARIATION DISTANCE
Noisy simulation without RC: 0.0288
Noisy simulation with RCKak: 0.0288
```

We observe that the error was not suppressed by randomized compiling in this case.

## A Note on using RCKak for qudit gates and gates on more than two qubits

As mentioned above, `RCKak`

inserts local randomizing and
correction gates that depend on the gate to be randomized. For one-qudit and two-qubit
gates, the group to sample from is calculated, and a randomization is always applied.
For multi-qudit gates and larger multi-qubit gates, the group to sample from is not
calculated, and instead randomization will be attempted numerically. If a solution is
not found, randomizing and correction gates are not inserted.

However the `RCKak`

pass can always be applied to cycles
containing these gates without error:

```
[10]:
```

```
tq.settings.set_dim(3)
# Apply RCKak to a cycle containing two-qutrit gates
qutrit_rc_circuits = rc_kak(
tq.Circuit({(0, 1): tq.Gate.random(9), (2, 3): tq.Gate.cx3}),
n_compilations=30,
local=True,
)
qutrit_rc_circuits[0].draw()
```

```
[10]:
```

Download

Download this file as Jupyter notebook: rc_kak.ipynb.