Example #1
0
    def __init__(self,
                 duration: int,
                 kernel: Optional[Kernel] = None,
                 discriminator: Optional[Discriminator] = None,
                 name: Optional[str] = None):
        """Create new acquire command.

        Args:
            duration: Duration of acquisition
            kernel: The data structures defining the measurement kernels
                to be used (from the list of available kernels) and set of parameters
                (if applicable) if the measurement level is 1 or 2.
            discriminator: Discriminators to be used (from the list of available discriminator)
                if the measurement level is 2
            name: Name of this command.

        Raises:
            PulseError: when invalid discriminator or kernel object is input.
        """
        super().__init__(duration=duration)

        self._name = Acquire.create_name(name)

        if kernel and not isinstance(kernel, Kernel):
            raise PulseError('Invalid kernel object is specified.')
        self._kernel = kernel

        if discriminator and not isinstance(discriminator, Discriminator):
            raise PulseError('Invalid discriminator object is specified.')
        self._discriminator = discriminator
Example #2
0
    def __init__(self,
                 command: Acquire,
                 acquire: Union[AcquireChannel, List[AcquireChannel]],
                 mem_slot: Optional[Union[MemorySlot,
                                          List[MemorySlot]]] = None,
                 reg_slots: Optional[Union[RegisterSlot,
                                           List[RegisterSlot]]] = None,
                 mem_slots: Optional[Union[List[MemorySlot]]] = None,
                 reg_slot: Optional[RegisterSlot] = None,
                 name: Optional[str] = None):

        if isinstance(acquire, list) or isinstance(mem_slot,
                                                   list) or reg_slots:
            warnings.warn(
                'The AcquireInstruction on multiple qubits, multiple '
                'memory slots and multiple reg slots is deprecated. The '
                'parameter "mem_slots" has been replaced by "mem_slot" and '
                '"reg_slots" has been replaced by "reg_slot"',
                DeprecationWarning, 3)

        if not isinstance(acquire, list):
            acquire = [acquire]

        if isinstance(acquire[0], Qubit):
            raise PulseError(
                "AcquireInstruction can not be instantiated with Qubits, "
                "which are deprecated.")

        if mem_slot and not isinstance(mem_slot, list):
            mem_slot = [mem_slot]
        elif mem_slots:
            mem_slot = mem_slots

        if reg_slot:
            reg_slot = [reg_slot]
        elif reg_slots and not isinstance(reg_slots, list):
            reg_slot = [reg_slots]
        else:
            reg_slot = reg_slots

        if not (mem_slot or reg_slot):
            raise PulseError('Neither memoryslots or registers were supplied')

        if mem_slot and len(acquire) != len(mem_slot):
            raise PulseError(
                "The number of mem_slots must be equals to the number of acquires"
            )

        if reg_slot:
            if len(acquire) != len(reg_slot):
                raise PulseError("The number of reg_slots must be equals "
                                 "to the number of acquires")
        else:
            reg_slot = []

        super().__init__(command, *acquire, *mem_slot, *reg_slot, name=name)

        self._acquires = acquire
        self._mem_slots = mem_slot
        self._reg_slots = reg_slot
Example #3
0
    def __init__(self,
                 pulse: Pulse,
                 channel: PulseChannel,
                 name: Optional[str] = None):
        """Create a new pulse instruction.

        Args:
            pulse: A pulse waveform description, such as
                   :py:class:`~qiskit.pulse.library.Waveform`.
            channel: The channel to which the pulse is applied.
            name: Name of the instruction for display purposes. Defaults to ``pulse.name``.

        Raises:
            PulseError: If pulse is not a Pulse type.
        """
        if not isinstance(pulse, Pulse):
            raise PulseError(
                "The `pulse` argument to `Play` must be of type `library.Pulse`."
            )
        if not isinstance(channel, PulseChannel):
            raise PulseError(
                "The `channel` argument to `Play` must be of type `channels.PulseChannel`."
            )
        if name is None:
            name = pulse.name
        super().__init__(operands=(pulse, channel), name=name)
