Skip to content

Commit

Permalink
BREAKING CHANGE: drop SweeperType
Browse files Browse the repository at this point in the history
  • Loading branch information
stavros11 committed Aug 28, 2024
1 parent 3cbccee commit 4756d54
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 66 deletions.
10 changes: 4 additions & 6 deletions doc/source/getting-started/experiment.rst
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ We leave to the dedicated tutorial a full explanation of the experiment, but her
from qibolab import create_platform
from qibolab.sequence import PulseSequence
from qibolab.result import magnitude
from qibolab.sweeper import Sweeper, SweeperType, Parameter
from qibolab.sweeper import Sweeper, Parameter
from qibolab.execution_parameters import (
ExecutionParameters,
AveragingMode,
Expand All @@ -221,11 +221,11 @@ We leave to the dedicated tutorial a full explanation of the experiment, but her
sequence = natives.MZ.create_sequence()

# define a sweeper for a frequency scan
f0 = platform.config(str(qubit.probe.name)).frequency # center frequency
sweeper = Sweeper(
parameter=Parameter.frequency,
values=np.arange(-2e8, +2e8, 1e6),
values=f0 + np.arange(-2e8, +2e8, 1e6),
channels=[qubit.probe.name],
type=SweeperType.OFFSET,
)

# perform the experiment using specific options
Expand All @@ -241,9 +241,7 @@ We leave to the dedicated tutorial a full explanation of the experiment, but her

# plot the results
amplitudes = magnitude(results[acq.id][0])
frequencies = (
np.arange(-2e8, +2e8, 1e6) + platform.config(str(qubit.probe.name)).frequency
)
frequencies = sweeper.values

plt.title("Resonator Spectroscopy")
plt.xlabel("Frequencies [Hz]")
Expand Down
52 changes: 20 additions & 32 deletions doc/source/main-documentation/qibolab.rst
Original file line number Diff line number Diff line change
Expand Up @@ -374,13 +374,7 @@ To designate the pulse(s) or channel(s) to which a sweeper is applied, you can u

To effectively specify the sweeping behavior, Qibolab provides the ``values`` attribute along with the ``type`` attribute.

The ``values`` attribute comprises an array of numerical values that define the sweeper's progression. To facilitate multi-qubit execution, these numbers can be interpreted in three ways:

- Absolute Values: Represented by `qibolab.sweeper.SweeperType.ABSOLUTE`, these values are used directly.
- Relative Values with Offset: Utilizing `qibolab.sweeper.SweeperType.OFFSET`, these values are relative to a designated base value, corresponding to the pulse or qubit value.
- Relative Values with Factor: Employing `qibolab.sweeper.SweeperType.FACTOR`, these values are scaled by a factor from the base value, akin to a multiplier.

For offset and factor sweepers, the base value is determined by the respective pulse or qubit value.
The ``values`` attribute comprises an array of numerical values that define the sweeper's progression.

Let's see some examples.
Consider now a system with three qubits (qubit 0, qubit 1, qubit 2) with resonator frequency at 4 GHz, 5 GHz and 6 GHz.
Expand All @@ -390,7 +384,7 @@ A tipical resonator spectroscopy experiment could be defined with:

import numpy as np

from qibolab.sweeper import Parameter, Sweeper, SweeperType
from qibolab.sweeper import Parameter, Sweeper

natives = platform.natives.single_qubit

Expand All @@ -405,32 +399,27 @@ A tipical resonator spectroscopy experiment could be defined with:
natives[2].MZ.create_sequence()
) # readout pulse for qubit 2 at 6 GHz

sweeper = Sweeper(
parameter=Parameter.frequency,
values=np.arange(-200_000, +200_000, 1), # define an interval of swept values
channels=[qubit.probe.name for qubit in platform.qubits.values()],
type=SweeperType.OFFSET,
)
sweepers = [
Sweeper(
parameter=Parameter.frequency,
values=platform.config(str(qubit.probe.name)).frequency
+ np.arange(-200_000, +200_000, 1), # define an interval of swept values
channels=[qubit.probe.name],
)
for qubit in platform.qubits.values()
]

results = platform.execute([sequence], options, [[sweeper]])
results = platform.execute([sequence], options, [sweepers])

.. note::

options is an :class:`qibolab.execution_parameters.ExecutionParameters` object, detailed in a separate section.

In this way, we first define a sweeper with an interval of 400 MHz (-200 MHz --- 200 MHz), assigning it to all three readout pulses and setting is as an offset sweeper. The resulting probed frequency will then be:
In this way, we first define three parallel sweepers with an interval of 400 MHz (-200 MHz --- 200 MHz). The resulting probed frequency will then be:
- for qubit 0: [3.8 GHz, 4.2 GHz]
- for qubit 1: [4.8 GHz, 5.2 GHz]
- for qubit 2: [5.8 GHz, 6.2 GHz]

If we had used the :class:`qibolab.sweeper.SweeperType` absolute, we would have probed for all qubits the same frequencies [-200 MHz, 200 MHz].

.. note::

The default :class:`qibolab.sweeper.SweeperType` is absolute!

For factor sweepers, usually useful when dealing with amplitudes, the base value is multipled by the values set.

It is possible to define and executes multiple sweepers at the same time.
For example:

Expand All @@ -448,15 +437,14 @@ For example:

sweeper_freq = Sweeper(
parameter=Parameter.frequency,
values=np.arange(-100_000, +100_000, 10_000),
values=platform.config(str(qubit.drive.name)).frequency
+ np.arange(-100_000, +100_000, 10_000),
channels=[qubit.drive.name],
type=SweeperType.OFFSET,
)
sweeper_amp = Sweeper(
parameter=Parameter.amplitude,
values=np.arange(0, 1.5, 0.1),
values=np.arange(0, 0.43, 0.3),
pulses=[next(iter(sequence.channel(qubit.drive.name)))],
type=SweeperType.FACTOR,
)

results = platform.execute([sequence], options, [[sweeper_freq], [sweeper_amp]])
Expand Down Expand Up @@ -567,15 +555,15 @@ The shape of the values of an integreted acquisition with 2 sweepers will be:

sweeper1 = Sweeper(
parameter=Parameter.frequency,
values=np.arange(-100_000, +100_000, 1), # define an interval of swept values
values=platform.config(str(qubit.drive.name)).frequency
+ np.arange(-100_000, +100_000, 1),
channels=[qubit.drive.name],
type=SweeperType.OFFSET,
)
sweeper2 = Sweeper(
parameter=Parameter.frequency,
values=np.arange(-200_000, +200_000, 1), # define an interval of swept values
values=platform.config(str(qubit.drive.name)).frequency
+ np.arange(-200_000, +200_000, 1),
channels=[qubit.probe.name],
type=SweeperType.OFFSET,
)
shape = (options.nshots, len(sweeper1.values), len(sweeper2.values))

Expand Down
22 changes: 9 additions & 13 deletions doc/source/tutorials/calibration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ around the pre-defined frequency.
from qibolab import create_platform
from qibolab.sequence import PulseSequence
from qibolab.result import magnitude
from qibolab.sweeper import Sweeper, SweeperType, Parameter
from qibolab.sweeper import Sweeper, Parameter
from qibolab.execution_parameters import (
ExecutionParameters,
AveragingMode,
Expand All @@ -47,11 +47,11 @@ around the pre-defined frequency.
sequence = natives.MZ.create_sequence()

# allocate frequency sweeper
f0 = platform.config(str(qubit.probe.name)).frequency
sweeper = Sweeper(
parameter=Parameter.frequency,
values=np.arange(-2e8, +2e8, 1e6),
values=f0 + np.arange(-2e8, +2e8, 1e6),
channels=[qubit.probe.name],
type=SweeperType.OFFSET,
)

We then define the execution parameters and launch the experiment.
Expand All @@ -75,9 +75,7 @@ In few seconds, the experiment will be finished and we can proceed to plot it.

acq = sequence.acquisitions[0][1]
amplitudes = magnitude(results[acq.id][0])
frequencies = (
np.arange(-2e8, +2e8, 1e6) + platform.config(str(qubit.probe.name)).frequency
)
frequencies = sweeper.values

plt.title("Resonator Spectroscopy")
plt.xlabel("Frequencies [Hz]")
Expand Down Expand Up @@ -116,7 +114,7 @@ complex pulse sequence. Therefore with start with that:
from qibolab.pulses import Pulse, Delay, Gaussian
from qibolab.sequence import PulseSequence
from qibolab.result import magnitude
from qibolab.sweeper import Sweeper, SweeperType, Parameter
from qibolab.sweeper import Sweeper, Parameter
from qibolab.execution_parameters import (
ExecutionParameters,
AveragingMode,
Expand All @@ -143,11 +141,11 @@ complex pulse sequence. Therefore with start with that:
sequence.concatenate(natives.MZ.create_sequence())

# allocate frequency sweeper
f0 = platform.config(str(qubit.probe.name)).frequency
sweeper = Sweeper(
parameter=Parameter.frequency,
values=np.arange(-2e8, +2e8, 1e6),
values=f0 + np.arange(-2e8, +2e8, 1e6),
channels=[qubit.drive.name],
type=SweeperType.OFFSET,
)

Note that the drive pulse has been changed to match the characteristics required
Expand All @@ -168,9 +166,7 @@ We can now proceed to launch on hardware:

_, acq = next(iter(sequence.acquisitions))
amplitudes = magnitude(results[acq.id][0])
frequencies = (
np.arange(-2e8, +2e8, 1e6) + platform.config(str(qubit.drive.name)).frequency
)
frequencies = sweeper.values

plt.title("Resonator Spectroscopy")
plt.xlabel("Frequencies [Hz]")
Expand Down Expand Up @@ -223,7 +219,7 @@ and its impact on qubit states in the IQ plane.
from qibolab.pulses import Delay
from qibolab.sequence import PulseSequence
from qibolab.result import unpack
from qibolab.sweeper import Sweeper, SweeperType, Parameter
from qibolab.sweeper import Sweeper, Parameter
from qibolab.execution_parameters import (
ExecutionParameters,
AveragingMode,
Expand Down
15 changes: 0 additions & 15 deletions src/qibolab/sweeper.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import operator
from dataclasses import dataclass
from enum import Enum, auto
from functools import partial
from typing import Optional

import numpy.typing as npt
Expand Down Expand Up @@ -32,14 +30,6 @@ class Parameter(Enum):
BIAS = Parameter.bias


class SweeperType(Enum):
"""Type of the Sweeper."""

ABSOLUTE = partial(lambda x, y=None: x)
FACTOR = operator.mul
OFFSET = operator.add


ChannelParameter = {
Parameter.frequency,
Parameter.bias,
Expand Down Expand Up @@ -90,7 +80,6 @@ class Sweeper:
values: npt.NDArray
pulses: Optional[list] = None
channels: Optional[list] = None
type: Optional[SweeperType] = SweeperType.ABSOLUTE

def __post_init__(self):
if self.pulses is not None and self.channels is not None:
Expand All @@ -110,10 +99,6 @@ def __post_init__(self):
"Cannot create a sweeper without specifying pulses or channels."
)

def get_values(self, base_value):
"""Convert sweeper values depending on the sweeper type."""
return self.type.value(self.values, base_value)


ParallelSweepers = list[Sweeper]
"""Sweepers that should be iterated in parallel."""

0 comments on commit 4756d54

Please sign in to comment.