How to connect Qibolab to your lab?#

In this section we will show how to let Qibolab communicate with your lab’s instruments and run an experiment.

The main required object, in this case, is the qibolab.Platform. A Platform is defined as a QPU (quantum processing unit with one or more qubits) controlled by one ore more instruments.

How to define a platform for a self-hosted QPU?#

The qibolab.Platform object holds all the information required to execute programs, and in particular qibolab.PulseSequence in a real QPU. It is comprised by different objects that contain information about the native gates and the lab’s instrumentation.

This is permanently stored as its constructing function, create(), defined in a source file, but whose data could be stored in a package-defined format, for which loading (and even dumping) methods are provided. The details of this process are explained in the following sections.

Note

The main distinction between the content of the Python source file and the parameters stored as data is based on the possibility to automatically read and consume these parameters, and possibly even update them in a calibration process.

More parameters may be introduced, and occasionally some platforms are defining them in the source, in a first stage.

The general idea is to retain as much flexibility as possible, while avoiding the custom handling of commonly structured data by each platform, that would also complicate its handling by downstream projects.

Registering platforms#

The create() function described in the above example can be called or imported directly in any Python script. Alternatively, it is also possible to make the platform available as

from qibolab import create_platform

platform = create_platform("my_platform")

To do so, create() needs to be saved in a module called platform.py inside a folder with the name of this platform (in this case my_platform). Moreover, the environment flag QIBOLAB_PLATFORMS needs to point to the directory that contains this folder. Examples of advanced platforms are available at this repository.

Loading platform parameters from JSON#

Operating a QPU requires calibrating a set of parameters, the number of which increases with the number of qubits. Hardcoding such parameters in the create() function is not scalable. However, since create() is part of a Python module, is is possible to load parameters from an external file or database.

Qibolab provides some utility functions, accessible through qibolab._core.parameters, for loading calibration parameters stored in a JSON file with a specific format. Here is an example

{
  "settings": {
    "nshots": 1024,
    "relaxation_time": 50000
  },
  "configs": {
    "0/drive": {
      "kind": "iq",
      "frequency": 4855663000
    },
    "1/drive": {
      "kind": "iq",
      "frequency": 5800563000
    },
    "0/flux": {
      "kind": "dc",
      "offset": 0.0
    },
    "1/flux": {
      "kind": "dc",
      "offset": 0.0
    },
    "0/probe": {
      "kind": "iq",
      "frequency": 7453265000
    },
    "1/probe": {
      "kind": "iq",
      "frequency": 7655107000
    },
    "0/acquisition": {
      "kind": "acquisition",
      "delay": 0,
      "smearing": 0
    },
    "1/acquisition": {
      "kind": "acquisition",
      "delay": 0,
      "smearing": 0
    },
    "01/coupler": {
      "kind": "dc",
      "offset": 0.12
    }
  },
  "native_gates": {
    "single_qubit": {
      "0": {
        "RX": [
          [
            "0/drive",
            {
              "kind": "pulse",
              "duration": 40,
              "amplitude": 0.0484,
              "envelope": {
                "kind": "drag",
                "rel_sigma": 0.2,
                "beta": -0.02
              }
            }
          ]
        ],
        "MZ": [
            [
              "0/acquisition",
              {
                  "kind": "readout",
                  "acquisition": {
                      "kind": "acquisition",
                      "duration": 620.0
                  },
                  "probe": {
                      "kind": "pulse",
                      "duration": 620.0,
                      "amplitude": 0.003575,
                      "envelope": {
                          "kind": "rectangular"
                      }
                  }
              }
          ]
        ]
      },
      "1": {
        "RX": [
          [
            "1/drive",
            {
              "kind": "pulse",
              "duration": 40,
              "amplitude": 0.05682,
              "envelope": {
                "kind": "drag",
                "rel_sigma": 0.2,
                "beta": -0.04
              }
            }
          ]
        ],
        "MZ": [
          [
            "1/acquisition",
            {
                "kind": "readout",
                "acquisition": {
                    "kind": "acquisition",
                    "duration": 960.0
                },
                "probe": {
                    "kind": "pulse",
                    "duration": 960.0,
                    "amplitude": 0.00325,
                    "envelope": {
                        "kind": "rectangular"
                    }
                }
            }
          ]
        ]
      }
    },
    "two_qubit": {
      "0-1": {
        "CZ": [
          [
            "01/coupler",
            {
              "kind": "pulse",
              "duration": 40,
              "amplitude": 0.1,
              "envelope": {
                "kind": "rectangular"
              }
            }
          ],
          [
            "0/flux",
            {
              "kind": "pulse",
              "duration": 30,
              "amplitude": 0.6025,
              "envelope": {
                "kind": "rectangular"
              }
            }
          ],
          [
            "0/drive",
            {
              "kind": "virtualz",
              "phase": -1
            }
          ],
          [
            "1/drive",
            {
              "kind": "virtualz",
              "phase": -3
            }
          ]
        ]
      }
    }
  }
}

