次元に依存しないコードを作成するにはどうすればよいですか?


19

特定の操作/アルゴリズムの1次元、2次元、3次元のバージョンについて、非常によく似たコードを書くことがよくあります。これらのバージョンをすべて維持するのは面倒です。単純なコード生成はかなりうまく機能しますが、より良い方法が必要だと考えられているようです。

一度操作を記述し、それを高次元または低次元に一般化する比較的簡単な方法はありますか?

具体例の1つ​​は次のとおりです。スペクトル空間で速度場の勾配を計算する必要があるとします。3次元では、Fortranループは次のようになります。

do k = 1, n
  do j = 1, n
    do i = 1, n
      phi(i,j,k) = ddx(i)*u(i,j,k) + ddx(j)*v(i,j,k) + ddx(k)*w(i,j,k)
    end do
  end do
end do

ここで、ddx配列は適切に定義されています。(マトリックス乗算でもこれを行うことができます。)2次元フローのコードはほぼ同じですが、3番目の次元がループ、インデックス、およびコンポーネントの数から削除されます。これを表現するより良い方法はありますか?

別の例は次のとおりです。3次元グリッド上で点ごとに定義された流体速度があるとします。速度を任意の位置(つまり、グリッドポイントに対応しない)に補間するには、3次元すべてにわたって連続して1次元ネビルアルゴリズムを使用します(つまり、次元削減)。単純なアルゴリズムの1次元の実装を前提として、次元削減を行う簡単な方法はありますか?

回答:


13

