In [ ]:
from QGL import *
See Auspex example notebooks on how to configure a channel library.
For the examples in this notebook, we will the channel library generated by running ex1_QGL_basics.ipynb
in this same directory.
In [ ]:
cl = ChannelLibrary(db_resource_name="example.sqlite")
In [ ]:
q1 = cl["q1"]
# Repeat similar configuration for q2
q2 = cl.new_qubit("q2")
aps2_3 = cl.new_APS2("BBNAPS3", address="192.168.5.103")
aps2_4 = cl.new_APS2("BBNAPS4", address="192.168.5.104")
dig_2 = cl.new_X6("X6_2", address=0)
cl.set_control(q2, aps2_3)
cl.set_measure(q2, aps2_4, dig_2.ch(1))
One can define simultaneous operations on qubits using the *
operator (indicating a tensor product), see ex1_QGL_basics.
Below is an example of QGL basic sequence for two qubits.
measChans
selects which qubits to measure (default = all driven)add_cals
adds reference segments corresponding to the computational states at the end of the sequence, used to normalize the readout signals
In [ ]:
RabiPoints = 101;
plot_pulse_files(RabiAmp_NQubits((q1,q2),np.linspace(0,1,RabiPoints), measChans=(q1,q2), add_cals=True))
To write a two-qubit gate in QGL, you must add to your channel library a logical channel
representing the coupling between qubits. In QGL terminology, this is known as
an Edge
, and is a directed edge in the connectivity graph of your device.
QGL uses directed edges because certain two-qubit interactions have a preferred
ordering of the interaction. For instance, a cross resonance gate has a
preferred sign of the qubit-qubit detuning. By storing directed edges, we can
write two-qubit primitives that emit different pulses depending on whether the
(control, target) pair is aligned or anti-aligned with the underlying
interaction Hamiltonian.
The following examples declares that q1 and q2 are connected, and defines an edge connecting from q1 to q2:
In [ ]:
e = cl.new_edge(q1, q2)
We can now include CNOT
gates in our sequences. In this example you can see the use of the two-qubit primitive CNOT
.
The exact sequence will depend on the (source,
target) order you selected in creating the q1-q2 Edge
and on the
chosen cnot_implementation
.
You can select a
different default CNOT
implementation by modifying the cnot_implementation key
in your local QGL's config.py
file.
In order to actually compile a CNOT, there needs to be a physical channel and generator associated with the edge, i.e.:
In [ ]:
# Most calls required label and address. Let's define
# an AWG for control pulse generation
aps2_5 = cl.new_APS2("BBNAPS5", address="192.168.5.106")
cl.set_control(e, aps2_5)
In [ ]:
seqs = [[Id(q1), CNOT(q1, q2)]] # use the default CNOT_simple implementation, where the CNOT is represented as an X pulse
mf = compile_to_hardware(seqs,'CNOT_simple')
plot_pulse_files(mf)
You can also explicitly call CNOT_CR
to
use the CR decomposition, independently of the global configuration.
In [ ]:
seqs = [[Id(q1), CNOT_CR(q1, q2)]] # use the CNOT_CR implementation, where the CNOT is decomposed
# into a sequence of single-qubit gates and a ZX90, as is appropriate for a cross-resonance interaction.
mf = compile_to_hardware(seqs,'CNOT_CR')
plot_pulse_files(mf)
Inverting the order of the CNOT_CR
input will also produce a CNOT using the same directed edge (q1->q2), but with added single-qubit gates to invert the CNOT control and target.
In [ ]:
seqs = [[Id(q1), CNOT_CR(q2, q1)]] # use the CNOT_CR implementation, where the CNOT is decomposed
# into a sequence of single-qubit gates and a ZX90, as is appropriate for a cross-resonance interaction.
mf = compile_to_hardware(seqs,'CNOT_CR_inv')
plot_pulse_files(mf)
In [ ]: