创建多数组

时间:2016-02-09 14:32:13

标签: c++ arrays boost multidimensional-array generic-programming

我正在编写一个c ++函数来计算边缘PDF(概率密度函数)。这基本上意味着我获得了沿着许多变量的网格定义的多维数据(PDF)。我希望将数据集成到未定义的维数上,以保持函数的通用性。

PDF的尺寸可以是任意的,边缘PDF的尺寸也可以是任意的。无法定义输入数据的维度顺序,因此我向该函数发送一个向量,该向量表示需要保留哪些变量。其他变量需要集成。

所以例如: 变量数:5(a,b,c,d,e),PDF数据维5,计算(a,c,d)的边际PDF。这意味着需要保留变量/尺寸0,2,3并且需要将其他变量/尺寸整合出来(通过一个确定的积分)。 所以:PDF [a] [b] [c] [d] [e] - > MARGPDF [a] [c] [d](包含其他值) 对于每个[a] [c] [d]我需要对其他维度的数据执行操作[b] [e]。我可以通过查看来做到这一点,但我现在不能动态地做到这一点。动态我的意思是我希望保留尺寸和尺寸的数量可以自由选择。

基本上,我想要的是创建维度b和e中所有值的视图,并为a,c,d的每个(循环)值执行此操作。但是,我希望函数是通用的,这样输入可以是任何多数组,输出变量可以自由选择。所以它也可以是:PDF [a] [b] [c] [d] - > MARGPDF [c]或PDF [a] [b] [c] [d] [e] [f] - > MARGPDF并[b] [d]。

我有以下想法: 我按维度对PDF多数组进行排序,这样我就可以创建最后一个维度的视图,所以: PDF [a] [b] [c] [d] [e]变为PDF [a] [c] [d] [b] [e]。然后我遍历每个a,c,d并创建剩余的2个维度b和e的视图。我使用此视图执行计算并将值保存到MARGPDF [a] [c] [d]。

执行此类操作我需要知道的是: 如何切换boost :: multi_array的维度/索引的顺序? 如何在尺寸空闲时创建视图? 或者你有任何其他想法来完成同样的事情吗?

我的代码的开头如下:

