%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\newpage
\chapter{モデル検査システム}
\label{sec:model-check}
%\parttoc
%
\section{モデル検査システムの機能}
\label{sec:model-check-func}

モデル検査システムの目的は，仕様化部品の組み合わせで構成されたシステム
が，デッドロック不在やデータ整合性といった安全性を満たすことを
網羅的に検査することである．
詳細化仕様検査システムで触れた双対帰納(co-indution)も一種の安全性とみ
なすことが可能であるので，モデル検査システムは振舞等式の詳細化検証にも
用いられる．

モデル検査の基本機能は, 
\begin{itemize}
\item システムの初期状態(始状態)と検査すべき性質が
  与えられ, 
\item 始状態から遷移可能な全ての状態について, 
  与えられた性質が満足されるかどうかを検査する
\end{itemize}
ことである. 
この場合の結果としては次の3通りが考えられる：
\begin{description}
\item[正常に終了]
  この場合，システムは与えれた性質を満たすことが保証される．

\item[反例を発見して終了]
  この場合，システムの実行系列の中に与えられた性質が成立しないような反例
  が存在することが証明される．モデル検査システムは，その始状態からの実行
  系列と反駁証明から, 反例として返す．

\item[結果不定のまま終了]
  前述の詳細化検証と同じく，モデル検査は一般に決定不能であるので，事前に
  定められた計算資源の使用量を超過した場合には，モデル検査システムは結果
  不定のまま終了する．この場合でも，探索が終了した実行系列についての情報
  は提示する．

\end{description}

\subsection{モデル検査の実行方式}

振舞仕様におけるモデル検査については，システムの性質の記述及び検査ア
ルゴリズムに関してμ計算を参考としている．μ計算系における不動点演算子
を用いることで安全性や活性以外の性質も柔軟に記述できる上に，モデル検査
アルゴリズムも不動点の繰り返し計算として定式化されため，
反駁エンジンを用いた述語計算による実装が可能となっている．

詳細を安全性のモデル検査を例にとって説明する．証明したい安全性を示す
述語をP(X:h) (X は隠蔽ソート h の変数)とすると，安全性 P のモデル検査
(後向きの)手続きは次のような述語に関する漸化式 $p_n$ で与えられる．
ここで，隠蔽ソート上の述語が対応するシステムの状態集合の記号表現を与え
ている点に注意されたい．

\begin{itemize}
\item $\mathtt{p_0(X)=\neg P(X)}$
\item $\mathtt{p_{n+1}(X)=p_n(X)\vee pre(p_n(X))}$
\item $\mathtt{\neg p_n(init)}$ かどうかを各ステップごとに調べる
\end{itemize}

ただし init はシステムの始状態を表す(隠蔽ソート h の)定数記号であり，
状態 P(X) の前状態 pre(P(X)) は，全てのメソッド m について
$\mathtt{(\forall Y) P(m(Y,X))}$ の選言(disjunction) をとった述語(論理
式)として定義される．ここで Y は m のアリティに適切なソートを持つ限量
変数の集合である．直観的には，$p_n(X)$ は，そこから n ステップで安全性 
P(X) を破ることができるような状態の集合を表している．

上述の漸化式は収束することが保証されているいるものの，有限ステップで収
束する(ある n について $p_{n+1} = p_{n}$)とは限らない．したがって，アル
ゴリズム化する場合には，１)有限で収束，２) $p_n(init)$ となって反例発見，
３)どちらも不明，の３通りの結果が得られ，これは上述のモデル検査システ
ムの実行結果に対応する．

前向きのモデル検査手続きについても，状態 P(X) の次状態 post(P(X))を定
義すれば同様の漸化式として定式化できる．このように，始状態(init)，状態
集合(P(X))，次状態(post(P(X)))，前状態(pre(P(X)))が定義されていれば，
μ計算におけるモデル検査手続きを 反駁エンジン を用いた述語計算として行うこ
とができる．いわば，この点が本モデル検査システムの核心である．

\subsection{双対機能法の実行方式}
\label{sec:how-to-co-induction}

仕様検証システムにおける振舞等式の詳細化検証に必要な双対帰納法を行うに
は，状態の対(即ち関係)を状態とみなして安全性モデル検査を実行すれば良い．

例えば，振舞等式$s\sim t$を証明するには状態の対 (s,t) を始状態として
属性同値関係$\mathtt{{=}{*}{=}}$(全ての属性の値が同じ)が安全性を持つこ
とを示せば良い．これは以下の関係に関する漸化式として定式化でき，前述の
安全性モデル検査と同様に反駁エンジン を用いて処理することができる．

\begin{itemize}
\item $\mathtt{r_0(X,Y)=\neg(X{=}{*}{=}Y)}$
\item $\mathtt{r_{n+1}(X,Y)=r_n(X,Y)\vee pre(r_n(X,Y))}$
\item $\mathtt{\neg r_n(s,t)}$　かどうか各ステップごとに調べる
\end{itemize}

