2016-08-24 12 views
0

私はdocumentationの多くを読んだことがありますが、私が逃したものが見つかった場合は、私の問題を説明することができます。背景としては、3.2.7固有ライブラリを使用してVisual Studio 2015でx86 Windows 10でコンパイルしています。 3.2.7のバージョンは5月からのもので、その後リリースされている間に私はchangelogに何も見ていないので、私の問題が解決されたことを示しています。大きなブロック係数の乗算がEigenライブラリで失敗するC++

この問題は、特定のサイズを超える行列に対してのみ発生するようです。私はこれが私のシステムに固有の何か、またはアイゲン固有の何かの副産物かどうかはわかりません。

次のコードは、デバッグモードとリリースモードの両方でアクセス違反を生成します。

int mx1Rows = 255, cols = 254; 
{//this has an access violation at the assignment of mx2 
     Eigen::MatrixXd mx1(mx1Rows, cols); 
     Eigen::MatrixXd mx2(mx1Rows + 1, cols); 
     Eigen::Block<Eigen::MatrixXd, -1, -1, false> temp = mx2.topRows(mx1Rows); 
     mx2 = temp.array() * mx1.array();//error 
} 

私は結果がaliasedする必要がありますので、係数毎の乗算の割り当ては安全であると信じています。

この問題は、mx1Rowsが値254に減少すると、アクセス違反が表示されない場合には面白くなります。それは正しいですが、256 x 254のmx2寸法では問題は発生しますが、寸法は255 x 254ではありません。列のサイズを大きくすると、アクセス違反も発生する可能性があるので、問題はエントリの総数と関係している可能性があります。問題はmx1とmx2が値で満たされていても表示されますが、埋め込まれた行列を持つことで問題を再現する必要はありません。

tempにtopRows()ブロックを割り当てない同様のコードは、リリースモードでアクセス違反を生成しません。もともとこの問題はかなり複雑であり、特定の数のループ(行列のサイズはループ間で一貫していた)の後にのみ現れたので、この問題がこれ以上あると思います。私のコードでは、あまりにも多くのことが起こっているため、特定の回数のループの後にしかアクセス違反が発生しない条件を切り離すことができませんでした。私が知って好奇心何

1)私はいくつか明らかに間違った方法で固有値を使用していますか?

2)この問題を再現できますか? (あなたの環境の詳細は?)

3)これはEigenライブラリのバグですか?

この問題を回避するには、ブロックではなく一時ブロックにブロックを割り当てるだけで十分ですが、非効率であってもそのことを知りたいとは思いません。

+1

負の1のインデックスが許されているように見えるバグのように見えます。行列を初期化すると、mx1Rowsとcolの小さな値でも配列multにガベージが置かれていることがわかります。 'temp.array()。eval()。array()'を実行すると問題ありません。あなたはデータの外でエイリアシングしていません。 x64で実行されますが、間違っています。それは間違いではありません。 – doug

答えて

2

問題はtempmx2によって保持された係数を参照するが、式が評価される前に、最後の割り当てでは、mx2が最初にサイズ変更されることです。したがって、式の実際の評価の間に、tempはごみを参照します。より正確には、ここでは実際に(簡略化された方法で)生成されたものである。

double* temp_data = mx2.data; 
free(mx2.data); 
mx2.data = malloc(sizeof(double)*mx1Rows*cols); 
for(j=0;j<cols;++j) 
    for(i=0;i<mx1Rows;++i) 
    mx2(i,j) = temp_data[i+j*(mw1Rows+1)] * mx1(i,j); 

これはaliasing issueと呼ばれています。

あなたは一時的に式を評価することによって回避することができます

MatrixXd temp = mx2.topRows(mx1Rows); 
mx2 = temp.array() * mx1.array(); 

さらに別の解決策を評価することである。

mx2 = (temp.array() * mx1.array()).eval(); 

別の解決策は、独自のメモリを保持している真のMatrixXdmx2.topRows(.)をコピーすることですその後tempに変更してからサイズを変更してください。

Block<MatrixXd, -1, -1, false> temp = mx2.topRows(mx1Rows); 
temp = temp.array() * mx1.array(); 
mx2.conservativeResize(mx1Rows,cols); 
+0

この種の問題は、(オブジェクトではなく)他の場所で操作されるオブジェクトへの参照を保持する状況で常に発生します。 – Walter

+0

Eigenの容量の概念(STLの文字列の容量とサイズのような)がない限り、コンポーネント単位の乗算は再配置を必要としないはずです。それは基本的に問題ですか? LHSはもはや同じ次元を持たないので、RHSは配列インタフェースが使用されているためエイリアス行列乗法を使用していないため、再割り当てされ、これが問題になります。これが当てはまる場合、おそらく次のことは再配分や腐敗がなければ成功するだろうか? mx2.topRows(mx1Rows)= mx2.topRows(mx1Rows).array()* mx1.array(); –

+0

これは私が提案した最新の解決策ですが、 'temp'をショートカットとして使用しています。また、デフォルトでMatrixXdにはカラムメジャーストレージがあるので、 'temp'のカラムは順次メモリに格納されません。したがって、ここでは、conservativeResizeは最初に、最後のバイトを解放する前に、メモリコピーを介してカラムをパックする必要があります。もちろん、サイズ変更を省略し、残りの計算で 'mx2'の代わりに' temp'を使用することもできます。 – ggael

-2

小さなサイズにも影響するバグのように見えます。正しい結果を得るために、バグ誘発ラインのコメントを削除します。

訂正。 ggaelの答えが指摘するように、それはエイリアシングです。これは、後で同じオブジェクトで使用されるtempを作成するためにautoを使用してよく見られるタイプのものです。

#include <iostream> 
#include <Eigen/Dense> 

int main() 
{//this has an access violation at the assignment of mx2 
    //const int mx1Rows = 255, cols = 254; 
    const int mx1Rows = 3, cols = 2; 
    Eigen::MatrixXd mx1(mx1Rows, cols); 
    int value = 0; 
    for (int j = 0; j < cols; j++) 
     for (int i = 0; i < mx1Rows; i++) 
      mx1(i,j)=value++; 
    Eigen::MatrixXd mx2(mx1Rows + 1, cols); 
    for (int j = 0; j < cols; j++) 
     for (int i = 0; i < mx1Rows+1; i++) 
      mx2(i,j)=value++; 
    Eigen::Block<Eigen::MatrixXd, -1, -1> temp = mx2.topRows(mx1Rows); 
    mx2 = temp.array()/*.eval().array()*/ * mx1.array();r 
    std::cout << mx2.array() << std::endl; 
} 

// with /*.eval().array()*/ uncommented 
//0 30 
//7 44 
//16 60 

// Original showing bug 
//-0 -4.37045e+144 
//-1.45682e+144 -5.82726e+144 
//-2.91363e+144 -7.28408e+144 
+0

これは問題を説明するものではありませんが、単にコメントには適しているが解答ではない方法で説明するだけです。 – Walter

+0

@Walter Agreed。これは、autoを使用するときに複数行の "temps"にも発生するエイリアシングのタイプです。効果的に 'Eigen :: Block temp 'は、tempをMatrixXd宣言して右のブロックを使うのではなく、それを行います。私はそれを見たはずです。 – doug

関連する問題