Mathematica関連 Menu

Mathematicaの落とし穴

プログラミング中、実際に遭遇した落とし穴の事例。
Mathematicaの落とし穴の絵

CoefficientListによるリストの長さの判定

 任意の関数を純関数で指定する引数を持ち、その関数を Maclaurin 展開したときの係数と、その係数リストの長さを内部計算で必要とするようなコードを書いていたとする。ここでは簡単のため、0次~10次の係数 (つまり、11個の要素からなる長さ11のリスト) を仮定する。
 このとき、引数関数が余弦関数ならば、
  • CoefficientListを余弦関数に使用した場合

となり、意図した結果となる。しかし正弦関数ならば、
  • CoefficientListを正弦関数に使用した場合

となってしまう。もし、内部コードでリストの長さの判定条件にこれを使用していたら、エラーが発生する。よって Join[list, {0}] 等を用いて、リストの末尾に要素0を補う必要がある。
 CoefficientList は、元々多項式の係数を求める組み込み関数である。そこで、次のように SeriesCoefficient を使用するのが本来の方法であり、かつオールマイティーな方法であると思われる。
  • SeriesCoefficientを正弦関数に使用した場合

 なお、他の理由、例えば計算速度に関して CoefficientList と SeriesCoefficient のどちらを使うのが望ましいかは、個々の問題や条件によって異なってくると思われるので、実際には動作テストで確認した方がいい。

行列を指定するパターンマッチングでの混乱

 変数がリスト (ベクトル) のときは、パターンマッチングで次のように指定する。
  • リスト(ベクトル)のパターンマッチング

 一方、変数が行列のとき、パターンマッチングで次のように指定すると、うまくいかない。
 (パターンにマッチしないと判断されたため、入力した命令文がそのまま返されるだけとなる。)
  • (誤った)行列のパターンマッチング

 行列のときは、次のように指定すれば意図した動作をする。
 ( Mathematica の仕様によるものであり、全く理由はない。)
  • (正しい)行列のパターンマッチング

 これは特に、行列変数とベクトル変数が混在するようなコード内で混同しやすい

ループ型? or 関数型?

 冪級数の形の級数を While ループで記述しようとして、次の2通りのコードを書いた。
 (級数展開式の初項が1となるように定数倍した、重みkEisenstein 級数E k (τ)の例。)
  • 2通りのループ型プログラミング例

 後者は意図した結果になる。しかし、前者は級数冪級数になってしまう。

 前者のコードを用いて Klein の楕円モジュラー関数J(τ)を求めようとしたが、誤った結果になっている。
  • 楕円モジュラー関数の(誤った)プログラミング結果

 後者のコードを用いれば、正しいJ(τ)のグラフになる。
  • 楕円モジュラー関数の(正しい)プログラミング結果

 勿論、局所変数q1は必須ではなく、もっと簡単な次のコードでも正しい結果になる。
  • 簡略化したループ型プログラミング例

 しかしこのコードは、前の例よりも計算が遅いのである!。
  • 楕円モジュラー関数の(正しいが遅い)プログラミング結果

 高速化の定石は、ループ型ではなく、関数型プログラミングを用いることである。しかし、後者にも思わぬ落とし穴がある。以下、その一例を示す。
 前にループ型でプログラミングしたコードes2が、虚部の大きさに応じて反復回数をどのように決めているかを確認する。
  • ループの反復回数を分析するコード

 実部が異なるいくつかの虚軸方向で描画した、反復回数のグラフ。概ね一次分数関数に近いように見える。
  • ループの反復回数のグラフ

 そこで、この曲線に概ね適合する具体的な一次分数関数を求める。
  • 反復回数曲線の適合関数を求める

 先の反復回数のグラフと重ねて描画してみると、よくフィットしていることが分かる。
  • 反復回数曲線の適合関数のグラフ

 この結果を用いて、関数型プログラミングを作成する。
  • 関数型プログラミング例1

 これまでで最も高速である。
  • 関数型プログラミング例1の結果

 もし、上記のような加算回数の見積を行わず、単に固定回数で関数型プログラミングを作成すると、どのような結果になるか確認する。
  • 関数型プログラミング例2

 実軸の近傍では加算回数不足のため不正確になっている。逆に虚部が大きい領域では回数が冗長なため余分に時間がかかり、全体として遅くなっている。関数型プログラミングは記述しやすく高速だが、被プログラミング関数の特性をよく踏まえないと、この事例のように本来の利点が発揮できない場合もある (つまり万能ではない) 。
  • 関数型プログラミング例2の結果


*******


  • Mathematicaでの誤った入力の例
何となく悪い予感が…

  • Mathematicaでの誤った入力の例2
なぜ意図した結果にならないの? orz

Mathematica関連 Menu