数値計算におけるデバッグ作業

数値実験をする上では、さまざまなレベルでの誤解、勘違い、間違いが起こり得ます。そのため、実験する際に、先入観や思い込みをなくし、色々な可能性つ常に考えながら実験を行う必要があります。

しかし、一方で、実験結果で得られる数値が、常識的な意味で明らかにおかしい・数値がお互いに矛盾している場合には冷静にその結果が生まれる原因を推察する必要があります。

デバッグの基本: 

数値計算実験の流れ

  1. アルゴリズムの構築
  2. プログラムでの実装
  3. 実験
  4. 検証

間違いが発生するそれぞれのレベル
以下の分類は、説明上の便宜的な分類です。実際のアルゴリズム・プログラムの不備がおこるレベル・個所は、厳密な切り分けを行うことはできないような複合的な原因で起こっているはずです。それぞれのレベルでプログラムの正当性を一づつチェックしない限り基本的には、それが正常に動くことは保証されません。

「ソースコードが見たところ間違っていないようだ」「実行結果についても正常そうだ」

という理由で、プログラムのチェックをさぼることのないようにしてください。

  1. アルゴリズムの構築 
    1. アルゴリズムの理論的な不備、誤り 
      近似ヘッセ行列の更新式の理論的誤り:正定値の保証されない更新式
    2. アルゴリズム解釈上の誤り
      近似ヘッセ行列の更新式を数学的な意味でとりちがえる
      
  2. プログラム実装時 
    1. プログラムソースの誤り プログラムが、実行できないまたは異常終了する
      1. 文法上の誤り: コンパイラー検出可能
        コンパイル時に各種 errorがでる。

        $ gcc -Wall test.c
        test.c:27: parse error before `f'
        test.c:27: warning: type defaults to `int' in declaration of `f'
        test.c:28: parse error before `x'
        test.c:29: warning: type defaults to `int' in declaration of `Cmax'
        test.c:63: parse error before `x'
        test.c: In function `main':
        test.c:136: `flag' undeclared (first use in this function)
        test.c:136: (Each undeclared identifier is reported only once
        test.c:136: for each function it appears in.)

        原因: 宣言文、代入式、ポインタの扱いの誤りなど...

      2. プログラムとしての誤り:コンパイラー検出不能
        実行時のエラー: Segmentation fault 無限ループに入ってプログラムが終了しない..など
              原因: malloc関数ので領域確保ミス、free関数での領域解法ミス、配列を参照する際に、配列のサイズ           をこえた領域にアクセス、ループ文の終了条件の設定ミス

      3. 数値計算上の特性を考慮しない誤り
        プログラムは終了するが、Inf(無限大)、NaN(非実数)など意味のない結果が出力される。
        • 浮動小数点演算するとき、overflow, underflow, ゼロ割りなどの状況を避ける方法を使用していない。
        • double-int 型など、実数-整数間の変換(cast)を適切に行っていない。答えが実数手欲しいのにもかかわらすint 型・整数どうしの割り算を行っている。
        • 終了条件等のif文の条件が不正

    2. アルゴリズム表記(数式)からプログラム言語表現への変換ミス  
        プログラムは、(一見)正常に終了するが期待する解とは異なる。実行時間がやたらと長い。 
               
      1. アルゴリズムとは違った形での実装
        • 添え字 i について i = 1, ..., N まで、和をとる必要があるにもかかわらず途中までしか和を計算していない。
        • 使用前に、0で初期化すべき変数を初期化していない。
        • 配列への代入の際に、間違った場所に代入している。
        • 使用する必要のない大きな配列をいくつも宣言している。一時的にか使用しない配列を多量に宣言。         
      2. 0*0, 0+0 などの完全に無駄な演算を多量に行っている。一度計算して求まっている値を各反復で繰り返し行っている。
      3. その他、非効率・無意味なルーティンを度々行っている。

  3. 数値実験時の誤り
    1. パラメータ設定の不備 
      解くべき問題、設定パラメータの読み込み・設定ミス。初期解の条件、制約充足ミス
    2. プログラムの挙動、結果の解釈の誤り
      プログラムの実行状況の取り違え・記憶違い。shell での走らせ方の間違い。
    3. 実行マシンの状況
      比較するプログラムを実行させるマシンスペックを揃えていない(CPU, memory 等)
      プログラムの実行時間を適切に計っていない。    

  4. 結果の検証