Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

collection of small fixes, features, and random pieces of code #993

Merged
merged 23 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
9786545
qblox support for couplers bias
aorgazf May 15, 2024
db43333
coupler support for qblox drivers
aorgazf May 18, 2024
a751d72
coupler support for qblox drivers
aorgazf May 18, 2024
40c53fc
coupler support for qblox drivers
aorgazf May 18, 2024
e6e7d2f
support for baking Custom pulses and enhancements in their evaluation…
aorgazf May 20, 2024
1d97650
add possibility to define shape in create_qubit_flux_pulse
aorgazf May 20, 2024
f174b6f
support the creation of coupler flux pulses with a particular shape
aorgazf May 31, 2024
9ac6400
fix custom pulse initialisation so that it works both with strings an…
aorgazf May 31, 2024
93559ad
expose i & q offsets properties in qblox port, for mixer calibration
aorgazf Jun 3, 2024
84c61cc
cz_sweep & standard_rb
aorgazf Jul 16, 2024
a18a6f1
cleanup the cherry-picked commits
hay-k Aug 13, 2024
48e8d26
revert a5013cbb9c0f9145b1889943b245e2b928e4989c
hay-k Aug 13, 2024
39f8fc1
remove duplicate block
hay-k Aug 13, 2024
169f397
fix reference to shape
hay-k Aug 14, 2024
6110d39
fix pulse shape deserialization not working for Custom, IIR, and SNZ
hay-k Aug 15, 2024
f34b3fb
update qblox test setup
hay-k Aug 20, 2024
4074c9d
workaround to make experiments with start sweeper on non RO lines mov…
hay-k Sep 18, 2024
108c3c2
trim waveforms of pulses with custom shapes for shorter pulse durations
hay-k Sep 20, 2024
9242c2a
workaround to make sure RO pulse does not overlap with duration-swept…
hay-k Sep 20, 2024
730e291
change order of arctan2 arguments
hay-k Sep 20, 2024
1e56825
take sequencer number from defaults
hay-k Sep 26, 2024
035b325
Fix for frequencies not overlapping. With @aorgazf.
jevillegasdatTII Oct 9, 2024
f85447a
fix result tests
hay-k Oct 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 52 additions & 13 deletions src/qibolab/instruments/qblox/cluster_qcm_bb.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,25 +201,54 @@ def setup(self, **settings):
"""
pass

def _get_next_sequencer(self, port, frequency, qubits: dict):
"""Retrieves and configures the next avaliable sequencer.
def _get_next_sequencer(
self, port: str, frequency: float, qubits: dict, couplers: dict
):
"""Retrieves and configures the next available sequencer.

The parameters of the new sequencer are copied from those of the default sequencer, except for the
intermediate frequency and classification parameters.
Args:
port (str):
frequency ():
qubit ():
port: name of the output port
frequency: NCO frequency
qubits: qubits associated with this sequencer
couplers: couplers associated with this sequencer
Raises:
Exception = If attempting to set a parameter without a connection to the instrument.
"""
# select the qubit with flux line, if present, connected to the specific port
# check if this port is responsible for the flux of any qubit or coupler
qubit = None
for _qubit in qubits.values():
name = _qubit.flux.port.name
module = _qubit.flux.port.module
if _qubit.flux is not None and (name, module) == (port, self):
qubit = _qubit
if _qubit.flux.port is not None:
if (
_qubit.flux.port.name == port
and _qubit.flux.port.module.name == self.name
):
qubit = _qubit
else:
log.warning(f"Qubit {_qubit.name} has no flux line connected")

coupler = None
for _coupler in couplers.values():
if _coupler.flux.port is not None:
if (
_coupler.flux.port.name == port
and _coupler.flux.port.module.name == self.name
):
coupler = _coupler
else:
log.warning(f"Coupler {_coupler.name} has no flux line connected")

if qubit and coupler:
raise ValueError(
f"Port {port} of device {self.name} is configured for more than one line (flux lines of qubit {qubit.name} and coupler {coupler.name}"
)
if qubit:
self._ports[port].offset = qubit.sweetspot
elif coupler:
self._ports[port].offset = coupler.sweetspot
else:
self._ports[port].offset = 0

