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

Kernel class #758

Merged
merged 34 commits into from
Jan 26, 2024
Merged

Kernel class #758

merged 34 commits into from
Jan 26, 2024

Conversation

Jacfomg
Copy link
Contributor

@Jacfomg Jacfomg commented Jan 16, 2024

Follows what discussed in #754. The only thing I removed the kernel from the qubit repr

Checklist:

  • Reviewers confirm new code works as expected.
  • Tests are passing.
  • Coverage does not decrease.
  • Documentation is updated.

Copy link

codecov bot commented Jan 16, 2024

Codecov Report

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

Comparison is base (3063105) 63.86% compared to head (8f49f89) 63.97%.

Files Patch % Lines
src/qibolab/instruments/zhinst.py 66.66% 2 Missing ⚠️
src/qibolab/__init__.py 83.33% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #758      +/-   ##
==========================================
+ Coverage   63.86%   63.97%   +0.10%     
==========================================
  Files          47       49       +2     
  Lines        5756     5773      +17     
==========================================
+ Hits         3676     3693      +17     
  Misses       2080     2080              
Flag Coverage Δ
unittests 63.97% <94.00%> (+0.10%) ⬆️

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

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

src/qibolab/dummy.py Outdated Show resolved Hide resolved
src/qibolab/kernels.py Outdated Show resolved Hide resolved
src/qibolab/dummy.py Outdated Show resolved Hide resolved
src/qibolab/kernels.py Outdated Show resolved Hide resolved
alecandido
alecandido previously approved these changes Jan 16, 2024
src/qibolab/kernels.py Outdated Show resolved Hide resolved
src/qibolab/serialize.py Outdated Show resolved Hide resolved
src/qibolab/serialize.py Outdated Show resolved Hide resolved
src/qibolab/serialize.py Outdated Show resolved Hide resolved
src/qibolab/serialize.py Outdated Show resolved Hide resolved
tests/dummy_qrc/zurich.py Outdated Show resolved Hide resolved
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 @Jacfomg.
Apart from some minor changes this PR should be ready to merge.

src/qibolab/instruments/zhinst.py Outdated Show resolved Hide resolved
src/qibolab/instruments/zhinst.py Outdated Show resolved Hide resolved
src/qibolab/instruments/zhinst.py Outdated Show resolved Hide resolved
src/qibolab/kernels.py Outdated Show resolved Hide resolved
single_qubit = runcard["characterization"]["single_qubit"]
for qubit in qubits.values():
qubit.kernel_path = extras_folder / single_qubit[qubit.name]["kernel_path"]
kernels = Kernels.load(path=extras_folder / "kernels.npz")
Copy link
Contributor

Choose a reason for hiding this comment

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

I would define "kernels.npz" above a top level variable.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When using it I found here that I would like

Kernels(kernels).dump(Path(__file__).parent / "dummy" / KERNELS_FILE)

to be something like

Kernels(kernels).dump(Path(__file__).parent / platform.name / KERNELS_FILE)

but for some reason we it can be named "dummy" or "dummy_couplers" should we join them ?

src/qibolab/serialize.py Outdated Show resolved Hide resolved
src/qibolab/serialize.py Outdated Show resolved Hide resolved
src/qibolab/dummy.py Outdated Show resolved Hide resolved
single_qubit = runcard["characterization"]["single_qubit"]
for qubit in qubits.values():
qubit.kernel_path = extras_folder / single_qubit[qubit.name]["kernel_path"]
kernels = Kernels.load(path=extras_folder / KERNELS_FILE)
Copy link
Member

Choose a reason for hiding this comment

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

At this stage extras_folder is used only for the kernels. Why don't we accept the whole kernel path (including the file name) instead of hardcoding to "kernels.npz"? Maybe I want to use a different name for my kernel file, or play with different kernel files for the same platform.

Copy link
Member

Choose a reason for hiding this comment

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

I guess it's really a matter of taste.

But you can do the things you asked even with the current strategy. Just have multiple kernel files inside the extras_folder, and rename those to kernels.npz.

About the idea of replacing a folder with a file, since there is nothing else, in general I'd agree with you. But here it would be already better to have a single file per runcard (to avoid spreading them), and we start from two (the .py and .yml).

Copy link
Member

@alecandido alecandido Jan 19, 2024

Choose a reason for hiding this comment

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

Maybe I have a counterproposal: what if we keep the directory, but we move all the files in there?

I.e.

iqm5q/
  platform.py
  parameters.yml
  kernels.npz

At least it would be easy to collect all the related files, and in case you want to preserve/circulate a platform, you could always tar/zip the folder and grab it as a single file.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It seemed more future proof as we may need more stuff to be in that folder, and also what @alecandido mentioned would be more organized so we can do it on another PR on the qrc_cluster platforms.

Copy link
Member

