2. "Implementing QuantLib"の和訳

Chapter VIII   The Finite-Difference Framework (有限差分法のフレームワーク)

8.1   古いフレームワーク

8.1.2 Evolution schemes: 時間軸方向の差分スキーム 

偏微分方程式 \(\frac{\partial f}{\partial t }= L ・ f\) において、前セクションで説明した差分演算子は、右辺の微分演算子の差分化スキームを提供しています。それに対し、Evolution scheme(時間軸方向の差分スキーム) は左辺の時間軸での偏微分を差分化したものです。 

ご存知の通り、金融工学の世界で使われている有限差分法では、時間軸を進む方向は、関数値 \(f(T)\) が既知である“満期日”(すなわち満期時における Pay-off関数) からスタートし、時間を現在まで遡って行きます。その間、各Timeステップにおいて順番に、\(f(t)\ を\ f(t+\Delta t)\) から導出します。 

上記の偏微分方程式の差分近似を計算する、最も簡単な方法は、下記の式で表されます。 

\[ \frac{f(t+\Delta t)-f (t)}{\Delta t}=L ∙ f(t+\Delta t)         \]

この式をさらに単純にすると 

\[ f(t)=f(t+∆t)-\Delta t ∙ L ∙ f(t+\Delta t) ~~~または~~~ f(t)=(I-\Delta t ∙ L) ∙ f(t+\Delta t) \]

となります。(訳注:式中の \(  f \) は配列、IL は行列)。 右辺は単純な行列と配列の積であり、行列を、関数値配列に作用させる動作をプログラムコードに直すと次のようになります (訳注: f が a で表現されている点に注意)。 

			a = (I - dt*L).applyTo(a);

(当然ながら、実際のプログラムコードでは、(I-dt*L) の部分は事前に計算され時間ステップごとに保持されています。) 

 

これよりやや複雑なEvolutionスキームは、下記の式で表されます。 

\[ \frac{f(t+\Delta t) -f (t)}{\Delta t}=L∙f(t) \]

この式をさらに変換すると、 

\[ (I+\Delta t∙L)∙f(t)=f(t+\Delta t) \]

となります。この式から \(f(t)\) を求めるには、連立1次方程式を解く必要があるので(あるいは演算子行列の逆行列を計算するので)、より複雑な計算ステップになります。しかし、我々の設計した演算子インターフェース(訳注:solveFor( )メソッドのこと)を使えば、以下のように、applyTo( )と同じくらい簡単なプログラムコードで済みます。 

      a = (I + dt*L).solveFor(a);

ここで三重対角演算子を使うメリットが発揮されます。 

まず \(f(t)\) を求める2つの方法の違いについて、ごく簡単に説明させて下さい。端的に言えば、前者のスキームは(explicit schemes(陽的解法)と呼ばれています)、計算はより簡単ですが解が不安定になる可能性があります。一方、後者のスキームは(implicit schemes(陰的解法)と呼ばれています)、より安定した解が導出でき、また離散時間の間隔も大きく取れます(訳注:陽的スキームでは解の安定性を確保する為に、時間間隔と状態変数の間隔の取り方に一定の制約が発生する。一方、陰的スキームではそういった制約が小さい)。仮に(行列の形を取る)一般的な Operator(差分演算子)であれば、implicit step(陰的解法のための時間間隔の取り方)を使う計算時間は、グリッドのサイズNに対して、\(Ο(N^3)\) のオーダーになり、explicit step(陽的解法のための時間間隔の取り方)の \(Ο(N^2)\) に対して、実用性で劣ります。しかし、三重対角演算子は、どちらの方法でも(ステップ数に対する計算時間は) \(O(N)\) のオーダーとなり、追加の計算時間の心配をせずに、explicit stepの利点を生かす事ができます。 

QuantLibライブラリーでは、上記の2つの解法 (それぞれexplicit Euler scheme(陽的オイラー法)、implicit Euler scheme(陰的オイラー法)と呼ばれています)を用意しています。実際の所、この2つの解法は、より汎用的な MixedSchemeテンプレートクラスから派生する2つの特殊な方法として実装されています。MixedSchemeクラステンプレートのコードを下記 Listing 8.2に示しますが、これは次の Evolutionスキーム式をオブジェクトモデル化したものです。 

\[ \frac{f(t+\Delta t)-f(t)}{\Delta t}=L∙ \left[(1-\theta )∙f(t+\Delta t)+\theta ∙f(t)\right] \]

