简体   繁体   English

Eigen 中的模板协方差函数

[英]Templated covariance function in Eigen

One of my projects requires a templated covariance function, ie taking a MatrixXd, ArrayXXf and/or a .block() thereof as input and returning an expression to be used in further calculations.我的一个项目需要模板化协方差函数,即以 MatrixXd、ArrayXXf 和/或其中的 .block() 作为输入并返回一个表达式以用于进一步计算。

q1. q1。 As verification, my attempt below seems to work, but does it actually return an Eigen expression?作为验证,我在下面的尝试似乎有效,但它实际上返回了一个特征表达式吗? (edit: @Darhuuk confirmed it does not; fortunately, C++14 or higher is not a problem!) (编辑:@Darhuuk 确认没有;幸运的是,C++14 或更高版本不是问题!)

q2. q2。 Obviously, I'd need something of an 'internal Matrix' rather than the internal RowVector I came across in the Eigen documentation about templated functions.显然,我需要一些“内部矩阵”而不是我在 Eigen 文档中遇到的关于模板化函数的内部 RowVector。 Hence my question, how should one create an internal MatrixType z = x.rowwise() - x.colwise().mean(), so the function can return (z.transpose()*z)/(x.rows()-1)?因此我的问题是,应该如何创建一个内部 MatrixType z = x.rowwise() - x.colwise().mean(),以便函数可以返回 (z.transpose()*z)/(x.rows() -1)? The internal rowvector is used in the covariance examples in the Eigen manual ( https://eigen.tuxfamily.org/dox/TopicFunctionTakingEigenTypes.html )内部行向量用于 Eigen 手册 ( https://eigen.tuxfamily.org/dox/TopicFunctionTakingEigenTypes.html ) 中的协方差示例