Choose a reason for hiding this comment

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

iqm5q/
  platform.py
  parameters.yml
  kernels.npz

In my opinion, this is the best solution, but I agree doing it in another PR because it requires a few more changes which are unrelated to the kernels.

Copy link
Contributor

Choose a reason for hiding this comment

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

@alecandido @stavros11 @Jacfomg if everyone agrees I can open a PR in qibocal to generate directly folders and not just platform runcards. I believe that it should be feasible. It should be useful for the qibocal user to be able to see directly the create_function alongside the parameters and the kernels.

Copy link
Member

@stavros11 stavros11 Jan 22, 2024

Choose a reason for hiding this comment

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

Indeed, it would be useful. I would just wait to do it after we implement the structure proposed above (one directory per platform) in qibolab and in the platforms. Also, I think it makes more sense to implement the function you are proposing in qibolab, perhaps replacing the current dump_runcard and just use it qibocal, because at the end of the day it solves the problem of platform serialization which is a qibolab problem. However, this is a bit orthogonal to #762, so let’s wait to see what others think.

Copy link
Contributor

Choose a reason for hiding this comment

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

You are right. The function should be in qibolab. I will wait.

Copy link
Member

Choose a reason for hiding this comment

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

I agree with you that we should try to roll it out, and we should try to do it in Qibolab.
Btw, if it's going through Qibolab it should be also a non-breaking change, since Qibocal should not care much about the runcard serialization structure.

However, this is a bit orthogonal to #762, so let’s wait to see what others think.

I would say orthogonal is the correct word, not opposite. The moment we implement the folder, we could well dump the parameters in JSON, and drop the PyYAML dependency as well.

src/qibolab/serialize.py Outdated Show resolved Hide resolved
if qubit.kernel is not None:
kernels[qubit.name] = qubit.kernel
qubit.kernel = None
Kernels(kernels).dump(Path(__file__).parent / "dummy" / KERNELS_FILE)
Copy link
Member

Choose a reason for hiding this comment

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

I think this method is used by all platforms. Why are we always dumping in the directory of the dummy?

I think this point requires some discussion. If you want to always dump the kernels in the file you loaded them from, it is probably convenient to make path an attribute of Kernels instead of passing it seperately to load and dump. But I am not sure if we always want to overwrite kernels during calibration. If no, we need a different mechanism to pass the the path to dump to, which will also be exposed to qibocal (and the user).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Two reasons, the first I forgot and the second it's related to this. #758 (comment) I wanted to use the platform.name but the dummy has two, I think the best would be something like this ?

name = platform.name
if platform.name == "dummy_couplers":
    name = "dummy"
Kernels(kernels).dump(Path(__file__).parent / name / KERNELS_FILE)

Copy link
Member

Choose a reason for hiding this comment

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

For as long as we need to maintain two dummy platforms, couldn't we have actually two folders?

Copy link
Member

@stavros11 stavros11 Jan 19, 2024

Choose a reason for hiding this comment

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

I am not that much concerned about the dummy, the implemented solution should be fine. However, this is still a bit problematic because it is asymmetric to the loading.

If I understand it correctly, when you create the platform you will load the kernels from whatever extras_folder is passed to load_qubits. This is typically located together with the platform runcard. Then when you (or qibocal) call dump it is using Path(__file__).parent / name to determine the directory, where __file__ is the serialize.py. Therefore it will dump the new kernels inside your qibolab installation, which is in general a different place than where the runcards are stored. Is this what we want?

More info: I understand this works okay for the dummy, but did you try it with other platforms, eg. iqm5q?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's the other solution, I'm fine with any.

Copy link
Contributor Author

@Jacfomg Jacfomg Jan 19, 2024

Choose a reason for hiding this comment

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

We want that for the dummy, but not for the other platforms. I will think of something then. We would need the extras_folder or something in qibocal as well to dump the kernels properly, any suggestions ?

Copy link
Member

@stavros11 stavros11 Jan 19, 2024

Choose a reason for hiding this comment

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

I think dump_runcard is automatically called by qibocal after every routine. So if you add kernels to any platform (eg. iqm5q) and you run any qibocal routine, after execution it will try to dump the "new" kernels to a directory like

qibolab/src/qibolab/iqm5q/kernels.npz

and will probably fail if there is no folder iqm5q within qibolab. At least that's what I think, I didn't do the actual test.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Tests are failing because of that, can I add the extras_folder path as a atribute for the platform ?

Copy link
Member

Choose a reason for hiding this comment

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

That’s probably acceptable for now and we can improve once we implement the folder structure proposed by @alecandido.

Copy link
Member

Choose a reason for hiding this comment

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

I agree we could go incrementally (eventually we can even use an env var, or even better the same variable we use to locate the runcards).

def get_platforms_path():

