From 676120229b6a0cd723e192b57fef1c5331d2ac41 Mon Sep 17 00:00:00 2001
From: dehnert <dehnert@cs.rwth-aachen.de>
Date: Thu, 21 Dec 2017 15:59:39 +0100
Subject: [PATCH 1/2] intermediate stage

---
 resources/examples/testfiles/dtmc/die.pm      |   2 +-
 .../SMTMinimalLabelSetGenerator.h             | 281 ++++++++++--------
 src/storm/models/ModelBase.h                  |   9 +-
 src/storm/models/sparse/Model.cpp             |   7 +-
 src/storm/models/sparse/Model.h               |   9 +-
 .../models/sparse/NondeterministicModel.cpp   |   5 -
 .../models/sparse/NondeterministicModel.h     |   7 -
 src/storm/models/symbolic/Model.cpp           |   5 +
 src/storm/models/symbolic/Model.h             |   2 +
 .../models/symbolic/NondeterministicModel.h   |   2 +-
 10 files changed, 184 insertions(+), 145 deletions(-)

diff --git a/resources/examples/testfiles/dtmc/die.pm b/resources/examples/testfiles/dtmc/die.pm
index e41139001..1873bb8c1 100644
--- a/resources/examples/testfiles/dtmc/die.pm
+++ b/resources/examples/testfiles/dtmc/die.pm
@@ -1,5 +1,5 @@
 // Knuth's model of a fair die using only fair coins
-dtmc
+mdp
 
 module die
 
diff --git a/src/storm/counterexamples/SMTMinimalLabelSetGenerator.h b/src/storm/counterexamples/SMTMinimalLabelSetGenerator.h
index 0237985f0..e659209a3 100644
--- a/src/storm/counterexamples/SMTMinimalLabelSetGenerator.h
+++ b/src/storm/counterexamples/SMTMinimalLabelSetGenerator.h
@@ -1,5 +1,4 @@
-#ifndef STORM_COUNTEREXAMPLES_SMTMINIMALLABELSETGENERATOR_MDP_H_
-#define STORM_COUNTEREXAMPLES_SMTMINIMALLABELSETGENERATOR_MDP_H_
+#pragma once
 
 #include <queue>
 #include <chrono>
@@ -11,7 +10,8 @@
 #include "storm/storage/prism/Program.h"
 #include "storm/storage/expressions/Expression.h"
 #include "storm/storage/sparse/PrismChoiceOrigins.h"
-#include "storm/modelchecker/prctl/SparseMdpPrctlModelChecker.h"
+#include "storm/modelchecker/propositional/SparsePropositionalModelChecker.h"
+#include "storm/modelchecker/prctl/helper/SparseDtmcPrctlHelper.h"
 #include "storm/modelchecker/prctl/helper/SparseMdpPrctlHelper.h"
 #include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h"
 #include "storm/settings/SettingsManager.h"
