Skip to content

Commit

Permalink
Add preliminary implementation of a 1-port up-/down-converter externa…
Browse files Browse the repository at this point in the history
…l mixer instrument.
  • Loading branch information
deanpoulos committed Dec 19, 2024
1 parent be92404 commit 91a0cff
Show file tree
Hide file tree
Showing 10 changed files with 205 additions and 10 deletions.
38 changes: 34 additions & 4 deletions qualang_tools/wirer/instruments/instrument_channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,30 @@ class InstrumentChannelAnalog:
signal_type = "analog"


InstrumentIdType = Literal["lf-fem", "mw-fem", "opx+", "octave", "external-mixer"]
@dataclass(eq=False)
class InstrumentChannelLfFem:
instrument_id: Literal["lf-fem", "mw-fem", "opx+", "octave"] = "lf-fem"
instrument_id: InstrumentIdType = "lf-fem"


@dataclass(eq=False)
class InstrumentChannelMwFem:
instrument_id: Literal["lf-fem", "mw-fem", "opx+", "octave"] = "mw-fem"
instrument_id: InstrumentIdType = "mw-fem"


@dataclass(eq=False)
class InstrumentChannelOpxPlus:
instrument_id: Literal["lf-fem", "mw-fem", "opx+", "octave"] = "opx+"
instrument_id: InstrumentIdType = "opx+"


@dataclass(eq=False)
class InstrumentChannelOctave:
instrument_id: Literal["lf-fem", "mw-fem", "opx+", "octave"] = "octave"
instrument_id: InstrumentIdType = "octave"


@dataclass(eq=False)
class InstrumentChannelExternalMixer:
instrument_id: InstrumentIdType = "external-mixer"


