web-dev-qa-db-ja.com

高速スパース行列乗算

クラスの場合、疎行列用の独自の線形方程式ソルバーを作成する必要があります。私はスパース行列に任意のタイプのデータ構造を自由に使用でき、共役勾配法を含むいくつかの解法を実装する必要があります。

ベクトルとの乗算が比較的高速になるようなスパース行列を格納する有名な方法があるかどうか疑問に思っていました。

現在、私の疎行列は基本的にラップされたstd::map< std::pair<int, int>, double>存在する場合、データを格納します。これは、各行列要素のルックアップを実行する必要があるため、ベクトルからO(n²)の複雑度からO(n²log(n))への行列の乗算を変換します。イェールスパースマトリックス形式を調べたところ、要素の取得もO(log(n))のように思われるため、はるかに高速かどうかはわかりません。

参考までに、5000のエントリが入力された800x800マトリックスがあります。共役勾配法でこのようなシステムを解くには、およそ450秒かかります。

別のデータ構造でそれをはるかに速く行うことが可能だと思いますか?

ありがとう!

18
lezebulon

最も一般的な選択肢は CSCまたはCSRストレージ です。これらは両方とも、行列とベクトルの乗算に効率的です。また、自分で行う必要がある場合は、これらの乗算ルーチンをコーディングするのも非常に簡単です。

とはいえ、イェールストレージも非常に効率的な行列-ベクトル乗算を生成します。マトリックス要素のルックアップを実行している場合、フォーマットの使用方法を誤解しています。行列とベクトルの乗算がどのように実装されているかを学ぶために、いくつかの標準スパースライブラリを検討することをお勧めします。

現在のストレージでも、O(n)複雑さ)で行列乗算を実行できます。これまでに見たすべての疎行列-ベクトル乗算アルゴリズムは、同じステップに要約されます。たとえば、y =斧。

  1. 結果のベクトルyをゼロ化します。
  2. 行列Aの非ゼロ要素の反復子を初期化します。
  3. A [i、j]は、行列の次の非ゼロ要素を取得します。 i、jのパターンは通常のパターンに従っていないことに注意してください。これは単に、Aの非ゼロ要素が格納される順序を反映しています。
  4. y [i] + = A [i、j] * x [j]
  5. Aの要素がさらにある場合は、3に進みます。

私はあなたが古典的なdouble forループ密乗算コードを書いているのではないかと思います:

for (i=0; i<N; i++)
    for (j=0; j<N; j++)
        y[i] += A[i,j]*x[j]

それが、ルックアップを実行するように導いているものです。

しかし、私はあなたがあなたのstd::map ストレージ。それは非常に効率的ではありません。 CSCが最も広く使用されているので、主にCSCをお勧めします。

24
David Heffernan