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 14 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
5 changes: 4 additions & 1 deletion src/qibolab/couplers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from typing import Dict, Optional, Union

from qibolab.channels import Channel
from qibolab.native import CouplerNatives

QubitId = Union[str, int]
"""Type for Coupler names."""
Expand All @@ -19,7 +20,9 @@ class Coupler:
"Coupler number or name."

sweetspot: float = 0
"Coupler sweetspot to turn it on/off or off/on."
"Coupler sweetspot to center it's flux dependence if needed."
native_pulse: CouplerNatives = field(default_factory=CouplerNatives)
"For now this only contains the calibrated pulse to activate the coupler."

flux: Optional[Channel] = None
"flux (:class:`qibolab.platforms.utils.Channel`): Channel used to send flux pulses to the qubit."
Expand Down
45 changes: 45 additions & 0 deletions src/qibolab/dummy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,31 +52,76 @@ native_gates:
relative_start: 0, phase: 0, type: qd}
MZ: {duration: 2000, amplitude: 0.1, shape: Rectangular(), frequency: 5500000000.0,
relative_start: 0, phase: 0, type: ro}
coupler:
0:
CP: {duration: 30, amplitude: 0.05, shape: Rectangular(), relative_start: 0, type: coupler, coupler: 0}
1:
CP: {duration: 30, amplitude: 0.05, shape: Rectangular(), relative_start: 0, type: coupler, coupler: 1}
3:
CP: {duration: 30, amplitude: 0.05, shape: Rectangular(), relative_start: 0, type: coupler, coupler: 3}
4:
CP: {duration: 30, amplitude: 0.05, shape: Rectangular(), relative_start: 0, type: coupler, coupler: 4}
two_qubit:
0-2:
CZ:
- {duration: 30, amplitude: 0.05, shape: Rectangular(), qubit: 2, relative_start: 0,
type: qf}
- {type: virtual_z, phase: 0.0, qubit: 0}
- {type: virtual_z, phase: 0.0, qubit: 2}
- {duration: 30, amplitude: 0.05, shape: Rectangular(), coupler: 0, relative_start: 0,
type: coupler}
Jacfomg marked this conversation as resolved.
Show resolved Hide resolved
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}
- {duration: 30, amplitude: 0.05, shape: Rectangular(), coupler: 1, relative_start: 0,
type: coupler}
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}
- {duration: 30, amplitude: 0.05, shape: Rectangular(), coupler: 3, relative_start: 0,
type: coupler}
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}
- {duration: 30, amplitude: 0.05, shape: Rectangular(), coupler: 4, relative_start: 0,
type: coupler}
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
120 changes: 94 additions & 26 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,48 @@ 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=0,
duration=sequence.duration + sequence.start,
amplitude=1,
shape="Rectangular",
channel=qubit.flux.name,
qubit=qubit.name,
)
self.signal = f"flux{qubit.name}"
if isinstance(qubit, Coupler):
pulse = CouplerFluxPulse(
start=0,
duration=sequence.duration + sequence.start,
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 @@ -324,7 +348,7 @@ def connect(self):
self.create_device_setup()
# To fully remove logging #configure_logging=False
# I strongly advise to set it to 20 to have time estimates of the experiment duration!
self.session = lo.Session(self.device_setup, log_level=30)
self.session = lo.Session(self.device_setup, log_level=20)
self.device = self.session.connect(do_emulation=self.emulation)
self.is_connected = True

Expand Down Expand Up @@ -590,8 +614,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 +631,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 +793,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 +890,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_qf and seq_cf > seq_qd:
play_after = self.play_after_set(self.sequence_qibo.cf_pulse, "bias_coupler")

readout_schedule = defaultdict(list)
qubit_readout_schedule = defaultdict(list)
Expand All @@ -893,6 +947,16 @@ def measure_relax(self, exp, qubits, relaxation_time, acquisition_type):
for pulse, q, iq_angle in zip(pulses, qubits, iq_angles):
pulse.zhpulse.uid += str(i)

if play_after is None:
exp.delay(
signal=f"measure{q}",
time=self.sequence_qibo.start * NANO_TO_SECONDS,
)
exp.delay(
signal=f"acquire{q}",
time=self.sequence_qibo.start * NANO_TO_SECONDS,
)

# Integration weights definition or load from the chip folder
weights_file = (
INSTRUMENTS_DATA_FOLDER / f"{self.chip}/weights/integration_weights_optimization_qubit_{q}.npy"
Expand Down Expand Up @@ -1060,7 +1124,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 +1162,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
39 changes: 36 additions & 3 deletions src/qibolab/native.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ def from_dict(cls, pulse, coupler):
"""
kwargs = pulse.copy()
kwargs["coupler"] = coupler
kwargs.pop("type")
return cls(**kwargs)

@property
Expand Down Expand Up @@ -210,8 +211,8 @@ def from_dict(cls, name, sequence, qubits, couplers):
qubits (list): List of :class:`qibolab.qubits.Qubit` object for all
qubits in the platform. All qubits are required because the sequence may be
acting on qubits that the implemented gate is not targeting.
couplres (list): List of :class:`qibolab.couplers.Coupler` object for all
couplers in the platform. All couplers are required because the sequence may be
couplers (list): List of :class:`qibolab.couplers.Coupler` object for all
cruplers in the platform. All couplers are required because the sequence may be
Jacfomg marked this conversation as resolved.
Show resolved Hide resolved
acting on couplers that the implemented gate is not targeting.
"""
pulses = []
Expand Down Expand Up @@ -278,7 +279,7 @@ def from_dict(cls, qubit, native_gates):
"""Parse native gates of the qubit from the runcard.

Args:
qubit (:class:`qibolab.platforms.abstract.Qubit`): Qubit object that the
qubit (:class:`qibolab.qubits.Qubit`): Qubit object that the
native gates are acting on.
native_gates (dict): Dictionary with native gate pulse parameters as loaded
from the runcard.
Expand All @@ -298,6 +299,38 @@ def raw(self):
return data


@dataclass
class CouplerNatives:
"""Container with the native single-qubit gates acting on a specific qubit."""

CP: Optional[NativePulse] = None
"""Pulse to activate the coupler."""

@classmethod
def from_dict(cls, coupler, native_gates):
"""Parse coupler native gates from the runcard.

Args:
coupler (:class:`qibolab.couplers.Coupler`): Coupler object that the
native pulses are acting on.
native_gates (dict): Dictionary with native gate pulse parameters as loaded
from the runcard [Reusing the dict from qubits].
"""
pulses = {n: CouplerPulse.from_dict(pulse, coupler=coupler) for n, pulse in native_gates.items()}
return cls(**pulses)

@property
def raw(self):
"""Serialize native gate pulses. ``None`` gates are not included."""
data = {}
for fld in fields(self):
attr = getattr(self, fld.name)
if attr is not None:
data[fld.name] = attr.raw
del data[fld.name]["coupler"]
return data


@dataclass
class TwoQubitNatives:
"""Container with the native two-qubit gates acting on a specific pair of qubits."""
Expand Down
Loading