Vending Machine in NuSMV - model-checking

I am new to NuSMV, I am trying to create Vending Machine implementation from Kripke structure, I have three boolean (coin, selection, brewing) as well as three states.However, When I compile the code I receive "Line 25: at token ":": syntax error" If anyone sees any errors in my code I would appreciate the help.
Kripke structure
my attempt to write the code is as follow:
MODULE main
VAR
location : {s1,s2,s3};
coin : boolean;
selection: boolean;
brweing: boolean;
ASSIGN
init(location) := s1;
init(coin) := FALSE;
init(selection) := FALSE;
init(brweing) := FALSE;
next(location) :=
case
location = s1 : s2;
TRUE: coin;
esac;
next(location) :=
case
location = (s2 : s3 & (TRUE: selection));
location = (s2 : s1 & (FALSE: selection) & (FALSE: coin));
esac;
next(location) :=
case
location = (s3 : s3 & (TRUE: brewing));
location = (s3 : s1 & (FALSE: selection) & (FALSE: coin) & (FALSE: brewing));
esac;
-- specification
• AG [s ⇒ b] whenever a selection is made coffee is brewed for sure.
• E [(¬s) U (b)] the coffee will not be brewed as no selection were made.
• EF[b] there is a state where coffee is brewed.

The line (among others)
location = (s2 : s3 & (TRUE: selection));
doesn't make much sense. You need only one next statement to assign the next location from all possible values of location. Also, you don't need to declare coin, selection, and brewing as variables. Use DEFINE to define their values based on location:
MODULE main
VAR
location : {s1,s2,s3};
ASSIGN
init(location) := s1;
next(location) :=
case
location = s1 : s2;
location = s2 : {s1,s3};
location = s3 : {s1,s3};
esac;
DEFINE
coin := location = s2 | location = s3;
-- similarly for selection and brewing

What I understand from the model is that coin, selection and brew are not only labels but events that trigger the transition. If so, I would write the model like this:
MODULE main
VAR
location: {s1, s2, s3};
coin: boolean;
selection: boolean;
brew: boolean;
abort: boolean;
INIT
!coin & !selection & !brew;
ASSIGN
init(location) := s1;
next(location) := case
location = s1 & next(coin) : s2;
location = s2 & next(selection) : s3;
location = s2 & next(abort) : s1;
location = s3 : {s1, s3};
TRUE : location;
esac;
next(brew) := (next(location) = s3);
next(coin) := case
next(state) = s1 : FALSE;
state = s1 : {TRUE, FALSE};
TRUE : coin;
esac;
next(selection) := case
state = s2 : {TRUE, FALSE};
next(state) = s1 : FALSE;
esac;

Related

NuSMV: Initialising range constant with parameter

