自動かき氷機の形式検証の話

夏なので自動かき氷機の「正しさ」を数学的に検証する話を書きます。 (この記事は以下のツイートから始まるスレッドの内容をまとめ直したものです。)

とりあえずかき氷機の抽象的な動作を記述したオートマトンを書きます。

このオートマトンだとボールを置かずにボタンを押したときに虚空に向かってかき氷を作るので、ボールがあるかどうかを検知するようにします。

これでもまだ動作中にボールを除いたときに虚空に向かってかき氷を作るので、そういうときは止まるようにします。

さて、モデル検査ツールであるNuSMVの形式でモデルを書いてみます。

MODULE main
VAR
bowl : boolean;
button : boolean;
state : {initial, wait, running, done};
ASSIGN
init(bowl) := {FALSE, TRUE};
init(button) := {FALSE, TRUE};
init(state) := {initial};
next(bowl) := {FALSE, TRUE};
next(button) := {FALSE, TRUE};
next(state) :=
case
state = initial & bowl : wait;
state = wait & button : running;
state = running & !bowl : initial;
state = running : done;
state = done & !bowl : initial;
TRUE : state;
esac;

動作中はボールがない (LTL で"G(state = running -> bowl)")という仕様を検証してみようとすると、反例が見つかります。ボタンを押す前にボールを取り除いた場合でも、そのままかき氷を作る仕様になっていました。

*** This is NuSMV 2.6.0 (compiled on Sat Oct 20 18:58:24 2018)
*** Enabled addons are: compass
*** For more information on NuSMV see <http://nusmv.fbk.eu>
*** or email to <nusmv-users@list.fbk.eu>.
*** Please report bugs to <Please report bugs to <nusmv-users@fbk.eu>>
*** Copyright (c) 2010-2014, Fondazione Bruno Kessler
*** This version of NuSMV is linked to the CUDD library version 2.4.1
*** Copyright (c) 1995-2004, Regents of the University of Colorado
*** This version of NuSMV is linked to the MiniSat SAT solver.
*** See http://minisat.se/MiniSat.html
*** Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson
*** Copyright (c) 2007-2010, Niklas Sorensson
-- specification G (state = running -> bowl) is false
-- as demonstrated by the following execution sequence
Trace Description: LTL Counterexample
Trace Type: Counterexample
-> State: 1.1 <-
bowl = FALSE
button = FALSE
state = initial
-> State: 1.2 <-
bowl = TRUE
-> State: 1.3 <-
bowl = FALSE
button = TRUE
state = wait
-> State: 1.4 <-
button = FALSE
state = running
-- Loop starts here
-> State: 1.5 <-
state = initial
-> State: 1.6 <-
view raw NuSMV-result hosted with ❤ by GitHub

ということで、一度ボールが置かれた待機状態でボールが取り除かれた場合は初期状態に戻るようにしてみます。

MODULE main
VAR
bowl : boolean;
button : boolean;
state : {initial, wait, running, done};
ASSIGN
init(bowl) := {FALSE, TRUE};
init(button) := {FALSE, TRUE};
init(state) := {initial};
next(bowl) := {FALSE, TRUE};
next(button) := {FALSE, TRUE};
next(state) :=
case
state = initial & bowl : wait;
state = wait & !bowl : initial;
state = wait & button & bowl: running;
state = running & !bowl : initial;
state = running : done;
state = done & !bowl : initial;
TRUE : state;
esac;
LTLSPEC G(state = running -> bowl)

このモデルでもまだエラーが出ます。このモデルだとボタンを押した直後にボールを取り除くと、初期状態に戻るまでの 1 ステップは虚空にボールをかき氷をばらまくようです。

*** This is NuSMV 2.6.0 (compiled on Sat Oct 20 18:58:24 2018)
*** Enabled addons are: compass
*** For more information on NuSMV see <http://nusmv.fbk.eu>
*** or email to <nusmv-users@list.fbk.eu>.
*** Please report bugs to <Please report bugs to <nusmv-users@fbk.eu>>
*** Copyright (c) 2010-2014, Fondazione Bruno Kessler
*** This version of NuSMV is linked to the CUDD library version 2.4.1
*** Copyright (c) 1995-2004, Regents of the University of Colorado
*** This version of NuSMV is linked to the MiniSat SAT solver.
*** See http://minisat.se/MiniSat.html
*** Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson
*** Copyright (c) 2007-2010, Niklas Sorensson
-- specification G (state = running -> bowl) is false
-- as demonstrated by the following execution sequence
Trace Description: LTL Counterexample
Trace Type: Counterexample
-> State: 1.1 <-
bowl = FALSE
button = FALSE
state = initial
-> State: 1.2 <-
bowl = TRUE
-> State: 1.3 <-
button = TRUE
state = wait
-> State: 1.4 <-
bowl = FALSE
button = FALSE
state = running
-- Loop starts here
-> State: 1.5 <-
state = initial
-> State: 1.6 <-
view raw NuSMV-result hosted with ❤ by GitHub

頑張ればなんとかなるかもしれないですが、 そろそろお昼ご飯を食べたいので 今回は仕様の方を妥協して、「ボールがないなら次のステップでは動作していない」という仕様 (LTL 式だと G(!bowl -> X(state != running)))を検証してみます。できました。よかったよかった。

MODULE main
VAR
bowl : boolean;
button : boolean;
state : {initial, wait, running, done};
ASSIGN
init(bowl) := {FALSE, TRUE};
init(button) := {FALSE, TRUE};
init(state) := {initial};
next(bowl) := {FALSE, TRUE};
next(button) := {FALSE, TRUE};
next(state) :=
case
state = initial & bowl : wait;
state = wait & !bowl : initial;
state = wait & button & bowl: running;
state = running & !bowl : initial;
state = running : done;
state = done & !bowl : initial;
TRUE : state;
esac;
LTLSPEC G(!bowl -> X(state != running) )
*** This is NuSMV 2.6.0 (compiled on Sat Oct 20 18:58:24 2018)
*** Enabled addons are: compass
*** For more information on NuSMV see <http://nusmv.fbk.eu>
*** or email to <nusmv-users@list.fbk.eu>.
*** Please report bugs to <Please report bugs to <nusmv-users@fbk.eu>>
*** Copyright (c) 2010-2014, Fondazione Bruno Kessler
*** This version of NuSMV is linked to the CUDD library version 2.4.1
*** Copyright (c) 1995-2004, Regents of the University of Colorado
*** This version of NuSMV is linked to the MiniSat SAT solver.
*** See http://minisat.se/MiniSat.html
*** Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson
*** Copyright (c) 2007-2010, Niklas Sorensson
-- specification G (!bowl -> X state != running) is true
view raw NuSMV-result hosted with ❤ by GitHub