Skip to content

Commit

Permalink
Merge branch 'circuit-stretch' into stretchy-delay
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinhartman committed Feb 20, 2025
2 parents 0beed94 + 723e4f0 commit 803fa8f
Show file tree
Hide file tree
Showing 35 changed files with 473 additions and 2,572 deletions.
2 changes: 1 addition & 1 deletion .mergify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ pull_request_rules:
actions:
backport:
branches:
- stable/1.3
- stable/1.4
109 changes: 109 additions & 0 deletions crates/accelerate/src/circuit_duration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// This code is part of Qiskit.
//
// (C) Copyright IBM 2025
//
// This code is licensed under the Apache License, Version 2.0. You may
// obtain a copy of this license in the LICENSE.txt file in the root directory
// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
//
// Any modifications or derivative works of this code must retain this
// copyright notice, and modified files need to carry a notice indicating
// that they have been altered from the originals.

use pyo3::prelude::*;
use pyo3::wrap_pyfunction;

use qiskit_circuit::dag_circuit::{DAGCircuit, NodeType, Wire};
use qiskit_circuit::operations::{DelayUnit, Operation, OperationRef, Param, StandardInstruction};

use crate::nlayout::PhysicalQubit;
use crate::target_transpiler::Target;
use crate::QiskitError;
use rustworkx_core::dag_algo::longest_path;
use rustworkx_core::petgraph::stable_graph::StableDiGraph;
use rustworkx_core::petgraph::visit::{EdgeRef, IntoEdgeReferences};

/// Estimate the duration of a scheduled circuit in seconds
#[pyfunction]
pub(crate) fn compute_estimated_duration(dag: &DAGCircuit, target: &Target) -> PyResult<f64> {
let dt = target.dt;

let get_duration =
|edge: <&StableDiGraph<NodeType, Wire> as IntoEdgeReferences>::EdgeRef| -> PyResult<f64> {
let node_weight = &dag[edge.target()];
match node_weight {
NodeType::Operation(inst) => {
let name = inst.op.name();
let qubits = dag.get_qargs(inst.qubits);
let physical_qubits: Vec<PhysicalQubit> =
qubits.iter().map(|x| PhysicalQubit::new(x.0)).collect();

if let OperationRef::StandardInstruction(op) = inst.op.view() {
if let StandardInstruction::Delay(unit) = op {
let dur = &inst.params.as_ref().unwrap()[0];
return if unit == DelayUnit::DT {
if let Some(dt) = dt {
match dur {
Param::Float(val) =>
{
Ok(val * dt)

},
Param::Obj(val) => {
Python::with_gil(|py| {
let dur_float: f64 = val.extract(py)?;
Ok(dur_float * dt)
})
},
Param::ParameterExpression(_) => Err(QiskitError::new_err(
"Circuit contains parameterized delays, can't compute a duration estimate with this circuit"
)),
}
} else {
Err(QiskitError::new_err(
"Circuit contains delays in dt but the target doesn't specify dt"
))
}
} else if unit == DelayUnit::S {
match dur {
Param::Float(val) => Ok(*val),
_ => Err(QiskitError::new_err(
"Invalid type for parameter value for delay in circuit",
)),
}
} else {
Err(QiskitError::new_err(
"Circuit contains delays in units other then seconds or dt, the circuit is not scheduled."
))
};
} else if let StandardInstruction::Barrier(_) = op {
return Ok(0.);
}
}
match target.get_duration(name, &physical_qubits) {
Some(dur) => Ok(dur),
None => Err(QiskitError::new_err(format!(
"Duration not found for {} on qubits: {:?}",
name, qubits
))),
}
}
NodeType::QubitOut(_) | NodeType::ClbitOut(_) => Ok(0.),
NodeType::ClbitIn(_) | NodeType::QubitIn(_) => {
Err(QiskitError::new_err("Invalid circuit provided"))
}
_ => Err(QiskitError::new_err(
"Circuit contains Vars, duration can't be calculated with classical variables",
)),
}
};
match longest_path(dag.dag(), get_duration)? {
Some((_, weight)) => Ok(weight),
None => Err(QiskitError::new_err("Invalid circuit provided")),
}
}

pub fn compute_duration(m: &Bound<PyModule>) -> PyResult<()> {
m.add_wrapped(wrap_pyfunction!(compute_estimated_duration))?;
Ok(())
}
1 change: 1 addition & 0 deletions crates/accelerate/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use pyo3::import_exception;
pub mod barrier_before_final_measurement;
pub mod basis;
pub mod check_map;
pub mod circuit_duration;
pub mod circuit_library;
pub mod commutation_analysis;
pub mod commutation_cancellation;
Expand Down
11 changes: 11 additions & 0 deletions crates/accelerate/src/target_transpiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -977,6 +977,17 @@ impl Target {
})
}

/// Get the duration of a given instruction in the target
pub fn get_duration(&self, name: &str, qargs: &[PhysicalQubit]) -> Option<f64> {
self.gate_map.get(name).and_then(|gate_props| {
let qargs_key: Qargs = qargs.iter().cloned().collect();
match gate_props.get(Some(&qargs_key)) {
Some(props) => props.as_ref().and_then(|inst_props| inst_props.duration),
None => None,
}
})
}

/// Get an iterator over the indices of all physical qubits of the target
pub fn physical_qubits(&self) -> impl ExactSizeIterator<Item = usize> {
0..self.num_qubits.unwrap_or_default()
Expand Down
1 change: 1 addition & 0 deletions crates/pyext/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ fn _accelerate(m: &Bound<PyModule>) -> PyResult<()> {
add_submodule(m, ::qiskit_accelerate::barrier_before_final_measurement::barrier_before_final_measurements_mod, "barrier_before_final_measurement")?;
add_submodule(m, ::qiskit_accelerate::basis::basis, "basis")?;
add_submodule(m, ::qiskit_accelerate::check_map::check_map_mod, "check_map")?;
add_submodule(m, ::qiskit_accelerate::circuit_duration::compute_duration, "circuit_duration")?;
add_submodule(m, ::qiskit_accelerate::circuit_library::circuit_library, "circuit_library")?;
add_submodule(m, ::qiskit_accelerate::commutation_analysis::commutation_analysis, "commutation_analysis")?;
add_submodule(m, ::qiskit_accelerate::commutation_cancellation::commutation_cancellation, "commutation_cancellation")?;
Expand Down
1 change: 1 addition & 0 deletions qiskit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
sys.modules["qiskit._accelerate.twirling"] = _accelerate.twirling
sys.modules["qiskit._accelerate.high_level_synthesis"] = _accelerate.high_level_synthesis
sys.modules["qiskit._accelerate.remove_identity_equiv"] = _accelerate.remove_identity_equiv
sys.modules["qiskit._accelerate.circuit_duration"] = _accelerate.circuit_duration

from qiskit.exceptions import QiskitError, MissingOptionalLibraryError

Expand Down
10 changes: 8 additions & 2 deletions qiskit/circuit/instruction.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,20 @@ class Instruction(Operation):
def __init__(self, name, num_qubits, num_clbits, params, duration=None, unit="dt", label=None):
"""Create a new instruction.
.. deprecated:: 1.3
The parameters ``duration`` and ``unit`` are deprecated since
Qiskit 1.3, and they will be removed in 2.0 or later.
An instruction's duration is defined in a backend's Target object.
Args:
name (str): instruction name
num_qubits (int): instruction's qubit width
num_clbits (int): instruction's clbit width
params (list[int|float|complex|str|ndarray|list|ParameterExpression]):
list of parameters
duration (int or float): instruction's duration. it must be integer if ``unit`` is 'dt'
unit (str): time unit of duration
duration (int|float): (DEPRECATED) instruction's duration. it must be
an integer if ``unit`` is ``'dt'``
unit (str): (DEPRECATED) time unit of duration
label (str or None): An optional label for identifying the instruction.
Raises:
Expand Down
Loading

0 comments on commit 803fa8f

Please sign in to comment.