root / tmp / org.txm.analec.rcp / src / JamaPlus / SingularValueDecomposition.java @ 673
Historique | Voir | Annoter | Télécharger (16,18 ko)
| 1 | package JamaPlus; | 
|---|---|
| 2 | import JamaPlus.util.*; | 
| 3 |  | 
| 4 |    /** Singular Value Decomposition.
 | 
| 5 |    <P>
 | 
| 6 |    For an m-by-n matrix A with m >= n, the singular value decomposition is
 | 
| 7 |    an m-by-n orthogonal matrix U, an n-by-n diagonal matrix S, and
 | 
| 8 |    an n-by-n orthogonal matrix V so that A = U*S*V'.
 | 
| 9 |    <P>
 | 
| 10 |    The singular values, sigma[k] = S[k][k], are ordered so that
 | 
| 11 |    sigma[0] >= sigma[1] >= ... >= sigma[n-1].
 | 
| 12 |    <P>
 | 
| 13 |    The singular value decompostion always exists, so the constructor will
 | 
| 14 |    never fail.  The matrix condition number and the effective numerical
 | 
| 15 |    rank can be computed from this decomposition.
 | 
| 16 |    */
 | 
| 17 |  | 
| 18 | public class SingularValueDecomposition implements java.io.Serializable { | 
| 19 |  | 
| 20 | /* ------------------------
 | 
| 21 |    Class variables
 | 
| 22 |  * ------------------------ */
 | 
| 23 |  | 
| 24 |    /** Arrays for internal storage of U and V.
 | 
| 25 |    @serial internal storage of U.
 | 
| 26 |    @serial internal storage of V.
 | 
| 27 |    */
 | 
| 28 | private double[][] U, V; | 
| 29 |  | 
| 30 |    /** Array for internal storage of singular values.
 | 
| 31 |    @serial internal storage of singular values.
 | 
| 32 |    */
 | 
| 33 | private double[] s; | 
| 34 |  | 
| 35 |    /** Row and column dimensions.
 | 
| 36 |    @serial row dimension.
 | 
| 37 |    @serial column dimension.
 | 
| 38 |    */
 | 
| 39 | private int m, n; | 
| 40 |  | 
| 41 | private boolean transpose; // pour éviter les bugs quand m<n (cf.ci-dessous) | 
| 42 |  | 
| 43 | /* ------------------------
 | 
| 44 |    Constructor
 | 
| 45 |  * ------------------------ */
 | 
| 46 |  | 
| 47 |    /** Construct the singular value decomposition
 | 
| 48 |    @param A    Rectangular matrix
 | 
| 49 |    @return     Structure to access U, S and V.
 | 
| 50 |    */
 | 
| 51 |  | 
| 52 |    public SingularValueDecomposition (Matrix Arg) {
 | 
| 53 |  | 
| 54 |       // Derived from LINPACK code.
 | 
| 55 |       // Initialize.
 | 
| 56 |       /* Apparently the failing cases are only a proper subset of (m<n), 
 | 
| 57 |          so let's not throw error.  Correct fix to come later?
 | 
| 58 |       if (m<n) {
 | 
| 59 |           throw new IllegalArgumentException("Jama SVD only works for m >= n"); }
 | 
| 60 |       */
 | 
| 61 |       /* 
 | 
| 62 |        * Une façon très simple de contourner le problème ci-dessus :
 | 
| 63 |        * si m < n on transpose A
 | 
| 64 |        * (ce qui revient à échanger U et V, S restant inchangé)
 | 
| 65 |        */
 | 
| 66 | transpose = Arg.getColumnDimension()>Arg.getRowDimension(); | 
| 67 | Matrix M = transpose ? Arg.transpose() : Arg.copy(); | 
| 68 |  | 
| 69 | double[][] A = M.getArray(); | 
| 70 | m = M.getRowDimension(); | 
| 71 | n = M.getColumnDimension(); | 
| 72 |  | 
| 73 | s = new double [n]; | 
| 74 | U = new double [m][n]; | 
| 75 | V = new double [n][n]; | 
| 76 | double[] e = new double [n]; | 
| 77 | double[] work = new double [m]; | 
| 78 | boolean wantu = true; | 
| 79 | boolean wantv = true; | 
| 80 |  | 
| 81 |       // Reduce A to bidiagonal form, storing the diagonal elements
 | 
| 82 |       // in s and the super-diagonal elements in e.
 | 
| 83 |  | 
| 84 | int nct = Math.min(m-1,n); | 
| 85 | int nrt = Math.max(0,Math.min(n-2,m)); | 
| 86 | for (int k = 0; k < Math.max(nct,nrt); k++) { | 
| 87 |          if (k < nct) {
 | 
| 88 |  | 
| 89 |             // Compute the transformation for the k-th column and
 | 
| 90 |             // place the k-th diagonal in s[k].
 | 
| 91 |             // Compute 2-norm of k-th column without under/overflow.
 | 
| 92 |             s[k] = 0;
 | 
| 93 | for (int i = k; i < m; i++) { | 
| 94 |                s[k] = Math.hypot(s[k],A[i][k]);
 | 
| 95 | } | 
| 96 | if (s[k] != 0.0) { | 
| 97 | if (A[k][k] < 0.0) { | 
| 98 | s[k] = -s[k]; | 
| 99 | } | 
| 100 | for (int i = k; i < m; i++) { | 
| 101 | A[i][k] /= s[k]; | 
| 102 | } | 
| 103 |                A[k][k] += 1.0;
 | 
| 104 | } | 
| 105 | s[k] = -s[k]; | 
| 106 | } | 
| 107 | for (int j = k+1; j < n; j++) { | 
| 108 | if ((k < nct) & (s[k] != 0.0)) { | 
| 109 |  | 
| 110 |             // Apply the transformation.
 | 
| 111 |  | 
| 112 | double t = 0; | 
| 113 | for (int i = k; i < m; i++) { | 
| 114 | t += A[i][k]*A[i][j]; | 
| 115 | } | 
| 116 | t = -t/A[k][k]; | 
| 117 | for (int i = k; i < m; i++) { | 
| 118 | A[i][j] += t*A[i][k]; | 
| 119 | } | 
| 120 | } | 
| 121 |  | 
| 122 |             // Place the k-th row of A into e for the
 | 
| 123 |             // subsequent calculation of the row transformation.
 | 
| 124 |  | 
| 125 | e[j] = A[k][j]; | 
| 126 | } | 
| 127 |          if (wantu & (k < nct)) {
 | 
| 128 |  | 
| 129 |             // Place the transformation in U for subsequent back
 | 
| 130 |             // multiplication.
 | 
| 131 |  | 
| 132 | for (int i = k; i < m; i++) { | 
| 133 | U[i][k] = A[i][k]; | 
| 134 | } | 
| 135 | } | 
| 136 |          if (k < nrt) {
 | 
| 137 |  | 
| 138 |             // Compute the k-th row transformation and place the
 | 
| 139 |             // k-th super-diagonal in e[k].
 | 
| 140 |             // Compute 2-norm without under/overflow.
 | 
| 141 |             e[k] = 0;
 | 
| 142 | for (int i = k+1; i < n; i++) { | 
| 143 |                e[k] = Math.hypot(e[k],e[i]);
 | 
| 144 | } | 
| 145 | if (e[k] != 0.0) { | 
| 146 | if (e[k+1] < 0.0) { | 
| 147 | e[k] = -e[k]; | 
| 148 | } | 
| 149 | for (int i = k+1; i < n; i++) { | 
| 150 | e[i] /= e[k]; | 
| 151 | } | 
| 152 | e[k+1] += 1.0; | 
| 153 | } | 
| 154 | e[k] = -e[k]; | 
| 155 | if ((k+1 < m) & (e[k] != 0.0)) { | 
| 156 |  | 
| 157 |             // Apply the transformation.
 | 
| 158 |  | 
| 159 | for (int i = k+1; i < m; i++) { | 
| 160 |                   work[i] = 0.0;
 | 
| 161 | } | 
| 162 | for (int j = k+1; j < n; j++) { | 
| 163 | for (int i = k+1; i < m; i++) { | 
| 164 | work[i] += e[j]*A[i][j]; | 
| 165 | } | 
| 166 | } | 
| 167 | for (int j = k+1; j < n; j++) { | 
| 168 | double t = -e[j]/e[k+1]; | 
| 169 | for (int i = k+1; i < m; i++) { | 
| 170 | A[i][j] += t*work[i]; | 
| 171 | } | 
| 172 | } | 
| 173 | } | 
| 174 |             if (wantv) {
 | 
| 175 |  | 
| 176 |             // Place the transformation in V for subsequent
 | 
| 177 |             // back multiplication.
 | 
| 178 |  | 
| 179 | for (int i = k+1; i < n; i++) { | 
| 180 | V[i][k] = e[i]; | 
| 181 | } | 
| 182 | } | 
| 183 | } | 
| 184 | } | 
| 185 |  | 
| 186 |       // Set up the final bidiagonal matrix or order p.
 | 
| 187 |  | 
| 188 | int p = Math.min(n,m+1); | 
| 189 |       if (nct < n) {
 | 
| 190 | s[nct] = A[nct][nct]; | 
| 191 | } | 
| 192 |       if (m < p) {
 | 
| 193 | s[p-1] = 0.0; | 
| 194 | } | 
| 195 | if (nrt+1 < p) { | 
| 196 |          e[nrt] = A[nrt][p-1];
 | 
| 197 | } | 
| 198 | e[p-1] = 0.0; | 
| 199 |  | 
| 200 |       // If required, generate U.
 | 
| 201 |  | 
| 202 |       if (wantu) {
 | 
| 203 | for (int j = nct; j < n; j++) { | 
| 204 | for (int i = 0; i < m; i++) { | 
| 205 |                U[i][j] = 0.0;
 | 
| 206 | } | 
| 207 |             U[j][j] = 1.0;
 | 
| 208 | } | 
| 209 | for (int k = nct-1; k >= 0; k--) { | 
| 210 | if (s[k] != 0.0) { | 
| 211 | for (int j = k+1; j < n; j++) { | 
| 212 | double t = 0; | 
| 213 | for (int i = k; i < m; i++) { | 
| 214 | t += U[i][k]*U[i][j]; | 
| 215 | } | 
| 216 | t = -t/U[k][k]; | 
| 217 | for (int i = k; i < m; i++) { | 
| 218 | U[i][j] += t*U[i][k]; | 
| 219 | } | 
| 220 | } | 
| 221 | for (int i = k; i < m; i++ ) { | 
| 222 | U[i][k] = -U[i][k]; | 
| 223 | } | 
| 224 |                U[k][k] = 1.0 + U[k][k];
 | 
| 225 | for (int i = 0; i < k-1; i++) { | 
| 226 |                   U[i][k] = 0.0;
 | 
| 227 | } | 
| 228 |             } else {
 | 
| 229 | for (int i = 0; i < m; i++) { | 
| 230 |                   U[i][k] = 0.0;
 | 
| 231 | } | 
| 232 |                U[k][k] = 1.0;
 | 
| 233 | } | 
| 234 | } | 
| 235 | } | 
| 236 |  | 
| 237 |       // If required, generate V.
 | 
| 238 |  | 
| 239 |       if (wantv) {
 | 
| 240 | for (int k = n-1; k >= 0; k--) { | 
| 241 | if ((k < nrt) & (e[k] != 0.0)) { | 
| 242 | for (int j = k+1; j < n; j++) { | 
| 243 | double t = 0; | 
| 244 | for (int i = k+1; i < n; i++) { | 
| 245 | t += V[i][k]*V[i][j]; | 
| 246 | } | 
| 247 |                   t = -t/V[k+1][k];
 | 
| 248 | for (int i = k+1; i < n; i++) { | 
| 249 | V[i][j] += t*V[i][k]; | 
| 250 | } | 
| 251 | } | 
| 252 | } | 
| 253 | for (int i = 0; i < n; i++) { | 
| 254 |                V[i][k] = 0.0;
 | 
| 255 | } | 
| 256 |             V[k][k] = 1.0;
 | 
| 257 | } | 
| 258 | } | 
| 259 |  | 
| 260 |       // Main iteration loop for the singular values.
 | 
| 261 |  | 
| 262 | int pp = p-1; | 
| 263 | int iter = 0; | 
| 264 | double eps = Math.pow(2.0,-52.0); | 
| 265 | double tiny = Math.pow(2.0,-966.0); | 
| 266 | while (p > 0) { | 
| 267 |          int k,kase;
 | 
| 268 |  | 
| 269 |          // Here is where a test for too many iterations would go.
 | 
| 270 |  | 
| 271 |          // This section of the program inspects for
 | 
| 272 |          // negligible elements in the s and e arrays.  On
 | 
| 273 |          // completion the variables kase and k are set as follows.
 | 
| 274 |  | 
| 275 |          // kase = 1     if s(p) and e[k-1] are negligible and k<p
 | 
| 276 |          // kase = 2     if s(k) is negligible and k<p
 | 
| 277 |          // kase = 3     if e[k-1] is negligible, k<p, and
 | 
| 278 |          //              s(k), ..., s(p) are not negligible (qr step).
 | 
| 279 |          // kase = 4     if e(p-1) is negligible (convergence).
 | 
| 280 |  | 
| 281 | for (k = p-2; k >= -1; k--) { | 
| 282 | if (k == -1) { | 
| 283 |                break;
 | 
| 284 | } | 
| 285 | if (Math.abs(e[k]) <= | 
| 286 | tiny + eps*(Math.abs(s[k]) + Math.abs(s[k+1]))) { | 
| 287 |                e[k] = 0.0;
 | 
| 288 |                break;
 | 
| 289 | } | 
| 290 | } | 
| 291 | if (k == p-2) { | 
| 292 |             kase = 4;
 | 
| 293 |          } else {
 | 
| 294 |             int ks;
 | 
| 295 | for (ks = p-1; ks >= k; ks--) { | 
| 296 |                if (ks == k) {
 | 
| 297 |                   break;
 | 
| 298 | } | 
| 299 | double t = (ks != p ? Math.abs(e[ks]) : 0.) + | 
| 300 | (ks != k+1 ? Math.abs(e[ks-1]) : 0.); | 
| 301 | if (Math.abs(s[ks]) <= tiny + eps*t) { | 
| 302 |                   s[ks] = 0.0;
 | 
| 303 |                   break;
 | 
| 304 | } | 
| 305 | } | 
| 306 |             if (ks == k) {
 | 
| 307 |                kase = 3;
 | 
| 308 | } else if (ks == p-1) { | 
| 309 |                kase = 1;
 | 
| 310 |             } else {
 | 
| 311 |                kase = 2;
 | 
| 312 | k = ks; | 
| 313 | } | 
| 314 | } | 
| 315 | k++; | 
| 316 |  | 
| 317 |          // Perform the task indicated by kase.
 | 
| 318 |  | 
| 319 |          switch (kase) {
 | 
| 320 |  | 
| 321 |             // Deflate negligible s(p).
 | 
| 322 |  | 
| 323 | case 1: { | 
| 324 | double f = e[p-2]; | 
| 325 | e[p-2] = 0.0; | 
| 326 | for (int j = p-2; j >= k; j--) { | 
| 327 | double t = Math.hypot(s[j],f); | 
| 328 |                   double cs = s[j]/t;
 | 
| 329 |                   double sn = f/t;
 | 
| 330 | s[j] = t; | 
| 331 |                   if (j != k) {
 | 
| 332 |                      f = -sn*e[j-1];
 | 
| 333 | e[j-1] = cs*e[j-1]; | 
| 334 | } | 
| 335 |                   if (wantv) {
 | 
| 336 | for (int i = 0; i < n; i++) { | 
| 337 |                         t = cs*V[i][j] + sn*V[i][p-1];
 | 
| 338 | V[i][p-1] = -sn*V[i][j] + cs*V[i][p-1]; | 
| 339 | V[i][j] = t; | 
| 340 | } | 
| 341 | } | 
| 342 | } | 
| 343 | } | 
| 344 |             break;
 | 
| 345 |  | 
| 346 |             // Split at negligible s(k).
 | 
| 347 |  | 
| 348 | case 2: { | 
| 349 | double f = e[k-1]; | 
| 350 | e[k-1] = 0.0; | 
| 351 | for (int j = k; j < p; j++) { | 
| 352 | double t = Math.hypot(s[j],f); | 
| 353 |                   double cs = s[j]/t;
 | 
| 354 |                   double sn = f/t;
 | 
| 355 | s[j] = t; | 
| 356 | f = -sn*e[j]; | 
| 357 | e[j] = cs*e[j]; | 
| 358 |                   if (wantu) {
 | 
| 359 | for (int i = 0; i < m; i++) { | 
| 360 |                         t = cs*U[i][j] + sn*U[i][k-1];
 | 
| 361 | U[i][k-1] = -sn*U[i][j] + cs*U[i][k-1]; | 
| 362 | U[i][j] = t; | 
| 363 | } | 
| 364 | } | 
| 365 | } | 
| 366 | } | 
| 367 |             break;
 | 
| 368 |  | 
| 369 |             // Perform one qr step.
 | 
| 370 |  | 
| 371 | case 3: { | 
| 372 |  | 
| 373 |                // Calculate the shift.
 | 
| 374 |  | 
| 375 | double scale = Math.max(Math.max(Math.max(Math.max( | 
| 376 | Math.abs(s[p-1]),Math.abs(s[p-2])),Math.abs(e[p-2])), | 
| 377 | Math.abs(s[k])),Math.abs(e[k])); | 
| 378 | double sp = s[p-1]/scale; | 
| 379 | double spm1 = s[p-2]/scale; | 
| 380 | double epm1 = e[p-2]/scale; | 
| 381 |                double sk = s[k]/scale;
 | 
| 382 |                double ek = e[k]/scale;
 | 
| 383 | double b = ((spm1 + sp)*(spm1 - sp) + epm1*epm1)/2.0; | 
| 384 |                double c = (sp*epm1)*(sp*epm1);
 | 
| 385 | double shift = 0.0; | 
| 386 | if ((b != 0.0) | (c != 0.0)) { | 
| 387 |                   shift = Math.sqrt(b*b + c);
 | 
| 388 | if (b < 0.0) { | 
| 389 | shift = -shift; | 
| 390 | } | 
| 391 | shift = c/(b + shift); | 
| 392 | } | 
| 393 |                double f = (sk + sp)*(sk - sp) + shift;
 | 
| 394 |                double g = sk*ek;
 | 
| 395 |  | 
| 396 |                // Chase zeros.
 | 
| 397 |  | 
| 398 | for (int j = k; j < p-1; j++) { | 
| 399 | double t = Math.hypot(f,g); | 
| 400 |                   double cs = f/t;
 | 
| 401 |                   double sn = g/t;
 | 
| 402 |                   if (j != k) {
 | 
| 403 |                      e[j-1] = t;
 | 
| 404 | } | 
| 405 | f = cs*s[j] + sn*e[j]; | 
| 406 | e[j] = cs*e[j] - sn*s[j]; | 
| 407 |                   g = sn*s[j+1];
 | 
| 408 | s[j+1] = cs*s[j+1]; | 
| 409 |                   if (wantv) {
 | 
| 410 | for (int i = 0; i < n; i++) { | 
| 411 |                         t = cs*V[i][j] + sn*V[i][j+1];
 | 
| 412 | V[i][j+1] = -sn*V[i][j] + cs*V[i][j+1]; | 
| 413 | V[i][j] = t; | 
| 414 | } | 
| 415 | } | 
| 416 |                   t = Math.hypot(f,g);
 | 
| 417 | cs = f/t; | 
| 418 | sn = g/t; | 
| 419 | s[j] = t; | 
| 420 |                   f = cs*e[j] + sn*s[j+1];
 | 
| 421 | s[j+1] = -sn*e[j] + cs*s[j+1]; | 
| 422 |                   g = sn*e[j+1];
 | 
| 423 | e[j+1] = cs*e[j+1]; | 
| 424 | if (wantu && (j < m-1)) { | 
| 425 | for (int i = 0; i < m; i++) { | 
| 426 |                         t = cs*U[i][j] + sn*U[i][j+1];
 | 
| 427 | U[i][j+1] = -sn*U[i][j] + cs*U[i][j+1]; | 
| 428 | U[i][j] = t; | 
| 429 | } | 
| 430 | } | 
| 431 | } | 
| 432 |                e[p-2] = f;
 | 
| 433 |                iter = iter + 1;
 | 
| 434 | } | 
| 435 |             break;
 | 
| 436 |  | 
| 437 |             // Convergence.
 | 
| 438 |  | 
| 439 | case 4: { | 
| 440 |  | 
| 441 |                // Make the singular values positive.
 | 
| 442 |  | 
| 443 | if (s[k] <= 0.0) { | 
| 444 | s[k] = (s[k] < 0.0 ? -s[k] : 0.0); | 
| 445 |                   if (wantv) {
 | 
| 446 | for (int i = 0; i <= pp; i++) { | 
| 447 | V[i][k] = -V[i][k]; | 
| 448 | } | 
| 449 | } | 
| 450 | } | 
| 451 |  | 
| 452 |                // Order the singular values.
 | 
| 453 |  | 
| 454 |                while (k < pp) {
 | 
| 455 | if (s[k] >= s[k+1]) { | 
| 456 |                      break;
 | 
| 457 | } | 
| 458 |                   double t = s[k];
 | 
| 459 |                   s[k] = s[k+1];
 | 
| 460 |                   s[k+1] = t;
 | 
| 461 | if (wantv && (k < n-1)) { | 
| 462 | for (int i = 0; i < n; i++) { | 
| 463 | t = V[i][k+1]; V[i][k+1] = V[i][k]; V[i][k] = t; | 
| 464 | } | 
| 465 | } | 
| 466 | if (wantu && (k < m-1)) { | 
| 467 | for (int i = 0; i < m; i++) { | 
| 468 | t = U[i][k+1]; U[i][k+1] = U[i][k]; U[i][k] = t; | 
| 469 | } | 
| 470 | } | 
| 471 | k++; | 
| 472 | } | 
| 473 |                iter = 0;
 | 
| 474 | p--; | 
| 475 | } | 
| 476 |             break;
 | 
| 477 | } | 
| 478 | } | 
| 479 | } | 
| 480 |  | 
| 481 | /* ------------------------
 | 
| 482 |    Public Methods
 | 
| 483 |  * ------------------------ */
 | 
| 484 |  | 
| 485 |    /** Return the left singular vectors
 | 
| 486 |    @return     U
 | 
| 487 |    */
 | 
| 488 |  | 
| 489 |    public Matrix getU () {
 | 
| 490 | return transpose ? new Matrix(V,n,n) : new Matrix(U,m,n); | 
| 491 | } | 
| 492 |  | 
| 493 |    /** Return the right singular vectors
 | 
| 494 |    @return     V
 | 
| 495 |    */
 | 
| 496 |  | 
| 497 |    public Matrix getV () {
 | 
| 498 | return transpose ? new Matrix(U,m,n) : new Matrix(V,n,n); | 
| 499 | } | 
| 500 |  | 
| 501 |    /** Return the one-dimensional array of singular values
 | 
| 502 |    @return     diagonal of S.
 | 
| 503 |    */
 | 
| 504 |  | 
| 505 | public double[] getSingularValues () { | 
| 506 |       return s;
 | 
| 507 | } | 
| 508 |  | 
| 509 |    /** Return the diagonal matrix of singular values
 | 
| 510 |    @return     S
 | 
| 511 |    */
 | 
| 512 |  | 
| 513 |    public Matrix getS () {
 | 
| 514 |       Matrix X = new Matrix(n,n);
 | 
| 515 | double[][] S = X.getArray(); | 
| 516 | for (int i = 0; i < n; i++) { | 
| 517 | for (int j = 0; j < n; j++) { | 
| 518 |             S[i][j] = 0.0;
 | 
| 519 | } | 
| 520 |          S[i][i] = this.s[i];
 | 
| 521 | } | 
| 522 |       return X;
 | 
| 523 | } | 
| 524 |  | 
| 525 |    /** Two norm
 | 
| 526 |    @return     max(S)
 | 
| 527 |    */
 | 
| 528 |  | 
| 529 | public double norm2 () { | 
| 530 | return s[0]; | 
| 531 | } | 
| 532 |  | 
| 533 |    /** Two norm condition number
 | 
| 534 |    @return     max(S)/min(S)
 | 
| 535 |    */
 | 
| 536 |  | 
| 537 | public double cond () { | 
| 538 | return s[0]/s[Math.min(m,n)-1]; | 
| 539 | } | 
| 540 |  | 
| 541 |    /** Effective numerical matrix rank
 | 
| 542 |    @return     Number of nonnegligible singular values.
 | 
| 543 |    */
 | 
| 544 |  | 
| 545 | public int rank () { | 
| 546 | double eps = Math.pow(2.0,-52.0); | 
| 547 | double tol = Math.max(m,n)*s[0]*eps; | 
| 548 | int r = 0; | 
| 549 | for (int i = 0; i < s.length; i++) { | 
| 550 |          if (s[i] > tol) {
 | 
| 551 | r++; | 
| 552 | } | 
| 553 | } | 
| 554 |       return r;
 | 
| 555 | } | 
| 556 | } |