From 6bcb07a77a5c7cf78a41a08c4dad9216fb6f592f Mon Sep 17 00:00:00 2001
From: Robert Bacula <bacularobert@gmail.com>
Date: Tue, 21 Jan 2025 17:10:55 +0200
Subject: [PATCH 1/2] svd now tests for NaN values

---
 src/linalg/svd.rs   | 32 ++++++++++++++++++++++++++------
 tests/linalg/svd.rs |  2 +-
 2 files changed, 27 insertions(+), 7 deletions(-)

diff --git a/src/linalg/svd.rs b/src/linalg/svd.rs
index fc5ed6b4d..4ce46e91a 100644
--- a/src/linalg/svd.rs
+++ b/src/linalg/svd.rs
@@ -119,21 +119,24 @@ where
         eps: T::RealField,
         max_niter: usize,
     ) -> Option<Self> {
+        // Ensure that the matrix is not empty before proceeding
         assert!(
             !matrix.is_empty(),
             "Cannot compute the SVD of an empty matrix."
         );
+
         let (nrows, ncols) = matrix.shape_generic();
         let min_nrows_ncols = nrows.min(ncols);
 
+        // Special cases for 2x2 and 3x3 matrices, handled by predefined methods
         if Self::use_special_always_ordered_svd2() {
-            // SAFETY: the reference transmutes are OK since we checked that the types match exactly.
+            // SAFETY: Reference transmutes are OK because the types match exactly
             let matrix: &Matrix2<T::RealField> = unsafe { std::mem::transmute(&matrix) };
             let result = super::svd2::svd_ordered2(matrix, compute_u, compute_v);
             let typed_result: &Self = unsafe { std::mem::transmute(&result) };
             return Some(typed_result.clone());
         } else if Self::use_special_always_ordered_svd3() {
-            // SAFETY: the reference transmutes are OK since we checked that the types match exactly.
+            // SAFETY: Reference transmutes are OK because the types match exactly
             let matrix: &Matrix3<T::RealField> = unsafe { std::mem::transmute(&matrix) };
             let result = super::svd3::svd_ordered3(matrix, compute_u, compute_v, eps, max_niter);
             let typed_result: &Self = unsafe { std::mem::transmute(&result) };
@@ -142,8 +145,10 @@ where
 
         let dim = min_nrows_ncols.value();
 
+        // Get the maximum absolute value of the matrix for normalization
         let m_amax = matrix.camax();
 
+        // If the max value is not zero, unscale the matrix by dividing by m_amax
         if !m_amax.is_zero() {
             matrix.unscale_mut(m_amax.clone());
         }
@@ -158,6 +163,16 @@ where
         let mut diagonal = bi_matrix.diagonal();
         let mut off_diagonal = bi_matrix.off_diagonal();
 
+        // **Check for NaN values in the diagonal elements**
+        // We check whether any singular value in the diagonal is NaN by comparing each value to itself.
+        // This works because NaN is the only value in Rust that is not equal to itself.
+        if diagonal.iter().any(|s| s != s) {
+            // If any singular value is NaN, return None early
+            // Explanation:
+            // NaN != NaN is always true, so if `s != s`, we know `s` is NaN.
+            return None; // Return early if NaN found
+        }
+
         let mut niter = 0;
         let (mut start, mut end) = Self::delimit_subproblem(
             &mut diagonal,
@@ -169,10 +184,11 @@ where
             eps.clone(),
         );
 
+        // Iterative SVD computation with Givens rotations
         while end != start {
             let subdim = end - start + 1;
 
-            // Solve the subproblem.
+            // Solve subproblem for larger subdimensions (> 2)
             #[allow(clippy::comparison_chain)]
             if subdim > 2 {
                 let m = end - 1;
@@ -184,19 +200,23 @@ where
                     let dn = diagonal[n].clone();
                     let fm = off_diagonal[m].clone();
 
+                    // Perform calculations to determine the shift value for Givens rotation
                     let tmm = dm.clone() * dm.clone()
                         + off_diagonal[m - 1].clone() * off_diagonal[m - 1].clone();
                     let tmn = dm * fm.clone();
                     let tnn = dn.clone() * dn + fm.clone() * fm;
 
+                    // Compute Wilkinson's shift
                     let shift = symmetric_eigen::wilkinson_shift(tmm, tnn, tmn);
 
+                    // Create vector for subsequent Givens rotations
                     vec = Vector2::new(
                         diagonal[start].clone() * diagonal[start].clone() - shift,
                         diagonal[start].clone() * off_diagonal[start].clone(),
                     );
                 }
 
+                // Perform Givens rotations to reduce the bidiagonal matrix
                 for k in start..n {
                     let m12 = if k == n - 1 {
                         T::RealField::zero()
@@ -224,7 +244,6 @@ where
                         }
 
                         let v = Vector2::new(subm[(0, 0)].clone(), subm[(1, 0)].clone());
-                        // TODO: does the case `v.y == 0` ever happen?
                         let (rot2, norm2) = GivensRotation::cancel_y(&v)
                             .unwrap_or((GivensRotation::identity(), subm[(0, 0)].clone()));
 
@@ -264,7 +283,7 @@ where
                     }
                 }
             } else if subdim == 2 {
-                // Solve the remaining 2x2 subproblem.
+                // Solve the 2x2 subproblem if subdim == 2
                 let (u2, s, v2) = compute_2x2_uptrig_svd(
                     diagonal[start].clone(),
                     off_diagonal[start].clone(),
@@ -321,9 +340,10 @@ where
             }
         }
 
+        // Unscale the singular values after SVD computation
         diagonal *= m_amax;
 
-        // Ensure all singular value are non-negative.
+        // Ensure all singular values are non-negative
         for i in 0..dim {
             let sval = diagonal[i].clone();
 
diff --git a/tests/linalg/svd.rs b/tests/linalg/svd.rs
index d8b23d024..94c34cb52 100644
--- a/tests/linalg/svd.rs
+++ b/tests/linalg/svd.rs
@@ -501,7 +501,7 @@ fn svd_regression_issue_1072() {
 }
 
 #[test]
-// Exercises bug reported in issue #1313 of nalgebra (https://github.com/dimforge/nalgebra/issues/1313)
+// Exercises bug reported in issue #1313 of nalgebra (https://github.   com/dimforge/nalgebra/issues/1313)
 fn svd_regression_issue_1313() {
     let s = 6.123234e-16_f32;
     let m = nalgebra::dmatrix![

From dad6334a217865f211c2fd58cdfba0ba98135ef1 Mon Sep 17 00:00:00 2001
From: Robert Bacula <bacularobert@gmail.com>
Date: Tue, 21 Jan 2025 17:13:18 +0200
Subject: [PATCH 2/2] format

---
 tests/linalg/svd.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/linalg/svd.rs b/tests/linalg/svd.rs
index 94c34cb52..d8b23d024 100644
--- a/tests/linalg/svd.rs
+++ b/tests/linalg/svd.rs
@@ -501,7 +501,7 @@ fn svd_regression_issue_1072() {
 }
 
 #[test]
-// Exercises bug reported in issue #1313 of nalgebra (https://github.   com/dimforge/nalgebra/issues/1313)
+// Exercises bug reported in issue #1313 of nalgebra (https://github.com/dimforge/nalgebra/issues/1313)
 fn svd_regression_issue_1313() {
     let s = 6.123234e-16_f32;
     let m = nalgebra::dmatrix![