Creating arbitrary views of multi array
我正在编写一个 c 函数来计算边际 PDF(概率密度函数)。这基本上意味着我得到了沿多个变量的网格定义的多维数据 (PDF)。我想在未定义的维度上整合数据以保持函数的通用性。
PDF的维度可以是任意的,边缘PDF的维度也可以是任意的。无法定义输入数据的维度顺序,因此我向函数发送了一个向量,其中说明了需要保留哪些变量。其他变量需要整合。
例如:
变量数:5(a,b,c,d,e) ,PDF 数据维度 5,计算 (a,c,d) 的边际 PDF。这意味着需要保留变量/维度 0、2、3,而其他变量/维度需要积分(通过定积分)。
所以: PDF[a][b][d][e] -> MARGPDF[a][d] (包含其他值)
对于每个 [a][d],我需要对其他维度 [b][e] 中的数据执行操作。我可以通过制作视图来做到这一点,但是我现在不知道如何动态地做到这一点。动态是指我希望可以自由选择维度的数量和保留的维度。
基本上,我想要的是创建维度 b 和 e 中所有值的视图,并对 a、c、d 的每个(循环)值执行此操作。但是,我希望该函数具有通用性,以便输入可以是任何多数组,并且可以自由选择输出变量。所以它也可能是: PDF[a][b][d] -> MARGPDF 或 PDF[a][b][d][e][f] -> MARGPDF[b ][d].
我有以下想法:
我按维度对 PDF 多数组进行排序,这样我就可以创建最后一个维度的视图,因此:
PDF[a][b][d][e] 变为 PDF[a][d][b][e] 。然后我循环遍历每个 a、c、d 并创建剩余 2 个维度 b 和 e 的视图。我使用此视图执行计算并将值保存到 MARGPDF[a][d]。
执行这样的操作我需要知道的是:
如何切换 boost::multi_array 的维度/索引的顺序?
尺寸空闲时如何创建视图?
或者你有其他想法来完成同样的事情吗?
下面提供了我的代码的开头:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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 |
我希望我不会混淆。欢迎提出任何改进问题的建议。
几个月前我遇到了类似的问题,但我只需要计算一维边际。这是对我有用的解决方案的大纲,我想它也可以适应多维边缘:
我基本上将 pdf 存储在一维数组/向量中(使用任何你喜欢的东西):
1 | double* pdf = new double[a*b*c*d*e]; |
然后我使用你可以将二维数组
从一维索引到 N 维索引以及反之亦然的计算都使用模板非常简单。这允许您将取决于您的维度的符号
如果您认为这种方法可能对您有所帮助,我可以尝试获取我的实现的一些关键功能并将它们添加到此处。
编辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | 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 是大小为DIM 的数组,包含不同方向的宽度(在您的情况下,例如 {a,b,c,d,e}) -
Iterator 是一个迭代器类,允许在 DIM 维数组上进行基于范围或基于迭代器的 for 循环。注意返回 DIM 维坐标的第 n 个分量的operator[](size_t n) 和返回指向坐标为{posN[0], posN[1], ...,posN[dim] + steps , ... posN[DIM]} 的元素的迭代器的advance() 函数 -
Array 应该很简单 -
calcMarginals 是我用于计算边际的函数。dir 是我要计算边际的方向(记住:一维边际)并写入p_xPos 和p_yPos ,_p_theta 是Array 。请注意基于迭代器的 for 循环,(*it) 在这里指的是存储在数组中的 pdf 的双值,就像通常的迭代器一样。此外,it[dim] 返回实际值在方向暗淡的坐标。写入 p_xPos 的最后一个循环只是因为我不想要这个数组中的索引而是真正的值。
我猜如果您重新定义
我解决了这个问题。我认为我无法创建任意维度的 boost::multi_array,因为它需要维度作为模板参数,这需要在编译器时知道。这意味着我无法创建任意维度的视图。
因此,我做了以下事情:
我对 PDF 进行了排序,以便将整合出来的维度是最后一个维度(很可能不是最有效的方法)。
然后我将PDF的尺寸一一缩小。每个循环我只集成了 1 个维度,我将其保存在与初始数组大小相同的 multi_array 中(因为我无法使维度动态化)。
之后,我将值复制到减小大小的 multi_array(已知)。
我使用以下链接在维度上独立地循环维度:
// boost::multi_array 上的维度无关循环?