Skip to content

Commit 056f286

Browse files
eliarbelElePT
andauthored
Deprecate Pulse package and dependencies (#13164)
* Deprecate classes, a couple functions & fix tests * Deprecate classes, a couple functions & fix tests * Deprecate some functionality that uses Pulse * Filter deprecation warnings in QiskitTestCase * Fix import path for deprecate_pulse_func * Deprecate `schedule_circuit` * Deprecate compiler's `schedule`, `sequence` and related functions * Deprecate passing SchduleBlock(s) to qpy.dump * More deprecations and misc updates * Mark deprecated properties as such * Add, refine and fix deprecations Added deprecations in `dag_circuit.rs` Added more deprecations in `builder.py` Fixed some deprecations comments * Add initial release notes and refine deprecations * Warn about pulse gates serialization * Handle deprecation warnings in unit testing * Add alternative privates paths & refine deprecations * Catch pulse deprecation warnings in certain methods This commit adds `warnings.catch_warnings` blocks to some methods which are not directly deprecated but otherwise use classes or methods which are being deprecated. Adding this filter to specific methods which are used in common workflow, e.g. transpilation and for which we don't want pulse deprecations to be emitted if pulse is not used directly by the user. * Misc changes to pulse deprecation functions and release notes * Fix lint issues * Fix CI failure with Python 3.10 in `catch_warnings` * Update releasenotes/notes/deprecate-pulse-package-07a621be1db7fa30.yaml Co-authored-by: Elena Peña Tapia <57907331+ElePT@users.noreply.github.com> * Indicate explicitly dependency removal or move to Dynamics * Fix unit test failure The inner class `TestAddCalibration` in `test_transpiler.py::TestTranspilerParallel` calls a deprecated functionality. In an attempt to use `self.assertWarns` inside the inner class, a reference to the outer class was stored as a class attribute of `TestAddCalibration`. dill got confused as to how to serialize the outer class which derives from `QiskitTestCase`. So switched to using `warnings.catch_warnnigs` instead in the inner class to avoid this reference of the outer class in the serialized inner class. * Standardize docstring of `deprecate_pulse_dependency` --------- Co-authored-by: Elena Peña Tapia <57907331+ElePT@users.noreply.github.com>
1 parent 9a1d8d3 commit 056f286

145 files changed

Lines changed: 3207 additions & 1915 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

crates/circuit/src/converters.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,10 @@ impl<'py> FromPyObject<'py> for QuantumCircuitData<'py> {
4747
Ok(QuantumCircuitData {
4848
data: data_borrowed,
4949
name: ob.getattr(intern!(py, "name")).ok(),
50-
calibrations: ob.getattr(intern!(py, "calibrations"))?.extract().ok(),
50+
calibrations: ob
51+
.getattr(intern!(py, "_calibrations_prop"))?
52+
.extract()
53+
.ok(),
5154
metadata: ob.getattr(intern!(py, "metadata")).ok(),
5255
qregs: ob
5356
.getattr(intern!(py, "qregs"))

crates/circuit/src/dag_circuit.rs

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,7 @@ impl DAGCircuit {
569569
let out_dict = PyDict::new_bound(py);
570570
out_dict.set_item("name", self.name.as_ref().map(|x| x.clone_ref(py)))?;
571571
out_dict.set_item("metadata", self.metadata.as_ref().map(|x| x.clone_ref(py)))?;
572-
out_dict.set_item("calibrations", self.calibrations.clone())?;
572+
out_dict.set_item("_calibrations_prop", self.calibrations.clone())?;
573573
out_dict.set_item("qregs", self.qregs.clone_ref(py))?;
574574
out_dict.set_item("cregs", self.cregs.clone_ref(py))?;
575575
out_dict.set_item("global_phase", self.global_phase.clone())?;
@@ -648,7 +648,10 @@ impl DAGCircuit {
648648
let dict_state = state.downcast_bound::<PyDict>(py)?;
649649
self.name = dict_state.get_item("name")?.unwrap().extract()?;
650650
self.metadata = dict_state.get_item("metadata")?.unwrap().extract()?;
651-
self.calibrations = dict_state.get_item("calibrations")?.unwrap().extract()?;
651+
self.calibrations = dict_state
652+
.get_item("_calibrations_prop")?
653+
.unwrap()
654+
.extract()?;
652655
self.qregs = dict_state.get_item("qregs")?.unwrap().extract()?;
653656
self.cregs = dict_state.get_item("cregs")?.unwrap().extract()?;
654657
self.global_phase = dict_state.get_item("global_phase")?.unwrap().extract()?;
@@ -864,8 +867,15 @@ impl DAGCircuit {
864867
///
865868
/// The custom pulse definition of a given gate is of the form
866869
/// {'gate_name': {(qubits, params): schedule}}
870+
///
871+
/// DEPRECATED since Qiskit 1.3.0 and will be removed in Qiskit 2.0.0
867872
#[getter]
868-
fn get_calibrations(&self) -> HashMap<String, Py<PyDict>> {
873+
fn get_calibrations(&self, py: Python) -> HashMap<String, Py<PyDict>> {
874+
emit_pulse_dependency_deprecation(
875+
py,
876+
"property ``qiskit.dagcircuit.dagcircuit.DAGCircuit.calibrations``",
877+
);
878+
869879
self.calibrations.clone()
870880
}
871881

@@ -874,8 +884,29 @@ impl DAGCircuit {
874884
/// Args:
875885
/// calibrations (dict): A dictionary of input in the format
876886
/// {'gate_name': {(qubits, gate_params): schedule}}
887+
///
888+
/// DEPRECATED since Qiskit 1.3.0 and will be removed in Qiskit 2.0.0
877889
#[setter]
878-
fn set_calibrations(&mut self, calibrations: HashMap<String, Py<PyDict>>) {
890+
fn set_calibrations(&mut self, py: Python, calibrations: HashMap<String, Py<PyDict>>) {
891+
emit_pulse_dependency_deprecation(
892+
py,
893+
"property ``qiskit.dagcircuit.dagcircuit.DAGCircuit.calibrations``",
894+
);
895+
896+
self.calibrations = calibrations;
897+
}
898+
899+
// This is an alternative and Python-private path to 'get_calibration' to avoid
900+
// deprecation warnings
901+
#[getter(_calibrations_prop)]
902+
fn get_calibrations_prop(&self) -> HashMap<String, Py<PyDict>> {
903+
self.calibrations.clone()
904+
}
905+
906+
// This is an alternative and Python-private path to 'set_calibration' to avoid
907+
// deprecation warnings
908+
#[setter(_calibrations_prop)]
909+
fn set_calibrations_prop(&mut self, calibrations: HashMap<String, Py<PyDict>>) {
879910
self.calibrations = calibrations;
880911
}
881912

@@ -898,6 +929,11 @@ impl DAGCircuit {
898929
schedule: Py<PyAny>,
899930
mut params: Option<Bound<'py, PyAny>>,
900931
) -> PyResult<()> {
932+
emit_pulse_dependency_deprecation(
933+
py,
934+
"method ``qiskit.dagcircuit.dagcircuit.DAGCircuit.add_calibration``",
935+
);
936+
901937
if gate.is_instance(imports::GATE.get_bound(py))? {
902938
params = Some(gate.getattr(intern!(py, "params"))?);
903939
gate = gate.getattr(intern!(py, "name"))?;
@@ -955,7 +991,18 @@ def _format(operand):
955991

956992
/// Return True if the dag has a calibration defined for the node operation. In this
957993
/// case, the operation does not need to be translated to the device basis.
994+
///
995+
/// DEPRECATED since Qiskit 1.3.0 and will be removed in Qiskit 2.0.0
958996
fn has_calibration_for(&self, py: Python, node: PyRef<DAGOpNode>) -> PyResult<bool> {
997+
emit_pulse_dependency_deprecation(
998+
py,
999+
"method ``qiskit.dagcircuit.dagcircuit.DAGCircuit.has_calibration_for``",
1000+
);
1001+
1002+
self._has_calibration_for(py, node)
1003+
}
1004+
1005+
fn _has_calibration_for(&self, py: Python, node: PyRef<DAGOpNode>) -> PyResult<bool> {
9591006
if !self
9601007
.calibrations
9611008
.contains_key(node.instruction.operation.name())
@@ -6960,3 +7007,20 @@ fn add_global_phase(py: Python, phase: &Param, other: &Param) -> PyResult<Param>
69607007
}
69617008

69627009
type SortKeyType<'a> = (&'a [Qubit], &'a [Clbit]);
7010+
7011+
/// Emit a Python `DeprecationWarning` for pulse-related dependencies.
7012+
fn emit_pulse_dependency_deprecation(py: Python, msg: &str) {
7013+
let _ = imports::WARNINGS_WARN.get_bound(py).call1((
7014+
PyString::new_bound(
7015+
py,
7016+
&format!(
7017+
"The {} is deprecated as of qiskit 1.3.0. It will be removed in Qiskit 2.0.0. \
7018+
The entire Qiskit Pulse package is being deprecated \
7019+
and this is a dependency on the package.",
7020+
msg
7021+
),
7022+
),
7023+
py.get_type_bound::<PyDeprecationWarning>(),
7024+
1,
7025+
));
7026+
}

qiskit/assembler/assemble_schedules.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@
2222
from qiskit.pulse import instructions, transforms, library, schedule, channels
2323
from qiskit.qobj import utils as qobj_utils, converters
2424
from qiskit.qobj.converters.pulse_instruction import ParametricPulseShapes
25+
from qiskit.utils.deprecate_pulse import deprecate_pulse_dependency
2526

2627

28+
@deprecate_pulse_dependency
2729
def assemble_schedules(
2830
schedules: List[
2931
Union[

qiskit/circuit/quantumcircuit.py

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
from qiskit.circuit.parameter import Parameter
4848
from qiskit.circuit.exceptions import CircuitError
4949
from qiskit.utils import deprecate_func
50+
from qiskit.utils.deprecate_pulse import deprecate_pulse_dependency
5051
from . import _classical_resource_map
5152
from .controlflow import ControlFlowOp, _builder_utils
5253
from .controlflow.builder import CircuitScopeInterface, ControlFlowBuilderBlock
@@ -70,6 +71,7 @@
7071
from .delay import Delay
7172
from .store import Store
7273

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

13411343
@property
1344+
@deprecate_pulse_dependency(is_property=True)
13421345
def calibrations(self) -> dict:
13431346
"""Return calibration dictionary.
13441347
13451348
The custom pulse definition of a given gate is of the form
13461349
``{'gate_name': {(qubits, params): schedule}}``
13471350
"""
1348-
return dict(self._calibrations)
1351+
return self._calibrations_prop
13491352

13501353
@calibrations.setter
1354+
@deprecate_pulse_dependency(is_property=True)
13511355
def calibrations(self, calibrations: dict):
13521356
"""Set the circuit calibration data from a dictionary of calibration definition.
13531357
13541358
Args:
13551359
calibrations (dict): A dictionary of input in the format
13561360
``{'gate_name': {(qubits, gate_params): schedule}}``
13571361
"""
1362+
self._calibrations_prop = calibrations
1363+
1364+
@property
1365+
def _calibrations_prop(self) -> dict:
1366+
"""An alternative private path to the `calibrations` property for
1367+
avoiding deprecation warnings."""
1368+
return dict(self._calibrations)
1369+
1370+
@_calibrations_prop.setter
1371+
def _calibrations_prop(self, calibrations: dict):
1372+
"""An alternative private path to the `calibrations` property for
1373+
avoiding deprecation warnings."""
13581374
self._calibrations = defaultdict(dict, calibrations)
13591375

1376+
@deprecate_pulse_dependency
13601377
def has_calibration_for(self, instruction: CircuitInstruction | tuple):
13611378
"""Return True if the circuit has a calibration defined for the instruction context. In this
13621379
case, the operation does not need to be translated to the device basis.
13631380
"""
1381+
1382+
return self._has_calibration_for(instruction)
1383+
1384+
def _has_calibration_for(self, instruction: CircuitInstruction | tuple):
1385+
"""An alternative private path to the `has_calibration_for` method for
1386+
avoiding deprecation warnings."""
13641387
if isinstance(instruction, CircuitInstruction):
13651388
operation = instruction.operation
13661389
qubits = instruction.qubits
13671390
else:
13681391
operation, qubits, _ = instruction
1369-
if not self.calibrations or operation.name not in self.calibrations:
1392+
if not self._calibrations_prop or operation.name not in self._calibrations_prop:
13701393
return False
13711394
qubits = tuple(self.qubits.index(qubit) for qubit in qubits)
13721395
params = []
@@ -1376,7 +1399,7 @@ def has_calibration_for(self, instruction: CircuitInstruction | tuple):
13761399
else:
13771400
params.append(p)
13781401
params = tuple(params)
1379-
return (qubits, params) in self.calibrations[operation.name]
1402+
return (qubits, params) in self._calibrations_prop[operation.name]
13801403

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

2020-
for gate, cals in other.calibrations.items():
2043+
for gate, cals in other._calibrations_prop.items():
20212044
dest._calibrations[gate].update(cals)
20222045

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

6503+
@deprecate_pulse_dependency
64806504
def add_calibration(
64816505
self,
64826506
gate: Union[Gate, str],

qiskit/compiler/scheduler.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from qiskit.scheduler.config import ScheduleConfig
2727
from qiskit.scheduler.schedule_circuit import schedule_circuit
2828
from qiskit.utils.parallel import parallel_map
29+
from qiskit.utils.deprecate_pulse import deprecate_pulse_dependency
2930

3031
logger = logging.getLogger(__name__)
3132

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

3738

39+
@deprecate_pulse_dependency(moving_to_dynamics=True)
3840
def schedule(
3941
circuits: Union[QuantumCircuit, List[QuantumCircuit]],
4042
backend: Optional[Backend] = None,

qiskit/compiler/sequencer.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@
2121
from qiskit.pulse import InstructionScheduleMap, Schedule
2222
from qiskit.scheduler import ScheduleConfig
2323
from qiskit.scheduler.sequence import sequence as _sequence
24+
from qiskit.utils.deprecate_pulse import deprecate_pulse_dependency
2425

2526

27+
@deprecate_pulse_dependency(moving_to_dynamics=True)
2628
def sequence(
2729
scheduled_circuits: Union[QuantumCircuit, List[QuantumCircuit]],
2830
backend: Optional[Backend] = None,

qiskit/compiler/transpiler.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,14 @@
3232
from qiskit.transpiler.passes.synthesis.high_level_synthesis import HLSConfig
3333
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
3434
from qiskit.transpiler.target import Target
35+
from qiskit.utils.deprecate_pulse import deprecate_pulse_arg
3536

3637
logger = logging.getLogger(__name__)
3738

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

4041

42+
@deprecate_pulse_arg("inst_map", predicate=lambda inst_map: inst_map is not None)
4143
def transpile( # pylint: disable=too-many-return-statements
4244
circuits: _CircuitT,
4345
backend: Optional[Backend] = None,
@@ -104,7 +106,7 @@ def transpile( # pylint: disable=too-many-return-statements
104106
will override the backend's.
105107
basis_gates: List of basis gate names to unroll to
106108
(e.g: ``['u1', 'u2', 'u3', 'cx']``). If ``None``, do not unroll.
107-
inst_map: Mapping of unrolled gates to pulse schedules. If this is not provided,
109+
inst_map: DEPRECATED. Mapping of unrolled gates to pulse schedules. If this is not provided,
108110
transpiler tries to get from the backend. If any user defined calibration
109111
is found in the map and this is used in a circuit, transpiler attaches
110112
the custom gate definition to the circuit. This enables one to flexibly

qiskit/converters/circuit_to_dagdependency.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,6 @@ def circuit_to_dagdependency(circuit, create_preds_and_succs=True):
4646
dagdependency._add_predecessors()
4747
dagdependency._add_successors()
4848

49-
dagdependency.calibrations = circuit.calibrations
49+
dagdependency._calibrations = circuit._calibrations_prop
5050

5151
return dagdependency

qiskit/converters/circuit_to_dagdependency_v2.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def _circuit_to_dagdependency_v2(circuit):
2727
dagdependency = _DAGDependencyV2()
2828
dagdependency.name = circuit.name
2929
dagdependency.metadata = circuit.metadata
30-
dagdependency.calibrations = circuit.calibrations
30+
dagdependency._calibrations = circuit._calibrations_prop
3131
dagdependency.global_phase = circuit.global_phase
3232

3333
dagdependency.add_qubits(circuit.qubits)

qiskit/converters/dag_to_circuit.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def dag_to_circuit(dag, copy_operations=True):
7070
for var in dag.iter_declared_vars():
7171
circuit.add_uninitialized_var(var)
7272
circuit.metadata = dag.metadata
73-
circuit.calibrations = dag.calibrations
73+
circuit._calibrations_prop = dag._calibrations_prop
7474

7575
circuit._data = circuit_data
7676

0 commit comments

Comments
 (0)