Example #4
0
    def visit_Call(self, node: ast.Call) -> Union[ast.Call, ast.Constant]:
        """Evaluate function and return ast.Constant if all arguments are bound.

        Args:
            node: Function to evaluate.

        Returns:
            Evaluated value.

        Raises:
            PulseError: When unsupported or unsafe function is specified.
        """
        if not isinstance(node.func, ast.Name):
            raise PulseError("Unsafe expression is detected.")
        node.args = [self.visit(arg) for arg in node.args]
        if all(isinstance(arg, (ast.Constant, ast.Num)) for arg in node.args):
            if node.func.id not in self._math_ops.keys():
                raise PulseError("Function %s is not supported." % node.func.id)
            _args = [arg.n for arg in node.args]
            _val = self._math_ops[node.func.id](*_args)
            if not _val.imag:
                _val = _val.real
            val = ast.Constant(n=_val)
            return ast.copy_location(val, node)
        return node
    def validate_parameters(self) -> None:
        if not _is_parameterized(self.amp) and abs(self.amp) > 1.:
            raise PulseError("The amplitude norm must be <= 1, "
                             "found: {}".format(abs(self.amp)))
        if not _is_parameterized(self.sigma) and self.sigma <= 0:
            raise PulseError("Sigma must be greater than 0.")
        if not _is_parameterized(self.beta) and isinstance(self.beta, complex):
            raise PulseError("Beta must be real.")
        # Check if beta is too large: the amplitude norm must be <=1 for all points
        if (not _is_parameterized(self.beta) and not _is_parameterized(self.sigma)
                and self.beta > self.sigma):
            # If beta <= sigma, then the maximum amplitude is at duration / 2, which is
            # already constrainted by self.amp <= 1

            # 1. Find the first maxima associated with the beta * d/dx gaussian term
            #    This eq is derived from solving for the roots of the norm of the drag function.
            #    There is a second maxima mirrored around the center of the pulse with the same
            #    norm as the first, so checking the value at the first x maxima is sufficient.
            argmax_x = (self.duration / 2
                        - (self.sigma / self.beta) * math.sqrt(self.beta ** 2 - self.sigma ** 2))
            if argmax_x < 0:
                # If the max point is out of range, either end of the pulse will do
                argmax_x = 0

            # 2. Find the value at that maximum
            max_val = continuous.drag(np.array(argmax_x), sigma=self.sigma,
                                      beta=self.beta, amp=self.amp, center=self.duration / 2)
            if abs(max_val) > 1.:
                raise PulseError("Beta is too large; pulse amplitude norm exceeds 1.")
    def validate_parameters(self) -> None:
        """Validate parameters.

        Raises:
            PulseError: If the parameters passed are not valid.
        """
        if self.is_parameterized():
            return

        if self._constraints is not None:
            fargs = _get_expression_args(self._constraints, self.parameters)
            if not bool(self._constraints_lam(*fargs)):
                param_repr = ", ".join(f"{p}={v}" for p, v in self.parameters.items())
                const_repr = str(self._constraints)
                raise PulseError(
                    f"Assigned parameters {param_repr} violate following constraint: {const_repr}."
                )

        if self._limit_amplitude:
            if self._valid_amp_conditions is not None:
                fargs = _get_expression_args(self._valid_amp_conditions, self.parameters)
                check_full_waveform = not bool(self._valid_amp_conditions_lam(*fargs))
            else:
                check_full_waveform = True

            if check_full_waveform:
                # Check full waveform only when the condition is satisified or
                # evaluation condition is not provided.
                # This operation is slower due to overhead of 'get_waveform'.
                if not _is_amplitude_valid(self):
                    param_repr = ", ".join(f"{p}={v}" for p, v in self.parameters.items())
                    raise PulseError(
                        f"Maximum pulse amplitude norm exceeds 1.0 with parameters {param_repr}."
                        "This can be overruled by setting Pulse.limit_amplitude."
                    )
Example #7
0
    def _remove_timeslots(self, time: int, schedule: ScheduleComponent):
        """Delete the timeslots if present for the respective schedule component.

        Args:
            time: The time to remove the timeslots for the ``schedule`` component.
            schedule: The schedule to insert into self.

        Raises:
            PulseError: If timeslots overlap or an invalid start time is provided.
        """
        if not isinstance(time, int):
            raise PulseError("Schedule start time must be an integer.")

        for channel in schedule.channels:

            if channel not in self._timeslots:
                raise PulseError(
                    'The channel {} is not present in the schedule'.format(channel))

            channel_timeslots = self._timeslots[channel]
            for interval in schedule._timeslots[channel]:
                if channel_timeslots:
                    interval = (interval[0] + time, interval[1] + time)
                    index = _interval_index(channel_timeslots, interval)
                    if channel_timeslots[index] == interval:
                        channel_timeslots.pop(index)
                        continue

                raise PulseError(
                    "Cannot find interval ({t0}, {tf}) to remove from "
                    "channel {ch} in Schedule(name='{name}').".format(
                        ch=channel, t0=interval[0], tf=interval[1], name=schedule.name))

            if not channel_timeslots:
                self._timeslots.pop(channel)
    def get_waveform(self) -> Waveform:
        r"""Return a Waveform with samples filled according to the formula that the pulse
        represents and the parameter values it contains.

        Since the returned array is a discretized time series of the continuous function,
        this method uses a midpoint sampler. For ``duration``, return:

        .. math::

            \{f(t+0.5) \in \mathbb{C} | t \in \mathbb{Z} \wedge  0<=t<\texttt{duration}\}

        Returns:
            A waveform representation of this pulse.

        Raises:
            PulseError: When parameters are not assigned.
            PulseError: When expression for pulse envelope is not assigned.
        """
        if self.is_parameterized():
            raise PulseError("Unassigned parameter exists. All parameters must be assigned.")

        if self._envelope is None:
            raise PulseError("Pulse envelope expression is not assigned.")

        fargs = _get_expression_args(self._envelope, self.parameters)
        return Waveform(samples=self._envelope_lam(*fargs), name=self.name)