@dataclass(eq=False)
Expand Down Expand Up @@ -122,6 +128,21 @@ class InstrumentChannelOpxPlusDigitalOutput(
pass


@dataclass(eq=False)
class InstrumentChannelExternalMixerInput(
InstrumentChannelAnalog, InstrumentChannelExternalMixer, InstrumentChannelInput, InstrumentChannel
):
pass


@dataclass(eq=False)
class InstrumentChannelExternalMixerOutput(
InstrumentChannelAnalog, InstrumentChannelExternalMixer, InstrumentChannelOutput, InstrumentChannel
):
pass



@dataclass(eq=False)
class InstrumentChannelOctaveInput(
InstrumentChannelAnalog, InstrumentChannelOctave, InstrumentChannelInput, InstrumentChannel
Expand All @@ -143,6 +164,13 @@ class InstrumentChannelOctaveDigitalInput(
pass


@dataclass(eq=False)
class InstrumentChannelExternalMixerDigitalInput(
InstrumentChannelDigital, InstrumentChannelExternalMixer, InstrumentChannelInput, InstrumentChannel
):
pass


AnyInstrumentChannel = Union[
InstrumentChannelLfFemInput,
InstrumentChannelLfFemOutput,
Expand All @@ -152,4 +180,6 @@ class InstrumentChannelOctaveDigitalInput(
InstrumentChannelOpxPlusOutput,
InstrumentChannelOctaveInput,
InstrumentChannelOctaveOutput,
InstrumentChannelExternalMixerInput,
InstrumentChannelExternalMixerOutput,
]
21 changes: 21 additions & 0 deletions qualang_tools/wirer/instruments/instruments.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
InstrumentChannelOctaveInput,
InstrumentChannelOctaveOutput,
InstrumentChannelOctaveDigitalInput,
InstrumentChannelExternalMixerInput,
InstrumentChannelExternalMixerOutput,
InstrumentChannelExternalMixerDigitalInput,
)
from .instrument_channels import *
from .constants import *
Expand All @@ -20,6 +23,24 @@ def __init__(self):
self.used_channels = InstrumentChannels()
self.available_channels = InstrumentChannels()

def add_external_mixer(self, indices: Union[List[int], int]):
"""
`indices` (List[int] | int): Can be one or more indices for one or more external mixers.
"""
if isinstance(indices, int):
indices = [indices]

for index in indices:
channel = InstrumentChannelExternalMixerInput(con=index, port=1)
self.available_channels.add(channel)

channel = InstrumentChannelExternalMixerOutput(con=index, port=1)
self.available_channels.add(channel)

channel = InstrumentChannelExternalMixerDigitalInput(con=index, port=1)
self.available_channels.add(channel)


def add_octave(self, indices: Union[List[int], int]):
if isinstance(indices, int):
indices = [indices]
Expand Down
34 changes: 30 additions & 4 deletions qualang_tools/wirer/visualizer/instrument_figure_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,19 @@ def get_ax(self, con: int, slot: int, instrument_id: str) -> Axes:
if instrument_id == "OPX1000":
fig, axs = self._make_opx1000_figure()
self.figures[key] = {i + 1: ax for i, ax in enumerate(axs)}
fig.suptitle(f"con{con} - {instrument_id} Wiring Diagram", fontweight="bold", fontsize=14)
fig.suptitle(f"con{con} - {instrument_id} Wiring", fontweight="bold", fontsize=14)
elif instrument_id == "OPX+":
fig = self._make_opx_plus_figure()
self.figures[key] = fig.axes[0]
fig.suptitle(f"con{con} - {instrument_id} Wiring Diagram", fontweight="bold", fontsize=14)
fig.suptitle(f"con{con} - {instrument_id} Wiring", fontweight="bold", fontsize=14)
elif instrument_id == "Octave":
fig = self._make_octave_figure()
self.figures[key] = fig.axes[0]
fig.suptitle(f"oct{con} - {instrument_id} Wiring Diagram", fontweight="bold", fontsize=14)
fig.suptitle(f"oct{con} - {instrument_id} Wiring", fontweight="bold", fontsize=14)
else:
raise NotImplementedError()
fig = self._make_external_mixer_figure()
self.figures[key] = fig.axes[0]
fig.suptitle(f"Mixers {con} Wiring", fontweight="bold", fontsize=14)

return self.figures[key][slot] if slot is not None else self.figures[key]

Expand Down Expand Up @@ -84,3 +86,27 @@ def _make_opx_plus_figure() -> Figure:
@classmethod
def _make_octave_figure(cls) -> Figure:
return cls._make_opx_plus_figure()

@classmethod
def _make_external_mixer_figure(cls) -> Figure:
fig, ax = plt.subplots(
1,
1,
figsize=(
INSTRUMENT_FIGURE_DIMENSIONS["Mixers"]["width"] * 2,
INSTRUMENT_FIGURE_DIMENSIONS["Mixers"]["height"] * 2,
),
)
ax.text(0.25, 0.25, "Up. RF", ha="center", va="center")
ax.text(0.25, 0.65, "Up. Mkr", ha="center")
ax.text(0.75, 0.25, "Down. RF", ha="center", va="center")
ax.set_ylim([0.15, 1.15])
# ax.set_xlim([0.15 / 8 * 3, 1.15 / 8 * 3])
ax.set_facecolor("darkgrey")
ax.set_xticks([])
ax.set_yticks([])
ax.set_xticklabels([])
ax.set_yticklabels([])
ax.set_aspect("equal")

return fig
11 changes: 11 additions & 0 deletions qualang_tools/wirer/visualizer/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
"mw-fem": "OPX1000",
"opx+": "OPX+",
"octave": "Octave",
"external-mixer": "Mixers"
}

# Define the chassis dimensions
INSTRUMENT_FIGURE_DIMENSIONS = {
"OPX1000": {"width": 8, "height": 3},
"OPX+": {"width": 8, "height": 1},
"Octave": {"width": 3, "height": 1},
"Mixers": {"width": 1, "height": 1},
}

OPX_PLUS_ASPECT = INSTRUMENT_FIGURE_DIMENSIONS["OPX+"]["height"] / INSTRUMENT_FIGURE_DIMENSIONS["OPX+"]["width"]
Expand Down Expand Up @@ -57,4 +59,13 @@
"input": [((0.3 + j * 0.06) * 3, 0.18) for j in range(5)],
},
},
"external-mixer": {
"analog": {
"input": [(0.75, 0.45)],
"output": [(0.25, 0.45)],
},
"digital": {
"input": [(0.25, 0.85)],
},
},
}
18 changes: 17 additions & 1 deletion qualang_tools/wirer/visualizer/port_annotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def draw(self, ax: Axes):
if self.signal_type == "digital" and self.instrument_id in ["lf-fem", "mw-fem"]:
port_size = PORT_SIZE / 2.4
bbox = dict(facecolor=fill_color, alpha=0.8, edgecolor="none")
else:
elif self.instrument_id in ["opx+", "octave"]:
port_size = PORT_SIZE
port_label_distance = PORT_SIZE * 1.3
ax.text(
Expand All @@ -46,6 +46,22 @@ def draw(self, ax: Axes):
color=outline_colour,
)
bbox = None
elif self.instrument_id in ["external-mixer"]:
port_size = PORT_SIZE * 2.4
port_label_distance = PORT_SIZE * 3.2
ax.text(
x - port_label_distance,
y,
str(self.port),
ha="center",
va="center",
fontsize=8,
fontweight="bold",
color=outline_colour,
)
bbox = None
else:
raise NotImplementedError(f"No port-annotation drawing for {self.instrument_id}")