q3. Q3。 Finally, when adding the templated function to a class with a header file, how do I find which Eigen types need to be used to explicitly instantiate the template for eg MatrixXd, a .block() of an ArrayXXd, or a Map?最后,当将模板化函数添加到带有头文件的类时,我如何找到需要使用哪些特征类型来显式实例化模板,例如 MatrixXd、ArrayXXd 的 .block() 或 Map? All I could find were examples using simple data types (eg Storing C++ template function definitions in a .CPP file )我能找到的只是使用简单数据类型的示例(例如,在 .CPP 文件中存储 C++ 模板函数定义

template <typename Derived>
Matrix<typename Derived::Scalar, Derived::ColsAtCompileTime, Derived::ColsAtCompileTime>  
SampleCov( const DenseBase<Derived>& x )
{

  typedef typename internal::plain_row_type<Derived>::type RowVectorType;
  const RowVectorType x_mean = x.colwise().mean();

  return ((x.rowwise() - x_mean).matrix().transpose() * (x.rowwise() - x_mean).matrix()) / (x.rows()-1);
}

Your function explicitly returns a Matrix object, so no, it does not return an expression object.您的函数显式返回一个Matrix对象,因此不,它不返回表达式对象。 One way to fix this is by letting the compiler deduce the return type for you (since the expression object will be some gigantic templated mess):解决此问题的一种方法是让编译器为您推断返回类型(因为表达式对象将是一些巨大的模板化混乱):

template <typename Derived>
auto SampleCov (DenseBase<Derived> const & x) {
  auto const x_mean = x.colwise().mean();
  return ((x.rowwise() - x_mean).matrix().transpose()
    * (x.rowwise() - x_mean).matrix()) / (x.rows() - 1);
}

This is assuming you're using C++14 or higher.这是假设您使用的是 C++14 或更高版本。 For C++11 using just auto as the return type doesn't cut it, you need a trailing return type:对于 C++11 只使用 auto 作为返回类型不会削减它,你需要一个尾随返回类型:

template <typename Derived>
auto SampleCov (DenseBase<Derived> const & x)
    -> decltype(((x.rowwise() - x.colwise().mean()).matrix().transpose()
      * (x.rowwise() - x.colwise().mean()).matrix()) / (x.rows() - 1)) {
  auto const x_mean = x.colwise().mean();
  return ((x.rowwise() - x_mean).matrix().transpose()
    * (x.rowwise() - x_mean).matrix()) / (x.rows() - 1);
}

Note that I removed the RowVectorType typedef, since I didn't see the point.请注意,我删除了RowVectorType typedef,因为我没有看到这一点。

Regarding question 2, I think the above examples solve that issue, since there's no more explicitly named types.关于问题 2,我认为上面的例子解决了这个问题,因为没有更明确命名的类型。 The auto for both the return type and inside the function takes care of that.返回类型和函数内部的auto会处理这个问题。

Finally, question 3. It depends on what exactly you want to do.最后,问题3。这取决于你到底想做什么。 Based on what you're saying, it seems the above functions don't work for MatrixXd objects or objects returned by calling MatrixXd::block() .根据您的说法,上述函数似乎不适用于MatrixXd对象或通过调用MatrixXd::block()返回的对象。

To me that doesn't really indicate that you need explicit template instantiation , which is what you're asking.对我来说,这并不真正表明您需要显式模板实例化,这正是您要问的。

Instead, you could simply make SampleCov 's argument type more generic:相反,您可以简单地使SampleCov的参数类型更通用:

template <typename T>
auto SampleCovGeneric (T const & x) {
  auto const x_mean = x.colwise().mean();
  return ((x.rowwise() - x_mean).matrix().transpose()
    * (x.rowwise() - x_mean).matrix()) / (x.rows() - 1);
}

As long as you can call colwise() , matrix() , ... on an object of type T , you're good to go.只要您可以在T类型的对象上调用colwise()matrix() 、 ... ,您就可以开始了。 This is probably a better way to go anyway, since now it's possible to pass in Eigen expressions to SampleCovGeneric .无论如何,这可能是一种更好的方法,因为现在可以将 Eigen 表达式传递给SampleCovGeneric For example:例如:

Eigen::MatrixXd a(2,2);

// aTranspose is NOT a matrix object!
// Its type is Eigen::Transpose<Eigen::Matrix<double, Dynamic, Dynamic>>.
auto aTranspose = a.transpose();

/* Note use of auto and eval() calls!
 * See https://eigen.tuxfamily.org/dox/TopicPitfalls.html.
 */
auto b = SampleCov(aTranspose.eval()).eval();
auto c = SampleCovGeneric(aTranspose).eval();

In the above example, SampleCov expects an object of type DenseBase<Derived> .在上面的例子中, SampleCov需要一个DenseBase<Derived>类型的对象。 But aTranspose is not such a type.但是aTranspose不是这样的类型。 So we have to explicitly evaluate (ie actually calculate) the transpose first.所以我们必须首先明确评估(即实际计算)转置。 Depending on what happens inside SampleCov that's a useless calculation.根据SampleCov内部发生的情况,这是一个无用的计算。 Eg imagine the first thing calculated is the argument ( x ) its transpose.例如,假设计算的第一件事是参数 ( x ) 的转置。 In this case that would simply be the original a again.在这种情况下,这将再次成为原始的 a 。 But if the transpose is already evaluated then Eigen can't "see" that and needs to calculate the transpose's transpose.但是如果已经评估了转置,那么 Eigen 无法“看到”它并且需要计算转置的转置。

SampleCovGeneric on the other hand accepts any type, so there's no need to evaluate aTranspose first.另一方面, SampleCovGeneric接受任何类型,因此无需先评估aTranspose That (might) allow Eigen to "see through" a bunch of calculations and thus calculate the result in a more optimized manner.这(可能)允许 Eigen “看穿”一堆计算,从而以更优化的方式计算结果。 See eg https://eigen.tuxfamily.org/dox/TopicLazyEvaluation.html .参见例如https://eigen.tuxfamily.org/dox/TopicLazyEvaluation.html

If you really need to, you can explicitly instantiate templates so they can be split in header & source files.如果您确实需要,您可以显式实例化模板,以便将它们拆分为头文件和源文件。 That would go something like this:那会是这样的:

/* Header */
#pragma once

#include <utility>

// Reduce trailing return type mess a little
template <class T>
using SampleCovResultType = decltype(((std::declval<T const &>().rowwise()
      - std::declval<T const &>().colwise().mean()).matrix().transpose()
    * (std::declval<T const &>().rowwise()
      - std::declval<T const &>().colwise().mean()).matrix())
  / (std::declval<T const &>().rows() - 1));

template <typename T>
auto SampleCov (T const & x) -> SampleCovResultType<T>;

// Explicit template declaration
extern template
auto SampleCov<Eigen::MatrixXd> (Eigen::MatrixXd const & x)
    -> SampleCovResultType<Eigen::MatrixXd>;

/* Source */
#include "SampleCov.h"

template <class T>
auto SampleCov (T const & x) -> SampleCovResultType<T> {
  auto const x_mean = x.colwise().mean();
  return ((x.rowwise() - x_mean).matrix().transpose()
    * (x.rowwise() - x_mean).matrix()) / (x.rows() - 1);
}

// Explicit template definition
template
auto SampleCov<Eigen::MatrixXd> (Eigen::MatrixXd const & x)
    -> SampleCovResultType<Eigen::MatrixXd>;

I don't think you can get away with using only auto as the return type.我认为您不能只使用 auto 作为返回类型。 Since the function definition is put into a separate source file, the compiler can't see the function body, so it has no way to deduce the return type when you call the function.由于将函数定义放在单独的源文件中,编译器看不到函数体,因此在调用函数时无法推断返回类型。 So you have to use a trailing return type.所以你必须使用尾随返回类型。

Note the following though:但请注意以下几点:

An explicit instantiation declaration (an extern template) prevents implicit instantiations: the code that would otherwise cause an implicit instantiation has to use the explicit instantiation definition provided somewhere else in the program.显式实例化声明(外部模板)可防止隐式实例化:否则会导致隐式实例化的代码必须使用程序中其他地方提供的显式实例化定义。

Ie if you try to call SampleCov with a type T for which you did not explicitly instantiate SampleCov , compilation will fail.也就是说,如果你尝试调用SampleCov与类型T了,你没有显式实例SampleCov ,编译将失败。

Note: All of the above code is untested.注意:以上所有代码都未经测试。 Chance of typos is high :).错别字的可能性很高:)。

Edits:编辑:

  • Fixing missing template argument.修复丢失的模板参数。
  • Removed remark about calling matrix() .删除了关于调用matrix() I wondered whether it evaluated the expression, which you don't want it to do.我想知道它是否评估了您不希望它执行的表达式。 AFAIK, it does not, based on eg ArrayBase's documentation . AFAIK,它没有,基于例如ArrayBase 的文档
  • Added info about explicit instantiation.添加了有关显式实例化的信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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