Esempio n. 1
0
/**
 * 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;

    }
}