qiboteam / qibocal

Quantum calibration, characterization and validation module for Qibo.
https://qibo.science
Apache License 2.0
30 stars 7 forks source link

Expose single protocol run to executor #865

Closed andrea-pasquale closed 3 months ago

andrea-pasquale commented 4 months ago

Adds possibility to run one protocol at a time using Executor. Possible script:

from qibocal.auto.execute import Executor
from qibocal.auto.mode import ExecutionMode
from qibocal.auto.runcard import Action
from qibolab import create_platform
import pathlib

protocol = {"id": "flipping",
            "operation": "flipping",
            "targets": [0,1,2],
            "parameters": {
                "nflips_max": 20,
                "nflips_step": 2,
                "detuning": +0.1,
            }}

action = Action(**protocol)
executor = Executor([action], platform=create_platform("dummy"), output=pathlib.Path("FOLDER"))

completed = executor.run_protocol(ExecutionMode.acquire, action)

@alecandido I think this should be more or less what we discussed :)

Checklist:

codecov[bot] commented 4 months ago

Codecov Report

All modified and coverable lines are covered by tests :white_check_mark:

Project coverage is 97.32%. Comparing base (a1161f6) to head (7a03b1b).

:exclamation: Current head 7a03b1b differs from pull request most recent head c10d86d

Please upload reports for the commit c10d86d to get more accurate results.