\section{モデル検査での新規コマンド}
\label{sec:model-check-new-command}

モデル検査では CafeOBJ に従来からある \texttt{check} コマンドを拡張した
次に示すコマンドを用いる:

\begin{vvtm}
\begin{simplev}
 check { safety | invariance } <述語名> [ of <文脈項> ] from <初期状態>
\end{simplev}
\end{vvtm}

ここで，\texttt{<述語名>} は，ある隠蔽ソート $H$ に関する述語
$P(X:H)$ の名前である．
$<$初期状態$>$ は，$H$ の初期状態を表現する定数オペレータの名前である．
オプションの \texttt{of <文脈項>} があった場合, 検査は\texttt{<文脈項>} で
表現されるシステム状態に対して実施される. そうで無ければ, システムが生成
するソート $H$ の変数を文脈として検査を実施する.

このコマンドの動作は以下の通りである.
% まず\texttt{<初期状態>} として, 項 $I$ が指定されているものとする.
検査は大きく2段階に分けて行われる.
第一段階は初期文脈に関して $P$ が成り立つかどうかの検査であり,
第二段階は各メソッドを適用した文脈で $P$ が成立するかどうかを
検査するループである.

\paragraph{初期文脈に関する検査}
まず初期文脈に関する検査は以下のように行われる.
\begin{enumerate}
\item 初期検査対象項 $t_1$ を次のようにして設定する：
  \begin{itemize}
  \item check コマンドで \texttt{<文脈項>} $c$ が指定されているならば
    $t_1 = c$ とする
  \item さもなければ, これを\texttt{<初期状態>}として指定されて
    いる項 $I$ とする($t_1 = I$)
  \end{itemize}
\item $P(t_1)$ が成立するかどうかを調べる.

  これは, 反駁エンジンを用いて, \verb:~:$P(t_1)$ が反駁出来るか
  否かをしらべることによって行われる.
  
  \begin{itemize}
  \item $P(t_1)$ が成立する場合 OK として第一段階を終了する.
  \item \verb:~:$P(t_1)$ の反駁に失敗した場合:
    \begin{itemize}
    \item $t_1$ が指定の文脈項によるものであった場合,
      \begin{enumerate}
      \item $P(I)$ が成立するかどうかを調べる.
        \begin{itemize}
        \item 成立するならば OK として第一段階を終了する.
        \item 不成立ならば, 反例を具体的に示すため,
          :verb:~$P(I)$ が成立するかどうかを調べる：
          \begin{itemize}
          \item 成立すれば, この証明木が反例となる.
            この時点でモデル検査を終了する.
          \item 不成立ならば, NG として第二段階へ行く.
          \end{itemize}
        \end{itemize}
      \end{enumerate}
    \item $t_1$ が指定の文脈項によるもので無かった場合,
      失敗として第二段階へ行く.
    \end{itemize}
  \end{itemize}
\end{enumerate}

\paragraph{メソッド適用文脈での検査}
上で述べた第一段階で失敗となった場合,
検査対象となった $P(t_1)$ は
反駁エンジンを用いて証明を行う際の新たな公理として追加される.
これは以降で説明するメソッドを適用した文脈において証明に
失敗した文脈についても同様であり, 繰り返しの毎にその回で
失敗したものが順次追加されて行く.
この新公理の集合を $Ax$ とする.

\noindent
したがって, 第一段階で失敗の場合は
$Ax={P(t_1)}$ である. 成功の場合は $Ax={}$(空集合) となる.

\noindent
第二段階での検査は以下のようにして行われる.
\begin{enumerate}
\item 検査対象文脈集合 $C$ を初期化する.
  \begin{itemize}
  \item \texttt{<文脈項>} $c$ が指定された場合は $C={c}$ とする.
  \item そうでなければ, $C={I}$ とする.
  \end{itemize}
\item 仮定節集合 $H$ を空集合 ${}$ に初期化する.
\item 文脈構成子集合 $M$ を空集合に初期化する.
\item 以下を, 文脈集合 $C$ が空になるかシステムリソースの制約条件
  によって反駁エンジンが終了するまで繰り返す.
  \begin{enumerate}
  \item $C$ に含まれる各項 $c_i$について：
    \begin{enumerate}
    \item $C=C - {c_i}$ とする.
    \item $M$ に含まれる各文脈構成子 $gen_j$ について,
      $c_i$ と $gen_j$ から, 検査対象項 $t_ij$ を作る.
    \end{enumerate}
  \end{enumerate}
\item 指定された述語 $P$ に関して，$P(c)$ が
      成り立つかどうかを調べる．
