1use ndarray::{Array, Array1, Array2, ArrayView1, ArrayView2};
7use num_traits::Zero;
8use std::{convert::TryInto, ops::Mul};
9
10use crate::{common_matrices::nq_eye, C64};
11
12pub trait HasDagger {
16 type Output;
18
19 fn dag(&self) -> Self::Output;
25}
26
27impl HasDagger for Array2<C64> {
28 type Output = Self;
29
30 fn dag(&self) -> Self {
31 self.t().map(|element| element.conj())
32 }
33}
34
35impl HasDagger for ArrayView2<'_, C64> {
36 type Output = Array2<C64>;
37
38 fn dag(&self) -> Self::Output {
39 self.t().map(|element| element.conj())
40 }
41}
42
43pub trait ConjBy {
46 fn conjugate_by(&self, op: &ArrayView2<C64>) -> Self;
48}
49
50impl ConjBy for Array2<C64> {
51 fn conjugate_by(&self, op: &ArrayView2<C64>) -> Self {
52 op.dot(self).dot(&op.dag())
53 }
54}
55
56pub trait Tensor<Rhs = Self> {
58 type Output;
60
61 fn tensor(self, rhs: Rhs) -> Self::Output;
68}
69
70impl<Other: Into<Self>, T: Copy + Mul<Output = T>> Tensor<Other> for ArrayView1<'_, T> {
71 type Output = Array1<T>;
72 fn tensor(self, other: Other) -> Self::Output {
73 let other: Self = other.into();
74 let unflat = Array::from_shape_fn((self.shape()[0], other.shape()[0]), |(i, j)| {
75 self[(i)] * other[(j)]
76 });
77 unflat
78 .into_shape(self.shape()[0] * other.shape()[0])
79 .unwrap()
80 }
81}
82
83impl<Other: Into<Self>, T: Copy + Mul<Output = T>> Tensor<Other> for &Array1<T> {
84 type Output = Array1<T>;
85
86 fn tensor(self, other: Other) -> Self::Output {
87 let other: Self = other.into();
88 self.view().tensor(other)
89 }
90}
91
92impl<Other: Into<Self>, T: Copy + Mul<Output = T>> Tensor<Other> for ArrayView2<'_, T> {
93 type Output = Array2<T>;
94 fn tensor(self, other: Other) -> Self::Output {
95 let other: Self = other.into();
96 let unflat = Array::from_shape_fn(
97 (
98 self.shape()[0],
99 other.shape()[0],
100 self.shape()[1],
101 other.shape()[1],
102 ),
103 |(i, j, k, l)| self[(i, k)] * other[(j, l)],
104 );
105 unflat
106 .into_shape((
107 self.shape()[0] * other.shape()[0],
108 self.shape()[1] * other.shape()[1],
109 ))
110 .unwrap()
111 }
112}
113
114impl<Other: Into<Self>, T: Copy + Mul<Output = T>> Tensor<Other> for &Array2<T> {
115 type Output = Array2<T>;
116
117 fn tensor(self, other: Other) -> Self::Output {
118 let other: Self = other.into();
119 self.view().tensor(other).to_owned()
120 }
121}
122
123pub trait Trace {
125 type Output;
127
128 fn trace(self) -> Self::Output;
136}
137
138impl<T: Clone + Zero> Trace for Array2<T> {
139 type Output = T;
140
141 fn trace(self) -> Self::Output {
142 self.diag().sum()
143 }
144}
145
146impl<T: Clone + Zero> Trace for &Array2<T> {
147 type Output = T;
148
149 fn trace(self) -> Self::Output {
150 self.diag().sum()
151 }
152}
153
154pub fn extend_one_to_n(data: ArrayView2<C64>, idx_qubit: usize, n_qubits: usize) -> Array2<C64> {
160 let n_left = idx_qubit;
161 let n_right = n_qubits - idx_qubit - 1;
162 match (n_left, n_right) {
163 (0, _) => {
164 let right_eye = nq_eye(n_right);
165 data.view().tensor(&right_eye)
166 }
167 (_, 0) => {
168 let left_eye = Array2::eye(2usize.pow(n_left.try_into().unwrap()));
169 left_eye.view().tensor(&data)
170 }
171 (_, _) => {
172 let eye = nq_eye(n_right);
173 let right = data.view().tensor(&eye);
174 nq_eye(n_left).view().tensor(&right)
175 }
176 }
177}
178
179pub fn extend_two_to_n(
182 data: ArrayView2<C64>,
183 idx_qubit1: usize,
184 idx_qubit2: usize,
185 n_qubits: usize,
186) -> Array2<C64> {
187 let mut permutation = Array::from((0..n_qubits).collect::<Vec<usize>>());
189 match (idx_qubit1, idx_qubit2) {
190 (1, 0) => permutation.swap(0, 1),
191 (_, 0) => {
192 permutation.swap(1, idx_qubit2);
193 permutation.swap(1, idx_qubit1);
194 }
195 _ => {
196 permutation.swap(1, idx_qubit2);
197 permutation.swap(0, idx_qubit1);
198 }
199 };
200
201 if n_qubits == 2 {
203 permute_mtx(&data.to_owned(), &permutation.to_vec()[..])
205 } else {
206 permute_mtx(
207 &data.view().tensor(&nq_eye(n_qubits - 2)),
208 &permutation.to_vec()[..],
209 )
210 }
211}
212
213pub fn permute_mtx(data: &Array2<C64>, new_order: &[usize]) -> Array2<C64> {
217 let (n_rows, n_cols) = (data.shape()[0], data.shape()[1]);
219 assert_eq!(n_rows, n_cols);
220
221 let n_qubits = (n_rows as f64).log2().floor() as usize;
223 assert_eq!(n_rows, 2usize.pow(n_qubits.try_into().unwrap()));
224
225 assert_eq!(n_qubits, new_order.len());
227
228 let new_dims: Vec<usize> = vec![2usize]
231 .iter()
232 .cycle()
233 .take(2 * n_qubits)
234 .copied()
235 .collect();
236 let tensor = data.clone().into_shared().reshape(new_dims);
238 let mut permutation = new_order.to_vec();
239 permutation.extend(new_order.to_vec().iter().map(|idx| idx + n_qubits));
240 let permuted = tensor.permuted_axes(permutation);
241
242 permuted.reshape([n_rows, n_rows]).into_owned()
244}
245
246pub fn zeros_like<T: Clone + Zero, Ix: ndarray::Dimension>(data: &Array<T, Ix>) -> Array<T, Ix> {
249 Array::zeros(data.dim())
250}