我们试图计算由对角线块U和V以及偏移对角线块W和W'(W的转置)组成的大对称正定矩阵H的Schur补码,并且V是对角正定矩阵。 Schur补充公式是
H <-- U - W*V^(-1)*W' (1)
在实施操作时,一种方法是直接使用上述等式。
第二种方式利用了V将全零作为其对角线元素的事实。公式1写出并实现如下
H <-- U - \Sigma W_i * V_{ii}^(-1) * (W_i)' (2)
其中W_i是W的第i列,V_ {ii}是V的第i个对角线元素,并且\ Sigma对W的每一列求和。显然,(2)预期比(1)更有效。它避免了整个大矩阵的乘法。
这两种方法都是用C ++语言的Eigen库实现的,并在PC和带有intel atom处理器的移动设备上进行测试。
令人惊讶的是,在intel核心i7 4核PC上,(2)的代码片段(用gcc,-O3,发布模式编译)效率低于(1)。 (1)和(2)的中值计算时间分别为0.092 ms,0.212 ms,注意比率为2.30。在intel atom 4核处理器上,观察到类似的(用clang -O3编译的代码片段)。 (1)和(2)的中值计算时间分别为0.575 ms,1.923 ms,注意比率为3.34。
有人可以帮我解释为什么(2)比(1)慢吗?
有关这两种方法的代码段,请参阅以下内容。
// preconditioner
Eigen::VectorXd p = (H_.diagonal().array() > 1.0e-9).select(H_.diagonal().cwiseSqrt(),1.0e-3);
Eigen::VectorXd p_inv = p.cwiseInverse();
// scale H and b
H_ = p_inv.asDiagonal() * H_ * p_inv.asDiagonal();
b0_ = p_inv.asDiagonal() * b0_;
Eigen::MatrixXd U = H_.topLeftCorner(H_.rows() - marginalizationParametersLandmarks,
H_.rows() - marginalizationParametersLandmarks);
Eigen::MatrixXd V = H_.bottomRightCorner(marginalizationParametersLandmarks,
marginalizationParametersLandmarks);
Eigen::MatrixXd W = H_.topRightCorner(H_.rows() - marginalizationParametersLandmarks,
marginalizationParametersLandmarks);
Eigen::VectorXd b_a = b0_.head(H_.rows() - marginalizationParametersLandmarks);
Eigen::VectorXd b_b = b0_.tail(marginalizationParametersLandmarks);
// split preconditioner
Eigen::VectorXd p_a = p.head(H_.rows() - marginalizationParametersLandmarks);
// invert the marginalization block
static const int sdim = SIZE_PARAMETERIZATION_SIZE_FEATURE;
b0_.resize(b_a.rows());
b0_ = b_a;
H_.resize(U.rows(), U.cols());
H_ = U;
// surprisingly, the below operation is more efficient than its alternative on both
// an intel atom processor and on an intel core i7 PC
#if defined(ANDROID) || defined(__ANDROID__)
Eigen::VectorXd V_inv(V.cols());
for (int jack = 0; jack < V.cols(); ++jack) {
V_inv[jack] = 1.0/V(jack, jack);
}
H_ -= W*V_inv.asDiagonal()*W.transpose();
b0_ -= W*V_inv.asDiagonal()*b_b;
#else
// assume V off-diagonal elements are all zeros
Eigen::MatrixXd delta_H(U.rows(), U.cols());
Eigen::VectorXd delta_b(b_a.rows());
delta_H.setZero();
delta_b.setZero();
for (int i = 0; i < V.cols(); ++i) {
double V_inv = 1.0 / V(i, i);
double V_inv_sqrt = std::sqrt(V_inv);
Eigen::VectorXd M = W.block(0, i, W.rows(), 1) * V_inv_sqrt;
Eigen::VectorXd M1 = W.block(0, i, W.rows(), 1) * V_inv;
delta_H += M * M.transpose();
delta_b += M1 * b_b[i];
}
b0_ -= delta_b;
H_ -= delta_H;
#endif
// unscale
H_ = p_a.asDiagonal() * H_ * p_a.asDiagonal();
b0_ = p_a.asDiagonal() * b0_;