Skip to content

Commit 8ee2ba4

Browse files
authored
Merge branch 'main' into cons-block-docs
2 parents 1eac4c6 + 9c6baac commit 8ee2ba4

38 files changed

Lines changed: 174 additions & 40 deletions

File tree

crates/quantum_info/src/convert_2q_block_matrix.rs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use pyo3::prelude::*;
1616

1717
use num_complex::Complex64;
1818
use numpy::PyReadonlyArray2;
19-
use numpy::nalgebra::{Matrix4, MatrixViewMut4};
19+
use numpy::nalgebra::{Matrix2, Matrix4, MatrixViewMut4};
2020
use numpy::ndarray::{Array2, ArrayView2};
2121
use rustworkx_core::petgraph::stable_graph::NodeIndex;
2222

@@ -52,6 +52,16 @@ fn matrix4_from_pyreadonly(array: &PyReadonlyArray2<Complex64>) -> Matrix4<Compl
5252
)
5353
}
5454

55+
#[inline]
56+
fn matrix2_from_pyreadonly(array: &PyReadonlyArray2<Complex64>) -> Matrix2<Complex64> {
57+
Matrix2::new(
58+
*array.get((0, 0)).unwrap(),
59+
*array.get((0, 1)).unwrap(),
60+
*array.get((1, 0)).unwrap(),
61+
*array.get((1, 1)).unwrap(),
62+
)
63+
}
64+
5565
#[inline]
5666
pub fn get_matrix_from_inst(inst: &PackedInstruction) -> PyResult<Array2<Complex64>> {
5767
if let Some(mat) = inst.try_matrix() {
@@ -109,6 +119,34 @@ pub fn get_2q_matrix_from_inst(inst: &PackedInstruction) -> PyResult<Matrix4<Com
109119
})
110120
}
111121

