This library includes tools to help handling units and conversions.
This class has an API for introducing units and conversion methods.
The available units are regrouped in the table below:
Unit | ns | us | ms | s | mHz | Hz | kHz | MHz | GHz | uV | mV | V |
---|---|---|---|---|---|---|---|---|---|---|---|---|
factor | 1 | 10^3 | 10^6 | 10^9 | 10^-3 | 1 | 10^3 | 10^6 | 10^9 | 10^-6 | 10^-3 | 1 |
Please note the special behavior of time units
Several conversion methods are available:
to_clock_cycles(t)
: Converts a duration to clock cycles.demod2volts(data, duration)
: Converts the demodulated data to volts.raw2volts(data)
: Converts the raw data to volts.
from qualang_tools.units import unit
u = unit()
qubit_LO = 6.8957 * u.GHz
qubit_IF = 245 * u.MHz
readout_len = 256 * u.us
pi_len = 224 * u.ns
qubit_T1 = 6 * u.ms
plt.figure()
plt.scatter(u.demod2volts(I, readout_len) * u.uV, u.demod2volts(Q, readout_len)* u.uV)
plt.xlabel("I quadrature [µV]")
plt.ylabel("Q quadrature [µV]")
plt.figure()
plt.plot(u.raw2volts(raw_data) * u.mV)
plt.ylabel("Raw trace [mV]")
Time units are specified differently while declaring a python variable or in the context of a qua program
.
In particular, in python, time is defined in nanoseconds, while in a qua program it must be specified in units of clock cycles of the FPGA (of the duration of 4 nanoseconds).
unit
respects this behavior, and the value of time units is calculated dynamically depending on the context.
This translates to the normal behavior of time units being
u.ns == 1 # Times are in ns in python
while inside a qua program
with program() as prog:
4 * u.ns == 1 # Times are in clock cycles in qua
from qm.qua import *
from qualang_tools.units import unit
u = unit()
pulse_duration_in_ns = 40 * u.ns # == 40
with program() as prog:
play('pulse1', 'element1', duration = pulse_duration_in_ns * u.ns)
# Automatically converts the duration to 10 clock cycles
The OPX only supports an integer number of clock cycles. Therefore, the result of a multiplication with u.ns
and similar time units can be automatically cast to int
.
For example
with program() as prog:
6 * u.ns == 1
This behavior is disabled by default and can be enabled when declaring unit
as
u = unit(coerce_to_integer = True)
If casting occurs with nonzero reminder, (e.g. 6 * u.ns
), this will emit a RuntimeWarning
. This can be avoided by declaring
u = unit(verbose = False)
To avoid floating point errors and incompatibilities with some configuration entries and mixer calibration parameters,
the flag coerce_to_integer = True
will automatically round and cast to integers the frequencies defined with the
unit class.
For instance:
u = unit(coerce_to_integer = True)
qubit_LO = 4.1 * u.GHz # --> will return 4_100_000_000 instead of 4099999999.9999995 (= 4.1 * 1e9)
qubit_IF = 4.1 * u.MHz # --> Will return 4_100_000 instead of 4099999.9999995 (= 4.1 * 1e6)