template<class DataType, int Dimension, int ReducedDimension>
boost::multi_array<DataType, ReducedDimension> ComputeMarginalPDF(boost::multi_array<DataType, Dimension> PDF,
                                                           std::vector< std::vector<DataType> > Variables , std::vector<int> VarsToKeep ){
// check input dimensions
if (VarsToKeep.size() != ReducedDimension ){
    std::cout << "Dimensions do not match" << std::endl;
}

std::vector< std::vector<double> > NewVariables(0) ;

// Construct reduced array with proper dimensions
typedef boost::multi_array< DataType , ReducedDimension > ReducedArray ;
boost::array< ReducedArray::index , ReducedDimension > dimensions;

// get dimensions from array and insert into dimensions ;
// set Marginal PDF dimensions
for(int i = 0 ; i < VarsToKeep.size() ; i++){
    dimensions[i] = PDF.shape()[ VarsToKeep[i] ] ;
    NewVariables.push_back( Variables[ VarsToKeep[i] ] );
}

ReducedArray Marginal(dimensions) ;

// to be filled with code

我希望我不要混淆。欢迎任何改进问题的建议。

2 个答案:

答案 0 :(得分:2)

几个月前我遇到过类似的问题,但我只需要计算一维边距。这是对我有用的解决方案的概述,我想它也可以适用于多维边缘:

我基本上将pdf存储在一维数组/向量中(使用你喜欢的任何东西):

double* pdf = new double[a*b*c*d*e];

然后我使用了你可以存储二维数组a[width][height]作为一维数组b[widht*height]并访问任何元素a[x][y]作为b[width*x + y]。您可以将此公式推广为任意维度,并且通过正确使用模/整数除法,您还可以计算逆矩阵。

使用模板,从一维索引到N维索引的计算(反之亦然)非常简单。这样,您就可以将依赖于维度的符号PDF[a][b][c][d][e]转换为PDF(std::vector<size_t>{a,b,c,d,e})之类的符号,这种符号可以很容易地扩展到任意维度,因为您可以在循环中提前填充向量。

如果您认为这种方法可能会对您有所帮助,我可以尝试抓住我的实现的一些关键功能并在此处添加它们。

修改

template <size_t DIM>
inline void posToPosN(const size_t& pos,
    const size_t* const size,
    size_t* const posN){
    size_t r = pos;

    for (size_t i = DIM; i > 0; --i){
        posN[i - 1] = r % size[i - 1];
        r /= size[i - 1];
    }
}

template <size_t DIM>
inline void posNToPos(size_t& pos,
    const size_t* const size,
    const size_t* const posN){
    pos = 0;
    size_t mult = 1;

    for (size_t i = DIM; i > 0; --i){
        pos += mult * posN[i - 1];
        mult *= size[i - 1];
    }
}

template<typename type, size_t DIM>
class Iterator{
private:
    type* const _data; //pointer to start of Array
    size_t _pos; //1-dimensional position
    size_t _posN[DIM]; //n-dimensional position
    size_t const * const _size; //pointer to the _size-Member of Array
    size_t _total;

private:

public:
    Iterator(type* const data, const size_t* const size, size_t total, size_t pos)
        : _data(data), _pos(pos), _size(size), _total(total)
    {
        if (_pos > _total || _pos < 0) _pos = _total;
        posToPosN<DIM>(_pos, _size, _posN);
    }

    bool operator!= (const Iterator& other) const
    {
        return _pos != other._pos;
    }

    type& operator* () const{
        if (_pos >= _total)
            std::cout << "ERROR, dereferencing too high operator";
        return *(_data + _pos);
    }

    const Iterator& operator++ ()
    {
        ++_pos;
        if (_pos > _total) _pos = _total;

        posToPosN<DIM>(_pos, _size, _posN);
        return *this;
    }

    Iterator& operator +=(const size_t& b)
    {
        _pos += b;
        if (_pos > _total) _pos = _total;

        posToPosN<DIM>(_pos, _size, _posN);
        return *this;
    }

    const Iterator& operator-- ()
    {
        if (_pos == 0)
            _pos = _total;
        else
            --_pos;

        posToPosN<DIM>(_pos, _size, _posN);
        return *this;
    }

    //returns position in n-th dimension
    size_t operator[](size_t n){
        return _posN[n];
    }

    //returns a new iterator, advanced by n steps in the dim Dimension
    Iterator advance(size_t dim, int steps = 1){
        if (_posN[dim] + steps < 0 || _posN[dim] + steps >= _size[dim]){
            return Iterator(_data, _size, _total, _total);
        }

        size_t stride = 1;
        for (size_t i = DIM - 1; i > dim; --i){
            stride *= _size[i];
        }

        return Iterator(_data, _size, _total, _pos + steps*stride);
    }
};


template <typename type, size_t DIM>
class Array{
    type* _data;
    size_t _size[DIM];
    size_t _total;

    void init(const size_t* const dimensions){
        _total = 1;
        for (int i = 0; i < DIM; i++){
            _size[i] = dimensions[i];
            _total *= _size[i];
        }

        _data = new type[_total];
    }

public:
    Array(const size_t* const dimensions){
        init(dimensions);
    }

    Array(const std::array<size_t, DIM>& dimensions){
        init(&dimensions[0]);
    }

    ~Array(){
        delete _data;
    }
    Iterator<type, DIM> begin(){
        return Iterator<type, DIM>(_data, _size, _total, 0);
    }
    Iterator<type, DIM> end(){
        return Iterator<type, DIM>(_data, _size, _total, _total);
    }
    const size_t* const size(){
        return _size;
    }
};


//for projections of the PDF
void calc_marginals(size_t dir, double* p_xPos, double* p_yPos){
    assert(dir < N_THETA);

    std::lock_guard<std::mutex> lock(calcInProgress);

    //reset to 0
    for (size_t i = 0; i < _size[dir]; ++i){
        p_yPos[i] = 0;
    }

    //calc projection
    double sum = 0;
    for (auto it = _p_theta.begin(); it != _p_theta.end(); ++it){

        p_yPos[it[dir]] += (*it);
        sum += (*it);
    }

    if (abs(sum - 1) > 0.001){ cout << "Warning: marginal[" << dir << "] not normalized" << endl; }
    //calc x-Axis
    for (size_t i = 0; i < _size[dir]; ++i){
        p_xPos[i] = _p[dir].start + double(i) / double(_size[dir] - 1)*(_p[dir].stop - _p[dir].start);
    }
}

代码由几部分组成:

  • 两个函数posToPosN()posNToPos(),它们在一维和DIM维坐标之间进行上述转换。尺寸在此处作为模板参数DIM给出。 pos只是一维位置,posN指向大小为DIM的数组的指针,指的是DIM维坐标,size是一个大小为{{1}的数组包含不同方向的宽度(在您的情况下类似{a,b,c,d,e})
  • DIM是一个Iterator类,允许在DIM-Dimensional Array上使用基于范围或基于迭代器的for循环。注意Iterator返回DIM维坐标的第n个分量,operator[](size_t n)函数返回迭代器到坐标为advance()的元素
  • {posN[0], posN[1], ...,posN[dim] + steps , ... posN[DIM]}应该非常简单
  • Array是我用来计算边际的函数。 calcMarginals在这里是我想要计算边际的方向(记住:一维边际)并写入dirp_xPosp_yPos是{{1} }。注意基于迭代器的for循环,_p_theta在这里指的是存储在数组中的pdf的double值,就像通常的迭代器那样。另外,Array返回dim方向的实际值的坐标。写入p_xPos的最后一个循环只是因为我不想要这个数组中的索引而是真正的值。

我想如果你重新定义(*it)以获取维度索引的向量/数组并返回适当坐标的向量/数组,并为随机访问添加it[dim],这需要一个向量/数组也应该完成。

答案 1 :(得分:1)

我解决了这个问题。我想我无法创建任意维度的boost :: multi_array,因为它需要维度作为模板参数,需要在编译时知道。这意味着我无法创建任意维度的视图。

因此,我做了以下事情: 我对PDF进行了排序,以便将要整合的维度是最后的维度(很可能不是最有效的方法)。 然后我逐一减少了PDF的尺寸。每个循环我只集成了1个维度,我保存在一个与初始数组大小相同的multi_array中(因为我无法使维度动态化)。 之后,我将值复制到缩小尺寸的multi_array(已知)。

我使用以下链接在尺寸上独立地进行循环:

// Dimension-independent loop over boost::multi_array?