deal.II(http://www.dealii.org/)がそれをどのように行うかを見てください。そこでは、次元の独立性がライブラリの中心にあり、ほとんどのデータ型に対するテンプレート引数としてモデル化されています。たとえば、ステップ4チュートリアルプログラムの次元に依存しないラプラスソルバーを参照してください。

http://www.dealii.org/developer/doxygen/deal.II/step_4.html

こちらもご覧ください

https://github.com/dealii/dealii/wiki/Frequently-Asked-Questions#why-use-templates-for-the-space-dimension


私は強く同意します。Deal.IIがやっていることよりも良いアプローチを見つけていない。彼らはこの問題を回避するために非常に興味深い方法でテンプレートを使用しています。
エルディラ

1
優れたリソースですが、C ++テンプレートをgrockしなければ威圧的です。
meawoppl

@Wolfgang Bangerth:deal.iiはテンプレートを使用してイテレータも定義しますか?
マシューエメット

@MatthewEmmett:はい。
ウォルフガングバンガース

@meawoppl:実際、いいえ。私は定期的に取引のクラスを教えていますII。最初は、ClassWhatever <2>が2dに、ClassWhatever <3>が3dに、ClassWhatever <dim>がdim-dになっていることをすべて生徒に伝えます。3週目のどこかでテンプレートに関するレッスンを行いますが、それ以前は学生がそれどのように機能する理解していない可能性がありますが、とにかくそれを使用して完全に機能ています。
ウルフギャングバンガース

12

この質問は、ほとんどの「プレーンな」プログラミング言語(少なくともC、Fortran)では、これをきれいに行えないことを強調しています。追加の制約は、表記上の利便性優れたパフォーマンスが必要であることです。

そのため、代わりに書い次元固有のコードを、コード作成を検討生成次元固有のコードを。計算コードがそうでなくても、このジェネレーターは次元に依存しません。つまり、表記法と計算を表すコードの間に推論の層を追加します。C ++テンプレートの量は同じです。逆に、テンプレートは言語に直接組み込まれています。欠点は、書くのがやや面倒だということです。これにより、コードジェネレーターを実際にどのように実現するかという疑問が軽減されます。

OpenCLを使用すると、実行時にコードをかなりきれいに生成できます。また、「外部制御プログラム」と「内部ループ/カーネル」の間の非常にきれいな分割を行います。外側の生成プログラムはパフォーマンスの制約がはるかに少ないため、Pythonなどの快適な言語で作成することもできます。それが、PyOpenCLがどのように使用されるかについての私の希望です。


アンドレアス!scicompへようこそ!このサイトをご利用いただきありがとうございます。ご質問がございましたら、ご連絡ください。
アロンアフマディア

2
C ++マジックの代わりに、この問題の解決策として自動コード生成のために+10000。
ジェフ

9

これは、次の大まかなメンタルプロトタイプを使用して、任意の言語で実現できます。

  1. 各次元の範囲のリストを作成します(MATLABのshape()のようなものです)
  2. 各次元で現在の場所のリストを作成します。
  3. 外側のループに基づいてサイズが変化するループを含む、各次元のループを記述します。

そこから、コードをnd準拠に保つために、特定の言語の構文と戦うことが問題になります。

n次元の流体力学ソルバーを作成したので、オブジェクトのようなリストを関数の引数としてアンパックすることをサポートする言語が役立つことがわかりました。すなわち、a =(1,2,3)f(a *)-> f(1,2,3)。さらに、高度なイテレータ(numpyのndenumerateなど)により、コードが1桁クリーンになります。


これを行うためのPython構文は、簡潔で簡潔に見えます。Fortranでこれを行う良い方法があるのだろうか?
マシューエメット

1
Fortranで動的メモリを扱うのは少し苦痛です。おそらく私の言語に関する私の主な苦情。
-meawoppl

5

n1×n2×n3nj=1


したがって、ディメンションに依存しないようにするには、maxdim + 1ディメンション用にコードを記述する必要があります。maxdimは、ユーザーが遭遇する可能性のある最大ディメンションです。maxdim = 100としましょう。結果のコードはどれほど便利ですか?
ジェフ

4

Fortranの速度を維持したい場合の明確な答えは、JuliaやC ++などの適切なコード生成を備えた言語を使用することです。C ++テンプレートはすでに言及されているので、ここでJuliaのツールについて言及します。Juliaの生成された関数を使用すると、メタプログラミングを使用して、型情報を介してオンデマンドで関数を作成できます。ここでできることは基本的に

@generated function f(x)
   N = ndims(x)
   quote
     # build the code for the function
   end
end

次に、を使用して、ディメンションをN指定して、実行するコードをプログラムでビルドしますN。次に、ジュリアのデカルトライブラリまたはEinsum.jlのようなパッケージ式を、N次元関数用に簡単に構築できます。

ここでJuliaの良い点は、この関数が静的にコンパイルされ、使用する新しい次元配列ごとに最適化されるため、必要以上にコンパイルされず、C / Fortranの速度が得られることです。結局、これはC ++テンプレートを使用するのと似ていますが、より簡単にするための多くのツールを備えた高レベル言語です(学部生にとって素晴らしい宿題の問題になるほど簡単です)。

これに適した別の言語は、Common LispのようなLispです。Juliaと同様に、多くの組み込みイントロスペクションツールを備えたコンパイル済みASTを提供するため、使いやすいですが、Juliaとは異なり、ほとんどのディストリビューションでは自動的にコンパイルされません。


1

私は同じ(Fortran)ボートに乗っています。1D、2D、3D、および4D(射影幾何を行う)要素を取得したら、各タイプに対して同じ演算子を作成し、その後、何が起こっているかを明確にする高レベルの方程式でロジックを記述します。各操作の個別のループと大量のメモリコピーがあると思うほど遅くはありません。コンパイラ/プロセッサに最適化を行わせます。

例えば

interface operator (.x.)
    module procedure cross_product_1x2
    module procedure cross_product_2x1
    module procedure cross_product_2x2
    module procedure cross_product_3x3
end interface 

subroutine cross_product_1x2(a,b,c)
    real(dp), intent(in) :: a(1), b(2)
    real(dp), intent(out) :: c(2)

    c = [ -a(1)*b(2), a(1)*b(1) ]
end subroutine

subroutine cross_product_2x1(a,b,c)
    real(dp), intent(in) :: a(2), b(1)
    real(dp), intent(out) :: c(2)

    c = [ a(2)*b(1), -a(1)*b(1) ]
end subroutine

subroutine cross_product_2x2(a,b,c)
    real(dp), intent(in) :: a(2), b(2)
    real(dp), intent(out) :: c(1)

    c = [ a(1)*b(2)-a(2)*b(1) ]
end subroutine

subroutine cross_product_3x3(a,b,c)
    real(dp), intent(in) :: a(3), b(3)
    real(dp), intent(out) :: c(3)

    c = [a(2)*b(3)-a(3)*b(2), a(3)*b(1)-a(1)*b(3), a(1)*b(2)-a(2)*b(1)]
end subroutine

等式で使用される

m = e .x. (r .x. g)  ! m = e×(r×g)

ここerおよびgは、数学的に意味のある任意の次元を持つことができます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.