Download

Download this file as Jupyter notebook: phase_tracking.ipynb.

Example: Phase Tracking with the Compiler

Phase tracking is a technique that exploits the ability of quantum control hardware to perform accurate and arbitrary phase rotations of pulse shapes in order to reduce the number of pulse shapes required to form a device’s native quantum gate set. Moreover, it causes the native gate set to be continuously parametric which greatly reduces the synthesis cost of arbitrary unitaries. For example, if an \(R_x(90)\) gate is tuned up, then any single qubit unitary can be synthesized using two of these pulses and phase tracking.

Below, we call these phase rotation gates virtual gates because they are not directly used to update the quantum state. Instead, virtual gates are better thought of as pulse compiler directives, where the pulse compiler is a program that prepares a sequence of waveforms for the control electronics given a circuit of native gates. In a multi-qubit device that employs phase tracking, the pulse compiler accumulates a phase on each qubit in the circuit whenever a virtual gate is encountered. Any time a non-virtual gate is encountered on a given subset of qubits, the corresponding cummulative phases are used to modify the phases of the pulse shape.

We illustrate how phase tracking works using the following single-qubit circuit expressed as rotations in matrix multiplication order. This circuit is assumed to be the output of a pre-compiler that has already converted some original circuit into \(R_x(90)\) and \(R_z(\phi)\) rotations.

\[R_z(d)\cdot R_x(90) \cdot R_z(c)\cdot R_x(90) \cdot R_z(b) \cdot R_x(90) \cdot R_z(a)\]

This can be rewritten as

\[\begin{split}R_z(a+b+c+d) \cdot R_z(-(a+b+c)) \cdot R_x(90) \cdot R_z(a+b+c) \\ \quad \cdot R_z(-(a+b)) \cdot R_x(90) \cdot R_z(a+b) \cdot R_z(-a) \cdot R_x(90) \cdot R_z(a)\end{split}\]

and further simplified to

\[R_z(a+b+c+d)R_{a+b+c}(90)R_{a+b}(90) R_a(90)\]

where we have defined a nutation about some vector in the x-y plane as

\[R_\phi(\theta)=R_z(-\phi)R_x(90)R_z(\phi) =\operatorname{exp}(-i \theta (\cos(\phi)X-\sin(\phi)Y)/2).\]

Thus if a pulse shape for the \(R_x(90)\) gate is tuned up, and our control electronics are able to rotate it in quadrature by arbitrary angles thereby producing operations \(R_\phi(90)\), then we have sufficient control to perform the original circuit. Note further that any qubit unitary can be decomposed into an alternating sequence \(R_z(c)\cdot R_x(90) \cdot R_z(b) \cdot R_x(90) \cdot R_z(a)\) where \(a\), \(b\), \(c\) are its ZXZ Euler angles. It follows that this is the only pulse shape that is required to perform single qubit gates.

For most systems, the final accumulated phase in the z-axis, \(R_z(a+b+c+d)\), does not actually need to be implemented on the qubit. This is because the final operation will be a measurement of the qubit along the z-axis, whose outcome will not be affected by a z-rotation.

The process of phase tracking on multi-qubit gates is similar to the single qubit case, which will be seen in the examples below. As previously mentioned, a different phase needs to be tracked for each qubit, and pulse shapes may require a parameter for every qubit they act on.

[2]:
import trueq as tq
import numpy as np

Using the Controlled-Z Gate

A device whose only native two-qubit gate is the CZ gate is the simplest case because this commutes with the virtual Z gate on both qubits. This means we only need to apply accumulated phases to the single qubit gates.

First, we define a config object that contains all of the necessary gate factories. Since we will be doing phase tracking, we require not only the parametric native gate factories and the virtual gate factory, but also a factory for each of the static gates that correspond to the parametric gates at parameter value 0.

factories for our parametric gates. the first is a 90 degree rotation about an axis in the x-y plane, equivalent to z(-a)*x(90)*z(a). the second is our cz gate which happens to need no parameters because it commutes with virtual gates

