本征不支持张量到本征矩阵

Eigen unsupported Tensor to Eigen matrix

在对具有更多维度的 Tensor 进行一些操作后,我得到了 Eigen::Tensor<std::complex, 2>。有没有简单的方法从这个张量对象创建一个 Eigen::MatrixXcf 还是我必须手动复制这些值?

原始答案。进一步查看更新。

我目前使用的是 Eigen 版本 3.3.4,没有简单的 built-in 函数可以在 Eigen::Tensor 类型和更熟悉的 Matrix 或 Array 类型之间进行转换。拥有 .matrix().array() 方法之类的东西会很方便。

也没有简单的方法add such methods via the plugin mechanism,因为张量模块似乎不支持插件。如果有人知道如何请评论。

同时,可以使用 Map 函数制定相当有效的解决方法。以下在 C++14 中工作,用于将张量转换为矩阵

#include <unsupported/Eigen/CXX11/Tensor>
#include <iostream>
   
template<typename T>
using  MatrixType = Eigen::Matrix<T,Eigen::Dynamic, Eigen::Dynamic>;
   
template<typename Scalar,int rank, typename sizeType>
auto Tensor_to_Matrix(const Eigen::Tensor<Scalar,rank> &tensor,const sizeType rows,const sizeType cols)
{
    return Eigen::Map<const MatrixType<Scalar>> (tensor.data(), rows,cols);
}


template<typename Scalar, typename... Dims>
auto Matrix_to_Tensor(const MatrixType<Scalar> &matrix, Dims... dims)
{
    constexpr int rank = sizeof... (Dims);
    return Eigen::TensorMap<Eigen::Tensor<const Scalar, rank>>(matrix.data(), {dims...});
}


int main () {
    Eigen::Tensor<double,4> my_rank4 (2,2,2,2);
    my_rank4.setRandom();

    Eigen::MatrixXd         mymatrix =  Tensor_to_Matrix(my_rank4, 4,4);
    Eigen::Tensor<double,3> my_rank3 =  Matrix_to_Tensor(mymatrix, 2,2,4);

    std::cout << my_rank3 << std::endl;

    return 0;
}

这同样适用于复杂类型。

不幸的是,这些函数只接受张量,而不是张量运算。例如,这不起作用:

Eigen::MatrixXd mymatrix =  Tensor_to_Matrix(my_rank4.shuffle(Eigen::array<long,4>{1,0,3,2}), 4,4);

2021 年 8 月更新:

内存泄漏已修复,请参阅下面的代码。简而言之,必须记住在 Eigen::TensorEvaluator 对象上调用 .cleanup() 成员函数来释放内部临时缓冲区。

2021 年 10 月更新:

自从我在 2018 年提交上述答案以来,我已经学会了如何使用最少的临时变量在 Eigen::TensorEigen::Matrix(及其表达式)之间进行映射和转换。

自 Eigen 3.3.7 使用 C++17 以来,我一直在使用下面的辅助函数

#include <unsupported/Eigen/CXX11/Tensor>
#include <iostream>
    
template<typename T>
using  MatrixType = Eigen::Matrix<T,Eigen::Dynamic, Eigen::Dynamic>;
template<typename T>
using  VectorType = Eigen::Matrix<T,Eigen::Dynamic, 1>;


/*
 *
 *  Convert Eigen::Tensor --> Eigen::Matrix
 *
 */


template<typename Derived, typename Device = Eigen::DefaultDevice>
class selfCleaningEvaluator {
    private:
    using Evaluator = Eigen::TensorEvaluator<const Eigen::TensorForcedEvalOp<const Derived>, Device>;
    Evaluator m_eval;

    public:
    selfCleaningEvaluator(const Evaluator &eval) : m_eval(eval) {}
    selfCleaningEvaluator(const Eigen::TensorBase<Derived, Eigen::ReadOnlyAccessors> &expr, const Device &device = Device())
        : m_eval(Evaluator(expr.eval(), device)) {
        m_eval.evalSubExprsIfNeeded(nullptr);
    }

