プログラミングなどなど

プログラミングに関係しそうな内容を

SPIN:LTLを使った2プロセスの実行順序確認

SPINでは記述したモデルについてLTLで記述した性質の検証を行うことで反例がないかどうかをチェックしてくれます。私はまだ理解が浅いのでLTLで記述した検証が意図したものになっているかどうかについて自信がありません。そこで、記述した検証が、意図した検証になっていそうだということを確かめたいと思いました。

結論としては解をつかめていないのですが、大丈夫そうだと思える部分まで行くことができましたので、行った手順とメモを残します。

モデルとしては、2つのプロセスがそれぞれが管理する変数を更新するパターンが2通りあるということを検出したいと思います。

モデル

$ cat -n 2pro.pml
     1	bool isAProced = false;
     2	bool isBProced = false;
     3	
     4	active proctype A()
     5	{
     6	  isAProced = true;
     7	}
     8	
     9	active proctype B()
    10	{
    11	  isBProced = true;
    12	}

プロセスAとBがあり、並列に動作するモデルです。実行の制約は無いので、6行目:プロセスAのisAProced = trueが先に実行される場合と11行目:プロセスBのisBProced = trueが先に実行される場合があるはずです。

ランダムシミュレーション

ランダムシミュレーションを何度か行ってみると、確かに上記の2ケースが結果として出てきます。

プロセスAの処理が先に実行されるケース

$ spin -p 2pro.pml
  0:	proc  - (:root:) creates proc  0 (A)
  0:	proc  - (:root:) creates proc  1 (B)
  1:	proc  0 (A:1) 2pro.pml:6 (state 1)	[isAProced = 1]
  2:	proc  1 (B:1) 2pro.pml:11 (state 1)	[isBProced = 1]
  2:	proc  1 (B:1)           terminates
  2:	proc  0 (A:1)       terminates
2 processes created

プロセスBの処理が先に実行されるケース

$ spin -p 2pro.pml
  0:	proc  - (:root:) creates proc  0 (A)
  0:	proc  - (:root:) creates proc  1 (B)
  1:	proc  1 (B:1) 2pro.pml:11 (state 1)	[isBProced = 1]
  1:	proc  1 (B:1)           terminates
  2:	proc  0 (A:1) 2pro.pml:6 (state 1)	[isAProced = 1]
  2:	proc  0 (A:1)       terminates
2 processes created

LTLでの確認

上記のモデルではプロセスA,Bのどちらが先に実行されても最終的にisAProced == true && isBProced == trueが成立します。成立する性質の否定を使って、プロセスAから実行される場合とプロセスBから実行される場合を検出させたいと思います。

検証式

以下の検証式をモデルに追加します。

検証したい性質
ltl a01 { <>[](isAProced == true && isBProced == true) }
検証したい性質の否定
ltl c01 { !<>[](isAProced == true && isBProced == true) }
実行
検証したい性質の確認
$ spin -run -ltl a01 2pro.pml
pan: ltl formula a01

(Spin Version 6.5.0 -- 17 July 2019)
	+ Partial Order Reduction

Full statespace search for:
	never claim         	+ (a01)
	assertion violations	+ (if within scope of claim)
	acceptance   cycles 	+ (fairness disabled)
	invalid end states	- (disabled by never claim)

State-vector 28 byte, depth reached 9, errors: 0
       12 states, stored (21 visited)
       13 states, matched
       34 transitions (= visited+matched)
        0 atomic steps
hash conflicts:         0 (resolved)

Stats on memory usage (in Megabytes):
    0.001	equivalent memory usage for states (stored*(State-vector + overhead))
    0.290	actual memory usage for states
  128.000	memory used for hash table (-w24)
    0.534	memory used for DFS stack (-m10000)
  128.730	total actual memory usage


unreached in proctype A
	(0 of 2 states)
unreached in proctype B
	(0 of 2 states)
unreached in claim a01
	_spin_nvr.tmp:10, state 13, "-end-"
	(1 of 13 states)
unreached in claim c01
	_spin_nvr.tmp:19, state 10, "(((isAProced==1)&&(isBProced==1)))"
	_spin_nvr.tmp:21, state 13, "-end-"
	(2 of 13 states)

pan: elapsed time 0 seconds
検証したい性質がどのように成り立ったかの確認

実行してみるとエラーが3件検出されます。私の理想としては、2件の検出なのですがどの様な式にすればよいのかわかりませんでした。

$ spin -run -ltl c01 -c0 -e 2pro.pml
pan: ltl formula c01
pan:1: acceptance cycle (at depth 8)
pan: wrote 2pro.pml1.trail
pan: wrote 2pro.pml2.trail
pan: wrote 2pro.pml3.trail

(Spin Version 6.5.0 -- 17 July 2019)
	+ Partial Order Reduction

