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

RX90 calibration implementation #1044

Open
wants to merge 69 commits into
base: 0.2
Choose a base branch
from
Open

RX90 calibration implementation #1044

wants to merge 69 commits into from

Conversation

ElStabilini
Copy link
Contributor

@ElStabilini ElStabilini commented Nov 20, 2024

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

Copy link

codecov bot commented Nov 20, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 97.05%. Comparing base (4a1251a) to head (af82fec).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##              0.2    #1044      +/-   ##
==========================================
+ Coverage   97.04%   97.05%   +0.01%     
==========================================
  Files          98       98              
  Lines        7893     7958      +65     
==========================================
+ Hits         7660     7724      +64     
- Misses        233      234       +1     
Flag Coverage Δ
unittests 97.05% <100.00%> (+0.01%) ⬆️

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

Files with missing lines Coverage Δ
src/qibocal/protocols/flipping.py 98.64% <100.00%> (+0.18%) ⬆️
src/qibocal/protocols/rabi/amplitude.py 96.87% <100.00%> (+0.10%) ⬆️
src/qibocal/protocols/rabi/amplitude_frequency.py 97.95% <100.00%> (+0.02%) ⬆️
...bocal/protocols/rabi/amplitude_frequency_signal.py 98.46% <100.00%> (+0.08%) ⬆️
src/qibocal/protocols/rabi/amplitude_signal.py 97.59% <100.00%> (+0.18%) ⬆️
src/qibocal/protocols/rabi/ef.py 100.00% <100.00%> (ø)
src/qibocal/protocols/rabi/length.py 96.20% <100.00%> (+0.09%) ⬆️
src/qibocal/protocols/rabi/length_frequency.py 97.05% <100.00%> (+0.02%) ⬆️
.../qibocal/protocols/rabi/length_frequency_signal.py 97.79% <100.00%> (+0.11%) ⬆️
src/qibocal/protocols/rabi/length_signal.py 96.59% <100.00%> (+0.24%) ⬆️
... and 2 more

... and 2 files with indirect coverage changes

@ElStabilini
Copy link
Contributor Author

I attach here some reports where I tested the code:

@andrea-pasquale
Copy link
Contributor

Thanks @ElStabilini, the results seem to make sense since for both amplitude and duration there is a factor 2. I think that rather than changing the protocol completly we could consider adding a flag to specify whether we want to calibrate the RX or RX90 pulse. We should also check the corresponding update function in the protocols to be consistent.
Eventually we can do the same for both the flipping and the drag calibration. For now let's finish first rabi.

src/qibocal/update.py Outdated Show resolved Hide resolved
src/qibocal/protocols/rabi/length_signal.py Outdated Show resolved Hide resolved
src/qibocal/protocols/rabi/length_frequency_signal.py Outdated Show resolved Hide resolved
@ElStabilini
Copy link
Contributor Author

ElStabilini commented Nov 21, 2024

Thanks @ElStabilini, the results seem to make sense since for both amplitude and duration there is a factor 2. I think that rather than changing the protocol completly we could consider adding a flag to specify whether we want to calibrate the RX or RX90 pulse. We should also check the corresponding update function in the protocols to be consistent. Eventually we can do the same for both the flipping and the drag calibration. For now let's finish first rabi.

@andrea-pasquale I see this only now, I should have done this by adding the pihalf_pulse (soon rx90) option, correct?
Regarding routines, should I change them even though I added a new RX90 native gate and left untouched the RX?

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 the updates @ElStabilini.
At this point I would rename everywhere pihalf_pulse to rx90.
Everything else looks good to me.

