From 4d36abd664eb40d51e982d7186bb5dc04b8d69ce Mon Sep 17 00:00:00 2001 From: HiroQM Date: Tue, 24 Jun 2025 13:08:05 +0900 Subject: [PATCH 01/17] added an argument to specify number of IQ pairs --- .../architecture/superconducting/qpu/base_quam.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/quam_builder/architecture/superconducting/qpu/base_quam.py b/quam_builder/architecture/superconducting/qpu/base_quam.py index 7fef1254..a2ed6bab 100644 --- a/quam_builder/architecture/superconducting/qpu/base_quam.py +++ b/quam_builder/architecture/superconducting/qpu/base_quam.py @@ -137,6 +137,7 @@ def thermalization_time(self) -> int: def declare_qua_variables( self, + num_IQ_pairs: Optional[int] = None, ) -> tuple[ list[QuaVariable], list[StreamType], @@ -150,12 +151,15 @@ def declare_qua_variables( Returns: tuple: A tuple containing lists of QUA variables and streams. """ + if num_IQ_pairs is None: + num_IQ_pairs = len(self.qubits) + n = declare(int) n_st = declare_stream() - I = [declare(fixed) for _ in range(len(self.qubits))] - Q = [declare(fixed) for _ in range(len(self.qubits))] - I_st = [declare_stream() for _ in range(len(self.qubits))] - Q_st = [declare_stream() for _ in range(len(self.qubits))] + I = [declare(fixed) for _ in range(num_IQ_pairs)] + Q = [declare(fixed) for _ in range(num_IQ_pairs)] + I_st = [declare_stream() for _ in range(num_IQ_pairs)] + Q_st = [declare_stream() for _ in range(num_IQ_pairs)] return I, I_st, Q, Q_st, n, n_st def initialize_qpu(self, **kwargs): From 42adaa5fb86fd440d769c201be99e3903694e6f6 Mon Sep 17 00:00:00 2001 From: HiroQM Date: Tue, 24 Jun 2025 13:14:32 +0900 Subject: [PATCH 02/17] added docstring --- quam_builder/architecture/superconducting/qpu/base_quam.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/quam_builder/architecture/superconducting/qpu/base_quam.py b/quam_builder/architecture/superconducting/qpu/base_quam.py index a2ed6bab..df654cdc 100644 --- a/quam_builder/architecture/superconducting/qpu/base_quam.py +++ b/quam_builder/architecture/superconducting/qpu/base_quam.py @@ -148,6 +148,10 @@ def declare_qua_variables( ]: """Macro to declare the necessary QUA variables for all qubits. + Args: + num_IQ_pairs (Optional[int]): Number of IQ pairs (I and Q variables) to declare. + If None, it defaults to the number of qubits in `self.qubits`. + Returns: tuple: A tuple containing lists of QUA variables and streams. """ From 698a3ec65d4933b5effd96ceb56d0d896ce8fb1f Mon Sep 17 00:00:00 2001 From: HiroQM Date: Mon, 14 Jul 2025 11:19:32 +0900 Subject: [PATCH 03/17] updated CHANGELOG --- CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29a6592c..84823563 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ## [Unreleased] +## [0.1.1] - 2025-07-14 +### Added +- Optional `num_IQ_pairs` argument to `declare_qua_variables`. + ## [0.1.0] - 2025-05-07 ### Added - Architecture components for Flux Tunable Transmons. @@ -12,5 +16,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Builder functions for the general QUAM wiring. - Builder functions for Transmons. -[Unreleased]: https://github.com/qua-platform/quam-builder/compare/v0.1.0...HEAD +[Unreleased]: https://github.com/qua-platform/quam-builder/compare/v0.1.1...HEAD +[0.1.1]: https://github.com/qua-platform/quam-builder/releases/tag/v0.1.1 [0.1.0]: https://github.com/qua-platform/quam-builder/releases/tag/v0.1.0 From fcd4e0e75e255f67df350b2f0709db0cef32d9d6 Mon Sep 17 00:00:00 2001 From: HiroQM Date: Tue, 15 Jul 2025 00:13:48 +0900 Subject: [PATCH 04/17] added quam macro for CR --- .../architecture/superconducting/__init__.py | 3 + .../superconducting/custom_gates/__init__.py | 5 + .../custom_gates/cross_resonance.py | 155 ++++++++++++++++++ 3 files changed, 163 insertions(+) create mode 100644 quam_builder/architecture/superconducting/custom_gates/__init__.py create mode 100644 quam_builder/architecture/superconducting/custom_gates/cross_resonance.py diff --git a/quam_builder/architecture/superconducting/__init__.py b/quam_builder/architecture/superconducting/__init__.py index a48671d3..3a4b8934 100644 --- a/quam_builder/architecture/superconducting/__init__.py +++ b/quam_builder/architecture/superconducting/__init__.py @@ -10,9 +10,12 @@ zz_drive, xy_drive, ) +from .custom_gates.cross_resonance import CRGate + __all__ = [ *qpu.__all__, *qubit.__all__, *qubit_pair.__all__, + *custom_gates.__all__, ] diff --git a/quam_builder/architecture/superconducting/custom_gates/__init__.py b/quam_builder/architecture/superconducting/custom_gates/__init__.py new file mode 100644 index 00000000..a9517951 --- /dev/null +++ b/quam_builder/architecture/superconducting/custom_gates/__init__.py @@ -0,0 +1,5 @@ +from quam_builder.architecture.superconducting.custom_gates import cross_resonance + +__all__ = [ + *cross_resonance.__all__, +] \ No newline at end of file diff --git a/quam_builder/architecture/superconducting/custom_gates/cross_resonance.py b/quam_builder/architecture/superconducting/custom_gates/cross_resonance.py new file mode 100644 index 00000000..5600c2dd --- /dev/null +++ b/quam_builder/architecture/superconducting/custom_gates/cross_resonance.py @@ -0,0 +1,155 @@ +from typing import Optional, Literal +from qm.qua import * +from quam.core import quam_dataclass +from quam.components.pulses import Pulse +from quam.components.macro import QubitPairMacro +from qm.qua._dsl import QuaExpression, QuaVariable + +__all__ = ["CRGate"] + + +qua_T = QuaVariable | QuaExpression + + +@quam_dataclass +class CRGate(QubitPairMacro): + qc_correction_phase: float = 0.0 + + def apply( + self, + cr_type: Literal[ + "direct", "direct+cancel", "direct+echo", "direct+cancel+echo" + ] = "direct", + wf_type: Literal["square", "cosine", "gauss", "flattop"] = "square", + cr_drive_amp_scaling: Optional[float | qua_T] = None, + cr_drive_phase: Optional[float | qua_T] = None, + cr_cancel_amp_scaling: Optional[float | qua_T] = None, + cr_cancel_phase: Optional[float | qua_T] = None, + cr_duration_clock_cycles: Optional[float | qua_T] = None, + qc_correction_phase: Optional[float | qua_T] = None, + ) -> None: + qc = self.qubit_pair.qubit_control + qt = self.qubit_pair.qubit_target + cr = self.qubit_pair.cross_resonance + cr_elems = [qc.xy.name, qt.xy.name, cr.name] + + def _play_cr_pulse( + elem, + wf_type: str = wf_type, + amp_scale: Optional[float | qua_T] = None, + duration: Optional[float | qua_T] = None, + sgn: int = 1, + ): + if amp_scale is None and duration is None: + elem.play(wf_type) + elif amp_scale is None: + elem.play(wf_type, duration=duration) + elif duration is None: + elem.play(wf_type, amplitude_scale=sgn * amp_scale) + else: + elem.play(wf_type, amplitude_scale=sgn * amp_scale, duration=duration) + + def cr_drive_shift_phase(): + if cr_drive_phase is not None: + cr.frame_rotation_2pi(cr_drive_phase) + + def cr_cancel_shift_phase(): + if cr_cancel_phase is not None: + qt.xy.frame_rotation_2pi(cr_cancel_phase) + + def qc_shift_correction_phase(): + if qc_correction_phase is not None: + qc.xy.frame_rotation_2pi(qc_correction_phase) + + def cr_drive_play( + sgn: Literal["direct", "echo"] = "direct", + wf_type=wf_type, + ): + _play_cr_pulse( + elem=cr, + wf_type=wf_type, + amp_scale=cr_drive_amp_scaling, + duration=cr_duration_clock_cycles, + sgn=1 if sgn == "direct" else -1, + ) + + def cr_cancel_play( + sgn: Literal["direct", "echo"] = "direct", + wf_type=wf_type, + ): + _play_cr_pulse( + elem=qt.xy, + wf_type=f"cr_{wf_type}_{self.qubit_pair.name}", + amp_scale=cr_cancel_amp_scaling, + duration=cr_duration_clock_cycles, + sgn=1 if sgn == "direct" else -1, + ) + + if cr_type == "direct": + cr_drive_shift_phase() + align(*cr_elems) + + cr_drive_play(sgn="direct") + align(*cr_elems) + + reset_frame(cr.name) + qc_shift_correction_phase() + align(*cr_elems) + + elif cr_type == "direct+echo": + cr_drive_shift_phase() + align(*cr_elems) + + cr_drive_play(sgn="direct") + align(*cr_elems) + + qc.xy.play("x180") + align(*cr_elems) + + cr_drive_play(sgn="echo") + align(*cr_elems) + + qc.xy.play("x180") + align(*cr_elems) + + reset_frame(cr.name) + qc_shift_correction_phase() + align(*cr_elems) + + elif cr_type == "direct+cancel": + cr_drive_shift_phase() + cr_cancel_shift_phase() + align(*cr_elems) + + cr_drive_play(sgn="direct") + cr_cancel_play(sgn="direct") + align(*cr_elems) + + reset_frame(cr.name) + reset_frame(qt.xy.name) + qc_shift_correction_phase() + align(*cr_elems) + + elif cr_type == "direct+cancel+echo": + cr_drive_shift_phase() + cr_cancel_shift_phase() + align(*cr_elems) + + cr_drive_play(sgn="direct") + cr_cancel_play(sgn="direct") + align(*cr_elems) + + qc.xy.play("x180") + align(*cr_elems) + + cr_drive_play(sgn="echo") + cr_cancel_play(sgn="echo") + align(*cr_elems) + + qc.xy.play("x180") + align(*cr_elems) + + reset_frame(cr.name) + reset_frame(qt.xy.name) + qc_shift_correction_phase() + align(*cr_elems) From 00edf15545ad7f5449da2947a955526be31a59a2 Mon Sep 17 00:00:00 2001 From: paulQM Date: Fri, 18 Jul 2025 12:27:38 +0200 Subject: [PATCH 05/17] add cz macro for tunable transmons --- .../superconducting/custom_gates/__init__.py | 3 +- .../superconducting/custom_gates/cz.py | 87 +++++++++++++++++++ 2 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 quam_builder/architecture/superconducting/custom_gates/cz.py diff --git a/quam_builder/architecture/superconducting/custom_gates/__init__.py b/quam_builder/architecture/superconducting/custom_gates/__init__.py index a9517951..87708dff 100644 --- a/quam_builder/architecture/superconducting/custom_gates/__init__.py +++ b/quam_builder/architecture/superconducting/custom_gates/__init__.py @@ -1,5 +1,6 @@ -from quam_builder.architecture.superconducting.custom_gates import cross_resonance +from quam_builder.architecture.superconducting.custom_gates import cross_resonance, cz __all__ = [ *cross_resonance.__all__, + *cz.__all__, ] \ No newline at end of file diff --git a/quam_builder/architecture/superconducting/custom_gates/cz.py b/quam_builder/architecture/superconducting/custom_gates/cz.py new file mode 100644 index 00000000..904b8bd1 --- /dev/null +++ b/quam_builder/architecture/superconducting/custom_gates/cz.py @@ -0,0 +1,87 @@ +from typing import Literal, Union + +import numpy as np +from qm.qua import Cast, assign, broadcast, declare, fixed, while_ + +from quam.components.macro import QubitMacro, QubitPairMacro +from quam.components.pulses import Pulse, ReadoutPulse +from quam.core import quam_dataclass +from quam.utils.qua_types import QuaVariableBool, QuaVariableFloat, QuaVariableInt + +__all__ = ["CZGate"] + + +def get_pulse_name(pulse: Pulse) -> str: + """ + Get the name of the pulse. If the pulse has an id, return it. + """ + if pulse.id is not None: + return pulse.id + elif pulse.parent is not None: + return pulse.parent.get_attr_name(pulse) + else: + raise AttributeError(f"Cannot infer id of {pulse} because it is not attached to a parent") + + +@quam_dataclass +class CZGate(QubitPairMacro): + flux_pulse_control: Union[Pulse, str] + coupler_flux_pulse: Pulse = None + + pre_wait: int = 4 + + phase_shift_control: float = 0.0 + phase_shift_target: float = 0.0 + + @property + def coupler(self): # -> "TunableCoupler": + return self.qubit_pair.coupler + + @property + def flux_pulse_control_label(self) -> str: + pulse = ( + self.qubit_control.get_pulse(self.flux_pulse_control) + if isinstance(self.flux_pulse_control, str) + else self.flux_pulse_control + ) + return get_pulse_name(pulse) + + @property + def coupler_flux_pulse_label(self) -> str: + pulse = ( + self.coupler.get_pulse(self.coupler_flux_pulse) + if isinstance(self.coupler_flux_pulse, str) + else self.coupler_flux_pulse + ) + return get_pulse_name(pulse) + + def apply( + self, + *, + amplitude_scale=None, + phase_shift_control=None, + phase_shift_target=None, + **kwargs, + ) -> None: + self.qubit_pair.qubit_control.z.play( + self.flux_pulse_control_label, + validate=False, + amplitude_scale=amplitude_scale, + ) + + if self.coupler_flux_pulse is not None: + self.qubit_pair.coupler.play(self.coupler_flux_pulse_label, validate=False) + + self.qubit_pair.align() + if phase_shift_control is not None: + self.qubit_pair.qubit_control.xy.frame_rotation_2pi(phase_shift_control) + elif np.abs(self.phase_shift_control) > 1e-6: + self.qubit_pair.qubit_control.xy.frame_rotation_2pi(self.phase_shift_control) + if phase_shift_target is not None: + self.qubit_pair.qubit_target.xy.frame_rotation_2pi(phase_shift_target) + elif np.abs(self.phase_shift_target) > 1e-6: + self.qubit_pair.qubit_target.xy.frame_rotation_2pi(self.phase_shift_target) + + self.qubit_pair.qubit_control.xy.play("x180", amplitude_scale=0.0, duration=4) + self.qubit_pair.qubit_target.xy.play("x180", amplitude_scale=0.0, duration=4) + self.qubit_pair.align() From 3faad30e585a2ee44c2763e98b4918c4778334a0 Mon Sep 17 00:00:00 2001 From: HiroQM Date: Wed, 23 Jul 2025 14:36:42 +0900 Subject: [PATCH 06/17] renamed the module --- .../architecture/superconducting/custom_gates/__init__.py | 4 ++-- .../{cross_resonance.py => cross_resonance_gate.py} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename quam_builder/architecture/superconducting/custom_gates/{cross_resonance.py => cross_resonance_gate.py} (100%) diff --git a/quam_builder/architecture/superconducting/custom_gates/__init__.py b/quam_builder/architecture/superconducting/custom_gates/__init__.py index a9517951..4472203f 100644 --- a/quam_builder/architecture/superconducting/custom_gates/__init__.py +++ b/quam_builder/architecture/superconducting/custom_gates/__init__.py @@ -1,5 +1,5 @@ -from quam_builder.architecture.superconducting.custom_gates import cross_resonance +from quam_builder.architecture.superconducting.custom_gates import cross_resonance_gate __all__ = [ - *cross_resonance.__all__, + *cross_resonance_gate.__all__, ] \ No newline at end of file diff --git a/quam_builder/architecture/superconducting/custom_gates/cross_resonance.py b/quam_builder/architecture/superconducting/custom_gates/cross_resonance_gate.py similarity index 100% rename from quam_builder/architecture/superconducting/custom_gates/cross_resonance.py rename to quam_builder/architecture/superconducting/custom_gates/cross_resonance_gate.py From 7508dcaf66d0d4d2d204cf7f5b1afbbe01409bac Mon Sep 17 00:00:00 2001 From: HiroQM Date: Wed, 23 Jul 2025 14:45:50 +0900 Subject: [PATCH 07/17] resolved conflict and merged --- .../architecture/superconducting/custom_gates/__init__.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/quam_builder/architecture/superconducting/custom_gates/__init__.py b/quam_builder/architecture/superconducting/custom_gates/__init__.py index 6c007735..87708dff 100644 --- a/quam_builder/architecture/superconducting/custom_gates/__init__.py +++ b/quam_builder/architecture/superconducting/custom_gates/__init__.py @@ -1,13 +1,6 @@ -<<<<<<< HEAD -from quam_builder.architecture.superconducting.custom_gates import cross_resonance_gate - -__all__ = [ - *cross_resonance_gate.__all__, -======= from quam_builder.architecture.superconducting.custom_gates import cross_resonance, cz __all__ = [ *cross_resonance.__all__, *cz.__all__, ->>>>>>> 00edf15545ad7f5449da2947a955526be31a59a2 ] \ No newline at end of file From 3192d951684c802028cd332becd284fcfe42857c Mon Sep 17 00:00:00 2001 From: HiroQM Date: Fri, 25 Jul 2025 11:44:37 +0900 Subject: [PATCH 08/17] added flux-tunable cross-drive classes --- .../superconducting/qpu/__init__.py | 3 +- .../superconducting/qpu/flux_tunable_quam.py | 21 +++++- .../superconducting/qubit_pair/__init__.py | 3 +- .../qubit_pair/flux_tunable_transmon_pair.py | 71 ++++++++++++++++++- .../add_transmon_pair_component.py | 13 ++-- .../builder/superconducting/pulses.py | 5 +- 6 files changed, 103 insertions(+), 13 deletions(-) diff --git a/quam_builder/architecture/superconducting/qpu/__init__.py b/quam_builder/architecture/superconducting/qpu/__init__.py index acc76940..12aadb96 100644 --- a/quam_builder/architecture/superconducting/qpu/__init__.py +++ b/quam_builder/architecture/superconducting/qpu/__init__.py @@ -4,6 +4,7 @@ ) from quam_builder.architecture.superconducting.qpu.flux_tunable_quam import ( FluxTunableQuam, + FluxTunableCrossDriveQuam, ) from typing import Union @@ -13,4 +14,4 @@ *flux_tunable_quam.__all__, ] -AnyQuam = Union[BaseQuam, FixedFrequencyQuam, FluxTunableQuam] +AnyQuam = Union[BaseQuam, FixedFrequencyQuam, FluxTunableQuam, FluxTunableCrossDriveQuam] diff --git a/quam_builder/architecture/superconducting/qpu/flux_tunable_quam.py b/quam_builder/architecture/superconducting/qpu/flux_tunable_quam.py index 38c2e270..2ef53d9d 100644 --- a/quam_builder/architecture/superconducting/qpu/flux_tunable_quam.py +++ b/quam_builder/architecture/superconducting/qpu/flux_tunable_quam.py @@ -5,11 +5,11 @@ from quam.core import quam_dataclass from quam_builder.architecture.superconducting.qubit import FluxTunableTransmon -from quam_builder.architecture.superconducting.qubit_pair import FluxTunableTransmonPair +from quam_builder.architecture.superconducting.qubit_pair import FluxTunableTransmonPair, FluxTunableCrossDriveTransmonPair from quam_builder.architecture.superconducting.qpu.base_quam import BaseQuam -__all__ = ["FluxTunableQuam", "FluxTunableTransmon", "FluxTunableTransmonPair"] +__all__ = ["FluxTunableQuam", "FluxTunableCrossDriveQuam", "FluxTunableTransmon", "FluxTunableTransmonPair"] @quam_dataclass @@ -133,3 +133,20 @@ def initialize_qpu(self, **kwargs): flux_point = kwargs.get("flux_point", "joint") target = kwargs.get("target", None) self.set_all_fluxes(flux_point, target) + + +class FluxTunableCrossDriveQuam(FluxTunableQuam): + """Example of a QUAM composed of flux tunable cr transmons. + + Attributes: + qubit_pair_type (ClassVar[Type[FixedFrequencyTransmonPair]]): The type of the qubit pairs in the QUAM for type hinting. + qubit_pairs (Dict[str, FixedFrequencyTransmonPair]): A dictionary of qubit pairs composing the QUAM. + + """ + qubit_pair_type: ClassVar[Type[FluxTunableCrossDriveTransmonPair]] = FluxTunableCrossDriveTransmonPair + qubit_pairs: Dict[str, FluxTunableCrossDriveTransmonPair] = field(default_factory=dict) + + @classmethod + def load(cls, *args, **kwargs) -> "FluxTunableCrossDriveQuam": + return super().load(*args, **kwargs) + \ No newline at end of file diff --git a/quam_builder/architecture/superconducting/qubit_pair/__init__.py b/quam_builder/architecture/superconducting/qubit_pair/__init__.py index 35f8158e..b70adcfe 100644 --- a/quam_builder/architecture/superconducting/qubit_pair/__init__.py +++ b/quam_builder/architecture/superconducting/qubit_pair/__init__.py @@ -1,6 +1,7 @@ from typing import Union from quam_builder.architecture.superconducting.qubit_pair.flux_tunable_transmon_pair import ( FluxTunableTransmonPair, + FluxTunableCrossDriveTransmonPair, ) from quam_builder.architecture.superconducting.qubit_pair.fixed_frequency_transmon_pair import ( FixedFrequencyTransmonPair, @@ -11,4 +12,4 @@ *flux_tunable_transmon_pair.__all__, ] -AnyTransmonPair = Union[FixedFrequencyTransmonPair, FluxTunableTransmonPair] +AnyTransmonPair = Union[FixedFrequencyTransmonPair, FluxTunableTransmonPair, FluxTunableCrossDriveTransmonPair] diff --git a/quam_builder/architecture/superconducting/qubit_pair/flux_tunable_transmon_pair.py b/quam_builder/architecture/superconducting/qubit_pair/flux_tunable_transmon_pair.py index 329b6b79..06683174 100644 --- a/quam_builder/architecture/superconducting/qubit_pair/flux_tunable_transmon_pair.py +++ b/quam_builder/architecture/superconducting/qubit_pair/flux_tunable_transmon_pair.py @@ -4,14 +4,24 @@ from quam.core import quam_dataclass from quam.components.quantum_components import QubitPair +from quam.components.channels import IQChannel, MWChannel from quam_builder.architecture.superconducting.components.tunable_coupler import ( TunableCoupler, ) from quam_builder.architecture.superconducting.qubit.flux_tunable_transmon import ( FluxTunableTransmon, ) +from quam_builder.architecture.superconducting.components.cross_resonance import ( + CrossResonanceIQ, + CrossResonanceMW, +) +from quam_builder.architecture.superconducting.components.zz_drive import ( + ZZDriveIQ, + ZZDriveMW, +) + -__all__ = ["FluxTunableTransmonPair"] +__all__ = ["FluxTunableTransmonPair", "FluxTunableCrossDriveTransmonPair"] @quam_dataclass @@ -85,3 +95,62 @@ def to_mutual_idle(self): """Sets the flux bias to the mutual idle offset for the control and target qubits.""" self.qubit_control.z.set_dc_offset(self.mutual_flux_bias[0]) self.qubit_target.z.set_dc_offset(self.mutual_flux_bias[1]) + + + +@quam_dataclass +class FluxTunableCrossDriveTransmonPair(QubitPair): + """A mixed qubit pair with both fixed-frequency and flux-tunable features.""" + + id: Union[int, str] + + # From FluxTunableTransmonPair + qubit_control: FluxTunableTransmon = None + qubit_target: FluxTunableTransmon = None + coupler: Optional[TunableCoupler] = None + detuning: Optional[float] = None + mutual_flux_bias: List[float] = field(default_factory=lambda: [0, 0]) + + # From FixedFrequencyTransmonPair + cross_resonance: Optional[Union[CrossResonanceMW, CrossResonanceIQ]] = None + zz_drive: Optional[Union[ZZDriveMW, ZZDriveIQ]] = None + xy_detuned: Union[MWChannel, IQChannel] = None + + # Shared fields + confusion: Optional[List[List[float]]] = None + extras: Dict[str, Any] = field(default_factory=dict) + + def align(self): + """Align channels from both subsystems and any additional components.""" + channels = [] + + for qubit in [self.qubit_control, self.qubit_target]: + if qubit: + channels += [ch.name for ch in qubit.channels.values()] + + for qubit in [self.fixed_qubit_control, self.fixed_qubit_target]: + if qubit: + channels += [ch.name for ch in qubit.channels.values()] + + for component in [self.coupler, self.cross_resonance, self.zz_drive, self.xy_detuned]: + if component: + channels.append(component.name) + + # Include CZ compensation channels if present + if hasattr(self, "macros") and "CZ" in self.macros: + for compensation in getattr(self.macros["CZ"], "compensations", []): + channels += [ch.name for ch in compensation["qubit"].channels.values()] + + align(*channels) + + def wait(self, duration): + """Wait across all relevant channels.""" + # Same channel logic as align() + self.align() # align does the same collection, so reuse it + wait(duration, *self._last_aligned_channels) + + def to_mutual_idle(self): + """Set mutual idle bias if using the flux qubits.""" + if self.qubit_control and self.qubit_target: + self.qubit_control.z.set_dc_offset(self.mutual_flux_bias[0]) + self.qubit_target.z.set_dc_offset(self.mutual_flux_bias[1]) diff --git a/quam_builder/builder/superconducting/add_transmon_pair_component.py b/quam_builder/builder/superconducting/add_transmon_pair_component.py index 739f4e61..16ac62e9 100644 --- a/quam_builder/builder/superconducting/add_transmon_pair_component.py +++ b/quam_builder/builder/superconducting/add_transmon_pair_component.py @@ -13,18 +13,19 @@ from quam_builder.architecture.superconducting.qubit_pair import ( FixedFrequencyTransmonPair, FluxTunableTransmonPair, + FluxTunableCrossDriveTransmonPair, ) def add_transmon_pair_tunable_coupler_component( - transmon_pair: Union[FixedFrequencyTransmonPair, FluxTunableTransmonPair], + transmon_pair: Union[FixedFrequencyTransmonPair, FluxTunableTransmonPair, FluxTunableCrossDriveTransmonPair], wiring_path: str, ports: Dict[str, str], ): """Adds a tunable coupler component to a transmon qubit pair based on the provided wiring path and ports. Args: - transmon_pair (Union[FixedFrequencyTransmonPair, FluxTunableTransmonPair]): The transmon qubit pair to which the tunable coupler component will be added. + transmon_pair (Union[FixedFrequencyTransmonPair, FluxTunableTransmonPair, FluxTunableCrossDriveTransmonPair]): The transmon qubit pair to which the tunable coupler component will be added. wiring_path (str): The path to the wiring configuration. ports (Dict[str, str]): A dictionary mapping port names to their respective configurations. @@ -48,14 +49,14 @@ def add_transmon_pair_tunable_coupler_component( def add_transmon_pair_cross_resonance_component( - transmon_pair: Union[FixedFrequencyTransmonPair, FluxTunableTransmonPair], + transmon_pair: Union[FixedFrequencyTransmonPair, FluxTunableTransmonPair, FluxTunableCrossDriveTransmonPair], wiring_path: str, ports: Dict[str, str], ): """Adds a cross resonance component to a transmon qubit pair based on the provided wiring path and ports. Args: - transmon_pair (Union[FixedFrequencyTransmonPair, FluxTunableTransmonPair]): The transmon qubit pair to which the cross resonance component will be added. + transmon_pair (Union[FixedFrequencyTransmonPair, FluxTunableTransmonPair, FluxTunableCrossDriveTransmonPair]): The transmon qubit pair to which the cross resonance component will be added. wiring_path (str): The path to the wiring configuration. ports (Dict[str, str]): A dictionary mapping port names to their respective configurations. @@ -91,14 +92,14 @@ def add_transmon_pair_cross_resonance_component( def add_transmon_pair_zz_drive_component( - transmon_pair: Union[FixedFrequencyTransmonPair, FluxTunableTransmonPair], + transmon_pair: Union[FixedFrequencyTransmonPair, FluxTunableTransmonPair, FluxTunableCrossDriveTransmonPair], wiring_path: str, ports: Dict[str, str], ): """Adds a ZZ drive component to a transmon qubit pair based on the provided wiring path and ports. Args: - transmon_pair (Union[FixedFrequencyTransmonPair, FluxTunableTransmonPair]): The transmon qubit pair to which the ZZ drive component will be added. + transmon_pair (Union[FixedFrequencyTransmonPair, FluxTunableTransmonPair, FluxTunableCrossDriveTransmonPair]): The transmon qubit pair to which the ZZ drive component will be added. wiring_path (str): The path to the wiring configuration. ports (Dict[str, str]): A dictionary mapping port names to their respective configurations. diff --git a/quam_builder/builder/superconducting/pulses.py b/quam_builder/builder/superconducting/pulses.py index 0f6817aa..70585547 100644 --- a/quam_builder/builder/superconducting/pulses.py +++ b/quam_builder/builder/superconducting/pulses.py @@ -7,6 +7,7 @@ from quam_builder.architecture.superconducting.qubit_pair import ( FixedFrequencyTransmonPair, FluxTunableTransmonPair, + FluxTunableCrossDriveTransmonPair, ) import numpy as np from typing import Union @@ -292,7 +293,7 @@ def add_default_transmon_pulses( def add_default_transmon_pair_pulses( - transmon_pair: Union[FixedFrequencyTransmonPair, FluxTunableTransmonPair] + transmon_pair: Union[FixedFrequencyTransmonPair, FluxTunableTransmonPair, FluxTunableCrossDriveTransmonPair] ): """Adds default pulses to a transmon qubit pair depending on its attributes: * transmon_pair.coupler.operations["const"] = pulses.SquarePulse(amplitude=0.1, length=100) @@ -300,7 +301,7 @@ def add_default_transmon_pair_pulses( * transmon_pair.zz_drive.operations["square"] = pulses.SquarePulse(amplitude=0.1, length=100) Args: - transmon_pair (Union[FixedFrequencyTransmonPair, FluxTunableTransmonPair]): The transmon qubit pair to which the pulses will be added. + transmon_pair (Union[FixedFrequencyTransmonPair, FluxTunableTransmonPair, FluxTunableCrossDriveTransmonPair]): The transmon qubit pair to which the pulses will be added. """ if hasattr(transmon_pair, "coupler"): if transmon_pair.coupler is not None: From 05daa9d07902de06681e20ba3941816c15c67663 Mon Sep 17 00:00:00 2001 From: HiroQM Date: Fri, 25 Jul 2025 11:46:01 +0900 Subject: [PATCH 09/17] unnecessary method override and also missing the case of upconverters --- .../architecture/superconducting/components/xy_drive.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/quam_builder/architecture/superconducting/components/xy_drive.py b/quam_builder/architecture/superconducting/components/xy_drive.py index 7e5e9253..ae7193cd 100644 --- a/quam_builder/architecture/superconducting/components/xy_drive.py +++ b/quam_builder/architecture/superconducting/components/xy_drive.py @@ -101,11 +101,6 @@ def set_output_power( class XYDriveMW(MWChannel, XYDriveBase): intermediate_frequency: float = "#./inferred_intermediate_frequency" - @property - def upconverter_frequency(self): - """Returns the up-converter/LO frequency in Hz.""" - return self.opx_output.upconverter_frequency - def get_output_power(self, operation, Z=50) -> float: """ Calculate the output power in dBm of the specified operation. From 7291eea1b7fac1de1284c9bd88b028c94e6da769 Mon Sep 17 00:00:00 2001 From: HiroQM Date: Fri, 25 Jul 2025 12:12:31 +0900 Subject: [PATCH 10/17] updated Changelog --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef2aaa6f..8828e5e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Fixed - Remove qualibrate and xarray from the requirements. +## [0.1.2] - 2025-07-25 +### Added +- QuamMacros for Cross Resonance and CZ. +- Architecture components for Flux Tunable Cross Drive TransmonPair. +- Fix for `upconverter_frequency` property under `XYDriveMW`. + ## [0.1.1] - 2025-07-14 ### Added - Optional `num_IQ_pairs` argument to `declare_qua_variables`. @@ -18,6 +24,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Builder functions for the general QUAM wiring. - Builder functions for Transmons. -[Unreleased]: https://github.com/qua-platform/quam-builder/compare/v0.1.1...HEAD +[Unreleased]: https://github.com/qua-platform/quam-builder/compare/v0.1.2...HEAD +[0.1.2]: https://github.com/qua-platform/quam-builder/releases/tag/v0.1.2 [0.1.1]: https://github.com/qua-platform/quam-builder/releases/tag/v0.1.1 [0.1.0]: https://github.com/qua-platform/quam-builder/releases/tag/v0.1.0 From 298f07437715dfc3cdf850dc527f93a68a0e5446 Mon Sep 17 00:00:00 2001 From: HiroQM Date: Thu, 14 Aug 2025 10:14:36 +0900 Subject: [PATCH 11/17] added architecture components for fixed-frequency zz-drive and cr/cz in flux-tunable transmons --- .../superconducting/qpu/__init__.py | 3 +- .../qpu/fixed_frequency_quam.py | 33 ++++++++++-- .../superconducting/qpu/flux_tunable_quam.py | 52 +++++++++++++++++-- .../superconducting/qubit/__init__.py | 20 ++++++- .../qubit/fixed_frequency_transmon.py | 12 ++++- .../qubit/flux_tunable_transmon.py | 11 +++- .../fixed_frequency_transmon_pair.py | 13 ++--- .../qubit_pair/flux_tunable_transmon_pair.py | 51 ++---------------- .../builder/qop_connectivity/create_wiring.py | 1 + .../add_transmon_drive_component.py | 46 ++++++++++++++++ .../builder/superconducting/build_quam.py | 3 ++ 11 files changed, 180 insertions(+), 65 deletions(-) diff --git a/quam_builder/architecture/superconducting/qpu/__init__.py b/quam_builder/architecture/superconducting/qpu/__init__.py index 12aadb96..bb18124a 100644 --- a/quam_builder/architecture/superconducting/qpu/__init__.py +++ b/quam_builder/architecture/superconducting/qpu/__init__.py @@ -1,6 +1,7 @@ from quam_builder.architecture.superconducting.qpu.base_quam import BaseQuam from quam_builder.architecture.superconducting.qpu.fixed_frequency_quam import ( FixedFrequencyQuam, + FixedFrequencyZZDriveQuam, ) from quam_builder.architecture.superconducting.qpu.flux_tunable_quam import ( FluxTunableQuam, @@ -14,4 +15,4 @@ *flux_tunable_quam.__all__, ] -AnyQuam = Union[BaseQuam, FixedFrequencyQuam, FluxTunableQuam, FluxTunableCrossDriveQuam] +AnyQuam = Union[BaseQuam, FixedFrequencyQuam, FluxTunableQuam, FluxTunableCrossDriveQuam, FixedFrequencyZZDriveQuam] diff --git a/quam_builder/architecture/superconducting/qpu/fixed_frequency_quam.py b/quam_builder/architecture/superconducting/qpu/fixed_frequency_quam.py index 744d383e..1798d2dc 100644 --- a/quam_builder/architecture/superconducting/qpu/fixed_frequency_quam.py +++ b/quam_builder/architecture/superconducting/qpu/fixed_frequency_quam.py @@ -1,16 +1,16 @@ from dataclasses import field -from typing import Dict, ClassVar, Type +from typing import Dict, ClassVar, Type, Union from quam.core import quam_dataclass -from quam_builder.architecture.superconducting.qubit import FixedFrequencyTransmon +from quam_builder.architecture.superconducting.qubit import FixedFrequencyTransmon, FixedFrequencyZZDriveTransmon from quam_builder.architecture.superconducting.qubit_pair import ( FixedFrequencyTransmonPair, ) from quam_builder.architecture.superconducting.qpu.base_quam import BaseQuam -__all__ = ["FixedFrequencyQuam", "FixedFrequencyTransmon", "FixedFrequencyTransmonPair"] +__all__ = ["FixedFrequencyQuam", "FixedFrequencyZZDriveQuam"] @quam_dataclass @@ -38,3 +38,30 @@ class FixedFrequencyQuam(BaseQuam): @classmethod def load(cls, *args, **kwargs) -> "FixedFrequencyQuam": return super().load(*args, **kwargs) + + +@quam_dataclass +class FixedFrequencyZZDriveQuam(FixedFrequencyQuam): + """Example of a QUAM composed of fixed frequency transmons. + + Attributes: + qubit_type (ClassVar[Type[FixedFrequencyTransmon]]): The type of the qubits in the QUAM for type hinting. + qubit_pair_type (ClassVar[Type[FixedFrequencyTransmonPair]]): The type of the qubit pairs in the QUAM for type hinting. + qubits (Dict[str, FixedFrequencyTransmon]): A dictionary of qubits composing the QUAM. + qubit_pairs (Dict[str, FixedFrequencyTransmonPair]): A dictionary of qubit pairs composing the QUAM. + + Methods: + load: Loads the QUAM from the state.json file. + """ + + qubit_type: ClassVar[Type[FixedFrequencyZZDriveTransmon]] = FixedFrequencyZZDriveTransmon + qubit_pair_type: ClassVar[Type[FixedFrequencyTransmonPair]] = ( + FixedFrequencyTransmonPair + ) + + qubits: Dict[str, FixedFrequencyZZDriveTransmon] = field(default_factory=dict) + qubit_pairs: Dict[str, FixedFrequencyTransmonPair] = field(default_factory=dict) + + @classmethod + def load(cls, *args, **kwargs) -> "FixedFrequencyZZDriveQuam": + return super().load(*args, **kwargs) diff --git a/quam_builder/architecture/superconducting/qpu/flux_tunable_quam.py b/quam_builder/architecture/superconducting/qpu/flux_tunable_quam.py index 2ef53d9d..c4ea9225 100644 --- a/quam_builder/architecture/superconducting/qpu/flux_tunable_quam.py +++ b/quam_builder/architecture/superconducting/qpu/flux_tunable_quam.py @@ -4,12 +4,13 @@ from quam.core import quam_dataclass -from quam_builder.architecture.superconducting.qubit import FluxTunableTransmon +from quam_builder.architecture.superconducting.qubit import AnyFluxTunableTransmon +from quam_builder.architecture.superconducting.qubit import FluxTunableTransmon, FluxTunableZZDriveTransmon from quam_builder.architecture.superconducting.qubit_pair import FluxTunableTransmonPair, FluxTunableCrossDriveTransmonPair from quam_builder.architecture.superconducting.qpu.base_quam import BaseQuam -__all__ = ["FluxTunableQuam", "FluxTunableCrossDriveQuam", "FluxTunableTransmon", "FluxTunableTransmonPair"] +__all__ = ["FluxTunableQuam", "FluxTunableCrossDriveQuam"] @quam_dataclass @@ -143,10 +144,55 @@ class FluxTunableCrossDriveQuam(FluxTunableQuam): qubit_pairs (Dict[str, FixedFrequencyTransmonPair]): A dictionary of qubit pairs composing the QUAM. """ + qubit_type: ClassVar[Type[FluxTunableZZDriveTransmon]] = FluxTunableZZDriveTransmon qubit_pair_type: ClassVar[Type[FluxTunableCrossDriveTransmonPair]] = FluxTunableCrossDriveTransmonPair + + qubits: Dict[str, FluxTunableZZDriveTransmon] = field(default_factory=dict) qubit_pairs: Dict[str, FluxTunableCrossDriveTransmonPair] = field(default_factory=dict) + @classmethod def load(cls, *args, **kwargs) -> "FluxTunableCrossDriveQuam": return super().load(*args, **kwargs) - \ No newline at end of file + + def set_all_fluxes( + self, + flux_point: str, + target: Union[FluxTunableZZDriveTransmon, FluxTunableCrossDriveTransmonPair], + ): + """Set the fluxes to the specified point for the target qubit or qubit pair. + + Args: + flux_point (str): The flux point to set ('independent', 'pairwise', 'joint', 'min'). + target (Union[FluxTunableZZDriveTransmon, FluxTunableCrossDriveTransmonPair]): The target qubit or qubit pair. + """ + if flux_point == "independent": + assert isinstance( + target, FluxTunableZZDriveTransmon + ), "Independent flux point is only supported for individual transmons" + elif flux_point == "pairwise": + assert isinstance( + target, FluxTunableCrossDriveTransmonPair + ), "Pairwise flux point is only supported for transmon pairs" + + target_bias = None + if flux_point == "joint": + self.apply_all_flux_to_joint_idle() + if isinstance(target, FluxTunableCrossDriveTransmonPair): + target_bias = target.mutual_flux_bias + else: + target_bias = target.z.joint_offset + else: + self.apply_all_flux_to_min() + + if flux_point == "independent": + target.z.to_independent_idle() + target_bias = target.z.independent_offset + + elif flux_point == "pairwise": + target.to_mutual_idle() + target_bias = target.mutual_flux_bias + + target.z.settle() + target.align() + return target_bias \ No newline at end of file diff --git a/quam_builder/architecture/superconducting/qubit/__init__.py b/quam_builder/architecture/superconducting/qubit/__init__.py index 349f4339..6fe3129b 100644 --- a/quam_builder/architecture/superconducting/qubit/__init__.py +++ b/quam_builder/architecture/superconducting/qubit/__init__.py @@ -1,9 +1,11 @@ from quam_builder.architecture.superconducting.qubit.base_transmon import BaseTransmon from quam_builder.architecture.superconducting.qubit.fixed_frequency_transmon import ( FixedFrequencyTransmon, + FixedFrequencyZZDriveTransmon, ) from quam_builder.architecture.superconducting.qubit.flux_tunable_transmon import ( FluxTunableTransmon, + FluxTunableZZDriveTransmon, ) from typing import Union @@ -13,4 +15,20 @@ *flux_tunable_transmon.__all__, ] -AnyTransmon = Union[BaseTransmon, FixedFrequencyTransmon, FluxTunableTransmon] +AnyTransmon = Union[ + BaseTransmon, + FixedFrequencyTransmon, + FixedFrequencyZZDriveTransmon, + FluxTunableTransmon, + FluxTunableZZDriveTransmon, +] + +AnyFixedFrequencyTransmon = Union[ + FixedFrequencyTransmon, + FixedFrequencyZZDriveTransmon, +] + +AnyFluxTunableTransmon = Union[ + FluxTunableTransmon, + FluxTunableZZDriveTransmon, +] diff --git a/quam_builder/architecture/superconducting/qubit/fixed_frequency_transmon.py b/quam_builder/architecture/superconducting/qubit/fixed_frequency_transmon.py index be59aa77..34657943 100644 --- a/quam_builder/architecture/superconducting/qubit/fixed_frequency_transmon.py +++ b/quam_builder/architecture/superconducting/qubit/fixed_frequency_transmon.py @@ -1,7 +1,10 @@ +from typing import Union from quam.core import quam_dataclass + +from quam.components.channels import IQChannel, MWChannel from quam_builder.architecture.superconducting.qubit.base_transmon import BaseTransmon -__all__ = ["FixedFrequencyTransmon"] +__all__ = ["FixedFrequencyTransmon", "FixedFrequencyZZDriveTransmon"] @quam_dataclass @@ -14,3 +17,10 @@ class FixedFrequencyTransmon(BaseTransmon): """ pass + + +@quam_dataclass +class FixedFrequencyZZDriveTransmon(FixedFrequencyTransmon): + """Quam Component for flux-tunable features and added Stark ZZ drive.""" + + xy_detuned: Union[MWChannel, IQChannel] = None diff --git a/quam_builder/architecture/superconducting/qubit/flux_tunable_transmon.py b/quam_builder/architecture/superconducting/qubit/flux_tunable_transmon.py index d98271a7..52c437e5 100644 --- a/quam_builder/architecture/superconducting/qubit/flux_tunable_transmon.py +++ b/quam_builder/architecture/superconducting/qubit/flux_tunable_transmon.py @@ -1,11 +1,13 @@ +from typing import Union from quam.core import quam_dataclass +from quam.components.channels import IQChannel, MWChannel from quam_builder.architecture.superconducting.qubit.fixed_frequency_transmon import ( FixedFrequencyTransmon, ) from quam_builder.architecture.superconducting.components.flux_line import FluxLine -__all__ = ["FluxTunableTransmon"] +__all__ = ["FluxTunableTransmon", "FluxTunableZZDriveTransmon"] @quam_dataclass @@ -25,3 +27,10 @@ class FluxTunableTransmon(FixedFrequencyTransmon): freq_vs_flux_01_quad_term: float = 0.0 phi0_current: float = 0.0 phi0_voltage: float = 0.0 + + +@quam_dataclass +class FluxTunableZZDriveTransmon(FluxTunableTransmon): + """Quam Component for flux-tunable features and added Stark ZZ drive.""" + + xy_detuned: Union[MWChannel, IQChannel] = None diff --git a/quam_builder/architecture/superconducting/qubit_pair/fixed_frequency_transmon_pair.py b/quam_builder/architecture/superconducting/qubit_pair/fixed_frequency_transmon_pair.py index e12924a1..d7f94374 100644 --- a/quam_builder/architecture/superconducting/qubit_pair/fixed_frequency_transmon_pair.py +++ b/quam_builder/architecture/superconducting/qubit_pair/fixed_frequency_transmon_pair.py @@ -12,9 +12,7 @@ ZZDriveIQ, ZZDriveMW, ) -from quam_builder.architecture.superconducting.qubit.fixed_frequency_transmon import ( - FixedFrequencyTransmon, -) +from quam_builder.architecture.superconducting.qubit import AnyFixedFrequencyTransmon __all__ = ["FixedFrequencyTransmonPair"] @@ -27,8 +25,8 @@ class FixedFrequencyTransmonPair(QubitPair): Attributes: id (Union[int, str]): The id of the Transmon pair, used to generate the name. Can be a string, or an integer in which case it will add `Channel._default_label`. - qubit_control (FixedFrequencyTransmon): The control qubit of the pair. - qubit_target (FixedFrequencyTransmon): The target qubit of the pair. + qubit_control (Union[FixedFrequencyTransmon, FixedFrequencyZZDriveTransmon]): The control qubit of the pair. + qubit_target (Union[FixedFrequencyTransmon, FixedFrequencyZZDriveTransmon]): The target qubit of the pair. cross_resonance (Optional[Union[CrossResonanceMW, CrossResonanceIQ]]): The cross resonance component. zz_drive (Optional[Union[ZZDriveMW, ZZDriveIQ]]): The ZZ drive component. xy_detuned (Union[MWChannel, IQChannel]): The detuned xy drive component. @@ -37,12 +35,11 @@ class FixedFrequencyTransmonPair(QubitPair): """ id: Union[int, str] - qubit_control: FixedFrequencyTransmon = None - qubit_target: FixedFrequencyTransmon = None + qubit_control: AnyFixedFrequencyTransmon = None + qubit_target: AnyFixedFrequencyTransmon = None cross_resonance: Optional[Union[CrossResonanceMW, CrossResonanceIQ]] = None zz_drive: Optional[Union[ZZDriveMW, ZZDriveIQ]] = None - xy_detuned: Union[MWChannel, IQChannel] = None confusion: list = None diff --git a/quam_builder/architecture/superconducting/qubit_pair/flux_tunable_transmon_pair.py b/quam_builder/architecture/superconducting/qubit_pair/flux_tunable_transmon_pair.py index 06683174..11497935 100644 --- a/quam_builder/architecture/superconducting/qubit_pair/flux_tunable_transmon_pair.py +++ b/quam_builder/architecture/superconducting/qubit_pair/flux_tunable_transmon_pair.py @@ -10,6 +10,7 @@ ) from quam_builder.architecture.superconducting.qubit.flux_tunable_transmon import ( FluxTunableTransmon, + FluxTunableZZDriveTransmon, ) from quam_builder.architecture.superconducting.components.cross_resonance import ( CrossResonanceIQ, @@ -97,60 +98,16 @@ def to_mutual_idle(self): self.qubit_target.z.set_dc_offset(self.mutual_flux_bias[1]) - @quam_dataclass -class FluxTunableCrossDriveTransmonPair(QubitPair): +class FluxTunableCrossDriveTransmonPair(FluxTunableTransmonPair): """A mixed qubit pair with both fixed-frequency and flux-tunable features.""" - id: Union[int, str] - # From FluxTunableTransmonPair - qubit_control: FluxTunableTransmon = None - qubit_target: FluxTunableTransmon = None - coupler: Optional[TunableCoupler] = None + qubit_control: FluxTunableZZDriveTransmon = None + qubit_target: FluxTunableZZDriveTransmon = None detuning: Optional[float] = None - mutual_flux_bias: List[float] = field(default_factory=lambda: [0, 0]) # From FixedFrequencyTransmonPair cross_resonance: Optional[Union[CrossResonanceMW, CrossResonanceIQ]] = None zz_drive: Optional[Union[ZZDriveMW, ZZDriveIQ]] = None - xy_detuned: Union[MWChannel, IQChannel] = None - - # Shared fields - confusion: Optional[List[List[float]]] = None - extras: Dict[str, Any] = field(default_factory=dict) - - def align(self): - """Align channels from both subsystems and any additional components.""" - channels = [] - - for qubit in [self.qubit_control, self.qubit_target]: - if qubit: - channels += [ch.name for ch in qubit.channels.values()] - - for qubit in [self.fixed_qubit_control, self.fixed_qubit_target]: - if qubit: - channels += [ch.name for ch in qubit.channels.values()] - - for component in [self.coupler, self.cross_resonance, self.zz_drive, self.xy_detuned]: - if component: - channels.append(component.name) - - # Include CZ compensation channels if present - if hasattr(self, "macros") and "CZ" in self.macros: - for compensation in getattr(self.macros["CZ"], "compensations", []): - channels += [ch.name for ch in compensation["qubit"].channels.values()] - - align(*channels) - def wait(self, duration): - """Wait across all relevant channels.""" - # Same channel logic as align() - self.align() # align does the same collection, so reuse it - wait(duration, *self._last_aligned_channels) - - def to_mutual_idle(self): - """Set mutual idle bias if using the flux qubits.""" - if self.qubit_control and self.qubit_target: - self.qubit_control.z.set_dc_offset(self.mutual_flux_bias[0]) - self.qubit_target.z.set_dc_offset(self.mutual_flux_bias[1]) diff --git a/quam_builder/builder/qop_connectivity/create_wiring.py b/quam_builder/builder/qop_connectivity/create_wiring.py index f98e4bfb..8085afc2 100644 --- a/quam_builder/builder/qop_connectivity/create_wiring.py +++ b/quam_builder/builder/qop_connectivity/create_wiring.py @@ -35,6 +35,7 @@ def create_wiring(connectivity: Connectivity) -> dict: WiringLineType.RESONATOR, WiringLineType.DRIVE, WiringLineType.FLUX, + WiringLineType.DETUNED_DRIVE, ]: for k, v in qubit_wiring(channels, element_id, line_type).items(): set_nested_value_with_path( diff --git a/quam_builder/builder/superconducting/add_transmon_drive_component.py b/quam_builder/builder/superconducting/add_transmon_drive_component.py index c7672680..a46fee01 100644 --- a/quam_builder/builder/superconducting/add_transmon_drive_component.py +++ b/quam_builder/builder/superconducting/add_transmon_drive_component.py @@ -14,6 +14,7 @@ FixedFrequencyTransmon, FluxTunableTransmon, ) +from quam_builder.architecture.superconducting.qubit import AnyTransmon from qualang_tools.addons.calibration.calibrations import unit @@ -63,3 +64,48 @@ def add_transmon_drive_component( raise ValueError( f"Unimplemented mapping of port keys to channel for ports: {ports}" ) + + +def add_transmon_detuned_drive_component( + transmon: AnyTransmon, + wiring_path: str, + ports: Dict[str, str], +): + """Adds a drive component to a transmon qubit based on the provided wiring path and ports. + + Args: + transmon (Union[FixedFrequencyTransmon, FluxTunableTransmon]): The transmon qubit to which the drive component will be added. + wiring_path (str): The path to the wiring configuration. + ports (Dict[str, str]): A dictionary mapping port names to their respective configurations. + + Raises: + ValueError: If the port keys do not match any implemented mapping. + """ + digital_outputs = get_digital_outputs(wiring_path, ports) + + if all(key in ports for key in iq_out_channel_ports): + # LF-FEM & Octave or OPX+ & Octave + transmon.xy_detuned = XYDriveIQ( + opx_output_I=f"{wiring_path}/opx_output_I", + opx_output_Q=f"{wiring_path}/opx_output_Q", + frequency_converter_up=f"{wiring_path}/frequency_converter_up", + RF_frequency=None, + digital_outputs=digital_outputs, + ) + + RF_output = transmon.xy.frequency_converter_up + RF_output.channel = transmon.xy.get_reference() + RF_output.output_mode = "always_on" # "triggered" + + elif all(key in ports for key in mw_out_channel_ports): + # MW-FEM single channel + transmon.xy_detuned = XYDriveMW( + opx_output=f"{wiring_path}/opx_output", + digital_outputs=digital_outputs, + RF_frequency=None, + ) + + else: + raise ValueError( + f"Unimplemented mapping of port keys to channel for ports: {ports}" + ) diff --git a/quam_builder/builder/superconducting/build_quam.py b/quam_builder/builder/superconducting/build_quam.py index d25a3d3e..199a486d 100644 --- a/quam_builder/builder/superconducting/build_quam.py +++ b/quam_builder/builder/superconducting/build_quam.py @@ -9,6 +9,7 @@ ) from quam_builder.builder.superconducting.add_transmon_drive_component import ( add_transmon_drive_component, + add_transmon_detuned_drive_component, ) from quam_builder.builder.superconducting.add_transmon_flux_component import ( add_transmon_flux_component, @@ -107,6 +108,8 @@ def add_transmons(machine: AnyQuam): add_transmon_drive_component(transmon, wiring_path, ports) elif line_type == WiringLineType.FLUX.value: add_transmon_flux_component(transmon, wiring_path, ports) + elif line_type == WiringLineType.DETUNED_DRIVE.value: + add_transmon_detuned_drive_component(transmon, wiring_path, ports) else: raise ValueError(f"Unknown line type: {line_type}") machine.active_qubit_names.append(transmon.name) From 4396ded3c3e1b8f702b904d6076bb32d287b3c4d Mon Sep 17 00:00:00 2001 From: HiroQM Date: Thu, 14 Aug 2025 10:22:32 +0900 Subject: [PATCH 12/17] added stark-induced cz quammacro --- .../superconducting/custom_gates/__init__.py | 7 +- .../custom_gates/stark_induced_cz.py | 84 +++++++++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 quam_builder/architecture/superconducting/custom_gates/stark_induced_cz.py diff --git a/quam_builder/architecture/superconducting/custom_gates/__init__.py b/quam_builder/architecture/superconducting/custom_gates/__init__.py index 87708dff..14952849 100644 --- a/quam_builder/architecture/superconducting/custom_gates/__init__.py +++ b/quam_builder/architecture/superconducting/custom_gates/__init__.py @@ -1,6 +1,11 @@ -from quam_builder.architecture.superconducting.custom_gates import cross_resonance, cz +from quam_builder.architecture.superconducting.custom_gates import ( + cross_resonance, + cz, + stark_induced_cz, +) __all__ = [ *cross_resonance.__all__, *cz.__all__, + *stark_induced_cz.__all__, ] \ No newline at end of file diff --git a/quam_builder/architecture/superconducting/custom_gates/stark_induced_cz.py b/quam_builder/architecture/superconducting/custom_gates/stark_induced_cz.py new file mode 100644 index 00000000..97fa5af0 --- /dev/null +++ b/quam_builder/architecture/superconducting/custom_gates/stark_induced_cz.py @@ -0,0 +1,84 @@ +from typing import Optional, Literal, Union, Tuple, List +from qm.qua import * +from quam.core import quam_dataclass +from quam.components.pulses import Pulse +from quam.components.macro import QubitPairMacro +from qm.qua._dsl import QuaExpression, QuaVariable + +__all__ = ["StarkInducedCZGate"] + + +qua_T = Union[QuaVariable, QuaExpression] +# type for rotation matrix amp(c, -s, s, c) +# relative phase between control and target is done by amp(c, -s, s, c) instead +_tuple = Tuple[Union[float, qua_T]] +_list = List[Union[float, qua_T]] + +@quam_dataclass +class StarkInducedCZGate(QubitPairMacro): + qc_correction_phase: float = 0.0 + qt_correction_phase: float = 0.0 + + def apply( + self, + wf_type: Literal["square", "cosine", "gauss", "flattop"] = "flattop", + zz_control_amp_scaling: Optional[Union[float, qua_T, _tuple, _list]] = None, + zz_target_amp_scaling: Optional[Union[float, qua_T, _tuple, _list]] = None, + qc_correction_phase: Optional[Union[float, qua_T]] = None, + qt_correction_phase: Optional[Union[float, qua_T]] = None, + zz_duration_clock_cycles: Optional[Union[float, qua_T]] = None, + ) -> None: + + qc = self.qubit_pair.qubit_control + qt = self.qubit_pair.qubit_target + zz = self.qubit_pair.zz_drive + + def _play_zz_pulse( + elem, + wf_type: str = wf_type, + amp_scale: Optional[Union[float, qua_T, _tuple, _list]] = None, + duration: Optional[Union[float, qua_T, _tuple, _list]] = None, + ): + if amp_scale is None and duration is None: + elem.play(wf_type) + elif amp_scale is None: + elem.play(wf_type, duration=duration) + elif duration is None: + elem.play(wf_type, amplitude_scale=amp_scale) + else: + elem.play(wf_type, amplitude_scale=amp_scale, duration=duration) + + # ZI phase correction + def qc_shift_correction_phase(): + if qc_correction_phase is not None: + qc.xy.frame_rotation_2pi(qc_correction_phase) + + # IZ phase correction + def qt_shift_correction_phase(): + if qt_correction_phase is not None: + qt.xy.frame_rotation_2pi(qt_correction_phase) + + def zz_control_drive_play(wf_type=wf_type): + _play_zz_pulse( + elem=zz, + wf_type=wf_type, + amp_scale=zz_control_amp_scaling, + duration=zz_duration_clock_cycles, + ) + + def zz_target_drive_play(wf_type=wf_type): + _play_zz_pulse( + elem=qt.xy_detuned, + wf_type=f"zz_{wf_type}_{self.qubit_pair.name}", + amp_scale=zz_target_amp_scaling, + duration=zz_duration_clock_cycles, + ) + + # Pulse sequence + zz_control_drive_play() + zz_target_drive_play() + + self.qubit_pair.align() + + qt_shift_correction_phase() + qt_shift_correction_phase() From b8a25c355629f1108060860633b648abded26c87 Mon Sep 17 00:00:00 2001 From: HiroQM Date: Thu, 14 Aug 2025 10:24:19 +0900 Subject: [PATCH 13/17] changed required version for qualibration-libs --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 475983c3..fe81563a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ authors = [{ name = "Theo Laudat", email = "theo@quantum-machines.co" }] requires-python = ">=3.9,<=3.12" dependencies = [ "qualang-tools>=0.19.0", - "qualibration-libs@git+https://github.com/qua-platform/qualibration-libs.git", + "qualibration-libs>=0.1.0", "quam>=0.4.0", ] From f537f6ff10c1ea33ef8358d722100b0cf727cdbd Mon Sep 17 00:00:00 2001 From: HiroQM Date: Thu, 14 Aug 2025 10:22:32 +0900 Subject: [PATCH 14/17] added stark-induced cz quammacro --- .../superconducting/custom_gates/__init__.py | 7 +- .../custom_gates/stark_induced_cz.py | 93 +++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 quam_builder/architecture/superconducting/custom_gates/stark_induced_cz.py diff --git a/quam_builder/architecture/superconducting/custom_gates/__init__.py b/quam_builder/architecture/superconducting/custom_gates/__init__.py index 87708dff..14952849 100644 --- a/quam_builder/architecture/superconducting/custom_gates/__init__.py +++ b/quam_builder/architecture/superconducting/custom_gates/__init__.py @@ -1,6 +1,11 @@ -from quam_builder.architecture.superconducting.custom_gates import cross_resonance, cz +from quam_builder.architecture.superconducting.custom_gates import ( + cross_resonance, + cz, + stark_induced_cz, +) __all__ = [ *cross_resonance.__all__, *cz.__all__, + *stark_induced_cz.__all__, ] \ No newline at end of file diff --git a/quam_builder/architecture/superconducting/custom_gates/stark_induced_cz.py b/quam_builder/architecture/superconducting/custom_gates/stark_induced_cz.py new file mode 100644 index 00000000..82d2b4ef --- /dev/null +++ b/quam_builder/architecture/superconducting/custom_gates/stark_induced_cz.py @@ -0,0 +1,93 @@ +from typing import Optional, Literal, Union, Tuple, List +from qm.qua import * +from quam.core import quam_dataclass +from quam.components.pulses import Pulse +from quam.components.macro import QubitPairMacro +from qm.qua._dsl import QuaExpression, QuaVariable + +__all__ = ["StarkInducedCZGate"] + + +qua_T = Union[QuaVariable, QuaExpression] +# type for rotation matrix amp(c, -s, s, c) +# relative phase between control and target is done by amp(c, -s, s, c) instead +_tuple = Tuple[Union[float, qua_T]] +_list = List[Union[float, qua_T]] + +@quam_dataclass +class StarkInducedCZGate(QubitPairMacro): + qc_correction_phase: float = 0.0 + qt_correction_phase: float = 0.0 + zz_relative_phase: float = 0.0 + + def apply( + self, + wf_type: Literal["square", "cosine", "gauss", "flattop"] = "flattop", + zz_control_amp_scaling: Optional[Union[float, qua_T, _tuple, _list]] = None, + zz_target_amp_scaling: Optional[Union[float, qua_T, _tuple, _list]] = None, + zz_relative_phase: Optional[Union[float, qua_T, _tuple, _list]] = None, + qc_correction_phase: Optional[Union[float, qua_T]] = None, + qt_correction_phase: Optional[Union[float, qua_T]] = None, + zz_duration_clock_cycles: Optional[Union[float, qua_T]] = None, + ) -> None: + + qc = self.qubit_pair.qubit_control + qt = self.qubit_pair.qubit_target + zz = self.qubit_pair.zz_drive + + def _play_zz_pulse( + elem, + wf_type: str = wf_type, + amp_scale: Optional[Union[float, qua_T, _tuple, _list]] = None, + duration: Optional[Union[float, qua_T, _tuple, _list]] = None, + ): + if amp_scale is None and duration is None: + elem.play(wf_type) + elif amp_scale is None: + elem.play(wf_type, duration=duration) + elif duration is None: + elem.play(wf_type, amplitude_scale=amp_scale) + else: + elem.play(wf_type, amplitude_scale=amp_scale, duration=duration) + + # ZI phase correction + def qc_shift_correction_phase(): + if qc_correction_phase is not None: + qc.xy.frame_rotation_2pi(qc_correction_phase) + + # IZ phase correction + def qt_shift_correction_phase(): + if qt_correction_phase is not None: + qt.xy.frame_rotation_2pi(qt_correction_phase) + + # relative phase rotation + def zz_shift_relative_phase(): + if zz_relative_phase is not None: + qt.xy_detuned.frame_rotation_2pi(zz_relative_phase) + + def zz_control_drive_play(wf_type=wf_type): + _play_zz_pulse( + elem=zz, + wf_type=wf_type, + amp_scale=zz_control_amp_scaling, + duration=zz_duration_clock_cycles, + ) + + def zz_target_drive_play(wf_type=wf_type): + _play_zz_pulse( + elem=qt.xy_detuned, + wf_type=f"zz_{wf_type}_{self.qubit_pair.name}", + amp_scale=zz_target_amp_scaling, + duration=zz_duration_clock_cycles, + ) + + # Pulse sequence + zz_shift_relative_phase() + align(zz.name, qt.xy_detuned.name) + zz_control_drive_play() + zz_target_drive_play() + + align(zz.name, qt.xy_detuned.name, qc.xy.name, qt.xy.name) + + qt_shift_correction_phase() + qt_shift_correction_phase() From 4e5bbd8b908416617cd348a7e698d21a344dbe09 Mon Sep 17 00:00:00 2001 From: HiroQM Date: Thu, 14 Aug 2025 10:24:19 +0900 Subject: [PATCH 15/17] changed required version for qualibration-libs --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 475983c3..fe81563a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ authors = [{ name = "Theo Laudat", email = "theo@quantum-machines.co" }] requires-python = ">=3.9,<=3.12" dependencies = [ "qualang-tools>=0.19.0", - "qualibration-libs@git+https://github.com/qua-platform/qualibration-libs.git", + "qualibration-libs>=0.1.0", "quam>=0.4.0", ] From 2020967c0a4e97b6b749f47686107bce8825edf0 Mon Sep 17 00:00:00 2001 From: soonqm Date: Mon, 25 Aug 2025 02:11:24 +0900 Subject: [PATCH 16/17] add amp scaling params to stark CZ --- .../superconducting/custom_gates/stark_induced_cz.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/quam_builder/architecture/superconducting/custom_gates/stark_induced_cz.py b/quam_builder/architecture/superconducting/custom_gates/stark_induced_cz.py index 97fa5af0..46c247e7 100644 --- a/quam_builder/architecture/superconducting/custom_gates/stark_induced_cz.py +++ b/quam_builder/architecture/superconducting/custom_gates/stark_induced_cz.py @@ -14,10 +14,13 @@ _tuple = Tuple[Union[float, qua_T]] _list = List[Union[float, qua_T]] + @quam_dataclass class StarkInducedCZGate(QubitPairMacro): qc_correction_phase: float = 0.0 qt_correction_phase: float = 0.0 + zz_control_amp_scaling: Union[float, qua_T, _tuple, _list] = 1.0 + zz_target_amp_scaling: Union[float, qua_T, _tuple, _list] = 1.0 def apply( self, @@ -28,7 +31,6 @@ def apply( qt_correction_phase: Optional[Union[float, qua_T]] = None, zz_duration_clock_cycles: Optional[Union[float, qua_T]] = None, ) -> None: - qc = self.qubit_pair.qubit_control qt = self.qubit_pair.qubit_target zz = self.qubit_pair.zz_drive @@ -80,5 +82,5 @@ def zz_target_drive_play(wf_type=wf_type): self.qubit_pair.align() - qt_shift_correction_phase() + qc_shift_correction_phase() qt_shift_correction_phase() From bf12b11a44199b61c5e4e322ed925dc4575ac496 Mon Sep 17 00:00:00 2001 From: HiroQM Date: Fri, 10 Oct 2025 15:36:03 +0900 Subject: [PATCH 17/17] added component for detuned xy drive --- .../components/xy_detuned_drive.py | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 quam_builder/architecture/superconducting/components/xy_detuned_drive.py diff --git a/quam_builder/architecture/superconducting/components/xy_detuned_drive.py b/quam_builder/architecture/superconducting/components/xy_detuned_drive.py new file mode 100644 index 00000000..aa473335 --- /dev/null +++ b/quam_builder/architecture/superconducting/components/xy_detuned_drive.py @@ -0,0 +1,40 @@ +from quam.core import quam_dataclass +from quam.components.channels import IQChannel, MWChannel +from quam_builder.architecture.superconducting.components.xy_drive import XYDriveMW, XYDriveIQ + + +__all__ = ["XYDetunedDriveIQ", "XYDetunedDriveMW"] + +@quam_dataclass +class XYDetunedDriveBase: + xy_RF_frequency: float = None + xy_intermediate_frequency: float = None + detuning: float = None + + +@quam_dataclass +class XYDetunedDriveIQ(XYDriveIQ, XYDetunedDriveBase): + RF_frequency: float = None + intermediate_frequency: float = None + + @property + def inferred_RF_frequency(self) -> float: + return self.xy_RF_frequency + self.detuning + + @property + def inferred_intermediate_frequency(self) -> float: + return self.xy_intermediate_frequency + self.detuning + + +@quam_dataclass +class XYDetunedDriveMW(XYDriveMW, XYDetunedDriveBase): + RF_frequency: float = None + intermediate_frequency: float = None + + @property + def inferred_RF_frequency(self) -> float: + return self.xy_RF_frequency + self.detuning + + @property + def inferred_intermediate_frequency(self) -> float: + return self.xy_intermediate_frequency + self.detuning