Example #9
0
    def __init__(self, duration, discriminator=None, kernel=None):
        """Create new acquire command.

        Args:
            duration (int): Duration of acquisition.
            discriminator (Discriminator): Discriminators to be used
                (from the list of available discriminator) if the measurement level is 2.
            kernel (Kernel): The data structures defining the measurement kernels
                to be used (from the list of available kernels) and set of parameters
                (if applicable) if the measurement level is 1 or 2.

        Raises:
            PulseError: when invalid discriminator or kernel object is input.
        """
        super().__init__(duration=duration)

        if discriminator:
            if isinstance(discriminator, Discriminator):
                self.discriminator = discriminator
            else:
                raise PulseError('Invalid discriminator object is specified.')
        else:
            self.discriminator = None

        if kernel:
            if isinstance(kernel, Kernel):
                self.kernel = kernel
            else:
                raise PulseError('Invalid kernel object is specified.')
        else:
            self.kernel = None
Example #10
0
    def __init__(self,
                 pulse: Pulse,
                 channel: PulseChannel,
                 name: Optional[str] = None):
        """Create a new pulse instruction.

        Args:
            pulse: A pulse waveform description, such as
                   :py:class:`~qiskit.pulse.library.Waveform`.
            channel: The channel to which the pulse is applied.
            name: Name of the instruction for display purposes. Defaults to ``pulse.name``.

        Raises:
            PulseError: If pulse is not a Pulse type.
        """
        if not isinstance(pulse, Pulse):
            raise PulseError(
                "The `pulse` argument to `Play` must be of type `library.Pulse`."
            )
        if not isinstance(channel, PulseChannel):
            raise PulseError(
                "The `channel` argument to `Play` must be of type "
                "`channels.PulseChannel`.")
        if name is None:
            name = pulse.name
        super().__init__((pulse, channel), None, (channel, ), name=name)

        if pulse.is_parameterized():
            for value in pulse.parameters.values():
                if isinstance(value, ParameterExpression):
                    for param in value.parameters:
                        # Table maps parameter to operand index, 0 for ``pulse``
                        self._parameter_table[param].append(0)
    def get(
        self,
        instruction: Union[str, Instruction],
        qubits: Union[int, Iterable[int]],
        *params: Union[complex, ParameterExpression],
        **kwparams: Union[complex, ParameterExpression],
    ) -> Union[Schedule, ScheduleBlock]:
        """Return the defined :py:class:`~qiskit.pulse.Schedule` or
        :py:class:`~qiskit.pulse.ScheduleBlock` for the given instruction on the given qubits.

        If all keys are not specified this method returns schedule with unbound parameters.

        Args:
            instruction: Name of the instruction or the instruction itself.
            qubits: The qubits for the instruction.
            *params: Command parameters for generating the output schedule.
            **kwparams: Keyworded command parameters for generating the schedule.

        Returns:
            The Schedule defined for the input.

        Raises:
            PulseError: When invalid parameters are specified.
        """
        instruction = _get_instruction_string(instruction)
        self.assert_has(instruction, qubits)
        generator = self._map[instruction][_to_tuple(qubits)]

        _error_message = (
            f"*params={params}, **kwparams={kwparams} do not match with "
            f"the schedule generator signature {generator.signature}."
        )

        function = generator.function
        if callable(function):
            try:
                # callables require full binding, but default values can exist.
                binds = generator.signature.bind(*params, **kwparams)
                binds.apply_defaults()
            except TypeError as ex:
                raise PulseError(_error_message) from ex
            return function(**binds.arguments)

        try:
            # schedules allow partial binding
            binds = generator.signature.bind_partial(*params, **kwparams)
        except TypeError as ex:
            raise PulseError(_error_message) from ex

        if len(binds.arguments) > 0:
            value_dict = dict()
            for param in function.parameters:
                try:
                    value_dict[param] = binds.arguments[param.name]
                except KeyError:
                    pass

            return function.assign_parameters(value_dict, inplace=False)
        else:
            return function
