private unsafe void init(MatrixFixed M, float zero_out_tol) { m_ = M.Rows; n_ = M.Columns; U_ = new MatrixFixed(m_, n_); W_ = new DiagMatrix(n_); Winverse_ = new DiagMatrix(n_); V_ = new MatrixFixed(n_, n_); //assert(m_ > 0); //assert(n_ > 0); int n = M.Rows; int p = M.Columns; int mm = Netlib.min(n+1,p); // Copy source matrix into fortran storage // SVD is slow, don't worry about the cost of this transpose. Vector X = Vector.fortran_copy(M); // Make workspace vectors Vector work = new Vector(n); work.Fill(0); Vector uspace = new Vector(n*p); uspace.Fill(0); Vector vspace = new Vector(p*p); vspace.Fill(0); Vector wspace = new Vector(mm); wspace.Fill(0); // complex fortran routine actually _wants_ complex W! Vector espace = new Vector(p); espace.Fill(0); // Call Linpack SVD int info = 0; int job = 21; fixed (float* data = X.Datablock()) { fixed (float* data2 = wspace.Datablock()) { fixed (float* data3 = espace.Datablock()) { fixed (float* data4 = uspace.Datablock()) { fixed (float* data5 = vspace.Datablock()) { fixed (float* data6 = work.Datablock()) { Netlib.dsvdc_(data, &n, &n, &p, data2, data3, data4, &n, data5, &p, data6, &job, &info); } } } } } } // Error return? if (info != 0) { // If info is non-zero, it contains the number of singular values // for this the SVD algorithm failed to converge. The condition is // not bogus. Even if the returned singular values are sensible, // the singular vectors can be utterly wrong. // It is possible the failure was due to NaNs or infinities in the // matrix. Check for that now. M.assert_finite(); // If we get here it might be because // 1. The scalar type has such // extreme precision that too few iterations were performed to // converge to within machine precision (that is the svdc criterion). // One solution to that is to increase the maximum number of // iterations in the netlib code. // // 2. The LINPACK dsvdc_ code expects correct IEEE rounding behaviour, // which some platforms (notably x86 processors) // have trouble doing. For example, gcc can output // code in -O2 and static-linked code that causes this problem. // One solution to this is to persuade gcc to output slightly different code // by adding and -fPIC option to the command line for v3p\netlib\dsvdc.c. If // that doesn't work try adding -ffloat-store, which should fix the problem // at the expense of being significantly slower for big problems. Note that // if this is the cause, vxl/vnl/tests/test_svd should have failed. // // You may be able to diagnose the problem here by printing a warning message. Debug.WriteLine("__FILE__ : suspicious return value (" + Convert.ToString(info) + ") from SVDC" + "__FILE__ : M is " + Convert.ToString(M.Rows) + "x" + Convert.ToString(M.Columns)); valid_ = false; } else valid_ = true; // Copy fortran outputs into our storage int ctr = 0; for (int j = 0; j < p; ++j) for (int i = 0; i < n; ++i) { U_[i, j] = uspace[ctr]; ctr++; } for (int j = 0; j < mm; ++j) W_[j, j] = Math.Abs(wspace[j]); // we get rid of complexness here. for (int j = mm; j < n_; ++j) W_[j, j] = 0; ctr = 0; for (int j = 0; j < p; ++j) for (int i = 0; i < p; ++i) { V_[i, j] = vspace[ctr]; ctr++; } //if (test_heavily) { // Test that recomposed matrix == M //float recomposition_residual = Math.Abs((Recompose() - M).FrobeniusNorm()); //float n2 = Math.Abs(M.FrobeniusNorm()); //float thresh = m_ * (float)(eps) * n2; //if (recomposition_residual > thresh) { //std::cerr << "VNL::SVD<T>::SVD<T>() -- Warning, recomposition_residual = " //<< recomposition_residual << std::endl //<< "FrobeniusNorm(M) = " << n << std::endl //<< "eps*FrobeniusNorm(M) = " << thresh << std::endl //<< "Press return to continue\n"; //char x; //std::cin.get(&x, 1, '\n'); } } if (zero_out_tol >= 0) // Zero out small sv's and update rank count. ZeroOutAbsolute((float)(+zero_out_tol)); else // negative tolerance implies relative to max elt. ZeroOutRelative((float)(-zero_out_tol)); }