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

Adding qq update command #905

Merged
merged 16 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions doc/source/getting-started/interface.rst
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,20 @@ The report is generated iteratively as soon as each protocol finished.

qq auto <path_to_runcard> -o <output_folder>

``qq update``
^^^^^^^^^^^^^
Using ``qq update`` it is possible to update the platform calibrated by Qibocal.
The correct syntax is the following

.. code-block::

qq update <output_folder>

which will copy the configuration of the platform in the corresponding
directory specified using the environment variable ``QIBOLAB_PLATFORMS``.
See the Qibolab `documentation <https://qibo.science/qibolab/stable/tutorials/lab.html#how-to-connect-qibolab-to-your-lab>`_ for more details.


``qq upload``
^^^^^^^^^^^^^

Expand Down
14 changes: 11 additions & 3 deletions src/qibocal/auto/execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from qibolab import create_platform
from qibolab.platform import Platform
from qibolab.serialize import dump_platform

from qibocal import protocols
from qibocal.config import log, raise_error
Expand All @@ -17,6 +18,9 @@
from .runcard import Action, Runcard, Targets
from .task import Completed, Task

PLATFORM_DIR = "platform"
"""Folder where platform will be dumped."""


@dataclass
class Executor:
Expand Down Expand Up @@ -56,6 +60,7 @@ def run_protocol(
protocol: Routine,
parameters: Union[dict, Action],
mode: ExecutionMode = ExecutionMode.ACQUIRE | ExecutionMode.FIT,
update: bool = True,
) -> Completed:
"""Run single protocol in ExecutionMode mode."""
if isinstance(parameters, dict):
Expand All @@ -80,17 +85,19 @@ def run_protocol(
folder=self.output,
mode=mode,
)

if ExecutionMode.FIT in mode and self.platform is not None:
if ExecutionMode.FIT in mode and self.platform is not None and update:
completed.update_platform(platform=self.platform, update=self.update)

(completed.datapath / PLATFORM_DIR).mkdir(parents=True, exist_ok=True)
dump_platform(self.platform, completed.datapath / PLATFORM_DIR)

self.history.push(completed)
completed.dump(self.output)

return completed


def run(runcard: Runcard, output: Path, mode: ExecutionMode):
def run(runcard: Runcard, output: Path, mode: ExecutionMode, update: bool = True):
"""Run runcard and dump to output."""
platform = runcard.platform_obj
targets = runcard.targets if runcard.targets is not None else list(platform.qubits)
Expand All @@ -107,5 +114,6 @@ def run(runcard: Runcard, output: Path, mode: ExecutionMode):
protocol=getattr(protocols, action.operation),
parameters=action,
mode=mode,
update=update,
)
return instance.history
5 changes: 0 additions & 5 deletions src/qibocal/auto/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from typing import Optional

from qibolab.platform import Platform
from qibolab.serialize import dump_platform

from qibocal import protocols

Expand All @@ -22,8 +21,6 @@
"""Default number on shots when the platform is not provided."""
TaskId = tuple[Id, int]
"""Unique identifier for executed tasks."""
PLATFORM_DIR = "platform"
"""Folder where platform will be dumped."""


@dataclass
Expand Down Expand Up @@ -193,5 +190,3 @@ def update_platform(self, platform: Platform, update: bool):
log.warning(
f"Skipping update of qubit {qubit} due to error in fit."
)
(self.datapath / PLATFORM_DIR).mkdir(parents=True, exist_ok=True)
dump_platform(platform, self.datapath / PLATFORM_DIR)
18 changes: 18 additions & 0 deletions src/qibocal/cli/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from .autocalibration import autocalibrate
from .fit import fit as fitting
from .report import report as reporting
from .update import update as updating
from .upload import upload_report

CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"])
Expand Down Expand Up @@ -115,6 +116,23 @@ def acquire(runcard, folder, force, platform, backend):
acquisition(runcard, folder, force)


@command.command(context_settings=CONTEXT_SETTINGS)
@click.argument(
"folder", metavar="folder", type=click.Path(exists=True, path_type=pathlib.Path)
)
def update(folder):
"""Update platform configuration.

All configuration files related to platform will be copied
in the corresponding QIBOLAB_PLAFORMS folder.

Arguments:
- folder: Qibocal output folder.

"""
updating(folder)


@command.command(context_settings=CONTEXT_SETTINGS)
@click.argument(
"folder", metavar="folder", type=click.Path(exists=True, path_type=pathlib.Path)
Expand Down
2 changes: 1 addition & 1 deletion src/qibocal/cli/autocalibration.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def autocalibrate(runcard, folder, force, update):
platform.connect()

# run
history = run(output=path, runcard=runcard, mode=AUTOCALIBRATION)
history = run(output=path, runcard=runcard, mode=AUTOCALIBRATION, update=update)

# TODO: implement iterative dump of report...

Expand Down
6 changes: 4 additions & 2 deletions src/qibocal/cli/fit.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,11 @@ def fit(input_path, update, output_path, force):
e = datetime.datetime.now(datetime.timezone.utc)
meta["end-time"] = e.strftime("%H:%M:%S")

# checking if at least on the task had local update
local_update = any(completed.task.update for completed in list(history.values()))

# dump updated runcard
if runcard.platform_obj is not None and update: # pragma: no cover
# cannot test update since dummy may produce wrong values and trigger errors
if runcard.platform_obj is not None:
(path / UPDATED_PLATFORM).mkdir(parents=True, exist_ok=True)
dump_runcard(runcard.platform_obj, path / UPDATED_PLATFORM)

Expand Down
27 changes: 27 additions & 0 deletions src/qibocal/cli/update.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import json
import os
import pathlib
import shutil

from ..config import raise_error
from .utils import META, UPDATED_PLATFORM


def update(path: pathlib.Path):
"""Perform copy of updated platform in QIBOLAB_PLATFORM."

Arguments:
- input_path: Qibocal output folder.
"""
new_platform_path = path / UPDATED_PLATFORM
if not new_platform_path.exists():
raise_error(FileNotFoundError, f"No updated runcard platform found in {path}.")

platform_name = json.loads((path / META).read_text())["platform"]
platform_path = pathlib.Path(os.getenv("QIBOLAB_PLATFORMS")) / platform_name

for filename in os.listdir(new_platform_path):
shutil.copy(
new_platform_path / filename,
platform_path / filename,
)
3 changes: 2 additions & 1 deletion src/qibocal/protocols/coherence/spin_echo_signal.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from copy import deepcopy
from dataclasses import dataclass
from typing import Union

import numpy as np
import plotly.graph_objects as go
Expand Down Expand Up @@ -37,7 +38,7 @@ class SpinEchoSignalParameters(Parameters):
class SpinEchoSignalResults(Results):
"""SpinEchoSignal outputs."""

t2_spin_echo: dict[QubitId, tuple[float]]
t2_spin_echo: dict[QubitId, Union[float, list[float]]]
"""T2 echo for each qubit."""
fitted_parameters: dict[QubitId, dict[str, float]]
"""Raw fitting output."""
Expand Down
4 changes: 1 addition & 3 deletions src/qibocal/protocols/coherence/t1.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ class T1Parameters(t1_signal.T1SignalParameters):
class T1Results(t1_signal.T1SignalResults):
"""T1 outputs."""

chi2: Optional[dict[QubitId, tuple[float, Optional[float]]]] = field(
default_factory=dict
)
chi2: Optional[dict[QubitId, list[float, float]]] = field(default_factory=dict)
andrea-pasquale marked this conversation as resolved.
Show resolved Hide resolved
"""Chi squared estimate mean value and error."""


Expand Down
3 changes: 2 additions & 1 deletion src/qibocal/protocols/coherence/t1_signal.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from dataclasses import dataclass, field
from typing import Union

import numpy as np
import numpy.typing as npt
Expand Down Expand Up @@ -34,7 +35,7 @@ class T1SignalParameters(Parameters):
class T1SignalResults(Results):
"""T1 Signal outputs."""

t1: dict[QubitId, tuple[float]]
t1: dict[QubitId, Union[float, list[float]]]
"""T1 for each qubit."""
fitted_parameters: dict[QubitId, dict[str, float]]
"""Raw fitting output."""
Expand Down
3 changes: 2 additions & 1 deletion src/qibocal/protocols/coherence/t2_signal.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from dataclasses import dataclass
from typing import Union

import numpy as np
import plotly.graph_objects as go
Expand Down Expand Up @@ -33,7 +34,7 @@ class T2SignalParameters(Parameters):
class T2SignalResults(Results):
"""T2Signal outputs."""

t2: dict[QubitId, tuple[float]]
t2: dict[QubitId, Union[float, list[float]]]
"""T2 for each qubit [ns]."""
fitted_parameters: dict[QubitId, dict[str, float]]
"""Raw fitting output."""
Expand Down
8 changes: 4 additions & 4 deletions src/qibocal/protocols/coherence/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def exponential_fit(data, zeno=False):
]
fitted_parameters[qubit] = popt
pcovs[qubit] = pcov.tolist()
decay[qubit] = (popt[2], np.sqrt(pcov[2, 2]) * (x_max - x_min))
decay[qubit] = [popt[2], np.sqrt(pcov[2, 2]) * (x_max - x_min)]

except Exception as e:
log.warning(f"Exponential decay fit failed for qubit {qubit} due to {e}")
Expand Down Expand Up @@ -129,15 +129,15 @@ def exponential_fit_probability(data, zeno=False):
pcovs[qubit] = pcov.tolist()
fitted_parameters[qubit] = popt
dec = popt[2]
decay[qubit] = (dec, np.sqrt(pcov[2, 2]) * (x_max - x_min))
chi2[qubit] = (
decay[qubit] = [dec, np.sqrt(pcov[2, 2]) * (x_max - x_min)]
chi2[qubit] = [
chi2_reduced(
data[qubit].prob,
exp_decay(data[qubit].wait, *fitted_parameters[qubit]),
data[qubit].error,
),
np.sqrt(2 / len(data[qubit].prob)),
)
]