Example #12
0
    def __init__(self,
                 command: Acquire,
                 qubits: Union[Qubit, List[Qubit]],
                 mem_slots: Union[MemorySlot, List[MemorySlot]],
                 reg_slots: Union[RegisterSlot, List[RegisterSlot]] = None,
                 start_time: int = 0):
        if isinstance(qubits, Qubit):
            qubits = [qubits]
        if mem_slots:
            if isinstance(mem_slots, MemorySlot):
                mem_slots = [mem_slots]
            elif len(qubits) != len(mem_slots):
                raise PulseError("#mem_slots must be equals to #qubits")
        if reg_slots:
            if isinstance(reg_slots, RegisterSlot):
                reg_slots = [reg_slots]
            if len(qubits) != len(reg_slots):
                raise PulseError("#reg_slots must be equals to #qubits")
        else:
            reg_slots = []

        # TODO: more precise time-slots
        slots = [
            Timeslot(Interval(0, command.duration), q.acquire) for q in qubits
        ]
        slots.extend([
            Timeslot(Interval(0, command.duration), mem) for mem in mem_slots
        ])

        super().__init__(command, start_time, TimeslotCollection(slots))

        self._qubits = qubits
        self._mem_slots = mem_slots
        self._reg_slots = reg_slots
 def validate_parameters(self) -> None:
     if not _is_parameterized(self.amp) and abs(self.amp) > 1.:
         raise PulseError("The amplitude norm must be <= 1, "
                          "found: {}".format(abs(self.amp)))
     if not _is_parameterized(self.sigma) and self.sigma <= 0:
         raise PulseError("Sigma must be greater than 0.")
     if not _is_parameterized(self.width) and self.width < 0 or self.width >= self.duration:
         raise PulseError("The pulse width must be at least 0 and less than its duration.")
Example #14
0
 def validate_parameters(self) -> None:
     if not _is_parameterized(
             self.amp) and abs(self.amp) > 1.0 and self.limit_amplitude:
         raise PulseError(
             f"The amplitude norm must be <= 1, found: {abs(self.amp)}" +
             "This can be overruled by setting Pulse.limit_amplitude.")
     if not _is_parameterized(self.sigma) and self.sigma <= 0:
         raise PulseError("Sigma must be greater than 0.")
Example #15
0
    def get(
        self, instruction: Union[str, Instruction],
        qubits: Union[int, Iterable[int]], *params: Union[int, float, complex,
                                                          ParameterExpression],
        **kwparams: Union[int, float, complex,
                          ParameterExpression]) -> Schedule:
        """Return the defined :py:class:`~qiskit.pulse.Schedule` for the given instruction on
        the given qubits.

        If all keys are not specified this method returns schedule with unbound parameters.

        Args:
            instruction: Name of the instruction or the instruction itself.
            qubits: The qubits for the instruction.
            *params: Command parameters for generating the output schedule.
            **kwparams: Keyworded command parameters for generating the schedule.

        Returns:
            The Schedule defined for the input.

        Raises:
            PulseError: When invalid parameters are specified.
        """
        instruction = _get_instruction_string(instruction)
        self.assert_has(instruction, qubits)
        schedule_args_tuple = self._map[instruction][_to_tuple(qubits)]

        # Verify parameter-value mapping
        if len(params) > len(schedule_args_tuple.arguments):
            raise PulseError('Too many values to bind: {}.'.format(', '.join(
                map(str, params))))
        if not all(key in schedule_args_tuple.arguments for key in kwparams):
            raise PulseError('Parameters not defined: {}'.format(', '.join(
                kwparams.keys())))

        bind_parameters = dict(
            zip_longest(schedule_args_tuple.arguments, params))
        bind_parameters.update(kwparams)

        sched = schedule_args_tuple.schedule

        # callback function
        if callable(sched):
            return sched(**bind_parameters)

        # schedule
        if sched.is_parameterized():
            parameter_mapping = dict()
            for param_obj in sched.parameters:
                bind_value = bind_parameters[param_obj.name]
                # if value is not set, keep the parameter unassigned
                if bind_value is not None:
                    parameter_mapping[param_obj] = bind_value
            return deepcopy(sched).assign_parameters(parameter_mapping)

        return sched
