プログラミングなどなど

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

SPIN:3x3の魔法陣の解を求める

SPINモデル検査入門」という本の11章ケーススタディに8クイーン問題の解を求めるサンプルがあったので、自身の練習として3x3の魔法陣の解を求めてみたいと思います。

作成したソースは以下のとおりです。

ソース magicSquare.pml

#define SIZE 3

/* -------------------------
  魔法陣ms
        1列目 2列目 3列目
  1行目 ms[0] ms[1] ms[2]
  2行目 ms[3] ms[4] ms[5]
  3行目 ms[6] ms[7] ms[8]
 -------------------------- */
byte ms[SIZE*SIZE];

/* -------------------------
 使用済みの数値判定用

 used[0] == true  <- 数値1使用済み
 used[1] == false <- 数値2未使用
 :
 used[8] == true  <- 数値9使用済み
 -------------------------- */
bool used[SIZE*SIZE];

active proctype p()
{

  /* 魔法陣に1から9の数値を非決定的に設定していく */
  byte a;
  byte i = 0
  do
  :: i < (SIZE*SIZE) ->
      /* 数値を非決定的に選ぶ */
      if
      :: a = 1
      :: a = 2
      :: a = 3
      :: a = 4
      :: a = 5
      :: a = 6
      :: a = 7
      :: a = 8
      :: a = 9
      fi

      if
      /* 選んだ数値が未使用の場合 */
      :: used[a-1] != true ->
           ms[i] = a;
           used[a-1] = true
           i++
      /* 選んだ数値が使用済みの場合 */
      :: else -> skip
      fi
  /* 魔法陣に数値を入れきったらループを抜ける */
  :: i == SIZE*SIZE -> break
  od

  /* 魔法陣 */
  printf("\nMS : %d %d %d\nMS : %d %d %d\nMS : %d %d %d\n",
   ms[0],ms[1],ms[2],ms[3],ms[4],ms[5],ms[6],ms[7],ms[8]);

  /* 魔法陣の和を確認する */
  bool check = true;
  if
  :: /* 各行のチェック */
     (ms[0] + ms[1] + ms[2] == 15) &&
     (ms[3] + ms[4] + ms[5] == 15) &&
     (ms[6] + ms[7] + ms[8] == 15) &&
     /* 各列のチェック */
     (ms[0] + ms[3] + ms[6] == 15) &&
     (ms[1] + ms[4] + ms[7] == 15) &&
     (ms[2] + ms[5] + ms[8] == 15) &&
     /* 対角線のチェック */
     (ms[0] + ms[4] + ms[8] == 15) &&
     (ms[6] + ms[4] + ms[2] == 15) -> check = true
  :: else -> check = false
  fi
  
  /* 検証により検出させるため check == false を検査式にする */
  assert(check == false);
}

手動シミュレーションでの簡易動作確認

手動シミュレーションで検証エラーになる場合とならない場合を簡易的に確認します。

魔法陣が成立しない(検証エラーにならない)場合

手動で何度も入力するのが面倒なので入力データを作成しました。

入力データ in01
$ cat in01 
1
2
3
4
5
6
7
8
9
シミュレーション

作成したデータをリダイレクトで入力します。

$ spin -i magicSquare.pml < in01 
Select stmnt (proc  0 (p:1) )
	choice 1: ((i<(3*3)))
:
(略)
:
Select stmnt (proc  0 (p:1) )
	choice 2: ((i==(3*3)))
      
MS : 1 2 3
MS : 4 5 6
MS : 7 8 9
Select stmnt (proc  0 (p:1) )
	choice 2: (else)
1 process created

魔法陣が成立していないので、検証エラーも出ていません。

魔法陣が成立する(検証エラーになる)場合

入力データ in02
$ cat in02 
2
7
6
9
5
1
4
3
8
シミュレーション
$ spin -i magicSquare.pml < in02
Select stmnt (proc  0 (p:1) )
	choice 1: ((i<(3*3)))
:
(略)
:
spin: magicSquare.pml:78, Error: assertion violated
spin: text of failed assertion: assert((check==0))
#processes: 1
		ms[0] = 2
		ms[1] = 7
		ms[2] = 6
		ms[3] = 9
		ms[4] = 5
		ms[5] = 1
		ms[6] = 4
		ms[7] = 3
		ms[8] = 8
		used[0] = 1
		used[1] = 1
		used[2] = 1
		used[3] = 1
		used[4] = 1
		used[5] = 1
		used[6] = 1
		used[7] = 1
		used[8] = 1
 89:	proc  0 (p:1) magicSquare.pml:78 (state 34)
1 process created

魔法陣が成立しているので、検証エラーとなっています。

検証器を作成しての確認

検証時の生成から実行

$ spin -run -e magicSquare.pml 
Depth=     172 States=    1e+06 Transitions= 2.47e+06 Memory=   181.952	t=     0.51 R=   2e+06
Depth=     172 States=    2e+06 Transitions= 4.93e+06 Memory=   235.272	t=     1.04 R=   2e+06
Depth=     172 States=    3e+06 Transitions=  7.4e+06 Memory=   288.593	t=     1.59 R=   2e+06
Depth=     172 States=    4e+06 Transitions= 9.87e+06 Memory=   341.913	t=     2.15 R=   2e+06
pan:1: assertion violated (check==0) (at depth 110)
pan: wrote magicSquare.pml1.trail
Depth=     172 States=    5e+06 Transitions= 1.23e+07 Memory=   395.233	t=     2.78 R=   2e+06
pan: wrote magicSquare.pml2.trail
Depth=     172 States=    6e+06 Transitions= 1.48e+07 Memory=   448.554	t=     3.47 R=   2e+06
Depth=     172 States=    7e+06 Transitions= 1.73e+07 Memory=   501.776	t=     4.18 R=   2e+06
Depth=     172 States=    8e+06 Transitions= 1.97e+07 Memory=   555.097	t=     4.83 R=   2e+06
Depth=     172 States=    9e+06 Transitions= 2.22e+07 Memory=   608.417	t=     5.47 R=   2e+06
pan: wrote magicSquare.pml3.trail
Depth=     172 States=    1e+07 Transitions= 2.47e+07 Memory=   661.737	t=     6.15 R=   2e+06
pan: wrote magicSquare.pml4.trail
Depth=     172 States=  1.1e+07 Transitions= 2.71e+07 Memory=   715.058	t=     6.84 R=   2e+06
Depth=     172 States=  1.2e+07 Transitions= 2.96e+07 Memory=   768.378	t=     7.56 R=   2e+06
Depth=     172 States=  1.3e+07 Transitions= 3.21e+07 Memory=   821.698	t=     8.39 R=   2e+06
Depth=     172 States=  1.4e+07 Transitions= 3.45e+07 Memory=   874.921	t=     9.22 R=   2e+06
pan: wrote magicSquare.pml5.trail
Depth=     172 States=  1.5e+07 Transitions=  3.7e+07 Memory=   928.241	t=     10.1 R=   1e+06
pan: wrote magicSquare.pml6.trail
Depth=     172 States=  1.6e+07 Transitions= 3.95e+07 Memory=   981.562	t=     10.9 R=   1e+06
Depth=     172 States=  1.7e+07 Transitions= 4.19e+07 Memory=  1034.882	t=     11.9 R=   1e+06
Depth=     172 States=  1.8e+07 Transitions= 4.44e+07 Memory=  1088.202	t=       13 R=   1e+06
Depth=     172 States=  1.9e+07 Transitions= 4.69e+07 Memory=  1141.522	t=     13.9 R=   1e+06
pan: wrote magicSquare.pml7.trail
Depth=     172 States=    2e+07 Transitions= 4.93e+07 Memory=  1194.843	t=     14.8 R=   1e+06
pan: wrote magicSquare.pml8.trail
Depth=     172 States=  2.1e+07 Transitions= 5.18e+07 Memory=  1248.065	t=     15.7 R=   1e+06
Depth=     172 States=  2.2e+07 Transitions= 5.43e+07 Memory=  1301.386	t=     16.7 R=   1e+06
Depth=     172 States=  2.3e+07 Transitions= 5.67e+07 Memory=  1354.706	t=     17.9 R=   1e+06
Depth=     172 States=  2.4e+07 Transitions= 5.92e+07 Memory=  1408.026	t=     18.9 R=   1e+06

(Spin Version 6.4.9 -- 17 December 2018)
	+ Partial Order Reduction

Full statespace search for:
	never claim         	- (none specified)
	assertion violations	+
	cycle checks       	- (disabled by -DSAFETY)
	invalid end states	+

State-vector 32 byte, depth reached 172, errors: 8
 24987242 states, stored
 36640017 states, matched
 61627259 transitions (= stored+matched)
        0 atomic steps
hash conflicts:  19533061 (resolved)

Stats on memory usage (in Megabytes):
 1429.781	equivalent memory usage for states (stored*(State-vector + overhead))
 1332.493	actual memory usage for states (compression: 93.20%)
         	state-vector as stored = 28 byte + 28 byte overhead
  128.000	memory used for hash table (-w24)
    0.534	memory used for DFS stack (-m10000)
 1460.663	total actual memory usage


unreached in proctype p
	(0 of 35 states)

pan: elapsed time 19.8 seconds
pan: rate 1263257.9 states/second

8パターンのエラーが検出されています。

エラートレイルの列挙

8パターンあるので魔法陣出力部分を抜き出して出力させます。

$ for i in $(seq 1 8); do echo magicSquare.pml$i.trail; spin -t$i -B magicSquare.pml | grep MS; done
magicSquare.pml1.trail
MS : 2 7 6
MS : 9 5 1
MS : 4 3 8
magicSquare.pml2.trail
MS : 2 9 4
MS : 7 5 3
MS : 6 1 8
magicSquare.pml3.trail
MS : 4 3 8
MS : 9 5 1
MS : 2 7 6
magicSquare.pml4.trail
MS : 4 9 2
MS : 3 5 7
MS : 8 1 6
magicSquare.pml5.trail
MS : 6 1 8
MS : 7 5 3
MS : 2 9 4
magicSquare.pml6.trail
MS : 6 7 2
MS : 1 5 9
MS : 8 3 4
magicSquare.pml7.trail
MS : 8 1 6
MS : 3 5 7
MS : 4 9 2
magicSquare.pml8.trail
MS : 8 3 4
MS : 1 5 9
MS : 6 7 2

確かに魔法陣になっていそうです。