Commit 13fc26b5 authored by Edgar Reehuis's avatar Edgar Reehuis
Browse files

[Docs Qblox backend] Fix/style Cluster docs and remove Pulsar docs

parent 7c9d4ffc
Loading
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -306,12 +306,12 @@ pprint(dut.generate_device_config())
## Specifying pulse magnitudes

```{warning}
Please note that currently only the user interface of the reference magnitude is defined. The physical magnitude scaling has not yet been implemented in any hardware backend. Therefore, changing the reference magnitude parameter will not have any physical effect in the current version of quantify scheduler.
Please note that currently only the user interface of the reference magnitude is defined. The physical magnitude scaling has not yet been implemented in any hardware backend. Therefore, changing the reference magnitude parameter will not have any physical effect in the current version of `quantify-scheduler`.
```

In the course of a quantum experiment, we may use a variety of electronic pulses of varying amplitudes or powers. The size of the pulses can potentially vary across many orders of magnitude. Additionally, there are many ways in which we might express the magnitude of said pulses (such as amplitude in volts, current in amps or power in dBm), which we may use in different contexts. When generating arbitrary waveforms, it is typical to use a DAC with limited dynamic range, so only a limited range of pulse amplitudes may be generated electronically. Therefore, devices such as variable attenuators or amplifiers are often used to produce pulses over many different orders of magnitude. In quantify, we provide the functionality to express the absolute magnitude of pulses in a range of different units, as well as to define the amplitude of any signal produced by an arbitrary waveform generator.

In the definition of any pulse operation in Quantify Scheduler, there will typically be two parameters related to the magnitude of the pulse: the `amplitude` and the `reference_magnitude`. The `amplitude` is a unitless parameter which expresses the amplitude of the signal produced by the DAC of the signal generator, relative to the maximum output level of the DAC. The `amplitude` can vary from -1 to 1, where 1 is the maximum output level. Since `amplitude` is a relative scale, it does not express the absolute power level of the signal that reaches the device. This will be affected by a number of different variables, including any attenuation or gain that is applied to the signal after it produced by the DAC. In order to specify the magnitude of the signal in absolute terms, we have the `reference_magnitude` parameter. This provides a reference scale for the absolute magnitude of the signal when it reaches the device port in question. The reference magnitude can be specified in a number of different units: Volts, current in Amperes or power in dBm or W, depending on the physical implementation of the control pulse. The scaling is defined such that the power/amplitude of a pulse with amplitude 1 will have a value equal to the `reference_magnitude` when it reaches the port. How exactly this scaling is implemented physically will depend on the hardware backend. For example, it may be that a variable attenuation is applied to the pulse in order to scale its power to the right level. Or the gain of an amplifier will be varied. Whenever a quantify schedule is compiled, the quantify compilation backend will automatically compile all instructions necessary for all hardware instruments required to scale the pulse magnitude to the correct level.
In the definition of any pulse operation in `quantify-scheduler`, there will typically be two parameters related to the magnitude of the pulse: the `amplitude` and the `reference_magnitude`. The `amplitude` is a unitless parameter which expresses the amplitude of the signal produced by the DAC of the signal generator, relative to the maximum output level of the DAC. The `amplitude` can vary from -1 to 1, where 1 is the maximum output level. Since `amplitude` is a relative scale, it does not express the absolute power level of the signal that reaches the device. This will be affected by a number of different variables, including any attenuation or gain that is applied to the signal after it produced by the DAC. In order to specify the magnitude of the signal in absolute terms, we have the `reference_magnitude` parameter. This provides a reference scale for the absolute magnitude of the signal when it reaches the device port in question. The reference magnitude can be specified in a number of different units: Volts, current in Amperes or power in dBm or W, depending on the physical implementation of the control pulse. The scaling is defined such that the power/amplitude of a pulse with amplitude 1 will have a value equal to the `reference_magnitude` when it reaches the port. How exactly this scaling is implemented physically will depend on the hardware backend. For example, it may be that a variable attenuation is applied to the pulse in order to scale its power to the right level. Or the gain of an amplifier will be varied. Whenever a quantify schedule is compiled, the quantify compilation backend will automatically compile all instructions necessary for all hardware instruments required to scale the pulse magnitude to the correct level.

