-
Notifications
You must be signed in to change notification settings - Fork 17
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
Patch RFSoC result shape #914
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #914 +/- ##
==========================================
+ Coverage 66.90% 66.97% +0.06%
==========================================
Files 58 58
Lines 5983 6001 +18
==========================================
+ Hits 4003 4019 +16
- Misses 1980 1982 +2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
There were several issues with the shapes returned by different drivers in the past, and several attempts to fix them, so I do not recall which could be the one that broke rfsoc. I am also not sure what is the status right now, other than that dummy and QM were working, at least as qibocal expects, with the latest main. I believe we have also fixed ZI accordingly, but maybe @andrea-pasquale has more info on other instruments.
I think the tests in https://github.com/qiboteam/qibolab/blob/main/tests/test_result_shapes.py were introduced to exactly check the shapes and I remember having them passing with different instruments, but probably we never tried rfsoc. Do these tests work for you? If there is a case that is not captured by them maybe we need to extend them. You cannot run them in GitHub CI, but if you have access to a platform, you can still run manually as pytest -m qpu --no-cov --platform {platform_name} tests/test_result_shapes.py |
@stavros11 is right, and recently there has been some rework in ZI for that. I inspected some of these issues after qiboteam/qibocal#737, and I reported in there (e.g. qiboteam/qibocal#737 (comment)). Summary: this was triggered by the single shot acquisition in order to implement standard deviations' calculation, that added one more dimension to many routines. In principle, the results should just be an array, with a shape of the kind We will to reassess anyhow in 0.2, because this will have an interplay with the diagonal(/simultaneous/...) sweepers. Though, if everything is already properly implemented, it should be a smooth extension. |
Ok thanks @stavros11 and @alecandido! I will run |
for pulse_idx in range(kdx + 1, len(sequence)): | ||
sequence[pulse_idx].start = sequence[pulse_idx - 1].duration |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is slightly better than the former patch, because it is less hard-coded for the Rabi.
However, it is still a quick and dirty patch, because it would advance even pulses that should be played in parallel.
E.g.:
drive (start=0, duration swept)
flux (start=0)
readout (start=drive.duration)
not only the readout pulse is advanced, but even the flux.
The proper fix for this is one of these:
- diagonal (simultaneous) sweeper, and sweep not only the duration of the drive, but even the measurement start
- add a delay on the readout line, and you can sweep the duration of the drive and readout delay in the same sweeper (this should be fixed in 0.2, if we'll have simultaneous sweepers, better to use one pulse per sweeper @stavros11)
- add an
align()
between the drive and readout pulse
Since the diagonal sweepers and aligns are not yet supported by Qibolab, the only viable solution is 2., assuming you could mock a readout delay (like adding a readout pulse with 0 amplitude, and then discarding the result), but it would require to change the Qibocal routine.
To be fair, the Qibocal routine should be changed in any case, since the sweeper is just on the duration, and it is never specified that you should advance the reaodut pulse as well, so it might be up to the drivers to push measurements at the end, and it would be wrong (because in general, you should be free of playing the readout wherever you wish).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be fair, the Qibocal routine should be changed in any case, since the sweeper is just on the duration, and it is never specified that you should advance the reaodut pulse as well, so it might be up to the drivers to push measurements at the end, and it would be wrong (because in general, you should be free of playing the readout wherever you wish).
@andrea-pasquale is it correct? In case, I'll open an issue on Qibocal
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I believe that this is the case for the majority of protocols involving duration sweepers. Right now qibocal is leveraging the start
attribute to hint at the fact that the readout should be pushed.
https://github.com/qiboteam/qibocal/blob/e66294c038aa42409160eae13fe7aca5641cde63/src/qibocal/protocols/rabi/utils.py#L265-L267
I know that in qibolab 0.2 start
will be dropped, so in any case all this business related to the readout pulses being pushed when a previous pulse is swept will be addressed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now qibocal is leveraging the
start
attribute to hint at the fact that the readout should be pushed.
The problem is that is not truly a hint, since the official specification of that would be that is exactly starting at that time (the .finish
of the previous pulse) that is a number even in the case in which the other pulse is swept.
Pulses swept are normal pulses in the sequence, then the sweeper will later change its attributes, but there is no link with other pulses with accidentally the same values (or related ones) for same or other properties (like pulse1.finish == pulse2.start
is a relation for the original sequence, but it is not checked in any way when the sweeper will modify the value of pulse1.duration
, and thus its end).
Improve? xD |
All these tests now pass, with the exception of the ones involving "statistical_frequency" that I'm not sure how should behave |
It appeared about one year ago in a merge commit f43c401 by @Jacfomg. But maybe is just a merge mess, and it's actually coming from someone else... Do you remember what's its meaning? (the only other person who may remember is @stavros11) If no one knows, we could even schedule for removal... |
I believe it was me, but the naming convention was someone else. It is supposed to be for averaged results and it should be the just a number that it is the average of the shots or list of numbers in the case of a sweep made also from the average of the shots. |
But then, do you agree it could be a property? I.e. it can always be recomputed from the |
This seems to me a bit more reasonable that what we have now |
If that's the case, I can make a PR in Qibolab 0.1 to replace it (it should be fairly easy to replace in all drivers). |
Is this about the qibolab/tests/test_result_shapes.py Line 61 in f335e4d
this is used when using (AcquisitionType.DISCRIMINATION, AveragingMode.CYCLIC) and if I interpret this correctly, in that case you will never get back all the samples from the instrument, but only their average value. Therefore, I am not sure if you can make it a property and recompute it from .samples . If that's correct, then I am not sure why AveragedSampleResults also accepts a samples argument.
As for the name, I don't know where it came from, but I believe |
Indeed, the point is that you should require only one between
Fine with me for the new name. Also note that in Qibocal this attribute is never used: Lines 185 to 189 in f335e4d
(so statistical_frequency is essentially the probability of the 1 state)
|
This property already exists here: Line 151 in f335e4d
and calculates the I think with the current design of results, the only inconsistency is that |
for serial in dict_b: | ||
if serial in dict_a: | ||
cls = dict_a[serial].__class__ | ||
if isinstance(dict_a[serial], IntegratedResults): | ||
new_data = np.column_stack( | ||
[dict_a[serial].voltage, dict_b[serial].voltage] | ||
) | ||
elif isinstance(dict_a[serial], SampleResults): | ||
new_data = np.append(dict_a[serial].samples, dict_b[serial].samples) | ||
dict_a[serial] = cls(new_data) | ||
data = lambda res: ( | ||
res.voltage if isinstance(res, IntegratedResults) else res.samples | ||
) | ||
dict_a[serial] = type(dict_a[serial])( | ||
np.append(data(dict_a[serial]), data(dict_b[serial])) | ||
) | ||
else: | ||
dict_a[serial] = dict_b[serial] | ||
return dict_a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not this PR business, but all this function would be just an np.append
(or np.stack
, along the out-enough dimension) if the results' objects were simple NumPy arrays...
(possibly even just an np.append
, and later reshape/transpose...)
The code is still a bit messy, but the main problem here is the result objects... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small comment about static methods, but I'm ok with the fix.
Thank you very much! @rodolfocarobene
@staticmethod | ||
def validate_input_command( | ||
sequence: PulseSequence, execution_parameters: ExecutionParameters, sweep: bool | ||
): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here and above, since these methods does not make of self
(as all @staticmethod
s) you could even consider keeping them out of the class.
To me, the static methods are useful to bring a function together with a class, such that it is available whenever a class is available.
If, instead, you want to have a function to be used inside the class, then just define a separate function in the same module (and, if it is to be used only by that class, prepend an _
to its name :P).
Thanks for the review @alecandido! Maybe after we finish the measures in our lab I can try to fix some coding style issues in another PR. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @rodolfocarobene. I am not sure if we have any platform available to test this here, so I just trust it works.
Closes #913. The patch is quite ugly, moreover I am not sure if it works for every case... I've tested it only for a single qubit sweep (readout frequency).
I guess at some point it was introduced that the results must be squeezed like for dummy:
qibolab/src/qibolab/instruments/dummy.py
Lines 130 to 134 in 2e6c082
I was instead always returning values in the form of
[[val1, val2, val3, val4...]]
(not sure why, maybe it was wrong from the beginning). However, downgrading qibolab at my last commit and qibocal at 0.0.8 "solves" the issue, so maybe something was introduced in the middle? Do you, @stavros11, @alecandido , @hay-k, recall something specific?Moreover, are there plans to introduce again some automatic tests with instruments? I wanted to add a specific test that could have caught this problem before
Checklist: