Skip to content

Commit 00c65e9

Browse files
Merge pull request #1221 from qiboteam/set-if-tof
Adding detuning to `time_of_flight` experiment
2 parents ea5ef46 + 2433377 commit 00c65e9

7 files changed

Lines changed: 96 additions & 60 deletions

File tree

148 KB
Loading

doc/source/protocols/signal/time_of_flight.rst

Lines changed: 7 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,42 +24,27 @@ How to execute the experiment
2424
- id: time of flight experiment # custom ID of the experiment
2525
operation: time_of_flight_readout # unique name of the routine
2626
parameters:
27-
readout_amplitude: 1 # usually high
27+
readout_amplitude: 0.5
2828
window_size: 10
29+
detuning: 50_000_000
2930
nshots: 1024
3031
relaxation_time: 20_000
3132
3233
Although it is possible to avoid setting a specific readout amplitude, it is generally useful to set a high value here. Indeed, we are not looking for the optimal amplitude but we want to have a signal with enough power so that it is clear when it starts.
3334

35+
3436
Acquisition
3537
^^^^^^^^^^^
3638

37-
.. testcode::
38-
:hide:
39-
40-
from qibolab import AcquisitionType
41-
42-
It is important to note that this experiment makes use of the RAW acquisition mode (see `Qibolab documentation <https://qibo.science/qibolab/stable/api-reference/qibolab.html#qibolab.execution_parameters.AcquisitionType>`_), which may require some specific care depending on the instrument employed (for some devices demodulation could be used, or this mode could be available for just a single qubit at a time).
43-
44-
For a 3D cavity we expect a plot (considering demodulation) as the following:
45-
4639
.. image:: time_of_flight.png
4740

48-
In this image, for example, we can see that the fit procedure did not find the correct pulse starting point (that is around 200). This could be improved increasing the window size.
49-
50-
For a 2D resonator, some more oscillation could appear:
51-
52-
.. image:: time_of_flight_2d.png
53-
54-
Just as an example, we provide here also a plot for a 2D resonator case, where the demodulation is not carried out.
55-
As we can see the plot is very different, but the starting point of the signal is still clearly visible.
56-
57-
.. image:: time_of_flight_2d_nodem.png
58-
5941
Fit
6042
^^^
6143

62-
The fit procedure (:func:`qibocal.protocols.signal_experiments.time_of_flight_readout._fit`) employs a moving average, returning the time when it is maximum, namely when the signal starts being acquired.
44+
The fit procedure computes the expected time at which the signal should appear.
45+
To estimate the time of flight in the fitting a threshold is estimated to distinguish the noise from the signal, then
46+
the first point where the signal exceed this value is selected as the time of flight.
47+
6348

6449
Requirements
6550
^^^^^^^^^^^^
-58.1 KB
Binary file not shown.
-71.6 KB
Binary file not shown.

src/qibocal/protocols/signal_experiments/calibrate_state_discrimination.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
from qibocal.auto.operation import Data, Parameters, QubitId, Results, Routine
1212
from qibocal.calibration import CalibrationPlatform
1313

14+
from .utils import _get_lo_frequency
15+
1416
__all__ = ["calibrate_state_discrimination"]
1517

1618

@@ -34,16 +36,6 @@ class CalibrateStateDiscriminationParameters(Parameters):
3436
"""Custom dtype for CalibrateStateDiscrimination."""
3537

3638

37-
def _get_lo_frequency(platform: CalibrationPlatform, qubit: QubitId) -> float:
38-
"""Get LO frequency given QubitId.
39-
40-
Currently it assumes that instruments with LOs is first one.
41-
"""
42-
probe = platform.channels[platform.qubits[qubit].probe]
43-
lo_config = platform.config(probe.lo)
44-
return lo_config.frequency
45-
46-
4739
@dataclass
4840
class CalibrateStateDiscriminationResults(Results):
4941
"""Calibrate State Discrimination outputs."""

src/qibocal/protocols/signal_experiments/time_of_flight_readout.py

Lines changed: 75 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
from qibocal.result import magnitude
1313
from qibocal.update import replace
1414

15+
from .utils import _get_lo_frequency
16+
1517
__all__ = ["time_of_flight_readout"]
1618

1719
MINIMUM_TOF = 24
@@ -22,6 +24,8 @@
2224
class TimeOfFlightReadoutParameters(Parameters):
2325
"""TimeOfFlightReadout runcard inputs."""
2426

27+
detuning: float = 10e6
28+
"""Detuning with respect to corresponding LO frequency [Hz]."""
2529
readout_amplitude: Optional[int] = None
2630
"""Amplitude of the readout pulse."""
2731
window_size: Optional[int] = 10
@@ -36,16 +40,14 @@ class TimeOfFlightReadoutResults(Results):
3640
"""Raw fitting output."""
3741

3842

39-
TimeOfFlightReadoutType = np.dtype([("samples", np.float64)])
40-
41-
4243
@dataclass
4344
class TimeOfFlightReadoutData(Data):
4445
"""TimeOfFlightReadout acquisition outputs."""
4546

4647
windows_size: int
4748
sampling_rate: int
48-
49+
intermediate_frequency: float
50+
amplitude: dict[QubitId, float] = field(default_factory=dict)
4951
data: dict[QubitId, npt.NDArray] = field(default_factory=dict)
5052
"""Raw data acquired."""
5153

