/** * Computes the eigenvalues and eigenvectors of a symmetric matrix, using * the jacobi eigenvalue algorithm. * * @pre matrix must be square * @pre matrix must be symmetric * @pre matrix must be at least 2x2 * @param input symmetric n x n matrix * @return tuple containing the n-vector of eigenvalues, a matrix of eigenvectors and the number of rotations performed. */ std::tuple<vec, mat, uint32_t> eigenvalues(mat input){ // @pre matrix must be square assert(input.n_cols == input.n_rows); // @pre matrix must be symmetric assert(util::check_symmetric(input)); // @pre matrix must be at least 2x2 assert(input.n_cols >= 2); mat current = input; mat eigenvectors = arma::eye(input.n_cols, input.n_cols); uint32_t iteration = 1; while(true){ auto largest = util::find_largest(current); if(std::abs(current(std::get<0>(largest), std::get<1>(largest))) < GLOBAL_PRECISION_THRESHOLD){ //std::cout << "Desired precision reached, exiting." << std::endl; break; } //std::cout << "Iteration #" << iteration << ": iterating about " << std::get<0>(largest) // << ", " << std::get<1>(largest) << std::endl; std::tie(current, eigenvectors) = givens_rotation(current, largest, eigenvectors); //std::cout << "Matrix after iteration: " << std::endl << current << std::endl; iteration++; } return std::make_tuple(current.diag(0), eigenvectors, iteration); }
void tridiagonalQRStep(std::vector<double> &diag, std::vector<double> &subdiag, int start, int end, e::Matrix<double, 3, 3> &Q) { double td = (diag[end-1] - diag[end])*0.5; double e2 = subdiag[end-1]*subdiag[end-1]; double mu = diag[end] - e2/(td + (td>0? 1: -1)*std::sqrt(td*td + e2)); double x = diag[start] - mu; double z = subdiag[start]; for(int k = start; k < end; ++k) { double c, s; givens_rotation(x, z, c, s); //do T = G' T G double sdk = s * diag[k] + c * subdiag[k]; double dkp1 = s*subdiag[k] + c*diag[k + 1]; diag[k] = c * (c * diag[k] - s * subdiag[k]) - s * (c * subdiag[k] - s * diag[k+1]); diag[k+1] = s * sdk + c * dkp1; subdiag[k] = c * sdk - s * dkp1; if (k > start) subdiag[k - 1] = c * subdiag[k-1] - s * z; x = subdiag[k]; if (k < end - 1) { z = -s * subdiag[k+1]; subdiag[k + 1] = c * subdiag[k+1]; } //apply givens rotation //this only modifies two columns k and k+1 //0 double m_i_k = Q(0, k); double m_i_k1 = Q(0, k+1); Q(0, k) = c*m_i_k - s*m_i_k1; Q(0, k+1) = s*m_i_k + c*m_i_k1; //1 m_i_k = Q(1, k); m_i_k1 = Q(1, k+1); Q(1, k) = c*m_i_k - s*m_i_k1; Q(1, k+1) = s*m_i_k + c*m_i_k1; //2 m_i_k = Q(2, k); m_i_k1 = Q(2, k+1); Q(2, k) = c*m_i_k - s*m_i_k1; Q(2, k+1) = s*m_i_k + c*m_i_k1; } }