この等式は、implicit と explicit step(陰的と陽的ステップ)がミックスされています。この等式かわわかる通り、\(\theta =0\) とすると陽的オイラー法となり、\(\theta =1\) とすると陰的オイラー法となります。θは 0 と 1の 間のどのような値を取る事も可能で、仮に 0.5 とすると Crank-Nicolson(クランク・ニコルソン法)になります。 

Listing 8.2:MixedSchemeクラステンプレートと、その派生クラスの概要 

    template <class Operator>
    class MixedScheme  {
      public:
        typedef OperatorTraits<Operator> traits;
        typedef typename traits::operator_type operator_type;
        typedef typename traits::array_type array_type;
        typedef typename traits::bc_set bc_set;
        typedef typename traits::condition_type condition_type;

        MixedScheme(const operator_type& L,
                    Real theta,
                    const bc_set& bcs)
        : L_(L), I_(operator_type::identity(L.size())),
          dt_(0.0), theta_(theta) , bcs_(bcs) {}

        void setStep(Time dt) {
            dt_ = dt;
            if (theta_!=1.0) // there is an explicit part
                explicitPart_ = I_-((1.0-theta_) * dt_)*L_;
            if (theta_!=0.0) // there is an implicit part
                implicitPart_ = I_+(theta_ * dt_)*L_;
        }
        void step(array_type& a, Time t) {
            if (theta_!=1.0) {
                a = explicitPart_.applyTo(a);
            if (theta_!=0.0) {
                implicitPart_.solveFor(a, a);
        }
      protected:
        operator_type L_, I_, explicitPart_, implicitPart_;
        Time dt_;
        Real theta_;
        bc_set bcs_;
    };

    template <class Operator>
    class ExplicitEuler : public MixedScheme<Operator> {
      public:
        // typedefs, not shown
        ExplicitEuler(const operator_type& L,
                      const bc_set> >& bcs)
        : MixedScheme<Operator>(L, 0.0, bcs) {}
    };

既に述べた通り、Evolution Schemeテンプレートクラスは、何等かの Differential Operator型を、テンプレート引数として取ります。この MixedSchemeクラスでも同様で、Differential Operatorクラスの中から、OperatorTraitsテンプレートを使って、必要な Operatorを選択します(これについては後で説明します)。 

MixedSchemeクラスのコンストラクターは、引数として、①差分演算子Lと、②θの値(訳注: \(0 \leq \theta \leq 1\)で、implicit とexplicitスキームに対するウェイトとして使われる)、及び③境界条件(これについても後で説明します)を取り、それぞれメンバー変数に保存します。setStep( )メソッドは、離散時間の間隔となる Δt を保存し、差分演算子となる 

\[ I-(1-\theta )・\Delta t・L ~~~  および ~~~  I +\theta ・\Delta t・L \]

を事前に計算しておきます。その演算子を使って、applyTo( ) と solveFor( )メソッドが呼び出され事になります。θの値が 0 または 1 で設定されている場合は、いずれか一方の事前計算は、不要なので行われません。この事前計算を、コンストラクターとは別の setStep( )メソッドで行うことにより、仮にシミュレーションの途中で離散時間の間隔を変える必要がある場合でも、このメソッドを使えば同じ Evolution Schemeのインスタンスが再利用できます。 

最後に、step( )メソッドは、関数値の配列を求めるアルゴリズムを、時間軸に沿って遡及する動作を行います。上記 Listing中の実装内容は、基本的な概要を示しているだけで、次のセクションで、さらに詳しく説明します。ここでは、(θの値に従って)陽的解法か陰的解法かのいずれかを実行するようになっています (ここでも θ が 0 か 1 の場合は、不必要な計算を避けるようにしています)。 

QuantLibライブラリーの中で提供されている、いくつかの Evolution Schemeは (このListingにあるExplicitEuler法など)、すべて MixedSchemeクラスの特殊な形態です。 (注:ここでは「古いフレームワーク」について説明しています。新しいフレームワークでは別の方法を提供しています。)  

Evolution Schemeについても(Differential Operatorと同様)ベースクラスは存在しません。有限差分フレームワークの中の別のオブジェクトが、Evolution Schemeをテンプレート引数として取り込み、それが提供する setStep( )メソッドや、step( )メソッドを使うようになっています。 

 

 

<ライセンス表示>

QuantLibのソースコードを使う場合は、ライセンス表示とDisclaimerの表示が義務付けられているので、添付します。   ライセンス

目次

Page Top に戻る

// // //