MiniTensor Version of the Day
Loading...
Searching...
No Matches
MiniTensor_Norms.h
Go to the documentation of this file.
1// @HEADER
2// *****************************************************************************
3// MiniTensor Package
4//
5// Copyright 2016 NTESS and the MiniTensor contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8// @HEADER
9
10#if !defined(MiniTensor_Norms_h)
11#define MiniTensor_Norms_h
12
13// Scalar measures of tensors: norms, invariants, argmax queries.
14#include "MiniTensor_Tensor.h"
15
16namespace minitensor {
17
20
25template<typename T, Index N>
27T
28norm(Tensor<T, N> const & A);
29
34template<typename T, Index N>
36T
37norm_1(Tensor<T, N> const & A);
38
43template<typename T, Index N>
45T
46norm_infinity(Tensor<T, N> const & A);
47
55template<typename T, Index N>
57Tensor<T, N>
58subtensor(Tensor<T, N> const & A, Index const i, Index const j);
59
64template<typename T, Index N>
66T
67det(Tensor<T, N> const & A);
68
73template<typename T, Index N>
75T
76trace(Tensor<T, N> const & A);
77
82template<typename T, Index N>
84T
85I1(Tensor<T, N> const & A);
86
91template<typename T, Index N>
93T
94I2(Tensor<T, N> const & A);
95
100template<typename T, Index N>
102T
103I3(Tensor<T, N> const & A);
104
110template<typename T, Index N>
112T
113norm_off_diagonal(Tensor<T, N> const & A);
114
120template <typename T, Index N>
121std::pair<Index, Index> arg_max_abs(Tensor<T, N> const &A);
122
128template <typename T, Index N>
129std::pair<Index, Index> arg_max_off_diagonal(Tensor<T, N> const &A);
130
131//
132// R^N tensor Frobenius norm
133// \return \f$ \sqrt{A:A} \f$
134//
135template<typename T, Index N>
137T
139{
140 Index const
141 dimension = A.get_dimension();
142
143 T
144 s = 0.0;
145
146 switch (dimension) {
147
148 default:
149 s = norm_f_square(A);
150 break;
151
152 case 3:
153 s+= A(0,0)*A(0,0) + A(0,1)*A(0,1) + A(0,2)*A(0,2);
154 s+= A(1,0)*A(1,0) + A(1,1)*A(1,1) + A(1,2)*A(1,2);
155 s+= A(2,0)*A(2,0) + A(2,1)*A(2,1) + A(2,2)*A(2,2);
156 break;
157
158 case 2:
159 s+= A(0,0)*A(0,0) + A(0,1)*A(0,1);
160 s+= A(1,0)*A(1,0) + A(1,1)*A(1,1);
161 break;
162
163 case 1:
164 s+= A(0,0)*A(0,0);
165 break;
166
167 }
168
169 if (s > 0.0) return std::sqrt(s);
170 return 0.0;
171
172}
173
174//
175// R^N tensor 1-norm
176// \return \f$ \max_{j \in {0,\cdots,N}}\Sigma_{i=0}^N |A_{ij}| \f$
177//
178template<typename T, Index N>
180T
182{
183 Index const
184 dimension = A.get_dimension();
185
187 v(dimension);
188
189 T
190 s = 0.0;
191
192 switch (dimension) {
193
194 default:
195
196 for (Index i = 0; i < dimension; ++i) {
197 T t = 0.0;
198 for (Index j = 0; j < dimension; ++j) {
199 t += minitensor::abs(A(j, i));
200 }
201 v(i) = t;
202 }
203
204 for (Index i = 0; i < dimension; ++i) {
205 s = max(s, v(i));
206 }
207 break;
208
209 case 3:
210 v(0) = minitensor::abs(A(0, 0)) + minitensor::abs(A(1, 0)) + minitensor::abs(A(2, 0));
211 v(1) = minitensor::abs(A(0, 1)) + minitensor::abs(A(1, 1)) + minitensor::abs(A(2, 1));
212 v(2) = minitensor::abs(A(0, 2)) + minitensor::abs(A(1, 2)) + minitensor::abs(A(2, 2));
213
214 s = max(max(v(0), v(1)), v(2));
215 break;
216
217 case 2:
218 v(0) = minitensor::abs(A(0, 0)) + minitensor::abs(A(1, 0));
219 v(1) = minitensor::abs(A(0, 1)) + minitensor::abs(A(1, 1));
220
221 s = max(v(0), v(1));
222 break;
223
224 case 1:
225 s = minitensor::abs(A(0, 0));
226 break;
227 }
228
229 return s;
230}
231
232//
233// R^N tensor infinity-norm
234// \return \f$ \max_{i \in {0,\cdots,N}}\Sigma_{j=0}^N |A_{ij}| \f$
235//
236template<typename T, Index N>
238T
240{
241 Index const
242 dimension = A.get_dimension();
243
245 v(dimension);
246
247 T s = 0.0;
248
249 switch (dimension) {
250
251 default:
252 for (Index i = 0; i < dimension; ++i) {
253 T t = 0.0;
254 for (Index j = 0; j < dimension; ++j) {
255 t += minitensor::abs(A(i, j));
256 }
257 v(i) = t;
258 }
259
260 for (Index i = 0; i < dimension; ++i) {
261 s = max(s, v(i));
262 }
263 break;
264
265 case 3:
266 v(0) = minitensor::abs(A(0, 0)) + minitensor::abs(A(0, 1)) + minitensor::abs(A(0, 2));
267 v(1) = minitensor::abs(A(1, 0)) + minitensor::abs(A(1, 1)) + minitensor::abs(A(1, 2));
268 v(2) = minitensor::abs(A(2, 0)) + minitensor::abs(A(2, 1)) + minitensor::abs(A(2, 2));
269
270 s = max(max(v(0), v(1)), v(2));
271 break;
272
273 case 2:
274 v(0) = minitensor::abs(A(0, 0)) + minitensor::abs(A(0, 1));
275 v(1) = minitensor::abs(A(1, 0)) + minitensor::abs(A(1, 1));
276
277 s = max(v(0), v(1));
278 break;
279
280 case 1:
281 s = minitensor::abs(A(0, 0));
282 break;
283
284 }
285
286 return s;
287}
288
289//
290// R^N determinant
291// Laplace expansion. Warning: no pivoting.
292// Casual use only. Use Teuchos LAPACK interface for
293// more efficient and robust techniques.
294// \param A tensor
295// \return \f$ \det A \f$
296//
297template<typename T, Index N>
299T
301{
302 Index const
303 dimension = A.get_dimension();
304
305 T
306 s = 0.0;
307
308 // The dimension is a run-time quantity, so every branch of the
309 // dispatch below is compiled even for a statically sized tensor that
310 // can never reach it. The branches for the larger dimensions index
311 // past the storage of the smaller ones, which the compiler reports as
312 // an out-of-bounds access, so they are discarded here instead.
313
314 if (dimension == 1) {
315 s = A(0,0);
316 return s;
317 }
318
319 if constexpr (dimension_reachable<N, 2>) {
320 if (dimension == 2) {
321 s = A(0,0) * A(1,1) - A(1,0) * A(0,1);
322 return s;
323 }
324 }
325
326 if constexpr (dimension_reachable<N, 3>) {
327 if (dimension == 3) {
328 s = -A(0,2)*A(1,1)*A(2,0) + A(0,1)*A(1,2)*A(2,0) +
329 A(0,2)*A(1,0)*A(2,1) - A(0,0)*A(1,2)*A(2,1) -
330 A(0,1)*A(1,0)*A(2,2) + A(0,0)*A(1,1)*A(2,2);
331 return s;
332 }
333 }
334
335 if constexpr (dimension_reachable<N, 4>) {
336 int sign = 1;
337 for (Index i = 0; i < dimension; ++i) {
338 const T d = det(subtensor(A, i, 1));
339 s += sign * d * A(i, 1);
340 sign *= -1;
341 }
342 }
343
344 return s;
345}
346
347//
348// R^N trace
349// \param A tensor
350// \return \f$ A:I \f$
351//
352template<typename T, Index N>
354T
356{
357 Index const
358 dimension = A.get_dimension();
359
360 T s = 0.0;
361
362 switch (dimension) {
363
364 default:
365 for (Index i = 0; i < dimension; ++i) {
366 s += A(i,i);
367 }
368 break;
369
370 case 3:
371 s = A(0,0) + A(1,1) + A(2,2);
372 break;
373
374 case 2:
375 s = A(0,0) + A(1,1);
376 break;
377
378 case 1:
379 s = A(0,0);
380 break;
381
382 }
383
384 return s;
385}
386
387//
388// R^N first invariant, trace
389// \param A tensor
390// \return \f$ I_A = A:I \f$
391//
392template<typename T, Index N>
394T
395I1(Tensor<T, N> const & A)
396{
397 return trace(A);
398}
399
400//
401// R^N second invariant
402// \param A tensor
403// \return \f$ II_A \f$
404//
405template<typename T, Index N>
407T
408I2(Tensor<T, N> const & A)
409{
410 Index const
411 dimension = A.get_dimension();
412
413 T
414 s = 0.0;
415
416 T const
417 trA = trace(A);
418
419 switch (dimension) {
420
421 default:
422#ifdef KOKKOS_ENABLE_CUDA
423 Kokkos::abort("I2 for N > 3 not implemented.");
424 return T();
425#else
426 std::cerr << "I2 for N > 3 not implemented." << std::endl;
427 exit(1);
428#endif
429 break;
430
431 case 3:
432 s = 0.5 * (trA*trA - A(0,0)*A(0,0) - A(1,1)*A(1,1) - A(2,2)*A(2,2)) -
433 A(0,1)*A(1,0) - A(0,2)*A(2,0) - A(1,2)*A(2,1);
434 break;
435
436 case 2:
437 s = - det(A);
438 break;
439
440 case 1:
441 s = 0.0;
442 break;
443
444 }
445
446 return s;
447}
448
449//
450// R^N third invariant
451// \param A tensor
452// \return \f$ III_A \f$
453//
454template<typename T, Index N>
456T
457I3(Tensor<T, N> const & A)
458{
459 Index const
460 dimension = A.get_dimension();
461
462 T
463 s = 0.0;
464
465 switch (dimension) {
466
467 default:
468#ifdef KOKKOS_ENABLE_CUDA
469 Kokkos::abort("I3 for N > 3 not implemented.");
470 return T();
471#else
472 std::cerr << "I3 for N > 3 not implemented." << std::endl;
473 exit(1);
474#endif
475 break;
476
477 case 3:
478 s = det(A);
479 break;
480
481 case 2:
482 s = 0.0;
483 break;
484
485 case 1:
486 s = 0.0;
487 break;
488
489 }
490
491 return s;
492}
493
494//
495// R^N Subtensor
496// \param A tensor
497// \param i index
498// \param j index
499// \return Subtensor with i-row and j-col deleted.
500//
501template<typename T, Index N>
503Tensor<T, N>
504subtensor(Tensor<T, N> const & A, Index const i, Index const j)
505{
506 Index const
507 dimension = A.get_dimension();
508
509 assert(i < dimension);
510 assert(j < dimension);
511
513 B(dimension - 1);
514
515 Index p = 0;
516 for (Index m = 0; m < dimension; ++m) {
517 if (m == i) continue;
518 Index q = 0;
519 for (Index n = 0; n < dimension; ++n) {
520 if (n == j) continue;
521 B(p, q) = A(m, n);
522 ++q;
523 }
524 ++p;
525 }
526
527 return B;
528}
529
530//
531// R^N off-diagonal norm. Useful for SVD and other algorithms
532// that rely on Jacobi-type procedures.
533// \param A
534// \return \f$ \sqrt(\sum_i \sum_{j, j\neq i} a_{ij}^2) \f$
535//
536template<typename T, Index N>
538T
540{
541 Index const
542 dimension = A.get_dimension();
543
544 T
545 s = 0.0;
546
547 switch (dimension) {
548
549 default:
550 for (Index i = 0; i < dimension; ++i) {
551 for (Index j = 0; j < dimension; ++j) {
552 if (i != j) s += A(i,j)*A(i,j);
553 }
554 }
555 break;
556
557 case 3:
558 s = A(0,1)*A(0,1) + A(0,2)*A(0,2) + A(1,2)*A(1,2) +
559 A(1,0)*A(1,0) + A(2,0)*A(2,0) + A(2,1)*A(2,1);
560 break;
561
562 case 2:
563 s = A(0,1)*A(0,1) + A(1,0)*A(1,0);
564 break;
565
566 case 1:
567 s = 0.0;
568 break;
569
570 }
571 return std::sqrt(s);
572}
573
574//
575// R^N arg max abs. Useful for inverse and other algorithms
576// that rely on Jacobi-type procedures.
577// \param A
578// \return \f$ (p,q) = arg max_{i,j} |a_{ij}| \f$
579//
580template <typename T, Index N>
581std::pair<Index, Index> arg_max_abs(Tensor<T, N> const &A) {
582
583 Index p = 0;
584 Index q = 0;
585
586 T
587 s = std::abs(A(p,q));
588
589 Index const
590 dimension = A.get_dimension();
591
592 for (Index i = 0; i < dimension; ++i) {
593 for (Index j = 0; j < dimension; ++j) {
594 if (std::abs(A(i,j)) > s) {
595 p = i;
596 q = j;
597 s = std::abs(A(i,j));
598 }
599 }
600 }
601
602 return std::make_pair(p,q);
603}
604
605//
606// R^N arg max off-diagonal. Useful for SVD and other algorithms
607// that rely on Jacobi-type procedures.
608// \param A
609// \return \f$ (p,q) = arg max_{i \neq j} |a_{ij}| \f$
610//
611template <typename T, Index N>
612std::pair<Index, Index> arg_max_off_diagonal(Tensor<T, N> const &A) {
613 Index p = 0;
614 Index q = 1;
615
616 T s = std::abs(A(p,q));
617
618 Index const
619 dimension = A.get_dimension();
620
621 for (Index i = 0; i < dimension; ++i) {
622 for (Index j = 0; j < dimension; ++j) {
623 if (i != j && std::abs(A(i,j)) > s) {
624 p = i;
625 q = j;
626 s = std::abs(A(i,j));
627 }
628 }
629 }
630
631 return std::make_pair(p,q);
632}
633
635} // namespace minitensor
636
637#endif // MiniTensor_Norms_h
#define KOKKOS_INLINE_FUNCTION
KOKKOS_INLINE_FUNCTION Index get_dimension() const
KOKKOS_INLINE_FUNCTION T norm_f_square(TensorBase< T, ST > const &X)
KOKKOS_INLINE_FUNCTION T I1(Tensor< T, N > const &A)
KOKKOS_INLINE_FUNCTION Tensor< T, N > subtensor(Tensor< T, N > const &A, Index const i, Index const j)
KOKKOS_INLINE_FUNCTION T norm(Tensor< T, N > const &A)
KOKKOS_INLINE_FUNCTION T det(Tensor< T, N > const &A)
KOKKOS_INLINE_FUNCTION T norm_off_diagonal(Tensor< T, N > const &A)
KOKKOS_INLINE_FUNCTION T norm_1(Tensor< T, N > const &A)
std::pair< Index, Index > arg_max_abs(Tensor< T, N > const &A)
std::pair< Index, Index > arg_max_off_diagonal(Tensor< T, N > const &A)
KOKKOS_INLINE_FUNCTION T I3(Tensor< T, N > const &A)
KOKKOS_INLINE_FUNCTION T norm_infinity(Tensor< T, N > const &A)
KOKKOS_INLINE_FUNCTION T I2(Tensor< T, N > const &A)
KOKKOS_INLINE_FUNCTION T trace(Tensor< T, N > const &A)
uint32_t Index
Indexing type.
KOKKOS_INLINE_FUNCTION T abs(T const &a)
KOKKOS_INLINE_FUNCTION T max(T const &a, T const &b)