# select a new sequencer and configure it as required
next_sequencer_number = self._free_sequencers_numbers.pop(0)
Expand Down Expand Up @@ -248,6 +277,7 @@ def _get_next_sequencer(self, port, frequency, qubits: dict):
# create sequencer wrapper
sequencer = Sequencer(next_sequencer_number)
sequencer.qubit = qubit.name if qubit else None
sequencer.coupler = coupler.name if coupler else None
return sequencer

def get_if(self, pulse):
Expand All @@ -267,6 +297,7 @@ def get_if(self, pulse):
def process_pulse_sequence(
self,
qubits: dict,
couplers: dict,
instrument_pulses: PulseSequence,
navgs: int,
nshots: int,
Expand Down Expand Up @@ -349,6 +380,7 @@ def process_pulse_sequence(
port=port,
frequency=self.get_if(non_overlapping_pulses[0]),
qubits=qubits,
couplers=couplers,
)
# add the sequencer to the list of sequencers required by the port
self._sequencers[port].append(sequencer)
Expand Down Expand Up @@ -383,12 +415,13 @@ def process_pulse_sequence(
port=port,
frequency=self.get_if(non_overlapping_pulses[0]),
qubits=qubits,
couplers=couplers,
)
# add the sequencer to the list of sequencers required by the port
self._sequencers[port].append(sequencer)
else:
sequencer = self._get_next_sequencer(
port=port, frequency=0, qubits=qubits
port=port, frequency=0, qubits=qubits, couplers=couplers
)
# add the sequencer to the list of sequencers required by the port
self._sequencers[port].append(sequencer)
Expand Down Expand Up @@ -493,7 +526,13 @@ def process_pulse_sequence(
)

else: # qubit_sweeper_parameters
if sequencer.qubit in [qubit.name for qubit in sweeper.qubits]:
if (
sweeper.qubits
and sequencer.qubit in [q.name for q in sweeper.qubits]
) or (
sweeper.couplers
and sequencer.coupler in [c.name for c in sweeper.couplers]
):
# plays an active role
if sweeper.parameter == Parameter.bias:
reference_value = self._ports[port].offset
Expand Down Expand Up @@ -616,7 +655,7 @@ def process_pulse_sequence(
and pulses[n].sweeper.type == QbloxSweeperType.duration
):
RI = pulses[n].sweeper.register
if pulses[n].type == PulseType.FLUX:
if pulses[n].type in (PulseType.FLUX, PulseType.COUPLERFLUX):
RQ = pulses[n].sweeper.register
else:
RQ = pulses[n].sweeper.aux_register
Expand Down
7 changes: 6 additions & 1 deletion src/qibolab/instruments/qblox/cluster_qcm_rf.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,10 @@ def connect(self, cluster: Cluster = None):
self._ports[port].lo_frequency = self.settings[port][
"lo_frequency"
]
if "mixer_calibration" in self.settings[port]:
self._ports[port].mixer_calibration = self.settings[port][
"mixer_calibration"
]
self._ports[port].attenuation = self.settings[port]["attenuation"]
self._ports[port].hardware_mod_en = True
self._ports[port].nco_freq = 0
Expand Down Expand Up @@ -289,6 +293,7 @@ def get_if(self, pulse):
def process_pulse_sequence(
self,
qubits: dict,
couplers: dict,
instrument_pulses: PulseSequence,
navgs: int,
nshots: int,
Expand Down Expand Up @@ -610,7 +615,7 @@ def process_pulse_sequence(
and pulses[n].sweeper.type == QbloxSweeperType.duration
):
RI = pulses[n].sweeper.register
if pulses[n].type == PulseType.FLUX:
if pulses[n].type in (PulseType.FLUX, PulseType.COUPLERFLUX):
RQ = pulses[n].sweeper.register
else:
RQ = pulses[n].sweeper.aux_register
Expand Down
15 changes: 13 additions & 2 deletions src/qibolab/instruments/qblox/cluster_qrm_rf.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,10 @@ def connect(self, cluster: Cluster = None):
self._ports["o1"].lo_frequency = self.settings["o1"][
"lo_frequency"
]
if "mixer_calibration" in self.settings["o1"]:
self._ports["o1"].mixer_calibration = self.settings["o1"][
"mixer_calibration"
]
self._ports["o1"].hardware_mod_en = True
self._ports["o1"].nco_freq = 0
self._ports["o1"].nco_phase_offs = 0
Expand Down Expand Up @@ -337,6 +341,7 @@ def get_if(self, pulse: Pulse):
def process_pulse_sequence(
self,
qubits: dict,
couplers: dict,
instrument_pulses: PulseSequence,
navgs: int,
nshots: int,
Expand Down Expand Up @@ -740,7 +745,10 @@ def process_pulse_sequence(
and pulses[n].sweeper.type == QbloxSweeperType.duration
):
RI = pulses[n].sweeper.register
if pulses[n].type == PulseType.FLUX:
if pulses[n].type in (
PulseType.FLUX,
PulseType.COUPLERFLUX,
):
RQ = pulses[n].sweeper.register
else:
RQ = pulses[n].sweeper.aux_register
Expand Down Expand Up @@ -787,7 +795,10 @@ def process_pulse_sequence(
and pulses[n].sweeper.type == QbloxSweeperType.duration
):
RI = pulses[n].sweeper.register
if pulses[n].type == PulseType.FLUX:
if pulses[n].type in (
PulseType.FLUX,
PulseType.COUPLERFLUX,
):
RQ = pulses[n].sweeper.register
else:
RQ = pulses[n].sweeper.aux_register
Expand Down
75 changes: 68 additions & 7 deletions src/qibolab/instruments/qblox/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from qibolab.instruments.qblox.cluster_qcm_rf import QcmRf
from qibolab.instruments.qblox.cluster_qrm_rf import QrmRf
from qibolab.instruments.qblox.sequencer import SAMPLING_RATE
from qibolab.pulses import PulseSequence, PulseType
from qibolab.pulses import Custom, PulseSequence, PulseType, ReadoutPulse
from qibolab.result import SampleResults
from qibolab.sweeper import Parameter, Sweeper, SweeperType
from qibolab.unrolling import Bounds
Expand Down Expand Up @@ -102,7 +102,7 @@ def _termination_handler(self, signum, frame):
log.warning("QbloxController: all modules are disconnected.")
exit(0)

def _set_module_channel_map(self, module: QrmRf, qubits: dict):
def _set_module_channel_map(self, module: QrmRf, qubits: dict, couplers: dict):
"""Retrieve all the channels connected to a specific Qblox module.

This method updates the `channel_port_map` attribute of the
Expand All @@ -115,11 +115,16 @@ def _set_module_channel_map(self, module: QrmRf, qubits: dict):
for channel in qubit.channels:
if channel.port and channel.port.module.name == module.name:
module.channel_map[channel.name] = channel
for coupler in couplers.values():
for channel in coupler.channels:
if channel.port and channel.port.module.name == module.name:
module.channel_map[channel.name] = channel
return list(module.channel_map)

def _execute_pulse_sequence(
self,
qubits: dict,
couplers: dict,
sequence: PulseSequence,
options: ExecutionParameters,
sweepers: list() = [], # list(Sweeper) = []
Expand Down Expand Up @@ -172,12 +177,13 @@ def _execute_pulse_sequence(
data = {}
for name, module in self.modules.items():
# from the pulse sequence, select those pulses to be synthesised by the module
module_channels = self._set_module_channel_map(module, qubits)
module_channels = self._set_module_channel_map(module, qubits, couplers)
module_pulses[name] = sequence.get_channel_pulses(*module_channels)

# ask each module to generate waveforms & program and upload them to the device
module.process_pulse_sequence(
qubits,
couplers,
module_pulses[name],
navgs,
nshots,
Expand Down Expand Up @@ -228,7 +234,7 @@ def _execute_pulse_sequence(
return data

def play(self, qubits, couplers, sequence, options):
return self._execute_pulse_sequence(qubits, sequence, options)
return self._execute_pulse_sequence(qubits, couplers, sequence, options)

def sweep(
self,
Expand Down Expand Up @@ -269,10 +275,56 @@ def sweep(
values=sweeper.values,
pulses=ps,
qubits=sweeper.qubits,
couplers=sweeper.couplers,
type=sweeper.type,
)
)

serial_map = {p.serial: p.serial for p in sequence_copy.ro_pulses}
for sweeper in sweepers_copy:
if sweeper.parameter in (Parameter.duration, Parameter.start) and not any(
pulse.type is PulseType.READOUT for pulse in sweeper.pulses
):
for pulse in sequence_copy.ro_pulses:
current_serial = pulse.serial
if sweeper.parameter is Parameter.duration:
sweep_values = sweeper.get_values(sweeper.pulses[0].duration)
if (
max_finish := sweeper.pulses[0].start + np.max(sweep_values)
) > pulse.start:
pulse.start = max_finish
serial_map[pulse.serial] = current_serial
if sweeper.parameter is Parameter.start:
idx = sequence_copy.index(pulse)
padded_pulse = ReadoutPulse(
start=0,
duration=pulse.start + pulse.duration,
amplitude=pulse.amplitude,
frequency=pulse.frequency,
relative_phase=pulse.relative_phase,
shape=Custom(
envelope_i=np.concatenate(
(
np.zeros(pulse.start),
pulse.envelope_waveform_i().data
/ pulse.amplitude,
)
),
envelope_q=np.concatenate(
(
np.zeros(pulse.start),
pulse.envelope_waveform_q().data
/ pulse.amplitude,
)
),
),
channel=pulse.channel,
qubit=pulse.qubit,
)
serial_map[padded_pulse.serial] = current_serial
sequence_copy[idx] = padded_pulse
sweeper.pulses.append(padded_pulse)

# reverse sweepers exept for res punchout att
contains_attenuation_frequency = any(
sweepers_copy[i].parameter == Parameter.attenuation
Expand All @@ -292,6 +344,7 @@ def sweep(
# execute the each sweeper recursively
self._sweep_recursion(
qubits,
couplers,
sequence_copy,
options,
*tuple(sweepers_copy),
Expand All @@ -301,13 +354,14 @@ def sweep(
# return the results using the original serials
serial_results = {}
for pulse in sequence_copy.ro_pulses:
serial_results[map_id_serial[pulse.id]] = id_results[pulse.id]
serial_results[serial_map[map_id_serial[pulse.id]]] = id_results[pulse.id]
serial_results[pulse.qubit] = id_results[pulse.id]
return serial_results

def _sweep_recursion(
self,
qubits,
couplers,
sequence,
options: ExecutionParameters,
*sweepers,
Expand Down Expand Up @@ -386,14 +440,18 @@ def _sweep_recursion(
if len(sweepers) > 1:
self._sweep_recursion(
qubits,
couplers,
sequence,
options,
*sweepers[1:],
results=results,
)
else:
result = self._execute_pulse_sequence(
qubits=qubits, sequence=sequence, options=options
qubits=qubits,
couplers=couplers,
sequence=sequence,
options=options,
)
for pulse in sequence.ro_pulses:
if results[pulse.id]:
Expand Down Expand Up @@ -437,9 +495,11 @@ def _sweep_recursion(
values=_values,
pulses=sweeper.pulses,
qubits=sweeper.qubits,
couplers=sweeper.couplers,
)
self._sweep_recursion(
qubits,
couplers,
sequence,
options,
*((split_sweeper,) + sweepers[1:]),
Expand Down Expand Up @@ -486,7 +546,7 @@ def _sweep_recursion(
# qubits[pulse.qubit].drive.gain = 1

result = self._execute_pulse_sequence(
qubits, sequence, options, sweepers
qubits, couplers, sequence, options, sweepers
)
self._add_to_results(sequence, results, result)
else:
Expand All @@ -509,6 +569,7 @@ def _sweep_recursion(

res = self._execute_pulse_sequence(
qubits,
couplers,
sequence,
replace(options, nshots=_nshots),
sweepers,
Expand Down
Loading
Loading