Example #16
0
    def _add_timeslots(self, time: int, schedule: Union['Schedule',
                                                        Instruction]) -> None:
        """Update all time tracking within this schedule based on the given schedule.

        Args:
            time: The time to insert the schedule into self.
            schedule: The schedule to insert into self.

        Raises:
            PulseError: If timeslots overlap or an invalid start time is provided.
        """
        if not isinstance(time, int):
            raise PulseError("Schedule start time must be an integer.")

        other_timeslots = _get_timeslots(schedule)
        self._duration = max(self._duration, time + schedule.duration)

        for channel in schedule.channels:
            if channel not in self._timeslots:
                if time == 0:
                    self._timeslots[channel] = copy.copy(
                        other_timeslots[channel])
                else:
                    self._timeslots[channel] = [
                        (i[0] + time, i[1] + time)
                        for i in other_timeslots[channel]
                    ]
                continue

            for idx, interval in enumerate(other_timeslots[channel]):
                if interval[0] + time >= self._timeslots[channel][-1][1]:
                    # Can append the remaining intervals
                    self._timeslots[channel].extend([
                        (i[0] + time, i[1] + time)
                        for i in other_timeslots[channel][idx:]
                    ])
                    break

                try:
                    interval = (interval[0] + time, interval[1] + time)
                    index = _find_insertion_index(self._timeslots[channel],
                                                  interval)
                    self._timeslots[channel].insert(index, interval)
                except PulseError:
                    raise PulseError(
                        "Schedule(name='{new}') cannot be inserted into Schedule(name='{old}') at "
                        "time {time} because its instruction on channel {ch} scheduled from time "
                        "{t0} to {tf} overlaps with an existing instruction."
                        "".format(new=schedule.name or '',
                                  old=self.name or '',
                                  time=time,
                                  ch=channel,
                                  t0=interval[0],
                                  tf=interval[1]))

        _check_nonnegative_timeslot(self._timeslots)
Example #17
0
def gaussian_square(duration: int, amp: complex, sigma: float,
                    risefall: Optional[float] = None, width: Optional[float] = None,
                    name: Optional[str] = None, zero_ends: bool = True) -> 'SamplePulse':
    r"""Generates gaussian square :class:`~qiskit.pulse.SamplePulse`.

    For :math:`d=` ``duration``, :math:`A=` ``amp``, :math:`\sigma=` ``sigma``,
    and :math:`r=` ``risefall``, applies the `midpoint` sampling strategy to
    generate a discrete pulse sampled from the continuous function:

    .. math::

        f(x) = \begin{cases}
                    g(x - r) ) & x\leq r \\
                    A & r\leq x\leq d-r \\
                    g(x - (d - r)) & d-r\leq x
                \end{cases}

    where :math:`g(x)` is the Gaussian function sampled from in :meth:`gaussian`
    with :math:`A=` ``amp``, :math:`\mu=1`, and :math:`\sigma=` ``sigma``. I.e.
    :math:`f(x)` represents a square pulse with smooth Gaussian edges.

    If ``zero_ends == True``, the samples for the Gaussian ramps are remapped as in
    :meth:`gaussian`.

    Args:
        duration: Duration of pulse. Must be greater than zero.
        amp: Pulse amplitude.
        sigma: Width (standard deviation) of Gaussian rise/fall portion of the pulse.
        risefall: Number of samples over which pulse rise and fall happen. Width of
            square portion of pulse will be ``duration-2*risefall``.
        width: The duration of the embedded square pulse. Only one of ``width`` or ``risefall``
               should be specified as the functional form requires
               ``width = duration - 2 * risefall``.
        name: Name of pulse.
        zero_ends: If ``True``, make the first and last sample zero, but rescale to preserve amp.
    Raises:
        PulseError: If ``risefall`` and ``width`` arguments are inconsistent or not enough info.
    """
    if risefall is None and width is None:
        raise PulseError("gaussian_square missing required argument: 'width' or 'risefall'.")
    if risefall is not None:
        if width is None:
            width = duration - 2 * risefall
        elif 2 * risefall + width != duration:
            raise PulseError("Both width and risefall were specified, and they are "
                             "inconsistent: 2 * risefall + width == {} != "
                             "duration == {}.".format(2 * risefall + width, duration))
    center = duration / 2
    zeroed_width = duration if zero_ends else None
    return _sampled_gaussian_square_pulse(duration, amp, center, width, sigma,
                                          zeroed_width=zeroed_width, name=name)
