Eigen, как получить доступ к базовому массиву MatrixBase <derived>
Мне нужно получить доступ к массиву, который содержит данные матрицы MatrixBase Eigen.
Библиотека Eigen имеет метод data(), который возвращает указатель на массив, однако он доступен только из типа Matrix. MatrixBase не имеет аналогичного метода, хотя класс MatrixBase должен действовать как шаблон, а фактический тип должен быть просто матрицей. Если я попытаюсь получить доступ к MatrixBase.data(), я получаю ошибку времени компиляции:
template <typename ScalarA, typename Index, typename DerivedB, typename DerivedC>
void uscgemv(float alpha,
const USCMatrix<ScalarA,Index> &a,
const MatrixBase<DerivedB> &b,
const MatrixBase<DerivedC> &c_const)
{
//...some code
float * bMat = b.data();
///more code
}
Этот код создает следующую ошибку времени компиляции.
error: ‘const class Eigen::MatrixBase<Eigen::CwiseNullaryOp<Eigen::internal::scalar_constant_op<float>, Eigen::Matrix<float, -1, 1> > > has no member named ‘data
float * bMat = b.data();
Поэтому я должен прибегать к трюкам, таким как...
float * bMat;
int bRows = b.rows();
int bCols = b.cols();
mallocPinnedMemory(&bMat, bRows*bCols*sizeof(float));
Eigen::Map<Matrix<float, Dynamic, Dynamic> > bmat_temp(bMat, bRows, bCols);
bmat_temp = b; //THis is SLOW, we should avoid it.
Затем я могу получить доступ к массиву bMat...
Эти копии назад и вперед являются самыми большими затратами на умножение матрицы gpu, поскольку я, по сути, должен сделать дополнительную копию, прежде чем даже справиться с устройством...
Я не могу использовать Eigen-magma, так как это разреженный матричный-в-странный формат для плотного матричного (или иногда векторного) умножения, поэтому я не могу использовать какие-либо функции автоматического gpu там. Также я бы скорее не объявлял матрицы как что-то еще, потому что это потребовало бы изменения МНОГО строк кода во всей программе (что я не писал).
EDIT: Было предложено статическое решение для литья:
float * bMat = (static_cast<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> >(b)).data();
Однако я получаю segfault при первой попытке доступа к элементу массива bMat.
EDIT 2: Я ищу нулевой способ копирования для доступа к базовым массивам. Мне нужно только читать b, но мне также нужно написать c. В настоящее время c не имеет ограничений со следующим макросом:
#define UNCONST(t,c,uc) Eigen::MatrixBase<t> &uc = const_cast<Eigen::MatrixBase<t>&>(c);
EDIT 3: После перекрестного перехода на Eigen Forums, похоже, я не могу сделать лучше, чем предлагаемый ответ.
Ответы
Ответ 1
MatrixBase
является базовым классом любого плотного выражения. Это не обязательно соответствует объекту с хранилищем. Например, может быть абстрактное представление A+B
, или в вашем случае абстрактное представление вектора с постоянными значениями. Вы можете заставить uscgemv принимать только выражение, имеющее соответствующее хранилище, используя класс Ref<>
, например:
template <typename ScalarA, typename Index>
void uscgemv(float alpha,
const USCMatrix<ScalarA,Index> &a,
Ref<const VectorXf> b,
Ref<VectorXf> c);
Если третий аргумент не соответствует хранилищу VectorXf
он будет оценен для вас. Затем вы можете безопасно вызвать b.data()
. Чтобы сохранить скалярный тип b
generic, вы все равно можете объявить его как MatrixBase<DerivedB>&
а затем скопировать его в Ref<const Matrix<typename DerivedB::Scalar, DerivedB::RowsAtCompileTime, DerivedB::ColsAtCompileTime> >
:
typedef Ref<const Matrix<typename DerivedB::Scalar, DerivedB::RowsAtCompileTime, DerivedB::ColsAtCompileTime> > RefB;
RefB actual_b(b);
actual_b.data();
Ответ 2
Я предполагаю, что проблема такова: вам не разрешено получать указатель на данные MatrixBase<Derived>
, поскольку последнее может быть любым выражением в Eigen
, например, произведением матриц. Чтобы получить указатель, вы, вероятно, должны сначала неявно преобразовать MatrixBase<Derived>
в Matrix<Scalar, Dynamic, Dynamic>
, а затем использовать член data()
последнего.
Таким образом, вы можете создать глубокую копию выражения, то есть использовать что-то вроде
Eigen::Matrix<typename Derived::Scalar, Eigen::Dynamic, Eigen::Dynamic tmp = b;
затем используйте
tmp.data()
Этот код работает сейчас
#include <Eigen/Dense>
#include <iostream>
template<typename Derived>
void use_data\
(const Eigen::MatrixBase<Derived>& mat)
{
Eigen::Matrix<typename Derived::Scalar, Eigen::Dynamic, Eigen::Dynamic>tmp = mat();
typename Derived::Scalar* p = tmp.data();
std::cout << std::endl;
for(std::size_t i = 0; i < tmp.size(); i++)
std::cout << *(p+i) << " ";
}
int main()
{
Eigen::MatrixXd A = Eigen::MatrixXd::Random(2, 2);
Eigen::MatrixXd B = Eigen::MatrixXd::Random(2, 2);
// now A*B is an expression, of type MatrixBase<EigenSum....>
use_data(A + B);
}
Ответ 3
Есть простое решение для решения вашего вопроса, объедините EigenMap, & a (0, 0) и const_cast, вы можете восстановить буфер MatrixBase.
Пример:
template<typename Derived1,
typename Derived2>
void example(Eigen::MatrixBase<Derived1> const &input,
Eigen::MatrixBase<Derived2> const &output)
{
static_assert(std::is_same<Derived1::Scalar, Derived2::Scalar>::value,
"Data type of matrix input, weight, bias and output should be the same");
using Scalar = typename Derived3::Scalar;
using MatType = Eigen::Matrix<Scalar, Eigen::Dynamic, 1>;
using Mapper = Eigen::Map<const MatType, Eigen::Aligned>;
//in the worst case, you can do const_cast<Scalar *> on
//&bias(0, 0).That is, if you cannot explicitly define the Map
//type as const
Mapper Map(&input(0, 0), input.size());
output.colwise() += Map;
}
}
Я пробовал это на windows 8, vc2013 32bits, Eigen версии 3.2.5, никакой ошибки сегментации (пока), все выглядит отлично. Я также проверяю адрес Карты, это то же самое, что и исходный вход. Вы можете проверить это с помощью другого примера
#include <Eigen/Dense>
#include <iostream>
template<typename Derived>
void example_2(Eigen::MatrixBase<Derived> &input)
{
using Scalar = decltype(input[0]);
Eigen::Map<Derived> map(&input(0, 0),
input.rows(),
input.cols());
map(0, 0) = 300;
}
int main()
{
Eigen::MatrixXd mat(2, 2);
mat<<0, 1, 2, 3;
example_2(mat);
std::cout<<mat<<"\n\n";
return 0;
}
Первым элементом мата будет "300",