[3]:
r90 = tq.config.GateFactory.from_hamiltonian(
    "r90", [["Z", "-theta"], ["X", "90"], ["Z", "theta"]]
)
cz = tq.config.GateFactory.from_matrix("cz", np.diag([1, 1, 1, -1]))

# factory for our virtual gate
z = tq.config.GateFactory.from_hamiltonian("z", [["Z", "theta"]])

# factory for static gates, equal to our parametric gates at 0
x90 = tq.config.GateFactory.from_matrix("x90", r90(0))
factories = [x90, z, cz, r90]

Next, we initialize our phase-tracking compiler pattern. Note that it chooses the virtual gate factory by looking for a rotation about the Z-axis in units of degrees. If this is not the case for you, specify any other single qubit rotation manually using virtual=factory.

[4]:
phase_tracker = tq.compilation.PhaseTrack(factories=factories)

Next we set up our compiler and the rest of its passes. There are two essential steps:

  1. Convert all abstract gates into the static gates and the virtual gate (Native1Q and Native2Q).

  2. Use the PhaseTrack pattern to accumulate phases and compile them into the parametric gate parameters.

There are additionally justification, merge, and identity removal book-keeping passes that can be adjusted to one’s needs.

[5]:
compiler = tq.Compiler(
    [
        tq.compilation.Native2Q(factories),
        tq.compilation.Justify(),
        tq.compilation.Merge(),
        tq.compilation.RemoveId(),
        tq.compilation.Native1Q(factories),
        phase_tracker,
        tq.compilation.RemoveEmptyCycle(),
    ]
)

We illustrate the output of this compiler using a circuit that generates the GHZ state on \(n\) qubits. Before compilation, our abstract circuit on 4 qubits is as follows:

[6]:
def ghz(n):
    circuit = tq.Circuit([{0: tq.Gate.h}])
    for idx in range(n - 1):
        circuit.append({(idx, idx + 1): tq.Gate.cnot})
    circuit.measure_all()
    return circuit


ghz(4)
[6]:
True-Q formatting will not be loaded without trusting this notebook or rerunning the affected cells. Notebooks can be marked as trusted by clicking "File -> Trust Notebook".
Circuit
Key:
No key present in circuit.
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(0): Gate.h
Name:
  • Gate.h
Aliases:
  • Gate.h
  • Gate.f
  • Gate.cliff12
Generators:
  • 'X': 127.279
  • 'Z': 127.279
Matrix:
  • 0.71 0.71 0.71 -0.71
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(0, 1): Gate.cx
Name:
  • Gate.cx
Aliases:
  • Gate.cx
  • Gate.cnot
Likeness:
  • CNOT
Generators:
  • 'ZX': -90.0
  • 'IX': 90.0
  • 'ZI': 90.0
Matrix:
  • 1.00 1.00 1.00 1.00
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(1, 2): Gate.cx
Name:
  • Gate.cx
Aliases:
  • Gate.cx
  • Gate.cnot
Likeness:
  • CNOT
Generators:
  • 'ZX': -90.0
  • 'IX': 90.0
  • 'ZI': 90.0
Matrix:
  • 1.00 1.00 1.00 1.00
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(2, 3): Gate.cx
Name:
  • Gate.cx
Aliases:
  • Gate.cx
  • Gate.cnot
Likeness:
  • CNOT
Generators:
  • 'ZX': -90.0
  • 'IX': 90.0
  • 'ZI': 90.0
Matrix:
  • 1.00 1.00 1.00 1.00
 
1
Marker 1
Compilation tools may only recompile cycles with equal markers.
(0): Meas()
Name:
  • Meas()
(1): Meas()
Name:
  • Meas()
(2): Meas()
Name:
  • Meas()
(3): Meas()
Name:
  • Meas()

The output bitstring distribution is as expected:

[7]:
tq.Simulator().sample(ghz(4), n_shots=np.inf).plot()
../../_images/guides_compilation_phase_tracking_12_0.png

