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

Add external mixer support to QuAM. #271

Merged
merged 11 commits into from
Jan 6, 2025
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

qubits = machine.active_qubits

simulate = False

with program() as prog:

qubits[2].xy.update_frequency(-100e6)
Expand Down Expand Up @@ -52,17 +54,19 @@
# qubits[2].xy.wait(4)


# job = qmm.simulate(config, prog, SimulationConfig(duration=1000))
# samples = job.get_simulated_samples()
if simulate:
job = qmm.simulate(config, prog, SimulationConfig(duration=1000))
samples = job.get_simulated_samples()
fig, ax = plt.subplots(nrows=len(samples.keys()), sharex=True)
for i, con in enumerate(samples.keys()):
plt.subplot(len(samples.keys()),1,i+1)
samples[con].plot()
plt.title(con)
plt.tight_layout()
else:
qm = qmm.open_qm(config)
job = qm.execute(prog)

qm = qmm.open_qm(config)
job = qm.execute(prog)
plt.show()


Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# %%
from qualang_tools.wirer.wirer.channel_specs import opx_spec, octave_spec, opx_iq_octave_spec, opx_iq_ext_mixer_spec
from qualang_tools.wirer import Instruments, Connectivity, allocate_wiring, visualize
from quam_libs.quam_builder.machine import build_quam_wiring

# Define static parameters
host_ip = "172.16.33.101" # QOP IP address
port = None # QOP Port
cluster_name = "Cluster_83" # Name of the cluster
# Desired location of wiring.json and state.json
# The folder must not contain other json files.
path = "./quam_state"

# Define the available instrument setup
instruments = Instruments()
instruments.add_opx_plus(controllers=[1])
instruments.add_external_mixer(indices=[1, 2])

# Define which qubit indices are present in the system
qubits = [1, 2, 3]
# Allocate the wiring to the connectivity object based on the available instruments
connectivity = Connectivity()

# Single feed-line for reading the resonators & individual qubit drive lines

# Define any custom/hardcoded channel addresses
# q1_res_ch = octave_spec(index=1, rf_out=1, rf_in=1) # pass to the `constraints` argument
# connectivity.add_resonator_line(qubits=qubits, constraints=None)
# connectivity.add_qubit_flux_lines(qubits=qubits)
# connectivity.add_qubit_drive_lines(qubits=qubits)
# # connectivity.add_qubit_pair_flux_lines(qubit_pairs=[(1,2)]) # Tunable coupler
# allocate_wiring(connectivity, instruments)

# Single feed-line for reading the resonators & driving the qubits + flux on specific fem slot
for qubit in qubits:
connectivity.add_qubit_drive_lines(qubits=qubit, constraints=opx_iq_ext_mixer_spec(out_port_i=3, out_port_q=4))
allocate_wiring(connectivity, instruments, block_used_channels=qubit==3)

connectivity.add_resonator_line(qubits=qubits, constraints=None)
connectivity.add_qubit_flux_lines(qubits=qubits)
allocate_wiring(connectivity, instruments)
# connectivity.add_qubit_drive_lines(qubits=qubits)
# connectivity.add_qubit_pair_flux_lines(qubit_pairs=[(1,2)]) # Tunable coupler

# Build the wiring and network into a QuAM machine and save it as "wiring.json"
build_quam_wiring(connectivity, host_ip, cluster_name, path)

# View wiring schematic
visualize(connectivity.elements, available_channels=instruments.available_channels)
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# %%
import json
from qualang_tools.units import unit
from quam_libs.components import QuAM
from quam_libs.quam_builder.machine import save_machine
import numpy as np


path = r"C:\Users\KevinAVillegasRosale\OneDrive - QM Machines LTD\Documents\GitKraken\qua-libs\quam_state"

machine = QuAM.load(path)

u = unit(coerce_to_integer=True)

# Change active qubits
# machine.active_qubit_names = ["q0"]

for i in range(len(machine.qubits.items())):
machine.qubits[f"q{i+1}"].grid_location = f"{i},0"

# Update frequencies
rr_freq = np.array([4.395, 4.412, 4.521]) * u.GHz
rr_LO = 4.75 * u.GHz
rr_if = rr_freq - rr_LO

xy_freq = np.array([6.012, 6.20, 6.4]) * u.GHz
# xy_LO = np.array([6.0, 6.5, 6.5]) * u.GHz
xy_LO = 6.3 * u.GHz
xy_if = xy_freq - xy_LO

