査読付き論文でやらかした話【テストコードはとても大切】

はじめに

1年近くかけて書き続けていた査読付き論文を先々月末についに投稿しました。↓は関連の記事です。

この論文に関するレヴューはまだ帰ってきていないのですが、つい3日前、この論文に掲載しているグラフを作成するために用いた材料定数に誤りが見つかりました。さらに、この材料定数の誤りが論文の結果に非常に大きく影響していることも分かりました。

レフェリーには直接土下座して謝りたいほどに申し訳なく思っています。本当にすみません。

この件についてボスと話し、現在はこの論文の撤回または訂正論文の再投稿を考えています(恐らく撤回の可能性の方が高いと思います)。

この誤りが見つかるまでの経緯と見つかった後の経緯を書き留めておこうと思います。

ことの発端

先週の研究室でのゼミでのことでした。ゼミの終わり際にこんな会話をしていました(適当)。

~~~~~

ボス「来月に次の学会発表の概要の締め切りがあるから、ワイ君は来週までに概要に載せるメインの内容をまとめて何にするかまで決めておいてね。」

ワイ「おかのした。」

~~~~~~

惨劇の発覚

↑の後、1日半程度かけて直近2, 3か月分の成果を見返し、どの成果を発表のメインに置くか考えていました。さらに、追加で調べたいことがあったので過去に使っていたプログラムを確認していた時のことでした。

~~~~~~

ワイ「(この時に$B$がこうなるなら…こっちが正になるはずだから………)」パソコンカタカタ

ワイ「(そしたらこれが…..こうなってないと…………?)」カタカタカタカタ

ワイ「(……ん…?…おかしいな…こっちは負になるはずじゃ……)」カタカタカタカタ

ワイ「(そしたらこっちの$\Delta$値が正に……あれ…(汗)…そしたら……あれ…?????)…」カタカタカタカタ

ワイ「(そしたら材料定数が間違ってる……よな…………)」

ワイ「(ん…まてよ…..そしたら….こないだの論文に使った材料定数も間違ってるんじゃ……)」

ワイ「(待て待て、まだあわてるような時間じゃ…)」資料ペラペラ

ワイ「あ、あぁ…………オワッタ…」

~~~~~~~

結果として、本来どんなに大きくても1未満になってないとおかしいとある材料定数がプログラム側では5.07…という明らかに大きすぎる値になっていました

人生初の査読付き論文で、なおかつ一年近くかけて書いては書き直してを繰り返していた論文でこんな事が起こるなんて1ミリも想像していませんでした。

惨劇はなぜ起きてしまったのか

専門的な用語が出てくるので勉強してください飛ばしてもらっても大丈夫です。

もともと一年ちょっと前に作成したプログラムで二次元の直交異方性材料のポアソン比$\nu_{jk}$$(j, k=x, y, z, j\ne k)$(とある材料定数です)を文献から参照して使おうとしていました。

いくつかの書籍を読んで適当なポアソン比を利用しようとしたのですが、必要なポアソン比が全て記載されたものを見つけられませんでした。そこで材料の特性を考慮し、 \begin{equation} \nu_{xy}=\nu_{zx} \end{equation}

が成り立つと仮定しさらに、マクスウェルの相反定理から得られる、 \begin{equation} \nu_{jk}=\frac{E_j\cdot\nu_{kj}}{E_k} \end{equation}

を用いてプログラム側で必要なポアソン比を計算していました。が、既にここまでに間違っている点があることに気付けませんでした

今回考えている二次元の直交異方性材料は$x$軸方向にのみ極めて高い強度を持つ材料だったので、式(1)は間違いで、正しくは、 \begin{equation} \nu_{xy}=\nu_{xz} \end{equation}

でした。この間違いによって$\nu_{xz}>5$などという明らかにおかしい値が出てきていました。

二度と惨劇が生まれないためにどうしたのか

この惨劇が生まれる可能性を可能な限り下げるために次の三つを徹底することにしました。

  • 文献以外から用いる数値や条件が妥当なのか、プログラミング以前に第三者によるチェックを行う
  • プログラムにテストコードを追加する
  • テストコードをクリアした結果のみを論文に載せる

一点目は当たり前なのですが、人間は間違っているものを一度正しいと思うと何かのきっかけが無いとその間違いに気付き難いので、客観的に文章を第三者に確かめてもらう必要があると考えました。