Following compilation, our circuit contains only gates that were generated using the r90 or cz factory. The parameters the factories used to construct the gates are stored in the gate objects, along with the gates’ matrix representations. Single qubit gates, under this compiler, are always decomposed into two 90 degree nutations.

[8]:
circuit = compiler.compile(ghz(4))
circuit
[8]:
True-Q formatting will not be loaded without trusting this notebook or rerunning the affected cells. Notebooks can be marked as trusted by clicking "File -> Trust Notebook".
Circuit
Key:
No key present in circuit.
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(0): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.sx
  • Gate.cliff5
Parameters:
  • theta = 0.0
Generators:
  • 'X': 90.0
Matrix:
  • 0.71 -0.71j -0.71j 0.71
(1): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.sx
  • Gate.cliff5
Parameters:
  • theta = 0.0
Generators:
  • 'X': 90.0
Matrix:
  • 0.71 -0.71j -0.71j 0.71
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(0): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.cliff6
Parameters:
  • theta = 90.0
Generators:
  • 'Y': -90.0
Matrix:
  • 0.71 0.71 -0.71 0.71
(1): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.cliff6
Parameters:
  • theta = 90.0
Generators:
  • 'Y': -90.0
Matrix:
  • 0.71 0.71 -0.71 0.71
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(0, 1): cz()
Name:
  • cz()
Aliases:
  • Gate.cz
Likeness:
  • CNOT
Generators:
  • 'ZZ': -90.0
  • 'ZI': 90.0
  • 'IZ': 90.0
Matrix:
  • 1.00 1.00 1.00 -1.00
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(1): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.cliff6
Parameters:
  • theta = 90.0
Generators:
  • 'Y': -90.0
Matrix:
  • 0.71 0.71 -0.71 0.71
(2): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.sx
  • Gate.cliff5
Parameters:
  • theta = 0.0
Generators:
  • 'X': 90.0
Matrix:
  • 0.71 -0.71j -0.71j 0.71
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(1): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.cliff4
Parameters:
  • theta = 180.0
Generators:
  • 'X': -90.0
Matrix:
  • 0.71 0.71j 0.71j 0.71
(2): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.cliff6
Parameters:
  • theta = 90.0
Generators:
  • 'Y': -90.0
Matrix:
  • 0.71 0.71 -0.71 0.71
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(1, 2): cz()
Name:
  • cz()
Aliases:
  • Gate.cz
Likeness:
  • CNOT
Generators:
  • 'ZZ': -90.0
  • 'ZI': 90.0
  • 'IZ': 90.0
Matrix:
  • 1.00 1.00 1.00 -1.00
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(2): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.cliff6
Parameters:
  • theta = 90.0
Generators:
  • 'Y': -90.0
Matrix:
  • 0.71 0.71 -0.71 0.71
(3): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.sx
  • Gate.cliff5
Parameters:
  • theta = 0.0
Generators:
  • 'X': 90.0
Matrix:
  • 0.71 -0.71j -0.71j 0.71
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(2): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.cliff4
Parameters:
  • theta = 180.0
Generators:
  • 'X': -90.0
Matrix:
  • 0.71 0.71j 0.71j 0.71
(3): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.cliff6
Parameters:
  • theta = 90.0
Generators:
  • 'Y': -90.0
Matrix:
  • 0.71 0.71 -0.71 0.71
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(2, 3): cz()
Name:
  • cz()
Aliases:
  • Gate.cz
Likeness:
  • CNOT
Generators:
  • 'ZZ': -90.0
  • 'ZI': 90.0
  • 'IZ': 90.0
Matrix:
  • 1.00 1.00 1.00 -1.00
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(0): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.cliff6
Parameters:
  • theta = 90.0
Generators:
  • 'Y': -90.0
Matrix:
  • 0.71 0.71 -0.71 0.71
(1): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.cliff4
Parameters:
  • theta = 180.0
Generators:
  • 'X': -90.0
Matrix:
  • 0.71 0.71j 0.71j 0.71
(2): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.cliff4
Parameters:
  • theta = 180.0
Generators:
  • 'X': -90.0
Matrix:
  • 0.71 0.71j 0.71j 0.71
(3): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.cliff6
Parameters:
  • theta = 90.0
Generators:
  • 'Y': -90.0
Matrix:
  • 0.71 0.71 -0.71 0.71
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(0): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.sy
  • Gate.cliff7
Parameters:
  • theta = 270.0
Generators:
  • 'Y': 90.0
Matrix:
  • 0.71 -0.71 0.71 0.71
(1): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.sx
  • Gate.cliff5
Parameters:
  • theta = 0.0
Generators:
  • 'X': 90.0
Matrix:
  • 0.71 -0.71j -0.71j 0.71
(2): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.sx
  • Gate.cliff5
Parameters:
  • theta = 0.0
Generators:
  • 'X': 90.0
Matrix:
  • 0.71 -0.71j -0.71j 0.71
(3): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.cliff4
Parameters:
  • theta = 180.0
Generators:
  • 'X': -90.0
Matrix:
  • 0.71 0.71j 0.71j 0.71
1
Marker 1
Compilation tools may only recompile cycles with equal markers.
(0): Meas()
Name:
  • Meas()
(1): Meas()
Name:
  • Meas()
(2): Meas()
Name:
  • Meas()
(3): Meas()
Name:
  • Meas()

Finally, our output bitstring distribution is unchanged, as expected.

[9]:
tq.Simulator().sample(circuit, n_shots=np.inf).plot()
../../_images/guides_compilation_phase_tracking_16_0.png

Using the Cross-Resonance Gate

The maximally entangling cross-resonance gate is equal to

\[\begin{split}CR=\begin{pmatrix} 1 & -i & 0 & 0 \\ -i & 1 & 0 & 0 \\ 0 & 0 & 1 & i \\ 0 & 0 & i & 1 \end{pmatrix}/\sqrt{2} =\frac{I\otimes I-iZ\otimes X}{\sqrt{2}}.\end{split}\]

By adjusting the phase of the microwave control used to generate this gate, we can rotate action of the second qubit about the z-axis, which results in the parametric gate

\[CR(\phi) = \frac{I\otimes I-i\cos(\phi)Z\otimes X+i\sin(\phi)Z\otimes Y}{\sqrt{2}}.\]

This parametric gate is defined below as a gate factory, along with other factories necessary for this example.

[10]:
# define parametric gate factories
r90 = tq.config.GateFactory.from_hamiltonian(
    "r90", [["Z", "-theta"], ["X", "90"], ["Z", "theta"]]
)
cr = tq.config.GateFactory.from_hamiltonian(
    "cr", [["IZ", "-theta"], ["ZX", "90"], ["IZ", "theta"]]
)

# factory for our virtual gate
z = tq.config.GateFactory.from_hamiltonian("z", [["Z", "theta"]])

# factory for static gates, equal to our parametric gates at 0
x90 = tq.config.GateFactory.from_matrix("x90", r90(0))
cr0 = tq.config.GateFactory.from_matrix("cr0", cr(0))

# put everything into a factory list. static gates should go first
factories = [x90, z, cr0, r90, cr]

As in the CZ section, we define a compiler that performs phase-tracking using our native gates, and test it on a circuit that generates a 4-qubit GHZ state.

[11]:
compiler = tq.Compiler(
    [
        tq.compilation.Native2Q(factories),
        tq.compilation.Justify(),
        tq.compilation.Merge(),
        tq.compilation.RemoveId(),
        tq.compilation.Native1Q(factories),
        tq.compilation.PhaseTrack(factories),
        tq.compilation.Justify(),
    ]
)

circuit = compiler.compile(ghz(4))
circuit
[11]:
True-Q formatting will not be loaded without trusting this notebook or rerunning the affected cells. Notebooks can be marked as trusted by clicking "File -> Trust Notebook".
Circuit
Key:
No key present in circuit.
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(0): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.sx
  • Gate.cliff5