122+
#[inline]
123+
pub fn get_1q_matrix_from_inst(inst: &PackedInstruction) -> PyResult<Matrix2<Complex64>> {
124+
if let Some(mat) = inst.try_matrix_as_nalgebra_1q() {
125+
return Ok(mat);
126+
}
127+
if inst.op.try_standard_gate().is_some() {
128+
return Err(QiskitError::new_err(
129+
"Parameterized gates can't be consolidated",
130+
));
131+
}
132+
let OperationRef::Gate(gate) = inst.op.view() else {
133+
return Err(QiskitError::new_err(
134+
"Can't compute matrix of non-unitary op",
135+
));
136+
};
137+
// If the operation is a custom python gate, we will acquire the gil and use an
138+
// Operator. Otherwise, using op.matrix() should work. A user should not be
139+
// able to reach this condition in Rust standalone mode.
140+
Python::attach(|py| {
141+
let res = QI_OPERATOR
142+
.get_bound(py)
143+
.call1((gate.instruction.clone_ref(py),))?
144+
.getattr(intern!(py, "data"))?
145+
.extract()?;
146+
Ok(matrix2_from_pyreadonly(&res))
147+
})
148+
}
149+
112150
/// Quaternion-based collect of two parallel runs of 1q gates.
113151
#[derive(Clone, Debug)]
114152
struct Separable1q {

crates/transpiler/src/passes/consolidate_blocks.rs

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ use qiskit_circuit::interner::Interned;
3131
use qiskit_circuit::operations::StandardGate;
3232
use qiskit_circuit::operations::{ArrayType, Operation, Param, UnitaryGate};
3333
use qiskit_circuit::packed_instruction::PackedOperation;
34-
use qiskit_quantum_info::convert_2q_block_matrix::{blocks_to_matrix, get_matrix_from_inst};
34+
use qiskit_quantum_info::convert_2q_block_matrix::{
35+
blocks_to_matrix, get_1q_matrix_from_inst, get_2q_matrix_from_inst, get_matrix_from_inst,
36+
};
3537
use qiskit_synthesis::linalg::nalgebra_array_view;
3638
use qiskit_synthesis::two_qubit_decompose::RXXEquivalent;
3739
use qiskit_synthesis::two_qubit_decompose::{
@@ -289,14 +291,31 @@ fn py_run_consolidate_blocks(
289291
phys_qargs.get(dag, inst.qubits),
290292
) {
291293
all_block_gates.insert(inst_node);
292-
let matrix = match get_matrix_from_inst(inst) {
293-
Ok(mat) => mat,
294-
Err(_) => continue,
295-
};
296-
// TODO: Use Matrix2/ArrayType::OneQ when we're using nalgebra
297-
// for consolidation
298-
let unitary_gate = UnitaryGate {
299-
array: ArrayType::NDArray(matrix),
294+
let num_qubits = inst.op.num_qubits();
295+
let unitary_gate = if num_qubits == 1 {
296+
let matrix = match get_1q_matrix_from_inst(inst) {
297+
Ok(mat) => mat,
298+
Err(_) => continue,
299+
};
300+
UnitaryGate {
301+
array: ArrayType::OneQ(matrix),
302+
}
303+
} else if num_qubits == 2 {
304+
let matrix = match get_2q_matrix_from_inst(inst) {
305+
Ok(mat) => mat,
306+
Err(_) => continue,
307+
};
308+
UnitaryGate {
309+
array: ArrayType::TwoQ(matrix),
310+
}
311+
} else {
312+
let matrix = match get_matrix_from_inst(inst) {
313+
Ok(mat) => mat,
314+
Err(_) => continue,
315+
};
316+
UnitaryGate {
317+
array: ArrayType::NDArray(matrix),
318+
}
300319
};
301320
dag.substitute_op(
302321
inst_node,
@@ -450,12 +469,15 @@ fn py_run_consolidate_blocks(
450469
first_qubits,
451470
)
452471
{
453-
let matrix = match get_matrix_from_inst(first_inst) {
472+
// Runs are necessarily single qubit gates so if it has a matrix then it
473+
// must have a single qubit matrix or it's not a run and the blocks handling above
474+
// would have covered it
475+
let matrix = match get_1q_matrix_from_inst(first_inst) {
454476
Ok(mat) => mat,
455477
Err(_) => continue,
456478
};
457479
let unitary_gate = UnitaryGate {
458-
array: ArrayType::NDArray(matrix),
480+
array: ArrayType::OneQ(matrix),
459481
};
460482
dag.substitute_op(
461483
first_inst_node,

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,8 @@ select = [
347347
"INP",
348348
# flake8-print
349349
"T20",
350+
# Missing arguments in docstring
351+
"D417",
350352
]
351353
ignore = [
352354
"E402", # module-import-not-at-top-of-file false positives with module docs

qiskit/circuit/controlflow/box.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,9 @@ def __init__(
154154
duration: the final duration of the box.
155155
unit: the unit of ``duration``.
156156
label: an optional label for the box.
157+
annotations: any :class:`.Annotation`\\ s to apply to the box. In cases where order
158+
is important, annotations are to be interpreted in the same order they appear in
159+
the iterable.
157160
"""
158161
self._circuit = circuit
159162
self._duration = duration

qiskit/circuit/controlflow/switch_case.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ def __init__(
7070
cases: an ordered iterable of the corresponding value of the ``target`` and the circuit
7171
block that should be executed if this is matched. There is no fall-through between
7272
blocks, and the order matters.
73+
label: An optional label for identifying the instruction.
7374
"""
7475

7576
from qiskit.circuit import QuantumCircuit

qiskit/circuit/duration.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def convert_durations_to_dt(qc: QuantumCircuit, dt_in_sec: float, inplace=True):
4747
4848
Returns a new circuit if `inplace=False`.
4949
50-
Parameters:
50+
Args:
5151
qc (QuantumCircuit): Duration of dt in seconds used for conversion.
5252
dt_in_sec (float): Duration of dt in seconds used for conversion.
5353
inplace (bool): All durations are converted inplace or return new circuit.

qiskit/circuit/library/arithmetic/adders/adder.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ def __init__(self, num_state_qubits: int, label: str | None = None) -> None:
100100
Args:
101101
num_state_qubits: The number of qubits in each of the registers.
102102
name: The name of the circuit.
103+
label: An optional label for identifying the instruction.
103104
"""
104105
if num_state_qubits < 1:
105106
raise ValueError("Need at least 1 state qubit.")
@@ -155,6 +156,7 @@ def __init__(self, num_state_qubits: int, label: str | None = None) -> None:
155156
Args:
156157
num_state_qubits: The number of qubits in each of the registers.
157158
name: The name of the circuit.
159+
label: An optional label for identifying the instruction.
158160
"""
159161
if num_state_qubits < 1:
160162
raise ValueError("Need at least 1 state qubit.")
@@ -211,6 +213,7 @@ def __init__(self, num_state_qubits: int, label: str | None = None) -> None:
211213
Args:
212214
num_state_qubits: The number of qubits in each of the registers.
213215
name: The name of the circuit.
216+
label: An optional label for identifying the instruction.
214217
"""
215218
if num_state_qubits < 1:
216219
raise ValueError("Need at least 1 state qubit.")

qiskit/circuit/library/arithmetic/linear_pauli_rotations.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ def slope(self, slope: float) -> None:
9393
"""Set the multiplicative factor of the rotation angles.
9494
9595
Args:
96-
The slope of the rotation angles.
96+
slope: The slope of the rotation angles.
9797
"""
9898
if self._slope is None or slope != self._slope:
9999
self._invalidate()

qiskit/circuit/library/arithmetic/multipliers/multiplier.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ def __init__(
151151
num_result_qubits: The number of result qubits to limit the output to.
152152
Default value is ``2 * num_state_qubits`` to represent any possible
153153
result from the multiplication of the two inputs.
154-
name: The name of the circuit.
154+
label: The optional string label to apply to the instruction.
155155
Raises:
156156
ValueError: If ``num_state_qubits`` is smaller than 1.
157157
ValueError: If ``num_result_qubits`` is smaller than ``num_state_qubits``.

qiskit/circuit/library/arithmetic/polynomial_pauli_rotations.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ def coeffs(self, coeffs: list[float]) -> None:
206206
``coeffs[i]`` is the coefficient of the i-th power of x.
207207
208208
Args:
209-
The coefficients of the polynomial.
209+
coeffs: The coefficients of the polynomial.
210210
"""
211211
self._invalidate()
212212
self._coeffs = coeffs

0 commit comments

Comments
 (0)