However, for dummy we should mock the serialization, even when deployed: the installation folder might require privileges that the user does not have.
Furthermore, I start considering the option to split dummy from the regular Qibolab (we could not implement just in tests/ because it's being used also by Qibocal... I'm thinking...)

@alecandido
Copy link
Member

@Jacfomg could you also merge/rebase on main?

@alecandido
Copy link
Member

I definitely agree with this @stavros11 here #758 (comment), and it's sensible to address also the other comments.

But concerning the rest, I have nothing else to amend. Once solved and rebased, you could merge (I could give a further approval, but since I'd wait for the comments above, I'll leave the pleasure to @stavros11).

@alecandido alecandido removed their request for review January 19, 2024 17:39
@andrea-pasquale andrea-pasquale dismissed their stale review January 24, 2024 15:11

Currenlty working on this PR

@andrea-pasquale
Copy link
Contributor

@stavros11 I implemented the folders approach discussed here:

iqm5q/
  platform.py
  parameters.yml
  kernels.npz

If you approve it I will open the corresponding PR in qibolab_platforms_qrc.

@alecandido
Copy link
Member

alecandido commented Jan 24, 2024

If you approve it I will open the corresponding PR in qibolab_platforms_qrc.

First we'd need to have a PR for that in Qibolab ;)

(you could start from there if you wish)

Internal names are up for discussion...

@alecandido
Copy link
Member

Btw, as @stavros11 pointed out above, this is orthogonal to #762 in a certain sense, since they are two separate changes.
But on the other side, they are acting on the same surface (since they are both concerning the serialization format). So, for whoever is going to author the Qibolab PR, I'd include the conversion to JSON, since it's a tiny change.

@andrea-pasquale
Copy link
Contributor

andrea-pasquale commented Jan 24, 2024

If you approve it I will open the corresponding PR in qibolab_platforms_qrc.

First we'd need to have a PR for that in Qibolab ;)

(you could start from there if you wish)

Internal names are up for discussion...

I've just implemented it here in this PR :)

@alecandido
Copy link
Member

Ah, I didn't notice 0351323

@alecandido
Copy link
Member

@andrea-pasquale I would say names are up for discussion, but I'd avoid using the folder name for internal names. If we choose something, you won't need to know anything beyond the folder: after that, all we be loaded in the same way.

I know it's now a minimal difference, because the only function resolving the internal names is the same one receiving the path. But it's more decoupled if you do not require them to be the same (e.g. you could just rename the folder, without renaming any file inside).

@andrea-pasquale
Copy link
Contributor

@andrea-pasquale I would say names are up for discussion, but I'd avoid using the folder name for internal names. If we choose something, you won't need to know anything beyond the folder: after that, all we be loaded in the same way.

I know it's now a minimal difference, because the only function resolving the internal names is the same one receiving the path. But it's more decoupled if you do not require them to be the same (e.g. you could just rename the folder, without renaming any file inside).

If I understood correctly since all that matters is the path to the folder every file inside the folder can be standardized, correct?
Which is actually what you were suggesting in your original approach...

iqm5q/
  platform.py
  parameters.yml
  kernels.npz

Since I was just moving files around I forgot to think about using standardized names.

@alecandido
Copy link
Member

Indeed.

I wanted to point out that I'm not particularly keen on those names, for as long as we pick some standardized names.

@andrea-pasquale
Copy link
Contributor

Indeed.

I wanted to point out that I'm not particularly keen on those names, for as long as we pick some standardized names.

Great, so I understood correctly.
I am also not good at names so I used exactly the same ones that you proposed in 853dc97

@andrea-pasquale
Copy link
Contributor

Btw, as @stavros11 pointed out above, this is orthogonal to #762 in a certain sense, since they are two separate changes.
But on the other side, they are acting on the same surface (since they are both concerning the serialization format). So, for whoever is going to author the Qibolab PR, I'd include the conversion to JSON, since it's a tiny change

I didn't really follow the discussion related to #762. The plan is to switch from yaml to json everywhere?

@alecandido
Copy link
Member

alecandido commented Jan 24, 2024

I didn't really follow the discussion related to #762. The plan is to switch from yaml to json everywhere?

In Qibolab YAML is used in a single place, a runcard that should not be manually written.
Plus, it is an external dependency. A further one, and non-negligible (though not the most controversial one).

json is there in any case, and for the time being we don't need anything else.


A few notes in advance: for when you need to write manually, it's not difficult to convert to YAML and convert back:

from pathlib import Path
import json
import sys

import yaml

path = Path(sys.argv[1])
d = json.loads(path.load_text())
path.write_text(yaml.dumps(d))

and forth (swap json and yaml)