Parameters:
  • theta = 0.0
Generators:
  • 'X': 90.0
Matrix:
  • 0.71 -0.71j -0.71j 0.71
(1): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.cliff6
Parameters:
  • theta = 90.0
Generators:
  • 'Y': -90.0
Matrix:
  • 0.71 0.71 -0.71 0.71
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(0): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.cliff6
Parameters:
  • theta = 90.0
Generators:
  • 'Y': -90.0
Matrix:
  • 0.71 0.71 -0.71 0.71
(1): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.cliff4
Parameters:
  • theta = 180.0
Generators:
  • 'X': -90.0
Matrix:
  • 0.71 0.71j 0.71j 0.71
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(0, 1): cr(theta)
Name:
  • cr(theta)
Likeness:
  • CNOT
Parameters:
  • theta = 90.0
Generators:
  • 'ZY': -90.0
Matrix:
  • 0.71 0.71 -0.71 0.71 0.71 -0.71 0.71 0.71
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(1): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.sx
  • Gate.cliff5
Parameters:
  • theta = 0.0
Generators:
  • 'X': 90.0
Matrix:
  • 0.71 -0.71j -0.71j 0.71
(2): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.cliff6
Parameters:
  • theta = 90.0
Generators:
  • 'Y': -90.0
Matrix:
  • 0.71 0.71 -0.71 0.71
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(1): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.sx
  • Gate.cliff5
Parameters:
  • theta = 0.0
Generators:
  • 'X': 90.0
Matrix:
  • 0.71 -0.71j -0.71j 0.71
(2): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.cliff4
Parameters:
  • theta = 180.0
Generators:
  • 'X': -90.0
Matrix:
  • 0.71 0.71j 0.71j 0.71
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(1, 2): cr(theta)
Name:
  • cr(theta)
Likeness:
  • CNOT
Parameters:
  • theta = 90.0
Generators:
  • 'ZY': -90.0
Matrix:
  • 0.71 0.71 -0.71 0.71 0.71 -0.71 0.71 0.71
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(2): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.sx
  • Gate.cliff5
Parameters:
  • theta = 0.0
Generators:
  • 'X': 90.0
Matrix:
  • 0.71 -0.71j -0.71j 0.71
(3): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.cliff6
Parameters:
  • theta = 90.0
Generators:
  • 'Y': -90.0
Matrix:
  • 0.71 0.71 -0.71 0.71
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(2): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.sx
  • Gate.cliff5
Parameters:
  • theta = 0.0
Generators:
  • 'X': 90.0
Matrix:
  • 0.71 -0.71j -0.71j 0.71
(3): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.cliff4
Parameters:
  • theta = 180.0
Generators:
  • 'X': -90.0
Matrix:
  • 0.71 0.71j 0.71j 0.71
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(2, 3): cr(theta)
Name:
  • cr(theta)
Likeness:
  • CNOT
Parameters:
  • theta = 90.0
Generators:
  • 'ZY': -90.0
Matrix:
  • 0.71 0.71 -0.71 0.71 0.71 -0.71 0.71 0.71
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(3): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.sx
  • Gate.cliff5
Parameters:
  • theta = 0.0
Generators:
  • 'X': 90.0
Matrix:
  • 0.71 -0.71j -0.71j 0.71
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(3): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.sx
  • Gate.cliff5
Parameters:
  • theta = 0.0
Generators:
  • 'X': 90.0
Matrix:
  • 0.71 -0.71j -0.71j 0.71
 
1
Marker 1
Compilation tools may only recompile cycles with equal markers.
(0): Meas()
Name:
  • Meas()
(1): Meas()
Name:
  • Meas()
(2): Meas()
Name:
  • Meas()
(3): Meas()
Name:
  • Meas()

Finally, we verify that our bitstring output is as expected.

[12]:
tq.Simulator().sample(circuit, n_shots=np.inf).plot()
../../_images/guides_compilation_phase_tracking_22_0.png