この第三者は可能な限りその研究内容を客観的に見ることのできる人物が好ましいと考えいています。ただし、余りにも予備知識が無さ過ぎるとチェック自体に時間がかかりすぎてしまうのでそれは好ましくないでしょう。人選の難しいところだと思います。

続いて、二点目と三点目は一点目と同じかそれ以上に重要です。

私は普段、研究に使うプログラムを誰かに見てもらうことをしてきませんでした。研究室によってはプログラム自体のダブルチェック(いわゆるコードレビュー)のある所もあるのかもしれませんが、私の研究室ではそれが無いので、プログラムと得られるデータの正確さをプログラマー(私)自身で担保するしかありませんでした。

今までは明らかにおかしいデータやグラフでない限りはほぼどのような結果でもスルーされてきており(これはこれで問題ですが)、得られたデータの正確さを担保するものはほとんどありませんでした(そしてその結果が今回の惨劇)。

そして今回は特に、プログラム側で材料定数の値を確認し、ある値の範囲外であれば強制的にエラーを出す、といった処理さえ行っていれば気付けたミスでした。

今回の件を通じて、プログラマー自身が成果物であるプログラムと得られたデータの正確さを担保するためにテストコードも書くべきだと思うようになりました。切実に。

おまけ

追加したコード

一応、追加したテストコードの一部を記載しておきます。言語はOctaveです。MATLABとも互換性があると思います。

# main.m
E_xx = 148*10^9;
E_yy = 10.50*10^9; # axial + vertical young's modulus
E_zz = E_yy; # two dimensinal orthotropy's condition
n_xy = 0.30;
n_yz = 0.59;
n_xz = 0.30; # poisson's ratio
n_yx = (E_yy * n_xy)/E_xx; # two dimensinal orthotropy's condition
n_zy = E_zz * n_yz/E_yy;
n_zx = E_zz * n_xz/E_xx;

for array = [n_yx n_zy n_zx]
  test_poisson(array);
end
(以下略)
# test_poisson.m
function test_poisson(n)
  if (n >= 1)
    error('Invalid poisson raio is inputted(more than 1).');
  elseif ( n < 0)
    error('Suspicious poisson raio is inputted(less than 0).');
  end
end

材料定数(ここではポアソン比)が閾値を超えると強制的にエラーをだすようなプログラムです。

とはいってもコーディング作法(特にテスト)って学ばないじゃん…

私は大学の講義としてはプログラミングの初歩・応用みたいなものは取ってきましたが、プログラミング・コーディングの作法についてはほとんど学んできませんでした(あってもせいぜい「インデント揃えよう」とかその程度だったと思います)。

また、研究していても周りの学生・教員が得られたデータの精度をどう担保しているのかという話は全く聞いたことがありませんでした。もちろんテストコードの話もそう。

この辺りの話の詳細は↓の二つの記事が非常に参考になるので参考にしてください。私ももっと早く読んでおきたかった…(泣)

どのようにコーディングについて学習すべきなのか

私の所属する大学のプログラミングの講義や研究室のガイドラインにはコーディングの作法については全く書かれていませんでした。おそらくコーディングの作法までしっかり描かれている研究室は少ないと思います。

その中でテストコードの重要性を知るためには先ほどのリンクの記事を参考にしたり、

  • 実際のプロダクトを自分で作ってみる
  • プログラムを多用する o r 頻繁に書く研究室 / 研究者 / 学生との繋がりを広く持つ

というのは非常に有効な方法だと思っています。残念ながら前者についてはハードルが高めですが、実際に多くのユーザーに使ってもらうようなアプリケーション等を作った経験があれば、予期しないエラーなどを避けるためのテストの重要性を深く理解できると思います。

二点目も重要で、研究のためのプログラミングについて多くの知見が蓄積された研究室であればこうした知識は(少なくとも私の研究室よりはるかに)手に入りやすいはずです。

最後に

同じ轍を踏む方が増えないように祈っています。そして同じ轍を踏んでしまった方を励ましたいとも思っています。TwitterではみんなOne acceptedとかイキってるけど誰も論文を撤回した話とかしないじゃん…それはそれでおかしくね?

ついでにテストと和解せよ(適当)