This file contains different sections: configs defines the default configuration of channel parameters, while native_gates specifies the calibrated pulse parameters for implementing single and two-qubit gates. Note that such parameters may slightly differ depending on the QPU architecture.

Providing the above JSON is not sufficient to instantiate a qibolab.Platform. This should still be done using a create() method. The create() method should be put in a file named platform.py inside the my_platform directory. Here is the create() method that loads the parameters from the JSON:

# my_platform / platform.py

from pathlib import Path
from qibolab import (
    AcquisitionChannel,
    DcChannel,
    IqChannel,
    Platform,
    Qubit,
)
from qibolab.instruments import DummyInstrument


FOLDER = Path.cwd()


def create():
    qubits = {}
    for q in range(2):
        qubits[q] = Qubit(
            drive=f"{q}/drive",
            flux=f"{q}/flux",
            probe=f"{q}/probe",
            acquisition=f"{q}/acquisition",
        )

    couplers = {0: Qubit(flux="01/coupler")}

    channels = {}
    for q in range(2):
        channels[qubits[q].drive] = IqChannel(
            device="my_instrument", path="1", mixer=None, lo=None
        )
        channels[qubits[q].flux] = DcChannel(device="my_instrument", path="2")
        channels[qubits[q].probe] = IqChannel(
            device="my_instrument", path="0", mixer=None, lo=None
        )
        channels[qubits[q].acquisition] = AcquisitionChannel(
            device="my_instrument", path="0", twpa_pump=None, probe=qubits[q].probe
        )

    channels[couplers[0].flux] = DcChannel(device="my_instrument", path="5")

    instruments = {
        "my_instrument": DummyInstrument(
            name="my_instrument", address="0.0.0.0:0", channels=channels
        )
    }

    return Platform.load(FOLDER, instruments, qubits, couplers=couplers)

Note that this assumes that the JSON with parameters is saved as <folder>/parameters.json where <folder> is the directory containing platform.py.

Instrument settings#

The parameters of the previous example contains only parameters associated to the channel configuration and the native gates. In some cases parameters associated to instruments also need to be calibrated. An example is the frequency and the power of local oscillators, such as the one used to pump a traveling wave parametric amplifier (TWPA).

The parameters JSON can contain such parameters in the configs section:

{
    "settings": {
        "nshots": 1024,
        "relaxation_time": 50000
    },
    "configs": {
        "twpa_pump": {
            "kind": "oscillator",
            "frequency": 4600000000,
            "power": 5
        }
    },
}

Note that the key used in the JSON have to be the same with the instrument name used in the instrument dictionary when instantiating the qibolab.Platform, in this case "twpa_pump".

# my_platform / platform.py

from pathlib import Path
from qibolab import (
    AcquisitionChannel,
    DcChannel,
    IqChannel,
    Platform,
    Qubit,
)
from qibolab.instruments import DummyInstrument


FOLDER = Path.cwd()


def create():
    qubits = {}
    for q in range(2):
        qubits[q] = Qubit(
            drive=f"{q}/drive",
            flux=f"{q}/flux",
            probe=f"{q}/probe",
            acquisition=f"{q}/acquisition",
        )

    couplers = {0: Qubit(flux="01/coupler")}

    channels = {}
    for q in range(2):
        channels[qubits[q].drive] = IqChannel(
            device="my_instrument", path="1", mixer=None, lo=None
        )
        channels[qubits[q].flux] = DcChannel(device="my_instrument", path="2")
        channels[qubits[q].probe] = IqChannel(
            device="my_instrument", path="0", mixer=None, lo=None
        )
        channels[qubits[q].acquisition] = AcquisitionChannel(
            device="my_instrument", path="0", twpa_pump=None, probe=qubits[q].probe
        )

    channels[couplers[0].flux] = DcChannel(device="my_instrument", path="5")

    instruments = {
        "my_instrument": DummyInstrument(
            name="my_instrument", address="0.0.0.0:0", channels=channels
        ),
        "twpa_pump": DummyLocalOscillator(name="twpa_pump", address="0.0.0.1:0"),
    }

    return Platform.load(FOLDER, instruments, qubits, couplers=couplers)