Additional details and impacted files [![Impacted file tree graph](https://app.codecov.io/gh/qiboteam/qibocal/pull/865/graphs/tree.svg?width=650&height=150&src=pr&token=3EA9ZUNKUY&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=qiboteam)](https://app.codecov.io/gh/qiboteam/qibocal/pull/865?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=qiboteam) ```diff @@ Coverage Diff @@ ## main #865 +/- ## ========================================== - Coverage 97.37% 97.32% -0.05% ========================================== Files 115 115 Lines 8695 8645 -50 ========================================== - Hits 8467 8414 -53 - Misses 228 231 +3 ``` | [Flag](https://app.codecov.io/gh/qiboteam/qibocal/pull/865/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=qiboteam) | Coverage Δ | | |---|---|---| | [unittests](https://app.codecov.io/gh/qiboteam/qibocal/pull/865/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=qiboteam) | `97.32% <100.00%> (-0.05%)` | :arrow_down: | Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=qiboteam#carryforward-flags-in-the-pull-request-comment) to find out more. | [Files](https://app.codecov.io/gh/qiboteam/qibocal/pull/865?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=qiboteam) | Coverage Δ | | |---|---|---| | [src/qibocal/\_\_init\_\_.py](https://app.codecov.io/gh/qiboteam/qibocal/pull/865?src=pr&el=tree&filepath=src%2Fqibocal%2F__init__.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=qiboteam#diff-c3JjL3FpYm9jYWwvX19pbml0X18ucHk=) | `100.00% <100.00%> (ø)` | | | [src/qibocal/auto/execute.py](https://app.codecov.io/gh/qiboteam/qibocal/pull/865?src=pr&el=tree&filepath=src%2Fqibocal%2Fauto%2Fexecute.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=qiboteam#diff-c3JjL3FpYm9jYWwvYXV0by9leGVjdXRlLnB5) | `100.00% <100.00%> (ø)` | | | [src/qibocal/auto/mode.py](https://app.codecov.io/gh/qiboteam/qibocal/pull/865?src=pr&el=tree&filepath=src%2Fqibocal%2Fauto%2Fmode.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=qiboteam#diff-c3JjL3FpYm9jYWwvYXV0by9tb2RlLnB5) | `100.00% <100.00%> (ø)` | | | [src/qibocal/auto/runcard.py](https://app.codecov.io/gh/qiboteam/qibocal/pull/865?src=pr&el=tree&filepath=src%2Fqibocal%2Fauto%2Fruncard.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=qiboteam#diff-c3JjL3FpYm9jYWwvYXV0by9ydW5jYXJkLnB5) | `95.91% <100.00%> (ø)` | | | [src/qibocal/auto/task.py](https://app.codecov.io/gh/qiboteam/qibocal/pull/865?src=pr&el=tree&filepath=src%2Fqibocal%2Fauto%2Ftask.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=qiboteam#diff-c3JjL3FpYm9jYWwvYXV0by90YXNrLnB5) | `99.09% <100.00%> (-0.04%)` | :arrow_down: | | [src/qibocal/cli/acquisition.py](https://app.codecov.io/gh/qiboteam/qibocal/pull/865?src=pr&el=tree&filepath=src%2Fqibocal%2Fcli%2Facquisition.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=qiboteam#diff-c3JjL3FpYm9jYWwvY2xpL2FjcXVpc2l0aW9uLnB5) | `100.00% <100.00%> (ø)` | | | [src/qibocal/cli/autocalibration.py](https://app.codecov.io/gh/qiboteam/qibocal/pull/865?src=pr&el=tree&filepath=src%2Fqibocal%2Fcli%2Fautocalibration.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=qiboteam#diff-c3JjL3FpYm9jYWwvY2xpL2F1dG9jYWxpYnJhdGlvbi5weQ==) | `100.00% <100.00%> (ø)` | | | [src/qibocal/cli/fit.py](https://app.codecov.io/gh/qiboteam/qibocal/pull/865?src=pr&el=tree&filepath=src%2Fqibocal%2Fcli%2Ffit.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=qiboteam#diff-c3JjL3FpYm9jYWwvY2xpL2ZpdC5weQ==) | `100.00% <100.00%> (ø)` | | | [src/qibocal/cli/report.py](https://app.codecov.io/gh/qiboteam/qibocal/pull/865?src=pr&el=tree&filepath=src%2Fqibocal%2Fcli%2Freport.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=qiboteam#diff-c3JjL3FpYm9jYWwvY2xpL3JlcG9ydC5weQ==) | `100.00% <100.00%> (ø)` | | | [src/qibocal/protocols/\_\_init\_\_.py](https://app.codecov.io/gh/qiboteam/qibocal/pull/865?src=pr&el=tree&filepath=src%2Fqibocal%2Fprotocols%2F__init__.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=qiboteam#diff-c3JjL3FpYm9jYWwvcHJvdG9jb2xzL19faW5pdF9fLnB5) | `100.00% <100.00%> (ø)` | | ... and [1 file with indirect coverage changes](https://app.codecov.io/gh/qiboteam/qibocal/pull/865/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=qiboteam)
alecandido commented 4 months ago

To make it (slightly) simpler, I would:

turning your example into:

from qibocal import Executor

protocol = {"id": "flipping",
            "operation": "flipping",
            "targets": [0,1,2],
            "parameters": {
                "nflips_max": 20,
                "nflips_step": 2,
                "detuning": +0.1,
            }}
executor = Executor.create([protocol], platform="dummy", output="FOLDER")

completed = executor.run_protocol("flipping", mode="acquire")

This is just for interfacing purposes, saving the user some boilerplate and limiting the required understanding of Qibocal internals.

alecandido commented 4 months ago

Concerning the mode, currently, Qibocal is using: https://github.com/qiboteam/qibocal/blob/a668a08c9cfd118f185bcd17ab80acc00fbc27eb/src/qibocal/auto/mode.py#L4-L8

However, we could use an IntFlag and define auto as:

auto = ExecutionMode.acquire | ExecutionMode.fit | ExecutionMode.report

with all the bits set.

For .run_protocol() I'd just use ExecutionMode.acquire | ExecutionMode.fit. We could give it a name, or, to make it simpler, set the default to None, and set the mode internally to that, instead of retrieving it by name.

alecandido commented 4 months ago

Apart from names and other interface suggestions, the current proposal looks essentially good: it should be minimal, but it should allow running a fully flexible calibration program.

Of course, if you have many of these protocols (calibration programs, not individual routines) there will be repeated patterns (e.g. the dependencies) and we could abstract them in the framework. But given that (for the time being) we do not even have a single one of them, I believe this could be an optimal starting point :)

andrea-pasquale commented 4 months ago

Thanks for the review @alecandido. I think I have addressed all comments. There is only one think worth mentioning ExecutionMode.REPORT will not actually generate any report. It is meant to indicate a mode where both acquisition and fit are skipped. The data is just loaded and then pass it to the ReportBuilder to perform the report generation. At this point we could even drop it or expose to the user a proper way to generate the report.

alecandido commented 4 months ago

At this point we could even drop it or expose to the user a proper way to generate the report.

Yes, that's definitely an option.

Since the Executor is not involved in the report generation, we could restrict the modes to acquire and fit (and the combination), and instead delegate the rest to the ReportBuilder. This would only change the modes and the way the CLI is implemented, but you could keep the exact same interface with qq auto/acquire/fit/report.

andrea-pasquale commented 4 months ago

The only thing missing is an example of how to use it, but given that is a non-breaking/minimal contribution you could even merge it like this, and contribute the example in a further PR, considering this just as a refactor that will allow that feature (and not a new feature to be documented).

I can write an example on how to use it. We have already a section in documentation on how to use qibocal as a library https://qibo.science/qibocal/stable/tutorials/advanced.html#how-to-use-qibocal-as-a-library, which was doing similar things in a more complicated way. At this point shall we remove it and modify this section with the new layout proposed here? Again the only difference concern the plotting, I believe that we the current layout it is not trivial to generate plots. What do you think?

scarrazza commented 4 months ago

In view of #866, I would suggest a minor API change (demo code):

from qibocal import Executor, Routine
from qibocal.protocols.operation import flipping

flipping_params = {
            "id" : "flipping initial guess",
            "targets": [0,1,2],
            "parameters": {
                "nflips_max": 20,
                "nflips_step": 2,
                "detuning": +0.1,
            }}

def acquire(data, ...):
    """Custom routine"""
    return ...

my_routine = Routine(acquire)
routine_params = {"......."}
executor = Executor.create(platform="dummy", output="FOLDER")

for i in range(10):
    completed = executor.run_protocol(flipping, flipping_params, mode="acquire")
    flipping_params['parameters']['detuning'] += i
    completed2 = executor.run_protocol(my_routine, routine_params, mode="acquire")

Where:

alecandido commented 4 months ago

We have already a section in documentation on how to use qibocal as a library qibo.science/qibocal/stable/tutorials/advanced.html#how-to-use-qibocal-as-a-library, which was doing similar things in a more complicated way. At this point shall we remove it and modify this section with the new layout proposed here?

Yes, and I'd move it out from "Advanced Examples", and rather make its own page under tutorials (just to avoid passing the idea that this is something you should beware of).

Again the only difference concern the plotting, I believe that we the current layout it is not trivial to generate plots. What do you think?

Currently, we're coupling report generation to the execution, though the two things are well separate. What we could do is to expose the report function https://github.com/qiboteam/qibocal/blob/77f7bd1364a19bb3b749460d63b337e4c6a9e2a1/src/qibocal/cli/report.py#L65 (that already has a pretty simple API), even dropping the executor argument, i.e. assuming that an executor already ran. What we will need instead will be a History, but I would make that optional (if possible), and try to deserialize the History from the path itself (the purpose of having a History anyhow is to make the input path optional, i.e. you could generate everything in memory, before dumping to disk). The only other executor attribute used is: https://github.com/qiboteam/qibocal/blob/77f7bd1364a19bb3b749460d63b337e4c6a9e2a1/src/qibocal/cli/report.py#L99 but we can inherit that in the history.

This should be sufficient for qq report (it is not qq auto, so it's not supposed to run a calibration on its own), and report(path) or report(path, executor.history) should be a simple enough API even for an end user (at which point I'd take out the report function from the cli subpackage, and expose top-level). We could even do report(executor=executor), in such a way that the path and history are automatically inferred (or report(executor) and check the type, also using @overload for that).

In any case, I'd do this in a separate PR.

alecandido commented 4 months ago

Since this is working, and #869's target is to provide a simpler syntax to use what's already here, I wonder whether we should aim to merge this now.

I will re-review, but I've already approved it, and this PR is just exposing something that is already more or less there in main (i.e. executing routines explicitly with the Executor, now possible individually).

Since it's at an internal stage, we can avoid documenting it as a public feature (the public interface will come with #869), but the improvement is simple and valid on its own.

alecandido commented 3 months ago

@andrea-pasquale @Edoardo-Pedicillo should we then solve the conflicts and merge this PR?

I know we are going to change it even further, in #870 and #869, but this PR is rather self-contained, despite introducing a temporary solution (in any case, main is not a release, and the other two PRs will come quite soon).