except Exception as e:
log.warning(f"Exponential decay fit failed for qubit {qubit} due to {e}")
Expand Down
19 changes: 9 additions & 10 deletions src/qibocal/protocols/flipping.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from dataclasses import dataclass, field
from typing import Optional

import numpy as np
import numpy.typing as npt
Expand Down Expand Up @@ -35,7 +34,7 @@ class FlippingParameters(FlippingSignalParameters):
class FlippingResults(FlippingSignalResults):
"""Flipping outputs."""

chi2: dict[QubitId, tuple[float, Optional[float]]] = field(default_factory=dict)
chi2: dict[QubitId, list[float]] = field(default_factory=dict)
"""Chi squared estimate mean value and error. """


Expand Down Expand Up @@ -188,7 +187,7 @@ def _fit(data: FlippingData) -> FlippingResults:
else:
signed_correction = popt[2] / 2
# The amplitude is directly proportional to the rotation angle
corrected_amplitudes[qubit] = (
corrected_amplitudes[qubit] = [
float(detuned_pi_pulse_amplitude * np.pi / (np.pi + signed_correction)),
float(
detuned_pi_pulse_amplitude
Expand All @@ -198,11 +197,11 @@ def _fit(data: FlippingData) -> FlippingResults:
* perr[2]
/ 2
),
)
]

fitted_parameters[qubit] = popt

delta_amplitude[qubit] = (
delta_amplitude[qubit] = [
-signed_correction
* detuned_pi_pulse_amplitude
/ (np.pi + signed_correction),
Expand All @@ -213,20 +212,20 @@ def _fit(data: FlippingData) -> FlippingResults:
)
* perr[2]
/ 2,
)
delta_amplitude_detuned[qubit] = (
]
delta_amplitude_detuned[qubit] = [
delta_amplitude[qubit][0] - data.detuning,
delta_amplitude[qubit][1],
)
]

chi2[qubit] = (
chi2[qubit] = [
chi2_reduced(
y,
flipping_fit(x, *popt),
qubit_data.error,
),
np.sqrt(2 / len(x)),
)
]
except Exception as e:
log.warning(f"Error in flipping fit for qubit {qubit} due to {e}.")

Expand Down
8 changes: 4 additions & 4 deletions src/qibocal/protocols/flipping_signal.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from dataclasses import dataclass, field
from typing import Optional
from typing import Union

import numpy as np
import numpy.typing as npt
Expand Down Expand Up @@ -36,11 +36,11 @@ class FlippingSignalParameters(Parameters):
class FlippingSignalResults(Results):
"""Flipping outputs."""

amplitude: dict[QubitId, tuple[float, Optional[float]]]
amplitude: dict[QubitId, Union[float, list[float]]]
"""Drive amplitude for each qubit."""
delta_amplitude: dict[QubitId, tuple[float, Optional[float]]]
delta_amplitude: dict[QubitId, Union[float, list[float]]]
"""Difference in amplitude between initial value and fit."""
delta_amplitude_detuned: dict[QubitId, tuple[float, Optional[float]]]
delta_amplitude_detuned: dict[QubitId, Union[float, list[float]]]
"""Difference in amplitude between detuned value and fit."""
fitted_parameters: dict[QubitId, dict[str, float]]
"""Raw fitting output."""
Expand Down
11 changes: 5 additions & 6 deletions src/qibocal/protocols/rabi/amplitude.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from dataclasses import dataclass, field
from typing import Optional

import numpy as np
import numpy.typing as npt
Expand All @@ -26,7 +25,7 @@ class RabiAmplitudeParameters(RabiAmplitudeSignalParameters):
class RabiAmplitudeResults(RabiAmplitudeSignalResults):
"""RabiAmplitude outputs."""

chi2: dict[QubitId, tuple[float, Optional[float]]] = field(default_factory=dict)
chi2: dict[QubitId, list[float]] = field(default_factory=dict)


RabiAmpType = np.dtype(
Expand Down Expand Up @@ -123,17 +122,17 @@ def _fit(data: RabiAmplitudeData) -> RabiAmplitudeResults:
sigma=qubit_data.error,
signal=False,
)
pi_pulse_amplitudes[qubit] = (pi_pulse_parameter, perr[2] / 2)
pi_pulse_amplitudes[qubit] = [pi_pulse_parameter, perr[2] / 2]
fitted_parameters[qubit] = popt.tolist()
durations = {key: (value, 0) for key, value in data.durations.items()}
chi2[qubit] = (
durations = {key: [value, 0] for key, value in data.durations.items()}
chi2[qubit] = [
chi2_reduced(
y,
utils.rabi_amplitude_function(x, *popt),
qubit_data.error,
),
np.sqrt(2 / len(y)),
)
]

except Exception as e:
log.warning(f"Rabi fit failed for qubit {qubit} due to {e}.")
Expand Down
Loading
Loading