Skip to content
81 changes: 0 additions & 81 deletions qiskit/pulse/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -1645,87 +1645,6 @@ def snapshot(label: str, snapshot_type: str = "statevector"):
append_instruction(instructions.Snapshot(label, snapshot_type=snapshot_type))


def call_schedule(schedule: Schedule):
"""Call a pulse ``schedule`` in the builder context.

Examples:

.. jupyter-execute::

from qiskit import pulse
from qiskit.pulse import builder

d0 = pulse.DriveChannel(0)

sched = pulse.Schedule()
sched += pulse.Play(pulse.Constant(10, 1.0), d0)

with pulse.build() as pulse_prog:
builder.call_schedule(sched)

assert pulse_prog == sched

Args:
Schedule to call.
"""
warnings.warn(
"``call_schedule`` is being deprecated. "
"``call`` function can take both a schedule and a circuit.",
DeprecationWarning,
)

call(schedule)


def call_circuit(circ: circuit.QuantumCircuit):
"""Call a quantum ``circuit`` within the active builder context.

.. note::
Calling gates directly within the pulse builder namespace will be
deprecated in the future in favor of tight integration with a circuit
builder interface which is under development.

Examples:

.. jupyter-execute::

from qiskit import circuit, pulse, schedule, transpile
from qiskit.pulse import builder
from qiskit.test.mock import FakeOpenPulse2Q

backend = FakeOpenPulse2Q()

d0 = pulse.DriveChannel(0)

qc = circuit.QuantumCircuit(2)
qc.cx(0, 1)
qc_transpiled = transpile(qc, optimization_level=3)
sched = schedule(qc_transpiled, backend)

with pulse.build(backend) as pulse_prog:
# with default settings
builder.call_circuit(qc)

with pulse.build(backend) as pulse_prog:
with pulse.transpiler_settings(optimization_level=3):
builder.call_circuit(qc)

assert pulse_prog == sched

.. note:: Requires the active builder context to have a backend set.

Args:
Circuit to call.
"""
warnings.warn(
"``call_circuit`` is being deprecated. "
"``call`` function can take both a schedule and a circuit.",
DeprecationWarning,
)

call(circ)


def call(
target: Union[circuit.QuantumCircuit, Schedule, ScheduleBlock],
name: Optional[str] = None,
Expand Down
32 changes: 1 addition & 31 deletions qiskit/pulse/channels.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,8 @@

import numpy as np

from qiskit.circuit import Parameter
from qiskit.circuit.parameterexpression import ParameterExpression, ParameterValueType
from qiskit.circuit.parameterexpression import ParameterExpression
from qiskit.pulse.exceptions import PulseError
from qiskit.pulse.utils import deprecated_functionality


class Channel(metaclass=ABCMeta):
Expand Down Expand Up @@ -71,10 +69,6 @@ def __init__(self, index: int):
self._index = index
self._hash = hash((self.__class__.__name__, self._index))

self._parameters = set()
if isinstance(index, ParameterExpression):
self._parameters = index.parameters

@property
def index(self) -> Union[int, ParameterExpression]:
"""Return the index of this channel. The index is a label for a control signal line
Expand Down Expand Up @@ -112,30 +106,6 @@ def is_parameterized(self) -> bool:
"""Return True iff the channel is parameterized."""
return isinstance(self.index, ParameterExpression)

@deprecated_functionality
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)

@property
def name(self) -> str:
"""Return the shorthand alias for this channel, which is based on its type and index."""
Expand Down
54 changes: 1 addition & 53 deletions qiskit/pulse/instructions/call.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,12 @@

"""Call instruction that represents calling a schedule as a subroutine."""

from typing import Optional, Union, Dict, Tuple, Any, Set
from typing import Optional, Union, Dict, Tuple, Set

from qiskit.circuit.parameterexpression import ParameterExpression, ParameterValueType
from qiskit.pulse.channels import Channel
from qiskit.pulse.exceptions import PulseError
from qiskit.pulse.instructions import instruction
from qiskit.pulse.utils import format_parameter_value, deprecated_functionality


class Call(instruction.Instruction):
Expand Down Expand Up @@ -58,7 +57,6 @@ def __init__(
value_dict = value_dict or {}

# initialize parameter template
# TODO remove self._parameter_table
if subroutine.is_parameterized():
self._arguments = {par: value_dict.get(par, par) for par in subroutine.parameters}
assigned_subroutine = subroutine.assign_parameters(
Expand Down Expand Up @@ -114,56 +112,6 @@ def assigned_subroutine(self):

return subroutine

def _initialize_parameter_table(self, operands: Tuple[Any]):
"""A helper method to initialize parameter table.

The behavior of the parameter table of the ``Call`` instruction is slightly different from
other instructions. The actual parameter mapper object is defined only in the
subroutine, thus the call instruction doesn't have operand of ``ParameterExpression`` type.
The parameter table is defined as a mapping of parameter objects to assigned values,
whereas the standard instruction stores the mapping to the operand tuple index.

Note that this instruction doesn't immediately bind parameter values when the
:meth:`assign_parameters` method is called with the parameter dictionary.
Instead, this instruction separately keeps the parameter values from the subroutine.
This logic enables the compiler to reuse the subroutine with different parameters.

Args:
operands: List of operands associated with this instruction.
"""
if operands[0].is_parameterized():
for value in operands[0].parameters:
self._parameter_table[value] = value

@deprecated_functionality
def assign_parameters(
self, value_dict: Dict[ParameterExpression, ParameterValueType]
) -> "Call":
"""Store parameters which will be later assigned to the subroutine.

Parameter values are not immediately assigned. The subroutine with parameters
assigned according to the populated parameter table will be generated only when
:func:`~qiskit.pulse.transforms.inline_subroutines` function is applied to this
instruction. Note that parameter assignment logic creates a copy of subroutine
to avoid the mutation problem. This function is usually applied by the Qiskit
compiler when the program is submitted to the backend.

Args:
value_dict: A mapping from Parameters to either numeric values or another
Parameter expression.

Returns:
Self with updated parameters.
"""
for param_obj, assigned_value in value_dict.items():
for key_obj, value in self._parameter_table.items():
# assign value to parameter expression (it can consist of multiple parameters)
if isinstance(value, ParameterExpression) and param_obj in value.parameters:
new_value = format_parameter_value(value.assign(param_obj, assigned_value))
self._parameter_table[key_obj] = new_value

return self

def is_parameterized(self) -> bool:
"""Return True iff the instruction is parameterized."""
return any(isinstance(value, ParameterExpression) for value in self.arguments.values())
Expand Down
93 changes: 1 addition & 92 deletions qiskit/pulse/instructions/instruction.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,11 @@
sched = Schedule()
sched += Delay(duration, channel) # Delay is a specific subclass of Instruction
"""
import warnings
from abc import ABC, abstractmethod
from collections import defaultdict
from typing import Callable, Dict, Iterable, List, Optional, Set, Tuple, Any
from typing import Callable, Iterable, List, Optional, Set, Tuple

from qiskit.circuit.parameterexpression import ParameterExpression, ParameterValueType
from qiskit.pulse.channels import Channel
from qiskit.pulse.exceptions import PulseError
from qiskit.pulse.utils import format_parameter_value, deprecated_functionality


# pylint: disable=missing-return-doc
Expand All @@ -43,46 +39,23 @@ class Instruction(ABC):
def __init__(
self,
operands: Tuple,
duration: int = None,
channels: Tuple[Channel] = None,
name: Optional[str] = None,
):
"""Instruction initializer.

Args:
operands: The argument list.
duration: Deprecated.
channels: Deprecated.
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 duration is not None:
warnings.warn(
"Specifying duration in the constructor is deprecated. "
"Now duration is an abstract property rather than class variable. "
"All subclasses should implement ``duration`` accordingly. "
"See Qiskit-Terra #5679 for more information.",
DeprecationWarning,
)

if channels is not None:
warnings.warn(
"Specifying ``channels`` in the constructor is deprecated. "
"All channels should be stored in ``operands``.",
DeprecationWarning,
)

self._operands = operands
self._name = name
self._hash = None

self._parameter_table = defaultdict(list)
self._initialize_parameter_table(operands)

for channel in self.channels:
if not isinstance(channel, Channel):
raise PulseError(f"Expected a channel, got {channel} instead.")
Expand Down Expand Up @@ -172,17 +145,6 @@ def _instructions(self, time: int = 0) -> Iterable[Tuple[int, "Instruction"]]:
"""
yield (time, self)

def flatten(self) -> "Instruction":
"""Return itself as already single instruction."""

warnings.warn(
"`This method is being deprecated. Please use "
"`qiskit.pulse.transforms.flatten` function with this schedule.",
DeprecationWarning,
)

return self

def shift(self, time: int, name: Optional[str] = None):
"""Return a new schedule shifted forward by `time`.

Expand Down Expand Up @@ -246,59 +208,6 @@ def is_parameterized(self) -> bool:
"""Return True iff the instruction is parameterized."""
return any(chan.is_parameterized() for chan in self.channels)

def _initialize_parameter_table(self, operands: Tuple[Any]):
"""A helper method to initialize parameter table.

Args:
operands: List of operands associated with this instruction.
"""
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 isinstance(op.index, ParameterExpression):
for param in op.index.parameters:
self._parameter_table[param].append(idx)

@deprecated_functionality
def assign_parameters(
self, value_dict: Dict[ParameterExpression, ParameterValueType]
) -> "Instruction":
"""Modify and return self with parameters assigned according to the input.

Args:
value_dict: A mapping from Parameters to either numeric values or another
Parameter expression.

Returns:
Self with updated parameters.
"""
new_operands = list(self.operands)

for parameter in self.parameters:
if parameter not in value_dict:
continue

value = value_dict[parameter]
op_indices = self._parameter_table[parameter]
for op_idx in op_indices:
param_expr = new_operands[op_idx]
new_operands[op_idx] = format_parameter_value(param_expr.assign(parameter, value))

# Update parameter table
entry = self._parameter_table.pop(parameter)
if isinstance(value, ParameterExpression):
for new_parameter in value.parameters:
if new_parameter in self._parameter_table:
new_entry = set(entry + self._parameter_table[new_parameter])
self._parameter_table[new_parameter] = list(new_entry)
else:
self._parameter_table[new_parameter] = entry

self._operands = tuple(new_operands)

return self

def draw(
self,
dt: float = 1,
Expand Down
Loading