1use mdarray::{Array, Dim, Layout, Shape, Slice, tensor};
9use num_complex::ComplexFloat;
10use num_traits::{One, Zero};
11
12pub fn pretty_print<T: ComplexFloat + std::fmt::Display, D0: Dim, D1: Dim>(mat: &Array<T, (D0, D1)>)
14where
15 <T as num_complex::ComplexFloat>::Real: std::fmt::Display,
16{
17 let shape = mat.shape();
18 for i in 0..shape.dim(0) {
19 for j in 0..shape.dim(1) {
20 let v = mat[[i, j]];
21 print!("{:>10.4} {:+.4}i ", v.re(), v.im(),);
22 }
23 println!();
24 }
25 println!();
26}
27
28#[doc(hidden)]
33pub fn into_i32<T>(x: T) -> i32
34where
35 T: TryInto<i32>,
36 <T as TryInto<i32>>::Error: std::fmt::Debug,
37{
38 x.try_into().expect("dimension must fit into i32")
39}
40
41#[doc(hidden)]
45pub fn dims3(a_shape: impl Shape, b_shape: impl Shape, c_shape: impl Shape) -> (i32, i32, i32) {
46 let (m, k) = (a_shape.dim(0), a_shape.dim(1));
47 let (k2, n) = (b_shape.dim(0), b_shape.dim(1));
48 let (m2, n2) = (c_shape.dim(0), c_shape.dim(1));
49
50 assert!(m == m2, "a and c must agree in number of rows");
51 assert!(n == n2, "b and c must agree in number of columns");
52 assert!(
53 k == k2,
54 "a's number of columns must be equal to b's number of rows"
55 );
56
57 (into_i32(m), into_i32(n), into_i32(k))
58}
59
60#[doc(hidden)]
63pub fn dims2(a_shape: impl Shape, b_shape: impl Shape) -> (i32, i32) {
64 let (m, k) = (a_shape.dim(0), a_shape.dim(1));
65 let (k2, n) = (b_shape.dim(0), b_shape.dim(1));
66
67 assert!(
68 k == k2,
69 "a's number of columns must be equal to b's number of rows"
70 );
71
72 (into_i32(m), into_i32(n))
73}
74
75#[doc(hidden)]
80pub fn transpose_in_place<T, D0, D1, L>(c: &mut Slice<T, (D0, D1), L>)
81where
82 T: ComplexFloat + Default,
83 D0: Dim,
84 D1: Dim,
85 L: Layout,
86{
87 let (m, n) = *c.shape();
88
89 let m = m.size();
90 let n = n.size();
91
92 if n == m {
93 for i in 0..m {
94 for j in (i + 1)..n {
95 c.swap(i * n + j, j * n + i);
96 }
97 }
98 } else {
99 let mut result = tensor![[T::default(); m]; n];
100 for j in 0..n {
101 for i in 0..m {
102 result[j * m + i] = c[i * n + j];
103 }
104 }
105 for j in 0..n {
106 for i in 0..m {
107 c[j * m + i] = result[j * m + i];
108 }
109 }
110 }
111}
112
113#[doc(hidden)]
117pub fn conjugate_in_place<T, D0, D1, L>(c: &mut Slice<T, (D0, D1), L>)
118where
119 T: ComplexFloat + Default,
120 D0: Dim,
121 D1: Dim,
122 L: Layout,
123{
124 c.iter_mut().for_each(|elem| *elem = elem.conj());
125}
126
127#[doc(hidden)]
129pub fn ipiv_to_perm_mat<T: ComplexFloat, D0: Dim, D1: Dim>(
130 ipiv: &[i32],
131 m: usize,
132) -> Array<T, (D0, D1)> {
133 let mut p = Array::from_elem(<(D0, D1) as Shape>::from_dims(&[m, m]), T::zero());
134
135 for i in 0..m {
136 p[[i, i]] = T::one();
137 }
138
139 for i in 0..ipiv.len() {
141 let pivot_row = (ipiv[i] - 1) as usize; if pivot_row != i {
143 for j in 0..m {
144 let temp = p[[i, j]];
145 p[[i, j]] = p[[pivot_row, j]];
146 p[[pivot_row, j]] = temp;
147 }
148 }
149 }
150
151 p
152}
153
154#[doc(hidden)]
158pub fn to_col_major<T, D0: Dim, D1: Dim, L>(c: &Slice<T, (D0, D1), L>) -> Array<T, (D1, D0)>
159where
160 T: ComplexFloat + Default + Clone,
161 L: Layout,
162{
163 let csh = *c.shape();
164 let (m, n) = (csh.dim(0), csh.dim(1));
165
166 let shape = <(D1, D0) as Shape>::from_dims(&[n, m]);
167 let mut result = Array::<T, (D1, D0)>::zeros(shape);
168
169 for i in 0..m {
170 for j in 0..n {
171 result[[j, i]] = c[[i, j]];
172 }
173 }
174
175 result
176}
177
178pub fn trace<T, D0, D1, L>(a: &Slice<T, (D0, D1), L>) -> T
192where
193 T: ComplexFloat + std::ops::Add<Output = T> + Copy,
194 D0: Dim,
195 D1: Dim,
196 L: Layout,
197{
198 let ash = *a.shape();
199 let (m, n) = (ash.dim(0), ash.dim(1));
200 assert_eq!(m, n, "trace is only defined for square matrices");
201
202 let mut tr = T::zero();
203 for i in 0..n {
204 tr = tr + a[[i, i]];
205 }
206 tr
207}
208
209pub fn identity<T: Zero + One, D0: Dim, D1: Dim>(n: usize) -> Array<T, (D0, D1)> {
219 Array::<T, (D0, D1)>::from_fn(<(D0, D1) as Shape>::from_dims(&[n, n]), |i| {
220 if i[0] == i[1] { T::one() } else { T::zero() }
221 })
222}
223
224pub fn identity_k<T: Zero + One, D0: Dim, D1: Dim>(n: usize, k: isize) -> Array<T, (D0, D1)> {
239 Array::<T, (D0, D1)>::from_fn(<(D0, D1) as Shape>::from_dims(&[n, n]), |i| {
240 if (i[1] as isize - i[0] as isize) == k {
241 T::one()
242 } else {
243 T::zero()
244 }
245 })
246}
247
248pub fn kron<T, D0, D1, La, Lb>(
275 a: &Slice<T, (D0, D1), La>,
276 b: &Slice<T, (D0, D1), Lb>,
277) -> Array<T, (D0, D1)>
278where
279 T: ComplexFloat + std::ops::Mul<Output = T> + Copy,
280 D0: Dim,
281 D1: Dim,
282 La: Layout,
283 Lb: Layout,
284{
285 let ash = *a.shape();
286 let (ma, na) = (ash.dim(0), ash.dim(1));
287
288 let bsh = *b.shape();
289 let (mb, nb) = (bsh.dim(0), bsh.dim(1));
290
291 let out_shape = <(D0, D1) as Shape>::from_dims(&[ma * mb, na * nb]);
292
293 Array::<T, (D0, D1)>::from_fn(out_shape, |idx| {
294 let i = idx[0];
295 let j = idx[1];
296
297 let ai = i / mb;
298 let bi = i % mb;
299 let aj = j / nb;
300 let bj = j % nb;
301
302 a[[ai, aj]] * b[[bi, bj]]
303 })
304}
305
306pub fn unravel_index<T, S: Shape, L: Layout>(x: &Slice<T, S, L>, mut flat: usize) -> Vec<usize> {
325 let rank = x.rank();
326
327 assert!(
328 flat < x.len(),
329 "flat index out of bounds: {} >= {}",
330 flat,
331 x.len()
332 );
333
334 let mut coords = vec![0usize; rank];
335
336 for i in (0..rank).rev() {
337 let dim = x.shape().dim(i);
338 coords[i] = flat % dim;
339 flat /= dim;
340 }
341
342 coords
343}
344
345pub fn diag<T: Zero + One + Clone, D: Dim>(v: &Slice<T, (D,)>) -> Array<T, (D, D)> {
357 let n = v.dim(0);
358 Array::<T, (D, D)>::from_fn(<(D, D) as Shape>::from_dims(&[n, n]), |i| {
359 if i[0] == i[1] {
360 v[i[0]].clone()
361 } else {
362 T::zero()
363 }
364 })
365}