Example #18
0
    def __init__(self,
                 operands: Tuple,
                 duration: int,
                 channels: Tuple[Channel],
                 name: Optional[str] = None):
        """Instruction initializer.

        Args:
            operands: The argument list.
            duration: Length of time taken by the instruction in terms of dt.
            channels: Tuple of pulse channels that this instruction operates on.
            name: Optional display name for this instruction.

        Raises:
            PulseError: If duration is negative.
            PulseError: If the input ``channels`` are not all of
                type :class:`Channel`.
        """
        if not isinstance(duration, (int, np.integer)):
            raise PulseError("Instruction duration must be an integer, "
                             "got {} instead.".format(duration))
        if duration < 0:
            raise PulseError(
                "{} duration of {} is invalid: must be nonnegative."
                "".format(self.__class__.__name__, duration))

        for channel in channels:
            if not isinstance(channel, Channel):
                raise PulseError(
                    "Expected a channel, got {} instead.".format(channel))

        self._duration = duration
        self._channels = channels
        self._timeslots = {
            channel: [(0, self.duration)]
            for channel in channels
        }
        self._operands = operands
        self._name = name
        self._hash = None

        self._parameter_table = defaultdict(list)
        for idx, op in enumerate(operands):
            if isinstance(op, ParameterExpression):
                for param in op.parameters:
                    self._parameter_table[param].append(idx)
            elif isinstance(op, Channel) and op.is_parameterized():
                for param in op.parameters:
                    self._parameter_table[param].append(idx)
Example #19
0
    def __init__(self,
                 index: int,
                 lo_freq: float = None,
                 lo_freq_range: Tuple[float, float] = (0, float("inf"))):
        super().__init__(index)
        self._lo_freq = lo_freq

        if (not isinstance(lo_freq_range, tuple)) or len(lo_freq_range) != 2:
            raise PulseError("Invalid form of lo_freq_range is specified.")
        self._lo_freq_range = LoRange(*lo_freq_range)

        if self._lo_freq:
            if not self._lo_freq_range.includes(self._lo_freq):
                raise PulseError("lo_freq %f must be within lo_freq_range %s" %
                                 (self._lo_freq, self._lo_freq_range))
Example #20
0
def gaussian_square(duration: int,
                    amp: complex,
                    sigma: float,
                    risefall: Optional[float] = None,
                    width: Optional[float] = None,
                    name: Optional[str] = None,
                    zero_ends: bool = True) -> 'SamplePulse':
    """Generates gaussian square `SamplePulse`.

    Centered at `duration/2` and zeroed at `t=0` and `t=duration` to prevent
    large initial/final discontinuities.

    Applies `midpoint` sampling strategy to generate discrete pulse from continuous function.

    Args:
        duration: Duration of pulse. Must be greater than zero.
        amp: Pulse amplitude.
        sigma: Width (standard deviation) of Gaussian rise/fall portion of the pulse.
        risefall: Number of samples over which pulse rise and fall happen. Width of
            square portion of pulse will be `duration-2*risefall`.
        width: The duration of the embedded square pulse. Only one of `width` or `risefall`
               should be specified since width = duration - 2 * risefall.
        name: Name of pulse.
        zero_ends: If True, make the first and last sample zero, but rescale to preserve amp.
    Raises:
        PulseError: If risefall and width arguments are inconsistent or not enough info.
    """
    if risefall is None and width is None:
        raise PulseError(
            "gaussian_square missing required argument: 'width' or 'risefall'."
        )
    if risefall is not None:
        if width is None:
            width = duration - 2 * risefall
        elif 2 * risefall + width != duration:
            raise PulseError(
                "Both width and risefall were specified, and they are "
                "inconsistent: 2 * risefall + width == {} != "
                "duration == {}.".format(2 * risefall + width, duration))
    center = duration / 2
    zeroed_width = duration if zero_ends else None
    return _sampled_gaussian_square_pulse(duration,
                                          amp,
                                          center,
                                          width,
                                          sigma,
                                          zeroed_width=zeroed_width,
                                          name=name)