ax.add_patch(patches.Circle((x, y), port_size, edgecolor=outline_colour, facecolor=fill_color))
labels = combine_labels_for_same_line_type(self.labels)
Expand Down
18 changes: 18 additions & 0 deletions qualang_tools/wirer/wirer/channel_specs.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@
InstrumentChannelOpxPlusOutput,
InstrumentChannelOctaveInput,
InstrumentChannelOctaveOutput,
InstrumentChannelExternalMixerInput,
InstrumentChannelExternalMixerOutput,
InstrumentChannelOpxPlusDigitalOutput,
InstrumentChannelMwFemDigitalOutput,
InstrumentChannelLfFemDigitalOutput,
InstrumentChannelOctaveDigitalInput,
InstrumentChannelExternalMixerDigitalInput,
)

# A channel template is a partially filled InstrumentChannel object
Expand Down Expand Up @@ -99,6 +102,15 @@ def __init__(self, index: int = None, rf_in: int = None, rf_out: int = None):
]


class ChannelSpecExternalMixer(ChannelSpec):
def __init__(self, index: int = None, rf_in: int = None, rf_out: int = None):
super().__init__()
self.channel_templates = [
InstrumentChannelExternalMixerInput(con=index, port=rf_in),
InstrumentChannelExternalMixerOutput(con=index, port=rf_out),
]


