Короче говоря: я пытаюсь вывести возвращаемое значение (которое является шаблоном выражения) функции, которая выполняет унарную операцию над матрицей.
В этом случае операция заключается в вычислении ковариационной матрицы.
Я следовал документации Eigen здесь: https://eigen.tuxfamily.org/dox/TopicCustomizing_NullaryExpr.html и создал минимальный пример умножения матрицы на два и возврата ее результата.Фрагмент кода ниже показывает рабочий пример.Ключевым моментом для меня является то, что выражение не оценивается как промежуточный результат, поэтому я не хочу возвращать что-то вроде Eigen::MatrixBase<Derived>
.
template<typename ArgType>
struct times_two_helper {
template<typename Derived>
static auto TimesTwo(Eigen::MatrixBase<Derived> const& mat) {
return mat + mat;
}
using ResultType = Eigen::Matrix<typename ArgType::Scalar,
ArgType::ColsAtCompileTime,
ArgType::RowsAtCompileTime,
ArgType::Options,
ArgType::MaxColsAtCompileTime,
ArgType::MaxRowsAtCompileTime>;
using ExpressionType = decltype(TimesTwo(std::declval<ArgType>()));
};
template<typename ArgType>
struct times_two_functor {
using ResultType = typename times_two_helper<ArgType>::ResultType;
using ExpressionType = typename times_two_helper<ArgType>::ExpressionType;
ArgType const& arg_;
// Here I want to store the expression without evaluating it!
ExpressionType expression_;
public:
times_two_functor(ArgType const& arg)
: arg_{arg}
, expression_{times_two_helper<ArgType>::TimesTwo(arg)}
{}
typename ArgType::Scalar operator() (Eigen::Index row, Eigen::Index col) const {
return expression_(row, col);
}
};
template <class ArgType>
Eigen::CwiseNullaryOp<times_two_functor<ArgType>, typename times_two_helper<ArgType>::ResultType>
TimesTwo(Eigen::MatrixBase<ArgType> const& arg) {
using ResultType = typename times_two_helper<ArgType>::ResultType;
return ResultType::NullaryExpr(arg.rows(), arg.cols(), times_two_functor<ArgType>(arg.derived()));
}
Используется так:
TEST(Stat, TimesTwo) {
Eigen::Matrix<double, 3, 3> input;
input << 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0;
Eigen::Matrix<double, 3, 3> result = TimesTwo(input);
std::cout << result << "\n";
}
Но когда я пытаюсь сделать то же самое для ковариационной матрицы, он выводит неправильный тип для ExpressionType
(сообщение об ошибке отображается внизу).
template<class ArgType>
struct covariance_helper {
template<typename Derived>
static auto Covariance(Eigen::MatrixBase<Derived> const& mat) {
auto centered = mat.rowwise() - mat.colwise().mean();
return (centered.adjoint() * centered) / double(mat.rows() - 1);
}
using ResultType = Eigen::Matrix<typename ArgType::Scalar,
ArgType::ColsAtCompileTime,
ArgType::ColsAtCompileTime,
ArgType::Options,
ArgType::MaxColsAtCompileTime,
ArgType::MaxColsAtCompileTime>;
using ExpressionType = decltype(covariance_helper::Covariance(std::declval<ArgType>()));
};
template<class ArgType>
class covariance_functor {
using ResultType = typename times_two_helper<ArgType>::ResultType;
using ExpressionType = typename times_two_helper<ArgType>::ExpressionType;
const ArgType &mat_;
ExpressionType expression_;
public:
covariance_functor(const ArgType& arg)
: mat_{arg}
, expression_(covariance_helper<ArgType>::Covariance(arg))
{}
typename ArgType::Scalar operator() (Eigen::Index row, Eigen::Index col) const {
return expression_(row, col);
}
};
template <class ArgType>
Eigen::CwiseNullaryOp<covariance_functor<ArgType>, typename covariance_helper<ArgType>::ResultType>
Covariance(Eigen::MatrixBase<ArgType> const& arg) {
using ResultType = typename covariance_helper<ArgType>::ResultType;
return ResultType::NullaryExpr(arg.cols(), arg.cols(), covariance_functor<ArgType>(arg.derived()));
}
Вызывая это так:
TEST(Stat, Covariance) {
constexpr double kPrecision = 1e-12;
// source: https://www.itl.nist.gov/div898/handbook/pmc/section5/pmc541.htm
Eigen::Matrix<double, 5, 3> mat;
mat << 4.0, 2.0, 0.6, 4.2, 2.1, 0.59, 3.9, 2.0, 0.58, 4.3, 2.1, 0.62, 4.1, 2.2, 0.63;
Eigen::Matrix<double, 3, 3> cov_expected;
cov_expected << 0.025, 0.0075, 0.00175, 0.0075, 0.0070, 0.00135, 0.00175, 0.00135, 0.00043;
Covariance(mat);
}
выдает мне следующее сообщение об ошибке:
In file included from /test/test_stat.cpp:8:0:
/lib/core/stat.hpp: In instantiation of ‘covariance_functor<ArgType>::covariance_functor(const ArgType&) [with ArgType = Eigen::Matrix<double, 5, 3>]’:
/lib/core/stat.hpp:138:60: required from ‘Eigen::CwiseNullaryOp<covariance_functor<ArgType>, typename covariance_helper<ArgType>::ResultType> Covariance(const Eigen::MatrixBase<Derived>&) [with ArgType = Eigen::Matrix<double, 5, 3>; typename covariance_helper<ArgType>::ResultType = Eigen::Matrix<double, 3, 3>]’
/test/test_stat.cpp:19:29: required from here
/lib/core/stat.hpp:127:66: error: no matching function for call to ‘Eigen::CwiseBinaryOp<Eigen::internal::scalar_sum_op<double, double>, const Eigen::Matrix<double, 5, 3>, const Eigen::Matrix<double, 5, 3> >::CwiseBinaryOp(Eigen::CwiseBinaryOp<Eigen::internal::scalar_quotient_op<double, double>, const Eigen::Product<Eigen::Transpose<const Eigen::CwiseBinaryOp<Eigen::internal::scalar_difference_op<double, double>, const Eigen::Matrix<double, 5, 3>, const Eigen::Replicate<Eigen::PartialReduxExpr<const Eigen::Matrix<double, 5, 3>, Eigen::internal::member_mean<double>, 0>, 5, 1> > >, Eigen::CwiseBinaryOp<Eigen::internal::scalar_difference_op<double, double>, const Eigen::Matrix<double, 5, 3>, const Eigen::Replicate<Eigen::PartialReduxExpr<const Eigen::Matrix<double, 5, 3>, Eigen::internal::member_mean<double>, 0>, 5, 1> >, 0>, const Eigen::CwiseNullaryOp<Eigen::internal::scalar_constant_op<double>, const Eigen::Matrix<double, 3, 3> > >)’
, expression_(covariance_helper<ArgType>::Covariance(arg))
Что мне здесь не хватает?Возможно ли то, что я пытаюсь сделать?Разумно ли хотеть этого в контексте отложенной оценки (специфичной для ковариационных матриц)?
Это упражнение для меня (не домашняя работа), поэтому я действительно хочу знать, возможно ли вывести полный типшаблона выражения таким образом.
Меня, конечно, также интересует практичность этого подхода, но в меньшей степени.
РЕДАКТИРОВАТЬ:
, поскольку это может помочькто-то, вот рабочая версия.
template<class ArgType>
struct covariance_helper {
template<typename Derived>
static auto Covariance(Eigen::MatrixBase<Derived> const& mat) {
auto centered = mat.rowwise() - mat.colwise().mean();
return (centered.adjoint() * centered) / double(mat.rows() - 1);
}
using ResultType = Eigen::Matrix<typename ArgType::Scalar,
ArgType::ColsAtCompileTime,
ArgType::ColsAtCompileTime,
ArgType::Options,
ArgType::MaxColsAtCompileTime,
ArgType::MaxColsAtCompileTime>;
using ExpressionType = decltype(covariance_helper::Covariance(std::declval<Eigen::MatrixBase<ArgType> const&>()));
};
template<class ArgType>
class covariance_functor {
using ResultType = typename covariance_helper<ArgType>::ResultType;
using ExpressionType = typename covariance_helper<ArgType>::ExpressionType;
const ArgType &mat_;
ExpressionType expression_;
public:
covariance_functor(const ArgType& arg)
: mat_{arg}
, expression_(covariance_helper<ArgType>::Covariance(arg))
{}
typename ArgType::Scalar operator() (Eigen::Index row, Eigen::Index col) const {
return expression_(row, col);
}
};
template <class ArgType>
Eigen::CwiseNullaryOp<covariance_functor<ArgType>, typename covariance_helper<ArgType>::ResultType>
Covariance(Eigen::MatrixBase<ArgType> const& arg) {
using ResultType = typename covariance_helper<ArgType>::ResultType;
return ResultType::NullaryExpr(arg.cols(), arg.cols(), covariance_functor<ArgType>(arg.derived()));
}
Назовите это так:
TEST(Stat, Covariance) {
constexpr double kPrecision = 1e-12;
// source: https://www.itl.nist.gov/div898/handbook/pmc/section5/pmc541.htm
Eigen::Matrix<double, 5, 3> mat;
mat << 4.0, 2.0, 0.6, 4.2, 2.1, 0.59, 3.9, 2.0, 0.58, 4.3, 2.1, 0.62, 4.1, 2.2, 0.63;
Eigen::Matrix<double, 3, 3> cov_expected;
cov_expected << 0.025, 0.0075, 0.00175, 0.0075, 0.0070, 0.00135, 0.00175, 0.00135, 0.00043;
Eigen::MatrixXd cov = Covariance(mat);
ASSERT_NEAR((cov - cov_expected).norm(), 0.0, kPrecision);
}
Или более простой вариант:
template<typename Derived>
struct Covariance {
using MBase = Eigen::MatrixBase<Derived> const&;
constexpr static auto compute(MBase mat) {
auto centered = mat.rowwise() - mat.colwise().mean();
return (centered.adjoint() * centered) / double(mat.rows() - 1);
}
using ResultExpr = decltype(compute(std::declval<MBase>()));
Covariance(MBase mat)
: result_expr{compute(mat)}
{}
typename Derived::Scalar operator() (Eigen::Index row, Eigen::Index col) const {
return result_expr(row, col);
}
ResultExpr result_expr;
};
TEST(Stat, EigenUnaryExpr) {
constexpr double kPrecision = 1e-12;
// source: https://www.itl.nist.gov/div898/handbook/pmc/section5/pmc541.htm
Eigen::Matrix<double, 5, 3> mat;
mat << 4.0, 2.0, 0.6, 4.2, 2.1, 0.59, 3.9, 2.0, 0.58, 4.3, 2.1, 0.62, 4.1, 2.2, 0.63;
Eigen::Matrix<double, 3, 3> cov_expected;
cov_expected << 0.025, 0.0075, 0.00175, 0.0075, 0.0070, 0.00135, 0.00175, 0.00135, 0.00043;
Covariance cov{mat};
using ResultExpr = decltype(cov)::ResultExpr;
auto cov_expr = ResultExpr::NullaryExpr(cov.result_expr.rows(), cov.result_expr.cols(), cov);
std::cerr << cov_expr << "\n";
ASSERT_NEAR((cov_expr - cov_expected).norm(), 0.0, kPrecision);
}