\item これが成り立たなければ, 初期状態に対して $P(<初期状態>)$ 
  が成り立つかどうか調べる.
  \begin{enumerate}
  \item 初期状態に対して成り立つ場合は OK とする
  \item 成り立たない場合は, $\neg P(初期状態)$ をゴールとして
    反駁を試みる事により, 反例を探す.
    反例が見付かればこれを印字する.
    いずれの場合も, 安全性の検査に不成功として終了する.
  \end{enumerate}
\item $H$ に関する全てのメソッド $m_i$ に対して，
      $\forall(Y) . P(m(X, Y))$ が成り立つかどうかを調べる．
\end{enumerate}

\newpage
\section{モデル検査の使用例}
\label{sec:model-check-example}

下は銀行口座についての単純な仕様である.
\begin{vvtm}
\begin{simplev}
mod! INT' {
  protecting(FOPL-CLAUSE)
  [ Int ]
  op 0 : -> Int
  op _+_ : Int Int -> Int
  op _-_ : Int Int -> Int
  pred _<=_ : Int Int

  vars M N : Int
  ax M <= M .
  ax 0 <= M & 0 <= N -> 0 <= M + N .
  ax M <= N -> 0 <= N - M .
}

mod* ACCOUNT {
  protecting(INT')
  *[ Account ]*
  op new-account : -> Account
  bop balance : Account -> Int
  bop deposit : Int Account -> Account
  bop withdraw : Int Account -> Account

  var A : Account    vars M N : Int

  eq balance(new-account) = 0 .
  ax 0 <= N -> balance(deposit(N,A)) = balance(A) + N .
  ax ~(0 <= N) -> balance(deposit(N,A)) = balance(A) .
  ax N <= balance(A) -> balance(withdraw(N,A)) = balance(A) - N .
  ax ~(N <= balance(A)) -> balance(withdraw(N,A)) = balance(A) .

}
\end{simplev}
\end{vvtm}

モジュール INT' は整数の仕様であるが, 
銀行口座の仕様を表現するために必要最低限の演算と公理のみが仕様化されている.
口座はモジュール ACCOUNT で定義されている隠れソート $Account$ に
よってモデル化されている. 
観察子(attribute) \texttt{balance} は現在の預金高を見るものであり,
メソッド \texttt{dposit} と \texttt{withdraw} は, それぞれ預金の預け入れと
引出しに相当する. 口座の初期状態は, 定数 \texttt{new-account} で表現
されており, 初期の預金残高は 0 である. これは公理

\begin{vvtm}
\begin{simplev}
   eq balance(new-account) = 0 .
 \end{simplev}
\end{vvtm}

によって表現されている.
その他の公理は, \texttt{deposit} と \texttt{withdraw} の動作の意味
および制約条件を表現したものである.
例えば

\begin{vvtm}
\begin{simplev}
  ax N <= balance(A) -> balance(withdraw(N,A)) = balance(A) - N .
\end{simplev}
\end{vvtm}

は, 現在の預金高が $N$ 以上であった時に,
\texttt{withdraw} が実行出来, 結果として預金高が $N$ だけ減ることを
表現している. 一般に隠れソートを用いてこのようなシステム状態遷移を
表現する場合は, 全ての条件に対して陽に状態変化を表現してやる必要が
ある. 例えば \texttt{withdraw} の場合, 預金残高が $N$ より少ない場合
に \texttt{withdraw(N,A)} が実行されないのは上の公理で明らかで
あるが, この場合でもシステム状態が「変化しない」ということを
表現する必要がある. これが, もう一つの \texttt{withdraw} に関する公理

\begin{vvtm}
\begin{simplev}
  ax ~(N <= balance(A)) -> balance(withdraw(N,A)) = balance(A) .
\end{simplev}
\end{vvtm}

である.

下は ACCOUNT モジュールでモデル検査を行うために用意した
モジュール PROOF である.

\begin{vvtm}
\begin{simplev}
mod* PROOF {
  protecting(ACCOUNT)

  pred P : Account .
  #define P(A:Account) ::= 0 <= balance(A) .

  op a : -> Account .
}
\end{simplev}
\end{vvtm}

述語 $P$ は, 口座の残高が決して 0 より小さくなる事はない,
ということを表現するものである.
これが実際に成立する事を, モデル検査システムを用いて調べる：
下はそのためのスクリプトである：

\begin{vvtm}
\begin{simplev}
option reset
flag(auto,on)
flag(quiet,on)
param(max-proofs,1)
flag(universal-symmetry,on)
flag(print-proofs,on)
flag(print-stats,off)

open PROOF

check safety P from new-account .
\end{simplev}
\end{vvtm}

最後の行で, モデル検査システムが起動されている. 
これを実行すると結果は次のようになるはずである
(この例では上記のモジュール宣言ならびに実行スクリプトを
\texttt{bak.cafe} という名前のファイルにいれてある.)

\begin{vvtm}
\begin{examplev}
CafeOBJ> in bank
processing input : ./bank.mod
-- defining module! INT'......._...* done.
-- defining module* ACCOUNT
** system failed to prove =*= is a congruence of ACCOUNT done.
-- defining module* PROOF
-- setting flag "auto" to "on"
   dependent: flag(auto1, on)
   dependent: flag(process-input, on)
   dependent: flag(print-kept, off)
   dependent: flag(print-new-demod, off)
   dependent: flag(print-back-demod, off)
   dependent: flag(print-back-sub, off)
   dependent: flag(control-memory, on)
   dependent: param(max-sos, 500).
   dependent: param(pick-given-ratio, 4).
   dependent: param(max-seconds, 3600).
-- setting flag "quiet" to "on"
   dependent: flag(print-message, off)
-- opening module PROOF.. done.
\end{examplev}
\end{vvtm}

\begin{vvtm}
\begin{examplev}
==========
case #0-1: new-account
----------_
goal: P(new-account)*
 
** PROOF ________________________________
 
  1:[back-demod:2] ~(0 <= 0)
  2:[] balance(new-account) = 0
  7:[] _v61:Int <= _v61
  24:[back-demod:2,1] ~(0 <= 0)
  25:[binary:24,7] 
 
** ______________________________________
** success
==========
case #1-1: deposit(_V339:Int,_hole329:Account)
----------
hypo: \A [ _V337:Int ] (\A [ _hole329:Account ] P(withdraw(_V337,
                                                           _hole329)))_
goal: \A [ _V339:Int ] (\A [ _hole329:Account ] P(_hole329) 
                                                -> P(deposit(_V339,
                                                             _hole329)))*_*
 
** PROOF ________________________________
 
  1:[] 0 <= balance(#c-1.Account)
  2:[back-demod:161] ~(0 <= (balance(#c-1.Account) + #c-1.Int))
  5:[] ~(0 <= _v34:Int) | balance(deposit(_v34:Int,_v33:Account)) 
                          = balance(_v33) + _v34
  6:[] 0 <= _v62:Int | balance(deposit(_v62:Int,_v63:Account)) 
                       = balance(_v63)
  10:[] ~(0 <= _v41:Int) | ~(0 <= _v42:Int) | 0 <= (_v41:Int 
                                                    + _v42:Int)
  136:[para-from:6,2,unit-del:1] 
    0 <= #c-1.Int
  150:[hyper:136,10,1] 0 <= (balance(#c-1.Account) + #c-1.Int)
  161:[hyper:136,5] balance(deposit(#c-1.Int,_v175:Account)) 
                    = balance(_v175) + #c-1.Int
  162:[back-demod:161,2] ~(0 <= (balance(#c-1.Account) + #c-1.Int))
  163:[binary:162,150] 
 
** ______________________________________
** succes

\end{examplev} 
\end{vvtm}

\begin{vvtm}
\begin{examplev}
==========
case #1-2: withdraw(_V337:Int,_hole329:Account)
----------_
goal: \A [ _V337:Int ] (\A [ _hole329:Account ] P(_hole329) 
                                                -> P(withdraw(_V337,
                                                              _hole329)))*_*
 
** PROOF ________________________________
 
  1:[] 0 <= balance(#c-1.Account)
  2:[back-demod:134] ~(0 <= (balance(#c-1.Account) - #c-1.Int))
  6:[] ~(_v34:Int <= balance(_v33:Account)) | balance(withdraw(_v34:Int,
                                                               _v33:Account)) 
                                              = balance(_v33) 
                                                - _v34
  7:[] _v58:Int <= balance(_v59:Account) | balance(withdraw(_v58:Int,
                                                            _v59:Account)) 
                                           = balance(_v59)
  10:[] ~(_v45:Int <= _v44:Int) | 0 <= (_v44:Int - _v45:Int)
  126:[para-from:7,2,unit-del:1] 
    #c-1.Int <= balance(#c-1.Account)
  133:[hyper:126,10] 0 <= (balance(#c-1.Account) - #c-1.Int)
  134:[hyper:126,6] balance(withdraw(#c-1.Int,#c-1.Account)) 
                    = balance(#c-1.Account) - #c-1.Int
  135:[back-demod:134,2] ~(0 <= (balance(#c-1.Account) - #c-1.Int))
  136:[binary:135,133] 
 
** ______________________________________
 

** success
** Predicate P is safe!!
 
CafeOBJ> 
\end{examplev}
\end{vvtm}

この例の場合は単純であり, 繰り返し探索は行われず, 深さ1レベルで
成功の元に終了している.

%%% Local Variables: 
%%% mode: latex
%%% TeX-master: t
%%% End: 
