From f099733996c56c598d1926a35ced33ffe19feff6 Mon Sep 17 00:00:00 2001 From: Stavros Efthymiou <35475381+stavros11@users.noreply.github.com> Date: Tue, 9 Jan 2024 19:01:02 +0400 Subject: [PATCH 1/7] BREAKING CHANGE: Remove start and stop from instruments and platform --- README.md | 6 ----- doc/source/main-documentation/qibolab.rst | 9 ++----- doc/source/tutorials/pulses.rst | 9 ------- examples/fidelity_example.py | 6 ----- examples/minimum_working_example.py | 6 ----- src/qibolab/backends.py | 8 ++---- src/qibolab/couplers.py | 16 ++++++++++- src/qibolab/dummy.py | 9 ++++--- src/qibolab/instruments/abstract.py | 26 ++++-------------- src/qibolab/instruments/dummy.py | 12 +++------ src/qibolab/instruments/oscillator.py | 19 ++++++------- src/qibolab/instruments/qblox/controller.py | 30 +++++++-------------- src/qibolab/instruments/qm/driver.py | 14 +--------- src/qibolab/instruments/rfsoc/driver.py | 6 ----- src/qibolab/instruments/zhinst.py | 6 ----- src/qibolab/platform.py | 26 ------------------ src/qibolab/qubits.py | 18 +++++++++++-- tests/conftest.py | 1 - tests/dummy_qrc/qm.py | 16 ++++++----- tests/dummy_qrc/rfsoc.py | 8 +++--- tests/dummy_qrc/zurich.py | 13 ++++----- tests/test_backends.py | 3 --- tests/test_dummy.py | 3 --- tests/test_instruments_oscillator.py | 8 ------ tests/test_instruments_qm.py | 6 ----- tests/test_instruments_qmsim.py | 1 - tests/test_instruments_rfsoc.py | 8 +++--- tests/test_instruments_zhinst.py | 20 -------------- tests/test_platform.py | 8 ------ 29 files changed, 91 insertions(+), 230 deletions(-) diff --git a/README.md b/README.md index b8e05fbea..cf6234e2c 100644 --- a/README.md +++ b/README.md @@ -58,10 +58,6 @@ platform = create_platform("my_platform") # Connects to lab instruments using the details specified in the calibration settings. platform.connect() -# Configures instruments using the loaded calibration settings. -platform.setup() -# Turns on the local oscillators -platform.start() # Execute a pulse sequence options = ExecutionParameters(nshots=1000) @@ -70,8 +66,6 @@ results = platform.execute_pulse_sequence(sequence, options) # Print the acquired shots print(results.samples) -# Turn off lab instruments -platform.stop() # Disconnect from the instruments platform.disconnect() ``` diff --git a/doc/source/main-documentation/qibolab.rst b/doc/source/main-documentation/qibolab.rst index 0011fc589..3d919d623 100644 --- a/doc/source/main-documentation/qibolab.rst +++ b/doc/source/main-documentation/qibolab.rst @@ -14,7 +14,7 @@ The API reference section provides a description of all the attributes and metho In the platform, the main methods can be divided in different sections: - functions save and change qubit parameters (``dump``, ``update``) -- functions to coordinate the instruments (``connect``, ``setup``, ``start``, ``stop``, ``disconnect``) +- functions to coordinate the instruments (``connect``, ``setup``, ``disconnect``) - functions to execute experiments (``execute_pulse_sequence``, ``execute_pulse_sequences``, ``sweep``) - functions to initialize gates (``create_RX90_pulse``, ``create_RX_pulse``, ``create_CZ_pulse``, ``create_MZ_pulse``, ``create_qubit_drive_pulse``, ``create_qubit_readout_pulse``, ``create_RX90_drag_pulse``, ``create_RX_drag_pulse``) - setters and getters of channel/qubit parameters (local oscillator parameters, attenuations, gain and biases) @@ -29,13 +29,11 @@ For example, let's first define a platform (that we consider to be a single qubi platform = create_platform("dummy") -Now we connect and start the instruments (note that we, the user, do not need to know which instruments are connected). +Now we connect to the instruments (note that we, the user, do not need to know which instruments are connected). .. testcode:: python platform.connect() - platform.setup() - platform.start() We can easily print some of the parameters of the channels (similarly we can set those, if needed): @@ -93,7 +91,6 @@ Finally, we can stop instruments and close connections. .. testcode:: python - platform.stop() platform.disconnect() @@ -719,8 +716,6 @@ Instruments all implement a set of methods: - connect - setup -- start -- stop - disconnect While the controllers, the main instruments in a typical setup, add other two methods: diff --git a/doc/source/tutorials/pulses.rst b/doc/source/tutorials/pulses.rst index 45c087280..51fa61d76 100644 --- a/doc/source/tutorials/pulses.rst +++ b/doc/source/tutorials/pulses.rst @@ -66,19 +66,10 @@ pulse sequence according to the number of shots ``nshots`` specified. # Connects to lab instruments using the details specified in the calibration settings. platform.connect() - # Configures instruments using the loaded calibration settings. - platform.setup() - - # Turns on the local oscillators - platform.start() - # Executes a pulse sequence. options = ExecutionParameters(nshots=1000, relaxation_time=100) results = platform.execute_pulse_sequence(sequence, options=options) - # Turn off lab instruments - platform.stop() - # Disconnect from the instruments platform.disconnect() diff --git a/examples/fidelity_example.py b/examples/fidelity_example.py index 7238b485b..a2f2e5612 100644 --- a/examples/fidelity_example.py +++ b/examples/fidelity_example.py @@ -7,16 +7,10 @@ # Connects to lab instruments using the details specified in the calibration settings. platform.connect() -# Configures instruments using the loaded calibration settings. -platform.setup() -# Turns on the local oscillators -platform.start() # Executes a pulse sequence. results = platform.measure_fidelity(qubits=[1, 2, 3, 4], nshots=3000) print( f"results[qubit] (rotation_angle, threshold, fidelity, assignment_fidelity): {results}" ) -# Turn off lab instruments -platform.stop() # Disconnect from the instruments platform.disconnect() diff --git a/examples/minimum_working_example.py b/examples/minimum_working_example.py index 1cfd5f8d1..3213c70aa 100644 --- a/examples/minimum_working_example.py +++ b/examples/minimum_working_example.py @@ -37,14 +37,8 @@ # Connects to lab instruments using the details specified in the calibration settings. platform.connect() -# Configures instruments using the loaded calibration settings. -platform.setup() -# Turns on the local oscillators -platform.start() # Executes a pulse sequence. results = platform.execute_pulse_sequence(sequence, nshots=3000) print(f"results (amplitude, phase, i, q): {results}") -# Turn off lab instruments -platform.stop() # Disconnect from the instruments platform.disconnect() diff --git a/src/qibolab/backends.py b/src/qibolab/backends.py index aa2e56b53..8c4d49c56 100644 --- a/src/qibolab/backends.py +++ b/src/qibolab/backends.py @@ -97,13 +97,11 @@ def execute_circuit(self, circuit, initial_state=None, nshots=1000): if not self.platform.is_connected: self.platform.connect() - self.platform.setup() - self.platform.start() + readout = self.platform.execute_pulse_sequence( sequence, ExecutionParameters(nshots=nshots), ) - self.platform.stop() result = MeasurementOutcomes(circuit.measurements, self, nshots=nshots) self.assign_measurements(measurement_map, readout) return result @@ -145,13 +143,11 @@ def execute_circuits(self, circuits, initial_state=None, nshots=1000): if not self.platform.is_connected: self.platform.connect() - self.platform.setup() - self.platform.start() + readout = self.platform.execute_pulse_sequences( sequences, ExecutionParameters(nshots=nshots), ) - self.platform.stop() results = [] readout = {k: deque(v) for k, v in readout.items()} diff --git a/src/qibolab/couplers.py b/src/qibolab/couplers.py index 8b525d4f2..d05563e93 100644 --- a/src/qibolab/couplers.py +++ b/src/qibolab/couplers.py @@ -25,7 +25,7 @@ class Coupler: native_pulse: CouplerNatives = field(default_factory=CouplerNatives) "For now this only contains the calibrated pulse to activate the coupler." - flux: Optional[Channel] = None + _flux: Optional[Channel] = None "flux (:class:`qibolab.platforms.utils.Channel`): Channel used to send flux pulses to the qubit." # TODO: With topology or conectivity @@ -33,6 +33,20 @@ class Coupler: qubits: Dict = field(default_factory=dict) "Qubits the coupler acts on" + def __post_init__(self): + if self.flux is not None and self.sweetspot != 0: + self.flux.offset = self.sweetspot + + @property + def flux(self): + return self._flux + + @flux.setter + def flux(self, channel): + if self.sweetspot != 0: + channel.offset = self.sweetspot + self._flux = channel + @property def channels(self): if self.flux is not None: diff --git a/src/qibolab/dummy.py b/src/qibolab/dummy.py index b6e16bd6f..179af429d 100644 --- a/src/qibolab/dummy.py +++ b/src/qibolab/dummy.py @@ -42,17 +42,18 @@ def create_dummy(with_couplers: bool = True): # Create channel objects nqubits = runcard["nqubits"] channels = ChannelMap() - channels |= Channel("readout", port=instrument["readout"]) + channels |= Channel("readout", port=instrument.ports("readout")) channels |= ( - Channel(f"drive-{i}", port=instrument[f"drive-{i}"]) for i in range(nqubits) + Channel(f"drive-{i}", port=instrument.ports(f"drive-{i}")) + for i in range(nqubits) ) channels |= ( - Channel(f"flux-{i}", port=instrument[f"flux-{i}"]) for i in range(nqubits) + Channel(f"flux-{i}", port=instrument.ports(f"flux-{i}")) for i in range(nqubits) ) channels |= Channel("twpa", port=None) if with_couplers: channels |= ( - Channel(f"flux_coupler-{c}", port=instrument[f"flux_coupler-{c}"]) + Channel(f"flux_coupler-{c}", port=instrument.ports(f"flux_coupler-{c}")) for c in itertools.chain(range(0, 2), range(3, 5)) ) channels["readout"].attenuation = 0 diff --git a/src/qibolab/instruments/abstract.py b/src/qibolab/instruments/abstract.py index 59bd7734b..3fe4286f8 100644 --- a/src/qibolab/instruments/abstract.py +++ b/src/qibolab/instruments/abstract.py @@ -46,27 +46,14 @@ def __init__(self, name, address): @abstractmethod def connect(self): """Establish connection to the physical instrument.""" - raise NotImplementedError - - @abstractmethod - def setup(self, *args, **kwargs): - """Upload settings to the physical instrument.""" - raise NotImplementedError - - @abstractmethod - def start(self): - """Turn on the physical instrument.""" - raise NotImplementedError - - @abstractmethod - def stop(self): - """Turn off the physical instrument.""" - raise NotImplementedError @abstractmethod def disconnect(self): """Close connection to the physical instrument.""" - raise NotImplementedError + + @abstractmethod + def setup(self, *args, **kwargs): + """Set instrument settings.""" class Controller(Instrument): @@ -85,10 +72,7 @@ def sampling_rate(self): """Sampling rate of control electronics in giga samples per second (GSps).""" - def __getitem__(self, port_name): - return self.ports(port_name) - - def ports(self, port_name): + def ports(self, port_name, *args, **kwargs): """Get ports associated to this controller. Args: diff --git a/src/qibolab/instruments/dummy.py b/src/qibolab/instruments/dummy.py index 922894d58..1859ed270 100644 --- a/src/qibolab/instruments/dummy.py +++ b/src/qibolab/instruments/dummy.py @@ -83,18 +83,12 @@ def sampling_rate(self): def connect(self): log.info(f"Connecting to {self.name} instrument.") - def setup(self, *args, **kwargs): - log.info(f"Setting up {self.name} instrument.") - - def start(self): - log.info(f"Starting {self.name} instrument.") - - def stop(self): - log.info(f"Stopping {self.name} instrument.") - def disconnect(self): log.info(f"Disconnecting {self.name} instrument.") + def setup(self, *args, **kwargs): + log.info(f"Setting up {self.name} instrument.") + def get_values(self, options, ro_pulse, shape): if options.acquisition_type is AcquisitionType.DISCRIMINATION: if options.averaging_mode is AveragingMode.SINGLESHOT: diff --git a/src/qibolab/instruments/oscillator.py b/src/qibolab/instruments/oscillator.py index e0a3a14b6..162545894 100644 --- a/src/qibolab/instruments/oscillator.py +++ b/src/qibolab/instruments/oscillator.py @@ -91,6 +91,14 @@ def connect(self): for fld in fields(self.settings): self.sync(fld.name) + self.device.on() + + def disconnect(self): + if self.is_connected: + self.device.off() + self.device.close() + self.is_connected = False + def sync(self, parameter): """Sync parameter value between our cache and the instrument. @@ -123,14 +131,3 @@ def setup(self, **kwargs): f"Cannot set {name} to instrument {self.name} of type {type_.__name__}" ) setattr(self, name, value) - - def start(self): - self.device.on() - - def stop(self): - self.device.off() - - def disconnect(self): - if self.is_connected: - self.device.close() - self.is_connected = False diff --git a/src/qibolab/instruments/qblox/controller.py b/src/qibolab/instruments/qblox/controller.py index 9a79322d2..147eea885 100644 --- a/src/qibolab/instruments/qblox/controller.py +++ b/src/qibolab/instruments/qblox/controller.py @@ -57,6 +57,7 @@ def connect(self): # Connect modules for module in self.modules.values(): module.connect(self.cluster) + module.start() self.is_connected = True log.info("QbloxController: all modules connected.") @@ -64,6 +65,15 @@ def connect(self): raise ConnectionError(f"Unable to connect:\n{str(exception)}\n") # TODO: check for exception 'The module qrm_rf0 does not have parameters in0_att' and reboot the cluster + def disconnect(self): + """Disconnects all modules.""" + if self.is_connected: + for module in self.modules.values(): + module.stop() + module.disconnect() + self.cluster.close() + self.is_connected = False + def setup(self): """Empty method to comply with Instrument interface. Setup of the modules happens in the create method: @@ -71,18 +81,6 @@ def setup(self): >>> instruments = load_instrument_settings(runcard, instruments) """ - def start(self): - """Starts all modules.""" - if self.is_connected: - for name in self.modules: - self.modules[name].start() - - def stop(self): - """Stops all modules.""" - if self.is_connected: - for name in self.modules: - self.modules[name].stop() - def _termination_handler(self, signum, frame): """Calls all modules to stop if the program receives a termination signal.""" @@ -94,14 +92,6 @@ def _termination_handler(self, signum, frame): log.warning("QbloxController: all modules stopped.") exit(0) - def disconnect(self): - """Disconnects all modules.""" - if self.is_connected: - for name in self.modules: - self.modules[name].disconnect() - self.cluster.close() - self.is_connected = False - def _set_module_channel_map(self, module: ClusterQRM_RF, qubits: dict): """Retrieve all the channels connected to a specific Qblox module. diff --git a/src/qibolab/instruments/qm/driver.py b/src/qibolab/instruments/qm/driver.py index 0b99f7b9d..d687c1b77 100644 --- a/src/qibolab/instruments/qm/driver.py +++ b/src/qibolab/instruments/qm/driver.py @@ -68,23 +68,11 @@ def connect(self): def setup(self): """Deprecated method.""" - # controllers are defined when registering pulses - pass - - def start(self): - # TODO: Start the OPX flux offsets? - pass - - def stop(self): - """Close all running Quantum Machines.""" - # TODO: Use logging - # log.warn("Closing all Quantum Machines.") - print("Closing all Quantum Machines.") - self.manager.close_all_quantum_machines() def disconnect(self): """Disconnect from QM manager.""" if self.is_connected: + self.manager.close_all_quantum_machines() self.manager.close() self.is_connected = False diff --git a/src/qibolab/instruments/rfsoc/driver.py b/src/qibolab/instruments/rfsoc/driver.py index ef8f05266..934318067 100644 --- a/src/qibolab/instruments/rfsoc/driver.py +++ b/src/qibolab/instruments/rfsoc/driver.py @@ -66,12 +66,6 @@ def sampling_rate(self): def connect(self): """Empty method to comply with Instrument interface.""" - def start(self): - """Empty method to comply with Instrument interface.""" - - def stop(self): - """Empty method to comply with Instrument interface.""" - def disconnect(self): """Empty method to comply with Instrument interface.""" diff --git a/src/qibolab/instruments/zhinst.py b/src/qibolab/instruments/zhinst.py index b80c2008d..c44f1237c 100644 --- a/src/qibolab/instruments/zhinst.py +++ b/src/qibolab/instruments/zhinst.py @@ -355,12 +355,6 @@ def connect(self): self.device = self.session.connect(do_emulation=self.emulation) self.is_connected = True - def start(self): - """Empty method to comply with Instrument interface.""" - - def stop(self): - """Empty method to comply with Instrument interface.""" - def disconnect(self): if self.is_connected: self.device = self.session.disconnect() diff --git a/src/qibolab/platform.py b/src/qibolab/platform.py index 60da8c097..1c75fd612 100644 --- a/src/qibolab/platform.py +++ b/src/qibolab/platform.py @@ -166,32 +166,6 @@ def connect(self): ) self.is_connected = True - def setup(self): - """Prepares instruments to execute experiments. - - Sets flux port offsets to the qubit sweetspots. - """ - for instrument in self.instruments.values(): - instrument.setup() - for qubit in self.qubits.values(): - if qubit.flux is not None and qubit.sweetspot != 0: - qubit.flux.offset = qubit.sweetspot - for coupler in self.couplers.values(): - if coupler.flux is not None and coupler.sweetspot != 0: - coupler.flux.offset = coupler.sweetspot - - def start(self): - """Starts all the instruments.""" - if self.is_connected: - for instrument in self.instruments.values(): - instrument.start() - - def stop(self): - """Starts all the instruments.""" - if self.is_connected: - for instrument in self.instruments.values(): - instrument.stop() - def disconnect(self): """Disconnects from instruments.""" if self.is_connected: diff --git a/src/qibolab/qubits.py b/src/qibolab/qubits.py index 02b9fdb6e..81baac359 100644 --- a/src/qibolab/qubits.py +++ b/src/qibolab/qubits.py @@ -14,7 +14,7 @@ Not all channels are required to operate a qubit. """ -EXCLUDED_FIELDS = CHANNEL_NAMES + ("name", "native_gates") +EXCLUDED_FIELDS = CHANNEL_NAMES + ("name", "native_gates", "_flux") """Qubit dataclass fields that are excluded by the ``characterization`` property.""" @@ -90,10 +90,24 @@ class Qubit: feedback: Optional[Channel] = None twpa: Optional[Channel] = None drive: Optional[Channel] = None - flux: Optional[Channel] = None + _flux: Optional[Channel] = None native_gates: SingleQubitNatives = field(default_factory=SingleQubitNatives) + def __post_init__(self): + if self.flux is not None and self.sweetspot != 0: + self.flux.offset = self.sweetspot + + @property + def flux(self): + return self._flux + + @flux.setter + def flux(self, channel): + if self.sweetspot != 0: + channel.offset = self.sweetspot + self._flux = channel + @property def channels(self): for name in CHANNEL_NAMES: diff --git a/tests/conftest.py b/tests/conftest.py index 9ca69576f..779d9044a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -97,6 +97,5 @@ def connected_platform(request): name = request.config.getoption("--platform") platform = create_platform(name) platform.connect() - platform.setup() yield platform platform.disconnect() diff --git a/tests/dummy_qrc/qm.py b/tests/dummy_qrc/qm.py index 69cc239a3..745a77f6e 100644 --- a/tests/dummy_qrc/qm.py +++ b/tests/dummy_qrc/qm.py @@ -29,20 +29,22 @@ def create(runcard_path=RUNCARD): # Create channel objects and map controllers to channels channels = ChannelMap() # readout - channels |= Channel("L3-25_a", port=controller[(("con1", 10), ("con1", 9))]) - channels |= Channel("L3-25_b", port=controller[(("con2", 10), ("con2", 9))]) + channels |= Channel("L3-25_a", port=controller.ports((("con1", 10), ("con1", 9)))) + channels |= Channel("L3-25_b", port=controller.ports((("con2", 10), ("con2", 9)))) # feedback - channels |= Channel("L2-5_a", port=controller[(("con1", 2), ("con1", 1))]) - channels |= Channel("L2-5_b", port=controller[(("con2", 2), ("con2", 1))]) + channels |= Channel("L2-5_a", port=controller.ports((("con1", 2), ("con1", 1)))) + channels |= Channel("L2-5_b", port=controller.ports((("con2", 2), ("con2", 1)))) # drive channels |= ( - Channel(f"L3-1{i}", port=controller[(("con1", 2 * i), ("con1", 2 * i - 1))]) + Channel( + f"L3-1{i}", port=controller.ports((("con1", 2 * i), ("con1", 2 * i - 1))) + ) for i in range(1, 5) ) - channels |= Channel("L3-15", port=controller[(("con3", 2), ("con3", 1))]) + channels |= Channel("L3-15", port=controller.ports((("con3", 2), ("con3", 1)))) # flux channels |= ( - Channel(f"L4-{i}", port=controller[(("con2", i),)]) for i in range(1, 6) + Channel(f"L4-{i}", port=controller.ports((("con2", i),))) for i in range(1, 6) ) # TWPA channels |= "L4-26" diff --git a/tests/dummy_qrc/rfsoc.py b/tests/dummy_qrc/rfsoc.py index 77f9038be..5bab3f5f1 100644 --- a/tests/dummy_qrc/rfsoc.py +++ b/tests/dummy_qrc/rfsoc.py @@ -25,10 +25,10 @@ def create(runcard_path=RUNCARD): # Create channel objects and map to instrument controllers channels = ChannelMap() - channels |= Channel("L3-18_ro", port=controller[0]) # readout (DAC) - channels |= Channel("L2-RO", port=controller[0]) # feedback (readout DAC) - channels |= Channel("L3-18_qd", port=controller[1]) # drive - channels |= Channel("L2-22_qf", port=controller[2]) # flux + channels |= Channel("L3-18_ro", port=controller.ports(0)) # readout (DAC) + channels |= Channel("L2-RO", port=controller.ports(0)) # feedback (readout DAC) + channels |= Channel("L3-18_qd", port=controller.ports(1)) # drive + channels |= Channel("L2-22_qf", port=controller.ports(2)) # flux lo_twpa = SGS100A("twpa_a", "192.168.0.32") lo_era = ERA("ErasynthLO", "192.168.0.212", ethernet=True) diff --git a/tests/dummy_qrc/zurich.py b/tests/dummy_qrc/zurich.py index 0076512df..7a251739c 100644 --- a/tests/dummy_qrc/zurich.py +++ b/tests/dummy_qrc/zurich.py @@ -95,30 +95,31 @@ def create(runcard_path=RUNCARD): channels = ChannelMap() # feedback channels |= Channel( - "L2-7", port=controller[("device_shfqc", "[QACHANNELS/0/INPUT]")] + "L2-7", port=controller.ports(("device_shfqc", "[QACHANNELS/0/INPUT]")) ) # readout channels |= Channel( - "L3-31", port=controller[("device_shfqc", "[QACHANNELS/0/OUTPUT]")] + "L3-31", port=controller.ports(("device_shfqc", "[QACHANNELS/0/OUTPUT]")) ) # drive channels |= ( Channel( - f"L4-{i}", port=controller[("device_shfqc", f"SGCHANNELS/{i-5}/OUTPUT")] + f"L4-{i}", + port=controller.ports(("device_shfqc", f"SGCHANNELS/{i-5}/OUTPUT")), ) for i in range(15, 20) ) # flux qubits (CAREFUL WITH THIS !!!) channels |= ( - Channel(f"L4-{i}", port=controller[("device_hdawg", f"SIGOUTS/{i-6}")]) + Channel(f"L4-{i}", port=controller.ports(("device_hdawg", f"SIGOUTS/{i-6}"))) for i in range(6, 11) ) # flux couplers channels |= ( - Channel(f"L4-{i}", port=controller[("device_hdawg", f"SIGOUTS/{i-11+5}")]) + Channel(f"L4-{i}", port=controller.ports(("device_hdawg", f"SIGOUTS/{i-11+5}"))) for i in range(11, 14) ) - channels |= Channel("L4-14", port=controller[("device_hdawg2", "SIGOUTS/0")]) + channels |= Channel("L4-14", port=controller.ports(("device_hdawg2", "SIGOUTS/0"))) # TWPA pump(EraSynth) channels |= Channel("L3-32") diff --git a/tests/test_backends.py b/tests/test_backends.py index 57d29dbe6..c0fad8d22 100644 --- a/tests/test_backends.py +++ b/tests/test_backends.py @@ -17,10 +17,7 @@ def generate_circuit_with_gate(nqubits, gate, **kwargs): @pytest.fixture(scope="module") def connected_backend(connected_platform): - connected_platform.setup() - connected_platform.start() yield QibolabBackend(connected_platform) - connected_platform.stop() def test_execute_circuit_initial_state(): diff --git a/tests/test_dummy.py b/tests/test_dummy.py index 0e3bcc734..63bdb22b1 100644 --- a/tests/test_dummy.py +++ b/tests/test_dummy.py @@ -14,9 +14,6 @@ def test_dummy_initialization(name): platform = create_platform(name) platform.connect() - platform.setup() - platform.start() - platform.stop() platform.disconnect() diff --git a/tests/test_instruments_oscillator.py b/tests/test_instruments_oscillator.py index a30d64036..56a92deac 100644 --- a/tests/test_instruments_oscillator.py +++ b/tests/test_instruments_oscillator.py @@ -30,11 +30,3 @@ def test_oscillator_setup(lo): lo.setup(frequency=5e9, power=0) assert lo.frequency == 5e9 assert lo.power == 0 - - -def test_oscillator_start_stop(lo): - with pytest.raises(AttributeError): - lo.start() - lo.connect() - lo.start() - lo.stop() diff --git a/tests/test_instruments_qm.py b/tests/test_instruments_qm.py index 5c369ca43..d94e4b482 100644 --- a/tests/test_instruments_qm.py +++ b/tests/test_instruments_qm.py @@ -144,14 +144,10 @@ def test_qmpulse_previous_and_next_flux(): def test_qmopx_setup(dummy_qrc): platform = create_platform("qm") - platform.setup() opx = platform.instruments["qmopx"] assert opx.time_of_flight == 280 -# TODO: Test start/stop - - def test_qmopx_register_analog_output_controllers(): name = "test" address = "0.0.0.0:0" @@ -358,7 +354,6 @@ def test_qmopx_register_baked_pulse(dummy_qrc, duration): @patch("qibolab.instruments.qm.simulator.QMSim.execute_program") def test_qmopx_qubit_spectroscopy(mocker): platform = create_platform("qm") - platform.setup() opx = platform.instruments["qmopx"] # disable program dump otherwise it will fail if we don't connect opx.script_file_name = None @@ -381,7 +376,6 @@ def test_qmopx_qubit_spectroscopy(mocker): @patch("qibolab.instruments.qm.simulator.QMSim.execute_program") def test_qmopx_duration_sweeper(mocker): platform = create_platform("qm") - platform.setup() opx = platform.instruments["qmopx"] # disable program dump otherwise it will fail if we don't connect opx.script_file_name = None diff --git a/tests/test_instruments_qmsim.py b/tests/test_instruments_qmsim.py index 27940ba23..7cd66f4df 100644 --- a/tests/test_instruments_qmsim.py +++ b/tests/test_instruments_qmsim.py @@ -48,7 +48,6 @@ def simulator(request): controller.time_of_flight = 280 platform.instruments["qmopx"] = controller platform.connect() - platform.setup() yield platform platform.disconnect() diff --git a/tests/test_instruments_rfsoc.py b/tests/test_instruments_rfsoc.py index 9f6c2f628..67aa7f796 100644 --- a/tests/test_instruments_rfsoc.py +++ b/tests/test_instruments_rfsoc.py @@ -54,7 +54,7 @@ def test_convert_qubit(dummy_qrc): """ platform = create_platform("rfsoc") qubit = platform.qubits[0] - qubit.flux.port = platform.instruments["tii_rfsoc4x2"][4] + qubit.flux.port = platform.instruments["tii_rfsoc4x2"].ports(4) qubit.flux.offset = 0.05 qubit = convert(qubit) targ = rfsoc.Qubit(0.05, 4) @@ -102,9 +102,9 @@ def test_convert_pulse(dummy_qrc): platform = create_platform("rfsoc") controller = platform.instruments["tii_rfsoc4x2"] qubit = platform.qubits[0] - qubit.drive.port = controller[4] - qubit.readout.port = controller[2] - qubit.feedback.port = controller[1] + qubit.drive.port = controller.ports(4) + qubit.readout.port = controller.ports(2) + qubit.feedback.port = controller.ports(1) qubit.readout.local_oscillator.frequency = 1e6 pulse = Pulse(0, 40, 0.9, 50e6, 0, Drag(5, 2), 0, PulseType.DRIVE, 0) diff --git a/tests/test_instruments_zhinst.py b/tests/test_instruments_zhinst.py index d508fd14a..b2e0bb2ab 100644 --- a/tests/test_instruments_zhinst.py +++ b/tests/test_instruments_zhinst.py @@ -84,7 +84,6 @@ def test_select_sweeper(dummy_qrc, parameter): def test_zhinst_setup(dummy_qrc): platform = create_platform("zurich") - platform.setup() IQM5q = platform.instruments["EL_ZURO"] assert IQM5q.time_of_flight == 75 @@ -188,7 +187,6 @@ def test_zhsequence_multiple_ro(dummy_qrc): def test_zhinst_register_readout_line(dummy_qrc): platform = create_platform("zurich") - platform.setup() IQM5q = platform.instruments["EL_ZURO"] options = ExecutionParameters( @@ -210,7 +208,6 @@ def test_zhinst_register_readout_line(dummy_qrc): def test_zhinst_register_drive_line(dummy_qrc): platform = create_platform("zurich") - platform.setup() IQM5q = platform.instruments["EL_ZURO"] IQM5q.register_drive_line(platform.qubits[0], intermediate_frequency=int(1e6)) @@ -220,7 +217,6 @@ def test_zhinst_register_drive_line(dummy_qrc): def test_zhinst_register_flux_line(dummy_qrc): platform = create_platform("zurich") - platform.setup() IQM5q = platform.instruments["EL_ZURO"] IQM5q.register_flux_line(platform.qubits[0]) @@ -230,7 +226,6 @@ def test_zhinst_register_flux_line(dummy_qrc): def test_experiment_execute_pulse_sequence(dummy_qrc): platform = create_platform("zurich") - platform.setup() IQM5q = platform.instruments["EL_ZURO"] sequence = PulseSequence() @@ -269,7 +264,6 @@ def test_experiment_execute_pulse_sequence(dummy_qrc): def test_experiment_execute_pulse_sequence_coupler(dummy_qrc): platform = create_platform("zurich") - platform.setup() IQM5q = platform.instruments["EL_ZURO"] sequence = PulseSequence() @@ -322,7 +316,6 @@ def test_experiment_execute_pulse_sequence_coupler(dummy_qrc): def test_experiment_fast_reset_readout(dummy_qrc): platform = create_platform("zurich") - platform.setup() IQM5q = platform.instruments["EL_ZURO"] sequence = PulseSequence() @@ -354,7 +347,6 @@ def test_experiment_fast_reset_readout(dummy_qrc): @pytest.mark.parametrize("fast_reset", [True, False]) def test_experiment_execute_pulse_sequence(dummy_qrc, fast_reset): platform = create_platform("zurich") - platform.setup() IQM5q = platform.instruments["EL_ZURO"] sequence = PulseSequence() @@ -405,7 +397,6 @@ def test_experiment_execute_pulse_sequence(dummy_qrc, fast_reset): @pytest.mark.parametrize("parameter1", [Parameter.start, Parameter.duration]) def test_experiment_sweep_single(dummy_qrc, parameter1): platform = create_platform("zurich") - platform.setup() IQM5q = platform.instruments["EL_ZURO"] sequence = PulseSequence() @@ -451,7 +442,6 @@ def test_experiment_sweep_single(dummy_qrc, parameter1): @pytest.mark.parametrize("parameter1", [Parameter.start, Parameter.duration]) def test_experiment_sweep_single_coupler(dummy_qrc, parameter1): platform = create_platform("zurich") - platform.setup() IQM5q = platform.instruments["EL_ZURO"] sequence = PulseSequence() @@ -521,7 +511,6 @@ def test_experiment_sweep_single_coupler(dummy_qrc, parameter1): @pytest.mark.parametrize("parameter2", Parameter) def test_experiment_sweep_2d_general(dummy_qrc, parameter1, parameter2): platform = create_platform("zurich") - platform.setup() IQM5q = platform.instruments["EL_ZURO"] sequence = PulseSequence() @@ -582,7 +571,6 @@ def test_experiment_sweep_2d_general(dummy_qrc, parameter1, parameter2): def test_experiment_sweep_2d_specific(dummy_qrc): platform = create_platform("zurich") - platform.setup() IQM5q = platform.instruments["EL_ZURO"] sequence = PulseSequence() @@ -641,7 +629,6 @@ def test_experiment_sweep_2d_specific(dummy_qrc): ) def test_experiment_sweep_punchouts(dummy_qrc, parameter): platform = create_platform("zurich") - platform.setup() IQM5q = platform.instruments["EL_ZURO"] sequence = PulseSequence() @@ -703,7 +690,6 @@ def test_experiment_sweep_punchouts(dummy_qrc, parameter): # TODO: Fix this def test_sim(dummy_qrc): platform = create_platform("zurich") - platform.setup() IQM5q = platform.instruments["EL_ZURO"] sequence = PulseSequence() qubits = {0: platform.qubits[0]} @@ -731,7 +717,6 @@ def test_sim(dummy_qrc): def test_split_batches(dummy_qrc): platform = create_platform("zurich") - platform.setup() instrument = platform.instruments["EL_ZURO"] sequence = PulseSequence() @@ -763,8 +748,6 @@ def test_connections(instrument): @pytest.mark.qpu def test_experiment_execute_pulse_sequence(connected_platform, instrument): platform = connected_platform - platform.setup() - sequence = PulseSequence() qubits = {0: platform.qubits[0], "c0": platform.qubits["c0"]} platform.qubits = qubits @@ -804,8 +787,6 @@ def test_experiment_execute_pulse_sequence(connected_platform, instrument): @pytest.mark.qpu def test_experiment_sweep_2d_specific(connected_platform, instrument): platform = connected_platform - platform.setup() - sequence = PulseSequence() qubits = {0: platform.qubits[0]} @@ -881,7 +862,6 @@ def get_previous_subsequence_finish(instrument, name): def test_experiment_measurement_sequence(dummy_qrc): platform = create_platform("zurich") - platform.setup() IQM5q = platform.instruments["EL_ZURO"] sequence = PulseSequence() diff --git a/tests/test_platform.py b/tests/test_platform.py index f9ca4a88e..e340d6d88 100644 --- a/tests/test_platform.py +++ b/tests/test_platform.py @@ -83,15 +83,7 @@ def test_dump_runcard(platform): @pytest.fixture(scope="module") def qpu_platform(connected_platform): connected_platform.connect() - connected_platform.setup() - connected_platform.start() yield connected_platform - connected_platform.stop() - - -@pytest.mark.qpu -def test_platform_setup_start_stop(qpu_platform): - pass @pytest.mark.qpu From 83a398fdcbd2f1b66351aa94ec7f9e3f36520663 Mon Sep 17 00:00:00 2001 From: Stavros Efthymiou <35475381+stavros11@users.noreply.github.com> Date: Tue, 9 Jan 2024 19:04:09 +0400 Subject: [PATCH 2/7] chore: Drop SPI driver --- src/qibolab/instruments/qutech.py | 163 ------------------------------ tests/test_instruments_qutech.py | 36 ------- 2 files changed, 199 deletions(-) delete mode 100644 src/qibolab/instruments/qutech.py delete mode 100644 tests/test_instruments_qutech.py diff --git a/src/qibolab/instruments/qutech.py b/src/qibolab/instruments/qutech.py deleted file mode 100644 index 585b011c9..000000000 --- a/src/qibolab/instruments/qutech.py +++ /dev/null @@ -1,163 +0,0 @@ -"""Class to interface with the SPI Rack Qutech Delft.""" -from qblox_instruments import SpiRack -from qibo.config import log, raise_error - -from qibolab.instruments.abstract import Instrument, InstrumentException - - -class SPI(Instrument): - property_wrapper = lambda parent, device, *parameter: property( - lambda self: device.get(parameter[0]), - lambda self, x: parent._set_device_parameter(device, *parameter, value=x), - ) - - def __init__(self, name, address): - super().__init__(name, address) - self.device: SpiRack = None - self.s4g_modules_settings = {} - self.d5a_modules_settings = {} - self.dacs = {} - self.device_parameters = {} - - def connect(self): - """Connects to the instrument using the IP address set in the - runcard.""" - if not self.is_connected: - for attempt in range(3): - try: - self.device = SpiRack(self.name, self.address) - self.is_connected = True - break - except KeyError as exc: - log.info(f"Unable to connect:\n{str(exc)}\nRetrying...") - self.name += "_" + str(attempt) - except Exception as exc: - log.info(f"Unable to connect:\n{str(exc)}\nRetrying...") - if not self.is_connected: - raise InstrumentException(self, f"Unable to connect to {self.name}") - else: - raise_error( - Exception, "There is an open connection to the instrument already" - ) - - def _set_device_parameter(self, target, *parameters, value): - if self.is_connected: - key = target.name + "." + parameters[0] - if not key in self.device_parameters: - for parameter in parameters: - if not hasattr(target, parameter): - raise Exception( - f"The instrument {self.name} does not have parameters {parameter}" - ) - target.set(parameter, value) - self.device_parameters[key] = value - elif self.device_parameters[key] != value: - for parameter in parameters: - target.set(parameter, value) - self.device_parameters[key] = value - else: - raise Exception("There is no connection to the instrument {self.name}") - - def setup(self, **kwargs): - # Init S4g and D5a modules in SPI mapped on runcard - if self.is_connected: - # TODO: Check data format from yml - # Make d5g modules optional in runcard - # Define span values in setup - # Implement parameters cache - # export current / voltage properties (and make them sweepable) - if "s4g_modules" in kwargs: - self.s4g_modules_settings = kwargs["s4g_modules"] - if "d5a_modules" in kwargs: - self.d5a_modules_settings = kwargs["d5a_modules"] - - for channel, settings in self.s4g_modules_settings.items(): - module_number = settings[0] - port_number = settings[1] - module_name = f"S4g_module{module_number}" - current = settings[2] - if not module_name in self.device.instrument_modules: - self.device.add_spi_module(settings[0], "S4g", module_name) - device = self.device.instrument_modules[module_name].instrument_modules[ - "dac" + str(port_number - 1) - ] - self.dacs[channel] = type( - "S4g_dac", - (), - { - "current": self.property_wrapper(device, "current"), - "device": device, - }, - )() - self.dacs[channel].device.span("range_min_bi") - # self.dacs[channel].current = current - - for channel, settings in self.d5a_modules_settings.items(): - module_number = settings[0] - port_number = settings[1] - module_name = f"D5a_module{module_number}" - voltage = settings[2] - if not module_name in self.device.instrument_modules: - self.device.add_spi_module(settings[0], "D5a", module_name) - device = self.device.instrument_modules[module_name].instrument_modules[ - "dac" + str(port_number - 1) - ] - self.dacs[channel] = type( - "D5a_dac", - (), - { - "voltage": self.property_wrapper(device, "voltage"), - "device": device, - }, - )() - self.dacs[channel].device.span("range_min_bi") - # self.dacs[channel].voltage = voltage - else: - raise_error(Exception, "There is no connection to the instrument") - - def set_SPI_DACS_to_cero(self): - self.device.set_dacs_zero() - - def get_SPI_IDN(self): - return self.device.IDN() - - def get_SPI_temperature(self): - return self.device.temperature() - - def get_SPI_battery_voltage(self): - return self.device.battery_voltages() - - def disconnect(self): - if self.is_connected: - self.is_connected = False - - def close(self): - if self.is_connected: - self.device.close() - self.is_connected = False - - def start(self): - # Set the dacs to the values stored for each qubit in the runcard - if self.is_connected: - for channel, settings in self.s4g_modules_settings.items(): - current = settings[2] - # Check current current of the module and warning - if abs(self.dacs[channel].current) > 0.010: - log.info( - f"WARNING: S4g module {settings[0]} - port {settings[1]} current was: {self.dacs[channel].current}, now setting current to: {current}" - ) - self.dacs[channel].current = current - - for channel, settings in self.d5a_modules_settings.items(): - voltage = settings[2] - # Check current current of the module and warning - if abs(self.dacs[channel].voltage) > 0.010: - log.info( - f"WARNING: D5a module {settings[0]} - port {settings[1]} voltage was: {self.dacs[channel].voltage}, now setting voltage to: {voltage}" - ) - self.dacs[channel].voltage = voltage - - def stop(self): - # if self.is_connected: - # self.device.set_dacs_zero() - return diff --git a/tests/test_instruments_qutech.py b/tests/test_instruments_qutech.py deleted file mode 100644 index 9f354b235..000000000 --- a/tests/test_instruments_qutech.py +++ /dev/null @@ -1,36 +0,0 @@ -import pytest - -from qibolab.instruments.qutech import SPI - -from .conftest import get_instrument - - -@pytest.fixture(scope="module") -def spi(connected_platform): - return get_instrument(connected_platform, SPI) - - -# To test --> name = SpiRack -@pytest.mark.qpu -def test_instruments_qutech_init(spi): - assert spi.is_connected == True - assert spi.device is None - assert ( - spi.data_folder == INSTRUMENTS_DATA_FOLDER / spi.tmp_folder.name.split("/")[-1] - ) - - -@pytest.mark.qpu -def test_instruments_qutech_disconnect(spi): - spi.connect() - assert spi.is_connected == True - spi.disconnect() - assert spi.is_connected == False - spi.connect() - - -@pytest.mark.qpu -def test_instruments_qutech_close(spi): - spi.close() - assert spi.is_connected == False - spi.connect() From df93b4cc92fac5a17e5f8d331b43b99cd177ba20 Mon Sep 17 00:00:00 2001 From: Stavros Efthymiou <35475381+stavros11@users.noreply.github.com> Date: Tue, 9 Jan 2024 19:06:08 +0400 Subject: [PATCH 3/7] chore: Drop platform getters and setters --- src/qibolab/platform.py | 114 ---------------------------------------- 1 file changed, 114 deletions(-) diff --git a/src/qibolab/platform.py b/src/qibolab/platform.py index 1c75fd612..faa2e61cc 100644 --- a/src/qibolab/platform.py +++ b/src/qibolab/platform.py @@ -421,117 +421,3 @@ def create_RX_drag_pulse(self, qubit, start, relative_phase=0, beta=None): if beta is not None: pulse.shape = "Drag(5," + str(beta) + ")" return pulse - - def set_lo_drive_frequency(self, qubit, freq): - """Set frequency of the qubit drive local oscillator. - - Args: - qubit (int): qubit whose local oscillator will be modified. - freq (int): new value of the frequency in Hz. - """ - self.qubits[qubit].drive.lo_frequency = freq - - def get_lo_drive_frequency(self, qubit): - """Get frequency of the qubit drive local oscillator in Hz.""" - return self.qubits[qubit].drive.lo_frequency - - def set_lo_readout_frequency(self, qubit, freq): - """Set frequency of the qubit drive local oscillator. - - Args: - qubit (int): qubit whose local oscillator will be modified. - freq (int): new value of the frequency in Hz. - """ - self.qubits[qubit].readout.lo_frequency = freq - - def get_lo_readout_frequency(self, qubit): - """Get frequency of the qubit readout local oscillator in Hz.""" - return self.qubits[qubit].readout.lo_frequency - - def set_lo_twpa_frequency(self, qubit, freq): - """Set frequency of the local oscillator of the TWPA to which the - qubit's feedline is connected to. - - Args: - qubit (int): qubit whose local oscillator will be modified. - freq (int): new value of the frequency in Hz. - """ - self.qubits[qubit].twpa.lo_frequency = freq - - def get_lo_twpa_frequency(self, qubit): - """Get frequency of the local oscillator of the TWPA to which the - qubit's feedline is connected to in Hz.""" - return self.qubits[qubit].twpa.lo_frequency - - def set_lo_twpa_power(self, qubit, power): - """Set power of the local oscillator of the TWPA to which the qubit's - feedline is connected to. - - Args: - qubit (int): qubit whose local oscillator will be modified. - power (int): new value of the power in dBm. - """ - self.qubits[qubit].twpa.lo_power = power - - def get_lo_twpa_power(self, qubit): - """Get power of the local oscillator of the TWPA to which the qubit's - feedline is connected to in dBm.""" - return self.qubits[qubit].twpa.lo_power - - def set_attenuation(self, qubit, att): - """Set attenuation value. Usefeul for calibration routines such as - punchout. - - Args: - qubit (int): qubit whose attenuation will be modified. - att (int): new value of the attenuation (dB). - Returns: - None - """ - self.qubits[qubit].readout.attenuation = att - - def get_attenuation(self, qubit): - """Get attenuation value. - - Usefeul for calibration routines such as punchout. - """ - return self.qubits[qubit].readout.attenuation - - def set_gain(self, qubit, gain): - """Set gain value. Usefeul for calibration routines such as Rabi - oscillations. - - Args: - qubit (int): qubit whose attenuation will be modified. - gain (int): new value of the gain (dimensionless). - Returns: - None - """ - raise_error(NotImplementedError, f"{self.name} does not support gain.") - - def get_gain(self, qubit): - """Get gain value. - - Usefeul for calibration routines such as Rabi oscillations. - """ - raise_error(NotImplementedError, f"{self.name} does not support gain.") - - def set_bias(self, qubit, bias): - """Set bias value. Usefeul for calibration routines involving flux. - - Args: - qubit (int): qubit whose attenuation will be modified. - bias (int): new value of the bias (V). - Returns: - None - """ - if self.qubits[qubit].flux is None: - raise_error(NotImplementedError, f"{self.name} does not have flux.") - self.qubits[qubit].flux.offset = bias - - def get_bias(self, qubit): - """Get bias value. - - Usefeul for calibration routines involving flux. - """ - return self.qubits[qubit].flux.offset From d881d1f7ebccaabf488224f26d69e35f8d279418 Mon Sep 17 00:00:00 2001 From: Stavros Efthymiou <35475381+stavros11@users.noreply.github.com> Date: Tue, 9 Jan 2024 19:15:46 +0400 Subject: [PATCH 4/7] chore: Further cleanup of abstract instrument --- src/qibolab/instruments/abstract.py | 14 ++++---------- src/qibolab/instruments/oscillator.py | 12 ++++-------- tests/test_instruments_qblox_cluster_qcm_bb.py | 3 --- tests/test_instruments_qblox_cluster_qcm_rf.py | 3 --- tests/test_instruments_qblox_cluster_qrm_rf.py | 3 --- 5 files changed, 8 insertions(+), 27 deletions(-) diff --git a/src/qibolab/instruments/abstract.py b/src/qibolab/instruments/abstract.py index 3fe4286f8..05e1811bc 100644 --- a/src/qibolab/instruments/abstract.py +++ b/src/qibolab/instruments/abstract.py @@ -1,13 +1,10 @@ -import tempfile from abc import ABC, abstractmethod from dataclasses import asdict, dataclass -from pathlib import Path from typing import Optional from qibolab.instruments.port import Port InstrumentId = str -INSTRUMENTS_DATA_FOLDER = Path.home() / ".qibolab" / "instruments" / "data" @dataclass @@ -34,14 +31,11 @@ def __init__(self, name, address): self.name: InstrumentId = name self.address: str = address self.is_connected: bool = False - self.signature: str = f"{type(self).__name__}@{address}" self.settings: Optional[InstrumentSettings] = None - # create local storage folder - instruments_data_folder = INSTRUMENTS_DATA_FOLDER - instruments_data_folder.mkdir(parents=True, exist_ok=True) - # create temporary directory - self.tmp_folder = tempfile.TemporaryDirectory(dir=instruments_data_folder) - self.data_folder = Path(self.tmp_folder.name) + + @property + def signature(self): + return f"{type(self).__name__}@{self.address}" @abstractmethod def connect(self): diff --git a/src/qibolab/instruments/oscillator.py b/src/qibolab/instruments/oscillator.py index 162545894..3fb7b4123 100644 --- a/src/qibolab/instruments/oscillator.py +++ b/src/qibolab/instruments/oscillator.py @@ -2,11 +2,7 @@ from dataclasses import dataclass, fields from typing import Optional -from qibolab.instruments.abstract import ( - Instrument, - InstrumentException, - InstrumentSettings, -) +from qibolab.instruments.abstract import Instrument, InstrumentSettings RECONNECTION_ATTEMPTS = 3 """Number of times to attempt connecting to instrument in case of failure.""" @@ -82,10 +78,10 @@ def connect(self): self.device = self.create() self.is_connected = True if not self.is_connected: - raise InstrumentException(self, "Unable to connect to {self.name}.") + raise RuntimeError(f"Unable to connect to {self.name}.") else: - raise InstrumentException( - self, "There is an open connection to the instrument already" + raise RuntimeError( + f"There is an open connection to the instrument {self.name}." ) for fld in fields(self.settings): diff --git a/tests/test_instruments_qblox_cluster_qcm_bb.py b/tests/test_instruments_qblox_cluster_qcm_bb.py index dc61bd3b0..65d2b64a4 100644 --- a/tests/test_instruments_qblox_cluster_qcm_bb.py +++ b/tests/test_instruments_qblox_cluster_qcm_bb.py @@ -68,9 +68,6 @@ def test_instrument_interface(qcm_bb: ClusterQCM_BB): "name", "address", "is_connected", - "signature", - "tmp_folder", - "data_folder", ]: assert hasattr(qcm_bb, attribute) diff --git a/tests/test_instruments_qblox_cluster_qcm_rf.py b/tests/test_instruments_qblox_cluster_qcm_rf.py index 66d0576e6..bab26e5b5 100644 --- a/tests/test_instruments_qblox_cluster_qcm_rf.py +++ b/tests/test_instruments_qblox_cluster_qcm_rf.py @@ -60,9 +60,6 @@ def test_instrument_interface(qcm_rf: ClusterQCM_RF): "name", "address", "is_connected", - "signature", - "tmp_folder", - "data_folder", ]: assert hasattr(qcm_rf, attribute) diff --git a/tests/test_instruments_qblox_cluster_qrm_rf.py b/tests/test_instruments_qblox_cluster_qrm_rf.py index a1e837788..56d621a6e 100644 --- a/tests/test_instruments_qblox_cluster_qrm_rf.py +++ b/tests/test_instruments_qblox_cluster_qrm_rf.py @@ -59,9 +59,6 @@ def test_instrument_interface(qrm_rf: ClusterQRM_RF): "name", "address", "is_connected", - "signature", - "tmp_folder", - "data_folder", ]: assert hasattr(qrm_rf, attribute) From d459ddc9ea2330204eacd0b67a06f67ba87f0cf3 Mon Sep 17 00:00:00 2001 From: Stavros Efthymiou <35475381+stavros11@users.noreply.github.com> Date: Wed, 10 Jan 2024 14:02:57 +0400 Subject: [PATCH 5/7] docs: Fix doctests and warnings --- doc/source/main-documentation/qibolab.rst | 9 ++++----- doc/source/tutorials/instrument.rst | 2 +- doc/source/tutorials/transpiler.rst | 4 ++-- src/qibolab/channels.py | 9 +++------ src/qibolab/couplers.py | 4 ++-- src/qibolab/instruments/port.py | 7 +------ src/qibolab/instruments/qblox/controller.py | 4 ++-- 7 files changed, 15 insertions(+), 24 deletions(-) diff --git a/doc/source/main-documentation/qibolab.rst b/doc/source/main-documentation/qibolab.rst index 3d919d623..ad2c467d1 100644 --- a/doc/source/main-documentation/qibolab.rst +++ b/doc/source/main-documentation/qibolab.rst @@ -201,9 +201,9 @@ Note that while channels are defined in a device-independent manner, the port pa from qibolab.instruments.rfsoc import RFSoC controller = RFSoC(name="dummy", address="192.168.0.10", port="6000") - channel1 = Channel("my_channel_name_1", port=controller[1]) - channel2 = Channel("my_channel_name_2", port=controller[2]) - channel3 = Channel("my_channel_name_3", port=controller[3]) + channel1 = Channel("my_channel_name_1", port=controller.ports(1)) + channel2 = Channel("my_channel_name_2", port=controller.ports(2)) + channel3 = Channel("my_channel_name_3", port=controller.ports(3)) Channels are then organized in :class:`qibolab.channels.ChannelMap` to be passed as a single argument to the platform. Following the tutorial in :doc:`/tutorials/lab`, we can continue the initialization: @@ -704,13 +704,12 @@ Controllers (subclasses of :class:`qibolab.instruments.abstract.Controller`): - Dummy Instrument: :class:`qibolab.instruments.dummy.DummyInstrument` - Zurich Instruments: :class:`qibolab.instruments.zhinst.Zurich` - Quantum Machines: :class:`qibolab.instruments.qm.driver.QMOPX` - - Qblox: :class:`qibolab.instruments.qblox.cluster.Cluster` + - Qblox: :class:`qibolab.instruments.qblox.controller.QbloxCluster` - Xilinx RFSoCs: :class:`qibolab.instruments.rfsoc.driver.RFSoC` Other Instruments (subclasses of :class:`qibolab.instruments.abstract.Instrument`): - Erasynth++: :class:`qibolab.instruments.erasynth.ERA` - RohseSchwarz SGS100A: :class:`qibolab.instruments.rohde_schwarz.SGS100A` - - Qutech SPI rack: :class:`qibolab.instruments.qutech.SPI` Instruments all implement a set of methods: diff --git a/doc/source/tutorials/instrument.rst b/doc/source/tutorials/instrument.rst index 783470c81..40fe60273 100644 --- a/doc/source/tutorials/instrument.rst +++ b/doc/source/tutorials/instrument.rst @@ -189,7 +189,7 @@ write something like this: instrument = DummyInstrument("my_instrument", "0.0.0.0:0") channels = ChannelMap() - channels |= Channel("ch1out", port=instrument["o1"]) + channels |= Channel("ch1out", port=instrument.ports("o1")) The interesting part of this section is the ``port`` parameter that works as an diff --git a/doc/source/tutorials/transpiler.rst b/doc/source/tutorials/transpiler.rst index bc31caea6..5b06086dd 100644 --- a/doc/source/tutorials/transpiler.rst +++ b/doc/source/tutorials/transpiler.rst @@ -64,14 +64,14 @@ Instead of completely disabling, custom transpilation steps can be given: Now circuits will only be transpiled to native gates, without any connectivity matching steps. -The :class:`qibolab.transpilers.gate_decompositions.NativeGates` transpiler used in this example assumes Z, RZ, GPI2 or U3 as the single-qubit native gates, and supports CZ and iSWAP as two-qubit natives. +The transpiler used in this example assumes Z, RZ, GPI2 or U3 as the single-qubit native gates, and supports CZ and iSWAP as two-qubit natives. In this case we restricted the two-qubit gate set to CZ only. If the circuit to be executed contains gates that are not included in this gate set, they will be transformed to multiple gates from the gate set. Arbitrary single-qubit gates are typically transformed to U3. Arbitrary two-qubit gates are transformed to two or three CZ gates following their `universal CNOT decomposition `_. The decomposition of some common gates such as the SWAP and CNOT is hard-coded for efficiency. -Multiple transpilation steps can be implemented using the :class:`qibolab.transpilers.pipeline.Pipeline`: +Multiple transpilation steps can be implemented using the transpiler ``Passes``: .. testcode:: python diff --git a/src/qibolab/channels.py b/src/qibolab/channels.py index 9d934f6d1..b9b7f0d22 100644 --- a/src/qibolab/channels.py +++ b/src/qibolab/channels.py @@ -127,12 +127,9 @@ class ChannelMap: def add(self, *items): """Add multiple items to the channel map. - If - :class: `qibolab.channels.Channel` objects are given they are - added to the channel map. If a different type is - given, a - :class: `qibolab.channels.Channel` with the corresponding name - is created and added to the channel map. + If :class: `qibolab.channels.Channel` objects are given they are dded to the channel map. + If a different type is given, a :class: `qibolab.channels.Channel` with the corresponding name + is created and added to the channel map. """ for item in items: if isinstance(item, Channel): diff --git a/src/qibolab/couplers.py b/src/qibolab/couplers.py index d05563e93..8f1884dda 100644 --- a/src/qibolab/couplers.py +++ b/src/qibolab/couplers.py @@ -13,8 +13,8 @@ class Coupler: """Representation of a physical coupler. Coupler objects are instantiated by - :class: `qibolab.platforms.platform.Platform` but they are - passed to instrument designs in order to play pulses. + :class: `qibolab.platforms.platform.Platform` + and are passed to instruments to play pulses on them. """ name: QubitId diff --git a/src/qibolab/instruments/port.py b/src/qibolab/instruments/port.py index 51aecff6a..09f786097 100644 --- a/src/qibolab/instruments/port.py +++ b/src/qibolab/instruments/port.py @@ -35,9 +35,4 @@ class Port: instruments).""" filters: dict """Filters to be applied to the channel to reduce the distortions when - sending flux pulses. - - Useful for two-qubit gates. Quantum Machines ( - :class: `qibolab.instruments.qm.QMOPX`) associate filters to - channels but this may not be the case in other instruments. - """ + sending flux pulses.""" diff --git a/src/qibolab/instruments/qblox/controller.py b/src/qibolab/instruments/qblox/controller.py index 147eea885..4c1325a1d 100644 --- a/src/qibolab/instruments/qblox/controller.py +++ b/src/qibolab/instruments/qblox/controller.py @@ -76,9 +76,9 @@ def disconnect(self): def setup(self): """Empty method to comply with Instrument interface. - Setup of the modules happens in the create method: - >>> instruments = load_instrument_settings(runcard, instruments) + Setup of the modules happens in the platform ``create`` method + using :meth:`qibolab.serialize.load_instrument_settings`. """ def _termination_handler(self, signum, frame): From 445fc6a04f0d249c7e230f2267220155866805bd Mon Sep 17 00:00:00 2001 From: Stavros Efthymiou <35475381+stavros11@users.noreply.github.com> Date: Fri, 12 Jan 2024 15:32:11 +0400 Subject: [PATCH 6/7] fix: qblox ports no longer a dict --- src/qibolab/instruments/qblox/cluster_qcm_bb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibolab/instruments/qblox/cluster_qcm_bb.py b/src/qibolab/instruments/qblox/cluster_qcm_bb.py index 43bf365cf..5aadb08ef 100644 --- a/src/qibolab/instruments/qblox/cluster_qcm_bb.py +++ b/src/qibolab/instruments/qblox/cluster_qcm_bb.py @@ -213,7 +213,7 @@ def _get_next_sequencer(self, port, frequency, qubits: dict): # select the qubit with flux line, if present, connected to the specific port qubit = None for _qubit in qubits.values(): - if _qubit.flux is not None and _qubit.flux.port == self.ports[port]: + if _qubit.flux is not None and _qubit.flux.port == self.ports(port): qubit = _qubit # select a new sequencer and configure it as required From fc55f7990b491c8fb895d88144a2b85de4f3b415 Mon Sep 17 00:00:00 2001 From: Stavros Efthymiou <35475381+stavros11@users.noreply.github.com> Date: Fri, 12 Jan 2024 15:46:03 +0400 Subject: [PATCH 7/7] test: remove local oscillator tests for start and stop --- tests/test_instruments_erasynth.py | 6 ------ tests/test_instruments_rohde_schwarz.py | 6 ------ 2 files changed, 12 deletions(-) diff --git a/tests/test_instruments_erasynth.py b/tests/test_instruments_erasynth.py index 40f7a536f..1aacd04dd 100644 --- a/tests/test_instruments_erasynth.py +++ b/tests/test_instruments_erasynth.py @@ -24,9 +24,3 @@ def test_instruments_erasynth_setup(era): assert era.power == -10 era.frequency = original_frequency era.power = original_power - - -@pytest.mark.qpu -def test_instruments_erasynth_start_stop_disconnect(era): - era.start() - era.stop() diff --git a/tests/test_instruments_rohde_schwarz.py b/tests/test_instruments_rohde_schwarz.py index ac2aca2be..5c156d28e 100644 --- a/tests/test_instruments_rohde_schwarz.py +++ b/tests/test_instruments_rohde_schwarz.py @@ -69,9 +69,3 @@ def test_instruments_rohde_schwarz_set_device_paramter(instrument): """ instrument.frequency = original_frequency instrument.power = original_power - - -@pytest.mark.qpu -def test_instruments_rohde_schwarz_start_stop_disconnect(instrument): - instrument.start() - instrument.stop()