@@ -44,6 +44,9 @@ namespace storm {
                 // The set of relevant labels.
                 boost::container::flat_set<uint_fast64_t> relevantLabels;
                 
+                // The set of labels that matter in terms of minimality.
+                boost::container::flat_set<uint_fast64_t> minimalityLabels;
+                
                 // A set of labels that is definitely known to be taken in the final solution.
                 boost::container::flat_set<uint_fast64_t> knownLabels;
                 
@@ -58,6 +61,9 @@ namespace storm {
                 // The variables associated with the relevant labels.
                 std::vector<storm::expressions::Variable> labelVariables;
                 
+                // The variables associated with the labels that matter in terms of minimality.
+                std::vector<storm::expressions::Variable> minimalityLabelVariables;
+                
                 // A mapping from relevant labels to their indices in the variable vector.
                 std::map<uint_fast64_t, uint_fast64_t> labelToIndexMap;
 
@@ -93,30 +99,30 @@ namespace storm {
              * Computes the set of relevant labels in the model. Relevant labels are choice labels such that there exists
              * a scheduler that satisfies phi until psi with a nonzero probability.
              *
-             * @param mdp The MDP to search for relevant labels.
+             * @param model The model to search for relevant labels.
              * @param phiStates A bit vector representing all states that satisfy phi.
              * @param psiStates A bit vector representing all states that satisfy psi.
+             * @param dontCareLabels A set of labels that are "don't care" labels wrt. minimality.
              * @return A structure containing the relevant labels as well as states.
              */
-            static RelevancyInformation determineRelevantStatesAndLabels(storm::models::sparse::Mdp<T> const& mdp, std::vector<boost::container::flat_set<uint_fast64_t>> const& labelSets, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates) {
+            static RelevancyInformation determineRelevantStatesAndLabels(storm::models::sparse::Model<T> const& model, std::vector<boost::container::flat_set<uint_fast64_t>> const& labelSets, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, boost::container::flat_set<uint_fast64_t> const& dontCareLabels) {
                 // Create result.
                 RelevancyInformation relevancyInformation;
                 
                 // Compute all relevant states, i.e. states for which there exists a scheduler that has a non-zero
                 // probabilitiy of satisfying phi until psi.
-                storm::storage::SparseMatrix<T> backwardTransitions = mdp.getBackwardTransitions();
+                storm::storage::SparseMatrix<T> backwardTransitions = model.getBackwardTransitions();
                 relevancyInformation.relevantStates = storm::utility::graph::performProbGreater0E(backwardTransitions, phiStates, psiStates);
                 relevancyInformation.relevantStates &= ~psiStates;
 
                 STORM_LOG_DEBUG("Found " << relevancyInformation.relevantStates.getNumberOfSetBits() << " relevant states.");
-                STORM_LOG_DEBUG(relevancyInformation.relevantStates);
 
                 // Retrieve some references for convenient access.
-                storm::storage::SparseMatrix<T> const& transitionMatrix = mdp.getTransitionMatrix();
-                std::vector<uint_fast64_t> const& nondeterministicChoiceIndices = mdp.getNondeterministicChoiceIndices();
+                storm::storage::SparseMatrix<T> const& transitionMatrix = model.getTransitionMatrix();
+                std::vector<uint_fast64_t> const& nondeterministicChoiceIndices = model.getNondeterministicChoiceIndices();
 
                 // Now traverse all choices of all relevant states and check whether there is a successor target state.
-                // If so, the associated labels become relevant. Also, if a choice of relevant state has at least one
+                // If so, the associated labels become relevant. Also, if a choice of a relevant state has at least one
                 // relevant successor, the choice becomes relevant.
                 for (auto state : relevancyInformation.relevantStates) {
                     relevancyInformation.relevantChoicesForRelevantStates.emplace(state, std::list<uint_fast64_t>());
@@ -130,6 +136,7 @@ namespace storm {
                                 for (auto const& label : labelSets[row]) {
                                     relevancyInformation.relevantLabels.insert(label);
                                 }
+
                                 if (!currentChoiceRelevant) {
                                     currentChoiceRelevant = true;
                                     relevancyInformation.relevantChoicesForRelevantStates[state].push_back(row);
@@ -140,15 +147,15 @@ namespace storm {
                 }
                 
                 // Compute the set of labels that are known to be taken in any case.
-                relevancyInformation.knownLabels = storm::utility::counterexamples::getGuaranteedLabelSet(mdp, labelSets, psiStates, relevancyInformation.relevantLabels);
+                relevancyInformation.knownLabels = storm::utility::counterexamples::getGuaranteedLabelSet(model, labelSets, psiStates, relevancyInformation.relevantLabels);
                 if (!relevancyInformation.knownLabels.empty()) {
                     boost::container::flat_set<uint_fast64_t> remainingLabels;
                     std::set_difference(relevancyInformation.relevantLabels.begin(), relevancyInformation.relevantLabels.end(), relevancyInformation.knownLabels.begin(), relevancyInformation.knownLabels.end(), std::inserter(remainingLabels, remainingLabels.end()));
                     relevancyInformation.relevantLabels = remainingLabels;
                 }
                 
-                std::cout << "Found " << relevancyInformation.relevantLabels.size() << " relevant and " << relevancyInformation.knownLabels.size() << " known labels." << std::endl;
-
+                std::set_difference(relevancyInformation.relevantLabels.begin(), relevancyInformation.relevantLabels.end(), dontCareLabels.begin(), dontCareLabels.end(), std::inserter(relevancyInformation.minimalityLabels, relevancyInformation.minimalityLabels.begin()));
+                
                 STORM_LOG_DEBUG("Found " << relevancyInformation.relevantLabels.size() << " relevant and " << relevancyInformation.knownLabels.size() << " known labels.");
                 return relevancyInformation;
             }
@@ -160,7 +167,7 @@ namespace storm {
              * @param relevantCommands A set of relevant labels for which to create the expressions.
              * @return A mapping from relevant labels to their corresponding expressions.
              */
-            static VariableInformation createVariables(std::shared_ptr<storm::expressions::ExpressionManager> const& manager, storm::models::sparse::Mdp<T> const& mdp, storm::storage::BitVector const& psiStates, RelevancyInformation const& relevancyInformation, bool createReachabilityVariables) {
+            static VariableInformation createVariables(std::shared_ptr<storm::expressions::ExpressionManager> const& manager, storm::models::sparse::Model<T> const& model, storm::storage::BitVector const& psiStates, RelevancyInformation const& relevancyInformation, bool createReachabilityVariables) {
                 VariableInformation variableInformation;
                 variableInformation.manager = manager;
                 
@@ -178,6 +185,11 @@ namespace storm {
                     
                     variableInformation.labelVariables.push_back(manager->declareBooleanVariable(variableName.str()));
                     
+                    // Record if the label is among the ones that matter for minimality.
+                    if (relevancyInformation.minimalityLabels.find(label) != relevancyInformation.minimalityLabels.end()) {
+                        variableInformation.minimalityLabelVariables.push_back(variableInformation.labelVariables.back());
+                    }
+                    
                     // Clear contents of the stream to construct new expression name.
                     variableName.clear();
                     variableName.str("");
@@ -197,7 +209,7 @@ namespace storm {
                 if (createReachabilityVariables) {
                     variableInformation.hasReachabilityVariables = true;
                     
-                    storm::storage::SparseMatrix<T> const& transitionMatrix = mdp.getTransitionMatrix();
+                    storm::storage::SparseMatrix<T> const& transitionMatrix = model.getTransitionMatrix();
                     
                     for (auto state : relevancyInformation.relevantStates) {
                         variableInformation.relevantStatesToOrderVariableIndexMap[state] = variableInformation.stateOrderVariables.size();
@@ -253,13 +265,10 @@ namespace storm {
             /*!
              * Asserts the constraints that are initially needed for the Fu-Malik procedure.
              *
-             * @param program The program for which to build the constraints.
-             * @param mdp The MDP that results from the given program.
-             * @param context The Z3 context in which to build the expressions.
              * @param solver The solver in which to assert the constraints.
              * @param variableInformation A structure with information about the variables for the labels.
              */
-            static void assertFuMalikInitialConstraints(storm::prism::Program const& program, storm::models::sparse::Mdp<T> const& mdp, storm::storage::BitVector const& psiStates, z3::context& context, z3::solver& solver, VariableInformation const& variableInformation, RelevancyInformation const& relevancyInformation) {
+            static void assertFuMalikInitialConstraints(z3::solver& solver, VariableInformation const& variableInformation) {
                 // Assert that at least one of the labels must be taken.
                 z3::expr formula = variableInformation.labelVariables.at(0);
                 for (uint_fast64_t index = 1; index < variableInformation.labelVariables.size(); ++index) {
@@ -272,12 +281,12 @@ namespace storm {
              * Asserts cuts that are derived from the explicit representation of the model and rule out a lot of
              * suboptimal solutions.
              *
-             * @param mdp The labeled MDP for which to compute the cuts.
+             * @param model The labeled model for which to compute the cuts.
              * @param context The Z3 context in which to build the expressions.
              * @param solver The solver to use for the satisfiability evaluation.
              */
-            static void assertExplicitCuts(storm::models::sparse::Mdp<T> const& mdp, std::vector<boost::container::flat_set<uint_fast64_t>> const& labelSets, storm::storage::BitVector const& psiStates, VariableInformation const& variableInformation, RelevancyInformation const& relevancyInformation, storm::solver::SmtSolver& solver) {
-                // Walk through the MDP and
+            static void assertExplicitCuts(storm::models::sparse::Model<T> const& model, std::vector<boost::container::flat_set<uint_fast64_t>> const& labelSets, storm::storage::BitVector const& psiStates, VariableInformation const& variableInformation, RelevancyInformation const& relevancyInformation, storm::solver::SmtSolver& solver) {
+                // Walk through the model and
                 // * identify labels enabled in initial states
                 // * identify labels that can directly precede a given action
                 // * identify labels that directly reach a target state
@@ -291,10 +300,10 @@ namespace storm {
                 std::map<boost::container::flat_set<uint_fast64_t>, std::set<boost::container::flat_set<uint_fast64_t>>> followingLabels;
                 std::map<uint_fast64_t, std::set<boost::container::flat_set<uint_fast64_t>>> synchronizingLabels;
                 
-                // Get some data from the MDP for convenient access.
-                storm::storage::SparseMatrix<T> const& transitionMatrix = mdp.getTransitionMatrix();
-                storm::storage::BitVector const& initialStates = mdp.getInitialStates();
-                storm::storage::SparseMatrix<T> backwardTransitions = mdp.getBackwardTransitions();
+                // Get some data from the model for convenient access.
+                storm::storage::SparseMatrix<T> const& transitionMatrix = model.getTransitionMatrix();
+                storm::storage::BitVector const& initialStates = model.getInitialStates();
+                storm::storage::SparseMatrix<T> backwardTransitions = model.getBackwardTransitions();
                 
                 for (auto currentState : relevancyInformation.relevantStates) {
                     for (auto currentChoice : relevancyInformation.relevantChoicesForRelevantStates.at(currentState)) {
@@ -552,16 +561,16 @@ namespace storm {
              * @param program The symbolic representation of the model in terms of a program.
              * @param solver The solver to use for the satisfiability evaluation.
              */
-            static void assertSymbolicCuts(storm::prism::Program& program, storm::models::sparse::Mdp<T> const& mdp, std::vector<boost::container::flat_set<uint_fast64_t>> const& labelSets, VariableInformation const& variableInformation, RelevancyInformation const& relevancyInformation, storm::solver::SmtSolver& solver) {
+            static void assertSymbolicCuts(storm::prism::Program& program, storm::models::sparse::Model<T> const& model, std::vector<boost::container::flat_set<uint_fast64_t>> const& labelSets, VariableInformation const& variableInformation, RelevancyInformation const& relevancyInformation, storm::solver::SmtSolver& solver) {
                 // A container storing the label sets that may precede a given label set.
                 std::map<boost::container::flat_set<uint_fast64_t>, std::set<boost::container::flat_set<uint_fast64_t>>> precedingLabelSets;
 
                 // A container that maps labels to their reachable synchronization sets.
                 std::map<uint_fast64_t, std::set<boost::container::flat_set<uint_fast64_t>>> synchronizingLabels;
 
-                // Get some data from the MDP for convenient access.
-                storm::storage::SparseMatrix<T> const& transitionMatrix = mdp.getTransitionMatrix();
-                storm::storage::SparseMatrix<T> backwardTransitions = mdp.getBackwardTransitions();
+                // Get some data from the model for convenient access.
+                storm::storage::SparseMatrix<T> const& transitionMatrix = model.getTransitionMatrix();
+                storm::storage::SparseMatrix<T> backwardTransitions = model.getBackwardTransitions();
                 
                 // Compute the set of labels that may precede a given action.
                 for (auto currentState : relevancyInformation.relevantStates) {
@@ -871,22 +880,22 @@ namespace storm {
             /*!
              * Asserts constraints necessary to encode the reachability of at least one target state from the initial states.
              */
-            static void assertReachabilityCuts(storm::models::sparse::Mdp<T> const& mdp, std::vector<boost::container::flat_set<uint_fast64_t>> const& labelSets, storm::storage::BitVector const& psiStates, VariableInformation const& variableInformation, RelevancyInformation const& relevancyInformation, storm::solver::SmtSolver& solver) {
+            static void assertReachabilityCuts(storm::models::sparse::Model<T> const& model, std::vector<boost::container::flat_set<uint_fast64_t>> const& labelSets, storm::storage::BitVector const& psiStates, VariableInformation const& variableInformation, RelevancyInformation const& relevancyInformation, storm::solver::SmtSolver& solver) {
                 
                 if (!variableInformation.hasReachabilityVariables) {
                     throw storm::exceptions::InvalidStateException() << "Impossible to assert reachability cuts without the necessary variables.";
                 }
                 
-                // Get some data from the MDP for convenient access.
-                storm::storage::SparseMatrix<T> const& transitionMatrix = mdp.getTransitionMatrix();
-                storm::storage::SparseMatrix<T> backwardTransitions = mdp.getBackwardTransitions();
+                // Get some data from the model for convenient access.
+                storm::storage::SparseMatrix<T> const& transitionMatrix = model.getTransitionMatrix();
+                storm::storage::SparseMatrix<T> backwardTransitions = model.getBackwardTransitions();
 
                 // First, we add the formulas that encode
                 // (1) if an incoming transition is chosen, an outgoing one is chosen as well (for non-initial states)
                 // (2) an outgoing transition out of the initial states is taken.
                 storm::expressions::Expression initialStateExpression = variableInformation.manager->boolean(false);
                 for (auto relevantState : relevancyInformation.relevantStates) {
-                    if (!mdp.getInitialStates().get(relevantState)) {
+                    if (!model.getInitialStates().get(relevantState)) {
                         // Assert the constraints (1).
                         boost::container::flat_set<uint_fast64_t> relevantPredecessors;
                         for (auto const& predecessorEntry : backwardTransitions.getRow(relevantState)) {
@@ -1300,7 +1309,7 @@ namespace storm {
                 std::stringstream variableName;
                 std::vector<storm::expressions::Variable> result;
                 
-                std::vector<storm::expressions::Expression> adderVariables = createCounterCircuit(variableInformation, variableInformation.labelVariables);
+                std::vector<storm::expressions::Expression> adderVariables = createCounterCircuit(variableInformation, variableInformation.minimalityLabelVariables);
                 for (uint_fast64_t i = 0; i < adderVariables.size(); ++i) {
                     variableName.str("");
                     variableName.clear();
@@ -1340,32 +1349,32 @@ namespace storm {
             }
             
             /*!
-             * Analyzes the given sub-MDP that has a maximal reachability of zero (i.e. no psi states are reachable) and tries to construct assertions that aim to make at least one psi state reachable.
+             * Analyzes the given sub-model that has a maximal reachability of zero (i.e. no psi states are reachable) and tries to construct assertions that aim to make at least one psi state reachable.
              *
              * @param solver The solver to use for the satisfiability evaluation.
-             * @param subMdp The sub-MDP resulting from restricting the original MDP to the given command set.
-             * @param originalMdp The original MDP.
+             * @param subModel The sub-model resulting from restricting the original model to the given command set.
+             * @param originalModel The original model.
              * @param phiStates A bit vector characterizing all phi states in the model.
              * @param psiState A bit vector characterizing all psi states in the model.
              * @param commandSet The currently chosen set of commands.
              * @param variableInformation A structure with information about the variables of the solver.
              */
-            static void analyzeZeroProbabilitySolution(storm::solver::SmtSolver& solver, storm::models::sparse::Mdp<T> const& subMdp, std::vector<boost::container::flat_set<uint_fast64_t>> const& subLabelSets, storm::models::sparse::Mdp<T> const& originalMdp, std::vector<boost::container::flat_set<uint_fast64_t>> const& originalLabelSets, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, boost::container::flat_set<uint_fast64_t> const& commandSet, VariableInformation& variableInformation, RelevancyInformation const& relevancyInformation) {
-                storm::storage::BitVector reachableStates(subMdp.getNumberOfStates());
+            static void analyzeZeroProbabilitySolution(storm::solver::SmtSolver& solver, storm::models::sparse::Model<T> const& subModel, std::vector<boost::container::flat_set<uint_fast64_t>> const& subLabelSets, storm::models::sparse::Model<T> const& originalModel, std::vector<boost::container::flat_set<uint_fast64_t>> const& originalLabelSets, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, boost::container::flat_set<uint_fast64_t> const& commandSet, VariableInformation& variableInformation, RelevancyInformation const& relevancyInformation) {
+                storm::storage::BitVector reachableStates(subModel.getNumberOfStates());
                 
                 STORM_LOG_DEBUG("Analyzing solution with zero probability.");
                 
                 // Initialize the stack for the DFS.
                 bool targetStateIsReachable = false;
                 std::vector<uint_fast64_t> stack;
-                stack.reserve(subMdp.getNumberOfStates());
-                for (auto initialState : subMdp.getInitialStates()) {
+                stack.reserve(subModel.getNumberOfStates());
+                for (auto initialState : subModel.getInitialStates()) {
                     stack.push_back(initialState);
                     reachableStates.set(initialState, true);
                 }
                 
-                storm::storage::SparseMatrix<T> const& transitionMatrix = subMdp.getTransitionMatrix();
-                std::vector<uint_fast64_t> const& nondeterministicChoiceIndices = subMdp.getNondeterministicChoiceIndices();
+                storm::storage::SparseMatrix<T> const& transitionMatrix = subModel.getTransitionMatrix();
+                std::vector<uint_fast64_t> const& nondeterministicChoiceIndices = subModel.getNondeterministicChoiceIndices();
                 
                 // Now determine which states and labels are actually reachable.
                 boost::container::flat_set<uint_fast64_t> reachableLabels;
@@ -1404,12 +1413,12 @@ namespace storm {
                 }
                 
                 storm::storage::BitVector unreachableRelevantStates = ~reachableStates & relevancyInformation.relevantStates;
-                storm::storage::BitVector statesThatCanReachTargetStates = storm::utility::graph::performProbGreater0E(subMdp.getBackwardTransitions(), phiStates, psiStates);
+                storm::storage::BitVector statesThatCanReachTargetStates = storm::utility::graph::performProbGreater0E(subModel.getBackwardTransitions(), phiStates, psiStates);
                 
                 boost::container::flat_set<uint_fast64_t> locallyRelevantLabels;
                 std::set_difference(relevancyInformation.relevantLabels.begin(), relevancyInformation.relevantLabels.end(), commandSet.begin(), commandSet.end(), std::inserter(locallyRelevantLabels, locallyRelevantLabels.begin()));
                 
-                std::vector<boost::container::flat_set<uint_fast64_t>> guaranteedLabelSets = storm::utility::counterexamples::getGuaranteedLabelSets(originalMdp, originalLabelSets, statesThatCanReachTargetStates, locallyRelevantLabels);
+                std::vector<boost::container::flat_set<uint_fast64_t>> guaranteedLabelSets = storm::utility::counterexamples::getGuaranteedLabelSets(originalModel, originalLabelSets, statesThatCanReachTargetStates, locallyRelevantLabels);
                 STORM_LOG_DEBUG("Found " << reachableLabels.size() << " reachable labels and " << reachableStates.getNumberOfSetBits() << " reachable states.");
                 
                 // Search for states on the border of the reachable state space, i.e. states that are still reachable
@@ -1421,7 +1430,7 @@ namespace storm {
                             bool isBorderChoice = false;
 
                             // Determine whether the state has the option to leave the reachable state space and go to the unreachable relevant states.
-                            for (auto const& successorEntry : originalMdp.getTransitionMatrix().getRow(currentChoice)) {
+                            for (auto const& successorEntry : originalModel.getTransitionMatrix().getRow(currentChoice)) {
                                 if (unreachableRelevantStates.get(successorEntry.getColumn())) {
                                     isBorderChoice = true;
                                 }
@@ -1463,33 +1472,33 @@ namespace storm {
             }
             
             /*!
-             * Analyzes the given sub-MDP that has a non-zero maximal reachability and tries to construct assertions that aim to guide the solver to solutions
+             * Analyzes the given sub-model that has a non-zero maximal reachability and tries to construct assertions that aim to guide the solver to solutions
              * with an improved probability value.
              *
              * @param solver The solver to use for the satisfiability evaluation.
-             * @param subMdp The sub-MDP resulting from restricting the original MDP to the given command set.
-             * @param originalMdp The original MDP.
+             * @param subModel The sub-model resulting from restricting the original model to the given command set.
+             * @param originalModel The original model.
              * @param phiStates A bit vector characterizing all phi states in the model.
              * @param psiState A bit vector characterizing all psi states in the model.
              * @param commandSet The currently chosen set of commands.
              * @param variableInformation A structure with information about the variables of the solver.
              */
-            static void analyzeInsufficientProbabilitySolution(storm::solver::SmtSolver& solver, storm::models::sparse::Mdp<T> const& subMdp, std::vector<boost::container::flat_set<uint_fast64_t>> const& subLabelSets, storm::models::sparse::Mdp<T> const& originalMdp, std::vector<boost::container::flat_set<uint_fast64_t>> const& originalLabelSets, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, boost::container::flat_set<uint_fast64_t> const& commandSet, VariableInformation& variableInformation, RelevancyInformation const& relevancyInformation) {
+            static void analyzeInsufficientProbabilitySolution(storm::solver::SmtSolver& solver, storm::models::sparse::Model<T> const& subModel, std::vector<boost::container::flat_set<uint_fast64_t>> const& subLabelSets, storm::models::sparse::Model<T> const& originalModel, std::vector<boost::container::flat_set<uint_fast64_t>> const& originalLabelSets, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, boost::container::flat_set<uint_fast64_t> const& commandSet, VariableInformation& variableInformation, RelevancyInformation const& relevancyInformation) {
 
                 STORM_LOG_DEBUG("Analyzing solution with insufficient probability.");
 
-                storm::storage::BitVector reachableStates(subMdp.getNumberOfStates());
+                storm::storage::BitVector reachableStates(subModel.getNumberOfStates());
                 
                 // Initialize the stack for the DFS.
                 std::vector<uint_fast64_t> stack;
-                stack.reserve(subMdp.getNumberOfStates());
-                for (auto initialState : subMdp.getInitialStates()) {
+                stack.reserve(subModel.getNumberOfStates());
+                for (auto initialState : subModel.getInitialStates()) {
                     stack.push_back(initialState);
                     reachableStates.set(initialState, true);
                 }
                 
-                storm::storage::SparseMatrix<T> const& transitionMatrix = subMdp.getTransitionMatrix();
-                std::vector<uint_fast64_t> const& nondeterministicChoiceIndices = subMdp.getNondeterministicChoiceIndices();
+                storm::storage::SparseMatrix<T> const& transitionMatrix = subModel.getTransitionMatrix();
+                std::vector<uint_fast64_t> const& nondeterministicChoiceIndices = subModel.getNondeterministicChoiceIndices();
                 
                 // Now determine which states and labels are actually reachable.
                 boost::container::flat_set<uint_fast64_t> reachableLabels;
@@ -1522,12 +1531,12 @@ namespace storm {
                 STORM_LOG_DEBUG("Successfully determined reachable state space.");
                 
                 storm::storage::BitVector unreachableRelevantStates = ~reachableStates & relevancyInformation.relevantStates;
-                storm::storage::BitVector statesThatCanReachTargetStates = storm::utility::graph::performProbGreater0E(subMdp.getBackwardTransitions(), phiStates, psiStates);
+                storm::storage::BitVector statesThatCanReachTargetStates = storm::utility::graph::performProbGreater0E(subModel.getBackwardTransitions(), phiStates, psiStates);
                 
                 boost::container::flat_set<uint_fast64_t> locallyRelevantLabels;
                 std::set_difference(relevancyInformation.relevantLabels.begin(), relevancyInformation.relevantLabels.end(), commandSet.begin(), commandSet.end(), std::inserter(locallyRelevantLabels, locallyRelevantLabels.begin()));
                 
-                std::vector<boost::container::flat_set<uint_fast64_t>> guaranteedLabelSets = storm::utility::counterexamples::getGuaranteedLabelSets(originalMdp, originalLabelSets, statesThatCanReachTargetStates, locallyRelevantLabels);
+                std::vector<boost::container::flat_set<uint_fast64_t>> guaranteedLabelSets = storm::utility::counterexamples::getGuaranteedLabelSets(originalModel, originalLabelSets, statesThatCanReachTargetStates, locallyRelevantLabels);
                 
                 // Search for states for which we could enable another option and possibly improve the reachability probability.
                 std::set<boost::container::flat_set<uint_fast64_t>> cutLabels;
@@ -1570,20 +1579,20 @@ namespace storm {
         
             
             /*!
-             * Returns the submdp obtained from removing all choices that do not originate from the specified filterLabelSet.
-             * Also returns the Labelsets of the submdp
+             * Returns the sub-model obtained from removing all choices that do not originate from the specified filterLabelSet.
+             * Also returns the Labelsets of the sub-model.
              */
-            static std::pair<storm::models::sparse::Mdp<T>, std::vector<boost::container::flat_set<uint_fast64_t>>> restrictMdpToLabelSet(storm::models::sparse::Mdp<T> const& mdp,  boost::container::flat_set<uint_fast64_t> const& filterLabelSet) {
+            static std::pair<std::shared_ptr<storm::models::sparse::Model<T>>, std::vector<boost::container::flat_set<uint_fast64_t>>> restrictModelToLabelSet(storm::models::sparse::Model<T> const& model,  boost::container::flat_set<uint_fast64_t> const& filterLabelSet) {
 
                 std::vector<boost::container::flat_set<uint_fast64_t>> resultLabelSet;
-                storm::storage::SparseMatrixBuilder<T> transitionMatrixBuilder(0, mdp.getTransitionMatrix().getColumnCount(), 0, true, true, mdp.getTransitionMatrix().getRowGroupCount());
+                storm::storage::SparseMatrixBuilder<T> transitionMatrixBuilder(0, model.getTransitionMatrix().getColumnCount(), 0, true, true, model.getTransitionMatrix().getRowGroupCount());
 
                 // Check for each choice of each state, whether the choice commands are fully contained in the given command set.
                 uint_fast64_t currentRow = 0;
-                for(uint_fast64_t state = 0; state < mdp.getNumberOfStates(); ++state) {
+                for(uint_fast64_t state = 0; state < model.getNumberOfStates(); ++state) {
                     bool stateHasValidChoice = false;
-                    for (uint_fast64_t choice = mdp.getTransitionMatrix().getRowGroupIndices()[state]; choice < mdp.getTransitionMatrix().getRowGroupIndices()[state + 1]; ++choice) {
-                        auto const& choiceLabelSet = mdp.getChoiceOrigins()->asPrismChoiceOrigins().getCommandSet(choice);
+                    for (uint_fast64_t choice = model.getTransitionMatrix().getRowGroupIndices()[state]; choice < model.getTransitionMatrix().getRowGroupIndices()[state + 1]; ++choice) {
+                        auto const& choiceLabelSet = model.getChoiceOrigins()->asPrismChoiceOrigins().getCommandSet(choice);
                         bool choiceValid = std::includes(filterLabelSet.begin(), filterLabelSet.end(), choiceLabelSet.begin(), choiceLabelSet.end());
 
                         // If the choice is valid, copy over all its elements.
@@ -1592,7 +1601,7 @@ namespace storm {
                                 transitionMatrixBuilder.newRowGroup(currentRow);
                             }
                             stateHasValidChoice = true;
-                            for (auto const& entry : mdp.getTransitionMatrix().getRow(choice)) {
+                            for (auto const& entry : model.getTransitionMatrix().getRow(choice)) {
                                 transitionMatrixBuilder.addNextValue(currentRow, entry.getColumn(), entry.getValue());
                             }
                             resultLabelSet.push_back(choiceLabelSet);
@@ -1610,17 +1619,40 @@ namespace storm {
                     }
                 }
                 
-                storm::models::sparse::Mdp<T> resultMdp(transitionMatrixBuilder.build(), storm::models::sparse::StateLabeling(mdp.getStateLabeling()));
+                std::shared_ptr<storm::models::sparse::Model<T>> resultModel;
+                if (model.isOfType(storm::models::ModelType::Dtmc)) {
+                    resultModel = std::make_shared<storm::models::sparse::Dtmc<T>>(transitionMatrixBuilder.build(), storm::models::sparse::StateLabeling(model.getStateLabeling()));
+                } else {
+                    resultModel = std::make_shared<storm::models::sparse::Mdp<T>>(transitionMatrixBuilder.build(), storm::models::sparse::StateLabeling(model.getStateLabeling()));
+                }
                 
-                return std::make_pair(std::move(resultMdp), std::move(resultLabelSet));
+                return std::make_pair(resultModel, std::move(resultLabelSet));
             }
 
+            static T computeMaximalReachabilityProbability(Environment const& env, storm::models::sparse::Model<T> const& model, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates) {
+                T result = storm::utility::zero<T>();
+                
+                std::vector<T> allStatesResult;
+                
+                STORM_LOG_DEBUG("Invoking model checker.");
+                if (model.isOfType(storm::models::ModelType::Dtmc)) {
+                    allStatesResult = storm::modelchecker::helper::SparseDtmcPrctlHelper<T>::computeUntilProbabilities(env, false, model.getTransitionMatrix(), model.getBackwardTransitions(), phiStates, psiStates, false, storm::solver::GeneralLinearEquationSolverFactory<T>());
+                } else {
+                    storm::modelchecker::helper::SparseMdpPrctlHelper<T> modelCheckerHelper;
+                    allStatesResult = std::move(modelCheckerHelper.computeUntilProbabilities(env, false, model.getTransitionMatrix(), model.getBackwardTransitions(), phiStates, psiStates, false, false, storm::solver::GeneralMinMaxLinearEquationSolverFactory<T>()).values);
+                }
+                for (auto state : model.getInitialStates()) {
+                    result = std::max(result, allStatesResult[state]);
+                }
+                return result;
+            }
+            
         public:
             /*!
-             * Computes the minimal command set that is needed in the given MDP to exceed the given probability threshold for satisfying phi until psi.
+             * Computes the minimal command set that is needed in the given model to exceed the given probability threshold for satisfying phi until psi.
              *
-             * @param program The program that was used to build the MDP.
-             * @param mdp The MDP in which to find the minimal command set.
+             * @param program The program that was used to build the model.
+             * @param model The sparse model in which to find the minimal command set.
              * @param phiStates A bit vector characterizing all phi states in the model.
              * @param psiStates A bit vector characterizing all psi states in the model.
              * @param probabilityThreshold The threshold that is to be achieved or exceeded.
@@ -1628,7 +1660,7 @@ namespace storm {
              * @param checkThresholdFeasible If set, it is verified that the model can actually achieve/exceed the given probability value. If this check
              * is made and fails, an exception is thrown.
              */
-            static boost::container::flat_set<uint_fast64_t> getMinimalCommandSet(Environment const& env,storm::prism::Program program, storm::models::sparse::Mdp<T> const& mdp, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, double probabilityThreshold, bool strictBound, bool checkThresholdFeasible = false, bool includeReachabilityEncoding = false) {
+            static boost::container::flat_set<uint_fast64_t> getMinimalCommandSet(Environment const& env, storm::prism::Program program, storm::models::sparse::Model<T> const& model, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, double probabilityThreshold, bool strictBound, boost::container::flat_set<uint_fast64_t> const& dontCareLabels = boost::container::flat_set<uint_fast64_t>(), bool checkThresholdFeasible = false, bool includeReachabilityEncoding = false) {
 #ifdef STORM_HAVE_Z3
                 // Set up all clocks used for time measurement.
                 auto totalClock = std::chrono::high_resolution_clock::now();
@@ -1649,37 +1681,33 @@ namespace storm {
 
                 // (0) Obtain the label sets for each choice.
                 // The label set of a choice corresponds to the set of prism commands that induce the choice.
-                STORM_LOG_THROW(mdp.hasChoiceOrigins(), storm::exceptions::InvalidArgumentException, "Restriction to minimal command set is impossible for model without choice origins.");
-                STORM_LOG_THROW(mdp.getChoiceOrigins()->isPrismChoiceOrigins(), storm::exceptions::InvalidArgumentException, "Restriction to command set is impossible for model without prism choice origins.");
-                storm::storage::sparse::PrismChoiceOrigins const& choiceOrigins = mdp.getChoiceOrigins()->asPrismChoiceOrigins();
+                STORM_LOG_THROW(model.hasChoiceOrigins(), storm::exceptions::InvalidArgumentException, "Restriction to minimal command set is impossible for model without choice origins.");
+                STORM_LOG_THROW(model.getChoiceOrigins()->isPrismChoiceOrigins(), storm::exceptions::InvalidArgumentException, "Restriction to command set is impossible for model without prism choice origins.");
+                storm::storage::sparse::PrismChoiceOrigins const& choiceOrigins = model.getChoiceOrigins()->asPrismChoiceOrigins();
                 std::vector<boost::container::flat_set<uint_fast64_t>> labelSets;
-                labelSets.reserve(mdp.getNumberOfChoices());
-                for (uint_fast64_t choice = 0; choice < mdp.getNumberOfChoices(); ++choice) {
+                labelSets.reserve(model.getNumberOfChoices());
+                for (uint_fast64_t choice = 0; choice < model.getNumberOfChoices(); ++choice) {
                     labelSets.push_back(choiceOrigins.getCommandSet(choice));
                 }
                 
                 // (1) Check whether its possible to exceed the threshold if checkThresholdFeasible is set.
                 double maximalReachabilityProbability = 0;
                 if (checkThresholdFeasible) {
-                    storm::modelchecker::helper::SparseMdpPrctlHelper<T> modelCheckerHelper;
-                    STORM_LOG_DEBUG("Invoking model checker.");
-                    std::vector<T> result = std::move(modelCheckerHelper.computeUntilProbabilities(env, false, mdp.getTransitionMatrix(), mdp.getBackwardTransitions(), phiStates, psiStates, false, false, storm::solver::GeneralMinMaxLinearEquationSolverFactory<T>()).values);
-                    for (auto state : mdp.getInitialStates()) {
-                        maximalReachabilityProbability = std::max(maximalReachabilityProbability, result[state]);
-                    }
+                    maximalReachabilityProbability = computeMaximalReachabilityProbability(env, model, phiStates, psiStates);
+                    
                     STORM_LOG_THROW((strictBound && maximalReachabilityProbability >= probabilityThreshold) || (!strictBound && maximalReachabilityProbability > probabilityThreshold), storm::exceptions::InvalidArgumentException, "Given probability threshold " << probabilityThreshold << " can not be " << (strictBound ? "achieved" : "exceeded") << " in model with maximal reachability probability of " << maximalReachabilityProbability << ".");
                     std::cout << std::endl << "Maximal reachability in model is " << maximalReachabilityProbability << "." << std::endl << std::endl;
                 }
                 
                 // (2) Identify all states and commands that are relevant, because only these need to be considered later.
-                RelevancyInformation relevancyInformation = determineRelevantStatesAndLabels(mdp, labelSets, phiStates, psiStates);
+                RelevancyInformation relevancyInformation = determineRelevantStatesAndLabels(model, labelSets, phiStates, psiStates, dontCareLabels);
                 
                 // (3) Create a solver.
                 std::shared_ptr<storm::expressions::ExpressionManager> manager = std::make_shared<storm::expressions::ExpressionManager>();
                 std::unique_ptr<storm::solver::SmtSolver> solver = std::make_unique<storm::solver::Z3SmtSolver>(*manager);
                 
                 // (4) Create the variables for the relevant commands.
-                VariableInformation variableInformation = createVariables(manager, mdp, psiStates, relevancyInformation, includeReachabilityEncoding);
+                VariableInformation variableInformation = createVariables(manager, model, psiStates, relevancyInformation, includeReachabilityEncoding);
                 STORM_LOG_DEBUG("Created variables.");
 
                 // (5) Now assert an adder whose result variables can later be used to constrain the nummber of label
@@ -1690,12 +1718,12 @@ namespace storm {
                 
                 // (6) Add constraints that cut off a lot of suboptimal solutions.
                 STORM_LOG_DEBUG("Asserting cuts.");
-                assertExplicitCuts(mdp, labelSets, psiStates, variableInformation, relevancyInformation, *solver);
+                assertExplicitCuts(model, labelSets, psiStates, variableInformation, relevancyInformation, *solver);
                 STORM_LOG_DEBUG("Asserted explicit cuts.");
-                assertSymbolicCuts(program, mdp, labelSets, variableInformation, relevancyInformation, *solver);
+                assertSymbolicCuts(program, model, labelSets, variableInformation, relevancyInformation, *solver);
                 STORM_LOG_DEBUG("Asserted symbolic cuts.");
                 if (includeReachabilityEncoding) {
-                    assertReachabilityCuts(mdp, labelSets, psiStates, variableInformation, relevancyInformation, *solver);
+                    assertReachabilityCuts(model, labelSets, psiStates, variableInformation, relevancyInformation, *solver);
                     STORM_LOG_DEBUG("Asserted reachability cuts.");
                 }
                 
@@ -1706,12 +1734,18 @@ namespace storm {
                 // satisfying phi until psi exceeds the given threshold, the set of labels is minimal and can be returned.
                 // Otherwise, the current solution has to be ruled out and the next smallest solution is retrieved from
                 // the solver.
+
+                boost::container::flat_set<uint_fast64_t> commandSet(relevancyInformation.knownLabels);
                 
-                // Set up some variables for the iterations.
-                boost::container::flat_set<uint_fast64_t> commandSet(relevancyInformation.relevantLabels);
+                // If there are no relevant labels, return directly.
                 if (relevancyInformation.relevantLabels.empty()) {
                     return commandSet;
+                } else if (relevancyInformation.minimalityLabels.empty()) {
+                    commandSet.insert(relevancyInformation.relevantLabels.begin(), relevancyInformation.relevantLabels.end());
+                    return commandSet;
                 }
+                
+                // Set up some variables for the iterations.
                 bool done = false;
                 uint_fast64_t iterations = 0;
                 uint_fast64_t currentBound = 0;
@@ -1724,24 +1758,16 @@ namespace storm {
                     totalSolverTime += std::chrono::high_resolution_clock::now() - solverClock;
                     STORM_LOG_DEBUG("Computed minimal command set of size " << (commandSet.size() + relevancyInformation.knownLabels.size()) << ".");
                     
-                    // Restrict the given MDP to the current set of labels and compute the reachability probability.
+                    // Restrict the given model to the current set of labels and compute the reachability probability.
                     modelCheckingClock = std::chrono::high_resolution_clock::now();
                     commandSet.insert(relevancyInformation.knownLabels.begin(), relevancyInformation.knownLabels.end());
-                    auto subMdpChoiceOrigins = restrictMdpToLabelSet(mdp, commandSet);
-                    storm::models::sparse::Mdp<T> const& subMdp = subMdpChoiceOrigins.first;
-                    std::vector<boost::container::flat_set<uint_fast64_t>> const& subLabelSets = subMdpChoiceOrigins.second;
+                    auto subChoiceOrigins = restrictModelToLabelSet(model, commandSet);
+                    std::shared_ptr<storm::models::sparse::Model<T>> const& subModel = subChoiceOrigins.first;
+                    std::vector<boost::container::flat_set<uint_fast64_t>> const& subLabelSets = subChoiceOrigins.second;
   
-                    storm::modelchecker::helper::SparseMdpPrctlHelper<T> modelCheckerHelper;
-                    STORM_LOG_DEBUG("Invoking model checker.");
-                    std::vector<T> result = std::move(modelCheckerHelper.computeUntilProbabilities(env, false, subMdp.getTransitionMatrix(), subMdp.getBackwardTransitions(), phiStates, psiStates, false, false, storm::solver::GeneralMinMaxLinearEquationSolverFactory<T>()).values);
-                    STORM_LOG_DEBUG("Computed model checking results.");
+                    // Now determine the maximal reachability probability in the sub-model.
+                    maximalReachabilityProbability = computeMaximalReachabilityProbability(env, *subModel, phiStates, psiStates);
                     totalModelCheckingTime += std::chrono::high_resolution_clock::now() - modelCheckingClock;
-
-                    // Now determine the maximal reachability probability by checking all initial states.
-                    maximalReachabilityProbability = 0;
-                    for (auto state : mdp.getInitialStates()) {
-                        maximalReachabilityProbability = std::max(maximalReachabilityProbability, result[state]);
-                    }
                     
                     // Depending on whether the threshold was successfully achieved or not, we proceed by either analyzing the bad solution or stopping the iteration process.
                     analysisClock = std::chrono::high_resolution_clock::now();
@@ -1750,11 +1776,11 @@ namespace storm {
                             ++zeroProbabilityCount;
                             
                             // If there was no target state reachable, analyze the solution and guide the solver into the right direction.
-                            analyzeZeroProbabilitySolution(*solver, subMdp, subLabelSets, mdp, labelSets, phiStates, psiStates, commandSet, variableInformation, relevancyInformation);
+                            analyzeZeroProbabilitySolution(*solver, *subModel, subLabelSets, model, labelSets, phiStates, psiStates, commandSet, variableInformation, relevancyInformation);
                         } else {
                             // If the reachability probability was greater than zero (i.e. there is a reachable target state), but the probability was insufficient to exceed
                             // the given threshold, we analyze the solution and try to guide the solver into the right direction.
-                            analyzeInsufficientProbabilitySolution(*solver, subMdp, subLabelSets, mdp, labelSets, phiStates, psiStates, commandSet, variableInformation, relevancyInformation);
+                            analyzeInsufficientProbabilitySolution(*solver, *subModel, subLabelSets, model, labelSets, phiStates, psiStates, commandSet, variableInformation, relevancyInformation);
                         }
                     } else {
                         done = true;
@@ -1791,14 +1817,14 @@ namespace storm {
 #endif
             }
             
-            static void extendCommandSetLowerBound(storm::models::sparse::Mdp<T> const& mdp, boost::container::flat_set<uint_fast64_t>& commandSet, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates) {
+            static void extendCommandSetLowerBound(storm::models::sparse::Model<T> const& model, boost::container::flat_set<uint_fast64_t>& commandSet, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates) {
                 auto startTime = std::chrono::high_resolution_clock::now();
                 
-                // Create sub-MDP that only contains the choices allowed by the given command set.
-                storm::models::sparse::Mdp<T> subMdp = restrictMdpToLabelSet(mdp, commandSet).first;
+                // Create sub-model that only contains the choices allowed by the given command set.
+                storm::models::sparse::Model<T> subModel = restrictModelToLabelSet(model, commandSet).first;
                 
-                // Then determine all prob0E(psi) states that are reachable in the sub-MDP.
-                storm::storage::BitVector reachableProb0EStates = storm::utility::graph::getReachableStates(subMdp.getTransitionMatrix(), subMdp.getInitialStates(), phiStates, psiStates);
+                // Then determine all prob0E(psi) states that are reachable in the sub-model.
+                storm::storage::BitVector reachableProb0EStates = storm::utility::graph::getReachableStates(subModel.getTransitionMatrix(), subModel.getInitialStates(), phiStates, psiStates);
                 
                 // Create a queue of reachable prob0E(psi) states so we can check which commands need to be added
                 // to give them a strategy that avoids psi states.
@@ -1816,12 +1842,12 @@ namespace storm {
                     // Now iterate over the original choices of the prob0E(psi) state and add at least one.
                     bool hasLabeledChoice = false;
                     uint64_t smallestCommandSetSize = 0;
-                    uint64_t smallestCommandChoice = mdp.getTransitionMatrix().getRowGroupIndices()[state];
+                    uint64_t smallestCommandChoice = model.getTransitionMatrix().getRowGroupIndices()[state];
                     
                     // Determine the choice with the least amount of commands (bad heuristic).
-                    for (uint64_t choice = smallestCommandChoice; choice < mdp.getTransitionMatrix().getRowGroupIndices()[state + 1]; ++choice) {
+                    for (uint64_t choice = smallestCommandChoice; choice < model.getTransitionMatrix().getRowGroupIndices()[state + 1]; ++choice) {
                         bool onlyProb0ESuccessors = true;
-                        for (auto const& successorEntry : mdp.getTransitionMatrix().getRow(choice)) {
+                        for (auto const& successorEntry : model.getTransitionMatrix().getRow(choice)) {
                             if (!psiStates.get(successorEntry.getColumn())) {
                                 onlyProb0ESuccessors = false;
                                 break;
@@ -1829,7 +1855,7 @@ namespace storm {
                         }
                         
                         if (onlyProb0ESuccessors) {
-                            auto const& labelSet = mdp.getChoiceOrigins()->asPrismChoiceOrigins().getCommandSet(choice);
+                            auto const& labelSet = model.getChoiceOrigins()->asPrismChoiceOrigins().getCommandSet(choice);
                             hasLabeledChoice |= !labelSet.empty();
                             
                             if (smallestCommandChoice == 0 || labelSet.size() < smallestCommandSetSize) {
@@ -1841,11 +1867,11 @@ namespace storm {
                     
                     if (hasLabeledChoice) {
                         // Take all labels of the selected choice.
-                        auto const& labelSet = mdp.getChoiceOrigins()->asPrismChoiceOrigins().getCommandSet(smallestCommandChoice);
+                        auto const& labelSet = model.getChoiceOrigins()->asPrismChoiceOrigins().getCommandSet(smallestCommandChoice);
                         commandSet.insert(labelSet.begin(), labelSet.end());
                         
                         // Check for which successor states choices need to be added
-                        for (auto const& successorEntry : mdp.getTransitionMatrix().getRow(smallestCommandChoice)) {
+                        for (auto const& successorEntry : model.getTransitionMatrix().getRow(smallestCommandChoice)) {
                             if (!storm::utility::isZero(successorEntry.getValue())) {
                                 if (!reachableProb0EStates.get(successorEntry.getColumn())) {
                                     reachableProb0EStates.set(successorEntry.getColumn());
@@ -1860,7 +1886,7 @@ namespace storm {
                 std::cout << std::endl << "Extended command for lower bounded property to size " << commandSet.size() << " in " << std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count() << "ms." << std::endl;
             }
 
-            static boost::container::flat_set<uint_fast64_t>  computeCounterexampleCommandSet(Environment const& env, storm::prism::Program program, storm::models::sparse::Mdp<T> const& mdp, std::shared_ptr<storm::logic::Formula const> const& formula) {
+            static boost::container::flat_set<uint_fast64_t>  computeCounterexampleCommandSet(Environment const& env, storm::prism::Program program, storm::models::sparse::Model<T> const& model, std::shared_ptr<storm::logic::Formula const> const& formula) {
                 std::cout << std::endl << "Generating minimal label counterexample for formula " << *formula << std::endl;
 
                 STORM_LOG_THROW(formula->isProbabilityOperatorFormula(), storm::exceptions::InvalidPropertyException, "Counterexample generation does not support this kind of formula. Expecting a probability operator as the outermost formula element.");
@@ -1874,7 +1900,7 @@ namespace storm {
 
                 storm::storage::BitVector phiStates;
                 storm::storage::BitVector psiStates;
-                storm::modelchecker::SparseMdpPrctlModelChecker<storm::models::sparse::Mdp<T>> modelchecker(mdp);
+                storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Model<T>> modelchecker(model);
 
                 if (probabilityOperator.getSubformula().isUntilFormula()) {
                     STORM_LOG_THROW(!storm::logic::isLowerBound(comparisonType), storm::exceptions::NotSupportedException, "Lower bounds in counterexamples are only supported for eventually formulas.");
@@ -1895,7 +1921,7 @@ namespace storm {
 
                     storm::modelchecker::ExplicitQualitativeCheckResult const& subQualitativeResult = subResult->asExplicitQualitativeCheckResult();
 
-                    phiStates = storm::storage::BitVector(mdp.getNumberOfStates(), true);
+                    phiStates = storm::storage::BitVector(model.getNumberOfStates(), true);
                     psiStates = subQualitativeResult.getTruthValuesVector();
                 }
 
@@ -1907,7 +1933,7 @@ namespace storm {
                     // reaching psi states completely.
 
                     // This means that from all states in prob0E(psi) we need to include labels such that \sigma_0
-                    // is actually included in the resulting MDP. This prevents us from guaranteeing the minimality of
+                    // is actually included in the resulting model. This prevents us from guaranteeing the minimality of
                     // the returned counterexample, so we warn about that.
                     STORM_LOG_WARN("Generating counterexample for lower-bounded property. The resulting command set need not be minimal.");
 
@@ -1916,7 +1942,7 @@ namespace storm {
                     threshold = storm::utility::one<T>() - threshold;
 
                     // Modify the phi and psi states appropriately.
-                    storm::storage::BitVector statesWithProbability0E = storm::utility::graph::performProb0E(mdp.getTransitionMatrix(), mdp.getTransitionMatrix().getRowGroupIndices(), mdp.getBackwardTransitions(), phiStates, psiStates);
+                    storm::storage::BitVector statesWithProbability0E = storm::utility::graph::performProb0E(model.getTransitionMatrix(), model.getTransitionMatrix().getRowGroupIndices(), model.getBackwardTransitions(), phiStates, psiStates);
                     phiStates = ~psiStates;
                     psiStates = std::move(statesWithProbability0E);
 
@@ -1927,20 +1953,20 @@ namespace storm {
 
                 // Delegate the actual computation work to the function of equal name.
                 auto startTime = std::chrono::high_resolution_clock::now();
-                auto commandSet = getMinimalCommandSet(env, program, mdp, phiStates, psiStates, threshold, strictBound, true, storm::settings::getModule<storm::settings::modules::CounterexampleGeneratorSettings>().isEncodeReachabilitySet());
+                auto commandSet = getMinimalCommandSet(env, program, model, phiStates, psiStates, threshold, strictBound, boost::container::flat_set<uint_fast64_t>(), true, storm::settings::getModule<storm::settings::modules::CounterexampleGeneratorSettings>().isEncodeReachabilitySet());
                 auto endTime = std::chrono::high_resolution_clock::now();
                 std::cout << std::endl << "Computed minimal command set of size " << commandSet.size() << " in " << std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count() << "ms." << std::endl;
 
                 // Extend the command set properly.
                 if (lowerBoundedFormula) {
-                    extendCommandSetLowerBound(mdp, commandSet, phiStates, psiStates);
+                    extendCommandSetLowerBound(model, commandSet, phiStates, psiStates);
                 }
                 return commandSet;
             }
 
-            static std::shared_ptr<PrismHighLevelCounterexample> computeCounterexample(Environment const& env, storm::prism::Program program, storm::models::sparse::Mdp<T> const& mdp, std::shared_ptr<storm::logic::Formula const> const& formula) {
+            static std::shared_ptr<PrismHighLevelCounterexample> computeCounterexample(Environment const& env, storm::prism::Program program, storm::models::sparse::Model<T> const& model, std::shared_ptr<storm::logic::Formula const> const& formula) {
 #ifdef STORM_HAVE_Z3
-                auto commandSet = computeCounterexampleCommandSet(env, program, mdp, formula);
+                auto commandSet = computeCounterexampleCommandSet(env, program, model, formula);
                 
                 return std::make_shared<PrismHighLevelCounterexample>(program.restrictCommands(commandSet));
 
@@ -1955,4 +1981,3 @@ namespace storm {
     } // namespace counterexamples
 } // namespace storm
 
-#endif /* STORM_COUNTEREXAMPLES_SMTMINIMALLABELSETGENERATOR_MDP_H_ */
diff --git a/src/storm/models/ModelBase.h b/src/storm/models/ModelBase.h
index 9eace3911..e816adac6 100644
--- a/src/storm/models/ModelBase.h
+++ b/src/storm/models/ModelBase.h
@@ -71,7 +71,14 @@ namespace storm {
              * @return The number of (non-zero) transitions of the model.
              */
             virtual uint_fast64_t getNumberOfTransitions() const = 0;
-                        
+
+            /*!
+             * Returns the number of choices ine the model.
+             *
+             * @return The number of choices in of the model.
+             */
+            virtual uint_fast64_t getNumberOfChoices() const = 0;
+
             /*!
              * Prints information about the model to the specified stream.
              *
diff --git a/src/storm/models/sparse/Model.cpp b/src/storm/models/sparse/Model.cpp
index b86f11c7a..edeefb825 100644
--- a/src/storm/models/sparse/Model.cpp
+++ b/src/storm/models/sparse/Model.cpp
@@ -98,7 +98,12 @@ namespace storm {
             uint_fast64_t Model<ValueType, RewardModelType>::getNumberOfTransitions() const {
                 return this->getTransitionMatrix().getNonzeroEntryCount();
             }
-            
+
+            template<typename ValueType, typename RewardModelType>
+            uint_fast64_t Model<ValueType, RewardModelType>::getNumberOfChoices() const {
+                return this->getTransitionMatrix().getRowCount();
+            }
+
             template<typename ValueType, typename RewardModelType>
             storm::storage::BitVector const& Model<ValueType, RewardModelType>::getInitialStates() const {
                 return this->getStates("init");
diff --git a/src/storm/models/sparse/Model.h b/src/storm/models/sparse/Model.h
index b796a30f9..aa61eb3bd 100644
--- a/src/storm/models/sparse/Model.h
+++ b/src/storm/models/sparse/Model.h
@@ -76,7 +76,14 @@ namespace storm {
                  * @return The number of (non-zero) transitions of the model.
                  */
                 virtual uint_fast64_t getNumberOfTransitions() const override;
-                
+
+                /*!
+                 * Returns the number of choices ine the model.
+                 *
+                 * @return The number of choices in of the model.
+                 */
+                virtual uint_fast64_t getNumberOfChoices() const override;
+
                 /*!
                  * Retrieves the initial states of the model.
                  *
diff --git a/src/storm/models/sparse/NondeterministicModel.cpp b/src/storm/models/sparse/NondeterministicModel.cpp
index cd67e384f..263f67c16 100644
--- a/src/storm/models/sparse/NondeterministicModel.cpp
+++ b/src/storm/models/sparse/NondeterministicModel.cpp
@@ -26,11 +26,6 @@ namespace storm {
                 // Intentionally left empty
             }
             
-            template<typename ValueType, typename RewardModelType>
-            uint_fast64_t NondeterministicModel<ValueType, RewardModelType>::getNumberOfChoices() const {
-                return this->getTransitionMatrix().getRowCount();
-            }
-            
             template<typename ValueType, typename RewardModelType>
             std::vector<uint_fast64_t> const& NondeterministicModel<ValueType, RewardModelType>::getNondeterministicChoiceIndices() const {
                 return this->getTransitionMatrix().getRowGroupIndices();
diff --git a/src/storm/models/sparse/NondeterministicModel.h b/src/storm/models/sparse/NondeterministicModel.h
index dd1335718..2274e137a 100644
--- a/src/storm/models/sparse/NondeterministicModel.h
+++ b/src/storm/models/sparse/NondeterministicModel.h
@@ -31,13 +31,6 @@ namespace storm {
                  */
                 NondeterministicModel(ModelType modelType, storm::storage::sparse::ModelComponents<ValueType, RewardModelType> const& components);
                 NondeterministicModel(ModelType modelType, storm::storage::sparse::ModelComponents<ValueType, RewardModelType>&& components);
-
-                /*!
-                 * Retrieves the number of (nondeterministic) choices in the model.
-                 *
-                 * @return The number of (nondeterministic) choices in the model.
-                 */
-                uint_fast64_t getNumberOfChoices() const;
                 
                 /*!
                  * Retrieves the vector indicating which matrix rows represent non-deterministic choices of a certain state.
diff --git a/src/storm/models/symbolic/Model.cpp b/src/storm/models/symbolic/Model.cpp
index f468cc377..5364a4d4f 100644
--- a/src/storm/models/symbolic/Model.cpp
+++ b/src/storm/models/symbolic/Model.cpp
@@ -64,6 +64,11 @@ namespace storm {
             uint_fast64_t Model<Type, ValueType>::getNumberOfTransitions() const {
                 return transitionMatrix.getNonZeroCount();
             }
+
+            template<storm::dd::DdType Type, typename ValueType>
+            uint_fast64_t Model<Type, ValueType>::getNumberOfChoices() const {
+                return reachableStates.getNonZeroCount();
+            }
             
             template<storm::dd::DdType Type, typename ValueType>
             storm::dd::DdManager<Type>& Model<Type, ValueType>::getManager() const {
diff --git a/src/storm/models/symbolic/Model.h b/src/storm/models/symbolic/Model.h
index fd0839a2b..2f39d3970 100644
--- a/src/storm/models/symbolic/Model.h
+++ b/src/storm/models/symbolic/Model.h
@@ -119,6 +119,8 @@ namespace storm {
                 
                 virtual uint_fast64_t getNumberOfTransitions() const override;
                 
+                virtual uint_fast64_t getNumberOfChoices() const override;
+
                 /*!
                  * Retrieves the manager responsible for the DDs that represent this model.
                  *
diff --git a/src/storm/models/symbolic/NondeterministicModel.h b/src/storm/models/symbolic/NondeterministicModel.h
index c75874644..7fb42cf7b 100644
--- a/src/storm/models/symbolic/NondeterministicModel.h
+++ b/src/storm/models/symbolic/NondeterministicModel.h
@@ -90,7 +90,7 @@ namespace storm {
                  *
                  * @return The number of nondeterministic choices in the model.
                  */
-                uint_fast64_t getNumberOfChoices() const;
+                virtual uint_fast64_t getNumberOfChoices() const override;
                 
                 /*!
                  * Retrieves the meta variables used to encode the nondeterminism in the model.

From 4591dba631111f31a65a5252b1ab622f70d91f0b Mon Sep 17 00:00:00 2001
From: dehnert <dehnert@cs.rwth-aachen.de>
Date: Thu, 21 Dec 2017 18:46:34 +0100
Subject: [PATCH 2/2] made maxsat-based counterexample generation be applicable
 to DTMCs and MDPs

---
 resources/examples/testfiles/dtmc/die.pm      |  2 +-
 src/storm-cli-utilities/model-handling.h      | 13 ++++++----
 src/storm/api/counterexamples.cpp             |  4 ++--
 src/storm/api/counterexamples.h               |  2 +-
 .../SMTMinimalLabelSetGenerator.h             | 11 +++++----
 .../models/sparse/NondeterministicModel.h     |  6 ++---
 src/storm/utility/counterexamples.h           | 24 +++++++++----------
 7 files changed, 34 insertions(+), 28 deletions(-)

diff --git a/resources/examples/testfiles/dtmc/die.pm b/resources/examples/testfiles/dtmc/die.pm
index 1873bb8c1..e41139001 100644
--- a/resources/examples/testfiles/dtmc/die.pm
+++ b/resources/examples/testfiles/dtmc/die.pm
@@ -1,5 +1,5 @@
 // Knuth's model of a fair die using only fair coins
-mdp
+dtmc
 
 module die
 
diff --git a/src/storm-cli-utilities/model-handling.h b/src/storm-cli-utilities/model-handling.h
index 8cf964ca5..4180bdc33 100644
--- a/src/storm-cli-utilities/model-handling.h
+++ b/src/storm-cli-utilities/model-handling.h
@@ -373,8 +373,7 @@ namespace storm {
             STORM_LOG_THROW(model->isSparseModel(), storm::exceptions::NotSupportedException, "Counterexample generation is currently only supported for sparse models.");
             auto sparseModel = model->as<storm::models::sparse::Model<ValueType>>();
             
-            STORM_LOG_THROW(sparseModel->isOfType(storm::models::ModelType::Mdp), storm::exceptions::NotSupportedException, "Counterexample is currently only supported for MDPs.");
-            auto mdp = sparseModel->template as<storm::models::sparse::Mdp<ValueType>>();
+            STORM_LOG_THROW(sparseModel->isOfType(storm::models::ModelType::Dtmc) || sparseModel->isOfType(storm::models::ModelType::Mdp), storm::exceptions::NotSupportedException, "Counterexample is currently only supported for discrete-time models.");
             
             auto counterexampleSettings = storm::settings::getModule<storm::settings::modules::CounterexampleGeneratorSettings>();
             if (counterexampleSettings.isMinimalCommandSetGenerationSet()) {
@@ -387,9 +386,15 @@ namespace storm {
                     printComputingCounterexample(property);
                     storm::utility::Stopwatch watch(true);
                     if (useMilp) {
-                        counterexample = storm::api::computePrismHighLevelCounterexampleMilp(program, mdp, property.getRawFormula());
+                        STORM_LOG_THROW(sparseModel->isOfType(storm::models::ModelType::Mdp), storm::exceptions::NotSupportedException, "Counterexample generation using MILP is currently only supported for MDPs.");
+                        counterexample = storm::api::computePrismHighLevelCounterexampleMilp(program, sparseModel->template as<storm::models::sparse::Mdp<ValueType>>(), property.getRawFormula());
                     } else {
-                        counterexample = storm::api::computePrismHighLevelCounterexampleMaxSmt(program, mdp, property.getRawFormula());
+                        STORM_LOG_THROW(sparseModel->isOfType(storm::models::ModelType::Dtmc) || sparseModel->isOfType(storm::models::ModelType::Mdp), storm::exceptions::NotSupportedException, "Counterexample generation using MaxSAT is currently only supported for discrete-time models.");
+                        if (sparseModel->isOfType(storm::models::ModelType::Dtmc)) {
+                            counterexample = storm::api::computePrismHighLevelCounterexampleMaxSmt(program, sparseModel->template as<storm::models::sparse::Dtmc<ValueType>>(), property.getRawFormula());
+                        } else {
+                            counterexample = storm::api::computePrismHighLevelCounterexampleMaxSmt(program, sparseModel->template as<storm::models::sparse::Mdp<ValueType>>(), property.getRawFormula());
+                        }
                     }
                     watch.stop();
                     printCounterexample(counterexample, &watch);
diff --git a/src/storm/api/counterexamples.cpp b/src/storm/api/counterexamples.cpp
index 98c2f07bf..483dc57d4 100644
--- a/src/storm/api/counterexamples.cpp
+++ b/src/storm/api/counterexamples.cpp
@@ -10,9 +10,9 @@ namespace storm {
             return storm::counterexamples::MILPMinimalLabelSetGenerator<double>::computeCounterexample(env, program, *mdp, formula);
         }
         
-        std::shared_ptr<storm::counterexamples::Counterexample> computePrismHighLevelCounterexampleMaxSmt(storm::prism::Program const& program, std::shared_ptr<storm::models::sparse::Mdp<double>> mdp, std::shared_ptr<storm::logic::Formula const> const& formula) {
+        std::shared_ptr<storm::counterexamples::Counterexample> computePrismHighLevelCounterexampleMaxSmt(storm::prism::Program const& program, std::shared_ptr<storm::models::sparse::Model<double>> model, std::shared_ptr<storm::logic::Formula const> const& formula) {
             Environment env;
-            return storm::counterexamples::SMTMinimalLabelSetGenerator<double>::computeCounterexample(env, program, *mdp, formula);
+            return storm::counterexamples::SMTMinimalLabelSetGenerator<double>::computeCounterexample(env, program, *model, formula);
         }
         
     }
diff --git a/src/storm/api/counterexamples.h b/src/storm/api/counterexamples.h
index d8f3d1ddd..6481c4efd 100644
--- a/src/storm/api/counterexamples.h
+++ b/src/storm/api/counterexamples.h
@@ -8,7 +8,7 @@ namespace storm {
         
         std::shared_ptr<storm::counterexamples::Counterexample> computePrismHighLevelCounterexampleMilp(storm::prism::Program const& program, std::shared_ptr<storm::models::sparse::Mdp<double>> mdp, std::shared_ptr<storm::logic::Formula const> const& formula);
         
-        std::shared_ptr<storm::counterexamples::Counterexample> computePrismHighLevelCounterexampleMaxSmt(storm::prism::Program const& program, std::shared_ptr<storm::models::sparse::Mdp<double>> mdp, std::shared_ptr<storm::logic::Formula const> const& formula);
+        std::shared_ptr<storm::counterexamples::Counterexample> computePrismHighLevelCounterexampleMaxSmt(storm::prism::Program const& program, std::shared_ptr<storm::models::sparse::Model<double>> model, std::shared_ptr<storm::logic::Formula const> const& formula);
         
     }
 }
diff --git a/src/storm/counterexamples/SMTMinimalLabelSetGenerator.h b/src/storm/counterexamples/SMTMinimalLabelSetGenerator.h
index e659209a3..5ad54a0f5 100644
--- a/src/storm/counterexamples/SMTMinimalLabelSetGenerator.h
+++ b/src/storm/counterexamples/SMTMinimalLabelSetGenerator.h
@@ -119,7 +119,7 @@ namespace storm {
 
                 // Retrieve some references for convenient access.
                 storm::storage::SparseMatrix<T> const& transitionMatrix = model.getTransitionMatrix();
-                std::vector<uint_fast64_t> const& nondeterministicChoiceIndices = model.getNondeterministicChoiceIndices();
+                std::vector<uint_fast64_t> const& nondeterministicChoiceIndices = transitionMatrix.getRowGroupIndices();
 
                 // Now traverse all choices of all relevant states and check whether there is a successor target state.
                 // If so, the associated labels become relevant. Also, if a choice of a relevant state has at least one
@@ -1374,7 +1374,7 @@ namespace storm {
                 }
                 
                 storm::storage::SparseMatrix<T> const& transitionMatrix = subModel.getTransitionMatrix();
-                std::vector<uint_fast64_t> const& nondeterministicChoiceIndices = subModel.getNondeterministicChoiceIndices();
+                std::vector<uint_fast64_t> const& nondeterministicChoiceIndices = transitionMatrix.getRowGroupIndices();
                 
                 // Now determine which states and labels are actually reachable.
                 boost::container::flat_set<uint_fast64_t> reachableLabels;
@@ -1498,7 +1498,7 @@ namespace storm {
                 }
                 
                 storm::storage::SparseMatrix<T> const& transitionMatrix = subModel.getTransitionMatrix();
-                std::vector<uint_fast64_t> const& nondeterministicChoiceIndices = subModel.getNondeterministicChoiceIndices();
+                std::vector<uint_fast64_t> const& nondeterministicChoiceIndices = transitionMatrix.getRowGroupIndices();
                 
                 // Now determine which states and labels are actually reachable.
                 boost::container::flat_set<uint_fast64_t> reachableLabels;
@@ -1821,10 +1821,10 @@ namespace storm {
                 auto startTime = std::chrono::high_resolution_clock::now();
                 
                 // Create sub-model that only contains the choices allowed by the given command set.
-                storm::models::sparse::Model<T> subModel = restrictModelToLabelSet(model, commandSet).first;
+                std::shared_ptr<storm::models::sparse::Model<T>> subModel = restrictModelToLabelSet(model, commandSet).first;
                 
                 // Then determine all prob0E(psi) states that are reachable in the sub-model.
-                storm::storage::BitVector reachableProb0EStates = storm::utility::graph::getReachableStates(subModel.getTransitionMatrix(), subModel.getInitialStates(), phiStates, psiStates);
+                storm::storage::BitVector reachableProb0EStates = storm::utility::graph::getReachableStates(subModel->getTransitionMatrix(), subModel->getInitialStates(), phiStates, psiStates);
                 
                 // Create a queue of reachable prob0E(psi) states so we can check which commands need to be added
                 // to give them a strategy that avoids psi states.
@@ -1887,6 +1887,7 @@ namespace storm {
             }
 
             static boost::container::flat_set<uint_fast64_t>  computeCounterexampleCommandSet(Environment const& env, storm::prism::Program program, storm::models::sparse::Model<T> const& model, std::shared_ptr<storm::logic::Formula const> const& formula) {
+                STORM_LOG_THROW(model.isOfType(storm::models::ModelType::Dtmc) || model.isOfType(storm::models::ModelType::Mdp), storm::exceptions::NotSupportedException, "MaxSAT-based counterexample generation is supported only for discrete-time models.");
                 std::cout << std::endl << "Generating minimal label counterexample for formula " << *formula << std::endl;
 
                 STORM_LOG_THROW(formula->isProbabilityOperatorFormula(), storm::exceptions::InvalidPropertyException, "Counterexample generation does not support this kind of formula. Expecting a probability operator as the outermost formula element.");
diff --git a/src/storm/models/sparse/NondeterministicModel.h b/src/storm/models/sparse/NondeterministicModel.h
index 2274e137a..ac8603e7f 100644
--- a/src/storm/models/sparse/NondeterministicModel.h
+++ b/src/storm/models/sparse/NondeterministicModel.h
@@ -19,10 +19,8 @@ namespace storm {
              * The base class of sparse nondeterministic models.
              */
             template<class ValueType, typename RewardModelType = StandardRewardModel<ValueType>>
-            class NondeterministicModel: public Model<ValueType, RewardModelType> {
+            class NondeterministicModel : public Model<ValueType, RewardModelType> {
             public:
-                
-                
                 /*!
                  * Constructs a model from the given data.
                  *
@@ -39,6 +37,8 @@ namespace storm {
                  */
                 std::vector<uint_fast64_t> const& getNondeterministicChoiceIndices() const;
                 
+                using Model<ValueType, RewardModelType>::getNumberOfChoices;
+                
                 /*!
                  * @param state State for which we want to know how many choices it has
                  * 
diff --git a/src/storm/utility/counterexamples.h b/src/storm/utility/counterexamples.h
index ec20c20cb..1ff429a5d 100644
--- a/src/storm/utility/counterexamples.h
+++ b/src/storm/utility/counterexamples.h
@@ -18,20 +18,20 @@ namespace storm {
              * @return The set of labels that is visited on all paths from any state to a target state.
              */
             template <typename T>
-            std::vector<boost::container::flat_set<uint_fast64_t>> getGuaranteedLabelSets(storm::models::sparse::Mdp<T> const& mdp, std::vector<boost::container::flat_set<uint_fast64_t>> const& labelSets, storm::storage::BitVector const& psiStates, boost::container::flat_set<uint_fast64_t> const& relevantLabels) {
-                STORM_LOG_THROW(mdp.getNumberOfChoices() == labelSets.size(), storm::exceptions::InvalidArgumentException, "The given number of labels does not match the number of choices.");
+            std::vector<boost::container::flat_set<uint_fast64_t>> getGuaranteedLabelSets(storm::models::sparse::Model<T> const& model, std::vector<boost::container::flat_set<uint_fast64_t>> const& labelSets, storm::storage::BitVector const& psiStates, boost::container::flat_set<uint_fast64_t> const& relevantLabels) {
+                STORM_LOG_THROW(model.getNumberOfChoices() == labelSets.size(), storm::exceptions::InvalidArgumentException, "The given number of labels does not match the number of choices.");
                 
-                // Get some data from the MDP for convenient access.
-                storm::storage::SparseMatrix<T> const& transitionMatrix = mdp.getTransitionMatrix();
-                std::vector<uint_fast64_t> const& nondeterministicChoiceIndices = mdp.getNondeterministicChoiceIndices();
-                storm::storage::SparseMatrix<T> backwardTransitions = mdp.getBackwardTransitions();
+                // Get some data from the model for convenient access.
+                storm::storage::SparseMatrix<T> const& transitionMatrix = model.getTransitionMatrix();
+                std::vector<uint_fast64_t> const& nondeterministicChoiceIndices = transitionMatrix.getRowGroupIndices();
+                storm::storage::SparseMatrix<T> backwardTransitions = model.getBackwardTransitions();
 
                 // Now we compute the set of labels that is present on all paths from the initial to the target states.
-                std::vector<boost::container::flat_set<uint_fast64_t>> analysisInformation(mdp.getNumberOfStates(), relevantLabels);
+                std::vector<boost::container::flat_set<uint_fast64_t>> analysisInformation(model.getNumberOfStates(), relevantLabels);
                 
                 std::queue<uint_fast64_t> worklist;
-                storm::storage::BitVector statesInWorkList(mdp.getNumberOfStates());
-                storm::storage::BitVector markedStates(mdp.getNumberOfStates());
+                storm::storage::BitVector statesInWorkList(model.getNumberOfStates());
+                storm::storage::BitVector markedStates(model.getNumberOfStates());
                 
                 // Initially, put all predecessors of target states in the worklist and empty the analysis information them.
                 for (auto state : psiStates) {
@@ -108,12 +108,12 @@ namespace storm {
              * @return The set of labels that is executed on all paths from an initial state to a target state.
              */
             template <typename T>
-            boost::container::flat_set<uint_fast64_t> getGuaranteedLabelSet(storm::models::sparse::Mdp<T> const& mdp, std::vector<boost::container::flat_set<uint_fast64_t>> const& labelSets, storm::storage::BitVector const& psiStates, boost::container::flat_set<uint_fast64_t> const& relevantLabels) {
-                std::vector<boost::container::flat_set<uint_fast64_t>> guaranteedLabels = getGuaranteedLabelSets(mdp, labelSets, psiStates, relevantLabels);
+            boost::container::flat_set<uint_fast64_t> getGuaranteedLabelSet(storm::models::sparse::Model<T> const& model, std::vector<boost::container::flat_set<uint_fast64_t>> const& labelSets, storm::storage::BitVector const& psiStates, boost::container::flat_set<uint_fast64_t> const& relevantLabels) {
+                std::vector<boost::container::flat_set<uint_fast64_t>> guaranteedLabels = getGuaranteedLabelSets(model, labelSets, psiStates, relevantLabels);
                 
                 boost::container::flat_set<uint_fast64_t> knownLabels(relevantLabels);
                 boost::container::flat_set<uint_fast64_t> tempIntersection;
-                for (auto initialState : mdp.getInitialStates()) {
+                for (auto initialState : model.getInitialStates()) {
                     std::set_intersection(knownLabels.begin(), knownLabels.end(), guaranteedLabels[initialState].begin(), guaranteedLabels[initialState].end(), std::inserter(tempIntersection, tempIntersection.end()));
                     std::swap(knownLabels, tempIntersection);
                 }