⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 svd.cc

📁 basic linear algebra classes and applications (SVD,interpolation, multivariate optimization)
💻 CC
📖 第 1 页 / 共 2 页
字号:
 * consequence of Frantsis' Theorem, see the book above. In that case, we can * eliminate the N-th row and column of JJ and carry out further transforms * with a smaller matrix. If any other superdiag element of JJ turns 0, * then JJ effectively falls into two independent matrices. We will process * them independently (the bottom one first). * * Since matrix J is a bidiagonal, it can be stored efficiently. As a matter * of fact, its N diagonal elements are in array Sig, and superdiag elements * are stored in array super_diag. */ 				// Carry out U * S with a rotation matrix				// S (which combines i-th and j-th columns				// of U, i>j)inline void SVD::rotate(Matrix& U, const int i, const int j,		   const double cos_ph, const double sin_ph){  LAStreamOut Ui(MatrixColumn (U,i));  LAStreamOut Uj(MatrixColumn (U,j));  while( !Ui.eof() )  {    REAL& Uil = Ui.get(); REAL& Ujl = Uj.get();    const REAL Ujl_was = Ujl;    Ujl =  cos_ph*Ujl_was + sin_ph*Uil;    Uil = -sin_ph*Ujl_was + cos_ph*Uil;  }}/* * A diagonal element J[l-1,l-1] turns out 0 at the k-th step of the * algorithm. That means that one of the original matrix' singular numbers * shall be zero. In that case, we multiply J by specially selected * matrices S' of simple rotations to eliminate a superdiag element J[l-1,l]. * After that, matrix J falls into two pieces, which can be dealt with * in a regular way (the bottom piece first). *  * These special S transformations are accumulated into matrix U: since J * is left-multiplied by S', U would be right-multiplied by S. Transform * formulas for doing these rotations are similar to (5) above. See the * book cited above for more details. */inline void SVD::rip_through(	Vector& super_diag, const int k, const int l, const double eps){  double cos_ph = 0, sin_ph = 1;	// Accumulate cos,sin of Ph  				// The first step of the loop below  				// (when i==l) would eliminate J[l-1,l],  				// which is stored in super_diag(l)  				// However, it gives rise to J[l-1,l+1]  				// and J[l,l+2]  				// The following steps eliminate these  				// until they fall below  				// significance  for(register int i=l; i<=k; i++)  {    const double f = sin_ph * super_diag(i);    super_diag(i) *= cos_ph;    if( abs(f) <= eps )      break;			// Current J[l-1,l] became unsignificant    cos_ph = sig(i); sin_ph = -f;	// unnormalized sin/cos    const double norm = (sig(i) = hypot(cos_ph,sin_ph)); // sqrt(sin^2+cos^2)    cos_ph /= norm, sin_ph /= norm;	// Normalize sin/cos    rotate(U,i,l-1,cos_ph,sin_ph);  }}			// We're about to proceed doing QR-transforms			// on a (bidiag) matrix J[1:k,1:k]. It may happen			// though that the matrix splits (or can be			// split) into two independent pieces. This function			// checks for splitting and returns the lowerbound			// index l of the bottom piece, J[l:k,l:k]inline int SVD::get_submatrix_to_work_on(	Vector& super_diag, const int k, const double eps){  for(register int l=k; l>1; l--)    if( abs(super_diag(l)) <= eps )      return l;				// The breaking point: zero J[l-1,l]    else if( abs(sig(l-1)) <= eps )	// Diagonal J[l,l] turns out 0    {					// meaning J[l-1,l] _can_ be made      rip_through(super_diag,k,l,eps);	// zero after some rotations      return l;    }  return 1;			// Deal with J[1:k,1:k] as a whole}		// Diagonalize root modulevoid SVD::diagonalize(Vector& super_diag, const double eps){  for(register int k=N; k>=1; k--)	// QR-iterate upon J[l:k,l:k]  {    register int l;    while(l=get_submatrix_to_work_on(super_diag,k,eps),    	  abs(super_diag(k)) > eps)	// until superdiag J[k-1,k] becomes 0    {      double shift;			// Compute a QR-shift from a bottom      {					// corner minor of J[l:k,l:k] order 2      	REAL Jk2k1 = super_diag(k-1),	// J[k-2,k-1]      	     Jk1k  = super_diag(k),      	     Jk1k1 = sig(k-1),		// J[k-1,k-1]      	     Jkk   = sig(k),      	     Jll   = sig(l);		// J[l,l]      	shift = (Jk1k1-Jkk)*(Jk1k1+Jkk) + (Jk2k1-Jk1k)*(Jk2k1+Jk1k);      	shift /= 2*Jk1k*Jk1k1;      	shift += (shift < 0 ? -1 : 1) * sqrt(shift*shift+1);      	shift = ( (Jll-Jkk)*(Jll+Jkk) + Jk1k*(Jk1k1/shift-Jk1k) )/Jll;      }      				// Carry on multiplications by T2, S2, T3...      double cos_th = 1, sin_th = 1;      REAL Ji1i1 = sig(l);	// J[i-1,i-1] at i=l+1...k      for(register int i=l+1; i<=k; i++)      {      	REAL Ji1i = super_diag(i), Jii = sig(i);  // J[i-1,i] and J[i,i]      	sin_th *= Ji1i; Ji1i *= cos_th; cos_th = shift;      	double norm_f = (super_diag(i-1) = hypot(cos_th,sin_th));      	cos_th /= norm_f, sin_th /= norm_f;      					// Rotate J[i-1:i,i-1:i] by Ti      	shift = cos_th*Ji1i1 + sin_th*Ji1i;	// new J[i-1,i-1]      	Ji1i = -sin_th*Ji1i1 + cos_th*Ji1i;	// J[i-1,i] after rotation      	const double Jii1 = Jii*sin_th;		// Emerged J[i,i-1]      	Jii *= cos_th;				// new J[i,i]        rotate(V,i,i-1,cos_th,sin_th); // Accumulate T rotations in V                double cos_ph = shift, sin_ph = Jii1;// Make Si to get rid of J[i,i-1]        sig(i-1) = (norm_f = hypot(cos_ph,sin_ph));	// New J[i-1,i-1]        if( norm_f == 0 )		// If norm =0, rotation angle          cos_ph = cos_th, sin_ph = sin_th; // can be anything now        else          cos_ph /= norm_f, sin_ph /= norm_f;      					// Rotate J[i-1:i,i-1:i] by Si        shift = cos_ph * Ji1i + sin_ph*Jii;	// New J[i-1,i]        Ji1i1 = -sin_ph*Ji1i + cos_ph*Jii;	// New Jii, would carry over        					// as J[i-1,i-1] for next i        rotate(U,i,i-1,cos_ph,sin_ph);  // Accumulate S rotations in U        				// Jii1 disappears, sin_th would        cos_th = cos_ph, sin_th = sin_ph; // carry over a (scaled) J[i-1,i+1]        				// to eliminate on the next i, cos_th        				// would carry over a scaled J[i,i+1]      }      super_diag(l) = 0;		// Supposed to be eliminated by now      super_diag(k) = shift;      sig(k) = Ji1i1;    }		// --- end-of-QR-iterations    if( sig(k) < 0 )		// Correct the sign of the sing number    {      sig(k) = -sig(k);      for(LAStreamOut Vk(MatrixColumn(V,k)); !Vk.eof(); )        { REAL& vk = Vk.get(); vk = -vk; }    }  }    } /* *------------------------------------------------------------------------ *				The root Module */SVD::SVD(const Matrix& A)   : M(A.q_nrows()), N(A.q_ncols()),     U(A.q_nrows(),A.q_nrows()),     V(A.q_ncols(),A.q_ncols()),     sig(A.q_ncols()){  if( M < N )    A.info(),    _error("Matrix A should have at least as many rows as it has columns");       U.unit_matrix(); V.unit_matrix();  Vector super_diag(N);  const double bidiag_norm = bidiagonalize(super_diag,A);  const double eps = FLT_EPSILON * bidiag_norm;	// Significance threshold  diagonalize(super_diag,eps);}/* *------------------------------------------------------------------------ *		Print some info about the SVD that has been built */				// Print the info about the SVDvoid SVD::info(void) const{  U.is_valid();  message("\nSVD of an %dx%d matrix",M,N);}				// Return min and max singular valuesSVD::operator MinMax(void) const{  LAStreamIn sigs(sig);  MinMax mm(sigs.get());  while( !sigs.eof() )    mm << sigs.get();  return mm;}					// sig_max/sig_mindouble SVD::q_cond_number(void) const{  return ((MinMax)(*this)).ratio();}/* *------------------------------------------------------------------------ * 			class SVD_inv_mult * 	Solving an (overspecified) set of linear equations A*X=B * 		using a least squares method via SVD * * Matrix A is a rectangular M*N matrix with M>=N, B is a M*K matrix * with K either =1 (that is, B is merely a column-vector) or K>1. * If B is a unit matrix of the size that of A, the present LazyMatrix is * a regularized (pseudo)inverse of A. * * Algorithm *   Matrix A is decomposed first using SVD: *	(1) A = U*Sig*V' * where matrices U and V are orthogonal and Sig is diagonal. * The set of simultaneous linear equations AX=B can be written then as *	(2) Sig*V'*X = U'*B * (where we have used the fact that U'=inv(U)), or *	(3) Sig*Vx = Ub * where we introduced *	(4) Vx = V'*X and Ub = U'*b * Since Sig is a diag matrix, eq. (3) is solved trivially: * 	(5) Vx[i] = Ub[i]/sig[i], if sig[i] > tau *		  = 0	otherwise * In the latter case matrix Sig has an incomplete rank, or close to that. * That is, one or more singular values are _too_ small. This means that * there is linear dependence among the equations of the set. In that case, * we will print the corresponding "null coefficients", the corresponding * columns of V. Adding them to the solution X won't change A*X. * * Having computed Vx, X is simply recovered as *	(6) X = V*Vx * since V*V' = E. * * Threshold tau in (5) is chosen as N*FLT_EPSILON*max(Sig[i,i]) unless * specified otherwise by the user. */SVD_inv_mult::SVD_inv_mult	(const SVD& _svd, const Matrix& _B,const double _tau)	: LazyMatrix(_svd.q_V().q_ncols(),_B.q_ncols()),	  svd(_svd), B(_B), tau(_tau){  if( svd.q_U().q_nrows() != B.q_nrows() )    svd.info(), B.info(),    _error("Unsuitable matrices for SVD*X=B set");  MinMax sig_minmax = (MinMax)svd;  if( tau == 0 )   tau = svd.q_V().q_nrows() * sig_minmax.max() * FLT_EPSILON;  are_zero_coeff = sig_minmax.min() < tau;}#if 0				// Computing X in a special case where				// B (and X) is a vectorinline void SVD_inv_mult::fill_in_vector(Matrix& X) const{  X.clear();  bool x_is_vector = x.q_ncols() == 1;  if( are_zero_coeff )    message("\nSVD solver of AX=B detected a linear dependency among X"    	    "\n  #  \tsingular value\tnull coefficients\n");  for(register int i=1; i<=svd.q_V().q_nrows(); i++)  {     MatrixCol Ui(U,i);     const double sigi = svd.q_sig()(i);     if( sigi > tau )       if( x_is_vector )         X(i) = (Ui * B)/sigi;       else         X(i) =0, print null coeffs...  }  X *= V;			// ???}#endif				// Computing X in a general case where				// B (and X) is a rectangular matrixvoid SVD_inv_mult::fill_in(Matrix& X) const{  if( are_zero_coeff )    message("\nSVD solver of AX=B detected a linear dependency among X"    	    "\n  #  singular value\tnull coefficients\n");  Matrix Vx = zero(X);  Vector Ui(svd.q_U().q_nrows());			// i-th col of U  Vector Bj(B.q_nrows());				// j-th col of B  const Matrix& V = svd.q_V();  const Matrix& U = svd.q_U();  LAStreamIn sigs(svd.q_sig());  for(register int i=1; i<=V.q_ncols(); i++)  {    const double sigi = sigs.get();    LAStrideStreamOut Vxi( MatrixRow(Vx,i) );    if( sigi > tau )    {      to_every(Ui) = of_every(ConstMatrixColumn(U,i));      for(register int j=1; j<=X.q_ncols(); j++)        to_every(Bj) = of_every(ConstMatrixColumn(B,j)), Vxi.get() = Ui * Bj/sigi;    }    else    {      while( !Vxi.eof() ) Vxi.get() = 0;      message(" %d %12.2g \t(",i,sigi);      LAStreamIn Vi(ConstMatrixColumn(V,i));      while( !Vi.eof() )	message("%10g ",Vi.get());      message(")\n");    }  }  X.mult(V,Vx);				// that is, set X to be V*Vx}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -