数値計算におけるデバッグ作業
数値実験をする上では、さまざまなレベルでの誤解、勘違い、間違いが起こり得ます。そのため、実験する際に、先入観や思い込みをなくし、色々な可能性つ常に考えながら実験を行う必要があります。
しかし、一方で、実験結果で得られる数値が、常識的な意味で明らかにおかしい・数値がお互いに矛盾している場合には冷静にその結果が生まれる原因を推察する必要があります。
デバッグの基本:
- 疑わしい個所を絞っていく:
- 因果関係をよく考えて、疑わしいところを狭めていく。
疑わしい関数を別のテストプログラム等で呼び出して、正常に動くかどうかのチェックをする。
- ある程度、間違いが含まれると予測できる部分を重点的に行うのはいいが、チェックもせずに他の個所が正しいことを仮定しないこと。たいていのバグはとても些細なものです。
- 誤り個所が一箇所とは限らない:
一つ見つけたとしても、今修正しようとしているバグには関係しないかもしれない。
他にもまだ、バグが潜んでいるかもしれない。
- バグと誤った結果の因果関係を検証する:
- バグをとった(と思えた)ときに、どのようなことが間違っていたのか確認し、それを原因と考えたときに先ほどまで正常に動いていなかったプログラムの挙動・間違った結果を説明できるのかどうかチェックする
因果関係がはっきりしなければ:
- 仮に今の条件で正常な結果が出たとしても、他の状況下では保証されない。
- 複数の誤り個所によってたまたま正常な結果がでているだけにかもしれない。
数値計算実験の流れ
- アルゴリズムの構築
- プログラムでの実装
- 実験
- 検証
間違いが発生するそれぞれのレベル
以下の分類は、説明上の便宜的な分類です。実際のアルゴリズム・プログラムの不備がおこるレベル・個所は、厳密な切り分けを行うことはできないような複合的な原因で起こっているはずです。それぞれのレベルでプログラムの正当性を一づつチェックしない限り基本的には、それが正常に動くことは保証されません。
「ソースコードが見たところ間違っていないようだ」「実行結果についても正常そうだ」
という理由で、プログラムのチェックをさぼることのないようにしてください。
- アルゴリズムの構築
- アルゴリズムの理論的な不備、誤り
近似ヘッセ行列の更新式の理論的誤り:正定値の保証されない更新式
- アルゴリズム解釈上の誤り
近似ヘッセ行列の更新式を数学的な意味でとりちがえる
- プログラム実装時
- プログラムソースの誤り プログラムが、実行できないまたは異常終了する
- 文法上の誤り: コンパイラー検出可能
コンパイル時に各種 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.) |
原因: 宣言文、代入式、ポインタの扱いの誤りなど...
- プログラムとしての誤り:コンパイラー検出不能
実行時のエラー: Segmentation fault
無限ループに入ってプログラムが終了しない..など
原因: malloc関数ので領域確保ミス、free関数での領域解法ミス、配列を参照する際に、配列のサイズ をこえた領域にアクセス、ループ文の終了条件の設定ミス
- 数値計算上の特性を考慮しない誤り
プログラムは終了するが、Inf(無限大)、NaN(非実数)など意味のない結果が出力される。
- 浮動小数点演算するとき、overflow,
underflow, ゼロ割りなどの状況を避ける方法を使用していない。
- double-int
型など、実数-整数間の変換(cast)を適切に行っていない。答えが実数手欲しいのにもかかわらすint
型・整数どうしの割り算を行っている。
- 終了条件等のif文の条件が不正
- アルゴリズム表記(数式)からプログラム言語表現への変換ミス
プログラムは、(一見)正常に終了するが期待する解とは異なる。実行時間がやたらと長い。
- アルゴリズムとは違った形での実装
- 添え字 i について i = 1, ..., N
まで、和をとる必要があるにもかかわらず途中までしか和を計算していない。
- 使用前に、0で初期化すべき変数を初期化していない。
- 配列への代入の際に、間違った場所に代入している。
- 使用する必要のない大きな配列をいくつも宣言している。一時的にか使用しない配列を多量に宣言。
- 0*0, 0+0 などの完全に無駄な演算を多量に行っている。一度計算して求まっている値を各反復で繰り返し行っている。
- その他、非効率・無意味なルーティンを度々行っている。
- 数値実験時の誤り
- パラメータ設定の不備
解くべき問題、設定パラメータの読み込み・設定ミス。初期解の条件、制約充足ミス。
- プログラムの挙動、結果の解釈の誤り
プログラムの実行状況の取り違え・記憶違い。shell
での走らせ方の間違い。
- 実行マシンの状況
比較するプログラムを実行させるマシンスペックを揃えていない(CPU,
memory 等)
プログラムの実行時間を適切に計っていない。
- 結果の検証
- 実験結果が常識的な値をこえた値を出している ほぼ同様な計算を行っているプログラム同士で実行時間、反復回数が大幅に違う。同一プログラムにおいてパラメータ等をわずかに変えるだけで、全く違う答えが求まる。