Skip to content

Commit 6fafd8e

Browse files
authored
Add fixed size 2q matrix methods to operations and PackedInstruction (#15858)
This commit adds new methods to the circuit operation types and the PackedInstruction type to support getting fixed size 2q matrices. 2q matrices are only 16 complex numbers and are commonly used in the transpiler as part of peephole optimization and other optimizations. Using a fixed size array for these will avoid needing heap allocation and also let the compiler potentially do more compile time optimization because the compiler knows exactly how many values are being used for operations. We already exposed these methods for 1q gates for the same reason, this just expands it to 2q gates. We probably won't go beyond this for 3q gates however, because they're 8x8 so too large and also are not as commonly used as 2q gates and matrices.
1 parent 572045f commit 6fafd8e

2 files changed

Lines changed: 245 additions & 1 deletion

File tree

crates/circuit/src/operations.rs

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2838,6 +2838,146 @@ impl StandardGate {
28382838
Self::RC3X => None,
28392839
}
28402840
}
2841+
2842+
pub fn matrix_as_static_2q(&self, params: &[Param]) -> Option<[[Complex64; 4]; 4]> {
2843+
match self {
2844+
Self::GlobalPhase => None,
2845+
Self::H => None,
2846+
Self::I => None,
2847+
Self::X => None,
2848+
Self::Y => None,
2849+
Self::Z => None,
2850+
Self::Phase => None,
2851+
Self::R => None,
2852+
Self::RX => None,
2853+
Self::RY => None,
2854+
Self::RZ => None,
2855+
Self::S => None,
2856+
Self::Sdg => None,
2857+
Self::SX => None,
2858+
Self::SXdg => None,
2859+
Self::T => None,
2860+
Self::Tdg => None,
2861+
Self::U => None,
2862+
Self::U1 => None,
2863+
Self::U2 => None,
2864+
Self::U3 => None,
2865+
Self::CH => match params {
2866+
[] => Some(gate_matrix::CH_GATE),
2867+
_ => None,
2868+
},
2869+
Self::CX => match params {
2870+
[] => Some(gate_matrix::CX_GATE),
2871+
_ => None,
2872+
},
2873+
Self::CY => match params {
2874+
[] => Some(gate_matrix::CY_GATE),
2875+
_ => None,
2876+
},
2877+
Self::CZ => match params {
2878+
[] => Some(gate_matrix::CZ_GATE),
2879+
_ => None,
2880+
},
2881+
Self::DCX => match params {
2882+
[] => Some(gate_matrix::DCX_GATE),
2883+
_ => None,
2884+
},
2885+
Self::ECR => match params {
2886+
[] => Some(gate_matrix::ECR_GATE),
2887+
_ => None,
2888+
},
2889+
Self::Swap => match params {
2890+
[] => Some(gate_matrix::SWAP_GATE),
2891+
_ => None,
2892+
},
2893+
Self::ISwap => match params {
2894+
[] => Some(gate_matrix::ISWAP_GATE),
2895+
_ => None,
2896+
},
2897+
Self::CPhase => match params {
2898+
[Param::Float(lam)] => Some(gate_matrix::cp_gate(*lam)),
2899+
_ => None,
2900+
},
2901+
Self::CRX => match params {
2902+
[Param::Float(theta)] => Some(gate_matrix::crx_gate(*theta)),
2903+
_ => None,
2904+
},
2905+
Self::CRY => match params {
2906+
[Param::Float(theta)] => Some(gate_matrix::cry_gate(*theta)),
2907+
_ => None,
2908+
},
2909+
Self::CRZ => match params {
2910+
[Param::Float(theta)] => Some(gate_matrix::crz_gate(*theta)),
2911+
_ => None,
2912+
},
2913+
Self::CS => match params {
2914+
[] => Some(gate_matrix::CS_GATE),
2915+
_ => None,
2916+
},
2917+
Self::CSdg => match params {
2918+
[] => Some(gate_matrix::CSDG_GATE),
2919+
_ => None,
2920+
},
2921+
Self::CSX => match params {
2922+
[] => Some(gate_matrix::CSX_GATE),
2923+
_ => None,
2924+
},
2925+
Self::CU => match params {
2926+
[
2927+
Param::Float(theta),
2928+
Param::Float(phi),
2929+
Param::Float(lam),
2930+
Param::Float(gamma),
2931+
] => Some(gate_matrix::cu_gate(*theta, *phi, *lam, *gamma)),
2932+
_ => None,
2933+
},
2934+
Self::CU1 => match params[0] {
2935+
Param::Float(lam) => Some(gate_matrix::cu1_gate(lam)),
2936+
_ => None,
2937+
},
2938+
Self::CU3 => match params {
2939+
[Param::Float(theta), Param::Float(phi), Param::Float(lam)] => {
2940+
Some(gate_matrix::cu3_gate(*theta, *phi, *lam))
2941+
}
2942+
_ => None,
2943+
},
2944+
Self::RXX => match params[0] {
2945+
Param::Float(theta) => Some(gate_matrix::rxx_gate(theta)),
2946+
_ => None,
2947+
},
2948+
Self::RYY => match params[0] {
2949+
Param::Float(theta) => Some(gate_matrix::ryy_gate(theta)),
2950+
_ => None,
2951+
},
2952+
Self::RZZ => match params[0] {
2953+
Param::Float(theta) => Some(gate_matrix::rzz_gate(theta)),
2954+
_ => None,
2955+
},
2956+
Self::RZX => match params[0] {
2957+
Param::Float(theta) => Some(gate_matrix::rzx_gate(theta)),
2958+
_ => None,
2959+
},
2960+
Self::XXMinusYY => match params {
2961+
[Param::Float(theta), Param::Float(beta)] => {
2962+
Some(gate_matrix::xx_minus_yy_gate(*theta, *beta))
2963+
}
2964+
_ => None,
2965+
},
2966+
Self::XXPlusYY => match params {
2967+
[Param::Float(theta), Param::Float(beta)] => {
2968+
Some(gate_matrix::xx_plus_yy_gate(*theta, *beta))
2969+
}
2970+
_ => None,
2971+
},
2972+
Self::CCX => None,
2973+
Self::CCZ => None,
2974+
Self::CSwap => None,
2975+
Self::RCCX => None,
2976+
Self::C3X => None,
2977+
Self::C3SX => None,
2978+
Self::RC3X => None,
2979+
}
2980+
}
28412981
}
28422982

28432983
#[pymethods]
@@ -3170,6 +3310,27 @@ impl PyInstruction {
31703310
Some([[arr[[0, 0]], arr[[0, 1]]], [arr[[1, 0]], arr[[1, 1]]]])
31713311
})
31723312
}
3313+
3314+
pub fn matrix_as_static_2q(&self) -> Option<[[Complex64; 4]; 4]> {
3315+
if self.num_qubits() != 2 {
3316+
return None;
3317+
}
3318+
Python::attach(|py| -> Option<[[Complex64; 4]; 4]> {
3319+
let array = self
3320+
.instruction
3321+
.call_method0(py, intern!(py, "to_matrix"))
3322+
.ok()?
3323+
.extract::<PyReadonlyArray2<Complex64>>(py)
3324+
.ok()?;
3325+
let arr = array.as_array();
3326+
Some([
3327+
[arr[[0, 0]], arr[[0, 1]], arr[[0, 2]], arr[[0, 3]]],
3328+
[arr[[1, 0]], arr[[1, 1]], arr[[1, 2]], arr[[1, 3]]],
3329+
[arr[[2, 0]], arr[[2, 1]], arr[[2, 2]], arr[[2, 3]]],
3330+
[arr[[3, 0]], arr[[3, 1]], arr[[3, 2]], arr[[3, 3]]],
3331+
])
3332+
})
3333+
}
31733334
}
31743335

31753336
#[derive(Clone, Debug)]
@@ -3252,6 +3413,30 @@ impl UnitaryGate {
32523413
}
32533414
}
32543415

