ITK 6.0.0
Insight Toolkit
 
Loading...
Searching...
No Matches
itkSymmetricEigenAnalysis.h
Go to the documentation of this file.
1/*=========================================================================
2 *
3 * Copyright NumFOCUS
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0.txt
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 *=========================================================================*/
18#ifndef itkSymmetricEigenAnalysis_h
19#define itkSymmetricEigenAnalysis_h
20
21#include "itkMacro.h"
22#include "itk_eigen.h"
23#include ITK_EIGEN(Eigenvalues)
24#include <numeric>
25#include <vector>
26// For GetPointerToMatrixData
27#include "vnl/vnl_matrix.h"
28#include "vnl/vnl_matrix_fixed.h"
29#include "itkMatrix.h"
30
31namespace itk
32{
33namespace detail
34{
35/* Helper functions returning pointer to matrix data for different types. */
36template <typename TValueType, unsigned int VRows, unsigned int VColumns>
37const TValueType *
38GetPointerToMatrixData(const vnl_matrix_fixed<TValueType, VRows, VColumns> & inputMatrix)
39{
40 return inputMatrix.data_block();
41}
42template <typename TValueType>
43const TValueType *
44GetPointerToMatrixData(const vnl_matrix<TValueType> & inputMatrix)
45{
46 return inputMatrix.data_block();
47}
48
49template <typename TValueType, unsigned int VRows, unsigned int VColumns>
50const TValueType *
52{
53 return inputMatrix.GetVnlMatrix().data_block();
54}
55
71template <typename TArray>
72std::vector<int>
73sortEigenValuesByMagnitude(TArray & eigenValues, const unsigned int numberOfElements)
74{
75 std::vector<int> indicesSortPermutations(numberOfElements, 0);
76 std::iota(std::begin(indicesSortPermutations), std::end(indicesSortPermutations), 0);
77
78 std::sort(std::begin(indicesSortPermutations),
79 std::end(indicesSortPermutations),
80 [&eigenValues](unsigned int a, unsigned int b) {
81 return itk::Math::Absolute(eigenValues[a]) < itk::Math::Absolute(eigenValues[b]);
82 });
83 auto tmpCopy = eigenValues;
84 for (unsigned int i = 0; i < numberOfElements; ++i)
85 {
86 eigenValues[i] = tmpCopy[indicesSortPermutations[i]];
87 }
88 return indicesSortPermutations;
89}
90
99template <typename QMatrix>
100void
101permuteColumnsWithSortIndices(QMatrix & eigenVectors, const std::vector<int> & indicesSortPermutations)
102{
103 using EigenLibPermutationMatrix = Eigen::PermutationMatrix<Eigen::Dynamic, Eigen::Dynamic>;
104 auto numberOfElements = indicesSortPermutations.size();
105 // Creates a NxN permutation matrix copying our permutation to the matrix indices.
106 // Which holds the 1D array representation of a permutation.
107 EigenLibPermutationMatrix perm(numberOfElements);
108 perm.setIdentity();
109 std::copy(indicesSortPermutations.begin(), indicesSortPermutations.end(), perm.indices().data());
110 // .eval() avoids a GCC -Wmaybe-uninitialized false positive on assignment.
111 eigenVectors = (eigenVectors * perm).eval();
112}
113
114} // end namespace detail
115
121{
122public:
130 enum class EigenValueOrder : uint8_t
131 {
135 };
136};
137// Define how to print enumeration
138extern ITKCommon_EXPORT std::ostream &
139 operator<<(std::ostream & out, const SymmetricEigenAnalysisEnums::EigenValueOrder value);
140
142
144Int2EigenValueOrderEnum(const uint8_t value)
145{
146 switch (value)
147 {
148 case 1:
149 return EigenValueOrderEnum::OrderByValue;
150 case 2:
151 return EigenValueOrderEnum::OrderByMagnitude;
152 case 3:
153 return EigenValueOrderEnum::DoNotOrder;
154 default:
155 break;
156 }
157 itkGenericExceptionMacro("Error: Invalid value for conversion.");
158}
159
160#if !defined(ITK_LEGACY_REMOVE)
162inline constexpr EigenValueOrderEnum OrderByValue = EigenValueOrderEnum::OrderByValue;
163inline constexpr EigenValueOrderEnum OrderByMagnitude = EigenValueOrderEnum::OrderByMagnitude;
164inline constexpr EigenValueOrderEnum DoNotOrder = EigenValueOrderEnum::DoNotOrder;
165#endif
166
205
206template <typename TMatrix, typename TVector, typename TEigenMatrix = TMatrix>
207class ITK_TEMPLATE_EXPORT SymmetricEigenAnalysis
208{
209public:
211#if !defined(ITK_LEGACY_REMOVE)
213 using EigenValueOrderType = EigenValueOrderEnum;
214#endif
215
217
218 SymmetricEigenAnalysis(const unsigned int dimension)
219 : m_Dimension(dimension)
220 , m_Order(dimension)
221 {}
222
224
225 using MatrixType = TMatrix;
226 using EigenMatrixType = TEigenMatrix;
227 using VectorType = TVector;
228
242 unsigned int
243 ComputeEigenValues(const TMatrix & A, TVector & D) const;
244
265 unsigned int
266 ComputeEigenValuesAndVectors(const TMatrix & A, TVector & EigenValues, TEigenMatrix & EigenVectors) const;
267
268
270 void
271 SetOrder(const unsigned int n)
272 {
273 m_Order = n;
274 }
275
279 [[nodiscard]] unsigned int
280 GetOrder() const
281 {
282 return m_Order;
283 }
284
289 void
291 {
292 if (b)
293 {
294 m_OrderEigenValues = EigenValueOrderEnum::OrderByValue;
295 }
296 else
297 {
298 m_OrderEigenValues = EigenValueOrderEnum::DoNotOrder;
299 }
300 }
301
302 [[nodiscard]] bool
304 {
305 return m_OrderEigenValues == EigenValueOrderEnum::OrderByValue;
306 }
307
308
313 void
315 {
316 if (b)
317 {
318 m_OrderEigenValues = EigenValueOrderEnum::OrderByMagnitude;
319 }
320 else
321 {
322 m_OrderEigenValues = EigenValueOrderEnum::DoNotOrder;
323 }
324 }
325
326 [[nodiscard]] bool
328 {
329 return m_OrderEigenValues == EigenValueOrderEnum::OrderByMagnitude;
330 }
331
332
335 void
336 SetDimension(const unsigned int n)
337 {
338 m_Dimension = n;
339 if (m_Order == 0)
340 {
342 }
343 }
344
347 [[nodiscard]] unsigned int
349 {
350 return m_Dimension;
351 }
352
355 void
356 SetUseEigenLibrary(const bool input)
357 {
358 m_UseEigenLibrary = input;
359 }
360 void
362 {
363 m_UseEigenLibrary = true;
364 }
365 void
367 {
368 m_UseEigenLibrary = false;
369 }
370 [[nodiscard]] bool
372 {
373 return m_UseEigenLibrary;
374 }
375
376private:
377 bool m_UseEigenLibrary{ false };
378 unsigned int m_Dimension{ 0 };
379 unsigned int m_Order{ 0 };
380 EigenValueOrderEnum m_OrderEigenValues{ EigenValueOrderEnum::OrderByValue };
381
400 void
401 ReduceToTridiagonalMatrix(double * a, double * d, double * e, double * e2) const;
402
423 void
424 ReduceToTridiagonalMatrixAndGetTransformation(const double * a, double * d, double * e, double * z) const;
425
453 unsigned int
454 ComputeEigenValuesUsingQL(double * d, double * e) const;
455
491 unsigned int
492 ComputeEigenValuesAndVectorsUsingQL(double * d, double * e, double * z) const;
493
494 /* Legacy algorithms using thread-safe netlib.
495 * \sa ComputeEigenValues and \sa ComputeEigenValuesAndVectors
496 */
497 unsigned int
498 ComputeEigenValuesLegacy(const TMatrix & A, TVector & D) const;
499
500 unsigned int
501 ComputeEigenValuesAndVectorsLegacy(const TMatrix & A, TVector & EigenValues, TEigenMatrix & EigenVectors) const;
502
503 /* Helper to get the matrix value type for EigenLibMatrix typename.
504 *
505 * If the TMatrix is vnl, the type is in element_type.
506 * In TMatrix is itk::Matrix, or any itk::FixedArray is in ValueType.
507 *
508 * To use this function:
509 * using ValueType = decltype(this->GetMatrixType(true));
510 *
511 * \note The two `GetMatrixValueType` overloads have different
512 * parameter declarations (`bool` and `...`), to avoid that both
513 * functions are equally good candidates during overload resolution,
514 * in case `element_type` and `ValueType` are both nested types of
515 * `TMatrix` (which is the case when `TMatrix` = `itk::Array2D`).
516 */
517 template <typename QMatrix = TMatrix>
518 [[nodiscard]] auto
519 GetMatrixValueType(bool) const -> typename QMatrix::element_type
520 {
521 return QMatrix::element_type();
522 }
523 template <typename QMatrix = TMatrix>
524 auto
525 GetMatrixValueType(...) const -> typename QMatrix::ValueType
526 {
527 return QMatrix::ValueType();
528 }
529
530 /* Wrapper that call the right implementation for the type of matrix. */
531 unsigned int
533 TVector & EigenValues,
534 TEigenMatrix & EigenVectors) const
535 {
536 return ComputeEigenValuesAndVectorsWithEigenLibraryImpl(A, EigenValues, EigenVectors, true);
537 }
538
539 /* Implementation detail using EigenLib that performs a copy of the input matrix.
540 *
541 * @param (long) implementation detail argument making this implementation less favourable
542 * to be chosen if alternatives are available.
543 *
544 * @return an unsigned int with no information value (no error code in EigenLib) */
545 template <typename QMatrix>
546 auto
548 TVector & EigenValues,
549 TEigenMatrix & EigenVectors,
550 long) const -> decltype(1U)
551 {
552 using ValueType = decltype(GetMatrixValueType(true));
553 using EigenLibMatrixType = Eigen::Matrix<ValueType, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
554 EigenLibMatrixType inputMatrix(m_Dimension, m_Dimension);
555 for (unsigned int row = 0; row < m_Dimension; ++row)
556 {
557 for (unsigned int col = 0; col < m_Dimension; ++col)
558 {
559 inputMatrix(row, col) = A(row, col);
560 }
561 }
562 using EigenSolverType = Eigen::SelfAdjointEigenSolver<EigenLibMatrixType>;
563 const EigenSolverType solver(inputMatrix); // Computes EigenValues and EigenVectors
564 const auto & eigenValues = solver.eigenvalues();
565 /* Column k of the returned matrix is an eigenvector corresponding to
566 * eigenvalue number $ k $ as returned by eigenvalues().
567 * The eigenvectors are normalized to have (Euclidean) norm equal to one. */
568 const auto & eigenVectors = solver.eigenvectors();
569
570 if (m_OrderEigenValues == EigenValueOrderEnum::OrderByMagnitude)
571 {
572 auto copyEigenValues = eigenValues;
573 auto copyEigenVectors = eigenVectors;
574 auto indicesSortPermutations = detail::sortEigenValuesByMagnitude(copyEigenValues, m_Dimension);
575 detail::permuteColumnsWithSortIndices(copyEigenVectors, indicesSortPermutations);
576
577 for (unsigned int row = 0; row < m_Dimension; ++row)
578 {
579 EigenValues[row] = copyEigenValues[row];
580 for (unsigned int col = 0; col < m_Dimension; ++col)
581 {
582 EigenVectors[row][col] = copyEigenVectors(col, row);
583 }
584 }
585 }
586 else
587 {
588 for (unsigned int row = 0; row < m_Dimension; ++row)
589 {
590 EigenValues[row] = eigenValues[row];
591 for (unsigned int col = 0; col < m_Dimension; ++col)
592 {
593 EigenVectors[row][col] = eigenVectors(col, row);
594 }
595 }
596 }
597 // No error code
598 return 1;
599 }
600
601
602 /* Implementation detail using EigenLib that do not perform a copy.
603 * It needs the existence of a pointer to matrix data. \sa GetPointerToMatrixData
604 * If new types want to use this method, an appropriate overload of GetPointerToMatrixData
605 * should be included.
606 *
607 * @param (bool) implementation detail argument making this implementation the most favourable
608 * to be chosen from all the alternative implementations.
609 *
610 * @return an unsigned int with no information value (no error code in EigenLib) */
611 template <typename QMatrix>
612 auto
614 TVector & EigenValues,
615 TEigenMatrix & EigenVectors,
616 bool) const -> decltype(GetPointerToMatrixData(A), 1U)
617 {
618 auto pointerToData = GetPointerToMatrixData(A);
619 using PointerType = decltype(pointerToData);
620 using ValueTypeCV = std::remove_pointer_t<PointerType>;
621 using ValueType = std::remove_cv_t<ValueTypeCV>;
622 using EigenLibMatrixType = Eigen::Matrix<ValueType, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
623 using EigenConstMatrixMap = Eigen::Map<const EigenLibMatrixType>;
624 EigenConstMatrixMap inputMatrix(pointerToData, m_Dimension, m_Dimension);
625 using EigenSolverType = Eigen::SelfAdjointEigenSolver<EigenLibMatrixType>;
626 EigenSolverType solver(inputMatrix); // Computes EigenValues and EigenVectors
627 const auto & eigenValues = solver.eigenvalues();
628 /* Column k of the returned matrix is an eigenvector corresponding to
629 * eigenvalue number $ k $ as returned by eigenvalues().
630 * The eigenvectors are normalized to have (Euclidean) norm equal to one. */
631 const auto & eigenVectors = solver.eigenvectors();
632 if (m_OrderEigenValues == EigenValueOrderEnum::OrderByMagnitude)
633 {
634 auto copyEigenValues = eigenValues;
635 auto copyEigenVectors = eigenVectors;
636 auto indicesSortPermutations = detail::sortEigenValuesByMagnitude(copyEigenValues, m_Dimension);
637 detail::permuteColumnsWithSortIndices(copyEigenVectors, indicesSortPermutations);
638 for (unsigned int row = 0; row < m_Dimension; ++row)
639 {
640 EigenValues[row] = copyEigenValues[row];
641 for (unsigned int col = 0; col < m_Dimension; ++col)
642 {
643 EigenVectors[row][col] = copyEigenVectors(col, row);
644 }
645 }
646 }
647 else
648 {
649 for (unsigned int row = 0; row < m_Dimension; ++row)
650 {
651 EigenValues[row] = eigenValues[row];
652 for (unsigned int col = 0; col < m_Dimension; ++col)
653 {
654 EigenVectors[row][col] = eigenVectors(col, row);
655 }
656 }
657 }
658 // No error code
659 return 1;
660 }
661
662 /* Wrapper that call the right implementation for the type of matrix. */
663 unsigned int
664 ComputeEigenValuesWithEigenLibrary(const TMatrix & A, TVector & EigenValues) const
665 {
666 return ComputeEigenValuesWithEigenLibraryImpl(A, EigenValues, true);
667 }
668
669 /* Implementation detail using EigenLib that performs a copy of the input matrix.
670 *
671 * @param (long) implementation detail argument making this implementation less favourable
672 * to be chosen if alternatives are available.
673 *
674 * @return an unsigned int with no information value (no error code in EigenLib) */
675 template <typename QMatrix>
676 auto
677 ComputeEigenValuesWithEigenLibraryImpl(const QMatrix & A, TVector & EigenValues, long) const -> decltype(1U)
678 {
679 using ValueType = decltype(GetMatrixValueType(true));
680 using EigenLibMatrixType = Eigen::Matrix<ValueType, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
681 EigenLibMatrixType inputMatrix(m_Dimension, m_Dimension);
682 for (unsigned int row = 0; row < m_Dimension; ++row)
683 {
684 for (unsigned int col = 0; col < m_Dimension; ++col)
685 {
686 inputMatrix(row, col) = A(row, col);
687 }
688 }
689 using EigenSolverType = Eigen::SelfAdjointEigenSolver<EigenLibMatrixType>;
690 const EigenSolverType solver(inputMatrix, Eigen::EigenvaluesOnly);
691 auto eigenValues = solver.eigenvalues();
692 if (m_OrderEigenValues == EigenValueOrderEnum::OrderByMagnitude)
693 {
695 }
696 for (unsigned int i = 0; i < m_Dimension; ++i)
697 {
698 EigenValues[i] = eigenValues[i];
699 }
700
701 // No error code
702 return 1;
703 }
704
705 /* Implementation detail using EigenLib that do not perform a copy.
706 * It needs the existence of a pointer to matrix data. \sa GetPointerToMatrixData
707 * If new types want to use this method, an appropriate overload of GetPointerToMatrixData
708 * should be included.
709 *
710 * @param (bool) implementation detail argument making this implementation the most favourable
711 * to be chosen from all the alternative implementations.
712 *
713 * @return an unsigned int with no information value (no error code in EigenLib) */
714 template <typename QMatrix>
715 auto
716 ComputeEigenValuesWithEigenLibraryImpl(const QMatrix & A, TVector & EigenValues, bool) const
717 -> decltype(GetPointerToMatrixData(A), 1U)
718 {
719 auto pointerToData = GetPointerToMatrixData(A);
720 using PointerType = decltype(pointerToData);
721 using ValueTypeCV = std::remove_pointer_t<PointerType>;
722 using ValueType = std::remove_cv_t<ValueTypeCV>;
723 using EigenLibMatrixType = Eigen::Matrix<ValueType, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
724 using EigenConstMatrixMap = Eigen::Map<const EigenLibMatrixType>;
725 EigenConstMatrixMap inputMatrix(pointerToData, m_Dimension, m_Dimension);
726 using EigenSolverType = Eigen::SelfAdjointEigenSolver<EigenLibMatrixType>;
727 EigenSolverType solver(inputMatrix, Eigen::EigenvaluesOnly);
728 auto eigenValues = solver.eigenvalues();
729 if (m_OrderEigenValues == EigenValueOrderEnum::OrderByMagnitude)
730 {
732 }
733 for (unsigned int i = 0; i < m_Dimension; ++i)
734 {
735 EigenValues[i] = eigenValues[i];
736 }
737 // No error code
738 return 1;
739 }
740};
741
742template <typename TMatrix, typename TVector, typename TEigenMatrix>
743std::ostream &
745{
746 os << "[ClassType: SymmetricEigenAnalysis]" << std::endl;
747 os << " Dimension : " << s.GetDimension() << std::endl;
748 os << " Order : " << s.GetOrder() << std::endl;
749 os << " OrderEigenValues: " << s.GetOrderEigenValues() << std::endl;
750 os << " OrderEigenMagnitudes: " << s.GetOrderEigenMagnitudes() << std::endl;
751 os << " UseEigenLibrary: " << s.GetUseEigenLibrary() << std::endl;
752 return os;
753}
754
755template <unsigned int VDimension, typename TMatrix, typename TVector, typename TEigenMatrix = TMatrix>
757{
758public:
759#if !defined(ITK_LEGACY_REMOVE)
761 using EigenValueOrderType = EigenValueOrderEnum;
762#endif
763#if !defined(ITK_LEGACY_REMOVE)
764 // We need to expose the enum values at the class level
765 // for backwards compatibility
766 static constexpr EigenValueOrderEnum OrderByValue = EigenValueOrderEnum::OrderByValue;
767 static constexpr EigenValueOrderEnum OrderByMagnitude = EigenValueOrderEnum::OrderByMagnitude;
768 static constexpr EigenValueOrderEnum DoNotOrder = EigenValueOrderEnum::DoNotOrder;
769#endif
770
771 using MatrixType = TMatrix;
772 using EigenMatrixType = TEigenMatrix;
773 using VectorType = TVector;
774
788 unsigned int
789 ComputeEigenValues(const TMatrix & A, TVector & EigenValues) const
790 {
791 return ComputeEigenValuesWithEigenLibraryImpl(A, EigenValues, true);
792 }
793
814 unsigned int
815 ComputeEigenValuesAndVectors(const TMatrix & A, TVector & EigenValues, TEigenMatrix & EigenVectors) const
816 {
817 return ComputeEigenValuesAndVectorsWithEigenLibraryImpl(A, EigenValues, EigenVectors, true);
818 }
819
820 void
822 {
823 if (b)
824 {
825 m_OrderEigenValues = EigenValueOrderEnum::OrderByValue;
826 }
827 else
828 {
829 m_OrderEigenValues = EigenValueOrderEnum::DoNotOrder;
830 }
831 }
832 [[nodiscard]] bool
834 {
835 return m_OrderEigenValues == EigenValueOrderEnum::OrderByValue;
836 }
837 void
839 {
840 if (b)
841 {
842 m_OrderEigenValues = EigenValueOrderEnum::OrderByMagnitude;
843 }
844 else
845 {
846 m_OrderEigenValues = EigenValueOrderEnum::DoNotOrder;
847 }
848 }
849 [[nodiscard]] bool
851 {
852 return m_OrderEigenValues == EigenValueOrderEnum::OrderByMagnitude;
853 }
854 [[nodiscard]] constexpr unsigned int
855 GetOrder() const
856 {
857 return VDimension;
858 }
859 [[nodiscard]] constexpr unsigned int
861 {
862 return VDimension;
863 }
864 [[nodiscard]] constexpr bool
866 {
867 return true;
868 }
869
870private:
871 EigenValueOrderEnum m_OrderEigenValues{ EigenValueOrderEnum::OrderByValue };
872
873 /* Helper to get the matrix value type for EigenLibMatrix typename.
874 *
875 * If the TMatrix is vnl, the type is in element_type.
876 * In TMatrix is itk::Matrix, or any itk::FixedArray is in ValueType.
877 *
878 * To use this function:
879 * using ValueType = decltype(this->GetMatrixType(true));
880 */
881 template <typename QMatrix = TMatrix>
882 auto
883 GetMatrixValueType(bool) const -> typename QMatrix::element_type
884 {
885 return QMatrix::element_type();
886 }
887 template <typename QMatrix = TMatrix>
888 [[nodiscard]] auto
889 GetMatrixValueType(bool) const -> typename QMatrix::ValueType
890 {
891 return QMatrix::ValueType();
892 }
893
894 /* Implementation detail using EigenLib that do not perform a copy.
895 * It needs the existence of a pointer to matrix data. \sa GetPointerToMatrixData
896 * If new types want to use this method, an appropriate overload of GetPointerToMatrixData
897 * should be included.
898 *
899 * @param (bool) implementation detail argument making this implementation the most favourable
900 * to be chosen from all the alternative implementations.
901 *
902 * @return an unsigned int with no information value (no error code in EigenLib) */
903 template <typename QMatrix>
904 auto
906 TVector & EigenValues,
907 TEigenMatrix & EigenVectors,
908 bool) const -> decltype(GetPointerToMatrixData(A), 1U)
909 {
910 auto pointerToData = GetPointerToMatrixData(A);
911 using PointerType = decltype(pointerToData);
912 using ValueTypeCV = std::remove_pointer_t<PointerType>;
913 using ValueType = std::remove_cv_t<ValueTypeCV>;
914 using EigenLibMatrixType = Eigen::Matrix<ValueType, VDimension, VDimension, Eigen::RowMajor>;
915 using EigenConstMatrixMap = Eigen::Map<const EigenLibMatrixType>;
916 EigenConstMatrixMap inputMatrix(pointerToData);
917 using EigenSolverType = Eigen::SelfAdjointEigenSolver<EigenLibMatrixType>;
918 EigenSolverType solver(inputMatrix); // Computes EigenValues and EigenVectors
919 const auto & eigenValues = solver.eigenvalues();
920 /* Column k of the returned matrix is an eigenvector corresponding to
921 * eigenvalue number $ k $ as returned by eigenvalues().
922 * The eigenvectors are normalized to have (Euclidean) norm equal to one. */
923 const auto & eigenVectors = solver.eigenvectors();
924 if (m_OrderEigenValues == EigenValueOrderEnum::OrderByMagnitude)
925 {
926 auto copyEigenValues = eigenValues;
927 auto copyEigenVectors = eigenVectors;
928 auto indicesSortPermutations = detail::sortEigenValuesByMagnitude(copyEigenValues, VDimension);
929 detail::permuteColumnsWithSortIndices(copyEigenVectors, indicesSortPermutations);
930 for (unsigned int row = 0; row < VDimension; ++row)
931 {
932 EigenValues[row] = copyEigenValues[row];
933 for (unsigned int col = 0; col < VDimension; ++col)
934 {
935 EigenVectors[row][col] = copyEigenVectors(col, row);
936 }
937 }
938 }
939 else
940 {
941 for (unsigned int row = 0; row < VDimension; ++row)
942 {
943 EigenValues[row] = eigenValues[row];
944 for (unsigned int col = 0; col < VDimension; ++col)
945 {
946 EigenVectors[row][col] = eigenVectors(col, row);
947 }
948 }
949 }
950 // No error code
951 return 1;
952 }
953
954 /* Implementation detail using EigenLib that performs a copy of the input matrix.
955 *
956 * @param (long) implementation detail argument making this implementation less favourable
957 * to be chosen if alternatives are available.
958 *
959 * @return an unsigned int with no information value (no error code in EigenLib) */
960 template <typename QMatrix>
961 auto
963 TVector & EigenValues,
964 TEigenMatrix & EigenVectors,
965 long) const -> decltype(1U)
966 {
967 using ValueType = decltype(GetMatrixValueType(true));
968 using EigenLibMatrixType = Eigen::Matrix<ValueType, VDimension, VDimension, Eigen::RowMajor>;
969 EigenLibMatrixType inputMatrix;
970 for (unsigned int row = 0; row < VDimension; ++row)
971 {
972 for (unsigned int col = 0; col < VDimension; ++col)
973 {
974 inputMatrix(row, col) = A(row, col);
975 }
976 }
977 using EigenSolverType = Eigen::SelfAdjointEigenSolver<EigenLibMatrixType>;
978 const EigenSolverType solver(inputMatrix); // Computes EigenValues and EigenVectors
979 const auto & eigenValues = solver.eigenvalues();
980 /* Column k of the returned matrix is an eigenvector corresponding to
981 * eigenvalue number $ k $ as returned by eigenvalues().
982 * The eigenvectors are normalized to have (Euclidean) norm equal to one. */
983 const auto & eigenVectors = solver.eigenvectors();
984
985 if (m_OrderEigenValues == EigenValueOrderEnum::OrderByMagnitude)
986 {
987 auto copyEigenValues = eigenValues;
988 auto copyEigenVectors = eigenVectors;
989 auto indicesSortPermutations = detail::sortEigenValuesByMagnitude(copyEigenValues, VDimension);
990 detail::permuteColumnsWithSortIndices(copyEigenVectors, indicesSortPermutations);
991
992 for (unsigned int row = 0; row < VDimension; ++row)
993 {
994 EigenValues[row] = copyEigenValues[row];
995 for (unsigned int col = 0; col < VDimension; ++col)
996 {
997 EigenVectors[row][col] = copyEigenVectors(col, row);
998 }
999 }
1000 }
1001 else
1002 {
1003 for (unsigned int row = 0; row < VDimension; ++row)
1004 {
1005 EigenValues[row] = eigenValues[row];
1006 for (unsigned int col = 0; col < VDimension; ++col)
1007 {
1008 EigenVectors[row][col] = eigenVectors(col, row);
1009 }
1010 }
1011 }
1012 // No error code
1013 return 1;
1014 }
1015
1016 /* Implementation detail using EigenLib that performs a copy of the input matrix.
1017 *
1018 * @param (long) implementation detail argument making this implementation less favourable
1019 * to be chosen if alternatives are available.
1020 *
1021 * @return an unsigned int with no information value (no error code in EigenLib) */
1022 template <typename QMatrix>
1023 auto
1024 ComputeEigenValuesWithEigenLibraryImpl(const QMatrix & A, TVector & EigenValues, long) const -> decltype(1U)
1025 {
1026 using ValueType = decltype(GetMatrixValueType(true));
1027 using EigenLibMatrixType = Eigen::Matrix<ValueType, VDimension, VDimension, Eigen::RowMajor>;
1028 EigenLibMatrixType inputMatrix;
1029 for (unsigned int row = 0; row < VDimension; ++row)
1030 {
1031 for (unsigned int col = 0; col < VDimension; ++col)
1032 {
1033 inputMatrix(row, col) = A(row, col);
1034 }
1035 }
1036 using EigenSolverType = Eigen::SelfAdjointEigenSolver<EigenLibMatrixType>;
1037 // GCC's IPA cannot prove that Eigen 5's analytical 3x3 SelfAdjointEigenSolver
1038 // fully writes m_eivalues; Eigen's own CMakeLists adds -Wno-maybe-uninitialized
1039 // for the same reason (see Modules/ThirdParty/Eigen3/.../CMakeLists.txt).
1040 // clang-format off
1041 ITK_GCC_PRAGMA_PUSH
1042 ITK_GCC_SUPPRESS_Wmaybe_uninitialized
1043 const EigenSolverType solver(inputMatrix, Eigen::EigenvaluesOnly);
1044 auto eigenValues = solver.eigenvalues();
1045 ITK_GCC_PRAGMA_POP
1046 // clang-format on
1047 if (m_OrderEigenValues == EigenValueOrderEnum::OrderByMagnitude)
1048 {
1049 detail::sortEigenValuesByMagnitude(eigenValues, VDimension);
1050 }
1051 for (unsigned int i = 0; i < VDimension; ++i)
1052 {
1053 EigenValues[i] = eigenValues[i];
1054 }
1055
1056 // No error code
1057 return 1;
1058 }
1059
1060 /* Implementation detail using EigenLib that do not perform a copy.
1061 * It needs the existence of a pointer to matrix data. \sa GetPointerToMatrixData
1062 * If new types want to use this method, an appropriate overload of GetPointerToMatrixData
1063 * should be included.
1064 *
1065 * @param (bool) implementation detail argument making this implementation the most favourable
1066 * to be chosen from all the alternative implementations.
1067 *
1068 * @return an unsigned int with no information value (no error code in EigenLib) */
1069 template <typename QMatrix>
1070 auto
1071 ComputeEigenValuesWithEigenLibraryImpl(const QMatrix & A, TVector & EigenValues, bool) const
1072 -> decltype(GetPointerToMatrixData(A), 1U)
1073 {
1074 auto pointerToData = GetPointerToMatrixData(A);
1075 using PointerType = decltype(pointerToData);
1076 using ValueTypeCV = std::remove_pointer_t<PointerType>;
1077 using ValueType = std::remove_cv_t<ValueTypeCV>;
1078 using EigenLibMatrixType = Eigen::Matrix<ValueType, VDimension, VDimension, Eigen::RowMajor>;
1079 using EigenConstMatrixMap = Eigen::Map<const EigenLibMatrixType>;
1080 EigenConstMatrixMap inputMatrix(pointerToData);
1081 using EigenSolverType = Eigen::SelfAdjointEigenSolver<EigenLibMatrixType>;
1082 EigenSolverType solver(inputMatrix, Eigen::EigenvaluesOnly);
1083 auto eigenValues = solver.eigenvalues();
1084 if (m_OrderEigenValues == EigenValueOrderEnum::OrderByMagnitude)
1085 {
1086 detail::sortEigenValuesByMagnitude(eigenValues, VDimension);
1087 }
1088 for (unsigned int i = 0; i < VDimension; ++i)
1089 {
1090 EigenValues[i] = eigenValues[i];
1091 }
1092 // No error code
1093 return 1;
1094 }
1095};
1096
1097template <unsigned int VDimension, typename TMatrix, typename TVector, typename TEigenMatrix>
1098std::ostream &
1099operator<<(std::ostream & os,
1101{
1102 os << "[ClassType: SymmetricEigenAnalysisFixedDimension]" << std::endl;
1103 os << " Dimension : " << s.GetDimension() << std::endl;
1104 os << " Order : " << s.GetOrder() << std::endl;
1105 os << " OrderEigenValues: " << s.GetOrderEigenValues() << std::endl;
1106 os << " OrderEigenMagnitudes: " << s.GetOrderEigenMagnitudes() << std::endl;
1107 os << " UseEigenLibrary: " << s.GetUseEigenLibrary() << std::endl;
1108 return os;
1109}
1110} // end namespace itk
1111
1112#ifndef ITK_MANUAL_INSTANTIATION
1113# include "itkSymmetricEigenAnalysis.hxx"
1114#endif
1115
1116#endif
A templated class holding a M x N size Matrix.
Definition itkMatrix.h:53
InternalMatrixType & GetVnlMatrix()
Definition itkMatrix.h:214
This class contains all enum classes used by SymmetricEigenAnalysis class.
auto GetMatrixValueType(bool) const -> typename QMatrix::element_type
auto ComputeEigenValuesWithEigenLibraryImpl(const QMatrix &A, EigenValuesArrayType &EigenValues, long) const -> decltype(1U)
unsigned int ComputeEigenValuesAndVectors(const TMatrix &A, TVector &EigenValues, TEigenMatrix &EigenVectors) const
auto ComputeEigenValuesWithEigenLibraryImpl(const QMatrix &A, TVector &EigenValues, bool) const -> decltype(GetPointerToMatrixData(A), 1U)
auto ComputeEigenValuesAndVectorsWithEigenLibraryImpl(const QMatrix &A, EigenValuesArrayType &EigenValues, EigenVectorsMatrixType &EigenVectors, bool) const -> decltype(GetPointerToMatrixData(A), 1U)
unsigned int ComputeEigenValues(const TMatrix &A, TVector &EigenValues) const
auto GetMatrixValueType(bool) const -> typename QMatrix::ValueType
auto ComputeEigenValuesAndVectorsWithEigenLibraryImpl(const QMatrix &A, TVector &EigenValues, TEigenMatrix &EigenVectors, long) const -> decltype(1U)
Find Eigen values of a real 2D symmetric matrix. It serves as a thread-safe alternative to the class:...
void SetDimension(const unsigned int n)
unsigned int ComputeEigenValuesAndVectors(const TMatrix &A, TVector &EigenValues, TEigenMatrix &EigenVectors) const
itk::EigenValueOrderEnum EigenValueOrderEnum
unsigned int ComputeEigenValuesLegacy(const TMatrix &A, TVector &D) const
unsigned int ComputeEigenValuesAndVectorsWithEigenLibrary(const TMatrix &A, TVector &EigenValues, TEigenMatrix &EigenVectors) const
unsigned int ComputeEigenValuesWithEigenLibrary(const TMatrix &A, TVector &EigenValues) const
unsigned int ComputeEigenValuesAndVectorsUsingQL(double *d, double *e, double *z) const
auto ComputeEigenValuesWithEigenLibraryImpl(const QMatrix &A, TVector &EigenValues, bool) const -> decltype(GetPointerToMatrixData(A), 1U)
unsigned int ComputeEigenValuesAndVectorsLegacy(const TMatrix &A, TVector &EigenValues, TEigenMatrix &EigenVectors) const
auto GetMatrixValueType(...) const -> typename QMatrix::ValueType
SymmetricEigenAnalysis(const unsigned int dimension)
void SetOrder(const unsigned int n)
auto ComputeEigenValuesAndVectorsWithEigenLibraryImpl(const QMatrix &A, TVector &EigenValues, TEigenMatrix &EigenVectors, bool) const -> decltype(GetPointerToMatrixData(A), 1U)
void SetUseEigenLibrary(const bool input)
unsigned int ComputeEigenValuesUsingQL(double *d, double *e) const
void ReduceToTridiagonalMatrixAndGetTransformation(const double *a, double *d, double *e, double *z) const
unsigned int ComputeEigenValues(const TMatrix &A, TVector &D) const
auto ComputeEigenValuesAndVectorsWithEigenLibraryImpl(const QMatrix &A, TOutput &EigenValues, TInput &EigenVectors, long) const -> decltype(1U)
auto ComputeEigenValuesWithEigenLibraryImpl(const QMatrix &A, TOutput &EigenValues, long) const -> decltype(1U)
void ReduceToTridiagonalMatrix(double *a, double *d, double *e, double *e2) const
auto GetMatrixValueType(bool) const -> typename QMatrix::element_type
constexpr auto Absolute(T x) noexcept
Returns the absolute value of a number.
Definition itkMath.h:1134
const TValueType * GetPointerToMatrixData(const vnl_matrix_fixed< TValueType, VRows, VColumns > &inputMatrix)
void permuteColumnsWithSortIndices(QMatrix &eigenVectors, const std::vector< int > &indicesSortPermutations)
std::vector< int > sortEigenValuesByMagnitude(TArray &eigenValues, const unsigned int numberOfElements)
The "itk" namespace contains all Insight Segmentation and Registration Toolkit (ITK) classes....
ITKCommon_EXPORT std::ostream & operator<<(std::ostream &out, AnatomicalOrientation::CoordinateEnum value)
SymmetricEigenAnalysisEnums::EigenValueOrder EigenValueOrderEnum
EigenValueOrderEnum Int2EigenValueOrderEnum(const uint8_t value)