単体テストが科学アプリケーションで機能しない理由

この記事では、科学アプリケーションの開発における私の経験を共有し、少なくともソフトウェアエラーを見つけるという観点から、最近では一般的に信じられているように、テスト駆動開発と単体テストが万能薬ではない理由を説明したいと思います。 なぜそうですか?



この記事はいくつかのセクションで構成されます。

  1. 「はじめに」では、なぜそれを書くことにしたのかを説明します
  2. 「図解アルゴリズム。」 科学的な問題とアルゴリズムをここで簡単に説明します。
  3. 「単体テストの短所」ここでは、科学アプリケーション向けの単体テストの短所を説明する議論を行います。
  4. おわりに


すぐに、TDDの利点をアーキテクチャを開発する方法としてではなく、プログラムのテストの観点からのみ考えることに注意してください。



はじめに



ハブでの航空の1週間の主要な記事はDigital Aircraft [1]であり、それに関する興味深い解説[2]は、20年前には単体テストも通常の開発方法もなかったため、トリプルトリプルスキームが一般的でした特に、ソフトウェアエラーを排除するために、異なる言語で3つの異なるコマンドを使用して同じプログラムを作成します。



それにもかかわらず、目撃者によると([3]ちなみに、これは優秀な科学者リチャードファインマンによるもう1つの優れた本であり、ハブで2番目の人生を歩んだ)、テスト駆動開発(TDDさらに)ではない場合、手動テストは開発の非常に重要な段階でした。

そのため、私は科学的応用の開発へのアプローチを分析し、このテーマに関する私の考えを共有したいのです。特に、最初に考えたように、TDDはエラーを見つけるための特効薬ではないということです。



イラストアルゴリズム



過去20年間に、流体力学のモデル化のための珍しい方法、Lattice-Boltzmann法(以降-LBM)[4]、[5]が開発されました。 ドイツのMax Planck InstituteのこのアルゴリズムのC ++での実装に参加しました。



やむを得ない質問を避けるために、はい、この方法を使用した商用パッケージおよびオープンパッケージが既に存在します[6]。 しかし、当初は、メソッドの変更を使用することを計画していました。これにより、パフォーマンスとメモリ消費量が増加するはずです。 それにもかかわらず、私たちは一貫してそれを拒否しました(最初に物質移動、次に熱移動)。 その時までに、多くのコードが記述されていたため、独自の実装を継続することにしました。



流体力学は、知られているように、ナビエ・ストークス方程式によって記述されます。 モデル化する場合、たとえば、有限要素法[7]を使用できます。 残念ながら、並列化の相対的な複雑さ、多相流をシミュレートできないこと、多孔質媒体内の流れのモデリングの難しさなど、欠点があります。 これらの欠点にはLBMがありません。



希薄気体の場合、空間の各点での粒子速度分布密度が時間とともにどのように変化するかを説明するボルツマン方程式[8]が有効です。 巨視的には、この方程式はナビエ・ストークス方程式と等価です(つまり、巨視的な量への移行後-密度と速度)。



ボルツマン方程式は高密度の液体には適用できないという事実にもかかわらず、それをモデル化することを学べば、これらの液体のナビエ・ストークス方程式もモデル化できます。 つまり、それによって抽象化の基礎レベル(高密度液体の微視的方程式)を物理的に正しくない実装(希薄気体の微視的方程式)に置き換えますが、上位レベル(巨視的ナビエ・ストークス方程式)は正しく記述されます。



次の段階は、ボルツマン方程式の離散化です。時間、空間座標(モデリング用の空間ノードを取得)、および各空間ノードの粒子の可能な方向です。 方向は特別な方法で選択され、常にいくつかの隣接ノードを指します。



したがって、これらはさらなる例にとって重要な機能です:1.複雑な式がアルゴリズムで使用されます2.アルゴリズムは非ローカルです。つまり、特定のノードのシステムの状態を計算するには、前のステップからの近隣ノードに関する情報を使用する必要があります。



単体テストの欠点



最後に、単体テストが複雑な科学アプリケーションを完全にカバーできない理由に移りましょう。

このセクションの主なアイデアは、複雑なアルゴリズムがテストを妨害するということです...あなた自身。



簡単なテストの欠如


複雑なアルゴリズムと方法の場合、簡単なテストを考え出すのは困難です。



これは、一般的に明らかです。 単純で明確な数値が出力で得られるように、式の単純な入力パラメーターを選択することはしばしば困難です。 つまり、もちろん、紙の上またはMatlabで個別に、テストするメソッドの出力でパラメータを取得するために、入力する必要があるパラメータを計算できます。 ただし、入力パラメーターと出力単位はどちらもマジックナンバーに似ています。 例はMaxwell分布です[9]。 LBMアルゴリズムでは、わずかに変更された形式で使用されます。



狭いカバレッジエリア


複雑な数式やアルゴリズムの簡単なテストを思いついたとしても、このテストではほとんどのエラーは検出されません。



実際、シンプルで読みやすいテストを思いつくことがあります:たとえば、3Dエンジンで回転行列をどのように記述したかをテストすると(多くのHabr住民が手を出したと思います)、すべての軸に沿ってゼロの角度で回転すると、単一の行列が返されます。 ただし、多くの場合、これらのテストはほとんどのエラーを検出しません。 たとえば、回転角のサインの代わりに(回転行列の要素で)接線を使用すると、このような簡単なテストに合格します。



