#pragma once #include "mat.h" #include "diag_mat.h" #include "functions.h" #include #include namespace cgv { namespace math { //helper funcition for svd template T pythag(const T a, const T b) { T absa=std::abs(a), absb=std::abs(b); return T((absa > absb ? absa*sqrt(1.0+(absb/absa)*(absb/absa)) : (absb == 0.0 ? 0.0 : absb*sqrt(1.0+(absa/absb)*(absa/absb))))); } ///computes the singular value decomposition of an MxN matrix a = u* w * v^t where u is an MxN matrix, w is a diagonal ///NxN matrix and v is a NxN square matrix. If the algorithm can't achieve a convergence after 20 iteration false is returned other wise true. ///The resulting matrices are stored in the parameters u,w,v. Attention: v is returned not v^T ! So to compute the original matrix a from the ///decomposed matrices you have to multiply u*w*transpose(v). ///It is possible to store u directly into a to save memory, just put the same reference into a and u. ///If ordering is true the singular values are sorted in descending order. ///To ensure that u*w*v^t remains equal to the matrix a the algorithm also exchanges the columns of u and v. template bool svd(const mat &a, mat &u, diag_mat &w, mat &v,bool ordering=true, int maxiter=30) { int m = a.nrows(); int n = a.ncols(); u=a; v.resize(n,n); w.resize(n); T eps=std::numeric_limits::epsilon(); //decompose bool flag; int i,its,j,jj,k,l,nm; T anorm,c,f,g,h,s,scale,x,y,z; vec rv1(n); g = scale = anorm = 0.0; for (i=0;i=0;i--) { if (i < n-1) { if (g != 0.0) { for (j=l;j=0;i--) { l=i+1; g=w[i]; for (j=l;j=0;k--) { for (its=0;its=0;l--) { nm=l-1; if (l == 0 || std::abs(rv1[l]) <= eps*anorm) { flag=false; break; } if (std::abs(w[nm]) <= eps*anorm) break; } if (flag) { c=0.0; s=1.0; for (i=l;i su(m), sv(n); do { inc *= 3; inc++; } while (inc <= n); do { inc /= 3; for (i=inc;i 1); for (k=0;k (m+n)/2) { for (i=0;i mat null(const mat& a) { T eps =std::numeric_limits::epsilon(); mat u,v; diag_mat s; svd(a,u,s,v); unsigned r = 0; T tol; if(a.nrows() > a.ncols()) tol = (T)a.nrows()*eps; else tol = (T)a.ncols()*eps; for(unsigned i = 0; i < s.ncols(); i++) { if(s(i) > tol) r++; } if (r < a.ncols()) { mat N = v.sub_mat(0, r,v.nrows(),a.ncols()-r); for(unsigned i = 0; i < N.nrows();i++) for(unsigned j = 0; j < N.ncols();j++) if(std::abs(N(i,j)) < eps) N(i,j)=(T)0; return N; } else return zeros (a.ncols(), 0); } template mat pseudo_inv(const mat& a, T eps = std::numeric_limits::epsilon()) { mat u,v; diag_mat s; svd(a,u,s,v); for(unsigned i = 0; i < s.ncols(); i++) { if(s(i) > eps) s(i)=1/s(i); else s(i) = 0; } return v*s*transpose(u); } ///compute the effective null space of a matrix using user defined tolerance template mat null(const mat& a, T tol) { T eps =std::numeric_limits::epsilon(); mat u,v; diag_mat s; svd(a,u,s,v); unsigned r = 0; for(unsigned i = 0; i < s.ncols(); i++) { if(s(i) > tol) r++; } if (r < a.ncols()) { mat N = v.submat(0, r,v.nrows(),a.ncols()-r); for(unsigned i = 0; i < N.nrows();i++) for(unsigned j = 0; j < N.ncols();j++) if(std::abs(N(i,j)) < eps) N(i,j)=(T)0; return N; } else return zeros (a.ncols(), 0); } ///computes the rank of a matrix using svd template unsigned rank(const mat& a) { mat u,v; diag_mat s; svd(a,u,s,v); unsigned r = 0; T tol; if(a.nrows() > a.ncols()) tol = (T)a.nrows()*std::numeric_limits::epsilon(); else tol = (T)a.ncols()*std::numeric_limits::epsilon(); for(unsigned i = 0; i < s.ncols(); i++) { if(s(i) > tol) r++; } return r; } ///computes the effective rank of a matrix using svd and the given tolerance tol template unsigned rank(const mat& a, T tol) { mat u,v; diag_mat s; svd(a,u,s,v); unsigned rank = 0; for(unsigned i = 0; i < s.ncols(); i++) { if(s(i) > tol) rank++; } return rank; } ///return the rank of m x n matrix A with m < n /// the solution of the system Ax=b is given as x= p + N *(l_1,...,l_{n-r})^T, r is the rank of A, are the free parameters and N is the nullspace template unsigned solve_underdetermined_system(const cgv::math::mat& A, const cgv::math::vec& b, cgv::math::vec& p, cgv::math::mat& N) { T eps =std::numeric_limits::epsilon(); mat u,v; diag_mat s; svd(A,u,s,v); //compute rank unsigned r = 0; T tol; if(A.nrows() > A.ncols()) tol = (T)A.nrows()*eps; else tol = (T)A.ncols()*eps; for(unsigned i = 0; i < s.ncols(); i++) { if(s(i) > tol) r++; else s(i)=0; } p=v*s*transpose(u)*b; if (r < A.ncols()) { N = v.sub_mat(0, r,v.nrows(),A.ncols()-r); for(unsigned i = 0; i < N.nrows();i++) for(unsigned j = 0; j < N.ncols();j++) if(std::abs(N(i,j)) < eps) N(i,j)=(T)0; return r; } else { N = zeros (A.ncols(), 0); return r; } } } }