c++ - 如何通过 solve() 跟踪 Eigen 对象?

标签 c++ eigen expression-templates temporaries

这个问题与cast from Eigen::CwiseBinaryOp to MatrixXd causes segfault有关. 它的解决方案可能与前者一样简单。

在这个最小的示例中,我定义了 Holder,它保存 Eigen 矩阵,并通过其 get() 成员函数返回它。类似地,Decomp 是该矩阵的 LDLT 分解的表达式模板,Solve 求解 AX=B,得到 X。

#include <Eigen/Dense>
#include <Eigen/Cholesky>

template <class EigenType> class Holder {
public:
typedef EigenType result_type;

private:
result_type in_;

public:
Holder(const EigenType& in) : in_(in) {}
const result_type& get() const { return in_; }
};

template <class Hold> class Decomp {
public:
typedef typename Eigen::LDLT
    <typename Eigen::MatrixBase<typename Hold::result_type>::PlainObject>
        result_type;

private:
Hold mat_;

public:
Decomp(const Hold& mat) : mat_(mat) {}

result_type get() const { return mat_.get().ldlt(); }
};

template <class Derived, class OtherDerived> class Solve {
public:
typedef typename Eigen::internal::solve_retval
    <typename Derived::result_type, typename OtherDerived::result_type>
        result_type;

private:
Derived decomp_;
// typename Derived::result_type decomp_;
OtherDerived mat_;

public:
Solve(const Derived& decomp, const OtherDerived& mat)
    : decomp_(decomp), mat_(mat) {}
//: decomp_(decomp.get()), mat_(mat) {}

result_type get() const { return decomp_.get().solve(mat_.get()); }
// result_type get() const { return decomp_.solve(mat_.get()); }
};

typedef Holder<Eigen::MatrixXd> MatrixHolder;
typedef Decomp<MatrixHolder> MatrixDecomp;
typedef Solve<MatrixDecomp, MatrixHolder> SimpleSolve;

以下测试在 X.get() 上失败

#include "Simple.h"
#include <Eigen/Dense>
#include <iostream>

int main(int, char * []) {
MatrixHolder A(Eigen::MatrixXd::Identity(3, 3));
MatrixHolder B(Eigen::MatrixXd::Random(3, 2));
MatrixDecomp ldlt(A);
SimpleSolve X(ldlt, B);
std::cout << X.get() << std::endl;
return 0;
}

但是如果您在头文件中使用注释掉的行,则一切正常。不幸的是,这将分解的评估转移到求解器的构造中,这不适合我的使用。通常,我想构建一个涉及此 Solve 的复杂表达式 expr,并稍后调用 expr.get()

我该如何解决这个问题?是否有一般规则可遵循,以避免进一步的相关问题?

最佳答案

为避免无用且昂贵的拷贝,内部 solve_retval 结构通过 const 引用存储分解和右侧。然而,在 Decomp::get 函数中创建的 LDLT 对象在该函数返回的同时被删除,因此 solve_retval 对象引用死对象。

一种可能的解决方法是在 Solve 中添加一个 Decomp::result_type 对象,并在 Solve::get 中对其进行初始化。此外,为了避免多次深拷贝,我建议对一些属性使用 const 引用,如下所示:

#include <Eigen/Dense>
#include <Eigen/Cholesky>

template <class EigenType> class Holder {
public:
  typedef EigenType result_type;

private:
  result_type in_;

public:
  Holder(const EigenType& in) : in_(in) {}
  const result_type& get() const { return in_; }
};

template <class Hold> class Decomp {
public:
  typedef typename Eigen::LDLT
      <typename Eigen::MatrixBase<typename Hold::result_type>::PlainObject>
          result_type;

private:
  const Hold& mat_;
  mutable result_type result_;
  mutable bool init_;

public:
  Decomp(const Hold& mat) : mat_(mat), init_(false) {}

  const result_type& get() const {
    if(!init_) {
      init_ = true;
      result_.compute(mat_.get());
      return result_;
    }
  }
};

template <class Derived, class OtherDerived> class Solve {
public:
  typedef typename Eigen::internal::solve_retval
      <typename Derived::result_type, typename OtherDerived::result_type>
          result_type;

private:
  const Derived& decomp_;
  const OtherDerived& mat_;

public:
  Solve(const Derived& decomp, const OtherDerived& mat)
      : decomp_(decomp), mat_(mat) {}

  result_type get() const {
    return decomp_.get().solve(mat_.get());
  }
};

一般规则是通过 const 引用存储重对象(避免深度复制)和按值存储轻量级表达式(以减少临时生命问题)。

关于c++ - 如何通过 solve() 跟踪 Eigen 对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21219214/

相关文章:

c++ - 使用表达式模板的中间结果

c++ - 将虚函数与重载常规函数混合?

c++ - 你如何在 C++ 中将指数与变量一起使用?

c++ - 如何在 Eigen 中将矩阵与另一个矩阵按行相乘?

c++ - 我可以使用 Eigen 求解线性方程组,形式为 Ax = b,A 是稀疏的吗?

c++ - 如何集成使用表达式模板的库?

C++11:防止将对象分配给引用

c++ - 我将如何水平打印更多 block ?

c++ - Qt定时器问题

c++ - 在访问 Eigen::VectorXd 时使用零作为第二个索引是否安全?