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

Couplers2 #623

Merged
merged 23 commits into from
Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
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
28 changes: 28 additions & 0 deletions src/qibolab/dummy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,24 +59,52 @@ native_gates:
type: qf}
- {type: virtual_z, phase: 0.0, qubit: 0}
- {type: virtual_z, phase: 0.0, qubit: 2}
iSWAP:
- {duration: 30, amplitude: 0.05, shape: Rectangular(), qubit: 2, relative_start: 0,
type: qf}
- {type: virtual_z, phase: 0.0, qubit: 1}
- {type: virtual_z, phase: 0.0, qubit: 2}
- {duration: 30, amplitude: 0.05, shape: Rectangular(), coupler: 0, relative_start: 0,
type: coupler}
1-2:
CZ:
- {duration: 30, amplitude: 0.05, shape: Rectangular(), qubit: 2, relative_start: 0,
type: qf}
- {type: virtual_z, phase: 0.0, qubit: 1}
- {type: virtual_z, phase: 0.0, qubit: 2}
iSWAP:
- {duration: 30, amplitude: 0.05, shape: Rectangular(), qubit: 2, relative_start: 0,
type: qf}
- {type: virtual_z, phase: 0.0, qubit: 1}
- {type: virtual_z, phase: 0.0, qubit: 2}
- {duration: 30, amplitude: 0.05, shape: Rectangular(), coupler: 1, relative_start: 0,
type: coupler}
2-3:
CZ:
- {duration: 30, amplitude: 0.05, shape: Rectangular(), qubit: 2, relative_start: 0,
type: qf}
- {type: virtual_z, phase: 0.0, qubit: 3}
- {type: virtual_z, phase: 0.0, qubit: 2}
iSWAP:
- {duration: 30, amplitude: 0.05, shape: Rectangular(), qubit: 2, relative_start: 0,
type: qf}
- {type: virtual_z, phase: 0.0, qubit: 1}
- {type: virtual_z, phase: 0.0, qubit: 2}
- {duration: 30, amplitude: 0.05, shape: Rectangular(), coupler: 3, relative_start: 0,
type: coupler}
2-4:
CZ:
- {duration: 30, amplitude: 0.05, shape: Rectangular(), qubit: 2, relative_start: 0,
type: qf}
- {type: virtual_z, phase: 0.0, qubit: 4}
- {type: virtual_z, phase: 0.0, qubit: 2}
iSWAP:
- {duration: 30, amplitude: 0.05, shape: Rectangular(), qubit: 2, relative_start: 0,
type: qf}
- {type: virtual_z, phase: 0.0, qubit: 1}
- {type: virtual_z, phase: 0.0, qubit: 2}
- {duration: 30, amplitude: 0.05, shape: Rectangular(), coupler: 4, relative_start: 0,
type: coupler}

characterization:
single_qubit:
Expand Down
109 changes: 84 additions & 25 deletions src/qibolab/instruments/zhinst.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
from qibo.config import log

from qibolab import AcquisitionType, AveragingMode, ExecutionParameters
from qibolab.couplers import Coupler
from qibolab.instruments.abstract import INSTRUMENTS_DATA_FOLDER, Controller
from qibolab.instruments.port import Port
from qibolab.pulses import CouplerFluxPulse, FluxPulse, PulseSequence, PulseType
from qibolab.qubits import Qubit
from qibolab.sweeper import Parameter

# this env var just needs to be set
Expand All @@ -32,6 +34,8 @@
"SHFSG_FORCE_COMMAND_TABLE": True,
"SHFSG_MIN_PLAYWAVE_HINT": 32,
"SHFSG_MIN_PLAYZERO_HINT": 32,
"HDAWG_MIN_PLAYWAVE_HINT": 64,
"HDAWG_MIN_PLAYZERO_HINT": 64,
}

"""Translating to Zurich ExecutionParameters"""
Expand Down Expand Up @@ -220,28 +224,49 @@ class ZhSweeperLine:
Near Time sweeps
"""

def __init__(self, sweeper, qubit=None, sequence=None):
def __init__(self, sweeper, qubit=None, sequence=None, pulse=None):
self.sweeper = sweeper
"""Qibolab sweeper"""

# TODO: Do something with the pulse coming here
if sweeper.parameter is Parameter.bias:
pulse = FluxPulse(
start=sequence.start,
duration=sequence.duration,
amplitude=1,
shape="Rectangular",
channel=qubit.flux.name,
qubit=qubit.name,
)
if isinstance(qubit, Qubit):
pulse = FluxPulse(
start=sequence.start,
duration=sequence.duration,
amplitude=1,
shape="Rectangular",
channel=qubit.flux.name,
qubit=qubit.name,
)
self.signal = f"flux{qubit.name}"
if isinstance(qubit, Coupler):
# TODO: For the couplers specs is enough, may need changing for 2q coupler routines ?
pulse = CouplerFluxPulse(
start=sequence.start,
duration=sequence.duration,
amplitude=1,
shape="Rectangular",
channel=qubit.flux.name,
qubit=qubit.name,
)
self.signal = f"couplerflux{qubit.name}"

self.pulse = pulse
self.signal = f"flux{qubit.name}"

self.zhpulse = lo.pulse_library.const(
uid=(f"{pulse.type.name.lower()}_{pulse.qubit}_"),
length=round(pulse.duration * NANO_TO_SECONDS, 9),
amplitude=pulse.amplitude,
)

elif sweeper.parameter is Parameter.start:
if pulse:
self.pulse = pulse
self.signal = f"flux{qubit}"

self.zhpulse = ZhPulse(pulse).zhpulse

# Need something better to store multiple sweeps on the same pulse
self.zhsweeper = self.select_sweeper(sweeper)

Expand Down Expand Up @@ -590,8 +615,13 @@ def nt_loop(sweeper):
aux_list[aux_list.index(element)].add_sweeper(sweeper, qubits[pulse.qubit])

if sweeper.parameter.name in SWEEPER_BIAS:
for qubit in sweeper.qubits:
zhsequence[f"flux{qubit.name}"] = [ZhSweeperLine(sweeper, qubit, sequence)]
# TODO: This should be joined
if sweeper.qubits:
for qubit in sweeper.qubits:
zhsequence[f"flux{qubit.name}"] = [ZhSweeperLine(sweeper, qubit, sequence)]
if sweeper.couplers:
for coupler in sweeper.couplers:
zhsequence[f"couplerflux{coupler.name}"] = [ZhSweeperLine(sweeper, coupler, sequence)]

# FIXME: This may not place the Zhsweeper when the start occurs among different sections or lines
if sweeper.parameter.name in SWEEPER_START:
Expand All @@ -602,7 +632,7 @@ def nt_loop(sweeper):
if isinstance(aux_list[aux_list.index(element)], ZhPulse):
aux_list.insert(
aux_list.index(element),
ZhSweeperLine(sweeper, pulse.qubit, sequence),
ZhSweeperLine(sweeper, pulse.qubit, sequence, pulse),
)
break

Expand Down Expand Up @@ -764,15 +794,16 @@ def couplerflux(self, exp, couplers):
i = 0
time = 0
for pulse in self.sequence[f"couplerflux{c}"]:
if not isinstance(pulse, ZhSweeperLine):
pulse.zhpulse.uid += str(i)
exp.delay(
signal=f"couplerflux{c}",
time=round(pulse.pulse.start * NANO_TO_SECONDS, 9) - time,
)
time = round(pulse.pulse.duration * NANO_TO_SECONDS, 9) + round(
pulse.pulse.start * NANO_TO_SECONDS, 9
)
# TODO: Needed ?
# if not isinstance(pulse, ZhSweeperLine):
pulse.zhpulse.uid += str(i)
exp.delay(
signal=f"couplerflux{c}",
time=round(pulse.pulse.start * NANO_TO_SECONDS, 9) - time,
)
time = round(pulse.pulse.duration * NANO_TO_SECONDS, 9) + round(
pulse.pulse.start * NANO_TO_SECONDS, 9
)
# TODO: Check of play sweep doesnt need changes
if isinstance(pulse, ZhSweeperLine):
self.play_sweep(exp, coupler, pulse, section="couplerflux")
Expand Down Expand Up @@ -860,16 +891,40 @@ def play_after_set(sequence, ptype):
def measure_relax(self, exp, qubits, relaxation_time, acquisition_type):
"""qubit readout pulse, data acquisition and qubit relaxation"""
play_after = None

# TODO: This need to be simplified !!!
if len(self.sequence_qibo.qf_pulses) != 0 and len(self.sequence_qibo.qd_pulses) != 0:
play_after = (
self.play_after_set(self.sequence_qibo.qf_pulses, "bias")
if self.sequence_qibo.qf_pulses.finish > self.sequence_qibo.qd_pulses.finish
else self.play_after_set(self.sequence_qibo.qd_pulses, "drive")
)
if len(self.sequence_qibo.cf_pulses) != 0 and len(self.sequence_qibo.qd_pulses) != 0:
play_after = (
self.play_after_set(self.sequence_qibo.cf_pulses, "bias_coupler")
if self.sequence_qibo.cf_pulses.finish > self.sequence_qibo.qd_pulses.finish
else self.play_after_set(self.sequence_qibo.qd_pulses, "drive")
)

elif len(self.sequence_qibo.qf_pulses) != 0:
play_after = self.play_after_set(self.sequence_qibo.qf_pulses, "bias")
elif len(self.sequence_qibo.qd_pulses) != 0:
play_after = self.play_after_set(self.sequence_qibo.qd_pulses, "drive")
elif (
len(self.sequence_qibo.qf_pulses) != 0
and len(self.sequence_qibo.qd_pulses) != 0
and len(self.sequence_qibo.cf_pulses) != 0
):
seq_qf = self.sequence_qibo.qf_pulses.finish
seq_qd = self.sequence_qibo.qd_pulses.finish
seq_cf = self.sequence_qibo.cf_pulses.finish
# add here for flux coupler pulses
if seq_qf > seq_qd and seq_qf > seq_cf:
play_after = self.play_after_set(self.sequence_qibo.qf_pulses, "bias")
elif seq_qd > seq_qf and seq_qd > seq_cf:
play_after = self.play_after_set(self.sequence_qibo.qd_pulses, "drive")
elif seq_cf > seq_qd and seq_cf > seq_qd:
Jacfomg marked this conversation as resolved.
Show resolved Hide resolved
play_after = self.play_after_set(self.sequence_qibo.cf_pulse, "bias_coupler")

readout_schedule = defaultdict(list)
qubit_readout_schedule = defaultdict(list)
Expand Down Expand Up @@ -1060,7 +1115,7 @@ def sweep(self, qubits, couplers, sequence: PulseSequence, options, *sweepers):
self.offsets_off()

# html containing the pulse sequence schedule
# lo.show_pulse_sheet("pulses", self.exp)
lo.show_pulse_sheet("pulses", self.exp)
return results

# TODO: This may work without changes due to couplers
Expand Down Expand Up @@ -1098,8 +1153,12 @@ def sweep_recursion(self, qubits, couplers, exp, exp_calib, exp_options):
sweeper.values *= aux_max

if sweeper.parameter is Parameter.bias:
for qubit in sweeper.qubits:
parameter = ZhSweeperLine(sweeper, qubit, self.sequence_qibo).zhsweeper
if sweeper.qubits:
for qubit in sweeper.qubits:
parameter = ZhSweeperLine(sweeper, qubit, self.sequence_qibo).zhsweeper
if sweeper.couplers:
for qubit in sweeper.couplers:
parameter = ZhSweeperLine(sweeper, qubit, self.sequence_qibo).zhsweeper

elif sweeper.parameter is Parameter.start:
parameter = ZhSweeperLine(sweeper).zhsweeper
Expand Down
27 changes: 26 additions & 1 deletion src/qibolab/platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from qibolab.execution_parameters import ExecutionParameters
from qibolab.instruments.abstract import Controller, Instrument, InstrumentId
from qibolab.native import NativeType
from qibolab.pulses import PulseSequence
from qibolab.pulses import CouplerFluxPulse, PulseSequence
from qibolab.qubits import Qubit, QubitId, QubitPair, QubitPairId
from qibolab.sweeper import Sweeper

Expand Down Expand Up @@ -109,6 +109,9 @@ def setup(self):
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
Jacfomg marked this conversation as resolved.
Show resolved Hide resolved

def start(self):
"""Starts all the instruments."""
Expand Down Expand Up @@ -271,6 +274,16 @@ def create_CZ_pulse_sequence(self, qubits, start=0):
)
return self.pairs[pair].native_gates.CZ.sequence(start)

def create_iSWAP_pulse_sequence(self, qubits, start=0):
# Check in the settings if qubits[0]-qubits[1] is a key
pair = tuple(sorted(self.get_qubit(q) for q in qubits))
if pair not in self.pairs or self.pairs[pair].native_gates.iSWAP is None:
raise_error(
ValueError,
f"Calibration for iSWAP gate between qubits {qubits[0]} and {qubits[1]} not found.",
)
return self.pairs[pair].native_gates.iSWAP.sequence(start)

def create_MZ_pulse(self, qubit, start):
qubit = self.get_qubit(qubit)
return self.qubits[qubit].native_gates.MZ.pulse(start)
Expand All @@ -285,6 +298,18 @@ def create_qubit_readout_pulse(self, qubit, start):
qubit = self.get_qubit(qubit)
return self.create_MZ_pulse(qubit, start)

def create_coupler_pulse(self, coupler, start, duration, amplitude):
pulse = CouplerFluxPulse(
start, # + self.relative_start,
duration,
amplitude,
"Rectangular()",
channel=coupler.flux.name,
qubit=coupler.name,
)

return pulse
Jacfomg marked this conversation as resolved.
Show resolved Hide resolved

# TODO Remove RX90_drag_pulse and RX_drag_pulse, replace them with create_qubit_drive_pulse
# TODO Add RY90 and RY pulses

Expand Down
14 changes: 12 additions & 2 deletions src/qibolab/pulses.py
Original file line number Diff line number Diff line change
Expand Up @@ -777,9 +777,9 @@ def duration(self, value):
value (se_int | int | np.integer): the time in ns.
"""

if not isinstance(value, (se_int, int, np.integer)):
if not isinstance(value, (se_int, int, np.integer, float)):
raise TypeError(
f"duration argument type should be intSymbolicExpression or int, got {type(value).__name__}"
f"duration argument type should be float, intSymbolicExpression or int, got {type(value).__name__}"
)
if not value >= 0:
raise ValueError(f"duration argument must be >= 0, got {value}")
Expand Down Expand Up @@ -1797,6 +1797,16 @@ def qf_pulses(self):
new_pc.add(pulse)
return new_pc

@property
def cf_pulses(self):
"""Returns a new PulseSequence containing only its coupler flux pulses."""

new_pc = PulseSequence()
for pulse in self.pulses:
if pulse.type == PulseType.COUPLERFLUX:
Jacfomg marked this conversation as resolved.
Show resolved Hide resolved
new_pc.add(pulse)
return new_pc

def get_channel_pulses(self, *channels):
"""Returns a new PulseSequence containing only the pulses on a specific set of channels."""

Expand Down
30 changes: 29 additions & 1 deletion tests/test_instruments_zhinst.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
ReadoutPulse,
Rectangular,
)
from qibolab.sweeper import Parameter, Sweeper
from qibolab.sweeper import Parameter, Sweeper, SweeperType

from .conftest import get_instrument

Expand Down Expand Up @@ -118,6 +118,34 @@ def test_zhsequence_couplers(dummy_qrc):
assert len(zhsequence["couplerflux3"]) == 1


def test_zhsequence_couplers_sweeper(dummy_qrc):
ro_pulse = ReadoutPulse(0, 40, 0.05, int(3e9), 0.0, Rectangular(), "ch1", qubit=0)
sequence = PulseSequence()
sequence.add(ro_pulse)
IQM5q = create_platform("zurich")
controller = IQM5q.instruments["EL_ZURO"]

delta_bias_range = np.arange(-1, 1, 0.5)

sweeper = Sweeper(
Parameter.bias,
delta_bias_range,
couplers=[IQM5q.couplers[0]],
type=SweeperType.ABSOLUTE,
)

controller.sequence_zh(sequence, IQM5q.qubits, IQM5q.couplers, sweepers=[sweeper])
zhsequence = controller.sequence

with pytest.raises(AttributeError):
controller.sequence_zh("sequence", IQM5q.qubits, IQM5q.couplers, sweepers=[sweeper])
zhsequence = controller.sequence

assert len(zhsequence) == 2
assert len(zhsequence["readout0"]) == 1
assert len(zhsequence["couplerflux0"]) == 1


def test_zhsequence_multiple_ro(dummy_qrc):
sequence = PulseSequence()
qd_pulse = Pulse(0, 40, 0.05, int(3e9), 0.0, Rectangular(), "ch0", qubit=0)
Expand Down
11 changes: 11 additions & 0 deletions tests/test_platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,17 @@ def test_platform_execute_one_drive_pulse(qpu_platform):
platform.execute_pulse_sequence(sequence, ExecutionParameters(nshots=nshots))


@pytest.mark.qpu
def test_platform_execute_one_coupler_pulse(qpu_platform):
# One drive pulse
platform = qpu_platform
coupler = next(iter(platform.couplers))
sequence = PulseSequence()
sequence.add(platform.create_coupler_pulse(coupler, start=0, duration=200, amplitude=1))
platform.execute_pulse_sequence(sequence, ExecutionParameters(nshots=nshots))
assert len(sequence.cf_pulses) > 0


@pytest.mark.qpu
def test_platform_execute_one_long_drive_pulse(qpu_platform):
# Long duration
Expand Down
Loading