別の例:LBMアルゴリズムの境界条件を実装する必要があります。 これらの条件は、ノードが「液体」近傍に完全に囲まれていない場合(たとえば、壁の近くにある場合)にノードの状態を補完する必要があります。 簡単なテストを考え出すことができます-すべての隣接ノードが平衡状態にある場合(つまり、速度がMaxwellの法則に従って分布している場合)、反復後に境界ノードも平衡状態になります。 しかし、このテストではほとんどのエラーを見逃していることが判明しました...



議論することができます-ビジネスアプリケーションでは、テストはすべての可能な入力パラメータをカバーするために呼び出されません。 ただし、科学的アプリケーションの違いは、通常、異なるパワーのセットで動作することです[10]。 ビジネスアプリケーションは主に、有限または可算セット(有限の累乗またはAlefゼロの累乗:ブール変数、文字列、整数)、および数えられないセット(実数または複素数)の科学的なもので動作します。



低エラー率


典型的なセットは数えられないという事実により、エラーをすぐに検出することは困難です。



事実は、ブール変数のみを使用するメソッドで「or」と「and」を混同すると、その出力ですぐに何か問題が発生するということです。 また、小さい入力パラメーターで機能する式でプラスとマイナスを混同すると、この式で1つのメソッドを呼び出した後にエラーを検出できない場合があります。 不一致を検出するには、このメソッドを数十万回呼び出す必要があります。 また、実数で機能するテストでは、誤検知を回避する方法について考える必要があることを覚えておく価値があります。



例:LBMアルゴリズムの重要なサイクルの1つで、continueはbreakと混同されました。 ただし、すべてのテストに合格しました。 単純なシステムでアルゴリズムを手動でチェックした後にのみエラーが発見されました-ポアズイユの流れ[11]、100,000回の反復後、または5分(!) -10)。



モジュール性の欠如


多くのメソッドでは、単体テストを作成できません。



複雑な物理モデリングアルゴリズムがローカルになることはほとんどありません。 つまり、各空間モデリングノードは隣接ノードと対話できますが、これを避けることはできず、ユニットテストは不可能になります。



たとえば、LBMアルゴリズムの典型的なステップまたは2次元空間での典型的な境界条件をテストするには、少なくとも3x3ノードのシステムが必要です。 密度、速度、温度を指定し、予備計算を行い、モデリングコンテキスト(物理プロパティやモデルのさまざまなプロバイダーなど)を設定する必要があります。その結果、テストのモジュール性は統合のヘイズに徐々に隠されています。



別の(それほど特徴的ではない)例:ゲームの物理エンジンで衝突アルゴリズムをテストする必要がある場合、衝突する少なくとも2つのボディ、それらの座標、速度、ジオメトリなどを指定する必要があります。これにより、ユニットテストが再び統合テストに変わります。



大きなファサード


アルゴリズムが自明でない場合、このアルゴリズムを実装するクラスの1つのパブリックメソッドは、10〜15のメソッドと300〜400行の複雑なコードを隠すことができます[12]。



そうであっても:300-400行の厳しい、容赦のない、解析できないコード。 このクラスには、タイプa、b、ガンマ、LAPACKのメソッド呼び出し[13]、BLASライブラリ、乱数生成、および他に何を知っているかという5つの変数があります。 そして、企業アプリケーションを開発する際に長い間そのような名前をscられることを知っているので、変数「a」に何とか名前を付けたいと思うかもしれませんが、元の記事ではそのように呼ばれているため、明示的な意味はありません。 最善の方法は、クラスの最初に記事へのリンクを提供することです。



また、この単一のパブリックメソッドの単純な入力パラメーターと単純な出力値を使用して単純な単体テストを考え出し、ある瞬間にパスが停止した場合でも、無私のデバッグや最終的な方法でもこのクラスでエラーを見つけることはできません注意深い読書。



このような状況に遭遇した後、最良の解決策は、MATLABでこのアルゴリズムを書き換え、計算を行ごとに比較することでした。



おわりに



かなり良い議論をし、異なるチームが異なるプログラムを異なる言語で書くことは、ユニットテスト、TDD、インターフェイス駆動型開発、アジャイルにとって必然的な追加であることが多いことをHabrの読者に納得させることを願っていますその他の最新のテクニック。



読んでくれてありがとう!



参照資料



[1] デジタル飛行機

[2] デジタル航空機に関する解説

[3] 他の人があなたのことをどう思うか、あなたどう思いますか?

[4] 格子ボルツマン法

[5] LBMに関する書籍 。 いくつかはグーグルブックを通して入手可能です。

[6] 並列格子ボルツマンソルバー

[7] 有限要素法

[8] ボルツマン方程式

[9] Maxwell-Boltzmann分布

[10] カーディナリティ

[11] ポアズイユストリーム

[12] パターンファサード

[13] LAPACK



PSサイトのほとんどの読者にとって、アルゴリズムはややエキゾチックであると思いますが、これは最良の例だと思います。 最終的には、それらについての記事に科学的なアルゴリズムがほとんどないところです。



PPS私は科学論文へのリンクを提供しません。なぜならそれらはすべて購読によってのみ利用可能だからです。



PPS招待してくれたSychevIgorに感謝します!



UPD



コメントを読んだ後、マジックナンバーの恐怖を取り除き、すべてのモジュール(BoltzmannDistributionProviderなど)の表形式のテストを記述して、Matlabaの計算またはアプリケーションの動作状態の計算と比較する必要があると思います。 また、同様のシステムテストを追加します(たとえば、5x5ノードシステムのダイナミクスを1回計算し、ファイルに書き込み、毎回実行の結果をこのファイルと比較します)。 アドバイスありがとうございます!



All Articles