From 3977cafe73eaa8cfe59a45822d1f6d9ef85fb991 Mon Sep 17 00:00:00 2001
From: dehnert <dehnert@cs.rwth-aachen.de>
Date: Wed, 18 Feb 2015 22:11:00 +0100
Subject: [PATCH] Extended DD-based model building to also build the MDP models
 of our benchmark suite. Added (MDP) tests for DD-based model building and
 explicit model building.

Former-commit-id: 4e18f98ee6921d4bb6a82b1c78017970e8e4f270
---
 src/builder/DdPrismModelBuilder.cpp           |  90 +++----
 .../builder/DdPrismModelBuilderTest.cpp       |   5 +
 .../builder/ExplicitPrismModelBuilderTest.cpp |   5 +
 test/functional/builder/brp-16-2.pm           | 136 +++++++++++
 test/functional/builder/crowds-5-5.pm         |  69 ++++++
 test/functional/builder/die.pm                |  31 +++
 test/functional/builder/leader-3-5.pm         |  85 +++++++
 test/functional/builder/nand-5-2.pm           |  76 ++++++
 test/functional/builder/wlan0-2-2.nm          | 219 ++++++++++++++++++
 wlan0_collide.nm                              |  13 --
 10 files changed, 652 insertions(+), 77 deletions(-)
 create mode 100644 test/functional/builder/brp-16-2.pm
 create mode 100644 test/functional/builder/crowds-5-5.pm
 create mode 100644 test/functional/builder/die.pm
 create mode 100644 test/functional/builder/leader-3-5.pm
 create mode 100644 test/functional/builder/nand-5-2.pm
 create mode 100644 test/functional/builder/wlan0-2-2.nm
 delete mode 100644 wlan0_collide.nm

diff --git a/src/builder/DdPrismModelBuilder.cpp b/src/builder/DdPrismModelBuilder.cpp
index 43f051eb5..ef165a877 100644
--- a/src/builder/DdPrismModelBuilder.cpp
+++ b/src/builder/DdPrismModelBuilder.cpp
@@ -142,7 +142,6 @@ namespace storm {
                     commandDd += updateDd;
                 }
                 
-                (guardDd * commandDd).exportToDot(module.getName() + "_" + command.getActionName() + ".dot");
                 return ActionDecisionDiagram(guardDd, guardDd * commandDd);
             } else {
                 return ActionDecisionDiagram(*generationInfo.manager);
@@ -161,22 +160,10 @@ namespace storm {
                     continue;
                 }
                 
-                if (synchronizationActionIndex) {
-                    std::cout << command << " is relevant for synch " << synchronizationActionIndex.get() << std::endl;
-                }
-                
                 // At this point, the command is known to be relevant for the action.
                 commandDds.push_back(createCommandDecisionDiagram(generationInfo, module, command));
             }
             
