Skip to content

Commit ceba759

Browse files
authored
Switch TwoQubitWeylDecomposition to use nalgebra internally (#15960)
* Switch TwoQubitWeylDecomposition to use nalgebra internally This is the next step towards using nalgebra as the matrix container type in the two qubit decomposers. This commit updates the weyl_decomposition module to leverage nalgebra to contain all the 1 and 2 qubit unitary matrices used during the decomposition. This has the same advantages outlined in the previous patches in this series, mainly that the nalgebra types used are fixed size and stack allocated which provides advantages for these small matrices. This commit also undoes some of the temporary work in the previous commit that was adding a dual nalgebra path for some utility functions in the two qubit basis decomposer. Now that both the weyl decomposition and the two qubit basis decomposer are based on storage types in nalgebra a lot of these aren't needed anymore and can be removed. * Use nalgebra determinant instead of faer's * Cleanup internal helper functions This commit cleans up the internal helper functions added for working with nalgebra and converting between the other libraries used. These functions were not intended to be used outside of the specific cases as in this PR as they make assumptions about matrix shape and/or layout. This adjusts the visibility of the functions and adds docstrings around the assumptions they make in case someone tries to use them outside of in this PR. * Reuse identity in decompose_two_qubit_product_gate()
1 parent e877510 commit ceba759

5 files changed

Lines changed: 341 additions & 295 deletions

File tree

crates/synthesis/src/linalg/mod.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,10 @@ pub fn faer_to_ndarray<T>(mat: MatRef<'_, T>) -> ArrayView2<'_, T> {
9696
unsafe { ArrayView2::from_shape_ptr((nrows, ncols).strides((row_stride, col_stride)), ptr) }
9797
}
9898

99-
fn nalgebra_to_faer<R: Dim, C: Dim, RStride: Dim, CStride: Dim>(
100-
mat: MatrixView<'_, Complex64, R, C, RStride, CStride>,
101-
) -> MatRef<'_, Complex64> {
99+
#[inline]
100+
pub(crate) fn nalgebra_to_faer<R: Dim, C: Dim, RStride: Dim, CStride: Dim, T: nalgebra::Scalar>(
101+
mat: MatrixView<'_, T, R, C, RStride, CStride>,
102+
) -> MatRef<'_, T> {
102103
let dim = ::ndarray::Dim(mat.shape());
103104
let strides = ::ndarray::Dim(mat.strides());
104105

@@ -121,7 +122,10 @@ fn nalgebra_to_faer<R: Dim, C: Dim, RStride: Dim, CStride: Dim>(
121122
/// striding and will access the memory as if the array was a contiguous R x C
122123
/// matrix. If using striding here it's best to not ever call `into_slice()`. There
123124
/// might also be similar sharp edges with strided matrices.
124-
fn faer_to_nalgebra(mat: MatRef<'_, Complex64>) -> MatrixView<'_, Complex64, Dyn, Dyn, Dyn, Dyn> {
125+
#[inline]
126+
pub(crate) fn faer_to_nalgebra(
127+
mat: MatRef<'_, Complex64>,
128+
) -> MatrixView<'_, Complex64, Dyn, Dyn, Dyn, Dyn> {
125129
// This function's code is based on faer-ext's IntoNalgebra::into_nalgebra implementation at:
126130
// https://codeberg.org/sarah-quinones/faer-ext/src/commit/0f055b39529c94d1a000982df745cb9ce170f994/src/lib.rs#L77-L96
127131

crates/synthesis/src/two_qubit_decompose/basis_decomposer.rs

Lines changed: 50 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use numpy::{IntoPyArray, ToPyArray};
2525
use pyo3::exceptions::PyValueError;
2626
use pyo3::prelude::*;
2727

28-
use super::common::{DEFAULT_FIDELITY, TraceToFidelity, rx_matrix_nalgebra, rz_matrix_nalgebra};
28+
use super::common::{DEFAULT_FIDELITY, IPZ, TraceToFidelity, rx_matrix, rz_matrix};
2929
use super::gate_sequence::{TwoQubitGateSequence, TwoQubitSequenceVec};
3030
use super::weyl_decomposition::{__num_basis_gates, _num_basis_gates, TwoQubitWeylDecomposition};
3131

@@ -46,7 +46,7 @@ use qiskit_circuit::instruction::{Instruction, Parameters};
4646
use qiskit_circuit::operations::{Operation, OperationRef, Param, StandardGate};
4747
use qiskit_circuit::packed_instruction::PackedOperation;
4848
use qiskit_circuit::{NoBlocks, Qubit};
49-
use qiskit_util::complex::{C_M_ONE, C_ONE, C_ZERO, IM, M_IM, c64};
49+
use qiskit_util::complex::{C_M_ONE, C_ONE, IM, M_IM, c64};
5050

5151
// Worst case length is 5x 1q gates for each 1q decomposition + 1x 2q gate
5252
// We might overallocate a bit if the euler basis is different but
@@ -56,8 +56,6 @@ use qiskit_util::complex::{C_M_ONE, C_ONE, C_ZERO, IM, M_IM, c64};
5656
// Python space.
5757
const TWO_QUBIT_SEQUENCE_DEFAULT_CAPACITY: usize = 21;
5858

59-
static IPZ: Matrix2<Complex64> = Matrix2::new(IM, C_ZERO, C_ZERO, M_IM);
60-
6159
static HGATE: Matrix2<Complex64> =
6260
Matrix2::new(H_GATE[0][0], H_GATE[0][1], H_GATE[1][0], H_GATE[1][1]);
6361

@@ -93,11 +91,6 @@ static K22L: Matrix2<Complex64> = Matrix2::new(
9391
);
9492
static K22R: Matrix2<Complex64> = Matrix2::new(Complex64::ZERO, C_ONE, C_M_ONE, Complex64::ZERO);
9593

96-
#[inline]
97-
pub fn ndarray_to_matrix2<T: Copy>(view: ArrayView2<T>) -> Matrix2<T> {
98-
Matrix2::new(view[[0, 0]], view[(0, 1)], view[(1, 0)], view[(1, 1)])
99-
}
100-
10194
#[derive(Clone, Debug)]
10295
#[allow(non_snake_case)]
10396
#[pyclass(
@@ -152,14 +145,10 @@ impl TwoQubitBasisDecomposer {
152145
) -> SmallVec<[Matrix2<Complex64>; 8]> {
153146
// FIXME: fix for z!=0 and c!=0 using closest reflection (not always in the Weyl chamber)
154147
smallvec![
155-
ndarray_to_matrix2(self.basis_decomposer.K2r.view()).adjoint()
156-
* ndarray_to_matrix2(target.K2r.view()),
157-
ndarray_to_matrix2(self.basis_decomposer.K2l.view()).adjoint()
158-
* ndarray_to_matrix2(target.K2l.view()),
159-
ndarray_to_matrix2(target.K1r.view())
160-
* ndarray_to_matrix2(self.basis_decomposer.K1r.view()).adjoint(),
161-
ndarray_to_matrix2(target.K1l.view())
162-
* ndarray_to_matrix2(self.basis_decomposer.K1l.view()).adjoint(),
148+
self.basis_decomposer.K2r.adjoint() * target.K2r,
149+
self.basis_decomposer.K2l.adjoint() * target.K2l,
150+
target.K1r * self.basis_decomposer.K1r.adjoint(),
151+
target.K1l * self.basis_decomposer.K1l.adjoint(),
163152
]
164153
}
165154

@@ -168,12 +157,12 @@ impl TwoQubitBasisDecomposer {
168157
target: &TwoQubitWeylDecomposition,
169158
) -> SmallVec<[Matrix2<Complex64>; 8]> {
170159
smallvec![
171-
self.q2r * ndarray_to_matrix2(target.K2r.view()),
172-
self.q2l * ndarray_to_matrix2(target.K2l.view()),
173-
self.q1ra * rz_matrix_nalgebra(2. * target.b) * self.q1rb,
174-
self.q1la * rz_matrix_nalgebra(-2. * target.a) * self.q1lb,
175-
ndarray_to_matrix2(target.K1r.view()) * self.q0r,
176-
ndarray_to_matrix2(target.K1l.view()) * self.q0l,
160+
self.q2r * target.K2r,
161+
self.q2l * target.K2l,
162+
self.q1ra * rz_matrix(2. * target.b) * self.q1rb,
163+
self.q1la * rz_matrix(-2. * target.a) * self.q1lb,
164+
target.K1r * self.q0r,
165+
target.K1l * self.q0l,
177166
]
178167
}
179168

@@ -182,14 +171,14 @@ impl TwoQubitBasisDecomposer {
182171
target: &TwoQubitWeylDecomposition,
183172
) -> SmallVec<[Matrix2<Complex64>; 8]> {
184173
smallvec![
185-
self.u3r * ndarray_to_matrix2(target.K2r.view()),
186-
self.u3l * ndarray_to_matrix2(target.K2l.view()),
187-
self.u2ra * rz_matrix_nalgebra(2. * target.b) * self.u2rb,
188-
self.u2la * rz_matrix_nalgebra(-2. * target.a) * self.u2lb,
189-
self.u1ra * rz_matrix_nalgebra(-2. * target.c) * self.u1rb,
174+
self.u3r * target.K2r,
175+
self.u3l * target.K2l,
176+
self.u2ra * rz_matrix(2. * target.b) * self.u2rb,
177+
self.u2la * rz_matrix(-2. * target.a) * self.u2lb,
178+
self.u1ra * rz_matrix(-2. * target.c) * self.u1rb,
190179
self.u1l,
191-
ndarray_to_matrix2(target.K1r.view()) * self.u0r,
192-
ndarray_to_matrix2(target.K1l.view()) * self.u0l,
180+
target.K1r * self.u0r,
181+
target.K1l * self.u0l,
193182
]
194183
}
195184

@@ -235,14 +224,11 @@ impl TwoQubitBasisDecomposer {
235224
[euler_angles[2], euler_angles[0], euler_angles[1]]
236225
})
237226
.collect();
238-
let mut euler_matrix_q0 =
239-
rx_matrix_nalgebra(euler_q0[0][1]) * rz_matrix_nalgebra(euler_q0[0][0]);
240-
euler_matrix_q0 =
241-
rz_matrix_nalgebra(euler_q0[0][2] + euler_q0[1][0] + FRAC_PI_2) * euler_matrix_q0;
227+
let mut euler_matrix_q0 = rx_matrix(euler_q0[0][1]) * rz_matrix(euler_q0[0][0]);
228+
euler_matrix_q0 = rz_matrix(euler_q0[0][2] + euler_q0[1][0] + FRAC_PI_2) * euler_matrix_q0;
242229
self.append_1q_sequence(&mut gates, &mut global_phase, euler_matrix_q0.as_view(), 0);
243-
let mut euler_matrix_q1 =
244-
rz_matrix_nalgebra(euler_q1[0][1]) * rx_matrix_nalgebra(euler_q1[0][0]);
245-
euler_matrix_q1 = rx_matrix_nalgebra(euler_q1[0][2] + euler_q1[1][0]) * euler_matrix_q1;
230+
let mut euler_matrix_q1 = rz_matrix(euler_q1[0][1]) * rx_matrix(euler_q1[0][0]);
231+
euler_matrix_q1 = rx_matrix(euler_q1[0][2] + euler_q1[1][0]) * euler_matrix_q1;
246232
self.append_1q_sequence(&mut gates, &mut global_phase, euler_matrix_q1.as_view(), 1);
247233
gates.push((StandardGate::CX.into(), smallvec![], smallvec![0, 1]));
248234
if (euler_q0[1][1] - PI).abs() < ANGLE_ZERO_EPSILON {
@@ -268,13 +254,13 @@ impl TwoQubitBasisDecomposer {
268254
));
269255
global_phase += FRAC_PI_2;
270256
gates.push((StandardGate::CX.into(), smallvec![], smallvec![0, 1]));
271-
let mut euler_matrix_q0 = rx_matrix_nalgebra(euler_q0[2][1])
272-
* rz_matrix_nalgebra(euler_q0[1][2] + euler_q0[2][0] + FRAC_PI_2);
273-
euler_matrix_q0 = rz_matrix_nalgebra(euler_q0[2][2]) * euler_matrix_q0;
257+
let mut euler_matrix_q0 =
258+
rx_matrix(euler_q0[2][1]) * rz_matrix(euler_q0[1][2] + euler_q0[2][0] + FRAC_PI_2);
259+
euler_matrix_q0 = rz_matrix(euler_q0[2][2]) * euler_matrix_q0;
274260
self.append_1q_sequence(&mut gates, &mut global_phase, euler_matrix_q0.as_view(), 0);
275-
let mut euler_matrix_q1 = rz_matrix_nalgebra(euler_q1[2][1])
276-
* rx_matrix_nalgebra(euler_q1[1][2] + euler_q1[2][0]);
277-
euler_matrix_q1 = rx_matrix_nalgebra(euler_q1[2][2]) * euler_matrix_q1;
261+
let mut euler_matrix_q1 =
262+
rz_matrix(euler_q1[2][1]) * rx_matrix(euler_q1[1][2] + euler_q1[2][0]);
263+
euler_matrix_q1 = rx_matrix(euler_q1[2][2]) * euler_matrix_q1;
278264
self.append_1q_sequence(&mut gates, &mut global_phase, euler_matrix_q1.as_view(), 1);
279265
Some(TwoQubitGateSequence {
280266
gates,
@@ -341,19 +327,18 @@ impl TwoQubitBasisDecomposer {
341327
let x02_add = x12 - euler_q0[1][0];
342328
let x12_is_half_pi = abs_diff_eq!(x12, FRAC_PI_2, epsilon = atol);
343329

344-
let mut euler_matrix_q0 =
345-
rx_matrix_nalgebra(euler_q0[0][1]) * rz_matrix_nalgebra(euler_q0[0][0]);
330+
let mut euler_matrix_q0 = rx_matrix(euler_q0[0][1]) * rz_matrix(euler_q0[0][0]);
346331
if x12_is_non_zero && x12_is_pi_mult {
347-
euler_matrix_q0 = rz_matrix_nalgebra(euler_q0[0][2] - x02_add) * euler_matrix_q0;
332+
euler_matrix_q0 = rz_matrix(euler_q0[0][2] - x02_add) * euler_matrix_q0;
348333
} else {
349-
euler_matrix_q0 = rz_matrix_nalgebra(euler_q0[0][2] + euler_q0[1][0]) * euler_matrix_q0;
334+
euler_matrix_q0 = rz_matrix(euler_q0[0][2] + euler_q0[1][0]) * euler_matrix_q0;
350335
}
351336
euler_matrix_q0 = HGATE * euler_matrix_q0;
352337
self.append_1q_sequence(&mut gates, &mut global_phase, euler_matrix_q0.as_view(), 0);
353338

354-
let rx_0 = rx_matrix_nalgebra(euler_q1[0][0]);
355-
let rz = rz_matrix_nalgebra(euler_q1[0][1]);
356-
let rx_1 = rx_matrix_nalgebra(euler_q1[0][2] + euler_q1[1][0]);
339+
let rx_0 = rx_matrix(euler_q1[0][0]);
340+
let rz = rz_matrix(euler_q1[0][1]);
341+
let rx_1 = rx_matrix(euler_q1[0][2] + euler_q1[1][0]);
357342
let mut euler_matrix_q1 = rz * rx_0;
358343
euler_matrix_q1 = rx_1 * euler_matrix_q1;
359344
euler_matrix_q1 = HGATE * euler_matrix_q1;
@@ -386,12 +371,7 @@ impl TwoQubitBasisDecomposer {
386371
global_phase -= FRAC_PI_4;
387372
} else if x12_is_non_zero && !x12_is_pi_mult {
388373
if self.pulse_optimize.is_none() {
389-
self.append_1q_sequence(
390-
&mut gates,
391-
&mut global_phase,
392-
rx_matrix_nalgebra(x12).as_view(),
393-
0,
394-
);
374+
self.append_1q_sequence(&mut gates, &mut global_phase, rx_matrix(x12).as_view(), 0);
395375
} else {
396376
return None;
397377
}
@@ -403,7 +383,7 @@ impl TwoQubitBasisDecomposer {
403383
self.append_1q_sequence(
404384
&mut gates,
405385
&mut global_phase,
406-
rx_matrix_nalgebra(euler_q1[1][1]).as_view(),
386+
rx_matrix(euler_q1[1][1]).as_view(),
407387
1,
408388
);
409389
} else {
@@ -427,27 +407,27 @@ impl TwoQubitBasisDecomposer {
427407
self.append_1q_sequence(
428408
&mut gates,
429409
&mut global_phase,
430-
rx_matrix_nalgebra(euler_q1[2][1]).as_view(),
410+
rx_matrix(euler_q1[2][1]).as_view(),
431411
1,
432412
);
433413
} else {
434414
return None;
435415
}
436416
gates.push((StandardGate::CX.into(), smallvec![], smallvec![1, 0]));
437-
let mut euler_matrix = rz_matrix_nalgebra(euler_q0[2][2] + euler_q0[3][0]) * HGATE;
438-
euler_matrix = rx_matrix_nalgebra(euler_q0[3][1]) * euler_matrix;
439-
euler_matrix = rz_matrix_nalgebra(euler_q0[3][2]) * euler_matrix;
417+
let mut euler_matrix = rz_matrix(euler_q0[2][2] + euler_q0[3][0]) * HGATE;
418+
euler_matrix = rx_matrix(euler_q0[3][1]) * euler_matrix;
419+
euler_matrix = rz_matrix(euler_q0[3][2]) * euler_matrix;
440420
self.append_1q_sequence(&mut gates, &mut global_phase, euler_matrix.as_view(), 0);
441421

442-
let mut euler_matrix = rx_matrix_nalgebra(euler_q1[2][2] + euler_q1[3][0]) * HGATE;
443-
euler_matrix = rz_matrix_nalgebra(euler_q1[3][1]) * euler_matrix;
444-
euler_matrix = rx_matrix_nalgebra(euler_q1[3][2]) * euler_matrix;
422+
let mut euler_matrix = rx_matrix(euler_q1[2][2] + euler_q1[3][0]) * HGATE;
423+
euler_matrix = rz_matrix(euler_q1[3][1]) * euler_matrix;
424+
euler_matrix = rx_matrix(euler_q1[3][2]) * euler_matrix;
445425
self.append_1q_sequence(&mut gates, &mut global_phase, euler_matrix.as_view(), 1);
446426

447427
let out_unitary = compute_unitary(&gates, global_phase);
448428
// TODO: fix the sign problem to avoid correction here
449429
if abs_diff_eq!(
450-
target_decomposed.unitary_matrix[[0, 0]],
430+
target_decomposed.unitary_matrix[(0, 0)],
451431
-out_unitary[[0, 0]],
452432
epsilon = atol
453433
) {
@@ -600,10 +580,10 @@ impl TwoQubitBasisDecomposer {
600580
temp * (M_IM * c64(0., b).exp()),
601581
temp * (M_IM * c64(0., -b).exp()),
602582
);
603-
let k1ld = ndarray_to_matrix2(basis_decomposer.K1l.view()).adjoint();
604-
let k1rd = ndarray_to_matrix2(basis_decomposer.K1r.view()).adjoint();
605-
let k2ld = ndarray_to_matrix2(basis_decomposer.K2l.view()).adjoint();
606-
let k2rd = ndarray_to_matrix2(basis_decomposer.K2r.view()).adjoint();
583+
let k1ld = basis_decomposer.K1l.adjoint();
584+
let k1rd = basis_decomposer.K1r.adjoint();
585+
let k2ld = basis_decomposer.K2l.adjoint();
586+
let k2rd = basis_decomposer.K2r.adjoint();
607587
// Pre-build the fixed parts of the matrices used in 3-part decomposition
608588
let u0l = k31l * k1ld;
609589
let u0r = k31r * k1rd;
@@ -752,10 +732,7 @@ impl TwoQubitBasisDecomposer {
752732
}
753733

754734
fn decomp0_inner(target: &TwoQubitWeylDecomposition) -> SmallVec<[Matrix2<Complex64>; 8]> {
755-
smallvec![
756-
ndarray_to_matrix2(target.K1r.view()) * ndarray_to_matrix2(target.K2r.view()),
757-
ndarray_to_matrix2(target.K1l.view()) * ndarray_to_matrix2(target.K2l.view()),
758-
]
735+
smallvec![target.K1r * target.K2r, target.K1l * target.K2l,]
759736
}
760737

761738
type PickleNewArgs<'a> = (Py<PyAny>, Py<PyAny>, f64, &'a str, Option<bool>);

crates/synthesis/src/two_qubit_decompose/common.rs

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@
1212

1313
use std::f64::consts::FRAC_1_SQRT_2;
1414

15-
use nalgebra::Matrix2;
15+
use nalgebra::{Matrix2, Matrix4};
1616
use ndarray::{Array2, ArrayView2, array, aview2};
1717
use num_complex::{Complex64, ComplexFloat};
1818
use numpy::{IntoPyArray, PyArray2, PyReadonlyArray1, PyReadonlyArray2};
1919
use pyo3::prelude::*;
2020

2121
use crate::linalg::ndarray_to_faer;
22-
use qiskit_util::alias::{GateArray1Q, GateArray2Q};
22+
use qiskit_util::alias::GateArray2Q;
2323
use qiskit_util::complex::{C_M_ONE, C_ONE, C_ZERO, IM, M_IM, c64};
2424
pub(super) const DEFAULT_FIDELITY: f64 = 1.0 - 1.0e-9;
2525

@@ -98,14 +98,11 @@ static MAGIC_DAGGER: GateArray2Q = [
9898
],
9999
];
100100

101-
pub static IPZ: GateArray1Q = [[IM, C_ZERO], [C_ZERO, M_IM]];
102-
pub static IPY: GateArray1Q = [[C_ZERO, C_ONE], [C_M_ONE, C_ZERO]];
103-
pub static IPX: GateArray1Q = [[C_ZERO, IM], [IM, C_ZERO]];
101+
pub(super) static IPZ: Matrix2<Complex64> = Matrix2::new(IM, C_ZERO, C_ZERO, M_IM);
104102

105-
#[inline(always)]
106-
pub(crate) fn transpose_conjugate(mat: ArrayView2<Complex64>) -> Array2<Complex64> {
107-
mat.t().mapv(|x| x.conj())
108-
}
103+
pub(super) static IPY: Matrix2<Complex64> = Matrix2::new(C_ZERO, C_ONE, C_M_ONE, C_ZERO);
104+
105+
pub(super) static IPX: Matrix2<Complex64> = Matrix2::new(C_ZERO, IM, IM, C_ZERO);
109106

110107
/// A good approximation to the best value x to get the minimum
111108
/// trace distance for :math:`U_d(x, x, x)` from :math:`U_d(a, b, c)`.
@@ -116,33 +113,21 @@ pub(crate) fn closest_partial_swap(a: f64, b: f64, c: f64) -> f64 {
116113
m + am * bm * cm * (6. + ab * ab + bc * bc + ca * ca) / 18.
117114
}
118115

119-
pub(crate) fn rx_matrix(theta: f64) -> Array2<Complex64> {
120-
let half_theta = theta / 2.;
121-
let cos = c64(half_theta.cos(), 0.);
122-
let isin = c64(0., -half_theta.sin());
123-
array![[cos, isin], [isin, cos]]
124-
}
125-
126-
pub(crate) fn rx_matrix_nalgebra(theta: f64) -> Matrix2<Complex64> {
116+
pub(crate) fn rx_matrix(theta: f64) -> Matrix2<Complex64> {
127117
let half_theta = theta / 2.;
128118
let cos = c64(half_theta.cos(), 0.);
129119
let isin = c64(0., -half_theta.sin());
130120
Matrix2::new(cos, isin, isin, cos)
131121
}
132122

133-
pub(crate) fn ry_matrix(theta: f64) -> Array2<Complex64> {
123+
pub(crate) fn ry_matrix(theta: f64) -> Matrix2<Complex64> {
134124
let half_theta = theta / 2.;
135125
let cos = c64(half_theta.cos(), 0.);
136126
let sin = c64(half_theta.sin(), 0.);
137-
array![[cos, -sin], [sin, cos]]
138-
}
139-
140-
pub(crate) fn rz_matrix(theta: f64) -> Array2<Complex64> {
141-
let ilam2 = c64(0., 0.5 * theta);
142-
array![[(-ilam2).exp(), C_ZERO], [C_ZERO, ilam2.exp()]]
127+
Matrix2::new(cos, -sin, sin, cos)
143128
}
144129

145-
pub(crate) fn rz_matrix_nalgebra(theta: f64) -> Matrix2<Complex64> {
130+
pub(crate) fn rz_matrix(theta: f64) -> Matrix2<Complex64> {
146131
let ilam2 = c64(0., 0.5 * theta);
147132
Matrix2::new((-ilam2).exp(), C_ZERO, C_ZERO, ilam2.exp())
148133
}
@@ -224,3 +209,25 @@ pub fn local_equivalence(weyl: PyReadonlyArray1<f64>) -> PyResult<[f64; 3]> {
224209
- weyl.iter().map(|x| (4. * x).cos()).product::<f64>();
225210
Ok([g0_equiv + 0., g1_equiv + 0., g2_equiv + 0.])
226211
}
212+
213+
/// Copy the input array view into a Matrix2 output
214+
///
215+
/// This function assumes the input is a 2x2 matrix. If a matrix of a
216+
/// different shape is passed in it will pick the first 4 elements in logical
217+
/// order (row major) and if it doesn't have 4 elements it will panic. This
218+
/// should only really be used for copying a 2x2 ndarray view into a Matrix2.
219+
#[inline]
220+
pub(super) fn ndarray_to_matrix2<T: Copy>(view: ArrayView2<T>) -> Matrix2<T> {
221+
Matrix2::new(view[[0, 0]], view[(0, 1)], view[(1, 0)], view[(1, 1)])
222+
}
223+
224+
/// Copy the input array view into a Matrix4 output
225+
///
226+
/// This function assumes the input is a 4x4 matrix. If a matrix of a
227+
/// different shape is passed in it will pick the first 16 elements in logical
228+
/// order (row major) and if it doesn't have 16 elements it will panic. This
229+
/// should only really be used for copying a 4x4 ndarray view into a Matrix4.
230+
#[inline]
231+
pub(super) fn ndarray_to_matrix4(view: ArrayView2<Complex64>) -> Matrix4<Complex64> {
232+
Matrix4::from_row_iterator(view.iter().copied())
233+
}

0 commit comments

Comments
 (0)