Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/auto wiring #229

Merged
merged 44 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
71cf334
Migrate from qua-libs branch.
deanpoulos Aug 24, 2024
af3ecdf
Merge remote-tracking branch 'origin/main' into feature/auto_wiring
deanpoulos Aug 24, 2024
af34cb4
Fix broken tests after refactoring for channel_specs.
deanpoulos Aug 24, 2024
9c4c41a
Refactor visualization.
deanpoulos Aug 25, 2024
5206547
Get OPX1000 visualization working and draft OPX+/Octave.
deanpoulos Aug 25, 2024
3be3249
Combine labels for different qubits on same line type.
deanpoulos Aug 26, 2024
41d08f1
Combine labels for different qubits on same line type and fix hyphena…
deanpoulos Aug 26, 2024
08ccd32
Add some constants.
deanpoulos Aug 27, 2024
69efe21
Enhance accessibility of core features.
deanpoulos Aug 28, 2024
a97e6f4
Add channel reuse, digital channels, proper channel masking, lf/opx+ …
deanpoulos Aug 28, 2024
afc66d1
Clean up plotting.
deanpoulos Aug 28, 2024
743275e
Clean up plotting.
deanpoulos Aug 28, 2024
9e1f253
Remove bolding from Slot title.
deanpoulos Aug 29, 2024
e147b4a
Fix unwanted import.
deanpoulos Aug 30, 2024
5133615
Minor bug fixes.
deanpoulos Aug 30, 2024
792c613
Fix bug where dataclass equality was based on attribute equality alone.
deanpoulos Aug 31, 2024
95a73d0
Fill top row, then bottom on OPX+ digital outputs.
deanpoulos Sep 4, 2024
64db509
Merge remote-tracking branch 'origin/main' into feature/auto_wiring
deanpoulos Sep 6, 2024
8826358
Add charge lines.
deanpoulos Sep 10, 2024
d6b617c
Add support for untyped WiringLineTypes.
deanpoulos Sep 12, 2024
7828f02
Change convention of qubit-pair naming so that it doesn't conflict wi…
deanpoulos Oct 11, 2024
26e0068
Swap rf_in/rf_out in channel_spec API so it is more intuitive.
deanpoulos Oct 11, 2024
d5afb07
Add quantitative regression tests for exact connectivity allocations.
deanpoulos Oct 11, 2024
d299db7
Write tests for channel reuse.:
deanpoulos Oct 11, 2024
8e9ea70
Fix behaviour for blocking used channels and add type-check to channe…
deanpoulos Oct 11, 2024
281ac6e
Add plotting for digital channels and octave-opx inter-channel connec…
deanpoulos Oct 11, 2024
ff22e76
Start README.g
deanpoulos Oct 11, 2024
bb82ecc
Test extension of table of contents.
deanpoulos Oct 11, 2024
cbf8b62
Test extension of table of contents.
deanpoulos Oct 11, 2024
38b97ad
Test image.
deanpoulos Oct 11, 2024
b1ed490
Test image.
deanpoulos Oct 11, 2024
10a1cdc
Complete README.
deanpoulos Oct 11, 2024
73f08f2
Merge remote-tracking branch 'origin/main' into feature/auto_wiring
deanpoulos Oct 11, 2024
944d1b4
Update changelog.
deanpoulos Oct 11, 2024
62ae268
Update formatting.
deanpoulos Oct 11, 2024
df3575c
Remove stray import.
deanpoulos Oct 11, 2024
e130817
black format
TheoLaudatQM Oct 21, 2024
7190030
remove unused import
TheoLaudatQM Oct 21, 2024
b1140e3
fis linting F541
TheoLaudatQM Oct 21, 2024
0dbd8e5
Make black and flake happy.
deanpoulos Oct 22, 2024
7f9b6ed
Merge branch 'feature/auto_wiring' of github.com:qua-platform/py-qua-…
deanpoulos Oct 22, 2024
15ae400
Fix broken test and linting problems.
deanpoulos Oct 22, 2024
fc916d8
Add note to README.
deanpoulos Oct 22, 2024
8e8945d
Fix formatting error.
deanpoulos Oct 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
## [Unreleased]
### Added
- results - Allow the data saver to create the root folder if it doesn't exist.
- wirer - Automatic tool to allocate channels for arbitrary combinations of QM instruments and superconducting qubits.

