0

これが長すぎる場合は申し訳ありませんが、質問を明確にする必要があると思います:

私はExcel用のxllライブラリ、つまりセルから直接登録して呼び出すことができる関数を含むCライブラリに取り組んでいます。理想的には、これらの関数は VBA からも呼び出す (または呼び出すように調整する) 必要があります。これにより、Excel シート内ではうまく収まらない、より複雑な計算 (ルート検索、ode、最適化) の解釈環境が提供されます。明確にするために:vba(関数Application.Run)からシート関数を呼び出す方法がありますが、すべてのパラメーターと戻り値のバリアント型との間の変換の許容できない代償を払います。ここで面白いのは、同じ Excel 環境内で、2 次元行列がさまざまな方法で渡されるということです。

  • シート関数の場合、ネイティブの Excel-C インターフェイスは行列を C に行優先順 (Excel SDK のタイプ FP12) で渡します。

  • vba の場合、行列は LPSAFEARRAY であり、データは列優先順に編成されています。

1 次元データ (ベクトル) の場合、BLAS (30 年前) にさかのぼるソリューションがあり、次のような構造を持つ C に変換できます。

struct VECTOR { 
    int n;
    int stride;
    double * data;
    double & operator [] (int i) { return data[(i - 1)*stride]; }   
}   

言い換えれば、データを所有していないが、連続したデータまたは一定のギャップ (ストライド) で線形に配置されたデータの両方をマッピングできる中間構造を計算に使用します。Struct のデータは順次処理できますが、配列セクションに変換することもできます (cilk を使用している場合)。

data [ 0 : n : stride ]

ベクトルから行列に移行するとき、行ストライドと列ストライドの両方を使用して行列の順序から抽象化できることを読みました。私の素朴な試みは次のとおりです。

struct MATRIX {
    int rows;
    int cols;
    int rowstride;
    int colstride;
    double * data;
    inline double & operator () (int i, int j)  { return data[(i - 1)*rowstride + (j - 1)*colstride]; }
    MATRIX(int nrows, int ncols, int incrw, int inccl, double* dt) {rows = nrows; cols = ncols, rowstride = incrw; colstride = inccl; data = dt; }
    MATRIX(FP12 & A)        { rows = A.rows;  cols = A.cols;  data = A.array; rowstride = cols; colstride = 1;  }
    MATRIX(LPSAFEARRAY & x) { rows = ROWS(x); cols = COLS(x); data = DATA(x); rowstride = 1;    colstride = rows; }
    int els() { return rows * cols; }
    bool isRowMajor() { return rowstride > 1; }
    bool isScalar()   { return (rows == 1) & (cols == 1); }
    bool isRow()      { return (rows == 1); }
    bool isCol()      { return (cols == 1); }
    VECTOR col(int i) { return {rows, rowstride, &data[(i - 1)*colstride] }; }      // Col(1..)
    VECTOR row(int i) { return {cols, colstride, &data[(i - 1)*rowstride] }; }      // Row(1..)
    VECTOR all()      { return {els(), 1, data}; }  
    void copyFrom   (MATRIX & B) { for (int i = 1; i <= rows; i++) ROW(*this, i) = ROW(B, i); }
    MATRIX & Transp (MATRIX & B) { for (int i = 1; i <= rows; i++) ROW(*this, i) = COL(B, i); return *this; }
    void BinaryOp   (BinaryFcn f, MATRIX & B);
    MATRIX TranspInPlace() { return MATRIX(cols, rows, colstride, rowstride, data); }
    MATRIX SubMatrix(int irow, int icol, int nrows, int ncols) { return MATRIX(nrows, ncols, rowstride, colstride, &(*this)(irow, icol)); }
};  

FP12 または LPSAFEARRAY からの 2 つのコンストラクターは、行優先または列優先で編成されたデータを指す構造を初期化します。これは順序中立ですか? 私の意見では、はい: データ アクセス (インデックス作成) と行/列の選択の両方が、順序とは関係なく正しいです。2 つの乗算があるため、インデックス作成は遅くなりますが、行と列を非常に迅速にマップすることができます。行列ライブラリの目的は、1 回のデータ アクセスを最小限に抑えることです。さらに、行または列の配列セクションを返すマクロを作成するのは非常に簡単で、行列全体も同様です。

#define COL(A,i) (A).data[(i-1)*(A).colstride : (A).rows : (A).rowstride]           // COL(A,1)
#define ROW(A,i) (A).data[(i-1)*(A).rowstride : (A).cols : (A).colstride]           // ROW(A,1)
#define FULL(A)  (A).data[0 : (A).rows * (A).cols]                                  // FULL MATRIX

上記のコードでは、インデックスは 1 から始まりますが、ハードコードされた 1 の代わりに (変更可能な) ofs 0-1 パラメーターを使用して抽象化することもできます。 all() 関数 / FULL() マクロは全体に対してのみ正しいです。 、連続した行列であり、部分行列ではありません。ただし、これも調整できます。この場合、行のループに切り替えます。上記の copyFrom method() と多かれ少なかれ似ており、行列を行優先表現と列優先表現の間で変換 (コピー) できます。

TranspInPlace メソッドにも注意してください。rows/cols と rowstride/colstride を交換することにより、同じ未処理のデータの論理的な転置が行われます。つまり、論理スイッチを汎用ルーチン (行列乗算の GEMM など) に渡す必要がなくなります。 、(公平を期すために)これは共役転置の場合をカバーしていませんが)。

上記を考慮して、このアプローチを実装するライブラリを探して使用 (フック) できるようにしていますが、これまでの検索では満足のいくものではありませんでした:

GSL Gsl は行優先順を使用します。止まる。

LAPACK ネイティブ コードは列優先です。C インターフェイスは、行優先のデータを処理する可能性を提供しますが、テーラーメイドのコードを追加するか、(いくつかのルーチンで) 行列を操作する前に物理的に転置するだけです。

EIGEN テンプレート化されたライブラリであるため、両方を扱うことができます。残念ながら、マトリックスの順序はテンプレートのパラメーターです。つまり、すべてのマトリックス メソッドが複製されます。動作しますが、理想的ではありません。

ライブラリが私が求めているものに近いかどうか教えてください。どうも。

4

1 に答える 1

1

MapEigen では、ランタイムの内側と外側のストライドを使用してこれを模倣できます。たとえば、そのままにしておくとColumnMajor、内側のストライドは行のストライドに対応し、外側のストライドは列のストライドに対応します。

Map<MatrixXd,0,Stride<> > mat(ptr, rows, cols, Stride<>(colStride,rowStride));

次に、行、列matへのアクセス、積の実行、線形システムの解決など、に対して任意の操作を実行できます。mat.row(i)mat.col(j)

このアプローチの主な欠点は、SIMD ベクトル化が失われることです。

于 2016-11-05T13:23:54.273 に答える