Example #21
0
    def _mutable_shift(self,
                       time: int
                       ) -> 'Schedule':
        """Return this schedule shifted forward by `time`.

        Args:
            time: Time to shift by

        Raises:
            PulseError: if ``time`` is not an integer.
        """
        if not isinstance(time, int):
            raise PulseError(
                "Schedule start time must be an integer.")

        timeslots = {}
        for chan, ch_timeslots in self._timeslots.items():
            timeslots[chan] = [(ts[0] + time, ts[1] + time) for
                               ts in ch_timeslots]

        _check_nonnegative_timeslot(timeslots)

        self._duration = self._duration + time
        self._timeslots = timeslots
        self.__children = [(orig_time + time, child) for
                           orig_time, child in self._children]
        return self
Example #22
0
def gaussian_square(times: np.ndarray, amp: complex, center: float, square_width: float,
                    sigma: float, zeroed_width: Optional[float] = None) -> np.ndarray:
    r"""Continuous gaussian square pulse.

    Args:
        times: Times to output pulse for.
        amp: Pulse amplitude.
        center: Center of the square pulse component.
        square_width: Width of the square pulse component.
        sigma: Standard deviation of Gaussian rise/fall portion of the pulse.
        zeroed_width: Subtract baseline of gaussian square pulse
            to enforce $\OmegaSquare(center \pm zeroed_width/2)=0$.

    Raises:
        PulseError: if zeroed_width is not compatible with square_width.
    """
    square_start = center-square_width/2
    square_stop = center+square_width/2
    if zeroed_width:
        if zeroed_width < square_width:
            raise PulseError("zeroed_width cannot be smaller than square_width.")
        gaussian_zeroed_width = zeroed_width-square_width
    else:
        gaussian_zeroed_width = None

    funclist = [functools.partial(gaussian, amp=amp, center=square_start, sigma=sigma,
                                  zeroed_width=gaussian_zeroed_width, rescale_amp=True),
                functools.partial(gaussian, amp=amp, center=square_stop, sigma=sigma,
                                  zeroed_width=gaussian_zeroed_width, rescale_amp=True),
                functools.partial(constant, amp=amp)]
    condlist = [times <= square_start, times >= square_stop]
    return np.piecewise(times.astype(np.complex_), condlist, funclist)
    def assign(self, parameter: Parameter,
               value: ParameterValueType) -> "Channel":
        """Return a new channel with the input Parameter assigned to value.

        Args:
            parameter: A parameter in this expression whose value will be updated.
            value: The new value to bind to.

        Returns:
            A new channel with updated parameters.

        Raises:
            PulseError: If the parameter is not present in the channel.
        """
        if parameter not in self.parameters:
            raise PulseError(
                f"Cannot bind parameters ({parameter}) not present in the channel."
            )

        new_index = self.index.assign(parameter, value)
        if not new_index.parameters:
            self._validate_index(new_index)
            new_index = int(new_index)

        return type(self)(new_index)
Example #24
0
def _insertion_index(intervals: List[Interval], new_interval: Interval, index: int = 0) -> int:
    """Using binary search on start times, return the index into `intervals` where the new interval
    belongs, or raise an error if the new interval overlaps with any existing ones.

    Args:
        intervals: A sorted list of non-overlapping Intervals.
        new_interval: The interval for which the index into intervals will be found.
        index: A running tally of the index, for recursion. The user should not pass a value.

    Returns:
        The index into intervals that new_interval should be inserted to maintain a sorted list
        of intervals.

    Raises:
        PulseError: If new_interval overlaps with the given intervals.
    """
    if not intervals:
        return index
    if len(intervals) == 1:
        if _overlaps(intervals[0], new_interval):
            raise PulseError("New interval overlaps with existing.")
        return index if new_interval[1] <= intervals[0][0] else index + 1

    mid_idx = len(intervals) // 2
    if new_interval[1] <= intervals[mid_idx][0]:
        return _insertion_index(intervals[:mid_idx], new_interval, index=index)
    else:
        return _insertion_index(intervals[mid_idx:], new_interval, index=index + mid_idx)
