I am going through the model checking methods and I want to use the techniques in order to solve a game called Numbers Paranoia lite as a planning problem.
Given an MxN, (MxN > 8), matrix in which each plate is either Problem Image - empty - identified by a unique number ranging from 1 to 8
the goal is to find a path that starts from the plate labelled with 1, covers all the plates in the matrix and ends on the plate labelled with 8. All non-empty plates in a valid path must be sorted in increasing order from 1 to 8.
I am confused with modelling the game into transition state and running NuSMV for results. Here is my solution
--·¹º§ : 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))))))))
In this new answer I will provide you with another possible solution that, differently from my previous one, can be run on both NuSMV and nuXmv.
Your current attempt of a solution goes out of memory and --although at the time being I don't know enough on the specific internals of NuSMV to explain the exact reason-- it is clear to me that this is due to a rather poor encoding of the problem.
Your LTL property is overly complicated, and for no reason at all. The goal_state shouldn't be more complicated than there exists no path from A to B, where A is the initial state and B is the desired end state.
For doing this, you need to encode the following things as part of your transition system first:
1. each plate can be visited at most once
2. each numbered plate must be visited in the correct order
A possible approach for point 1. is to keep a local copy of the whole matrix and mark each location as visited, and then modify the transition relation accordingly so to remove those transitions that would end up over an already visited plate.
For the point 2., instead, since you already know what is the desired order among the numbered plates, then you can simply do a (sort of) synchronous composition of the two transitions systems.
Let's consider again the puzzle shown in the following picture:
Then, an encoding that solves that puzzle is:
-- 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)
You can verify the model in NuSMV (or nuXmv) with the following commands:
~$ NuSMV -int
NuSMV> read_model -i numbers_lite.smv
NuSMV> go;
NuSMV> check_property
You can also simulate the model with the commands
NuSMV> pick_state -v
NuSMV> simulate -i -v -k 11
The solution found by the solver is the following one:
-- 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
I hope you'll find this answer helpful.