I'm new to NuSMV. I'm trying to define a module, where each state has a duration variable than can range from 0 to the specified bound.
MODULE state(inc, bound)
VAR
duration : 0..bound;
ASSIGN
init(duration) := 0;
next(duration) := inc ? (duration + 1) mod (bound+1) : duration ;
DEFINE limit := duration = bound;
However, this yields the syntax error: A variable is expected in left-hand-side of assignment: init(duration) := 0. I'm able to fix this by declaring duration to duration : 0..1+bound.
In my main module, I wish to calculate the total_duration (or actually calculate all possible combinations of state's duration and make sure that no combination exceeds e.i. 3 as in the SPEC) of running my model and make sure that variable does not succeed a specific limit.
Here's my main module:
MODULE main
VAR
s0 : state(TRUE, 0);
s1 : state(s0.limit, 0);
s2 : state(s1.limit, 3);
state : {s0, s1, s2};
DEFINE
max_duration := s0.bound + s1.bound + s2.bound;
VAR
total_duration : 0..max_duration;
ASSIGN
init(state) := s0;
next(state) :=
case
state = s0 : s1;
state = s1 : s2;
state = s2 : s2;
esac;
total_duration := s0.duration + s1.duration + s2.duration;
SPEC
AG (state = s2 -> AF total_duration <= 3);
My problem is: When I run the model, NuSMV keeps adding to the total_duration variable and thus fails with the message "line 39: cannot assign value 5 to variable total_duration". This is due to the declaration of duration : 0..1+bound, because, in the particular example of
s0.duration = 0, s1.duration = 0 and s2.duration = 3, it will try to add 1 + 1 + 4 to total_duration, as that is the state's bound + 1.
However, if I check the trace there's no point where total_duration exceed 3. I have checked the followed specs:
-- specification AG total_duration < 4 is true
-- specification F total_duration = 4 is false
-- specification EF total_duration >= 4 is false
How can I fix this? Either by declaring duration in another way or changing anything else?
The software does something very simple. It takes the domain of each addend, and checks whether the result variable would be able to hold the result of every possible combination of value. In this case:
the domain of s0.duration is 0..1
the domain of s1.duration is 0..1
the domain of s2.duration is 0..4
so, in principle, the maximum total_duration could be 6 and its domain should thus be 0..6. Therefore:
DEFINE
max_duration := s0.bound + s1.bound + s2.bound + 3
You may want to run NuSMV with the following option:
-keep_single_value_vars
Does not convert variables that have only one
single possible value into constant DEFINEs
In this way, you'll be able to run the model without having to add +1 to the domain of bound.

ADA - Records in a hashmap. Problems with printing the hash map

I'm new to programming and this is my first attempt at a data container with records. I'm having difficulty with printing the hashmap between line 65-70. I'm guessing I need to break down the record and print each of its attributes individually but I'm not sure on the best way to do that. The error state 'no candidates match the actuals: missing argument for parameter.
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Ada.Text_IO.Unbounded_IO; use Ada.Text_IO.Unbounded_IO;
with Ada.Strings.Hash;
with Ada.Containers.Hashed_Maps;
with Ada.Characters.Handling;
procedure Hashmap_Records is
type Guest is record
Name : Unbounded_String := Null_Unbounded_String;
Attending : Boolean := False;
Vegitarian : Boolean := False;
Kids : Natural := 0;
end record;
function Equivalent_Keys(Left : Unbounded_String;
Right : Unbounded_String)
return Boolean is
begin
return Left = Right;
end Equivalent_Keys;
function U_To_L(Key : in Unbounded_String)
return Unbounded_String is
begin
return To_Unbounded_String(Ada.Characters.Handling.To_Lower(To_String(Key)));
end U_To_L;
function Hash_Func(Key : in Unbounded_String)
return Ada.Containers.Hash_Type is
begin
return Ada.Strings.Hash(To_String(Key));
end Hash_Func;
package Guest_Tracker is new Ada.Containers.Hashed_Maps(Key_Type => Unbounded_String,
Element_Type => Guest,
Hash => Hash_Func,
Equivalent_Keys => Equivalent_Keys);
Tracked_Guests : Guest_Tracker.Map;
User_Input : Natural := 0;
Name_Input : Unbounded_String := Null_Unbounded_String;
Name : Unbounded_String := Null_Unbounded_String;
Attendance_Input : Unbounded_String := Null_Unbounded_String;
Vegitarian_Input : Unbounded_String := Null_Unbounded_String;
Kids_Input : Natural := 0;
Temp_Attendance : Boolean := False;
Temp_Vegi : Boolean := False;
Procedure Populate_Hash_Map is
begin
Tracked_Guests.Insert(Key => To_Unbounded_String("John Smith"),
New_Item => (To_Unbounded_String("John"), True, False, 1));
Tracked_Guests.Insert(Key => To_Unbounded_String("Robert Johnson"),
New_Item => (To_Unbounded_String("Rob"), True, True, 2));
end Populate_Hash_Map;
procedure Print_Hash_Map(Position : Guest_Tracker.Cursor) is ---
begin
Put_Line("The key: " & To_String(Guest_Tracker.Key(Position)) &
" the data item: ");
Put(Guest_Tracker.Element(Position)); ----THIS IS WHERE ERRORS OCCUR
end Print_Hash_Map; ----
begin
Populate_Hash_Map;
loop
Put_Line(" - Menu - ");
Put_Line(" - 1 - Enter new value.");
Put_Line(" - 2 - Delete Existing Value.");
Put_Line(" - 3 - Print entire hashmap.");
Put_Line(" - 4 - Exit Application.");
New_Line;
Put(" - > ");
declare
begin
Get(User_Input);
exception
when Data_Error =>
Put_Line("ERROR : The entered value is not an integer, please try again!");
User_Input := 0;
when others =>
Put_Line("ERROR: An unknown error has occured!");
end;
Skip_Line;
New_Line;
if User_Input = 1 then
Put_Line("Enter a new value.");
Put(" Name - > ");
Name_Input := Get_Line;
Name := Name_Input;
New_Line;
Put(" Attending? (yes/y/no/n) - > ");
Attendance_Input := Get_Line;
New_Line;
Put(" Vegitarian? (yes/y/no/n) - > ");
Vegitarian_Input := Get_Line;
New_Line;
Put("How many children attending? - > ");
Get(Kids_Input);
New_Line;
if (U_To_L(Attendance_Input) = To_Unbounded_String("no"))
or (U_To_L(Attendance_Input) = To_Unbounded_String("n")) then
Temp_Attendance := False;
elsif (U_To_L(Attendance_Input) = To_Unbounded_String("y"))
or (U_To_L(Attendance_Input) = To_Unbounded_String("yes")) then
Temp_attendance := True;
else
Put_Line("WARNING: The confirmation that you entered is not recognized.");
end if;
if (U_To_L(Vegitarian_Input) = To_Unbounded_String("no"))
or (U_To_L(Vegitarian_Input) = To_Unbounded_String("n")) then
Temp_Vegi := False;
elsif (U_To_L(Vegitarian_Input) = To_Unbounded_String("y"))
or (U_To_L(Vegitarian_Input) = To_Unbounded_String("yes")) then
Temp_Vegi := True;
else
Put_Line("WARNING: The confirmation that you entered is not recognized.");
end if;
Guest_Tracker.Insert(Container => Tracked_Guests,
Key => Name_Input,
New_item => (Name, Temp_Attendance, Temp_Vegi, Kids_Input));
elsif User_Input = 2 then
Put("Delete a value - > ");
Name_Input := Get_Line;
New_Line;
declare
begin
Guest_Tracker.Delete(Container => Tracked_Guests,
Key => Name_Input);
exception
when Constraint_Error =>
Put_Line("The name: '" & To_String(Name_Input) & "' is not found.");
when others =>
Put_Line("ERROR: Another error has been discovered!");
end;
elsif User_Input = 3 then
Tracked_Guests.Iterate(Print_Hash_Map'access);
New_Line;
elsif User_Input = 4 then
exit;
end if;
end loop;
end Hashmap_Records;
This has nothing to do with the hash map. You are assuming that there is a Put procedure that outputs your record type Guest but there is none. You only have the Put subroutines from Ada.Text_IO, Ada.Integer_Text_IO and Ada.Text_IO.Unbounded_IO available, none of which take a value of type Guest as parameter.
Ada does not automatically generate a subroutine to pretty-print a record value when you define the record type. You have to do it yourself, e.g.
procedure Put (Value : Guest) is
begin
Put ("Guest(Name: "); -- supplied by Ada.Text_IO
Put (Value.Name); -- supplied by Ada.Text_IO.Unbounded_IO
Put (", Attending: ");
Put (Value.Attending'Img); -- 'Img is GNAT-specific; standard is
-- Boolean'Image (Value.Attending)
Put (", Vegitarian: ");
Put (Value.Vegitarian'Img);
Put (", Kids: ");
Put (Value.Kids); -- supplied by Ada.Integer_Text_IO
Put (")");
end Put;

Case conditions are not exhaustive?

I am writing two modules in NuSMV but I am receiving the error, "Case conditions are not exhaustive" This error points to the last case statement I have in the code. I am not sure how to fix this because the cases that I currently have there are the only cases that the variable requires. The first module "train" is instantiated twice so that two trains can be on one track. The module "controller" acts as a controller which receives input from the two trains and prevents them both from being on a bridge at the same time.
Here is the code:
MODULE main
VAR
trainE : Train(controller1.signalE);
trainW : Train(controller1.signalW);
controller1 : controller(trainE.out, trainW.out);
INVARSPEC(!(trainE.mode = bridge & trainW.mode = bridge))
MODULE Train(signal)
VAR
mode: {away, wait, bridge};
out: {None, arrive, leave};
ASSIGN
init(mode) := away;
init(out) := None;
--Task A1
next(out) := case
mode = away: arrive;
mode = bridge: leave;
TRUE: None;
esac;
--Task A2
next(mode) := case
mode = away & next(out) = arrive: wait;
mode = bridge & next(out) = leave: away;
mode = wait & signal = green: bridge;
TRUE: mode;
esac;
MODULE controller(outE, outW)
VAR
signalE: {green, red};
signalW: {green, red};
west: {green, red};
east: {green, red};
nearE: boolean;
nearW: boolean;
ASSIGN
init(west):= red;
init(east):= red;
init(nearW):= FALSE;
init(nearE):= FALSE;
--Task A1
next(signalW):= west;
--Task A2
next(signalE):= east;
--Task A3
next(nearE):= case
outE = arrive: TRUE;
outE = leave: FALSE;
esac;
next(nearW):= case
outW = arrive: TRUE;
outW = leave: FALSE;
esac;
next(east):= case
next(nearE) = FALSE: red;
west = red: green;
esac;
next(west):= case
next(nearW) = FALSE: red;
east = red: green;
esac;
You actually have the same error in all case conditions:
file test.smv: line 68: case conditions are not exhaustive
file test.smv: line 64: case conditions are not exhaustive
file test.smv: line 60: case conditions are not exhaustive
file test.smv: line 56: case conditions are not exhaustive
Let's consider the error at line 56. You wrote the following cases:
next(nearE) := case
outE = arrive : TRUE;
outE = leave : FALSE;
esac;
Now, outE is an input connected to trainE.out. Inside module Train, out is declared as a variable that can have 3 possible values: {None, arrive, leave}. However, in your code, you specify the future value of nearE only for two possible current values of outE. Therefore, NuSMV rightfully complains because it doesn't know what value should be assigned to nearE in the next state when in the current state outE is equal to None.
Thus, in order to fix this error, you should think what you would like to happen when outE = None and add that specification to your model.
In the case in which you don't want the value of nearE to change, a common design practice is to add a catch all case condition as follows:
next(nearE) := case
outE = arrive : TRUE;
outE = leave : FALSE;
TRUE : nearE;
esac;

Ada case statement with strings

I'm trying to use a string in a case statement, however it is giving me expected a discrete type. Found type Standard.String I understand that strings are not discrete. I'm wondering if there is a work around or not. Here is my code:
function Is_Valid_Direction(Direction_To_Go : in String) return Integer is
Room : Integer := 0;
begin
--if (Direction_To_Go = "NORTH" or Direction_To_Go = "N") then
-- Room := Building(currentRoom).exits(NORTH);
--elsif (Direction_To_Go = "SOUTH" or Direction_To_Go = "S") then
-- Room := Building(currentRoom).exits(SOUTH);
--elsif (Direction_To_Go = "EAST" or Direction_To_Go = "E") then
-- Room := Building(currentRoom).exits(EAST);
--elsif (Direction_To_Go = "WEST" or Direction_To_Go = "W") then
-- Room := Building(currentRoom).exits(WEST);
--elsif (Direction_To_Go = "UP" or Direction_To_Go = "U") then
-- Room := Building(currentRoom).exits(UP);
--elsif (Direction_To_Go = "DOWN" or Direction_To_Go = "D") then
-- Room := Building(currentRoom).exits(DOWN);
--end if;
case Direction_To_Go is
when "NORTH" | "N" => Room := Building(currentRoom).exits(NORTH);
when "SOUTH" | "S" => Room := Building(currentRoom).exits(SOUTH);
when "EAST" | "E" => Room := Building(currentRoom).exits(EAST);
when "WEST" | "W" => Room := Building(currentRoom).exits(WEST);
when "UP" | "U" => Room := Building(currentRoom).exits(UP);
when "DOWN" | "D" => Room := Building(currentRoom).exits(DOWN);
when others => Room := 0;
end case;
return Room;
end Is_Valid_Direction;
The commented section is doing exactly what I want, but with if statements. I'm just trying to see if it's possible with a case statement.
You could map your strings to a discrete type. The easiest being an enumerated type:
procedure Light (Colour : in String) is
type Colours is (Red, Green, Blue);
begin
case Colours'Value (Colour) is -- ' <- magic ;-)
when Red =>
Switch_Red_LED;
when Green =>
Switch_Green_LED;
when Blue =>
Switch_Blue_LED;
end case;
exception
when Constraint_Error =>
raise Constraint_Error with "There is no " & Colour & " LED.";
end Light;
I frequently use an actual map to do this kind of mapping, as it gives you more flexibility than enumerations. Your "names" don't have to conform to enumeration syntax, and you can easily provide variations that all map to a single value.
For the desired function definition, as provided in a package:
package Case_Map is
function Is_Valid_Direction(Direction_To_Go : in String) return Integer;
end Case_Map;
This (non-compiling due to missing game-specific declarations) implementation uses a mapping of strings to an enum that is in turn the case expression:
with Ada.Characters.Handling;
with Ada.Containers.Indefinite_Ordered_Maps;
package body Case_Map is
use Ada.Characters.Handling;
type Directions is (Go_North, Go_South, Go_East, Go_West, Go_Up, Go_Down);
package Direction_Management is new Ada.Containers.Indefinite_Ordered_Maps
(String, Directions);
Direction_Map : Direction_Management.Map;
function Is_Valid_Direction(Direction_To_Go : in String) return Integer is
Room : Integer := 0;
begin
case Direction_Map(To_Upper(Direction_To_Go)) is
when Go_North => Room := Building(CurrentRoom).Exits(NORTH);
when Go_South => Room := Building(CurrentRoom).Exits(SOUTH);
when Go_East => Room := Building(CurrentRoom).Exits(EAST);
when Go_West => Room := Building(CurrentRoom).Exits(WEST);
when Go_Up => Room := Building(CurrentRoom).Exits(UP);
when Go_Down => Room := Building(CurrentRoom).Exits(DOWN);
end case;
return Room;
exception
when Constraint_Error =>
return 0;
end Is_Valid_Direction;
begin
Direction_Map.Insert("NORTH", Go_North);
Direction_Map.Insert("N", Go_North);
Direction_Map.Insert("SOUTH", Go_South);
Direction_Map.Insert("S", Go_South);
Direction_Map.Insert("EAST", Go_East);
Direction_Map.Insert("E", Go_East);
Direction_Map.Insert("WEST", Go_West);
Direction_Map.Insert("W", Go_West);
Direction_Map.Insert("UP", Go_Up);
Direction_Map.Insert("U", Go_Up);
Direction_Map.Insert("DOWN", Go_Down);
Direction_Map.Insert("D", Go_Down);
end Case_Map;
The GNAT compiler itself uses a hash table that maps strings (identifiers, keywords,...) to integer. This is the package namet.ads, and GNATCOLL.Symbolic provides a similar API. This simplifies a number of things (string comparison for instance is much faster), and allow the use of case statements as in your example. So if you are using a limited (or at least slow-growing) list of strings, GNATCOLL.Symbolic might be a suitable approach

how to create a structure of kripke in NuSMV?

i must to create a structure of Kripke in NuSMV and i must to check some properties.
Anybody help me? The structure and the properties(LTL, CTL and CTL*) are in the pictures.
Here there is a structure and properties:
http://cl.ly/image/1x0b1v3E0P0D/Screen%20Shot%202014-10-16%20at%2016.52.34.png
I found a simpler and seemingly more reliable NuSMV code for your Kripke Structure. Thanks to dejvuth for his answer to my question. The code is as follows
MODULE main
VAR
state : {s0,s1,s2,s3,s4};
ASSIGN
init(state) := s0;
next(state):=
case
state = s0 : {s1,s2};
state = s1 : {s1,s2};
state = s2 : {s1,s2,s3};
state = s3 : {s1,s4};
state = s4 : {s4};
esac;
DEFINE
p := state = s1 | state = s2 | state = s3 | state = s4;
q := state = s1 | state = s2;
r := state = s3;
SPEC
EG p;
SPEC
AG p;
SPEC
EF (AG p);
As far as I know NuSMV only handles LTL and CTL formulas (see NuSMV in Wikipedia). The formulas in problem 1-3 are CTL formulas, hence it can be model-checked by NuSMV. However the formulas in problem 4 & 5 are CTL* formulas, and thus we cannot straightforwardly use them as an input to NuSMV. You also need to understand that the set of all CTL* formulas is the proper superset of the union of all LTL and CTL formulas. This conditions implies that some CTL* formulas do not have their equivalent LTL or CTL formulas (see CTL* in Wikipedia). Your Kripke structure can be defined in NuSMV by following code:
MODULE main
VAR
p : boolean;
q : boolean;
r : boolean;
state : {s0,s1,s2,s3,s4};
ASSIGN
init (state) := s0;
next (state) :=
case
state = s0 : {s1, s2};
state = s1 : {s1, s2};
state = s2 : {s1, s2, s3};
state = s3 : {s1, s4};
state = s4 : {s4};
TRUE : state;
esac;
init (p) := FALSE;
init (q) := FALSE;
init (r) := FALSE;
next(p) :=
case
state = s1 | state = s2 | state = s3 | state = s4 : TRUE;
TRUE : p;
esac;
next(q) :=
case
state = s1 | state = s2 : TRUE;
state = s3 | state = s4 : FALSE;
TRUE : q;
esac;
next(r) :=
case
state = s3 : TRUE;
state = s1 | state = s2 | state = s4 : FALSE;
TRUE : r;
esac;
SPEC
EG p;
SPEC
AG p;
SPEC
EF (AG p);
Of course, there is another way to define your Kripke structure in NuSMV, but I think this is one of the easiest. (Anyway, thanks for helping me with my problem).
As for the formulas in problem 4 & 5, here is my answer.
The formula AF [p U EG ( p -> q)] is of the form AF [\phi], where \phi is an LTL formula p U EG (p->q). Since the LTL formula \phi is satisfied in a Kripke model if for every path starting at s0 we have the satisfaction of \phi, then we translate AF [p U EG ( p -> q)] into AF A[p U EG ( p -> q)].
By similar argument, we translate EG[(( p & q ) | r) U ( r U AG p)] into EG[A(( p & q ) | r) U A( r U AG p)].

Resources