### Fixed
- data_handler - Fix figure saving cutting off title text if it is long using `bbox_inches="tight"`.
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ storing them in the usual configuration file. It allows defining waveforms in a
* [Digital filters](qualang_tools/digital_filters/README.md) - Library of functions allowing the derivation of the digital filter taps to correct distortions.

* [Voltage Gates](qualang_tools/voltage_gates/README.md) - The `VoltageGateSequence` class facilitates the creation and management of complex pulse sequences for quantum operations, allowing for dynamic voltage control, ramping, and bias compensation across multiple gate elements.
* [Wirer](qualang_tools/wirer/README.md) - The `wirer` tool allows for automatic allocation of arbitrary channels to an arbitrary combination of instruments.

## Installation

Expand Down
1 change: 1 addition & 0 deletions qualang_tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@
"units",
"external_frameworks",
"callable_from_qua",
"wirer",
]
4 changes: 2 additions & 2 deletions qualang_tools/octave_tools/octave_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,10 +208,10 @@ def octave_calibration_tool(
:param lo_frequencies: single value or list of LO frequencies to calibrate in Hz.
:param intermediate_frequencies: single value or list of Intermediate frequencies to calibrate in Hz.
"""
if not isinstance(lo_frequencies, Union[list, np.ndarray]):
if not isinstance(lo_frequencies, (list, np.ndarray)):
lo_frequencies = [lo_frequencies]

if not isinstance(intermediate_frequencies, Union[list, np.ndarray]):
if not isinstance(intermediate_frequencies, (list, np.ndarray)):
intermediate_frequencies = [intermediate_frequencies]

for lo in lo_frequencies:
Expand Down
Binary file added qualang_tools/wirer/.img/basic_sc_octave.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added qualang_tools/wirer/.img/basic_sc_opx.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added qualang_tools/wirer/.img/empty_octave.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added qualang_tools/wirer/.img/empty_opx.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added qualang_tools/wirer/.img/empty_opx1000.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added qualang_tools/wirer/.img/overview_octave.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added qualang_tools/wirer/.img/overview_opx1000.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added qualang_tools/wirer/.img/overview_opx_plus.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added qualang_tools/wirer/.img/reuse_octave.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added qualang_tools/wirer/.img/reuse_opx.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
263 changes: 263 additions & 0 deletions qualang_tools/wirer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
# Table of Contents
1. [Description](#description)
2. [Overview](#overview)
3. [Features](#features)
4. [Example](#example)
5. [Usage](#usage)
1. [Instruments](#instruments)
1. [Common Setups](#common-setups)
2. [Disjoint Setups](#common-setups)
2. [Connectivity](#connectivity)
1. [Basic Elements (Superconducting Qubits)](#basic-elements-superconducting-qubits)
2. [How to add digital triggers](#how-to-add-digital-triggers)
3. [How to constrain where channels will be allocated](#how-to-constrain-where-channels-will-be-allocated)
3. [Allocation](#allocation)
1. [Basic Allocation](#allocating-channels)
2. [Re-using Channels](#make-certain-channels-re-usable)
4. [Visualization](#visualization)

# Description
The `wirer` module provides a way to auto-assigning channels for a collection of Quantum elements given a specific QM instrument setup.

# Overview
The module decouples two parts of a quantum computing setup:
- `Instruments`: Defines all accesssible Quantum Machines instruments.
- `Connectivity`: Defines all functional quantum elements to be controlled by Quantum Machines instruments.

Once fully defined, the quantum elements can be mapped to channels on the Quantum Machines instruments using the `allocate_wiring` function:
```mermaid
graph TD
A(Instruments) --> D["allocate_wiring()"]
C(Connectivity) --> D["allocate_wiring()"]
subgraph Wirer
D["allocate_wiring()"]
end
```
deanpoulos marked this conversation as resolved.
Show resolved Hide resolved

# Features
The wirer tool supports the following features:
- Assignment of channels to any combination of MW-FEM, LF-FEM, Octave or OPX+.
- Any mapping of N resonator lines to M qubits.
- Any mapping of N FEMs to OPX1000 chassis slots.
- Constrained-scope allocation according to user preferences.
- Natural overflowing during assignment to multiple slots, chassis, modules, octaves, etc.
- Any combination of resonator, drive line, flux line, coupler line for each qubit.
- Total visualization of the final connectivity.

# Example
```python
from qualang_tools.wirer import Instruments, Connectivity, allocate_wiring, visualize, lf_fem_spec

# Define instruments
instruments = Instruments()
instruments.add_lf_fem(controller=1, slots=[1, 2])
instruments.add_mw_fem(controller=1, slots=[3, 7])

qubits = [1, 2, 3, 4, 5, 6]
qubit_pairs = [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]

# Define quantum elements
connectivity = Connectivity()
connectivity.add_resonator_line(qubits=qubits, triggered=True)
connectivity.add_qubit_drive_lines(qubits=qubits, triggered=True)
connectivity.add_qubit_flux_lines(qubits=qubits)
connectivity.add_qubit_pair_flux_lines(qubit_pairs=qubit_pairs, constraints=lf_fem_spec(out_slot=2))

# Allocate channels for each quantum element
allocate_wiring(connectivity, instruments)

# Visualize the result
visualize(connectivity.elements, instruments.available_channels)
```
![opx1000 example](.img/overview_opx1000.png "OPX+ Example")

# Usage
## Instruments
Start with an empty instruments container:
```python
from qualang_tools.wirer import Instruments

instruments = Instruments()
```
Using the "builder" pattern, you are able to define and add instruments to the container, line-by-line.

### Common Setups
Below are examples of some instrument setups:
```python
# Single OPX+
instruments.add_opx_plus(con=1)
```
```python
# Single OPX+ and Octave
instruments.add_opx_plus(controllers=1)
instruments.add_octave(indices=1)
```
<details>
<summary>Image</summary>
<img alt="empty octave" src=".img/empty_octave.png">
<img alt="empty opx+" src=".img/empty_opx.png">
</details>

```python
# Multiple OPXs and Octaves
instruments.add_opx_plus(controllers=[1, 2])
instruments.add_octave(indices=[1, 2])
```
```python
# Single LF-FEM and Octave
instruments.add_lf_fem(controller=1, slots=[1])
instruments.add_octave(indices=1)
```
```python
# Single LF-FEM and MW-FEM
instruments.add_lf_fem(controller=1, slots=[1])
instruments.add_mw_fem(controller=1, slots=[2])
```
<details>
<summary>Image</summary>
<img alt="Empty OPX1000" src=".img/empty_opx1000.png">
</details>

```python
# Multiple LF-FEMs and MW-FEMs
instruments.add_lf_fem(controller=1, slots=[1,2,3,4,5])
instruments.add_mw_fem(controller=1, slots=[6,7,8])
```

### Disjoint Setups
The instruments container allows you to construct a "disjoint" setup, that is one where the indices are not logically ordered or belong only partially to a full cluster:
```python
# Disjoint OPX+ and Octave addressing
instruments.add_opx_plus(controllers=[2, 5])
instruments.add_octave(indices=[1, 3, 8])
```
```python
# Disjoint OPX+ and Octave addressing
instruments.add_opx_plus(controllers=[2, 5])
instruments.add_octave(indices=[1, 3, 8])
```

## Connectivity
The `Connectivity` class is like a blueprint for what quantum elements exist in our system, along with some optional
information about how channels should be allocated for these elements.

To define your own `Connectivity` object, start with an empty container:
```python
from qualang_tools.wirer import Connectivity

connectivity = Connectivity()
```
### Basic Elements (Superconducting Qubits)
Now you can add "wiring specifications" to the container. These indicate what channels you need for a particular type
of quantum functionality.

**Note**: Channels aren't being allocated at this stage- instead, we are adding "templates" for channels which will be allocated later.
```python
# Define arbitrary set of qubits and qubit pairs for convenience
qubits = [1, 2, 5]
qubit_pairs = [(1, 2)]

# Define a single resonator feedline for multiple qubits
connectivity.add_resonator_line(qubits=qubits)

# Define a RF drive-lines for controlling multiple qubits
connectivity.add_qubit_drive_lines(qubits=qubits)

# Define DC lines for controlling flux
connectivity.add_qubit_flux_lines(qubits=qubits)

# Define DC lines for controlling tunable coupler
connectivity.add_qubit_pair_flux_lines(qubit_pairs=qubits)
```
<details>
<summary>Example on OPX+ and Octave</summary>
<img alt="Basic SC-qubit example octave" src=".img/basic_sc_octave.png">
<img alt="Basic SC-qubit example opx" src=".img/basic_sc_opx.png">
</details>

### How to define digital triggers
```python
connectivity.add_resonator_line(qubits=qubits, triggered=True)
```
### How to constrain where channels will be allocated
In order to be more specific about the connectivity, we can add `constraints`. First, you need to select which
type of constraint you will apply, based on the instruments you wish the line to be allocated. The types you can
choose from include:
```python
from qualang_tools.wirer import (
mw_fem_spec,
lf_fem_spec,
lf_fem_iq_spec,
lf_fem_iq_octave_spec,
opx_spec,
opx_iq_spec,
opx_iq_octave_spec,
octave_spec
)
```
Then, you can set almost **any combination** of the available attributes according to what you want constrained. Here are some
examples of how you could constrain something to a particular set of I/Q channels between an OPX+ and an Octave.
```python
# Must allocate to OPX con1
constraint = opx_iq_octave_spec(con=1)
```
```python
# Must allocate to OPX con2
constraint = opx_iq_octave_spec(con=2)
```
```python
# Must allocate to Octave RF3
constraint = opx_iq_octave_spec(rf_out=3)
```
```python
# Must allocate to OPX con1, I/Q channels 7 & 8, Octave RF3
constraint = opx_iq_octave_spec(con=1, out_port_i=7, out_port_q=8, rf_out=3)
```
After defining some `constraint`, you can apply it to a wiring specification:
```python
connectivity.add_qubit_drive_lines(qubits=[1], constraints=constraint)
```

## Allocation
### Allocating channels
Once you have defined suitable `Instruments` and `Connectivity` objects, you can allocate channels using the
`allocate_wiring` function:
```python
from qualang_tools.wirer import allocate_wiring

allocate_wiring(connectivity, instruments)
```
At the time of allocation, all of the channel information is put inside the `connectivity.elements` dictionary.

### Make certain channels re-usable
In order to double-up allocations to channels, you can keep them "free for allocation" even after doing a round of
allocation. Here's an example of how you could allocate multiple qubits to the same channel, either if you don't
have enough channels to measure every element, or if you'd like to multiplex the outputs:
```python
connectivity = Connectivity()

connectivity.add_qubit_drive_lines(qubits=1)
allocate_wiring(connectivity, instruments, block_used_channels=False)

connectivity.add_qubit_drive_lines(qubits=2)
allocate_wiring(connectivity, instruments)

connectivity.add_qubit_drive_lines(qubits=3)
allocate_wiring(connectivity, instruments, block_used_channels=False)

connectivity.add_qubit_drive_lines(qubits=4)
allocate_wiring(connectivity, instruments)
```
<details>
<summary>Image</summary>
<img alt="empty octave" src=".img/reuse_octave.png">
<img alt="empty opx+" src=".img/reuse_opx.png">
</details>

## Visualization
To visualize an allocated `connectivity` object, you can pass it into the visualization function:
```python
visualize(connectivity.elements, available_channels=instruments.available_channels)
```
**Note**: Passing `instruments.available_channels` is optional, but it makes the plotting nicer by plotting white
space on un-allocated channels!
29 changes: 29 additions & 0 deletions qualang_tools/wirer/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from .instruments import Instruments
from .connectivity import Connectivity
from .wirer.wirer import allocate_wiring
from .visualizer.visualizer import visualize
from .wirer.channel_specs_interface import (
mw_fem_spec,
lf_fem_spec,
lf_fem_iq_spec,
lf_fem_iq_octave_spec,
opx_spec,
opx_iq_spec,
opx_iq_octave_spec,
octave_spec,
)

__all__ = [
"Instruments",
"Connectivity",
"allocate_wiring",
"visualize",
"mw_fem_spec",
"lf_fem_spec",
"lf_fem_iq_spec",
"lf_fem_iq_octave_spec",
"opx_spec",
"opx_iq_spec",
"opx_iq_octave_spec",
"octave_spec",
]
3 changes: 3 additions & 0 deletions qualang_tools/wirer/connectivity/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .connectivity_transmon_interface import Connectivity

__all__ = ["Connectivity"]
Loading
Loading