def align_func(schedule: Schedule,
               duration: int,
               func: Callable[[int], float]) -> Schedule:
    """Schedule a list of pulse instructions with schedule position defined by the
    numerical expression.

    Args:
        schedule: Input schedule of which top-level ``child`` nodes will be
            reschedulued.
        duration: Duration of context. This should be larger than the schedule duration.
        func: A function that takes an index of sub-schedule and returns the
            fractional coordinate of of that sub-schedule.
            The returned value should be defined within [0, 1].
            The pulse index starts from 1.

    Returns:
        New schedule with input `schedule`` child schedules and instructions
        aligned with equivalent interval.

    Notes:
        This context is convenient for writing UDD sequence for example.
    """
    if duration < schedule.duration:
        return schedule

    aligned = Schedule()
    for ind, (_, child) in enumerate(schedule._children):
        _t_center = duration * func(ind + 1)
        _t0 = int(_t_center - 0.5 * child.duration)
        if _t0 < 0 or _t0 > duration:
            PulseError('Invalid schedule position t=%d is specified at index=%d' % (_t0, ind))
        aligned.insert(_t0, child, inplace=True)

    return pad(aligned, aligned.channels, until=duration, inplace=True)
    def __init__(self,
                 command,
                 *channels: List[Channel],
                 timeslots: TimeslotCollection = None,
                 name=None):
        """
        command (Command): Pulse command to schedule
        *channels: List of pulse channels to schedule with command
        timeslots: Optional list of timeslots. If channels are supplied timeslots
            cannot also be given
        name: Name of Instruction
        """
        self._command = command
        self._name = name if name else self._command.name

        if timeslots and channels:
            raise PulseError(
                'Channels and timeslots may not both be supplied.')

        if not timeslots:
            duration = command.duration
            self._timeslots = TimeslotCollection(
                *(Timeslot(Interval(0, duration), channel)
                  for channel in channels))
        else:
            self._timeslots = timeslots
Example #27
0
    def align(self, schedule: Schedule) -> Schedule:
        """Reallocate instructions according to the policy.

        Only top-level sub-schedules are aligned. If sub-schedules are nested,
        nested schedules are not recursively aligned.

        Args:
            schedule: Schedule to align.

        Returns:
            Schedule with reallocated instructions.
        """
        instruction_duration_validation(self.duration)

        if self.duration < schedule.duration:
            return schedule

        aligned = Schedule.initialize_from(schedule)
        for ind, (_, child) in enumerate(schedule.children):
            _t_center = self.duration * self._func(ind + 1)
            _t0 = int(_t_center - 0.5 * child.duration)
            if _t0 < 0 or _t0 > self.duration:
                PulseError(
                    "Invalid schedule position t=%d is specified at index=%d" %
                    (_t0, ind))
            aligned.insert(_t0, child, inplace=True)

        return aligned
Example #28
0
    def __init__(self, begin: int, end: int):
        """Create an interval = (begin, end))

        Args:
            begin: begin time of this interval
            end: end time of this interval

        Raises:
            PulseError: when invalid time or duration is specified
        """
        if begin < 0:
            raise PulseError("Cannot create Interval with negative begin time")
        if end < 0:
            raise PulseError("Cannot create Interval with negative end time")
        self._begin = begin
        self._end = end
Example #29
0
def _inline_block(block: ScheduleBlock) -> ScheduleBlock:
    """A helper function to inline subroutine of schedule block.

    .. note:: If subroutine is ``Schedule`` the function raises an error.
    """
    ret_block = ScheduleBlock.initialize_from(block)
    for inst in block.blocks:
        if isinstance(inst, instructions.Call):
            # bind parameter
            subroutine = inst.assigned_subroutine()
            if isinstance(subroutine, Schedule):
                raise PulseError(
                    f"A subroutine {subroutine.name} is a pulse Schedule. "
                    "This program cannot be inserted into ScheduleBlock because "
                    "t0 associated with instruction will be lost.")
            # recursively inline the program
            inline_block = _inline_block(subroutine)
            ret_block.append(inline_block, inplace=True)
        elif isinstance(inst, ScheduleBlock):
            # recursively inline the program
            inline_block = _inline_block(inst)
            ret_block.append(inline_block, inplace=True)
        else:
            ret_block.append(inst, inplace=True)
    return ret_block
Example #30
0
    def __init__(self,
                 *schedules,
                 parameters: Optional[Dict[str, Union[float, complex]]] = None,
                 name: Optional[str] = None):

        warnings.warn(
            'ParameterizedSchedule is deprecated. Use Schedule with '
            'circuit.Parameter objects.', DeprecationWarning)

        full_schedules = []
        parameterized = []
        parameters = parameters or []
        self.name = name or ''
        # partition schedules into callable and schedules
        for schedule in schedules:
            if isinstance(schedule, ParameterizedSchedule):
                parameterized.append(schedule)
                parameters += schedule.parameters
            elif callable(schedule):
                parameterized.append(schedule)
            elif isinstance(schedule, Schedule):
                full_schedules.append(schedule)
            else:
                raise PulseError('Input type: {} not supported'.format(
                    type(schedule)))

        self._parameterized = tuple(parameterized)
        self._schedules = tuple(full_schedules)
        self._parameters = tuple(sorted(set(parameters)))