class ChannelSpecLfFemBasebandAndOctave(ChannelSpec):
def __init__(
self,
Expand Down Expand Up @@ -170,6 +182,12 @@ def __init__(self, con: int = None, in_port: int = None):
self.channel_templates = [InstrumentChannelOctaveDigitalInput(con=con, port=in_port)]


class ChannelSpecExternalMixerDigital(ChannelSpec):
def __init__(self, con: int = None, in_port: int = None):
super().__init__()
self.channel_templates = [InstrumentChannelExternalMixerDigitalInput(con=con, port=in_port)]


mw_fem_spec = ChannelSpecMwFemSingle
lf_fem_spec = ChannelSpecLfFemSingle
lf_fem_iq_spec = ChannelSpecLfFemBaseband
Expand Down
9 changes: 9 additions & 0 deletions qualang_tools/wirer/wirer/wirer.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@
ChannelSpecMwFemSingle,
ChannelSpecLfFemBaseband,
ChannelSpecOctave,
ChannelSpecExternalMixer,
ChannelSpecOpxPlusBaseband,
ChannelSpecMwFemDigital,
ChannelSpecLfFemDigital,
ChannelSpecOctaveDigital,
ChannelSpecOpxPlusDigital,
ChannelSpecExternalMixerDigital
)
from .wirer_assign_channels_to_spec import assign_channels_to_spec
from .wirer_exceptions import ConstraintsTooStrictException, NotEnoughChannelsException
Expand Down Expand Up @@ -98,9 +100,16 @@ def allocate_rf_channels(spec: WiringSpec, instruments: Instruments):
channels.
"""
rf_specs = [
# MW-FEM, Single RF output
ChannelSpecMwFemSingle() & ChannelSpecMwFemDigital(),
# LF-FEM I/Q output with Octave for upconversion
ChannelSpecLfFemBaseband() & ChannelSpecLfFemDigital() & ChannelSpecOctave() & ChannelSpecOctaveDigital(),
# LF-FEM I/Q output with External Mixer for upconversion
ChannelSpecLfFemBaseband() & ChannelSpecLfFemDigital() & ChannelSpecExternalMixer() & ChannelSpecExternalMixerDigital(),
# OPX+ I/Q output with Octave for upconversion
ChannelSpecOpxPlusBaseband() & ChannelSpecOpxPlusDigital() & ChannelSpecOctave() & ChannelSpecOctaveDigital(),
# OPX+ I/Q output with External Mixer for upconversion
ChannelSpecOpxPlusBaseband() & ChannelSpecOpxPlusDigital() & ChannelSpecExternalMixer() & ChannelSpecExternalMixerDigital(),
]

allocate_channels(spec, rf_specs, instruments, same_con=True, same_slot=True)
Expand Down
4 changes: 3 additions & 1 deletion qualang_tools/wirer/wirer/wirer_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ def __init__(self, wiring_spec: WiringSpec, constraints: List[ChannelSpec]):
f"{wiring_spec.io_type.value} channels on the "
f"{wiring_spec.line_type.value} line for elements "
f"{','.join([str(e.id) for e in wiring_spec.elements])} with the "
f"following constraints: {constraints}"
f"following constraints: {constraints}. If you are intentionally trying to "
f"allocate multiple lines to the same port, see documentation for the "
f"correct approach."
)
super(ConstraintsTooStrictException, self).__init__(message)

Expand Down
8 changes: 8 additions & 0 deletions tests/wirer/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,11 @@ def instruments_2lf_2mw() -> Instruments:
instruments.add_lf_fem(controller=1, slots=[1, 2])
instruments.add_mw_fem(controller=1, slots=[3, 7])
return instruments


@pytest.fixture()
def instruments_1opx_2external_mixer() -> Instruments:
instruments = Instruments()
instruments.add_opx_plus(controllers=[1])
instruments.add_external_mixer(indices=[1, 2])
return instruments
54 changes: 54 additions & 0 deletions tests/wirer/test_wirer_opxp_and_external_mixers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import pytest

from qualang_tools.wirer import *
from qualang_tools.wirer.connectivity.element import QubitReference
from qualang_tools.wirer.connectivity.wiring_spec import WiringLineType
from qualang_tools.wirer.instruments.instrument_channel import (
InstrumentChannelOpxPlusInput,
InstrumentChannelOpxPlusOutput,
InstrumentChannelExternalMixerInput,
InstrumentChannelExternalMixerOutput,
)

visualize_flag = pytest.visualize_flag


def test_1q_allocation(instruments_1opx_2external_mixer):
qubits = [1]

connectivity = Connectivity()
connectivity.add_resonator_line(qubits=qubits)
connectivity.add_qubit_drive_lines(qubits=qubits)
connectivity.add_qubit_flux_lines(qubits=qubits)

allocate_wiring(connectivity, instruments_1opx_2external_mixer)

if visualize_flag:
visualize(connectivity.elements, instruments_1opx_2external_mixer.available_channels)

for qubit in qubits:
for i, channel in enumerate(connectivity.elements[QubitReference(qubit)].channels[WiringLineType.RESONATOR]):
assert pytest.channels_are_equal(
channel,
[
InstrumentChannelOpxPlusInput(con=1, port=1),
InstrumentChannelOpxPlusInput(con=1, port=2),
InstrumentChannelOpxPlusOutput(con=1, port=1),
InstrumentChannelOpxPlusOutput(con=1, port=2),
InstrumentChannelExternalMixerInput(con=1, port=1),
InstrumentChannelExternalMixerOutput(con=1, port=1),
][i],
)

for i, channel in enumerate(connectivity.elements[QubitReference(qubit)].channels[WiringLineType.DRIVE]):
assert pytest.channels_are_equal(
channel,
[
InstrumentChannelOpxPlusOutput(con=1, port=3),
InstrumentChannelOpxPlusOutput(con=1, port=4),
InstrumentChannelExternalMixerOutput(con=2, port=1),
][i],
)

for i, channel in enumerate(connectivity.elements[QubitReference(qubit)].channels[WiringLineType.FLUX]):
assert pytest.channels_are_equal(channel, [InstrumentChannelOpxPlusOutput(con=1, port=5)][i])

0 comments on commit 91a0cff

Please sign in to comment.