From 7558c0e4d26f3ee5b4527492a0e6640c497bf5da Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Fri, 4 Oct 2024 16:16:54 -0400 Subject: [PATCH] Set num_qubits kwarg when building UnitaryGate from Rust In the accelerate crate we currently have two places that are building `UnitaryGate`, the quantum_volume() function that builds a quantum volume model circuit and the Split2QUnitaries transpiler pass. Currently this can only be done by calling Python (until #13272 is implemented) and there was a potential optimization we could make to specify the number of qubits as an argument to the Python constructor. This skipss the need for a few python operations to compute the number of qubits from the size of the matrix. These operations are not exceedingly slow, but as these Python space constructors are often the bottleneck so it should help runtime performance of these two functions. --- crates/accelerate/src/circuit_library/quantum_volume.rs | 7 ++++++- crates/accelerate/src/split_2q_unitaries.rs | 9 +++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/crates/accelerate/src/circuit_library/quantum_volume.rs b/crates/accelerate/src/circuit_library/quantum_volume.rs index 88b558cfe5c..3b664d0b5fc 100644 --- a/crates/accelerate/src/circuit_library/quantum_volume.rs +++ b/crates/accelerate/src/circuit_library/quantum_volume.rs @@ -10,7 +10,9 @@ // copyright notice, and modified files need to carry a notice indicating // that they have been altered from the originals. +use pyo3::intern; use pyo3::prelude::*; +use pyo3::types::PyDict; use crate::getenv_use_multiple_threads; use faer_ext::{IntoFaerComplex, IntoNdarrayComplex}; @@ -110,6 +112,8 @@ pub fn quantum_volume( let num_unitaries = width * depth; let mut permutation: Vec = (0..num_qubits).map(Qubit).collect(); + let kwargs = PyDict::new_bound(py); + kwargs.set_item(intern!(py, "num_qubits"), 2)?; let mut build_instruction = |(unitary_index, unitary_array): (usize, Array2), rng: &mut Pcg64Mcg| -> PyResult { @@ -118,9 +122,10 @@ pub fn quantum_volume( permutation.shuffle(rng); } let unitary = unitary_array.into_pyarray_bound(py); + let unitary_gate = UNITARY_GATE .get_bound(py) - .call1((unitary.clone(), py.None(), false))?; + .call((unitary.clone(), py.None(), false), Some(&kwargs))?; let instruction = PyInstruction { qubits: 2, clbits: 0, diff --git a/crates/accelerate/src/split_2q_unitaries.rs b/crates/accelerate/src/split_2q_unitaries.rs index 411571114ba..f3a3d945498 100644 --- a/crates/accelerate/src/split_2q_unitaries.rs +++ b/crates/accelerate/src/split_2q_unitaries.rs @@ -10,7 +10,9 @@ // copyright notice, and modified files need to carry a notice indicating // that they have been altered from the originals. +use pyo3::intern; use pyo3::prelude::*; +use pyo3::types::PyDict; use rustworkx_core::petgraph::stable_graph::NodeIndex; use qiskit_circuit::circuit_instruction::OperationFromPython; @@ -27,6 +29,7 @@ pub fn split_2q_unitaries( requested_fidelity: f64, ) -> PyResult<()> { let nodes: Vec = dag.op_nodes(false).collect(); + for node in nodes { if let NodeType::Operation(inst) = &dag.dag()[node] { let qubits = dag.get_qargs(inst.qubits).to_vec(); @@ -45,12 +48,14 @@ pub fn split_2q_unitaries( if matches!(decomp.specialization, Specialization::IdEquiv) { let k1r_arr = decomp.K1r(py); let k1l_arr = decomp.K1l(py); + let kwargs = PyDict::new_bound(py); + kwargs.set_item(intern!(py, "num_qubits"), 1)?; let k1r_gate = UNITARY_GATE .get_bound(py) - .call1((k1r_arr, py.None(), false))?; + .call((k1r_arr, py.None(), false), Some(&kwargs))?; let k1l_gate = UNITARY_GATE .get_bound(py) - .call1((k1l_arr, py.None(), false))?; + .call((k1l_arr, py.None(), false), Some(&kwargs))?; let insert_fn = |edge: &Wire| -> PyResult { if let Wire::Qubit(qubit) = edge { if *qubit == qubits[0] {