Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TWPA calibration using SNR #664

Merged
merged 25 commits into from
Feb 1, 2024
Merged

TWPA calibration using SNR #664

merged 25 commits into from
Feb 1, 2024

Conversation

DavidSarlle
Copy link
Contributor

@DavidSarlle DavidSarlle commented Dec 8, 2023

This Pr implements 2 routines for characterizing the TWPA frequency and power doing a resonator spectroscopy while modifying the values of frequency / power of the TWPA.

Checklist:

  • Reviewers confirm new code works as expected.
  • Tests are passing.
  • Coverage does not decrease.
  • Documentation is updated.
  • Compatibility with Qibo modules (Please edit this section if the current pull request is not compatible with the following branches).
    • Qibo: master
    • Qibolab: main
    • Qibolab_platforms_qrc: main

@DavidSarlle DavidSarlle changed the title implementing twpa calibration using SNR as a figuere of merit TWPA calibration using SNR Dec 8, 2023
Copy link

codecov bot commented Dec 8, 2023

Codecov Report

Attention: 10 lines in your changes are missing coverage. Please review.

Comparison is base (0e7a02b) 96.01% compared to head (fa86200) 96.00%.
Report is 17 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #664      +/-   ##
==========================================
- Coverage   96.01%   96.00%   -0.02%     
==========================================
  Files         106      108       +2     
  Lines        7356     7584     +228     