The reference magnitude and amplitude of a pulse can both be configured via QCoDeS parameters in the device element. The pulse amplitude can be configured via a standard QCoDeS parameter, for example `qubit.rxy.amp180` can be used to set the pi-pulse amplitude of an RXY operation of a transmon qubit (between -1 and 1). The reference magnitude is configured slightly differently. Because of the need to express the reference magnitude in a variety of different units in different contexts, the reference magnitude is configured via a custom QCoDeS submodule within the device element - of class {class}`~quantify_scheduler.device_under_test.transmon_element.ReferenceMagnitude`. For example, we may have the submodule `qubit.rxy.reference_magnitude`, which is used to scale the amplitudes of the aforementioned RXY pulses. The {class}`~quantify_scheduler.device_under_test.transmon_element.ReferenceMagnitude` submodule contains three parameters, one for each of the possible units which the reference magnitude may be expressed in: Volts, dBm and Amperes. Only one of these unit parameters may have a defined numerical value at any given time. That is, if `reference_magnitude.dBm` is set to a particular value, both of the other parameters will automatically be set to `nan`. This allows the reference magnitude to be uniquely defined with respect to a particular unit. The defined unit and value of reference magnitude can be inquired via the `get_val_unit` method of the submodule, which returns both the numerical value and the unit in a tuple. If all of the reference magnitude parameters are `nan`, then no reference magnitude is defined and no extra scaling will be applied to the pulse.

+212 −83

File changed.

Preview size limit exceeded, changes collapsed.

docs/tutorials/qblox/Pulsar.md

deleted100644 → 0
+0 −536
Original line number Diff line number Diff line
---
file_format: mystnb
kernelspec:
    name: python3

---
(sec-qblox-pulsar)=

# Pulsar QCM/QRM

```{warning}
Pulsar hardware is deprecated. Use cluster modules instead if possible!
```

```{code-cell} ipython3
---
mystnb:
  remove_code_source: true
  remove_code_outputs: true
---

# in the hidden cells we include some code that checks for correctness of the examples
from tempfile import TemporaryDirectory

from quantify_scheduler.device_under_test.quantum_device import QuantumDevice
from quantify_scheduler.operations import pulse_library
from quantify_scheduler.compilation import determine_absolute_timing
from quantify_scheduler.backends.qblox_backend import hardware_compile
from quantify_scheduler import Schedule
from quantify_scheduler.resources import ClockResource

from quantify_core.data.handling import set_datadir

temp_dir = TemporaryDirectory()
set_datadir(temp_dir.name)
```

Each device in the setup can be individually configured using the entry in the config. For instance:

```{code-cell} ipython3
---
mystnb:
  remove_code_outputs: true
  number_source_lines: true
---

hardware_compilation_cfg = {
    "backend": "quantify_scheduler.backends.qblox_backend.hardware_compile",
    "hardware_description": {
        "qcm0": {
            "hardware_type": "Qblox",
            "instrument_type": "Pulsar_QCM",
            "ref": "internal",
        },
       "lo0": {"hardware_type": "LocalOscillator", "power": 20},
       "lo1": {"hardware_type": "LocalOscillator", "power": 20} 
    },
    "hardware_options": {
        "modulation_frequencies": {
            "q0:mw-q0.01": {"interm_freq": 50e6},
            "q1:mw-q1.01": {"lo_freq": 7.2e9}
        }
    },
    "connectivity": {
        "qcm0": {
            "complex_output_0": {
                "lo_name": "lo0",
                "portclock_configs": [
                    {
                        "port": "q0:mw",
                        "clock": "q0.01",
                    }
                ]
            },
            "complex_output_1": {
                "lo_name": "lo1",
                "portclock_configs": [
                    {
                        "port": "q1:mw",
                        "clock": "q1.01",
                    }
                ]
            }
        },
    }
}
```

```{code-cell} ipython3
---
mystnb:
  remove_code_source: true
  remove_code_outputs: true
---

test_sched = Schedule("test_sched")
test_sched.add(
    pulse_library.SquarePulse(amp=0.2, duration=1e-6, port="q0:mw", clock="q0.01")
)
test_sched.add_resource(ClockResource(name="q0.01", freq=7e9))
test_sched = determine_absolute_timing(test_sched)

quantum_device = QuantumDevice("DUT")
quantum_device.hardware_config(hardware_compilation_cfg)

hardware_compile(schedule=test_sched, config=quantum_device.generate_compilation_config())
```