Using the Mølmer-Sørensen Gate

A maximally entangling MS gate has the form

\[\begin{split}MS(90)=\begin{pmatrix} 1 & 0 & 0 & -i \\ 0 & 1 & -i & 0 \\ 0 & -i & 1 & 0 \\ -i & 0 & 0 & 1 \end{pmatrix}/\sqrt{2} =\frac{I\otimes I-iX\otimes X}{/\sqrt{2}}\end{split}\]

which, for a trapped ion, is generated by lasing on both qubits simultaneously. However, by changing the phase of the frequency sources sent to the optical modulators on each qubit, the azimuthal angle of the MS gate can be changed independently on both qubits qubits. This allows the implementation of the parametric gate

\[MS(90, \phi_1, \phi_2) = \frac{II-iR_{\phi_1}(90)\otimes R_{\phi_2}(90)}{\sqrt{2}}\]

where \(R_\phi(\theta)\) is defined above.

This parametric gate is defined below as a gate factory, along with other factories necessary for this example.

[13]:
# factories for our parametric gates. the first is a 90 degree rotation about an axis in
# the x-y plane, equivalent to z(a)*x(90)*z(-a). the second is a Mølmer-Sørensen gate
# with phase rotation a on the first qubit and b on the second qubit, i.e. equivalent to
# (z(a) & z(b))*xx(90)*(z(-a) & z(-b))
r90 = tq.config.GateFactory.from_hamiltonian(
    "r90", [["Z", "-theta"], ["X", "90"], ["Z", "theta"]]
)
ms = tq.config.GateFactory.from_hamiltonian(
    "ms", [["IZ", "-b"], ["ZI", "-a"], ["XX", 90], ["ZI", "a"], ["IZ", "b"]]
)

# factory for our virtual gate
z = tq.config.GateFactory.from_hamiltonian("z", [["Z", "theta"]])

# factory for static gates, equal to our parametric gates at 0
x90 = tq.config.GateFactory.from_matrix("x90", r90(0))
ms0 = tq.config.GateFactory.from_matrix("ms0", ms(0, 0))

# put everything into a factory list
factories = [x90, z, ms0, r90, ms]

phase_tracker = tq.compilation.PhaseTrack(factories)
phase_tracker._rules
[13]:
{x90(): ({0: 'theta'},
  GateFactory(name='r90', layers=[Rotation.from_pauli('Z', 'theta', -1.0), FixedRotation.from_pauli('X', 90.0), Rotation.from_pauli('Z', 'theta', 1.0)], parameters={'theta': None})),
 ms0(): ({1: 'b', 0: 'a'},
  GateFactory(name='ms', layers=[Rotation.from_pauli('IZ', 'b', -1.0), Rotation.from_pauli('ZI', 'a', -1.0), FixedRotation.from_pauli('XX', 90.0), Rotation.from_pauli('ZI', 'a', 1.0), Rotation.from_pauli('IZ', 'b', 1.0)], parameters={'b': None, 'a': None}))}

As in the CZ section, we define a compiler that performs phase-tracking using our native gates, and test it on a circuit that generates a 4-qubit GHZ state.

[14]:
compiler = tq.Compiler(
    [
        tq.compilation.Native2Q(factories),
        tq.compilation.Justify(),
        tq.compilation.Merge(),
        tq.compilation.RemoveId(),
        tq.compilation.Native1Q(factories),
        tq.compilation.PhaseTrack(factories),
        tq.compilation.Justify(),
    ]
)

circuit = compiler.compile(ghz(4))
circuit
[14]:
True-Q formatting will not be loaded without trusting this notebook or rerunning the affected cells. Notebooks can be marked as trusted by clicking "File -> Trust Notebook".
Circuit
Key:
No key present in circuit.
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(0): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.sy
  • Gate.cliff7
Parameters:
  • theta = -90.0
Generators:
  • 'Y': 90.0
Matrix:
  • 0.71 -0.71 0.71 0.71
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(0): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.cliff6
Parameters:
  • theta = 90.0
