Download
Download this file as Jupyter notebook: rc_with_markers.ipynb.
Example: Customizing Randomized Compiling with Cycle Markers
Randomized Compiling is based on breaking the cycles contained in a circuit into two groups: easy cycles and hard cycles. In a typical usage scenario, hard cycles contain operations with significant miscalibration or crosstalk, such as entangling gates. In contrast, easy cycles contain operations which can be executed with high fidelity, such as single-qubit gates.
In True-Q™, the user can specify which cycles are easy or hard by setting the
marker
attribute of the cycles in the circuit: if a cycle
has its marker
set to 0
it is considered as easy;
otherwise it is considered hard.
The randomly_compile()
function creates a collection of randomly
compiled circuits that each describe an equivalent but physically different
implementation of the original circuit. Specifically, for each hard cycle \(U\)
in the circuit, randomly_compile()
picks random Pauli operators
\(P\) and \(Q\), such that \(U = PUQ\) and, as an intermediate step,
replaces the cycle \(U\) by three consecutive cycles \([Q, U, P]\). The
additional cycles \(P\) and \(Q\) are then merged into any existing adjacent
single-qubit cycles, as long as the latter have their marker
attribute set to 0
and can be interpreted as easy cycles. This compilation step
where we fold the random gates into the neighbouring easy cycles allows the depth of a
circuit to remain unchanged by applying randomized compiling.
Note
Randomized compiling produces a new circuit collection after each call, so the output of this example will be different if it’s executed again.
Default configuration: equal cycle markers
By default, if randomly_compile()
is called on a circuit without
explicitly specifying the hard cycles (i.e. the circuit contains only zero
marker
values), it interprets that as the user intending
the hard cycles to consist of all cycles containing two-qubit gates, and the easy
cycles to consist of everything else.
For example, consider a circuit which applies alternating cycles, with an \(X\)
gate on qubit 0 in one cycle and a \(CZ\) gate on qubits 0 and 1 in the other
cycle. To be fully explicit, we set the marker
arguments to
zero (which would otherwise also be the default value):
[2]:
import trueq as tq
cycle1 = tq.Cycle({0: tq.Gate.x}, marker=0)
cycle2 = tq.Cycle({(0, 1): tq.Gate.cz}, marker=0)
circuit = tq.Circuit([cycle1, cycle2, cycle1, cycle2, cycle1])
# Display the circuit
circuit
[2]:
Circuit
|
|
|
(0):
Gate.x
|
|
(0, 1):
Gate.cz
|
|
(0):
Gate.x
|
|
(0, 1):
Gate.cz
|
|
(0):
Gate.x
|
Note that when all cycle marker
s are set to 0
, they are
not displayed in the output.
Next, we generate a set of randomly compiled versions of this circuit:
[3]:
compiled_circuits = tq.randomly_compile(circuit)
# Display an example circuit:
compiled_circuits[0]
[3]:
Circuit
|
||
|
(0):
Gate.x
|
(1):
Gate.y
|
1
|
(0, 1):
Gate.cz
|
  |
|
(0):
Gate.id
|
(1):
Gate.id
|
2
|
(0, 1):
Gate.cz
|
  |
|
(0):
Gate.id
|
(1):
Gate.x
|
This circuit now has two new explicit cycle marker
s for the
two-qubit gates, and the original single-qubit gates have been absorbed into random
twirling gates. The overall circuit length is preserved.
Custom configuration: different cycle markers
If there are cycles in the circuit with non-zero marker
s,
then the randomly_compile()
function interprets them as hard
cycles.
For example, consider again the circuit from above but this time with a third cycle in the middle containing a single-qubit \(X\) gate that is uniquely marked:
[4]:
cycle1 = tq.Cycle({0: tq.Gate.x})
cycle2 = tq.Cycle({(0, 1): tq.Gate.cz})
cycle3 = tq.Cycle({0: tq.Gate.x}, marker=1)
circuit = tq.Circuit([cycle1, cycle2, cycle3, cycle2, cycle1])
# Display the circuit:
circuit
[4]:
Circuit
|
|
|
(0):
Gate.x
|
|
(0, 1):
Gate.cz
|
1
|
(0):
Gate.x
|
|
(0, 1):
Gate.cz
|
|
(0):
Gate.x
|
Now, when we generate a set of randomly compiled versions of this circuit, the resulting circuits will have randomizations inserted only around the marked gate:
[5]:
compiled_circuits = tq.randomly_compile(circuit)
# Display an example circuit:
compiled_circuits[0]
[5]:
Circuit
|
||
|
(0):
Gate.x
|
  |
|
(0, 1):
Gate.cz
|
  |
|
(0):
Gate.z
|
(1):
Gate.z
|
1
|
(0):
Gate.x
|
  |
|
(0):
Gate.z
|
(1):
Gate.z
|
|
(0, 1):
Gate.cz
|
  |
|
(0):
Gate.x
|
  |
When randomly_compile()
is given an input circuit with at least one
non-zero marker
, these cycles are treated as hard cycles. In
contrast, when there are no marker
s specified, any cycles
containing two-qubit gates will be marked and treated as hard cycles. Any cycle which
is not treated as a hard cycle is treated as an easy cycle.
In summary, a cycle marker
provides a configuration option
for Randomized Compiling to specify where random gates can be inserted and which
cycles to leave untouched.
Download
Download this file as Jupyter notebook: rc_with_markers.ipynb.