Here we specify a setup containing only a Pulsar QCM, with both outputs connected to local oscillator sources.

The first entry specifies the backend, the function that will compile a schedule using the information specified in this hardware compilation config.
The other entries specify the instruments that are used (`"hardware_description"`), how they are connected to ports on the quantum device (`"connectivity"`), and options used in the compilation (`"hardware_options"`).
The instrument names need to match the names of the corresponding QCoDeS instruments.

## Hardware description

The hardware description of {code}`"qcm0"` contain settings and information for the entire device:

```{eval-rst}
.. autoclass:: quantify_scheduler.backends.types.qblox.PulsarQCMDescription
    :noindex:
    :members:
    :inherited-members: BaseModel

```

The examples given below will be for a single Pulsar QCM, but the other devices can be configured similarly. In order to use a Pulsar QRM, QCM-RF or QRM-RF, change the {code}`"instrument_type"` entry to {code}`"Pulsar_QRM"`, {code}`"Pulsar_QCM_RF"` or {code}`"Pulsar_QRM_RF"`
respectively. Multiple devices can be added to the config, similar to how we added the local oscillators in the example given above.
The name of the Pulsar (the key of the structure, `"qcm0"` in the example) can be chosen freely.

## Connectivity
The {class}`~.backends.graph_compilation.Connectivity` describes how the inputs/outputs of the Pulsar are connected to ports on the {class}`~.device_under_test.quantum_device.QuantumDevice`.

```{note}
The {class}`~.backends.graph_compilation.Connectivity` datastructure is currently under development. Information on the connectivity between port-clock combinations on the quantum device and ports on the control hardware is currently included in the old-style hardware configuration file, which should be included in the `"connectivity"` field of the {class}`~.backends.graph_compilation.HardwareCompilationConfig`.
```

Most notably under the {code}`complex_output_0`, we specify the port-clock combinations the output may target (see the {ref}`User guide <sec-user-guide>`
for more information on the role of ports and clocks within `quantify-scheduler`).

```{code-block} python
:linenos: true

"portclock_configs": [
    {
        "port": "q0:mw",
        "clock": "q0.01",
    }
]
```

### Usage without an LO

In order to use the backend without an LO, we simply remove the {code}`"lo_name"` and all other related parameters. This includes the
mixer correction parameters as well as the frequencies.

```{code-cell} ipython3
---
mystnb:
  number_source_lines: true
  remove_code_outputs: true
---

hardware_compilation_cfg = {
    "backend": "quantify_scheduler.backends.qblox_backend.hardware_compile",
    "hardware_description": {
        "qcm0": {
            "hardware_type": "Qblox",
            "instrument_type": "Pulsar_QCM",
            "ref": "internal",
        },
    },
    "connectivity": {
        "qcm0": {
            "complex_output_0": {
                "portclock_configs": [
                    {
                        "port": "q0:mw",
                        "clock": "q0.01",
                    }
                ]
            },
            "complex_output_1": {
                "portclock_configs": [
                    {
                        "port": "q1:mw",
                        "clock": "q1.01",
                    }
                ]
            }
        },
    }
}
```

```{code-cell} ipython3
---
mystnb:
  remove_code_source: true
  remove_code_outputs: true
---

quantum_device.hardware_config(hardware_compilation_cfg)
hardware_compile(schedule=test_sched, config=quantum_device.generate_compilation_config())
```

### Frequency multiplexing

It is possible to do frequency multiplexing of the signals by adding multiple port-clock configurations to the same output.

