Main Content

スパース行列のコード生成

スパース行列は、多くのゼロ要素をもつ配列の効率的なメモリ内のストレージを提供します。スパース行列により、パフォーマンスが向上し、生成されたコードのメモリ使用量が削減されます。スパース行列の計算時間は、非ゼロ要素に対する演算数のみに合わせてスケーリングします。

スパース行列の作成および操作のための関数は、スパース行列にリストされています。コード生成で関数がサポートされているかどうかを確認するには、関数のリファレンス ページを参照してください。コード生成では、すべての関数で sparse を使用して作成されたスパース行列入力をサポートしていません。

生成されたコードでのスパース データ型

ターゲット言語が C の場合、コード ジェネレーターは sparse というスパース行列向けの型定義を作成します。この定義には、スパース行列の行インデックス、列インデックスおよび対応する要素値の配列が格納されます。sparse 型定義はファイル myFunction_types.h に生成されます。myFunction は、最上位レベル関数の名前を指します。

ターゲット言語が C++ の場合、コード ジェネレーターはクラス sparse をファイル sparse.h に作成します。

スパース行列内の非ゼロ要素数は、計算中に変化する可能性があります。この理由から、生成されたコードのスパース行列は可変サイズ配列および動的メモリ割り当てを使用します。ターゲット言語が C の場合、生成されたコードは emxArray 型を使用して動的に割り当てられた変数を実装します。ターゲット言語が C++ の場合、生成されたコードは coder::array クラス テンプレートを使用して動的に割り当てられた変数を実装します。

たとえば、関数 myDiag について考えます。

function out = myDiag(n,k)
% create diagonal sparse matrix
%#codegen
A = speye(n);
out = A.*k;
end

codegen コマンドを使用して関数のコードを生成します。

codegen -config:lib myDiag -args {3, 5} -launchreport

スパース型はファイル myDiag_types.h にあります。

入力定義

スパース行列を入力として受け入れる関数 foo があるとします。この関数は、スパース行列に単位行列を乗算して積を出力します。

function C = foo(ASparseInput)
%#codegen
B = speye(size(ASparseInput'));
C = ASparseInput*B;

MATLAB® 環境の外で使用するために、スタンドアロンの libdll または exe コードを生成するとします。lib コードを生成するには、次を入力します。

codegen -config:lib foo -args {sparse(5,5)} -launchreport

スパース行列を入力として渡すのではなくエントリポイント関数の内部でスパース行列を作成することで、スタンドアロンのコードを単純化できます。このガイドラインに従うと、スパース行列の作成はコード ジェネレーターが行います。生成されたコードを使用するその他のコードは、特殊なスパース型ではなく配列のような入力型を渡すことができます。

たとえば、foo から直接コードを生成せずに、新しいエントリポイント関数 fooMain を作成して、ここからコードを生成します。スパース入力をスパース データの三つ組形式に置き換えます。

function [ii,jj,out] = fooMain(i,j,v,m,n)
%#codegen
S = sparse(i,j,v,m,n);
[ii,jj,out] = find(foo(S));

可変サイズの非ゼロ要素数をもつ 5 行 5 列のスパース行列 S のコードを生成するとします。コードを生成するには、次を入力します。

S = sparse(5,5);
[m,n] = size(S);
[i,j,v] = find(S);
i = coder.typeof(i,[inf 1]);
codegen -config:lib fooMain -args {i,i,i,m,n} -launchreport

整数および可変サイズの配列タイプを使用する fooMain の入力を指定できます。foo から直接コードを生成する場合、入力を sparse 型として作成しなければなりません。

スパース行列をエントリポイント関数の入力として渡すことを選択する場合、入力を初期化するために coder.typeof を使用できます。たとえば、関数 foo の場合、次を入力できます。

t = coder.typeof(sparse(5,5));
codegen -config:lib foo -args {t} -launchreport

スパース行列の場合、コード ジェネレーターは可変サイズの次元の上限を追跡しません。すべての可変サイズの次元は制限なしとして扱われます。

foo の MEX 関数を生成する場合、入力データおよび出力データは sparse 型に変換しなければなりません。MEX 関数を繰り返し呼び出したり、入力および出力が大きくなると、この変換によりパフォーマンスが低下する可能性があります。

assert ステートメントを使用して、スパース入力の型をプログラムで定義することはできません。

コード生成ガイドライン

コードの効率性を最大化するためにスパース コンストラクターを使用して行列を初期化します。たとえば、3 行 3 列の単位行列を作成するには、sparse(eye(3,3)) ではなく speye(3,3) を使用します。

スパース行列へのインデックス付き代入では、非スパース行列へのインデックス付き代入と比較して、オーバーヘッドが発生します。例:

S = speye(10);
S(7,7) = 42;

MATLAB でのように、スパース行列は圧縮されたスパース列形式で格納されます。新しい非ゼロ要素をスパース行列に挿入する場合、すべての後続の非ゼロ要素は 1 列ずつ下方向にシフトしなければなりません。これらの余分な操作によりパフォーマンスが遅くなる可能性があります。スパース行列へのアクセスを参照してください。

コード生成の制限

スパース行列を使用するコードを生成するには、動的メモリ割り当てを有効にしなければなりません。数が変化する非ゼロ要素およびその値を格納するために、スパース行列は生成されたコードで可変サイズ配列を使用します。動的メモリ割り当て設定を変更するには、可変サイズの配列に対するメモリ割り当ての制御を参照してください。スパース行列が動的メモリ割り当てに可変サイズ配列を使用するため、可変サイズ データの制限もスパース行列に適用されます。

スパース データを非スパースのデータに割り当てることはできません。生成されたコードは、スパース行列と非スパース行列では異なるデータ型表現を使用します。スパース データの変換には明示的な変換関数 sparse および full を使用します。

矛盾するサイズ仕様を使用するスパース行列を定義できません。コード ジェネレーターが対応するデータ型定義を C/C++ で作成する際に、スパース行列のサイズを修正します。たとえば、関数 foo はコード生成でエラーになります。

function y = foo(n)
%#codegen
if n > 0
    y = sparse(3,2);
else
    y = sparse(4,3);
end

スパース行列への論理インデックス作成は、コード生成ではサポートされていません。たとえば、この構文はエラーになります。

S = magic(3);
S(S > 7) = 42;

スパース行列の場合、空の配列の割り当てにより配列要素を削除することはできません。

S(:,2) = [];

参考

| | | | |

関連するトピック