一般产品中的特征自动型扣除

时间:2014-11-02 22:57:25

标签: c++ c++11 eigen eigen3

我有以下代码(我为更大的代码道歉 片段,这是我能够将问题减少的最小例子:

#include <Eigen/Dense>
#include <complex>
#include <iostream>
#include <typeinfo>

// Dynamic Matrix over Scalar field
template <typename Scalar> 
using DynMat = Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>;

// Dynamic column vector over Scalar field
template <typename Scalar>
using DynVect = Eigen::Matrix<Scalar, Eigen::Dynamic, 1>;

// Returns the D x D Identity matrix over the field Derived::Scalar
// deduced from the expression Eigen::MatrixBase<Derived>& A
template<typename Derived>
DynMat<typename Derived::Scalar> Id(const Eigen::MatrixBase<Derived>& A, std::size_t D)
{   
    DynMat<typename Derived::Scalar> result =
            DynMat<typename Derived::Scalar>::Identity(D, D);

    return result;
}

int main()
{
    //using ScalarField = std::complex<double>; // same issue even if I use complex numbers
    using ScalarField = double; // we use doubles in this example

    // A double dynamic matrix (i.e. MatrixXd)
    DynMat<ScalarField> Foo; // used to deduce the type in Id<>()

    // A double dynamic column vector (i.e. VectorXd)
    DynVect<ScalarField> v(4);
    v << 1., 0. , 0. ,0.; // plug in some values into it

    // Make sure that Id(Foo, 4) correctly deduces the template parameters
    std::cout << "Id(Foo, 4) is indeed the 4 x 4 identiy matrix over the ScalarField of "
              << "typeid().name(): " << typeid(ScalarField).name() << std::endl;
    std::cout << Id(Foo, 4) << std::endl; // Indeed the 4 x 4 complex Identity matrix

    // Use auto type deduction for GenMatProduct, junk is displayed. Why?!
    std::cout << std::endl << "Use auto type deduction for GenMatProduct,\
                 sometimes junk is displayed. Why?!" << std::endl;
    auto autoresult = Id(Foo, 4) * v; // evaluated result must be identically equal to v
    for(int i=0; i<10; i++)
    {
            std::cout << autoresult.transpose(); // thought 1 0 0 0 is the result, but NO, junk
            std::cout << " has norm: " << autoresult.norm() << std::endl; // junk
    }

    // Use implicit cast to Dynamic Matrix, works fine
    std::cout << std::endl << "Use implicit cast to Dynamic Matrix, works fine" << std::endl;
    DynMat<ScalarField> castresult = Id(Foo, 4) * v; // evaluated result must be identically equal to v
    for(int i=0; i<10; i++)
    {
            std::cout << castresult.transpose(); // 1 0 0 0, works ok
            std::cout << " has norm: " << castresult.norm() << std::endl; // ok
    }
}

主要思想是模板函数Id<>()采用特征表达式A 作为参数,与大小D一起,并生成单位矩阵 在表达式A的标量字段上。这个功能本身很好用。然而, 当我在具有auto推导类型的特征乘积中使用它时,例如在行中 auto autoresult = Id(Foo, 4) * v,我希望将向量乘以v 通过单位矩阵,所以净结果应该是一个表达式, 评估时,应该等于v。但这种情况并非如此, 看到第一个for循环,每当我显示结果并计算其规范时, 我大部分时间都是垃圾。另一方面,如果我暗中施展 将特征乘积Id(Foo, 4) * v转换为动态矩阵,一切正常, 结果得到了适当的评估。

我在OS X Yosemite上使用Eigen 3.2.2,并得到同样的怪异 使用g ++ 4.9.1和 Apple LLVM 6.0版(clang-600.0.54)(基于LLVM 3.5svn)

问题:

  • 我不明白第一个for循环中发生了什么,为什么 当我使用std::cout时,甚至在我使用时,我都不会对产品进行评估 使用norm方法?我错过了什么吗?没有别名 涉及到这里,我对发生的事情感到困惑。我知道 Eigen使用延迟评估,并在何时评估表达式 需要,但这似乎不是这里的情况。这个问题是 对我来说非常重要,因为我有很多相同的功能 味道为Id<>(),在auto推导出的表达式中使用 可能会失败。

问题经常发生,但并非总是如此。但是,如果你运行 程序3-4次,你一定会看到它。

我用来编译和运行它的命令是:

clang++ (g++) -std=c++11 -isystem ./eigen_3.2.2/ testeigen.cpp -otesteigen; ./testeigen

我在实际运行中获得的典型输出是:

Id(Foo, 4) is indeed the 4 x 4 identiy matrix over the ScalarField of typeid().name(): d
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1

Use GenMatProduct, sometimes junk is displayed. Why?!
1 0 0 0 has norm: inf
3.10504e+231 3.10504e+231 3.95253e-323            0 has norm: inf
3.10504e+231 3.10504e+231 3.95253e-323            0 has norm: inf
3.10504e+231 3.10504e+231 3.95253e-323            0 has norm: inf
3.10504e+231 3.10504e+231 3.95253e-323            0 has norm: inf
3.10504e+231 3.10504e+231 3.95253e-323            0 has norm: inf
3.10504e+231 3.10504e+231 3.95253e-323            0 has norm: inf
3.10504e+231 3.10504e+231 3.95253e-323            0 has norm: inf
3.10504e+231 3.10504e+231 3.95253e-323            0 has norm: inf
3.10504e+231 3.10504e+231 3.95253e-323            0 has norm: inf

Use implicit cast to Dynamic Matrix, works fine
1 0 0 0 has norm: 1
1 0 0 0 has norm: 1
1 0 0 0 has norm: 1
1 0 0 0 has norm: 1
1 0 0 0 has norm: 1
1 0 0 0 has norm: 1
1 0 0 0 has norm: 1
1 0 0 0 has norm: 1
1 0 0 0 has norm: 1
1 0 0 0 has norm: 1

即使我在

中使用eval()
  std::cout << autoresult.eval().transpose(); // thought 1 0 0 0 is the result, but NO, junk
  std::cout << " has norm: " << autoresult.eval().norm() << std::endl; // junk

我得到了同样奇怪的行为。

2 个答案:

答案 0 :(得分:3)

问题是Id()返回一个临时值,该临时值通过引用存储在表示表达式Id(Foo, 4) * v的对象中。因此,在auto语句之后,autoresult存储对死对象的引用。如果您不想要抽象表达式而是实际结果,请不要使用auto或致电eval来强制执行评估:

auto autoresult = (Id(Foo, 4) * v).eval();

第三个选项是使Id()返回的对象可用于进一步计算:

auto id4 = Id(Foo,4);
auto autoresult = id4 * v;

但在这种情况下,只要您使用autoresult,就会重新评估产品,以下内容会输出不同的结果:

cout << autoresult;
v.setRandom();
cout << autoresult;

答案 1 :(得分:1)

它可能有一个懒惰的评估类型,只能评估一次。您可以使用以下方式捕获它:

auto autoresultmatrix = autoresult.eval()