First experiment#

The aim of this introductory example is to present the main aspects of the Qibolab interface, bringing up all the required ingredients for an actual execution.

For configuration simplicity, a dummy instrument will be used. Since its only purpose is to generate purely random numbers, respecting the layout. At the end, the results will be compared with those resulting from a more comprehensive tutorial, based on the numerically computed evolution of a simulated system.

Define the platform#

To launch experiments on quantum hardware, users have first to define their platform.

For a first experiment, let’s define a single qubit platform. In this example, the qubit is only coupled to a drive channel and a transmission line. Where the latter is represented by a pair of related entities: an output probe channel, and an input acquisition channel.

from qibolab import AcquisitionChannel, Hardware, IqChannel, Qubit
from qibolab.instruments.dummy import DummyInstrument


def create() -> Hardware:
    # Define qubit
    qubits = {0: Qubit.default(0)}

    # Create channels and connect to instrument ports
    channels = {}
    qubit = qubits[0]
    # Readout
    channels[qubit.probe] = IqChannel(mixer=None, lo=None)
    # Acquire
    channels[qubit.acquisition] = AcquisitionChannel(probe=qubit.probe, twpa_pump=None)
    # Drive
    channels[qubit.drive] = IqChannel(mixer=None, lo=None)

    # Define instruments
    controller = DummyInstrument(address="192.168.0.101:80", channels=channels)

    # Define and return platform
    return Hardware(instruments={"dummy": controller}, qubits=qubits)

And the we can define the following parameters (the exact content is not yet relevant, and it will be explained in the related section).

Parameters dictionary
parameters = {
    "settings": {"nshots": 1000, "relaxation_time": 70000},
    "configs": {
        "dummy/bounds": {
            "kind": "bounds",
            "waveforms": 0,
            "readout": 0,
            "instructions": 0,
        },
        "0/drive": {"kind": "iq", "frequency": 4833726197},
        "0/probe": {"kind": "iq", "frequency": 7320000000},
        "0/acquisition": {
            "kind": "acquisition",
            "delay": 224,
            "smearing": 0,
            "threshold": 0.002100861788865835,
            "iq_angle": -0.7669877581038627,
        },
    },
    "native_gates": {
        "single_qubit": {
            "0": {
                "RX": [
                    [
                        "0/drive",
                        {
                            "kind": "pulse",
                            "duration": 40,
                            "amplitude": 0.5,
                            "envelope": {"kind": "gaussian", "rel_sigma": 3.0},
                        },
                    ],
                ],
                "MZ": [
                    [
                        "0/acquisition",
                        {
                            "kind": "readout",
                            "acquisition": {
                                "kind": "acquisition",
                                "duration": 2000.0,
                            },
                            "probe": {
                                "kind": "pulse",
                                "duration": 2000.0,
                                "amplitude": 0.003,
                                "envelope": {"kind": "rectangular"},
                            },
                        },
                    ]
                ],
            }
        },
        "two_qubit": {},
    },
}

Finally, we can instantiate the defined platform as follows:

from qibolab import Platform, Parameters

params = Parameters.model_validate(parameters)
platform = Platform(name="my_platform", parameters=params, **vars(create()))

Note

In this case, even defining create() and parameters separately appears redundant. However, this pattern is particularly convenient to separate the established devices arrangement, which is considered to be the fixed part of the platform, from the set of parameters, that are instead subject to calibration.

The division is especially useful to store platforms as files. Qibolab also supplies built-in machinery to load these stored platforms, as described in the Platforms storage tutorial.

Further information about defining platforms is provided in the Platforms page, and several examples can be found at the TII QRC lab-dedicated repository.

Perform an experiment#

Once the platform is available, we can finally use it to execute an experiment.

One of the simplest options is a single-shot classification. It will make limited usage of the available Experiment API, which will be explored in its dedicated guide, or in further tutorials.

Here it is the required code:

import matplotlib.pyplot as plt

from qibolab import AcquisitionType

# access the native gates
gates = platform.natives.single_qubit[0]

results = []
# iterate over pulse sequences
for sequence in [gates.MZ(), gates.RX() | gates.MZ()]:
    # perform the experiment using specific options
    signal = platform.execute(
        [sequence],
        nshots=1000,
        acquisition_type=AcquisitionType.INTEGRATION,
    )
    _, acq = next(iter(sequence.acquisitions))

    # collect the results
    sig = signal[acq.id]
    results.append([sig[..., 0], sig[..., 1]])

plt.title("Single shot classification")
plt.xlabel("In-phase [a.u.]")
plt.ylabel("Quadrature [a.u.]")

plt.scatter(*results[0], label="0")
plt.scatter(*results[1], label="1")
plt.legend()

The main features of this snippet are:

  • the calibrated native gates are accessed from the platform parameters

  • they are used to construct a sequence (e.g. gates.RX() | gates.MZ())

  • the sequence is executed by the platform

  • the results consist of a dictionary, mapping the identifier of the acquisition operations to the arrays of results, which are organized over multiple dimensions (more in the Results section)

As announced from the beginning, the results are pure white noise:

../_images/dummy-single-shot.svg

This is because the platform we defined adopted a dummy instrument, which is mainly provided for debugging purpose.

Using a more meaningful platform, e.g. one based on QPU numerical simulation, the result would have been the following

../_images/emulator-single-shot.svg