Making a custom device model — ParityOS 2.1.1 documentation

# Making a custom device model

In the quickstart section, we have discussed the default devices for both analog and digital quantum computing. In this document, we will show how to define a device from scratch.

Nevertheless, we strongly recommend to contact ParityQC first, so that we can provide a customized device model for your hardware that takes maximum advantage of the hardware-specific features of the ParityOS compiler.

## Device models

Custom device models can be defined by creating a new device model class, derived from the `DeviceModelBase` class:

    class MyDeviceModel(DeviceModelBase):
        """
        Describes my splendid quantum computing device.
        """

The relevant attributes of such a class are:
  `qubit_connections`

    A list of connections of qubits that are implemented as direct interactions on the device. All qubits should be labeled with two-dimensional coordinates, as in `Qubit((i, j))`.

  `device_type`

    The type of this device, can either be `'cnot'` or `'plaquette'`.

  `preset`

    Determines the parameters that are passed on to the compiler in order to optimize the code for the device. Standard values are `'analog_default'` and `'digital_default'`. Customer-specific presets can be provided upon request.

## Analog device model

In this example we will define a custom device which has 5 qubits in the following layout:

    (0, 1) -- (1, 1) -- (2, 1)
      |          |     /
      |          |   /
    (0, 0) -- (1, 0)

Where on the square we can have a 4-body constraint as well as all 4 possible 3-body constraints. On the triangle on the left, only a 3-body constraint is possible. All qubits have the ability to get a local field in the `Z` direction.

We start by labeling all the qubit sites. We just have to assign a unique label to each qubit. This can be a string, an integer or a coordinate as in the above example.:

    qubit_sites = Qubit((0, 0)), Qubit((1, 0)), Qubit((0, 1)), Qubit((1, 1)), Qubit((2, 1))

Then for the `qubit_connections` we have to specify that each qubit can get a local field, which is a single-qubit collection. For example, a local field on qubit `Qubit((0, 0))` would become `{Qubit((0, 0))}`. In the above example, the local fields are:

    local_fields = [{qubit} for qubit in qubit_sites]

The next step is to define all possible constraints that are possible on the device, where each constraint is a collection of 3 or 4 qubits. The order of the qubits in the constraint, as well as the order of the constraints does not matter:

    constraint_connections = [
        {Qubit((0, 0)), Qubit((0, 1)), Qubit((1, 0)), Qubit((1, 1))},
        {Qubit((0, 0)), Qubit((0, 1)), Qubit((1, 0))},
        {Qubit((0, 0)), Qubit((0, 1)), Qubit((1, 1))},
        {Qubit((0, 0)), Qubit((1, 0)), Qubit((1, 1))},
        {Qubit((0, 1)), Qubit((1, 0)), Qubit((1, 1))},
        {Qubit((1, 0)), Qubit((1, 1)), Qubit((2, 1))},
    ]

Where the first element is the 4-body constraint, after which the 4 possible 3-body constraints on the left plaquette are listed. Finally, there is the 3-body constraint on the right plaquette.

Now we have all the information we need to define the device model, where it is important that we set `device_type = 'plaquette'`. We can also specify a preset for the compiler, e.g. `preset = 'default_analog'`:

    from parityos.device_model import DeviceModelBase

    class MyDeviceModel(DeviceModelBase):
        qubit_connections = local_fields + constraint_connections
        device_type = 'plaquette'
        preset = 'analog_default'

So to define the custom device, you can run the following code:

    from parityos import Qubit
    from parityos.device_model import DeviceModelBase

    qubit_sites = Qubit((0, 0)), Qubit((1, 0)), Qubit((0, 1)), Qubit((1, 1)), Qubit((2, 1))
    local_fields = [{qubit} for qubit in qubit_sites]

    constraint_connections = [
        {Qubit((0, 0)), Qubit((0, 1)), Qubit((1, 0)), Qubit((1, 1))},
        {Qubit((0, 0)), Qubit((0, 1)), Qubit((1, 0))},
        {Qubit((0, 0)), Qubit((0, 1)), Qubit((1, 1))},
        {Qubit((0, 0)), Qubit((1, 0)), Qubit((1, 1))},
        {Qubit((0, 1)), Qubit((1, 0)), Qubit((1, 1))},
        {Qubit((1, 0)), Qubit((1, 1)), Qubit((2, 1))},
    ]

    class MyDeviceModel(DeviceModelBase):
        qubit_connections = local_fields + constraint_connections
        device_type = 'plaquette'
        preset = 'analog_default'

    my_device_model = MyDeviceModel()