Full statespace search for:
	never claim         	+ (c01)
	assertion violations	+ (if within scope of claim)
	acceptance   cycles 	+ (fairness disabled)
	invalid end states	- (disabled by never claim)

State-vector 28 byte, depth reached 9, errors: 3
        9 states, stored (11 visited)
        7 states, matched
       18 transitions (= visited+matched)
        0 atomic steps
hash conflicts:         0 (resolved)

Stats on memory usage (in Megabytes):
    0.000	equivalent memory usage for states (stored*(State-vector + overhead))
    0.290	actual memory usage for states
  128.000	memory used for hash table (-w24)
    0.534	memory used for DFS stack (-m10000)
  128.730	total actual memory usage


unreached in proctype A
	(0 of 2 states)
unreached in proctype B
	(0 of 2 states)
unreached in claim c01
	_spin_nvr.tmp:21, state 13, "-end-"
	(1 of 13 states)

pan: elapsed time 0 seconds

エラートレイルの確認

プロセスBの処理が先に実行されるケース
$ spin -t1 -p 2pro.pml
ltl a01: <> ([] (((isAProced==1)) && ((isBProced==1))))
ltl c01: ! (<> ([] (((isAProced==1)) && ((isBProced==1)))))
starting claim 3
Never claim moves to line 15	[(1)]
  2:	proc  1 (B:1) 2pro.pml:11 (state 1)	[isBProced = 1]
  4: proc 1 terminates
  6:	proc  0 (A:1) 2pro.pml:6 (state 1)	[isAProced = 1]
Never claim moves to line 14	[(((isAProced==1)&&(isBProced==1)))]
  8: proc 0 terminates
  <<<<<START OF CYCLE>>>>>
Never claim moves to line 19	[(((isAProced==1)&&(isBProced==1)))]
spin: trail ends after 10 steps
#processes: 0
		isAProced = 1
		isBProced = 1
 10:	proc  - (c01:1) _spin_nvr.tmp:18 (state 10)
2 processes created

$ spin -t2 -p 2pro.pml
ltl a01: <> ([] (((isAProced==1)) && ((isBProced==1))))
ltl c01: ! (<> ([] (((isAProced==1)) && ((isBProced==1)))))
starting claim 3
Never claim moves to line 15	[(1)]
  2:	proc  1 (B:1) 2pro.pml:11 (state 1)	[isBProced = 1]
  4: proc 1 terminates
  6:	proc  0 (A:1) 2pro.pml:6 (state 1)	[isAProced = 1]
Never claim moves to line 14	[(((isAProced==1)&&(isBProced==1)))]
  8: proc 0 terminates
Never claim moves to line 19	[(((isAProced==1)&&(isBProced==1)))]
  <<<<<START OF CYCLE>>>>>
spin: trail ends after 10 steps
#processes: 0
		isAProced = 1
		isBProced = 1
 10:	proc  - (c01:1) _spin_nvr.tmp:18 (state 10)
2 processes created
プロセスAの処理が先に実行されるケース
$ spin -t3 -p 2pro.pml
ltl a01: <> ( (((isAProced==1)) && ((isBProced==1))))
ltl c01: ! (<> ( (((isAProced==1)) && ((isBProced==1)))))
starting claim 3
Never claim moves to line 15	[(1)]
  2:	proc  0 (A:1) 2pro.pml:6 (state 1)	[isAProced = 1]
  4:	proc  1 (B:1) 2pro.pml:11 (state 1)	[isBProced = 1]
Never claim moves to line 14	[(((isAProced==1)&&(isBProced==1)))]
  6: proc 1 terminates
Never claim moves to line 19	[(((isAProced==1)&&(isBProced==1)))]
  8: proc 0 terminates
spin: trail ends after 8 steps
#processes: 0
		isAProced = 1
		isBProced = 1
  8:	proc  - (c01:1) _spin_nvr.tmp:18 (state 10)
2 processes created

トライ&エラー

上記の結果が得られるまで、あ〜だ。こ〜だ。とトライ&エラーを繰り返しました。今後の役に立ちそうだったので残しておきます。

常に失敗させる

以下の様なLTLを記述して常に失敗させました。結果としては、18のエラーが検出されました。中身を確認すると、確かに欲しい結果が入っていたので、これを元に検証したい性質でエラーが出るように進めました。

/* errors: 18 */
ltl l01 { false }

改良1

ゴールにすこし近づくように以下を記述しました。なぜ、エラー検出数が異なるのかは、まだ理解できていません。

/* errors: 6 */
ltl l02 { !<>(isAProced == true) }

/* errors: 7 */
ltl l03 { !<>(isBProced == true) }

改良2

さらにゴールに近づけました。
これに[]を追加したものが最初に紹介した検証式となります。

/* errors: 5 */
ltl l04 { !<>(isAProced == true && isBProced == true) }