1#[cfg(test)]
2mod test;
3
4pub mod list;
5pub mod list_2d;
6pub mod vec;
7pub mod vec_2d;
8
9use std::{
10 array::from_fn,
11 f64::consts::TAU,
12 fmt::{self, Display, Formatter},
13 iter::Sum,
14 mem::transmute,
15 ops::{Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Sub, SubAssign},
16};
17
18use super::{
19 super::assert_eq_within_tols,
20 Hessian, Jacobian, Rank2, Solution, SquareMatrix, Tensor, TensorArray, Vector,
21 rank_0::{TensorRank0, list::TensorRank0List},
22 rank_1::{
23 TensorRank1, list::TensorRank1List, tensor_rank_1, vec::TensorRank1Vec,
24 zero as tensor_rank_1_zero,
25 },
26 rank_4::TensorRank4,
27};
28use crate::ABS_TOL;
29use list_2d::TensorRank2List2D;
30use vec_2d::TensorRank2Vec2D;
31
32#[cfg(test)]
33use super::test::ErrorTensor;
34
35#[derive(Clone, Debug, PartialEq)]
39pub struct TensorRank2<const D: usize, const I: usize, const J: usize>([TensorRank1<D, J>; D]);
40
41impl<const D: usize, const I: usize, const J: usize> Default for TensorRank2<D, I, J> {
42 fn default() -> Self {
43 Self::zero()
44 }
45}
46
47pub const fn tensor_rank_2<const D: usize, const I: usize, const J: usize>(
48 array: [TensorRank1<D, J>; D],
49) -> TensorRank2<D, I, J> {
50 TensorRank2(array)
51}
52
53pub const fn get_levi_civita_parts<const I: usize, const J: usize>() -> [TensorRank2<3, I, J>; 3] {
54 [
55 TensorRank2([
56 tensor_rank_1_zero(),
57 tensor_rank_1([0.0, 0.0, 1.0]),
58 tensor_rank_1([0.0, -1.0, 0.0]),
59 ]),
60 TensorRank2([
61 tensor_rank_1([0.0, 0.0, -1.0]),
62 tensor_rank_1_zero(),
63 tensor_rank_1([1.0, 0.0, 0.0]),
64 ]),
65 TensorRank2([
66 tensor_rank_1([0.0, 1.0, 0.0]),
67 tensor_rank_1([-1.0, 0.0, 0.0]),
68 tensor_rank_1_zero(),
69 ]),
70 ]
71}
72
73pub const fn get_identity_1010_parts_1<const I: usize, const J: usize>() -> [TensorRank2<3, I, J>; 3]
74{
75 [
76 TensorRank2([
77 tensor_rank_1([1.0, 0.0, 0.0]),
78 tensor_rank_1_zero(),
79 tensor_rank_1_zero(),
80 ]),
81 TensorRank2([
82 tensor_rank_1([0.0, 1.0, 0.0]),
83 tensor_rank_1_zero(),
84 tensor_rank_1_zero(),
85 ]),
86 TensorRank2([
87 tensor_rank_1([0.0, 0.0, 1.0]),
88 tensor_rank_1_zero(),
89 tensor_rank_1_zero(),
90 ]),
91 ]
92}
93
94pub const fn get_identity_1010_parts_2<const I: usize, const J: usize>() -> [TensorRank2<3, I, J>; 3]
95{
96 [
97 TensorRank2([
98 tensor_rank_1_zero(),
99 tensor_rank_1([1.0, 0.0, 0.0]),
100 tensor_rank_1_zero(),
101 ]),
102 TensorRank2([
103 tensor_rank_1_zero(),
104 tensor_rank_1([0.0, 1.0, 0.0]),
105 tensor_rank_1_zero(),
106 ]),
107 TensorRank2([
108 tensor_rank_1_zero(),
109 tensor_rank_1([0.0, 0.0, 1.0]),
110 tensor_rank_1_zero(),
111 ]),
112 ]
113}
114
115pub const fn get_identity_1010_parts_3<const I: usize, const J: usize>() -> [TensorRank2<3, I, J>; 3]
116{
117 [
118 TensorRank2([
119 tensor_rank_1_zero(),
120 tensor_rank_1_zero(),
121 tensor_rank_1([1.0, 0.0, 0.0]),
122 ]),
123 TensorRank2([
124 tensor_rank_1_zero(),
125 tensor_rank_1_zero(),
126 tensor_rank_1([0.0, 1.0, 0.0]),
127 ]),
128 TensorRank2([
129 tensor_rank_1_zero(),
130 tensor_rank_1_zero(),
131 tensor_rank_1([0.0, 0.0, 1.0]),
132 ]),
133 ]
134}
135
136pub const IDENTITY: TensorRank2<3, 1, 1> = TensorRank2([
137 tensor_rank_1([1.0, 0.0, 0.0]),
138 tensor_rank_1([0.0, 1.0, 0.0]),
139 tensor_rank_1([0.0, 0.0, 1.0]),
140]);
141
142pub const IDENTITY_00: TensorRank2<3, 0, 0> = TensorRank2([
143 tensor_rank_1([1.0, 0.0, 0.0]),
144 tensor_rank_1([0.0, 1.0, 0.0]),
145 tensor_rank_1([0.0, 0.0, 1.0]),
146]);
147
148pub const IDENTITY_10: TensorRank2<3, 1, 0> = TensorRank2([
149 tensor_rank_1([1.0, 0.0, 0.0]),
150 tensor_rank_1([0.0, 1.0, 0.0]),
151 tensor_rank_1([0.0, 0.0, 1.0]),
152]);
153
154pub const ZERO: TensorRank2<3, 1, 1> = TensorRank2([
155 tensor_rank_1_zero(),
156 tensor_rank_1_zero(),
157 tensor_rank_1_zero(),
158]);
159
160pub const ZERO_10: TensorRank2<3, 1, 0> = TensorRank2([
161 tensor_rank_1_zero(),
162 tensor_rank_1_zero(),
163 tensor_rank_1_zero(),
164]);
165
166impl<const D: usize, const I: usize, const J: usize> From<Vec<Vec<TensorRank0>>>
167 for TensorRank2<D, I, J>
168{
169 fn from(vec: Vec<Vec<TensorRank0>>) -> Self {
170 assert_eq!(vec.len(), D);
171 vec.iter().for_each(|entry| assert_eq!(entry.len(), D));
172 vec.into_iter()
173 .map(|entry| entry.into_iter().collect())
174 .collect()
175 }
176}
177
178impl<const D: usize, const I: usize, const J: usize> From<TensorRank2<D, I, J>>
179 for Vec<Vec<TensorRank0>>
180{
181 fn from(tensor: TensorRank2<D, I, J>) -> Self {
182 tensor
183 .iter()
184 .map(|entry| entry.iter().copied().collect())
185 .collect()
186 }
187}
188
189impl<const D: usize, const I: usize, const J: usize> Display for TensorRank2<D, I, J> {
190 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
191 write!(f, "[")?;
192 self.iter()
193 .enumerate()
194 .try_for_each(|(i, row)| write!(f, "{row},\n\x1B[u\x1B[{}B", i + 1))?;
195 write!(f, "\x1B[u\x1B[1A\x1B[{}C]", 16 * D)
196 }
197}
198
199#[cfg(test)]
200impl<const D: usize, const I: usize, const J: usize> ErrorTensor for TensorRank2<D, I, J> {
201 fn error_fd(&self, comparator: &Self, epsilon: &TensorRank0) -> Option<(bool, usize)> {
202 let error_count = self
203 .iter()
204 .zip(comparator.iter())
205 .map(|(self_i, comparator_i)| {
206 self_i
207 .iter()
208 .zip(comparator_i.iter())
209 .filter(|&(&self_ij, &comparator_ij)| {
210 &(self_ij / comparator_ij - 1.0).abs() >= epsilon
211 && (&self_ij.abs() >= epsilon || &comparator_ij.abs() >= epsilon)
212 })
213 .count()
214 })
215 .sum();
216 if error_count > 0 {
217 Some((true, error_count))
218 } else {
219 None
220 }
221 }
222}
223
224impl<const D: usize, const I: usize, const J: usize> TensorRank2<D, I, J> {
225 pub const fn as_ptr(&self) -> *const TensorRank1<D, J> {
227 self.0.as_ptr()
228 }
229 pub fn as_tensor_rank_1(&self) -> TensorRank1<9, 88> {
231 assert_eq!(D, 3);
232 let mut tensor_rank_1 = TensorRank1::<9, 88>::zero();
233 self.iter().enumerate().for_each(|(i, self_i)| {
234 self_i
235 .iter()
236 .enumerate()
237 .for_each(|(j, self_ij)| tensor_rank_1[3 * i + j] = *self_ij)
238 });
239 tensor_rank_1
240 }
241 pub fn determinant(&self) -> TensorRank0 {
243 if D == 2 {
244 self[0][0] * self[1][1] - self[0][1] * self[1][0]
245 } else if D == 3 {
246 let c_00 = self[1][1] * self[2][2] - self[1][2] * self[2][1];
247 let c_10 = self[1][2] * self[2][0] - self[1][0] * self[2][2];
248 let c_20 = self[1][0] * self[2][1] - self[1][1] * self[2][0];
249 self[0][0] * c_00 + self[0][1] * c_10 + self[0][2] * c_20
250 } else if D == 4 {
251 let s0 = self[0][0] * self[1][1] - self[0][1] * self[1][0];
252 let s1 = self[0][0] * self[1][2] - self[0][2] * self[1][0];
253 let s2 = self[0][0] * self[1][3] - self[0][3] * self[1][0];
254 let s3 = self[0][1] * self[1][2] - self[0][2] * self[1][1];
255 let s4 = self[0][1] * self[1][3] - self[0][3] * self[1][1];
256 let s5 = self[0][2] * self[1][3] - self[0][3] * self[1][2];
257 let c5 = self[2][2] * self[3][3] - self[2][3] * self[3][2];
258 let c4 = self[2][1] * self[3][3] - self[2][3] * self[3][1];
259 let c3 = self[2][1] * self[3][2] - self[2][2] * self[3][1];
260 let c2 = self[2][0] * self[3][3] - self[2][3] * self[3][0];
261 let c1 = self[2][0] * self[3][2] - self[2][2] * self[3][0];
262 let c0 = self[2][0] * self[3][1] - self[2][1] * self[3][0];
263 s0 * c5 - s1 * c4 + s2 * c3 + s3 * c2 - s4 * c1 + s5 * c0
264 } else {
265 let (_, u, p) = self.lu_decomposition();
266 let num_swaps = p.iter().enumerate().filter(|(i, p_i)| p_i != &i).count();
267 u.into_iter()
268 .enumerate()
269 .map(|(i, u_i)| u_i[i])
270 .product::<TensorRank0>()
271 * if num_swaps % 2 == 0 { 1.0 } else { -1.0 }
272 }
273 }
274 pub fn dyad(vector_a: &TensorRank1<D, I>, vector_b: &TensorRank1<D, J>) -> Self {
276 vector_a
277 .iter()
278 .map(|vector_a_i| {
279 vector_b
280 .iter()
281 .map(|vector_b_j| vector_a_i * vector_b_j)
282 .collect()
283 })
284 .collect()
285 }
286 pub fn inverse(&self) -> TensorRank2<D, J, I> {
288 if D == 2 {
289 let mut adjugate = TensorRank2::<D, J, I>::zero();
290 adjugate[0][0] = self[1][1];
291 adjugate[0][1] = -self[0][1];
292 adjugate[1][0] = -self[1][0];
293 adjugate[1][1] = self[0][0];
294 adjugate / self.determinant()
295 } else if D == 3 {
296 let mut adjugate = TensorRank2::<D, J, I>::zero();
297 let c_00 = self[1][1] * self[2][2] - self[1][2] * self[2][1];
298 let c_10 = self[1][2] * self[2][0] - self[1][0] * self[2][2];
299 let c_20 = self[1][0] * self[2][1] - self[1][1] * self[2][0];
300 adjugate[0][0] = c_00;
301 adjugate[0][1] = self[0][2] * self[2][1] - self[0][1] * self[2][2];
302 adjugate[0][2] = self[0][1] * self[1][2] - self[0][2] * self[1][1];
303 adjugate[1][0] = c_10;
304 adjugate[1][1] = self[0][0] * self[2][2] - self[0][2] * self[2][0];
305 adjugate[1][2] = self[0][2] * self[1][0] - self[0][0] * self[1][2];
306 adjugate[2][0] = c_20;
307 adjugate[2][1] = self[0][1] * self[2][0] - self[0][0] * self[2][1];
308 adjugate[2][2] = self[0][0] * self[1][1] - self[0][1] * self[1][0];
309 adjugate / (self[0][0] * c_00 + self[0][1] * c_10 + self[0][2] * c_20)
310 } else if D == 4 {
311 let mut adjugate = TensorRank2::<D, J, I>::zero();
312 let s0 = self[0][0] * self[1][1] - self[0][1] * self[1][0];
313 let s1 = self[0][0] * self[1][2] - self[0][2] * self[1][0];
314 let s2 = self[0][0] * self[1][3] - self[0][3] * self[1][0];
315 let s3 = self[0][1] * self[1][2] - self[0][2] * self[1][1];
316 let s4 = self[0][1] * self[1][3] - self[0][3] * self[1][1];
317 let s5 = self[0][2] * self[1][3] - self[0][3] * self[1][2];
318 let c5 = self[2][2] * self[3][3] - self[2][3] * self[3][2];
319 let c4 = self[2][1] * self[3][3] - self[2][3] * self[3][1];
320 let c3 = self[2][1] * self[3][2] - self[2][2] * self[3][1];
321 let c2 = self[2][0] * self[3][3] - self[2][3] * self[3][0];
322 let c1 = self[2][0] * self[3][2] - self[2][2] * self[3][0];
323 let c0 = self[2][0] * self[3][1] - self[2][1] * self[3][0];
324 adjugate[0][0] = self[1][1] * c5 - self[1][2] * c4 + self[1][3] * c3;
325 adjugate[0][1] = self[0][2] * c4 - self[0][1] * c5 - self[0][3] * c3;
326 adjugate[0][2] = self[3][1] * s5 - self[3][2] * s4 + self[3][3] * s3;
327 adjugate[0][3] = self[2][2] * s4 - self[2][1] * s5 - self[2][3] * s3;
328 adjugate[1][0] = self[1][2] * c2 - self[1][0] * c5 - self[1][3] * c1;
329 adjugate[1][1] = self[0][0] * c5 - self[0][2] * c2 + self[0][3] * c1;
330 adjugate[1][2] = self[3][2] * s2 - self[3][0] * s5 - self[3][3] * s1;
331 adjugate[1][3] = self[2][0] * s5 - self[2][2] * s2 + self[2][3] * s1;
332 adjugate[2][0] = self[1][0] * c4 - self[1][1] * c2 + self[1][3] * c0;
333 adjugate[2][1] = self[0][1] * c2 - self[0][0] * c4 - self[0][3] * c0;
334 adjugate[2][2] = self[3][0] * s4 - self[3][1] * s2 + self[3][3] * s0;
335 adjugate[2][3] = self[2][1] * s2 - self[2][0] * s4 - self[2][3] * s0;
336 adjugate[3][0] = self[1][1] * c1 - self[1][0] * c3 - self[1][2] * c0;
337 adjugate[3][1] = self[0][0] * c3 - self[0][1] * c1 + self[0][2] * c0;
338 adjugate[3][2] = self[3][1] * s1 - self[3][0] * s3 - self[3][2] * s0;
339 adjugate[3][3] = self[2][0] * s3 - self[2][1] * s1 + self[2][2] * s0;
340 adjugate / (s0 * c5 - s1 * c4 + s2 * c3 + s3 * c2 - s4 * c1 + s5 * c0)
341 } else {
342 let (l_inverse, u_inverse, p) = self.lu_decomposition_inverse();
343 let mut q = [0; D];
344 p.into_iter().enumerate().for_each(|(i, p_i)| q[p_i] = i);
345 u_inverse
346 .into_iter()
347 .map(|u_inverse_i| {
348 q.iter()
349 .map(|&q_j| {
350 u_inverse_i
351 .iter()
352 .zip(l_inverse.iter())
353 .map(|(u_inverse_ik, l_inverse_k)| u_inverse_ik * l_inverse_k[q_j])
354 .sum()
355 })
356 .collect()
357 })
358 .collect()
359 }
360 }
361 pub fn inverse_and_determinant(&self) -> (TensorRank2<D, J, I>, TensorRank0) {
363 if D == 2 {
364 let mut adjugate = TensorRank2::<D, J, I>::zero();
365 adjugate[0][0] = self[1][1];
366 adjugate[0][1] = -self[0][1];
367 adjugate[1][0] = -self[1][0];
368 adjugate[1][1] = self[0][0];
369 let determinant = self.determinant();
370 (adjugate / determinant, determinant)
371 } else if D == 3 {
372 let mut adjugate = TensorRank2::<D, J, I>::zero();
373 let c_00 = self[1][1] * self[2][2] - self[1][2] * self[2][1];
374 let c_10 = self[1][2] * self[2][0] - self[1][0] * self[2][2];
375 let c_20 = self[1][0] * self[2][1] - self[1][1] * self[2][0];
376 let determinant = self[0][0] * c_00 + self[0][1] * c_10 + self[0][2] * c_20;
377 adjugate[0][0] = c_00;
378 adjugate[0][1] = self[0][2] * self[2][1] - self[0][1] * self[2][2];
379 adjugate[0][2] = self[0][1] * self[1][2] - self[0][2] * self[1][1];
380 adjugate[1][0] = c_10;
381 adjugate[1][1] = self[0][0] * self[2][2] - self[0][2] * self[2][0];
382 adjugate[1][2] = self[0][2] * self[1][0] - self[0][0] * self[1][2];
383 adjugate[2][0] = c_20;
384 adjugate[2][1] = self[0][1] * self[2][0] - self[0][0] * self[2][1];
385 adjugate[2][2] = self[0][0] * self[1][1] - self[0][1] * self[1][0];
386 (adjugate / determinant, determinant)
387 } else if D == 4 {
388 let mut adjugate = TensorRank2::<D, J, I>::zero();
389 let s0 = self[0][0] * self[1][1] - self[0][1] * self[1][0];
390 let s1 = self[0][0] * self[1][2] - self[0][2] * self[1][0];
391 let s2 = self[0][0] * self[1][3] - self[0][3] * self[1][0];
392 let s3 = self[0][1] * self[1][2] - self[0][2] * self[1][1];
393 let s4 = self[0][1] * self[1][3] - self[0][3] * self[1][1];
394 let s5 = self[0][2] * self[1][3] - self[0][3] * self[1][2];
395 let c5 = self[2][2] * self[3][3] - self[2][3] * self[3][2];
396 let c4 = self[2][1] * self[3][3] - self[2][3] * self[3][1];
397 let c3 = self[2][1] * self[3][2] - self[2][2] * self[3][1];
398 let c2 = self[2][0] * self[3][3] - self[2][3] * self[3][0];
399 let c1 = self[2][0] * self[3][2] - self[2][2] * self[3][0];
400 let c0 = self[2][0] * self[3][1] - self[2][1] * self[3][0];
401 let determinant = s0 * c5 - s1 * c4 + s2 * c3 + s3 * c2 - s4 * c1 + s5 * c0;
402 adjugate[0][0] = self[1][1] * c5 - self[1][2] * c4 + self[1][3] * c3;
403 adjugate[0][1] = self[0][2] * c4 - self[0][1] * c5 - self[0][3] * c3;
404 adjugate[0][2] = self[3][1] * s5 - self[3][2] * s4 + self[3][3] * s3;
405 adjugate[0][3] = self[2][2] * s4 - self[2][1] * s5 - self[2][3] * s3;
406 adjugate[1][0] = self[1][2] * c2 - self[1][0] * c5 - self[1][3] * c1;
407 adjugate[1][1] = self[0][0] * c5 - self[0][2] * c2 + self[0][3] * c1;
408 adjugate[1][2] = self[3][2] * s2 - self[3][0] * s5 - self[3][3] * s1;
409 adjugate[1][3] = self[2][0] * s5 - self[2][2] * s2 + self[2][3] * s1;
410 adjugate[2][0] = self[1][0] * c4 - self[1][1] * c2 + self[1][3] * c0;
411 adjugate[2][1] = self[0][1] * c2 - self[0][0] * c4 - self[0][3] * c0;
412 adjugate[2][2] = self[3][0] * s4 - self[3][1] * s2 + self[3][3] * s0;
413 adjugate[2][3] = self[2][1] * s2 - self[2][0] * s4 - self[2][3] * s0;
414 adjugate[3][0] = self[1][1] * c1 - self[1][0] * c3 - self[1][2] * c0;
415 adjugate[3][1] = self[0][0] * c3 - self[0][1] * c1 + self[0][2] * c0;
416 adjugate[3][2] = self[3][1] * s1 - self[3][0] * s3 - self[3][2] * s0;
417 adjugate[3][3] = self[2][0] * s3 - self[2][1] * s1 + self[2][2] * s0;
418 (adjugate / determinant, determinant)
419 } else {
420 panic!()
421 }
422 }
423 pub fn inverse_transpose(&self) -> Self {
425 if D == 2 {
426 let mut adjugate_transpose = TensorRank2::<D, I, J>::zero();
427 adjugate_transpose[0][0] = self[1][1];
428 adjugate_transpose[0][1] = -self[1][0];
429 adjugate_transpose[1][0] = -self[0][1];
430 adjugate_transpose[1][1] = self[0][0];
431 adjugate_transpose / self.determinant()
432 } else if D == 3 {
433 let mut adjugate_transpose = TensorRank2::<D, I, J>::zero();
434 let c_00 = self[1][1] * self[2][2] - self[1][2] * self[2][1];
435 let c_10 = self[1][2] * self[2][0] - self[1][0] * self[2][2];
436 let c_20 = self[1][0] * self[2][1] - self[1][1] * self[2][0];
437 adjugate_transpose[0][0] = c_00;
438 adjugate_transpose[1][0] = self[0][2] * self[2][1] - self[0][1] * self[2][2];
439 adjugate_transpose[2][0] = self[0][1] * self[1][2] - self[0][2] * self[1][1];
440 adjugate_transpose[0][1] = c_10;
441 adjugate_transpose[1][1] = self[0][0] * self[2][2] - self[0][2] * self[2][0];
442 adjugate_transpose[2][1] = self[0][2] * self[1][0] - self[0][0] * self[1][2];
443 adjugate_transpose[0][2] = c_20;
444 adjugate_transpose[1][2] = self[0][1] * self[2][0] - self[0][0] * self[2][1];
445 adjugate_transpose[2][2] = self[0][0] * self[1][1] - self[0][1] * self[1][0];
446 adjugate_transpose / (self[0][0] * c_00 + self[0][1] * c_10 + self[0][2] * c_20)
447 } else if D == 4 {
448 let mut adjugate_transpose = TensorRank2::<D, I, J>::zero();
449 let s0 = self[0][0] * self[1][1] - self[0][1] * self[1][0];
450 let s1 = self[0][0] * self[1][2] - self[0][2] * self[1][0];
451 let s2 = self[0][0] * self[1][3] - self[0][3] * self[1][0];
452 let s3 = self[0][1] * self[1][2] - self[0][2] * self[1][1];
453 let s4 = self[0][1] * self[1][3] - self[0][3] * self[1][1];
454 let s5 = self[0][2] * self[1][3] - self[0][3] * self[1][2];
455 let c5 = self[2][2] * self[3][3] - self[2][3] * self[3][2];
456 let c4 = self[2][1] * self[3][3] - self[2][3] * self[3][1];
457 let c3 = self[2][1] * self[3][2] - self[2][2] * self[3][1];
458 let c2 = self[2][0] * self[3][3] - self[2][3] * self[3][0];
459 let c1 = self[2][0] * self[3][2] - self[2][2] * self[3][0];
460 let c0 = self[2][0] * self[3][1] - self[2][1] * self[3][0];
461 adjugate_transpose[0][0] = self[1][1] * c5 - self[1][2] * c4 + self[1][3] * c3;
462 adjugate_transpose[1][0] = self[0][2] * c4 - self[0][1] * c5 - self[0][3] * c3;
463 adjugate_transpose[2][0] = self[3][1] * s5 - self[3][2] * s4 + self[3][3] * s3;
464 adjugate_transpose[3][0] = self[2][2] * s4 - self[2][1] * s5 - self[2][3] * s3;
465 adjugate_transpose[0][1] = self[1][2] * c2 - self[1][0] * c5 - self[1][3] * c1;
466 adjugate_transpose[1][1] = self[0][0] * c5 - self[0][2] * c2 + self[0][3] * c1;
467 adjugate_transpose[2][1] = self[3][2] * s2 - self[3][0] * s5 - self[3][3] * s1;
468 adjugate_transpose[3][1] = self[2][0] * s5 - self[2][2] * s2 + self[2][3] * s1;
469 adjugate_transpose[0][2] = self[1][0] * c4 - self[1][1] * c2 + self[1][3] * c0;
470 adjugate_transpose[1][2] = self[0][1] * c2 - self[0][0] * c4 - self[0][3] * c0;
471 adjugate_transpose[2][2] = self[3][0] * s4 - self[3][1] * s2 + self[3][3] * s0;
472 adjugate_transpose[3][2] = self[2][1] * s2 - self[2][0] * s4 - self[2][3] * s0;
473 adjugate_transpose[0][3] = self[1][1] * c1 - self[1][0] * c3 - self[1][2] * c0;
474 adjugate_transpose[1][3] = self[0][0] * c3 - self[0][1] * c1 + self[0][2] * c0;
475 adjugate_transpose[2][3] = self[3][1] * s1 - self[3][0] * s3 - self[3][2] * s0;
476 adjugate_transpose[3][3] = self[2][0] * s3 - self[2][1] * s1 + self[2][2] * s0;
477 adjugate_transpose / (s0 * c5 - s1 * c4 + s2 * c3 + s3 * c2 - s4 * c1 + s5 * c0)
478 } else {
479 self.inverse().transpose()
480 }
481 }
482 pub fn inverse_transpose_and_determinant(&self) -> (Self, TensorRank0) {
484 if D == 2 {
485 let mut adjugate_transpose = TensorRank2::<D, I, J>::zero();
486 adjugate_transpose[0][0] = self[1][1];
487 adjugate_transpose[0][1] = -self[1][0];
488 adjugate_transpose[1][0] = -self[0][1];
489 adjugate_transpose[1][1] = self[0][0];
490 let determinant = self.determinant();
491 (adjugate_transpose / determinant, determinant)
492 } else if D == 3 {
493 let mut adjugate_transpose = TensorRank2::<D, I, J>::zero();
494 let c_00 = self[1][1] * self[2][2] - self[1][2] * self[2][1];
495 let c_10 = self[1][2] * self[2][0] - self[1][0] * self[2][2];
496 let c_20 = self[1][0] * self[2][1] - self[1][1] * self[2][0];
497 let determinant = self[0][0] * c_00 + self[0][1] * c_10 + self[0][2] * c_20;
498 adjugate_transpose[0][0] = c_00;
499 adjugate_transpose[1][0] = self[0][2] * self[2][1] - self[0][1] * self[2][2];
500 adjugate_transpose[2][0] = self[0][1] * self[1][2] - self[0][2] * self[1][1];
501 adjugate_transpose[0][1] = c_10;
502 adjugate_transpose[1][1] = self[0][0] * self[2][2] - self[0][2] * self[2][0];
503 adjugate_transpose[2][1] = self[0][2] * self[1][0] - self[0][0] * self[1][2];
504 adjugate_transpose[0][2] = c_20;
505 adjugate_transpose[1][2] = self[0][1] * self[2][0] - self[0][0] * self[2][1];
506 adjugate_transpose[2][2] = self[0][0] * self[1][1] - self[0][1] * self[1][0];
507 (adjugate_transpose / determinant, determinant)
508 } else if D == 4 {
509 let mut adjugate_transpose = TensorRank2::<D, I, J>::zero();
510 let s0 = self[0][0] * self[1][1] - self[0][1] * self[1][0];
511 let s1 = self[0][0] * self[1][2] - self[0][2] * self[1][0];
512 let s2 = self[0][0] * self[1][3] - self[0][3] * self[1][0];
513 let s3 = self[0][1] * self[1][2] - self[0][2] * self[1][1];
514 let s4 = self[0][1] * self[1][3] - self[0][3] * self[1][1];
515 let s5 = self[0][2] * self[1][3] - self[0][3] * self[1][2];
516 let c5 = self[2][2] * self[3][3] - self[2][3] * self[3][2];
517 let c4 = self[2][1] * self[3][3] - self[2][3] * self[3][1];
518 let c3 = self[2][1] * self[3][2] - self[2][2] * self[3][1];
519 let c2 = self[2][0] * self[3][3] - self[2][3] * self[3][0];
520 let c1 = self[2][0] * self[3][2] - self[2][2] * self[3][0];
521 let c0 = self[2][0] * self[3][1] - self[2][1] * self[3][0];
522 let determinant = s0 * c5 - s1 * c4 + s2 * c3 + s3 * c2 - s4 * c1 + s5 * c0;
523 adjugate_transpose[0][0] = self[1][1] * c5 - self[1][2] * c4 + self[1][3] * c3;
524 adjugate_transpose[1][0] = self[0][2] * c4 - self[0][1] * c5 - self[0][3] * c3;
525 adjugate_transpose[2][0] = self[3][1] * s5 - self[3][2] * s4 + self[3][3] * s3;
526 adjugate_transpose[3][0] = self[2][2] * s4 - self[2][1] * s5 - self[2][3] * s3;
527 adjugate_transpose[0][1] = self[1][2] * c2 - self[1][0] * c5 - self[1][3] * c1;
528 adjugate_transpose[1][1] = self[0][0] * c5 - self[0][2] * c2 + self[0][3] * c1;
529 adjugate_transpose[2][1] = self[3][2] * s2 - self[3][0] * s5 - self[3][3] * s1;
530 adjugate_transpose[3][1] = self[2][0] * s5 - self[2][2] * s2 + self[2][3] * s1;
531 adjugate_transpose[0][2] = self[1][0] * c4 - self[1][1] * c2 + self[1][3] * c0;
532 adjugate_transpose[1][2] = self[0][1] * c2 - self[0][0] * c4 - self[0][3] * c0;
533 adjugate_transpose[2][2] = self[3][0] * s4 - self[3][1] * s2 + self[3][3] * s0;
534 adjugate_transpose[3][2] = self[2][1] * s2 - self[2][0] * s4 - self[2][3] * s0;
535 adjugate_transpose[0][3] = self[1][1] * c1 - self[1][0] * c3 - self[1][2] * c0;
536 adjugate_transpose[1][3] = self[0][0] * c3 - self[0][1] * c1 + self[0][2] * c0;
537 adjugate_transpose[2][3] = self[3][1] * s1 - self[3][0] * s3 - self[3][2] * s0;
538 adjugate_transpose[3][3] = self[2][0] * s3 - self[2][1] * s1 + self[2][2] * s0;
539 (adjugate_transpose / determinant, determinant)
540 } else {
541 panic!()
542 }
543 }
544 pub fn lu_decomposition(&self) -> (TensorRank2<D, I, 88>, TensorRank2<D, 88, J>, Vec<usize>) {
546 let n = D;
547 let mut p: Vec<usize> = (0..n).collect();
548 let mut factor;
549 let mut lu = self.clone();
550 let mut max_row;
551 let mut max_val;
552 let mut pivot;
553 for i in 0..n {
554 max_row = i;
555 max_val = lu[max_row][i].abs();
556 for k in i + 1..n {
557 if lu[k][i].abs() > max_val {
558 max_row = k;
559 max_val = lu[max_row][i].abs();
560 }
561 }
562 if max_row != i {
563 lu.0.swap(i, max_row);
564 p.swap(i, max_row);
565 }
566 pivot = lu[i][i];
567 if pivot.abs() < ABS_TOL {
568 panic!("LU decomposition failed (zero pivot).")
569 }
570 for j in i + 1..n {
571 if lu[j][i] != 0.0 {
572 lu[j][i] /= pivot;
573 factor = lu[j][i];
574 for k in i + 1..n {
575 lu[j][k] -= factor * lu[i][k];
576 }
577 }
578 }
579 }
580 let mut l = TensorRank2::identity();
581 for i in 0..D {
582 for j in 0..i {
583 l[i][j] = lu[i][j]
584 }
585 }
586 let mut u = TensorRank2::zero();
587 for i in 0..D {
588 for j in i..D {
589 u[i][j] = lu[i][j]
590 }
591 }
592 (l, u, p)
593 }
594 pub fn lu_decomposition_inverse(
596 &self,
597 ) -> (TensorRank2<D, I, 88>, TensorRank2<D, 88, J>, Vec<usize>) {
598 let (mut tensor_l, mut tensor_u, p) = self.lu_decomposition();
599 let mut sum;
600 for i in 0..D {
601 tensor_l[i][i] = 1.0 / tensor_l[i][i];
602 for j in 0..i {
603 sum = 0.0;
604 for k in j..i {
605 sum += tensor_l[i][k] * tensor_l[k][j];
606 }
607 tensor_l[i][j] = -sum * tensor_l[i][i];
608 }
609 }
610 for i in 0..D {
611 tensor_u[i][i] = 1.0 / tensor_u[i][i];
612 for j in 0..i {
613 sum = 0.0;
614 for k in j..i {
615 sum += tensor_u[j][k] * tensor_u[k][i];
616 }
617 tensor_u[j][i] = -sum * tensor_u[i][i];
618 }
619 }
620 (tensor_l, tensor_u, p)
621 }
622}
623
624impl<const I: usize> TensorRank2<3, I, I> {
625 pub fn logm(&self) -> Self {
627 if self.is_diagonal() {
628 let mut logm = TensorRank2::zero();
629 logm.iter_mut()
630 .enumerate()
631 .zip(self.iter())
632 .for_each(|((i, logm_i), self_i)| logm_i[i] = self_i[i].ln());
633 logm
634 } else {
635 let tensor = self - &TensorRank2::identity();
636 let norm = tensor.norm();
637 if norm < 1e-2 {
638 let num_terms = if norm < 1e-4 {
639 2
640 } else if norm < 1e-3 {
641 3
642 } else {
643 5
644 };
645 let mut logm = tensor.clone();
646 let mut power = tensor.clone();
647 (2..=num_terms).for_each(|k| {
648 power *= &tensor;
649 logm += &power / (if k % 2 == 0 { -1.0 } else { 1.0 } / k as f64);
650 });
651 logm
652 } else if self.is_symmetric() {
653 let mut eigenvalues = solve_cubic_symmetric(self.invariants());
654 if eigenvalues.iter().any(|eigenvalue| eigenvalue <= &0.0) {
655 panic!("Symmetric matrix has a non-positive eigenvalue")
656 }
657 let eigenvectors = find_orthonormal_eigenvectors(&eigenvalues, self);
658 eigenvalues
659 .iter_mut()
660 .for_each(|eigenvalue| *eigenvalue = eigenvalue.ln());
661 reconstruct_symmetric(eigenvalues, eigenvectors)
662 } else {
663 panic!("Matrix logarithm only implemented for symmetric cases")
664 }
665 }
666 }
667 pub fn dlogm(&self) -> TensorRank4<3, I, I, I, I> {
669 if self.is_diagonal() {
670 let mut dlogm = TensorRank4::zero();
671 dlogm.iter_mut().enumerate().for_each(|(i, dlogm_i)| {
672 dlogm_i.iter_mut().enumerate().for_each(|(j, dlogm_ij)| {
673 dlogm_ij.iter_mut().enumerate().for_each(|(k, dlogm_ijk)| {
674 dlogm_ijk
675 .iter_mut()
676 .enumerate()
677 .filter(|(l, _)| i == k && &j == l)
678 .for_each(|(_, dlogm_ijkl)| {
679 *dlogm_ijkl = if assert_eq_within_tols(&self[i][i], &self[j][j])
680 .is_ok()
681 {
682 1.0 / self[j][j]
683 } else {
684 (self[i][i].ln() - self[j][j].ln()) / (self[i][i] - self[j][j])
685 }
686 })
687 })
688 })
689 });
690 dlogm
691 } else if self.is_symmetric() {
692 let eigenvalues = solve_cubic_symmetric(self.invariants());
693 if eigenvalues.iter().any(|eigenvalue| eigenvalue <= &0.0) {
694 panic!("Symmetric matrix has a non-positive eigenvalue")
695 }
696 let divided_difference: Self = eigenvalues
697 .iter()
698 .map(|eigenvalue_i| {
699 eigenvalues
700 .iter()
701 .map(|eigenvalue_j| {
702 if assert_eq_within_tols(eigenvalue_i, eigenvalue_j).is_ok() {
703 1.0 / eigenvalue_j
704 } else {
705 (eigenvalue_i.ln() - eigenvalue_j.ln())
706 / (eigenvalue_i - eigenvalue_j)
707 }
708 })
709 .collect()
710 })
711 .collect();
712 let eigenvectors = find_orthonormal_eigenvectors(&eigenvalues, self).transpose();
713 eigenvectors.iter().map(|eigenvector_i|
714 eigenvectors.iter().map(|eigenvector_j|
715 eigenvectors.iter().map(|eigenvector_k|
716 eigenvectors.iter().map(|eigenvector_l|
717 eigenvector_i.iter().zip(eigenvector_k.iter().zip(divided_difference.iter())).map(|(eigenvector_ip, (eigenvector_kp, divided_difference_p))|
718 eigenvector_j.iter().zip(eigenvector_l.iter().zip(divided_difference_p.iter())).map(|(eigenvector_jq, (eigenvector_lq, divided_difference_pq))|
719 eigenvector_ip * eigenvector_kp * divided_difference_pq * eigenvector_jq * eigenvector_lq
720 ).sum::<TensorRank0>()
721 ).sum()
722 ).collect()
723 ).collect()
724 ).collect()
725 ).collect()
726 } else {
727 panic!("Matrix logarithm only implemented for symmetric cases")
728 }
729 }
730 pub fn invariants(&self) -> TensorRank0List<3> {
732 let trace = self.trace();
733 TensorRank0List::new([
734 trace,
735 0.5 * (trace.powi(2) - self.squared_trace()),
736 self.determinant(),
737 ])
738 }
739}
740
741fn solve_cubic_symmetric(coefficients: TensorRank0List<3>) -> TensorRank0List<3> {
742 let c2 = coefficients[0];
743 let c1 = coefficients[1];
744 let c0 = coefficients[2];
745 let p = c1 - c2 * c2 / 3.0;
746 let q = -(2.0 * c2.powi(3) - 9.0 * c2 * c1 + 27.0 * c0) / 27.0;
747 if p.abs() < ABS_TOL {
748 let t = (-q).cbrt();
749 let lambda = t + c2 / 3.0;
750 return TensorRank0List::new([lambda; _]);
751 }
752 let discriminant = -4.0 * p * p * p - 27.0 * q * q;
753 if discriminant >= ABS_TOL {
754 let sqrt_term = (-p / 3.0).sqrt();
755 let cos_arg = 3.0 * q / (2.0 * p * (-p / 3.0).sqrt());
756 let cos_arg = cos_arg.clamp(-1.0, 1.0);
757 let theta = cos_arg.acos();
758 let mut lambdas = [
759 2.0 * sqrt_term * (theta / 3.0).cos() + c2 / 3.0,
760 2.0 * sqrt_term * ((theta + TAU) / 3.0).cos() + c2 / 3.0,
761 2.0 * sqrt_term * ((theta + 2.0 * TAU) / 3.0).cos() + c2 / 3.0,
762 ];
763 lambdas.sort_by(|a, b| b.partial_cmp(a).unwrap());
764 TensorRank0List::new(lambdas)
765 } else {
766 panic!("Symmetric matrix produced complex eigenvalues");
767 }
768}
769
770fn find_orthonormal_eigenvectors<const I: usize>(
771 eigenvalues: &TensorRank0List<3>,
772 tensor: &TensorRank2<3, I, I>,
773) -> TensorRank2<3, I, I> {
774 let mut eigenvectors = eigenvalues
775 .iter()
776 .map(|eigenvalue| eigenvector_symmetric(eigenvalue, tensor))
777 .collect::<TensorRank2<3, I, I>>();
778 eigenvectors[0].normalize();
779 let proj1 = &eigenvectors[1] * &eigenvectors[0];
780 for i in 0..3 {
781 eigenvectors[1][i] -= proj1 * eigenvectors[0][i];
782 }
783 eigenvectors[1].normalize();
784 eigenvectors[2] = eigenvectors[0].cross(&eigenvectors[1]);
785 eigenvectors
786}
787
788fn eigenvector_symmetric<const I: usize>(
789 eigenvalue: &TensorRank0,
790 tensor: &TensorRank2<3, I, I>,
791) -> TensorRank1<3, I> {
792 let m = tensor - TensorRank2::identity() * eigenvalue;
793 let mut pivot_row = 0;
794 m.iter().enumerate().for_each(|(i, m_i)| {
795 if m_i[i].abs() < m[pivot_row][pivot_row].abs() {
796 pivot_row = i;
797 }
798 });
799 if pivot_row == 0 {
800 m[1].cross(&m[2])
801 } else if pivot_row == 1 {
802 m[0].cross(&m[2])
803 } else {
804 m[0].cross(&m[1])
805 }
806 .normalized()
807}
808
809fn reconstruct_symmetric<const I: usize>(
810 eigenvalues: TensorRank0List<3>,
811 eigenvectors: TensorRank2<3, I, I>,
812) -> TensorRank2<3, I, I> {
813 let mut tensor = TensorRank2::zero();
814 eigenvalues
815 .iter()
816 .zip(eigenvectors.iter())
817 .for_each(|(eigenvalue, eigenvector)| {
818 tensor
819 .iter_mut()
820 .zip(eigenvector.iter())
821 .for_each(|(tensor_i, eigenvector_i)| {
822 tensor_i.iter_mut().zip(eigenvector.iter()).for_each(
823 |(tensor_ij, eigenvector_j)| {
824 *tensor_ij += eigenvalue * eigenvector_i * eigenvector_j
825 },
826 )
827 })
828 });
829 tensor
830}
831
832impl<const D: usize, const I: usize, const J: usize> Hessian for TensorRank2<D, I, J> {
833 fn fill_into(self, square_matrix: &mut SquareMatrix) {
834 self.into_iter().enumerate().for_each(|(i, self_i)| {
835 self_i
836 .into_iter()
837 .enumerate()
838 .for_each(|(j, self_ij)| square_matrix[i][j] = self_ij)
839 })
840 }
841}
842
843impl<const D: usize, const I: usize, const J: usize> Rank2 for TensorRank2<D, I, J> {
844 type Transpose = TensorRank2<D, J, I>;
845 fn deviatoric(&self) -> Self {
846 Self::identity() * (self.trace() / -(D as TensorRank0)) + self
847 }
848 fn deviatoric_and_trace(&self) -> (Self, TensorRank0) {
849 let trace = self.trace();
850 (
851 Self::identity() * (trace / -(D as TensorRank0)) + self,
852 trace,
853 )
854 }
855 fn is_diagonal(&self) -> bool {
856 self.iter()
857 .enumerate()
858 .map(|(i, self_i)| {
859 self_i
860 .iter()
861 .enumerate()
862 .map(|(j, self_ij)| (self_ij.abs() < ABS_TOL) as u8 * (i != j) as u8)
863 .sum::<u8>()
864 })
865 .sum::<u8>()
866 == (D.pow(2) - D) as u8
867 }
868 fn is_identity(&self) -> bool {
869 self.iter().enumerate().all(|(i, self_i)| {
870 self_i
871 .iter()
872 .enumerate()
873 .all(|(j, self_ij)| self_ij == &((i == j) as u8 as TensorRank0))
874 })
875 }
876 fn is_symmetric(&self) -> bool {
877 self.iter().enumerate().all(|(i, self_i)| {
878 self_i
879 .iter()
880 .zip(self.iter())
881 .all(|(self_ij, self_j)| self_ij == &self_j[i])
882 })
883 }
884 fn squared_trace(&self) -> TensorRank0 {
885 self.iter()
886 .enumerate()
887 .map(|(i, self_i)| {
888 self_i
889 .iter()
890 .zip(self.iter())
891 .map(|(self_ij, self_j)| self_ij * self_j[i])
892 .sum::<TensorRank0>()
893 })
894 .sum()
895 }
896 fn trace(&self) -> TensorRank0 {
897 self.iter().enumerate().map(|(i, self_i)| self_i[i]).sum()
898 }
899 fn transpose(&self) -> Self::Transpose {
900 (0..D)
901 .map(|i| (0..D).map(|j| self[j][i]).collect())
902 .collect()
904 }
905}
906
907impl<const D: usize, const I: usize, const J: usize> Tensor for TensorRank2<D, I, J> {
908 type Item = TensorRank1<D, J>;
909 fn iter(&self) -> impl Iterator<Item = &Self::Item> {
910 self.0.iter()
911 }
912 fn iter_mut(&mut self) -> impl Iterator<Item = &mut Self::Item> {
913 self.0.iter_mut()
914 }
915 fn norm_inf(&self) -> TensorRank0 {
916 self.iter()
917 .map(|tensor_rank_1| {
918 tensor_rank_1
919 .iter()
920 .fold(0.0, |acc, entry| entry.abs().max(acc))
921 })
922 .reduce(TensorRank0::max)
923 .unwrap()
924 }
925 fn num_entries(&self) -> usize {
926 D * D
927 }
928}
929
930impl<const D: usize, const I: usize, const J: usize> IntoIterator for TensorRank2<D, I, J> {
931 type Item = TensorRank1<D, J>;
932 type IntoIter = std::array::IntoIter<Self::Item, D>;
933 fn into_iter(self) -> Self::IntoIter {
934 self.0.into_iter()
935 }
936}
937
938impl<const D: usize, const I: usize, const J: usize> TensorArray for TensorRank2<D, I, J> {
939 type Array = [[TensorRank0; D]; D];
940 type Item = TensorRank1<D, J>;
941 fn as_array(&self) -> Self::Array {
942 let mut array = [[0.0; D]; D];
943 array
944 .iter_mut()
945 .zip(self.iter())
946 .for_each(|(entry, tensor_rank_1)| *entry = tensor_rank_1.as_array());
947 array
948 }
949 fn identity() -> Self {
950 (0..D)
951 .map(|i| (0..D).map(|j| ((i == j) as u8) as TensorRank0).collect())
952 .collect()
953 }
954 fn new(array: Self::Array) -> Self {
955 array.into_iter().map(Self::Item::new).collect()
956 }
957 fn zero() -> Self {
958 Self(from_fn(|_| Self::Item::zero()))
959 }
960}
961
962impl<const D: usize, const I: usize, const J: usize> Solution for TensorRank2<D, I, J> {
963 fn decrement_from(&mut self, other: &Vector) {
964 self.iter_mut()
965 .flat_map(|x| x.iter_mut())
966 .zip(other.iter())
967 .for_each(|(self_i, vector_i)| *self_i -= vector_i)
968 }
969 fn decrement_from_chained(&mut self, other: &mut Vector, vector: Vector) {
970 self.iter_mut()
971 .flat_map(|x| x.iter_mut())
972 .chain(other.iter_mut())
973 .zip(vector)
974 .for_each(|(entry_i, vector_i)| *entry_i -= vector_i)
975 }
976}
977
978impl<const D: usize, const I: usize, const J: usize> Jacobian for TensorRank2<D, I, J> {
979 fn fill_into(self, vector: &mut Vector) {
980 self.into_iter()
981 .flatten()
982 .zip(vector.iter_mut())
983 .for_each(|(self_i, vector_i)| *vector_i = self_i)
984 }
985 fn fill_into_chained(self, other: Vector, vector: &mut Vector) {
986 self.into_iter()
987 .flatten()
988 .chain(other)
989 .zip(vector.iter_mut())
990 .for_each(|(self_i, vector_i)| *vector_i = self_i)
991 }
992}
993
994impl<const D: usize, const I: usize, const J: usize> Sub<Vector> for TensorRank2<D, I, J> {
995 type Output = Self;
996 fn sub(mut self, vector: Vector) -> Self::Output {
997 self.iter_mut().enumerate().for_each(|(i, self_i)| {
998 self_i
999 .iter_mut()
1000 .enumerate()
1001 .for_each(|(j, self_ij)| *self_ij -= vector[D * i + j])
1002 });
1003 self
1004 }
1005}
1006
1007impl<const D: usize, const I: usize, const J: usize> Sub<&Vector> for TensorRank2<D, I, J> {
1008 type Output = Self;
1009 fn sub(mut self, vector: &Vector) -> Self::Output {
1010 self.iter_mut().enumerate().for_each(|(i, self_i)| {
1011 self_i
1012 .iter_mut()
1013 .enumerate()
1014 .for_each(|(j, self_ij)| *self_ij -= vector[D * i + j])
1015 });
1016 self
1017 }
1018}
1019
1020impl<const D: usize, const I: usize, const J: usize, const K: usize, const L: usize>
1021 From<TensorRank4<D, I, J, K, L>> for TensorRank2<9, 88, 99>
1022{
1023 fn from(tensor_rank_4: TensorRank4<D, I, J, K, L>) -> Self {
1024 assert_eq!(D, 3);
1025 tensor_rank_4
1026 .into_iter()
1027 .flatten()
1028 .map(|entry_ij| entry_ij.into_iter().flatten().collect())
1029 .collect()
1030 }
1031}
1032
1033impl<const D: usize, const I: usize, const J: usize, const K: usize, const L: usize>
1034 From<&TensorRank4<D, I, J, K, L>> for TensorRank2<9, 88, 99>
1035{
1036 fn from(tensor_rank_4: &TensorRank4<D, I, J, K, L>) -> Self {
1037 assert_eq!(D, 3);
1038 tensor_rank_4
1039 .clone()
1040 .into_iter()
1041 .flatten()
1042 .map(|entry_ij| entry_ij.into_iter().flatten().collect())
1043 .collect()
1044 }
1045}
1046
1047impl<const I: usize> From<TensorRank2<3, I, 1>> for TensorRank2<3, I, 0> {
1048 fn from(tensor_rank_2: TensorRank2<3, I, 1>) -> Self {
1049 unsafe { transmute::<TensorRank2<3, I, 1>, TensorRank2<3, I, 0>>(tensor_rank_2) }
1050 }
1051}
1052
1053impl<const I: usize> From<TensorRank2<3, I, 2>> for TensorRank2<3, I, 0> {
1054 fn from(tensor_rank_2: TensorRank2<3, I, 2>) -> Self {
1055 unsafe { transmute::<TensorRank2<3, I, 2>, TensorRank2<3, I, 0>>(tensor_rank_2) }
1056 }
1057}
1058
1059impl<const J: usize> From<TensorRank2<3, 0, J>> for TensorRank2<3, 1, J> {
1060 fn from(tensor_rank_2: TensorRank2<3, 0, J>) -> Self {
1061 unsafe { transmute::<TensorRank2<3, 0, J>, TensorRank2<3, 1, J>>(tensor_rank_2) }
1062 }
1063}
1064
1065impl<const J: usize> From<TensorRank2<3, 1, J>> for TensorRank2<3, 0, J> {
1066 fn from(tensor_rank_2: TensorRank2<3, 1, J>) -> Self {
1067 unsafe { transmute::<TensorRank2<3, 1, J>, TensorRank2<3, 0, J>>(tensor_rank_2) }
1068 }
1069}
1070
1071impl From<TensorRank2<3, 0, 0>> for TensorRank2<3, 1, 1> {
1072 fn from(tensor_rank_2: TensorRank2<3, 0, 0>) -> Self {
1073 unsafe { transmute::<TensorRank2<3, 0, 0>, TensorRank2<3, 1, 1>>(tensor_rank_2) }
1074 }
1075}
1076
1077impl<const D: usize, const I: usize, const J: usize> From<Vector> for TensorRank2<D, I, J> {
1078 fn from(_vector: Vector) -> Self {
1079 unimplemented!()
1080 }
1081}
1082
1083impl<const D: usize, const I: usize, const J: usize> FromIterator<TensorRank1<D, J>>
1084 for TensorRank2<D, I, J>
1085{
1086 fn from_iter<Ii: IntoIterator<Item = TensorRank1<D, J>>>(into_iterator: Ii) -> Self {
1087 let mut tensor_rank_2 = Self::zero();
1088 tensor_rank_2
1089 .iter_mut()
1090 .zip(into_iterator)
1091 .for_each(|(tensor_rank_2_i, value_i)| *tensor_rank_2_i = value_i);
1092 tensor_rank_2
1093 }
1094}
1095
1096impl<const D: usize, const I: usize, const J: usize> Index<usize> for TensorRank2<D, I, J> {
1097 type Output = TensorRank1<D, J>;
1098 fn index(&self, index: usize) -> &Self::Output {
1099 &self.0[index]
1100 }
1101}
1102
1103impl<const D: usize, const I: usize, const J: usize> IndexMut<usize> for TensorRank2<D, I, J> {
1104 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
1105 &mut self.0[index]
1106 }
1107}
1108
1109impl<const D: usize, const I: usize, const J: usize> Sum for TensorRank2<D, I, J> {
1110 fn sum<Ii>(iter: Ii) -> Self
1111 where
1112 Ii: Iterator<Item = Self>,
1113 {
1114 let mut output = Self::zero();
1115 iter.for_each(|item| output += item);
1116 output
1117 }
1118}
1119
1120impl<const D: usize, const I: usize, const J: usize> Div<TensorRank0> for TensorRank2<D, I, J> {
1121 type Output = Self;
1122 fn div(mut self, tensor_rank_0: TensorRank0) -> Self::Output {
1123 self /= &tensor_rank_0;
1124 self
1125 }
1126}
1127
1128impl<const D: usize, const I: usize, const J: usize> Div<TensorRank0> for &TensorRank2<D, I, J> {
1129 type Output = TensorRank2<D, I, J>;
1130 fn div(self, tensor_rank_0: TensorRank0) -> Self::Output {
1131 self.iter().map(|self_i| self_i / tensor_rank_0).collect()
1132 }
1133}
1134
1135impl<const D: usize, const I: usize, const J: usize> Div<&TensorRank0> for TensorRank2<D, I, J> {
1136 type Output = Self;
1137 fn div(mut self, tensor_rank_0: &TensorRank0) -> Self::Output {
1138 self /= tensor_rank_0;
1139 self
1140 }
1141}
1142
1143impl<const D: usize, const I: usize, const J: usize> Div<&TensorRank0> for &TensorRank2<D, I, J> {
1144 type Output = TensorRank2<D, I, J>;
1145 fn div(self, tensor_rank_0: &TensorRank0) -> Self::Output {
1146 self.iter().map(|self_i| self_i / tensor_rank_0).collect()
1147 }
1148}
1149
1150impl<const D: usize, const I: usize, const J: usize> DivAssign<TensorRank0>
1151 for TensorRank2<D, I, J>
1152{
1153 fn div_assign(&mut self, tensor_rank_0: TensorRank0) {
1154 self.iter_mut().for_each(|self_i| *self_i /= &tensor_rank_0);
1155 }
1156}
1157
1158impl<const D: usize, const I: usize, const J: usize> DivAssign<&TensorRank0>
1159 for TensorRank2<D, I, J>
1160{
1161 fn div_assign(&mut self, tensor_rank_0: &TensorRank0) {
1162 self.iter_mut().for_each(|self_i| *self_i /= tensor_rank_0);
1163 }
1164}
1165
1166impl<const D: usize, const I: usize, const J: usize> Mul<TensorRank0> for TensorRank2<D, I, J> {
1167 type Output = Self;
1168 fn mul(mut self, tensor_rank_0: TensorRank0) -> Self::Output {
1169 self *= &tensor_rank_0;
1170 self
1171 }
1172}
1173
1174impl<const D: usize, const I: usize, const J: usize> Mul<TensorRank0> for &TensorRank2<D, I, J> {
1175 type Output = TensorRank2<D, I, J>;
1176 fn mul(self, tensor_rank_0: TensorRank0) -> Self::Output {
1177 self.iter().map(|self_i| self_i * tensor_rank_0).collect()
1178 }
1179}
1180
1181impl<const D: usize, const I: usize, const J: usize> Mul<&TensorRank0> for TensorRank2<D, I, J> {
1182 type Output = Self;
1183 fn mul(mut self, tensor_rank_0: &TensorRank0) -> Self::Output {
1184 self *= tensor_rank_0;
1185 self
1186 }
1187}
1188
1189impl<const D: usize, const I: usize, const J: usize> Mul<&TensorRank0> for &TensorRank2<D, I, J> {
1190 type Output = TensorRank2<D, I, J>;
1191 fn mul(self, tensor_rank_0: &TensorRank0) -> Self::Output {
1192 self.iter().map(|self_i| self_i * tensor_rank_0).collect()
1193 }
1194}
1195
1196impl<const D: usize, const I: usize, const J: usize> MulAssign<TensorRank0>
1197 for TensorRank2<D, I, J>
1198{
1199 fn mul_assign(&mut self, tensor_rank_0: TensorRank0) {
1200 self.iter_mut().for_each(|self_i| *self_i *= &tensor_rank_0);
1201 }
1202}
1203
1204impl<const D: usize, const I: usize, const J: usize> MulAssign<&TensorRank0>
1205 for TensorRank2<D, I, J>
1206{
1207 fn mul_assign(&mut self, tensor_rank_0: &TensorRank0) {
1208 self.iter_mut().for_each(|self_i| *self_i *= tensor_rank_0);
1209 }
1210}
1211
1212impl<const D: usize, const I: usize, const J: usize> Mul<TensorRank1<D, J>>
1213 for TensorRank2<D, I, J>
1214{
1215 type Output = TensorRank1<D, I>;
1216 fn mul(self, tensor_rank_1: TensorRank1<D, J>) -> Self::Output {
1217 self.iter().map(|self_i| self_i * &tensor_rank_1).collect()
1218 }
1219}
1220
1221impl<const D: usize, const I: usize, const J: usize> Mul<&TensorRank1<D, J>>
1222 for TensorRank2<D, I, J>
1223{
1224 type Output = TensorRank1<D, I>;
1225 fn mul(self, tensor_rank_1: &TensorRank1<D, J>) -> Self::Output {
1226 self.iter().map(|self_i| self_i * tensor_rank_1).collect()
1227 }
1228}
1229
1230impl<const D: usize, const I: usize, const J: usize> Mul<TensorRank1<D, J>>
1231 for &TensorRank2<D, I, J>
1232{
1233 type Output = TensorRank1<D, I>;
1234 fn mul(self, tensor_rank_1: TensorRank1<D, J>) -> Self::Output {
1235 self.iter().map(|self_i| self_i * &tensor_rank_1).collect()
1236 }
1237}
1238
1239impl<const D: usize, const I: usize, const J: usize> Mul<&TensorRank1<D, J>>
1240 for &TensorRank2<D, I, J>
1241{
1242 type Output = TensorRank1<D, I>;
1243 fn mul(self, tensor_rank_1: &TensorRank1<D, J>) -> Self::Output {
1244 self.iter().map(|self_i| self_i * tensor_rank_1).collect()
1245 }
1246}
1247
1248impl<const D: usize, const I: usize, const J: usize> Add for TensorRank2<D, I, J> {
1249 type Output = Self;
1250 fn add(mut self, tensor_rank_2: Self) -> Self::Output {
1251 self += tensor_rank_2;
1252 self
1253 }
1254}
1255
1256impl<const D: usize, const I: usize, const J: usize> Add<&Self> for TensorRank2<D, I, J> {
1257 type Output = Self;
1258 fn add(mut self, tensor_rank_2: &Self) -> Self::Output {
1259 self += tensor_rank_2;
1260 self
1261 }
1262}
1263
1264impl<const D: usize, const I: usize, const J: usize> Add<TensorRank2<D, I, J>>
1265 for &TensorRank2<D, I, J>
1266{
1267 type Output = TensorRank2<D, I, J>;
1268 fn add(self, mut tensor_rank_2: TensorRank2<D, I, J>) -> Self::Output {
1269 tensor_rank_2 += self;
1270 tensor_rank_2
1271 }
1272}
1273
1274impl<const D: usize, const I: usize, const J: usize> AddAssign for TensorRank2<D, I, J> {
1275 fn add_assign(&mut self, tensor_rank_2: Self) {
1276 self.iter_mut()
1277 .zip(tensor_rank_2.iter())
1278 .for_each(|(self_i, tensor_rank_2_i)| *self_i += tensor_rank_2_i);
1279 }
1280}
1281
1282impl<const D: usize, const I: usize, const J: usize> AddAssign<&Self> for TensorRank2<D, I, J> {
1283 fn add_assign(&mut self, tensor_rank_2: &Self) {
1284 self.iter_mut()
1285 .zip(tensor_rank_2.iter())
1286 .for_each(|(self_i, tensor_rank_2_i)| *self_i += tensor_rank_2_i);
1287 }
1288}
1289
1290impl<const D: usize, const I: usize, const J: usize, const K: usize> Mul<TensorRank2<D, J, K>>
1291 for TensorRank2<D, I, J>
1292{
1293 type Output = TensorRank2<D, I, K>;
1294 fn mul(self, tensor_rank_2: TensorRank2<D, J, K>) -> Self::Output {
1295 self.iter()
1296 .map(|self_i| {
1297 self_i
1298 .iter()
1299 .zip(tensor_rank_2.iter())
1300 .map(|(self_ij, tensor_rank_2_j)| tensor_rank_2_j * self_ij)
1301 .sum()
1302 })
1303 .collect()
1304 }
1305}
1306
1307impl<const D: usize, const I: usize, const J: usize, const K: usize> Mul<&TensorRank2<D, J, K>>
1308 for TensorRank2<D, I, J>
1309{
1310 type Output = TensorRank2<D, I, K>;
1311 fn mul(self, tensor_rank_2: &TensorRank2<D, J, K>) -> Self::Output {
1312 self.iter()
1313 .map(|self_i| {
1314 self_i
1315 .iter()
1316 .zip(tensor_rank_2.iter())
1317 .map(|(self_ij, tensor_rank_2_j)| tensor_rank_2_j * self_ij)
1318 .sum()
1319 })
1320 .collect()
1321 }
1322}
1323
1324impl<const D: usize, const I: usize, const J: usize, const K: usize> Mul<TensorRank2<D, J, K>>
1325 for &TensorRank2<D, I, J>
1326{
1327 type Output = TensorRank2<D, I, K>;
1328 fn mul(self, tensor_rank_2: TensorRank2<D, J, K>) -> Self::Output {
1329 self.iter()
1330 .map(|self_i| {
1331 self_i
1332 .iter()
1333 .zip(tensor_rank_2.iter())
1334 .map(|(self_ij, tensor_rank_2_j)| tensor_rank_2_j * self_ij)
1335 .sum()
1336 })
1337 .collect()
1338 }
1339}
1340
1341impl<const D: usize, const I: usize, const J: usize, const K: usize> Mul<&TensorRank2<D, J, K>>
1342 for &TensorRank2<D, I, J>
1343{
1344 type Output = TensorRank2<D, I, K>;
1345 fn mul(self, tensor_rank_2: &TensorRank2<D, J, K>) -> Self::Output {
1346 self.iter()
1347 .map(|self_i| {
1348 self_i
1349 .iter()
1350 .zip(tensor_rank_2.iter())
1351 .map(|(self_ij, tensor_rank_2_j)| tensor_rank_2_j * self_ij)
1352 .sum()
1353 })
1354 .collect()
1355 }
1356}
1357
1358impl<const D: usize, const I: usize, const J: usize> MulAssign<TensorRank2<D, J, J>>
1359 for TensorRank2<D, I, J>
1360{
1361 fn mul_assign(&mut self, tensor_rank_2: TensorRank2<D, J, J>) {
1362 *self = &*self * tensor_rank_2
1363 }
1364}
1365
1366impl<const D: usize, const I: usize, const J: usize> MulAssign<&TensorRank2<D, J, J>>
1367 for TensorRank2<D, I, J>
1368{
1369 fn mul_assign(&mut self, tensor_rank_2: &TensorRank2<D, J, J>) {
1370 *self = &*self * tensor_rank_2
1371 }
1372}
1373
1374impl<const D: usize, const I: usize, const J: usize> Sub for TensorRank2<D, I, J> {
1375 type Output = Self;
1376 fn sub(mut self, tensor_rank_2: Self) -> Self::Output {
1377 self -= tensor_rank_2;
1378 self
1379 }
1380}
1381
1382impl<const D: usize, const I: usize, const J: usize> Sub<&Self> for TensorRank2<D, I, J> {
1383 type Output = Self;
1384 fn sub(mut self, tensor_rank_2: &Self) -> Self::Output {
1385 self -= tensor_rank_2;
1386 self
1387 }
1388}
1389
1390impl<const D: usize, const I: usize, const J: usize> Sub<TensorRank2<D, I, J>>
1391 for &TensorRank2<D, I, J>
1392{
1393 type Output = TensorRank2<D, I, J>;
1394 fn sub(self, tensor_rank_2: TensorRank2<D, I, J>) -> Self::Output {
1395 let mut output = self.clone();
1396 output -= tensor_rank_2;
1397 output
1398 }
1399}
1400
1401impl<const D: usize, const I: usize, const J: usize> Sub for &TensorRank2<D, I, J> {
1402 type Output = TensorRank2<D, I, J>;
1403 fn sub(self, tensor_rank_2: Self) -> Self::Output {
1404 let mut output = self.clone();
1405 output -= tensor_rank_2;
1406 output
1407 }
1408}
1409
1410impl<const D: usize, const I: usize, const J: usize> SubAssign for TensorRank2<D, I, J> {
1411 fn sub_assign(&mut self, tensor_rank_2: Self) {
1412 self.iter_mut()
1413 .zip(tensor_rank_2.iter())
1414 .for_each(|(self_i, tensor_rank_2_i)| *self_i -= tensor_rank_2_i);
1415 }
1416}
1417
1418impl<const D: usize, const I: usize, const J: usize> SubAssign<&Self> for TensorRank2<D, I, J> {
1419 fn sub_assign(&mut self, tensor_rank_2: &Self) {
1420 self.iter_mut()
1421 .zip(tensor_rank_2.iter())
1422 .for_each(|(self_i, tensor_rank_2_i)| *self_i -= tensor_rank_2_i);
1423 }
1424}
1425
1426impl<const D: usize, const I: usize, const J: usize, const W: usize> Mul<TensorRank1List<D, J, W>>
1427 for TensorRank2<D, I, J>
1428{
1429 type Output = TensorRank1List<D, I, W>;
1430 fn mul(self, tensor_rank_1_list: TensorRank1List<D, J, W>) -> Self::Output {
1431 tensor_rank_1_list
1432 .iter()
1433 .map(|tensor_rank_1| &self * tensor_rank_1)
1434 .collect()
1435 }
1436}
1437
1438impl<const D: usize, const I: usize, const J: usize, const W: usize> Mul<&TensorRank1List<D, J, W>>
1439 for TensorRank2<D, I, J>
1440{
1441 type Output = TensorRank1List<D, I, W>;
1442 fn mul(self, tensor_rank_1_list: &TensorRank1List<D, J, W>) -> Self::Output {
1443 tensor_rank_1_list
1444 .iter()
1445 .map(|tensor_rank_1| &self * tensor_rank_1)
1446 .collect()
1447 }
1448}
1449
1450impl<const D: usize, const I: usize, const J: usize, const W: usize> Mul<TensorRank1List<D, J, W>>
1451 for &TensorRank2<D, I, J>
1452{
1453 type Output = TensorRank1List<D, I, W>;
1454 fn mul(self, tensor_rank_1_list: TensorRank1List<D, J, W>) -> Self::Output {
1455 tensor_rank_1_list
1456 .iter()
1457 .map(|tensor_rank_1| self * tensor_rank_1)
1458 .collect()
1459 }
1460}
1461
1462impl<const D: usize, const I: usize, const J: usize, const W: usize> Mul<&TensorRank1List<D, J, W>>
1463 for &TensorRank2<D, I, J>
1464{
1465 type Output = TensorRank1List<D, I, W>;
1466 fn mul(self, tensor_rank_1_list: &TensorRank1List<D, J, W>) -> Self::Output {
1467 tensor_rank_1_list
1468 .iter()
1469 .map(|tensor_rank_1| self * tensor_rank_1)
1470 .collect()
1471 }
1472}
1473
1474impl<const D: usize, const I: usize, const J: usize> Mul<TensorRank1Vec<D, J>>
1475 for TensorRank2<D, I, J>
1476{
1477 type Output = TensorRank1Vec<D, I>;
1478 fn mul(self, tensor_rank_1_vec: TensorRank1Vec<D, J>) -> Self::Output {
1479 tensor_rank_1_vec
1480 .iter()
1481 .map(|tensor_rank_1| &self * tensor_rank_1)
1482 .collect()
1483 }
1484}
1485
1486impl<const D: usize, const I: usize, const J: usize> Mul<&TensorRank1Vec<D, J>>
1487 for TensorRank2<D, I, J>
1488{
1489 type Output = TensorRank1Vec<D, I>;
1490 fn mul(self, tensor_rank_1_vec: &TensorRank1Vec<D, J>) -> Self::Output {
1491 tensor_rank_1_vec
1492 .iter()
1493 .map(|tensor_rank_1| &self * tensor_rank_1)
1494 .collect()
1495 }
1496}
1497
1498impl<const D: usize, const I: usize, const J: usize> Mul<TensorRank1Vec<D, J>>
1499 for &TensorRank2<D, I, J>
1500{
1501 type Output = TensorRank1Vec<D, I>;
1502 fn mul(self, tensor_rank_1_vec: TensorRank1Vec<D, J>) -> Self::Output {
1503 tensor_rank_1_vec
1504 .iter()
1505 .map(|tensor_rank_1| self * tensor_rank_1)
1506 .collect()
1507 }
1508}
1509
1510impl<const D: usize, const I: usize, const J: usize> Mul<&TensorRank1Vec<D, J>>
1511 for &TensorRank2<D, I, J>
1512{
1513 type Output = TensorRank1Vec<D, I>;
1514 fn mul(self, tensor_rank_1_vec: &TensorRank1Vec<D, J>) -> Self::Output {
1515 tensor_rank_1_vec
1516 .iter()
1517 .map(|tensor_rank_1| self * tensor_rank_1)
1518 .collect()
1519 }
1520}
1521
1522impl<const D: usize, const I: usize, const J: usize, const K: usize, const W: usize, const X: usize>
1523 Mul<TensorRank2List2D<D, J, K, W, X>> for TensorRank2<D, I, J>
1524{
1525 type Output = TensorRank2List2D<D, I, K, W, X>;
1526 fn mul(self, tensor_rank_2_list_2d: TensorRank2List2D<D, J, K, W, X>) -> Self::Output {
1527 tensor_rank_2_list_2d
1528 .iter()
1529 .map(|tensor_rank_2_list_2d_entry| {
1530 tensor_rank_2_list_2d_entry
1531 .iter()
1532 .map(|tensor_rank_2| &self * tensor_rank_2)
1533 .collect()
1534 })
1535 .collect()
1536 }
1537}
1538
1539impl<const D: usize, const I: usize, const J: usize, const K: usize, const W: usize, const X: usize>
1540 Mul<TensorRank2List2D<D, J, K, W, X>> for &TensorRank2<D, I, J>
1541{
1542 type Output = TensorRank2List2D<D, I, K, W, X>;
1543 fn mul(self, tensor_rank_2_list_2d: TensorRank2List2D<D, J, K, W, X>) -> Self::Output {
1544 tensor_rank_2_list_2d
1545 .iter()
1546 .map(|tensor_rank_2_list_2d_entry| {
1547 tensor_rank_2_list_2d_entry
1548 .iter()
1549 .map(|tensor_rank_2| self * tensor_rank_2)
1550 .collect()
1551 })
1552 .collect()
1553 }
1554}
1555
1556impl<const D: usize, const I: usize, const J: usize, const K: usize> Mul<TensorRank2Vec2D<D, J, K>>
1557 for TensorRank2<D, I, J>
1558{
1559 type Output = TensorRank2Vec2D<D, I, K>;
1560 fn mul(self, tensor_rank_2_list_2d: TensorRank2Vec2D<D, J, K>) -> Self::Output {
1561 tensor_rank_2_list_2d
1562 .iter()
1563 .map(|tensor_rank_2_list_2d_entry| {
1564 tensor_rank_2_list_2d_entry
1565 .iter()
1566 .map(|tensor_rank_2| &self * tensor_rank_2)
1567 .collect()
1568 })
1569 .collect()
1570 }
1571}
1572
1573impl<const D: usize, const I: usize, const J: usize, const K: usize> Mul<TensorRank2Vec2D<D, J, K>>
1574 for &TensorRank2<D, I, J>
1575{
1576 type Output = TensorRank2Vec2D<D, I, K>;
1577 fn mul(self, tensor_rank_2_list_2d: TensorRank2Vec2D<D, J, K>) -> Self::Output {
1578 tensor_rank_2_list_2d
1579 .iter()
1580 .map(|tensor_rank_2_list_2d_entry| {
1581 tensor_rank_2_list_2d_entry
1582 .iter()
1583 .map(|tensor_rank_2| self * tensor_rank_2)
1584 .collect()
1585 })
1586 .collect()
1587 }
1588}
1589
1590#[allow(clippy::suspicious_arithmetic_impl)]
1591impl<const I: usize, const J: usize, const K: usize, const L: usize> Div<TensorRank4<3, I, J, K, L>>
1592 for &TensorRank2<3, I, J>
1593{
1594 type Output = TensorRank2<3, K, L>;
1595 fn div(self, tensor_rank_4: TensorRank4<3, I, J, K, L>) -> Self::Output {
1596 let tensor_rank_2: TensorRank2<9, 88, 99> = tensor_rank_4.into();
1597 let output_tensor_rank_1 = tensor_rank_2.inverse() * self.as_tensor_rank_1();
1598 let mut output = TensorRank2::zero();
1599 output.iter_mut().enumerate().for_each(|(i, output_i)| {
1600 output_i
1601 .iter_mut()
1602 .enumerate()
1603 .for_each(|(j, output_ij)| *output_ij = output_tensor_rank_1[3 * i + j])
1604 });
1605 output
1606 }
1607}