==========================================
+ Hits         7063     7281     +218     
- Misses        293      303      +10     
Flag Coverage Δ
unittests 96.00% <95.61%> (-0.02%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files Coverage Δ
src/qibocal/protocols/characterization/__init__.py 100.00% <100.00%> (ø)
...out_optimization/twpa_calibration/frequency_SNR.py 95.53% <95.53%> (ø)
...readout_optimization/twpa_calibration/power_SNR.py 95.53% <95.53%> (ø)

Copy link
Contributor

@andrea-pasquale andrea-pasquale left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @DavidSarlle.
The code looks more or less good to me.
The comments are mainly suggestions to have the code updated to the latest version.

Two important points:

  • It would be nice to perform a fitting, let me know if you need help with that.
  • Could you please add another entry for these new protocols here

src/qibocal/protocols/characterization/__init__.py Outdated Show resolved Hide resolved
Comment on lines +60 to +61
resonator_type: str
"""Resonator type."""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This attribute is not used anywhere

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will keep it because is going to be needed during the fitting.

Comment on lines 106 to 143
ro_pulses = {}
for qubit in qubits:
ro_pulses[qubit] = platform.create_qubit_readout_pulse(qubit, start=0)
sequence.add(ro_pulses[qubit])

data = ResonatorTWPAFrequencyData(platform.resonator_type)

# define the parameters to sweep and their range:
# resonator frequency
delta_frequency_range = np.arange(
-params.freq_width // 2, params.freq_width // 2, params.freq_step
)
freq_sweeper = Sweeper(
Parameter.frequency,
delta_frequency_range,
[ro_pulses[qubit] for qubit in qubits],
type=SweeperType.OFFSET,
)

# TWPAFrequency
TWPAFrequency_range = np.arange(
params.min_twpa_freq, params.max_twpa_freq, params.step_twpa_freq
)

for _freq in TWPAFrequency_range:
for z in qubits:
qubits[z].twpa.local_oscillator.frequency = _freq

results = platform.sweep(
sequence,
ExecutionParameters(
nshots=params.nshots,
relaxation_time=params.relaxation_time,
acquisition_type=AcquisitionType.INTEGRATION,
averaging_mode=AveragingMode.CYCLIC,
),
freq_sweeper,
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you are basically running a resonator spectroscopy varying the twpa you can just call directly the resonator spectroscopy protocol here. Like we did in the twpa optimization protocols by calling directly the classification protocol

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Contributor

@andrea-pasquale andrea-pasquale left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @DavidSarlle for all the updates.
I have a few more suggestions.
To fix coverage could you the two new protocols in this runcard ?

for qubit in qubits:
data_qubit = data[qubit]
if data.resonator_type == "3D":
print("3D")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
print("3D")

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove all the print

@@ -196,5 +196,5 @@ def _update(results: ResonatorSpectroscopyResults, platform: Platform, qubit: Qu
update.bare_resonator_frequency(results.bare_frequency[qubit], platform, qubit)


resonator_spectroscopy = Routine(_acquisition, _fit, _plot, _update)
resonator_spec = Routine(_acquisition, _fit, _plot, _update)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I ask why you renamed resonator_spectroscopy in resonator_spec?

Copy link
Contributor Author

@DavidSarlle DavidSarlle Jan 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name of the object "Routine" created in the in the "resonator_spectroscopy" class and the class itself have the same name. This is not a problem until you import that class from a different one. Python understands that you are importing the object "Routine" instead of the class. So, we can not use the dataclasses needed from the class "resonator_spectroscopy" like "ResonatorSpectroscopyParameters" during the twpa routines. Same with the private methods like "_acquisition" (but this is a minor problem because the Routine object inherits the methods needed)

@DavidSarlle
Copy link
Contributor Author

Thanks @DavidSarlle for all the updates. I have a few more suggestions. To fix coverage could you the two new protocols in this runcard ?

Done

@DavidSarlle
Copy link
Contributor Author

@andrea-pasquale the tests are not passing, can you help me with that?

for qubit in qubits:
data_qubit = data[qubit]
if data.resonator_type == "3D":
print("3D")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove all the print

Comment on lines 250 to 275
if fit is not None:
if qubit in fit.bare_frequency:
summary = table_dict(
qubit,
[
"High Power Resonator Frequency [Hz]",
"TWPA Frequency [Hz]",
],
[
np.round(fit.bare_frequency[qubit]),
np.round(fit.twpa_frequency[qubit]),
],
)
else:
summary = table_dict(
qubit,
[
"Low Power Resonator Frequency [Hz]",
"TWPA Frequency [Hz]",
],
[
np.round(fit.frequency[qubit]),
np.round(fit.twpa_frequency[qubit]),
],
)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If possible, try not to repeat code, i.e. abstract all the common parts, or define a function collecting what inside the branches.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

size = len(freq)
ar = np.empty(size, dtype=ResonatorTWPAPowerType)
ar["freq"] = freq
ar["twpa_pow"] = np.array([twpa_pow] * size)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add an attribute


fitting_report = ""
qubit_data = data[qubit]
resonator_frequencies = qubit_data.freq * HZ_TO_GHZ
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
resonator_frequencies = qubit_data.freq * HZ_TO_GHZ
resonator_frequencies = qubit_data.freq

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed conversion

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I don't understand why @Edoardo-Pedicillo asked you to remove the conversion. I suggest to perform the conversion mainly because I don't want to see bilions of Hz in the x axis. I already performed this change in fa86200

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I made a mistake

Comment on lines 174 to 179
if data.resonator_type == "3D":
index_best_pow = np.argmax(data_qubit["signal"])
twpa_power[qubit] = data_qubit["twpa_pow"][index_best_pow]
else:
index_best_pow = np.argmin(data_qubit["signal"])
twpa_power[qubit] = data_qubit["twpa_pow"][index_best_pow]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if data.resonator_type == "3D":
index_best_pow = np.argmax(data_qubit["signal"])
twpa_power[qubit] = data_qubit["twpa_pow"][index_best_pow]
else:
index_best_pow = np.argmin(data_qubit["signal"])
twpa_power[qubit] = data_qubit["twpa_pow"][index_best_pow]
if data.resonator_type == "3D":
index_best_pow = np.argmax(data_qubit["signal"])
else:
index_best_pow = np.argmin(data_qubit["signal"])
twpa_power[qubit] = data_qubit["twpa_pow"][index_best_pow]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented

Comment on lines 88 to 99
def register_qubit(self, qubit, freq, twpa_freq, signal, phase):
"""Store output for single qubit."""
size = len(freq)
ar = np.empty(size, dtype=ResonatorTWPAFrequencyType)
ar["freq"] = freq
ar["twpa_freq"] = np.array([twpa_freq] * size)
ar["signal"] = signal
ar["phase"] = phase
if qubit in self.data:
self.data[qubit] = np.rec.array(np.concatenate((self.data[qubit], ar)))
else:
self.data[qubit] = np.rec.array(ar)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def register_qubit(self, qubit, freq, twpa_freq, signal, phase):
"""Store output for single qubit."""
size = len(freq)
ar = np.empty(size, dtype=ResonatorTWPAFrequencyType)
ar["freq"] = freq
ar["twpa_freq"] = np.array([twpa_freq] * size)
ar["signal"] = signal
ar["phase"] = phase
if qubit in self.data:
self.data[qubit] = np.rec.array(np.concatenate((self.data[qubit], ar)))
else:
self.data[qubit] = np.rec.array(ar)

The class Data has a default register_qubit method, and it seems that it is not different from the one you implemented.

def register_qubit(self, dtype, data_keys, data_dict):
"""Store output for single qubit.
Args:
data_keys (tuple): Keys of Data.data.
data_dict (dict): The keys are the fields of `dtype` and
the values are the related arrays.
"""
# to be able to handle the non-sweeper case
ar = np.empty(np.shape(data_dict[list(data_dict.keys())[0]]), dtype=dtype)
for key, value in data_dict.items():
ar[key] = value
if data_keys in self.data:
self.data[data_keys] = np.rec.array(
np.concatenate((self.data[data_keys], ar))
)
else:
self.data[data_keys] = np.rec.array(ar)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Implemented

@DavidSarlle
Copy link
Contributor Author

@Edoardo-Pedicillo all your comments has been addressed. Thanks for the review.

Copy link
Contributor

@andrea-pasquale andrea-pasquale left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for all the updates @DavidSarlle.
I believe we are ready to merge.
Just before merging...
Regarding this:

The name of the object "Routine" created in the in the "resonator_spectroscopy" class and the class itself have the same name. This is not a problem until you import that class from a different one. Python understands that you are importing the object "Routine" instead of the class. So, we can not use the dataclasses needed from the class "resonator_spectroscopy" like "ResonatorSpectroscopyParameters" during the twpa routines. Same with the private methods like "_acquisition" (but this is a minor problem because the Routine object inherits the methods needed)
I tried to replace resonator_spec with resonator_spectroscopy and everything worked as expected. If you want I can push the change directly here.

@DavidSarlle
Copy link
Contributor Author

Thanks for all the updates @DavidSarlle. I believe we are ready to merge. Just before merging... Regarding this:

The name of the object "Routine" created in the in the "resonator_spectroscopy" class and the class itself have the same name. This is not a problem until you import that class from a different one. Python understands that you are importing the object "Routine" instead of the class. So, we can not use the dataclasses needed from the class "resonator_spectroscopy" like "ResonatorSpectroscopyParameters" during the twpa routines. Same with the private methods like "_acquisition" (but this is a minor problem because the Routine object inherits the methods needed)
I tried to replace resonator_spec with resonator_spectroscopy and everything worked as expected. If you want I can push the change directly here.

ok. push the change please.
Thanks!

@andrea-pasquale
Copy link
Contributor

Thanks for all the updates @DavidSarlle. I believe we are ready to merge. Just before merging... Regarding this:

The name of the object "Routine" created in the in the "resonator_spectroscopy" class and the class itself have the same name. This is not a problem until you import that class from a different one. Python understands that you are importing the object "Routine" instead of the class. So, we can not use the dataclasses needed from the class "resonator_spectroscopy" like "ResonatorSpectroscopyParameters" during the twpa routines. Same with the private methods like "_acquisition" (but this is a minor problem because the Routine object inherits the methods needed)
I tried to replace resonator_spec with resonator_spectroscopy and everything worked as expected. If you want I can push the change directly here.

ok. push the change please. Thanks!

Actually it was not as easy as I expected. Strangely only pylint complains but not pytest. I've also tested the code and it works as expected. I manage to fix it but by importing directly the resonator_spectroscopy routine object. In this way pylint does not complain.

@andrea-pasquale andrea-pasquale added this pull request to the merge queue Feb 1, 2024
Merged via the queue into main with commit a5217a3 Feb 1, 2024
20 of 21 checks passed
@andrea-pasquale andrea-pasquale deleted the david/twpa_SNR branch February 1, 2024 12:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants