模型检查数字精简版游戏

时间:2016-03-30 08:21:08

标签: model-checking planning nusmv

我正在浏览模型检查方法,我想使用这些技术来解决名为Numbers Paranoia lite的游戏 计划问题

给出 MxN ,(MxN> 8),矩阵,其中每个板都是 Problem Image - 空 - 由唯一号码标识,范围从1到8

目标是从标有1的板块中找到开始的路径,覆盖矩阵中的所有板块并且。有效路径中的所有非空盘必须按1到8的递增顺序排序。

我对将游戏建模为过渡状态并运行 NuSMV 以获得结果感到困惑。这是我的解决方案

--·¹º§ : 01
MODULE main
VAR
rec: {101,102,103,104,105,106,107,201,202,203,204,205,206,207,301,302,303,304,305,306,307,401,402,403,404,405,406,407,501,502,503,504,505,506,507};
ASSIGN
init(rec) := 101;
next(rec) := case
rec = 101 : {102, 201};
rec = 102 : {101,202,103};
rec = 103 : {102,203,104};
rec = 104 : {103, 204,105};
rec = 105 : {104,205,106};
rec = 106 : {105,206,107};
rec = 107 : {106,207};
rec = 201 : {101, 202, 301};
rec = 202 : {201,102,203,302};
rec = 203 : {103, 202, 204, 303};
rec = 204 : {203, 104, 304,205};
rec = 205 : {105,305,206,204};
rec = 206 : {106,207,205,306};
rec = 207 : {107,206,307};
rec = 301 : {201, 302,401};
rec = 302 : {301, 303, 202,402};
rec = 303 : {302, 304, 203,403};
rec = 304 : {303, 204,305,404};
--rec = 305 : {305};
rec = 306 : {305,206,307,406};
rec = 307 : {306,207,407};
rec = 401 : {301,402,501};
rec = 402 : {401,302,403,502};
rec = 403 : {402,303,404,503};
rec = 404 : {403,304,405,504};
rec = 405 : {404,305,406,505};
rec = 406 : {405,306,407,506};
rec = 407 : {406,307,507};
rec = 501 : {401,502};
rec = 502 : {501,402,503};
rec = 503 : {502,403,504};
rec = 504 : {503,404,505};
rec = 505 : {504,405,506};
rec = 506 : {505,406,507};
rec = 507 : {506,407};
TRUE : rec;
esac;
LTLSPEC !(G(rec=101 -> X(!(rec=101) U rec=305))
& G(rec=102 -> X(!(rec=102) U rec=305))
& G(rec=103 -> X(!(rec=103) U rec=305))
& G(rec=104 -> X(!(rec=104) U rec=305))
& G(rec=105 -> X(!(rec=105) U rec=305))
& G(rec=106 -> X(!(rec=106) U rec=305))
& G(rec=107 -> X(!(rec=107) U rec=305))
& G(rec=201 -> X(!(rec=201) U rec=305))
& G(rec=202 -> X(!(rec=202) U rec=305))
& G(rec=203 -> X(!(rec=203) U rec=305))
& G(rec=204 -> X(!(rec=204) U rec=305))
& G(rec=205 -> X(!(rec=205) U rec=305))
& G(rec=206 -> X(!(rec=206) U rec=305))
& G(rec=207 -> X(!(rec=207) U rec=305))
& G(rec=301 -> X(!(rec=304) U rec=305))
& G(rec=302 -> X(!(rec=302) U rec=305))
& G(rec=303 -> X(!(rec=303) U rec=305))
& G(rec=304 -> X(!(rec=304) U rec=305))
--& G(rec=305 -> X(!(rec=305)))
& G(rec=306 -> X(!(rec=306) U rec=305))
& G(rec=307 -> X(!(rec=307) U rec=305))
& G(rec=401 -> X(!(rec=401) U rec=305))
& G(rec=402 -> X(!(rec=402) U rec=305))
& G(rec=403 -> X(!(rec=403) U rec=305))
& G(rec=404 -> X(!(rec=404) U rec=305))
& G(rec=405 -> X(!(rec=405) U rec=305))
& G(rec=406 -> X(!(rec=406) U rec=305))
& G(rec=407 -> X(!(rec=407) U rec=305))
& G(rec=501 -> X(!(rec=501) U rec=305))
& G(rec=502 -> X(!(rec=502) U rec=305))
& G(rec=503 -> X(!(rec=503) U rec=305))
& G(rec=504 -> X(!(rec=504) U rec=305))
& G(rec=505 -> X(!(rec=505) U rec=305))
& G(rec=506 -> X(!(rec=506) U rec=305))
& G(rec=507 -> X(!(rec=507) U rec=305))
& (F rec=101 & F rec=102 & F rec=103 & F rec=104 & F rec=105 & F rec=106 & F rec=107 & F rec=201 & F rec=202 & F rec=203 & F rec=204 & F rec=205 & F rec=206 & F rec=207 & F rec=301 & F rec=302 & F rec=303 & F rec=304 & F rec=305 & F rec=306 & F rec=307 & F rec=401 & F rec=402 & F rec=403 & F rec=404 & F rec=405 & F rec=406 & F rec=407  & F rec=501 & F rec=502 & F rec=503 & F rec=504 & F rec=505 & F rec=506 & F rec=507)& (F(rec=402 & F(rec=107 & F(rec=303 & F(rec=202  &  F(rec=102 & F(rec=205 U rec=305))))))))

2 个答案:

答案 0 :(得分:1)

回答中,我将为您提供另一个可能的解决方案,与之前的解决方案不同,可以在 NuSMV上运行 nuXmv

您目前的解决方案尝试内存不足 - 尽管目前我对NuSMV的具体内部结构还不太了解解释其确切原因 - 我很清楚,这是由于问题的编码相当差。

您的 LTL 属性过于复杂,完全没有任何理由。 goal_state 不应该比没有从A到B的路径更复杂,其中 A 是初始状态, B 是理想的最终状态。

为此,您需要首先将以下内容编码为转换系统的一部分:

1。每个板块最多可以访问一次

2。必须以正确的顺序访问每个编号的牌照

1 的可行方法是保留整个矩阵标记本地副本每个位置访问,然后相应地修改转换关系,以便删除那些最终会在已访问过的广告牌上的转换。

对于 2。这一点,相反,因为您已经知道编号板中的所需顺序是什么,那么您可以简单地执行(某种)<两个过渡系统的强>同步合成。

让我们再次考虑下图中显示的难题

enter image description here

然后,解决这个难题的编码是:

-- Numbers Paranoia lite game model example
--
-- author: Patrick Trentin
--
MODULE main
VAR
  index    : 0..11;        -- current position
  sequence : {3, 4, 2, 5}; -- last numbered plate visited
  plates   : array 0..11 of { 0, 1 };
                           -- 0: plate not visited, 1: plate visited

DEFINE
  goal_state := sequence = 5 & index = 5 &
                plates[0] = 1 & plates[1] = 1 & plates[2] = 1  & plates[3] = 1 &
                plates[4] = 1 & plates[5] = 1 & plates[6] = 1  & plates[7] = 1 &
                plates[8] = 1 & plates[9] = 1 & plates[10] = 1 & plates[11] = 1;

ASSIGN
  init(index) := 3;
  init(sequence) := 3;

  init(plates[0])  := 0;
  init(plates[1])  := 0;
  init(plates[2])  := 0;
  init(plates[3])  := 1; -- starting plate, marked as visited
  init(plates[4])  := 0;
  init(plates[5])  := 0;
  init(plates[6])  := 0;
  init(plates[7])  := 0;
  init(plates[8])  := 0;
  init(plates[9])  := 0;
  init(plates[10]) := 0;
  init(plates[11]) := 0;


  -- possible moves from any given plate, ignoring
  -- the restriction over visiting the same plate
  -- more than once
  next(index) := case
    index = 0  : {1, 4};
    index = 1  : {0, 2, 5};
    index = 2  : {1, 3, 6};
    index = 3  : {2, 7};
    index = 4  : {0, 5, 8};
--  end plate: omitted
    index = 6  : {2, 5, 7, 10};
    index = 7  : {3, 6, 11};
    index = 8  : {4, 9};
    index = 9  : {5, 8, 10};
    index = 10 : {6, 9, 11};
    index = 11 : {7, 10};
    TRUE : index;
  esac;

  -- advance sequence only when we hit the correct plate on the table
  next(sequence) := case
--  starting plate: omitted
    sequence = 3 & next(index) = 4 : 4;
    sequence = 4 & next(index) = 2 : 2;
    sequence = 2 & next(index) = 5 : 5;
    TRUE : sequence;
  esac;

  -- mark each plate as visited as soon as we move on it
  next(plates[0])  := case next(index) = 0  : 1; TRUE : plates[0]; esac;
  next(plates[1])  := case next(index) = 1  : 1; TRUE : plates[1]; esac;
  next(plates[2])  := case next(index) = 2  : 1; TRUE : plates[2]; esac;
  next(plates[3])  := case next(index) = 3  : 1; TRUE : plates[3]; esac;
  next(plates[4])  := case next(index) = 4  : 1; TRUE : plates[4]; esac;
  next(plates[5])  := case next(index) = 5  : 1; TRUE : plates[5]; esac;
  next(plates[6])  := case next(index) = 6  : 1; TRUE : plates[6]; esac;
  next(plates[7])  := case next(index) = 7  : 1; TRUE : plates[7]; esac;
  next(plates[8])  := case next(index) = 8  : 1; TRUE : plates[8]; esac;
  next(plates[9])  := case next(index) = 9  : 1; TRUE : plates[9]; esac;
  next(plates[10]) := case next(index) = 10 : 1; TRUE : plates[10]; esac;
  next(plates[11]) := case next(index) = 11 : 1; TRUE : plates[11]; esac;

-- forbid stepping over an already visited plate,
-- unless it is the end plate
TRANS
  (index = 5) | (plates[next(index)] != 1)

-- There is no possible path that reaches the goal state
LTLSPEC !(F goal_state)

您可以使用以下命令在 NuSMV (或 nuXmv )中验证模型:

 ~$ NuSMV -int
 NuSMV> read_model -i numbers_lite.smv
 NuSMV> go;
 NuSMV> check_property

您还可以使用命令

模拟模型
 NuSMV> pick_state -v
 NuSMV> simulate -i -v -k 11

解算器找到的解决方案如下:

-- specification !(F goal_state)  is false
-- as demonstrated by the following execution sequence
Trace Description: LTL Counterexample 
Trace Type: Counterexample 
-> State: 1.1 <-
  index = 3
  sequence = 3
  plates[0] = 0
  plates[1] = 0
  plates[2] = 0
  plates[3] = 1
  plates[4] = 0
  plates[5] = 0
  plates[6] = 0
  plates[7] = 0
  plates[8] = 0
  plates[9] = 0
  plates[10] = 0
  plates[11] = 0
  goal_state = FALSE
-> State: 1.2 <-
  index = 7
  plates[7] = 1
-> State: 1.3 <-
  index = 11
  plates[11] = 1
-> State: 1.4 <-
  index = 10
  plates[10] = 1
-> State: 1.5 <-
  index = 9
  plates[9] = 1
-> State: 1.6 <-
  index = 8
  plates[8] = 1
-> State: 1.7 <-
  index = 4
  sequence = 4
  plates[4] = 1
-> State: 1.8 <-
  index = 0
  plates[0] = 1
-> State: 1.9 <-
  index = 1
  plates[1] = 1
-> State: 1.10 <-
  index = 2
  sequence = 2
  plates[2] = 1
-> State: 1.11 <-
  index = 6
  plates[6] = 1
-- Loop starts here
-> State: 1.12 <-
  index = 5
  sequence = 5
  plates[5] = 1
  goal_state = TRUE

我希望你会发现这个答案很有用。

答案 1 :(得分:0)

作为解决自己问题的一个例子,我将游戏编码为 nuXmv 的过渡状态系统,这是一个扩展 NuSMV 的工具。一些有趣的功能。

更详细地说,我模拟了以下谜题:

enter image description here

因此,在我的模型中,解决方案必须采用完全 11 步骤,因为矩阵中只有 12 板。

smv 代码如下:

-- Numbers Paranoia lite game model example
--
-- author: Patrick Trentin
--

MODULE main()
VAR
  table : array 0..11 of {1, 2, 3, 4, 0};
  index : 0..11;
  cur_max : 0..4;
  steps : 0..11;

ASSIGN
  init(steps) := 0;

  -- starting position is known
  init(index) := 3;
  init(cur_max) := 1;

  -- initialize table configuration
  init(table[0])  := 0;
  init(table[1])  := 0;
  init(table[2])  := 3;
  init(table[3])  := 1;
  init(table[4])  := 2;
  init(table[5])  := 4;
  init(table[6])  := 0;
  init(table[7])  := 0;
  init(table[8])  := 0;
  init(table[9])  := 0;
  init(table[10]) := 0;
  init(table[11]) := 0;

  -- update max value each time we hit a plate with greater value
  next(cur_max) := case
     table[index] > cur_max : table[index];
     TRUE : cur_max;
  esac;

  -- overwrite the value in the plate each time we visit it,
  -- to prevent visiting it again from another plate
  next(table[0])  := case index = 0  : cur_max; TRUE : table[0]; esac;
  next(table[1])  := case index = 1  : cur_max; TRUE : table[1]; esac;
  next(table[2])  := case index = 2  : cur_max; TRUE : table[2]; esac;
  next(table[3])  := case index = 3  : cur_max; TRUE : table[3]; esac;
  next(table[4])  := case index = 4  : cur_max; TRUE : table[4]; esac;
  next(table[5])  := case index = 5  : cur_max; TRUE : table[5]; esac;
  next(table[6])  := case index = 6  : cur_max; TRUE : table[6]; esac;
  next(table[7])  := case index = 7  : cur_max; TRUE : table[7]; esac;
  next(table[8])  := case index = 8  : cur_max; TRUE : table[8]; esac;
  next(table[9])  := case index = 9  : cur_max; TRUE : table[9]; esac;
  next(table[10]) := case index = 10 : cur_max; TRUE : table[10]; esac;
  next(table[11]) := case index = 11 : cur_max; TRUE : table[11]; esac;

DEFINE
  upper_plate := (index - 4);
  lower_plate := (index + 4);
  left_plate  := (index - 1);
  right_plate := (index + 1);

  upper_exists := upper_plate >= 0;
  lower_exists := lower_plate <= 11;
  left_exists  := (left_plate >= 0)  &
                  (left_plate <= 11) &
                  (left_plate mod 4) < (index mod 4); 
  right_exists := (right_plate >= 0)  &
                  (right_plate <= 11) &
                  (right_plate mod 4) > (index mod 4);

  can_go_upper := upper_exists &
                  (table[upper_plate] = 0 |
                  table[upper_plate] = cur_max + 1);
  can_go_lower := lower_exists &
                  (table[lower_plate] = 0 |
                  table[lower_plate] = cur_max + 1);
  can_go_left  := left_exists &
                  (table[left_plate] = 0 |
                  table[left_plate] = cur_max + 1);
  can_go_right := right_exists &
                  (table[right_plate] = 0 |
                  table[right_plate] = cur_max + 1);

-- set of legal moves
TRANS
  !can_go_upper -> next(index) != upper_plate
TRANS
  !can_go_lower -> next(index) != lower_plate
TRANS
  !can_go_left  -> next(index) != left_plate
TRANS
  !can_go_right -> next(index) != right_plate
TRANS
  next(index) = index | next(index) = upper_plate |
  next(index) = lower_plate | next(index) = left_plate |
  next(index) = right_plate

-- loop on the current plate only when search has ended
TRANS
  (!can_go_upper & !can_go_lower & !can_go_left & !can_go_right) -> next(index) = index
TRANS
  (can_go_upper | can_go_lower | can_go_left | can_go_right) -> next(index) != index

-- increase steps value if plate changes
TRANS
  index != next(index) -> next(steps) = steps + 1
TRANS
  index = next(index) -> next(steps) = steps

-- there does not exist a path that terminates on the 
-- highest value and takes exactly N = size(array)-1 
-- steps to reach.
-- A path violating this property is a possible solution
-- for the Numbers Paranoia lite game.
LTLSPEC !(F (table[index] = 4 & steps = 11))

您可以使用以下命令验证模型,这些命令依赖于基础MathSAT5 SMT解算器来执行验证步骤:

 ~$ nuXmv -int
 nuXmv> read_model -i numbers_lite.smv
 nuXmv> go_msat;
 nuXmv> msat_check_ltlspec_bmc -k 11

您还可以使用命令

模拟模型
 nuXmv> msat_pick_state -i -v
 nuXmv> msat_simulate -i -v -k 11

解算器找到的解决方案如下:

-- specification !( F (table[index] = 4 & steps = 11))  is false
-- as demonstrated by the following execution sequence
Trace Description: MSAT BMC counterexample 
Trace Type: Counterexample 
  -> State: 1.1 <-
    table[0] = 0
    table[1] = 0
    table[2] = 3
    table[3] = 1
    table[4] = 2
    table[5] = 4
    table[6] = 0
    table[7] = 0
    table[8] = 0
    table[9] = 0
    table[10] = 0
    table[11] = 0
    index = 3
    cur_max = 1
    steps = 0
    can_go_right = FALSE
    can_go_left = FALSE
    can_go_lower = TRUE
    right_exists = FALSE
    left_exists = TRUE
    lower_exists = TRUE
    upper_exists = FALSE
    right_plate = 4
    left_plate = 2
    lower_plate = 7
    upper_plate = -1
  -> State: 1.2 <-
    index = 7
    steps = 1
    can_go_left = TRUE
    can_go_upper = FALSE
    upper_exists = TRUE
    right_plate = 8
    left_plate = 6
    lower_plate = 11
    upper_plate = 3
  -> State: 1.3 <-
    table[7] = 1
    index = 11
    steps = 2
    lower_exists = FALSE
    right_plate = 12
    left_plate = 10
    lower_plate = 15
    upper_plate = 7
  -> State: 1.4 <-
    table[11] = 1
    index = 10
    steps = 3
    can_go_upper = TRUE
    right_exists = TRUE
    right_plate = 11
    left_plate = 9
    lower_plate = 14
    upper_plate = 6
  -> State: 1.5 <-
    table[10] = 1
    index = 9
    steps = 4
    can_go_upper = FALSE
    right_plate = 10
    left_plate = 8
    lower_plate = 13
    upper_plate = 5
  -> State: 1.6 <-
    table[9] = 1
    index = 8
    steps = 5
    can_go_left = FALSE
    can_go_upper = TRUE
    left_exists = FALSE
    right_plate = 9
    left_plate = 7
    lower_plate = 12
    upper_plate = 4
  -> State: 1.7 <-
    table[8] = 1
    index = 4
    steps = 6
    can_go_lower = FALSE
    lower_exists = TRUE
    right_plate = 5
    left_plate = 3
    lower_plate = 8
    upper_plate = 0
  -> State: 1.8 <-
    table[4] = 1
    index = 0
    cur_max = 2
    steps = 7
    can_go_right = TRUE
    upper_exists = FALSE
    right_plate = 1
    left_plate = -1
    lower_plate = 4
    upper_plate = -4
  -> State: 1.9 <-
    table[0] = 2
    index = 1
    steps = 8
    left_exists = TRUE
    right_plate = 2
    left_plate = 0
    lower_plate = 5
    upper_plate = -3
  -> State: 1.10 <-
    table[1] = 2
    index = 2
    steps = 9
    can_go_right = FALSE
    can_go_lower = TRUE
    right_plate = 3
    left_plate = 1
    lower_plate = 6
    upper_plate = -2
  -> State: 1.11 <-
    table[2] = 2
    index = 6
    cur_max = 3
    steps = 10
    can_go_left = TRUE
    can_go_lower = FALSE
    can_go_upper = FALSE
    upper_exists = TRUE
    right_plate = 7
    left_plate = 5
    lower_plate = 10
    upper_plate = 2
  -> State: 1.12 <-
    table[6] = 3
    index = 5
    steps = 11
    can_go_left = FALSE
    right_plate = 6
    left_plate = 4
    lower_plate = 9
    upper_plate = 1

请注意,命令-k 11的选项msat_check_ltlspec_bmc要求求解程序查找任何解决方案,其转换次数最多 11鉴于我对问题的编码,如果在 11个步骤中找不到解决方案,那么我们可以安全地得出结论,输入表没有可能的解决方案。

您可以轻松地为更大的表扩展此模型,我建议您自动编写 python脚本,因为模型的结构保持不变,只有少数参数和初始化值变化

我希望你会发现这个答案很有帮助。