We could make a minimal CLI in a separate package (distributed together, with dependencies as extras - or even distributed separately, or not at all, if it's simpler and enough), named qibolab to help with chores like these (if ever relevant), and e.g. to bootstrap a platform folder from a very basic description (e.g. the number of qubits, or not even that).

Comment on lines 213 to 227
def dump_kernels(platform: Platform, path: Path):
"""Creates Kernels instance from platform and dumps as npz.

Args:
platform (qibolab.platform.Platform): The platform to be serialized.
path (pathlib.Path): Path that the kernels file will be saved.
"""

# create and dump kernels
kernels = Kernels()
for qubit in platform.qubits.values():
if qubit.kernel is not None:
kernels[qubit.name] = qubit.kernel

kernels.dump(path / KERNELS)
Copy link
Member

@alecandido alecandido Jan 25, 2024

Choose a reason for hiding this comment

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

@stavros11 @hay-k

For even better isolation, eventually I'd consider having these functions just writing on byte streams, and returning them.
The top level dump could be the one responsible to attribute each byte stream a path, and dump it there.

In any case, it's not for this PR.

Copy link
Member

Choose a reason for hiding this comment

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

The drawback of byte streams is just memory consumption: if you dump on streams, instead of directly on disk, you double the amount of memory you consume.

But I'd say CPU memory should not be an issue in Qibolab...

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 isolating the conversion to JSON in #782 @alecandido

Copy link
Member

@stavros11 stavros11 left a comment

Choose a reason for hiding this comment

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

Thanks @andrea-pasquale. Looks good to me, other than some minor comments.

Even though it already has two approvals, the only thing before merging is that I’d open the corresponding PR in qibolab_platforms_qrc and try to test with some instruments (which I believe we haven’t done yet). Let me know if you’ll do that, otherwise I can also help rewriting the existing platforms to the new folder structure.


where ``platform.py`` contains instruments information, ``parameters.yml``
includes calibration parameters and ``kernels.npz`` is an optional
file with additional calibration parameters.
Copy link
Member

Choose a reason for hiding this comment

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

In principle, parameters.yml is also optional, as someone can define the qubit/channel objects hardcoded in the create. This is basically explained in the lab.rst tutorial.

However, dumping the platform after will automatically create parameters, so I am not sure whether we should advocate this kind of usage.

doc/source/tutorials/lab.rst Outdated Show resolved Hide resolved
src/qibolab/serialize.py Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

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

We could expose create_dummy here to keep the same interface as before

from qibolab.dummy import create_dummy

@@ -29,32 +30,33 @@ def get_platforms_path():
return Path(profiles)


def create_platform(name, runcard=None):
def create_platform(name, path: Path = None) -> Platform:
Copy link
Member

Choose a reason for hiding this comment

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

I have the impression that the path argument is no longer required here. Before it was used to link a YAML to the create (assuming you wanted to use different YAMLs for the same create?). But this is no longer valid given that we’re proposing a fixed folder structure.

However, this will become more clear once we try to define some actual platforms (eg in qibolab_platforms_qrc).

platform (qibolab.platform.Platform): The platform to be serialized.
path (pathlib.Path): Path where yaml and npz will be dumped.
"""

Copy link
Member

Choose a reason for hiding this comment

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

Didn’t try to use this yet, but shouldn’t it:

  1. Create the folder specified by path if it doesn’t exist (given that it’ll try to write files inside)
  2. Dump also platform.py inside this folder?

I’ll try to use and review this comment.

Co-authored-by: Stavros Efthymiou <[email protected]>
@andrea-pasquale
Copy link
Contributor

Even though it already has two approvals, the only thing before merging is that I’d open the corresponding PR in qibolab_platforms_qrc and try to test with some instruments (which I believe we haven’t done yet). Let me know if you’ll do that, otherwise I can also help rewriting the existing platforms to the new folder structure.

Thanks for the review @stavros11.
I've just opened the PR this morning qiboteam/qibolab_platforms_qrc#108

if qubit.kernel is not None:
kernels[qubit.name] = qubit.kernel

kernels.dump(path / KERNELS)
Copy link
Member

Choose a reason for hiding this comment

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

I think this will not work. KERNELS is appended to the path twice, once here and once in

path / KERNELS,

We should test the PRs better before considering to merge. Particularly the new dump_kernels and dump_platform methods, I believe are not captured by the tests, while they can be easily tested without access to instruments/QPUs.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, I also found the same error while trying to run tests in qibocal.
I will try to add tests to cover dump_platform and dump_kernels.

Copy link
Contributor

Choose a reason for hiding this comment

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

Tests added in 8f49f89

@stavros11
Copy link
Member

Thanks @andrea-pasquale. I will merge this to unlock the CI in the platforms repo (and the change is already big). If you like to change dump_platform to run a folder please open a new PR (I'll open an issue).

@stavros11 stavros11 merged commit b861009 into main Jan 26, 2024
24 checks passed
@stavros11 stavros11 deleted the kernel branch January 26, 2024 13:43
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