简体   繁体   中英

Eigen: dividing a vector by a scalar in two steps works but not in one step

So I'm trying to implement Power iteration to find the greatest eigenvalue and corresponding eigenvector in C++. I'm using the Eigen library. The weird thing is that, when I divide a vector by a scalar in a single line (marked in line (1)), it throws an error, and is as helpful as C++ errors can be:

No viable conversion from 'typename internal::enable_if<true, const CwiseBinaryOp<internal::scalar_quotient_op<typename internal::traits<Product<Product<Transpose<Matrix<double, -1, 1, 0, -1, 1> >, Matrix<double, -1, -1, 1, -1, -1>, 0>, Matrix<double, -1, 1, 0, -1, 1>, 0> >::Scalar, typename internal::promote_scalar_arg<Scalar, double, (Eigen::internal::has_ReturnType<Eigen::ScalarBinaryOpTraits<Scalar, double, Eigen::internal::scalar_quotient_op<Scalar, double> > >::value)>::type>, const Product<Product<Transpose<Matrix<double, -1, 1, 0, -1, 1> >, Matrix<double, -1, -1, 1, -1, -1>, 0>, Matrix<double, -1, 1, 0, -1, 1>, 0>, const typename internal::plain_constant_type<Product<Product<Transpose<Matrix<double, -1, 1, 0, -1, 1> >, Matrix<double, -1, -1, 1, -1, -1>, 0>, Matrix<double, -1, 1, 0, -1, 1>, 0>, typename internal::promote_scalar_arg<Scalar, double, (Eigen::internal::has_ReturnType<Eigen::ScalarBinaryOpTraits<Scalar, double, Eigen::internal::scalar_quotient_op<Scalar, double> > >::value)>::type>::type> >::type'
(aka 'const Eigen::CwiseBinaryOp<Eigen::internal::scalar_quotient_op<double, double>, const Eigen::Product<Eigen::Product<Eigen::Transpose<Eigen::Matrix<double, -1, 1, 0, -1, 1> >, Eigen::Matrix<double, -1, -1, 1, -1, -1>, 0>, Eigen::Matrix<double, -1, 1, 0, -1, 1>, 0>, const Eigen::CwiseNullaryOp<Eigen::internal::scalar_constant_op<double>, const Eigen::Matrix<double, 1, 1, 0, 1, 1> > >')
to 'double'

but when I do it in two steps (marked in line (2)), everything is fine and no errors are thrown. So I find it very weird that Eigen can't do division by scalars in a single step. What is happening here, why it fails when I do it on a single line?

pair<double, Vector> power_iteration(const Matrix& X, unsigned num_iter, double eps)
{
    Vector b = Vector::Random(X.cols());
    b.normalize();
    Vector b_old;
    for(unsigned int i = 0; i < num_iter; i++){
        b_old =b;
        b = X*b_old;
        b.normalize();
        double cos_angle = b.dot(b_old);
        if(cos_angle > 1-eps){
            i= num_iter+1;
        }
    }

   (1)  double eigenvalue = (b.transpose() * X * b)/(b.transpose().dot(b));

   (2)  double eigenvalue2 = b.transpose() * X * b;
    eigenvalue2 = eigenvalue2/b.transpose().dot(b);

    return make_pair(eigenvalue, b / b.norm());
}

In (2), when you assign the former expression first, it triggers an implicit conversion to double since the variable to which it's assigned to is a double. In (1), the former expression is first evaluated which seems to be of a type of a higher rank defined by the Eigen library, and the later double expression (the dot product) cannot be promoted to this type, hence the error. If you tell the compiler explicitly that you want b.transpose() * X * b to be reduced to a double, it will work:

double eigenvalue = static_cast<double>(b.transpose() * X * b)/(b.transpose().dot(b));

Eigen distinguishes between 1x1 matrices and scalars, eg, b.transpose()*b is actually a 1x1 matrix. There are some special cases where products resulting in a 1x1 matrix are implicitly convertible to a scalar, but the preferred way is to always use a.dot(b) for these products (if indeed you want to have a scalar).

Moreover b.tranpose().dot(b) is actually the same as just b.dot(b) or just b.squaredNorm() (the latter might be slightly faster, since b needs to be read only once, although compilers are often smart enough to optimize both to the same assembly code).

Overall, I suggest using this notation:

double eigenvalue = b.dot(X * b) / b.squaredNorm();
// or:
double eigenvalue = (b.transpose()*X).dot(b) / b.squaredNorm();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM