1

次元配列の次元にf()沿って演算を適用したいことがよくあります。これは、 の残りのすべての次元をループすることを意味します。これができるかどうかを調べてみました。関数は、 、 、およびを含むすべての種類の で動作する必要があり、理想的には などの右辺値型でも動作する必要があります。dNAAboost::multi_arrayf(A)boost::multi_arrayboost:multi_array_refboost::detail::multi_array::sub_arrayboost::detail::multi_array::array_viewboost::multi_array_ref<T, NDims>::reference

私が思いついた最善の方法はreshape()、作業次元が常に中央の次元になるように、ND 配列を 3D 配列に再形成するために使用できる関数の実装です。ここにあるf.hpp

#include "boost/multi_array.hpp"
#include <ostream>

using namespace boost;

typedef multi_array_types::index index_t;
typedef multi_array_types::index_range range;

template <template <typename, std::size_t, typename...> class Array,
          typename T, std::size_t NDims, typename index_t, std::size_t NDimsNew>
multi_array_ref<T, NDimsNew>
reshape(Array<T, NDims>& A, const array<index_t, NDimsNew>& dims) {
    multi_array_ref<T, NDimsNew> a(A.origin(), dims);
    return a;
}

template <template <typename, std::size_t, typename...> class Array, typename T>
void f(Array<T, 1>& A) {
    for (auto it : A) {
        // do something with it
        std::cout << it << " ";
    }
    std::cout << std::endl;
}

template <template <typename, std::size_t, typename...> class Array, 
          typename T, std::size_t NDims>
void f(Array<T, NDims>& A, long d) {
    auto dims = A.shape();
    typedef typename std::decay<decltype(*dims)>::type type;

    // collapse dimensions [0,d) and (d,Ndims)
    array<type, 3> dims3 = {
        std::accumulate(dims, dims + d, type(1), std::multiplies<type>()),
        dims[d],
        std::accumulate(dims + d + 1, dims + NDims, type(1), std::multiplies<type>())
    };

    // reshape to collapsed dimensions
    auto A3 = reshape(A, dims3);

    // call f for each slice [i,:,k]
    for (auto Ai : A3) {
        for (index_t k = 0; k < dims3[2]; ++k) {
            auto S = Ai[indices[range()][k]];
            f(S);
        }
    }
}

template <template <typename, std::size_t, typename...> class Array, 
          typename T, std::size_t NDims>
void f(Array<T, NDims>& A) {
    for (long d = NDims; d--; ) {
        f(A, d);
    }
}

これはテストプログラムtest.cppです:

#include "f.hpp"

int main() {
    boost::multi_array<double, 3> A(boost::extents[2][2][3]);
    boost::multi_array_ref<double, 1> a(A.data(), boost::extents[A.num_elements()]);
    auto Ajk = A[1];
    auto Aik = A[boost::indices[range()][1][range()]];

    int i = 0;
    for (auto& ai : a) ai = i++;

    std::cout << "work on boost::multi_array_ref" << std::endl;
    f(a);

    std::cout << "work on boost::multi_array" << std::endl;
    f(A);

    std::cout << "work on boost::detail::multi_array:sub_array" << std::endl;
    f(Ajk);

    std::cout << "work on boost::detail::multi_array:sub_array" << std::endl;
    f(Aik);   // wrong result, since reshape() ignores strides!

    //f(A[1]);   // fails: rvalue A[1] is boost::multi_array_ref<double, 3ul>::reference
}

明らかに、このアプローチには問題があります。つまり、スライスが に渡されるf()と、メモリが連続しなくなり、 の実装が無効になりreshape()ます。

より良い (より C++ に似た) 方法は、指定された次元に沿った非ユニティ ストライドを自動的に処理するため、ブースト型が提供する反復子から集計反復子を構築することです。boost::detail::multi_array::index_gen関連しているように見えますが、これを使用して dimension 内のすべてのスライスに対してイテレータを作成する方法がよくわかりませんd。何か案は?

ノート:

SOにはすでに同様の質問がありますが、どれも私にとって非常に満足のいくものではありませんでした。N = 3またはの特殊なソリューションには興味がありませんN = 2。どんなものでもうまくいくはずNです。

アップデート:

これは私がPythonで欲しいものと同等です:

def idx_iterator(s, d, idx):
    if len(s) == 0:
        yield idx
    else: 
        ii = (slice(None),) if d == 0 else xrange(s[0])
        for i in ii:
            for new_idx in idx_iterator(s[1:], d - 1, idx + [i]):
                yield new_idx

def iterator(A, d=0):
    for idx in idx_iterator(A.shape, d, []):
        yield A[idx]

def f(A):
    for d in reversed(xrange(A.ndim)):
        for it in iterator(A, d):
            print it
        print

import numpy as np
A = np.arange(12).reshape((2, 2, 3))

print "Work on flattened array"
f(A.ravel())

print "Work on array"
f(A)

print "Work on contiguous slice"
f(A[1])

print "Work on discontiguous slice"
f(A[:,1,:])

の機能を使用して何とか同じことが可能になるはずですindex_gen.hppが、私はまだその方法を理解できていません。

4

1 に答える 1