```{code-cell} ipython3
---
mystnb:
  number_source_lines: true
  remove_code_outputs: true
---

hardware_compilation_cfg = {
    "backend": "quantify_scheduler.backends.qblox_backend.hardware_compile",
    "hardware_description": {
        "qcm0": {
            "hardware_type": "Qblox",
            "instrument_type": "Pulsar_QCM",
            "ref": "internal",
        },
    },
    "connectivity": {
        "qcm0": {
            "complex_output_0": {
                "portclock_configs": [
                    {
                        "port": "q0:mw",
                        "clock": "q0.01",
                    },
                    {
                        "port": "q0:mw",
                        "clock": "some_other_clock",
                    }
                ]
            },
            "complex_output_1": {
                "portclock_configs": [
                    {
                        "port": "q1:mw",
                        "clock": "q1.01",
                    }
                ]
            }
        },
    }
}
```

```{code-cell} ipython3
---
mystnb:
  remove_code_source: true
  remove_code_outputs: true
---

test_sched = Schedule("test_sched")
test_sched.add(
    pulse_library.SquarePulse(amp=1, duration=1e-6, port="q0:mw", clock="q0.01")
)
test_sched.add_resource(ClockResource(name="q0.01", freq=200e6))
test_sched.add_resource(ClockResource(name="some_other_clock", freq=100e6))

test_sched = determine_absolute_timing(test_sched)

quantum_device.hardware_config(hardware_compilation_cfg)
hardware_compile(schedule=test_sched, config=quantum_device.generate_compilation_config())
```

In the given example, we added a second port-clock configuration to output 0. Now any signal on port {code}`"q0:mw"` with clock {code}`"some_other_clock"` will be added digitally to the signal with the same port but clock {code}`"q0.01"`. The Qblox modules currently have six sequencers available, which sets the upper limit to our multiplexing capabilities.

```{note}
We note that it is a requirement of the backend that each combination of a port and a clock is unique, i.e. it is possible to use the same port or clock multiple times in the hardware config but the combination of a port with a certain clock can only occur once.
```

### Real mode

```{note}
This setting will soon move to a different place in the {class}`~.backends.graph_compilation.HardwareCompilationConfig`.
```

For the baseband modules, it is also possible to use the backend to generate signals for the outputs individually rather than using IQ pairs.

In order to do this, instead of {code}`"complex_output_X"`, we use {code}`"real_output_X"`. In the case of a QCM, we have four of those outputs. The QRM has two available.

The resulting config looks like this:

```{code-cell} ipython3
---
mystnb:
  number_source_lines: true
  remove_code_outputs: true
---

hardare_compilation_cfg = {
    "backend": "quantify_scheduler.backends.qblox_backend.hardware_compile",
    "hardware_description": {
        "qcm0": {
            "hardware_type": "Qblox",
            "instrument_type": "Pulsar_QCM",
            "ref": "internal"
        }
    },
    "connectivity": {
        "qcm0": {
            "real_output_0": {
                "portclock_configs": [
                    {
                        "port": "q0:mw",
                        "clock": "q0.01",
                    }
                ]
            },
            "real_output_1": {
                "portclock_configs": [
                    {
                        "port": "q1:mw",
                        "clock": "q1.01",
                    }
                ]
            },
            "real_output_2": {
                "portclock_configs": [
                    {
                        "port": "q2:mw",
                        "clock": "q2.01",
                    }
                ]
            }
        },
    }
}
```

```{code-cell} ipython3
---
mystnb:
  remove_code_source: true
  remove_code_outputs: true
---

test_sched = Schedule("test_sched")
test_sched.add(
    pulse_library.SquarePulse(amp=1, duration=1e-6, port="q0:mw", clock="q0.01")
)
test_sched.add(
    pulse_library.SquarePulse(amp=1, duration=1e-6, port="q1:mw", clock="q1.01")
)
test_sched.add_resource(ClockResource(name="q0.01", freq=200e6))
test_sched.add_resource(ClockResource(name="q1.01", freq=100e6))

test_sched = determine_absolute_timing(test_sched)
quantum_device.hardware_config(hardware_compilation_cfg)

hardware_compile(schedule=test_sched, config=quantum_device.generate_compilation_config())
```

When using real outputs, the backend automatically maps the signals to the correct output paths. We note that for real outputs, it is not allowed to use any pulses that have an imaginary component i.e. only real-valued pulses are allowed. If you were to use a complex pulse, the backend will produce an error, e.g. square and ramp pulses are allowed but DRAG pulses are not.

