スパース行列乗算で非ゼロの数を決定する最良の方法は何ですか?


17

両方の行列がCSCまたはCSR形式であると仮定して、スパース行列の乗算演算で事前に非ゼロの数を見つけるための高速で効率的な方法があるかどうか疑問に思いました。

smmpパッケージにあるものは知っていますが、CまたはC ++で既に実装されているものが必要です。

任意の助けをいただければ幸いです。前もって感謝します。


行列に対称性、または非ゼロエントリの位置に対する構造がありますか?
ゴドリックシーア

@GodricSeer ...いいえ私は一般的なスパース行列について話しているだけです。
レッカー

私は個人的には、対称性や構造を利用せずに実際の行列乗算を行うよりも低次の数を計算する方法を考えられません。私はあなたが乗算を行う前にメモリ割り当てのためにこれが欲しいと仮定していますか?
ゴドリックシーア

また、ブール行列積の数を推定する方法を説明するこの論文を見つけました(これは、行列積の要素を数えるのと同じです)。
ゴドリックシーア

@GodricSeer ..はい、正しいです。結果のマトリックスのメモリ割り当てのために正確な数が必要です。しかし、紙へのリンクをありがとう。
レッカー

回答:


14

2つのスパースパターンの積を形成することで、マトリックスとマトリックスの積をシミュレートできます。つまり、スパースパターン(CSR形式で別の配列に格納されている)を0または1のいずれかを含むマトリックスと見なします。各エントリ。このシミュレートされた製品を実行するにはこれらのゼロと1での操作は、実際の行列行列積よりもはるかに高速です。実際、2つの行列の行と列を調べて、少なくとも1つのエントリがあることを確認するだけです。両方の行列がゼロ以外の場合、乗算する行と列。これは安価な操作です。実際の製品で実際に浮動小数点の乗算を行う必要がある場合よりもはるかに安く、浮動小数点演算(高価な)を行うだけでなく、メモリから実際の浮動小数点数を読み込む必要があります(さらに高価ですが、行列のゼロ以外の値はCSRに個別に保存されるため、スパースパターンを乗算するときにその必要はありません)。


6
これはシンボリック乗算と呼ばれます。特に並列では、数値乗算よりも必ずしも安価ではありませんが、スパースパターンごとに1回だけ実行する必要があります。多くのアルゴリズムは、異なる数値で同じスパースパターンを使用して操作を複数回行います。この場合、シンボリック乗算を再利用できます。
ジェドブラウン

これはいい考えですが、float * floatを並列に実行している何百万ものトランジスタを考えると、ここでは50%程度の速度の節約について話しているだけです。
エフゲニーセルゲイエフ

1
@EvgeniSergeev-ポイントは計算の節約ではなく、メモリ転送の節約です。現在、スパース行列乗算のメモリ転送に80%以上の時間を費やしているため、メモリとの間で浮動小数点データの読み取り/書き込みを行う必要がない場合、大幅に増加する可能性があります。
ウルフギャングバンガース

メソッドの複雑さを明確に述べてください。場合はあるメートルによるKあなたの方法が必要であることを私に見えるO M K 正しい仕事を?CmkOmk
カールクリスチャン

Omkpm=kOmpログpOm2

13

実際には、AとBの両方がスパースであるA * Bの元のコードをMatlabで作成しました。結果のためのスペースの事前割り当ては確かに興味深い部分でした。Godricが指摘していることを観察しました。ABの非ゼロの数を知ることは、ABを計算するのと同じくらい費用がかかります。

ABのサイズを正確に推定する最初の実用的で高速な方法を示したEdith Cohenの論文の前に、1990年頃にスパースMatlabの初期実装を行いました。下位のサイズ推定器を作成し、計算の途中でスペースがなくなった場合、割り当てを2倍にし、部分的に計算された結果をコピーしました。

現在、Matlabに何があるのか​​わかりません。

別の可能性は、一度に1列ずつABを計算することです。各列は一時的にスパースアキュムレータに保存でき(これらの説明についてはスパースMatlabの論文を参照)、結果列の正確に既知のサイズを保持するために割り当てられたスペース。結果は、CSCの各列で、列間連続性のない散在圧縮スパース列形式になります。メタデータとして1つではなく、長さnumcols(col start、col length)の2つのベクトルを使用します。そのストレージ形式は一見の価値があるかもしれません。別の強みがあります。行列全体を再割り当てせずに列を拡大できます。


まあ私のGPUの実装のために、私は最初の非ゼロ構造を見つけ、expected.Iは、彼らがに記載された方法を使用考えると、実際のmatrix.Performanceは恐ろしいことでした見つけてしまったこの MATLAB上で効率的に乗算2つのスパース行列に本を。
レッカー

2
本当にクールで、歴史的な観点に感謝し、scicompへようこそ:)
アロン

4

このホワイトペーパーでは、2つのスパースマトリックスのマトリックス積の結果のサイズを近似するアルゴリズムについて説明します。

スパース行列乗算で非ゼロエントリの正確な数を見つける際の問題は、結果の各要素が2つのベクトルの相互作用に依存することです。両方のベクトルは少なくともいくつかの非ゼロ要素を含む可能性があります。したがって、数値を計算するには、結果のすべての要素のベクトルのペアで論理演算を評価する必要があります。これに伴う問題は、行列積自体を計算するために必要な操作の数に類似した操作の数を必要とすることです。私のコメントで、元の行列の非ゼロ要素の特定の構造を活用する可能性について言及しましたが、それらの同じ活用を使用して、行列乗算で行われる作業を減らすこともできます。

上記のペーパーを使用してメモリ要件を過大評価し、乗算を行ってから割り当てられたメモリを切り捨てるか、結果の行列をより適切なサイズの配列に移動する方が良いでしょう。また、まばらな行列積はめったに発生するものではなく、この問題が以前に解決されたことをほぼ保証します。いくつかのオープンソースのスパースマトリックスライブラリを少し掘り下げると、メモリを事前に割り当てるために使用するアルゴリズムに導かれるはずです。


0

CSRまたはCSCの場合、行列要素の配列にすでにゼロがないことが保証されていますか?その場合、次のようなものを使用して、ゼロ以外の要素がいくつあるかを簡単に把握できます。

int nnz = sizeof(My_Array)/sizeof(long int);

これはあなたが試すことができるもの(少し簡単すぎると思われる)場合ではない場合はあるの減少。行列要素の配列が非常に大きい場合、これは非ゼロ要素の数を計算する最も効率的な方法かもしれません。Thrust(CUDAライブラリー)やOpenCL(GPUを使用する必要はありません)などの多くの並列C / C ++ライブラリーは、条件付き縮小をサポートしています-各要素に対して、の結果を追加しCondition(Element)ます。条件を設定するとElement != 0、ゼロ以外の要素の数が加算されます。要素の配列、行/列インデックスの配列からゼロ値の要素を削除し、列/行ポインターを調整することもできます。


返信ありがとうございます...しかし、私はA * Bの非ゼロについて言及していました。ここで、AとBはスパース行列です。結果の行列を格納するために正確なメモリ量を割り当てることができるように、事前に非ゼロの数が必要です。
レッカー

0

CSRを実装する最も簡単な方法は、

std::vector< std::map<int, complex<float>> > 

マトリックスを表現します。その場合、ゼロ以外の要素の数については実際には心配しません。すべてにアクセスするには

std::map< int, complex<float> >::iterator

各行に。ベスト..


2
STL。スパースマトリックスルーチンをこれ以上遅くすることはできないと思ったとき。
ジェドブラウン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.