    ~selfCleaningEvaluator() {
        // This whole point of this object is to call cleanup automatically on destruct.
        // If there are pending operations to evaluate, m_eval will allocate a buffer to hold a result,
        // which needs to be deallocated.
        m_eval.cleanup();
    }

    constexpr auto rank() {
        using DimType = typename decltype(m_eval)::Dimensions::Base;
        return DimType{}.size(); // Because Derived::Dimensions is sometimes wrong
    }
    const Evaluator *operator->() const { return &m_eval; }
    Evaluator       *operator->() { return &m_eval; }
    constexpr auto   map() {
        // We inspect m_eval to get the type, rank and dimensions, because it has the resulting tensor,
        // whereas Derived is the tensor type _before_ whatever operation is pending (if any).
        using DimType       = typename decltype(m_eval)::Dimensions::Base;
        constexpr auto rank = DimType{}.size();
        using Scalar        = typename Eigen::internal::remove_const<typename decltype(m_eval)::Scalar>::type;
        return Eigen::TensorMap<Eigen::Tensor<Scalar, rank>>(m_eval.data(), m_eval.dimensions());
    }
};

// Evaluates expressions if needed
template<typename T, typename Device = Eigen::DefaultDevice>
auto asEval(const Eigen::TensorBase<T, Eigen::ReadOnlyAccessors> &expr,  // An Eigen::TensorBase object (Tensor, TensorMap, TensorExpr... )
            const Device &device = Device()) {                           // Override to evaluate on another device, e.g. thread pool or gpu.
    return selfCleaningEvaluator(expr, device);
}

// Converts any Eigen::Tensor (or expression) to an Eigen::Matrix with shape rows/cols
template<typename T, typename sizeType, typename Device = Eigen::DefaultDevice>
auto MatrixCast(const Eigen::TensorBase<T, Eigen::ReadOnlyAccessors> &expr, const sizeType rows, const sizeType cols, const Device &device = Device()) {
    auto tensor    = asEval(expr, device);
    auto tensorMap = tensor.map();
    using Scalar   = typename decltype(tensorMap)::Scalar;
    return static_cast<MatrixType<Scalar>>(Eigen::Map<const MatrixType<Scalar>>(tensorMap.data(), rows, cols));
}

// Converts any Eigen::Tensor (or expression) to an Eigen::Matrix with shape rows/cols
template<typename T, typename Device = Eigen::DefaultDevice>
auto VectorCast(const Eigen::TensorBase<T, Eigen::ReadOnlyAccessors> &expr, const Device &device = Device()) {
    auto tensor    = asEval(expr, device);
    auto tensorMap = tensor.map();
    auto size      = Eigen::internal::array_prod(tensorMap.dimensions());
    using Scalar   = typename decltype(tensorMap)::Scalar;
    return static_cast<VectorType<Scalar>>(Eigen::Map<const VectorType<Scalar>>(tensorMap.data(), size));
}

// View an existing Eigen::Tensor as an Eigen::Map<Eigen::Matrix>
template<typename Scalar, auto rank, typename sizeType>
auto MatrixMap(const Eigen::Tensor<Scalar, rank> &tensor, const sizeType rows, const sizeType cols) {
    return Eigen::Map<const MatrixType<Scalar>>(tensor.data(), rows, cols);
}

// View an existing Eigen::Tensor of rank 2 as an Eigen::Map<Eigen::Matrix>
// Rows/Cols are determined from the matrix
template<typename Scalar>
auto MatrixMap(const Eigen::Tensor<Scalar, 2> &tensor) {
    return Eigen::Map<const MatrixType<Scalar>>(tensor.data(), tensor.dimension(0), tensor.dimension(1));
}

// View an existing Eigen::Tensor of rank 1 as an Eigen::Map<Eigen::Vector>
// Rows is the same as the size of the tensor. 
template<typename Scalar, auto rank>
auto VectorMap(const Eigen::Tensor<Scalar, rank> &tensor) {
    return Eigen::Map<const VectorType<Scalar>>(tensor.data(), tensor.size());
}


