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

Deprecate Pulse package and dependencies #13164

Merged
merged 31 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
b13979b
Deprecate classes, a couple functions & fix tests
eliarbel Sep 17, 2024
945e7f5
Deprecate classes, a couple functions & fix tests
eliarbel Sep 17, 2024
6d47499
Merge branch 'deprecate-pulse' of github.com:eliarbel/qiskit into dep…
eliarbel Sep 17, 2024
d0de19f
Deprecate some functionality that uses Pulse
eliarbel Sep 22, 2024
ca92308
Filter deprecation warnings in QiskitTestCase
eliarbel Sep 22, 2024
ce2ef84
Fix import path for deprecate_pulse_func
eliarbel Sep 23, 2024
b697bd6
Deprecate `schedule_circuit`
eliarbel Sep 23, 2024
eebb01d
Deprecate compiler's `schedule`, `sequence` and related functions
eliarbel Sep 23, 2024
8bff31c
Deprecate passing SchduleBlock(s) to qpy.dump
eliarbel Sep 25, 2024
c90dd89
More deprecations and misc updates
eliarbel Sep 25, 2024
e94e3ea
Mark deprecated properties as such
eliarbel Sep 29, 2024
fe15972
Add, refine and fix deprecations
eliarbel Oct 6, 2024
9954e7a
Add initial release notes and refine deprecations
eliarbel Oct 7, 2024
3a08782
Warn about pulse gates serialization
eliarbel Oct 9, 2024
e68cfa0
Merge branch 'main' into deprecate-pulse
eliarbel Oct 11, 2024
e15d2ef
Merge branch 'main' into deprecate-pulse
eliarbel Oct 14, 2024
081401e
Handle deprecation warnings in unit testing
eliarbel Oct 14, 2024
5a9a928
Add alternative privates paths & refine deprecations
eliarbel Oct 14, 2024
b46c24e
Catch pulse deprecation warnings in certain methods
eliarbel Oct 14, 2024
9a42679
Misc changes to pulse deprecation functions and release notes
eliarbel Oct 14, 2024
3b2f8a3
Fix lint issues
eliarbel Oct 14, 2024
fd5d8d1
Merge branch 'main' into deprecate-pulse
eliarbel Oct 15, 2024
f73a5b0
Fix CI failure with Python 3.10 in `catch_warnings`
eliarbel Oct 15, 2024
f78a957
Update releasenotes/notes/deprecate-pulse-package-07a621be1db7fa30.yaml
eliarbel Oct 15, 2024
0401194
Indicate explicitly dependency removal or move to Dynamics
eliarbel Oct 17, 2024
dffff06
Merge branch 'main' into deprecate-pulse
ElePT Oct 17, 2024
c8d13f2
Merge branch 'main' into deprecate-pulse
eliarbel Oct 21, 2024
38a8cba
Merge branch 'Qiskit:main' into deprecate-pulse
eliarbel Oct 21, 2024
55cc12f
Merge branch 'deprecate-pulse' of github.com:eliarbel/qiskit into dep…
eliarbel Oct 21, 2024
f80e677
Fix unit test failure
eliarbel Oct 21, 2024
66cbae4
Standardize docstring of `deprecate_pulse_dependency`
eliarbel Oct 22, 2024
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 crates/circuit/src/converters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ impl<'py> FromPyObject<'py> for QuantumCircuitData<'py> {
Ok(QuantumCircuitData {
data: data_borrowed,
name: ob.getattr(intern!(py, "name")).ok(),
calibrations: ob.getattr(intern!(py, "calibrations"))?.extract().ok(),
calibrations: ob
.getattr(intern!(py, "_calibrations_prop"))?
.extract()
.ok(),
metadata: ob.getattr(intern!(py, "metadata")).ok(),
qregs: ob
.getattr(intern!(py, "qregs"))
Expand Down
72 changes: 68 additions & 4 deletions crates/circuit/src/dag_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,7 @@ impl DAGCircuit {
let out_dict = PyDict::new_bound(py);
out_dict.set_item("name", self.name.as_ref().map(|x| x.clone_ref(py)))?;
out_dict.set_item("metadata", self.metadata.as_ref().map(|x| x.clone_ref(py)))?;
out_dict.set_item("calibrations", self.calibrations.clone())?;
out_dict.set_item("_calibrations_prop", self.calibrations.clone())?;
out_dict.set_item("qregs", self.qregs.clone_ref(py))?;
out_dict.set_item("cregs", self.cregs.clone_ref(py))?;
out_dict.set_item("global_phase", self.global_phase.clone())?;
Expand Down Expand Up @@ -648,7 +648,10 @@ impl DAGCircuit {
let dict_state = state.downcast_bound::<PyDict>(py)?;
self.name = dict_state.get_item("name")?.unwrap().extract()?;
self.metadata = dict_state.get_item("metadata")?.unwrap().extract()?;
self.calibrations = dict_state.get_item("calibrations")?.unwrap().extract()?;
self.calibrations = dict_state
.get_item("_calibrations_prop")?
.unwrap()
.extract()?;
self.qregs = dict_state.get_item("qregs")?.unwrap().extract()?;
self.cregs = dict_state.get_item("cregs")?.unwrap().extract()?;
self.global_phase = dict_state.get_item("global_phase")?.unwrap().extract()?;
Expand Down Expand Up @@ -864,8 +867,15 @@ impl DAGCircuit {
///
/// The custom pulse definition of a given gate is of the form
/// {'gate_name': {(qubits, params): schedule}}
///
/// DEPRECATED since Qiskit 1.3.0 and will be removed in Qiskit 2.0.0
#[getter]
fn get_calibrations(&self) -> HashMap<String, Py<PyDict>> {
fn get_calibrations(&self, py: Python) -> HashMap<String, Py<PyDict>> {
emit_pulse_dependency_deprecation(
py,
"property ``qiskit.dagcircuit.dagcircuit.DAGCircuit.calibrations``",
);

self.calibrations.clone()
}

Expand All @@ -874,8 +884,29 @@ impl DAGCircuit {
/// Args:
/// calibrations (dict): A dictionary of input in the format
/// {'gate_name': {(qubits, gate_params): schedule}}
///
/// DEPRECATED since Qiskit 1.3.0 and will be removed in Qiskit 2.0.0
#[setter]
fn set_calibrations(&mut self, calibrations: HashMap<String, Py<PyDict>>) {
fn set_calibrations(&mut self, py: Python, calibrations: HashMap<String, Py<PyDict>>) {
emit_pulse_dependency_deprecation(
py,
"property ``qiskit.dagcircuit.dagcircuit.DAGCircuit.calibrations``",
);

self.calibrations = calibrations;
}

// This is an alternative and Python-private path to 'get_calibration' to avoid
// deprecation warnings
#[getter(_calibrations_prop)]
fn get_calibrations_prop(&self) -> HashMap<String, Py<PyDict>> {
self.calibrations.clone()
}

// This is an alternative and Python-private path to 'set_calibration' to avoid
// deprecation warnings
#[setter(_calibrations_prop)]
fn set_calibrations_prop(&mut self, calibrations: HashMap<String, Py<PyDict>>) {
self.calibrations = calibrations;
}

Expand All @@ -898,6 +929,11 @@ impl DAGCircuit {
schedule: Py<PyAny>,
mut params: Option<Bound<'py, PyAny>>,
) -> PyResult<()> {
emit_pulse_dependency_deprecation(
py,
"method ``qiskit.dagcircuit.dagcircuit.DAGCircuit.add_calibration``",
);

if gate.is_instance(imports::GATE.get_bound(py))? {
params = Some(gate.getattr(intern!(py, "params"))?);
gate = gate.getattr(intern!(py, "name"))?;
Expand Down Expand Up @@ -955,7 +991,18 @@ def _format(operand):

/// Return True if the dag has a calibration defined for the node operation. In this
/// case, the operation does not need to be translated to the device basis.
///
/// DEPRECATED since Qiskit 1.3.0 and will be removed in Qiskit 2.0.0
fn has_calibration_for(&self, py: Python, node: PyRef<DAGOpNode>) -> PyResult<bool> {
emit_pulse_dependency_deprecation(
py,
"method ``qiskit.dagcircuit.dagcircuit.DAGCircuit.has_calibration_for``",
);

self._has_calibration_for(py, node)
}

fn _has_calibration_for(&self, py: Python, node: PyRef<DAGOpNode>) -> PyResult<bool> {
if !self
.calibrations
.contains_key(node.instruction.operation.name())
Expand Down Expand Up @@ -6960,3 +7007,20 @@ fn add_global_phase(py: Python, phase: &Param, other: &Param) -> PyResult<Param>
}

type SortKeyType<'a> = (&'a [Qubit], &'a [Clbit]);

/// Emit a Python `DeprecationWarning` for pulse-related dependencies.
fn emit_pulse_dependency_deprecation(py: Python, msg: &str) {
let _ = imports::WARNINGS_WARN.get_bound(py).call1((
PyString::new_bound(
py,
&format!(
"The {} is deprecated as of qiskit 1.3.0. It will be removed in Qiskit 2.0.0. \
The entire Qiskit Pulse package is being deprecated \
and this is a dependency on the package.",
msg
),
),
py.get_type_bound::<PyDeprecationWarning>(),
1,
));
}
2 changes: 2 additions & 0 deletions qiskit/assembler/assemble_schedules.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@
from qiskit.pulse import instructions, transforms, library, schedule, channels
from qiskit.qobj import utils as qobj_utils, converters
from qiskit.qobj.converters.pulse_instruction import ParametricPulseShapes
from qiskit.utils.deprecate_pulse import deprecate_pulse_dependency


@deprecate_pulse_dependency
def assemble_schedules(
schedules: List[
Union[
Expand Down
32 changes: 28 additions & 4 deletions qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
from qiskit.circuit.parameter import Parameter
from qiskit.circuit.exceptions import CircuitError
from qiskit.utils import deprecate_func
from qiskit.utils.deprecate_pulse import deprecate_pulse_dependency
from . import _classical_resource_map
from .controlflow import ControlFlowOp, _builder_utils
from .controlflow.builder import CircuitScopeInterface, ControlFlowBuilderBlock
Expand All @@ -70,6 +71,7 @@
from .delay import Delay
from .store import Store


if typing.TYPE_CHECKING:
import qiskit # pylint: disable=cyclic-import
from qiskit.transpiler.layout import TranspileLayout # pylint: disable=cyclic-import
Expand Down Expand Up @@ -1339,34 +1341,55 @@ def op_start_times(self) -> list[int]:
return self._op_start_times

@property
@deprecate_pulse_dependency(is_property=True)
def calibrations(self) -> dict:
"""Return calibration dictionary.

The custom pulse definition of a given gate is of the form
``{'gate_name': {(qubits, params): schedule}}``
"""
return dict(self._calibrations)
return self._calibrations_prop

@calibrations.setter
@deprecate_pulse_dependency(is_property=True)
def calibrations(self, calibrations: dict):
"""Set the circuit calibration data from a dictionary of calibration definition.

Args:
calibrations (dict): A dictionary of input in the format
``{'gate_name': {(qubits, gate_params): schedule}}``
"""
self._calibrations_prop = calibrations

@property
def _calibrations_prop(self) -> dict:
"""An alternative private path to the `calibrations` property for
avoiding deprecation warnings."""
return dict(self._calibrations)

@_calibrations_prop.setter
def _calibrations_prop(self, calibrations: dict):
"""An alternative private path to the `calibrations` property for
avoiding deprecation warnings."""
self._calibrations = defaultdict(dict, calibrations)

@deprecate_pulse_dependency
def has_calibration_for(self, instruction: CircuitInstruction | tuple):
"""Return True if the circuit has a calibration defined for the instruction context. In this
case, the operation does not need to be translated to the device basis.
"""

return self._has_calibration_for(instruction)

def _has_calibration_for(self, instruction: CircuitInstruction | tuple):
"""An alternative private path to the `has_calibration_for` method for
avoiding deprecation warnings."""
if isinstance(instruction, CircuitInstruction):
operation = instruction.operation
qubits = instruction.qubits
else:
operation, qubits, _ = instruction
if not self.calibrations or operation.name not in self.calibrations:
if not self._calibrations_prop or operation.name not in self._calibrations_prop:
return False
qubits = tuple(self.qubits.index(qubit) for qubit in qubits)
params = []
Expand All @@ -1376,7 +1399,7 @@ def has_calibration_for(self, instruction: CircuitInstruction | tuple):
else:
params.append(p)
params = tuple(params)
return (qubits, params) in self.calibrations[operation.name]
return (qubits, params) in self._calibrations_prop[operation.name]

@property
def metadata(self) -> dict:
Expand Down Expand Up @@ -2017,7 +2040,7 @@ def replace_var(var: expr.Var, cache: Mapping[expr.Var, expr.Var]) -> expr.Var:
)
edge_map.update(zip(other.clbits, dest._cbit_argument_conversion(clbits)))

for gate, cals in other.calibrations.items():
for gate, cals in other._calibrations_prop.items():
dest._calibrations[gate].update(cals)

dest.duration = None
Expand Down Expand Up @@ -6477,6 +6500,7 @@ def continue_loop(self) -> InstructionSet:
ContinueLoopOp(self.num_qubits, self.num_clbits), self.qubits, self.clbits, copy=False
)

@deprecate_pulse_dependency
def add_calibration(
self,
gate: Union[Gate, str],
Expand Down
2 changes: 2 additions & 0 deletions qiskit/compiler/scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from qiskit.scheduler.config import ScheduleConfig
from qiskit.scheduler.schedule_circuit import schedule_circuit
from qiskit.utils.parallel import parallel_map
from qiskit.utils.deprecate_pulse import deprecate_pulse_dependency

logger = logging.getLogger(__name__)

Expand All @@ -35,6 +36,7 @@ def _log_schedule_time(start_time, end_time):
logger.info(log_msg)


@deprecate_pulse_dependency
def schedule(
circuits: Union[QuantumCircuit, List[QuantumCircuit]],
backend: Optional[Backend] = None,
Expand Down
2 changes: 2 additions & 0 deletions qiskit/compiler/sequencer.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
from qiskit.pulse import InstructionScheduleMap, Schedule
from qiskit.scheduler import ScheduleConfig
from qiskit.scheduler.sequence import sequence as _sequence
from qiskit.utils.deprecate_pulse import deprecate_pulse_dependency


@deprecate_pulse_dependency
def sequence(
scheduled_circuits: Union[QuantumCircuit, List[QuantumCircuit]],
backend: Optional[Backend] = None,
Expand Down
4 changes: 3 additions & 1 deletion qiskit/compiler/transpiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,14 @@
from qiskit.transpiler.passes.synthesis.high_level_synthesis import HLSConfig
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.transpiler.target import Target
from qiskit.utils.deprecate_pulse import deprecate_pulse_arg

logger = logging.getLogger(__name__)

_CircuitT = TypeVar("_CircuitT", bound=Union[QuantumCircuit, List[QuantumCircuit]])


@deprecate_pulse_arg("inst_map", predicate=lambda inst_map: inst_map is not None)
def transpile( # pylint: disable=too-many-return-statements
circuits: _CircuitT,
backend: Optional[Backend] = None,
Expand Down Expand Up @@ -104,7 +106,7 @@ def transpile( # pylint: disable=too-many-return-statements
will override the backend's.
basis_gates: List of basis gate names to unroll to
(e.g: ``['u1', 'u2', 'u3', 'cx']``). If ``None``, do not unroll.
inst_map: Mapping of unrolled gates to pulse schedules. If this is not provided,
inst_map: DEPRECATED. Mapping of unrolled gates to pulse schedules. If this is not provided,
transpiler tries to get from the backend. If any user defined calibration
is found in the map and this is used in a circuit, transpiler attaches
the custom gate definition to the circuit. This enables one to flexibly
Expand Down
2 changes: 1 addition & 1 deletion qiskit/converters/circuit_to_dagdependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,6 @@ def circuit_to_dagdependency(circuit, create_preds_and_succs=True):
dagdependency._add_predecessors()
dagdependency._add_successors()

dagdependency.calibrations = circuit.calibrations
dagdependency._calibrations = circuit._calibrations_prop

return dagdependency
2 changes: 1 addition & 1 deletion qiskit/converters/circuit_to_dagdependency_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def _circuit_to_dagdependency_v2(circuit):
dagdependency = _DAGDependencyV2()
dagdependency.name = circuit.name
dagdependency.metadata = circuit.metadata
dagdependency.calibrations = circuit.calibrations
dagdependency._calibrations = circuit._calibrations_prop
dagdependency.global_phase = circuit.global_phase

dagdependency.add_qubits(circuit.qubits)
Expand Down
2 changes: 1 addition & 1 deletion qiskit/converters/dag_to_circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def dag_to_circuit(dag, copy_operations=True):
for var in dag.iter_declared_vars():
circuit.add_uninitialized_var(var)
circuit.metadata = dag.metadata
circuit.calibrations = dag.calibrations
circuit._calibrations_prop = dag._calibrations_prop

circuit._data = circuit_data

Expand Down
2 changes: 1 addition & 1 deletion qiskit/converters/dag_to_dagdependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,6 @@ def dag_to_dagdependency(dag, create_preds_and_succs=True):

# copy metadata
dagdependency.global_phase = dag.global_phase
dagdependency.calibrations = dag.calibrations
dagdependency._calibrations_prop = dag._calibrations_prop

return dagdependency
2 changes: 1 addition & 1 deletion qiskit/converters/dag_to_dagdependency_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def _dag_to_dagdependency_v2(dag):
dagdependency.name = dag.name
dagdependency.metadata = dag.metadata
dagdependency.global_phase = dag.global_phase
dagdependency.calibrations = dag.calibrations
dagdependency.calibrations = dag._calibrations_prop

dagdependency.add_qubits(dag.qubits)
dagdependency.add_clbits(dag.clbits)
Expand Down
6 changes: 5 additions & 1 deletion qiskit/converters/dagdependency_to_circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ def dagdependency_to_circuit(dagdependency):
)
circuit.metadata = dagdependency.metadata

circuit.calibrations = dagdependency.calibrations
if hasattr(dagdependency, "_calibrations_prop"):
circuit._calibrations_prop = dagdependency._calibrations_prop
else:
# This can be _DAGDependencyV2
circuit._calibrations_prop = dagdependency.calibrations

for node in dagdependency.topological_nodes():
circuit._append(CircuitInstruction(node.op.copy(), node.qargs, node.cargs))
Expand Down
7 changes: 6 additions & 1 deletion qiskit/converters/dagdependency_to_dag.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

"""Helper function for converting a dag dependency to a dag circuit"""
from qiskit.dagcircuit.dagcircuit import DAGCircuit
from qiskit.dagcircuit.dagdependency import DAGDependency


def dagdependency_to_dag(dagdependency):
Expand Down Expand Up @@ -44,6 +45,10 @@ def dagdependency_to_dag(dagdependency):

# copy metadata
dagcircuit.global_phase = dagdependency.global_phase
dagcircuit.calibrations = dagdependency.calibrations
if isinstance(dagdependency, DAGDependency):
dagcircuit._calibrations_prop = dagdependency._calibrations_prop
else:
# This can be _DAGDependencyV2
dagcircuit._calibrations_prop = dagdependency.calibrations

return dagcircuit
Loading
Loading