本征:将一个向量除以一个标量分两步有效但不能一步

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

所以我正在尝试实现 Power iteration 以在 C++ 中找到最大的特征值和相应的特征向量。我正在使用 Eigen 库。奇怪的是,当我在一行中将一个向量除以一个标量时(在第 (1) 行中标记),它会抛出一个错误,并且与 C++ 错误一样有用:

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'

但是当我分两步执行时(在第 (2) 行中标记),一切都很好,没有抛出任何错误。所以我觉得 Eigen 不能一步完成标量除法很奇怪。这里发生了什么,为什么我在一行上做它会失败?

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());
}

在 (2) 中,当您首先对前一个表达式赋值时,它会触发到 double 的隐式转换,因为它赋值给的变量是 double。 在(1)中,前一个表达式首先求值,它似乎是 Eigen 库定义的更高级别的类型,而后面的 double 表达式(点积)不能提升为该类型,因此错误。 如果您明确告诉编译器您希望将 b.transpose() * X * b 缩减为 double,它将起作用:

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

Eigen 区分 1x1 矩阵和标量,例如 b.transpose()*b 实际上是 1x1 矩阵。在某些特殊情况下,产生 1x1 矩阵的乘积可隐式转换为标量,但首选方法是始终对这些乘积使用 a.dot(b)(如果您确实想要标量)。

此外 b.tranpose().dot(b) 实际上与 b.dot(b)b.squaredNorm() 相同(后者可能稍微快一些,因为 b 只需要读取一次,尽管编译器通常足够聪明,可以将两者优化为相同的汇编代码。

总的来说,我建议使用这种表示法:

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