/*
 *
 *  Convert Eigen::Matrix --> Eigen::Tensor
 *
 */


// Converts an Eigen::Matrix (or expression) to Eigen::Tensor
// with dimensions specified in std::array
template<typename Derived, typename T, auto rank>
Eigen::Tensor<typename Derived::Scalar, rank>
TensorCast(const Eigen::EigenBase<Derived> &matrix, const std::array<T, rank> &dims) {
    return Eigen::TensorMap<const Eigen::Tensor<const typename Derived::Scalar, rank>>
                (matrix.derived().eval().data(), dims);
}

// Converts an Eigen::Matrix (or expression) to Eigen::Tensor
// with dimensions specified in Eigen::DSizes
template<typename Derived, typename T, auto rank>
Eigen::Tensor<typename Derived::Scalar, rank>
TensorCast(const Eigen::EigenBase<Derived> &matrix, const Eigen::DSizes<T, rank> &dims) {
    return Eigen::TensorMap<const Eigen::Tensor<const typename Derived::Scalar, rank>>
                (matrix.derived().eval().data(), dims);
}

// Converts an Eigen::Matrix (or expression) to Eigen::Tensor
// with dimensions as variadic arguments
template<typename Derived, typename... Dims>
auto TensorCast(const Eigen::EigenBase<Derived> &matrix, const Dims... dims) {
    static_assert(sizeof...(Dims) > 0, "TensorCast: sizeof... (Dims) must be larger than 0");
    return TensorCast(matrix, std::array<Eigen::Index, sizeof...(Dims)>{dims...});
}

// Converts an Eigen::Matrix (or expression) to Eigen::Tensor
// with dimensions directly as arguments in a variadic template
template<typename Derived>
auto TensorCast(const Eigen::EigenBase<Derived> &matrix) {
    if constexpr(Derived::ColsAtCompileTime == 1 or Derived::RowsAtCompileTime == 1) {
        return TensorCast(matrix, matrix.size());
    } else {
        return TensorCast(matrix, matrix.rows(), matrix.cols());
    }
}

// View an existing Eigen::Matrix as Eigen::TensorMap
// with dimensions specified in std::array
template<typename Derived, auto rank>
auto TensorMap(const Eigen::PlainObjectBase<Derived> &matrix, const std::array<long, rank> &dims) {
    return Eigen::TensorMap<const Eigen::Tensor<const typename Derived::Scalar, rank>>(matrix.derived().data(), dims);
}

// View an existing Eigen::Matrix as Eigen::TensorMap
// with dimensions as variadic arguments
template<typename Derived, typename... Dims>
auto TensorMap(const Eigen::PlainObjectBase<Derived> &matrix, const Dims... dims) {
    return TensorMap(matrix, std::array<long, static_cast<int>(sizeof...(Dims))>{dims...});
}

// View an existing Eigen::Matrix as Eigen::TensorMap
// with dimensions determined automatically from the given matrix
template<typename Derived>
auto TensorMap(const Eigen::PlainObjectBase<Derived> &matrix) {
    if constexpr(Derived::ColsAtCompileTime == 1 or Derived::RowsAtCompileTime == 1) {
        return TensorMap(matrix, matrix.size());
    } else {
        return TensorMap(matrix, matrix.rows(), matrix.cols());
    }
}



int main () {
    Eigen::Tensor<double,4> my_rank4 (2,2,2,2);
    my_rank4.setRandom();

    Eigen::MatrixXd         mymatrix =  MatrixCast(my_rank4, 4,4);   // Cast Eigen::Tensor --> Eigen::Matrix
    Eigen::Tensor<double,3> my_rank3 =  TensorCast(mymatrix, 2,2,4); // Cast Eigen::Matrix --> Eigen::Tensor

    std::cout << my_rank3 << std::endl;

    return 0;
}    

Try on compiler-explorer