MiniTensor Version of the Day
Loading...
Searching...
No Matches
MiniTensor_Inverse.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_Inverse_h)
11#define MiniTensor_Inverse_h
12
13// Tensor inversion and linear solves.
14#include "MiniTensor_Norms.h"
15
16namespace minitensor {
17
20
26template<typename T, Index N>
28Tensor<T, N>
29inverse(Tensor<T, N> const & A);
30
36template<typename T, Index N>
38Tensor<T, N>
39inverse_fast23(Tensor<T, N> const & A);
40
46template<typename T, Index N>
48Tensor<T, N>
49inverse_full_pivot(Tensor<T, N> const & A);
50
57template<typename T, Index N>
59void
60swap_row(Tensor<T, N> & A, Index const i, Index const j);
61
68template<typename T, Index N>
70void
71swap_col(Tensor<T, N> & A, Index const i, Index const j);
72
77template<typename T, Index N>
79void
80rank_one_left(T const & beta, Vector<T, N> const & v, Tensor<T, N> & A);
81
86template<typename T, Index N>
88void
89rank_one_right(T const & beta, Vector<T, N> const & v, Tensor<T, N> & A);
90
95{
96 UNDEFINED = 0,
97 IDENTITY = 1,
98 DIAGONAL = 2,
99 MAX_ABS_ROW = 3,
100};
101
106template <typename T, Index N, typename RHS>
107std::pair<Tensor<T, N>, RHS> precon(PreconditionerType const pt,
108 Tensor<T, N> const &A, RHS const &B);
109
122template <typename T, Index N, typename RHS>
123RHS solve(Tensor<T, N> const &A, RHS const &b,
125
126template<typename T, Index N, typename RHS>
128RHS
129solve_full_pivot(Tensor<T, N> const & A, RHS const & b);
130
131//
132// Swap row. Exchange rows i and j in place
133// \param A tensor
134// \param i index
135// \param j index
136//
137template<typename T, Index N>
139void
140swap_row(Tensor<T, N> & A, Index const i, Index const j)
141{
142 Index const
143 dimension = A.get_dimension();
144
145 if (i != j) {
146 for (Index k = 0; k < dimension; ++k) {
147 std::swap(A(i, k), A(j, k));
148 }
149 }
150 return;
151}
152
153//
154// Swap column. Exchange columns i and j in place
155// \param A tensor
156// \param i index
157// \param j index
158//
159template<typename T, Index N>
161void
162swap_col(Tensor<T, N> & A, Index const i, Index const j)
163{
164 Index const
165 dimension = A.get_dimension();
166
167 if (i != j) {
168 for (Index k = 0; k < dimension; ++k) {
169 std::swap(A(k, i), A(k, j));
170 }
171 }
172 return;
173}
174
175//
176// Inverse defaults to fast inverse for 2 and 3 dimensions, otherwise
177// use full piviting version
178//
179template<typename T, Index N>
181Tensor<T, N>
183{
184 return inverse_fast23(A);
185}
186
187//
188// R^N 2nd-order tensor inverse
189// Gauss-Jordan elimination. Warning: full pivoting for small tensors.
190// Use Teuchos LAPACK interface for more efficient and robust techniques.
191// \param A nonsingular tensor
192// \return \f$ A^{-1} \f$
193//
194template<typename T, Index N>
196Tensor<T, N>
198{
199 Index const
200 dimension = A.get_dimension();
201
203 B = identity<T, N>(dimension);
204
205 return solve_full_pivot(A, B);
206}
207
208//
209// R^N 2nd-order tensor inverse
210// Fast analytic expressions for 2 and 3 dimensions
211// \param A nonsingular tensor
212// \return \f$ A^{-1} \f$
213//
214template<typename T, Index N>
216Tensor<T, N>
218{
219 Index const
220 dimension = A.get_dimension();
221
222 // As in det, the branches that a statically sized tensor can never
223 // reach are discarded, since they index past its storage.
224
225 if (dimension == 1) {
226 return Tensor<T, N>(1, Filler::ONES) / A(0,0);
227 }
228
229 if constexpr (dimension_reachable<N, 2>) {
230 if (dimension == 2) {
231 T const determinant = det(A);
232 assert(determinant != 0.0);
233 return Tensor<T, N>(A(1,1), -A(0,1), -A(1,0), A(0,0)) / determinant;
234 }
235 }
236
237 if constexpr (dimension_reachable<N, 3>) {
238 if (dimension == 3) {
239 T const determinant = det(A);
240 assert(determinant != 0.0);
241 return Tensor<T, N>(
242 -A(1,2)*A(2,1) + A(1,1)*A(2,2),
243 A(0,2)*A(2,1) - A(0,1)*A(2,2),
244 -A(0,2)*A(1,1) + A(0,1)*A(1,2),
245 A(1,2)*A(2,0) - A(1,0)*A(2,2),
246 -A(0,2)*A(2,0) + A(0,0)*A(2,2),
247 A(0,2)*A(1,0) - A(0,0)*A(1,2),
248 -A(1,1)*A(2,0) + A(1,0)*A(2,1),
249 A(0,1)*A(2,0) - A(0,0)*A(2,1),
250 -A(0,1)*A(1,0) + A(0,0)*A(1,1)
251 ) / determinant;
252 }
253 }
254
255 if constexpr (dimension_reachable<N, 4>) {
256 return inverse_full_pivot(A);
257 }
258
259 // Unreachable: a statically sized tensor of dimension three or less
260 // has been inverted above.
261 return A;
262}
263
264//
265//
266//
271template<typename T, Index N, typename RHS>
273RHS
274solve_full_pivot(Tensor<T, N> const & A, RHS const & b)
275{
276 Index const
277 dimension{A.get_dimension()};
278
279 Index const
280 maximum_dimension{INDEX_SIZE};
281
282 if (dimension > maximum_dimension) {
283 MT_ERROR_EXIT("Max dim (%d) exceeded: %d.", dimension, maximum_dimension);
284 }
285
286 RHS
287 B{b};
288
289 Index const
290 num_rhs{B.get_num_cols()};
291
292 switch (dimension) {
293
294 case 1:
295 for (Index i{0}; i < num_rhs; ++i) {
296 B(0, i) = b(0, i) / A(0, 0);
297 }
298 return B;
299
300 default:
301 break;
302 }
303
305 S{A};
306
307 // Set 1 ... dimension bits to one.
308 Index
309 intact_rows{static_cast<Index>((1UL << dimension) - 1)};
310
311 Index
312 intact_cols{static_cast<Index>((1UL << dimension) - 1)};
313
314 // Gauss-Jordan elimination with full pivoting
315 for (Index k{0}; k < dimension; ++k) {
316
317 // Determine full pivot
318 T
319 pivot{0.0};
320
321 Index
322 pivot_row{dimension};
323
324 Index
325 pivot_col{dimension};
326
327 for (Index row{0}; row < dimension; ++row) {
328
329 if (!(intact_rows & (1 << row))) continue;
330
331 for (Index col{0}; col < dimension; ++col) {
332
333 if (!(intact_cols & (1 << col))) continue;
334
335 T
336 s{std::abs(S(row, col))};
337 if (s > pivot) {
338
339 pivot_row = row;
340 pivot_col = col;
341 pivot = s;
342
343 }
344
345 }
346
347 }
348
349 // The search finds no pivot if all remaining entries are zero or NaN
350 // (NaN compares false against the running maximum). Indexing with
351 // pivot_row == dimension would write out of bounds, so fail explicitly.
352 if (pivot_row >= dimension || pivot_col >= dimension) {
353 MT_ERROR_EXIT("Full-pivot solve failed: singular or non-finite matrix.");
354 }
355
356 // Gauss-Jordan elimination
357 T const
358 t{S(pivot_row, pivot_col)};
359
360 for (Index j{0}; j < dimension; ++j) {
361 S(pivot_row, j) /= t;
362 }
363 for (Index j{0}; j < num_rhs; ++j) {
364 B(pivot_row, j) /= t;
365 }
366
367 for (Index i{0}; i < dimension; ++i) {
368 if (i == pivot_row) continue;
369
370 T const
371 c{S(i, pivot_col)};
372
373 for (Index j = 0; j < dimension; ++j) {
374 S(i, j) -= c * S(pivot_row, j);
375 }
376 for (Index j = 0; j < num_rhs; ++j) {
377 B(i, j) -= c * B(pivot_row, j);
378 }
379 }
380
381 // Eliminate current row and col from intact rows and cols
382 intact_rows &= ~(1 << pivot_row);
383 intact_cols &= ~(1 << pivot_col);
384
385 }
386
387 RHS const
388 X = t_dot(S, B);
389
390 return X;
391}
392
393//
394// Apply rank-one update on the left in place
395//
396template<typename T, Index N>
398void
399rank_one_left(T const & beta, Vector<T, N> const & v, Tensor<T, N> & A)
400{
401 A -= beta * dyad(v, dot(v, A));
402 return;
403}
404
405//
406// Apply rank-one update on the right in place
407//
408template<typename T, Index N>
410void
411rank_one_right(T const & beta, Vector<T, N> const & v, Tensor<T, N> & A)
412{
413 A -= beta * dyad(dot(A, v), v);
414 return;
415}
416
417// Auxiliary functions for precondioners.
418namespace impl {
419
420//
421//
422//
423template <typename T, Index N, typename RHS>
424std::pair<Tensor<T, N>, RHS> identity_precon(Tensor<T, N> const &A,
425 RHS const &B) {
426 return std::make_pair(A, B);
427}
428
429//
430//
431//
432template <typename T, Index N, typename RHS>
433std::pair<Tensor<T, N>, RHS> diagonal_precon(Tensor<T, N> const &A,
434 RHS const &B) {
435 Vector<T, N> const
436 d = diag(A);
437
438 Vector<T, N> const
439 v = 1.0 / d;
440
441 Tensor<T, N> const
442 P = diag(v);
443
444 return std::make_pair(P * A, P * B);
445}
446
447//
448//
449//
450template <typename T, Index N, typename RHS>
451std::pair<Tensor<T, N>, RHS> maxabsrow_precon(Tensor<T, N> const &A, RHS &B) {
452 Index const
453 dimension = A.get_dimension();
454
455 Tensor<T, N>
456 P(dimension, Filler::ZEROS);
457
458 for (Index i{0}; i < dimension; ++i) {
459 P(i, i) = 1.0 / norm_infinity(row(A, i));
460 }
461
462 return std::make_pair(P * A, P * B);
463}
464
465} // namespace impl
466
467//
468//
469//
470template <typename T, Index N, typename RHS>
471std::pair<Tensor<T, N>, RHS> precon(PreconditionerType const pt,
472 Tensor<T, N> const &A, RHS const &B) {
473 switch (pt) {
474 default:
475 MT_ERROR_EXIT("Unknown preconditioner type.");
476 break;
477
479 break;
480
482 return impl::diagonal_precon(A, B);
483
485 return impl::maxabsrow_precon(A, B);
486 }
487
488 return std::make_pair(A, B);
489}
490
491//
492// Solve linear system of equations.
493// This is meant for the solution of small linear systems of equations
494// typically found in constitutive updates.
495// Right now the implementation is very inefficient (but accurate)
496// as it just Gauss-Jordan elimination. It is intended to be used in
497// conjunction with Kokkos to take advantage of thread parallelism.
498//
499template <typename T, Index N, typename RHS>
500RHS solve(Tensor<T, N> const &A, RHS const &b, PreconditionerType const pt) {
502 PA;
503
504 RHS
505 Pb;
506
507 std::tie(PA, Pb) = precon(pt, A, b);
508
509 return solve_full_pivot(PA, Pb);
510}
511
513} // namespace minitensor
514
515#endif // MiniTensor_Inverse_h
#define KOKKOS_INLINE_FUNCTION
#define MT_ERROR_EXIT(...)
KOKKOS_INLINE_FUNCTION Tensor< typename Promote< S, T >::type, N > dyad(Vector< S, N > const &u, Vector< T, N > const &v)
KOKKOS_INLINE_FUNCTION Vector< T, M > col(Matrix< T, M, N > const &A, Index const j)
KOKKOS_INLINE_FUNCTION Index get_dimension() const
KOKKOS_INLINE_FUNCTION Tensor< T, N > diag(Vector< T, N > const &v)
KOKKOS_INLINE_FUNCTION Vector< typename Promote< S, T >::type, M > dot(Matrix< T, M, N > const &A, Vector< S, N > const &u)
KOKKOS_INLINE_FUNCTION Matrix< typename Promote< S, T >::type, M, N > t_dot(Matrix< S, P, M > const &A, Matrix< T, P, N > const &B)
KOKKOS_INLINE_FUNCTION Vector< T, N > row(Matrix< T, M, N > const &A, Index const i)
KOKKOS_INLINE_FUNCTION void swap_col(Tensor< T, N > &A, Index const i, Index const j)
KOKKOS_INLINE_FUNCTION RHS solve_full_pivot(Tensor< T, N > const &A, RHS const &b)
KOKKOS_INLINE_FUNCTION void rank_one_right(T const &beta, Vector< T, N > const &v, Tensor< T, N > &A)
KOKKOS_INLINE_FUNCTION Tensor< T, N > inverse_full_pivot(Tensor< T, N > const &A)
KOKKOS_INLINE_FUNCTION Tensor< T, N > inverse(Tensor< T, N > const &A)
KOKKOS_INLINE_FUNCTION void swap_row(Tensor< T, N > &A, Index const i, Index const j)
KOKKOS_INLINE_FUNCTION void rank_one_left(T const &beta, Vector< T, N > const &v, Tensor< T, N > &A)
std::pair< Tensor< T, N >, RHS > precon(PreconditionerType const pt, Tensor< T, N > const &A, RHS const &B)
RHS solve(Tensor< T, N > const &A, RHS const &b, PreconditionerType const pt=PreconditionerType::IDENTITY)
KOKKOS_INLINE_FUNCTION Tensor< T, N > inverse_fast23(Tensor< T, N > const &A)
KOKKOS_INLINE_FUNCTION T det(Tensor< T, N > const &A)
KOKKOS_INLINE_FUNCTION T norm_infinity(Tensor< T, N > const &A)
uint32_t Index
Indexing type.
constexpr Index INDEX_SIZE