Note

If you want to try out this custom device, use the standard submission (see Submitting a Job to the Compiler) to compile the following optimization problem:

    optimization_problem = ProblemRepresentation(
        interactions=[{Qubit(0), Qubit(1)},
                      {Qubit(1), Qubit(2)},
                      {Qubit(1), Qubit(3)},
                      {Qubit(0), Qubit(3)},
                      {Qubit(2), Qubit(3)}],
        coefficients=[1, 0.5, -0.7, 0.8, -0.3],
    )

where you set the `device_model` to the one we defined here!

## Digital device model

In this example we will define a custom device which has 5 qubits in the following layout:

    (0, 1) -- (1, 1) -- (2, 1)
      |          |
      |          |
    (0, 0) -- (1, 0)

On all qubits that are connected with a dotted line, it is possible to do a CNOT gate. All qubits have the ability to get a local field in the `Z` direction.

We start by defining all the qubit sites (the order does not matter):

    qubit_sites = Qubit((0, 0)), Qubit((1, 0)), Qubit((0, 1)), Qubit((1, 1)), Qubit((2, 1))

Then for the `qubit_connections` we have to specify that each qubit can get a local field, which is a single-qubit collection. For example, a local field on qubit `Qubit((0, 0))` would be a collection with a single qubit, `{Qubit((0, 0))}`. The local fields are:

    local_fields = [{qubit} for qubit in qubit_sites]

The next step is to define all possible CNOT connection that are possible on the device. The order of the elements in the connections does not matter, it is assumed that a CNOT can be performed in both directions. This results in the following lists of connections, where each connection is of length `2`:

    constraint_connections = [
        {Qubit((0, 0)), Qubit((0, 1))},
        {Qubit((0, 1)), Qubit((1, 1))},
        {Qubit((0, 0)), Qubit((1, 0))},
        {Qubit((1, 0)), Qubit((1, 1))},
        {Qubit((1, 1)), Qubit((2, 1))},
    ]

Now we have all the information we need to define the DeviceModel, where it is important that we set `device_type = 'cnot'`. In order to use the standard compiler for digital devices, we add the attribute `preset = 'default_digital'`:

    from parityos.device_model import DeviceModelBase

    class MyDigitalDeviceModel(DeviceModelBase):
        qubit_connections = local_fields + constraint_connections
        device_type = 'cnot'
        preset = 'digital_default'

So to define the custom device, you can run the following code:

    from parityos import Qubit
    from parityos.device_model import DeviceModelBase

    qubit_sites = Qubit((0, 0)), Qubit((1, 0)), Qubit((0, 1)), Qubit((1, 1)), Qubit((2, 1))
    local_fields = [{qubit} for qubit in qubit_sites]
    constraint_connections = [
        {Qubit((0, 0)), Qubit((0, 1))},
        {Qubit((0, 1)), Qubit((1, 1))},
        {Qubit((0, 0)), Qubit((1, 0))},
        {Qubit((1, 0)), Qubit((1, 1))},
        {Qubit((1, 1)), Qubit((2, 1))},
    ]

    class MyDigitalDeviceModel(DeviceModelBase):
        qubit_connections = local_fields + constraint_connections
        device_type = 'cnot'
        preset = 'digital_default'

    my_digital_device_model = MyDigitalDeviceModel()

Note

If you want to try out this custom device, use the standard submission (see Submitting a Job to the Compiler) to compile the following optimization problem:

    optimization_problem = ProblemRepresentation(
        interactions=[{Qubit(0), Qubit(1)},
                      {Qubit(1), Qubit(2)},
                      {Qubit(1), Qubit(3)},
                      {Qubit(0), Qubit(3)},
                      {Qubit(2), Qubit(3)}],
        coefficients=[1, 0.5, -0.7, 0.8, -0.3],
    )

where you set the `device_model` to the `my_digital_device_model` defined above.