@@ -57,34 +59,47 @@ def _acquisition(
5759
) -> TimeOfFlightReadoutData:
5860
"""Data acquisition for time of flight experiment."""
5961
sequence = PulseSequence()
60-
ro_pulses = {}
6162
native = platform.natives.single_qubit
62-
ro_channels = []
6363
for qubit in targets:
6464
ro_channel, ro_pulse = native[qubit].MZ()[0]
65-
ro_channels.append(ro_channel)
6665
if params.readout_amplitude is not None:
67-
ro_pulse = replace(ro_pulse, amplitude=params.readout_amplitude)
68-
ro_pulses[qubit] = ro_pulse
66+
probe = replace(ro_pulse.probe, amplitude=params.readout_amplitude)
67+
ro_pulse = replace(ro_pulse, probe=probe)
6968
sequence.append((ro_channel, ro_pulse))
69+
7070
results = platform.execute(
7171
[sequence],
7272
nshots=params.nshots,
7373
relaxation_time=params.relaxation_time,
7474
acquisition_type=AcquisitionType.RAW,
7575
averaging_mode=AveragingMode.CYCLIC,
76-
updates=[{ro_channel: {"delay": MINIMUM_TOF} for ro_channel in ro_channels}],
76+
updates=[
77+
{
78+
platform.qubits[qubit].acquisition: {"delay": MINIMUM_TOF},
79+
platform.qubits[qubit].probe: {
80+
"frequency": _get_lo_frequency(platform, qubit) + params.detuning,
81+
},
82+
}
83+
for qubit in targets
84+
],
7785
)
7886

7987
data = TimeOfFlightReadoutData(
80-
windows_size=params.window_size, sampling_rate=platform.sampling_rate
88+
windows_size=params.window_size,
89+
sampling_rate=platform.sampling_rate,
90+
intermediate_frequency=params.detuning,
91+
amplitude={
92+
qubit: list(sequence.channel(platform.qubits[qubit].acquisition))[
93+
-1
94+
].probe.amplitude
95+
for qubit in targets
96+
},
8197
)
82-
8398
# retrieve and store the results for every qubit
8499
for qubit in targets:
85-
samples = magnitude(results[ro_pulses[qubit].id])
86-
# store the results
87-
data.register_qubit(TimeOfFlightReadoutType, (qubit), dict(samples=samples))
100+
acq_handle = list(sequence.channel(platform.qubits[qubit].acquisition))[-1].id
101+
data.data[qubit] = results[acq_handle]
102+
88103
return data
89104

90105

@@ -96,16 +111,27 @@ def _fit(data: TimeOfFlightReadoutData) -> TimeOfFlightReadoutResults:
96111

97112
window_size = data.windows_size
98113
sampling_rate = data.sampling_rate
99-
100114
for qubit in qubits:
101-
qubit_data = data[qubit]
102-
samples = qubit_data.samples
103-
window_size = int(len(qubit_data) / 10)
104-
th = (np.mean(samples[:window_size]) + np.mean(samples[:-window_size])) / 2
105-
delay = np.where(samples > th)[0][0]
106-
time_of_flight_readout = float(delay / sampling_rate + MINIMUM_TOF)
115+
delays = []
116+
for i in range(2):
117+
samples = data.data[qubit][:, i]
118+
window_size = int(len(samples) / 10)
119+
120+
for feat in ["min", "max"]:
121+
th = (
122+
getattr(np, feat)(samples[:window_size])
123+
+ getattr(np, feat)(samples[:-window_size])
124+
) / 2
125+
# try-expect in order to handle sporadic failing with mock
126+
try:
127+
delay = np.where(samples < th if feat == "min" else samples > th)[
128+
0
129+
][0]
130+
except IndexError:
131+
delay = 0
132+
delays.append(delay)
133+
time_of_flight_readout = float(min(delays) / sampling_rate + MINIMUM_TOF)
107134
time_of_flights[qubit] = time_of_flight_readout
108-
109135
return TimeOfFlightReadoutResults(time_of_flights)
110136

111137

@@ -117,16 +143,25 @@ def _plot(
117143
figures = []
118144
fitting_report = ""
119145
fig = go.Figure()
120-
qubit_data = data[target]
121146
sampling_rate = data.sampling_rate
122-
y = qubit_data.samples
147+
y = magnitude(data.data[target])
123148

124149
fig.add_trace(
125150
go.Scatter(
126151
x=np.arange(0, len(y)) * sampling_rate + MINIMUM_TOF,
127-
y=y,
152+
y=data.data[target][:, 0],
153+
textposition="bottom center",
154+
name="I",
155+
showlegend=True,
156+
legendgroup="group1",
157+
),
158+
)
159+
fig.add_trace(
160+
go.Scatter(
161+
x=np.arange(0, len(y)) * sampling_rate + MINIMUM_TOF,
162+
y=data.data[target][:, 1],
128163
textposition="bottom center",
129-
name="Expectation value",
164+
name="Q",
130165
showlegend=True,
131166
legendgroup="group1",
132167
),
@@ -145,7 +180,19 @@ def _plot(
145180
line_color="grey",
146181
)
147182
fitting_report = table_html(
148-
table_dict(target, "Time of flights [ns]", fit.time_of_flights[target])
183+
table_dict(
184+
target,
185+
[
186+
"Intermediate Frequency [Hz]",
187+
"Readout amplitude [a.u.]",
188+
"Time of flights [ns]",
189+
],
190+
[
191+
data.intermediate_frequency,
192+
data.amplitude[target],
193+
fit.time_of_flights[target],
194+
],
195+
)
149196
)
150197
fig.update_layout(
151198
showlegend=True,
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from ...auto.operation import QubitId
2+
from ...calibration import CalibrationPlatform
3+
4+
5+
def _get_lo_frequency(platform: CalibrationPlatform, qubit: QubitId) -> float:
6+
"""Get LO frequency given QubitId.
7+
8+
Currently it assumes that instruments with LOs is first one.
9+
"""
10+
probe = platform.channels[platform.qubits[qubit].probe]
11+
lo_config = platform.config(probe.lo)
12+
return lo_config.frequency

0 commit comments

Comments
 (0)