Repository URL to install this package:
|
Version:
0.24.2 ▾
|
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2019.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""
Configurations for pulse experiments.
"""
from typing import Dict, Union, Tuple, Optional
from .channels import PulseChannel, DriveChannel, MeasureChannel
from .exceptions import PulseError
class Kernel:
"""Settings for this Kernel, which is responsible for integrating time series (raw) data
into IQ points.
"""
def __init__(self, name: Optional[str] = None, **params):
"""Create new kernel.
Args:
name: Name of kernel to be used
params: Any settings for kerneling.
"""
self.name = name
self.params = params
def __repr__(self):
return "{}({}{})".format(
self.__class__.__name__,
"'" + self.name + "', " or "",
", ".join(f"{str(k)}={str(v)}" for k, v in self.params.items()),
)
class Discriminator:
"""Setting for this Discriminator, which is responsible for classifying kerneled IQ points
into 0/1 state results.
"""
def __init__(self, name: Optional[str] = None, **params):
"""Create new discriminator.
Args:
name: Name of discriminator to be used
params: Any settings for discrimination.
"""
self.name = name
self.params = params
def __repr__(self):
return "{}({}{})".format(
self.__class__.__name__,
"'" + self.name + "', " or "",
", ".join(f"{str(k)}={str(v)}" for k, v in self.params.items()),
)
class LoRange:
"""Range of LO frequency."""
def __init__(self, lower_bound: float, upper_bound: float):
self._lb = lower_bound
self._ub = upper_bound
def includes(self, lo_freq: complex) -> bool:
"""Whether `lo_freq` is within the `LoRange`.
Args:
lo_freq: LO frequency to be validated
Returns:
bool: True if lo_freq is included in this range, otherwise False
"""
if self._lb <= abs(lo_freq) <= self._ub:
return True
return False
@property
def lower_bound(self) -> float:
"""Lower bound of the LO range"""
return self._lb
@property
def upper_bound(self) -> float:
"""Upper bound of the LO range"""
return self._ub
def __repr__(self):
return f"{self.__class__.__name__}({self._lb:f}, {self._ub:f})"
def __eq__(self, other):
"""Two LO ranges are the same if they are of the same type, and
have the same frequency range
Args:
other (LoRange): other LoRange
Returns:
bool: are self and other equal.
"""
if type(self) is type(other) and self._ub == other._ub and self._lb == other._lb:
return True
return False
class LoConfig:
"""Pulse channel LO frequency container."""
def __init__(
self,
channel_los: Optional[Dict[PulseChannel, float]] = None,
lo_ranges: Optional[Dict[PulseChannel, Union[LoRange, Tuple[int]]]] = None,
):
"""Lo channel configuration data structure.
Args:
channel_los: Dictionary of mappings from configurable channel to lo
lo_ranges: Dictionary of mappings to be enforced from configurable channel to `LoRange`
Raises:
PulseError: If channel is not configurable or set lo is out of range.
"""
self._q_lo_freq = {}
self._m_lo_freq = {}
self._lo_ranges = {}
lo_ranges = lo_ranges if lo_ranges else {}
for channel, freq in lo_ranges.items():
self.add_lo_range(channel, freq)
channel_los = channel_los if channel_los else {}
for channel, freq in channel_los.items():
self.add_lo(channel, freq)
def add_lo(self, channel: Union[DriveChannel, MeasureChannel], freq: float):
"""Add a lo mapping for a channel."""
if isinstance(channel, DriveChannel):
# add qubit_lo_freq
self.check_lo(channel, freq)
self._q_lo_freq[channel] = freq
elif isinstance(channel, MeasureChannel):
# add meas_lo_freq
self.check_lo(channel, freq)
self._m_lo_freq[channel] = freq
else:
raise PulseError("Specified channel %s cannot be configured." % channel.name)
def add_lo_range(self, channel: DriveChannel, lo_range: Union[LoRange, Tuple[int]]):
"""Add lo range to configuration.
Args:
channel: Channel to add lo range for
lo_range: Lo range to add
"""
if isinstance(lo_range, (list, tuple)):
lo_range = LoRange(*lo_range)
self._lo_ranges[channel] = lo_range
def check_lo(self, channel: Union[DriveChannel, MeasureChannel], freq: float) -> bool:
"""Check that lo is valid for channel.
Args:
channel: Channel to validate lo for
freq: lo frequency
Raises:
PulseError: If freq is outside of channels range
"""
lo_ranges = self._lo_ranges
if channel in lo_ranges:
lo_range = lo_ranges[channel]
if not lo_range.includes(freq):
raise PulseError(f"Specified LO freq {freq:f} is out of range {lo_range}")
def channel_lo(self, channel: Union[DriveChannel, MeasureChannel]) -> float:
"""Return channel lo.
Args:
channel: Channel to get lo for
Raises:
PulseError: If channel is not configured
Returns:
Lo of supplied channel if present
"""
if isinstance(channel, DriveChannel):
if channel in self.qubit_los:
return self.qubit_los[channel]
if isinstance(channel, MeasureChannel):
if channel in self.meas_los:
return self.meas_los[channel]
raise PulseError("Channel %s is not configured" % channel)
@property
def qubit_los(self) -> Dict:
"""Returns dictionary mapping qubit channels (DriveChannel) to los."""
return self._q_lo_freq
@property
def meas_los(self) -> Dict:
"""Returns dictionary mapping measure channels (MeasureChannel) to los."""
return self._m_lo_freq