-            if (synchronizationActionIndex && synchronizationActionIndex.get() == 0) {
-                int i = 0;
-                for (auto const& commandDd : commandDds) {
-                    commandDd.transitionsDd.exportToDot("cmd_" + std::to_string(i) + ".dot");
-                    ++i;
-                }
-            }
-            
             ActionDecisionDiagram result(*generationInfo.manager);
             if (!commandDds.empty()) {
                 switch (generationInfo.program.getModelType()){
@@ -214,14 +201,15 @@ namespace storm {
         
         template <storm::dd::DdType Type>
         storm::dd::Dd<Type> DdPrismModelBuilder<Type>::encodeChoice(GenerationInformation& generationInfo, uint_fast64_t nondeterminismVariableOffset, uint_fast64_t numberOfBinaryVariables, int_fast64_t value) {
-            storm::dd::Dd<Type> result = generationInfo.manager->getOne();
+            storm::dd::Dd<Type> result = generationInfo.manager->getZero();
+            
+            STORM_LOG_TRACE("Encoding " << value << " with " << numberOfBinaryVariables << " binary variable(s) starting from offset " << nondeterminismVariableOffset << ".");
             
             std::map<storm::expressions::Variable, int_fast64_t> metaVariableNameToValueMap;
             for (uint_fast64_t i = nondeterminismVariableOffset; i < nondeterminismVariableOffset + numberOfBinaryVariables; ++i) {
                 if (value & (1ull << (numberOfBinaryVariables - i - 1))) {
                     metaVariableNameToValueMap.emplace(generationInfo.nondeterminismMetaVariables[i], 1);
-                }
-                else {
+                } else {
                     metaVariableNameToValueMap.emplace(generationInfo.nondeterminismMetaVariables[i], 0);
                 }
             }
@@ -243,6 +231,8 @@ namespace storm {
             }
             uint_fast64_t maxChoices = static_cast<uint_fast64_t>(sumOfGuards.getMax());
 
+            STORM_LOG_TRACE("Found " << maxChoices << " local choices.");
+            
             // Depending on the maximal number of nondeterminstic choices, we need to use some variables to encode the nondeterminism.
             if (maxChoices == 0) {
                 return ActionDecisionDiagram(*generationInfo.manager);
@@ -291,7 +281,7 @@ namespace storm {
                             // Calculate the overlapping part of command guard and the remaining DD.
                             storm::dd::Dd<Type> remainingGuardChoicesIntersection = guardChoicesIntersection && remainingDds[k];
                             
-                            // Check if we can add some overlapping parts to the current index
+                            // Check if we can add some overlapping parts to the current index.
                             if (!remainingGuardChoicesIntersection.isZero()) {
                                 // Remove overlapping parts from the remaining DD.
                                 remainingDds[k] = remainingDds[k] && !remainingGuardChoicesIntersection;
@@ -333,9 +323,6 @@ namespace storm {
             storm::dd::Dd<Type> action1Extended = action1.transitionsDd * identityDd2;
             storm::dd::Dd<Type> action2Extended = action2.transitionsDd * identityDd1;
 
-            action1.transitionsDd.exportToDot("act1.dot");
-            action2.transitionsDd.exportToDot("act2.dot");
-            
             if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::DTMC) {
                 return ActionDecisionDiagram(action1.guardDd + action2.guardDd, action1Extended + action2Extended, 0);
             } else if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::MDP) {
@@ -347,7 +334,6 @@ namespace storm {
                 
                 // Bring both choices to the same number of variables that encode the nondeterminism.
                 uint_fast64_t numberOfUsedNondeterminismVariables = std::max(action1.numberOfUsedNondeterminismVariables, action2.numberOfUsedNondeterminismVariables);
-                std::cout << "max used nondet: " << numberOfUsedNondeterminismVariables << std::endl;
                 if (action1.numberOfUsedNondeterminismVariables > action2.numberOfUsedNondeterminismVariables) {
                     storm::dd::Dd<Type> nondeterminisimEncoding = generationInfo.manager->getOne();
                     
@@ -364,9 +350,6 @@ namespace storm {
                     action1Extended *= nondeterminisimEncoding;
                 }
                 
-                action1Extended.exportToDot("act1ext.dot");
-                action2Extended.exportToDot("act2ext.dot");
-                
                 // Add a new variable that resolves the nondeterminism between the two choices.
                 storm::dd::Dd<Type> combinedTransitions = generationInfo.manager->getEncoding(generationInfo.nondeterminismMetaVariables[numberOfUsedNondeterminismVariables], 1).ite(action2Extended, action1Extended);
                 
@@ -403,12 +386,10 @@ namespace storm {
                 // First, determine the highest number of nondeterminism variables that is used in any action and make
                 // all actions use the same amout of nondeterminism variables.
                 uint_fast64_t numberOfUsedNondeterminismVariables = module.numberOfUsedNondeterminismVariables;
-                std::cout << "pumping number of used nondet variables to " << numberOfUsedNondeterminismVariables << std::endl;
 
                 // Add variables to independent action DD.
                 storm::dd::Dd<Type> nondeterminismEncoding = generationInfo.manager->getOne();
                 for (uint_fast64_t i = module.independentAction.numberOfUsedNondeterminismVariables; i < numberOfUsedNondeterminismVariables; ++i) {
-                    std::cout << "adding " << i << " to independent" << std::endl;
                     nondeterminismEncoding *= generationInfo.manager->getEncoding(generationInfo.nondeterminismMetaVariables[i], 0);
                 }
                 result = module.independentAction.transitionsDd * nondeterminismEncoding;
@@ -446,7 +427,6 @@ namespace storm {
                 
                 // Now, we can simply add all synchronizing actions to the result.
                 for (auto const& synchronizingAction : synchronizingActionToDdMap) {
-                    synchronizingAction.second.exportToDot("synch" + std::to_string(synchronizingAction.first) + ".dot");
                     result += synchronizingAction.second;
                 }
 
@@ -516,7 +496,6 @@ namespace storm {
                 system.identity = system.identity * nextModule.identity;
                 
                 // Keep track of the number of nondeterminism variables used.
-                std::cout << "num used: " << numberOfUsedNondeterminismVariables << std::endl;
                 system.numberOfUsedNondeterminismVariables = std::max(system.numberOfUsedNondeterminismVariables, numberOfUsedNondeterminismVariables);
             }
             
@@ -528,9 +507,7 @@ namespace storm {
             } else if (generationInfo.program.getModelType() == storm::prism::Program::ModelType::MDP) {
                 // For MDPs, we need to throw away the nondeterminism variables from the generation information that
                 // were never used.
-                std::cout << "System uses " << system.numberOfUsedNondeterminismVariables << "nd vars" << std::endl;
                 for (uint_fast64_t index = system.numberOfUsedNondeterminismVariables; index < generationInfo.nondeterminismMetaVariables.size(); ++index) {
-                    std::cout << "removing " << generationInfo.nondeterminismMetaVariables[index].getName() << std::endl;
                     generationInfo.allNondeterminismVariables.erase(generationInfo.nondeterminismMetaVariables[index]);
                 }
                 generationInfo.nondeterminismMetaVariables.resize(system.numberOfUsedNondeterminismVariables);
@@ -566,7 +543,7 @@ namespace storm {
             }
             
             preparedProgram = preparedProgram.substituteConstants();
-            std::cout << "translated prog: " << preparedProgram << std::endl;
+//            std::cout << "translated prog: " << preparedProgram << std::endl;
             
             // Start by initializing the structure used for storing all information needed during the model generation.
             // In particular, this creates the meta variables used to encode the model.
@@ -575,51 +552,38 @@ namespace storm {
             auto clock = std::chrono::high_resolution_clock::now();
             std::pair<storm::dd::Dd<Type>, ModuleDecisionDiagram> transitionMatrixModulePair = createSystemDecisionDiagram(generationInfo);
             storm::dd::Dd<Type> transitionMatrix = transitionMatrixModulePair.first;
-            transitionMatrix.exportToDot("trans.dot");
             ModuleDecisionDiagram const& globalModule = transitionMatrixModulePair.second;
             
             // Cut the transition matrix to the reachable fragment of the state space.
             storm::dd::Dd<Type> reachableStates = computeReachableStates(generationInfo, createInitialStatesDecisionDiagram(generationInfo), transitionMatrix);
             transitionMatrix *= reachableStates;
-            reachableStates.exportToDot("reach.dot");
 
             // Detect deadlocks and 1) fix them if requested 2) throw an error otherwise.
             storm::dd::Dd<Type> statesWithTransition = transitionMatrix.notZero();
             if (program.getModelType() == storm::prism::Program::ModelType::MDP) {
                 statesWithTransition = statesWithTransition.existsAbstract(generationInfo.allNondeterminismVariables);
-                statesWithTransition.exportToDot("after_exists.dot");
             }
             statesWithTransition = statesWithTransition.existsAbstract(generationInfo.columnMetaVariables);
             storm::dd::Dd<Type> deadlockStates = reachableStates * !statesWithTransition;
-            deadlockStates.exportToDot("deadlocks.dot");
 
-//            if (!deadlockStates.isZero()) {
-//                // If we need to fix deadlocks, we do so now.
-//                if (!storm::settings::generalSettings().isDontFixDeadlocksSet()) {
-//                    std::cout << "fixing " << deadlockStates.getNonZeroCount() << std::endl;
-//                    STORM_LOG_WARN("Fixing deadlocks in " << deadlockStates.getNonZeroCount() << " states.");
-//
-//                    if (program.getModelType() == storm::prism::Program::ModelType::DTMC) {
-//                        // For DTMCs, we can simply add the identity of the global module for all deadlock states.
-//                        transitionMatrix += deadlockStates * globalModule.identity;
-//                    } else if (program.getModelType() == storm::prism::Program::ModelType::MDP) {
-//                        // For MDPs, however, we need to select an action associated with the self-loop, if we do not
-//                        // want to attach a lot of self-loops to the deadlock states.
-//                        storm::dd::Dd<Type> action = generationInfo.manager->getOne();
-//                        std::for_each(generationInfo.allNondeterminismVariables.begin(), generationInfo.allNondeterminismVariables.end(), [&action,&generationInfo] (storm::expressions::Variable const& metaVariable) { action *= !generationInfo.manager->getIdentity(metaVariable); } );
-//                        transitionMatrix += deadlockStates * globalModule.identity * action;
-//                        (deadlockStates * globalModule.identity * action).exportToDot("selfloops.dot");
-//                    }
-//                } else {
-//                    STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "The model contains " << deadlockStates.getNonZeroCount() << " deadlock states. Please unset the option to not fix deadlocks, if you want to fix them automatically.");
-//                }
-//            } else {
-//                std::cout << "no deadlocks" << std::endl;
-//            }
-            
-            transitionMatrix.exportToDot("trans_reach.dot");
-            for (auto const& var : transitionMatrix.getContainedMetaVariables()) {
-                std::cout << "var: " << var.getName() << std::endl;
+            if (!deadlockStates.isZero()) {
+                // If we need to fix deadlocks, we do so now.
+                if (!storm::settings::generalSettings().isDontFixDeadlocksSet()) {
+                    STORM_LOG_WARN("Fixing deadlocks in " << deadlockStates.getNonZeroCount() << " states.");
+
+                    if (program.getModelType() == storm::prism::Program::ModelType::DTMC) {
+                        // For DTMCs, we can simply add the identity of the global module for all deadlock states.
+                        transitionMatrix += deadlockStates * globalModule.identity;
+                    } else if (program.getModelType() == storm::prism::Program::ModelType::MDP) {
+                        // For MDPs, however, we need to select an action associated with the self-loop, if we do not
+                        // want to attach a lot of self-loops to the deadlock states.
+                        storm::dd::Dd<Type> action = generationInfo.manager->getOne();
+                        std::for_each(generationInfo.allNondeterminismVariables.begin(), generationInfo.allNondeterminismVariables.end(), [&action,&generationInfo] (storm::expressions::Variable const& metaVariable) { action *= !generationInfo.manager->getIdentity(metaVariable); } );
+                        transitionMatrix += deadlockStates * globalModule.identity * action;
+                    }
+                } else {
+                    STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "The model contains " << deadlockStates.getNonZeroCount() << " deadlock states. Please unset the option to not fix deadlocks, if you want to fix them automatically.");
+                }
             }
             
             std::cout << reachableStates.getNonZeroCount() << " states and " << transitionMatrix.getNonZeroCount() << " transitions." << std::endl;
@@ -647,8 +611,6 @@ namespace storm {
                 transitionBdd = transitionBdd.existsAbstract(generationInfo.allNondeterminismVariables);
             }
             
-            transitionBdd.exportToDot("trans01.dot");
-            
             // Perform the BFS to discover all reachable states.
             bool changed = true;
             uint_fast64_t iteration = 0;
diff --git a/test/functional/builder/DdPrismModelBuilderTest.cpp b/test/functional/builder/DdPrismModelBuilderTest.cpp
index c81e46103..67ae4d1ac 100644
--- a/test/functional/builder/DdPrismModelBuilderTest.cpp
+++ b/test/functional/builder/DdPrismModelBuilderTest.cpp
@@ -60,4 +60,9 @@ TEST(DdPrismModelBuilderTest, Mdp) {
     model = storm::builder::DdPrismModelBuilder<storm::dd::DdType::CUDD>::translateProgram(program);
     EXPECT_EQ(4093, model.first.getNonZeroCount());
     EXPECT_EQ(5585, model.second.getNonZeroCount());
+
+    program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/wlan0-2-2.nm");
+    model = storm::builder::DdPrismModelBuilder<storm::dd::DdType::CUDD>::translateProgram(program);
+    EXPECT_EQ(37, model.first.getNonZeroCount());
+    EXPECT_EQ(59, model.second.getNonZeroCount());
 }
\ No newline at end of file
diff --git a/test/functional/builder/ExplicitPrismModelBuilderTest.cpp b/test/functional/builder/ExplicitPrismModelBuilderTest.cpp
index b2861f33d..88996a549 100644
--- a/test/functional/builder/ExplicitPrismModelBuilderTest.cpp
+++ b/test/functional/builder/ExplicitPrismModelBuilderTest.cpp
@@ -58,4 +58,9 @@ TEST(ExplicitPrismModelBuilderTest, Mdp) {
     model = storm::builder::ExplicitPrismModelBuilder<double>::translateProgram(program);
     EXPECT_EQ(4093, model->getNumberOfStates());
     EXPECT_EQ(5585, model->getNumberOfTransitions());
+    
+    program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/wlan0-2-2.nm");
+    model = storm::builder::ExplicitPrismModelBuilder<double>::translateProgram(program);
+    EXPECT_EQ(37, model->getNumberOfStates());
+    EXPECT_EQ(59, model->getNumberOfTransitions());
 }
\ No newline at end of file
diff --git a/test/functional/builder/brp-16-2.pm b/test/functional/builder/brp-16-2.pm
new file mode 100644
index 000000000..e3c30cb98
--- /dev/null
+++ b/test/functional/builder/brp-16-2.pm
@@ -0,0 +1,136 @@
+// bounded retransmission protocol [D'AJJL01]
+// gxn/dxp 23/05/2001
+
+dtmc
+
+// number of chunks
+const int N = 16;
+// maximum number of retransmissions
+const int MAX = 2;
+
+module sender
+
+	s : [0..6];
+	// 0 idle
+	// 1 next_frame	
+	// 2 wait_ack
+	// 3 retransmit
+	// 4 success
+	// 5 error
+	// 6 wait sync
+	srep : [0..3];
+	// 0 bottom
+	// 1 not ok (nok)
+	// 2 do not know (dk)
+	// 3 ok (ok)
+	nrtr : [0..MAX];
+	i : [0..N];
+	bs : bool;
+	s_ab : bool;
+	fs : bool;
+	ls : bool;
+	
+	// idle
+	[NewFile] (s=0) -> (s'=1) & (i'=1) & (srep'=0);
+	// next_frame
+	[aF] (s=1) -> (s'=2) & (fs'=(i=1)) & (ls'=(i=N)) & (bs'=s_ab) & (nrtr'=0);
+	// wait_ack
+	[aB] (s=2) -> (s'=4) & (s_ab'=!s_ab);
+	[TO_Msg] (s=2) -> (s'=3);
+	[TO_Ack] (s=2) -> (s'=3);
+	// retransmit
+	[aF] (s=3) & (nrtr<MAX) -> (s'=2) & (fs'=(i=1)) & (ls'=(i=N)) & (bs'=s_ab) & (nrtr'=nrtr+1);
+	[] (s=3) & (nrtr=MAX) & (i<N) -> (s'=5) & (srep'=1);
+	[] (s=3) & (nrtr=MAX) & (i=N) -> (s'=5) & (srep'=2);
+	// success
+	[] (s=4) & (i<N) -> (s'=1) & (i'=i+1);
+	[] (s=4) & (i=N) -> (s'=0) & (srep'=3);
+	// error
+	[SyncWait] (s=5) -> (s'=6); 
+	// wait sync
+	[SyncWait] (s=6) -> (s'=0) & (s_ab'=false); 
+
+endmodule
+
+module receiver
+
+	r : [0..5];
+	// 0 new_file
+	// 1 fst_safe
+	// 2 frame_received
+	// 3 frame_reported
+	// 4 idle
+	// 5 resync
+	rrep : [0..4];
+	// 0 bottom
+	// 1 fst
+	// 2 inc
+	// 3 ok
+	// 4 nok
+	fr : bool;
+	lr : bool;
+	br : bool;
+	r_ab : bool;
+	recv : bool;
+	
+	
+	// new_file
+	[SyncWait] (r=0) -> (r'=0);
+	[aG] (r=0) -> (r'=1) & (fr'=fs) & (lr'=ls) & (br'=bs) & (recv'=T);
+	// fst_safe_frame
+	[] (r=1) -> (r'=2) & (r_ab'=br);
+	// frame_received
+	[] (r=2) & (r_ab=br) & (fr=true) & (lr=false)  -> (r'=3) & (rrep'=1);
+	[] (r=2) & (r_ab=br) & (fr=false) & (lr=false) -> (r'=3) & (rrep'=2);
+	[] (r=2) & (r_ab=br) & (fr=false) & (lr=true)  -> (r'=3) & (rrep'=3);
+	[aA] (r=2) & !(r_ab=br) -> (r'=4);  
+	// frame_reported
+	[aA] (r=3) -> (r'=4) & (r_ab'=!r_ab);
+	// idle
+	[aG] (r=4) -> (r'=2) & (fr'=fs) & (lr'=ls) & (br'=bs) & (recv'=T);
+	[SyncWait] (r=4) & (ls=true) -> (r'=5);
+	[SyncWait] (r=4) & (ls=false) -> (r'=5) & (rrep'=4);
+	// resync
+	[SyncWait] (r=5) -> (r'=0) & (rrep'=0);
+	
+endmodule
+	
+module checker // prevents more than one frame being set
+
+	T : bool;
+	
+	[NewFile] (T=false) -> (T'=true);
+	
+endmodule
+
+module	channelK
+
+	k : [0..2];
+	
+	// idle
+	[aF] (k=0) -> 0.98 : (k'=1) + 0.02 : (k'=2);
+	// sending
+	[aG] (k=1) -> (k'=0);
+	// lost
+	[TO_Msg] (k=2) -> (k'=0);
+	
+endmodule
+
+module	channelL
+
+	l : [0..2];
+	
+	// idle
+	[aA] (l=0) -> 0.99 : (l'=1) + 0.01 : (l'=2);
+	// sending
+	[aB] (l=1) -> (l'=0);
+	// lost
+	[TO_Ack] (l=2) -> (l'=0);
+	
+endmodule
+
+rewards
+	[aF] i=1 : 1;
+endrewards
+
+label "target" = s=5;
diff --git a/test/functional/builder/crowds-5-5.pm b/test/functional/builder/crowds-5-5.pm
new file mode 100644
index 000000000..60bdaa7ea
--- /dev/null
+++ b/test/functional/builder/crowds-5-5.pm
@@ -0,0 +1,69 @@
+dtmc
+
+// probability of forwarding
+const double    PF = 0.8;
+const double notPF = .2;  // must be 1-PF
+// probability that a crowd member is bad
+const double  badC = .167;
+ // probability that a crowd member is good
+const double goodC = 0.833;
+// Total number of protocol runs to analyze
+const int TotalRuns = 5;
+// size of the crowd
+const int CrowdSize = 5;
+
+module crowds
+	// protocol phase
+	phase: [0..4] init 0;
+
+	// crowd member good (or bad)
+	good: bool init false;
+
+	// number of protocol runs
+	runCount: [0..TotalRuns] init 0;
+
+	// observe_i is the number of times the attacker observed crowd member i
+	observe0: [0..TotalRuns] init 0;
+
+	observe1: [0..TotalRuns] init 0;
+
+	observe2: [0..TotalRuns] init 0;
+
+	observe3: [0..TotalRuns] init 0;
+
+	observe4: [0..TotalRuns] init 0;
+
+	// the last seen crowd member
+	lastSeen: [0..CrowdSize - 1] init 0;
+
+	// get the protocol started
+	[] phase=0 & runCount<TotalRuns -> 1: (phase'=1) & (runCount'=runCount+1) & (lastSeen'=0);
+
+	// decide whether crowd member is good or bad according to given probabilities
+	[] phase=1 -> goodC : (phase'=2) & (good'=true) + badC : (phase'=2) & (good'=false);
+
+	// if the current member is a good member, update the last seen index (chosen uniformly)
+	[] phase=2 & good -> 1/5 : (lastSeen'=0) & (phase'=3) + 1/5 : (lastSeen'=1) & (phase'=3) + 1/5 : (lastSeen'=2) & (phase'=3) + 1/5 : (lastSeen'=3) & (phase'=3) + 1/5 : (lastSeen'=4) & (phase'=3);
+
+	// if the current member is a bad member, record the most recently seen index
+	[] phase=2 & !good & lastSeen=0 & observe0 < TotalRuns -> 1: (observe0'=observe0+1) & (phase'=4);
+	[] phase=2 & !good & lastSeen=1 & observe1 < TotalRuns -> 1: (observe1'=observe1+1) & (phase'=4);
+	[] phase=2 & !good & lastSeen=2 & observe2 < TotalRuns -> 1: (observe2'=observe2+1) & (phase'=4);
+	[] phase=2 & !good & lastSeen=3 & observe3 < TotalRuns -> 1: (observe3'=observe3+1) & (phase'=4);
+	[] phase=2 & !good & lastSeen=4 & observe4 < TotalRuns -> 1: (observe4'=observe4+1) & (phase'=4);
+
+	// good crowd members forward with probability PF and deliver otherwise
+	[] phase=3 -> PF : (phase'=1) + notPF : (phase'=4);
+
+	// deliver the message and start over
+	[] phase=4 -> 1: (phase'=0);
+
+endmodule
+
+label "observe0Greater1" = observe0>1;
+label "observe1Greater1" = observe1>1;
+label "observe2Greater1" = observe2>1;
+label "observe3Greater1" = observe3>1;
+label "observe4Greater1" = observe4>1;
+label "observeIGreater1" = observe1>1|observe2>1|observe3>1|observe4>1;
+label "observeOnlyTrueSender" = observe0>1&observe1<=1 & observe2<=1 & observe3<=1 & observe4<=1;
diff --git a/test/functional/builder/die.pm b/test/functional/builder/die.pm
new file mode 100644
index 000000000..dacde8a79
--- /dev/null
+++ b/test/functional/builder/die.pm
@@ -0,0 +1,31 @@
+// Knuth's model of a fair die using only fair coins
+dtmc
+
+module die
+
+	// local state
+	s : [0..7] init 0;
+	// value of the dice
+	d : [0..6] init 0;
+	
+	[] s=0 -> 0.5 : (s'=1) + 0.5 : (s'=2);
+	[] s=1 -> 0.5 : (s'=3) + 0.5 : (s'=4);
+	[] s=2 -> 0.5 : (s'=5) + 0.5 : (s'=6);
+	[] s=3 -> 0.5 : (s'=1) + 0.5 : (s'=7) & (d'=1);
+	[] s=4 -> 0.5 : (s'=7) & (d'=2) + 0.5 : (s'=7) & (d'=3);
+	[] s=5 -> 0.5 : (s'=7) & (d'=4) + 0.5 : (s'=7) & (d'=5);
+	[] s=6 -> 0.5 : (s'=2) + 0.5 : (s'=7) & (d'=6);
+	[] s=7 -> 1: (s'=7);
+	
+endmodule
+
+rewards "coin_flips"
+	[] s<7 : 1;
+endrewards
+
+label "one" = s=7&d=1;
+label "two" = s=7&d=2;
+label "three" = s=7&d=3;
+label "four" = s=7&d=4;
+label "five" = s=7&d=5;
+label "six" = s=7&d=6;
diff --git a/test/functional/builder/leader-3-5.pm b/test/functional/builder/leader-3-5.pm
new file mode 100644
index 000000000..0703e733d
--- /dev/null
+++ b/test/functional/builder/leader-3-5.pm
@@ -0,0 +1,85 @@
+// synchronous leader election protocol  (itai & Rodeh)
+// dxp/gxn 25/01/01
+
+dtmc
+
+// CONSTANTS
+const int N = 3; // number of processes
+const int K = 5; // range of probabilistic choice
+
+// counter module used to count the number of processes that have been read
+// and to know when a process has decided
+module counter
+	
+	// counter (c=i  means process j reading process (i-1)+j next)
+	c : [1..N-1];
+	
+	// reading
+	[read] c<N-1 -> 1:(c'=c+1);
+	// finished reading
+	[read] c=N-1 -> 1:(c'=c);
+	//decide
+	[done] u1|u2|u3 -> 1:(c'=c);
+	// pick again reset counter 
+	[retry] !(u1|u2|u3) -> 1:(c'=1);
+	// loop (when finished to avoid deadlocks)
+	[loop] s1=3 -> 1:(c'=c);
+	
+endmodule
+
+//  processes form a ring and suppose:
+// process 1 reads process 2
+// process 2 reads process 3
+// process 3 reads process 1
+module process1
+	
+	// local state
+	s1 : [0..3];
+	// s1=0 make random choice
+	// s1=1 reading
+	// s1=2 deciding
+	// s1=3 finished
+	
+	// has a unique id so far (initially true)
+	u1 : bool;
+	
+	// value to be sent to next process in the ring (initially sets this to its own value)
+	v1 : [0..K-1];
+	
+	// random choice
+	p1 : [0..K-1];
+	
+	// pick value
+	[pick] s1=0 -> 1/K : (s1'=1) & (p1'=0) & (v1'=0) & (u1'=true)
+	             + 1/K : (s1'=1) & (p1'=1) & (v1'=1) & (u1'=true)
+	             + 1/K : (s1'=1) & (p1'=2) & (v1'=2) & (u1'=true)
+	             + 1/K : (s1'=1) & (p1'=3) & (v1'=3) & (u1'=true)
+	             + 1/K : (s1'=1) & (p1'=4) & (v1'=4) & (u1'=true);
+	// read
+	[read] s1=1 &  u1 & c<N-1 -> 1:(u1'=(p1!=v2)) & (v1'=v2);
+	[read] s1=1 & !u1 & c<N-1 -> 1:(u1'=false) & (v1'=v2) & (p1'=0);
+	// read and move to decide
+	[read] s1=1 &  u1 & c=N-1 -> 1:(s1'=2) & (u1'=(p1!=v2)) & (v1'=0) & (p1'=0);
+	[read] s1=1 & !u1 & c=N-1 -> 1:(s1'=2) & (u1'=false) & (v1'=0);
+	// deciding
+	// done
+	[done] s1=2 -> 1:(s1'=3) & (u1'=false) & (v1'=0) & (p1'=0);
+	//retry
+	[retry] s1=2 -> 1:(s1'=0) & (u1'=false) & (v1'=0) & (p1'=0);
+	// loop (when finished to avoid deadlocks)
+	[loop] s1=3 -> 1:(s1'=3);
+	
+endmodule
+
+// construct remaining processes through renaming
+module process2 = process1 [ s1=s2,p1=p2,v1=v2,u1=u2,v2=v3 ] endmodule
+module process3 = process1 [ s1=s3,p1=p3,v1=v3,u1=u3,v2=v1 ] endmodule
+
+// expected number of rounds
+rewards "num_rounds"
+	[pick] true : 1;
+endrewards
+
+// labels
+label "elected" = s1=3&s2=3&s3=3;
+
diff --git a/test/functional/builder/nand-5-2.pm b/test/functional/builder/nand-5-2.pm
new file mode 100644
index 000000000..e3f2f4dee
--- /dev/null
+++ b/test/functional/builder/nand-5-2.pm
@@ -0,0 +1,76 @@
+// nand multiplex system
+// gxn/dxp 20/03/03
+
+// U (correctly) performs a random permutation of the outputs of the previous stage
+
+dtmc
+
+const int N = 5; // number of inputs in each bundle
+const int K = 2; // number of restorative stages
+
+const int M = 2*K+1; // total number of multiplexing units
+
+// parameters taken from the following paper
+// A system architecture solution for unreliable nanoelectric devices
+// J. Han & P. Jonker
+// IEEEE trans. on nanotechnology vol 1(4) 2002
+
+const double perr = 0.02; // probability nand works correctly
+const double prob1 = 0.9; // probability initial inputs are stimulated
+
+// model whole system as a single module by resuing variables 
+// to decrease the state space
+module multiplex
+
+	u : [1..M]; // number of stages
+	c : [0..N]; // counter (number of copies of the nand done)
+
+	s : [0..4]; // local state
+	// 0 - initial state
+	// 1 - set x inputs
+	// 2 - set y inputs
+	// 3 - set outputs
+	// 4 - done
+
+	z : [0..N]; // number of new outputs equal to 1
+	zx : [0..N]; // number of old outputs equal to 1
+	zy : [0..N]; // need second copy for y
+	// initially 9 since initially probability of stimulated state is 0.9
+
+	x : [0..1]; // value of first input
+	y : [0..1]; // value of second input
+	
+	[] s=0 & (c<N) -> (s'=1); // do next nand if have not done N yet
+	[] s=0 & (c=N) & (u<M) -> (s'=1) & (zx'=z) & (zy'=z) & (z'=0) & (u'=u+1) & (c'=0); // move on to next u if not finished
+	[] s=0 & (c=N) & (u=M) -> (s'=4) & (zx'=0) & (zy'=0) & (x'=0) & (y'=0); // finished (so reset variables not needed to reduce state space)
+
+	// choose x permute selection (have zx stimulated inputs)
+	// note only need y to be random	
+	[] s=1 & u=1  -> prob1 : (x'=1) & (s'=2) + (1-prob1) : (x'=0) & (s'=2); // initially random
+	[] s=1 & u>1 & zx>0 -> (x'=1) & (s'=2) & (zx'=zx-1);
+	[] s=1 & u>1 & zx=0 -> (x'=0) & (s'=2);
+
+	// choose x randomly from selection (have zy stimulated inputs)
+	[] s=2 & u=1 -> prob1 : (y'=1) & (s'=3) + (1-prob1) : (y'=0) & (s'=3); // initially random
+	[] s=2 & u>1 & zy<(N-c) & zy>0  -> zy/(N-c) : (y'=1) & (s'=3) & (zy'=zy-1) + 1-(zy/(N-c)) : (y'=0) & (s'=3);
+	[] s=2 & u>1 & zy=(N-c) & c<N -> 1 : (y'=1) & (s'=3) & (zy'=zy-1);
+	[] s=2 & u>1 & zy=0 -> 1 : (y'=0) & (s'=3);
+
+	// use nand gate
+	[] s=3 & z<N & c<N -> (1-perr) : (z'=z+(1-x*y)) & (s'=0) & (c'=c+1) & (x'=0) & (y'=0) // not faulty
+	         + perr    : (z'=z+(x*y))    & (s'=0) & (c'=c+1) & (x'=0) & (y'=0); // von neumann fault
+	// [] s=3 & z<N -> (1-perr) : (z'=z+(1-x*y)) & (s'=0) & (c'=c+1) & (x'=0) & (y'=0) // not faulty
+	//         + perr    : (z'=z+(x*y))    & (s'=0) & (c'=c+1) & (x'=0) & (y'=0); // von neumann fault
+	
+	[] s=4 -> (s'=s);
+	
+endmodule
+
+// rewards: final value of gate
+rewards
+	// [] s=0 & (c=N) & (u=M) : z/N;
+	s=0 & (c=N) & (u=M) : z/N;
+endrewards
+
+label "target" = s=4 & z/N<0.1;
+label "end" = s=4;
diff --git a/test/functional/builder/wlan0-2-2.nm b/test/functional/builder/wlan0-2-2.nm
new file mode 100644
index 000000000..a8ae9df7e
--- /dev/null
+++ b/test/functional/builder/wlan0-2-2.nm
@@ -0,0 +1,219 @@
+// WLAN PROTOCOL (two stations)
+// discrete time model
+// gxn/jzs 20/02/02
+
+mdp
+
+// COLLISIONS
+const int COL = 2; // maximum number of collisions
+
+// TIMING CONSTRAINTS
+// we have used the FHSS parameters
+// then scaled by the value of ASLOTTIME
+const int ASLOTTIME = 1;
+const int DIFS = 3; // due to scaling can be either 2 or 3 which is modelled by a non-deterministic choice
+const int VULN = 1; // due to scaling can be either 0 or 1 which is modelled by a non-deterministic choice
+const int TRANS_TIME_MAX = 2; // scaling up
+const int TRANS_TIME_MIN = 4; // scaling down
+const int ACK_TO = 6; 
+const int ACK = 4; // due to scaling can be either 3 or 4 which is modelled by a non-deterministic choice
+const int SIFS = 1; // due to scaling can be either 0 or 1 which is modelled by a non-deterministic choice
+// maximum constant used in timing constraints + 1
+const int TIME_MAX = max(ACK_TO,TRANS_TIME_MAX)+1;
+
+// CONTENTION WINDOW
+// CWMIN =15 & CWMAX =16
+// this means that MAX_BACKOFF IS 2
+const int MAX_BACKOFF = 0;
+
+//-----------------------------------------------------------------//
+// THE MEDIUM/CHANNEL
+
+// FORMULAE FOR THE CHANNEL
+// channel is busy
+formula busy = c1>0 | c2>0;
+// channel is free
+formula free = c1=0 & c2=0;
+
+module medium
+	
+	// number of collisions
+	col : [0..COL];
+	
+	// medium status 
+	c1 : [0..2];
+	c2 : [0..2];
+	// ci corresponds to messages associated with station i
+	// 0 nothing being sent
+	// 1 being sent correctly
+	// 2 being sent garbled	  
+	
+	// begin sending message and nothing else currently being sent
+	[send1] c1=0 & c2=0 -> (c1'=1);
+	[send2] c2=0 & c1=0 -> (c2'=1);
+	
+	// begin sending message and  something is already being sent
+	// in this case both messages become garbled
+	[send1] c1=0 & c2>0 -> (c1'=2) & (c2'=2) & (col'=min(col+1,COL));
+	[send2] c2=0 & c1>0 -> (c1'=2) & (c2'=2) & (col'=min(col+1,COL));
+	
+	// finish sending message
+	[finish1] c1>0 -> (c1'=0);
+	[finish2] c2>0 -> (c2'=0);
+
+endmodule
+
+//-----------------------------------------------------------------//
+// STATION 1
+module station1
+	// clock for station 1
+	x1 : [0..TIME_MAX];
+	
+	// local state
+	s1 : [1..12];
+	// 1 sense
+	// 2 wait until free before setting backoff
+	// 3 wait for DIFS then set slot
+	// 4 set backoff 
+	// 5 backoff
+	// 6 wait until free in backoff
+	// 7 wait for DIFS then resume backoff
+	// 8 vulnerable 
+	// 9 transmit
+	// 11 wait for SIFS and then ACK
+	// 10 wait for ACT_TO 
+	// 12 done
+	// BACKOFF
+	// separate into slots
+	slot1 : [0..1]; 
+	backoff1 : [0..15];
+	
+	// BACKOFF COUNTER
+	bc1 : [0..1];
+	// SENSE
+	// let time pass
+	[time] s1=1 & x1<DIFS & free -> (x1'=min(x1+1,TIME_MAX));
+	// ready to transmit
+	[] s1=1 & (x1=DIFS | x1=DIFS-1) -> (s1'=8) & (x1'=0);
+	// found channel busy so wait until free
+	[] s1=1 & busy -> (s1'=2) & (x1'=0);
+	// WAIT UNTIL FREE BEFORE SETTING BACKOFF
+	// let time pass (no need for the clock x1 to change)
+	[time] s1=2 & busy -> (s1'=2);
+	// find that channel is free so check its free for DIFS before setting backoff
+	[] s1=2 & free -> (s1'=3);
+	// WAIT FOR DIFS THEN SET BACKOFF
+	// let time pass
+	[time] s1=3 & x1<DIFS & free -> (x1'=min(x1+1,TIME_MAX));
+	// found channel busy so wait until free
+	[] s1=3 & busy -> (s1'=2) & (x1'=0);
+	// start backoff  first uniformly choose slot
+	// backoff counter 0
+	[] s1=3 & (x1=DIFS | x1=DIFS-1) & bc1=0 -> (s1'=4) & (x1'=0) & (slot1'=0) & (bc1'=min(bc1+1,MAX_BACKOFF));
+	// SET BACKOFF (no time can pass)
+	// chosen slot now set backoff
+	[] s1=4 -> 1/16 : (s1'=5) & (backoff1'=0 )
+	         + 1/16 : (s1'=5) & (backoff1'=1 )
+	         + 1/16 : (s1'=5) & (backoff1'=2 )
+	         + 1/16 : (s1'=5) & (backoff1'=3 )
+	         + 1/16 : (s1'=5) & (backoff1'=4 )
+	         + 1/16 : (s1'=5) & (backoff1'=5 )
+	         + 1/16 : (s1'=5) & (backoff1'=6 )
+	         + 1/16 : (s1'=5) & (backoff1'=7 )
+	         + 1/16 : (s1'=5) & (backoff1'=8 )
+	         + 1/16 : (s1'=5) & (backoff1'=9 )
+	         + 1/16 : (s1'=5) & (backoff1'=10)
+	         + 1/16 : (s1'=5) & (backoff1'=11)
+	         + 1/16 : (s1'=5) & (backoff1'=12)
+	         + 1/16 : (s1'=5) & (backoff1'=13)
+	         + 1/16 : (s1'=5) & (backoff1'=14)
+	         + 1/16 : (s1'=5) & (backoff1'=15);
+	// BACKOFF
+	// let time pass
+	[time] s1=5 & x1<ASLOTTIME & free -> (x1'=min(x1+1,TIME_MAX));
+	// decrement backoff
+	[] s1=5 & x1=ASLOTTIME & backoff1>0 -> (s1'=5) & (x1'=0) & (backoff1'=backoff1-1);	
+	[] s1=5 & x1=ASLOTTIME & backoff1=0 & slot1>0 -> (s1'=5) & (x1'=0) & (backoff1'=15) & (slot1'=slot1-1);	
+	// finish backoff 
+	[] s1=5 & x1=ASLOTTIME & backoff1=0 & slot1=0 -> (s1'=8) & (x1'=0);
+	// found channel busy
+	[] s1=5 & busy -> (s1'=6) & (x1'=0);
+	// WAIT UNTIL FREE IN BACKOFF
+	// let time pass (no need for the clock x1 to change)
+	[time] s1=6 & busy -> (s1'=6);
+	// find that channel is free
+	[] s1=6 & free -> (s1'=7);
+	
+	// WAIT FOR DIFS THEN RESUME BACKOFF
+	// let time pass
+	[time] s1=7 & x1<DIFS & free -> (x1'=min(x1+1,TIME_MAX));
+	// resume backoff (start again from previous backoff)
+	[] s1=7 & (x1=DIFS | x1=DIFS-1) -> (s1'=5) & (x1'=0);
+	// found channel busy
+	[] s1=7 & busy -> (s1'=6) & (x1'=0);
+	
+	// VULNERABLE
+	// let time pass
+	[time] s1=8 & x1<VULN -> (x1'=min(x1+1,TIME_MAX));
+	// move to transmit
+	[send1] s1=8 & (x1=VULN | x1=VULN-1) -> (s1'=9) & (x1'=0);
+	// TRANSMIT
+	// let time pass
+	[time] s1=9 & x1<TRANS_TIME_MAX -> (x1'=min(x1+1,TIME_MAX));
+	// finish transmission successful	
+	[finish1] s1=9 & x1>=TRANS_TIME_MIN & c1=1 -> (s1'=10) & (x1'=0);
+	// finish transmission garbled
+	[finish1] s1=9 & x1>=TRANS_TIME_MIN & c1=2 -> (s1'=11) & (x1'=0);
+	// WAIT FOR SIFS THEN WAIT FOR ACK
+	
+	// WAIT FOR SIFS i.e. c1=0
+	// check channel and busy: go into backoff
+	[] s1=10 & c1=0 & x1=0 & busy -> (s1'=2);
+	// check channel and free: let time pass
+	[time] s1=10 & c1=0 & x1=0 & free -> (x1'=min(x1+1,TIME_MAX));
+	// let time pass
+	// following guard is always false as SIFS=1
+	// [time] s1=10 & c1=0 & x1>0 & x1<SIFS -> (x1'=min(x1+1,TIME_MAX));
+	// ack is sent after SIFS (since SIFS-1=0 add condition that channel is free)
+	[send1] s1=10 & c1=0 & (x1=SIFS | (x1=SIFS-1 & free)) -> (s1'=10) & (x1'=0);
+	
+	// WAIT FOR ACK i.e. c1=1
+	// let time pass
+	[time] s1=10 & c1=1 & x1<ACK -> (x1'=min(x1+1,TIME_MAX));
+	// get acknowledgement so packet sent correctly and move to done
+	[finish1] s1=10 & c1=1 & (x1=ACK | x1=ACK-1) -> (s1'=12) & (x1'=0) & (bc1'=0);
+	
+	// WAIT FOR ACK_TO
+	// check channel and busy: go into backoff
+	[] s1=11 & x1=0 & busy -> (s1'=2);
+	// check channel and free: let time pass
+	[time] s1=11 & x1=0 & free -> (x1'=min(x1+1,TIME_MAX));
+	// let time pass
+	[time] s1=11 & x1>0 & x1<ACK_TO -> (x1'=min(x1+1,TIME_MAX));
+	// no acknowledgement (go to backoff waiting DIFS first)
+	[] s1=11 & x1=ACK_TO -> (s1'=3) & (x1'=0);
+		
+	// DONE
+	[time] s1=12 -> (s1'=12);
+
+endmodule	
+
+// ---------------------------------------------------------------------------- //
+// STATION 2 (rename STATION 1)
+module 
+station2=station1[x1=x2, 
+                  s1=s2,
+				  s2=s1,
+				  c1=c2,
+				  c2=c1, 
+				  slot1=slot2, 
+				  backoff1=backoff2, 
+				  bc1=bc2, 
+				  send1=send2, 
+				  finish1=finish2] 
+endmodule
+// ---------------------------------------------------------------------------- //
+
+label "twoCollisions" = col=2;
+label "fourCollisions" = col=4;
+label "sixCollisions" = col=6;
diff --git a/wlan0_collide.nm b/wlan0_collide.nm
deleted file mode 100644
index 5d94e23de..000000000
--- a/wlan0_collide.nm
+++ /dev/null
@@ -1,13 +0,0 @@
-mdp
-
-module station1
-    s1 : [0..12] init 0;
-
-//	[] s1=0 -> (s1'=8) ;
-
-	[] s1=1 -> (s1'=1);
-	[] s1=1 -> (s1'=1);
-	
-//[] s1=8 -> (s1'=8);
-
-endmodule