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

Remove calibrations APIs and related functionality #13861

Merged
merged 33 commits into from
Mar 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
04e2c79
Handle ScheduleBlock and pulse gates loading
eliarbel Feb 9, 2025
39d12b4
Add documentation and remove redundant code
eliarbel Feb 9, 2025
ddb4f56
Limit QPY version when generating circuits for compatibility test
eliarbel Feb 9, 2025
06a8551
Handle QPY compatibility testing. Misc other fixes
eliarbel Feb 11, 2025
32ffb79
Merge branch 'main' into remove-pulse-qpy
eliarbel Feb 11, 2025
9ea2fd3
Update qiskit/qpy/binary_io/circuits.py
eliarbel Feb 12, 2025
7aff7e2
Merge branch 'main' into remove-pulse-qpy
eliarbel Feb 12, 2025
27ffb51
Remove pulse from GenericBackendV2
eliarbel Feb 12, 2025
173cea2
Merge remote-tracking branch 'upstream/main' into remove-pulse-generi…
eliarbel Feb 13, 2025
ff66b38
Merge branch 'remove-pulse-qpy' into remove-pulse-calibrations
eliarbel Feb 13, 2025
5eb63c9
Merge branch 'remove-pulse-generic-backendv2' into remove-pulse-calib…
eliarbel Feb 13, 2025
082a26f
First pass
eliarbel Feb 14, 2025
2478527
Merge remote-tracking branch 'upstream/main' into remove-pulse-qpy
eliarbel Feb 16, 2025
17ffa6f
Avoid generating pulse circuits in load_qpy & version >= 2.0
eliarbel Feb 16, 2025
86bbeaf
Merge branch 'remove-pulse-qpy' of github.com:eliarbel/qiskit into re…
eliarbel Feb 16, 2025
f0a4f42
Merge remote-tracking branch 'upstream/main' into remove-pulse-calibr…
eliarbel Feb 16, 2025
c2fabe4
Remove more stuff
eliarbel Feb 16, 2025
fb53bab
Add reno and some misc fixes
eliarbel Feb 17, 2025
c44bc0f
Add recent changes from remove-pulse-qpy branch
eliarbel Feb 17, 2025
30fd28e
Merge remote-tracking branch 'upstream/main' into remove-pulse-qpy
eliarbel Feb 19, 2025
46510ac
Raise QpyError when loading ScheduleBlock payloads
eliarbel Feb 19, 2025
888545b
Clean up TODOs
eliarbel Feb 19, 2025
85c77b7
Merge branch 'remove-pulse-qpy' into remove-pulse-calibrations
eliarbel Feb 19, 2025
b20ef1d
Merge remote-tracking branch 'upstream/main' into remove-pulse-calibr…
eliarbel Feb 20, 2025
0a760e7
Unify transpiler renos w.r.t pulse removal
eliarbel Feb 20, 2025
4f63bb4
Merge remote-tracking branch 'upstream/main' into remove-pulse-calibr…
eliarbel Feb 28, 2025
b54f25e
Remove inst_map from transpile() docstring
eliarbel Feb 28, 2025
31ea945
Small cleanup and reno update
eliarbel Feb 28, 2025
edfd785
Fix lint
eliarbel Feb 28, 2025
bba5f7d
Merge remote-tracking branch 'upstream/main' into remove-pulse-calibr…
eliarbel Feb 28, 2025
e1d8014
Merge remote-tracking branch 'upstream/main' into remove-pulse-calibr…
eliarbel Feb 28, 2025
8e14bd5
Applying comments from Elena's review
eliarbel Mar 1, 2025
6bccdf8
Merge branch 'main' into remove-pulse-calibrations
eliarbel Mar 1, 2025
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
56 changes: 12 additions & 44 deletions crates/accelerate/src/basis/basis_translator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ fn run(
Ok(out_dag)
}

/// Method that extracts all non-calibrated gate instances identifiers from a DAGCircuit.
/// Method that extracts all gate instances identifiers from a DAGCircuit.
fn extract_basis(
py: Python,
circuit: &DAGCircuit,
Expand All @@ -232,10 +232,8 @@ fn extract_basis(
basis: &mut IndexSet<GateIdentifier, ahash::RandomState>,
min_qubits: usize,
) -> PyResult<()> {
for (node, operation) in circuit.op_nodes(true) {
if !circuit.has_calibration_for_index(py, node)?
&& circuit.get_qargs(operation.qubits).len() >= min_qubits
{
for (_node, operation) in circuit.op_nodes(true) {
if circuit.get_qargs(operation.qubits).len() >= min_qubits {
basis.insert((operation.op.name().to_string(), operation.op.num_qubits()));
}
if operation.op.control_flow() {
Expand Down Expand Up @@ -264,11 +262,7 @@ fn extract_basis(
.borrow();
for (index, inst) in circuit_data.iter().enumerate() {
let instruction_object = circuit.get_item(index)?;
let has_calibration = circuit
.call_method1(intern!(py, "_has_calibration_for"), (&instruction_object,))?;
if !has_calibration.is_truthy()?
&& circuit_data.get_qargs(inst.qubits).len() >= min_qubits
{
if circuit_data.get_qargs(inst.qubits).len() >= min_qubits {
basis.insert((inst.op.name().to_string(), inst.op.num_qubits()));
}
if inst.op.control_flow() {
Expand All @@ -287,7 +281,7 @@ fn extract_basis(
}

/// Method that extracts a mapping of all the qargs in the local_source basis
/// obtained from the [Target], to all non-calibrated gate instances identifiers from a DAGCircuit.
/// obtained from the [Target], to all gate instances identifiers from a DAGCircuit.
/// When dealing with `ControlFlowOp` instances the function will perform a recursion call
/// to a variant design to handle instances of `QuantumCircuit`.
fn extract_basis_target(
Expand All @@ -306,9 +300,9 @@ fn extract_basis_target(
ahash::RandomState,
>,
) -> PyResult<()> {
for (node, node_obj) in dag.op_nodes(true) {
for (_node, node_obj) in dag.op_nodes(true) {
let qargs: &[Qubit] = dag.get_qargs(node_obj.qubits);
if dag.has_calibration_for_index(py, node)? || qargs.len() < min_qubits {
if qargs.len() < min_qubits {
continue;
}
// Treat the instruction as on an incomplete basis if the qargs are in the
Expand Down Expand Up @@ -351,8 +345,9 @@ fn extract_basis_target(
unreachable!("Control flow op is not a control flow op. But control_flow is `true`")
};
let bound_inst = op.instruction.bind(py);
// Use python side extraction instead of the Rust method `op.blocks` due to
// required usage of a python-space method `QuantumCircuit.has_calibration_for`.
// TODO: Use Rust method `op.blocks` instead of Python side extraction now that
// the python-space method `QuantumCircuit.has_calibration_for`
// has been removed and we don't need to account for it.
let blocks = bound_inst.getattr("blocks")?.try_iter()?;
for block in blocks {
extract_basis_target_circ(
Expand Down Expand Up @@ -390,13 +385,9 @@ fn extract_basis_target_circ(
let py = circuit.py();
let circ_data_bound = circuit.getattr("_data")?.downcast_into::<CircuitData>()?;
let circ_data = circ_data_bound.borrow();
for (index, node_obj) in circ_data.iter().enumerate() {
for node_obj in circ_data.iter() {
let qargs = circ_data.get_qargs(node_obj.qubits);
if circuit
.call_method1("_has_calibration_for", (circuit.get_item(index)?,))?
.is_truthy()?
|| qargs.len() < min_qubits
{
if qargs.len() < min_qubits {
continue;
}
// Treat the instruction as on an incomplete basis if the qargs are in the
Expand Down Expand Up @@ -578,29 +569,6 @@ fn apply_translation(
continue;
}

if dag.has_calibration_for_index(py, node)? {
out_dag.apply_operation_back(
py,
node_obj.op.clone(),
node_qarg,
node_carg,
if node_obj.params_view().is_empty() {
None
} else {
Some(
node_obj
.params_view()
.iter()
.map(|param| param.clone_ref(py))
.collect(),
)
},
node_obj.label.as_ref().map(|x| x.as_ref().clone()),
#[cfg(feature = "cache_pygates")]
None,
)?;
continue;
}
let unique_qargs: Option<Qargs> = if qubit_set.is_empty() {
None
} else {
Expand Down
7 changes: 2 additions & 5 deletions crates/accelerate/src/check_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ fn recurse<'py>(
None => edge_set.contains(&[qubits[0].into(), qubits[1].into()]),
}
};
for (node, inst) in dag.op_nodes(false) {
for (_node, inst) in dag.op_nodes(false) {
let qubits = dag.get_qargs(inst.qubits);
if inst.op.control_flow() {
if let OperationRef::Instruction(py_inst) = inst.op.view() {
Expand Down Expand Up @@ -65,10 +65,7 @@ fn recurse<'py>(
}
}
}
} else if qubits.len() == 2
&& (dag.calibrations_empty() || !dag.has_calibration_for_index(py, node)?)
&& !check_qubits(qubits)
{
} else if qubits.len() == 2 && !check_qubits(qubits) {
return Ok(Some((
inst.op.name().to_string(),
[qubits[0].0, qubits[1].0],
Expand Down
12 changes: 0 additions & 12 deletions crates/accelerate/src/euler_one_qubit_decomposer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1094,18 +1094,6 @@ pub(crate) fn optimize_1q_gates_decomposition(
} else {
unreachable!("nodes in runs will always be op nodes")
};
if !dag.calibrations_empty() {
let mut has_calibration = false;
for node in &raw_run {
if dag.has_calibration_for_index(py, *node)? {
has_calibration = true;
break;
}
}
if has_calibration {
continue;
}
}
if basis_gates_per_qubit[qubit.index()].is_none() {
let basis_gates = match target {
Some(target) => Some(
Expand Down
38 changes: 2 additions & 36 deletions crates/accelerate/src/gate_direction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,8 @@ use pyo3::types::PyTuple;
use qiskit_circuit::operations::OperationRef;
use qiskit_circuit::packed_instruction::PackedOperation;
use qiskit_circuit::{
circuit_instruction::CircuitInstruction,
converters::{circuit_to_dag, QuantumCircuitData},
dag_circuit::DAGCircuit,
dag_node::{DAGNode, DAGOpNode},
imports,
imports::get_std_gate_class,
operations::Operation,
Expand Down Expand Up @@ -291,7 +289,7 @@ where
}
}

if op_args.len() != 2 || dag.has_calibration_for_index(py, node)? {
if op_args.len() != 2 {
continue;
};

Expand Down Expand Up @@ -334,9 +332,7 @@ where
}
}
// No matching replacement found
if gate_complies(packed_inst, &[op_args1, op_args0])
|| has_calibration_for_op_node(py, dag, packed_inst, &[op_args1, op_args0])?
{
if gate_complies(packed_inst, &[op_args1, op_args0]) {
return Err(TranspilerError::new_err(format!("{} would be supported on {:?} if the direction was swapped, but no rules are known to do that. {:?} can be automatically flipped.", packed_inst.op.name(), op_args, vec!["cx", "cz", "ecr", "swap", "rzx", "rxx", "ryy", "rzz"])));
// NOTE: Make sure to update the list of the supported gates if adding more replacements
} else {
Expand Down Expand Up @@ -375,36 +371,6 @@ where
Ok(dag)
}

// Check whether the dag as calibration for a DAGOpNode
fn has_calibration_for_op_node(
py: Python,
dag: &DAGCircuit,
packed_inst: &PackedInstruction,
qargs: &[Qubit],
) -> PyResult<bool> {
let py_args = PyTuple::new(py, dag.qubits().map_indices(qargs))?;

let dag_op_node = Py::new(
py,
(
DAGOpNode {
instruction: CircuitInstruction {
operation: packed_inst.op.clone(),
qubits: py_args.unbind(),
clbits: PyTuple::empty(py).unbind(),
params: packed_inst.params_view().iter().cloned().collect(),
label: packed_inst.label.clone(),
#[cfg(feature = "cache_pygates")]
py_op: packed_inst.py_op.clone(),
},
},
DAGNode { node: None },
),
)?;

dag.has_calibration_for(py, dag_op_node.borrow(py))
}

// Return a replacement DAG for the given standard gate in the supported list
// TODO: optimize it by caching the DAGs of the non-parametric gates and caching and
// mutating upon request the DAGs of the parametric gates
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ impl InstructionProperties {
/// specified set of qubits
/// error (Option<f64>): The average error rate for the instruction on the specified
/// set of qubits.
/// calibration (Option<PyObject>): The pulse representation of the instruction.
#[new]
#[pyo3(signature = (duration=None, error=None))]
pub fn new(_py: Python<'_>, duration: Option<f64>, error: Option<f64>) -> Self {
Expand Down
11 changes: 1 addition & 10 deletions crates/circuit/src/converters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,8 @@
#[cfg(feature = "cache_pygates")]
use std::sync::OnceLock;

use hashbrown::HashMap;
use pyo3::prelude::*;
use pyo3::{
intern,
types::{PyDict, PyList},
};
use pyo3::{intern, types::PyList};

use crate::circuit_data::CircuitData;
use crate::dag_circuit::{DAGCircuit, NodeType};
Expand All @@ -30,7 +26,6 @@ use crate::packed_instruction::PackedInstruction;
pub struct QuantumCircuitData<'py> {
pub data: CircuitData,
pub name: Option<Bound<'py, PyAny>>,
pub calibrations: Option<HashMap<String, Py<PyDict>>>,
pub metadata: Option<Bound<'py, PyAny>>,
pub qregs: Option<Bound<'py, PyList>>,
pub cregs: Option<Bound<'py, PyList>>,
Expand All @@ -47,10 +42,6 @@ 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_prop"))?
.extract()
.ok(),
metadata: ob.getattr(intern!(py, "metadata")).ok(),
qregs: ob
.getattr(intern!(py, "qregs"))
Expand Down
Loading