diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cc01769..4fc114c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Fixed - `add_default_transmon_pair_macros` now passes `flux_pulse_qubit="const"` (was `flux_pulse_control="const"`) to `CZGate`, matching the field rename introduced in v0.4.0 and aligning with the `"const"` pulse added to every `FluxTunableTransmon` Z line by `add_default_transmon_pulses`. +- Applied black formatting across the entire repo. ## [0.4.0] - 2026-05-26 diff --git a/pylint_qua_plugin/INTEGRATION_GUIDE.md b/pylint_qua_plugin/INTEGRATION_GUIDE.md index 53e20ed3..8a6d7b8c 100644 --- a/pylint_qua_plugin/INTEGRATION_GUIDE.md +++ b/pylint_qua_plugin/INTEGRATION_GUIDE.md @@ -342,4 +342,4 @@ If pre-commit isn't finding the plugin: ## License -Apache-2.0 \ No newline at end of file +Apache-2.0 diff --git a/pylint_qua_plugin/README.md b/pylint_qua_plugin/README.md index 6e6e2979..f8514d3a 100644 --- a/pylint_qua_plugin/README.md +++ b/pylint_qua_plugin/README.md @@ -153,4 +153,4 @@ if_(n_op & 1 == 0) # pylint: disable=C1805 ## License -Apache-2.0 \ No newline at end of file +Apache-2.0 diff --git a/pylint_qua_plugin/__init__.py b/pylint_qua_plugin/__init__.py index f6c3b3b5..d4924f11 100644 --- a/pylint_qua_plugin/__init__.py +++ b/pylint_qua_plugin/__init__.py @@ -7,4 +7,4 @@ from .pylint_qua_plugin import register __version__ = "0.1.0" -__all__ = ["register"] \ No newline at end of file +__all__ = ["register"] diff --git a/pylint_qua_plugin/pylint_qua_plugin.py b/pylint_qua_plugin/pylint_qua_plugin.py index f2d07fcd..b2b019d8 100644 --- a/pylint_qua_plugin/pylint_qua_plugin.py +++ b/pylint_qua_plugin/pylint_qua_plugin.py @@ -8,11 +8,11 @@ Usage: 1. Add to your pylint configuration: - + # pyproject.toml [tool.pylint.main] load-plugins = ["pylint_qua_plugin"] - + # Or .pylintrc [MAIN] load-plugins=pylint_qua_plugin @@ -25,7 +25,7 @@ Suppressed rules: - C1805: use-implicit-booleaness-not-comparison-to-zero - - C1803: use-implicit-booleaness-not-comparison-to-string + - C1803: use-implicit-booleaness-not-comparison-to-string - C0121: singleton-comparison - R1714: consider-using-in - W0104: pointless-statement @@ -45,11 +45,19 @@ from astroid import nodes, MANAGER from pylint.lint import PyLinter - # Rules that conflict with QUA DSL semantics (both codes and symbols) QUA_SUPPRESSED_MSGIDS: Set[str] = { # Message codes (uppercase) - "C1805", "C1803", "C0121", "R1714", "W0104", "W0106", "R1705", "R1720", "R1709", "W0127", + "C1805", + "C1803", + "C0121", + "R1714", + "W0104", + "W0106", + "R1705", + "R1720", + "R1709", + "W0127", # Symbolic names (pylint uses these internally) "use-implicit-booleaness-not-comparison-to-zero", "use-implicit-booleaness-not-comparison-to-string", @@ -67,44 +75,83 @@ # These can appear outside of `with qua.program()` blocks in helper functions QUA_CONTROL_FLOW_FUNCTIONS: Set[str] = { # Control flow - "if_", "else_", "elif_", - "for_", "for_each_", + "if_", + "else_", + "elif_", + "for_", + "for_each_", "while_", - "switch_", "case_", "default_", + "switch_", + "case_", + "default_", # Variable operations - "assign", "assign_addition_", "assign_subtraction_", - "declare", "declare_stream", + "assign", + "assign_addition_", + "assign_subtraction_", + "declare", + "declare_stream", # Timing and synchronization - "wait", "wait_for_trigger", "align", "reset_phase", "reset_frame", "reset_global_phase", - "frame_rotation", "frame_rotation_2pi", "update_frequency", + "wait", + "wait_for_trigger", + "align", + "reset_phase", + "reset_frame", + "reset_global_phase", + "frame_rotation", + "frame_rotation_2pi", + "update_frequency", # Playback - "play", "measure", "save", "pause", - "ramp", "ramp_to_zero", + "play", + "measure", + "save", + "pause", + "ramp", + "ramp_to_zero", # Math operations that take QUA variables - "Math.abs", "Math.log", "Math.log2", "Math.log10", "Math.exp", "Math.sqrt", - "Math.pow", "Math.sin", "Math.cos", "Math.tan", "Math.asin", "Math.acos", "Math.atan", - "Math.div", "Math.msb", "Math.sum", "Math.max", "Math.min", - "Cast.to_int", "Cast.to_fixed", "Cast.to_bool", "Cast.unsafe_cast_int", "Cast.unsafe_cast_fixed", + "Math.abs", + "Math.log", + "Math.log2", + "Math.log10", + "Math.exp", + "Math.sqrt", + "Math.pow", + "Math.sin", + "Math.cos", + "Math.tan", + "Math.asin", + "Math.acos", + "Math.atan", + "Math.div", + "Math.msb", + "Math.sum", + "Math.max", + "Math.min", + "Cast.to_int", + "Cast.to_fixed", + "Cast.to_bool", + "Cast.unsafe_cast_int", + "Cast.unsafe_cast_fixed", "Util.cond", # I/O - "set_dc_offset", "get_dc_offset", - "IO1", "IO2", + "set_dc_offset", + "get_dc_offset", + "IO1", + "IO2", # Randomization - "Random.rand_int", "Random.rand_fixed", + "Random.rand_int", + "Random.rand_fixed", } # Also match these as bare function names (without module prefix) QUA_CONTROL_FLOW_BARE_NAMES: Set[str] = { name.split(".")[-1] for name in QUA_CONTROL_FLOW_FUNCTIONS -} | { - name for name in QUA_CONTROL_FLOW_FUNCTIONS if "." not in name -} +} | {name for name in QUA_CONTROL_FLOW_FUNCTIONS if "." not in name} def _is_qua_context_call(node: nodes.Call) -> bool: """Check if a Call node represents a QUA program context manager.""" func = node.func - + if isinstance(func, nodes.Attribute): if func.attrname != "program": return False @@ -118,21 +165,21 @@ def _is_qua_context_call(node: nodes.Call) -> bool: parts.insert(0, current.name) module_path = ".".join(parts) return "qua" in module_path.lower() - + elif isinstance(func, nodes.Name): return func.name == "program" - + return False def _is_qua_control_flow_call(node: nodes.Call) -> bool: """Check if a Call node is a QUA control flow function.""" func = node.func - + # Handle bare function names: if_(condition) if isinstance(func, nodes.Name): return func.name in QUA_CONTROL_FLOW_BARE_NAMES - + # Handle attribute access: qua.if_(condition), Math.abs(x) if isinstance(func, nodes.Attribute): # Build the full call path @@ -143,18 +190,18 @@ def _is_qua_control_flow_call(node: nodes.Call) -> bool: current = current.expr if isinstance(current, nodes.Name): parts.insert(0, current.name) - + full_path = ".".join(parts) - + # Check if it matches any QUA function pattern # Match: Math.abs, Cast.to_int, qua.if_, etc. if full_path in QUA_CONTROL_FLOW_FUNCTIONS: return True - + # Also match if the last part is a QUA function (e.g., qm.qua.if_) if func.attrname in QUA_CONTROL_FLOW_BARE_NAMES: return True - + return False @@ -168,18 +215,18 @@ def _get_call_line_range(node: nodes.Call) -> Tuple[int, int]: def _collect_qua_ranges(module: nodes.Module) -> List[Tuple[int, int]]: """Collect all line ranges inside QUA contexts and QUA control flow calls.""" ranges = [] - + # Collect `with qua.program()` blocks for node in module.nodes_of_class(nodes.With): for context_item, _ in node.items: if isinstance(context_item, nodes.Call) and _is_qua_context_call(context_item): ranges.append((node.lineno, node.end_lineno or node.lineno)) - + # Collect QUA control flow function calls (can be anywhere in the file) for node in module.nodes_of_class(nodes.Call): if _is_qua_control_flow_call(node): ranges.append(_get_call_line_range(node)) - + return ranges @@ -196,8 +243,8 @@ def _in_qua_range(line: int, ranges: List[Tuple[int, int]]) -> bool: def _module_transform(module: nodes.Module) -> nodes.Module: """AST transform that collects QUA ranges before any checking happens.""" global _qua_ranges - filepath = getattr(module, 'file', None) - if filepath and filepath != '': + filepath = getattr(module, "file", None) + if filepath and filepath != "": ranges = _collect_qua_ranges(module) if ranges: _qua_ranges[filepath] = ranges @@ -208,15 +255,15 @@ def register(linter: PyLinter) -> None: """Register the QUA plugin with pylint.""" global _qua_ranges, _transform_registered _qua_ranges = {} - + # Register the AST transform (only once globally) if not _transform_registered: MANAGER.register_transform(nodes.Module, _module_transform) _transform_registered = True - + # Wrap add_message to filter QUA-incompatible messages original_add_message = linter.add_message - + @functools.wraps(original_add_message) def filtered_add_message( msgid: str, @@ -230,14 +277,14 @@ def filtered_add_message( ) -> None: # Get line number check_line = line if line is not None else (node.lineno if node else None) - + # Get filepath - try multiple sources - filepath = getattr(linter, 'current_file', None) + filepath = getattr(linter, "current_file", None) if not filepath and node is not None: # Try to get file from the node's root root = node.root() - filepath = getattr(root, 'file', None) - + filepath = getattr(root, "file", None) + # Check if should suppress if ( check_line is not None @@ -247,13 +294,18 @@ def filtered_add_message( and _in_qua_range(check_line, _qua_ranges[filepath]) ): return # Suppress this message - + return original_add_message( - msgid, line=line, node=node, args=args, - confidence=confidence, col_offset=col_offset, - end_lineno=end_lineno, end_col_offset=end_col_offset, + msgid, + line=line, + node=node, + args=args, + confidence=confidence, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, ) - + linter.add_message = filtered_add_message diff --git a/quam_builder/architecture/nv_center/components/laser.py b/quam_builder/architecture/nv_center/components/laser.py index 021d9a05..9cdfbf03 100644 --- a/quam_builder/architecture/nv_center/components/laser.py +++ b/quam_builder/architecture/nv_center/components/laser.py @@ -3,7 +3,6 @@ from quam.core import QuamComponent, quam_dataclass from quam.components.channels import Channel, SingleChannel - __all__ = ["LaserControl", "LaserLFAnalog", "LaserLFDigital"] diff --git a/quam_builder/architecture/nv_center/components/spcm.py b/quam_builder/architecture/nv_center/components/spcm.py index b1728bb0..7218e49a 100644 --- a/quam_builder/architecture/nv_center/components/spcm.py +++ b/quam_builder/architecture/nv_center/components/spcm.py @@ -1,7 +1,6 @@ from quam.core import quam_dataclass from quam.components.channels import InOutSingleChannel - __all__ = ["SPCM"] diff --git a/quam_builder/architecture/nv_center/components/xy_drive.py b/quam_builder/architecture/nv_center/components/xy_drive.py index 6af81284..4e3a2ed3 100644 --- a/quam_builder/architecture/nv_center/components/xy_drive.py +++ b/quam_builder/architecture/nv_center/components/xy_drive.py @@ -11,7 +11,6 @@ get_output_power_iq_channel, ) - __all__ = ["XYDriveIQ", "XYDriveMW"] @@ -22,9 +21,7 @@ class XYDriveBase: """ @staticmethod - def calculate_voltage_scaling_factor( - fixed_power_dBm: float, target_power_dBm: float - ): + def calculate_voltage_scaling_factor(fixed_power_dBm: float, target_power_dBm: float): """ Calculate the voltage scaling factor required to scale fixed power to target power. @@ -92,9 +89,7 @@ def set_output_power( ValueError: If `gain` or `amplitude` is outside their valid ranges. """ - return set_output_power_iq_channel( - self, power_in_dbm, gain, max_amplitude, Z, operation - ) + return set_output_power_iq_channel(self, power_in_dbm, gain, max_amplitude, Z, operation) @quam_dataclass diff --git a/quam_builder/architecture/nv_center/qpu/base_quam.py b/quam_builder/architecture/nv_center/qpu/base_quam.py index af3b4a0f..313a4593 100644 --- a/quam_builder/architecture/nv_center/qpu/base_quam.py +++ b/quam_builder/architecture/nv_center/qpu/base_quam.py @@ -69,9 +69,7 @@ def get_serialiser(cls) -> JSONSerialiser: This method can be overridden by subclasses to provide a custom serialiser. """ - return JSONSerialiser( - content_mapping={"wiring": "wiring.json", "network": "wiring.json"} - ) + return JSONSerialiser(content_mapping={"wiring": "wiring.json", "network": "wiring.json"}) def get_octave_config(self) -> QmOctaveConfig: """Return the Octave configuration.""" @@ -109,9 +107,7 @@ def calibrate_octave_ports(self, QM: QuantumMachine) -> None: try: self.qubits[name].calibrate_octave(QM) except NoCalibrationElements: - print( - f"No calibration elements found for {name}. Skipping calibration." - ) + print(f"No calibration elements found for {name}. Skipping calibration.") @property def active_qubits(self) -> List[NVCenter]: diff --git a/quam_builder/architecture/nv_center/qubit/nv_center_spin.py b/quam_builder/architecture/nv_center/qubit/nv_center_spin.py index 5038f52b..8ac2a61a 100644 --- a/quam_builder/architecture/nv_center/qubit/nv_center_spin.py +++ b/quam_builder/architecture/nv_center/qubit/nv_center_spin.py @@ -63,9 +63,7 @@ def calibrate_octave( QM: QuantumMachine, calibrate_drive: bool = True, calibrate_resonator: bool = False, - ) -> Tuple[ - Union[None, MixerCalibrationResults], Union[None, MixerCalibrationResults] - ]: + ) -> Tuple[Union[None, MixerCalibrationResults], Union[None, MixerCalibrationResults]]: """Calibrate the Octave channels (xy and resonator) linked to this NV center for the LO frequency, intermediate frequency and Octave gain as defined in the state. @@ -126,9 +124,7 @@ def set_gate_shape(self, gate_shape: str) -> None: f"The gate '{gate}_{gate_shape}' is not part of the existing operations for {self.xy.name} --> {self.xy.operations.keys()}." ) - def readout_counts( - self, state, readout_method: str = "optical", readout_name: str = "readout" - ): + def readout_counts(self, state, readout_method: str = "optical", readout_name: str = "readout"): """ Perform a readout of the qubit state using the specified method. @@ -207,15 +203,11 @@ def reset( if reset_type == "laser": self.reset_qubit_laser(**kwargs) else: - raise NotImplementedError( - f"Reset type {reset_type} is not implemented." - ) + raise NotImplementedError(f"Reset type {reset_type} is not implemented.") else: if log_callable is None: log_callable = getLogger(__name__).warning - log_callable( - "For simulating the QUA program, the qubit reset has been skipped." - ) + log_callable("For simulating the QUA program, the qubit reset has been skipped.") def reset_qubit_laser(self, **kwargs): """ diff --git a/quam_builder/architecture/quantum_dots/README.md b/quam_builder/architecture/quantum_dots/README.md index 22c2f3e3..0b01ad9a 100644 --- a/quam_builder/architecture/quantum_dots/README.md +++ b/quam_builder/architecture/quantum_dots/README.md @@ -3,11 +3,11 @@ ## 1. Introduction -This document first introduces the **GateSet** component with the **VoltageSequence** tool, a python framework for generating QUA sequences to group control of DC gate voltages, particularly useful for spin qubit experiments. +This document first introduces the **GateSet** component with the **VoltageSequence** tool, a python framework for generating QUA sequences to group control of DC gate voltages, particularly useful for spin qubit experiments. The components `GateSet` and `VoltageSequence` enable precise physical voltage control, essential for quantum dot operations and forming a basis for `VirtualGateSet`. -Spin qubit experiments are encouraged to use the **VoltageGate** QuAM channel. A `VoltageGate` channel is a Quantum Dot specific channel inheriting from QuAM's `SingleChannel` object. It adds to the `SingleChannel` by containing an `offset_parameter` and an `attenuation` value. +Spin qubit experiments are encouraged to use the **VoltageGate** QuAM channel. A `VoltageGate` channel is a Quantum Dot specific channel inheriting from QuAM's `SingleChannel` object. It adds to the `SingleChannel` by containing an `offset_parameter` and an `attenuation` value. Subsequently, this document introduces the **VirtualGateSet** and **VirtualizationLayer** components. These extend the physical gate control capabilities provided by `GateSet` and `VoltageSequence` by adding one or more layers of virtual gates. @@ -33,9 +33,9 @@ The `VirtualGateSet` framework provides the necessary tools to implement these a #### 2.1.1 VoltageGate -`VoltageGate` is a QuAM channel built specifically to handle quantum dot and spin qubit experiments. It inherits from `SingleChannel`, adding an `offset_parameter` and `attenuation` values. +`VoltageGate` is a QuAM channel built specifically to handle quantum dot and spin qubit experiments. It inherits from `SingleChannel`, adding an `offset_parameter` and `attenuation` values. -- `offset_parameter` is built to work with external voltage sources (and external drivers) in mind. For example, in the case that the external voltage is provided by the QM QDAC-II, one can use the QCoDeS driver as follows: +- `offset_parameter` is built to work with external voltage sources (and external drivers) in mind. For example, in the case that the external voltage is provided by the QM QDAC-II, one can use the QCoDeS driver as follows: ```python channel_p1 = VoltageGate(...) @@ -68,10 +68,10 @@ Represents a single linear transformation (matrix) from a set of source (virtual #### 1. Define QUAM `VoltageGate` objects for physical gates -- Below is an example of how a `VoltageGate` is instantiated. As appropriate, add `offset_parameter` and `attenuation` arguments. +- Below is an example of how a `VoltageGate` is instantiated. As appropriate, add `offset_parameter` and `attenuation` arguments. -- In order for `GateSet`, `VirtualGateSet` and `VoltageSequence` to function properly, the channels **must** be instantiated as **sticky** elements. - - This means that any applied offset is maintained. +- In order for `GateSet`, `VirtualGateSet` and `VoltageSequence` to function properly, the channels **must** be instantiated as **sticky** elements. + - This means that any applied offset is maintained. - Any `sequence` in the `GateSet`/`VirtualGateSet`, as well as core functionalities such as `ramp_to_zero`, rely on sticky elements. This is a core requirement. ```python @@ -107,7 +107,7 @@ Represents a single linear transformation (matrix) from a set of source (virtual - When creating this mapping, it is important to ensure that the string names used here match the string names in your QuAM machine. -- If your channel object are already parented by a QuAM machine (i.e. `machine.channel["channel_p1"] = VoltageGate(...)`), then the channels cannot be re-parented into your GateSet. In this case, it is important to use the channel reference as such: +- If your channel object are already parented by a QuAM machine (i.e. `machine.channel["channel_p1"] = VoltageGate(...)`), then the channels cannot be re-parented into your GateSet. In this case, it is important to use the channel reference as such: ```python channels = { @@ -119,15 +119,15 @@ Represents a single linear transformation (matrix) from a set of source (virtual #### 3. Instantiate your GateSet with your channel mapping -- Below shows an example of instantiating your `GateSet`, for basic group control of `VoltageGate` channels. +- Below shows an example of instantiating your `GateSet`, for basic group control of `VoltageGate` channels. - ```python + ```python from quam_builder.architecture.quantum_dots.components import GateSet my_gate_set = GateSet(id="dot_plungers", channels=channels) ``` -- If virtual gates are necessary in your setup, use the `VirtualGateSet` instead. The instantiation of `VirtualGateSet` is identical to the `GateSet`. +- If virtual gates are necessary in your setup, use the `VirtualGateSet` instead. The instantiation of `VirtualGateSet` is identical to the `GateSet`. ```python from quam_builder.architecture.quantum_dots.components import VirtualGateSet @@ -136,9 +136,9 @@ Represents a single linear transformation (matrix) from a set of source (virtual ``` -##### 3.1 (Optional) Add Virtualization Layers +##### 3.1 (Optional) Add Virtualization Layers -- If you are using the `VirtualGateSet`, you can map virtualization layers onto your existing physical or virtual gates using the `.add_layer()` method. You must name the new virtual `source_gates` and input a transformation matrix. This does not need to map onto all of your existing physical or virtual gates. +- If you are using the `VirtualGateSet`, you can map virtualization layers onto your existing physical or virtual gates using the `.add_layer()` method. You must name the new virtual `source_gates` and input a transformation matrix. This does not need to map onto all of your existing physical or virtual gates. ```python # Add coarse tuning layer (virtual gates for overall dot positions) @@ -157,16 +157,16 @@ Represents a single linear transformation (matrix) from a set of source (virtual ``` #### 4. Add `VoltageTuningPoint` macros to the `GateSet` or `VirtualGateSet` - + - This is useful for when you have set points in your charge-stability that must be re-used in the experiment. GateSet can hold VoltageTuningPoints which can easily be accessed by VoltageSequence ```python my_gate_set.add_point(name="idle", voltages={"channel_P1": 0.1, "channel_P2": -0.05}, duration=1000) ``` - + - Internally this adds a **`VoltageTuningPoint` to GateSet.macros** -- This is not unique to `GateSet`, or indeed physical gates. `VirtualGateSet` is capable of holding virtual tuning points. The input dictionary mapping can contain any combination of physical or virtual gates, from any layer in the `VirtualGateSet`. The exact mechanism with which the output voltage is calculated is covered later. +- This is not unique to `GateSet`, or indeed physical gates. `VirtualGateSet` is capable of holding virtual tuning points. The input dictionary mapping can contain any combination of physical or virtual gates, from any layer in the `VirtualGateSet`. The exact mechanism with which the output voltage is calculated is covered later. ```python my_virtual_gate_set.add_point(name="idle", voltages={"v_FineTune1": 0.1, "v_Coarse2": -0.05}, duration=1000) @@ -178,15 +178,15 @@ Represents a single linear transformation (matrix) from a set of source (virtual - `voltage_seq` in the below example can be used in QUA programs to easily step/ramp to points defined as macros in your `GateSet` or `VirtualGateSet` - ```python - with program() as basic_control: + ```python + with program() as basic_control: voltage_seq = my_gate_set.new_sequence() ``` -- Or, if using the `VirtualGateSet`, +- Or, if using the `VirtualGateSet`, - ```python - with program() as complex_control: + ```python + with program() as complex_control: voltage_seq = my_virtual_gate_set.new_sequence() ``` @@ -194,7 +194,7 @@ Represents a single linear transformation (matrix) from a set of source (virtual - Instantiate your new sequence in the QUA programme, and step/ramp to any point. -- For a basic `GateSet`, +- For a basic `GateSet`, ```python with qua.program() as basic_control: @@ -248,9 +248,9 @@ Represents a single linear transformation (matrix) from a set of source (virtual ## 3. `GateSet` -A `GateSet` is a higher-level abstraction that collects a group of `VoltageGate` or `SingleChannel` channels and treats them as a single, coordinated object for unified control. This is especially useful in Quantum Dot architectures, where one often has many physical gate electrodes that must be tuned together. Instead of controlling each `VoltageGate`/`SingleChannel` channel in isolation, the GateSet allows +A `GateSet` is a higher-level abstraction that collects a group of `VoltageGate` or `SingleChannel` channels and treats them as a single, coordinated object for unified control. This is especially useful in Quantum Dot architectures, where one often has many physical gate electrodes that must be tuned together. Instead of controlling each `VoltageGate`/`SingleChannel` channel in isolation, the GateSet allows -- Unified control: Iterate, configure, and programme multiple gates at once through a single object +- Unified control: Iterate, configure, and programme multiple gates at once through a single object - Pre-defined DC points: Store named DC working points using add_point(...) @@ -267,7 +267,7 @@ A `GateSet` is a higher-level abstraction that collects a group of `VoltageGate` ```python # Assume gate_set has channels: {"P1": channel_P1, "P2": channel_P2, "B1": channel_B1} - # Only specify the voltages of a partial subset of all the gates in the GateSet. + # Only specify the voltages of a partial subset of all the gates in the GateSet. partial_voltages = {"P1": 0.3, "B1": -0.1} # resolve_voltages fills in missing channels, creating a complete voltages dict internally by replacing all the un-named gate voltages with 0.0V @@ -306,26 +306,26 @@ Implication: virtual gates do not maintain state across calls. To preserve a vir **Creating a `VoltageSequence`:** -- The sequence must be defined within a QUA program. +- The sequence must be defined within a QUA program. **Core Methods (used in `qua.program()` context):** -- `step_to_voltages(voltages: Dict[str, float], duration: int)` +- `step_to_voltages(voltages: Dict[str, float], duration: int)` Steps all specified channels directly to the given voltage levels and holds them for the specified duration (in nanoseconds). This creates immediate voltage changes without ramping. Both `voltages` values and `duration` can be QUA variables for dynamic control. ```python voltage_seq.step_to_voltages(voltages={"P1": 0.3, "P2": 0.1}, duration=1000) ``` -- `ramp_to_voltages(voltages: Dict[str, float], duration: int, ramp_duration: int)` +- `ramp_to_voltages(voltages: Dict[str, float], duration: int, ramp_duration: int)` Ramps all specified channels to the given voltage levels over the specified ramp duration, then holds them for the duration (both in nanoseconds). This provides smooth voltage transitions useful for avoiding voltage spikes that could affect sensitive quantum systems. All parameters can be QUA variables. ```python voltage_seq.ramp_to_voltages(voltages={"P1": 0.0}, duration=500, ramp_duration=40) ``` -- `step_to_point(name: str, duration: Optional[int] = None)` +- `step_to_point(name: str, duration: Optional[int] = None)` Steps all channels to the voltages defined in a predefined `VoltageTuningPoint` macro. If no duration is provided, uses the default duration from the tuning point definition. This enables quick transitions to well-defined system states. The `duration` parameter can be a QUA variable. ```python @@ -333,14 +333,14 @@ Implication: virtual gates do not maintain state across calls. To preserve a vir voltage_seq.step_to_point("readout", duration=2000) # Override default duration ``` -- `ramp_to_point(name: str, ramp_duration: int, duration: Optional[int] = None)` +- `ramp_to_point(name: str, ramp_duration: int, duration: Optional[int] = None)` Ramps all channels to the voltages defined in a predefined `VoltageTuningPoint` over the specified ramp duration, then holds them. Combines the smooth transitions of ramping with the convenience of predefined voltage states. Both `ramp_duration` and `duration` can be QUA variables. ```python voltage_seq.ramp_to_point("idle", ramp_duration=50, duration=1000) ``` -- `ramp_to_zero(ramp_duration: Optional[int] = None)` +- `ramp_to_zero(ramp_duration: Optional[int] = None)` Ramps the voltage on all channels in the GateSet to zero and resets the integrated voltage tracking for each channel. If no duration is specified, uses QUA's built-in `ramp_to_zero` command for immediate ramping. Essential for safely returning to a neutral state. The `ramp_duration` parameter can be a QUA variable. ```python @@ -348,7 +348,7 @@ Implication: virtual gates do not maintain state across calls. To preserve a vir voltage_seq.ramp_to_zero(ramp_duration=100) # Controlled ramp over 100ns ``` -- `apply_compensation_pulse(max_voltage: float = 0.49)` +- `apply_compensation_pulse(max_voltage: float = 0.49)` Applies a compensation pulse to each channel to counteract integrated voltage drift when tracking is enabled. The compensation amplitude is calculated based on the accumulated integrated voltage, with the pulse duration optimized to stay within the specified maximum voltage limit. Only available when `track_integrated_voltage=True`. ```python @@ -574,8 +574,8 @@ To validate a rectangular virtualization layer: ```python from quam.components import ( - BasicQuam, - StickyChannelAddon, + BasicQuam, + StickyChannelAddon, pulses ) from quam_builder.architecture.quantum_dots.components import VoltageGate @@ -640,7 +640,7 @@ machine.virtual_gate_set.add_point("meas", {"ch3": -0.12}, duration = 3_000) ### 8.5. Write QUA program ```python -with program() as prog: +with program() as prog: my_new_seq = machine.virtual_gate_set.new_sequence(track_integrated_voltage=True) my_new_seq.step_to_point("init") # also valid: my_new_seq.step_to_voltages(voltages = {"ch1": -0.25, "ch3": 0.12}, duration = 10_000) my_new_seq.step_to_point("op") @@ -648,8 +648,8 @@ with program() as prog: ``` **What is happening here?** -- In `init`, the input dict is `{"ch1": -0.25, "ch3": 0.12}`. Since `ch2` is omitted in this layer, this will internally translate to a full dict of `{"ch1": -0.25, "ch2": 0.0, "ch3": 0.12}`. +- In `init`, the input dict is `{"ch1": -0.25, "ch3": 0.12}`. Since `ch2` is omitted in this layer, this will internally translate to a full dict of `{"ch1": -0.25, "ch2": 0.0, "ch3": 0.12}`. - In `op`, the input dict is comprised of virtual gates `{"V1": 0.2, "V2": 0.1}`. `ch3` is absent, and since `V1` and `V2` map only to `ch1` and `ch2`, `ch3` is interpreted as having an input 0.0, to produce a dict of `{"V1": 0.2, "V2": 0.1, "ch3": 0.0}`. Internally, the physical gate voltages are calculated using the inverse of the virtual gate matrix, to a physical gate dict of `{"ch1": 0.05, "ch2": 0.1, "ch3": 0.0}`. Bear in mind that these voltages are absolute, not relative, despite the sticky elements. -- In `meas`, the input dict is simply `{"ch3": -0.12}`, which is interpreted as `{"ch1": 0.0, "ch2": 0.0, "ch3": -0.12}`. +- In `meas`, the input dict is simply `{"ch3": -0.12}`, which is interpreted as `{"ch1": 0.0, "ch2": 0.0, "ch3": -0.12}`. diff --git a/quam_builder/architecture/quantum_dots/__init__.py b/quam_builder/architecture/quantum_dots/__init__.py index 4a9c67dd..0f111d48 100644 --- a/quam_builder/architecture/quantum_dots/__init__.py +++ b/quam_builder/architecture/quantum_dots/__init__.py @@ -18,4 +18,4 @@ *qpu.__all__, *qubit.__all__, *qubit_pair.__all__, -] \ No newline at end of file +] diff --git a/quam_builder/architecture/quantum_dots/components/quantum_dot.py b/quam_builder/architecture/quantum_dots/components/quantum_dot.py index d93e8f6d..1b8c6eb7 100644 --- a/quam_builder/architecture/quantum_dots/components/quantum_dot.py +++ b/quam_builder/architecture/quantum_dots/components/quantum_dot.py @@ -79,11 +79,14 @@ def _update_current_voltage(self, voltage: float): def get_offset(self): v = getattr(self.physical_channel, "offset_parameter", None) - return float(v()) if callable(v) else 0.0 + if callable(v): + return 0.0 + else: + return float(v) def set_offset(self, value: float): if self.physical_channel.offset_parameter is not None: - self.physical_channel.offset_parameter(value) + self.physical_channel.offset_parameter(value) # pylint: disable=not-callable return raise ValueError("External offset source not connected") diff --git a/quam_builder/architecture/quantum_dots/components/readout_resonator.py b/quam_builder/architecture/quantum_dots/components/readout_resonator.py index 2dfa3248..0d8e5ef1 100644 --- a/quam_builder/architecture/quantum_dots/components/readout_resonator.py +++ b/quam_builder/architecture/quantum_dots/components/readout_resonator.py @@ -11,7 +11,6 @@ get_output_power_iq_channel, ) - __all__ = [ "ReadoutResonatorBase", "ReadoutResonatorIQ", diff --git a/quam_builder/architecture/quantum_dots/components/readout_transport.py b/quam_builder/architecture/quantum_dots/components/readout_transport.py index b5f70c50..5dde6e01 100644 --- a/quam_builder/architecture/quantum_dots/components/readout_transport.py +++ b/quam_builder/architecture/quantum_dots/components/readout_transport.py @@ -3,7 +3,6 @@ from quam.core import quam_dataclass from quam.components.channels import InSingleChannel, InOutSingleChannel, InMWChannel, InIQChannel - __all__ = ["ReadoutTransportBase", "ReadoutTransportSingle", "ReadoutTransportSingleIO"] @@ -15,6 +14,7 @@ class ReadoutTransportBase: # pylint: disable=too-few-public-methods pass + @quam_dataclass class ReadoutTransportSingle( InSingleChannel, ReadoutTransportBase @@ -25,6 +25,7 @@ class ReadoutTransportSingle( pass + @quam_dataclass class ReadoutTransportSingleIO( InOutSingleChannel, ReadoutTransportBase diff --git a/quam_builder/architecture/quantum_dots/components/virtual_dc_set.py b/quam_builder/architecture/quantum_dots/components/virtual_dc_set.py index 8cabcd5d..dc8f84af 100644 --- a/quam_builder/architecture/quantum_dots/components/virtual_dc_set.py +++ b/quam_builder/architecture/quantum_dots/components/virtual_dc_set.py @@ -294,7 +294,7 @@ def add_layer( self._current_levels.update(all_voltages) return virtualization_layer - def add_to_layer( + def add_to_layer( # pylint: disable=too-many-statements self, layer_id: str, source_gates: List[str], diff --git a/quam_builder/architecture/quantum_dots/components/virtual_gate_set.py b/quam_builder/architecture/quantum_dots/components/virtual_gate_set.py index 0fbe9f40..32a61e41 100644 --- a/quam_builder/architecture/quantum_dots/components/virtual_gate_set.py +++ b/quam_builder/architecture/quantum_dots/components/virtual_gate_set.py @@ -12,7 +12,6 @@ from .gate_set import GateSet from quam_builder.tools.qua_tools import VoltageLevelType - __all__ = ["VirtualGateSet", "VirtualizationLayer"] diff --git a/quam_builder/architecture/quantum_dots/examples/__init__.py b/quam_builder/architecture/quantum_dots/examples/__init__.py index 9c5dd100..5dbe3935 100644 --- a/quam_builder/architecture/quantum_dots/examples/__init__.py +++ b/quam_builder/architecture/quantum_dots/examples/__init__.py @@ -2,4 +2,4 @@ __all__ = [ *macro_examples.__all__, -] \ No newline at end of file +] diff --git a/quam_builder/architecture/quantum_dots/examples/init_circuit.py b/quam_builder/architecture/quantum_dots/examples/init_circuit.py index 085c8b9d..ba901433 100644 --- a/quam_builder/architecture/quantum_dots/examples/init_circuit.py +++ b/quam_builder/architecture/quantum_dots/examples/init_circuit.py @@ -35,7 +35,6 @@ from quam_qd_generator_example import machine - # ============================================================================ # Configuration Parameters # ============================================================================ diff --git a/quam_builder/architecture/quantum_dots/examples/macro_examples.py b/quam_builder/architecture/quantum_dots/examples/macro_examples.py index 72c8eba2..b5ecc496 100644 --- a/quam_builder/architecture/quantum_dots/examples/macro_examples.py +++ b/quam_builder/architecture/quantum_dots/examples/macro_examples.py @@ -25,7 +25,6 @@ from quam_builder.architecture.quantum_dots.components.mixins import VoltageMacroMixin - # ============================================================================ # Example Component Setup # ============================================================================ diff --git a/quam_builder/architecture/quantum_dots/examples/mwe_sensor_resonator_same_port.py b/quam_builder/architecture/quantum_dots/examples/mwe_sensor_resonator_same_port.py index 73123e49..efa83a2c 100644 --- a/quam_builder/architecture/quantum_dots/examples/mwe_sensor_resonator_same_port.py +++ b/quam_builder/architecture/quantum_dots/examples/mwe_sensor_resonator_same_port.py @@ -1,7 +1,6 @@ from qualang_tools.wirer import Connectivity, Instruments, allocate_wiring from qualang_tools.wirer.wirer.channel_specs import lf_fem_spec - SENSOR_DOTS = [1, 2, 3] QUANTUM_DOTS = [1, 2, 3] QUANTUM_DOT_PAIRS = list(zip(QUANTUM_DOTS, QUANTUM_DOTS[1:])) @@ -70,7 +69,9 @@ def case_2_resonator_plus_sensor_gate() -> None: shared_line=False, constraints=S1_RESONATOR_CONSTRAINTS, ) - connectivity.add_sensor_dot_voltage_gate_lines([SENSOR_DOTS[0]], constraints=SENSOR_GATE_CONSTRAINTS) + connectivity.add_sensor_dot_voltage_gate_lines( + [SENSOR_DOTS[0]], constraints=SENSOR_GATE_CONSTRAINTS + ) _run_allocation(connectivity, "resonator + sensor gate voltage line") @@ -103,7 +104,9 @@ def case_4_add_quantum_dot_plungers() -> None: constraints=S2TO3_RESONATOR_CONSTRAINTS, ) connectivity.add_sensor_dot_voltage_gate_lines(SENSOR_DOTS, constraints=SENSOR_GATE_CONSTRAINTS) - connectivity.add_quantum_dot_voltage_gate_lines(QUANTUM_DOTS, constraints=SENSOR_GATE_CONSTRAINTS) + connectivity.add_quantum_dot_voltage_gate_lines( + QUANTUM_DOTS, constraints=SENSOR_GATE_CONSTRAINTS + ) _run_allocation(connectivity, "add quantum dot plunger gates") @@ -120,7 +123,9 @@ def case_5_add_barriers_unconstrained() -> None: constraints=S2TO3_RESONATOR_CONSTRAINTS, ) connectivity.add_sensor_dot_voltage_gate_lines(SENSOR_DOTS, constraints=SENSOR_GATE_CONSTRAINTS) - connectivity.add_quantum_dot_voltage_gate_lines(QUANTUM_DOTS, constraints=SENSOR_GATE_CONSTRAINTS) + connectivity.add_quantum_dot_voltage_gate_lines( + QUANTUM_DOTS, constraints=SENSOR_GATE_CONSTRAINTS + ) connectivity.add_quantum_dot_pairs(QUANTUM_DOT_PAIRS) _run_allocation(connectivity, "add barrier gates (unconstrained)") @@ -138,7 +143,9 @@ def case_6_add_barriers_constrained_unused_slot() -> None: constraints=S2TO3_RESONATOR_CONSTRAINTS, ) connectivity.add_sensor_dot_voltage_gate_lines(SENSOR_DOTS, constraints=SENSOR_GATE_CONSTRAINTS) - connectivity.add_quantum_dot_voltage_gate_lines(QUANTUM_DOTS, constraints=SENSOR_GATE_CONSTRAINTS) + connectivity.add_quantum_dot_voltage_gate_lines( + QUANTUM_DOTS, constraints=SENSOR_GATE_CONSTRAINTS + ) connectivity.add_quantum_dot_pairs([QUANTUM_DOT_PAIRS[0]], constraints=BAR_PAIR_1_CONSTRAINTS) connectivity.add_quantum_dot_pairs([QUANTUM_DOT_PAIRS[1]], constraints=BAR_PAIR_2_CONSTRAINTS) _run_allocation(connectivity, "add barrier gates (constrained to free slot)") diff --git a/quam_builder/architecture/quantum_dots/examples/port_constraints_bug_example.py b/quam_builder/architecture/quantum_dots/examples/port_constraints_bug_example.py index a45b17a5..f2decc4c 100644 --- a/quam_builder/architecture/quantum_dots/examples/port_constraints_bug_example.py +++ b/quam_builder/architecture/quantum_dots/examples/port_constraints_bug_example.py @@ -4,7 +4,6 @@ from qualang_tools.wirer import Connectivity, Instruments, allocate_wiring from qualang_tools.wirer.wirer.channel_specs import lf_fem_spec - EXAMPLES_DIR = Path(__file__).resolve().parent os.environ.setdefault("QUAM_STATE_PATH", str(EXAMPLES_DIR / "quam_state")) diff --git a/quam_builder/architecture/quantum_dots/examples/quam_ld_example.py b/quam_builder/architecture/quantum_dots/examples/quam_ld_example.py index 99d1e793..215fb660 100644 --- a/quam_builder/architecture/quantum_dots/examples/quam_ld_example.py +++ b/quam_builder/architecture/quantum_dots/examples/quam_ld_example.py @@ -26,7 +26,6 @@ from quam_builder.architecture.quantum_dots.qpu import LossDiVincenzoQuam from qm.qua import * - machine = LossDiVincenzoQuam.load() lf_fem = 6 diff --git a/quam_builder/architecture/quantum_dots/examples/quam_qd_example.py b/quam_builder/architecture/quantum_dots/examples/quam_qd_example.py index 26140a85..b0bf3f6c 100644 --- a/quam_builder/architecture/quantum_dots/examples/quam_qd_example.py +++ b/quam_builder/architecture/quantum_dots/examples/quam_qd_example.py @@ -58,7 +58,6 @@ from quam_builder.architecture.quantum_dots.components.reservoir import DrainSingle from qm.qua import * - # Instantiate Quam machine = BaseQuamQD() lf_fem = 6 diff --git a/quam_builder/architecture/quantum_dots/examples/rabi_chevron.py b/quam_builder/architecture/quantum_dots/examples/rabi_chevron.py index 30dd6915..dd9adf18 100644 --- a/quam_builder/architecture/quantum_dots/examples/rabi_chevron.py +++ b/quam_builder/architecture/quantum_dots/examples/rabi_chevron.py @@ -63,7 +63,6 @@ ) from quam_builder.architecture.quantum_dots.qubit import LDQubit - # ============================================================================= # SECTION 1: Create Machine and Physical Channels # ============================================================================= diff --git a/quam_builder/architecture/quantum_dots/examples/rabi_chevron_transport.py b/quam_builder/architecture/quantum_dots/examples/rabi_chevron_transport.py index 98df3370..42915f46 100644 --- a/quam_builder/architecture/quantum_dots/examples/rabi_chevron_transport.py +++ b/quam_builder/architecture/quantum_dots/examples/rabi_chevron_transport.py @@ -75,7 +75,6 @@ ) from quam_builder.architecture.quantum_dots.qubit import LDQubit - # ============================================================================= # SECTION 1: Create Machine and Physical Channels # ============================================================================= diff --git a/quam_builder/architecture/quantum_dots/examples/virtual_dc_set_example.py b/quam_builder/architecture/quantum_dots/examples/virtual_dc_set_example.py index 638c78b0..71574da4 100644 --- a/quam_builder/architecture/quantum_dots/examples/virtual_dc_set_example.py +++ b/quam_builder/architecture/quantum_dots/examples/virtual_dc_set_example.py @@ -1,41 +1,38 @@ """ -This is an example script on how to instantiate a QPU which contains Loss-DiVincenzo qubits, with other barrier gates and sensor dots. +This is an example script on how to instantiate a QPU which contains Loss-DiVincenzo qubits, with other barrier gates and sensor dots. -Workflow: +Workflow: -1. Instantiate your machine. +1. Instantiate your machine. -2. Instantiate the base hardware channels for the machine. +2. Instantiate the base hardware channels for the machine. - In this example, arbitrary HW gates are created as VoltageGates. For QuantumDots and SensorDots, the base channel must be VoltageGate and sticky. They are instantiated in a mapping dictionary to be input into the machine -3. Create your VirtualGateSet. You do not need to manually add all the channels, the function create_virtual_gate_set should do it automatically. - Ensure that the mapping of the desired virtual gate to the relevant HW channel is correct, as the QuantumDot names will be extracted from this input dict. +3. Create your VirtualGateSet. You do not need to manually add all the channels, the function create_virtual_gate_set should do it automatically. + Ensure that the mapping of the desired virtual gate to the relevant HW channel is correct, as the QuantumDot names will be extracted from this input dict. -4. Register your components. - - Register the relevant QuantumDots, SensorDots and BarrierGates, mapped correctly to the relevant output channel. As long as the channel is correctly mapped, +4. Register your components. + - Register the relevant QuantumDots, SensorDots and BarrierGates, mapped correctly to the relevant output channel. As long as the channel is correctly mapped, the name of the element will be made consistent to that in the VirtualGateSet 5. Create your QUA programme - - For simultaneous stepping/ramping, use either + - For simultaneous stepping/ramping, use either sequence = machine.voltage_sequences[gate_set_id] sequence.step_to_voltages({"virtual_dot_1": ..., "virtual_dot_2": ...}) - or use sequence.simultaneous: - with sequence.simultaneous(duration = ...): + or use sequence.simultaneous: + with sequence.simultaneous(duration = ...): machine.qubits["virtual_dot_1"].step_to_voltages(...) machine.qubits["virtual_dot_2"].step_to_voltages(...) """ -from quam.components import ( - StickyChannelAddon, - pulses -) +from quam.components import StickyChannelAddon, pulses from quam.components.ports import ( - LFFEMAnalogOutputPort, + LFFEMAnalogOutputPort, LFFEMAnalogInputPort, MWFEMAnalogOutputPort, - MWFEMAnalogInputPort + MWFEMAnalogInputPort, ) from quam_builder.architecture.quantum_dots.components import VoltageGate @@ -45,28 +42,68 @@ from quam_builder.architecture.quantum_dots.components import ReadoutResonatorSingle from qm.qua import * - ########################################### ###### Instantiate Physical Channels ###### ########################################### from qcodes import Instrument from qcodes_contrib_drivers.drivers.QDevil.QDAC2 import QDac2 + qdac_ip = "172.16.33.101" lf_fem = 5 name = "QDAC" try: qdac = Instrument.find_instrument(name) except KeyError: - qdac = QDac2(name, visalib='@py', address=f'TCPIP::{qdac_ip}::5025::SOCKET') + qdac = QDac2(name, visalib="@py", address=f"TCPIP::{qdac_ip}::5025::SOCKET") -p1 = VoltageGate(id = f"plunger_1", opx_output = LFFEMAnalogOutputPort("con1", lf_fem, port_id = 1), qdac_channel = 1, sticky = StickyChannelAddon(duration = 16, digital = False)) -p2 = VoltageGate(id = f"plunger_2", opx_output = LFFEMAnalogOutputPort("con1", lf_fem, port_id = 2), qdac_channel = 2, sticky = StickyChannelAddon(duration = 16, digital = False)) -p3 = VoltageGate(id = f"plunger_3", opx_output = LFFEMAnalogOutputPort("con1", lf_fem, port_id = 3), qdac_channel = 3, sticky = StickyChannelAddon(duration = 16, digital = False)) -p4 = VoltageGate(id = f"plunger_4", opx_output = LFFEMAnalogOutputPort("con1", lf_fem, port_id = 4), qdac_channel = 4, sticky = StickyChannelAddon(duration = 16, digital = False)) -b1 = VoltageGate(id = f"barrier_1", opx_output = LFFEMAnalogOutputPort("con1", lf_fem, port_id = 5), qdac_channel = 5, sticky = StickyChannelAddon(duration = 16, digital = False)) -b2 = VoltageGate(id = f"barrier_2", opx_output = LFFEMAnalogOutputPort("con1", lf_fem, port_id = 6), qdac_channel = 6, sticky = StickyChannelAddon(duration = 16, digital = False)) -b3 = VoltageGate(id = f"barrier_3", opx_output = LFFEMAnalogOutputPort("con1", lf_fem, port_id = 7), qdac_channel = 7, sticky = StickyChannelAddon(duration = 16, digital = False)) -s1 = VoltageGate(id = f"sensor_DC", opx_output = LFFEMAnalogOutputPort("con1", lf_fem, port_id = 8), qdac_channel = 8, sticky = StickyChannelAddon(duration = 16, digital = False)) +p1 = VoltageGate( + id=f"plunger_1", + opx_output=LFFEMAnalogOutputPort("con1", lf_fem, port_id=1), + qdac_channel=1, + sticky=StickyChannelAddon(duration=16, digital=False), +) +p2 = VoltageGate( + id=f"plunger_2", + opx_output=LFFEMAnalogOutputPort("con1", lf_fem, port_id=2), + qdac_channel=2, + sticky=StickyChannelAddon(duration=16, digital=False), +) +p3 = VoltageGate( + id=f"plunger_3", + opx_output=LFFEMAnalogOutputPort("con1", lf_fem, port_id=3), + qdac_channel=3, + sticky=StickyChannelAddon(duration=16, digital=False), +) +p4 = VoltageGate( + id=f"plunger_4", + opx_output=LFFEMAnalogOutputPort("con1", lf_fem, port_id=4), + qdac_channel=4, + sticky=StickyChannelAddon(duration=16, digital=False), +) +b1 = VoltageGate( + id=f"barrier_1", + opx_output=LFFEMAnalogOutputPort("con1", lf_fem, port_id=5), + qdac_channel=5, + sticky=StickyChannelAddon(duration=16, digital=False), +) +b2 = VoltageGate( + id=f"barrier_2", + opx_output=LFFEMAnalogOutputPort("con1", lf_fem, port_id=6), + qdac_channel=6, + sticky=StickyChannelAddon(duration=16, digital=False), +) +b3 = VoltageGate( + id=f"barrier_3", + opx_output=LFFEMAnalogOutputPort("con1", lf_fem, port_id=7), + qdac_channel=7, + sticky=StickyChannelAddon(duration=16, digital=False), +) +s1 = VoltageGate( + id=f"sensor_DC", + opx_output=LFFEMAnalogOutputPort("con1", lf_fem, port_id=8), + qdac_channel=8, + sticky=StickyChannelAddon(duration=16, digital=False), +) p1.offset_parameter = qdac.channel(p1.qdac_channel).dc_constant_V p2.offset_parameter = qdac.channel(p2.qdac_channel).dc_constant_V @@ -81,47 +118,47 @@ from quam_builder.architecture.quantum_dots.components import VirtualDCSet virtual_dc_set = VirtualDCSet( - id = "Dots DC", - channels = { - "plunger_1": p1, - "plunger_2": p2, - "plunger_3": p3, - "plunger_4": p4, - "barrier_1": b1, - "barrier_2": b2, - "barrier_3": b3, - "sensor_DC": s1 - } + id="Dots DC", + channels={ + "plunger_1": p1, + "plunger_2": p2, + "plunger_3": p3, + "plunger_4": p4, + "barrier_1": b1, + "barrier_2": b2, + "barrier_3": b3, + "sensor_DC": s1, + }, ) virtual_dc_set.add_layer( - layer_id = "cross_compensation", - source_gates = ["VP1", "VP2", "VP3", "VP4"], - target_gates = ["plunger_1", "plunger_2", "plunger_3", "plunger_4"], - matrix = [ - [1, 0.2, 0, 0.3], - [0.5, 1, 0.7, 0], - [0.3, 0, 1, 0.6], + layer_id="cross_compensation", + source_gates=["VP1", "VP2", "VP3", "VP4"], + target_gates=["plunger_1", "plunger_2", "plunger_3", "plunger_4"], + matrix=[ + [1, 0.2, 0, 0.3], + [0.5, 1, 0.7, 0], + [0.3, 0, 1, 0.6], [0, 0.1, 0.3, 1], - ] + ], ) virtual_dc_set.add_layer( - layer_id = "detuning_1", - source_gates = ["det_1", "det_2"], - target_gates = ["VP1", "VP2"], - matrix = [ - [0.8, 0.5], - [0.3, 0.7], - ] + layer_id="detuning_1", + source_gates=["det_1", "det_2"], + target_gates=["VP1", "VP2"], + matrix=[ + [0.8, 0.5], + [0.3, 0.7], + ], ) virtual_dc_set.add_layer( - layer_id = "detuning_2", - source_gates = ["det_3", "det_4"], - target_gates = ["VP3", "VP4"], - matrix = [ - [0.7, 0.4], - [0.6, 0.9], - ] + layer_id="detuning_2", + source_gates=["det_3", "det_4"], + target_gates=["VP3", "VP4"], + matrix=[ + [0.7, 0.4], + [0.6, 0.9], + ], ) diff --git a/quam_builder/architecture/quantum_dots/examples/virtual_gate_set_example.py b/quam_builder/architecture/quantum_dots/examples/virtual_gate_set_example.py index 93d1e2b2..5ea8ce65 100644 --- a/quam_builder/architecture/quantum_dots/examples/virtual_gate_set_example.py +++ b/quam_builder/architecture/quantum_dots/examples/virtual_gate_set_example.py @@ -1,4 +1,3 @@ - import numpy as np import matplotlib @@ -9,13 +8,14 @@ VirtualGateSet, ) + def _channels(names): return { - name: SingleChannel(id=name, opx_output=("con", idx + 1)) - for idx, name in enumerate(names) + name: SingleChannel(id=name, opx_output=("con", idx + 1)) for idx, name in enumerate(names) } -matplotlib.use('TkAgg') + +matplotlib.use("TkAgg") gate_set = VirtualGateSet( id="rectangular_roundtrip", channels=_channels(["P1", "P2", "P3"]), @@ -52,9 +52,7 @@ def _channels(names): resolved = gate_set.resolve_voltages( {gate: value for gate, value in zip(source_gates, source_vector)} ) - resolved_samples.append( - np.array([resolved["P1"], resolved["P2"], resolved["P3"]]) - ) + resolved_samples.append(np.array([resolved["P1"], resolved["P2"], resolved["P3"]])) resolved_samples = np.array(resolved_samples) np.testing.assert_allclose(resolved_samples, physical_samples, rtol=1e-9, atol=1e-9) @@ -128,4 +126,4 @@ def _channels(names): ax2.set_title("Tall matrix resolution check") ax2.legend() fig2.tight_layout() -plt.show() \ No newline at end of file +plt.show() diff --git a/quam_builder/architecture/quantum_dots/examples/wiring_example.py b/quam_builder/architecture/quantum_dots/examples/wiring_example.py index adf92cbf..a24e75e4 100644 --- a/quam_builder/architecture/quantum_dots/examples/wiring_example.py +++ b/quam_builder/architecture/quantum_dots/examples/wiring_example.py @@ -1,465 +1,510 @@ -import os -from pathlib import Path - -import matplotlib.pyplot as plt -from qualang_tools.wirer import Connectivity, Instruments, allocate_wiring, visualize - -from qualang_tools.wirer.connectivity.wiring_spec import ( - WiringFrequency, - WiringIOType, - WiringLineType, -) -from qualang_tools.wirer.wirer.channel_specs import * -from quam_builder.architecture.quantum_dots.qpu import BaseQuamQD -from quam_builder.builder.qop_connectivity import build_quam_wiring -from quam_builder.builder.quantum_dots import ( - build_base_quam, - build_loss_divincenzo_quam, - build_quam, -) - -# from quam_config import Quam - -EXAMPLES_DIR = Path(__file__).resolve().parent -os.environ.setdefault("QUAM_STATE_PATH", str(EXAMPLES_DIR / "quam_state")) - -######################################################################################################################## -# %% Define static parameters -######################################################################################################################## -host_ip = "172.16.33.115" # QOP IP address -port = None # QOP Port -cluster_name = "CS_3" # Name of the cluster - -# Define which quantum dot ids are present in the system -global_gates = [1, 2] -sensor_dots = [1, 2] -quantum_dots = [1, 2, 3, 4, 5] -quantum_dot_pairs = [(1, 2), (2, 3), (3, 4), (4, 5)] - -# Qubit pair to sensor mapping -qubit_pair_sensor_map = { - "q1_q2": ["sensor_1"], - "q2_q3": ["sensor_1", "sensor_2"], - "q3_q4": ["sensor_2"], -} - -######################################################################################################################## -# %% EXAMPLE 1: Two-Stage Workflow (Recommended for Calibration) -######################################################################################################################## -# This workflow separates the dot-layer wiring (Stage 1) from qubit drive lines (Stage 2), -# allowing you to calibrate quantum dots before adding qubit drive lines. - - -def example_1_two_stage_workflow(): - """Two-stage workflow: separate dot-layer wiring from qubit drive lines.""" - print("=" * 80) - print("EXAMPLE 1: Two-Stage Workflow") - print("=" * 80) - - # ============================================================================ - # STAGE 1: Create connectivity WITHOUT drive lines (dot-layer only) - # ============================================================================ - print("\n--- STAGE 1: Setting up connectivity WITHOUT drive lines ---") - connectivity_stage1 = Connectivity() - - # Add global gates (readout bias lines) - connectivity_stage1.add_voltage_gate_lines(voltage_gates=global_gates, name="rb") - - # Add sensor dots (voltage gates + resonator lines) - # Using convenience method that adds both voltage gates and resonator lines - connectivity_stage1.add_sensor_dots(sensor_dots=sensor_dots, shared_resonator_line=False, use_mw_fem=False) - - # Add quantum dots with plunger gates ONLY (no drive lines) - # Note: add_drive_lines=False is the default, so we're only adding plunger gates - connectivity_stage1.add_quantum_dots(quantum_dots=quantum_dots, add_drive_lines=False) - - # Add quantum dot pairs (barrier gates) - connectivity_stage1.add_quantum_dot_pairs(quantum_dot_pairs=quantum_dot_pairs) - - # Create instruments for Stage 1 - instruments_stage1 = Instruments() - instruments_stage1.add_mw_fem(controller=1, slots=[1]) - instruments_stage1.add_lf_fem(controller=1, slots=[2, 3]) - - # Allocate wiring - print("Allocating wiring for Stage 1...") - try: - allocate_wiring(connectivity_stage1, instruments_stage1) - print("✓ Stage 1 wiring allocation successful") - except Exception as e: - print(f"✗ Error in allocate_wiring: {e}") - import traceback - traceback.print_exc() - raise - - # Build wiring and QUAM for Stage 1 - print("\nBuilding QUAM for Stage 1 (dot-layer only)...") - try: - machine_stage1 = BaseQuamQD() - machine_stage1 = build_quam_wiring( - connectivity_stage1, - host_ip, - cluster_name, - machine_stage1, - ) - print("✓ Stage 1 wiring and QUAM build successful") - except Exception as e: - print(f"✗ Error in build_quam_wiring: {e}") - import traceback - traceback.print_exc() - raise - - # Build BaseQuamQD (quantum dots only, no qubits) - print("\nBuilding BaseQuamQD (quantum dots only)...") - try: - machine_stage1 = build_base_quam( - machine_stage1, - connect_qdac=False, - # qdac_ip="172.16.33.101", # QDAC IP address - save=True, # Save the BaseQuamQD state - ) - print("✓ Stage 1 BaseQuamQD build successful") - print(" → You can now calibrate quantum dots, update cross-compensation matrix, etc.") - print(" → Saved state can be loaded later for Stage 2") - except Exception as e: - print(f"✗ Error in build_base_quam: {e}") - import traceback - traceback.print_exc() - raise - - # ============================================================================ - # STAGE 2: Recreate connectivity WITH drive lines, then build full QUAM - # ============================================================================ - print("\n" + "=" * 80) - print("--- STAGE 2: Setting up connectivity WITH drive lines ---") - print("=" * 80) - - # Recreate instruments for Stage 2 (channels from Stage 1 may have been marked as used) - # Alternatively, you can reuse the same instruments object if block_used_channels=False - instruments_stage2 = Instruments() - instruments_stage2.add_mw_fem(controller=1, slots=[1]) - instruments_stage2.add_lf_fem(controller=1, slots=[2, 3]) - - # Recreate connectivity with the same dot-layer wiring, but now add drive lines - connectivity_stage2 = Connectivity() - - # Add the same dot-layer components as Stage 1 - connectivity_stage2.add_voltage_gate_lines(voltage_gates=global_gates, name="rb") - connectivity_stage2.add_sensor_dots(sensor_dots=sensor_dots, shared_resonator_line=False, use_mw_fem=False) - connectivity_stage2.add_quantum_dot_pairs(quantum_dot_pairs=quantum_dot_pairs) - - # Add quantum dots WITH drive lines this time - # Using convenience method that adds both plunger gates and drive lines - connectivity_stage2.add_quantum_dots( - quantum_dots=quantum_dots, - add_drive_lines=True, # ← Key: Add drive lines in Stage 2 - use_mw_fem=True, # Use MW-FEM for drive (set to False for LF-FEM) - shared_drive_line=True, # Share drive line across ALL quantum dots (saves channels - only needs 1 channel) - ) - - # Allocate wiring - print("Allocating wiring for Stage 2...") - try: - allocate_wiring(connectivity_stage2, instruments_stage2) - print("✓ Stage 2 wiring allocation successful") - except Exception as e: - print(f"✗ Error in allocate_wiring: {e}") - import traceback - traceback.print_exc() - raise - - # Build wiring and QUAM for Stage 2 - print("\nBuilding QUAM for Stage 2 (with drive lines)...") - try: - machine_stage2 = build_quam_wiring( - connectivity_stage2, - host_ip, - cluster_name, - machine_stage1, - ) - print("✓ Stage 2 wiring and QUAM build successful") - except Exception as e: - print(f"✗ Error in build_quam_wiring: {e}") - import traceback - traceback.print_exc() - raise - - # Build full QUAM with qubits - print("\nBuilding LossDiVincenzoQuam (with qubits)...") - try: - machine_stage2 = build_loss_divincenzo_quam( - machine_stage2, - qubit_pair_sensor_map=qubit_pair_sensor_map, - implicit_mapping=True, # q1 → virtual_dot_1 mapping - save=True, - ) - print("✓ Stage 2 LossDiVincenzoQuam build successful") - print(" → Machine now has both quantum dots AND qubits with drive lines") - except Exception as e: - print(f"✗ Error in build_loss_divincenzo_quam: {e}") - import traceback - traceback.print_exc() - raise - - -######################################################################################################################## -# %% EXAMPLE 2: Combined Workflow (Single-Stage) -######################################################################################################################## -# This workflow creates connectivity with all components (including drive lines) in one go. -# Use this when you don't need to calibrate quantum dots separately. - - -def example_2_combined_workflow(): - """Combined workflow: create connectivity with all components in one go.""" - print("\n" + "=" * 80) - print("EXAMPLE 2: Combined Workflow (Single-Stage)") - print("=" * 80) - print("\n--- Setting up connectivity WITH all components (including drive lines) ---") - - # Create fresh instruments for the combined workflow - instruments_combined = Instruments() - instruments_combined.add_mw_fem(controller=1, slots=[1]) - instruments_combined.add_lf_fem(controller=1, slots=[2, 3]) - - # Create connectivity with everything at once - connectivity_combined = Connectivity() - - # Add global gates - connectivity_combined.add_voltage_gate_lines(voltage_gates=global_gates, name="rb") - - # Add sensor dots - connectivity_combined.add_sensor_dots(sensor_dots=sensor_dots, shared_resonator_line=False, use_mw_fem=False) - - # Add quantum dots WITH drive lines in a single call - connectivity_combined.add_quantum_dots( - quantum_dots=quantum_dots, - add_drive_lines=True, # Include drive lines from the start - use_mw_fem=True, # Use MW-FEM for drive (set to False for LF-FEM) - shared_drive_line=True, # Share drive line across quantum dots (only needs 1 channel) - ) - - # Add quantum dot pairs - connectivity_combined.add_quantum_dot_pairs(quantum_dot_pairs=quantum_dot_pairs) - - # Allocate wiring - print("Allocating wiring...") - try: - allocate_wiring(connectivity_combined, instruments_combined) - print("✓ Combined wiring allocation successful") - except Exception as e: - print(f"✗ Error in allocate_wiring: {e}") - import traceback - traceback.print_exc() - raise - - # Build wiring and QUAM - print("\nBuilding QUAM (combined approach)...") - try: - machine_combined = BaseQuamQD() - machine_combined = build_quam_wiring( - connectivity_combined, - host_ip, - cluster_name, - machine_combined, - ) - print("✓ Combined wiring and QUAM build successful") - except Exception as e: - print(f"✗ Error in build_quam_wiring: {e}") - import traceback - traceback.print_exc() - raise - - # Build full QUAM using convenience wrapper - print("\nBuilding full QUAM using convenience wrapper...") - try: - machine_combined = build_quam( - machine_combined, - qubit_pair_sensor_map=qubit_pair_sensor_map, - connect_qdac=False, - qdac_ip="172.16.33.101", - save=True, - ) - print("✓ Combined QUAM build successful") - except Exception as e: - print(f"✗ Error in build_quam: {e}") - import traceback - traceback.print_exc() - raise - - -######################################################################################################################## -# %% EXAMPLE 3: Two-Stage with Same Instruments (Add Drive Lines Only) -######################################################################################################################## -# This example tests whether we can reuse the same instruments instance and only add drive lines -# to the connectivity in Stage 2, without recreating all the dot-layer components. - - -def example_3_incremental_drive_lines(): - """Two-stage workflow: reuse same instruments and add drive lines incrementally.""" - print("\n" + "=" * 80) - print("EXAMPLE 3: Two-Stage with Same Instruments (Add Drive Lines Only)") - print("=" * 80) - - # Create a fresh instruments instance for this example (will be reused in Stage 2) - instruments_stage3 = Instruments() - instruments_stage3.add_mw_fem(controller=1, slots=[1]) - instruments_stage3.add_lf_fem(controller=1, slots=[2, 3]) - - # ============================================================================ - # STAGE 1: Create connectivity WITHOUT drive lines (dot-layer only) - # ============================================================================ - print("\n--- STAGE 1: Setting up connectivity WITHOUT drive lines ---") - connectivity_stage3 = Connectivity() - - # Add global gates (readout bias lines) - connectivity_stage3.add_voltage_gate_lines(voltage_gates=global_gates, name="rb") - - # Add sensor dots (voltage gates + resonator lines) - connectivity_stage3.add_sensor_dots(sensor_dots=sensor_dots, shared_resonator_line=False, use_mw_fem=False) - - # Add quantum dots with plunger gates ONLY (no drive lines) - connectivity_stage3.add_quantum_dots(quantum_dots=quantum_dots, add_drive_lines=False) - - # Add quantum dot pairs (barrier gates) - connectivity_stage3.add_quantum_dot_pairs(quantum_dot_pairs=quantum_dot_pairs) - - # Allocate wiring - using instruments_stage3 (will be reused in Stage 2) - print("Allocating wiring for Stage 1...") - try: - allocate_wiring(connectivity_stage3, instruments_stage3) - print("✓ Stage 1 wiring allocation successful") - except Exception as e: - print(f"✗ Error in allocate_wiring: {e}") - import traceback - traceback.print_exc() - raise - - # Build wiring and QUAM for Stage 1 - print("\nBuilding QUAM for Stage 1 (dot-layer only)...") - try: - machine_stage3 = BaseQuamQD() - machine_stage3 = build_quam_wiring( - connectivity_stage3, - host_ip, - cluster_name, - machine_stage3, - ) - print("✓ Stage 1 wiring and QUAM build successful") - except Exception as e: - print(f"✗ Error in build_quam_wiring: {e}") - import traceback - traceback.print_exc() - raise - - # Build BaseQuamQD (quantum dots only, no qubits) - print("\nBuilding BaseQuamQD (quantum dots only)...") - try: - machine_stage3 = build_base_quam( - machine_stage3, - connect_qdac=False, - save=True, - ) - print("✓ Stage 1 BaseQuamQD build successful") - except Exception as e: - print(f"✗ Error in build_base_quam: {e}") - import traceback - traceback.print_exc() - raise - - # ============================================================================ - # STAGE 2: Add ONLY drive lines to the same connectivity, reuse same instruments - # ============================================================================ - print("\n" + "=" * 80) - print("--- STAGE 2: Adding ONLY drive lines to existing connectivity ---") - print("=" * 80) - print("Note: Using the SAME instruments instance and adding drive lines to the SAME connectivity object") - - # Add ONLY drive lines to the existing connectivity - # The dot-layer components (gates, sensors, pairs) are already there from Stage 1 - connectivity_stage3 = Connectivity() - connectivity_stage3.add_quantum_dot_drive_lines( - quantum_dots=quantum_dots, - use_mw_fem=True, # Use MW-FEM for drive - shared_line=True, # Share drive line across ALL quantum dots - ) - - # Allocate wiring for the NEW drive line specs using the SAME instruments instance - # Note: block_used_channels=True (default) means channels from Stage 1 are still marked as used - # but new channels can be allocated for the drive lines - print("Allocating wiring for Stage 2 (drive lines only)...") - try: - allocate_wiring(connectivity_stage3, instruments_stage3) - print("✓ Stage 2 wiring allocation successful (drive lines added to existing connectivity)") - except Exception as e: - print(f"✗ Error in allocate_wiring: {e}") - print(" → This might fail if there aren't enough available channels after Stage 1") - import traceback - traceback.print_exc() - raise - - # Build wiring and QUAM for Stage 2 - print("\nBuilding QUAM for Stage 2 (with drive lines added)...") - try: - machine_stage3 = build_quam_wiring( - connectivity_stage3, - host_ip, - cluster_name, - machine_stage3, - ) - print("✓ Stage 2 wiring and QUAM build successful") - except Exception as e: - print(f"✗ Error in build_quam_wiring: {e}") - import traceback - traceback.print_exc() - raise - - # Build full QUAM with qubits - print("\nBuilding LossDiVincenzoQuam (with qubits)...") - try: - machine_stage3 = build_loss_divincenzo_quam( - machine_stage3, - qubit_pair_sensor_map=qubit_pair_sensor_map, - implicit_mapping=True, - save=True, - ) - print("✓ Stage 3 LossDiVincenzoQuam build successful") - print(" → Machine now has both quantum dots AND qubits with drive lines") - print(" → Successfully reused same instruments instance and added drive lines incrementally") - except Exception as e: - print(f"✗ Error in build_loss_divincenzo_quam: {e}") - import traceback - traceback.print_exc() - raise - - -######################################################################################################################## -# %% Main Entry Point -######################################################################################################################## - -if __name__ == "__main__": - # Run all examples - example_1_two_stage_workflow() - example_2_combined_workflow() - example_3_incremental_drive_lines() - - # Optional: Visualize Wiring - # Uncomment to visualize wiring (requires a GUI backend) - # import matplotlib - # matplotlib.use("TkAgg") - # visualize( - # connectivity_combined.elements, - # available_channels=instruments_combined.available_channels, - # use_matplotlib=True, - # ) - # plt.show() - - # Optional: Generate QM Configuration - # Uncomment to generate config: - # try: - # machine_combined.generate_config() - # print("✓ Configuration generation successful") - # except Exception as e: - # print(f"✗ Error in generate_config: {e}") - # import traceback - # traceback.print_exc() - # raise +import os +from pathlib import Path + +import matplotlib.pyplot as plt +from qualang_tools.wirer import Connectivity, Instruments, allocate_wiring, visualize + +from qualang_tools.wirer.connectivity.wiring_spec import ( + WiringFrequency, + WiringIOType, + WiringLineType, +) +from qualang_tools.wirer.wirer.channel_specs import * +from quam_builder.architecture.quantum_dots.qpu import BaseQuamQD +from quam_builder.builder.qop_connectivity import build_quam_wiring +from quam_builder.builder.quantum_dots import ( + build_base_quam, + build_loss_divincenzo_quam, + build_quam, +) + +# from quam_config import Quam + +EXAMPLES_DIR = Path(__file__).resolve().parent +os.environ.setdefault("QUAM_STATE_PATH", str(EXAMPLES_DIR / "quam_state")) + +######################################################################################################################## +# %% Define static parameters +######################################################################################################################## +host_ip = "172.16.33.115" # QOP IP address +port = None # QOP Port +cluster_name = "CS_3" # Name of the cluster + +# Define which quantum dot ids are present in the system +global_gates = [1, 2] +sensor_dots = [1, 2] +quantum_dots = [1, 2, 3, 4, 5] +quantum_dot_pairs = [(1, 2), (2, 3), (3, 4), (4, 5)] + +# Qubit pair to sensor mapping +qubit_pair_sensor_map = { + "q1_q2": ["sensor_1"], + "q2_q3": ["sensor_1", "sensor_2"], + "q3_q4": ["sensor_2"], +} + +######################################################################################################################## +# %% EXAMPLE 1: Two-Stage Workflow (Recommended for Calibration) +######################################################################################################################## +# This workflow separates the dot-layer wiring (Stage 1) from qubit drive lines (Stage 2), +# allowing you to calibrate quantum dots before adding qubit drive lines. + + +def example_1_two_stage_workflow(): + """Two-stage workflow: separate dot-layer wiring from qubit drive lines.""" + print("=" * 80) + print("EXAMPLE 1: Two-Stage Workflow") + print("=" * 80) + + machine_stage1 = example_1_stage_1() + example_1_stage_2(machine_stage1) + + +def example_1_stage_1(): + # ============================================================================ + # STAGE 1: Create connectivity WITHOUT drive lines (dot-layer only) + # ============================================================================ + print("\n--- STAGE 1: Setting up connectivity WITHOUT drive lines ---") + connectivity_stage1 = Connectivity() + + # Add global gates (readout bias lines) + connectivity_stage1.add_voltage_gate_lines(voltage_gates=global_gates, name="rb") + + # Add sensor dots (voltage gates + resonator lines) + # Using convenience method that adds both voltage gates and resonator lines + connectivity_stage1.add_sensor_dots( + sensor_dots=sensor_dots, shared_resonator_line=False, use_mw_fem=False + ) + + # Add quantum dots with plunger gates ONLY (no drive lines) + # Note: add_drive_lines=False is the default, so we're only adding plunger gates + connectivity_stage1.add_quantum_dots(quantum_dots=quantum_dots, add_drive_lines=False) + + # Add quantum dot pairs (barrier gates) + connectivity_stage1.add_quantum_dot_pairs(quantum_dot_pairs=quantum_dot_pairs) + + # Create instruments for Stage 1 + instruments_stage1 = Instruments() + instruments_stage1.add_mw_fem(controller=1, slots=[1]) + instruments_stage1.add_lf_fem(controller=1, slots=[2, 3]) + + # Allocate wiring + print("Allocating wiring for Stage 1...") + try: + allocate_wiring(connectivity_stage1, instruments_stage1) + print("✓ Stage 1 wiring allocation successful") + except Exception as e: + print(f"✗ Error in allocate_wiring: {e}") + import traceback + + traceback.print_exc() + raise + + # Build wiring and QUAM for Stage 1 + print("\nBuilding QUAM for Stage 1 (dot-layer only)...") + try: + machine_stage1 = BaseQuamQD() + machine_stage1 = build_quam_wiring( + connectivity_stage1, + host_ip, + cluster_name, + machine_stage1, + ) + print("✓ Stage 1 wiring and QUAM build successful") + except Exception as e: + print(f"✗ Error in build_quam_wiring: {e}") + import traceback + + traceback.print_exc() + raise + + # Build BaseQuamQD (quantum dots only, no qubits) + print("\nBuilding BaseQuamQD (quantum dots only)...") + try: + machine_stage1 = build_base_quam( + machine_stage1, + connect_qdac=False, + # qdac_ip="172.16.33.101", # QDAC IP address + save=True, # Save the BaseQuamQD state + ) + print("✓ Stage 1 BaseQuamQD build successful") + print(" → You can now calibrate quantum dots, update cross-compensation matrix, etc.") + print(" → Saved state can be loaded later for Stage 2") + except Exception as e: + print(f"✗ Error in build_base_quam: {e}") + import traceback + + traceback.print_exc() + raise + + return machine_stage1 + + +def example_1_stage_2(machine_stage1): + # ============================================================================ + # STAGE 2: Recreate connectivity WITH drive lines, then build full QUAM + # ============================================================================ + print("\n" + "=" * 80) + print("--- STAGE 2: Setting up connectivity WITH drive lines ---") + print("=" * 80) + + # Recreate instruments for Stage 2 (channels from Stage 1 may have been marked as used) + # Alternatively, you can reuse the same instruments object if block_used_channels=False + instruments_stage2 = Instruments() + instruments_stage2.add_mw_fem(controller=1, slots=[1]) + instruments_stage2.add_lf_fem(controller=1, slots=[2, 3]) + + # Recreate connectivity with the same dot-layer wiring, but now add drive lines + connectivity_stage2 = Connectivity() + + # Add the same dot-layer components as Stage 1 + connectivity_stage2.add_voltage_gate_lines(voltage_gates=global_gates, name="rb") + connectivity_stage2.add_sensor_dots( + sensor_dots=sensor_dots, shared_resonator_line=False, use_mw_fem=False + ) + connectivity_stage2.add_quantum_dot_pairs(quantum_dot_pairs=quantum_dot_pairs) + + # Add quantum dots WITH drive lines this time + # Using convenience method that adds both plunger gates and drive lines + connectivity_stage2.add_quantum_dots( + quantum_dots=quantum_dots, + add_drive_lines=True, # ← Key: Add drive lines in Stage 2 + use_mw_fem=True, # Use MW-FEM for drive (set to False for LF-FEM) + shared_drive_line=True, # Share drive line across ALL quantum dots (saves channels - only needs 1 channel) + ) + + # Allocate wiring + print("Allocating wiring for Stage 2...") + try: + allocate_wiring(connectivity_stage2, instruments_stage2) + print("✓ Stage 2 wiring allocation successful") + except Exception as e: + print(f"✗ Error in allocate_wiring: {e}") + import traceback + + traceback.print_exc() + raise + + # Build wiring and QUAM for Stage 2 + print("\nBuilding QUAM for Stage 2 (with drive lines)...") + try: + machine_stage2 = build_quam_wiring( + connectivity_stage2, + host_ip, + cluster_name, + machine_stage1, + ) + print("✓ Stage 2 wiring and QUAM build successful") + except Exception as e: + print(f"✗ Error in build_quam_wiring: {e}") + import traceback + + traceback.print_exc() + raise + + # Build full QUAM with qubits + print("\nBuilding LossDiVincenzoQuam (with qubits)...") + try: + machine_stage2 = build_loss_divincenzo_quam( + machine_stage2, + qubit_pair_sensor_map=qubit_pair_sensor_map, + implicit_mapping=True, # q1 → virtual_dot_1 mapping + save=True, + ) + print("✓ Stage 2 LossDiVincenzoQuam build successful") + print(" → Machine now has both quantum dots AND qubits with drive lines") + except Exception as e: + print(f"✗ Error in build_loss_divincenzo_quam: {e}") + import traceback + + traceback.print_exc() + raise + + +######################################################################################################################## +# %% EXAMPLE 2: Combined Workflow (Single-Stage) +######################################################################################################################## +# This workflow creates connectivity with all components (including drive lines) in one go. +# Use this when you don't need to calibrate quantum dots separately. + + +def example_2_combined_workflow(): + """Combined workflow: create connectivity with all components in one go.""" + print("\n" + "=" * 80) + print("EXAMPLE 2: Combined Workflow (Single-Stage)") + print("=" * 80) + print("\n--- Setting up connectivity WITH all components (including drive lines) ---") + + # Create fresh instruments for the combined workflow + instruments_combined = Instruments() + instruments_combined.add_mw_fem(controller=1, slots=[1]) + instruments_combined.add_lf_fem(controller=1, slots=[2, 3]) + + # Create connectivity with everything at once + connectivity_combined = Connectivity() + + # Add global gates + connectivity_combined.add_voltage_gate_lines(voltage_gates=global_gates, name="rb") + + # Add sensor dots + connectivity_combined.add_sensor_dots( + sensor_dots=sensor_dots, shared_resonator_line=False, use_mw_fem=False + ) + + # Add quantum dots WITH drive lines in a single call + connectivity_combined.add_quantum_dots( + quantum_dots=quantum_dots, + add_drive_lines=True, # Include drive lines from the start + use_mw_fem=True, # Use MW-FEM for drive (set to False for LF-FEM) + shared_drive_line=True, # Share drive line across quantum dots (only needs 1 channel) + ) + + # Add quantum dot pairs + connectivity_combined.add_quantum_dot_pairs(quantum_dot_pairs=quantum_dot_pairs) + + # Allocate wiring + print("Allocating wiring...") + try: + allocate_wiring(connectivity_combined, instruments_combined) + print("✓ Combined wiring allocation successful") + except Exception as e: + print(f"✗ Error in allocate_wiring: {e}") + import traceback + + traceback.print_exc() + raise + + # Build wiring and QUAM + print("\nBuilding QUAM (combined approach)...") + try: + machine_combined = BaseQuamQD() + machine_combined = build_quam_wiring( + connectivity_combined, + host_ip, + cluster_name, + machine_combined, + ) + print("✓ Combined wiring and QUAM build successful") + except Exception as e: + print(f"✗ Error in build_quam_wiring: {e}") + import traceback + + traceback.print_exc() + raise + + # Build full QUAM using convenience wrapper + print("\nBuilding full QUAM using convenience wrapper...") + try: + machine_combined = build_quam( + machine_combined, + qubit_pair_sensor_map=qubit_pair_sensor_map, + connect_qdac=False, + qdac_ip="172.16.33.101", + save=True, + ) + print("✓ Combined QUAM build successful") + except Exception as e: + print(f"✗ Error in build_quam: {e}") + import traceback + + traceback.print_exc() + raise + + +######################################################################################################################## +# %% EXAMPLE 3: Two-Stage with Same Instruments (Add Drive Lines Only) +######################################################################################################################## +# This example tests whether we can reuse the same instruments instance and only add drive lines +# to the connectivity in Stage 2, without recreating all the dot-layer components. + + +def example_3_incremental_drive_lines(): + """Two-stage workflow: reuse same instruments and add drive lines incrementally.""" + print("\n" + "=" * 80) + print("EXAMPLE 3: Two-Stage with Same Instruments (Add Drive Lines Only)") + print("=" * 80) + + # Create a fresh instruments instance for this example (will be reused in Stage 2) + instruments_stage3 = Instruments() + instruments_stage3.add_mw_fem(controller=1, slots=[1]) + instruments_stage3.add_lf_fem(controller=1, slots=[2, 3]) + + machine_stage3 = example_3_stage_1(instruments_stage3) + example_3_stage_2(instruments_stage3, machine_stage3) + + +def example_3_stage_1(instruments_stage3): + # ============================================================================ + # STAGE 1: Create connectivity WITHOUT drive lines (dot-layer only) + # ============================================================================ + print("\n--- STAGE 1: Setting up connectivity WITHOUT drive lines ---") + connectivity_stage3 = Connectivity() + + # Add global gates (readout bias lines) + connectivity_stage3.add_voltage_gate_lines(voltage_gates=global_gates, name="rb") + + # Add sensor dots (voltage gates + resonator lines) + connectivity_stage3.add_sensor_dots( + sensor_dots=sensor_dots, shared_resonator_line=False, use_mw_fem=False + ) + + # Add quantum dots with plunger gates ONLY (no drive lines) + connectivity_stage3.add_quantum_dots(quantum_dots=quantum_dots, add_drive_lines=False) + + # Add quantum dot pairs (barrier gates) + connectivity_stage3.add_quantum_dot_pairs(quantum_dot_pairs=quantum_dot_pairs) + + # Allocate wiring - using instruments_stage3 (will be reused in Stage 2) + print("Allocating wiring for Stage 1...") + try: + allocate_wiring(connectivity_stage3, instruments_stage3) + print("✓ Stage 1 wiring allocation successful") + except Exception as e: + print(f"✗ Error in allocate_wiring: {e}") + import traceback + + traceback.print_exc() + raise + + # Build wiring and QUAM for Stage 1 + print("\nBuilding QUAM for Stage 1 (dot-layer only)...") + try: + machine_stage3 = BaseQuamQD() + machine_stage3 = build_quam_wiring( + connectivity_stage3, + host_ip, + cluster_name, + machine_stage3, + ) + print("✓ Stage 1 wiring and QUAM build successful") + except Exception as e: + print(f"✗ Error in build_quam_wiring: {e}") + import traceback + + traceback.print_exc() + raise + + # Build BaseQuamQD (quantum dots only, no qubits) + print("\nBuilding BaseQuamQD (quantum dots only)...") + try: + machine_stage3 = build_base_quam( + machine_stage3, + connect_qdac=False, + save=True, + ) + print("✓ Stage 1 BaseQuamQD build successful") + except Exception as e: + print(f"✗ Error in build_base_quam: {e}") + import traceback + + traceback.print_exc() + raise + + return machine_stage3 + + +def example_3_stage_2(instruments_stage3, machine_stage3): + # ============================================================================ + # STAGE 2: Add ONLY drive lines to the same connectivity, reuse same instruments + # ============================================================================ + print("\n" + "=" * 80) + print("--- STAGE 2: Adding ONLY drive lines to existing connectivity ---") + print("=" * 80) + print( + "Note: Using the SAME instruments instance and adding drive lines to the SAME connectivity object" + ) + + # Add ONLY drive lines to the existing connectivity + # The dot-layer components (gates, sensors, pairs) are already there from Stage 1 + connectivity_stage3 = Connectivity() + connectivity_stage3.add_quantum_dot_drive_lines( + quantum_dots=quantum_dots, + use_mw_fem=True, # Use MW-FEM for drive + shared_line=True, # Share drive line across ALL quantum dots + ) + + # Allocate wiring for the NEW drive line specs using the SAME instruments instance + # Note: block_used_channels=True (default) means channels from Stage 1 are still marked as used + # but new channels can be allocated for the drive lines + print("Allocating wiring for Stage 2 (drive lines only)...") + try: + allocate_wiring(connectivity_stage3, instruments_stage3) + print("✓ Stage 2 wiring allocation successful (drive lines added to existing connectivity)") + except Exception as e: + print(f"✗ Error in allocate_wiring: {e}") + print(" → This might fail if there aren't enough available channels after Stage 1") + import traceback + + traceback.print_exc() + raise + + # Build wiring and QUAM for Stage 2 + print("\nBuilding QUAM for Stage 2 (with drive lines added)...") + try: + machine_stage3 = build_quam_wiring( + connectivity_stage3, + host_ip, + cluster_name, + machine_stage3, + ) + print("✓ Stage 2 wiring and QUAM build successful") + except Exception as e: + print(f"✗ Error in build_quam_wiring: {e}") + import traceback + + traceback.print_exc() + raise + + # Build full QUAM with qubits + print("\nBuilding LossDiVincenzoQuam (with qubits)...") + try: + machine_stage3 = build_loss_divincenzo_quam( + machine_stage3, + qubit_pair_sensor_map=qubit_pair_sensor_map, + implicit_mapping=True, + save=True, + ) + print("✓ Stage 3 LossDiVincenzoQuam build successful") + print(" → Machine now has both quantum dots AND qubits with drive lines") + print( + " → Successfully reused same instruments instance and added drive lines incrementally" + ) + except Exception as e: + print(f"✗ Error in build_loss_divincenzo_quam: {e}") + import traceback + + traceback.print_exc() + raise + + +######################################################################################################################## +# %% Main Entry Point +######################################################################################################################## + +if __name__ == "__main__": + # Run all examples + example_1_two_stage_workflow() + example_2_combined_workflow() + example_3_incremental_drive_lines() + + # Optional: Visualize Wiring + # Uncomment to visualize wiring (requires a GUI backend) + # import matplotlib + # matplotlib.use("TkAgg") + # visualize( + # connectivity_combined.elements, + # available_channels=instruments_combined.available_channels, + # use_matplotlib=True, + # ) + # plt.show() + + # Optional: Generate QM Configuration + # Uncomment to generate config: + # try: + # machine_combined.generate_config() + # print("✓ Configuration generation successful") + # except Exception as e: + # print(f"✗ Error in generate_config: {e}") + # import traceback + # traceback.print_exc() + # raise diff --git a/quam_builder/architecture/quantum_dots/operations/__init__.py b/quam_builder/architecture/quantum_dots/operations/__init__.py index f79fc4e7..80388d4f 100644 --- a/quam_builder/architecture/quantum_dots/operations/__init__.py +++ b/quam_builder/architecture/quantum_dots/operations/__init__.py @@ -13,4 +13,4 @@ __all__ = [ *default_macros.__all__, *default_operations.__all__, -] \ No newline at end of file +] diff --git a/quam_builder/architecture/quantum_dots/operations/default_macros/__init__.py b/quam_builder/architecture/quantum_dots/operations/default_macros/__init__.py index 90a151a0..d5615310 100644 --- a/quam_builder/architecture/quantum_dots/operations/default_macros/__init__.py +++ b/quam_builder/architecture/quantum_dots/operations/default_macros/__init__.py @@ -10,10 +10,10 @@ The default_macros dictionary provides a unified collection of all available macros for easy registration with quantum dot components. """ + from .single_qubit_macros import * from .two_qubit_macros import * - __all__ = [ *single_qubit_macros.__all__, *two_qubit_macros.__all__, diff --git a/quam_builder/architecture/quantum_dots/operations/default_macros/single_qubit_macros.py b/quam_builder/architecture/quantum_dots/operations/default_macros/single_qubit_macros.py index 460b1f5c..6530b476 100644 --- a/quam_builder/architecture/quantum_dots/operations/default_macros/single_qubit_macros.py +++ b/quam_builder/architecture/quantum_dots/operations/default_macros/single_qubit_macros.py @@ -9,7 +9,6 @@ from quam.components.macro import QubitMacro - __all__ = [ "SINGLE_QUBIT_MACROS", # state macros diff --git a/quam_builder/architecture/quantum_dots/operations/default_macros/two_qubit_macros.py b/quam_builder/architecture/quantum_dots/operations/default_macros/two_qubit_macros.py index 5a1def1a..c4f8f10e 100644 --- a/quam_builder/architecture/quantum_dots/operations/default_macros/two_qubit_macros.py +++ b/quam_builder/architecture/quantum_dots/operations/default_macros/two_qubit_macros.py @@ -8,7 +8,6 @@ from quam.components.macro import QubitPairMacro - __all__ = [ "TWO_QUBIT_MACROS", "Initialize2QMacro", diff --git a/quam_builder/architecture/quantum_dots/qpu/base_quam_qd.py b/quam_builder/architecture/quantum_dots/qpu/base_quam_qd.py index 99017843..815fbd1a 100644 --- a/quam_builder/architecture/quantum_dots/qpu/base_quam_qd.py +++ b/quam_builder/architecture/quantum_dots/qpu/base_quam_qd.py @@ -648,18 +648,18 @@ def step_to_voltage( actual_voltages[name] = value if not default_to_zero: - for qubit in self.qubits.keys(): + for qubit, qubit_obj in self.qubits.items(): if qubit in voltages: continue else: - voltages[qubit] = self.qubits[qubit].current_voltage + voltages[qubit] = qubit_obj.current_voltage new_sequence.step_to_voltages(actual_voltages) def initialise(self, qubit_name: str) -> None: if qubit_name not in self.qubits: raise ValueError( - f"Qubit {qubit_name} not in registered qubits: {list[self.qubits.keys()]}" + f"Qubit {qubit_name} not in registered qubits: {list(self.qubits.keys())}" ) try: diff --git a/quam_builder/architecture/quantum_dots/qpu/loss_divincenzo_quam.py b/quam_builder/architecture/quantum_dots/qpu/loss_divincenzo_quam.py index ea63d17f..69718416 100644 --- a/quam_builder/architecture/quantum_dots/qpu/loss_divincenzo_quam.py +++ b/quam_builder/architecture/quantum_dots/qpu/loss_divincenzo_quam.py @@ -32,7 +32,6 @@ LDQubitPair, ) - __all__ = ["LossDiVincenzoQuam"] diff --git a/quam_builder/architecture/superconducting/__init__.py b/quam_builder/architecture/superconducting/__init__.py index 49990dff..51b31a39 100644 --- a/quam_builder/architecture/superconducting/__init__.py +++ b/quam_builder/architecture/superconducting/__init__.py @@ -1,4 +1,12 @@ -from .components import cross_resonance, flux_line, mixer, readout_resonator, tunable_coupler, xy_drive, zz_drive +from .components import ( + cross_resonance, + flux_line, + mixer, + readout_resonator, + tunable_coupler, + xy_drive, + zz_drive, +) from .custom_gates import CZGate from .qpu import BaseQuam, FixedFrequencyQuam, FluxTunableQuam from .qubit import BaseTransmon, FixedFrequencyTransmon, FluxTunableTransmon diff --git a/quam_builder/architecture/superconducting/components/flux_line.py b/quam_builder/architecture/superconducting/components/flux_line.py index 25c0d157..1e93ca75 100644 --- a/quam_builder/architecture/superconducting/components/flux_line.py +++ b/quam_builder/architecture/superconducting/components/flux_line.py @@ -3,7 +3,6 @@ from quam.core import quam_dataclass from quam.components import SingleChannel - __all__ = ["FluxLine"] diff --git a/quam_builder/architecture/superconducting/components/readout_resonator.py b/quam_builder/architecture/superconducting/components/readout_resonator.py index 0a7e2a19..1cbe6d78 100644 --- a/quam_builder/architecture/superconducting/components/readout_resonator.py +++ b/quam_builder/architecture/superconducting/components/readout_resonator.py @@ -12,7 +12,6 @@ get_output_power_iq_channel, ) - __all__ = ["ReadoutResonatorIQ", "ReadoutResonatorMW"] diff --git a/quam_builder/architecture/superconducting/components/xy_drive.py b/quam_builder/architecture/superconducting/components/xy_drive.py index 708b6d67..25035e7f 100644 --- a/quam_builder/architecture/superconducting/components/xy_drive.py +++ b/quam_builder/architecture/superconducting/components/xy_drive.py @@ -12,7 +12,6 @@ get_output_power_iq_channel, ) - __all__ = ["XYDriveIQ", "XYDriveMW"] diff --git a/quam_builder/architecture/superconducting/qpu/fixed_frequency_quam.py b/quam_builder/architecture/superconducting/qpu/fixed_frequency_quam.py index 388a55ee..265acdd6 100644 --- a/quam_builder/architecture/superconducting/qpu/fixed_frequency_quam.py +++ b/quam_builder/architecture/superconducting/qpu/fixed_frequency_quam.py @@ -9,7 +9,6 @@ ) from quam_builder.architecture.superconducting.qpu.base_quam import BaseQuam - __all__ = ["FixedFrequencyQuam", "FixedFrequencyTransmon", "FixedFrequencyTransmonPair"] diff --git a/quam_builder/architecture/superconducting/qpu/flux_tunable_quam.py b/quam_builder/architecture/superconducting/qpu/flux_tunable_quam.py index 4f937f07..d348ba32 100644 --- a/quam_builder/architecture/superconducting/qpu/flux_tunable_quam.py +++ b/quam_builder/architecture/superconducting/qpu/flux_tunable_quam.py @@ -9,7 +9,6 @@ from quam_builder.architecture.superconducting.qubit_pair import FluxTunableTransmonPair from quam_builder.architecture.superconducting.qpu.base_quam import BaseQuam - __all__ = ["FluxTunableQuam", "FluxTunableTransmon", "FluxTunableTransmonPair"] diff --git a/quam_builder/architecture/superconducting/qubit/base_transmon.py b/quam_builder/architecture/superconducting/qubit/base_transmon.py index 0b76305c..e638f5f0 100644 --- a/quam_builder/architecture/superconducting/qubit/base_transmon.py +++ b/quam_builder/architecture/superconducting/qubit/base_transmon.py @@ -30,7 +30,6 @@ Cast, ) - __all__ = ["BaseTransmon"] 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..a2b879da 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 @@ -16,7 +16,6 @@ FixedFrequencyTransmon, ) - __all__ = ["FixedFrequencyTransmonPair"] diff --git a/quam_builder/builder/nv_center/add_nv_drive_component.py b/quam_builder/builder/nv_center/add_nv_drive_component.py index 30702d0b..482f0961 100644 --- a/quam_builder/builder/nv_center/add_nv_drive_component.py +++ b/quam_builder/builder/nv_center/add_nv_drive_component.py @@ -58,6 +58,4 @@ def add_nv_drive_component( ) else: - raise ValueError( - f"Unimplemented mapping of port keys to channel for ports: {ports}" - ) + raise ValueError(f"Unimplemented mapping of port keys to channel for ports: {ports}") diff --git a/quam_builder/builder/nv_center/add_nv_laser_component.py b/quam_builder/builder/nv_center/add_nv_laser_component.py index 45b3a258..622ee01c 100644 --- a/quam_builder/builder/nv_center/add_nv_laser_component.py +++ b/quam_builder/builder/nv_center/add_nv_laser_component.py @@ -15,9 +15,7 @@ u = unit(coerce_to_integer=True) -def add_nv_laser_component( - nv_center: AnyNVCenter, wiring_path: str, ports: Dict[str, str] -): +def add_nv_laser_component(nv_center: AnyNVCenter, wiring_path: str, ports: Dict[str, str]): """Adds a laser component to a nv_center qubit based on the provided wiring path and ports. Args: diff --git a/quam_builder/builder/nv_center/add_nv_spcm_component.py b/quam_builder/builder/nv_center/add_nv_spcm_component.py index feaf60fb..fa84ed53 100644 --- a/quam_builder/builder/nv_center/add_nv_spcm_component.py +++ b/quam_builder/builder/nv_center/add_nv_spcm_component.py @@ -25,9 +25,7 @@ def add_nv_spcm_component( time_of_flight: int = 32 # 4ns above default so that it appears in state.json signal_threshold: float = 800 / 4096 # The signal threshold in volts - signal_polarity: Literal["above", "below"] = ( - "below" # The polarity of the signal threshold - ) + signal_polarity: Literal["above", "below"] = "below" # The polarity of the signal threshold derivative_threshold: float = 300 / 4096 # The derivative threshold in volts/ns derivative_polarity: Literal["above", "below"] = ( "below" # The polarity of the derivative threshold @@ -52,6 +50,4 @@ def add_nv_spcm_component( setattr(nv_center, name, spcm) else: - raise ValueError( - f"Unimplemented mapping of port keys to channel for ports: {ports}" - ) + raise ValueError(f"Unimplemented mapping of port keys to channel for ports: {ports}") diff --git a/quam_builder/builder/qop_connectivity/build_quam_wiring.py b/quam_builder/builder/qop_connectivity/build_quam_wiring.py index 12c7eb86..6926f1d5 100644 --- a/quam_builder/builder/qop_connectivity/build_quam_wiring.py +++ b/quam_builder/builder/qop_connectivity/build_quam_wiring.py @@ -7,7 +7,6 @@ from quam_builder.architecture.nv_center.qpu import AnyQuamNV from quam_builder.builder.qop_connectivity.create_wiring import create_wiring - AnyQuam = Union[AnyQuamSC, AnyQuamNV] @@ -33,6 +32,8 @@ def build_quam_wiring( machine.wiring = create_wiring(connectivity) machine.save() + return machine + def add_ports_container(connectivity: Connectivity, machine: AnyQuam): """Detects whether the `connectivity` is using OPX+ or OPX1000 and returns the corresponding base object. diff --git a/quam_builder/builder/qop_connectivity/create_analog_ports.py b/quam_builder/builder/qop_connectivity/create_analog_ports.py index 74751853..76997304 100644 --- a/quam_builder/builder/qop_connectivity/create_analog_ports.py +++ b/quam_builder/builder/qop_connectivity/create_analog_ports.py @@ -115,10 +115,7 @@ def create_lf_opx_plus_port( key = f"opx_{channel.io_type}" elif len(channels_with_same_type) == 2: if channel.port == min( - [ - channel_with_same_type.port - for channel_with_same_type in channels_with_same_type - ] + channel_with_same_type.port for channel_with_same_type in channels_with_same_type ): key = f"opx_{channel.io_type}_I" else: diff --git a/quam_builder/builder/superconducting/add_transmon_drive_component.py b/quam_builder/builder/superconducting/add_transmon_drive_component.py index c7672680..5642e3ba 100644 --- a/quam_builder/builder/superconducting/add_transmon_drive_component.py +++ b/quam_builder/builder/superconducting/add_transmon_drive_component.py @@ -16,7 +16,6 @@ ) from qualang_tools.addons.calibration.calibrations import unit - u = unit(coerce_to_integer=True) @@ -60,6 +59,4 @@ def add_transmon_drive_component( ) else: - raise ValueError( - f"Unimplemented mapping of port keys to channel for ports: {ports}" - ) + raise ValueError(f"Unimplemented mapping of port keys to channel for ports: {ports}") diff --git a/quam_builder/builder/superconducting/add_transmon_flux_component.py b/quam_builder/builder/superconducting/add_transmon_flux_component.py index 0a39a2ef..540d7cb2 100644 --- a/quam_builder/builder/superconducting/add_transmon_flux_component.py +++ b/quam_builder/builder/superconducting/add_transmon_flux_component.py @@ -24,6 +24,4 @@ def add_transmon_flux_component( if "opx_output" in ports: transmon.z = FluxLine(opx_output=f"{wiring_path}/opx_output") else: - raise ValueError( - f"Unimplemented mapping of port keys to channel for ports: {ports}" - ) + raise ValueError(f"Unimplemented mapping of port keys to channel for ports: {ports}") diff --git a/quam_builder/builder/superconducting/add_transmon_pair_component.py b/quam_builder/builder/superconducting/add_transmon_pair_component.py index 739f4e61..1b2f0b6e 100644 --- a/quam_builder/builder/superconducting/add_transmon_pair_component.py +++ b/quam_builder/builder/superconducting/add_transmon_pair_component.py @@ -42,9 +42,7 @@ def add_transmon_pair_tunable_coupler_component( ) else: - raise ValueError( - f"Unimplemented mapping of port keys to channel for ports: {ports}" - ) + raise ValueError(f"Unimplemented mapping of port keys to channel for ports: {ports}") def add_transmon_pair_cross_resonance_component( @@ -72,11 +70,9 @@ def add_transmon_pair_cross_resonance_component( opx_output_I=f"{wiring_path}/opx_output_I", opx_output_Q=f"{wiring_path}/opx_output_Q", intermediate_frequency="#./inferred_intermediate_frequency", - frequency_converter_up=ports.data["control_qubit"] - + "/xy/frequency_converter_up", + frequency_converter_up=ports.data["control_qubit"] + "/xy/frequency_converter_up", target_qubit_LO_frequency=ports.data["target_qubit"] + "/xy/LO_frequency", - target_qubit_IF_frequency=ports.data["target_qubit"] - + "/xy/intermediate_frequency", + target_qubit_IF_frequency=ports.data["target_qubit"] + "/xy/intermediate_frequency", ) elif "opx_output" in ports.keys(): @@ -85,9 +81,7 @@ def add_transmon_pair_cross_resonance_component( ) else: - raise ValueError( - f"Unimplemented mapping of port keys to channel for ports: {ports}" - ) + raise ValueError(f"Unimplemented mapping of port keys to channel for ports: {ports}") def add_transmon_pair_zz_drive_component( @@ -115,20 +109,14 @@ def add_transmon_pair_zz_drive_component( opx_output_I=f"{wiring_path}/opx_output_I", opx_output_Q=f"{wiring_path}/opx_output_Q", intermediate_frequency="#./inferred_intermediate_frequency", - frequency_converter_up=ports.data["control_qubit"] - + "/xy/frequency_converter_up", + frequency_converter_up=ports.data["control_qubit"] + "/xy/frequency_converter_up", target_qubit_LO_frequency=ports.data["target_qubit"] + "/xy/LO_frequency", - target_qubit_IF_frequency=ports.data["target_qubit"] - + "/xy/intermediate_frequency", + target_qubit_IF_frequency=ports.data["target_qubit"] + "/xy/intermediate_frequency", detuning=0, ) elif "opx_output" in ports.keys(): - transmon_pair.zz_drive = ZZDriveMW( - id=zz_drive_name, opx_output=f"{wiring_path}/opx_output" - ) + transmon_pair.zz_drive = ZZDriveMW(id=zz_drive_name, opx_output=f"{wiring_path}/opx_output") else: - raise ValueError( - f"Unimplemented mapping of port keys to channel for ports: {ports}" - ) + raise ValueError(f"Unimplemented mapping of port keys to channel for ports: {ports}") diff --git a/quam_builder/builder/superconducting/add_transmon_resonator_component.py b/quam_builder/builder/superconducting/add_transmon_resonator_component.py index fa392b68..17f46b77 100644 --- a/quam_builder/builder/superconducting/add_transmon_resonator_component.py +++ b/quam_builder/builder/superconducting/add_transmon_resonator_component.py @@ -58,9 +58,7 @@ def add_transmon_resonator_component( RF_input_resonator.channel = transmon.resonator.get_reference() if RF_output_resonator != RF_input_resonator: # If there are separate up/down-converters, link their LOs by reference - RF_input_resonator.LO_frequency = ( - f"{RF_output_resonator.get_reference()}/LO_frequency" - ) + RF_input_resonator.LO_frequency = f"{RF_output_resonator.get_reference()}/LO_frequency" elif all(key in ports for key in mw_in_out_channel_ports): transmon.resonator = ReadoutResonatorMW( @@ -76,6 +74,4 @@ def add_transmon_resonator_component( ) else: - raise ValueError( - f"Unimplemented mapping of port keys to channel for ports: {ports}" - ) + raise ValueError(f"Unimplemented mapping of port keys to channel for ports: {ports}") diff --git a/quam_builder/builder/superconducting/add_twpa_component.py b/quam_builder/builder/superconducting/add_twpa_component.py index 9a63b18c..f15356d6 100644 --- a/quam_builder/builder/superconducting/add_twpa_component.py +++ b/quam_builder/builder/superconducting/add_twpa_component.py @@ -14,7 +14,6 @@ ) from qualang_tools.addons.calibration.calibrations import unit - u = unit(coerce_to_integer=True) diff --git a/quam_builder/tools/macros/__init__.py b/quam_builder/tools/macros/__init__.py index a057b886..ab6c68f4 100644 --- a/quam_builder/tools/macros/__init__.py +++ b/quam_builder/tools/macros/__init__.py @@ -8,4 +8,4 @@ *point_macros.__all__, *composable_macros.__all__, *measure_macros.__all__, -] \ No newline at end of file +] diff --git a/quam_builder/tools/macros/composable_macros.py b/quam_builder/tools/macros/composable_macros.py index 456a856b..4ab1a23e 100644 --- a/quam_builder/tools/macros/composable_macros.py +++ b/quam_builder/tools/macros/composable_macros.py @@ -270,9 +270,14 @@ def apply(self, **kwargs): if previous_element is not None and self.align_elements: - if isinstance(previous_element, (Qubit, QubitPair)) and not isinstance(new_element, (Qubit, QubitPair)): + if isinstance(previous_element, (Qubit, QubitPair)) and not isinstance( + new_element, (Qubit, QubitPair) + ): previous_element.align() - elif isinstance(previous_element, (Qubit, QubitPair)) and previous_element==new_element: + elif ( + isinstance(previous_element, (Qubit, QubitPair)) + and previous_element == new_element + ): previous_element.align() elif isinstance(previous_element, (Qubit)) and isinstance(new_element, (Qubit)): previous_element.align(new_element) @@ -332,4 +337,4 @@ def total_duration_seconds(self, component: QuamComponent) -> Optional[float]: if any(duration is None for duration in durations): return None - return sum(d for d in durations if d is not None) \ No newline at end of file + return sum(d for d in durations if d is not None) diff --git a/quam_builder/tools/macros/measure_macros.py b/quam_builder/tools/macros/measure_macros.py index e8827a71..cfa0a823 100644 --- a/quam_builder/tools/macros/measure_macros.py +++ b/quam_builder/tools/macros/measure_macros.py @@ -34,4 +34,4 @@ def apply(self, **kwargs) -> QuaVariableBool: I, Q = self.component.measure("readout") state = qua.declare(bool) qua.assign(state, I > self.threshold) - return state \ No newline at end of file + return state diff --git a/quam_builder/tools/power_tools.py b/quam_builder/tools/power_tools.py index 6be86aeb..c989583a 100644 --- a/quam_builder/tools/power_tools.py +++ b/quam_builder/tools/power_tools.py @@ -138,8 +138,9 @@ def set_output_power_iq_channel( """ u = unit(coerce_to_integer=True) + amplitude = None - if not ((max_amplitude is None) ^ (gain is None)): + if (max_amplitude is None) == (gain is None): raise RuntimeError("Either or gain or amplitude must be specified.") elif max_amplitude is not None: gain = round((power_in_dbm - u.volts2dBm(max_amplitude, Z=Z)) * 2) / 2 diff --git a/quam_builder/tools/qua_tools.py b/quam_builder/tools/qua_tools.py index c4363be8..372dc714 100644 --- a/quam_builder/tools/qua_tools.py +++ b/quam_builder/tools/qua_tools.py @@ -4,7 +4,6 @@ from typing import Any - # --- Type Aliases --- VoltageLevelType = Scalar[float] DurationType = Scalar[int] @@ -43,8 +42,7 @@ def validate_duration(duration: Optional[DurationType], param_name: str): raise TypeError(f"{param_name} ({duration_int}ns) must be non-negative.") if duration_int % CLOCK_CYCLE_NS != 0: raise TypeError( - f"{param_name} ({duration_int}ns) must be a multiple of " - f"{CLOCK_CYCLE_NS}ns." + f"{param_name} ({duration_int}ns) must be a multiple of " f"{CLOCK_CYCLE_NS}ns." ) if 0 < duration_int < MIN_PULSE_DURATION_NS: raise TypeError( diff --git a/quam_builder/tools/voltage_sequence/README.md b/quam_builder/tools/voltage_sequence/README.md index b15ce4bd..0843f66a 100644 --- a/quam_builder/tools/voltage_sequence/README.md +++ b/quam_builder/tools/voltage_sequence/README.md @@ -1,6 +1,6 @@ # VoltageSequence: Orchestrating DC Voltage Control in QUA -NOTE: This document is a brief intro into `GateSet` and `VoltageSequence`, not a full guide. For a more thorough guide on `GateSet`, `VoltageSequence` and `VirtualGateSet`, please refer to the [quantum_dots README](../../architecture/quantum_dots/README.md). +NOTE: This document is a brief intro into `GateSet` and `VoltageSequence`, not a full guide. For a more thorough guide on `GateSet`, `VoltageSequence` and `VirtualGateSet`, please refer to the [quantum_dots README](../../architecture/quantum_dots/README.md). ## 1. Introduction @@ -20,9 +20,9 @@ This framework is specifically designed to work with channels that have **sticky #### 1. Define QUAM `VoltageGate` objects for physical gates -- A `VoltageGate` channel is a Quantum Dot specific channel inheriting from QuAM's `SingleChannel` object. It adds to the `SingleChannel` by containing an `offset_parameter` and an `attenuation` value. +- A `VoltageGate` channel is a Quantum Dot specific channel inheriting from QuAM's `SingleChannel` object. It adds to the `SingleChannel` by containing an `offset_parameter` and an `attenuation` value. -- Below is an example of how a `VoltageGate` is instantiated. +- Below is an example of how a `VoltageGate` is instantiated. ```python from quam_builder.architecture.quantum_dots.components import VoltageGate @@ -58,7 +58,7 @@ This framework is specifically designed to work with channels that have **sticky - It is important to ensure that the string names used here match the string names in your QuAM machine -- If your channel object are already parented by a QuAM machine (i.e. `machine.channel["channel_p1"] = VoltageGate(...)`), then the channels cannot be re-parented into your GateSet. In this case, it is important to use the channel reference as such: +- If your channel object are already parented by a QuAM machine (i.e. `machine.channel["channel_p1"] = VoltageGate(...)`), then the channels cannot be re-parented into your GateSet. In this case, it is important to use the channel reference as such: ```python channels = { @@ -69,29 +69,29 @@ This framework is specifically designed to work with channels that have **sticky #### 3. Instantiate your GateSet with your channel mapping - ```python + ```python from quam_builder.architecture.quantum_dots.components import GateSet my_gate_set = GateSet(id="dot_plungers", channels=channels) ``` #### 4. Optionally, add `VoltageTuningPoint` macros to the `GateSet` - + - This is useful for when you have set points in your charge-stability that must be re-used in the experiment. GateSet can hold VoltageTuningPoints which can easily be accessed by VoltageSequence ```python my_gate_set.add_point(name="idle", voltages={"channel_P1": 0.1, "channel_P2": -0.05}, duration=1000) ``` - + Internally this adds a **`VoltageTuningPoint` to GateSet.macros** #### 5. Create a `VoltageSequence` from the `GateSet` - ```python + ```python voltage_seq = my_gate_set.new_sequence() ``` -- `voltage_seq` can be used in QUA programs to easily step/ramp to points. +- `voltage_seq` can be used in QUA programs to easily step/ramp to points. #### 6. Use `VoltageSequence` methods within a QUA `program()` to define voltage changes @@ -100,15 +100,15 @@ This framework is specifically designed to work with channels that have **sticky ```python with qua.program() as prog: voltage_seq = my_gate_set.new_sequence() - voltage_seq.step_to_point("idle") # Step to point "idle". ramp_to_point also valid, with a ramp_duration argument. + voltage_seq.step_to_point("idle") # Step to point "idle". ramp_to_point also valid, with a ramp_duration argument. voltage_seq.step_to_voltages(voltages = {...}, duration = ...) # In-case you would like to step to a point not saved as a macro in the GateSet, you can just define it here ``` ## 3. `GateSet` -A `GateSet` is a higher-level abstraction that collects a group of `VoltageGate` or `SingleChannel` channels and treats them as a single, coordinated object for unified control. This is especially useful in Quantum Dot architectures, where one often has many physical gate electrodes that must be tuned together. Instead of controlling each `VoltageGate`/`SingleChannel` channel in isolation, the GateSet allows +A `GateSet` is a higher-level abstraction that collects a group of `VoltageGate` or `SingleChannel` channels and treats them as a single, coordinated object for unified control. This is especially useful in Quantum Dot architectures, where one often has many physical gate electrodes that must be tuned together. Instead of controlling each `VoltageGate`/`SingleChannel` channel in isolation, the GateSet allows -- Unified control: Iterate, configure, and programme multiple gates at once through a single object +- Unified control: Iterate, configure, and programme multiple gates at once through a single object - Pre-defined DC points: Store named DC working points using add_point(...) @@ -125,7 +125,7 @@ A `GateSet` is a higher-level abstraction that collects a group of `VoltageGate` ```python # Assume gate_set has channels: {"P1": channel_P1, "P2": channel_P2, "B1": channel_B1} - # Only specify the voltages of a partial subset of all the gates in the GateSet. + # Only specify the voltages of a partial subset of all the gates in the GateSet. partial_voltages = {"P1": 0.3, "B1": -0.1} # resolve_voltages fills in missing channels, creating a complete voltages dict internally by replacing all the un-named gate voltages with 0.0V @@ -164,26 +164,26 @@ Implication: virtual gates do not maintain state across calls. To preserve a vir **Creating a `VoltageSequence`:** -- The sequence must be defined within a QUA program. +- The sequence must be defined within a QUA program. **Core Methods (used in `qua.program()` context):** -- `step_to_voltages(voltages: Dict[str, float], duration: int)` +- `step_to_voltages(voltages: Dict[str, float], duration: int)` Steps all specified channels directly to the given voltage levels and holds them for the specified duration (in nanoseconds). This creates immediate voltage changes without ramping. Both `voltages` values and `duration` can be QUA variables for dynamic control. ```python voltage_seq.step_to_voltages(voltages={"P1": 0.3, "P2": 0.1}, duration=1000) ``` -- `ramp_to_voltages(voltages: Dict[str, float], duration: int, ramp_duration: int)` +- `ramp_to_voltages(voltages: Dict[str, float], duration: int, ramp_duration: int)` Ramps all specified channels to the given voltage levels over the specified ramp duration, then holds them for the duration (both in nanoseconds). This provides smooth voltage transitions useful for avoiding voltage spikes that could affect sensitive quantum systems. All parameters can be QUA variables. ```python voltage_seq.ramp_to_voltages(voltages={"P1": 0.0}, duration=500, ramp_duration=40) ``` -- `step_to_point(name: str, duration: Optional[int] = None)` +- `step_to_point(name: str, duration: Optional[int] = None)` Steps all channels to the voltages defined in a predefined `VoltageTuningPoint` macro. If no duration is provided, uses the default duration from the tuning point definition. This enables quick transitions to well-defined system states. The `duration` parameter can be a QUA variable. ```python @@ -191,14 +191,14 @@ Implication: virtual gates do not maintain state across calls. To preserve a vir voltage_seq.step_to_point("readout", duration=2000) # Override default duration ``` -- `ramp_to_point(name: str, ramp_duration: int, duration: Optional[int] = None)` +- `ramp_to_point(name: str, ramp_duration: int, duration: Optional[int] = None)` Ramps all channels to the voltages defined in a predefined `VoltageTuningPoint` over the specified ramp duration, then holds them. Combines the smooth transitions of ramping with the convenience of predefined voltage states. Both `ramp_duration` and `duration` can be QUA variables. ```python voltage_seq.ramp_to_point("idle", ramp_duration=50, duration=1000) ``` -- `ramp_to_zero(ramp_duration: Optional[int] = None)` +- `ramp_to_zero(ramp_duration: Optional[int] = None)` Ramps the voltage on all channels in the GateSet to zero and resets the integrated voltage tracking for each channel. If no duration is specified, uses QUA's built-in `ramp_to_zero` command for immediate ramping. Essential for safely returning to a neutral state. The `ramp_duration` parameter can be a QUA variable. ```python @@ -206,7 +206,7 @@ Implication: virtual gates do not maintain state across calls. To preserve a vir voltage_seq.ramp_to_zero(ramp_duration=100) # Controlled ramp over 100ns ``` -- `apply_compensation_pulse(max_voltage: float = 0.49)` +- `apply_compensation_pulse(max_voltage: float = 0.49)` Applies a compensation pulse to each channel to counteract integrated voltage drift when tracking is enabled. The compensation amplitude is calculated based on the accumulated integrated voltage, with the pulse duration optimized to stay within the specified maximum voltage limit. Only available when `track_integrated_voltage=True`. ```python diff --git a/quam_builder/tools/voltage_sequence/sequence_state_tracker.py b/quam_builder/tools/voltage_sequence/sequence_state_tracker.py index 2d682d89..078080e0 100644 --- a/quam_builder/tools/voltage_sequence/sequence_state_tracker.py +++ b/quam_builder/tools/voltage_sequence/sequence_state_tracker.py @@ -94,9 +94,7 @@ def __init__( # Initialize state variables directly for the single element self._current_level_internal: Scalar[float] = 0.0 if enforce_qua_calcs: - self._current_level_internal = declare( - fixed, value=self._current_level_internal - ) + self._current_level_internal = declare(fixed, value=self._current_level_internal) # Whether to track integrated voltage self._track_integrated_voltage: bool = track_integrated_voltage # Stores accumulated voltage*duration*scale_factor @@ -214,9 +212,7 @@ def _ensure_qua_integrated_voltage_var(self) -> QuaVariable: self._current_py_val_before_promotion = current_py_val int_v_var = declare(int, value=current_py_val) self._integrated_voltage_qua_var = int_v_var - self._integrated_voltage_internal = ( - int_v_var # Update tracking to use the QUA var - ) + self._integrated_voltage_internal = int_v_var # Update tracking to use the QUA var return int_v_var else: # QUA Variable already exists for this element's integrated voltage @@ -298,11 +294,7 @@ def update_integrated_voltage( assign(int_v_var, int_v_var + ramp_contribution) else: # All inputs for ramp part are Python types ramp_contribution = int( - np.round( - avg_ramp_level - * ramp_duration - * INTEGRATED_VOLTAGE_SCALING_FACTOR - ) + np.round(avg_ramp_level * ramp_duration * INTEGRATED_VOLTAGE_SCALING_FACTOR) ) # _integrated_voltage_internal is guaranteed to be an int here self._integrated_voltage_internal += ramp_contribution @@ -325,18 +317,13 @@ def __init__(self, gate_set: GateSet | VirtualGateSet): channel, track_integrated_voltage=False ) - def update_voltage_dict_with_current( - self, voltages_dict: Dict[str, VoltageLevelType] - ): + def update_voltage_dict_with_current(self, voltages_dict: Dict[str, VoltageLevelType]): """ adds points that are not supplied to the voltages_dict """ self.update_tracking(voltages_dict=voltages_dict) - return { - name: tracker.current_level - for name, tracker in self._keep_levels_dict.items() - } + return {name: tracker.current_level for name, tracker in self._keep_levels_dict.items()} def update_tracking(self, voltages_dict: Dict[str, VoltageLevelType]): """ diff --git a/quam_builder/tools/voltage_sequence/voltage_sequence.py b/quam_builder/tools/voltage_sequence/voltage_sequence.py index bb50e70f..22c867e6 100644 --- a/quam_builder/tools/voltage_sequence/voltage_sequence.py +++ b/quam_builder/tools/voltage_sequence/voltage_sequence.py @@ -72,6 +72,7 @@ def round_amplitude(level): level = float(np.float16(level)) return level + class VoltageSequence: """ Manages the generation of a QUA sequence for setting and adjusting DC voltages @@ -133,14 +134,14 @@ def __init__( if self._keep_levels: self._keep_levels_tracker = KeepLevels(self.gate_set) - + self._batched_voltages = None self._prog_id = None - def _initialise_attenuation_qua_vars(self) -> None: + def _initialise_attenuation_qua_vars(self) -> None: """Lazy initiation of QUA variables that runs only at the start of the QUA program.""" current_program_scope = id(scopes_manager.program_scope) - if self._prog_id != current_program_scope: + if self._prog_id != current_program_scope: self._prog_id = current_program_scope self.attenuation_qua_variables = { @@ -156,11 +157,10 @@ def _initialise_attenuation_qua_vars(self) -> None: } if self.gate_set.adjust_for_attenuation: self._attenuated_delta_v_vars: Dict[str, QuaVariable] = { - ch_name: declare(fixed) - for ch_name in self.gate_set.channels.keys() - } + ch_name: declare(fixed) for ch_name in self.gate_set.channels.keys() + } - else: + else: return @contextmanager @@ -173,7 +173,7 @@ def simultaneous(self, duration: int = 16, ramp_duration: int = None): if self._batched_voltages: voltages_to_execute = self._batched_voltages.copy() self._batched_voltages = None - + # Cater for ramps if ramp_duration == 0 or ramp_duration is None: self.step_to_voltages(voltages_to_execute, duration) @@ -181,7 +181,7 @@ def simultaneous(self, duration: int = 16, ramp_duration: int = None): self.ramp_to_voltages(voltages_to_execute, ramp_duration, duration) else: self._batched_voltages = None - + def _get_temp_qua_var(self, name_suffix: str, var_type=fixed) -> QuaVariable: """Gets or declares a temporary QUA variable for internal calculations.""" # Use a prefix related to the VoltageSequence instance if multiple exist @@ -192,18 +192,14 @@ def _get_temp_qua_var(self, name_suffix: str, var_type=fixed) -> QuaVariable: return self._temp_qua_vars[internal_name] def _adjust_for_attenuation(self, channel, delta_v): - ch_name = next( - name for name, ch in self.gate_set.channels.items() if ch is channel - ) + ch_name = next(name for name, ch in self.gate_set.channels.items() if ch is channel) attenuation_scale = self.attenuation_qua_variables[ch_name] if is_qua_type(delta_v): unattenuated_delta_v = self._attenuated_delta_v_vars[ch_name] assign(unattenuated_delta_v, (delta_v * attenuation_scale) << ATTENUATION_BITSHIFT) else: unattenuated_delta_v = delta_v * ( - 10 ** (channel.attenuation / 20) - if hasattr(channel, "attenuation") - else 1 + 10 ** (channel.attenuation / 20) if hasattr(channel, "attenuation") else 1 ) return unattenuated_delta_v @@ -340,10 +336,8 @@ def _common_voltages_change( ) if self._keep_levels: - target_voltages_dict = ( - self._keep_levels_tracker.update_voltage_dict_with_current( - target_voltages_dict - ) + target_voltages_dict = self._keep_levels_tracker.update_voltage_dict_with_current( + target_voltages_dict ) full_target_voltages_dict = self.gate_set.resolve_voltages(target_voltages_dict) @@ -391,9 +385,7 @@ def _common_voltages_change( ) tracker.current_level = target_voltage - def step_to_voltages( - self, voltages: Dict[str, VoltageLevelType], duration: DurationType - ): + def step_to_voltages(self, voltages: Dict[str, VoltageLevelType], duration: DurationType): """ Steps all specified channels directly to the given voltage levels. @@ -507,9 +499,7 @@ def step_to_point(self, name: str, duration: Optional[DurationType] = None): ) tuning_point: VoltageTuningPoint = tuning_point_macro effective_duration = duration if duration is not None else tuning_point.duration - self._common_voltages_change( - tuning_point.voltages, effective_duration, ramp_duration=None - ) + self._common_voltages_change(tuning_point.voltages, effective_duration, ramp_duration=None) def ramp_to_point( self, @@ -574,9 +564,7 @@ def _calculate_python_compensation_params( ideal_dur = abs(py_int_v * COMPENSATION_SCALING_FACTOR / max_voltage) py_comp_dur = max(ideal_dur, MIN_COMPENSATION_DURATION_NS) py_comp_dur = ( - (int(np.ceil(py_comp_dur)) + CLOCK_CYCLE_NS - 1) - // CLOCK_CYCLE_NS - * CLOCK_CYCLE_NS + (int(np.ceil(py_comp_dur)) + CLOCK_CYCLE_NS - 1) // CLOCK_CYCLE_NS * CLOCK_CYCLE_NS ) py_comp_dur = max(py_comp_dur, DEFAULT_QUA_COMPENSATION_DURATION_NS) @@ -689,9 +677,7 @@ def apply_compensation_pulse( raise ValueError("max_voltage must be positive.") if self._keep_levels: - zero_dict = { - name: 0.0 for name in self._keep_levels_tracker._keep_levels_dict - } + zero_dict = {name: 0.0 for name in self._keep_levels_tracker._keep_levels_dict} else: zero_dict = {} @@ -702,7 +688,10 @@ def apply_compensation_pulse( DEFAULT_WF_AMPLITUDE = channel_obj.operations[DEFAULT_PULSE_NAME].amplitude DEFAULT_AMPLITUDE_BITSHIFT = int(np.log2(1 / DEFAULT_WF_AMPLITUDE)) opx_voltage_limit = ( - 2.5 if hasattr(channel_obj.opx_output, "output_mode") and channel_obj.opx_output.output_mode == "amplified" else 0.5 + 2.5 + if hasattr(channel_obj.opx_output, "output_mode") + and channel_obj.opx_output.output_mode == "amplified" + else 0.5 ) if self.gate_set.adjust_for_attenuation and hasattr(channel_obj, "attenuation"): @@ -716,9 +705,7 @@ def apply_compensation_pulse( comp_amp_val: VoltageLevelType - if not is_qua_type(tracker.integrated_voltage) and not is_qua_type( - current_v - ): + if not is_qua_type(tracker.integrated_voltage) and not is_qua_type(current_v): py_comp_amp, py_comp_dur = self._calculate_python_compensation_params( tracker, max_voltage ) @@ -781,8 +768,7 @@ def _perform_ramp_to_zero_with_duration( with else_(): # Duration is 0, effectively a step channel_obj.play( DEFAULT_PULSE_NAME, - amplitude_scale=-current_v - * np.round(1.0 / DEFAULT_WF_AMPLITUDE, 10), + amplitude_scale=-current_v * np.round(1.0 / DEFAULT_WF_AMPLITUDE, 10), duration=ramp_duration >> 2, validate=False, # Do not validate as pulse may not exist yet ) diff --git a/tests/architecture/quantum_dots/components/test_rectangular_virtual_gate_set.py b/tests/architecture/quantum_dots/components/test_rectangular_virtual_gate_set.py index 1449b89f..c88fed62 100644 --- a/tests/architecture/quantum_dots/components/test_rectangular_virtual_gate_set.py +++ b/tests/architecture/quantum_dots/components/test_rectangular_virtual_gate_set.py @@ -12,8 +12,7 @@ def _channels(names): return { - name: SingleChannel(id=name, opx_output=("con", idx + 1)) - for idx, name in enumerate(names) + name: SingleChannel(id=name, opx_output=("con", idx + 1)) for idx, name in enumerate(names) } @@ -127,9 +126,7 @@ def test_rectangular_roundtrip_visualisation(): resolved = gate_set.resolve_voltages( {gate: value for gate, value in zip(source_gates, source_vector)} ) - resolved_samples.append( - np.array([resolved["P1"], resolved["P2"], resolved["P3"]]) - ) + resolved_samples.append(np.array([resolved["P1"], resolved["P2"], resolved["P3"]])) resolved_samples = np.array(resolved_samples) np.testing.assert_allclose(resolved_samples, physical_samples, rtol=1e-9, atol=1e-9) diff --git a/tests/architecture/quantum_dots/components/test_virtual_gate_set.py b/tests/architecture/quantum_dots/components/test_virtual_gate_set.py index 51af4e19..3a3799a9 100644 --- a/tests/architecture/quantum_dots/components/test_virtual_gate_set.py +++ b/tests/architecture/quantum_dots/components/test_virtual_gate_set.py @@ -45,9 +45,7 @@ def test_virtualization_layer_resolve_square_matrix(): source_vector = np.array([0.7, -0.4]) expected_physical = np.linalg.inv(np.asarray(matrix)) @ source_vector - resolved = layer.resolve_voltages( - {"v_comp": source_vector[0], "v_tilt": source_vector[1]} - ) + resolved = layer.resolve_voltages({"v_comp": source_vector[0], "v_tilt": source_vector[1]}) assert np.isclose(resolved["P1"], expected_physical[0]) assert np.isclose(resolved["P2"], expected_physical[1]) @@ -55,9 +53,7 @@ def test_virtualization_layer_resolve_square_matrix(): def test_virtual_gate_set_resolve_single_layer_square(gate_set): matrix = [[1.0, 0.1], [0.0, 0.8]] - gate_set.add_layer( - source_gates=["Vx", "Vy"], target_gates=["P1", "P2"], matrix=matrix - ) + gate_set.add_layer(source_gates=["Vx", "Vy"], target_gates=["P1", "P2"], matrix=matrix) source_vector = np.array([0.5, 0.2]) expected = np.linalg.inv(np.asarray(matrix)) @ source_vector @@ -69,12 +65,8 @@ def test_virtual_gate_set_resolve_single_layer_square(gate_set): def test_virtual_gate_set_resolve_stacked_layers_square(gate_set): - gate_set.add_layer( - source_gates=["V_mid"], target_gates=["P1"], matrix=[[2.0]] - ) - gate_set.add_layer( - source_gates=["V_top"], target_gates=["V_mid"], matrix=[[0.5]] - ) + gate_set.add_layer(source_gates=["V_mid"], target_gates=["P1"], matrix=[[2.0]]) + gate_set.add_layer(source_gates=["V_top"], target_gates=["V_mid"], matrix=[[0.5]]) resolved = gate_set.resolve_voltages({"V_top": 1.2}) @@ -83,9 +75,7 @@ def test_virtual_gate_set_resolve_stacked_layers_square(gate_set): def test_virtual_gate_set_unknown_channel_rejected(gate_set): - gate_set.add_layer( - source_gates=["Vx"], target_gates=["P1"], matrix=[[1.0]] - ) + gate_set.add_layer(source_gates=["Vx"], target_gates=["P1"], matrix=[[1.0]]) with pytest.raises(ValueError): gate_set.resolve_voltages({"Vx": 0.2, "unknown": 0.1}) @@ -93,8 +83,7 @@ def test_virtual_gate_set_unknown_channel_rejected(gate_set): def _make_channels(names): return { - name: SingleChannel(id=name, opx_output=("con1", idx + 1)) - for idx, name in enumerate(names) + name: SingleChannel(id=name, opx_output=("con1", idx + 1)) for idx, name in enumerate(names) } diff --git a/tests/conftest.py b/tests/conftest.py index af1b2bcb..9aa08b52 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,7 +4,6 @@ from qualang_tools.wirer.connectivity import wiring_spec - # Extend WiringLineType with quantum-dot specific entries when missing in the installed # qualang_tools version. This keeps the tests compatible with older releases. _existing_members = {member.name: member.value for member in wiring_spec.WiringLineType} diff --git a/tests/pylint_plugin/__init__.py b/tests/pylint_plugin/__init__.py index 356edc8b..7a33550e 100644 --- a/tests/pylint_plugin/__init__.py +++ b/tests/pylint_plugin/__init__.py @@ -1 +1 @@ -"""Tests for the pylint QUA plugin.""" \ No newline at end of file +"""Tests for the pylint QUA plugin.""" diff --git a/tests/pylint_plugin/sample_qua_file.py b/tests/pylint_plugin/sample_qua_file.py index 97a2f31e..9ce7c612 100644 --- a/tests/pylint_plugin/sample_qua_file.py +++ b/tests/pylint_plugin/sample_qua_file.py @@ -27,7 +27,6 @@ Math, ) - # ============================================================================= # TEST CASE 1: Regular Python code - pylint SHOULD flag these # ============================================================================= diff --git a/tests/test_qubit_base_transmon.py b/tests/test_qubit_base_transmon.py index b0840660..ab664fb7 100644 --- a/tests/test_qubit_base_transmon.py +++ b/tests/test_qubit_base_transmon.py @@ -43,7 +43,7 @@ def test_class_attribute(): def test_name(): for name in ["AbCd", "q12", 0, 51]: transmon = BaseTransmon(id=name) - if type(name) == str: + if isinstance(name, str): assert transmon.name == name else: assert transmon.name == f"q{name}" diff --git a/tests/virtual_gates/test_virtual_voltage_sequence.py b/tests/virtual_gates/test_virtual_voltage_sequence.py index ce43f3d5..61ff8f13 100644 --- a/tests/virtual_gates/test_virtual_voltage_sequence.py +++ b/tests/virtual_gates/test_virtual_voltage_sequence.py @@ -127,8 +127,7 @@ def test_vgs_step_to_virtual_level_qua_voltage(virtual_gate_set: VirtualGateSet) duration=30, ) qua.play( - DEFAULT_PULSE_NAME - * qua.amp((((0.0 + ((0.0 * exp_qua_v_g1_level) + 0.6)) - 0.0) << 2)), + DEFAULT_PULSE_NAME * qua.amp((((0.0 + ((0.0 * exp_qua_v_g1_level) + 0.6)) - 0.0) << 2)), "ch2", duration=30, ) @@ -189,9 +188,7 @@ def test_ramp_to_voltages_simple(machine): def test_ramp_to_point_simple(machine): """Tests a simple ramp_to_point operation.""" - machine.gate_set.add_point( - "p_ramp", voltages={"ch1": 0.15, "ch2": 0.05}, duration=200 - ) + machine.gate_set.add_point("p_ramp", voltages={"ch1": 0.15, "ch2": 0.05}, duration=200) with qua.program() as prog: seq = machine.gate_set.new_sequence(track_integrated_voltage=False) seq.ramp_to_point("p_ramp", ramp_duration=80) @@ -210,15 +207,11 @@ def test_ramp_to_point_simple(machine): def test_step_then_ramp(machine): """Tests a step operation followed by a ramp operation.""" - machine.gate_set.add_point( - "p_step", voltages={"ch1": 0.1, "ch2": 0.1}, duration=100 - ) + machine.gate_set.add_point("p_step", voltages={"ch1": 0.1, "ch2": 0.1}, duration=100) with qua.program() as prog: seq = machine.gate_set.new_sequence(track_integrated_voltage=False) seq.step_to_point("p_step") # Current level: ch1=0.1, ch2=0.1 - seq.ramp_to_voltages( - voltages={"ch1": 0.3, "ch2": -0.1}, duration=160, ramp_duration=80 - ) + seq.ramp_to_voltages(voltages={"ch1": 0.3, "ch2": -0.1}, duration=160, ramp_duration=80) ast = ProgramTreeBuilder().build(prog) with qua.program() as expected_program: @@ -238,9 +231,7 @@ def test_step_then_ramp(machine): def test_ramp_then_step(machine): """Tests a ramp operation followed by a step operation.""" - machine.gate_set.add_point( - "p_final", voltages={"ch1": 0.05, "ch2": -0.05}, duration=500 - ) + machine.gate_set.add_point("p_final", voltages={"ch1": 0.05, "ch2": -0.05}, duration=500) with qua.program() as prog: seq = machine.gate_set.new_sequence(track_integrated_voltage=False) seq.ramp_to_voltages( @@ -300,9 +291,7 @@ def test_ramp_to_voltages_with_qua_ramp_duration(machine): seq = machine.gate_set.new_sequence(track_integrated_voltage=False) qua_ramp_dur = qua.declare(int) qua.assign(qua_ramp_dur, 80) # ns - seq.ramp_to_voltages( - voltages={"ch1": 0.2}, duration=160, ramp_duration=qua_ramp_dur - ) + seq.ramp_to_voltages(voltages={"ch1": 0.2}, duration=160, ramp_duration=qua_ramp_dur) ast = ProgramTreeBuilder().build(prog) with qua.program() as expected_program: @@ -312,18 +301,14 @@ def test_ramp_to_voltages_with_qua_ramp_duration(machine): qua.assign(expected_qua_ramp_dur, 80) # ch1: 0.0 -> 0.2 (delta=0.2), ramp=80(20), hold=160(40) -> 40 # ch2: 0.0 -> 0.0 (delta=0.0), ramp=80(20), hold=160(40) -> 40 - qua.assign( - _vseq_tmp_ch1_ramp_rate, 0.2 * qua.Math.div(1.0, expected_qua_ramp_dur) - ) + qua.assign(_vseq_tmp_ch1_ramp_rate, 0.2 * qua.Math.div(1.0, expected_qua_ramp_dur)) qua.play( qua.ramp(_vseq_tmp_ch1_ramp_rate), "ch1", duration=expected_qua_ramp_dur >> 2, ) qua.wait(40, "ch1") - qua.assign( - _vseq_tmp_ch2_ramp_rate, 0.0 * qua.Math.div(1.0, expected_qua_ramp_dur) - ) + qua.assign(_vseq_tmp_ch2_ramp_rate, 0.0 * qua.Math.div(1.0, expected_qua_ramp_dur)) qua.play( qua.ramp(_vseq_tmp_ch2_ramp_rate), "ch2", diff --git a/tests/voltage_sequence/conftest.py b/tests/voltage_sequence/conftest.py index 35564e51..f3fe9dda 100644 --- a/tests/voltage_sequence/conftest.py +++ b/tests/voltage_sequence/conftest.py @@ -26,4 +26,4 @@ def machine(): }, ), ) - return machine \ No newline at end of file + return machine diff --git a/tests/voltage_sequence/test_voltage_sequence.py b/tests/voltage_sequence/test_voltage_sequence.py index 1b9119ee..767ed209 100644 --- a/tests/voltage_sequence/test_voltage_sequence.py +++ b/tests/voltage_sequence/test_voltage_sequence.py @@ -80,9 +80,7 @@ def test_go_to_multiple_points(machine): def test_step_to_point_with_custom_duration(machine): """Tests overriding the point's default duration in step_to_point.""" - machine.gate_set.add_point( - "p1", voltages={"ch1": 0.1}, duration=100 - ) # Default duration + machine.gate_set.add_point("p1", voltages={"ch1": 0.1}, duration=100) # Default duration with qua.program() as prog: seq = machine.gate_set.new_sequence() seq.step_to_point("p1", duration=60) @@ -129,9 +127,7 @@ def test_step_to_voltages_multiple_channels(machine): def test_step_to_voltages_then_step_to_point(machine): """Tests a step_to_voltages operation followed by a step_to_point.""" - machine.gate_set.add_point( - "p_after_step", voltages={"ch1": 0.2, "ch2": 0.2}, duration=80 - ) + machine.gate_set.add_point("p_after_step", voltages={"ch1": 0.2, "ch2": 0.2}, duration=80) with qua.program() as prog: seq = machine.gate_set.new_sequence() seq.step_to_voltages(voltages={"ch1": 0.1}, duration=100) diff --git a/tests_against_server/voltage_gate_sequence/conftest.py b/tests_against_server/voltage_gate_sequence/conftest.py index aa246b62..f79bb2df 100644 --- a/tests_against_server/voltage_gate_sequence/conftest.py +++ b/tests_against_server/voltage_gate_sequence/conftest.py @@ -39,16 +39,12 @@ def machine(): id="test_gate_set", channels={ "ch1": VoltageGate( - opx_output=LFFEMAnalogOutputPort( - "con1", 5, 6, upsampling_mode="pulse" - ), + opx_output=LFFEMAnalogOutputPort("con1", 5, 6, upsampling_mode="pulse"), sticky=StickyChannelAddon(duration=100, digital=False), attenuation=10, ), "ch2": VoltageGate( - opx_output=LFFEMAnalogOutputPort( - "con1", 5, 3, upsampling_mode="pulse" - ), + opx_output=LFFEMAnalogOutputPort("con1", 5, 3, upsampling_mode="pulse"), sticky=StickyChannelAddon(duration=100, digital=False), attenuation=10, ), diff --git a/tests_against_server/voltage_gate_sequence/test_compensation_ramps.py b/tests_against_server/voltage_gate_sequence/test_compensation_ramps.py index f25ee0e8..a64304b4 100644 --- a/tests_against_server/voltage_gate_sequence/test_compensation_ramps.py +++ b/tests_against_server/voltage_gate_sequence/test_compensation_ramps.py @@ -7,18 +7,10 @@ def test_python_voltage_sequence_ramps(qmm, machine: QuamGateSet): with qua.program() as program: seq = machine.gate_set.new_sequence(track_integrated_voltage=True) - seq.ramp_to_voltages( - voltages={"ch1": 0.01, "ch2": -0.01}, duration=100, ramp_duration=16 - ) - seq.ramp_to_voltages( - voltages={"ch1": 0.02, "ch2": -0.02}, duration=100, ramp_duration=16 - ) - seq.ramp_to_voltages( - voltages={"ch1": 0.03, "ch2": -0.03}, duration=100, ramp_duration=16 - ) - seq.ramp_to_voltages( - voltages={"ch1": 0, "ch2": 0}, duration=16, ramp_duration=16 - ) + seq.ramp_to_voltages(voltages={"ch1": 0.01, "ch2": -0.01}, duration=100, ramp_duration=16) + seq.ramp_to_voltages(voltages={"ch1": 0.02, "ch2": -0.02}, duration=100, ramp_duration=16) + seq.ramp_to_voltages(voltages={"ch1": 0.03, "ch2": -0.03}, duration=100, ramp_duration=16) + seq.ramp_to_voltages(voltages={"ch1": 0, "ch2": 0}, duration=16, ramp_duration=16) seq.apply_compensation_pulse(max_voltage=0.03) seq.step_to_voltages(voltages={"ch1": 0, "ch2": 0}, duration=16) diff --git a/tests_against_server/voltage_gate_sequence/test_square_pulses.py b/tests_against_server/voltage_gate_sequence/test_square_pulses.py index fc329cba..c6c88b10 100644 --- a/tests_against_server/voltage_gate_sequence/test_square_pulses.py +++ b/tests_against_server/voltage_gate_sequence/test_square_pulses.py @@ -156,15 +156,9 @@ def test_qua_voltage_sequence(qmm, machine: QuamGateSet): amplitude_2 = qua.declare(qua.fixed, value=0.02) amplitude_3 = qua.declare(qua.fixed, value=0.03) seq = machine.gate_set.new_sequence(track_integrated_voltage=True) - seq.step_to_voltages( - voltages={"ch1": amplitude_1, "ch2": -amplitude_1}, duration=100 - ) - seq.step_to_voltages( - voltages={"ch1": amplitude_2, "ch2": -amplitude_2}, duration=100 - ) - seq.step_to_voltages( - voltages={"ch1": amplitude_3, "ch2": -amplitude_3}, duration=100 - ) + seq.step_to_voltages(voltages={"ch1": amplitude_1, "ch2": -amplitude_1}, duration=100) + seq.step_to_voltages(voltages={"ch1": amplitude_2, "ch2": -amplitude_2}, duration=100) + seq.step_to_voltages(voltages={"ch1": amplitude_3, "ch2": -amplitude_3}, duration=100) seq.apply_compensation_pulse(max_voltage=0.03) qmm, samples = simulate_program(qmm, machine, program, int(2e3)) @@ -178,15 +172,9 @@ def test_qua_voltage_sequence_durations(qmm, machine: QuamGateSet): amplitude_2 = qua.declare(qua.fixed, value=0.02) amplitude_3 = qua.declare(qua.fixed, value=0.03) seq = machine.gate_set.new_sequence(track_integrated_voltage=True) - seq.step_to_voltages( - voltages={"ch1": amplitude_1, "ch2": -amplitude_1}, duration=duration - ) - seq.step_to_voltages( - voltages={"ch1": amplitude_2, "ch2": -amplitude_2}, duration=duration - ) - seq.step_to_voltages( - voltages={"ch1": amplitude_3, "ch2": -amplitude_3}, duration=duration - ) + seq.step_to_voltages(voltages={"ch1": amplitude_1, "ch2": -amplitude_1}, duration=duration) + seq.step_to_voltages(voltages={"ch1": amplitude_2, "ch2": -amplitude_2}, duration=duration) + seq.step_to_voltages(voltages={"ch1": amplitude_3, "ch2": -amplitude_3}, duration=duration) seq.step_to_voltages(voltages={"ch1": 0, "ch2": 0}, duration=16) qmm, samples = simulate_program(qmm, machine, program, int(2e3)) @@ -196,9 +184,7 @@ def test_qua_voltage_sequence_durations(qmm, machine: QuamGateSet): ) # duration * 2 because we are validating on OPX1k with 2GS/s -def test_qua_voltage_sequence_double_loop( - qmm, machine: QuamGateSet | QuamVirtualGateSet -): +def test_qua_voltage_sequence_double_loop(qmm, machine: QuamGateSet | QuamVirtualGateSet): with qua.program() as program: amplitude_1 = qua.declare(qua.fixed) amplitude_2 = qua.declare(qua.fixed) @@ -223,9 +209,7 @@ def test_qua_voltage_sequence_double_loop( validate_compensation(samples, allowed=100.0) -def test_qua_voltage_sequence_single_loop( - qmm, machine: QuamGateSet | QuamVirtualGateSet -): +def test_qua_voltage_sequence_single_loop(qmm, machine: QuamGateSet | QuamVirtualGateSet): with qua.program() as program: amplitude_1 = qua.declare(qua.fixed, value=-0.02) amplitude_2 = qua.declare(qua.fixed) @@ -235,9 +219,7 @@ def test_qua_voltage_sequence_single_loop( with qua.for_each_(amplitude_2, [-0.02, 0.02]): seq.step_to_point("init") seq.step_to_point("init_return") - seq.step_to_voltages( - voltages={"ch1": amplitude_1, "ch2": amplitude_2}, duration=2000 - ) + seq.step_to_voltages(voltages={"ch1": amplitude_1, "ch2": amplitude_2}, duration=2000) seq.step_to_point("init_return") seq.step_to_point("init") seq.apply_compensation_pulse(max_voltage=0.02) @@ -259,14 +241,10 @@ def test_python_voltage_sequence_zero_comp(qmm, machine: QuamGateSet): validate_compensation(samples) -def test_python_voltage_sequence_virtual_gates( - qmm, virtual_machine: QuamVirtualGateSet -): +def test_python_voltage_sequence_virtual_gates(qmm, virtual_machine: QuamVirtualGateSet): with qua.program() as program: - seq = virtual_machine.virtual_gate_set.new_sequence( - track_integrated_voltage=True - ) + seq = virtual_machine.virtual_gate_set.new_sequence(track_integrated_voltage=True) seq.step_to_voltages(voltages={"detuning": 0.01}, duration=100) seq.step_to_voltages(voltages={"detuning": 0.02}, duration=100) seq.step_to_voltages(voltages={"detuning": 0.03}, duration=100) @@ -284,9 +262,7 @@ def test_python_voltage_sequence_virtual_gates_and_elements( seq.step_to_voltages(voltages={"detuning": 0.1, "ch1":0.2}, duration=100) """ with qua.program() as program: - seq = virtual_machine.virtual_gate_set.new_sequence( - track_integrated_voltage=True - ) + seq = virtual_machine.virtual_gate_set.new_sequence(track_integrated_voltage=True) seq.step_to_voltages(voltages={"detuning": 0.01, "ch1": 0.02}, duration=100) seq.apply_compensation_pulse(max_voltage=0.03) @@ -295,13 +271,10 @@ def test_python_voltage_sequence_virtual_gates_and_elements( def test_keep_levels(qmm, virtual_machine: QuamVirtualGateSet): - """ """ virtual_machine.virtual_gate_set.channels["ch1"].attenuation = 0 virtual_machine.virtual_gate_set.channels["ch2"].attenuation = 0 with qua.program() as program: - seq = virtual_machine.virtual_gate_set.new_sequence( - track_integrated_voltage=True - ) + seq = virtual_machine.virtual_gate_set.new_sequence(track_integrated_voltage=True) seq.step_to_voltages(voltages={"ch1": 0.02}, duration=100) seq.step_to_voltages(voltages={"ch2": 0.01}, duration=100) seq.step_to_voltages(voltages={"ch1": 0.03}, duration=100) diff --git a/tests_against_server/voltage_gate_sequence/validation_utils.py b/tests_against_server/voltage_gate_sequence/validation_utils.py index 9d74daac..db6c7a32 100644 --- a/tests_against_server/voltage_gate_sequence/validation_utils.py +++ b/tests_against_server/voltage_gate_sequence/validation_utils.py @@ -5,6 +5,7 @@ import pytest from qm import SimulationConfig, QuantumMachinesManager, generate_qua_script + pytest.importorskip("qm_saas") from qm_saas import QOPVersion, QmSaas import matplotlib.pyplot as plt