【问题标题】:C++ Eigen: dynamic tensorC++ Eigen:动态张量
【发布时间】:2026-02-01 15:50:01
【问题描述】:

我想实现一个 C++ 类,它有一个张量向量作为成员。张量的维度不是预定义的,但会根据一些输入数据取值。此外,张量的等级可以不同。像这样的:

std::vector< TensorXd > myTensors;

然而,在Eigen 中,没有用于动态张量的TensorXd 类型。

为了构造每个张量,我将读取一个数据向量std::vector&lt;double&gt; values,它表示维度为n x n x ... x nr 次)的张量。像这样的:

Tensor<double, r> tensor = TensorMap<double, r>(values.data(), std::vector<size_t>(r, n);
myTensors.push_back(tensor);

有可能吗?

非常感谢您的帮助!

更新:

正如 Yaroslav Bulatov 所指出的,Eigen 不支持动态排名,因此必须明确写出支持的排名。在我的代码中:

#include <iostream>
#include <vector>
#include <Eigen/Dense>
#include <unsupported/Eigen/CXX11/Tensor>

typedef Eigen::Tensor< double , 3 > Tensor3d;
typedef Eigen::Tensor< double , 4 > Tensor4d;
typedef Eigen::Tensor< double , 5 > Tensor5d;
typedef Eigen::Tensor< double , 6 > Tensor6d;
typedef Eigen::Tensor< double , 7 > Tensor7d;
typedef Eigen::Tensor< double , 8 > Tensor8d;
typedef Eigen::Tensor< double , 9 > Tensor9d;
typedef Eigen::Tensor< double , 10 > Tensor10d;

class MyClass
{
private:                          
    Eigen::MatrixXd Potentials_1;            
    std::vector<Eigen::MatrixXd> Potentials_2;  
    std::vector< Tensor3d > Potentials_3;
    std::vector< Tensor4d > Potentials_4;
    std::vector< Tensor5d > Potentials_5;
    std::vector< Tensor6d > Potentials_6;
    std::vector< Tensor7d > Potentials_7;
    std::vector< Tensor8d > Potentials_8;
    std::vector< Tensor9d > Potentials_9;
    std::vector< Tensor10d > Potentials_10;

public:
    MyClass();
    void setPotentials_1(const Eigen::MatrixXd &_Potentials_1){ Potentials_1 = _Potentials_1; }
    void setPotentials_2(const std::vector<Eigen::MatrixXd> &_Potentials_2){ Potentials_2 = _Potentials_2; }
    void setPotentials_3(const std::vector<Tensor3d> &_Potentials_3){ Potentials_3 = _Potentials_3; }
    void setPotentials_4(const std::vector<Tensor4d> &_Potentials_4){ Potentials_4 = _Potentials_4; }
    void setPotentials_5(const std::vector<Tensor5d> &_Potentials_5){ Potentials_5 = _Potentials_5; }
    void setPotentials_6(const std::vector<Tensor6d> &_Potentials_6){ Potentials_6 = _Potentials_6; }
    void setPotentials_7(const std::vector<Tensor7d> &_Potentials_7){ Potentials_7 = _Potentials_7; }
    void setPotentials_8(const std::vector<Tensor8d> &_Potentials_8){ Potentials_8 = _Potentials_8; }
    void setPotentials_9(const std::vector<Tensor9d> &_Potentials_9){ Potentials_9 = _Potentials_9; }
    void setPotentials_10(const std::vector<Tensor10d> &_Potentials_10){ Potentials_10 = _Potentials_10; }
};

Yaroslav 还建议使用宏有助于避免代码重复。我不熟悉 C++ 宏,因此非常感谢任何帮助。

感谢您的帮助!

【问题讨论】:

  • Eigen 不支持动态排名,因此必须明确写出每个支持的排名,使用宏来节省代码重复。请参阅 github.com/tensorflow/tensorflow/commit/eaf96c45 以获取对 ops 额外排名的支持的示例
  • @YaroslavBulatov 谢谢。我不熟悉 C++ 宏。您能否阅读更新并告诉我如何在我的情况下使用宏?非常感谢!

标签: c++ tensorflow eigen


【解决方案1】:

在我的实现中,我通过使用新的 C++ 17 实用程序 std :: variant 克服了这个问题;我用它作为联合模板

typedef std::variant<Eigen::Tensor<double, 2>, Eigen::Tensor<double, 3>, /* ... */> TensorOptions;

我定义了上面的特征以便于阅读。

// i is the order of the tensor
TensorOptions makeTensor(const std::size_t& i,const std::initializer_list<int>& dimensions) const
        {
            int * Arr= new int[i];
            std::copy(std::begin(dimensions), std::end(dimensions), Arr);
            switch (i) {
        
               case 2: {
                    Eigen::Tensor<double, 2> T2;
                    T2.resize(Arr);
                    return T2;
                }
                case 3: {
                    Eigen::Tensor<double, 3> T3;
                    T3.resize(Arr);
                    return T3;
                }
                /* ... */
              }
              delete [] Arr;
         }
                
int main() {
        auto myTensor{makeTensor(2, {4, 5})};  // Tensor 2D 4x5
 }

我想指出,要访问这些方法,必须使用std :: visit。这里我举几个例子

// Rank
auto rnk = std::visit([](const auto &tensor) { return tensor.rank(); }, myTensor);



// Unfolding
// idxResh and idxSh are dynamic arrays

Eigen::Tensor<double,2> unfolded = std::visit([&idxResh, &idxSh]( auto& tensor) {
        // Shuffle
        auto tensSh=tensor.shuffle(idxSh);
        // Reshape
        Eigen::Tensor<double,2> tensResh=tensSh.reshape(idxResh);
        return tensResh;},myTensor);

【讨论】:

  • 不错。我不能很快检查这个,但它看起来很整洁。 +1
【解决方案2】:

您可以查看支持动态和静态维度的 xtensor C++ 模板库。

http://xtensor.readthedocs.io/en/latest/

xtensor 有一个与numpy 非常相似的API,包括向量化、广播、通用功能。这里有一个 numpy to xtensor 备忘单:http://xtensor.readthedocs.io/en/latest/numpy.html

最后,您可以点击https://github.com/QuantStack/xtensor/ 顶部的活页夹徽章,在 C++ Jupyter 笔记本中实时试用它

xtensor 还为科学计算的主要语言(R、Julia、Python)提供了绑定。

【讨论】:

  • 谢谢。好一个!但是 xtensor 在性能方面与 Eigen tensor 相比如何呢?尤其是张量收缩(这是我项目中的关键操作)。
  • 没有系统的基准。 SIMD 加速最近才插入 xtensor。
  • 我检查了文档,似乎 xtensor 还不支持张量收缩。更具体地说,我需要使用 (D-1) 个向量执行 D 维张量的(内部)乘积(因此结果是一个向量)。是否有可能在 xtensor 中有效地做到这一点?我想我应该发布一个单独的问题。
  • 有一个xtensor-blas 项目,它公开了一些 blas 级别的功能,但还没有一般的张量收缩。目前,xtensor 只有几个月的历史,而且我们正在添加新功能。