@@ -56,34 +56,36 @@ typename V::non_const_value_type simpleNorm2(const V& v) {
5656
5757// Check that all columns of X are unit length and pairwise orthogonal
5858template <typename Mat>
59- void verifyOrthogonal (const Mat& X) {
60- using Scalar = typename Mat::non_const_value_type;
61- int k = X.extent (1 );
59+ void verifyOrthogonal (const Mat& X, const double epsilon = -1 ) {
60+ using Scalar = typename Mat::non_const_value_type;
61+ int k = X.extent (1 );
62+ const double tol = (epsilon <= 0 ? Test::svdEpsilon<Scalar>() : epsilon);
6263 for (int i = 0 ; i < k; i++) {
6364 auto col1 = Kokkos::subview (X, Kokkos::ALL (), i);
6465 double len = simpleNorm2 (col1);
65- Test::EXPECT_NEAR_KK (len, 1.0 , Test::svdEpsilon<Scalar>() );
66+ Test::EXPECT_NEAR_KK (len, 1.0 , tol );
6667 for (int j = 0 ; j < i; j++) {
6768 auto col2 = Kokkos::subview (X, Kokkos::ALL (), j);
6869 double d = Kokkos::ArithTraits<Scalar>::abs (simpleDot (col1, col2));
69- Test::EXPECT_NEAR_KK (d, 0.0 , Test::svdEpsilon<Scalar>() );
70+ Test::EXPECT_NEAR_KK (d, 0.0 , tol );
7071 }
7172 }
7273}
7374
7475template <typename AView, typename UView, typename VtView, typename SigmaView>
75- void verifySVD (const AView& A, const UView& U, const VtView& Vt, const SigmaView& sigma) {
76+ void verifySVD (const AView& A, const UView& U, const VtView& Vt, const SigmaView& sigma, const double epsilon = - 1 ) {
7677 using Scalar = typename AView::non_const_value_type;
7778 using KAT = Kokkos::ArithTraits<Scalar>;
78- // Check that U/V columns are unit length and orthogonal, and that U *
79- // diag(sigma) * V^T == A
80- int m = A.extent (0 );
81- int n = A.extent (1 );
82- int maxrank = std::min (m, n);
83- verifyOrthogonal (U);
79+ // Check that U/V columns are unit length and orthogonal
80+ // and that: U * diag(sigma) * V^T == A
81+ int m = A.extent (0 );
82+ int n = A.extent (1 );
83+ int maxrank = std::min (m, n);
84+ const double tol = (epsilon <= 0 ? Test::svdEpsilon<Scalar>() : epsilon);
85+ verifyOrthogonal (U, epsilon);
8486 // NOTE: V^T being square and orthonormal implies that V is, so we don't have
8587 // to transpose it here.
86- verifyOrthogonal (Vt);
88+ verifyOrthogonal (Vt, epsilon );
8789 Kokkos::View<Scalar**, typename AView::device_type> usvt (" USV^T" , m, n);
8890 for (int i = 0 ; i < maxrank; i++) {
8991 auto Ucol = Kokkos::subview (U, Kokkos::ALL (), Kokkos::make_pair<int >(i, i + 1 ));
@@ -92,7 +94,7 @@ void verifySVD(const AView& A, const UView& U, const VtView& Vt, const SigmaView
9294 }
9395 for (int i = 0 ; i < m; i++) {
9496 for (int j = 0 ; j < n; j++) {
95- Test::EXPECT_NEAR_KK (usvt (i, j), A (i, j), Test::svdEpsilon<Scalar>() );
97+ Test::EXPECT_NEAR_KK (usvt (i, j), A (i, j), tol );
9698 }
9799 }
98100 // Make sure all singular values are positive
@@ -109,19 +111,26 @@ template <typename Matrix, typename Vector>
109111struct SerialSVDFunctor_Full {
110112 SerialSVDFunctor_Full (const Matrix& A_, const Matrix& U_, const Matrix& Vt_, const Vector& sigma_,
111113 const Vector& work_)
112- : A(A_), U(U_), Vt(Vt_), sigma(sigma_), work(work_) {}
114+ : A(A_), U(U_), Vt(Vt_), sigma(sigma_), work(work_) {
115+ tol = Kokkos::ArithTraits<double >::zero ();
116+ }
117+
118+ SerialSVDFunctor_Full (const Matrix& A_, const Matrix& U_, const Matrix& Vt_, const Vector& sigma_,
119+ const Vector& work_, const double tol_)
120+ : A(A_), U(U_), Vt(Vt_), sigma(sigma_), work(work_), tol(tol_) {}
113121
114122 // NOTE: this functor is only meant to be launched with a single element range
115123 // policy
116124 KOKKOS_INLINE_FUNCTION void operator ()(int ) const {
117- KokkosBatched::SerialSVD::invoke (KokkosBatched::SVD_USV_Tag (), A, U, sigma, Vt, work);
125+ KokkosBatched::SerialSVD::invoke (KokkosBatched::SVD_USV_Tag (), A, U, sigma, Vt, work, tol );
118126 }
119127
120128 Matrix A;
121129 Matrix U;
122130 Matrix Vt;
123131 Vector sigma;
124132 Vector work;
133+ double tol;
125134};
126135
127136template <typename Matrix, typename Vector>
@@ -497,6 +506,27 @@ Kokkos::View<Scalar**, Layout, Device> getTestCase(int testCase) {
497506 Ahost = MatrixHost (" A5" , m, n);
498507 break ;
499508 }
509+ case 6 : {
510+ m = 3 ;
511+ n = 6 ;
512+ Ahost = MatrixHost (" A6" , m, n);
513+ Ahost (0 , 0 ) = -2.3588494081694974e-03 ;
514+ Ahost (0 , 1 ) = -2.3602176428346553e-03 ;
515+ Ahost (0 , 2 ) = -3.3360574050870077e-03 ;
516+ Ahost (0 , 3 ) = -2.3589487578561312e-03 ;
517+ Ahost (0 , 4 ) = -3.3359167956075490e-03 ;
518+ Ahost (0 , 5 ) = -3.3378517656821728e-03 ;
519+ Ahost (1 , 0 ) = 3.3359168246290603e-03 ;
520+ Ahost (1 , 1 ) = 3.3378518006490351e-03 ;
521+ Ahost (1 , 3 ) = 3.3360573263032968e-03 ;
522+ Ahost (2 , 0 ) = -2.3588494081695022e-03 ;
523+ Ahost (2 , 1 ) = -2.3602176428346587e-03 ;
524+ Ahost (2 , 2 ) = 3.3360574050869769e-03 ;
525+ Ahost (2 , 3 ) = -2.3589487578561286e-03 ;
526+ Ahost (2 , 4 ) = 3.3359167956075399e-03 ;
527+ Ahost (2 , 5 ) = 3.3378517656821581e-03 ;
528+ break ;
529+ }
500530 default : throw std::runtime_error (" Test case out of bounds." );
501531 }
502532 Kokkos::View<Scalar**, Layout, Device> A (Ahost.label (), m, n);
@@ -509,7 +539,7 @@ void testSpecialCases() {
509539 using Matrix = Kokkos::View<Scalar**, Layout, Device>;
510540 using Vector = Kokkos::View<Scalar*, Device>;
511541 using ExecSpace = typename Device::execution_space;
512- for (int i = 0 ; i < 6 ; i++) {
542+ for (int i = 0 ; i < 7 ; i++) {
513543 Matrix A = getTestCase<Scalar, Layout, Device>(i);
514544 int m = A.extent (0 );
515545 int n = A.extent (1 );
@@ -527,15 +557,24 @@ void testSpecialCases() {
527557 typename Matrix::host_mirror_type Acopy (" Acopy" , m, n);
528558 Kokkos::deep_copy (Acopy, A);
529559 // Run the SVD
530- Kokkos::parallel_for (Kokkos::RangePolicy<ExecSpace>(0 , 1 ),
531- SerialSVDFunctor_Full<Matrix, Vector>(A, U, Vt, sigma, work));
560+ if (std::is_same_v<Scalar, double > && i == 6 ) {
561+ Kokkos::parallel_for (Kokkos::RangePolicy<ExecSpace>(0 , 1 ),
562+ SerialSVDFunctor_Full<Matrix, Vector>(A, U, Vt, sigma, work, 1e-9 ));
563+ } else {
564+ Kokkos::parallel_for (Kokkos::RangePolicy<ExecSpace>(0 , 1 ),
565+ SerialSVDFunctor_Full<Matrix, Vector>(A, U, Vt, sigma, work));
566+ }
532567 // Get the results back
533568 auto Uhost = Kokkos::create_mirror_view_and_copy (Kokkos::HostSpace (), U);
534569 auto Vthost = Kokkos::create_mirror_view_and_copy (Kokkos::HostSpace (), Vt);
535570 auto sigmaHost = Kokkos::create_mirror_view_and_copy (Kokkos::HostSpace (), sigma);
536571
537572 // Verify the SVD is correct
538- verifySVD (Acopy, Uhost, Vthost, sigmaHost);
573+ if (std::is_same_v<Scalar, double > && i == 6 ) {
574+ verifySVD (Acopy, Uhost, Vthost, sigmaHost, 1e-11 );
575+ } else {
576+ verifySVD (Acopy, Uhost, Vthost, sigmaHost);
577+ }
539578 }
540579}
541580
0 commit comments