Skip to content

Commit

Permalink
Merge branch 'main' into qpy-paramexpr-native
Browse files Browse the repository at this point in the history
  • Loading branch information
mtreinish authored Oct 24, 2024
2 parents 54ef86e + f2e07bc commit 7d1fbfe
Show file tree
Hide file tree
Showing 148 changed files with 3,272 additions and 1,954 deletions.
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

75 changes: 41 additions & 34 deletions crates/accelerate/src/target_transpiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use std::ops::Index;
use ahash::RandomState;

use hashbrown::HashSet;
use indexmap::{IndexMap, IndexSet};
use indexmap::IndexMap;
use itertools::Itertools;
use nullable_index_map::NullableIndexMap;
use pyo3::{
Expand Down Expand Up @@ -57,7 +57,7 @@ type GateMapState = Vec<(String, Vec<(Option<Qargs>, Option<InstructionPropertie

/// Represents a Qiskit `Gate` object or a Variadic instruction.
/// Keeps a reference to its Python instance for caching purposes.
#[derive(Debug, Clone, FromPyObject)]
#[derive(FromPyObject, Debug, Clone)]
pub(crate) enum TargetOperation {
Normal(NormalOperation),
Variadic(PyObject),
Expand All @@ -82,19 +82,23 @@ impl ToPyObject for TargetOperation {
}

impl TargetOperation {
fn num_qubits(&self) -> u32 {
/// Gets the number of qubits of a [TargetOperation], will panic if the operation is [TargetOperation::Variadic].
pub fn num_qubits(&self) -> u32 {
match &self {
Self::Normal(normal) => normal.operation.view().num_qubits(),
Self::Normal(normal) => normal.operation.num_qubits(),
Self::Variadic(_) => {
unreachable!("'num_qubits' property is reserved for normal operations only.")
panic!("'num_qubits' property doesn't exist for Variadic operations")
}
}
}

fn params(&self) -> &[Param] {
/// Gets the parameters of a [TargetOperation], will panic if the operation is [TargetOperation::Variadic].
pub fn params(&self) -> &[Param] {
match &self {
TargetOperation::Normal(normal) => normal.params.as_slice(),
TargetOperation::Variadic(_) => &[],
TargetOperation::Variadic(_) => {
panic!("'parameters' property doesn't exist for Variadic operations")
}
}
}
}
Expand Down Expand Up @@ -173,7 +177,6 @@ pub(crate) struct Target {
#[pyo3(get)]
_gate_name_map: IndexMap<String, TargetOperation, RandomState>,
global_operations: IndexMap<u32, HashSet<String>, RandomState>,
variable_class_operations: IndexSet<String, RandomState>,
qarg_gate_map: NullableIndexMap<Qargs, Option<HashSet<String>>>,
non_global_strict_basis: Option<Vec<String>>,
non_global_basis: Option<Vec<String>>,
Expand Down Expand Up @@ -269,7 +272,6 @@ impl Target {
concurrent_measurements,
gate_map: GateMap::default(),
_gate_name_map: IndexMap::default(),
variable_class_operations: IndexSet::default(),
global_operations: IndexMap::default(),
qarg_gate_map: NullableIndexMap::default(),
non_global_basis: None,
Expand Down Expand Up @@ -302,16 +304,15 @@ impl Target {
)));
}
let mut qargs_val: PropsMap;
match instruction {
match &instruction {
TargetOperation::Variadic(_) => {
qargs_val = PropsMap::with_capacity(1);
qargs_val.extend([(None, None)]);
self.variable_class_operations.insert(name.to_string());
}
TargetOperation::Normal(_) => {
TargetOperation::Normal(normal) => {
if let Some(mut properties) = properties {
qargs_val = PropsMap::with_capacity(properties.len());
let inst_num_qubits = instruction.num_qubits();
let inst_num_qubits = normal.operation.view().num_qubits();
if properties.contains_key(None) {
self.global_operations
.entry(inst_num_qubits)
Expand Down Expand Up @@ -619,7 +620,7 @@ impl Target {
} else if let Some(operation_name) = operation_name {
if let Some(parameters) = parameters {
if let Some(obj) = self._gate_name_map.get(&operation_name) {
if self.variable_class_operations.contains(&operation_name) {
if matches!(obj, TargetOperation::Variadic(_)) {
if let Some(_qargs) = qargs {
let qarg_set: HashSet<PhysicalQubit> = _qargs.iter().cloned().collect();
return Ok(_qargs
Expand Down Expand Up @@ -1053,8 +1054,8 @@ impl Target {
if let Some(Some(qarg_gate_map_arg)) = self.qarg_gate_map.get(qargs).as_ref() {
res.extend(qarg_gate_map_arg.iter().map(|key| key.as_str()));
}
for name in self._gate_name_map.keys() {
if self.variable_class_operations.contains(name) {
for (name, obj) in self._gate_name_map.iter() {
if matches!(obj, TargetOperation::Variadic(_)) {
res.insert(name);
}
}
Expand Down Expand Up @@ -1160,34 +1161,40 @@ impl Target {
}
if gate_prop_name.contains_key(None) {
let obj = &self._gate_name_map[operation_name];
if self.variable_class_operations.contains(operation_name) {
match obj {
TargetOperation::Variadic(_) => {
return qargs.is_none()
|| _qargs.iter().all(|qarg| {
qarg.index() <= self.num_qubits.unwrap_or_default()
}) && qarg_set.len() == _qargs.len();
}
TargetOperation::Normal(obj) => {
let qubit_comparison = obj.operation.num_qubits();
return qubit_comparison == _qargs.len() as u32
&& _qargs.iter().all(|qarg| {
qarg.index() < self.num_qubits.unwrap_or_default()
});
}
}
}
} else {
// Duplicate case is if it contains none
let obj = &self._gate_name_map[operation_name];
match obj {
TargetOperation::Variadic(_) => {
return qargs.is_none()
|| _qargs.iter().all(|qarg| {
qarg.index() <= self.num_qubits.unwrap_or_default()
}) && qarg_set.len() == _qargs.len();
} else {
let qubit_comparison = obj.num_qubits();
}
TargetOperation::Normal(obj) => {
let qubit_comparison = obj.operation.num_qubits();
return qubit_comparison == _qargs.len() as u32
&& _qargs.iter().all(|qarg| {
qarg.index() < self.num_qubits.unwrap_or_default()
});
}
}
} else {
// Duplicate case is if it contains none
if self.variable_class_operations.contains(operation_name) {
return qargs.is_none()
|| _qargs
.iter()
.all(|qarg| qarg.index() <= self.num_qubits.unwrap_or_default())
&& qarg_set.len() == _qargs.len();
} else {
let qubit_comparison = self._gate_name_map[operation_name].num_qubits();
return qubit_comparison == _qargs.len() as u32
&& _qargs
.iter()
.all(|qarg| qarg.index() < self.num_qubits.unwrap_or_default());
}
}
} else {
return true;
Expand Down
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
Loading

0 comments on commit 7d1fbfe

Please sign in to comment.