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.
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:
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:
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]:
defrc_kak(circuit,n_compilations=30,local=True):circuits=CircuitCollection(circuit.__copy__()for_inrange(n_compilations))passes=[tqc.MarkCycles(),# mark any cycles with two-qubit gatestqc.RCKak(local=local),# apply randomizing cycles around marked cyclestqc.Merge(),tqc.RemoveEmptyCycle(),]returntqc.Compiler(passes).compile(circuits)
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:
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 distributionsim=tq.Simulator()ideal_result=sim.sample(circuit,n_shots=np.inf)# create a noisy simulator to observe the output in the presence of overrotation noisenoisy_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 RCKakkak_rc_result=sum(noisy_sim.sample(kak_circuit,n_shots=np.inf)forkak_circuitinkak_rc_circuits)# likewise using the collection of circuits obtained from RC with an additional# decomposition steprc_result=sum(noisy_sim.sample(rc_circuit,n_shots=np.inf)forrc_circuitinrc_circuits)# compare the total variation distance between the estimated distributions and the idealbare_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)
0 1 Key: Labels:(0, 1)Name:GateLocally Equivalent:Non-CliffordGenerators: YY: 45.00 0.92 0.38j 0.92 -0.38j -0.38j 0.92 0.38j 0.92 1Labels:(0,)Name:Meas M Labels:(1,)Name:Meas M
[9]:
kak_rc_circuits=rc_kak(circuit,n_compilations=30,local=True)# obtain the ideal output distributionideal_result=sim.sample(circuit,n_shots=np.inf)# simulate with overrotation noisenoisy_sim=tq.Simulator().add_overrotation(multi_sys=0.1)noisy_result=noisy_sim.sample(circuit,n_shots=np.inf)# estimate using RCKakkak_rc_result=sum(noisy_sim.sample(kak_circuit,n_shots=np.inf)forkak_circuitinkak_rc_circuits)# compare the total variation distancebare_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 gatesqutrit_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()