1
votes

I have the following code:

template <typename T>
using Arr = Array<T, Dynamic, 1>;

template <typename T>
using Arr2 = Array<T, Dynamic, Dynamic>;

template <typename T>
Arr2<typename T::Scalar> reshape (const ArrayBase<T> & A, const uint n, const uint m) {
    return Map<const Arr2<typename T::Scalar>>(A.eval().data(), n, m);
}

Arr<double> v = Arr<double>::LinSpaced(16, 0, 15);
auto w = reshape(v, 4, 4).transpose();
std::cout << w << std::endl;

which produces the error

static void Eigen::internal::checkTransposeAliasing_impl::run(const Derived&, const OtherDerived&) [with Derived = Eigen::Array; OtherDerived = Eigen::Transpose >; bool MightHaveTransposeAliasing = true]: Assertion `(!check_transpose_aliasing_run_time_selector ::IsTransposed,OtherDerived> ::run(extract_data(dst), other)) && "aliasing detected during transposition, use transposeInPlace() " "or evaluate the rhs into a temporary using .eval()"' failed.

Adding eval indeed fixes the issue:

auto w = reshape(v, 4, 4).transpose().eval();

(but adding it do the printing line as << w.eval() << doesn't).

I would like to have a better understanding of what going on and to find out if there's another way to fix this.

Thank you

1

1 Answers

0
votes

Basically you can only create expression/reference of an existing object. When the object is out of scope, the expression/reference is invalid/undefined.

With auto w, you are creating an expression of a temp Arr2 object, whose life ends after this line , making w invalid. The assertion you saw helps check this situation. This temp object is created when you cast a Map object to an Arr2 object at the line of return, which involves an undesired data copy from the input vector to the temp Arr2 object. This happens to be one of the situations that you should not use auto. If you would accept one more copy, the following code will work. It is equivalent to you adding .eval() in your original code.

#include <Eigen/Eigen>
#include <iostream>
using namespace Eigen;

template<typename T>
using Arr = Array<T, Dynamic, 1>;
template<typename T>
using Arr2 = Array<T, Dynamic, Dynamic>;

template<typename T>
Arr2<typename T::Scalar> reshape(const ArrayBase<T> & A, const uint n,
                                 const uint m) {
  return Map<const Arr2<typename T::Scalar>>(A.eval().data(), n, m);
}

int main() {
  Arr<double> v = Arr<double>::LinSpaced(16, 0, 15);
  Arr2<double> w = reshape(v, 4, 4).transpose();
  std::cout << w << std::endl;
  return 0;
}

Alternatively, changing the return type to Map<const Arr2<typename T::Scalar>> is a better way to avoid the two copies mentioned above, and you can still use auto to keep w as an expression. The following code is a better working version.

#include <Eigen/Eigen>
#include <iostream>
using namespace Eigen;

template<typename T>
using Arr = Array<T, Dynamic, 1>;
template<typename T>
using Arr2 = Array<T, Dynamic, Dynamic>;

template<typename T>
Map<const Arr2<typename T::Scalar>> reshape(const ArrayBase<T> & A,
                                            const uint n, const uint m) {
  return Map<const Arr2<typename T::Scalar>>(A.eval().data(), n, m);
}

int main() {
  Arr<double> v = Arr<double>::LinSpaced(16, 0, 15);
  auto w = reshape(v, 4, 4).transpose();
  std::cout << w << std::endl;
  return 0;
}

On the other hand, I think your reshape is still wrong when A is an expression, as A.eval().data() is a temp object whose life ends after the function call. Your reshape API will confuse people who don't know this restriction.

Actually creating a reshape function is unnecessary, as constructing a Map object itself semantically means reshaping some data in a contiguous memory space. I would recommend the following form. It is only a little bit longer than the word "reshape", no temp buffer/data copy involved, and auto w is still an expression object.

#include <Eigen/Eigen>
#include <iostream>
using namespace Eigen;

template<typename T>
using Arr = Array<T, Dynamic, 1>;
template<typename T>
using Arr2 = Array<T, Dynamic, Dynamic>;

int main() {
  Arr<double> v = Arr<double>::LinSpaced(16, 0, 15);
  auto w = Map<Arr2<double>>(v.data(), 4, 4).transpose();
  std::cout << w << std::endl;
  return 0;
}