# NOTE: be aware of coupled ports for bands
for i, q in enumerate(machine.qubits):
## Update qubit rr freq and power
machine.qubits[q].resonator.intermediate_frequency = rr_if[i]

## Update qubit xy freq and power
machine.qubits[q].xy.intermediate_frequency = xy_if[i]

# Update flux channels
machine.qubits[q].z.opx_output.output_mode = "amplified"
machine.qubits[q].z.opx_output.upsampling_mode = "pulse"

## Update pulses
# readout
machine.qubits[q].resonator.operations["readout"].length = 2.5 * u.us
machine.qubits[q].resonator.operations["readout"].amplitude = 1e-3
# Qubit saturation
machine.qubits[q].xy.operations["saturation"].length = 20 * u.us
machine.qubits[q].xy.operations["saturation"].amplitude = 0.25
# Single qubit gates - DragCosine
machine.qubits[q].xy.operations["x180_DragCosine"].length = 48
machine.qubits[q].xy.operations["x180_DragCosine"].amplitude = 0.2
machine.qubits[q].xy.operations["x90_DragCosine"].amplitude = (
machine.qubits[q].xy.operations["x180_DragCosine"].amplitude / 2
)
# Single qubit gates - Square
machine.qubits[q].xy.operations["x180_Square"].length = 40
machine.qubits[q].xy.operations["x180_Square"].amplitude = 0.1
machine.qubits[q].xy.operations["x90_Square"].amplitude = (
machine.qubits[q].xy.operations["x180_Square"].amplitude / 2
)

# %%
# save into state.json
save_machine(machine, path)

# %%
# View the corresponding "raw-QUA" config
with open("qua_config.json", "w+") as f:
json.dump(machine.generate_config(), f, indent=4)

# %%
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class FluxLine(SingleChannel):
joint_offset: float = 0.0
min_offset: float = 0.0
arbitrary_offset: float = 0.0
settle_time: float = 0.0
settle_time: float = 1000.00

def settle(self):
"""Wait for the flux bias to settle"""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from quam.components import Mixer

from quam import quam_dataclass


@quam_dataclass
class StandaloneMixer(Mixer):
@property
def name(self):
for name, frequency_converter in self.parent.parent.items():
if self.parent == frequency_converter:
return name
raise KeyError()
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import os
import warnings
from pathlib import Path