Generators:
  • 'Y': -90.0
Matrix:
  • 0.71 0.71 -0.71 0.71
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(0, 1): ms(b, a)
Name:
  • ms(b, a)
Likeness:
  • CNOT
Parameters:
  • b = 0.0
  • a = 180.0
Generators:
  • 'XX': -90.0
Matrix:
  • 0.71 0.71j 0.71 0.71j 0.71j 0.71 0.71j 0.71
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(1): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.sx
  • Gate.cliff5
Parameters:
  • theta = 0.0
Generators:
  • 'X': 90.0
Matrix:
  • 0.71 -0.71j -0.71j 0.71
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(1): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.cliff6
Parameters:
  • theta = 90.0
Generators:
  • 'Y': -90.0
Matrix:
  • 0.71 0.71 -0.71 0.71
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(1, 2): ms(b, a)
Name:
  • ms(b, a)
Likeness:
  • CNOT
Parameters:
  • b = 0.0
  • a = 0.0
Generators:
  • 'XX': 90.0
Matrix:
  • 0.71 -0.71j 0.71 -0.71j -0.71j 0.71 -0.71j 0.71
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(2): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.sx
  • Gate.cliff5
Parameters:
  • theta = 0.0
Generators:
  • 'X': 90.0
Matrix:
  • 0.71 -0.71j -0.71j 0.71
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(2): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.cliff6
Parameters:
  • theta = 90.0
Generators:
  • 'Y': -90.0
Matrix:
  • 0.71 0.71 -0.71 0.71
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(2, 3): ms(b, a)
Name:
  • ms(b, a)
Likeness:
  • CNOT
Parameters:
  • b = 0.0
  • a = 0.0
Generators:
  • 'XX': 90.0
Matrix:
  • 0.71 -0.71j 0.71 -0.71j -0.71j 0.71 -0.71j 0.71
 
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(0): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.sx
  • Gate.cliff5
Parameters:
  • theta = 0.0
Generators:
  • 'X': 90.0
Matrix:
  • 0.71 -0.71j -0.71j 0.71
(1): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.cliff4
Parameters:
  • theta = -180.0
Generators:
  • 'X': -90.0
Matrix:
  • 0.71 0.71j 0.71j 0.71
(2): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.cliff4
Parameters:
  • theta = -180.0
Generators:
  • 'X': -90.0
Matrix:
  • 0.71 0.71j 0.71j 0.71
(3): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.sy
  • Gate.cliff7
Parameters:
  • theta = -90.0
Generators:
  • 'Y': 90.0
Matrix:
  • 0.71 -0.71 0.71 0.71
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(0): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.cliff6
Parameters:
  • theta = 90.0
Generators:
  • 'Y': -90.0
Matrix:
  • 0.71 0.71 -0.71 0.71
(1): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.sy
  • Gate.cliff7
Parameters:
  • theta = -90.0
Generators:
  • 'Y': 90.0
Matrix:
  • 0.71 -0.71 0.71 0.71
(2): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.sy
  • Gate.cliff7
Parameters:
  • theta = -90.0
Generators:
  • 'Y': 90.0
Matrix:
  • 0.71 -0.71 0.71 0.71
(3): r90(theta)
Name:
  • r90(theta)
Aliases:
  • Gate.sx
  • Gate.cliff5
Parameters:
  • theta = 0.0
Generators:
  • 'X': 90.0
Matrix:
  • 0.71 -0.71j -0.71j 0.71
1
Marker 1
Compilation tools may only recompile cycles with equal markers.
(0): Meas()
Name:
  • Meas()
(1): Meas()
Name:
  • Meas()
(2): Meas()
Name:
  • Meas()
(3): Meas()
Name:
  • Meas()

Finally, we verify that our bitstring output is as expected.

[15]:
tq.Simulator().sample(circuit, n_shots=np.inf).plot()
../../_images/guides_compilation_phase_tracking_28_0.png

Download

Download this file as Jupyter notebook: phase_tracking.ipynb.