3416+
pub fn matrix_as_static_2q(&self) -> Option<[[Complex64; 4]; 4]> {
3417+
match &self.array {
3418+
ArrayType::OneQ(_mat) => None,
3419+
ArrayType::NDArray(arr) => {
3420+
if self.num_qubits() == 2 {
3421+
Some([
3422+
[arr[[0, 0]], arr[[0, 1]], arr[[0, 2]], arr[[0, 3]]],
3423+
[arr[[1, 0]], arr[[1, 1]], arr[[1, 2]], arr[[1, 3]]],
3424+
[arr[[2, 0]], arr[[2, 1]], arr[[2, 2]], arr[[2, 3]]],
3425+
[arr[[3, 0]], arr[[3, 1]], arr[[3, 2]], arr[[3, 3]]],
3426+
])
3427+
} else {
3428+
None
3429+
}
3430+
}
3431+
ArrayType::TwoQ(mat) => Some([
3432+
[mat[(0, 0)], mat[(0, 1)], mat[(0, 2)], mat[(0, 3)]],
3433+
[mat[(1, 0)], mat[(1, 1)], mat[(1, 2)], mat[(1, 3)]],
3434+
[mat[(2, 0)], mat[(2, 1)], mat[(2, 2)], mat[(2, 3)]],
3435+
[mat[(3, 0)], mat[(3, 1)], mat[(3, 2)], mat[(3, 3)]],
3436+
]),
3437+
}
3438+
}
3439+
32553440
pub fn matrix_as_nalgebra_1q(&self) -> Option<Matrix2<Complex64>> {
32563441
match &self.array {
32573442
ArrayType::OneQ(mat) => Some(*mat),
@@ -3270,6 +3455,36 @@ impl UnitaryGate {
32703455
ArrayType::TwoQ(_) => None,
32713456
}
32723457
}
3458+
pub fn matrix_as_nalgebra_2q(&self) -> Option<Matrix4<Complex64>> {
3459+
match &self.array {
3460+
ArrayType::OneQ(_mat) => None,
3461+
ArrayType::NDArray(arr) => {
3462+
if self.num_qubits() == 2 {
3463+
Some(Matrix4::new(
3464+
arr[[0, 0]],
3465+
arr[[0, 1]],
3466+
arr[[0, 2]],
3467+
arr[[0, 3]],
3468+
arr[[1, 0]],
3469+
arr[[1, 1]],
3470+
arr[[1, 2]],
3471+
arr[[1, 3]],
3472+
arr[[2, 0]],
3473+
arr[[2, 1]],
3474+
arr[[2, 2]],
3475+
arr[[2, 3]],
3476+
arr[[3, 0]],
3477+
arr[[3, 1]],
3478+
arr[[3, 2]],
3479+
arr[[3, 3]],
3480+
))
3481+
} else {
3482+
None
3483+
}
3484+
}
3485+
ArrayType::TwoQ(mat) => Some(*mat),
3486+
}
3487+
}
32733488
}
32743489

32753490
impl UnitaryGate {

crates/circuit/src/packed_instruction.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use crate::operations::{
2424
};
2525
use crate::{Block, Clbit, Qubit};
2626
use hashbrown::HashMap;
27-
use nalgebra::Matrix2;
27+
use nalgebra::{Matrix2, Matrix4};
2828
use ndarray::{Array2, CowArray, Ix2};
2929
use num_complex::Complex64;
3030
use pyo3::prelude::*;
@@ -984,6 +984,7 @@ impl PackedInstruction {
984984
}
985985
}
986986

987+
#[inline]
987988
pub fn try_matrix_as_nalgebra_1q(&self) -> Option<Matrix2<Complex64>> {
988989
match self.op.view() {
989990
OperationRef::Unitary(u) => u.matrix_as_nalgebra_1q(),
@@ -994,6 +995,34 @@ impl PackedInstruction {
994995
}
995996
}
996997

998+
/// Returns a static matrix for 1-qubit gates. Will return `None` when the gate is not 1-qubit.
999+
#[inline]
1000+
pub fn try_matrix_as_static_2q(&self) -> Option<[[Complex64; 4]; 4]> {
1001+
match self.op.view() {
1002+
OperationRef::StandardGate(standard) => {
1003+
standard.matrix_as_static_2q(self.params_view())
1004+
}
1005+
OperationRef::Gate(gate) => gate.matrix_as_static_2q(),
1006+
OperationRef::Unitary(unitary) => unitary.matrix_as_static_2q(),
1007+
_ => None,
1008+
}
1009+
}
1010+
1011+
#[inline]
1012+
pub fn try_matrix_as_nalgebra_2q(&self) -> Option<Matrix4<Complex64>> {
1013+
match self.op.view() {
1014+
OperationRef::Unitary(u) => u.matrix_as_nalgebra_2q(),
1015+
// default implementation
1016+
_ => self.try_matrix_as_static_2q().map(|arr| {
1017+
Matrix4::new(
1018+
arr[0][0], arr[0][1], arr[0][2], arr[0][3], arr[1][0], arr[1][1], arr[1][2],
1019+
arr[1][3], arr[2][0], arr[2][1], arr[2][2], arr[2][3], arr[3][0], arr[3][1],
1020+
arr[3][2], arr[3][3],
1021+
)
1022+
}),
1023+
}
1024+
}
1025+
9971026
pub fn try_definition(&self) -> Option<CircuitData> {
9981027
match self.op.view() {
9991028
OperationRef::StandardGate(g) => g.definition(self.params_view()),

0 commit comments

Comments
 (0)