from quam.components import FrequencyConverter
from quam.core import QuamRoot, quam_dataclass
from quam.components.octave import Octave
from quam.components.ports import (
Expand Down Expand Up @@ -30,6 +32,7 @@ class QuAM(QuamRoot):
"""Example QuAM root component."""

octaves: Dict[str, Octave] = field(default_factory=dict)
mixers: Dict[str, FrequencyConverter] = field(default_factory=dict)

qubits: Dict[str, Transmon] = field(default_factory=dict)
qubit_pairs: Dict[str, TransmonPair] = field(default_factory=dict)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
from pathlib import Path
from typing import Union, Dict

from quam.components import Octave
from quam.components import Octave, LocalOscillator

from qualang_tools.wirer import Connectivity
from quam.components import FrequencyConverter

from quam_libs.components.mixer import StandaloneMixer
from quam_libs.quam_builder.pulses import add_default_transmon_pulses, add_default_transmon_pair_pulses
from quam_libs.quam_builder.transmons.add_transmon_drive_component import add_transmon_drive_component
from quam_libs.quam_builder.transmons.add_transmon_flux_component import add_transmon_flux_component
Expand All @@ -17,6 +20,7 @@

def build_quam(machine: QuAM, quam_state_path: Union[Path, str], octaves_settings: Dict = {}) -> QuAM:
add_octaves(machine, octaves_settings, quam_state_path)
add_external_mixers(machine)
add_ports(machine)
add_transmons(machine)
add_pulses(machine)
Expand Down Expand Up @@ -142,6 +146,28 @@ def add_octaves(machine: QuAM, octaves_settings: Dict, quam_state_path: Union[Pa
return machine


def add_external_mixers(machine: QuAM):
for wiring_by_element in machine.wiring.values():
for qubit, wiring_by_line_type in wiring_by_element.items():
for line_type, references in wiring_by_line_type.items():
for reference in references:
if "mixers" in references.get_unreferenced_value(reference):
mixer_name = references.get_unreferenced_value(reference).split('/')[2]
transmon_channel = {
WiringLineType.DRIVE.value: "xy",
WiringLineType.RESONATOR.value: "resonator"
}
frequency_converter = FrequencyConverter(
deanpoulos marked this conversation as resolved.
Show resolved Hide resolved
local_oscillator=LocalOscillator(),
mixer=StandaloneMixer(
intermediate_frequency=f"#/qubits/{qubit}/{transmon_channel[line_type]}/intermediate_frequency",
)
)
machine.mixers[mixer_name] = frequency_converter

return machine


def save_machine(machine: QuAM, quam_state_path: Union[Path, str]):
machine.save(
path=quam_state_path,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,30 @@
from typing import List

from .paths import OCTAVES_BASE_JSON_PATH, PORTS_BASE_JSON_PATH
from qualang_tools.wirer.connectivity.element import QubitReference
from qualang_tools.wirer.connectivity.wiring_spec import WiringLineType
from .paths import OCTAVES_BASE_JSON_PATH, PORTS_BASE_JSON_PATH, MIXERS_BASE_JSON_PATH
from qualang_tools.wirer.instruments.instrument_channel import AnyInstrumentChannel


def create_external_mixer_reference(channel: AnyInstrumentChannel, element_id: QubitReference,
line_type: WiringLineType) -> (str, str):
"""
Generates a key/JSON reference pair from which a QuAM port can be created
for a single Octave channel.
"""
if channel.io_type == "output":
key = "frequency_converter_up"
elif channel.io_type == "input":
key = "frequency_converter_down"
else:
raise ValueError(f"Unknown IO type {channel.io_type}")

reference = MIXERS_BASE_JSON_PATH
reference += f"/mixer{channel.con}_{element_id}.{line_type.value}"

return key, reference


def create_octave_port(channel: AnyInstrumentChannel) -> (str, str):
"""
Generates a key/JSON reference pair from which a QuAM port can be created
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
from functools import reduce

from qualang_tools.wirer import Connectivity
from qualang_tools.wirer.connectivity.element import QubitPairReference
from qualang_tools.wirer.connectivity.element import QubitPairReference, QubitReference
from qualang_tools.wirer.connectivity.wiring_spec import WiringLineType
from qualang_tools.wirer.instruments.instrument_channel import AnyInstrumentChannel

from .create_analog_ports import create_octave_port, create_mw_fem_port, create_lf_opx_plus_port
from .create_analog_ports import create_octave_port, create_mw_fem_port, create_lf_opx_plus_port, \
create_external_mixer_reference
from .create_digital_ports import create_digital_output_port
from .paths import *

Expand All @@ -20,7 +21,7 @@ def create_wiring(connectivity: Connectivity) -> dict:
for element_id, element in connectivity.elements.items():
for line_type, channels in element.channels.items():
if line_type in [WiringLineType.RESONATOR, WiringLineType.DRIVE, WiringLineType.FLUX]:
for k, v in qubit_wiring(channels).items():
for k, v in qubit_wiring(channels, element_id, line_type).items():
set_nested_value_with_path(wiring, f"qubits/{element_id}/{line_type.value}/{k}", v)

elif line_type == WiringLineType.COUPLER:
Expand All @@ -33,14 +34,17 @@ def create_wiring(connectivity: Connectivity) -> dict:
return wiring


def qubit_wiring(channels: List[AnyInstrumentChannel]) -> dict:
def qubit_wiring(channels: List[AnyInstrumentChannel], element_id: QubitReference, line_type: WiringLineType) -> dict:
"""
Generates a dictionary containing QuAM-compatible JSON references for a
list of channels from a single qubit and the same line type.
"""
qubit_line_wiring = {}
for channel in channels:
if not (channel.signal_type == "digital" and channel.io_type == "input"):
if channel.instrument_id == "external-mixer":
key, reference = create_external_mixer_reference(channel, element_id, line_type)
qubit_line_wiring[key] = reference
elif not (channel.signal_type == "digital" and channel.io_type == "input"):
key, reference = get_channel_port(channel, channels)
qubit_line_wiring[key] = reference

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
MIXERS_BASE_JSON_PATH = "#/mixers"
OCTAVES_BASE_JSON_PATH = "#/octaves"
QUBITS_BASE_JSON_PATH = "#/qubits"
PORTS_BASE_JSON_PATH = "#/ports"
Loading