```{warning}
When using real mode, we highly recommend using it in combination with the instrument coordinator as the outputs need to be configured correctly in order for this to function.
```

```{code-cell} ipython3
---
tags: [raises-exception]
mystnb:
  remove_code_source: true
  remove_code_outputs: true
---

test_sched.add(
    pulse_library.DRAGPulse(
        G_amp=1, D_amp=1, duration=1e-6, port="q1:mw", clock="q1.01", phase=0
    )
)

test_sched = determine_absolute_timing(test_sched)

hardware_compile(schedule=test_sched, config=quantum_device.generate_compilation_config())

```

(sec-qblox-pulsar-instruction-generated)=
### Instruction-generated pulses

```{note}
This setting will soon move to a different place in the {class}`~.backends.graph_compilation.HardwareCompilationConfig`.
```

```{warning}
The {code}`instruction_generated_pulses_enabled` option is deprecated and will be removed in a future version. Long square pulses and staircase pulses can be generated with the newly introduced {class}`~quantify_scheduler.operations.stitched_pulse.StitchedPulseBuilder`. More information can be found in the {ref}`relevant section of the Cluster user guide <sec-qblox-cluster-long-waveform-support>`.
```

The Qblox backend contains some intelligence that allows it to generate certain specific waveforms from the pulse library using a more complicated series of sequencer instructions, which helps conserve waveform memory. Though in order to keep the backend fully transparent, all such advanced capabilities are disabled by default.

In order to enable the advanced capabilities we need to add line {code}`"instruction_generated_pulses_enabled": True` to the port-clock configuration.

```{code-cell} ipython3
---
mystnb:
  number_source_lines: true
  remove_code_outputs: true
---

hardware_compilation_cfg = {
    "backend": "quantify_scheduler.backends.qblox_backend.hardware_compile",
    "hardware_description": {
        "qcm0": {
            "hardware_type": "Qblox",
            "instrument_type": "Pulsar_QCM",
            "ref": "internal",
        },
    },
    "connectivity": {
        "qcm0": {
            "complex_output_0": {
                "portclock_configs": [
                    {
                        "port": "q0:mw",
                        "clock": "q0.01",
                        "instruction_generated_pulses_enabled": True,
                    }
                ]
            }
        }
    }  
}
```

```{code-cell} ipython3
---
mystnb:
  remove_code_source: true
  remove_code_outputs: true
---

test_sched = Schedule("test_sched")
test_sched.add(
    pulse_library.SquarePulse(amp=1, duration=1e-3, port="q0:mw", clock="q0.01")
)

test_sched.add_resource(ClockResource(name="q0.01", freq=200e6))

test_sched = determine_absolute_timing(test_sched)

quantum_device.hardware_config(hardware_compilation_cfg)
hardware_compile(schedule=test_sched, config=quantum_device.generate_compilation_config())
```

Currently, this has the following effects:

- Long square pulses get broken up into separate pulses with durations \<= 1 us, which allows the modules to play square pulses longer than the waveform memory normally allows.
- Staircase pulses are generated using offset instructions instead of using waveform memory

## Hardware options
The {ref}`Hardware Options <sec-hardware-options>` provide a way of specifying some specific settings on the Pulsar.

### I/Q modulation

To perform upconversion using an I/Q mixer and an external local oscillator, simply specify a local oscillator in the `"connectivity"` using the {code}`lo_name` entry.
{code}`complex_output_0` is connected to a local oscillator instrument named
{code}`lo0` and {code}`complex_output_1` to {code}`lo1`.
Since the aim of `quantify-scheduler` is to only specify the final RF frequency when the signal arrives at the chip, rather than any parameters related to I/Q modulation, we specify this information here.

The backend assumes that upconversion happens according to the relation

```{math} f_{RF} = f_{IF} + f_{LO}
```

This means that in order to generate a certain {math}`f_{RF}`, we need to specify either an IF or an LO frequency. This is done in the `"modulation_frequencies"` within the `"hardware_options"`, where we either set the {code}`lo_freq` or the {code}`interm_freq` and leave the other to be calculated by the backend. Specifying both will raise an error if it violates {math}`f_{RF} = f_{IF} + f_{LO}`.

### Mixer corrections

The backend also supports setting the parameters that are used by the hardware to correct for mixer imperfections in real-time.