doc/source/protocols/rabi/rabi.rst Outdated Show resolved Hide resolved
Comment on lines 127 to 128
update.drive_12_amplitude(
results.amplitude[target], results.pihalf_pulse, platform, target
Copy link
Contributor

Choose a reason for hiding this comment

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

Here we should force the user to calibrate just with RX, given that we have no RX1290 pulse.

Copy link
Contributor Author

@ElStabilini ElStabilini Nov 22, 2024

Choose a reason for hiding this comment

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

I should have fixed this too, in this commit 349e312.

If the user sets rx90: True it will raise an error

Comment on lines 127 to 128
relaxation_time: 50000
RX90: False
Copy link
Contributor

Choose a reason for hiding this comment

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

This line needs to be fixed.

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 should have fixed it and also added the figure

@andrea-pasquale andrea-pasquale changed the title RX$\pi/2$ calibration implementation RX90 calibration implementation Nov 21, 2024
Merge branch 'pi_half' of github.com:qiboteam/qibocal into pi_half
@andrea-pasquale
Copy link
Contributor

Regarding routines, should I change them even though I added a new RX90 native gate and left untouched the RX?

Just to avoid too many things at the same time I suggest to focus first on rabi experiments. In another PR we can propagate these changes to the other protocols.

@alecandido
Copy link
Member

alecandido commented Nov 27, 2024

Someone noticed a certain streak of pretty close commit messages (once in a while, I was not the first one - I won't take this merit :P).

Commit messages are extremely useful for debugging, to navigate the commits in case something goes wrong. So, ideally, they should describe in the most succinct and expressive way the commits' changes.
Think about searching for some change by yourself, which you don't remember exactly when and where it happened, or if you need to reprocess commits (e.g. during a rebase).

Of course, sometimes you need to push to debug the workflow. But this is better done with a local reproduction as much as possible. And, if really needed, possibly even squashing some failed attempts afterward, to leave a better history for later :)

Comment on lines 239 to 242
if data.rx90:
pulse_name = "Pi-half pulse"
else:
pulse_name = "Pi pulse"
Copy link
Member

Choose a reason for hiding this comment

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

In these cases, it is especially convenient to use the ternary if operator (exact same of the ... ? ... : ... construct in C)

Suggested change
if data.rx90:
pulse_name = "Pi-half pulse"
else:
pulse_name = "Pi pulse"
pulse_name = "Pi-half pulse" if data.rx90 else "Pi pulse"

@alecandido
Copy link
Member

@ElStabilini just to provide you some context about #1044 (comment), the clear advantage of the proposed syntax is neither saving characters nor lines.

The most prominent difference between the equivalent expressions is that one is imperative, the other functional.
In the first one, you or conditioning the entire assignment operation, as the process of defining a variable name and its associated value. In principle, you need to analyze both branches of the conditional to ensure that the variable (pulse_name in this case) is even defined.
Instead, when using the ternary operation you are not conditioning the assignment as an operation, which becomes just an alias for a given computation. The computation is an expression, that always has a value. But it's the value itself the one being conditioned.

The functional approach makes the computation flow clearer, since it is somehow more rigid (you're not even thinking about operations, and possibly memory, but just values manipulation), and makes the analysis simpler and more effective. In languages which are actually functional, this may empower the compiler to act more aggressively. In Python, it makes the linters more effective[*], leading to fewer errors and more helpful messages :)

[*]: since it optimizes the simplicity of the code, it makes it also more human-readable in general

@ElStabilini
Copy link
Contributor Author

@ElStabilini just to provide you some context about #1044 (comment), the clear advantage of the proposed syntax is neither saving characters nor lines.

The most prominent difference between the equivalent expressions is that one is imperative, the other functional. In the first one, you or conditioning the entire assignment operation, as the process of defining a variable name and its associated value. In principle, you need to analyze both branches of the conditional to ensure that the variable (pulse_name in this case) is even defined. Instead, when using the ternary operation you are not conditioning the assignment as an operation, which becomes just an alias for a given computation. The computation is an expression, that always has a value. But it's the value itself the one being conditioned.

The functional approach makes the computation flow clearer, since it is somehow more rigid (you're not even thinking about operations, and possibly memory, but just values manipulation), and makes the analysis simpler and more effective. In languages which are actually functional, this may empower the compiler to act more aggressively. In Python, it makes the linters more effective[*], leading to fewer errors and more helpful messages :)

[*]: since it optimizes the simplicity of the code, it makes it also more human-readable in general

It definitely makes sense, thank you!

@ElStabilini ElStabilini marked this pull request as ready for review December 3, 2024 15:31
Copy link
Member

@alecandido alecandido left a comment

Choose a reason for hiding this comment

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

Generally good, and pretty much ready to go.

The main suggestions are about the docs. The other ones are very minor refactor requests, which could even be neglected.

Comment on lines +44 to +50
- id: flipping
operation: flipping
parameters:
delta_amplitude: 0.05
nflips_max: 30
nflips_step: 1
rx90: True
Copy link
Member

Choose a reason for hiding this comment

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

Try to indent with spaces, not tabs (you can set your editor to do that)

pulse_length: 40
nshots: 1024
relaxation_time: 50000
rx90: True
Copy link
Member

Choose a reason for hiding this comment

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

Another tab here ^^

Comment on lines +109 to +112
In all the previous examples we run Rabi experiments for calibrating the amplitude (duration) of the drive pulse
to excite the qubit from the ground state up to state :math:`\ket{1}`.
All these example runcards can be modified to calibrate the amplitude (duration) of the drive pulse
to excite the qubit from the ground state up to state :math:`\frac{\ket{0}-i\ket{1}}{\sqrt{2}}` by simply setting the `rx90` parameter to `True`.
Copy link
Member

Choose a reason for hiding this comment

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

Instead of mentioning the experiment in the general section, and describe it only in the example, you may move this description up, and just cite here you're performing the pi/2 described above.

Copy link
Member

Choose a reason for hiding this comment

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

Moreover, a description of the sequence (i.e. "same, but replace each pi with two pi/2") and a brief motivation may be appreciated. Then, you may cite this section in the flipping, instead of repeating it.

Copy link
Member

Choose a reason for hiding this comment

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

Even a reference to the literature you consulted may be appreciated as well :)

Comment on lines +37 to +66
if rx90:
sequence |= natives.RX90()

qd_channel, rx_pulse = natives.RX()[0]
for _ in range(flips):
qd_channel, qd_pulse = natives.RX90()[0]

rx_detuned = update.replace(
rx_pulse, amplitude=rx_pulse.amplitude + delta_amplitude
)
sequence.append((qd_channel, rx_detuned))
sequence.append((qd_channel, rx_detuned))
qd_detuned = update.replace(
qd_pulse, delta_amplitude=qd_pulse.amplitude + delta_amplitude
)

sequence.append((qd_channel, qd_detuned))
sequence.append((qd_channel, qd_detuned))
sequence.append((qd_channel, qd_detuned))
sequence.append((qd_channel, qd_detuned))

sequence |= natives.MZ()

else:
sequence |= natives.R(theta=np.pi / 2)

sequence |= natives.MZ()
for _ in range(flips):
qd_channel, qd_pulse = natives.RX()[0]

qd_detuned = update.replace(
qd_pulse, amplitude=qd_pulse.amplitude + delta_amplitude
)
sequence.append((qd_channel, qd_detuned))
sequence.append((qd_channel, qd_detuned))

sequence |= natives.MZ()
Copy link
Member

Choose a reason for hiding this comment

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

A good chunk of code is duplicated between the two branches. You may try to scope further the if within the sequence definition, to minimize duplication.

(In general, there is no exact solution about how to make code "minimal" or "simplest" - but here there is some room for improvements ^^)

@@ -222,10 +254,14 @@ def _fit(data: FlippingData) -> FlippingResults:
perr = np.sqrt(np.diag(perr)).tolist()
popt = popt.tolist()
correction = popt[2] / 2

if data.rx90:
correction = correction / 2
Copy link
Member

Choose a reason for hiding this comment

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

In some other contexts, there is a difference between redefinition and in-place mutation, and the distinction may be very relevant.

However, in this case it's just about a single scalar, so it makes no difference (to the point that a compiler may optimize the operation anyhow). So, better to use the idiomatic shortcut ;)

Suggested change
correction = correction / 2
correction /= 2

Comment on lines +152 to +156
qubit: (
platform.natives.single_qubit[qubit].RX90[0][1].amplitude
if params.rx90
else platform.natives.single_qubit[qubit].RX[0][1].amplitude
)
Copy link
Member

Choose a reason for hiding this comment

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

Ok, this is just for fun, and at the level of code golf. But just for you to know that such an option exists

Suggested change
qubit: (
platform.natives.single_qubit[qubit].RX90[0][1].amplitude
if params.rx90
else platform.natives.single_qubit[qubit].RX[0][1].amplitude
)
qubit: getattr(platform.natives.single_qubit[qubit], "RX90" if params.rx90 else "RX")[0][1].amplitude

Comment on lines +246 to +249
if rx90:
qd_channel, qd_pulse = natives.RX90()[0]
else:
qd_channel, qd_pulse = natives.RX()[0]
Copy link
Member

Choose a reason for hiding this comment

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

In case you like code golf as well, you may try to deduplicate even this one (but it's absolutely not needed)

extract_rabi(RabiAmplitudeEFData)


def test_extract_rabi():
Copy link
Member

Choose a reason for hiding this comment

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

This is redefining the former, thus shadowing it.

You have to use two different names in order to be able to access both, and thus also to run both in Pytest.

While a redefinition is not an error, it is unlikely that you want to define a function that already exists. Consequently, your editor may have warned you about this (or better, Ruff should have done it).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants