Skip to content

Commit 82f4b02

Browse files
committed
Use rust-native error for ConsolidateBlocks
The following commits modify `ConsolidateBlocks` to use rust-native errors throughout its pipeline. We implement the `ConsolidateBlocksError` enumeration which derives from `DAGCircuitError`, and `CircuitDataError`, as well as adding two new variants for python specific errors dealing with matrices and controlled u gates.
1 parent 9471050 commit 82f4b02

1 file changed

Lines changed: 84 additions & 25 deletions

File tree

crates/transpiler/src/passes/consolidate_blocks.rs

Lines changed: 84 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ use pyo3::exceptions::PyIndexError;
2121
use pyo3::intern;
2222
use pyo3::prelude::*;
2323
use qiskit_circuit::Qubit;
24-
use qiskit_circuit::circuit_data::CircuitData;
25-
use qiskit_circuit::dag_circuit::{DAGCircuit, NodeType};
24+
use qiskit_circuit::circuit_data::{CircuitData, CircuitDataError};
25+
use qiskit_circuit::dag_circuit::{DAGCircuit, DAGCircuitInnerError, NodeType};
2626
use qiskit_circuit::gate_matrix::{
2727
CH_GATE, CX_GATE, CY_GATE, CZ_GATE, DCX_GATE, ECR_GATE, ISWAP_GATE, ONE_QUBIT_IDENTITY,
2828
};
@@ -46,6 +46,39 @@ use crate::passes::unitary_synthesis::{PARAM_SET, TWO_QUBIT_BASIS_SET};
4646
use crate::target::{Qargs, Target};
4747
use qiskit_circuit::PhysicalQubit;
4848

49+
/// Possible errors coming from the `ConsolidateBlocks` pass.
50+
#[derive(Debug, thiserror::Error)]
51+
pub enum ConsolidateBlocksError {
52+
#[error("node index in run or block was not a valid operation")]
53+
InvalidIndexOp,
54+
#[error(transparent)]
55+
DAGCircuit(#[from] DAGCircuitInnerError),
56+
#[error(transparent)]
57+
Circuit(#[from] CircuitDataError),
58+
#[error(transparent)]
59+
// TODO: Replace with decomposer rust native error
60+
PyTwoQubitBasisDecomposer(PyErr),
61+
#[error(transparent)]
62+
PyMatrixError(PyErr),
63+
#[error(transparent)]
64+
PyTwoQubitControlledU(PyErr),
65+
}
66+
67+
impl From<ConsolidateBlocksError> for PyErr {
68+
fn from(value: ConsolidateBlocksError) -> Self {
69+
match value {
70+
ConsolidateBlocksError::DAGCircuit(dagcircuit_inner_error) => {
71+
dagcircuit_inner_error.into()
72+
}
73+
ConsolidateBlocksError::Circuit(circuit_data_error) => circuit_data_error.into(),
74+
ConsolidateBlocksError::InvalidIndexOp => PyIndexError::new_err(value.to_string()),
75+
ConsolidateBlocksError::PyMatrixError(py_err)
76+
| ConsolidateBlocksError::PyTwoQubitBasisDecomposer(py_err)
77+
| ConsolidateBlocksError::PyTwoQubitControlledU(py_err) => py_err,
78+
}
79+
}
80+
}
81+
4982
static IDENTITY_2Q: Matrix4<Complex64> = Matrix4::new(
5083
// Row 1
5184
Complex64::ONE,
@@ -232,6 +265,32 @@ fn py_run_consolidate_blocks(
232265
runs: Option<Vec<Vec<usize>>>,
233266
qubit_map: Option<Vec<PhysicalQubit>>,
234267
) -> PyResult<()> {
268+
inner_run_consolidate_blocks(
269+
dag,
270+
decomposer,
271+
basis_gate_name,
272+
force_consolidate,
273+
target,
274+
basis_gates,
275+
blocks,
276+
runs,
277+
qubit_map,
278+
)
279+
.map_err(Into::into)
280+
}
281+
282+
#[allow(clippy::too_many_arguments)]
283+
fn inner_run_consolidate_blocks(
284+
dag: &mut DAGCircuit,
285+
decomposer: Option<DecomposerType>,
286+
basis_gate_name: &str,
287+
force_consolidate: bool,
288+
target: Option<&Target>,
289+
basis_gates: Option<HashSet<String>>,
290+
blocks: Option<Vec<Vec<usize>>>,
291+
runs: Option<Vec<Vec<usize>>>,
292+
qubit_map: Option<Vec<PhysicalQubit>>,
293+
) -> Result<(), ConsolidateBlocksError> {
235294
// If we don't have a decomposer and force consolidate is not set then there is not any
236295
// consolidation to do.
237296
if decomposer.is_none() && !force_consolidate {
@@ -241,15 +300,14 @@ fn py_run_consolidate_blocks(
241300
// trust that they come from a correct analysis (or the block/run collection might have been
242301
// invalidated). Rather than panicking, we should raise Python-space exceptions. We don't have
243302
// to check indices that are generated by trusted Rust-only methods within this function.
244-
let valid_op_node = |dag: &mut DAGCircuit, index: usize| -> PyResult<NodeIndex> {
245-
let index = NodeIndex::new(index);
246-
match dag.dag().node_weight(index) {
247-
Some(NodeType::Operation(_)) => Ok(index),
248-
_ => Err(PyIndexError::new_err(
249-
"node index in run or block was not a valid operation",
250-
)),
251-
}
252-
};
303+
let valid_op_node =
304+
|dag: &mut DAGCircuit, index: usize| -> Result<NodeIndex, ConsolidateBlocksError> {
305+
let index = NodeIndex::new(index);
306+
match dag.dag().node_weight(index) {
307+
Some(NodeType::Operation(_)) => Ok(index),
308+
_ => Err(ConsolidateBlocksError::InvalidIndexOp),
309+
}
310+
};
253311
let blocks = match blocks {
254312
Some(runs) => runs
255313
.into_iter()
@@ -385,7 +443,8 @@ fn py_run_consolidate_blocks(
385443
.as_array()
386444
.to_owned();
387445
Ok(matrix)
388-
})?;
446+
})
447+
.map_err(ConsolidateBlocksError::PyMatrixError)?;
389448
let identity: Array2<Complex64> = Array2::eye(2usize.pow(block_qargs.len() as u32));
390449
if approx::abs_diff_eq!(identity, matrix.view()) {
391450
for node in block {
@@ -422,19 +481,19 @@ fn py_run_consolidate_blocks(
422481
} else {
423482
let num_basis_gates = if let Some(ref decomposer) = decomposer {
424483
match decomposer {
425-
DecomposerType::TwoQubitBasis(decomp) => {
426-
decomp.num_basis_gates_inner(nalgebra_array_view::<
427-
Complex64,
428-
U4,
429-
U4,
430-
>(
431-
matrix.as_view()
432-
))?
433-
}
484+
DecomposerType::TwoQubitBasis(decomp) => decomp
485+
.num_basis_gates_inner(nalgebra_array_view::<Complex64, U4, U4>(
486+
matrix.as_view(),
487+
))
488+
.map_err(ConsolidateBlocksError::PyTwoQubitBasisDecomposer)?,
434489
DecomposerType::TwoQubitControlledU(decomp) => decomp
435490
.num_basis_gates_inner(nalgebra_array_view::<Complex64, U4, U4>(
436491
matrix.as_view(),
437-
))?,
492+
))
493+
// The only way this throws an error is if it deals with a
494+
// bad Python operation. So we can just wrap it into its own
495+
// special case.
496+
.map_err(ConsolidateBlocksError::PyTwoQubitControlledU)?,
438497
}
439498
} else {
440499
unreachable!("A decomposer is always set unless force_consolidate is true");
@@ -576,10 +635,10 @@ pub fn run_consolidate_blocks(
576635
force_consolidate: bool,
577636
approximation_degree: Option<f64>,
578637
target: Option<&Target>,
579-
) -> PyResult<()> {
638+
) -> Result<(), ConsolidateBlocksError> {
580639
let approximation_degree = approximation_degree.unwrap_or(1.0);
581640
if force_consolidate {
582-
py_run_consolidate_blocks(
641+
inner_run_consolidate_blocks(
583642
dag,
584643
None,
585644
"cx",
@@ -593,7 +652,7 @@ pub fn run_consolidate_blocks(
593652
)
594653
} else {
595654
let (decomposer, basis_gate) = get_decomposer_and_basis_gate(target, approximation_degree);
596-
py_run_consolidate_blocks(
655+
inner_run_consolidate_blocks(
597656
dag,
598657
Some(decomposer),
599658
basis_gate.name(),

0 commit comments

Comments
 (0)