We configure this by adding these settings to the `"hardware_options"`:

```{code-block} python
:linenos: true

"hardware_options": {
    "mixer_corrections": {
        "dc_offset_i": -0.054,
        "dc_offset_q": -0.034,
        "amp_ratio": 0.9997,
        "phase_error": -4.0,
    }
}
```

Here, the `"dc_offset_i"` and `"dc_offset_q"` parameters add a DC offset to the outputs to correct for feed-through of the local oscillator signal.
The `"amp_ratio"` and `"phase_error"` are used to correct for imperfect rejection of the unwanted sideband.

### Gain and attenuation

For QRM, you can set the gain and attenuation parameters in dB. See the example below.
* The `"input_gain"` parameter for QRM corresponds to the qcodes parameters [in0_gain](https://qblox-qblox-instruments.readthedocs-hosted.com/en/master/api_reference/qcm_qrm.html#pulsar-qrm-pulsar-in0-gain) and [in1_gain](https://qblox-qblox-instruments.readthedocs-hosted.com/en/master/api_reference/qcm_qrm.html#pulsar-qrm-pulsar-in1-gain) respectively.

```{code-block} python
:linenos: true

hardware_compilation_cfg = {
    "backend": "quantify_scheduler.backends.qblox_backend.hardware_compile",
    "hardware_description": {
        "qrm0": {
            "hardware_type": "Qblox",
            "instrument_type": "Pulsar_QRM",
            "ref": "internal",
        }
    },
    "hardware_options": {
        "power_scaling": {
            "q0:res-q0.ro": {"input_gain": (2,3)}
        }
    },
    "connectivity": {
        "qrm0": {
            "complex_output_0": {
                "portclock_configs": [
                    {
                        "port": "q0:res",
                        "clock": "q0.ro",
                    }
                ]
            }
        }
    }
}
```

See [Qblox Instruments: QCM-QRM](https://qblox-qblox-instruments.readthedocs-hosted.com/en/master/api_reference/qcm_qrm.html) documentation for allowed values.
+12 −31

File changed.

Preview size limit exceeded, changes collapsed.

+1 −1
Original line number Diff line number Diff line
@@ -6,7 +6,7 @@ The {class}`~.backends.graph_compilation.HardwareCompilationConfig` datastructur

## 0.13.0: Long waveform support

The {code}`instruction_generated_pulses_enabled` option (see {ref}`Cluster <sec-qblox-instruction-generated-pulses>` and {ref}`Pulsar <sec-qblox-pulsar-instruction-generated>` user guides) is deprecated and will be removed in a future version. Long square pulses, staircase pulses and long ramps can be generated with the newly introduced helper functions {class}`~quantify_scheduler.operations.pulse_factories.long_square_pulse`, {class}`~quantify_scheduler.operations.pulse_factories.staircase_pulse` and {class}`~quantify_scheduler.operations.pulse_factories.long_ramp_pulse`. More complex long waveforms can now also be created from the {class}`~quantify_scheduler.operations.stitched_pulse.StitchedPulseBuilder`, which generates a {class}`~quantify_scheduler.operations.stitched_pulse.StitchedPulse`. For more information, see {ref}`Long waveform support <sec-qblox-cluster-long-waveform-support>`.
The {code}`instruction_generated_pulses_enabled` option (see {ref}`Cluster <sec-qblox-instruction-generated-pulses>` user guide) is deprecated and will be removed in a future version. Long square pulses, staircase pulses and long ramps can be generated with the newly introduced helper functions {class}`~quantify_scheduler.operations.pulse_factories.long_square_pulse`, {class}`~quantify_scheduler.operations.pulse_factories.staircase_pulse` and {class}`~quantify_scheduler.operations.pulse_factories.long_ramp_pulse`. More complex long waveforms can now also be created from the {class}`~quantify_scheduler.operations.stitched_pulse.StitchedPulseBuilder`, which generates a {class}`~quantify_scheduler.operations.stitched_pulse.StitchedPulse`. For more information, see {ref}`Long waveform support <sec-qblox-cluster-long-waveform-support>`.

## 0.12.0: Marker behavior for RF modules; Custom Qblox downconverter